第三十章 RGB显示屏实验
对于那些阅读过MicroPython手册的读者,他们可能已经注意到,该源码库并未提供针对ESP32 RGB显示屏的API接口。为了在MicroPython环境下实现对RGB显示屏的驱动,作者编写了RGB显示屏的C模块扩展组件。该扩展组件的RGB驱动实现代码是参考ESP-IDF源码库来实现的。在本章中,我们将探讨如何在MicroPython环境下驱动RGB显示屏,并实现字符串显示以及颜色的刷新。
本章分为如下几个小节:
30.1 RGB C模块解析
30.2 硬件设计
30.3 软件设计
30.4 下载验证
30.1 RGB C模块解析
RGB屏相关内容,可参考正点原子提供的《ATK-MD0430R模块用户手册_V1.0》和《ATK-MD0430R模块使用说明_V1.0》,在这两个手册中,已经详细说明了RGB的工作原理及相关参数信息。
30.1.1 C模块解析
本章的RGB C模块组件可在资料A盘à6,软件资料→1,软件→2,MicroPython开发工具→01-Windows→2,正点原子MicroPython驱动→CModules_Lib→RGBLCD目录下找到。下面作者将简要介绍正点原子RGB C模块驱动。这个讲解内容会分为几个部分:RGB构造函数、读取面板ID和RGB初始化。
1,RGB构造函数
mp_obj_t ltdc_make_new(const mp_obj_type_t *type,size_t n_args,size_t n_kw, const mp_obj_t *all_args ) { /* 创建对象的参数 */ enum { ARG_dir, }; /* 创建对象参数的默认值 */ static const mp_arg_t allowed_args[] = { { MP_QSTR_dir, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); /* 创建对象 */ ltdc_self = m_new_obj(_ltdc_dev); ltdc_self->base.type = <dc_type; /* 设置对象参数 */ ltdc_self->dir = args[ARG_dir].u_int; if (ltdc_self->dir != 1 && ltdc_self->dir != 0) { mp_raise_ValueError(MP_ERROR_TEXT("Not horizontal and vertical screens")); } /* 初始化ltdc */ ltdc_init(); /* 返回RGB对象 */ return MP_OBJ_FROM_PTR(ltdc_self); }
在RGB类的构造函数中,我们只需要传入一个参数即可创建RGB对象。这个参数用于设置RGB屏幕的方向。当dir参数的值为0时,屏幕方向为竖屏模式;否则,屏幕方向为横屏模式。最后,我们需要初始化LCD,并返回创建的RGB对象。
2,读取面板ID函数
读取面板ID是为了兼容多款RGB显示屏,设计代码如下所示:
/** * @brief LTDC读取面板ID * @note 利用LCD RGB线的最高位(R7,G7,B7)来识别面板ID * PG6 = R7(M0); PI2 = G7(M1); PI7 = B7(M2); * M2:M1:M0 * 0 :0 :0 4.3 寸480*272 RGB屏,ID = 0X4342 * 1 :0 :0 4.3 寸800*480 RGB屏,ID = 0X4384 * @param 无 * @retval 0, 非法; * 其他, LCD ID */ uint16_t ltdc_panelid_read(void) { uint8_t idx = 0; gpio_config_t gpio_init_struct; gpio_init_struct.intr_type = GPIO_INTR_DISABLE; /* 失能引脚中断 */ gpio_init_struct.mode = GPIO_MODE_INPUT; /* 输入输出模式 */ gpio_init_struct.pull_up_en = GPIO_PULLUP_ENABLE; /* 使能上拉 */ gpio_init_struct.pull_down_en = GPIO_PULLDOWN_DISABLE; /* 失能下拉 */ gpio_init_struct.pin_bit_mask = 1ull << GPIO_NUM_6; gpio_config(&gpio_init_struct); /* 配置GPIO */ gpio_init_struct.pin_bit_mask = 1ull << GPIO_NUM_14 || 1ull << GPIO_NUM_18; gpio_config(&gpio_init_struct); /* 配置GPIO */ idx = (uint8_t)gpio_get_level(GPIO_NUM_14); /* 读取M0 */ idx|= (uint8_t)gpio_get_level(GPIO_NUM_18) << 1; /* 读取M1 */ idx|= (uint8_t)gpio_get_level(GPIO_NUM_6) << 2; /* 读取M2 */ switch (idx) { case 0 : return 0X4342; /* 4.3寸屏, 480*272分辨率 */ case 4 : return 0X4384; /* 4.3寸屏, 800*480分辨率 */ default : return 0; } }
上述源代码通过读取M0~M2的电平来确定面板的ID,并据此执行相应的代码。由此可见,正点原子ESP32-S3开发板仅支持两种RGB屏幕,它们的ID分别为0x4342和0x4384。
3,RGB初始化函数
/** * @brief 初始化ltdc * @param 无 * @retval 无 */ void ltdc_init(void) { panel_handle = NULL; /* 初始化XL9555 */ ltdc_self->id = ltdc_panelid_read(); /* 读取LCD面板ID */ if (ltdc_self->id == 0X4342) /* 4.3寸屏, 480*272 RGB屏 */ { ltdc_self->pwidth = 480; /* 面板宽度,单位:像素 */ ltdc_self->pheight = 272; /* 面板高度,单位:像素 */ ltdc_self->hsw = 1; /* 水平同步宽度 */ ltdc_self->vsw = 1; /* 垂直同步宽度 */ ltdc_self->hbp = 40; /* 水平后廊 */ ltdc_self->vbp = 8; /* 垂直后廊 */ ltdc_self->hfp = 5; /* 水平前廊 */ ltdc_self->vfp = 8; /* 垂直前廊 */ ltdc_self->pclk_hz = 9 * 1000 * 1000; /* 设置像素时钟 9Mhz */ } else if (ltdc_self->id == 0X4384) { ltdc_self->pwidth = 800; /* 面板宽度,单位:像素 */ ltdc_self->pheight = 480; /* 面板高度,单位:像素 */ ltdc_self->hbp = 88; /* 水平后廊 */ ltdc_self->hfp = 40; /* 水平前廊 */ ltdc_self->hsw = 48; /* 水平同步宽度 */ ltdc_self->vbp = 32; /* 垂直后廊 */ ltdc_self->vfp = 13; /* 垂直前廊 */ ltdc_self->vsw = 3; /* 垂直同步宽度 */ ltdc_self->pclk_hz = 15 * 1000 * 1000; /* 设置像素时钟 15Mhz */ } /* 配置RGB参数 */ esp_lcd_rgb_panel_config_t panel_config = { .data_width = 16, /* RGB565处于并行模式,因此宽度为16位 */ .psram_trans_align = 64, .clk_src = LCD_CLK_SRC_PLL160M, .disp_gpio_num = GPIO_NUM_NC, .pclk_gpio_num = GPIO_LCD_PCLK, .de_gpio_num = GPIO_LCD_DE, .data_gpio_nums = { GPIO_LCD_B3, GPIO_LCD_B4, GPIO_LCD_B5, GPIO_LCD_B6, GPIO_LCD_B7, GPIO_LCD_G2, GPIO_LCD_G3, GPIO_LCD_G4, GPIO_LCD_G5, GPIO_LCD_G6, GPIO_LCD_G7, GPIO_LCD_R3, GPIO_LCD_R4, GPIO_LCD_R5, GPIO_LCD_R6, GPIO_LCD_R7, }, .timings = { .pclk_hz = ltdc_self->pclk_hz, /* 像素时钟频率 */ .h_res = ltdc_self->pwidth, /* 水平分辨率,即一行中的像素数 */ .v_res = ltdc_self->pheight, /* 垂直分辨率,即帧中的行数 */ /* 水平后廊,hsync和行活动数据开始之间的PCLK数 */ .hsync_back_porch = ltdc_self->hbp, /* 水平前廊,活动数据结束和下一个hsync之间的PCLK数 */ .hsync_front_porch = ltdc_self->hfp, .hsync_pulse_width = ltdc_self->vsw, /* 垂直同步宽度,单位:行数 */ /* 垂直后廊,vsync和帧开始之间的无效行数 */ .vsync_back_porch = ltdc_self->vbp, /* 垂直前廊,帧结束和下一个vsync之间的无效行数 */ .vsync_front_porch = ltdc_self->vfp, .vsync_pulse_width = ltdc_self->hsw,/* 水平同步宽度,单位:PCLK周期 */ .flags.pclk_active_neg = true, /* RGB数据在下降沿计时 */ }, .flags.fb_in_psram = true, /* 在PSRAM中分配帧缓冲区 */ }; /* 创建RGB对象 */ ESP_ERROR_CHECK(esp_lcd_new_rgb_panel(&panel_config, &panel_handle)); /* 复位RGB屏 */ ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle)); /* 初始化RGB */ ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle)); ltdc_display_dir(ltdc_self->dir); /* 清除屏幕为颜色 */ ltdc_clear(WHITE); }
从上述源代码中可以得知,我们首先获取面板的ID号,然后根据该ID号来设置RGB相关的参数,如时钟频率、水平同步宽度等。接着,我们调用ESP-IDF源码库中的RGB API函数来初始化RGB显示屏。最后,复位显示屏并设置屏幕的方向。
30.1.2 C模块构造与类的方法
1,RGBLCD类的构造方法
RGBLCD的构造对象方法如下:
class atk_ltdc.init(dir) 使用示例:ltdc = atk_ltdc.init(dir)
该构造函数的参数描述,如下表所示。
表30.1.2.1 atk_ltdc.init构造函数参数描述
返回值: RGBLCD对象。
2, RGBLCD类的方法
①:清屏方法
其函数原型如下:
ltdc.clear(color)
该方法的参数描述,如下表所示。
表30.1.2.2 ltdc.clear方法参数描述
返回值:无。
②:画点
其方法原型如下:
ltdc.pixel(x,y,color)
该方法的参数描述,如下表所示。
表30.1.2.3 ltdc.pixel方法参数描述
返回值:无。
③:画一个矩形
其方法原型如下:
ltdc.line(x1,y1,x2,y2,color)
该方法的参数描述,如下表所示。
表30.1.2.4 ltdc.line方法参数描述
返回值:无。
④:画一个矩形
其方法原型如下:
ltdc.rectangle(x0,y0,x1,y1,color)
该方法的参数描述,如下表所示。
表30.1.2.5 ltdc.rectangle方法参数描述
⑤:画圆
其方法原型如下:
ltdc.circle(x0,y0,r,color)
该方法的参数描述,如下表所示。
表30.1.2.6 ltdc.circle方法参数描述
返回值:无。
⑥:显示字符
其方法原型如下:
ltdc.char(x,y,chr,size,mode,color)
该方法的参数描述,如下表所示。
表30.1.2.7 ltdc.char方法参数描述
返回值:无。
⑦:显示len个数字
其方法原型如下:
ltdc.num(x,y,num,len,size,color)
该方法的参数描述,如下表所示。
表30.1.2.8 ltdc.num方法参数描述
⑧:显示字符串
其方法原型如下:
ltdc.string(x,y,width,heught,size,p,color)
该方法的参数描述,如下表所示。
表30.1.2.9 ltdc.string方法参数描述
返回值:无。
⑨:填充区域
其方法原型如下:
ltdc.fill(sx,sy,ex,ey,color)
该方法的参数描述,如下表所示。
表30.1.2.10 ltdc.fill方法参数描述
返回值:无。
⑩,RGB888转RGB565。
其方法原型如下:
atk_ltdc.color565(r,g,b)
该方法的参数描述,如下表所示。
表30.1.2.11 atk_ltdc.color565方法参数描述
返回值:RGB565颜色数值。
温馨提示:调用RGB屏对象的方法之前,必须先导入atk_ltdc模块,后调用此对象的方法,如下是导入模块示例:
import atk_ltdc as ltdc
30.2 硬件设计
1. 例程功能
本章实验功能简介:本实验利用ESP32-S3开发板的RGB接口来驱动RGB屏,RGBLCD模块的接口在核心板上,通过40P的FPC排线连接RGBLCD模块,实现RGBLCD模块的驱动和显示,下载成功后,按下复位之后,就可以看到RGBLCD模块不停的显示一些信息并不断切换底色。
2. 硬件资源
1)LED灯
LED-IO1
2)XL9555
IIC_INT-IO0(需在P5连接IO0)
IIC_SDA-IO41
IIC_SCL-IO42
3)RGBLCD
LCD_BL-IO1_3(XL9555)
LCD_DE-IO4
LCD_VSYNC-NC
LCD_HSYNC-NC
LCD_PCLK-IO5
LCD_R3-IO45
LCD_R4-IO48
LCD_R5-IO47
LCD_R6-IO21
LCD_R7-IO14
LCD_G2-IO10
LCD_G3-IO9
LCD_G4-IO46
LCD_G5-IO3
LCD_G6-IO8
LCD_G7-IO18
LCD_B3-IO17
LCD_B4-IO16
LCD_B5-IO15
LCD_B6-IO7
LCD_B7-IO6
3. 原理图
RGB接口与ESP32-S3的连接关系,如下图所示:
图30.2.1 RGB接口与ESP32-S3的连接电路图
30.3 软件设计
30.3.1 程序流程图
程序流程图能帮助我们更好的理解一个工程的功能和实现的过程,对学习和设计工程有很好的主导作用。下面看看本实验的程序流程图。
图30.3.1.1 程序流程图
30.3.2 程序解析
本书籍的代码都在main.py脚本下编写的,读者可在光盘资料下找到对应的源码。RGB实验main.py源码如下:
from machine import Pin,I2C import atk_xl9555 as io_ex import atk_ltdc as ltdc import time """ * @brief 程序入口 * @param 无 * @retval 无 """ if __name__ == '__main__': x = 0 # 初始化LED并输出高电平 led = Pin(1,Pin.OUT,value = 1) # IIC初始化 i2c0 = I2C(0, scl = Pin(42), sda = Pin(41), freq = 400000) # XL9555初始化 xl9555 = io_ex.init(i2c0) # 初始化RGB display = ltdc.init(dir = 1) # 打开RGB屏背光 xl9555.write_bit(io_ex.LCD_BL,1) time.sleep_ms(100) while True: #创建字典 seasondict = { 0: ltdc.BLACK, 1: ltdc.BLUE, 2: ltdc.RED, 3: ltdc.GREEN, 4: ltdc.CYAN, 5: ltdc.MAGENTA, 6: ltdc.YELLOW} #刷新颜色 display.clear(seasondict[x]) #显示字体 display.string(0, 5, 240, 32, 32, "ESP32S3",ltdc.RED) display.string(0, 34, 240, 16, 16, "RGB Test",ltdc.RED) display.string(0, 50, 240, 16, 16, "ATOM@ALIENTEK",ltdc.RED) x += 1 if x == 7: x = 0 led_state = led.value() led.value(not led_state) time.sleep(1)
该示例首先实例化IIC、XL9555和RGB对象,并打开RGB背光。随后,系统不停地切换背景颜色,每1秒切换一次。同时,LED也会不停地闪烁,以指示程序正在运行。
30.4 下载验证
程序下载到开发板后,可以看到LED不停的闪烁,提示程序已经在运行了。同时,可以看到RGBLCD模块的显示如下图所示:
图30.4.1 RGBLCD显示效果图
我们可以看到屏幕的背景是不停切换的,同时 LED不停的闪烁,证明我们的代码被正确的执行了,达到了我们预期的目的。