資(zi)深(shen)程序(xu)員(yuan)告訴你(ni)串口(kou)配置的(de)詳細流程,不(bu)容錯過
時間:2018-06-20 來源:未(wei)知
1,按照通(tong)信的(de)基本方式分(fen)類(lei),可(ke)以分(fen)為以下兩類(lei):
1)并(bing)行通(tong)信
2)串行通信兩種。
并(bing)行通(tong)信是(shi)指利用多條數據傳(chuan)輸(shu)(shu)線將(jiang)一個(ge)資料的各位(wei)同時傳(chuan)送。它的特點是(shi)傳(chuan)輸(shu)(shu)速度
快,適用于(yu)短距離通信,但要求傳輸速(su)度(du)較(jiao)高的應用場合(he)。
串行通信是指利(li)(li)用一條傳(chuan)輸(shu)線將資料一位(wei)位(wei)地順序傳(chuan)送。特點是通信線路簡單,利(li)(li)用
簡單的(de)線(xian)纜就可實現通信(xin),降低成本,適用于遠距離通信(xin),但傳輸(shu)速度慢(man)的(de)應用場合。
那(nei)么接下來(lai)我們就(jiu)來(lai)分析串口設置(zhi):
一(yi)般情況下(xia),我們對(dui)串(chuan)口中最基本的包(bao)括(kuo):波特率設置,校驗位和停止位設置。然而(er)串(chuan)口的設置主要(yao)是設置struct termios結(jie)構體的各成員值,如下(xia)所示(shi):
struct termio
{
unsigned short c_iflag; /* 輸入模式標志 */
unsigned short c_oflag; /* 輸出(chu)模式標志(zhi) */
unsigned short c_cflag; /* 控制模(mo)式標志(zhi)*/
unsigned short c_lflag; /*本地模(mo)式標志 */
unsigned char c_line; /* line discipline */
unsigned char c_cc[NCC]; /* control characters */
};
在這個結(jie)構中最(zui)為重要的是c_cflag,通(tong)過對(dui)它(ta)的賦值,用戶(hu)可以設置波(bo)特率、字符大小、
數(shu)據位(wei)、停止(zhi)位(wei)、奇偶校驗(yan)位(wei)和(he)硬件流控等(deng)。另外c_iflag 和(he)c_cc 也是比較常用(yong)的標志。
波特率:即每秒傳輸的(de)位(wei)數,一般情況下(xia)有以(yi)下(xia)幾種:
B0 0波特率(放棄DTR)
B1800 1800波特率
B2400 2400波特率(lv)
B4800 4800波特率(lv)
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;
/*保存(cun)測試現(xian)有串口(kou)參數(shu)設置,在(zai)這里如(ru)果串口(kou)號等出錯,會(hui)有相關的(de)出錯信息*/
if ( tcgetattr( fd,&oldtio) != 0)
{
perror("SetupSerial 1");
return -1;
}
bzero( &newtio, sizeof( newtio ) );
/*步驟一,設置字(zi)符大小*/
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;
}
/*設置奇偶校(xiao)驗位*/
switch( nEvent )
{
case 'O': //奇(qi)數
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': //無奇偶校(xiao)驗位
newtio.c_cflag &= ~PARENB;
break;
}
/*設置波(bo)特率*/
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;
}
/*設(she)置停止(zhi)位(wei)*/
if( nStop == 1 )
newtio.c_cflag &= ~CSTOPB;
else if ( nStop == 2 )
newtio.c_cflag |= CSTOPB;
/*設置等待時(shi)間和最小(xiao)接(jie)收字符*/
newtio.c_cc[VTIME] = 0;
newtio.c_cc[VMIN] = 0;
/*處(chu)理未接(jie)收字符*/
tcflush(fd,TCIFLUSH);
/*激活(huo)新(xin)配置*/
if((tcsetattr(fd,TCSANOW,&newtio))!=0)
{
perror("com set error");
return -1;
}
printf("set done!\n");
return 0;
}
在(zai)配置完串口的相(xiang)關屬性(xing)后,就可對串口進行打(da)開(kai),讀寫操作了。其使(shi)用方(fang)式與文(wen)件操作一樣(yang),區別在(zai)于串口是一個(ge)終(zhong)端設備(bei)。
2,打(da)開串口(kou):
串口位于/dev中,可作為標準文件(jian)的形式(shi)打開,其(qi)中:
串(chuan)口1 /dev/ttyS0
串口2 /dev/ttyS1
fd = open( "/dev/ttyS0", O_RDWR|O_NOCTTY|O_NDELAY);
Open函數中除(chu)普(pu)通參(can)(can)數外,另有兩個參(can)(can)數O_NOCTTY和O_NDELAY。
O_NOCTTY: 通知linix系統,這個程序不會成為這個端口的控制終(zhong)端。
O_NDELAY: 通知Linux系統不(bu)關(guan)心DCD信號(hao)線(xian)所處(chu)的狀態(端口(kou)的另一端是否激活(huo)或者停(ting)止(zhi))。
然(ran)后,恢復串口的(de)狀態為阻塞狀態,用于(yu)等待串口數(shu)據的(de)讀入。用fcntl函數(shu):
fcntl(fd, F_SETFL, 0);
接(jie)著,測試打(da)開(kai)的(de)文件描述府是(shi)否引(yin)用一個終端設備,以進一步(bu)確認串(chuan)口是(shi)否正確打(da)開(kai)。
isatty(STDIN_FILENO);
串口的讀寫與(yu)普通文件一樣,使用read,write函數。
read(fd,buff,8);
write(fd,buff,8);
3,linux串口編程需(xu)要的頭(tou)文件
#include //標準輸入(ru)輸出定義
#include //標準(zhun)函數庫定義
#include //Unix標準函數定義(yi)
#include
#include
#include //文件控制定義
#include //POSIX中(zhong)斷控制定(ding)義
#include //錯(cuo)誤號定義
4,設(she)置數據位、停(ting)止位和校(xiao)驗位
以下是幾(ji)個(ge)數據位(wei)、停(ting)止位(wei)和校驗位(wei)的(de)設置方法(fa):(以下均(jun)為1位(wei)停(ting)止位(wei))
8位數(shu)據位、無(wu)校驗位:
Opt.c_cflag &= ~PARENB;
Opt.c_cflag &= ~CSTOPB;
Opt.c_cflag &= ~CSIZE;
Opt.c_cflag |= CS8;
7位數據(ju)位、奇(qi)校驗:
Opt.c_cflag |= PARENB;
Opt.c_cflag |= PARODD;
Opt.c_cflag &= ~CSTOPB;
Opt.c_cflag &= ~CSIZE;
Opt.c_cflag |= CS7;
7位數(shu)據位、偶校(xiao)驗:
Opt.c_cflag |= PARENB;
Opt.c_cflag &= ~PARODD;
Opt.c_cflag &= ~CSTOPB;
Opt.c_cflag &= ~CSIZE;
Opt.c_cflag |= CS7;
7位數據位、Space校驗(yan):
Opt.c_cflag &= ~PARENB;
Opt.c_cflag &= ~CSTOPB;
Opt.c_cflag &= ~CSIZE;
Opt.c_cflag |= CS7;
5,某(mou)些設置項(xiang)
在上(shang)面我們看到一些比較特殊的設(she)置,下面簡述一下他們的作用。
c_cc數組的VSTART和VSTOP元素被設定成DC1和DC3,代表ASCII標準的XON和XOFF字(zi)符(fu),如果在(zai)傳輸這兩個字(zi)符(fu)的時(shi)候就(jiu)傳不過去,需要把軟件流控(kong)制屏蔽,即:
Opt.c_iflag &= ~ (IXON | IXOFF | IXANY);
有時(shi)候,在(zai)(zai)用write發送數據時(shi)沒有鍵(jian)入(ru)回車,信息就(jiu)發送不出(chu)去,這主要(yao)是(shi)因為我們在(zai)(zai)輸入(ru)輸出(chu)時(shi)是(shi)按照規范模式接收到回車或換(huan)行才發送,而更多情(qing)況(kuang)下我們是(shi)不必鍵(jian)入(ru)回車或換(huan)行的。此(ci)時(shi)應轉換(huan)到行方式輸入(ru),不經處理直接發送,設置(zhi)如下:
Opt.c_lflag &= ~ (ICANON | ECHO | ECHOE | ISIG);
還存在這樣的情況:發送字(zi)(zi)符0X0d的時(shi)候,往往接收(shou)端得(de)到的字(zi)(zi)符是0X0a,原因是因為(wei)在串口設置中c_iflag和(he)c_oflag中存在從NL-CR和(he)CR-NL的映射,即串口能(neng)把回車和(he)換行(xing)當成同一個字(zi)(zi)符,可以進行(xing)如下設置屏蔽之(zhi):
Opt.c_iflag &= ~ (INLCR | ICRNL | IGNCR);
Opt.c_oflag &= ~(ONLCR | OCRNL);
注(zhu)意:控制符VTIME和(he)VMIN之間有復雜的關系(xi)。VTIME定義(yi)要(yao)求等(deng)待的時間(百(bai)毫米,通(tong)常是unsigned char變量),而VMIN定義(yi)了(le)要(yao)求等(deng)待的最(zui)小字節(jie)數(shu)(相(xiang)比之下,read函數(shu)的第三個參數(shu)指定了(le)要(yao)求讀的最(zui)大字節(jie)數(shu))。
如果VTIME=0,VMIN=要求等待(dai)讀取(qu)的最小字節數,read必須在讀取(qu)了VMIN個字節的數據或者收(shou)到一個信號才會返回。
如果VTIME=時間(jian)量,VMIN=0,不管能否讀取(qu)到數據,read也要(yao)等待VTIME的時間(jian)量。
如果VTIME=時間量,VMIN=要求等(deng)待(dai)讀(du)取(qu)的(de)最小字節數(shu),那么將從(cong)read讀(du)取(qu)第一個(ge)字節的(de)數(shu)據時開始計時,并(bing)會在讀(du)取(qu)到(dao)VMIN個(ge)字節或者VTIME時間后返回。
如果VTIME=0,VMIN=0,不管能否讀取到數據,read都(dou)會(hui)立即返(fan)回
讀寫串口
發送數(shu)(shu)據方(fang)式如下(xia),write函數(shu)(shu)將返回寫的位數(shu)(shu)或者當錯誤時為-1。
char buffer[1024];
int length;
int nByte;
nByte = write(fd, buffer, length);
讀取(qu)數據方(fang)式如(ru)下(xia),原始(shi)數據模式下(xia)每個(ge)read函數將返回(hui)實際串(chuan)口(kou)收(shou)到(dao)的字符(fu)數,如(ru)果串(chuan)口(kou)中沒有(you)字符(fu)可(ke)用,回(hui)叫將會阻(zu)塞直到(dao)以下(xia)幾種情況:有(you)字符(fu)進入;一個(ge)間隔計時器失效;錯誤發(fa)送。
在(zai)(zai)打開串口(kou)成(cheng)功后,使用fcntl(fd, F_SETFL, FNDELAY)語句,可以使read函數(shu)立(li)即(ji)返回而不阻塞。FNDELAY選項(xiang)使read函數(shu)在(zai)(zai)串口(kou)無(wu)字(zi)符時(shi)立(li)即(ji)返回且為0。
char buffer[1024];
int length;
int readByte;
readByte = read(fd, buffer, len);
注 意:設置(zhi)為(wei)原始模式傳輸(shu)數(shu)(shu)據(ju)的話(hua),read函(han)數(shu)(shu)返回的字符(fu)數(shu)(shu)是實(shi)際串口收到的字符(fu)數(shu)(shu)。Linux下直接用(yong)(yong)read讀(du)串口可(ke)能(neng)會造成堵塞,或(huo)者(zhe)(zhe)數(shu)(shu)據(ju)讀(du)出錯 誤,此時可(ke)使(shi)用(yong)(yong)tcntl或(huo)者(zhe)(zhe)select等函(han)數(shu)(shu)實(shi)現(xian)異步(bu)讀(du)取。用(yong)(yong)select先(xian)查詢(xun)com口,再(zai)用(yong)(yong)read去讀(du)就可(ke)以避(bi)免上述錯誤。
6,關閉(bi)串口(kou)
串口(kou)作為文件來處理,所以一般的關(guan)閉(bi)文件函數即(ji)可:
close(fd);

