CubeMX 配置使用DMA读取regular ADC数据
选择ADC通道
添加DMA请求。配置Priority为High,配置Mode为Circular循环执行DMA,Memory地址自增,相应的数据宽度(ADC 最多12bit,Half Word 16bit够用)
使能Scan Conversion Mode,扫描每个regular ADC通道
使能DMA Continuous Requests,在每个regular ADC通道转换完成后,都发起DMA请求
配置ADC regular ADC。包含2个转换,分别对应Channel1和Channel2,我这里使用HRTimer Trigger Out 1 event外部regular触发ADC转换
CubeMX 生成初始化代码可能错误
在MX_ADC1_Init()中包含DMA初始化
MX_DMA_Init()中包含DMA时钟使能和DMA中断配置
CubeMX可能生成初始化代码顺序如下:
/*ADC和DMA初始化*/
MX_ADC1_Init();
/*DMA时钟使能和DMA中断配置*/
MX_DMA_Init();
DMA初始化过程在DMA时钟使能前,导致DMA初始化失败!!!
具体失败现象为:
- ADC数据读取错误,并且产生overrun标志位
- DMA中断只能在一开始时进入,后续不再触发DMA中断
- MX_ADC1_Init()函数中,不能写入DMA CCR寄存器(hdma->Instance->CCR = tmp)
/*以下均在MX_ADC1_Init()内*/
/* Get the CR register value */
tmp = hdma->Instance->CCR;
/* Clear PL, MSIZE, PSIZE, MINC, PINC, CIRC, DIR bits */
tmp &= ((uint32_t)~(DMA_CCR_PL | DMA_CCR_MSIZE | DMA_CCR_PSIZE | \
DMA_CCR_MINC | DMA_CCR_PINC | DMA_CCR_CIRC | \
DMA_CCR_DIR));
/* Prepare the DMA Channel configuration */
tmp |= hdma->Init.Direction |
hdma->Init.PeriphInc | hdma->Init.MemInc |
hdma->Init.PeriphDataAlignment | hdma->Init.MemDataAlignment |
hdma->Init.Mode | hdma->Init.Priority;
/* Write to DMA Channel CR register */
hdma->Instance->CCR = tmp; /*由于DMA时钟尚未使能,所以执行完该语句并不能在CCR内写入值*/
解决
检查问题所在功能的时钟使能和初始化顺序
尤其功能间具有依赖关系时,很可能其一功能的使能和初始化没在同一个函数内,可能CubeMX把初始化错误的放在使能前面
结语
当时用CubeMX生成了两个工程,明明配置都一样,其中一个工程运行时很正常,另一个一直读不出ADC数据。
费解,连Beyond Compare都用上了,痛苦面具😭。
最后Debug模式下查看DMA、ADC的寄存器值变化,才找到这个顺序的问题。