【正点原子Linux连载】第三十三章 U-Boot移植 -摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0
1)實(shí)驗(yàn)平臺(tái):正點(diǎn)原子阿爾法Linux開發(fā)板
2)平臺(tái)購買地址:https://item.taobao.com/item.htm?id=603672744434
2)全套實(shí)驗(yàn)源碼+手冊+視頻下載地址:http://www.openedv.com/thread-300792-1-1.html
3)對(duì)正點(diǎn)原子Linux感興趣的同學(xué)可以加群討論:935446741
4)關(guān)注正點(diǎn)原子公眾號(hào),獲取最新資料更新
第三十三章 U-Boot移植
上一章節(jié)我們詳細(xì)的分析了uboot的啟動(dòng)流程,對(duì)uboot有了一個(gè)初步的了解。前兩章我們都是使用的正點(diǎn)原子提供的uboot,本章我們就來學(xué)習(xí)如何將NXP官方的uboot移植到正點(diǎn)原子的I.MX6ULL開發(fā)板上,學(xué)習(xí)如何在uboot中添加我們自己的板子。33.1 NXP官方開發(fā)板uboot編譯測試
33.1.1 查找NXP官方的開發(fā)板默認(rèn)配置文件
uboot的移植并不是說我們完完全全的從零開始將uboot移植到我們現(xiàn)在所使用的開發(fā)板或者開發(fā)平臺(tái)上。這個(gè)對(duì)于我們來說基本是不可能的,這個(gè)工作一般是半導(dǎo)體廠商做的,半導(dǎo)體廠商負(fù)責(zé)將uboot移植到他們的芯片上,因此半導(dǎo)體廠商都會(huì)自己做一個(gè)開發(fā)板,這個(gè)開發(fā)板就叫做原廠開發(fā)板,比如大家學(xué)習(xí)STM32的時(shí)候聽說過的discover開發(fā)板就是ST自己做的。半導(dǎo)體廠商會(huì)將uboot移植到他們自己的原廠開發(fā)板上,測試好以后就會(huì)將這個(gè)uboot發(fā)布出去,這就是大家常說的原廠BSP包。我們一般做產(chǎn)品的時(shí)候就會(huì)參考原廠的開發(fā)板做硬件,然后在原廠提供的BSP包上做修改,將uboot或者linux kernel移植到我們的硬件上。這個(gè)就是uboot移植的一般流程:
①、在uboot中找到參考的開發(fā)平臺(tái),一般是原廠的開發(fā)板。
②、參考原廠開發(fā)板移植uboot到我們所使用的開發(fā)板上。
正點(diǎn)原子的I.MX6ULL開發(fā)板參考的是NXP官方的I.MX6ULL EVK開發(fā)板做的硬件,因此我們在移植uboot的時(shí)候就可以以NXP官方的I.MX6ULL EVK開發(fā)板為藍(lán)本。
本章我們是將NXP官方的uboot移植到正點(diǎn)原子的I.MX6ULL開發(fā)板上,NXP官方的uboot放到了開發(fā)板光盤中,路徑為:1、例程源碼->4、NXP官方原版Uboot和Linux->uboot-imx-rel_imx_4.1.15_2.1.0_ga.tar.bz2。將uboot-imx-rel_imx_4.1.15_2.1.0_ga.tar.bz2發(fā)送到Ubuntu中并解壓,然后創(chuàng)建VSCode工程。
在移植之前,我們先編譯一下NXP官方I.MX6ULL EVK開發(fā)板對(duì)應(yīng)的uboot,首先是配置uboot,configs目錄下有很多跟I.MX6UL/6ULL有關(guān)的配置如圖33.1.1.1所示,
圖33.1.1.1 NXP官方I.MX6UL/6ULL默認(rèn)配置文件
從圖33.1.1.1可以看出有很多的默認(rèn)配置文件,其中以mx6ul開頭的是I.MX6UL芯片的,mx6ull開頭的是I.MX6ULL開發(fā)板的。I.MX6UL/6ULL有9x9mm和14x14mm兩種尺寸的,所以我們可以看到會(huì)有mx6ull_9x9和mx6ull_14x14開頭的默認(rèn)配置文件。我們使用的是14x14mm的芯片,所以關(guān)注mx6ull_14x14開頭的默認(rèn)配置文件。正點(diǎn)原子的I.MX6ULL有EMMC和NAND兩個(gè)版本的,因此我們最終只需要關(guān)注mx6ull_14x14_evk_emmc_defconfig和mx6ull_14x14_evk_nand_defconfig這兩個(gè)配置文件就行了。本章我們講解EMMC版本的移植(NAND版本移植很多類似),所以使用mx6ull_14x14_evk_emmc_defconfig作為默認(rèn)配置文件。
33.1.2 編譯NXP官方開發(fā)板對(duì)應(yīng)的uboot
找到NXP官方I.MX6ULL EVK開發(fā)板對(duì)應(yīng)的默認(rèn)配置文件以后就可以編譯一下,使用如下命令編譯uboot:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_14x14_evk_emmc_defconfig
make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j16
編譯完成以后結(jié)果如圖33.1.2.1所示:
圖33.1.2.1 編譯結(jié)果
從圖33.1.2.1可以看出,編譯成功。我們在編譯的時(shí)候需要輸入ARCH和CORSS_COMPILE這兩個(gè)變量的值,這樣太麻煩了。我們可以直接在頂層Makefile中直接給ARCH和CORSS_COMPILE賦值,修改如圖33.1.2.2所示:
圖33.1.2.2 添加ARCH和CROSS_COMPILE值
圖33.1.2.2中的250、251行就是直接給ARCH和CROSS_COMPILE賦值,這樣我們就可以使用如下簡短的命令來編譯uboot了:
make mx6ull_14x14_evk_emmc_defconfig
make V=1 -j16
如果既不想修改uboot的頂層Makefile,又想編譯的時(shí)候不用輸入那么多,那么就直接創(chuàng)建個(gè)shell腳本就行了,shell腳本名為mx6ull_14x14_emmc.sh,然后在shell腳本里面輸入如下內(nèi)容:
示例代碼33.1.2.1 mx6ull_14x14_emmc.sh文件
./mx6ull_14x14_evk_emmc.sh
編譯完成以后會(huì)生成u-boot.bin、u-boot.imx等文件,但是這些文件是NXP官方I.MX6ULL EVK開發(fā)板。能不能用到正點(diǎn)原子的I.MX6ULL開發(fā)板上呢?試一下不就知道了!
33.1.3 燒寫驗(yàn)證與驅(qū)動(dòng)測試
將imxdownload軟件拷貝到uboot源碼根目錄下,然后使用imxdownload軟件將u-boot.bin燒寫到SD卡中,燒寫命令如下:
chmod 777 imxdownload //給予imxdownload可執(zhí)行權(quán)限
./imxdownload u-boot.bin /dev/sdd //燒寫到SD卡中,不能燒寫到/dev/sda或sda1里面
燒寫完成以后將SD卡插入I.MX6U-ALPHA開發(fā)板的TF卡槽中,最后設(shè)置開發(fā)板從SD卡啟動(dòng)。打開SecureCRT,設(shè)置好開發(fā)板所使用的串口并打開,復(fù)位開發(fā)板,SecureCRT接收到如下圖33.1.3.1所示信息:
圖33.1.3.1 uboot啟動(dòng)信息
從圖33.1.3.1可以看出,uboot啟動(dòng)正常,雖然我們用的是NXP官方I.MX6ULL開發(fā)板的uboot,但是在正點(diǎn)原子的I.MX6ULL開發(fā)板上是可以正常啟動(dòng)的。而且DRAM識(shí)別正確,為512MB,如果用的NAND版本的核心版的話uboot啟動(dòng)會(huì)失敗!因?yàn)镹AND核心版用的256MB的DRAM。
1、SD卡和EMMC驅(qū)動(dòng)檢查
檢查一下SD卡和EMMC驅(qū)動(dòng)是否正常,使用命令mmc list列出當(dāng)前的MMC設(shè)備,結(jié)果如圖33.1.3.2所示:
圖33.1.3.2 emmc設(shè)備檢查
從圖33.1.3.2可以看出當(dāng)前有兩個(gè)MMC設(shè)備,檢查每個(gè)MMC設(shè)備信息,先檢查MMC設(shè)備0,輸入如下命令:
mmc dev 0
mmc info
結(jié)果如圖33.1.3.3所示:
圖33.1.3.3 mmc設(shè)備0信息
從圖33.1.3.3可以看出,mmc設(shè)備0是SD卡,SD卡容量為14.8GB,這個(gè)和我所使用的SD卡信息相符,說明SD卡驅(qū)動(dòng)正常。再來檢查MMC設(shè)備1,輸入如下命令:
mmc dev 1
mmc info
結(jié)果如圖33.1.3.4所示:
圖33.1.3.4 mmc設(shè)備1信息
從圖33.1.3.4可以看出,mmc設(shè)備1為EMMC,容量為7.3GB,說明EMMC驅(qū)動(dòng)也成功,SD卡和EMMC的驅(qū)動(dòng)都沒問題。
2、LCD驅(qū)動(dòng)檢查
如果uboot中的LCD驅(qū)動(dòng)正確的話,啟動(dòng)uboot以后LCD上應(yīng)該會(huì)顯示出NXP的logo,如下圖33.1.3.5所示:
圖33.1.3.5 uboot LCD界面
如果你用的不是正點(diǎn)原子的4.3寸480x272分辨率的屏幕的話,那么LCD就不會(huì)顯示33.1.3.5所示logo界面。因?yàn)镹XP官方I.MX6ULL開發(fā)板的屏幕就是4.3寸480x272分辨率的,所以u(píng)boot默認(rèn)LCD驅(qū)動(dòng)是4.3寸480x272分辨率的。如果使用其他分辨率的LCD就需要修改LCD驅(qū)動(dòng),這里我們先不修改LCD驅(qū)動(dòng)了,稍后我們在講解如何修改uboot中的LCD驅(qū)動(dòng),我們只需要記得,uboot的LCD需要修改就行了。
3、網(wǎng)絡(luò)驅(qū)動(dòng)
uboot啟動(dòng)的時(shí)候提示“Board Net Initialization Failed”和“No ethernet found.”這兩行,說明網(wǎng)絡(luò)驅(qū)動(dòng)也有問題,正常情況下應(yīng)該是如圖33.1.3.6所示提示:
圖33.1.3.6 網(wǎng)絡(luò)信息
現(xiàn)在沒有圖33.1.3.6中的信息,那更別說ping一下ubuntu主機(jī)了,說明當(dāng)前uboot的網(wǎng)絡(luò)部驅(qū)動(dòng)也是有問題的,這是因?yàn)檎c(diǎn)原子開發(fā)板的網(wǎng)絡(luò)芯片復(fù)位引腳和NXP官方開發(fā)板不一樣,因此需要修改驅(qū)動(dòng)。
總結(jié)一下NXP官方I.MX6ULL EVK開發(fā)板的uboot在正點(diǎn)原子EMMC版本I.MX6ULL 開發(fā)板上的運(yùn)行情況:
①、uboot啟動(dòng)正常,DRAM識(shí)別正確,SD卡和EMMC驅(qū)動(dòng)正常。
②、uboot里面的LCD驅(qū)動(dòng)默認(rèn)是給4.3寸480x272分辨率的,如果使用的其他分辨率的屏幕需要修改驅(qū)動(dòng)。
③、網(wǎng)絡(luò)不能工作,識(shí)別不出來網(wǎng)絡(luò)信息,需要修改驅(qū)動(dòng)。
接下來我們要做的工作如下:
①、前面我們一直使用著NXP官方開發(fā)板的uboot配置,接下來需要在uboot中添加我們自己的開發(fā)板,也就是正點(diǎn)原子的I.MX6ULL開發(fā)板。
②、解決LCD驅(qū)動(dòng)和網(wǎng)絡(luò)驅(qū)動(dòng)的問題。
33.2 在U-Boot中添加自己的開發(fā)板
NXP官方uboot中默認(rèn)都是NXP自己的開發(fā)板,雖說我們可以直接在官方的開發(fā)板上直接修改,使uboot可以完整的運(yùn)行在我們的板子上。但是從學(xué)習(xí)的角度來講,這樣我們就不能了解到uboot是如何添加新平臺(tái)的。接下來我們就參考NXP官方的I.MX6ULL EVK開發(fā)板,學(xué)習(xí)如何在uboot中添加我們的開發(fā)板或者開發(fā)平臺(tái)。
33.2.1 添加開發(fā)板默認(rèn)配置文件
先在configs目錄下創(chuàng)建默認(rèn)配置文件,復(fù)制mx6ull_14x14_evk_emmc_defconfig,然后重命名為mx6ull_alientek_emmc_defconfig,命令如下:
cd configs
cp mx6ull_14x14_evk_emmc_defconfig mx6ull_alientek_emmc_defconfig
然后將文件mx6ull_alientek_emmc_defconfig中的內(nèi)容改成下面的:
示例代碼33.2.1.1 mx6ull_alientek_emmc_defconfig文件
33.2.2 添加開發(fā)板對(duì)應(yīng)的頭文件
在目錄include/configs下添加I.MX6ULL-ALPHA開發(fā)板對(duì)應(yīng)的頭文件,復(fù)制include/configs/mx6ullevk.h,并重命名為mx6ull_alientek_emmc.h,命令如下:
cp include/configs/mx6ullevk.h mx6ull_alientek_emmc.h
拷貝完成以后將:
mx6ull_alientek_emmc.h里面有很多宏定義,這些宏定義基本用于配置uboot,也有一些I.MX6ULL的配置項(xiàng)目。如果我們自己要想使能或者禁止uboot的某些功能,那就在mx6ull_alientek_emmc.h里面做修改即可。mx6ull_alientek_emmc.h里面的內(nèi)容比較多,去掉一些用不到的配置,精簡后的內(nèi)容如下:
示例代碼33.2.2.1 mx6ull_alientek_emmc.h文件
UART1_BASE= (ATZ1_BASE_ADDR + 0x20000)
=AIPS1_ARB_BASE_ADDR + 0x20000
=0x02000000 + 0x20000
=0X02020000
查閱I.MX6ULL參考手冊,UART1的寄存器基地址正是0X02020000,如圖33.2.2.1所示:
圖33.2.2.1 UART1寄存器地址表
第63、64行, EMMC接在I.MX6ULL的USDHC2上,宏CONFIG_SYS_FSL_ESDHC_ADDR為EMMC所使用接口的寄存器基地址,也就是USDHC2的基地址。
第67~72行,跟NAND相關(guān)的宏,因?yàn)镹AND和USDHC2的引腳沖突,因此如果使用NAND的只能使用一個(gè)USDHC設(shè)備(SD卡)。如果沒有使用NAND,那么就有兩個(gè)USDHC設(shè)備(EMMC和SD卡),宏CONFIG_SYS_FSL_USDHC_NUM表示USDHC數(shù)量。EMMC版本的核心版沒有用到NAND,所以CONFIG_SYS_FSL_USDHC_NUM=2。
第75~81,和I2C有關(guān)的宏定義,用于控制使能哪個(gè)I2C,I2C的速度為多少。
第92~96行,NAND的分區(qū)設(shè)置,如果使用NAND的話,默認(rèn)的NAND分區(qū)為:“mtdparts=gpmi-nand:64m(boot),16m(kernel),16m(dtb),1m(misc),-(rootfs) “,分區(qū)結(jié)果如表33.2.2.1所示:
范圍 大小 分區(qū)
0~63M 64M boot(uboot)
64~79M 16M kernel(linux內(nèi)核)
80~94M 16M dtb(設(shè)備樹)
95M 1M misc(雜項(xiàng))
96M – end 剩余的所有空間 rootfs(根文件系統(tǒng))
表33.2.2.1 NAND分區(qū)設(shè)置
NAND的分區(qū)是可以調(diào)整的,比如boot分區(qū)我們用不了64M這么大,因此可以將其改小,其他的分區(qū)一樣的。
第98~111行,宏CONFIG_MFG_ENV_SETTINGS定義了一些環(huán)境變量,使用MfgTool燒寫系統(tǒng)時(shí)候會(huì)用到這里面的環(huán)境變量。
第113~202行,通過條件編譯來設(shè)置宏CONFIG_EXTRA_ENV_SETTINGS,宏CONFIG_EXTRA_ENV_SETTINGS也是設(shè)置一些環(huán)境變量,此宏會(huì)設(shè)置bootargs這個(gè)環(huán)境變量,后面我們會(huì)詳細(xì)分析這個(gè)宏定義。
第204~217行,設(shè)置宏CONFIG_BOOTCOMMAND,此宏就是設(shè)置環(huán)境變量bootcmd的值。后面會(huì)詳細(xì)的分析這個(gè)宏定義。
第220~222行,設(shè)置命令memtest相關(guān)宏定義,比如使能命令memtest,設(shè)置memtest測試的內(nèi)存起始地址和內(nèi)存大小。
第224行,宏CONFIG_SYS_LOAD_ADDR表示linux kernel在DRAM中的加載地址,也就是linux kernel在DRAM中的存儲(chǔ)首地址,CONFIG_LOADADDR=0X80800000。
第225行,宏CONFIG_SYS_HZ為系統(tǒng)時(shí)鐘頻率,這里為1000Hz。
第227行,宏CONFIG_STACKSIZE為棧大小,這里為128KB。
第230行,宏CONFIG_NR_DRAM_BANKS為DRAM BANK的數(shù)量,I.MX6ULL只有一個(gè)DRAM BANK,我們也只用到了一個(gè)BANK,所以為1。
第231行,宏P(guān)HYS_SDRAM為I.MX6ULL的DRAM控制器MMDC0所管轄的DRAM范圍起始地址,也就是0X80000000。
第233行,宏CONFIG_SYS_SDRAM_BASE為DRAM的起始地址。
第234行,宏CONFIG_SYS_INIT_RAM_ADDR為I.MX6ULL內(nèi)部IRAM的起始地址(也就是OCRAM的起始地址),為0X00900000。
第235行,宏CONFIG_SYS_INIT_RAM_SIZE為I.MX6ULL內(nèi)部IRAM的大小(OCRAM的大小),為0X00040000=128KB。
第237~240行,宏CONFIG_SYS_INIT_SP_OFFSET和CONFIG_SYS_INIT_SP_ADDR與初始SP有關(guān),第一個(gè)為初始SP偏移,第二個(gè)為初始SP地址。
第256行,宏CONFIG_SYS_MMC_ENV_DEV為默認(rèn)的MMC設(shè)備,這里默認(rèn)為USDHC2,也就是EMMC。
第257行,宏CONFIG_SYS_MMC_ENV_PART為模式分區(qū),默認(rèn)為第0個(gè)分區(qū)。
第258行,宏CONFIG_MMCROOT設(shè)置進(jìn)入linux系統(tǒng)的根文件系統(tǒng)所在的分區(qū),這里設(shè)置為”/dev/mmcblk1p2”,也就是EMMC設(shè)備的第2個(gè)分區(qū)。第0個(gè)分區(qū)保存uboot,第1個(gè)分區(qū)保存linux鏡像和設(shè)備樹,第2個(gè)分區(qū)為Linux系統(tǒng)的根文件系統(tǒng)。
第277~291行,與NAND有關(guān)的宏定義,如果使用NAND的話。
第293行,宏CONFIG_ENV_SIZE為環(huán)境變量大小,默認(rèn)為8KB。
第294~308行,宏CONFIG_ENV_OFFSET為環(huán)境變量偏移地址,這里的偏移地址是相對(duì)于存儲(chǔ)器的首地址。如果環(huán)境變量保存在EMMC中的話,環(huán)境變量偏移地址為1264KB。如果環(huán)境變量保存在SPI FLASH中的話,偏移地址為7681024。如果環(huán)境變量保存在NAND中的話,偏移地址為60<<20(60MB),并且重新設(shè)置環(huán)境變量的大小為128KB。
第312~323行,與USB相關(guān)的宏定義。
第325~342行,與網(wǎng)絡(luò)相關(guān)的宏定義,比如使能dhcp、ping等命令。第331行的宏CONFIG_FEC_ENET_DEV指定uboot所使用的網(wǎng)口,I.MX6ULL有兩個(gè)網(wǎng)口,為0的時(shí)候使用ENET1,為1的時(shí)候使用ENET2。宏IMX_FEC_BASE為ENET接口的寄存器首地址,宏CONFIG_FEC_MXC_PHYADDR為網(wǎng)口PHY芯片的地址。宏CONFIG_FEC_XCV_TYPE為PHY芯片所使用的接口類型,I.MX6U-ALPHA開發(fā)板的兩個(gè)PHY都使用的RMII接口。
第344~END,剩下的都是一些配置宏,比如CONFIG_VIDEO宏用于開啟LCD,CONFIG_VIDEO_LOGO使能LOGO顯示,CONFIG_CMD_BMP使能BMP圖片顯示指令。這樣就可以在uboot中顯示圖片了,一般用于顯示logo。
關(guān)于mx6ull_alientek_emmc.h就講解到這里,其中以CONFIG_CMD開頭的宏都是用于使能相應(yīng)命令的,其他的以CONFIG開頭的宏都是完成一些配置功能的。以后會(huì)頻繁的和mx6ull_alientek_emmc.h這個(gè)文件打交道。
33.2.3 添加開發(fā)板對(duì)應(yīng)的板級(jí)文件夾
uboot中每個(gè)板子都有一個(gè)對(duì)應(yīng)的文件夾來存放板級(jí)文件,比如開發(fā)板上外設(shè)驅(qū)動(dòng)文件等等。NXP的I.MX系列芯片的所有板級(jí)文件夾都存放在board/freescale目錄下,在這個(gè)目錄下有個(gè)名為mx6ullevk的文件夾,這個(gè)文件夾就是NXP官方I.MX6ULL EVK開發(fā)板的板級(jí)文件夾。復(fù)制mx6ullevk,將其重命名為mx6ull_alientek_emmc,命令如下:
cd board/freescale/
cp mx6ullevk/ -r mx6ull_alientek_emmc
進(jìn)入mx6ull_alientek_emmc目錄中,將其中的mx6ullevk.c文件重命名為mx6ull_alientek_emmc.c,命令如下:
cd mx6ull_alientek_emmc
mv mx6ullevk.c mx6ull_alientek_emmc.c
我們還需要對(duì)mx6ull_alientek_emmc目錄下的文件做一些修改:
1、修改mx6ull_alientek_emmc目錄下的Makefile文件
將mx6ull_alientek_emmc下的Makefile文件內(nèi)容改為如下所示:
示例代碼33.2.3.1 Makefile文件
PLUGIN board/freescale/mx6ullevk/plugin.bin 0x00907000
改為:
PLUGIN board/freescale/mx6ull_alientek_emmc /plugin.bin 0x00907000
3、修改mx6ull_alientek_emmc目錄下的Kconfig文件
修改Kconfig文件,修改后的內(nèi)容如下:
示例代碼33.2.3.2 Kconfig文件
1 MX6ULL_ALIENTEK_EMMC BOARD
2 M: Peng Fan peng.fan@nxp.com
3 S: Maintained
4 F: board/freescale/mx6ull_alientek_emmc/
5 F: include/configs/mx6ull_alientek_emmc.h
33.2.4 修改U-Boot圖形界面配置文件
uboot是支持圖形界面配置,關(guān)于uboot的圖形界面配置下一章會(huì)詳細(xì)的講解。修改文件arch/arm/cpu/armv7/mx6/Kconfig(如果用的I.MX6UL的話,應(yīng)該修改arch/arm/Kconfig這個(gè)文件),在207行加入如下內(nèi)容:
示例代碼33.2.4.1 Kconfig文件
示例代碼33.2.4.2 Kconfig文件
1 source “board/freescale/mx6ull_alientek_emmc/Kconfig”
添加完成以后的Kconfig文件如圖33.2.4.1所示:
圖33.2.4.1 修改后的Kconfig文件
到此為止,I.MX6U-ALPHA開發(fā)板就已經(jīng)添加到uboot中了,接下來就是編譯這個(gè)新添加的開發(fā)板。
33.2.5 使用新添加的板子配置編譯uboot
在uboot根目錄下新建一個(gè)名為mx6ull_alientek_emmc.sh的shell腳本,在這個(gè)shell腳本里面輸入如下內(nèi)容:
示例代碼33.2.5.1 mx6ull_alientek_emmc.sh腳本文件
chmod 777 mx6ull_alientek_emmc.sh //給予可執(zhí)行權(quán)限,一次即可
./mx6ull_alientek_emmc.sh //運(yùn)行腳本編譯uboot
等待編譯完成,編譯完成以后輸入如下命令,查看一下33.2.2小節(jié)中添加的mx6ull_alientek_emmc.h這個(gè)頭文件有沒有被引用。
grep -nR “mx6ull_alientek_emmc.h”
如果有很多文件都引用了mx6ull_alientek_emmc.h這個(gè)頭文件,那就說明新板子添加成功,如圖33.2.5.1所示:
圖33.2.5.1 查找結(jié)果
編譯完成以后就使用imxdownload將新編譯出來的u-boot.bin燒寫到SD卡中測試,SecureCRT輸出結(jié)果如圖33.2.5.2所示:
圖33.2.5.1 uboot啟動(dòng)過程
從圖33.2.5.1可以看出,此時(shí)的Board還是“MX6ULL 14x14 EVK”,因?yàn)槲覀儏⒖嫉腘XP官方的I.MX6ULL開發(fā)板來添加自己的開發(fā)板。如果接了LCD屏幕的話會(huì)發(fā)現(xiàn)LCD屏幕并沒有顯示NXP的logo,而且從圖33.2.5.1可以看出此時(shí)的網(wǎng)絡(luò)同樣也沒識(shí)別出來。前面已經(jīng)說了,默認(rèn)uboot中的LCD驅(qū)動(dòng)和網(wǎng)絡(luò)驅(qū)動(dòng)在正點(diǎn)原子的I.MX6U-ALPHA開發(fā)板上是有問題的,需要修改。
33.2.6 LCD驅(qū)動(dòng)修改
一般uboot中修改驅(qū)動(dòng)基本都是在xxx.h和xxx.c這兩個(gè)文件中進(jìn)行的,xxx為板子名稱,比如mx6ull_alientek_emmc.h和mx6ull_alientek_emmc.c這兩個(gè)文件。
一般修改LCD驅(qū)動(dòng)重點(diǎn)注意以下幾點(diǎn):
①、LCD所使用的GPIO,查看uboot中LCD的IO配置是否正確。
②、LCD背光引腳GPIO的配置。
③、LCD配置參數(shù)是否正確。
正點(diǎn)原子的I.MX6U-ALPHA開發(fā)板LCD原理圖和NXP官方I.MX6ULL開發(fā)板一致,也就是LCD的IO和背光IO都一樣的,所以IO部分就不用修改了。需要修改的之后LCD參數(shù),打開文件mx6ull_alientek_emmc.c,找到如下所示內(nèi)容:
示例代碼33.2.6.1 LCD驅(qū)動(dòng)參數(shù)
20 } } };
示例代碼33.2.6.1中定義了一個(gè)變量displays,類型為display_info_t,這個(gè)結(jié)構(gòu)體是LCD信息結(jié)構(gòu)體,其中包括了LCD的分辨率,像素格式,LCD的各個(gè)參數(shù)等。display_info_t定義在文件arch/arm/include/asm/imx-common/video.h中,定義如下:
示例代碼33.2.6.2 display_info結(jié)構(gòu)體
示例代碼33.2.6.3 fb_videomode結(jié)構(gòu)體
1 struct fb_videomode { 2 const char *name; /* optional */ 3 u32 refresh; /* optional */ 4 u32 xres; 5 u32 yres; 6 u32 pixclock; 7 u32 left_margin; 8 u32 right_margin; 9 u32 upper_margin; 10 u32 lower_margin; 11 u32 hsync_len; 12 u32 vsync_len; 13 u32 sync; 14 u32 vmode; 15 u32 flag; 16 }; 結(jié)構(gòu)體fb_videomode里面的成員變量為LCD的參數(shù),這些成員變量函數(shù)如下: name:LCD名字,要和環(huán)境變量中的panel相等。 xres、yres:LCD X軸和Y軸像素?cái)?shù)量。 pixclock:像素時(shí)鐘,每個(gè)像素時(shí)鐘周期的長度,單位為皮秒。 left_margin:HBP,水平同步后肩。 right_margin:HFP,水平同步前肩。 upper_margin:VBP,垂直同步后肩。 lower_margin:VFP,垂直同步前肩。 hsync_len:HSPW,行同步脈寬。 vsync_len:VSPW,垂直同步脈寬。 vmode:大多數(shù)使用FB_VMODE_NONINTERLACED,也就是不使用隔行掃描。 可以看出,這些參數(shù)和我們第二十四章講解RGB LCD的時(shí)候參數(shù)基本一樣,唯一不同的像素時(shí)鐘pixclock的含義不同,以正點(diǎn)原子的7寸1024*600分辨率的屏幕(ATK7016)為例,屏幕要求的像素時(shí)鐘為51.2MHz,因此:pixclock=(1/51200000)*10^12=19531
在根據(jù)其他的屏幕參數(shù),可以得出ATK7016屏幕的配置參數(shù)如下:
示例代碼33.2.6.4 ATK7016屏幕配置參數(shù)
panel=TFT43AB
將其改為:
panel=TFT7016
也就是設(shè)置panel為TFT7016,panel的值要與示例代碼33.2.6.4中的.name成員變量的值一致。修改完成以后重新編譯一遍uboot并燒寫到SD中啟動(dòng)。
重啟以后LCD驅(qū)動(dòng)一般就會(huì)工作正常了,LCD上回顯示NXP的logo。但是有可能會(huì)遇到LCD并沒有工作,還是黑屏,這是什么原因呢?在uboot命令模式輸入“print”來查看環(huán)境變量panel的值,會(huì)發(fā)現(xiàn)panel的值要是TFT43AB(或其他的,反正不是TFT7016),如圖33.2.6.1所示:
圖33.2.6.1 panel的值
這是因?yàn)橹坝袑h(huán)境變量保存到EMMC中,uboot啟動(dòng)以后會(huì)先從EMMC中讀取環(huán)境變量,如果EMMC中沒有環(huán)境變量的話才會(huì)使用mx6ull_alientek_emmc.h中的默認(rèn)環(huán)境變量。如果EMMC中的環(huán)境變量panel不等于TFT7016,那么LCD顯示肯定不正常,我們只需要在uboot中修改panel的值為TFT7016即可,在uboot的命令模式下輸入如下命令:
setenv panel TFT7016
saveenv
上述命令修改環(huán)境變量panel為TFT7016,然后保存,重啟uboot,此時(shí)LCD驅(qū)動(dòng)就工作正常了。如果LCD還是沒有正常工作的,那就要檢查自己哪里有沒有改錯(cuò),或者還有哪里沒有修改。
33.2.7 網(wǎng)絡(luò)驅(qū)動(dòng)修改
1、I.MX6U-ALPHA開發(fā)板網(wǎng)絡(luò)簡介
I.MX6UL/ULL內(nèi)部有個(gè)以太網(wǎng)MAC外設(shè),也就是ENET,需要外接一個(gè)PHY芯片來實(shí)現(xiàn)網(wǎng)絡(luò)通信功能,也就是內(nèi)部MAC+外部PHY芯片的方案。大家可能聽過DM9000這個(gè)網(wǎng)絡(luò)芯片,在一些沒有內(nèi)部MAC的CPU中,比如三星的2440,4412等,就會(huì)采用DM9000來實(shí)現(xiàn)聯(lián)網(wǎng)功能。DM9000提供了一個(gè)類似SRAM的訪問接口,主控CPU通過這個(gè)接口即可與DM9000進(jìn)行通信,DM9000就是一個(gè)MAC+PHY芯片。這個(gè)方案就相當(dāng)于外部MAC+外部PHY,那么I.MX6U這樣的內(nèi)部MAC+PHY芯片與DM9000方案比有什么優(yōu)勢嗎?那優(yōu)勢大了去了!首先就是通信效率和速度,一般SOC內(nèi)部的MAC是帶有一個(gè)專用DMA的,專門用于處理網(wǎng)絡(luò)數(shù)據(jù)包,采用SRAM來讀寫DM9000的速度是壓根就沒法和內(nèi)部MAC+外部PHY芯片的速度比。采用外部DM9000完全是無奈之舉,誰讓2440,4412這些芯片內(nèi)部沒有以太網(wǎng)外設(shè)呢,現(xiàn)在又想用有線網(wǎng)絡(luò),沒有辦法只能找個(gè)DM9000的方案。從這里也可以看出,三星的2440、4412這些芯片設(shè)計(jì)之初就不是給工業(yè)產(chǎn)品用的,他們是給消費(fèi)類電子使用的,比如手機(jī)、平板等,手機(jī)或平板要上網(wǎng),可以通過WIFI或者4G,我是沒有見過哪個(gè)手機(jī)或者平板上網(wǎng)是要接根網(wǎng)線的。正點(diǎn)原子的I.MX6U-ALPHA開發(fā)板也可以通過WIFI或者4G上網(wǎng),這個(gè)是后話了。
I.MX6UL/ULL有兩個(gè)網(wǎng)絡(luò)接口ENET1和ENET2,正點(diǎn)原子的I.MX6U-ALPHA開發(fā)板提供了這兩個(gè)網(wǎng)絡(luò)接口,其中ENET1和ENET2都使用LAN8720A作為PHY芯片。NXP官方的I.MX6ULL EVK開發(fā)板使用KSZ8081這顆PHY芯片,LAN8720A相比KSZ8081具有體積小、外圍器件少、價(jià)格便宜等優(yōu)點(diǎn)。直接使用KSZ8081固然可以,但是我們在實(shí)際的產(chǎn)品中不一定會(huì)使用KSZ8081,有時(shí)候?yàn)榱私档统杀緯?huì)選擇其他的PHY芯片,這個(gè)時(shí)候就有個(gè)問題:換了PHY芯片以后網(wǎng)絡(luò)驅(qū)動(dòng)怎么辦?為此,正點(diǎn)原子的I.MX6U-ALPHA開發(fā)板將ENET1和ENET2的PHY換成了LAN8720A,這樣就可以給大家講解更換PHY芯片以后如何調(diào)整網(wǎng)絡(luò)驅(qū)動(dòng),使網(wǎng)絡(luò)工作正常。
I.MX6U-ALPHA開發(fā)板ENET1的網(wǎng)絡(luò)原理圖如圖33.2.7.1所示:
圖33.2.7.1 ENET1原理圖
ENET1的網(wǎng)絡(luò)PHY芯片為LAN8720A,通過RMII接口與I.MX6ULL相連,正點(diǎn)原子I.MX6U-ALPHA開發(fā)板的ENET1引腳與NXP官方的I.MX6ULL EVK開發(fā)板基本一樣,唯獨(dú)復(fù)位引腳不同。從圖33.2.7.1可以看出,正點(diǎn)原子I.MX6U-ALPHA開發(fā)板的ENET1復(fù)位引腳ENET1_RST接到了I.M6ULL的SNVS_TAMPER7這個(gè)引腳上。
LAN8720A內(nèi)部是有寄存器的,I.MX6ULL會(huì)讀取LAN8720內(nèi)部寄存器來判斷當(dāng)前的物理鏈接狀態(tài)、連接速度(10M還是100M)和雙工狀態(tài)(半雙工還是全雙工)。I.MX6ULL通過MDIO接口來讀取PHY芯片的內(nèi)部寄存器,MDIO接口有兩個(gè)引腳,ENET_MDC和ENET_MDIO, ENET_MDC提供時(shí)鐘,ENET_MDIO進(jìn)行數(shù)據(jù)傳輸。一個(gè)MDIO接口可以管理32個(gè)PHY芯片,同一個(gè)MDIO接口下的這些PHY使用不同的器件地址來做區(qū)分,MIDO接口通過不同的器件地址即可訪問到相應(yīng)的PHY芯片。I.MX6U-ALPHA開發(fā)板ENET1上連接的LAN8720A器件地址為0X0,所示我們要修改ENET1網(wǎng)絡(luò)驅(qū)動(dòng)的話重點(diǎn)就三點(diǎn):
①、ENET1復(fù)位引腳初始化。
②、LAN8720A的器件ID。
③、LAN8720驅(qū)動(dòng)
再來看一下ENET2的原理圖,如圖33.2.7.2所示:
圖33.2.7.2 ENET2原理圖
關(guān)于ENET2網(wǎng)絡(luò)驅(qū)動(dòng)的修改也注意一下三點(diǎn):
①、ENET2的復(fù)位引腳,從圖33.2.7.2可以看出,ENET2的復(fù)位引腳ENET2_RST接到了I.MX6ULL的SNVS_TAMPER8上。
②、ENET2所使用的PHY芯片器件地址,從圖33.2.7.2可以看出,PHY器件地址為0X1。
③、LAN8720驅(qū)動(dòng),ENET1和ENET2都使用的LAN8720,所以驅(qū)動(dòng)肯定是一樣的。
2、網(wǎng)絡(luò)PHY地址修改
首先修改uboot中的ENET1和ENET2的PHY地址和驅(qū)動(dòng),打開mx6ull_alientek_emmc.h這個(gè)文件,找到如下代碼:
示例代碼33.2.7.1 網(wǎng)絡(luò)默認(rèn)ID配置參數(shù)
①、修改ENET1網(wǎng)絡(luò)PHY的地址。
②、修改ENET2網(wǎng)絡(luò)PHY的地址。
③、使能SMSC公司的PHY驅(qū)動(dòng)。
修改后的網(wǎng)絡(luò)PHY地址參數(shù)如下所示:
示例代碼33.2.7.2 網(wǎng)絡(luò)PHY地址配置參數(shù)
uboot中網(wǎng)絡(luò)PHY芯片地址修改完成以后就是網(wǎng)絡(luò)復(fù)位引腳的驅(qū)動(dòng)修改了,打開mx6ull_alientek_emmc.c,找到如下代碼:
示例代碼33.2.7.3 74LV595引腳
示例代碼33.2.7.4 修改后的網(wǎng)絡(luò)引腳
#define ENET1_RESET IMX_GPIO_NR(5, 7) #define ENET2_RESET IMX_GPIO_NR(5, 8) ENET1的復(fù)位引腳連接到SNVS_TAMPER7上,對(duì)應(yīng)GPIO5_IO07,ENET2的復(fù)位引腳連接到SNVS_TAMPER8上,對(duì)應(yīng)GPIO5_IO08。 繼續(xù)在mx6ull_alientek_emmc.c中找到如下代碼:示例代碼33.2.7.5 74LV595引腳配置
static iomux_v3_cfg_t const iox_pads[] = {/* IOX_SDI */MX6_PAD_BOOT_MODE0__GPIO5_IO10 | MUX_PAD_CTRL(NO_PAD_CTRL),/* IOX_SHCP */MX6_PAD_BOOT_MODE1__GPIO5_IO11 | MUX_PAD_CTRL(NO_PAD_CTRL),/* IOX_STCP */MX6_PAD_SNVS_TAMPER7__GPIO5_IO07 | MUX_PAD_CTRL(NO_PAD_CTRL),/* IOX_nOE */MX6_PAD_SNVS_TAMPER8__GPIO5_IO08 | MUX_PAD_CTRL(NO_PAD_CTRL), }; 同理,示例代碼33.2.7.5是74LV595的IO配置參數(shù)結(jié)構(gòu)體,將其刪除掉。繼續(xù)在mx6ull_alientek_emmc.c中找到函數(shù)iox74lv_init,如下所示:示例代碼33.2.7.6 74LV595初始化函數(shù)
static void iox74lv_init(void) {int i;gpio_direction_output(IOX_OE, 0);for (i = 7; i >= 0; i--) {gpio_direction_output(IOX_SHCP, 0);gpio_direction_output(IOX_SDI, seq[qn_output[i]][0]);udelay(500);gpio_direction_output(IOX_SHCP, 1);udelay(500);}....../** shift register will be output to pins*/gpio_direction_output(IOX_STCP, 1); };void iox74lv_set(int index) {int i;for (i = 7; i >= 0; i--) {gpio_direction_output(IOX_SHCP, 0);if (i == index)gpio_direction_output(IOX_SDI, seq[qn_output[i]][0]);elsegpio_direction_output(IOX_SDI, seq[qn_output[i]][1]);udelay(500);gpio_direction_output(IOX_SHCP, 1);udelay(500);}....../** shift register will be output to pins*/gpio_direction_output(IOX_STCP, 1); }; iox74lv_init函數(shù)是74LV595的初始化函數(shù),iox74lv_set函數(shù)用于控制74LV595的IO輸出電平,將這兩個(gè)函數(shù)全部刪除掉! 在mx6ull_alientek_emmc.c中找到board_init函數(shù),此函數(shù)是板子初始化函數(shù),會(huì)被board_init_r調(diào)用,board_init函數(shù)內(nèi)容如下:示例代碼33.2.7.7 board_init函數(shù)
int board_init(void) { ...... imx_iomux_v3_setup_multiple_pads(iox_pads, ARRAY_SIZE(iox_pads));iox74lv_init();......return 0; } board_init會(huì)調(diào)用imx_iomux_v3_setup_multiple_pads 和iox74lv_init這兩個(gè)函數(shù)來初始化74lv595的GPIO,將這兩行刪除掉。至此,mx6ull_alientek_emmc.c中關(guān)于74LV595芯片的驅(qū)動(dòng)代碼都刪除掉了,接下來就是添加I.MX6U-ALPHA開發(fā)板兩個(gè)網(wǎng)絡(luò)復(fù)位引腳了。 4、添加I.MX6U-ALPHA開發(fā)板網(wǎng)絡(luò)復(fù)位引腳驅(qū)動(dòng) 在mx6ull_alientek_emmc.c中找到如下所示代碼:示例代碼33.2.7.8 默認(rèn)網(wǎng)絡(luò)IO結(jié)構(gòu)體數(shù)組
640 static iomux_v3_cfg_t const fec1_pads[] = { 641 MX6_PAD_GPIO1_IO06__ENET1_MDIO | MUX_PAD_CTRL(MDIO_PAD_CTRL), 642 MX6_PAD_GPIO1_IO07__ENET1_MDC | MUX_PAD_CTRL(ENET_PAD_CTRL), ...... 649 MX6_PAD_ENET1_RX_ER__ENET1_RX_ER | MUX_PAD_CTRL(ENET_PAD_CTRL), 650 MX6_PAD_ENET1_RX_EN__ENET1_RX_EN | MUX_PAD_CTRL(ENET_PAD_CTRL), 651 }; 652 653 static iomux_v3_cfg_t const fec2_pads[] = { 654 MX6_PAD_GPIO1_IO06__ENET2_MDIO | MUX_PAD_CTRL(MDIO_PAD_CTRL), 655 MX6_PAD_GPIO1_IO07__ENET2_MDC | MUX_PAD_CTRL(ENET_PAD_CTRL), ...... 664 MX6_PAD_ENET2_RX_EN__ENET2_RX_EN | MUX_PAD_CTRL(ENET_PAD_CTRL), 665 MX6_PAD_ENET2_RX_ER__ENET2_RX_ER | MUX_PAD_CTRL(ENET_PAD_CTRL), 666 }; 結(jié)構(gòu)體數(shù)組fec1_pads和fec2_pads是ENET1和ENET2這兩個(gè)網(wǎng)口的IO配置參數(shù),在這兩個(gè)數(shù)組中添加兩個(gè)網(wǎng)口的復(fù)位IO配置參數(shù),完成以后如下所示:示例代碼33.2.7.9 添加網(wǎng)絡(luò)復(fù)位IO后的結(jié)構(gòu)體數(shù)組
640 static iomux_v3_cfg_t const fec1_pads[] = { 641 MX6_PAD_GPIO1_IO06__ENET1_MDIO | MUX_PAD_CTRL(MDIO_PAD_CTRL), 642 MX6_PAD_GPIO1_IO07__ENET1_MDC | MUX_PAD_CTRL(ENET_PAD_CTRL), ...... 649 MX6_PAD_ENET1_RX_ER__ENET1_RX_ER | MUX_PAD_CTRL(ENET_PAD_CTRL), 650 MX6_PAD_ENET1_RX_EN__ENET1_RX_EN | MUX_PAD_CTRL(ENET_PAD_CTRL), 651 MX6_PAD_SNVS_TAMPER7__GPIO5_IO07 | MUX_PAD_CTRL(NO_PAD_CTRL), 652 }; 653 654 static iomux_v3_cfg_t const fec2_pads[] = { 655 MX6_PAD_GPIO1_IO06__ENET2_MDIO | MUX_PAD_CTRL(MDIO_PAD_CTRL), 656 MX6_PAD_GPIO1_IO07__ENET2_MDC | MUX_PAD_CTRL(ENET_PAD_CTRL), ...... 665 MX6_PAD_ENET2_RX_EN__ENET2_RX_EN | MUX_PAD_CTRL(ENET_PAD_CTRL), 666 MX6_PAD_ENET2_RX_ER__ENET2_RX_ER | MUX_PAD_CTRL(ENET_PAD_CTRL), 667 MX6_PAD_SNVS_TAMPER8__GPIO5_IO08 | MUX_PAD_CTRL(NO_PAD_CTRL), 668 }; 示例代碼33.2.7.9中,第651行和667行分別是ENET1和ENET2的復(fù)位IO配置參數(shù)。繼續(xù)在文件mx6ull_alientek_emmc.c中找到函數(shù)setup_iomux_fec,此函數(shù)默認(rèn)代碼如下:示例代碼33.2.7.10 setup_iomux_fec函數(shù)默認(rèn)代碼
668 static void setup_iomux_fec(int fec_id) 669 { 670 if (fec_id == 0) 671 imx_iomux_v3_setup_multiple_pads(fec1_pads, 672 ARRAY_SIZE(fec1_pads)); 673 else 674 imx_iomux_v3_setup_multiple_pads(fec2_pads, 675 ARRAY_SIZE(fec2_pads)); 676 } 函數(shù)setup_iomux_fec就是根據(jù)fec1_pads和fec2_pads這兩個(gè)網(wǎng)絡(luò)IO配置數(shù)組來初始化I.MX6ULL的網(wǎng)絡(luò)IO。我們需要在其中添加網(wǎng)絡(luò)復(fù)位IO的初始化代碼,并且復(fù)位一下PHY芯片,修改后的setup_iomux_fec函數(shù)如下:示例代碼33.2.7.11 修改后的setup_iomux_fec函數(shù)
668 static void setup_iomux_fec(int fec_id) 669 { 670 if (fec_id == 0) 671 { 672 673 imx_iomux_v3_setup_multiple_pads(fec1_pads, 674 ARRAY_SIZE(fec1_pads)); 675 676 gpio_direction_output(ENET1_RESET, 1); 677 gpio_set_value(ENET1_RESET, 0); 678 mdelay(20); 679 gpio_set_value(ENET1_RESET, 1); 680 } 681 else 682 { 683 imx_iomux_v3_setup_multiple_pads(fec2_pads, 684 ARRAY_SIZE(fec2_pads)); 685 gpio_direction_output(ENET2_RESET, 1); 686 gpio_set_value(ENET2_RESET, 0); 687 mdelay(20); 688 gpio_set_value(ENET2_RESET, 1); 689 } 690 } 示例代碼33.2.7.11中第676行~679行和第685行~688行分別對(duì)應(yīng)ENET1和ENET2的復(fù)位IO初始化,將這兩個(gè)IO設(shè)置為輸出并且硬件復(fù)位一下LAN8720A,這個(gè)硬件復(fù)位很重要!否則可能導(dǎo)致uboot無法識(shí)別LAN8720A。 5、修改drivers/net/phy/phy.c文件中的函數(shù)genphy_update_link 大功基本上告成,還差最后一步,uboot中的LAN8720A驅(qū)動(dòng)有點(diǎn)問題,打開文件drivers/net/phy/phy.c,找到函數(shù)genphy_update_link,這是個(gè)通用PHY驅(qū)動(dòng)函數(shù),此函數(shù)用于更新PHY的連接狀態(tài)和速度。使用LAN8720A的時(shí)候需要在此函數(shù)中添加一些代碼,修改后的函數(shù)genphy_update_link如下所示:示例代碼33.2.7.12 修改后的genphy_update_link函數(shù)
221 int genphy_update_link(struct phy_device *phydev) 222 { 223 unsigned int mii_reg; 224 225 #ifdef CONFIG_PHY_SMSC 226 static int lan8720_flag = 0; 227 int bmcr_reg = 0; 228 if (lan8720_flag == 0) { 229 bmcr_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR); 230 phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET); 231 while(phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR) & 0X8000) { 232 udelay(100); 233 } 234 phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, bmcr_reg); 235 lan8720_flag = 1; 236 } 237 #endif 238 239 /* 240 * Wait if the link is up, and autonegotiation is in progress 241 * (ie - we're capable and it's not done) 242 */ 243 mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR); ...... 291 292 return 0; 293 } 225行~237行就是新添加的代碼,為條件編譯代碼段,只有使用SMSC公司的PHY這段代碼才會(huì)執(zhí)行(目前只測試了LAN8720A,SMSC公司其他的芯片還未測試)。第229行讀取LAN8720A的BMCR寄存器(寄存器地址為0),此寄存器為LAN8720A的配置寄存器,這里先讀取此寄存器的默認(rèn)值并保存起來。230行向寄存器BMCR寄存器寫入BMCR_RESET(值為0X8000),因?yàn)锽MCR的bit15是軟件復(fù)位控制位,因此230行就是軟件復(fù)位LAN8720A,復(fù)位完成以后此位會(huì)自動(dòng)清零。第231~233行等待LAN8720A軟件復(fù)位完成,也就是判斷BMCR的bit15位是否為1,為1的話表示還沒有復(fù)位完成。第234行重新向BMCR寄存器寫入以前的值,也就是229行讀出的那個(gè)值。 至此網(wǎng)絡(luò)的復(fù)位引腳驅(qū)動(dòng)修改完成,重新編譯uboot,然后將u-boot.bin燒寫到SD卡中并啟動(dòng),uboot啟動(dòng)信息如圖33.2.7.3所示:圖33.2.7.3 uboot啟動(dòng)信息
從圖33.2.6.4中可以看到“Net: FEC1”這一行,提示當(dāng)前使用的FEC1這個(gè)網(wǎng)口,也就是ENET2。在uboot中使用網(wǎng)絡(luò)之前要先設(shè)置幾個(gè)環(huán)境變量,命令如下:
setenv ipaddr 192.168.1.55 //開發(fā)板IP地址
setenv ethaddr b8:ae:1d:01:00:00 //開發(fā)板網(wǎng)卡MAC地址
setenv gatewayip 192.168.1.1 //開發(fā)板默認(rèn)網(wǎng)關(guān)
setenv netmask 255.255.255.0 //開發(fā)板子網(wǎng)掩碼
setenv serverip 192.168.1.250 //服務(wù)器地址,也就是Ubuntu地址
saveenv //保存環(huán)境變量
設(shè)置好環(huán)境變量以后就可以在uboot中使用網(wǎng)絡(luò)了,用網(wǎng)線將I.MX6U-ALPHA上的ENET2與電腦或者路由器連接起來,保證開發(fā)板和電腦在同一個(gè)網(wǎng)段內(nèi),通過ping命令來測試一下網(wǎng)絡(luò)連接,命令如下:
ping 192.168.1.250
結(jié)果如圖33.2.7.4所示:
圖33.2.7.4 ping命令測試
從圖33.2.7.4可以看出,有“host 192.168.1.250 is alive”這句,說明ping主機(jī)成功,說明ENET2網(wǎng)絡(luò)工作正常。再來測試一下ENET1的網(wǎng)絡(luò)是否正常工作,打開mx6ull_alientek_emmc.h,將CONFIG_FEC_ENET_DEV改為0,然后重新編譯一下uboot并燒寫到SD卡中重啟。重啟開發(fā)板,uboot輸出信息如圖33.2.7.5所示:
圖33.2.7.5 uboot啟動(dòng)信息
從圖33.2.7.5可以出,有“Net:FEC0”這一行,說明當(dāng)前使用的FEC0這個(gè)網(wǎng)卡,也就是ENET1,同樣的ping一下主機(jī),結(jié)果如圖33.2.7.5所示:
圖33.2.7.6 ping命令測試
從圖33.2.7.6可以看出,ping主機(jī)也成功,說明ENET1網(wǎng)絡(luò)也工作正常,至此,I.MX6U-ALPHA開發(fā)板的兩個(gè)網(wǎng)絡(luò)都工作正常了,建議大家將ENET2設(shè)置為uboot的默認(rèn)網(wǎng)卡!也就是將宏CONFIG_FEC_ENET_DEV設(shè)置為1。
33.2.8 其他需要修改的地方
在uboot啟動(dòng)信息中會(huì)有“Board: MX6ULL 14x14 EVK”這一句,也就是說板子名字為“MX6ULL 14x14 EVK”,要將其改為我們所使用的板子名字,比如“MX6ULL ALIENTEK EMMC”或者“MX6ULL ALIENTEK NAND”。打開文件mx6ull_alientek_emmc.c,找到函數(shù)checkboard,將其改為如下所示內(nèi)容:
示例代碼33.2.8.1 修改后的checkboard函數(shù)
圖33.2.8.1 uboot啟動(dòng)信息
從圖33.2.8.1可以看出,Board變成了“MX6ULL ALIENTEK EMMC”。至此uboot的驅(qū)動(dòng)部分就修改完成了,uboot移植也完成了,uboot的最終目的就是啟動(dòng)Linux內(nèi)核,所以需要通過啟動(dòng)Linux內(nèi)核來判斷uboot移植是否成功。在啟動(dòng)Linux內(nèi)核之前我們先來學(xué)習(xí)兩個(gè)重要的環(huán)境變量bootcmd和bootargs。
33.3 bootcmd和bootargs環(huán)境變量
uboot中有兩個(gè)非常重要的環(huán)境變量bootcmd和bootargs,接下來看一下這兩個(gè)環(huán)境變量。bootcmd和bootagrs是采用類似shell腳本語言編寫的,里面有很多的變量引用,這些變量其實(shí)都是環(huán)境變量,有很多是NXP自己定義的。文件mx6ull_alientek_emmc.h中的宏CONFIG_EXTRA_ENV_SETTINGS保存著這些環(huán)境變量的默認(rèn)值,內(nèi)容如下:
示例代碼33.3.1.1 宏CONFIG_EXTRA_ENV_SETTINGS默認(rèn)值
33.3.1 環(huán)境變量bootcmd
bootcmd在前面已經(jīng)說了很多次了,bootcmd保存著uboot默認(rèn)命令,uboot倒計(jì)時(shí)結(jié)束以后就會(huì)執(zhí)行bootcmd中的命令。這些命令一般都是用來啟動(dòng)Linux內(nèi)核的,比如讀取EMMC或者NAND Flash中的Linux內(nèi)核鏡像文件和設(shè)備樹文件到DRAM中,然后啟動(dòng)Linux內(nèi)核??梢栽趗boot啟動(dòng)以后進(jìn)入命令行設(shè)置bootcmd環(huán)境變量的值。如果EMMC或者NAND中沒有保存bootcmd的值,那么uboot就會(huì)使用默認(rèn)的值,板子第一次運(yùn)行uboot的時(shí)候都會(huì)使用默認(rèn)值來設(shè)置bootcmd環(huán)境變量。打開文件include/env_default.h,在此文件中有如下所示內(nèi)容:
示例代碼33.3.1.1 默認(rèn)環(huán)境變量
在示例代碼33.3.1.1中指定了很多環(huán)境變量的默認(rèn)值,比如bootcmd的默認(rèn)值就是CONFIG_BOOTCOMMAND,bootargs的默認(rèn)值就是CONFIG_BOOTARGS。我們可以在mx6ull_alientek_emmc.h文件中通過設(shè)置宏CONFIG_BOOTCOMMAND來設(shè)置bootcmd的默認(rèn)值,NXP官方設(shè)置的CONFIG_BOOTCOMMAND值如下:
示例代碼33.3.1.3 CONFIG_BOOTCOMMAND默認(rèn)值
第205行,run findfdt;使用的是uboot的run命令來運(yùn)行findfdt,findfdt是NXP自行添加的環(huán)境變量。findfdt是用來查找開發(fā)板對(duì)應(yīng)的設(shè)備樹文件(.dtb)。IMX6ULL EVK的設(shè)備樹文件為imx6ull-14x14-evk.dtb,findfdt內(nèi)容如下:
“findfdt=”
"if test $fdt_file = undefined; then " \
"if test $board_name = EVK && test $board_rev = 9X9; then "
"setenv fdt_file imx6ull-9x9-evk.dtb; fi; "
"if test $board_name = EVK && test $board_rev = 14X14; then "
"setenv fdt_file imx6ull-14x14-evk.dtb; fi; "
"if test $fdt_file = undefined; then "
"echo WARNING: Could not determine dtb to use; fi; "
“fi;\0”
findfdt里面用到的變量有fdt_file,board_name,board_rev,這三個(gè)變量內(nèi)容如下:
fdt_file=undefined,board_name=EVK,board_rev=14X14
findfdt做的事情就是判斷,fdt_file是否為undefined,如果fdt_file為undefined的話那就要根據(jù)板子信息得出所需的.dtb文件名。此時(shí)fdt_file為undefined,所以根據(jù)board_name和board_rev來判斷實(shí)際所需的.dtb文件,如果board_name為EVK并且board_rev=9x9的話fdt_file就為imx6ull-9x9-evk.dtb。如果board_name為EVK并且board_rev=14x14的話fdt_file就設(shè)置為imx6ull-14x14-evk.dtb。因此IMX6ULL EVK板子的設(shè)備樹文件就是imx6ull-14x14-evk.dtb,
因此run findfdt的結(jié)果就是設(shè)置fdt_file為imx6ull-14x14-evk.dtb。
第206行,mmc dev ${mmcdev}用于切換mmc設(shè)備,mmcdev為1,因此這行代碼就是:mmc dev 1,也就是切換到EMMC上。
第207行,先執(zhí)行mmc dev ${mmcdev}切換到EMMC上,然后使用命令mmc rescan掃描看有沒有SD卡或者EMMC存在,如果沒有的話就直接跳到216行,執(zhí)行run netboot,netboot也是一個(gè)自定義的環(huán)境變量,這個(gè)變量是從網(wǎng)絡(luò)啟動(dòng)Linux的。如果mmc設(shè)備存在的話就從mmc設(shè)備啟動(dòng)。
第208行,運(yùn)行l(wèi)oadbootscript環(huán)境變量,此環(huán)境變量內(nèi)容如下:
loadbootscript=fatload mmc mmcdev:{mmcdev}:mmcdev:{mmcpart} ${loadaddr} ${script};
其中mmcdev=1,mmcpart=1,loadaddr=0x80800000,script= boot.scr,因此展開以后就是:
loadbootscript=fatload mmc 1:1 0x80800000 boot.scr;
loadbootscript就是從mmc1的分區(qū)1中讀取文件boot.src到DRAM的0X80800000處。但是mmc1的分區(qū)1中沒有boot.src這個(gè)文件,可以使用命令“l(fā)s mmc 1:1”查看一下mmc1分區(qū)1中的所有文件,看看有沒有boot.src這個(gè)文件。
第209行,如果加載boot.src文件成功的話就運(yùn)行bootscript環(huán)境變量,bootscript的內(nèi)容如下:
bootscript=echo Running bootscript from mmc …;
source
因?yàn)閎oot.src文件不存在,所以bootscript也就不會(huì)運(yùn)行。
第211行,如果loadbootscript沒有找到boot.src的話就運(yùn)行環(huán)境變量loadimage,環(huán)境變量loadimage內(nèi)容如下:
loadimage=fatload mmc mmcdev:{mmcdev}:mmcdev:{mmcpart} ${loadaddr} ${image}
其中mmcdev=1,mmcpart=1,loadaddr=0x80800000,image = zImage,展開以后就是:
loadimage=fatload mmc 1:1 0x80800000 zImage
可以看出loadimage就是從mmc1的分區(qū)中讀取zImage到內(nèi)存的0X80800000處,而mmc1的分區(qū)1中存在zImage。
第212行,加載linux鏡像文件zImage成功以后就運(yùn)行環(huán)境變量mmcboot,否則的話運(yùn)行netboot環(huán)境變量。mmcboot環(huán)境變量如下:
示例代碼33.3.1.4 mmcboot環(huán)境變量
loadfdt=fatload mmc mmcdev:{mmcdev}:mmcdev:{mmcpart} ${fdt_addr} ${fdt_file}
展開以后就是:
loadfdt=fatload mmc 1:1 0x83000000 imx6ull-14x14-evk.dtb
因此loadfdt的作用就是從mmc1的分區(qū)1中讀取imx6ull-14x14-evk.dtb文件并放到0x83000000處。
第158行,如果讀取.dtb文件成功的話那就調(diào)用命令bootz啟動(dòng)linux,調(diào)用方法如下:
bootz ${loadaddr} - KaTeX parse error: Expected 'EOF', got '#' at position 481: …TCOMMAND就可簡化為: #?define CONFIG_B…{console},baudrateroot={baudrate} root=baudrateroot={mmcroot}
其中console=ttymxc0,baudrate=115200,mmcroot=/dev/mmcblk1p2 rootwait rw,因此將mmcargs展開以后就是:
mmcargs=setenv bootargs console= ttymxc0, 115200 root= /dev/mmcblk1p2 rootwait rw
可以看出環(huán)境變量mmcargs就是設(shè)置bootargs的值為“console= ttymxc0, 115200 root= /dev/mmcblk1p2 rootwait rw”,bootargs就是設(shè)置了很多的參數(shù)的值,這些參數(shù)Linux內(nèi)核會(huì)使用到,常用的參數(shù)有:
1、console
console用來設(shè)置linux終端(或者叫控制臺(tái)),也就是通過什么設(shè)備來和Linux進(jìn)行交互,是串口還是LCD屏幕?如果是串口的話應(yīng)該是串口幾等等。一般設(shè)置串口作為Linux終端,這樣我們就可以在電腦上通過SecureCRT來和linux交互了。這里設(shè)置console為ttymxc0,因?yàn)閘inux啟動(dòng)以后I.MX6ULL的串口1在linux下的設(shè)備文件就是/dev/ttymxc0,在Linux下,一切皆文件。
ttymxc0后面有個(gè)“,115200”,這是設(shè)置串口的波特率,console=ttymxc0,115200綜合起來就是設(shè)置ttymxc0(也就是串口1)作為Linux的終端,并且串口波特率設(shè)置為115200。
2、root
root用來設(shè)置根文件系統(tǒng)的位置,root=/dev/mmcblk1p2用于指明根文件系統(tǒng)存放在mmcblk1設(shè)備的分區(qū)2中。EMMC版本的核心板啟動(dòng)linux以后會(huì)存在/dev/mmcblk0、/dev/mmcblk1、/dev/mmcblk0p1、/dev/mmcblk0p2、/dev/mmcblk1p1和/dev/mmcblk1p2這樣的文件,其中/dev/mmcblkx(x=0n)表示mmc設(shè)備,而/dev/mmcblkxpy(x=0n,y=1~n)表示mmc設(shè)備x的分區(qū)y。在I.MX6U-ALPHA開發(fā)板中/dev/mmcblk1表示EMMC,而/dev/mmcblk1p2表示EMMC的分區(qū)2。
root后面有“rootwait rw”,rootwait表示等待mmc設(shè)備初始化完成以后再掛載,否則的話mmc設(shè)備還沒初始化完成就掛載根文件系統(tǒng)會(huì)出錯(cuò)的。rw表示根文件系統(tǒng)是可以讀寫的,不加rw的話可能無法在根文件系統(tǒng)中進(jìn)行寫操作,只能進(jìn)行讀操作。
3、rootfstype
此選項(xiàng)一般配置root一起使用,rootfstype用于指定根文件系統(tǒng)類型,如果根文件系統(tǒng)為ext格式的話此選項(xiàng)無所謂。如果根文件系統(tǒng)是yaffs、jffs或ubifs的話就需要設(shè)置此選項(xiàng),指定根文件系統(tǒng)的類型。
bootargs常設(shè)置的選項(xiàng)就這三個(gè),后面遇到其他選項(xiàng)的話再講解。
33.4 uboot啟動(dòng)Linux測試
uboot已經(jīng)移植好了,bootcmd和bootargs這兩個(gè)重要的環(huán)境變量也講解了,接下來就要測試一下uboot能不能完成它的工作:啟動(dòng)Linux內(nèi)核。我們測試兩種啟動(dòng)Linux內(nèi)核的方法,一種是直接從EMMC啟動(dòng),一種是從網(wǎng)絡(luò)啟動(dòng)。
33.4.1 從EMMC啟動(dòng)Linux系統(tǒng)
從EMMC啟動(dòng)也就是將編譯出來的Linux鏡像文件zImage和設(shè)備樹文件保存在EMMC中,uboot從EMMC中讀取這兩個(gè)文件并啟動(dòng),這個(gè)是我們產(chǎn)品最終的啟動(dòng)方式。但是我們目前還沒有講解如何移植linux和設(shè)備樹文件,以及如何將zImage和設(shè)備樹文件保存到EMMC中。不過大家拿到手的I.MX6U-ALPHA開發(fā)板(EMMC版本)已經(jīng)將zImage文件和設(shè)備樹文件燒寫到了EMMC中,所以我們可以直接讀取來測試。先檢查一下EMMC的分區(qū)1中有沒有zImage文件和設(shè)備樹文件,輸入命令“l(fā)s mmc 1:1”,結(jié)果如圖33.4.1.1所示:
圖33.4.1.1 EMMC分區(qū)1文件
從圖33.4.1.1中可以看出,此時(shí)EMMC分區(qū)1中存在zimage和imx6ull-alientek-emmc.dtb這兩個(gè)文件,所以我們可以測試新移植的uboot能不能啟動(dòng)linux內(nèi)核。設(shè)置bootargs和bootcmd這兩個(gè)環(huán)境變量,設(shè)置如下:
setenv bootargs ‘console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw’
setenv bootcmd ‘mmc dev 1; fatload mmc 1:1 80800000 zImage; fatload mmc 1:1 83000000 imx6ull-alientek-emmc.dtb; bootz 80800000 - 83000000;’
saveenv
設(shè)置好以后直接輸入boot,或者run bootcmd即可啟動(dòng)Linux內(nèi)核,如果Linux內(nèi)核啟動(dòng)成功的話就會(huì)輸出如圖33.4.1.2所示的啟動(dòng)信息:
圖33.4.1.2 linux內(nèi)核啟動(dòng)成功
33.4.2 從網(wǎng)絡(luò)啟動(dòng)Linux系統(tǒng)
從網(wǎng)絡(luò)啟動(dòng)linux系統(tǒng)的唯一目的就是為了調(diào)試!不管是為了調(diào)試linux系統(tǒng)還是linux下的驅(qū)動(dòng)。每次修改linux系統(tǒng)文件或者linux下的某個(gè)驅(qū)動(dòng)以后都要將其燒寫到EMMC中去測試,這樣太麻煩了。我們可以設(shè)置linux從網(wǎng)絡(luò)啟動(dòng),也就是將linux鏡像文件和根文件系統(tǒng)都放到Ubuntu下某個(gè)指定的文件夾中,這樣每次重新編譯linux內(nèi)核或者某個(gè)linux驅(qū)動(dòng)以后只需要使用cp命令將其拷貝到這個(gè)指定的文件夾中即可,這樣就不用需要頻繁的燒寫EMMC,這樣就加快了開發(fā)速度。我們可以通過nfs或者tftp從Ubuntu中下載zImage和設(shè)備樹文件,根文件系統(tǒng)的話也可以通過nfs掛載,不過本小節(jié)我們不講解如何通過nfs掛載根文件系統(tǒng),這個(gè)在講解根文件系統(tǒng)移植的時(shí)候再講解。這里我們使用tftp從Ubuntu中下載zImage和設(shè)備樹文件,前提是要將zImage和設(shè)備樹文件放到Ubuntu下的tftp目錄中,具體方法在30.4.4小節(jié)講解tftp命令的時(shí)候已經(jīng)詳細(xì)的介紹過了。
設(shè)置bootargs和bootcmd這兩個(gè)環(huán)境變量,設(shè)置如下:
setenv bootargs ‘console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw’
setenv bootcmd ‘tftp 80800000 zImage; tftp 83000000 imx6ull-alientek-emmc.dtb; bootz 80800000 - 83000000’
saveenv
一開始是通過tftp下載zImage和imx6ull-alientek-emmc.dtb這兩個(gè)文件,過程如下圖33.4.2.1所示:
圖33.4.2.1 下載過程
下載完成以后就是啟動(dòng)Linux內(nèi)核,啟動(dòng)過程如圖33.4.2.2所示:
圖33.4.2.2 Linux啟動(dòng)過程
uboot移植到此結(jié)束,簡單總結(jié)一下uboot移植的過程:
①、不管是購買的開發(fā)板還是自己做的開發(fā)板,基本都是參考半導(dǎo)體廠商的dmeo板,而半導(dǎo)體廠商會(huì)在他們自己的開發(fā)板上移植好uboot、linux kernel和rootfs等,最終制作好BSP包提供給用戶。我們可以在官方提供的BSP包的基礎(chǔ)上添加我們的板子,也就是俗稱的移植。
②、我們購買的開發(fā)板或者自己做的板子一般都不會(huì)原封不動(dòng)的照抄半導(dǎo)體廠商的demo板,都會(huì)根據(jù)實(shí)際的情況來做修改,既然有修改就必然涉及到uboot下驅(qū)動(dòng)的移植。
③、一般uboot中需要解決串口、NAND、EMMC或SD卡、網(wǎng)絡(luò)和LCD驅(qū)動(dòng),因?yàn)閡boot的主要目的就是啟動(dòng)Linux內(nèi)核,所以不需要考慮太多的外設(shè)驅(qū)動(dòng)。
④、在uboot中添加自己的板子信息,根據(jù)自己板子的實(shí)際情況來修改uboot中的驅(qū)動(dòng)。
總結(jié)
以上是生活随笔為你收集整理的【正点原子Linux连载】第三十三章 U-Boot移植 -摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CList的用法理解
- 下一篇: Base64变种实现,如何实现Base6