由于工作的需要,要用VC实现通过串口波特率为115200的实时数据采集任务,然后我便开始在网上查找关于串口通讯的文章,最终的结果是一般的串口通讯文章很多,但关于实时串口通讯的文章几乎没有。完成该工作以后,我便想把自己所采用的方法和应用心得写出来,以供大家参考。
一、
串口通讯
在这一部分中我并不系统地介绍串口通讯,而只是结合该例来介绍串口通讯。
1. 打开通讯资源句柄
使用CreateFile函数,该函数原形如下:
HANDLE CreateFile( LPCTSTR lpFileName, // pointer to name of the
file
DWORD dwDesiredAccess, // access (read-write) mode
DWORD dwShareMode, // share mode
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
// pointer to security attributes
DWORD dwCreationDistribution, // how to create
DWORD dwFlagsAndAttributes, // file attributes
HANDLE hTemplateFile // handle to file with attributes to copy
);
当使用CreateFile打开通讯资源时,必须指定以下该值:
。dwShareMode参数必须是零,打开独占访问的资源
。dwCreationDistribution参数必须指定为OPEN_EXISTING标志
。hTemplateFile参数必须是NULL
2. 指定并初始化读写缓冲
程序通过调用SetupComm函数来指定读写缓冲的大小,该函数执行重新分配内部输入和输出缓冲的任务,而对输入和输出缓冲初始化时,用到PurgeComm函数。
3. DCB结构配置
当用CreateFile完成串口打开操作时,继承了设备控制块(DCB结构)设置和I/O操作的超时值。可用GetCommTimeouts来得到当前配置,如要修改此配置,需要调用SetCommTimeouts函数。
4. 设置超时值
关于超时值有一个结构COMMTIMEOUTS和两个相关的函数GetCommTimeouts和SetCommTimeouts。
注意:为完成实时串口通信必须把COMMTIMEOUTS结构的读超时参数设为MAXDWORD,而且两个读超时参数设0。只有在这种设置情况下,当读完输入缓冲器就完成读操作,而不管你得到什么字符,即使读缓冲为空。即:保证读操作立即返回!这是实现串口实时通讯的要点。
二、
多媒体定时器
基于WM_TIMER消息的定时器是低精度的,它最多可以精确到54.915毫秒,大约每秒18.2次,并且WM_TIMER消息的优先级比较低,它可能造成WM_TIMER消息的"丢失",从精度和优先级的考虑,在本例中不能使用该定时器完成任务,因此我用到了Windows多媒体服务中提供的多媒体定时器。
1. 确定最大和最小周期
可以用timeGetDevCaps函数确定定时器服务提供的最大和最小定时器事件周期,这些数值对不同的计算机是变化的,也与Windows运行方式有关。
2. 建立最小时间精度
在启动定时器事件前,应用程序必须建立想要使用的最小定时器精度,在定时器服务事件结束之后,必须清除该精度。用户可以使用timeBeginPeriod和timeEndPeriod函数来设置和清除最小定时器精度,每个timeBeginPeriod调用都必须有一个timeEndPeriod与之对应,且两个函数必须指定相同的最小精度。
3. 启动定时器事件
与该步骤相关的两个函数为timeSetEvent和timeKillEvent,读者可以查阅这两个函数,有两个值得注意的地方:一是启动定时器事件就一定要把它清楚,因为小于100ms
的定时器对CPU 的消耗是非常大的;二是在timeSetEvent调用中设置定时器回调函数时要遵循其规则。读者可以参考下面的例程。
三、
例程
本例程提供了串口类(CCOM)和多媒体定时器操作类(CMMTimer)的完整代码,有兴趣的读者可以直接把它们应用于您的程序中。
//COM.h
class CCOM
{
public:
CCOM();
virtual ~CCOM();
BOOL
InitCOM();
BOOL SendCOMCode(unsigned _int8 chCode[], int nNum);
DWORD GetCOMData(unsigned char *pchBuffer);
void CloseCOM();
HANDLE hCOM;
};
//COM.cpp
CCOM::CCOM()
{
}
CCOM::~CCOM()
{
}
BOOL
CCOM::InitCOM()//初始化串口
{
DCB dCB;
COMMTIMEOUTS ct;
//得到打开串口,并得到串口句柄
hCOM = CreateFile( "COM1", GENERIC_READ | GENERIC_WRITE,
0, NULL, OPEN_EXISTING, 0, NULL);
if(hCOM == INVALID_HANDLE_VALUE)
return FALSE;
//初始化串口,READBUFFER和WRITEBUFFER是用户自己定义的两个宏
SetupComm(hCOM, READBUFFER, WRITEBUFFER);
PurgeComm(hCOM, PURGE_TXCLEAR|PURGE_RXCLEAR);
//设置DCB结构
if(!GetCommState(hCOM, &dCB))
{
CloseHandle(hCOM);
return FALSE;
}
dCB.BaudRate = 38400;
dCB.ByteSize = 8;
dCB.Parity = NOPARITY;
dCB.StopBits = ONESTOPBIT;
if(!SetCommState(hCOM, &dCB))
{
CloseHandle(hCOM);
return FALSE;
}
//设置超时值
ct.ReadIntervalTimeout = MAXDWORD;
ct.ReadTotalTimeoutConstant = 0;
ct.ReadTotalTimeoutMultiplier = 0;
ct.WriteTotalTimeoutConstant = 0;
ct.WriteTotalTimeoutMultiplier = 0;
if(!SetCommTimeouts(hCOM, &ct))
{
CloseHandle(hCOM);
return FALSE;
}
return TRUE;
}
BOOL
CCOM::SendCOMCode(unsigned _int8 chCode[], int nNum)//写串口
{
DWORD dwWritenNum;
return WriteFile(hCOM, chCode, nNum, &dwWritenNum, NULL);
}
DWORD
CCOM::GetCOMData(unsigned char *pchBuffer)//读串口
{
DWORD dwReadNum;
ReadFile(hCOM, pchBuffer, READBUFFER, &dwReadNum, NULL);
return dwReadNum;
}
void
CCOM::CloseCOM()
{
CloseHandle(hCOM);
}
//MMTimer.h
class CMMTimer
{
public:
CMMTimer();
virtual ~CMMTimer();
BOOL
SetMMTimer(UINT nInterval, UINT nResolution);
void KillMMTimer();
UINT nTimerRes, nTimerID;
};
//MMTimer.cpp
//回调函数声明
void PASCAL MMTimerProc(UINT wTimerID, UINT msg, DWORD dwUser, DWORD
dw1,
DWORD dw2);
CMMTimer::CMMTimer()
{
}
CMMTimer::~CMMTimer()
{
}
BOOL
CMMTimer::SetMMTimer(UINT nInterval, UINT nResolution)//
装载多媒体时钟
{
//得到定时器精度
TIMECAPS tc;
nTimerRes = nResolution;
if (timeGetDevCaps(&tc, sizeof(TIMECAPS))==TIMERR_NOERROR)
{
if(nTimerRes!=min(max(tc.wPeriodMin, nTimerRes), tc.wPeriodMax))
return FALSE;
}
if(timeBeginPeriod(nTimerRes)==TIMERR_NOERROR)//启动定时器精度
{
nTimerID = timeSetEvent(nInterval, nTimerRes, MMTimerProc, NULL,
TIME_PERIODIC);//启动定时器
if(!nTimerID)
return TRUE;
else
return FALSE;
}
else
return FALSE;
}
void
CMMTimer::KillMMTimer()//卸载多媒体时钟
{
if(nTimerID)
{
timeKillEvent(nTimerID);
nTimerID = 0;
}
timeEndPeriod(nTimerRes);
}
void PASCAL MMTimerProc(UINT wTimerID, UINT msg, DWORD dwUser, DWORD
dw1,
DWORD dw2)
{
//在该例中为读串口数据,您可替换为您的多媒体时钟任务
static unsigned char chData[READBUFFER];
static DWORD dwNum;
dwNum = c_com.GetCOMData(chData);//串口读数
}
|