linux串列埠程式設計(termios結構體說明)
termios結構體說明
轉https://www.cnblogs.com/li-hao/archive/2012/02/19/2358158.html
termios結構體中,該結構體一般包括如下的成員:
tcflag_t c_iflag;
tcflag_t c_oflag;
tcflag_t c_cflag;
tcflag_t c_lflag;
cc_t c_cc[NCCS];
其具體意義如下
c_iflag:輸入模式標誌,控制終端輸入方式,具體引數如下所示。
c_iflag引數表
鍵值說明
IGNBRK 忽略BREAK鍵輸入
BRKINT 如果設定了IGNBRK,BREAK鍵的輸入將被忽略,如果設定了BRKINT ,將產生SIGINT中斷
IGNPAR 忽略奇偶校驗錯誤
PARMRK 標識奇偶校驗錯誤
INPCK 允許輸入奇偶校驗
ISTRIP 去除字元的第8個位元
INLCR 將輸入的NL(換行)轉換成CR(回車)
IGNCR 忽略輸入的回車
ICRNL 將輸入的回車轉化成換行(如果IGNCR未設定的情況下)
IUCLC 將輸入的大寫字元轉換成小寫字元(非POSIX)
IXON 允許輸入時對XON/XOFF流進行控制
IXANY 輸入任何字元將重啟停止的輸出
IXOFF 允許輸入時對XON/XOFF流進行控制
IMAXBEL 當輸入佇列滿的時候開始響鈴,Linux在使用該引數而是認為該引數總是已經設定
c_oflag: 輸出模式標誌,控制終端輸出方式,具體引數如下所示。
c_oflag引數
鍵值說明
OPOST 處理後輸出
OLCUC 將輸入的小寫字元轉換成大寫字元(非POSIX)
ONLCR 將輸入的NL(換行)轉換成CR(回車)及NL(換行)
OCRNL 將輸入的CR(回車)轉換成NL(換行)
ONOCR 第一行不輸出回車符
ONLRET 不輸出回車符
OFILL 傳送填充字元以延遲終端輸出
OFDEL 以ASCII碼的DEL作為填充字元,如果未設定該引數,填充字元將是NUL(‘/0’)(非POSIX)
NLDLY 換行輸出延時,可以取NL0(不延遲)或NL1(延遲0.1s)
CRDLY 回車延遲,取值範圍為:CR0、CR1、CR2和 CR3
TABDLY 水平製表符輸出延遲,取值範圍為:TAB0、TAB1、TAB2和TAB3
BSDLY 空格輸出延遲,可以取BS0或BS1
VTDLY 垂直製表符輸出延遲,可以取VT0或VT1
FFDLY 換頁延遲,可以取FF0或FF1
c_cflag:控制模式標誌,指定終端硬體控制資訊,具體引數如下所示。
c_oflag引數
鍵值說明
CBAUD 波特率(4+1位)(非POSIX)
CBAUDEX 附加波特率(1位)(非POSIX)
CSIZE 字元長度,取值範圍為CS5、CS6、CS7或CS8
CSTOPB 設定兩個停止位
CREAD 使用接收器
PARENB 使用奇偶校驗
PARODD 對輸入使用奇偶校驗,對輸出使用偶校驗
HUPCL 關閉裝置時掛起
CLOCAL 忽略調變解調器線路狀態
CRTSCTS 使用RTS/CTS流控制
c_lflag:本地模式標誌,控制終端編輯功能,具體引數如下所示。
c_lflag引數
鍵值說明
ISIG 當輸入INTR、QUIT、SUSP或DSUSP時,產生相應的訊號
ICANON 使用標準輸入模式
XCASE 在ICANON和XCASE同時設定的情況下,終端只使用大寫。如果只設置了XCASE,則輸入字元將被轉換為小寫字元,除非字元使用了轉義字元(非POSIX,且Linux不支援該引數)
ECHO 顯示輸入字元
ECHOE 如果ICANON同時設定,ERASE將刪除輸入的字元,WERASE將刪除輸入的單詞
ECHOK 如果ICANON同時設定,KILL將刪除當前行
ECHONL 如果ICANON同時設定,即使ECHO沒有設定依然顯示換行符
ECHOPRT 如果ECHO和ICANON同時設定,將刪除打印出的字元(非POSIX)
TOSTOP 向後臺輸出傳送SIGTTOU訊號
與此結構體相關的函式
(一)tcgetattr() // tc get attr
1.原型
int tcgetattr (int fd,struct termois & termios_p);
2.功能
取得終端介質(fd)初始值,並把其值 賦給temios_p;函式可以從後臺程序中呼叫;
但是,終端屬性可能被後來的前臺程序所改變。
(二)tcsetattr() //tc set attr
1.原型
int tcsetattr (int fd,int actions,const struct termios *termios_p);
2.功能
設定與終端相關的引數 (除非需要底層支援卻無法滿足),使用 termios_p 引用的 termios 結構。optional_actions (tcsetattr函式的第二個引數)指定了什麼時候改變會起作用:
TCSANOW: 改變立即發生
TCSADRAIN: 改變在所有寫入 fd 的輸出都被傳輸後生效。這個函式應當用於修改影響輸出的引數時使用。(當前輸出完成時將值改變)
TCSAFLUSH : 改變在所有寫入 fd 引用的物件的輸出都被傳輸後生效,所有已接受但未讀入的輸入都在改變發生前丟棄(同TCSADRAIN,但會捨棄當前所有值)。
(三)tcsendbreak() // tc send break
傳送連續的 0 值位元流,持續一段時間,如果終端使用非同步序列資料傳輸的話。如果 duration 是 0,它至少傳輸 0.25 秒,不會超過 0.5 秒。如果 duration 非零,它傳送的時間長度由實現定義。
如果終端並非使用非同步序列資料傳輸,tcsendbreak() 什麼都不做。
(四)tcdrain() //tc drain
等待直到所有寫入 fd 引用的物件的輸出都被傳輸。
(五)tcflush() //tc flush
丟棄要寫入 引用的物件,但是尚未傳輸的資料,或者收到但是尚未讀取的資料,取決於 queue_selector 的值:
TCIFLUSH : 重新整理收到的資料但是不讀
TCOFLUSH : 重新整理寫入的資料但是不傳送
TCIOFLUSH :同時重新整理收到的資料但是不讀,並且重新整理寫入的資料但是不傳送
(六)tcflow() //tc flow
掛起 fd 引用的物件上的資料傳輸或接收,取決於 action 的值:
TCOOFF : 掛起輸出
TCOON : 重新開始被掛起的輸出
TCIOFF : 傳送一個 STOP 字元,停止終端裝置向系統傳送資料
TCION : 傳送一個 START 字元,使終端裝置向系統傳輸資料
開啟一個終端裝置時的預設設定是輸入和輸出都沒有掛起。
(七)波特率函式
被用來獲取和設定 termios 結構中,輸入和輸出波特率的值。新值不會馬上生效,直到成功呼叫了 tcsetattr() 函式。
設定速度為 B0 使得 modem "掛機"。與 B38400 相應的實際位元率可以用 setserial(8) 調整。
輸入和輸出波特率被保存於 termios 結構中。
cfmakeraw 設定終端屬性如下:
//輸入模式標誌
termios_p->c_iflag &= ~( IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON );
// 輸出模式標誌
termios_p->c_oflag &= ~OPOST;
// 本地模式標誌
termios_p->c_lflag &= ~( ECHO | ECHONL | ICANON | ISIG | IEXTEN );
// 控制模式標誌
termios_p->c_cflag &= ~(CSIZE | PARENB);
termios_p->c_cflag |= CS8;
cf get o speed
cf set o speed
cf get i speed
cf set i speed
1.cfgetospeed() 返回 termios_p 指向的 termios 結構中儲存的輸出波特率
2.cfsetospeed() 設定 termios_p 指向的 termios 結構中儲存的輸出波特率為 speed。取值必須是以下常量之一:
B0 B50 B75 B110 B134 B150 B200 B300 B600 B1200 B1800 B2400 B4800 B9600 B19200 B38400 B57600 B115200 B230400
其中:零值 B0 用來中斷連線。如果指定了 B0,不應當再假定存在連線。通常,這樣將斷開連線。CBAUDEX 是一個掩碼,指示高於 POSIX.1 定義的速度的那一些 (57600 及以上)。因此,B57600 & CBAUDEX 為非零。
3.cfgetispeed() 返回 termios 結構中儲存的輸入波特率。
4.cfsetispeed() 設定 termios 結構中儲存的輸入波特率為 speed。如果輸入波特率被設為0,實際輸入波特率將等於輸出波特率。
RETURN VALUE 返回值
1.cfgetispeed() 返回 termios 結構中儲存的輸入波特率。
2.cfgetospeed() 返回 termios 結構中儲存的輸出波特率。
3.其他函式返回:
(1)0:成功
(2) -1:失敗,
並且為 errno 置值來指示錯誤。
tcsetattr() 任何一個設定屬性設定成功,都會返回成功,但因為要設定幾個屬性,但並不確定每個個都成成功,因此,要呼叫tcgetattr 來讀取驗證。
注意 tcsetattr() 返回成功,如果任何所要求的修改可以實現的話。因此,當進行多重修改時,應當在這個函式之後再次呼叫 tcgetattr() 來檢測是否所有修改都成功實現
#if 0
#include <termios.h>
#include <unistd.h>
獲取 fd 串列埠終端的屬性,儲存到 tremios_p
int tcgetattr(int fd, struct termios *termios_p);
設定 tremios_p 串列埠終端屬性到 fd 中,使用 optional_action
int tcsetattr(int fd, int optional_actions,
const struct termios *termios_p);
optional_actions 可取值:
TCSANOW 立即生效
TCSADRAIN 所有寫到 fd 的輸出已完成後才生效
TCSAFLUSH 所有寫到 fd 的輸出已完成,並且輸入已接收完成,
但還未被讀取
int tcsendbreak(int fd, int duration);
int tcdrain(int fd);
重新整理串列埠終端 fd。queue_selector 可取值:
TCIFLUSH 重新整理接收的資料而不讀
TCOFLUSH 重新整理寫的資料而不傳送
TCIOFLUSH 重新整理接收和寫的資料而不讀和傳送
int tcflush(int fd, int queue_selector);
int tcflow(int fd, int action);
void cfmakeraw(struct termios *termios_p);
獲取輸入波特率
speed_t cfgetispeed(const struct termios *termios_p);
獲取輸出波特率
speed_t cfgetospeed(const struct termios *termios_p);
設定輸入波特率
int cfsetispeed(struct termios *termios_p, speed_t speed);
設定輸出波特率
int cfsetospeed(struct termios *termios_p, speed_t speed);
設定輸入/輸出波特率
int cfsetspeed(struct termios *termios_p, speed_t speed);
struct termios 結構體至少包含以下成員:
tcflag_t c_iflag; /* input modes */
tcflag_t c_oflag; /* output modes */
tcflag_t c_cflag; /* control modes */
tcflag_t c_lflag; /* local modes */
cc_t c_cc[NCCS]; /* special characters */
非規範模式下,輸入立即可用,對 read 來說,c_cc[NCSS] 陣列的
c_cc[VMIN] 和 c_cc[VTIME] 有以下四種情況:
c_cc[VMIN] == 0, c_cc[VTIME] == 0:
輪詢讀,無資料立即返回,read 返回 0
c_cc[VMIN] > 0, c_cc[VTIME] == 0:
阻塞讀,直到讀到請求的位元組數(即 c_cc[VMIN] 個位元組)
c_cc[VMIN] == 0, c_cc[VTIME] > 0:
帶超時的讀,如果在超時時間內有資料,則返回資料,如果超時到期,
沒有資料,則返回 0
c_cc[VMIN] > 0, c_cc[VTIME] > 0:
帶超時及請求位元組的讀,在超時期內等待請求的位元組數,超時到期,
不管請求的資料是否滿足都會返回,如果請求的資料滿足,會立即返回。
原始模式的典型配置:
termios_p->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
| INLCR | IGNCR | ICRNL | IXON);
termios_p->c_oflag &= ~OPOST;
termios_p->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
termios_p->c_cflag &= ~(CSIZE | PARENB);
termios_p->c_cflag |= CS8;
以上函式的返回值:
除 cfgetispeed 和 cfgetospeed 返回波特率外,其它的函式:
成功返回 0,失敗返回 -1
注意:tcsetattr 函式只有請求的設定有一個生效,就會返回成功,
因此要驗證是否全部設定成功,應該使用 tcgetattr 獲取
屬性後,與設定的屬性進行對比來驗證是否全部設定成功。
#endif
程式設計:
開啟串列埠
/*
在 Linux 下串列埠檔案是位於 /dev 下的
開啟串列埠是通過使用標準的檔案開啟函式open操作:
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
static int fd;
int uart_open(int fd,const char *pathname)
{
assert(pathname);
fd = open(pathname,O_RDWR|O_NOCTTY| O_NONBLOCK);
if(fd == -1)
{
perror("Open UART failed!");
return -1;
}
return fd;
}
其中:
O_NOCTTY |
如果路徑名指向終端裝置,不要把這個裝置用作控制終端 |
O_NONBLOCK |
如果路徑名指向 FIFO/塊檔案/字元檔案,則把檔案的開啟和後繼 I/O設定為非阻塞模式(nonblocking mode) |
對於串列埠的開啟操作,必須使用O_NOCTTY引數,它表示開啟的是一個終端裝置,程式不會成為該埠的控制終端。如果不使用此標誌,任務的一個輸入都會影響程序。如鍵盤上過來的Ctrl+C中止訊號等都將影響程序。
設定串列埠
串列埠初始化需要設定串列埠波特率,資料流控制,幀的格式(即資料位個數,停止位,校驗位,資料流控制)
int uart_set(int fd,int baude,int c_flow,int bits,char parity,int stop)
{
struct termios options;
/*獲取終端屬性*/
if(tcgetattr(fd,&options) < 0)
{
perror("tcgetattr error");
return -1;
}
/*設定輸入輸出波特率,兩者保持一致*/
switch(baude)
{
case 4800:
cfsetispeed(&options,B4800);
cfsetospeed(&options,B4800);
break;
case 9600:
cfsetispeed(&options,B9600);
cfsetospeed(&options,B9600);
break;
case 19200:
cfsetispeed(&options,B19200);
cfsetospeed(&options,B19200);
break;
case 38400:
cfsetispeed(&options,B38400);
cfsetospeed(&options,B38400);
break;
default:
fprintf(stderr,"Unkown baude!\n");
return -1;
}
/*設定控制模式*/
options.c_cflag |= CLOCAL;//保證程式不佔用串列埠
options.c_cflag |= CREAD;//保證程式可以從串列埠中讀取資料
/*設定資料流控制*/
switch(c_flow)
{
case 0://不進行流控制
options.c_cflag &= ~CRTSCTS;
break;
case 1://進行硬體流控制
options.c_cflag |= CRTSCTS;
break;
case 2://進行軟體流控制
options.c_cflag |= IXON|IXOFF|IXANY;
break;
default:
fprintf(stderr,"Unkown c_flow!\n");
return -1;
}
/*設定資料位*/
switch(bits)
{
case 5:
options.c_cflag &= ~CSIZE;//遮蔽其它標誌位
options.c_cflag |= CS5;
break;
case 6:
options.c_cflag &= ~CSIZE;//遮蔽其它標誌位
options.c_cflag |= CS6;
break;
case 7:
options.c_cflag &= ~CSIZE;//遮蔽其它標誌位
options.c_cflag |= CS7;
break;
case 8:
options.c_cflag &= ~CSIZE;//遮蔽其它標誌位
options.c_cflag |= CS8;
break;
default:
fprintf(stderr,"Unkown bits!\n");
return -1;
}
/*設定校驗位*/
switch(parity)
{
/*無奇偶校驗位*/
case 'n':
case 'N':
options.c_cflag &= ~PARENB;//PARENB:產生奇偶位,執行奇偶校驗
options.c_cflag &= ~INPCK;//INPCK:使奇偶校驗起作用
break;
/*設為空格,即停止位為2位*/
case 's':
case 'S':
options.c_cflag &= ~PARENB;//PARENB:產生奇偶位,執行奇偶校驗
options.c_cflag &= ~CSTOPB;//CSTOPB:使用兩位停止位
break;
/*設定奇校驗*/
case 'o':
case 'O':
options.c_cflag |= PARENB;//PARENB:產生奇偶位,執行奇偶校驗
options.c_cflag |= PARODD;//PARODD:若設定則為奇校驗,否則為偶校驗
options.c_cflag |= INPCK;//INPCK:使奇偶校驗起作用
options.c_cflag |= ISTRIP;//ISTRIP:若設定則有效輸入數字被剝離7個位元組,否則保留全部8位
break;
/*設定偶校驗*/
case 'e':
case 'E':
options.c_cflag |= PARENB;//PARENB:產生奇偶位,執行奇偶校驗
options.c_cflag &= ~PARODD;//PARODD:若設定則為奇校驗,否則為偶校驗
options.c_cflag |= INPCK;//INPCK:使奇偶校驗起作用
options.c_cflag |= ISTRIP;//ISTRIP:若設定則有效輸入數字被剝離7個位元組,否則保留全部8位
break;
default:
fprintf(stderr,"Unkown parity!\n");
return -1;
}
/*設定停止位*/
switch(stop)
{
case 1:
options.c_cflag &= ~CSTOPB;//CSTOPB:使用兩位停止位
break;
case 2:
options.c_cflag |= CSTOPB;//CSTOPB:使用兩位停止位
break;
default:
fprintf(stderr,"Unkown stop!\n");
return -1;
}
/*設定輸出模式為原始輸出*/
options.c_oflag &= ~OPOST;//OPOST:若設定則按定義的輸出處理,否則所有c_oflag失效
/*設定本地模式為原始模式*/
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
/*
*ICANON:允許規範模式進行輸入處理
*ECHO:允許輸入字元的本地回顯
*ECHOE:在接收EPASE時執行Backspace,Space,Backspace組合
*ISIG:允許訊號
*/
/*設定等待時間和最小接受字元*/
options.c_cc[VTIME] = 0;//可以在select中設定
options.c_cc[VMIN] = 1;//最少讀取一個字元
/*如果發生資料溢位,只接受資料,但是不進行讀操作*/
tcflush(fd,TCIFLUSH);
/*啟用配置*/
if(tcsetattr(fd,TCSANOW,&options) < 0)
{
perror("tcsetattr failed");
return -1;
}
return 0;
}
讀寫串列埠
ssize_t safe_write(int fd, const void *vptr, size_t n)
{
size_t nleft;
ssize_t nwritten;
const char *ptr;
ptr = vptr;
nleft = n;
while(nleft > 0)
{
if((nwritten = write(fd, ptr, nleft)) <= 0)
{
if(nwritten < 0&&errno == EINTR)
nwritten = 0;
else
return -1;
}
nleft -= nwritten;
ptr += nwritten;
}
return(n);
}
ssize_t safe_read(int fd,void *vptr,size_t n)
{
size_t nleft;
ssize_t nread;
char *ptr;
ptr=vptr;
nleft=n;
while(nleft > 0)
{
if((nread = read(fd,ptr,nleft)) < 0)
{
if(errno == EINTR)//被訊號中斷
nread = 0;
else
return -1;
}
else
if(nread == 0)
break;
nleft -= nread;
ptr += nread;
}
return (n-nleft);
}
int uart_read(int fd,char *r_buf,size_t len)
{
ssize_t cnt = 0;
fd_set rfds;
struct timeval time;
/*將檔案描述符加入讀描述符集合*/
FD_ZERO(&rfds);
FD_SET(fd,&rfds);
/*設定超時為15s*/
time.tv_sec = 15;
time.tv_usec = 0;
/*實現串列埠的多路I/O*/
ret = select(fd+1,&rfds,NULL,NULL,&time);
switch(ret)
{
case -1:
fprintf(stderr,"select error!\n");
return -1;
case 0:
fprintf(stderr,"time over!\n");
return -1;
default:
cnt = safe_read(fd,r_buf,len);
if(cnt == -1)
{
fprintf(stderr,"read error!\n");
return -1;
}
return cnt;
}
}
int uart_write(int fd,const char *w_buf,size_t len)
{
ssize_t cnt = 0;
cnt = safe_write(fd,w_buf,len);
if(cnt == -1)
{
fprintf(stderr,"write error!\n");
return -1;
}
return cnt;
}
關閉串列埠
int uart_close(int fd)
{
assert(fd);
close(fd);
/*可以在這裡做些清理工作*/
return 0;
}
完整程式碼
#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<unistd.h>
#include<assert.h>
#include<termios.h>
#include<string.h>
#include<sys/time.h>
#include<sys/types.h>
#include<errno.h>
static int ret;
static int fd;
/*
* 安全讀寫函式
*/
ssize_t safe_write(int fd, const void *vptr, size_t n)
{
size_t nleft;
ssize_t nwritten;
const char *ptr;
ptr = vptr;
nleft = n;
while(nleft > 0)
{
if((nwritten = write(fd, ptr, nleft)) <= 0)
{
if(nwritten < 0&&errno == EINTR)
nwritten = 0;
else
return -1;
}
nleft -= nwritten;
ptr += nwritten;
}
return(n);
}
ssize_t safe_read(int fd,void *vptr,size_t n)
{
size_t nleft;
ssize_t nread;
char *ptr;
ptr=vptr;
nleft=n;
while(nleft > 0)
{
if((nread = read(fd,ptr,nleft)) < 0)
{
if(errno == EINTR)//被訊號中斷
nread = 0;
else
return -1;
}
else
if(nread == 0)
break;
nleft -= nread;
ptr += nread;
}
return (n-nleft);
}
int uart_open(int fd,const char *pathname)
{
assert(pathname);
/*開啟串列埠*/
fd = open(pathname,O_RDWR|O_NOCTTY|O_NDELAY);
if(fd == -1)
{
perror("Open UART failed!");
return -1;
}
/*清除串列埠非阻塞標誌*/
if(fcntl(fd,F_SETFL,0) < 0)
{
fprintf(stderr,"fcntl failed!\n");
return -1;
}
return fd;
}
int uart_set(int fd,int baude,int c_flow,int bits,char parity,int stop)
{
struct termios options;
/*獲取終端屬性*/
if(tcgetattr(fd,&options) < 0)
{
perror("tcgetattr error");
return -1;
}
/*設定輸入輸出波特率,兩者保持一致*/
switch(baude)
{
case 4800:
cfsetispeed(&options,B4800);
cfsetospeed(&options,B4800);
break;
case 9600:
cfsetispeed(&options,B9600);
cfsetospeed(&options,B9600);
break;
case 19200:
cfsetispeed(&options,B19200);
cfsetospeed(&options,B19200);
break;
case 38400:
cfsetispeed(&options,B38400);
cfsetospeed(&options,B38400);
break;
default:
fprintf(stderr,"Unkown baude!\n");
return -1;
}
/*設定控制模式*/
options.c_cflag |= CLOCAL;//保證程式不佔用串列埠
options.c_cflag |= CREAD;//保證程式可以從串列埠中讀取資料
/*設定資料流控制*/
switch(c_flow)
{
case 0://不進行流控制
options.c_cflag &= ~CRTSCTS;
break;
case 1://進行硬體流控制
options.c_cflag |= CRTSCTS;
break;
case 2://進行軟體流控制
options.c_cflag |= IXON|IXOFF|IXANY;
break;
default:
fprintf(stderr,"Unkown c_flow!\n");
return -1;
}
/*設定資料位*/
switch(bits)
{
case 5:
options.c_cflag &= ~CSIZE;//遮蔽其它標誌位
options.c_cflag |= CS5;
break;
case 6:
options.c_cflag &= ~CSIZE;//遮蔽其它標誌位
options.c_cflag |= CS6;
break;
case 7:
options.c_cflag &= ~CSIZE;//遮蔽其它標誌位
options.c_cflag |= CS7;
break;
case 8:
options.c_cflag &= ~CSIZE;//遮蔽其它標誌位
options.c_cflag |= CS8;
break;
default:
fprintf(stderr,"Unkown bits!\n");
return -1;
}
/*設定校驗位*/
switch(parity)
{
/*無奇偶校驗位*/
case 'n':
case 'N':
options.c_cflag &= ~PARENB;//PARENB:產生奇偶位,執行奇偶校驗
options.c_cflag &= ~INPCK;//INPCK:使奇偶校驗起作用
break;
/*設為空格,即停止位為2位*/
case 's':
case 'S':
options.c_cflag &= ~PARENB;//PARENB:產生奇偶位,執行奇偶校驗
options.c_cflag &= ~CSTOPB;//CSTOPB:使用兩位停止位
break;
/*設定奇校驗*/
case 'o':
case 'O':
options.c_cflag |= PARENB;//PARENB:產生奇偶位,執行奇偶校驗
options.c_cflag |= PARODD;//PARODD:若設定則為奇校驗,否則為偶校驗
options.c_cflag |= INPCK;//INPCK:使奇偶校驗起作用
options.c_cflag |= ISTRIP;//ISTRIP:若設定則有效輸入數字被剝離7個位元組,否則保留全部8位
break;
/*設定偶校驗*/
case 'e':
case 'E':
options.c_cflag |= PARENB;//PARENB:產生奇偶位,執行奇偶校驗
options.c_cflag &= ~PARODD;//PARODD:若設定則為奇校驗,否則為偶校驗
options.c_cflag |= INPCK;//INPCK:使奇偶校驗起作用
options.c_cflag |= ISTRIP;//ISTRIP:若設定則有效輸入數字被剝離7個位元組,否則保留全部8位
break;
default:
fprintf(stderr,"Unkown parity!\n");
return -1;
}
/*設定停止位*/
switch(stop)
{
case 1:
options.c_cflag &= ~CSTOPB;//CSTOPB:使用兩位停止位
break;
case 2:
options.c_cflag |= CSTOPB;//CSTOPB:使用兩位停止位
break;
default:
fprintf(stderr,"Unkown stop!\n");
return -1;
}
/*設定輸出模式為原始輸出*/
options.c_oflag &= ~OPOST;//OPOST:若設定則按定義的輸出處理,否則所有c_oflag失效
/*設定本地模式為原始模式*/
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
/*
*ICANON:允許規範模式進行輸入處理
*ECHO:允許輸入字元的本地回顯
*ECHOE:在接收EPASE時執行Backspace,Space,Backspace組合
*ISIG:允許訊號
*/
/*設定等待時間和最小接受字元*/
options.c_cc[VTIME] = 0;//可以在select中設定
options.c_cc[VMIN] = 1;//最少讀取一個字元
/*如果發生資料溢位,只接受資料,但是不進行讀操作*/
tcflush(fd,TCIFLUSH);
/*啟用配置*/
if(tcsetattr(fd,TCSANOW,&options) < 0)
{
perror("tcsetattr failed");
return -1;
}
return 0;
}
int uart_read(int fd,char *r_buf,size_t len)
{
ssize_t cnt = 0;
fd_set rfds;
struct timeval time;
/*將檔案描述符加入讀描述符集合*/
FD_ZERO(&rfds);
FD_SET(fd,&rfds);
/*設定超時為15s*/
time.tv_sec = 15;
time.tv_usec = 0;
/*實現串列埠的多路I/O*/
ret = select(fd+1,&rfds,NULL,NULL,&time);
switch(ret)
{
case -1:
fprintf(stderr,"select error!\n");
return -1;
case 0:
fprintf(stderr,"time over!\n");
return -1;
default:
cnt = safe_read(fd,r_buf,len);
if(cnt == -1)
{
fprintf(stderr,"read error!\n");
return -1;
}
return cnt;
}
}
int uart_write(int fd,const char *w_buf,size_t len)
{
ssize_t cnt = 0;
cnt = safe_write(fd,w_buf,len);
if(cnt == -1)
{
fprintf(stderr,"write error!\n");
return -1;
}
return cnt;
}
int uart_close(int fd)
{
assert(fd);
close(fd);
/*可以在這裡做些清理工作*/
return 0;
}
int main(void)
{
const char *w_buf = "something to write";
size_t w_len = sizeof(w_buf);
char r_buf[1024];
bzero(r_buf,1024);
fd = uart_open(fd,"/dev/ttyS0");/*串列埠號/dev/ttySn,USB口號/dev/ttyUSBn*/
if(fd == -1)
{
fprintf(stderr,"uart_open error\n");
exit(EXIT_FAILURE);
}
if(uart_set(fd,9600,0,8,'N',1) == -1)
{
fprintf(stderr,"uart set failed!\n");
exit(EXIT_FAILURE);
}
ret = uart_write(fd,w_buf,w_len);
if(ret == -1)
{
fprintf(stderr,"uart write failed!\n");
exit(EXIT_FAILURE);
}
while(1)
{
ret = uart_read(fd,r_buf,1024);
if(ret == -1)
{
fprintf(stderr,"uart read failed!\n");
exit(EXIT_FAILURE);
}
}
ret = uart_close(fd);
if(ret == -1)
{
fprintf(stderr,"uart_close error\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}