串口接收数据如何处理?

magicsnake 2002-04-29 10:29:44
怎样接受串口的数据,接受后怎样处理。
原先考虑使用线程建立死循环监听串口,但是接受数据后应该怎样处理才好?中断这个线程,在处理完数据后重新开心的监听线程?
请教各位大虾!
...全文
2569 31 打赏 收藏 转发到动态 举报
写回复
用AI写文章
31 条回复
切换为时间正序
请发表友善的回复…
发表回复
magicsnake 2002-05-13
  • 打赏
  • 举报
回复
哇,还有这么多道理啊!
pc加延时!!我怎么就没想到呢!!
真是一语惊醒梦中人啊!非常感谢!
发7位ASCII码我也考虑考虑,呵呵!
Thank You!
shornmao 2002-05-13
  • 打赏
  • 举报
回复
to magicsnake(北极狐) :

平衡按键反映和串口响应的最简单办法就是PC加延时。没有想到吧,这是经常出现的问题,PC速度远远大于单片机,即使你的单片机全速用来响应串口,也跟不上Pentium的速度。

另外一种方法就是改变协议,由单片机主动发送命令,由PC来答复,不过这样数据封装就比较麻烦了。或者想办法解决PC机处理SM2的问题,或者使用7位ASCII码来传送,最高位用来做地址/数据标示,如果你需要传送真正的二进制数据,就需要采用一些二进制到文本的编码方案了,如BASE64,这样的话单片机的运算负载就又加重了。
magicsnake 2002-05-13
  • 打赏
  • 举报
回复
o
终于基本上了解了!:)
非常感谢大家!放分!
hotxu 2002-05-11
  • 打赏
  • 举报
回复
“要是几台单片机同时向上位机发送请求会产生数据冲突,上位机收到的字符会交错,无法辨识。”

这个问题好解决:
1.首先单片机发送的数据中包括校验和,上位机接收时判断校验和,如果
校验和正确,发回查询数据,反之返回查询失败。这样即使几台同时发送
,单片机可收到查询失败的消息,提示重新发送。
2.几台单片机同时按键的可能性不大,如果你的查询请求数据不是很长(一秒内都发不完)。冲突的可能性很小。即使串口一次只接收一个字符,假设波特率为9600,也就是一秒内能接收或发送9600位,如果查询数据有20个字符,不到20毫秒就可以完成收发,你说冲突的可能性大吗?
还有多串口扩展卡,就是为了增加串口的卡,如果你只是为了个人练功,就没有必要买了,你的方法也不错。
hotxu 2002-05-11
  • 打赏
  • 举报
回复
你误会了:
在计算机中,数据的本质是0和1构成的,反映在串口上也就是高低电平。
波特率9600也就是在串口上产生了9600次高低电平,也就是9600个0或1。
而你说的字符,如果是ASCII码,一个字符是一个字节(8位)。发送字符
的最终形式是发送位,不过这不用你来处理。

校验和你可以这样来处理,如果你要发送的数据是2,3,4这三个数字,你把
他们三个相加也就是9,然后你发送2,3,4,9这四个数字。9是校验和。
接收时接收四个数字,将前三个相加的和与第四个比较,如果正确,也就是
说接收无误。

当然发送的数据不是这样简单。
CCLIS 2002-05-11
  • 打赏
  • 举报
回复
1、首先制定一个协议,单片机发出的各种请求用单个字符表示(8位数据位共可以有256种请求,应该够了吧)。
2、单片机和上位机之间的通讯采用XON/XOFF模式。
3、每台单片机在发出请求时同时带上自行的标识号(如果请求种类和标识号可以合并到一个字符中更好)
4、上位机的XON始终打开,就是说随时等待单片的请求。
5、单片机在发出请求后,将XON打开,接收数据,其他的单片机因未打开XON,则不会接收数据,不会影响键盘中断。
6、冲突解决:多台单片机同时发出请求,同时接收数据,则判断接收的数据中是否有自己的标识号,如果没有,则继续接收,有,则处理数据;
多台单片机同时向上位机发出请求,超时未收到数据后自动重发,注意:每台单片机的等待时间不要一样,以免造成第二次请求冲突。

具体的代码,建议使用API中的FILE操作来编写。
magicsnake 2002-05-11
  • 打赏
  • 举报
回复
to hotxu()
校验和?我刚接触串口,这个怎么用?
串口一次不是只能发送一个字符么?(还是一位?)
意思是说,你把一个字符的8个位分解了,其中用了一位作校验用?是不是这样?能说清楚点么?谢谢!~
magicsnake 2002-05-10
  • 打赏
  • 举报
回复
to hotxu()
这个是我原来的想法,但是我的串口数据接收线是绑在一起的,多个单片机同时向上位机发数据会产生冲突,串口扩展卡我没有用过,不知道怎么用。

to shornmao(死猫)
这只是我个人练功用的一个小项目,呵呵。小弟实在是很穷,没什么money聘请大侠你哦555555~~~~

言归正传,打个比方,我有10台单片机要和一台上位微机通信,单片机和上位机的串口的连接方式是将所有单片机的输出线绑在一起与上位机连接,输入线也是同样由上位机分出来的。这些单片机主要是作显示用的,他们可以通过按键选择一些显示的内容,比如说是一个查询的选项,然后把这个查询的请求发送给上位机,上位机接到请求后作查询,把结果返回给单片机显示,这就是大概的流程。而我的这个单片机的查询请求不可能是一两个字符就能够表示完的(据我理解,串口一次只能接收一个字符),要是几台单片机同时向上位机发送请求会产生数据冲突,上位机收到的字符会交错,无法辨识。所以我采用由上位机作为主导者,向单片机发侦测命令,判断单片机有没有请求,有就处理这个请求,没有就向下一台单片机发侦测命令,就这样循环,而且对每个单片机每一个循环只处理一个请求。单片机这边就是把按键的请求依次存储起来,直到收到上位机的侦测命令才把队列的第一个请求发出给上位机处理。这时候出现的问题是为了保证不会漏过上位机的侦测信号,所以单片机的串口接收中断的优先级必定是最高的,要是在按键的同时上位机有侦测信号发送过来,这样的按键就会无效,而上位机循环侦测一轮(带处理)用不到1毫秒,也就是说侦测命令总是很频繁的,这样的话单片机上的按键不就总是很容易无效?就这个问题,应给怎样平衡接收数据的中断和按键的中断?
shornmao 2002-05-10
  • 打赏
  • 举报
回复
to magicsnake(北极狐):
你上次提到的问题是什么?方便的话请再重复一次。
如果这是你工作的项目,你们公司聘请我做技术咨询顾问算了,我可是串口软硬件专家啊。(啊?有点王婆卖瓜了,呵呵)
hotxu 2002-05-10
  • 打赏
  • 举报
回复
倒过来试一试,采用问答方式:
上位机监控串口,单片机按键后发送数据,然后监控串口,上位机收到
数据后再发送数据。当然数据包中要有地址信息。单片机只是在发送数据以后
才接受数据。
magicsnake 2002-05-10
  • 打赏
  • 举报
回复
不好意思各位,最近两天没有时间上网,来晚了。
to jzrbcb(jzrbcb)
谢谢你的意见,你的这个类我也有,不过我也还是很感谢你的积极参与。
分我是会给的,不用担心:)只要问题解决。我现在使用的正好是 shornmao(死猫)现在的主意,所以已经不存在数据冲突了,所以我上位机的接收线程可以不用关闭了,但是又出现了新的问题,就是上次我提到的单片机接收数据的问题,可以给我个主意么?

to jerry921(jerry)
谢谢,能不能也帮我想一下:)

to hotxu()
我也是想要停下来处理按键事件,但是单片机要处理按键事件就必须发出按键中断请求,这时候不能够接收数据,要是这时候上位机正好发来查询请求单片机就会收不到,怎样平衡这两者(接收或者响应按键)?我原先的思路是判断固定上位机的查询和处理请求的时间,比如说是10毫秒,这时候单片机处于等待状态,这时候可以处理按键请求,等10毫秒后再重新开串口接收数据。但是这样做很浪费时间,因为上位机处理请求有时快有时慢,我只能按最慢的计算,耗时太大,不实用。有没有别的解决方法?

to shornmao(死猫)
你所说的方法我正在使用,我们可真是不谋而和啊:)
不过出现了上述问题,有什么好意见么?谢谢啦!

另外:我看过网上好多前辈的串口线程类,似乎都没有使用 EV_CTS,EV_RTS这些宏,一般只用了EV_RXCHAR,那两个怎么用?
shornmao 2002-05-07
  • 打赏
  • 举报
回复
To 搂主:
为了避免多机通信之间的干扰问题,由PC机作为命令发出者,单片机作为响应发出者。
会话过程:
1)PC机发命令(数据包中包括单片机地址标示)
2)单片机接受属于自己的命令,执行,然后返回响应(数据包中包括自己的地址标示)
3)PC机接受响应,如果地址标示与发送不相符,则废弃响应,继续等待,如果超时,则出错。
liangzongqiang 2002-05-07
  • 打赏
  • 举报
回复
串口编程我用的是spcom控件,很简单、也很经典的。只要
设置好spcom的属性,在OnRecveiveData 中写接受事件,
在OnSendData中写发送数据的事件,在Form1的OnCreate中写
上打开串口的事件,在Form1的OnClose中写上关闭串口的
事件即可,根本不需要那么复杂,Spcom控件也很好找。
jzrbcb 2002-05-07
  • 打赏
  • 举报
回复
int TCommPort::ReadBytes(BYTE *buffer, unsigned int MaxBytes)
{ //读字节函数
VerifyOpen();
DWORD bytes_read;

if(!ReadFile(m_hCom,buffer,MaxBytes,&bytes_read,NULL))
throw ECommError(ECommError::READ_ERROR);

// add a null terminate if bytes_read < byteCount
// if the two are equal, there is no space to put a null terminator
if(bytes_read < MaxBytes)
buffer[bytes_read]='\0';

return bytes_read;
}
int TCommPort::ReadString(char *str, unsigned int MaxBytes)
{ //读字符串函数
VerifyOpen();

if(MaxBytes == 0u)
return 0;
str[0]='\0';
if(BytesAvailable() ==0)
return 0;
BYTE NewChar;
unsigned int Index=0;
while(Index < MaxBytes)
{
NewChar = GetByte();

// if the byte is a \r or \n, don't add it to the string
if( (NewChar != '\r') && (NewChar != '\n'))
{
str[Index] = (char) NewChar;
Index++;
}
if(NewChar == '\r')
{
str[Index] = '\0';
return Index +1;
}
}
str[MaxBytes-1]='\0';
return MaxBytes;
}
void TCommPort::DiscardBytes(unsigned int MaxBytes)
{ //删除字节函数
VerifyOpen();
if(MaxBytes == 0)
return;

BYTE *dummy= new BYTE[MaxBytes];
ReadBytes(dummy, MaxBytes);
delete []dummy;
}
void TCommPort::PurgeCommPort(void) //丢弃输入,输出缓冲区全部数据
{
VerifyOpen();
if(!PurgeComm(m_hCom,PURGE_RXCLEAR))
//API,终止读全部输入缓冲区,m_hCom通讯句柄,
//PURGE_RXCLEAR不管读操作是否完成,终止读缓冲区操作
throw ECommError(ECommError::PURGECOMM);
}
void TCommPort::FlushCommPort(void)
{
VerifyOpen();
if(!FlushFileBuffers(m_hCom)) //API,清除输出缓冲区数据,m_hCom文件句柄
throw ECommError(ECommError::FLUSHFILEBUFFERS);
}
void TCommPort::PutByte(BYTE value) //输出一个字节函数
{
VerifyOpen();
DWORD dummy;
if(!WriteFile(m_hCom,&value,1,&dummy,NULL))
//API,m_hCom句柄,&value写数据的指针,1表示写一个字节,
//&dummy写字节数的指针,NULL表示层叠结构为空
throw ECommError(ECommError::WRITE_ERROR);
}
BYTE TCommPort::GetByte() //读输入缓冲区一个字节
{
VerifyOpen();
DWORD dummy;
BYTE value;
if(!ReadFile(m_hCom,&value,1,&dummy,NULL))
//API,m_hCom句柄,&value读数据的指针,1表示读一个字节,
//&dummy读字节数的指针,NULL表示层叠结构为空
throw ECommError(ECommError::READ_ERROR);
return value;
}
unsigned int TCommPort::BytesAvailable(void) //可用到的字节函数
{
VerifyOpen();
COMSTAT comstat; //COMSTAT结构包含通讯状态
DWORD dummy;
if(!ClearCommError(m_hCom, &dummy, &comstat))
//API,m_hCom通讯句柄,&dummy错误代码指针,&comstat缓冲区状态指针
throw ECommError(ECommError::CLEARCOMMERROR);
return comstat.cbInQue; //返回输入缓冲区字节
}
void TCommPort::SetCommPort(const std::string & port) //设置端口函数
{
VerifyClosed();
m_CommPort = port;}
std::string TCommPort::GetCommPort(void) //获得端口名函数
{
return m_CommPort;
}
void TCommPort::GetCommProperties(COMMPROP &properties) //获得端口属性函数
{
VerifyOpen();
COMMPROP prop; //COMMPROP结构,返回通讯驱动程序的有关信息
ZeroMemory(&prop, sizeof(COMMPROP));
//API,&prop目的地址,COMMPROP大小
::GetCommProperties(m_hCom, &prop);
properties = prop;
}
做一后台线程类。
jzrbcb 2002-05-07
  • 打赏
  • 举报
回复
赶快加分!!!!!!
void TCommPort::SetByteSize(BYTE newByteSize) //设置端口缓冲区大小函数
{
BYTE oldByteSize = m_dcb.ByteSize; // make a backup of the old byte size
// 保存以前的缓冲区尺寸
m_dcb.ByteSize = newByteSize; // assign new size
// 重新设置缓冲器尺寸
if(m_CommOpen) // check for open comm port
{
if(!SetCommState(m_hCom,&m_dcb)) // try to set the new comm settings
{ // if failure
m_dcb.ByteSize = oldByteSize; // restore old byte size
// 如果设置未成功恢复以前的缓冲区尺寸
throw ECommError (ECommError::BAD_BYTESIZE); // bomb out
}
}
}
void TCommPort::SetParity(BYTE newParity) //设置端口奇偶为函数
{
BYTE oldParity = m_dcb.Parity; // make a backup of the old parity
//保存上一个奇偶位
m_dcb.Parity = newParity; // assign new parity
//设置新的奇偶位
if(m_CommOpen) // check for open comm port
{ // 判定串口是否打开
//SetCommState用于设置串口的所有可配置参数
//m_hCom设置串口句柄,m_dcb指向dcb结构指针,成功返回true,否则返回false
if(!SetCommState(m_hCom,&m_dcb)) // try to set the new comm settings
{ // if failure 表示设置参数失败
m_dcb.Parity = oldParity; // restore old parity
// 恢复设置前设置前的奇偶位
throw ECommError(ECommError::BAD_PARITY); // bomb out
// 抛出异常
}
}
}
void TCommPort::SetStopBits(BYTE newStopBits) //设置停止位函数
{
BYTE oldStopBits = m_dcb.StopBits; // make a backup of old #of stop bits
m_dcb.StopBits = newStopBits; // assign new # of stop bits

if(m_CommOpen) // check for open comm port
{
if(!SetCommState(m_hCom,&m_dcb)) // try to set the new comm settings
{ // if failure
m_dcb.StopBits = oldStopBits; // restore old # of stop bits
throw ECommError(ECommError::BAD_STOP_BITS); // bomb out
}
}
}
unsigned int TCommPort::GetBaudRate(void) //获得波特率函数
{
return m_dcb.BaudRate;
}

BYTE TCommPort::GetByteSize(void) //获得端口字节尺寸函数函数
{
return m_dcb.ByteSize;
}

BYTE TCommPort::GetParity(void) //获得奇偶位函数
{
return m_dcb.Parity;
}

BYTE TCommPort::GetStopBits(void) //获得停止位函数
{
return m_dcb.StopBits;
}

void TCommPort::WriteBuffer(BYTE *buffer, unsigned int ByteCount)
{ //写缓冲区函数
VerifyOpen();
DWORD dummy;
if( (ByteCount == 0) || (buffer == NULL))
return;

if(!WriteFile(m_hCom,buffer,ByteCount,&dummy,NULL))
throw ECommError(ECommError::WRITE_ERROR);
}
void TCommPort::WriteBufferSlowly(BYTE *buffer, unsigned int ByteCount)
{ //写缓冲区,清除缓冲区函数
VerifyOpen();
DWORD dummy;
BYTE *ptr = buffer;

for (unsigned int j=0; j<ByteCount; j++)
{
if(!WriteFile(m_hCom,ptr,1,&dummy,NULL))
throw ECommError(ECommError::WRITE_ERROR);

// Use FlushCommPort to wait until the character has been sent.
FlushCommPort(); //清除缓冲区
++ptr;
}
}

void TCommPort::WriteString(const char *outString)
{ //写字符串函数
VerifyOpen();

DWORD dummy;
if(!WriteFile(m_hCom,outString, strlen(outString),&dummy,NULL))
throw ECommError(ECommError::WRITE_ERROR);
}
shornmao 2002-05-07
  • 打赏
  • 举报
回复
to jzrbcb(jzrbcb) :
你把网上下载的源代码的注释换成中文,然后宣称这是自己编的,似乎有点那个吧?
还不如直接把borland代码中心的这个连接给人家,让他自己去下载。
这个类我用过,不好意思。:Q
jzrbcb 2002-05-07
  • 打赏
  • 举报
回复
赶快加分!!!!!
#ifdef __BORLANDC__
#include <vcl.h>
#pragma hdrstop
#endif

#include "comm.h"

ECommError::ECommError( ErrorType error)
: Error(error),
Errno(GetLastError())
{
}

////////////////////////////////////////////////////////////////////////////////
///// TCommPort::TCommPort() (constructor)
/////
TCommPort::TCommPort() //构造函数
:m_CommOpen(false),
m_CommPort("COM1"),
m_hCom(0)
{
// initialize the comm port to N81 9600 baud communications. These values
// will be used to initialize the port if OpenCommPort is called before any
// of the SetXXXX functions are called.
m_dcb.DCBlength = sizeof(DCB); //初始化DCB结构的大小
m_dcb.BaudRate =9600; //初始化DCB结构的波特率为9600
m_dcb.ByteSize =8; //初始化UART发送和接收8位数
m_dcb.Parity =NOPARITY; //NOPARITY and friends are #defined in windows.h
//无奇偶位
m_dcb.StopBits =ONESTOPBIT; //ONESTOPBIT is also from windows.h
//无停止位
}
///// end of TCommPort::TCommPort (constructor)
////////////////////////////////////////////////////////////////////////////////

TCommPort::~TCommPort() //析构函数
{
if(m_CommOpen) //假如串口打开则关闭串口
CloseCommPort();
}


////////////////////////////////////////////////////////////////////////////////
///// TCommPort::OpenCommPort()

void TCommPort::OpenCommPort(void) //打开端口函数
{
if(m_CommOpen) // if already open, don't bother
return;

// we need to get the default settings while preserving the settings
// that we override. The DCB struct has 20 or so members. We override 4.
// Make of copy of the settings we care about.
DCB tempDCB;
tempDCB.BaudRate = m_dcb.BaudRate;
tempDCB.ByteSize = m_dcb.ByteSize;
tempDCB.Parity = m_dcb.Parity;
tempDCB.StopBits = m_dcb.StopBits;

m_hCom = CreateFile(m_CommPort.c_str(), //打开串口名
GENERIC_READ | GENERIC_WRITE, //设置文件读写
0, /* comm devices must be opened w/exclusive-access */
//文件共享选项在串口中必须设为零
NULL, /* no security attrs */
//安全性能设为NULL
OPEN_EXISTING, /* comm devices must use OPEN_EXISTING */
//通讯中不能创建新串口,只能使用现有参数
0, /* not overlapped I/O */
//不使用重叠I/O
NULL /* hTemplate must be NULL for comm devices */
//串口使用中无意义
);

// If CreateFile fails, throw an exception. CreateFile will fail if the
// port is already open, or if the com port does not exist.
if(m_hCom == INVALID_HANDLE_VALUE) //CreateFile()失败
throw ECommError(ECommError::OPEN_ERROR);


// Now get the DCB properties of the port we just opened
if(!GetCommState(m_hCom,&m_dcb)) //如果不能获得串口状态则关闭串口
{
// something is hay wire, close the port and return
CloseHandle(m_hCom);
throw ECommError(ECommError::GETCOMMSTATE);
}

// dcb contains the actual port properties. Now copy our settings into this dcb
m_dcb.BaudRate = tempDCB.BaudRate;
m_dcb.ByteSize = tempDCB.ByteSize;
m_dcb.Parity = tempDCB.Parity;
m_dcb.StopBits = tempDCB.StopBits;

// now we can set the properties of the port with our settings.
if(!SetCommState(m_hCom,&m_dcb)) //如果不能获得串口状态则关闭串口
{
// something is hay wire, close the port and return
CloseHandle(m_hCom);
throw ECommError(ECommError::SETCOMMSTATE);
}

// set the intial size of the transmit and receive queues. These are
// not exposed to outside clients of the class either. Perhaps they should be?
// I set the receive buffer to 32k, and the transmit buffer to 9k (a default).
// 通过SetupComm函数初始化端口,默认接收缓冲区32K,发送缓冲区9K
// SetupComm函数初始化端口成功位真,否则未假
if(!SetupComm(m_hCom, 1024*32, 1024*9))
{
// something is hay wire, close the port and return
CloseHandle(m_hCom);
throw ECommError(ECommError::SETUPCOMM);
}

// These values are just default values that I determined empirically.
// Adjust as necessary. I don't expose these to the outside because
// most people aren't sure how they work (uhhh, like me included).
m_TimeOuts.ReadIntervalTimeout = 15; //以毫秒为单位的空闲现超时
m_TimeOuts.ReadTotalTimeoutMultiplier = 1; //字符间的超时值
m_TimeOuts.ReadTotalTimeoutConstant = 250; //与上一字符相加为总超时
m_TimeOuts.WriteTotalTimeoutMultiplier = 1;
m_TimeOuts.WriteTotalTimeoutConstant = 250;
if(!SetCommTimeouts(m_hCom, &m_TimeOuts))
{
// something is hay wire, close the port and return
CloseHandle(m_hCom);
throw ECommError(ECommError::SETCOMMTIMEOUTS);
}

// if we made it to here then success
m_CommOpen = true; //如果到这里说明已经打开端口
}
///// end of TCommPort::OpenCommPort
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////

void TCommPort::CloseCommPort(void) //关闭断口函数
{
if(!m_CommOpen) // if already closed, return
return;

if(CloseHandle(m_hCom) != 0) // CloseHandle is non-zero on success
{
m_CommOpen = false;
}
else
throw ECommError(ECommError::CLOSE_ERROR);
}
///// end of TCommPort::CloseCommPort
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////

void TCommPort::SetCommDCBProperties(DCB &properties) //设置DCB属性函数
{
// we need to act differently based on whether the port is already open or
// not. If it is open, then attempt to change the ports properties.
// If not open, then just make a copy of the new properties.
if(m_CommOpen) // port is open
{
if(!SetCommState(m_hCom,&properties)) // try to set the state directly
throw ECommError(ECommError::SETCOMMSTATE); // bomb out if failed
else // copy the new properties
m_dcb = properties; // if success
}
else // comm port is not open
m_dcb = properties; // best we can do is save a copy
}
///// end of TCommPort::SetCommProperties()
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
void TCommPort::GetCommDCBProperties(DCB &properties) //获得DCB属性函数
{
properties = m_dcb;
}
///// end of TCommPort::GetCommDCBProperties()
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
///// TCommPort::SetBaudRate()
/////
void TCommPort::SetBaudRate(unsigned int newBaud) //设置波特率函数
{
unsigned int oldBaudRate = m_dcb.BaudRate; // make a backup of the old baud rate
m_dcb.BaudRate = newBaud; // assign new rate

if(m_CommOpen) // check for open comm port
{
if(!SetCommState(m_hCom,&m_dcb)) // try to set the new comm settings
{ // if failure
m_dcb.BaudRate = oldBaudRate; // restore old baud rate
throw ECommError(ECommError::BAD_BAUD_RAT
jzrbcb 2002-05-07
  • 打赏
  • 举报
回复
这是我最近正在做的一个串行通讯类,封装了大部分DCB结构希望对你有帮助,请赶快给我打分!!!

#ifndef COMM_H
#define COMM_H

#include <windows.h>
#include <string>
class ECommError
{
public:
enum ErrorType //定义枚举错误类型
{
BAD_SERIAL_PORT ,
BAD_BAUD_RATE ,
BAD_PORT_NUMBER ,
BAD_STOP_BITS ,
BAD_PARITY ,
BAD_BYTESIZE ,
PORT_ALREADY_OPEN ,
PORT_NOT_OPEN ,
OPEN_ERROR ,
WRITE_ERROR ,
READ_ERROR ,
CLOSE_ERROR ,
PURGECOMM ,
FLUSHFILEBUFFERS ,
GETCOMMSTATE ,
SETCOMMSTATE ,
SETUPCOMM ,
SETCOMMTIMEOUTS ,
CLEARCOMMERROR
};

ECommError( ErrorType error);

ErrorType Error;
DWORD Errno; // Errno == return value from GetLastError. Can be used with FormatMessage
};

class TCommPort
{
public:
TCommPort(); //构造函数
~TCommPort(); //析构函数
void OpenCommPort(void); //打开串口函数
void CloseCommPort(void); //断开串口函数

void SetCommPort(const std::string & port); //设置打开串口名函数
std::string GetCommPort(void); //获得正在使用串口名函数
void SetBaudRate(unsigned int newBaud); //设置波特率函数
unsigned int GetBaudRate(void); //获得正在使用串口波特率函数
void SetParity(BYTE newParity); // see source for possible values
// 设置端口奇偶为函数
BYTE GetParity(void); //获得奇偶位函数
void SetByteSize(BYTE newByteSize); //设置端口缓冲区大小函数
BYTE GetByteSize(void);
void SetStopBits(BYTE newStopBits);
BYTE GetStopBits(void);

void SetCommDCBProperties(DCB &properties); // avoid using DCB interface
void GetCommDCBProperties(DCB &properties); // Use SetBaudRate et al instead

void GetCommProperties(COMMPROP &properties);

void WriteString(const char *outString);
void WriteBuffer(BYTE *buffer, unsigned int ByteCount);
void WriteBufferSlowly(BYTE *buffer, unsigned int ByteCount);
int ReadString(char *string, unsigned int MaxBytes);
int ReadBytes(BYTE *bytes, unsigned int byteCount);
void DiscardBytes(unsigned int MaxBytes);
void PurgeCommPort(void);
void FlushCommPort(void);

void PutByte(BYTE value);
BYTE GetByte();
unsigned int BytesAvailable(void);

bool GetConnected()
{
return m_CommOpen;
}

HANDLE GetHandle() // allow access to the handle in case the user needs to
{ // do something hardcore. Avoid this if possible
return m_hCom;
}

private:
// Note: the destructor of the commport class automatically closes the
// port. This makes copy construction and assignment impossible.
// That is why I privatize them, and don't define them. In order
// to make copy construction and assignment feasible, we would need
// to employ a reference counting scheme.
TCommPort(const TCommPort &); // privatize copy construction
TCommPort & operator=(const TCommPort&); // and assignment.

void VerifyOpen() //校验端口打开函数
{
if(!m_CommOpen)
throw ECommError(ECommError::PORT_NOT_OPEN) ;
}
void VerifyClosed() //校验端口关闭函数
{
if(m_CommOpen)
throw ECommError(ECommError::PORT_ALREADY_OPEN) ;
}

// this stuff is private because we want to hide these details from clients
bool m_CommOpen; //布尔变量判定串口是否打开
COMMTIMEOUTS m_TimeOuts; //COMMTIMEOUTS结构设定读写时的超时值
std::string m_CommPort;
DCB m_dcb; // a DCB is a windows structure used for configuring the port
HANDLE m_hCom; // handle to the comm port.
};

#endif


jzrbcb 2002-05-07
  • 打赏
  • 举报
回复
在C++ Builder中用MSCOMM32实现与单片机的串行通信,用MSCOMM32比较简单,可以完成大多数的功能。
  用C++ Builder来编写串行通信程序,我们需要用到几个Windows API函数而不像在DOS下那样直接操作串口和中断芯片。这几个函数有OpenFile、CloseFile,GetCommState、SetCommState等,另外要比较熟悉DCB结构。Microsoft的Visual Basic 中有一个OCX控件MSComm32,在VB中用它做串行通信程序很方便,将它装入Builder 3中,它的Input和Output属性是UnKnown,即Builder 3不认识MSComm32的这两个属性。升级到Borland 的C++ Builder4.0,在Object Inspector中将不再看到这两个属性,但它们仍然存在,这两个属性的类型是OleVariant。使用这种类型方法如下:

  在要发送数据时,我们声明一个发送数据缓冲区,然后重置它的大小,填充它的元素,发送它,例如:

  unsigned char buff[200];//请声明为全局变量,应该是动态数组,否则会出现乱码

  OleVariant TxBuff;//声明一个OleVariant 变量

  TxBuff=VarArrayCreate(OPENARRAY(int,(0,200)),varByte);//重置它的大小,为0~n,int 为n的类型。

  //varByte为TxBuff每一个元素的类型。
for(int i=0;ibr<200+1;i++)TxBuff.PutElement(buff[i],i);

   //填充元素,其中buff为你定义的一个固定数组,其中有你要发的数据。

  MSComm1->Output=TxBuff;//发送数据,MSComm1为你放在窗体上的一个MSComm32控件。

  按收数据时请看下面的例子:

  unsigned char buff[200];//声明一个存储接收数据的缓冲区,全局变量

  int ByteNum;//收到的字节数

  int BuffPtr;//接收缓冲区的指针,请声明为全局变量,

  OleVariant RxBuff;//一个用于接收的OleVariant变量,

  if(MSComm1->InBufferCount>0)RxBuff=Communica1->Input;//如果缓冲区中有多于一个字节的数据

  ByteNum=RxBuff.ArrayHighBound(1);//将实际读的字节数取出
for(int i=0;ibr<ByteNum+1;i++)
{buff[BuffPtr++]=RxBuff.GetElement(i);}//将接收数据读入自己的缓冲区>

  在Object Inspector的Event标签中只有一个事件OnComm,这个事件在MSComm32控件收到数据时会被调用,但你必须设置RThreshold属性。这是一个门槛,表示收到几个字节就发送通知消息,如果为零,就不发送通知消息,这样你的OnComm函数就不会得到执行,TThreshold是发送门槛,不要忘记Settings。

  另外值得注意的是MSComm32的OnComm事件不是很准确,有时候会丢失,你不能过分依赖这个事件,否则,经常发生的不是发不出数据,就是接收不到数据,最好的办法是使用一个定时控件,需要的时候就去读MSComm32控件的缓冲区。
hotxu 2002-05-07
  • 打赏
  • 举报
回复
你处理完按键以后再接收数据不行吗?
加载更多回复(11)

13,825

社区成员

发帖
与我相关
我的任务
社区描述
C++ Builder相关内容讨论区
社区管理员
  • 基础类社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧