首页资源分类嵌入式处理器MSP430 > MSP430G2553之ILI9341液晶屏的使用(超级好)

MSP430G2553之ILI9341液晶屏的使用(超级好)

已有 445500个资源

下载专区

上传者其他资源

    文档信息举报收藏

    标    签:MSP430G2553液晶屏简单

    分    享:

    文档简介

    这个是我自己写的,特别容易读懂,对于理解液晶屏与单片机之间通信的理解非常有帮助。

    文档预览

    前言 我的这篇文章根本不是什么出版社发行的书籍,更不是某个工程师写的宏篇 巨著,这篇文章充其量就是一篇应用笔记。其实这样的小文章没有写前言的必要, 但我想在正文前写点什么,以便告诉大家我这篇文章是干什么用的,所以就套用 了书籍的格式,开头写上了前言。希望大家能够理解,勿喷~~ 这篇文章主要介绍了 MSP430G2553 单片机和 320*240 TFT 液晶屏的连接 使用方法,并详细解释了代码中所有语句的含义。解释代码过程中,又渗透了查 阅单片机手册(附件:《G2 手册》)、查阅单片机头文件的方法。 用三天的时间,大家踏踏实实仔细看这篇文章,就能达到以下程度: 1.会使用液晶屏,了解液晶屏的工作原理,显示颜色、字符、图案(双色) 2.会使用自模提取软件 3.会查阅手册,进一步理解寄存器的配置(@_@||) 4.初步掌握 SPI 通信机理 附几项说明: 1.源代码都用 WDTCTL =WDTPW+WDTHOLD;深色背景标出,方便查看 2.截图都已注明来源,用【】标注,你可以从附件里的 PDF 文档里找到 3.本实验所用器件有:G2 单片机(已焊上晶振)、数据线、8 根杜邦线、2.2 寸 液晶屏(或引脚完全兼容的 2.8 寸液晶屏,这个液晶屏每个像素点比 2.2 寸的大, 但是分辨率还是 240*320,引脚上比 2.2 寸的多了四个脚——触屏功能。但是 在做这个实验的时候,只需连上后文提到的那几个引脚) 4.液晶屏的膜禁止撕掉,液晶屏要轻拿轻放,不要长时间通电(原因下页会说的) 借用液晶屏要去管理员那里登记,如有损坏原价赔偿(非暴力手段是不会坏的) 1 第一步:把线连好,连线方式如下表所示 液晶屏 中文解释 单片机 备注 SDO 主机输入 空 所用液晶屏不会给单片机发数据的,所以这 (MISO) 从机输出 根线不用连(能省则省) LED 液 晶 屏 背 VCC 正常是 VCC 通过一个滑动变阻器连到 LED 光 上,这里为了方便直接连上。这样做虽然可 以使背光很亮,但是接线的地方等一会儿会 很烫,所以建议隔一段时间断电冷却一下下 SCK 时 钟 信 号 P1.4 SPI 通信需要有同一根时钟线 线 P1.4 口第二功能就是输出 SMCLK 时钟信号 SDI 主机输出 P1.2 M(monster 主人,主机)O(Output 输出) (MOSI) 从机输入 S(Slave 奴隶,从机)I(Input 输入) P1.2 口第二功能是 MOSI D/C 数据/控制 P1.3 这一位可以随便设定,只要可以输出高低电 选择位 平就行。本程序里选 P1.3 口 RESET 液 晶 屏 复 P1.5 这一位也可以随便设定,跟 D/C 一样。 位 本程序里选 P1.5 口 CS 片选 GND C(Chip 芯片)S(Select 选择)SPI 通信需 要给片选拉低(置 0)才能对其操作,这里为 了方便,把它一直拉低 GND 地 GND 供电用 VCC 电源 VCC 供电用 2 连好之后如下图所示: 第二步:把 tft_tft 工程导入 CCS 里,下载到单片机里(不能有中文路径) 第三步:运行程序,观察效果。能看到,白色、红色先后填充满整个屏幕,之后 显示东北大学四个白色的字,说明一切正常,下面就开始学习如何控制液晶屏。 最后的效果图: 虽然这个实验是要学习 SPI 通信的,但是在这里我几乎没有提到通信原理那一大 堆恶心的理论知识,而是先让大家看到效果,然后再去分析代码的含义。我觉得 这样学起来才有趣,才能更快学习。 好了,不多扯了,下面一行一行分析代码。 3 首先看这个 CCS 工程的结构:(强烈建议一边看 CCS 一边看看本讲解) 只有两个重要的文件——主函数 main.c 和液晶屏头文件 tft.h 接下来先看 main.c #include 不用说,include 单片机名字.h #include "tft.h"这个.h 文件是自己写的,而且在 tft_tft 这个 CCS 工程名字下面, 所以 include 时应该用“”而非<>,以前有同学在此出错。 void main() { WDTCTL =WDTPW+WDTHOLD;不用多说,每次都要把看门狗关掉 4 ######################一、IO 配置######################## P1DIR|=BIT2+BIT3+BIT4+BIT5; P1SEL=BIT2+BIT4; P1SEL2=BIT2+BIT4; P1.2 P1.3 P1.4 P1.5 都设置为输出,其中 P1.2 P1.4 不是简简单单的输出高低电 平,而是 SPI 功能,所以应该用 P1SEL 和 P1SEL2 来选择 IO 口第二功能 【G2 手册 327 页】 【来自:引脚功能】 有图有真相:P1.2 第二功能是 UCA0SIMO P1.4 第二功能是 SMCLK 5 ######################二、时钟配置####################### DCOCTL=DCO0+DCO1+DCO2; BCSCTL1=RSEL0+RSEL1+RSEL2+RSEL3; 不说别的,查手册! 【G2 手册 280 页】 关于时钟的寄存器总共就这么多,对应着找找上面那两句话是什么意思。 【G2 手册 281 页】 DCOCTL 已经找到,但是 DCO0 DCO1 DCO2 都是什么意思呢?手册里就只写 到 DCOx,但是详细点的解释是什么呢? 哈哈,别忘了,我们手里还有另一个法宝:Ctrl 键。 按住 Ctrl 键,鼠标点击 DCO0,进入了 msp430g2553.h 的系统文件夹,在 296~298 行代码里写着: 【来自 msp430g2553.h】 6 简单翻译一下:DCO0 0010 0000 DCO1 0100 0000 DCO2 1000 0000 正好分别对应了 DCOCTL 这个八位寄存器的 5、6、7 位 7 6 5 4 3 2 1 0 DCO0 0 0 1 0 0 0 0 0 DCO1 0 1 0 0 0 0 0 0 DCO2 1 0 0 0 0 0 0 0 DCO0+ 1 1 1 0 0 0 0 0 DCO1+ DCO2 这下彻彻底底地明白了 DCOCTL=DCO0+DCO1+DCO2;的意思:8 位寄存 器 DCOCTL 的 5、6、7 位全部置 1,其余位全部置 0 同理,找到 BCSCTL1 那句话的意思(我直接贴图,请自己分析,完全同理) 【G2 手册 281 页】 7 【来自 msp430g2553.h】 最后贴上一个更容易懂的图,解释一下 DCO 和 RSEL 配置过来配置过去到 底有什么实际用途。看下图,看见没,DCO 和 RESL 的不同组合使得时钟频率 不同!! 那么现在给小伙伴们问几个问题: 【G2 手册 277 页】 (1).刚才那两句话使得 DCO=? RSEL=?时钟频率大概是多少? 【答案】DCO=2E^0+2E^1+2E^2=7 RSEL=2E^0+2E^1+2E^2+2E^3=15 看纵轴,大约是 25000kHz,也就是 25MHz (2).如何使 DCO=5? 【答案】5=1+4; DCOCTL=DCO0+DCO2; (3).如何使 RSEL=7? 【答案】7=1+2+4; BCSCTL1=RSEL0+RSEL1+RSEL2; 8 通过刚才很恶心的分析之后,我们能知道,时钟是通过配置两个寄存器—— BCSCTL1 和 DCOCTL 完成的。如果你不想在时钟这一块儿滞留太长时间,那就 跳过这一部分吧。如果你想通过这个例程深入学习时钟的有关知识,那么我推荐 你继续看一看 BCSCTL2 寄存器(G2 手册.282 页),因为正常来说还要对它配置。 这个例程里没有配置它,是因为用了它的默认配置。其实这个道理很简单,这么 说吧,单片机必须靠时钟节拍才能工作,但是你以前写程序的时候根本没有配置 过时钟,是不是,可单片机也并没有因为你没有配置时钟而不工作了呀。原因就 在于我们不是没有配置时钟,而是保持了单片机的默认设置,没有修改。所以还 能得出一个结论:没有配置 =等价于= 保持默认配置。 附:G2 单片机的默认时钟频率是 1MHz(左右,不是太稳定) __delay_cycles(1000000);大约延时 1 秒。 9 ####################三、SPI 寄存器配置#################### 呃呃呃,其实我还是想简单说两句 SPI 究竟是个神马玩意。 1.它是一种通信协议,所谓协议,就是规则。SPI 通信协议是外国几个搞 通信的科学家鼓捣出来的,经过测试挺好用,后来就广泛流行起来了; 2.SPI 与其他通信协议显著不同之处是四个东西:MISO、MOSI、CS、SCK 它们的意思分别是主机输入从机输出、主机输出从机输入、片选、时钟,我们先 不管它们的具体用途,现在多念几遍,混个脸(zui)熟; 3.详细介绍 MISO:主机接收来自从机的信号,本例程里只有单片机(主机)向液晶屏(从 机)发送信号,所以 MISO 这个口不用,直接 Pass 掉~ MOSI:从机接收来自主机的信号,本例程里最重要的 IO 口之一,单片机向 液晶屏发送什么指令都是通过这个通道传送的; CS:片选,是多机通信用的,比如一个单片机控制三个液晶屏,每次发送指 令前要告诉这三个液晶屏谁接收这个指令。具体片选的方法就是发送指令前将从 机 CS 口电平拉低,发送结束后再将 CS 口电平抬高。可是,哈哈,本例程里只 有一个从机,每次单片机发指令肯定是发给液晶屏这个从机,所以我们偷工减料 地从硬件上将液晶屏的 CS 口电平拉低,就是把这个口直接连到单片机的 GND 上。 →__→ SCK:时钟线接口。看看 SPI 通信的属性:同步全双工通信协议,同步就是说 有公共的时钟信号,怎么保持时钟信号一样呢?那就要单片机的时钟系统产生时 钟信号,再通过一个具有传输时钟信号功能的 IO 口传输给液晶屏。这个不二人 选就是 P1.4 口,它具有对外输出 SMCLK 的时钟信号的功能。这个口也是本例 10 程最重要的 IO 口之一。 刚才简单介绍了 SPI 通信是怎么回事,如果想系统地看一看 G2 单片机都带 有哪些通信功能就看书上第九章(159 页) 接下来开始一句句分析代码的含义 UCA0CTL1 |= UCSWRST; UCA0CTL0 |= UCCKPH+UCCKPL+UCMSB+UCMST+UCSYNC; UCA0CTL1 |= UCSSEL_2; UCA0CTL1 &=~UCSWRST; UCA0MCTL = 0; UCA0BR0 = 0; UCA0BR1 = 0; 首先我跟大家说,见到这么多类似于汇编语言的全是大写字母的寄存器不要 害怕,因为,它们只不过是一堆变量而已。我告诉你,在我眼里,上面那些语句 就是这个样子的:把 0000 0000 变成 0010 0111……其实更何况我们手里有两 大法宝专门攻克这些寄存器:G2 手册和 Ctrl 键。好了,现在直接贴截图: 【G2 手册 443 页】 11 #define UCSSEL_1 (0x40) /* USCI 0 Clock Source: 1 */ #define UCSSEL_2 (0x80) /* USCI 0 Clock Source: 2 */ #define UCSSEL_3 ( 0x C0) /* USCI 0 Clock Source: 3*/ #define UCSWRST (0x01) /* USCI Software Reset */ 【来自 msp430g2553.h 第 705~707、689 行】 第一行、第四行代码都是对 UCA0CTL1 操作的,第一行先复位一下 USCI 功能,第四行再启用一下 USCI 功能(USCI 功能是什么?是一个硬件通信模块, 它可以通过软件配置成 UART、SPI、I2C 三种通信模式)。复位和启用之间的第 三行也是对 UCA0CTL1 操作的,UCSSEL_2 是 1000 0000,7-6 位是 10,查上 面寄存器图知道这句话对应的意义是 USCI 时钟来源是 SMCLK。 【G2 手册 443 页】 12 #define UCCKPH (0x80) /* Sync. Mode: Clock Phase */ #define UCCKPL (0x40) /* Sync. Mode: Clock Polarity */ #define UCMST (0x08) /* Sync. Mode: Master Select */ #define UCMSB (0x20) /* Async. Mode: MSB first 0:LSB / 1:MSB */ #define UCSYNC (0x01)/* Sync-Mode 0:UART-Mode/1:SPI-Mode */ 【来自 msp430g2553.h 第 667~669、659、664 行】 【G2 手册 440 页】 UCA0CTL0 |= UCCKPH+UCCKPL+UCMSB+UCMST+UCSYNC; 上面这句话翻译过来就是: UCCKPH+UCCKPL:时钟的相位和极性是上图第一个红框所选样式 UCMSB:MSB 优先,如上图所示 MSB 在前,LSB 在后 UCMST:采用主控模式 UCMODEx :没有配置,采用默认设置,2-1 位是 00,查寄存器表知,设置成 3 引脚 SPI UCSYNC:采用同步模式 13 到现在,已经解决了 2 个寄存器了:UCA0CTL1 和 UCA0CTL0,返回去看 SPI 寄存器配置的代码,还剩三个寄存器没有解释:UCA0MCTL、UCA0BR0 和 UCA0BR1 。我告诉你,这三个寄存器不配置也没事,你可能纳闷了,这话怎么 讲呢?你别看这个程序里给他们配置了,但你看看给他们赋的值是多少??全都 是 0!而 0 是什么?就是默认状态!我让这三个寄存器保持默认状态。软件里配 置成默认状态 跟 不配置让单片机自己选择默认状态的效果是一样一样的~ 那有人问了,既然配不配置都一样,那为什么要配置呢?嘿嘿,其实吧,我 当时学这个的时候,看别人的程序,他在程序里就写上了这三句话,于是在这里 我直接 Copy 过来了。真要较真地说,在这个程序里,这三句话没什么用处。(不 信的话,把这三句话注释掉,看看效果有没有变化) 还有人爱钻牛角尖,就死活要知道这三个寄存器是什么意思,配置后有什么 影响没有。好,为你的精神感动,下面简单讲一下。(没兴趣的话直接跳过这一 段文字) 依旧是贴寄存器的图: 不知道爱钻牛角尖的你能看清图片下面的小字不? 【G2 手册 444 页】 上面写着——位时钟预分频器设置。(UCxxBR0+UCxxBR1×256)的 16 值 组成了预分频器值。看懂没?这两个寄存器通过上面那个公式把比特率分频了!! 14 都知道二分频速度就慢一倍,我们这是液晶屏,刷屏越快越好,所以我们不要分 频,所以我们直接给他俩赋 0。 还有一个 UCA0MCTL 寄存器,在 G2 手册里翻了个底朝天才发现有一点点 介绍,它的名字叫 USCI_A1 调制控制寄存器,然后,然后就没有然后了!!后面 竟然也没有更详细的介绍。我想,爱钻牛角尖的你一定还能想到 Ctrl 键,可是, 结果令人……失望: SFR_8BIT(UCA0MCTL); /* USCI A0 Modulation Control */ 【G2 手册 442 页】 难道,这个寄存器的存在感就这么低么?!!如果我到这里戛然而止,那对于爱 钻牛角尖的你来说算是一种折磨……不行,我要带你一起找到更好的解释 ………… 终于,在【课本 203 页】找到了这个: //-----f_UCxCLK = 12MHz/50 = 240kHz----UCA0BR0=50; UCA0BR1=0; 15 UCA0MCTL=0; 这能说明什么?说明 Grace 配置的时候也会自动将三线制 SPI 主机模式的 UCA0MCTL 寄存器赋 0。至此,我可以义正言辞地对你说:钻到头了,不要再 往下钻了,UCA0MCTL=0;可以理解成 SPI 通信里不进行调制。OVER。 以后记住,配置 SPI 的时候,直接将 UCA0MCTL 赋 0,或者根本不对这个寄存 器配置。这个属于旁枝末节型的寄存器,希望钻牛角尖的人能分得清孰轻孰重。 不管你看没看上面一段钻牛角尖的话,看到现在,我们都已经把 SPI 寄存器 配置看完了,如果嫌麻烦,那就直接用书上的方法:Grace 图形化配置寄存器, 自动生成寄存器配置代码(书上 43~48,198、199 页)。 那现在有人问了:为什么一定要把寄存器配置成这样呢?配置成别的样子不 行么?好,我现在告诉你,这是最常用的 SPI 模式,也就是说,我们一旦用到 SPI,90%以上都要配置成这种模式(3 线 SPI,主控模式),所以到时候直接抄 就行了。唯一有点变化的就是那两个看起来没什么用的,我们直接赋 0 的寄存器 ——UCA0BR0 和 UCA0BR1,因为它们是控制波特率的。 16 #####################四、液晶屏初始化##################### R_ESET0; __delay_cycles(1000000); R_ESET1; __delay_cycles(1000000); TFT_INIT(); __delay_cycles(1000000); 液晶屏这个玩意吧,你用之前总是要给它复位一下。 按住 Ctrl,点击 R_ESET0,进入 tft.h 的宏定义里面,发现液晶屏上复位接 口连的是单片机的 P1.5。所谓的复位就是将 RESET 电平拉低,再抬高。复位有 什么用呢?复位可以让机器回到最初设置,好比手机恢复出厂设置一样。初始化 之后再操作,这是对高级外设的习惯做法。然后就是 TFT_INIT()这个液晶屏初始 化函数,我们先放一放,一会儿再分析这个函数。 17 #####################五、液晶屏显示###################### CLEAR_TFT(White);__delay_cycles(1000000); CLEAR_TFT(Red);__delay_cycles(1000000); HANZI(100,150,White,Red);__delay_cycles(1000000); _bis_SR_register(LPM3_bits); 这里只表面上解释一下这些函数的作用,函数的内容一会儿再展开讲。 第一个是全屏填充白色,之后延时; 第二个是全屏填充红色,之后延时; 第三个是从屏幕上的(100,150)坐标点上开始画汉字,汉字是红底白字 第四个是完成上面操作之后,单片机进入低功耗模式三。 至此,我们已经看完 main.c 里的全部内容了,接下来开始解剖 tft.h 18 #####################六、tft.h 代码分析#################### 这里面好多液晶屏驱动,那里面的寄存器多得可够写一本书的了,在这里我 就不复制粘贴那些代码了,建议一边看代码,一边看本讲解。 一开始就是颜色宏定义,每一种颜色对应一个 16 位的二进制数,所以最多 可以代表 65536 种颜色。接下来是 IO 口宏定义,这样做有一个很好的用处就是 方便移植。比如以后写程序时 P1.3 口被占用,想改成 P1.6 口,那就直接把两句 话中的 BIT3 都改成 BIT6 就行啦~如果不是这样,那就得把所有出现 P1.3 的位 置都改成 P1.6………很麻烦的。再往下的四个函数是最最恶心的!!!即便到现在, 我都不知道为什么要这样写。但是我大体上能解释一下这些都是干什么的。 void LCD_WRITE_BUS(char a) { UCA0TXBUF = a; __delay_cycles(50); while (!(IFG2 & UCA0TXIFG)); } 这是写总线函数,a 是一个 8 位的数,把它丢给 UCA0TXBUF(发送缓冲寄 存器),关于这个寄存器的详细介绍可以 Ctrl 也可以按照前面的方法去 G2 手册 里找。在这里我就形象地说一说这个寄存器的工作过程。 UCA0TXBUF 好比是一个弹射器,一旦有东西放到它上面(即:给它赋值), 它就会瞬间把这个东西弹出去(即:通过数据线发送出去),在弹出去之后,会 将 IFG2 这个寄存器里的 UCA0TXIFG 置位。诶我去,这样说真抽象,还是贴图 吧: 19 SFR_8BIT(IFG2); /* Interrupt Flag 2 */ 【G2 手册 445 页】 #define UC0IFG IFG2 #define UCA0RXIFG (0x01) #define UCA0TXIFG (0x02) #define UCB0RXIFG (0x04) #define UCB0TXIFG (0x08) 【来自 msp430g2553.h 第 163~168 行】 查头文件第 166 行知道 UCA0TXIFG 对应 IFG2 的第 1 位,再查手册上面写 着(上图红框里的):UCA0TXIFG 是 USCI_A0 的发送中断标志,UCA0TXBUF 为空时,UCA0TXIFG 被置位。——简单点说,就是,弹射器上面没有放东西(分 两种情况:本来就没有放东西或放上东西弹出去之后)的时候,IFG2 的 UCA0TXIFG 是置位的,即 IFG2 的第 1 位肯定是 1。 回过头来再看写总线函数: 第一句话是把一个 8 位的数 a 扔到弹射器上;第二句话是延时 50 个时钟周 期;第三句话是 while 循环,while(1)是死循环,while(!1)就能跳出循环,当然 20 while(!2)、while(!100)也能跳出循环,也就是说“!”之后的数非 0 就能跳出循 环。所以当 IFG2&UCA0TXIFG 非 0 的时候就能跳出循环。那么什么时候 IFG2&UCA0TXIFG 不是 0 呢?首先,查头文件知道 UCA0TXIFG 的本来面目就 是 0000 0010,所以经过简单思考就知道只要 IFG2 的第 1 位不是 0 就可以保证 IFG2&UCA0TXIFG 不是 0。IFG2 的第 1 位不是 0 还能是几呢?二进制里的就 只能是 1 了,IFG2 的第 1 位是 1 代表什么意思呢?代表弹射器上没有东西!到 现在终于明白了这句绕嘴的 while 循环的意思了:当 a 发送出去后跳出循环。 现在你可能有问题要问了:为什么要延时一段时间?为什么要用 while 语句 等待 a 发送出去?a 不是一放上弹射器上就发射出去了吗?好,综合这两个问题, 我给出一个不太靠谱的解释:a 的发送需要一段时间,比如要 10 秒钟。而第一 句话就是告诉弹射器:“我给你个 a,你把它弹出去”,这句话是一个命令,命令 下达可能就需要 0.1 秒钟,但完成这件事需要 10 秒钟,所以在下达下一个命令 之前至少要等 10 秒钟才能让弹射器两次任务不会冲突,所以我们大概延时一段 时间比如说延时 8 秒钟(去类比第二句话),这个时间其实还是不够 10 秒钟, 那为了节约时间我们就让弹射器把 a 弹出去之后举一下旗子(UCA0TXIFG),告 诉我们(CPU)它可以弹第二个数据了,我们看到这个旗子之后(while 循环), 就可以给弹射器下达第二个命令了。这样,最有效率。 好吧,现在坦白,那个奇葩的问题是我在写这段文字的时候突然想到的,那 个解释也是灵机一动想出来的,解释的不够圆满,但大体意思应该是那样的。后 来我又想去掉第二三句话会怎样,因为实际的弹射器 UCA0TXBUF 弹射速度非 常快,在刚才的情景里实际上甚至能达到 0.05 秒,而非慢吞吞的 10 秒,也就 是弹射速度说比 CPU 下命令的时间还要短。那也就是说,CPU 根被没必要去等 21 弹射器完成一次弹射,CPU 想什么时候下命令就什么时候下命令!!!这是何等 的发现啊!为了验证猜想,现在把第二三句话注释掉,编译,下载到单片机里。 果然!速度大大提高!刷屏速度快了许多! 再来个奇葩问题,既然去掉二三两句代码后效果比以前还要好,那为什么学 长以前写了这段代码呢?个人猜想:有的时候配置的时钟不如本例程快,导致 CPU 运行速度快而 SPI 通信的速率慢。那个时候这两句话的防止通信内容被“拦 腰折断”的功能就体现出来了。 好了,这个问题就此打住,我们正事还没有办完。接着看后面的程序。 LCD_WRITE_CMD 这个函数先把 D/C 位拉低(置 0),再对液晶屏通过写总线 函数发数据。 LCD_WRITE_COM_DATA 这个函数先把 D/C 位抬高(置 1),再对液晶屏通过 写总线函数发数据。 LCD_WRITE_DATA 这个函数也是先把 D/C 位抬高(置 1),再对液晶屏发数据。 稍微不同的是这个函数的形参是 16 位的,分两次通过写总线函数发出数据。先 发高 8 位,再发低 8 位。 Address_set 这个函数是窗口设定函数,它里面为什么用 LCD_WRITE_CMD 函 数和 LCD_WRITE_DATA 函数,函数实参为什么用那些十六进制数(寄存器), 我一点儿不知道。但是我知道这个函数的作用:在液晶屏上画出一块儿矩形区域。 这,也就足够了。 TFT_INIT 这个函数更是恶心。为什么这样写,我不知道。目前也没必要知道。 看函数名知道这是液晶屏初始化函数,个人感觉就像刷机包一样,让液晶屏恢复 出厂设置。这个恢复出厂设置跟 RESET 不一样,RESET 后好比是裸机一样,而 22 TFI_INIT 是装上必要的操作系统(啊啊啊,这个比喻太不恰当了,勿喷,勿喷) 到这里,跌跌撞撞地解释完 5 个奇葩的函数,现在小小总结一下:这几个函 数,就好比是液晶屏的驱动函数一样。他们为什么这样写,里面提到的寄存器都 是什么意思,我们根本没必要管。那以后怎么用呢?好办——直接复制粘贴。 原模原样地粘过去就行!其实这又扯到我们这个专业的定位了:我们是电子大类 的,电子包含的东西多之又多,涉猎范围广之又广,我们根本不可能面面俱到, 所以不要幻想自己研究得又广泛又深入。鱼与熊掌不可兼得,大学生不是研究生, 所以我们现在学东西要学会点到为止。就液晶屏而言,再往下研究液晶屏的寄存 器意义也就不大了。你今天用一个液晶屏,明天又会用到一个蓝牙模块,根本就 没有时间去太深入地研究,毕竟你的主要任务是怎么使用它,而非怎么制造它。 话说回来,你说单片机有多少种类?成千上万我不敢说,成百上千怎么也有,我 们根本不可能把每一个单片机都用得出神入化,但是单片机的基本思想都是一样 的——配置寄存器。所以学单片机要往活了学,而非,你懂的。 刚才说的那一段话味如嚼蜡,但是,对于一些同学还是有用的,不喜勿喷。 好,现在我们讲下一段程序之前先简单介绍一下液晶屏的工作原理。 先看右图,这是一个 7*7 的 LED 面阵,也就是说它上面一共有 49 个 LED 灯泡。点亮不同位置的灯泡,会组合成不同的图案。再看左图,液晶屏就像是一 个超级大的 LED 面阵,它上面一共有 240*320=76800 个彩色 LED,同样的原 理,点亮不同位置的灯泡,会组合成为更加绚丽的彩色图案。 23 再给大家说一个“定理”:本液晶屏的填充顺序(TFT_INIT 函数规定的,你想修 改的话,自己找液晶屏手册去,找到对应寄存器再去修改)是, 从(X_min,Y_min)→(X_min+1,Y_min)→(X_min+2,Y_min)→(X_min+3, Y_min)→……→(X_max,Y_min) 再从(X_min,Y_min+1)填充到(X_max,Y_min+1), 再从(X_min,Y_min+2)填充到(X_max,Y_min+2)……以此类推。 现在接着往下看程序: void CLEAR_TFT(int Discolor){ int i,j; Address_set(0,239,0,319); for (i=0;i<320;i++){ for ( j=0;j<240;j++){ LCD_WRITE_DATA(Discolor); } 24 } } 这是液晶屏清屏函数。 首先定义了两个 int 型字符 i,j,看后面代码知道它们是用来循环的。 再往后是屏幕窗口设定函数,画点范围是 X 轴:0~239,Y 轴:0~319 所包围 的区域,即整个屏幕作为画点区域。 然后是两个嵌套的循环,共循环 320*240=76800 次。 (有人可能问:为什么不直接让变量 i 从 0 到 76799 循环呢?答案是这个编译 环境下 int 型数据是 16 位的,最大到 65535;76799 超出范围,所以不行) 每次循环都执行一句 LCD_WRITE_DATA(Discolor);语句。执行 76800 次正 好把屏幕上所有点都点亮了,整体效果就是某种颜色(Discolor)充满整个屏幕, 也就是我们所说的刷屏。 (有人还问,每次循环都执行 LCD_WRITE_DATA(Discolor);也没见到有移动到 下一个像素点的语句啊,这 76800 次循环一直在一个像素点操作,怎么能充满 整个屏幕呢?——这个问题曾经也困扰过我,后来查液晶屏手册知道了, TFT_INIT 初始化函数里有这个设置,每写完一个像素点,系统会自动跳到下一 个像素点,所以第一次循环写完第一个像素点之后,第二次循环自动变成写第二 个像素点。所以无需我们在 CCS 工程里写上移动像素点的语句。) 25 转眼间我们到了最后一个函数了:写汉字函数。HANZI() 跟以前一样,先扯两句。 其实写汉字跟上面的刷屏函数没什么两样,只不过写一个汉字是在一个 16*16 或 24*24 或 30*30 的正方形范围里写的。你现在看到的屏幕上的汉字就 是在这样的正方形里画出来的(不信的话拿放大镜数数你手机或电脑屏幕上这个 字——“字”占了多少像素点,多大我不知道,但肯定是正方形。如果你的 CCS 软件没有调字体的话,你看到的绿色的注释中,汉字就是 16*16 的,字母是 8*16 的,我当年就数过…(我再啰嗦一句:CCS 默认中文字体太小太难看,还是看看 书上 29 页改一下吧)) 我想了半天不知道怎么解释显示汉字的方法,就直接贴图。你体会一下啦~ 这是 16*16 的汉字。 先在屏幕上画出一个 16*64(因为是 4 个汉字)的矩形区域(液晶屏操作第 一步必须是这个,先画好地盘,再在自己的地盘上做事) 然后填充颜色,让暗颜色的填充上背景色(底色,本例程里是红色),让亮 颜色的填充上字的颜色(本例程里是白色),然后一个点一个点地点亮,充满自 己画好的矩形区域就行啦~其实就这么简单。 在这里还得介绍一个软件“PCtoLCD2002 完美版”(不用安装,打开文件夹, 直接双击.exe 文件就行)软件不华丽的界面如下所示: 26 点击设置,进入设置界面 @点阵格式:阴码(亮点为 1,暗点为 0)(本程序里是这样的) @取模方式:逐列式(必须设置成逐列式,TFT_INIT 里规定是这样的) @每行显示数据: 点阵(生成字模之后,每行 0x__(2 位十六进制数)的个数) 索引(字模上面有个索引,东(0)北(1)大(2)学(3),每行显示的个数。这个不用 设置,每个汉字后面的数字是为二维数组用的,本程序里只用到一维数组,所以 27 不用管它) @取模走向:逆向(低位在前)(这是 HANZI 函数规定的) @输出数制:十六进制(远比十进制方便) @输出选项:不用设置,全都打勾(想看看不打勾的效果的自行实验) @液晶面板仿真:一看就懂,不用多说 @自定义格式:设置成 C51 格式,行前缀删去 { 行后缀删去 } 至此已经设置完毕,下面读一下@取模说明:从第一个点开始向下,每取 8 个点作为一个字节,如果最后不足 8 个点就补满 8 位。取模顺序是从低至高, 即第一个点作为低位。如*——取为 00000001 再看一下@取模演示:设置完之后的显示的动画就是我们用的液晶屏的填充 方式,这是最最重要的!!(注意,液晶屏要正着放) 先别嫌麻烦,再看一下取模说明的意思:每 8 个点的信息为一个单位,放到 一个 8 位二进制数里,为什么要这样做呢?是为了方便存储和读取。其实主要是 为了方便存储。 好了好了,扯得太多了,该看程序了。 28 void HANZI(unsigned int x,unsigned int y,unsigned int front_color, unsigned int back_color) {unsigned char neu[]={ 0x00,0x00,0x08,0x20,0x88,0x11,0x48,0x09,0x28,0x05,0x18,0x41,0x0F, 0x81,0xE8,0x7F,0x08,0x01,0x08,0x01,0x08,0x05,0x08,0x09,0x08,0x11, 0x08,0x20,0x00,0x00,0x00,0x00,/*"东",0*/ 0x00,0x20,0x20,0x60,0x20,0x20,0x20,0x10,0x20,0x10,0xFF,0xFF,0x00, 0x00,0x00,0x00,0x00,0x00,0xFF,0x3F,0x40,0x40,0x20,0x40,0x10,0x40, 0x08,0x40,0x00,0x78,0x00,0x00,/*"北",1*/ 0x20,0x80,0x20,0x80,0x20,0x40,0x20,0x20,0x20,0x10,0x20,0x0C,0x20, 0x03,0xFF,0x00,0x20,0x03,0x20,0x0C,0x20,0x10,0x20,0x20,0x20,0x40, 0x20,0x80,0x20,0x80,0x00,0x00,/*"大",2*/ 0x40,0x04,0x30,0x04,0x11,0x04,0x96,0x04,0x90,0x04,0x90,0x44,0x91, 0x84,0x96,0x7E,0x90,0x06,0x90,0x05,0x98,0x04,0x14,0x04,0x13,0x04, 0x50,0x04,0x30,0x04,0x00,0x00,/*"学",3*/ }; int i,j; Address_set(x,x+15,y,y+63); for( j=0;j<16*8;j++) for(i=0;i<8;i++) { if((neu[j]&(1<

    Top_arrow
    回到顶部
    EEWORLD下载中心所有资源均来自网友分享,如有侵权,请发送举报邮件到客服邮箱bbs_service@eeworld.com.cn 或通过站内短信息或QQ:273568022联系管理员 高进,我们会尽快处理。