LV060-LCD控制器
接下来了解一下 imx6ull 中的 lcd 控制器——eLCDIF?若笔记中有错误或者不合适的地方,欢迎批评指正 😃。
一、eLCDIF——液晶控制器
1. eLCDIF 简介
IMX6U 系列芯片内部自带一个增强型液晶接口外设 eLCDIF(Enhanced LCD Interface),配合使用 DDR 作为显存, 可直接控制液晶面板,无需额外增加液晶控制器芯片。
IMX6U 的 eLCDIF 液晶控制器最高支持 1366x768 分辨率的屏幕; 可支持多种颜色格式,包括 RGB888、RGB565、ARGB8888 等(其中的“A”是指透明像素)。 还可配合像素渲染流水线 PXP(Pixel Pipeline)进行复杂的图像处理,如格式转换、缩放、翻转以及图层混合等操作, 使 IMX6U 有非常出色的图形显示性能。特性如下:
①、支持 DOTCLK 模式,也就是 RGB LCD 的 DE 模式。
②、支持 VSYNC 模式以实现高速数据传输,跟 MPU 模式类似,多了 VSYNC 信号。针对高速数据传输(行场信号)。
③、支持 MPU 模式:有些显示屏自带显存,只需要把命令、数据发送给显示屏即可;就是前面讲的 8080 接口
④、支持 ITU-R BT.656 格式的 4:2:2 的 YCbCr 数字视频,并且将其转换为模拟 TV 信号。
⑤、支持 8/16/18/24/32 位 LCD,取决于 IO 的复用设置及寄存器配置。
2. 支持多种接口?
eLCDIF 支持三种接口: MPU 接口、 VSYNC 接口和 DOTCLK 接口。
2.1 MPU 接口
MPU 接口用于在 I.MX6U 和 LCD 屏幕直接传输数据和命令,这个接口用于 6080/8080 接口的 LCD 屏幕,比如我们学习 STM32 的时候常用到的 MCU 屏幕。如果寄存器 LCDIF_CTRL 的位 DOTCLK_MODE、 DVI_MODE 和 VSYNC_MODE 都为 0 的话就表示 LCDIF 工作在 MPU 接口模式。
关于 MPU 接口的详细信息以及时序参考《I.MX6ULL 参考手册》第 2150 页的“34.4.6 MPU Interface”小节.
2.2 VSYNC 接口
VSYNC 接口时序和 MPU 接口时序基本一样,只是多了 VSYNC 信号来作为帧同步,当 LCDIF_CTRL 的位 VSYNC_MODE 为 1 的时候此接口使能。
关于 VSYNC 接口的详细信息可以参考《I.MX6ULL 参考手册》第 2152 页的“34.4.7 VSYNC Interface”小节 。
2.3 DOTCLK 接口
DOTCLK 接口就是用来连接 RGB LCD 接口屏幕的, 它包括 VSYNC、 HSYNC、 DOTCLK 和 ENABLE(可选的)这四个信号,这样的接口通常被称为 RGB 接口。
可以看《I.MX6ULL 参考手册》——34.4.8 DOTCLK Interface
3. 框图解析

3.1 通讯引脚
上图的标号 1 处表示 eLCDIF 的通讯引脚,eLCDIF 的通讯引脚与液晶显示面板控制信号一一对应, 包含有 HSYNC、VSYNC、DE、CLK 以及 RGB 数据线各 8 根。设计硬件时把液晶面板与 IMX6 对应的这些引脚连接起来即可, 查阅《I.MX6ULL 参考手册》——Table 34-5. Pin use in DOTCLK Mode ,可获知 eLCDIF 信号线对应的引脚,具体见下表。
3.2 总线接口
eLCDIF 的液晶接口有两个总线接口,System Bus 总线,用于向 eLCDIF 液晶接口的 FIFO 中写入数据。 而 Control Bus 用于设置 eLCDIF 用于读、写控制寄存器以及 DMA、数据寄存器等等。
3.3 液晶接口(LCD Interface)
上图的标号处表示 eLCDIF 的液晶接口(LCD Interface),它是 eLCDIF 外设的主要功能部件,受控制总线(Control Bus)的寄存器控制, 从系统总线(System Bus)获得输入像素数据,经过一系列转换后通过 eLCDIF 的通讯引脚发送至外接的液晶面板。
其中控制总线的寄存器可以配置显存地址、输入像素数据的格式、输出的数据信号线宽度、 各个控制信号的有效极性以及控制时序中的 VSW、VBP 等参数,还能配置使用 DMA 传输。
使用寄存器初始化好 eLCDIF 的后,它会从“LFIFO”和“TXFIFO”中获取数据进行转换处理(格式转换、移位等操作)并传输出去。 当 FIFO 中的数据量低于一定程度时,它会向系统总线(SystemBus)发起请求,系统总线会把显存地址的数据搬运至 FIFO 中。 FIFO 还可以配置阈值,低于该阈值时系统总线会提高获取数据的优先级。
eLCDIF 正常运行后,数据从显存到液晶屏全程不需要内核的干预,程序控制时我们只要把像素数据写入到显存即可。
3.4 驱动时钟
elcdf 模块包含两个时钟信号,分别是 BUS CLOCK(apb_clk)和 DISPLAY CLOCK(pix_clk)。
3.4.1 BUS CLOCK(apb_clk)
这个 BUS CLOCK(apb_clk)就是指 eLCDIF 外设的 根时钟 LCDIF_CLK_ROOT,它给 eLCDIF 提供驱动的时钟源,在时钟树中的结构具体如下图所示。

LCDIF_CLK_ROOT 根时钟可以选择多种输入时钟源,首先是时钟源预选择器(Pre-multiplexer)支持使用如下时钟:
- PLL2:System PLL,该时钟频率通常为 528MHz。
- PLL2 PFD0:该时钟常规配置为 352MHz。
- PLL2 PFD1:该时钟常规配置为 594MHz。
- PLL3 PFD3:该时钟常规配置为 454.74MHz。
- PLL3 PFD1:该时钟常规配置为 664.62MHz。
- PLL5:Video PLL,该时钟常规配置为 649.52MHz。
预选择器得到的时钟,可根据需要进行分频配置,分频后输入到时钟源选择器(multiplexer)作为 LCDIF_CLK_ROOT 默认的时钟源, 除此之外,图中的时钟的选择器还包含其它可选的输入时钟:ipp_di0_clk、ipp_di1_clk、ldb_di0_clk、ldb_di1_clk, 不过关于这些时钟在参考手册中并没有介绍,而且在寄存器中并没有这些时钟源的分频、选择的配置, 也许该选择器是兼容其它设备而保留的内容,所以使用时我们直接选择预选择器得到的时钟作为 LCDIF_CLK_ROOT 的输入时钟源即可。
3.4.2 DISPLAY CLOCK(pix_clk)
这个 DISPLAY CLOCK(pix_clk)是指 eLCDIF 与液晶面板接口的 像素时钟 LCDIF_pix_clk,它的时钟频率与根时钟 LCDIF_CLK_ROOT 一致,不过它们 的时钟开关是分开的,其中 LCDIF_CLK_ROOT 使用寄存器位 CCM_CCGR2 [CG14] 控制,而 LCDIF_pix_clk 使用寄存器位 CCM_CCGR3 [CG5] 控制。
4. 数据传输与处理
这里就简单了解下吧,看看手册中的框图。
- 《I.MX6ULL 参考手册》——34.4.2 Write Data Path

- 《I.MX6ULL 参考手册》——34.4.3 Read Data Path

二、eLCDIF 寄存器
查看任何芯片的 LCD 控制器寄存器时,记住几个要点:
① 怎么把 LCD 的信息告诉 LCD 控制器:即分辨率、行列时序、像素时钟等;
② 怎么把显存地址、像素格式告诉 LCD 控制器。
对于 i.MX6ULL 来说,我们可以看《I.MX6ULL 参考手册》——34.6 eLCDIF Memory Map/Register Definition

1. LCDIF_CTRLn
eLCDIF General Control Register (LCDIF_CTRLn):

| 位域 | 名称 | 读写 | 描述 |
| [31] | SFTRST | R/W | 软件复位,正常工作时应设为 0;如果设为 1,它会复位整个 LCD 控制器 |
| [30] | CLKGATE | R/W | 时钟开关, 0:正常工作时要设置为 0; 1:关闭 LCD 控制器时钟 |
| [29] | YCBCR422_INPUT | R/W | 使用 RGB 接口时,设置为 0;其他接口我们暂时不关心 |
| [28] | READ_WRITEB | R/W | 使用 RGB 接口时,设置为 0;其他接口我们暂时不关心 |
| [27] | WAIT_FOR_VSYNC_EDGE | R/W | 在 VSYNC 模式时,设置为 1;我们不关心 |
| [26] | DATA_SHIFT_DIR | R/W | 在 DVI 模式下才需要设置,我们不关心 |
| [25:21] | SHIFT_NUM_BITS | R/W | 在 DVI 模式下才需要设置,我们不关心 |
| [20] | DVI_MODE | R/W | 设置为 1 时,使用 DVI 模式,就是 ITU-R BT.656 数字接口 |
| [19] | BYPASS_COUNT | R/W | DOTCLK 和 DVI 模式下需要设置为 1; MPU、VSYNC 模式时设为 0 |
| [18] | VSYNC_MODE | R/W | 使用 VSYNC 模式时,设置为 1 |
| [17] | DOTCLK_MODE | R/W | 使用 DOTCLK 模式时,设置为 1;本实验用的就是这个模式 |
| [16] | DATA_SELECT | R/W | MPU 模式下才用到,我们不关心 |
| [15:14] | INPUT_DATA_SWIZZLE | R/W | 显存中像素颜色的数据转给 LCD 控制器时,字节位置是否交换: 0x0:NO_SWAP,不交换; 0x0:LITTLE_ENDIAN,小字节序,跟 NO_SWAP 一样; 0x1:BIG_ENDIAN_SWAP,字节 0、3 交换;字节 1、2 交换; 0x1:SWAP_ALL_BYTES,字节 0、3 交换;字节 1、2 交换; 0x2:HWD_SWAP,半字交换,即 0x12345678 转为 0x56781234 0x3:HWD_BYTE_SWAP,在每个半字内部放换字节, 即 0x12345678 转换为 0x34127856 |
| [13:12] | CSC_DATA_SWIZZLE | R/W | 显存中的数据被传入 LCD 控制器内部并被转换为 24BPP 后,在它被转给 LCD 接口之前,字节位置是否交换: 0x0:NO_SWAP,不交换; 0x0:LITTLE_ENDIAN,小字节序,跟 NO_SWAP 一样; 0x1:BIG_ENDIAN_SWAP,字节 0、3 交换;字节 1、2 交换; 0x1:SWAP_ALL_BYTES,字节 0、3 交换;字节 1、2 交换; 0x2:HWD_SWAP,半字交换,即 0x12345678 转为 0x56781234 0x3:HWD_BYTE_SWAP,在每个半字内部放换字节, 即 0x12345678 转换为 0x34127856 |
| [11:10] | LCD_DATABUS_WIDTH | R/W | LCD 数据总线宽度,就是对外输出的 LCD 数据的位宽, 0x0:16 位; 0x1:8 位; 0x2:18 位; 0x3:24 位 |
| [9:8] | WORD_LENGTH | R/W | 输入的数据格式,即显存中每个像素占多少位, 0x0:16 位; 0x1:8 位; 0x2:18 位; 0x3:24 位 |
| [7] | RGB_TO_YCBCR422_CSC | R/W | 设置为 1 时,使能颜色空间转换:RGB 转为 YCbCr |
| [6] | ENABLE_PXP_HANDSHAKE | R/W | 当 LCDIF_MASTER 设置为 1 时,再设置这位, 则 LCD 控制器跟 PXP 之间的握手机制被关闭(我们不关心) |
| [5] | MASTER | R/W | 设置为 1 时,LCD 控制器成为 bus master |
| [4] | RSRVD0 | R/W | 保留 |
| [3] | DATA_FORMAT_16_BIT | R/W | WORD_LENGTH 为 0 时,表示一个像素用 16 位,此位作用如下: 0:数据格式为 ARGB555; 1:数据格式为 RGB565 |
| [2] | DATA_FORMAT_18_BIT | R/W | WORD_LENGTH 为 2 时,表示一个像素用 18 位,RGB 数据还是保存在 32 位数据里,此位作用如下: 0:低 18 位用来表示 RGB666,高 14 位无效 1:高 18 位用来表示 RGB666,低 14 位无效 |
| [1] | DATA_FORMAT_24_BIT | R/W | WORD_LENGTH 为 3 时,表示一个像素用 24 位,此位作用如下: 0:所有的 24 位数据都有效,格式为 RGB888 1:转给 LCD 控制器的数据是 24 位的,但只用到其中的 18 位, 每个字节用来表示一个原色,每字节中高 2 位无效 |
| [0] | RUN | R/W | 使能 LCD 控制器,开始传输数据 |
2. LCDIF_CTRL1n
eLCDIF General Control1 Register (LCDIF_CTRL1n):

我是使用 TFT LCD,LCD 控制器使用 DOTCLK 模式。本寄存器会用到 BYTE_PACKING_FORMAT 。
| 位域 | 名称 | 读写 | 描述 |
| [19:16] | BYTE_PACKING_FORMAT | R/W | 用来表示一个 32 位的 word 中,哪些字节是有效的,即哪些字节是用来表示颜色的。 bit16、17、18、19 分别对应 byte0、1、2、3;某位为 1,就表示对应的字节有效。 默认值是 0xf,表示 32 位的 word 中,所有字节都有效。 对于 8bpp,可以忽略本设置,所有的字节都是有效的; 对于 16bpp,bit [1:0]、bit [3:2] 分别对应一个字节,组合中的 2 位都为 1 时,对应的字节才有效; 对于 24bpp,0x7 表示 32 位数据中只用到 3 个字节,这称为“24 bit unpacked format”,即 ARGB,其中的 A 字节被丢弃 |
| [0] | RESET | R/W | 用来复位了接的 LCD, 0:LCD_RESET 引脚输出低电平; 1:LCD_RESET 引脚输出高电平 |
3. LCDIF_TRANSFER_COUNT
eLCDIF Horizontal and Vertical Valid Data Count Register(LCDIF_TRANSFER_COUNT):

| 位域 | 名称 | 读写 | 描述 |
| [31:16] | V_COUNT | R/W | 一帧中,有多少行有效数据,高 16 位是 V_COUNT,是 LCD 的垂直分辨率。 |
| [15:0] | H_COUNT | R/W | 一行中,有多少个像素,低 16 位是 H_COUNT,是 LCD 的水平分辨率。 |
如果 LCD 分辨率为 1024*600 的话,那么 V_COUNT 就是 600, H_COUNT 就是 1024。
4. LCDIF_VDCTRL0n
eLCDIF VSYNC Mode and Dotclk Mode Control Register0(LCDIF_VDCTRL0n):

本寄存器用来设置 Vsync 信号相关的时序,及极性。
| 位域 | 名称 | 读写 | 描述 |
| [29] | VSYNC_OEB | R/W | 用来控制 VSYNC 信号,对于 DOTCLK 模式,设为 0, 0:VSYNC 是输出引脚,用 LCD 控制器产生; 1:VSYNC 是输入引脚 |
| [28] | ENABLE_PRESENT | R/W | 在 DOTCLK 模式下,硬件是否会产生数据使能信号 ENALBE: 0:不产生; 1:产生 |
| [27] | VSYNC_POL | R/W | 用来决定 VSYNC 脉冲的极性, 0:低脉冲; 1:高脉冲 |
| [26] | HSYNC_POL | R/W | 用来决定 HSYNC 脉冲的极性, 0:低脉冲; 1:高脉冲 |
| [25] | DOTCLK_POL | R/W | 用来决定 DOTCLK 的极性, 0:LCD 控制器在 DOTCLK 下降沿发送数据,LCD 在上升沿捕获数据; 1:反过来 |
| [24] | ENABLE_POL | R/W | 用来决定 ENABLE 信号的极性, 0:数据有效期间,ENABLE 信号为低; 1:反过来 |
| [21] | VSYNC_PERIOD_UNIT | R/W | 用来决定 VSYNC_PERIOD 的单位, 0:单位是像素时钟(pix_clk),这在 VSYNC 模式下使用; 1:单位是“整行”,这在 DOTCLK 模式下使用 |
| [20] | VSYNC_PULSE_WIDTH_UNIT | R/W | 用来决定 VSYNC_PULSE_WIDTH 的单位, 0:单位是像素时钟(pix_clk); 1:单位是“整行” |
| [19] | HALF_LINE | R/W | VSYNC 周期是否周加上半行的时间, 0:VSYNC 周期 = VSYNC_PERIOD; 1:VSYNC 周期 = VSYNC_PERIOD+HORIZONTAL_PERIOD/2 |
| [18] | HALF_LINE_MODE | R/W | 0:第 1 帧将在一行的中间结束,第 2 帧在一行的中间开始; 1:所有帧结束前都加上半行时间,这样所有帧都会起始于“行的开头” |
| [17:0] | VSYNC_PULSE_WIDTH | R/W | VSYNC 脉冲的宽度 |
5. LCDIF_VDCTRL1
eLCDIF VSYNC Mode and Dotclk Mode Control Register1(LCDIF_VDCTRL1):

| 位域 | 名称 | 读写 | 描述 |
| [29] | VSYNC_PERIOD | R/W | 两个垂直同步信号之间的间隔,即垂直方向同步信号的总周期; 单位由 VSYNC_PERIOD_UNIT 决定 |
这个寄存器是 VSYNC 和 DOTCLK 模式控制寄存器 1,此寄存器只有一个功能,用来设置 VSYNC 总周期,就是:屏幕高度+VSPW+VBP+VFP。
6. LCDIF_VDCTRL2
LCDIF VSYNC Mode and Dotclk Mode Control Register2(LCDIF_VDCTRL2):

HSYNC_PULSE_WIDTH:水平同步信号脉冲宽度;
HSYNC_PERIOD:两个水平同步信号之间的总数,即水平方向同步信号的总周期
| 位域 | 名称 | 读写 | 描述 |
| [31:18] | HSYNC_PULSE_WIDTH | R/W | HSYNC 脉冲的宽度(单位:pix_clk) |
| [17:0] | HSYNC_PERIOD | R/W | 整行的宽度,即两个 HYSNC 信号之间的宽度(单位:pix_clk) |
这个寄存器分为高 16 位和低 16 位两部分,高 16 位是 HSYNC_PULSE_WIDTH,用来设置 HSYNC 信号宽度,也就是 HSPW。低 16 位是 HSYNC_PERIOD,设置 HSYNC 总周期,就是:屏幕宽度+HSPW+HBP+HFP。
7. LCDIF_VDCTRL3
eLCDIF VSYNC Mode and Dotclk Mode Control Register3(LCDIF_VDCTRL3):

| 位域 | 名称 | 读写 | 描述 |
| [29] | MUX_SYNC_SIGNALS | R/W | 用不着 |
| [28] | VSYNC_ONLY | R/W | 0:DOTCLK 模式时必须设置为 0; 1:VSYNC 模式时必须设置为 1 |
| [27:16] | HORIZONTAL_WAIT_CNT | R/W | 水平方向上的等待像素个数,等于 thp+thb |
| [15:0] | VERTICAL_WAIT_CNT | R/W | 垂直方向上的等待行数,等于 tvp+tvb |
HORIZONTAL_WAIT_CNT(bit27:16):此位用于 DOTCLK 模式,用于设置 HSYNC 信号产生到有效数据产生之间的时间,也就是 HSPW+HBP。
VERTICAL_WAIR_CNT(bit15:0):和 HORIZONTAL_WAIT_CNT 一样,只是此位用于 VSYNC 信号,也就是 VSPW+VBP。
8. LCDIF_VDCTRL4
eLCDIF VSYNC Mode and Dotclk Mode Control Register4(LCDIF_VDCTRL4):

| 位域 | 名称 | 读写 | 描述 |
| [31:29] | DOTCLK_DLY_SEL | R/W | 在 LCD 控制器内部的 DOTCLK 输出到 LCD_DOTCK 引脚时,延时多久: 0:2ns; 1:4ns; 2:6ns; 3:8ns; 其他值保留 |
| [18] | SYNC_SIGNALS_ON | R/W | 同步信号使能位,设置为 1 的话使能 VSYNC、 HSYNC、DOTCLK 这些信号。DOTCLK 模式下必须设为 1 |
| [17:0] | DOTCLK_H_VALID_DATA_CNT | R/W | 水平方向上的有效像素个数(pix_clk),即分辨率的 y |
9. LCDIF_CUR_BUF
LCD Interface Current Buffer Address Register(LCDIF_CUR_BUF):

| 位域 | 名称 | 读写 | 描述 |
| [31:0] | ADDR | R/W | LCD 控制器正在传输的当前帧在显存中的地址 |
10. LCDIF_NEXT_BUF
LCD Interface Next Buffer Address Register(LCDIF_NEXT_BUF):

| 位域 | 名称 | 读写 | 描述 |
| [31:0] | ADDR | R/W | 下一帧在显存中的地址 |
LCD 控制器传输完当前帧后,会把 LCDIF_NEXT_BUF 寄存器的值复制到 LCDIF_CUR_BUF 寄存器。
三、裸机 demo
1. 硬件原理图
1.1 电路原理图

三个 SGM3157 的目的是在未使用 RGBLCD 的时候将 LCD_DATA7、LCD_DATA15 和 LCD_DATA23 这三个线隔离开来,因为 ALIENTEK 的屏幕的 LCD_R7/G7/B7 这几个线用来设置 LCD 的 ID,所以这几根线上有上拉/下拉电阻。但是 I.MX6U 的 BOOT 设置也用到了 LCD_DATA7、 LCD_DATA15 和 LCD_DATA23 这三个引脚,所以接上屏幕以后屏幕上的 ID 电阻就会影响到 BOOT 设置,会导致代码无法运行,所以先将其隔离开来,如果要使用 RGB LCD 屏幕的时候再通过 LCD_DE 将其“连接”起来。
1.2 连接方式

1.3 参数计算
我使用的屏幕是 ATK-MD0430R-800480,读出来的 ID 会是 0x4384。这款屏幕对应的一些时间参数如下:
| ATK-MD0430R-800480 ID = 4384 | |||
| HOZVAL(水平显示区域) | thd | 800 | tCLK |
| HSPW(horizontal sync width) | thp | 48 | tCLK |
| HBP(horizontal back porch) | thb | 88 | tCLK |
| HFP(horizontal front porth) | thf | 40 | tCLK |
| LINE(垂直显示区域) | tvd | 480 | th |
| VSPW(vertical sync width) | tvp | 3 | th |
| VBP(vertical back porch) | tvb | 32 | th |
| VFP(vertical front porch) | tvf | 13 | th |
| 像素时钟 | - | 31 | MHz |
显示一行的时间: HSPW + HBP + HOZVAL + HFP
显示一帧的时间: (VSPW+VBP+LINE+VFP) * (HSPW + HBP + HOZVAL + HFP)
显示一帧的时钟: (VSPW+VBP+LINE+VFP) * (HSPW + HBP + HOZVAL + HFP)
=(3+32+480+13) * (48+88+800+40)
=528*976
=515328
显示60帧的时钟:(VSPW+VBP+LINE+VFP) * (HSPW + HBP + HOZVAL + HFP) * 60
=515328*60
=30919680
≈30.92M
≈31M2. 生成字模
想要显示英文字符的话需要用软件生成字模,这里可以用 PCtoLCD2002 。字模选项设置界面。设置界面中点阵格式和取模方式等参数配置如图

上图设置的取模方式,在右上角的取模说明里面有,即:从第一列开始向下每取 8 个点作为一个字节,如果最后不足 8 个点就补满 8 位。取模顺序是从高到低,即第一个点作为最高位。如*-------取为 10000000。其实就是按下图的方式:
从上到下,从左到右,高位在前。我们按这样的取模方式,然后把 ASCII 字符集按 12*6 大小、 16*8、 24*12 和 32*16 大小取模出来(对应汉字大小为 12*12、 16*16、 24*24 和 32*32,字符的只有汉字的一半大!)。将取出的点阵数组保存在 font.h 里面,每个 12*6 的字符占用 12 个字节,每个 16*8 的字符占用 16 个字节,每个 24*12 的字符占用 36 个字节,每个 32*16 的字符占用 64 个字节。
3. demo 源码
看这里:21_lcd/01_lcd_demo · 苏木/imx6ull-bare-demo。
3.1 出现的报错问题
这里有个坑,就是关于内联函数的。接下来看一下:
原本在 21_lcd/01_lcd_demo/bsp/lcd/bsp_lcd.c 文件中定义了两个内联函数:
/*
* @description : 画点函数
* @param - x : x 轴坐标
* @param - y : y 轴坐标
* @param - color : 颜色值
* @return : 无
*/
inline void lcd_drawpoint(unsigned short x,unsigned short y,unsigned int color)
{
*(unsigned int*)((unsigned int)tftlcd_dev.framebuffer +
tftlcd_dev.pixsize * (tftlcd_dev.width * y+x))=color;
}
/*
* @description : 读取指定点的颜色值
* @param - x : x 轴坐标
* @param - y : y 轴坐标
* @return : 读取到的指定点的颜色值
*/
inline unsigned int lcd_readpoint(unsigned short x,unsigned short y)
{
return *(unsigned int*)((unsigned int)tftlcd_dev.framebuffer +
tftlcd_dev.pixsize * (tftlcd_dev.width * y + x));
}然后在 21_lcd/01_lcd_demo/bsp/lcd/bsp_lcd.h 中做如下声明:
inline void lcd_drawpoint(unsigned short x,unsigned short y,unsigned int color);
inline unsigned int lcd_readpoint(unsigned short x,unsigned short y);这样在编译的时候会警告,说有找不到这两个函数定义。这个,一般来说,内联函数最好是定义在头文件中,并且加上 static 关键字保证一定可以展开。
3.2 解决办法
这里若是想要在.h 文件中声明,在.c 文件中实现,需要在 21_lcd/01_lcd_demo/bsp/lcd/bsp_lcd.c 文件中定义两个内联函数:
/*
* @description : 画点函数
* @param - x : x 轴坐标
* @param - y : y 轴坐标
* @param - color : 颜色值
* @return : 无
*/
inline void lcd_drawpoint(unsigned short x,unsigned short y,unsigned int color)
{
*(unsigned int*)((unsigned int)tftlcd_dev.framebuffer +
tftlcd_dev.pixsize * (tftlcd_dev.width * y+x))=color;
}
/*
* @description : 读取指定点的颜色值
* @param - x : x 轴坐标
* @param - y : y 轴坐标
* @return : 读取到的指定点的颜色值
*/
inline unsigned int lcd_readpoint(unsigned short x,unsigned short y)
{
return *(unsigned int*)((unsigned int)tftlcd_dev.framebuffer +
tftlcd_dev.pixsize * (tftlcd_dev.width * y + x));
}然后在 21_lcd/01_lcd_demo/bsp/lcd/bsp_lcd.h 中做如下声明:
void lcd_drawpoint(unsigned short x,unsigned short y,unsigned int color);
unsigned int lcd_readpoint(unsigned short x,unsigned short y);我一般还是直接定义到头文件去。