最新日志

发表于:2007-1-21 11:43:34
标签:windows  ce  

0

WinCE的驱动程序

Windows CE有些日子了,个人觉得学习者个东西不是很难,关键他的编程是很多电子工程师没有办法搞定的,这是限制Windows CE被大量应用的一个瓶颈,并且他操作地层硬件的方法对于电子工程师来所太困难了,驱动的编写倒是和单片机编程差不多,但是要编写高质量的驱动程序还是很困难得。

前两天搞定一个有3000多行代码的驱动程序,调试比较困难,差点调到我吐血。

点击此处查看原文 >>

系统分类: 嵌入式   |    用户分类:    |    来源: 原创

评论(2) | 阅读(743)
发表于:2006-12-10 10:13:50
标签:win  ce  

0

公司让我搞wince

公司让我搞win ce.......................

学习学习疯狂的学习!

点击此处查看原文 >>

系统分类: 生活点滴   |    用户分类: 无分类    |    来源: 原创

评论(2) | 阅读(934)
发表于:2006-11-27 16:20:51
标签:VS2003  

0

终于安装好了VS2003

今天终于把该死的vs2003装上了

又可以开始更新ucgui相关的帖子了。希望关注的网友继续支持。

点击此处查看原文 >>

系统分类: 嵌入式   |    用户分类:    |    来源: 无分类

评论(1) | 阅读(736)
发表于:2006-11-26 23:39:18
标签:GUI  

3

UCGUI的模拟器UCGUISim详解

本文转自www.ucgui.com感谢原作者。

 

一、问题的由来-----模拟器完成了什么
在官方发布的UCGUI的源码包当中,附有很多的示例,源码与示例都打包成一个VC工程,在这个工程中我们可以编译和运行UCGUI的示例,进行UCGUI的图形编程,非常方便,这个工程结构下:
􀂾 Application-------UCGUI应用程序目录。
􀂾 Config-------------UCGUI配制文件目录。
􀂾 GUI----------------UCGUI源码文件。
􀂾 Simulation--------模拟器库文件、模拟器头文件目录,主要有GUISim.lib这个提供模拟器的库文件。
􀂾 System-------------应用程序的接口调用主文件,即调用用户的MainTask函数的main函数。
在这个官方提供的包中,我们进行UCGUI的图形编程时, 大多都是将要写的程序源文件放加到Application目录中, 其中应用程序中必不可少要提供的一个函数是MainTask(), 好奇的朋友会发觉, 我们程序并没创建窗口, 为什么一运行就有窗口界面并有一个LCD显示屏显示出自己的UCGUI程序的运行效果出来, 其实这些我们看不到的代码都是写在GUISim.lib这个库文件当中的,为了了解模拟器具体做了些什么,我通过反编译,还原了官方模拟器的源码,下面详细介绍模拟器的构成[以下均为官方模拟器采用的方法]。
GUISim.lib主要完成以下几件事,具体的模块划分将在文中后面描述:
1. 创建模拟器主窗口,这个窗体包括一些模拟器控制菜单,如打开LOG记录/打开调色板显示窗体/新建LCD模拟器窗体,以及暂停/继续模拟器。
2. 创建一个LCD模拟显示窗口并初始化其显示所需的各种数据结构,LCD窗口的大小由UCGUI中LCDConf.h中配制时指定(LCD_XSIZE/LCD_YSIZE),及其它的诸如每个象素占用多少位(LCD_BITSPERPIXEL),首先分配一块足够大的显存(4M)并初始化这块数据为0,每个象素占用不大于 8位时模拟显示屏幕需要用到8位图,还必须初始化要用到的调色板。
3. 提供操作LCD模拟显示屏幕的几个基本图形函数,UCGUI是一个设计层次非常清晰的图形系统,它将GUI的底层图形功能作为一层向上层提供最基本的图形功能,只有这一层才与具体的硬件相关,这一层包括基本的画点函数/矩形填充函数/调色板初始化函数/颜色索引与RGB的转换函数。
4. 提供上层的LOG记录接口的实现,主要完成UCGUI中调试信息的输出,是否输出调试信息可以由UCGUI中的一个开关设置。
5. 开启一个新的线程,在此线程中调用System\Main.c中的main()函数,此函数中再调用MainTask()函数,这个函数即为我们在模拟器中编程必须提供的一个函数,在单任务情形下UCGUI的程序均写在MainTask函数当中,供模拟器开启的线程调用。这里必须分清楚:主线程创建模拟器主窗口及LCD模拟窗口;UCGUI图形应用程序以新开的另外一个线程运行,这个线程结束时则UCGUI图形应用程序结束。
6. 从LCD模拟器窗口消息函数当中接收KEY消息及MOUSE消息,并通过UCGUI中的KEY及MOUSE接口传送到UCGUI内部以驱动UCGUI事件消息LOOP。
二、进一步入了解-----模拟器的基本实现原理
-----LCD模拟显示器的实现原理。
1. 上文中已经提到了UCGUI是一个设计层次分明的图形系统,具体的图形功能分为一层;再细化图形层,还可细分为两层:层一是最底层的直接实现基本的画点函数/矩形填充函数/调色板初始化函数/颜色索引与RGB的转换函数,这一层与直接的硬件及调色板相关,由模拟器中的LCDSIM.c文件实现;层二是位于上述 层一之上,提供更多更强图形功能的函数,如位图[1位/2位/4位/8位/16位]显示函数/水平垂直画线函数/矩形填充函数/画点函数,这些功能在\LCDDriver\LCDWin.c中完成,这一层当中的画点函数均以宏的形式提供,具体实现由上述更低的上述层一即模拟器的图形驱动来实现。
在WIN环境下,实现这个模拟器比较简单,要做的就是将GUI的结果显示给用户看,即做出一个LCD模拟显示器,将GUI画图的结果呈现在上面,
GUI画图的结果在显存中,所以也就是将显存中的GUI画图数据用位图显示出来,处理这个位图显示时,可以分两种情况:第一种情况是单个象素点占8位及8位以下的情况,此时显示位图需要用到调色板(所以初始化时必须初始化调色板),每个象素点简化处理为占用一个字节(8位以下实际情况下并非如此,实际是几位就占几位),值表示的是该象素点在调色板中的颜色索引,此时将显存中的数据以8位位图来显示处理;第二种情况是单个象素点占用8位以上的情况,这种情况下,每个象素点简化处理为占用4个字节(实际情况占几位就是几位),其值表示的是实际的该点的RGB颜色值,此时将显存中的数据以32位位图来显示处理。
2. 在LCD窗口中,将GUI画图的结果以上述位图形式画到LCD窗体当中并隔一定时间刷新显示,虽然这样做不一定在速度上非常准确实时,但基本上可以满足要求了,只要我们设定一个重画定时器,定时检测在显示中数据是否发生变化,有变化则刷新一次LCD窗口中显示的内容,可以满足要求。
3. 在解决了模拟LCD显示的问题后,还有要弄清的问题是,要显示器的一屏象素的存放位置,也即画图时所读写象素的内存,其实只需开一块足够大的内存来存放一屏LCD的象素,读写象素均在此块内存中,我们可以称此块内存为显存。如LCD宽XSize,高YSize, 显存起始地址为pFix,单个象素点占用8位或8位以下(此时一个象素占1字节),则象素点(x,y)的在显存中的地址即为 x + y * YSize + pFix;如果是单个象素点占8位以上(此时一个象素占四字节),则为(x + y * YSize)*4 + pFix。显存中象素是按行存放的,如果为了查看器(ucguiview)可以同样显示出独立运的模拟器程序的GUI画图结果,那么这个显存必须以内存映象文件的方式来实现多个进程之间内存共享访问支持。
4. 关于一个象素用多少位来表示的问题,其实这个问题与几位位图是一个意思:位图中,8位及8位以下的均会使用一个调色板,原因:用调色板[实际为一维组,表项个数由位图位数决定]可以以颜色索引来表示出一个象素点的RGB颜色,调色板中每一个表项存的是该表项索引对应的RGB颜色值,在位图存储时只须要将该位图对应的调色板数组存起来,那么存单个象素点时仅须要存其对应索引而不需要存4个字节的RGB颜色值,这样节约空间。位图中的数据为调色板中颜色的索引值,没有调色板是无法解析显示此位图的,所以8位及位以下位图对应的BMP文件会多出一个调色板内容;8位以上的位图,其位图数据即为RGB值或索引值(16位时),16位/24位/32位单象素分别占用2/3/4个字节,这里必须注意区别的是,我们这里所说的位图一个象素所占用情况分析,是指位图存储为文件时的占用情况,其中也并没有提到具体一个象素点中R/G/B分别占用多少位,特别对于16位的情况下,RGB占用情况一般分5/6/5或5/5/5,此时R/G/B中可以表示的最大值索引值为64,此时无法表示出0x0~0xff之间255种颜色,所以16位位图中的象素点数据还是直接的颜色索引值,并非直接RGB颜色值,但此时并不须要用调色板来辅助转化此索引值为RGB颜色值,而是通过一个转换算法,不过这样会对速度产生影响,没有使用调色板快,不采用调色板是因为需要太多调色板表项。
[注意:关于位图文件,这时就不多介绍了,介绍大家下载这篇文章看一下,"BMP档案结构及平滑缩放.doc",也可以自己通过WIN下面自带的画板来分析不同位数的位图,总之只要记住一点,要处理显示一个位图文件,必须知道位图文件大小及单个象素占用位数(即位图位数),然后显示时就是取得每个象素点的RGB值,这个RGB颜色值是根据调色板来取得或者由索引根据一定的转换算法取得或者象素点数据本身就表示RGB值]。
5. LCD_BITSPERPIXEL是UCGUI中定义的一个象素用多少位来表示的宏定义,则可以表示的颜色总数为1L<<LCD_BITSPERPIXEL种,但是RGB分别占多少位,则其可表示的具体哪些颜色是不同的,模拟器中以LCD_FIXEDPALETTE表示R/G/B(正常情况下没有进行R/B交换时)分别所占位数,主要有444(12)、555(15)、565(16)等,一个象素是由RGB三值来描述,RGB三值范围都是0x00-0xf;所以除非每种颜色都用(r|g|b)8位表示,否则不可能把0x00-0xf的值均表示到;另外,对于每一种可显示的颜色模拟器中均以索引值来表示,最基本的画象素的LCDSIM_SetPixelIndex(int x, int y, int Index)中用的是索引值,这个索引值可以通过一种转换转为RGB值。
6. 颜色索引值到RGB值的转换,索引转换成RGB的基本原理,这里我们不讲含调色板的情况------首先,对于RGB三值,由FixedPalette指定其每个位所占的位数,如为444则各4位. FixedPalette中指定能表示的RGB的范围分别为(R,G,B)1111/1111/1111,即16(R)/16(G)/16(B). 则当索引值Index为444时,则是RGB各值索引最大的时候,由此即可分析,R/G/B三值要表示的颜色范围是0x00-0xff,为了让有限的0-16的索引来表示0x00-0xff这些颜色值, 那么当然只能是跳隔着来表示,拿R来说,最好是取值为(17*0, 17*1,17*2,17*3,...17*15=248),如此的话,才能差不多在有限的16个索引值下将0xff种颜色最大程度的涵盖到(比较平均),但实际你可以自己控制索引到RGB颜色值的转换算法,但一定要合理,尽可能涵盖全面。
同理,对于FixedPalette为555时,则R取值最好为(8*0,8*1,8*2,
8*3,...8*31=243)...
同理,对于FixedPalette为565时,则B取值最好为(4*0,4*1,4*2,
4*3,...4*63=252)知道这一点,则对于以下函数的理解就非常容易...
7. 在LCD_BITSPERPIXEL小于等于8时,颜色索引值则无须通过运算来转换,因为它所能表示的颜色数不多于256种,此时我们就会用一个调色板来表示这不多于256种的颜色,取用也非常方便,通过调色板数组就可以取得索引对应的RGB值。对FixedPalette为111时,调色板中只有八种颜色,不须要256个选项, 8种为(0x000000[0],0x0000ff[1],0x00ff00[2],0x00ffff[3], 0xff0000[4],0xff00ff[5],0xffff00[6],0xffffff[7]),即调色板索引为(0-7)其余对于FixedPalette为111时并未用于;初始化调色板时,要先调用LCD_L0_Index2Color(i)将Index转为RGB表示的颜色,再与黑白成比例综合. 这样了就可适应所有实际使用调色板为2项,4项,8项,16项,256项的所有用到调色板的情况。
另外还要提到的是在8位以下的灰度问题,灰度时,R/G/G三值相等,所以最多可以有256级灰度,其中二级灰度/四级灰度/十六级等,都是间隔着取256级灰度中的颜色值,取的方法是平均涵盖。

以上六点是LCD模拟显示器中用到的几点核心要点,要理解LCD模拟显示器,就必然先理解以上几点。
-----实现模拟LCD的内存布局。
[1]、首先是创建一个名为“emWinLCDMap”的大小为0x00400000(4M)的可读可写及其它所有权限的内存映象文件,如果已存在则是打开该内存映象文件, 设其分配所得初始地址为pSMemFix,类型字节。
[2]、pSMemFix+0x20开始,分别存放XSize、YSize、VXSize、VYSize、FixedPalette、BPP、NumColors(分别为LCD水平宽度/竖直高度、LCD虚拟屏水平及竖直大小、调色板模式、单个象素点倍数、可用颜色总数),其计28个字节,这里存入这些信息是为ucguiview查看器使用的,所有的对于查看器要使用的信息均可以在这里存放。
[3]、pSMemFix + 0x100开始,用于存放位图结构信息40(0x28)个字节及1000个字节(256*4)的调色板(最多可用表项为256项)信息,即如下结构:
typedef struct tagBITMAPINFO { // bmi
BITMAPINFOHEADER bmiHeader;
RGBQUAD bmiColors[1];
} BITMAPINFO;
[4]、pSMemFix + 0x1000开始,用作LCD的显存,存放LCD屏幕的象素点,大小总计为4M。
-----实现LCD功能的几个基本函数。
[1]、LCDSIM_SetPixelIndex--------画点.
///////////////////////////////////////////////////////////////////////
//
// 函数名 : LCDSIM_SetPixelIndex
// 功能描述 : 最基本的画点函数...
// 参数 : int x[点x坐标]
// 参数 : int y[点y坐标]
// 参数 : int Index[颜色索引值]
// 返回值 : void
//
///////////////////////////////////////////////////////////////////////
void LCDSIM_SetPixelIndex(int x, int y, int Index)

{
static int preIndex = 0, preLUT = 0, curLUT = 0, curColor = 0;
int pixPos = 0;
char* lptemp = 0;
if(paaPixel == 0) return;
if(BPP <= 8){
pixPos = y * BytesPerLine + x;
lptemp = (char*)paaPixel + pixPos;
*lptemp = Index;
}
else{
// 2005-8-27 13:04:52
lptemp = (char*)pFix + 0x40;
curLUT = *((int*)lptemp);
if(curLUT != preLUT){//改变了调色板时,一定要重新根据索引转颜色...
preLUT = curLUT;
preIndex = -1;
}
if(preIndex != Index){
//转索引为RGB颜色值..
curColor = Convert_Index16IntoIndex32(Index);
}
pixPos = y * BytesPerLine + x * 4;
lptemp = (char*)paaPixel + pixPos;
*(int*)lptemp = curColor;
preIndex = Index;
}
//表明LCD显存数据已有变化,在LCD窗口中的定时器会检测此值以决定是
//否重画窗口,即更新显示LCD屏幕...
ModifyCnt++;
if(pFix != 0){
lptemp = (char*)((char*)pFix + 0x3c);
*((int*)lptemp) = ModifyCnt;
}
}
这个函数是最基本的画点函数,是LCD模拟显示器的最基本与最核心的函数,其余画线、画矩形、画位图等等功能均由以此为基础,画点时指定颜色时是以索引指定的,具体这个索引到RGB颜色的转换分两种情况,前面已经反复的讲过索引到RGB颜色的转换,这里再重复一下Convert_Index16IntoIndex32这个8位以上象素点的索引转颜色,其实在UCGUI中有一个专门的文件中存放的文件都是用于各种情况下的索引转颜色,无论一个象素点占几位,只要用到颜色索引的都必须提供一个索引到颜色之间互转的两个函数,调色板模式下在画点函数中不用再转换,直接从根据索引从调色板中取,但是在实始化调色板时还是用到了索引到RGB的转换来初始化调色板;UCGUI提供的颜色索引到RGB颜色的互转算法只是一种比较适合的算法但并非必须如此,只是一种转换约定方法而已。
这个函数当中有一个小的优化,那就是当与上一次颜色相同时,则不必再进行索引到颜色之间的转换,这一点对于区域性画图很有优势,不过要记住调色板改变后就必须重新进行索引与颜色间的转换。
􀂾 [paaPixel]-------------LCD显存起始地址。
􀂾 [BytesPerLine]---------LCD显示屏一行象素之字节数。
􀂾 [ModifyCnt]------------表示LCD显示是否有更新,每画一个点加1。
[2]、LCDSIM_FillRect--------矩形填充.
//填充一个矩形...
//#define FASTTING 1 //定义是否加速,使用串传送,注意移值性问题...
void LCDSIM_FillRect(int x0, int y0, int x1, int y1, int Index)
{
int step = 1, color = 0;
int line = 0, comlum = 0;
char* lptemp = 0;
if(BPP == 0) return;
if(x1 > XSize) x1 = XSize - 1;
if(y1 > YSize) y1 = YSize - 1;
if(x0 < 0) x1 = 1;
if(y1 < 0) y1 = 1;
if(paaPixel == 0) return;
if(BPP > 8){
step = 4;
color = Convert_Index16IntoIndex32(Index);
}
else{
color = Index;
}
comlum = (x1 - x0);
lptemp = (char*)paaPixel + BytesPerLine * y0 + x0 * step;
//矩形宽度是否为整行...
#ifdef FASTTING
if(comlum == XSize){
comlum = comlum*(y1-y0);
_asm{
mov eax, color

mov ecx, comlum
mov edi, lptemp
}
if(step == 1) __asm rep stosb
else __asm rep stosd
return;
}
#endif
// 2005-8-27 14:35:38 使用串传送指令提高速度...
for(line = y0; line < (y1 - y0); line++){
#ifdef FASTTING
_asm{
mov eax, color
mov ecx, comlum
mov edi, lptemp
}
if(step == 1) __asm rep stosb
else __asm rep stosd
lptemp += BytesPerLine;
#else
// 2005-8-27 14:54:02
for(comlum = x0; comlum < (x1 - x0); comlum++){
lptemp = (char*)paaPixel + BytesPerLine * line + comlum * step;
if(step == 4) *((int*)lptemp) = color;
else *lptemp = color;
}
#endif
}
}
这个函数进行了优化处理,在画矩形时因为是块填充,把以只须进行一次索引到颜色的转换,而且在行填充当中,也是采用串传送,一次填充四字节,如果是全屏填充,那么可以采用一串传送完成任务,传送数据时是直接传送到显存中,矩形填充的效率很重要,调用频率比较高,一定要注意优化,而且在真正的硬件应用当中,也必须进行类似于此的专门的优化处理,这样会大大提高效率。
[3]、Convert_Index16IntoIndex32----转换象素位数大于8位时的颜色索引为RGB颜色值。
当单个象素位数 LCD_BITSPERPIXEL 不大于8位时,直接写索引值到显存中(因为后面采用的位图显示函数会根据指定调色板来显示这些象素点,内部会包含索引到RGB颜色的转换);大于8位时,必须将索引值转换为RGB颜色值后再写入。 其实在我们这个模拟LCD显示屏当中,我们往其中画点的过程就是构造位图数据的过程,但没有BMP文件头、位图信息头,在BMP位图文件中,这些都辅助用于解析显示位图用的,我们这里的LCD调色板是放在另外一块内存中,并已经初始化; 当构造好位图数据后,我们调用WIN的API SetDIBitsToDevice或StretchDIBits来将位图画到LCD窗口中,下面说明一下这两个API:
􀂾 [SetDIBitsToDevice]----是将设备无关位图中的颜色数据画到与指定的设备场景相关的设备上的指定矩形中,这个函数不会进行缩放,即原矩形与目标矩形大小相同,它有一个参数为位图数据,还有一个参数为位图信息结构及调色板(BITMAPINFO),8位及其以下位图必须指定调色板,其余为指定源矩形与目标矩形及设备场景句柄。
􀂾 [StretchDIBits]--------除完成以上功能还会对位图进行缩放显示,此种情况即为模拟器中LCD_YMAG、LCD_XMAG不为1的情况,即放大显示。
调用这两个函数之前必须先调用SetStretchBltMode位图映射模式。
[4]、LCDSIM_GetPixelColor----取点RGB颜色值
int LCDSIM_GetPixelColor(int x, int y)
{
int Color = 0;
if(paaPixel == 0) return Color;
if(BPP > 8){
//直接在显示内存中取,其值即为该点RGB值.
Color = *((int*)paaPixel + y * BytesPerLine + x * 4);
}
else if(BPP <= 8){
Color = *((char*)paaPixel + BytesPerLine * y + x * 4);
Color = LCDSIM_Index2Color(LCDSIM_GetPixelIndex(x,y));
}
return Color;
}
获取指定象素点的RGB颜色值,当LCD_BITSPERPIXEL大于8时,此点RGB直接在LCD显存中取即是;当不大于8时,则须根据从LCD显存中取回的索引值再取调色板中RGB值,即下面的函数:转换索引为颜色值。
[5]、LCDSIM_Index2Color------根据索引转换成RGB值。
int LCDSIM_Index2Color(int Index)
{

int getColor = 0;
if(BPP == 0) return getColor;
else if(BPP > 8){
getColor = Convert_Index16IntoIndex32(Index);
}
else if(BPP <= 8){
getColor = *((char*)pBitmapInfo + 0x28 + Index*4) << 16 | *((char*)pBitmapInfo + 0x29 + Index*4) << 8 | *((char*)pBitmapInfo + 0x2a + Index*4);
}
return getColor;
}
􀂾 [pBitmapInfo]-----------位图信息起始地址,其中位图结构大小0x28.
􀂾 [pBitmapInfo+0x28]---调色板起始址址。
[6]、Convert_Index16IntoIndex32----转换LCD_BITSPERPIXEL大于8时的颜色索引值为RGB颜色值
LCD_COLOR Convert_Index16IntoIndex32(int Index)
{
// 2005-6-4 12:33:06
//此一句可以底上下面的switch N句,编译器将由FixedPalette预定义值来选定
//具体的RGB颜色与颜色索引间的转换函数。
return INDEX2COLOR(Index);
/* LCD_COLOR convertColor = 0;
switch(FixedPalette){
case 444:
convertColor = LCD_Index2Color_444(Index);
break;
case 555:
convertColor = LCD_Index2Color_555(Index);
break;
case -555:
convertColor = LCD_Index2Color_M555(Index);
break;
case 565:
convertColor = LCD_Index2Color_565(Index);
break;
case -565:
convertColor = LCD_Index2Color_M555(Index);
break;
case -444: //无此项转换...

break;
}
return convertColor;
*/
}
在这个函数中,具体的转换的功能是由GUI\ConvertColor目录下的几个文件中提供的,具体参看UCGUI源码。
[7]、LCDSIM_SetLUTEntry------设置调色板信息。
void LCDSIM_SetLUTEntry(U8 Pos, LCD_COLOR color)
{
char* lptemp = 0;
if(BPP == 0) return;
color=FilterColor(color,LCDSIM_aLCDColorBlack[0],LCDSIM_aLCDColorWhite[0]);
lptemp = (char*)pBitmapInfo + 0x28 + Pos * 4;
*(char*)lptemp++ = (color & 0xff0000) >> 16;
*(char*)lptemp++ = (color & 0xff00) >> 8;
*(char*)lptemp = color & 0xff;
ModifyCnt++;
LUT_ModifyCnt++;
if(pFix != 0){
lptemp = (char*)pFix + 0x3c;
*lptemp = ModifyCnt;
lptemp = (char*)pFix + 0x40;
*((int*)lptemp) = LUT_ModifyCnt;
}
}
􀂾 [LUT_ModifyCnt]----记录调色信息的更改次数。
􀂾 [pBitmapInfo]---------位图信息起始地址。
􀂾 [FilterColor]-----------此函数负责将指定颜色值转换为在背景色与前景色之间的值。转换的方法是(colorWhite & 0xff - colorBlack &0xff) * (color & 0xff) / 0xff; colorWhite为前景色,colorBlack为背景色,取相应的R/G/B位,用前景色减去背景色之值再乘以color的对应R/G/B的值, 所得值再除以255得商即为调整后颜色值,这样做目的是将指定颜色值调整为前景色与背景色之间。
-----模拟器的输入模拟在UCGUI当中有专门的接收输入的接口,有MOUSE、KEY、TOUCH的,分别为GUI_MOUSE_StoreState、GUI_StoreKeyMsg、GUI_TOUCH_StoreState这三个函数,三UCGUI中关于这些消息的处理都比较简易,并没有什么队列,都是接收一个处理一个,这对于简单的嵌入式应用来说已经足够,可以减少内存占用,但有可能造成消息的丢失,MOUSE及KEY消息均来自于LCD窗口的窗口消息处理函数中。
[MOUSE]
[1]、LCDSIM_SetMouseState--------传送MOUSE及TOUCH(触摸屏)消息到UCGUI。
void NotifyMouseState(LCD_tMouseState mouseState)
{
if(mouseState.KeyStat == 0){
GUI_TOUCH_StoreState(-1, -1);
}
else{
GUI_TOUCH_StoreState(mouseState.x, mouseState.y);
}
GUI_MOUSE_StoreState((const GUI_PID_STATE*)&mouseState);
}
MOUSE消息处理中,包括WM_MOUSEMOVE、WM_LBUTTONDOWN、WM_LBUTTONUP这三种,NotifyMouseState的功能就是内存消息至ucgui.
[2]、LCDSIM_GetMouseState--------获取MOUSE消息。
[KEY]
[1]、HandleKeyEvents-------------传送KEY消息到UCGUI。
void HandleKeyEvents(UINT message, WPARAM wParam)
{
int key = 0, keyCount = 0;
switch(message){
case WM_KEYDOWN:
key = VirtKey2Key(wParam);
if(key == 0) key = Keydown2ASCII(wParam);
keyCount = 1;
break;
case WM_KEYUP:
key = VirtKey2Key(wParam);
keyCount = 0;
break;
}

if(key != 0) GUI_StoreKeyMsg(key, keyCount);
}
􀂾 [VirtKey2Key]----主要处理一些特殊键,如SHIFT、DELTE、BACKSPACE、INSERT、CRTL、ENTER、方向键等。
􀂾 [Keydown2ASCII]--将键盘虚拟码转换为ASCII码,大小写区别的,在键盘处理当中,如上所说的特殊键是有特别用途的,UCGUI中已重定义这些特殊键所对应的KEY值,可以在GUI.h当中查找GUI_KEY_UP来找到这些特殊键在UCGUI中的KEY值。在传送键盘消息时,这里只在WM_KEYDOWN时处理普通键,WM_KEYUP中并未处理,WM_KEYUP只处理了特殊键。其它人可以根据自己人须求要改写这个传送键盘消息到UCGUI的函数。
HandleKeyEvents在LCD的窗口消息函数中,当LCD窗口有键盘消息时,即传送至UCGUI内部,以驱动UCGUI的键盘处理。
三、拔去见日-----UCGUISim模拟器的模块划分。
在上面介如了模拟器的基本原理,差不多将核心的东西都说出来了,这时简要的说明一下模拟器的几个构成模块。
􀂾 emWin.c----------创建UCGUISim模器主窗口及LCD显示窗口,处理KEY、MOUSE消息传送,开启、暂停UCGUI程序,输出LOG等。
􀂾 LCDSIM.c--------实现模拟LCD显示屏。
􀂾 Branding.c--------显示版权。
􀂾 GUI_X_SIM.c---实现UCGUI的临界代码锁及实际的LOG输出及延时功能。
具体可以能见源码,这里就不就代码作详细解说了。

 

点击此处查看原文 >>

系统分类: 嵌入式   |    用户分类:    |    来源: 无分类

评论(0) | 阅读(2303)
发表于:2006-11-26 23:10:16
标签:讨论  

0

时间换空间、空间换时间

一个永远争论不休的问题

特别的是在嵌入式系统里面,更是永远的话题。

到底用什么换什么,不好说,就事论事把。

点击此处查看原文 >>

系统分类: 单片机   |    用户分类: 无分类    |    来源: 无分类

评论(2) | 阅读(1020)
发表于:2006-11-18 20:58:37
标签:UART调试  

2

一分钱耽误了1天时间

这几天调试遇到了见鬼的问题

jtag调试程序正常,一脱机运行就完蛋。

我的程序是将两个UART的内容互相倒来倒去

UART0收到的转发到UART1,UART1收到的转发到UART0。

用Jtag调试很正常,脱机运行就完蛋了。百思不得其解。在检查了晶振..........................................一系列东西后,非常郁闷。

后来突然想到在用p89c51ra2的时候也遇到过类似问题,在仿真器上正常。弄到片子里就完蛋。结果是将那个用来isp的管教接地了,永远处于isp模式。

经过检查果然如此。加个上拉电阻问题解决。

一分钱耽误了1天时间。

点击此处查看原文 >>

系统分类: 嵌入式   |    用户分类:    |    来源: 无分类

评论(2) | 阅读(1043)
发表于:2006-11-18 20:55:39
标签:485  

2

关于485总线的接地问题

 在485总线的应用中,如果简单地只用一对双绞线将各个接口的“A”、“B”端连接起来,而忽略了信号地的互连,这种连接方法在许多场合是能正常工作的,但却埋下了很大的隐患,这有下面二个原因:
1.共模干扰问题:485总线虽采用差分方式传输信号,似乎并不需要相对于某个参照点来判定信号,系统只需检测两线之间的电位差就可以了,但有人往往忽视了任何485接口IC总有一定的共模电压承受范围,如一般的-7~+12V,只有满足这个条件,整个网络才能正常工作。当网络线路中共模电压超出此范围时就会影响通信的稳定可靠,甚至损坏接口。比如当发送器A向接收器B发送数据时,发送器A的输出共模电压为VOS,由于两个系统具有各自独立的接地系统,存在着地电位差VGPD,那么接收器输入端的共模电压VCM就会达到VCM=VOS+VGPD。RS-485标准规定VOS≤3V,但VGPD可能会有很大幅度如十几伏甚至上百伏,且可能伴有强干扰(快速波动),致使接收器共模输入超出正常范围并在传输线路上产生干扰电流,轻则影响正常通信,重则损坏通信接口电路。
2.EMI问题:发送器输出信号中的共模部分需要一个返回通路,如没有一个低阻的返回通道(信号地),就会以辐射的形式返回源端(注意485的交流模型),整个总线就会像一个巨大的天线向外辐射电磁波。
    由于上述原因,尽管485采用了差分平衡传输方式,但对整个485网络而言,必须有一条低阻的信号地将各个接口的工作地连接起来,使共模干扰电压VGPD被短路。这条信号地可以是额外的一条线(非屏蔽双绞线),或者是屏蔽双绞线的屏蔽层。这是最通常的接地方法。
    值得注意的是,这种做法仅对高阻型共模干扰有效,由于干扰源内阻大,短接后不会形成很大的接地环路电流,对于通信不会有很大影响。若共模干扰源内阻较低时,则会在接地线上形成较大的环路电流,影响正常通信,这时应采用下述解决方案:
(1) 采用浮地技术,隔断接地环路。这是较常用也是十分有效的一种方法,当共模干扰内阻很小时上述方法已不能奏效,此时可以考虑将引入干扰的节点(例如处于恶劣的工作环境的现场设备)浮置起来(也就是系统的电路地与机壳或大地隔离),这样就隔断了接地环路,不会形成很大的环路电流。
(2) 采用隔离接口。有些情况下,出于安全或其它方面的考虑,电路地必须与机壳或大地相连,不能悬浮,这时可以采用隔离接口来隔断接地回路,但是仍然应该有一条地线将隔离侧的公共端与其它接口的工作地相连。

点击此处查看原文 >>

系统分类: 嵌入式   |    用户分类:    |    来源: 无分类

评论(0) | 阅读(1132)
发表于:2006-11-18 20:54:26
标签:485  

1

提高RS-485总线可靠性的几种方法及常见故障处理

在MCU之间中长距离通信的诸多方案中,RS-485因硬件设计简单、控制方便、成本低廉等优点广泛应用于工厂自动化、工业控制、小区监控、水利自动报测等领域。但RS-485总线在抗干扰、自适应、通信效率等方面仍存在缺陷,一些细节的处理不当常会导致通信失败甚至系统瘫痪等故障,因此提高RS-485总线的运行可靠性至关重要。

一、RS-485接口电路的硬件设计

1、总线匹配

  总线匹配有两种方法,一种是加匹配电阻,如图1a所示。位于总线两端的差分端口VA与VB之间应跨接120Ω匹配电阻,以减少由于不匹配而引起的反射、吸收噪声,有效地抑制了噪声干扰。但匹配电阻要消耗较大电流,不适用于功耗限制严格的系统。

点击看大图

  另外一种比较省电的匹配方案是RC 匹配(图2 )利用一只电容C 隔断直流成分,可以节省大部分功率,但电容C的取值是个难点,需要在功耗和匹配质量间进行折衷。除上述两种外还有一种采用二极管的匹配方案(图3),这种方案虽未实现真正的匹配,但它利用二极管的钳位作用,迅速削弱反射信号达到改善信号质量的目的,节能效果显著。

2、RO及DI端配置上拉电阻

  异步通信数据以字节的方式传送,在每一个字节传送之前,先要通过一个低电平起始位实现握手。为防止干扰信号误触发RO(接收器输出)产生负跳变,使接收端MCU进入接收状态,建议RO外接10kΩ上拉电阻。

3、保证系统上电时的RS-485芯片处于接收输入状态

  对于收发控制端TC建议采用MCU引脚通过反相器进行控制,不宜采用MCU引脚直接进行控制,以防止MCU上电时对总线的干扰,如图4所示。

4、总线隔离

  RS-485总线为并接式二线制接口,一旦有一只芯片故障就可能将总线“拉死”,因此对其二线口VA、VB与总线之间应加以隔离。通常在VA、VB与总线之间各串接一只4~10Ω的PTC电阻,同时与地之间各跨接5V的TVS二极管,以消除线路浪涌干扰。如没有PTC电阻和TVS二极管,可用普通电阻和稳压管代替。

5、合理选用芯片

  例如,对外置设备为防止强电磁(雷电)冲击,建议选用TI的75LBC184等防雷击芯片,对节点数要求较多的可选用SIPEX的SP485R。

二、RS-485网络配置

1、网络节点数

  网络节点数与所选RS-485芯片驱动能力和接收器的输入阻抗有关,如75LBC184标称最大值为64点,SP485R标称最大值为400点。实际使用时,因线缆长度、线径、网络分布、传输速率不同,实际节点数均达不到理论值。例如75LBC184运用在500m分布的RS-485网络上节点数超过50或速率大于9.6kb/s时,工作可靠性明显下降。通常推荐节点数按RS-485芯片最大值的70%选取,传输速率在1200~9600b/s之间选取。通信距离1km以内,从通信效率、节点数、通信距离等综合考虑选用4800b/s最佳。通信距离1km以上时,应考虑通过增加中继模块或降低速率的方法提高数据传输可靠性。

2、节点与主干距离

  理论上讲,RS-485节点与主干之间距离(T头,也称引出线)越短越好。T头小于10m的节点采用T型,连接对网络匹配并无太大影响,可放心使用,但对于节点间距非常小(小于1m,如LED模块组合屏)应采用星型连接,若采用T型或串珠型连接就不能正常工作。RS-485是一种半双工结构通信总线,大多用于一对多点的通信系统,因此主机(PC)应置于一端,不要置于中间而形成主干的T型分布。

三、提高RS-485通信效率

  RS-485通常应用于一对多点的主从应答式通信系统中,相对于RS-232等全双工总线效率低了许多,因此选用合适的通信协议及控制方式非常重要。

1总线稳态控制(握手信号)

  大多数使用者选择在数据发送前1ms将收发控制端TC置成高电平,使总线进入稳定的发送状态后才发送数据;数据发送完毕再延迟1ms后置TC端成低电平,使可靠发送完毕后才转入接收状态。据笔者使用TC端的延时有4个机器周期已满足要求;

2、为保证数据传输质量,对每个字节进行校验的同时,应尽量减少特征字和校验字

  惯用的数据包格式由引导码、长度码、地址码、命令码、数据、校验码、尾码组成,每个数据包长度达20~30字节。在RS-485系统中这样的协议不太简练。推荐用户使用MODBUS协议,该协议已广泛应用于水利、水文、电力等行业设备及系统的国际标准中。

 四、RS-485接口电路的电源、接地

  对于由MCU结合RS-485微系统组建的测控网络,应优先采用各微系统独立供电方案,最好不要采用一台大电源给微系统并联供电,同时电源线(交直流)不能与RS-485信号线共用同一股多芯电缆。RS-485信号线宜选用截面积0.75mm2以上双绞线而不是平直线。对于每个小容量直流电源选用线性电源LM7805比选用开关电源更合适。当然应注意LM7805的保护:

1、LM7805输入端与地应跨接220~1000μF电解电容;

2、LM7805输入端与输出端反接1N4007二极管;

3、LM7805输出端与地应跨接470~1000μF电解电容和104pF独石电容并反接1N4007二极管;

4、输入电压以8~10V为佳,最大允许范围为6.5~24V。可选用TI的PT5100替代LM7805,以实现9~38V的超宽电压输入。

  五、光电隔离

  在某些工业控制领域,由于现场情况十分复杂,各个节点之间存在很高的共模电压。虽然RS-485接口采用的是差分传输方式,具有一定的抗共模干扰的能力,但当共模电压超过RS-485接收器的极限接收电压,即大于+12V或小于-7V时,接收器就再也无法正常工作了,严重时甚至会烧毁芯片和仪器设备。

  解决此类问题的方法是通过DC-DC将系统电源和RS-485收发器的电源隔离;通过光耦将信号隔离,彻底消除共模电压的影响。实现此方案的途径可分为:

1、用光耦、带隔离的DC-DC、RS-485芯片构筑电路;

2、使用二次集成芯片,如PS1480、MAX1480等。

 六、RS-485系统的常见故障及处理方法

  RS-485是一种低成本、易操作的通信系统,但是稳定性弱同时相互牵制性强,通常有一个节点出现故障会导致系统整体或局部的瘫痪,而且又难以判断。故向读者介绍一些维护RS-485的常用方法。

1、若出现系统完全瘫痪,大多因为某节点芯片的VA、VB对电源击穿,使用万用表测VA、VB间差模电压为零,而对地的共模电压大于3V,此时可通过测共模电压大小来排查,共模电压越大说明离故障点越近,反之越远;

2、总线连续几个节点不能正常工作。一般是由其中的一个节点故障导致的。一个节点故障会导致邻近的2~3个节点(一般为后续)无法通信,因此将其逐一与总线脱离,如某节点脱离后总线能恢复正常,说明该节点故障;

3、集中供电的RS-485系统在上电时常常出现部分节点不正常,但每次又不完全一样。这是由于对RS-485的收发控制端TC设计不合理,造成微系统上电时节点收发状态混乱从而导致总线堵塞。改进的方法是将各微系统加装电源开关然后分别上电;

4、系统基本正常但偶尔会出现通信失败。一般是由于网络施工不合理导致系统可靠性处于临界状态,最好改变走线或增加中继模块。应急方法之一是将出现失败的节点更换成性能更优异的芯片;

5、因MCU故障导致TC端处于长发状态而将总线拉死一片。提醒读者不要忘记对TC端的检查。尽管RS-485规定差模电压大于200mV即能正常工作。但实际测量:一个运行良好的系统其差模电压一般在1.2V左右(因网络分布、速率的差异有可能使差模电压在0.8~1.5V范围内)。

点击此处查看原文 >>

系统分类: 嵌入式   |    用户分类:    |    来源: 无分类

评论(0) | 阅读(972)
发表于:2006-11-18 20:52:53
标签:杂感  

0

开除猪八戒

猪八戒、孙悟空、沙河尚中开除一个,你开谁?

本人首选猪八戒。

就为它把师傅的西瓜都吃了

点击此处查看原文 >>

系统分类: 生活点滴   |    用户分类: 无分类    |    来源: 无分类

评论(0) | 阅读(653)
发表于:2006-11-13 0:31:43
标签:GUI  

2

uC/GUI3.32应用笔记连载之七

uC/GUI NIOSII移植之Window Manager(窗口管理器)

昨天太晚了没有时间更新了。今天继续。
今天来看看Window manager,窗口管理器。
先来看一个小例子。

#define MSG_CHANGE_TEXT WM_USER+0

#ifndef NULL
#define NULL 0
#endif

typedef struct {
  int x;
  int y;
  int xHere, yHere;
  const GUI_BITMAP* pBitmap;
} tDrawContext;


extern GUI_FLASH const GUI_FONT GUI_FontHZ_FangSong_GB2312_14;
extern GUI_FLASH const GUI_FONT GUI_FontHZ_Times_New_Roman_14;
extern GUI_FLASH const GUI_FONT GUI_Font8x10_ASCII;
extern GUI_FLASH const GUI_FONT GUI_Font32_ASCII;
extern GUI_FLASH const GUI_FONT GUI_Font32B_ASCII;
extern const GUI_BITMAP bm3;
extern const GUI_LOGPALETTE Pal3;
extern const unsigned char ac3[];
extern const GUI_BITMAP bm3_compressed_with_palette;
extern const GUI_BITMAP bmmap;                                        /*以上全部是外部变量声明,有位图,有字体*/

static WM_CALLBACK* _cbBkWindowOld;                             /*声明背景窗口的回调函数(Call back function),后面会具体说明这个Call back是干什么的*/
static char _acInfoText[40];                                                 /*要在背景窗口中显示的文字*/

static WM_HWIN _hWindow2;                                                /*窗口2*/

static void        _cbWindow2(WM_MESSAGE* pMsg) {             /*窗口2的call back*/
    int x, y;
    switch (pMsg->MsgId) {                                                      /*pMsg这个参数用来告诉这个回调函数发生了什么事件的!这里列出了系统已经定义好的所有的事件*/
        case WM_CREATE:
            /*to do add code here*/
        break;

        case WM_DELETE:
             /*to do add code here*/
        break;

        case WM_HIDE:
             /*to do add code here*/
        break;

        case WM_MOVE:
             /*to do add code here*/
        break;

        case WM_NOTIFY_PARENT:
             /*to do add code here*/
        break;

        case WM_PAINT:                               /*这个Paint事件,准确地翻译我不知道,但他的功能主要就是自动重绘窗口,这十分重要,有了自动重绘你只要在这个CB里面告诉GUI你想绘什么东西就行了,GUI会自动在适当的时机重绘*/
            GUI_SetBkColor(GUI_RED);
            GUI_Clear();
            GUI_SetColor(GUI_WHITE);
            GUI_SetFont(&GUI_Font24_ASCII);
            x =        WM_GetWindowSizeX(pMsg->hWin);
            y =        WM_GetWindowSizeY(pMsg->hWin);
            GUI_DispStringHCenterAt("Test Window No.2", x / 2, 1); /*在这里面你可以调用上面贴子中所有出现过的内容,但不是在整个屏幕上绘图了,而是在这个窗口中绘图*/
        break;

        case WM_SHOW:
             /*to do add code here*/
        break;

        case WM_SIZE:
             /*to do add code here*/
        break;

        case WM_TOUCH:
             /*to do add code here*/
        break;

        default:
            WM_DefaultProc(pMsg);   /*在回调函数中还有很多其他系统定义的事件,例如WM_SIZE你可以在发生了该事件后改变窗口的内容。有了这个回调函数就可以实现很多的功能*/
    }
}

static void _ChangeInfoText(char* pStr) {      /*将要在背景窗口中显示的文字放到一个全局变量里面去,再由Bk窗口的回调函数自动刷新*/
  WM_MESSAGE Message;
  Message.MsgId  = MSG_CHANGE_TEXT;
  Message.Data.p = pStr;
  WM_SendMessage(WM_HBKWIN, &Message);  /*发送一个消息告诉Bk的cb发生了文字改变的事件*/
  WM_InvalidateWindow(WM_HBKWIN);
}

static void _cbBkWindow(WM_MESSAGE* pMsg) {  /*Bk窗口的CB函数,在这里可以更清楚地看到cb是怎么工作的*/
  switch (pMsg->MsgId) {
  case MSG_CHANGE_TEXT:                     /*发生了MSG change设个事件,将待显示的文本拷贝的变量中去*/
    strcpy(_acInfoText, pMsg->Data.p);  /*这里有点小小的编程技巧,在这里没有使用break,而是直接执行下面的case里面的内容这样就直接刷新的Bk窗口*/
  case WM_PAINT:
    GUI_SetBkColor(GUI_CYAN);
    GUI_Clear();
    GUI_SetColor(GUI_RED);
    GUI_SetFont(&GUI_Font24_ASCII);
    GUI_DispStringHCenterAt("WindowManager - Sample", 160, 5);
    GUI_SetFont(&GUI_FontHZ_FangSong_GB2312_14);
    GUI_DispStringAt(_acInfoText, 5, 40);  /*在这里就可以看到桌面是怎么来的了,这里的图片和lcd的大小不一样,如果一样就是桌面了*/
    GUI_DrawBitmap(&bm3, 50, 60);
    break;                                                      /*这里就看到了Bk窗口里面发生的一切*/
  default:
    WM_DefaultProc(pMsg);
  }
}
/*
  *******************************************************************
  *
  *              main()
  *
  *******************************************************************
*/

void MainTask(void) {
    unsigned int i;
    WM_HWIN hClient;
    GUI_Init();

    _cbBkWindowOld = WM_SetCallback(WM_HBKWIN, _cbBkWindow);
    GUI_Delay(1000);
    _ChangeInfoText("uC/GUI演示程序!");


    _hWindow2 = WM_CreateWindow(330, 130, 300, 300, WM_CF_SHOW | WM_CF_FGND, _cbWindow2, 0);
    GUI_Delay(2000);
}

点击看大图

这段代码就比较复杂了,可以看出来想把GUI用好不是一件容易的事情!

那如果不使用call back机制呢。一样可以实现相同的功能。但是屏幕的刷新可就是你的责任了。
用户就需要调用相关的函数来实现屏幕内容的刷新。
可能有很多人还不理解这个cb机制。我也说不太清。

在前几天的所有例子中都是用户控制刷屏的。就直在调用GUI_Delay()的时候内容才会改变!
单步运行就知道了。

下面看看队窗口的操作
    WM_ResizeWindow(_hWindow2, -100, -100);
    WM_MoveTo(_hWindow2, 50, 100);
执行这两句,窗口就变了样了。

点击看大图

当然了你还可以一次让它移动1个像素,这样看上去就是平滑移动的!

窗口管理还有很多的内容,以后慢慢再贴出来。

这些东西决的原创,欢迎大家提意见,随意转载,但请注明出处和作者。

点击此处查看原文 >>

系统分类: 嵌入式   |    用户分类:    |    来源: 无分类

评论(1) | 阅读(1231)
总共 , 当前 /,2下一页