文件I/O編程之: 嵌入式Linux串口應(yīng)用編程
常見的數(shù)據(jù)通信的基本方式可分為并行通信與串行通信兩種。
n 并行通信是指利用多條數(shù)據(jù)傳輸線將一個字數(shù)據(jù)的各比特位同時傳送。它的特點是傳輸速度快,適用于傳輸距離短且傳輸速度較高的通信。
n 串行通信是指利用一條傳輸線將數(shù)據(jù)以比特位為單位順序傳送。特點是通信線路簡單,利用簡單的線纜就可實現(xiàn)通信,降低成本,適用于傳輸距離長且傳輸速度較慢的通信。
串口是計算機一種常用的接口,常用的串口有RS-232-C接口。它是于1970年由美國電子工業(yè)協(xié)會(EIA)聯(lián)合貝爾系統(tǒng)、調(diào)制解調(diào)器廠家及計算機終端生產(chǎn)廠家共同制定的用于串行通信的標準,它的全稱是“數(shù)據(jù)終端設(shè)備(DTE)和數(shù)據(jù)通信設(shè)備(DCE)之間串行二進制數(shù)據(jù)交換接口技術(shù)標準”。該標準規(guī)定采用一個DB25芯引腳的連接器或9芯引腳的連接器,其中25芯引腳的連接器如圖6.3所示。
圖6.325引腳串行接口圖
S3C2410X內(nèi)部具有兩個獨立的UART控制器,每個控制器都可以工作在Interrupt(中斷)模式或者DMA(直接存儲訪問)模式。同時,每個UART均具有16字節(jié)的FIFO(先入先出寄存器),支持的最高波特率可達到230.4Kbps。UART的操作主要可分為以下幾個部分:數(shù)據(jù)發(fā)送、數(shù)據(jù)接收、產(chǎn)生中斷、設(shè)置波特率、Loopback模式、紅外模式以及硬軟流控模式。
串口參數(shù)的配置讀者在配置超級終端和minicom時也已經(jīng)接觸過,一般包括波特率、起始位比特數(shù)、數(shù)據(jù)位比特數(shù)、停止位比特數(shù)和流控模式。在此,可以將其配置為波特率115200、起始位1b、數(shù)據(jù)位8b、停止位1b和無流控模式。
在Linux中,所有的設(shè)備文件一般都位于“/dev”下,其中串口1和串口2對應(yīng)的設(shè)備名依次為“/dev/ttyS0”和“/dev/ttyS1”,而且USB轉(zhuǎn)串口的設(shè)備名通常為“/dev/ttyUSB0”和“/dev/ttyUSB1”(因版本不同該設(shè)備名會有所不同),可以查看在“/dev”下的文件以確認。在本章中已經(jīng)提到過,在Linux下對設(shè)備的操作方法與對文件的操作方法是一樣的,因此,對串口的讀寫就可以使用簡單的read()、write()函數(shù)來完成,所不同的只是需要對串口的其他參數(shù)另做配置,下面就來詳細講解串口應(yīng)用開發(fā)的步驟。
6.4.2串口設(shè)置詳解串口的設(shè)置主要是設(shè)置structtermios結(jié)構(gòu)體的各成員值,如下所示:
#include<termios.h>
structtermios
{
unsignedshortc_iflag;/*輸入模式標志*/
unsignedshortc_oflag;/*輸出模式標志*/
unsignedshortc_cflag;/*控制模式標志*/
unsignedshortc_lflag;/*本地模式標志*/
unsignedcharc_line;/*線路規(guī)程*/
unsignedcharc_cc[NCC];/*控制特性*/
speed_tc_ispeed;/*輸入速度*/
speed_tc_ospeed;/*輸出速度*/
};
termios是在POSIX規(guī)范中定義的標準接口,表示終端設(shè)備(包括虛擬終端、串口等)。口是一種終端設(shè)備,一般通過終端編程接口對其進行配置和控制。在具體講解串口相關(guān)編程之前,先了解一下終端相關(guān)知識。
終端有3種工作模式,分別為規(guī)范模式(canonicalmode)、非規(guī)范模式(non-canonicalmode)和原始模式(rawmode)。
通過在termios結(jié)構(gòu)的c_lflag中設(shè)置ICANNON標志來定義終端是以規(guī)范模式(設(shè)置ICANNON標志)還是以非規(guī)范模式(清除ICANNON標志)工作,默認情況為規(guī)范模式。
在規(guī)范模式下,所有的輸入是基于行進行處理。在用戶輸入一個行結(jié)束符(回車符、EOF等)之前,系統(tǒng)調(diào)用read()函數(shù)讀不到用戶輸入的任何字符。除了EOF之外的行結(jié)束符(回車符等)與普通字符一樣會被read()函數(shù)讀取到緩沖區(qū)之中。在規(guī)范模式中,行編輯是可行的,而且一次調(diào)用read()函數(shù)最多只能讀取一行數(shù)據(jù)。如果在read()函數(shù)中被請求讀取的數(shù)據(jù)字節(jié)數(shù)小于當前行可讀取的字節(jié)數(shù),則read()函數(shù)只會讀取被請求的字節(jié)數(shù),剩下的字節(jié)下次再被讀取。
在非規(guī)范模式下,所有的輸入是即時有效的,不需要用戶另外輸入行結(jié)束符,而且不可進行行編輯。在非規(guī)范模式下,對參數(shù)MIN(c_cc[VMIN])和TIME(c_cc[VTIME])的設(shè)置決定read()函數(shù)的調(diào)用方式。設(shè)置可以有4種不同的情況。
n MIN=0和TIME=0:read()函數(shù)立即返回。若有可讀數(shù)據(jù),則讀取數(shù)據(jù)并返回被讀取的字節(jié)數(shù),否則讀取失敗并返回0。
n MIN>0和TIME=0:read()函數(shù)會被阻塞直到MIN個字節(jié)數(shù)據(jù)可被讀取。
n MIN=0和TIME>0:只要有數(shù)據(jù)可讀或者經(jīng)過TIME個十分之一秒的時間,read()函數(shù)則立即返回,返回值為被讀取的字節(jié)數(shù)。如果超時并且未讀到數(shù)據(jù),則read()函數(shù)返回0。
n MIN>0和TIME>0:當有MIN個字節(jié)可讀或者兩個輸入字符之間的時間間隔超過TIME個十分之一秒時,read()函數(shù)才返回。因為在輸入第一個字符之后系統(tǒng)才會啟動定時器,所以在這種情況下,read()函數(shù)至少讀取一個字節(jié)之后才返回。
按照嚴格意義來講,原始模式是一種特殊的非規(guī)范模式。在原始模式下,所有的輸入數(shù)據(jù)以字節(jié)為單位被處理。在這個模式下,終端是不可回顯的,而且所有特定的終端輸入/輸出控制處理不可用。通過調(diào)用cfmakeraw()函數(shù)可以將終端設(shè)置為原始模式,而且該函數(shù)通過以下代碼可以得到實現(xiàn)。
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;
下面講解設(shè)置串口的基本方法。設(shè)置串口中最基本的包括波特率設(shè)置,校驗位和停止位設(shè)置。在這個結(jié)構(gòu)中最為重要的是c_cflag,通過對它的賦值,用戶可以設(shè)置波特率、字符大小、數(shù)據(jù)位、停止位、奇偶校驗位和硬軟流控等。另外c_iflag和c_cc也是比較常用的標志。在此主要對這3個成員進行詳細說明。c_cflag支持的常量名稱如表6.11所示。其中設(shè)置波特率宏名為相應(yīng)的波特率數(shù)值前加上‘B’,由于數(shù)值較多,本表沒有全部列出。
表6.11 c_cflag支持的常量名稱
CBAUD
波特率的位掩碼
B0
0波特率(放棄DTR)
…
…
B1800
1800波特率
B2400
2400波特率
續(xù)表
B4800
4800波特率
B9600
9600波特率
B19200
19200波特率
B38400
38400波特率
B57600
57600波特率
B115200
115200波特率
EXTA
外部時鐘率
EXTB
外部時鐘率
CSIZE
數(shù)據(jù)位的位掩碼
CS5
5個數(shù)據(jù)位
CS6
6個數(shù)據(jù)位
CS7
7個數(shù)據(jù)位
CS8
8個數(shù)據(jù)位
CSTOPB
2個停止位(不設(shè)則是1個停止位)
CREAD
接收使能
PARENB
PARODD
校驗位使能
使用奇校驗而不使用偶校驗
HUPCL
最后關(guān)閉時掛線(放棄DTR)
CLOCAL
本地連接(不改變端口所有者)
CRTSCTS
硬件流控
在這里,不能直接對c_cflag成員初始化,而要將其通過“與”、“或”操作使用其中的某些選項。輸入模式標志c_iflag用于控制端口接收端的字符輸入處理。c_iflag支持的常量名稱如表6.12所示。
表6.12 c_iflag支持的常量名稱
INPCK
奇偶校驗使能
IGNPAR
忽略奇偶校驗錯誤
PARMRK
奇偶校驗錯誤掩碼
ISTRIP
裁減掉第8位比特
IXON
啟動輸出軟件流控
IXOFF
啟動輸入軟件流控
IXANY
輸入任意字符可以重新啟動輸出(默認為輸入起始字符才重啟輸出)
IGNBRK
忽略輸入終止條件
BRKINT
當檢測到輸入終止條件時發(fā)送SIGINT信號
INLCR
將接收到的NL(換行符)轉(zhuǎn)換為CR(回車符)
IGNCR
忽略接收到的CR(回車符)
ICRNL
將接收到的CR(回車符)轉(zhuǎn)換為NL(換行符)
IUCLC
將接收到的大寫字符映射為小寫字符
IMAXBEL
當輸入隊列滿時響鈴
c_oflag用于控制終端端口發(fā)送出去的字符處理,c_oflag支持的常量名稱如表6.12所示。因為現(xiàn)在終端的速度比以前快得多,所以大部分延時掩碼幾乎沒什么用途。
表6.13 c_oflag支持的常量名稱
OPOST
啟用輸出處理功能,如果不設(shè)置該標志,則其他標志都被忽略
OLCUC
將輸出中的大寫字符轉(zhuǎn)換成小寫字符
ONLCR
將輸出中的換行符(‘\n’)轉(zhuǎn)換成回車符(‘\r’)
ONOCR
如果當前列號為0,則不輸出回車符
OCRNL
將輸出中的回車符(‘\r’)轉(zhuǎn)換成換行符(‘\n’)
ONLRET
不輸出回車符
OFILL
發(fā)送填充字符以提供延時
OFDEL
如果設(shè)置該標志,則表示填充字符為DEL字符,否則為NUL字符
NLDLY
換行延時掩碼
CRDLY
回車延時掩碼
TABDLY
制表符延時掩碼
BSDLY
水平退格符延時掩碼
VTDLY
垂直退格符延時掩碼
FFLDY
換頁符延時掩碼
c_lflag用于控制控制終端的本地數(shù)據(jù)處理和工作模式,c_lflag所支持的常量名稱如表6.14所示。
表6.14 c_lflag支持的常量名稱
ISIG
若收到信號字符(INTR、QUIT等),則會產(chǎn)生相應(yīng)的信號
ICANON
啟用規(guī)范模式
ECHO
啟用本地回顯功能
ECHOE
若設(shè)置ICANON,則允許退格操作
ECHOK
若設(shè)置ICANON,則KILL字符會刪除當前行
ECHONL
若設(shè)置ICANON,則允許回顯換行符
ECHOCTL
若設(shè)置ECHO,則控制字符(制表符、換行符等)會顯示成“^X”,其中X的ASCII碼等于給相應(yīng)控制字符的ASCII碼加上0x40。例如:退格字符(0x08)會顯示為“^H”(’H’的ASCII碼為0x48)
ECHOPRT
若設(shè)置ICANON和IECHO,則刪除字符(退格符等)和被刪除的字符都會被顯示
ECHOKE
若設(shè)置ICANON,則允許回顯在ECHOE和ECHOPRT中設(shè)定的KILL字符
NOFLSH
在通常情況下,當接收到INTR、QUIT和SUSP控制字符時,會清空輸入和輸出隊列。如果設(shè)置該標志,則所有的隊列不會被清空
TOSTOP
若一個后臺進程試圖向它的控制終端進行寫操作,則系統(tǒng)向該后臺進程的進程組發(fā)送SIGTTOU信號。該信號通常終止進程的執(zhí)行
IEXTEN
啟用輸入處理功能
c_cc定義特殊控制特性。c_cc所支持的常量名稱如表6.13所示。
表6.13 c_cc支持的常量名稱
VINTR
中斷控制字符,對應(yīng)鍵為CTRL+C
VQUIT
退出操作符,對應(yīng)鍵為CRTL+Z
VERASE
刪除操作符,對應(yīng)鍵為Backspace(BS)
VKILL
刪除行符,對應(yīng)鍵為CTRL+U
VEOF
文件結(jié)尾符,對應(yīng)鍵為CTRL+D
VEOL
附加行結(jié)尾符,對應(yīng)鍵為Carriagereturn(CR)
VEOL2
第二行結(jié)尾符,對應(yīng)鍵為Linefeed(LF)
VMIN
指定最少讀取的字符數(shù)
VTIME
指定讀取的每個字符之間的超時時間
下面就詳細講解設(shè)置串口屬性的基本流程。
1.保存原先串口配置首先,為了安全起見和以后調(diào)試程序方便,可以先保存原先串口的配置,在這里可以使用函數(shù)tcgetattr(fd,&old_cfg)。該函數(shù)得到fd指向的終端的配置參數(shù),并將它們保存于termios結(jié)構(gòu)變量old_cfg中。該函數(shù)還可以測試配置是否正確、該串口是否可用等。若調(diào)用成功,函數(shù)返回值為0,若調(diào)用失敗,函數(shù)返回值為-1,其使用如下所示:
if(tcgetattr(fd,&old_cfg)!=0)
{
perror("tcgetattr");
return-1;
}
2.激活選項CLOCAL和CREAD分別用于本地連接和接受使能,因此,首先要通過位掩碼的方式激活這兩個選項。
newtio.c_cflag|=CLOCAL|CREAD;
調(diào)用cfmakeraw()函數(shù)可以將終端設(shè)置為原始模式,在后面的實例中,采用原始模式進行串口數(shù)據(jù)通信。
cfmakeraw(&new_cfg);
3.設(shè)置波特率設(shè)置波特率有專門的函數(shù),用戶不能直接通過位掩碼來操作。設(shè)置波特率的主要函數(shù)有:cfsetispeed()和cfsetospeed()。這兩個函數(shù)的使用很簡單,如下所示:
cfsetispeed(&new_cfg,B115200);
cfsetospeed(&new_cfg,B115200);
一般地,用戶需將終端的輸入和輸出波特率設(shè)置成一樣的。這幾個函數(shù)在成功時返回0,失敗時返回-1。
4.設(shè)置字符大小與設(shè)置波特率不同,設(shè)置字符大小并沒有現(xiàn)成可用的函數(shù),需要用位掩碼。一般首先去除數(shù)據(jù)位中的位掩碼,再重新按要求設(shè)置。如下所示:
new_cfg.c_cflag&=~CSIZE;/*用數(shù)據(jù)位掩碼清空數(shù)據(jù)位設(shè)置*/
new_cfg.c_cflag|=CS8;
5.設(shè)置奇偶校驗位設(shè)置奇偶校驗位需要用到termios中的兩個成員:c_cflag和c_iflag。首先要激活c_cflag中的校驗位使能標志PARENB和是否要進行偶校驗,同時還要激活c_iflag中的對于輸入數(shù)據(jù)的奇偶校驗使能(INPCK)。如使能奇校驗時,代碼如下所示:
new_cfg.c_cflag|=(PARODD|PARENB);
new_cfg.c_iflag|=INPCK;
而使能偶校驗時,代碼如下所示:
new_cfg.c_cflag|=PARENB;
new_cfg.c_cflag&=~PARODD;/*清除偶校驗標志,則配置為奇校驗*/
new_cfg.c_iflag|=INPCK;
6.設(shè)置停止位設(shè)置停止位是通過激活c_cflag中的CSTOPB而實現(xiàn)的。若停止位為一個,則清除CSTOPB,若停止位為兩個,則激活CSTOPB。以下分別是停止位為一個和兩個比特時的代碼:
new_cfg.c_cflag&=~CSTOPB;/*將停止位設(shè)置為一個比特*/
new_cfg.c_cflag|=CSTOPB;/*將停止位設(shè)置為兩個比特*/
7.設(shè)置最少字符和等待時間在對接收字符和等待時間沒有特別要求的情況下,可以將其設(shè)置為0,則在任何情況下read()函數(shù)立即返回,如下所示:
new_cfg.c_cc[VTIME]=0;
new_cfg.c_cc[VMIN]=0;
8.清除串口緩沖由于串口在重新設(shè)置之后,需要對當前的串口設(shè)備進行適當?shù)奶幚恚@時就可調(diào)用在<termios.h>中聲明的tcdrain()、tcflow()、tcflush()等函數(shù)來處理目前串口緩沖中的數(shù)據(jù),它們的格式如下所示。
inttcdrain(intfd);/*使程序阻塞,直到輸出緩沖區(qū)的數(shù)據(jù)全部發(fā)送完畢*/
inttcflow(intfd,intaction);/*用于暫停或重新開始輸出*/
inttcflush(intfd,intqueue_selector);/*用于清空輸入/輸出緩沖區(qū)*/
在本實例中使用tcflush()函數(shù),對于在緩沖區(qū)中的尚未傳輸?shù)臄?shù)據(jù),或者收到的但是尚未讀取的數(shù)據(jù),其處理方法取決于queue_selector的值,它可能的取值有以下幾種。
n TCIFLUSH:對接收到而未被讀取的數(shù)據(jù)進行清空處理。
n TCOFLUSH:對尚未傳送成功的輸出數(shù)據(jù)進行清空處理。
n TCIOFLUSH:包括前兩種功能,即對尚未處理的輸入輸出數(shù)據(jù)進行清空處理。
如在本例中所采用的是第一種方法:
tcflush(fd,TCIFLUSH);
9.激活配置在完成全部串口配置之后,要激活剛才的配置并使配置生效。這里用到的函數(shù)是tcsetattr(),它的函數(shù)原型是:
tcsetattr(intfd,intoptional_actions,conststructtermios*termios_p);
其中參數(shù)termios_p是termios類型的新配置變量。
參數(shù)optional_actions可能的取值有以下3種:
n TCSANOW:配置的修改立即生效。
n TCSADRAIN:配置的修改在所有寫入fd的輸出都傳輸完畢之后生效。
n TCSAFLUSH:所有已接受但未讀入的輸入都將在修改生效之前被丟棄。
該函數(shù)若調(diào)用成功則返回0,若失敗則返回-1,代碼如下所示:
if((tcsetattr(fd,TCSANOW,&new_cfg))!=0)
{
perror("tcsetattr");
return-1;
}
下面給出了串口配置的完整函數(shù)。通常,為了函數(shù)的通用性,通常將常用的選項都在函數(shù)中列出,這樣可以大大方便以后用戶的調(diào)試使用。該設(shè)置函數(shù)如下所示:
intset_com_config(intfd,intbaud_rate,
intdata_bits,charparity,intstop_bits)
{
structtermiosnew_cfg,old_cfg;
intspeed;
/*保存并測試現(xiàn)有串口參數(shù)設(shè)置,在這里如果串口號等出錯,會有相關(guān)的出錯信息*/
if(tcgetattr(fd,&old_cfg)!=0)
{
perror("tcgetattr");
return-1;
}
/*設(shè)置字符大小*/
new_cfg=old_cfg;
cfmakeraw(&new_cfg);/*配置為原始模式*/
new_cfg.c_cflag&=~CSIZE;
/*設(shè)置波特率*/
switch(baud_rate)
{
case2400:
{
speed=B2400;
}
break;
case4800:
{
speed=B4800;
}
break;
case9600:
{
speed=B9600;
}
break;
case19200:
{
speed=B19200;
}
break;
case38400:
{
speed=B38400;
}
break;
default:
case115200:
{
speed=B115200;
}
break;
}
cfsetispeed(&new_cfg,speed);
cfsetospeed(&new_cfg,speed);
/*設(shè)置停止位*/
switch(data_bits)
{
case7:
{
new_cfg.c_cflag|=CS7;
}
break;
default:
case8:
{
new_cfg.c_cflag|=CS8;
}
break;
}
/*設(shè)置奇偶校驗位*/
switch(parity)
{
default:
case'n':
case'N':
{
new_cfg.c_cflag&=~PARENB;
new_cfg.c_iflag&=~INPCK;
}
break;
case'o':
case'O':
{
new_cfg.c_cflag|=(PARODD|PARENB);
new_cfg.c_iflag|=INPCK;
}
break;
case'e':
case'E':
{
new_cfg.c_cflag|=PARENB;
new_cfg.c_cflag&=~PARODD;
new_cfg.c_iflag|=INPCK;
}
break;
case's':/*asnoparity*/
case'S':
{
new_cfg.c_cflag&=~PARENB;
new_cfg.c_cflag&=~CSTOPB;
}
break;
}
/*設(shè)置停止位*/
switch(stop_bits)
{
default:
case1:
{
new_cfg.c_cflag&=~CSTOPB;
}
break;
case2:
{
new_cfg.c_cflag|=CSTOPB;
}
}
/*設(shè)置等待時間和最小接收字符*/
new_cfg.c_cc[VTIME]=0;
new_cfg.c_cc[VMIN]=1;
/*處理未接收字符*/
tcflush(fd,TCIFLUSH);
/*激活新配置*/
if((tcsetattr(fd,TCSANOW,&new_cfg))!=0)
{
perror("tcsetattr");
return-1;
}
return0;
}
6.4.3串口使用詳解在配置完串口的相關(guān)屬性后,就可以對串口進行打開和讀寫操作了。它所使用的函數(shù)和普通文件的讀寫函數(shù)一樣,都是open()、write()和read()。它們之間的區(qū)別的只是串口是一個終端設(shè)備,因此在選擇函數(shù)的具體參數(shù)時會有一些區(qū)別。另外,這里會用到一些附加的函數(shù),用于測試終端設(shè)備的連接情況等。下面將對其進行具體講解。
1.打開串口打開串口和打開普通文件一樣,都是使用open()函數(shù),如下所示:
fd=open("/dev/ttyS0",O_RDWR|O_NOCTTY|O_NDELAY);
可以看到,這里除了普通的讀寫參數(shù)外,還有兩個參數(shù)O_NOCTTY和O_NDELAY。
n O_NOCTTY標志用于通知Linux系統(tǒng),該參數(shù)不會使打開的文件成為這個進程的控制終端。如果沒有指定這個標志,那么任何一個輸入(諸如鍵盤中止信號等)都將會影響用戶的進程。
n O_NDELAY標志通知Linux系統(tǒng),這個程序不關(guān)心DCD信號線所處的狀態(tài)(端口的另一端是否激活或者停止)。如果用戶指定了這個標志,則進程將會一直處在睡眠狀態(tài),直到DCD信號線被激活。
接下來可恢復(fù)串口的狀態(tài)為阻塞狀態(tài),用于等待串口數(shù)據(jù)的讀入,可用fcntl()函數(shù)實現(xiàn),如下所示:
fcntl(fd,F_SETFL,0);
再接著可以測試打開文件描述符是否連接到一個終端設(shè)備,以進一步確認串口是否正確打開,如下所示:
isatty(STDIN_FILENO);
該函數(shù)調(diào)用成功則返回0,若失敗則返回-1。
這時,一個串口就已經(jīng)成功打開了。接下來就可以對這個串口進行讀和寫操作。下面給出了一個完整的打開串口的函數(shù),同樣考慮到了各種不同的情況。程序如下所示:
/*打開串口函數(shù)*/
intopen_port(intcom_port)
{
intfd;
#if(COM_TYPE==GNR_COM)/*使用普通串口*/
char*dev[]={"/dev/ttyS0","/dev/ttyS1","/dev/ttyS2"};
#else/*使用USB轉(zhuǎn)串口*/
char*dev[]={"/dev/ttyUSB0","/dev/ttyUSB1","/dev/ttyUSB2"};
#endif
if((com_port<0)||(com_port>MAX_COM_NUM))
{
return-1;
}
/*打開串口*/
fd=open(dev[com_port-1],O_RDWR|O_NOCTTY|O_NDELAY);
if(fd<0)
{
perror("openserialport");
return(-1);
}
/*恢復(fù)串口為阻塞狀態(tài)*/
if(fcntl(fd,F_SETFL,0)<0)
{
perror("fcntlF_SETFL\n");
}
/*測試是否為終端設(shè)備*/
if(isatty(STDIN_FILENO)==0)
{
perror("standardinputisnotaterminaldevice");
}
returnfd;
}
2.讀寫串口讀寫串口操作和讀寫普通文件一樣,使用read()和write()函數(shù)即可,如下所示:
write(fd,buff,strlen(buff));
read(fd,buff,BUFFER_SIZE);
下面兩個實例給出了串口讀和寫的兩個程序,其中用到前面所講述的open_port()和set_com_config()函數(shù)。寫串口的程序?qū)⒃谒拗鳈C上運行,讀串口的程序?qū)⒃谀繕税迳线\行。
寫串口的程序如下所示。
/*com_writer.c*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<errno.h>
#include"uart_api.h"
intmain(void)
{
intfd;
charbuff[BUFFER_SIZE];
if((fd=open_port(HOST_COM_PORT))<0)/*打開串口*/
{
perror("open_port");
return1;
}
if(set_com_config(fd,115200,8,'N',1)<0)/*配置串口*/
{
perror("set_com_config");
return1;
}
do
{
printf("Inputsomewords(enter'quit'toexit):");
memset(buff,0,BUFFER_SIZE);
if(fgets(buff,BUFFER_SIZE,stdin)==NULL)
{
perror("fgets");
break;
}
write(fd,buff,strlen(buff));
}while(strncmp(buff,"quit",4));
close(fd);
return0;
}
讀串口的程序如下所示:
/*com_reader.c*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<errno.h>
#include"uart_api.h"
intmain(void)
{
intfd;
charbuff[BUFFER_SIZE];
if((fd=open_port(TARGET_COM_PORT))<0)/*打開串口*/
{
perror("open_port");
return1;
}
if(set_com_config(fd,115200,8,'N',1)<0)/*配置串口*/
{
perror("set_com_config");
return1;
}
do
{
memset(buff,0,BUFFER_SIZE);
if(read(fd,buff,BUFFER_SIZE)>0)
{
printf("Thereceivedwordsare:%s",buff);
}
}while(strncmp(buff,"quit",4));
close(fd);
return0;
}
在宿主機上運行寫串口的程序,而在目標板上運行讀串口的程序,運行結(jié)果如下所示。
/*宿主機,寫串口*/
$./com_writer
Inputsomewords(enter'quit'toexit):hello,Reader!
Inputsomewords(enter'quit'toexit):I'mWriter!
Inputsomewords(enter'quit'toexit):Thisisaserialporttestingprogram.
Inputsomewords(enter'quit'toexit):quit
/*目標板,讀串口*/
$./com_reader
Thereceivedwordsare:hello,Reader!
Thereceivedwordsare:I'mWriter!
Thereceivedwordsare:Thisisaserialporttestingprogram.
Thereceivedwordsare:quit
另外,讀者還可以考慮一下如何使用select()函數(shù)實現(xiàn)串口的非阻塞讀寫,具體實例會在本章的后面的實驗中給出。