• 正文
    • 串口調試
    • 串口編程調試
    • 串口編程實例
  • 推薦器件
  • 相關推薦
申請入駐 產業(yè)圖譜

【驅動】串口驅動分析(四)-串口編程和調試方法

2024/05/16
1977
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

串口調試

串口調試主要有 根據/proc系統(tǒng)信息確認串口狀態(tài),stty命令,編程調試 三種調試方法,下面我們分別具體介紹下。

根據設備節(jié)點確認串口是否正常

系統(tǒng)上電時,默認會使能串口,我們可以通過dmesg | grep ttyS 查看系統(tǒng)加載的串口設備。

也可以通過 ?cat /proc/tty/driver/serial 查看串口信息。

如果執(zhí)行 ?echo "123" > /dev/ttyS4 ,則會發(fā)送數據到ttyS4,通過串口線將串口接到PC 串口助手,確認是否收到數據。

如果此時再去查看串口設備,會看到uart4 的tx 發(fā)送了5個字符。

stty 命令

stty語法

stty?--help

Usage:?stty?[-F?DEVICE]?[--file=DEVICE]?[SETTING]...
??or:?stty?[-F?DEVICE]?[--file=DEVICE]?[-a|--all]
??or:?stty?[-F?DEVICE]?[--file=DEVICE]?[-g|--save]
[選項]
-a,?--all?:???以容易閱讀的方式打印當前的所有配置;
-g,?--save:?以stty終端可讀方式打印當前的所有配置。
-F,?--file:???打印當前的所有設置打開指定的設備,并用此設備作為輸入來代替標準輸入
[參數]
終端設置:指定終端命令行的設置選項。??

串口配置

stty?-F?/dev/ttyS0?speed?115200?cs8?-parenb?-cstopb

設置串口ttyS0波特率為115200,8位數據位,1位停止位,無校驗位。

查看串口屬性

stty?-a?-F?/dev/ttyS0
speed?9600?baud;?rows?0;?columns?0;?line?=?0;
intr?=?^C;?quit?=?^;?erase?=?^?;?kill?=?^U;?eof?=?^D;?eol?=?<undef>;
eol2?=?<undef>;?swtch?=?<undef>;?start?=?^Q;?stop?=?^S;?susp?=?^Z;?rprnt?=?^R;
werase?=?^W;?lnext?=?^V;?discard?=?^O;?min?=?1;?time?=?0;
-parenb?-parodd?-cmspar?cs8?hupcl?-cstopb?cread?clocal?-crtscts
-ignbrk?-brkint?-ignpar?-parmrk?-inpck?-istrip?-inlcr?-igncr?icrnl?ixon?-ixoff
-iuclc?-ixany?-imaxbel?-iutf8
opost?-olcuc?-ocrnl?onlcr?-onocr?-onlret?-ofill?-ofdel?nl0?cr0?tab0?bs0?vt0?ff0
isig?icanon?iexten?echo?echoe?echok?-echonl?-noflsh?-xcase?-tostop?-echoprt
echoctl?echoke?-flusho?-extproc

stty參數含義

特殊字符:
??dsusp?字符???每當輸入刷新時會發(fā)送一個用于終端阻塞信號的字符
???eof??字符????表示文件末尾而發(fā)送的字符(用于終止輸入)
???eol??字符????為表示行尾而發(fā)送的字符
??eol2?字符????為表示行尾而發(fā)送的另一個可選字符
???erase?字符???擦除前一個輸入文字的字符
???intr?字符????用于發(fā)送中斷信號的字符
???kill?字符????用于擦除當前終端行的字符
??lnext?字符???用于輸入下一個引用文字的字符
???quit?字符????用于發(fā)送退出信號的字符
??rprnt?字符???用于重繪當前行的字符
???start?字符???在停止后重新開啟輸出的字符
???stop?字符????停止輸出的字符
???susp?字符????發(fā)送終端阻斷信號的字符
??swtch?字符???在不同的shell?層次間切換的字符
??werase?字符??擦除前一個輸入的單詞的字符
?
特殊設置:
???N????????????設置輸入輸出速度為N?波特
??cols?N???????統(tǒng)治內核終端上有N?欄
??columns?N????等于cols?N
???ispeed?N?????設置輸入速度為N?波特
??line?N???????設置行約束規(guī)則為N
???min?N????????和?-icanon?配合使用,設置每次一完整讀入的最小字符數為<N>
???ospeed?N?????設置輸出速度為N?波特
??rows?N???????向內核通告此終端有N?行
??size?????????根據內核信息輸出當前終端的行數和列數
???speed????????輸出終端速度(單位為波特)
???time?N???????和-icanon?配合使用,設置讀取超時為N?個十分之一秒
?
控制設置:
???[-]clocal????禁用調制解調器控制信號
???[-]cread?????允許接收輸入
??[-]crtscts???啟用RTS/CTS?握手
???csN??????????設置字符大小為N?位,N?的范圍為5?到8
???[-]cstopb????每個字符使用2?位停止位?(要恢復成1?位配合"-"即可)
???[-]hup???????當最后一個進程關閉標準終端后發(fā)送掛起信號
???[-]hupcl?????等于[-]hup
???[-]parenb????對輸出生成奇偶校驗位并等待輸入的奇偶校驗位
???[-]parodd????設置校驗位為奇數?(配合"-"則為偶數)
?
輸入設置:
???[-]brkint????任務中斷會觸發(fā)中斷信號
???[-]icrnl?????將回車轉換為換行符
???[-]ignbrk????忽略中斷字符
???[-]igncr?????忽略回車
???[-]ignpar????忽略含有奇偶不對稱錯誤的字符
??[-]imaxbel???發(fā)出終端響鈴但不刷新字符的完整輸入緩沖
???[-]inlcr?????將換行符轉換為回車
???[-]inpck?????啟用輸入奇偶性校驗
???[-]istrip????剝除輸入字符的高8?位比特
??[-]iutf8?????假定輸入字符都是UTF-8?編碼
??[-]iuclc?????將大寫字母轉換為小寫
??[-]ixany?????使得任何字符都會重啟輸出,不僅僅是起始字符
???[-]ixoff?????啟用開始/停止字符傳送
???[-]ixon??????啟用XON/XOFF?流控制
???[-]parmrk????標記奇偶校驗錯誤?(結合255-0?字符序列)
???[-]tandem????等于[-]ixoff
?
輸出設置:
??bsN??????????退格延遲的風格,N?的值為0?至1
??crN??????????回車延遲的風格,N?的值為0?至3
??ffN??????????換頁延遲的風格,N?的值為0?至1
??nlN??????????換行延遲的風格,N?的值為0?至1
??[-]ocrnl?????將回車轉換為換行符
??[-]ofdel?????使用刪除字符代替空字符作填充
??[-]ofill?????延遲時使用字符填充代替定時器同步
??[-]olcuc?????轉換小寫字母為大寫
??[-]onlcr?????將換行符轉換為回車
??[-]onlret????使得換行符的行為表現和回車相同
??[-]onocr?????不在第一列輸出回車
???[-]opost?????后續(xù)進程輸出
??tabN?????????水平制表符延遲的風格,N?的值為0?至3
??tabs?????????等于tab0
??-tabs????????等于tab3
??vtN??????????垂直制表符延遲的風格,N?的值為0?至1
?
本地設置:
???[-]crterase??擦除字符回顯為退格符
??crtkill??????依照echoprt?和echoe?的設置清除所有行
??-crtkill?????依照echoctl?和echol?的設置清除所有行
??[-]ctlecho???在頭字符中輸出控制符號("^c")
???[-]echo??????回顯輸入字符
??[-]echoctl???等于[-]ctlecho
???[-]echoe?????等于[-]crterase
???[-]echok?????在每清除一個字符后輸出一次換行
??[-]echoke????等于[-]crtkill?意義相同
???[-]echonl????即使沒有回顯任何其它字符也輸出換行
??[-]echoprt???在""和"/"之間向后顯示擦除的字符
???[-]icanon????啟用erase、kill、werase?和rprnt?等特殊字符
???[-]iexten????允許POSIX?標準以外的特殊字符
???[-]isig??????啟用interrupt、quit和suspend?等特殊字符
???[-]noflsh????在interrupt?和?quit?特殊字符后禁止刷新
??[-]prterase??等于[-]echoprt
??[-]tostop????中止嘗試向終端寫入數據的后臺任務
??[-]xcase?????和icanon?配合使用,用轉義符""退出大寫狀態(tài)
?
綜合設置:
??[-]LCASE?????等于[-]lcase
???cbreak???????等于-icanon
???-cbreak??????等于icanon
???cooked???????等于brkint?ignpar?istrip?icrnl?ixon?opost?isig?icanon?eof?eol?等的默認值
???-cooked??????等于-raw
???crt??????????等于echoe?echoctl?echoke
???dec??????????等于echoe?echoctl?echoke?-ixany?intr?^c?erase?0177?kill?^u
??[-]decctlq???等于[-]ixany
???ek???????????清除所有字符,將它們回溯為默認值
???evenp????????等于parenb?-parodd?cs7
???-evenp???????等于-parenb?cs8
??[-]lcase?????等于xcase?iuclc?olcuc
???litout???????等于-parenb?-istrip?-opost?cs8
???-litout??????等于parenb?istrip?opost?cs7
???nl???????????等于-icrnl?-onlcr
???-nl??????????等于icrnl?-inlcr?-igncr?onlcr?-ocrnl?-onlret
???oddp?????????等于parenb?parodd?cs7
???-oddp????????等于-parenb?cs8
???[-]parity????等于[-]evenp
???pass8????????等于-parenb?-istrip?cs8
???-pass8???????等于parenb?istrip?cs7
???raw??????????等于-ignbrk?-brkint?-ignpar?-parmrk?-inpck?-istrip
?????????????????-inlcr?-igncr?-icrnl?-ixon?-ixoff?-iuclc?-ixany
?????????????????-imaxbel?-opost?-isig?-icanon?-xcase?min?1?time?0
???-raw?????????等于cooked
???sane?????????等于cread?-ignbrk?brkint?-inlcr?-igncr?icrnl?-iutf8
????????????????-ixoff?-iuclc?-ixany?imaxbel?opost?-olcuc?-ocrnl?onlcr
????????????????-onocr?-onlret?-ofill?-ofdel?nl0?cr0?tab0?bs0?vt0?ff0
????????????????isig?icanon?iexten?echo?echoe?echok?-echonl?-noflsh
????????????????-xcase?-tostop?-echoprt?echoctl?echoke,
????????????????所有特殊字符均使用默認值

串口編程調試

串口相關操作

Linux下,除了網絡設備,其余的都是文件的形式,串口設備也一樣在/dev下。

打開串口

示例:

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

在打開串口時,除了需要用到 O_RDWR (可讀寫)選項標志外,

O_NOCTTY:告訴 Linux “本程序不作為串口的‘控制終端’”。如果不使用該選項,會有一些輸入字符影響進程運行(如一些產生中斷信號的鍵盤輸入字符等)。

O_NDELAY:``標志則是告訴Linux,這個程序并不關心DCD信號線的狀態(tài)——也就是不關心端口另一端是否已經連接。

關閉串口:
close(fd)
讀寫串口:

與普通文件一樣,使用read,write函數。
示例:

read(fd,buff,8);
write(fd,buff,8);

串口屬性設置

很多系統(tǒng)都支持POSIX終端(串口)接口.程序可以利用這個接口來改變終端的參數,比如,波特率,字符大小等等.要使用這個端口的話,你必須將<termios.h>頭文件包含到你的程序中。這個頭文件中定義了終端控制結構體和POSIX控制函數。

termios 結構體:

struct?termios?{
??tcflag_t?c_cflag????/?控制標志
??tcflag_t?c_iflag;???/?輸入標志
??tcflag_t?c_oflag;???/?輸出標志
??tcflag_t?c_lflag;???/?本地標志
??tcflag_t?c_cc[NCCS];?/?控制字符
};

其中我們更關注的是c_cflag控制選項。其中包含了波特率、數據位、校驗位、停止位的設置。

c_cflag 控制標志常量
    CBAUD   (不屬于 POSIX) 波特率掩碼 (4+1 位)。CBAUDEX  (不屬于 POSIX) 擴展的波特率掩碼 (1 位),包含在 CBAUD 中。(POSIX 規(guī)定波特率存儲在 termios 結構中,并未精確指定它的位置,而是提供了函數 cfgetispeed() 和 cfsetispeed() 來存取它。一些系統(tǒng)使用 c_cflag 中 CBAUD 選擇的位,其他系統(tǒng)使用單獨的變量,例如 sg_ispeed 和 sg_ospeed 。)CSIZE   字符長度掩碼。取值為 CS5, CS6, CS7, 或 CS8。CSTOPB 設置兩個停止位,而不是一個。CREAD ?打開接受者。PARENB  允許輸出產生奇偶信息以及輸入的奇偶校驗。PARODD 輸入和輸出是奇校驗。HUPCL   在最后一個進程關閉設備后,降低 modem 控制線 (掛斷)。(?)CLOCAL 忽略 modem 控制線。LOBLK   (不屬于 POSIX) 從非當前 shell 層阻塞輸出(用于 shl )。(?)CIBAUD (不屬于 POSIX) 輸入速度的掩碼。CIBAUD 各位的值與 CBAUD 各位相同,左移了 IBSHIFT 位。CRTSCTS  (不屬于 POSIX) 啟用 RTS/CTS (硬件) 流控制。
c_iflag 輸入標志常量
    IGNBRK   忽略輸入中的 BREAK 狀態(tài)。BRKINT   如果設置了 IGNBRK,將忽略 BREAK。如果沒有設置,但是設置了 BRKINT,那么 BREAK 將使得輸入和輸出隊列被刷新,如果終端是一個前臺進程組的控制終端,這個進程組中所有進程將收到 SIGINT 信號。如果既未設置 IGNBRK 也未設置 BRKINT,BREAK 將視為與 NUL 字符同義,除非設置了 PARMRK,這種情況下它被視為序列 377 。IGNPAR   忽略楨錯誤和奇偶校驗錯。PARMRK  如果沒有設置 IGNPAR,在有奇偶校驗錯或楨錯誤的字符前插入 377 。如果既沒有設置 IGNPAR 也沒有設PARMRK,將有奇偶校驗錯或楨錯誤的字符視為 。INPCK   啟用輸入奇偶檢測。ISTRIP  去掉第八位。INLCR   將輸入中的 NL 翻譯為 CR。IGNCR   忽略輸入中的回車。ICRNL   將輸入中的回車翻譯為新行 (除非設置了 IGNCR)。IUCLC   (不屬于 POSIX) 將輸入中的大寫字母映射為小寫字母。IXON    啟用輸出的 XON/XOFF 流控制。IXANY  (不屬于 POSIX.1;XSI) 允許任何字符來重新開始輸出。(?)IXOFF  啟用輸入的 XON/XOFF 流控制。IMAXBEL (不屬于 POSIX) 當輸入隊列滿時響零。Linux 沒有實現這一位,總是將它視為已設置。
c_oflag 輸出標志常量
    OPOST   啟用具體實現自行定義的輸出處理。其余 c_oflag 標志常量定義在 POSIX 1003.1-2001 中,除非另外說明。OLCUC   (不屬于 POSIX) 將輸出中的小寫字母映射為大寫字母。ONLCR   (XSI) 將輸出中的新行符映射為回車-換行。OCRNL   將輸出中的回車映射為新行符ONOCR ?不在第 0 列輸出回車。ONLRET ?不輸出回車。OFILL ? ?發(fā)送填充字符作為延時,而不是使用定時來延時。OFDEL   (不屬于 POSIX) 填充字符是 ASCII DEL (0177)。如果不設置,填充字符則是 ASCII NUL。NLDLY   新行延時掩碼。取值為 NL0 和 NL1。CRDLY   回車延時掩碼。取值為 CR0, CR1, CR2, 或 CR3。TABDLY  水平跳格延時掩碼。取值為 TAB0, TAB1, TAB2, TAB3 (或 XTABS)。取值為 TAB3,即 XTABS,將擴展跳格為空格 (每個跳格符填充 8 個空格)。(?)BSDLY   回退延時掩碼。取值為 BS0 或 BS1。(從來沒有被實現過)VTDLY   豎直跳格延時掩碼。取值為 VT0 或 VT1。FFDLY   進表延時掩碼。取值為 FF0 或 FF1。
c_lflag 本地標志常量
    ISIG    ?當接受到字符 INTR, QUIT, SUSP, 或 DSUSP 時,產生相應的信號。ICANON   啟用標準模式 (canonical mode)。允許使用特殊字符 EOF, EOL, EOL2, ERASE, KILL, LNEXT, REPRINT, STATUS, 和 WERASE,以及按行的緩沖。XCASE   ?(不屬于 POSIX; Linux 下不被支持) 如果同時設置了 ICANON,終端只有大寫。輸入被轉換為小寫,除了以 前綴的字符。輸出時,大寫字符被前綴 ,小寫字符被轉換成大寫。ECHO    回顯輸入字符。ECHOE  如果同時設置了 ICANON,字符 ERASE 擦除前一個輸入字符,WERASE 擦除前一個詞。ECHOK  如果同時設置了 ICANON,字符 KILL 刪除當前行。ECHONL  如果同時設置了 ICANON,回顯字符 NL,即使沒有設置 ECHO。ECHOCTL (不屬于 POSIX) 如果同時設置了 ECHO,除了 TAB, NL, START, 和 STOP 之外的 ASCII 控制信號被回顯為 ^X, 這里 X 是比控制信號大 0x40 的 ASCII 碼。例如,字符 0x08 (BS) 被回顯為 ^H。ECHOPRT (不屬于 POSIX) 如果同時設置了 ICANON 和 IECHO,字符在刪除的同時被打印。ECHOKE   (不屬于 POSIX) 如果同時設置了 ICANON,回顯 KILL 時將刪除一行中的每個字符,如同指定了 ECHOE 和 ECHOPRT 一樣。DEFECHO (不屬于 POSIX) 只在一個進程讀的時候回顯。FLUSHO   (不屬于 POSIX; Linux 下不被支持) 輸出被刷新。這個標志可以通過鍵入字符 DISCARD 來開關。NOFLSH   禁止在產生 SIGINT, SIGQUIT 和 SIGSUSP 信號時刷新輸入和輸出隊列。TOSTOP   向試圖寫控制終端的后臺進程組發(fā)送 SIGTTOU 信號。IEXTEN   啟用實現自定義的輸入處理。這個標志必須與ICANON 同時使用,才能解釋特殊字符 EOL2,LNEXT,REPRINT 和 WERASE,IUCLC 標志才有效。
c_cc 特殊的控制字符
    VINTR   (003, ETX, Ctrl-C, or also 0177, DEL, rubout) 中斷字符。發(fā)出 SIGINT 信號。當設置 ISIG 時可被識別,不再作為輸入傳遞。VQUIT   (034, FS, Ctrl-) 退出字符。發(fā)出 SIGQUIT 信號。當設置 ISIG 時可被識別,不再作為輸入傳遞。VERASE  (0177, DEL, rubout, or 010, BS, Ctrl-H, or also #) 刪除字符。刪除上一個還沒有刪掉的字符,但不刪除上一個 EOF 或行首。當設置 ICANON 時可被識別,不再作為輸入傳遞。VKILL   ? (025, NAK, Ctrl-U, or Ctrl-X, or also @) 終止字符。刪除自上一個 EOF 或行首以來的輸入。當設置 ICANON 時可被識別,不再作為輸入傳遞。VEOF   ? (004, EOT, Ctrl-D) 文件尾字符。更精確地說,這個字符使得 tty 緩沖中的內容被送到等待輸入的用戶程序中,而不必等到 EOL。如果它是一行的第一個字符,那么用戶程序的 read() 將返回 0,指示讀到了 EOF。當設置 ICANON 時可被識別,不再作為輸入傳遞。VMIN   ? 非 canonical 模式讀的最小字符數。VEOL   ? (0, NUL) 附加的行尾字符。當設置 ICANON 時可被識別。VTIME  非 canonical 模式讀時的延時,以十分之一秒為單位。VEOL2  (not in POSIX; 0, NUL) 另一個行尾字符。當設置 ICANON 時可被識別。VSWTCH ?(not in POSIX; not supported under Linux; 0, NUL) 開關字符。(只為 shl 所用。)VSTART (021, DC1, Ctrl-Q) 開始字符。重新開始被 Stop 字符中止的輸出。當設置 IXON 時可被識別,不再作為輸入傳遞。VSTOP   (023, DC3, Ctrl-S) 停止字符。停止輸出,直到鍵入 Start 字符。當設置 IXON 時可被識別,不再作為輸入傳遞。VSUSP   (032, SUB, Ctrl-Z) 掛起字符。發(fā)送 SIGTSTP 信號。當設置 ISIG 時可被識別,不再作為輸入傳遞。VDSUSP ?(not in POSIX; not supported under Linux; 031, EM, Ctrl-Y) 延時掛起信號。當用戶程序讀到這個字符時,發(fā)送 SIGTSTP 信號。當設置 IEXTEN 和 ISIG,并且系統(tǒng)支持作業(yè)管理時可被識別,不再作為輸入傳遞。VLNEXT  (not in POSIX; 026, SYN, Ctrl-V) 字面上的下一個。引用下一個輸入字符,取消它的任何特殊含義。當設置 IEXTEN 時可被識別,不再作為輸入傳遞。VWERASE (not in POSIX; 027, ETB, Ctrl-W) 刪除詞。當設置 ICANON 和 IEXTEN 時可被識別,不再作為輸入傳遞。VREPRINT (not in POSIX; 022, DC2, Ctrl-R) 重新輸出未讀的字符。當設置 ICANON 和 IEXTEN 時可被識別,不再作為輸入傳遞。VDISCARD  (not in POSIX; not supported under Linux; 017, SI, Ctrl-O) 開關:開始/結束丟棄未完成的輸出。當設置 IEXTEN 時可被識別,不再作為輸入傳遞。

調用read 函數讀取串口數據時, 返回讀取數據的數量需要考慮兩個變量: MIN 和 TIME 。

MIN 和 TIME 在 termios 結構的 c_cc 成員的數組 下標名為 VMIN 和 VTIME 。MIN是指一次 read 調用期望返回的最小字節(jié)數 。 VTIME 說明等待數據到達的分秒數(秒的 1/10 為分秒) 。 TIME 與 MIN 組合使用的 具體含義分 為 以下四種情形:

當 MIN > 0 TIME > 0 時計時器在收到第一個字節(jié)后啟動,在計時器超時之前 TIME 的時間到) ),若已收到 MIN個字節(jié),則 read 返回 MIN 個字節(jié),否則,在計時器超時后返回實際接收到的字節(jié)。注意:因為只有在接收到第一個字節(jié)時才開始計時,所以至少可以返回1個字節(jié)。這種情形中,在接到第一個字節(jié)之前,調用者阻塞。如果在調用read時數據已經可用,則如同在read后數據立即被接到一樣。

當 MIN > 0 TIME = 0 時MIN個字節(jié)完整接收后, read 才返回,這可能會造成 read 無限期地阻塞。

當 MIN = 0, TIME > 0 時TIME為允許等待的最大時間,計 時器在調用 read 時立即啟動,在串口接到 1 字節(jié)數據或者計時器超時后即返回,如果是計時器超時,則返回 0 。

當 MIN = 0 TIME = 0 時如果有數據可用,則read 最多返回所要求的字節(jié)數,如果無數據可用,則 read 立即返回 0 。

終端api函數接口

tcgetattr

tcgetattr() 得到與 fd 指向的對象相關的參數,將它們保存于 termios_p 引用的 termios 結構中。函數可以從后臺進程中調用;但是,終端屬性可能被后來的前臺進程所改變。

tcsetattr

tcsetattr() 設置與終端相關的參數 (除非需要底層支持卻無法滿足),使用 termios_p 引用的 termios 結構。optional_actions 指定了什么時候改變會起作用:

    TCSANOW   改變立即發(fā)生TCSADRAIN   改變在所有寫入 fd 的輸出都被傳輸后生效。這個函數應當用于修改影響輸出的參數時使用。TCSAFLUSH  改變在所有寫入 fd 引用的對象的輸出都被傳輸后生效,所有已接受但未讀入的輸入都在改變發(fā)生前丟棄。

tcsendbreak

tcsendbreak() 傳送連續(xù)的 0 值比特流,持續(xù)一段時間,如果終端使用異步串行數據傳輸的話。如果 duration 是 0,它至少傳輸 0.25 秒,不會超過 0.5 秒。如果 duration 非零,它發(fā)送的時間長度由實現定義。如果終端并非使用異步串行數據傳輸,tcsendbreak() 什么都不做。

tcdrain

tcdrain() 等待直到所有寫入 fd 引用的對象的輸出都被傳輸。

tcflush

tcflush() 丟棄要寫入 引用的對象,但是尚未傳輸的數據,或者收到但是尚未讀取的數據,取決于 queue_selector 的值:

    TCIFLUSH   ?刷新收到的數據但是不讀TCOFLUSH ?刷新寫入的數據但是不傳送TCIOFLUSH  同時刷新收到的數據但是不讀,并且刷新寫入的數據但是不傳送

tcflow

tcflow() 掛起 fd 引用的對象上的數據傳輸或接收,取決于 action 的值:

    TCOOFF   ?掛起輸出、TCOON    重新開始被掛起的輸出TCIOFF   發(fā)送一個 STOP 字符,停止終端設備向系統(tǒng)傳送數據TCION    ?發(fā)送一個 START 字符,使終端設備向系統(tǒng)傳輸數據
串口屬性設置示例

設置串口屬性主要是配置termios結構體中的各個變量,大致流程如下:

    使用函數tcgetattr保存原串口屬性
struct?termios?newtio,oldtio;
tcgetattr(fd,&oldtio);
    通過位掩碼的方式激活本地連接和接受使能選項:CLOCAL和CREAD
newtio.c_cflag?|?=?CLOCAL?|?CREAD;
    使用函數cfsetispeed和cfsetospeed設置數據傳輸率
cfsetispeed(&newtio,B115200);
cfsetospeed(&newtio,B115200);
    通過位掩碼設置字符大小。
newtio.c_cflag?&=?~CSIZE;
newtio.c_cflag?|=?CS8;
    1. 設置奇偶效驗位需要用到兩個termios中的成員:c_cflag和c_iflag。首先要激活c_cflag中的校驗位使能標志PARENB和是否進行奇偶效驗,同時還要激活c_iflag中的奇偶效驗使能。

 

    設置奇校驗:
newtio.c_cflag?|=?PARENB;
newtio.c_cflag?|=?PARODD;
newtio.c_iflag?|=?(INPCK?|?ISTRIP);
設置偶校驗:
newtio.c_iflag?|=?(INPCK|ISTRIP);
newtio.c_cflag?|=?PARENB;
newtio.c_cflag?|=?~PARODD;
    激活c_cflag中的CSTOPB設置停止位。若停止位為1,則清除CSTOPB;若停止位為0,則激活CSTOPB。
newtio.c_cflag?&=?~CSTOPB;
    設置最少字符和等待時間。在對接收字符和等待時間沒有特別要求的情況下,可以將其設置為0。
newtio.c_cc[VTIME]?=?0;
newtio.c_cc[VMIN]?=?0;
    調用函數”tcflush(fd,queue_selector)”來處理要寫入引用的對象,queue_selector可能的取值有以下幾種。
    TCIFLUSH:刷新收到的數據但是不讀TCOFLUSH:刷新寫入的數據但是不傳送TCIOFLUSH:同時刷新收到的數據但是不讀,并且刷新寫入的數據但是不傳送。
    激活配置。在完成配置后,需要激活配置使其生效。使用tcsetattr()函數。
int?tcsetattr(int?filedes,int?opt,const?struct?termios?termptr);

串口編程實例

/@file??????main.c
??@brief???????串口應用編程測試
??@details
??@author??????zhongyi??
??@date????????2022-04-30
??@version?????V1.0
?
?/

/?包含的頭文件?/
#include?<stdio.h>????????//標準輸入輸出,如printf、scanf以及文件操作
#include?<stdlib.h>????????//標準庫頭文件,定義了五種類型、一些宏和通用工具函數
#include?<unistd.h>????????//定義?read?write?close?lseek?等Unix標準函數
#include?<sys/types.h>????//定義數據類型,如?ssiz?e_t?off_t?等
#include?<sys/stat.h>????//文件狀態(tài)
#include?<fcntl.h>????????//文件控制定義
#include?<termios.h>????//終端I/O
#include?<errno.h>????????//與全局變量?errno?相關的定義
#include?<getopt.h>????????//處理命令行參數
#include?<string.h>????????//字符串操作
#include?<time.h>????????//時間
#include?<sys/select.h>????//select函數

#define?DEV_NAME????"/dev/ttyS4"????///<?串口設備


/@brief???設置串口參數:波特率,數據位,停止位和效驗位
??@param[in]??fd?????????類型??int??????打開的串口文件句柄
??@param[in]??nSpeed?????類型??int?????波特率
??@param[in]??nBits?????類型??int?????數據位???取值?為?7?或者8
??@param[in]??nParity?????類型??int?????停止位???取值為?1?或者2
??@param[in]??nStop??????類型??int??????效驗類型?取值為N,E,O,,S
??@return?????返回設置結果
??-?0?????????設置成功
??-?-1?????設置失敗
?/
int?setOpt(int?fd,?int?nSpeed,?int?nBits,?int?nParity,?int?nStop)
{
????struct?termios?newtio,?oldtio;

????//?保存測試現有串口參數設置,在這里如果串口號等出錯,會有相關的出錯信息
????if?(tcgetattr(fd,?&oldtio)?!=?0)
????{
????????perror("SetupSerial?1");
????????return?-1;
????}

????bzero(&newtio,?sizeof(newtio));????????//新termios參數清零
????newtio.c_cflag?|=?CLOCAL?|?CREAD;????//CLOCAL--忽略?modem?控制線,本地連線,?不具數據機控制功能,?CREAD--使能接收標志
????//?設置數據位數
????newtio.c_cflag?&=?~CSIZE;????//清數據位標志
????switch?(nBits)
????{
????????case?7:
????????????newtio.c_cflag?|=?CS7;
????????break;
????????case?8:
????????????newtio.c_cflag?|=?CS8;
????????break;
????????default:
????????????fprintf(stderr,?"Unsupported?data?sizen");
????????????return?-1;
????}
????//?設置校驗位
????switch?(nParity)
????{
????????case?'o':
????????case?'O':?????????????????????//奇校驗
????????????newtio.c_cflag?|=?PARENB;
????????????newtio.c_cflag?|=?PARODD;
????????????newtio.c_iflag?|=?(INPCK?|?ISTRIP);
????????????break;
????????case?'e':
????????case?'E':?????????????????????//偶校驗
????????????newtio.c_iflag?|=?(INPCK?|?ISTRIP);
????????????newtio.c_cflag?|=?PARENB;
????????????newtio.c_cflag?&=?~PARODD;
????????????break;
????????case?'n':
????????case?'N':????????????????????//無校驗
????????????newtio.c_cflag?&=?~PARENB;
????????????break;
????????default:
????????????fprintf(stderr,?"Unsupported?parityn");
????????????return?-1;
????}
????//?設置停止位
????switch?(nStop)
????{
????????case?1:
????????????newtio.c_cflag?&=?~CSTOPB;
????????break;
????????case?2:
????????????newtio.c_cflag?|=?CSTOPB;
????????break;
????????default:
????????????fprintf(stderr,"Unsupported?stop?bitsn");
????????????return?-1;
????}
????//?設置波特率?2400/4800/9600/19200/38400/57600/115200/230400
????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?19200:
????????????cfsetispeed(&newtio,?B19200);
????????????cfsetospeed(&newtio,?B19200);
????????????break;
????????case?38400:
????????????cfsetispeed(&newtio,?B38400);
????????????cfsetospeed(&newtio,?B38400);
????????????break;
????????case?57600:
????????????cfsetispeed(&newtio,?B57600);
????????????cfsetospeed(&newtio,?B57600);
????????????break;
????????case?115200:
????????????cfsetispeed(&newtio,?B115200);
????????????cfsetospeed(&newtio,?B115200);
????????????break;
????????case?230400:
????????????cfsetispeed(&newtio,?B230400);
????????????cfsetospeed(&newtio,?B230400);
????????????break;
????????default:
????????????printf("tSorry,?Unsupported?baud?rate,?set?default?9600!nn");
????????????cfsetispeed(&newtio,?B9600);
????????????cfsetospeed(&newtio,?B9600);
????????????break;
????}
????//?設置read讀取最小字節(jié)數和超時時間
????newtio.c_cc[VTIME]?=?1;?????//?讀取一個字符等待1(1/10)s
????newtio.c_cc[VMIN]?=?1;????????//?讀取字符的最少個數為1

??????tcflush(fd,TCIFLUSH);?????????//清空緩沖區(qū)
??????if?(tcsetattr(fd,?TCSANOW,?&newtio)?!=?0)????//激活新設置
??????{
????????perror("SetupSerial?3");
??????????return?-1;
?????}
??????printf("Serial?set?done!n");
????return?0;
}

/@brief?串口讀取函數
??@param[in]??fd?????????打開的串口文件句柄
??@param[in]??rcv_buf?接收緩存指針
??@param[in]??data_len????要讀取數據長度
??@param[in]??timeout?????接收等待超時時間,單位ms
??@return?????返回設置結果
??-?>0??????設置成功
??-?其他??????讀取超時或錯誤
?/
int?UART_Recv(int?fd,?char?rcv_buf,?int?data_len,?int?timeout)
{
????int?len,?fs_sel;
????fd_set?fs_read;
????struct?timeval?time;

????time.tv_sec?=?timeout?/?1000;??????????????//set?the?rcv?wait?time
????time.tv_usec?=?timeout?%?1000??1000;????//100000us?=?0.1s

????FD_ZERO(&fs_read);????????//每次循環(huán)都要清空集合,否則不能檢測描述符變化
????FD_SET(fd,?&fs_read);????//添加描述符

????//?超時等待讀變化,>0:就緒描述字的正數目,?-1:出錯,?0?:超時
????fs_sel?=?select(fd?+?1,?&fs_read,?NULL,?NULL,?&time);
//????printf("fs_sel?=?%dn",?fs_sel);
????if(fs_sel)
????{
????????len?=?read(fd,?rcv_buf,?data_len);
????????return?len;
????}
????else
????{
//????????printf("Sorry,I?am?wrong!");
????????return?-1;
????}
}

/@brief?串口發(fā)送函數
??@param[in]??fd????????????打開的串口文件句柄
??@param[in]??send_buf?????發(fā)送數據指針
??@param[in]??data_len?????發(fā)送數據長度
??@return?????返回結果
??-?data_len????成功
??-?-1????????????失敗
?/
int?UART_Send(int?fd,?char?send_buf,?int?data_len)
{
????ssize_t?ret?=?0;

????ret?=?write(fd,?send_buf,?data_len);
????if?(ret?==?data_len)
????{
????????printf("send?data?is?%sn",?send_buf);
????????return?ret;
????}
????else
????{
????????printf("write?device?errorn");
????????tcflush(fd,TCOFLUSH);
????????return?-1;
????}
}


/@fn?main
??@brief?main入口函數
?/
int?main?(int?argc,?char?argv[])
{
????int?fdSerial;

????//?打開串口設備
????fdSerial?=?open(DEV_NAME,?O_RDWR?|?O_NOCTTY?|?O_NDELAY);
????if(fdSerial?<?0)
????{
????????perror(DEV_NAME);
????????return?-1;
????}
????//?設置串口阻塞,?0:阻塞,?FNDELAY:非阻塞
????if?(fcntl(fdSerial,?F_SETFL,?0)?<?0)????//阻塞,即使前面在open串口設備時設置的是非阻塞的
????{
????????printf("fcntl?failed!n");
????}
????else
????{
????????printf("fcntl=%dn",?fcntl(fdSerial,?F_SETFL,?0));
????}
????if?(isatty(fdSerial)?==?0)
????{
????????printf("standard?input?is?not?a?terminal?devicen");
????????close(fdSerial);
????????return?-1;
????}
????else
????{
????????printf("is?a?tty?success!n");
????}
????printf("fd-open=%dn",?fdSerial);


????//?設置串口參數
????if?(setOpt(fdSerial,?115200,?8,?'N',?1)==?-1)????//設置8位數據位、1位停止位、無校驗
????{
????????fprintf(stderr,?"Set?opt?Errorn");
????????close(fdSerial);
????????exit(1);
????}

????tcflush(fdSerial,?TCIOFLUSH);????//清掉串口緩存
????fcntl(fdSerial,?F_SETFL,?0);????//串口阻塞


????char?rcv_buf[100];
????int?len;

????while(1)????//循環(huán)讀取數據
????{
????????len?=?UART_Recv(fdSerial,?rcv_buf,?99,?10000);
????????if(len?>?0)
????????{
????????????rcv_buf[len]?=?'';
????????????printf("receive?data?is?%sn",?rcv_buf);
????????????printf("len?=?%dn",?len);
????????????UART_Send(fdSerial,?rcv_buf,?len);
????????}
????????else
????????{
//????????????printf("cannot?receive?datan");
????????}
????????usleep(100000);????//休眠100ms
????}
}

本文參考

https://blog.csdn.net/luotuo28/article/details/125896180

https://www.cnblogs.com/silencehuan/p/11103074.html

推薦器件

更多器件
器件型號 數量 器件廠商 器件描述 數據手冊 ECAD模型 風險等級 參考價格 更多信息
NC7SZ08M5 1 Rochester Electronics LLC LVC/LCX/Z SERIES, 2-INPUT AND GATE, PDSO5, 1.60 MM, MO-178, SOT-23, 5 PIN
$0.44 查看
ABS07-LR-32.768KHZ-6-1-T 1 Abracon Corporation CRYSTAL 32.768KHZ 6PF SMD

ECAD模型

下載ECAD模型
$2.7 查看
ASFLMB-25.000MHZ-LC-T 1 Abracon Corporation MEMS OSC XO 25.0000MHZ LVCMOS
$1.57 查看

相關推薦

登錄即可解鎖
  • 海量技術文章
  • 設計資源下載
  • 產業(yè)鏈客戶資源
  • 寫文章/發(fā)需求
立即登錄

作者就職于某500強公司,擔任BSP工程師。具有豐富的嵌入式開發(fā)經驗。專欄主要分享計算機基礎,操作系統(tǒng),Linux驅動開發(fā),Arm體系與架構,C/C++,數據結構與算法等相關文章。歡迎關注我的公眾號【嵌入式與Linux那些事】,一起學習交流。