記錄:
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; } } }