编辑
风紊
现役大学牲,半退休robomaster视觉队员
写在前面
本文章主要介绍的是如何通过开源的serial
库和虚拟串口实现上位机和下位机通信。
需求
假设下位机有这样一个数据报发送给上位机
struct DataRecv {char start = 's';TeamColor color = TeamColor::Blue;Mode mode = Mode::Armor;float speed = 20;float euler[3] = {}; //(0,1,2) = (yaw,roll,pitch)char shoot_bool = 0;char RuneFlag = 0; char unused[10] = {};char end = 'e';
}//TeamColor是一个char类型的迭代类,Mode也是一个char类型的迭代类
其他数据我都不需要,只需要欧拉角,也就是一个浮点数数组,euler[3]
解决方法
serial库的github仓库
先clone下来,安装,得到头文件和动态库。
我们来看看serial库的构造函数:
class Serial {
public:/*!* Creates a Serial object and opens the port if a port is specified,* otherwise it remains closed until serial::Serial::open is called.** \param port A std::string containing the address of the serial port,* which would be something like 'COM1' on Windows and '/dev/ttyS0'* on Linux.** \param baudrate An unsigned 32-bit integer that represents the baudrate** \param timeout A serial::Timeout struct that defines the timeout* conditions for the serial port. \see serial::Timeout** \param bytesize Size of each byte in the serial transmission of data,* default is eightbits, possible values are: fivebits, sixbits, sevenbits,* eightbits** \param parity Method of parity, default is parity_none, possible values* are: parity_none, parity_odd, parity_even** \param stopbits Number of stop bits used, default is stopbits_one,* possible values are: stopbits_one, stopbits_one_point_five, stopbits_two** \param flowcontrol Type of flowcontrol used, default is* flowcontrol_none, possible values are: flowcontrol_none,* flowcontrol_software, flowcontrol_hardware** \throw serial::PortNotOpenedException* \throw serial::IOException* \throw std::invalid_argument*/Serial (const std::string &port = "" //需要打开的端口uint32_t baudrate = 9600, //设置波特率,缺省值位9600Timeout timeout = Timeout(), //打开超时的时间,缺省值时间为0bytesize_t bytesize = eightbits, //字节大小,缺省值位8位parity_t parity = parity_none, //奇偶校验位,默认无奇偶校验stopbits_t stopbits = stopbits_one, //停止位,缺省值为1位flowcontrol_t flowcontrol = flowcontrol_none);//流控制,默认五流控制,可选择软件流控制和硬件流控制
选项都设置好后,直接调用对象的open()方法就能打开串口通信。
读数据的方法:
size_t
Serial::read (uint8_t *buffer, size_t size)
{ScopedReadLock lock(this->pimpl_);return this->pimpl_->read (buffer, size);
}
所以我们只需要将数据包转为uint8_t的类型的存储格式指针,传给函数,并指定字节数size就能读取数据了。
通信实现文件test.cpp
的代码
#include <serial/serial.h>
#include <iostream>struct data_package
{char start = 's';char unused1[2];float speed = 20;float euler[3] = {}; //(0,1,2) = (yaw,roll,pitch)char shoot_bool = 0;char RuneFlag = 0; //char unused2[10] = {};char end = 'e';
} __attribute__((packed));
static_assert(sizeof(data_package) == 32);data_package data;
int main()
{std::cout << "helloworld" << std::endl;serial::Serial ser; // 实例化一个串口的对象ser.setPort("/dev/serial_sdk"); // 设置串口设备ser.setBaudrate(115200); // 设置波特率try{ser.open(); // 打开串口while (true){std::cout << "number" << ser.available() << std::endl; // 读取到缓存区数据的字节数ser.read(reinterpret_cast<uint8_t *>(&data), 32);//将data_package类型结构体强制转换位uint8_t类型的指针,来接收32字节的数据std::cout << data.start << data.unused1[0] << data.unused1[1] << std::endl;std::cout << "(yaw,pitch,roll)" << data.euler[0] << " " << data.euler[1] << " " << data.euler[2] << std::endl;}}catch (std::exception &e){std::cerr << e.what() << std::endl;}
}
使用g++编译代码失败的话,可以参考关于库不在默认搜索路径时,g++链接库时找不到函数实现的问题。
如果要给设备起别名,可以参考Linux下给外部挂载的设备起别名,而不使用内核名称
Notice
如果结构体最后不接 attribute((packed)),经笔者测试,数据包大小变为36位。