linux 串列埠通訊

qilimi-1發表於2020-11-28

自己弄了一個串列埠通訊的程式碼,比較少就沒上傳git了。
h檔案


#ifndef __SERIAL__INCLUDE__
#define __SERIAL__INCLUDE__

#include <stdio.h>
#include <iostream>
#include <stdint.h>

namespace sharenew {
	class SerialControl {
	public:
		SerialControl(){}
		virtual ~SerialControl(){}

		virtual int open_serial(const char* path) = 0;
		virtual void close_serial() = 0;

		virtual int send_data(uint8_t* data, int len) = 0;
		virtual int recv_data(uint8_t* data, int len) = 0;

		//設定波特率
		// speed 引數型別(int),用來設定串列埠的波特率 
		// return 返回值型別(int),函式執行成功返回零值
		virtual int setSpeed(int speed) = 0;
		// 設定串列埠資料位,停止位和效驗位 
		// databits 引數型別(int), 資料位,取值為7或者8 
		// parity 引數型別(int),效驗型別 取值為N,E,O,,S 
		// stopbits 引數型別(int),停止位,取值為1或者2 
		// return 返回值型別(int),函式執行成功返回零值,否則返回大於零的值 
		virtual int setParity(int databits, int parity, int stopbits) = 0;

		static SerialControl* getSerialControl(int type);
	};
};


#endif

cpp檔案

#include "SerialControl.h"
#include <string>
#include <mutex>

#include <stdlib.h> 
#include <unistd.h> 
#include <sys/types.h> 
#include <sys/stat.h> 
#include <fcntl.h> 
#include <termios.h> 
#include <errno.h> 

using namespace std;

namespace sharenew {

#define SERIAL_CONTROL_HEADER_0 (0x49)
#define SERIAL_CONTROL_HEADER_1 (0x85)

#define SERIAL_CONTROL_VERSION_0 (0x00)
#define SERIAL_CONTROL_VERSION_1 (0x01)

	struct SerialControlData {
		uint8_t m_header[2];//0x49 85
		uint8_t m_version[2];//0x00 0x01
		uint16_t m_len;//資料長度
		uint16_t crc16;//
		uint8_t data;
	};

	//struct termios {
	//	uint8_t c_cflag;
	//	uint8_t c_oflag;
	//	uint8_t c_iflag;
	//	uint8_t c_cc[8];
	//};

	static uint16_t getCrc16Serial(const uint8_t* data, int offset, int len);

	class SerialControlDefault:public SerialControl {
	public:
		SerialControlDefault();
		virtual ~SerialControlDefault();

		virtual int open_serial(const char* path);
		virtual void close_serial();

		virtual int send_data(uint8_t* data, int len);
		virtual int recv_data(uint8_t* data, int len);

		//設定波特率
		virtual int setSpeed(int speed);

		virtual int setParity(int databits, int parity, int stopbits);
	protected:
		mutex m_fd_ctx;
		int m_fd;
		string m_path;

		struct termios m_ntm; // 新的串列埠裝置選項 
		struct termios m_otm; // 舊的串列埠裝置選項 

		SerialControlData* m_write_data;//組裝資料

		static const int g_send_data_max_len;

		int write_data(uint8_t* data, int len);
		int read_data(uint8_t* data, int len);
	};
	const int SerialControlDefault::g_send_data_max_len = 256;

	SerialControl* SerialControl::getSerialControl(int type) {
		switch (type)
		{
		case 0:
			return new SerialControlDefault();
		default:
			break;
		}
	}
	SerialControlDefault::SerialControlDefault():m_fd(-1){
		bzero(&m_ntm, sizeof(m_ntm));
		bzero(&m_otm, sizeof(m_otm));

		m_write_data = (SerialControlData*)malloc(sizeof(SerialControlData) - 4 + g_send_data_max_len);
	}
	SerialControlDefault::~SerialControlDefault() {
		close_serial();
		if (NULL != m_write_data) {
			free(m_write_data);
			m_write_data = NULL;
		}
	}

	int SerialControlDefault::open_serial(const char* path) {
		lock_guard<mutex> lock(m_fd_ctx);
		if (m_fd >= 0 || NULL == m_write_data) {
			return -1;
		}
		//m_fd = open(path, O_RDWR | O_NOCTTY | O_NDELAY);
		if (m_fd < 0) {
			//開啟失敗
			return -1;
		}
		//開啟成功
		m_path = path;
		//儲存原有的配置
		//tcgetattr(m_fd, &m_otm);
	}
	void SerialControlDefault::close_serial() {
		lock_guard<mutex> lock(m_fd_ctx);
		if (m_fd >= 0) {
			tcsetattr(m_fd, TCSANOW, &m_otm);
			//close(m_fd);
			m_fd = -1;
		}
		bzero(&m_ntm, sizeof(m_ntm));
		bzero(&m_otm, sizeof(m_otm));
	}

	int SerialControlDefault::send_data(uint8_t* data, int len) {
		lock_guard<mutex> lock(m_fd_ctx);
		if (m_fd < 0 || NULL == m_write_data || len > g_send_data_max_len) {
			return -1;
		}
		//先計算crc16
		m_write_data->crc16 = getCrc16Serial(data, 0, len);
		//組裝資料
		memcpy((void*)(&(m_write_data->data)), data, len);
		//確定資料頭
		m_write_data->m_header[0] = SERIAL_CONTROL_HEADER_0;
		m_write_data->m_header[1] = SERIAL_CONTROL_HEADER_1;
		m_write_data->m_version[0] = SERIAL_CONTROL_VERSION_0;
		m_write_data->m_version[1] = SERIAL_CONTROL_VERSION_1;
		//傳送資料
		return write_data((uint8_t*)&m_write_data, sizeof(SerialControlData) - 4 + len);
	}
	int SerialControlDefault::recv_data(uint8_t* data, int len) {
		lock_guard<mutex> lock(m_fd_ctx);
		if (m_fd < 0 || NULL == m_write_data) {
			return -1;
		}
		//接收資料
		int ret = read_data((uint8_t*)&m_write_data, sizeof(SerialControlData) - 4 + g_send_data_max_len);
		if (ret <= 0) {
			return -1;
		}
		//確定資料頭和版本
		if (SERIAL_CONTROL_HEADER_0 != m_write_data->m_header[0] || SERIAL_CONTROL_HEADER_1 != m_write_data->m_header[1]
			|| SERIAL_CONTROL_VERSION_0 != m_write_data->m_version[0] || SERIAL_CONTROL_VERSION_1 != m_write_data->m_version[1]) {
			return -1;//接收資料錯誤
		}
		//確定長度
		if (ret != (m_write_data->m_len + sizeof(SerialControlData) - 4)) {
			return -1;//接收資料錯誤
		}
		//crc校驗
		uint16_t crc16 = getCrc16Serial((uint8_t*)(&(m_write_data->data)), 0, m_write_data->m_len);
		if (crc16 != m_write_data->crc16) {
			return -1;//crc16錯誤
		}
		if (len < m_write_data->m_len) {
			return -1;//長度太短了
		}
		memcpy(data, (void*)(&(m_write_data->data)), m_write_data->m_len);
		return m_write_data->m_len;
	}

	int SerialControlDefault::write_data(uint8_t* data, int len) {
		int ret, nleft;
		uint8_t* ptmp;

		ret = 0;
		nleft = len;
		ptmp = data;

		while (nleft > 0)
		{
			{
				lock_guard<mutex> lock(m_fd_ctx);
				ret = write(m_fd, ptmp, nleft);
			}

			if (ret > 0)
			{
				nleft -= ret;
				ptmp += ret;
			}
			//usleep(100); 
		}

		return len - nleft;
	}
	int SerialControlDefault::read_data(uint8_t* data, int len) {
		int ret, left, bytes;
		left = len;
		while (left > 0)
		{
			ret = 0;
			bytes = 0;
			{
				lock_guard<mutex> lock(m_fd_ctx);
				ioctl(m_fd, FIONREAD, &bytes);
				if (bytes > 0){
					ret = read(m_fd, data, left);
				}

			}
			if (ret > 0){
				left -= ret;
				data += ret;
			}
			usleep(100);
		}

		return len - left;
	}
	
	//設定波特率
	int SerialControlDefault::setSpeed(int speed) {
		lock_guard<mutex> lock(m_fd_ctx);
		bzero(&m_ntm, sizeof(m_ntm));
		//儲存配置
		//tcgetattr(m_fd, &m_ntm);
		m_ntm.c_cflag = CLOCAL | CREAD;//CS8

		switch (speed)
		{
		case 300:
			m_ntm.c_cflag |= B300;
			break;
		case 1200:
			m_ntm.c_cflag |= B1200;
			break;
		case 2400:
			m_ntm.c_cflag |= B2400;
			break;
		case 4800:
			m_ntm.c_cflag |= B4800;
			break;
		case 9600:
			m_ntm.c_cflag |= B9600;
			break;
		case 19200:
			m_ntm.c_cflag |= B19200;
			break;
		case 38400:
			m_ntm.c_cflag |= B38400;
			break;
		case 115200:
			m_ntm.c_cflag |= B115200;
			break;
		}
		m_ntm.c_iflag = IGNPAR;
		m_ntm.c_oflag = 0;

		return 0;
	}

	int SerialControlDefault::setParity(int databits, int parity, int stopbits) {
		lock_guard<mutex> lock(m_fd_ctx);
		if (0 != tcgetattr(m_fd, m_ntm)) {
			return -1;
		}

		bzero(&m_ntm, sizeof(m_ntm));
		//儲存配置
		//tcgetattr(m_fd, &m_ntm);
		m_ntm.c_cflag = CS8 | CLOCAL | CREAD;
		m_ntm.c_iflag = IGNPAR;
		m_ntm.c_oflag = 0;

		//設定串列埠的各種引數
		m_ntm.c_cflag &= ~CSIZE;
		switch (databits)
		{ //設定資料位數 
		case 7:
			m_ntm.c_cflag |= CS7;
			break;
		case 8:
			m_ntm.c_cflag |= CS8;
			break;
		default:
			//printf("Unsupported data size\n");
			return 5;
		}

		switch (parity)
		{ // 設定奇偶校驗位數 
		case n:
		case N:
			m_ntm.c_cflag &= ~PARENB; /* Clear parity enable */
			m_ntm.c_iflag &= ~INPCK; /* Enable parity checking */
			break;
		case o:
		case O:
			m_ntm.c_cflag |= (PARODD | PARENB); /* 設定為奇效驗*/
			m_ntm.c_iflag |= INPCK; /* Disnable parity checking */
			break;
		case e:
		case E:
			m_ntm.c_cflag |= PARENB; /* Enable parity */
			m_ntm.c_cflag &= ~PARODD; /* 轉換為偶效驗*/
			m_ntm.c_iflag |= INPCK; /* Disnable parity checking */
			break;
		case S:
		case s: /*as no parity*/
			m_ntm.c_cflag &= ~PARENB;
			m_ntm.c_cflag &= ~CSTOPB;
			break;
		default:
			printf("Unsupported parity\n");
			return 2;
		}
		// 
		// 設定停止位 
		switch (stopbits)
		{
		case 1:
			m_ntm.c_cflag &= ~CSTOPB;
			break;
		case 2:
			m_ntm.c_cflag |= CSTOPB;
			break;
		default:
			printf("Unsupported stop bits\n");
			return 3;
		}
		// 
		// 
		m_ntm.c_lflag = 0;
		m_ntm.c_cc[VTIME] = 0; // inter-character timer unused 
		m_ntm.c_cc[VMIN] = 1; // blocking read until 1 chars received 
		tcflush(m_fd, TCIFLUSH);
		if (tcsetattr(m_fd, TCSANOW, &m_ntm) != 0)
		{
			printf("SetupSerial \n");
			return 4;
		}

		return 0;
	}

	static uint16_t getCrc16Serial(const uint8_t* data, int offset, int len) {
		uint16_t ret = 0x00;
		for (int i = 0; i < len; ++i) {
			ret &= data[offset + i];
			ret = ret << 1;
		}
		return ret;
	}
};




相關文章