C++ boost::asio::serial_port 串口通信类 使用 封装

2024-03-17 15:38

本文主要是介绍C++ boost::asio::serial_port 串口通信类 使用 封装,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

串口一旦存在后,C++ boost::asio就当串口为一种流(文件流 )来使用

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

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

#include  <boost/asio.hpp>

using namespace  boost::asio;

 

2 serial_port类实现串口通信步骤

(1) 定义io_service对象

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

io_service io_s;

 

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

serial_port sp(  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]向串口写数据

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

size_t  data_len;

boost::system::error_code ec;

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

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

 

[2]读/接收串口数据

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

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

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

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

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

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

void handle_read(  boost::system::error_code ec,std::size_t bytes_transferred );  //如果不使用bind,则async_read函数的回调函数必须为如此形式

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

ios.run();

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

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

eadline_timer timer(ios);

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

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

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

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

 

3 将boost串口通信封装成类

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

 

(1)类头文件

[cpp]  view plain copy print ?
  1. #include <iostream>  
  2. #include <boost/asio.hpp>  
  3. #include <boost/bind.hpp>  
  4.   
  5. using namespace std;  
  6. using namespace boost::asio;  
  7.   
  8. typedef string any_type;  
  9.   
  10.   
  11. class MySerialPort  
  12. {  
  13. private:  
  14.     //Initialize port  
  15.     bool init_port( const any_type port, const unsigned int char_size );  
  16. public:  
  17.     //Constructor  
  18.     //Destructor  
  19.         MySerialPort( const any_type &port_name );  
  20.     ~MySerialPort();  
  21.       
  22.     //Write some data to port  
  23.     void write_to_serial( const any_type data );  
  24.   
  25.     //Read data from port which write data just now  
  26.     void read_from_serial();  
  27.   
  28.     //The asyanc callback function of asyanc_read  
  29.     void handle_read( char buf[], boost::system::error_code ec,  
  30.         std::size_t bytes_transferred );  
  31.   
  32.     //To Call io_service::run function to call asyanc callback funciton  
  33.     void call_handle();  
  34.       
  35. private:  
  36.     //io_service Object  
  37.     io_service m_ios;  
  38.   
  39.     //Serial port Object  
  40.     serial_port *pSerialPort;  
  41.   
  42.     //For save com name  
  43.     any_type m_port;  
  44.   
  45.     //Serial_port function exception  
  46.     boost::system::error_code ec;  
  47. };  


(2)成员函数实现

[cpp]  view plain copy print ?
  1. #include "stdafx.h"  
  2. #include <string>  
  3. #include <vector>  
  4. #include "SerialCom.h"  
  5.   
  6.   
  7. //Define Constructor function  
  8. MySerialPort::MySerialPort( const any_type &port_name ):pSerialPort( NULL )  
  9. {  
  10.         pSerialPort = new serial_port( m_ios );  
  11.     if ( pSerialPort ){  
  12.         init_port( port_name, 8 );  
  13.     }  
  14. }   
  15.   
  16. //Define destructor function  
  17. MySerialPort::~MySerialPort()  
  18. {  
  19.        if( pSerialPort )  
  20.        {  
  21.                delete pSerialPort;  
  22.        }       
  23. }  
  24.   
  25.   
  26. //Initialize port  
  27. bool MySerialPort::init_port( const any_type port, const unsigned int char_size )  
  28. {  
  29.     //New not success  
  30.     if ( !pSerialPort ){  
  31.         return false;  
  32.     }  
  33.   
  34.         //Open Serial port object  
  35.         pSerialPort->open( port, ec );  
  36.       
  37.   
  38.     //Set port argument  
  39.     pSerialPort->set_option( serial_port::baud_rate( 9600 ), ec );  
  40.     pSerialPort->set_option( serial_port::flow_control( serial_port::flow_control::none ), ec );  
  41.     pSerialPort->set_option( serial_port::parity( serial_port::parity::none ), ec );  
  42.     pSerialPort->set_option( serial_port::stop_bits( serial_port::stop_bits::one ), ec);  
  43.     pSerialPort->set_option( serial_port::character_size( char_size ), ec);  
  44.   
  45.     return true;  
  46. }  
  47.   
  48.   
  49. //Define wirte_to_serial to write data to serial  
  50. void MySerialPort::write_to_serial( const any_type data )  
  51. {  
  52.         size_t len = write( *pSerialPort, buffer( data ), ec );  
  53.         cout << len << "\t" << data << "\n";  
  54. }  
  55.   
  56. void MySerialPort::handle_read( char buf[], boost::system::error_code ec,  
  57.     std::size_t bytes_transferred )  
  58. {  
  59.     cout << "\nhandle_read: ";  
  60.     cout.write(buf, bytes_transferred);  
  61. }  
  62.   
  63.   
  64.   
  65. //Read data from the serial  
  66. void MySerialPort::read_from_serial()  
  67. {  
  68.        char v[10];  
  69.        async_read( *pSerialPort, buffer(v), boost::bind( &MySerialPort::handle_read, this, v, _1, _2) );  
  70. }  
  71.   
  72.   
  73. //Call handle_read function when async function read complete or come out exception  
  74. void MySerialPort::call_handle()  
  75. {  
  76.         //There can use deadline_timer to cancle serial_port read data  
  77.   
  78.     //Wait for call callback function  
  79.     m_ios.run();  
  80. }  


(3) 使用类

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

[cpp]  view plain copy print ?
  1. // BoostSerialCommunication.cpp : Defines the entry point for the console application.  
  2. //  
  3. #ifdef _MSC_VER  
  4. #define _WIN32_WINNT        0X0501      
  5. #endif  
  6.   
  7. #define BOOST_REGEX_NO_LIB  
  8. #define BOOST_DATE_TIME_SOURCE  
  9. #define BOOST_SYSTEM_NO_LIB  
  10.   
  11. #include "stdafx.h"  
  12. #include "SerialCom.h"  
  13.   
  14.   
  15. int _tmain(int argc, _TCHAR* argv[])  
  16. try  
  17. {  
  18.      {  
  19.     MySerialPort my_Sp( "COM3");  
  20.     my_Sp.write_to_serial( "SerialPort" );  
  21.     my_Sp.read_from_serial();  
  22.     my_Sp.call_handle();  
  23.     getchar();  
  24.      }  
  25.     getchar();  
  26.     return 0;  
  27. }  
  28. catch(  std::exception &e )  
  29. {  
  30.        cout << e.what();  
  31.        getchar();  
  32. }  

 

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

 

此次笔记记录完毕。

这篇关于C++ boost::asio::serial_port 串口通信类 使用 封装的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/819384

相关文章

Java中String字符串使用避坑指南

《Java中String字符串使用避坑指南》Java中的String字符串是我们日常编程中用得最多的类之一,看似简单的String使用,却隐藏着不少“坑”,如果不注意,可能会导致性能问题、意外的错误容... 目录8个避坑点如下:1. 字符串的不可变性:每次修改都创建新对象2. 使用 == 比较字符串,陷阱满

Python使用国内镜像加速pip安装的方法讲解

《Python使用国内镜像加速pip安装的方法讲解》在Python开发中,pip是一个非常重要的工具,用于安装和管理Python的第三方库,然而,在国内使用pip安装依赖时,往往会因为网络问题而导致速... 目录一、pip 工具简介1. 什么是 pip?2. 什么是 -i 参数?二、国内镜像源的选择三、如何

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

Linux使用nload监控网络流量的方法

《Linux使用nload监控网络流量的方法》Linux中的nload命令是一个用于实时监控网络流量的工具,它提供了传入和传出流量的可视化表示,帮助用户一目了然地了解网络活动,本文给大家介绍了Linu... 目录简介安装示例用法基础用法指定网络接口限制显示特定流量类型指定刷新率设置流量速率的显示单位监控多个

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

C++初始化数组的几种常见方法(简单易懂)

《C++初始化数组的几种常见方法(简单易懂)》本文介绍了C++中数组的初始化方法,包括一维数组和二维数组的初始化,以及用new动态初始化数组,在C++11及以上版本中,还提供了使用std::array... 目录1、初始化一维数组1.1、使用列表初始化(推荐方式)1.2、初始化部分列表1.3、使用std::

C++ Primer 多维数组的使用

《C++Primer多维数组的使用》本文主要介绍了多维数组在C++语言中的定义、初始化、下标引用以及使用范围for语句处理多维数组的方法,具有一定的参考价值,感兴趣的可以了解一下... 目录多维数组多维数组的初始化多维数组的下标引用使用范围for语句处理多维数组指针和多维数组多维数组严格来说,C++语言没

在 Spring Boot 中使用 @Autowired和 @Bean注解的示例详解

《在SpringBoot中使用@Autowired和@Bean注解的示例详解》本文通过一个示例演示了如何在SpringBoot中使用@Autowired和@Bean注解进行依赖注入和Bean... 目录在 Spring Boot 中使用 @Autowired 和 @Bean 注解示例背景1. 定义 Stud