• U-Boot 移植深入


    1. 在U-Boot中驱动LCD和网络

    U-Boot 移植初探一文中介绍了如何修改官方uboot,使得 uboot能匹配我们自己的开发板,但是匹配完成后 LCD驱动和网络驱动还是不正常的,所以下面将介绍如何修改 LCD驱动和网络驱动等

    1.1 LCD驱动修改

    uboot 中修改驱动都是在对应板子的 .c文件和 .h文件中进行,即下面两个文件中:mx6ull_andyxi_emmc.cmx6ull_andyxi_emmc.h

    修改 LCD驱动重点注意以下几点:

    • LCD的 IO配置是否正确
    • LCD背光引脚 GPIO的配置
    • LCD配置参数是否正确

    我们使用的 I.MX6ULL开发板的 LCD原理图和 NXP官方的开发板一致,也就是LCD的IO和背光IO都是一样的, 所以IO部分就不用修改了,只需修改 LCD配置参数即可

    ⏩ mx6ull_andyxi_emmc.c 文件中修改下面内容:

    /*######### 原始内容 ###############################*/
    /*该代码定义了一个变量displays,类型为display_info_t,这个结构体
    是LCD信息结构体,其中包括了LCD的分辨率,像素格式,LCD的各个参数等*/
    struct display_info_t const displays[] = {
    	{
        	.bus = MX6UL_LCDIF1_BASE_ADDR,
        	.addr = 0,
        	.pixfmt = 24,
        	.detect = NULL,
       	 	.enable = do_enable_parallel_lcd,
        	.mode = {
    			.name	      = "TFT43AB",
    			.xres         = 480,
    			.yres         = 272,
    			.pixclock     = 108695,
    			.left_margin  = 8,
    			.right_margin = 4,
    			.upper_margin = 2,
    			.lower_margin = 4,
    			.hsync_len    = 41,
    			.vsync_len    = 10,
    			.sync         = 0,
    			.vmode        = FB_VMODE_NONINTERLACED
    		}
    	}
    };
    /*######### 修改后的内容 ###########################*/
    struct display_info_t const displays[] = {
    	{
        	.bus = MX6UL_LCDIF1_BASE_ADDR,
       	 	.addr = 0,
        	.pixfmt = 24,
        	.detect = NULL,
        	.enable = do_enable_parallel_lcd,
        	.mode = {
    			.name	      = "TFT7016",
    			.xres         = 1024,
    			.yres         = 600,
    			.pixclock     = 19531,
    			.left_margin  = 140,  //HBPD
    			.right_margin = 160,  //HFPD
    			.upper_margin = 20,  //VBPD
    			.lower_margin = 12,  //VFPD
    			.hsync_len    = 20,   //HSPW
    			.vsync_len    = 3,   //VSPW
    			.sync         = 0,
    			.vmode        = FB_VMODE_NONINTERLACED
    		} 
    	} 
    };
    
    • 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

    ⏩ mx6ull_andyxi_emmc.h 文件中修改 panel值

    panel=TFT7016    #根据具体使用型号修改
    
    • 1

    ⏩ 重新编译 uboot并烧写到 SD中启动后,若 LCD仍不能显示,则需要在uboot命令模式下,检查环境变量 panel的值,确保与 LCD参数里的 name一致

    panel=TFT7016    #与mx6ull_andyxi_emmc.c中修改的名称保持一致
    
    • 1
    1.2 网络驱动修改

    I.MX6UL/ULL 内部有个以太网 MAC外设,需要外接一个 PHY芯片来实现网络通信功能。我们使用的 I.MX6U 开发板提供了这两个网络接口,都使用 LAN8720A 作为 PHY 芯片。NXP 官方的I.MX6ULL EVK 开发板使用 KSZ8081 的 PHY 芯片。下面将介绍更换了PHY 芯片以后如何调整网络驱动,使网络工作正常。

    开发板 ENET1/ENET2的原理图如下示:

    在这里插入图片描述

    网络 PHY芯片LAN8720A,通过 RMII接口与 I.MX6ULL相连,引脚与 NXP官方的 I.MX6ULL EVK 开发板基本一样,只是复位引脚不同。从上图可以看出,复位引脚ENET1_RST 接到了 I.M6ULL 的 SNVS_TAMPER7 引脚上,复位引脚 ENET2_RST 接到了 SNVS_TAMPER8 引脚上

    修改网络驱动重点注意以下几点:

    • LAN8720A复位引脚初始化
    • LAN8720A的器件ID
    • LAN8720A驱动

    ⏩ 在 mx6ull_andyxi_emmc.c中添加复位引脚驱动

    /* 结构体数组fec1_pads和fec2_pads是ENET1和ENET2这两个网口的IO 
     * 配置参数,在这两个数组中添加两个网口的复位 IO 配置参数 */
    /*######### 原始内容 ###############################*/
    static iomux_v3_cfg_t const fec1_pads[] = {
      MX6_PAD_GPIO1_IO06__ENET1_MDIO | MUX_PAD_CTRL(MDIO_PAD_CTRL),
      MX6_PAD_GPIO1_IO07__ENET1_MDC | MUX_PAD_CTRL(ENET_PAD_CTRL),
      ......
      MX6_PAD_ENET1_RX_ER__ENET1_RX_ER | MUX_PAD_CTRL(ENET_PAD_CTRL),
      MX6_PAD_ENET1_RX_EN__ENET1_RX_EN | MUX_PAD_CTRL(ENET_PAD_CTRL),
    };
    
    static iomux_v3_cfg_t const fec2_pads[] = {
      MX6_PAD_GPIO1_IO06__ENET2_MDIO | MUX_PAD_CTRL(MDIO_PAD_CTRL),
      MX6_PAD_GPIO1_IO07__ENET2_MDC | MUX_PAD_CTRL(ENET_PAD_CTRL),
      ......
      MX6_PAD_ENET2_RX_EN__ENET2_RX_EN | MUX_PAD_CTRL(ENET_PAD_CTRL),
      MX6_PAD_ENET2_RX_ER__ENET2_RX_ER | MUX_PAD_CTRL(ENET_PAD_CTRL),
    };
    /*######### 修改后的内容 ###########################*/
    static iomux_v3_cfg_t const fec1_pads[] = {
      MX6_PAD_GPIO1_IO06__ENET1_MDIO | MUX_PAD_CTRL(MDIO_PAD_CTRL),
      MX6_PAD_GPIO1_IO07__ENET1_MDC | MUX_PAD_CTRL(ENET_PAD_CTRL),
      ......
      MX6_PAD_ENET1_RX_ER__ENET1_RX_ER | MUX_PAD_CTRL(ENET_PAD_CTRL),
      MX6_PAD_ENET1_RX_EN__ENET1_RX_EN | MUX_PAD_CTRL(ENET_PAD_CTRL),
      MX6_PAD_SNVS_TAMPER7__GPIO5_IO07 | MUX_PAD_CTRL(NO_PAD_CTRL), //添加此行
    };
    
    static iomux_v3_cfg_t const fec2_pads[] = {
      MX6_PAD_GPIO1_IO06__ENET2_MDIO | MUX_PAD_CTRL(MDIO_PAD_CTRL),
      MX6_PAD_GPIO1_IO07__ENET2_MDC | MUX_PAD_CTRL(ENET_PAD_CTRL),
      ......
      MX6_PAD_ENET2_RX_EN__ENET2_RX_EN | MUX_PAD_CTRL(ENET_PAD_CTRL),
      MX6_PAD_ENET2_RX_ER__ENET2_RX_ER | MUX_PAD_CTRL(ENET_PAD_CTRL),
      MX6_PAD_SNVS_TAMPER8__GPIO5_IO08 | MUX_PAD_CTRL(NO_PAD_CTRL), //添加此行
    };
    
    /* 函数 setup_iomux_fec 就是根据 fec1_pads 和 fec2_pads 这两个网络IO配置
       数组来初始化I.MX6ULL的网络IO,此处需要在其中添加网络复位IO的初始化代码,
       并且复位一下 PHY 芯片
     */
    /*######### 原始内容 ###############################*/
    static void setup_iomux_fec(int fec_id)
    {
      if (fec_id == 0)
        imx_iomux_v3_setup_multiple_pads(fec1_pads, ARRAY_SIZE(fec1_pads));
      else
        imx_iomux_v3_setup_multiple_pads(fec2_pads,	ARRAY_SIZE(fec2_pads));
    }
    /*######### 修改后的内容 ###########################*/
    static void setup_iomux_fec(int fec_id)
    {
      if (fec_id == 0)
      {
        imx_iomux_v3_setup_multiple_pads(fec1_pads,ARRAY_SIZE(fec1_pads));
        gpio_direction_output(ENET1_RESET, 1);
        gpio_set_value(ENET1_RESET, 0);
        mdelay(20);
        gpio_set_value(ENET1_RESET, 1);
      }
      else {
        imx_iomux_v3_setup_multiple_pads(fec2_pads,ARRAY_SIZE(fec2_pads));
        gpio_direction_output(ENET2_RESET, 1);
        gpio_set_value(ENET2_RESET, 0);
        mdelay(20);
        gpio_set_value(ENET2_RESET, 1);
      }
    }
    
    • 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
    • 66
    • 67
    • 68

    ⏩ 在 mx6ull_andyxi_emmc.h中修改 PHY器件地址及驱动

    /*######### 原始内容 ###############################*/
    #ifdef CONFIG_CMD_NET
    #define CONFIG_CMD_PING
    #define CONFIG_CMD_DHCP
    #define CONFIG_CMD_MII
    #define CONFIG_FEC_MXC
    #define CONFIG_MII
    #define CONFIG_FEC_ENET_DEV 1  //用于选择使用哪个网卡,默认为1(ENET2)
    #if (CONFIG_FEC_ENET_DEV == 0)
    #define IMX_FEC_BASE ENET_BASE_ADDR
    #define CONFIG_FEC_MXC_PHYADDR 0x2  //ENET1的PHY地址,默认为0x2
    #define CONFIG_FEC_XCV_TYPE RMII
    #elif (CONFIG_FEC_ENET_DEV == 1)
    #define IMX_FEC_BASE ENET2_BASE_ADDR
    #define CONFIG_FEC_MXC_PHYADDR 0x1  //ENET2的PHY地址,默认为0x1
    #define CONFIG_FEC_XCV_TYPE RMII
    #endif
    #define CONFIG_ETHPRIME "FEC"
    #define CONFIG_PHYLIB
    #define CONFIG_PHY_MICREL  //用于使能Micrel公司的PHY驱动(KSZ8081芯片)
    #endif
    /*######### 修改后的内容 ###########################*/
    #ifdef CONFIG_CMD_NET
    #define CONFIG_CMD_PING
    #define CONFIG_CMD_DHCP
    #define CONFIG_CMD_MII
    #define CONFIG_FEC_MXC
    #define CONFIG_MII
    #define CONFIG_FEC_ENET_DEV 1
    #if (CONFIG_FEC_ENET_DEV == 0)
    #define IMX_FEC_BASE ENET_BASE_ADDR
    #define CONFIG_FEC_MXC_PHYADDR 0x0  //修改ENET1的PHY地址为0x0
    #define CONFIG_FEC_XCV_TYPE RMII
    #elif (CONFIG_FEC_ENET_DEV == 1)
    #define IMX_FEC_BASE ENET2_BASE_ADDR
    #define CONFIG_FEC_MXC_PHYADDR 0x1  //修改ENET2的PHY地址为为0x1
    #define CONFIG_FEC_XCV_TYPE RMII
    #endif
    #define CONFIG_ETHPRIME "FEC"
    #define CONFIG_PHYLIB
    #define CONFIG_PHY_SMSC	 //使能SMSC公司的PHY驱动(LAN8720A是SMSC生产的)
    #endif
    
    • 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

    ⏩ 在 mx6ull_andyxi_emmc.c中删除 74LV595的驱动代码

    /* NXP 官方I.MX6ULL EVK 开发板使用74LV595来扩展 IO,两个网络的复位引脚
     * 就是由74LV595来控制的,I.MX6U-ALPHA开发板并没有使用74LV595,因此删除掉
     */
    /*######### 原始内容 ###############################*/
    #define IOX_SDI IMX_GPIO_NR(5, 10)
    #define IOX_STCP IMX_GPIO_NR(5, 7)
    #define IOX_SHCP IMX_GPIO_NR(5, 11)
    #define IOX_OE IMX_GPIO_NR(5, 8)
    /*##### 修改后的内容:以上四行替换为以下两行 #######*/
    /* ENET1 的复位引脚连接到 SNVS_TAMPER7 上,对应 GPIO5_IO07
     * ENET2 的复位引脚连接到 SNVS_TAMPER8 上,对应 GPIO5_IO08
     */
    #define ENET1_RESET IMX_GPIO_NR(5, 7)
    #define ENET2_RESET IMX_GPIO_NR(5, 8)
    
    //删除74LV595的IO配置参数结构体
    /*######### 删除以下内容 ###############################*/
    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),
    };
    
    //删除74LV595的初始化函数
    /*######### 删除以下内容 ###############################*/
    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);
    };
    
    //删除iox74lv_set函数(用于控制74LV595的IO输出电平)
    /*######### 删除以下内容 ###############################*/
    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]);
        else
          gpio_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);
    };
    
    /* 删除板子初始化函数 board_init 中的74lv595 的GPIO初始化代码 */
    /*######### 删除部分代码 ###############################*/
    int board_init(void)
    {
      ......
      imx_iomux_v3_setup_multiple_pads(iox_pads, ARRAY_SIZE(iox_pads));//删除此行 
      iox74lv_init();  //删除此行
      ......
      return 0;
    }
    
    • 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
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80

    ⏩ 在drivers/net/phy/phy.c 中的 genphy_update_link函数里,添加 SMSC的 PHY芯片条件编译代码段

    /*######### 修改后的内容 ###########################*/
    int genphy_update_link(struct phy_device *phydev){
      unsigned int mii_reg;
    //以下为添加的SMSC的PHY芯片条件编译代码段,只有使用SMSC的PHY才会执行
    #ifdef CONFIG_PHY_SMSC
      static int lan8720_flag = 0;
      int bmcr_reg = 0;
      if (lan8720_flag == 0) {
        bmcr_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
        phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET);
        while(phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR) & 0X8000) {
          udelay(100);
        }	
        phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, bmcr_reg);
        lan8720_flag = 1;
      }
    #endif
    
      mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR);
      ......
      return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    至此网络的复位引脚驱动修改完成,重新编译 uboot,然后将 u-boot.bin 烧写到 SD 卡中并启动,uboot 启动信息如下图所示:

    在这里插入图片描述

    由上图可见当前使用FEC1这个网口(ENET2),但此时网络地址还没有设置。在uboot使用网络之前还要先设置以下几个网络环境变量

    setenv ipaddr 192.168.10.50 	  //开发板IP地址
    setenv ethaddr b8:ae:1d:01:00:00  //开发板网卡MAC地址
    setenv gatewayip 192.168.10.1 	  //开发板默认网关
    setenv netmask 255.255.255.0 	  //开发板子网掩码
    setenv serverip 192.168.10.100 	  //服务器地址,也就是Ubuntu地址
    saveenv 			  			  //保存环境变量
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    设置好环境变量以后就可以在 uboot 中使用网络了,用网线将开发板上的 ENET2与电脑连接起来,保证开发板和电脑在同一个网段内,通过 ping 命令来测试网络连接,命令如下:

    ping 192.168.1.100
    
    • 1

    结果如下图示,说明ping 主机成功,至此说明ENET2网络工作正常

    在这里插入图片描述

    将 mx6ull_andyxi_emmc.h中的CONFIG_FEC_ENET_DEV参数改为 0,然后重新编译 uboot 并烧写到 SD 卡中重启,按以上步骤可以测试 ENET1 的网络是否正常工作

    1.3 其他修改

    在 uboot启动信息中会有“Board: MX6ULL 14x14 EVK”这一句,即板子名字为“ MX6ULL 14x14 EVK”,可将其改为我们自已的名字“ MX6ULL ANDYXI EMMC”

    ⏩ 在mx6ull_andyxi_emmc.c中的checkboard函数里,做如下修改

    /*######### 修改后的内容 ###########################*/
    int checkboard(void)
    {
      if (is_mx6ull_9x9_evk())
        puts("Board: MX6ULL 9x9 EVK\n");
      else
        puts("Board: MX6ULL ANDYXI EMMC\n");
      return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    修改完成以后重新编译 uboot 并烧写到 SD 卡中验证,uboot 启动信息如下图,可见名字已经修改过来了

    在这里插入图片描述

    2. bootcmd和bootargs环境变量

    uboot 中有两个非常重要的环境变量 bootcmd 和 bootargs,是采用类似 shell 脚本语言编写的,里面有很多 NXP自己定义的的环境变量引用
    文件 mx6ull_andyxi_emmc.h中的宏CONFIG_EXTRA_ENV_SETTINGS保存着这些环境变量的默认值,其内容如下:

    #if defined(CONFIG_SYS_BOOT_NAND)
    #define CONFIG_EXTRA_ENV_SETTINGS \
      CONFIG_MFG_ENV_SETTINGS \
      "panel=TFT43AB\0" \
      "fdt_addr=0x83000000\0" \
      "fdt_high=0xffffffff\0" \
      ......
      "bootz ${loadaddr} - ${fdt_addr}\0"
    #else
    #define CONFIG_EXTRA_ENV_SETTINGS \
      CONFIG_MFG_ENV_SETTINGS \
      "script=boot.scr\0" \
      "image=zImage\0" \
      "console=ttymxc0\0" \
      "fdt_high=0xffffffff\0" \
      "initrd_high=0xffffffff\0" \
      "fdt_file=undefined\0" \
      ......
      "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" \
    
    • 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
    2.1 bootcmd环境变量

    bootcmd 保存着 uboot 默认命令, uboot 倒计时结束后就会执行其中的命令。一般是用来启动内核的,比如读取 EMMC或 NAND Flash中的内核镜像文件和设备树文件到 DRAM中,然后启动内核

    可在 uboot启动后进入命令行设置 bootcmd环境变量的值。如果 EMMC或者 NAND中没有保存 bootcmd的值,那么 uboot就会使用默认值,板子第一次运行 uboot 时都会使用默认值来设置 bootcmd 环境变量

    打开文件 include/env_default.h,在此文件中有如下所示内容:

    ......
    #ifdef CONFIG_BOOTARGS
      "bootargs=" CONFIG_BOOTARGS "\0"
    #endif
    #ifdef CONFIG_BOOTCOMMAND
      "bootcmd=" CONFIG_BOOTCOMMAND "\0"
    #endif
    ......
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    env_default.h中指定了很多环境变量的默认值,比如 bootcmd 的默认值是CONFIG_BOOTCOMMAND,bootargs的是CONFIG_BOOTARGS。我们可在mx6ull_andyxi_emmc.h 文件中通过设置宏CONFIG_BOOTCOMMAND来设置 bootcmd 的默认值, NXP官方设置的 CONFIG_BOOTCOMMAND 值如下:

    #define CONFIG_BOOTCOMMAND \
      "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"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    2.2 bootargs环境变量

    bootargs 保存着 uboot 传递给 Linux 内核的参数,以下面命令为例

    setenv bootargs console= ttymxc0, 115200 root= /dev/mmcblk1p2 rootwait rw
    
    • 1

    bootargs 中常用的参数有:

    • console:用来设置 linux终端,即通过什么设备来和 Linux进行交互,是串口还是 LCD屏幕,一般设置串口作为 Linux终端,这样就可以在电脑上通过串口助手来和 linux进行交互了
    console= ttymxc0, 115200
    //console 为 ttymxc0,因为 linux启动以后I.MX6ULL的串口1
    //在linux下的设备文件就是/dev/ttymxc0
    //ttymxc0 后面的“115200”,是设置串口的波特率
    
    • 1
    • 2
    • 3
    • 4
    • root:用来设置根文件系统的位置
    root= /dev/mmcblk1p2 rootwait rw
    // /dev/mmcblk1p2用于指明根文件系统存放在mmcblk1设备的分区2中
    // /dev/mmcblkxpy(x=0~ n,y=1~ n)表示mmc设备x的分区y
    // rootwait表示等待mmc设备初始化完成以后再挂载,否则的话会出错
    // rw 表示根文件系统是可以读写的,不加rw的话可能只能读而无法进行写操作
    
    • 1
    • 2
    • 3
    • 4
    • 5

    rootfstype:一般与root一起使用, 用于指定根文件系统类型,如果根文件系统为ext 格式的话此选项无所谓。如果根文件系统是 yaffs、 jffs 或 ubifs 的话就需要设置此选项,指定根文件系统的类型

    3. U-Boot启动Linux测试

    uboot 已经移植好后,就要测试一下 uboot 能不能完成它的工作:启动 Linux 内核。这里测试两种启动 Linux 内核的方法,一种是直接从 EMMC 启动,一种是从网络启动

    3.1 从EMMC启动Linux

    从 EMMC 启动也就是将编译出来的 Linux 镜像文件 zImage 和设备树文件保存在 EMMC中, uboot 从 EMMC 中读取这两个文件并启动,这个是产品最终的启动方式。此处默认已经将 zImage 文件和设备树文件烧写到了 EMMC 中,可以直接读取来进行测试

    ⏩ 使用命令“ls mmc 1:1”检查 EMMC 的分区 1 中是否已有相关文件,下图结果表示已有相关文件

    在这里插入图片描述

    ⏩ 设置 bootargs 和 bootcmd 这两个环境变量

    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-andyxi-emmc.dtb; bootz 80800000 - 83000000;'
    saveenv
    
    • 1
    • 2
    • 3
    • 4

    ⏩ 输入“boot”命令,启动内核即可

    3.2 从网络启动Linux

    从网络启动 linux系统的唯一目的就是为了调试!不管是为了调试 linux系统还是 linux下的驱动,每次修改 linux系统文件或者 linux下的某个驱动后都要将其烧写到 EMMC 中去测试,这样太麻烦了。可以设置从网络启动,将 linux镜像文件和根文件系统都放到 Ubuntu下指定的文件夹中,这样每次重新编译 linux内核或者 linux驱动后只需要将其拷贝到这个指定的文件夹中即可,这样就无需频繁的烧写 EMMC

    可以通过 nfs或者 tftp从 Ubuntu中下载 zImage和设备树文件,本文使用 tftp从 Ubuntu中下载 zImage和设备树文件,默认已经将 zImage和设备树文件放到 Ubuntu下的 tftp目录中

    ⏩ 设置 bootargs 和 bootcmd 这两个环境变量

    setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw'
    setenv bootcmd 'tftp 80800000 zImage; tftp 83000000 imx6ull-andyxi-emmc.dtb; bootz
    80800000 - 83000000'
    saveenv
    
    • 1
    • 2
    • 3
    • 4

    ⏩ 输入“boot”命令,启动内核,下图结果表示内核启动成功

    在这里插入图片描述

    4.U-Boot移植总结

    uboot 移植到此结束,简单总结一下 uboot 移植的过程:

    • 不管是购买的还是自己做的开发板,基本都是参考半导体厂商的 dmeo板,半导体厂商会在他们的开发板上移植好 uboot、kernel和 rootfs等,最终制作好 BSP包提供给用户。我们可在官方提供的 BSP包的基础上添加自已的板子,即俗称的移植
    • 购买的或者自己做的开发板一般不会原封不动的照抄半导体厂商的 demo板,都会根据实际情况来做修改,有修改就会涉及到 uboot下驱动的移植
    • 一般 uboot中需要解决串口、NAND、EMMC或 SD卡、网络和 LCD动,因为 uboot的主要目的是启动 Linux内核,所以不用考虑太多的外设驱动
    • 在 uboot中添加自己的开发板信息,并根据实际情况来修改 uboot中的驱动
  • 相关阅读:
    【JAVA】总结线程Thread的基本用法
    【高性能计算】CUDA,OpenCL,FPGA 加速,MPI
    java web:springboot mysql开发的一套家政预约上门服务系统源码:家政上门服务系统的运行流程
    SpringBoot2.0数据访问之整合数据源(Druid)
    如何看待黑客入侵我们的电脑?会有哪些影响?如何感知及应对?
    python实验2 π的计算
    python企业编码管理的程序(附源码)
    Netty5-Netty模型
    SAP ABAP——数据类型(四)【TYPE系列关键字】
    基于SpringBoot的导师双选系统设计与实现
  • 原文地址:https://blog.csdn.net/Chuangke_Andy/article/details/126724626