IM6ull LCD 驱动

news2024/11/25 8:14:40

  一 LCD  简介

LCD(Liquid Crystal Display),液晶显示器。LCD的构造是在两片平行的玻璃基板中放置液晶盒,下基板玻璃上设置TFT(薄膜晶体管),上基板玻璃上设置彩色滤光片,通过TFT上的信号与电压改变来控制液晶分子的转动方向,从而达到控制每个像素点偏振光出射与否而达到显示目的。

1.1 LCD结构

序号名称描述
1背光层包括反光板、导光板、LED灯、扩散膜、棱镜膜
2液晶层由液晶、定向膜、背光板、滤光片、导光板组成
3TFT层包括TFT晶体管、电容、配向膜等
4玻璃基板提供芯片和电路的载体,提供机械支撑和保护
5PCB基板连接晶体管和其他电路元件,传输信号和电力

LCD 屏幕或者说显示器有很多种接口,比如在显示器上常见的 VGA、 HDMI、 MIPI DP 等等,
但是I.MX6U-ALPHA开发板不支持这些接口。

IMX6ULL-ALPHA支持RGB接口的LCD,RGBLCD有DE和HV两种驱动模式,RGBLCD接口信号线如下表示

信号线    描述
R[7:0]    8根红色数据线
G[7:0]    8根绿色数据线
B[7:0]    8根蓝色数据线
DE    数据使能线
VSYNC    垂直同步信号线 也叫做帧同步信号,当产生此信号的话就表示开始显示新的一帧图像了,
HSYNC    水平同步信号线
PCLK    像素时钟信号线

1.2 RGB LCD 硬件图

正点原子屏幕支持多个屏幕ID 通过SGM3157 控制 R7/G7/B7 的上/下拉,来自定义 LCD 模块的 ID,帮助 MCU 判断当前 LCD 面板的分辨率和相关参数,以提
高程序兼容性

                                              图 1.2

24位色彩数据线

ALIENTEK 一共有三款 RGB LCD 屏幕,型号分别为: ATK-4342(4.3 寸, 480*272)、 ATK-
7084(7 寸, 800*480)和 ATK-7016(7 寸, 1024*600),本教程就以 ATK-7016 这款屏幕为例讲解,
ATK-7016 的屏幕接口原理图如图 24.1.1.4 所示
 

二 成像原理

LCD的每一帧图像成像过程涉及到一系列的步骤和信号,以下是基本的成像原理:

  1. 同步信号:首先,LCD会接收到一个同步信号,这个信号指示着一帧图像的开始。同步信号通常是一个脉冲信号,它告诉LCD显示器开始接收新的图像数据。
  2. 数据传输:在接收到同步信号后,LCD会开始接收图像数据,这些数据包括每个像素的灰度值和颜色信息。图像数据通常以串行或并行的方式传输到LCD显示器中。
  3. 行扫描:在传输完图像数据后,LCD会进行行扫描,以确定每个像素的状态。行扫描通常由一个行驱动器芯片来完成,它会按照一定的顺序扫描每一行像素,并根据图像数据来确定每个像素的亮度。
  4. 帧刷新:在完成行扫描后,LCD会进行帧刷新,以更新整个图像。帧刷新过程中,每个像素都会根据图像数据来调整亮度,最终合成为一幅完整的图像。
  5. 垂直消隐:在帧刷新过程中,为了确保图像的稳定性,LCD会在每帧图像的末尾进行垂直消隐。垂直消隐期间,所有的像素都会被清除,以便下一帧图像的显示。

总的来说,LCD的每一帧图像成像过程涉及到同步信号、数据传输、行扫描、帧刷新和垂直消隐等步骤。通过这些步骤,LCD显示器可以实时地显示动态图像。需要注意的是,实际的成像过程可能更加复杂和多样化,具体取决于LCD显示器的设计和应用需求。

2.1 LCD 一帧图像扫描图
 


当显示完一行以后会发出 HSYNC 信号,此时电子枪(CRT 显示器)就会关闭,然后迅速的移动到屏幕的左边,当 HSYNC 信号结束以后就可以显示新的一行数据了,电子枪就会重新打开。

在 HSYNC信号结束到电子枪重新打开之间会插入一段延时,这段延时就图 24.1.1.5 中的 HBP。当显示完一行以后就会关闭电子枪等待 HSYNC 信号产生,关闭电子枪到 HSYNC 信号产生之间会插入一段延时,这段延时就是图 24.1.1.5 中的 HFP 信号。同理,当显示完一帧图像以后电子枪也会
关闭,然后等到 VSYNC 信号产生,期间也会加入一段延时,这段延时就是图 24.1.1.5 中的 VFP。
VSYNC 信号产生,电子枪移动到左上角,当 VSYNC 信号结束以后电子枪重新打开,中间也会
加入一段延时,这段延时就是图 24.1.1.5 中的 VBP

– HSYNC 是水平同步信号(行同步信号),当产生此信号表示开始显示新的一行
– VSYNC 是垂直同步信号(帧同步信号),当产生此信号表示开始显示新的一帧图像
– HBP、HFP、VBP、VFP 是导致上图中黑边的原因,发送一行或者一帧数据给屏幕内部的IC,IC是需要反应时间的,通过这段反应时间可以让IC识别到一行或者一帧图像扫描完了

HBP、 HFP、 VBP 和 VFP 就是导致图 24.1.1.5 中黑边的原因,但是这是 CRT 显示器存在黑
边的原因,现在是 LCD 显示器,不需要电子枪了,那么为何还会有黑边呢?这是因为 RGB LCD
屏幕内部是有一个 IC 的,发送一行或者一帧数据给 IC, IC 是需要反应时间的。通过这段反应
时间可以让 IC 识别到一行数据扫描完了
,要换行了,或者一帧图像扫描完了,要开始下一帧图
像显示了。因此,在 LCD 屏幕中继续存在 HBP、 HFP、 VPB 和 VFP 这四个参数的主要目的是
为了锁定有效的像素数据。这四个时间是 LCD 重要的时间参数
,后面编写 LCD 驱动的时候要
用到的,至于这四个时间参数具体值是多少,那要需要去查看所使用的 LCD 数据手册了。

2.2 RGB LCD 屏幕时序

2.2.1 行显示时序图

行显示时序
– HSYNC:水平同步信号(行同步信号),当产生此信号表示开始显示新的一行
– HSPW:HSYNC信号宽度,即HSYNC信号持续时间,单位为CLK
– HBP:行同步信号后肩,单位为CLK
– HOZVAL:显示一行数据所需的时间,若分辨率为1024*600,该值就是1024,单位为CLK
– HFP:行同步信号前肩,单位为CLK

当显示完一行数据以后需要等待 HFP 个 CLK 时间才能发出下一个 HSYNC 信号,所以
显示一行所需要的时间就是: HSPW + HBP + HOZVAL + HFP。


2.2.2 帧显示时序图

2.2.3 像素时钟

像素时钟就是 RGB LCD 的时钟信号,以 ATK7016 这款屏幕为例,显示一帧图像所需要的
时钟数就是:
= (VSPW+VBP+LINE+VFP) * (HSPW + HBP + HOZVAL + HFP)
= (3 + 20 + 600 + 12) * (20 + 140 + 1024 + 160)
= 635 * 1344
= 853440。
显示一帧图像需要853440个时钟数,那么显示60帧就是: 853440 * 60 = 51206400≈51.2M,
所以像素时钟就是 51.2MHz

  • 1. 选择器,用于选择哪个PLL 作为LCDIF时钟源  CCM_CSCDR2 的位 LCDIF1_PRE_CLK_SEL(bit17:15)来决定  专用的 PLL5 给 VIDEO 使用,所以LCDIF1_PRE_CLK_SEL 设置为 2
  • 2. LCDIF时钟的预分频器   由寄存器 CCM_CSCDR2 的位 LCDIF1_PRED 来决定预分频值。 可设置值为 0~7,分别对应 1~8 分频。
  • 3. 进一步分频   由寄存器 CBCMR 的位 LCDIF1_PODF 来决定分频值。 可设置值为 0~7,分别对应 1~8 分频。
  • 4. 选择器,用于选择LCDIF最终的根时钟。 由寄存器 CSCDR2 的位LCDIF1_CLK_SEL 决定
     

CCM_CSCDR2


 

2.2.4 显存

在讲像素格式的时候就已经说过了,如果采用 ARGB8888 格式的话一个像素需要 4 个字节
的内存来存放像素数据,那么 1024*600 分辨率就需要 1024*600*4=2457600B≈2.4MB 内存。但
是 RGB LCD 内部是没有内存的,所以就需要在开发板上的 DDR3 中分出一段内存作为 RGB
LCD 屏幕的显存,我们如果要在屏幕上显示什么图像的话直接操作这部分显存即可
 

三  IMX6ULL Enhanced LCD Interface (eLCDIF)  

3,.1 eLCDIF 接口

eLCDIF是IMX6U自带的液晶屏幕接口,用于连接RGB LCD接口的屏幕,eLCDIF接口特性如下:

  • 支持RGB LCD的DE模式
  • 支持VSYNC模式以实现高速数据传输
  • 支持ITU-R BT.656格式的4:2:2的YCbCr数字视频,并将其转换为模拟TV信号
  • 支持8/16/18/24/32位LCD

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”小节,本教程不使用 MPU 接口。
2、 VSYNC 接口
VSYNC 接口时序和 MPU 接口时序基本一样,只是多了 VSYNC 信号来作为帧同步,当
LCDIF_CTRL 的位 VSYNC_MODE 为 1 的时候此接口使能。关于 VSYNC 接口的详细信息请参
考《I.MX6ULL 参考手册》第 2152 页的“34.4.7 VSYNC Interface”小节,本教程不使用 VSYNC
接口。
3、 DOTCLK 接口
DOTCLK 接口就是用来连接 RGB LCD 接口屏幕的, 它包括 VSYNC、 HSYNC、 DOTCLK
和 ENABLE(可选的)这四个信号,这样的接口通常被称为 RGB 接口
。 DOTCLK 接口时序如图
24.1.2.1 所示:

因为 DOTCLK 接口就是连接 RGB 屏幕的
本例程中使用的就是 DOTCLK接口,下面介绍eLCDIF接口的几个重要的寄存器

3.2 重要寄存器

3.2.1 LCDIF_CTRL 寄存器

  • SFTRST:eLCDIF软复位控制位  当此位为 1 的话就会强制复位 LCD
  • CLKGATE:此位必须为0,否则时钟不会进入到LCDIF
  • BYPASS_COUNT:DOTCLK模式下,必须置1
  • VSYNC_MODE:置1表示工作在VSYNC接口模式
  • DOTCLK_MODE:置1表示工作在DOTCLK接口模式
  •  INPUT_DATA_SWIZZLE:输入数据字节交换设置
  • CSC_DATA_SWIZZLE:CSC数据字节交换设置
  • LCD_DATABUS_WIDTH:LCD数据总线宽度   为 3 的话总线宽度为 24 位
  • WORD_LENGTH:输入的数据格式,即像素数据宽度
  • MASTER:置1表示eLCDIF工作在主模式
  • DATA_FORMAT_16_BIT:16位像素   RGB565 /ARGB 555
  • DATA_FORMAT_18_BIT:18位像素   RGB666
  • DATA_FORMAT_24_BIT:24位像素
  • RUN:eLCDIF接口运行控制位

3.2.2 LCDIF_CTRL1  

只用到BYTE_PACKING_FORMAT位,用来决定在32位的数据中哪些字节的数据有效,默认值为0xF(即所有字节有效),为0表示所有字节都无效

3.2.3 LCDIF_TRANSFER_COUNT 寄存器

用来设置所连接的RGB LCD屏幕分辨率大小

高16位是V_COUNT,是 LCD 的垂直分辨率。

低 16 位是 H_COUNT,是 LCD 的水平分辨率

如果 LCD 分辨率为1024*600 的话,那么 V_COUNT 就是 600, H_COUNT 就是 1024。

3.2.4 LCDIF_VDCTRL0 寄存器

这个寄存器是 VSYNC 和 DOTCLK 模式控制寄
存器 0
 

LCDIF_VDCTRL0  

  •  – VSYNC:VSYNC信号方向控制位,为0表示输出,为1表示输入
  • – ENABLE_PRESENT:数据线使能位,即DE数据线
  • – VSYNC_POL:VSYNC数据线极性设置位,为0表示低电平有效
  • – HSYNC_POL:HSYNC数据线极性设置位,为0表示低电平有效
  • – DOTCLK_POL:DOTCLK数据线极性设置位,为0表示下降沿锁存,上升沿捕获数据
  • – ENABLE_POL:ENABLE数据线极性设置位,为0表示低电平有效
  • – VSYNC_PERIOD_UNIT:VSYNC信号周期单位,为0表示以像素时钟为单位,为1表示以水平行为单位
  • – VSYNC_PULSE_WIDTH_UNIT:VSYNC信号脉冲宽度单位,为0表示以像素时钟为单位,为1表示以水平行为单位
  • – VSYNC_PULSE_WIDTH:VSPW参数设置位
     

3.2.5  LCDIF_VDCTRL1-4 寄存器

  1. LCDIF_VDCTRL1寄存器为两个VSYNC信号之间的长度,那就是VSPW+VBPD+HEIGHT+VFP。 
  2. LCDIF_VDCTRL2:VSYNC和DOTCLK模式控制寄存器2,高16位用于设置HSYNC信号宽度,低16位用于设置HSYNC总周期
  3. LCDIF_VDCTRL3:VSYNC和DOTCLK模式控制寄存器3
  4. HORIZONTAL_WAIT_CNT:用于DOTCLK模式,设置HSYNC信号产生到有效数据产生之间的时间,即HSPW+HBP

    VERTICAL_WAIT_CNT:用于VSYNC模式,设置VSYNC信号产生到有效数据产生之间的时间,即VSPW+VBP

  5. LCDIF_VDCTRL4:VSYNC和DOTCLK模式控制寄存器4 

  • SYNC_SIGNALS_ON(bit18):同步信号使能位,设置为 1 的话使能 VSYNC、 HSYNC、
  • DOTCLK 这些信号。
  • DOTCLK_H_VALID_DATA_CNT(bit15:0): 设置 LCD 的宽度,也就是水平像素数3量。。

3.2.6 BUF 显存寄存器

  • LCDIF_CUR_BUF 寄存器:当前帧缓冲区,一般为显存首地址
  • LCDIF_NEXT_BUF 寄存器:下一帧缓冲区,一般为显存首地址 

关于这些寄存器详细的描述,请参考《I.MX6ULL参考手册》第 2165 页的 34.6 小节。

四 实验 

本章我们使用 I.MX6U 的 eLCDIF 接口来驱动 ALIENTEK
的 ATK7016 这款屏幕,配置步骤如下:
1、初始化 LCD 所使用的 IO
首先肯定是初始化 LCD 所示使用的 IO,将其复用为 eLCDIF 接口 IO。
2、设置 LCD 的像素时钟
查阅所使用的 LCD 屏幕数据手册,或者自己计算出的时钟像素,然后设置 CCM 相应的寄
存器。
3、配置 eLCDIF 接口
设置 LCDIF 的寄存器 CTRL、 CTRL1、 TRANSFER_COUNT、 VDCTRL0~4、 CUR_BUF 和
NEXT_BUF。根据 LCD 的数据手册设置相应的参数。
4、编写 API 函数
驱动 LCD 屏幕的目的就是显示内容,所以需要编写一些基本的 API 函数,比如画点、画
线、画圆函数,字符串显示函数等

4.1 代码结构

例程主要文件:bsp_lcd.c、 bsp_lcd.h、 bsp_lcdapi.c、 bsp_lcdapi.h 和 font.h
这五个文件。 bsp_lcd.c 和 bsp_lcd.h 是 LCD 的驱动文件, bsp_lcdapi.c 和 bsp_lcdapi.h 是 LCD 的API 操作函数文件, font.h 是字符集点阵数据数组文件

4.2  代码解析

4.2.1 LCD初始化

1 void lcd_init(void); 

正点原子开发板 先读取屏幕ID    图 1.2

/*
 * 读取屏幕ID,
 * 描述:LCD_DATA23=R7(M0);LCD_DATA15=G7(M1);LCD_DATA07=B7(M2);
 * 		M2:M1:M0
 *		0 :0 :0	//4.3寸480*272 RGB屏,ID=0X4342
 *		0 :0 :1	//7寸800*480 RGB屏,ID=0X7084
 *	 	0 :1 :0	//7寸1024*600 RGB屏,ID=0X7016
 *  	1 :0 :1	//10.1寸1280*800,RGB屏,ID=0X1018
 *		1 :0 :0	//4.3寸800*480 RGB屏,ID=0X4384
 * @param 		: 无
 * @return 		: 屏幕ID
 */
unsigned short lcd_read_panelid(void)
{
	unsigned char idx = 0;

	/* 配置屏幕ID信号线 */
	IOMUXC_SetPinMux(IOMUXC_LCD_VSYNC_GPIO3_IO03, 0);
	IOMUXC_SetPinConfig(IOMUXC_LCD_VSYNC_GPIO3_IO03, 0X10B0);

	/* 打开模拟开关 */
	gpio_pin_config_t idio_config;
	idio_config.direction = kGPIO_DigitalOutput;
	idio_config.outputLogic = 1;
	gpio_init(GPIO3, 3, &idio_config);

	/* 读取ID值,设置G7 B7 R7为输入 */
	IOMUXC_SetPinMux(IOMUXC_LCD_DATA07_GPIO3_IO12, 0);		/* B7(M2) */
	IOMUXC_SetPinMux(IOMUXC_LCD_DATA15_GPIO3_IO20, 0);		/* G7(M1) */
	IOMUXC_SetPinMux(IOMUXC_LCD_DATA23_GPIO3_IO28, 0);		/* R7(M0) */

    //电器属性 输入
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA07_GPIO3_IO12, 0xF080);
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA15_GPIO3_IO20, 0xF080);
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA23_GPIO3_IO28, 0xF080);  

	idio_config.direction = kGPIO_DigitalInput;
	gpio_init(GPIO3, 12, &idio_config);
	gpio_init(GPIO3, 20, &idio_config);
	gpio_init(GPIO3, 28, &idio_config);

	idx = (unsigned char)gpio_pinread(GPIO3, 28); 	/* 读取M0 */
	idx |= (unsigned char)gpio_pinread(GPIO3, 20) << 1;	/* 读取M1 */
	idx |= (unsigned char)gpio_pinread(GPIO3, 12) << 2;	/* 读取M2 */

	if(idx==0)return ATK4342;		//4.3寸屏,480*272分辨率
	else if(idx==1)return ATK7084;	//7寸屏,800*480分辨率
	else if(idx==2)return ATK7016;	//7寸屏,1024*600分辨率
	else if(idx==4)return ATK4384;	//4寸屏,800*480分辨率
	else if(idx==5)return ATK1018;	//10.1寸屏,1280*800分辨率		
	else if(idx==7)return ATKVGA;   //VGA模块,1366*768分辨率
	else return 0;

}

2 定义LCD结构体


/* LCD控制参数结构体 */
struct tftlcd_typedef{
	unsigned short height;		/* LCD屏幕高度 */
	unsigned short width;		/* LCD屏幕宽度 */
	unsigned char pixsize;		/* LCD每个像素所占字节大小 */
	unsigned short vspw;
	unsigned short vbpd;
	unsigned short vfpd;
	unsigned short hspw;
	unsigned short hbpd;
	unsigned short hfpd;
	unsigned int framebuffer; 	/* LCD显存首地址   	  */
	unsigned int forecolor;		/* 前景色 */
	unsigned int backcolor;		/* 背景色 */
	unsigned int id;  			/*	屏幕ID */
};

3 LCD gpio初始化 复用 电器特性

/* 1、IO初始化复用功能 */
	IOMUXC_SetPinMux(IOMUXC_LCD_DATA00_LCDIF_DATA00,0);
	IOMUXC_SetPinMux(IOMUXC_LCD_DATA01_LCDIF_DATA01,0);
	IOMUXC_SetPinMux(IOMUXC_LCD_DATA02_LCDIF_DATA02,0);
	IOMUXC_SetPinMux(IOMUXC_LCD_DATA03_LCDIF_DATA03,0);
	IOMUXC_SetPinMux(IOMUXC_LCD_DATA04_LCDIF_DATA04,0);
	IOMUXC_SetPinMux(IOMUXC_LCD_DATA05_LCDIF_DATA05,0);
	IOMUXC_SetPinMux(IOMUXC_LCD_DATA06_LCDIF_DATA06,0);
	IOMUXC_SetPinMux(IOMUXC_LCD_DATA07_LCDIF_DATA07,0);
	
	IOMUXC_SetPinMux(IOMUXC_LCD_DATA08_LCDIF_DATA08,0);
	IOMUXC_SetPinMux(IOMUXC_LCD_DATA09_LCDIF_DATA09,0);
	IOMUXC_SetPinMux(IOMUXC_LCD_DATA10_LCDIF_DATA10,0);
	IOMUXC_SetPinMux(IOMUXC_LCD_DATA11_LCDIF_DATA11,0);
	IOMUXC_SetPinMux(IOMUXC_LCD_DATA12_LCDIF_DATA12,0);
	IOMUXC_SetPinMux(IOMUXC_LCD_DATA13_LCDIF_DATA13,0);
	IOMUXC_SetPinMux(IOMUXC_LCD_DATA14_LCDIF_DATA14,0);
	IOMUXC_SetPinMux(IOMUXC_LCD_DATA15_LCDIF_DATA15,0);

	IOMUXC_SetPinMux(IOMUXC_LCD_DATA16_LCDIF_DATA16,0);
	
	IOMUXC_SetPinMux(IOMUXC_LCD_DATA17_LCDIF_DATA17,0);
	IOMUXC_SetPinMux(IOMUXC_LCD_DATA18_LCDIF_DATA18,0);
	IOMUXC_SetPinMux(IOMUXC_LCD_DATA19_LCDIF_DATA19,0);
	IOMUXC_SetPinMux(IOMUXC_LCD_DATA20_LCDIF_DATA20,0);
	IOMUXC_SetPinMux(IOMUXC_LCD_DATA21_LCDIF_DATA21,0);
	IOMUXC_SetPinMux(IOMUXC_LCD_DATA22_LCDIF_DATA22,0);
	IOMUXC_SetPinMux(IOMUXC_LCD_DATA23_LCDIF_DATA23,0);

	IOMUXC_SetPinMux(IOMUXC_LCD_CLK_LCDIF_CLK,0);	
	IOMUXC_SetPinMux(IOMUXC_LCD_ENABLE_LCDIF_ENABLE,0);	
	IOMUXC_SetPinMux(IOMUXC_LCD_HSYNC_LCDIF_HSYNC,0);
	IOMUXC_SetPinMux(IOMUXC_LCD_VSYNC_LCDIF_VSYNC,0);

	IOMUXC_SetPinMux(IOMUXC_GPIO1_IO08_GPIO1_IO08,0);			/* 背光BL引脚      */
	IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_GPIO1_IO18,0xF080);

特性设置
 

/* 2、配置LCD IO属性	
	 *bit 16:0 HYS关闭
	 *bit [15:14]: 0 默认22K上拉
	 *bit [13]: 0 pull功能
	 *bit [12]: 0 pull/keeper使能 
	 *bit [11]: 0 关闭开路输出
	 *bit [7:6]: 10 速度100Mhz
	 *bit [5:3]: 111 驱动能力为R0/7
	 *bit [0]: 1 高转换率
	 */
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA00_LCDIF_DATA00,0xB9);
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA01_LCDIF_DATA01,0xB9);
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA02_LCDIF_DATA02,0xB9);
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA03_LCDIF_DATA03,0xB9);
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA04_LCDIF_DATA04,0xB9);
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA05_LCDIF_DATA05,0xB9);
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA06_LCDIF_DATA06,0xB9);
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA07_LCDIF_DATA07,0xB9);
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA08_LCDIF_DATA08,0xB9);
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA09_LCDIF_DATA09,0xB9);
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA10_LCDIF_DATA10,0xB9);
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA11_LCDIF_DATA11,0xB9);
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA12_LCDIF_DATA12,0xB9);
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA13_LCDIF_DATA13,0xB9);
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA14_LCDIF_DATA14,0xB9);
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA15_LCDIF_DATA15,0xB9);
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA16_LCDIF_DATA16,0xB9);
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA17_LCDIF_DATA17,0xB9);
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA18_LCDIF_DATA18,0xB9);
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA19_LCDIF_DATA19,0xB9);
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA20_LCDIF_DATA20,0xB9);
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA21_LCDIF_DATA21,0xB9);
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA22_LCDIF_DATA22,0xB9);
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA23_LCDIF_DATA23,0xB9);

	IOMUXC_SetPinConfig(IOMUXC_LCD_CLK_LCDIF_CLK,0xB9);
	IOMUXC_SetPinConfig(IOMUXC_LCD_ENABLE_LCDIF_ENABLE,0xB9);
	IOMUXC_SetPinConfig(IOMUXC_LCD_HSYNC_LCDIF_HSYNC,0xB9);
	IOMUXC_SetPinConfig(IOMUXC_LCD_VSYNC_LCDIF_VSYNC,0xB9);

	IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO08_GPIO1_IO08,0xB9);	/* 背光BL引脚 		*/

4 打开背光 否则屏幕黑屏

/* GPIO初始化 */
	gpio_config.direction = kGPIO_DigitalOutput;			/* 输出 			*/
	gpio_config.outputLogic = 1; 							/* 默认关闭背光 */
	gpio_init(GPIO1, 8, &gpio_config);						/* 背光默认打开 */
	gpio_pinwrite(GPIO1, 8, 1);								/* 打开背光     */

5 软复位LCD 结束复位 

/*
 * @description	: 复位ELCDIF接口
 * @param 		: 无
 * @return 		: 无
 */
void lcd_reset(void)
{
	LCDIF->CTRL  = 1<<31; /* 强制复位 */
}

/*
 * @description	: 结束复位ELCDIF接口
 * @param 		: 无
 * @return 		: 无
 */
void lcd_noreset(void)
{
	LCDIF->CTRL  = 0<<31; /* 取消强制复位 */
}

6 设置屏幕framebuffer、时序、像素格式、、参数、背光及像素时钟 时钟频率参考对应屏幕  2.2.3 像素时钟

//分辨率 vsync 信号宽度 、帧同步信号后肩 、前肩、Hsync 信号宽度、水平后肩、前肩

if(lcdid == ATK4342) {
        //分辨率 vsync 信号宽度 、帧同步信号后肩 、前肩、Hsync 信号宽度、水平后肩、前肩
		tftlcd_dev.height = 272;	
		tftlcd_dev.width = 480;
		tftlcd_dev.vspw = 1;
		tftlcd_dev.vbpd = 8;
		tftlcd_dev.vfpd = 8;
		tftlcd_dev.hspw = 1;
		tftlcd_dev.hbpd = 40;
		tftlcd_dev.hfpd = 5; 	
		lcdclk_init(27, 8, 8);	/* 初始化LCD时钟 10.1MHz */
	} else if(lcdid == ATK4384) {
		tftlcd_dev.height = 480;	
		tftlcd_dev.width = 800;
		tftlcd_dev.vspw = 3;
		tftlcd_dev.vbpd = 32;
		tftlcd_dev.vfpd = 13;
		tftlcd_dev.hspw = 48;
		tftlcd_dev.hbpd = 88;
		tftlcd_dev.hfpd = 40;
		lcdclk_init(42, 4, 8);	/* 初始化LCD时钟 31.5MHz */
	} else if(lcdid == ATK7084) {
		tftlcd_dev.height = 480;	
		tftlcd_dev.width = 800;
		tftlcd_dev.vspw = 1;
		tftlcd_dev.vbpd = 23;
		tftlcd_dev.vfpd = 22;
		tftlcd_dev.hspw = 1;
		tftlcd_dev.hbpd = 46;
		tftlcd_dev.hfpd = 210;	
		lcdclk_init(30, 3, 7);	/* 初始化LCD时钟 34.2MHz */
	} else if(lcdid == ATK7016) {
		tftlcd_dev.height = 600;	
		tftlcd_dev.width = 1024;
		tftlcd_dev.vspw = 3;
		tftlcd_dev.vbpd = 20;
		tftlcd_dev.vfpd = 12;
		tftlcd_dev.hspw = 20;
		tftlcd_dev.hbpd = 140;
		tftlcd_dev.hfpd = 160;
		lcdclk_init(32, 3, 5);	/* 初始化LCD时钟 51.2MHz */
	} else if(lcdid == ATK1018) {
		tftlcd_dev.height = 800;	
		tftlcd_dev.width = 1280;
		tftlcd_dev.vspw = 3;
		tftlcd_dev.vbpd = 10;
		tftlcd_dev.vfpd = 10;
		tftlcd_dev.hspw = 10;
		tftlcd_dev.hbpd = 80;
		tftlcd_dev.hfpd = 70;
		lcdclk_init(35, 3, 5);	/* 初始化LCD时钟 56MHz */
	} else if(lcdid == ATKVGA) {  
		tftlcd_dev.height = 768;	
		tftlcd_dev.width = 1366;
		tftlcd_dev.vspw = 3;
		tftlcd_dev.vbpd = 24;
		tftlcd_dev.vfpd = 3;
		tftlcd_dev.hspw = 143;
		tftlcd_dev.hbpd = 213;
		tftlcd_dev.hfpd = 70;
		lcdclk_init(32, 3, 3);	/* 初始化LCD时钟 85MHz */
	}
	tftlcd_dev.pixsize = 4;				/* ARGB8888模式,每个像素4字节 */
	tftlcd_dev.framebuffer = LCD_FRAMEBUF_ADDR;	
	tftlcd_dev.backcolor = LCD_WHITE;	/* 背景色为白色 */
	tftlcd_dev.forecolor = LCD_BLACK;	/* 前景色为黑色 */

clk 时钟源 lcdclk_init 2.2.3 像素时钟 多级分频

/*
 * @description		: LCD时钟初始化, LCD时钟计算公式如下:
 *                	  LCD CLK = 24 * loopDiv / prediv / div
 * @param -	loopDiv	: loopDivider值
 * @param -	prediv  : lcdifprediv值
 * @param -	div		: lcdifdiv值
 * @return 			: 无
 */
void lcdclk_init(unsigned char loopDiv, unsigned char prediv, unsigned char div)
{
	/* 先初始化video pll 
     * VIDEO PLL = OSC24M * (loopDivider + (denominator / numerator)) / postDivider
 	 *不使用小数分频器,因此denominator和numerator设置为0
 	 */
	CCM_ANALOG->PLL_VIDEO_NUM = 0;		/* 不使用小数分频器 */
	CCM_ANALOG->PLL_VIDEO_DENOM = 0;	

	/*
     * PLL_VIDEO寄存器设置
     * bit[13]:    1   使能VIDEO PLL时钟
     * bit[20:19]  2  设置postDivider为1分频
     * bit[6:0] : 32  设置loopDivider寄存器
	 */
	CCM_ANALOG->PLL_VIDEO =  (2 << 19) | (1 << 13) | (loopDiv << 0); 

	/*
     * MISC2寄存器设置
     * bit[31:30]: 0  VIDEO的post-div设置,时钟源来源于postDivider,1分频
	 */
	CCM_ANALOG->MISC2 &= ~(3 << 30);
	CCM_ANALOG->MISC2 = 0 << 30;

	/* LCD时钟源来源与PLL5,也就是VIDEO           PLL  */
	CCM->CSCDR2 &= ~(7 << 15);  	
	CCM->CSCDR2 |= (2 << 15);			/* 设置LCDIF_PRE_CLK使用PLL5 */

	/* 设置LCDIF_PRE分频 */
	CCM->CSCDR2 &= ~(7 << 12);		
	CCM->CSCDR2 |= (prediv - 1) << 12;	/* 设置分频  */

	/* 设置LCDIF分频 */
	CCM->CBCMR &= ~(7 << 23);					
	CCM->CBCMR |= (div - 1) << 23;				

	/* 设置LCD时钟源为LCDIF_PRE时钟 */
	CCM->CSCDR2 &= ~(7 << 9);					/* 清除原来的设置		 	*/
	CCM->CSCDR2 |= (0 << 9);					/* LCDIF_PRE时钟源选择LCDIF_PRE时钟 */
}

7 相关寄存器设置

/* 初始化ELCDIF的CTRL寄存器
     * bit [31] 0 : 停止复位
     * bit [19] 1 : 旁路计数器模式
     * bit [17] 1 : LCD工作在dotclk模式
     * bit [15:14] 00 : 输入数据不交换
     * bit [13:12] 00 : CSC不交换
     * bit [11:10] 11 : 24位总线宽度
     * bit [9:8]   11 : 24位数据宽度,也就是RGB888
     * bit [5]     1  : elcdif工作在主模式
     * bit [1]     0  : 所有的24位均有效
	 */
	 LCDIF->CTRL |= (1 << 19) | (1 << 17) | (0 << 14) | (0 << 12) |
	 				(3 << 10) | (3 << 8) | (1 << 5) | (0 << 1);
	/*
     * 初始化ELCDIF的寄存器CTRL1
     * bit [19:16]  : 0X7 ARGB模式下,传输24位数据,A通道不用传输
	 */	
	 LCDIF->CTRL1 = 0X7 << 16; 

	 /*
      * 初始化ELCDIF的寄存器TRANSFER_COUNT寄存器
      * bit [31:16]  : 高度
      * bit [15:0]   : 宽度
	  */
	LCDIF->TRANSFER_COUNT  = (tftlcd_dev.height << 16) | (tftlcd_dev.width << 0);

	/*
     * 初始化ELCDIF的VDCTRL0寄存器
     * bit [29] 0 : VSYNC输出
     * bit [28] 1 : 使能ENABLE输出
     * bit [27] 0 : VSYNC低电平有效
     * bit [26] 0 : HSYNC低电平有效
     * bit [25] 0 : DOTCLK上升沿有效
     * bit [24] 1 : ENABLE信号高电平有效
     * bit [21] 1 : DOTCLK模式下设置为1
     * bit [20] 1 : DOTCLK模式下设置为1
     * bit [17:0] : vsw参数
	 */
	LCDIF->VDCTRL0 = 0;	//先清零
	if(lcdid == ATKVGA) {   //VGA需要特殊处理
		LCDIF->VDCTRL0 = (0 << 29) | (1 << 28) | (0 << 27) |
					 (0 << 26) | (1 << 25) | (0 << 24) |
					 (1 << 21) | (1 << 20) | (tftlcd_dev.vspw << 0);
	} else {
		LCDIF->VDCTRL0 = (0 << 29) | (1 << 28) | (0 << 27) |
					 (0 << 26) | (0 << 25) | (1 << 24) |
					 (1 << 21) | (1 << 20) | (tftlcd_dev.vspw << 0);
	}

	/*
	 * 初始化ELCDIF的VDCTRL1寄存器
	 * 设置VSYNC总周期
	 */  
	LCDIF->VDCTRL1 = tftlcd_dev.height + tftlcd_dev.vspw + tftlcd_dev.vfpd + tftlcd_dev.vbpd;  //VSYNC周期
	 
	 /*
	  * 初始化ELCDIF的VDCTRL2寄存器
	  * 设置HSYNC周期
	  * bit[31:18] :hsw
	  * bit[17:0]  : HSYNC总周期
	  */ 
	LCDIF->VDCTRL2 = (tftlcd_dev.hspw << 18) | (tftlcd_dev.width + tftlcd_dev.hspw + tftlcd_dev.hfpd + tftlcd_dev.hbpd);

	/*
	 * 初始化ELCDIF的VDCTRL3寄存器
	 * 设置HSYNC周期
	 * bit[27:16] :水平等待时钟数
	 * bit[15:0]  : 垂直等待时钟数
	 */ 
	LCDIF->VDCTRL3 = ((tftlcd_dev.hbpd + tftlcd_dev.hspw) << 16) | (tftlcd_dev.vbpd + tftlcd_dev.vspw);

	/*
	 * 初始化ELCDIF的VDCTRL4寄存器
	 * 设置HSYNC周期
	 * bit[18] 1 : 当使用VSHYNC、HSYNC、DOTCLK的话此为置1
	 * bit[17:0]  : 宽度
	 */ 
	
	LCDIF->VDCTRL4 = (1<<18) | (tftlcd_dev.width);

	/*
     * 初始化ELCDIF的CUR_BUF和NEXT_BUF寄存器
     * 设置当前显存地址和下一帧的显存地址
	 */
	LCDIF->CUR_BUF = (unsigned int)tftlcd_dev.framebuffer;
	LCDIF->NEXT_BUF = (unsigned int)tftlcd_dev.framebuffer;

8 使能LCD及清屏


	lcd_enable();			/* 使能LCD 	*/
	delayms(10);
	lcd_clear(LCD_WHITE);	/* 清屏 		*/
/*
 * @description		: 清屏
 * @param - color	: 颜色值
 * @return 			: 读取到的指定点的颜色值
 */
void lcd_clear(unsigned int color)
{
	unsigned int num;
	unsigned int i = 0; 

	unsigned int *startaddr=(unsigned int*)tftlcd_dev.framebuffer;	//指向帧缓存首地址
	num=(unsigned int)tftlcd_dev.width * tftlcd_dev.height;			//缓冲区总长度
	for(i = 0; i < num; i++)
	{
		startaddr[i] = color;
	}		
}

 

4.2.2 LCD Api

读写像素点 即写framebuffer 偏移对应地址

/*
 * @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));
}

填充api

/*
 * @description		: 以指定的颜色填充一块矩形
 * @param - x0		: 矩形起始点坐标X轴
 * @param - y0		: 矩形起始点坐标Y轴
 * @param - x1		: 矩形终止点坐标X轴
 * @param - y1		: 矩形终止点坐标Y轴
 * @param - color	: 要填充的颜色
 * @return 			: 读取到的指定点的颜色值
 */
void lcd_fill(unsigned    short x0, unsigned short y0, 
                 unsigned short x1, unsigned short y1, unsigned int color)
{ 
    unsigned short x, y;

	if(x0 < 0) x0 = 0;
	if(y0 < 0) y0 = 0;
	if(x1 >= tftlcd_dev.width) x1 = tftlcd_dev.width - 1;
	if(y1 >= tftlcd_dev.height) y1 = tftlcd_dev.height - 1;
	
    for(y = y0; y <= y1; y++)
    {
        for(x = x0; x <= x1; x++)
			lcd_drawpoint(x, y, color);
    }
}

LCD 显示字体 

/***************************************************************
Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
文件名	: 	 bsp_lcdapi.c
作者	   : 左忠凯
版本	   : V1.0
描述	   : LCD API函数文件。
其他	   : 无
论坛 	   : www.wtmembed.com
日志	   : 初版V1.0 2019/3/18 左忠凯创建
***************************************************************/
#include "bsp_lcdapi.h"
#include "font.h" 

/*
 * @description		: 画线函数
 * @param - x1		: 线起始点坐标X轴
 * @param - y1		: 线起始点坐标Y轴
 * @param - x2		: 线终止点坐标X轴
 * @param - y2		: 线终止点坐标Y轴
 * @return 			: 无
 */ 
void lcd_drawline(unsigned short x1, unsigned short y1, unsigned short x2, unsigned short y2)
{
	u16 t; 
	int xerr = 0, yerr = 0, delta_x, delta_y, distance; 
	int incx, incy, uRow, uCol; 
	delta_x = x2 - x1; 					/* 计算坐标增量 */
	delta_y = y2 - y1; 
	uRow = x1; 
	uCol = y1; 
	if(delta_x > 0) 					/* 设置单步方向 */ 
		incx = 1;
	else if(delta_x==0)					/* 垂直线 */
		incx = 0;		
	else 
	{
		incx = -1;
		delta_x = -delta_x;
	} 
	if(delta_y>0)
		incy=1; 
	else if(delta_y == 0)				/* 水平线 */
		incy=0;
	else
	{
		incy = -1;
		delta_y = -delta_y;
	} 
	if( delta_x > delta_y)				/*选取基本增量坐标轴  */
		distance = delta_x; 
	else 
		distance = delta_y; 
	for(t = 0; t <= distance+1; t++ )	/* 画线输出 */
	{  

		lcd_drawpoint(uRow, uCol, tftlcd_dev.forecolor);/* 画点 */
		xerr += delta_x ; 
		yerr += delta_y ; 
		if(xerr > distance) 
		{ 
			xerr -= distance; 
			uRow += incx; 
		} 
		if(yerr > distance) 
		{ 
			yerr -= distance; 
			uCol += incy; 
		} 
	}  
}   

/*
 * @description	: 画矩形函数
 * @param - x1	: 矩形坐上角坐标X轴
 * @param - y1	: 矩形坐上角坐标Y轴
 * @param - x2	: 矩形右下角坐标X轴
 * @param - y2	: 矩形右下角坐标Y轴
 * @return 		: 无
 */
void lcd_draw_rectangle(unsigned short x1, unsigned short y1, unsigned short x2, unsigned short y2)
{
	lcd_drawline(x1, y1, x2, y1);
	lcd_drawline(x1, y1, x1, y2);
	lcd_drawline(x1, y2, x2, y2);
	lcd_drawline(x2, y1, x2, y2);
}

/*
 * @description	: 在指定位置画一个指定大小的圆
 * @param - x0	: 圆心坐标X轴
 * @param - y0	: 圆心坐标Y轴
 * @param - y2	: 圆形半径
 * @return 		: 无
 */
void lcd_draw_Circle(unsigned short x0,unsigned short y0,unsigned char r)
{
    int mx = x0, my = y0;
    int x = 0, y = r;

    int d = 1 - r;   
    while(y > x)    	/* y>x即第一象限的第1区八分圆 */
    {
        lcd_drawpoint(x  + mx, y  + my, tftlcd_dev.forecolor);
        lcd_drawpoint(y  + mx, x  + my, tftlcd_dev.forecolor);
        lcd_drawpoint(-x + mx, y  + my, tftlcd_dev.forecolor);
        lcd_drawpoint(-y + mx, x  + my, tftlcd_dev.forecolor);

        lcd_drawpoint(-x + mx, -y + my, tftlcd_dev.forecolor);
        lcd_drawpoint(-y + mx, -x + my, tftlcd_dev.forecolor);
        lcd_drawpoint(x  + mx, -y + my, tftlcd_dev.forecolor);
        lcd_drawpoint(y  + mx, -x + my, tftlcd_dev.forecolor);
        if( d < 0)
        {
            d = d + 2 * x + 3;
        }
        else
        {
            d= d + 2 * (x - y) + 5;
            y--;
        }
        x++;
    }
}

/*
 * @description	: 在指定位置显示一个字符
 * @param - x	: 起始坐标X轴
 * @param - y	: 起始坐标Y轴
 * @param - num	: 显示字符
 * @param - size: 字体大小, 可选12/16/24/32
 * @param - mode: 叠加方式(1)还是非叠加方式(0)
 * @return 		: 无
 */
void lcd_showchar(unsigned     short x, unsigned short y,
				      unsigned char num, unsigned char size, 
				      unsigned char mode)
{  							  
    unsigned char  temp, t1, t;
	unsigned short y0 = y;
	unsigned char csize = (size / 8+ ((size % 8) ? 1 : 0)) * (size / 2);	/* 得到字体一个字符对应点阵集所占的字节数	 */	
 	num = num - ' ';  	/*得到偏移后的值(ASCII字库是从空格开始取模,所以-' '就是对应字符的字库)  */
	for(t = 0; t < csize; t++)
	{   
		if(size == 12) temp = asc2_1206[num][t]; 		/* 调用1206字体 */
		else if(size == 16)temp = asc2_1608[num][t];	/* 调用1608字体 */
		else if(size == 24)temp = asc2_2412[num][t];	/* 调用2412字体 */
		else if(size == 32)temp = asc2_3216[num][t];	/* 调用3216字体 */
		else return;									/* 没有的字库 		*/
		for(t1 = 0; t1 < 8; t1++)
		{			    
			if(temp & 0x80)lcd_drawpoint(x, y, tftlcd_dev.forecolor);
			else if(mode==0)lcd_drawpoint(x, y, tftlcd_dev.backcolor);
			temp <<= 1;
			y++;
			if(y >= tftlcd_dev.height) return;			/* 超区域了 */	
			if((y - y0) == size)
			{
				y = y0;
				x++;
				if(x >= tftlcd_dev.width) return;		/* 超区域了 */
				break;
			}
		}  	 
	}  		    	   	 	  
} 

/*
 * @description	: 计算m的n次方
 * @param - m	: 要计算的值
 * @param - n	: n次方
 * @return 		: m^n次方.
 */
unsigned int lcd_pow(unsigned char m,unsigned char n)
{
	unsigned int result = 1;	 
	while(n--) result *= m;    
	return result;
}

/*
 * @description	: 显示指定的数字,高位为0的话不显示
 * @param - x	: 起始坐标点X轴。
 * @param - y	: 起始坐标点Y轴。
 * @param - num	: 数值(0~999999999)。
 * @param - len : 数字位数。
 * @param - size: 字体大小
 * @return 		: 无
 */
void lcd_shownum(unsigned     short x, 
					 unsigned short y, 
					 unsigned int num, 
					 unsigned char len,
					 unsigned char size)
{         	
	unsigned char  t, temp;
	unsigned char  enshow = 0;						   
	for(t = 0; t < len; t++)
	{
		temp = (num / lcd_pow(10, len - t - 1)) % 10;
		if(enshow == 0 && t < (len - 1))
		{
			if(temp == 0)
			{
				lcd_showchar(x + (size / 2) * t, y, ' ', size, 0);
				continue;
			}else enshow = 1; 	 
		}
	 	lcd_showchar(x + (size / 2) * t, y, temp + '0', size, 0); 
	}
} 

/*
 * @description		: 显示指定的数字,高位为0,还是显示
 * @param - x	 	: 起始坐标点X轴。
 * @param - y	 	: 起始坐标点Y轴。
 * @param - num		: 数值(0~999999999)。
 * @param - len 	: 数字位数。
 * @param - size	: 字体大小
 * @param - mode	: [7]:0,不填充;1,填充0.
 * 					  [6:1]:保留
 *					  [0]:0,非叠加显示;1,叠加显示.
 * @return 	 		: 无
 */
void lcd_showxnum(unsigned     short x, unsigned short y, 
					  unsigned int num, unsigned char len, 
					  unsigned char size, unsigned char mode)
{  
	unsigned char t, temp;
	unsigned char enshow = 0;						   
	for(t = 0; t < len; t++)
	{
		temp = (num / lcd_pow(10, len - t- 1)) % 10;
		if(enshow == 0 && t < (len - 1))
		{
			if(temp == 0)
			{
				if(mode & 0X80) lcd_showchar(x + (size / 2) * t, y, '0', size, mode & 0X01);  
				else  lcd_showchar(x + (size / 2) * t, y , ' ', size, mode & 0X01);  
 				continue;
			}else enshow=1; 
		 	 
		}
	 	lcd_showchar( x + (size / 2) * t, y, temp + '0' , size , mode & 0X01); 
	}
} 

/*
 * @description		: 显示一串字符串
 * @param - x		: 起始坐标点X轴。
 * @param - y		: 起始坐标点Y轴。
 * @param - width 	: 字符串显示区域长度
 * @param - height	: 字符串显示区域高度
 * @param - size	: 字体大小
 * @param - p		: 要显示的字符串首地址
 * @return 			: 无
 */
void lcd_show_string(unsigned short x,unsigned short y,
						  unsigned short width,unsigned short height,
						  unsigned char size,char *p)
{         
	unsigned char x0 = x;
	width += x;
	height += y;
    while((*p <= '~') &&(*p >= ' '))		/* 判断是不是非法字符! */ 
    {       
        if(x >= width) {x = x0; y += size;}
        if(y >= height) break;				/* 退出 */
        lcd_showchar(x, y, *p , size, 0);
        x += size / 2;
        p++;
    }  
}


4.2.3 测试验证 显示

/**************************************************************
Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
文件名	: 	 mian.c
作者	   : 左忠凯
版本	   : V1.0
描述	   : I.MX6U开发板裸机实验16 LCD液晶屏实验
其他	   : 本实验学习如何在I.MX6U上驱动RGB LCD液晶屏幕,I.MX6U有个
 		 ELCDIF接口,通过此接口可以连接一个RGB LCD液晶屏。
论坛 	   : www.wtmembed.com
日志	   : 初版V1.0 2019/1/15 左忠凯创建
**************************************************************/
#include "bsp_clk.h"
#include "bsp_delay.h"
#include "bsp_led.h"
#include "bsp_beep.h"
#include "bsp_key.h"
#include "bsp_int.h"
#include "bsp_uart.h"
#include "stdio.h"
#include "bsp_lcd.h"
#include "bsp_lcdapi.h"


/* 背景颜色索引 */
unsigned int backcolor[10] = {
	LCD_BLUE, 		LCD_GREEN, 		LCD_RED, 	LCD_CYAN, 	LCD_YELLOW, 
	LCD_LIGHTBLUE, 	LCD_DARKBLUE, 	LCD_WHITE, 	LCD_BLACK, 	LCD_ORANGE

}; 
	

/*
 * @description	: main函数
 * @param 		: 无
 * @return 		: 无
 */
int main(void)
{
	unsigned char index = 0;
	unsigned char state = OFF;

	int_init(); 				/* 初始化中断(一定要最先调用!) */
	imx6u_clkinit();			/* 初始化系统时钟 			*/
	delay_init();				/* 初始化延时 			*/
	clk_enable();				/* 使能所有的时钟 			*/
	led_init();					/* 初始化led 			*/
	beep_init();				/* 初始化beep	 		*/
	uart_init();				/* 初始化串口,波特率115200 */
	lcd_init();					/* 初始化LCD 			*/

	tftlcd_dev.forecolor = LCD_RED;	  
	while(1)				
	{	
		lcd_clear(backcolor[index]);
		delayms(1); 	  
		lcd_show_string(10, 40, 260, 32, 32,(char*)"ALPHA IMX6U"); 	
		lcd_show_string(10, 80, 240, 24, 24,(char*)"RGBLCD TEST");
		lcd_show_string(10, 110, 240, 16, 16,(char*)"ATOM@ALIENTEK");      					 
		lcd_show_string(10, 130, 240, 12, 12,(char*)"2019/8/14");	      					 
	    index++;
		if(index == 10)
			index = 0;      
		state = !state;
		led_switch(LED0,state);
		delayms(1000);

#if 0
		index++;
		if(index == 10)
			index = 0;
		lcd_fill(0, 300, 1023, 599, backcolor[index]);
		lcd_show_string(800,10,240,32,32,(char*)"INDEX=");  /*显示字符串				  */
		lcd_shownum(896,10, index, 2, 32); 					/* 显示数字,叠加显示	*/
		
		state = !state;
		led_switch(LED0,state);
		delayms(1000);	/* 延时一秒	*/
#endif
	}
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/718833.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

IDEA 集成 GIT

文章目录 16.IDEA中使用GIT-基本操作-add与commit 掌握目标内容 17.IDEA中使用GIT-基本操作-差异化比较目标内容 18.IDEA中使用GIT-基本操作-版本回退及撤消目标内容 19.IDEA中使用GIT-远程仓库-创建与关联远程仓库学习目标&#xff1a; 20.GitLab使用介绍_项目、用户和组介绍(…

Spark环境搭建与使用

第4关&#xff1a;使用Maven对Java独立应用程序进行编译打包 目录 第4关&#xff1a;使用Maven对Java独立应用程序进行编译打包 任务描述 相关知识 Maven安装 编程要求 任务描述 本关任务&#xff1a;编写一个简单的应用程序simpleApp2&#xff08;实现spark自带文件REA…

Git使用入门

文章目录 简介安装linux离线安装windows安装 用户配置简单使用设置用户名提交至暂存区提交delete变更git add . 与 git add * 向远程库pushpush -f 分支建空白分支分支合并分支冲突 从远程库pull当远程库版本跟本地仓库不一致直接pull尚未pull 暂存区文件移除gitignoregitignor…

DuDuTalk:人工智能时代,AI技术如何赋能销售?

在数字化时代&#xff0c;人工智能&#xff08;AI&#xff09;正悄然改变着我们的生活和工作方式。销售领域作为商业运营的核心&#xff0c;自然也不能幸免于AI技术的影响。通过应用人工智能&#xff0c;销售团队能够在客户关系管理、销售预测、个性化营销等方面获得巨大的优势…

保姆级AT32F437 配置RT-Thread 以太网(UDP/TCP Server)

好记性不如烂笔头&#xff0c;既然不够聪明&#xff0c;就乖乖的做笔记&#xff0c;温故而知新。 本文档用于本人对知识点的梳理和记录。 一、前言 ENV版本&#xff1a;v1.3.5 rt-thread版本&#xff1a;V5 开发板&#xff1a;雅特力AT32F437 AT-START-F437 二、ENV配置 E…

回顾 | Let's Learn .NET - Web 开发实战

点击蓝字 关注我们 编辑&#xff1a;Alan Wang 排版&#xff1a;Rani Sun Lets Learn .NET 系列 “Lets Learn .NET” 是面向全球的 .NET 初学者学习系列&#xff0c;旨在通过不同语言&#xff0c;帮助不同地区的开发者掌握最新的 .NET 开发知识与技能。 “Lets Learn .NET”系…

【云原生架构模式】库(Library ) vs 服务(Service ) vs Sidecar(边车)

所有软件应用程序都由可重用的元素组成。这些可重用元素的目标和功能从基础设施级别到安全级别到业务能力各不相同。本文的目的是比较用于构建和部署这些可重用元素的不同方法。 1. 库 这是重用代码的最广泛使用的方法。可重用代码作为库开发和发布。在这种方法中&#xff0c;客…

web前端开发工程师工作的岗位职责(合集)

web前端开发工程师工作的岗位职责1 职责&#xff1a; 1、根据设计图进行前端页面开发并设计编写业务交互脚本 2、优化前端页面&#xff0c;保证良好的用户体验以及不同浏览器的兼容性 3、web前沿技术研究和新技术调研&#xff0c;将主流的特效应用到业务场景中 4、配合后台…

政府关注 | 国家人社部报道盖雅工场以云服务助力劳动者灵活就业

党的二十大报告提出&#xff0c;「加强灵活就业和新就业形态劳动者权益保障」&#xff0c;为促进新就业形态规范健康发展指明了方向。近年来&#xff0c;随着数字经济快速发展&#xff0c;依托互联网平台的新就业形态脱颖而出&#xff0c;成为吸纳就业、提高劳动参与率、增加劳…

FFmpeg从入门到入魔(2):保存流到本地MP4

1 . FFmpeg裁剪移植 之前我们简单地讲解了下如何在Linux系统中编译FFmpeg&#xff0c;但是编译出来的so体积太大&#xff0c;而且得到的多个so不便于使用。本节在此基础上&#xff0c;将详细讲解在编译FFmpeg时如何对相关模块作裁剪以精简so的体积&#xff0c;并且编译只生成一…

【Flutter 组件】005-基础组件:单选、开关和复选框

【Flutter 组件】005-基础组件&#xff1a;单选、开关和复选框 文章目录 【Flutter 组件】005-基础组件&#xff1a;单选、开关和复选框一、概述二、基本使用1、开关代码示例运行结果 2、复选框代码示例运行结果 3、多个选项单选代码示例运行结果 4、多个选项多选代码示例运行结…

一次源码编译安装PostgreSql失败

需要perl&#xff1b;之前博文已提到&#xff1b;之前有一种编程语言叫perl&#xff0c;此perl应该不是那个&#xff1b;可到其官网下载&#xff0c;Perl Download - www.perl.org 安装时添加到环境变量&#xff1b; 可能是一个东西&#xff1b;有编程语言和工具&#xff1b;大…

怎样愉快的使用串口发送16进制数据并读取串口内容

像雷达 imu 陀螺仪一类的传感器&#xff0c;一般都是用的usb转串口和主机连接&#xff0c;然后通过串口读取传感器数据&#xff0c;串口是我们绕不过的一道坎&#xff0c;那我们就来继续手撕串口。 串口连接主机问题看上篇&#xff1a; 怎样愉快的连接使用usb转串口设备_JT_B…

WalxPlugin免Root框架使用详解和示例代码

2023年7月4日首发 WalxPlugin框架&#xff08;以下简称WP框架&#xff09;是一个不需要root权限就能使用的插件化模块&#xff0c;能够轻松实现在非root设备hook其它应用的调用和访问进程数据等功能。目前该框架已发布测试版。 一.WP框架工具包提供了以下几个模块&#xff1a…

5.8.5 TCP可靠传输(一)序号确认机制

5.8.5 TCP可靠传输&#xff08;一&#xff09;序号确认机制 TCP是可靠的传输层协议&#xff0c;主要通过序号确认机制、超时重传机制、定时器三个方面实现可靠传输。 一、序号确认机制 TCP将所要传送的整个的应用层报文看成是一个一个字节组成的数据流&#xff0c;并对每一个…

Cyclo(-Ala-Tyr),21754-26-7,环(丙氨酸-酪氨酸)二肽,由两个氨基酸通过肽键环合形成

&#xff08;文章资料汇总&#xff1a;陕西新研博美生物科技有限公司小编MISSwu&#xff09;​ 【产品描述】 Cyclo(-Ala-Tyr)&#xff0c;环(丙氨酸-酪氨酸)二肽&#xff0c;环二肽由两个氨基酸通过肽键环合形成&#xff0c;在氢键相互作用驱动下具有较强的自组装倾向&#x…

python的作用域、globals()-全局变量 和 locals()-局部变量

目录 查看全局变量和局部变量 变量解析规则 变量生存周期 在python中&#xff0c;函数会创建一个自己的作用域&#xff0c;也称为为命名空间。当我们在函数内部访问某个变量时&#xff0c;函数会优先在自己的命名空间中寻找。 我们自己定义的全局变量均在python内建的globa…

java项目linux启动文件

更改jar包名称和jar包所在目录 JAR包名称 替换成自己的项目包名称 JAR包所在目录替换成自己的jar包所在的目录 这里面的字符建议手打到服务器文件内&#xff0c;复制粘贴的话可能存在特殊符号 ps -ef|grep java SERVICE_PID$(ps aux | grep JAR包名称 | grep -v grep | awk…

influxDB聚合类函数

influxDB聚合类函数 1&#xff09;count()函数 返回一个&#xff08;field&#xff09;字段中的非空值的数量。 SELECT COUNT(<field_key>) FROM <measurement_name> [WHERE <stuff>] [GROUP BY <stuff>] 例子1 计算非空water_level数量SELECT COUN…

Web3 处理智能合约部署到本地区块链,并在本地进行测试

上文 Web3 在Truffle项目中编写出自己的第一个solidity智能合约我们演示了 在Truffle环境下写一个智能合约并编译的功能 编译出的文件夹中的这个JSON就非常重要了 我们就可以通过 它这个ABI链接到需要的智能合约程序上去 但这也仅仅是编译完了 我们的智能合约还没有部署到我们…