STM32 f407 多通道ADC采集+DMA传输 基于HAL库和Cubemx配置

在实际应用中ADC往往是要和DMA一起使用的,DMA将ADC转换值放入预设好的变量中,该过程不占用CPU资源,需要读取ADC输入时再让CPU读取变量即可。

下面记录使用cubemx配置多通道ADC采集的具体流程,并编写对ADC采样值进行滤波的程序(选择开发板、开启外部时钟和SW调试接口、Project Manager设置相关操作略过)

1.选择要使用的ADC和对应通道

f407内部ADC资源如下,我们选择ADC3的通道10、11、12、13进行ADC采集

图片来源29. ADC—电压采集 — [野火]STM32 HAL库开发实战指南——基于野火F4系列开发板 文档

在cubemx中选择

2.配置ADC参数

Scan Conversion Mode:如果是单通道转换设置为DISABLE,如果是多通道AD转换设置为ENABLE;

Continuous Conversion Mode:配置是启动自动连续转换还是单次转换。选择ENABLE配置为使能自动连续转换,如果是DISABLE则配置为单次转换,转换一次后停止需要手动控制才重新启动转换;

Number of Conversion:进行规则组的通道配置。如果没有特殊需要把所有的通道都放在规则组即可,这里使用了4个通道就把number of conversion设置为4,并把channel分配到每一个RANK

Sampling Time:越大则ADC结果越准确。由于我们使用了DMA,采样时并不需要CPU等待,所以这里的耗时几乎对程序没什么影响。而ADC转换精度是我们很在意的,所以直接将其设为最大的480时钟周期。

DMA Continuous Requests:选择Enable,开启DMA传输(在没有分配DMA时是不可以Enable的,进行完3再来设置)。

3.配置DMA

点击Add添加ADC3的DMA传输通道,并将Mode设置为周期模式即可(即不断的将ADC转换值搬运到指定区域)

4.生成代码,开启DMA传输

首先DMA需要一个目的地,即把ADC转换的结果往哪个变量中搬运,所以我们先创建一个变量用于储存ADC搬运的结果。由于我们还要对ADC转换值进行平均滤波,所以这里把计算过程中需要的变量全部创建了,并保存在结构体ADC3_sampling中

#define Filter_width 30	
typedef struct
{
	uint8_t adc_count;
	uint16_t adc3_dma_storage[4*Filter_width];   
	uint32_t adc3_sum_A1, adc3_sum_A2, adc3_sum_A3, adc3_sum_A4;
	uint16_t adc3_average_A1, adc3_average_A2, adc3_average_A3, adc3_average_A4;
	uint32_t time;
}ADC3_SAMPLING;
extern ADC3_SAMPLING ADC3_sampling;

由于我们的结构体ADC3_sampling在main中定义,而我们要把滤波函数写到adc对应的文件中,所以用extern声明。filter width是我们定义的滤波器宽度

ADC转换值储存只能使用uint16_t数据类型!不然DMA搬运时会混乱!

PS:由于滤波是对adc采样值的处理,只与adc相关的函数就放到adc文件中,这样做是为了整个工程更有条理。而把变量定义到main中是为了后续的调试方便,可以在main中就看到所有的变量

开启DMA传输(注意要MX_ADC3_Init()的后面)

HAL_ADC_Start_DMA(&hadc3, (uint32_t *)ADC3_sampling.adc3_dma_storage, 4*Filter_width);

5.对采样值进行滤波

在我的实际项目中,虽然Sampling Time已被设为480,但不进行滤波的话ADC每次的采样值任然存在很大偏差,所以进行滤波是必要的。

首先重定义HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc),该函数在DMA将ADC转换结果传输完成时被调用。然后在回调函数中进行滤波操作。

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{	
    for(uint8_t i=0; i<30; i++)
	{
		// 计算累计值
		ADC3_sampling.adc3_sum_A4 += ADC3_sampling.adc3_dma_storage[4*i];
		ADC3_sampling.adc3_sum_A3 += ADC3_sampling.adc3_dma_storage[4*i+1];
		ADC3_sampling.adc3_sum_A2 += ADC3_sampling.adc3_dma_storage[4*i+2];
		ADC3_sampling.adc3_sum_A1 += ADC3_sampling.adc3_dma_storage[4*i+3];
	}
	// 计算均值
	ADC3_sampling.adc3_average_A4 = 0.8057 * ADC3_sampling.adc3_sum_A4/Filter_width;
	ADC3_sampling.adc3_average_A3 = 0.8057 * ADC3_sampling.adc3_sum_A3/Filter_width;
	ADC3_sampling.adc3_average_A2 = 0.8057 * ADC3_sampling.adc3_sum_A2/Filter_width;
	ADC3_sampling.adc3_average_A1 = 0.8057 * ADC3_sampling.adc3_sum_A1/Filter_width;
	// 累计值清零以待下次计算		
	ADC3_sampling.adc3_sum_A4 = 0;
	ADC3_sampling.adc3_sum_A3 = 0;
	ADC3_sampling.adc3_sum_A2 = 0;
	ADC3_sampling.adc3_sum_A1 = 0;
}

注意0.8057是3300/4096得来的。分别代表3.3V和2^12

6.总结

很多教程喜欢从HAL_ADC_Start()和HAL_ADC_Start_IT()开始教,这样虽然能让理论知识理解的更加充分,但我觉得不是很有必要。因为其实HAL_ADC_Start_DMA ()内部的实现中是调用了HAL_ADC_Start_IT()的,用多了遇到问题多了自然就会去看内部实现了,然后自然而然就明白了。至于HAL_ADC_Start()这种阻塞式实现,完全不会用的到。

先会用,再精通!

补充:根据后续测试,采样4通道的数据30组并搬运耗时约2.8ms(CPU不参与),而滤波占用CPU的时间非常小,几乎可以忽略不计(小于10微秒)。应用时可以对滤波语句加上条件判断(比如设置标志位10ms进行一次ADC采样值滤波),整个ADC采样过程就几乎无感实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值