OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 35|回复: 0

《ESP32-P4开发指南— V1.0》第二十一章 RGBLCD实验

[复制链接]

1224

主题

1238

帖子

2

精华

超级版主

Rank: 8Rank: 8

积分
5268
金钱
5268
注册时间
2019-5-8
在线时间
1340 小时
发表于 前天 09:56 | 显示全部楼层 |阅读模式
第二十一章 RGBLCD实验

1)实验平台:正点原子DNESP32P4开发板

2)章节摘自【正点原子】ESP32-P4开发指南— V1.0

3)购买链接:https://detail.tmall.com/item.htm?id=873309579825

4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/esp32/ATK-DNESP32P4.html

5)正点原子官方B站:https://space.bilibili.com/394620890

6)正点原子DNESP32S3开发板技术交流群:132780729


2.jpg

3.png

本章将继续学习ESP_IDF的LCD外设驱动,主要学习RGB接口屏幕的驱动方法。本章用到的是ESP32-P4的 Camera-LCD控制器驱动RGBLCD屏幕,实现和RGBLCD屏之间的通信,实现ASCII字符、图形和彩色的显示。
本章分为如下几个小节:
21.1 RGBLCD和ESP32-P4的LCD模块介绍
21.2 硬件设计
21.3 程序设计
21.4 下载验证


21.1 RGBLCD和ESP32-P4的LCD模块介绍

21.1.1 RGBLCD简介
在第20章,我们已经介绍过TFTLCD液晶屏了,实际上RGBLCD也是TFTLCD,只是接口不同而已。接下来我们简单介绍一下RGBLCD的驱动。
(1)RGBLCD的信号线
RGBLCD的信号线如表21.1.1.1所示:

1.png
表21.1.1.1 RGBLCD信号线

一般RGB屏都有如表21.1.1.1所示的信号线,有24根颜色数据线(RGB各8根,即RGB888格式),这样可以表示最多1600W色,DE、VS、HS和CLK,用于控制数据传输。
像素同步时钟信号线LCD_CLK:液晶屏与外部使用同步通讯方式,以CLK信号作为同步时钟,在同步时钟的驱动下,每个时钟传输一个像素点数据。
水平同步信号线LCD_HSYNC:有时也被称为行同步信号,用于表示液晶屏一行像素数据的传输结束,每传输完成液晶屏的一行像素数据时,LCD_HSYNC发生电平跳变,如分辨率为800x480的显示屏(800列,480行),传输一帧的图像LCD_HSYNC的电平会跳变480次。
垂直同步信号线LCD_VSYNC:有时也被称为场同步信号,用于表示液晶屏一帧像素数据的传输结束,每传输完成一帧像素时,LCD_VSYNC会发生电平跳变。其中“帧”是图像的单位,一幅图像称为一帧,在液晶屏中,一帧指一个完整屏液晶像素点。
数据使能信号线DE:用于表示数据的有效性,当DE信号线为高电平时,RGB信号线表示的数据有效。
RGB数据线:用来传输颜色数据。
(2)RGBLCD的驱动模式
RGB屏一般有2种驱动模式:DE模式和HV模式。DE模式使用DE信号来确定有效数据(DE为高/低时,数据有效),而HV模式,则需要行同步信号HSYNC和场同步信号VSYNC,来表示扫描的行和列。
DE模式和HV模式的行扫描时序图(以800*480的LCD面板为例),如图21.1.1.1所示:


第二十一章 RGBLCD实验1179.png
图21.1.1.1 DE/HV模式行扫描时序图

从图中可以看出,DE和HV模式,时序基本一样,DE模式需要提供DE信号(DEN),而HV模式,则无需DE信号。图中的HSD即HSYNC信号,用于行同步,注意:在DE模式下面,是可以不用HSYNC信号和VSYNC信号,即可以不接,液晶照样可以正常工作。在引脚不是太充足的情况下,可以选择使用DE模式。
图中的thpw为水平同步有效信号脉宽,用于表示一行数据的开始;thb为水平后廊,表示从水平有效信号开始,到有效数据输出之间的像素时钟个数;thfp为水平前廊,表示一行数据结束后,到下一个水平同步信号开始之前的像素时钟个数。
图21.1.1.1仅是一行数据的扫描,输出800个像素点数据,而液晶面板总共有480行,这就还需要一个垂直扫描时序图,如图21.1.1.2所示:


第二十一章 RGBLCD实验1540.png
图21.1.1.2 垂直扫描时序图

图中的VSD就是垂直同步信号LCD_VSYNC,HSD就是水平同步信号LCD_HSYNC,DE为数据使能信号。如图可知,一个垂直扫描,刚好就是480个有效的DE脉冲信号,每一个DE时钟周期,扫描一行,总共扫描480行,完成一帧数据的显示。这就是800*480的LCD面板扫描时序,其他分辨率的LCD面板,时序类似。
图中的tvpw为垂直同步有效信号脉宽,用于表示一帧数据的开始;tvb为垂直后廊,表示垂直同步信号以后的无效行数,tvfp为垂直前廊,表示一帧数据输出结束后,到下一个垂直同步信号开始之前的无效行数;这几个时间在配置RGBLCD设备时序时,需要进行设置。
(3)正点原子 RGBLCD模块
正点原子目前提供五款 RGBLCD 模块:ATK-MD0430R-480272(4.3寸,480*272)、ATK-MD0430R-800480(4.3寸,800*480) ATK-MD0700R-800480(7寸,800*480)、 ATK-MD0700R-1024600(7 寸,1024*600)和ATK-MD1010R(10.1 寸,1280*800),这里我们以 ATK-MD0430R-800480为例,给大家介绍。该模块的接口原理图如图21.1.1.3 所示:


第二十一章 RGBLCD实验2100.png
图21.1.1.3 RGBLCD模块对外接口原理图

图中 J1就是对外接口,是一个 40PIN 的 FPC 座( 0.5mm 间距),通过 FPC 线,可以连接到ESP32-P4开发板底板的RGB接口上面,从而实现和ESP32-P4的连接。该模块的接口十分完善,采用 RGB888 格式,并支持 DE&HV 模式,还支持触摸屏(电阻/电容)和背光控制。右侧的几个电阻,并不是都焊接的,而是可以用户自己选择。LCD_R7/G7/B7是用来设置RGBLCD的ID的, 由于 RGBLCD 没有读写寄存器,也就没有所谓的 ID, 所以在这里我们通过在模块上面,控制 R7/G7/B7 的上/下拉,来自定义 RGBLCD 模块的 ID,帮助主控芯片去判断当前 LCD 面板的分辨率和相关参数,以提高程序兼容性。这几个位的设置关系如表21.1.1.2 所示:

2.png
表21.1.1.2正点原子 RGBLCD模块ID对应关系

这样我们在程序里面,读取 LCD_R7/G7/B7,得到 M0:M2 的值,从而判断 RGBLCD 模块的型号,并执行不同的配置,即可实现不同 LCD模块的兼容。
更详细的RGBLCD相关内容,可以参考正点原子提供的《ATK-MD0430R模块用户手册_V1.0》和《ATK-MD0430R模块使用说明_V1.0》,在这两个手册中,会有LCD相关参数信息。
这里还要说明一下,市面上有一些RGBLCD屏幕,是带有三线SPI接口,需要通过SPI接口对其进行配置操作,才能让屏幕正常显示。而正点原子的RGBLCD屏幕是不需要通过SPI接口配置的,使用起来更加方便。

21.1.2 ESP32-P4的LCD模块简介
ESP32-P4的LCD_CAM控制器包括独立的LCD控制模块和Camera(摄像头)控制模块。其中LCD模块用于发送并行视频数据信号,其总线支持RGB、MOTO6800和I8080等接口时序。Camera模块用于接收并行视频数据信号,其总线支持DVP 8/16位并行输入模式。
本小节主要介绍LCD_CAM控制器的LCD模块-RGB接口部分内容。
首先我们通过分析LCD_CAM控制器的结构框图,了解一下其工作过程。结构框图如下图所示。

第二十一章 RGBLCD实验3250.png
图21.1.2.1 LCD_CAM控制器结构框图

LCD控制模块(红色虚线以下)包含发送控制单元(LCD_Ctrl)、发送异步FIFO(Async TX FIFO)、LCD时钟生成模块(LCD_Clock Generator)和格式转换模块(RGB/YUV Converter)。
发送控制单元:用于控制LCD数据的发送。LCD数据从上图可知,可由GDMA取自内部或外部存储器。
发送异步FIFO:用于与外部设备进行交互。
LCD时钟生成模块:用于生成LCD_PCLK时钟。时钟源经过时钟生成模块处理后,生成LCD模块所需要的时钟LCD_PCLK,这一过程如下图所示。


第二十一章 RGBLCD实验3537.png
图21.1.2.2 LCD模块时钟生成过程

LCD模块时钟由三个时钟源提供,分别是XTAL_CLK、PLL_F160M_CLK和APLL_CLK。可由HP_SYS_CLKRST_PERI_CLK_CTRL19_REG的HP_SYS_CLKRST_LCD_CLK_SRC_SEL位决定,0选择使用XTAL_CLK,1选择使用PLL_F160M_CLK,2选择使用APLL_CLK。        
选择好时钟源后(即得到LCD_CLK_SRC),经过降频得到LCD_CLK,后面再经过分频最终得到LCD_PCLK,这三者的关系如下:


第二十一章 RGBLCD实验3800.png

Note:
① 第一条公式中的N、b和a都是通过HP_SYS_CLKRST_PERI_CLK_CTRL110_REG寄存器进行配置的。N的取值范围为2≤N≤256,通过HP_SYS_CLKRST_LCD_CLK_DIV_NUM位进行配置。b是通过HP_SYS_CLKRST_LCD_CLK_DIV_NUMERATOR位进行配置,而a是通过HP_SYS_CLKRST_LCD_CLK_DIV_DENONMINATOR位进行配置。
② 第二条公式中的MO是通过LCD_CAM_LCD_CLOCK_REG寄存器进行配置的。MO的数值主要是由LCD_CAM_LCD_CLK_EQU_SYSCLK和LCD_CAM_LCD_CLKCNT_N去决定,当LCD_CAM_LCD_CLK_EQU_SYSCLK为1,MO为1;当LCD_CAM_LCD_CLK_EQU_SYSCLK为0时,MO为LCD_CAM_LCD_CLKCNT_N+1。
格式转换模块:用于各种格式的视频数据互相转换。在上图中,该部分是虚线框,也就是可不使用该模块。
Camera-LCD控制器的CAM和LCD接口都可通过GPIO交换矩阵灵活配置使用任意GPIO引脚。对于LCD模块的信号,即图21.1.2.1右下角部分,在这里也简单介绍一下,如下表所示。


3.png
表21.1.2.1 LCD模块信号描述

使用不同接口,使用的数据线会有差异。通过RGB接口去驱动正点原子的RGBLCD屏幕,没有用到LCD_CD和LCD_CS引脚,且数据总线引脚只用到16根(RGB565格式)。
LCD模块还支持数据格式控制,比如:
① 反转数据位顺序,LCD_DATA_out[x:0]变为LCD_DATA_out[0:x],即MSB与LSB切换。
② 反转数据字节顺序,在16bit模式下有效,{Byte1,Byte0}变为{Byte0,Byte1}
③ 两个字节反转位置,在8bit模式下有效,{Byte1}{Byte0}变为{Byte0}{Byte1}
假如一款LCD模块RGB565像素数据是先发送低位后发送高位,若硬件不支持反转数据字节顺序,那么要显示正常颜色,就得通过代码切换高低字节发送;若硬件支持反转数据字节顺序的功能,便可以提高LCD的显示效率。在SPILCD例程中,就有用到这个功能配置。

21.2 硬件设计

21.2.1 例程功能
使用ESP32-P4开发板的RGB接口来驱动RGB屏,RGBLCD模块的接口底板上,通过40P的FPC排线连接RGBLCD模块,实现RGBLCD模块的驱动和显示,下载成功后,按下复位之后,就可以看到RGBLCD模块不停的显示一些信息并不断切换底色。同时,屏幕上会显示LCD的ID。注意:若想支持正点原子的7寸1024*600 RGBLCD和10.1寸 1280*800 RGBLCD,则需要修改ESP-IDF v5.4版本的lcd_hal.c文件,该文件位于esp-idf-v5.4\components\hal目录下,然后找到lcd_hal_cal_pclk_freq函数屏蔽一下代码段。

第二十一章 RGBLCD实验5338.png

21.2.2 硬件资源
1)LED灯
        LED        0        - IO51
2)RGBLCD
        LCD_R3        - IO18                        LCD_R4        - IO17                        LCD_R5        - IO16
        LCD_R6        - IO15                        LCD_R7        - IO14
        LCD_G2        - IO13                        LCD_G3        - IO12                        LCD_G4        - IO11
        LCD_G5        - IO10                        LCD_G6        - IO9                        LCD_G7        - IO8
        LCD_B3        - IO7                        LCD_B4        - IO6                        LCD_B5        - IO5
        LCD_B6        - IO4                        LCD_B7 - IO3
        LCD_CLK - IO20                        LCD_DE        - IO22                        LCD_BL        - IO53
        CT_RST         - IO45                        CT_INT        - IO21
        IIC_SCL        - IO32                        IIC_SDA        - IO33

21.2.3 原理图
RGBLCD原理图,如下图所示。

第二十一章 RGBLCD实验5766.png
图21.2.3.1 RGBLCD原理图

21.3 程序设计

21.3.1 LCD的IDF驱动
LCD外设驱动位于ESP-IDF下的components/esp_lcd目录下。要使用esp_lcd功能,需要导入一下头文件:
  1. <font size="3">#include "esp_lcd_panel_interface.h"        /* LCD面板结构体类型 */</font>
  2. <font size="3">#include "esp_lcd_panel_io.h"                        /* 驱动芯片接收/发送命令,发送颜色数据等函数 */</font>
  3. <font size="3">#include "esp_lcd_panel_vendor.h"                /* 包含LCD外设驱动支持的几款驱动芯片 */</font>
  4. <font size="3">#include "esp_lcd_panel_ops.h"                        /* LCD设备接口函数(reset/init/del等) */</font>
  5. <font size="3">#include "esp_lcd_panel_commands.h"        /* LCD驱动芯片的命令 */</font>
复制代码
RGB LCD 驱动流程可大致分为三个部分:初始化接口设备、移植驱动组件和初始化 LCD 设备。
初始化接口设备
初始化接口设备需要先初始化总线,再创建接口设备。
对于仅有RGB接口的LCD,不支持传输命令及参数,所以不需要初始化接口设备。
对于具备SPI接口和RGB接口的LCD,这里就需要创建SPI接口设备,如第20章操作一样,这里不做展开。
初始化接口设备,主要就是为了上层接口能够调用到底层接口函数进行数据发送。
移植驱动组件
对于仅有RGB接口的LCD,RGB接口驱动已经通过注册回调函数的方式实现了结构体esp_lcd_panel_t中的各项功能,即后续通过结构体esp_lcd_panel_t的调用即可访问到RGB底层接口函数。在LCD驱动组件中还提供esp_lcd_new_rgb_panel函数,该函数用于创建数据类型为esp_lcd_panel_handle_t的LCD设备,esp_lcd_panel_handle_t和esp_lcd_panel_t实际上是一样的。通过esp_lcd_new_rgb_panel函数,便可以使得应用程序能够使用LCD通用API函数(初始化/画点/打开显示)去操作LCD设备。因此,这种LCD不需要移植驱动组件,直接可以跳到初始化LCD设备步骤。
对于具备SPI接口和RGB接口的LCD,初始化接口后,还需要通过SPI接口发送命令以及参数,如第20章操作一样,这里不做展开。
初始化LCD设备
1,为RGBLCD创建LCD面板esp_lcd_new_rgb_panel
该函数用于为RGBLCD创建LCD面板并对LCD设备配置,其函数原型如下:
  1. esp_err_t esp_lcd_new_rgb_panel(const esp_lcd_rgb_panel_config_t *rgb_panel_config, esp_lcd_panel_handle_t *ret_panel)
复制代码
函数形参:

4.png
表21.3.1.1 esp_lcd_new_rgb_panel函数形参描述

函数返回值:
ESP_OK表示创建成功。
ESP_ERR_INVALID_ARG表示参数有误。
ESP_ERR_NO_MEM表示内存不足。
ESP_ERR_NOT_FOUND表示找不到硬件资源。
esp_panel_config为指向LCD设备配置结构体指针,esp_lcd_rgb_panel_config_t结构体中包含非常多成员,如下代码所示。
  1. typedef struct {
  2.     lcd_clock_source_t clk_src;                   /* LCD模块时钟源 */
  3.     esp_lcd_rgb_timing_t timings;                 /* RGBLCD时序参数*/
  4.     size_t data_width;                            /* 数据位宽(RGB565格式即16位) */
  5.     size_t bits_per_pixel;                        /* 帧缓冲区颜色深度(0时默认为data_width)  */
  6.     size_t num_fbs;                               /* 整屏帧缓冲区数目(0/1表示1个,最多3个)  */
  7.     size_t bounce_buffer_size_px;                 /* DRAM缓冲区供DMA使用(速度比PSRAM快)  */
  8.     size_t sram_trans_align;                      /* SRAM申请的缓冲区对齐  */
  9.     size_t psram_trans_align;                     /* PSRAM申请的缓冲区对齐  */
  10.     int hsync_gpio_num;                           /* LCD_HSYNC信号引脚  */
  11.     int vsync_gpio_num;                           /* LCD_VSYNC信号引脚  */
  12.     int de_gpio_num;                              /* LCD_DE信号引脚  */
  13.     int pclk_gpio_num;                            /* LCD_PCLK信号引脚  */
  14.     int disp_gpio_num;                            /* LCD_BL信号引脚  */
  15.     int data_gpio_nums[SOC_LCD_RGB_DATA_WIDTH];         /* 数据线信号引脚(16个IO)  */
  16.     struct {
  17.         uint32_t disp_active_low: 1;     /* disp信号引脚的有效电平  */
  18.         uint32_t refresh_on_demand: 1;   /* 1时调用draw_bitmap函数刷新帧缓存区  */
  19.         uint32_t fb_in_psram: 1;         /* 帧缓冲区优先从PSRAM中分配  */
  20.         uint32_t double_fb: 1;           /* 分配两个整屏缓存区,与num_fbs一致效果  */
  21.         uint32_t no_fb: 1;               /* 不分配帧缓冲区,需手动在回调函数中处理  */
  22.         uint32_t bb_invalidate_cache: 1; /* 对读取数据执行缓存无效操作,释放缓存  */
  23.     } flags;                             /* RGBLCD配置标志  */
  24. } esp_lcd_rgb_panel_config_t;
复制代码
esp_lcd_rgb_panel_config_t结构体中涉及到一个比较重要的结构体esp_lcd_rgb_timing_t,该结构体主要就是对RGBLCD的时序参数做配置,如下代码所示。
  1. typedef struct {
  2.     uint32_t pclk_hz;                           /* LCD_PCLK时钟频率  */
  3.     uint32_t h_res;                             /* 水平分辨率,即一行中的像素数  */
  4.     uint32_t v_res;                             /* 垂直分辨率,即帧中的行数  */
  5.     uint32_t hsync_pulse_width;                 /* 水平同步宽度,单位为PCLK周期  */
  6.     uint32_t hsync_back_porch;                  /* 水平后廊  */
  7.     uint32_t hsync_front_porch;                        /* 水平前廊  */
  8.     uint32_t vsync_pulse_width;                 /* 垂直同步宽度,单位为行数  */
  9.     uint32_t vsync_back_porch;                  /* 垂直后廊  */
  10.     uint32_t vsync_front_porch;                 /* 垂直前廊  */
  11.     struct {
  12.         uint32_t hsync_idle_low: 1;          /* 空闲状态下,LCD_HSYNC低电平  */
  13.         uint32_t vsync_idle_low: 1;          /* 空闲状态下,LCD_VSYNC低电平  */
  14.         uint32_t de_idle_high: 1;            /* 空闲状态下,LCD_DE高电平  */
  15.         uint32_t pclk_active_neg: 1;         /* 显示数据是否在LCD_PCLK下降沿上发送  */
  16.         uint32_t pclk_idle_high: 1;          /* 空闲状态下,LCD_PCLK高电平  */
  17.     } flags;                                 /* RGBLCD时序参数  */
  18. } esp_lcd_rgb_timing_t;
复制代码
创建好RGBLCD屏幕后,就可以调用LCD设备的函数,对RGBLCD进行初始化以及做一些配置工作,如下代码所示。
  1.     /* 复位屏幕 */
  2.     ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle));
  3.     /* 初始化RGB */
  4.     ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));
复制代码
初始化LCD设备完成之后,就得编写一些LCD操作的函数,比如设置横竖屏、画点、画线、画图形、显示字符等。

21.3.2 程序流程图

第二十一章 RGBLCD实验9895.png
图21.3.2.1 RGBLCD实验程序流程图

21.3.3 程序解析
在11_rgblcd例程中,作者在11_rgblcd\components\BSP路径下新建LCD文件夹,并且需要更改CMakeLists.txt内容,以便在其他文件上调用。
1. RGBLCD驱动代码
这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。LCD驱动源码包括五个文件:lcd.c、lcd.h、rgblcd.c、rgblcd.h和lcdfont.h。
rgblcd.c文件存放的是RGBLCD的驱动函数,而rgblcd.h存放的是RGBLCD接口相关引脚宏以及管理RGBLCD屏幕的重要结构体类型。lcd.c文件主要lcd的一些绘图函数,而lcd.h则存放的是引脚接口宏定义以及函数声明。lcdfont.h存放的是4种字体大小不一样的ASCII字符集(12*12、16*16、24*24和32*32)。
这里主要给大家介绍一下RGBLCD的初始化函数rgblcd_init,如下代码所示:
  1. <font size="3">/**</font>
  2. <font size="3"> * @brief           初始化RGBLCD</font>
  3. <font size="3"> * [url=home.php?mod=space&uid=271674]@param[/url]            无</font>
  4. <font size="3"> * @retval            RGBLCD句柄</font>
  5. <font size="3"> */</font>
  6. <font size="3">esp_lcd_panel_handle_t rgblcd_init(void)</font>
  7. <font size="3">{</font>
  8. <font size="3">    rgbdev.id = lcddev.id;                       /* 读取LCD面板ID */</font>
  9. <font size="3">    /* 配置VDDPST_1管理的IO电压 */</font>
  10. <font size="3">    esp_ldo_channel_handle_t ldo_rgblcd_phy = NULL;</font>
  11. <font size="3">    esp_ldo_channel_config_t ldo_rgblcd_phy_config = {</font>
  12. <font size="3">        .chan_id    = 4,                         /* 选择内存LDO */</font>
  13. <font size="3">        .voltage_mv = 3300,                     /* 输出标准电压提供VDD_RGBLCD_DPHY */</font>
  14. <font size="3">    };</font>
  15. <font size="3">ESP_ERROR_CHECK(esp_ldo_acquire_channel(&ldo_rgblcd_phy_config, </font>
  16. <font size="3">&ldo_rgblcd_phy));</font>

  17. <font size="3">    if (rgbdev.id == 0X4342)                    /* 4.3寸屏, 480*272 RGB屏 */</font>
  18. <font size="3">    {</font>
  19. <font size="3">        rgbdev.pwidth   = 480;                  /* 面板宽度,单位:像素 */</font>
  20. <font size="3">        rgbdev.pheight  = 272;                  /* 面板高度,单位:像素 */</font>
  21. <font size="3">        rgbdev.hsw      = 4;                    /* 水平同步宽度 */</font>
  22. <font size="3">        rgbdev.hbp      = 43;                   /* 水平后廊 */</font>
  23. <font size="3">        rgbdev.hfp      = 8;                    /* 水平前廊 */</font>
  24. <font size="3">        rgbdev.vsw      = 4;                    /* 垂直同步宽度 */</font>
  25. <font size="3">        rgbdev.vbp      = 12;                   /* 垂直后廊 */</font>
  26. <font size="3">        rgbdev.vfp      = 8;                    /* 垂直前廊 */</font>
  27. <font size="3">        rgbdev.pclk_hz  = 9 * 1000 * 1000;      /* 设置像素时钟 9Mhz */</font>
  28. <font size="3">    }</font>
  29. <font size="3">    else if (rgbdev.id == 0X4384)</font>
  30. <font size="3">    {</font>
  31. <font size="3">        rgbdev.pwidth   = 800;                  /* 面板宽度,单位:像素 */</font>
  32. <font size="3">        rgbdev.pheight  = 480;                  /* 面板高度,单位:像素 */</font>
  33. <font size="3">        rgbdev.hsw      = 48;                   /* 水平同步宽度 */</font>
  34. <font size="3">        rgbdev.hbp      = 88;                   /* 水平后廊 */</font>
  35. <font size="3">        rgbdev.hfp      = 40;                   /* 水平前廊 */</font>
  36. <font size="3">        rgbdev.vsw      = 3;                    /* 垂直同步宽度 */</font>
  37. <font size="3">        rgbdev.vbp      = 32;                   /* 垂直后廊 */</font>
  38. <font size="3">        rgbdev.vfp      = 13;                   /* 垂直前廊 */</font>
  39. <font size="3">        rgbdev.pclk_hz  = 30 * 1000 * 1000;     /* 设置像素时钟 30Mhz */</font>
  40. <font size="3">    }</font>
  41. <font size="3">    else if (rgbdev.id == 0x7084)               /* ATK-MD0700R-800480 */</font>
  42. <font size="3">    {</font>
  43. <font size="3">        rgbdev.pwidth   = 800;                  /* LCD面板的宽度 */</font>
  44. <font size="3">        rgbdev.pheight  = 480;                  /* LCD面板的高度 */</font>
  45. <font size="3">        rgbdev.hsw      = 1;                    /* 水平同步宽度 */</font>
  46. <font size="3">        rgbdev.hbp      = 46;                   /* 水平后廊 */</font>
  47. <font size="3">        rgbdev.hfp      = 210;                  /* 水平前廊 */</font>
  48. <font size="3">        rgbdev.vsw      = 1;                    /* 垂直同步宽度 */</font>
  49. <font size="3">        rgbdev.vbp      = 23;                   /* 垂直后廊 */</font>
  50. <font size="3">        rgbdev.vfp      = 22;                   /* 垂直前廊 */</font>
  51. <font size="3">        rgbdev.pclk_hz  = 33 * 1000 * 1000;     /* 设置像素时钟 33Mhz */</font>
  52. <font size="3">    }</font>
  53. <font size="3">    else if (rgbdev.id == 0x7016)               /* ATK-MD0700R-1024600 */</font>
  54. <font size="3">    {</font>
  55. <font size="3">        rgbdev.pwidth   = 1024;                 /* LCD面板的宽度 */</font>
  56. <font size="3">        rgbdev.pheight  = 600;                  /* LCD面板的高度 */</font>
  57. <font size="3">        rgbdev.hsw      = 20;                   /* 水平同步宽度 */</font>
  58. <font size="3">        rgbdev.hbp      = 140;                  /* 水平后廊 */</font>
  59. <font size="3">        rgbdev.hfp      = 160;                  /* 水平前廊 */</font>
  60. <font size="3">        rgbdev.vsw      = 3;                    /* 垂直同步宽度 */</font>
  61. <font size="3">        rgbdev.vbp      = 20;                   /* 垂直后廊 */</font>
  62. <font size="3">        rgbdev.vfp      = 12;                   /* 垂直前廊 */</font>
  63. <font size="3">        rgbdev.pclk_hz  = 48 * 1000 * 1000;     /* 设置像素时钟 48Mhz */</font>
  64. <font size="3">    }</font>
  65. <font size="3">    else if (rgbdev.id == 0x1018)               /* ATK-MD1018R-1280800 */</font>
  66. <font size="3">    {</font>
  67. <font size="3">        rgbdev.pwidth   = 1280;                 /* LCD面板的宽度 */</font>
  68. <font size="3">        rgbdev.pheight  = 800;                  /* LCD面板的高度 */</font>
  69. <font size="3">        rgbdev.hsw      = 10;                   /* 水平同步宽度 */</font>
  70. <font size="3">        rgbdev.hbp      = 140;                  /* 水平后廊 */</font>
  71. <font size="3">        rgbdev.hfp      = 10;                   /* 水平前廊 */</font>
  72. <font size="3">        rgbdev.vsw      = 3;                    /* 垂直同步宽度 */</font>
  73. <font size="3">        rgbdev.vbp      = 23;                   /* 垂直后廊 */</font>
  74. <font size="3">        rgbdev.vfp      = 10;                   /* 垂直前廊 */</font>
  75. <font size="3">        rgbdev.pclk_hz  = 48 * 1000 * 1000;     /* 设置像素时钟 48Mhz */</font>
  76. <font size="3">    }</font>

  77. <font size="3">    /* 配置RGB参数 */</font>
  78. <font size="3">    esp_lcd_rgb_panel_config_t panel_config = {     /* RGBLCD配置结构体 */</font>
  79. <font size="3">        .num_fbs            = 2,                    /* 缓存区数量 */</font>
  80. <font size="3">        .data_width         = 16,                   /* 数据宽度为16位 */</font>
  81. <font size="3">        .psram_trans_align  = 64,                   /* 在PSRAM分配的缓冲区的对齐 */</font>
  82. <font size="3">        .clk_src            = LCD_CLK_SRC_DEFAULT,  /* RGBLCD外设时钟源 */</font>
  83. <font size="3">        .disp_gpio_num      = GPIO_NUM_NC,          /* 用于显示控制信号,不用设-1 */</font>
  84. <font size="3">        .pclk_gpio_num      = GPIO_LCD_PCLK,        /* PCLK信号引脚 */</font>
  85. <font size="3">        .hsync_gpio_num     = GPIO_NUM_NC,          /* HSYNC信号引脚,DE模式不用 */</font>
  86. <font size="3">        .vsync_gpio_num     = GPIO_NUM_NC,          /* VSYNC信号引脚,DE模式不用 */</font>
  87. <font size="3">        .de_gpio_num        = GPIO_LCD_DE,          /* DE信号引脚 */</font>
  88. <font size="3">        .data_gpio_nums = {                         /* 数据线引脚 */</font>
  89. <font size="3">            GPIO_LCD_B3, GPIO_LCD_B4, GPIO_LCD_B5, GPIO_LCD_B6, GPIO_LCD_B7,</font>
  90. <font size="3">            GPIO_LCD_G2, GPIO_LCD_G3, GPIO_LCD_G4, GPIO_LCD_G5, GPIO_LCD_G6, </font>
  91. <font size="3">GPIO_LCD_G7,</font>
  92. <font size="3">            GPIO_LCD_R3, GPIO_LCD_R4, GPIO_LCD_R5, GPIO_LCD_R6, GPIO_LCD_R7,</font>
  93. <font size="3">        },</font>
  94. <font size="3">        .timings = {                                /* RGBLCD时序参数 */</font>
  95. <font size="3">            .pclk_hz            = rgbdev.pclk_hz,   /* 像素时钟频率 */</font>
  96. <font size="3">            .h_res              = rgbdev.pwidth,    /* 水平分辨率,即一行中的像素数 */</font>
  97. <font size="3">            .v_res              = rgbdev.pheight,   /* 垂直分辨率,即帧中的行数 */</font>
  98. <font size="3">            .hsync_back_porch   = rgbdev.hbp,       /* 水平后廊 */</font>
  99. <font size="3">            .hsync_front_porch  = rgbdev.hfp,       /* 水平前廊 */</font>
  100. <font size="3">            .hsync_pulse_width  = rgbdev.vsw,       /* 垂直同步宽度,单位:行数 */</font>
  101. <font size="3">            .vsync_back_porch   = rgbdev.vbp,       /* 垂直后廊 */</font>
  102. <font size="3">            .vsync_front_porch  = rgbdev.vfp,       /* 垂直前廊 */</font>
  103. <font size="3">            .vsync_pulse_width  = rgbdev.hsw,       /* 水平同步宽度 */</font>
  104. <font size="3">            .flags = {</font>
  105. <font size="3">                .pclk_active_neg = true,            /* RGB数据在下降沿计时 */</font>
  106. <font size="3">            },</font>
  107. <font size="3">        },</font>
  108. <font size="3">        .flags.fb_in_psram = true,                  /* 在PSRAM中分配帧缓冲区 */</font>
  109. <font size="3">    };</font>

  110. <font size="3">    esp_lcd_new_rgb_panel(&panel_config, &panel_handle);/* 创建RGB对象 */</font>

  111. <font size="3">    ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle)); /* 复位RGB屏 */</font>
  112. <font size="3">    </font>
  113. <font size="3">    ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));  /* 初始化RGB */</font>
  114. <font size="3">    </font>
  115. <font size="3">    rgblcd_display_dir(1);                              /* 设置横屏 */</font>

  116. <font size="3">    return panel_handle;                                /* RGBLCD句柄 */</font>
  117. <font size="3">}</font>
复制代码
该函数就是对RGBLCD进行初始化,整个过程就围绕着21.3.1小节中描述的驱动流程实现。通过esp_lcd_new_rgb_panel函数创建RGBLCD屏对象,后续通过LCD通用API接口对LCD进行配置,比如esp_lcd_panel_reset复位LCD屏,esp_lcd_panel_init初始化LCD屏。最后通过rgblcd_display_dir函数设置横屏显示。函数中还涉及到配置VDDPST_1管理的IO电压,通过调用esp_ldo_acquire_channel函数设置LDO_VO4输出3.3V。
上面RGBLCD对应的时序参数,可到对应的裸屏规格书中查找到,一般来说都是以典型值作为配置值。
接下来,介绍一下设置RGBLCD横竖屏的函数rgblcd_display_dir,如下代码所示:
  1. <font size="3">/**</font>
  2. <font size="3"> * @brief             RGBLCD显示方向设置</font>
  3. <font size="3"> * @param            dir:0,竖屏;1,横屏</font>
  4. <font size="3"> * @retval          无</font>
  5. <font size="3"> */</font>
  6. <font size="3">void rgblcd_display_dir(uint8_t dir)</font>
  7. <font size="3">{</font>
  8. <font size="3">    rgbdev.dir = dir;              /* 显示方向 */</font>

  9. <font size="3">    if (rgbdev.dir == 0)           /* 竖屏 */</font>
  10. <font size="3">    {</font>
  11. <font size="3">        rgbdev.width = rgbdev.pheight;</font>
  12. <font size="3">        rgbdev.height = rgbdev.pwidth;</font>
  13. <font size="3">        esp_lcd_panel_swap_xy(panel_handle, true);          /* 交换X和Y轴 */ </font>
  14. <font size="3">        esp_lcd_panel_mirror(panel_handle, false, true);    /* Y轴进行镜像处理 */</font>
  15. <font size="3">    }</font>
  16. <font size="3">    else if (rgbdev.dir == 1)      /* 横屏 */</font>
  17. <font size="3">    {</font>
  18. <font size="3">        rgbdev.width = rgbdev.pwidth;</font>
  19. <font size="3">        rgbdev.height = rgbdev.pheight;</font>
  20. <font size="3">        esp_lcd_panel_swap_xy(panel_handle, false);         /* 不需要交换XY轴 */</font>
  21. <font size="3">        esp_lcd_panel_mirror(panel_handle, false, false);   /* XY轴不镜像处理 */</font>
  22. <font size="3">    }</font>
  23. <font size="3">    lcddev.width = rgbdev.width;   /* 宽度 */</font>
  24. <font size="3">    lcddev.height = rgbdev.height; /* 高度 */</font>
  25. <font size="3">}</font>
复制代码
该函数主要就是通过调用esp_lcd_panel_swap_xy函数和esp_lcd_panel_mirror函数去实现,前者将X轴和Y轴交换,而后者就是对X轴或Y轴进行镜像。
下面介绍在rgblcd.h文件定义的重要结构体:
  1. <font size="3">/* LCD RGBLCD重要参数集 */</font>
  2. <font size="3">typedef struct  </font>
  3. <font size="3">{</font>
  4. <font size="3">    uint32_t pwidth;        /* RGBLCD面板的宽度,固定参数,不随显示方向改变 */</font>
  5. <font size="3">    uint32_t pheight;       /* RGBLCD面板的高度,固定参数,不随显示方向改变 */</font>
  6. <font size="3">    uint16_t hsw;           /* 水平同步宽度 */</font>
  7. <font size="3">    uint16_t vsw;           /* 垂直同步宽度 */</font>
  8. <font size="3">    uint16_t hbp;           /* 水平后廊 */</font>
  9. <font size="3">    uint16_t vbp;           /* 垂直后廊 */</font>
  10. <font size="3">    uint16_t hfp;           /* 水平前廊 */</font>
  11. <font size="3">    uint16_t vfp;           /* 垂直前廊  */</font>
  12. <font size="3">    uint8_t activelayer;    /* 当前层编号:0/1 */</font>
  13. <font size="3">    uint8_t dir;            /* 0,竖屏;1,横屏; */</font>
  14. <font size="3">    uint16_t id;            /* RGBLCD ID */</font>
  15. <font size="3">    uint32_t pclk_hz;       /* 设置像素时钟 */</font>
  16. <font size="3">    uint16_t width;         /* RGBLCD宽度 */</font>
  17. <font size="3">    uint16_t height;        /* RGBLCD高度 */</font>
  18. <font size="3">} _rgblcd_dev;</font>
复制代码
_rgblcd_dev结构体用于保存一些RGBLCD重要参数信息,比如RGBLCD的ID、RGBLCD的长宽、RGBLCD的时序参数等。最后声明_rgblcd_dev结构体类型变量rgblcd_dev,rgblcd_dev在rgblcd.c中定义。
在lcd.h文件中也定义了一个重要参数结构体,如下代码所示。
  1. <font size="3">/* LCD重要参数集 */</font>
  2. <font size="3">typedef struct  </font>
  3. <font size="3">{</font>
  4. <font size="3">    uint16_t id;                               /* 读取ID */</font>
  5. <font size="3">    uint32_t width;                            /* 面板宽度,固定参数,不随显示方向改变 */</font>
  6. <font size="3">    uint32_t height;                                /* 面板高度,固定参数,不随显示方向改变 */</font>
  7. <font size="3">    uint8_t  dir;                                   /* 0,竖屏(MIPI只能竖屏);1,横屏; */</font>
  8. <font size="3">    uint8_t  color_byte;                              /* 颜色格式 */</font>
  9. <font size="3">    esp_lcd_panel_handle_t lcd_panel_handle;         /* LCD控制句柄 */</font>
  10. <font size="3">    struct</font>
  11. <font size="3">    {</font>
  12. <font size="3">        int lcd_rst;                                    /* 复位引脚 */</font>
  13. <font size="3">        int lcd_bl;                            /* 背光引脚 */</font>
  14. <font size="3">    } ctrl;</font>
  15. <font size="3">} _lcd_dev;</font>
复制代码
_lcd_dev结构体用于保存一些LCD重要参数信息,比如LCD的ID(RGBLCD)、LCD的长宽、LCD控制句柄等。最后声明_lcd_dev结构体类型变量lcddev,lcddev在lcd.c中定义。
下面我们再解析lcd.c的程序,看一下初始化函数lcd_init,代码如下:
  1. <font size="3">/**</font>
  2. <font size="3"> * @brief             初始化LCD</font>
  3. <font size="3"> * @param             无</font>
  4. <font size="3"> * @retval             无</font>
  5. <font size="3"> */</font>
  6. <font size="3">void lcd_init(void)</font>
  7. <font size="3">{</font>
  8. <font size="3">    lcddev.id = lcd_panelid_read();                   /* 读取RGB LCD面板ID */</font>
  9. <font size="3">    lcddev.ctrl.lcd_rst = LCD_RST_PIN;               /* 复位管脚 */</font>
  10. <font size="3">    lcddev.ctrl.lcd_bl  = LCD_BL_PIN;                 /* 背光管脚 */</font>

  11. <font size="3">    gpio_config_t gpio_init_struct = {0};</font>
  12. <font size="3">    gpio_init_struct.intr_type    = GPIO_INTR_DISABLE;          /* 失能引脚中断 */</font>
  13. <font size="3">    gpio_init_struct.mode         = GPIO_MODE_OUTPUT;           /* 输出模式 */</font>
  14. <font size="3">    gpio_init_struct.pull_up_en   = GPIO_PULLUP_DISABLE;        /* 失能上拉 */</font>
  15. <font size="3">    gpio_init_struct.pull_down_en = GPIO_PULLDOWN_DISABLE;      /* 失能下拉 */</font>
  16. <font size="3">    gpio_init_struct.pin_bit_mask = 1ull << lcddev.ctrl.lcd_bl; /* 设置的引脚 */</font>
  17. <font size="3">    ESP_ERROR_CHECK(gpio_config(&gpio_init_struct));            /* 配置GPIO */</font>

  18. <font size="3">    LCD_BL(0);      /* 背光关闭 */</font>

  19. <font size="3">    lcddev.lcd_panel_handle = rgblcd_init();                                  /* 初始化RGB LCD */</font>
  20. <font size="3">ESP_ERROR_CHECK(esp_lcd_rgb_panel_get_frame_buffer(lcddev.lcd_panel_handle,</font>
  21. <font size="3">2, &lcd_buffer[0], &lcd_buffer[1]));         /* 获取帧缓冲区 */</font>
  22. <font size="3">    </font>
  23. <font size="3">/* 内部缓冲区刷新完成回调函数 */</font>
  24. <font size="3">const esp_lcd_rgb_panel_event_callbacks_t rgb_cbs = {</font>
  25. <font size="3">        .on_bounce_frame_finish = lcd_rgb_panel_refresh_done_callback,  </font>
  26. <font size="3">    };</font>

  27. <font size="3">    ESP_ERROR_CHECK(esp_lcd_rgb_panel_register_event_callbacks(</font>
  28. <font size="3">lcddev.lcd_panel_handle, &rgb_cbs, NULL));</font>
  29. <font size="3">    lcd_clear(WHITE);</font>

  30. <font size="3">    LCD_BL(1);      /* 打开背光 */</font>
  31. <font size="3">}</font>
复制代码
在lcd_init函数中,首先通过调用lcd_panelid_read函数读取RGBLCD的ID,把ID赋值给lcddev.id成员,然后对LCD背光控制引脚配置,后面再调用rgblcd_init函数初始化RGBLCD,紧接着通过esp_lcd_rgb_panel_get_frame_buffer函数接口把数据获取到。为了防止屏幕撕裂,这里还注册了刷新完成回调函数,在进行清屏函数中,需要等待一帧刷新完成,再进行下一帧刷新。最后调用lcd_clear函数清屏,拉高背光控制引脚,打开背光。
现在,来看一下读取RGBLCD的ID函数lcd_panelid_read,如下代码所示。
  1. <font size="3">/**</font>
  2. <font size="3"> * @brief           读取RGB LCD ID</font>
  3. <font size="3"> * [url=home.php?mod=space&uid=60778]@note[/url]             利用LCD RGB线的最高位(R7,G7,B7)来识别面板ID</font>
  4. <font size="3"> *                  IO14 = R7(M0); IO8 = G7(M1); IO3 = B7(M2);</font>
  5. <font size="3"> *                    M2:M1:M0</font>
  6. <font size="3"> *                   0 :0 :0     4.3 寸480*272  RGB屏,ID = 0X4342</font>
  7. <font size="3"> *                    0 :0 :1     7   寸800*480  RGB屏,ID = 0X7084</font>
  8. <font size="3"> *                   0 :1 :0     7   寸1024*600 RGB屏,ID = 0X7016</font>
  9. <font size="3"> *                   0 :1 :1     7   寸1280*800 RGB屏,ID = 0X7018</font>
  10. <font size="3"> *                   1 :0 :0     4.3 寸800*480  RGB屏,ID = 0X4348</font>
  11. <font size="3"> *                    1 :0 :1     10.1寸1280*800 RGB屏,ID = 0X1018</font>
  12. <font size="3"> * </font>
  13. <font size="3"> * @param             无</font>
  14. <font size="3"> * @retval          0, 非法; </font>
  15. <font size="3"> *                   其他, LCD ID</font>
  16. <font size="3"> */</font>
  17. <font size="3">uint16_t lcd_panelid_read(void)</font>
  18. <font size="3">{</font>
  19. <font size="3">    uint8_t idx = 0;</font>
  20. <font size="3">    gpio_config_t gpio_init_struct = {0};</font>

  21. <font size="3">    gpio_init_struct.intr_type      = GPIO_INTR_DISABLE;            /* 失能引脚中断 */</font>
  22. <font size="3">    gpio_init_struct.mode           = GPIO_MODE_INPUT;              /* 输入输出模式 */</font>
  23. <font size="3">    gpio_init_struct.pull_up_en     = GPIO_PULLUP_ENABLE;            /* 使能上拉 */</font>
  24. <font size="3">    gpio_init_struct.pull_down_en   = GPIO_PULLDOWN_DISABLE;        /* 失能下拉 */</font>
  25. <font size="3">gpio_init_struct.pin_bit_mask   = 1ull << GPIO_LCD_R7 | 1ull << GPIO_LCD_G7 </font>
  26. <font size="3">| 1ull << GPIO_LCD_B7;</font>
  27. <font size="3">    gpio_config(&gpio_init_struct);                                 /* 配置GPIO */</font>

  28. <font size="3">    idx  = (uint8_t)gpio_get_level(GPIO_LCD_R7);                        /* 读取M0 */</font>
  29. <font size="3">    idx |= (uint8_t)gpio_get_level(GPIO_LCD_G7) << 1;                   /* 读取M1 */</font>
  30. <font size="3">    idx |= (uint8_t)gpio_get_level(GPIO_LCD_B7) << 2;                   /* 读取M2 */</font>

  31. <font size="3">    /* 正点原子其他的RGB LCD自行匹配 */</font>
  32. <font size="3">    switch (idx)</font>
  33. <font size="3">    {</font>
  34. <font size="3">        case 0:</font>
  35. <font size="3">        {</font>
  36. <font size="3">            return 0x4342;                      /* ATK-MD0430R-480272 */</font>
  37. <font size="3">        }</font>
  38. <font size="3">        case 1:</font>
  39. <font size="3">        {</font>
  40. <font size="3">            return 0x7084;                      /* ATK-MD0700R-800480 */</font>
  41. <font size="3">        }</font>
  42. <font size="3">        case 2:</font>
  43. <font size="3">        {</font>
  44. <font size="3">            return 0x7016;                      /* ATK-MD0700R-1024600 */</font>
  45. <font size="3">        }</font>
  46. <font size="3">        case 3:</font>
  47. <font size="3">        {</font>
  48. <font size="3">            return 0x7018;                      /* ATK-MD0700R-1280800 */</font>
  49. <font size="3">        }</font>
  50. <font size="3">        case 4:</font>
  51. <font size="3">        {</font>
  52. <font size="3">            return 0x4384;                      /* ATK-MD0430R-800480 */</font>
  53. <font size="3">        }</font>
  54. <font size="3">        case 5:</font>
  55. <font size="3">        {</font>
  56. <font size="3">            return 0x1018;                      /* ATK-MD1018R-1280800 */</font>
  57. <font size="3">        }</font>
  58. <font size="3">        default:</font>
  59. <font size="3">        {</font>
  60. <font size="3">            return 0;</font>
  61. <font size="3">        }</font>
  62. <font size="3">    }</font>
  63. <font size="3">}</font>
复制代码
在前面也说到,RGBLCD屏并没有读功能,但是在正点原子的RGBLCD模块上,利用数据线(R7/G7/B7)做了一个巧妙的设计,可让主控芯片读到RGBLCD模块的ID,从而执行不同的初始化,实现不同分辨率的RGBLCD模块的兼容。
接下来,再看看这个回调函数里面的实现,如下代码所示。
  1. <font size="3">/**</font>
  2. <font size="3"> * @brief             内部缓存刷新完成回调函数</font>
  3. <font size="3"> * @param            panel_io: RGBLCD IO的句柄</font>
  4. <font size="3"> * @param                  edata: 事件数据类型</font>
  5. <font size="3"> * @param            user_ctx: 传入参数</font>
  6. <font size="3"> * @retval            无</font>
  7. <font size="3"> */</font>
  8. <font size="3">IRAM_ATTR static bool lcd_rgb_panel_refresh_done_callback(esp_lcd_panel_handle_t panel, const esp_lcd_rgb_panel_event_data_t *edata, void *user_ctx)</font>
  9. <font size="3">{</font>
  10. <font size="3">    refresh_done_flag = 1;</font>
  11. <font size="3">    return false;</font>
  12. <font size="3">}</font>
复制代码
当发送一帧完成之后,就回进入到回调函数中,把refresh_done_flag标志置1。
现在看一下LCD的清屏函数lcd_clear,如下代码所示:
  1. <font size="3">/**</font>
  2. <font size="3"> * @brief             清屏</font>
  3. <font size="3"> * @param             color :清屏颜色</font>
  4. <font size="3"> * @retval           无</font>
  5. <font size="3"> */</font>
  6. <font size="3">IRAM_ATTR void lcd_clear(uint16_t color)</font>
  7. <font size="3">{</font>
  8. <font size="3">/* 将 void* 转换为 uint16_t* */</font>
  9. <font size="3">    uint16_t *buffer = (uint16_t *)lcd_buffer[buffer_sw];  </font>

  10. <font size="3">    /* 制定缓存区填充颜色值 */</font>
  11. <font size="3">    for (uint32_t i = 0; i < lcddev.width * lcddev.height; i++)</font>
  12. <font size="3">    {</font>
  13. <font size="3">        buffer</font><span style="font-size: medium;"> = color;
  14.     }

  15. esp_lcd_panel_draw_bitmap(lcddev.lcd_panel_handle, 0, 0, lcddev.width,
  16. lcddev.height, buffer);

  17.     refresh_done_flag = 0;

  18.     do
  19.     {
  20.         vTaskDelay(1);                /* 等待内部缓存刷新完成 */
  21.     }
  22.     while (refresh_done_flag != 1);
  23.     /* 使用异或操作在 0 和 1 之间切换,目的是为了切换另一个缓冲区 */
  24.     buffer_sw ^= 1;
  25. }</span>
复制代码
在清屏函数里面,按照熟悉的流程,先申请内存,然后往内存存入颜色数据,再调用esp_lcd_panel_draw_bitmap进行区域画点。只不过这里没有申请内存,而是直接使用LCD初始化时候申请到的整屏缓存,即esp_lcd_rgb_panel_config_t结构体中的num_fbs成员。在mipi_lcd_init函数里面,设置该成员为2,所以看到lcd_buffer有两个元素。当发送完esp_lcd_panel_draw_bitmap函数,这时候dma就开始搬运数据,这个过程需要一点时间,所以利用refresh_done_flag变量进行等待完成。完成一帧数据的显示,最后切换另一个buffer,避免出现LCD撕裂现象。
最后介绍一下字符显示函数lcd_show_char,该函数代码如下:
  1. /**
  2. * @brief            在指定位置显示一个字符
  3. * @param           x,y  :坐标
  4. * @param            chr  :要显示的字符:" "--->"~"
  5. * @param            size :字体大小 12/16/24/32
  6. * @param            mode :叠加方式(1); 非叠加方式(0);
  7. * @param            color:字体颜色
  8. * @retval            无
  9. */
  10. void lcd_show_char(uint16_t x, uint16_t y, char chr, uint8_t size, uint8_t mode, uint16_t color)
  11. {
  12.     uint8_t temp, t1, t;
  13.     uint16_t y0 = y;
  14.     uint8_t csize = 0;
  15. uint8_t *pfont = 0;

  16.     csize = (size / 8 + ((size % 8) ? 1 : 0)) * (size / 2);        /* 得到字体大小 */
  17.     chr = (char)chr - ' ';                                                                      /* 得到偏移后的值*/

  18.     switch (size)
  19.     {
  20.         case 12:
  21.             pfont = (uint8_t *)asc2_1206[(uint8_t)chr];             /* 调用1206字体 */
  22.             break;

  23.         case 16:
  24.             pfont = (uint8_t *)asc2_1608[(uint8_t)chr];             /* 调用1608字体 */
  25.             break;

  26.         case 24:
  27.             pfont = (uint8_t *)asc2_2412[(uint8_t)chr];             /* 调用2412字体 */
  28.             break;

  29.         case 32:
  30.             pfont = (uint8_t *)asc2_3216[(uint8_t)chr];             /* 调用3216字体 */
  31.             break;

  32.         default:
  33.             return ;
  34.     }

  35.     for (t = 0; t < csize; t++)
  36.     {
  37.         temp = pfont[t];                                /* 获取字符的点阵数据 */

  38.         for (t1 = 0; t1 < 8; t1++)                            /* 一个字节8个点 */
  39.         {
  40.             if (temp & 0x80)                                  /* 有效点,需要显示 */
  41.             {
  42.                 lcd_draw_point(x, y, color);            /* 画点出来,要显示这个点 */
  43.             }
  44.             else if (mode == 0)                        /* 无效点,不显示 */
  45.             {
  46.                 lcd_draw_point(x, y, g_back_color);         /* 画背景色 */
  47.             }

  48.             temp <<= 1;                                   /* 移位, 以便获取下一个位的状态 */
  49.             y++;

  50.             if (y >= lcddev.height) return;              /* 超区域了 */

  51.             if ((y - y0) == size)                      /* 显示完一列了? */
  52.             {
  53.                 y = y0;                                 /* y坐标复位 */
  54.                 x++;                                   /* x坐标递增 */
  55.                
  56.                 if (x >= lcddev.width)
  57.                 {
  58.                     return;                              /* x坐标超区域了 */
  59.                 }

  60.                 break;
  61.             }
  62.         }
  63.     }
  64. }
复制代码
在lcd_show_char函数里面,我们用到了四个字符集点阵数据数组asc2_1206、asc2_1608、asc2_2412和asc2_3216,通过参数font决定。此外该函数增加以叠加方式显示,或者以非叠加方式显示。叠加方式显示多用于在显示的图片上再显示字符。非叠加方式一般用于普通的显示。
注意:字符点阵数据的生成是依靠正点原子的XFONT软件,取模方式设置为:阴码+逐列式+顺向+C51格式,具体教程请参考正点原子任何一个《STM32开发指南》的OLED显示实验章节的程序解析处。
lcd.c的函数比较多,其他函数请大家自行查看源码,都有详细的注释。
2. CMakeLists.txt文件
本例程的功能实现主要依靠LCD驱动。要在main函数中,成功调用LCD文件中的内容,就得需要修改BSP文件夹下的CMakeLists.txt文件,修改如下:
  1. set(src_dirs
  2.                    LED
  3. LCD)

  4. set(include_dirs
  5.                    LED
  6. LCD)

  7. set(requires
  8.                   driver
  9.                         esp_lcd
  10. esp_common)

  11. idf_component_register(        SRC_DIRS ${src_dirs} INCLUDE_DIRS ${include_dirs} REQUIRES ${requires})

  12. component_compile_options(-ffast-math -O3 -Wno-error=format=-Wno-format)
复制代码
3. main.c驱动代码
在main.c里面编写如下代码。
  1. void app_main(void)
  2. {
  3.     esp_err_t ret;
  4.     uint8_t x = 0;

  5.     ret = nvs_flash_init();          /* 初始化NVS */
  6.     if(ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
  7.     {
  8.         ESP_ERROR_CHECK(nvs_flash_erase());
  9.         ESP_ERROR_CHECK(nvs_flash_init());
  10.     }

  11.     led_init();                             /* LED初始化 */
  12.     lcd_init();                             /* LCD屏初始化 */

  13.     while (1)
  14.     {
  15.         switch (x)
  16.         {
  17.             case 0:
  18.             {
  19.                 lcd_clear(WHITE);
  20.                 break;
  21.             }
  22.             case 1:
  23.             {
  24.                 lcd_clear(BLACK);
  25.                 break;
  26.             }
  27.             case 2:
  28.             {
  29.                 lcd_clear(BLUE);
  30.                 break;
  31.             }
  32.             case 3:
  33.             {
  34.                 lcd_clear(RED);
  35.                 break;
  36.             }
  37.             case 4:
  38.             {
  39.                 lcd_clear(MAGENTA);
  40.                 break;
  41.             }
  42.             case 5:
  43.             {
  44.                 lcd_clear(GREEN);
  45.                 break;
  46.             }
  47.             case 6:
  48.             {
  49.                 lcd_clear(CYAN);
  50.                 break;
  51.             }
  52.             case 7:
  53.             {
  54.                 lcd_clear(YELLOW);
  55.                 break;
  56.             }
  57.             case 8:
  58.             {
  59.                 lcd_clear(BRRED);
  60.                 break;
  61.             }
  62.             case 9:
  63.             {
  64.                 lcd_clear(GRAY);
  65.                 break;
  66.             }
  67.             case 10:
  68.             {
  69.                 lcd_clear(LGRAY);
  70.                 break;
  71.             }
  72.             case 11:
  73.             {
  74.                 lcd_clear(BROWN);
  75.                 break;
  76.             }
  77.         }

  78.         lcd_show_string(10, 40,  240, 32, 32, "ESP32-P4", RED);
  79.         lcd_show_string(10, 80,  240, 24, 24, "RGBLCD TEST", RED);
  80.         lcd_show_string(10, 110, 240, 16, 16, "ATOM@ALIENTEK", RED);

  81.         x++;
  82.         if (x == 12)
  83.         {
  84.             x = 0;
  85.         }

  86.         LED0_TOGGLE();
  87.         vTaskDelay(pdMS_TO_TICKS(1000));
  88.     }
  89. }
复制代码
app_main函数功能主要是显示一些固定的字符,字体大小包括32、24和16三种,然后不停的切换背景颜色,每500毫秒切换一次。而LED0也会不停地闪烁,指示程序已经在运行了。

21.4 下载验证
下载代码后,LED0不停地闪烁,提示程序已经在运行了。同时可以看到RGBLCD屏幕模块显示背景色不停切换,如下图所示。

第二十一章 RGBLCD实验29336.png
图21.4.1 RGBLCD显示效果图
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则


关闭

原子哥极力推荐上一条 /1 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-12-27 06:44

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表