十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于STM32与HAL库的嵌入式系统开发实战:从电赛项目到工程化构建

基于STM32与HAL库的嵌入式系统开发实战:从电赛项目到工程化构建 简介本资源为2021年全国大学生电子设计竞赛H题信号失真度测量装置的完整嵌入式实现方案面向电子类专业本科生、嵌入式初学者及电赛备赛者聚焦高精度模拟信号采集、FFT频谱分析与失真度算法实现等核心难点。压缩包共138个文件含78个头文件.h定义外设驱动与算法接口50个C源文件.c实现STM32F4系列主控的HAL库底层驱动如ADC、TIM、UART、SPI、DAC、CMSIS-DSP数学运算及主控逻辑另有链接脚本.ld、启动文件.s及STM32CubeMX工程配置.ioc、.mxproject等关键构建文件总大小1.98MB。已有74人学习下载资源经实测可直接编译运行涵盖从信号调理、采样控制、频谱计算到LCD显示的全链路代码结构清晰、模块解耦特别适合用于课程设计、工程实训或毕设参考并可在其基础上快速扩展谐波分析、自动量程切换等功能。1. 项目概述从“2021年电赛H题.zip”说起看到“2021年电赛H题.zip”这个标题很多参加过电子设计竞赛的朋友尤其是搞嵌入式、玩STM32的估计会心一笑或者心头一紧。这不仅仅是一个压缩包它背后是一段浓缩的、高强度的开发记忆是无数个通宵调试、方案推倒重来、代码写到天亮的具象化。对于没参加过电赛的朋友可能不太理解我简单解释一下全国大学生电子设计竞赛简称电赛是国内电子类最高级别的学科竞赛之一题目难度大、综合性强非常考验软硬件综合能力。每年的赛题都会在赛后公布而“H题.zip”就是其中一道赛题的官方发布文件包里面通常包含了题目要求、技术指标、评分标准等所有官方资料。拿到这个压缩包对于参赛者而言是挑战的开始对于赛后的学习者而言则是一个绝佳的、贴近实战的学习案例。它不像书本上的例程那么理想化里面充满了真实的工程约束和性能指标。今天我就以这个“2021年电赛H题.zip”为引子结合我这些年带学生参赛和做项目的经验深入拆解一下这类电赛题目背后我们到底需要掌握哪些核心技能特别是围绕STM32和HAL库这一对“黄金搭档”如何系统性地构建一个稳定、高效且易于维护的嵌入式系统。无论你是正在备赛的电赛选手还是希望提升工程能力的嵌入式开发者这篇文章都能给你提供一套清晰的思路和可落地的实操方法。2. 电赛题目深度解析与核心需求拆解电赛的题目尤其是国赛题从来都不是单一知识点的考察。它更像是一个微型的产品需求文档你需要从零开始完成从需求分析、方案设计、硬件选型、软件实现到系统调试的全过程。我们以典型的电赛题目结构为例来拆解你需要关注的核心点。2.1 题目类型与技术要求映射首先你需要快速判断题目类型。电赛题目大致可分为几类控制类如小车、云台、仪器仪表类如示波器、信号源、电源类如逆变器、稳压电源以及高频通信类。不同类型的题目技术栈侧重点完全不同。控制类核心是实时性和稳定性。你会频繁用到STM32的定时器产生PWM控制电机、舵机、编码器接口读取电机转速、ADC采集传感器信号如电位器、超声波、以及高级定时器用于精确的脉冲捕获测速、测频。软件上PID控制算法是必考项如何整定PID参数、防止积分饱和、应对非线性是调试的重点和难点。仪器仪表类核心是精度和速度。对STM32的ADC、DAC性能要求高需要深入理解采样率、分辨率、信噪比。通常会用到DMA来高速搬运数据避免CPU干预同时要熟练运用FFT等数字信号处理算法。显示部分如OLED、LCD的刷新效率和波形绘制算法也是关键。电源类核心是功率和效率。硬件电路拓扑、MOS管驱动、电感电容选型是基础软件上需要实现闭环控制电压、电流环对ADC的采样同步性、PWM输出的死区控制、保护逻辑过压、过流、过温的响应速度要求极高。高频通信类核心是协议和抗干扰。可能需要实现特定的通信协议如模拟的FSK、PSK或数字的LoRa、Wi-Fi对STM32的定时器、外部中断、DMA以及射频前端电路的配合要求很高。“2021年电赛H题”具体是什么由于没有原始文件我们无法确定。但通过上述分类你可以快速将手中的题目对号入座明确主攻方向。例如如果题目涉及“用STM32控制伺服电机485”那它很可能是一个控制类题目且带有通信要求。2.2 从评分标准反推设计重点电赛的评分标准是设计的“指挥棒”。一定要仔细研读。它通常会分为“基本要求”和“发挥部分”。基本要求是及格线必须100%稳定实现发挥部分是拉开差距的关键。基本要求追求稳定可靠。你的系统必须在规定时间内反复上电测试都能正常工作。这意味着代码要有良好的初始化流程、错误处理机制比如传感器断线检测、参数掉电保存用内部Flash或外置EEPROM。硬件上电源要干净复位电路要可靠关键信号线要做好屏蔽或滤波。发挥部分追求性能极限和创新性。比如要求控制精度更高、响应速度更快、功能更丰富。这时就需要你在完成基本功能的基础上进行算法优化如自适应PID、卡尔曼滤波、资源压榨用DMA中断替代轮询、优化代码执行效率或增加智能功能如自动标定、参数自整定。一个关键心得不要一开始就冲着发挥部分去。一定要先搭建一个能稳定满足基本要求的框架在此基础上迭代优化。很多队伍折戟就是因为基础功能都没调稳却花了大量时间在复杂算法上最后比赛时间到了系统连基本功能都跑不起来。3. STM32与HAL库电赛中的核心工具链选型为什么STM32和HAL库在电赛圈子里如此流行这不是没有道理的。3.1 为什么是STM32性价比与生态STM32系列从低端的Cortex-M0到高端的M7产品线极其丰富价格覆盖广。无论是简单的控制还是复杂的运算都能找到合适的型号。其庞大的用户群体带来了海量的学习资源、开源项目和问题解答论坛、博客、视频教程极大地降低了学习门槛和开发风险。外设丰富STM32集成了你能想到的几乎所有常用外设多种定时器、ADC/DAC、通信接口UART, I2C, SPI, CAN, USB等、加密模块等。对于电赛这种高度集成化的项目一颗MCU往往就能解决大部分问题减少了外围芯片提高了系统可靠性。开发工具成熟Keil MDK、IAR、STM32CubeIDE等IDE支持完善调试工具ST-Link便宜又好用。3.2 为什么是HAL库兼论与标准库的取舍这是新手最困惑的问题之一。早期电赛多用标准库Standard Peripheral Library现在HAL库Hardware Abstraction Layer已成为ST主推和CubeMX默认生成的标准。HAL库的优势跨型号兼容性HAL库的API在不同系列的STM32芯片上高度统一。比如操作一个UART在F1和F4系列上代码几乎一样移植起来非常方便。这对于电赛这种可能临时更换MCU型号的场景非常友好。与CubeMX无缝集成ST的图形化配置工具CubeMX可以直观地配置时钟、引脚、外设参数并自动生成初始化代码。这大大减少了底层寄存器配置的工作量让你能更专注于应用逻辑。尤其是配置复杂的外设如USB、以太网时优势明显。结构更现代采用了句柄Handle结构体来管理外设状态代码结构更清晰。提供了更完善的超时Timeout管理和错误回调Callback机制有助于编写更健壮的程序。标准库的“遗风”代码效率一些资深开发者认为标准库更接近底层代码体积和执行效率可能略优于HAL库。在极端资源受限如Flash只有几十KB的老型号或对时序极其苛刻如某些高速通信协议模拟的场景下有人仍会选择标准库或直接操作寄存器。学习价值标准库能让你更清楚地理解外设的工作流程因为它是对寄存器操作的一层较薄的封装。我的建议是对于电赛和绝大多数应用坚定地选择HAL库。它的开发效率优势是压倒性的。所谓的性能差距在STM32如今动辄几百MHz主频、上百KB RAM的硬件条件下对于电赛题目而言几乎可以忽略。你应该把宝贵的时间花在系统架构和算法实现上而不是纠结于如何更高效地配置一个寄存器。注意网上有些教程会教你如何用DWT数据观察点单元替代HAL库的HAL_Delay函数以实现更精确的微秒级延时。这确实是个有用的技巧因为HAL_Delay是基于SysTick的毫秒级延时且会阻塞CPU。在需要精确时序如驱动WS2812灯珠时可以考虑使用DWT。但这属于对HAL库的补充优化而不是否定HAL库本身。4. 系统设计与模块化构建实战面对一个电赛题目切忌一上来就打开CubeMX点点点或者直接埋头写代码。好的设计是成功的一半。我们需要一个系统化的构建方法。4.1 硬件系统框架设计首先根据题目要求画出系统框图。这能帮你理清信号流和数据流。传感器1 - 信号调理 - ADC - STM32 - 算法处理 - PWM/DAC - 执行机构 传感器2 - ... - UART/I2C/SPI - 显示/通信模块 按键/旋钮 - GPIO中断 - ...关键设计点电源树这是整个系统的基石。明确各部分所需电压3.3V, 5V, ±12V等、电流。使用LDO还是DCDC模拟部分和数字部分的电源是否需要隔离电机驱动的大电流路径要单独走线避免噪声串扰到MCU。传感器与执行机构接口数字传感器如DHT11温湿度注意时序要求HAL库的HAL_Delay在时序要求严苛时可能不准需调整或使用定时器。模拟传感器如MQ135气体传感器需要设计合理的运放调理电路将信号调整到STM32的ADC量程内通常0-3.3V。注意参考电压的稳定性。电机驱动直流电机常用H桥驱动芯片如TB6612、DRV8833注意使能、方向、PWM控制线的连接。步进/伺服电机则需对应的驱动器。通信模块如OLED通常I2C/SPI、蓝牙/Wi-Fi模块UART。注意电平匹配5V模块与3.3V MCU之间可能需要电平转换。PCB布局布线如果时间允许自己画板要遵循“模块化布局、星型接地、电源先宽后细、信号线远离干扰源”等原则。对于ADC采样通道走线要短尽量远离数字信号线和电源线。4.2 软件架构与模块化编程软件上强烈推荐采用模块化和面向对象的思想来组织代码即使是用C语言。工程目录结构Project/ ├── Core/ (CubeMX生成的核心文件如main.c, gpio.c等) ├── Drivers/ (HAL库、CMSIS等) ├── Inc/ (自定义头文件) │ ├── bsp/ (板级支持包按键、LED、蜂鸣器驱动) │ ├── dev/ (设备驱动oled.h, mpu6050.h, motor.h) │ ├── algo/ (算法模块pid.h, filter.h) │ └── sys/ (系统模块timer.h, state_machine.h) ├── Src/ (自定义源文件与Inc目录对应) └── Middlewares/ (第三方库如FreeRTOS, LVGL)创建设备驱动模块以驱动OLEDSSD1306I2C接口为例。oled.h声明初始化函数OLED_Init()、清屏OLED_Clear()、显示字符串OLED_ShowString()、显示图像OLED_DrawBMP()等API。oled.c实现这些API内部调用HAL库的HAL_I2C_Mem_Write等函数进行底层通信。将I2C句柄、设备地址等作为模块内部静态变量或通过初始化函数传入。好处主程序只需要调用OLED_ShowString(1, 1, Hello)完全不用关心底层是I2C还是SPI。如果更换了显示屏型号只需修改oled.c上层应用代码无需变动。应用层与底层分离主循环while(1)中尽量只出现高层逻辑如StateMachine_Run()状态机运行、ControlLoop_Update()控制环更新。具体的传感器读数、电机控制等都在相应的模块函数内完成。5. 关键外设的HAL库驱动与深度优化电赛题目会逼你用遍STM32的各种外设。这里挑几个最常考、也最容易出问题的讲讲HAL库下的实战用法和坑。5.1 ADC多通道扫描与DMA传输这是仪器仪表和电源类题目的核心。要求高速、同步采集多个通道比如三相电流、电压。常规做法轮询/中断配置多通道扫描使能ADC在转换完成中断中读取数据。问题CPU频繁被中断占用在高速采样时可能来不及处理其他任务。推荐做法DMA循环模式CubeMX配置在ADC配置中使能“DMA Continuous Requests”并添加一个DMA通道模式设为“Circular”循环。这样ADC每转换完一组数据DMA会自动将其搬运到你指定的内存数组中并周而复始无需CPU干预。代码实现// 在main.c的全局变量区 uint16_t adc_values[3]; // 假设3个通道 // 在main函数初始化后 HAL_ADC_Start_DMA(hadc1, (uint32_t*)adc_values, 3);之后adc_values这个数组里就会自动、实时地更新着三个通道的ADC值。你可以在任何需要的时候直接读取它。注意事项数据对齐确保DMA搬运的数据宽度字节、半字、字与ADC分辨率匹配。缓冲区溢出在“Circular”模式下DMA会覆盖旧数据。如果你的处理速度跟不上采样速度需要采用“双缓冲区”Double Buffer模式利用DMA的半传输完成和传输完成中断来切换缓冲区进行处理。触发源如果需要定时采样如固定10kHz可以将ADC的转换触发源设置为某个定时器的更新事件TRGO实现硬件级别的精准定时采样。5.2 定时器PWM输出、输入捕获与正交编码定时器是STM32的灵魂外设。PWM输出用于控制电机速度、舵机角度、LED亮度等。CubeMX配置选择定时器某个通道设为“PWM Generation CHx”。关键参数预分频器PSC、自动重装载值ARR共同决定PWM频率Frequency Timer_CLK / ((PSC1)*(ARR1))。脉冲宽度通过__HAL_TIM_SET_COMPARE(htimx, TIM_CHANNEL_x, pulse)设置占空比 pulse / (ARR1)。高级应用对于电机驱动可能需要互补输出和死区插入Break and Dead-time这需要用到高级定时器TIM1, TIM8并在CubeMX中配置“Break and Dead-time”功能以保护功率管。输入捕获用于测量脉冲宽度或频率例如测超声波模块回响时间、红外遥控信号解码。CubeMX配置通道设为“Input Capture direct mode”。开启捕获中断。代码思路在捕获中断回调函数HAL_TIM_IC_CaptureCallback中读取捕获比较寄存器CCR的值。两次捕获值之差即为脉冲高电平时间。注意定时器溢出处理对于长脉冲可能需要结合溢出次数计算。正交编码器接口用于读取带编码器的直流电机转速和方向是控制类题目的必备。CubeMX配置最简单将定时器模式选为“Encoder Mode”。将编码器的A、B相分别接到定时器的CH1和CH2引脚。代码使用启动编码器模式HAL_TIM_Encoder_Start(htimx, TIM_CHANNEL_ALL)。之后直接读取定时器的计数器值__HAL_TIM_GET_COUNTER(htimx)其值的变化量和方向就对应了电机的位移和转向。注意定时器的ARR值要设置得足够大如65535防止快速正反转时计数值溢出。5.3 串口通信不定长数据接收与高效处理与上位机电脑、其他模块蓝牙、GPS通信串口是首选。如何稳定接收不定长数据是一大考验。不推荐做法在HAL_UART_Receive中死等或在一个字节的中断里拼装数据。前者会阻塞系统后者效率低且容易丢失数据。推荐做法空闲中断Idle Interrupt DMA这是HAL库下处理不定长数据的“黄金组合”。CubeMX配置使能串口的全局中断并在代码中手动开启空闲中断。代码流程// 全局变量 uint8_t uart_rx_buf[256]; // DMA接收缓冲区 // 初始化后 HAL_UART_Receive_DMA(huart1, uart_rx_buf, 256); // 启动DMA接收填满整个缓冲区 __HAL_UART_ENABLE_IT(huart1, UART_IT_IDLE); // 使能空闲中断// 在stm32fxx_it.c的USART1_IRQHandler中或使用HAL库的回调机制 void HAL_UART_IdleCallback(UART_HandleTypeDef *huart) { if(huart-Instance USART1) { __HAL_UART_DISABLE_IT(huart1, UART_IT_IDLE); // 先关闭中断 uint16_t len 256 - __HAL_DMA_GET_COUNTER(huart1.hdmarx); // 计算接收到的数据长度 if(len 0) { // 处理 uart_rx_buf 中长度为 len 的数据 process_uart_data(uart_rx_buf, len); } // 重新启动DMA接收 HAL_UART_Receive_DMA(huart1, uart_rx_buf, 256); __HAL_UART_ENABLE_IT(huart1, UART_IT_IDLE); } }原理DMA负责将接收到的数据默默搬到uart_rx_bufCPU不参与。当一帧数据发送完毕串口总线会有一段空闲时间此时产生“空闲中断”。我们在中断里计算出DMA已经搬运了多少数据即本次接收的长度然后进行处理。处理完再重新启动DMA接收等待下一帧。避坑指南网上很多例程在空闲中断回调里直接调用HAL_UART_Receive_DMA重启但如果在处理数据的过程中特别是处理时间较长时又来了新数据可能会造成数据覆盖或DMA状态错误。更稳健的做法是设置一个标志位在中断里只置位标志和计算长度在主循环里处理数据和重启DMA。6. 控制系统核心PID算法实现与整定无论是控制小车速度、位置还是稳定电源电压PID都是绕不开的经典算法。这里不讲理论只讲在STM32上如何实现和调试。6.1 位置式PID的代码实现一个健壮的PID模块应该包含以下要素// pid.h typedef struct { float Kp, Ki, Kd; // PID参数 float integral; // 积分项 float prev_error; // 上一次误差 float integral_limit; // 积分限幅 float output_limit; // 输出限幅 } PID_Controller; void PID_Init(PID_Controller *pid, float kp, float ki, float kd, float i_limit, float out_limit); float PID_Calculate(PID_Controller *pid, float setpoint, float measurement);// pid.c float PID_Calculate(PID_Controller *pid, float setpoint, float measurement) { float error setpoint - measurement; pid-integral error; // 积分限幅防止积分饱和Integral Windup if(pid-integral pid-integral_limit) pid-integral pid-integral_limit; else if(pid-integral -pid-integral_limit) pid-integral -pid-integral_limit; float derivative error - pid-prev_error; pid-prev_error error; float output pid-Kp * error pid-Ki * pid-integral pid-Kd * derivative; // 输出限幅 if(output pid-output_limit) output pid-output_limit; else if(output -pid-output_limit) output -pid-output_limit; return output; }6.2 PID参数整定实战技巧电赛现场调PID时间紧任务重讲究“快、准、稳”。准备工作确保你的传感器反馈如编码器值、ADC电压是准确的、稳定的。垃圾进垃圾出反馈不准PID调再好也白搭。确定控制周期。用一个定时器中断固定时间如1ms、5ms执行一次PID计算和控制输出。周期太慢响应迟滞太快可能系统来不及响应或引入噪声。齐格勒-尼科尔斯Z-N法快速上手第一步将Ki和Kd设为0逐渐增大Kp直到系统出现等幅振荡比如小车来回匀速摆动而不是发散。记录此时的Kp值为Ku振荡周期为Tu。第二步根据公式计算经典PID参数Kp 0.6 * KuKi 2 * Kp / Tu注意这里的Ki是公式中的系数需要除以控制周期T即Ki (2*Kp/Tu) * TT是你的控制周期秒数Kd Kp * Tu / 8这组参数通常能提供一个不错的起点。手动微调“看曲线”最好能有实时绘图工具如串口发送数据到上位机用SerialPlot、VOFA等软件显示。观察系统响应曲线设定值、实际值、输出值。Kp比例增大Kp能加快响应减小静差但过大会引起振荡甚至不稳定。先调Kp让系统能较快跟上设定值变化允许有一些超调。Kd微分增大Kd可以抑制超调增加系统阻尼让响应更平滑。但Kd对噪声非常敏感如果传感器噪声大微分项会放大噪声导致输出抖动。此时可能需要先对测量值进行滤波如一阶低通滤波。Ki积分用于消除静差稳态误差。慢慢增大Ki直到静差在可接受范围内。一定要加积分限幅否则在系统启动或设定值突变时误差累积会导致巨大的“积分饱和”输出卡在极限值系统失控。电赛场景下的“土办法”如果没时间细调一个保守的策略是Kp保证基本跟随Ki给一个很小的值慢慢消除静差Kd暂时为0。这样系统可能响应慢点但至少是稳定的能拿到基本要求的分数。这比一个振荡甚至发散的系统要好得多。7. 系统调试、问题排查与性能优化实录调试是电赛过程中最耗时、也最考验经验的环节。这里记录一些典型问题的排查思路和“救命”技巧。7.1 常见问题速查表现象可能原因排查思路程序下载后不运行1. 启动模式跳线错误应为Flash启动2. 复位电路问题3. 晶振未起振4. 电源电压不稳1. 检查BOOT0/BOOT1引脚电平。2. 测量NRST引脚电压手动复位试试。3. 用示波器测晶振引脚波形。4. 测量MCU供电引脚电压纹波是否过大。外设如UART、I2C初始化失败1. CubeMX时钟树配置错误外设时钟未使能2. 引脚复用冲突如JTAG引脚用作GPIO3. HAL库句柄未正确传递或初始化顺序错误1. 在SystemClock_Config()后调用__HAL_RCC_xxx_CLK_ENABLE()。2. 检查CubeMX的引脚分配视图禁用不用的调试接口如JTAG。3. 确保外设初始化函数如MX_USART1_UART_Init()被正确调用且在主函数初始化流程中。串口能发不能收或收数据乱码1. 波特率、数据位、停止位、校验位不匹配2. 硬件流控未正确配置3. 接收缓冲区溢出或处理不及时4. 电平不匹配3.3V vs 5V1. 双发确认通信参数。2. 如果不使用RTS/CTS在CubeMX中禁用硬件流控。3. 检查是否使用了DMA或中断并确保及时读取数据。4. 检查通信双方电平必要时加电平转换芯片。ADC采样值跳动大1. 模拟地AGND噪声大2. 参考电压VREF不稳3. 采样时间太短4. 信号源本身有噪声或阻抗不匹配1. 优化PCB布局模拟部分单点接地。2. 使用独立的基准电压源芯片为VREF供电。3. 在CubeMX中增加ADC的“采样时间”Sample Time。4. 在信号输入端加RC低通滤波。电机控制抖动、噪音大1. PID参数不合适特别是微分项D2. PWM频率不在电机最佳工作范围3. 电源功率不足带负载后电压跌落4. 电机驱动桥臂直通死区时间不足1. 重新整定PID尝试减小Kd或对反馈值滤波。2. 尝试调整PWM频率通常几kHz到几十kHz。3. 测量电机工作时电源电压波形。4. 检查高级定时器的死区时间配置。程序运行一段时间后死机1. 堆栈溢出2. 中断服务程序执行时间过长或发生嵌套导致硬件错误3. 内存泄漏频繁malloc/free4. 看门狗未喂狗1. 在启动文件.s中增大堆栈大小。2. 优化中断服务程序只做标志位设置等最小操作。3. 避免在嵌入式实时系统中动态分配内存。4. 如果使能了独立看门狗IWDG确保在主循环或定时中断中定期喂狗。7.2 调试工具与技巧LED和串口是最朴素的调试工具在关键代码段前后点亮不同的LED或通过串口打印变量值和状态标志printf重定向到串口。虽然原始但在没有仿真器的现场极其有效。逻辑分析仪是神器几十块钱的USB逻辑分析仪配合Sigrok/PulseView软件可以同时抓取多路数字信号如PWM、UART、I2C、SPI波形直观分析时序是否正确是调试通信协议、测量脉冲宽度的利器。ST-Link CubeIDE/Keil调试实时变量查看在调试模式下可以将关键变量如PID误差、ADC值添加到“Live Watch”窗口实时观察其变化。断点和单步用于定位程序卡死或逻辑错误的位置。注意在中断服务程序里打断点要小心可能会影响实时性。性能分析有些IDE可以统计函数调用时间和次数帮助优化代码热点。示波器看电源和信号质量用示波器测量MCU的供电引脚纹波、ADC输入信号波形、PWM输出波形。很多“玄学”问题根源都在电源噪声上。7.3 代码与性能优化当系统功能都实现后为了追求更高的性能指标如控制频率、响应速度可能需要优化。编译器优化等级在Keil或CubeIDE的工程选项里将优化等级从-O0不优化提高到-O1或-O2可以显著减小代码体积和提高运行速度。但优化等级越高调试可能会越困难变量被优化掉。关键代码用寄存器操作对于极度频繁调用的函数如某个数学运算如果HAL库函数调用开销过大可以考虑在函数内部直接操作寄存器。但要做好注释因为这牺牲了可读性和可移植性。浮点运算如果MCU不带硬件浮点单元FPU浮点运算会非常慢。可以考虑使用q格式定点数运算来替代浮点。将常数提前计算好避免运行时计算。对于PID如果精度要求不是极高可以将参数和变量都乘以一个缩放因子如1000用整数运算。减少HAL_Delay的使用这个函数是阻塞式的会浪费CPU时间。尽量用定时器中断产生标志位的方式来替代延时。例如需要一个10ms的周期任务就设置一个10ms的定时器中断在中断里置位一个标志在主循环里检查这个标志并执行任务。8. 从赛题到作品工程化思维与备赛建议最后我想谈谈比具体技术更重要的东西工程化思维。电赛不只是编程和焊电路它是一个微型的产品开发过程。版本管理即使是一个人开发也强烈建议使用Git。每天结束工作前提交一次。这能在你代码改崩了的时候快速回退到上一个可用的版本是真正的“后悔药”。文档记录准备一个电子实验室笔记本可以用OneNote、Notion或简单的Markdown文件。记录每天的工作进展、遇到的问题、尝试的解决方案、测试的数据、电路的修改。这不仅是整理思路的过程在最后撰写报告时这些记录就是宝贵的素材。模块测试不要等所有模块都做好了再联调。每做好一个部分如电机驱动、传感器读取就单独编写测试程序验证其功能是否正常。确保每个模块都是可靠的“乐高积木”。系统联调与压力测试所有模块组合后进行长时间、全功能、满负荷的测试。模拟比赛时的各种操作观察系统是否稳定有没有内存泄漏会不会偶尔死机。备赛建议吃透往年赛题像“2021年电赛H题.zip”这样的往年赛题是最好的练习材料。尝试独立或组队完成限时模拟。做完后对比优秀作品找差距。构建自己的代码库把调试好的OLED驱动、MPU6050驱动、PID库、滤波器、串口协议解析等代码整理成干净的模块放在自己的代码仓库里。下次比赛或项目直接复用事半功倍。熟悉你的武器提前准备好常用的模块电机驱动板、传感器模块、显示模块和代码模板。比赛时才能快速搭建原型。电赛的魅力就在于在极限的压力下将书本上的知识转化为一个实实在在能跑起来的系统。这个过程痛苦但也充满成就感。希望这篇基于“2021年电赛H题.zip”延展开的漫谈能为你提供一些不止于这道题目的、更普适的嵌入式开发思路和实战技巧。记住最重要的不是记住某个特定的代码片段而是理解系统是如何工作的以及当它不工作时你该如何有条不紊地找到问题所在。本文还有配套的精品资源点击获取
返回列表