当前位置: 首页 > news >正文

【电赛保姆级教程】别在比赛时从零写代码了!电赛“祖传代码库”搭建与OLED多级菜单硬核指南

前言
全国大学生电子设计竞赛虽然是“四天三夜”的极限挑战,但它本质上是一场开卷考试。因为比赛允许你携带以前写好的所有代码和模块!
如果你在发题后,还在慢吞吞地去网上找 OLED 驱动、还在写带 delay() 的按键防抖逻辑,你的比赛已经输了一半。
真正的高手,在赛前就已经备好了一套**“祖传代码库”,比赛时只需要像搭积木一样调用。本文将手把手教你如何打造一套包含无阻塞按键状态机、OLED 丝滑多级菜单、Flash 掉电保存**的终极人机交互框架!

@TOC


一、 为什么你需要一套“人机交互”框架?

不管你选什么题,到了测评现场,评委都会提出这些要求:

  • 电源题:“同学,把输出电压从 5V 调到 12V 给我看看。”

  • 信号题:“把正弦波频率切换到 100kHz,步进 10Hz。”

  • 控制题:“小车先跑基础题代码,再跑发挥题代码。”

如果你只能靠拔插杜邦线、或者重新烧录代码来改变状态,评委直接扣掉操作分。你必须拥有一个**“屏幕显示 + 按键调参”**的 UI 系统。


二、 告别 delay:无阻塞按键状态机(核心源码)

初学者的按键防抖都是 if(KEY==0) { delay_ms(20); if(KEY==0) { ... } }。
在复杂的电赛系统中,这 20ms 的死等可能会让你的无人机直接坠毁,让你的 ADC 丢失几十个采样点!

🏆 进阶方案:基于时间片的按键状态机

将按键检测放入每 10ms 执行一次的定时器中断或时间片中。通过记录按键按下的“持续时间”,轻松实现单击、双击、长按功能,且0阻塞CPU

核心逻辑(直接抄作业):

codeC

// 定义按键状态枚举 typedef enum { KEY_STATE_IDLE = 0, // 空闲 KEY_STATE_PRESS, // 刚按下(防抖确认) KEY_STATE_HOLD, // 持续按下(长按判断) KEY_STATE_RELEASE // 松开 } KeyState_t; uint8_t Key_Scan_SM(void) { static KeyState_t state = KEY_STATE_IDLE; static uint16_t hold_time = 0; uint8_t key_event = 0; // 0:无动作, 1:单击, 2:长按 uint8_t key_val = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0); // 读取物理按键(假设低电平按下) switch (state) { case KEY_STATE_IDLE: if (key_val == 0) { state = KEY_STATE_PRESS; // 发生跳变,进入防抖 } break; case KEY_STATE_PRESS: if (key_val == 0) { state = KEY_STATE_HOLD; // 10ms后依然是低电平,确认为有效按下 hold_time = 0; } else { state = KEY_STATE_IDLE; // 只是抖动 } break; case KEY_STATE_HOLD: if (key_val == 0) { hold_time++; // 计时 if (hold_time == 100) { // 100 * 10ms = 1秒 key_event = 2; // 触发长按事件! } } else { if (hold_time < 100) { key_event = 1; // 未达到长按时间就松手,触发单击事件! } state = KEY_STATE_RELEASE; } break; case KEY_STATE_RELEASE: state = KEY_STATE_IDLE; // 等待下一次按下 break; } return key_event; }

用法:把这个函数扔进 10ms 的定时器任务里。返回值是 1 就让菜单光标往下走,返回值是 2 就进入子菜单,丝滑无比。


三、 C语言的艺术:OLED 多级菜单架构

如果要在 OLED 上写菜单,很多人的代码是无数个 if...else 和 switch...case 嵌套,最后连自己都不知道在哪一页了,这叫“屎山代码”。
真正优雅的做法是:表驱动法(Table-Driven)+ 链表/结构体数组。

1. 结构体定义:菜单的“节点”

我们把每一个菜单页面(如“主菜单”、“PID设置”、“电压输出”)看作一个节点,节点里包含了当前页面的内容,以及按下不同按键后应该跳转的下一个节点的索引。

codeC

typedef struct { uint8_t current_index; // 当前页面索引 uint8_t next_up; // 按下"上键"跳转的索引 uint8_t next_down; // 按下"下键"跳转的索引 uint8_t next_enter; // 按下"确认键"跳转的索引 uint8_t next_back; // 按下"返回键"跳转的索引 void (*show_func)(void); // 刷新当前屏幕的函数指针(重要!) } Menu_Item_t;

2. 菜单页面配置(查表)

用一个静态数组把所有页面链接起来,简直是艺术:

codeC

// 页面索引宏定义 #define PAGE_MAIN 0 #define PAGE_SET_PID 1 #define PAGE_SET_VOLT 2 // 页面显示函数声明 void Show_Main_Page(void); void Show_PID_Page(void); void Show_Volt_Page(void); // 核心菜单映射表 const Menu_Item_t Menu_Table[] = { // index, UP, DOWN, ENTER, BACK, show_func {PAGE_MAIN, PAGE_MAIN, PAGE_MAIN, PAGE_SET_PID, PAGE_MAIN, Show_Main_Page}, {PAGE_SET_PID, PAGE_SET_PID, PAGE_SET_PID, PAGE_SET_PID, PAGE_MAIN, Show_PID_Page}, {PAGE_SET_VOLT, PAGE_SET_VOLT, PAGE_SET_VOLT, PAGE_SET_VOLT, PAGE_MAIN, Show_Volt_Page}, };

3. 主循环菜单调度器

在你的主循环中,只需要一个全局变量 current_page 记录当前处于哪一页,然后根据按键查表跳转!

codeC

uint8_t current_page = PAGE_MAIN; void Menu_Task(void) { uint8_t key = Get_Key_Value(); // 获取按键键值 // 状态转移逻辑 if (key == KEY_UP) current_page = Menu_Table[current_page].next_up; if (key == KEY_DOWN) current_page = Menu_Table[current_page].next_down; if (key == KEY_ENTER) current_page = Menu_Table[current_page].next_enter; if (key == KEY_BACK) current_page = Menu_Table[current_page].next_back; // 执行当前页面的显示函数 Menu_Table[current_page].show_func(); }

总结:有了这个框架,比赛时如果要增加一个“视觉识别模式”的页面,你只需要在表里加一行代码,写一个对应的显示函数即可,系统极度稳定,一辈子都不会出现菜单卡死或死循环!


四、 救命神技:掉电保存参数(Flash / EEPROM)

想象一下:你花了三个小时在赛场外面的走廊上,把小车的 PID 调到了完美状态。然后你需要拔掉电池,抱着小车走进测评室。一插电,小车的参数全部归零了......(每年都有这种惨案)。

比赛中,所有重要的可调参数,必须存入非易失性存储器!

STM32 内部 Flash 读写方案(无需外挂芯片)

STM32 内部除了存代码,还可以留出最后一两页(Page)作为数据存储区。

封装一套简单的函数:

  1. 开机读取:系统启动时,首先从 Flash 指定地址(如 0x0801FC00)读取 PID 结构体。如果是 0xFFFFFFFF(说明是第一次开机没存过),就给一个默认值。

  2. 菜单保存:在你的 OLED 菜单里加一个选项叫“Save Parameters”。当你调好参数后,点一下保存。

  3. 保存逻辑:先 HAL_FLASH_Unlock() 解锁,再擦除对应的 Page,最后用 HAL_FLASH_Program() 把当前的参数结构体写入 Flash,上锁。

注:电赛期间不要在主循环里实时写入 Flash,Flash 有擦写寿命限制,且擦写过程耗时较长可能阻塞中断,做成“手动触发保存”最稳妥。


五、 赛前模块化大一统:硬件接口标准化

除了代码框架,硬件的“祖传模块”也必须准备好。不要在比赛时东拼西凑杜邦线。

  1. IIC 大统一:OLED 屏幕、MPU6050、EEPROM,这些全是 IIC 接口。赛前自己画一块主板,引出多个GND-VCC-SCL-SDA排母接口,比赛时即插即用,一条总线挂载所有设备。

  2. 串口大统一:预留至少 3 个标准 UART 接口(VCC-GND-TX-RX)。一个接蓝牙/透传模块(调参用),一个接 OpenMV/K210(视觉用),一个留作备用。

  3. 万能测试夹:准备几根一头是杜邦线、一头是“微型测试钩(夹子)”的线。测板子引脚信号时,直接钩在芯片管脚上,解放双手,绝不会因为手抖造成短路。


结语

在电赛的考场上,最可怕的不是你不会算某个复杂的算法,而是你花了大量的时间在修补底层的 Bug(按键不灵、屏幕花屏、数据丢失)。

磨刀不误砍柴工。距离比赛还有几个月,趁现在,去建立属于你们队伍的 Github/Gitee 仓库,把这些造好的“轮子”封装成一个个 .c 和 .h 文件。
当比赛发题的那一刻,别的队伍还在新建工程写 HAL_Init,你们的屏幕上已经亮起了丝滑的多级菜单,这就是高手的降维打击。

预祝大家:按键零抖动,菜单纵享丝滑,参数永不丢失,国一稳稳拿下!🏆


觉得干货满满?
👍点赞+ ⭐收藏,赶快去你的工程里把这套框架搭起来!
你在写 OLED 菜单或者按键时踩过什么坑?你有什么私藏的“造轮子”技巧?欢迎在评论区分享交流!👇

http://www.gsyq.cn/news/1429828.html

相关文章:

  • 2026年5月AI模型性能排行:代码能力Claude霸榜,智谱GLM杀入前十
  • 调试记录 - 2024年1月15日
  • 告别排版焦虑:西安交大LaTeX论文模板让你专注学术创新
  • 【电赛保姆级教程】别再用L298N了!电赛电机驱动与高阶控制(带FOC扫盲)硬核避坑指南
  • LabVIEW与外部设备通信秘籍:用DLL传递复杂结构体(含数组/嵌套结构)的完整配置流程
  • 那些年,我追Google Trends追到精疲力尽的故事
  • 深入FIO引擎:除了libaio,这些ioengine(如sync, psync, mmap)在Linux下到底怎么选?性能差多少?
  • 口袋神器!Arduino 创客必备,可接入 DeepSeek、Qwen 等 AI 大模型,通过 GPIO 串口控制 IoT 智能设备
  • C# 泛型
  • C++之父开撕AI Coding:资深开发者宁愿退休也不愿伺候AI生成的代码
  • 为什么你的论文参考文献格式总是不对?3个GB/T 7714 BibTeX样式终极解决方案
  • 187、运动控制中的行业应用:机械臂力控打磨
  • 前端内存泄漏常见场景与排查
  • GTA5线上小助手:免费开源工具帮你轻松称霸洛圣都终极指南
  • Kettle官网大变样?别慌!手把手教你找到最新9.3版本的下载入口(附Hadoop Shims获取指南)
  • 【AI+房地产实战指南】:2024年最值得落地的7大智能整合场景与避坑清单
  • ARP 协议:网络世界里的“地址翻译官“
  • SBM-20-1盖革管3D打印端盖制作:从零打造专业级辐射探测器接口
  • 2026AI漫剧创作深度测评:如何为你的创作需求匹配最佳方案? - 速递信息
  • 189、运动控制中的行业应用:医疗设备(手术机器人)
  • 英雄联盟R3nzSkin换肤工具实战指南:国服安全自定义皮肤完整方案
  • yuzu模拟器架构深度解析:从Switch硬件仿真到跨平台渲染优化
  • 2026年AI漫剧创作推荐榜:主流工具平台深度测评,优质品牌选型指南 - 速递信息
  • Translumo:专为游戏玩家设计的屏幕实时翻译工具,打破语言障碍的终极解决方案
  • 平台算法审核已升级!你的AI视频正被自动标记为“潜在侵权内容”(附2024主流平台检测逻辑逆向分析)
  • TPAMI 2026 | DC-SAM 横空出世!融合 SAM 特征,打造图像视频通用上下文分割框架
  • 2026年专业做床垫的公司哪家强?南宁市雅兰床垫值得一探! - 资讯快报
  • 2026年华为OD机试(A卷,100分)- 机器人(Java JS Python)带详细答案和源码
  • 终极JSON转Java实体类工具:3分钟掌握GsonFormatPlus完整使用指南
  • 虚表 —— 表头多按钮示例