news 2026/5/4 12:43:26

用STM32F103的定时器+DMA+ADC,实现多通道数据采集与波形生成的完整项目

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用STM32F103的定时器+DMA+ADC,实现多通道数据采集与波形生成的完整项目

STM32F103多通道数据采集与波形生成实战指南

在嵌入式系统开发中,高效的数据采集和信号生成能力往往是项目成功的关键。STM32F103系列微控制器凭借其丰富的外设资源和出色的性能,成为众多工业测量和控制系统中的首选。本文将深入探讨如何利用STM32F103的定时器、DMA和ADC外设协同工作,构建一个完整的多通道数据采集与波形生成系统。

1. 系统架构设计

本项目的核心目标是通过硬件协同实现以下功能:

  • 定时器触发ADC进行多通道扫描采样
  • DMA自动搬运采样数据到内存
  • 同步使用定时器PWM输出特定波形
  • 最小化CPU干预,提高系统实时性

关键外设协同原理

TIMx(触发源) ├─ ADC(多通道扫描) │ └─ DMA(自动传输) └─ TIMy(PWM波形生成)

系统采用主从定时器架构,TIM3作为主定时器产生ADC触发信号,TIM2配置为PWM模式生成波形,ADC1工作在扫描模式并通过DMA1通道1传输数据。这种设计充分利用了硬件自动化特性,使得CPU只需在数据缓冲区满时进行后期处理。

2. 硬件配置与初始化

2.1 时钟树配置

首先需要正确配置系统时钟和外设时钟:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 | RCC_APB1Periph_TIM3, ENABLE); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); // ADC时钟配置(PCLK2 6分频) RCC_ADCCLKConfig(RCC_PCLK2_Div6);

2.2 GPIO初始化

配置模拟输入和PWM输出引脚:

// ADC通道0-3(PA0-PA3) GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_Init(GPIOA, &GPIO_InitStructure); // PWM输出(PA6) GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure);

2.3 ADC多通道扫描配置

设置ADC工作在定时器触发、扫描模式:

ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; ADC_InitStructure.ADC_ScanConvMode = ENABLE; ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T3_TRGO; ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfChannel = 4; ADC_Init(ADC1, &ADC_InitStructure); // 配置采样通道和顺序 ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5); ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_55Cycles5); ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_55Cycles5); ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_55Cycles5); // 启用DMA传输 ADC_DMACmd(ADC1, ENABLE);

3. DMA数据传输实现

3.1 DMA通道配置

建立ADC到内存的自动传输通道:

DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)adc_buffer; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize = BUFFER_SIZE; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_Init(DMA1_Channel1, &DMA_InitStructure);

3.2 双缓冲技术应用

为避免数据竞争,推荐使用双缓冲机制:

// 定义双缓冲区和切换标志 uint16_t adc_buffer[2][BUFFER_SIZE]; volatile uint8_t current_buffer = 0; // DMA中断处理 void DMA1_Channel1_IRQHandler(void) { if(DMA_GetITStatus(DMA1_IT_TC1)) { DMA_ClearITPendingBit(DMA1_IT_TC1); current_buffer ^= 1; // 切换缓冲区 // 处理非当前缓冲区数据 process_data(adc_buffer[current_buffer ^ 1]); } }

4. 定时器协同工作

4.1 主定时器(TIM3)配置

产生ADC触发时钟:

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_TimeBaseStructure.TIM_Period = 1000 - 1; // 1kHz采样率 TIM_TimeBaseStructure.TIM_Prescaler = 72 - 1; // 1MHz计数频率 TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); // 配置主模式输出触发 TIM_SelectOutputTrigger(TIM3, TIM_TRGOSource_Update); TIM_Cmd(TIM3, ENABLE);

4.2 PWM定时器(TIM2)配置

生成可调波形:

TIM_OCInitTypeDef TIM_OCInitStructure; TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = 500; // 初始占空比50% TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OC3Init(TIM2, &TIM_OCInitStructure); // 动态调整占空比 void set_pwm_duty(uint16_t duty) { TIM_SetCompare3(TIM2, duty); }

5. 系统集成与优化

5.1 低延迟中断处理

优化中断服务例程:

void DMA1_Channel1_IRQHandler(void) { if(DMA_GetITStatus(DMA1_IT_HT1)) { // 半传输中断处理前半缓冲区 process_half_buffer(adc_buffer[0]); } else if(DMA_GetITStatus(DMA1_IT_TC1)) { // 传输完成中断处理后半缓冲区 process_half_buffer(adc_buffer[1]); } DMA_ClearITPendingBit(DMA1_IT_HT1 | DMA_IT_TC1); }

5.2 实时性能监测

添加性能计数器:

// 在main循环中监测CPU负载 while(1) { static uint32_t last_tick = 0; uint32_t current_tick = xTaskGetTickCount(); if(current_tick - last_tick >= 1000) { printf("CPU负载: %.2f%%\r\n", calculate_cpu_usage()); last_tick = current_tick; } }

6. 实际应用案例

6.1 工业温度监测系统

配置参数

参数说明
采样率1kHz每通道250Hz
分辨率12位0.8mV精度
通道数4支持4路PT100
波形输出1kHz PWM加热控制
// 温度转换函数 float adc_to_temperature(uint16_t adc_value) { const float Vref = 3.3f; const float R0 = 100.0f; // PT100在0°C时的阻值 float voltage = adc_value * Vref / 4095.0f; float resistance = voltage * 1000.0f / (Vref - voltage); // 使用简化线性模型 return (resistance - R0) / 0.385f; }

6.2 振动信号分析仪

性能优化技巧

  1. 使用定时器触发同步采样
  2. 启用DMA双缓冲减少数据丢失
  3. 利用FPU进行实时FFT运算
  4. PWM输出作为激励信号源
// FFT预处理函数 void preprocess_fft(uint16_t *buffer) { for(int i=0; i<FFT_SIZE; i++) { fft_input[i] = (float)(buffer[i] - 2048) / 2048.0f; // 归一化 } arm_cfft_f32(&arm_cfft_sR_f32_len1024, fft_input, 0, 1); arm_cmplx_mag_f32(fft_input, fft_output, FFT_SIZE); }

7. 调试与问题排查

常见问题及解决方案:

问题1:ADC采样值不稳定

  • 检查电源滤波电容(推荐100nF+10μF组合)
  • 确保模拟地(AGND)与数字地(DGND)单点连接
  • 增加采样保持时间(ADC_SampleTime_239Cycles5)

问题2:DMA传输不触发

// 检查清单: 1. DMA通道是否使能(DMA_Cmd) 2. ADC的DMA请求是否开启(ADC_DMACmd) 3. 触发源是否配置正确(TIM_SelectOutputTrigger) 4. 缓冲区地址是否对齐到4字节边界

问题3:PWM输出波形失真

  • 验证GPIO是否配置为复用推挽输出
  • 检查TIMx_CR1的ARPE位是否使能
  • 确保预分频和自动重载值计算正确
// PWM频率计算公式: PWM_Freq = TIMx_CLK / ((TIMx_PSC + 1) * (TIMx_ARR + 1))

通过本文介绍的技术方案,开发者可以构建出高性能的数据采集与信号生成系统。在实际项目中,建议根据具体需求调整采样率、通道数和数据处理算法。这种基于硬件协同的设计不仅提高了系统效率,也为实现更复杂的实时信号处理奠定了基础。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/4 12:38:04

手把手教你用国内手机号注册Poe AI,免代理畅玩Claude和GPT-3.5

中国大陆用户零门槛注册Poe AI全攻略&#xff1a;轻松体验Claude与GPT-3.5 在人工智能技术快速发展的今天&#xff0c;全球顶尖的AI对话平台如Claude和GPT系列正吸引着越来越多中国用户的关注。然而&#xff0c;网络访问限制和复杂的注册流程往往成为阻碍。本文将详细介绍如何…

作者头像 李华
网站建设 2026/5/4 12:37:39

别再按着按钮不放了!HC-05蓝牙模块AT模式一键进入的两种实用方法(附串口助手配置)

HC-05蓝牙模块AT模式免按键进入全攻略&#xff1a;硬件改造与软件方案深度解析 第一次接触HC-05蓝牙模块的开发者&#xff0c;大多会被那个神秘的按钮困扰——每次进入AT指令模式都需要精确计时地按住按钮再上电&#xff0c;稍有不慎就得重复这个令人抓狂的物理操作。这种反人类…

作者头像 李华
网站建设 2026/5/4 12:36:35

新手友好:借助快马平台与Gemini生成带详细注释的Python数据分析示例

作为一个刚接触Python数据分析的新手&#xff0c;我最近尝试用InsCode(快马)平台结合Gemini模型完成了一个小项目&#xff0c;整个过程对初学者特别友好。这里记录下我的实践过程&#xff0c;希望能帮到同样想入门的朋友。 明确需求与工具选择 我的目标是生成虚拟学生成绩数据&…

作者头像 李华