网站按钮确定后图片怎么做,电影网站如何建设会员式,海西高端网站建设,南京百度seo代理LCD是很常用的一个外设#xff0c;通过LCD可以显示绚丽的图片、界面等#xff0c;提交人机交互的效率。STM32MP1提供了一个LTDC接口用于连接RGB接口的液晶屏。本章就来学校一下如何在Linux下驱动LCD屏。
LCD和LTDC简介
LCD简介
这里在当时学习stm32裸机开发的时候就学过了…LCD是很常用的一个外设通过LCD可以显示绚丽的图片、界面等提交人机交互的效率。STM32MP1提供了一个LTDC接口用于连接RGB接口的液晶屏。本章就来学校一下如何在Linux下驱动LCD屏。
LCD和LTDC简介
LCD简介
这里在当时学习stm32裸机开发的时候就学过了但是当时我的是一个2.8寸的电阻触摸屏而且接口是MCU的8080时序和现在买的这个RGB不一样所以还是要学习一下。
现在要在STM32MP1开发板上使用LCD所以不需要去研究LCD的具体实现原理只需要从使用的角度去关注LCD的几个重要点
分辨率
LCD显示器都是由一个个的像素点组成像素点就类似一个灯(在OLED显示器中像素点就是一个小灯)这个小灯是RGB灯也就是由R(红色)、G(绿色)和B(蓝色)这三种颜色组成的而RGB就是光的三原色。1080P的意思就是LCD屏幕上的像素数量是1920*1080个也就是这个屏幕一列1080个像素点一共1920列如下所示 上图就是1080P显示器的像素示意图X轴就是LCD显示器的横轴Y轴就是显示器的竖轴。图中的小方块就是像素点一共有1920*10802073600个像素点。左上角的A点是第一个像素点右下角的C点就是最后一个像素点。2K就是2560*1440个像素点4K是3840*2160个像素点。
LCD显示器的分辨率是一个很重要的参数但是并不是分辨率越高的LCD就越好。衡量一款LCD的好坏分辨率只是其中的一个参数还有色彩还原程度、色彩偏离、亮度、可视角度、屏幕刷新率等其他参数。
像素格式
一个像素点就相当于一个RGB小灯通过控制R、G、B这三种颜色的亮度就可以显示出各种各样的色彩。一般一个像素点的R、G、B这三部分分别使用8bit的数据那么一个像素点就是8bit*324bit也就是说一个像素点3个字节这种像素格式称为RGB888。如果再加入8bit的Alpha(透明)通道的话一个像素点就是32bit也就是4个字节这种像素格式称为ARGB8888。本次实验中使用ARGB8888这种像素格式一个像素占用4个字节的内存这四个字节每个位的分配如下图所示 在上图中一个像素点是4个字节其中bit31-bit24 是Alpha通道bit23-bit16是RED通道bit15-bit8是GREEN通道bit7~bit0是BLUE通道。所以红色对应的值就是0X00FF0000蓝色对应的值就是0X000000FF绿色对应的值为0X0000FF00。通过调节R、G、B的比例可以产生其它的颜色比如0X00FFFF00就是黄色0X00000000就是黑色0X00FFFFFF就是白色。
LCD屏幕接口
LCD屏幕或者说显示器有很多种接口比如在显示器上常见的VGA、HDMI、DP等等但是STM32MP1开发板不支持这些接口。STM32MP1支持RGB接口的LCDRGBLCD 接口的信号线如下图所示 上图就是RGBLCD的信号线R[7:0]、G[7:0]和B[7:0]这24根是数据线DE、VSYNC、HSYNC和PCLK这四根是控制信号线。RGB LCD一般有两种驱动模式DE模式和HV模式这两个模式的区别是DE模式需要用到DE信号线而HV模式不需要用到DE信号线在DE模式下是可以不需要HSYNC信号线的即使不接HSYNC信号线LCD也可以正常工作。
ALIENTEK一共有四款RGB LCD屏幕这个针对STM32MP157开发板的linux驱动开发教程是以ATK-7016这款屏幕为例讲解(鼓励你买贵的当然我确实买了这个)ATK-7016的屏幕接口原理图如下图所示 图中J1就是对外接口是一个40PIN的FPC座(0.5mm间距)通过FPC线可以连接到STM32MP1开发板上面。该接口十分完善采用RGB888格式并支持DEHV模式还支持触摸屏和背光控制。右侧的几个电阻并不是都焊接的可以根据自己需要而选择是否焊接(正点原子出厂屏幕不能做修改 )。默认情况R1和R6焊接设置LCD_LR和LCD_UD控制LCD的扫描方向是从左到右从上到下(横屏看)。而LCD_R7/G7/B7则用来设置LCD的 ID由于RGBLCD没有读写寄存器也就没有所谓的ID这里通过在模块上面控制R7/G7/B7的上/下拉来自定义 LCD模块的ID帮助SOC判断当前LCD面板的分辨率和相关参数以提高程序兼容性。这几个位的设置关系如下图所示 ATK-7016模块就设置M2:M0010即可。这样在程序里面读取LCD_R7/G7/B7 得到M0:M2的值从而判断RGBLCD模块的型号并执行不同的配置即可实现不同 LCD模块的兼容。
LCD时间参数
如果将LCD显示一帧图像按照从左至右、从上到下的顺序扫描每个像素点并且在像素画上对应的颜色当画到最后一个像素点的时候一幅图像就绘制好了。假如一个LCD的分辨率为1024*600那么其扫描如下图所示 结合上图来看一下LCD是怎么扫描显示一帧图像的。一帧图像也是由一行一行组成的。HSYNC是水平同步信号也叫做行同步信号当产生此信号的话就表示开始显示新 的一行了所以此信号都是在上图的最左边。VSYNC信号是垂直同步信号也叫做帧同步信号当产生此信号的话就表示开始显示新的一帧图像了所以此信号在上图的左上角。
当显示完一行以后会发出HSYNC信号此时电子枪就会关闭然后迅速的移动到屏幕的左边当HSYNC信号结束以后就可以显示新的一行数据了电子枪就会重新打开。在HSYNC信号结束到电子枪重新打开之间会插入一段延时这段延时就是上图中的HBP。当显示完一行以后就会关闭电子枪等待HSYNC信号产生关闭电子枪到HSYNC信号产生之间会插入一段延时这段延时就是上图中的HFP信号。同理当显示完一帧图像以后电子枪也会关闭然后等到VSYNC信号产生期间也会加入一段延时这段延时就是上图的VFP。VSYNC信号产生电子枪移动到左上角当VSYNC信号结束以后电子枪重新打开中间也会加入一段延时这段延时就是上图中的VBP。
HBP、 HFP、 VBP和 VFP就是导致上图中黑边的原因RGB LCD屏幕内部是有一个IC的发送一行或者一帧数据给ICIC是需要反应时间的。通过这段反应时间可以让IC识别到一行数据扫描完了要换行了或者一帧图像扫描完了要开始下一帧图像显示了。因此在LCD屏幕中继续存在HBP、HFP、VPB和VFP这四个参数的主要目的是为了锁定有效的像素数据。这四个时间是LCD重要的时间参数后面编写LCD驱动的时候要用到的至于这四个时间参数具体值是多少那要需要去查看所使用的LCD数据手册了。
RGB LCD屏幕时序
先看一下行显示对应的时序图如下图所示 上图就是RGB LCD的行显示时序来分析一下其中重要几个的参数
HSYNC行同步信号当此信号有效的话就表示开始显示新的一行数据查阅所使用的LCD数据手册可以知道此信号是低电平有效还是高电平有效假设此时是低电平有效。HSPW有些地方也叫做thp是HSYNC信号宽度也就是HSYNC信号持续时间。HSYNC信号不是一个脉冲而是需要持续一段时间才是有效的单位为CLK。HBP有些地方叫做thb术语叫做行同步信号后肩单位是CLK。HOZVAL有些地方叫做thd显示一行数据所需的时间假如屏幕分辨率为1024*600那么HOZVAL就是1024单位为CLK。HFP有些地方叫做thf术语叫做行同步信号前肩单位是CLK。
当HSYNC信号发出以后需要等待HSPWHBP个CLK时间才会接收到真正有效的像素数据。当显示完一行数据以后需要等待HFP个CLK时间才能发出下一个HSYNC信号所以显示一行所需要的时间就是HSPW HBP HOZVAL HFP。
一帧图像就是由很多个行组成的RGB LCD的帧显示时序如下图所示 上图就是RGB LCD的帧显示时序来分析一下其中重要的几个参数
VSYNC帧同步信号当此信号有效的话就表示开始显示新的一帧数据查阅所使用的LCD数据手册可以知道此信号是低电平有效还是高电平有效假设此时是低电平有效。VSPW有些地方也叫做tvp是VSYNC信号宽度也就是VSYNC信号持续时间单位为1行的时间。VBP有些地方叫做tvb术语叫做帧同步信号后肩单位为1行的时间。LINE有些地方叫做tvd显示一帧有效数据所需的时间假如屏幕分辨率为1024*600那么LINE就是600行的时间。VFP有些地方叫做tvf术语叫做帧同步信号前肩单位为1行的时间。
显示一帧所需要的时间就是VSPWVBPLINEVFP个行时间最终的计算公式
T (VSPWVBPLINEVFP) * (HSPW HBP HOZVAL HFP)
因此在配置一款RGB LCD的时候需要知道这几个参数HOZVAL(屏幕有效宽度)、LINE(屏幕有效高度)、HBP、HSPW、HFP、VSPW、VBP和VFP。 ALIENTEK四款RGB LCD屏幕的参数如下图所示 像素时钟
像素时钟就是RGB LCD的时钟信号以ATK7016这款屏幕为例显示一帧图像所需要的时钟数就是 (VSPWVBPLINEVFP) * (HSPW HBP HOZVAL HFP) (3 20 600 12) * (20 140 1024 160) 635 * 1344 853440。
显示一帧图像需要853440个时钟数那么显示60帧就是853440 * 60 51206400 51.2M所以像素时钟就是51.2MHz。
显存
如果采用ARGB8888格式的话一个像素需要4个字节的内存来存放像素数据那么1024600分辨率就需要1024600*42457600B2.4MB内存。但 是RGB LCD内部是没有内存的所以就需要在开发板上的DDR3中分出一段内存作为RGB LCD屏幕的显存如果要在屏幕上显示什么图像的话直接操作这部分显存即可。
LTDC接口
LTDC是STM32MP1自带的液晶屏幕接口用于连接RGB LCD接口的屏幕LTDC接口特性如下
24位RGB并行像素输出每像素8位(RGB888)。2个带有专用FIFO的显示层(FIFO深度 64x64位)。查色表(CLUT)每个图层最高256 种颜色 (256x24)位。可针对不同显示面板编程时序。每层有多达8个输入颜色格式可供选择分别为ARGB8888、RGB888、RGB565、ARGB1555、ARGB4444、L8、AL44、AL88。
下图为LTDC功能框架图 从图可以看出LTDC的信号可以分为两类4个控制信号(LCD_CLK像素时钟、LCD_HSYNC水平同步、LCD_VSYNC垂直同步、LCD_DE数据有效)和3个RGB数据信号(8bit x 3)。
DRM驱动框架
DRM简介
在Linux系统中主流的显示框架有两种DRM(Direct Rendering Module)框架和FB(FrameBuffer)框架。 FB框架不能处理基于 3D加速GPU显卡DRM是可以统一管理GPU显示所以DRM相对于FB更能适应新的显示硬件。比如DRM支持多层合成、支持VSYNC、支持DMA-BUF、支持fence机制等等。
下图就是一个DRM驱动框架包括两部分DRM core和DRM driver。DRM core提供了一个基本的DRM框架DRM driver就可以注册进DRM框架同时为用户空间提供一组ioctl。 1ibdrm对底层接口(DRM driver提供的ioctl)进行封装向上层提供统一的API接口。DRM driver包含了GEM模块和KMS模块这两模块也分为好几个小模块。 先介绍上图中的部分名词意思和作用
图形执行管理器(GEM)全称Graphics Execution Manager这是一个内存管理器主要负责内存的分配和释放可以调用GPU。DUMB这是一个dumb缓冲区主要负责一些简单的buffer显示可以通过CPU直接渲染dumbGPU不会使用dumb。内核显示模式设置(KMS)全称Kernel Mode Setting主要负责显示的控制包括屏幕分辨率、屏幕刷新率和颜色深度等等。CRTC就是指显示控制器在DRM里有多个显存就可以通过操作CRTC来控制要显示那个显存。Encoder负责从CRTC里输出的timing时转换成外部设备所需要的信号的模块同时也负责控制LCD的显示。Connector连接物理显示设备的连接器比如DSI、HDMI等等。Plane负责获取显存在输出到CRTC里说明CRTC必须要有一个Plane。帧缓冲(FB)能够显示图层的buffer。
接着看下DRM driver里的两大模块GEM和KMS是如何连接显示器的如下图所示 上图里蓝色框表示KMS里的模块。plane是连接crtc和framebuffer的纽带而encoder是连接crtc和connector的纽带。GEM是负责和 物理的buffer打交道不是framebuffer。plane把获取到显存输出到crtc里crtc通过connector接口输出到显示器。
ST官方的DRM驱动框架介绍
在Linux系统中DRM驱动的核心主要就一个drm_driver结构体驱动程序要初始化drm_driver结构体然后调用drm_dev_init函数将其注册到DRM core。
简单分析ST官方编写的在Linux下的DRM驱动 ,打开stm32mp151.dtsi然后找到ltdc节点内容如下所示 示例代码40.2.2.1中的ltdc节点信息是所有使用STM32MP1芯片的板子所共有的并不是 完整的ltdc节点信息。可以看出ltdc节点的compatible属性值为“st,stm32-ltdc”因此在Linux源码中搜索这个字符串就可以找到STM32MP1的DRM驱动文件这个文件为“drivers/gpu/drm/stm/drv.c”文件打开此文件找的如下内容所示 从示例代码40.2.2.2可以看出这是一个标准的platform驱动当驱动和设备匹配以后stm_drm_platform_probe函数就会执行。
和其他设备驱动一样DRM也分为DRM设备和DRM驱动drm_device结构体为DRM设备drm_driver为DRM驱动依次来看一下这两个结构体。
drm_device结构体
drm_device结构体定义在include/drm/drm_device.h文件里内容如下 (有省略) 在编写DRM驱动的时候需要自行申请drm_device内存并且使用初始化这个可以直接通过drm_dev_alloc函数来完成函数原型如下
struct drm_device *drm_dev_alloc(struct drm_driver *driver, struct device *parent)此函数会先调用kzalloc为drm_device分配内存然后调用drm_dev_init初始化drm_device。函数参数和返回值含义如下
driverdrm_driver结构体指针也就是DRM设备对应的DRM 驱动。parent父设备。返回值返回分配成功的新DRM设备如果返回ERR_PTR的话就表示drm_device申请失败。
drm_device分配成功以后还需要使用drm_dev_register函数向内核注册函数原型如下
int drm_dev_register(struct drm_device *dev, unsigned long flags)函数参数和返回值含义如下
dev需要注册到内核的drm_device。flags传递给驱动.load函数的标志。返回值0成功负数失败。
drm_driver结构体
Linux内核为DRM驱动提供一个叫做drm_driver的结构体drm_driver结构体包含了DRM驱动的完整属性和操作集合因此每一个DRM驱动都必须有一个drm_driver。 drm_driver结构体定义在include/drm/drm_drv.h文件里内容如下(有省略): drm_driver结构体的成员变量很多重点关注driver_features、fops和dumb_create。
第57行dumb_create是一个回调函数用于创建gem对象并分配物理buffer。
第74行driver_features用来描述驱动特性枚举类型drm_driver_feature定义了可以选择 的驱动特性
DRIVER_GEM驱动使用GEM内存管理此特性必须选中DRIVER_MODESET驱动支持模式设置接口(KMS)。DRIVER_RENDER驱动支持专用渲染节点。DRIVER_ATOMIC驱动提供完整的原子操作以供用户空间API函数操作。DRIVER_SYNCOBJ驱动支持SYNCOBJ用于命令提交的显式同步。DRIVER_SYNCOBJ_TIMELINE驱动支持SYNCOBJ时间线。DRIVER_USE_AGP驱动程序使用AGP接口DRM核心将管理AGP资源。DRIVER_LEGACY表明这是一个使用影子附着的旧驱动程序不使用。DRIVER_PCI_DMA驱动支持PCI DMA。DRIVER_SG驱动可以提供scatter/gather DMA功能。DRIVER_HAVE_DMA驱动支持DMA。DRIVER_HAVE_IRQ驱动支持IRQ旧驱动使用。DRIVER_KMS_LEGACY_CONTEXT仅供nouveau使用
第77行fops就是一个简单的字符设备接口结构体。
STM32MP1的LTDC是个platform驱动当设备和驱动匹配成功以后stm_drm_platform_probe函数就会执行 函数内容如下 第201行 调用drm_dev_alloc函数此函数主要完成两个功能
给drm_device分配内存。通过drm_dev_init函数初始化drm_device。
drm_dev_alloc会通过调用drm_dev_init函数将drm_driver和drm_device联系起来drm_device结构体里面有个drvier指针成员变量此成员变量指向DRM设备对应的DRM驱动的。因此drm_dev_init函数会通过将drm_device下的driver成员变量指向drm_driver来实现两者相连。
第205 行drv_load这个函数就是初始化KMS。
第209 行注册drm_device对象进DRM core。
接下来看下drv_load函数内容如下
示例代码40.2.2.6 drv_load函数
29 #define STM_MAX_FB_WIDTH 2048
30 #define STM_MAX_FB_HEIGHT 2048
31
32 static const struct drm_mode_config_funcs drv_mode_config_funcs {
33 .fb_create drm_gem_fb_create,
34 .atomic_check drm_atomic_helper_check,
35 .atomic_commit drm_atomic_helper_commit,
36 };
......
79 static int drv_load(struct drm_device *ddev)
80 {
81 struct platform_device *pdev to_platform_device(ddev-dev);
82 struct ltdc_device *ldev;
83 int ret;
84
85 DRM_DEBUG(%s\n, __func__);
86
87 ldev devm_kzalloc(ddev-dev, sizeof(*ldev), GFP_KERNEL);
88 if (!ldev)
89 return -ENOMEM;
90
91 ddev-dev_private (void *)ldev;
92
93 drm_mode_config_init(ddev);
94
95 /*
96 * set max width and height as default value.
97 * this value would be used to check framebuffer size limitation
98 * at drm_mode_addfb().
99 */
100 ddev-mode_config.min_width 0;
101 ddev-mode_config.min_height 0;
102 ddev-mode_config.max_width STM_MAX_FB_WIDTH;
103 ddev-mode_config.max_height STM_MAX_FB_HEIGHT;
104 ddev-mode_config.funcs drv_mode_config_funcs;
105
106 ret ltdc_load(ddev);
107 if (ret)
108 goto err;
109
110 drm_mode_config_reset(ddev);
111 drm_kms_helper_poll_init(ddev);
112
113 platform_set_drvdata(pdev, ddev);
114
115 return 0;
116 err:
117 drm_mode_config_cleanup(ddev);
118 return ret;
119 }第102行设置DRM驱动X轴(宽度)最大支持2048个像素。
第103行设置DRM驱动Y轴(宽度)最大支持2048个像素结合上一行可以看出驱动里面设置的最大分辨率支持2048*2048。但是根据STM32MP157手册所描述最大支持1366*768分辨率的屏幕。
第104行设置framebuffer的回调函数结构体。
第106行这里要引入drm_panel结构体此结构体作用是提供一堆控制回调函数。比如屏幕参数回调函数背光控制函数等等。ltdc_load函数是负责初始化ltdc接口(同时connector和encoder一起初始化)。在connector初始化的时候就会调用drm_panel结构体里的获取屏幕参数函数(所以只需要提供一个屏的驱动就能正常显示了)。通常encoder和connector是放在同一个驱动初始化的目的是为了方便驱动程序设计。
重点来了 要完成整个DRM驱动的正常初始化前面的GEM和KMS这些模块ST官方 已经提供了只需提供一个drm_panel对象就行了。打开“include/drm/drm_panel.h”文 件找到如下内容所示 这个结构体主要是第5行funcs对象成员。在这里DRM驱动分析基本完成了。
RGB LCD驱动分析(屏幕驱动)
drm_panel就是DRM驱动的核心结构体需要实现此结构体。先来看一下panel_simple结构体。这里用到了面向对象的思维drm_panel结构体是基类panel_simple在drm_panel基础上增加了一些成员变量相当于继承类。
使用的是RGB LCD屏所以LCD驱动文件为drivers/gpu/drm/panel/panel-simple.c打文件找到如下内容所示 panel_simple结构体用来管理RGB LCD设备。
第100行base成员变量为drm_panel结构体类型。可以看出panel_simple就是在drm_panel的基础上发展而来的在DRM驱动注册的时候就会回调base-funcs。
第105行desc属性就是RGB屏参数结构体。
第107行屏的背光结构体。
接着看下如何跟设备树匹配的 找到如下所示内容 示例代码40.3.1中这是一个标准的platform驱动框架第3494行就是匹配表。platform_of_match内容如下所示(有省略) 可以看出platform_of_match里面有大量的匹配项分别针对不同的屏幕比如
第3135行这就是一个匹配项compatible内容为“ampire,am-480272h3tmqw-t01h”。
第3136行在platform框架里有个data成员变量这个是一个void类型的指针这里指向ampire_am_480272h3tmqw_t01h内容如 下所示 第516-525行drm_display_mode结构体就是用来设置屏幕参数。
第529行定义一个panel_desc结构体对象。
第530行modes变量设置为ampire_am_480272h3tmqw_t01h_mode。
第531行设置modes的数量。
第532行设置屏幕为8bit。
第533-536行设置屏幕实际显示区域的物理宽度单位为毫米此屏幕尺寸为105mm×67mm。
第537行bus_format属性设置总线模式include/uapi/linux/media-bus-format.h里面定义了所有可选的总线类型 可以看出Linux内核支持很多种不同的总线格式比如RGB、YUV、Bayer等。以MEDIA_BUS_FMT_RGB888_1X24为例看看这个总线格式的含义
从“RGB888”可以看出这是一个RGB888格式的。后面的“1X24”表示一个像素点使用24bit如果是2X8就表示一个像素点使用2个8bit表示。有的右面也会有“BE”或“LE”BE表示最高位先传输LE表示最低位先传输。
假设设备树里有个设备节点的compatible属性为“ampire,am-480272h3tmqw-t01h”那么就 会和驱动匹配成功然后运行panel_simple_platform_probe函数此函数的内容如下所示 第3474行使用of_match_node函数查找匹配的设备ID。
第3478行当得到匹配的设备ID(of_device_id)以后就可以通过提取data成员变量得到屏幕参数信息比如此处id-data就是ampire_am_480272h3tmqw_t01h。最后调用panel_simple_probe函数将其注册到内核panel_simple_probe函数也定义在drivers/gpu/drm/panel/panel-simple.c文件中函数内容如下所示
示例代码40.3.7 panel_simple_probe函数
414 static int panel_simple_probe(struct device *dev, const struct panel_desc *desc)
415 {
416 struct device_node *backlight, *ddc;
417 struct panel_simple *panel;
418 struct display_timing dt;
419 int err;
420
421 panel devm_kzalloc(dev, sizeof(*panel), GFP_KERNEL);
422 if (!panel)
423 return -ENOMEM;
424
425 panel-enabled false;
426 panel-prepared false;
427 panel-desc desc;
428
......
444 backlight of_parse_phandle(dev-of_node, backlight, 0);
445 if (backlight) {
446 panel-backlight of_find_backlight_by_node(backlight);
447 of_node_put(backlight);
448
449 if (!panel-backlight)
450 return -EPROBE_DEFER;
451 }
452
......
467 drm_panel_init(panel-base);
468 panel-base.dev dev;
469 panel-base.funcs panel_simple_funcs;
470
471 err drm_panel_add(panel-base);
472 if (err 0)
473 goto free_ddc;
474
475 dev_set_drvdata(dev, panel);
476
477 return 0;
478
479 free_ddc:
480 if (panel-ddc)
481 put_device(panel-ddc-dev);
482 free_backlight:
483 if (panel-backlight)
484 put_device(panel-backlight-dev);
485
486 return err;
487 }第427行设置屏幕参数。
第444行从设备树里获取背光节点所以设备树要提供“backlight”属性。
第467行用drm_panel_init函数初始化屏幕。
第469行设置panel_simple的回调函数为DRM驱动注册的时候提供屏的参数。
第471行把屏幕注册到内核。
LCD屏的驱动分析结束总结一下添加自己的屏要做那些操作
在根节点下提供一个LCD设备树包含背光的节点和引用ltdc节点。在panel-simple.c文件里的platform_of_match结构体里添加一组设备ID此设备ID对应所使用的屏幕重点是屏幕参数panel_desc结构体。
硬件原理图分析
在STM32MP1开发板里有一个RGB LCD接口其原理如下所示 上图左边对应的是LCD接口引脚图右边为STM32MP1开发板RGB LCD接口的原理图。
LCD驱动程序编写
修改设备树
首先就是修改设备树重点要注意以下几点
LCD所使用的IO配置由于STM32MP1的IO支持复用所以实际所使用的LCD引脚可能不一样。因此首先要根据实际硬件设计修改LCD所有使用的IO配置。LDTC接口节点修改修改相应的属性值告诉内核要指定那个输出接口比如输出到RGB LCD屏、MIPI屏等。本实验用到是正点原子ATK7016屏也就是RGB LCD屏。输出接口节点的编写比如本实验用到的RGB LCD屏需要在根节点下添加RGB LCD节点。LCD背光节点信息修改要根据实际所使用的背光IO来修改相应的设备节点信息。
接下来依次来看一下上面这三个节点如何去修改。
LCD屏幕使用IO配置
首先要检查一下设备树中LCD所使用的IO配置这个其实ST都已经写好了需要修改不过还是要看一下。打开arch/arm/boot/dts/stm32mp15-pinctrl.dtsi文件在pinctrl节点中找到如下内容
示例代码40.5.1.1 ltdc的pinmux节点
1 ltdc_pins_b: ltdc-b-0 {
2 pins {
3 pinmux STM32_PINMUX(I, 14, AF14), /* LCD_CLK */
4 STM32_PINMUX(I, 12, AF14), /* LCD_HSYNC */
5 STM32_PINMUX(I, 13, AF14), /* LCD_VSYNC */
6 STM32_PINMUX(K, 7, AF14), /* LCD_DE */
7 STM32_PINMUX(I, 15, AF14), /* LCD_R0 */
8 STM32_PINMUX(J, 0, AF14), /* LCD_R1 */
9 STM32_PINMUX(J, 1, AF14), /* LCD_R2 */
10 STM32_PINMUX(J, 2, AF14), /* LCD_R3 */
11 STM32_PINMUX(J, 3, AF14), /* LCD_R4 */
12 STM32_PINMUX(J, 4, AF14), /* LCD_R5 */
13 STM32_PINMUX(J, 5, AF14), /* LCD_R6 */
14 STM32_PINMUX(J, 6, AF14), /* LCD_R7 */
15 STM32_PINMUX(J, 7, AF14), /* LCD_G0 */
16 STM32_PINMUX(J, 8, AF14), /* LCD_G1 */
17 STM32_PINMUX(J, 9, AF14), /* LCD_G2 */
18 STM32_PINMUX(J, 10, AF14), /* LCD_G3 */
19 STM32_PINMUX(J, 11, AF14), /* LCD_G4 */
20 STM32_PINMUX(K, 0, AF14), /* LCD_G5 */
21 STM32_PINMUX(K, 1, AF14), /* LCD_G6 */
22 STM32_PINMUX(K, 2, AF14), /* LCD_G7 */
23 STM32_PINMUX(J, 12, AF14), /* LCD_B0 */
24 STM32_PINMUX(J, 13, AF14), /* LCD_B1 */
25 STM32_PINMUX(J, 14, AF14), /* LCD_B2 */
26 STM32_PINMUX(J, 15, AF14), /* LCD_B3 */
27 STM32_PINMUX(K, 3, AF14), /* LCD_B4 */
28 STM32_PINMUX(K, 4, AF14), /* LCD_B5 */
29 STM32_PINMUX(K, 5, AF14), /* LCD_B6 */
30 STM32_PINMUX(K, 6, AF14); /* LCD_B7 */
31 bias-disable;
32 drive-push-pull;
33 slew-rate 1;
34 };
35 };
36
37 ltdc_pins_sleep_b: ltdc-b-1 {
38 pins {
39 pinmux STM32_PINMUX(I, 14, ANALOG), /* LCD_CLK */
40 STM32_PINMUX(I, 12, ANALOG), /* LCD_HSYNC */
41 STM32_PINMUX(I, 13, ANALOG), /* LCD_VSYNC */
42 STM32_PINMUX(K, 7, ANALOG), /* LCD_DE */
43 STM32_PINMUX(I, 15, ANALOG), /* LCD_R0 */
44 STM32_PINMUX(J, 0, ANALOG), /* LCD_R1 */
45 STM32_PINMUX(J, 1, ANALOG), /* LCD_R2 */
46 STM32_PINMUX(J, 2, ANALOG), /* LCD_R3 */
47 STM32_PINMUX(J, 3, ANALOG), /* LCD_R4 */
48 STM32_PINMUX(J, 4, ANALOG), /* LCD_R5 */
49 STM32_PINMUX(J, 5, ANALOG), /* LCD_R6 */
50 STM32_PINMUX(J, 6, ANALOG), /* LCD_R7 */
51 STM32_PINMUX(J, 7, ANALOG), /* LCD_G0 */
52 STM32_PINMUX(J, 8, ANALOG), /* LCD_G1 */
53 STM32_PINMUX(J, 9, ANALOG), /* LCD_G2 */
54 STM32_PINMUX(J, 10, ANALOG), /* LCD_G3 */
55 STM32_PINMUX(J, 11, ANALOG), /* LCD_G4 */
56 STM32_PINMUX(K, 0, ANALOG), /* LCD_G5 */
57 STM32_PINMUX(K, 1, ANALOG), /* LCD_G6 */
58 STM32_PINMUX(K, 2, ANALOG), /* LCD_G7 */
59 STM32_PINMUX(J, 12, ANALOG), /* LCD_B0 */
60 STM32_PINMUX(J, 13, ANALOG), /* LCD_B1 */
61 STM32_PINMUX(J, 14, ANALOG), /* LCD_B2 */
62 STM32_PINMUX(J, 15, ANALOG), /* LCD_B3 */
63 STM32_PINMUX(K, 3, ANALOG), /* LCD_B4 */
64 STM32_PINMUX(K, 4, ANALOG), /* LCD_B5 */
65 STM32_PINMUX(K, 5, ANALOG), /* LCD_B6 */
66 STM32_PINMUX(K, 6, ANALOG); /* LCD_B7 */
67 };
68 };第1-35行子节点ltdc_pins_b为RGB LCD的24根数据线配置项和4根控制线配置项。
第37-67行子节点ltdc_pins_sleep_b同样也是RGB LCD的24根数据线配置项和4根 控制线配置项。
可以看出这里有两个pinmux分别为ltdc_pins_b和ltdc_pins_sleep_b其中ltdc_pins_b是在默认模式下的RGB LCD pinmux配置ltdc_pins_sleep_b是在sleep模式下RGB LCD的pinmux配置。如果想要LCD屏进入睡眠模式就切换为sleep模式。正点原子STM32MP1开发板RGB LCD屏幕所使用的引脚和ST官方开发板一致因此示 例代码40.5.1不需要做任何修改。如果所使用的开发板其LCD引脚和正点原子STM32MP1开发板不一致一定要根据自己的实际硬件修改示例代码40.5.1.1。
LDTC接口节点修改
LTDC节点在stm32mp151.dtsi里已经写好一部分了只需要告诉LTDC节点输出到RGB LCD 屏里就行。在stm32mp157d-atk.dts文件添加如下内容所示
示例代码40.5.5.2 ltdc节点
1 ltdc {
2 pinctrl-names default, sleep;
3 pinctrl-0 ltdc_pins_b;
4 pinctrl-1 ltdc_pins_sleep_b;
5 status okay;
6
7 port {
8 #address-cells 1;
9 #size-cells 0;
10
11 ltdc_ep0_out: endpoint0 {
12 reg 0;
13 remote-endpoint rgb_panel_in;
14 };
15 };
16 };第2-4行给LTDC设置了两个pinmux模式pinctrl-0为default模式pinctrl-1为sleep模式系统默认使用default模式。
第8-9行设置port下子节点的reg属性的地址信息描述。
第11-14行在port下添加了一个子节点为ltdc_ep0_out。在第12行里reg属性值为0。
在第13行里remote-endpoint属性是用来告诉ltdc节点输出到那里是用RGB LCD屏做 实验所以输出到rgb_panel_in接口。
输出接口编写
还需要添加一个LCD设备树节点在stm32mp157d-atk.dts文件的根节点“/”下添加如下所示内容
示例代码40.5.5.3 LCD屏的设备节点
1 panel_rgb: panel-rgb {
2 compatible alientek,lcd-rgb;
3 backlight backlight;
4 status okay;
5
6 port {
7 rgb_panel_in: endpoint {
8 remote-endpoint ltdc_ep0_out;
9 };
10 };
11 };第2行设置compatible属性值为“alientek,lcd-rgb”所以稍后要在panel-simple.c文件里的platform_of_match数组增加一个of_device_id结构体此结构体的compatible成员属性 值为“alientek,lcd-rgb”。
第3行设置backlight属性值为“backlight”此属性值为引用背光节点。
第6-9行告诉LCD驱动要从LTDC节点里获取显示数据。第8行就是引用ltdc节点。
在panel-simple.c中添加屏幕参数
接着就要在panel-simple.c文件里面添加屏幕参数打开此文件找到platform_of_match数组添加如下内容
示例代码40.5.2.1 屏的匹配属性
1 .compatible alientek,lcd-rgb,
2 .data alientek_desc,添加完成以后如下图所示 在上图里的alientek_desc保存参数还需要继续在panel-simple.c里面实现alientek_desc添加如下所示代码
示例代码40.5.2.2 alientek_desc结构体
1 static const struct drm_display_mode ATK7016_mode {
2 .clock 51200, /* LCD像素时钟单位KHz */
3 .hdisplay 1024, /* LCD X轴像素个数 */
4 .hsync_start 1024 140, /* LCD X轴hbp的像素个数 */
5 .hsync_end 1024 140 20, /* LCD X轴hbphspw的像素个数 */
6 .htotal 1024 140 20 160,/* LCD X轴hbphspwhfp */
7 .vdisplay 600, /* LCD Y轴像素个数 */
8 .vsync_start 600 20, /* LCD Y轴vbp的像素个数 */
9 .vsync_end 600 20 3, /* LCD Y轴vbpvspw的像素个数 */
10 .vtotal 600 20 3 12,/* LCD Y轴vbpvspwvfp */
11 .vrefresh 60, /* LCD的刷新频率为60HZ */
12 };
13
14 static const struct panel_desc alientek_desc {
15 .modes ATK7016_mode,
16 .num_modes 1,
17 .bus_format MEDIA_BUS_FMT_RGB888_1X24,
18 };在示例代码40.5.2.2就是ATK7016屏的参数并且设置为RGB888模式。如果使用的其他 屏幕请按照屏幕手册对应的时序参数设置。
第2-11行ATK7016屏幕时序参数根据自己所使用的屏幕修改即可。
LCD屏幕背光节点信息
背光PWM节点设置
LCD背光使用PWM来控制通过PWM波形来调节屏幕亮度。
正点原子的LCD接口背光控制IO连接到了STM32MP1的PD13引脚上需要将PD13复用为TIM4_CH2然后配置TIM4的CH2输出PWM信号然后通过此PWM信号来控制LCD屏幕背光的亮度接着来看一下如何在设备树中添加背光节点信息。
首先是PD13这个pinmux的配置在stm32mp15-pinctrl.dtsi中找到如下内容
示例代码40.5.3.1 PD13的pinmux配置
1 pwm4_pins_b: pwm4-1 {
2 pins {
3 pinmux STM32_PINMUX(D, 13, AF2); /* TIM4_CH2 */
4 bias-pull-down;
5 drive-push-pull;
6 slew-rate 0;
7 };
8 };
9
10 pwm4_sleep_pins_b: pwm4-sleep-1 {
11 pins {
12 pinmux STM32_PINMUX(D, 13, ANALOG); /* TIM4_CH2 */
13 };
14 };示例代码40.5.3.1默认设置了PD13引脚的两种 pinmux配置从第3行可以看出设置PD13复用为TIM4_CH2并且设置电气属性为内部下拉和推挽输出。这是因为ST官方开发板就使用了PD13作为LCD的背光控制引脚正点原子STM32MP1开发板也使用PD13作为LCD背光控制引脚所以不需要修改。如果使用其他引脚作为LCD的背光控制引脚那么就需要进行修改。
继续在stm32mp157d-atk.dts文件中向timers4追加内容如下所示
示例代码40.5.3.2 向tim4节点追加内容
1 timers4 {
2 status okay;
3 /* spare dmas for other usage */
4 /delete-property/dmas;
5 /delete-property/dma-names;
6 pwm4: pwm {
7 pinctrl-0 pwm4_pins_b;
8 pinctrl-1 pwm4_sleep_pins_b;
9 pinctrl-names default, sleep;
10 #pwm-cells 2;
11 status okay;
12 };
13 };第2行把status设置为okay。
第4-5行设置此节点不用dma。
第6行pwm4是为pwm设置的一个别名。
第7-9行设置PWM所使用的IO配置。
第10行此参数是用来规定pwms属性的参数。比如#pwm-cells 2表示pwms属性有2个参数如下所示 pwms pwm4 1 5000000
其中pwm4表示使用PWM4后面两个是参数其中1表示使用PWM4的通道2(通道从1开始)5000000表示为200Hz。
如果背光用的其他pwm通道比如pwm2那么就需要仿照示例代码40.5.3.2向timers2节点追加相应的内容。比如如果要设置TIM2_CH4那么timers2里的pwm节点下的pinmux配置就是TIM2_CH4。
backlight节点设置
到这里PWM和相关的IO已经准备好了但是Linux系统怎么知道TIM4_CH2就是控制LCD背光的呢因此还需要一个节点来将LCD背光和TIM4_CH2连接起来。这个节点就是backlightbacklight节点描述可以参考Documentation/devicetree/bindings/leds/backlight/pwm-backlight.txt这个文档此文档详细讲解了backlight节点该如何去创建大概总结一下
节点名称要为“backlight”。节点的compatible属性值要为“pwm-backlight”因此可以通过在Linux内核中搜索“pwm-backlight”来查找PWM背光控制驱动程序这个驱动程序文件为drivers/video/backlight/pwm_bl.c。pwms属性用于描述背光所使用的PWM的通道以及PWM频率比如本章要使用的pwm4的第二个通道pwm频率设置为200Hz。brightness-levels属性描述亮度级别范围为0~2550表示PWM占空比为0%也就是亮度最低255表示100%占空比也就是亮度最高。至于设置几级亮度可以自行填写此属性。default-brightness-level属性为默认亮度级别。
根据上述5点设置 backlight节点在根节点创建一个backlight节点在 tm32mp157d-atk.dts文件中新建内容如下
示例代码40.5.3.3 backlight节点
1 backlight: backlight {
2 compatible pwm-backlight;
3 pwms pwm4 1 5000000;
4 brightness-levels 0 4 8 16 32 64 128 255;
5 power-supply v3v3;
6 default-brightness-level 7;
7 status okay;
8 };第3行设置背光使用pwm4的第二个通道PWM 频率为200Hz。
第4行设置8级背光(0-7)分别为0、4、8、16、32、64、128、255对应占空比为0%、1.57%、3.13%、6.27%、12.55%、25.1%、50.19%、100%如果嫌少的话可以自行添加一 些其他的背光等级值。
第5行设置默认背光等级为7也就是100%的亮度。
注意背光的驱动代码有点bug如果设置0级屏不会灭屏打开pwm_bl.c文件找到123行如下图所示 将上图中123行的“”改为‘‘’即可。
关于背光的设备树节点信息就讲到这里整个的LCD设备树节点内容就讲完了按照这些节点内容配置自己的开发板即可。
运行测试
LCD屏幕的DRM基本测试
编译新的内核和设备树
输入如下命令重新编译Linux内核和设备树 make uImage dtbs LOADADDR0xC2000040 -j16
编译完成后用新的设备树和Linux内核启动。
配置内核
ST官方的默认配置已经使能了DRM驱动还是要告诉各位如何配置内核打开Linux内核图形化配置界面按下路径找到对应的配置项 - Device Drivers - Graphics support [*] Direct Rendering Manager (XFree86 4.1.0 and higher DRI support) //选中 [*] DRM Support for STMicroelectronics SoC Series //选中 - Display Panels [*] support for simple panels //选中 - Backlight LCD device support [*] Generic PWM based Backlight Driver //选中
用新的uImage镜像和stm32mp157d-atk.dtb设备树用来启动内核如果设置正确那么在文件系统 /sys/class/drm/路径下有如下图所示内容 如果没有出现上图的内容就要检查一下设备树和panel-simple.c文件是否设置正确。因为Linux一切皆文件所以DRM驱动肯定会提供一个接口给用户使用接口为“/dev/dri/card0”。可以通过此接口来设置LCD的显示。
文件系统使能libdrm库
没有libdrm库是不能调用drm驱动的所以要在文件系统使能libdrm库跳转到buildroot-2020.02.6的目录下打开buildroot的图形化配置界面 根据如下配置去使能libdrm库。 Location: - Target packages - Libraries - Graphics -[*]libdrm //选中 -[*]Install test programs //选中
如下图所示 上图中的Install test programs配置会生成modetest命令此命令是用来测试DRM驱 动。保存并重新编译buildroot的文件系统然后直接将新得到的根文件系统解压到开发板正在使用的根文件系统中命令如下 sudo tar -axvf rootfs.tar
测试
重新启动开发板使用modetest命令进行测试输入如下命令可以查看modetest命令的使用方法 modetest --help
帮助信息如下图所示 先输入如下命令查看设备信息 modetest -M stm
-M指定模块这里查看“stm”这个设备。
输入命令以后就会打印出stm这个设备的详细信息比较长下图是一些重要信息 从上图可以看出Connectors的id为32CRTC的id为35这两个 ID很重要一会测试要用到要根据自己的实际情况填写。
输入如下命令测试DRM驱动 modetest -M stm -s 3235:1024x600
命令参数介绍如下
-M指定stm模块。-s32表示connectors的ID35表示CRTC的ID1024x600表示显示的模式。
显示结果如下图所示 LCD屏幕的FB基本测试
之前的学习中有提到KMS包含了FB框架。DRM驱动默认为CRTC用来控制CRTC是可以模仿FB框架实现使用FB接口。示例代码40.2.2.5中的第213行就是负责初始化基于CRTC的FB接口。只需在Linux内核图形化配置界面里配置以下选项。
使能DRM驱动的FB
配置路径如下 - Device Drivers - Graphics support - Direct Rendering Manager (XFree86 4.1.0 and higher DRI support) - [*]Enable legacy fbdev support for your modesetting driver //选中
如下图所示 使能PL110 - Device Drivers - Graphics support - Frame buffer Devices - Support for frame buffer devices - *ARM PrimeCell PL110 support //选中支持 /dev/fb0
配置如下图所示 使能Linux logo显示
Linux内核启动的时候可以选择显示小企鹅logo。打开Linux内核图形化配置界面按如下路径找到对应的配置项 - Device Drivers - Graphics support - [*] Bootup logo //选中 - [*] Standard black and white Linux logo (NEW) //选中 - [*] Standard 16-color Linux logo (NEW) //选中 - [*] Standard 224-color Linux logo (NEW) //选中
如下图所示 上图中这三个选项分别对应黑白、16位、24位色彩格式的logo把这三个都选中都编译进Linux内核里面。设置好以后保存退出重新编译Linux内核编译完成以后使用新编译出来uImage镜像启动系统在LCD屏幕左上角出现两个彩色的小企鹅logo(企鹅的个数和CPU的个数相同 )屏幕背景色为黑色如下图所示 设置LCD作为终端控制台
LCD作为终端控制台前提条件要实现FB接口。之前的学习过程中一直使用MobaXterm作为Linux开发板终端开发板通过串口和MobaXterm进行通信。现在已经驱动起来LCD并且提供了FB接口所以可以设置LCD作为终端也就是开发板使用自己的显示设备作为自己的终端接上键盘就可以直接在开发板上敲命令了将LCD设置为终端控制台的方法如下
设置uboot中的bootargs
重启开发板进入Linux命令行重新设置bootargs参数的console内容命令如下所示 setenv bootargs consoletty1 consolettySTM0,115200 root/dev/nfs nfsroot192.168.1.21:/home/liangwencong/linux/nfs/rootfs,prototcp rw ip192.168.1.246:192.168.1.21:192.168.1.1:255.255.255.0::eth0:off
注意红色字体部分设置console这里设置了两遍console第一次设置consoletty1也就是设置LCD屏幕为控制台第二遍又设置consolettySTM0,115200也就是设置串口也作为控制台。相当于打开了两个console一个是LCD一个是串口重启开发板就会发现LCD和串口都会显示Linux启动log信息。但是此时还不能使用LCD作为终端进行交互。
修改/etc/inittab文件
打开开发板根文件系统中的/etc/inittab文件在里面加入下面这一行 tty1::askfirst:-/bin/sh
添加完成以后的/etc/inittab文件内容如下图所示 修改完成以后保存/etc/inittab 并退出然后重启开发板重启以后开发板LCD屏幕最后一行会显示下面一行语句Please press Enter to activate this console至此就拥有了两套终端一个是基于串口的MobaXterm一个就是开发板的LCD屏幕但是为了方便调试以后还是以MobaXterm 为主。可以通过下面这一行命令向LCD 屏幕输出“hello linux” echo hello linux! /dev/tty1
屏幕显示如下图所示 LCD背光调节
背光调节不用像上一章PWM实验那样直接操作pwmchipX(X0~N)目录里面的文件但是还是需要看一下。因为本章开启了TIM4_CH2这路PWM上一章开启了TIM1_CH3这路PWM相当于开启了两个不同的定时器对应的PWM通道。因此会在/sys/class/pwm目录下存在两个pwmchipX目录如下图所示 从上图可以看出此时有pwmchip0和pwmchip4。进入pwmchip0目录查看一下路径中的寄存器首地址如下图所示 从上图看出此时pwmchip0对应的定时器寄存器首地址为0X40002000这个正是TIM4的寄存器收地址所以在本章pwmchip0对应的是TIM4。同样的方法查看一下pwmchip4的地址如下图所示 从上图可以看出pwmchip4对应的定时器寄存器首地址为0X4000000这个是TIM1定时器寄存器地址。所以在本章pwmchip0对应的是TIM4pwmchip4对应的是TIM1。
在开启多路PWM以后一定要使用这个的方法来确定TIM对应的pwmchip文件
背光设备树节点设置了8个等级的背光调节可以设置为0-7可以通过设置背光等级来实现LCD背光亮度的调节进入如下目录 /sys/devices/platform/backlight/backlight/backlight
此目录下的文件如下图所示 上图中的brightness表示当前亮度等级max_brigntness表示最大亮度等级。当前这两个文件内容如下图所示 从上图可以看出当前屏幕亮度等级为7根据前面的分析可以这个是100%亮度。屏幕最大亮度等级为7。如果要修改屏幕亮度只需要向brightness写入需要设置的屏幕亮度等级即可。比如设置屏幕亮度等级为6那么可以使用如下命令 echo 6 brightness
输入上述命令以后就会发现屏幕亮度变暗了如果设置brightness为0的话就会关闭LCD背光屏幕就会熄灭。
总结
这里因为我之前学习stm32逻辑的时候买的是精英板加一个2.8寸的MCU屏那个是用8080时序去读写的接口是FSMC接口现在Linux驱动这边是一个RGB屏接口是LTDC这个STM32MP1自带的屏幕接口还是跟之前有所区别的。而且这里的屏幕驱动主要是修改设备树文件不用自行编写驱动程序stm32裸机开发还是要自己写程序然后根据时序来完成驱动的。
采用DRM框架来驱动屏幕不过需要完成一些配置来完成使用。屏幕的驱动和DRM的驱动都不用自己编写都是在Linux里面配置就OK。
需要修改设备树在stm32mp15-pinctrl.dtsi里面正点原子的屏幕就是采用的ltdc_pins_b和ltdc_pins_sleep_b这两个节点这里我是正点原子的开发板就不需要修改如果是其他开发板就可能需要改。
在stm32mp157d-atk.dts设备树中需要添加ltdc节点把关联的pinctrl给写进去同时要在port里面设置reg属性信息并添加ltdc_ep0_out子节点并通过其remote-endpoint来告诉ltdc输出到LCD屏幕也就是rgb_panel_in接口。
继续在设备树中在根节点“/”下添加panel_rgb节点需要设置compatile和backlight属性status要写okay来开启然后在port里面添加rgb_panel_in: endpoint设置remote-endpoint连接到刚才LTDC的节点ltdc_ep0_out。
打开drivers/gpu/drm/panel/panel-simple.c添加自己的屏幕参数需要在platform_of_match数组中添加.compatible和.data然后添加drm_display_mode结构体的屏幕参数具体配置可以看上文最后添加panel_desc结构体的刚才.data设置的节点alientek_desc里面设置了.modes关联屏幕参数。
背光设置需要通过PWM来控制正点原子的开发板配置里面是通过PD13的TIM4_CH2来控制的pinctrl里面是配置好了的只要在stm32mp157d-atk.dts设备树里面追加timer4节点在里面的pwm4:pwm里面添加关联的pinctrl通过#pwm-cells控制pwms属性的参数(一般频率和占空比就2个)然后status设置okay就好了。
pwm设置完还需要再根节点“/”添加backlight节点设置compatible属性并通过pwms关联刚才设置的pwm4通过brightness-levels设置背光级别power-supply设置供电然后default-brightness-level设置默认背光级别最后status okay掉就好了。(设置背光为0可能不会熄灭需要在drivers/video/backlight/pwm_bl.c里面修改123行把if里面的条件改为0)
LCD的测试Linux内核是默认打开的但是buildroot需要使能libdrm库重新编译一下。值域使用FB驱动需要配置Linux内核具体配置看上文有好几个我就不赘述了配置完FB还要设置uboot的bootargs加一个console这样才能把LCD作为终端控制台。