MiniGUI串口.docx
《MiniGUI串口.docx》由会员分享,可在线阅读,更多相关《MiniGUI串口.docx(42页珍藏版)》请在冰豆网上搜索。
MiniGUI串口
目前,嵌入式Linux设备已广泛应用到计算机、通信和工业控制等领域,负责各种数据的处理和存储,并进行控制决策。
MiniGUI是一种面向嵌入式系统和实时系统的图形用户界面支持系统。
在MiniGUI环境下实现多个RS-232串口通信,多串口实时协同工作,可满足许多工控和通信场合需要。
该文实现平台为扩展了5个串口的IntelPXA255Sitsang的嵌入式硬件平台以及基于嵌入式Linux2.4.19的软件平台,采用MiniGUI作为图形用户界面,在MiniGUI的两个运行模式下设计并实现了多个串口的实时双工通信。
1 MiniGUI及其两个运行模式
目前,在Linux之上进行(实时)嵌入式系统开发,一般选择如下几种GUI系统:
紧缩的XWindow系统、MiniGUI、MicroWindows、OpenGUI、QT/Embedded等。
该文采用MiniGUI1.3.0版本作为系统GUI,应用程序建立在图形界面和嵌入式Linux内核之上。
MiniGUI可以编译成两个截然不同的运行模式:
一个是MiniGUI-Threads,一个是MiniGUI-Lite。
MiniGUI-Threads具有非常高的实时性,采用了线程机制。
MiniGUI-Threads的应用程序,可以具有多个线程,每个线程有不同的功能和任务,可以在不同的线程中建立多个窗口,但所有的窗口在一个进程或者地址空间中运行。
不同的线程之间可通过MiniGUI的消息传递机制进行事件传送和同步。
但显然,这种基于线程的结构也导致了系统整体的脆弱,如果某个线程因为非法的数据访问而终止运行,则整个进程都将受到影响。
不过,这种体系结构对实时控制系统等时间关键的系统来讲,还是非常适合的。
为了改变Threads版本体系上的脆弱,MiniGUI-Lite版本使用进程机制。
和MiniGUI-Threads相反,MiniGUI-Lite上的每个程序是单独的进程,每个进程也可以建立多个窗口。
在Lite版本中,可以同时运行多个MiniGUI应用程序。
首先需要启动一个服务器程序mginit,然后启动其他作为客户端运行的MiniGUI应用程序。
如果因为某种原因客户终止,服务器不受影响,可以继续运行。
2 MiniGUI-Threads下多线程串口通信
串口通信程序要做到实时性高,吞吐量大,程序的输出与输入是两个需要并发执行的操作,因此在MiniGUI-Threads下采用多线程技术。
在多串口的MiniGUI环境下,通信程序至少应具备如下两个线程:
主线程和串口*线程。
主线程是串口通信程序的管理者,用来进行人机交互的操作、部分串口操作和数据处理及协调好各线程运行;串口*线程的职责是实时*各个串口的状态,一旦发生预定的事件,需要判断是哪个串口有数据到来,然后进行一定的数据处理,并立即向主线程发送相应消息,请求主线程对其进行相应处理。
主线程在接到串口*线程发送来的消息后,立即调用相应的过程函数进行处理。
串口通信程序可以在MiniGUIMain()中通过CreateThreadForMainWindow函数创建了两个并发的线程,定义线程的入口函数地址并返回线程标识符。
在主线程中配置termios结构完成对串口的初始化操作,创建主窗口,建立控键并进入消息循环。
*线程和主线程同时启动,此后串口*线程在后台对各个串口进行实时*,在*到预定事件时,立即通过SendMessage向主线程发送相应的消息。
与此同时,主线程对*线程发送来的消息作出相应的处理,读取串口数据到缓冲区。
主线程在处理完该消息后,串口*线程继续执行后面的程序代码,对串口继续*。
主线程还可以同时进行其他工作,比如接收或处理键盘、鼠标一类的消息,数据显示和响应控件事件等。
MiniGUI-Threads下的SendMessage的传递机制,可以用来发送“同步消息”。
如果发送消息的线程和接收消息的线程不是同一个线程,发送消息的线程将阻塞并等待另一个线程的处理结果,然后继续运行;如果发送消息的线程和接收消息的线程是同一个线程,则与MiniGUI-Lite的SendMessage一样,直接调用接收消息窗口的过程函数。
MiniGUI定义了MSG_USER宏,用户能够自定义消息,并利用自定义消息传递数据。
应用程序可如下定义自己的消息:
#defineMSG_MYMESSAGE1(MSG_USER+1);
#defineMSG_MYMESSAGE2(MSG_USER+2)。
这种方式能有效防止数据的堵塞,避免线程出现死锁等情况的出现。
MiniGUI-Threads下串口通信程序过程如图1所示。
图1 串口通信程序框图
3 MiniGUI-Lite下多进程串口通信
Lite版本是支持客户服务器(C/S)方式的多进程系统,在运行过程中有且仅有一个服务器程序在运行,它的全局变量mgServer被设为TRUE,其余的MiniGUI应用程序为客户,mgServer变量被设为FALSE。
各个应用程序分别运行于各自不同的进程空间,如图2所示。
图2 Lite版本下的多进程系统
串口通信程序要在MiniGUI-Lite下做到实时性高,吞吐量大,管理协调,可以参照MiniGUI-Threads下串口通信程序的设计,分别建立服务器程序和客户*程序。
根据MiniGUI-Lite的特点,多串口通信程序首先初始化并启动服务器程序。
MiniGUI-Lite下服务器程序名需要命名为mginit,该程序为客户应用程序准备共享资源,并管理客户应用程序。
首先,需要初始化OnNewDelClient和OnChangeLayer这两个服务器程序特有的全局变量。
这两个变量是两个函数的指针变量,分别用来*来自客户和层的事件。
当客户连接到mginit或者断开与mginit之间的套接字连接时,如果程序设置了OnNewDelClient这个变量,将调用这个变量指向的函数。
同样,当MiniGUI-Lite的层发生变化时,比如有新的客户加入到某个层,或者层中的活动客户发生了变化,如果程序中设置了OnChangeLayer这个变量,则会调用这个变量指向的函数。
通过调用这些函数,可以得到客户标号或者层的指针、客户指针,mginit程序就可以方便地访问MiniGUI函数库中的内部数据结构,获得当前的客户以及当前层的所有信息,从而管理客户程序。
接着调用ServerStartup函数启动mginit的服务器功能,这个函数所做的工作比较简单,就是建立*客户连接的套接字(/var/tmp/minigui)并返回。
最后调用SetDesktopRect设定屏幕上由服务器独占的矩形区域,客户程序不能使用这块矩形区域。
设定之后,客户程序就只能在这个独占的区域以外进行绘制。
服务器初始化完毕后,用exec_app()函数启动客户串口*程序,完成服务器和客户程序的建立。
为了实现客户和服务器之间的通讯,MiniGUI-Lite通过经过封装的UNIXDomainSocket处理函数在服务器和*程序之间进行数据传递。
服务器可以使用serv_listen()函数建立一个*套接字,并返回套接字文件描述符:
#defineLISTEN_SOCKET"/var/tmp/mysocket"
staticintlisten_fd;
BOOLlisten_socket(HWNDhwnd)
{
if((listen_fd=serv_listen(LISTEN_SOCKET))<0)
returnFALSE;
eturnRegisterListenFD(fd,POLL_IN,hwnd,NULL);/*在系统中注册*文件描述符,在被*的文件描述符上发生指定事件时,向某窗口发送MSG_FDEVENT消息*/
}
当服务器接收到来自客户的连接请求时,服务器hwnd窗口将接收到MSG_FDEVENT消息,这时,服务器可接受该连接请求:
intMyWndProc(HWNDhwnd,intmessage,WPARAMwParam,LPARAMlParam)
{
switch(message){
caseMSG_FDEVENT:
if(LOWORD(wParam)==listen_fd){/*来自*套接字*/
pid_tpid;uid_tuid;intconn_fd;
conn_fd=serv_accept(listen_fd,&pid,&uid);/*服务器调用serv_accept()函数接受来自客户的连接请求*/
if(conn_fd>=0){
RegisterListenFD(conn_fd,POLL_IN,hwnd,NULL);}
}
else{ intfd=LOWORD(wParam);/*来自已连接套接字*/
sock_read_t(fd,...);/*处理来自客户的数据*/
sock_write_t(fd,....);}
break;
}
}
上面的代码中,服务器将连接得到的新文件描述符也注册为*描述符,因此,在MSG_FDEVENT消息的处理中,应该判断导致MSG_FDEVENT消息的文件描述符类型,并适当处理。
在客户端,当需要连接到服务器时,可通过如下代码:
intconn_fd;
if((conn_fd=cli_conn(LISTEN_SOCKET,'b'))>=0){
/*客户调用cli_conn函数连接到服务器*/
sock_write_t(fd,....);/*向服务器发送请求*/
sock_read_t(fd,....);}/*获取来自服务器的处理结果*/
4 两种模式下需要注意的一些差别
4.1 实现串口*程序的差别
串口*程序担当着实时*的任务,*预定义事件。
在MiniGUI-Threads下*线程需要给主线程发送预定义消息;在MiniGUI-Lite下,*程序要完成和服务器的数据交换和通信。
在这里,预定义事件就是多个串口中有一个串口有数据到来时,判断并锁定这个串口进行数据读取。
使用I/O多路复用(I/Omultiplexing)技术可以很好地解决这个问题。
其基本思想是:
先构造一张有关描述符的表,然后调用一个函数,它要到这些描述符中的一个已准备好的进行I/O时才返回。
在返回时,它告诉线程哪一个描述符已准备好可以进行I/O。
使用这种返回值,就可调用相应的I/O函数(一般是read或write),并且确知该函数不会阻塞。
Linux下的系统调用select函数可以执行I/O多路复用。
Select函数原型为:
Intselect(intmaxfdp1,fd_set*readfds,fd_set*writefds,fd_set*exceptfds,Structtimeval*tvptr);
在MiniGUI-Threads中,因为每个线程都有自己相应的消息队列,而系统消息队列是由单独运行的desktop线程管理的,所以任何一个应用程序建立的线程都可以长时间阻塞,从而可以使用select系统调用。
在MiniGUI-Lite之上运行的应用程序只有一个消息队列。
应用程序在初始化之后,会建立一个消息循环,然后不停地从这个消息队列当中获得消息并处理,直到接收到MSG_QUIT消息为止。
应用程序的窗口过程在处理消息时,要在处理完消息之后立即返回,以便有机会获得其他的消息并处理。
现在,如果应用程序在处理某个消息时*某个文件描述符而调用select系统调用,就有可能会出现问题———因为select系统调用可能会长时间阻塞,而由MiniGUI-Lite服务器发送给客户的事件得不到及时处理。
这样,消息驱动的方式和select系统调用就难于很好地融合。
在MiniGUI-Lite当中,可以用以下几种方法解决这一问题:
①在调用select系统调用时,传递超时值,保证select系统调用不会长时间阻塞;
②设置定时器,定时器到期时,利用select系统调用查看被*的文件描述符。
如果没有相应的事件发生,则立即返回,否则进行读写操作;
③利用MiniGUI-Lite提供的RegisterListenFD函数在系统中注册*文件描述符,并在被*的文件描述符上发生指定的事件时,向某个窗口发送MSG_FDEVENT消息。
4.2 处理同步问题的差别
在MiniGUI-Threads下,对于共享资源的互斥性同步,可以使用“互斥(mutex)”来解决,它是一种锁或者信号灯,相关宏定义和函数包含在中。
当一个线程调用pthread_mutex_lock()函数锁定某个Mutex后,其它也要锁定Mutex的线程将被阻塞,直至Mutex被释放,从而达到资源的独占。
对于线程按正确顺序执行的顺序同步,正如上面提到的,Sendmessage的消息传递机制起到了“事件同步”的作用。
当一个线程要等待另一个线程的某个事件时,会把自己挂起,直至另一个线程的相应事件发生后把自己唤醒。
而在MiniGUI-Lite中主要是应用程序间的互斥性同步,可以使用Linux下常用的“信号量机制”等方式解决多个进程的共享与互斥问题。
5 结论
在嵌入式GUI环境下,需要结合操作系统和GUI系统的特点,充分利用两者的资源来实现多串口通信。
在IntelPXA255Sitsang板上实践证明,在MiniGUI-Threads下采用多线程技术,在MiniGUI-Lite下采用服务器程序结合*程序,实现多串口全双工通信,有效地解决了在串口通信中的实时响应问题,降低了数据的丢失率,提高了嵌入式系统的可靠性。
MiniGUI-Threads与MiniGUI-Lite下多串口通信的设计与实现
来源:
电子开发网作者:
章坚武许作鹏董平时间:
2008-07-08
目前,嵌入式Linux设备已广泛应用到计算机、通信和工业控制等领域,负责各种数据的处理和存储,并进行控制决策。
MiniGUI是一种面向嵌入式系统和实时系统的图形用户界面支持系统。
在MiniGUI环境下实现多个RS-232串口通信,多串口实时协同工作,可满足许多工控和通信场合需要。
该文实现平台为扩展了5个串口的IntelPXA255Sitsang的嵌入式硬件平台以及基于嵌入式Linux2.4.19的软件平台,采用MiniGUI作为图形用户界面,在MiniGUI的两个运行模式下设计并实现了多个串口的实时双工通信。
1 MiniGUI及其两个运行模式
目前,在Linux之上进行(实时)嵌入式系统开发,一般选择如下几种GUI系统:
紧缩的XWindow系统、MiniGUI、MicroWindows、OpenGUI、QT/Embedded等。
该文采用MiniGUI1.3.0版本作为系统GUI,应用程序建立在图形界面和嵌入式Linux内核之上。
MiniGUI可以编译成两个截然不同的运行模式:
一个是MiniGUI-Threads,一个是MiniGUI-Lite。
MiniGUI-Threads具有非常高的实时性,采用了线程机制。
MiniGUI-Threads的应用程序,可以具有多个线程,每个线程有不同的功能和任务,可以在不同的线程中建立多个窗口,但所有的窗口在一个进程或者地址空间中运行。
不同的线程之间可通过MiniGUI的消息传递机制进行事件传送和同步。
但显然,这种基于线程的结构也导致了系统整体的脆弱,如果某个线程因为非法的数据访问而终止运行,则整个进程都将受到影响。
不过,这种体系结构对实时控制系统等时间关键的系统来讲,还是非常适合的。
为了改变Threads版本体系上的脆弱,MiniGUI-Lite版本使用进程机制。
和MiniGUI-Threads相反,MiniGUI-Lite上的每个程序是单独的进程,每个进程也可以建立多个窗口。
在Lite版本中,可以同时运行多个MiniGUI应用程序。
首先需要启动一个服务器程序mginit,然后启动其他作为客户端运行的MiniGUI应用程序。
如果因为某种原因客户终止,服务器不受影响,可以继续运行。
2 MiniGUI-Threads下多线程串口通信
串口通信程序要做到实时性高,吞吐量大,程序的输出与输入是两个需要并发执行的操作,因此在MiniGUI-Threads下采用多线程技术。
在多串口的MiniGUI环境下,通信程序至少应具备如下两个线程:
主线程和串口监听线程。
主线程是串口通信程序的管理者,用来进行人机交互的操作、部分串口操作和数据处理及协调好各线程运行;串口监听线程的职责是实时监听各个串口的状态,一旦发生预定的事件,需要判断是哪个串口有数据到来,然后进行一定的数据处理,并立即向主线程发送相应消息,请求主线程对其进行相应处理。
主线程在接到串口监听线程发送来的消息后,立即调用相应的过程函数进行处理。
串口通信程序可以在MiniGUIMain()中通过CreateThreadForMainWindow函数创建了两个并发的线程,定义线程的入口函数地址并返回线程标识符。
在主线程中配置termios结构完成对串口的初始化操作,创建主窗口,建立控键并进入消息循环。
监视线程和主线程同时启动,此后串口监视线程在后台对各个串口进行实时监控,在监视到预定事件时,立即通过SendMessage向主线程发送相应的消息。
与此同时,主线程对监视线程发送来的消息作出相应的处理,读取串口数据到缓冲区。
主线程在处理完该消息后,串口监视线程继续执行后面的程序代码,对串口继续监听。
主线程还可以同时进行其他工作,比如接收或处理键盘、鼠标一类的消息,数据显示和响应控件事件等。
MiniGUI-Threads下的SendMessage的传递机制,可以用来发送“同步消息”。
如果发送消息的线程和接收消息的线程不是同一个线程,发送消息的线程将阻塞并等待另一个线程的处理结果,然后继续运行;如果发送消息的线程和接收消息的线程是同一个线程,则与MiniGUI-Lite的SendMessage一样,直接调用接收消息窗口的过程函数。
MiniGUI定义了MSG_USER宏,用户能够自定义消息,并利用自定义消息传递数据。
应用程序可如下定义自己的消息:
#defineMSG_MYMESSAGE1(MSG_USER+1);
#defineMSG_MYMESSAGE2(MSG_USER+2)。
这种方式能有效防止数据的堵塞,避免线程出现死锁等情况的出现。
MiniGUI-Threads下串口通信程序过程如图1所示。
图1 串口通信程序框图
3 MiniGUI-Lite下多进程串口通信
Lite版本是支持客户服务器(C/S)方式的多进程系统,在运行过程中有且仅有一个服务器程序在运行,它的全局变量mgServer被设为TRUE,其余的MiniGUI应用程序为客户,mgServer变量被设为FALSE。
各个应用程序分别运行于各自不同的进程空间,如图2所示。
图2 Lite版本下的多进程系统
串口通信程序要在MiniGUI-Lite下做到实时性高,吞吐量大,管理协调,可以参照MiniGUI-Threads下串口通信程序的设计,分别建立服务器程序和客户监听程序。
根据MiniGUI-Lite的特点,多串口通信程序首先初始化并启动服务器程序。
MiniGUI-Lite下服务器程序名需要命名为mginit,该程序为客户应用程序准备共享资源,并管理客户应用程序。
首先,需要初始化OnNewDelClient和OnChangeLayer这两个服务器程序特有的全局变量。
这两个变量是两个函数的指针变量,分别用来监视来自客户和层的事件。
当客户连接到mginit或者断开与mginit之间的套接字连接时,如果程序设置了OnNewDelClient这个变量,将调用这个变量指向的函数。
同样,当MiniGUI-Lite的层发生变化时,比如有新的客户加入到某个层,或者层中的活动客户发生了变化,如果程序中设置了OnChangeLayer这个变量,则会调用这个变量指向的函数。
通过调用这些函数,可以得到客户标号或者层的指针、客户指针,mginit程序就可以方便地访问MiniGUI函数库中的内部数据结构,获得当前的客户以及当前层的所有信息,从而管理客户程序。
接着调用ServerStartup函数启动mginit的服务器功能,这个函数所做的工作比较简单,就是建立监听客户连接的套接字(/var/tmp/minigui)并返回。
最后调用SetDesktopRect设定屏幕上由服务器独占的矩形区域,客户程序不能使用这块矩形区域。
设定之后,客户程序就只能在这个独占的区域以外进行绘制。
服务器初始化完毕后,用exec_app()函数启动客户串口监听程序,完成服务器和客户程序的建立。
为了实现客户和服务器之间的通讯,MiniGUI-Lite通过经过封装的UNIXDomainSocket处理函数在服务器和监听程序之间进行数据传递。
服务器可以使用serv_listen()函数建立一个监听套接字,并返回套接字文件描述符:
#defineLISTEN_SOCKET"/var/tmp/mysocket"
staticintlisten_fd;
BOOLlisten_socket(HWNDhwnd)
{
if((listen_fd=serv_listen(LISTEN_SOCKET))<0)
returnFALSE;
eturnRegisterListenFD(fd,POLL_IN,hwnd,NULL);/*在系统中注册监听文件描述符,在被监听的文件描述符上发生指定事件时,向某窗口发送MSG_FDEVENT消息*/
}
当服务器接收到来自客户的连接请求时,服务器hwnd窗口将接收到MSG_FDEVENT消息,这时,服务器可接受该连接请求:
intMyWndProc(HWNDhwnd,intmessage,WPARAMwParam,LPARAMlParam)
{
switch(message){
caseMSG_FDEVENT:
if(LOWORD(wParam)==listen_fd){/*来自监听套接字