STM32F103 ADC DMA采样与均值滤波处理实战指南
在嵌入式开发中,ADC(模数转换器)是与外部模拟信号交互的重要外设之一。而STM32作为一款功能强大的微控制器,其ADC模块配合DMA(直接存储器访问)功能,可以高效地实现多通道数据采集与处理。本文将以STM32F103为例,详细介绍如何通过STM32CubeMX配置ADC DMA方式采样,并对3个通道的数据进行均值滤波处理。
一、硬件准备
• STM32F103开发板:确保开发板正常工作,所有引脚连接良好。
• 外部模拟信号源:例如可变电阻、温度传感器等,用于提供待采集的模拟信号。
• 串口调试助手:用于接收和查看最终的滤波结果。
二、软件环境搭建
• STM32CubeMX:用于图形化配置STM32的外设。
• STM32CubeIDE:用于编写代码和烧录程序。
三、ADC与DMA配置
(一)ADC配置
• 启动STM32CubeMX,创建一个新的项目,选择STM32F103C8T6作为目标芯片。
• 配置ADC模块:
• 在左侧的外设列表中找到ADC1,双击打开配置界面。
• 设置ADC模式:选择Single模式,因为我们是逐个通道采集数据。
• 选择时钟源:选择APB2,确保ADC有足够的时钟频率。
• 配置采样时间:根据实际需求选择合适的采样时间,例如1.5 Cycles。
• 选择通道:将需要采集的3个通道(假设为PA0、PA1、PA2)添加到Regular Conversion Group中,并设置它们的采样顺序。例如,将PA0设置为第1个通道,PA1为第2个通道,PA2为第3个通道。

(二)DMA配置
• 启用DMA功能:
• 在左侧的外设列表中找到DMA1,双击打开配置界面。
• 选择DMA1_Channel1(因为ADC1的DMA请求映射到DMA1_Channel1)。
• 配置DMA参数:
• 方向:选择Peripheral to Memory,因为数据是从ADC传输到内存。
• 内存增量:选择Incremented Address,因为我们需要将数据存储到连续的内存地址中。
• 循环模式:选择Circular,这样在DMA传输完成后会自动重新开始,方便连续采集。
• 传输大小:设置为150(3个通道×50次采样)。
• 优先级:根据实际需求选择合适的优先级。

• 将DMA与ADC关联:
• 在DMA Request选项中,选择ADC1作为请求源。
• 确保DMA Enable选项被勾选,以启用DMA功能。
(三)生成代码
• 配置完成后,点击Project菜单,选择Generate Code。
• 在生成代码的过程中,STM32CubeMX会自动生成ADC和DMA的初始化代码,并将它们添加到项目中。
四、代码实现
(一)初始化代码
在生成的代码中,ADC1_Init()和DMA1_Channel1_Init()函数已经完成了大部分初始化工作。我们需要确保这些函数被正确调用。
c
/* ADC1 DMA Init */
void MX_ADC1_DMA_Init(void)
{
ADC_ChannelConfTypeDef sConfig = {0};
DMA_HandleTypeDef hdma_adc1;
/* DMA1_Channel1 初始化 */
hdma_adc1.Instance = DMA1_Channel1;
hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_adc1.Init.Mode = DMA_CIRCULAR;
hdma_adc1.Init.Priority = DMA_PRIORITY_LOW;
hdma_adc1.Init.MemBurst = DMA_MBURST_SINGLE;
hdma_adc1.Init.PeriphBurst = DMA_PBURST_SINGLE;
HAL_DMA_Init(&hdma_adc1);
__HAL_LINKDMA(&hadc1, DMA_Handle, hdma_adc1);
/* ADC1 初始化 */
hadc1.Instance = ADC1;
hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;
hadc1.Init.ContinuousConvMode = ENABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 3;
HAL_ADC_Init(&hadc1);
/* 配置通道 */
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = 2;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
sConfig.Channel = ADC_CHANNEL_2;
sConfig.Rank = 3;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
}
(二)DMA中断处理
在DMA中断中,我们需要处理采集到的数据。由于我们选择了循环模式,DMA会自动将采集到的数据存储到指定的内存地址中。在下面示例中,50次采样(传输大小:设置为150(3个通道×50次采样))完成后,3个通道就是150个数据,会进入以下的采样完成回调函数中,在这个函数中,我们进行均值滤波处理,并将数据发送到串口调试助手显示。实际应用中,只需要计算出平均值更新,然后在需要的时候使用这个值。
另外,如果对50个采样值滤波的周期有其他特殊需求的话,可以更改采样的缓存大小,以及更改ADC采样时间。比如,防止引入工频50Hz的干扰,采样周期就不能是50Hz,大家可以根据自己的实际情况进行调整。
c
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
if (hadc->Instance == ADC1)
{
// 在这里处理采集到的数据
// 例如,计算每个通道的均值
uint16_t *data = (uint16_t*)hdma_adc1.Instance->CMAR;
uint32_t sum[3] = {0};
uint32_t avg[3] = {0};
for (int i = 0; i < 50; i++)
{
sum[0] += data[i * 3];
sum[1] += data[i * 3 + 1];
sum[2] += data[i * 3 + 2];
}
avg[0] = sum[0] / 50;
avg[1] = sum[1] / 50;
avg[2] = sum[2] / 50;
// 将均值发送到串口
char buffer[50];
sprintf(buffer, "Channel 0: %d, Channel 1: %d, Channel 2: %d\r\n", avg[0], avg[1], avg[2]);
HAL_UART_Transmit(&huart1, (uint8_t*)buffer, strlen(buffer), HAL_MAX_DELAY);
}
}
(三)启动ADC采样
在主函数中,启动ADC采样并启动DMA传输。
c
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_DMA_Init();
MX_ADC1_DMA_Init();
MX_USART1_UART_Init();
// 启动DMA传输
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adcBuffer, 150);
while (1)
{
// 主循环中可以执行其他任务
}
}
五、测试与验证
• 连接外部信号源:将模拟信号源连接到PA0、PA1和PA2引脚。
• 烧录程序:将编译好的程序烧录到STM32F103开发板中。
• 打开串口调试助手:设置波特率与程序中一致(例如115200)。
• 观察结果:观察串口调试助手的输出,查看每个通道的均值滤波结果。
六、总结
通过STM32CubeMX配置ADC和DMA,可以高效地实现多通道数据采集与处理。在本例中,我们成功实现了3个通道的DMA采样,并对每个通道的数据进行了均值滤波处理。这种方法不仅提高了数据采集的效率,还通过均值滤波降低了噪声的影响,使得采集到的数据更加稳定和可靠。希望本文能为你的STM32开发提供帮助!