嵌入式裸机之LCD学习笔记

前期准备

一 . LCD简介

1.1 什么是LCD?

(1)LCD(Liquid Crystal Display)俗称液晶.
(2)液晶是一种材料,液晶这种材料具有一种特点:可以在电信号的驱动下液晶分子进行旋转,旋转时会影响透光性,因此我们可以在整个液晶面板后面用白光照(称为背光),可以通过不同电信号让液晶分子进行选择性的透光,此时在液晶面板前面看到的就是各种各样不同的颜色,这就是LCD显示。
(3)被动发光和主动发光。有些显示器(譬如LED显示器、CRT显示器)自己本身会发光称为主动发光,有些(LCD)本身不会发光只会透光,需要背光的协助才能看起来是发光的,称为被动发光。
(4)液晶应用领域:电视机、电脑显示屏、手机显示屏、工业显示屏等····

1.2 其他主流显示设备(LED、CRT、等离子、OLED)

(1)CRT:阴极摄像管显示器。
(2)等离子显示:未成为主流
(3)OLED:目前是智能手机行业的一个主流,并且广泛用于折叠屏
(4)LED:主要用在户外大屏幕
(5)LCD:目前是主流显示器

1.3 LCD的显示原理和特点(液晶分子透光+背光)

白光其实是由各种不同颜色的光组成的,所以白光被选择性透光之后可以产生各种不同颜色的光。

1.4 LCD的发展史和种类(TN/STN/TFT)

(1)TN最早。坏处是响应性不够好,有拖尾现象。
(2)STN是TN的升级版。有效解决拖尾现象,显示更清晰。
(3)TFT的最大特点就是超薄。
(4)TFT技术之上发展出来很多更新的技术。
(参考资料:http://display.ofweek.com/2013-12/ART-8321301-8300-28763136.html)

二. LCD的接口技术

2.1 从电平角度来讲本质上都是TTL信号

(1)什么是TTL接口。+5V表示逻辑1,0V表示逻辑0.这种就叫TTL电平,和CMOS电平相对比。
(2)SoC的LCD控制器硬件接口是TTL电平的,LCD这边硬件接口也是TTL电平的。所以他们俩本来是可以直接对接的,手机、平板、开发板都是这样直接对接的(一般用软排线连接)。
(3)TTL电平的缺陷就是不能传递太远,如果LCD屏幕和主板控制器太远(1米甚至更远)就不能直接TTL连接了,要进行转换。转换方式:主机SoC(TTL) ->VGA-> LCD屏幕(TTL)
TTL电平和CMOS电平的区别参考:https://www.21ic.com/jichuzhishi/analog/questions/2013-05-17/181650.html

各种接口(TTL、LVDS、EDP、MIPI、)在传输速率、距离、适配性方面不同(参考资料:http://blog.csdn.net/wocao1226/article/details/23870149

2.1 RGB接口详解(参考数据手册P1207页时序图)

嵌入式裸机之LCD学习笔记_第1张图片
(1)VD[23:0]:24根数据线,用来传输图像信息。可见LCD是并行接口,速率才够快。
(2)HSYNC(水平同步信号)
(3)VSYNC(垂直同步信号):时序信号线,为了让LCD能够正常显示给的控制信号
(4)VCLK(像素时钟):LCD工作时需要主板控制器给LCD模组一个工作时钟信号,就是VCLK。
(5)VDEN(数据有效标志):时序信号,和HSYNC、VSYNC结合使用。
(6)LEND(行结束标志,不是必须的):时序信号,非必须,譬如X210接口就没有。

三. LCD如何显示图像1

3.1 像素(pixel)

(1)像素就是组成图像的最基本元素,或者说显示中可以被控制的最小单位,整个图像就是由很多个像素组成的。
(2)像素可以被单独控制,或控制其亮或不亮(单色屏)、或控制其亮度强弱(譬如亮50%,35%,这样叫灰度屏,以前的黑白电视机)、或控制其显示一定的颜色(这就是我们现在最常用的彩色显示屏)。
总结:像素很重要,整个显示图像是由一个个的像素组成的。我们要在显示器上显示一个图像,就是把这个图像离散化成一个一个的点,然后把各个点的颜色对应在显示器的像素上。

3.2 扫描

(1)扫描是一个动作而不是一个名字,扫描就是依次将颜色数值放入屏幕中所有的像素的这个过程。
(2)扫描这个词是由最早的CRT显示器遗留下来的,到LCD显示器的年代本来已经失去意义了,但是我们还是延续着这么叫。
(3)显示器的扫描显示原理依赖于人眼的视觉暂留。只要显示器扫描频率大于人眼的发现频率,人眼看到的图像就是恒定的。如果扫描频率偏小人眼就会看到闪动。(扫描频率的概念就叫做刷新率)

3.3 驱动器&控制器

(1)LCD驱动器一般和LCD显示面板集成在一起(本来是分开的,做面板的是只做面板的,譬如说三星、LG、台湾的友达、奇美都是做面板的;驱动器也由专门的IC厂商生产;集成厂商买来面板和驱动器后集成在一起做成LCD屏幕),面板只负责里面的液晶分子旋转透光,面板需要一定的模拟电信号来控制液晶分子;LCD驱动器芯片负责给面板提供控制液晶分子的模拟电信号,驱动器的控制信号(数字信号)来自于自己的数字接口,这个接口就是LCD屏幕的外部接口(第二节中讲到的接口)
(2)LCD控制器一般集成在SoC内部,他负责通过数字接口向远端的LCD驱动器提供控制像素显示的数字信号。LCD控制器的关键在于时序,它必须按照一定的时序和LCD驱动器通信;LCD控制器受SoC控制,SoC会从内存中拿像素数据给LCD控制器并最终传给LCD驱动器。

3.4 显示内存(简称:显存)

(1)SoC在内存中挑选一段内存(一般来说是程序员随便挑选的,但是挑选的时候必须符合一定规矩),然后通过配置将LCD控制器和这一段内存(以后称为显存)连接起来构成一个映射关系。一旦这个关系建立之后,LCD控制器就会自动从显存中读取像素数据传输给LCD驱动器。这个显示的过程不需要CPU的参与。
(2)显示体系建立起来后,CPU就不用再管LCD控制器、驱动器、面板这些东西了;以后CPU就只关心显存了,因为我只要把要显示的图像的像素数据丢到显存中,硬件就会自动响应(屏幕上就能自动看到显示的图像了)。

总结: LCD显示是分为2个阶段的:第一个阶段就是建立显示体系的过程,目的就是CPU初始化LCD控制器使其和显存联系起来构成映射;第二个阶段就是映射建立之后,此阶段主要任务是将要显示的图像丢到显存中去。

四. LCD的六个主要时序参数

4.1 LCD显示单位:帧(frame)

(1)显示器上一整个画面的内容成为一个帧(frame),整个显示器工作时是一帧一帧的在显示。
(2)电影实际就是以每秒种24帧的速度在播放图片。
(3)帧内数据:一帧分为多行,一行分为多像素,因此一帧图像其实就是多个像素组成的矩阵。
(4)帧外数据:整个视频由很多个帧构成,最终播放视频时逐个播放各个图像帧即可。

4.2 LCD显示一帧图像的过程

(1)首先把帧分为行,然后再把行分为像素,然后逐个像素去显示。(显示像素:其实就是LCD驱动器按照接收到的LCD控制器给的显示数据,驱动一个像素的液晶分子旋转,让这个像素显示出相应的颜色值的过程)
(2)关键点:LCD控制器和驱动器之间一次只能传一个像素点的显示数据。所以一帧图像在屏幕上其实是串行的依次被显示上去的,不是同一时间显示出来的。

4.3 为了向前兼容出现的六个时序参数

嵌入式裸机之LCD学习笔记_第2张图片

HSPW 水平同步信号脉宽:(horizontal sync pulse width):表示水平同步信号的宽度

HBPD 水平同步信号前肩 :(horizontal back porch):表示从水平同步信号開始到一行的有效数据開始之间的VCLK的个数

HFPD 水平同步信号后肩 :(horizontal front porth):表示一行的有效数据结束到下一个水平同步信号開始之间的VCLK的个数

VSPW 垂直同步信号脉宽 :(vertical sync pulse width):表示垂直同步脉冲的宽度,用行数计算

VBPD 垂直同步信号前肩 :(vertical back porch):表示在一帧图像開始时,垂直同步信号以后的无效的行数

VFPD 垂直同步信号后肩 :(vertical front porch):表示在一帧图像结束后,垂直同步信号曾经的无效的行数

(1)一行的通信过程是这样的:LCD控制器先发送一个HSYNC高电平脉冲(脉冲宽度是HSPW),脉冲告诉驱动器下面的信息是一行信息。然后开始这一行信息,这一行信息包括3部分:HBPD+有效行信息+HFPD。其中前肩和后肩都属于时序信息(和LCD屏幕具体有关),有效行信息就是横向分辨率。所以你可以认为一行总共包含4部分:HSPW+HBPD+有效行信息+HFPD。
(2)一帧图像其实就是一列,一列图像由多个行组成,每行都是上面讲的这个时序。
(3)一帧图像的通信过程是这样的:整个帧图像信号分为4部分:VSPW+VBPD+帧有效信号+VFPD。VSPW是帧同步信号宽度,用来告诉驱动器一帧图像要开始了;VBPD和VFPD分别是垂直同步信号前后肩。
(4)必须说明:这6个参数对于LCD显示器其实本来是没用的,这些信号其实是老式的CRT显示器才需要的,LCD本身不需要,但是出于历史兼容性要求,LCD选择了兼容CRT显示器的这些时序要求,所以理解LCD显示器时序和编程时,用CRT的方式来理解不会错。
(5)要注意,这几个时序参数本身是LCD屏幕本身的参数,与LCD控制器无关。所以同一个主板如果接的屏幕不一样则时序参数设置也会不同。这些参数的来源一般是:第一,厂家会直接给出,一般以实例代码的形式给出;第二,来自于LCD的数据手册。
譬如:第二种LCD 的数据手册
嵌入式裸机之LCD学习笔记_第3张图片

五. LCD显示的主要相关概念

5.1 像素(pixel)

整个图像是由一个个的像素组成的,像素就是一个显示点。

5.2 像素间距(pitch)

(1)pitch是连续2个像素的像素中心的距离。一般的像素是方形的,所以横向pitch和纵向的pitch一样的。但是也有不一样的。
(2)像素间距会影响屏幕的最佳观看距离。像素间距大的适合远距离看,像素间距小的适合近距离看。

5.3 分辨率(resolution)

(1)整个屏幕的横向和纵向的像素个数就叫分辨率,譬如X210开发板用的屏幕是800×480.
(2)屏幕尺寸和分辨率无关的,像开发板的屏幕尺寸是7寸的(纯屏幕对角线尺寸是7英寸)。
(3)屏幕尺寸和分辨率和像素间距三者之间有关联。

5.4 清晰度

(1)清晰度是一个主观概念,是人眼对显示效果的一个主观判断。说白了就是人看起来感觉清晰不清晰。
(2)客观来讲,清晰度由分辨率和像素间距共同决定。一般的,屏幕尺寸固定时分辨率越高越清晰,分辨率越低就越不清晰;分辨率固定下,屏幕尺寸越小越清晰,越大越不清晰。
(3)清晰度还由其他很多因素共同决定。

5.5 像素深度(bits per pixel,简称bpp)

(1)一个像素在计算机中由多少个字节数据来描述。
(2)计算机中用二进制位来表示一个像素的数据,用来表示一个像素的数据位越多,则这个像素的颜色值更加丰富、分的更细,颜色深度就更深。
(3)一般来说像素深度有这么几种:1位、8位、16位、24位、32位。

六. 颜色在计算机中的表示

6.1 颜色的本质

(1)颜色是主观存在,颜色其实是自然光在人的眼睛中和大脑中产生的一种映像。
(2)颜色的本质决定于光的波长。

6.2 自然光的颜色是连续的

光的波长是连续的,导致颜色也是连续的。理论上,只要你的眼睛分辨能力足够好,可以在自然界中发现无数中颜色。

6.3 计算机中的颜色是离散的

(1)计算机中不可能存储无数种颜色,所以必须将颜色有限化,所以就用有限种颜色来代表自然界中的无限种颜色。这个理论非常类似于之前学过的AD转换。
(2)这种离散化表达颜色的缺点是不够真实,漏掉了很多种颜色。因此计算机中所能表达的颜色没有自然界中丰富(计算机屏幕上显示的图像和真实图像有差别)
(3)计算机所能表达的颜色种类个数,这个参数叫:像素深度bpp。

6.4 常见像素深度:1位、8位、16位、24位、32位

1位:用1个二进制位来表示颜色,这种就叫单色显示。示例就是小饭店、理发店门口的LED屏。
8位:用8个二进制位来表示颜色,此时能表示256种颜色。这种叫灰度显示。这时候是黑白的,没有彩色,我们把纯白到纯黑分别对应255到0,中间的数值对应不同的灰。示例就是以前的黑白电视机。
16位:用16个二进制位表示颜色,此时能表示65536种颜色。这时候就可以彩色显示了,一般是RGB565的颜色分布(用5位二进制表示红色、用6位二进制表示绿色、用5位二进制表示蓝色)。这种红绿蓝都有的颜色表示法就是一种模拟自然界中所有颜色的表示方式。但是因为RGB的颜色表达本身二进制位数不够多(导致红绿蓝三种颜色本身分的都不够细致),所以这样显示的彩色失真比较重,人眼能明显看到显示的不真实。
24位:用24个二进制位来表示颜色,此时能表示16777216种颜色。这种表示方式和16位色原理是一样的,只是RGB三种颜色各自的精度都更高了(RGB各8位),叫RGB888。此时颜色比RGB565更加真实细腻,虽然说比自然界无数种颜色还是少了很多,不过由于人眼的不理想性所以人眼几乎不能区分1677万种颜色和无数种颜色的差别了。于是乎就把这种RGB888的表示方法叫做真彩色。(RGB565就是假彩色)
32位:总共用32位二进制来表示颜色,其中24位表示红绿蓝三元色(还是RGB888分布),剩下8位表示透明度。这种显色方式就叫ARGB(A是阿尔法,表示透明度),现在PC机中一般都用ARGB表示颜色。

七. S5PV210的LCD控制器

7.1 FIMD结构框图

嵌入式裸机之LCD学习笔记_第4张图片
(1)210的LCD控制器叫FIMD,FIMD是210内部和图像处理相关的一些部件,在摄像头等和图像处理有关的部分都可以有关联。
(2)FIMD在内部与AHB总线等相连接,在外部提供RGB接口、I80接口、YUV接口与外部相连接,我们实际使用的是RGB接口。

7.2 虚拟屏幕叠加(数据手册P1194)

(1)虚拟屏幕的意思是,我们平时看到的屏幕上显示出来的场景实际是很多个屏幕显示叠加在一起的效果(譬如新闻图像、电视台台标、下方飘动的字幕新闻)
(2)像S5PV210的LCD控制器中有5个虚拟屏幕Window0到Window4,虚拟屏幕不存在于真实而存在于内存中。(之前讲过,LCD显示时实际是显示的是对应的内存中的显存区域的数值)虚拟屏幕其实就是一个内存中的显存区域,有几个显存区域就有几个虚拟屏幕,但是这些虚拟屏幕都被映射到一个真实的显示屏上面,所以将来真实的现实效果实际是这几个虚拟屏幕的显示内容的叠加。(叠加时要注意上面一层会覆盖下面一层,所以要注意谁在前谁在后,设置寄存器时有这个选项)
(3)使用虚拟屏幕而不是整个LCD使用一个显存是有一定好处的:第一,可以保证不污染源图像,方便程序处理;第二,可以减少屏幕刷新,提高显示效率,减少CPU工作量。

7.3 虚拟显示(数据手册P1206)

目标:如何实现在小分辨率的屏幕上(真实)显示大分辨率的图像
存在的问题:细节上,我们需要屏幕上看到不同图像时,需要对显存区域进行刷新。即使我们只需要屏幕显示移动一点点,整个屏幕对应的显存空间也需要整个重新刷新,工作量和完全重新显示一幅图像是一样的。这个显然不好,这样CPU刷新屏幕的工作量太大了,效率很低。
解决方案 :如何能够在显示一个大图片的不同区域时让CPU刷新屏幕工作量减少?有,方法就是虚拟显示。具体做法就是在内存中建立显示缓存的时候实际建立一个很大的区域,然后让LCD去对应其中的一部分区域作为有效的显示区域。将来要显示大图像时,直接将大图像全部一次性加载入显示缓存区,然后通过移动有效显示区域就可以显示大图像的不同区域了。

八. 硬件连接

嵌入式裸机之LCD学习笔记_第5张图片

PWMTOUT0(XpwmTOUT0/GPD0_0 ):控制LCD背光
SYS_OE(SYS_OE/VEN_FIELD/GPF3_5):Output enable(本次不需要进行配置)
LCD_VDEN(XvVDEN/SYSRS/VENHREF/GPF0_2):Data Enable bit11~bit8
TSYM0/TSYP0/TSXP0/TSXM0 :touch相关的
B0~B7 / G0 ~G7/R0 ~ R7(GPF0 ~ GPF3) :LCD_VD[23:0] YCbCr data output ,YCbCr其中Y是指亮度分量,Cb指蓝色色度分量,而Cr指红色色度分量。
LCD_HSYNC(XvHSYNC/SYSCS0/VENHSYNC/GPF0_0):Horizontal Sync. Signal
LCD_VSYNC(XvVSYNC/SYSCS1/VENVSYNC/GPF0_1):Vertical Sync. Signal
LCD_VCLK(XvVCLK/SYSWE/V601CLK/GPF0_3):LCD Video Clock
VSYNC_LDI(VSYNC_LDI/GPF3_4) : LCD i80 VSYNC Interface(本次不需要配置)

九. 主要寄存器简介

VIDCON0~3 :Specifies video control 0 ~ 3 register.
VIDTCON0~3 :Specifies video time control 0 ~3 register
WINCON0~4 :Specifies window control 0 ~ 4 register
SHADOWCON :Specifies window shadow control register
VIDINTCON0 : Specifies video interrupt control register.
TRIGCON : Specifies i80/ RGB trigger control register
DISPLAY_CONTROL :Display output path selection
WINCON0 :Specifies window control 0 register
VIDOSD0A:Specifies video window 0’s position control
VIDW00ADD0B0:Specifies window 0’s buffer start address
VIDW00ADD1B0:Specifies window 0’s buffer end address

编程实践

编程思路:
第一步:根据对应的硬件连接配置对应的GPIO
第二步:设置LCD显示相关的重要寄存器
第三步:编写main函数(搭建大体框架)
第四步:编写各个子函数
第五步:调试代码

一. LCD控制器初始化

参考代码lcd_init函数详解
要想LCD工作,必须给LCD屏幕和显存之间建立一个映射(映射是在CPU初始化LCD控制器来完成的)。本部分就是在完成这个过程(这也是LCD显示的2个阶段的第一阶段,第二阶段中我们只需要给显存中丢入相应的数据,LCD屏幕就会自动显示相应内容)。

#include "main.h"

#define GPF0CON			(*(volatile unsigned long *)0xE0200120)
#define GPF1CON			(*(volatile unsigned long *)0xE0200140)
#define GPF2CON			(*(volatile unsigned long *)0xE0200160)
#define GPF3CON			(*(volatile unsigned long *)0xE0200180)

#define GPD0CON			(*(volatile unsigned long *)0xE02000A0)
#define GPD0DAT			(*(volatile unsigned long *)0xE02000A4)

#define CLK_SRC1		(*(volatile unsigned long *)0xe0100204)
#define CLK_DIV1		(*(volatile unsigned long *)0xe0100304)
#define DISPLAY_CONTROL	(*(volatile unsigned long *)0xe0107008)

#define VIDCON0			(*(volatile unsigned long *)0xF8000000)
#define VIDCON1			(*(volatile unsigned long *)0xF8000004)
#define VIDTCON2		(*(volatile unsigned long *)0xF8000018)
#define WINCON0 		(*(volatile unsigned long *)0xF8000020)
#define WINCON2 		(*(volatile unsigned long *)0xF8000028)
#define SHADOWCON 		(*(volatile unsigned long *)0xF8000034)
#define VIDOSD0A 		(*(volatile unsigned long *)0xF8000040)
#define VIDOSD0B 		(*(volatile unsigned long *)0xF8000044)
#define VIDOSD0C 		(*(volatile unsigned long *)0xF8000048)

#define VIDW00ADD0B0 	(*(volatile unsigned long *)0xF80000A0)
#define VIDW00ADD1B0 	(*(volatile unsigned long *)0xF80000D0)

#define VIDTCON0 		(*(volatile unsigned long *)0xF8000010)
#define VIDTCON1 		(*(volatile unsigned long *)0xF8000014)

#define HSPW 			(40)				// 1~40 DCLK
#define HBPD			(10 - 1)			// 46
#define HFPD 			(240 - 1)			// 16 210 354
#define VSPW			(20)				// 1~20 DCLK
#define VBPD 			(10 - 1)			// 23
#define VFPD 			(30 - 1)			// 7 22 147

// FB地址
#define FB_ADDR			(0x23000000)
#define ROW				(600)
#define COL				(1024)
#define HOZVAL			(COL-1)
#define LINEVAL			(ROW-1)

#define XSIZE			COL
#define YSIZE			ROW

u32 *pfb = (u32 *)FB_ADDR;

// 常用颜色定义
#define BLUE	0x0000FF
#define RED		0xFF0000
#define GREEN	0x00FF00
#define WHITE	0xFFFFFF
#define BLACK   0X000000

void lcd_init(void)
{

/* 根据硬件连接并对应数据手册配置如下
LCD_HSYNC(GPF0_0):0010 = LCD_HSYNC
LCD_VSYNC(GPF0_1):0010 = LCD_VSYNC
LCD_VDEN(GPF0_2) :0010 = LCD_VDEN
LCD_VCLK(GPF0_3) :0010 = LCD_VCLK
B0~B7 / G0 ~G7/R0 ~ R7(GPF0_4~7, GPF1_0~7,GPF2_0~7,GPF3_0~3) :0010 = LCD_VD[n]
*/
	GPF0CON = 0x22222222;
	GPF1CON = 0x22222222;
	GPF2CON = 0x22222222;
	GPF3CON = 0x22222222;

	// 打开背光	GPD0_0(PWMTOUT0)
	
	GPD0CON &= ~(0xf<<0);
	GPD0CON |= (1<<0);			// output mode
	GPD0DAT &= ~(1<<0);			// output 0 to enable backlight

	// 10: RGB=FIMD I80=FIMD ITU=FIMD
	DISPLAY_CONTROL = 2<<0;

	// bit[26~28]:使用RGB接口
	// bit[18]:RGB 并行
	// bit[2]:选择时钟源为HCLK_DSYS=166MHz
	VIDCON0 &= ~( (3<<26)|(1<<18)|(1<<2) );

	// bit[1]:使能lcd控制器
	// bit[0]:当前帧结束后使能lcd控制器
	VIDCON0 |= ( (1<<0)|(1<<1) );

	// bit[6]:选择需要分频
	// bit[6~13]:分频系数为5,即VCLK = 166M/(4+1) = 33M
	VIDCON0 |= 4<<6 | 1<<4;
	
	// H43-HSD043I9W1.pdf(p13) 时序图:VSYNC和HSYNC都是低脉冲
	// s5pv210芯片手册(p1207) 时序图:VSYNC和HSYNC都是高脉冲有效,所以需要反转
	VIDCON1 |= 1<<5 | 1<<6;

	// 设置时序,将特定的设置数据放置至对应的位
	VIDTCON0 = VBPD<<16 | VFPD<<8 | VSPW<<0;
	VIDTCON1 = HBPD<<16 | HFPD<<8 | HSPW<<0;
	// 设置长宽(物理屏幕)
	VIDTCON2 = (LINEVAL << 11) | (HOZVAL << 0);

	// 设置window0
	// bit[0]:使能
	// bit[2~5]:24bpp(RGB888)
	WINCON0 |= 1<<0;
	WINCON0 &= ~(0xf << 2);
	WINCON0 |= (0xB<<2) | (1<<15);

#define LeftTopX     0
#define LeftTopY     0
#define RightBotX   799
#define RightBotY   479

	// 设置window0的上下左右
	// 设置的是显存空间的大小
	VIDOSD0A = (LeftTopX<<11) | (LeftTopY << 0);  //配置左上角的坐标(0,0)
	VIDOSD0B = (RightBotX<<11) | (RightBotY << 0);//配置屏幕右下角的坐标(799,179)
	VIDOSD0C = (LINEVAL + 1) * (HOZVAL + 1);

	// 设置fb的地址,Frame Buffer Address 0 Register,给Window 0 设置一块内存作为显存使用
	VIDW00ADD0B0 = FB_ADDR;  
	VIDW00ADD1B0 = (((HOZVAL + 1)*4 + 0) * (LINEVAL + 1)) & (0xffffff);
	 //*4 的原因是RGB88 每个颜色占用一个字节,但为了字节对齐我们一般设为4

	// 使能channel 0传输数据
	SHADOWCON = 0x1;
}

二. 显示像素&刷背景

画一个像素函数

static inline void lcd_draw_pixel(u32 x, u32 y, u32 color)
{
	*(pfb + COL * y + x) = color;
}

刷背景函数

static void lcd_draw_background(u32 color)
{
	u32 i, j;
	
	for (j=0; j<ROW; j++)
	{
		for (i=0; i<COL; i++)
		{
			lcd_draw_pixel(i, j, color);
		}
	}
}

三.横线竖线斜线&画圆

画线函数

// glib库中的画线函数,可以画斜线,线两端分别是(x1, y1)和(x2, y2)
void glib_line(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2, unsigned int color)
{
	int dx,dy,e;
	dx=x2-x1; 
	dy=y2-y1;
    
	if(dx>=0)
	{
		if(dy >= 0) // dy>=0
		{
			if(dx>=dy) // 1/8 octant
			{
				e=dy-dx/2;  
				while(x1<=x2)
				{
					lcd_draw_pixel(x1,y1,color);
					if(e>0){y1+=1;e-=dx;}	
					x1+=1;
					e+=dy;
				}
			}
			else		// 2/8 octant
			{
				e=dx-dy/2;
				while(y1<=y2)
				{
					lcd_draw_pixel(x1,y1,color);
					if(e>0){x1+=1;e-=dy;}	
					y1+=1;
					e+=dx;
				}
			}
		}
		else		   // dy<0
		{
			dy=-dy;   // dy=abs(dy)

			if(dx>=dy) // 8/8 octant
			{
				e=dy-dx/2;
				while(x1<=x2)
				{
					lcd_draw_pixel(x1,y1,color);
					if(e>0){y1-=1;e-=dx;}	
					x1+=1;
					e+=dy;
				}
			}
			else	 // 7/8 octant
			{
				e=dx-dy/2;
				while(y1>=y2)
				{
					lcd_draw_pixel(x1,y1,color);
					if(e>0){x1+=1;e-=dy;}	
					y1-=1;
					e+=dx;
				}
			}
		}	
	}
	else //dx<0
	{
		dx=-dx;		//dx=abs(dx)
		if(dy >= 0) // dy>=0
		{
			if(dx>=dy) // 4/8 octant
			{
				e=dy-dx/2;
				while(x1>=x2)
				{
					lcd_draw_pixel(x1,y1,color);
					if(e>0){y1+=1;e-=dx;}	
					x1-=1;
					e+=dy;
				}
			}
			else		// 3/8 octant
			{
				e=dx-dy/2;
				while(y1<=y2)
				{
					lcd_draw_pixel(x1,y1,color);
					if(e>0){x1-=1;e-=dy;}	
					y1+=1;
					e+=dx;
				}
			}
		}
		else		   // dy<0
		{
			dy=-dy;   // dy=abs(dy)

			if(dx>=dy) // 5/8 octant
			{
				e=dy-dx/2;
				while(x1>=x2)
				{
					lcd_draw_pixel(x1,y1,color);
					if(e>0){y1-=1;e-=dx;}	
					x1-=1;
					e+=dy;
				}
			}
			else		// 6/8 octant
			{
				e=dx-dy/2;
				while(y1>=y2)
				{
					lcd_draw_pixel(x1,y1,color);
					if(e>0){x1-=1;e-=dy;}	
					y1-=1;
					e+=dx;
				}
			}
		}	
	}
}

画圆函数

//画圆函数,圆心坐标是(centerX, centerY),半径是radius,圆的颜色是color
void draw_circular(unsigned int centerX, unsigned int centerY, unsigned int radius, unsigned int color)
{
	int x,y ;
	int tempX,tempY;;
    int SquareOfR = radius*radius;

	for(y=0; y<YSIZE; y++)
	{
		for(x=0; x<XSIZE; x++)
		{
			if(y<=centerY && x<=centerX)
			{
				tempY=centerY-y;
				tempX=centerX-x;                        
			}
			else if(y<=centerY&& x>=centerX)
			{
				tempY=centerY-y;
				tempX=x-centerX;                        
			}
			else if(y>=centerY&& x<=centerX)
			{
				tempY=y-centerY;
				tempX=centerX-x;                        
			}
			else
			{
				tempY = y-centerY;
				tempX = x-centerX;
			}
			if ((tempY*tempY+tempX*tempX)<=SquareOfR)
				lcd_draw_pixel(x, y, color);
		}
	}
}

四. 写英文字符串

写英文字符和字符串函数

// 写字
// 写字的左上角坐标(x, y),字的颜色是color,字的字模信息存储在data中
static void show_8_16(unsigned int x, unsigned int y, unsigned int color, unsigned char *data)  
{  
// count记录当前正在绘制的像素的次序
    int i, j, count = 0;  
	  
    for (j=y; j<(y+16); j++)  
    {  
        for (i=x; i<(x+8); i++)  
        {  
            if (i<XSIZE && j<YSIZE)  
            {  
			// 在坐标(i, j)这个像素处判断是0还是1,如果是1写color;如果是0直接跳过
            	if (data[count/8] & (1<<(count%8)))   
					lcd_draw_pixel(i, j, color);
            }  
            count++;  
        }  
    }  
} 

// 写字符串
// 字符串起始坐标左上角为(x, y),字符串文字颜色是color,字符串内容为str
void draw_ascii_ok(unsigned int x, unsigned int y, unsigned int color, unsigned char *str)
{
	int i;  
	unsigned char *ch;
    for (i=0; str[i]!='\0'; i++)  
    {  
		ch = (unsigned char *)ascii_8_16[(unsigned char)str[i]-0x20];
        show_8_16(x, y, color, ch); 
		
        x += 8;
		if (x >= XSIZE)
		{
			x -= XSIZE;			// 回车
			y += 16;			// 换行
		}
    }  
}

五. 画图

首先需要将图片使用image2lcd 软件转换成数组,然后通过函数调用实现图片的显示,需要注意的是转化时输出的data数据需要大致等于10246004,才能确保全屏显示,否则就有可能出现黑边。

// 画1024×600的图,图像数据存储在pData所指向的数组中
void lcd_draw_picture(const unsigned char *pData)
{
	u32 x, y, color, p = 0;
	
	for (y=0; y<600; y++)
	{
		for (x=0; x<1024; x++)
		{
			// 在这里将坐标点(x, y)的那个像素填充上相应的颜色值即可
			color = ((pData[p+2] << 0) | (pData[p+1] << 8) | (pData[p+0] << 16));
			lcd_draw_pixel(x, y, color);
			p += 3;
		}
	}
}

  • C语言代码详细解析

注: 以上内容来自朱老师物联网大讲堂裸机课件

你可能感兴趣的:(嵌入式裸机笔记)