當(dāng)前位置:首頁 > 嵌入式 > 嵌入式教程
[導(dǎo)讀]串口是計(jì)算機(jī)一種常用的接口,常用的串口有RS-232-C接口。它是于1970年由美國電子工業(yè)協(xié)會(EIA)聯(lián)合貝爾系統(tǒng)、調(diào)制解調(diào)器廠家及計(jì)算機(jī)終端生產(chǎn)廠家共同制定的用于串行通信的標(biāo)準(zhǔn),它的全稱是“數(shù)據(jù)終端設(shè)備(DTE)和數(shù)據(jù)通信設(shè)備(DCE)之間串行二進(jìn)制數(shù)據(jù)交換接口技術(shù)標(biāo)準(zhǔn)”。

6.4嵌入式Linux串口應(yīng)用編程6.4.1串口概述

常見的數(shù)據(jù)通信的基本方式可分為并行通信與串行通信兩種。

n 并行通信是指利用多條數(shù)據(jù)傳輸線將一個(gè)字?jǐn)?shù)據(jù)的各比特位同時(shí)傳送。它的特點(diǎn)是傳輸速度快,適用于傳輸距離短且傳輸速度較高的通信。

n 串行通信是指利用一條傳輸線將數(shù)據(jù)以比特位為單位順序傳送。特點(diǎn)是通信線路簡單,利用簡單的線纜就可實(shí)現(xiàn)通信,降低成本,適用于傳輸距離長且傳輸速度較慢的通信。

串口是計(jì)算機(jī)一種常用的接口,常用的串口有RS-232-C接口。它是于1970年由美國電子工業(yè)協(xié)會(EIA)聯(lián)合貝爾系統(tǒng)、調(diào)制解調(diào)器廠家及計(jì)算機(jī)終端生產(chǎn)廠家共同制定的用于串行通信的標(biāo)準(zhǔn),它的全稱是“數(shù)據(jù)終端設(shè)備(DTE)和數(shù)據(jù)通信設(shè)備(DCE)之間串行二進(jìn)制數(shù)據(jù)交換接口技術(shù)標(biāo)準(zhǔn)”。該標(biāo)準(zhǔn)規(guī)定采用一個(gè)DB25芯引腳的連接器或9芯引腳的連接器,其中25芯引腳的連接器如圖6.3所示。

圖6.325引腳串行接口圖

S3C2410X內(nèi)部具有兩個(gè)獨(dú)立的UART控制器,每個(gè)控制器都可以工作在Interrupt(中斷)模式或者DMA(直接存儲訪問)模式。同時(shí),每個(gè)UART均具有16字節(jié)的FIFO(先入先出寄存器),支持的最高波特率可達(dá)到230.4Kbps。UART的操作主要可分為以下幾個(gè)部分:數(shù)據(jù)發(fā)送、數(shù)據(jù)接收、產(chǎn)生中斷、設(shè)置波特率、Loopback模式、紅外模式以及硬軟流控模式。

串口參數(shù)的配置讀者在配置超級終端和minicom時(shí)也已經(jīng)接觸過,一般包括波特率、起始位比特?cái)?shù)、數(shù)據(jù)位比特?cái)?shù)、停止位比特?cái)?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”下的文件以確認(rèn)。在本章中已經(jīng)提到過,在Linux下對設(shè)備的操作方法與對文件的操作方法是一樣的,因此,對串口的讀寫就可以使用簡單的read()、write()函數(shù)來完成,所不同的只是需要對串口的其他參數(shù)另做配置,下面就來詳細(xì)講解串口應(yīng)用開發(fā)的步驟。

6.4.2串口設(shè)置詳解

串口的設(shè)置主要是設(shè)置structtermios結(jié)構(gòu)體的各成員值,如下所示:

#include<termios.h>

structtermios

{

unsignedshortc_iflag;/*輸入模式標(biāo)志*/

unsignedshortc_oflag;/*輸出模式標(biāo)志*/

unsignedshortc_cflag;/*控制模式標(biāo)志*/

unsignedshortc_lflag;/*本地模式標(biāo)志*/

unsignedcharc_line;/*線路規(guī)程*/

unsignedcharc_cc[NCC];/*控制特性*/

speed_tc_ispeed;/*輸入速度*/

speed_tc_ospeed;/*輸出速度*/

};

termios是在POSIX規(guī)范中定義的標(biāo)準(zhǔn)接口,表示終端設(shè)備(包括虛擬終端、串口等)??谑且环N終端設(shè)備,一般通過終端編程接口對其進(jìn)行配置和控制。在具體講解串口相關(guān)編程之前,先了解一下終端相關(guān)知識。

終端有3種工作模式,分別為規(guī)范模式(canonicalmode)、非規(guī)范模式(non-canonicalmode)和原始模式(rawmode)。

通過在termios結(jié)構(gòu)的c_lflag中設(shè)置ICANNON標(biāo)志來定義終端是以規(guī)范模式(設(shè)置ICANNON標(biāo)志)還是以非規(guī)范模式(清除ICANNON標(biāo)志)工作,默認(rèn)情況為規(guī)范模式。

在規(guī)范模式下,所有的輸入是基于行進(jìn)行處理。在用戶輸入一個(gè)行結(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ù)小于當(dāng)前行可讀取的字節(jié)數(shù),則read()函數(shù)只會讀取被請求的字節(jié)數(shù),剩下的字節(jié)下次再被讀取。

在非規(guī)范模式下,所有的輸入是即時(shí)有效的,不需要用戶另外輸入行結(jié)束符,而且不可進(jìn)行行編輯。在非規(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個(gè)字節(jié)數(shù)據(jù)可被讀取。

n MIN=0和TIME>0:只要有數(shù)據(jù)可讀或者經(jīng)過TIME個(gè)十分之一秒的時(shí)間,read()函數(shù)則立即返回,返回值為被讀取的字節(jié)數(shù)。如果超時(shí)并且未讀到數(shù)據(jù),則read()函數(shù)返回0。

n MIN>0和TIME>0:當(dāng)有MIN個(gè)字節(jié)可讀或者兩個(gè)輸入字符之間的時(shí)間間隔超過TIME個(gè)十分之一秒時(shí),read()函數(shù)才返回。因?yàn)樵谳斎氲谝粋€(gè)字符之后系統(tǒng)才會啟動定時(shí)器,所以在這種情況下,read()函數(shù)至少讀取一個(gè)字節(jié)之后才返回。

按照嚴(yán)格意義來講,原始模式是一種特殊的非規(guī)范模式。在原始模式下,所有的輸入數(shù)據(jù)以字節(jié)為單位被處理。在這個(gè)模式下,終端是不可回顯的,而且所有特定的終端輸入/輸出控制處理不可用。通過調(diào)用cfmakeraw()函數(shù)可以將終端設(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è)置,校驗(yàn)位和停止位設(shè)置。在這個(gè)結(jié)構(gòu)中最為重要的是c_cflag,通過對它的賦值,用戶可以設(shè)置波特率、字符大小、數(shù)據(jù)位、停止位、奇偶校驗(yàn)位和硬軟流控等。另外c_iflag和c_cc也是比較常用的標(biāo)志。在此主要對這3個(gè)成員進(jìn)行詳細(xì)說明。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

外部時(shí)鐘率

EXTB

外部時(shí)鐘率

CSIZE

數(shù)據(jù)位的位掩碼

CS5

5個(gè)數(shù)據(jù)位

CS6

6個(gè)數(shù)據(jù)位

CS7

7個(gè)數(shù)據(jù)位

CS8

8個(gè)數(shù)據(jù)位

CSTOPB

2個(gè)停止位(不設(shè)則是1個(gè)停止位)

CREAD

接收使能

PARENB

PARODD

校驗(yàn)位使能

使用奇校驗(yàn)而不使用偶校驗(yàn)

HUPCL

最后關(guān)閉時(shí)掛線(放棄DTR)

CLOCAL

本地連接(不改變端口所有者)

CRTSCTS

硬件流控

在這里,不能直接對c_cflag成員初始化,而要將其通過“與”、“或”操作使用其中的某些選項(xiàng)。輸入模式標(biāo)志c_iflag用于控制端口接收端的字符輸入處理。c_iflag支持的常量名稱如表6.12所示。

表6.12 c_iflag支持的常量名稱

INPCK

奇偶校驗(yàn)使能

IGNPAR

忽略奇偶校驗(yàn)錯誤

PARMRK

奇偶校驗(yàn)錯誤掩碼

ISTRIP

裁減掉第8位比特

IXON

啟動輸出軟件流控

IXOFF

啟動輸入軟件流控

IXANY

輸入任意字符可以重新啟動輸出(默認(rèn)為輸入起始字符才重啟輸出)

IGNBRK

忽略輸入終止條件

BRKINT

當(dāng)檢測到輸入終止條件時(shí)發(fā)送SIGINT信號

INLCR

將接收到的NL(換行符)轉(zhuǎn)換為CR(回車符)

IGNCR

忽略接收到的CR(回車符)

ICRNL

將接收到的CR(回車符)轉(zhuǎn)換為NL(換行符)

IUCLC

將接收到的大寫字符映射為小寫字符

IMAXBEL

當(dāng)輸入隊(duì)列滿時(shí)響鈴

c_oflag用于控制終端端口發(fā)送出去的字符處理,c_oflag支持的常量名稱如表6.12所示。因?yàn)楝F(xiàn)在終端的速度比以前快得多,所以大部分延時(shí)掩碼幾乎沒什么用途。

表6.13 c_oflag支持的常量名稱

OPOST

啟用輸出處理功能,如果不設(shè)置該標(biāo)志,則其他標(biāo)志都被忽略

OLCUC

將輸出中的大寫字符轉(zhuǎn)換成小寫字符

ONLCR

將輸出中的換行符(‘\n’)轉(zhuǎn)換成回車符(‘\r’)

ONOCR

如果當(dāng)前列號為0,則不輸出回車符

OCRNL

將輸出中的回車符(‘\r’)轉(zhuǎn)換成換行符(‘\n’)

ONLRET

不輸出回車符

OFILL

發(fā)送填充字符以提供延時(shí)

OFDEL

如果設(shè)置該標(biāo)志,則表示填充字符為DEL字符,否則為NUL字符

NLDLY

換行延時(shí)掩碼

CRDLY

回車延時(shí)掩碼

TABDLY

制表符延時(shí)掩碼

BSDLY

水平退格符延時(shí)掩碼

VTDLY

垂直退格符延時(shí)掩碼

FFLDY

換頁符延時(shí)掩碼

c_lflag用于控制控制終端的本地?cái)?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字符會刪除當(dāng)前行

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

在通常情況下,當(dāng)接收到INTR、QUIT和SUSP控制字符時(shí),會清空輸入和輸出隊(duì)列。如果設(shè)置該標(biāo)志,則所有的隊(duì)列不會被清空

TOSTOP

若一個(gè)后臺進(jìn)程試圖向它的控制終端進(jìn)行寫操作,則系統(tǒng)向該后臺進(jìn)程的進(jìn)程組發(fā)送SIGTTOU信號。該信號通常終止進(jìn)程的執(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

指定讀取的每個(gè)字符之間的超時(shí)時(shí)間

下面就詳細(xì)講解設(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.激活選項(xiàng)

CLOCAL和CREAD分別用于本地連接和接受使能,因此,首先要通過位掩碼的方式激活這兩個(gè)選項(xiàng)。

newtio.c_cflag|=CLOCAL|CREAD;

調(diào)用cfmakeraw()函數(shù)可以將終端設(shè)置為原始模式,在后面的實(shí)例中,采用原始模式進(jìn)行串口數(shù)據(jù)通信。

cfmakeraw(&new_cfg);

3.設(shè)置波特率

設(shè)置波特率有專門的函數(shù),用戶不能直接通過位掩碼來操作。設(shè)置波特率的主要函數(shù)有:cfsetispeed()和cfsetospeed()。這兩個(gè)函數(shù)的使用很簡單,如下所示:

cfsetispeed(&new_cfg,B115200);

cfsetospeed(&new_cfg,B115200);

一般地,用戶需將終端的輸入和輸出波特率設(shè)置成一樣的。這幾個(gè)函數(shù)在成功時(shí)返回0,失敗時(shí)返回-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è)置奇偶校驗(yàn)位

設(shè)置奇偶校驗(yàn)位需要用到termios中的兩個(gè)成員:c_cflag和c_iflag。首先要激活c_cflag中的校驗(yàn)位使能標(biāo)志PARENB和是否要進(jìn)行偶校驗(yàn),同時(shí)還要激活c_iflag中的對于輸入數(shù)據(jù)的奇偶校驗(yàn)使能(INPCK)。如使能奇校驗(yàn)時(shí),代碼如下所示:

new_cfg.c_cflag|=(PARODD|PARENB);

new_cfg.c_iflag|=INPCK;

而使能偶校驗(yàn)時(shí),代碼如下所示:

new_cfg.c_cflag|=PARENB;

new_cfg.c_cflag&=~PARODD;/*清除偶校驗(yàn)標(biāo)志,則配置為奇校驗(yàn)*/

new_cfg.c_iflag|=INPCK;

6.設(shè)置停止位

設(shè)置停止位是通過激活c_cflag中的CSTOPB而實(shí)現(xiàn)的。若停止位為一個(gè),則清除CSTOPB,若停止位為兩個(gè),則激活CSTOPB。以下分別是停止位為一個(gè)和兩個(gè)比特時(shí)的代碼:

new_cfg.c_cflag&=~CSTOPB;/*將停止位設(shè)置為一個(gè)比特*/

new_cfg.c_cflag|=CSTOPB;/*將停止位設(shè)置為兩個(gè)比特*/

7.設(shè)置最少字符和等待時(shí)間

在對接收字符和等待時(shí)間沒有特別要求的情況下,可以將其設(shè)置為0,則在任何情況下read()函數(shù)立即返回,如下所示:

new_cfg.c_cc[VTIME]=0;

new_cfg.c_cc[VMIN]=0;

8.清除串口緩沖

由于串口在重新設(shè)置之后,需要對當(dāng)前的串口設(shè)備進(jìn)行適當(dāng)?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ū)*/

在本實(shí)例中使用tcflush()函數(shù),對于在緩沖區(qū)中的尚未傳輸?shù)臄?shù)據(jù),或者收到的但是尚未讀取的數(shù)據(jù),其處理方法取決于queue_selector的值,它可能的取值有以下幾種。

n TCIFLUSH:對接收到而未被讀取的數(shù)據(jù)進(jìn)行清空處理。

n TCOFLUSH:對尚未傳送成功的輸出數(shù)據(jù)進(jìn)行清空處理。

n TCIOFLUSH:包括前兩種功能,即對尚未處理的輸入輸出數(shù)據(jù)進(jìn)行清空處理。

如在本例中所采用的是第一種方法:

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ù)的通用性,通常將常用的選項(xiàng)都在函數(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è)置奇偶校驗(yàn)位*/

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è)置等待時(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)屬性后,就可以對串口進(jìn)行打開和讀寫操作了。它所使用的函數(shù)和普通文件的讀寫函數(shù)一樣,都是open()、write()和read()。它們之間的區(qū)別的只是串口是一個(gè)終端設(shè)備,因此在選擇函數(shù)的具體參數(shù)時(shí)會有一些區(qū)別。另外,這里會用到一些附加的函數(shù),用于測試終端設(shè)備的連接情況等。下面將對其進(jìn)行具體講解。

1.打開串口

打開串口和打開普通文件一樣,都是使用open()函數(shù),如下所示:

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

可以看到,這里除了普通的讀寫參數(shù)外,還有兩個(gè)參數(shù)O_NOCTTY和O_NDELAY。

n O_NOCTTY標(biāo)志用于通知Linux系統(tǒng),該參數(shù)不會使打開的文件成為這個(gè)進(jìn)程的控制終端。如果沒有指定這個(gè)標(biāo)志,那么任何一個(gè)輸入(諸如鍵盤中止信號等)都將會影響用戶的進(jìn)程。

n O_NDELAY標(biāo)志通知Linux系統(tǒng),這個(gè)程序不關(guān)心DCD信號線所處的狀態(tài)(端口的另一端是否激活或者停止)。如果用戶指定了這個(gè)標(biāo)志,則進(jìn)程將會一直處在睡眠狀態(tài),直到DCD信號線被激活。

接下來可恢復(fù)串口的狀態(tài)為阻塞狀態(tài),用于等待串口數(shù)據(jù)的讀入,可用fcntl()函數(shù)實(shí)現(xiàn),如下所示:

fcntl(fd,F_SETFL,0);

再接著可以測試打開文件描述符是否連接到一個(gè)終端設(shè)備,以進(jìn)一步確認(rèn)串口是否正確打開,如下所示:

isatty(STDIN_FILENO);

該函數(shù)調(diào)用成功則返回0,若失敗則返回-1。

這時(shí),一個(gè)串口就已經(jīng)成功打開了。接下來就可以對這個(gè)串口進(jìn)行讀和寫操作。下面給出了一個(gè)完整的打開串口的函數(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);

下面兩個(gè)實(shí)例給出了串口讀和寫的兩個(gè)程序,其中用到前面所講述的open_port()和set_com_config()函數(shù)。寫串口的程序?qū)⒃谒拗鳈C(jī)上運(yùn)行,讀串口的程序?qū)⒃谀繕?biāo)板上運(yùn)行。

寫串口的程序如下所示。

/*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;

}

在宿主機(jī)上運(yùn)行寫串口的程序,而在目標(biāo)板上運(yùn)行讀串口的程序,運(yùn)行結(jié)果如下所示。

/*宿主機(jī),寫串口*/

$./com_writer

Inputsomewords(enter'quit'toexit):hello,Reader!

Inputsomewords(enter'quit'toexit):I'mWriter!

Inputsomewords(enter'quit'toexit):Thisisaserialporttestingprogram.

Inputsomewords(enter'quit'toexit):quit

/*目標(biāo)板,讀串口*/

$./com_reader

Thereceivedwordsare:hello,Reader!

Thereceivedwordsare:I'mWriter!

Thereceivedwordsare:Thisisaserialporttestingprogram.

Thereceivedwordsare:quit

另外,讀者還可以考慮一下如何使用select()函數(shù)實(shí)現(xiàn)串口的非阻塞讀寫,具體實(shí)例會在本章的后面的實(shí)驗(yàn)中給出。

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉(zhuǎn)型技術(shù)解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

關(guān)鍵字: AWS AN BSP 數(shù)字化

倫敦2024年8月29日 /美通社/ -- 英國汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時(shí)1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動 BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時(shí)企業(yè)卻面臨越來越多業(yè)務(wù)中斷的風(fēng)險(xiǎn),如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報(bào)道,騰訊和網(wǎng)易近期正在縮減他們對日本游戲市場的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競爭力 堅(jiān)持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競爭優(yōu)勢...

關(guān)鍵字: 通信 BSP 電信運(yùn)營商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺與中國電影電視技術(shù)學(xué)會聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會上宣布正式成立。 活動現(xiàn)場 NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會上,軟通動力信息技術(shù)(集團(tuán))股份有限公司(以下簡稱"軟通動力")與長三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉