?前言:
本文是根據嗶哩嗶哩網站上“Arm(iMX6U)Linux系統移植和根文件系統構鍵篇”視頻的學習筆記,在這里會記錄下正點原子 I.MX6ULL 開發板的配套視頻教程所作的實驗和學習筆記內容。本文大量引用了正點原子教學視頻和鏈接中的內容。
引用:
正點原子IMX6U倉庫 (GuangzhouXingyi) - Gitee.com
《【正點原子】I.MX6U嵌入式Linux驅動開發指南V1.5.2.pdf》
正點原子資料下載中心 — 正點原子資料下載中心 1.0.0 文檔
正點原子imx6ull-mini-Linux驅動之Linux I2C 驅動實驗(21)-CSDN博客
正文:
本文是 “Arm(iMX6U)Linux系統移植和根文件系統構鍵篇--2.1講uboot簡介。本節將參考正點原子的視頻教程和配套的正點原子開發指南文檔進行學習。
0. 概述
Linux系統要啟動就必須需要一個 bootloader程序,也就說芯片上電以后先運行一段bootloader程序。程序。
這段 bootloader程序會先初始化 DDR等外設,然后將 Linux內核從 flash(NAND NOR FLASH SD MMC等 )拷貝到 DDR中,最后啟動 Linux內核。
當然了,bootloader的實際工作要復雜的多,但是它最主要的工作就是啟動 Linux內核, bootloader和 Linux內核的系就跟 PC上的 BIOS和 Windows的關系一樣, bootloader就相當于 BIOS。。所以我們要先搞定bootloader,很慶幸,有很多現成的 bootloader軟件可以使用,比如 U-Boot、 vivi、 RedBoot等等,其中以 U-Boot使最為廣泛,為了方便書寫,本書會將 U-Boot寫為 uboot。
uboot的全稱是 Universal Boot Loader uboot是一個遵循 GPL協議的開源軟件, uboot是一個裸機代碼,可以看作是一個裸機綜合例程。現在的 uboot已經支持液晶屏、網絡、 USB等高級功能。 uboot官網為
The U-Boot Documentation — Das U-Boot unknown version documentation
我們可以在 uboot官網下載 uboot源碼,點擊圖 30.1.1中左側 Topics中的“ Source Code
打開如圖 30.1.2所示界面:
但是我們一般不會直接用 uboot 官方的 U-Boot 源碼的。uboot 官方的 uboot 源碼是給半導體廠商準備的,半導體廠商會下載 uboot 官方的 uboot 源碼,然后將自家相應的芯片移植進去。也就是半導體廠商會自己維護一個版本的 uboot,這個版本的 uboot 相當于是他們定制的。既然是定制的,那么肯定對自家的芯片支持會很全,雖然 uboot 官網的源碼中一般也會支持他們的芯片,但是決議是沒有半導體廠商自己維護的 uboot 全面。
- u-boot網站的源碼
- SoC芯片廠家從u-boot網站上下載源碼,然后把自家的芯片SoC移植進去,SoC廠家自己維護了一個版本的bu-boot,與原始u-boot相比,對自家的芯片支持更全面。
NXP 就 維 護 的 2016.03 這 個 版 本 的 uboot , 下 載 地 址 為 :
http://git.freescale.com/git/cgit.cgi/imx/uboot-imx.git/tag/?h=imx_v2016.03_4.1.15_2.0.0_ga&id=
rel_imx_4.1.15_2.1.0_ga
下載界面如圖 30.1.4 所示
圖 30.1.4 中的 uboot-imx_rel_imx4.1.15_2.1.0_ga.xx(xx 為 zip、 tar.gz 或 tar.bz2)就是 NXP 官方維護的 uboot,后面我們學習 uboot 移植的時候就是使用的圖 30.1.4 中的 uboot,下載ubootimx-rel_imx_4.1.15_2.1.0_ga.tar.bz2。圖 30.1.4中的 uboot 基本支持了 NXP 當前所有可以跑 Linux 的芯片,而且支持各種啟動方式,比如 EMMC、NAND、 NOR FLASH 等等,這些都是 uboot 官方所不支持的。
但是圖 30.1.4 中的 uboot 是針對NXP 自家評估板的,如果是我們自己做的板子就需要修改 NXP 官方的 uboot,使其支持我們自己做的板子,正點原子的 I.MX6U 開發板就是自己做的板子,雖然大部分都參考了 NXP 官方的I.MX6ULL EVK 開發板,但是還是有很多不同的地方,所以需要修改 NXP 官方的 uboot,使其適配正點原子的 I.MX6U 開發板。所以當我們拿到開發板以后,是有三種 uboot 的,這三種 uboot的區別如下表所示:
那么這三種 uboot 該如何選擇呢?首先 uboot 官方的基本是不會用的,因為支持太弱了。最常用的就是半導體廠商或者開發板廠商的 uboot,如果你用的半導體廠商的評估板,那么就使用半導體廠商的 uboot,如果你是購買的第三方開發板,比如正點原子的 I.MX6ULL 開發板,那么就使用正點原子提供的 uboot 源碼(也是在半導體廠商的 uboot 上修改的)。當然了,你也可以在購買了第三方開發板以后使用半導體廠商提供的 uboot, 只不過有些外設驅動可能不支持, 需要自己移植,這個就是我們常說的 uboot 移植。
本節是 uboot 的使用,所以就直接使用正點原子已經移植好的 uboot,這個已經放到了開發
板光盤中了。
1.U-Boot 初次編譯
在 Ubuntu 中創建存放 uboot 的目錄,比如我的是/home/chen/linux/uboot,然后在此目錄下新建一個名為“alientek_uboot”的文件夾用于存放正點原子提供的 uboot 源碼。(從正點原子網站上百度網盤鏈接下載)
dimon@dimon-VirtualBox:~/I.MX6ULL/uboot_altek$ lsuboot-imx-2016.03-2.1.0-g0ae7e33-v1.7.tar.bz2dimon@dimon-VirtualBox:~/I.MX6ULL/uboot_altek$
使用如下命令對其進行解壓縮
dimon@dimon-VirtualBox:~/I.MX6ULL/uboot_altek$ tar -xf uboot-imx-2016.03-2.1.0-g0ae7e33-v1.7.tar.bz2
解壓之后得到了整點原子修改移植過得針對正點原子 I.MX6ULL Alpha開發板的u-boot的源碼。解壓完成以后 alientek_uboot 文件夾內容如圖 30.2.2 所示。
圖 30.2.2 中除了 uboot-imx-2016.03-2.1.0-g8b546e4.tar.bz2 這個正點原子提供的 uboot 源碼壓縮包以外,其他的文件和文件夾都是解壓出來的 uboot 源碼。
512MB(DDR3)+8GB(EMMC)核心板
如果使用的是 512MB+8GB 的 EMMC 核心板,使用如下命令來編譯對應的 uboot
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_14x14_ddr512_emmc_defconfig
make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j12
這三條命令中 ARCH=arm 設置目標為 arm 架構, CROSS_COMPILE 指定所使用的交叉編譯器。第一條命令相當于“make distclean”,目的是清除工程,一般在第一次編譯的時候最好清理一下工程。第二條指令相當于“make mx6ull_14x14_ddr512_emmc_defconfig”,用于配置 uboot,配置文件為 mx6ull_14x14_ddr512_emmc_defconfig。最后一條指令相當于 “make -j12”也就是使用 12 核來編譯 uboot。當這三條命令執行完以后 uboot 也就編譯成功了,如圖 30.2.3 所示
編譯完成以后的 alentek_uboot 文件夾內容如圖
?可以看出,編譯完成以后 uboot 源碼多了一些文件,其中 u-boot.bin 就是編譯出來的 uboot二進制文件。uboot是個裸機程序,因此需要在其前面加上頭部(IVT、DCD等數據)才能在I.MX6U上執行,圖 30.2.4 中的 u-boot.imx 文件就是添加頭部以后的 u-boot.bin, u-boot.imx 就是我們最終要燒寫到開發板中的 uboot 鏡像文件.
每次編譯 uboot 都要輸入一長串命令,為了簡單起見,我們可以新建一個 shell 腳本文件,將這些命令寫到 shell 腳本文件里面,然后每次只需要執行 shell 腳本即可完成編譯工作。新建名為 mx6ull_alientek_emmc.sh 的 shell 腳本文件,然后在里面輸入如下內容.
##我購買的是正點原子I.MX6ULL Alpha Mini開發板,是256MB的DDR EMMC開發板
##所以選擇的 xxx_defconfig 是256MB emmc的 defconfig make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_14x14_ddr256_emmc_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j12
第 1 行是 shell 腳本要求的,必須是“#!/bin/bash”或者“#!/bin/sh”。
第 2 行使用了 make 命令,用于清理工程,也就是每次在編譯 uboot 之前都清理一下工程。這里的 make 命令帶有三個參數,第一個是 ARCH,也就是指定架構,這里肯定是 arm;第二個參數 CROSS_COMPILE 用于指定編譯器,只需要指明編譯器前綴就行了,比如 arm-linuxgnueabihf-gcc 編譯器的前綴就是“arm-linux-gnueabihf-”;最后一個參數 distclean 就是清除工程.
第 3 行也使用了 make 命令,用于配置 uboot。同樣有三個參數,不同的是,最后一個參數
是 mx6ull_14x14_ddr512_emmc_defconfig。
前面說了 uboot 是 bootloader 的一種,可以用來引導Linux,但是 uboot 除了引導 Linux 以外還可以引導其它的系統,而且 uboot 還支持其它的架構和外設, 比如 USB、 網絡、 SD 卡等。這些都是可以配置的,需要什么功能就使能什么功能。所以在編譯 uboot 之前,一定要根據自己的需求配置 uboot。
mx6ull_14x14_ddr512_emmc_defconfig就是正點原子針對 I.MX6U-ALPHA 的 EMMC 核心板編寫的配置文件,這個配置文件在 uboot源碼的 configs 目錄中。在 uboot 中,通過“make xxx_defconfig”來配置 uboot, xxx_defconfig就是不同板子的配置文件,這些配置文件都在 uboot/configs 目錄中.
在u-boot源碼目錄下, u-boot/configs/ 目錄下有非常多的 xxx_defconfig 的默認配置文件,一般一個xxx_defconfig 配置文件對應著一種硬件開發板(不同硬件開發板有不同的外設,所以u-boot的默認配置有所差異)。
?第 4 行有 4 個參數,用于編譯 uboot,通過第 3 行配置好 uboot 以后就可以直接“make”編譯 uboot 了。其中 V=1 用于設置編譯過程的信息輸出級別; -j 用于設置主機使用多少線程編譯uboot,最好設置成我們虛擬機所設置的核心數,如果在 VMware 里面給虛擬就分配了 4 個核,那么使用-j4 是最合適的,這樣 4 個核都會一起編譯。
使用 chmod 命令給予 mx6ull_alientek_emmc.sh 文件可執行權限,然后就可以使用這個 shell
腳本文件來重新編譯 uboot,命令如下
chmod +x build_im6ull_emmc.sh
bash build_im6ull_emmc.sh
2.U-Boot 燒寫與啟動
uboot 編譯好以后就可以燒寫到板子上使用了, 這里我們跟前面裸機例程一樣,將 uboot燒寫到 SD 卡中,然后通過 SD 卡來啟動來運行 uboot。使用 imxdownload 軟件燒寫,命令如下:
chmod 777 imxdownload //給予 imxdownload 可執行權限,一次即可
./imxdownload u-boot.bin /dev/sdd //燒寫到 SD 卡,不能燒寫到/dev/sda 或 sda1 設備里面
等待燒寫完成,完成以后將 SD 卡插到 I.MX6U-ALPHA 開發板上, BOOT 設置從 SD 卡啟動,使用 USB 線將 USB_TTL 和電腦連接,也就是將開發板的串口 1 連接到電腦上。打開MobaXterm,設置好串口參數并打開,最后復位開發板。在 MobaXterm 上出現“Hit any key to stop autoboot: ”倒計時的時候按下鍵盤上的回車鍵,默認是 3 秒倒計時,在 3 秒倒計時結束以后如果沒有按下回車鍵的話 uboot 就會使用默認參數來啟動 Linux 內核了。如果在 3 秒倒計時結束之前按下回車鍵,那么就會進入 uboot 的命令行模式,如圖 30.3.1 所示
從圖 30.3.1 可以看出,當進入到 uboot 的命令行模式以后,左側會出現一個“=>”標志。
uboot 啟動的時候會輸出一些信息,這些信息如下所示:
U-Boot 2016.03 (Mar 07 2025 - 23:35:20 +0800)CPU: Freescale i.MX6ULL rev1.1 792 MHz (running at 396 MHz)
CPU: Industrial temperature grade (-40C to 105C) at 23C
Reset cause: POR
Board: I.MX6U ALPHA|MINI
I2C: ready
DRAM: 512 MiB
MMC: FSL_SDHC: 0, FSL_SDHC: 1
*** Warning - bad CRC, using default environmentIn: serial
Out: serial
Err: serial
switch to partitions #0, OK
mmc0 is current device
Net: FEC1
Error: FEC1 address not set.Normal Boot
Hit any key to stop autoboot: 0
=>
第 1 行是 uboot 版本號和編譯時間,可以看出,當前的 uboot 版本號是 2016.03,編譯時間是 2025?年 3?月 7 日凌晨 23?點 35 分
第 3 和第 4 行是 CPU 信息,可以看出當前使用的 CPU 是飛思卡爾的 I.MX6ULL(I.MX 以前屬于飛思卡爾,然而飛思卡爾被 NXP 收購了), 頻率為 792MHz,但是此時運行在 396MHz。這顆芯片是工業級的, 結溫為-40° C~105° C
第 5 行是復位原因,當前的復位原因是 POR。 I.MX6ULL 芯片上有個 POR_B 引腳,將這個引腳拉低即可復位 I.MX6ULL。
第 6 行是板子名字,當前的板子名字為“I.MX6U ALPHA|MINI”。
第 7 行提示 I2C 準備就緒。
第 8 行提示當前板子的 DRAM(內存)為 512MB,如果是 NAND 版本的話內存為 256MB。
第 9 行提示當前有兩個 MMC/SD 卡控制器: FSL_SDHC(0)和 FSL_SDHC(1)。 I.MX6ULL支持兩個 MMC/SD,正點原子的 I.MX6ULL EMMC 核心板上 FSL_SDHC(0)接的 SD(TF), FSL_SDHC(1)接的 EMMC。第 10 和第 11 行是 LCD 型號,當前的 LCD 型號是 ATK-LCD-7-1024x600 (1024x600),分辨率為 1024x600,格式為 RGB888(24 位)。
第 12~14 是標準輸入、標準輸出和標準錯誤所使用的終端,這里都使用串口(serial)作為終端
第 15 和 16 行是切換到 emmc 的第 0 個分區上,因為當前的 uboot 是 emmc 版本的,也就是從 emmc 啟動的。我們只是為了方便將其燒寫到了 SD 卡上,但是它的“內心”還是 EMMC的。所以 uboot 啟動以后會將 emmc 作為默認存儲器,當然了,你也可以將 SD 卡作為 uboot 的存儲器,這個我們后面會講解怎么做。
第 17 行是網口信息,提示我們當前使用的 FEC1 這個網口, I.MX6ULL 支持兩個網口。
第 18 行提示 FEC1 網卡地址沒有設置,后面我們會講解如何在 uboot 里面設置網卡地址。
第 20 行提示正常啟動,也就是說 uboot 要從 emmc 里面讀取環境變量和參數信息啟動 Linux內核了
第 21 行是倒計時提示,默認倒計時 3 秒,倒計時結束之前按下回車鍵就會進入 Linux 命令行模式。如果在倒計時結束以后沒有按下回車鍵,那么 Linux 內核就會啟動, Linux 內核一旦啟動, uboot 就會壽終正寢。
這個就是 uboot 默認輸出信息的含義, NAND 版本的 uboot 也是類似的,只是 NAND 版本的就沒有 EMMC/SD 相關信息了,取而代之的就是 NAND 的信息,比如 NAND 容量大小信息。
uboot 是來干活的,我們現在已經進入 uboot 的命令行模式了,進入命令行模式以后就可以給 uboot 發號施令了。當然了,不能隨便發號施令,得看看 uboot 支持哪些命令,然后使用這些uboot 所支持的命令來做一些工作。下一節就講解 uboot 命令的使用。
3 U-Boot 命令使用
進入 uboot 的命令行模式以后輸入“help”或者“?”,然后按下回車即可查看當前 uboot 所支持的命令,如圖 30.4.1 所示
=> ?
? - alias for 'help'
base - print or set address offset
bdinfo - print Board Info structure
bmode - sd1|sd2|qspi1|normal|usb|sata|ecspi1:0|ecspi1:1|ecspi1:2|ecspi1:3|esdhc1|esdhc2|esdhc3|esdhc4 [noreset]
boot - boot default, i.e., run 'bootcmd'
bootd - boot default, i.e., run 'bootcmd'
bootelf - Boot from an ELF image in memory
圖 30.4.1 中只是 uboot 的一部分命令, 具體的命令列表以實際為準。圖 30.4.1 中的命令并不是 uboot 所支持的所有命令,前面說過 uboot 是可配置的,需要什么命令就使能什么命令。所以圖 30.4.1 中的命令是正點原子提供的 uboot 中使能的命令, uboot 支持的命令還有很多,而且
也可以在 uboot 中自定義命令。這些命令后面都跟有命令說明,用于描述此命令的作用,但是
命令具體怎么用呢?我們輸入“help(或?) 命令名”既可以查看命令的詳細用法,以“bootz”這
個命令為例,我們輸入如下命令即可查看“bootz”這個命令的用法:
=> help bootz
bootz - boot Linux zImage image from memoryUsage:
bootz [addr [initrd[:size]] [fdt]]- boot Linux zImage stored in memoryThe argument 'initrd' is optional and specifies the addressof the initrd in memory. The optional argument ':size' allowsspecifying the size of RAW initrd.When booting a Linux kernel which requires a flat device-treea third argument is required which is the address of thedevice-tree blob. To boot that kernel without an initrd image,use a '-' for the second argument. If you do not pass a thirda bd_info struct will be passed instead=>
圖 30.4.2 中就詳細的列出了“bootz”這個命令的詳細,其它的命令也可以使用此方法查詢具體的使用方法。接下來我們學習一下一些常用的 uboot 命令。
3.1?信息查詢命令
常用的和信息查詢有關的命令有 3 個: bdinfo、 printenv 和 version。先來看一下 bdinfo 命令,此命令用于查看板子信息,直接輸入“bdinfo”即可,結果如圖 30.4.1.1 所示:
bdinfo 命令
=> bdinfo
arch_number = 0x00000000
boot_params = 0x80000100
DRAM bank = 0x00000000
-> start = 0x80000000
-> size = 0x20000000
eth0name = FEC1
ethaddr = (not set)
current eth = FEC1
ip_addr = <NULL>
baudrate = 115200 bps
TLB addr = 0x9FFF0000
relocaddr = 0x9FF55000
reloc off = 0x18755000
irq_sp = 0x9EF52EA0
sp start = 0x9EF52E90
=>
從圖 30.4.1.1 中可以得出 DRAM 的起始地址和大小、啟動參數保存起始地址、波特率、sp(堆棧指針)起始地址等信息
命令“printenv”用于輸出環境變量信息, uboot 也支持 TAB 鍵自動補全功能,輸入“print”然后按下 TAB 鍵就會自動補全命令,直接輸入“print”也可以。輸入“print”,然后按下回車鍵,環境變量如圖 30.4.1.2 所示
=> printenv
baudrate=115200
board_name=EVK
board_rev=14X14
boot_fdt=try
bootcmd=run findfdt;mmc dev ${mmcdev};mmc dev ${mmcdev}; if mmc rescan; then if run loadbootscript; then run bootscript; else if run loadimage; then run mmcboot; else run netboot; fi; fi; else run netboot; fi
bootcmd_mfg=run mfgtool_args;bootz ${loadaddr} ${initrd_addr} ${fdt_addr};
bootdelay=1
bootscript=echo Running bootscript from mmc ...; source
console=ttymxc0
ethact=FEC1
ethprime=FEC
fdt_addr=0x83000000
fdt_file=imx6ull-14x14-emmc-4.3-480x272-c.dtb
fdt_high=0xffffffff
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;
image=zImage
initrd_addr=0x83800000
initrd_high=0xffffffff
ip_dyn=yes
loadaddr=0x80800000
loadbootscript=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${script};
loadfdt=fatload mmc ${mmcdev}:${mmcpart} ${fdt_addr} ${fdt_file}
loadimage=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${image}
logo_file=alientek.bmp
mfgtool_args=setenv bootargs console=${console},${baudrate} rdinit=/linuxrc g_mass_storage.stall=0 g_mass_storage.removable=1 g_mass_storage.file=/fat g_mass_storage.ro=1 g_mass_storage.idVendor=0x066F g_mass_storage.idProduct=0x37FF g_mass_storage.iSerialNumber="" clk_ignore_unused
mmcargs=setenv bootargs console=${console},${baudrate} root=${mmcroot}
mmcautodetect=yes
mmcboot=echo Booting from mmc ...; run mmcargs; if test ${boot_fdt} = yes || test ${boot_fdt} = try; then if run loadfdt; then bootz ${loadaddr} - ${fdt_addr}; else if test ${boot_fdt} = try; then bootz; else echo WARN: Cannot load the DT; fi; fi; else bootz; fi;
mmcdev=0
mmcpart=1
mmcroot=/dev/mmcblk0p2 rootwait rw
netargs=setenv bootargs console=${console},${baudrate} root=/dev/nfs ip=dhcp nfsroot=${serverip}:${nfsroot},v3,tcp
netboot=echo Booting from net ...; run netargs; if test ${ip_dyn} = yes; then setenv get_cmd dhcp; else setenv get_cmd tftp; fi; ${get_cmd} ${image}; if test ${boot_fdt} = yes || test ${boot_fdt} = try; then if ${get_cmd} ${fdt_addr} ${fdt_file}; then bootz ${loadaddr} - ${fdt_addr}; else if test ${boot_fdt} = try; then bootz; else echo WARN: Cannot load the DT; fi; fi; else bootz; fi;
panel=ATK-LCD-4.3-480x272
script=boot.scr
splashimage=0x88000000
splashpos=m,m
stderr=serial
stdin=serial
stdout=serialEnvironment size: 2583/8188 bytes
=>
在圖 30.4.1.2 中有很多的環境變量,比如 baudrate、 board_name、 board_rec、 boot_fdt、 bootcmd
等等。 uboot 中的環境變量都是字符串,既然叫做環境變量,那么它的作用就和“變量”一樣。
比如 bootdelay 這個環境變量就表示 uboot 啟動延時時間,默認 bootdelay=3,也就默認延時 3
秒。前面說的 3 秒倒計時就是由 bootdelay 定義的,如果將 bootdelay 改為 5 的話就會倒計時 5s
了。 uboot 中的環境變量是可以修改的,有專門的命令來修改環境變量的值,稍后我們會講解。
命令 version 用于查看 uboot 的版本號,輸入“version”, uboot 版本號如圖 30.4.1.3 所示:
?
=> versionU-Boot 2016.03 (Mar 07 2025 - 23:35:20 +0800)
arm-linux-gnueabihf-gcc (Linaro GCC 4.9-2017.01) 4.9.4
GNU ld (Linaro_Binutils-2017.01) 2.24.0.20141017 Linaro 2014_11-3-git
=>
從圖 30.4.1.3 可以看出,當前 uboot 版本號為 2016.03, 2020 年 8 月 7 日編譯的,編譯器為arm-poky-linux-gnueabi-gcc,這是 NXP 官方提供的編譯器,正點原子出廠系統用的此編譯器編譯的,但是本教程我們統一使用 arm-linux-gnueabihf-gcc。
3.2 環境變量操作命令
1、修改環境變量
環境變量的操作涉及到兩個命令: setenv 和 saveenv,命令 setenv 用于設置或者修改環境變量的值。命令 saveenv 用于保存修改后的環境變量,一般環境變量是存放在外部 flash 中的,uboot 啟動的時候會將環境變量從 flash 讀取到 DRAM 中。所以使用命令 setenv 修改的是 DRAM中的環境變量值,修改以后要使用 saveenv 命令將修改后的環境變量保存到 flash 中,否則的話uboot 下一次重啟會繼續使用以前的環境變量值。
命令 saveenv 使用起來很簡單,格式為:
saveenv
比如我們要將環境變量 bootdelay 改為 5,就可以使用如下所示命令
setenv bootdelay 5
saveenv
上述命令執行過程如圖 30.4.2.1 所示
=> setenv bootdelay 5
=> saveenv
Saving Environment to MMC...
Writing to MMC(0)... done
=>
在圖 30.4.2.1 中, 當我們使用命令 saveenv 保存修改后的環境變量的話會有保存過程提示信息,根據提示可以看出環境變量保存到了 MMC(0)中,也就是 SD 卡中。因為我們現在將 uboot燒寫到了 SD 卡里面,所以會保存到 MMC(0)中。如果燒寫到 EMMC 里面就會提示保存到MMC(1),也就是 EMMC 設備,同理,如果是 NAND 版本核心板的話就會提示保存到 NAND中。
修改 bootdelay 以后,重啟開發板, uboot 就是變為 5 秒倒計時,如圖 30.4.2.2 所示:
?有時候我們修改的環境變量值可能會有空格, 比如 bootcmd、 bootargs 等, 這個時候環境變量值就得用單引號括起來,比如下面修改環境變量 bootargs 的值:
setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw'
saveenv
上面命令設置 bootargs 的值為“console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw”,其中“console=ttymxc0,115200”、“root=/dev/mmcblk1p2”、“rootwait”和“rw”相當于四組“值”,這四組“值”之間用空格隔開,所以需要使用單引號‘’將其括起來,表示這四組“值”都屬于環境變bootargs。
2,新建環境變量
命令 setenv 也可以用于新建命令,用法和修改環境變量一樣,比如我們新建一個環境變量author, author 的值為我的名字拼音: zuozhongkai,那么就可以使用如下命令
=> setenv author chenhaoxu
=> saveenv
新建命令 author 完成以后重啟 uboot,然后使用命令 printenv 查看當前環境變量,如圖
?
Error: FEC1 address not set.Normal Boot
Hit any key to stop autoboot: 0
=>
=> printenv
author=chenhaoxu
baudrate=115200
board_name=EVK
board_rev=14X14
從圖 30.4.2.3 可以看到新建的環境變量: author,其值為: chenhaoxu
?3、刪除環境變量
既然可以新建環境變量, 肯定也可以刪除環境變量,刪除環境變量也是使用命令 setenv,要刪除一個環境變量只要給這個環境變量賦空值即可,比如我們刪除掉上面新建的 author 這個環境變量,命令如下
=> setenv author
=> saveenv
4,內存操作命令
內存操作命令就是用于直接對 DRAM 進行讀寫操作的,常用的內存操作命令有 md、 nm、mm、 mw、 cp 和 cmp。我們依次來看一下這些命令都是做什么的。
md 命令
md 命令用于顯示內存值,格式如下
md[.b, .w, .l] address [# of objects]
命令中的[.b .w .l]對應 byte、 word 和 long,也就是分別以 1 個字節、 2 個字節、 4 個字節來顯示內存值。 address 就是要查看的內存起始地址, [# of objects]表示要查看的數據長度,這個數據長度單位不是字節,而是跟你所選擇的顯示格式有關。比如你設置要查看的內存長度為20(十六進制為 0x14),如果顯示格式為.b 的話那就表示 20 個字節;如果顯示格式為.w 的話就表示 20 個 word,也就是 20*2=40 個字節;如果顯示格式為.l 的話就表示 20 個 long,也就是20*4=80 個字節。另外要注意:
uboot 命令中的數字都是十六進制的!不是十進制的!
比如你想查看以 0X80000000 開始的 20 個字節的內存值,顯示格式為.b 的話,應該使用
如下所示命令
md.b 80000000 14
而不是
md.b 80000000 20
上面說了, uboot 命令里面的數字都是十六進制的,所以可以不用寫“0x”前綴,十進制的 20 其六進制為 0x14,所以命令 md 后面的個數應該是 14,如果寫成 20 的話就表示查看32(十六進制為 0x20)個字節的數據。分析下面三個命令的區別
md.b 0x80000000 0x10
md.w 0x80000000 0x10
md.l 0x80000000 0x10
上面這三個命令都是查看以 0X80000000 為起始地址的內存數據,第一個命令以.b 格式顯示,長度為 0x10,也就是 16 個字節;第二個命令以.w 格式顯示,長度為 0x10,也就是 16*2=32個字節;最后一個命令以.l 格式顯示,長度也是 0x10,也就是 16*4=64 個字節。這三個命令的執行結果如圖 30.4.3.1 所示:
=> md.b 0x80000000 0x10
80000000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
=> md.w 0x80000000 0x10
80000000: ffff ffff ffff ffff ffff ffff ffff ffff ................
80000010: ffff ffff ffff ffff ffff ffff ffff ffff ................
=> md.l 0x80000000 0x10
80000000: ffffffff ffffffff ffffffff ffffffff ................
80000010: ffffffff ffffffff ffffffff ffffffff ................
80000020: ffffffff ffffffff ffffffff ffffffff ................
80000030: ffffffff ffffffff ffffffff ffffffff ................
=>
nm 命令
nm 命令用于修改指定地址的內存值,命令格式如下:
=> help nm
nm - memory modify (constant address)Usage:
nm [.b, .w, .l] address
=>
nm 命令同樣可以以.b、 .w 和.l 來指定操作格式,比如現在以.l 格式修改 0x80000000 地址的數據為 0x12345678。輸入命令
=> nm.l 0x80000000
80000000: ffffffff ? 0x12345678
在圖 30.4.3.2 中, 80000000 表示現在要修改的內存地址, 0500e031 表示地址 0x80000000 現在的數據,?后面就可以輸入要修改后的數據 0x12345678,輸入完成以后按下回車,然后再輸入‘q’即可退出,如圖 30.4.3.3 所示
=> nm.l 0x80000000
80000000: ffffffff ? 0x12345678
80000000: 12345678 ?
80000000: 12345678 ?
80000000: 12345678 ? q
=>
從上述命令執行過程可以看到nm修改內存中指定地址的數據時,連續輸入回車鍵,內存地址不會自動增加。
修改完成以后在使用命令 md 來查看一下有沒有修改成功,如圖 30.4.3.4 所示
=> md.l 0x80000000 1
80000000: 12345678 xV4.
=>
從圖 30.4.3.4 可以看出,此時地址 0X80000000 的值變為了 0x12345678。
mm 命令
mm 命令也是修改指定地址內存值的,使用 mm 修改內存值的時候地址會自增,而使用命令 nm 的話地址不會自增。
mm命令格式
=> help mm
mm - memory modify (auto-incrementing address)Usage:
mm [.b, .w, .l] address
=>
比如以.l 格式修改從地址 0x80000000 開始的連續 3 個內存塊(3*4=12個字節)的數據為 0X05050505,操作如圖 30.4.3.5 所示
=> mm.l 0x80000000
80000000: 12345678 ? 0x50505050
80000004: ffffffff ? 0x50505050
80000008: ffffffff ? 0x50505050
8000000c: ffffffff ? q
=>
從圖 30.4.3.5 可以看出,修改了地址 0X80000000、 0X80000004 和 0X8000000C 的內容為0x05050505。使用命令 md 查看修改后的值,結果如圖 30.4.3.6 所示
=> md.l 0x80000000 3
80000000: 50505050 50505050 50505050 PPPPPPPPPPPP
=>
從圖 30.4.3.6 可以看出內存數據修改成功。
mw 命令
命令 mw 用于使用一個指定的數據填充一段內存,命令格式如下
?
=> ? mw
mw - memory write (fill)Usage:
mw [.b, .w, .l] address value [count]
=>
mw 命令同樣可以以.b、 .w 和.l 來指定操作格式, address 表示要填充的內存起始地址, value為要填充的數據, count 是填充的長度。比如使用.l 格式將以 0X80000000 為起始地址的 0x10 個內存塊(0x10 * 4=64 字節)填充為 0X0A0A0A0A,命令如下:
=> mw 0x80000000 0x0A0A0A0A 0x10
然后使用命令 md 來查看,如圖 30.4.3.7 所示
=> md.l 0x80000000 0x10
80000000: 0a0a0a0a 0a0a0a0a 0a0a0a0a 0a0a0a0a ................
80000010: 0a0a0a0a 0a0a0a0a 0a0a0a0a 0a0a0a0a ................
80000020: 0a0a0a0a 0a0a0a0a 0a0a0a0a 0a0a0a0a ................
80000030: 0a0a0a0a 0a0a0a0a 0a0a0a0a 0a0a0a0a ................
=>
?從圖 30.4.3.7 可以看出內存數據修改成功
CP命令
cp 是數據拷貝命令,用于將 DRAM 中的數據從一段內存拷貝到另一段內存中,或者把 Nor Flash 中的數據拷貝到 DRAM 中。命令格式如下:
=> ? cp
cp - memory copyUsage:
cp [.b, .w, .l] source target count
=>
cp 命令同樣可以以.b、 .w 和.l 來指定操作格式, source 為源地址, target 為目的地址, count為拷貝的長度。我們使用.l 格式將 0x80000000 處的地址拷貝到 0X80000100 處,長度為 0x10 個內存塊(0x10 * 4=64 個字節),命令如下所示:
=> cp.l 0x80000000 0x80000100 0x10
結果如圖 30.4.3.8 所示
=> cp.l 0x80000000 0x80000100 0x10
=> md.l 0x80000000 0x10
80000000: 0a0a0a0a 0a0a0a0a 0a0a0a0a 0a0a0a0a ................
80000010: 0a0a0a0a 0a0a0a0a 0a0a0a0a 0a0a0a0a ................
80000020: 0a0a0a0a 0a0a0a0a 0a0a0a0a 0a0a0a0a ................
80000030: 0a0a0a0a 0a0a0a0a 0a0a0a0a 0a0a0a0a ................
=> md.l 0x80000100 0x10
80000100: 0a0a0a0a 0a0a0a0a 0a0a0a0a 0a0a0a0a ................
80000110: 0a0a0a0a 0a0a0a0a 0a0a0a0a 0a0a0a0a ................
80000120: 0a0a0a0a 0a0a0a0a 0a0a0a0a 0a0a0a0a ................
80000130: 0a0a0a0a 0a0a0a0a 0a0a0a0a 0a0a0a0a ................
=>
在圖 30.4.3.8 中,先使用 md.l 命令打印出地址 0x80000000 和 0x80000100 處的數據,然后使用命令cp.l將0x80000100處的數據拷貝到0x80000100處。最后使用命令md.l查看0x80000100處的數據有沒有變化,檢查拷貝是否成功。
cmp 命令
cmp 是比較命令,用于比較兩段內存的數據是否相等,命令格式如下
=> ? cmp
cmp - memory compareUsage:
cmp [.b, .w, .l] addr1 addr2 count
=>
cmp 命令同樣可以以.b、 .w 和.l 來指定操作格式, addr1 為第一段內存首地址, addr2 為第二段內存首地址, count 為要比較的長度。我們使用.l 格式來比較 0x80000000 和 0X80000100 這兩個地址數據是否相等,比較長度為 0x10 個內存塊(16 * 4=64 個字節),命令如下所示:
=> cmp.l 0x80000000 0x80000100 0x10
Total of 16 word(s) were the same
=>
從圖 30.4.3.9 可以看出兩段內存的數據相等。 我們再隨便挑兩段內存比較一下,比如地址0x80002000 和 0x800003000,長度為 0X10,比較結果如圖 30.4.3.10 所示
=> cmp.l 0x80002000 0x80003000 0x10
Total of 16 word(s) were the same
=> md.l 0x80002000 0x10
80002000: ffffffff ffffffff ffffffff ffffffff ................
80002010: ffffffff ffffffff ffffffff ffffffff ................
80002020: ffffffff ffffffff ffffffff ffffffff ................
80002030: ffffffff ffffffff ffffffff ffffffff ................
=> md.l 0x80003000 0x10
80003000: ffffffff ffffffff ffffffff ffffffff ................
80003010: ffffffff ffffffff ffffffff ffffffff ................
80003020: ffffffff ffffffff ffffffff ffffffff ................
80003030: ffffffff ffffffff ffffffff ffffffff ................
=>
哈哈🥲,剛好是一樣的,因為上電初始化之后DRAM內存里的數據是隨機的,可能剛好DRAM中內存里 0x80002000 和 0x80003000 里的數據都是0xff。
網絡操作命令
uboot 是支持網絡的,我們在移植 uboot 的時候一般都要調通網絡功能,因為在移植 linux kernel 的時候需要使用到 uboot 的網絡功能做調試。uboot 支持大量的網絡相關命令,比如 dhcp, ping、 nfs 和 tftpboot,我們接下來依次學習一下這幾個和網絡有關的命令。
在使用 uboot 的網絡功能之前先用網線將開發板的 ENET2 接口和電腦或者路由器連接起來, I.MX6U-ALPHA 開發板有兩個網口: ENET1 和 ENET2,一定要連接 ENET2,不能連接錯了, ENET2 接口如圖 30.4.4.1 所示。
建議開發板和主機 PC 都連接到同一個路由器上!最后設置表 30.4.4.1 中所示的幾個環境變量
=> setenv ipaddr 192.168.1.50
=> setenv ethaddr b8:ae:1d:01:00:00
=> setenv gatewayip 192.168.1.1
=> setenv netmask 255.255.255.0
=> setenv serverip 192.168.1.253
=> saveenv
?注意,網絡地址環境變量的設置要根據自己的實際情況,確保 Ubuntu 主機和開發板的 IP地址在同一個網段內,比如我現在的開發板和電腦都在 192.168.1.0 這個網段內,所以設置開發板的 IP 地址為 192.168.1.50,我的 Ubuntu 主機的地址為 192.168.1.253,因此 serverip 就是
192.168.1.253。 ethaddr 為網絡 MAC 地址,是一個 48bit 的地址,如果在同一個網段內有多個開發板的話一定要保證每個開發板的 ethaddr 是不同的,否則通信會有問題!設置好網絡相關的環境變量以后就可以使用網絡相關命令了。
ping命令
開發板的網絡能否使用,是否可以和服務器(Ubuntu 主機)進行通信,通過 ping 命令就可以驗證,直接 ping 服務器的 IP 地址即可,比如我的服務器 IP 地址為 192.168.1.253,命令如下:
?
?
?
?
?
?
?
?