STM32F103简易示波器:ADC+DMA+定时器采样链路实战

发布时间:2026/11/4 8:29:10

STM32F103简易示波器:ADC+DMA+定时器采样链路实战 简介这份资源是面向嵌入式初学者与电子竞赛备赛者的STM32F103C8T6简易示波器实战Demo围绕ADC采样、定时器触发、串口通信与波形显示等核心环节帮助读者理解如何用Cortex-M3内核微控制器完成从模拟信号采集到数字波形呈现的完整链路适合具备一定C语言与单片机基础、希望动手实践信号采集项目的人群。压缩包共298个文件约8.56MB以c源文件与h头文件为主体配合Keil工程文件uvprojx、uvoptx、编译产物o、d、crf、axf、hex及少量批处理与说明文件构成可直接打开编译的完整工程目录。目前已有2916人学习下载说明该案例在入门示波器与STM32外设综合练习中具有较高参考价值。读者可从中获得ADC多通道配置、定时器触发采样、UART数据传输、主机端波形绘制与滤波处理等具体实现思路并借助工程结构学习固件库组织方式与调试排错方法为后续扩展量程、提升采样率或增加显示界面打下基础。1. 一块十几块钱的蓝板子怎么就成了桌面上的简易示波器手头只有一块 STM32F103C8T6 最小系统板想测个 PWM 占空比、看个传感器输出的毛刺又不想搬出几千块的台式示波器——这个简易示波器 demo 就是为这种场景准备的。它的核心思路很朴素用 STM32 自带的 ADC 以固定采样率把模拟信号采进来存进一块环形缓冲区再通过串口把波形数据丢给上位机画出来或者直接驱动一块小屏做本地显示。整套东西跑在 72MHz 的 Cortex-M3 上ADC 最高 1Msps 的采样能力对付音频以下、几十千赫兹以内的低频信号够用。适合谁适合刚上手 STM32 外设、想找一个把 ADC、DMA、定时器、串口串起来练手的从业者也适合需要一台随手能抓波形的低成本工具的人。先把预期摆正它不是实验室仪器量程、带宽、精度都有硬边界但作为理解采样链路和练手外设协同的载体性价比很高。2. 采样链路怎么搭ADC、定时器与 DMA 的三件套2.1 为什么用定时器触发 ADC 而不是软件轮询很多人第一版示波器是这么写的主循环里HAL_ADC_GetValue()读一次存数组循环。跑起来就发现波形抖得没法看采样间隔完全取决于循环里其他代码的执行时间频率一高就混叠。血泪经验是采样率必须由硬件定时器来定软件只负责搬运。正确做法是让 TIM 的更新事件去触发 ADC 的规则组转换ADC 转换完成再由 DMA 把结果搬到内存全程 CPU 不参与单次采样。这样采样间隔由定时器周期严格决定抖动在纳秒级波形才稳得住。STM32F103 的 ADC 外部触发源里TIM2 的 CC2 事件是常用选择配置成ADC_EXTERNALTRIGCONV_T2_CC2即可。采样率的计算是这套链路的第一道坎。ADC 总转换时间 采样时间 12.5 个 ADC 周期ADC 时钟最高 14MHz72MHz 六分频。假设采样时间设 1.5 周期单次转换约 14 个周期理论极限约 1Msps。但实际能跑多快还受 DMA 搬运和缓冲区大小制约。我一般先把目标定在 100kSps 到 500kSps 之间够看大部分低频信号也留足余量。2.2 环形缓冲区与双缓冲的取舍采样数据往哪存直接决定波形能不能连续看。最省事的是开一个大数组采满就停等上位机读完再采——这叫单缓冲缺点是采的时候不能传、传的时候不能采波形有断点。进阶一点用双缓冲ping-pongDMA 配成循环模式半满中断和全满中断各触发一次前半段在采的时候后半段可以往外发交替进行波形就连续了。下面是一段基于 HAL 库的 ADC DMA TIM 初始化骨架双缓冲用 DMA 的 half-transfer 和 transfer-complete 两个中断实现// TIM2 配置72MHz / (711) 1MHz 计数频率ARR9 得 100kHz 触发 void TIM2_Init_100k(void) { __HAL_RCC_TIM2_CLK_ENABLE(); TIM_HandleTypeDef htim2 {0}; htim2.Instance TIM2; htim2.Init.Prescaler 72 - 1; // 分频到 1MHz htim2.Init.Period 10 - 1; // 100kHz 更新 htim2.Init.CounterMode TIM_COUNTERMODE_UP; HAL_TIM_Base_Init(htim2); // 用 CC2 事件做 ADC 触发源 TIM_OC_InitTypeDef sConfigOC {0}; sConfigOC.OCMode TIM_OCMODE_PWM1; sConfigOC.Pulse 5; // 50% 占空比仅用于产生事件 HAL_TIM_PWM_ConfigChannel(htim2, sConfigOC, TIM_CHANNEL_2); HAL_TIM_PWM_Start(htim2, TIM_CHANNEL_2); } // ADC1 DMA1_Channel1 双缓冲 #define BUF_LEN 1024 uint16_t adc_buf[BUF_LEN]; void ADC_DMA_Init(void) { __HAL_RCC_ADC1_CLK_ENABLE(); __HAL_RCC_DMA1_CLK_ENABLE(); ADC_HandleTypeDef hadc1 {0}; hadc1.Instance ADC1; hadc1.Init.ScanConvMode DISABLE; hadc1.Init.ContinuousConvMode DISABLE; // 由定时器触发不连续 hadc1.Init.ExternalTrigConv ADC_EXTERNALTRIGCONV_T2_CC2; hadc1.Init.DataAlign ADC_DATAALIGN_RIGHT; hadc1.Init.NbrOfConversion 1; HAL_ADC_Init(hadc1); // 采样时间 1.5 周期配合 14MHz ADC 时钟 ADC_ChannelConfTypeDef sConfig {0}; sConfig.Channel ADC_CHANNEL_0; // PA0 输入 sConfig.Rank 1; sConfig.SamplingTime ADC_SAMPLETIME_1CYCLE_5; HAL_ADC_ConfigChannel(hadc1, sConfig); // DMA 循环模式半满/全满各一次中断 HAL_ADC_Start_DMA(hadc1, (uint32_t*)adc_buf, BUF_LEN); }这段代码的关键参数有三个Prescaler和Period共同决定采样率改Period就能调采样率但要注意别超过 ADC 的转换极限SamplingTime决定输入阻抗匹配信号源内阻大时要加长采样时间否则采出来的值偏低ContinuousConvMode必须关掉否则定时器触发就失效了。DMA 配成循环模式后HAL_ADC_Start_DMA一次启动就一直跑半满和全满中断里分别处理前后半段数据。2.3 触发方式自由运行还是边沿触发真正的示波器有触发功能波形稳定显示靠的就是触发。简易 demo 里常见两种做法自由运行采到啥画啥和边沿触发检测到上升沿/下降沿才开始记录。自由运行实现简单但波形会在屏幕上左右乱跑看着像玄学。边沿触发需要在缓冲区里找第一个越过阈值的点把后续数据对齐输出波形才定得住。实现上可以在 DMA 全满中断里做一次软件触发判断从缓冲区头开始扫找到第一个从低于阈值跳到高于阈值的位置把该位置作为波形起点发给上位机。阈值可以设成缓冲区最大值和最小值的中间值也可以让上位机下发。这一步多做几十行代码观感提升非常明显属于性价比极高的改动。3. 上位机与本地显示数据怎么变成波形3.1 串口协议设计与上位机绘图STM32 采到的数据是 12 位 ADC 值0~4095要通过串口发给上位机。最省事的协议是直接发原始字节但这样上位机没法知道一帧的边界容易错位。常见做法是加帧头帧尾0xAA 0x55 长度 数据 校验。波特率建议 921600 或更高115200 在 100kSps 下根本传不完——每采样点 2 字节100kSps 就是 200KB/s115200 波特率理论上限才 11.5KB/s差了近 20 倍。这是新手最容易翻车的地方采样率设得很高结果串口成瓶颈波形严重失真。上位机侧用 Python 的pyserialmatplotlib就能快速搭一个import serial, struct import matplotlib.pyplot as plt import matplotlib.animation as animation ser serial.Serial(COM3, 921600, timeout1) FRAME_HEAD b\xaa\x55 def read_frame(): # 找帧头读长度再读数据 while True: if ser.read(1) b\xaa and ser.read(1) b\x55: length struct.unpack(H, ser.read(2))[0] payload ser.read(length) if len(payload) length: return struct.unpack(f{length//2}H, payload) fig, ax plt.subplots() line, ax.plot([], []) def update(_): data read_frame() if data: line.set_data(range(len(data)), data) ax.relim(); ax.autoscale_view() return line, ani animation.FuncAnimation(fig, update, interval50) plt.show()read_frame里先对齐帧头再读长度能避免串口噪声导致的错位struct.unpack按小端解析 16 位无符号数和 STM32 的内存布局一致。interval50控制刷新率太快会卡太慢波形拖影。这套上位机代码不到 30 行但足够把波形实时画出来调试阶段非常够用。3.2 本地 OLED 显示的取舍不想连电脑的话可以挂一块 0.96 寸 OLEDSSD1306I2C 接口。本地显示的好处是便携坏处是刷新率和分辨率都受限128×64 的屏横轴最多 128 个点纵轴 64 级波形细节远不如上位机。我一般把本地显示定位成“快速看有没有信号、大概什么幅度”精细分析还是走串口。OLED 刷波形时别整屏清再重画那样会闪。正确做法是用页地址模式只更新波形所在的页或者用双缓冲在内存里画好再一次性推屏。I2C 速率设 400kHz整屏刷新大约 20ms勉强能到 50fps够看。如果信号频率高本地屏基本只能看个包络这点要提前有心理预期。4. 避坑与排查五个让波形“看起来不对”的真实原因4.1 波形毛刺多、跳动大现象采一个稳定的直流或低频正弦屏幕上却全是毛刺数值上下跳几十个 LSB。原因通常是输入阻抗没匹配好或者采样时间太短。信号源内阻大时ADC 采样电容来不及充到稳定值采出来就偏低且抖。解决把SamplingTime从 1.5 周期加到 41.5 或 239.5 周期同时确认信号源地线跟板子共地。如果还抖在输入端并一个 0.1uF 电容做抗混叠。4.2 波形频率对不上像是被“压缩”了现象输入 1kHz 正弦画出来却是 100Hz 的样子。原因几乎都是采样率不够触发了混叠。根据奈奎斯特采样率至少要是信号频率的 2 倍实际工程里取 5 到 10 倍才看得清形状。解决先算清楚当前 TIM 配置下的实际采样率再对照信号频率调整Period。记住串口带宽也是硬约束采样率提上去之前先确认传输链路跟得上。4.3 串口数据错位、上位机画出来是乱码波形现象上位机偶尔画出一段完全不对的波形或者帧长度解析出天文数字。原因是帧同步丢了噪声或缓冲区溢出导致帧头错位。解决协议里加校验和上位机收到校验失败的帧直接丢弃并重新找帧头STM32 侧在 DMA 中断里发数据时确保发送完成再启动下一帧别在中断里做阻塞发送。用HAL_UART_Transmit_DMA比阻塞式HAL_UART_Transmit稳得多。4.4 ADC 读数始终是 4095 或 0现象不管输入什么ADC 值卡在满量程或零。原因通常是引脚配置错了——ADC 输入脚必须配成模拟输入模式GPIO_MODE_ANALOG配成浮空或上拉都会出问题另一个常见原因是参考电压没接好VREF 悬空或没滤波。解决检查 GPIO 初始化确认 VREF 引脚有 0.1uF 加 1uF 的退耦输入电压别超过 3.3V超了会钳位甚至损坏引脚。4.5 双缓冲切换时波形有断点现象用双缓冲后波形每隔一段就有一个明显的断裂。原因是半满/全满中断里处理太慢DMA 已经绕回覆盖了还没发完的数据。解决中断里只做标记和启动发送别在里面做浮点运算或长循环把发送缓冲区开成两份和 DMA 的两个半区一一对应必要时降低采样率给 CPU 留出搬运时间。这个坑很隐蔽因为单看代码逻辑没问题是时序上的竞争。5. 把 demo 用出仪器感校准与量程的两个技巧demo 跑通只是起点想让它读数可信还得做两件事校准和量程换算。ADC 的 12 位读数本身没有物理意义得先知道参考电压实际是多少。别信标称的 3.3V用万用表量一下 VREF 的实际值我见过板子上实测 3.28V 的按 3.3 算就有 0.6% 的系统误差。换算公式是电压 adc_value * vref / 4095把实测 vref 代进去精度立刻上一个台阶。量程方面ADC 输入范围是 0 到 VREF负电压和超过 VREF 的信号都进不来。测小信号时前面加一级运放做偏置和放大把信号抬到 1.65V 附近再进 ADC能充分利用 12 位动态范围测大信号时用电阻分压注意分压电阻别太大否则又回到输入阻抗不匹配的老问题。下面这个换算表可以贴在工位上场景前端处理有效分辨率注意点0~3.3V 直测无约 0.8mV/LSB输入别超 VREF小信号mV 级运放放大 10 倍 1.65V 偏置提升约 10 倍运放带宽要够高压信号电阻分压 1:10降低 10 倍分压电阻取 kΩ 级带直流偏置的交流隔直电容 偏置取决于偏置稳定性电容和输入阻抗构成高通还有一个提升观感的技巧在固件里做简单的滑动平均或中值滤波窗口取 4 到 8 个点能压掉不少随机噪声代价是牺牲一点带宽。滤波窗口别开太大否则高频细节被抹平反而看不出真实波形。我一般把滤波做成上位机可开关的选项调试时关掉看原始数据出图时打开。从那以后我每次搭采样链路都强制先把采样率、串口带宽、缓冲区大小这三个数算清楚再动手写代码因为绝大多数“波形不对”的问题根子都在这三个数的匹配上而不是代码写错了。希望这套拆解能帮到你把手里那块蓝板子变成真正能用的桌面工具。本文还有配套的精品资源点击获取
延伸阅读

更多相关文章

2026/11/4 8:29:10

三维数字孪生技术在智能仓储管理中的应用与实践

1. 项目背景与核心价值 在传统仓储管理中,我们常常会遇到这样的困境:明明系统显示某个货位上有库存,但实际盘点时却找不到货物;新来的操作员需要花费大量时间熟悉库区布局;旺季时多个作业班组在立体库中频繁发生动线冲…

2026/11/4 8:29:10

量化分析Token成本失控?WorkBuddy国际版限免模型实测与选型指南

量化分析这个圈子,玩久了都会碰到同一个尴尬:策略还没跑通,Token账单先烧起来了。尤其是做KNN算法股票量化分析这类需要反复迭代的任务,AI每给你改一次代码、每解释一段回测结果,背后都是白花花的Token在燃烧。上个月我…

2026/11/4 8:24:05

LangGraph:图结构在NLP中的应用与优化

1. LangGraph 核心概念解析LangGraph 是近年来在自然语言处理领域兴起的一种图结构表示方法。简单来说,它把文本中的语言单元(如词、短语、句子)及其关系用图的方式建模,让机器能像人类一样理解语言的层次结构和语义关联。我第一次…

2026/11/4 9:24:28

CentOS7下OpenSSL生成CA根证书全流程详解

1. CentOS7环境下CA根证书生成全流程解析在Linux服务器运维和分布式系统部署中,证书颁发机构(CA)的建立是构建可信安全体系的基础环节。作为在金融行业浸淫多年的系统架构师,我经常需要为内部系统搭建私有CA。本文将详细记录在CentOS7环境下从零开始建立…

2026/11/4 9:24:28

美团Hera分布式监控系统:微服务全链路追踪实战

1. 分布式监控系统Hera的核心价值在微服务架构成为主流的今天,系统复杂度呈指数级增长。记得去年我们团队遇到一个棘手的生产问题:用户投诉订单支付成功率突然下降,但各个服务日志都没有明显异常。整整两天时间,我们像无头苍蝇一样…

2026/11/4 9:24:28

CC Switch安装指南:配置档案管理与多环境切换

上周有同事凑过来看我屏幕,问了一句挺有意思的话:你这台机器上的命令行工具,怎么一会儿跑这套参数、一会儿又换另一套,切来切去跟变魔术似的。我说真不是魔术,就是装了个叫CC Switch的小工具,把几套配置档案…

2026/11/4 9:24:28

Vue3打包报错‘Vue未定义‘的6种解决方案

1. Vue3打包报错"Uncaught ReferenceError: Vue is not defined"深度解析 最近在Vue3项目部署过程中,不少开发者遇到了一个典型问题:开发环境运行正常,但执行 npm run build 打包部署后,浏览器控制台却抛出 Uncaught…

2026/11/4 9:19:27

AI智能体训练新方法与AI编程工具全流程自动化解析

今天是2026年9月23日,AI圈的信息照例多到让人眼花。我照常把热搜、社区热帖、产品更新轮着刷了一遍,发现这天的关键词非常集中:DeepSeek公开了AI智能体训练的新方法,AI短剧和AI漫剧的制作流程被越来越多人关注,AI编程插…

2026/11/3 7:47:06

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/3 14:24:53

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/4 0:03:20

AI辅助技术栈评估:提升研发决策效率60%

1. 项目背景与核心价值技术决策一直是研发团队面临的重大挑战。随着技术生态的快速演进,技术栈的选择、维护和迭代变得愈发复杂。我们团队在过去两年里,先后经历了三次重大技术栈迁移,每次都需要投入大量人力进行技术评估和风险分析。直到去年…

2026/11/4 0:03:20

从生成Token到直接决策:Jev如何重塑AI Agent工程实践

1. 从"生成文字"到"拍板决策":Jev 到底在解决什么问题大多数人第一次接触大模型,脑子里建立的模型是"输入问题、输出文字"。你问它今天天气怎么样,它给你一段描述;你让它写个周报,它给你…

2026/11/4 0:03:20

32G统一内存跑大模型:内存账、TPS实测与端云分流

想在一台 32G 统一内存的小主机上把大模型跑成"日常可用",这件事我前后折腾了小半年。标题里说的这台 Mac mini M6,是我们这次讨论的目标平台,具体到手规格以实物为准;但下面这套推算方法和实测思路,对任何统…

2026/11/3 7:47:06

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/3 14:24:53

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑