Skip to content

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. 框图解析

eLCDIF控制器的结构框图

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
image-20250410173212689
  • 《I.MX6ULL 参考手册》——34.4.3 Read Data Path
image-20250410173145926

二、eLCDIF 寄存器

查看任何芯片的 LCD 控制器寄存器时,记住几个要点:

① 怎么把 LCD 的信息告诉 LCD 控制器:即分辨率、行列时序、像素时钟等;

② 怎么把显存地址、像素格式告诉 LCD 控制器。

对于 i.MX6ULL 来说,我们可以看《I.MX6ULL 参考手册》——34.6 eLCDIF Memory Map/Register Definition

image-20260121202542377

1. LCDIF_CTRLn

eLCDIF General Control Register (LCDIF_CTRLn):

image-20260121202608792

位域 名称 读写 描述
[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):

image-20260121202637688

我是使用 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):

image-20260121202721575

                                       
位域 名称 读写 描述
[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):

image-20260121202921327

本寄存器用来设置 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):

image-20260121203008718

位域 名称 读写 描述
[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):

image-20260121203056046

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):

image-20260121203128285

位域 名称 读写 描述
[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):

image-20260121203155757

位域 名称 读写 描述
[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):

image-20260121203336277

位域 名称 读写 描述
[31:0] ADDR R/W LCD 控制器正在传输的当前帧在显存中的地址

10. LCDIF_NEXT_BUF

LCD Interface Next Buffer Address Register(LCDIF_NEXT_BUF):

image-20260121203403044

位域 名称 读写 描述
[31:0] ADDR R/W 下一帧在显存中的地址

LCD 控制器传输完当前帧后,会把 LCDIF_NEXT_BUF 寄存器的值复制到 LCDIF_CUR_BUF 寄存器。

三、裸机 demo

1. 硬件原理图

1.1 电路原理图

image-20250411104720096

三个 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 连接方式

image-20250411104820855

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
txt
显示一行的时间: 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
			 ≈31M

2. 生成字模

想要显示英文字符的话需要用软件生成字模,这里可以用 PCtoLCD2002 。字模选项设置界面。设置界面中点阵格式和取模方式等参数配置如图

image-20250411115351151

上图设置的取模方式,在右上角的取模说明里面有,即:从第一列开始向下每取 8 个点作为一个字节,如果最后不足 8 个点就补满 8 位。取模顺序是从高到低,即第一个点作为最高位。如*-------取为 10000000。其实就是按下图的方式:

image-20250411115435787

从上到下,从左到右,高位在前。我们按这样的取模方式,然后把 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 文件中定义了两个内联函数:

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 中做如下声明:

c
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 文件中定义两个内联函数:

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 中做如下声明:

c
void lcd_drawpoint(unsigned short x,unsigned short y,unsigned int color);
unsigned int lcd_readpoint(unsigned short x,unsigned short y);

我一般还是直接定义到头文件去。