linux下的串口通信類

    衆所周知, 計算機內部CPU與接口之間是按並行方式進行工作的,而許多外設和計算機之間是按串行方式進行通信,所謂的串行方式,就是在一根傳輸線上一位一位的傳送信息,所用的傳輸線少,並且可以藉助現成的電話網進行信息傳送,因此,特別適合於遠距離傳輸.對於那些與計算機相距不遠的人-機交換設備和串行存儲的外部設備如終端、打印機、邏輯分析儀、磁盤等,採用串行方式交換數據也很普遍. 所以不要看不起串行通信,它是一種經典的傳統的通信方式。網上用C寫的串口通信程序很多,這裏我用C++封裝了一個類,並加入了自己寫的日誌操作。


/*
 加入了日誌功能,記錄串口的狀態信息及接收信息,對發送信息新建日誌;
 加入自動鎖的功能,使主線程與接收線程和發送線程自動同步
 */
#ifndef MY_COM_H
#define MY_COM_H
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <termios.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/epoll.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>
#include "my_log/my_log.h"
#include <string>
#include <string.h>
#include "my_locker/auto_lock.h"
using namespace std;
const int MAXREADLEN = 256;
const int MAXSENDLEN = 256;
const int MINSIZE = 256;     //the minsize  of g_buffer
const int MAXSIZE = 10240;   // the maxsize of g_buffer
const int SLEEPTIME = 60;
const int SLEEPTINES = 5;
enum mutex
{
    RDLK = 0,
    WRLK = 1,
};
class My_com
{
public:
    My_com();
    My_com(char* log_path, char *log_name);
    ~My_com();
    int open_com(const int nport);
    int set_com(const int baud_rate, const int data_bits,
                char parity, const int stop_bits);
    int read_com(int rdlen = MAXREADLEN);
    int  write_com(int wrlen = MAXSENDLEN);
    static void* read_thread_function(void*);
    static void* send_thread_function(void*);
    int create_read_thread();
    int create_send_thread();
    friend int parse(My_com *pcom, string &str);  //find the str betweeb '$' and '\n'
    friend int send(My_com* , int fd, int wrlen = MAXSENDLEN);  //send to fd
    char m_recv_buf[MAXREADLEN];
    char m_send_buf[MAXSENDLEN];
    pthread_t m_read_thrdID; //default two thread
    pthread_t m_send_thrdID;
    My_log *m_plog;
private:
    int m_port;
};
#endif // MY_COM_H
以下實現:
#include "my_com.h"
string g_buffer;   //接收BUFFER
Mutex_locker g_locker;   //全局鎖, 用於讀寫線程對全局BUFFER的加鎖
//Mutex_locker g_log_locker;



My_com::My_com()
{
    this->m_read_thrdID = 0;
    this->m_send_thrdID = 0;
    this->m_port = 0;
    
    memset(m_recv_buf, 0, sizeof(m_recv_buf));
    memset(m_send_buf, 0 , sizeof(m_send_buf));
    
    m_plog = new  My_log("./", "com_status.log");
}
My_com::My_com(char *log_path, char *log_name)
{
    this->m_read_thrdID = 0;
    this->m_send_thrdID = 0;
    this->m_port = 0;

    memset(m_recv_buf, 0, sizeof(m_recv_buf));
    memset(m_send_buf, 0 , sizeof(m_send_buf));
    m_plog = new My_log(log_path, log_name);
}

My_com::~My_com()
{
    if(close(m_port))
    {
        char msg[100] = {0};
        sprintf(msg, "close port failed :%s\n", strerror(errno));
        m_plog->write_log(msg, ERR);
        exit(-1);
    }
    m_plog->write_log("close port done\n", INFO);
    delete m_plog;
}

int My_com::open_com(const int nport)
{
    char path_name[20];

    if((nport < 0) || (nport > 3))
    {
        char msg[100] = {0};
        sprintf(msg, "the port id [%d] is out of rang\n", nport);
        m_plog->write_log(msg, ERR);
        return -1;
    }
    sprintf(path_name, "/dev/ttyS%d", nport);
    if(m_port != 0)
    {
        m_plog->write_log("the port is busying", ERR);
        return -1;
    }
    if((m_port = open(path_name, O_RDWR | O_NOCTTY)) <= 0)
    {
        char msg[100] = {0};
        sprintf(msg, "can not open port [%d]: %s \n", nport, strerror(errno));
        m_plog->write_log(msg, ERR);
        exit(-1);
    }
    if(!isatty(m_port))
    {
        m_plog->write_log("is not a terminal\n", ERR);
    }
    char msg[100] = {0};
    sprintf(msg, "open port done, the port id is %d\n", m_port);
    m_plog->write_log(msg, INFO);
    return 0;
}
int My_com::set_com(const int baud_rate, const int data_bits, char parity, const int stop_bits)
{
    struct termios newtio;
    memset(&newtio,  0, sizeof(newtio));

    if(tcgetattr(m_port, &newtio))
    {
        char msg[100] = {0};
        sprintf(msg, "tcgetattr error : %s\n", strerror(errno));
        m_plog->write_log(msg, ERR);
        return -1;
    }
    m_plog->write_log("tcgetattr done\n", INFO);
    //set data bits
    newtio.c_cflag &= ~CSIZE;
    switch(data_bits)
    {
    case 7:
        newtio.c_cflag |= CS7;
        break;
    case 8:
        newtio .c_cflag |= CS8;
        break;
    default:
        newtio.c_cflag |= CS8;
        break;
    }
    //set baud rate
    switch (baud_rate)
    {
    case 2400:
        cfsetispeed(&newtio, B2400);
        cfsetospeed(&newtio, B2400);
        break;
    case 4800:
        cfsetispeed(&newtio, B4800);
        cfsetospeed(&newtio, B4800);
        break;
    case 9600:
        cfsetispeed(&newtio, B9600);
        cfsetospeed(&newtio, B9600);
        break;

    case 115200:
        cfsetispeed(&newtio, B115200);
        cfsetospeed(&newtio, B115200);
        break;
    default:
        cfsetispeed(&newtio, B9600);
        cfsetospeed(&newtio, B9600);
    }
    //set stop bits
    switch (stop_bits)
    {
    case 1:
        newtio.c_cflag |= ~CSTOPB;
        break;
    case 2:
        newtio.c_cflag |= CSTOPB;
        break;
    default:
        newtio.c_cflag |= ~CSTOPB;
        break;
    }
    //set parity
    switch (parity)
    {
    case 'O':
        newtio.c_cflag |= PARENB;
        newtio.c_cflag |= PARODD;
        newtio.c_iflag |= (INPCK | ISTRIP);
        break;
    case 'E':
        newtio.c_iflag |= (INPCK | ISTRIP);
        newtio.c_cflag |= PARENB;
        newtio.c_cflag &= ~PARODD;
        break;
    case 'N':
        newtio.c_cflag &= ~PARENB;
        newtio.c_cflag &= ~PARODD;
        break;
    }
    newtio.c_cflag |= CLOCAL | CREAD;
    newtio.c_cflag &= ~CRTSCTS;

    //raw model
    newtio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    newtio.c_oflag &= ~OPOST;
    newtio.c_iflag &= ~(INLCR|IGNCR|ICRNL);
    newtio.c_iflag &= ~(ONLCR|OCRNL);

    newtio.c_oflag &= ~(INLCR|IGNCR|ICRNL);
    newtio.c_oflag &= ~(ONLCR|OCRNL);
    //different from minicom set
    newtio.c_cflag &= ~HUPCL;
    newtio.c_iflag &= ~INPCK;
    newtio.c_iflag |= IGNBRK;
    newtio.c_iflag &= ~ICRNL;
    newtio.c_iflag &= ~IXON;
    newtio.c_lflag &= ~IEXTEN;
    newtio.c_lflag &= ~ECHOK;
    newtio.c_lflag &= ~ECHOCTL;
    newtio.c_lflag &= ~ECHOKE;
    newtio.c_oflag &= ~ONLCR;
    //
    newtio.c_cc[VMIN] = 1;
    newtio.c_cc[VTIME] = 0;

    tcflush(m_port, TCIOFLUSH);
    if(tcsetattr(m_port, TCSANOW, &newtio))
    {
        char msg[100] = {0};
        sprintf(msg, "tcsetattr error : %s\n", strerror(errno));
        m_plog->write_log(msg, ERR);
        return -1;
    }
    char msg[100];
    sprintf(msg, "tcsetattr done, baud_rate:%d, data_bits:%d, parity:%c, stop_bit:%d\n", baud_rate, data_bits, parity, stop_bits);
    m_plog->write_log(msg, INFO);
}
//@param read_len: read number one time
int My_com::read_com(int read_len)
{
    static long int rdcnt = 0;
    int rdlen = 0;
    int len = 0;

    if((m_recv_buf == NULL) || (read_len > MAXREADLEN))
    {
        m_plog->write_log("the recv_buf is null or out of range", ERR);
        return -1;
    }
    m_plog->write_log("\nstarting recving.........", INFO);
    do
    {
        while (true)
        {
            pthread_testcancel();    //set cancel state
            rdlen = read(m_port, m_recv_buf+ len, read_len);
            //m_plog->write_log(".", INFO);
            pthread_testcancel();
            len += rdlen;

            if(len >= MAXREADLEN)
            {

                rdlen = 0;
                break;
            }
        }
        rdcnt += len;  //in all read
        len = 0;
        char msg[100] = {0};
        sprintf(msg, "have read %ld : ", rdcnt);
        m_plog->write_log(msg, INFO);
        m_plog->write_log(m_recv_buf, INFO);
        //m_plog->write_log("\n", INFO);
    }while(false);
}

int My_com::write_com(int wrlen)
{
    m_plog->write_log("\nstarting sending.........", INFO);

    static long int sdcnt = 0;

    int len = write(m_port, m_send_buf, wrlen);
    sdcnt += len;

    if(len <= 0)
    {
        char msg[100] = {0};
        sprintf(msg, "send error: %s ", strerror(errno));

        m_plog->write_log(msg, ERR);
        return len;
    }
    char msg[100] = {0};
    sprintf(msg, "have send %ld :", sdcnt);
    m_plog->write_log(msg, INFO);
    m_plog->write_log(m_send_buf, INFO);
    //m_plog->write_log("\n", INFO);

    return len;
}
void* My_com::read_thread_function(void *arg)
{
    //My_log rd_thrd_log;

    char msg[100];
    sprintf(msg, "new read thead create, ID: %x", pthread_self());
    //rd_thrd_log.write_log(msg, INFO)
    My_com *pcom = (My_com*)arg;
    int cnt_sleep = 0;

    do
    {
        if(g_buffer.size() >= MAXSIZE)
        {
            sleep(SLEEPTIME);
            ++cnt_sleep;
            if(cnt_sleep >= SLEEPTINES)
            {
                break;
            }
        }
        pthread_testcancel();  //set cancel point
        pcom->read_com(1);

        Auto_lock lock(g_locker);
        g_buffer.append(pcom->m_recv_buf);
    }while (true);

    pthread_exit(NULL);
}
void* My_com::send_thread_function(void *arg)
{
    My_com *pcom = (My_com*)arg;

    int cnt_sleep = 0;
    do
    {
        pthread_testcancel();    //set cancel point
        if(g_buffer.size() <= MINSIZE)
        {
            sleep(SLEEPTIME);
            ++cnt_sleep;
            if(cnt_sleep >= SLEEPTINES)
            {
                break;
            }
        }
        Auto_lock lock(g_locker);
       cout<<g_buffer.size()<<endl;
        parse(pcom, g_buffer);
        cout<<g_buffer.size()<<endl;
        pcom->write_com(MAXSENDLEN);
        //pthread_testcancel();
    }while(true);

    pthread_exit(NULL);
}
int parse(My_com *pcom, string &str)
{
    int start = 0;
    int end = 0;
    int len = 0;

    start = str.find_first_of('$', 0);
    end = str.find('\n', start);
    len = end - start +1;
    strncpy(pcom->m_send_buf, str.substr(start, end).c_str(), len);
    str.erase(0, start + len);
    //cout<<str.size()<<endl;
}

int My_com::create_read_thread()
{
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    int res = pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
    res += pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
    if(pthread_create(&m_read_thrdID, &attr, read_thread_function, (void*)this) != 0)
    {
        m_plog->write_log("create read thread error", ERR);
        return -1;
    }
    return 0;
}
int My_com::create_send_thread()
{
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    int res = pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
    res += pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
    if(pthread_create(&m_read_thrdID, &attr, send_thread_function, (void*)this) != 0)
    {
        m_plog->write_log("create read thread error", ERR);
        return -1;
    }
    return 0;
}
int send(My_com* pcom, int fd, int wrlen)
{
    pcom->m_plog->write_log("\nstating sending.........", INFO);

    static long int sdcnt = 0;

    int len = write(fd, pcom->m_send_buf, wrlen);
    sdcnt += len;

    if(len <= 0)
    {
        char msg[100] = {0};
        sprintf(msg, "send error: %s ", strerror(errno));

        pcom->m_plog->write_log(msg, ERR);
        return len;
    }
    char msg[100] = {0};
    sprintf(msg, "have send %ld :", sdcnt);
    pcom->m_plog->write_log(msg, INFO);
    pcom->m_plog->write_log(pcom->m_send_buf, INFO);
    //m_plog->write_log("\n", INFO);

    return len;
}

demo:

#include <iostream>
#include "my_com.h"
#include "my_locker/auto_lock.h"
using namespace std;

extern string g_buffer;
extern Mutex_locker g_locker;     //lock the g_buffer
extern Mutex_locker g_log_locker;
bool rdstop_flag = false;
bool wrstop_flag = false;

int main()
{
    My_com com("./", "chen");
    com.open_com(0);
    com.set_com(9600, 8, 'N', 1);

//    string buf;
//    while(true)
//    {
//        com.read_com(1);
//        buf.append(com.m_recv_buf);
//    }
    com.create_read_thread();
    sleep(5);
    com.create_send_thread();


    sleep(10);
    com.m_plog->write_log("will cancel read thread\n", INFO);
    pthread_cancel(com.m_read_thrdID);
    pthread_join(com.m_read_thrdID, NULL);
    com.m_plog->write_log("cancel read thread done\n", INFO);

    com.m_plog->write_log("will cancel send thread\n", INFO);
    pthread_join(com.m_send_thrdID, NULL);
    com.m_plog->write_log("cancel send thread done\n", INFO);

    return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章