日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux串口编程详解

發布時間:2023/12/9 linux 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux串口编程详解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Linux串口編程詳解(阻塞模式、非阻塞模式、select函數)
之前一直覺得串口編程很簡單,這兩天仔細研究后發現串口里的各種參數還挺復雜,稍不注意就容易出錯,這里總結一下網上的各種文章及自己的理解與實踐。

open 函數
功能描述:用于打開或創建文件,成功則返回文件描述符,否則返回-1,open返回的文件描述符一定是最小的未被使用的描述符

#include<fcntl.h>
int open(const char * pathname,int flags);
int open(const char * pathname,int flags,mode_t mode);
參數flags:一些文件模式選擇,有如下幾個參數可以設置

O_RDONLY? ? ? ?只讀模式
O_WRONLY? ? ? 只寫模式
O_RDWR? ? ? ? ? 讀寫模式
上面三個參數在設置的時候必須選擇其中一個!下面的參數是可選的

O_APPEND? ? ? ? ? ?每次寫操作都寫入文件的末尾
O_CREAT? ? ? ? ? ? ? 如果指定文件不存在,則創建這個文件
O_EXCL? ? ? ? ? ? ? ??如果要創建的文件已存在,則返回 -1,并且修改 errno 的值
O_TRUNC? ? ? ? ? ? ?如果文件存在,并且以只寫/讀寫方式打開,則清空文件全部內容
O_NOCTTY? ? ? ? ? ?如果路徑名指向終端設備,不要把這個設備用作控制終端
O_NONBLOCK? ? ?如果路徑名指向 FIFO/塊文件/字符文件,則把文件的打開和后繼 I/O設置為非阻塞模式
O_NDELAY? ? ? ? ? ?表示用于設置非阻塞方式。同時通知Linux系統,這個程序不關心DCD信號線所處的狀態(端口的另一端是否激活或者停止)。如果用戶沒有指定這個標志,則進程將會一直處在睡眠狀態,直到DCD信號線被激活。
下面三個常量同樣是選用的,他們用于同步輸入輸出

O_DSYNC? ? ? ?等待物理 I/O 結束后再 write。在不影響讀取新寫入的數據的前提下,不等待文件屬性更新
O_RSYNC? ? ? ?read等待所有寫入同一區域的寫操作完成后再進行
O_SYNC? ? ? ? ? 等待物理 I/O 結束后再 write,包括更新文件屬性的 I/O
對于串口的打開操作,必須使用O_NOCTTY參數,它表示打開的是一個終端設備,程序不會成為該端口的控制終端。如果不使用此標志,任務的一個輸入(比如鍵盤終止信號等)都會影響進程。

open函數第三個參數mode為可選參數,表示打開文件權限

fcntl 函數
功能描述:根據文件描述詞來操作文件的特性,返回-1代表出錯

#include<unistd.h>
#include<fcntl.h>
int fcntl(int fd,int cmd);
int fcntl(int fd,int cmd,long arg);
int fcntl(int fd,int cmd,struct flock *lock);
fcntl()函數主要有5種功能:

復制一個現有的描述符(cmd=F_DUPFD/F_DUPFD_CLOEXEC)
獲得/設置文件描述符標記(cmd=F_GETFD/F_SETFD)
獲得/設置文件狀態標記(cmd=F_GETFL/F_SETFL) #常用
獲得/設置異步I/O所有權(cmd=F_GETOWN/F_SETOWN)
獲得/設置記錄鎖(cmd=F_GETLK/F_SETLK/F_SETLKW) #常用
F_SETFL :設置文件狀態標志。
其中O_RDONLY, O_WRONLY, O_RDWR, O_CREAT,? O_EXCL, O_NOCTTY 和 O_TRUNC不受影響,
能更改的標志有 O_APPEND,O_ASYNC, O_DIRECT, O_NOATIME 和 O_NONBLOCK。

此函數功能強大,在此不做詳細敘述!

串口參數初始化
串口參數由結構體termio設置:

struct termio
{
unsigned short c_iflag; /* 輸入模式標志 /
unsigned short c_oflag; / 輸出模式標志 /
unsigned short c_cflag; / 控制模式標志*/
unsigned short c_lflag; /* local mode flags /
unsigned char c_line; / line discipline /
unsigned char c_cc[NCC]; / control characters */
};
具體參見:http://kevinlad.me/2017/11/25/Termios%E7%BB%93%E6%9E%84%E4%BD%93/

這里列出常見配置:

int set_port_attr(int fd,int baudrate, int databit, const char *stopbit, char parity, int vtime,int vmin )
{
struct termios opt;
tcgetattr(fd, &opt); //獲取初始設置

set_baudrate(&opt, baudrate); set_data_bit(&opt, databit); set_parity(&opt, parity); set_stopbit(&opt, stopbit);opt.c_cflag &= ~CRTSCTS; // 不使用硬件流控制 opt.c_cflag |= CLOCAL | CREAD; //CLOCAL--忽略 modem 控制線,本地連線, 不具數據機控制功能, CREAD--使能接收標志 /* IXON--啟用輸出的 XON/XOFF 流控制 IXOFF--啟用輸入的 XON/XOFF 流控制 IXANY--允許任何字符來重新開始輸出 IGNCR--忽略輸入中的回車 */ opt.c_iflag &= ~(IXON | IXOFF | IXANY); opt.c_oflag &= ~OPOST; //啟用輸出處理 /* ICANON--啟用標準模式 (canonical mode)。允許使用特殊字符 EOF, EOL,EOL2, ERASE, KILL, LNEXT, REPRINT, STATUS, 和 WERASE,以及按行的緩沖。 ECHO--回顯輸入字符 ECHOE--如果同時設置了 ICANON,字符 ERASE 擦除前一個輸入字符,WERASE 擦除前一個詞 ISIG--當接受到字符 INTR, QUIT, SUSP, 或 DSUSP 時,產生相應的信號 */ opt.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); opt.c_cc[VMIN] = vmin; //設置非規范模式下的超時時長和最小字符數:阻塞模式起作用 opt.c_cc[VTIME] = vtime; //VTIME與VMIN配合使用,是指限定的傳輸或等待的最長時間 單位:0.1Stcflush (fd, TCIFLUSH); /* TCIFLUSH-- update the options and do it NOW */ return (tcsetattr (fd, TCSANOW, &opt)); /* TCSANOW--改變立即發生 */

}

static void set_baudrate(struct termios *opt, unsigned int baudrate)
{
cfsetispeed(opt, baudrate);
cfsetospeed(opt, baudrate);
}

static void set_stopbit(struct termios *opt, const char stopbit)
{
if (0 == strcmp (stopbit, “1”)) {
opt->c_cflag &= ~CSTOPB; / 1位停止位t /
}else if (0 == strcmp (stopbit, “1.5”)) {
opt->c_cflag &= ~CSTOPB; / 1.5位停止位 /
}else if (0 == strcmp (stopbit, “2”)) {
opt->c_cflag |= CSTOPB; / 2 位停止位 /
}else {
opt->c_cflag &= ~CSTOPB; / 1 位停止位 */
}
}

static void set_data_bit(struct termios *opt, unsigned int databit)
{
opt->c_cflag &= ~CSIZE;
switch (databit) {
case 8:
opt->c_cflag |= CS8;
break;
case 7:
opt->c_cflag |= CS7;
break;
case 6:
opt->c_cflag |= CS6;
break;
case 5:
opt->c_cflag |= CS5;
break;
default:
opt->c_cflag |= CS8;
break;
}
}

static void set_parity(struct termios opt, char parity)
{
switch (parity) {
case ‘N’: / 無校驗 /
case ‘n’:
opt->c_cflag &= ~PARENB;
break;
case ‘E’: / 偶校驗 /
case ‘e’:
opt->c_cflag |= PARENB;
opt->c_cflag &= ~PARODD;
break;
case ‘O’: / 奇校驗 */
case ‘o’:
opt->c_cflag |= PARENB;
opt->c_cflag |= ~PARODD;
break;
case ‘S’:
case ‘s’:
opt->c_cflag &= ~PARENB; /*清除校驗位 disable pairty checking Space校驗 /
opt->c_cflag &= ~CSTOPB;
opt->c_iflag |= INPCK;
break;
default: / 其它選擇為無校驗 */
opt->c_cflag &= ~PARENB;
break;
}
}
阻塞式讀寫配置
打開時使用:
fd = open(USAR1, O_RDWR | O_NOCTTY );//阻塞式讀寫

打開后使用fcntl函數修改:
fcntl(fd, F_SETFL, 0); //設為阻塞
阻塞式讀寫可設置以下兩參數:

opt.c_cc[VMIN] = vmin; ? //設置非規范模式下的超時時長和最小字符數:阻塞模式起作用
opt.c_cc[VTIME] = vtime; //VTIME與VMIN配合使用,是指限定的傳輸或等待的最長時間
若 VMIN = 0 ,VTIME = 0? ,函數read未讀到任何參數也立即返回,相當于非阻塞模式;

若 VMIN = 0,? ?VTIME > 0? ,函數read讀取到數據立即返回,若無數據則等待VTIME時間返回;

若 VMIN >?0,? ?VTIME =?0? ,函數read()只有在讀取到VMIN個字節的數據或者收到一個信號的時候才返回;

若 VMIN >?0,? ?VTIME > 0? ,從read讀取第一個字節的數據時開始計時,并會在讀取到VMIN個字節或者VTIME時間后返回。

非阻塞式讀寫配置
打開時使用
open(USAR1, O_RDWR | O_NOCTTY | O_NDELAY );//非阻塞式讀寫
open(USAR1, O_RDWR | O_NOCTTY | O_NONBLOCK);//非阻塞式讀寫
open(USAR1, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK);//非阻塞式讀寫

打開后使用fcntl修改設置
fcntl(socket_descriptor, F_SETFL, flags | O_NONBLOCK); //設為非阻塞
O_NONBLOCK和O_NDELAY都是設置為非阻塞模式,但是它們有些差別,O_NDELAY會使I/O函式馬上返回0,但是又衍生出一個問題,因為讀取到檔案結尾時所回傳的也是0,這樣無法得知是哪中情況;而O_NONBLOCK它在讀取不到數據時會回傳-1,并且設置errno為EAGAIN。

select函數讀寫
int select(int nfds, fd_set *rdfds, fd_set *wtfds, fd_set *exfds, struct timeval *timeout)
ndfs:select監視的文件句柄,一般設為要監視的文件中的最大文件號加一;
rdfds:select監視的可讀文件句柄集合,當rdfds映象的文件句柄狀態變成可讀時系統告訴select函數返回。這個集合中有一個文件可讀,select就會返回一個大于0的值,表示有文件可讀,如果沒有可讀的文件,則根據timeout參數再判斷是否超時,若超出timeout的時間,select返回0,若發生錯誤返回負值,可以傳入NULL值,表示不關心任何文件的讀變化;
wtfds: select監視的可寫文件句柄集合,當wtfds映象的文件句柄狀態變成可寫時系統告訴select函數返回,如果這個集合中有一個文件可寫,select就會返回一個大于0的值,表示有文件可寫,如果沒有可寫的文件,則根據timeout參數再判斷是否超時,若超出timeout的時間,select返回0,若發生錯誤返回負值,可以傳入NULL值,表示不關心任何文件的寫變化;
exfds:select監視的異常文件句柄集合,當exfds映象的文件句柄上有特殊情況發生時系統會告訴select函數返回;?
timeout:select的超時結束時間,設為0則為阻塞模式,設為大于0的值時若所監視的句柄無狀態變化則等待timeout時間后返回0;
配置函數:
FD_ZERO(fd_set *fdset):清空fdset與所有文件句柄的聯系。
FD_SET(int fd, fd_set *fdset):建立文件句柄fd與fdset的聯系。
FD_CLR(int fd, fd_set *fdset):清除文件句柄fd與fdset的聯系。
FD_ISSET(int fd, fdset *fdset):檢查fdset聯系的文件句柄fd是否可讀寫,>0表示可讀寫。

select函數非常強大,它能同時監測多個對象,只要在注冊的對象集中有一個或多個對象被激活就會有反應,所以利用select函數能在一個線程中處理多個等待式操作,這里以多串口阻塞讀取為例:

#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>

#define USAR1 “/dev/ttyS1”
#define USAR2 “/dev/ttyS2”
#define USAR3 “/dev/ttyS3”
#define USAR4 “/dev/ttyS4”
char buf[255] = {0};

int set_port_attr (int fd,int baudrate, int databit, const char *stopbit, char parity, int vtime,int vmin );
static void set_baudrate (struct termios *opt, unsigned int baudrate);
static void set_data_bit (struct termios *opt, unsigned int databit);
static void set_stopbit (struct termios *opt, const char *stopbit);
static void set_parity (struct termios *opt, char parity);

void main()
{
int fd1,fd2,fd3,fd4;
int ret;
int rxlen = 0;
fd_set rfds;
struct timeval tv;
int retval;

fd1 = open(USAR1, O_RDWR | O_NOCTTY );//阻塞式讀寫 非阻塞| O_NDELAY | O_NONBLOCK if (fd1 < 0) {perror("open uart1 error\n"); } fd2 = open(USAR2, O_RDWR | O_NOCTTY );//阻塞式讀寫 非阻塞| O_NDELAY | O_NONBLOCK if (fd2 < 0) {perror("open uart2 error\n"); } fd3 = open(USAR3, O_RDWR | O_NOCTTY );//阻塞式讀寫 非阻塞| O_NDELAY | O_NONBLOCK if (fd3 < 0) {perror("open uart3 error\n"); } fd4 = open(USAR4, O_RDWR | O_NOCTTY );//阻塞式讀寫 非阻塞| O_NDELAY | O_NONBLOCK if (fd4 < 0) {perror("open uart4 error\n"); } ret = set_port_attr(fd1, B115200, 8, "1", 'N',1,255 ); /* 115200 8n1 */ if(ret < 0) {printf("set uart1 arrt faile \n");exit(-1); } ret = set_port_attr(fd2, B115200, 8, "1", 'N',1,255 ); /* 115200 8n1 */ if(ret < 0) {printf("set uart2 arrt faile \n");exit(-1); } ret = set_port_attr(fd3, B115200, 8, "1", 'N',1,255 ); /* 115200 8n1 */ if(ret < 0) {printf("set uart3 arrt faile \n");exit(-1); } ret = set_port_attr(fd4, B115200, 8, "1", 'N',1,255 ); /* 115200 8n1 */ if(ret < 0) {printf("set uart4 arrt faile \n");exit(-1); } while(1) {FD_ZERO(&rfds);FD_SET(fd1, &rfds);FD_SET(fd2, &rfds);FD_SET(fd3, &rfds);FD_SET(fd4, &rfds); // tv.tv_sec = 1; //in block mode is not used // tv.tv_usec = 0;ret = select(fd4 + 1, &rfds, NULL, NULL, NULL); //block modeif(ret > 0){if(FD_ISSET(fd1,&rfds)){rxlen = read(fd1, buf, 255);if(rxlen > 0){printf("len = %d\n\r",rxlen);printf("rx:%s\n\r",buf);write(fd1, buf, rxlen);}}if(FD_ISSET(fd2,&rfds)){rxlen = read(fd2, buf, 255);if(rxlen > 0){printf("len = %d\n\r",rxlen);printf("rx:%s\n\r",buf);write(fd2, buf, rxlen);}}if(FD_ISSET(fd3,&rfds)){rxlen = read(fd3, buf, 255);if(rxlen > 0){printf("len = %d\n\r",rxlen);printf("rx:%s\n\r",buf);write(fd3, buf, rxlen);}}if(FD_ISSET(fd4,&rfds)) {rxlen = read(fd4, buf, 255);if(rxlen > 0){printf("len = %d\n\r",rxlen);printf("rx:%s\n\r",buf);write(fd4, buf, rxlen);}}} }

}
此段程序為同時監控4路串口接收狀態,將接受的內容直接原路返回,串口采用的是阻塞讀取模式,select函數也采用阻塞式讀取模式。
————————————————
版權聲明:本文為CSDN博主「發呆健將」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/m0_38096844/article/details/90716182

總結

以上是生活随笔為你收集整理的Linux串口编程详解的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。