0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

關(guān)于嵌入式驅(qū)動(dòng)開(kāi)發(fā)的4大方向

電子設(shè)計(jì) ? 來(lái)源:電子設(shè)計(jì) ? 作者:電子設(shè)計(jì) ? 2020-10-30 11:48 ? 次閱讀

01
嵌入式驅(qū)動(dòng)開(kāi)發(fā)到底學(xué)什么

嵌入式大體分為以下四個(gè)方向:

一、嵌入式硬件開(kāi)發(fā):熟悉電路等知識(shí),非常熟悉各種常用元器件,掌握模擬電路和數(shù)字電路設(shè)計(jì)的開(kāi)發(fā)能力。熟練掌握嵌入式硬件知識(shí),熟悉硬件開(kāi)發(fā)模式和設(shè)計(jì)模式,熟悉 ARM32 位處理器嵌入式硬件平臺(tái)開(kāi)發(fā)、并具備產(chǎn)品開(kāi)發(fā)經(jīng)驗(yàn)。精通常用的硬件設(shè)計(jì)工具:Protel/PADS(PowerPCB)/Cadence/OrCad。一般需要有 4~8 層高速 PCB 設(shè)計(jì)經(jīng)驗(yàn)。

二、嵌入式驅(qū)動(dòng)開(kāi)發(fā):熟練掌握 Linux 操作系統(tǒng)、系統(tǒng)結(jié)構(gòu)、計(jì)算機(jī)組成原理、數(shù)據(jù)結(jié)構(gòu)相關(guān)知識(shí)。熟悉嵌入式 ARM 開(kāi)發(fā),至少掌握 Linux 字符驅(qū)動(dòng)程序開(kāi)發(fā)。具有單片機(jī)、ARM 嵌入式處理器的移植開(kāi)發(fā)能力,理解硬件原理圖,能獨(dú)立完成相關(guān)硬件驅(qū)動(dòng)調(diào)試,具有扎實(shí)的硬件知識(shí),能夠根據(jù)芯片手冊(cè)編寫(xiě)軟件驅(qū)動(dòng)程序。

三、嵌入式系統(tǒng)開(kāi)發(fā):掌握 Linux 系統(tǒng)配置,精通處理器體系結(jié)構(gòu)、編程環(huán)境、指令集、尋址方式、調(diào)試、匯編和混合編程等方面的內(nèi)容;掌握 Linux 文件系統(tǒng)制作,熟悉各種文件系統(tǒng)格式(YAFFS2、JAFFS2、RAMDISK 等);熟悉嵌入式 Linux 啟動(dòng)流程,熟悉 Linux 配置文件的修改;掌握內(nèi)核裁減、內(nèi)核移植、交叉編譯、內(nèi)核調(diào)試、啟動(dòng)程序 Bootloader 編寫(xiě)、根文件系統(tǒng)制作和集成部署 Linux 系統(tǒng)等整個(gè)流程;、熟悉搭建 Linux 軟件開(kāi)發(fā)環(huán)境(庫(kù)文件的交叉編譯及環(huán)境配置等);

四、嵌入式軟件開(kāi)發(fā):精通 Linux 操作系統(tǒng)的概念和安裝方法、Linux 下的基本命令、管理配置和編輯器,包括 VI 編輯器,GCC 編譯器,GDB 調(diào)試器和 Make 項(xiàng)目管理工具等知識(shí);精通 C 語(yǔ)言的高級(jí)編程知識(shí),包括函數(shù)與程序結(jié)構(gòu)、指針、數(shù)組、常用算法、庫(kù)函數(shù)的使用等知識(shí)、數(shù)據(jù)結(jié)構(gòu)的基礎(chǔ)內(nèi)容,包括鏈表、隊(duì)列等;掌握面向?qū)ο缶幊痰幕舅枷?,以?C++語(yǔ)言的基礎(chǔ)內(nèi)容;精通嵌入式 Linux 下的程序設(shè)計(jì),精通嵌入式 Linux 開(kāi)發(fā)環(huán)境,包括系統(tǒng)編程、文件 I/O、多進(jìn)程和多線程、網(wǎng)絡(luò)編程、GUI 圖形界面編程、數(shù)據(jù)庫(kù);熟悉常用的圖形庫(kù)的編程,如 QT、GTK、miniGUI、fltk、nano-x 等。

公司的日?;顒?dòng)還是看公司的規(guī)模,大一點(diǎn)的一般只是讓你負(fù)責(zé)一個(gè)模塊,這樣你就要精通一點(diǎn)。若是公司比較小的話估計(jì)要你什么都做一點(diǎn)。還要了解點(diǎn)硬件的東西。

那么看了這么多,嵌入式和純軟最大的區(qū)別在于:

純軟學(xué)習(xí)的是一門(mén)語(yǔ)言,例如 C,C++,java,甚至 Python,語(yǔ)言說(shuō)到底只是一門(mén)工具,就像學(xué)會(huì)英語(yǔ)法語(yǔ)日語(yǔ)一樣。

但嵌入式學(xué)習(xí)的是軟件+硬件,通俗的講,它學(xué)的是做系統(tǒng)做產(chǎn)品,講究的是除了具體的語(yǔ)言工具,更多的是如何將一個(gè)產(chǎn)品分解為具體可實(shí)施的軟件和硬件,以及更小的單元。

不少人問(wèn),將來(lái)就業(yè)到底是選驅(qū)動(dòng)還是選應(yīng)用?只能說(shuō)憑興趣,并且驅(qū)動(dòng)和應(yīng)用并不是截然分開(kāi)的。

▍PART 01

我們說(shuō)的驅(qū)動(dòng),其實(shí)并不局限于硬件的操作,還有操作系統(tǒng)的原理、進(jìn)程的休眠喚醒調(diào)度等概念。想寫(xiě)出一個(gè)好的應(yīng)用,想比較好的解決應(yīng)用碰到的問(wèn)題,這些知識(shí)大家應(yīng)該都懂。

▍PART 02

做應(yīng)用的發(fā)展路徑個(gè)人認(rèn)為就是業(yè)務(wù)純熟。比如在通信行業(yè)、IPTV 行業(yè)、手機(jī)行業(yè),行業(yè)需求很了解。

▍PART 03

做驅(qū)動(dòng),其實(shí)不能稱為“做驅(qū)動(dòng)”,而是可以稱為“做底層系統(tǒng)”,做好了這是通殺各行業(yè)。比如一個(gè)人工作幾年,做過(guò)手機(jī)、IPTV、會(huì)議電視,但是這些產(chǎn)品對(duì)他毫無(wú)差別,因?yàn)樗蛔龅讓?。?dāng)應(yīng)用出現(xiàn)問(wèn)題,解決不了時(shí),他就可以從內(nèi)核角度給他們出主意,提供工具。做底層的發(fā)展方向,應(yīng)該是技術(shù)專家。

▍PART 04

其實(shí),做底層還是做應(yīng)用,之間并沒(méi)有一個(gè)界線,有底層經(jīng)驗(yàn),再去做應(yīng)用,會(huì)感覺(jué)很踏實(shí)。有了業(yè)務(wù)經(jīng)驗(yàn),再了解一下底層,很快就可以組成一個(gè)團(tuán)隊(duì)。

02
嵌入式 Linux 底層系統(tǒng)包含哪些東西?

嵌入式 LINUX 里含有 bootloader, 內(nèi)核, 驅(qū)動(dòng)程序、根文件系統(tǒng)這 4 大塊。

一、bootloader

它就是一個(gè)稍微復(fù)雜的裸板程序。但是要把這裸板程序看懂寫(xiě)好一點(diǎn)都不容易。Windows 下好用的工具弱化了我們的編程能力。很多人一玩嵌入式就用 ADS、KEIL。能回答這幾個(gè)問(wèn)題嗎?

Q:一上電,CPU 從哪里取指令執(zhí)行?


A:一般從 Flash 上指令。

Q:但是 Flash 一般是只能讀不能直接寫(xiě)的,如果用到全局變量,這些全局變量在哪里?


A:全局變量應(yīng)該在內(nèi)存里。

Q:那么誰(shuí)把全局變量放到內(nèi)存里去?


A:長(zhǎng)期用 ADS、KEIL 的朋友,你能回答嗎?這需要"重定位"。在 ADS 或 KEIL 里,重定位的代碼是制作這些工具的公司幫你寫(xiě)好了。你可曾去閱讀過(guò)?

Q:內(nèi)存那么大,我怎么知道把"原來(lái)存在 Flash 上的內(nèi)容"讀到內(nèi)存的"哪個(gè)地址去"?


A:這個(gè)地址用"鏈接腳本"決定,在 ADS 里有 scatter 文件,KEIL 里也有類似的文件。但是,你去研究過(guò)嗎?

Q:你說(shuō)重定位是把程序從 Flash 復(fù)制到內(nèi)存,那么這個(gè)程序可以讀 Flash ???


A:是的,要能操作 Flash。當(dāng)然不僅僅是這些,還有設(shè)置時(shí)鐘讓系統(tǒng)運(yùn)行得更快等等。

先自問(wèn)自答到這里吧,對(duì)于 bootloader 這一個(gè)裸板程序,其實(shí)有 3 部分要點(diǎn):

①對(duì)硬件的操作

對(duì)硬件的操作,需要看原理圖、芯片手冊(cè)。這需要一定的硬件知識(shí),不要求能設(shè)計(jì)硬件,但是至少能看懂; 不求能看懂模擬電路,但是要能看懂?dāng)?shù)字電路。這方面的能力在學(xué)校里都可以學(xué)到,微機(jī)原理、數(shù)字電路這 2 本書(shū)就足夠了。想速成的話,就先放掉這塊吧,不懂就 GOOGLE、發(fā)貼。另外,芯片手冊(cè)是肯定要讀的,別去找中文的,就看英文的。開(kāi)始是非常痛苦,以后就會(huì)發(fā)現(xiàn)那些語(yǔ)法、詞匯一旦熟悉后,讀任何芯片手冊(cè)都很容易。

②對(duì) ARM 體系處理器的了解

對(duì) ARM 體系處理器的了解,可以看杜春蕾的,里面講有匯編指令,有異常模式、MMU 等。也就這 3 塊內(nèi)容需要了解。

③程序的基本概念:重定位、棧、代碼段數(shù)據(jù)段 BSS 段等

程序的基本概念,王道當(dāng)然是去看編譯原理了。可惜,這類書(shū)絕對(duì)是天書(shū)級(jí)別的。若非超級(jí)天才還是別去看了。可以看韋東山的<嵌入式 Linux 應(yīng)用開(kāi)發(fā)完全手冊(cè)>。

對(duì)于 bootloader,可以先看,然后自己寫(xiě)程序把各個(gè)硬件的實(shí)驗(yàn)都做一遍,比如 GPIO、時(shí)鐘、SDRAM、UART、NAND。把它們都弄清楚了,組臺(tái)在一起就很容易看懂 u-boot 了 。

總結(jié)一下,看懂硬件原理圖、看芯片手冊(cè),這都需要自己去找資料。

二、內(nèi)核

想速成的人,先跨過(guò)內(nèi)核的學(xué)習(xí),直接學(xué)習(xí)怎么寫(xiě)驅(qū)動(dòng)。


想成為高手,內(nèi)核必須深刻了解。注意,是了解,要對(duì)里面的調(diào)度機(jī)制、內(nèi)存管理機(jī)制、文件管理機(jī)制等等有所了解。

推薦兩本書(shū):

1. 通讀,請(qǐng)看薄的那本


2. 選讀, 想了解哪一塊就讀哪一節(jié)

三、驅(qū)動(dòng)

驅(qū)動(dòng)包含兩部分:硬件本身的操作、驅(qū)動(dòng)程序的框架。


又是硬件,還是要看得懂原理圖、讀得懂芯片手冊(cè),多練吧。

①硬件本身的操作

說(shuō)到驅(qū)動(dòng)框架,有一些書(shū)介紹一下。LDD3,即,老外寫(xiě)的那本,里面介紹了不少概念,值得一讀。但是,它的作用 也就限于介紹概念了。入門(mén)之前可以用它來(lái)熟悉一下概念。

②驅(qū)動(dòng)程序的框架

驅(qū)動(dòng)方面比較全的介紹,應(yīng)該是宋寶華的了。要想深入了解某一塊,絕對(duì)是超 5 星級(jí)推薦。別指望把它讀完,1800 多頁(yè),上下兩冊(cè)呢。某一塊不清楚時(shí),就去翻一下它。任何一部分,這書(shū)都可以講上 2、3 百頁(yè),非常詳細(xì)。并且是以某個(gè)目標(biāo)來(lái)帶你分析內(nèi)核源碼。它以 linux2.4 為例,但是原理相通,同樣適用于其它版本的 linux。

把手上的開(kāi)發(fā)板所涉及的硬件,都去嘗試寫(xiě)一個(gè)驅(qū)動(dòng)吧。有問(wèn)題就先"痛苦地思考",思考的過(guò)程中會(huì)把很多不相關(guān)的知識(shí)串聯(lián)起來(lái),最終貫通。

四、根文件系統(tǒng)

大家有沒(méi)有想過(guò)這 2 個(gè)問(wèn)題:

Q:對(duì)于 Linux 做出來(lái)的產(chǎn)品,有些用作監(jiān)控、有些做手機(jī)、有些做平板。那么內(nèi)核啟動(dòng)后,掛載根文件系統(tǒng)后,應(yīng)該啟動(dòng)哪一個(gè)應(yīng)用程序呢?

A:內(nèi)核不知道也不管應(yīng)該啟動(dòng)哪一個(gè)用戶程序。它只啟動(dòng) init 這一個(gè)應(yīng)用程序,它對(duì)應(yīng) /sbin/init。

顯然,這個(gè)應(yīng)用程序就要讀取配置文件,根據(jù)配置文件去啟動(dòng)用戶程序(監(jiān)控、手冊(cè)界面、平板界面等等,這個(gè)問(wèn)題提示我們,文件系統(tǒng)的內(nèi)容是有一些約定的,比如要有 /sbin/init,要有配置文件 。

Q:你寫(xiě)的 hello,world 程序,有沒(méi)有想過(guò)里面用到的 printf 是誰(shuí)實(shí)現(xiàn)的?

A:這個(gè)函數(shù)不是你實(shí)現(xiàn)的,是庫(kù)函數(shù)實(shí)現(xiàn)的。它運(yùn)行時(shí),得找到庫(kù)。

這個(gè)問(wèn)題提示我們,文件系統(tǒng)里還要有庫(kù)。

簡(jiǎn)單的自問(wèn)自答到這里,要想深入了解,可以看一下 busybox 的 init.c,就可以知道 init 進(jìn)程做的事情了。

當(dāng)然,也可以看<嵌入式 Linux 應(yīng)用開(kāi)發(fā)完全手冊(cè)>里構(gòu)建根文件系統(tǒng)那章。

03
驅(qū)動(dòng)程序設(shè)計(jì)的 5 個(gè)方法

1. 使用設(shè)計(jì)模式

設(shè)計(jì)模式是一個(gè)用來(lái)處理那些在軟件中會(huì)重復(fù)出現(xiàn)的問(wèn)題的解決方案。開(kāi)發(fā)人員可以選擇浪費(fèi)寶貴的時(shí)間和預(yù)算從無(wú)到有地重新發(fā)明一個(gè)解決方案,也可以從他的解決方案工具箱中選擇一個(gè)最適合解決這個(gè)問(wèn)題的方案。在微處理器出現(xiàn)之初,底層驅(qū)動(dòng)已經(jīng)很成熟了,那么,為什么不利用現(xiàn)有的成熟的解決方案呢?

驅(qū)動(dòng)程序設(shè)計(jì)模式大致分屬以下 4 個(gè)類別:Bit bang、輪詢、中斷驅(qū)動(dòng)和直接存儲(chǔ)器訪問(wèn)(DMA)。

Bit bang 模式:當(dāng)微控制器沒(méi)有內(nèi)外設(shè)去執(zhí)行功能的時(shí)候,或者當(dāng)所有的內(nèi)外設(shè)都已經(jīng)被使用了,而此時(shí)又有一個(gè)新的請(qǐng)求,那么開(kāi)發(fā)者就應(yīng)該選擇 Bit bang 設(shè)計(jì)模式。Bit bang 模式的解決方案很有效率,但通常需要大量的軟件開(kāi)銷來(lái)確保其實(shí)施的能力。Bit bang 模式可以讓開(kāi)發(fā)者手動(dòng)完成通信協(xié)議或外部行為。

輪詢模式用于簡(jiǎn)單地監(jiān)視一個(gè)輪詢調(diào)度方式中的事件。輪詢模式適用于非常簡(jiǎn)單的系統(tǒng),但許多現(xiàn)代應(yīng)用程序都需要中斷。

中斷可以讓開(kāi)發(fā)者在事件發(fā)生時(shí)進(jìn)行處理,而不用等代碼手動(dòng)檢查。

DMA(直接存儲(chǔ)器訪問(wèn))模式允許其它外圍設(shè)備來(lái)處理數(shù)據(jù)傳輸?shù)男枨?,而不需要?qū)動(dòng)的干預(yù)。

2. 了解實(shí)時(shí)行為

一個(gè)實(shí)時(shí)系統(tǒng)是否能滿足實(shí)時(shí)需求取決于它的驅(qū)動(dòng)程序。寫(xiě)入能力差的驅(qū)動(dòng)是低效的,并可能使不知情的開(kāi)發(fā)者放棄系統(tǒng)的性能。設(shè)計(jì)者需要考慮驅(qū)動(dòng)的兩個(gè)特點(diǎn):阻塞和非阻塞。一個(gè)阻塞的驅(qū)動(dòng)程序在其完成工作之前會(huì)阻止其他任何軟件執(zhí)行操作。例如,一個(gè) USART 驅(qū)動(dòng)程序可以把一個(gè)字符裝入傳輸緩沖區(qū),然后一直等到接收到傳輸結(jié)束標(biāo)志符才繼續(xù)執(zhí)行下一步操作。

另一方面,非阻塞驅(qū)動(dòng)則是一般利用中斷來(lái)實(shí)現(xiàn)它的功能。中斷的使用可以防止驅(qū)動(dòng)程序在等待一個(gè)事件發(fā)生時(shí)攔截其他軟件的執(zhí)行操作。USART 的驅(qū)動(dòng)程序可以將一個(gè)字符裝入傳輸緩沖區(qū)然后等主程序發(fā)布下一個(gè)指令。傳輸結(jié)束標(biāo)志符的設(shè)置會(huì)導(dǎo)致中斷結(jié)束,讓驅(qū)動(dòng)進(jìn)行下一步操作。

無(wú)論哪種類型,為了保持實(shí)時(shí)性能,并防止系統(tǒng)中的故障,開(kāi)發(fā)人員必須了解驅(qū)動(dòng)的平均執(zhí)行時(shí)間和最壞情況下的執(zhí)行時(shí)間。一個(gè)完整的系統(tǒng)可能會(huì)因?yàn)橐粋€(gè)潛在的風(fēng)險(xiǎn)而造成更大的安全問(wèn)題。

3. 重用設(shè)計(jì)

在時(shí)間和預(yù)算都很緊張的情況下為什么還要再造輪子呢?在驅(qū)動(dòng)程序開(kāi)發(fā)中,重用、便攜性和可維護(hù)性都是驅(qū)動(dòng)設(shè)計(jì)的關(guān)鍵要求。這里面的許多特征可以通過(guò)硬件抽象層的設(shè)計(jì)和使用來(lái)說(shuō)明。

硬件抽象層(HAL)為開(kāi)發(fā)人員提供一種方式來(lái)創(chuàng)建一個(gè)標(biāo)準(zhǔn)接口去控制微控制器的外設(shè)。抽象隱藏實(shí)現(xiàn)細(xì)節(jié),取而代之的是提供了可視化功能,如 Usart_Init 和 Usart_Transmit。這個(gè)方法就是讓任何 USART、SPI、PWM 或其他外設(shè)具備所有微控制器都支持的共同特點(diǎn)。使用 HAL 隱藏底層、特定設(shè)備的細(xì)節(jié),讓?xiě)?yīng)用程序開(kāi)發(fā)人員專注于應(yīng)用的需求,而不是關(guān)注底層的硬件是如何工作的。同時(shí) HAL 提供了一個(gè)重用的容器。

4. 參考數(shù)據(jù)手冊(cè)

微控制器在過(guò)去的幾年里變得越來(lái)越復(fù)雜。以前想要完全了解一個(gè)微控制器需要掌握由一個(gè)大約包含 500 頁(yè)組成的單一數(shù)據(jù)手冊(cè)。而如今,一個(gè) 32 位微控制器通常包含由部分的數(shù)據(jù)手冊(cè)、整個(gè)微控制器系列的資料表、每個(gè)外設(shè)數(shù)以百計(jì)的資料以及所有的勘誤表組成的數(shù)據(jù)手冊(cè)。開(kāi)發(fā)人員如果想要完全掌握這部分的內(nèi)容需要了解幾千頁(yè)的文件。

不幸的是,所有這些數(shù)據(jù)手冊(cè)都是一個(gè)驅(qū)動(dòng)程序能真正合理實(shí)現(xiàn)所需要的。開(kāi)發(fā)人員在一開(kāi)始就要對(duì)每個(gè)數(shù)據(jù)手冊(cè)中包含的信息進(jìn)行收集和排序。通常它們中的每一個(gè)都需要被訪問(wèn)以使外設(shè)啟動(dòng)和運(yùn)行。關(guān)鍵信息被分散(或隱藏)在每種類型的數(shù)據(jù)手冊(cè)中。

5. 謹(jǐn)防外設(shè)故障

最近我剛好有機(jī)會(huì)把一系列的微控制器驅(qū)動(dòng)移植到其他的微處理器上。制造商和數(shù)據(jù)手冊(cè)都表明 PWM 外設(shè)在這兩個(gè)系列的微控制器之間是相同的。然而,實(shí)際情況卻是在運(yùn)行 PWM 驅(qū)動(dòng)器的時(shí)候兩者之間有很大的不同。該驅(qū)動(dòng)程序只能在原來(lái)的微控制器工作,而在新系列的微控制器上卻無(wú)效。

在反復(fù)翻看數(shù)據(jù)手冊(cè)之后,我在數(shù)據(jù)手冊(cè)中一個(gè)完全不相關(guān)的注腳里發(fā)現(xiàn)了 PWM 外設(shè)上電時(shí)會(huì)處于故障狀態(tài),需要將一個(gè)隱藏在寄存器中的標(biāo)志位清零。在驅(qū)動(dòng)程序?qū)崿F(xiàn)的開(kāi)始,確認(rèn)外設(shè)可能出現(xiàn)的故障并查看其他看似無(wú)關(guān)的寄存器錯(cuò)誤。

04
大牛對(duì)于嵌入式驅(qū)動(dòng)開(kāi)發(fā)的建議

1) 為了今后的發(fā)展,你除了考慮廣度以外,更重要的是注意知識(shí)的深度。

譬如,做過(guò)網(wǎng)絡(luò)驅(qū)動(dòng),那么是不是只停留在會(huì)寫(xiě)驅(qū)動(dòng)的表層上,有沒(méi)有對(duì) Linux 內(nèi)核的網(wǎng)絡(luò)結(jié)構(gòu),TCP/IP 協(xié)議作過(guò)深入的了解。

2) 在 Linux 下開(kāi)發(fā)很多時(shí)候都要利用現(xiàn)成的東西,沒(méi)必要什么都自己搞。關(guān)鍵是變成自己的驅(qū)動(dòng)后是否了解原作者編寫(xiě)時(shí)背后的一些東西。你應(yīng)該不止是簡(jiǎn)單的讓它工作。寫(xiě)驅(qū)動(dòng)的時(shí)候就要考慮它的性能問(wèn)題,并給出測(cè)試的方法(當(dāng)然可以利用現(xiàn)成的許多工具,譬如測(cè)試網(wǎng)絡(luò)性能的 netperf 等)。

當(dāng)你寫(xiě)過(guò) Flash 驅(qū)動(dòng),可能會(huì)知道 Flash 的性能有時(shí)候有多重要。

3) C 程序的自我修煉,是否考慮到軟件工程方面的一些東西,程序的可維護(hù)性和擴(kuò)展性,譬如 LCD 驅(qū)動(dòng),是不是從 Sharp 到 NEC 的只需要集中修改很少的幾個(gè)地方?

對(duì)于不同品牌的 Flash,如果使得 Flash 的驅(qū)動(dòng)做的更具有靈活性。

4) 如果有時(shí)間結(jié)余,可以關(guān)注 Linux 內(nèi)核的發(fā)展。譬如 LCD 的驅(qū)動(dòng)有沒(méi)有考慮到 V4L2 通用架構(gòu),譬如網(wǎng)絡(luò)驅(qū)動(dòng)用到了 NAPI 了嗎?當(dāng)然在此之前,假設(shè)已經(jīng)對(duì) LDD3, ULK2 理解的比較熟了。

5) 現(xiàn)在所作的這些驅(qū)動(dòng)還算不得非常核心的東西。如果你想有更好的發(fā)展,可以考慮往 audio,video,net 方面發(fā)展,你應(yīng)該多注意真?zhèn)€行業(yè)需要什么樣的人才,上述每一項(xiàng)都需要很厚的底蘊(yùn),譬如 video,需要了解 MPEG4, H264 等,怎么也要個(gè) 1 到 2 年才能算個(gè)入行阿,所以我建議不要只顧悶頭做東西,要適當(dāng)關(guān)注目前的一些應(yīng)用。

6) 對(duì)硬件知識(shí)的補(bǔ)給,做嵌入式 Linux 這一行不可能不讀硬件的 Spec,如果你對(duì)硬件的工作機(jī)制理解的比較透,會(huì)有助你寫(xiě)出性能好的驅(qū)動(dòng)程序。

順便提一點(diǎn),適時(shí)的提高你的英語(yǔ)水平,對(duì)你的職業(yè)生涯絕對(duì)有幫助。(不要等需要的時(shí)候再補(bǔ),來(lái)不及)

7) 如果有時(shí)間,平時(shí)注意對(duì) Linux 應(yīng)用程序編寫(xiě)的了解 / 積累,也將有助于你寫(xiě)出很好功能很好的驅(qū)動(dòng)程序。

8) 永遠(yuǎn)不能以為自己做了很多東西,就驅(qū)動(dòng)而言,像 TVIN/TVOUT, USB, SDIO 等等,好多未知領(lǐng)域呢。在問(wèn)題還沒(méi)有解決之前很難說(shuō)清是哪里不對(duì)了。

有時(shí)候是 datasheet 里面的一句話沒(méi)有注意,還有好幾次調(diào)不出來(lái)最后查到是 PCB 的問(wèn)題,所以有時(shí)候特別暈。

05
嵌入式驅(qū)動(dòng)自學(xué)者的感受

經(jīng)過(guò)了多年的嵌入式自學(xué),可謂是不斷在絕望中求生。性格使然,我是一個(gè)我也不知這種性格的學(xué)名叫什么,就是學(xué)習(xí)一種東西,非得想要能理解每一處的含義作用為什么,要這樣做沒(méi)有其他辦法了嗎等等問(wèn)題。并且當(dāng)一個(gè)問(wèn)題找不到讓我能接受的解釋時(shí),那么我的學(xué)習(xí)路程也就幾乎要停在這里了,大概是因?yàn)槲矣憛捯恢虢狻?br />
可能是小時(shí)候被老師教導(dǎo)不要做書(shū)呆子的教育有關(guān),小時(shí)候,聽(tīng)話孩子,認(rèn)真,長(zhǎng)輩的教育對(duì)孩子的影響真的是非常的大,很多影響如果你不細(xì)心的觀察自己,你根本不能察覺(jué)這些進(jìn)入了你骨子的觀念,在我成長(zhǎng)過(guò)程中,這些長(zhǎng)輩的教育除了某些讓我自己經(jīng)歷到并徹底認(rèn)識(shí)到某個(gè)觀念并不正確時(shí),我才會(huì)形成自己的觀點(diǎn),自己的觀念,但這些自己的觀念在所有的價(jià)值觀中,猶如滄海一粟。

這種討厭一知半解的性格,在現(xiàn)在這個(gè)社會(huì)來(lái)說(shuō),可以說(shuō)是極端的,因?yàn)楝F(xiàn)在你學(xué)習(xí)使用的很多東西,他都不是從零開(kāi)始的,就好比,你編程使用的是高級(jí)語(yǔ)言而不是低級(jí)語(yǔ)言不是機(jī)器碼,所以我的整個(gè)學(xué)習(xí)過(guò)程是非常緩慢緩慢地進(jìn)行著,這么說(shuō)吧,前面說(shuō)我經(jīng)過(guò)了半年多的學(xué)習(xí),但是到現(xiàn)在為止,我接觸嵌入式已經(jīng)有兩個(gè)年頭了,也就是說(shuō),學(xué)習(xí)期間,我有一年多是在停滯著。

學(xué)習(xí)嵌入式,或者說(shuō)學(xué)習(xí)現(xiàn)代的計(jì)算機(jī)編程,如果你想學(xué)好,有一個(gè)比較要求,那就是你能接受它的設(shè)定、它的模式。反過(guò)來(lái)說(shuō),當(dāng)你真正接受它的設(shè)定、它的模式,并記住它們時(shí),我認(rèn)為,你已經(jīng)學(xué)好了。

昨天,我又置之死地而后生了一次。最近一直在搞驅(qū)動(dòng),一個(gè) LCD 驅(qū)動(dòng)搞得我?guī)缀跻艞壚^續(xù)走嵌入式這條路。昨夜,睡不覺(jué),打開(kāi)嵌入學(xué)習(xí)視頻,躺在已關(guān)燈很久的房間的床上,大概凌晨 3,4 點(diǎn)吧。之前我一直都是學(xué)習(xí)著驅(qū)動(dòng)自編源碼的教學(xué),是那種幾乎和裸板程序沒(méi)多大區(qū)別的編程方式,只是多使用了一些向內(nèi)核注冊(cè)的接口函數(shù)。

而最近我想換一下,因?yàn)楹芏嘣O(shè)備驅(qū)動(dòng),內(nèi)核都是自帶的,而且是各種平臺(tái)的設(shè)備驅(qū)動(dòng)都有,我想如果能熟悉掌握內(nèi)核自帶驅(qū)動(dòng)的編程,那以后要做某個(gè)設(shè)備的驅(qū)動(dòng)時(shí),我只需要在自帶驅(qū)動(dòng)中修改一下便好了,通過(guò)學(xué)習(xí) LCD 平臺(tái)設(shè)備的驅(qū)動(dòng),我了解了其編程想法,同時(shí)也認(rèn)同這種想法,甚至讓我疑惑,學(xué)習(xí)資料中教自編驅(qū)動(dòng)的意義,為何不直接教如果修改內(nèi)核源碼驅(qū)動(dòng)?

于是,繼續(xù)按著書(shū)去修改內(nèi)核驅(qū)動(dòng)源碼,但問(wèn)題是,書(shū)中說(shuō)他們這種修改,代碼成功運(yùn)行了,但我這,無(wú)論怎么調(diào)試都失敗,我反復(fù)檢測(cè),我的修改是否與書(shū)中一致,檢測(cè)了很多遍依然沒(méi)發(fā)現(xiàn)哪一步不同,不過(guò),有一點(diǎn)發(fā)現(xiàn)是,書(shū)中的內(nèi)核源碼和我內(nèi)核使用的源碼有一點(diǎn)點(diǎn)區(qū)別(當(dāng)然書(shū)里并沒(méi)有把所有的源碼都貼上,只是修改部分附近會(huì)聯(lián)帶著一些,這就是發(fā)現(xiàn),這些聯(lián)帶的沒(méi)需要修改的源碼和我的源碼有點(diǎn)區(qū)別,比如,我的源碼中多了一些設(shè)置(看似無(wú)關(guān)緊要的設(shè)置))。

與書(shū)核對(duì)無(wú)誤但失敗后,我又與成功運(yùn)行的自編驅(qū)動(dòng)核對(duì),我陸續(xù)發(fā)現(xiàn)我修改的內(nèi)涵源碼中,沒(méi)有去啟動(dòng)設(shè)備,也更沒(méi)有去點(diǎn)亮背光,而在顯存分配后的寄存器設(shè)置似乎也有問(wèn)題,因?yàn)檫@里的地址使用各種宏定義不同的累加或計(jì)算,最后算得地址和我的寄存器地址也不知是否吻合,因?yàn)轵?qū)動(dòng)源碼中最后計(jì)算得到的是虛擬地址。于是我對(duì)比自編驅(qū)動(dòng),一點(diǎn)點(diǎn)修改嘗試,到睡覺(jué)前都沒(méi)成功。

我是想學(xué)得理直氣壯一點(diǎn)的,最后是能一眼就能找到問(wèn)題,并迅速輕松解決問(wèn)題的,我也承認(rèn)自己確實(shí)是有些浮躁。但是經(jīng)過(guò)了昨晚床上的一點(diǎn)絕望的思考掙扎后,我好像想通了:為什么嵌入式學(xué)習(xí)視頻老師要教自編驅(qū)動(dòng)。

下面我說(shuō)下自編驅(qū)動(dòng)與內(nèi)核驅(qū)動(dòng)源碼各自的問(wèn)題:

自便驅(qū)動(dòng):

程序簡(jiǎn)單簡(jiǎn)潔,它只能驅(qū)動(dòng)特定的某個(gè)設(shè)備。如果設(shè)備換了需要支持另一款設(shè)備,那么你需要重新修改該驅(qū)動(dòng);如果需要系統(tǒng)同時(shí)支持兩種 LCD,那么它就會(huì)變成復(fù)雜并且對(duì)于內(nèi)核驅(qū)動(dòng)的簡(jiǎn)潔優(yōu)勢(shì)會(huì)削弱不少;如果你想驅(qū)動(dòng)支持多種設(shè)備,那自編驅(qū)動(dòng),相對(duì)了內(nèi)核驅(qū)動(dòng)源碼的簡(jiǎn)潔優(yōu)勢(shì)會(huì)變成了劣勢(shì),因?yàn)榫幊趟枷氲倪m用范圍不同而產(chǎn)生的結(jié)果。

內(nèi)核自帶驅(qū)動(dòng)源碼:
①?gòu)南到y(tǒng)層次去考量,變量、宏定義使用多,甚至有些宏定義的值為了方便能讓各種在不同的階段需要不同的值調(diào)用,把簡(jiǎn)單的一個(gè)賦值調(diào)用變成了需要進(jìn)行多次運(yùn)算才能檢測(cè)到該值是否滿足使用要求,因?yàn)槲覀儾皇窃擈?qū)動(dòng)的編碼者,不清楚這樣做的好處,也或許是內(nèi)核驅(qū)動(dòng)源碼的開(kāi)發(fā)者從整個(gè)系統(tǒng)的編程簡(jiǎn)潔性去考量,這樣做或許也是為了讓整個(gè)系統(tǒng)代碼更少,簡(jiǎn)潔的一種做法,因?yàn)槊總€(gè)設(shè)備你都給它賦具體的值的話,整個(gè)系統(tǒng)中有幾百種驅(qū)動(dòng)設(shè)備源碼,給所有設(shè)備的這個(gè)位置參數(shù)都賦一個(gè)值的話,那各設(shè)備關(guān)于這個(gè)值的代碼就要多了幾百行了,所以還不如,讓各設(shè)備根據(jù)各種平臺(tái)去對(duì)某個(gè)宏進(jìn)行各自的計(jì)算來(lái)得到合適的值,但某些計(jì)算中相同的算法的也整合在一起,這樣就減少了系統(tǒng)不少行代碼。所以系統(tǒng)中驅(qū)動(dòng)源碼是系統(tǒng)開(kāi)發(fā)者對(duì)系統(tǒng)源碼的整合,是基于系統(tǒng)層的整合。所以,對(duì)于我這種對(duì)單個(gè)設(shè)備驅(qū)動(dòng)編碼的人,就會(huì)覺(jué)得系統(tǒng)源碼有好多不人性化的地方,會(huì)覺(jué)得簡(jiǎn)單的地方也被弄得很復(fù)雜。

②內(nèi)核自帶驅(qū)動(dòng)還有一些代碼是為了兼容以前的版本而添加了,比如以前硬件內(nèi)存資源稀少,需要使用調(diào)色板的方法來(lái)減少程序運(yùn)行時(shí)的內(nèi)存使用量,這也會(huì)真假代碼的復(fù)雜性,這一步雖不是必要的,但如果沒(méi)弄好,那 LCD 驅(qū)動(dòng)也不能正常使用。

③程序復(fù)雜,為了適用在多種設(shè)備型號(hào),更簡(jiǎn)單地添加不同型號(hào)的設(shè)備驅(qū)動(dòng),內(nèi)核對(duì)驅(qū)動(dòng)抽象分離,把驅(qū)動(dòng)分為平臺(tái)管理部分,驅(qū)動(dòng)代碼部分(與硬件無(wú)關(guān)碼),和設(shè)備代碼部分(硬件相關(guān)代碼)。用戶添加新型號(hào)設(shè)備驅(qū)動(dòng)時(shí),只需要在平臺(tái)管理部分檢查添加設(shè)備的匹配信息,和提供一個(gè)硬件設(shè)備相關(guān)的代碼(有格式)文件即可。


現(xiàn)在,站在驅(qū)動(dòng)開(kāi)發(fā)者而非系統(tǒng)開(kāi)發(fā)者的角度去衡量。


①自編的驅(qū)動(dòng),簡(jiǎn)潔,要點(diǎn)明確。這個(gè)對(duì)于驅(qū)動(dòng)開(kāi)發(fā)者的用處就是:無(wú)論你使用的是哪個(gè)版本的內(nèi)核,哪個(gè)芯片平臺(tái),你可以通過(guò)自編碼比較簡(jiǎn)單方便地就可以確認(rèn)硬件設(shè)備的情況,是否正常。如果自編碼通過(guò),那可以試用自編碼上使用的參數(shù)去與內(nèi)核進(jìn)行核對(duì)、修改,然后再去測(cè)試。如果不成功,對(duì)于內(nèi)核中多余的設(shè)置(這些大多可能是提供內(nèi)核用做基本判斷的變量)可以先屏蔽,編譯出錯(cuò)了,根據(jù)提示,找到出錯(cuò)的位置修改添加。因?yàn)檫@些多余的設(shè)置,設(shè)置對(duì)了還行,設(shè)置錯(cuò)了,你又不好去定位錯(cuò)在哪。


自編的驅(qū)動(dòng)在此處的用處,調(diào)試時(shí),可以讓你排除多余的失敗可能性問(wèn)題,在較少的代碼去查出錯(cuò)誤位置,如果你確定你的設(shè)置滿足了該設(shè)備的必需設(shè)置,還是失敗,你可以比較放心地去懷疑是硬件問(wèn)題了。如果自編碼成功,那個(gè)又可以當(dāng)做你修改內(nèi)核驅(qū)動(dòng)的一個(gè)標(biāo)準(zhǔn)。


②內(nèi)核驅(qū)動(dòng)源碼支持管理多種型號(hào)的設(shè)備的優(yōu)勢(shì)是我用使用它的原因。先了解本版本本平臺(tái)的設(shè)備驅(qū)動(dòng)結(jié)構(gòu),如果是添加型號(hào)支持,那就根據(jù)自編驅(qū)動(dòng)的參數(shù)與設(shè)置即可,如果是第一次啟動(dòng)這類設(shè)備,你就還需要檢測(cè)結(jié)構(gòu)是完整性,如果結(jié)構(gòu)完整,參數(shù)無(wú)誤依舊錯(cuò)誤,那就把內(nèi)核驅(qū)動(dòng)源碼精簡(jiǎn)到自編碼的簡(jiǎn)單粗暴設(shè)置吧。最終就變成了在基于內(nèi)核驅(qū)動(dòng)架構(gòu)下的自編驅(qū)動(dòng)。如果還不行,那無(wú)疑是結(jié)構(gòu)性問(wèn)題了。


所以自編驅(qū)動(dòng),還是有其存在價(jià)值的。內(nèi)核驅(qū)動(dòng)源碼內(nèi)容會(huì)變,平臺(tái)會(huì)變,但自編驅(qū)動(dòng)是變得最小的一個(gè),也是最容易實(shí)現(xiàn)驅(qū)動(dòng)目的的一個(gè)。是一碼打天下不可缺少的重要組成部分。

06
如何編寫(xiě)嵌入式 Linux 設(shè)備驅(qū)動(dòng)程序?

一、Linux device driver 的概念

系統(tǒng)調(diào)用是操作系統(tǒng)內(nèi)核和應(yīng)用程序之間的接口,設(shè)備驅(qū)動(dòng)程序是操作系統(tǒng)內(nèi)核和機(jī)器硬件之間的接口。設(shè)備驅(qū)動(dòng)程序?yàn)閼?yīng)用程序屏蔽了硬件的細(xì)節(jié),這樣在應(yīng)用程序看來(lái),硬件設(shè)備只是一個(gè)設(shè)備文件,應(yīng)用程序可以象操作普通文件一樣對(duì)硬件設(shè)備進(jìn)行操作。設(shè)備驅(qū)動(dòng)程序是內(nèi)核的一部分,它完成以下的功能:

1、對(duì)設(shè)備初始化和釋放;


2、把數(shù)據(jù)從內(nèi)核傳送到硬件和從硬件讀取數(shù)據(jù);


3、讀取應(yīng)用程序傳送給設(shè)備文件的數(shù)據(jù)和回送應(yīng)用程序請(qǐng)求的數(shù)據(jù);


4、檢測(cè)和處理設(shè)備出現(xiàn)的錯(cuò)誤。

在 linux 操作系統(tǒng)下有三類主要的設(shè)備文件類型,一是字符設(shè)備,二是塊設(shè)備,三是網(wǎng)絡(luò)設(shè)備。字符設(shè)備和塊設(shè)備的主要區(qū)別是:在對(duì)字符設(shè)備發(fā)出讀 / 寫(xiě)請(qǐng)求時(shí),實(shí)際的硬件 I/O 一般就緊接著發(fā)生了,塊設(shè)備則不然,它利用一塊系統(tǒng)內(nèi)存作緩沖區(qū),當(dāng)用戶進(jìn)程對(duì)設(shè)備請(qǐng)求能滿足用戶的要求,就返回請(qǐng)求的數(shù)據(jù),如果不能,就調(diào)用請(qǐng)求函數(shù)來(lái)進(jìn)行實(shí)際的 I/O 操作。塊設(shè)備是主要針對(duì)磁盤(pán)等慢速設(shè)備設(shè)計(jì)的,以免耗費(fèi)過(guò)多的 CPU 時(shí)間來(lái)等待。

已經(jīng)提到,用戶進(jìn)程是通過(guò)設(shè)備文件來(lái)與實(shí)際的硬件打交道。每個(gè)設(shè)備文件都都有其文件屬性(c/b),表示是字符設(shè)備還是塊設(shè)備?另外每個(gè)文件都有兩個(gè)設(shè)備號(hào),第一個(gè)是主設(shè)備號(hào),標(biāo)識(shí)驅(qū)動(dòng)程序,第二個(gè)是從設(shè)備號(hào),標(biāo)識(shí)使用同一個(gè)設(shè)備驅(qū)動(dòng)程序的不同的硬件設(shè)備,比如有兩個(gè)軟盤(pán),就可以用從設(shè)備號(hào)來(lái)區(qū)分他們。設(shè)備文件的的主設(shè)備號(hào)必須與設(shè)備驅(qū)動(dòng)程序在登記時(shí)申請(qǐng)的主設(shè)備號(hào)一致,否則用戶進(jìn)程將無(wú)法訪問(wèn)到驅(qū)動(dòng)程序。

最后必須提到的是,在用戶進(jìn)程調(diào)用驅(qū)動(dòng)程序時(shí),系統(tǒng)進(jìn)入核心態(tài),這時(shí)不再是搶先式調(diào)度。也就是說(shuō),系統(tǒng)必須在你的驅(qū)動(dòng)程序的子函數(shù)返回后才能進(jìn)行其他的工作。如果你的驅(qū)動(dòng)程序陷入死循環(huán),不幸的是你只有重新啟動(dòng)機(jī)器了,然后就是漫長(zhǎng)的 fsck。

二、實(shí)例剖析

我們來(lái)寫(xiě)一個(gè)最簡(jiǎn)單的字符設(shè)備驅(qū)動(dòng)程序。雖然它什么也不做,但是通過(guò)它可以了解 Linux 的設(shè)備驅(qū)動(dòng)程序的工作原理。把下面的 C 代碼輸入機(jī)器,你就會(huì)獲得一個(gè)真正的設(shè)備驅(qū)動(dòng)程序。

由于用戶進(jìn)程是通過(guò)設(shè)備文件同硬件打交道,對(duì)設(shè)備文件的操作方式不外乎就是一些系統(tǒng)調(diào)用,如 open,read,write,close…, 注意,不是 fopen, fread,但是如何把系統(tǒng)調(diào)用和驅(qū)動(dòng)程序關(guān)聯(lián)起來(lái)呢?這需要了解一個(gè)非常關(guān)鍵的數(shù)據(jù)結(jié)構(gòu):

struct file_operations {


int (*seek) (struct inode * ,struct file *, off_t ,int);


int (*read) (struct inode * ,struct file *, char ,int);


int (*write) (struct inode * ,struct file *, off_t ,int);


int (*readdir) (struct inode * ,struct file *, struct dirent * ,int);


int (*select) (struct inode * ,struct file *, int ,select_table *);


int (*ioctl) (struct inode * ,struct file *, unsined int ,unsigned long);


int (*mmap) (struct inode * ,struct file *, struct vm_area_struct *);


int (*open) (struct inode * ,struct file *);


int (*release) (struct inode * ,struct file *);


int (*fsync) (struct inode * ,struct file *);


int (*fasync) (struct inode * ,struct file *,int);


int (*check_media_change) (struct inode * ,struct file *);


int (*revalidate) (dev_t dev);


}

這個(gè)結(jié)構(gòu)的每一個(gè)成員的名字都對(duì)應(yīng)著一個(gè)系統(tǒng)調(diào)用。用戶進(jìn)程利用系統(tǒng)調(diào)用在對(duì)設(shè)備文件進(jìn)行諸如 read/write 操作時(shí),系統(tǒng)調(diào)用通過(guò)設(shè)備文件的主設(shè)備號(hào)找到相應(yīng)的設(shè)備驅(qū)動(dòng)程序,然后讀取這個(gè)數(shù)據(jù)結(jié)構(gòu)相應(yīng)的函數(shù)指針,接著把控制權(quán)交給該函數(shù)。這是 linux 的設(shè)備驅(qū)動(dòng)程序工作的基本原理。既然是這樣,則編寫(xiě)設(shè)備驅(qū)動(dòng)程序的主要工作就是編寫(xiě)子函數(shù),并填充 file_operations 的各個(gè)域。

下面就開(kāi)始寫(xiě)子程序。

#include 基本的類型定義


#include 文件系統(tǒng)使用相關(guān)的頭文件


#include


#include


#include


unsigned int test_major = 0;


static int read_test(struct inode *inode,struct file *file,char *buf,int count)


{


int left; 用戶空間和內(nèi)核空間


if (verify_area(VERIFY_WRITE,buf,count) == -EFAULT )


return -EFAULT;


for(left = count ; left > 0 ; left--)


{


__put_user(1,buf,1);


buf++;


}


return count;


}

這個(gè)函數(shù)是為 read 調(diào)用準(zhǔn)備的。當(dāng)調(diào)用 read 時(shí),read_test()被調(diào)用,它把用戶的緩沖區(qū)全部寫(xiě) 1。buf 是 read 調(diào)用的一個(gè)參數(shù)。它是用戶進(jìn)程空間的一個(gè)地址。但是在 read_test 被調(diào)用時(shí),系統(tǒng)進(jìn)入核心態(tài)。所以不能使用 buf 這個(gè)地址,必須用 __put_user(),這是 kernel 提供的一個(gè)函數(shù),用于向用戶傳送數(shù)據(jù)。另外還有很多類似功能的函數(shù)。請(qǐng)參考,在向用戶空間拷貝數(shù)據(jù)之前,必須驗(yàn)證 buf 是否可用。這就用到函數(shù) verify_area。為了驗(yàn)證 BUF 是否可以用。

static int write_test(struct inode *inode,struct file *file,const char *buf,int count)


{


return count;


}


static int open_test(struct inode *inode,struct file *file )


{


MOD_INC_USE_COUNT; 模塊計(jì)數(shù)加以,表示當(dāng)前內(nèi)核有個(gè)設(shè)備加載內(nèi)核當(dāng)中去


return 0;


}


static void release_test(struct inode *inode,struct file *file )


{


MOD_DEC_USE_COUNT;


}

這幾個(gè)函數(shù)都是空操作。實(shí)際調(diào)用發(fā)生時(shí)什么也不做,他們僅僅為下面的結(jié)構(gòu)提供函數(shù)指針。

struct file_operations test_fops = {?


read_test,


write_test,


open_test,


release_test,


};

設(shè)備驅(qū)動(dòng)程序的主體可以說(shuō)是寫(xiě)好了。現(xiàn)在要把驅(qū)動(dòng)程序嵌入內(nèi)核。驅(qū)動(dòng)程序可以按照兩種方式編譯。一種是編譯進(jìn) kernel,另一種是編譯成模塊(modules),如果編譯進(jìn)內(nèi)核的話,會(huì)增加內(nèi)核的大小,還要改動(dòng)內(nèi)核的源文件,而且不能動(dòng)態(tài)的卸載,不利于調(diào)試,所以推薦使用模塊方式。

int init_module(void)


{


int result;


result = register_chrdev(0, "test", &test_fops); 對(duì)設(shè)備操作的整個(gè)接口


if (result < 0) {?


printk(KERN_INFO "test: can't get major number/n");


return result;


}


if (test_major == 0) test_major = result; /* dynamic */


return 0;


}

在用 insmod 命令將編譯好的模塊調(diào)入內(nèi)存時(shí),init_module 函數(shù)被調(diào)用。在這里,init_module 只做了一件事,就是向系統(tǒng)的字符設(shè)備表登記了一個(gè)字符設(shè)備。register_chrdev 需要三個(gè)參數(shù),參數(shù)一是希望獲得的設(shè)備號(hào),如果是零的話,系統(tǒng)將選擇一個(gè)沒(méi)有被占用的設(shè)備號(hào)返回。參數(shù)二是設(shè)備文件名,參數(shù)三用來(lái)登記驅(qū)動(dòng)程序?qū)嶋H執(zhí)行操作的函數(shù)的指針。

如果登記成功,返回設(shè)備的主設(shè)備號(hào),不成功,返回一個(gè)負(fù)值。

void cleanup_module(void)


{


unregister_chrdev(test_major,"test");


}

在用 rmmod 卸載模塊時(shí),cleanup_module 函數(shù)被調(diào)用,它釋放字符設(shè)備 test 在系統(tǒng)字符設(shè)備表中占有的表項(xiàng)。

一個(gè)極其簡(jiǎn)單的字符設(shè)備可以說(shuō)寫(xiě)好了,文件名就叫 test.c 吧。

下面編譯 :

$ gcc -O2 -DMODULE -D__KERNEL__ -c test.c –c 表示輸出制定名,自動(dòng)生成 .o 文件

得到文件 test.o 就是一個(gè)設(shè)備驅(qū)動(dòng)程序。

如果設(shè)備驅(qū)動(dòng)程序有多個(gè)文件,把每個(gè)文件按上面的命令行編譯,然后

ld ?-r ?file1.o ?file2.o ?-o ?modulename。

驅(qū)動(dòng)程序已經(jīng)編譯好了,現(xiàn)在把它安裝到系統(tǒng)中去。

$ insmod ?–f ?test.o

如果安裝成功,在 /proc/devices 文件中就可以看到設(shè)備 test,并可以看到它的主設(shè)備號(hào)。要卸載的話,運(yùn)行 :

$ rmmod test

下一步要?jiǎng)?chuàng)建設(shè)備文件。

mknod /dev/test c major minor

c 是指字符設(shè)備,major 是主設(shè)備號(hào),就是在 /proc/devices 里看到的。

用 shell 命令

$ cat /proc/devices

就可以獲得主設(shè)備號(hào),可以把上面的命令行加入你的 shell script 中去。

minor 是從設(shè)備號(hào),設(shè)置成 0 就可以了。

我們現(xiàn)在可以通過(guò)設(shè)備文件來(lái)訪問(wèn)我們的驅(qū)動(dòng)程序。寫(xiě)一個(gè)小小的測(cè)試程序。

#include


#include


#include


#include


main()


{


int testdev;


int i;


char buf[10];


testdev = open("/dev/test",O_RDWR);


if ( testdev == -1 )


{


printf("Cann't open file /n");


exit(0);


}


read(testdev,buf,10);


for (i = 0; i < 10;i++)?


printf("%d/n",buf[i]);


close(testdev);


}

編譯運(yùn)行,看看是不是打印出全 1 ?

以上只是一個(gè)簡(jiǎn)單的演示。真正實(shí)用的驅(qū)動(dòng)程序要復(fù)雜的多,要處理如中斷,DMA,I/O port 等問(wèn)題。這些才是真正的難點(diǎn)。上述給出了一個(gè)簡(jiǎn)單的字符設(shè)備驅(qū)動(dòng)編寫(xiě)的框架和原理,更為復(fù)雜的編寫(xiě)需要去認(rèn)真研究 LINUX 內(nèi)核的運(yùn)行機(jī)制和具體的設(shè)備運(yùn)行的機(jī)制等等。希望大家好好掌握 LINUX 設(shè)備驅(qū)動(dòng)程序編寫(xiě)的方法。

07
嵌入式驅(qū)動(dòng)的結(jié)構(gòu)分析

在 Linux 系統(tǒng)上編寫(xiě)驅(qū)動(dòng)程序,說(shuō)簡(jiǎn)單也簡(jiǎn)單,說(shuō)難也難。難在于對(duì)算法的編寫(xiě)和設(shè)備的控制方面,是比較讓人頭疼的;說(shuō)它簡(jiǎn)單是因?yàn)樵?Linux 下已經(jīng)有一套驅(qū)動(dòng)開(kāi)發(fā)的模式,編寫(xiě)的時(shí)候只需要按照這個(gè)模式寫(xiě)就可以了,而這個(gè)模式就是它事先定義好的一些結(jié)構(gòu)體,在驅(qū)動(dòng)編寫(xiě)的時(shí)候,只要對(duì)這些結(jié)構(gòu)體根據(jù)設(shè)備的需求進(jìn)行適當(dāng)?shù)奶畛洌蛯?shí)現(xiàn)了驅(qū)動(dòng)的編寫(xiě)。

首先在 Linux 下,視一切事物皆為文件,它同樣把驅(qū)動(dòng)設(shè)備也看成是文件,對(duì)于簡(jiǎn)單的文件操作,無(wú)非就是 open/close/read/write,在 Linux 對(duì)于文件的操作有一個(gè)關(guān)鍵的數(shù)據(jù)結(jié)構(gòu):file_operation,它的定義在源碼目錄下的 include/linux/fs.h 中,內(nèi)容如下:


[cpp] view plain copy


1. struct file_operations {


2. struct module *owner;


3. loff_t (*llseek) (struct file *, loff_t, int);


4. ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);


5. ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);


6. ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);


7. ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);


8. int (*readdir) (struct file *, void *, filldir_t);


9. unsigned int (*poll) (struct file *, struct poll_table_struct *);


10. int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);


11. long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);


12. long (*compat_ioctl) (struct file *, unsigned int, unsigned long);


13. int (*mmap) (struct file *, struct vm_area_struct *);


14. int (*open) (struct inode *, struct file *);


15. int (*flush) (struct file *, fl_owner_t id);


16. int (*release) (struct inode *, struct file *);


17. int (*fsync) (struct file *, int datasync);


18. int (*aio_fsync) (struct kiocb *, int datasync);


19. int (*fasync) (int, struct file *, int);


20. int (*lock) (struct file *, int, struct file_lock *);


21. ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);


22. unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);


23. int (*check_flags)(int);


24. int (*flock) (struct file *, int, struct file_lock *);


25. ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);


26. ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);


27. int (*setlease)(struct file *, long, struct file_lock **);


28. };



對(duì)于這個(gè)結(jié)構(gòu)體中的元素來(lái)說(shuō),大家可以看到每個(gè)函數(shù)名前都有一個(gè)“*”,所以它們都是指向函數(shù)的指針。目前我們只需要關(guān)心

ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);


ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);


int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);


int (*open) (struct inode *, struct file *);


int (*release) (struct inode *, struct file *);


這幾條,因?yàn)檫@篇文章就叫簡(jiǎn)單驅(qū)動(dòng)。就是讀(read)、寫(xiě)(write)、控制(ioctl)、打開(kāi)(open)、卸載(release)。這個(gè)結(jié)構(gòu)體在驅(qū)動(dòng)中的作用就是把系統(tǒng)調(diào)用和驅(qū)動(dòng)程序關(guān)聯(lián)起來(lái),它本身就是一系列指針的集合,每一個(gè)都對(duì)應(yīng)一個(gè)系統(tǒng)調(diào)用。

但是畢竟 file_operation 是針對(duì)文件定義的一個(gè)結(jié)構(gòu)體,所以在寫(xiě)驅(qū)動(dòng)時(shí),其中有一些元素是用不到的,所以在 2.6 版本引入了一個(gè)針對(duì)驅(qū)動(dòng)的結(jié)構(gòu)體框架:platform,它是通過(guò)結(jié)構(gòu)體 platform_device 來(lái)描述設(shè)備,用 platform_driver 描述設(shè)備驅(qū)動(dòng),它們都在源代碼目錄下的 include/linux/platform_device.h 中定義,內(nèi)容如下:

[cpp] view plain copy


1. struct platform_device {


2. const char * name;


3. int id;


4. struct device dev;


5. u32 num_resources;


6. struct resource * resource;


7. const struct platform_device_id *id_entry;


8. /* arch specific additions */


9. struct pdev_archdata archdata;


10. };


11. struct platform_driver {


12. int (*probe)(struct platform_device *);


13. int (*remove)(struct platform_device *);


14. void (*shutdown)(struct platform_device *);


15. int (*suspend)(struct platform_device *, pm_message_t state);


16. int (*resume)(struct platform_device *);


17. struct device_driver driver;


18. const struct platform_device_id *id_table;


19. };


對(duì)于第一個(gè)結(jié)構(gòu)體來(lái)說(shuō),它的作用就是給一個(gè)設(shè)備進(jìn)行登記作用,相當(dāng)于設(shè)備的身份證,要有姓名,身份證號(hào),還有你的住址,當(dāng)然其他一些東西就直接從舊身份證上 copy 過(guò)來(lái),這就是其中的 struct device dev,這是傳統(tǒng)設(shè)備的一個(gè)封裝,基本就是 copy 的意思了。對(duì)于第二個(gè)結(jié)構(gòu)體,因?yàn)?Linux 源代碼都是 C 語(yǔ)言編寫(xiě)的,對(duì)于這里它是利用結(jié)構(gòu)體和函數(shù)指針,來(lái)實(shí)現(xiàn)了 C 語(yǔ)言中沒(méi)有的“類”這一種結(jié)構(gòu),使得驅(qū)動(dòng)模型成為一個(gè)面向?qū)ο蟮慕Y(jié)構(gòu)。對(duì)于其中的 struct device_driver driver,它是描述設(shè)備驅(qū)動(dòng)的基本數(shù)據(jù)結(jié)構(gòu),它是在源代碼目錄下的 include/linux/device.h 中定義的,內(nèi)容如下:

[cpp] view plain copy


1. struct device_driver {


2. const char *name;


3. struct bus_type *bus;


4. struct module *owner;


5. const char *mod_name; /* used for built-in modules */


6. bool suppress_bind_attrs; /* disables bind/unbind via sysfs */


7. #if defined(CONFIG_OF)


8. const struct of_device_id *of_match_table;


9. #endif


10. int (*probe) (struct device *dev);


11. int (*remove) (struct device *dev);


12. void (*shutdown) (struct device *dev);


13. int (*suspend) (struct device *dev, pm_message_t state);


14. int (*resume) (struct device *dev);


15. const struct attribute_group **groups;


16. const struct dev_pm_ops *pm;


17. struct driver_private *p;


18. };


依然全部都是以指針的形式定義的所有元素,對(duì)于驅(qū)動(dòng)這一塊來(lái)說(shuō),每一項(xiàng)肯定都是需要一個(gè)函數(shù)來(lái)實(shí)現(xiàn)的,如果不把它們集合起來(lái),是很難管理的,而且很容易找不到,而且對(duì)于不同的驅(qū)動(dòng)設(shè)備,它的每一個(gè)功能的函數(shù)名必定是不一樣的,那么我們?cè)陂_(kāi)發(fā)的時(shí)候,需要用到這些函數(shù)的時(shí)候,就會(huì)很不方便,不可能在使用的時(shí)候去查找對(duì)應(yīng)的源代碼吧,所以就要進(jìn)行一個(gè)封裝,對(duì)于函數(shù)的封裝,在 C 語(yǔ)言中一個(gè)對(duì)好的辦法就是在結(jié)構(gòu)體中使用指向函數(shù)的指針,這種方法其實(shí)我們?cè)谄綍r(shí)的程序開(kāi)發(fā)中也可以使用,原則就是體現(xiàn)出一個(gè)“類”的感覺(jué),就是面向?qū)ο蟮乃枷搿?/p>

在 Linux 系統(tǒng)中,設(shè)備可以大致分為 3 類:字符設(shè)備、塊設(shè)備和網(wǎng)絡(luò)設(shè)備,而每種設(shè)備中又分為不同的子系統(tǒng),由于具有自身的一些特殊性質(zhì),所以有不能歸到某個(gè)已經(jīng)存在的子類中,所以可以說(shuō)是便于管理,也可以說(shuō)是為了達(dá)到同一種定義模式,所以 linux 系統(tǒng)把這些子系統(tǒng)歸為一個(gè)新類:misc ,以結(jié)構(gòu)體 miscdevice 描述,在源代碼目錄下的 include/linux/miscdevice.h 中定義,內(nèi)容如下:

[cpp] view plain copy


1. struct miscdevice {


2. int minor;


3. const char *name;


4. const struct file_operations *fops;


5. struct list_head list;


6. struct device *parent;


7. struct device *this_device;


8. const char *nodename;


9. mode_t mode;


10. };

對(duì)于這些設(shè)備,它們都擁有一個(gè)共同主設(shè)備號(hào) 10,所以它們是以次設(shè)備號(hào)來(lái)區(qū)分的,對(duì)于它里面的元素,大應(yīng)該很眼熟吧,而且還有一個(gè)我們更熟悉的 list_head 的元素,這里也可以應(yīng)證我之前說(shuō)的 list_head 就是一個(gè)橋梁的說(shuō)法了。

其實(shí)對(duì)于上面介紹的結(jié)構(gòu)體,里面的元素的作用基本可以見(jiàn)名思意了,所以不用贅述了。其實(shí)寫(xiě)一個(gè)驅(qū)動(dòng)模塊就是填充上述的結(jié)構(gòu)體,根據(jù)設(shè)備的功能和用途寫(xiě)相應(yīng)的函數(shù),然后對(duì)應(yīng)到結(jié)構(gòu)體中的指針,然后再寫(xiě)一個(gè)入口一個(gè)出口(就是模塊編程中的 init 和 exit)就可以了,一般情況下入口程序就是在注冊(cè) platform_device 和 platform_driver(當(dāng)然,這樣說(shuō)是針對(duì)以 platform 模式編寫(xiě)驅(qū)動(dòng)程序)。

08
嵌入式書(shū)籍推薦

1. 硬件方面的書(shū): 微機(jī)原理、數(shù)字電路,高校里的教材。


2. Linux 方面的書(shū):


<嵌入式 Linux 應(yīng)用開(kāi)發(fā)完全手冊(cè)>


,老外寫(xiě)的那本

在做驅(qū)動(dòng)的時(shí)候,肯定會(huì)用到與內(nèi)核相關(guān)的東西,或者需要和內(nèi)核中的某些模塊配合,這樣你也要理解內(nèi)核的某些部分是如何實(shí)現(xiàn)的,最后,你應(yīng)該可以很好的掌握 linux 的內(nèi)核整體框架是什么。

這些都是進(jìn)步,都是在你一次又一次的開(kāi)發(fā)中需要總結(jié)的東西,如果你不總結(jié),永遠(yuǎn)都是從頭開(kāi)始(或者說(shuō)永遠(yuǎn)都是還沒(méi)看懂別人代碼為什么這么做的時(shí)候,就去改它,然后可以工作了),就完事了,這樣你永遠(yuǎn)也不可能提高,最后你就有了現(xiàn)在的這種感覺(jué),覺(jué)得自己什么都不是,什么都不懂。

還有一點(diǎn)要說(shuō)明的,現(xiàn)在有許多人搞 linux 開(kāi)發(fā),卻不去用 linux 系統(tǒng)做為自己工作的平臺(tái),在這種情況下,你很難理解 linux 內(nèi)核的實(shí)現(xiàn)機(jī)制,以及為什么要采用這種方式實(shí)現(xiàn)。

你都沒(méi)用過(guò) linux 系統(tǒng),就想去實(shí)現(xiàn)一個(gè)與 linux 運(yùn)行機(jī)理相符合的項(xiàng)目,這是不可能的。就是你這個(gè)項(xiàng)目成功了,它也肯定不是最優(yōu)的,或者是不符合 linux 的使用習(xí)慣的(包括內(nèi)核的擴(kuò)展和應(yīng)用程序的實(shí)現(xiàn))。

所以,最后想說(shuō)的是,你一定要定期總結(jié),總結(jié)你這段時(shí)間做了什么,你從中得到了什么,為了你以后可以更好的做好類似的工作,你應(yīng)該去看些其它的什么東西;二是你一定至少要在工作的開(kāi)發(fā)環(huán)境中使用 linux 作為你的平時(shí)工作平臺(tái),而不要使用虛擬機(jī)和服務(wù)期,因?yàn)槟阒挥型耆私饬?linux 的使用,你才可以為它開(kāi)發(fā)符合它規(guī)則的項(xiàng)目。

審核編輯 黃昊宇

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    嵌入式開(kāi)發(fā)的四大方向,你會(huì)選擇哪一種?

    嵌入式開(kāi)發(fā)的四大方向,你會(huì)選擇哪一種?近幾年來(lái),隨著移動(dòng)互聯(lián)網(wǎng)、物聯(lián)網(wǎng)的迅猛發(fā)展,嵌入式技術(shù)無(wú)處不在,籠罩著各行各業(yè),在通訊、網(wǎng)絡(luò)、工控、醫(yī)療、電子等領(lǐng)域發(fā)揮著越來(lái)越重要的作用;隨著嵌入式
    發(fā)表于 08-23 16:30

    嵌入式開(kāi)發(fā)的四大方向,你會(huì)選擇哪一種?

    ,但是具體的專業(yè)各有不同,門(mén)檻到底各有不同,先就簡(jiǎn)單從四大職業(yè)方向來(lái)具體闡述: 嵌入式開(kāi)發(fā)的四大方向,分別有哪幾個(gè)?下面小編來(lái)簡(jiǎn)單介紹這幾點(diǎn)內(nèi)容,通過(guò)以下內(nèi)容的簡(jiǎn)單介紹,相信會(huì)有更多的人會(huì)明確自己
    發(fā)表于 09-20 10:10

    嵌入式開(kāi)發(fā)以后的發(fā)展方向?學(xué)習(xí)嵌入式我們可以做什么?

    哪些?學(xué)習(xí)嵌入式我們可以做什么?從事嵌入式開(kāi)發(fā)工作有優(yōu)勢(shì)嗎?等等,接下來(lái)為大家一一解答:  學(xué)習(xí)嵌入式以后的方向有哪些?  首先:學(xué)習(xí)嵌入式開(kāi)發(fā)
    發(fā)表于 11-22 16:52

    嵌入式開(kāi)發(fā)技術(shù)路線

    就是進(jìn)步。嵌入式有兩大方向,供參考:方向一:ARM、Linux C/C++工作:手機(jī),工業(yè)4.0,人工智能,機(jī)器人,物聯(lián)網(wǎng),智能家居,VR職業(yè)路線:從應(yīng)用層到系統(tǒng)層再到驅(qū)動(dòng)
    發(fā)表于 10-25 15:54

    什么是嵌入式技術(shù)?嵌入式開(kāi)發(fā)方向是什么?

    嵌入式操作系統(tǒng)、開(kāi)發(fā)工具、系統(tǒng)應(yīng)用開(kāi)發(fā)(&GUI)、系統(tǒng)移植、驅(qū)動(dòng)、內(nèi)核。4、所具備的知識(shí)體系:C語(yǔ)言、單片機(jī)、
    發(fā)表于 11-05 09:00

    程序員的三大方向分別是哪些

    1. 程序員的三大方向程序員的方向,一般可以分為3類:專業(yè)領(lǐng)域、業(yè)務(wù)領(lǐng)域、操作系統(tǒng)領(lǐng)域。你了解它們后,按興趣選擇吧。對(duì)于專業(yè)領(lǐng)域,我提供不了建議。業(yè)務(wù),也就是應(yīng)用程序,它跟操作系統(tǒng)并不是截然分開(kāi)
    發(fā)表于 12-21 07:56

    關(guān)于嵌入式開(kāi)發(fā)雜談

    關(guān)于嵌入式開(kāi)發(fā)雜談
    發(fā)表于 10-31 15:39 ?8次下載
    <b class='flag-5'>關(guān)于</b><b class='flag-5'>嵌入式開(kāi)發(fā)</b>雜談

    嵌入式開(kāi)發(fā)職業(yè)發(fā)展四大方向

    嵌入式開(kāi)發(fā)的發(fā)展方向有很多,門(mén)檻高低不一樣。本文詳細(xì)分析嵌入式開(kāi)發(fā)職業(yè)發(fā)展四大方向。隨著移動(dòng)互聯(lián)網(wǎng)、物聯(lián)網(wǎng)的迅猛發(fā)展,嵌入式技術(shù)日漸普及,在
    發(fā)表于 01-29 13:39 ?1.2w次閱讀
    <b class='flag-5'>嵌入式開(kāi)發(fā)</b>職業(yè)發(fā)展四<b class='flag-5'>大方向</b>

    嵌入式開(kāi)發(fā)好學(xué)嗎_嵌入式開(kāi)發(fā)職業(yè)發(fā)展方向是什么

    本文首先來(lái)了解一下嵌入式前景及薪資待遇,其次解答了嵌入式開(kāi)發(fā)好學(xué)不好學(xué)的問(wèn)題,最后闡述了嵌入式開(kāi)發(fā)必會(huì)知識(shí)以及嵌入式開(kāi)發(fā)職業(yè)的四大發(fā)展方向,
    的頭像 發(fā)表于 05-18 16:41 ?9100次閱讀

    嵌入式的兩大方向您了解嗎?

    嵌入式系統(tǒng)無(wú)疑是當(dāng)前最熱門(mén)最有發(fā)展前途的IT應(yīng)用領(lǐng)域之一,它是各種電子產(chǎn)品的核心技術(shù),也是工業(yè)4.0、遠(yuǎn)程醫(yī)療、3D打印等新興產(chǎn)業(yè)的核心技術(shù),具有廣闊的發(fā)展前景,那么小編就帶您了解一下嵌入式的兩大方向
    發(fā)表于 07-09 12:00 ?254次閱讀

    嵌入式的兩大方向您了解嗎?

    嵌入式系統(tǒng)無(wú)疑是當(dāng)前最熱門(mén)最有發(fā)展前途的IT應(yīng)用領(lǐng)域之一,它是各種電子產(chǎn)品的核心技術(shù),也是工業(yè)4.0、遠(yuǎn)程醫(yī)療、3D打印等新興產(chǎn)業(yè)的核心技術(shù),具有廣闊的發(fā)展前景,那么小編就帶您了解一下嵌入式的兩大方向
    發(fā)表于 07-10 10:56 ?1034次閱讀

    嵌入式軟件方向的學(xué)習(xí)難點(diǎn)有哪些應(yīng)該如何學(xué)習(xí)

    嵌入式學(xué)習(xí)不是一蹴而就的,需要不斷的積累,如果是希望向嵌入式軟件方向發(fā)展的話,目前最常見(jiàn)的是嵌入式Linux方向,關(guān)注這個(gè)
    發(fā)表于 05-17 18:05 ?0次下載
    <b class='flag-5'>嵌入式</b>軟件<b class='flag-5'>方向</b>的學(xué)習(xí)難點(diǎn)有哪些應(yīng)該如何學(xué)習(xí)

    嵌入式驅(qū)動(dòng)開(kāi)發(fā) 主要分為以下四個(gè)大方向

    一、嵌入式硬件開(kāi)發(fā):熟悉電路等知識(shí),非常熟悉各種常用元器件,掌握模擬電路和數(shù)字電路設(shè)計(jì)的開(kāi)發(fā)能力。熟練掌握嵌入式硬件知識(shí),熟悉硬件開(kāi)發(fā)模式和
    發(fā)表于 03-08 09:24 ?7185次閱讀

    嵌入式開(kāi)發(fā)的發(fā)展方向和前景

    嵌入式開(kāi)發(fā)的發(fā)展方向和前景第一:硬件方向硬件方向發(fā)展的嵌入式工程師對(duì)硬件比較了解,主要是從事硬件設(shè)計(jì)類的工作。但也不是對(duì)軟件部分完全不了解
    發(fā)表于 11-02 19:06 ?37次下載
    <b class='flag-5'>嵌入式開(kāi)發(fā)</b>的發(fā)展<b class='flag-5'>方向</b>和前景

    關(guān)于嵌入式驅(qū)動(dòng)開(kāi)發(fā),這篇文章讓你了解透徹!

    01嵌入式驅(qū)動(dòng)開(kāi)發(fā)到底學(xué)什么嵌入式大體分為以下四個(gè)方向:一、嵌入式硬件
    發(fā)表于 11-03 14:21 ?14次下載
    <b class='flag-5'>關(guān)于</b><b class='flag-5'>嵌入式</b><b class='flag-5'>驅(qū)動(dòng)</b><b class='flag-5'>開(kāi)發(fā)</b>,這篇文章讓你了解透徹!