串行接口簡稱串口,也稱串行通信接口或串行通訊接口(通常指COM接口),是采用串行通信方式的擴展接口。串行接口(Serial Interface) 是指數據一位一位地順序傳送,其特點是通信線路簡單,只要一對傳輸線就可以實現雙向通信,大大降低了成本,特別適用於遠距離通信,但傳送速度較慢。根據信息的傳送方向,串行通訊可以進一步分為單工、半雙工和全雙工三種。串行接口按電氣標准及協議來分包括RS-232-C、RS-422、RS485等。
異步串行是指UART(Universal Asynchronous Receiver/Transmitter),通用異步接收/發送。UART包含TTL電平的串口和RS232電平的串口。 TTL電平是3.3V的,而RS232是負邏輯電平,它定義+5~+12V為低電平,而-12~-5V為高電平。UART作為異步串口通信協議的一種,工作原理是將傳輸數據的每個字符一位接一位地傳輸,其中各位的意義如下:
起始位:先發出一個邏輯”0”的信號,表示傳輸字符的開始。
數據位:緊接在起始位之后,數據位的個數可以是4、5、6、7、8等,構成一個字符,從最低位開始傳送,靠時鍾定位。
奇偶校驗位:數據位加上這一位后,使得“1”的位數應為偶數(偶校驗)或奇數(奇校驗),以此來校驗數據傳送的正確性。
停止位:它是一個字符數據的結束標志,可以是1位、1.5位、2位的高電平。 由於數據是在傳輸線上定時的,並且每一個設備有其自己的時鍾,很可能在通信中兩台設備間出現了小小的不同步。因此停止位不僅僅是表示傳輸的結束,並且提供計算機校正時鍾同步的機會。適用於停止位的位數越多,不同時鍾同步的容忍程度越大,但是數據傳輸率同時也越慢。
空閑位:處於邏輯“1”狀態,表示當前線路上沒有數據傳送。
此外,在異步通信中還有一個重要的參數,即波特率,它是衡量數據傳送速率的指標,表示每秒鍾傳送的符號數(symbol)。收發雙方的波特率必須保持一致,才能保證數據的正常通信。
在Linux系統中,所有的設備文件都位於/dev目錄下,以tty為前綴的文件,是終端設備。以tty加大寫S開頭的即為串口設備文件,如ttyS0即為串口0(即COM0)。但在嵌入式Linux中,串口是以ttySAC為前綴的,如ttySAC0即代表串口0。Linux系統中,要對文件進行相應地操作,必須先打開它,串口設備文件也不例外,所以需要調用open函數來打開對應的串口端口。另外在打開的同時,還需要設置打開文件的合適屬性(如可讀可寫、不將設備分配為控制終端、無延時模式等)。 比如,可執行語句“open("/dev/ttySAC0", O_RDWR | O_NOCTTY | O_NDELAY);”。
在進行串口通信之前,先要對打開的串口進行配置,其中的重要參數有:波特率、數據位數、停止位位數、奇偶校驗、硬件流量控制等。在Linux中,這些配置項目是通過一個名為termios 結構體來實現的。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_oflag,它是輸出模式標志,控制終端輸出方式,具體參數如下表所示。
接下來是c_cflag,它為控制模式標志,指定終端硬件控制信息,具體參數如下表所示。
下面是c_lflag,它為本地模式標志,控制終端編輯功能,具體參數如下表所示。
最后看c_cc[NCCS],它是控制字符,用於保存終端驅動程序中的特殊字符,具體參數如下表所示。
以上就是termios涉及到的全部內容,一般的串口通信,只需要操作c_cflag即可,可設置波特率、數據位、校驗位、停止位等,配置時需通過“與”、“或”的方式進行操作。此外,為了方便操作,系統還給出了一些操作函數,下面就是一些常用的操作函數。
1、讀取當前參數函數:
int tcgetattr(int fd,struct termios *termios_p)
fd:open操作后返回的文件描述符
*termios_p:為前面介紹的結構體
初始化開始前調用這個函數.
2、獲取當前波特率函數:
int speed_t cfgetispeed(const struct termios *termios_p)
int speed_t cfgetospeed(const struct termios *termios_p)
*termios_p:為前面介紹的結構體
成功返回0,失敗返回-1
3、波特率設置函數:
int cfsetispeed(struct termios *termios_p,speed_t speed)
int cfsetospeed(struct termios *termios_p,speed_t speed)
*termios_p:為前面介紹的結構體
speed:波特率,常用B2400,B4800,B9600,B115200,B460800
成功返回0,失敗返回-1
4、清空buffer數據函數:
int tcflush(int fd,int queue_selector)
queue_selector:有三個常用宏定義
TCIFLUSH:清空正讀的數據,且不會讀出
TCOFLUSH:清空正寫入的數據,且不會發送到終端
TCIOFLUSH:清空所有正在發生的I/O數據.
成功返回0,失敗返回-1
5、設置串口參數函數:
int tcsetattr(int fd,int optional_actions,cons struct termios *termios_p)
optional_actions:有三個常用宏定義
TCSANOW:不等數據傳輸完畢,立即改變屬性
TCSADRAIN:等所有數據傳輸完畢,再改變屬性
TCSAFLUSH:清空輸入輸出緩沖區才改變屬性
成功返回0,失敗返回-1
普通串口的配置步驟如下:
1、保存原先的串口配置,用tcgetattr()函數。
struct termios newtio,oldtio;
tcgetattr(fd,&oldtio);
2、激活選項有CLOCAL和CREAD,用於本地連接和接收使用。
newtio.c_cflag | = CLOCAL | CREAD;
3、設置波特率,使用函數cfsetispeed、cfsetospeed。
cfsetispeed(&newtio,B115200);
cfsetospeed(&newtio,B115200);
4、設置數據位,需使用掩碼設置。
newtio.c_cflag &= ~CSIZE;
newtio.c_cflag |= CS8;
5、設置奇偶校驗位,使用c_cflag和c_iflag。
(1)設置奇校驗:
newtio.c_cflag |= PARENB;
newtio.c_cflag |= PARODD;
newtio.c_iflag |= (INPCK | ISTRIP);
(2)設置偶校驗:
newtio.c_iflag |= (INPCK|ISTRIP);
newtio.c_cflag |= PARENB;
newtio.c_cflag &= ~PARODD;
(3)設置無校驗:
newtio.c_cflag &= ~PARENB;
6、設置停止位,通過激活c_cflag中的CSTOPB實現。若停止位為1,則清除CSTOPB,若停止位為2,則激活CSTOPB。
newtio.c_cflag &= ~CSTOPB;
7、設置最少字符和等待時間,對於接收字符和等待時間沒有特別的要求時,可設為0。
newtio.c_cc[VTIME] = 0;
newtio.c_cc[VMIN] = 0;
8、處理要寫入的引用對象
tcflush函數刷清(拋棄)輸入緩存(終端驅動程序已接收到,但用戶程序尚未讀)或輸出緩存(用戶程序已經寫,但尚未發送)。
int tcflush(int filedes,int quene)
quene數應當是下列三個常數之一:
TCIFLUSH 刷清輸入隊列
TCOFLUSH 刷清輸出隊列
TCIOFLUSH 刷清輸入、輸出隊列
如:tcflush(fd,TCIFLUSH);
9、激活配置,在完成配置后,需要激活配置使其生效。使用tcsetattr()函數。
int tcsetattr(int filedes,int opt,const struct termios *termptr);
opt可以指定在什么時候新的終端屬性才起作用。
TCSANOW 更改立即發生
TCSADRAIN 發送了所有輸出后更改才發生,若更改輸出參數則應使用此選項。
TCSAFLUSH 發送了所有輸出后更改才發生,更進一步,在更改發生時未讀的所有輸入數據都被刪除。
如:tcsetattr(fd,TCSANOW,&newtio);
以上設置的順序也非常重要,若不按上述順序進行設置,很可能會導致串口通信不正常。
在使用open()函數打開串口之后,緊接着應恢復串口為阻塞狀態,用於等待串口數據的讀入。可使用fcntl()函數來實現,如下:
fcntl(fd,F_SETFL,0); //F_SETFL:設置文件flag為0,即默認的阻塞狀態,返回值應為0
接着應測試打開的文件描述符是否應用一個終端設備,以進一步確認串口是否正確打開。可使用isatty()函數來實現,如下:
isatty(STDIN_FILENO); //返回值為非0即表明成功
串口設置時,應當在完成以上兩步之后,再對termios進行設置。以下給出一個用於設置串口波特率等參數的函數,僅供參考。
int Setup_Serial(int fd,int nSpeed, int nBits, char nEvent, int nStop)
{
struct termios newtio,oldtio;
if ( tcgetattr( fd,&oldtio) != 0)
{
perror("Setup Serial save error!");
return -1;
}
bzero( &newtio, sizeof( newtio ) );
newtio.c_cflag |= CLOCAL | CREAD;
newtio.c_cflag &= ~CSIZE;
switch( nBits )
{
case 5:
newtio.c_cflag |= CS5;
break;
case 6:
newtio.c_cflag |= CS6;
break;
case 7:
newtio.c_cflag |= CS7;
break;
case 8:
newtio.c_cflag |= CS8;
break;
}
switch( nEvent )
{
case 'O':
newtio.c_cflag |= PARENB;
newtio.c_cflag |= PARODD;
newtio.c_iflag |= (INPCK | ISTRIP);
break;
case 'E':
newtio.c_iflag |= (INPCK | ISTRIP);
newtio.c_cflag |= PARENB;
newtio.c_cflag &= ~PARODD;
break;
case 'N':
newtio.c_cflag &= ~PARENB;
break;
}
switch( nSpeed )
{
case 2400:
cfsetispeed(&newtio, B2400);
cfsetospeed(&newtio, B2400);
break;
case 4800:
cfsetispeed(&newtio, B4800);
cfsetospeed(&newtio, B4800);
break;
case 9600:
cfsetispeed(&newtio, B9600);
cfsetospeed(&newtio, B9600);
break;
case 115200:
cfsetispeed(&newtio, B115200);
cfsetospeed(&newtio, B115200);
break;
case 460800:
cfsetispeed(&newtio, B460800);
cfsetospeed(&newtio, B460800);
break;
default:
cfsetispeed(&newtio, B9600);
cfsetospeed(&newtio, B9600);
break;
}
if( nStop == 1 )
newtio.c_cflag &= ~CSTOPB;
else if ( nStop == 2 )
newtio.c_cflag |= CSTOPB;
newtio.c_cc[VTIME] = 0;
newtio.c_cc[VMIN] = 0;
tcflush(fd,TCIFLUSH);
if((tcsetattr(fd,TCSANOW,&newtio))!=0)
{
perror("Setup Serial error!");
return -1;
}
// printf("Setup Serial complete!\n");
return 0;
}
在調用時,只需要按照需要填寫參數即可,比如:Setup_Serial(fd, 115200, 8, N, 1),即把fd打開的串口設置成波特率115200,數據位8位,無校驗,1位停止位的模式。
在完成了以上設置之后,就可以通過讀寫fd來實現串口的數據通信了,比如要把串口接收到的數據讀入Buff中,可執行“read(fd, Buff, 8);”,就會讀入8字節數據,要向串口發送Buff中的數據,可執行“write(fd, Buff, 8);”,就會發送出8字節數據。需要注意的是,在嵌入式Linux系統中,受緩沖區的限制,一次只能讀取8字節數據,多於8字節的需要分次讀取,這就是主程序設計的事情了。