这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 活动中心 » 板卡试用 » M5Paperv1.1-过程贴Calculator-电子墨水屏上的复古计算器

共1条 1/1 1 跳转至

M5Paperv1.1-过程贴Calculator-电子墨水屏上的复古计算器

工程师
2026-05-14 10:39:27     打赏

拿到 M5Paper 这块 4.7 寸电子墨水屏开发板后,除了用它做信息展示类的应用,我一直在想——能不能在上面做一些有实际交互价值的工具?

正好平时写代码时需要偶尔算个表达式,打开手机计算器总觉得太慢。于是就有了这个念头:在 M5Paper 上做一个计算器。墨水屏的特性决定了它不能像普通 LCD 那样高刷,但作为计算器场景,每次按键后刷新一次,完美契合。

界面设计

计算器的界面设计参考了经典 Casio 计算器的布局。M5Paper 的屏幕是 540x960 竖屏,我把整个屏幕分成两个区域:

  • 顶部显示区:显示输入表达式和计算结果

  • 底部按键区:数字、运算符和功能按键

按键布局采用 4 列网格:

+-----+-----+-----+-----+
|  C  |  ←  |  %  |  ÷  |
+-----+-----+-----+-----+
|  7  |  8  |  9  |  ×  |
+-----+-----+-----+-----+
|  4  |  5  |  6  |  -  |
+-----+-----+-----+-----+
|  1  |  2  |  3  |  +  |
+-----+-----+-----+-----+
|  ±  |  0  |  .  |  =  |
+-----+-----+-----+-----+

每个按键采用圆角矩形设计,墨水屏的 16 级灰度虽然没有色彩冲击力,但浅灰底色 + 深灰按键的搭配,阅读体验反而比彩色 LCD 更舒服——有种打印出来的纸质感。

E-Ink 刷新策略

墨水屏开发最重要的一点:不能频繁刷新

计算器这种高频交互场景,我设计了一套"按需刷新"策略:

按键触发 → 更新表达式 → 局部刷新显示区 → 全屏刷新(每5次)

具体来说:

  1. 按数字键时,只刷新顶部的表达式显示区域

  2. 按 "=" 键计算结果时,全屏刷新确保结果清晰

  3. 每 5 次操作后全屏刷新一次,防止残影积累

# 局部刷新表达式区域
def refresh_expression(expr):
    # 只清除和重绘表达式区域
    M5.Lcd.fillRect(10, 10, 520, 120, COLOR_BG)
    M5.Lcd.setCursor(20, 40)
    M5.Lcd.setTextColor(COLOR_BLACK, COLOR_BG)
    M5.Lcd.setFont(M5.Lcd.FONTS.DejaVu40)
    M5.Lcd.print(expr)

日本字体风格

为了让计算器的数字显示更有质感,我专门找了一款开源的日本字体——这算是开发过程中比较有意思的点。

墨水屏因为低分辨率(相对 LCD),字体选择直接影响观感。我测试了几种字体后发现:

  • DejaVu 系列:数字显示最清晰,适合主数值显示

  • 日文字体 VLW 格式:中文字符显示效果好,适合按键标签

字体文件需要转换为 VLW 格式才能被 M5Paper 的 UIFlow2 固件识别。我用了一个在线字体转换工具,把 TTF 字体转成了 VLW 格式,存储在 SD 卡上。

触摸交互实现

M5Paper 的 GT911 触摸芯片支持两点触控,但计算器场景用单指就够了。

触摸检测的逻辑比较简单:

def get_touch_position():
    if M5.Touch.getCount() > 0:
        x = M5.Touch.getX()
        y = M5.Touch.getY()
        if x > 0 and y > 0:
            return (x, y)
    return None

def find_button(x, y):
    # 遍历按键布局,检测触摸区域
    for i, btn in enumerate(buttons):
        bx, by, bw, bh = btn['rect']
        if bx <= x <= bx + bw and by <= y <= by + bh:
            return btn
    return None

核心计算逻辑

计算器的核心是一个简单的表达式解析器。我不打算用 eval(安全性问题),而是自己实现了一个基于操作符优先级的中缀表达式计算器:

def calculate(expression):
    """计算表达式值,支持加减乘除和括号"""
    try:
        # 将表达式转为 RPN(逆波兰表示法)
        output = []
        ops = []
        precedence = {'+': 1, '-': 1, '×': 2, '÷': 2, '%': 2}
        
        for token in tokenize(expression):
            if token.is_number:
                output.append(token)
            elif token in precedence:
                while ops and ops[-1] != '(' and precedence[ops[-1]] >= precedence[token]:
                    output.append(ops.pop())
                ops.append(token)
            elif token == '(':
                ops.append(token)
            elif token == ')':
                while ops and ops[-1] != '(':
                    output.append(ops.pop())
                ops.pop()
        
        while ops:
            output.append(ops.pop())
        
        # 计算 RPN 表达式
        stack = []
        for token in output:
            if token.is_number:
                stack.append(token.value)
            else:
                b = stack.pop()
                a = stack.pop()
                if token == '+': stack.append(a + b)
                elif token == '-': stack.append(a - b)
                elif token == '×': stack.append(a * b)
                elif token == '÷': stack.append(a / b)
                elif token == '%': stack.append(a % b)
        
        return stack[0]
    except Exception as e:
        return "Error"

这个实现支持:

  • 四则运算(+ - × ÷)

  • 取余(%)

  • 正负号切换(±)

  • 括号优先级

  • 错误处理(除零、语法错误等)

踩坑记录

1. 触摸坐标偏移

最开始发现触摸按键不太准,总是点偏几毫米。排查后发现是屏幕旋转导致触摸坐标没跟着旋转。

解决方法:手动将触摸坐标映射到正确的屏幕坐标系。

2. 残影问题

连续按数字键后,墨水屏上的数字会出现残影,旧的数字隐约可见。

解决方法

  • 每 5 次操作做一次全屏刷新

  • 按键时先清除旧内容再写入新内容

  • 使用 EPD_FAST 模式减少刷新时间

3. 字体加载失败

刚加载日文字体时,程序直接崩溃。排查发现是微雪提供的 VLW 字体文件格式和 M5Paper 的 UIFlow2 固件不完全兼容。

解决方法:使用 UIFlow2 自带的字体转换工具重新生成 VLW 文件。

完整代码框架

import M5
from M5 import *
import time

# 颜色定义
COLOR_BG = 0xeeeeee
COLOR_BLACK = 0x000000
COLOR_GRAY = 0x999999
COLOR_DARK = 0x555555

# 屏幕尺寸
SCREEN_W = 540
SCREEN_H = 960

# 按键布局
BUTTONS = { ... }  # 4x5 行列布局

class Calculator:
    """M5Paper 计算器主类"""
    
    def __init__(self):
        self.expression = ""
        self.result = ""
        self.last_full_refresh = 0
        self.op_count = 0
    
    def draw_keypad(self):
        """绘制按键面板"""
        for row in BUTTONS:
            for btn in row:
                self.draw_button(btn)
    
    def draw_button(self, btn):
        """绘制单个按键"""
        x, y, w, h, label = btn
        M5.Lcd.fillRoundRect(x, y, w, h, 8, COLOR_DARK)
        text_x = x + (w - len(label) * 20) // 2
        M5.Lcd.setCursor(text_x, y + 20)
        M5.Lcd.setTextColor(COLOR_BG, COLOR_DARK)
        M5.Lcd.setFont(M5.Lcd.FONTS.EFontCN24)
        M5.Lcd.print(label)
    
    def handle_touch(self, x, y):
        """处理触摸事件"""
        for row in BUTTONS:
            for bx, by, bw, bh, label in row:
                if bx <= x <= bx + bw and by <= y <= by + bh:
                    self.on_key_press(label)
                    return
    
    def on_key_press(self, key):
        """按键逻辑处理"""
        if key == "=":
            self.result = str(calculate(self.expression))
            self.full_refresh()
        elif key == "C":
            self.expression = ""
            self.result = ""
            self.full_refresh()
        elif key == "←":
            self.expression = self.expression[:-1]
        else:
            self.expression += key
        
        self.op_count += 1
        if self.op_count % 5 == 0:
            self.full_refresh()
        else:
            self.partial_refresh()

def setup():
    """初始化"""
    M5.begin()
    calc = Calculator()
    calc.draw_keypad()

def loop():
    """主循环"""
    M5.update()
    touch = M5.Touch
    if touch.getCount() > 0:
        x, y = touch.getX(), touch.getY()
        calc.handle_touch(x, y)
    time.sleep(0.05)

总结

在 M5Paper 上做一个计算器,技术难度不大,但墨水屏适配是个好课题。通过这个项目,我深入理解了以下几点:

  1. 墨水屏的刷新策略:按需刷新 + 定期全刷,是墨水屏应用的核心设计模式

  2. 触摸交互优化:触摸坐标校正和防抖处理是用户体验的关键

  3. 字体选择:中英文混排场景,日本字体是个不错的中间选择

  4. Canvas 渲染:E-Ink 屏幕必须使用 Canvas 缓冲,直接绘制会出问题

这个计算器已经能满足日常使用,后续还会继续完善。如果你对 M5Paper 开发感兴趣,欢迎留言交流!

计算器演示.jpg


共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]