feat(school_stm32): 添加基础的 STM32F103RCT6 项目结构
- 创建了项目的基本目录结构和文件 - 添加了 CMakeLists.txt 和 Makefile 构建配置 - 创建了 main.c 文件,实现了简单的 LED 闪烁和按键检测功能 - 集成了 SEGGER RTT 库 - 添加了 .gitignore 文件,排除了不必要的生成文件
This commit is contained in:
333
libs/ge_interface/ge_render.c
Normal file
333
libs/ge_interface/ge_render.c
Normal file
@ -0,0 +1,333 @@
|
||||
#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,
|
||||
};
|
Reference in New Issue
Block a user