本文将依托QT工程,编写Windows下串口编程例子,此案例用到了两个串口COM1以及COM2
-Windows下串口编程需要底层提供的结构体以及函数,函数库
——DCB 结构体
typedef struct _DCB {
DWORD DCBlength;
DWORD BaudRate;
DWORD fBinary: 1 ;
DWORD fParity: 1 ;
DWORD fOutxCtsFlow: 1 ;
DWORD fOutxDsrFlow: 1 ;
DWORD fDtrControl: 2 ;
DWORD fDsrSensitivity: 1 ;
DWORD fTXContinueOnXoff: 1 ;
DWORD fOutX: 1 ;
DWORD fInX: 1 ;
DWORD fErrorChar: 1 ;
DWORD fNull: 1 ;
DWORD fRtsControl: 2 ;
DWORD fAbortOnError: 1 ;
DWORD fDummy2: 17 ;
WORD wReserved;
WORD XonLim;
WORD XoffLim;
BYTE ByteSize;
BYTE Parity;
BYTE StopBits;
char XonChar;
char XoffChar;
char ErrorChar;
char EofChar;
char EvtChar;
WORD wReserved1;
} DCB;
挑选几个常用的进行介绍:
DWORD DCBlength:DCB结构体长度
DWORD BaudRate:波特率
BYTE ByteSize:数据位
BYTE Parity:积偶校验位
BYTE StopBits:停止位
若使用一般串口通信则不需要进行流控,设置上述参数即可。
——函数及库说明:
1. 工程包含的库
#include
#include
#include
#include
#include
#include
#include
2.使用流程
LPCWSTR lpFileName = "COM1";
HANDLE m_hcoml;
m_hcoml = CreateFile(lpFileName,GENERIC_READ | GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);//打开串口
(1) lpFileName:串口号
(2) dwDesiredAccess:GENERIC_READ | GENERIC_WRITE --读写模式
(3) pSecurityAttributes:NULL(0) --指向安全指针
(4) dwCreationDisposition:OPEN_EXISTING --打开已存在的串口
(5) dwFlagsAndAttributes:文件属性 --此处为同步模式,异步模式则填写FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED
SetupComm(m_hcoml,1024,1024);
设置缓冲区大小,此处为1024;
DCB dcb;
dcb.BaudRate = 115200;
dcb.Parity = NOPARITY;
dcb.ByteSize = 8;
dcb.StopBits = ONESTOPBIT;
波特率为115200,无奇偶校验,8位数据位,1位停止位
COMMTIMEOUTS timeouts;
timeouts.WriteTotalTimeoutMultiplier = 5000;
timeouts.WriteTotalTimeoutConstant = 5000;
timeouts.ReadIntervalTimeout = 10;
timeouts.ReadTotalTimeoutConstant = 10;
timeouts.ReadTotalTimeoutMultiplier = 10;
SetCommTimeouts(m_hcoml,&timeouts);
设置读写超时,根据实际需求填写即可。
PurgeComm(m_hcoml,PURGE_TXCLEAR | PURGE_RXCLEAR);
清空缓存区
SetCommMask(m_hcoml,EV_RXCHAR|EV_CTS|EV_DSR);
进行事件设置
EV_RXCHAR //收到数据
EV_CTS //清除发送信号有变化 CTS引脚电平有变化)
EV_DSR // 数据设备就绪状态有变化(DSR引脚电平有变化)
SetCommState(m_hcoml,&dcb);
函数设置COM口的设备控制块
//此处简写,后文会有完整代码贴出
typedef struct the{
HANDLE *m_hcoml;
OVERLAPPED *roverlapped;
int comflag;
}thread_para;
thread_para *tp;
.....
.....
BOOL b_result;
unsigned char buffer[30]={
0x1,0x2,0x3,0x4,0x5};
b_result = WriteFile(*(tp->m_hcoml),buffer,30,&strlength,tp->roverlapped);
if(!b_result){
WaitForSingleObject(tp->roverlapped->hEvent,INFINITE);
GetOverlappedResult(tp->m_hcoml,tp->roverlapped,&strlength,false);
}
串口发送函数–WriteFile
参数解释:
(1) HANDLE hFile:*(tp->m_hcoml)文件句柄
(2) LPCVOID lpBuffer:buffer// 数据缓存区指针
(3) DWORD nNumberOfBytesToWrite:30// 你要写的字节数
(4) LPDWORD lpNumberOfBytesWritten:&strlength // 用于保存实际写入字节数的存储区域的指针
(5) LPOVERLAPPED lpOverlapped :tp->roverlapped // OVERLAPPED结构体指针
检测事件状态–WaitForSingleObject
(1) HANDLE hObject:tp->roverlapped->hEvent//指明一个内核对象的句柄
(2) DWORD dwMilliseconds: INFINITE //等待时间,此处定义无限
检索某重叠操作:GetOverlappedResult
这个函数还不知道为什么用,既然别人都用了,那也按照大家的套路这么用吧。。
typedef struct the{
HANDLE *m_hcoml;
OVERLAPPED *roverlapped;
int comflag;
}thread_para;
thread_para *tp;
.....
.....
BOOL b_result;
DWORD rec_size;
unsigned char recvbuffer[1024]={
0};
while(1){
b_result = ReadFile(*(tp->m_hcoml),recbuffer,1024,&rec_size,tp->roverlapped);
if(rec_size > 0){
printf("com is %d,recv size is: %d data is ",tp->comflag,(size_t)rec_size);
for(int i = 0;i<(unsigned long)rec_size;i++)
printf("%2x",recbuffer[i]);
printf("\r\n");
memset(recbuffer,0x0,sizeof(recbuffer));
}
::Sleep(10);
}
串口发送函数–ReadFile
(1) HANDLE hFile:*(tp->m_hcoml)文件句柄
(2) LPCVOID lpBuffer:recvbuffer// 数据缓存区指针
(3) DWORD nNumberOfBytesToWrite:1024// 你要写的字节数
(4) LPDWORD lpNumberOfBytesWritten:& DWORD rec_size; // 用于保存实际写入字节数的存储区域的指针
(5) LPOVERLAPPED lpOverlapped :tp->roverlapped // OVERLAPPED结构体指针
3 详细代码
#include
#include
#include
#include
#include
#include
#include
using namespace std;
int serialportthread(LPCWSTR lpFileName);
DWORD WINAPI sercom1(LPVOID lpParameter);
DWORD WINAPI sercom2(LPVOID lpParameter);
DWORD WINAPI sercomsendthread(void *data);
DWORD WINAPI sercomrecvthread(void *data);
void threadset();
typedef struct serportdata{
LPCWSTR lparameter;
int number;
}serport;
typedef struct the{
HANDLE *m_hcoml;
OVERLAPPED *roverlapped;
int comflag;
}thread_para;
int main(int argc, char *argv[])
{
threadset();
}
DWORD WINAPI sercom1(LPVOID lpParameter)
{
serport *spc = (serport*)lpParameter;
serialportthread(spc->lparameter);//传COM口参数
return 0;
}
DWORD WINAPI sercom2(LPVOID lpParameter)
{
serport *spc = (serport*)lpParameter;
serialportthread(spc->lparameter);//传COM口参数
return 0;
}
DWORD WINAPI sercomsendthread(void *data)
{
thread_para *tp = (thread_para *)data;
DWORD strlength = 5;
DWORD rec_size;
unsigned char buffer[30]={
0x1,0x2,0x3,0x4,0x5};
unsigned char recbuffer[1024]={
0};
BOOL b_result;
while(1){
ResetEvent(tp->roverlapped->hEvent);
b_result = WriteFile(*(tp->m_hcoml),buffer,30,&strlength,tp->roverlapped);
if(!b_result){
WaitForSingleObject(tp->roverlapped->hEvent,INFINITE);
GetOverlappedResult(tp->m_hcoml,tp->roverlapped,&strlength,false);
}
printf(" \r\n com is %d send strlen is %d\r\n",tp->comflag,strlength);
::Sleep(100);
//if发生中断,break--此处没写,跟根据实际情况跳出
}
ExitThread(0);
}
DWORD WINAPI sercomrecvthread(void *data)
{
thread_para *tp = (thread_para *)data;
DWORD strlength = 5;
DWORD rec_size;
unsigned char buffer[5]={
0x1,0x2,0x3,0x4,0x5};
unsigned char recbuffer[1024]={
0};
BOOL b_result;
while(1){
b_result = ReadFile(*(tp->m_hcoml),recbuffer,1024,&rec_size,tp->roverlapped);
if(rec_size > 0){
printf(" \r\n com is %d,recv size is: %d data is ",tp->comflag,(size_t)rec_size);
for(int i = 0;i<(unsigned long)rec_size;i++)
printf("%2x",recbuffer[i]);
printf("\r\n");
memset(recbuffer,0x0,sizeof(recbuffer));
}
::Sleep(10);
//if发生中断,break--此处没写,跟根据实际情况跳出
}
ExitThread(0);
}
void threadset()
{
serport Sp1,Sp2;
Sp1.number = 1;
Sp2.number = 2;
Sp1.lparameter = __TEXT("COM1");
Sp2.lparameter = __TEXT("COM2");
LPSECURITY_ATTRIBUTES lpThreadAttributescom1 = 0;
SIZE_T dwStackSizecom1 = 0;
LPTHREAD_START_ROUTINE lpStartAddresscom1 = sercom1;
LPVOID lpParametercom1 = (LPVOID)&Sp1;
DWORD dwCreationFlagscom1 = 0;
LPDWORD lpThreadIdcom1 = 0;
HANDLE threadcom1;
LPSECURITY_ATTRIBUTES lpThreadAttributescom2 = 0;
SIZE_T dwStackSizecom2 = 0;
LPTHREAD_START_ROUTINE lpStartAddresscom2 = sercom2;
LPVOID lpParametercom2 = (LPVOID)&Sp2;
DWORD dwCreationFlagscom2 = 0;
LPDWORD lpThreadIdcom2 = 0;
HANDLE threadcom2;
threadcom1 = CreateThread(lpThreadAttributescom1,dwStackSizecom1,lpStartAddresscom1,lpParametercom1,dwCreationFlagscom1,lpThreadIdcom1);
//创建COM1口初始化线程
threadcom2 = CreateThread(lpThreadAttributescom2,dwStackSizecom2,lpStartAddresscom2,lpParametercom2,dwCreationFlagscom2,lpThreadIdcom2);
//创建COM2口初始化线程
while(1)
{
::Sleep(10000);
}
//::exit(0);
}
int serialportthread(LPCWSTR lpFileName)
{
HANDLE m_hcoml;
m_hcoml = CreateFile(lpFileName,GENERIC_READ | GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if(m_hcoml == (HANDLE)-1)
{
printf("open serial faild!\r\n");
}
else{
printf("open serial success!\r\n");
}
SetupComm(m_hcoml,1024,1024);
DCB dcb;
if(!GetCommState(m_hcoml,&dcb))
{
printf("fiald");
}
dcb.BaudRate = 115200;
dcb.Parity = NOPARITY;
dcb.ByteSize = 8;
dcb.StopBits = ONESTOPBIT;
COMMTIMEOUTS timeouts;
timeouts.WriteTotalTimeoutMultiplier = 5000;
timeouts.WriteTotalTimeoutConstant = 5000;
timeouts.ReadIntervalTimeout = 10;
timeouts.ReadTotalTimeoutConstant = 1;
timeouts.ReadTotalTimeoutMultiplier = 1;
SetCommTimeouts(m_hcoml,&timeouts);
PurgeComm(m_hcoml,PURGE_TXCLEAR | PURGE_RXCLEAR);
SetCommMask(m_hcoml,EV_RXCHAR|EV_CTS|EV_DSR);
SetCommState(m_hcoml,&dcb);
OVERLAPPED wroverlapped;
ZeroMemory(&wroverlapped,sizeof(wroverlapped));
wroverlapped.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
OVERLAPPED rroverlapped;
ZeroMemory(&rroverlapped,sizeof(rroverlapped));
rroverlapped.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
thread_para spcomsend;
thread_para spcomrecv;
spcomsend.m_hcoml = &m_hcoml;
spcomsend.roverlapped = &wroverlapped;
spcomrecv.m_hcoml = &m_hcoml;
spcomrecv.roverlapped = &rroverlapped;
LPSECURITY_ATTRIBUTES lpThreadAttributescom1 = 0;
SIZE_T dwStackSizecom1 = 0;
LPTHREAD_START_ROUTINE lpStartAddresscom1 = sercomsendthread;
LPVOID lpParametercom1 = (LPVOID)&spcomsend;
DWORD dwCreationFlagscom1 = 0;
LPDWORD lpThreadIdcom1 = 0;
HANDLE threadsend;
LPSECURITY_ATTRIBUTES lpThreadAttributescom2 = 0;
SIZE_T dwStackSizecom2 = 0;
LPTHREAD_START_ROUTINE lpStartAddresscom2 = sercomrecvthread;
LPVOID lpParametercom2 = (LPVOID)&spcomrecv;
DWORD dwCreationFlagscom2 = 0;
LPDWORD lpThreadIdcom2 = 0;
HANDLE threadrecv;
const char *p = (const char *)lpFileName;
if(p[0] == 'C' && p[2] == 'O' && p[4] == 'M' && p[6] == '1'){
spcomsend.comflag = 1;
spcomrecv.comflag = 1;
}//若此线程是COM1口,令comflag = 1;
if(p[0] == 'C' && p[2] == 'O' && p[4] == 'M' && p[6] == '2'){
spcomsend.comflag = 2;
spcomrecv.comflag = 2;
}//若此线程是COM2口,令comflag = 2;
threadsend = CreateThread(lpThreadAttributescom1,dwStackSizecom1,lpStartAddresscom1,lpParametercom1,dwCreationFlagscom1,lpThreadIdcom1);
//创建COM X口发送线程
threadrecv = CreateThread(lpThreadAttributescom2,dwStackSizecom2,lpStartAddresscom2,lpParametercom2,dwCreationFlagscom2,lpThreadIdcom2);
//创建COM X口接收线程
while(1){
::Sleep(100);
//if发生中断,break--此处没写,跟根据实际情况跳出
}
ExitThread(0);
}
写的不太好请指出,谢谢每一位前辈的付出。