STM32基于hal库的adc以DMA的多通道采样以及所遇问题解决
目录
准备
正点原子的STM32F103ZET6开发板(精英版)
CUBEMX配置软件
KEIL5
配置
右对齐就是正常的数据格式。左对齐除以16后得正常数据。(当输出非常大时考虑是否改了对齐方式,默认都是右对齐)
扫描模式,连续转换模式使能。(多通道下扫描模式自动使能)
采样周期 SamplingTime 越大越精确,越小则则会频繁触发DMA中断(在开启dma中断时,我试了在14M的adc时钟程序进不来while(因为频繁触发DMA中断)
看数据手册,知道三个adc中(adc1,adc2,adc3只有adc1和adc3能用DMA通道。
ADC的时钟不能超过14Mhz,
配置外设到内存(cubeMX自动配置好了),外设adc地址是不变的,而DMA把数据存到存储器的地址是增长的。
Circular循环模式连续搬运adc数据。
在配置DMA时,因为ad的精度是12位,最大也就是4096,而DMA转储adc是一个通道接着一个通道去存储的,所以转储的时候dma搬运半字(stm32 32位为一个字,半字16位 最大位4096*16)就可以了,节省dma资源提升速度。然后在程序里用一个缓存为uint16_t去接受就不会有问题。
__IO uint16_t adcbuf[100]={0};
开启DMA
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&adcbuf,100);//
(不推荐的的一种配置,也不是不可以,接受数据缓存定义位32位)
正常情况下我们还是配置成半字长的
而我走的一个坑点就adcbuf这个DMA转移数据的缓存数组定义成立uint32_t,(在dma半字转移下)结果就翻船。。。
想着不应该啊,大类型接受小类型数据C是不会出问题的。
int temp=0;
for (int i=0 ;i < 100; ){
temp=i;
//adcreal[0]+= adcbuf[i++]*3.3/4096;
//adcreal[1]+= adcbuf[i++]*3.3/4096;
printf(" %d-Channel1:%d\r\n", temp,adcbuf[i++]);
printf(" %d-Channel2:%d\r\n",temp ,adcbuf[i++]);
}
依次取两个通道的数据(i为偶数是通道1,i为奇数则是通道二),看串口打印结果(两个通道,一个接3.3v一个接5v电压)
前50是3.3v电压采集,后50是接地采集结果,完全乱了。这种情况下把接受类型改成uint16_t就解决了。
步骤
定义接受缓存(定义成单字转移)
__IO uint16_t adcbuf[100]={0};
float adcreal[2]={0};
开启接受
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&adcbuf,100);//
打印数据 (平均滤波)
HAL_Delay(1000);
adcreal[0]=0.0;
adcreal[1]=0.0;
for (int i=0 ;i < 100; ){
adcreal[0]+= adcbuf[i++]*3.3/4096;
adcreal[1]+= adcbuf[i++]*3.3/4096;
}
printf(" -Channel1:%1.3f\r\n",adcreal[0]/50);
printf(" -Channel5:%1.3f\r\n",adcreal[1]/50);
printf("\r\n");
结果
总结
配置时单字dma转移,uint32_t类型定义缓存。
配置时半字dma转移,uint16_t类型定义接受缓存。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?