作者:螺丝松掉的人
最近对 stm32 的 adc 使用的比较多,也稍微有一点深入,之前都是简单使用,没有过多研究,发现还是有很多很意思的地方,并且一些功能在网上的介绍和使用也是比较少的,就想分享一下自己摸索的过程和思考,有些功能可能我用的也不太对或者有更好的用法,欢迎大家交流
基本功能
我们通过 adc 可以直接读取到电压的大小,通过对读取到的电压进行转换可以获取到电流、温度等模拟信号的数字转换结果。在 stm32 中通常具备 3 个 adc ,每个 adc 又具备十多个通道,每个通道可以对应引脚采集数据(部分通道仅用于采集内部数据,没有引出外部引脚),其中规则通道最多有16路,注入通道最多有4路。有的 adc 之间还可以配置为主从模式,进行交叉采样等更复杂高级的操作,一般电机控制时用的比较多。但是需要注意的是,对于 adc 中的多个通道,一般是不能直接指定通道进行读取的,除非将通道使能为注入模式(稍后介绍)。之前我一般 adc 只会用到 1 个通道,是否指定其实都只能读到那一个通道,所以并没有注意到这个地方。但要写设备驱动就必须要做好通用性,为用户屏蔽好这些比较绕的问题。
规则模式
规则模式(Regular)就是一般的普通模式,也是使用的比较多的。一般可以设置三个寄存器序列,可以根据自己的需要将多个规则通道划分到指定序列中的指定位置,开启 adc 序列转换后,就会开始按顺序对序列中的通道进行扫描转换。单数需要注意的是,每个 adc 只有一个规则数据寄存器,只够存储一个通道的数据,数据是会被覆盖的。也就是说在每个通道的数据读取完成以后,我们需要及时取出,并按顺序进行区分。可以用一个数组去存取转换的数据,之前设定的序列,也就知道的指定通道对应的数据。
一般还可以采用结合 DMA 的方式,在读取到数据后,将数据按顺序进行逐个装填,我们也能得到指定通道顺序的数据。
注入模式
注入模式有点像中断,可以选择多种触发源,也可以用户软件触发,可以直接打断正在进行的规则通道转换。最多可以使能 4 个通道作为注入通道,并且每个通道都具备自己的数据寄存器,不会存在数据覆盖的问题。对于 DMA 资源紧张,不想引入阻塞,不便于维护转换序列时,可以选择采用该模式。
交叉模式
交叉模式需要将两个adc配置为主从关系进行,一般是 ADC1/2,具体可参考官方手册。
注意事项
规则通道数据寄存器只有一个,会存在数据覆盖问题,需要做好数据维护对应工作;
通道使能为注入模式后无法使用连续转换模式;
每个ADC只能使用4个注入模式通道,需要主要资源分配;
ADC的数据寄存器不会存在读取到脏数据的情况;
设备驱动对比
接下来分析对比一下主流RTOS的adc设备驱动都是怎么写的,是如何为用户屏蔽好这些比较绕的底层细节的。
RT-Thread
adc初始化设置如下,关闭连续采样模式:
1
2
3#define ADC1_CONFIG \
4
5 { \
6
7 .Instance = ADC1, \
8
9 .Init.DataAlign = ADC_DATAALIGN_RIGHT, \
10
11 .Init.ScanConvMode = ADC_SCAN_DISABLE, \
12
13 .Init.ContinuousConvMode = DISABLE, \
14
15 .Init.NbrOfConversion = 1, \
16
17 .Init.DiscontinuousConvMode = DISABLE, \
18
19 .Init.NbrOfDiscConversion = 1, \
20
21 .Init.ExternalTrigConv = ADC_SOFTWARE_START, \
22
23 }
24
25#endif /* ADC1_CONFIG */
均设置为规则通道,并放到同一个序列中:
1ADC_ChanConf.Rank = ADC_REGULAR_RANK_1;
读取函数:(入参有 channel,但并没有用)
1static rt_err_t stm32_adc_get_value(struct rt_adc_device *device, rt_int8_t channel, rt_uint32_t *value)
2{
3 ADC_HandleTypeDef *stm32_adc_handler;
4 RT_ASSERT(device != RT_NULL);
5 RT_ASSERT(value != RT_NULL);
6 stm32_adc_handler = device->parent.user_data;
7 /* Wait for the ADC to convert */
8 HAL_ADC_PollForConversion(stm32_adc_handler, 100);
9 /* get ADC value */
10 *value = (rt_uint32_t)HAL_ADC_GetValue(stm32_adc_handler);
11 return RT_EOK;
12}
去adc设备设备驱动看看怎么维护的:
1static rt_ssize_t _adc_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
2{
3 rt_err_t result = RT_EOK;
4 rt_size_t i;
5 struct rt_adc_device *adc = (struct rt_adc_device *)dev;
6 rt_uint32_t *value = (rt_uint32_t *)buffer;
7 for (i = 0; i < size; i += sizeof(int))
8 {
9 result = adc->ops->convert(adc, pos + i, value);
10 if (result != RT_EOK)
11 {
12 return 0;
13 }
14 value++;
15 }
16 return i;
17}
可以看出来就是我们说的第一种方法,所有通道为规则模式,同一转换序列,逐个读取并对按序存储到数组中。
Refer
STM32H723/733, STM32H725/735 and STM32H730 Value line advanced Arm®-based 32-bit MCUs - Reference manual
野火]STM32库开发实战指南——基于野火指南者开发板 文档 (embedfire.com)
————————————————
版权声明:本文为RT-Thread论坛用户「螺丝松掉的人」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:
https://club.rt-thread.org/ask/article/3c37ce454d8fecc4.html
———————End———————
👇 点击阅读原文进入官网