久久精品人人爽,华人av在线,亚洲性视频网站,欧美专区一二三

Linux的tty架構(gòu)及UART驅(qū)動(dòng)知識(shí)點(diǎn)有哪些

172次閱讀
沒有評論

共計(jì) 22483 個(gè)字符,預(yù)計(jì)需要花費(fèi) 57 分鐘才能閱讀完成。

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

一、模塊硬件學(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 位、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)榈碗娢? 時(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)榕c TTL 電平不兼容故使用電平轉(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ù) / 停止)。

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

DTR ndash; 數(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ī)接收中止。

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

一般通過 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 概念

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

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

當(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   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ū)動(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. 注冊流程

此接口在 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)好的不需要修改

此接口用于注冊一個(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. 注銷流程

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

圖 3.9  uart 移除 port 流程圖

此接口在 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ù) lsquo;0 rsquo; 時(shí),由于 DI 口連接地,此時(shí)數(shù)據(jù) lsquo;0 rsquo; 就會(huì)傳輸?shù)?AB 口  A-B 0, 傳輸 lsquo;0 rsquo;,完成了低電平的傳輸。當(dāng)發(fā)送 lsquo;1 rsquo; 時(shí),此時(shí)三極管導(dǎo)通,按理說 RO 使能,此時(shí)由于還處在發(fā)送數(shù)據(jù)中,這種狀態(tài)下 485 處于高阻態(tài),此時(shí)的狀態(tài)通過 A 上拉 B 下拉電阻決定,此時(shí) A -B 0 傳輸 lsquo;1 rsquo;,完成高電平的傳輸。

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ù)

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  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ū)動(dòng)知識(shí)點(diǎn)有哪些”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對 Linux 的 tty 架構(gòu)及 UART 驅(qū)動(dòng)知識(shí)點(diǎn)有哪些這一問題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是丸趣 TV,丸趣 TV 小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

正文完
 
丸趣
版權(quán)聲明:本站原創(chuàng)文章,由 丸趣 2023-08-25發(fā)表,共計(jì)22483字。
轉(zhuǎn)載說明:除特殊說明外本站除技術(shù)相關(guān)以外文章皆由網(wǎng)絡(luò)搜集發(fā)布,轉(zhuǎn)載請注明出處。
評論(沒有評論)
主站蜘蛛池模板: 百色市| 辽阳市| 于田县| 长丰县| 漾濞| 松阳县| 射洪县| 金昌市| 定南县| 抚松县| 星座| 广饶县| 盐池县| 邵阳市| 托里县| 衡东县| 青海省| 永州市| 高平市| 渑池县| 石柱| 牡丹江市| 阿坝县| 德令哈市| 朝阳市| 顺昌县| 南城县| 隆昌县| 通海县| 民乐县| 永善县| 禄丰县| 德格县| 温泉县| 镇巴县| 德江县| 盐边县| 东莞市| 故城县| 宁陕县| 莲花县|