Files
SmartMassager_STM32/Core/Src/screen.c
wangbeihong 4c37261cc8 ```
refactor(gbk_text): 重构GBK文本常量定义并优化命名

移除过时的文本定义,使用更简洁通用的名称替换原有特定功能的文本
常量,并更新相应的长度定义和别名映射。

BREAKING CHANGE: 原有的特定功能文本常量已被移除,需使用新的通
用名称进行访问。
```
2026-02-17 18:55:34 +08:00

571 lines
15 KiB
C
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file screen.c
* @brief 串口屏驱动实现JC系列使用 USART1
******************************************************************************
*/
/* USER CODE END Header */
#include "screen.h"
#include "cmsis_os.h"
#include "cmsis_os2.h"
#include "elog.h"
#include "gbk_text.h"
#include "main.h"
#include "usart.h"
#include <stdint.h>
#include <string.h>
/* 发送缓冲 */
static uint8_t txbuf[512];
/* =========================================================
内部工具函数(替代 sprintf
========================================================= */
/* 将字符串复制到发送缓冲区 */
static uint16_t buf_str(uint16_t p, const char *s) {
while (*s)
txbuf[p++] = *s++;
return p;
}
/* 将32位无符号整数转换为字符串并复制到发送缓冲区 */
static uint16_t buf_u32(uint16_t p, uint32_t v) {
char tmp[12];
int i = 0;
if (v == 0) {
txbuf[p++] = '0';
return p;
}
while (v) {
tmp[i++] = v % 10 + '0';
v /= 10;
}
while (i--)
txbuf[p++] = tmp[i];
return p;
}
/* 发送指定长度的数据 */
static void send_buf(uint16_t len) {
HAL_UART_Transmit(&huart1, txbuf, len, HAL_MAX_DELAY);
osDelay(100);
}
/* =========================================================
基础接口
========================================================= */
/* 发送数据缓冲区 */
int Screen_SendBuf(const uint8_t *buf, uint16_t len) {
if (!buf || !len)
return -1;
HAL_StatusTypeDef st =
HAL_UART_Transmit(&huart1, (uint8_t *)buf, len, HAL_MAX_DELAY);
osDelay(20);
return (st == HAL_OK) ? 0 : -2;
}
/* 发送命令字符串 */
int Screen_SendCmd(const char *cmd) {
if (!cmd)
return -1;
return Screen_SendBuf((uint8_t *)cmd, strlen(cmd));
}
/* 发送命令并等待确认(当前未实现超时机制) */
int Screen_SendCmdWaitOK(const char *cmd, uint32_t timeout_ms) {
(void)timeout_ms;
return Screen_SendCmd(cmd);
}
/* =========================================================
初始化
========================================================= */
/* 屏幕初始化函数 */
void Screen_Init(void) {
osDelay(1000);
Screen_Clear(BG_COLOR);
osDelay(200);
Screen_SetBGColor(BG_COLOR);
Screen_SetBrightness(0);
osDelay(200);
// 绘制外边框
Screen_Box(0, 0, 127, 127, TEXT_COLOR);
// 绘制标题24号字体高度约为24-26像素留一点边距
Screen_DrawText24_GBK(5, 2, text_device_name, text_device_name_LEN,
TEXT_COLOR);
// 标题线在标题下方y=28位置
Screen_Line(1, 28, 126, 28, TEXT_COLOR);
// 将剩余区域28-127等分为3份
// 第1个等分区域29-60
Screen_Line(1, 60, 126, 60, TEXT_COLOR); // 第1条分隔线
// 第2个等分区域61-93
Screen_Line(1, 93, 126, 93, TEXT_COLOR); // 第2条分隔线
// 第3个等分区域94-126
// 底部区域不需要画线,因为已经有边框了
}
/* =========================================================
基础命令(全部保留)
========================================================= */
/* 清除指定图层 */
void Screen_Clear(uint8_t layer) {
uint16_t p = 0;
p = buf_str(p, "DIR(1);CLR(");
p = buf_u32(p, layer);
p = buf_str(p, ");\r\n");
send_buf(p);
}
/* 显示指定地址的图片 */
void Screen_DisplayImage(uint32_t addr) {
uint16_t p = 0;
p = buf_str(p, "DIR(1);FSIMG(");
p = buf_u32(p, addr);
p = buf_str(p, ",0,0,128,128,0);\r\n");
send_buf(p);
}
/* 设置屏幕亮度 */
void Screen_SetBrightness(uint8_t val) {
uint16_t p = 0;
p = buf_str(p, "BL(");
p = buf_u32(p, val);
p = buf_str(p, ");\r\n");
send_buf(p);
}
/* 设置屏幕旋转方向 */
void Screen_SetRotation(uint8_t dir) {
uint16_t p = 0;
p = buf_str(p, "DIR(");
p = buf_u32(p, dir);
p = buf_str(p, ");\r\n");
send_buf(p);
}
/* 设置串口波特率 */
void Screen_SetBaud(uint32_t baud) {
uint16_t p = 0;
p = buf_str(p, "BPS(");
p = buf_u32(p, baud);
p = buf_str(p, ");\r\n");
send_buf(p);
}
/* 在指定位置显示图片 */
void Screen_DisplayImageAtAddr(uint32_t addr, uint16_t x, uint16_t y,
uint16_t w, uint16_t h, uint8_t mode) {
uint16_t p = 0;
p = buf_str(p, "FSIMG(");
p = buf_u32(p, addr);
txbuf[p++] = ',';
p = buf_u32(p, x);
txbuf[p++] = ',';
p = buf_u32(p, y);
txbuf[p++] = ',';
p = buf_u32(p, w);
txbuf[p++] = ',';
p = buf_u32(p, h);
txbuf[p++] = ',';
p = buf_u32(p, mode);
p = buf_str(p, ");\r\n");
send_buf(p);
}
/* 通过索引显示图片 */
void Screen_DisplayImageByIndex(uint32_t index, uint16_t x, uint16_t y,
uint16_t w, uint16_t h, uint8_t mode) {
Screen_DisplayImageAtAddr(index, x, y, w, h, mode);
}
/* 绘制单个像素点 */
void Screen_Pixel(uint16_t x, uint16_t y, uint8_t color) {
uint16_t p = 0;
p = buf_str(p, "PS(");
p = buf_u32(p, x);
txbuf[p++] = ',';
p = buf_u32(p, y);
txbuf[p++] = ',';
p = buf_u32(p, color);
p = buf_str(p, ");\r\n");
send_buf(p);
}
/* 绘制直线 */
void Screen_Line(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2,
uint8_t color) {
uint16_t p = 0;
p = buf_str(p, "PL(");
p = buf_u32(p, x1);
txbuf[p++] = ',';
p = buf_u32(p, y1);
txbuf[p++] = ',';
p = buf_u32(p, x2);
txbuf[p++] = ',';
p = buf_u32(p, y2);
txbuf[p++] = ',';
p = buf_u32(p, color);
p = buf_str(p, ");\r\n");
send_buf(p);
}
/* 绘制矩形框 */
void Screen_Box(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2,
uint8_t color) {
uint16_t p = 0;
p = buf_str(p, "BOX(");
p = buf_u32(p, x1);
txbuf[p++] = ',';
p = buf_u32(p, y1);
txbuf[p++] = ',';
p = buf_u32(p, x2);
txbuf[p++] = ',';
p = buf_u32(p, y2);
txbuf[p++] = ',';
p = buf_u32(p, color);
p = buf_str(p, ");\r\n");
send_buf(p);
}
/* 绘制填充矩形 */
void Screen_BoxFill(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2,
uint8_t color) {
uint16_t p = 0;
p = buf_str(p, "BOXF(");
p = buf_u32(p, x1);
txbuf[p++] = ',';
p = buf_u32(p, y1);
txbuf[p++] = ',';
p = buf_u32(p, x2);
txbuf[p++] = ',';
p = buf_u32(p, y2);
txbuf[p++] = ',';
p = buf_u32(p, color);
p = buf_str(p, ");\r\n");
send_buf(p);
}
/* 绘制圆形 */
void Screen_Circle(uint16_t x, uint16_t y, uint16_t r, uint8_t color) {
uint16_t p = 0;
p = buf_str(p, "CIR(");
p = buf_u32(p, x);
txbuf[p++] = ',';
p = buf_u32(p, y);
txbuf[p++] = ',';
p = buf_u32(p, r);
txbuf[p++] = ',';
p = buf_u32(p, color);
p = buf_str(p, ");\r\n");
send_buf(p);
}
/* 绘制填充圆形 */
void Screen_CircleFill(uint16_t x, uint16_t y, uint16_t r, uint8_t color) {
uint16_t p = 0;
p = buf_str(p, "CIRF(");
p = buf_u32(p, x);
txbuf[p++] = ',';
p = buf_u32(p, y);
txbuf[p++] = ',';
p = buf_u32(p, r);
txbuf[p++] = ',';
p = buf_u32(p, color);
p = buf_str(p, ");\r\n");
send_buf(p);
}
/* 设置背景颜色 */
void Screen_SetBGColor(uint8_t color) {
uint16_t p = 0;
p = buf_str(p, "SBC(");
p = buf_u32(p, color);
p = buf_str(p, ");\r\n");
send_buf(p);
}
/* =========================================================
GBK核心所有字号
========================================================= */
/* 绘制GBK文本的核心函数 */
static void draw_gbk(const char *cmd, uint16_t x, uint16_t y,
const uint8_t *gbk, uint16_t len, uint8_t color,
uint8_t extra, uint8_t has_extra) {
uint16_t p = 0;
p = buf_str(p, cmd);
txbuf[p++] = '(';
p = buf_u32(p, x);
txbuf[p++] = ',';
p = buf_u32(p, y);
txbuf[p++] = ',';
txbuf[p++] = '\'';
memcpy(&txbuf[p], gbk, len);
p += len;
txbuf[p++] = '\'';
txbuf[p++] = ',';
p = buf_u32(p, color);
if (has_extra) {
txbuf[p++] = ',';
p = buf_u32(p, extra);
}
p = buf_str(p, ");\r\n");
send_buf(p);
}
/* 16号字体GBK文本绘制 */
void Screen_DrawText16_GBK(uint16_t x, uint16_t y, const uint8_t *g, uint16_t l,
uint8_t c) {
draw_gbk("DC16", x, y, g, l, c, 0, 0);
}
void Screen_DrawText16V_GBK(uint16_t x, uint16_t y, const uint8_t *g,
uint16_t l, uint8_t c) {
draw_gbk("DCV16", x, y, g, l, c, 0, 0);
}
/* 24号字体GBK文本绘制 */
void Screen_DrawText24_GBK(uint16_t x, uint16_t y, const uint8_t *g, uint16_t l,
uint8_t c) {
draw_gbk("DC24", x, y, g, l, c, 0, 0);
}
void Screen_DrawText24V_GBK(uint16_t x, uint16_t y, const uint8_t *g,
uint16_t l, uint8_t c) {
draw_gbk("DCV24", x, y, g, l, c, 0, 0);
}
/* 32号字体GBK文本绘制 */
void Screen_DrawText32_GBK(uint16_t x, uint16_t y, const uint8_t *g, uint16_t l,
uint8_t c) {
draw_gbk("DC32", x, y, g, l, c, 0, 0);
}
void Screen_DrawText32V_GBK(uint16_t x, uint16_t y, const uint8_t *g,
uint16_t l, uint8_t c) {
draw_gbk("DCV32", x, y, g, l, c, 0, 0);
}
/* 48号字体GBK文本绘制 */
void Screen_DrawText48_GBK(uint16_t x, uint16_t y, const uint8_t *g, uint16_t l,
uint8_t c, uint8_t t) {
draw_gbk("DC48", x, y, g, l, c, t, 1);
}
// /**
// * @brief 清空指定的等分区域
// * @param zone 区域编号1-3
// * @param redraw_line 是否重新绘制区域分隔线
// */
// void Screen_ClearZoneEx(uint8_t zone, bool redraw_line)
// {
// if (zone < 1 || zone > 3) {
// elog_e("Screen", "Invalid zone number: %d", zone);
// return;
// }
// elog_i("Screen", "Clearing zone %d (redraw_line=%d)...", zone,
// redraw_line);
// int16_t y_start, y_end;
// // 区域定义
// const struct {
// int16_t y_start;
// int16_t y_end;
// } zones[] = {
// {29, 60}, // 区域1
// {61, 93}, // 区域2
// {94, 126} // 区域3
// };
// y_start = zones[zone-1].y_start;
// y_end = zones[zone-1].y_end;
// // 清空指定区域
// Screen_BoxFill(1, y_start, 126, y_end, BG_COLOR);
// // 重新绘制分隔线(如果需要)
// if (redraw_line) {
// switch(zone) {
// case 1:
// Screen_Line(1, 60, 126, 60, TEXT_COLOR); // 区域1下边界线
// break;
// case 2:
// Screen_Line(1, 60, 126, 60, TEXT_COLOR); // 区域1下边界线
// Screen_Line(1, 93, 126, 93, TEXT_COLOR); // 区域2下边界线
// break;
// case 3:
// Screen_Line(1, 60, 126, 60, TEXT_COLOR); // 区域1下边界线
// Screen_Line(1, 93, 126, 93, TEXT_COLOR); // 区域2下边界线
// break;
// }
// }
// }
// /**
// * @brief 清空所有内容区域(保留标题和边框)
// */
// void Screen_ClearAllZones(void)
// {
// elog_i("Screen", "Clearing all zones...");
// // 一次性清空所有内容区域(从标题线下方到底部)
// Screen_BoxFill(1, 29, 126, 126, BG_COLOR);
// // 重新绘制所有分隔线
// Screen_Line(1, 60, 126, 60, TEXT_COLOR); // 第1条分隔线
// Screen_Line(1, 93, 126, 93, TEXT_COLOR); // 第2条分隔线
// }
// /**
// * @brief 清空指定的等分区域
// * @param zone 区域编号1-3
// * @note 区域划分:
// * 区域1y坐标 29-60高度32像素
// * 区域2y坐标 61-93高度33像素
// * 区域3y坐标 94-126高度33像素
// */
// void Screen_ClearZone(uint8_t zone)
// {
// if (zone < 1 || zone > 3) {
// elog_e("Screen", "Invalid zone number: %d", zone);
// return;
// }
// elog_i("Screen", "Clearing zone %d...", zone);
// int16_t y_start, y_end;
// switch(zone) {
// case 1:
// y_start = 29;
// y_end = 60;
// break;
// case 2:
// y_start = 61;
// y_end = 93;
// break;
// case 3:
// y_start = 94;
// y_end = 126;
// break;
// default:
// return;
// }
// // 清空指定区域x从1到126避开左右边框
// Screen_BoxFill(1, y_start, 126, y_end, BG_COLOR);
// }
// // 在指定区域显示内容(适应多行文字)
// void Screen_ShowInZone(uint8_t zone, const uint8_t *text, uint16_t text_len)
// {
// // 区域定义(扩大清空范围,确保完全清除)
// const struct {
// int16_t y_start_clear; // 清空起始Y向上多清空一点
// int16_t y_end_clear; // 清空结束Y向下多清空一点
// int16_t y_text; // 文字起始Y
// } zones[] = {
// {30, 61, 35}, // 区域1清空30-61从Y=30开始避免清除标题线
// {60, 94, 67}, // 区域2清空60-94包含上下边界线
// {93, 127, 100} // 区域3清空93-127包含上边界线和底边框
// };
// if (zone < 1 || zone > 3) {
// elog_e("Screen", "Invalid zone number: %d", zone);
// return;
// }
// elog_i("Screen", "Showing text in zone %d (multi-line support)...",
// zone);
// // 扩大清空范围确保完全清除两行文字X从1开始到126
// Screen_BoxFill(1, zones[zone-1].y_start_clear, 126,
// zones[zone-1].y_end_clear, BG_COLOR);
// // 重新绘制边界线X从1开始到126
// switch(zone) {
// case 1:
// Screen_Line(1, 28, 126, 28, TEXT_COLOR); // 标题线保持在28
// Screen_Line(1, 60, 126, 60, TEXT_COLOR); // 区域1下边界线
// break;
// case 2:
// Screen_Line(1, 60, 126, 60, TEXT_COLOR); // 区域1下边界线
// Screen_Line(1, 93, 126, 93, TEXT_COLOR); // 区域2下边界线
// break;
// case 3:
// Screen_Line(1, 93, 126, 93, TEXT_COLOR); // 区域2下边界线
// // 区域3下边界线是屏幕边框不需要重绘
// break;
// }
// // 显示文字X从1开始Y使用调整后的坐标
// Screen_DrawText16_GBK(2, zones[zone-1].y_text, text, text_len,
// TEXT_COLOR);
// }
static void Screen_DrawZoneLines(void) {
Screen_Line(SCREEN_LEFT, TITLE_LINE_Y, SCREEN_RIGHT, TITLE_LINE_Y,
TEXT_COLOR);
Screen_Line(SCREEN_LEFT, ZONE1_LINE_Y, SCREEN_RIGHT, ZONE1_LINE_Y,
TEXT_COLOR);
Screen_Line(SCREEN_LEFT, ZONE2_LINE_Y, SCREEN_RIGHT, ZONE2_LINE_Y,
TEXT_COLOR);
}
void Screen_ClearZone(uint8_t zone) {
if (zone < 1 || zone > 3)
return;
const ScreenZone_t *z = &g_zones[zone - 1];
Screen_BoxFill(SCREEN_LEFT, z->y_clear_start, SCREEN_RIGHT, z->y_clear_end,
BG_COLOR);
}
void Screen_ClearAllZones(void) {
Screen_BoxFill(SCREEN_LEFT, g_zones[0].y_clear_start, SCREEN_RIGHT,
g_zones[2].y_clear_end, BG_COLOR);
Screen_DrawZoneLines();
}
#define MAX_CHARS_PER_LINE 8
#define GBK_BYTES_PER_CHAR 2
#define MAX_LINE_BYTES (MAX_CHARS_PER_LINE * GBK_BYTES_PER_CHAR)
void Screen_ShowInZone(uint8_t zone, const uint8_t *text, uint16_t text_len) {
if (zone < 1 || zone > 3)
return;
const ScreenZone_t *z = &g_zones[zone - 1];
// 1⃣ 清空整个zone
Screen_ClearZone(zone);
Screen_DrawZoneLines();
// 2⃣ 强制裁剪长度
uint16_t len = text_len;
if (len > MAX_LINE_BYTES)
len = MAX_LINE_BYTES;
// 3⃣ 单行绘制(不允许自动换行)
Screen_DrawText16_GBK(2, z->y_text, text, len, TEXT_COLOR);
}