STM32ADC多通道转换DMA模式与⾮DMA模式两种⽅法(HAL库)
⼀、⾮DMA模式(转)
说明:这个是⾃⼰刚做的时候百度出来的,不是我⾃⼰做出来的,因为感觉有⽤就保存下来做学习⽤,原⽂链接:,下⾯第⼆部分我会补充⾃⼰的DMA模式的⽅法。
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。
先使能⼏个通道,我这⾥设置为0、1、4、5.
然后就要配置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采集。
⼆、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 在转换,后来查看就是Rank1、2、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校准
stm32怎么使用printf 串⼝打印结果如下,⾄于怎样串⼝打印这⾥就不多说了,想知道的可以看
补充:使⽤定时器与DMA中断定时采集
上⾯只是单纯的⼀直采集的,如果想要⽤到中断的话就可以按下⾯的⽅式来,ADC配置跟上⾯说的DMA模式⼀样:
先配置定时器中断,怎么配置可以参考我的另⼀个⽂章
接着在 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采集参考:
补充⼀个 4 通道采集 DMA 模式:
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论