Files
school_stm32_lab/libs/ge_interface/ge_render.c
ZZY 52097a36ee feat(school_stm32): 添加基础的 STM32F103RCT6 项目结构
- 创建了项目的基本目录结构和文件
- 添加了 CMakeLists.txt 和 Makefile 构建配置
- 创建了 main.c 文件,实现了简单的 LED 闪烁和按键检测功能
- 集成了 SEGGER RTT 库
- 添加了 .gitignore 文件,排除了不必要的生成文件
2025-06-27 23:09:57 +08:00

334 lines
9.3 KiB
C
Raw 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.

#include <main.h>
#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 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,
};