溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Linux的tty架構(gòu)及UART驅(qū)動知識點有哪些

發(fā)布時間:2021-11-23 15:02:13 來源:億速云 閱讀:156 作者:iii 欄目:系統(tǒng)運維

這篇文章主要講解了“Linux的tty架構(gòu)及UART驅(qū)動知識點有哪些”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“Linux的tty架構(gòu)及UART驅(qū)動知識點有哪些”吧!

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

1.1. Uart介紹

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

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

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

1.1.1. 通信協(xié)議

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

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

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

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

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

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

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

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

Linux的tty架構(gòu)及UART驅(qū)動知識點有哪些

1.1.2. 波特率

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

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

1.1.3. 工作原理

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

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

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

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

UART的接收數(shù)據(jù)時序為:當(dāng)檢測到數(shù)據(jù)下降沿時,表明線路上有數(shù)據(jù)進行傳輸,這時計數(shù)器CNT開始計數(shù),當(dāng)計數(shù)器,當(dāng)計數(shù)器為8時,采樣的值為“0”表示開始位;當(dāng)計數(shù)器為24=161+8時,采樣的值為bit0數(shù)據(jù);當(dāng)計數(shù)器的值為40=162+8時,采樣的值為bit1數(shù)據(jù);依次類推,進行后面6個數(shù)據(jù)的采樣。如果需要進行奇偶校驗位,則當(dāng)計數(shù)器的值為152=169+8時,采樣的值為奇偶位;當(dāng)計數(shù)器的值為168=1610+8時,采樣的值為“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ū)動芯片,將CPU經(jīng)過UART傳送過來的電壓信號驅(qū)動成RS232或者RS485電平邏輯。

RS232使用3-15V有效電平,而UART,因為對電氣特性沒有規(guī)定,所以直接使用CPU使用的電平,即TTL電平(在0-3.3V之間)。

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

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

  • RS485 接口是采用平衡驅(qū)動器和差分接收器的組合,具有抑制共模干擾的能力,抗噪聲干擾性強。

  • RS232接口使用一根信號線和一根信號返回線而構(gòu)成供地的傳輸形式,這種共地傳輸容易產(chǎn)生共模干擾,所以抗噪聲干擾性弱。(2)傳輸距離

  • RS485 接口的最大傳輸距離標(biāo)準(zhǔn)值為1200 米(9600bps 時),實際上可達3000米。

  • RS232 傳輸距離有限,最大傳輸距離標(biāo)準(zhǔn)值為50米,實際上也只能用15米左右。(3)通信能力

  • RS485接口在總線上最多可以連接128個收發(fā)器,即具有多站能力,而這樣的用戶可以利用單一的RS485接口方便的建立起設(shè)備網(wǎng)絡(luò)。

  • RS232只允許一對一通信。(4)傳輸速率

  • RS232傳輸速率較低,在異步傳輸時,波特率為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 合并起來,分時復(fù)用。

  • RS232只允許一對一通信 (6)電氣電平值

  • 邏輯“1”以兩線間的電壓差為+(2-6)V表示;邏輯“0”以兩線間的電壓差為-(2-6)V表示。

  • 在RS232中任何一條信號的電壓均為負邏輯關(guān)系。即:邏輯“1”-5-15V;邏輯“0”,+5~+15V,噪聲容限為2V。即要求接收器能識別低至+3V的信號作為邏輯“0”,高到-3V的信號的信號作為邏輯“1”。

  • RS232接口的信號電平值較高,易損壞接口電路的芯片,又因為與TTL電平不兼容故使用電平轉(zhuǎn)換電路方能與TTL電路連接。

  • RS485接口信號電平比RS232降低了,就不易損壞接口電路的芯片,且該電平與TTL電平兼容,方便與TTL電路連接。

1.1.5. 流控

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

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

Linux的tty架構(gòu)及UART驅(qū)動知識點有哪些

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

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

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

RTS - 請求發(fā)送(數(shù)據(jù))(Request To  Send)低有效,此信號由本設(shè)備在需要發(fā)送數(shù)據(jù)給對端設(shè)備時設(shè)置。當(dāng)為低時,表示本設(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流控制分析,分析主機發(fā)送/接收流程:

物理連接

Linux的tty架構(gòu)及UART驅(qū)動知識點有哪些

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

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

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

Linux的tty架構(gòu)及UART驅(qū)動知識點有哪些

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

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

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

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

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

二、Linux serial框架

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

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

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

2.1.1. TTY概念

Linux的tty架構(gòu)及UART驅(qū)動知識點有哪些

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

Linux的tty架構(gòu)及UART驅(qū)動知識點有哪些

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

Linux的tty架構(gòu)及UART驅(qū)動知識點有哪些

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

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

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

Linux的tty架構(gòu)及UART驅(qū)動知識點有哪些

圖3.1tty架構(gòu)圖

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

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

Linux的tty架構(gòu)及UART驅(qū)動知識點有哪些

圖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ū)動名,主次設(shè)備號,串口控制臺(可選))等信息,還封裝了tty_driver  (底層串口驅(qū)動無需關(guān)心tty_driver)

struct uart_driver {         struct module    *owner; /*擁有該uart_driver的模塊,一般為THIS_MODULE*/         const char        *driver_name; /*驅(qū)動串口名,串口設(shè)備名以驅(qū)動名為基礎(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ū)動層*/ struct tty_driver  *tty_driver; /*tty相關(guān)*/

2.2.2. struct console

實現(xiàn)控制臺打印功能必須要注冊的結(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使用哪一個uart port (對應(yīng)的uart_port中的line),如果為-1,kernel會自動選擇第一個uart port*/       int   cflag;       void  *data;       struct   console *next; };

2.2.3. struct uart_state

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

用戶空間程序通過串口發(fā)送數(shù)據(jù)時,上層驅(qū)動將用戶數(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)于一個串口設(shè)備*/ };

2.2.4. struct uart_port

uart_port用于描述串口端口的I/O端口或I/O內(nèi)存地址、FIFO大小、端口類型、串口時鐘等信息。實際上,一個uart_port實現(xiàn)對應(yīng)一個串口設(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 << 1)) #define UPF_SAK                 ((__force upf_t) (1 << 2)) #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 << 6)) #define UPF_AUTO_IRQ            ((__force upf_t) (1 << 7)) #define UPF_HARDPPS_CD          ((__force upf_t) (1 << 11)) #define UPF_LOW_LATENCY         ((__force upf_t) (1 << 13)) #define UPF_BUGGY_UART          ((__force upf_t) (1 << 14)) #define UPF_NO_TXEN_TEST        ((__force upf_t) (1 << 15)) #define UPF_MAGIC_MULTIPLIER    ((__force upf_t) (1 << 16)) /* Port has hardware-assisted h/w flow control (iow, auto-RTS *not* auto-CTS) */ #define UPF_HARD_FLOW           ((__force upf_t) (1 << 21)) /* Port has hardware-assisted s/w flow control */ #define UPF_SOFT_FLOW           ((__force upf_t) (1 << 22)) #define UPF_CONS_FLOW           ((__force upf_t) (1 << 23)) #define UPF_SHARE_IRQ           ((__force upf_t) (1 << 24)) #define UPF_EXAR_EFR            ((__force upf_t) (1 << 25)) #define UPF_BUG_THRE            ((__force upf_t) (1 << 26)) /* The exact UART type is known and should not be probed.  */ #define UPF_FIXED_TYPE          ((__force upf_t) (1 << 27)) #define UPF_BOOT_AUTOCONF       ((__force upf_t) (1 << 28)) #define UPF_FIXED_PORT          ((__force upf_t) (1 << 29)) #define UPF_DEAD                ((__force upf_t) (1 << 30)) #define UPF_IOREMAP             ((__force upf_t) (1 << 31))  #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ū)動可對串口的所有操作

 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. 注冊流程

Linux的tty架構(gòu)及UART驅(qū)動知識點有哪些

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

Linux的tty架構(gòu)及UART驅(qū)動知識點有哪些

圖3.3uart driver注冊流程

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

1、根據(jù)driver支持的最大設(shè)備數(shù),申請n個uart_state空間,每一個uart_state都有一個uart_port。

2、分配一個tty_driver,并將uart_driver->tty_driver指向它。

3、對tty_driver進行設(shè)置,其中包括默認波特率、檢驗方式等,還有一個重要的ops,結(jié)構(gòu)體tty_operation的注冊,它是tty核心與串口驅(qū)動通信的接口。

4、初始化每一個uart_state的tty_port;

5、注冊tty_driver。注冊uart_driver實際上是注冊tty_driver,與用戶空間打交道的工作完全交給tty_driver,這一部分是內(nèi)核實現(xiàn)好的不需要修改

Linux的tty架構(gòu)及UART驅(qū)動知識點有哪些

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

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

Linux的tty架構(gòu)及UART驅(qū)動知識點有哪些

圖3-4 uart添加port流程圖

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

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

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

Linux的tty架構(gòu)及UART驅(qū)動知識點有哪些

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

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

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

Linux的tty架構(gòu)及UART驅(qū)動知識點有哪些

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

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

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

Linux的tty架構(gòu)及UART驅(qū)動知識點有哪些

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

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

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

Linux的tty架構(gòu)及UART驅(qū)動知識點有哪些

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

2.4.5. 注銷流程

Linux的tty架構(gòu)及UART驅(qū)動知識點有哪些

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

Linux的tty架構(gòu)及UART驅(qū)動知識點有哪些

圖3.9  uart移除port流程圖

Linux的tty架構(gòu)及UART驅(qū)動知識點有哪些

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

Linux的tty架構(gòu)及UART驅(qū)動知識點有哪些

2.5. 使用rs485通信

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

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

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

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

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

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

  • a,保證uart tx/rx功能正常。

  • b,用gpio-output來控制 de/re 相關(guān)的2個gpio,觀察 de/re的gpio輸出low/high是否正常

  • c,在b的基礎(chǔ)上,單獨調(diào)試 rs485-tx/rs485-rx,單端調(diào)試是否pass.

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

  • a, 默認re-en, de-dis,默認rs485-rx

  • b, 當(dāng)要發(fā)送的時候,re-dis, de-enable, 然后uart-tx.

  • c, tx完成之后,de-dis; re-en,進入默認的rs485-rx模式。

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

  • a, re-en,進入rs485-rx模式 (re 通常是低電平有效,這一步就是 設(shè)置 re對應(yīng)的gpio為低電平)

  • b, 當(dāng)要發(fā)送的時候,設(shè)置gpio:re-disable, de-enable, 然后uart-tx.(re 通常是低電平有效,這一步就是 設(shè)置  re對應(yīng)的gpio為高電平)

  • c, tx完成之后,de-disable; re-enable,進入默認的rs485-rx模式。(re 通常是低電平有效,這一步就是 設(shè)置  re對應(yīng)的gpio為低電平)

模式3rs485-software-halfduplex(de/re 獨立輸出) (使能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, 默認為rs485-rx模式,設(shè)置 de-dis/re-en

  • d, 當(dāng)要tx的時候,設(shè)置 de-en/re-dis

  • e, 發(fā)送完成,設(shè)置 de-dis/re-en

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

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

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

Linux的tty架構(gòu)及UART驅(qū)動知識點有哪些

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

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

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

3.1.1. uart_register_driver

/*功能:  uart_register_driver用于串口驅(qū)動uart_driver注冊到內(nèi)核(串口核心層)中,通常在模塊初始化函數(shù)調(diào)用該函數(shù)。  *參數(shù):drv:要注冊的uart_driver  *返回值:成功,返回0;否則返回錯誤碼  */ 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,否則返回錯誤碼  */ void uart_unregister_driver(struct uart_driver *drv)

3.1.3. uart_add_one_port

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

3.1.4. uart_remove_one_port

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

3.1.5. uart_write_wakeup

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

3.1.6. uart_suspend_port

/*功能:uart_suspend_port用于掛起特定的串口端口  *參數(shù):  *     drv:要掛起的串口端口鎖所屬的串口驅(qū)動  *     port:要掛起的串口端口  *返回值:成功返回0;否則返回錯誤碼  */ 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ū)動  *     port:要恢復(fù)的串口端口  *返回值:成功返回0;否則返回錯誤碼  */ 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 用于計算某一波特率的串口時鐘分頻數(shù)(串口波特率除數(shù))  *參數(shù):  *     port:要計算分頻數(shù)的串口端口  *     baud:期望的波特率  *返回值:串口時鐘分頻數(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ù):  *     port:要更新超時間的串口端口  *     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層插入一個字符  *參數(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用于向串口端口寫一控制臺信息  *參數(shù):  *     port:要寫信息的串口端口  *     s:要寫的信息  *     count:信息的大小  *     putchar:用于向串口端口寫字符的函數(shù),該函數(shù)有兩個參數(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ù)

Linux的tty架構(gòu)及UART驅(qū)動知識點有哪些

4.1.2. 串口配置流程

(1) 保持原先串口配置,使用tegetatrr(fd, &oldtio);

struct termious newtio, oldtio; tegetattr(fd, &oldtio);

(2) 激活選項有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實現(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) 奇偶檢驗位設(shè)置,使用c_cflag和c_ifag. 設(shè)置奇校驗

newtio.c_cflag |= PARENB; newtio.c_cflag |= PARODD; newtio.c_iflag |= (INPCK | ISTRIP);

設(shè)置偶校驗

newtio.c_iflag |= (INPCK | ISTRIP); newtio.c_cflag |= PARENB; newtio.c_cflag |= ~PARODD;

(8) 設(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)是下列三個常數(shù)之一:   *TCIFLUSH 刷清輸入隊列   *TCOFLUSH 刷清輸出隊列   *TCIOFLUSH 刷清輸入、輸出隊列 例如: tcflush(fd, TCIFLUSH);

(10) 激活配置,在完成配置后,需要激活配置使其生效。使用tcsetattr()函數(shù):

int tcsetarr(int filedes, const struct termios *termptr); opt 指定在什么時候新的終端屬性才起作用,    *TCSANOW:更改立即發(fā)生    *TCSADRAIN:發(fā)送了所有輸出后更改才發(fā)生。若更改輸出參數(shù)則應(yīng)使用此選項    *TCSAFLUSH:發(fā)送了所有輸出后更改才發(fā)生。更進一步,在更改發(fā)生時未讀的                  所有輸入數(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這個程序不會成為這個端口上的“控制終端”。如果不這樣做的話,所有的輸入,比如鍵盤上過來的Ctrl+C中止信號等等,會影響到你的進程。 O_NDELAY:這個標(biāo)志則是告訴Linux這個程序并不關(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,即默認,即阻塞狀態(tài)

(3)接著測試打開的文件描述符是否應(yīng)用一個終端設(shè)備,以進一步確認串口是否正確打開。

isatty(STDIN_FILENO);

(4)讀寫串口

串口的讀寫與普通文件一樣,使用read,write函數(shù) read(fd, buf ,8); write(fd,buff,8);

4.1.4. Demo

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

#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <termios.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <log/log.h> #include <stdlib.h>  #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 < count; 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 < count; 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 < count - 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; }

感謝各位的閱讀,以上就是“Linux的tty架構(gòu)及UART驅(qū)動知識點有哪些”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對Linux的tty架構(gòu)及UART驅(qū)動知識點有哪些這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI