使用HAL库配置单通道ADC_1的步骤详解
文章目录
一、ADC概念
1.ADC特点—以STM32F4x系列为例
💦在使用ADC外设前,先查看下芯片手册,看下ADC的特点。
2.ADC引脚介绍—以STM32F4x系列为例
💦ADC的使用,一定是有参考电压的,在使用时要注意,芯片手册上关于ADC参考电压的范围。
通常正参考电压VREF+连接到VCC,负参考电压VREF-连接到GND
3.ADC框图简介—以STM32F4x系列为例
💦通过框图可以了解ADC的工作流程。
4.ADC参数的简介
下面是ADC比较常用的参数说明:
💦(1)测量范围:对于 ADC 来说测量范围就好比尺子的量程,需要采集的信号范围,不能超过 ADC 的测量范围(比如,STM32系列的 ADC 正常就不能超过3.3V)。
💦(2)分辨率:假如 ADC 的测量范围为 0-3.3V,分辨率设置为12位,分辨率就是最 > 💦低有效位(LSB)的对应输入电压值,分辨率 =3300/4095 ≈ 0.806mV。12位的2进制数最大可以表示4096,ADC采集就是将模拟信号量化为数字信号的过程,3300/4096就是将3.3V分成4096个份,每份电压为0.000805V。所以,分辨率越高,采集到的信号越精确,因此分辨率是衡量 ADC 的一个重要指标。
💦(3)采样时间:当 ADC 在某时刻采集外部电压信号的时候,此时外部的信号应该保持不变,但实际上外部的信号是不停变化的。所以在 ADC 内部有一个保持电路,保持某一时刻的外部信号,这样 ADC 就可以稳定采集了,保持这个信号的时间就是采样时间。
💦(4)采样率:也就是在一秒的时间内采集多少次。很明显,采样率越高越好,当采样率不够的时候可能会丢失部分信息,所以 ADC 采样率是衡量 ADC 性能的另一个重要指标。
5.采样时间和采样率的说明
💦ADC采样两点间隔的时间一定要大于ADC的采样时间
💦举例说明:
STM32F103一般将时钟配置主频为72M、APB2为72M。ADC挂在APB2时钟总线上,且ADC的时钟不能超过14M。所以一般将ADC的分频设置为6,ADC的时钟主频就为72/6=12MHz。那么一个周期就是:1/12MHz=0.0833us。
💦ADC转换时间 = 采样时间 + 12.5个周期
💦示例:
💦当ADC时钟主频为14MHz并且采样时间为1.5个周期时:采样时间 = 1.5 + 12.5 = 14个周期;一个周期的时间为1/14MHz,一共14个周期,1 / 14MHz * 14个周期 = 1us,
那么此时ADC的采样频率就是1/1us=1000KHz=1MHz,这也是理论上ADC的最大采样频率。
STM32F1系列的时钟主频一般设置为了12M,采样时间的设置所对应采样频率如下图所示:
💦从上图可以知道ADC的最小采样时间,当ADC时钟主频为12M并且采样时间为1.5个周期时,ADC采样两点的时间必须大于1.17us。
💦而STM32F031系列的ADC时钟主频为14MHz,采样时间的设置所对应采样频率如下图所示:
💦当ADC时钟主频为14M并且采样时间为1.5个周期时,ADC采样两点的时间必须大于1us。
💦举例说明:
💦确定采样率
(1)如果我们的输入信号是 20KHz (周期为 50us),若要将它恢复出来,一个周期最少采样20个点,此时采样率要达到400KHz(两个点的时间间隔为2.5us),所以ADC的采样率必须在400KHz 以上。为了达到最好的精度,我们选取ADC时钟为12MHz,即6分频。在12MHz 以及保证采样率的情况下,采样时间越长其,准确性就越好。
(2)可以计算 2.5us = (12.5 + 采样时间)/ 12MHz ,可以求得采样时间为17.5;所以采样时间的选择必须小于等于17.5个周期,才能保证采样率在400KHz 以上。所以我们可以选择1.5、7.5、13.5,为获得更高的精准度,我们可以选择13.5个周期。
切记采样点数必须达到要求。
二、ADC工作模式
💦1. 轮询模式
💦2. 中断模式
💦3. DMA模式
1.轮询模式
HAL库函数如下:
HAL_ADC_Start(ADC_HandleTypeDef* hadc);//轮询模式,需放在循环中不断开启
HAL_ADC_Stop(ADC_HandleTypeDef* hadc);
HAL_ADC_PollForConversion(ADC_HandleTypeDef* hadc, uint32_t Timeout); //等待转换结束,只适用于轮询,注意配置转换时间
2.中断模式
HAL库函数如下:
HAL_ADC_Start_IT(ADC_HandleTypeDef* hadc);//中断模式
HAL_ADC_Stop_IT(ADC_HandleTypeDef* hadc);
HAL_ADC_IRQHandler(ADC_HandleTypeDef* hadc);//中断
3.DMA模式
HAL库函数如下:
HAL_ADC_Start_DMA(ADC_HandleTypeDef* hadc, uint32_t* pData, uint32_t Length);//DMA模式
HAL_ADC_Stop_DMA(ADC_HandleTypeDef* hadc);
HAL_ADC_GetValue(ADC_HandleTypeDef* hadc); //读取ADC的值
HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc);//结束后回调
HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc);//转换过程中回调
三、ADC单通道采集-stm32Fx系列为例
1.轮询模式下单通道采集
STM32CubeMX配置
💦ADC模式选择
通常选择独立模式,在此模式下,ADC相互独立工作。在ADC_CDR寄存器中配置ADC的模式,详情如下。
💦ADC分频
用于设置ADC的工作频率。注意ADC的工作频率不能太高,根据实际情况进行选择。
💦ADC分辨率
💦ADC对齐方式
一般选择右对齐方式
💦ADC的扫描模式
扫描模式用于多通道转换,单通道的时候不适用。
💦ADC的连续转换模式
在连续转换模式下,ADC 结束一个转换后立即启动一个新的转换。
在CUBE中选中ENABLE就是连续模式,DISABLE就是单次模式。开启连续模式后,ADC的转换不由其他控制。例如将ADC设置为了定时器的TGRO触发采样,如果开启连续模式,ADC将忽略定时器的触发采样。(连续转换模式开启后其实就是满频率的采样)。
💦ADC的间接转换模式(不连续模式)
通俗来讲_间断模式: 可以将多个通道进行分组采集,例如你开启了CH0~3这4个通道,假如你设置了间断次数为4,就相当于将4个通道分成了4组,每组1个通道,那么要想采集完这4个通道就需要手动触发4次ADC采集;如果设置了间断次数为2,那么采集完4个通道就需要手动触发2次ADC采集
💦ADC的DMA
用于开启DMA模式
2.轮询模式-单通道下采集代码示例
单通道-单次采集模式
//获得ADC值
//ch: 通道值 0~16,取值范围为:ADC_CHANNEL_0~ADC_CHANNEL_16
//返回值:转换结果
uint16_t Get_Adc(uint32_t ch)
{
ADC_ChannelConfTypeDef ADC1_ChanConf;
ADC1_ChanConf.Channel=ch; //通道
ADC1_ChanConf.Rank=1; //第1个序列,序列1
ADC1_ChanConf.SamplingTime=ADC_SAMPLETIME_480CYCLES; //采样时间
ADC1_ChanConf.Offset=0;
HAL_ADC_ConfigChannel(&hadc1,&ADC1_ChanConf); //通道配置
HAL_ADC_Start(&hadc1); //开启ADC
HAL_ADC_PollForConversion(&hadc1,10); //轮询转换
return (uint16_t)HAL_ADC_GetValue(&hadc1); //返回最近一次ADC1规则组的转换结果
}
//获取指定通道的转换值,取times次,然后平均
//times:获取次数
//返回值:通道ch的times次转换结果平均值
uint16_t Get_Adc_Average(uint32_t ch,uint8_t times)
{
uint32_t temp_val=0;
uint8_t t;
for(t=0;t<times;t++)
{
temp_val+=Get_Adc(ch);
HAL_Delay(5);
}
return temp_val/times;
}
int main(void)
{
uint16_t adc_temp=0;
float voltage_temp=0;
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_ADC1_Init();
MX_USART1_UART_Init();
while (1)
{
adc_temp=Get_Adc_Average(ADC_CHANNEL_0,20);
voltage_temp=(float)adc_temp*(3.3/4096);
printf("V:%f\n",voltage_temp);
}
}
输入正弦信号,采集结果如下:
轮询模式,要在循环连续采集
单通道-连续采集模式
暂未实现,原因未知
3.中断模式-单通道下采集代码示例
单通道-单次采集模式
产生中断后回进入公共的中断服务函数,完成中断处理,中断服务函数最终会调用ADC转换完成回调函数,所以采集完成后在回调函数中处理。
在回调函数中,设置一个状态量,在主函数中对此状态量进行查询,当变量为1时,在进行数据的获取和处理。
uint8_t adc_state=0;
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
if(hadc==&hadc1)
{
adc_state=1;
}
}
下面代码是单次采集模式,转换一次就结束转换,所以要在循环中,开启下一次转换。
所以要在循环中,开启下一次转换。
int main(void)
{
uint16_t adc_temp=0,adc_cnt=0,temp=0;;
float voltage_temp=0;
uint32_t temp_val=0;
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_ADC1_Init();
MX_USART1_UART_Init();
HAL_ADC_Start_IT(&hadc1);
while (1)
{
extern uint8_t adc_state;
if(adc_state==1)
{
adc_state=0;
temp=HAL_ADC_GetValue(&hadc1);
temp_val+=temp;
HAL_Delay(1);
adc_cnt++;
HAL_ADC_Start_IT(&hadc1); //开启下一次转换
if(adc_cnt>=30)
{
adc_temp=(uint16_t)(temp_val/adc_cnt);
adc_cnt=0;
temp_val=0;
voltage_temp=(float)adc_temp*(3.3/4096);
printf("V:%f\n",voltage_temp);
}
}
}
}
输入正弦信号,采集结果如下:
单通道-连续采集模式
将连续转换模式使能,即可开启连续采集模式。
与单次采集的区别是,开始转换函数HAL_ADC_Start_IT(&hadc1),在主函数初始化时,调用一次即可,开启连续转换。
int main(void)
{
uint16_t adc_temp=0,adc_cnt=0,temp=0;;
float voltage_temp=0;
uint32_t temp_val=0;
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_ADC1_Init();
MX_USART1_UART_Init();
HAL_ADC_Start_IT(&hadc1);
while (1)
{
extern uint8_t adc_state;
if(adc_state==1)
{
adc_state=0;
temp=HAL_ADC_GetValue(&hadc1);
temp_val+=temp;
HAL_Delay(1);
adc_cnt++;
// HAL_ADC_Start_IT(&hadc1); //开启下一次转换
if(adc_cnt>=30)
{
adc_temp=(uint16_t)(temp_val/adc_cnt);
adc_cnt=0;
temp_val=0;
voltage_temp=(float)adc_temp*(3.3/4096);
printf("V:%f\n",voltage_temp);
}
}
}
}
4.DMA模式-单通道下采集代码示例
单通道-单次采集模式
产生中断后回进入公共的中断服务函数,完成中断处理,中断服务函数最终会调用ADC转换完成回调函数,所以采集完成后在回调函数中处理。
在回调函数中,获取数据,可以在回调函数里将数据进行赋值,处理数据可以在主函数中处理,也可以在回调中处理。
uint16_t adc_dma_temp=0;
uint8_t adc_dma_cnt=0;
uint32_t adc_dma_sum=0;
float voltage_temp=0;
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
if(hadc==&hadc1)
{
adc_dma_sum+=adc_dma_temp;
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&adc_dma_temp, 1);//获取数据后,开启下次转换
adc_dma_cnt++;
if(adc_dma_cnt>=20)
{
adc_dma_temp=(uint16_t)(adc_dma_sum/adc_dma_cnt);
voltage_temp=(float)adc_dma_temp*(3.3/4096);
printf("V:%f\n",voltage_temp);
adc_dma_cnt=0;
adc_dma_sum=0;
}
}
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_DMA_Init();
MX_ADC1_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
extern uint16_t adc_dma_temp;
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&adc_dma_temp, 1);//开启一次,让其进入中断
while (1)
{
HAL_Delay(5);
}
}
单通道-连续采集模式
暂未成功