加入收藏 | 设为首页 | 会员中心 | 我要投稿 | RSSRSS-巴斯仪表网
您当前的位置:首页 > 电子发烧 > 单片机学习

MiniGUI-Threads与MiniGUI-Lite下多串口通信的设计与实现

时间:2013-09-02  来源:123485.com  作者:9stone

 目前,嵌入式Linux 设备已广泛应用到计算机、通信和工业控制等领域,负责各种数据的处理和存储,并进行控制决策。MiniGUI 是一种面向嵌入式系统和实时系统的图形用户界面支持系统。在MiniGUI 环境下实现多个RS-232 串口通信,多串口实时协同工作,可满足许多工控和通信场合需要。该文实现平台为扩展了5 个串口的Intel PXA255 Sitsang 的嵌入式硬件平台以及基于嵌入式Linux2.4.19 的软件平台,采用MiniGUI 作为图形用户界面,在MiniGUI 的两个运行模式下设计并实现了多个串口的实时双工通信。

1  MiniGUI 及其两个运行模式
    目前,在Linux 之上进行(实时) 嵌入式系统开发,一般选择如下几种GUI 系统:紧缩的X Window 系统、MiniGUI、MicroWindows、OpenGUI、QT/Embedded 等。该文采用MiniGUI 1.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 宏,用户能够自定义消息,并利用自定义消息传递数据。应用程序可如下定义自己的消息:
# define MSG_MYMESSAGE1 (MSG_USER + 1) ;
# define MSG_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 函数库中的内部数据结构,获得当前的客户以及当前层的所有信息,从而管理客户程序。接着调用ServerStart up 函数启动mginit 的服务器功能,这个函数所做的工作比较简单,就是建立监听客户连接的套接字(/ var/ tmp/minigui) 并返回。最后调用SetDesktopRect 设定屏幕上由服务器独占的矩形区域,客户程序不能使用这块矩形区域。设定之后,客户程序就只能在这个独占的区域以外进行绘制。服务器初始化完毕后,用exec_app () 函数启动客户串口监听程序,完成服务器和客户程序的建立。
   为了实现客户和服务器之间的通讯,MiniGUI-Lite 通过经过封装的UNIX Domain Socket 处理函数在服务器和监听程序之间进行数据传递。服务器可以使用serv_listen ( ) 函数建立一个监听套接字,并返回套接字文件描述符:
# define L ISTEN_SOCKET " / var/ tmp/ mysocket"
static int listen_fd ;
BOOL listen_socket ( HWND hwnd)
{
 if ( (listen_fd = serv_listen (L ISTEN_ SOCKET) )< 0)
return FALSE;
eturn RegisterListenFD ( fd , POLL_IN , hwnd ,NULL) ; /*在系统中注册监听文件描述符,在被监听的文件描述符上发生指定事件时, 向某窗口发送MSG_ FDEVENT 消息* /
}
    当服务器接收到来自客户的连接请求时,服务器hwnd 窗口将接收到MSG_ FDEVEN T 消息,这时,服务器可接受该连接请求:
int MyWndProc ( HWND hwnd , int message ,WPARAM wParam , L PARAM lParam)
{
switch (message) {
case MSG_FDEVENT :
if (LOWORD (wParam) = = listen_fd) { / *来自监听套接字*/
pid_t pid ; uid_t uid ; int conn_fd ;
conn _ fd = serv _ accept ( listen _ fd , &pid ,&uid) ; /* 服务器调用serv_accept () 函数接受来自客户的连接请求*/
if (conn_fd > = 0) {
RegisterListenFD ( conn _ fd , POLL _ IN ,hwnd , NULL) ; }
}
else {  int fd = LOWORD(wParam) ; /* 来自已连接套接字*/
sock_read_t (fd , ...) ; /* 处理来自客户的数据*/
sock_write_t (fd , ....) ; }
break ;
 }
}
  上面的代码中,服务器将连接得到的新文件描述符也注册为监听描述符, 因此,在MSG_ FDEVEN T 消息的处理中,应该判断导致MSG_ FDEVEN T 消息的文件描述符类型,并适当处理。在客户端,当需要连接到服务器时,可通过如下代码:
int conn_fd ;
if ( (conn_fd = cli_conn (L ISTEN_SOCKET , 'b') ) >= 0) {
/* 客户调用cli_conn 函数连接到服务器*/
sock_write_t (fd , ....) ; /* 向服务器发送请求*/
sock_read_t (fd , ....) ; } /* 获取来自服务器的处理结果*/

4  两种模式下需要注意的一些差别
4.1  实现串口监听程序的差别
  串口监听程序担当着实时监控的任务,监视预定义事件。在MiniGUI-Threads 下监听线程需要给主线程发送预定义消息;在MiniGUI-Lite 下,监听程序要完成和服务器的数据交换和通信。在这里,预定义事件就是多个串口中有一个串口有数据到来时,判断并锁定这个串口进行数据读取。使用I/O 多路复用( I/O multiplexing) 技术可以很好地解决这个问题。其基本思想是:先构造一张有关描述符的表,然后调用一个函数,它要到这些描述符中的一个已准备好的进行I/O 时才返回。在返回时,它告诉线程哪一个描述符已准备好可以进行I/O。使用这种返回值,就可调用相应的I/O 函数(一般是read 或write ) , 并且确知该函数不会阻塞。
    Linux 下的系统调用select 函数可以执行I/O 多路复用。Select 函数原型为:
Int select ( int maxf dp1 , fd_set *readfds , f d_set *writefds , fd_ set *exceptf ds , Struct timeval *tvpt r) ;
    在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.h>中。当一个线程调用pthread_mutex_lock () 函数锁定某个Mutex 后,其它也要锁定Mutex 的线程将被阻塞,直至Mutex 被释放,从而达到资源的独占。对于线程按正确顺序执行的顺序同步,正如上面提到的,Sendmessage 的消息传递机制起到了“事件同步”的作用。当一个线程要等待另一个线程的某个事件时,会把自己挂起,直至另一个线程的相应事件发生后把自己唤醒。而在MiniGUI-Lite 中主要是应用程序间的互斥性同步,可以使用Linux 下常用的“信号量机制”等方式解决多个进程的共享与互斥问题。

5  结论
    在嵌入式GUI 环境下,需要结合操作系统和GUI 系统的特点,充分利用两者的资源来实现多串口通信。在Intel PXA255 Sitsang 板上实践证明,在MiniGUI-Threads 下采用多线程技术, 在MiniGUI-Lite 下采用服务器程序结合监听程序,实现多串口全双工通信,有效地解决了在串口通信中的实时响应问题,降低了数据的丢失率,提高了嵌入式系统的可靠性。


分享到:
来顶一下
返回首页
返回首页
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表
栏目导航->单片机学习
  • 电子应用基础
  • 电源技术
  • 无线传输技术
  • 信号处理
  • PCB设计
  • EDA技术
  • 单片机学习
  • 电子工具设备
  • 技术文章
  • 精彩拆解欣赏
  • 推荐资讯
    使用普通运放的仪表放大器
    使用普通运放的仪表放
    3V与5V混合系统中逻辑器接口问题
    3V与5V混合系统中逻辑
    数字PID控制及其改进算法的应用
    数字PID控制及其改进
    恶劣环境下的高性价比AD信号处理数据采集系统
    恶劣环境下的高性价比
    栏目更新
    栏目热门