<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          基于Linux的tty架構(gòu)及UART驅(qū)動(dòng)詳解

          共 9340字,需瀏覽 19分鐘

           ·

          2021-02-24 12:36


          一、模塊硬件學(xué)習(xí)

          1.1. Uart介紹

          通用異步收發(fā)傳輸器(Universal Asynchronous Receiver/Transmitter),通常稱為UART,是一種異步收發(fā)傳輸器,是電腦硬件的一部分。它將要傳輸?shù)馁Y料在串行通信與并行通信之間加以轉(zhuǎn)換。

          作為把并行輸入信號轉(zhuǎn)成串行輸出信號的芯片,UART 通常被集成于其他通訊接口的連上。

          UART 是一種通用串行數(shù)據(jù)總線,用于異步通信。該總線雙向通信,可以實(shí)現(xiàn)全雙工傳輸和接收。在嵌入式設(shè)備中,UART 用于主機(jī)與輔助設(shè)備通信,如汽車音與外接AP 之間的通信,與PC 機(jī)通信包括與監(jiān)控調(diào)試器和其它器件,如EEPOM通信。

          1.1.1. 通信協(xié)議

          UART作為異步串口通信協(xié)議的一種,工作原理是將傳輸數(shù)據(jù)的每個(gè)字符一位接一位地傳輸。其中各位的意義如下:

          • 起始位:先發(fā)出一個(gè)邏輯”0”的信號,表示傳輸字符的開始。

          • 數(shù)據(jù)位:緊接著起始位之后。數(shù)據(jù)位的個(gè)數(shù)可以是5、6、7、8等,構(gòu)成一個(gè)字符。通常采用ASCII碼。從最低位開始傳送,靠時(shí)鐘定位。

          • 奇偶校驗(yàn)位:數(shù)據(jù)位加上這一位后,使得“1”的位數(shù)應(yīng)為偶數(shù)(偶校驗(yàn))或奇數(shù)(奇校驗(yàn)),以此來校驗(yàn)數(shù)據(jù)傳送的正確性 。

          • 停止位:它是一個(gè)字符數(shù)據(jù)的結(jié)束標(biāo)志??梢允?位、1.5位、2位的高電平。

          由于數(shù)據(jù)是在傳輸線上定時(shí)的,并且每一個(gè)設(shè)備有其自己的時(shí)鐘,很可能在通信中兩臺(tái)設(shè)備間出現(xiàn)了小小的不同步。因此停止位不僅僅是表示傳輸?shù)慕Y(jié)束,并且提供計(jì)算機(jī)校正時(shí)鐘同步的機(jī)會(huì)。適用于停止位的位數(shù)越多,不同時(shí)鐘同步的容忍程度越大,但是數(shù)據(jù)傳輸率同時(shí)也越慢。

          • 空閑位:處于邏輯“1”狀態(tài),表示當(dāng)前線路上沒有數(shù)據(jù)傳送。

          Uart傳輸數(shù)據(jù)如圖2-1所示:

          1.1.2. 波特率

          波特率是衡量資料傳送速率的指標(biāo)。表示每秒鐘傳送的符號數(shù)(symbol)。一個(gè)符號代表的信息量(比特?cái)?shù))與符號的階數(shù)有關(guān)。例如傳輸使用256階符號,每8bit代表一個(gè)符號,數(shù)據(jù)傳送速率為120字符/秒,則波特率就是120 baud,比特率是120*8=960bit/s。這兩者的概念很容易搞錯(cuò)。

          UART 的接收和發(fā)送是按照相同的波特率進(jìn)行收發(fā)的。波特率發(fā)生器產(chǎn)生的時(shí)鐘頻率不是波特率時(shí)鐘頻率,而是波特率時(shí)鐘頻率的16倍,目的是為在接收時(shí)進(jìn)行精確的采樣,以提取出異步的串行數(shù)據(jù)。根據(jù)給定的晶振時(shí)鐘和要求的波特率,可以算出波特率分頻計(jì)數(shù)值。

          1.1.3. 工作原理

          發(fā)送數(shù)據(jù)過程:空閑狀態(tài),線路處于高電位;當(dāng)收到發(fā)送數(shù)據(jù)指令后,拉低線路一個(gè)數(shù)據(jù)位的時(shí)間T,接著數(shù)據(jù)位按低位到高位依次發(fā)送,數(shù)據(jù)發(fā)送完畢后,接著發(fā)送奇偶檢驗(yàn)位和停止位(停止位為高電位),一幀數(shù)據(jù)發(fā)送結(jié)束。

          接收數(shù)據(jù)過程: 空閑狀態(tài),線路處于高電位;當(dāng)檢測到線路的下降沿(線路電位由高電位變?yōu)榈碗娢唬r(shí)說明線路有數(shù)據(jù)傳輸,按照約定的波特率從低位到高位接收數(shù)據(jù),數(shù)據(jù)接收完畢后,接著接收并比較奇偶檢驗(yàn)位是否正確,如果正確則通知?jiǎng)t通知后續(xù)設(shè)備準(zhǔn)備接收數(shù)據(jù)或存入緩存。

          由于UART是異步傳輸,沒有傳輸同步時(shí)鐘。為了能保證數(shù)據(jù)傳輸?shù)恼_性,UART采用16倍數(shù)據(jù)波特率的時(shí)鐘進(jìn)行采樣。每個(gè)數(shù)據(jù)有16個(gè)時(shí)鐘采樣,取中間的采樣值,以保證采樣不會(huì)滑碼或誤碼。

          一般UART一幀的數(shù)據(jù)位為8,這樣即使每一個(gè)數(shù)據(jù)有一個(gè)時(shí)鐘的誤差,接收端也能正確地采樣到數(shù)據(jù)。

          UART的接收數(shù)據(jù)時(shí)序?yàn)?當(dāng)檢測到數(shù)據(jù)下降沿時(shí),表明線路上有數(shù)據(jù)進(jìn)行傳輸,這時(shí)計(jì)數(shù)器CNT開始計(jì)數(shù),當(dāng)計(jì)數(shù)器,當(dāng)計(jì)數(shù)器為8時(shí),采樣的值為“0”表示開始位;當(dāng)計(jì)數(shù)器為24=161+8時(shí),采樣的值為bit0數(shù)據(jù);當(dāng)計(jì)數(shù)器的值為40=162+8時(shí),采樣的值為bit1數(shù)據(jù);依次類推,進(jìn)行后面6個(gè)數(shù)據(jù)的采樣。如果需要進(jìn)行奇偶校驗(yàn)位,則當(dāng)計(jì)數(shù)器的值為152=169+8時(shí),采樣的值為奇偶位;當(dāng)計(jì)數(shù)器的值為168=1610+8時(shí),采樣的值為“1”表示停止位,一幀數(shù)據(jù)收發(fā)完成。

          1.1.4. RS232與RS485

          UART:通常說的UART指的是一種串行通信協(xié)議,規(guī)定了數(shù)據(jù)幀格式,波特率等。RS232和RS485:是兩種不同的電氣協(xié)議,也就是說,是對電氣特性以及物理特性的規(guī)定,作用于數(shù)據(jù)的傳輸通路上,它并不含對數(shù)據(jù)的處理方式。

          對應(yīng)的物理器件有RS232或者RS485驅(qū)動(dòng)芯片,將CPU經(jīng)過UART傳送過來的電壓信號驅(qū)動(dòng)成RS232或者RS485電平邏輯。

          RS232使用3-15V有效電平,而UART,因?yàn)閷﹄姎馓匦詻]有規(guī)定,所以直接使用CPU使用的電平,即TTL電平(在0-3.3V之間)。

          更具體的,電氣的特性也決定了線路的連接方式,比如RS232,規(guī)定用電平表示數(shù)據(jù),因此線路就是單線路的,兩根線能達(dá)到全雙工的目的;RS485使用差分電平表示數(shù)據(jù),因此必須用兩根線才能達(dá)到傳輸數(shù)據(jù)的基本要求,要實(shí)現(xiàn)全雙工,必須使用4根線。

          RS232和RS485的區(qū)別(1)抗干擾性

          • RS485 接口是采用平衡驅(qū)動(dòng)器和差分接收器的組合,具有抑制共模干擾的能力,抗噪聲干擾性強(qiáng)。
          • RS232接口使用一根信號線和一根信號返回線而構(gòu)成供地的傳輸形式,這種共地傳輸容易產(chǎn)生共模干擾,所以抗噪聲干擾性弱。(2)傳輸距離
          • RS485 接口的最大傳輸距離標(biāo)準(zhǔn)值為1200 米(9600bps 時(shí)),實(shí)際上可達(dá)3000米。
          • RS232 傳輸距離有限,最大傳輸距離標(biāo)準(zhǔn)值為50米,實(shí)際上也只能用15米左右。(3)通信能力
          • RS485接口在總線上最多可以連接128個(gè)收發(fā)器,即具有多站能力,而這樣的用戶可以利用單一的RS485接口方便的建立起設(shè)備網(wǎng)絡(luò)。
          • RS232只允許一對一通信。(4)傳輸速率
          • RS232傳輸速率較低,在異步傳輸時(shí),波特率為20Kbps.
          • RS485的數(shù)據(jù)最高傳輸速率為10Mbps. (5) 信號線
          • RS485全雙工:uart-tx 1根線,變成 RS485- A/B 2根線;uart-rx 1根線,變成 RS485- x/y 2根線,
          • RS485半雙工: 將全雙工的 A/B; X/Y 合并起來,分時(shí)復(fù)用。
          • RS232只允許一對一通信 (6)電氣電平值
          • 邏輯“1”以兩線間的電壓差為+(2-6)V表示;邏輯“0”以兩線間的電壓差為-(2-6)V表示。
          • 在RS232中任何一條信號的電壓均為負(fù)邏輯關(guān)系。即:邏輯“1”-5-15V;邏輯“0”,+5~+15V,噪聲容限為2V。即要求接收器能識(shí)別低至+3V的信號作為邏輯“0”,高到-3V的信號的信號作為邏輯“1”。
          • RS232接口的信號電平值較高,易損壞接口電路的芯片,又因?yàn)榕cTTL電平不兼容故使用電平轉(zhuǎn)換電路方能與TTL電路連接。
          • RS485接口信號電平比RS232降低了,就不易損壞接口電路的芯片,且該電平與TTL電平兼容,方便與TTL電路連接。

          1.1.5. 流控

          數(shù)據(jù)在兩個(gè)串口傳輸時(shí),常常會(huì)出現(xiàn)丟失數(shù)據(jù)的現(xiàn)象,或者兩臺(tái)計(jì)算機(jī)的處理速度不同,如臺(tái)式機(jī)與單片機(jī)之間的通訊,接收端數(shù)據(jù)緩沖區(qū)以滿,此時(shí)繼續(xù)發(fā)送的數(shù)據(jù)就會(huì)丟失,流控制能解決這個(gè)問題,當(dāng)接收端數(shù)據(jù)處理不過來時(shí),就發(fā)出“不再接收”的信號,發(fā)送端就停止發(fā)送,直到收到“可以繼續(xù)發(fā)送”的信號再發(fā)送數(shù)據(jù)。

          因此流控制可以控制數(shù)據(jù)傳輸?shù)倪M(jìn)程,防止數(shù)據(jù)丟失。PC機(jī)中常用的兩種流控為:硬件流控(包括RTS/CTS、DTR/CTS等)和軟件流控制XON/XOFF(繼續(xù)/停止)。

          1.1.5.1. 硬件流控

          硬件流控制常用的有RTS/CTS流控制和DTR/DSR流控制兩種。

          DTR–數(shù)據(jù)終端就緒(Data Terminal Ready)低有效,當(dāng)為低時(shí),表示本設(shè)備自身準(zhǔn)備就緒。此信號輸出對端設(shè)備,使用對端設(shè)備決定能否與本設(shè)備通信。

          DSR-數(shù)據(jù)裝置就緒(Data Set Ready)低有效,此信號由本設(shè)備相連接的對端設(shè)備提供,當(dāng)為低時(shí),本設(shè)備才能與設(shè)備端進(jìn)行通信。

          RTS - 請求發(fā)送(數(shù)據(jù))(Request To Send)低有效,此信號由本設(shè)備在需要發(fā)送數(shù)據(jù)給對端設(shè)備時(shí)設(shè)置。當(dāng)為低時(shí),表示本設(shè)備有數(shù)據(jù)需要向?qū)Χ嗽O(shè)備發(fā)送。對端設(shè)備能否接收到本方的發(fā)送數(shù)據(jù),則通過CTS信號來應(yīng)答。

          CTS - ?接收發(fā)送(請求)(Clear To Send)低有效,對端設(shè)備能否接收本方所發(fā)送的數(shù)據(jù),由CTS決定。若CTS為低,則表示對端的以準(zhǔn)備好,可以接收本端發(fā)送數(shù)據(jù)。

          以RTS/CTS流控制分析,分析主機(jī)發(fā)送/接收流程:

          物理連接

          主機(jī)的RTS(輸出信號),連接到從機(jī)的CTS(輸入信號)。主機(jī)是CTS(輸入信號),連接到從機(jī)的RTS(輸入信號)。

          • 1.主機(jī)的發(fā)送過程:主機(jī)查詢主機(jī)的CTS腳信號,此信號連接到從機(jī)的RTS信號,受從機(jī)控制。如果主機(jī)CTS信號有效(為低),表示從機(jī)的接收FIFO未滿,從機(jī)可以接收,此時(shí)主機(jī)可以向從機(jī)發(fā)送數(shù)據(jù),并且在發(fā)送過程中要一直查詢CTS信號是否為有效狀態(tài)。主機(jī)查詢到CTS無效時(shí),則中止發(fā)送。主機(jī)的CTS信號什么時(shí)候會(huì)無效呢?從機(jī)在接收到主機(jī)發(fā)送的數(shù)據(jù)時(shí),從機(jī)的接收模塊的FIFO如果滿了,則會(huì)使從機(jī)RTS無效,也即主機(jī)的CTS信號無效。主機(jī)查詢到CTS無效時(shí),主機(jī)發(fā)送中止。

          • 2.主機(jī)接收模式:如果主機(jī)接收FIFO未滿,那么使主機(jī)RTS信號有效(為低),即從機(jī)的CTS信號有效。此時(shí)如果從機(jī)要發(fā)送,發(fā)送前會(huì)查詢從機(jī)的CTS信號,如果有效,則開始發(fā)送。并且在發(fā)送過程中要一直查詢從機(jī)CTS信號的有效狀態(tài),如果無效則終止發(fā)送。是否有效由主機(jī)的RTS信號決定。如果主機(jī)FIFO滿了,則使主機(jī)的RTS信號無效,也即從機(jī)CTS信號無效,主機(jī)接收中止。

          1.1.5.2. 軟件流控

          由于電纜的限制,在普通的控制通訊中一般不采用硬件流控制,而是使用軟件流控制。

          一般通過XON/XOFF來實(shí)現(xiàn)軟件流控制。常用方法是:當(dāng)接收端的輸入緩沖區(qū)內(nèi)數(shù)據(jù)量超過設(shè)定的高位時(shí),就向數(shù)據(jù)發(fā)送端發(fā)送XOFF字符后就立即停止發(fā)送數(shù)據(jù)。

          當(dāng)接收端的輸入緩沖區(qū)內(nèi)數(shù)據(jù)量低于設(shè)定的低位時(shí),就向數(shù)據(jù)發(fā)送端發(fā)送XON字符(十進(jìn)制的17或Control-Q),發(fā)送端收到XON字符后就立即開始發(fā)送數(shù)據(jù)。

          一般可從設(shè)備配套源程序中找到發(fā)送端收到XON字符后就立即發(fā)送數(shù)據(jù)。一般可以從設(shè)備配套源程序中找到發(fā)送的是什么字節(jié)。

          應(yīng)注意,若傳輸?shù)氖嵌M(jìn)制的數(shù)據(jù),標(biāo)志字符也可能在數(shù)據(jù)流中出現(xiàn)而引起誤操作,這是軟件流控的缺陷,而硬件流控不會(huì)出現(xiàn)這樣的問題。

          二、Linux serial框架

          在Linux系統(tǒng)中,終端是一種字符型設(shè)備,它有多種類型,通常使用tty(Teletype)來簡稱各種類型的終端設(shè)備。

          對于嵌入式系統(tǒng)而言,最普遍采用的是Uart(Universal Asynchronous Receiver/Transmitter),串行端口,日常生活中簡稱端口

          2.1. TTY驅(qū)動(dòng)程序框架

          2.1.1. TTY概念

          2.1.1.1. 串口終端(/dev/ttyS*)

          串口終端是使用計(jì)算機(jī)串口連接的終端設(shè)備。Linux把每個(gè)串行端口都看做是一個(gè)字符設(shè)備。這些串行端口所對應(yīng)的設(shè)備名稱是/dev/ttySAC*;

          2.1.1.2. 控制臺(tái)終端(/dev/console)

          在Linux系統(tǒng)中,計(jì)算機(jī)的輸出設(shè)備通常被稱為控制臺(tái)終端,這里特指printk信息輸出到設(shè)備。/dev/console是一個(gè)虛擬的設(shè)備,它需要映射到真正的tty上,比如通過內(nèi)核啟動(dòng)參數(shù)“console=ttySCA0”就把console映射到了串口0

          2.1.1.3. 虛擬終端(/dev/tty*)

          當(dāng)用戶登錄時(shí),使用的是虛擬終端。使用Ctcl+Alt[F1 - F6]組合鍵時(shí),我們就可以切換到tty1、tty2、tty3等上面去。tty*就稱為虛擬終端,而tty0則是當(dāng)前所使用虛擬終端的一個(gè)別名。

          2.1.2. TTY架構(gòu)分析

          整個(gè) tty架構(gòu)大概的樣子如圖3.1所示,簡單來分的話可以說成兩層,一層是下層我們的串口驅(qū)動(dòng)層,它直接與硬件相接觸,我們需要填充一個(gè) struct uart_ops 的結(jié)構(gòu)體,另一層是上層 tty 層,包括 tty 核心以及線路規(guī)程,它們各自都有一個(gè) Ops 結(jié)構(gòu),用戶空通過間是 tty 注冊的字符設(shè)備節(jié)點(diǎn)來訪問。

          圖3.1tty架構(gòu)圖

          如圖3.2所示,tty設(shè)備發(fā)送數(shù)據(jù)的流程為:tty核心從一個(gè)用戶獲取將要發(fā)送給一個(gè)tty設(shè)備的數(shù)據(jù),tty核心將數(shù)據(jù)傳遞給tty線路規(guī)程驅(qū)動(dòng),接著數(shù)據(jù)被傳到tty驅(qū)動(dòng),tty驅(qū)動(dòng)將數(shù)據(jù)轉(zhuǎn)換為可以發(fā)給硬件的格式。

          接收數(shù)據(jù)的流程為:從tty硬件接收到的數(shù)據(jù)向上交給tty驅(qū)動(dòng),接著進(jìn)入tty線路規(guī)程驅(qū)動(dòng),再進(jìn)入tty核心,在這里它被一個(gè)用戶獲取。

          圖3.2 tty設(shè)備發(fā)送、接收數(shù)據(jù)流程

          2.2. 關(guān)鍵數(shù)據(jù)結(jié)構(gòu)

          2.2.1. Struct uart_driver

          uart_driver 包含了串口設(shè)備名,串口驅(qū)動(dòng)名,主次設(shè)備號,串口控制臺(tái)(可選))等信息,還封裝了tty_driver (底層串口驅(qū)動(dòng)無需關(guān)心tty_driver)

          struct?uart_driver?{
          ????????struct?module????*owner;?/*擁有該uart_driver的模塊,一般為THIS_MODULE*/
          ????????const?char????????*driver_name;?/*驅(qū)動(dòng)串口名,串口設(shè)備名以驅(qū)動(dòng)名為基礎(chǔ)*/
          ????????const?char????????*dev_name;?/*串口設(shè)備名*/
          ????????int?????????????????major;?/*主設(shè)備號*/
          ????????int?????????????????minor;?/*次設(shè)備號*/
          ????????int?????????????????nr;?/*該uart_driver支持的串口數(shù)*/
          ????????struct?console????*cons;?/*其對應(yīng)的console,若該uart_driver支持serial?console,
          *否則為NULL*/

          /*
          *?these?are?private;?the?low?level?driver?should?not
          *?touch?these;?they?should?be?initialised?to?NULL
          */

          struct?uart_state?*state;?/*下層,窗口驅(qū)動(dòng)層*/
          struct?tty_driver??*tty_driver;?/*tty相關(guān)*/

          2.2.2. struct console

          實(shí)現(xiàn)控制臺(tái)打印功能必須要注冊的結(jié)構(gòu)體

          struct?console?{
          ??????char?name[16];
          ??????void(*write)(struct?console?*,const?char?*,?unsigined);
          ??????int?(*read)(struct?console?*,?char?*,?unsigned);
          ??????struct?tty_driver?*(struct?console?*,int*);
          ??????void?(*unblank)(void);
          ??????int??(*setup)(struct?console?*,?char?*);
          ??????int??(*early_setup)(void);
          ??????short??flags;
          ??????short??index;?/*用來指定該console使用哪一個(gè)uart?port?(對應(yīng)的uart_port中的line),如果為-1,kernel會(huì)自動(dòng)選擇第一個(gè)uart?port*/
          ??????int???cflag;
          ??????void??*data;
          ??????struct???console?*next;
          };

          2.2.3. struct uart_state

          每一個(gè)uart端口對應(yīng)著一個(gè)uart_state,該結(jié)構(gòu)體將uart_port與對應(yīng)的circ_buf聯(lián)系起來。uart_state有兩個(gè)成員在底層串口驅(qū)動(dòng)會(huì)用到:xmit和port。

          用戶空間程序通過串口發(fā)送數(shù)據(jù)時(shí),上層驅(qū)動(dòng)將用戶數(shù)據(jù)保存在xmit;而串口發(fā)送中斷處理函數(shù)就是通過xmit獲取到用戶數(shù)據(jù)并將它們發(fā)送出去。串口接收中斷處理函數(shù)需要通過port將接收到的數(shù)據(jù)傳遞給線路規(guī)程層。

          struct?uart_state?{
          ???????struct??tty_port??port;
          ???????
          ???????enum?uart_pm_state???pm_state;
          ???????struct?circ_buf?????xmit;
          ???????
          ???????struct?uart_port?????*uart_port;?/*對應(yīng)于一個(gè)串口設(shè)備*/
          };

          2.2.4. struct uart_port

          uart_port用于描述串口端口的I/O端口或I/O內(nèi)存地址、FIFO大小、端口類型、串口時(shí)鐘等信息。實(shí)際上,一個(gè)uart_port實(shí)現(xiàn)對應(yīng)一個(gè)串口設(shè)備。

          struct?uart_port?{
          ????????spinlock_t??????????????lock;???????????????????/*?port?lock?*/
          ????????unsigned?long???????????iobase;?????????????????/*?in/out[bwl]?*/
          ????????unsigned?char?__iomem???*membase;???????????????/*?read/write[bwl]?*/
          ????????unsigned?int????????????(*serial_in)(struct?uart_port?*,?int);
          ????????void????????????????????(*serial_out)(struct?uart_port?*,?int,?int);
          ????????void????????????????????(*set_termios)(struct?uart_port?*,
          ???????????????????????????????????????????????struct?ktermios?*new,
          ???????????????????????????????????????????????struct?ktermios?*old);
          ????????int?????????????????????(*handle_irq)(struct?uart_port?*);
          ????????void????????????????????(*pm)(struct?uart_port?*,?unsigned?int?state,
          ??????????????????????????????????????unsigned?int?old);
          ????????void????????????????????(*handle_break)(struct?uart_port?*);
          ????????unsigned?int????????????irq;????????????????????/*?irq?number?*/
          ????????unsigned?long???????????irqflags;???????????????/*?irq?flags??*/
          ????????unsigned?int????????????uartclk;????????????????/*?base?uart?clock?*/
          ????????unsigned?int????????????fifosize;???????????????/*?tx?fifo?size?*/
          ????????unsigned?char???????????x_char;?????????????????/*?xon/xoff?char?*/
          ????????unsigned?char???????????regshift;???????????????/*?reg?offset?shift?*/
          ????????unsigned?char???????????iotype;?????????????????/*?io?access?style?*/
          ????????unsigned?char???????????unused1;

          #define?UPIO_PORT???????????????(0)
          #define?UPIO_HUB6???????????????(1)
          #define?UPIO_MEM????????????????(2)
          #define?UPIO_MEM32??????????????(3)
          #define?UPIO_AU?????????????????(4)?????????????????????/*?Au1x00?and?RT288x?type?IO?*/
          #define?UPIO_TSI????????????????(5)?????????????????????/*?Tsi108/109?type?IO?*/

          ????????unsigned?int????????????read_status_mask;???????/*?driver?specific?*/
          ????????unsigned?int????????????ignore_status_mask;?????/*?driver?specific?*/
          ????????struct?uart_state???????*state;?????????????????/*?pointer?to?parent?state?*/
          ????????struct?uart_icount??????icount;?????????????????/*?statistics?*/

          ????????struct?console??????????*cons;??????????????????/*?struct?console,?if?any?*/
          #if?defined(CONFIG_SERIAL_CORE_CONSOLE)?||?defined(SUPPORT_SYSRQ)
          ????????unsigned?long???????????sysrq;??????????????????/*?sysrq?timeout?*/
          #endif

          ????????upf_t???????????????????flags;

          #define?UPF_FOURPORT????????????((__force?upf_t)?(1?<
          #define?UPF_SAK?????????????????((__force?upf_t)?(1?<
          #define?UPF_SPD_MASK????????????((__force?upf_t)?(0x1030))
          #define?UPF_SPD_HI??????????????((__force?upf_t)?(0x0010))
          #define?UPF_SPD_VHI?????????????((__force?upf_t)?(0x0020))
          #define?UPF_SPD_CUST????????????((__force?upf_t)?(0x0030))
          #define?UPF_SPD_SHI?????????????((__force?upf_t)?(0x1000))
          #define?UPF_SPD_WARP????????????((__force?upf_t)?(0x1010))
          #define?UPF_SKIP_TEST???????????((__force?upf_t)?(1?<
          #define?UPF_AUTO_IRQ????????????((__force?upf_t)?(1?<
          #define?UPF_HARDPPS_CD??????????((__force?upf_t)?(1?<
          #define?UPF_LOW_LATENCY?????????((__force?upf_t)?(1?<
          #define?UPF_BUGGY_UART??????????((__force?upf_t)?(1?<
          #define?UPF_NO_TXEN_TEST????????((__force?upf_t)?(1?<
          #define?UPF_MAGIC_MULTIPLIER????((__force?upf_t)?(1?<
          /*?Port?has?hardware-assisted?h/w?flow?control?(iow,?auto-RTS?*not*?auto-CTS)?*/
          #define?UPF_HARD_FLOW???????????((__force?upf_t)?(1?<
          /*?Port?has?hardware-assisted?s/w?flow?control?*/
          #define?UPF_SOFT_FLOW???????????((__force?upf_t)?(1?<
          #define?UPF_CONS_FLOW???????????((__force?upf_t)?(1?<
          #define?UPF_SHARE_IRQ???????????((__force?upf_t)?(1?<
          #define?UPF_EXAR_EFR????????????((__force?upf_t)?(1?<
          #define?UPF_BUG_THRE????????????((__force?upf_t)?(1?<
          /*?The?exact?UART?type?is?known?and?should?not?be?probed.??*/
          #define?UPF_FIXED_TYPE??????????((__force?upf_t)?(1?<
          #define?UPF_BOOT_AUTOCONF???????((__force?upf_t)?(1?<
          #define?UPF_FIXED_PORT??????????((__force?upf_t)?(1?<
          #define?UPF_DEAD????????????????((__force?upf_t)?(1?<
          #define?UPF_IOREMAP?????????????((__force?upf_t)?(1?<

          #define?UPF_CHANGE_MASK?????????((__force?upf_t)?(0x17fff))
          #define?UPF_USR_MASK????????????((__force?upf_t)?(UPF_SPD_MASK|UPF_LOW_LATENCY))

          ????????unsigned?int????????????mctrl;??????????????????/*?current?modem?ctrl?settings?*/
          ????????unsigned?int????????????timeout;????????????????/*?character-based?timeout?*/
          ????????unsigned?int????????????type;???????????????????/*?port?type?*/
          ????????const?struct?uart_ops???*ops;
          ????????unsigned?int????????????custom_divisor;
          ????????unsigned?int????????????line;???????????????????/*?port?index?*/
          ????????resource_size_t?????????mapbase;????????????????/*?for?ioremap?*/
          ????????struct?device???????????*dev;???????????????????/*?parent?device?*/
          ????????unsigned?char???????????hub6;???????????????????/*?this?should?be?in?the?8250?driver?*/
          ????????unsigned?char???????????suspended;
          ????????unsigned?char???????????irq_wake;
          ????????unsigned?char???????????unused[2];
          ????????void????????????????????*private_data;??????????/*?generic?platform?data?pointer?*/
          };

          2.2.5. struct uart_ops

          struct uart_ops涵蓋了驅(qū)動(dòng)可對串口的所有操作

          ?struct?uart_ops?{
          ????????unsigned?int????(*tx_empty)(struct?uart_port?*);
          ????????void????????????(*set_mctrl)(struct?uart_port?*,?unsigned?int?mctrl);
          ????????unsigned?int????(*get_mctrl)(struct?uart_port?*);
          ????????void????????????(*stop_tx)(struct?uart_port?*);
          ????????void????????????(*start_tx)(struct?uart_port?*);
          ????????void????????????(*throttle)(struct?uart_port?*);
          ????????void????????????(*unthrottle)(struct?uart_port?*);
          ????????void????????????(*send_xchar)(struct?uart_port?*,?char?ch);
          ????????void????????????(*stop_rx)(struct?uart_port?*);
          ????????void????????????(*enable_ms)(struct?uart_port?*);
          ????????void????????????(*break_ctl)(struct?uart_port?*,?int?ctl);
          ????????int?????????????(*startup)(struct?uart_port?*);
          ????????void????????????(*shutdown)(struct?uart_port?*);
          ????????void????????????(*flush_buffer)(struct?uart_port?*);
          ????????void????????????(*set_termios)(struct?uart_port?*,?struct?ktermios?*new,
          ???????????????????????????????????????struct?ktermios?*old);
          ????????void????????????(*set_ldisc)(struct?uart_port?*,?int?new);
          ????????void????????????(*pm)(struct?uart_port?*,?unsigned?int?state,
          ??????????????????????????????unsigned?int?oldstate);
          ????????int?????????????(*set_wake)(struct?uart_port?*,?unsigned?int?state);

          ????????/*
          ?????????*?Return?a?string?describing?the?type?of?the?port
          ?????????*/

          ????????const?char??????*(*type)(struct?uart_port?*);

          ????????/*
          ?????????*?Release?IO?and?memory?resources?used?by?the?port.
          ?????????*?This?includes?iounmap?if?necessary.
          ?????????*/

          ????????void????????????(*release_port)(struct?uart_port?*);

          ????????/*
          ?????????*?Request?IO?and?memory?resources?used?by?the?port.
          ?????????*?This?includes?iomapping?the?port?if?necessary.
          ?????????*/

          ????????int?????????????(*request_port)(struct?uart_port?*);
          ????????void????????????(*config_port)(struct?uart_port?*,?int);
          ????????int?????????????(*verify_port)(struct?uart_port?*,?struct?serial_struct?*);
          ????????int?????????????(*ioctl)(struct?uart_port?*,?unsigned?int,?unsigned?long);
          #ifdef?CONFIG_CONSOLE_POLL
          ????????int?????????????(*poll_init)(struct?uart_port?*);
          ????????void????????????(*poll_put_char)(struct?uart_port?*,?unsigned?char);
          ????????int?????????????(*poll_get_char)(struct?uart_port?*);
          #endif
          };

          2.3. 關(guān)鍵流程

          2.3.1. 注冊流程

          2.3.1.1. 注冊uart_driver

          此接口在uart driver中調(diào)用,用來注冊uart_driver到kernel中,調(diào)用階段在uart driver的初始階段,例如:module_init(), uart_driver的注冊流程圖

          圖3.3uart driver注冊流程

          注冊過程主要做了以下操作:

          • 1、根據(jù)driver支持的最大設(shè)備數(shù),申請n個(gè)uart_state空間,每一個(gè)uart_state都有一個(gè)uart_port。
          • 2、分配一個(gè)tty_driver,并將uart_driver->tty_driver指向它。
          • 3、對tty_driver進(jìn)行設(shè)置,其中包括默認(rèn)波特率、檢驗(yàn)方式等,還有一個(gè)重要的ops,結(jié)構(gòu)體tty_operation的注冊,它是tty核心與串口驅(qū)動(dòng)通信的接口。
          • 4、初始化每一個(gè)uart_state的tty_port;
          • 5、注冊tty_driver。注冊uart_driver實(shí)際上是注冊tty_driver,與用戶空間打交道的工作完全交給tty_driver,這一部分是內(nèi)核實(shí)現(xiàn)好的不需要修改

          2.3.1.2. 添加uart_port

          此接口用于注冊一個(gè)uart port 到uart driver上,通過注冊,uart driver就可以訪問對應(yīng)的uart port,進(jìn)行數(shù)據(jù)收發(fā)。該接口在uart driver中的probe函數(shù)調(diào)用,必須保證晚于uart_register_drver的注冊過程。

          uart driver在調(diào)用接口前,要手動(dòng)設(shè)置uart_port的操作uart_ops,使得通過調(diào)用uart_add_one_port接口后驅(qū)動(dòng)完成硬件的操作接口注冊。uart添加port流程如圖3-4所示:

          圖3-4 uart添加port流程圖

          2.4. 數(shù)據(jù)收發(fā)流程

          2.4.1. 打開設(shè)備(open操作)

          open設(shè)備的大體流程如圖3-5所示:

          圖3-5 open設(shè)備流程

          2.4.2. 數(shù)據(jù)發(fā)送流程(write操作)

          發(fā)送數(shù)據(jù)大體流程如圖3-6所示:

          圖3-6 發(fā)送數(shù)據(jù)流程

          2.4.3. 數(shù)據(jù)接收流程(read操作)

          接收數(shù)據(jù)的大體流程如圖3-7所示:

          圖3-7數(shù)據(jù)接收流程

          2.4.4. 關(guān)閉設(shè)備(close操作)

          close設(shè)備的大體流程如圖3-8所示:

          圖3-8 close設(shè)備流程

          2.4.5. 注銷流程

          2.4.5.1. 移除uart_port

          此接口用于從uart driver上注銷一個(gè)uart port,該接口在uart driver中的remove函數(shù)中調(diào)用。uart移除port的流程如圖3-9所示:

          圖3.9 uart移除port流程圖

          2.4.5.2. 注銷uart_driver

          此接口在uart driver中調(diào)用,用來從kernel中注銷uart_driver,調(diào)用階段在uart driver的退出階段,例如:module_exit(),uart driver的注銷流程如圖3.10所示

          2.5. 使用rs485通信

          2.5.1. rs485和rs232的區(qū)別

          uart(TTL-3.3V)/rs232(工業(yè)級 +-12V)是電壓驅(qū)動(dòng),rs485是電流驅(qū)動(dòng)(能傳輸更遠(yuǎn)的距離) rS232用電平表示數(shù)據(jù),使用2根線可實(shí)現(xiàn)全雙工,rs485用差分電平表示數(shù)據(jù),因此必須用4根線實(shí)現(xiàn)全雙工rs485;

          全雙工:uart-tx 1根線變成rs485-A/B 2根線;uart-rx 1根線變成rs485- X/Y兩根線;

          rs485半雙工: 將全雙工的A/B和X/Y合并起來分時(shí)復(fù)用;rs485-de/re是給轉(zhuǎn)換器的一個(gè)控制信號,對我們芯片來說,都是輸出;

          2.5.2. rs485調(diào)試方法:

          首先保證uart模塊和相關(guān)gpio,電壓轉(zhuǎn)換芯片工作正常:

          • a,保證uart tx/rx功能正常。
          • b,用gpio-output來控制 de/re 相關(guān)的2個(gè)gpio,觀察 de/re的gpio輸出low/high是否正常
          • c,在b的基礎(chǔ)上,單獨(dú)調(diào)試 rs485-tx/rs485-rx,單端調(diào)試是否pass.

          模式12-gpio-normal-uart-rs485-halfduplex (2個(gè)gpio獨(dú)立控制de/re, enable就是將相關(guān)gpio設(shè)置到active電平;不用uart控制器的rs485模式;uart控制器處于normal模式)

          • a, 默認(rèn)re-en, de-dis,默認(rèn)rs485-rx
          • b, 當(dāng)要發(fā)送的時(shí)候,re-dis, de-enable, 然后uart-tx.
          • c, tx完成之后,de-dis; re-en,進(jìn)入默認(rèn)的rs485-rx模式。

          模式21-gpio-normal-uart-rs485-halfduplex 這個(gè)模式的前提條件,外設(shè)器件的 de/re必須是相反極性的,比如de是高電平有效,re是低電平有效,則可以用一個(gè)gpio,來控制 de/re,此時(shí)de/re一定是互斥的。(1個(gè)gpio控制de/re, enable就是將相關(guān)gpio設(shè)置到active電平;不用uart控制器的rs485模式;uart控制器處于normal模式)

          • a, re-en,進(jìn)入rs485-rx模式 (re 通常是低電平有效,這一步就是 設(shè)置 re對應(yīng)的gpio為低電平)
          • b, 當(dāng)要發(fā)送的時(shí)候,設(shè)置gpio:re-disable, de-enable, 然后uart-tx.(re 通常是低電平有效,這一步就是 設(shè)置 re對應(yīng)的gpio為高電平)
          • c, tx完成之后,de-disable; re-enable,進(jìn)入默認(rèn)的rs485-rx模式。(re 通常是低電平有效,這一步就是 設(shè)置 re對應(yīng)的gpio為低電平)

          模式3rs485-software-halfduplex(de/re 獨(dú)立輸出) (使能uart控制器的rs485模式; 通過uart模塊內(nèi)部reg來控制 de/re 信號)

          • a,使能uart控制器的 rs485模式,并按照電壓轉(zhuǎn)換芯片的特性,設(shè)置de/re polarity
          • b, 設(shè)置rs485的模式為 sw-half-duplex, 設(shè)置 de-timing寄存器; 設(shè)置 de/re turnaround 寄存器。
          • c, 默認(rèn)為rs485-rx模式,設(shè)置 de-dis/re-en
          • d, 當(dāng)要tx的時(shí)候,設(shè)置 de-en/re-dis
          • e, 發(fā)送完成,設(shè)置 de-dis/re-en

          模式4rs485-hardware-halfduplex(de/re 獨(dú)立輸出) 基本配置同模式3,但是設(shè)置 rs485模式為 hardware-halfduplex模式

          • a, 只要設(shè)置 de-en/rx-en 都為1,然后就不用管了,硬件實(shí)現(xiàn)半雙工切換。

          模式5:使用純硬件的辦法實(shí)現(xiàn)RS485半雙工功能,電路如圖所示:

          接收:默認(rèn)沒有數(shù)據(jù)時(shí),UART_TX為高電平,三極管導(dǎo)通,485芯片RE低電平使能,RO接收數(shù)據(jù)使能,此時(shí)從485AB口收到什么數(shù)據(jù)就會(huì)通過RO通道傳到MCU,完成數(shù)據(jù)接收過程。發(fā)送:當(dāng)發(fā)送數(shù)據(jù)時(shí),UART_TX會(huì)有一個(gè)下拉的電平,表示開始發(fā)送數(shù)據(jù),此時(shí)三極管截止,DE為高電平發(fā)送使能。當(dāng)發(fā)送數(shù)據(jù)‘0’時(shí),由于DI口連接地,此時(shí)數(shù)據(jù)‘0’就會(huì)傳輸?shù)紸B口 A-B<0,傳輸‘0’,完成了低電平的傳輸。當(dāng)發(fā)送‘1’時(shí),此時(shí)三極管導(dǎo)通,按理說RO使能,此時(shí)由于還處在發(fā)送數(shù)據(jù)中,這種狀態(tài)下485處于高阻態(tài),此時(shí)的狀態(tài)通過A上拉B下拉電阻決定,此時(shí)A-B>0傳輸‘1’,完成高電平的傳輸。

          3. 模塊詳細(xì)設(shè)計(jì)

          3.1. 關(guān)鍵函數(shù)接口

          3.1.1. uart_register_driver

          /*功能:? uart_register_driver用于串口驅(qū)動(dòng)uart_driver注冊到內(nèi)核(串口核心層)中,通常在模塊初始化函數(shù)調(diào)用該函數(shù)。
          ?*參數(shù):drv:要注冊的uart_driver
          ?*返回值:成功,返回0;否則返回錯(cuò)誤碼
          ?*/

          int?uart_register_driver(struct?uart_driver?*drv)

          3.1.2. uart_unregister_driver

          /*功能:uart_unregister 用于注銷我們已注冊的uart_driver,通常在模塊卸載函數(shù)調(diào)用該函數(shù),
          ?*參數(shù)?:?drv:要注銷的uart_driver
          ?*返回值:成功返回0,否則返回錯(cuò)誤碼
          ?*/

          void?uart_unregister_driver(struct?uart_driver?*drv)

          3.1.3. uart_add_one_port

          /*功能:uart_add_one_port用于為串口驅(qū)動(dòng)添加一個(gè)串口端口,通常在探測到設(shè)備后(驅(qū)動(dòng)的設(shè)備probe方法)調(diào)用該函數(shù)
          ?*參數(shù):
          ?*?????drv:串口驅(qū)動(dòng)
          ?*?????port:要添加的串口端口
          ?*返回值:成功,返回0;否則返回錯(cuò)誤碼
          ?*/

          int?uart_add_one_port(struct?uart_driver?*drv,struct?uart_port?*port)

          3.1.4. uart_remove_one_port

          /*功能:uart_remove_one_port用于刪除一個(gè)已經(jīng)添加到串口驅(qū)動(dòng)中的串口端口,通常在驅(qū)動(dòng)卸載時(shí)調(diào)用該函數(shù)
          ?*參數(shù):
          ?*?????drv:串口驅(qū)動(dòng)
          ?*?????port:要?jiǎng)h除的串口端口
          ?*返回值:成功,返回0;否則返回錯(cuò)誤碼
          ?*/

          int?uart_remove_one_port(struct?uart_driver?*drv,struct?uart_port?*port)

          3.1.5. uart_write_wakeup

          /*功能:uart_write_wakeup喚醒上層因串口端口寫數(shù)據(jù)而堵塞的進(jìn)程,通常在串口發(fā)送中斷處理函數(shù)中調(diào)用該函數(shù)
          ?*參數(shù):
          ?*?????port:?需要喚醒寫堵塞進(jìn)程的串口端口
          ?*/

          void?uart_write_wakeup(struct?uart_port?*port)

          3.1.6. uart_suspend_port

          /*功能:uart_suspend_port用于掛起特定的串口端口
          ?*參數(shù):
          ?*?????drv:要掛起的串口端口鎖所屬的串口驅(qū)動(dòng)
          ?*?????port:要掛起的串口端口
          ?*返回值:成功返回0;否則返回錯(cuò)誤碼
          ?*/

          int?uart_suspend_port(struct?uart_driver?*drv,?struct?uart_port?*port)

          3.1.7. uart_resume_port

          /*功能:uart_resume_port用于恢復(fù)某一已掛起的串口
          ?*參數(shù):
          ?*?????drv:要恢復(fù)的串口端口所屬的串口驅(qū)動(dòng)
          ?*?????port:要恢復(fù)的串口端口
          ?*返回值:成功返回0;否則返回錯(cuò)誤碼
          ?*/

          int?uart_resume_port(struct?uart_driver?*drv,?struct?uart_port?*port)

          3.1.8. uart_get_baud_rate

          /*功能:uart_get_baud_rate通過解碼termios結(jié)構(gòu)體來獲取指定串口的波特率
          ?*參數(shù):
          ?*???? port:要獲取波特率的串口端口
          ?*?????termios:當(dāng)前期望的termios配置(包括串口波特率)
          ?*?????old:以前的termios配置,可以為NULL
          ?*?????min:可以接受的最小波特率
          ?*?????max:可以接受的最大波特率
          ?*?????返回值:串口波特率
          ?*/

          unsigned?int?uart_get_baund_rate(struct?uart_port?*port,?struct?ktermios?*termios,?struct?ktermios?*old,unsigned?int?min,?unsigned?int?max)

          3.1.9. uart_get_divisor

          /*功能:uart_get_divisor 用于計(jì)算某一波特率的串口時(shí)鐘分頻數(shù)(串口波特率除數(shù))
          ?*參數(shù):
          ?*???? port:要計(jì)算分頻數(shù)的串口端口
          ?*???? baud:期望的波特率
          ?*返回值:串口時(shí)鐘分頻數(shù)
          ?*/

          unsigned?int?uart_get_divisor(struct?uart_port?*port,?unsigned?int?baund)

          3.1.10. uart_update_timeout

          /*功能:uart_update_timeout用于更新(設(shè)置)串口FIFO超出時(shí)間
          ?*參數(shù):
          ?*???? port:要更新超時(shí)間的串口端口
          ?*???? cfalg:termios結(jié)構(gòu)體的cflag值
          ?*???? baud:串口的波特率
          ?*/

          void?uart_update_timeout(struct?uart_port?*port,unsigned?int?cflag,?unsigned?int?baud)

          3.1.11. uart_insert_char

          /*功能:uart_insert_char用于向uart層插入一個(gè)字符
          ?*參數(shù):
          ?*???? port:要寫信息的串口端口
          ?*???? status:RX buffer狀態(tài)
          ?*???? overrun:在status中的overrun bit掩碼
          ?*???? ch:需要插入的字符
          ?*???? flag:插入字符的flag:TTY_BREAK,TTY_PSRIYY, TTY_FRAME
          ?*/

          void?uart_insert_char(struct?uart_port?*port,?unsigned?int?status,?unsigned?int?overrun,unsigned?int?ch,?unsigned?int?flag)

          3.1.12. uart_console_write

          /*功能:uart_console_write用于向串口端口寫一控制臺(tái)信息
          ?*參數(shù):
          ?*???? port:要寫信息的串口端口
          ?*?????s:要寫的信息
          ?*???? count:信息的大小
          ?*???? putchar:用于向串口端口寫字符的函數(shù),該函數(shù)有兩個(gè)參數(shù):串口端口和要寫的字符
          ?*/

          Void?uart_console_write(struct?uart_port?*port,const?char?*s,?unsigned?int?count,viod(*putchar)(struct?uart_port*,?int))

          4. 模塊使用說明

          4.1. 串口編程

          4.1.1. 串口控制函數(shù)

          屬性說明
          tcgetatrr取屬性(termios結(jié)構(gòu))
          tcsetarr設(shè)置屬性(termios結(jié)構(gòu))
          cfgetispeed得到輸入速度
          cfsetispeed得到輸出速度
          cfstospeed設(shè)置輸出速度
          tcdrain等待所有輸出都被傳輸
          tcflow掛起傳輸或接收
          tcflush刷請未決輸出和/或輸入
          tcsendbreak送BREAK字符
          tcgetpgrp得到前臺(tái)進(jìn)程組ID
          Tcsetpgrp設(shè)置前臺(tái)進(jìn)程組ID

          4.1.2. 串口配置流程

          • (1) 保持原先串口配置,使用tegetatrr(fd, &oldtio);
          struct?termious?newtio,?oldtio;
          tegetattr(fd,?&oldtio);
          • (2) 激活選項(xiàng)有CLOCAL和CREAD,用于本地連接和接收使用
          newtio.cflag?|=?CLOCAL|CREAD;
          • (3) 設(shè)置波特率
          newtio.c_cflag?=?B115200;
          • (4) 設(shè)置數(shù)據(jù)位,需使用掩碼設(shè)置
          newtio.c_cflag?&=?~CSIZE;
          Newtio.c_cflag?|=?CS8;
          • (5) 設(shè)置停止位,通過激活c_cflag中的CSTOP實(shí)現(xiàn)。若停止位為1,則清除CSTOPB,若停止位為2,則激活CSTOP
          newtio.c_cflag?&=?~CSTOPB;?/*停止位設(shè)置為1*/
          Newtio.c_cflag?|=?CSTOPB;?/*停止位設(shè)置為2?*/
          • (6) 設(shè)置流控
          newtio.c_cfag?|=?CRTSCTS?/*開啟硬件流控?*/
          newtio.c_cfag?|=?(IXON?|?IXOFF?|?IXANY);?/*開啟軟件流控*/
          • (7) 奇偶檢驗(yàn)位設(shè)置,使用c_cflag和c_ifag. 設(shè)置奇校驗(yàn)
          newtio.c_cflag?|=?PARENB;
          newtio.c_cflag?|=?PARODD;
          newtio.c_iflag?|=?(INPCK?|?ISTRIP);

          設(shè)置偶校驗(yàn)

          newtio.c_iflag?|=?(INPCK?|?ISTRIP);
          newtio.c_cflag?|=?PARENB;
          newtio.c_cflag?|=?~PARODD;
          • (8) 設(shè)置最少字符和等待時(shí)間,對于接收字符和等待時(shí)間沒有什么特別的要求,可設(shè)置為0:
          newtio.c_cc[VTIME]?=?0;
          newtio.c_cc[VMIN]??=?0;
          • (9) 處理要寫入的引用對象 tcflush函數(shù)刷清(拋棄)輸入緩沖(終端程序已經(jīng)接收到,但用戶程序尚未讀)或輸出緩沖(用戶程序已經(jīng)寫,但未發(fā)送)。
          int?tcflash(int?filedes,?int?quene)
          quene數(shù)應(yīng)當(dāng)是下列三個(gè)常數(shù)之一:
          ??*TCIFLUSH?刷清輸入隊(duì)列
          ??*TCOFLUSH?刷清輸出隊(duì)列
          ??*TCIOFLUSH?刷清輸入、輸出隊(duì)列
          例如:
          tcflush(fd,?TCIFLUSH)
          ;
          • (10) 激活配置,在完成配置后,需要激活配置使其生效。使用tcsetattr()函數(shù):
          int?tcsetarr(int?filedes,?const?struct?termios?*termptr);
          opt?指定在什么時(shí)候新的終端屬性才起作用,
          ???*TCSANOW:更改立即發(fā)生
          ???*TCSADRAIN:發(fā)送了所有輸出后更改才發(fā)生。若更改輸出參數(shù)則應(yīng)使用此選項(xiàng)
          ???*TCSAFLUSH:發(fā)送了所有輸出后更改才發(fā)生。更進(jìn)一步,在更改發(fā)生時(shí)未讀的
          ?????????????????所有輸入數(shù)據(jù)都被刪除(刷清)
          例如:tcsetatrr(fd, TCSANOW, &newtio);

          4.1.3. 使用流程

          • (1)打開串口,例如"/dev/ttySLB0"
          fd?=?open("/dev/ttySLB0",O_RDWR?|?O_NOCTTY?|?O_NDELAY);
          O_NOCTTY:是為了告訴Linux這個(gè)程序不會(huì)成為這個(gè)端口上的“控制終端”。如果不這樣做的話,所有的輸入,比如鍵盤上過來的Ctrl+C中止信號等等,會(huì)影響到你的進(jìn)程。
          O_NDELAY:這個(gè)標(biāo)志則是告訴Linux這個(gè)程序并不關(guān)心DCD信號線的狀態(tài),也就是不管串口是否有數(shù)據(jù)到來,都是非阻塞的,程序繼續(xù)執(zhí)行。
          • (2)恢復(fù)串口狀態(tài)為阻塞狀態(tài),用于等待串口數(shù)據(jù)的讀入,用fcntl函數(shù):
          fcntl(fd,F_SETFL,0);??//F_SETFL:設(shè)置文件flag為0,即默認(rèn),即阻塞狀態(tài)
          • (3)接著測試打開的文件描述符是否應(yīng)用一個(gè)終端設(shè)備,以進(jìn)一步確認(rèn)串口是否正確打開。
          isatty(STDIN_FILENO);
          • (4)讀寫串口
          串口的讀寫與普通文件一樣,使用read,write函數(shù)
          read(fd,?buf?,8);
          write(fd,buff,8);

          4.1.4. Demo

          以下給出一個(gè)測溫模塊收取數(shù)據(jù)的例子

          #include?
          #include?
          #include?
          #include?
          #include?
          #include?
          #include?
          #include?
          #include?

          #define?UART_DEVICE?????"/dev/ttySLB1"

          struct?temp?{
          ?float?temp_max1;
          ?float?temp_max2;
          ?float?temp_max3;
          ?float?temp_min;
          ?float?temp_mean;
          ?float?temp_enviromem;
          ?char?temp_col[1536];
          };

          int?main(void)
          {
          ?int?count,?i,?fd;
          ?struct?termios?oldtio,?newtio;
          ?struct?temp?*temp;
          ?temp?=?(struct?temp?*)malloc(sizeof(struct?temp));
          ?if?(!temp)?{
          ??printf("malloc?failed\n");
          ??return?-1;
          ?}

          ?char?cmd_buf1[]?=?{?0xAA,?0x01,?0x04,?0x00,?0x06,?0x10,?0x05,?0x00,?0xBB};
          ?char?cmd_buf2[]?=?{?0xAA,?0x01,?0x04,?0x00,?0x00,?0xA0,?0x00,?0x03,?0xBB};
          ?char?cmd_buf3[]?=?{?0xAA,?0x01,?0x04,?0x00,?0x03,?0x10,?0x01,?0x00,?0xBB};
          ?char?read_buf[2000];

          ?//-----------打開uart設(shè)備文件------------------
          ?fd?=?open(UART_DEVICE,?O_RDWR?|?O_NOCTTY);
          ?if?(fd?0)?{
          ??printf("Open?%s?failed\n",?UART_DEVICE);
          ??return?-1;
          ?}?else?{
          ??printf("Open?%s?successfully\n",?UART_DEVICE);
          ?}

          ?//-----------設(shè)置操作參數(shù)-----------------------
          ?tcgetattr(fd,?&oldtio);//獲取當(dāng)前操作模式參數(shù)
          ?memset(&newtio,?0,?sizeof(newtio));

          ?//波特率=230400?數(shù)據(jù)位=8?使能數(shù)據(jù)接收
          ?newtio.c_cflag?=?B230400?|?CS8?|?CLOCAL?|?CREAD?|?CSTOPB;
          ?newtio.c_iflag?=?IGNPAR;

          ?tcflush(fd,?TCIFLUSH);//清空輸入緩沖區(qū)和輸出緩沖區(qū)
          ?tcsetattr(fd,?TCSANOW,?&newtio);//設(shè)置新的操作參數(shù)

          ?//printf("input:?%s,?len?=?%d\n",?cmd_buf,?strlen(cmd_buf));
          ?//------------向urat發(fā)送數(shù)據(jù)-------------------

          ?for?(i?=?0;?i?9;?i++)
          ??printf("%#X?",?cmd_buf1[i]);

          ?count?=?write(fd,?cmd_buf1,?9);
          ?if?(count?!=?9)?{
          ??printf("send?failed\n");
          ??return?-1;
          ?}

          ?usleep(500000);

          ?memset(read_buf,?0,?sizeof(read_buf));
          ?count?=?read(fd,?read_buf,?sizeof(read_buf));
          ?if?(count?>?0)?{
          ??for?(i?=?0;?i???temp->temp_max1?=?read_buf[7]?<8?|?read_buf[6];
          ??temp->temp_max2?=?read_buf[9]?<8?|?read_buf[8];
          ??temp->temp_max3?=?read_buf[11]?<8?|?read_buf[10];
          ??temp->temp_min??=?read_buf[13]?<8?|?read_buf[12];
          ??temp->temp_mean?=?read_buf[15]?<8?|?read_buf[14];

          ??printf("temp->temp_max1?=?%f\n",?temp->temp_max1?*?0.01);
          ??printf("temp->temp_max2?=?%f\n",?temp->temp_max2?*?0.01);
          ??printf("temp->temp_max3?=?%f\n",?temp->temp_max3?*?0.01);
          ??printf("temp->temp_min??=?%f\n",?temp->temp_min??*?0.01);
          ??printf("temp->temp_mean?=?%f\n",?temp->temp_mean?*?0.01);
          ??

          ?}?else?{
          ??printf("read?temp?failed\n");
          ??return?-1;
          ?}

          ?count?=?write(fd,?cmd_buf3,?9);
          ?if?(count?!=?9)?{
          ??printf("send?failed\n");
          ??return?-1;
          ?}

          ?usleep(365);
          ?memset(read_buf,?0,?sizeof(read_buf));
          ?count?=?read(fd,?read_buf,?sizeof(read_buf));
          ?if?(count?>?0)?{
          ??for?(i?=?0;?i???temp->temp_enviromem?=?read_buf[7]?<8?|?read_buf[6];

          ??printf("temp->temp_enviromem?=?%f\n",?temp->temp_enviromem?*?0.01);?
          ?}?else?{
          ??printf("read?enviromem?failed\n");
          ??return?-1;
          ?}
          ??

          ?count?=?write(fd,?cmd_buf2,?9);
          ?if?(count?!=?9)?{
          ??printf("send?failed\n");
          ??return?-1;
          ?}

          ?usleep(70000);
          ?memset(read_buf,?0,?sizeof(read_buf));
          ?memset(temp->temp_col,?0,?sizeof(temp->temp_col));
          ?count?=?read(fd,?read_buf,?sizeof(read_buf));
          ?printf("count?=?%d\n",?count);
          ?if?(count?>?0)?{
          ??for?(i?=?0;?i?7;?i++)
          ??temp->temp_col[i]?=?read_buf[i+6];
          ??for?(i?=?0;?i?1536;?i++)
          ??{
          ???if?(!(i%10))
          ????printf("\n");
          ???printf("%#X?",?temp->temp_col[i]);
          ??}
          ?}?else?{
          ??printf("read?temp?colour?failed\n");
          ??return?-1;
          ?}
          ?free(temp);?

          ?close(fd);

          ?tcsetattr(fd,?TCSANOW,?&oldtio);?//恢復(fù)原先的設(shè)置

          ?return?0;
          }


          良許個(gè)人微信


          添加良許個(gè)人微信即送3套程序員必讀資料


          → 精選技術(shù)資料共享

          → 高手如云交流社群





          本公眾號全部博文已整理成一個(gè)目錄,請?jiān)诠娞柪锘貜?fù)「m」獲??!

          推薦閱讀:

          蘋果開源代碼中驚現(xiàn)“wechat”,老外注釋的吐槽亮了!

          Nginx 從入門到實(shí)踐,萬字詳解!

          17個(gè)在 Linux 運(yùn)維中定要掌握的實(shí)用技巧


          5T技術(shù)資源大放送!包括但不限于:C/C++,Linux,Python,Java,PHP,人工智能,單片機(jī),樹莓派,等等。在公眾號內(nèi)回復(fù)「1024」,即可免費(fèi)獲?。?!


          瀏覽 101
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  91九色porn蝌蚪 | 高清无码视频免费观看 | 俺来也在线视频 | 成人做爰A片免费看网站 | A片视频在线观看 |