☰
两年电赛嵌入式系统工程复盘:从模块到系统设计全流程经验
2026/10/21 14:02:57 网站建设 项目流程

全国大学生电子设计竞赛(电赛)是嵌入式方向学生接触真实工程问题的最快路径之一。两年电赛生涯结束后回看,最有价值的其实不是最后那张获奖证书,而是在备赛和参赛过程中被迫学会的一整套方法论:需求拆解、器件选型、电路连接、驱动编写、系统联调、现场排错和时间管理。

这篇文章不打算写成回忆录,而是用技术复盘的方式,把两年电赛沉淀下来的内容拆成可复用的经验。全程会围绕一条主线展开:从备赛时间规划,到作品通用架构,再到关键代码实现,最后落到故障排查和比赛现场操作清单。如果你正准备参加电赛,或者带学生参加电赛,这篇文章可以直接当作备赛参考。

1. 把两年电赛当成一个嵌入式系统工程来复盘

1.1 电赛到底在考核什么

外界很容易把电赛理解为“单片机编程比赛”,实际并不是。电赛要求参赛队伍在封闭场地、有限时间内,完成一个完整的电子系统设计。这个系统通常包含传感器检测、信号处理、主控逻辑、执行机构、人机交互和供电方案。作品做出来后,还要现场演示,并根据题目要求提交设计报告和测试数据。

所以电赛考核的不是单一技能,而是三件事的组合:

  • 硬件能不能搭起来,并且稳定工作。
  • 软件能不能跑通,并且满足性能指标。
  • 临场遇到问题时,能不能在有限时间内定位并修复。

这非常接近一个小型嵌入式项目的完整流程。区别在于,平时的课程设计可以慢慢调,电赛现场没有宽裕时间。这种环境会把“系统工程能力”和“知识储备”之间的差距迅速放大。

1.2 两年生涯里真正沉淀下来的四个能力层

这两年的经历可以按能力层拆开,方便后续复盘:

能力层具体内容对应场景
硬件底层理解传感器输出类型、信号调理、电源供电、共地焊接、连线、示波器测试
嵌入式软件能力GPIO、中断、定时器、ADC、PWM、串口、I2C/SPI驱动编写、功能调试
系统联调能力软硬件接口、协议约定、日志、仪表配合整机联调、性能优化
工程化习惯文档、版本备份、验收清单、时间管理赛前准备、现场操作

这四个能力层不是并列关系,而是递进关系。第一年主要补前两层,第二年才真正进入后两层。很多队伍第一年成绩不理想,问题往往不是代码不会写,而是没有把作品当成一个系统去管理,导致硬件和软件之间反复推诿,最后现场崩掉。

2. 两年备赛的技术路线:按阶段推进,不能只靠赛前突击

2.1 第一年:从点亮 LED 到控制闭环

第一年备赛的核心目标不是拿奖,而是把嵌入式开发的基础外设全部过一遍,并且每个外设都要做最小验证。推荐的练习顺序如下:

  1. GPIO 输出,点亮 LED,理解引脚模式和电平。
  2. GPIO 输入,读取按键,做软件消抖。
  3. 串口 UART,通过打印日志确认程序运行状态。
  4. 外部中断,理解中断触发条件和回调机制。
  5. 定时器,实现延时、计数、PWM 输出。
  6. ADC 采集,读取电位器或分压电路电压。
  7. I2C 或 SPI,读取 MPU6050、OLED 等常见模块。
  8. 基础 PID,通过编码器或电位器反馈,控制电机转速或舵机位置。

这个阶段不需要做完整作品,只需要确保每个模块单独工作时是可靠的。大多数电赛现场故障,追根溯源都是某个基础模块在单独测试时就没真正跑通,赛时叠加在一起后问题被放大。

2.2 第二年:从模块堆叠到系统设计

第二年的重点开始从“模块能用”转向“系统能稳定”。这里有一个常见误区:很多队伍在正式比赛前,把一堆模块用杜邦线连在一起,写一个 main 函数从头到尾调用,就认为作品完成了。实际联调时就会出现各种奇怪问题,例如 ADC 数值漂移、电机互相干扰、OLED 刷新卡顿、按键失灵。

正确的做法是先不要写业务逻辑,而是把系统拆成几层:

  • 传感层:负责采集数据,输出统一格式的物理量。
  • 控制层:负责决策和算法。
  • 执行层:负责把控制量转成电机、开关、显示等动作。
  • 调试层:负责打印关键状态,方便现场观测。

层与层之间通过结构体或全局状态变量传递数据,而不是在中断里直接写显示、在显示里直接做延时。每年真正好用的作品,代码结构不一定复杂,但分层一定清晰。

2.3 不同赛题方向要重点准备的技术点

电赛题目会覆盖多个方向,备赛时不能每个方向都平均用力。建议先根据队伍成员的技术基础,选择一到两个主攻方向,再针对性地准备模块。

赛题方向核心任务需要重点准备的技术点
电源类设计 DC-DC 变换、恒压恒流、效率测量Buck/Boost 拓扑、MOS 驱动、反馈环路、精密采样
控制类电机控制、小车循迹、平衡控制编码器、PID、PWM、姿态解算、滤波器
仪器仪表类信号测量、波形分析、参数显示高精度 ADC、信号调理、FFT、校准方法
通信类无线数据传输、组网、协议解析无线模块配置、数据封包、校验、重传机制

从备赛性价比看,控制类和仪器仪表类是新手最容易在两到三个月内做出成绩的方向,因为资料多、模块成熟、调试手段丰富。电源类对硬件设计和动手能力要求更高,如果队伍里没有人能熟练使用示波器和焊台,不建议作为首次参赛的主力方向。

3. 电赛作品的通用架构:采集、处理、控制、调试四层

3.1 主控选型:不是越贵越好,要看外设匹配度

很多准备电赛的同学容易陷入一个误区:主控芯片越高端越好。实际上电赛作品只需要满足题目要求即可,多出来的算力和外设反而会增加配置复杂度。选主控时建议按以下顺序确认:

  1. 需要几路 ADC,分辨率要求多高。
  2. 需要几路 PWM,频率范围是多少。
  3. 需要几个串口、I2C、SPI 接口。
  4. 是否需要 DMA 搬运数据。
  5. 工作电压和外设电平是否匹配。

以 STM32F103 系列为例,它拥有多个定时器、多路 ADC、多个通信接口,对于大多数电赛题目足够用。如果涉及图像处理、FFT 频谱分析等复杂计算,再考虑使用 STM32F4 系列或更高性能平台。

这里要区分平时练习环境和比赛现场环境。平时练习时,芯片型号、开发板品牌可以随意调整;比赛现场建议带至少两块相同型号的主控板、两个下载器、若干备用芯片,并且提前烧录好一个“最小系统测试固件”,用于确认主控板在上电后是否正常。

3.2 传感器和执行器接入时的通用处理流程

任何传感器接到主控之前,都要走一遍统一流程,不要直接写代码:

  1. 看数据手册,确认输出类型是模拟量、数字量还是通信协议。
  2. 确认传感器工作电压,判断是否需要逻辑电平转换。
  3. 确认接线方式和引脚定义,避免杜邦线接反。
  4. 先写一个最小读取程序,打印原始值。
  5. 对比原始值和实际物理量,判断是否需要标定。
  6. 做数据滤波或格式转换,再把数据交给上层业务逻辑。

执行器也一样。电机、舵机、继电器、加热丝等设备,通常不能由主控 GPIO 直接驱动,需要经过电机驱动芯片、MOS 管或光耦隔离。接入前要确认驱动板输入逻辑电平、输出能力和电源容量。

很多电赛现场故障都出在这一步:传感器输出的是 5V 电平,主控引脚不耐压;或者电机驱动板需要的逻辑高电平是 3.3V,结果输入信号不对导致电机不转。这类问题如果在备赛阶段统一处理,现场就不会浪费大量时间。

3.3 电源和通信:最容易在现场翻车的两个隐性模块

电源问题最隐蔽。电赛作品往往同时带主控、传感器、电机、显示屏,各部分对电压和电流要求不同。常见问题包括:

  • 使用同一个电源给电机和主控供电,电机启动瞬间拉低电压,导致主控复位。
  • 电源纹波过大,ADC 采样值跳动。
  • 多个模块共地处理不当,通信数据出错。
  • 电源线过细,长距离传输导致压降明显。

推荐做法是主控和传感器使用稳压后的电源,电机单独使用动力电源,两者只在电源输入处共地。如果空间允许,加入大容量电解电容来吸收启动瞬间的电流冲击。

通信问题则集中在串口、I2C、无线模块上。比赛现场最常见的串口通信故障是波特率不一致。其次是数据粘包和丢帧。自己做的协议里,建议固定使用帧头、长度、数据、校验的结构,不要使用裸字符判断。后续排查的时候,有协议边界和校验字段会轻松很多。

4. 核心代码模块的落地示例:以 STM32 平台为例

4.1 传感器数据采集:ADC 加均值滤波

传感器数据采集中,ADC 原始值不能直接当作物理量使用。以 STM32 的 HAL 库为例,先配置 ADC 通道,然后在代码里多次采样取平均值,减小噪声。

// 读取一次 ADC 原始值 uint32_t adc_read_raw(ADC_HandleTypeDef *hadc) { HAL_ADC_Start(hadc); if (HAL_ADC_PollForConversion(hadc, 10) == HAL_OK) { return HAL_ADC_GetValue(hadc); } return 0; } // 多次采样取平均,用于降低随机噪声 float adc_read_voltage(ADC_HandleTypeDef *hadc, float vref) { const int sample_count = 16; uint32_t sum = 0; for (int i = 0; i < sample_count; i++) { sum += adc_read_raw(hadc); } float avg_raw = (float)sum / sample_count; float voltage = avg_raw * vref / 4095.0f; return voltage; }

这段代码的要点是把采样次数提高到 16 次,使用平均值代替单次采样值。对于慢变化的物理量,例如温度、电位器、电池电压,这样做能明显减少显示抖动。但要注意,如果传感器本身响应很快,例如要采集振动信号或电流波形,就不能大量取平均,否则会丢失信号细节。

ADC 的参考电压 vref 通常就是 3.3V,如果主控板使用外部基准电压芯片,需要把实际基准值传进来,否则计算出的电压会有偏差。

4.2 输出控制:PWM 调电压调速度

PWM 是电赛作品里最常用的输出控制方式。用定时器产生 PWM 时,需要明确两个参数:频率和占空比。

// 假设已经通过 CubeMX 配置好 TIM2 的 PWM 通道 void pwm_set_duty(TIM_HandleTypeDef *htim, uint32_t channel, float duty_percent) { if (duty_percent < 0.0f) duty_percent = 0.0f; if (duty_percent > 100.0f) duty_percent = 100.0f; uint32_t pulse = (uint32_t)(__HAL_TIM_GET_AUTORELOAD(htim) * duty_percent / 100.0f); __HAL_TIM_SET_COMPARE(htim, channel, pulse); }

PWM 频率选择有一点讲究。频率太低时,电机或加热器会发出可听见的噪声,控制精度也差;频率太高时,功率管的开关损耗增加,驱动芯片发热。直流电机常用的 PWM 频率在 10kHz 到 20kHz 之间,舵机控制则一般是 50Hz 周期、1ms 到 2ms 高电平宽度。实际频率要结合驱动芯片的数据手册来确定,不要照抄别人的参数。

占空比的控制逻辑要在业务层实现,不要在主循环里反复修改定时器计数器值,而是通过统一的接口函数设置目标占空比,由定时器硬件自动保持输出。

4.3 控制算法:位置式 PID 的工程简化实现

电赛中最常见的闭环控制算法是 PID。位置式 PID 实现简单,适合初学者理解,但工程上要注意积分饱和问题。下面是一个适合嵌入式环境的简化版本。

typedef struct { float target; // 目标值 float feedback; // 反馈值 float kp; // 比例系数 float ki; // 积分系数 float kd; // 微分系数 float integral; // 积分累加值 float prev_error; // 上一次误差 float out_max; // 输出上限 float out_min; // 输出下限 } PidController; float pid_update(PidController *pid, float feedback, float dt) { float error = pid->target - feedback; pid->integral += error * dt; // 积分限幅,防止积分饱和 if (pid->integral > 300.0f) pid->integral = 300.0f; if (pid->integral < -300.0f) pid->integral = -300.0f; float derivative = (error - pid->prev_error) / dt; float output = pid->kp * error + pid->ki * pid->integral + pid->kd * derivative; pid->prev_error = error; // 输出限幅 if (output > pid->out_max) output = pid->out_max; if (output < pid->out_min) output = pid->out_min; return output; }

这段代码有两个关键点值得展开。

第一,积分限幅。如果误差长期存在,例如电机堵转,积分项会一直累加,导致输出到达上限,恢复后要很久才能回到正常。通过限制积分累加值,可以避免系统反应迟钝。

第二,输出限幅。PWM 占空比不能超过 100%,所以任何 PID 输出最终都要限制在执行机构的物理范围内。否则一旦输出异常,整机控制效果就会被破坏。

调参顺序建议是:先只开 P 项,让系统稳定且不振荡;再加入 D 项,抑制超调;最后加 I 项,消除稳态误差。不要同时修改三个参数,否则很难判断哪个参数导致系统表现变化。

4.4 调试通道:串口日志和状态机打印

电赛现场没有 IDE 在线调试环境,或者说不方便反复连接调试器。这时候串口日志就是最重要的观测手段。建议在代码里预留一个轻量日志函数,统一输出当前状态、目标值和反馈值。

// 日志格式建议固定,方便使用串口上位机或文本工具解析 void log_status(int state, float target, float feedback, float output) { char buf[64]; snprintf(buf, sizeof(buf), "state=%d,target=%.2f,fb=%.2f,out=%.2f\r\n", state, target, feedback, output); // 调用串口发送函数,例如 uart_send_string uart_send_string(buf); }

状态机的每个状态都打印一条日志,定位问题时会非常方便。例如小车走直线跑偏,如果日志显示反馈值一直在目标值附近波动,说明控制参数有问题;如果反馈值根本没更新,说明传感器或通信链路有问题。日志能快速把问题范围缩小到“硬件采集”还是“控制算法”。

5. 两年里踩过的坑:现象、原因和排查链路

5.1 硬件正常但软件读数为 0

现象:用万用表量传感器输出,电压正常,但单片机读取的 ADC 数值始终为 0。

可能原因:

  • 引脚配置错误,把不是 ADC 功能的引脚配成了模拟输入。
  • 开启了复用功能,但没有正确配置 ADC 通道。
  • 采样时间过短,信号源阻抗偏高。
  • ADC 没有执行校准。

检查方式:

  • 对照芯片数据手册,确认引脚是否支持 ADC 输入。
  • 查看 CubeMX 或寄存器配置,确认 ADC 通道号与引脚对应。
  • 用手触摸传感器输出点,观察读取值是否变化。
  • 打印 ADC 原始值,不要直接打印转换后的电压。

处理建议:先把 ADC 配置简化成单通道连续转换,确认能读到有效值后,再调整采样时间或加入 DMA。不要一上来就多通道扫描。

5.2 电机抖动、声音尖锐且发热

现象:电机低速运行时抖动明显,高速时声音尖锐,驱动芯片和电机外壳发热严重。

可能原因:

  • PWM 频率与驱动芯片匹配不佳。
  • PID 参数过激,输出振荡。
  • 电源供电能力不足,电机启动时电压跌落。
  • 电机或驱动板共地不良。

检查方式:

  • 用示波器观察 PWM 输出波形,确认频率和占空比稳定。
  • 把 PID 的 Ki、Kd 先置零,只留 Kp,观察系统是否还会抖动。
  • 用电流表测量电机启动时的工作电流,对比电源最大输出电流。
  • 检查电机电源地和主控地是否连接在同一电位。

处理建议:先用固定占空比测试电机的开环运行,确认电源和驱动没有问题;再加入闭环控制。如果抖动仍然存在,优先排查 P 项过大,不要先调积分。

5.3 系统偶尔复位,找不到稳定复现路径

现象:整机运行过程中偶尔重启,故障出现频率不高,难以稳定复现。

可能原因:

  • 电源电压跌落。
  • 外部干扰导致复位引脚误触发。
  • 独立看门狗超时。
  • 中断函数执行时间过长,导致主循环卡死。

检查方式:

  • 用示波器长时间观察主控 VDD 引脚,看电机启动时是否出现跌落尖峰。
  • 查看复位标志寄存器,区分是上电复位、外部复位还是看门狗复位。
  • 在中断函数和主循环里都加入日志,确认程序最后执行到哪里。
  • 逐个失能外设,缩小干扰来源。

处理建议:优先在电源端加大电容,并参考芯片手册检查复位引脚的上拉电阻。不要直接修改看门狗超时时间来掩盖问题,因为往往真正的原因不是看门狗,而是程序卡死。

5.4 比赛现场改参数改崩了

现象:现场演示前,为了优化性能修改参数,结果系统行为异常,无法快速恢复到之前可用的状态。

可能原因:

  • 没有保留上一版的固件备份。
  • 同一时间修改了多个参数,无法确定是哪个参数导致问题。
  • 没有在源码里注释原参数值。

预防措施:

  • 每次烧录前,把当前固件导出为 .bin 文件,按日期和版本号命名。
  • 现场只允许修改一个参数,如果无效立即改回。
  • 关键参数用宏定义或配置文件统一管理,不要在代码里散落多个魔法数字。

6. 两年电赛沉淀出来的工程素养

6.1 文档先行:接口约定比写代码更早

电赛作品往往需要两个人或三个人协作。如果软硬件分工不明确,联调时就会反复改代码、改接线。建议在动手写代码之前,先定一份接口文档,内容至少包括:

  • 主控引脚分配表。
  • 每个传感器和执行器的供电电压、通信接口、数据格式。
  • 数据结构体的定义,例如状态枚举、控制量、反馈量。
  • 串口协议格式。

下面是一个简单的引脚分配表示例:

功能引脚电平说明
编码器 A 相PA03.3V外部中断输入
编码器 B 相PA13.3V外部中断输入
电机 PWMPA83.3VTIM1_CH1
电机方向PB33.3VGPIO 输出
电压采样PC03.3VADC1_IN10

这份文档在比赛前写好,比赛现场就能减少大量沟通成本。

6.2 版本管理和备份策略

电赛备赛周期通常有两三个月,代码会经过无数次修改。可靠的做法是至少建立三层备份:

  • 本地开发目录,按日期建目录副本。
  • 使用 Git 做代码版本管理,每个功能完成就提交一次。
  • 比赛现场携带 U 盘,保存所有源码和固件。

即使不熟悉 Git,也要养成“改大功能前复制一份”的习惯。现场最怕的不是没有新功能,而是改到一半想回退却发现回不去。

6.3 测试与验收清单

电赛作品不要只测“能跑”。建议每个子模块都设置一个验收标准,避免联调时把基础模块问题带进系统。下面是一份常用测试清单:

检查项验收标准
主控上电电源指示灯亮,最小系统固件串口输出正常
传感器读取串口打印值随物理量变化,响应方向和量程正确
PWM 输出示波器测量频率和占空比与设定值一致
闭环控制施加扰动后,系统能在规定时间内回到目标值
连续运行整机连续运行 2 小时,无复位、无数据异常

比赛前一周,至少完整跑一遍整机连续运行测试。很多队伍只测功能不测稳定性,现场演示时系统运行十分钟后才开始出现异常,这种问题最难处理。

6.4 时间管理:备赛不同阶段怎么分配精力

备赛周期一般可以分成四个阶段:

  • 基础练习阶段:约前 6 周,集中解决单片机外设和模块驱动问题。
  • 模块准备阶段:约第 7 到第 10 周,把常用传感器、执行器、屏幕、无线模块全部备齐并单独验证。
  • 系统整合阶段:约第 11 到第 14 周,做一两次完整模拟题,从选题、方案设计到整机联调。
  • 现场冲刺阶段:最后一周,只做稳定性测试和文档整理,不要再引入新功能。

最容易出问题的是第三阶段。很多队伍到系统整合阶段才发现自己缺少某个模块,或者某个模块单独能用,但多个模块同时工作后互相干扰。所以模拟题做的时间要尽量提前,越早暴露问题,越有时间补齐。

7. 给后来者的可复用备赛清单

7.1 赛前三个月、一个月、一周的检查项

赛前三个月:

  • 确定主控平台,完成基础外设练习。
  • 准备常用模块:编码器电机、舵机、OLED、MPU6050、蓝牙/Wi-Fi 模块、各类传感器。
  • 编写每个模块的最小测试代码,保存为独立工程。
  • 建立接口文档模板和代码版本管理习惯。

赛前一个月:

  • 至少完成两次模拟题。
  • 每次模拟题后记录用时、卡点、缺失模块。
  • 根据模拟题暴露的问题补充器件和代码库。
  • 准备备用主控、下载器、杜邦线、常用芯片、电源模块。

赛前一周:

  • 整理现场工具箱:焊台、万用表、示波器、电源、热风枪、剥线钳、杜邦线、扎带。
  • 对每个模块做上电检查,确认没有损坏。
  • 打印接口文档、引脚分配表、测试清单。
  • 准备 U 盘,备份所有源码和固件。

7.2 现场比赛时的操作顺序

拿到赛题后,按以下顺序推进:

  1. 先通读题目,圈定基本要求和发挥部分。
  2. 评估手上模块是否能覆盖题目要求,区分“必须件”和“已有件”。
  3. 确定技术方案,把系统拆成硬件、软件、算法三个子任务。
  4. 先跑通最小闭环:主控、一个传感器、一个执行器、串口日志。
  5. 在最小闭环的基础上,逐步加入显示、通信、备用功能。
  6. 完成基本功能后,再优化指标,例如提高控制精度、降低功耗。
  7. 最后写设计报告和准备演示脚本。

不要在拿到题目当天就急着写全部代码。先把架构定清楚,把核心风险提前识别出来。现场四天里的节奏是:第一天定方案和搭硬件,第二天写核心代码,第三天联调和优化,第四天准备演示材料和备份。

7.3 赛后复盘记录什么

比赛结束后,不要急着休息或准备下一场,先花两个小时做一次复盘。记录内容:

  • 题目和最终方案。
  • 时间分配是否合理,哪个阶段超出预期。
  • 遇到的所有问题,包括问题现象、根因、解决方式。
  • 哪些准备不足,哪些判断准确。
  • 如果再做一次,会在哪个环节投入更多时间。

这份复盘的价值会在下一场比赛,或者后续的工程项目中体现。两年电赛生涯最宝贵的东西,其实是逐步建立起来的这套做事方式:先想清楚再动手,每一步可验证,每个问题可追溯。

如果你准备参赛,建议从今天开始,先给自己建一个“模块测试库”目录,把你手里的每一块传感器、每一种电机、每一个无线模块,都写一个最小测试工程并保留结果记录。真正拉开差距的不是最后几天的熬夜,而是每一次实验之后,你有没有把现象、原因和下一步行动写下来。有了这套基础,到比赛现场时,你手里握着的就不是零散模块,而是一个经过反复验证的工程工具箱。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询