ADC模数转换
1. ADC简介
ADC(analog to digital converter)即模数转换器,它可以将模拟量信号转换为数字信号,按照转换原理主要分为逐次逼近型、双积分型、电压频率转换型三种。STM32F1的ADC是12位逐次逼近型的模数转换器,它有18个通道,可测量16个外部和2个内部信号源。各通道的A/D转换可以单次、连续、扫描或间断模式执行。ADC的结果可以左对齐或右对齐方式存储在16位存储寄存器中。模拟看门狗特性允许应用程序检测输入电压是否超出用户定义的高/低阈值。ADC的时钟不要超过14M,否则将导致结果准确度下降。ADC结构框图以及ADC引脚说明如下图示:
- 电压输入引脚和输入通道引脚见上表;另外主ADC1中还有2个内部通道:通道16连接到芯片内部的温度传感器,通道17连接到了内部参考电压VREFINT;ADC2/ADC3的通道16/17都连接到了内部的VSS
- ADC的转换分为两个通道组:规则通道组(16路)和注入通道组(4路),规则通道相当于正常运行的程序,注入通道相当于中断
- 选择好输入通道和转换顺序后需要使能ADC,可以直接开启ADC转换或者选择外部事件触发转换
- ADC最大工作频率为14M,一般设置分配因子为6,即ADC的输入时钟ADC_CLK = 12M
- ADC要完成对输入电压的采样需要若干个ADC_CLK周期,采样周期最小是1.5个(即如果要达到最快的采样,应设置采样周期为1.5个周期,即1.5乘以1/ADC_CLK)
- ADC的总转换时间Tconv= 采样时间 + 12.5个周期;按以上设置的话,Tconv= (1.5+12.5)个周期 = 14 * (1/12M) = 1.17us,即最短转换时间为1.17us
- ADC转换后的数据根据不同的转换组,放在不同的数据寄存器(16位)中;由于ADC是12位转换精度,而数据寄存器是16位,因此存放数据的时候有左对齐和右对齐之分
- 规则组含有16个通道但是对应存放数据的寄存器只有一个,如果使用多通道转换则应当在通道转换完成后就把数据取走,或者开启DMA模式把数据传输到内存里,否则就会造成数据的覆盖
- 使能相应中断标志位,ADC能3种产生相应中断:规则转换与注入转换结束、模拟看门狗事件、DMA请求
2. 硬件设计
本实验通过ADC1通道1采样外部电压值,将采样的AD值和转换后的电压值通过USART1串口打印出来,同时D1指示灯闪烁,提示系统正常运行
- D1指示灯
- ADC1_INT1
- USART1串口
- 电位器
3. 软件设计
3.1 STM32CubeMX设置
- RCC设置外接HSE,时钟设置为72M,ADC预分频因子设置为6,ADC_CLK为12MHz
- PC0设置为GPIO推挽输出模式、上拉、高速、默认输出电平为高电平
- USART1选择为异步通讯方式,波特率设置为115200Bits/s,传输数据长度为8Bit,无奇偶校验,1位停止位
- 激活ADC1通道1,设置右对齐,关闭扫描、连续及间断模式,使能regular conversion,设置软件触发、设置采样时间1.5个周期
- 输入工程名,选择工程路径(不要有中文),选择MDK-ARM V5;勾选Generated periphera initialization as a pair of ‘.c/.h’ files per IP ;点击GENERATE CODE,生成工程代码
3.2 MDK-ARM编程
- 在adc.c文件中可以看到ADC初始化函数
void MX_ADC1_Init(void){
ADC_ChannelConfTypeDef sConfig = { 0};
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();
}
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK){
Error_Handler();
}
}
void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle){
GPIO_InitTypeDef GPIO_InitStruct = { 0};
if(adcHandle->Instance==ADC1){
__HAL_RCC_ADC1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
}
- 在主函数while循环中添加如下测试程序,ADC1是12位转换精度,因此电压分辨率为:3.3/(212) = 3.3/4096
while (1){
HAL_ADC_Start(&hadc1); //启动ADC转换
HAL_ADC_PollForConversion(&hadc1,10); //等待转换完成,10ms表示超时时间
AD_Value = HAL_ADC_GetValue(&hadc1); //读取ADC转换数据(12位数据)
printf("ADC1_IN1 ADC value: %d\r\n",AD_Value);
Vol_Value = AD_Value*(3.3/4096); //AD值乘以分辨率即为电压值
printf("ADC1_IN1 VOL value: %.2fV\r\n",Vol_Value);
HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_0);
HAL_Delay(1000);
}
4. 下载验证
编译无误后下载到开发板,可以看到系统运行时D1指示灯不断闪烁,调节电位器时,获取的AD转换值和电压值将变化,并通过串口打印出来