STM32CubeMX | HAL库的ADC多通道数据采集(轮训、DMA、DMA+TIM)、读取内部传感器温度
目录
打个广告
博主做的脱机烧录器:重磅来袭!CMSIS-DAP 脱机烧录器 EasyFlasher 发布~
某宝店铺:觉皇工作室
购买链接:https://item.taobao.com/item.htm?abbucket=18&id=841061310319
实物图,MINI版:
实物图,COOL版:
注:本片文章以STM32F103系列为例,其他系列可能稍有不同。
1、ADC简介
1.1 采样定理
1.2 模式介绍
-
扫描模式: 使用STM32CUBEMX配置了多通道后,这一项默认开启且无法设置成关闭。这个模式就是自动扫描你开启的所有通道进行转换,直至转换完。例如你开启了CH0、CH1、CH2、CH3这四个通道,启动转换后ADC会自动将这4个通道全部转换完,但是这种连续性是可以被打断的,所以就引出了间断模式。
-
连续模式: 在CUBE中选中ENABLE就是连续模式,DISABLE就是单次模式。开启连续模式后,ADC的转换不由其他控制。例如将ADC设置为了定时器的TGRO触发采样,如果开启连续模式,ADC将忽略定时器的触发采样。(连续转换模式开启后其实就是满频率的采样)。
-
间断模式: 可以将多个通道进行分组采集,例如你开启了CH0~3这4个通道,假如你设置了间断次数为4,就相当于将4个通道分成了4组,每组1个通道,那么要想采集完这4个通道就需要手动触发4次ADC采集;如果设置了间断次数为2,那么采集完4个通道就需要手动触发2次ADC采集。
1.3 采样时间和采样频率的计算
ADC采样两点间隔的时间一定要大于ADC的采样时间! 采样时间怎么算,下面就细说一下。
STM32F103一般将时钟配置主频为72M、APB2为72M。ADC挂在APB2时钟总线上,且ADC的时钟不能超过14M。所以一般将ADC的分频设置为6,ADC的时钟主频就为72/6=12MHz。那么一个周期就是:1/12MHz=0.0833us。
以下截图为STM32F1参考手册(手册编号RM0008,可在ST官网直接搜索下载):
上图的意思是:ADC对输入电压采样若干个ADC_CLK周期,这些周期可通过ADC_SMPR1和ADC_SMPR2寄存器中的SMP[2:0]位进行修改。每个通道都可以用不同的采样时间进行采样。
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时钟主频为12M并且采样时间为1.5个周期时,ADC采样两点的时间必须大于1.17us。
而STM32F031系列的ADC时钟主频为14MHz,采样时间的设置所对应采样频率如下图所示:
当ADC时钟主频为14M并且采样时间为1.5个周期时,ADC采样两点的时间必须大于1us。
2、轮训方式的多通道采集
2.1 方式一:间断模式+扫描模式
我开启了通道0、1、2以及内部温度读取通道一共四个通道:
如果想使用轮训方式并且不使用DMA的多通道采集,那么就要配置为 【单次模式+间断模式】,并且将Number Of Discontinuous Conversions为1,也就是每个通道分成了一个组,配置如下图:
由于我这里设置间断数为1,也就是将4个通道分成了4组,那么我每次采集的时候都需要手动去触发ADC采集,也就是调用一次HAL_ADC_Start函数,完整代码如下:
我将通道0分别接到3.3V和GND上,4通道采集运行效果如下:
2.2 方式二:完全轮训
完全使用轮训的方式不能使用扫描模式(理论上来说多通道需要使用扫描模式,而不是完全轮训),但是使用STM32CUBEMX配置多通道扫描模式不无法被关闭的,所以我们先用STM32CUBEMX配置成一个通道:
然后读取ADC采集数值的函数是这样:
uint16_t ADC_Read(uint32_t Channel)
{
ADC_ChannelConfTypeDef sConfig = {0};
sConfig.Channel = Channel; /* 通道 */
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_55CYCLES_5; /* 采样时间 */
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
HAL_ADC_Start(&hadc1);
HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY);
return (uint16_t)HAL_ADC_GetValue(&hadc1);
}
/*
uint16_t ADC_Value[5];
ADC_Value[0] = ADC_Read(ADC_CHANNEL_1); // 采集并读取通道1
ADC_Value[1] = ADC_Read(ADC_CHANNEL_4);
ADC_Value[2] = ADC_Read(ADC_CHANNEL_5);
ADC_Value[3] = ADC_Read(ADC_CHANNEL_6);
ADC_Value[4] = ADC_Read(ADC_CHANNEL_7);
*/
3、DMA实现多通道采集
通过DMA实现多通道数据采集,要将连续模式和间断模式关闭!
如果连续模式开启,那么通过DMA传输到的数组中,每个通道所采集到的值对应数组中的一个位置就是不固定的。
例如你开启了IN0~IN3这四个通道,并通过DMA将这四个通道的数据放到ADC_Value这个大小为4的u16类型数组,
你在第一次采集的时候IN0通道的数值通过DMA被放在ADC_Value[0],
第二次采集的时候IN0采集到的数值就可能被放到了ADC_Value[1],
这样的话就极不方便我们对每个通道的数据进行分析和提取。
配置如下,将连续转换模式和间断转换模式关闭,并开启ADC的DMA通道,将DMA的模式配置为周期模式:
代码如下:
4个通道采集运行效果如下:
4、TIM+DMA实现多通道采集
在不使用DMA的情况下,定时器控制ADC进行数据采集只能是单通道!如果开启了多通道,读取到的ADC采集值只会是最后一个通道的值!
所以,要想使用定时器控制ADC采集多通道,必须使用DMA!
CubeMX配置如下,使能ADC的DMA:
ADC的触发方式设置为定时器3的触发事件:
采样时间设置为了55.5个周期,对应采样频率为176.47KHz,所以定时器3的频率设置要低于176.47KHz。
设置定时器3分频系数为72,重载值为10,得到72MHz / 72分频 / 10 = 100KHz
的定时器3:
代码如下:
运行结果如下:
当然也可以选择进行一组数据,例如将32个点为一组数据进行采集,设置如下:
运行结果如下:
5、补充:内部温度传感器ADC通道
上面图片是截取在STM32F103RC的datasheet中的,中文意思就是:温度传感器必须产生随温度线性变化的电压。转换范围在2v < VDDA < 3.6 V之间。温度传感器内部连接ADC1_IN16输入通道,用于将传感器输出电压转换为数字值。
内部温度计算公式:
【Temperature = {(V25 - VSENSE) / Avg_Slope} + 25】
字段说明:
- V25: 最小1.34V,最大1.52V,典型值1.43V
- Avg_Slope: 最小4.0,最大4.6,典型值4.3mv/℃
- VSENSE: ADC采集到的电压
ADC值转电压值计算公式:【电压 = ADC采集到的值 * 3.3 / 4096】
看上图我采集到的值为1703,先转换为电压值:1703*3.3/4096≈1.37
(1.43 - 1.37)/ 0.0043 + 25 ≈ 38.95℃
ends…