C++ 之 串口通讯封装类
背景
最近在做一个动态库,涉及到
硬件
和linux
通讯,通讯方式使用串口
和tcp/ip
,故将相关通讯方式封装成了类。这里记录下,方便后续查找,有用的可以直接拿走。
环境
Linux 64位
g++ 4.8.5
串口
头文件
#pragma onceclass CSerial
{
public:CSerial(void);~CSerial(void);bool OpenSerialPort(const char* port, int BaudRate = 115200, int databits = 8, int stopbit = 0, int parity = 0, int timeOut = 10);bool ReadData(char* data, int len, int& readLen, int isDebug = 0);bool SendData(const char* data, int len, int isDebug = 0);bool CloseSerialPort();public:int m_fd; // Linux 下用文件描述符
};
源文件
#include "serial.h"
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <fstream>
#include <cstring>
#include <cstdio>
#include <cerrno>
#include <iostream>using namespace std;CSerial::CSerial(void)
{m_fd = -1;
}CSerial::~CSerial(void)
{CloseSerialPort();
}/******************************************************************************************** 功能 : 打开串口* port : 串口号, 如("/dev/ttyS0")* baud_rate: 波特率* date_bits: 数据位(有效范围4~8)* stop_bit : 停止位* parity : 奇偶校验。默认为无校验。NOPARITY 0; ODDPARITY 1;EVENPARITY 2;********************************************************************************************/
bool CSerial::OpenSerialPort(const char* port, int BaudRate, int databits, int stopbit, int parity, int timeOut)
{//打开串口m_fd = open(port, O_RDWR | O_NOCTTY | O_NDELAY);if (m_fd == -1) {trace_log(ERROR_LOG, 0, "open %s is fail, check the com is available!", port);return false;}// 设置为阻塞fcntl(m_fd, F_SETFL, 0);//获取串口默认配置struct termios options;if (tcgetattr(m_fd, &options) == -1) {trace_log(ERROR_LOG, 0, "tcgetattr error: %s\n", strerror(errno));CloseSerialPort();return false;}// 设置波特率speed_t speed;switch (BaudRate) {case 9600: speed = B9600; break;case 19200: speed = B19200; break;case 38400: speed = B38400; break;case 57600: speed = B57600; break;case 115200: speed = B115200; break;default: speed = B115200; break;}cfsetispeed(&options, speed);cfsetospeed(&options, speed);// 设置数据位options.c_cflag &= ~CSIZE;switch (databits) {case 5: options.c_cflag |= CS5; break;case 6: options.c_cflag |= CS6; break;case 7: options.c_cflag |= CS7; break;case 8: default: options.c_cflag |= CS8; break;}// 设置校验位switch (parity) {case 0: // 无校验options.c_cflag &= ~PARENB;options.c_iflag &= ~INPCK;break;case 1: // 奇校验options.c_cflag |= PARENB;options.c_cflag |= PARODD;options.c_iflag |= INPCK;break;case 2: // 偶校验options.c_cflag |= PARENB;options.c_cflag &= ~PARODD;options.c_iflag |= INPCK;break;default:options.c_cflag &= ~PARENB;options.c_iflag &= ~INPCK;break;}// 设置停止位if (stopbit == 2)options.c_cflag |= CSTOPB; // 2位停止位elseoptions.c_cflag &= ~CSTOPB; // 1位停止位// 其他设置options.c_cflag |= (CLOCAL | CREAD);options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // 原始模式options.c_oflag &= ~OPOST; // 原始输出options.c_iflag &= ~(IXON | IXOFF | IXANY); // 关闭软件流控// 设置超时options.c_cc[VTIME] = 10 * timeOut; // 读超时10*0.1s=1soptions.c_cc[VMIN] = 0; // 最小读取字符数tcflush(m_fd, TCIFLUSH);if (tcsetattr(m_fd, TCSANOW, &options) != 0) {trace_log(ERROR_LOG, 0, "tcsetattr error: %s\n", strerror(errno));CloseSerialPort();return false;}return true;
}/********************************************************************************************* 功能 : 通过串口发送一条数据********************************************************************************************/
bool CSerial::SendData(const char* data, int len, int isDebug) {if (m_fd == -1) {trace_log(ERROR_LOG, 0, "Serial port is not available! try to open first.");return false;}trace_log(DEBUG_LOG, 0, "starting sending data");//写串口int sendLen = write(m_fd, data, len);if (sendLen < 0) {trace_log(ERROR_LOG, 0, "Send Data is wrong! error [%s]", strerror(errno));return false;}if (isDebug != 0){trace_log(DEBUG_LOG, 0, "Send data:");trace_log(DEBUG_LOG, sendLen, data);}trace_log(DEBUG_LOG, 0, "Send Data is successfully!");return len == sendLen;
}bool CSerial::CloseSerialPort()
{if (m_fd != -1) {close(m_fd);m_fd = -1;}return true;
}bool CSerial::ReadData(char* data, int len, int& readLen, int isDebug)
{trace_log(DEBUG_LOG, 0, "Starting reading data");if (m_fd == -1) {trace_log(ERROR_LOG, 0, "Serial port is not available! try to open first.");return false;}int tmpLen = read(m_fd, data, len);if (tmpLen < 0) {trace_log(ERROR_LOG, 0, "Read Data is wrong! error [%s]", strerror(errno));readLen = 0;return false;}data[tmpLen] = '\0';readLen = tmpLen;if (isDebug != 0 && tmpLen > 0) {trace_log(DEBUG_LOG, 0, "Read data:");trace_log(DEBUG_LOG, tmpLen, data);}trace_log(DEBUG_LOG, 0, "Receive data successfully, Byte read: %d", tmpLen);return true;
}
socket
头文件
#pragma onceclass MySocket
{
public:MySocket();~MySocket();int ConnectToServer(const char* ip, int port, int timeOut);int SendDataToServer(const char* inData, int inLen);int RecvDataFromServer(char* recvData, int recvLen);void CloseSocket();private:int m_socket;bool m_socket_exist;
};
源文件
#include "MySocket.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>
#include <cerrno>#include <iostream>
using namespace std;MySocket::MySocket()
{m_socket = -1;m_socket_exist = false;
}MySocket::~MySocket()
{if (m_socket != -1){close(m_socket);}
}
/************************************************************************/
/* 建立socket链接 */
/************************************************************************/
int MySocket::ConnectToServer(const char* ip, int port, int timeOut)
{if (!m_socket_exist){m_socket = socket(AF_INET, SOCK_STREAM, 0);if (m_socket == -1) {trace_log(ERROR_LOG, 0, "Create socket failed: %s", strerror(errno));return -220;}// 设置接收超时1秒struct timeval timeout;timeout.tv_sec = timeOut;timeout.tv_usec = 0;if (setsockopt(m_socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) == -1) {trace_log(ERROR_LOG, 0, "setsockopt failed: %s", strerror(errno));close(m_socket);m_socket = -1;return 1;}// 填充服务器地址struct sockaddr_in server_addr;memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(port);if (inet_pton(AF_INET, ip, &server_addr.sin_addr) <= 0) {trace_log(ERROR_LOG, 0, "Invalid IP address: %s", ip);close(m_socket);m_socket = -1;return -221;}// 连接if (connect(m_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {trace_log(ERROR_LOG, 0, "Connect to server [%s], port:[%d] failed: %s", ip, port, strerror(errno));close(m_socket);m_socket = -1;return -221;}trace_log(INFO_LOG, 0, "Server connect successfully.");m_socket_exist = true;}return 0;
}/************************************************************************/
/* 向socket发送数据,成功返回实际发送的字节数; 失败返回错误码 */
/************************************************************************/
int MySocket::SendDataToServer(const char* inData, int inLen)
{int ret = -1;if (inData == NULL || inLen <= 0){trace_log(ERROR_LOG, 0, "SendDataToServer invalid parameter");return -201;}if (m_socket_exist && m_socket != -1){trace_log(DEBUG_LOG, 0, "Start sending data");ret = send(m_socket, inData, inLen, 0);trace_log(DEBUG_LOG, 0, "Send data, return value [%d]", ret);if (ret == -1){trace_log(ERROR_LOG, 0, "Send data to server failed, error: %s", strerror(errno));return -222;}}else {trace_log(ERROR_LOG, 0, "Socket is invalid");ret = -224;}return ret;
}/************************************************************************/
/* 从socket接收数据 */
/************************************************************************/
int MySocket::RecvDataFromServer(char* recvData, int recvLen)
{int ret = -1;if (recvData == NULL || recvLen <= 0){trace_log(ERROR_LOG, 0, "RecvDataToServer invalid parameter");return -201;}if (m_socket_exist && m_socket != -1){trace_log(DEBUG_LOG, 0, "Start reading data");ret = recv(m_socket, recvData, recvLen, 0);trace_log(DEBUG_LOG, 0, "Receive data, return value [%d]", ret);if (ret == -1){trace_log(ERROR_LOG, 0, "Receive data from server failed, error: %s", strerror(errno));return -223;}}else {trace_log(ERROR_LOG, 0, "Socket is invalid");ret = -224;}return ret;
}void MySocket::CloseSocket()
{if (m_socket_exist && m_socket != -1){close(m_socket);m_socket = -1;m_socket_exist = false;trace_log(INFO_LOG, 0, "Close socket.....");}
}
说明
上述函数中,用到的日志记录函数trace_log
就是简单日志写入到文件,可以删除也可以替换成你自己的。