STM32CubeMX | HAL库的ADC多通道数据采集(轮训、DMA、DMA+TIM)、读取内部传感器温度

目录

注:本片文章以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个周期

示例:


STM32F1系列的时钟主频一般设置为了12M,采样时间的设置所对应采样频率如下图所示:

![](https://img-blog.csdnimg.cn/de1135452e21490a94b671995f48824f.png)

知道了最短时间后,当ADC时钟主频为12M并且采样时间为1.5个周期时,ADC采样两点的时间必须大于1.17us。

而STM32F031系列的ADC时钟主频为14MHz,采样时间的设置所对应采样频率如下图所示:

![](https://img-blog.csdnimg.cn/4616052a159b4c6da44230ee81f9a4a0.png)

当ADC时钟主频为14M并且采样时间为1.5个周期时,ADC采样两点的时间必须大于1us。

#### <a name="2_56">;</a>  2、轮训方式的多通道采集

##### 2.1 方式一:间断模式+扫描模式

我开启了通道0、1、2以及内部温度读取通道一共四个通道:

![](https://img-blog.csdnimg.cn/20200820144708671.png#pic_center)
![](https://img-blog.csdnimg.cn/20200820144717271.png#pic_center)
如果想使用轮训方式并且不使用DMA的多通道采集,那么就要配置为 **【单次模式+间断模式】**,并且将Number Of Discontinuous Conversions为1,也就是每个通道分成了一个组,配置如下图:
![](https://img-blog.csdnimg.cn/20200820145205371.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxMTUzNDcxNTAz,size_16,color_FFFFFF,t_70#pic_center)

由于我这里设置间断数为1,也就是将4个通道分成了4组,那么我每次采集的时候都需要手动去触发ADC采集,也就是调用一次HAL_ADC_Start函数,完整代码如下:

![](https://img-blog.csdnimg.cn/2020082014582133.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxMTUzNDcxNTAz,size_16,color_FFFFFF,t_70#pic_center)
我将通道0分别接到3.3V和GND上,4通道采集运行效果如下:

![](https://img-blog.csdnimg.cn/20200820150008132.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxMTUzNDcxNTAz,size_16,color_FFFFFF,t_70#pic_center)

##### <a name="22__72">;</a>  2.2 方式二:完全轮训

完全使用轮训的方式不能使用 **扫描模式**(理论上来说多通道需要使用扫描模式,而不是完全轮训),但是使用STM32CUBEMX配置多通道扫描模式不无法被关闭的,所以我们先用STM32CUBEMX配置成一个通道:
![](https://img-blog.csdnimg.cn/0b4c62f299d54bcf89ba61ea0aee0eca.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAeS16aGVuZw==,size_20,color_FFFFFF,t_70,g_se,x_16)

然后读取ADC采集数值的函数是这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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);
}

#### 3、DMA实现多通道采集 通过DMA实现多通道数据采集, **要将连续模式和间断模式关闭!** 如果连续模式开启,那么通过DMA传输到的数组中,每个通道所采集到的值对应数组中的一个位置就是不固定的。 例如你开启了IN0~IN3这四个通道,并通过DMA将这四个通道的数据放到ADC_Value这个大小为4的u16类型数组, 你在第一次采集的时候IN0通道的数值通过DMA被放在ADC_Value[0], 第二次采集的时候IN0采集到的数值就可能被放到了ADC_Value[1], 这样的话就极不方便我们对每个通道的数据进行分析和提取。 配置如下,将连续转换模式和间断转换模式关闭,并开启ADC的DMA通道,将DMA的模式配置为周期模式: ![](https://img-blog.csdnimg.cn/20200820152200735.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxMTUzNDcxNTAz,size_16,color_FFFFFF,t_70#pic_center) ![](https://img-blog.csdnimg.cn/ddd590ed84b44942863728c7b84ff639.png) 代码如下: ![](https://img-blog.csdnimg.cn/c457e847f73847ceb3a35f112f66667e.png) 4个通道采集运行效果如下: ![](https://img-blog.csdnimg.cn/20200820152427227.png#pic_center) #### <a name="4timdma_136">;</a> 4、TIM+DMA实现多通道采集 在不使用DMA的情况下,定时器控制ADC进行数据采集只能是单通道!如果开启了多通道,读取到的ADC采集值只会是最后一个通道的值! 所以,要想使用定时器控制ADC采集 **多通道**,必须使用DMA! CubeMX配置如下,使能ADC的DMA: ![](https://img-blog.csdnimg.cn/42aa3e7658df4d89ac32b7f2a1ebc643.png) ADC的触发方式设置为定时器3的触发事件: ![](https://img-blog.csdnimg.cn/c4ff90b3330b44fda0a609316e2370db.png) 采样时间设置为了55.5个周期,对应采样频率为176.47KHz,所以定时器3的频率设置要低于176.47KHz。 设置定时器3分频系数为72,重载值为10,得到 `72MHz / 72&#x5206;&#x9891; / 10 = 100KHz`的定时器3: ![](https://img-blog.csdnimg.cn/a50e71979339457e90f451449ec69d86.png) 代码如下: ![](https://img-blog.csdnimg.cn/c418cbe4d5b340a2ae94438927728da5.png) 运行结果如下: ![](https://img-blog.csdnimg.cn/534899ff62e24bcb9909ffaff4b29065.png) 当然也可以选择进行一组数据,例如将32个点为一组数据进行采集,设置如下: ![](https://img-blog.csdnimg.cn/a08aa8f0ae7c4cd8bfd70d40a980b3e1.png) 运行结果如下: ![](https://img-blog.csdnimg.cn/eb3feca6e37140eeb65f794634e86da3.png) #### 5、补充:内部温度传感器ADC通道 ![](https://img-blog.csdnimg.cn/87fe51b0eb7b447798ade02be31b4329.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxMTUzNDcxNTAz,size_16,color_FFFFFF,t_70) 上面图片是截取在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采集到的电压 ![](https://img-blog.csdnimg.cn/da0745b68e874c41b42573887a74e20e.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxMTUzNDcxNTAz,size_16,color_FFFFFF,t_70) ADC值转电压值计算公式:【电压 = ADC采集到的值 * 3.3 / 4096】 看上图我采集到的值为1703,先转换为电压值:1703*3.3/4096≈1.37 **(1.43 - 1.37)/ 0.0043 + 25 ≈ 38.95℃** ends...