STM32G0多通道ADC采样定时器触发DMA传输

上一篇文章介绍了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起航!

发表评论

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据