linux串口操作接口封装

利用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;
}
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值