介绍在MSP430F149单片机上移植,μC/CUI到MSCl9264液晶的过程, 详细阐述了,μC/GUI移植的原理以及在移植中应注意的事项。
μC/GUI是美国Micrium公司出品的一款针对嵌入式系统的优秀图形软件。与μC/OS一样,μC/GUI具有源码公开、可移植、可裁减、稳定性和可靠性高的特点[1]。采用μC/GUI,开发人员可以很方便地在液晶上显示文本、曲线、图形以及各种窗口对象如按钮、编辑框、滑动条等,可完全产生类似于Windows的显示效果。另外,μC/GUI提供了在VC下的仿真库,这使得用户完全可以在Windows下仿真μC/GUI的各种效果。
采用μC/GUI,可以大大降低嵌入式系统中显示设计的难度,但μC/GUI的使用需针对不同的液晶编写相应的驱动程序才能实现。本文通过移植μC/GUI到MSGl9264液晶的过程,介绍了μC/GUI移植的原理以及移植中应注意的事项。
1 开发工具和运行环境
为了实现μC/GUI的移植,选用MSP430F149。MSP430F149是一款16位超低功耗单片机,具有强大的处理能力(RISC结构、125ns的指令周期)和丰富的片内外设(如硬件乘法器、ADC、定时器、看门狗等)。 它内部具有2KB的RAM和60KB的FLASH,能基本满足μC/GUI运行的需要[2]。
软件开发环境采用IAR公司的集成开发环境IAR EW430 2.10A。相对于较早的EW430 1.26A版本,2.10版本在各个方面有了较大改进,尤其是项目管理和调试上有了较大的改动,这使得移植μC/GUI更加方便。
2 μC/GUI移植
μc/GUI针对不同的液晶控制器提供了多种驱动程序,如KS0713、SEDl335、T6963等控制器都有对应的液晶驱动程序。但在很多情况下,用户采用的液晶,μC/GUI并没有提供其对应的驱动程序,需自己着手编写特定液晶的驱动程序。
2.1 液晶显示器工作原理
为了能编写正确的液晶驱动程序,了解相应液晶的显示原理非常重要。本文采用的MSGl9264液晶为192x64点阵单色液晶,其中包含一个行驱动器KS0107B和三个列驱动器KS0108B,每个列驱动器KS0108B对应一块64x64的液晶[3]。
MSGl9264液晶的控制线为R/W、RS、CSA、CSB和LCDEN,数据线为D0~D7。RS用于指示当前的操作是数据还是寄存器,R/W用于表明当前是读还是写,CSA、CSB用于选择相应的列驱动器(其选择关系可见图1)。RS和R/W的功能可见表1,液晶显示器的读写时序见图2。
MSGl9264模块一共提供7种指令(由RW、RS及数据总线的电子决定),用于对该模块状态及显示进行控制。这7种指令包括显示开关控制、设起始行、设起始列、设页地址、读状态、读/写显示内容。通过这些指令的组合,可以控制液晶显示各种图形。
2.2 μC/GUI结构
μC/GUI的软件体系结构如图3所示。μC/GUI函数库为用户程序提供GUI接口,包含的函数有文本、数值、二维图形、输入设备以及各种窗口对象。其中,输入设备可以是键盘、鼠标或触摸屏;二维图形包括图片、直线、多边形、园、椭圆、圆弧等;窗口对象包括按钮、编辑框、进度条、复选框等。μC/GUI函数库可以通过GUIConf.h文件进行配置,配置的内容包括是否采用内存设备,是否采用窗口管理器,是否支持操作系统、触摸屏,以及配置动态内存的大小等。
在LCDConf.h文件中定义了与硬件有关的各种属性,如液晶的大小、颜色以及与液晶的接口函数。而LCD驱动文件则负责把μC/GUI的各种函数解释成LCDConf.h文件中定义的液晶接口函数,这个文件与具体的硬件连接无关。
μC/GUI与LCD的硬件接口通过驱动文件把硬件接口函数转化为LCDConf.h中定义的LCD读写函数。
2.3 移植过程
2.3.1 修改LCDConf.h
LCDConf.h定义了LCD的大小、颜色,对应的LCD控制器以及与硬件连接有关的LCD读写函数。按照μC/GUI的规定,底层的读写LCD函数包括LCD_WRITE_A1()(即写LCD命令)、LCD_WRITE_A0()(写LCD数据)、LCD_READ_A0()(读LCD状态)、LCD_READ_A1()(读LCD数据)。这些函数的实现与底层硬件有关,必须根据硬件连接的具体情况编写这些函数。
MSP430F149是一款低功耗单片机,其供电电压为1.8~3.6V,而MSGl9264液晶为5V供电液晶,输入高电平为3.3V。为确保与液晶的输入电平兼容,MSP430F149的供电电压可设置为3.6V,这样就可以把MSP430F149与液晶直接连接而无需额外的驱动芯片。MSP430F149与LCD的接口电路如图4所示。
LCD_WRITE A1()函数的具体实现如下:
#define LCD_WRITE_A1(Byte) //定义写LCD控制命令函数
{ //参数Byte为要写入液晶的数据。
P40UT:Byte; //把数据放到LCD的数据线上
_NOP(); //空指令,确保能可靠地写入
P1OUT&=0xef; //LCDRS=0,表示写命令
P10UTI=Ox20; //LCDEN=1
_NOP(); //空指令
P1OUT&=0xcf; //LCDEN=0,把数据写入LCD
显示RAM
_NOP();
}
2.3.2 编写LCD驱动文件
图3中的μC/GUI硬件接口函数主要由表2所示函数构成。
μC/GUI提供的函数库和各种显示效果都是通过表2所示接口函数在LCD上实现,所以LCD驱动文件的实现也就是把这些硬件接口函数的实现。 由于MSGl9264液晶与μC/GUI提供的LCDSLin较相似,所以笔者以μC/GUI提供的LCDSLin.C文件为基础,编写针对MSGl9264液晶的驱动程序。
通过分析LCDSLin文件可以发现,液晶驱动程序的核心是画点函数,大部分硬件接口函数都可由画点函数实现。因此,改造画点函数及其调用函数成为移植的重点问题。
画点函数的要求是改变液晶上任意点的颜色而不影响其他点的颜色。考虑到单片机MSP430F149的输入电压不能超过3.6V,笔者没有采取读液晶显示器内部显示RAM的方法,而是在MSP430F149的RAM中定义一个数组存储LCD显示的数据。此数组可定义为unsigned char Cache[((LCD_YSIZE+7)>>3)xLCD_XSIZE]。LCD_XSIZE、LCD_YSIZE表示液晶的大小,在LCDConf.h文件中定义。考虑到液晶的长度可能不是8的整倍数,可定义数组大小为(LCD_YSIZE+7)>>3)xLCD_XSIZE。
在定义了Cache的基础上,画点函数可如下实现:
staTIc void_SetPixel(int x,int y,LCD_PIXELINDEX c) {
//画点函数
U8 Mask=1<<(y&7); //屏蔽字
int Adr=XY20FF(x,y); //由x,y的绝对位置得到
Cache中的相对位置
//XY20FF(x,y)可被定义为((y>>3)+x×((64+7)>>3))
U8 CacheByte=Cache[Adrl; //获得显示RAM的数值
if(c) //根据颜色修改显示RAM的值
CacheBytel=Mask; //对应位“置1”
else
CacheByte&=~Mask; //对应位清零
LCD_WRITE(Adr,CacheByte);
//把CacheByte写入液晶显存并更改
Cache[Adr]的值为CacheByte
}
函数的参数x,y代表要画点的位置(x为横坐标,y为纵坐标),参数c代表要画点的颜色。在函数内部,U8为μC/GUI提供的数据格式(相当于unsigned char),Mask为屏蔽字,Adr为x,y对应显示Cache的地址。
以把液晶的(5,5)处点亮为例,此时x=5,y=5,c=1,可计算出Mask=00100000,Adr=40(表示在Cache[40]处存有(5,5)点的颜色值)。由于c=1,所以应把Cache[40]中对应位“置1”,这是通过CacheByte的值“或”上Mask的值00100000实现的。最后通过调用LCD_WRITE函数把得到的新CacheByte值写入液晶对应的地址即可点亮该点。类似地,若要使某点不亮(c=0),则应该把对应位“清零”,这可以通过CacheByte&=~Mask这条命令实现。
画点函数中调用的LCD_Write函数可如下实现:
staTIc void LCD_Write(int Adr,U8 Byte){
if(CacheIAdrl!=Byte){ //若写入值与原值不符则
把写入值保存到显示RAM中
Cache[Adr]=Byte;
if(LCD_Adr!=Adr){
LCD_SETADR(Adr); //设置液晶的起始行、起始列和CSA、CSB
}
LCD_WRITEl(Bytc);
}}
由于此液晶由三块64x64的液晶组成,LCD_SETADR函数除了设置液晶的起始行、起始列外还应根据Adr的值设置CSA和CSB的值,才能写到对应的液晶屏上。此外,在LCD_WRITEl()函数中通过调用LCDConf.h文件中的LCD_WRITE_A1()和LCD_WRITE_A0()实现液晶显示。
除了_SetPixel()函数,基本函数还包括_GetPixel()函数和XorPixel()函数。_GetPixel()函数可以返回指定点的颜色信息,XorPixel()则可以对指定点颜色取反,实现“反白”的效果。由于这两个函数较简单,这里不再给出具体代码。
以函数_SetPixel()、_GetPixel()和XorPixel()为基础,结合MSGl9264液晶的7种指令就可以实现表1所给的硬件接口函数,以此构成了LCD驱动文件。
表1 RS和R/W的功能
RS | R/W | 功 能 |
0 | 0 | 写命令 |
0 | 1 | 读液晶状态(主要用于判忙) |
1 | 0 | 写液晶的显示RAM数据 |
1 | 1 | 读液晶的显示RAM数据 |
3 讨论
为了能使用μC/GUI,必须调用GUI_Init()初始化。与硬件有关的初始化如CPU时钟频率的选择等既可以放在GUI_Init()中,也可以单独编写一个函数初始化。
表2 硬件接口函数的名称和功能
函数名称 | 功 能 |
LCD_L0_InIt() | 显示初始化 |
LCD_L0_ReInIt() | 重新初始化而不擦除显示内容 |
LCD_L0_OFF | 关显示 |
LCD_L0_ON | 开显示 |
LCD_L0_DrawBitmap() | 画图 |
LCD_L0_DrawPixel() | 以指定颜色画点 |
LCD_L0_DrwaVline() | 画水平线 |
LCD_L0_DrwaVline() | 画垂直线 |
LCD_L0_FillRect() | 填充一矩形 |
LCD_L0_XorPixel() | 翻转指定点颜色 |
调试时应从基本的显示字符串开始,逐渐增加显示的功能和复杂度。
由于笔者采用单色液晶, 在LCDConf.h中定义LCD_FIXEDPALETFE为1;若为彩色液晶,应根据液晶支持的颜色设置LCD_FIXEDPALETYE,具体可参考手册。
若使用窗口对象,则在GUI_Conf.h中定义GUI—WINSUPPORT为1。
在GUIConf.h中定义GUI_ALLOC_SIZE为动态内存的大小,应根据需要合理选择。窗口对象(如按钮)的创建需要申请内存,若申请不到内存则无法创建,相应地创建函数值为0。可由此判断GUI_ALLOC_SIZE已经不能满足需要,一方面可以考虑增加GUI_ALLOC_SIZE(受制于芯片内存的大小);另一方面也可以删除不用的窗口对象,释放内存,再创建新的窗口对象。
采用内存设备能有效克服闪烁现象,获得更快的显示速度,但它需要额外的内存。由于MSP430F149内存较小,笔者没有采用内存设备。
可以设置窗口对象的默认字体及颜色以获得更好的显示效果。在单色液晶中,简单地改变背景颜色和字体颜色即可获得反显效果。
可以通过μC/GUI提供的软件(位图转换器和字体转换器)转换需要的图像或字体为μC/GUI格式。
由于定义的Cache占用了大量的RAM,若从液晶读回显存的值则可以省去Cache占用的RAM,但同时也会降低系统运行的速度。
在LCDConf.h文件中定义了与硬件连接有关的LCD读写函数,在液晶驱动文件中调用这些LCD读写函数。这样做的好处是使驱动文件与硬件无关,一旦一种液晶的驱动编写完毕可以很方便地移植到各种系统中而只需更改LCDConf.h即可。