如何編寫串列埠 uart 驅動 基於linux310

2021-06-26 16:02:00 字數 3545 閱讀 9297

本篇在介紹linux下串列埠驅動程式編寫,為了讓讓讀者能夠明白串列埠驅動如何編寫、為什麼這麼編寫,本文講述了串列埠驅動編寫的流程,此外還介紹了編寫的串列埠驅動在核心態下是如何被使用的,此外還介紹了其和應用的互動等。

在介紹串列埠驅動的編寫時,按照先介紹其在linux作業系統下的架構,然後重點介紹了其在核心下的資料結構的組織結構,在介紹組織結構的同時也介紹了uart驅動是如何和核心的一些底層構建互動的,最後介紹了串列埠的讀寫的執行流程,從最後的執行流程中能夠看到編寫的串列埠驅動是如何被核心的構建呼叫的。

首先來看一張圖,該圖顯示了從應用程式到驅動底層的過程,在這張圖即為linux作業系統的uart整個架構,在該圖的右側紅色方框內有三個資料結構,uart_driver是uart驅動的描述,串列埠驅動的核心實際上是tty,所以這裡當註冊該驅動時,該註冊函式內部會構建乙個tty結構體,來表示該串列埠驅動。uart_port對應的是乙個埠的表示,這正如同pc端的cm0、cm1等等

uart_ops是該埠可用的操作集,串列埠驅動的主要工作就是集中在這裡,該操作集將實際操作底層的硬體裝置。

有了上面的概念之後,下面來看看核心中串列埠的資料結構是如何組織的。

這張圖涵蓋了3.10核心下uart涉及的相關資料結構,和相關的函式操作集,編寫乙個串列埠驅動,圖中三個黃色的部分即為需要實現的部分,很有可能,串列埠驅動編寫人員還會實現深黃色部分的,有了整體架構和整個資料結構流圖,下面我們一步步講述串列埠驅動的工作過程,非常感謝核心驅動的編寫者,讓串列埠編寫的工作變得如此簡單。

上圖中虛線左邊的黃色部分uart_driver代表乙個串列埠驅動,會呼叫uart_register_driver對該串列埠進行註冊,該欄位需要的相關成員需要自己實現;

static struct uart_driver serial8250_reg = ;

註冊uart串列埠驅動,可以發現,後邊會呼叫tty相關的一些東西,其實在核心中,串列埠實際上是乙個tty裝置,但是由於串列埠的通用性,核心工作著將串列埠的底層封裝在了tty_driver裡了,所以在串列埠註冊函式uart_register_driver,通常我們能看見alloc_tty_driver函式申請struct tty_driver乙個結構的變數,並用uart_driver的相關的成員對其初始化,這時uart_driver的結構體會變成tt_driver的結構體,接著呼叫tty_register_driver註冊上圖中的tty_driver結構體表示的tty驅動,這些驅動工作者已經為我們實現了,通常我們並不關心uart_drvier的註冊過程。

在上圖中的tty_driver的*op欄位中,可以看到對(*open)成員,被賦值為uart_open,其它的函式指標被類似的賦值了,值得一說的是這裡的uart_open抑或uart_ioctl也好,這些核心工作者為我們編寫好了,它們最終會呼叫我們編寫的埠驅動,這個過程在讀寫流程中有敘述。

乙個uart驅動註冊好了以後,下面就要將對應的port和其進行繫結了,繫結之前,需要初始化uart_port,設定其相關的成員,比如終端號,*ops等,這裡的ops非常重要,在應用程式呼叫open、read等操作,到系統呼叫後,首先呼叫uart_ops,然後會呼叫這裡的埠的ops,在soc情況下,這裡ops一般是對片上系統的串列埠控制器暫存器相關的設定,比如工作模式等等,這和資料手冊和體系結構關係非常大,不同的晶元的位址範圍和控制方法以及串列埠能力都有較大的差別。

來看一看port新增到驅動函式的原型,該函式有兩個引數,其中乙個是uart_driver,這個就是就是上面呼叫uart_register_driver註冊的串列埠驅動,第二個引數就是這裡的uart_port了,int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport),這裡我們來看看8250的新增方法。

serial8250_register_ports(struct uart_driver *drv, struct device *dev)

這裡做了簡化,可以看到,serial8250_ports結構體,該結構體對應上面圖中的深黃色部分,一般soc下也會建立該結構體,這樣方便管理,但是該結構的內容獲得的方法使用的是devicetree方法,有點區別。上圖中紅色1的線標識了這一過程。

uart_add_one_port函式我們這裡走深一點看看,該函式呼叫uart_configure_port配置埠,註冊的實際工作呼叫tty_port_register_device_attr()完成,該函式又呼叫tty_register_device_attr(),該函式將tty裝置註冊到系統中,如果有sysfs檔案系統,則udev機制會建立裝置節點。

open操作,讀寫操作前會呼叫open開啟串列埠,open時

1、根據tty的driver_state欄位找到uart_driver,

2、根據uart_driver以及tty的index欄位,index表示的埠號,找到對應的uart_state,tty的index在tty_open時由tty_init_dev呼叫initialize_tty_struct設定

3、uart_state儲存在tty和uart_port中

4、tty_driver的ports欄位指向tty_port中

5、呼叫uart_start_up進一步完成uart的初始化,以使得串列埠可用

read操作:

1、對裝置來說就是接收資料,使用中斷方法,在start_up()函式中,即open乙個埠時會註冊接收資料的中斷服務函式,這樣可以最大限度的節約中斷資源。註冊中斷的函式類似於下面:

2、reques_irq(port->irq,serial_8250_irq,...),當soc裝置的串列埠有資料來時,產生中斷,中斷服務程式判斷是接收資料中斷標誌,那麼呼叫uart_insert_char將資料放入tty的快取中

3、呼叫tty_insert_flip_char/tty_flip_buffer_push將資料刷入到line discipline中,後tty讀取line discipline返回給使用者

write操作:

1、uart_write將資料放入circ_buf中,函式呼叫的過程write=>uart_write()(tty_driver)=>port->ops->start_tx();

2、uart_start,即是使用者編寫的傳送驅動程式。

其它操作(設定波特率等):

呼叫iotcl方法完成,修改 **termios;字段完成。

串列埠驅動編寫流程:

1、編寫乙個uart_driver結構體,並呼叫uart_register_driver註冊該驅動到tty核心。

2、編寫my_uart_port和uart_ops

3、編寫 platform_driver,然後解析裝置樹,獲得串列埠的相關資訊,關於devicetree可以參看《linux啟動那些事-基於310》然後呼叫platform_device_register註冊該soc下的平台裝置,在platform_driver中的probe呼叫前述的 uart_add_one_port完成埠的新增工作,正確返回後串列埠驅動可以正常工作了。

4、可以參考drivers/tty/serial目錄下已有的一些驅動。

基於FPGA的UART串列埠通訊

通過串列埠除錯助手pc傳送16bit給fpga,fpga接收後返還給pc。串列埠通訊其實沒我們想象中的那麼難,只要花點時間去理解,很快就會上手,在直入正題前,先來一點基礎知識。串列埠是指傳送和接收資料的序列口,就是我們開啟電腦裝置管理器後看到的com口。fpga或微控制器的板子中很多都用到的是uar...

S3C2440 UART串列埠驅動

通用非同步接收器和傳送器 universal asynchronous receiver and transmitter 簡稱uart。通常是嵌入式裝置中預設都會配置的通訊介面。這是因為,很多嵌入式裝置沒有顯示屏,無法獲得嵌入式裝置實時資料資訊,通過uart串列埠和超級終端相連,列印嵌入式裝置輸出資...

STM32 UART串列埠驅動程式

示例1.通過uart1進行資料傳送 uart 1 的初始化 brief uart1 initialise.param none.retval none.void uart1 init void 將資料0xba通過uart1傳送出 usart senddata usart1,0xba send dat...