1用VC++写上位机软件实现单片机串口通讯2007年05月30日星期三23:40工业控制领域(如DCS系统),经常涉及到串行通信问题。为了实现微机和单片机之间的数据交换,人们用各种不同方法实现串行通信,如DOS下采用汇编语言或C语言,但在Windows环境下却存在一些困难和不足。在Windows操作系统已经占据统治地位的情况下(何况有些系统根本不支持DOS如Windows2000)开发Windows环境下串行通信技术就显得日益重要。VC++6.0是微软公司于1998年推出的一种开发环境,以其强大的功能,友好的界面,32位面向对象的程序设计及ActiveX的灵活性而受广大软件开发者的青睐,被广泛应用于各个领域。应用VC++开发串行通信目前通常有如下几种方法:一是利用WindowsAPI通信函数;二是利用VC的标准通信函数inp、inpw、inpd、outp、outpw、outpd等直接对串口进行操作;三是使用MicrosoftVisualC++的通信控件(MSComm);四是利用第三方编写的通信类。以上几种方法中第一种使用面较广,但由于比较复杂,专业化程度较高,使用较困难;第二种需要了解硬件电路结构原理;第三种方法看来较简单,只需要对串口进行简单配置,但是由于使用令人费解的VARIANT类,使用也不是很容易;第四种方法是利用一种用于串行通信的CSerial类(这种类是由第三方提供),只要理解这种类的几个成员函数,就能方便的使用。笔者利用CSerial类很方便地实现了在固定式EBM气溶胶灭火系统分区启动器(单片机系统)与上位机的通信。以下将结合实例,给出实现串行通信的几种方法。1WindowsAPI通信函数方法与通信有关的WindowsAPI函数共有26个,但主要有关的有:CreateFile()用“comn”(n为串口号)作为文件名就可以打开串口。ReadFile()读串口。WriteFile()写串口。CloseHandle()关闭串口句柄。初始化时应注意CreateFile()函数中串口共享方式应设为0,串口为不可共享设备,其它与一般文件读写类似。以下给出API实现的源代码。1.1发送的例程//声明全局变量HANDLEm_hIDComDev;OVERLAPPEDm_OverlappedRead,m_OverlappedWrite;//初始化串口voidCSerialAPIView::OnInitialUpdate(){CView::OnInitialUpdate();CharszComParams[50];DCBdcb;Memset(&m_OverlappedRead,0,sizeof(OVERLAPPED));Memset(&m_OverlappedWrite,0,sizeof(OVERLAPPED));m_hIDComDev=NULL;m_hIDComDev=CreateFile(“COM2”,GENERIC_READ│GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL│FILE_FLAG_OVERLAPPED,NULL);if(m_hIDComDev==NULL){2AfxMessageBox(“Cannotopenserialport!”);gotoendd;}memset(&m_OverlappedRead,0,sizeof(OVERLAPPED));memset(&m_OverlappedWrite,0,sizeof(OVERLAPPED));COMMTIMEOUTSCommTimeOuts;CommTimeOuts.ReadIntervalTimeout=0×FFFFFFFF;CommTimeOuts.ReadTotalTimeoutMultiplier=0;CommTimeOuts.ReadTotalTimeoutConstant=0;CommTimeOuts.WriteTotalTimeoutMultiplier=0;CommTimeOuts.WriteTotalTimeoutConstant=5000;SetCommTimeouts(m_hIDComDev,&CommTimeOuts);Wsprintf(szComparams,“COM2:9600,n,8,1”);m_OverlappedRead.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);m_OverlappedWrite.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);dcb.DCBlength=sizeof(DCB);GetCommState(m_hIDComDev,&dcb);dcb.BaudRate=9600;dcb.ByteSize=8;unsignedcharucSet;ucSet=(unsignedchar)((FC_RTSCTS&FC_DTRDSR)!=0);ucSet=(unsignedchar)((FC_RTSCTS&FC_RTSCTS)!=0);ucSet=(unsignedchar)((FC_RTSCTS&FC_XONXOFF)!=0);if(!SetCommState(m_hIDComDev,&dcb)‖!SetupComm(m_hIDComDev,10000,10000)‖m_OverlappedRead.hEvent==NULL‖m_OverlappedWrite.hEvent==NULL){DWORDdwError=GetLastError();if(m_OverlappedRead.hEvent!=NULL)CloseHandle(m_OverlappedRead.hEvent);if(m_OverlappedWrite.hEvent!=NULL)CloseHandle(m_OverlappedWrite.hEvent);CloseHandle(m_hIDComDev);}endd:;3}//发送数据voidCSerialAPIView::OnSend(){charszMessage[20]=“thankyouverymuch”;DWORDdwBytesWritten;for(inti=0;isizeof(szMessage);i++){WriteFile(m_hIDComDev,(LPSTR)&szMessage[i],1,&dwBytesWritten,&m_OverlappedWrite);if(WaitForSingleObject(m_OverlapperWrite,hEvent,1000))dwBytesWritten=0;else{GentOverlappedResult(m_hIDComDev,&m_OverlappedWrite,&dwBytesWritten,FALSE);m_OverlappedWrite.Offset+=dwBytesWritten;}dwBytesWritten++;}}1.2接收例程DCBComDcb;//设备控制块HANDLEhCom;//globalhandlehCom=CreateFile(COM1,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);if(hCom==INVALID_HANDLE_VALUE){AfxMessageBox(无法打开串行口);}else{COMMTIMEOUTSCommTimeOuts;SetCommMask(hCom,EV_RXCHAR);SetupComm(hCom,4096,4096);/*设置收发缓冲区尺寸为4K*/PurgeComm(hCom,PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);//清收发缓冲区//以下初始化结构变量CommTimeOuts,设置超时参数CommTimeOuts.ReadIntervalTimeout=0×FFFFFFFF;CommTimeOuts.ReadTotalTimeoutMultiplier=0;CommTimeOuts.ReadTotalTimeoutConstant=4000;CommTimeOuts.WriteTotalTimeoutMultiplier=0;CommTimeOuts.WriteTotalTimeoutConstant=4000;SetCommTimeouts(hCom,&CommTimeOuts);//设置超时参数ComDcb.DCBlength=sizeof(DCB);4GetCommState(hCom,&ComDcb);//获取当前参数ComDcb.BaudRate=9600;//波特率ComDcb.ByteSize=8;//数据位ComDcb.Parity=0;/*校验0~4=no,odd,even,mark,space*/SetCommState(hCom,&ComDcb);}//设置新的通信参数接收可用定时器或线程等DWORDdRead,dReadNum;unsignedcharbuff[200];dRead=ReadFile(hCom,buff,100,&dReadNum,NULL);//接收100个字符,//dReadNum为实际接收字节数2利用端口函数直接操作这种方式主要是采用两个端口函数_inp(),_outp()实现对串口的读写,其中读端口函数的原型为:int_inp(unsignedshotport)该函数从端口读取一个字节,端口号为0~65535。写端口的函数原型为:int_outp(unsignedshotport,intdatabyte)该函数向指定端口写入一个字节。不同的计算机串口地址可能不一样,通过向串口的控制及收发寄存器进行读写,可以实现灵活的串口通信功能,由于涉及具体的硬件电路讨论比较复杂,在此不加赘述。3MSComm控件MSComm控件是微软开发的专用通信控件,封装了串口的所有功能,使用很方便,但在实际应用中要小心对其属性进行配置。下面详细说明该类应用方法。3.1MSComm控件的属性CommPort:设置串口号,类型short:1-comm12-comm2.Settings:设置串口通信参数,类型CString:B波特率,P奇偶性(N无校验,E偶校验,O奇校验),D字节有效位数,S停止位。PortOpen:设置或返回串口状态,类型BOOL:TURE打开,FALSE关闭。InputMode:设置从接收缓冲区读取数据的格式,类型long:0-Text1-Bin。Input:从接收缓冲区读取数据,类型VARIANT。InBufferCount:接收缓冲区中的字节数,类型:short。InBufferSize:接收缓冲区的大小,类型:short。Output:向发送缓冲区写入数据,类型:VARIANT。OutBufferCount:发送缓冲区中的字节数,类型:short。OutBufferSize:发送缓冲区的大小,类型:short。InputLen:设置或返回Input读出的字节数,类型:short。CommEvent:串口事件,类型:short。3.2程序示例//串口初始化if(!m_comm.GetPortOpen())m_comm.SetPortOpen(TURE);/*打开串口*/m_comm.SetSettings(4800,n,8,1);/*串口参数设置*/5m_comm.SetInputMode(0);/*设置TEXT缓冲区输入方式*/m_comm.SetRthresHold(1);/*每接收一个字符则激发OnComm()事件*/接收数据m_comm.SetInputLen(1);/*每次读取一个字符VARINATV1=m_comm.GetInput();/*读入字符*/m_V1=V1.bstrval;发