430 likes | 538 Views
串行通信机理 与 应用. 苏统华 tonghuasu@hit.edu.cn May 5, 2006. 概要. 背景介绍 串行通信系统模型 串行接口配置 串行通信机理简介 多线程串行通信的编程 扩展的串行通信编程. 背景介绍. 串行通信是指计算机与其它外围设备以 “ 位 ” 为单位进行信息交换的过程。 串行通信应用的领域 : 数据的采集与智能诊断 工业的检测及自动控制 ,eg. 控制单片机、采集核探测数据、接收 GPS 数据等 集散控制环境( DCS : Distributed Control System ) 无线调制环境 3D 空间定位器
E N D
串行通信机理与应用 苏统华 tonghuasu@hit.edu.cn May 5, 2006
概要 • 背景介绍 • 串行通信系统模型 • 串行接口配置 • 串行通信机理简介 • 多线程串行通信的编程 • 扩展的串行通信编程
背景介绍 • 串行通信是指计算机与其它外围设备以“位”为单位进行信息交换的过程。 • 串行通信应用的领域: • 数据的采集与智能诊断 • 工业的检测及自动控制,eg.控制单片机、采集核探测数据、接收GPS数据等 • 集散控制环境(DCS:Distributed Control System) • 无线调制环境 • 3D空间定位器 • 数据手套的发射器和接收器
串行通信系统模型 • 串行通信系统是一种典型的计算机数据通信系统。 • 抽象化的点-点计算机数据通信系统框图:
串行接口配置 1 5 5 1 6 9 9 6 • RS-232C标准:它对串行通信接口的有关问题,如连接电缆和机械、电气特性、信号功能及传送过程都作了明确规定。 • COM口一般是9脚的连接器,分成插针(Male)和插孔(Female)两类: 9脚的RS-232C插针 9脚的RS-232C插孔
串行接口配置 • DTE与DCE间的通信连接模型通常有3种,分别为: • 最简3线交叉连接模型 • 近距7线交叉连接模型 • 远距7线对应模型
串行接口配置 计算机 单片机 TxD TxD RxD RxD GND GND • 最简3线交叉连接模型:应用于控制信号较少的场合 • 优点: • 控制简单 • 节省线路
串行接口配置 • 近距7线交叉连接模型:用于控制信号较丰富的近距离通信场合 • 优点:这种方式省去了DCE
串行接口配置 • 远距7线对应模型:“豪华”配置,包含通信设备MODEM或其它数传设备 • 优点:适合远距离的通信任务
一个简单应用例子: • 系统中采用PIC16C73单片机作为下位机,工业控制PC机为上位机,二者通过RS-232C串行口接收或上传数据和指令。RS-232C信号的电平和单片机串口信号的电平不一致,必须进行二者之间的电平转换。在此使用MAXIM公司的集成电平转换芯片MAX202E作为RS-232C和TTL间的电平转换芯片。
串行通信机理 • 目前,Windows系统下通过VC开发串行通信的常用方法可分成如下6类: • 利用Windows API函数进行串行通信编程; • 利用开发环境自带的ActiveX通信控件来构建串行通信应用: • 利用VC的标准通信函数_inp,_outp等进行串行通信操作; • 利用第三方提供的通信类(如Cserial类)、通信库(如DLL); • 利用内嵌汇编直接操作通信端口来实现(只限Win9x下); • 通过编写VxD(Virtual Device Driver)来实现。
串行通信机理 • 可编程通用异步收发器(UART:Universal Asynchronous Receiver and Transmitter)是PC内部一块单独的芯片,是实现数据“串-并”转换功能的电路。 • 发送数据时,UART会把CPU经由数据总线送来的字符数据保存到“数据输出寄存器”;随后,UART再按顺序把“数据输出寄存器”的内容送到“发送移位寄存器”转换成连续的串行位流,并在其中插入奇偶检验位、起始位和停止位;最后将位流传送出去。 • 接收数据的过程与上相反。串行的位流通过串口时,送入UART的“接收移位寄存器”;当“接收移位寄存器”中己接收完1个字符的各位后,数据就从“接收移位寄存器”进入“数据输入寄存器”,成为了一个字符大小的并行数据;最后就是数据经由数据总线上传给CPU。
串行通信机理 • Windows系统为每个通信设备开辟了用户定义大小的接收和发送缓冲区,应用程序只需完成对接收和发送缓冲区操作就可以了。 • 通信的实际过程是每接收一个字符就产生一个低级硬件中断,Windows系统中的串行VxD取得控制权,将接收到的字符放入接收缓冲区,然后将控制权返给正在运行的应用程序. • 在定义了硬件流控制的条件下,如果接收缓冲区数据已满,串行驱动程序用定义的流控制机制通知发送方停止发送数据。缓冲区内的数据按FIFO方式处理。
串行通信机理 • Win32环境下的通信应用服务要依赖于虚拟通信驱动程序VCOMM。 • VCOMM提供一条应用程序到硬件的保护模式代码通路,以实现可靠的高速数据传输。 • VCOMM实质是一个VxD,它总是在Win32启动时载入。Win32为串口提供了端口驱动程序,端口驱动程序是动态装入VxD的,可由VCOMM调用来操作串口。
串行通信机理 • 通信API有一组基本的函数,它们为应用程序的编制提供了一个适于串行通信的接口,处理通信资源的打开、关闭、读写等操作。 • 通信API的扩展函数可为那些不为标准函数所支持的服务提供接口,这组扩展的函数允许用户使用仅为驱动程序专用的参数来配置,也允许用户调用驱动程序的专用函数。
串行通信机理 • 串行通信的公有的操作流程,大致四步: • 打开串行口。这样做就是通知Windows本应用程序需要这个串口,并封锁之,使其它应用程序在该串口释放前不能使用它。 • 通信前,初始化这个串行口。如配置BAUD,数据位等各种参数。 • 进行串口数据传输。有时要进行数据的检错或纠错。 • 通信完成时,关闭串口。即释放串口资源以供其它应用程序使用。
多线程与串行通信的示例 • 在串行通信系统的应用中,一般要求系统能较快响应系统事件同时还不能丢数据。如果把监视通信事件的功能单独作为一个线程来执行,即使主线程阻塞,也不影响系统对外来信息的接收。 • 主线程和监视线程各司其职又相互协调:主线程主要完成串口的初始化、串口的资源获取、启动监视事件、关闭串口句柄和发送信息等操作,监视线程则用于发现串口事件并进行相应事件的处理。
多线程与串行通信的示例 • Win32中禁止应用程序直接和硬件交互,开发者只能通过系统提供的各类Vxd来管理硬件。在这种情况下,Windows系统充当了应用程序与硬件之间的中介,而留给开发者的则是高级、统一的接口。Windows系统对串口硬件资源的管理类似于对普通文件的操作。 • 多线程串行通信系统的开发流程:
多线程与串行通信的示例 • l打开串口 • Windows通过调用CreateFile()以操作文件的方式来指定和获得串行口资源。函数成功执行后,将返回串行口的内存句柄,该句柄是随后操作(如读、写,参数设置等)的指向对象。 • 下面是一个获取串行口资源的例子: HANDLE m_pComm; m_pComm=CreateFile(“COM1”,GENERIC_READ|GENERIC_WRITE,0,NULL, OPEN_EXISTING,FILE_FLAG_OVERLAPPED,NULL); if(m_hComm==INVALID_HANDLE_VALUE) { //错误处理 return FALSE;}
多线程与串行通信的示例 • l串口初始化 • 初始化的内容有设置缓冲区、设置超时、配置通信参数几个过程。 1. 设置缓冲区 • 串行口拥有发送和接收缓冲区,调用SetupComm()可以根据需要设置串行口发送和接收缓冲区的大小。得到缓冲区之后应该首先清除缓冲区中原来的数据内容,调用的函数为PurgeComm()。 • 下面的语句将设置接收缓冲区大小为512字节,发送缓冲区为1024字节,并清空缓冲区: SetupComm(m_pComm,512,1024); PurgeComm(m_pComm,PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);
多线程与串行通信的示例 • 设置超时 • 由于在进行串行通信过程中,可能发生一些无法预测的事件,在进行串行口操作前,最好设置超时,以保障通信的安全性。通过函数SetCommTimeouts()可以设置所有读写操作的超时时间。SetCommTimeouts()的原型如下。 BOOL SetCommTimeouts( HANDLE hFile, //串行口句柄 LPCOMMTIMEOUTS lpCommTimeouts //指向超时结构COMMTIMEOUTS ); 一个设置超时的例子:
多线程与串行通信的示例 • 配置通信参数 • 常调用SetCommState()来配置串行口本次通信过程中的参数,如波特率(Baud)、数据位数、停止位数、校验情况等,这些参数的修改是通过对一个DCB结构的配置来实现的。DCB结构是一个拥有30个成员的大数据结构。由于一般要修改的参数很有限,故经常在SetCommState()之前调用GetCommState()来获得系统的缺省设备控制块值,然后在缺省值基础上进行一定的修改,再把修改后的DCB结构传送给SetCommState()。对于SetCommState()和GetCommState()的原型如下: BOOL SetCommState( HANDLE hFile, //串行口句柄 LPDCB lpDCB //指向DCB结构的指针 ); BOOL GetCommState( HANDLE hFile,//串行口句柄 LPDCB lpDCB //指向DCB结构的指针 ); 一个符合上述描述的示例如下: DCB m_dcb m_dcb.DCBlength=sizeof(DCB); GetCommState(m_pComm,&m_dcb); m_dcb.fBinary=TRUE; //… if(!SetCommState(m_pComm,&m_dcb)){ //出错处理 }
多线程与串行通信的示例 • 创建I/O重叠结构 • 要支持重叠I/O操作,必须声明重叠结构并为该重叠结构生成I/O重叠事件。以重叠方式调用的函数可以立即返回,即使操作还没有完成,这样能使费时的I/O操作以后台运行而调用线程仍可自由执行别的任务。重叠操作要求在获得通信设备时用FILE-FLAG-OVERLAPPED标志建立,并且重叠函数要指定OVERLAPPED结构的指针。重叠事件的生成可用CreateEvent()。 • 为读操作分配I/O重叠结构的例程如下: OVERLAPPED m_rdOL; memset(&m_rdOL,0,sizeof(OVERLAPPED)); m_rdOL.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL); if (m_rdOL.hEvent==NULL) { //出错处理 return FALSE;}
多线程与串行通信的示例 • 设置监视事件 • Windows系统是一个基于消息驱动的操作系统,它的许多事件来源于硬件设备,Windows设备驱动程序只是把这些事件进行简单处理,转换成相应的消息放到Windows消息队列中去。Win9x之后取消了串行通信的专用消息WM_COMMNOTIFY,使得用户进程要主动监视串口状态,并对相应事件作出响应。串口监视事件的设置是由SetCommMask()来完成,串口监视事件的捕获由WaitCommEvent()实现,对串口监视事件的捕获请见下文的串口读部分。SetCommMask()的原型如下: BOOL SetCommMask( HANDLE hFile,//串行口句柄 DWORD dwEvtMask//监视事件掩码 );
多线程与串行通信的示例 • 下面是一个设置监视事件的例程: /*为串口设置监视事件,包括接受到数据、输出缓冲区空、错误事件*/ if (!SetCommMask(m_pComm,EV_RXCHAR|EV_TXEMPTY|EV_ERR)) { //出错处理 return FALSE; }
多线程与串行通信的示例 • l启动监视线程 • 读串口时,我们并不知道数据什么时候到来,所以要建立一个在后台运行的专门负责读数据的工作线程(worker thread)。读监视线程的启动常通过调用AfxBeginThread()完成。AfxBeginThread()的原型如下: CWinThread* AfxBeginThread( AFX_THREADPROCpfnThreadProc, LPVOIDpParam, intnPriority = THREAD_PRIORITY_NORMAL, UINTnStackSize = 0, DWORDdwCreateFlags = 0, LPSECURITY_ATTRIBUTESlpSecurityAttrs = NULL );
多线程与串行通信的示例 • 启动一个读监视工作线程的示例如下: //启动一个负责串口读操作的监视线程 CWinThread* m_Thread; if (!m_Thread) { if(!(m_Thread=AfxBeginThread(CommWatchProc,this))) { //出错处理 return FALSE; } }
多线程与串行通信的示例 • 在监视线程的执行函数中,可通过WaitCommEvent()捕获串口事件,WaitCommEvent()的原型如下: BOOL WaitCommEvent( HANDLE hFile,//串行口句柄 LPDWORD lpEvtMask,//指向一个存储事件组的变量 LPOVERLAPPED lpOverlapped,//指向一个重叠结构 ); • 在调用了WaitCommEvent()之后可通过对返回的lpEvtMask 施行“AND”操作来进一步判定捕获到的串口事件类别,并可分别处理。示例如下 DWORD dwEvtMask; WaitCommEvent(m_pComm,&dwEvtMask,&m_rdOL); if(dwEvtMask&EV_ERR) { //捕捉到通信错误 } else if(dwEvtMask&EV_TXEMPTY) { //输出缓冲区的数据发送完 } else if (dwEvtMask&EV_RXCHAR) { //输入缓冲区接收到数据 }
多线程与串行通信的示例 • 进行通信—读写 • 在获得了串行口句柄并完成了准备工作后,即可对串行口进行读写操作了。其中读操作的执行是调用ReadFile()函数,它可以以读取文件的方式从串行口的接收缓冲区(也称为输入缓冲区)中读取指定长度的数据(如果有足够的数据的话)。写操作的执行是调用WriteFile()函数,它完成与读操作相反的过程。在本文的串行口通信中,它们都是以重叠方式进行。ReadFile()和WriteFile()的原型分别如下: BOOL ReadFile( HANDLE hFile,//串行口句柄 LPVOID lpBuffer, //指向接收数据的缓冲区 DWORD nNumberOfBytesToRead,//读的任务量 LPDWORD lpNumberOfBytesRead, //指向实际读取完成的量 LPOVERLAPPED lpOverlapped //指向一个重叠结构 ); BOOL WriteFile( HANDLE hFile, //串行口句柄 LPCVOID lpBuffer, //指向源数据地址 DWORD nNumberOfBytesToWrite, //写的任务量 LPDWORD lpNumberOfBytesWritten, //指向实际写的量 LPOVERLAPPED lpOverlapped //指向一个重叠结构 );
多线程与串行通信的示例 • 以重叠方式操纵串口时,要求ReadFile(或WriteFile)函数必须指定了OVERLAPPED结构,执行时,ReadFile(WriteFile)不必等到读(写)操作完成即可返回,返回值为FALSE,同时GetLastError函数返回 ERROR_IO_PENDING。串口重叠操作的结果由GetOverlappedResult函数返回,成功时为非零,失败时为零;如要得到进一步的扩展信息,则要再一次调用GetLastError函数,根据返回值进行必要处理。下面是重叠读的一个示例: BOOL bReadStat ; COMSTAT ComStat ; DWORD dwErrorFlags; DWORD dwLength; ClearCommError(m_pComm,&dwErrorFlags,&ComStat); if (ComStat.cbInQue>0) { bReadStat=ReadFile(m_pComm,lpszBlock,nMaxLength,&dwLength,&m_os); if (!bReadStat) { // 当读操作未完成继续进行 if(GetLastError()==ERROR_IO_PENDING) { while(!GetOverlappedResult(m_pComm,&m_os,&dwLength,TRUE)) {if(GetLastError() == ERROR_IO_INCOMPLETE) continue; } } else { dwLength=0; ClearCommError(m_pComm,&dwErrorFlags,&ComStat); }} } else dwLength=0;
多线程与串行通信的示例 • 结束串口通信 • 包括取消监视事件、中止读监视线程、清空缓冲区、关闭串口等过程。 • 通信前,为串口设置了监视事件,那么在通信结束时就要取消这些监视。取消的方式是把SetCommMask()的掩码设置为NULL。通信结束,读监视线程的任务也就完成了,这时可调用SuspendThread()来中止该线程。通信结束与通信开始一样要清空缓冲区,并中止未竟的读写操作。PurgeComm()的说明同前文。串口属于独占资源,所以在通信结束后应尽快调用CloseHandle()来关闭串口占用的内存句柄,释放资源。
扩展的串行通信编程 • 应用上述技术到如图3所示的远程通信模型中,两计算机能够有效实现扩展的串口通信,计算机与模块间能够正确区分数据和命令。目前,这种远程通信模型已成功应用于所开发虚拟现实系统的多机信息交换过程中,取得了较好效果。
扩展的串行通信编程 • D22无线数传模块通过天线可实现把从上位机(计算机)接受的数据调制到特定频点而发射出去,同时,能把从空间接受到的某一频点的数据流放大解调成计算机能识别的二进制流,交由计算机处理。 • D22无线数传模块留有一个DB-9的插座,通过它可以与计算机连接,连接示意图如图2示。
扩展的串行通信编程 • 由于计算机需要对模块进行控制,而模块要对计算机发出的命令作出响应,所以模块就要规定一种机制来区分数据和命令。这种机制在传送信息时通过信号线DTR或DSR的状态来标记信息的性质。协议的具体内容如表2。
扩展的串行通信编程 • EscapeCommFunction • 函数EscapeCommFunction可以驱使某一特定的通信设备执行扩展功能。该函数可将DTR、RTS等硬件信号置ON或OFF,并能模拟XON或XOFF。其原型如下: BOOL EscapeCommFunction(HANDLE hFile,DWORD dwFunc); 参数说明如下: lhFile 通信设备(如串行口)的句柄。该句柄由CreateFile()函数返回。 ldwFunc 指定执行扩展功能所对应的宏值。dwFunc的有效宏值(可用“|”组合多个值)说明见MSDN。
扩展的串行通信编程 • GetCommModemStatus 函数GetCommModemStatus可以获取Modem控制寄存器的值,如可读取DSR、CTS的状态。其原型如下: BOOL GetCommModemStatus(HANDLE hFile,LPDWORD lpModemStat); 参数说明如下: lhFile 通信设备(如串行口)的句柄。该句柄由CreateFile()函数返回。 llpModemStat 指向一个代表当前Modem状态控制寄存器的值的变量。该参数的有效值(可用“|”组合多个宏值)说明见MSDN。
扩展的串行通信编程 • 计算机与无线数传模块间的通信要按硬件协议来进行,则要对计算机发送信息和接收信息两种情况分别处理。 • 在计算机要发送信息时,首先要通过GetCommModemStatus函数来获取当前DSR的状态。只有当DSR为ON时,即模块空闲时,才可发送,且发送时设置DTR以标识信息的类型,完成后还要恢复DTR的状态。示例如下:
扩展的串行通信编程 if (GetCommModemStatus(m_pComm,&dwModSta)) if (!(dwModSta|MS_DSR_ON))//判断空闲 { //硬件忙 return ; } if (!EscapeCommFunction(m_pComm,CLRDTR))//设置DTR { //硬件错 AfxAbort(); } //可以发送信息 InfoSend();//发送信息 if (!EscapeCommFunction(m_pComm,SETDTR))//恢复DTR { //硬件错 AfxAbort(); }
扩展的串行通信编程 • 当计算机接收到信息时,首先要令DTR为高电平(表示上位机忙);读取信息时,同时要依据DSR的状态来判定信息的类型;读结束时,要恢复DTR的电平。具体过程如下: • //Step1:读之前 • EscapeCommFunction(pComm->m_hComm,CLRDTR);//置DTR为高 • //系统忙状态
扩展的串行通信编程 • //Step 2:读的过程中 • DWORD dwModStat; • if (GetCommModemStatus(m_hComm,&dwModStat)) • { • if (!(dwModStat&MS_DSR_ON))//判断DSR • { • //信息为数据 • } • else • { • //信息为命令 • } • } • else • { • //出错处理 • } • //Step3:读取后(完成) • EscapeCommFunction(pComm->m_hComm,SETDTR);//恢复DSR状态 • //以后为空闲状态
小结 • 串行通信定义与应用背景 • 串行通信系统的通用模型 • 串行接口配置 • 串行通信机理 • 多线程串行通信的编程 • 扩展的串行通信编程
The End! • 参考资料: • 苏统华.项目管理精髓.程序员,2002,(4):39-43 • 苏统华.多线程串行通信系统的研究.程序春秋,2003,(7):72-76 • 苏统华, 张田文. 用况驱动开发过程的研究与应用. 微机发展.2003,(11):107-109,113 • 其他未发表资料,见: http://blog.hexun.com/maotong Thanks for your attention