news 2026/9/30 2:46:28

STM32+FreeRTOS+OLED多级菜单

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STM32+FreeRTOS+OLED多级菜单

在成功使用上一章节的RCT时钟后,基于FreeRTOS来实现简单的时钟功能,我将功能划分如下:主界面、选择菜单、时间、闹钟、地区各设置选项。

在做好任务的构思后,先来理清各个任务之间的触发关系。在菜单中的操作命令:上移、下移、进入、返回由按键通过外部中断触发,在触发并接收命令后,执行各任务流程,刷新OLED,修改时钟、闹钟设置,修改地区设置并通过WiFi通知服务器修改要获取天气位置。

因为菜单选项较多,因此引入Free RTOS中的队列来用于记录当前菜单位置,需引入"queue.h"。菜单的结构分为两个部分来定义更清楚,第一部分是菜单中的某个选项与他对应的下一级菜单之间的联系,比如闹钟选项与其子菜单的关系。第二部分是当前菜单中相关的设置,menu_index用于说明当前菜单页中,被选中的菜单项下标(0 开始),menu_item_count则是说明当前菜单页包含的菜单项总数。

typedef struct menu_item_s { const char *next_menu_name; void (*action)(void); // 可选:确认时执行的动作 const struct menu_item_s *next_items; // 子菜单数组 uint8_t next_count; // 子菜单项数 }menu_item_t; typedef struct { const menu_item_t *menu_items; uint8_t menu_index; uint8_t menu_item_count; }menu_page_t; extern menu_page_t menu_stack[MENU_STACK_MAX]; extern xQueueHandle xQueueKey; //示例 ,这里主菜单中选择闹钟菜单-》对应的闹钟子菜单中有4个选项 const menu_item_t main_menu[] = { {"Time", NULL, time_menu, 2}, {"Alarm", NULL, alarm_menu, 4}, {"Area", NULL, area_menu, 1}, }; const menu_item_t alarm_menu[] = { {"Alarm Add", alarm_add_page, NULL, 0}, {"Alarm Delete", alarm_delete_page, NULL, 0}, {"Alarm Enable", alarm_enable_page, NULL, 0}, {"Alarm Disable", alarm_disable_page, NULL, 0}, };

这里因为涉及到按键和OLED屏两个硬件设备,因此我将任务划分为两部分,按键任务用于检测是否有按键中断,显示任务负责接收按键的时间信息并通知执行任务。这里还是使用的轮询扫描,我明天再看看能不能优化一下直接用中断发送信号量来获取。

void key_task(void *pvParameters) { key_event_t evt; while (1) { evt = KEY_Scan(); if (evt != NO_PRESS) { xQueueSend(xQueueKey, &evt, 0); } vTaskDelay(pdMS_TO_TICKS(20)); // 去抖 } } void oledmenu_task(void *pvParameters) { key_event_t evt; main_window_render(); // 开机先显示一次 while (1) { if (xQueueReceive(xQueueKey, &evt, pdMS_TO_TICKS(500)) == pdTRUE) { if(menu_top == -1) { // 如果栈为空,按下任意键进入主菜单 menu_push(main_menu, sizeof(main_menu)/sizeof(main_menu[0])); menu_render(); continue; } menu_key_handler(evt); menu_render(); } else{ // 没有按键事件,栈为空,刷新主界面时间显示 if (menu_top == -1) { main_window_render(); } } } }

和菜单的栈相关函数如下,主要需要考虑的就是出栈、入栈以及相关函数使用时存在的边界问题,发生越界会导致程序崩溃。

void menu_push(const menu_item_t *items, uint8_t count) { if (menu_top < MENU_STACK_MAX - 1) { menu_top++; menu_stack[menu_top].menu_items = items; menu_stack[menu_top].menu_index = 0; menu_stack[menu_top].menu_item_count = count; } } void menu_pop(void) { if (menu_top >= 0) { menu_top--; } } menu_page_t *menu_current(void)//当前菜单 { if (menu_top >= 0) { return &menu_stack[menu_top]; } return NULL; } void menu_render(void)//渲染选择菜单 { menu_page_t *p = menu_current(); if (p == NULL) return; // 栈为空,直接返回 OLED_Clear(); for (int i = 0; i < p->menu_item_count; i++) { char line[24]; sprintf(line, "%c %s", (i == p->menu_index) ? '>' : ' ', p->menu_items[i].next_menu_name); OLED_ShowString(0, i*24, (u8*)line, 16); } OLED_Refresh(); } void menu_key_handler(key_event_t key) { menu_page_t *p = menu_current(); if (p == NULL) return; switch (key) { case KEY_UP: if (p->menu_index > 0) {p->menu_index--;} break; case KEY_DOWN: if (p->menu_index < p->menu_item_count - 1) p->menu_index++; break; case KEY_OK: if (p->menu_items[p->menu_index].action) p->menu_items[p->menu_index].action(); if (p->menu_items[p->menu_index].next_items) menu_push(p->menu_items[p->menu_index].next_items, p->menu_items[p->menu_index].next_count); break; case KEY_BACK: menu_pop(); if (menu_top == -1) main_window_render(); break; case NO_PRESS: break; } }

实现效果如图所示。

总结:栈的结构确实很适合来做菜单的任务。

改进:只做了还没完全实现功能,可以通过加入WiFi来获取在线的准确时间以及天气。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/30 2:46:19

重磅|网宇集团荣获2026“金算奖”企业级AI智能调度平台创新奖

2026年9月&#xff0c;2026全球边缘AI算力大会暨“金算奖”颁奖晚宴在北京希尔顿逸林酒店举行。本次大会由边缘计算社区主办&#xff0c;汇聚鹏城实验室、百度、腾讯、阿里云等机构与企业的专家&#xff0c;以及来自AI、边缘计算和算力产业的298位企业代表。与会嘉宾围绕AI应用…

作者头像 李华
网站建设 2026/9/30 2:46:16

Asp.net Core 应用程序的多种运行模式

与传统的ASP.NET框架不同&#xff0c;ASP.NET Core彻底打破了对IIS的强绑定依赖&#xff0c;为开发者提供了灵活多样的运行模式选择。 不同的运行模式在资源占用、部署成本、运维复杂度、适用场景上存在显著差异&#xff0c;开发者需要根据项目的业务属性、部署环境、SLA要求等…

作者头像 李华
网站建设 2026/9/30 2:46:09

DeepEval + Langfuse 构建高自定义 RAG 评测体系

DeepEval Langfuse 构建高自定义 RAG 评测体系 核心定位与角色分工 DeepEval 是一款高度可定制的 LLM 应用评测框架&#xff0c;和 RAGAS 相比&#xff0c;核心优势是支持自定义业务规则、多维度扩展、灵活的评分 Prompt 配置、原生兼容安全合规类评测。搭配 Langfuse 全链路可…

作者头像 李华
网站建设 2026/9/30 2:46:07

用于机器人学习与控制的世界-动作模型:综述

26年9月来自阿联酋 MBZUAI 、加州理工、亚马逊公司FAR、维吉尼亚大学、乔治亚理工、纽约大学和加州伯克利分校的论文“World-Action Models for Robot Learning and Control: A Survey”。 这篇论文的核心观点是:机器人不仅需要知道“下一步做什么”,还需要预测“这样做会带…

作者头像 李华
网站建设 2026/9/30 2:45:32

ret2csu

上一篇的文章说的ret2libc的用法&#xff0c;在当时&#xff0c;我们没法控制rdx&#xff0c;所以用的是输出byebye延续下来的 rdx6&#xff0c;现在把byebye改成bye&#xff0c;这样延续下来的rdx就是3了&#xff0c;没法泄露全部的got表里的真实地址了&#xff0c;此时我们可…

作者头像 李华