在网络上看到已经有不少人把 UCGUI 成功移植到S3C44B0上了,不过他们只提供了他们的演示程序,而公开他们的移植方法和过程,这里我把我的移植过程写写,希望对各位有帮助。
我们采用的是思创嵌入式开发网研发的 S3C44B0 黄金开发板及其液晶显示模块。
我的移植分两个大部分来做的:
一是液晶的正确初始化
二是 UCGUI 移植
原本应该包括触控屏的移植 可是因为 IAR 下的中断一直没有调通 就没有做触控屏的移植。
我的液晶是 320X240 16 灰度的,跟 S3C44B0的连接方式是 4 位单扫描 。
一液晶的正确初始化
液晶的初始化可以参照下面的函数 最后得到显示缓冲区数组跟视窗屏幕的对应关系如下
事实上液晶的正确初始化就是需要明确显示缓冲区跟视窗屏幕的对应关系,了达到这个目的,可是通过单步调试逐个显示点来观察这种对应关系,在上图中那就是这样一个顺序。
Bmp[0]=0xF000;//点 0
Bmp[0]=0xFF00;//点 0 1
Bmp[0]=0xFFF0;//点 0 1 2
Bmp[0]=0xFFFF;//点 0 1 2 3
单步执行,查看液晶屏上显示点的位置,这样就可以确定对应关系了。
这S3C44B0中的液晶控制寄存器中有 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)
#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 没有
0xc00000 是 1375 控制器的显示缓冲区开始地址(Base Address)
0xc1ffe0 是 1375 控制器的寄存器开始地址(Base Address)
读写显示缓冲区就是根据地址偏移 off 和开始地址 0xc00000 来读写 Buffer 的
读写控制寄存器就是根据地址偏移 off 和开始地址 0xc1ffe0 来读写 Buffer 的
我想可以使用欺骗的一招 我们把我们液晶的显示缓冲区的开始地址(Bmp[0])告诉这几个函数,那么就可以了,而读写寄存器的两个函数我们就不用了。
等我移植完毕,我发现网络上的其它移植版本也是如此使用了1375控制器的了,不信看看他们提供的演示,DOME
显示的液晶控制器是不是 EPSON 13705 。
在 LCD.H中定义数据类型
#define I8signed char
#define U8unsigned char/* unsigned 8bits. */
#define I16signed short/*signed 16 bits. */
#define U16 unsigned short/* unsigned 16 bits. */
#define I32signed 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
->定义字节顺序
#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代码。
需要说明的是
除了LCDDriver下只加入LCD13XX.C 在Config下额外加入GUI_X.C外其余的都是加入相应文件夹中的所有C文件
我还额外加入了跟开发板有关的文件:
LCDLIB.C(液晶的初始化函数LcdInit())
Platform.C(板级初始化函数 它会调用LcdInit()来初始化液晶控制器)
设置include路径:
我加入的include路径为
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上 也没有加入触摸屏的支持
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 中文字体不成大问题的。