高速串口数据实时采集的解决方案和例程

 由于工作的需要,要用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);//串口读数
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值