Files

404 lines
11 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
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.
/*
* 模块名称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 <string.h>
/*
* 函数功能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;
}
}
}