/* 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 #include /* 发送缓冲 */ 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); }