分享

ucGUI基于samsung 44B0的移植小结

 wfsy1983 2011-05-12
由于工作缘故接触ucGUI一段时间,参考了网上的资料将ucGUI移植到44B0的目标板上。先转贴本人参考的一份资料,该份资料可以在网上搜索到:
    在网络上看到已经有不少人把UCGUI 成功移植到44BO 上了不过他们只提供了他们的演示程序而公开他们的
移植方法过程这里我把我的移植过程写写希望对各位有帮助
我们采用的是思创嵌入式开发网研发的S3C44B0 黄金开发板及其液晶显示模块
我的移植分两个大部分来做的一是液晶的正确初始化二是UCGUI 移植原本应该包括触控屏的移植可是因
为IAR 下的中断一直没有调通就没有做触控屏的移植
我的液晶是320 240 16 灰度的跟44BO 的连接方式是4 位单扫描
一 液晶的正确初始化
液晶的初始化可以参照下面的函数最后得到显示缓冲区数组跟视窗屏幕的对应关系如下
事实上液晶的正确初始化就是需要明确显示缓冲区跟视窗屏幕的对应关系为了达到这个目的可是通
过单步调试逐个显示点来观察这种对应关系在上图中那就是这样一个顺序
Bmp[0]=0xF000;//点0
Bmp[0]=0xFF00;//点0 1
Bmp[0]=0xFFF0;//点0 1 2
Bmp[0]=0xFFFF;//点0 1 2 3
单步执行查看液晶屏上显示点的位置这样就可以确定对应关系了
这44BO 中的液晶控制寄存器中有BSWP 这个设置位它是用于调整每4 个字节的字节顺序的也是通
过单步调试来明确这种对应关系的
#define SCR_XSIZE (320)//视窗屏幕大小
#define SCR_YSIZE (240)
#define LCD_XSIZE (320)//液晶屏幕大小
#define LCD_YSIZE (240)
#define MVAL_USED (0)
#define MVAL (13)
#define INVCLK (0)
#define INVFRAME (0)
思创嵌入式开发文档embed.8800.org
#define INVLINE (0)
#define CLKVAL_SL (8) //VCLK=MCLK/(CLKVAL*2) (CLKVAL >= 2)
#define M5D(n) ((n) & 0x1fffff)
#define ARRAY_SIZE_G16 (SCR_XSIZE*SCR_YSIZE)
unsigned short Bmp[ARRAY_SIZE_G16/2];//液晶显示缓冲数组
#define CLKVAL_G16 (10) //40Mhz, CLKVAL=10 ->101Hz
#define HOZVAL (LCD_XSIZE/4-1)
#define LINEVAL (LCD_YSIZE-1)
#define MVAL (13)
#define BSWP (1)//这个决定了每4 个字节的顺序是否进行交换
#define MODESEL (2)//Gray 16
void LcdInit(void)
{
//The following value has to be changed for better display.
rDITHMODE=0x12210;
//rDITHMODE=0x0;
rDP1_2 =0xa5a5;
rDP4_7 =0xba5da65;
rDP3_5 =0xa5a5f;
rDP2_3 =0xd6b;
rDP5_7 =0xeb7b5ed;
rDP3_4 =0x7dbe;
rDP4_5 =0x7ebdf;
rDP6_7 =0x7fdfbfe;
rLCDCON1 = (0)|(1<<5)|(MVAL_USED<<7)|(0x3<<8)|(0x3<<10)|(CLKVAL_G16<<12);
// disable,4B_SNGL_SCAN,WDLY=8clk,WLH=8clk,
rLCDCON2 = (LINEVAL)|(HOZVAL<<10)|(10<<21);
//LINEBLANK=10 (without any calculation)
rLCDSADDR1 = (MODESEL<<27) | ( ((uint)Bmp>>22)<<21 ) | M5D((uint)Bmp>>1);
// 16-gray, LCDBANK, LCDBASEU
rLCDSADDR2 = (BSWP<<29)|M5D((((uint)Bmp+(SCR_XSIZE*LCD_YSIZE/2))>>1))|(MVAL<<21);
rLCDSADDR3 = (LCD_XSIZE/4) | ( ((SCR_XSIZE-LCD_XSIZE)/4)<<9 );
rLCDCON1 = (1)|(1<<5)|(MVAL_USED<<7)|(0x3<<8)|(0x3<<10)|(CLKVAL_G16<<12);
}
二UCGUI 的移植
移植的思路是使用UCGUI 支持的buffer 型的LCD 控制器EPSON 1375 我想就是这样一个使用双口RAM 的LCD
控制器吧因为我注意到UCGUI 的代码中若是使用1375 控制器时需要定义四个读写函数
#define LCD_READ_MEM(Off) *((U16*) (0xc00000+(((U32)(Off))<<1)))
#define LCD_WRITE_MEM(Off,data) *((U16*) (0xc00000+(((U32)(Off))<<1)))=data
#define LCD_READ_REG(Off) *((volatile U16*)(0xc1ffe0+(((U16)(Off))<<1)))
#define LCD_WRITE_REG(Off,data) *((volatile U16*)(0xc1ffe0+(((U16)(Off))<<1)))=data
前两个是读写内存的定义
后两个是读写寄存器的定义
注意到常数0xc00000 和0xc1ffe0 没有
思创嵌入式开发文档embed.8800.org
0xc00000 是1375 控制器的显示缓冲区开始地址(Base Address)
0xc1ffe0 是1375 控制器的寄存器开始地址(Base Address)
读写显示缓冲区就是根据地址偏移off 和开始地址0xc00000 来读写Buffer 的
读写控制寄存器就是根据地址偏移off 和开始地址0xc1ffe0 来读写Buffer 的
我想可以使用欺骗的一招我们把我们液晶的显示缓冲区的开始地址(Bmp[0])告诉这几个函数那么就可以了而
读写寄存器的两个函数我们就不用了
等我移植完毕我发现网络上的其它移植版本也是如此使用了1375 控制器的了不信看看他们提供的演示DOME
显示的液晶控制器是不是EPSON 13705
在LCD.H 中定义数据类型
#define I8 signed char
#define U8 unsigned char /* unsigned 8 bits. */
#define I16 signed short /* signed 16 bits. */
#define U16 unsigned short /* unsigned 16 bits. */
#define I32 signed long /* signed 32 bits. */
#define U32 unsigned long /* unsigned 32 bits. */
#define I16P I16 /* signed 16 bits OR MORE ! */
#define U16P U16 /* unsigned 16 bits OR MORE ! */
在LCDConf.H 中定义
#define LCD_XSIZE (320) /* X-resolution of LCD, Logical coor. */
#define LCD_YSIZE (240) /* Y-resolution of LCD, Logical coor. */
#define LCD_BITSPERPIXEL (4) //16 灰度
#define LCD_CONTROLLER 1375
extern unsigned short Bmp[]; //引入显示缓冲区数组
#define LCD_READ_MEM(Off) *((U16*) (Bmp+(((U32)(Off)))))
#define LCD_WRITE_MEM(Off,data) *((U16*) (Bmp+(((U32)(Off)))))=data
//#define LCD_READ_REG(Off) //这个函数可以不用定义反正我们没有用到
#define LCD_WRITE_REG(Off,data) //有些地方用到了定义为空避免做大改动
#define LCD_SWAP_BYTE_ORDER (1) //这个是做字节转换的
在LCD13XX.C 中定义液晶总线宽度
#ifndef LCD_BUSWIDTH
#define LCD_BUSWIDTH (16)
#endif
这里提及关键对应部分
->定义显示缓冲区时使用的short数据类型,它是16bit的:
unsigned short Bmp[ARRAY_SIZE_G16/2];//液晶显示缓冲数组
->定义读写缓冲区时使用的数据类型,也是16bit的U16:
#define LCD_READ_MEM(Off) *((U16*) (Bmp+(((U32)(Off)))))
#define LCD_WRITE_MEM(Off,data) *((U16*) (Bmp+(((U32)(Off)))))=data
//#define LCD_READ_REG(Off) //这个函数可以不用定义反正我们没有用到
#define LCD_WRITE_REG(Off,data) //有些地方用到了定义为空避免做大改动
->定义液晶总线宽度定义位16bit 的
#ifndef LCD_BUSWIDTH
#define LCD_BUSWIDTH (16)
#endif
思创嵌入式开发文档embed.8800.org
->定义字节顺序
#define LCD_SWAP_BYTE_ORDER (1) //16bit时需要交换的
读者已经能看到了对应关系了都是使用16bit的数据类型这是关键别用错了
由此延伸若是把这些对应关系换成8bit的数据类型如下
->定义显示缓冲区时使用的char数据类型,它是8bit的:
unsigned char Bmp[ARRAY_SIZE_G16];//液晶显示缓冲数组
->定义读写缓冲区时使用的数据类型,也是8bit的U8:
#define LCD_READ_MEM(Off) *((U8*) (Bmp+(((U32)(Off)))))
#define LCD_WRITE_MEM(Off,data) *((U8*) (Bmp+(((U32)(Off)))))=data
//#define LCD_READ_REG(Off) //这个函数可以不用定义反正我们没有用到
#define LCD_WRITE_REG(Off,data) //有些地方用到了定义为空避免做大改动
->定义液晶总线宽度定义位8bit 的
#ifndef LCD_BUSWIDTH
#define LCD_BUSWIDTH (8)
#endif
->定义字节顺序
#define LCD_SWAP_BYTE_ORDER (0) //8bit时不需要交换的
这样的对应关系也是可行的我测试过不过若是你想把它换成32bit的话就不行了因为UCGUI不支持32bit
的液晶总线宽度的
以上两部分是整个移植工作的关键当然还有一些繁琐的事情请阅读UCGUI的手册中Getting Started一章
在IAR下移植时需要建立group 对照文件夹加入UCGUI代码
思创嵌入式开发文档embed.8800.org
需要说明的是
除了LCDDriver下只加入LCD13XX.C 在Config下额外加入GUI_X.C外其余的都是加入相应文件夹中的所有C文件
我还额外加入了跟开发板有关的文件:
LCDLIB.C(液晶的初始化函数LcdInit())
Platform.C(板级初始化函数它会调用LcdInit()来初始化液晶控制器)
设置include路径:
我加入的inclide路径为
D:\Program Files\IAR Systems\UCGUI\gui\core\
D:\Program Files\IAR Systems\UCGUI\Config\
D:\Program Files\IAR Systems\UCGUI\gui\WM\
D:\Program Files\IAR Systems\UCGUI\gui\Widget\
D:\Program Files\IAR Systems\UCGUI\Sample\GUIDemo\
在GUIConf.H中定义
#define GUI_OS (0) /* Compile with multitasking support */
#define GUI_WINSUPPORT (1) /* Use window manager if true (1) */
#define GUI_SUPPORT_MEMDEV (1) /* Support memory devices */
#define GUI_SUPPORT_TOUCH (0) /* Support a touch screen (req. win-manager) */
#define GUI_SUPPORT_UNICODE (1) /* Support mixed ASCII/UNICODE strings */
我们还没有移植到OS上也没有加入触摸屏的支持
在GUI_X.C中
定义三个未定义的函数
void GUI_X_Log (const char *s) {}
void GUI_X_Warn (const char *s) {}
void GUI_X_ErrorOut(const char *s) {}
修改一个函数定义
思创嵌入式开发文档embed.8800.org
void GUI_X_Delay(int ms) {
// int tEnd = OS_TimeMS + ms;
// while ((tEnd - OS_TimeMS) > 0);
extern void Delay(int time);//我自己的延时函数
Delay(150*ms);
}
原因是在UCGUI的应用中有一些函数是跟时间相关的UCGUI认为使用的是UCOS UCOS会维护时间OS_TimeMS;这里
我调用我自己的延迟程序来实现延迟事实上正是因为有一些函数跟时间有关因此在UCGUI提供的演示程序中就出
现了问题例如在GUIDEMO_Speed.C中有这么个循环
for (i = 0; (((t + 8000) - (int)GUI_GetTime()) > 0) && !GUIDEMO_CheckCancel(); i++) {
}
它调用了GUI_GetTime读取当前系统时间来控制循环若是系统没有维护时间OS_TimeMS 那就会出问题了我的简
单解决方法是改为
for (i = 0;i<0xFFF;i++){
}
在其它的GUIDEMO_XXXX.C中也有这样一些循环你要是调试是发现液晶屏上的显示一直停在一个画面上很久的话
估计就是碰上了上面的问题
好了加入那个最简单的主函数Basic_Helloworld.C
void BoardInit (void);
void main(void) {
/*
ToDo: Make sure hardware is initilized first!!
*/
BoardInit ();//板级初始化它调用了LcdInit()
GUI_Init();
GUI_DispString("Hello world!");
while(1);
}
应该能在你的液晶屏上看到UCGUI跟你打招呼的了"Hello world!"
这样UCGUI的移植基本上已经完成了当然了这里只提供了移植关键的部分更多的更完整的移植还需要做不
少的工作如触控屏的移植键盘鼠标的移植中文字体的移植UCGUI支持UNICODE 中文字体不成大问题的
还有DEBUG输出的移植不难吧直接输出到串口不久可以了嘛
若是在做UCGUI的移植前你的系统已经使用同一个编译环境移植了UCOS 那么UCGUI的演示效果将是更好的原
因就是由于UCGUI中有一些跟时间相关的调用
――liandao
lycld@163.com
以上是搜索的资料,本人的移植在基本上是根据这篇文档来的。随后将附上本人的一些移植总结。

前面我推荐了一篇网上很好的移植资料,下面是我的一些移植小结:

在这篇文章中介绍的是将gui移植到16级灰度的LCD上。我们现在移植的是256色的8位STN屏。下面是该款STN屏的接口图:

44b0     LFUBK9111A

PC4   ----D7

PC5   ----D6

PC6   ----D5

PC7   ----D4

PD3   ----D3

PD2   ----D2

PD1   ----D1

PD0   ----D0

PD4   ----CL2

PD5   ----CL1

PD7   ----FLM

DISP   ---/DP(或接+5V)

GND   ----GND

VDD50 --VDD(+5V)

VEE   -----外接+25V

V0   ------VEE与GND接20K微调电阻,V0接中间脚。

4.2 LCD初始化

由于移植的LCD不一样,我们必须对文档中介绍的方法进行必要修改,下面就是我在

移植过程中觉得需要注意的地方:

1.       由于LCD的不同,文档中的44B0的初始化必须修改,下面是我在程序中用到的初

始化程序:

    void LcdInitlib(int depth)

{

    rPCONC=rPCONC&~(0xff<<8)|(0xff<<8);      //GPC[4:7] => VD[7:4]

rLCDCON1=        

(0)| (2<<5)|(MVAL_USED<<7)|(0x3<<8)|(0x3<<10)|(CLKVAL_COLOR<<12);

           // disable,8B_SNGL_SCAN,WDLY=8clk,WLH=8clk,

       rLCDCON2=(LINEVAL)|(HOZVAL_COLOR<<10)|(10<<21);

           //LINEBLANK=10 (without any calculation)

       rLCDSADDR1=

(0x3<<27) | ( ((U32)frameBuffer256>>22)<<21 ) | M5D((U32)frameBuffer256>>1);

           // 256-color, LCDBANK, LCDBASEU

       rLCDSADDR2=

M5D((((U32)frameBuffer256+(SCR_XSIZE*LCD_YSIZE))>>1)) | (MVAL<<21) | (1 << 29);

       rLCDSADDR3= (LCD_XSIZE/2) | ( ((SCR_XSIZE-LCD_XSIZE)/2)<<9 );

       //The following value has to be changed for better display.

       rREDLUT =0xfdb96420;   

rGREENLUT=0xfdb96420;

       rBLUELUT =0xfb40;

       rDITHMODE=0x0;

       rDP1_2 =0xa5a5;     

       rDP4_7 =0xba5da65;

       rDP3_5 =0xa5a5f;

       rDP2_3 =0xd6b;

       rDP5_7 =0xeb7b5ed;

       rDP3_4 =0x7dbe;

       rDP4_5 =0x7ebdf;

       rDP6_7 =0x7fdfbfe;

       rLCDCON1=

(0)| (1)|(2<<5)|(MVAL_USED<<7)|(0x3<<8)|(0x3<<10)|(CLKVAL_COLOR<<12);

           // enable,8B_SNGL_SCAN,WDLY=8clk,WLH=8clk,

    }     

}

其中frameBuffer256是显存数组。

4.3 ucgui移植

在 lcdconf.h我们对读写显存数组的宏定义进行修改

#define LCD_READ_MEM(Off)        frameBuffer256[Off]

#define LCD_WRITE_MEM(Off,data)   frameBuffer256[Off] = data

注意不要将这两个宏定义定义成GUI原来的形式:

#define LCD_READ_MEM(Off)         *((U16*) (frameBuffer+(((U32)(Off))<<1)))

#define LCD_WRITE_MEM(Off,data)   *((U16*) (frameBuffer +(((U32)(Off))<<1)))=data

定义显存数组的时候一定要注意数据类型,还有还需要注意的是定义数组是一维数组:

unsigned short int frameBuffer256[320*240/2];

还有关于颜色显示方面,在文档上没有提及,因为44B0的RGB数据的存储模式是332模式,所以在头文件中我们还必须定义以下宏:

#define LCD_FIXEDPALETTE 332

#define LCD_SWAP_RB       1

在config.h我们的定义的宏定义和文档中的一致。

#define GUI_OS                    (0) /* Compile with multitasking support */

#define GUI_SUPPORT_TOUCH         (0) /* Support a touch screen (req. win-manager) */

#define GUI_SUPPORT_UNICODE       (1) /* Support mixed ASCII/UNICODE strings */

#define GUI_DEFAULT_FONT          &GUI_Font6x8

#define GUI_ALLOC_SIZE            12500 /* Size of dynamic memory ... For WM and memory devices*/

/*********************************************************************

*

*         Configuration of available packages

*/

#define GUI_WINSUPPORT            0 /* Window manager package available */

#define GUI_SUPPORT_MEMDEV        1 /* Memory devices available */

#define GUI_SUPPORT_AA            1 /* Anti aliasing available */

下面是我整个工程文件的目录结构:

在HardWare文件下,HardwareMain.c主要是硬件初始化,在这里函数会调用LCDLIB.C文件中的LCD初始化函数。就是上面介绍的到的void LcdInitlib(int depth),其中44binit.s、44BLIB.c、44BLIB_A.s是44B0的启动代码文件。其余文件是测试时使用,可以不包含。

还有在ConverColor文件下,我们只包含文件LCDPM332。C文件就可以了,因为我们只用到332模式的色彩模式。其余文件下的文件也可以精简,但是初步移植时,不建议精简。待日后实际应用时,我们可以逐步精简我们没有用到的功能文件。

原帖地址:http://hi.baidu.com/mikenoodle/blog/item/6d2f08b31bf1a1a1d8335a3e.html

6月22日

在uC/GUI中实现汉字显示

以人为本的系统开发观念不断深入人心,GUI做为典型的人机交互接口,在系统开发工程中具有不可动摇的地位。易用的操作界面已成为衡量产品开发成功的重要因素。据统计,系统工程师要花去近三分之一的时间用于GUI系统的开发。完善的GUI开发系统,把设计人员从繁重的低层图形界面设计中解放出来,使其有更多的时间用于系统性能的提高。这极大的缩短了开发周期,使产品在激烈的市场竞争中抢占先机。

uC/GUI是Micrium公司专门针对嵌入式系统开发的一款通用图形界面开发系统。它提供高效的图形LCD管理,可以嵌于单任务和多任务嵌入式系统中。在小模式(small system)下,只需要100 B的RAM+500 B的STACK(RAM)+10~25 KB的ROM,全部代码用C语言完成,具有良好的可移植性,适用于各种CPU。
uC/GUI是一款非常简洁的图形界面开发系统,但是它缺乏对中文汉字的支持,严重阻碍了在国内的使用。本文结合工程实践,着重讲述汉字在uC/GUI上的实现过程,汉字字库的创建方法及其相关程序代码。
1uC/GUI中字体系统结构
uC/GUI中字体的显示是通过查找字模的方式实现的。字体库中的每一个字母都有其对应的字模,所有的字母的字模由GUI_FONT和GUI_FONT_PROP这两个结构体来进行统一管理。GUI_FONT结构体中定义了该字母的点阵大小(比如16×16或者8×8)和GUI_FONT_PROP结构体的入口地址。GUI_FONT_PROP这个结构体建立了字库中字母编码(比如字母A在ASCII中的字母编码为33)和字模数据存放地址的映像。值得一提的是,GUI_FONT_PROP中pNext指针指向下一个GUI_FONT_PROP数据的入口地址,这为解决在字母编码不连续的情况下,保证字模数据在程序段的存储连续这一问题提供了一个良好的解决方案。设计人员可以定义多个GUI_FONT_PROP结构,只要使上一个结构体的pNext指针指向下一个GUI_FONT_PROP结构体,并且保证该指针的最终指向零地址空间即可。
2汉字编码
(1) 汉字字模的获取
在工程中出于节省程序存储空间的需要, GB231280(国标字符集也就是常说的国标码)中的3755个一级常用汉字足以满足实际需求。如果通过单字手工取模的方式,工作量是不可想象的,所幸有了电脑,又有了汉字库,可以编程解决。找个标准的汉字库,比如UCDOS中的HZK16就可以,但是在HZK16中字模是按区位码的顺序排列的。只要把汉字内码的高低位都减去0xA0后,就得到了汉字的区位码。一级常用汉字的内码范围是高字节0xB0~0xD7,低字节0xA1~0xFE。以下是提取HZK16文件中的一级常用汉字的部分代码:
for(i=0;i<=(0xD7-0xB0);i++)
for(j=0;j<=(0xFE-0xA1);j++)
if(fseek(fin,((i+0xB0-0xA1)*94+(j+0xA1-0xA1))*32L,SEEK_SET))
printf("seek err\\n");
fread(buffer,32,1,fin);
(2) 程序中直接输入汉字的实现
uC/GUI手册中介绍的用unicode编码进行输入的方法相当烦琐,当需要输入大量汉字时,这一问题就更加突出。经过分析内核代码,会发现程序中输入的unicode码其实就是字库中GUI_FONT_PROP定义的该字映像地址。在程序中,直接输入字母后计算机获得的是汉字内码,所以只要把GUI_FONT_PROP中的映像地址改为字母的内码就可以了,对于英文字母就是其ASCII编码,而汉字就是汉字内码。下面是uC/GUI中F16_1HK.C文件中定义的GUI_FONT_PROP结构体对象,通过注释了解映像地址和字模数据存储地址的对应关系。
GUI_FLASH const GUI_FONT_PROP GUI_Font16_1HK_Prop1 = {
0x3041/* 映像地址起始位置*/
,0x3093/* 映像地址结束位置*/
,&GUI_Font16_HK_CharInfo\[0\] /*字模代码的入口地址*/
,(void GUI_FLASH *)&GUI_Font16_1HK_Prop2/* 下一个GUI_FONT_PROP的地址*/
};
3新建汉字字库
在uC/GUI中动态地读取字模是无法实现的,即当程序运行时动态地从汉字库中读取所需字母的字模,因此只有把所有一级常用汉字的字模全部读出,按照uC/GUI中字库创建的标准去创建新的字库就可以了。下面以在F16_HZ_ALL.C中创建字体GUI_Font16_HZ为例来说明具体的步骤。
第一步:声明全局字体结构体对象GUI_Font16_HZ,该声明必须在GUI.H文件中加以声明。
extern const GUI_FONT GUI_Font16_HZ;
第二步:定义一个用于存放字模数据的数组。
GUI_FLASH const unsigned char acFont16HZ\[\]\[32\] ={{/*单字字模数据1*/},……,{/*单字字模数据n*/}};
第三步:定义一个GUI_CHARINFO的结构体对象数组,用于说明每个字母的字模数据在程序段存储的方式。
GUI_FLASH const GUI_CHARINFO GUI_Font16_HZ_CharInfo\[3760\]={
{16,16,2,(void GUI_FLASH *)&acFont16HZ\[0\],……,16,16,2,(void GUI_FLASH *)&acFont16HZ\[3759\] },};
第四步:按汉字内码的高位来定义多个结构体GUI_FONT_PROP对象。
GUI_FLASH const GUI_FONT_PROP GUI_Font16_HZ_Prop0 = {
0xb0a1,
0xb0fe,
&GUI_Font16_HZ_CharInfo\[0\],
(void GUI_FLASH *)&GUI_Font16_HZ_Prop1
};
第五步:把创建的汉字字库文件F16_HZ_ALL.C添加到uC/GUI的工程中,通过以下语句实现在LCD上显示汉字:
GUI_SetFont(&GUI_Font16_HZ);
GUI_DispStringAt(GUI_UC_START
"成都理工大学"
GUI_UC_END
,20,10);
结语
本文介绍了怎样在uC/GUI中解决汉字的显示问题,作者的汉字字库文件全部通过C语言程序创建,字库文件在uC/GUI的评估软件simulation上编译通过,读者可以根据实际的需求创建自己的字库,以适应项目的需求。

原帖地址:http://www./wz_8456.htm

μC/GUI在MSGl9264液晶上的移植

μ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结构 MSP430F149是一款低功耗单片机,其供电电压为1.8~3.6V,而MSGl9264液晶为5V供电液晶,输入高电平为3.3V。为确保与液晶的输入电平兼容,MSP430F149的供电电压可设置为3.6V,这样就可以把MSP430F149与液晶直接连接而无需额外的驱动芯片。MSP430F149与LCD的接口电路如图4所示。 μC/GUI提供的函数库和各种显示效果都是通过表2所示接口函数在LCD上实现,所以LCD驱动文件的实现也就是把这些硬件接口函数的实现。 由于MSGl9264液晶与μC/GUI提供的LCDSLin较相似,所以笔者以μC/GUI提供的LCDSLin.C文件为基础,编写针对MSGl9264液晶的驱动程序。

μ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数据)。这些函数的实现与底层硬件有关,必须根据硬件连接的具体情况编写这些函数。

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所示函数构成。

通过分析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即可。

上面那个转贴已经很详细的说明了UCGUI的移值过程,把UCGUI的移值所要做的工作全部都从头到尾,从深到浅讲了一遍,并且让读者知道了UCGUI的驱动的机制,把其对上的接口讲了, 而且对下LCD硬件的接口也讲了, 我想多数人看了就会明白UCGUI的驱动应该如何写了.

以后大家写简单总线型的驱动, 在UCGUI中必须完成的接口如下:

[通常情况下并不是所有结口都要实现, 但是有一个是必须实现的, 就是向LCD写数据及写命令这两个接口]

void LCD_X_Write00(char c);//传命令, 必须实现...
void LCD_X_Write01(char c);//传数据, 必须实现...
char LCD_X_Read00(void);//读数据或是状态, 不是必须...
char LCD_X_Read01(void);
#define LCD_WRITE_A1(Byte) LCD_X_Write01(Byte)
#define LCD_WRITE_A0(Byte) LCD_X_Write00(Byte)
#define LCD_READ_A1(Byte) Byte = LCD_X_Read01()
#define LCD_READ_A0(Byte) Byte = LCD_X_Read00()

读的接口不一定要实现, 有的硬件控制器不支持从LCD读回数据, 此时一般会用到数据缓冲, 将写到LCD的数据缓存起来.

所有的驱动函数, 最终的数据都是通过如下接口写入LCD或是从LCD读取的.

这里我再进一步的将与LCD硬件部分相关的再补充几点:

1.数据/指令到LCD控制器的传送.

2.传送数据前如何设定前列地址.

3.初始化工作.

另外, 还提供了有关这种LCD屏幕的两个应用说明资料如下:

http://www./ucgui/LM19264A-AppNote-V0.1.pdf

LM19264D-AppNote-V0.1.pdf http://www./ucgui/

以及LCD屏的说明资料:

http://www./ucgui/LM19264BBC-Manual-Rev0.1.pdf

以及此种LCD模块的驱动,仅供参考:

http://www./ucgui/lcd19264.rar

一.液晶显示屏的区域


发送图片到手机

二.命令集


发送图片到手机

三.硬件连接图


发送图片到手机

四.驱动截图

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多