桂宇琛李彥梅劉昊天郭 玉
(1.安慶師范大學物理與電氣工程學院;2.安慶師范大學數學與計算科學學院 安徽安慶 246133)
隨著科技的發展,嵌入式產品越來越豐富,從小孩的玩具,到航空科技,隨處可見嵌入式設備。而對于嵌入式系統,目前世界上使用廣泛的就是linux系統[1]。由于其自由,開源,Linux是使用用戶最多的操作系統,它也是一個強大的多用戶、多任務操作系統,支持多種處理器架構,具有可靠的系統安和良好的可移植性,所以成為了嵌入式開發系統的首選。
一個嵌入式產品分為硬件和軟件兩個部分[2],而其主要架構如下圖 1所示:應用層主要是軟件,硬件層是外接的器件。在下圖中設備驅動層是具體硬件相關的實現,也是驅動開發中主要完成的部分。輸入核心層主要提供一些API供設備驅動層調用,通過這些API設備驅動層上報的數據就可以傳遞到事件處理層。事件處理層負責創建設備文件以及將上報的事件傳遞到用戶空間。

圖1 嵌入式產品的架構圖
對于一個嵌入式產品,硬件也是必不可少的部分,操作系統為了去驅動硬件,就需要將驅動硬件的方法融入內核中[3]。為了達到融合,設備驅動中就需要設計面向操作系統的接口。不同的操作系統,接口的格式就不同,這些接口由操作系統規定。本文使用Linux3.14內核的操作系統,針對i2C總線驅動編寫,做了一個詳細的介紹。
i2C通信協議相對于SPI和UART通信方式,有這占用接口少,速度快的特點。SPI通信需要四條線才能實現和CPU芯片通信,也就是占用了CPU芯片四個引腳。而UART通信方式雖然也只占兩條線,但是他沒有時序作為參考,通信的速度不能太快,否則會增加出錯的概率,所以為了兼顧速度和所占用的資源,i2C通信方式無疑是最好的選擇。所以i2C通信在嵌入式中應用廣泛,很多傳感器都會選擇i2C通信協議實現和CPU芯片實現數據交互。比如我們使用的MPU6050和溫度傳感器ML75等。
操作系統為了使用這些傳感器就需要編寫驅動程序[4]。考慮到傳感器設備是基于i2C總線而寫的,而不是直接使用platform總線,所以需要i2C控制器驅動和傳感器設備驅動同時工作。傳感器的驅動基本都是一樣,但是CPU芯片由于生產的公司不同,設計的i2C控制器就不同,這就導致了一個問題。每使用一個公司的芯片,就需要去編寫一次驅動,而很多工作重復性很高,為了提高效率,也就是考慮到移植問題,提出了一種i2C子系統的編寫模式,將i2C控制器驅動和從設備驅動分離,以提高驅動的移植性。下圖2描述了i2C子系統的設計框架和原理。

圖2 i2C子系統的設計框架和原理
從上圖可以看出將i2C控制器驅動器和從設備驅動分離,而linux內核為了使i2C控制器驅動和從設備驅動能夠更好的匹配,設定了一個標準,就是在I2C控制器和從設備驅動之間加入了一個標準的函數接口層i2c_core.c。i2C控制器由芯片生產商提供,不同的廠家可以不同,但是必須要提供統一的接口給從設備調用,這樣就規范了i2C子系統的驅動編寫,給工程師的使用帶來了便利[5]。從上面的框架圖中可以看出,linux不僅規范了i2C控制器驅動的編寫,在linux內核中i2c-dev.c已經實現實現好了通用的IIC從設備驅動,所以我們編寫的驅動可以使用linux提供的通用的i2C控制器驅動,我們也可以自定義去編寫。下面就詳細的闡述從這兩個方面去編寫i2C子系統驅動。
i2C通用驅動會為SOC芯片上的每一個i2C控制器生成一個設備號為89的設備節點,用戶空間可以通過i2c設備節點,訪問i2c控制器。每個i2c控制器的編號從0開始,對應i2c設備文件的次設備號[6]。使用通用的i2C控制器驅動,首先就要配置linux內核,linux3.14版本通用的驅動在文件i2c-dev.c,我們要將該文件編譯進內核。i2c-dev.c實現的從設備驅動中,并不包括實際的從設備操作方法。它只是在提供給應用層一個設備文件,通過這個設備文件就可以找到特定的i2c控制器設備,。而i2c-dev.c的通用從設備驅動操作i2c從設備分為下面三個步驟:
1.首先確定從設備是由哪一個i2c控制器控制。
2.通過i2c控制器的設備文件,找到i2c控制器。
3.我么將i2c從設備的信息,發送給i2c控制器,然后i2c控制器收到從設備的信息后,解析從設備的信息,從而發出i2c總線時序,這樣就可以和i2c從設備通信。
而在應用層對應硬件信息的描述,linux定義了一個特定的結構體i2c_msg,用于描述i2c從設備的硬件信息,然后調用ioctrl函數將找個結構體傳遞到i2c控制器,控制器會進行解析,然后做出相應的應答。
從上面的分析可知,linux提供的通用I2C控制器驅動,為了達到通用的效果,linux內核提供的i2C控制器驅動就沒有具體的硬件信息,僅僅是封裝了一些函數通用的函數,如open,read,write,ioctl。應用層的工程師需要在應用層填寫從設備的硬件信息,然后通過函數去調用底層驅動接口。雖然這樣做很便捷,但是給應用層的工程師帶來了開發的難度,他們不僅要了解應用層的開發知識,還需要知道硬件的知識,去了解電路圖和芯片手冊。
自定義從設備驅動,就是將從設備的驅動封裝在底層,然后提供一些應用工程師熟悉的函數接口,應用層不需要管是什么從設備,不用關心i2C總線上接的什么傳感器,他們只需要調用相應的函數就可以了,這樣就給應用層的開發帶來了便利。
i2c從設備驅動是基于i2c總線而編寫的,對于總線編寫驅動的原理,我們使用“總線”“設備”“驅動”的框架去闡述[7]。arm芯片為了提高驅動的移植性,提出了AMBA總線的標準,工程師根據這個標準去編寫驅動,而不是根據自己去定義,這些總線都是標準的,所有的SOC芯片都是一樣的。外圍設備按照速度的不同掛載在SOC內部的不同速度總線上。如表1所示列出了部分總線:

表1 總線分類表
同樣為了提高移植性,驅動和設備分離,驅動中不包含硬件信息,而在設備描述硬件信息。總線會維護兩條鏈表,分別管理設備和驅動,當一個設備被注冊到總線上的時候,總線會根據其名字搜索對應的驅動,如果找到就將設備信息導入驅動程序并執行驅動;當一個驅動被注冊到平臺總線的時候,總線也會搜索設備。總之,平臺總線負責將設備信息和驅動代碼匹配,這樣就可以做到驅動和設備信息的分離[8]。注冊設備的流程圖和注冊驅動的流程圖如下圖所示:

圖3 注冊設備的流程圖
注冊設備的時候,一旦注冊進內核,內核就會根據設備的名字去找同名的驅動,如本文設定的設備名字為led,那么內核就會去驅動鏈表中找led名字的驅動,發現目標匹配成功后就會調用驅動的probe函數。這個函數的作用就是獲取匹配后的硬件資源并注冊字符設備。同樣,在注冊驅動的時候,內核也會根據這個驅動的名字led去設備鏈表上同名的設備。同樣在匹配成功后調用probe函數。

圖4 注冊驅動的流程圖
驅動和設備文件是通過文件進行匹配,也就是如果要想設備和驅動最終能夠互相匹配,就必須將設備的名字和驅動的名字設置為一樣的。若驅動提供了id_table,則那設備名和id_table進行比較。若驅動沒有提供id_table,則直接使用驅動名和設備名進行匹配。
本文基于linux3.14內核,分析了i2c子系統的原理和實現框架,最后將驅動移植到硬件平臺上,并且編寫測試程序,成功讀取了MUP6050里關于加速度的相關參數。
[1]宋寶華.Linux設備驅動開發詳解[M].北京:人民郵電出版社,2010.
[2]胡祖寶,董國通.基于S3C2440的嵌入式Linux內核移植及字符設備驅動開發[J].工業控制計算機,2015,28(12).
[3]Dong Yu Zhang.Design of Touch Screen Driver Based on Linux[J].KeyEngineeringMaterials,2011,1104(467):818-822.
[4]王新勇.基于ARM的Linux驅動開發研究[D].南昌:江西理工大學,2011.
[5]XuDongChen,LingChengKong,ZhiHuaZhang,DanWang,Tao Mei.Design of USB Interface Driver for WSNs Node Tester Based on Embedded Linux[J].Applied Mechanics and Materials,2011,1069(40):266-271.
[6]Peter H.Welch,Herman W.Roebbers,Jan F.Broenink,Frederick R.M.Barnes,Carl G.Ritson,Adam T.Sampson,Gardiner S.Stiles,Brian Vinter,Arjen Klomp,Herman Roebbers,Ruud Derwig,Leon Bouwmeester.Designing a Mathematically Verified ICDeviceDriverUsingASD[M].IOSPress:2009.
[7]王巖,王子牛.嵌入式Linux設備驅動程序開發[J].貴州工業大學學報(自然科學版),2008,37(1).
[8]汪海兵.嵌入式Linxu的研究與應用[D].昆明:昆明理工大學,2002.