【瑞萨AI挑战赛】Titan开发板MIPI LCD 显示国旗测试
瑞萨与与非组织的这次活动非常Nice。本次RT-Thread Titan Board 开发板:基于瑞萨MCU:R7KA8P1KFLCAC,高性能 1GHz Arm® Cortex®-M85和 Ethos-U55 NPU 的 AI 微控制。1MB code MRAM 以及 2MB 带 ECC 的 SRAM。开发板板载 DAP-Link 接口板载接口,40Pin 树莓派接口、MIPI DSI/CSI、CEU Camera、RGB LCD接口、2个以太网接口、4个 CAN 电机接口、2个 RS485接口、3路串口、1个 DBUS 接口、1个 USBHS/USBFS 接口、1个 RTC 电池接口。
开发板资源:
R7KA8P1KFLCAB#U 是瑞萨 RA8P1 系列旗舰级 AI 加速 MCU,堪称嵌入式领域性能与智能双巅峰的存在。它搭载 1GHz Cortex-M85 内核,CoreMark **超 7300 分,算力碾压同档**;集成 Ethos-U55 NPU,256 GOPS AI 算力可轻松驱动实时图像检测、语音识别,推理速度较纯 CPU 提升数十倍。存储配置堪称豪华:1MB 超高速 MRAM 实现零延迟写入,2MB 带 ECC 保护的 SRAM 告别内存焦虑,搭配 xSPI 接口外扩存储带宽拉满。外设覆盖视觉、语音、工业全场景,MIPI-CSI/DSI、千兆以太网 + TSN、CAN-FD 一应俱全,还支持 TrustZone-M 硬件安全隔离。台积电 22nm ULL 工艺加持,1GHz 满频运行仍兼顾低功耗,单芯片即可打通实时控制 + 边缘 AI,是智能工业、物联网终端的理想之选。
RA家族生态:
本次测评Titan Board 上使用 RA8 系列 MCU 的 GLCDC 模块结合 RT-Thread 的 LCD 驱动框架驱动 MIPI LCD 屏幕。
官方基础介绍:https://rt-thread-studio.github.io/sdk-bsp-ra8p1-titan-board/latest/driver/Titan_driver_mipi_lcd/README_zh.html
RT-Thread 的 LCD 驱动框架是一套面向嵌入式显示设备的标准化驱动抽象层,核心目标是解耦硬件操作与应用逻辑,让开发者无需关注 LCD 底层硬件细节(如 SPI/I2C 通信、寄存器配置、引脚控制等),只需调用统一的上层 API 即可实现显示功能。下面从框架设计理念、核心分层、关键抽象、使用特点四个维度,纯概念性讲解这个框架:
一、框架的核心设计理念
设备模型复用:LCD 驱动完全基于 RT-Thread 通用设备模型(rt_device)实现,LCD 屏被抽象为一个「字符设备」,可以通过 RT-Thread 标准的设备管理接口(如 rt_device_find()、rt_device_open())进行管理,和串口、I2C 等设备的使用逻辑保持一致,降低学习成本。
分层解耦:将「通用显示逻辑」和「硬件适配逻辑」分离,通用逻辑(如清屏、画点、区域填充)由框架统一实现,硬件适配逻辑(如寄存器配置、数据传输)由开发者针对具体 LCD 屏实现,大幅提升驱动的可复用性。
标准化接口:定义统一的 LCD 操作接口,无论底层是 ST7735、ILI9341、SSD1963 等不同型号的 LCD 屏,应用层调用的 API 完全一致,更换硬件时只需替换底层适配代码,无需修改应用层。
二、框架的分层结构(从上层到下层)
三、框架的关键抽象组件
3.1 LCD 设备结构体(lcd_device):这是框架对 LCD 屏的核心抽象,包含了 LCD 设备的所有关键信息:
继承通用设备结构体(rt_device),实现设备注册、查找、打开 / 关闭等通用管理;
基础属性:屏幕分辨率(宽 / 高)、像素位深(如 16bpp、24bpp)、帧缓冲区地址(显存);
3.2 操作函数集(lcd_ops):指向底层硬件适配的核心函数,是连接抽象层和硬件层的关键。
操作函数集(lcd_ops):定义了 LCD 驱动必须实现的最小化接口集合,框架通过这些接口调用硬件操作,核心函数包括:
init:LCD 硬件初始化(复位、寄存器配置、点亮屏幕);
clear:清屏操作(填充整个屏幕为指定颜色);
draw_point:画单个像素点(框架基础操作,其他复杂操作如画线、画矩形均基于此);
fill:填充指定区域(高效实现大块区域着色);
blit:图像块拷贝(用于显示图片、位图等)。
3.3帧缓冲区(Framebuffer):框架支持「帧缓冲模式」和「直接写屏模式」:
帧缓冲模式:在内存中开辟一块和屏幕分辨率匹配的区域作为显存,应用层先修改显存数据,再一次性刷新到屏幕,适合复杂图形显示,减少屏幕闪烁;
直接写屏模式:应用层调用接口时直接向屏幕写入数据,无需显存,节省内存,适合简单显示场景。
四、框架的使用特点
易用性:应用层只需通过 rt_device_find() 获取 LCD 设备句柄,再调用 rt_lcd_clear()、rt_lcd_draw_point() 等标准化 API 即可实现显示,无需关 注底层是 SPI 还是 I2C 通信。
可扩展性:支持屏幕旋转、多屏适配、不同像素格式(如 RGB565、RGB888),开发者只需在硬件适配层修改对应参数即可。
兼容性:兼容 RT-Thread 的组件生态,可直接对接 LVGL(嵌入式 GUI 库),只需将 LVGL 的显示接口指向 LCD 框架的 API,即可快速实现图形化界面。
在官方demo上设计国旗显示矢量,非图片
/*
* Copyright (c) 2006-2024, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-11-24 Rbb666 the first version
*/
#include <rtthread.h>
#include <rtdevice.h>
#include <math.h>
#ifdef BSP_USING_LCD
#ifdef SOC_SERIES_R7FA8M85
#include <ra8/lcd_config.h>
#elif defined(SOC_SERIES_R7KA8P1)
#include <lcd_port.h>
#else
#include <ra6m3/lcd_config.h>
#endif
#include <drv_lcd.h>
#include "hal_data.h"
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#define DRV_DEBUG
#define LOG_TAG "drv_lcd"
#include <drv_log.h>
#define G_LCD_CTRL g_display0_ctrl
#define G_LCD_CFG g_display0_cfg
struct drv_lcd_device
{
struct rt_device parent;
struct rt_device_graphic_info lcd_info;
};
struct drv_lcd_device _lcd;
static uint16_t screen_rotation;
static uint16_t *gp_single_buffer = NULL;
static uint16_t *gp_double_buffer = NULL;
static uint16_t *lcd_current_working_buffer = (uint16_t *) &fb_background;
#if defined(SOC_SERIES_R7FA8M85)
static uint8_t lcd_framebuffer BSP_ALIGN_VARIABLE(64) BSP_PLACE_IN_SECTION(".sdram");
#endif
// G2D
extern d2_device *d2_handle0;
static d2_device **_d2_handle_user = &d2_handle0;
static d2_renderbuffer *renderbuffer;
#if defined(SOC_SERIES_R7FA8M85) || defined(SOC_SERIES_R7KA8P1)
extern void ra8_mipi_lcd_init(void);
#endif
#if defined(SOC_SERIES_R7FA8M85) || defined(SOC_SERIES_R7KA8P1)
static struct rt_completion sync_completion;
rt_weak void DisplayVsyncCallback(display_callback_args_t *p_args)
{
rt_interrupt_enter();
if (DISPLAY_EVENT_LINE_DETECTION == p_args->event)
{
rt_completion_done(&sync_completion);
}
rt_interrupt_leave();
}
// Wait until Vsync is triggered through callback function
static void vsync_wait(void)
{
rt_completion_wait(&sync_completion, RT_WAITING_FOREVER);
}
#endif
static void turn_on_lcd_backlight(void)
{
#ifdef BSP_USING_LCD_PWM_BACKLIGHT
struct rt_device_pwm *pwm_dev;
/* turn on the LCD backlight */
pwm_dev = (struct rt_device_pwm *)rt_device_find(LCD_PWM_DEV_NAME);
/* pwm frequency:100K = 10000ns */
rt_pwm_set(pwm_dev, 0, 10000, 5000);
rt_pwm_enable(pwm_dev, 0);
#else
rt_pin_mode(LCD_BL_PIN, PIN_MODE_OUTPUT); /* LCD_BL */
rt_pin_write(LCD_BL_PIN, PIN_HIGH);
#endif
}
static void ra_bsp_lcd_clear(uint16_t color)
{
rt_memset(lcd_current_working_buffer, color, LCD_BUF_SIZE);
}
void lcd_draw_pixel(uint32_t x, uint32_t y, uint16_t color)
{
// Verify pixel is within LCD range
if ((x <= LCD_WIDTH) && (y <= LCD_HEIGHT))
{
switch (screen_rotation)
{
case ROTATION_ZERO:
{
lcd_current_working_buffer[(y * LCD_WIDTH) + x] = color;
break;
}
case ROTATION_180:
{
lcd_current_working_buffer[((LCD_HEIGHT - y) * LCD_WIDTH) + (LCD_WIDTH - x)] = color;
break;
}
default:
{
lcd_current_working_buffer[(y * LCD_WIDTH) + x] = color;
break;
}
}
}
else
{
LOG_D("draw pixel outof range:%d,%d", x, y);
}
}
void lcd_fill_array(uint16_t x_start, uint16_t y_start, uint16_t x_end, uint16_t y_end, void *pcolor)
{
uint16_t *pixel = RT_NULL;
uint16_t cycle_y, x_offset = 0;
pixel = (uint16_t *)pcolor;
for (cycle_y = y_start; cycle_y <= y_end;)
{
for (x_offset = 0; x_start + x_offset <= x_end; x_offset++)
{
lcd_draw_pixel(x_start + x_offset, cycle_y, *pixel++);
}
cycle_y++;
}
}
d2_device *d2_handle_obj_get(void)
{
return *_d2_handle_user;
}
d2_renderbuffer *d2_renderbuffer_get(void)
{
return renderbuffer;
}
void lcd_draw_jpg(int32_t x, int32_t y, const void *p, int32_t xSize, int32_t ySize)
{
uint32_t ModeSrc;
ModeSrc = d2_mode_rgb565;
// Generate render operations
d2_framebuffer(d2_handle_obj_get(), (uint16_t *)&fb_background, LCD_WIDTH, LCD_WIDTH, LCD_HEIGHT, ModeSrc);
d2_selectrenderbuffer(d2_handle_obj_get(), d2_renderbuffer_get());
d2_cliprect(d2_handle_obj_get(), 0, 0, LCD_WIDTH, LCD_HEIGHT);
d2_setblitsrc(d2_handle_obj_get(), (void *) p, xSize, xSize, ySize, ModeSrc);
d2_blitcopy(d2_handle_obj_get(), xSize, ySize, 0, 0, (d2_width)(xSize << 4), (d2_width)(ySize << 4),
(d2_point)(x << 4), (d2_point)(y << 4), 0);
// Execute render operations
d2_executerenderbuffer(d2_handle_obj_get(), d2_renderbuffer_get(), 0);
// In single-buffered mode always wait for DRW to finish before returning
d2_flushframe(d2_handle_obj_get());
}
void lcd_gpu_fill_array(size_t x1, size_t y1, size_t x2, size_t y2, uint16_t *color_data)
{
uint32_t ModeSrc;
int32_t width;
int32_t heigh;
width = (x2 - x1) + 1;
heigh = (y2 - y1) + 1;
ModeSrc = d2_mode_rgb565;
// Generate render operations
d2_framebuffer(d2_handle_obj_get(), (uint16_t *)&fb_background, LCD_WIDTH, LCD_WIDTH, LCD_HEIGHT, ModeSrc);
d2_selectrenderbuffer(d2_handle_obj_get(), d2_renderbuffer_get());
d2_cliprect(d2_handle_obj_get(), 0, 0, LCD_WIDTH, LCD_HEIGHT);
d2_setblitsrc(d2_handle_obj_get(), (void *) color_data, width, width, heigh, ModeSrc);
d2_blitcopy(d2_handle_obj_get(), width, heigh, 0, 0, (d2_width)(LCD_WIDTH << 4), (d2_width)(LCD_HEIGHT << 4),
(d2_point)(x1 << 4), (d2_point)(y1 << 4), 0);
// Execute render operations
d2_executerenderbuffer(d2_handle_obj_get(), d2_renderbuffer_get(), 0);
// In single-buffered mode always wait for DRW to finish before returning
d2_flushframe(d2_handle_obj_get());
}
void g2d_display_write_area(const void *pSrc, void *pDst, int WidthSrc, int HeightSrc, int x, int y)
{
uint32_t ModeSrc;
ModeSrc = d2_mode_rgb565;
/* Set the new buffer to the current draw buffer */
d2_framebuffer(d2_handle_obj_get(), (uint16_t *)pDst, LCD_WIDTH, LCD_WIDTH, LCD_HEIGHT, ModeSrc);
d2_selectrenderbuffer(d2_handle_obj_get(), d2_renderbuffer_get());
d2_cliprect(d2_handle_obj_get(), 0, 0, LCD_WIDTH, LCD_HEIGHT);
d2_setblitsrc(d2_handle_obj_get(), (void *) pSrc, WidthSrc, WidthSrc, HeightSrc, ModeSrc);
d2_blitcopy(d2_handle_obj_get(), WidthSrc, HeightSrc, 0, 0, (d2_width)(WidthSrc << 4), (d2_width)(HeightSrc << 4),
(d2_point)(x << 4), (d2_point)(y << 4), 0);
/* End the current display list */
d2_executerenderbuffer(d2_handle_obj_get(), d2_renderbuffer_get(), 0);
d2_flushframe(d2_handle_obj_get());
}
static int g2d_drv_hwInit(void)
{
d2_s32 d2_err;
uint32_t ModeSrc;
ModeSrc = d2_mode_rgb565;
// Initialize D/AVE 2D driver
*_d2_handle_user = d2_opendevice(0);
d2_err = d2_inithw(*_d2_handle_user, 0);
if (d2_err != D2_OK)
{
LOG_E("g2d init fail");
d2_closedevice(*_d2_handle_user);
return -RT_ERROR;
}
// Clear both buffers
d2_framebuffer(*_d2_handle_user, (uint16_t *)&fb_background, LCD_WIDTH, LCD_WIDTH,
LCD_HEIGHT, ModeSrc);
d2_clear(*_d2_handle_user, 0x000000);
// Set various D2 parameters
d2_setblendmode(*_d2_handle_user, d2_bm_alpha, d2_bm_one_minus_alpha);
d2_setalphamode(*_d2_handle_user, d2_am_constant);
d2_setalpha(*_d2_handle_user, UINT8_MAX);
d2_setantialiasing(*_d2_handle_user, 1);
d2_setlinecap(*_d2_handle_user, d2_lc_butt);
d2_setlinejoin(*_d2_handle_user, d2_lj_miter);
renderbuffer = d2_newrenderbuffer(*_d2_handle_user, 20, 20);
if (!renderbuffer)
{
LOG_E("no renderbuffer");
d2_closedevice(*_d2_handle_user);
return -RT_ERROR;
}
return RT_EOK;
}
static rt_err_t ra_lcd_control(rt_device_t device, int cmd, void *args)
{
struct drv_lcd_device *lcd = (struct drv_lcd_device *)device;
switch (cmd)
{
case RTGRAPHIC_CTRL_RECT_UPDATE:
{
#if defined(SOC_SERIES_R7FA8M85) || defined(SOC_SERIES_R7KA8P1)
struct rt_device_rect_info *info = (struct rt_device_rect_info *)args;
#if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
SCB_CleanInvalidateDCache_by_Addr((uint32_t *)lcd->lcd_info.framebuffer, sizeof(fb_background));
#endif
#if defined(ENABLE_DOUBLE_BUFFER) && ENABLE_DOUBLE_BUFFER
/* Swap the active framebuffer */
lcd_current_working_buffer = (lcd_current_working_buffer == gp_single_buffer) ? gp_double_buffer : gp_single_buffer;
#endif
g2d_display_write_area((uint8_t *)lcd->lcd_info.framebuffer, lcd_current_working_buffer,
info->width, info->height, info->x, info->y);
#if defined(ENABLE_DOUBLE_BUFFER) && ENABLE_DOUBLE_BUFFER
/* Now that the framebuffer is ready, update the GLCDC buffer pointer on the next Vsync */
fsp_err_t err = R_GLCDC_BufferChange(&G_LCD_CTRL, (uint8_t *) lcd_current_working_buffer, DISPLAY_FRAME_LAYER_1);
RT_ASSERT(err == 0);
#endif
#endif/* SOC_SERIES_R7FA8M85 || SOC_SERIES_R7KA8P1 */
#ifdef SOC_SERIES_R7FA8M85
/* wait for vsync interrupt */
vsync_wait();
#endif
}
break;
case RTGRAPHIC_CTRL_POWERON:
turn_on_lcd_backlight();
break;
case RTGRAPHIC_CTRL_POWEROFF:
rt_pin_write(LCD_BL_PIN, PIN_LOW);
break;
case RTGRAPHIC_CTRL_GET_INFO:
{
struct rt_device_graphic_info *info = (struct rt_device_graphic_info *)args;
RT_ASSERT(info != RT_NULL);
info->pixel_format= lcd->lcd_info.pixel_format;
info->bits_per_pixel = 16;
info->width = lcd->lcd_info.width;
info->height = lcd->lcd_info.height;
info->framebuffer = lcd->lcd_info.framebuffer;
}
break;
case RTGRAPHIC_CTRL_SET_MODE:
break;
}
return RT_EOK;
}
static rt_err_t drv_lcd_init(struct rt_device *device)
{
return RT_EOK;
}
static void reset_lcd_panel(void)
{
#ifdef LCD_RST_PIN
rt_pin_mode(LCD_RST_PIN, PIN_MODE_OUTPUT);
rt_pin_write(LCD_RST_PIN, PIN_LOW);
rt_thread_mdelay(100);
rt_pin_write(LCD_RST_PIN, PIN_HIGH);
rt_thread_mdelay(100);
#endif
}
static rt_err_t ra_bsp_lcd_init(void)
{
fsp_err_t error;
/* Set screen rotation to default view */
screen_rotation = ROTATION_ZERO;
/*Display driver open */
error = R_GLCDC_Open(&G_LCD_CTRL, &G_LCD_CFG);
if (FSP_SUCCESS == error)
{
#if defined(SOC_SERIES_R7FA8M85) || defined(SOC_SERIES_R7KA8P1)
#ifdef BSP_USING_MIPI_LCD
/* config mipi */
ra8_mipi_lcd_init();
#endif
#endif
/* Initialize g2d */
error = g2d_drv_hwInit();
/* Display driver start */
error = R_GLCDC_Start(&G_LCD_CTRL);
}
return error;
}
int rt_hw_lcd_init(void)
{
struct rt_device *device = &_lcd.parent;
/* memset _lcd to zero */
memset(&_lcd, 0x00, sizeof(_lcd));
/* config LCD dev info */
_lcd.lcd_info.height = LCD_HEIGHT;
_lcd.lcd_info.width = LCD_WIDTH;
_lcd.lcd_info.bits_per_pixel = LCD_BITS_PER_PIXEL;
_lcd.lcd_info.pixel_format = LCD_PIXEL_FORMAT;
#if defined(SOC_SERIES_R7FA8M85)
_lcd.lcd_info.framebuffer = (uint8_t *)lcd_framebuffer;
#elif defined(SOC_SERIES_R7KA8P1)
_lcd.lcd_info.framebuffer = (uint8_t *)&fb_background;
#endif
LOG_D("\nlcd framebuffer address:%#x", _lcd.lcd_info.framebuffer);
memset(_lcd.lcd_info.framebuffer, 0x0, LCD_BUF_SIZE);
device->type = RT_Device_Class_Graphic;
#ifdef RT_USING_DEVICE_OPS
device->ops = &lcd_ops;
#else
device->init = drv_lcd_init;
device->control = ra_lcd_control;
#endif
/* register lcd device */
rt_device_register(device, "lcd", RT_DEVICE_FLAG_RDWR);
#ifdef SOC_SERIES_R7FA8M85
rt_completion_init(&sync_completion);
#endif
/* Initialize buffer pointers */
gp_single_buffer = (uint16_t *) G_LCD_CFG.input.p_base;
/* Double buffer for drawing color bands with good quality */
gp_double_buffer = gp_single_buffer + LCD_BUF_SIZE;
reset_lcd_panel();
ra_bsp_lcd_init();
/* turn on lcdbacklight */
turn_on_lcd_backlight();
screen_rotation = ROTATION_ZERO;
return RT_EOK;
}
INIT_DEVICE_EXPORT(rt_hw_lcd_init);
#if defined(SOC_SERIES_R7FA8M85) || defined(SOC_SERIES_R7KA8P1)
rt_weak void ra8_mipi_lcd_init(void)
{
LOG_E("please Implementation function %s", __func__);
}
#endif
// 绘制直线
static void lcd_draw_line(int x0, int y0, int x1, int y1, uint16_t color)
{
int dx, dy, sx, sy, err, e2;
dx = abs(x1 - x0);
dy = abs(y1 - y0);
sx = (x0 < x1) ? 1 : -1;
sy = (y0 < y1) ? 1 : -1;
err = dx - dy;
while (1)
{
lcd_draw_pixel(x0, y0, color);
if (x0 == x1 && y0 == y1)
break;
e2 = 2 * err;
if (e2 > -dy)
{
err -= dy;
x0 += sx;
}
if (e2 < dx)
{
err += dx;
y0 += sy;
}
}
}
// 绘制水平线
static void lcd_draw_hline(int x0, int x1, int y, uint16_t color)
{
int x;
if (x0 > x1)
{
int temp = x0;
x0 = x1;
x1 = temp;
}
for (x = x0; x <= x1; x++)
{
lcd_draw_pixel(x, y, color);
}
}
// 绘制实心五角星(支持旋转)
static void lcd_draw_star_filled_rotated(int center_x, int center_y, int outer_r, int inner_r, float rotation, uint16_t color)
{
int points; // 10 个顶点坐标
int i, y;
int min_y, max_y;
// 计算五角星的 10 个顶点坐标(带旋转)
for (i = 0; i < 10; i++)
{
float angle = (i * 36 - 90) * 3.14159 / 180.0 + rotation;
int r = (i % 2 == 0) ? outer_r : inner_r;
points = center_x + (int)(r * cos(angle));
points = center_y + (int)(r * sin(angle));
}
// 计算 Y 范围
min_y = max_y = points;
for (i = 1; i < 10; i++)
{
if (points < min_y) min_y = points;
if (points > max_y) max_y = points;
}
// 使用水平扫描线填充内部
for (y = min_y; y <= max_y; y++)
{
int intersections;
int count = 0;
int j;
// 计算与所有边的交点
for (j = 0, i = 9; j < 10; i = j++)
{
int x1 = points, y1 = points;
int x2 = points, y2 = points;
if (((y1 <= y) && (y2 > y)) || ((y1 > y) && (y2 <= y)))
{
int x = x1 + (y - y1) * (x2 - x1) / (y2 - y1);
if (count < 20)
{
intersections = x;
}
}
}
// 对交点排序
for (i = 0; i < count - 1; i++)
{
for (j = i + 1; j < count; j++)
{
if (intersections > intersections)
{
int temp = intersections;
intersections = intersections;
intersections = temp;
}
}
}
// 填充奇偶对之间的区域
for (i = 0; i < count - 1; i += 2)
{
lcd_draw_hline(intersections, intersections, y, color);
}
}
}
// 绘制实心五角星
static void lcd_draw_star_filled(int center_x, int center_y, int outer_r, int inner_r, uint16_t color)
{
lcd_draw_star_filled_rotated(center_x, center_y, outer_r, inner_r, 0, color);
}
// 绘制五星红旗(旋转90度后显示,480×800 像素)
static void lcd_draw_national_flag(int start_x, int start_y, int width, int height)
{
int i;
// 红色背景 (RGB565: 0xF800)
for (i = 0; i < width * height; i++)
{
int x = start_x + i % width;
int y = start_y + i / width;
lcd_draw_pixel(x, y, 0xF800);// 红色
}
// 屏幕坐标系:原点在左上角,X 轴向右,Y 轴向下
// 480×800 像素
// 根据国旗法规定:横长与高之比为3:2,大五角星外接圆直径为旗高的1/10
// 整个旗面分为15×10的格子,每个格子为32×32像素(480×800÷15×10)
// 旋转90度后:原来的x坐标变成(height-1-y),原来的y坐标变成x
// 1. 大五角星参数(旋转90度后)
// 根据国旗标准:大星外接圆直径为旗高(480)的1/10 = 48px → 半径 24px
// 原坐标:(133.35, 120) - 左 5、上 5 格
// 旋转后:(start_x + height - 1 - 120, start_y + 133) = (start_x + 359, start_y + 133)
int big_x = start_x + 359;// height - 1 - 120 = 480 - 1 - 120 = 359
int big_y = start_y + 133;// 133
int big_outer_r = 24;// 直径48的一半
int big_inner_r = (int)(big_outer_r * 0.38);
// 绘制大星(一个角尖正向上方,但整体已旋转90度)
lcd_draw_star_filled_rotated(big_x, big_y, big_outer_r, big_inner_r, M_PI/2, 0xFFE0);// 旋转90度
// 2. 四颗小星参数
// 根据国旗标准:小星外接圆直径为旗高(480)的1/30 = 16px → 半径 8px
// 大星与小星直径比例为 48:16 = 3:1
int small_outer_r = 8;// 直径16的一半,符合国旗标准
int small_inner_r = (int)(small_outer_r * 0.38);
// 四颗小星的圆心坐标(旋转90度后,且相对于新坐标系重新计算)
// 小星 1(右上): 原坐标(266.7, 48) -> 新坐标(start_x + height - 1 - 48, start_y + 266.7) = (start_x + 431, start_y + 266)
int small1_x = start_x + 431;
int small1_y = start_y + 266;
float angle1 = atan2((float)(big_y - small1_y), (float)(big_x - small1_x)) + M_PI/2;// 加上整体旋转的90度
lcd_draw_star_filled_rotated(small1_x, small1_y, small_outer_r, small_inner_r, angle1, 0xFFE0);
// 小星 2(右中偏上): 原坐标(320, 96) -> 新坐标(start_x + height - 1 - 96, start_y + 320) = (start_x + 383, start_y + 320)
int small2_x = start_x + 383;
int small2_y = start_y + 320;
float angle2 = atan2((float)(big_y - small2_y), (float)(big_x - small2_x)) + M_PI/2;// 加上整体旋转的90度
lcd_draw_star_filled_rotated(small2_x, small2_y, small_outer_r, small_inner_r, angle2, 0xFFE0);
// 小星 3(右中偏下): 原坐标(320, 168) -> 新坐标(start_x + height - 1 - 168, start_y + 320) = (start_x + 311, start_y + 320)
int small3_x = start_x + 311;
int small3_y = start_y + 320;
float angle3 = atan2((float)(big_y - small3_y), (float)(big_x - small3_x)) + M_PI/2;// 加上整体旋转的90度
lcd_draw_star_filled_rotated(small3_x, small3_y, small_outer_r, small_inner_r, angle3, 0xFFE0);
// 小星 4(右下): 原坐标 (267, 216) -> 新坐标 (start_x + height - 1 - 216, start_y + 267) = (start_x + 263, start_y + 267)
int small4_x = start_x + 263;
int small4_y = start_y + 267;
float angle4 = atan2((float)(big_y - small4_y), (float)(big_x - small4_x)) + M_PI/2;// 加上整体旋转的 90 度
lcd_draw_star_filled_rotated(small4_x, small4_y, small_outer_r, small_inner_r, angle4, 0xFFE0);
}
// 绘制 64x64 红色五角星(实心填充)
static void lcd_draw_star_64x64(int center_x, int center_y, uint16_t color)
{
lcd_draw_star_filled(center_x, center_y, 30, 12, color);
}
int lcd_test(void)
{
struct drv_lcd_device *lcd;
struct rt_device_rect_info rect_info;
rect_info.x = 0;
rect_info.y = 0;
rect_info.width = LCD_WIDTH;
rect_info.height = LCD_HEIGHT;
lcd = (struct drv_lcd_device *)rt_device_find("lcd");
// 清屏为黑色
ra_bsp_lcd_clear(0x0000);
// 绘制五星红旗(全屏显示,符合标准比例)
LOG_D("display national flag...");
lcd_draw_national_flag(0, 0, LCD_WIDTH, LCD_HEIGHT);
// 更新显示
lcd->parent.control(&lcd->parent, RTGRAPHIC_CTRL_RECT_UPDATE, &rect_info);
// 保持显示
rt_thread_mdelay(5000);
return RT_EOK;
}
MSH_CMD_EXPORT(lcd_test, lcd test cmd);
// 简化版中文字符显示函数 - 使用线条绘制模拟汉字形状
// 由于完整汉字点阵数据庞大,这里提供框架和示例实现
// 在指定位置绘制"我"字 - 使用更清晰的几何形状模拟
static void lcd_draw_chinese_char_wo(int x, int y, uint16_t color)
{
int i;
// 绘制"我"字的简化形状 - 更接近真实字形
// 主竖线
for (i = 2; i < 30; i++) {
lcd_draw_pixel(x + 15, y + i, color);
}
// 上部横撇
for (i = 0; i < 10; i++) {
lcd_draw_pixel(x + 10 + i, y + 6, color);// 横
lcd_draw_pixel(x + 8, y + 6 + i, color); // 竖钩的一部分
}
// 撇和捺
for (i = 0; i < 8; i++) {
lcd_draw_pixel(x + 12 - i, y + 12 + i, color);// 撇
lcd_draw_pixel(x + 18 + i, y + 12 + i, color);// 捺
}
// 下部横线
for (i = 0; i < 14; i++) {
lcd_draw_pixel(x + 8 + i, y + 22, color);
}
}
// 在指定位置绘制"爱"字 - 使用更清晰的几何形状模拟
static void lcd_draw_chinese_char_ai(int x, int y, uint16_t color)
{
int i;
// 绘制"爱"字的简化形状 - 更接近真实字形
// 上部"爪"字头
for (i = 0; i < 12; i++) {
lcd_draw_pixel(x + 10 + i, y + 4, color);// 横
if (i < 6) lcd_draw_pixel(x + 8, y + 4 + i, color); // 左竖
if (i < 8) lcd_draw_pixel(x + 12, y + 4 + i, color);// 中竖
if (i < 6) lcd_draw_pixel(x + 20, y + 4 + i, color);// 右竖
}
// 中间"冖" - 帽子结构
for (i = 0; i < 18; i++) {
lcd_draw_pixel(x + 7 + i, y + 10, color);// 上横
}
for (i = 0; i < 6; i++) {
lcd_draw_pixel(x + 7, y + 10 + i, color); // 左斜
lcd_draw_pixel(x + 24, y + 10 + i, color);// 右斜
}
// 下部"友"的简化
for (i = 0; i < 14; i++) {
lcd_draw_pixel(x + 15, y + 16 + i, color);// 中竖
}
for (i = 0; i < 10; i++) {
lcd_draw_pixel(x + 10 + i, y + 20, color);// 横
}
// 右侧撇捺
for (i = 0; i < 6; i++) {
lcd_draw_pixel(x + 12 - i, y + 16 + i, color);// 撇
lcd_draw_pixel(x + 18 + i, y + 16 + i, color);// 捺
}
}
// 在指定位置绘制"你"字 - 使用更清晰的几何形状模拟
static void lcd_draw_chinese_char_ni(int x, int y, uint16_t color)
{
int i;
// 绘制"你"字的简化形状 - 更接近真实字形
// 左侧"亻" - 人旁
for (i = 0; i < 24; i++) {
lcd_draw_pixel(x + 8, y + 4 + i, color);// 竖
}
for (i = 0; i < 12; i++) {
lcd_draw_pixel(x + 6, y + 8 + i, color);// 弯钩
}
// 右侧"尔"的简化
for (i = 0; i < 16; i++) {
lcd_draw_pixel(x + 16 + i, y + 8, color);// 上横
lcd_draw_pixel(x + 16 + i, y + 20, color);// 下横
}
for (i = 0; i < 16; i++) {
lcd_draw_pixel(x + 20, y + 8 + i, color);// 中竖
}
// 点
lcd_draw_pixel(x + 22, y + 6, color);
lcd_draw_pixel(x + 22, y + 18, color);
}
// 在指定位置绘制"中"字 - 使用更清晰的几何形状模拟
static void lcd_draw_chinese_char_zhong(int x, int y, uint16_t color)
{
int i;
// 绘制"中"字的简化形状 - 方框加竖线,更精确
// 外框
for (i = 0; i < 28; i++) {
lcd_draw_pixel(x + 2, y + 4 + i, color); // 左边
lcd_draw_pixel(x + 29, y + 4 + i, color); // 右边
if (i < 26) {
lcd_draw_pixel(x + 2 + i, y + 4, color); // 上边
lcd_draw_pixel(x + 2 + i, y + 30, color); // 下边
}
}
// 中间竖线
for (i = 0; i < 22; i++) {
lcd_draw_pixel(x + 15, y + 8 + i, color);
}
// 框内横线
for (i = 0; i < 12; i++) {
lcd_draw_pixel(x + 9 + i, y + 18, color);
}
}
// 在指定位置绘制"国"字 - 使用更清晰的几何形状模拟
static void lcd_draw_chinese_char_guo(int x, int y, uint16_t color)
{
int i;
// 绘制"国"字的简化形状 - 口字框加玉字结构
// 外框
for (i = 0; i < 28; i++) {
lcd_draw_pixel(x + 2, y + 4 + i, color); // 左边
lcd_draw_pixel(x + 29, y + 4 + i, color); // 右边
if (i < 26) {
lcd_draw_pixel(x + 2 + i, y + 4, color); // 上边
lcd_draw_pixel(x + 2 + i, y + 30, color); // 下边
}
}
// 内部"玉"字结构
// 中央竖线
for (i = 0; i < 20; i++) {
lcd_draw_pixel(x + 15, y + 8 + i, color);
}
// 横线们
for (i = 0; i < 8; i++) {
lcd_draw_pixel(x + 11 + i, y + 10, color); // 上横
lcd_draw_pixel(x + 11 + i, y + 14, color); // 中上横
lcd_draw_pixel(x + 11 + i, y + 18, color); // 中下横
lcd_draw_pixel(x + 11 + i, y + 22, color); // 下横
}
// 点
lcd_draw_pixel(x + 13, y + 16, color);
}
// 显示"我爱你中国"文本
void lcd_show_chinese_love(int start_x, int start_y, uint16_t color)
{
lcd_draw_chinese_char_wo(start_x, start_y, color); // 我
lcd_draw_chinese_char_ai(start_x + 32, start_y, color); // 爱
lcd_draw_chinese_char_ni(start_x + 64, start_y, color); // 你
lcd_draw_chinese_char_zhong(start_x + 96, start_y, color); // 中
lcd_draw_chinese_char_guo(start_x + 128, start_y, color); // 国
}
// 测试中文显示的函数
int chinese_test(void)
{
struct drv_lcd_device *lcd;
struct rt_device_rect_info rect_info;
rect_info.x = 0;
rect_info.y = 0;
rect_info.width = LCD_WIDTH;
rect_info.height = LCD_HEIGHT;
lcd = (struct drv_lcd_device *)rt_device_find("lcd");
// 清屏为黑色
ra_bsp_lcd_clear(0x0000);
// 显示"我爱你中国"
lcd_show_chinese_love(100, 100, 0xFFFF);// 白色文字,居中显示
// 更新显示
lcd->parent.control(&lcd->parent, RTGRAPHIC_CTRL_RECT_UPDATE, &rect_info);
return RT_EOK;
}
MSH_CMD_EXPORT(chinese_test, chinese test cmd);
// 在国旗旁边添加中文标语的函数
int lcd_test_with_chinese(void)
{
struct drv_lcd_device *lcd;
struct rt_device_rect_info rect_info;
rect_info.x = 0;
rect_info.y = 0;
rect_info.width = LCD_WIDTH;
rect_info.height = LCD_HEIGHT;
lcd = (struct drv_lcd_device *)rt_device_find("lcd");
// 清屏为黑色
ra_bsp_lcd_clear(0x0000);
// 绘制旋转后的国旗
lcd_draw_national_flag(0, 0, LCD_WIDTH, LCD_HEIGHT);
lcd_show_chinese_love(80, LCD_HEIGHT - 60, 0xFFFF);// 白色文字,放在屏幕底部
// 更新显示
lcd->parent.control(&lcd->parent, RTGRAPHIC_CTRL_RECT_UPDATE, &rect_info);
// 保持显示
rt_thread_mdelay(5000);
return RT_EOK;
}
MSH_CMD_EXPORT(lcd_test_with_chinese, lcd test with chinese cmd);
#endif /* BSP_USING_LCD */
视频链接:https://www.bilibili.com/video/BV1qzw4ztE4m/?buvid=XY75B537C416E17A953A00A33EDB883CA28EB&is_story_h5=false&mid=Pw2Hq3t6IhfKj%2FPD%2Bao96g%3D%3D&plat_id=147&share_from=ugc&share_medium=android&share_plat=android&share_session_id=ab81534d-812f-46b1-875f-7680b2d7596c&share_source=WEIXIN&share_tag=s_i×tamp=1773500607&unique_k=oSXy132&up_id=526937168
页:
[1]