实现目的:利用ADC采集光敏传感器/烟雾传感器的值,并利用串口打印
实验平台:正点原子精英版
一、简介
1.DMA的介绍
参考:STM32 hal库使用笔记(四)DMA—内存到内存/内存到外设_乱码小伙的博客-CSDN博客
2.ADC简介
ADC(Analog-Digital Converter)模拟-数字转换器 ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁;
12位逐次逼近型ADC,1us转换时间;
输入电压范围:0~3.3V,转换结果范围:0~4095;
18个输入通道,可测量16个外部和2个内部信号源;
规则组和注入组两个转换单元,可利用模拟看门狗自动监测输入电压范围。
3.一些概念
ADC数据寄存器是32位,但是只用到16位,所以一般采用右对齐方式,方便计算。
如果多通道转换ADC,次数频繁,间隔时间短(循环模式),会导致数据寄存器的数值被覆盖,所以要利用DMA转运。
注入组限制较多,一般采用规则组进行转换。
TCONV = 采样时间 + 12.5个ADC周期,采样时间=n个ADC周期,ADC采用频率最大是14MHZ,由于ADC挂载总线的频率是72MHZ所以进行6分频,采用周期是12MHZ。
ADC有一个内置自校准模式。校准可大幅减小因内部电容器组的变化而造成的准精度误差。校准期间,在每个电容器上都会计算出一个误差修正码(数字值),这个码用于消除在随后的转换中每个电容器上产生的误差。HAL_ADCEx_Calibration_Start(),进行校准。
二、HAL库配置
1.时钟树的设置
按照下图配置即可:
2.ADC的配置
关于串口的配置参考:STM32 hal库使用笔记(二)中断—串口中断_乱码小伙的博客-CSDN博客
本实验不使用串口中断,中断部分的配置不用操作
2.1 单通道(代码对应3.1)
1)关闭扫描模式,由于只有一个通道;
2)关闭连续转换模式,每次需要ADC转换时打开ADC转换即可;
3)采用规则组;
4)软件触发模式;
5)28.5个ADC周期,所以整个采采样周期是30个ADC周期。
配置完成后生成代码即可。
2.2 DMA双通道(代码对应3.2)
1)开启双通道
2)扫描模式打开
3)转换组设置为2
4)打开通道1
5)周期28.5个
6)打开通道2
7)周期28.5个
注意每个ADC的每个通道只能对应一个GPIO。
ADC数据是16位的,所以采用半字字宽。
配置完成后,生成代码即可。
三、代码编写
3.1
以下代码在adc.c中编写
void MX_ADC1_Init(void)
{
ADC_ChannelConfTypeDef sConfig = {0};
/** Common config
*/
hadc1.Instance = ADC1;
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 1;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_28CYCLES_5;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)//配置ADC时钟,通道、序列
{
Error_Handler();
}
HAL_ADCEx_Calibration_Start(&hadc1);//用户添加,ADC校准,据了解最新版HAL库已经删除
}
用户编码区:
uint32_t adc_get_result(void)
{
HAL_ADC_Start(&hadc1);//单次转换模式,每次转换完成后ADC转换会自动停止
HAL_ADC_PollForConversion(&hadc1, 10);//ADC转换,转换参数ms
return (uint16_t)HAL_ADC_GetValue(&hadc1);
}
以下在main.c
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_ADC1_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
HAL_Delay(2000);
unsigned char arrary[4];
arrary[0] = ((adc_get_result()/1000))+0x30;
arrary[1] = (((adc_get_result()%100)/10))+0x30;
arrary[2] = ((adc_get_result()%10)/10)+0x30;
arrary[3] = ((adc_get_result()%1)/10)+0x30;
//unsigned char MyArray[1]={adc_get_result()};
//HAL_UART_Transmit(&huart1, MyArray,1, 10000);
HAL_UART_Transmit(&huart1, arrary,4,1000);
}
/* USER CODE END 3 */
}
实验现象:用手盖住和放开有明显变化
3.2
添加接收数据数组:uint16_t g_adc_dma_buf[2];
以下代码均在adc.c中编写:
void MX_ADC1_Init(void)
{
ADC_ChannelConfTypeDef sConfig = {0};
/** Common config
*/
hadc1.Instance = ADC1;
hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 2;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_28CYCLES_5;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_2;
sConfig.Rank = ADC_REGULAR_RANK_2;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
HAL_ADC_Start_DMA(&hadc1,(uint32_t *)(g_adc_dma_buf) ,0);//触发ADC转换,DMA传输数据
}
HAL_ADC_Start_DMA(&hadc1,(uint32_t *)(g_adc_dma_buf) ,0);这个可以省略,因为DMA不是循环模式,ADC也不是连续模式,所以每次采集ADC都需要重新开启。
用户编码区:
void adc_dma_enable(uint32_t cndtr)
{
__HAL_ADC_DISABLE(&hadc1);
__HAL_DMA_DISABLE(&hdma_adc1);
while (__HAL_DMA_GET_FLAG(&hdma_adc1, DMA_FLAG_TC1))
{
__HAL_DMA_CLEAR_FLAG(&hdma_adc1, DMA_FLAG_TC1); //清除标志位
}//实测,删去也能正常使用,因为测量间隔时间长,DMA肯定关闭了
DMA1_Channel1->CNDTR = cndtr;
__HAL_DMA_ENABLE(&hdma_adc1);
__HAL_ADC_ENABLE(&hadc1);
HAL_ADC_Start(&hadc1); //开启ADC转换,必须定时开启,因为ADC不是连续扫描模式
}
以下均在main.c
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_ADC1_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
HAL_Delay(2000);
adc_dma_enable(2);//必须添加
unsigned char arrary[4];
unsigned char arrary2[4];
arrary[0] = ((g_adc_dma_buf[0]/1000))+0x30;
arrary[1] = (((g_adc_dma_buf[0]%100)/10))+0x30;
arrary[2] = ((g_adc_dma_buf[0]%10)/10)+0x30;
arrary[3] = ((g_adc_dma_buf[0]%1)/10)+0x30;
printf("ADC1:");
HAL_UART_Transmit(&huart1, arrary,4,1000);
printf("\r\n");
arrary2[0] = ((g_adc_dma_buf[1]/1000))+0x30;
arrary2[1] = (((g_adc_dma_buf[1]%100)/10))+0x30;
arrary2[2] = ((g_adc_dma_buf[1]%10)/10)+0x30;
arrary2[3] = ((g_adc_dma_buf[1]%1)/10)+0x30;
printf("ADC2:");
HAL_UART_Transmit(&huart1, arrary2,4,1000);
printf("\r\n");
}
/* USER CODE END 3 */
}
实验现象:没有打火机,烟雾传感器值一直为0,ADC通道1连接光敏,有明显变化(连接到通道2上也有明显变化)。实测成功。
文章来源:https://www.toymoban.com/news/detail-773197.html
欢迎大家交流和指正!!!文章来源地址https://www.toymoban.com/news/detail-773197.html
到了这里,关于STM32 hal库使用笔记(五)ADC—单通道/双通道DMA传输的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!