您的位置:首页 > 编程语言 > C语言/C++

C++ boost::asio::serial_port 串口通信类 使用 封装 [大三四八九月实习]

2013-09-21 18:12 489 查看
串口一旦存在后,C++boost::asio就当串口为一种流(文件流
)来使用


C++的BOOST库中,通信库都在asio下,串口类结构为boost::asio::serial_port。串口通信由asio组件的serial_port类完成。BOOST库下的串口通信serial_port类的使用跟网络通信相似[网络通信
],只是在进行串口通信前需要初始化串口。下面笔记使用步骤:

1serial_port类头文件和命名空间声明

#include<boost/asio.hpp>

usingnamespaceboost::asio;


2serial_port类实现串口通信步骤

(1)定义io_service对象

io_service对象是使用boost::asio库的必需要有的对象。

io_serviceio_s;


(2)创建串口对象,传入io_service对象,打开串口

serial_portsp(io_s,"COM1");

io_s是步骤(1)中定义的io_service对象,COM1为串口名(计算机-->属性-->设备管理器-->端口)。一旦串口被打开,则此串口就会被当成流来被使用

(1)如果串口端口不存在,则try-catch能够获取“系统找不到指定的文件”文件异常。
(2)如果串口端口没有和实际的串口连接,则try-catch能够获取“设备没有连接”的异常。
(3)如果在电脑之上连接一个串口线,则用此函数打开对应的端口(如COM4)就不会出现以上两个异常。如果不在此串口下挂一个设备,则对端口进行读写操作的时候会出现异常,如读串口数据时会在read函数这里卡住。但如果在串口下挂一个设备如51单片机学习板则此函数会返回并读出一定的数据回来(虽不一定正确)。如果将挂在串口下的设备开启,try-catch会捕获“连接到系统上的设备没有发挥作用”或者是read函数又开始不能返回。

(3)初始化

利用串口类对象初始化串口。串口类对象就是(2)中定义的sp,串口就是以上的参数COM1。

sp.set_option(serial_port::baud_rate(9600));//比特率

sp.set_option(serial_port::flow_control(serial_port::flow_control::none));//流量控制

sp.set_option(serial_port::parity(serial_port::parity::none));//奇偶校验

sp.set_option(serial_port::stop_bits(serial_port::stop_bits::one));//停止位

sp.set_option(serial_port::character_size(8));//数据位


(4)调用serial_prot类的成员函数进行串口通信

如先前所述,serial_port类成功打开串口后,串口对于serial_port类来说就成了一种文件流。咱们就可以使用serial_port类中的成员函数往“流”写和读数据了。

向串口发送数据时是采用boost::asio::serial_port下含write字符串的函数将程序中的数据写入到串口流,接收串口数据时是用含read字符串的函数从串口读出数据,再放到程序变量中。比如用串口对象调用的write_some(),read_some()一类属于serial_port的成员函数,还有在函数内部指明串口对象的write(),read()等非serial_port类的成员函数,但它们是boost::asio下的函数。看名就知道“只写/读一些”的函数(比如读到空格或者其它特殊字符就会停止读下去)不如“写/读”函数功能完好。所以,咱都还是用write(),read()一类的函数从串口写、读完整的数据串吧。



[1]向串口写数据

write有4个重载(overload)函数,有2种都有接收异常的参数。[VS中选中write函数-->右键-->Go
ToDefinitionF12]

size_tdata_len;

boost::system::error_codeec;

data_len=write(*pSerialPort,buffer(data),ec);

write()的第一个参数表示serial_port对象,第二个参数是写向(传输)串口的数据,第三个参数是boost库下的异常参数,如果write函数传输数据发生什么异常就会自动抛出此异常给catch。向串口成功传进数据则返回写入数据data的长度,buffer是boost库的函数,一般的参数都需要buffer一下。



[2]读/接收串口数据

如果直接用read函数来读取串口流中的数据,则read函数在读满变量内存后才会返回(chara[6],则会读满6个后才会返回
)。而且返回输出字符串的时候还是乱码。使用read函数就会阻塞后面代码的执行。

可以使用异步读取/接收串口的方式:就算未完全读/接收到串口数据,异步读取函数依旧会马上返回执行后续代码,等串口数据读取完毕或者发生异常时io_service::run()函数会等待异步读取串口的数据操作,然后调用异步函数指定的回调函数。

提到异步操作,它包含三部分:

异步操作函数。
异步操作函数以形参方式指定的回调函数。
io_service::run()函数的调用。

这三个部分对应的执行流程为:

程序执行到异步操作函数处,异步操作函数立即返回,程序继续执行后续代码。
异步操作函数的功能完成[如读取到与设定缓冲区长度大小一致的数据时]或者出现异常时,io_service::run()函数机制会自动调用异步操作函数指定的回调函数。如果不io_service::run()函数,异步操作函数依然可以实现异步操作流程,只是回调函数不会被执行。

voidhandle_read(boost::system::error_codeec,std::size_tbytes_transferred);//如果不使用bind,则async_read函数的回调函数必须为如此形式

async_read(*pPort,buffer(v),handle_read);



ios.run();

不过,输出cout<<“\n”<<v;为乱码[在我挂一个单片机设备在串口的情形下]

在此种情形下,虽然可以使异步操作函数后续代码被执行。但在没有发生异常或者没有读满设定缓冲区大小时,回调函数不会被调用。所以可以使用boost库下的deadline_timer为异步操作定时,如果超过定时的时间就结束异步函数的异步操作去执行回调函数。

eadline_timertimer(ios);

timer.expires_from_now(boost::posix_time::millisec(100));

//超时后调用pSerialPort的cancel()方法放弃读取更多字符

timer.async_wait(boost::bind(&serial_port::cancel,boost::ref(*pSerialPort)));

以面向过程的方式实现这些步骤可以实现以上功能,但是将其封装称为类的时候会出点错误。



3将boost串口通信封装成类

将其封装为类时,boost的用法就要遵循按照包装成员函数的套路出发,bind用法。



(1)类头文件

#include<iostream>
#include<boost/asio.hpp>
#include<boost/bind.hpp>

usingnamespacestd;
usingnamespaceboost::asio;

typedefstringany_type;

classMySerialPort
{
private:
//Initializeport
boolinit_port(constany_typeport,constunsignedintchar_size);
public:
//Constructor
//Destructor
MySerialPort(constany_type&port_name);
~MySerialPort();

//Writesomedatatoport
voidwrite_to_serial(constany_typedata);

//Readdatafromportwhichwritedatajustnow
voidread_from_serial();

//Theasyanccallbackfunctionofasyanc_read
voidhandle_read(charbuf[],boost::system::error_codeec,
std::size_tbytes_transferred);

//ToCallio_service::runfunctiontocallasyanccallbackfunciton
voidcall_handle();

private:
//io_serviceObject
io_servicem_ios;

//SerialportObject
serial_port*pSerialPort;

//Forsavecomname
any_typem_port;

//Serial_portfunctionexception
boost::system::error_codeec;
};



(2)成员函数实现

#include"stdafx.h"
#include<string>
#include<vector>
#include"SerialCom.h"

//DefineConstructorfunction
MySerialPort::MySerialPort(constany_type&port_name):pSerialPort(NULL)
{
pSerialPort=newserial_port(m_ios);
if(pSerialPort){
init_port(port_name,8);
}
}

//Definedestructorfunction
MySerialPort::~MySerialPort()
{
if(pSerialPort)
{
deletepSerialPort;
}
}

//Initializeport
boolMySerialPort::init_port(constany_typeport,constunsignedintchar_size)
{
//Newnotsuccess
if(!pSerialPort){
returnfalse;
}

//OpenSerialportobject
pSerialPort->open(port,ec);


//Setportargument
pSerialPort->set_option(serial_port::baud_rate(9600),ec);
pSerialPort->set_option(serial_port::flow_control(serial_port::flow_control::none),ec);
pSerialPort->set_option(serial_port::parity(serial_port::parity::none),ec);
pSerialPort->set_option(serial_port::stop_bits(serial_port::stop_bits::one),ec);
pSerialPort->set_option(serial_port::character_size(char_size),ec);

returntrue;
}

//Definewirte_to_serialtowritedatatoserial
voidMySerialPort::write_to_serial(constany_typedata)
{
size_tlen=write(*pSerialPort,buffer(data),ec);
cout<<len<<"\t"<<data<<"\n";
}

voidMySerialPort::handle_read(charbuf[],boost::system::error_codeec,
std::size_tbytes_transferred)
{
cout<<"\nhandle_read:";
cout.write(buf,bytes_transferred);
}

//Readdatafromtheserial
voidMySerialPort::read_from_serial()
{
charv[10];
async_read(*pSerialPort,buffer(v),boost::bind(&MySerialPort::handle_read,this,v,_1,_2));
}

//Callhandle_readfunctionwhenasyncfunctionreadcompleteorcomeoutexception
voidMySerialPort::call_handle()
{
//Therecanusedeadline_timertocancleserial_portreaddata

//Waitforcallcallbackfunction
m_ios.run();
}



(3)使用类

VS下的控制台程序就可以,当然首先得配置VS2010与boost库环境:见VS2010BOOST库配置。

//BoostSerialCommunication.cpp:Definestheentrypointfortheconsoleapplication.
//
#ifdef_MSC_VER
#define_WIN32_WINNT0X0501
#endif

#defineBOOST_REGEX_NO_LIB
#defineBOOST_DATE_TIME_SOURCE
#defineBOOST_SYSTEM_NO_LIB

#include"stdafx.h"
#include"SerialCom.h"

int_tmain(intargc,_TCHAR*argv[])
try
{
{
MySerialPortmy_Sp("COM3");
my_Sp.write_to_serial("SerialPort");
my_Sp.read_from_serial();
my_Sp.call_handle();
getchar();
}
getchar();
return0;
}
catch(std::exception&e)
{
cout<<e.what();
getchar();
}



如果async_read读取的数据段长度大于写入串口的数据,则可以正确执行read_from_serial()后续代码,而回调函数handle_read不会被执行指导异常发生或者读满v的长度为止。但整个程序不会为止而阻塞。可以用boost库下的deadline_timer为serial_port读取数据定时,当时间到达时不再继续读取数据即可,立马就会调用回调函数handle_read。不过从串口读出来的数据输出到屏幕之上时为乱码,还没找到原因。



BoostNoteOver.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: