
简介这是一份基于STM32F103R6的4位LED数码管数字电压表设计资料包面向嵌入式入门学习者、电子竞赛备赛者及课程设计学生。资料内含完整的Proteus仿真电路图和Keil5工程源码实现0—5V电压测量、按键量程切换与数码管实时显示可直接在Proteus8.9与Keil5环境中打开运行。包内共160个文件涵盖Proteus仿真工程pdsprj、Keil工程配置uvprojx与嵌入式C/C源码、驱动库头文件以及编译生成的hex、map、axf等烧录与调试文件压缩包约5.65MB目录结构清晰便于查找。已有3600人学习下载。借助这份资料可快速理解STM32的ADC采集、按键输入与数码管动态扫描逻辑熟悉标准外设库的调用方式仿真与代码配合呈现从初始化到量程切换的完整设计流程既可用于直接验证测量结果也方便在此基础上修改量程范围、显示位数或增加报警功能适合作为课程设计、电子实训或嵌入式入门进阶的参考样例。 从课程设计到实际项目基于STM32F103R6的数字电压表是我这几年见过最适合练手嵌入式综合能力的选题之一。它不像点个流水灯那样单薄也够不上工业仪表的复杂度但恰好把模拟前端、ADC采样、数据处理、显示输出、误差标定这几个环节全部串了起来。这篇文章我就拿自己做过的方案为例从选型逻辑讲到硬件计算再到软件标定和调试踩坑尽量把每个“为什么这么做”都说清楚。如果你正准备做类似的题目或者刚学完单片机想找个有含金量的项目这篇应该能帮你省下不少走弯路的时间。1. 整体设计思路与方案选型1.1 为什么选STM32F103R6资源不多但刚好够用很多人在选型时会纠结做电压表用个几块钱的8位单片机不就行了理论上确实能测但你会发现从ADC精度到软件扩展性都憋屈。STM32F103R6这颗芯片看起来不怎么起眼Cortex-M3内核、主频72MHz、32KB Flash、10KB SRAM放在今天甚至有点“上古”的味道。但正因为这个配置门槛不高反而很适合把一个项目的底层逻辑吃透。它的12位逐次逼近型ADC分辨率比多数8位机高了一个台阶配合DMA和中断机制做连续采样处理很从容。而且F103系列生态资料多到溢出来无论是标准外设库还是HAL库遇到问题搜一圈基本都有答案。如果不是课程设计要求指定型号我个人其实也建议用R6这类小容量版本起步。原因很简单Flash小会逼着你写精简代码SRAM小会让你认真管理缓冲区等到真把这颗芯片的资源榨干用好你再去用大容量芯片就是降维打击。反过来一上来就上F103ZET6或者H7容易陷入“资源多、随便造”的心态反而不利于抠细节。1.2 整体方案链路从被测电压到屏幕读数一个数字电压表本质上做的事情只有三步把待测电压调理到ADC能量程的范围内用ADC量化成数字再由软件换算成真实电压值并展示。听起来简单但每一环节都有讲究。我这次设计的方案链路是这样的被测电压先经过电阻分压网络将最高20V的输入电压等比缩小到3.3V以内再经过RC低通滤波和钳位保护电路送入STM32F103R6的ADC引脚。片内ADC完成12位量化后由软件做数字滤波、标定换算最后把结果送到1602液晶屏上显示。为什么量程设成20V因为日常实验和维修场景里0到20V基本覆盖了绝大多数低压直流测量需求留到20V也方便以后扩展。显示方案我对比过几种数码管动态扫描最省钱但信息量少OLED显示效果好但I2C时序和驱动稍绕1602字符屏接线不多、驱动资料大把还能直接显示“U xx.xxV”这样的完整字符串。权衡之下选了1602。如果你手头有0.96寸OLED也可以替代逻辑完全一致只是初始化函数换一下。2. 硬件电路拆解与关键计算2.1 分压网络先算清楚“缩比”再动手硬件上最核心的电路就是分压网络它决定了整机的量程和测量精度。STM32F103R6的ADC参考电压接VDDA默认满量程是3.3V所以输入电压必须先缩小到不超过3.3V。做设计时我习惯留出约20%的余量按最高输入24V对应ADC满量程3V来算分压比。分压比等于3V除以24V也就是1/8。如果采用经典的上臂串联电阻R1、下臂接地电阻R2结构需要满足R2/(R1R2)1/8。我选的是R1220kΩ上臂、R230kΩ下臂总输入阻抗约250kΩ。这里要注意输入阻抗不是越大越好太大了ADC输入泄漏电流带来的误差会变明显太小了又会给被测电路增加额外负载250kΩ在常规场景下是个不错的平衡点。电阻精度我直接用1%金属膜电阻想提升精度可以换成0.1%精密电阻成本也就多几毛钱。这里有一个新手很容易忽略的坑分压电阻的阻值虽然算好了但ADC内部采样电容在采样瞬间会从分压网络抽取电流如果分压网络输出阻抗太高采样值会被拉低测量结果就会偏小。解决思路有两种要么把分压电阻整体调小比如R122kΩ、R23kΩ要么在分压输出后加一级运放跟随器做缓冲。我实测下来24kΩ级别的输出阻抗配F103的ADC还是能用的但稳定性一般想读数稳上加大电容。方案我用的是RC低通滤波在分压输出节点对地接一个0.1μF电容和30kΩ下臂组成低通网络同时滤掉高频噪声。2.2 输入保护与电源设计防止“一测就烧”测电压最怕的就是手一抖接过压ADC引脚直接冒烟。所以分压网络后面我加了BAV99双二极管钳位保护把节点电压钳制在GND-0.3V到3.3V0.3V之间。之前有一版电路省掉了保护二极管结果调试时误接过一次12V反极性电压芯片ADC引脚当场报废后来再也不敢省这个元件。如果校准时测的是外部直流稳压源输入线尽量用屏蔽线避免感应到工频噪声。电源部分同样不能马虎。STM32F103R6的模拟电源引脚VDDA和数字电源VDD虽然外部可以共用一个3.3V源但中间必须做隔离。我习惯在VDDA引脚前串一个磁珠或者10Ω小电阻再对地接0.1μF和10μF两个退耦电容让ADC的参考电压更稳定。ADC的参考电压稳定性会直接影响测量结果特别是做精确校准时如果VDDA上有毛刺读数的低位就会一直跳。这个细节在原理图阶段就要考虑到等PCB打样回来再改就麻烦了。2.3 关键器件清单为了让你看得清楚我把这版用到的核心器件整理成一个清单器件型号/参数用途数量主控MCUSTM32F103R6数据采集与处理1分压电阻220kΩ、30kΩ1%电压量程缩比各1保护二极管BAV99过压钳位1滤波电容0.1μF、10μF低通滤波、退耦若干运放可选MCP6002 或 LM358分压缓冲1液晶屏1602字符屏数据显示1对比度调节10kΩ电位器调节LCD显示效果1有一件事必须提醒1602液晶屏很多是5V逻辑电平的模块而STM32是3.3V系统两者直连虽然短期内能用但长期可靠性没保障。如果你的模块是5V版本建议加I/O电平转换或者选用3.3V供电版本别偷懒直接怼上去。3. 软件核心逻辑与标定实现3.1 ADC初始化与采样配置先把参数吃透硬件搭好后就进入软件部分。F103R6的ADC是12位分辨率转换结果范围0到4095。我的初始化思路是ADC时钟分频设为6分频PCLK2为72MHz得到12MHz的ADC时钟采用单次转换模式由定时器触发或者软件触发采样每次采样序列读取一通道。采样时间我习惯配成55.5周期也就是足够长的时间让内部采样电容充饱这样读数更稳。如果你配成1.5周期最快速度转换是快了但参考电压稍有波动读数就飘得不偿失。下面是基于HAL库的ADC初始化核心代码标准外设库的写法也类似只是函数名不同void MX_ADC1_Init(void) { ADC_ChannelConfTypeDef sConfig {0}; hadc1.Instance ADC1; hadc1.Init.ScanConvMode DISABLE; hadc1.Init.ContinuousConvMode DISABLE; hadc1.Init.DiscontinuousConvMode DISABLE; hadc1.Init.ExternalTrigConv ADC_SOFTWARE_START; hadc1.Init.DataAlign ADC_DATAALIGN_RIGHT; hadc1.Init.NbrOfConversion 1; if (HAL_ADC_Init(hadc1) ! HAL_OK) { Error_Handler(); } sConfig.Channel ADC_CHANNEL_0; sConfig.Rank 1; sConfig.SamplingTime ADC_SAMPLETIME_55CYCLES_5; if (HAL_ADC_ConfigChannel(hadc1, sConfig) ! HAL_OK) { Error_Handler(); } }每次读取采样的代码很简单先校准一次首次上电时执行然后启动转换等待转换完成读取结果寄存器uint16_t ADC_Read(void) { HAL_ADC_Start(hadc1); HAL_ADC_PollForConversion(hadc1, 10); return HAL_ADC_GetValue(hadc1); }实际生产级代码我不会用轮询方式会改成“定时器触发DMA搬运”的模式让CPU完全不用管ADC。课程设计和首版验证阶段轮询足够直观理解起来也顺手。等你跑通了这套逻辑再上DMA就顺理成章了。3.2 数字滤波为什么读数总是跳ADC采样出来的原始值通常不会很干净。即使输入是纯直流读数也可能在几个LSB之间抖动原因可能来自参考电压噪声、外界电磁干扰、甚至是测量线接触电阻的变化。这时候就需要软件滤波登场。我在实际项目中常用“去极值均值滤波”连续采20个点去掉一个最大值和一个最小值然后对剩余18个点求平均。这种组合滤波方式比单纯的均值滤波强很多能有效剔除突发性尖峰干扰。如果你采集过程中存在周期性工频干扰还可以把采样窗口对齐到20ms也就是一个工频周期有些老工程师会故意用50Hz整倍数时间去采样让干扰在一定程度上被平均掉。这个技巧在我做交流供电系统测量时特别管用直流电压表场景下也能改善稳定性。做个简单对比滤波方式优点缺点适用场景单次采样最快噪声大、不稳定粗略显示算术平均平滑效果好对尖峰敏感噪声均匀的场合中位值滤波抗脉冲干扰平滑度一般有随机尖峰时去极值均值抗尖峰平滑需要内存存窗口本次采用3.3 两点标定法让读数真正准起来硬件电阻的精度和ADC参考电压的偏差会导致理论计算值跟实际测量值有一定差距。比如分压电阻虽然是1%精度但两只电阻的误差可能叠加实际分压比就不是精确的1/8。这时候就要做标定。标定的核心是建立一个ADC读数到实际电压的线性映射。我用的方法很简单用一台精度较高的万用表或可调电源给系统输入两个已知电压比如5V和20V分别记录对应的ADC转换数值然后算出斜率和截距// 已知两个标定点(adc1, volt1) 和 (adc2, volt2) gain (volt2 - volt1) / (adc2 - adc1); offset volt1 - gain * adc1; // 之后实时换算 real_voltage gain * adc_value offset;这里的gain相当于每个ADC码对应的实际电压值注意不是直接用“阻值比”和“参考电压”算出理论值而是用实测点去反推。我在实际调试中对比过理论计算值和标定后的值最大能差出0.2V到0.3V标定之后读数明显贴近标准表。如果还想再精细一点可以多做几个标定点分区间做线性插值。比如在0-5V、5-10V、10-20V三个区间分别标定非线性误差能进一步压下去我在做测量范围比较大的电压表时就是这么处理的。3.4 主循环与显示刷新别让LCD拖累采样主循环的逻辑不复杂周期启动ADC转换 → 读取数据 → 滤波 → 标定换算 → 格式化输出到LCD。但刷新频率要留意LCD写字符其实是个相对费时的操作虽然对72MHz主频来说也就几百微秒但如果采样和显示放在一起采样率会被显示拖累。我的做法是把显示刷新频率控制在每秒两次到四次也就是每次更新前做一次时间判断没到更新周期就只采样不刷屏。这样既保证读数连续性也避免字符屏显示抖动。超量程处理也要提前想好。当电压超过20V时屏幕上显示“OL”而不是一堆乱码或负数。代码里加一个判断分支超过量程就点亮超量程提示同时禁止ADC继续输出非正常数据。这个小细节在答辩时很加分因为说明你考虑了异常场景。4. 常见问题排查与调试实录4.1 采样值跳变稳定不下来现象是输入一个稳定电压数码管或LCD上的末位数字不停跳甚至个位也在飘。排查时先别急着改代码用万用表测ADC引脚对地电压看硬件上是否已经稳定。如果引脚电压本身就不稳重点查分压电阻接触、电源纹波、基准电压这三个方面。我遇到过一次是某颗220kΩ电阻虚焊引脚电压直接是方波找了好半天才用示波器定位到。如果硬件电压稳了但软件读数还跳多半是采样时间太短或者滤波不够把ADC采样时间调到55.5周期以上再配合去极值均值滤波基本能压住。4.2 输入短路时读数不是0而是几十毫伏输入端接地显示却是0.05V甚至更高这通常是三部分原因叠加ADC本身存在偏移误差、分压网络的GND参考点有压差、运放如果用了存在失调电压。我当时的处理方式是软件零点校准上电后先测一次接地电压得到偏移值之后的每一个测量结果都减去这个偏移值。硬件调零比较麻烦软件补偿两条指令的事效果立竿见影。不过零点校准的前提是GND参考电位本身是干净的如果地线上有大电流流过形成压差光靠软件补偿是不够的要从PCB布局上解决问题。4.3 大电压下测不准误差成比例增大只在小量程准确一测十几伏就偏差大基本可以锁定是分压电阻精度或者参考电压问题。1%电阻在20V时可能带来0.2V左右误差这个不稀奇。想提高精度一是换0.1%精密电阻二是用前面提到的多点标定修正。如果误差不是线性关系而是电压越高偏差越大还要检查分压后节点电压是不是超过了ADC参考电压导致量程头部压缩这时候应调整分压比或者换更高耐压的输入设计。4.4 屏幕显示闪烁或出现乱码1602出现乱码大概率不是代码逻辑问题。我遇到过一次是LCD的RW引脚悬空导致读写状态不稳定接一个下拉电阻到地就恢复正常了。闪烁则多半是刷新频率和内部处理不同步可以试着降低刷新频率。另外1602模块的对比度电位器也很关键如果对比度调到极限还是看不清先检查模块供电电压是否达标5V模块用3.3V供电就会出现对比度不足、显示模糊的现象这时候要么换3.3V模块要么用DC-DC升压给屏供电。4.5 ADC读数始终是4095像“卡满”一样这个现象说明ADC引脚电压超过了参考电压也就是输入过压了。先查分压电阻焊接有没有短路再查输入电压是不是远大于设计量程。还有个隐蔽的问题如果ADC的通道引脚配置成了复用推挽输出而不是模拟输入也会出现异常满码值。检查一下GPIO初始化的模式是否正确F103的ADC引脚必须配成模拟输入模式否则内外电路打架读数肯定不准。我把上面这些问题整理成一张速查表方便你调试时对照故障现象可能原因排查顺序解决手段读数跳动噪声、采样时间短硬件电压→采样配置加滤波、延长采样时间零点偏移ADC失调、地线压差短接输入测零点软件/硬件零点校准大电压误差大分压电阻精度、参考电压标准表对比精密电阻、多点标定LCD乱码/闪烁RW悬空、刷新过快查接线→降刷新率加下拉、调整时序ADC卡在4095输入过压、引脚配置错查输入电压→查GPIO调整量程、修正配置调试这类项目最大的体会是不要一上来就怀疑软件。先用万用表把每个硬件节点的电压量一遍再让示波器看关键波形最后才回到代码里分析。数字电压表看起来是个入门项目真要把精度和稳定性做到位模拟电路、软件算法、系统调试全都得跑一遍这也是我为什么一直推荐身边的人拿它当第一个综合项目。做完它你后面再接触传感器采集、仪器仪表类的开发会发现很多东西都是相通的。本文还有配套的精品资源点击获取