
简介面向全国大学生电子设计竞赛备赛者这份2019年D题国家二等奖完整代码包提供了经实战验证的获奖级解决方案。压缩包共156个文件约2.98MB以39个.h头文件、37个.c源文件为核心辅以编译生成的.d、.o、.crf等中间文件以及工程配置文件.uvprojx、.uvoptx和烧录文件.hex、.axf囊括了从源码到构建产物的全链路内容。已有5337人浏览学习。代码基于STM32平台覆盖定时器、ADC、LCD、Flash、I2C、CAN等模块体现了完整的系统架构与硬件接口设计。通过研读获奖代码可以学习模块化组织方式、信号处理与控制算法实现、外设驱动编写、错误处理机制以及规范的代码风格理解一次竞赛作品从方案迭代到稳定运行的优化过程。对于希望提升嵌入式开发能力或备战电赛的同学这是一份难得的参考资料。 2019年全国电子设计大赛D题国家二等奖代码这份rar文件在我网盘里存了挺长时间。每次有学弟学妹来要我的第一句话基本都是先别急着解压想清楚你打算怎么用它。电赛这类比赛代码看起来是最终交付物可一份能拿奖的代码背后其实是硬件方案、测量算法、误差修正和现场调试这一整条链路。这篇文章就把D题“简易电路特性测试仪”的完整思路拆开讲一遍从题面理解一直讲到代码组织顺便把比赛现场踩过的坑也一并交代。给正在备赛的队伍一个参考也给那些拿到前辈代码却不知道从哪下手的同学一点方向。1. 这道题要的不只是“能测”而是“测得准”1.1 题面拆解D题到底考什么2019年D题《简易电路特性测试仪》核心就一个字测。题目给你一个装配好的晶体管放大器通常是以S9013这类小信号三极管为核心的共射极放大电路固定在一块小板上外围器件基本封闭你没法去改动它。测试仪要做的事情是自动测量这个放大器的输入电阻、输出电阻、电压增益和幅频特性并把结果显示出来。很多人第一反应是“这不就是在输入端加个信号、输出端接个示波器嘛”真上手就会发现完全不是那么回事。测试环境是自动化的现场测评的时候没有让你手调旋钮的余地所有参数必须由你的设备在几秒钟内自己测完、算完、显示完。这背后考题真正想考察的是你对“测量系统”的理解信号源怎么产生、幅值怎么提取、误差怎么控制、数据怎么处理。另外要提醒一点不同年份的题目对频率范围、精度要求会有差异D题当年的通频带上限在几百kHz量级具体以你们自己参赛那一年的题面为准。但不管指标怎么变测量原理是通用的。1.2 四个核心指标为什么难测电压增益Au Uo / Ui公式看起来很简单实际难点在于Ui和Uo不能“想当然”。信号源显示输出1V不代表加到放大器输入端就是1V因为信号源内阻、连接线阻抗、被测电路输入阻抗会分压。这是最常见的误差来源。输入电阻Ri的测量难点在于共射极放大器的输入阻抗不是一个纯电阻它含有电容分量频率不同时阻抗值会变化。题目给的频带范围内通常按电阻近似处理但测量时选用的已知串联电阻值必须量级匹配否则计算误差会大得离谱。输出电阻Ro同样不省心。共射极放大器的输出级本身有非线性在小信号条件下才能等效成理想电压源串联内阻所以测量时信号幅度不能太大否则你测到的根本不是小信号输出电阻。幅频特性就更典型了。不同频率下增益会变化而你在每个频点上的读数都被噪声、瞬态响应、DDS的相位噪声干扰。如果只测一次就画点画出来的曲线就是一条锯齿状的毛刺线。后面我会细讲稳定化和平滑处理的办法。2. 硬件方案DDS信号源和幅值检测是两大命门2.1 信号源选型为什么我放弃PWM滤波最初的方案是用STM32的定时器输出PWM再通过RC低通滤波得到正弦波。当时想着省一颗芯片结构也简单。实测下来问题不少一是频率一变滤波器的衰减也跟着变输出幅度会随频率漂移必须额外做幅度闭环才能稳住二是波形质量在几十kHz以上明显变差谐波分量大对测量精度影响直接。后来换成了DDS方案用AD9833作为正弦波发生器STM32通过SPI直接写频率寄存器。AD9833的好处是频率切换快、输出稳定从几Hz到几MHz都能保持不错的波形。而且它内部有相位累加器切换频率时波形相位连续这对接下来的幅值测量很重要——相位跳变会引入尖峰干扰。DDS输出信号幅度本身是固定的需要在后面加一级程控增益放大器或者数字电位器配合运放搭的衰减网络把输出幅度精确调节到题面要求的量级。我当时用的是数字电位器加运放跟随软件里预设几档衰减量实测线性度还不错。2.2 幅值检测ADC直采还是检波芯片幅值检测是整个系统的另一个核心分岔点这里我犹豫了很久。方案一是用高速ADC直接采样正弦波软件里算有效值方案二是用真有效值芯片比如AD8436或者对数检波器AD8318把正弦波变成直流电平再送ADC。最后我们选了混合方案被测电路的输出端用ADC直接采样加软件计算信号源输出端用检波器做幅度闭环。原因是这样分工比较合理——信号源那边需要的是“稳”用检波器反馈环路保证输出幅度不随频率和温度漂移被测电路输出那边需要的是“准”软件算有效值可以避开检波芯片本身的非线性误差。如果你也打算用ADC直采记住一个经验采样率至少要被测信号频率的10倍100kHz的测试信号就需要1Msps以上的采样率STM32F103的ADC勉强够用但要注意开启DMA别在中断里一个个读数据。2.3 程控增益与电源处理的细节估计有人会问ADC参考电压、信号幅度这些细节有那么重要吗答案是太重要了。D题被测放大器的增益可能随频带变化横跨几倍甚至十几倍如果测量前端没有程控增益放大低增益时信号太小被噪声淹没高增益时信号超出ADC量程数据根本没法用。我们当时在ADC前端放了一级PGA用运放加模拟开关切换反馈电阻实现1倍、10倍、100倍三档增益。这个设计后来被证明是拿分的关键因为现场测评时被测电路的具体参数是抽签决定的没有自动量程的话碰到高增益的板子就只能干瞪眼。电源方面模拟电路和数字电路分开供电是基本操作。数字地和模拟地在ADC芯片下方单点汇合避免数字噪声通过地平面耦合进模拟通道。另外连接被测电路的线尽量用短屏蔽线接插件用防反插的现场测评时老师不会给你时间理线。3. 四个测量算法的原理与误差控制3.1 电压增益别默认信号源输出就是放大器输入测电压增益时要在被测放大器的输入端单独引一根线回MCU的ADC直接测放大器输入端口的实际电压而不是用信号源的设定值。这一步是很多人忽略的也是精度差异的来源之一。具体操作是这样DDS输出固定频率如1kHz的正弦波先等波形稳定然后ADC分别采样输入端Uin和输出端Uo。每个通道连续采样若干个完整周期在软件里算出各自的峰值或有效值再相除得到增益。这里有个细节采样输入和输出必须“分时同测”。因为DDS的幅度可能存在缓慢漂移如果先采输入再采输出中间隔了几百毫秒两次测量的基准变了增益误差就会扩大。我们的做法是快速交替采样输入、输出各一轮取多个来回的平均值。3.2 输入电阻串联已知电阻法的挡位问题输入电阻的测量原理是在信号源和被测电路输入之间串联一个已知电阻R串分别测信号源侧电压U1和被测端电压U2然后利用分压关系Ri R串 × U2 / (U1 - U2)这个公式本身不难难在R串的取值。如果被测放大器的输入电阻很大比如200kΩ而R串只有1kΩ那么U2几乎等于U1分子分母都是小量一点噪声就会被放大成巨大的误差。解决办法是做几挡串联电阻比如1kΩ、10kΩ、100kΩ测量时先用默认挡位粗测根据算出的Ri范围再切换到合适的挡位精测。这个“两段式测量”的思路在现场测评时非常管用因为题目不会提前告诉你会抽到什么样的被测电路。串联电阻本身要用1%精度的金属膜电阻实测前还要用万用表标定一下实际阻值把这个值写进代码的配置参数里。别小看这一步5%精度的电阻会让最终计算出的Ri直接偏差5%以上。3.3 输出电阻空载与带载的电压差输出电阻的测量逻辑更直观先测放大器空载时的输出电压U空再接入一个已知负载RL测带载电压U载那么Ro RL × (U空 / U载 - 1)这个公式成立的前提是放大器输出端能等效成一个理想电压源串一个内阻。在小信号条件下共射极放大器是满足这个近似的。实操中要注意的是负载电阻不能选太小。如果RL比Ro小得多输出电压会被拉到接近零ADC量化误差会变得非常大。我们用的是2kΩ作为默认负载同时准备了10kΩ作为高阻场景的备用。另外一个容易踩的坑是切换负载时继电器或模拟开关的接触电阻会被算进Ro里。建议用低导通电阻的模拟开关并且在软件里对开关本身做一次短路校准。3.4 幅频扫描稳定时间比采样点数更重要幅频特性的测量本质上是把上面说到的增益测量在一个频率序列上重复做一遍。我们当时把起始频率到截止频率按对数间隔取了60个点每个频点测完记录增益。真正决定曲线质量的是“稳定时间”。DDS切换频率之后放大器的输出不会立刻稳定尤其是接近带宽边缘的频率点瞬态响应可能持续好几个毫秒。如果切换频率后立刻采样算出来的幅值必然偏小整个曲线的高频端会被“拉弯”。我们当时的处理是每次切换频率后固定等待10ms再做采样和计算。这个延时不是越长越好因为60个频点每个延时10ms总共也就0.6秒但如果每个延时100ms用户体验就会明显变差。本质上是在响应速度和精度之间取平衡10ms是实测下来比较稳的值。每个频点重复测3次取平均能有效抑制ADC噪声和随机干扰。这个“重复测量平均”的套路虽然朴素却是让最终展示曲线足够平滑的最直接手段。4. 代码怎么组织才不会在现场翻车4.1 模块划分DDS、测量、算法、UI别混在一起电赛代码不算大型工程但如果在写之前没有模块划分的意识第三天就会变成灾难。拿到一份别人写的代码先把文件列表看一遍基本就能判断这份代码的工程质量。我们当时的模块划分是dds.c负责SPI通信和频率、幅度寄存器写入measure.c负责ADC采集和有效值计算algorithm.c负责增益、输入电阻、输出电阻推算ui.c负责OLED显示和按键扫描main.c里只放一个状态机主循环。这样的好处是现场如果需要临时加一个“测完自动锁存数值”的功能只需要改ui.c和main.c的状态枚举不需要动measure.c里的采样逻辑。如果你拿到的代码是几百行全塞在main.c里的那种建议先自己动手拆一下。4.2 扫频状态机的具体设计状态机的价值在扫频流程里体现得最明显。我们定义的扫频状态序列大概是这样的扫描状态机IDLE空闲→ SET_FREQ设置DDS频率→ WAIT_STABLE等待稳定→ SAMPLE_IN采样输入→ SAMPLE_OUT采样输出→ COMPUTE计算增益→ NEXT_FREQ频率推进→ 全部完成后回到IDLE。WAIT_STABLE这个状态是最容易被新手漏掉的。很多人写完代码后发现“我这高频段增益怎么衰减得这么快”其实不是电路问题就是少了这一拍稳定的等待。用一个简单的延时函数就能解决但必须在状态机里给它一个明确的位置不要随手写在别的状态分支里。另外状态机里的每个状态都要有超时保护。万一现场某个频率点的采样异常整个循环卡死在等待状态那就尴尬了。我们给每个状态都加了一个超时计数超时后自动跳转到下一个状态保证整个测量流程最多几秒钟内一定能结束。4.3 ADC采样与RMS计算数字端的抗干扰手段ADC原始数据直接用来算有效值结果往往抖得厉害。我们当时用了两层处理第一层是硬件低通ADC前端加了截止频率略高于最高测试频率的RC滤波滤掉高频噪声第二层是软件平均对每个采样点做16次平均后再参与计算。具体到有效值计算推荐用“过零检测半周期积分”的方法而不是简单地取最大值减最小值。取峰峰值的方法对噪声和偶发尖峰非常敏感过零检测配合积分则具备天然的抑制能力。实现上就是检测采样序列里符号变化的点然后对这个半周期的采样值求均方根。这个算法有个前提输入信号必须是相对干净的正弦波谐波和噪声不能太大。如果硬件滤波做得不好软件怎么挣扎都没用所以算法和硬件永远是配合的关系。4.4 显示与交互显示我们用的是0.96寸OLEDSSD1306驱动I2C接口。选它的原因是体积小、刷新快、不会像LCD1602那样在低温下变色。菜单做了两层第一层选择测量项增益、输入电阻、输出电阻、幅频扫描第二层显示数据或曲线。交互上三个轻触开关解决短按切换菜单项、长按确认进入测量。有个经验是现场测评时评委都会在你旁边站着看操作流程越直接越好。我们做了“开机自动进入增益测量模式”评委一按电源键就能看到数据这个细节在测评时很加分。5. 校准和误差修正从“能跑”到“稳”5.1 幅值通道线性校准ADC通道的增益误差和偏置误差是系统误差的主要来源。运放、分压电阻、ADC参考电压的精度叠加起来不校准的话增益测量的误差能到10%~15%。校准过程不复杂用一台精度靠谱的信号发生器输出已知幅值的正弦波从我们的测量通道采一遍数据记录多点之后拟合一条线性曲线把斜率和截距存进EEPROM。实测校准后增益测量误差压到了3%以内。这个校准在赛前一定要做而且最好在正式测评前重新做一遍。因为环境温度变了之后模拟器件的特性会轻微漂移现场测量结果可能和前一天差一点。5.2 输入电阻测量里的“减法放大误差”这里要单拎出来说一下因为我们在这个坑里卡了整整一晚上。前面提到输入电阻公式Ri R串 × U2 / (U1 - U2)当U1和U2非常接近时U1 - U2这个减法会把ADC的量化误差成倍放大。解决思路有两层一是硬件上切换R串挡位让分压差保持在20%以上二是软件上用高分辨率多次平均提高有效位。两层都做好之后实测输入电阻误差控制在5%以内这在电赛现场是完全够用的。5.3 幅频曲线毛刺的处理幅频曲线画出来有毛刺先别急着改代码先检查是不是DDS在这个频点的输出幅度本身就在跳变。我们当时发现某些频率下AD9833的输出幅度会有周期性波动查了半天发现是SPI速率不够写入频率字时寄存器更新不及时导致的。后来把SPI速率调高每次写入后加一句“等待DDS更新完成”毛刺现象立刻少了很多。剩下的毛刺用“3次测量取中位数”处理掉了。中位数比平均值更快更省内存效果也比平均值好。6. 现场四天三夜的经验和关于代码的实话6.1 时间线安排电赛是四天三夜时间看着挺长实际效率不高。我们的节奏是第一天完成方案定型和PCB绘制晚上投板第二天上午等板子下午焊接和底层驱动调试第三天一整天做算法和整机联调第四天上午补测数据、整理设计报告下午封箱等待测评。这个安排的前提是第一天的方案不能推倒重来所以方案阶段的讨论一定要充分。6.2 那些容易在现场翻车的非技术细节现场最容易翻车的往往不是技术本身而是这些小事电源线带得不够现场电源又不稳定建议自己带一块隔离降压模块连接线不够短在几百kHz信号下线材就是天线干扰一测一个准被测电路的供电电压没确认用万用表实测一下再计算别直接用标称值。另外一个大经验是代码要随时提交备份哪怕只是在U盘里多存一份。我们比赛第三天晚上改了一版算法结果新代码在某几个频点上反而更差就是靠U盘里的旧版本秒回了状态。版本管理这件事哪怕只存两三个历史版本也能救命。6.3 拿到这份rar代码后该怎么用最后回到这份“2019年全国电子设计大赛D题国家二等奖代码.rar”本身。拿到rar之后先解压但别急着编译烧录。先看里面有没有原理图和README对照看代码里每个模块对应哪个硬件再看main.c里的状态机结构理解整套流程是怎么串起来的最后才看具体的寄存器配置和算法实现。这份代码跟它的硬件是强绑定的AD9833的控制字时序、ADC的引脚配置、PGA的增益切换逻辑换任何一块硬件都要改。直接拿去交是肯定不行的但把它作为一份“完整可运行的参考实现”对着自己的板子逐模块移植价值非常大。移植的过程其实就是把电赛考察的知识体系又过了一遍。对了解压密码通常在文件名的备注或者学长给的说明里别一个个去试浪费时间。本文还有配套的精品资源点击获取