裸机:LCD

什么是LCD?

LCD,全称为Liquid Crystal Display,即液晶显示屏,是一种利用液晶物质的光学特性,通过控制电场来改变光的透过性,从而实现图像显示的技术。以下是关于LCD的详细解释:

一、LCD的基本概念

  • 定义:LCD是一种平面超薄的显示设备,由一定数量的彩色或黑白像素组成,放置于光源或者反射面前方。
  • 核心元件:液晶分子是LCD的核心元件,其排列状态在电场作用下会发生变化,从而影响光线的透过性,实现图像的显示。

二、LCD的工作原理

  • 基本原理:LCD利用液晶分子的电光效应,即在电场作用下,液晶分子的排列会产生变化,从而影响到它的光学性质。通过控制电场,可以改变液晶分子的排列状态,进而控制光线的透过性,形成图像。
  • 具体过程:LCD由两片极化材料(通常是玻璃基板)组成,中间夹着一层液晶材料。当电流通过液晶时,会使液晶分子重新排列,从而改变光线的透过性。配合背光源和滤光片等组件,可以实现图像的显示。

三、LCD的特点

1.功耗低:由于LCD采用液晶分子作为显示元件,其功耗远低于传统的CRT显示器。
2.体积小、重量轻:LCD具有平面超薄的特点,可以大大节省空间,便于携带和安装。
3.辐射低:LCD在工作时不会产生电磁辐射,对人体健康无害。
4.色彩丰富、显示效果好:随着技术的进步,LCD的色彩表现和显示效果已经达到了非常高的水平,可以满足各种应用场合的需求。

四、LCD的应用领域

LCD广泛应用于各类电子设备中,如电视、电脑显示器、智能手机、平板电脑、车载显示屏等。随着科技的不断进步和人们对显示效果要求的提高,LCD技术也在不断发展和完善,为人们的生活和工作带来了更多的便利和享受。

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

1. LED显示屏

定义与原理:
LED显示屏是一种通过控制半导体发光二极管的显示方式,由几万到几十万个半导体发光二极管像素点均匀排列组成。它利用半导体P-N结电致发光原理产生红、绿、蓝颜色,进而组合成各种色彩和图像。
特点:

  • 亮度高:LED显示屏的亮度远高于传统显示屏,适合在户外或光线较强的环境中使用。
  • 功耗低:LED发光二极管的能效较高,因此LED显示屏在同等亮度下比传统显示屏更节能。
  • 寿命长:LED发光二极管的寿命长达数万小时,使得LED显示屏具有较长的使用寿命。
  • 色彩丰富:通过红、绿、蓝三基色的组合,LED显示屏可以显示丰富的色彩和图像。
    应用:
    LED显示屏广泛应用于车站、码头、机场、商场、医院等公共场所,以及户外广告、舞台演出等领域。

2. CRT显示屏(阴极射线管)

定义与原理:
CRT显示屏是一种使用阴极射线管(Cathode Ray Tube)的显示技术。它通过一个电子枪发射电子束,电子束在偏转装置的作用下扫描荧光屏,从而激发荧光粉发光形成图像。
特点:

  • 色彩丰富:CRT显示屏能够显示丰富的色彩和细腻的图像。
  • 响应速度快:由于电子束直接激发荧光粉发光,CRT显示屏的响应速度非常快。
    缺点:
  • 体积大、重量重:CRT显示屏需要较大的空间来容纳电子枪和偏转装置等部件。
  • 能耗高:CRT显示屏在工作时会产生较大的热量和能耗。
    现状:
    随着液晶显示技术的快速发展,CRT显示屏已经逐渐被淘汰,目前市场上已经很少见到新的CRT显示屏产品。

3. 等离子显示屏(PDP)

定义与原理:
等离子显示屏是一种利用气体放电原理的显示技术。它在两张薄玻璃板之间充填混合气体,通过电流激发气体放电产生紫外光,紫外光再激发荧光粉发光形成图像。
特点:

  • 超薄:等离子显示屏的厚度非常薄,适合作为壁挂电视使用。
  • 色彩鲜艳:等离子显示屏能够显示鲜艳、明亮的图像。
  • 大屏幕:等离子显示屏容易实现大屏幕显示,适合家庭影院等场合使用。
    缺点:
  • 烧屏现象:长时间显示固定图像可能会导致屏幕局部亮度降低或色彩失真。
  • 功耗较高:等离子显示屏在工作时会产生一定的功耗。
    现状:
    等离子显示屏在市场上的份额已经逐渐被液晶显示屏和OLED显示屏所取代,但仍有部分高端用户对其情有独钟。

4. OLED显示屏

定义与原理:
OLED显示屏是一种有机发光二极管(Organic Light-Emitting Diode)显示技术。它通过在有机材料薄膜上施加电压使材料发光来显示图像。
特点:

  • 自发光:OLED显示屏的每个像素都能独立发光,无需背光板。
  • 色彩鲜艳:OLED显示屏能够显示非常鲜艳、丰富的色彩。
  • 视角广:OLED显示屏的视角范围非常广,几乎可以从任何角度观看而不失真。
  • 超薄:OLED显示屏的厚度非常薄,适合制作超薄、轻便的显示设备。
    缺点:
  • 价格较高:目前OLED显示屏的制造成本较高,导致产品价格也相对较高。
  • 寿命问题:OLED显示屏的蓝色发光材料寿命相对较短,可能影响整体寿命。
    应用:
    OLED显示屏已经广泛应用于智能手机、平板电脑、电视等高端显示设备中,并逐渐向更多领域拓展。

接口技术介绍

  • 定义:RGB接口是一种并行接口,通过红(R)、绿(G)、蓝(B)三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色。
  • 特点:接口一般为3.3V电平,需要同步信号,需时刻刷新图像数据。接口线多,数据传输速率不高,传输距离较短,且抗电磁干扰(EMI)能力也比较差。适用于较小尺寸或较低分辨率的显示器。
    2.LVDS接口
  • 定义:Low Voltage Differential Signaling(低压差分信号)接口,是一种低压差分信号技术接口,利用非常低的电压摆幅(约350mV)在两条PCB走线或一对平衡电缆上通过差分进行数据传输。
  • 特点:高速率(一般655Mbps),低电压、低功耗、低EMI(摆幅350mv),抗干扰能力强。在液晶显示器中广泛应用,特别是在高分辨率和大尺寸屏幕中。
    3.MIPI接口
  • 定义:Mobile Industry Processor Interface(移动行业处理器接口),是一种内部嵌入式图像传输接口,广泛应用于移动应用、平板电脑或手机等设备。
  • 特点:高速(1Gbps/Lane,4Gbps吞吐量),低功耗(200mV差分摆幅,200mv共模电压),噪声抑制能力强,更少的pin,PCB layout更方便。支持高分辨率和高性能显示。
    4.HDMI接口
  • 定义:High-Definition Multimedia Interface(高清晰度多媒体接口),是一种数字接口,能够同时传输视频和音频信号。
  • 特点:支持高分辨率视频和多声道音频传输,具有更小的连接器,适用于薄型设备。广泛应用于高清电视、家庭影院和计算机显示器等设备。
    5.DisplayPort接口
  • 定义:一种数字显示接口,旨在提供高性能的视频和音频传输。
  • 特点:具有较高的带宽,可以支持多显示器配置和高分辨率。支持高分辨率、高刷新率和多显示器连接。常见于高性能计算机显示器和专业图形工作站。
    6.USB-C接口
  • 定义:一种多功能接口,支持数据传输、充电和视频输出。
  • 特点:具有小型连接器,适用于薄型设备。支持高速数据传输、充电和视频输出,并且可以通过适配器连接到不同类型的显示器。
    7.MCU接口(I80/I8080接口)
  • 定义:MCU接口其本质是由英特尔(Intel)提出的8080总线标准,是一种并行接口,广泛应用于单片机领域,因此也被称为MCU接口或DBI(Data Bus Interface)数据总线接口。
  • 特点:控制简单方便,无需时钟和同步信号。但缺点是要耗费GRAM,所以难以做大尺寸屏。
    8.SPI接口
  • 定义:Serial Peripheral Interface(串行外设接口),是一种串行的通信接口,用于在主从设备之间进行通信。
  • 特点:接口简单,利于硬件设计与实现,相对抗干扰能力强,传输稳定。但传输速度慢,一般应用于小尺寸、低分辨率的显示器。

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

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

在这里插入图片描述
在这里插入图片描述

LCD如何显示图像

像素(pixel)

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

扫描

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

驱动器&控制器

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

显示内存(简称:显存)

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

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

LCD的六个主要时序参数

LCD显示单位:帧(frame)

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

LCD显示一帧图像的过程

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

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

HSPW 水平同步信号脉宽
HBPD 水平同步信号前肩
HFPD 水平同步信号后肩
VSPW 垂直同步信号脉宽
VBPD 垂直同步信号前肩
VFPD 垂直同步信号后肩
(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的数据手册。

第一种方式,查看九鼎的210裸机教程(x210v3裸机开发教程\src\template-framebuffer-font\source\hardware\s5pv210-fb.c的第774行)
.h_fp = 210, // 160-210-354
.h_bp = 38, // 46
.h_sw = 10, // 1-40
.v_fp = 22, // 7-22-147
.v_fpe = 1,
.v_bp = 18, // 23
.v_bpe = 1,
.v_sw = 7, // 1-20
第二种方式,查看LCD数据手册(X210光盘资料\A盘\DataSheet\AT070TN92.pdf)

补充:
1、注意这些数字的单位。H开头的三个单位都是DCLK(像素时钟),V开头的三个单位是TH。这样设置的好处是我们改变了像素时钟的设置时,不用改变这里的时序参数。
2、这些时序参数如果没设置好会影响什么?屏幕会跑偏。

LCD显示的主要相关概念

像素(pixel)

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

像素间距(pitch)

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

分辨率(resolution)

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

清晰度

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

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

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

颜色在计算机中的表示

颜色的本质

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

自然光的颜色是连续的

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

计算机中的颜色是离散的

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

常见像素深度: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表示颜色。
补充:颜色的组成,三元色(三基色)是RGB,也就是说所有的颜色都可以由红绿蓝三种颜色组成。

S5PV210的LCD控制器

FIMD结构框图

(1)210的LCD控制器叫FIMD,FIMD是210内部和图像处理相关的一些部件,在摄像头等和图像处理有关的部分都可以有关联。
(2)FIMD在内部与AHB总线等相连接,在外部提供RGB接口、I80接口、YUV接口与外部相连接,我们实际使用的是RGB接口。

在这里插入图片描述

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

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

在这里插入图片描述

虚拟显示(数据手册P1206)

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

在这里插入图片描述

主要寄存器简介

在这里插入图片描述

代码

初始化

#include "main.h"
#include "ascii.h"
#include "800480.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 REC		0x0



// 初始化LCD
void lcd_init(void)
{
	// 配置引脚用于LCD功能
	GPF0CON = 0x22222222;
	GPF1CON = 0x22222222;
	GPF2CON = 0x22222222;
	GPF3CON = 0x22222222;

	// 打开背光GPD00(PWMTOUTOUTO)
	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]:使能]cd控制器
	//bit[0]:当前帧结束后使能1cd控制器
	VIDCON0 |= ( (1<<0)|(1<<1) );

	//bit[6]:选择需要分频
	// bit[6~13]:分频系数为5,即VCLK=166M/(4+1)=33M
	VIDCON0 |= 4<<6 | 1<<4;


	//H43-HSD04319W1.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);

	//设置windows0
	// 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   (1024-1)
#define RightBotY   (600-1)

	//设置windows0的上下左右 
	//显存大小 可以比物理屏幕大
	VIDOSD0A = (LeftTopX<<11) | (LeftTopY << 0);
	VIDOSD0B = (RightBotX<<11) | (RightBotY << 0);
	VIDOSD0C = (LINEVAL + 1) * (HOZVAL + 1);


	//设置fb的地址
	VIDW00ADD0B0 = FB_ADDR;
	VIDW00ADD1B0 = (((HOZVAL + 1)*4 + 0) * (LINEVAL + 1)) & (0xffffff);

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


// 画一个像素
static inline void lcd_draw_pixel(u32 x, u32 y, u32 color)
{
	*(pfb + COL * y + x) = color;
}

// 这个屏幕背景填充为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);
		}
	}
}

static void delay(void)
{
	volatile u32 i, j;
	for (i=0; i<4000; i++)
		for (j=0; j<1000; j++);
}

//绘制竖线,起始坐标为(x,y1)到(x,y2),颜色是color
void lcd_draw_vline(u32 x, u32 y1, u32 y2, u32 color)
{
	u32 y;
	
	for (y = y1; y<y2; y++)
	{
		lcd_draw_pixel(x, y, color);
	}
}

// 绘制横线,起始坐标为(x1,y)到(x2,y),颜色是color
void lcd_draw_hline(u32 x1, u32 x2, u32 y, u32 color)
{
	u32 x;
	
	for (x = x1; x<x2; x++)
	{
		lcd_draw_pixel(x, y, 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<XSIZE; y++)
	{
		for(x=0; x<YSIZE; 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中
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,
            	if (data[count/8] & (1<<(count%8))) //如果是1写color;如果是0直接跳过
					lcd_draw_pixel(i, j, color);
            }  
            count++;  
        }  
    }  
} 

//写字符串
//字符串起始坐标左上角为(x,y),字符串文字颜色是color,字符串内容为st
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;			// 换行
		}
    }  
}

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



void lcd_test(void)
{
	//第一步:建立一个显存和屏幕显示之间的映射
	lcd_init();
	uart_init();
	
	//第二步:向显存中填充合适的图像数据
	while (1)
	{
			
		putc('s');
		lcd_draw_background(WHITE);	
		draw_ascii_ok(0, 0, RED, "ABCDabcd1234!@#$%");
		delay();
	

		putc('p');
		lcd_draw_picture(gImage_800480);
		delay();

		putc('r');
		lcd_draw_background(RED);
		delay();
	
		
		putc('g');
		lcd_draw_background(GREEN);
		delay();
	
		putc('b');
		lcd_draw_background(BLUE);
		delay();
		
		putc('1');
		lcd_draw_background(WHITE);			
		glib_line(0, 479, 799, 0, RED);
		draw_circular(399, 239, 50, GREEN);
		delay();


		putc('2');
		lcd_draw_background(WHITE);
		lcd_draw_hline(350, 450, 240, RED);
		lcd_draw_vline(400, 190, 290, GREEN);
	}

}








学习记录,侵权联系删除。
来源:朱老师物联网大课堂

  • 20
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
你想要学习在IMX6ULL平台上裸机(即不依赖操作系统)编程控制LCD吗?这是一个不错的学习项目!在开始之前,你需要了解一些基本的知识,包括LCD的工作原理、接口协议以及IMX6ULL平台的GPIO控制等。以下是一些步骤供你参考: 1. 了解LCD的基本知识:学习LCD的工作原理、显示原理、分辨率、颜色深度等。 2. 确定LCD的接口类型:IMX6ULL平台可能支持不同的接口类型,如RGB、LVDS、MIPI等,你需要确定你使用的LCD的接口类型,并准备相应的连接线。 3. 配置IMX6ULL的GPIO:通过配置IMX6ULL的GPIO引脚,将其连接到LCD的控制和数据线上。你可以查阅IMX6ULL的技术文档或参考资料来了解如何配置GPIO。 4. 编写LCD控制代码:使用C语言或汇编语言编写裸机代码,通过设置GPIO引脚的电平和发送相应的命令和数据来控制LCD。你需要根据LCD的接口协议编写相应的代码。 5. 调试和测试:将代码下载到IMX6ULL开发板上,连接LCD,并进行调试和测试。你可以使用示波器或逻辑分析仪来检查信号是否正确发送到LCD,并观察LCD是否正确显示图像。 请注意,裸机编程需要一定的硬件和低级编程知识。确保在开始之前对相关的知识和技术有一定的了解。另外,IMX6ULL平台可能有其特定的文档和资料,你可以查阅相关文档以获取更详细的信息。祝你成功学习LCD控制!如果你有其他问题,欢迎继续提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

li星野

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值