[STM32F1]

STM32 ADC多通道转换DMA模式与非DMA模式两种方法(HAL库)

[复制链接]
551|3
手机看帖
扫描二维码
随时随地手机跟帖
soodesyt|  楼主 | 2022-1-15 21:30 | 显示全部楼层 |阅读模式
DM, AC, AD, ST, dc
 Stm32 ADC 的转换模式还是很灵活,很强大,模式种类很多,那么这也导致很多人使用的时候没细心研究参考手册的情况下容易混淆。不知道该用哪种方式来实现自己想要的功能。网上也可以搜到很多资料,但是大部分是针对之前老版本的标准库的。昨天帮客户解决这个问题,正好做个总结:使用stm32cubeMX配置生成多通道采集的例子。
软件:STM32Cumebx  MDK
硬件:eemaker板(基于stm32F103c8的)
在百度搜索ADC多通道采集,大部分的都是基于采用dma模式才实现的。而我讲的使用非dma方法。首先有几个概念要搞清楚:
  扫描模式(想采集多通道必须开启):是一次对所选中的通道进行转换,比如开了ch0,ch1,ch4,ch5。Ch0转换完以后就会自动转换通道0,1,4,5直到转换完。但是这种连续性并不是不能被打断。这就引入了间断模式,可以说是对扫描模式的一种补充。它可以把0,1,4,5这四个通道进行分组。可以分成0,1一组,4,5一组。也可以每个通道配置为一组。这样每一组转换之前都需要先触发一次。
  Stm32 ADC的单次模式和连续模式。这两中模式的概念是相对应的。这里的单次模式并不是指一个通道。假如你同时开了ch0,ch1,ch4,ch5这四个通道。单次模式转换模式下会把这四个通道采集一边就停止了。而连续模式就是这四个通道转换完以后再循环过来再从ch0开始。
  另外还有规则组和注入组的概念,因为我这个例程只用到了规则组,就不多介绍这两个概念,想要弄清楚请自行查阅手册。
下面进入正题,配置stm32cubeMX。
120816_0932_Stm32cubeMx1.png
先使能几个通道,我这里设置为0、1、4、5.
然后就要配置ADC的参数:
120816_0932_Stm32cubeMx2.png
  目前经过我的测试,要想用非dma和中断模式只有这样配置可以正确进行多通道转换:扫描模式+单次转换模式+间断转换模式(每个间断组一个通道)。
  分析配置成这样的模式,扫描模式是在配置为多个通道必须打开的,stm32cubeMX上也默认好了,只能enable。单次转换模式是我不需要不停的去采集每个通道值,而是把四个通道采集完以后就让它停止。这里间断配置是关键,间断模式可以让扫描的四个通道进行分成四个组,stm32cubeMX参数里面number of Discontinous Conversions是配置间断组每个组有几个通道的,这里必须配置为1(否则在获取ad值得时候只能读取到每个间断组最后一个通道)。
生成mdk工程代码。这时候还没有完成,只是实现了ADC的初始化,需要采集这四个通道值得函数还要自己写。下面这个是我main函数的while循环:
copycode.gif
for(i=1;i<5;i++){HAL_ADC_Start(&hadc1);HAL_ADC_PollForConversion(&hadc1,0xffff);//等待ADC转换完成adcBuf=HAL_ADC_GetValue(&hadc1);printf("------ch:%d--%d-------\r\n",i,adcBuf);}HAL_ADC_Stop(&hadc1);HAL_Delay(1000); copycode.gif

  调用hal库接口函数也需要注意,HAL_ADC_Start一定要放在for里面,即每一个通道都要触发。四个通道都采集完了,再去调用HAL_ADC_Stop(&hadc1);结束本次ADC采集。

二、DMA模式
  下面就是我自己的DMA模式的ADC多通道转换了。
  先配置一些ADC的基本配置:
  引脚
1433771-20190103101413019-490910326.png

  时钟
1433771-20190103101441290-919811527.png
  这个时钟可以结合ADC设置里配置的采样时间结合计算出ADC转换的时间,进而换算出频率。
1433771-20190103181013961-1210111623.png
  接着配置DMA
  ADC是12位的,其实DMA只需要用Half Word就可以了,但实际中HAL_ADC_Start_DMA(ADC_HandleTypeDef* hadc, uint32_t* pData, uint32_t Length);
该函数中pData为32位的,也就是DMA必须配置为Word才可以。
1433771-20190103101534541-297647774.png
 配置ADC基本设置 
  这里要注意选择对不同的通道,一开始我就是没留意到这个问题,就只有一个通道 Channel10 在转换,后来查看就是Rank1、2、3全配置成  Channel10 了,所以只有这个通道在转换,这里这个提醒大家注意一下。
1433771-20190103101646823-460599975.png
1433771-20190103101716285-209305291.png
  中断配置
1433771-20190103101736909-627414768.png
  最后在main文件的main函数里的while循环里加入下面代码
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&AD_DMA, 5); //启用DMA的ADC转换,AD_DMA 0~3 对应ADC 0~3,这里注意最后一个参数的大小printf("AD_DMA_0 = %d\r\n",AD_DMA[0]);printf("AD_DMA_1 = %d\r\n",AD_DMA[1]);printf("AD_DMA_2 = %d\r\n",AD_DMA[2]);HAL_Delay(500);
  注意:在while循环前要加ADC校准
    HAL_ADCEx_Calibration_Start(&hadc1);    //AD校准
  串口打印结果如下,至于怎样串口打印这里就不多说了,想知道的可以看https://www.cnblogs.com/xingboy/p/9522940.html
   1433771-20190103102334632-1715513577.png
补充:使用定时器与DMA中断定时采集
  上面只是单纯的一直采集的,如果想要用到中断的话就可以按下面的方式来,ADC配置跟上面说的DMA模式一样:
  先配置定时器中断,怎么配置可以参考我的另一个**https://www.cnblogs.com/xingboy/p/9897500.html
  接着在 main 函数的 while 循环前打开定时器中断
    HAL_TIM_Base_Start_IT(&htim3); //启动定时器中断
  然后重写定时器中断回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){    HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&AD_DMA, 5); //启用DMA的ADC转换,AD_DMA 0~3 对应ADC 0~3,这里注意最后一个参数的大小}
  这里要注意了,我调试的时候发现HAL_ADC_Start_DMA()函数中最后一个参数的大小起码要比你定义的AD_DMA数组大2,不过不能大于2倍,前面的使用这个函数的时候也是要这样,数据太小,会导致后面的AD通道采集不了数据,大于2倍程序会一直卡住,至于为什么这样子我也还没搞懂,知道的可以告诉我一声。【补充:关于这个参数大小的问题,我查了一些资料,一般ADC每次读进来的数据都是2个字节大小的半字,所以3个通道读进来的一般一次6个字节这样,4个通道类似,而这里的最后一个参数代表的就是要传输的字节数,所以这个参数要根据通道个数设置,通常ADC读入一个半字,也就是uint16_t,你设为Word,那么会去读一个uint32_t是4个字节,其实这个我也还不是很懂,不知道对不对的欢迎大家指出】
  最后写DMA中断服务函数
copycode.gif
void DMA1_Channel1_IRQHandler(void){  /* USER CODE BEGIN DMA1_Channel1_IRQn 0 */        /*自己添加代码部分*/    HAL_ADC_Stop_DMA(&hadc1); //停止DMA的ADC转换,AD_DMA 0~3 对应ADC 0~3    HAL_TIM_Base_Stop_IT(&htim3);//关闭定时器    printf("AD_DMA_0 = %d\r\n",AD_DMA[0]);    printf("AD_DMA_1 = %d\r\n",AD_DMA[1]);    printf("AD_DMA_2 = %d\r\n",AD_DMA[2]);    HAL_TIM_Base_Start_IT(&htim3); //重新开启定时器      /* USER CODE END DMA1_Channel1_IRQn 0 */  HAL_DMA_IRQHandler(&hdma_adc1);  /* USER CODE BEGIN DMA1_Channel1_IRQn 1 */    //__HAL_DMA_CLEAR_FLAG(&hdma_adc1, __HAL_DMA_GET_TC_FLAG_INDEX(&hdma_adc1)); //清楚标志位  /* USER CODE END DMA1_Channel1_IRQn 1 */} copycode.gif



使用特权

评论回复
soodesyt|  楼主 | 2022-1-15 21:31 | 显示全部楼层
补充一个 4 通道采集 DMA 模式:
   1433771-20190321113723977-1606783106.png
   1433771-20190321113751329-372747517.png
   1433771-20190321113823107-876716183.png

   1433771-20190321113854548-1365215618.png
       定义一个数组存放DMA数据
uint16_t AD_DMA[4];
  直接在 main 函数的 while 前面开启 ADC校验跟采集
HAL_ADCEx_Calibration_Start(&hadc1);              //AD校准HAL_ADC_Start_DMA(&hadc1, (uint32_t *)&AD_DMA, 8); //启用DMA的ADC转换,AD_DMA 0~3 对应ADC 0~3  
  while函数里打印DMA的值
        printf("AD0 = %d\r\n",AD_DMA[0]);        printf("AD1 = %d\r\n",AD_DMA[1]);        printf("AD2 = %d\r\n",AD_DMA[2]);        printf("AD3 = %d\r\n",AD_DMA[3]);        HAL_Delay(1000);
  打印结果如下
     1433771-20190321114417839-1522895263.png 1433771-20190321114450564-675219751.png 1433771-20190321114553609-1668897591.png 1433771-20190321114530037-1646455078.png


使用特权

评论回复
飞羽,| | 2022-1-22 21:40 | 显示全部楼层
需要DAC  ADC  高精运放,隔离器件,接口,仪放,MCU  电源模块  光耦的可以联系我,我司代理3peak,纳芯微,极海,爱浦,华联,联系方式13719034974

使用特权

评论回复
两只袜子| | 2022-1-25 10:57 | 显示全部楼层
学习学习

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

14

主题

2503

帖子

0

粉丝