#include #include "ge_interface.h" /** * lcd_driver */ #define lcd_write_bit_reg(bit) \ do { \ (bit) ? GE_FAST_SET_ON(LCD_SDA) : GE_FAST_SET_OFF(LCD_SDA); \ GE_FAST_SET_OFF(LCD_SCL); GE_FAST_SET_ON(LCD_SCL); \ } while (0) #define lcd_write_byte(data) \ do { \ uint8_t __d = (data); \ lcd_write_bit_reg(__d & 0x80); \ lcd_write_bit_reg(__d & 0x40); \ lcd_write_bit_reg(__d & 0x20); \ lcd_write_bit_reg(__d & 0x10); \ lcd_write_bit_reg(__d & 0x08); \ lcd_write_bit_reg(__d & 0x04); \ lcd_write_bit_reg(__d & 0x02); \ lcd_write_bit_reg(__d & 0x01); \ } while (0) #define lcd_write_32bit(data) \ do { \ uint32_t __d32 = (data); \ lcd_write_byte(__d32 >> 24); \ lcd_write_byte(__d32 >> 16); \ lcd_write_byte(__d32 >> 8); \ lcd_write_byte(__d32); \ } while (0) static inline void lcd_write_raw_data(const uint8_t* data, uint32_t len) { if (!data || len == 0) return; // 32位对齐部分(高效处理) uint32_t word_count = len >> 2; // len / 4 uint32_t* word_ptr = (uint32_t*)data; while (word_count--) { lcd_write_32bit(*word_ptr++); // 使用32位写入宏 } // 处理剩余字节(非4字节对齐部分) uint8_t remain = len % 4; if (remain) { uint8_t* byte_ptr = (uint8_t*)word_ptr; for (uint8_t i = 0; i < remain; i++) { lcd_write_byte(*byte_ptr++); // 使用8位写入宏 } } } static void lcd_write_cmd(uint8_t cmd, const uint8_t* data, uint32_t len) { // write index GE_FAST_SET_OFF(LCD_CS); GE_FAST_SET_OFF(LCD_DC); lcd_write_byte(cmd); GE_FAST_SET_ON(LCD_CS); if (len == 0 || data == NULL) return; // write data GE_FAST_SET_OFF(LCD_CS); GE_FAST_SET_ON(LCD_DC); lcd_write_raw_data(data, len); GE_FAST_SET_ON(LCD_CS); } static inline void lcd_reset(void) { // LCD_RES_Pin // 重启屏幕 RES(RE Set) GE_SET_OFF(LCD_RES); HAL_Delay(100); GE_SET_ON(LCD_RES); HAL_Delay(50); } static const struct { const uint8_t cmd; const uint32_t len; const uint8_t* data; } lcd_init_cmds[] = { {0x11, 0, NULL}, //ST7735R Frame Rate {0xB1, 3, (uint8_t[]){0x01, 0x2C, 0x2D}}, {0xB2, 3, (uint8_t[]){0x01, 0x2C, 0x2D}}, {0xB3, 6, (uint8_t[]){0x01, 0x2C, 0x2D, 0x01, 0x2C, 0x2D}}, // Column inversion {0xB4, 1, (uint8_t[]){0x07}}, // ST7735R Power Sequence {0xC0, 3, (uint8_t[]){0xA2, 0x02, 0x84}}, {0xC1, 1, (uint8_t[]){0xC5}}, {0xC2, 2, (uint8_t[]){0x0A, 0x00}}, {0xC3, 2, (uint8_t[]){0x8A, 0x2A}}, {0xC4, 2, (uint8_t[]){0x8A, 0xEE}}, // VCOM {0xC5, 1, (uint8_t[]){0x0E}}, //MX, MY, RGB mode {0x36, 1, (uint8_t[]){0xC8}}, // Gamma Sequence (正极性) {0xE0, 16, (uint8_t[]){ 0x0f, 0x1a, 0x0f, 0x18, 0x2f, 0x28, 0x20, 0x22, 0x1f, 0x1b, 0x23, 0x37, 0x00, 0x07, 0x02, 0x10 }}, // Gamma Sequence (负极性) {0xE1, 16, (uint8_t[]){ 0x0f, 0x1b, 0x0f, 0x17, 0x33, 0x2c, 0x29, 0x2e, 0x30, 0x30, 0x39, 0x3f, 0x00, 0x07, 0x03, 0x10 }}, // 设置X方向地址范围 {0x2A, 4, (uint8_t[]){0x00, 0x00, 0x00, 0x7f}}, // 设置Y方向地址范围 {0x2B, 4, (uint8_t[]){0x00, 0x00, 0x00, 0x9f}}, // 测试命令 {0xF0, 1, (uint8_t[]){0x01}}, // Enable test command {0xF6, 1, (uint8_t[]){0x00}}, // Disable ram power save mode // 颜色模式 {0x3A, 1, (uint8_t[]){0x05}}, // 65k mode (16-bit RGB565) // 显示开启 {0x29, 0, NULL} // Display on }; static inline void init_lcd() { lcd_reset(); //Reset before LCD Init. //LCD Init For 1.44Inch LCD Panel with ST7735R. lcd_write_cmd(0x11, NULL, 0); //Sleep exit HAL_Delay(120); // 批量处理初始化序列 for (size_t i = 0; i < sizeof(lcd_init_cmds)/sizeof(lcd_init_cmds[0]); i++) { lcd_write_cmd(lcd_init_cmds[i].cmd, lcd_init_cmds[i].data, lcd_init_cmds[i].len); } // 开启背光 (Back Light) GE_SET_ON(LCD_BL); } static inline void lcd_set_region(int pos_x, int pos_y, int size_x, int size_y) { #define LCD_WRITE_INDEX(data) do { \ GE_FAST_SET_OFF(LCD_CS); \ GE_FAST_SET_OFF(LCD_DC); \ lcd_write_byte(data); \ GE_FAST_SET_ON(LCD_CS); \ } while(0) #define LCD_WRITE_DATA(data) do { \ GE_FAST_SET_OFF(LCD_CS); \ GE_FAST_SET_ON(LCD_DC); \ lcd_write_byte(data); \ GE_FAST_SET_ON(LCD_CS); \ } while(0) const uint8_t x_start = pos_x; const uint8_t x_end = pos_x + size_x - 1; const uint8_t y_start = pos_y; const uint8_t y_end = pos_y + size_y - 1; LCD_WRITE_INDEX(0x2a); LCD_WRITE_DATA(0x00); LCD_WRITE_DATA(x_start+2); LCD_WRITE_DATA(0x00); LCD_WRITE_DATA(x_end+2); LCD_WRITE_INDEX(0x2b); LCD_WRITE_DATA(0x00); LCD_WRITE_DATA(y_start+3); LCD_WRITE_DATA(0x00); LCD_WRITE_DATA(y_end+3); LCD_WRITE_INDEX(0x2c); } static int lcd_draw_point(ge_render_t* ctx, const ge_render_pos2_t* pos, uint16_t color) { (void)ctx; Assert(pos != NULL); lcd_set_region(pos->x, pos->y, 1, 1); GE_FAST_SET_OFF(LCD_CS); GE_FAST_SET_ON(LCD_DC); lcd_write_byte(color); lcd_write_byte(color >> 8); GE_FAST_SET_OFF(LCD_DC); return 0; } static int lcd_draw_rect(ge_render_t* ctx, const ge_render_rect_t* rect, uint16_t color) { (void)ctx; const int pos_x = rect ? rect->pos.x : 0; const int pos_y = rect ? rect->pos.y : 0; const int size_x = rect ? rect->size.x : ctx->screen_size.x; const int size_y = rect ? rect->size.y : ctx->screen_size.y; const int len = size_x * size_y; lcd_set_region(pos_x, pos_y, size_x, size_y); // MUST SET GE_FAST_SET_OFF(LCD_CS); // MUST SET GE_FAST_SET_ON(LCD_DC); for (int i = 0; i < len; i++) { lcd_write_byte(color >> 8); lcd_write_byte(color); } GE_FAST_SET_ON(LCD_CS); return 0; } /** * 隔行扫描(Interlaced Rendering) 或半帧缓冲(Half-Frame Buffer) * 借鉴NES古早游戏的绘制优化策略减少闪屏和闪烁 * 重新设计绘制接口 */ #define SCREEN_HEIGHT 128 #define SCREEN_WIDTH 128 typedef enum { GE_RENDER_EVEN_LINES, // 渲染偶数数行 GE_RENDER_ODD_LINES, // 渲染奇数数行 } ge_render_field_t; static struct ge_content { uint16_t head_canary; uint16_t screen_buffer[SCREEN_HEIGHT / 2][SCREEN_WIDTH]; uint16_t tail_canary; } ge_content; static ge_render_field_t current_field = GE_RENDER_EVEN_LINES; #define CTX_BUF(ctx) (((struct ge_content*)ctx->context)->screen_buffer) #define CTX_BUF_PTR(ctx, x, y) ((uint16_t*)CTX_BUF(ctx) + SCREEN_WIDTH * (y) + (x)) #define HEAD_CANARY 0xDEAD #define TAIL_CANARY 0xBEEF static int init(ge_render_t* ctx, const ge_render_pos2_t* init_screen_size) { (void)init_screen_size; init_lcd(); /** * set canary */ ((struct ge_content*)ctx->context)->head_canary = HEAD_CANARY; ((struct ge_content*)ctx->context)->tail_canary = TAIL_CANARY; return 0; } static int flush(ge_render_t* ctx) { AssertFmt(((struct ge_content*)ctx->context)->head_canary == HEAD_CANARY, "Invalid head canary"); AssertFmt(((struct ge_content*)ctx->context)->tail_canary == TAIL_CANARY, "Invalid tail canary"); uint16_t* buffer = ((struct ge_content*)ctx->context)->screen_buffer; const int start_y = (current_field == GE_RENDER_EVEN_LINES) ? 0 : 1; for (int buffer_y = 0; buffer_y < ctx->screen_size.y / 2; buffer_y++) { const int screen_y = start_y + buffer_y * 2; // 设置当前行区域 (1行高) lcd_set_region(0, screen_y, ctx->screen_size.y, 1); GE_FAST_SET_OFF(LCD_CS); GE_FAST_SET_ON(LCD_DC); // 传输整行数据 uint16_t* line_ptr = buffer + buffer_y * ctx->screen_size.x; for (int x = 0; x < ctx->screen_size.x; x++) { lcd_write_byte(*line_ptr >> 8); // 高字节 lcd_write_byte(*line_ptr & 0xFF); // 低字节 line_ptr++; } GE_FAST_SET_ON(LCD_CS); } // 切换模式 current_field = (current_field == GE_RENDER_EVEN_LINES) ? GE_RENDER_ODD_LINES : GE_RENDER_EVEN_LINES; return 0; } static inline int check_need_flush(ge_render_t* ctx, const ge_render_pos2_t* pos) { const int isOdd = pos->y % 2; const int isNeedOdd = (current_field == GE_RENDER_ODD_LINES) ? 1 : 0; return isOdd == isNeedOdd; } static inline int should_render_line(int y) { return (y % 2) == (current_field == GE_RENDER_ODD_LINES); } static int buf_draw_rect(ge_render_t* ctx, const ge_render_rect_t* rect, uint16_t color) { Assert(ctx != NULL && rect != NULL); // 只更新属于当前场的行 for (int y = rect->pos.y; y < rect->pos.y + rect->size.y; y++) { if (!should_render_line(y)) { continue; } for (int x = rect->pos.x; x < rect->pos.x + rect->size.x; x++) { *CTX_BUF_PTR(ctx, x, y / 2) = color; } } } ge_render_t ge_render = { .context = &ge_content, .screen_size = { .x = 128, .y = 128, }, .init_func = init, .func_flush = flush, .func_draw_point = (ge_render_draw_point_func_t)lcd_draw_point, .func_draw_rect = (ge_render_draw_rect_func_t)buf_draw_rect, .func_draw_text = 0, .func_draw_res = 0, };