利用select封装的linux下串口操作函数,可以直接使用c版本接口,也可以使用c++版本接口。
c版本,包括serial_interface_impl.h、serial_interface_impl.c。
serial_interface_impl.h:
#ifndef _SERIAL_INTERFACE_IMPL_H_
#define _SERIAL_INTERFACE_IMPL_H_
#include <stdint.h>
#include <termios.h> /* tcgetattr, tcsetattr */
#ifdef __cplusplus
extern "C" {
#endif
/*****************************************************************************
*
* Macro Definition
*
*****************************************************************************/
/*****************************************************************************
*
* Structure/Class Definition
*
*****************************************************************************/
typedef struct SerialAttrDef {
uint32_t baud_rate; // Baudrate:
char stopbits[4]; // Stop bit: 1, 1.5, 2
uint8_t bytesize; // Data bit: 5, 6, 7, 8
char parity; // Parity: null(n), even(e), odd(o), mark(m), space(s)
} SerialAttrDef;
/*****************************************************************************
*
* Function Prototype
*
*****************************************************************************/
int32_t SerialOpen(const char *dev_name, const char *attr, SerialAttrDef *parsed_attr, struct termios *termios_ptr);
int32_t SerialClose(int32_t fd, struct termios *termios_ptr);
int32_t SerialRead(int32_t fd, char *rd_buf, unsigned int rd_len, uint8_t block, uint32_t baud_rate);
int32_t SerialWrite(int32_t fd, const char *wr_buf, unsigned int wr_len, uint32_t baud_rate);
int32_t SerialSetAttr(int32_t fd, const char *attr, SerialAttrDef *parsed_attr);
int32_t SerialParseAttr(const char *attr_str, SerialAttrDef *serial_attr);
/*****************************************************************************
*
* Data Declare
*
*****************************************************************************/
#ifdef __cplusplus
}
#endif
#endif
serial_interface_impl.c:
#include "serial_interface_impl.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <ctype.h>
#include <termios.h> /* tcgetattr, tcsetattr */
#include <unistd.h> /* read, write, close */
#include <fcntl.h> /* open */
#include <sys/signal.h>
#include <sys/types.h>
#include <limits.h> /* CHAR_MAX */
#include <sys/time.h>
#include <sys/ioctl.h>
#include <dlfcn.h>
/*****************************************************************************
*
* Macro Definition
*
*****************************************************************************/
/*
* Decription for TIMEOUT_SEC(buflen,baud);
* baud bits per second, buflen bytes to send.
* buflen*20 (20 means sending an octect-bit data by use of the maxim bits 20)
* eg. 9600bps baudrate, buflen=1024B, then TIMEOUT_SEC = 1024*20/9600+1 = 3
* don't change the two lines below unless you do know what you are doing.
*/
#define TIMEOUT_SEC_GET(buflen,baud) ((buflen)*20/(baud)+1)
#define TIMEOUT_USEC_GET (0)
#define ATTR_BUF_LEN (8)
#define IS_SPACE_CHAR(x) (((x) == ' ') || ((x) == '\t') || ((x) == '\r') || ((x) == '\n'))
//#define CMSPAR (010000000000) // define in bits/termios.h
/*****************************************************************************
*
* Data Definition
*
*****************************************************************************/
/*****************************************************************************
*
* Function Entity
*
*****************************************************************************/
/*
* reverse baudrate to kernel macro
*/
static uint32_t Baudrate2Macro(uint32_t baud_rate)
{
switch (baud_rate) {
#ifdef B0
case 0: return (B0);
#endif
#ifdef B50
case 50: return (B50);
#endif
#ifdef B75
case 75: return (B75);
#endif
#ifdef B110
case 110: return (B110);
#endif
#ifdef B134
case 134: return (B134);
#endif
#ifdef B150
case 150: return (B150);
#endif
#ifdef B200
case 200: return (B200);
#endif
#ifdef B300
case 300: return (B300);
#endif
#ifdef B600
case 600: return (B600);
#endif
#ifdef B1200
case 1200: return (B1200);
#endif
#ifdef B2400
case 2400: return (B2400);
#endif
#ifdef B4800
case 4800: return (B4800);
#endif
#ifdef B9600
case 9600: return (B9600);
#endif
#ifdef B19200
case 19200: return (B19200);
#endif
#ifdef B38400
case 38400: return (B38400);
#endif
#ifdef B57600
case 57600: return (B57600);
#endif
#ifdef B115200
case 115200: return (B115200);
#endif
default:
return (B9600);
}
}
/*
* set data bit
*/
static void SetDataBit(struct termios *termios_ptr, uint8_t bytesize)
{
termios_ptr->c_cflag &= ~CSIZE;
switch (bytesize) {
case 8:
termios_ptr->c_cflag |= CS8;
break;
case 7:
termios_ptr->c_cflag |= CS7;
break;
case 6:
termios_ptr->c_cflag |= CS6;
break;
case 5:
termios_ptr->c_cflag |= CS5;
break;
default:
termios_ptr->c_cflag |= CS8;
break;
}
}
/*
* set stop bit
*/
static void SetStopBit(struct termios *termios_ptr, const char *stopbits)
{
if (0 == strcmp (stopbits, "1")) {
termios_ptr->c_cflag &= ~CSTOPB; /* 1 stop bit */
} else if (0 == strcmp (stopbits, "1.5")) {
// ONE POINT FIVE same as TWO.. there is no POSIX support for 1.5
termios_ptr->c_cflag |= CSTOPB; /* 1.5 stop bits */
} else if (0 == strcmp (stopbits, "2")) {
termios_ptr->c_cflag |= CSTOPB; /* 2 stop bits */
} else {
termios_ptr->c_cflag &= ~CSTOPB; /* 1 stop bit */
}
}
/*
* set parity
*/
static void SetParity(struct termios *termios_ptr, char parity)
{
switch (parity) {
case 'N': /* no parity check */
case 'n':
termios_ptr->c_cflag &= ~PARENB;
termios_ptr->c_cflag &= ~CMSPAR;
break;
case 'E': /* even */
case 'e':
termios_ptr->c_cflag |= PARENB;
termios_ptr->c_cflag &= ~PARODD;
termios_ptr->c_cflag &= ~CMSPAR;
break;
case 'O': /* odd */
case 'o':
termios_ptr->c_cflag |= PARENB;
termios_ptr->c_cflag |= PARODD;
termios_ptr->c_cflag &= ~CMSPAR;
break;
case 'M': /* mark */
case 'm':
termios_ptr->c_cflag |= PARENB;
termios_ptr->c_cflag |= PARODD;
termios_ptr->c_cflag |= CMSPAR;
break;
case 'S': /* space */
case 's':
termios_ptr->c_cflag |= PARENB;
termios_ptr->c_cflag |= CMSPAR;
break;
default: /* no parity check */
termios_ptr->c_cflag &= ~PARENB;
termios_ptr->c_cflag &= ~CMSPAR;
break;
}
}
/*
* set serial attribute
*/
static int32_t SetAttr(int32_t fd, uint32_t baud_rate, char parity, uint8_t bytesize, const char *stopbits)
{
struct termios termios_data;
bzero(&termios_data, sizeof(struct termios));
cfmakeraw(&termios_data);
// set baudrate
termios_data.c_cflag = Baudrate2Macro(baud_rate);
termios_data.c_cflag |= CLOCAL | CREAD; /* | CRTSCTS */
SetParity(&termios_data, parity);
SetDataBit(&termios_data, bytesize);
SetStopBit(&termios_data, stopbits);
termios_data.c_oflag = 0;
termios_data.c_lflag |= 0;
termios_data.c_oflag &= ~OPOST;
termios_data.c_cc[VTIME] = 1; /* unit: 1/10 second. */
termios_data.c_cc[VMIN] = 1; /* minimal characters for reading */
tcflush (fd, TCIFLUSH);
return (tcsetattr(fd, TCSANOW, &termios_data));
}
/*
* Parse serial parameter, such as:115200,n,8,1
*/
int32_t SerialParseAttr(const char *attr_str, SerialAttrDef *serial_attr)
{
const char *attr_ptr;
char attr_buf[ATTR_BUF_LEN];
uint32_t i;
char stopbits[4]; // Stop bit: 1, 1.5, 2
uint32_t baud_rate; // Baudrate:
uint8_t bytesize; // Data bit: 5, 6, 7, 8
char parity; // n/o/e/m/s
if(NULL == attr_str || NULL == serial_attr) {
return -1;
}
attr_ptr = attr_str;
// get baudrate string
for (i = 0; (*attr_ptr != ',') && (*attr_ptr != ';') && (*attr_ptr != '\0') && (i < ATTR_BUF_LEN - 1);) {
if (IS_SPACE_CHAR(*attr_ptr)) {
attr_ptr ++;
} else if (isdigit(*attr_ptr)) {
attr_buf[i ++] = *attr_ptr ++;
} else {
return -1;
}
}
if ((*attr_ptr != ',') && (*attr_ptr != ';')) {
return -1;
}
attr_ptr ++; // skip ',' or ';'
// get baudrate attr
attr_buf[i] = '\0';
if ('\0' == attr_buf[0]) {
baud_rate = 9600;
} else {
baud_rate = 0;
for (i = 0; attr_buf[i] != '\0'; i ++) {
baud_rate = baud_rate * 10 + attr_buf[i] - '0';
}
}
// get parity char
parity = 'N';
for (; (*attr_ptr != ',') && (*attr_ptr != ';') && (*attr_ptr != '\0');) {
if (IS_SPACE_CHAR(*attr_ptr)) {
attr_ptr ++;
} else {
parity = *attr_ptr ++;
}
}
if ((*attr_ptr != ',') && (*attr_ptr != ';')) {
return -1;
}
attr_ptr ++; // skip ',' or ';'
// get databit string
for (i = 0; (*attr_ptr != ',') && (*attr_ptr != ';') && (*attr_ptr != '\0') && (i < 1);) {
if (IS_SPACE_CHAR(*attr_ptr)) {
attr_ptr ++;
} else if (isdigit(*attr_ptr)) {
attr_buf[i ++] = *attr_ptr ++;
} else {
return -1;
}
}
if ((*attr_ptr != ',') && (*attr_ptr != ';')) {
return -1;
}
attr_ptr ++; // skip ',' or ';'
// get databit attr
attr_buf[i] = '\0';
if ('\0' == attr_buf[0]) {
bytesize = 8;
} else {
bytesize = attr_buf[0] - '0';
}
// parse stopbit string
for (i = 0; (*attr_ptr != '\0') && (*attr_ptr != ',') && (*attr_ptr != ';') && (i < 3);) {
if (IS_SPACE_CHAR(*attr_ptr)) {
attr_ptr ++;
} else {
stopbits[i ++] = *attr_ptr ++;
}
}
if ((*attr_ptr != '\0') && (*attr_ptr != ',') && (*attr_ptr != ';')) {
return -1;
}
stopbits[i] = '\0';
if ('\0' == stopbits[0]) {
stopbits[0] = '1';
stopbits[1] = '\0';
}
// set attr
serial_attr->baud_rate = baud_rate;
serial_attr->bytesize = bytesize;
serial_attr->parity = parity;
strcpy(serial_attr->stopbits, stopbits);
return 0;
}
/*
* set port attribute
*/
int32_t SerialSetAttr (int32_t fd, const char *attr, SerialAttrDef *parsed_attr)
{
int32_t r;
if (fd < 0 || NULL == attr || NULL == parsed_attr) {
return -1;
}
r = SerialParseAttr(attr, parsed_attr);
if (r != 0) {
return r;
}
r = SetAttr(fd, parsed_attr->baud_rate, parsed_attr->parity, parsed_attr->bytesize, parsed_attr->stopbits);
if (-1 == r) {
fprintf (stderr, "Serial : cannot set baudrate at %d\n\r", parsed_attr->baud_rate);
}
return (r);
}
/*
* Open serial
*/
int32_t SerialOpen(const char *dev_name, const char *attr, SerialAttrDef *parsed_attr, struct termios *termios_ptr)
{
int32_t fd;
int32_t r;
if (NULL == dev_name || NULL == attr || NULL == parsed_attr) {
return -1;
}
// open serial
fd = open(dev_name, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (-1 == fd) {
fprintf (stderr, " Serial : cannot open %s\n\r", dev_name);
return -1;
}
// save old termios value
if (termios_ptr != NULL) {
tcgetattr(fd, termios_ptr);
}
// set serial parameter
r = SerialSetAttr(fd, attr, parsed_attr);
if (-1 == r) {
fprintf (stderr, " Serial : %s set parameter %s ERROR\n\r", dev_name, attr);
return r;
}
return fd;
}
/*
* close serial
*/
int32_t SerialClose(int32_t fd, struct termios *termios_ptr)
{
if (fd < 0) {
return -1;
}
// !!! clear all output data, do not send out
tcflush(fd, TCOFLUSH);
// restore old attribute
if (termios_ptr != NULL) {
tcsetattr(fd, TCSADRAIN, termios_ptr);
}
return close (fd);
}
/*
* read data
*/
int32_t SerialRead(int32_t fd, char *rd_buf, unsigned int rd_len, uint8_t block, uint32_t baud_rate)
{
struct timeval timeout;
fd_set read_fds;
int32_t r;
if ((fd < 0) || NULL == rd_buf || 0 == rd_len) {
return -1;
}
FD_ZERO(&read_fds);
FD_SET(fd, &read_fds);
if(block)
{
timeout.tv_sec = TIMEOUT_SEC_GET(rd_len, baud_rate);
timeout.tv_usec = TIMEOUT_USEC_GET;
} else {
timeout.tv_sec = 0;
timeout.tv_usec = 0;
}
r = select(fd + 1, &read_fds, NULL, NULL, &timeout);
if (r) {
return (read(fd, rd_buf, rd_len));
} else {
// flush all input data
// tcflush (fd, TCIFLUSH);
return -1;
}
}
/*
* Write data
*/
int32_t SerialWrite(int32_t fd, const char *wr_buf, unsigned int wr_len, uint32_t baud_rate)
{
struct timeval timeout;
fd_set write_fds;
int32_t r;
uint32_t len_done;
uint32_t total_done;
uint32_t try_count;
if ((fd < 0) || (NULL == wr_buf) || (0 == wr_len))
{
return -1;
}
FD_ZERO(&write_fds);
FD_SET(fd, &write_fds);
timeout.tv_sec = TIMEOUT_SEC_GET(wr_len, baud_rate);
timeout.tv_usec = TIMEOUT_USEC_GET;
for (total_done = 0, len_done = 0, try_count = 0; total_done < wr_len; try_count ++)
{
r = select(fd + 1, NULL, &write_fds, NULL, &timeout);
if (r)
{
len_done = write(fd, &wr_buf[total_done], wr_len - total_done);
if (len_done > 0)
{
total_done += len_done;
}
try_count = 0;
}
else
{
if (try_count > 10)
{
// !!! clear all output data, do not send out
// tcflush (fd, TCOFLUSH);
break;
}
}
}
tcflush (fd, TCOFLUSH);
//printf(" Serial : write try count = %d, total len = %d\n\r", try_count, total_done);
return total_done;
}
c++版本,对serial_interface_impl.h、serial_interface_impl.c进行了封装。
serial_interface.h:
#ifndef _SERIAL_INTERFACE_H_
#define _SERIAL_INTERFACE_H_
#include "serial_interface_impl.h"
/*****************************************************************************
*
* Structure/Class Definition
*
*****************************************************************************/
class SerialInterface {
public:
SerialInterface(const char *dev_name, const char *attr); // for example, "/dev/ttyUSB0", "9600,n,8,1"
virtual ~SerialInterface();
int32_t Close();
int32_t Read(char *rd_buf, unsigned int rd_len);
int32_t Write(const char *wr_buf, unsigned int wr_len);
int32_t ChangeAttr(const char *attr);
int32_t fd();
private:
int32_t fd_;
SerialAttrDef parsed_arrt_;
struct termios termios_bak_;
};
#endif
serial_interface.cpp:
#include "serial_interface.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <termios.h> /* tcgetattr, tcsetattr */
#include <unistd.h> /* read, write, close */
/*****************************************************************************
*
* Function Entity
*
*****************************************************************************/
/*
* 功能: 构造函数, 打开串口
* 输入: const char *dev_name, 串口设备路径, 例如: /dev/ttyUSB0
* 输入: const char *attr, 串口参数, 参数格式: "9600,n,8,1"
* 返回: 无
*/
SerialInterface::SerialInterface(const char *dev_name, const char *attr) {
fd_ = -1;
bzero(&parsed_arrt_, sizeof(SerialAttrDef));
bzero(&termios_bak_, sizeof(struct termios));
fd_ = SerialOpen(dev_name, attr, &parsed_arrt_, &termios_bak_);
}
/*
* 功能: 析构函数, 关闭打开的串口
*/
SerialInterface::~SerialInterface() {
if (fd_ > -1) {
SerialClose(fd_, &termios_bak_);
}
fd_ = -1;
}
/*
* 功能: 关闭打开的串口
* 输入: 无
* 返回: 0代表关闭成功, 其他值代表关闭失败
*/
int32_t SerialInterface::Close() {
int r = SerialClose(fd_, &termios_bak_);
if (0 == r) {
fd_ = -1;
}
return r;
}
/*
* 功能: 读取指定长度数据到给定的缓存区
* 输入: char *rd_buf, 存放读取数据的缓存区
* 输入: unsigned int rd_len, 想读取的数据长度
* 返回: 实际读取的数据长度, 负数表示读取出错
*/
int32_t SerialInterface::Read(char *rd_buf, unsigned int rd_len) {
return SerialRead(fd_, rd_buf, rd_len, 0, parsed_arrt_.baud_rate);
}
/*
* 功能: 写入指定长度数据到串口
* 输入: const char *wr_buf, 待写入的数据缓存区
* 输入: unsigned int wr_len, 想写入的数据长度
* 返回: 实际写入的数据长度, 负数表示写入出错
*/
int32_t SerialInterface::Write(const char *wr_buf, unsigned int wr_len) {
return SerialWrite(fd_, wr_buf, wr_len, parsed_arrt_.baud_rate);
}
/*
* 功能: 修改串口参数
* 输入: const char *attr, 串口参数, 参数格式: "9600,n,8,1"
* 返回: 0代表参数设置成功, 但不代表所以参数设置成功
*/
int32_t SerialInterface::ChangeAttr(const char *attr) {
return SerialSetAttr(fd_, attr, &parsed_arrt_);
}
/*
* 功能: 读取串口句柄
* 返回: 串口句柄
*/
int32_t SerialInterface::fd() {
return fd_;
}
/*
*
*/
/*
int main(void) {
const char *dev_name = "/dev/ttyUSB0";
char wr_buf[256] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
char rd_buf[256];
int32_t len;
SerialInterface ttyusb0(dev_name, "115200,n,8,1");
if (ttyusb0.fd() < 0) {
printf("%s open ERR!\n\r", dev_name);
}
while (1) {
wr_buf[9] ++;
len = ttyusb0.Write(wr_buf, 10);
printf("wr len = %d\n\r", len);
usleep(20000);
len = ttyusb0.Read(rd_buf, 10);
printf("rd len = %d, \t", len);
for (int i = 0; i < len; ++ i) {
printf("%d ", rd_buf[i]);
}
printf("\n\r");
sleep (1);
}
return 0;
}
*/