refactor(gbk_text): 重构GBK文本常量定义并优化命名 移除过时的文本定义,使用更简洁通用的名称替换原有特定功能的文本 常量,并更新相应的长度定义和别名映射。 BREAKING CHANGE: 原有的特定功能文本常量已被移除,需使用新的通 用名称进行访问。 ```
571 lines
15 KiB
C
571 lines
15 KiB
C
/* 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 区域划分:
|
||
// * 区域1:y坐标 29-60(高度32像素)
|
||
// * 区域2:y坐标 61-93(高度33像素)
|
||
// * 区域3:y坐标 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);
|
||
}
|