Linux教程

嵌入式Linux开发9——RGBLCD显示

本文主要是介绍嵌入式Linux开发9——RGBLCD显示,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

背景知识

1.分辨率

  提起 LCD 显示器,我们都会听到 720P、 1080P、 2K 或 4K 这样的字眼,这个就是 LCD 显示器分辨率。 LCD 显示器都是由一个一个的像素点组成,像素点就类似一个灯(在 OLED 显示器中,像素点就是一个小灯),这个小灯是 RGB 灯,也就是由 R(红色)、 G(绿色)和 B(蓝色)这三种颜色组成的,而 RGB 就是光的三原色。 1080P 的意思就是一个 LCD 屏幕上的像素数量是1920*1080 个,也就是这个屏幕一列 1080 个像素点,一共 1920 列,如下图所示:
在这里插入图片描述

  在图中显示的就是 1080P 显示器的像素示意图, X 轴就是 LCD 显示器的横轴, Y 轴就是显示器的竖轴。图中的小方块就是像素点,一共有 19201080=2073600 个像素点。左上角的 A 点是第一个像素点,右下角的 C 点就是最后一个像素点。 2K 就是 25601440 个像素点, 4K 是3840*2160 个像素点。很明显,在 LCD 尺寸不变的情况下,分辨率也高越清晰。同样的,分辨率不变的情况下, LCD 尺寸越小越清晰。比如我们常用的 24 寸显示器基本都是 1080P 的,而我们现在使用的 5 寸的手机基本也是 1080P 的,但是手机显示细腻程度就要比 24 寸的显示器要好很多!
  由此可见, LCD 显示器的分辨率是一个很重要的参数,但是并不是分辨率越高的 LCD 就越好。衡量一款 LCD 的好坏,分辨率只是其中的一个参数,还有色彩还原程度、色彩偏离、亮度、可视角度、屏幕刷新率等其他参数。

2.像素格式

  上面讲了,一个像素点就相当于一个 RGB 小灯,通过控制 R、 G、 B 这三种颜色的亮度就可以显示出各种各样的色彩。那该如何控制 R、 G、 B 这三种颜色的显示亮度呢?一般一个 R、G、 B 这三部分分别使用 8bit 的数据,那么一个像素点就是 8bit*3=24bit,也就是说一个像素点3 个字节,这种像素格式称为 RGB888。如果在加入 8bit 的 Alpha(透明)通道的话一个像素点就是 32bit,也就是 4 个字节,这种像素格式称为 ARGB8888。如果学习过 STM32 的话应该还听过 RGB565 这种像素格式,在本章实验中我们使用 ARGB8888 这种像素格式,一个像素占用 4个字节的内存,这四个字节每个位的分配如图:
在这里插入图片描述
  在图中,一个像素点是 4 个字节,其中 bit31~bit24 是 Alpha 通道, bit23~bit16 是RED 通道, bit15~bit14 是 GREEN 通道, bit7~bit0 是 BLUE 通道。所以红色对应的值就是0X00FF0000,蓝色对应的值就是 0X0000FF00,绿色对应的值为 0X000000FF。通过调节 R、G、B的比例可以产生其它的颜色,比如0X00FFFF00就是黄色, 0X00000000就是黑色, 0X00FFFFFF就是白色。

3.LCD时间参数

  如果将 LCD 显示一帧图像的过程想象成绘画,那么在显示的过程中就是用一根“笔”在不同的像素点画上不同的颜色。这根笔按照从左至右、从上到下的顺序扫描每个像素点,并且在像素画上对应的颜色,当画到最后一个像素点的时候一幅图像就绘制好了。假如一个 LCD 的分辨率为 1024*600,那么其扫描如图所示:
在这里插入图片描述
  HSYNC 是水平同步信号,也叫做行同步信号,当产生此信号的话就表示开始显示新的一行了,所以此信号都是在图的最左边。VSYNC 信号是垂直同步信号,也叫做帧同步信号,当产生此信号的话就表示开始显示新的一帧图像了,此信号在图的左上角。
  在上图中有一圈黑边,真正有效的显示区域是中间白色部分。为什么会存在黑边呢?因为RGB LCD屏幕内部是有一个 IC 的,发送一行或者一帧数据给 IC, IC 是需要反应时间的。通过这段反应时间可以让 IC 识别到一行数据扫描完了,要换行了,或者一帧图像扫描完了,要开始下一帧图像显示了。因此,在 LCD 屏幕中继续存在 HBP、 HFP、 VPB 和 VFP 这四个参数的主要目的是为了锁定有效的像素数据。

4.显存

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

5.配置步骤

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 函数,比如画点、画线、画圆函数,字符串显示函数等。

程序编写

1.cd.c

  lcd.c里面总共有10个函数:lcd_init调用LCD 的 IO 初始化函数、时钟初始化函数、复位函数等,而后初始化相关寄存器,使能eLCDIF;lcdgpio_init主要是LCD的IO初始化;lcdclk_init初始化LCD的时钟;lcd_reset以及lcd_noreset分别是LCD复位函数以及LCD停止复位函数;lcd_enable用来使能eLCDIF;lcd_drawpoint和lcd_readpoint是画点读点函数;lcd_clear用于清屏,最后一个是lcd_fill,给定矩阵的起始和终止坐标以及颜色,可以给一个矩形区域填色。

/* 液晶屏参数结构体 */
struct tftlcd_typedef tftlcd_dev;

/*
 * @description	: 始化LCD
 * @param 		: 无
 * @return 		: 无
 */
void lcd_init(void)
{
	lcdgpio_init();			/* 初始化IO 			*/
	lcdclk_init(32, 3, 5);	/* 初始化LCD时钟 		*/
	
	lcd_reset();			/* 复位LCD  			*/
	delayms(10);			/* 延时10ms 			*/
	lcd_noreset();			/* 结束复位 			*/

	/* TFTLCD参数结构体初始化 */
	tftlcd_dev.height = 600;	
	tftlcd_dev.width = 1024;
	tftlcd_dev.pixsize = 4;				/* ARGB8888模式,每个像素4字节 */
	tftlcd_dev.vspw = 3;
	tftlcd_dev.vbpd = 20;
	tftlcd_dev.vfpd = 12;
	tftlcd_dev.hspw = 20;
	tftlcd_dev.hbpd = 140;
	tftlcd_dev.hfpd = 160;
	tftlcd_dev.framebuffer = LCD_FRAMEBUF_ADDR;	
	tftlcd_dev.backcolor = LCD_WHITE;	/* 背景色为白色 */
	tftlcd_dev.forecolor = LCD_BLACK;	/* 前景色为黑色 */
     
	/* 初始化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;	//先清零
	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;


	lcd_enable();			/* 使能LCD 	*/
	delayms(10);
	lcd_clear(LCD_WHITE);	/* 清屏 		*/
	
}

/*
 * IO引脚: 	LCD_DATA00 -> LCD_B0
 *			LCD_DATA01 -> LCD_B1
 *			LCD_DATA02 -> LCD_B2
 *			LCD_DATA03 -> LCD_B3
 *			LCD_DATA04 -> LCD_B4
 *			LCD_DATA05 -> LCD_B5
 *			LCD_DATA06 -> LCD_B6
 *			LCD_DATA07 -> LCD_B7
 *
 *			LCD_DATA08 -> LCD_G0
 *			LCD_DATA09 -> LCD_G1
 *			LCD_DATA010 -> LCD_G2
 *			LCD_DATA011 -> LCD_G3
 *			LCD_DATA012 -> LCD_G4
 *			LCD_DATA012 -> LCD_G4
 *			LCD_DATA013 -> LCD_G5
 *			LCD_DATA014 -> LCD_G6
 *			LCD_DATA015 -> LCD_G7
 *
 *			LCD_DATA016 -> LCD_R0
 *			LCD_DATA017 -> LCD_R1
 *			LCD_DATA018 -> LCD_R2 
 *			LCD_DATA019 -> LCD_R3
 *			LCD_DATA020 -> LCD_R4
 *			LCD_DATA021 -> LCD_R5
 *			LCD_DATA022 -> LCD_R6
 *			LCD_DATA023 -> LCD_R7
 *
 *			LCD_CLK -> LCD_CLK
 *			LCD_VSYNC -> LCD_VSYNC
 *			LCD_HSYNC -> LCD_HSYNC
 *			LCD_DE -> LCD_DE
 *			LCD_BL -> GPIO1_IO08 
 */
 
/*
 * @description	: LCD GPIO初始化
 * @param 		: 无
 * @return 		: 无
 */
void lcdgpio_init(void)
{
	gpio_pin_config_t gpio_config;
	

	/* 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引脚      */

	/* 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引脚 		*/

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

/*
 * @description		: LCD时钟初始化, LCD时钟计算公式如下:
 *                	  LCD CLK = 24 * loopDiv / prediv / div
 * @param -	loopDiv	: loopDivider值
 * @param -	loopDiv : 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时钟 */
}

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

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

/*
 * @description	: 使能ELCDIF接口
 * @param 		: 无
 * @return 		: 无
 */
void lcd_enable(void)
{
	LCDIF->CTRL |= 1<<0; /* 使能ELCDIF */
}

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

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

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


2.lcdapi.c

  这里面都是一些LCD的API函数,比如画线、画矩形、画圆、显示数字、显示字符和字符串等函数。这些函数都是从 STM32 例程里面移植过来的。

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

3.main.c

  笔者的LCD分辨率为800*480,实现在LCD屏上显示字符串“I am jiajia2020 and I love CSDN”,并在下方矩形区域按1s的周期更换背景颜色。

#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;	  
	lcd_show_string(10,10,400,32,16,(char*)"I am jiajia2020 and I love CSDN !");  /* 显示字符串 */
	lcd_fill(10, 52, 1014, 290, backcolor[index]);

	while(1)				
	{	
		index++;
		if(index == 10)
			index = 0;
		lcd_fill(0, 50, 800, 480, backcolor[index]);
		state = !state;
		led_switch(LED0,state);
		delayms(1000);	/* 延时一秒	*/
	}
	return 0;
}

编译下载验证

  可以看到最终按照主函数的内容正常执行:
在这里插入图片描述

这篇关于嵌入式Linux开发9——RGBLCD显示的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!