• IMX6ULL | 从零开始移植uboot |(一)单板建立与编译


    i.MX6ULL - 从零开始移植uboot-imx_v2020.04_5.4.70_2.3.0



    前言

             ~~~~~~~~         一般的,我们做linux开发,移植uboot和kernel的时候并不需要从头开始移植,uboot和kernel庞大又复杂,从头开始移植不仅耗时耗力,而且还很容易出错,没有人比SOC设计人员更熟悉他们自己的芯片,所以我们不管是软件还是硬件的设计都是根据官方出的开发板进行参考设计,并且在设计的过程中,比较重要的部分会和官方保持一致,例如EMMC、内存、NAND、SD卡、网络等,这是因为如果想要启动起uboot内存是必须的,只要比较重要或常用的几个外设跟官方的原理图保持一致或基本保持一致,那么我们下载进官方的uboot或者依据官方给的uboot进行简单更改,就能适配我们自己的板子,大大缩短了我们的开发周期。

    uboot只是一个启动linux的作用,所以没必要将项目中使用到的驱动都适配好,因为在uboot阶段如果需要适配驱动是需要去修改源代码的,没有一些经验和基础是做不到这一步的。在项目开发阶段,uboot中最常用的就是控制台(串口)、SD卡、USB、网络这四个,对于i.MX6ULL来说,设计的时候对于这三个外设只要我们跟官方保持一致,那么用官方的uboot拿来就能直接用!毕竟我们项目开发的目的是在linux系统基础上开发应用而已(ps:这也是现在越来越趋向于嵌入式linux应用开发工程师的职位,因为现在有了设备树的开发方式以及半导体厂家做的BSP足够好了,所以嵌入式linux驱动工程师需要做的事越来越少。。。)。


    我所使用的开发板是飞凌的,OKMX6UL-C底板+FETMX6UL-C核心板(512MB内存+8G EMMC)。


    1、环境搭建

    开发环境使用的是ubuntu1804,64位系统。

    交叉编译器使用的是linaro提供的gcc7.5.0版本(用最新版本是因为移植的uboot版本也比较新,旧版本的交叉编译器无法编译新版uboot),im6ull支持硬件浮点,所以要使用带hf字样的编译器,如下图:

    https://releases.linaro.org/components/toolchain/binaries/7.5-2019.12/arm-linux-gnueabihf/

    编译uboot和内核所必须的依赖库安装:

    sudo apt-get install -y libncurses5-dev lsb-core lib32stdc++6 bison flex lzop git
    
    • 1

    将交叉编译器通过samba传入到虚拟机ubuntu中,解压:

    tar xvf gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz
    
    • 1

    然后将交叉编译器加入到系统环境变量,打开/etc/profile文件:

    sudo gedit /etc/profile
    
    • 1

    在最后一行加入并保存退出:

    export PATH=$PATH:/home/hello/install/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin
    
    • 1

    最后使环境变量生效:

    source /etc/profile
    
    • 1

    测试一下交叉编译器是否可用了,输入以下命令查看arm-linux-gnueabihf-gcc的版本号:

    arm-linux-gnueabihf-gcc -v
    
    • 1

    2、NXP官方原版UBOOT编译

    下载uboot(下载的很慢):

    git clone https://source.codeaurora.org/external/imx/uboot-imx
    cd uboot-imx
    
    • 1
    • 2

    查看所有分支:

    git branch --all
    
    • 1

    检出分支,这里我检出5.4.70_2.3.0版本,新版本的uboot支持了设备树,基于设备树的uboot可以更刚方便的便于我们移植:

    git checkout imx_v2020.04_5.4.70_2.3.0
    
    • 1

    修改顶层Makefile,指定编译器和架构:

    gedit Makefile
    
    • 1

    找到CROSS_COMPILE,在下方添加以下内容后并保存退出(效果如下图):

    ARCH = arm
    CROSS_COMPILE = arm-linux-gnueabihf-
    
    • 1
    • 2
    • ARCH:用于指定编译平台是ARM
    • CROSS_COMPILE:指定交叉编译工具链

    在configs目录下存在许多mx6ull的配置文件,其中带evk字样的为NXP为他们自己的EVK开发板定制的配置文件。

    我使用的板子是SOC是IM6ULL,EMMC版本,且也是根据NXP的EVK开发板参考设计而来,所以使用mx6ull_14x14_evk_emmc_defconfig这个配置文件进行编译。

    编译前先清理一下uboot工程:

    make distclean
    
    • 1

    加载编译配置(效果如下图):

    make mx6ull_14x14_evk_emmc_defconfig
    
    • 1

    开始编译:

    make -j 2
    
    • 1

    make的【-j】参数表示多核编译,能够加快编译速度。例如我虚拟机设置的是双核CPU,所以【- j 2】表示双核一块编译uboot源码。

    有时候你还会看到make -V=1,V参数表示编译时候的显示的详情。

    编译完成(如下图):

    其中u-boot-dtb.imx就是我们放到SD卡中刷写进板子的文件。


    3、自己的单板UBOOT建立并编译

    以上步骤是基于NXP官方EVK开发板进行的编译,下面我们建立自己的目标板进行编译,这样的话方便我们对工程进行管理。

    以下步骤参考的是官网EVK开发板所需的配置以及相关文件,以此来建立自己的板子配置。

    添加自己板子的配置文件和头文件:

    cp configs/mx6ull_14x14_evk_emmc_defconfig configs/mx6ull_14x14_hello_emmc_defconfig
    cp include/configs/mx6ullevk.h include/configs/mx6ullhello.h
    
    • 1
    • 2

    修改配置文件:

    gedit configs/mx6ull_14x14_hello_emmc_defconfig
    
    • 1
    • 找到CONFIG_TARGET_MX6ULL_14X14_EVK=y改为CONFIG_TARGET_MX6ULL_14X14_HELLO=y,这是指定编译我们自己的板子

    • 找到CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=board/freescale/mx6ullevk/imximage.cfg"改为CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=board/freescale/mx6ullhello/imximage.cfg",这是指定我们板子的配置

    • 找到CONFIG_DEFAULT_DEVICE_TREE="imx6ull-14x14-evk-emmc"改为CONFIG_DEFAULT_DEVICE_TREE="imx6ull-14x14-hello-emmc"

    • 找到CONFIG_DM_74X164=y改为# CONFIG_DM_74X164=y,官网EVK开发板使用了一个74LV594,我们的板子没有使用所以屏蔽掉

    • 找到CONFIG_SOFT_SPI=y改为# CONFIG_SOFT_SPI=y,屏蔽掉软件模拟的SPI,这是官方驱动74LV594用的,我们自己的板子也用不到

    修改头文件:

    gedit include/configs/mx6ullhello.h
    
    • 1

    修改宏编译:

    添加自己开发板的设备树内容:

    添加自己板子描述文件的文件夹:

    cp board/freescale/mx6ullevk board/freescale/mx6ullhello -r
    
    • 1

    修改board\freescale\mx6ullhello目录下的文件。

     cd board/freescale/mx6ullhello/
    
    • 1

    重命名mx6ullevk.cmx6ullhello.c

    mv mx6ullevk.c mx6ullhello.c
    
    • 1

    修改imximage.cfg文件如下:

    gedit imximage.cfg
    
    • 1


    修改Kconfig文件如下(注意该文件endif后面必须有换行):

    gedit Kconfig
    
    • 1

    修改MAINTAINERS文件如下:

    gedit MAINTAINERS
    
    • 1

    修改Makefile如下:

    gedit Makefile
    
    • 1

    添加自己板子所对应的设备树文件(新版本的uboot支持了设备树,移植阶段可以直接修改设备树进行裁剪移植,更方便开发,这也是选用新版本uboot的原因):

    添加imx6ull-14x14-hello-emmc.dts并修改:

    cp arch/arm/dts/imx6ull-14x14-evk-emmc.dts arch/arm/dts/imx6ull-14x14-hello-emmc.dts
    gedit arch/arm/dts/imx6ull-14x14-hello-emmc.dts
    
    • 1
    • 2

    添加imx6ull-14x14-hello.dts并修改:

    cp arch/arm/dts/imx6ull-14x14-evk.dts arch/arm/dts/imx6ull-14x14-hello.dts
    gedit arch/arm/dts/imx6ull-14x14-hello.dts
    
    • 1
    • 2

    添加imx6ul-14x14-hello.dtsiimx6ul-14x14-hello-u-boot.dtsi

    cp arch/arm/dts/imx6ul-14x14-evk.dtsi arch/arm/dts/imx6ul-14x14-hello.dtsi
    cp arch/arm/dts/imx6ul-14x14-evk-u-boot.dtsi arch/arm/dts/imx6ul-14x14-hello-u-boot.dtsi 
    
    • 1
    • 2

    修改arch/arm/dts/Makefile文件,将我们开发板的设备树文件添加进编译项:

    gedit arch/arm/dts/Makefile
    
    • 1

    将我们的开发板添加进编译uboot的编译选项,修改arch/arm/mach-imx/mx6/Kconfig文件:

    gedit arch/arm/mach-imx/mx6/Kconfig
    
    • 1

    找到config TARGET_MX6ULL_14X14_EVK的配置,在该配置下方添加:

    config TARGET_MX6ULL_14X14_HELLO
    	bool "Support mx6ull_14x14_hello"
    	select BOARD_LATE_INIT
    	select DM
    	select DM_THERMAL
    	select MX6ULL
    	imply CMD_DM
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7


    找到source "board/freescale/mx6ullevk/Kconfig",在下一行将我们板子的配置添加上:

    source "board/freescale/mx6ullhello/Kconfig"
    
    • 1

    如下图所示:


    4、SD卡驱动修改

    接下来我们检查一下自己板子的SD卡接口和使用到的IO是否和官方EVK开发板使用的一致,因为在uboot移植测试阶段,我们使用SD卡启动uboot是最方便的,所以首先要确保SD卡的驱动是正确的。

    一般情况下,SD卡的DATA[0:3]、CLK、CMD引脚是直接使用固定的IO,而CD(Card Detecet)引脚可能会根据自己板子的情况进行更改,我的板子原理图如下:

    从原理图可以看到CD引脚使用的是UART1_RTS这个引脚,这个引脚是GPIO1_IO19。

    检查uboot设备树文件arch/arm/dts/imx6ul-14x14-hello.dtsi中SD卡的CD引脚是否是用的这个引脚。


    检查后没问题,CD引脚跟我的原理图使用的是一致的。


    5、官方EVK开发板无用配置移除

    官方evk开发板使用了一颗扩展IO的芯片74lv595,我的板子没有用,所以需要去掉该芯片相关内容,屏蔽或删除以下内容:

    /*
    	aliases {
    		spi5 = &{/spi4};
    	};
    */
    
    /*
    	reg_can_3v3: regulator-can-3v3 {
    		compatible = "regulator-fixed";
    		regulator-name = "can-3v3";
    		regulator-min-microvolt = <3300000>;
    		regulator-max-microvolt = <3300000>;
    		gpios = <&gpio_spi 3 GPIO_ACTIVE_LOW>;
    	};
    
    	spi4 {
    		compatible = "spi-gpio";
    		pinctrl-names = "default";
    		pinctrl-0 = <&pinctrl_spi4>;
    		status = "okay";
    		pinctrl-assert-gpios = <&gpio5 8 GPIO_ACTIVE_LOW>;
    		gpio-sck = <&gpio5 11 0>;
    		gpio-mosi = <&gpio5 10 0>;
    		cs-gpios = <&gpio5 7 0>;
    		num-chipselects = <1>;
    		#address-cells = <1>;
    		#size-cells = <0>;
    
    		gpio_spi: gpio@0 {
    			compatible = "fairchild,74hc595";
    			gpio-controller;
    			#gpio-cells = <2>;
    			reg = <0>;
    			registers-number = <1>;
    			registers-default = /bits/ 8 <0x57>;
    			spi-max-frequency = <100000>;
    		};
    	};
    */
    
    &can1 {
    	pinctrl-names = "default";
    	pinctrl-0 = <&pinctrl_flexcan1>;
    	/* xceiver-supply = <&reg_can_3v3>; */
    	status = "okay";
    };
    
    &can2 {
    	pinctrl-names = "default";
    	pinctrl-0 = <&pinctrl_flexcan2>;
    	/* xceiver-supply = <&reg_can_3v3>; */
    	status = "okay";
    };
    
    /*
    	pinctrl_spi4: spi4grp {
    		fsl,pins = <
    			MX6UL_PAD_BOOT_MODE0__GPIO5_IO10	0x70a1
    			MX6UL_PAD_BOOT_MODE1__GPIO5_IO11	0x70a1
    			MX6UL_PAD_SNVS_TAMPER7__GPIO5_IO07	0x70a1
    			MX6UL_PAD_SNVS_TAMPER8__GPIO5_IO08	0x80000000
    		>;
    	};
    */
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    至此,我们已经完成了对自己板子的配置的建立,现在用我们板子的配置进行编译,编译前先清理一下:

    make distclean
    make mx6ull_14x14_hello_emmc_defconfig
    make -j 2
    
    • 1
    • 2
    • 3

    编译完成,无问题!

    成功编译通过!


    6、SD卡烧录uboot并启动测试

    下面我们将编译出的u-boot-dtb.imx使用dd命令刻录到SD卡中:

    sudo dd if=u-boot-dtb.imx of=/dev/sdb bs=1k seek=1 conv=fsync
    
    • 1

    注意:SD卡插入ubuntu后,会出现/dev/sdX,具体是/dev/sdb还是/dev/sdc又或者是/dev/sdd要视个人插入ubuntu的USB口设备情况而定。

    dd命令可从标准输入或文件中读取数据,根据指定的格式来转换数据,再输出到文件、设备或标准输出。详细命令解释参见:https://www.runoob.com/linux/linux-comm-dd.html

    • if=文件名:输入文件名,默认为标准输入。即指定源文件。
    • of=文件名:输出文件名,默认为标准输出。即指定目的文件。
    • bs=bytes:同时设置读入/输出的块大小为bytes个字节。
    • seek=blocks:从输出文件开头跳过blocks个块后再开始复制。
    • conv=<关键字>,关键字可以有以下11种:
      • conversion:用指定的参数转换文件。
      • ascii:转换ebcdic为ascii
      • ebcdic:转换ascii为ebcdic
      • ibm:转换ascii为alternate ebcdic
      • block:把每一行转换为长度为cbs,不足部分用空格填充
      • unblock:使每一行的长度都为cbs,不足部分用空格填充
      • lcase:把大写字符转换为小写字符
      • ucase:把小写字符转换为大写字符
      • swap:交换输入的每对字节
      • noerror:出错时不停止
      • notrunc:不截短输出文件
      • sync:将每个输入块填充到ibs个字节,不足部分用空(NUL)字符补齐。

    将SD卡插入板子卡槽,设置板子上的拨码开关为SD卡启动,控制台打印内容如下:

    成功启动!


    7、SD卡和eMMC测试

    测试一下SD卡和MMC是否都已经识别出来。

    mmc list  # 列出所有设备
    mmc info # 查看当前设备信息
    mmc dev 1 # 切换到emmc
    
    • 1
    • 2
    • 3


    8、网卡驱动移植测试

    emmc和SD卡都没问题了,但是网络显示错误,下面我们更改设备树,使网络也能够驱动起来。

    同样的,比对官方EVK开发板和我们自己的板子的enet所使用的的IO:


    比对后发现我的板子跟官方EVK开发板使用的IO是一致的,但是我的板子的phy还需要硬件复位的引脚,板子上有两路phy,查看原理图得知我的板子fec1的硬件复位IO是GPIO5_IO8,fec2的硬件复位引脚是GPIO5_IO04。

    从原理图得到引脚后,继续修改设备树文件arch/arm/dts/imx6ul-14x14-hello.dtsi,由于ENET1和ENET2的复位引脚都是在GPIO5下的,所以需要将这两个IO配置在imouxc_snvs节点下,找到iomuxc节点,在上方添加iomuxc_snvs节点:

    &iomuxc_snvs {
    	pinctrl-names = "default";
    	
    	pinctrl_eth2rst: eth2rstgrp {
    		fsl,pins = <
    			MX6ULL_PAD_SNVS_TAMPER4__GPIO5_IO04  0x17059    /* ENET2 NRST */
    		>;    
    	};
    	
    	pinctrl_eth1rst: eth1rstgrp {
    		fsl,pins = <
    			MX6ULL_PAD_SNVS_TAMPER8__GPIO5_IO08  0x17059    /* ENET1 NRST */
    		>;    
    	};
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    如下图所示:


    全局搜索MX6UL_PAD_SNVS_TAMPER8__GPIO5_IO08、MX6UL_PAD_SNVS_TAMPER4__GPIO5_IO04、MX6ULL_PAD_SNVS_TAMPER4__GPIO5_IO04、MX6ULL_PAD_SNVS_TAMPER8__GPIO5_IO08这四个宏,查看在其他地方有没有被使用,如果有,则屏蔽掉。


    找到&fec1&fec2节点,uboot阶段我们只只用fec2即可,修改fec1节点如下:

    &fec1 {
    	pinctrl-names = "default";
    	pinctrl-0 = <&pinctrl_enet1 &pinctrl_eth1rst>;
    	phy-mode = "rmii";
    	phy-handle = <&ethphy0>;
    	phy-reset-gpios = <&gpio5 8 GPIO_ACTIVE_LOW>;
    	phy-reset-duration = <200>;
    	mac-address = [
    		0a 1b 2c 3d 4e 5f
    	];
    	status = "okay";
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    phy-reset-gpios:指定phy的复位引脚且为拉低复位
    pht-reset-duration:指定复位的持续时间
    mac-address:指定mac地址

    修改fec2节点如下:

    &fec2 {
    	pinctrl-names = "default";
    	pinctrl-0 = <&pinctrl_enet2 &pinctrl_eth2rst>;
    	phy-mode = "rmii";
    	phy-handle = <&ethphy1>;
    	phy-reset-gpios = <&gpio5 4 GPIO_ACTIVE_LOW>;
    	phy-reset-duration = <200>;
    	mac-address = [
    		a0 b1 c2 d3 e4 f5
    	];
    	status = "okay";
    
    	mdio {
    		#address-cells = <1>;
    		#size-cells = <0>;
    
    		ethphy0: ethernet-phy@2 {
    			reg = <2>;
    			micrel,led-mode = <1>;
    			clocks = <&clks IMX6UL_CLK_ENET_REF>;
    			clock-names = "rmii-ref";
    		};
    
    		ethphy1: ethernet-phy@1 {
    			reg = <1>;
    			micrel,led-mode = <1>;
    			clocks = <&clks IMX6UL_CLK_ENET2_REF>;
    			clock-names = "rmii-ref";
    		};
    	};
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    ethphy0: ethernet-phy@2:这里的2表示的是phy1的地址为2
    ethphy1: ethernet-phy@1:这里的1表示的是phy2的地址为1

    对比我的原理图,发现phy的地址是一致的:

    可选操作:在uboot移植测试阶段可以将随机生成MAC地址使能,方便我们测试使用,但是当移植完了后进入到了上层应用开发阶段建议禁用随机MAC地址,因为使能随机MAC地址的话每次重启板子都会是新的MAC地址,不方便上层程序使用。

    打开uboot的图形化配置,使能随机MAC地址:

    make menuconfig
    
    • 1
    [*] Networking support  --->
        [*]   Random ethaddr if unset
    
    • 1
    • 2

    补充:如果你没有使能使用随机mac地址,那么需要在uboot中手动设置mac地址,设置命令如下:

    setenv ethaddr 00:04:9f:04:d2:35
    setenv eth1addr 00:04:9f:04:d2:36
    saveenv
    
    • 1
    • 2
    • 3

    其中ethaddr是指定eth0的,eth1addr是指定eth1的,具体需要设置哪一个,需要查看uboot启动时候的Net:这个信息提示。

    至此,网络的配置已经完成,再次编译uboot并将u-boot-dtb.imx刻录到SD卡中,板子使用SD卡启动,控制台打印如下(下图为使能随机MAC地址且在设备树中关闭了fec1节点的信息):

    下面这些命令是依次设置ip地址、网关、掩码、ubuntu的IP地址、保存环境变量,设置完这环境变量后就可以使用ping命令来测试网卡是否驱动成功了。

    setenv ipaddr 192.168.28.234
    setenv gatewayip 192.28.28.1
    setenv netmask 255.255.255.0
    setenv serverip 192.168.28.254
    saveenv
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在ping一下ubuntu:

    没问题,现在我们的网卡驱动就已经移植好了!


    9、uboot启动信息修改

    启动uboot的时候看到打印信息为EVK,这怎么能忍!

    修改gedit board/freescale/mx6ullhello/mx6ullhello.c文件,找到board_late_init函数,修改如下:

    gedit gedit board/freescale/mx6ullhello/mx6ullhello.c
    
    • 1

    找到checkboard函数,修改如下:

    重新编译uboot并刻录至SD卡中,开发板选择SD卡启动,控制台打印如下:

    完成!


    10、uboot增加默认IP地址的环境变量

    在开发阶段,在uboot中设置IP地址的操作比较频繁,回复一次默认环境变量,就得重新设置一下,那么有没有什么办法将ip地址增加进boot中呢?

    当然有!

    只需要在include/configs/mx6ullhello.h文件中新增以下几个宏定义即可!

    #define CONFIG_IPADDR       192.168.28.234      /* board ip */
    #define CONFIG_SERVERIP     192.168.28.254      /* ubuntu ip */
    #define CONFIG_GATEWAYIP    192.168.28.1        /* board gateway ip */
    #define CONFIG_NETMASK      255.255.255.0       /* board netmask */
    
    • 1
    • 2
    • 3
    • 4

    ends…

  • 相关阅读:
    UI自动化测试是什么?什么项目适合做UI自动化测试
    入坑KeePass(四)KeePass通过坚果云WebDav同步方法
    我的创作纪念日
    java毕业设计-基于springboot+vue的在线婚纱定制系统设计与实现,基于java的在线婚纱摄影预定系统,基于web的婚纱影楼管理系统设计,基于web婚纱影楼管理系统设计(附源码和配套资料)
    大数据flink篇之一-基础知识
    Vue2.x秘籍(上)
    自己选和被安排
    Node.js 是如何处理请求的
    Cloud Studio连接MySQL,Access denied for一系列问题
    【算法|动态规划No.19】leetcode413. 等差数列划分
  • 原文地址:https://blog.csdn.net/qq153471503/article/details/126587387