C++ 串口应用——自定义串口类

记录:  

  ARM板子串口的使用算是最基本的操作,经过3、4天的努力终于完成了可以应用在ARM板子上的串口类。代码多是参考网上,所以本博客开源分享

说明:

1、串口类使用静态函数,使其他文件可以引用。

2、特别适合Linux系统,调用设备文件(使用不同的串口,只需要更改初始化的配置即可)。

3、原理上还是调用C头文件,使用open、write、read函数完成打开读写关键操作;使用select,FD_ISSET对设备文件监听。

代码:

bsp_CSerial.h

#ifndef __BSP_CSERIAL_H
#define __BSP_CSERIAL_H

/* 获取 波特率 */
typedef struct port_info
{
    int  fd;
    int  baudrate;
    char databits;
    char stopbits;
    char parity;
    char flowctrl;
}*pport_info;
class CSerial
{
public:
    CSerial();
    ~CSerial();


    static int  UART_Init(pport_info p_info);
    static void UART_Close(int fd);
    static int  UART_Send(int fd,void *buf,int len);
    static int  UART_Recv(int fd,void *buf,int len);
};


#endif //#ifndef __BSP_USART_H

bsp_CSerial.cpp

/* Local Include */

#include "bsp_CSerial.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/un.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <pthread.h>
#include <termios.h>
#include <QIODevice>
#include <QDebug>

CSerial::CSerial()
{

}

CSerial::~CSerial()
{

}

int CSerial::UART_Init(pport_info p_info)
{
    struct termios new_opt;
    int baud_rate;
    int status;

    //get the current config -> new_opt
    tcgetattr(p_info->fd,&new_opt);
    bzero( &new_opt, sizeof(new_opt));

    //convert baud rate -> baud_flag
    switch(p_info->baudrate)
    {
        case 0:
            baud_rate = B0;
            break;
        case 50:
            baud_rate = B50;
        break;
        case 75:
            baud_rate = B75;
        break;
        case 110:
            baud_rate = B110;
        break;
        case 134:
            baud_rate = B134;
        break;
        case 150:
            baud_rate = B150;
        break;
        case 200:
            baud_rate = B200;
        break;
        case 300:
            baud_rate = B300;
        break;
        case 600:
            baud_rate = B600;
        break;
        case 1200:
            baud_rate = B1200;
        break;
        case 1800:
            baud_rate = B1800;
        break;
        case 2400:
            baud_rate = B2400;
        break;
        case 4800:
            baud_rate = B4800;
        break;
        case 9600:
            baud_rate = B9600;
        break;
        case 19200:
            baud_rate = B19200;
        break;
        case 38400:
            baud_rate = B38400;
        break;
        case 57600:
            baud_rate = B57600;
        break;
        case 115200:
            baud_rate = B115200;
        break;
        case 230400:
            baud_rate = B230400;
        break;
        default:break;
    }

    tcflush(p_info->fd, TCIOFLUSH);
    //setup input/output baudrate
    cfsetispeed(&new_opt,baud_rate);
    //printf("cfsetispeed::c_cflag = %x\r\n", new_opt.c_cflag);
    cfsetospeed(&new_opt,baud_rate);
    //printf("cfsetospeed::c_cflag = %x\r\n", new_opt.c_cflag);
    status = tcsetattr(p_info->fd, TCSANOW, &new_opt);
    if (status != 0)
    {
        qCritical("tcsetattr::set baud rate failed\n");
        return -1;
    }

    //修改控制模式,保证程序不会占用串口?
    new_opt.c_cflag |= CLOCAL;
    //printf("c_cflag |= CLOCAL => %x\r\n", new_opt.c_cflag);

    //修改控制模式,使得能够从串口读取输入数据
    new_opt.c_cflag |= CREAD;
    //printf("c_cflag |= CREAD => %x\r\n", new_opt.c_cflag);

    new_opt.c_cflag |= HUPCL;
    //setup control flow
    switch(p_info->flowctrl)
    {
    case '0':
        //no control-flow
        new_opt.c_cflag &=~CRTSCTS;
        break;
    case '1':
        //hardware control-flow
        new_opt.c_cflag |=CRTSCTS;
        break;
    case '2':
        new_opt.c_iflag |= IXON | IXOFF | IXANY;
        break;
    }
    //printf("c_cflag(no ctl-flow) = %x\r\n", new_opt.c_cflag);

    //setup bit size
    new_opt.c_cflag &=~CSIZE;
    switch(p_info->databits)
    {
    case '5':
        new_opt.c_cflag |=CS5;
        break;
    case '6':
        new_opt.c_cflag |=CS6;
        break;
    case '7':
        new_opt.c_cflag |=CS7;
        break;
    case '8':
        new_opt.c_cflag |=CS8;
        break;
    default:
        new_opt.c_cflag |=CS8;
    }
    qDebug("c_cflag |= CS8 => %x\r\n", new_opt.c_cflag);

    //setup parity
    switch(p_info->parity)
    {
    case 'n':
    case 'N':
        new_opt.c_cflag &= ~PARENB;   /* Clear parity enable */
        new_opt.c_iflag &= ~INPCK;     /* Enable parity checking */
        break;

    case 'o':
    case 'O':
        new_opt.c_cflag |= (PARODD | PARENB);    /* 设置为奇效验*/
        new_opt.c_iflag |= INPCK;                /* Disable parity checking */
        break;

    case 'e':
    case 'E':
        new_opt.c_cflag |= PARENB;        /* Enable parity */
        new_opt.c_cflag &= ~PARODD;        /* 转换为偶效验*/
        new_opt.c_iflag |= INPCK;       /* Disable parity checking */
        break;

    case 'S':
    case 's':  /*as no parity*/
        new_opt.c_cflag &= ~PARENB;
        new_opt.c_cflag &= ~CSTOPB;
        break;

    default:
        qDebug("Unsupported parity\n");
        return -1;
    }
    //printf("c_cflag &=~PARENB => %x\r\n", new_opt.c_cflag);


    //setup stop-bit
    if(p_info->stopbits=='2')
    {
        new_opt.c_cflag |=CSTOPB;
    }
    else
    {
        new_opt.c_cflag &=~CSTOPB;
    }
    //printf("c_cflag &=~CSTOPB => %x\r\n", new_opt.c_cflag);

    /* Set input parity option */
    if ((p_info->parity != 'n') || (p_info->parity != 'N'))
    {
        new_opt.c_iflag |= INPCK;
    }

    //修改输出模式:原始数据输出(raw 模式)
    new_opt.c_lflag &= ~(ICANON | ECHO | ISIG);                /*Input*/
    new_opt.c_oflag &= ~OPOST;                                /*Output*/

    //修改控制字符:读取字符的最少个数为1 ???
    new_opt.c_cc[VMIN]=1;

    //修改控制字符:读取第一个字符的超时时间为1×100ms
    new_opt.c_cc[VTIME]=1;

    //试图去掉在接收时必须收到'\n'才返回的问题
    //忽略输入的回车
    //new_opt.c_iflag |= IGNCR;
    //new_opt.c_iflag &= ~(IXON|IXOFF|IXANY);

    //如果发生数据溢出,接收数据,但是不再读取
    tcflush(p_info->fd,TCIFLUSH);

    status = tcsetattr(p_info->fd,TCSANOW,&new_opt);
    if(status != 0)
    {
        qCritical("Cannot set the serial port parameters");
        return -1;
    }

    return status;
}

void CSerial::UART_Close(int fd)
{
    close(fd);
}

int CSerial::UART_Send(int fd, void *buf, int len)
{
    int  sendlen=0;

    sendlen=write(fd, buf, len);

    if(sendlen==len)
    {
        return sendlen;
    }
    else
    {
        //如果出现溢出情况
        tcflush( fd,TCOFLUSH);
        return -1;
    }
}

int CSerial::UART_Recv(int fd, void *buf, int len)
{
    //定义读事件集合
    fd_set fdRead;
    int ret;
    struct timeval    aTime;

    FD_ZERO(&fdRead);
    FD_SET(fd,&fdRead);

    aTime.tv_sec = 0;
    aTime.tv_usec = 300000; //300ms

    ret = select( fd+1,&fdRead,NULL,NULL,&aTime );
    //printf( "select ret = %d\n", ret);

    if (ret < 0 )
    {
        //关闭串口
        UART_Close(fd);
    }else if (ret > 0)
    {
        //判断是否读事件
        if (FD_ISSET(fd,&fdRead))
        {
            //data available, so get it!
            ret = read( fd, buf, len );
            // 对接收的数据进行处理,这里为简单的数据回发
        }
    }
    return ret;
}

使用串口的线程:

thread_sericalport.cpp

1、初始化

Thread_SerialPort::Thread_SerialPort(char *PortName, int baudrate, char databits, char stopbits, char parity, DeviceType_m type)
{
    int fd;
    QMutexLocker locker(&mutex);
    isStop = false;
    this->type = type;
    if( (fd=open(PortName,O_RDWR | O_NOCTTY | O_NONBLOCK)) == -1 )
    {
        qCritical()<<"打开端口"<<PortName<<"失败";
    }
    info.fd       = fd;
    info.baudrate = baudrate;
    info.databits = databits;
    info.stopbits = stopbits;
    info.parity   = parity;//奇偶校验
    info.flowctrl = '0';


    if( CSerial::UART_Init( &info ) < 0)//创建了接收线程
    {
        qCritical()<<"端口"<<PortName<<"初始化设置失败";
    }

}

2、run() 包括读写

void Thread_SerialPort::run()
{
    uint8_t         receiveBuffer[128];
    int             receiveLength;
    while(1)
    {
        //----------判断全局变量中Carrier命令是否为空----------//
        if( type == DeviceType_WaterDepth)
        {

        }else if( type == DeviceType_Ins )
        {

        }else if( type == DeviceType_Carrier )
        {
            globeData->cmd_RS232_Carrier.mutex.lock();
            while( !globeData->cmd_RS232_Carrier.queue.isEmpty()  )
            {
                Device_Cmd_t packageToSend = globeData->cmd_RS232_Carrier.queue.dequeue();//获取全局变量中运载器命令
                //发送数据
                if( CSerial::UART_Send(info.fd,packageToSend.dataBuff,packageToSend.length) != packageToSend.length )
                {
                    qDebug()<<"Thread_SerialPort.cpp--->发送命令错误,请进行检查!!";
                }else
                {
                    qDebug("RS232_Carrier 命令发送成功! ");
                }
            }
            globeData->cmd_RS232_Carrier.mutex.unlock();
        }else if( type == DeviceType_UnderwaterAcoustic )
        {

        }else
        {
            qDebug()<<"Thread_SerialPort.cpp--->命令类型错误,请进行检查!!";
        }
        //----------接收数据----------//
        receiveLength = 0;
        while( CSerial::UART_Recv(info.fd,&receiveBuffer[receiveLength],1) > 0 )
        {
            receiveLength++;
        }
        if(receiveLength > 0)
        {
            uint8_t   str[receiveLength];//原str[receiveLength]
            Device_Buff_t packageToReceive;
            int i=0;
            for(i=0;i<receiveLength;i++)
            {
                str[i] = receiveBuffer[i];
                qDebug("str=%x",str[i]);
            }
            //str[i] = '\0';
            //qDebug("命令=%x",str);
            packageToReceive.deviceType = type;
            packageToReceive.length     = receiveLength;//算上\0
            memcpy(packageToReceive.dataBuff,receiveBuffer,receiveLength);
            globeData->data_Agreement.mutex.lock();
            globeData->data_Agreement.queue.append(packageToReceive);//接收到的协议入队
            globeData->data_Agreement.mutex.unlock();

        }


        {
            QMutexLocker locker(&mutex);
            if(true == isStop)
                break;
        }
    }
}

猜你喜欢

转载自www.cnblogs.com/shuoguoleilei/p/11446689.html