HAL库STM32G0单通道ADC采样DMA传输定时器TIMER触发

这是一个能让你解放双手的ADC采样配置。因为你只需要在程序开始的时候配置好定时器、DMA、ADC,剩下的,就等着采集完成后在中断中处理AD相关数据就行了。 前面也写过STM32的基于DMA的AD转换功能,但不够完善。

STM32的ADC采样功能一直很强大,它又引入了DMA,同时定时器还能做为ADC开始转换的触发源,不得不说,真的好使!当然了,其它家的cotex-m核的MCU也有类似功能,这里只是以ST的芯片为例,做为演示。

本文实现的AD功能为:通过定时器每隔一段时间,自动触发ADC转换,并通过DMA自动把转换后的值存入某变量或数组,存储完成后触发中断。接下来说一下具体的配置和代码,以及调试时的注意事项。

一、创建工程

使用STM32CubeMX创建工程,芯片选择STM32G030F6Px。创建后,使能下载调试接口:SW。

二、时钟树配置

STM32G030系列,在使用内部RC做为时钟源的情况下,最高可以运行在64MHZ。这里,我们选择内部RC做为时钟源,默认16MHZ,然后使用PLL倍频使其达到64MHZ。

三、配置定时器3

如图所示,这里对定时器的时钟源(我们要用64MHZ)做了640分频,分频后频率为100KHZ。计数周期2000,在100KHZ频率下对应20ms。下面的:

Triger Event Selection TRGO :Update Event

表示,每次TIMER3计数溢出更新时,做为一个触发事件。

注意:这里虽然用到了定时器中断,但是TIM3中的NVIC Settings中的中断可以使能,也可以不使能。个人测试,都能实现效果。

四、ADC采样配置

4.1 DMA配置

这里使用ADC1的通道0来采集数据,使能通道0后,先把DMA功能打开。模式为循环,数据宽度为半字:Half Word。其它的,没什么要改的。如下图所示:

4.2 ADC_Settings

接着ADC_Settings相关的参数,如下图:

Clock Prescaler:时钟分频,时钟频率越高,采样速度越快,越费电,精准度也会略微低一点;

Resolution:分辨率,支持6位、8位、10位、12位可选,这个功能不错,不过我默认习惯用12位。

Data Alignment:数据对其,正常人习惯都是右对齐,即Right alignment。

Sequencer:有两个选项,Sequencer set to fully configurable 和Sequencer set to not fully configurable。单通道的时候不用管,多通道的时候我再说。

Scan Convertion Mode:多通道采样的时候会用到,单通道的时候默认即可。

Continuous Convertion Mode:连续转换模式,开启后,AD转换功能一直运行。这不是我们需要的,我们需要它在特定的时刻转换,且每次只转换一次。

Discontinuouse Convertion Mode:非连续转换模式,又叫做间断模式。单通道时不用管,多通道时再考虑。

DMA Continuous Requests:这个参数是用来配置ADC与DMA之间的数据传输行为,这里设置为使能。第四步一开始先打开DMA,就是因为DMA使能的情况下,这个参数才能设置为使能。

这个参数使能后,DMA会不停地请求ADC转换数据。

后面的参数用不到,暂时不用管。

4.3 ADC_Regular_ConversionMode

然后是ADC_Regular_ConversionMode相关参数的设定。

SamplingTime Common 1和SamplingTime Common 2:这里是为不同的组别设置相应的采样时间,我们只有一个通道,所以只关注SamplingTime Common 1即可。

时间这里选择79.5 Cycles,即一次AD转换需要的周期。周期越大,时间越久,但是会越准确;反之,会更快,但不精准。

Number Of Conversion:转换通道的数量,这里是单通道,所以写1即可。

External Trigger Conversion Source:选择外部触发源。即,你想让什么事件来启动AD转换。这里选择Timer3 Trigger Out event,即步骤3中设置的Updata Event。

Exteranl Trigger Conversion Edge和Trigger Frequency,暂时不用管。

Rank:如果是多通道,这里会有多个rank。但是这里是单通道,所以只有一个。下面选择通道和希望的转换时间。

五、配置GPIO

设置PC15和PA3为输出模式,依次命名为LED和PA3。

这两个管脚做什么用? 后面就知道了。

六、生成工程

基本配置都完成了,进入Project Manger页面。

输入工程名称,选择保存路径,IDE工具,版本等,然后点击代码生成按钮。

七、完善代码

7.1 变量定义

添加部分变量定义或声明:

/* Private macro -------------------------------------------------------------*/ 
  /* USER CODE BEGIN PM */
  extern TIM_HandleTypeDef htim3;
  extern ADC_HandleTypeDef hadc1;
  uint16_t dat_buf[3] = {0};
  uint8_t con_cplt_flag = 0;
/* USER CODE END PM */

7.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,3);
/* USER CODE END 2 */

7.3 AD转换完成中断回调函数

AD转换完成,并通过DMA存入数组之后,会触发一个中断,在这个中断中,我们取反一个GPIO,通过示波器观测IO翻转的周期,可以确认AD转换是按照定时器定的时间去触发并转换的。

否则,我们无法明确确认这个转换间隔到底是多久。

代码如下:

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc) 
  {
  	con_cplt_flag = 1;
  	HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
}

7.4 定时器中断功能验证(可选)

前面提到过,TIM3做为AD转换的触发源后,自身的中断使能可以打开,也可以不打开。无论哪种,AD定时触发的功能都是可以正常运行的。

不过有个问题,定时器还能用过普通定时器产生定时中断吗?

答案是可以,但是ST-LINK仿真你是仿不出来的。

经测试,我同时编写了TIM3定时中断回调函数和AD转换完成中断回调函数。使用ST-LINK仿真观测,两个中断回调函数中打断点,TIM3定时中断回调函数仅在复位后执行一次或两次。之后就再也进不去了。

去掉AD转换完成中断回调函数中的断点,只保留TIM3的断点,就又能进去了。所以,情况有点诡异~~~

因此,这种情况下,我不再依靠断点。而是通过在TIM3定时中断回调函数中取反GPIO的方式来判断它是否进入中断,代码如下:

代码:

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) 
  {
  	HAL_GPIO_TogglePin(PA3_GPIO_Port,PA3_Pin);
}

八、程序下载,观测波形

程序完善后,保存、编译、下载。先看数组中是否采到数据,如下:

有数据,OK。

然后是AD转换完成中断回调函数,是多久执行一次,通过管脚LED的翻转周期,可以判断,如下:

翻转周期60ms。

咦?不应该是20ms吗?

是20ms,但那个是单次的时间间隔。AD转换时,我们定义了一个3元素的数组,所以要转换3次,也就是60ms了。

HAL_ADC_Start_DMA(&hadc1,(uint32_t *)dat_buf,3); 

接下来是TIM3定时中断,观测PA3管脚的波形,如下:

可以看到翻转周期是20ms,和TIM3中的配置是一致的。

至此,单通道ADC转换定时器自动触发+DMA传输,实现方式记录完成。

有需要源码的,关注公众号:单片机爱好者,回复关键词:026,即可获取下载链接。

打完收工!

发表评论

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