第一次接触蓝桥杯嵌入式赛题时,我被那个四层升降控制器的题目难住了——状态机跳转逻辑复杂、PWM参数配置繁琐、LED流水灯效果与楼层显示要同步更新。直到真正用STM32G431实现了一遍,才发现这些看似困难的技术点,拆解后都有清晰的解决路径。本文将带你从工程构建到烧录调试,完整复现这个典型嵌入式控制系统的开发过程。
拿到赛题后,我习惯先画出硬件连接示意图。这个升降控制器需要管理:
关键外设初始化顺序尤为重要:
c复制void Hardware_Init(void)
{
HAL_Init();
SystemClock_Config(); // 优先配置系统时钟
LED_KEY_Init(); // GPIO和外部中断
LCD_Init(); // 显示模块
TIM2_Init(); // 门控PWM
TIM3_Init(); // 升降PWM
TIM17_Init(); // 辅助PWM
}
注意:STM32G431的TIM2和TIM17是32位定时器,适合需要高精度控制的场景
外设配置常见问题排查表:
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| PWM无输出 | 时钟未使能 | 检查RCC相关寄存器 |
| 按键无反应 | 上拉电阻未配置 | 设置GPIO为内部上拉 |
| LCD显示乱码 | 初始化时序错误 | 调整延时参数 |
升降控制的核心是8状态有限状态机(FSM),我的实现方案是:
状态迁移逻辑:
c复制void Lift_Ctrl(void)
{
switch(Status_Ctrl){
case 0: // 状态0处理
if(key_press_flag) Status_Ctrl = 1;
break;
case 1: // 状态1超时检测
if((uwTick - uwTick_Time_Count)>=1000)
Status_Ctrl = 2;
break;
// 其他状态处理...
}
}
状态机优化技巧:
uwTick作为全局时间基准比赛要求用不同占空比控制升降速度:
PWM配置关键代码:
c复制// TIM3初始化示例(1kHz频率)
htim3.Instance = TIM3;
htim3.Init.Prescaler = 84-1; // 84MHz/84=1MHz
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 1000-1; // 1MHz/1000=1kHz
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_PWM_Init(&htim3);
// 设置上行占空比80%
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, 800);
提示:G431的TIM3_CH1默认对应PA6,但可能与LCD引脚冲突,需检查原理图
按键处理采用经典的"三行代码"消抖方案:
c复制key_value = KEY_Scan(); // 获取原始键值
key_down = key_value & (key_value ^ key_old); // 下降沿检测
key_up = ~key_value & (key_value ^ key_old); // 上升沿检测
LED显示策略:
c复制// 上行流水灯实现
ucLED &= 0x0F; // 保留低4位
ucLED |= (1<<LED_flow_up);
if(++LED_flow_up > 7) LED_flow_up = 4;
LCD显示优化:
sprintf格式化字符串在实验室调试时,这几个工具特别有用:
典型问题解决记录:
现象:升降机到楼层后不停
Set_floor清除逻辑Set_floor &= ~(1<<(Current_floor-1))现象:流水灯卡顿
HAL_Delay使用位置uwTick时间戳控制移植到不同开发板时,记得检查:
完成基础功能后,可以尝试:
内存优化技巧:
c复制#pragma pack(push, 1) // 1字节对齐
typedef struct {
uint8_t current_floor;
uint8_t target_floors;
uint8_t direction;
} Lift_State;
#pragma pack(pop)
这个项目让我深刻体会到,嵌入式开发就是不断在硬件限制和功能需求之间寻找平衡点。最值得分享的经验是:先确保状态机迁移正确,再完善各个状态的具体行为——这能节省大量调试时间。