Linux下实现串口读写操作
这里只贴串口读写操作部分代码,供大家参考学习用,该部分代码主要实现打开串口,配置串口参数波特率为115200、停止位1、数据位8、无校验位,发送2个数据,等待接收24个数据。代码是在QT窗体程序里实现,界面添加了了一个按钮,3个文本框,按下去发送2个数据,等待接收到下位机发送上来的24个数据后,把接收到的数据通过调试信息打印出来,然后再等待接收一次4个数据,再把接收到的数据通过调试信息打印出来,最后把发送数据长度、接收数据长度、串口句柄在文本框显示出来。
#include "mainwindow.h" #include "ui_mainwindow.h" #include <QMessageBox> #include <QTime> #include <QDebug> #include <fcntl.h> #include "stdio.h" #include "termios.h" #include "unistd.h" #include "limits.h" #include <stdint.h> #include "time.h" //=================== #include <sys/select.h> #include <sys/time.h> //=================== #define UART_DEV "/dev/ttyS0" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); } MainWindow::~MainWindow() { delete ui; } void MainWindow::on_pushButton_2_clicked() { int fd =0; int RxLen=0; int flag =0; uint8_t RxBuff[100]; uint8_t SenBuff[2]={0xaa,0xbb}; //==========串口打开============// fd = open(UART_DEV ,O_RDWR|O_NOCTTY); if(fd<0) QMessageBox::information(NULL, "COM","COM Open Fail !"); //==========配置串口============// struct termios opt; //配置串口的属性定义在结构体struct termios中 tcgetattr(fd, & opt); //获取终端控制属性 cfsetispeed(& opt, B115200); //指定输入波特率 cfsetospeed(& opt, B115200); //指定输出波特率 /* c_lflag 本地模式 */ opt.c_cflag &= ~ INPCK; //不启用输入奇偶检测 opt.c_cflag |= (CLOCAL | CREAD); //CLOCAL忽略 modem 控制线,CREAD打开接受者 /* c_lflag 本地模式 */ opt.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); //ICANON启用标准模式;ECHO回显输入字符;ECHOE如果同时设置了 ICANON,字符 ERASE 擦除前一个输入字符,WERASE 擦除前一个词;ISIG当接受到字符 INTR, QUIT, SUSP, 或 DSUSP 时,产生相应的信号 /* c_oflag 输出模式 */ opt.c_oflag &= ~ OPOST; //OPOST启用具体实现自行定义的输出处理 opt.c_oflag &= ~(ONLCR | OCRNL); //ONLCR将输出中的新行符映射为回车-换行,OCRNL将输出中的回车映射为新行符 /* c_iflag 输入模式 */ opt.c_iflag &= ~(ICRNL | INLCR); //ICRNL将输入中的回车翻译为新行 (除非设置了 IGNCR),INLCR将输入中的 NL 翻译为 CR opt.c_iflag &= ~(IXON | IXOFF | IXANY); //IXON启用输出的 XON/XOFF流控制,IXOFF启用输入的 XON/XOFF流控制,IXANY(不属于 POSIX.1;XSI) 允许任何字符来重新开始输出 /* c_cflag 控制模式 */ opt.c_cflag &= ~ CSIZE; //字符长度掩码,取值为 CS5, CS6, CS7, 或 CS8,加~就是无 opt.c_cflag |= CS8; //数据宽度是8bit opt.c_cflag &= ~ CSTOPB; //CSTOPB设置两个停止位,而不是一个,加~就是设置一个停止位 opt.c_cflag &= ~ PARENB; //PARENB允许输出产生奇偶信息以及输入的奇偶校验,加~就是无校验 /* c_cc[NCCS] 控制字符 */ opt.c_cc[VTIME] = 0 ; //等待数据时间(10秒的倍数),每个单位是0.1秒 20就是2秒 opt.c_cc[VMIN] = 255 ; //最少可读数据,非规范模式读取时的最小字符数,设为0则为非阻塞,如果设为其它值则阻塞,直到读到到对应的数据,就像一个阀值一样,比如设为8,如果只接收到3个数据,那么它是不会返回的,只有凑齐8个数据后一齐才READ返回,阻塞在那儿 /* new_cfg.c_cc[VMIN] = 8;//DATA_LEN; new_cfg.c_cc[VTIME] = 20;//每个单位是0.1秒 20就是2秒 如果这样设置,就完全阻塞了,只有串口收到至少8个数据才会对READ立即返回,或才少于8个数据时,超时2秒也会有返回 另外特别注意的是当设置VTIME后,如果read第三个参数小于VMIN ,将会将VMIN 修改为read的第三个参数*/ /*TCIFLUSH 刷清输入队列 TCOFLUSH 刷清输出队列 TCIOFLUSH 刷清输入、输出队列*/ tcflush(fd, TCIOFLUSH); //刷串口清缓存 tcsetattr(fd, TCSANOW, &opt); //设置终端控制属性,TCSANOW:不等数据传输完毕就立即改变属性 //==========串口发送============// int TxLen = write(fd,SenBuff,2); //如果你要连续发几包数据到下位机,需要对不同包之间的数据发生加延时,不然会出现粘包的情况;或者你还可以在下位机进行拆包处理 while(1) {
/*
添加sleep延时,让下位机串口数据全到缓存区,否则会出现只读到一部分数据情况,具体原因未知,可能虚拟机导致,不过该方法会导致接收变慢;
或者可以直接修改opt.c_cc[VMIN]的值,改为阻塞接收,这里就是用来这个方法;
或者直接修改为一次接收一个数据,根据累计接收到的数据判断你是否接收完成*/
//sleep(1); //==========串口接收============//
while( ((RxLen = read(fd, RxBuff, 24)) > 0) ) { for(int i=0;i<RxLen;i++) qDebug("Rbuff[%d] = %x",i,RxBuff[i]); flag=1; break; } if(flag==1) break; } while(1) { //==========串口接收============// while( ((RxLen = read(fd, RxBuff, 4)) > 0) ) { for(int i=0;i<RxLen;i++) qDebug("4 Rbuff[%d] = %x",i,RxBuff[i]); flag=1; break; } if(flag==1) break; } flag=0; ui->textA->setText(QString::number(fd)); ui->textB->setText(QString::number(RxLen)); ui->textC->setText(QString::number(TxLen)); ::close(fd); }
运行结果如下:
注意:使用串口需要在sudo环境下运行程序,否则要把用户组添加到串口的组
===========================================================================================
为了便于使用,下面的代码是我把上面的串口操作进行了整理封装的,供大家参考学习使用,实测可用,转载请标明出处
头文件
#ifndef MYUART_H #define MYUART_H #include <fcntl.h> #include "stdio.h" #include "termios.h" #include "unistd.h" #include "limits.h" #include <stdint.h> #include "time.h" #include <string.h> //#define UART_DEV "/dev/ttyS0" /* 要操作的串口号 */ int OpenUart(char* UART_DEV); int UartSend(int fd, uint8_t *SenBuff, long len); int UartRead(int fd, uint8_t *RxBuff, long RxLen); void UartClose(int fd); #endif // MYUART_H
串口操作文件
/* *File:实现串口的基本操作 */ #include "MyUart.h" /* * 函数名 : SetOpt * 函数功能: 设置串口的相关基本参数,这里固定了波特率为115200,数据位8,校验位无,停止位1 * 传入参数: fd 设备描述符 * 返回值 : 无 */ void SetOpt(int fd) { static struct termios termold, termnew; tcgetattr(fd, &termold) ; bzero(&termnew, sizeof(termnew)); termnew.c_iflag &= ~(ICRNL | IGNCR) ; termnew.c_cflag |= CLOCAL | CREAD; //CLOCAL:忽略modem控制线 CREAD:打开接受者 termnew.c_cflag &= ~CSIZE; termnew.c_cflag |= CS8; termnew.c_cflag &= ~PARENB; cfsetispeed(&termnew, B115200); cfsetospeed(&termnew, B115200); termnew.c_cflag &= ~CSTOPB; termnew.c_cc[VTIME] = 1; //VTIME:非cannoical模式读时的延时,以十分之一秒位单位,設置超時時間 ms 單位,設置阻塞后要關閉這裏,設0 termnew.c_cc[VMIN] = 0; //VMIN:非canonical模式读到最小字符数,阻塞讀取的字節數 tcflush(fd, TCIFLUSH); tcsetattr(fd, TCSANOW,&termnew); } /* * 函数名 : OpenUart * 函数功能: 串口打开,如果打开成功,会返回一个设备描述符,失败返回-1 * 传入参数: UART_DEV 要操作的串口号 * 返回值 : fd 设备描述符 */ int OpenUart(char* UART_DEV) { int fd=0; /*第1个参数:想要打开的文件路径名,或者文件名 *第2个参数:open_Status:文件打开方式,可采用下面的文件打开模式: O_RDONLY:以只读方式打开文件 O_WRONLY:以只写方式打开文件 O_RDWR:以读写方式打开文件 O_APPEND:写入数据时添加到文件末尾 O_CREATE:如果文件不存在则产生该文件,使用该标志需要设置访问权限位mode_t O_EXCL:指定该标志,并且指定了O_CREATE标志,如果打开的文件存在则会产生一个错误 O_TRUNC:如果文件存在并且成功以写或者只写方式打开,则清除文件所有内容,使得文件长度变为0 O_NOCTTY:如果打开的是一个终端设备,这个程序不会成为对应这个端口的控制终端,如果没有该标志,任何一个输入,例如键盘中止信号等,都将影响进程。 O_NONBLOCK:该标志与早期使用的O_NDELAY标志作用差不多。程序不关心DCD信号线的状态,如果指定该标志,进程将一直在休眠状态,直到DCD信号线为0。 O_NONBLOCK和O_NDELAY所产生的结果都是使I/O变成非搁置模式(non-blocking),在读取不到数据或是写入缓冲区已满会马上return,而不会搁置程序动作,直到有数据或写入完成; 它们的差别在于设立O_NDELAY会使I/O函式马上回传0,但是又衍生出一个问题,因为读取到档案结尾时所回传的也是0,这样无法得知是哪中情况;因此,O_NONBLOCK就产生出来,它在读取不到数据时会回传-1,并且设置errno为EAGAIN。 第3个参数:设置文件访问权限的初始值 */ fd = open(UART_DEV , O_RDWR|O_NOCTTY); if (fd < 0) { return -1; } SetOpt(fd); return fd; } /* * 函数名 : UartSend * 函数功能: 串口发送数据 * 传入参数: fd 设备描述符, *SenBuff 传输数据首地址, len 要发送字符大小 * 返回值 : 发送字符大小 */ int UartSend(int fd, uint8_t *SenBuff, long len) { int TxLen; TxLen = write(fd,SenBuff,len); return TxLen; } /* * 函数名 : UartRead * 函数功能: 串口接收数据 * 传入参数: fd 设备描述符, *RxBuff 接收传输数据Buff首地址, Rxlen 要接收字符大小 * 返回值 : 接收字符大小 */ int UartRead(int fd, uint8_t *RxBuff, long RxLen) {
//tcflush(fd, TCIOFLUSH); //刷串口清缓存,该函数可以清除 read 函数的串口缓存 int GetRxLen=0; while(RxLen){ GetRxLen += read(fd, RxBuff+GetRxLen, 1); RxLen--; } //GetRxLen = read(fd, RxBuff, RxLen); //把参数fd所指的文件传送sizeof (RxBuff)个字节到RxBuff指针所指的内存中 return GetRxLen ; } /* * 函数名 : UartClose * 函数功能: 关闭串口 * 传入参数: fd 设备描述符 * 返回值 : 无 */ void UartClose(int fd) { close(fd); } /*END*/
注意:使用串口读写时,要注意下位机的时序,不然可能会出现下位机还没发送数据上来,你就进行读取,导致读取失败的情况,时序调整可以用延时调整
本文来自博客园,作者:白菜没我白,转载请注明原文链接:https://www.cnblogs.com/xingboy/p/14416196.html