資深程式設計師告訴你串口配置的詳細流程,不容錯過

資深程序員告訴你串口配置的詳細流程,不容錯過

1,按照通信的基本方式分類,可以分為以下兩類:

1)並行通信

2)串行通信兩種。

並行通信是指利用多條數據傳輸線將一個資料的各位同時傳送。它的特點是傳輸速度

快,適用於短距離通信,但要求傳輸速度較高的應用場合。

串行通信是指利用一條傳輸線將資料一位位地順序傳送。特點是通信線路簡單,利用

簡單的線纜就可實現通信,降低成本,適用於遠距離通信,但傳輸速度慢的應用場合。

那麼接下來我們就來分析串口設置:

一般情況下,我們對串口中最基本的包括:波特率設置,校驗位和停止位設置。然而串口的設置主要是設置struct termios結構體的各成員值,如下所示:

struct termio

{

unsigned short c_iflag; /* 輸入模式標誌 */

unsigned short c_oflag; /* 輸出模式標誌 */

unsigned short c_cflag; /* 控制模式標誌*/

unsigned short c_lflag; /*本地模式標誌 */

unsigned char c_line; /* line discipline */

unsigned char c_cc[NCC]; /* control characters */

};

在這個結構中最為重要的是c_cflag,通過對它的賦值,用戶可以設置波特率、字符大小、

數據位、停止位、奇偶校驗位和硬件流控等。另外c_iflag 和c_cc 也是比較常用的標誌。

波特率:即每秒傳輸的位數,一般情況下有以下幾種:

B0 0波特率(放棄DTR)

B1800 1800波特率

B2400 2400波特率

B4800 4800波特率

B9600 9600波特率

B19200 19200波特率

B38400 38400波特率

B57600 57600波特率

B115200 115200波特率

int set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop)

{

struct termios newtio,oldtio;

/*保存測試現有串口參數設置,在這裡如果串口號等出錯,會有相關的出錯信息*/

if ( tcgetattr( fd,&oldtio) != 0)

{

perror("SetupSerial 1");

return -1;

}

bzero( &newtio, sizeof( newtio ) );

/*步驟一,設置字符大小*/

newtio.c_cflag |= CLOCAL | CREAD;

newtio.c_cflag &= ~CSIZE;

/*設置停止位*/

switch( nBits )

{

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("com set error");

return -1;

}

printf("set done!\n");

return 0;

}

在配置完串口的相關屬性後,就可對串口進行打開,讀寫操作了。其使用方式與文件操作一樣,區別在於串口是一個終端設備。

2,打開串口:

串口位於/dev中,可作為標準文件的形式打開,其中:

串口1 /dev/ttyS0

串口2 /dev/ttyS1

fd = open( "/dev/ttyS0", O_RDWR|O_NOCTTY|O_NDELAY);

Open函數中除普通參數外,另有兩個參數O_NOCTTY和O_NDELAY。

O_NOCTTY: 通知linix系統,這個程序不會成為這個端口的控制終端。

O_NDELAY: 通知Linux系統不關心DCD信號線所處的狀態(端口的另一端是否激活或者停止)。

然後,恢復串口的狀態為阻塞狀態,用於等待串口數據的讀入。用fcntl函數:

fcntl(fd, F_SETFL, 0);

接著,測試打開的文件描述府是否引用一個終端設備,以進一步確認串口是否正確打開。

isatty(STDIN_FILENO);

串口的讀寫與普通文件一樣,使用read,write函數。

read(fd,buff,8);

write(fd,buff,8);

3,linux串口編程需要的頭文件

#include //標準輸入輸出定義

#include //標準函數庫定義

#include //Unix標準函數定義

#include

#include

#include //文件控制定義

#include //POSIX中斷控制定義

#include //錯誤號定義

4,設置數據位、停止位和校驗位

以下是幾個數據位、停止位和校驗位的設置方法:(以下均為1位停止位)

8位數據位、無校驗位:

Opt.c_cflag &= ~PARENB;

Opt.c_cflag &= ~CSTOPB;

Opt.c_cflag &= ~CSIZE;

Opt.c_cflag |= CS8;

7位數據位、奇校驗:

Opt.c_cflag |= PARENB;

Opt.c_cflag |= PARODD;

Opt.c_cflag &= ~CSTOPB;

Opt.c_cflag &= ~CSIZE;

Opt.c_cflag |= CS7;

7位數據位、偶校驗:

Opt.c_cflag |= PARENB;

Opt.c_cflag &= ~PARODD;

Opt.c_cflag &= ~CSTOPB;

Opt.c_cflag &= ~CSIZE;

Opt.c_cflag |= CS7;

7位數據位、Space校驗:

Opt.c_cflag &= ~PARENB;

Opt.c_cflag &= ~CSTOPB;

Opt.c_cflag &= ~CSIZE;

Opt.c_cflag |= CS7;

5,某些設置項

在上面我們看到一些比較特殊的設置,下面簡述一下他們的作用。

c_cc數組的VSTART和VSTOP元素被設定成DC1和DC3,代表ASCII標準的XON和XOFF字符,如果在傳輸這兩個字符的時候就傳不過去,需要把軟件流控制屏蔽,即:

Opt.c_iflag &= ~ (IXON | IXOFF | IXANY);

有時候,在用write發送數據時沒有鍵入回車,信息就發送不出去,這主要是因為我們在輸入輸出時是按照規範模式接收到回車或換行才發送,而更多情況下我們是不必鍵入回車或換行的。此時應轉換到行方式輸入,不經處理直接發送,設置如下:

Opt.c_lflag &= ~ (ICANON | ECHO | ECHOE | ISIG);

還存在這樣的情況:發送字符0X0d的時候,往往接收端得到的字符是0X0a,原因是因為在串口設置中c_iflag和c_oflag中存在從NL-CR和CR-NL的映射,即串口能把回車和換行當成同一個字符,可以進行如下設置屏蔽之:

Opt.c_iflag &= ~ (INLCR | ICRNL | IGNCR);

Opt.c_oflag &= ~(ONLCR | OCRNL);

注意:控制符VTIME和VMIN之間有複雜的關係。VTIME定義要求等待的時間(百毫米,通常是unsigned char變量),而VMIN定義了要求等待的最小字節數(相比之下,read函數的第三個參數指定了要求讀的最大字節數)。

如果VTIME=0,VMIN=要求等待讀取的最小字節數,read必須在讀取了VMIN個字節的數據或者收到一個信號才會返回。

如果VTIME=時間量,VMIN=0,不管能否讀取到數據,read也要等待VTIME的時間量。

如果VTIME=時間量,VMIN=要求等待讀取的最小字節數,那麼將從read讀取第一個字節的數據時開始計時,並會在讀取到VMIN個字節或者VTIME時間後返回。

如果VTIME=0,VMIN=0,不管能否讀取到數據,read都會立即返回

讀寫串口

發送數據方式如下,write函數將返回寫的位數或者當錯誤時為-1。

char buffer[1024];

int length;

int nByte;

nByte = write(fd, buffer, length);

讀取數據方式如下,原始數據模式下每個read函數將返回實際串口收到的字符數,如果串口中沒有字符可用,回叫將會阻塞直到以下幾種情況:有字符進入;一個間隔計時器失效;錯誤發送。

在打開串口成功後,使用fcntl(fd, F_SETFL, FNDELAY)語句,可以使read函數立即返回而不阻塞。FNDELAY選項使read函數在串口無字符時立即返回且為0。

char buffer[1024];

int length;

int readByte;

readByte = read(fd, buffer, len);

注 意:設置為原始模式傳輸數據的話,read函數返回的字符數是實際串口收到的字符數。Linux下直接用read讀串口可能會造成堵塞,或者數據讀出錯 誤,此時可使用tcntl或者select等函數實現異步讀取。用select先查詢com口,再用read去讀就可以避免上述錯誤。

6,關閉串口

串口作為文件來處理,所以一般的關閉文件函數即可:

close(fd);


分享到:


相關文章: