ADC+DMA
ADC,全称为A/D转换器,在STM32上是一个将模拟电压量转换为数字量的一个外设。STM32F401RCT6上有一个12位的ADC,它可以将GND到参考电压VREF之间的任一电压值,转换为0到4095的其中一个数字(4095为12位的二进制能表示的最大数字)。其一般用于采样电压值,或者将电流信号转换成电压信号,再采样其表示的电流值,还可以使用1个ADC的IO做一个多按键判断的键盘等等。
直接内存访问(DMA,Direct Memory Access)是一些计算机总线架构提供的功能。它在STM32上的作用就是可以代替CPU完成数据搬运的工作。一般用于通信,将通讯寄存器中的数据按照条例搬运到内存中,当CPU需要时,可以直接访问内存,分析之前传来的数据,不要等待通信完成,而在浪费时间和性能。
在cubemx中,我们一般使用以下代码来实现ADC采集并且将数据以DMA传输的方式传到我们在内存中开辟的数组中。
HAL_ADC_Start_DMA(ADC_HandleTypeDef* hadc, uint32_t* pData, uint32_t Length)
其中hadc是我们要操作的ADC所代表的参数结构体指针,pData是我们在内存中提前开辟的数组的指针,length是一次传输的ADC值数量。
多通道非连续查询
在实际操作过程中,我比较喜欢关闭连续采样(连续采样有时会不工作),在循环或者中断中来调用上面的HAL_ADC_Start_DMA。在需要的时刻持续监测ADC的值,由于ADC和DMA均为自动执行,从执行该指令到提取到更新后的ADC值,可以控制在5us以内(4Ranks,12bit,3cycle),可能需要时间更少(未测试),下面是具体实现。
项目背景是在10us的中断内监测电池电压和UVW三相电机的电动势,Rank设置1–4
注意选择循环模式
接着我们定义一个数组
uint16_t ADCGET[4] = {0};
并在中断执行HAL_ADC_Start_DMA,将数组的首地址填进去。
HAL_ADC_Start_DMA(&hadc1, (uint32_t *)ADCGET, 4);
开启中断后,就可以在主函数中将这个数组的值显示出来了。
OLED_ShowNum(20, 13, ADCGET[0], 4, 12);
OLED_ShowNum(70, 13, ADCGET[1], 4, 12);
OLED_ShowNum(70, 26, ADCGET[2], 4, 12);
OLED_ShowNum(70, 39, ADCGET[3], 4, 12);
OLED_Refresh_Gram();
小问题
不需要在初始化中写HAL_ADC_Start,会使RANK顺序乱一位。仅仅需要CubeMX自己生成的初始化即可。需要更新ADC值直接调用HAL_ADC_Start_DMA。
我的芯片在选择连续工作模式时,地址内的数据不更新,而且在任何模式下,连续工作都不启用。