aaaaaaaaaaa

u8g2 库移植与使用说明(STM32G431 + ST7735S)

本文档面向本工程 Moisture_Meter_g431_lcd,完整说明 u8g2 图形库是如何移植到 STM32G431 + ST7735S 彩色 TFT 上 、如何使用 、以及本工程实际用到的每一个 u8g2 函数的作用。


目录

  1. 背景与总体思路
  2. [u8g2 库的分层架构](#u8g2 库的分层架构)
  3. 工程目录结构与编译范围
  4. 移植原理与数据流
  5. 初始化流程
  6. 使用流程(两种模式)
  7. 移植层自定义接口
  8. [u8g2 标准 API 函数详解](#u8g2 标准 API 函数详解)
  9. [u8x8 底层函数详解](#u8x8 底层函数详解)
  10. 字体系统
  11. 关键数据结构
  12. 常用常量与宏
  13. 常见问题与注意事项

1. 背景与总体思路

1.1 u8g2 是什么

u8g2 是一个开源的嵌入式单色(1bpp)图形库,广泛用于 OLED / LCD 点阵屏。它提供了统一的绘图 API(画点、线、矩形、圆、圆弧、字符串、位图等)以及大量字体,底层通过一个"显示驱动回调"适配各种具体屏幕。

1.2 本工程的特殊之处

u8g2 只支持单色屏 ,而本工程的 ST7735S 是 RGB565 彩色屏 。因此移植时没有 使用 u8g2 自带的任何显示驱动(u8g2_d_setup.c / u8g2_d_memory.c 里的驱动都没编译),而是:

  1. 让 u8g2 画在内部一块 1bpp 缓冲区 (128×160/8 = 2560 字节);
  2. 自己编写一个 u8x8 显示驱动回调 u8x8_d_st7735_128x160();
  3. 在回调里把 1bpp 像素展开成 RGB565(只用前景色 / 背景色两种颜色);
  4. 复用工程已有的 TFT_LCD 驱动(LCD_SetWindow() + SPI2)把数据刷到屏上。

所以 u8g2 画出来的画面只包含前景色、背景色两种颜色 (默认白字黑底),可通过 U8G2_Port_SetColors() 改成任意 RGB565 颜色。


2. u8g2 库的分层架构

u8g2 是典型的三层结构,本工程只用到其中与"画图"和"刷屏"相关的部分:

复制代码
┌─────────────────────────────────────────────────────┐
│  u8g2 层(高层绘图 API)                              │
│  u8g2_ClearBuffer / DrawStr / DrawLine / DrawCircle... │
│  负责把图形画进 1bpp 缓冲区                            │
└──────────────────────────┬──────────────────────────┘
                           │ 内部持有
┌──────────────────────────▼──────────────────────────┐
│  u8x8 层(低层 tile/文字/协议)                       │
│  u8x8_Setup / u8x8_InitDisplay / 消息分发(msg)       │
│  负责把 8x8 tile 数据交给"显示驱动回调"                │
└──────────────────────────┬──────────────────────────┘
                           │ 回调函数指针
┌──────────────────────────▼──────────────────────────┐
│  显示驱动回调(本工程自写)                            │
│  u8x8_d_st7735_128x160()                            │
│  负责把 tile 展开成 RGB565 并通过 SPI 写屏             │
└─────────────────────────────────────────────────────┘

关键点:u8g2_t 和 u8x8_t 实际上是同一块内存 ------u8g2_t 是 u8x8_t 的超集。宏定义:

c 复制代码
#define u8g2_GetU8x8(u8g2) ((u8x8_t *)(u8g2))

即 &g_u8g2 既可以被当作 u8g2_t * 使用(画图),也可以被当作 u8x8_t * 使用(底层控制)。


3. 工程目录结构与编译范围

u8g2 原始仓库 u8g2-master 非常庞大(含所有平台适配、所有字体、工具等),本工程只挑了一小部分放进 Keil 工程。

复制代码
BSP/Transplant/U8G2/
├── u8g2_port.c                 ★ 本工程自写的移植层
├── u8g2_port.h                 ★ 移植层头文件
├── u8g2-master/                u8g2 官方仓库完整克隆(仅作参考,大部分不参与编译)
│   └── csrc/
│       ├── u8g2.h              ★ 主头文件(所有 API 声明)
│       ├── u8x8.h              ★ 底层头文件
│       ├── u8g2_*.c            ★ 按需编译的核心绘图/缓冲源码
│       └── u8x8_*.c            ★ 按需编译的底层源码
└── fonts/                      ★ 按需拷贝出来的 4 个 ASCII 字体 + 中文字模生成脚本
    ├── u8g2_font_6x10_tr.c
    ├── u8g2_font_8x13_tr.c
    ├── u8g2_font_10x20_tr.c
    ├── u8g2_font_helvB12_tr.c
    └── gen_cn_glyphs.py        (生成中文点阵的工具,不参与编译)

3.1 实际参与编译的 u8g2 源码(对照 .o 文件)

源文件 提供的能力
u8g2_buffer.c u8g2_ClearBuffer / u8g2_SendBuffer / FirstPage / NextPage
u8g2_cleardisplay.c u8g2_ClearDisplay
u8g2_setup.c u8g2_SetupBuffer / 初始化流程
u8g2_font.c u8g2_SetFont / u8g2_DrawStr / 字体度量
u8g2_ll_hvline.c 低层水平/垂直刷像素 u8g2_ll_hvline_vertical_top_lsb
u8g2_hvline.c u8g2_DrawHLine / u8g2_DrawVLine / u8g2_DrawHVLine / u8g2_DrawPixel
u8g2_box.c u8g2_DrawBox / u8g2_DrawFrame / 圆角矩形
u8g2_line.c u8g2_DrawLine
u8g2_circle.c u8g2_DrawCircle / DrawDisc / 椭圆
u8g2_arc.c u8g2_DrawArc
u8g2_bitmap.c u8g2_DrawBitmap / u8g2_DrawXBM / u8g2_DrawXBMP
u8g2_polygon.c u8g2_DrawTriangle / u8g2_ClearPolygonXY / u8g2_DrawPolygon
u8g2_intersection.c u8g2_IsIntersection / u8g2_GetIntersection
u8g2_kerning.c 字体紧凑排布相关(SetFontRefHeight*)

3.2 实际参与编译的 u8x8 源码

源文件 提供的能力
u8x8_setup.c u8x8_Setup / u8x8_InitDisplay / u8x8_InitInterface
u8x8_display.c u8x8_d_helper_display_setup_memory / u8x8_dummy_cb / 显示信息
u8x8_byte.c 字节级通信接口(本工程空实现)
u8x8_cad.c 命令/参数/数据(CAD)协议(本工程空实现)
u8x8_gpio.c GPIO/延时回调(本工程空实现)
u8x8_8x8.c 内置 8x8 字体字形
u8x8_string.c 低层字符串绘制
u8x8_u8toa.c / u8x8_u16toa.c 数字转字符串
u8x8_capture.c 帧捕获(调试用)

3.3 明确未编译的部分(重要)

  • u8g2_d_setup.c / u8g2_d_memory.c:u8g2 自带的全部屏幕驱动,本工程不用。
  • u8g2_fonts.c(37MB 大字体总表):不用,只按需引入 4 个字体。
  • u8g2_selection_list.c / u8g2_input_value.c / u8g2_message.c / u8g2_button.c / mui_u8g2.c:菜单/输入控件,本工程不用。

4. 移植原理与数据流

核心代码位于 u8g2_port.c,数据流如下:

复制代码
u8g2 画图 API(DrawStr/DrawLine/...)
        ↓ 写入
1bpp 全缓冲 s_u8g2_buf[2560](vertical_top_lsb 布局)
        ↓ u8g2_SendBuffer() 遍历每个 8x8 tile
自定义显示驱动 u8x8_d_st7735_128x160()
        ↓ 收到 U8X8_MSG_DISPLAY_DRAW_TILE
st7735_draw_tile_block()
        ↓ 每个像素按 前景色/背景色 展开成 RGB565
LCD_SetWindow() + HAL_SPI_Transmit(LCD_SPI, ...) 刷屏

4.1 缓冲区布局(vertical_top_lsb)

  • 缓冲区总大小 U8G2_PORT_TILE_W * U8G2_PORT_TILE_H * 8 = 16*20*8 = 2560 字节。
  • 每个 tile 为 8×8 像素,占 8 字节。
  • 每字节 = 一列 (8 个像素),bit0 对应 tile 内最上面一行,bit7 对应最下面一行。
  • 因此 tile_ptr[col] 就是"第 col 列的 8 个像素",与 u8g2_ll_hvline_vertical_top_lsb 布局完全匹配。

4.2 自定义显示驱动回调

c 复制代码
static uint8_t u8x8_d_st7735_128x160(u8x8_t *u8x8, uint8_t msg,
                                      uint8_t arg_int, void *arg_ptr);

它按消息(msg)分发处理,各消息含义:

msg 处理方式
U8X8_MSG_DISPLAY_SETUP_MEMORY 调用 u8x8_d_helper_display_setup_memory() 把 u8x8_display_info_st7735_128x160 挂上,告诉 u8g2 屏幕是 16×20 tile(128×160 像素)
U8X8_MSG_DISPLAY_INIT 调用已有的 LCD_Init() 初始化 ST7735S(不重复写复位序列)
U8X8_MSG_DISPLAY_DRAW_TILE 把 arg_ptr 指向的 u8x8_tile_t 展开成 RGB565 并写屏(核心)
U8X8_MSG_DISPLAY_SET_POWER_SAVE 故意不处理,避免与工程低功耗流程冲突
U8X8_MSG_DISPLAY_REFRESH 数据已在 DRAW_TILE 里直接写屏,无需额外操作
其它 返回 1

4.3 1bpp → RGB565 的展开逻辑

st7735_draw_tile_block() 一次处理一块 8 行高的 tile:

  1. 用 LCD_SetWindow(x, y, x+w-1, y+7) 开一个整块窗口;
  2. 拉低 CS、置 RS=1(数据模式);
  3. 逐行(row 0..7)扫描,mask = 1 << row:
    • 像素为 1 → 写入前景色 s_fg_h/s_fg_l;
    • 像素为 0 → 写入背景色 s_bg_h/s_bg_l;
  4. 每行用一次 HAL_SPI_Transmit() 流式发送,最后拉高 CS。

前景/背景色的高、低字节在 U8G2_Port_SetColors() 里预先拆好 (s_fg_h/s_fg_l/s_bg_h/s_bg_l),避免在刷屏内循环做移位运算。


5. 初始化流程

对外入口是 U8G2_Port_Init(),内部调用 u8g2_port_setup(U8G2_PORT_TILE_H):

c 复制代码
static uint8_t u8g2_port_setup(uint8_t tile_rows)
{
    memset(&g_u8g2, 0, sizeof(g_u8g2));
    memset(s_u8g2_buf, 0, sizeof(s_u8g2_buf));

    /* 1) 挂自定义显示驱动;cad/byte/gpio 全部空实现 */
    u8x8_Setup(u8g2_GetU8x8(&g_u8g2),
               u8x8_d_st7735_128x160,   /* 显示驱动 */
               u8x8_dummy_cb,           /* cad */
               u8x8_dummy_cb,           /* byte */
               u8x8_dummy_cb);          /* gpio+delay */

    /* 2) 绑定 1bpp 缓冲区 */
    u8g2_SetupBuffer(&g_u8g2, s_u8g2_buf, tile_rows,
                     u8g2_ll_hvline_vertical_top_lsb, U8G2_R0);

    /* 3) 初始化面板(回调里会调 LCD_Init()) */
    u8g2_InitDisplay(&g_u8g2);
    u8g2_SetPowerSave(&g_u8g2, 0);

    /* 4) 清空缓冲 + 默认字体 */
    u8g2_ClearBuffer(&g_u8g2);
    u8g2_SetFont(&g_u8g2, U8G2_FONT_6X10);
    return 1;
}

调用关系:

复制代码
U8G2_Port_Init()
 └─ u8g2_port_setup(20)
     ├─ u8x8_Setup(...)        → 记录回调函数指针
     ├─ u8g2_SetupBuffer(...)  → 记录缓冲区 & 画点回调 & 旋转方向
     ├─ u8g2_InitDisplay(...)  → 触发 U8X8_MSG_DISPLAY_INIT → LCD_Init()
     ├─ u8g2_SetPowerSave(0)   → 正常显示
     └─ u8g2_ClearBuffer + u8g2_SetFont

注意:U8G2_Port_Init() 内部会调用 LCD_Init(),所以不必再单独调用 LCD_Init()。


6. 使用流程(两种模式)

6.1 全缓冲模式(默认,最简单)

三步:清缓冲 → 画图 → 一次性送显。中途都画在内存里,不会闪烁。

c 复制代码
U8G2_Port_Init();                    /* 初始化(含 LCD_Init) */

u8g2_ClearBuffer(&g_u8g2);           /* 1. 清空 1bpp 缓冲 */
u8g2_SetFont(&g_u8g2, U8G2_FONT_10X20);
u8g2_DrawStr(&g_u8g2, 0, 20, "Hello");
u8g2_DrawCircle(&g_u8g2, 64, 64, 20, U8G2_DRAW_ALL);
u8g2_SendBuffer(&g_u8g2);            /* 2. 一次性刷到屏上 */

RAM 占用:2560B(G431RB 有 32KB RAM,余量充足)。

6.2 页缓冲模式(省 RAM)

用 U8G2_Port_InitPage(tile_rows) 初始化,tile_rows 为缓冲区行数(单位 8 像素),取值 1~20:

c 复制代码
U8G2_Port_InitPage(1);               /* 只保留 128B 缓冲 */

u8g2_FirstPage(&g_u8g2);
do {
    /* 每一页都重新画一遍完整内容 */
    u8g2_DrawStr(&g_u8g2, 0, 20, "Page mode");
    /* ... 其它绘制 ... */
} while (u8g2_NextPage(&g_u8g2));
  • tile_rows = 20 时等价于全缓冲模式。
  • 每页内容要完整重画(因为缓冲被覆盖)。

7. 移植层自定义接口

以下函数由本工程 u8g2_port.h 定义、u8g2_port.c 实现。

7.1 uint8_t U8G2_Port_Init(void)

初始化 u8g2(全缓冲模式)。内部调用 LCD_Init(),并设置默认前景色白 / 背景色黑。

7.2 uint8_t U8G2_Port_InitPage(uint8_t tile_rows)

页缓冲模式初始化,tile_rows 取值 1~20。

7.3 void U8G2_Port_SetColors(uint16_t fg, uint16_t bg)

设置前景色/背景色(RGB565)。例如:

c 复制代码
U8G2_Port_SetColors(LCD_COLOR_YELLOW, LCD_COLOR_BLUE);

7.4 void U8G2_Port_Clear(void)

清屏(清空缓冲 + 整屏刷成背景色)。

7.5 void U8G2_Port_Demo(void)

移植自检画面:几行文字 + 线框 + 圆 + 斜线,用于验证移植是否成功。

7.6 void U8G2_DrawCnStr(u8g2_t *u8g2, uint16_t x, uint16_t y, const char *str, const Font *font)

用工程自己的波特律动字库(font.h 里的 Font 结构,如 &font16x16)画中文/混合字符串。

  • 与 u8g2_DrawStr 的"基线坐标"不同,这里 x,y 是左上角坐标。
  • str 必须是 UTF-8 编码。
  • 字库里找不到的字符会跳过一个格子宽度(不会回退到 ASCII 字体)。
  • 颜色由 U8G2_Port_SetColors() 决定,为"实心"绘制(0 的像素刷成背景色)。

为什么不用 u8g2 自带中文字库?因为 wqy/unifont 等中文字库动辄几十 KB~几 MB,128KB Flash 放不下,所以中文走工程自己的小字库,英文/数字仍走 u8g2 自带字体,互不影响。


8. u8g2 标准 API 函数详解

说明:以下只列本工程实际编译进来、可用 的函数。每个函数的第一个参数固定传 &g_u8g2。

8.1 初始化 / 设置类

函数 作用
u8g2_SetupBuffer(u8g2, buf, tile_buf_height, ll_hvline_cb, u8g2_cb) 绑定绘图缓冲区、指定"画点回调"(本工程用 u8g2_ll_hvline_vertical_top_lsb)和旋转方向(U8G2_R0)
u8g2_GetU8x8(u8g2) 返回 (u8x8_t *),用于底层控制
u8g2_InitDisplay(u8g2) 初始化屏幕(触发 U8X8_MSG_DISPLAY_INIT → 本工程 LCD_Init())
u8g2_SetPowerSave(u8g2, is_enable) 使能/关闭省电模式(本工程回调里被忽略)
u8g2_SetFlipMode(u8g2, mode) 设置翻转模式(0/1/2/3,本工程未用)
u8g2_SetContrast(u8g2, value) 设置对比度(本工程未用)
u8g2_SetDisplayRotation(u8g2, u8g2_cb) 切换屏幕旋转方向(U8G2_R0/R1/R2/R3)

8.2 缓冲区 / 送显类

函数 作用
void u8g2_ClearBuffer(u8g2) 把整个 1bpp 缓冲清零(不影响屏幕 ,需配合 SendBuffer)
void u8g2_SendBuffer(u8g2) 把缓冲区内容一次性发送到屏幕(遍历每个 tile,触发 DRAW_TILE)
void u8g2_ClearDisplay(u8g2) 清空缓冲 并立即 送显(= ClearBuffer + SendBuffer)
void u8g2_FirstPage(u8g2) 页缓冲模式:定位到第一页(配合 NextPage 循环)
uint8_t u8g2_NextPage(u8g2) 页缓冲模式:切换到下一页,返回 1 表示还有下一页;返回 0 表示结束
void u8g2_UpdateDisplayArea(u8g2, tx, ty, tw, th) 只刷新指定 tile 区域
void u8g2_UpdateDisplay(u8g2) 刷新整个显示(页缓冲模式用)
u8g2_GetBufferPtr(u8g2) 返回缓冲区指针
u8g2_GetBufferTileHeight(u8g2) 返回缓冲区 tile 行数
u8g2_GetBufferTileWidth(u8g2) 返回缓冲区 tile 列数
u8g2_GetBufferCurrTileRow(u8g2) 页缓冲模式:当前 tile 行(FirstPage 后有效)
u8g2_WriteBufferXBM / XBM2 / PBM / PBM2(u8g2, out) 把缓冲区以 XBM/PBM 文本格式导出(调试用)

8.3 画点 / 画线类

函数 作用
void u8g2_SetDrawColor(u8g2, color) 设置绘制颜色索引(0/1,本工程 1=前景色、0=背景色)
u8g2_GetDrawColor(u8g2) 返回当前绘制颜色
void u8g2_DrawPixel(u8g2, x, y) 画一个点
void u8g2_DrawHLine(u8g2, x, y, len) 画水平线,从 (x,y) 向右长 len 像素
void u8g2_DrawVLine(u8g2, x, y, len) 画垂直线,从 (x,y) 向下长 len 像素
void u8g2_DrawHVLine(u8g2, x, y, len, dir) 画水平或垂直线,dir=0 水平 / dir=1 垂直
void u8g2_DrawLine(u8g2, x1, y1, x2, y2) 画任意两点连线(Bresenham 算法)

8.4 矩形类

函数 作用
void u8g2_DrawBox(u8g2, x, y, w, h) 画实心矩形,左上角 (x,y),宽 w 高 h
void u8g2_DrawFrame(u8g2, x, y, w, h) 画矩形边框(1 像素宽)
void u8g2_DrawRBox(u8g2, x, y, w, h, r) 画圆角实心矩形,圆角半径 r
void u8g2_DrawRFrame(u8g2, x, y, w, h, r) 画圆角矩形边框

8.5 圆 / 椭圆 / 圆弧类

函数 作用
void u8g2_DrawCircle(u8g2, x0, y0, rad, option) 画圆边框,圆心 (x0,y0) 半径 rad;option 决定画哪些象限(见第 12 节)
void u8g2_DrawDisc(u8g2, x0, y0, rad, option) 画实心圆(圆盘)
void u8g2_DrawEllipse(u8g2, x0, y0, rx, ry, option) 画椭圆边框,横半径 rx 纵半径 ry
void u8g2_DrawFilledEllipse(u8g2, x0, y0, rx, ry, option) 画实心椭圆
void u8g2_DrawArc(u8g2, x0, y0, rad, start, end) 画圆弧。start/end 范围 0~255 ,每格 = 360/256 度,end 到达 255 即约整圆

本工程 main.c 里 u8g2_DrawArc(&g_u8g2, 64, 32, 10, 0, 255) 就是画一个以 (64,32) 为圆心、半径 10 的近似整圆。

8.6 多边形类

函数 作用
void u8g2_DrawTriangle(u8g2, x0,y0, x1,y1, x2,y2) 画实心三角形(三个顶点)
void u8g2_ClearPolygonXY(u8g2) 清空多边形顶点列表
void u8g2_DrawPolygon(u8g2) 用 AddPolygonXY 累积的顶点画填充多边形

8.7 位图类

函数 作用
void u8g2_SetBitmapMode(u8g2, is_transparent) 位图透明模式:1=透明(0 像素不画)、0=实心(0 像素画背景色)
void u8g2_DrawBitmap(u8g2, x, y, cnt, h, bitmap) 画位图,cnt=横向字节数(每字节一列 8 像素),h=高度
void u8g2_DrawXBM(u8g2, x, y, w, h, bitmap) 画 XBM 格式位图,w 宽 h 高
void u8g2_DrawXBMP(u8g2, x, y, w, h, bitmap) 同上,bitmap 假定在 PROGMEM(本工程与 XBM 相同)

8.8 文字 / 字体类

函数 作用
void u8g2_SetFont(u8g2, font) 选择字体(如 U8G2_FONT_6X10 / U8G2_FONT_10X20)
void u8g2_SetFontMode(u8g2, is_transparent) 字体透明模式:1=透明背景、0=实心背景
void u8g2_SetFontDirection(u8g2, dir) 文字方向(0/1/2/3,旋转 90° 递增)
u8g2_uint_t u8g2_DrawStr(u8g2, x, y, str) 画字符串,(x,y) 为基线坐标,返回实际像素宽度
u8g2_uint_t u8g2_DrawUTF8(u8g2, x, y, str) 画 UTF-8 字符串(需 U8G2_WITH_UNICODE,本工程已定义)
u8g2_uint_t u8g2_DrawGlyph(u8g2, x, y, encoding) 画单个字符字形
u8g2_uint_t u8g2_GetStrWidth(u8g2, s) 计算字符串像素宽度(不含实际绘制)
u8g2_uint_t u8g2_GetUTF8Width(u8g2, str) 计算 UTF-8 字符串像素宽度
u8g2_GetAscent(u8g2) 当前字体基线以上高度
u8g2_GetDescent(u8g2) 当前字体基线以下高度
u8g2_GetMaxCharWidth(u8g2) 当前字体最大字符宽度
u8g2_GetMaxCharHeight(u8g2) 当前字体最大字符高度
u8g2_SetFontPosBaseline / Bottom / Top / Center(u8g2) 设置文字基准位置(基线/底部/顶部/居中)
u8g2_SetFontRefHeightText(u8g2) 参考高度 = 文本高度(默认)
u8g2_SetFontRefHeightExtendedText(u8g2) 参考高度 = 含下延的扩展文本高度
u8g2_SetFontRefHeightAll(u8g2) 参考高度 = 完整字体高度

U8G2_FONT_6X10 等宏在本工程 u8g2_port.h 里定义,本质是 u8g2_font_xxx_tr 字库数组。

8.9 裁剪窗口类

函数 作用
void u8g2_SetClipWindow(u8g2, x0, y0, x1, y1) 设置裁剪窗口(超出部分不绘制)
void u8g2_SetMaxClipWindow(u8g2) 恢复裁剪窗口为整屏

8.10 相交检测类

函数 作用
uint8_t u8g2_IsIntersection(u8g2, x0, y0, x1, y1) 判断矩形区域是否与当前缓冲区/裁剪区相交(页缓冲模式加速用)

9. u8x8 底层函数详解

这些函数由 u8g2 内部使用,本工程移植层也直接调用,理解它们有助于理解移植。

函数 作用
void u8x8_Setup(u8x8, display_cb, cad_cb, byte_cb, gpio_and_delay_cb) 注册 4 个回调函数指针:显示驱动、CAD(命令/数据)、字节通信、GPIO/延时
uint8_t u8x8_dummy_cb(u8x8, msg, arg_int, arg_ptr) 空回调,什么都不做。本工程把 cad/byte/gpio 都设成它
void u8x8_d_helper_display_setup_memory(u8x8, display_info) 把 u8x8_display_info_t 挂到 u8x8->display_info,用于确定 tile 数/像素数
void u8x8_InitDisplay(u8x8) 触发 U8X8_MSG_DISPLAY_INIT
void u8x8_SetPowerSave(u8x8, is_enable) 触发 U8X8_MSG_DISPLAY_SET_POWER_SAVE
void u8x8_SetFlipMode(u8x8, mode) 触发翻转模式消息
void u8x8_SetContrast(u8x8, value) 触发对比度消息

u8x8 显示驱动回调的消息(msg) (u8x8.h 定义,本工程用到的):

宏 值 含义
U8X8_MSG_DISPLAY_SETUP_MEMORY 9 只设置内存信息,不与屏幕通信
U8X8_MSG_DISPLAY_INIT 10 初始化显示
U8X8_MSG_DISPLAY_SET_POWER_SAVE 11 进入/退出省电
U8X8_MSG_DISPLAY_DRAW_TILE 15 画 tile(核心消息)
U8X8_MSG_DISPLAY_REFRESH 16 刷新显示

10. 字体系统

10.1 本工程引入的 4 个 ASCII 字体

宏名 底层字体 说明
U8G2_FONT_6X10 u8g2_font_6x10_tr 6×10 等宽,最小
U8G2_FONT_8X13 u8g2_font_8x13_tr 8×13 等宽
U8G2_FONT_10X20 u8g2_font_10x20_tr 10×20 等宽,数字大
U8G2_FONT_HELVB12 u8g2_font_helvB12_tr Helvetica 粗体 12,比例字体

这些字体 .c 文件位于 BSP/Transplant/U8G2/fonts/。

10.2 u8g2 字体命名规则(u8g2_font_<字型><高度>_<后缀>.c)

  • t = transparent(透明);r = 基准在右上(reference);f = 完整(full);e = 扩展(extended);m = 中等;n = 不透明背景;B/O/b = 粗体/轮廓等变体。
  • 本工程用到的 _tr 表示"透明 + 右上基准"。

10.3 中文字库(工程自带,非 u8g2)

  • 定义于 font.c,Font 结构见 font.h。
  • font16x16 目前收录 11 个字符 :波 特 律 动 ℃ 你 好 , 世 界 !。
  • 每条目格式:4 字节 UTF-8 头(不足补 0x00)+ 32 字节字模(16×16 点阵)。
  • 字模取模约定:idx = (j/8)*w + i,bit = 1 << (j%8),即"上半 16 字节 + 下半 16 字节",每字节一列,bit0 为段内最上行。
  • 需要新增汉字时,可用 BSP/Transplant/U8G2/fonts/gen_cn_glyphs.py 生成,再粘进 font.c 的 zh16x16 数组并把 font16x16.len 加 1。

10.4 中文绘制方式

c 复制代码
U8G2_DrawCnStr(&g_u8g2, 8, 100, "你好,世界!", &font16x16);

U8G2_DrawCnStr 内部:解析 UTF-8 → 查 font->chars 字库 → 找到后逐像素调用 u8g2_SetDrawColor + u8g2_DrawPixel 画进 u8g2 缓冲。


11. 关键数据结构

11.1 u8g2_t(u8g2.h)

c 复制代码
typedef struct u8g2_struct u8g2_t;
struct u8g2_struct {
    const u8g2_cb_t *cb;         /* 旋转/坐标变换回调(U8G2_R0 等) */
    u8x8_t u8x8;                 /* 内嵌的 u8x8 底层结构(所以二者内存重叠) */
    u8g2_uint_t width, height;   /* 屏幕像素尺寸 */
    uint8_t *tile_buf_ptr;       /* 1bpp 缓冲区指针 */
    uint8_t tile_buf_height;     /* 缓冲区 tile 行数 */
    uint8_t tile_curr_row;       /* 页缓冲模式当前行 */
    u8g2_uint_t pixel_buf_width, pixel_buf_height, pixel_curr_row;
    u8g2_uint_t buf_y0, buf_y1;  /* 缓冲区纵向范围 */
    u8g2_uint_t user_x0,x1,y0,y1;/* 用户坐标窗口 */
    u8g2_uint_t clip_x0,x1,y0,y1;/* 裁剪窗口 */
    uint8_t font_mode;           /* 字体透明模式 */
    uint8_t draw_color;          /* 当前绘制颜色(0/1) */
    uint8_t bitmap_transparency; /* 位图透明模式 */
    ...                            /* 字体信息、像素循环状态等 */
};

11.2 u8x8_display_info_t(u8x8.h)

本工程在 u8g2_port.c 里定义 u8x8_display_info_st7735_128x160,关键字段:

c 复制代码
tile_width  = 16   /* 128/8 */
tile_height = 20   /* 160/8 */
pixel_width = 128
pixel_height= 160

11.3 u8x8_tile_t(u8x8.h)

c 复制代码
typedef struct u8x8_tile_struct {
    uint8_t *tile_ptr;  /* 指向 1bpp 数据 */
    uint8_t cnt;        /* 横向 tile 个数 */
    uint8_t x_pos;      /* 起始 tile 列 */
    uint8_t y_pos;      /* 起始 tile 行 */
} u8x8_tile_t;

11.4 u8g2_cb_t(旋转回调)

  • U8G2_R0:0 度(默认,本工程使用)。
  • U8G2_R1/R2/R3:顺时针 90/180/270 度。

12. 常用常量与宏

12.1 画圆象限选项(u8g2.h)

c 复制代码
#define U8G2_DRAW_UPPER_RIGHT 0x01
#define U8G2_DRAW_UPPER_LEFT  0x02
#define U8G2_DRAW_LOWER_LEFT  0x04
#define U8G2_DRAW_LOWER_RIGHT 0x08
#define U8G2_DRAW_ALL (UPPER_RIGHT|UPPER_LEFT|LOWER_RIGHT|LOWER_LEFT)  /* =0x0F 整圆 */

12.2 旋转方向

c 复制代码
#define U8G2_R0 (&u8g2_cb_r0)  /* 本工程使用 */
#define U8G2_R1 (&u8g2_cb_r1)
#define U8G2_R2 (&u8g2_cb_r2)
#define U8G2_R3 (&u8g2_cb_r3)

12.3 本工程移植层宏(u8g2_port.h)

c 复制代码
#define U8G2_PORT_WIDTH  LCD_Width     /* 128 */
#define U8G2_PORT_HEIGHT LCD_Height    /* 160 */
#define U8G2_PORT_TILE_W (WIDTH/8)     /* 16 */
#define U8G2_PORT_TILE_H (HEIGHT/8)    /* 20 */

13. 常见问题与注意事项

  1. u8g2 只能画两种颜色 :因为 ST7735S 是彩屏而 u8g2 是单色库,画面只有前景色/背景色。想画多色图请用工程原有的 TFT_LCD 驱动或 g_lcd 对象接口。

  2. 覆盖式刷新 :缓冲区内为 0 的像素会被刷成背景色,不要指望半透明叠加。

  3. 不要重复初始化面板 :U8G2_Port_Init() 内部已调用 LCD_Init(),外部无需再调。

  4. 省电消息被忽略 :U8X8_MSG_DISPLAY_SET_POWER_SAVE 在回调里故意不处理,休眠/唤醒请直接调 LCD_EnterSleep() / LCD_ExitSleep(),避免与工程低功耗流程打架。

  5. u8g2_DrawStr 的 y 是基线 ,而 U8G2_DrawCnStr 的 y 是左上角,两者语义不同。

  6. 中文只能显示字库里收录的字 :font16x16 只有 11 个字,其它中文会跳格不显示,需要先用 gen_cn_glyphs.py 生成字模并加入 font.c。

  7. 页缓冲模式必须整页重画 :FirstPage/NextPage 每次只保留一小块缓冲,所以每页都要把该页所有内容重画一遍。

  8. 缓冲区大小 :全缓冲固定 2560 字节(静态数组 s_u8g2_buf),勿越界。

  9. 源码需 UTF-8 编码 :U8G2_DrawCnStr 传入的中文字符串需要源文件保存为 UTF-8。

  10. SPI 超时 :刷屏用 HAL_SPI_Transmit(..., 100ms),超时宁可丢一块也不卡死整机(U8G2_PORT_SPI_TIMEOUT)。


附录:main.c 中的完整示例

c 复制代码
/* u8g2:初始化(内部会调 LCD_Init)→ 画图 → 一次性送显 */
U8G2_Port_Init();

u8g2_ClearBuffer(&g_u8g2);
u8g2_DrawArc(&g_u8g2, 64, 32, 10, 0, 255);   /* 角度 0~255,255≈整圆 */
u8g2_SetFont(&g_u8g2, U8G2_FONT_10X20);
u8g2_DrawStr(&g_u8g2, 5, 80, "Hello world!");
U8G2_DrawCnStr(&g_u8g2, 8, 100, "你好,世界!", &font16x16); /* 中文走工程字库 */
u8g2_SendBuffer(&g_u8g2);