/* * 模块名称:LCD 绘图模块 * 模块功能:提供字符、字符串(中英文混合)、线条、矩形、圆形等图形绘制 * 适用平台:STM32F103C8T6 + ST7735S + 底层 st7735s.c * 作者:王建锋 * 创建日期:2026-07-14 * 修改记录: * 2026-07-14 王建锋 从 st7735s.c 拆分 */ #include "lcd.h" #include "lcd_data.h" #include "hz16_data.h" #include /* * 函数功能:UTF-8字符转Unicode(辅助) * 入口参数:str - UTF-8字符串指针(会被推进到下一字符起始) * 返回值:Unicode码点,或0(异常/结束) * 函数说明:支持1~3字节UTF-8,推进str */ static uint16_t utf8_to_unicode(const char **str) { uint16_t code; uint8_t c; c = (uint8_t)(**str); if (c == 0) { return 0; } if (c < 0x80) { code = c; (*str)++; } else if ((c & 0xE0) == 0xC0) { code = c & 0x1F; (*str)++; code = (code << 6) | ((uint8_t)(**str) & 0x3F); (*str)++; } else if ((c & 0xF0) == 0xE0) { code = c & 0x0F; (*str)++; code = (code << 6) | ((uint8_t)(**str) & 0x3F); (*str)++; code = (code << 6) | ((uint8_t)(**str) & 0x3F); (*str)++; } else { (*str)++; return 0; } return code; } /* * 函数功能:显示一个8x16 ASCII字符 * 入口参数:x - 列坐标 * y - 行坐标 * ch - 字符 (ASCII 32-126) * color - 字符颜色 (RGB565) * bg_color - 背景颜色 (RGB565) * 返回值:无 * 函数说明:框选8x16区域→遍历字模bit→发送color或bg_color */ void lcd_draw_char(uint16_t x, uint16_t y, char ch, uint16_t color, uint16_t bg_color) { uint16_t i; uint16_t j; uint8_t line; if (ch < 32 || ch > 126) { ch = ' '; } st7735s_set_window(x, y, x + 7, y + 15); HAL_GPIO_WritePin(LCD_CS_PORT, LCD_CS_PIN, GPIO_PIN_RESET); HAL_GPIO_WritePin(LCD_DC_PORT, LCD_DC_PIN, GPIO_PIN_SET); for (i = 0; i < 16; i++) { line = LCD_F8x16[ch - 32][i]; for (j = 0; j < 8; j++) { if (line & 0x80) { st7735s_spi_write_byte((uint8_t)(color >> 8)); st7735s_spi_write_byte((uint8_t)(color & 0xFF)); } else { st7735s_spi_write_byte((uint8_t)(bg_color >> 8)); st7735s_spi_write_byte((uint8_t)(bg_color & 0xFF)); } line <<= 1; } } HAL_GPIO_WritePin(LCD_CS_PORT, LCD_CS_PIN, GPIO_PIN_SET); } /* * 函数功能:显示字符串 * 入口参数:x - 列坐标 * y - 行坐标 * str - 字符串指针 * color - 字符颜色 (RGB565) * bg_color - 背景颜色 (RGB565) * 返回值:无 * 函数说明:连续显示字符串,自动换行 */ void lcd_draw_string(uint16_t x, uint16_t y, const char *str, uint16_t color, uint16_t bg_color) { uint16_t start_x = x; while (*str != '\0') { if (*str == '\n') { y += FONT_HEIGHT_16; x = start_x; str++; continue; } if (x + 8 > s_lcd_width) { y += FONT_HEIGHT_16; x = start_x; } if (y + 16 > s_lcd_height) { break; } lcd_draw_char(x, y, *str, color, bg_color); x += FONT_WIDTH_8; str++; } } /* * 函数功能:显示一个6x8 ASCII字符 * 入口参数:x - 列坐标 * y - 行坐标 * ch - 字符 (ASCII 32-126) * color - 字符颜色 (RGB565) * bg_color - 背景颜色 (RGB565) * 返回值:无 * 函数说明:LCD_F6x8为列式存储(每字节一列,bit0=顶行,bit7=底行) * 框选6x8区域→逐行逐列取bit→发送color或bg_color */ void lcd_draw_char_6x8(uint16_t x, uint16_t y, char ch, uint16_t color, uint16_t bg_color) { uint8_t row; uint8_t col; uint8_t col_data; if (ch < 32 || ch > 126) { ch = ' '; } st7735s_set_window(x, y, x + 5, y + 7); HAL_GPIO_WritePin(LCD_CS_PORT, LCD_CS_PIN, GPIO_PIN_RESET); HAL_GPIO_WritePin(LCD_DC_PORT, LCD_DC_PIN, GPIO_PIN_SET); for (row = 0; row < 8; row++) { for (col = 0; col < 6; col++) { col_data = LCD_F6x8[ch - 32][col]; if (col_data & (1 << row)) { st7735s_spi_write_byte((uint8_t)(color >> 8)); st7735s_spi_write_byte((uint8_t)(color & 0xFF)); } else { st7735s_spi_write_byte((uint8_t)(bg_color >> 8)); st7735s_spi_write_byte((uint8_t)(bg_color & 0xFF)); } } } HAL_GPIO_WritePin(LCD_CS_PORT, LCD_CS_PIN, GPIO_PIN_SET); } /* * 函数功能:显示字符串(6x8字体) * 入口参数:x - 列坐标 * y - 行坐标 * str - 字符串指针 * color - 字符颜色 (RGB565) * bg_color - 背景颜色 (RGB565) * 返回值:无 * 函数说明:连续显示6x8字符,自动换行 */ void lcd_draw_string_6x8(uint16_t x, uint16_t y, const char *str, uint16_t color, uint16_t bg_color) { uint16_t start_x = x; while (*str != '\0') { if (*str == '\n') { y += FONT_HEIGHT_8; x = start_x; str++; continue; } if (x + FONT_WIDTH_6 > s_lcd_width) { y += FONT_HEIGHT_8; x = start_x; } if (y + FONT_HEIGHT_8 > s_lcd_height) { break; } lcd_draw_char_6x8(x, y, *str, color, bg_color); x += FONT_WIDTH_6; str++; } } /* * 函数功能:显示一个16x16 CJK汉字 * 入口参数:x - 列坐标 * y - 行坐标 * unicode - Unicode码点 (如 0x4E00="一") * color - 字符颜色 (RGB565) * bg_color - 背景颜色 (RGB565) * 返回值:无 * 函数说明:框选16x16区域→遍历字模bit→发送color或bg_color */ void lcd_draw_chinese(uint16_t x, uint16_t y, uint16_t unicode, uint16_t color, uint16_t bg_color) { uint16_t i; uint16_t j; uint8_t k; uint8_t line; const uint8_t *bitmap; bitmap = cjk_get_bitmap(unicode); if (bitmap == NULL) { return; } st7735s_set_window(x, y, x + CJK_WIDTH - 1, y + CJK_HEIGHT - 1); HAL_GPIO_WritePin(LCD_CS_PORT, LCD_CS_PIN, GPIO_PIN_RESET); HAL_GPIO_WritePin(LCD_DC_PORT, LCD_DC_PIN, GPIO_PIN_SET); for (i = 0; i < CJK_HEIGHT; i++) { for (j = 0; j < CJK_WIDTH / 8; j++) { line = bitmap[i * (CJK_WIDTH / 8) + j]; for (k = 0; k < 8; k++) { if (line & 0x80) { st7735s_spi_write_byte((uint8_t)(color >> 8)); st7735s_spi_write_byte((uint8_t)(color & 0xFF)); } else { st7735s_spi_write_byte((uint8_t)(bg_color >> 8)); st7735s_spi_write_byte((uint8_t)(bg_color & 0xFF)); } line <<= 1; } } } HAL_GPIO_WritePin(LCD_CS_PORT, LCD_CS_PIN, GPIO_PIN_SET); } /* * 函数功能:显示UTF-8字符串(自动中英文混合) * 入口参数:x - 列坐标 * y - 行坐标 * str - UTF-8字符串指针 * color - 字符颜色 (RGB565) * bg_color - 背景颜色 (RGB565) * 返回值:无 * 函数说明:自动识别ASCII(8x16)和CJK(16x16),自动换行 */ void lcd_draw_chinese_string(uint16_t x, uint16_t y, const char *str, uint16_t color, uint16_t bg_color) { uint16_t start_x = x; while (*str != '\0') { uint8_t c = (uint8_t)(*str); if (*str == '\n') { y += CJK_HEIGHT; x = start_x; str++; continue; } if (c < 0x80) { if (x + 8 > s_lcd_width) { y += CJK_HEIGHT; x = start_x; } if (y + 16 > s_lcd_height) { break; } lcd_draw_char(x, y, (char)c, color, bg_color); x += 8; str++; } else { const char *p = str; uint16_t unicode = utf8_to_unicode(&p); if (unicode == 0) { str = p; continue; } if (x + CJK_WIDTH > s_lcd_width) { y += CJK_HEIGHT; x = start_x; } if (y + CJK_HEIGHT > s_lcd_height) { break; } lcd_draw_chinese(x, y, unicode, color, bg_color); x += CJK_WIDTH; str = p; } } } /* * 函数功能:画线(Bresenham算法) * 入口参数:x1 - 起始列 * y1 - 起始行 * x2 - 结束列 * y2 - 结束行 * color - 颜色 (RGB565) * 返回值:无 */ void lcd_draw_line(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color) { int16_t dx; int16_t dy; int16_t sx; int16_t sy; int16_t err; int16_t e2; int16_t x = (int16_t)x1; int16_t y = (int16_t)y1; dx = (x1 < x2) ? (int16_t)(x2 - x1) : (int16_t)(x1 - x2); dy = (y1 < y2) ? (int16_t)(y2 - y1) : (int16_t)(y1 - y2); sx = (x1 < x2) ? 1 : -1; sy = (y1 < y2) ? 1 : -1; err = dx - dy; while (1) { st7735s_draw_pixel((uint16_t)x, (uint16_t)y, color); if (x == (int16_t)x2 && y == (int16_t)y2) { break; } e2 = 2 * err; if (e2 > -dy) { err -= dy; x += sx; } if (e2 < dx) { err += dx; y += sy; } } } /* * 函数功能:画空心矩形 * 入口参数:x - 左上角列 * y - 左上角行 * width - 宽度 * height - 高度 * color - 颜色 (RGB565) * 返回值:无 */ void lcd_draw_rect(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint16_t color) { uint16_t x2 = x + width - 1; uint16_t y2 = y + height - 1; lcd_draw_line(x, y, x2, y, color); lcd_draw_line(x, y, x, y2, color); lcd_draw_line(x2, y, x2, y2, color); lcd_draw_line(x, y2, x2, y2, color); } /* * 函数功能:画空心圆(Bresenham算法) * 入口参数:x0 - 圆心列 * y0 - 圆心行 * radius - 半径 * color - 颜色 (RGB565) * 返回值:无 */ void lcd_draw_circle(uint16_t x0, uint16_t y0, uint16_t radius, uint16_t color) { int16_t x = 0; int16_t y = (int16_t)radius; int16_t err = 1 - (int16_t)radius; int16_t xc = (int16_t)x0; int16_t yc = (int16_t)y0; while (x <= y) { st7735s_draw_pixel((uint16_t)(xc + x), (uint16_t)(yc + y), color); st7735s_draw_pixel((uint16_t)(xc - x), (uint16_t)(yc + y), color); st7735s_draw_pixel((uint16_t)(xc + x), (uint16_t)(yc - y), color); st7735s_draw_pixel((uint16_t)(xc - x), (uint16_t)(yc - y), color); st7735s_draw_pixel((uint16_t)(xc + y), (uint16_t)(yc + x), color); st7735s_draw_pixel((uint16_t)(xc - y), (uint16_t)(yc + x), color); st7735s_draw_pixel((uint16_t)(xc + y), (uint16_t)(yc - x), color); st7735s_draw_pixel((uint16_t)(xc - y), (uint16_t)(yc - x), color); x++; if (err < 0) { err += 2 * x + 1; } else { y--; err += 2 * (x - y) + 1; } } }