进来要用串口编程完结单片机和PC的通讯,现在网上收录了一些编程的比如作为备检。
串口API通讯函数编程
16位串口应用程序中,运用的16位的WindowsAPI通讯函数:
①OpenComm()翻开串口资源,并指定输入、输出缓冲区的巨细(以字节计)
CloseComm()封闭串口;
例:intidComDev;
idComDev=OpenComm(“COM1”,1024,128);
CloseComm(idComDev);
②BuildCommDCB()、setCommState()填写设备操控块DCB,然后对已翻开的串口进行参数装备;
例:DCBdcb;
BuildCommDCB(“COM1:2400,n,8,1”,&dcb);
SetCommState(&dcb);
③ReadComm、WriteComm()对串口进行读写操作,即数据的接纳和发送.
例:char*m_pRecieve;intcount;
ReadComm(idComDev,m_pRecieve,count);
Charwr[30];intcount2;
WriteComm(idComDev,wr,count2);
16位下的串口通讯程序最大的特色就在于:串口等外部设备的操作有自己特有的API函数;而32位程序则把串口操作(以及并口等)和文件操作一致起来了,运用相似的操作。
在MFC下的32位串口应用程序
32位下串口通讯程序能够用两种办法完结:运用ActiveX控件;运用API通讯函数。
运用ActiveX控件,程序完结十分简略,结构明晰,缺陷是欠灵敏;运用API通讯函数的优缺陷则基本上相反。
运用ActiveX控件:
VC++6.0供给的MSComm控件经过串行端口发送和接纳数据,为应用程序供给串行通讯功用。运用十分便利,但惋惜的是,很少有介绍MSComm控件的材料。
⑴.在当时的Workspace中刺进MSComm控件。
Project菜单——>AddtoProject—->ComponentsandControls—–>Registered
ActiveXControls—>挑选Components:MicrosoftCommunicationsControl,
version6.0刺进到当时的Workspace中。
成果添加了类CMSComm(及相应文件:mscomm.h和mscomm.cpp)。
⑵.在MainFrm.h中参加MSComm控件。
protected:
CMSCommm_ComPort;
在Mainfrm.cpp::OnCreare()中:
DWORDstyle=WS_VISIBLE|WS_CHILD;
if(!m_ComPort.Create(NULL,style,CRect(0,0,0,0),this,ID_COMMCTRL)){
TRACE0(“FailedtocreateOLECommunicationsControl”);
return-1; //failtocreate
}
⑶.初始化串口
m_ComPort.SetCommPort(1); //挑选COM?
m_ComPort.SetInBufferSize(1024);//设置输入缓冲区的巨细,Bytes
m_ComPort.SetOutBufferSize(512);//设置输入缓冲区的巨细,Bytes//
if(!m_ComPort.GetPortOpen())//翻开串口
m_ComPort.SetPortOpen(TRUE);
m_ComPort.SetInputMode(1);//设置输入办法为二进制办法
m_ComPort.SetSettings(“9600,n,8,1”);//设置波特率等参数
m_ComPort.SetRThreshold(1);//为1表明有一个字符引发一个事情
m_ComPort.SetInputLen(0);
⑷.捕捉串口事项。MSComm控件能够选用轮询或事情驱动的办法从端口获取数据。咱们介绍比较运用的事情驱动办法:有事情(如接纳到数据)时告诉程序。在程序中需求捕获并处理这些通讯事情。
在MainFrm.h中:
protected:
afx_msgvoidOnCommMscomm();
DECLARE_EVENTSINK_MAP()
在MainFrm.cpp中:
BEGIN_EVENTSINK_MAP(CMainFrame,CFrameWnd)
ON_EVENT(CMainFrame,ID_COMMCTRL,1,OnCommMscomm,VTS_NONE)//映射ActiveX控件事情
END_EVENTSINK_MAP()
⑸.串口读写.完结读写的函数确实很简略,GetInput()和SetOutput()就可。两个函数的原型是:
VARIANTGetInput();及voidSetOutput(constVARIANT&newValue);都要运用VARIANT类型(一切Idispatch::Invoke的参数和回来值在内部都是作为VARIANT目标处理的)。
无论是在PC机读取上传数据时仍是在PC机发送下行指令时,咱们都习惯于运用字符串的方法(也能够说是数组方法)。查阅VARIANT文档知道,能够用BSTR表明字符串,但惋惜的是一切的BSTR都是包括宽字符,即便咱们没有界说_UNICODE_UNICODE也是这样!WinNT支撑宽字符,而Win95并不支撑。为处理上述问题,咱们在实践作业中运用CbyteArray,给出相应的部分程序如下:
voidCMainFrame::OnCommMscomm(){
VARIANTvResponse; intk;
if(m_commCtrl.GetCommEvent()==2){
k=m_commCtrl.GetInBufferCount();//接纳到的字符数目
if(k>0){
vResponse=m_commCtrl.GetInput();//read
SaveData(k,(unsignedchar*)vResponse.parray->pvData);
}//接纳到字符,MSComm控件发送事情}
。。。。。//处理其他MSComm控件
}
voidCMainFrame::OnCommSend(){
。。。。。。。。//预备需求发送的指令,放在TxData[]中
CByteArrayarray;
array.RemoveAll();
array.SetSize(Count);
for(i=0;i
m_ComPort.SetOutput(COleVariant(array));//发送数据}
㈡运用32位的API通讯函数:
⑴.在中MainFrm.cpp界说大局变量
HANDLE hCom;//预备翻开的串口的句柄
HANDLE hCommWatchThread;//辅佐线程的大局函数
⑵.翻开串口,设置串口
hCom=CreateFile(“COM2”,GENERIC_READ|GENERIC_WRITE,//答应读写
0, //此项有必要为0
NULL, //nosecurityattrs
OPEN_EXISTING, //设置产生办法
FILE_FLAG_OVERLAPPED,//咱们预备运用异步通讯
NULL);
我运用了FILE_FLAG_OVERLAPPED结构。这正是运用API完结非堵塞通讯的关键所在。
ASSERT(hCom!=INVALID_HANDLE_VALUE);//检测翻开串口操作是否成功
SetCommMask(hCom,EV_RXCHAR|EV_TXEMPTY);//设置事情驱动的类型
SetupComm(hCom,1024,512);//设置输入、输出缓冲区的巨细
PurgeComm(hCom,PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR
|PURGE_RXCLEAR);//清洁净输入、输出缓冲区
COMMTIMEOUTSCommTimeOuts;//界说超时结构,并填写该结构
…………
SetCommTimeouts(hCom,&CommTimeOuts);//设置读写操作所答应的超时
DCB dcb;//界说数据操控块结构
GetCommState(hCom,&dcb);//读串口本来的参数设置
dcb.BaudRate=9600;dcb.ByteSize=8;dcb.Parity=NOPARITY;
dcb.StopBits=ONESTOPBIT;dcb.fBinary=TRUE;dcb.fParity=FALSE;
SetCommState(hCom,&dcb);//串口参数装备
上述的COMMTIMEOUTS结构和DCB都很重要,实践作业中需求细心挑选参数。
⑶发动一个辅佐线程,用于串口事情的处理。
Windows供给了两种线程,辅佐线程和用户界面线程。辅佐线程没有窗口,所以它没有自己的音讯循环。可是辅佐线程很简单编程,一般也很有用。
在次,咱们运用辅佐线程。首要用它来监督串口状况,看有无数据抵达、通讯有无过错;而主线程则可专注进行数据处理、供给友爱的用户界面等重要的作业。
hCommWatchThread=
CreateThread((LPSECURITY_ATTRIBUTES)NULL,//安全特点
0,//初始化线程栈的巨细,缺省为与主线程巨细相同
(LPTHREAD_START_ROUTINE)CommWatchProc,//线程的大局函数
GetSafeHwnd(),//此处传入了主结构的句柄
0,&dwThreadID);
ASSERT(hCommWatchThread!=NULL);
⑷为辅佐线程写一个大局函数,首要完结数据接纳的作业。请注意OVERLAPPED结构的运用,以及怎样完结了非堵塞通讯。
UINTCommWatchProc(HWNDhSendWnd){
DWORDdwEvtMask=0;
SetCommMask(hCom,EV_RXCHAR|EV_TXEMPTY);//有哪些串口事情需求监督?
WaitCommEvent(hCom,&dwEvtMask,os);//等候串口通讯事情的产生
检测回来的dwEvtMask,知道产生了什么串口事情:
if((dwEvtMask&EV_RXCHAR)==EV_RXCHAR){//缓冲区中有数据抵达
COMSTATComStat;DWORDdwLength;
ClearCommError(hCom,&dwErrorFlags,&ComStat);
dwLength=ComStat.cbInQue;//输入缓冲区有多少数据?
if(dwLength>0){BOOLfReadStat;
fReadStat=ReadFile(hCom,lpBuffer,dwLength,&dwBytesRead,&READ_OS(npTTYInfo));//读数据
注:咱们在CreareFile()时运用了FILE_FLAG_OVERLAPPED,现在ReadFile()也有必要运用
LPOVERLAPPED结构.不然,函数会不正确地陈述读操作已完结了.
运用LPOVERLAPPED结构,ReadFile()当即回来,不用等候读操作完结,完结非堵塞
通讯.此刻,ReadFile()回来FALSE,GetLastError()回来ERROR_IO_PENDING.
if(!fReadStat){
if(GetLastError()==ERROR_IO_PENDING){
while(!GetOverlappedResult(hCom,&READ_OS(npTTYInfo),&dwBytesRead,TRUE)){
dwError=GetLastError();
if(dwError==ERROR_IO_INCOMPLETE)continue;//缓冲区数据没有读完,持续
…………
::PostMessage((HWND)hSendWnd,WM_NOTIFYPROCESS,0,0);//告诉主线程,串口收到数据}
所谓的非堵塞通讯,也即异步通讯。是指在进行需求花费很多时刻的数据读写操作(不仅仅是指串行通讯操作)时,一旦调用ReadFile()、WriteFile(),就能当即回来,而让实践的读写操作在后台运转;相反,如运用堵塞通讯,则有必要在读或写操作悉数完结后才干回来。因为操作或许需求恣意长的时刻才干完结,所以问题就呈现了。
十分堵塞操作还答应读、写操作能一起进行(即堆叠操作?),在实践作业中十分有用。
要运用非堵塞通讯,首先在CreateFile()时有必要运用FILE_FLAG_OVERLAPPED;然后在ReadFile()时lpOverlapped参数必定不能为NULL,接着查看函数调用的回来值,调用GetLastError(),看是否回来ERROR_IO_PENDING。如是,最终调用GetOverlappedResult()回来堆叠操作(overlappedoperation)的成果;WriteFile()的运用相似。
⑸.在主线程中发送下行指令。
BOOL fWriteStat;charszBuffer[count];
…………//预备好发送的数据,放在szBuffer[]中
fWriteStat=WriteFile(hCom,szBuffer,dwBytesToWrite,
&dwBytesWritten,&WRITE_OS(npTTYInfo));//写数据
//我在CreareFile()时运用了FILE_FLAG_OVERLAPPED,现在WriteFile()也有必要运用LPOVERLAPPED结构.不然,函数会不正确地陈述写操作已完结了.
运用LPOVERLAPPED结构,WriteFile()当即回来,不用等候写操作完结,完结非堵塞通讯.此刻,WriteFile()回来FALSE,GetLastError()回来ERROR_IO_PENDING.
interr=GetLastError();
if(!fWriteStat){
if(GetLastError()==ERROR_IO_PENDING){
while(!GetOverlappedResult(hCom,&WRITE_OS(npTTYInfo),
&dwBytesWritten,TRUE)){
dwError=GetLastError();
if(dwError==ERROR_IO_INCOMPLETE){//normalresultifnotfinished
dwBytesSent+=dwBytesWritten;continue;}
………………….
//我运用了多线程技能,在辅佐线程中监督串口,有数据抵达时依托事情驱动,读入数据并向主线程陈述(发送数据在主线程中,相对说来,下行指令的数据总是少得多);而且,WaitCommEvent()、ReadFile()、WriteFile()都运用了非堵塞通讯技能,依托堆叠(overlapped)读写操作,让串口读写操作在后台运转。