上一篇文章介绍了STM32的单通道ADC采样功能,通过定时器定时触发AD转换,然后借助DMA自动传输到指定的数组。完整实现了模拟量采集的自动化、零干预,坐等收数据就行。
但是上一篇文章只实现了单个通道的采样,如果要实现多个通道的ADC采样、定时器触发、DMA传输,如何实现?
方法是类似的,这篇文章教你实现。
一、创建工程并配置时钟树
操作和上一篇文章一样,所以快速掠过。使用STM32CubeMX创建工程,芯片选择STM32G030F6Px,使能下载调试接口:SW。
选择内部RC做为时钟源,默认16MHZ,然后使用PLL倍频使其达到64MHZ。
二、配置定时器3
如图所示,这里对定时器的时钟源(我们要用64MHZ)做了640分频,分频后频率为100KHZ。计数周期这里调整为500,在100KHZ频率下对应5ms。因为用到了多个AD转换通道,20ms转换一个通道的话,需要的时间比较久。下面的:
Triger Event Selection TRGO :Update Event
表示每次TIMER3计数溢出更新时,做为一个触发事件。
三、ADC采样配置
3.1 DMA配置
采集通道这里选择了AIN4、AIN5、AIN6做为模拟量的输入采集通道。使能这三个通道后,先把DMA功能打开。模式为循环,数据宽度为半字:Half Word。
3.2 ADC_Regular_ConversionMode
和上一篇不一样,先进行ADC_Regular_ConversionMode的设置,稍后你就知道为什么了。
SamplingTime Common 1和SamplingTime Common 2:这里是为不同的组别设置相应的采样时间,我们虽然使能了多个通道,但并没有进行分组,所以只关注SamplingTime Common 1即可。
时间这里选择79.5 Cycles,即一次AD转换需要的周期。周期越大,时间越久,但是会越准确;反之,会更快,但不精准。
Number Of Conversion:转换通道的数量,这里使能了3个通道,所以写3。
External Trigger Conversion Source:选择外部触发源。这里选择Timer3 Trigger Out event,即步骤2中设置的Updata Event。
Exteranl Trigger Conversion Edge和Trigger Frequency,暂时不用管。
Rank:即通道排列。这里使能了3个通道,所以有3个。如果不显示,刷新一下即可。按照从低到高的顺序,依次选择三个通道,并配置采样时间。
3.3 ADC_Regular_ConversionMode
然后才是ADC_Regular_ConversionMode,如下图:
Clock Prescaler:时钟分频。
Resolution:分辨率。
Data Alignment:数据对齐。
Sequencer:有两个选项,Sequencer set to fully configurable 和Sequencer set to not fully configurable。默认不动。
Scan Convertion Mode:扫描转换模式,多通道转换时要使能(单通道时无法使能)。例如,使能了4、5、6三个通道,使能扫描转换模式后,只要开始转换,就会依次转换通道4、通道5、通道6,省去了手动转换的麻烦。
Continuous Convertion Mode:连续转换模式。
Discontinuouse Convertion Mode:非连续转换模式,又叫做间断模式。
DMA Continuous Requests:设置为使能。
其它的参数功能描述参照上一篇文章。
四、配置GPIO
设置PC15为输出模式,命名为LED。目的上一篇文章提到了,转换完成时翻转IO,用来验证其转换时序是否遵循定时器的定时中断。
五、生成工程
这里我命名为:MULTI_ADC_TIMER_DMA01。
六、完善代码
6.1 变量定义
添加部分变量定义或声明:
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
extern TIM_HandleTypeDef htim3;
extern ADC_HandleTypeDef hadc1;
uint16_t dat_buf[10] [3]= {0};
uint8_t con_cplt_flag = 0;
/* USER CODE END PM */
这里定义了一个二维数组dat_buf[10] [3],目的是让4、5、6这三个通道连续转换10次,然后发生一次转换完成中断。
6.2 AD转换校准与启动
依次进行校准、定时器启动、AD转换启动,代码如下:
/* USER CODE BEGIN 2 */
HAL_ADCEx_Calibration_Start(&hadc1);
HAL_TIM_Base_Start_IT(&htim3);
HAL_ADC_Start_DMA(&hadc1,(uint32_t *)dat_buf,30);
/* USER CODE END 2 */
注意函数HAL_ADC_Start_DMA中,最后一个参数的值,3个通道,转换10次,所以数据是30个。
6.3 AD转换完成中断回调函数
上一篇提过,AD转换完成,并通过DMA存入数组之后,会触发一个中断。在这个中断中,我们取反一个GPIO,通过示波器观测IO翻转的周期,进而确认AD转换的触发时间间隔。
/* USER CODE BEGIN 4 */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
con_cplt_flag = 1;
HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
}
/* USER CODE END 4 */
七、程序下载,观测波形
程序完善后,保存、编译、下载。
中断转换完成的回调函数中,打个断点:
然后把数组添加到观察窗口,接着点击:RUN,可以看到数组中的数据开始更新。
第一个通道数值大,因为我接了一个电位器,大约0.6V左右的电压。剩下两个通道基本都类似接地,所以数值接近0。
最后是每次转换之间的时间间隔,上示波器!
可以看到,配置的IO每隔50毫秒翻转一次,也就是每隔50毫秒触发一次AD转换。因为前面定义了一个10*3的二维数组,即每个通道要转换10次。每次间隔5ms,10次就是50ms。而这三个通道依次转换,是没有5ms间隔的,只有那个79.5个周期。
想要源码的,在公众号内部回复关键词:027,即可获取下载连接。
重点来了!
最后还是要说一下。多通道ADC转换、定时器触发、DMA传输,看起来比单通道更厉害,但这种用法其实有些鸡肋。因为对STM32的AD转换器来说,AD转换器就一个或两个,只是外面有多个通道。而多通道转换时,必然要进行通道间的切换。切换时,加上寄生电容的影响,上一个通道的电压就会影响到下一个通道,造成采样数据不准确。
所以,如果是采样精度要求不高的场合,这样用是能接受的。如果对精度要求很高,那就要慎重考虑了。
打完收工!我是单片机爱好者-MCU起航!