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

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

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

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

STM32嵌入式開(kāi)發(fā) ? 來(lái)源:STM32嵌入式開(kāi)發(fā) ? 作者:STM32嵌入式開(kāi)發(fā) ? 2022-04-13 11:21 ? 次閱讀

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

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

一、嵌入式硬件開(kāi)發(fā):熟悉電路等知識(shí),非常熟悉各種常用元器件,掌握模擬電路和數(shù)字電路設(shè)計(jì)的開(kāi)發(fā)能力。熟練掌握嵌入式硬件知識(shí),熟悉硬件開(kāi)發(fā)模式和設(shè)計(jì)模式,熟悉ARM 32位處理器嵌入式硬件平臺(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è)編寫軟件驅(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編寫、根文件系統(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ū)ο缶幊痰幕舅枷?,以?a href="http://ttokpm.com/tags/C++/" target="_blank">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í)的是一門語(yǔ)言,例如C,C++,java,甚至Python,語(yǔ)言說(shuō)到底只是一門工具,就像學(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)度等概念。想寫出一個(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ì)。

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

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

一、bootloader

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

Q:

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

A:

一般從Flash上指令。

Q:

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

A:

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

Q:

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

A:

長(zhǎng)期用ADS、KEIL的朋友,你能回答嗎?這需要“重定位”。在ADS或KEIL里,重定位的代碼是制作這些工具的公司幫你寫好了。你可曾去閱讀過(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本書就足夠了。想速成的話,就先放掉這塊吧,不懂就GOOGLE、發(fā)貼。另外,芯片手冊(cè)是肯定要讀的,別去找中文的,就看英文的。開(kāi)始是非常痛苦,以后就會(huì)發(fā)現(xiàn)那些語(yǔ)法、詞匯一旦熟悉后,讀任何芯片手冊(cè)都很容易。

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

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

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

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

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

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

二、內(nèi)核

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

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

推薦兩本書:

1. 通讀《linux內(nèi)核完全注釋》,請(qǐng)看薄的那本

2. 選讀《Linux內(nèi)核情景分析》, 想了解哪一塊就讀哪一節(jié)

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

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

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

①硬件本身的操作

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

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

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

把手上的開(kāi)發(fā)板所涉及的硬件,都去嘗試寫一個(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:

你寫的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)那章。

3 驅(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)程序。寫入能力差的驅(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é),讓應(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ò)誤。

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

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

譬如,做過(guò)網(wǎng)絡(luò)驅(qū)動(dòng),那么是不是只停留在會(huì)寫驅(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)后是否了解原作者編寫時(shí)背后的一些東西。你應(yīng)該不止是簡(jiǎn)單的讓它工作。寫驅(qū)動(dòng)的時(shí)候就要考慮它的性能問(wèn)題,并給出測(cè)試的方法(當(dāng)然可以利用現(xiàn)成的許多工具,譬如測(cè)試網(wǎng)絡(luò)性能的netperf等)。

當(dāng)你寫過(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ì)有助你寫出性能好的驅(qū)動(dòng)程序。

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

7) 如果有時(shí)間,平時(shí)注意對(duì)Linux應(yīng)用程序編寫的了解/積累,也將有助于你寫出很好功能很好的驅(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í)候特別暈。

5 嵌入式驅(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)槲矣憛捯恢虢狻?/p>

可能是小時(shí)候被老師教導(dǎo)不要做書呆子的教育有關(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ù)按著書去修改內(nèi)核驅(qū)動(dòng)源碼,但問(wèn)題是,書中說(shuō)他們這種修改,代碼成功運(yùn)行了,但我這,無(wú)論怎么調(diào)試都失敗,我反復(fù)檢測(cè),我的修改是否與書中一致,檢測(cè)了很多遍依然沒(méi)發(fā)現(xiàn)哪一步不同,不過(guò),有一點(diǎn)發(fā)現(xiàn)是,書中的內(nèi)核源碼和我內(nèi)核使用的源碼有一點(diǎn)點(diǎn)區(qū)別(當(dāng)然書里并沒(méi)有把所有的源碼都貼上,只是修改部分附近會(huì)聯(lián)帶著一些,這就是發(fā)現(xiàn),這些聯(lián)帶的沒(méi)需要修改的源碼和我的源碼有點(diǎn)區(qū)別,比如,我的源碼中多了一些設(shè)置(看似無(wú)關(guān)緊要的設(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è)。是一碼打天下不可缺少的重要組成部分。

6 如何編寫嵌入式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ā)出讀/寫請(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ì)磁盤等慢速設(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è)軟盤,就可以用從設(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)寫一個(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)程序工作的基本原理。既然是這樣,則編寫設(shè)備驅(qū)動(dòng)程序的主要工作就是編寫子函數(shù),并填充file_operations的各個(gè)域。

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

#include 《linux/types.h》

基本的類型定義#include 《linux/fs.h》

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

#include 《linux/mm.h》 #include 《linux/errno.h》 #include 《asm/segment.h》

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ū)全部寫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à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

”); 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ō)寫好了,文件名就叫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)程序。寫一個(gè)小小的測(cè)試程序。

#include 《stdio.h》 #include 《sys/types.h》

#include 《sys/stat.h》 #include 《fcntl.h》 main()

{

int testdev; int i; char buf[10];

testdev = open(“/dev/test”,O_RDWR);

if

testdev == -1 )

{

printf(“Cann’t open file

”); exit(0); }read(testdev,buf,10);

for (i = 0; i 《 10;i++) printf(“%d

”,buf[i]);

close(testdev);

}

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

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

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

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

首先在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)、寫(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)體,所以在寫驅(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)長(zhǎng)inux源代碼都是C語(yǔ)言編寫的,對(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í)寫一個(gè)驅(qū)動(dòng)模塊就是填充上述的結(jié)構(gòu)體,根據(jù)設(shè)備的功能和用途寫相應(yīng)的函數(shù),然后對(duì)應(yīng)到結(jié)構(gòu)體中的指針,然后再寫一個(gè)入口一個(gè)出口(就是模塊編程中的init和exit)就可以了,一般情況下入口程序就是在注冊(cè)platform_device和platform_driver(當(dāng)然,這樣說(shuō)是針對(duì)以platform模式編寫驅(qū)動(dòng)程序)。

8 嵌入式書籍推薦

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

2. Linux方面的書:

《ARM體系架構(gòu)與編程》

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

《Linux設(shè)備驅(qū)動(dòng)》,老外寫的那本

《linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)詳解》

《linux內(nèi)核完全注釋》

《Linux內(nèi)核情景分析》

在做驅(qū)動(dòng)的時(shí)候,肯定會(huì)用到與內(nèi)核相關(guān)的東西,或者需要和內(nèi)核中的某些模塊配合,這樣你也要理解內(nèi)核的某些部分是如何實(shí)現(xiàn)的,最后,你應(yīng)該可以很好的掌握l(shuí)inux的內(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)槟阒挥型耆私饬薼inux的使用,你才可以為它開(kāi)發(fā)符合它規(guī)則的項(xiàng)目。

審核編輯 :李倩

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(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)投訴

原文標(biāo)題:長(zhǎng)文 | 嵌入式驅(qū)動(dòng)方面,這篇文章講絕了!

文章出處:【微信號(hào):c-stm32,微信公眾號(hào):STM32嵌入式開(kāi)發(fā)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    北京迅為RK3568開(kāi)發(fā)嵌入式學(xué)習(xí)之Linux驅(qū)動(dòng)全新更新-CAN+

    北京迅為RK3568開(kāi)發(fā)嵌入式學(xué)習(xí)之Linux驅(qū)動(dòng)全新更新-CAN+
    的頭像 發(fā)表于 09-04 15:29 ?360次閱讀
    北京迅為RK3568<b class='flag-5'>開(kāi)發(fā)</b>板<b class='flag-5'>嵌入式</b>學(xué)習(xí)之Linux<b class='flag-5'>驅(qū)動(dòng)</b>全新更新-CAN+

    嵌入式linux開(kāi)發(fā)的基本步驟有哪些?

    嵌入式Linux開(kāi)發(fā)是一個(gè)復(fù)雜的過(guò)程,涉及到硬件選擇、操作系統(tǒng)移植、驅(qū)動(dòng)開(kāi)發(fā)、應(yīng)用程序開(kāi)發(fā)等多個(gè)方面。以下是
    的頭像 發(fā)表于 09-02 09:11 ?345次閱讀

    嵌入式linux開(kāi)發(fā)板怎么操作

    嵌入式Linux開(kāi)發(fā)板是一種基于Linux操作系統(tǒng)的嵌入式系統(tǒng)開(kāi)發(fā)平臺(tái)。它通常包括一個(gè)處理器、內(nèi)存、存儲(chǔ)器、輸入/輸出接口等硬件組件,以及一個(gè)基于Linux的操作系統(tǒng)和
    的頭像 發(fā)表于 09-02 09:09 ?305次閱讀

    嵌入式linux開(kāi)發(fā)板芯片的工作原理

    嵌入式Linux開(kāi)發(fā)板是一種基于Linux操作系統(tǒng)的嵌入式系統(tǒng)開(kāi)發(fā)平臺(tái),它廣泛應(yīng)用于工業(yè)控制、智能家居、智能交通、醫(yī)療設(shè)備等領(lǐng)域。 嵌入式L
    的頭像 發(fā)表于 09-02 09:07 ?302次閱讀

    嵌入式軟件開(kāi)發(fā)與AI整合

    嵌入式軟件開(kāi)發(fā)與AI整合是當(dāng)前技術(shù)發(fā)展的重要趨勢(shì)之一。隨著人工智能技術(shù)的快速發(fā)展,嵌入式系統(tǒng)越來(lái)越多地集成了AI算法,以實(shí)現(xiàn)更復(fù)雜的智能功能。以下是關(guān)于嵌入式軟件
    的頭像 發(fā)表于 07-31 09:25 ?634次閱讀
    <b class='flag-5'>嵌入式</b>軟件<b class='flag-5'>開(kāi)發(fā)</b>與AI整合

    嵌入式開(kāi)發(fā)前景怎么樣?

    嵌入式開(kāi)發(fā)前景非常廣闊,這主要得益于物聯(lián)網(wǎng)、人工智能、大數(shù)據(jù)等技術(shù)的快速發(fā)展,以及嵌入式系統(tǒng)在各個(gè)領(lǐng)域的廣泛應(yīng)用。以下是對(duì)嵌入式開(kāi)發(fā)前景的詳細(xì)分析
    的頭像 發(fā)表于 07-10 09:00 ?2349次閱讀
    <b class='flag-5'>嵌入式開(kāi)發(fā)</b>前景怎么樣?

    嵌入式系統(tǒng)怎么學(xué)?

    嵌入式系統(tǒng)怎么學(xué)? 隨著物聯(lián)網(wǎng)、智能制造等新技術(shù)的興起,嵌入式系統(tǒng)的應(yīng)用范圍更加廣泛。包括但不限于工業(yè)控制、汽車電子、醫(yī)療設(shè)備、智能家居、智慧城市、消費(fèi)電子、通信設(shè)備等。學(xué)習(xí)嵌入式
    發(fā)表于 07-02 10:10

    fpga與嵌入式的區(qū)別 嵌入式和fpga開(kāi)發(fā)有什么關(guān)系

    的,無(wú)法進(jìn)行大規(guī)模的硬件級(jí)別的修改。這意味著FPGA能更靈活地適應(yīng)不同的應(yīng)用場(chǎng)景和變化的需求。 開(kāi)發(fā)周期:FPGA的開(kāi)發(fā)周期相對(duì)較短,因?yàn)樗梢酝ㄟ^(guò)重新編程來(lái)實(shí)現(xiàn)新功能。相比之下,嵌入式系統(tǒng)的
    的頭像 發(fā)表于 03-14 17:04 ?6666次閱讀

    嵌入式軟件開(kāi)發(fā)和軟件開(kāi)發(fā)的區(qū)別

    嵌入式軟件開(kāi)發(fā)和軟件開(kāi)發(fā)是兩個(gè)不同的概念,它們?cè)谝恍╆P(guān)鍵方面有著明顯的區(qū)別。嵌入式軟件開(kāi)發(fā)是指開(kāi)發(fā)
    的頭像 發(fā)表于 01-22 15:27 ?2124次閱讀

    嵌入式Linux開(kāi)發(fā)的三種方式

    嵌入式Linux開(kāi)發(fā)主要有三種方式:裸機(jī)開(kāi)發(fā)、SDK開(kāi)發(fā)驅(qū)動(dòng)開(kāi)發(fā)。
    的頭像 發(fā)表于 01-22 14:22 ?898次閱讀

    到底要不要放棄嵌入式這條路?

    。 興趣激發(fā)動(dòng)力 決定是否堅(jiān)持這條路的重要因素之一就是個(gè)人興趣。如果你對(duì)嵌入式技術(shù)充滿熱情,喜歡從事硬件設(shè)計(jì)、驅(qū)動(dòng)開(kāi)發(fā)嵌入式系統(tǒng)編程等工作,那么堅(jiān)持下去是有意義的。興趣是最好的老師
    發(fā)表于 12-07 10:43

    嵌入式開(kāi)發(fā)測(cè)試秘訣

    。 多年前,一位工程師為了對(duì)嵌入式擁有更深層次理解的追求,曾發(fā)出這樣的疑問(wèn):“我怎么才能知道并懂得我的系統(tǒng)到底在干些什么呢?”。同時(shí)代的嵌入式開(kāi)發(fā)人員問(wèn)得最多的問(wèn)題大都圍繞“我怎么才能使程序跑得更快”、“什么編譯器最
    的頭像 發(fā)表于 11-24 16:18 ?483次閱讀

    嵌入式軟件的開(kāi)發(fā)流程

    電子發(fā)燒友網(wǎng)站提供《嵌入式軟件的開(kāi)發(fā)流程.doc》資料免費(fèi)下載
    發(fā)表于 11-17 14:39 ?0次下載
    <b class='flag-5'>嵌入式</b>軟件的<b class='flag-5'>開(kāi)發(fā)</b>流程

    嵌入式系統(tǒng)的組成及開(kāi)發(fā)流程

    電子發(fā)燒友網(wǎng)站提供《嵌入式系統(tǒng)的組成及開(kāi)發(fā)流程.ppt》資料免費(fèi)下載
    發(fā)表于 11-17 14:35 ?1次下載
    <b class='flag-5'>嵌入式</b>系統(tǒng)的組成及<b class='flag-5'>開(kāi)發(fā)</b>流程

    嵌入式開(kāi)發(fā)學(xué)習(xí)路線分享

    的實(shí)際應(yīng)用范疇,可以說(shuō)目前市場(chǎng)上對(duì)嵌入式開(kāi)發(fā)人才的需求越來(lái)越大,很多小伙伴都想通過(guò)培訓(xùn)學(xué)習(xí)的方式走向嵌入式開(kāi)發(fā)相關(guān)的崗位。下面,小編就給各位小伙伴介紹一個(gè)簡(jiǎn)單易懂的學(xué)
    的頭像 發(fā)表于 11-15 11:51 ?1275次閱讀
    <b class='flag-5'>嵌入式開(kāi)發(fā)</b>學(xué)習(xí)路線分享