# 屏幕模组框架介绍 ## 简介 思澈的显示框架是基于[rt_device框架](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/device/device?id=%e8%ae%bf%e9%97%ae-io-%e8%ae%be%e5%a4%87)的,具有如下特点: - 同一个屏幕驱动、TP驱动、背光驱动可以在不同的开发板之间复用 - 同一个开发板可以通过menuconfig选择不同的屏幕模组 - 支持同时兼容多个屏幕驱动、TP驱动,根据ID来区分

这样设计确实提高了复用度,但也给屏幕模组驱动配置带来了分散性的问题: - 屏幕模组的上下电、复位接口、pinmux的设置、背光用到的pwm等程序都与开发板的相关联(类似屏驱模组的BIOS) - 屏幕驱动、TP驱动、背光驱动的编写需要基于上面提供的这些宏定义、IO接口来实现 - 最终将实现的屏幕驱动、TP驱动、背光驱动汇集成一个menuconfig菜单,供开发板选择。



## 屏幕部分 ### 屏幕框架图 ![alt text](assets/LCD_module_sw_framework.png) **图内各项解释:** - 驱动层(drv_lcd.c) --- 实现了名为“lcd”的rt_device,供上层操作屏幕。 - 中间绿色部分具体的驱动代码(为客户添加屏幕驱动的主要代码) - 板级IO接口文件(bsp_lcd_tp.c, 或 drv_io.c) --- 为屏幕驱动提供统一的上电、下电、复位的接口。 - 背光设备”lcdlight" --- 为屏幕驱动提供统一的背光控制接口 - HAL层(bf0_hal_lcdc.c) --- 为屏幕驱动提供基本统一的参数配置、LCD寄存器读写等接口



(lcd-driver-register)= ### 注册屏驱到系统 思澈的屏驱框架支持同时注册多个屏驱到系统,通过宏`LCD_DRIVER_EXPORT2`生成一个特殊段名的变量,链接到一起。 在nv3051f1.c中通过LCD_DRIVER_EXPORT2将屏驱的回调函数注册到系统,每个函数的详细解析请参考[屏驱回调函数](./屏驱回调函数.md): ``` static const LCD_DrvOpsDef LCD_drv = { LCD_Init, //【必选】,屏驱初始函数(包括复位,初始化程序等) LCD_ReadID, //【必选】,屏幕在位检测函数 LCD_DisplayOn, //【必选】,屏幕打开 LCD_DisplayOff, //【必选】,屏幕关闭 LCD_SetRegion, //【必选】,设置屏幕接受数据时的区域(2A,2B 的区域) LCD_WritePixel, // 可选,写一个像素点到屏幕上 LCD_WriteMultiplePixels, //【必选】,写批量像素点到屏幕上 LCD_ReadPixel, // 可选,读屏幕上的一个像素点数据,返回像素的RGB值 LCD_SetColorMode, // 可选,切换输出给屏幕的颜色格式 LCD_SetBrightness, // 可选,设置屏幕的亮度 LCD_IdleModeOn, // 可选,进入待机显示模式(低功耗模式) LCD_IdleModeOff, // 可选,退出待机显示模式(低功耗模式) LCD_Rotate, // 可选,旋转屏幕一定角度 LCD_TimeoutDbg, // 可选,批量送数超时后,屏幕自检 LCD_TimeoutReset, // 可选,批量送数超时后,屏幕复位 LCD_ESDCheck, // 可选,屏幕定时ESD检测 }; LCD_DRIVER_EXPORT2(nv3051f1, LCD_ID, &lcdc_int_cfg, &LCD_drv,2); ```



(lcd-driver-detect-method)= ### 屏幕在位检测 当系统注册了多个屏幕驱动时,如何判断使用哪个屏驱来驱动当前屏幕,就需要通过屏幕在位检测。方法是先调用每个屏驱的[LCD_Init](lcd-cb-func-lcd-init)函数,让其初始化,然后在调用[LCD_ReadID](lcd-cb-func-lcd-readid)函数,如果[LCD_ReadID](lcd-cb-func-lcd-readid)函数返回的值与LCD_ID值相同时,认为屏幕在位,则使用该屏驱。否则继续调用下一个屏驱的[LCD_Init](lcd-cb-func-lcd-init)和[LCD_ReadID](lcd-cb-func-lcd-readid)。 - _[LCD_Init](lcd-cb-func-lcd-init)和[LCD_ReadID](lcd-cb-func-lcd-readid) 是每个[屏驱注册的回调函数](lcd-driver-register)_ - _LCD_ID是通过LCD_DRIVER_EXPORT2传入的参数_ - 可以直接返回LCD_ID,如果强制使用该屏幕驱动。 适用于只有1个屏幕驱动或者屏幕不支持读ID的情况。 (lcd-ic-pixel-alignment)= ### 屏幕刷新的像素对齐 有些屏驱的刷新区域有像素对齐要求,思澈的屏驱框架可以支持设置像素对齐(如果有的屏幕行列对齐要求不一致,则用最大的值。比如某屏幕行对齐要求是2,列对齐要求是4,则取4)。 屏驱IC的更新区域像素对齐要求一般在0x2A(起止列)和0x2B(起止行) 这个2个寄存器的描述里面, 如下图的这个IC行列都是要求2像素对齐: ```{figure} assets/LCD_IC_pixel_alignment.png :scale: 30 % ```



## 触控部分 ### 触控(TP)框架图 ![alt text](../assets/image-1.png)



### TP驱动注册到系统的接口 在gt911.c中通过INIT_COMPONENT_EXPORT注册初始化函数rt_tp_device_init,然后在rt_tp_device_init里面通过函数rt_touch_drivers_register,将TP驱动注册到系统。 ```c static struct touch_drivers driver; static struct touch_ops ops = { read_point, //TP数据读取回调函数 init, //TP初始化回调函数 deinit //TP去初始化回调函数 }; static int rt_tp_device_init(void) { driver.probe = probe; //TP在位检测回调函数 driver.ops = &ops; driver.user_data = RT_NULL; driver.isr_sem = rt_sem_create("gt911", 0, RT_IPC_FLAG_FIFO); //TP数据读取信号量 rt_touch_drivers_register(&driver); //注册到系统TP驱动框架 return 0; } INIT_COMPONENT_EXPORT(rt_tp_device_init); //注册初始化函数 ``` ## 背光部分 非自发光的屏幕一般需要背光,目前屏幕的背光驱动都通过各种办法,实现了一个“lcdlight”的rt_device设备,然后统一在屏驱的回调函数[LCD_SetBrightness](lcd-cb-func-LCD-SetBrightness)内使用。 目前支持2种模式: - PWM直驱背光,芯片直接输出PWM波形,直驱背光 - 外部背光驱动, 通过GPIO控制外部背光驱动芯片输出PWM波形,来驱动背光 ### PWM直驱背光 这种设备已经在drv_lcd.c里面注册了“lcdlight”的rt_device设备,见rt_hw_lcd_backlight_init函数。 使用的PWM频率默认是1KHz。 里面会用到2个宏`LCD_PWM_BACKLIGHT_INTERFACE_NAME`和`LCD_PWM_BACKLIGHT_CHANEL_NUM`,分别指定了PWM的设备名称和channel号,这2个宏一般在[Kconfig.board](lcd_driver_folder_strcuture)里面定义。 注意:需要在menuconfig里面使能`LCD_PWM_BACKLIGHT_INTERFACE_NAME`指定的pwm rt_device,例如指定"pwm2"时需要选择: ![alt text](assets/lcd_bl_enable_pwm_device.png) 通过在屏幕模组里面选择宏[LCD_USING_PWM_AS_BACKLIGHT](lcd_menuconfig_select_backlight_type)来使用这种类型的背光。 ### 外部背光驱动 例如aw9364.c,是在sif_aw9364_init函数里面注册的“lcdlight”的rt_device设备。 里面宏`LCD_BACKLIGHT_CONTROL_PIN`来指定使用哪个GPIO来控制aw9364。这个宏也在[Kconfig.board](lcd_driver_folder_strcuture)里面定义。 通过在屏幕模组里面选择宏[BL_USING_AW9364](lcd_menuconfig_select_backlight_type)来使用这种类型的背光。