嵌入式GUI开发:基于GUILite的万年历实现

张开发
2026/4/7 0:45:09 15 分钟阅读

分享文章

嵌入式GUI开发:基于GUILite的万年历实现
1. 项目概述最近在嵌入式开发社区看到不少关于GUILite这个轻量级GUI库的讨论作为一个常年和单片机打交道的工程师我也忍不住想试试水。这个开源GUI库最大的特点就是极其轻量核心代码只有几千行却能在资源有限的MCU上实现流畅的图形界面效果。正好手头有个小熊派开发板就决定用它来实现一个简易的万年历应用顺便深入了解一下这个库的使用方法。提示GUILite特别适合那些只有SPI接口小屏幕的开发板比如常见的0.96寸OLED或者2.4寸TFT屏它能在这些资源受限的设备上跑出不错的UI效果。2. 环境准备与移植2.1 硬件准备我使用的是小熊派开发板主控是STM32L431RCT6搭配一块1.3寸的SPI接口LCD屏幕。这套配置对于运行GUILite来说绰绰有余实际上GUILite甚至能在更简单的硬件上运行比如STM32F103这样的入门级MCU。2.2 软件准备首先需要从GUILite的Gitee仓库下载源码git clone https://gitee.com/idea4good/GuiLite.git这个库的核心文件并不多主要关注以下几个GuiLite.h核心头文件GuiLite.cpp核心实现Adapter目录包含各种平台的适配层代码2.3 移植适配移植GUILite主要需要实现两个关键接口像素绘制函数void gfx_draw_pixel(int x, int y, unsigned int color) { // 这里实现具体的LCD像素绘制逻辑 LCD_DrawPoint(x, y, color); }毫秒级延时函数void gfx_delay_ms(int ms) { HAL_Delay(ms); // 使用HAL库的延时函数 }注意在Keil工程设置中需要取消勾选Use MicroLIB选项因为GUILite依赖标准C库的一些功能。3. 万年历实现细节3.1 时间获取方案在实现万年历时我考虑了两种时间获取方案使用硬件RTC这是最准确的方式可以保持时间持续运行使用软件计时依赖系统tick精度较低但不需要额外硬件最终选择了硬件RTC方案因为STM32L4系列内置了精度不错的RTC模块。初始化代码如下void RTC_Init(void) { RTC_TimeTypeDef sTime {0}; RTC_DateTypeDef sDate {0}; // 配置RTC时钟源 __HAL_RCC_RTC_ENABLE(); // 设置初始时间2023年1月1日 00:00:00 sTime.Hours 0; sTime.Minutes 0; sTime.Seconds 0; HAL_RTC_SetTime(hrtc, sTime, RTC_FORMAT_BIN); sDate.WeekDay RTC_WEEKDAY_SUNDAY; sDate.Month RTC_MONTH_JANUARY; sDate.Date 1; sDate.Year 23; // 2023年 HAL_RTC_SetDate(hrtc, sDate, RTC_FORMAT_BIN); }3.2 字体处理GUILite使用位图字体需要通过官方提供的GuiLiteToolkit工具生成字体数据。这个工具使用非常简单输入需要显示的字符支持中文选择字体和大小生成对应的C代码生成的字体数据可以直接包含到工程中。例如显示嵌入式大杂烩这几个中文字符const FONT_INFO chinese_font_16 { // 字体数据... }; // 显示中文字符串 draw_string(surface, 1, \xe5\xb5\x8c\xe5\x85\xa5\xe5\xbc\x8f\xe5\xa4\xa7\xe6\x9d\x82\xe7\x83\xa9, 50, 50, chinese_font_16, GL_RGB(255,255,255), GL_RGB(0,0,0));技巧中文字符需要转换为UTF-8编码格式可以使用在线工具转换后再以十六进制形式嵌入代码。3.3 界面布局万年历的界面主要分为三个部分顶部显示年份和月份中间显示日期表格底部显示当前时间使用GUILite的布局功能可以很方便地实现// 创建主窗口 c_display* display get_display(); c_surface* surface display-alloc_surface(); surface-set_active(true); // 绘制标题 draw_string(surface, 1, 2023年 1月, 50, 10, font_16, GL_RGB(255,255,255), GL_RGB(0,0,0)); // 绘制星期栏 const char* weekdays[] {日,一,二,三,四,五,六}; for(int i0; i7; i) { draw_string(surface, 1, weekdays[i], 20i*30, 40, font_16, GL_RGB(255,255,0), GL_RGB(0,0,0)); } // 绘制日期格子 for(int day1; day31; day) { int x 20 ((day-1)%7)*30; int y 70 ((day-1)/7)*30; char day_str[3]; sprintf(day_str, %d, day); draw_string(surface, 1, day_str, x, y, font_16, GL_RGB(255,255,255), GL_RGB(0,0,128)); }4. 常见问题与优化4.1 中文显示乱码最常见的问题是中文显示为乱码这通常是因为字体数据中没有包含对应的中文字符字符串编码格式不正确解决方案确保在生成字体时包含了所有需要显示的中文字符使用UTF-8编码格式的字符串4.2 刷新闪烁问题在更新界面时可能会出现闪烁现象这是因为直接在全屏上进行了重绘。优化方法是使用双缓冲技术只重绘发生变化的部分GUILite本身支持局部刷新可以通过设置脏矩形区域来优化// 只刷新日期变化的区域 surface-set_dirty_rect(20, 70, 200, 150);4.3 性能优化对于低端MCU可以采取以下优化措施减少界面元素数量使用更小的字体降低刷新频率使用更简单的颜色模式如从RGB565改为RGB3325. 功能扩展思路这个基础万年历还可以进一步扩展添加农历显示需要实现农历算法增加闹钟功能利用RTC的闹钟中断美化界面添加动画效果和更精美的图标触摸支持增加触摸交互功能GUILite本身支持触摸事件处理可以通过重载on_touch函数来实现virtual void on_touch(int x, int y, TOUCH_ACTION action) { if(action TOUCH_DOWN) { // 处理触摸按下事件 } }在实际项目中我发现GUILite虽然轻量但功能足够强大能够满足大多数嵌入式GUI需求。它的跨平台特性也很实用同一套界面代码可以运行在Windows、Linux和嵌入式系统上。对于资源受限的MCU项目来说这是一个非常值得考虑的GUI解决方案。

更多文章