基于对话框的API串口软件开发



基于对话框的API串口软件开发

一、项目需求

应用API函数实现一个基于对话框的串口软件,要求软件启动后显示软件界面,在界面中能选择串口、波特率、校验位、数据位、停止位等串口参数,能打开串口和关闭串口,能输入发送数据,发送数据,显示接收数据,显示接收字节数的累计值,显示发送字节数的累计值。

一、软件开发过程

基于对话框的API串口软件开发_第1张图片

串口界面效果图

API串口通讯软件的5个模块:

(1) 串口初始化程序:设置串口参数,设置超时,Overlapped变量,清理串口,启动串口监测线程;

(2) 串口监测线程序;

(3) 接收数据程序:读取接收缓冲区数据;

(4) 发送数据程序:发送数据写入缓冲区;

(5) 关闭串口程序:关闭串口,停止和撤销串口监测线程;

1、串口初始化程序:

/*******串口参数控件初始化*********/

m_ctrlPort.SetCurSel(0);

m_ctrlBaud.SetCurSel(3);

m_ctrlParity.SetCurSel(1);

m_ctrlDataBits.SetCurSel(0);

m_ctrlStopBits.SetCurSel(0);

m_nBaud = 9600;

m_nComPort = 1;

m_cParity = 'N';

m_nDataBits = 8;

m_nStopBits = 0;

 

OpenPortStatus = FALSE;//串口处于关闭状态

m_ctrlClosePort.EnableWindow(FALSE);//“关闭串口”按钮无效

RX_Count = 0;//接收数据字节计数器置0

TX_Count = 0;//发送数据字节计数器置0

 

打开串口程序:

int errorCode;//错误码

CString str;

m_nComPort = m_ctrlPort.GetCurSel() + 1;//获得串口号

 

m_nBaud = m_ctrlBaud.GetCurSel();//获得波特率列表序号

switch (m_nBaud)//根据波特率列表序号求得波特率

{

case 0: m_nBaud = 115200;

break;

case 1: m_nBaud = 1200;

break;

case 2: m_nBaud = 19200;

break;

case 3: m_nBaud = 9600;

}

 

m_cParity = m_ctrlParity.GetCurSel();//获得校验位列表序号

switch (m_cParity)//根据校验位列表序号求得校验位字符

{

case 0: m_cParity = 'E';

break;

case 1: m_cParity = 'N';

break;

case 2: m_cParity = 'O';

}

 

m_nDataBits = m_ctrlDataBits.GetCurSel();//获得数据位列表序号

switch (m_nDataBits)//根据数据位列表序号求得数据位字符

{

case 0: m_nDataBits = 6;

break;

case 1: m_nDataBits = 7;

break;

case 2: m_nDataBits = 8;

}

 

m_nStopBits = m_ctrlStopBits.GetCurSel();//获得停止位列表序号

 

if (!OpenPortStatus)//串口未打开

{

errorCode = InitialComPort(m_nComPort, m_hCom);//调用串口初始化函数

 

if (errorCode)//串口初始化未成功,则显示信息和错误码,并返回

{

str.Format("%d", errorCode);

AfxMessageBox("不能打开串口\r\nerrorCode" + str);

return;

}

 

m_ctrlOpenPort.EnableWindow(FALSE);//置“打开串口”按钮无效

m_ctrlClosePort.EnableWindow(TRUE);//置“关闭串口”按钮有效

OpenPortStatus = TRUE;//串口处于打开状态

m_ctrlPort.EnableWindow(FALSE);//置串口组合框控件无效

m_ctrlBaud.EnableWindow(FALSE);//置波特率组合框控件无效

m_ctrlParity.EnableWindow(FALSE);//置校验位组合框控件无效

m_ctrlDataBits.EnableWindow(FALSE);//置数据位组合框控件无效

m_ctrlStopBits.EnableWindow(FALSE);//置停止位组合框控件无效

}

初始化串口函数的主要由以下几个部分构成:

CreateFile//创建串口句柄

SetCommTimeouts//设置串口超时

SetupComm//设置串口缓冲区

SetCommMask//设置串口事件允许字

GetCommState//得到串口DCB

SetCommState//设置串口DCB

CreateEvent//初始化Overlapped变量

PurgeComm//清理串口

AfxBeginThread//启动串口监测线程

 

相应的代码为:

int CAPICOMTestDlg::InitialComPort(int m_nPort, HANDLE hCom)

{

BOOL b_setOK;

CString szPort;

szPort.Format("COM%d", m_nComPort);//获得串口标识符

/***********创建串口句柄,即打开串口*************/

hCom = CreateFile(szPort, //打开的文件名为szPort

GENERIC_READ | GENERIC_WRITE, //文件的操作属性为可读可写

0, //文件的共享属性为不共享

NULL, //文件的安全属性为NULL

OPEN_EXISTING,//文件必须已经存在,由设备提出

FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, //默认属性,允许对文件进行重叠操作

NULL);

 

if (hCom == INVALID_HANDLE_VALUE)//创建串口句柄失败

{

AfxMessageBox("CreateFile失败(串口不存在或已被占用)" + szPort);

return 1;

}

 

/*设置超时*/

COMMTIMEOUTS TimeOuts;//定义超时结构变量

TimeOuts.ReadIntervalTimeout = MAXDWORD;//设置读操作间隔超时数据

TimeOuts.ReadTotalTimeoutMultiplier = 0;//设置读操作超时系数

TimeOuts.ReadTotalTimeoutConstant = 0;//设置读操作超时常数

TimeOuts.WriteTotalTimeoutMultiplier = 50;//设置写操作超时系数

TimeOuts.WriteTotalTimeoutConstant = 2000;//设置写操作超时常数

 

b_setOK = SetCommTimeouts(hCom, &TimeOuts);//串口超时设置

if (!b_setOK)

{

AfxMessageBox("SetCommTimeouts失败");

return 2;

}

 

/*设置缓冲区*/

b_setOK = SetupComm(hCom, 8192, 4096);//接收缓冲区为8192个字节,发送缓冲区为4096个字节,仅指定推荐的大小

//实际的接收缓冲区为8192字节,发送缓冲区0字节(不受限制),系统用推荐的缓冲区大小来优化性能和避免缓冲区超限

if (!b_setOK)

{

AfxMessageBox("SetupComm失败");

return 3;

}

 

/*设置串口事件允许字*/

b_setOK = SetCommMask(hCom, EV_RXCHAR);//允许产生接收数据事件消息

if (!b_setOK)

{

AfxMessageBox("SetCommMask失败");

return 4;

}

 

/*设置串口参数(设置DCB*/

DCB m_dcb;

b_setOK = GetCommState(hCom, &m_dcb);//得到串口的DCB,并存入m_dcb

if (!b_setOK)

return 5;

//开始设置应用层DCB

m_dcb.fBinary = TRUE;//通信数据模式为二进制

m_dcb.BaudRate = m_nBaud;//设置波特率

m_dcb.ByteSize = m_nDataBits;//设置数据位

m_dcb.fParity = TRUE;//设置允许奇偶校验

 

switch (m_cParity)//设置校验位

{

case 'N':

m_dcb.Parity = NOPARITY;

break;

case 'E':

m_dcb.Parity = EVENPARITY;

break;

case 'O':

m_dcb.Parity = ODDPARITY;

break;

default:;

}

 

switch (m_nStopBits)//设置停止位

{

case 0:

m_dcb.StopBits = ONESTOPBIT;

break;

case 1:

m_dcb.StopBits = ONE5STOPBITS;

break;

case 2:

m_dcb.StopBits = TWOSTOPBITS;

break;

default:;

}

//结束应用层DCB的设置

 

b_setOK = SetCommState(hCom, &m_dcb);//设置串口的DCB,即将应用层的DCB写入串口的DCB

if (!b_setOK)

{

AfxMessageBox("SetCommState失败");

return 6;

}

/*初始化(串口事件监测,读结束数据操作,写接收数据操作)OVERLAPPED*/

memset(&m_ovCom, 0, sizeof(OVERLAPPED));

m_ovCom.hEvent=CreateEvent(NULL, TRUE, FALSE, NULL);

 

memset(&m_ovread, 0, sizeof(OVERLAPPED));

m_ovread.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

memset(&m_ovwrite, 0, sizeof(OVERLAPPED));

m_ovwrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

/*清理通信端口*/

PurgeComm(hCom, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);

 

/*启动串口监测线程*/

m_hCom = hCom;//将句柄赋给m_hCom

m_pOwner = this->GetSafeHwnd();//获得当前窗口句柄

if (!(p_ComThread = AfxBeginThread(CommThread, this)))

return 7;

return 0;

}

 

2、串口监测线程的开发

主要由以下几个部分组成:

WaitCommEvent//等待串口事件函数

GetLastError()//继续查明前面的overlapped方式函数的执行情况

GetOverlappedResult//获得前面overlapped方式函数执行的最终结果

::PostMessage//发出消息响应

CloseHandle//关闭串口

AfxEndThread//停止并撤销线程

 

相应的代码为:

 

UINT CAPICOMTestDlg::CommThread(LPVOID pParam)

{

BOOL bResult = TRUE;

BOOL ExitThread = FALSE;

DWORD dwComEventMask;//存储所发生的串口事件掩码

DWORD dwCommOvTrans;

DWORD dwError = 0;

DWORD length = 0;

 

CAPICOMTestDlg* pDlg = (CAPICOMTestDlg*) pParam;//LPVOID类型转换为本软件的类型,

//以获得本软件对话框类的指针且类型与本软件定义相同

 

while (1)//进行无线循环,对串口事件进行监测

{

 

dwComEventMask = 0;

bResult = WaitCommEvent(pDlg->m_hCom, &dwComEventMask, &pDlg->m_ovCom);//等待串口事件函数

if (!bResult)

{

switch (dwError = GetLastError())

{

case ERROR_IO_PENDING://没有串口事件发生

GetOverlappedResult(pDlg->m_hCom, &pDlg->m_ovCom, &dwCommOvTrans, TRUE);//等待串口事件

if (dwComEventMask && EV_RXCHAR)//是否有串口事件发生

{

if (!ExitThread)

{

::PostMessage(pDlg->m_pOwner, WM_COMM_RXCHAR, EV_RXCHAR, length);//发出WM_COMM_RXCHAR消息

ExitThread = TRUE;

}

else

{

ExitThread = FALSE;

}

}

else

{

if (CloseStatus)//是否点击了“关闭串口”按钮

{

CloseStatus = FALSE;

CloseHandle(pDlg->m_hCom);//关闭串口

pDlg->m_hCom = NULL;

AfxEndThread(Thread_exitCode);//停止并撤销线程

}

}

break;

case 87:

break;

default:

AfxMessageBox("WaitCommEvent执行出错");

break;

}

}

}

return 0;

}

 

3、接收数据程序

 

ClearCommError

ReadFile//从串口缓冲区读取数据,并将读得的数据存入lpBuffer数组中

相应的代码为:

BOOL CAPICOMTestDlg::ReadCommData(HANDLE hCommPort, char* readBuffer, DWORD* readlength)

{

DWORD dwErrorFlags;

DWORD charlength = 0;

ClearCommError(m_hCom, &dwErrorFlags, &ComStat);

charlength = ComStat.cbInQue;

if (charlength == 0)

return FALSE;

ReadFile(hCommPort, readBuffer, charlength, readlength, &m_ovread);

return TRUE;

}

 

 

4、发送数据程序

WriteFile//lpBuffer数组中的数据写入串口发送缓冲区,由此实现串口发送数据

相应的代码为:

void CAPICOMTestDlg::OnButtonSend() 

{

// TODO: Add your control notification handler code here

if (!OpenPortStatus)

{

AfxMessageBox("串口未打开");

return;

}

else

{

m_ctrlSendData.GetWindowText(m_strSendData);//获得发送数据

DWORD len = m_strSendData.GetLength();

 

for (int i=0; (DWORD)i

{

writeBuff[i] = m_strSendData[i];//CString型转换为字节数组

}

 

writelength = sendCommDataBlock(m_hCom, writeBuff, len);//发送数据块函数

TX_Count += len;

CString strTemp;

strTemp.Format("TX:%d", TX_Count);

m_ctrlTXCount.SetWindowText(strTemp);

}

}

 

DWORD CAPICOMTestDlg::sendCommDataBlock(HANDLE hCommPort, char* writeBuffer, DWORD writelength)

{

BOOL fwritestate;

DWORD realwrite_length;

fwritestate = WriteFile(m_hCom, writeBuffer, writelength, &realwrite_length, &m_ovwrite);

//writeBuffer中数据写入串口缓冲区,所写长度由writelength决定

return realwrite_length;

}

 

5、关闭串口程序

GetExitCodeThread

SetCommMask

SetEvent

实现步骤:先获得串口监测线程的退出码;屏蔽所以串口事件;强行产生串口Overlapped事件;串口监测线程响应这个强行发送的Overlapped事件,并执行关闭串口和撤销线程的API函数。

相应的代码为:

void CAPICOMTestDlg::OnButtonClosePort() 

{

// TODO: Add your control notification handler code here

BOOL ClosPortFlag;

 

if (ClosPortFlag = ClosePort(m_hCom))

{

OpenPortStatus = FALSE;

m_ctrlClosePort.EnableWindow(FALSE);

m_ctrlOpenPort.EnableWindow(TRUE);

m_ctrlPort.EnableWindow(TRUE);

m_ctrlBaud.EnableWindow(TRUE);

m_ctrlParity.EnableWindow(TRUE);

m_ctrlDataBits.EnableWindow(TRUE);

m_ctrlStopBits.EnableWindow(TRUE);

}

}

 

BOOL CAPICOMTestDlg::ClosePort(HANDLE hCommPort)

{

if (!OpenPortStatus)

return TRUE;

 

if (!GetExitCodeThread(p_ComThread->m_hThread, &Thread_exitCode))//获得退出码

{

AfxMessageBox("获得线程退出码错误");

return FALSE;

}

 

p_DuThread = p_ComThread;//保存线程对象到一个全局型对象中

CloseStatus = TRUE;

SetCommMask(hCommPort, 0);//禁止串口的所以事件

return TRUE;

}

 

你可能感兴趣的:(串口通信,C/C++)