imx6ull UI开发简介
- 在imx6ull上开发UI 应用
- 硬件层面
- 内核驱动
- 显示设备
- 文件描述符
- 设备树
- 软件
- LVGL
- 用户空间
- 内核
- QT
在imx6ull上开发UI 应用
在 Linux 系统中,应用程序需要通过操作 RGB LCD 的显存来实现在屏幕上显示字符、图像等信息。由于 Linux 采用严格的内存管理机制,显存必须经过申请才能使用。由于虚拟内存机制的存在,驱动程序设置的显存地址必须与应用程序访问的物理内存区域保持一致。
下面从硬件,内核 驱动和软件层面介绍整个流程。
硬件层面
imx6ull 的外设 LCDIF(Liquid Crystal Display Interface) 支持以下功能:
- 具有异步并行MPU接口的显示器,用于与集成帧缓冲区进行命令和数据传输。
- 支持动态图像且需要RGB接口模式(DOTCLK接口)的显示器。
- 用于高速数据传输的VSYNC模式。
- 接受ITU-R BT.656格式4的数字视频编码器。
因此可以用这个外设来驱动 TFT(RGB 接口)
内核驱动
通常情况下, 芯片厂商和屏幕厂商已经在Linux内核中实现了驱动, 对于应用来说, 只需要根据实际的硬件来修改一些设置, 如 device tree里LCDIF的有关IO口, 屏幕的有关参数等。
显示设备
在Linux系统中,显示设备通常通过图形子系统来管理,主要有两种方式:直接使用帧缓冲(Framebuffer)和间接使用图形用户界面(GUI)系统,如X Window System或QT等。
帧缓冲是Linux内核提供的一个设备驱动接口,它允许用户空间程序直接访问显示硬件的帧缓冲区。通过/dev/fbX(X: 0~n)等设备文件,用户空间程序可以读写显示内存,从而实现图形显示。
文件描述符
/dev/fbX是字符设备, 文件描述符定义在drivers/video/fbdev/core/fbmem.c文件
设备树
- imx6ul.dtsi: 设置 LCDIF的有关属性
lcdif: lcdif@21c8000 {compatible = "fsl,imx6ul-lcdif", "fsl,imx28-lcdif";reg = <0x021c8000 0x4000>;interrupts = <GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>;clocks = <&clks IMX6UL_CLK_LCDIF_PIX>,<&clks IMX6UL_CLK_LCDIF_APB>,<&clks IMX6UL_CLK_DUMMY>;clock-names = "pix", "axi", "disp_axi";status = "disabled";};
- OEM工程的设备树: 设置屏幕相关的属性和pinctrl 有关信息
&lcdif {assigned-clocks = <&clks IMX6UL_CLK_LCDIF_PRE_SEL>;assigned-clock-parents = <&clks IMX6UL_CLK_PLL5_VIDEO_DIV>;pinctrl-names = "default";pinctrl-0 = <&pinctrl_lcdif_dat&pinctrl_lcdif_ctrl>;display = <&display0>;status = "okay";display0: display {bits-per-pixel = <24>;bus-width = <24>;display-timings {native-mode = <&timing0>;timing0: timing0 {clock-frequency =<60000000>;hactive = <1024>;vactive = <600>;hfront-porch = <160>;hback-porch = <160>;hsync-len = <10>;vback-porch = <23>;vfront-porch = <12>;vsync-len = <3>;hsync-active = <0>;vsync-active = <0>;de-active = <1>;pixelclk-active = <1>;};};};
};// backlight
pinctrl_pwm1: pwm1grp {fsl,pins = <MX6UL_PAD_LCD_DATA00__PWM1_OUT 0x110b0>;};// lcdif command line
pinctrl_lcdif_ctrl: lcdifctrlgrp {fsl,pins = <MX6UL_PAD_LCD_CLK__LCDIF_CLK 0x79MX6UL_PAD_LCD_HSYNC__LCDIF_HSYNC 0x79MX6UL_PAD_LCD_VSYNC__LCDIF_VSYNC 0x79MX6UL_PAD_LCD_ENABLE__LCDIF_ENABLE 0x79MX6UL_PAD_NAND_ALE__GPIO4_IO10 0x17059>;};
// lcdif data linepinctrl_lcdif_dat: lcdifdatgrp {fsl,pins = <MX6UL_PAD_LCD_DATA00__LCDIF_DATA00 0x79MX6UL_PAD_LCD_DATA01__LCDIF_DATA01 0x79MX6UL_PAD_LCD_DATA02__LCDIF_DATA02 0x79MX6UL_PAD_LCD_DATA03__LCDIF_DATA03 0x79MX6UL_PAD_LCD_DATA04__LCDIF_DATA04 0x79MX6UL_PAD_LCD_DATA05__LCDIF_DATA05 0x79MX6UL_PAD_LCD_DATA06__LCDIF_DATA06 0x79MX6UL_PAD_LCD_DATA07__LCDIF_DATA07 0x79MX6UL_PAD_LCD_DATA08__LCDIF_DATA08 0x79MX6UL_PAD_LCD_DATA09__LCDIF_DATA09 0x79MX6UL_PAD_LCD_DATA10__LCDIF_DATA10 0x79MX6UL_PAD_LCD_DATA11__LCDIF_DATA11 0x79MX6UL_PAD_LCD_DATA12__LCDIF_DATA12 0x79MX6UL_PAD_LCD_DATA13__LCDIF_DATA13 0x79MX6UL_PAD_LCD_DATA14__LCDIF_DATA14 0x79MX6UL_PAD_LCD_DATA15__LCDIF_DATA15 0x79MX6UL_PAD_LCD_DATA16__LCDIF_DATA16 0x79MX6UL_PAD_LCD_DATA17__LCDIF_DATA17 0x79MX6UL_PAD_LCD_DATA18__LCDIF_DATA18 0x79MX6UL_PAD_LCD_DATA19__LCDIF_DATA19 0x79MX6UL_PAD_LCD_DATA20__LCDIF_DATA20 0x79MX6UL_PAD_LCD_DATA21__LCDIF_DATA21 0x79MX6UL_PAD_LCD_DATA22__LCDIF_DATA22 0x79MX6UL_PAD_LCD_DATA23__LCDIF_DATA23 0x79>;};
- 驱动
搜索 imx6ul.dtsi里定义的 LCDIF 的 compatible , 可以找到 imx6ull LCDIF的驱动文件 drivers/video/fbdev/mxsfb.c
软件
分别以LVGL 和QT两种UI 开发框架角度介绍
LVGL
LVGL移植的详细介绍可以参考LVGL仓库。现简要介绍LVGL实现图形显示的框架流程。
用户空间
- 使用 “mmap” 将framebuffer和 /dev/fbX 映射起来
- 实现flush_cb
–使用memcpy将 UI 数据copy 到 framebuffer
–最后调用 lv_disp_flush_ready 表示显示刷新操作已完成
内核
/dev/fb0
是 Linux FrameBuffer 驱动导出的字符设备。- 用户空间通过
mmap
把 FrameBuffer 物理内存映射到用户空间指针(fbp
)。 - 用户空间写
fbp
,实际上就是直接写显存。 - 内核 FrameBuffer 驱动负责把这些数据同步到实际的显示控制器(如 i.MX6ULL 的 LCDIF 控制器)。
- 显示控制器周期性地从显存读取数据,驱动 TFT 屏显示。
- 内核 FrameBuffer 驱动的主要职责
- 初始化 LCD 控制器(如设置分辨率、时序、显存地址等)。
- 响应用户空间的 ioctl(如获取/设置分辨率、虚拟分辨率、offset 等)。
- 提供 mmap 支持,让用户空间可以直接访问显存。
- 处理可能的同步(如部分硬件需要显存 flush 或 cache 操作)。
- 流程图
LVGL (flush_cb)|vmemcpy/写入 fbp (mmap 映射的显存)|v/dev/fb0 (FrameBuffer 设备)|vLinux FrameBuffer 驱动 (fbmem.c, fbdev.c, mxsfb.c 等)|vLCD 控制器 (i.MX6ULL LCDIF)|vTFT 屏显示
QT
- 移植QT 到 imx6ull
-
移植完成后, 设置qt的环境变量 /etx/profile/
把/dev/fb0指定为QT的显示设备
export QT_QPA_PLATFORM=li nuxfb:tty=/dev/fb0
-
- QT应用程序结合Qt的屏幕管理功能,通过QScreen类获取和操作多个显示设备
QList<QScreen *> screens = QGuiApplication::screens();for (QScreen *screen : screens) {qDebug() << "Found screen:" << screen->name();// 可以在这里进行其他屏幕相关的操作}