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

 

1、非DMA模式(转)html

  说明:这个是本身刚作的时候百度出来的,不是我本身作出来的,由于感受有用就保存下来作学习用,原文连接:https://blog.csdn.net/qq_24815615/article/details/70227385,下面第二部分我会补充本身的DMA模式的方法。数组

  Stm32 ADC 的转换模式仍是很灵活,很强大,模式种类不少,那么这也致使不少人使用的时候没细心研究参考手册的状况下容易混淆。不知道该用哪一种方式来实现本身想要的功能。网上也能够搜到不少资料,可是大部分是针对以前老版本的标准库的。昨天帮客户解决这个问题,正好作个总结:使用stm32cubeMX配置生成多通道采集的例子。函数

软件:STM32Cumebx  MDK学习

硬件:eemaker板(基于stm32F103c8的)测试

在百度搜索ADC多通道采集,大部分的都是基于采用dma模式才实现的。而我讲的使用非dma方法。首先有几个概念要搞清楚:ui

  扫描模式(想采集多通道必须开启):是一次对所选中的通道进行转换,好比开了ch0,ch1,ch4,ch5。Ch0转换完之后就会自动转换通道0,1,4,5直到转换完。可是这种连续性并非不能被打断。这就引入了间断模式,能够说是对扫描模式的一种补充。它能够把0,1,4,5这四个通道进行分组。能够分红0,1一组,4,5一组。也能够每一个通道配置为一组。这样每一组转换以前都须要先触发一次。spa

  Stm32 ADC的单次模式和连续模式。这两中模式的概念是相对应的。这里的单次模式并非指一个通道。假如你同时开了ch0,ch1,ch4,ch5这四个通道。单次模式转换模式下会把这四个通道采集一边就中止了。而连续模式就是这四个通道转换完之后再循环过来再从ch0开始。.net

  另外还有规则组和注入组的概念,由于我这个例程只用到了规则组,就很少介绍这两个概念,想要弄清楚请自行查阅手册。3d

下面进入正题,配置stm32cubeMX。调试

Stm32cubeMx配置ADC多通道采集

先使能几个通道,我这里设置为0、一、四、5.

而后就要配置ADC的参数:

Stm32cubeMx配置ADC多通道采集

  目前通过个人测试,要想用非dma和中断模式只有这样配置能够正确进行多通道转换:扫描模式+单次转换模式+间断转换模式(每一个间断组一个通道)。

  分析配置成这样的模式,扫描模式是在配置为多个通道必须打开的,stm32cubeMX上也默认好了,只能enable。单次转换模式是我不须要不停的去采集每一个通道值,而是把四个通道采集完之后就让它中止。这里间断配置是关键,间断模式可让扫描的四个通道进行分红四个组,stm32cubeMX参数里面number of Discontinous Conversions是配置间断组每一个组有几个通道的,这里必须配置为1(不然在获取ad值得时候只能读取到每一个间断组最后一个通道)。

生成mdk工程代码。这时候尚未完成,只是实现了ADC的初始化,须要采集这四个通道值得函数还要本身写。下面这个是我main函数的while循环:

for(i=1;i<5;i++) { HAL_ADC_Start(&hadc1); HAL_ADC_PollForConversion(&hadc1,0xffff);//等待ADC转换完成 adcBuf[i]=HAL_ADC_GetValue(&hadc1); printf("------ch:%d--%d-------\r\n",i,adcBuf[i]); } HAL_ADC_Stop(&hadc1); HAL_Delay(1000);

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

 

2、DMA模式

  下面就是我本身的DMA模式的ADC多通道转换了。

  先配置一些ADC的基本配置:

  引脚

 

  时钟

  这个时钟能够结合ADC设置里配置的采样时间结合计算出ADC转换的时间,进而换算出频率。

  接着配置DMA

  ADC是12位的,其实DMA只须要用Half Word就能够了,但实际中HAL_ADC_Start_DMA(ADC_HandleTypeDef* hadc, uint32_t* pData, uint32_t Length);
该函数中pData为32位的,也就是DMA必须配置为Word才能够。

 配置ADC基本设置 

  这里要注意选择对不一样的通道,一开始我就是没留意到这个问题,就只有一个通道 Channel10 在转换,后来查看就是Rank一、二、3全配置成  Channel10 了,因此只有这个通道在转换,这里这个提醒你们注意一下。

  中断配置

  最后在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校准

  串口打印结果以下,至于怎样串口打印这里就很少说了,想知道的能够看http://www.javashuo.com/article/p-xgbapsoq-kz.html

   

补充:使用定时器与DMA中判定时采集

  上面只是单纯的一直采集的,若是想要用到中断的话就能够按下面的方式来,ADC配置跟上面说的DMA模式同样:

  先配置定时器中断,怎么配置能够参考个人另外一个文章http://www.javashuo.com/article/p-mooxlrej-mb.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中断服务函数

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 */ } 

  这样子,就能够实现1S采集多少次ADC了,而不用单纯控制采样频率来控制1S的ADC采集次数了,我的以为单纯控制采样频率比较难算。

补充:单通ADC采集参考:http://www.javashuo.com/article/p-cjjyldyp-hr.html

 

 补充一个 4 通道采集 DMA 模式:

  

  

  

  

  

       定义一个数组存放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);

  打印结果以下

    

相关文章
相关标签/搜索