- 创建了项目的基本目录结构和文件 - 添加了 CMakeLists.txt 和 Makefile 构建配置 - 创建了 main.c 文件,实现了简单的 LED 闪烁和按键检测功能 - 集成了 SEGGER RTT 库 - 添加了 .gitignore 文件,排除了不必要的生成文件
334 lines
9.3 KiB
C
334 lines
9.3 KiB
C
#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,
|
||
};
|