第1节 基本驱动程序(LCD_Driver_User)
推荐给好友
打印
加入收藏
更新于2009-04-21 15:49:04

在适当的硬件的基础介绍之后,这里将以MzDesign所提供的针对MzL02的通用版LCD驱动程序为对像介绍一种LCD驱动程序的设计思想;将以在LCD上的绘点功能程序为基础,构建较为完整的驱动程序(包括显示控制)。通过下图来大概了解一下这种驱动程序的结构以及各模块之间的关系。
驱动程序架构如所示:


基础驱动程序由8个文件组成,分别为:底层驱动程序文件LCD_PortConfig.h、LCD_Driver_User.c、用户API功能接口函数文件LCD_Driver_User.h、LCD_Config.h、LCD_ASCII.c、LCD_Dis.c以及GB_Table.c、LCD_Dis.h。
LCD_PortConfig.h:该文件为底层驱动程序的头文件,主要对使用到的端口进行定义以及配置,用户在使用基础驱动程序时,要使端口的分配符合实际硬件的接线。
LCD_Driver_User.c:该文件为底层驱动程序,负责MCU与液晶模块进行数据传输的任务,主要包括初始化模块、写控制指令、写数据、读数据等函数;这些函数仅供给上一层的LCD_Dis.c调用,不建议用户在应用程序中调用这些函数。
LCD_Driver_User.h:该文件为LCD_Driver_User.c的对应头文件,里面对应C源文件当中的函数进行外部声明。
LCD_Config.h:此文件为LCD模块的功能配置文件,实际上此基础驱动程序是通用于各种不同的LCD模块的,如果需要将驱动程序应用于其它的LCD模块时,可以在该文件里面修改相关的配置;另外,功能配置文件里面提供了软件的坐标轴翻转功能配置(有些版本的代码中将此功能屏蔽了,仅为了让MCU对LCD的操作更快些)。
LCD_Dis.c:该文件中提供了供用户应用程序调用的LCD驱动API功能函数,如绘点、绘线、绘矩形、绘圆等绘图函数,以及写字符、字符串等功能。
LCD_ASCII.c:为基础驱动程序的自带西文字库的数据文件。
GB_Table.c:为基础驱动程序的汉字/位图字库的数据文件,为用户自定义使用,另外如果定义了一些有别于基础程序所提供的范例汉定规格的字库,则用户需要自行修改LCD_Dis.c当中的FontSet函数。
LCD_Dis.h:该文件为LCD_Driver_User.c的对应头文件,里面对应C源文件当中的函数进行外部声明。 

3.1.1. 端口配置头文件LCD_Portconfig
以MCS51单片机版的通用LCD驱动程序为例,看看基本驱动程序里是什么东西。
首先,LCD_PortCongif.h文件里定义了驱动程序当中所占用MCU对LCD驱动控制的端口,这里是对这些端口进行了重定义,如下:



在上面的程序当中,大量使用了标准C里的宏定义代码,包括了一些条件编译、define定义等,为的只有一个目的:方便移植及修改。
比如,在前面的代码当中就使用了一个条件编译的宏代码:


大意为:
第一行“#ifndef __LCD_PORTCONFIG_H__” 如果没有作过“__LCD_PORTCONFIG_H__”的定义,则C编译器将会把该行之后的代码(宏)进行编译,直至“#endif”之处;而在第二行中有“#define __LCD_PORTCONFIG_H__”,对“__LCD_PORTCONFIG_H__”进行了定义,此各的意义在于当该头文件在重复多次被包含时,这些被#ifndef~~~~#endif之间的宏以及相关的代码,将不会再次被重复编译。其实将一些指令译成中文意义,大概如下,这样可能对一些初学者来说更容易理解一些:


接下来对一些连接LCD的控制端口进行了定义,如控制LCD的CS端口的定义如下:


利用C51的位定义指令sbit将LCD_CS等同于端口位P2^7,即P2端口的最高位,接下来的代码当中包括操作LCD的其它程序当中,将不会直接的出现对P2端口的操作,我们使用LCD_CS的位变量定义来替代它;而接着作了两个定义,如将“LCD_CS_SET()”定义为“LCD_CS = 1”,实际上,在之后的代码当中再出现“ LCD_CS_SET()”之时,等同于出现“P2^7 = 1”,即将P2的bit7端口置高1,相当于将LCD的CS端口置高电平操作。
同样的理解,还用定义“LCD_CS_CLR()”为“LCD_CS = 0”。

然而为什么非要这样使用呢?其实试着想一下,如果需要修改连接端口,将LCD模块的CS端口连接到P1口的bit0位时,在前面的定义中,只需要修改“sbit LCD_CS = P1^0;”即可;而对于为何要定义“LCD_CS_SET()”和“LCD_CS_CLR()”感兴趣的朋友将可以从后面的章节中体会到(移植通用版LCD驱动程序至另一颗MCU);当然也不排除有很多朋友早就对这块很熟悉了。

在上面的程序代码当中,对P0端口进行了重定义,即:”#define DAT_PORT P0”,这样,在其它的代码里面都将使用DAT_PORT进行操作P0端口;实际上是定义了与MzL02的8位数据端口连接的P0口,如果在其它的应用当中,不再使用P0口作数据口的话,如改用了P1口之类的,就可以在LCD_PortConfig.h当中修改这个重定义就可以了。

其它的端口定义也是类似的意思,LCD_EP定义到了P2.3的端口,即接MzL02的EP口;LCD_RW定义为P2.4口,接RW读写控制;LCD_A0定义到P2.5,接A0数据命令选择端口;LCD_CS定义到P2.7口,接片选;LCD_RS定义为P2.6口,接LCD模块的外部复位端口。

3.1.2. MCU与LCD基本时序控制程序
一般来说,LCD模块的控制都是通过MCU对LCD模块的内部寄存器、显存进行操作来最终完成的;在此我们设计了三个基本的时序控制程序,分别是:

  • 写寄存器函数(LCD_RegWrite)
  • 数据写函数(LCD_DataWrite)
  • 数据读函数(LCD_DataRead)

这三个函数需要严格的按照LCD所要求的时序来编写,下面可以看看MzL02模块的时序图:


注意:上图是该模块的控制IC资料中的原版时序图,其实有些示意不是太稳妥(少标出了RW线信号的要求),或者说是不太严谨,不过这些不作讨论,请看分析即可。

图中CS1B(CS2)的信号即为片选CS,RS即为数据/寄存器的选择端口A0信号,E为EP;当作写入寄存器数据操作时,首先要将A0置低,以通知LCD模块即将进行的是对寄存器的操作;而RW线需要置低,以示即将要进行的是写入的操作;然后片选CS信号置低,装载数据至总线,然后在EP线上产生一个下降沿以触发LCD模块将总线上的数据最终载入;在前面的操作完成后一般都会将各个信号线的状态恢复。而数据(显存)写入、数据读出的操作时序也比较类似,这里就不多作介绍,直接参考例程即可。


数据写入以及读出的函数源码如下:


以上便是要介绍的最基本的时序操作程序,它们几乎是整个LCD驱动程序当中与底层硬件打交道的代码了,这样的话,当要改变驱动LCD的MCU端口时或者换用别的MCU来驱动LCD时,基本上只需要在这些代码里作一下修改即可。

关于读LCD状态:
而在一般的LCD模块当中,还有一个功能同样重要,就是读LCD状态;可以通过此操作获取当前LCD模块的忙状态以及一些相关的状态信息,当LCD模块正处于忙状态时,则不宜对它进行数据的写入或读出操作(有很多较老式的LCD控制器规定在忙的状态下时不允许写入或读出数据)。
所以在很多LCD的驱动程序当中,会在寄存器写入、数据写入/读出的操作前加入读取LCD状态并判别忙状态的代码;这点可以参考网上流传的很多LCD驱动程序。不过,对于MzL02这样的较新出的LCD控制器来说,已经对忙状态不是很在乎了,或者说影响已经很小甚至没有了;所以我们在前面的代码当中并没有加入这样的代码。
至于有没有必要加读状态判忙的代码,要视具体的LCD控制器而定。

关于时序的时间要求:
时序的一个非常重要的数据就是类似上图中标出的tAS88之类的时间长短要求,只是上图中并没有标出它们的具体最大最小值要求而已;但在编写这类的时序接口程序时它们还是非常重要的,当然还要看MCU的端口操作速度以及MCU的指令执行速度。打个比方,有的时序里就会有要求某些信号的电平保持最小宽度,而如果MCU的指令执行速度以及端口操作速度非常快的话,就需要酌情在连续操作端口的代码之间加入适量的延时(通用用空操作来代替,具体多少个多少时长视具体的MCU以及LCD控制器而定)以保证该信号的脉冲宽度满足要求。

在本文的所列出的源代码当中,并没有如前所述的为时序的要求而插入空操作或延时处理,因为MCU的速度并不是非常快,况且现在的LCD控制器的总线速度都挺快的了,没有必要加入而已。

 

上一节                    下一节




 
关于我们 | 诚邀加盟 | 客户服务 | 相关法律 | 网站地图 | 友情链接 | 服务信箱:service@eefocus.com
© 2006 与非门科技信息咨询(北京)有限公司 All Rights Reserved.