当前位置: 首页 > news >正文

手把手教你用STM32的ADC+DMA+定时器,DIY一个能测频率的简易示波器

从零构建STM32示波器:ADC+DMA+定时器联动的工程实践

在嵌入式开发领域,掌握外设协同工作是进阶的关键。本文将带您用STM32的三大核心外设——ADC、DMA和定时器,构建一个能测量频率的简易示波器。不同于单纯的功能演示,我们更关注如何将这些技术模块有机整合,形成完整的信号采集与分析系统。

1. 系统架构设计

1.1 硬件选型与连接

我们选用正点原子精英板(STM32F103ZET6)作为硬件平台,其核心配置如下:

外设引脚分配功能说明
ADC1PA6信号输入通道
TIM2_CH2PA1PWM触发信号输出
DAC1PA4正弦波参考信号输出
DAC2PA5噪声/三角波参考信号输出
LCD默认接口波形显示与参数展示

关键连接方案

  • 测试信号源:将PA4(正弦波)或PA5(三角波)连接到PA6(ADC输入)
  • 触发控制:TIM2的PWM输出通过内部线路触发ADC采样

1.2 软件工作流程

graph TD A[定时器PWM触发] --> B[ADC采样] B --> C[DMA传输数据] C --> D[FFT频率分析] D --> E[LCD显示波形] E --> F[按键交互控制]

2. 核心外设配置

2.1 定时器触发机制

定时器2配置为PWM模式,产生精确的采样时钟:

void TIM2_PWM_Init(u16 arr, u16 psc) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; // 时钟使能 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 时基配置 TIM_TimeBaseStructure.TIM_Period = arr; TIM_TimeBaseStructure.TIM_Prescaler = psc; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); // PWM输出配置 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = arr/2; // 50%占空比 TIM_OC2Init(TIM2, &TIM_OCInitStructure); TIM_Cmd(TIM2, ENABLE); }

关键参数计算

  • 采样频率 = TIM2时钟 / (ARR * PSC)
  • 例如:72MHz/(720*100) = 1kHz采样率

2.2 ADC与DMA联动配置

ADC采用定时器触发、DMA传输的循环模式:

void ADC1_Init(void) { ADC_InitTypeDef ADC_InitStructure; ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; ADC_InitStructure.ADC_ScanConvMode = DISABLE; ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_CC2; ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_Init(ADC1, &ADC_InitStructure); // 启用DMA ADC_DMACmd(ADC1, ENABLE); DMA_Config(DMA1_Channel1, (u32)&ADC1->DR, (u32)adc_buffer, 1024); }

DMA配置要点

  • 循环模式(Circular)确保连续采集
  • 内存地址递增,外设地址固定
  • 半字传输(16bit)匹配ADC数据宽度

3. 信号处理实现

3.1 FFT频率分析

使用STM32 DSP库进行快速傅里叶变换:

void FFT_Analysis(void) { cr4_fft_1024_stm32(fft_output, adc_buffer, NPT); // 计算幅值谱 for(int i=1; i<NPT/2; i++) { float real = (fft_output[i] << 16) >> 16; float imag = (fft_output[i] >> 16); magnitude[i] = sqrtf(real*real + imag*imag); // 寻找主频分量 if(magnitude[i] > max_mag) { max_mag = magnitude[i]; dominant_bin = i; } } // 计算实际频率 frequency = (dominant_bin * sampling_rate) / NPT; }

参数选择原则

  • 采样点数NPT=1024(满足2^n要求)
  • 频率分辨率 = 采样率/NPT
  • 仅分析前NPT/2个点(奈奎斯特频率限制)

3.2 波形显示优化

采用双缓冲机制避免显示闪烁:

void Waveform_Refresh(void) { static u16 prev_y = 0; LCD_SetWindow(0, 50, 320, 150); // 设置波形显示区域 for(int x=0; x<320; x++) { u16 adc_index = x * (NPT/320); u16 curr_y = 150 - (adc_buffer[adc_index] * 100 / 4096); if(x > 0) { LCD_DrawLine(x-1, prev_y, x, curr_y); } prev_y = curr_y; } }

4. 调试技巧与性能优化

4.1 常见问题排查

现象可能原因解决方案
波形失真采样率不足提高TIM2触发频率
FFT结果不稳定频谱泄露添加汉宁窗函数
DMA传输中断缓冲区边界错误检查内存对齐和缓冲区大小
ADC值跳动大电源噪声添加RC滤波,使用稳压基准源

4.2 性能提升技巧

  1. 时钟树优化

    • 将ADC时钟配置为独立时钟源
    • 使用PLL倍频提高系统时钟
  2. 内存管理

    __attribute__((aligned(4))) u16 adc_buffer[1024]; // 4字节对齐
  3. 实时性优化

    • 启用DMA双缓冲模式
    • 使用硬件触发代替软件触发
  4. 精度提升

    ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 1, ADC_SampleTime_239Cycles5);

5. 扩展功能实现

5.1 自动量程切换

根据信号幅度动态调整采样参数:

void Auto_Range(void) { u16 max_val = 0; // 检测信号峰值 for(int i=0; i<1024; i++) { if(adc_buffer[i] > max_val) max_val = adc_buffer[i]; } // 动态调整PWM频率 if(max_val > 3800) { TIM2->ARR = 900; // 降低采样率 } else if(max_val < 1000) { TIM2->ARR = 100; // 提高采样率 } }

5.2 多波形测量

扩展支持同时测量多个特征参数:

参数类型测量方法显示位置
频率FFT主频分析右上角
峰峰值最大值-最小值右下角
有效值RMS计算左下角
占空比脉冲宽度测量左上角

6. 系统集成与测试

构建完整的工程框架:

Project/ ├── Drivers/ │ ├── adc_dma.c │ ├── timer_pwm.c │ └── fft_analysis.c ├── Application/ │ ├── waveform_display.c │ ├── user_interface.c │ └── system_ctrl.c └── Libraries/ ├── STM32_DSP/ └── LCD_Driver/

测试案例:输入1kHz正弦波时的系统响应

测试项预期结果实测结果误差率
频率测量1000Hz1003Hz0.3%
幅值测量3.00V2.97V1.0%
波形失真度<1% THD1.2% THD-

在项目开发过程中,最耗时的部分是调试DMA传输与FFT计算的同步问题。通过增加状态标志位和错误校验机制,最终实现了稳定的数据流水线处理。

http://www.jsqmd.com/news/682819/

相关文章:

  • 嘉兴黄金回收实体店权威榜单 - 福正美黄金回收
  • 保姆级教程:用VMware+PHPStudy复现CFS三层靶场(附全套网盘环境与排错指南)
  • 智能合约安全审计要点
  • 3分钟掌握ROFL-Player:无需启动客户端查看英雄联盟回放文件的终极指南
  • Abaqus二次开发避坑:给复合材料纤维定义材料方向时,局部坐标系到底该建在哪?
  • 2026性价比高的工业听诊传感器有哪些?检测效果好,这几款靠谱品牌推荐 - 品牌种草官
  • 信息学奥赛刷题笔记:我是如何用BFS‘通关’3D地牢迷宫题的
  • CFM-ID进阶指南:除了预测,如何用`cfm-train`训练你自己的质谱碎片模型?
  • 深圳全居邦防水工程:性价比高的深圳外墙防水公司 - LYL仔仔
  • Blazor 2026配置避坑大全,12个高频崩溃场景+对应csproj/.cshtml/.razor配置修复代码块
  • 2026年上下床/宿舍单人床/衣柜等宿舍家具厂家推荐:泉州市奇皇星五金制品有限公司,多场景家具全系供应 - 品牌推荐官
  • NVFP4:4比特精度训练的技术突破与应用
  • 从MATLAB建模到Verilog实现:我的Sigma-Delta ADC数字滤波器设计全流程复盘(含Sinc3代码)
  • 【生产环境零事故日志架构】:基于127个微服务节点验证的Docker日志分级采集方案(含logrotate+rsyslog+Loki无缝迁移路径)
  • 3步解锁Cursor Pro限制:终极免费使用指南
  • Phi-mini-MoE-instruct效果实测:长文本摘要+关键信息抽取双任务
  • 从Verilog到门级网表:用Yosys在Ubuntu 20.04上跑通你的第一个RTL综合流程
  • 3个简单步骤,让你在Windows上获得终极免费媒体播放体验
  • Q-Learning入门:从骑士救公主理解强化学习核心算法
  • 【限时开放】Java 25虚拟线程高并发调优手册(含Arthas动态注入vthread堆栈、Prometheus自定义指标采集脚本)
  • PPTAgent智能演示文稿生成框架:从文档到专业PPT的AI解决方案
  • 从‘nvidia-smi’命令失效到GPU环境一键配置:跨平台实战指南
  • 如何快速掌握ReTerraForged:打造个性化Minecraft地形的完整实践指南
  • MATLAB/Simulink仿真研究:基于下垂控制的蓄电池SOC均衡策略
  • 抖音批量下载神器:三分钟搞定无水印视频采集,告别手动烦恼
  • 2026年摄影培训及商业摄影学习机构推荐:长沙市拾画新知教育科技有限公司旗下朴画社,提供摄影培训、商业摄影等多类课程 - 品牌推荐官
  • 别再死记硬背了!深入理解51单片机生成波形的数学原理(正弦/三角/锯齿波)
  • 2026年不锈钢板材/管材/卷板等厂家推荐:沈阳元良实业有限公司,全品类不锈钢产品供应 - 品牌推荐官
  • C++ map和set的使用
  • LFM2.5-VL-1.6B效果展示:科研论文图→方法复现步骤图文拆解+公式解释