aaaaaaaaaaa
u8g2 库移植与使用说明(STM32G431 + ST7735S)
本文档面向本工程
Moisture_Meter_g431_lcd,完整说明 u8g2 图形库是如何移植到 STM32G431 + ST7735S 彩色 TFT 上、如何使用、以及本工程实际用到的每一个 u8g2 函数的作用。
目录
- 背景与总体思路
- u8g2 库的分层架构
- 工程目录结构与编译范围
- 移植原理与数据流
- 初始化流程
- 使用流程(两种模式)
- 移植层自定义接口
- u8g2 标准 API 函数详解
- u8x8 底层函数详解
- 字体系统
- 关键数据结构
- 常用常量与宏
- 常见问题与注意事项
1. 背景与总体思路
1.1 u8g2 是什么
u8g2 是一个开源的嵌入式单色(1bpp)图形库,广泛用于 OLED / LCD 点阵屏。它提供了统一的绘图 API(画点、线、矩形、圆、圆弧、字符串、位图等)以及大量字体,底层通过一个"显示驱动回调"适配各种具体屏幕。
1.2 本工程的特殊之处
u8g2 只支持单色屏,而本工程的 ST7735S 是 RGB565 彩色屏。因此移植时没有使用 u8g2 自带的任何显示驱动(u8g2_d_setup.c / u8g2_d_memory.c 里的驱动都没编译),而是:
- 让 u8g2 画在内部一块 1bpp 缓冲区(
128×160/8 = 2560 字节); - 自己编写一个 u8x8 显示驱动回调
u8x8_d_st7735_128x160(); - 在回调里把 1bpp 像素展开成 RGB565(只用前景色 / 背景色两种颜色);
- 复用工程已有的
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 的超集。宏定义:
#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 自定义显示驱动回调
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:
- 用
LCD_SetWindow(x, y, x+w-1, y+7)开一个整块窗口; - 拉低 CS、置 RS=1(数据模式);
- 逐行(row 0..7)扫描,
mask = 1 << row:- 像素为 1 → 写入前景色
s_fg_h/s_fg_l; - 像素为 0 → 写入背景色
s_bg_h/s_bg_l;
- 像素为 1 → 写入前景色
- 每行用一次
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):
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 全缓冲模式(默认,最简单)
三步:清缓冲 → 画图 → 一次性送显。中途都画在内存里,不会闪烁。
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:
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)。例如:
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 中文绘制方式
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)
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,关键字段:
tile_width = 16 /* 128/8 */
tile_height = 20 /* 160/8 */
pixel_width = 128
pixel_height= 160
11.3 u8x8_tile_t(u8x8.h)
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)
#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 旋转方向
#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)
#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. 常见问题与注意事项
-
u8g2 只能画两种颜色:因为 ST7735S 是彩屏而 u8g2 是单色库,画面只有前景色/背景色。想画多色图请用工程原有的
TFT_LCD驱动或g_lcd对象接口。 -
覆盖式刷新:缓冲区内为 0 的像素会被刷成背景色,不要指望半透明叠加。
-
不要重复初始化面板:
U8G2_Port_Init()内部已调用LCD_Init(),外部无需再调。 -
省电消息被忽略:
U8X8_MSG_DISPLAY_SET_POWER_SAVE在回调里故意不处理,休眠/唤醒请直接调LCD_EnterSleep()/LCD_ExitSleep(),避免与工程低功耗流程打架。 -
u8g2_DrawStr的 y 是基线,而U8G2_DrawCnStr的 y 是左上角,两者语义不同。 -
中文只能显示字库里收录的字:
font16x16只有 11 个字,其它中文会跳格不显示,需要先用gen_cn_glyphs.py生成字模并加入font.c。 -
页缓冲模式必须整页重画:
FirstPage/NextPage每次只保留一小块缓冲,所以每页都要把该页所有内容重画一遍。 -
缓冲区大小:全缓冲固定 2560 字节(静态数组
s_u8g2_buf),勿越界。 -
源码需 UTF-8 编码:
U8G2_DrawCnStr传入的中文字符串需要源文件保存为 UTF-8。 -
SPI 超时:刷屏用
HAL_SPI_Transmit(..., 100ms),超时宁可丢一块也不卡死整机(U8G2_PORT_SPI_TIMEOUT)。
附录:main.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);

浙公网安备 33010602011771号