實(shí)踐?|?Linux內(nèi)核編譯、啟動(dòng)和相關(guān)驅(qū)動(dòng)構(gòu)建
01修改與編譯內(nèi)核前面小哥主要是跟大家講解了uboot的燒錄、使用等等,而對(duì)于嵌入式Linux環(huán)境而言其實(shí)主要是分為三大塊 : uboot,Linux Kernel(內(nèi)核),文件系統(tǒng),當(dāng)然高版本的內(nèi)核存在設(shè)備樹文件等等,不過感覺還不足以認(rèn)為是一大塊,只能說是驅(qū)動(dòng)的一部分。那么本文就主要是通過uboot把Linux系統(tǒng)運(yùn)行起來,而掛載根文件系統(tǒng)并進(jìn)入終端命令行估計(jì)得到下一篇文章了。如果大家手上有已經(jīng)移植或制作OK的Linux內(nèi)核image和文件系統(tǒng),只需要將他們燒錄到板子上的對(duì)應(yīng)存儲(chǔ)位置上,然后設(shè)置uboot中的一些啟動(dòng)參數(shù)即可完成整個(gè)Linux系統(tǒng)的啟動(dòng)。然而每塊開發(fā)板的外設(shè)總會(huì)存在或多或少的差異,這樣就需要開發(fā)人員修改相應(yīng)的與硬件交互的代碼(Linux驅(qū)動(dòng)),或者調(diào)整各部分在內(nèi)存分布的大小與地址等,以適應(yīng)新的硬件平臺(tái),這個(gè)過程就叫做移植。看起來移植很高大上,相對(duì)Linux內(nèi)核這么龐大的代碼而言還是小部分,畢竟Linux系統(tǒng)在開發(fā)過程中都會(huì)考慮與硬件相關(guān)部分進(jìn)行的分離,并且大部分開發(fā)板都會(huì)參考官方發(fā)布的單板來進(jìn)行布局。好了,那么小哥就大致講解一下這個(gè)過程。02移植與燒錄內(nèi)核這里使用的是百問網(wǎng)科技超級(jí)老的jz2440開發(fā)板,他們提供了對(duì)應(yīng)內(nèi)核版本的適配其開發(fā)板的移植補(bǔ)丁,也就是說在Linux-3.4.2原始的內(nèi)核源碼上通過提供的補(bǔ)丁包自動(dòng)化的進(jìn)行代碼的修改以適應(yīng)當(dāng)前開發(fā)板,從單片機(jī)的角度看來就是修改一些引腳,配置驅(qū)動(dòng)等等。然而小哥手頭板子的NandFlash上存在壞塊,uboot,內(nèi)核、文件系統(tǒng)等都會(huì)燒錄到NandFlash上,你可以認(rèn)為就是單片機(jī)的Flash,不過單片機(jī)的Flash大部分為NorFlash,而由于存在壞塊所以對(duì)相關(guān)的分區(qū)進(jìn)行調(diào)整,否則壞塊會(huì)導(dǎo)致相應(yīng)的燒錄文件不完整而啟動(dòng)失敗。由于后面打算移植QT,而之前拿到的補(bǔ)丁包沒有移植好觸摸驅(qū)動(dòng)和網(wǎng)卡驅(qū)動(dòng),即使打了之前拿到的補(bǔ)丁還需要繼續(xù)進(jìn)行相關(guān)代碼上的移植,那慢慢來,先把一些適配的補(bǔ)丁打上:1tar?jxvf?linux-3.4.2.tar.bz2
2cd?linux-3.4.2/
3patch?-p1?<../linux-3.4.2_100ask.patch
Linux-3.4.2_100ask.patch就是百問網(wǎng)提供的補(bǔ)丁包,所謂的補(bǔ)丁包不是什么高級(jí)東西,就是通過patch命令根據(jù).patch文件的修改描述,直接添加和修改內(nèi)核中文件的內(nèi)容,與我們手動(dòng)修改本質(zhì)上是一樣的。不過它把所有的差異都整理成了一個(gè)文件,實(shí)現(xiàn)了一種一鍵修改源文件工程的目的,因?yàn)閮?nèi)核源碼官方都可以獲取,而我們只需要一個(gè)補(bǔ)丁包就可以通過打補(bǔ)丁把官方源碼改成適配自己?jiǎn)伟宓脑创a,美滋滋~03內(nèi)核分區(qū)修改前面uboot也存在這樣的一個(gè)默認(rèn)分區(qū)表kernel部分分了36M,同樣內(nèi)核中的默認(rèn)分區(qū)表中的內(nèi)核區(qū)域也分配了36M。
很多人該問了為什么有兩個(gè)默認(rèn)的分區(qū)表?前面小哥講解了存儲(chǔ)地址與運(yùn)行地址,Linux內(nèi)核中的相關(guān)操作都是基于Linux內(nèi)核中的分區(qū)表,比如uboot向Linux內(nèi)核的傳參中:就是告訴內(nèi)核加載文件系統(tǒng)需要在MTD的第三個(gè)區(qū)分進(jìn)行加載,那么uboot在進(jìn)行文件系統(tǒng)燒錄的時(shí)候就需要把燒錄文件放到第三個(gè)分區(qū)地址區(qū)域,且與內(nèi)核中的分區(qū)地址一致,不然Linux內(nèi)核掛載文件失敗。其實(shí)對(duì)于uboot中的分區(qū),僅僅只是為了部署Linux環(huán)境而設(shè)置的,一旦啟動(dòng)內(nèi)核了uboot的生命周期就結(jié)束了,比如之前我們?cè)趗boot中使用nand write 源地址 目的地址 長(zhǎng)度,我們也可以直接簡(jiǎn)化為對(duì)對(duì)應(yīng)分區(qū)的操作:nand write 0x30000000 kernel。從上圖中uboot的環(huán)境變量bootcmd中使用的kernel,也是類似的使用方法。那為什么要把Linux內(nèi)核分區(qū)設(shè)置36M呢?其實(shí)我當(dāng)前編譯的Linux內(nèi)核映像也才幾M的大小,主要是Linux內(nèi)核中這種分區(qū)方式是連續(xù)的,前一個(gè)區(qū)的結(jié)束地址是后一個(gè)分區(qū)的起始地址,然而經(jīng)過小哥測(cè)試,剛好文件系統(tǒng)分區(qū)前面一部分NandFlash存在壞塊,這樣會(huì)導(dǎo)致文件系統(tǒng)不完整,后期掛載會(huì)失敗,所以這樣通過加載內(nèi)核分區(qū)以使得文件系統(tǒng)分區(qū)地址后移的方式來規(guī)避掉這些壞塊,同時(shí)對(duì)于內(nèi)核部分由于其燒錄文件不大,有效文件不會(huì)燒錄到壞塊部分,算是一個(gè)捷徑。03網(wǎng)卡驅(qū)動(dòng)修改uboot雖然可以進(jìn)行TFTP網(wǎng)絡(luò)服務(wù),但是并不意味著Linux內(nèi)核也可以正常使用,他們兩個(gè)驅(qū)動(dòng)是分開的,一旦uboot成功啟動(dòng)內(nèi)核以后uboot的生命周期就結(jié)束了。Linux內(nèi)核也必須擁有正確的網(wǎng)卡驅(qū)動(dòng)才能夠使用相應(yīng)的網(wǎng)絡(luò)服務(wù),其實(shí)這跟我們windows系統(tǒng)是一樣的,當(dāng)我們電腦沒有相應(yīng)的驅(qū)動(dòng),相應(yīng)的硬件也是無法使用的,比如最好用的網(wǎng)絡(luò)文件系統(tǒng)NFS。由于我們使用的是dm9000網(wǎng)卡,且mini2440單板已經(jīng)得到很好的支持,所以我們參考直接移植過來即可,主要是填充相應(yīng)的平臺(tái)設(shè)備結(jié)構(gòu)體以描述dm9000網(wǎng)卡資源并注冊(cè)與驅(qū)動(dòng)匹配。首先要包含dm9000網(wǎng)卡的頭文件,以便使用到其頭文件中的宏定義或者數(shù)據(jù),然后使用resource結(jié)構(gòu)體描述dm9000網(wǎng)卡的一些資源,包括IO資源和中斷資源,并且使用平臺(tái)設(shè)備platformdevice來描述dm9000網(wǎng)卡,以便后續(xù)總線上device與driver匹配在,這樣也就實(shí)現(xiàn)了設(shè)備與驅(qū)動(dòng)的分離。最后把平臺(tái)設(shè)備作為smdk2440眾多設(shè)備初始化中的一員加入到initdata中,以便系統(tǒng)啟動(dòng)的時(shí)候便加載設(shè)備和匹配驅(qū)動(dòng)。有了以上移植,基本上單板的網(wǎng)卡驅(qū)動(dòng)就搞定了,相應(yīng)的網(wǎng)絡(luò)相關(guān)的服務(wù)就可以使用了,不像單片機(jī)那樣你還要直接移植相應(yīng)的網(wǎng)絡(luò)協(xié)議棧等等,一旦搞定了驅(qū)動(dòng),基本上跟windows系統(tǒng)上面開發(fā)應(yīng)用程序大同小異~04LCD驅(qū)動(dòng)程序修改對(duì)于LCD的支持其實(shí)與前面網(wǎng)卡驅(qū)動(dòng)修改其實(shí)是類似的,還是采用Linux設(shè)備與驅(qū)動(dòng)分開的思想,基本上驅(qū)動(dòng)部分不用太多修改,僅僅只需要把驅(qū)動(dòng)部分根據(jù)Linux提供的框架進(jìn)行相應(yīng)的描述填充即可,說得直白一點(diǎn)就是填充再賦值結(jié)構(gòu)體,你可以認(rèn)為這些都是固定的套路吧~這里小哥使用的是4.3寸的屏幕,所以進(jìn)行如下填充與配置:同樣因?yàn)?440是自帶LCD控制器的,唯一要做的就是把LCD控制這塊以及LCD屏幕的屬性描述清楚,如上面各個(gè)結(jié)構(gòu)體設(shè)置所選擇LCD的相關(guān)屬性,比如尺寸,刷新時(shí)序等等,最后把整個(gè)結(jié)構(gòu)體填充好并注冊(cè)。同時(shí)記得確認(rèn)一下menconfig里面是否已經(jīng)選擇了LCD_FB,并選擇編譯到Linux內(nèi)核,執(zhí)行命令:
1make?menuconfig?CROSS_COMPILE=/home/book/WorkSpace/Qt/src/arm-linux-gcc-4.4.3/opt/FriendlyARM/toolschain/4.4.3/bin/arm-linux-?ARCH=arm?-j8
1Device?Drivers??--->?
2??Graphics?support??--->?
3????Support?for?frame?buffer?devices??--->?
4??????S3C2410?LCD?framebuffer?support??配置好以后,記得save到對(duì)應(yīng)的.config即可,這樣編譯前的配置才能夠生效。05觸摸驅(qū)動(dòng)編譯與移植玩單片機(jī)的小伙伴都知道電阻觸摸屏,就是通過獲得屏幕橫縱的AD采樣值最終來定位屏幕上的位置,而S3C2440也是存在觸摸屏的外設(shè)接口的,我們通過配置觸摸屏外設(shè)接口,即可驅(qū)動(dòng)觸摸屏獲得相應(yīng)的ADC值最終定位到屏幕上所點(diǎn)擊的位置。在Linux中對(duì)于鼠標(biāo)、觸摸等等都屬于輸入設(shè)備,所以這類驅(qū)動(dòng)都可以歸為輸入子系統(tǒng)input,那么我們只需要注冊(cè)一個(gè)輸入子系統(tǒng)即完成了觸摸屏驅(qū)動(dòng)。觸摸驅(qū)動(dòng)程序主要分為這樣幾步,首先獲得一個(gè)輸入設(shè)備結(jié)構(gòu)體,然后根據(jù)觸摸的特性進(jìn)行相關(guān)的事件的配置,因?yàn)檩斎胂到y(tǒng)都是以事件的方式上報(bào)給系統(tǒng),不同的事件當(dāng)然配置也就不同,配置好了以后就把輸入設(shè)備結(jié)構(gòu)體注冊(cè)到系統(tǒng),以便設(shè)備識(shí)別。而當(dāng)所配置的事件一旦條件觸發(fā),就會(huì)把觸發(fā)信號(hào)和數(shù)據(jù)通過input_report上報(bào)給系統(tǒng),供系統(tǒng)使用,所以單片機(jī)你想做得通用化,也可以直接這么玩,不過考慮到單片機(jī)的簡(jiǎn)潔,還是慎重考慮~雖然我們可以直接把該驅(qū)動(dòng)程序編譯成.ko驅(qū)動(dòng)程序,可是這樣需要每次內(nèi)核啟動(dòng)完成以后就需要重新加載驅(qū)動(dòng),有點(diǎn)麻煩,所以考慮把它編譯到Linux內(nèi)核中。要把驅(qū)動(dòng)程序添加到內(nèi)核需要做三件事:1)添加源碼到相應(yīng)目錄;2)在相應(yīng)的Kconfig文件中增加編譯選項(xiàng);3)在makefile中增加相應(yīng)的編譯項(xiàng)。前面我們大致編寫了源碼并且放到了相應(yīng)的目錄,這里就只需要完成后面的兩項(xiàng),這兩項(xiàng)可能相應(yīng)的語法規(guī)則剛開始并不是很熟,不過可以查閱相應(yīng)的知識(shí)補(bǔ)充,也可以直接照著其他touch驅(qū)動(dòng)類似編寫即可。
比如s3c2410的Kconfig如下編寫:于是我們可以模仿著把S3C2440類似的添加到后面:
第二步完成,接下來在當(dāng)前目錄的Makefile添加編譯項(xiàng)目:
照著S3C2410的來即可。雖然我們完成上面的三步,但只是完成了能夠提供選擇的是否編譯進(jìn)內(nèi)核的驅(qū)動(dòng)選項(xiàng),在menuconfig菜單中你可以看到,而到底最終是否編譯到內(nèi)核,還需要在menconfig菜單中進(jìn)行配置選擇并保存到config中。1Device?Drivers??--->?
2??Input?device?support??--->??
3????Touchscreens??--->至此,觸摸的驅(qū)動(dòng)就編寫并添加到了內(nèi)核中。06內(nèi)核的編譯與燒錄一切準(zhǔn)備就緒,那就是編譯內(nèi)核了,編譯內(nèi)核的目的就是為了獲得Linux kernel映像文件,最終燒錄到板子上的內(nèi)容。如下是我使用的編譯命令,由于沒有把相應(yīng)的路徑放到環(huán)境變量,所以這里就制定了編譯器路徑,比較長(zhǎng)。1make?uImage?CROSS_COMPILE=/home/book/WorkSpace/Qt/src/arm-linux-gcc-4.4.3/opt/FriendlyARM/toolschain/4.4.3/bin/arm-linux-?ARCH=arm?-j8
如果編譯過程中遇到什么不理解的error,基本上都是根據(jù)所報(bào)錯(cuò)誤的提示,進(jìn)行網(wǎng)絡(luò)查找,一般都可以得到解決,因?yàn)榇蠹叶加龅竭^~最后順利編譯內(nèi)核成功,如下是編譯結(jié)果:以上的輸出信息,我們也可以了解到想要的uImage所在路徑,以及文件的大小,類型和入口地址等。
我們借助uboot直接通過TFTP服務(wù)把uImage先下載到SDRAM中,然后在使用NandFlash命令燒錄到Flash中對(duì)應(yīng)的分區(qū)即可。其實(shí)燒錄過程在往期的uboot中已經(jīng)說得很詳細(xì)了,這里主要提兩點(diǎn):1)由于uboot是一個(gè)單任務(wù)的裸機(jī)程序,所以連接好網(wǎng)線以后,你的電腦網(wǎng)絡(luò)狀態(tài)還是沒有連接的,所以需要uboot主動(dòng)發(fā)起網(wǎng)絡(luò),電腦端就會(huì)有網(wǎng)絡(luò)鏈接狀態(tài)顯示了。
2)在進(jìn)行網(wǎng)絡(luò)通信過程中,要記得關(guān)掉電腦主機(jī)的防火墻,以便鏈接失敗。我們使用TFTP服務(wù),需要設(shè)置好服務(wù)器IP,也就是我們的電腦主機(jī)IP地址,然后通過TFTP命令獲得相應(yīng)的內(nèi)核文件,實(shí)驗(yàn)結(jié)果如下:
這樣我們就把uimage下載到了SDRAM的0x30000000的位置,接下來我們需要把他燒錄到對(duì)應(yīng)的NandFlash的Kernel分區(qū)上,使用如下命令:1nand?erase.part?kernel
2nand?write?0x30000000?kernel重新啟動(dòng)開發(fā)板,即可看到成功啟動(dòng)了內(nèi)核:但是最終由于我們還沒有為L(zhǎng)inux系統(tǒng)構(gòu)建文件系統(tǒng),系統(tǒng)啟動(dòng)還需要一些必備的啟動(dòng)文件和工具,最終會(huì)報(bào)錯(cuò)。
不過我們今天的目標(biāo)達(dá)到,Linux內(nèi)核得到了啟動(dòng),并且移植好了我們想要的一些驅(qū)動(dòng)。
最? 后?
這里小哥就介紹了一下Linux內(nèi)核的移植、燒錄、相關(guān)驅(qū)動(dòng)的相關(guān)知識(shí),希望本文能夠?qū)δ阌袔椭?,下期帶來文件系統(tǒng)的構(gòu)建過程~往期干貨:往期推薦
嵌入式項(xiàng)目生成器,了解一下!一個(gè)清晰的LCD驅(qū)動(dòng)編寫思路(附代碼分析)RT-Thread和Freertos的區(qū)別?程序如何運(yùn)行?編譯、鏈接、裝入?