STM32CubeMX能够极大减小STM32外设配置的工作量,因此作者也借助空闲时间对STM32CubeMX相关配置进行了学习,本文介绍如何利用STM32CubeMX配置ADC采样,记录了作者学习过程中遇到的问题及解决办法,使大家少走弯路,并方便以后复习


目录

  • 1、单通道轮询
  • 2、单通道中断
  • 3、单通道DMA
  • 4、多通道轮询
  • 5、多通道中断
  • 6、多通道DMA


1、单通道轮询

cubemx配置nvic不通过_单片机


先选择所使用的MCU,这里我使用的是STM32F407ZGT系列

cubemx配置nvic不通过_单片机_02


修改一下DEBUG功能,否则后续无法调试

cubemx配置nvic不通过_cubemx配置nvic不通过_03


修改时钟,采用外部晶振

cubemx配置nvic不通过_单片机_04


配置一串口,用于打印采集的ADC值

cubemx配置nvic不通过_stm32_05


这里我采用ADC1的通道0,并开启连续采样模式,否则每次开启ADC采样后只进行一次采样

cubemx配置nvic不通过_stm32_06


开启ADC全局中断(如果只采用轮询采样就不用开启,这里开启是为了方便后面演示中断采样

cubemx配置nvic不通过_单片机_07


修改一下ADC全局中断的优先级

cubemx配置nvic不通过_单片机_08


在HCLK处输入其时钟最高频率168,按回车,其余时钟会自动配置好,一般情况下无需改动

cubemx配置nvic不通过_单片机_09


cubemx配置nvic不通过_cubemx配置nvic不通过_10


勾选上后,每个外设会单独保存至一个.c文件,便于查看

cubemx配置nvic不通过_数据_11


cubemx配置nvic不通过_stm32_12


生成并打开文件

cubemx配置nvic不通过_单片机_13


在main.h中包含头文件stdio.h

cubemx配置nvic不通过_stm32_14


在usart.c文件中加入fputc函数,加入后才能使用printf函数进行打印数据

cubemx配置nvic不通过_数据_15

在while循环中添加以上代码,由于开启了连续转换模式,即hadc1.Init.ContinuousConvMode = ENABLE;因此每次转换完成后都要调用HAL_ADC_Stop来关闭ADC;如果不想每次都调用该函数来关闭ADC,可以关闭连续转换模式,即hadc1.Init.ContinuousConvMode = DISABLE,这样每次开启ADC转换以后只会进行一次采样,这样就不需要每次都关闭ADC了

实验现象如下图:

cubemx配置nvic不通过_数据_16

2、单通道中断

单通道中断采样配置过程与单通道轮询采样相同,利用cubemx配置完成后有两种开始ADC采样的方法

方法一

cubemx配置nvic不通过_ci_17


在主函数中加入上述代码,__HAL_ADC_ENABLE_IT(&hadc1,ADC_IT_EOC);用于打开ADC转换完成中断,HAL_ADC_Start(&hadc1);用于开启ADC采样,这两个函数一定要放到外设初始化函数的后面,否则串口函数初始化未完成,在ADC中断中使用printf函数可能会卡死

cubemx配置nvic不通过_ci_18


在adc.c文件中加入上面代码,HAL_ADC_ConvCpltCallback是ADC中断的回调函数,其原型是个弱函数,重新定义后原来的函数就失去作用了

cubemx配置nvic不通过_ci_19


最后将adc.c文件中的通道转换结束标准修改为ADC_EOC_SEQ_CONV,否则只会进入一次中断

ADC_EOC_SEQ_CONV:在所有通道转换完成后进入中断
ADC_EOC_SINGLE_CONV:单个通道转换完成后进入中断
多个通道时,两种均可采用,单通道采样时需使用ADC_EOC_SEQ_CONV才能连续进入中断(经测试)

最后将程序下载到单片机中,现象如下:

cubemx配置nvic不通过_单片机_20


方法二

我们利用HAL_ADC_Start_IT开启adc中断,该函数比较特殊,调用一次该函数就打开了ADC转换完成中断开启ADC采样

cubemx配置nvic不通过_cubemx配置nvic不通过_21


在主函数中直接调用HAL_ADC_Start_IT即可

3、单通道DMA

dma有两种模式,分别为circular和normal

circular模式:dma的circular模式只需要调用一次dma开启函数,dma就会持续的搬运数据,提高了数据的刷新速度,但是在circular模式下,不管adc新的一轮数据采集是否完成,有可能直接将旧数据搬运走

normal模式:该模式下,dma启动函数调用一次,dma通道只会搬运一次数据,这样每调一次dma启动函数,dma只会搬运一次数据,等待数据传输完成后再次开启dma启动函数,这样更能保证adc数据采集的可靠性

circular模式:

cubemux配置步骤如下(时钟配置等已略去):

cubemx配置nvic不通过_ci_22


这里我们同样用ADC1的通道IN0来测试,开启adc连续采集模式

cubemx配置nvic不通过_ci_23


修改ADC采样时间,通常采样时间越长,adc采样精度越高

cubemx配置nvic不通过_数据_24


选用dma的circular模式

cubemx配置nvic不通过_stm32_25


返回开启dma连续请求(若不开启,只能进入一次dma采集完成中断函数

uint16_t AD_value=0;
 float f_AD_value;

打开工程文件,在文件中添加上面代码,AD_value用于保存测量的adc原值,f_AD_value保存计算得到的电压值

HAL_ADC_Start_DMA(&hadc1,(uint32_t*)&AD_value,sizeof(AD_value));

在主函数中调用HAL_ADC_Start_DMA开启adc采样,同时开启dma传输数据(调用一次即可

cubemx配置nvic不通过_cubemx配置nvic不通过_26


最后在dma中断函数中添加上述代码,将adc采集完成的数据打印出来

normal模式:

normal模式下,我们的想法是调用一次HAL_ADC_Start_DMA函数,adc进行一次数据采集,dma搬运一次数据,我们在上面配置完成的代码中做简单修改就能实现

cubemx配置nvic不通过_stm32_27


关闭adc连续采集模式,这样开启一次adc,只会进行一次数据采集

cubemx配置nvic不通过_ci_28

将dma模式修改为normal

cubemx配置nvic不通过_单片机_29


修改dma中断函数

HAL_ADC_Start_DMA(&hadc1,(uint32_t*)&AD_value,sizeof(AD_value));

最后在主函数中调用HAL_ADC_Start_DMA开启adc采样,同时开启dma传输数据(每调用一次只会采集一次数据

4、多通道轮询

cubemx配置nvic不通过_cubemx配置nvic不通过_30


这里用ADC1的通道IN0、IN1、IN2、IN3四个通道作演示,多个通道时必须开启间断模式,并且每个间断组中只有一个通道,否则每次只能读取到每组最后一个通道的值

cubemx配置nvic不通过_单片机_31


设置通道转换顺序

uint16_t AD_value[4]={0};

打开工程文件,创建一数组用于存储四个通道的ADC值

for(i=0;i<4;i++){
HAL_ADC_Start(&hadc1);
HAL_ADC_PollForConversion(&hadc1,10);
AD_value[i]=HAL_ADC_GetValue(&hadc1);
printf("PA%d:%d\r\n",i,AD_value[i]);
printf("PA%d:%.3f v\r\n",i,AD_value[i]*3.3/4096);
}
HAL_Delay(500);

在主循环中添加上述代码,HAL_ADC_Start必须放在for循环中,否则只能采集第一个通道的ADC值;HAL_ADC_PollForConversion用于轮询转换,是一个阻塞函数,等待转换完成,参数10是阻塞时间

5、多通道中断

多通道情况下使用中断来读取数据理论上是可行的,但是读取的数据会混淆,即无法确定读取的数据是属于哪一个通道的,因此我们不使用

6、多通道DMA

cubemx配置nvic不通过_ci_32

cubemx配置nvic不通过_数据_33

cubemx配置nvic不通过_stm32_34


开启DMA并采用circular模式

uint16_t AD_value[4]={0};

定义一数组用于保存ADC采样值

cubemx配置nvic不通过_cubemx配置nvic不通过_35


在外设初始化函数下面调用 HAL_ADC_Start_DMA(&hadc1,(uint32_t*)AD_value,sizeof(AD_value));启动ADC转换和DMA数据传输

cubemx配置nvic不通过_stm32_36


最后在DMA接收完成中断中将采集到的ADC数据打印出来

经测试:DMA接收中断在数据接收缓冲区满了后触发,这里的缓冲区是数据AD_value[4]

注意:
通常为了方便使用一些滤波算法,我们可以将缓冲区数据创建的更大一些(为通道数的整数倍),这里我们采用了4个通道,我们可以将缓冲数组创建为AD_value[40]
则AD_value[0]、AD_value[4]、AD_value[8]…AD_value[36]均为通道IN0的值
AD_value[1]、AD_value[5]、AD_value[9]…AD_value[37]均为通道IN1的值,
AD_value[2]、AD_value[6]、AD_value[10]…AD_value[38]均为通道IN2的值,
AD_value[3]、AD_value[7]、AD_value[11]…AD_value[39]均为通道IN3的值,

cubemx配置nvic不通过_cubemx配置nvic不通过_37


我们同样在dma接收完成中断中将数据打印出来

cubemx配置nvic不通过_cubemx配置nvic不通过_38


实验现象如上,只打印了一次数据,表示只进入了一次DMA中断中,并且主循环中的程序也不再继续执行,程序卡死了

cubemx配置nvic不通过_数据_39


可以在进入DMA中断函数时调用HAL_ADC_Stop_DMA来关闭ADC采集,当需要采集ADC的时候再调用 HAL_ADC_Start_DMA即可,这里为了演示在中断函数结束时又重新开启了ADC

cubemx配置nvic不通过_cubemx配置nvic不通过_40


这样就能连续采集打印数据了