LVGL简易计算器实战
文章目录
- 📁 文件结构建议
- 🔹 `eval.h` 表达式求值头文件
- 🔹 `eval.c` 表达式求值实现文件(带详细注释)
- 🔹 `ui.h` 界面头文件
- 🔹 `ui.c` 界面实现文件
- 🔹 `main.c` 主函数入口
- ✅ 总结
项目效果:
这个项目是一个基于 LVGL(Light and Versatile Graphics Library) 的简单计算器,实现了基本四则运算(含括号)的表达式求值,支持界面输入、清除和计算结果显示功能。
我将把项目分为以下几个文件进行组织,并添加详细注释,便于维护和扩展:
📁 文件结构建议
calculator_project/
├── main.c // 主函数入口
├── ui.c // LVGL 界面实现
├── ui.h // 界面头文件
├── eval.c // 表达式求值模块
├── eval.h // 求值函数声明
└── CMakeLists.txt / Makefile(视构建工具而定)
🔹 eval.h
表达式求值头文件
#ifndef EVAL_H
#define EVAL_H// 表达式求值主接口
double eval_expression(const char *expr);#endif // EVAL_H
🔹 eval.c
表达式求值实现文件(带详细注释)
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "eval.h"#define STACK_SIZE 64// 数字栈结构
typedef struct {double data[STACK_SIZE];int top;
} NumStack;// 运算符栈结构
typedef struct {char data[STACK_SIZE];int top;
} OpStack;// 数字栈操作
void num_push(NumStack *s, double val) { s->data[++s->top] = val; }
double num_pop(NumStack *s) { return s->data[s->top--]; }// 运算符栈操作
void op_push(OpStack *s, char op) { s->data[++s->top] = op; }
char op_pop(OpStack *s) { return s->data[s->top--]; }
char op_peek(OpStack *s) { return s->data[s->top]; }// 获取运算符优先级
int precedence(char op) {switch (op) {case '(': return 0;case '+':case '-': return 1;case '*':case '/': return 2;default: return -1;}
}// 执行单个操作符计算
void apply_operator(NumStack *nums, char op) {double b = num_pop(nums);double a = num_pop(nums);double res = 0;switch (op) {case '+': res = a + b; break;case '-': res = a - b; break;case '*': res = a * b; break;case '/': res = (b == 0) ? 0 : a / b; break; // 简单处理除零}num_push(nums, res);
}// 表达式求值主函数
double eval_expression(const char *expr) {NumStack nums = {.top = -1};OpStack ops = {.top = -1};char token[32];while (*expr) {if (isspace(*expr)) {expr++;} else if (isdigit(*expr) || *expr == '.') {int j = 0;while (isdigit(*expr) || *expr == '.') {token[j++] = *expr++;}token[j] = '\0';num_push(&nums, atof(token)); // 转为 double 后压入数字栈} else if (*expr == '(') {op_push(&ops, *expr++);} else if (*expr == ')') {while (ops.top != -1 && op_peek(&ops) != '(') {apply_operator(&nums, op_pop(&ops));}op_pop(&ops); // 弹出 '('expr++;} else if (strchr("+-*/", *expr)) {while (ops.top != -1 && precedence(op_peek(&ops)) >= precedence(*expr)) {apply_operator(&nums, op_pop(&ops));}op_push(&ops, *expr++);} else {expr++; // 非法字符跳过}}while (ops.top != -1) {apply_operator(&nums, op_pop(&ops));}return nums.data[0]; // 返回栈顶值为最终结果
}
🔹 ui.h
界面头文件
#ifndef UI_H
#define UI_Hvoid create_calculator_ui(void); // 创建 UI 主函数#endif // UI_H
🔹 ui.c
界面实现文件
#include "lvgl.h"
#include "ui.h"
#include "eval.h" // 调用表达式求值
#include <stdio.h>
#include <string.h>static lv_obj_t * ta; // 输入框全局指针// 按钮标签布局
static const char * btnm_map[] = {"7", "8", "9", "/", "\n","4", "5", "6", "*", "\n","1", "2", "3", "-", "\n","0", ".", "=", "+", "\n","(", ")", "C", ""
};// 按钮点击事件处理函数
static void btnm_event_cb(lv_event_t * e) {lv_event_code_t code = lv_event_get_code(e);lv_obj_t * obj = lv_event_get_target(e);const char * txt = lv_btnmatrix_get_btn_text(obj, lv_btnmatrix_get_selected_btn(obj));if (code == LV_EVENT_VALUE_CHANGED) {if (strcmp(txt, "=") == 0) {const char * expr = lv_textarea_get_text(ta);double result = eval_expression(expr); // 求值char buf[32];snprintf(buf, sizeof(buf), "%.4f", result); // 保留4位小数lv_textarea_set_text(ta, buf);} else if (strcmp(txt, "C") == 0) {lv_textarea_set_text(ta, ""); // 清空输入框} else {lv_textarea_add_text(ta, txt); // 添加字符}}
}// 创建计算器界面
void create_calculator_ui(void) {// 容器居中并设置垂直布局lv_obj_t * cont = lv_obj_create(lv_scr_act());lv_obj_set_size(cont, 240, 320);lv_obj_center(cont);lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_COLUMN);lv_obj_set_flex_align(cont, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);// 输入框ta = lv_textarea_create(cont);lv_obj_set_width(ta, 220);lv_textarea_set_one_line(ta, true);lv_textarea_set_placeholder_text(ta, "0");// 按钮矩阵lv_obj_t * btnm = lv_btnmatrix_create(cont);lv_btnmatrix_set_map(btnm, btnm_map);lv_obj_set_size(btnm, 220, 200);lv_obj_add_event_cb(btnm, btnm_event_cb, LV_EVENT_ALL, NULL);
}
🔹 main.c
主函数入口
#include "lvgl.h"
#include "ui.h"int main(void) {lv_init(); // 初始化 LVGL// 这里需要根据平台初始化显示、输入设备// lv_port_disp_init();// lv_port_indev_init();create_calculator_ui(); // 创建计算器界面while (1) {lv_timer_handler(); // LVGL 主循环usleep(5000); // 延时 5 ms(可根据平台调整)}return 0;
}
✅ 总结
这个项目完整实现了一个简洁的嵌入式图形界面计算器:
- 使用 栈结构 + 中缀表达式求值算法 解析和计算表达式;
- 使用 LVGL 图形库实现可点击界面与输入框;
- 代码 结构清晰、注释详细,适合嵌入式 UI 项目入门;
如果你还需要增加函数功能(比如 pow
, sqrt
等),可以在 eval.c
中扩展 apply_operator
并修改解析器。
计算器界面优化:
void create_calculator_ui(void) {// 设置屏幕背景为浅灰色lv_obj_set_style_bg_color(lv_scr_act(), lv_palette_lighten(LV_PALETTE_GREY, 4), 0);// 创建主容器lv_obj_t * cont = lv_obj_create(lv_scr_act());lv_obj_set_size(cont, 260, 340);lv_obj_center(cont);lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_COLUMN);lv_obj_set_flex_align(cont, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);lv_obj_set_style_pad_all(cont, 10, 0);lv_obj_set_style_radius(cont, 10, 0);lv_obj_set_style_bg_color(cont, lv_color_white(), 0);lv_obj_set_style_bg_opa(cont, LV_OPA_COVER, 0);lv_obj_set_style_shadow_width(cont, 8, 0);// 输入框ta = lv_textarea_create(cont);lv_obj_set_width(ta, lv_pct(100));lv_obj_set_height(ta, 50);lv_textarea_set_max_length(ta, 128);lv_textarea_set_one_line(ta, false);lv_textarea_set_placeholder_text(ta, "0");lv_textarea_set_align(ta, LV_TEXT_ALIGN_RIGHT);lv_textarea_set_text(ta, "");lv_obj_set_style_text_font(ta, &lv_font_montserrat_20, 0);lv_obj_set_style_radius(ta, 8, 0);lv_obj_set_style_bg_color(ta, lv_palette_lighten(LV_PALETTE_BLUE, 4), 0);lv_obj_set_style_text_color(ta, lv_color_white(), 0);// 按钮矩阵lv_obj_t * btnm = lv_btnmatrix_create(cont);lv_btnmatrix_set_map(btnm, btnm_map);lv_obj_set_size(btnm, lv_pct(100), 220);lv_obj_add_event_cb(btnm, btnm_event_cb, LV_EVENT_ALL, NULL);// 设置按钮样式static lv_style_t style_btn;lv_style_init(&style_btn);lv_style_set_radius(&style_btn, 4);lv_style_set_bg_opa(&style_btn, LV_OPA_COVER);lv_style_set_bg_color(&style_btn, lv_palette_lighten(LV_PALETTE_GREY, 2));lv_style_set_text_font(&style_btn, &lv_font_montserrat_18);lv_obj_add_style(btnm, &style_btn, LV_PART_ITEMS);// 获取 mapconst char ** map = lv_btnmatrix_get_map(btnm);uint16_t btn_cnt = 0;for (int i = 0; map[i] != NULL; i++) {btn_cnt++;}// 遍历按钮设置颜色for (uint16_t i = 0; i < btn_cnt; i++) {const char * label = map[i];if (strcmp(label, "=") == 0) {lv_btnmatrix_set_btn_ctrl(btnm, i, LV_BTNMATRIX_CTRL_CHECKABLE);lv_obj_set_style_bg_color(btnm, lv_palette_main(LV_PALETTE_GREEN), LV_PART_ITEMS | LV_STATE_CHECKED);} else if (strcmp(label, "C") == 0) {lv_btnmatrix_set_btn_ctrl(btnm, i, LV_BTNMATRIX_CTRL_CHECKABLE);lv_obj_set_style_bg_color(btnm, lv_palette_main(LV_PALETTE_RED), LV_PART_ITEMS | LV_STATE_CHECKED);}}
}
运行效果:
相关文章:

LVGL简易计算器实战
文章目录 📁 文件结构建议🔹 eval.h 表达式求值头文件🔹 eval.c 表达式求值实现文件(带详细注释)🔹 ui.h 界面头文件🔹 ui.c 界面实现文件🔹 main.c 主函数入口✅ 总结 项目效果&…...
【FMMT】基于模糊多模态变压器模型的个性化情感分析
遇到很难的文献看不懂,不应该感到气馁,应该激动,因为外审估计也看不太懂,那么学明白了可以吓唬他 缺陷一:输入依赖性与上下文建模不足 缺陷描述: 传统自注意力机制缺乏因果关系,难以捕捉序列历史背景多模态数据间的复杂依赖关系未被充分建模CNN/RNN类模型在…...
聊一聊接口测试依赖第三方服务变更时如何处理?
目录 一、依赖隔离与模拟 二、契约测试 三、版本控制与兼容性 四、变更监控与告警 五、容错设计 六、自动化测试维护 七、协作机制与文档自动化 第三方API突然改了参数或者返回结构,导致我们的测试用例失败,这时候该怎么办呢?首先想到…...

代码随想录算法训练营第60期第三十四天打卡
大家好,我们今天的内容依旧是贪心算法,我们上次的题目主要是围绕多维问题,那种时候我们需要分开讨论,不要一起并发进行很容易顾此失彼,那么我们今天的问题主要是重叠区间问题,又是一种全新的贪心算法思想&a…...
Midscene.js Chrome 插件实战:基于 AI 驱动 WEB UI 自动化测试「喂饭教程」
Midscene.js Chrome 插件实战:基于 AI 驱动 WEB UI 自动化测试「喂饭教程」 前言一、Midscene.js 简介二、环境准备与插件安装1. 安装 Chrome 插件2. 配置模型与 API Key三、插件界面与功能总览四、实战演练:用自然语言驱动网页自动化1. 典型场景一(Action):账号登录步骤一…...
JVM——方法内联之去虚化
引入 在Java虚拟机的即时编译体系中,方法内联是提升性能的核心手段,但面对虚方法调用(invokevirtual/invokeinterface)时,即时编译器无法直接内联,必须先进行去虚化(Devirtualizationÿ…...
Objective-C Block 底层原理深度解析
Objective-C Block 底层原理深度解析 1. Block 是什么? 1.1 Block 的本质 Block 是 Objective-C 中的特殊对象,实现了匿名函数的功能 通过 isa 指针继承自 NSObject,可以响应(如 copy、retain、release)等内存管理方…...

关于IDE的相关知识之二【插件推荐】
成长路上不孤单😊😊😊😊😊😊 【14后😊///计算机爱好者😊///持续分享所学😊///如有需要欢迎收藏转发///😊】 今日分享关于ide插件推荐的相关内容!…...
Python+Streamlit实现登录页
PythonStreamlit实现登录页 Streamlit 是一个开源的 Python 库,专为数据科学家和机器学习工程师设计,用于快速构建交互式 Web 应用。 其核心功能与特点包括: 1.快速原型开发 2.交互式数据展示 3.极简开发 4.实时更新 5.内置组件 6.无前端依赖…...
RDD案例数据清洗
在 Spark 中,RDD(Resilient Distributed Dataset)是分布式数据集的基本抽象。数据清洗是数据预处理中的一个重要步骤,通常包括去除重复数据、过滤无效数据、转换数据格式等操作。以下是一个使用 RDD 进行数据清洗的完整示例。 示…...
按键精灵ios脚本新增元素功能助力辅助工具开发(三)
元素节点功能(iOSElement) 在按键精灵 iOS 新版 APP v2.2.0 中,新增了元素节点功能 iOSElement,该功能包含共 15 个函数。这一功能的出现,为开发者在处理 iOS 应用界面元素时提供了更为精准和高效的方式。通过这些函…...

Axure RP9:列表新增
文章目录 列表新增思路新增按钮操作说明保存新增交互设置列表新增 思路 利用中继器新增行实现列表新增功能 新增按钮操作说明 工具栏中添加新增图标及标签,在图标标签基础上添加热区;对热区添加鼠标单击时交互事件,同步插入如下动作:显示/隐藏动作,设置目标元件为新增窗…...

06 mysql之DML
一、什么是DML DML 用于操作数据库中的数据。主要命令包括: INSERT:添加数据SELECT:查询数据UPDATE:修改数据DELETE:删除数据 二、插入数据(INSERT) 2.1 插入单条记录 -- 插入学生记录&…...
游戏引擎学习第277天:稀疏实体系统
回顾并为今天定下基调 上次我们结束的时候,基本上已经控制住了跳跃的部分,达到了我想要的效果,现在我们主要是在等待一些新的艺术资源。因此,等新艺术资源到位后,我们可能会重新处理跳跃的部分,因为现在的…...

【最新版】likeshop连锁点餐系统-PHP版+uniapp前端全开源
一.系统介绍 likeshop外卖点餐系统适用于茶饮类的外卖点餐场景,搭建自己的一点点、奈雪、喜茶点餐系统。 系统基于总部多门店的连锁模式,拥有门店独立管理后台,支持总部定价和门店定价LBS定位点餐,可堂食可外卖。无论运营还是二开…...
机器学习之决策树模型:从基础概念到条件类型详解
机器学习之决策树模型:从基础概念到条件类型详解 摘要:本文深入探讨决策树模型的概念、构成以及不同条件类型。首先介绍决策树的基本结构和工作原理,随后详细阐述轴心对齐条件与倾斜条件、二元条件与非二元条件的差异及应用场景,…...
网络编程(一)网络编程入门
本节课学习TCP客户端和服务器端编程架构,其分为分为C/S(客户端/服务器模式)和B/S(浏览器/服务器架构模式)两种模式。接下来我们分别了解这两种模式 C/S模式 C/S模式:服务器首先先启动,并根据客…...
黑名单中的随机数-leetcode710
题目描述 给定一个整数 n 和一个 无重复 黑名单整数数组 blacklist 。设计一种算法,从 [0, n - 1] 范围内的任意整数中选取一个 未加入 黑名单 blacklist 的整数。任何在上述范围内且不在黑名单 blacklist 中的整数都应该有 同等的可能性 被返回。 优化你的算法&am…...

纯Java实现反向传播算法:零依赖神经网络实战
在深度学习框架泛滥的今天,理解算法底层实现变得愈发重要。反向传播(Backpropagation)作为神经网络训练的基石算法,其实现往往被各种框架封装。本文将突破常规,仅用Java标准库实现完整BP算法,帮助开发者: 1) 深入理解BP数学原理。2) 掌握面向对象的神经网络实现。3) 构建可…...

海纳思(Hi3798MV300)机顶盒遇到海思摄像头
海纳思机顶盒遇到海思摄像头,正好家里有个海思Hi3516的摄像头模组开发板,结合机顶盒来做个录像。 准备工作 海纳斯机顶盒摄像机模组两根网线、两个电源、路由器一块64G固态硬盘 摄像机模组和机顶盒都接入路由器的LAN口,确保网络正常通信。 …...
MCP项目实例 - client sever交互
1. 项目概述 项目目标 构建一个本地智能舆论分析系统。 利用自然语言处理和多工具协作,实现用户查询意图的自动理解。 进行新闻检索、情绪分析、结构化输出和邮件推送。 系统流程 用户查询:用户输入查询请求。 提取关键词:从用户查询中…...

Axure应用交互设计:表格跟随菜单移动效果(超长表单)
亲爱的小伙伴,在您浏览之前,烦请关注一下,在此深表感谢!本文如有帮助请订阅 Axure产品经理精品视频课已登录CSDN可点击学习https://edu.csdn.net/course/detail/40420 课程主题:表格跟随菜单移动 主要内容:表格交互设计、动态面板嵌套、拖动时事件、移动动作 应用场景…...

7系列 之 I/O标准和终端技术
背景 《ug471_7Series_SelectIO.pdf》介绍了Xilinx 7 系列 SelectIO 的输入/输出特性及逻辑资源的相关内容。 第 1 章《SelectIO Resources》介绍了输出驱动器和输入接收器的电气特性,并通过大量实例解析了各类标准接口的实现。 第 2 章《SelectIO Logic Resource…...

github 上的 CI/CD 的尝试
效果 步骤 新建仓库设置仓库的 page 新建一个 vite 的项目,改一下 vite.config.js 中的 base 工作流 在项目的根目录下新建一个 .github/workflows/ci.yml 文件,然后编辑一下内容 name: Build & Deploy Vue 3 Appon:push:branches: [main]permi…...
Scala和Go差异
Scala和Go(又称Golang)是两种现代编程语言,各自具有独特的特性和设计哲学。 尽管它们都可以用于构建高性能、可扩展的应用程序,但在许多方面存在显著差异。 Scala和Go的详细比较,涵盖它们的异同点: 1. 语…...

yup 使用 3 - 利用 meta 实现表单字段与表格列的统一结构配置(适配 React Table)
yup 使用 3 - 利用 meta 实现表单字段与表格列的统一结构配置(适配 React Table) Categories: Tools Last edited time: May 11, 2025 7:45 PM Status: Done Tags: form validation, schema design, yup 本文介绍如何通过 Yup 的 meta() 字段࿰…...
类初始化方法
一、类初始化方法 成员初始化列表 class Point {int x, y; public:Point(int a, int b) : x(a), y(b) {} };就地初始化(C11) 声明时初始化。 class Widget {int size 10; // 类内成员初始化vector<int> data{1,2,3}; };特殊情况:静…...

【OpenCV】imread函数的简单分析
目录 1.imread()1.1 imread()1.2 imread_()1.2.1 查找解码器(findDecoder)1.2.2 读取数据头(JpegDecoder-->readHeader)1.2.2.1 初始化错误信息(jpeg_std_error)1.2.2.2 创建jpeg解压缩对象(…...

【Linux实践系列】:进程间通信:万字详解共享内存实现通信
🔥 本文专栏:Linux Linux实践项目 🌸作者主页:努力努力再努力wz 💪 今日博客励志语录: 人生就像一场马拉松,重要的不是起点,而是坚持到终点的勇气 ★★★ 本文前置知识: …...

【笔记】BCEWithLogitsLoss
工作原理 BCEWithLogitsLoss 是 PyTorch 中的一个损失函数,用于二分类问题。 它结合了 Sigmoid 激活函数和二元交叉熵(Binary Cross Entropy, BCE)损失在一个类中。 这不仅简化了代码,而且通过数值稳定性优化提高了模型训练的效…...