目录
信息查询命令:bdinfo\printenv\version
食用方法
需要有一块开发板(没有固定的哪一家,我用的是NXP的6ull,至于哪一家的我不说了),以及完善的、配套的学习资料。先学习一些Linux中常用的shell指令(例如你们熟知的删库跑路指令),还有makeflie的知识也学一下(用来写编译文件的)。还有最重要的一句话(大概意思):Linux里面的操作,实质上都是对文件的操作。然后慢慢食用这一句话。
上述这一些知识点,无需全部一字不漏的记下来,背出来。大概了解一下即可学习,重点是掌握学习的思维。另外,如果可以的话,强烈建议学习过单片机的RTOS、bootloader、OTA再来学习Linux,这样会帮助你更好理解Linux。在这里,强烈推荐走马观花地学一下RT-Thread。
我不是官方托,是因为这玩意长得太像Linux了。
最后,可以开始学习Linux(嵌入式方向)。
注意,本文只提供学习参考(相当于课外参考书,详细内容请配合开发板教程进行学习,那才是上课的教材,这个只是参考书!!!),需要配合你的开发板和配套教程食用。到这里,默认你已经安装好虚拟机,安装好Ubuntu的,并且已经把单片机玩明白了的。如果没明白,不建议直接食用。
Warning
提示:如果你看的是某火教程的话,他家板子是跑Debian的(他家连让你学uboot的机会都不给,直接把uboot、内核、设备树、根文件都打包好一个镜像的。不过他家板子硬件上确实要比某粒子的香),让你烧进去板子然后直奔驱动开发的,你可以跳过前面uboot、Linux移植、根文件这几部分。
Linux系统分层的意义
在Linux中,它是把内核态(内核源码工作的空间)、底层硬件(gpio、uart、iic、adc、以太网、音频通信等外设)、用户态(用户可此可操作空间)分开。分开的原因很简单:防止不同区域的工作影响到其他区域的工作(为了系统的安全性),甚至使得系统内核崩溃。例如:
1)底层硬件和系统内核中间隔着抽象层,是为了整个系统有更好的交互性。
2)用户和系统内核中间隔着应用层,是为了防止用户的不正当操作:直接操作底层硬件或者操作内核导致系统崩溃。
在底层的,实实在在的物理硬件,通过不同的逻辑电路和实实在在的物理寄存器一一对应。让底层硬件进行工作,需要把其对应的寄存器设置好,再利用函数对这一些操作进行封装,这一些写好的函数(用于操作底层硬件的函数)就是该硬件对应的Linux驱动。(Linux驱动工程师)
平时所说的嵌入式QT开发,就是给这些跑Linux的板子接上屏幕,写一个图形界面的应用程序。这一些应用程序,需要在板子的Linux系统里面安装一个可以运行QT的环境,才可以把在电脑上交叉编译(在电脑上编译板卡的程序)好的QT应用程序移植到板子上并且运行。这一些应用程序,通常都需要调用上述做好的Linux驱动函数进行工作的。(Linux应用工程师)
在这里,引申出两个工作岗位:Linux驱动工程师和Linux应用工程师。
理论上,只要处理器性能跟得上,单片机(这里只说ARM Cortex-M系列的,RISC-V的另提)也可以跑Linux。可是因为单片机没有一个叫MMU(Memory Management Unit内存管理单元)的东西,所以没跑上。但是,最新的Linux系统已经支持无MMU系列芯片。所以,你会看到有人在STM32H7跑Linux了。
系统移植和烧写
基本上,所有能运行Linux系统的soc(片上系统)都需要将系统镜像烧写到NAND、EMMC、SD卡等其他不易失性的存储器上,或者板子有uboot(这个后面讲到)搭好了一个局域网,从指定机器里面下载打包好的镜像。当启动Linux系统的时候,需要将这些不易失性的存储器上的数据拷贝到DDR(双倍速率的RAM)上面运行。在这里有好几种移植的方法。
Windows系统下通过OTG烧写
芯片厂商会提供芯片的匹配Linux系统,或者客户修改后的Linux系统(这个我还没学到),打包成一个烧写工具。只需要把板子的设置到USB-OTG启动方式(板子需要有对应的硬件),板子通过USB线连接到电脑,使用这个工具进行烧写即可(烧写到NADA或者EMMC里面)。这个烧写方式适合已经完成开发的产品一键烧写。
Ubuntu脚本烧写
利用芯片厂商提供的烧写脚本,把Ubuntu里面编译好全部文件烧写到SD卡里面。再把SD卡插到板子上,再启动。这一种方式比较笨比,不推荐。
Windows脚本烧写
这个和ubuntu脚本烧写的工作原理差不多,通过windows下面的脚本烧写到SD卡里面,比较笨比,不推荐。
通过uboot进行操作
在开发阶段强烈推荐使用该方法。先在Ubuntu里面将uboot编译好,使用官方脚本把编译好的uboot烧写到SD卡里面。把SD插到板卡上面,使用SD卡启动方式启动uboot。通过uboot配置好nfs、tftp的通信方式,从主服务器那里把系统镜像拷过来启动。后面将会针对这个展开学习。
Debian移植(EBF6ULL系列请看!!!)
Debian系统其实也是Linux的一种,它的系统结构框架和Linux也是大同小异。需要移植这一个系统的是某火家的Linux开发板。如果你是使用他家开发板的话,后面的uboot、Linux内核移植、根文件目录可以跳过,但是本章你务必要看。因为你看了他家的教学视频,你会有以下几个坑要踩。还有,如果你想深入了解整个系统是怎么启动,由什么组成的,请按照顺序看下去。如果你不是他家的板子,请跳过本部分。不建议你用某火的板子跑某例子的系统。
系统烧写
因为他家板子出厂的时候已经再EMMC里面固化好系统,为了方便起见,请你自备一个大于2G的SD卡即可。在拿到他家的资料包以后,建议烧写这一个镜像(选中的)。
第二个镜像是已经移植了QT环境的镜像,目前驱动学习阶段暂不需要这一个。有的人是用NADA Falsh版本的,用OTG脚本把系统烧写到NADA Falsh里面会直接显示空间不足,所以需要烧写到SD卡里面启动。至于烧写这些XZ文件,需要用到一个东西:Etcher,这个东西自行安装。
电脑插上SD卡之后,打开软件。我用的版本比较旧,你们用的应该是新版,界面有点不太一样。第一步先选择选中XZ文件;然后第二步选中你的SD卡;第三步开始烧写,有可能烧写失败,但是只要是在显示验证阶段的时候显示失败,那问题不大(如果是烧写阶段失败的,用擦除工具擦除之后重新烧写即可)。直接弹出SD卡,插到板子上面使用就可以了。
(擦除工具)
给开发板安装nfs(注意!这里有坑)
到这里,默认你已经在Ubuntu里面安装好nfs了。在上一步完成系统烧写之后,开发板拨码开关设置到SD卡启动,然后打开串口终端,电路板上电等待系统启动完成(记得用网线接上网络,就是你的开发板的ETH1接口和你家路由器接上,和你的电脑主机形成一个局域网。)
登录系统,用户名:root,密码:root。你如果按照他家当前的视频教程,你会发现你根本安装不了nfs。因为你没有更新源(不然你后面没办法安装软件)!!!执行以下命令:
sudo apt-get -y update
然后执行以下命令,安装nfs:
sudo apt install nfs-common -y
挂载
然后等待安装完成,然后你用以下的这个命令,你就可以看到你主机上Ubuntu的共享文件夹位置(我主机的ip给分了206,不是固定的)。
showmount -e 192.168.31.206
然后,使用以下这一个命令,把你主机的共享文件夹挂载到你的开发板子上:
sudo mount -t nfs 192.168.31.206:/home/linux/nfs /mnt
其中,192.168.31.206是我Ubuntu的ip,记得ip后面加上:。然后就是Ubuntu里面的nfs位置。空格,再输入/mnt即可。然后你就可以看到,你在Ubuntu里面的nfs新建一个文件,你的开发板再mnt里面ls一下也可以看到。
取消挂载
使用以下命令即可(在开发板上):
sudo umount /mnt
uboot
在这一章节,会重点讲以下内容:uboot的编译&SD卡的烧写、uboot打印的信息、uboot的指令、nfs和tftp搭建与使用。至于uboot图像化操作,这个看情况,如果有时间就讲,没时间就算了。
uboot是什么
如果你单片机玩的还挺溜,我就告诉你uboot相当于bootloader。如果你没玩过单片机,那么请看下面这一个示意图。uboot相当于一个裸机程序,一个用来启动Linux的程序。我们知道Linux系统功能十分庞大,而核心是系统内核。而uboot则是在Linux启动之前,修改一下系统将要启动时候的配置。通过uboot,我们可以在Linux启动之前升级系统,切换系统,重装系统等等。这个和平时用的PC电脑上的BIOS是相类似的。
如何获得uboot
关于uboot如何获得,可以参考你的板子提供的教程(某粒子、某火、某山教程)。另外,uboot是开源的,免费的。一般地,uboot是可以修改的,但通常会使用soc厂商提供的uboot。
uboot的食用方法
首先,uboot是一个工具。它可以帮助Linux驱动工程师进行Linux驱动开发,甚至是裸机开发(但是一般没有人这样做)。关于uboot,需要重点学习uboot的命令,uboot的操作方法,在uboot下配置nfs、tftp并且使用,以及uboot启动的时候打印出来的信息。学习玩这些以后,你可以利用uboot进行开发。
uboot的初次编译&烧写
在获得uboot后,请把uboot拉到Ubuntu里面,把文件进行解压。这里要用到这一个软件。
具体使用方法请参考根据各大教程进行学习,这里不再赘述。把uboot的压缩包拉到Ubuntu里面之后,需要对其进行解压。在终端里面,进入到uboot存放的文件夹,执行以下命令进行解压(我用的6ull使用这个包):
tar -vxjf uboot-imx-2016.03-2.1.0-g8b546e4.tar.bz2
然后,这个文件夹里面就会多了这一些内容。
除了红色的哪个文件以外,其他都是uboot的源码。然后,需要对这里的文件进行编译。因为我用的板子是512MB的DDR+8GB的EMMC,那就依次执行下面的命令:
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
当然这样会累死人的,这里会弄一个shell文件,把这里的东西链接起来进行编译。内容是:
#!/bin/bash
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
最后需要把这一个shell文件绿了,至于怎么绿了,执行sudo chomd 777命令就可以了。然后再执行这一个文件,就完成编译了,某粒子的教程都会讲这个怎么绿。
注意:不同板子不一样的,请跟着你的板子教程弄!!!!!!
然后,进行编译,编译完成以后就会多了下面这么多的bin文件,这一些文件都是用来烧写到SD卡里面的。
最后一步,还要再把imxdownload给绿了(提前把这个东西拷贝到uboot所在文件夹)。这个东西如果详细看裸机开发的话就会知道这个东西什么情况,就是一个官方脚本,用来烧写bin文件到SD卡的。
chmod 777 imxdownload
把uboot.bin烧写到SD卡里面。然后就完成了烧写,SD卡插到板子上,给板子接上串口就可以看到uboot的启动信息。
/imxdownload u-boot.bin /dev/sdd
修改uboot配置
如果你移植的是官方(芯片厂)的uboot,而不是用的开发板官方提供的uboot(用开发板官方的uboot可以跳过这一部分,有兴趣的可以细看一下)。那么,你需要生成一些uboot配置文件,让芯片厂的uboot适应你现在用的板子。其实市面上几乎所有的开发板的都是参照最原始的官方demo板设计。那么修改的内容主要是围绕端口的配置。如果你不修改配置,那么你后续使用uboot的话可能会出现各种问题。(例如不能ping网络)
添加配置文件
理论上,这个芯片官方的uboot是可以在你手上的开发板上运行的,所以就直接在原来芯片官方提供的uboot上面做修改。先在之前编译好的uboot里面,找到configs这一个文件夹。然后进行如下操作:
cd configs
cp mx6ull_14x14_evk_emmc_defconfig mx6ull_xxx_emmc_defconfig
在configs里面,复制一个广泛的evk配置文件mx6ull_14x14_evk_emmc_defconfig并且从新命名为你喜欢的名字:mx6ull_xxx_emmc_defconfig。然后打开mx6ull_xxx_emmc_defconfig,做出下面修改(修改第一行和第四行,这个是修改后的):
CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=board/freescale/mx6ull_xxx_emmc/imximage.cfg,MX6ULL_EVK_EMMC_REWORK"
CONFIG_ARM=y
CONFIG_ARCH_MX6=y
CONFIG_TARGET_MX6ULL_xxx_EMMC=y
CONFIG_CMD_GPIO=y
在include/configs下面,添加你的开发板头文件。复制一份mx6ullevk.h并且改名为你喜欢的名字:mx6ull_xxx_emmc.h
cp include/configs/mx6ullevk.h mx6ull_xxx_emmc.h
然后打开文件,把头文件的头部和尾部修改一下(主要是删除一些不要的功能):
#ifndef __MX6ULL_ALEITENK_EMMC_CONFIG_H
#define __MX6ULL_ALEITENK_EMMC_CONFIG_H
#include <asm/arch/imx-regs.h>
#include <linux/sizes.h>
#include "mx6_common.h"
#include <asm/imx-common/gpio.h>
/*......这里省略一些东西,其他参考改一下可以了*/
#define is_mx6ull_9x9_evk() CONFIG_IS_ENABLED(TARGET_MX6ULL_9X9_EVK)
#ifdef CONFIG_TARGET_MX6ULL_9X9_EVK
#define PHYS_SDRAM_SIZE SZ_256M
#define CONFIG_BOOTARGS_CMA_SIZE "cma=96M "
#else
#define PHYS_SDRAM_SIZE SZ_512M
#define CONFIG_BOOTARGS_CMA_SIZE ""
/* DCDC used on 14x14 EVK, no PMIC */
#undef CONFIG_LDO_BYPASS_CHECK
#endif
/* SPL options */
/* We default not support SPL
* #define CONFIG_SPL_LIBCOMMON_SUPPORT
* #define CONFIG_SPL_MMC_SUPPORT
* #include "imx6_spl.h"
*/
#define CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG
#define CONFIG_DISPLAY_CPUINFO
#define CONFIG_DISPLAY_BOARDINFO
/* Size of malloc() pool */
#define CONFIG_SYS_MALLOC_LEN (16 * SZ_1M)
#define CONFIG_BOARD_EARLY_INIT_F
#define CONFIG_BOARD_LATE_INIT
#define CONFIG_MXC_UART
#define CONFIG_MXC_UART_BASE UART1_BASE
/* MMC Configs */
#ifdef CONFIG_FSL_USDHC
#define CONFIG_SYS_FSL_ESDHC_ADDR USDHC2_BASE_ADDR
/* NAND pin conflicts with usdhc2 */
#ifdef CONFIG_SYS_USE_NAND
#define CONFIG_SYS_FSL_USDHC_NUM 1
#else
#define CONFIG_SYS_FSL_USDHC_NUM 2
#endif
#endif
/* I2C configs */
#define CONFIG_CMD_I2C
#ifdef CONFIG_CMD_I2C
#define CONFIG_SYS_I2C
#define CONFIG_SYS_I2C_MXC
#define CONFIG_SYS_I2C_MXC_I2C1 /* enable I2C bus 1 */
#define CONFIG_SYS_I2C_MXC_I2C2 /* enable I2C bus 2 */
#define CONFIG_SYS_I2C_SPEED 100000
/*......这里省略一些东西,其他参考改一下可以了*/
#define CONFIG_SYS_MMC_IMG_LOAD_PART 1
#ifdef CONFIG_SYS_BOOT_NAND
#define CONFIG_MFG_NAND_PARTITION "mtdparts=gpminand:64m(boot),16m(kernel),16m(dtb),1m(misc),-(rootfs) "
#else
#define CONFIG_MFG_NAND_PARTITION ""
#endif
#define CONFIG_MFG_ENV_SETTINGS \
"mfgtool_args=setenv bootargs console=${console},${baudrate} " \
/*......这里省略一些东西,其他参考改一下可以了*/
"bootcmd_mfg=run mfgtool_args;bootz ${loadaddr} ${initrd_addr}
${fdt_addr};\0" \
#if defined(CONFIG_SYS_BOOT_NAND)
#define CONFIG_EXTRA_ENV_SETTINGS \
CONFIG_MFG_ENV_SETTINGS \
"panel=TFT43AB\0" \
/*......这里省略一些东西,其他参考改一下可以了*/
"bootz ${loadaddr} - ${fdt_addr}\0
#else
#define CONFIG_EXTRA_ENV_SETTINGS \
CONFIG_MFG_ENV_SETTINGS \
"script=boot.scr\0" \
/*......这里省略一些东西,其他参考改一下可以了*/
"fi;\0" \
#define CONFIG_BOOTCOMMAND \
"run findfdt;" \
/*......这里省略一些东西,其他参考改一下可以了*/
"else run netboot; fi"
#endif
/* Miscellaneous configurable options */
#define CONFIG_CMD_MEMTEST
#define CONFIG_SYS_MEMTEST_START 0x80000000
#define CONFIG_SYS_MEMTEST_END (CONFIG_SYS_MEMTEST_START +
0x8000000)
#define CONFIG_SYS_LOAD_ADDR CONFIG_LOADADDR
#define CONFIG_SYS_HZ 1000
#define CONFIG_STACKSIZE SZ_128K
/* Physical Memory Map */
#define CONFIG_NR_DRAM_BANKS 1
#define PHYS_SDRAM MMDC0_ARB_BASE_ADDR
#define CONFIG_SYS_SDRAM_BASE PHYS_SDRAM
#define CONFIG_SYS_INIT_RAM_ADDR IRAM_BASE_ADDR
#define CONFIG_SYS_INIT_RAM_SIZE IRAM_SIZE
#define CONFIG_SYS_INIT_SP_OFFSET \
(CONFIG_SYS_INIT_RAM_SIZE - GENERATED_GBL_DATA_SIZE)
#define CONFIG_SYS_INIT_SP_ADDR \
(CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_INIT_SP_OFFSET)
/* FLASH and environment organization */
#define CONFIG_SYS_NO_FLASH
/*......这里省略一些东西,其他参考改一下可以了*/
#define CONFIG_SYS_MMC_ENV_DEV 1 /* USDHC2 */
#define CONFIG_SYS_MMC_ENV_PART 0 /* user area */
#define CONFIG_MMCROOT "/dev/mmcblk1p2" /* USDHC2 */
#define CONFIG_CMD_BMODE
/*......这里省略一些东西,其他参考改一下可以了*/
/* NAND stuff */
#ifdef CONFIG_SYS_USE_NAND
#define CONFIG_CMD_NAND
#define CONFIG_CMD_NAND_TRIMFFS
#define CONFIG_NAND_MXS
#define CONFIG_SYS_MAX_NAND_DEVICE 1
#define CONFIG_SYS_NAND_BASE 0x40000000
#define CONFIG_SYS_NAND_5_ADDR_CYCLE
#define CONFIG_SYS_NAND_ONFI_DETECTION
/* DMA stuff, needed for GPMI/MXS NAND support */
#define CONFIG_APBH_DMA
#define CONFIG_APBH_DMA_BURST
#define CONFIG_APBH_DMA_BURST8
#endif
#define CONFIG_ENV_SIZE SZ_8K
#if defined(CONFIG_ENV_IS_IN_MMC)
#define CONFIG_ENV_OFFSET (12 * SZ_64K)
#elif defined(CONFIG_ENV_IS_IN_SPI_FLASH)
#define CONFIG_ENV_OFFSET (768 * 1024)
#define CONFIG_ENV_SECT_SIZE (64 * 1024)
#define CONFIG_ENV_SPI_BUS CONFIG_SF_DEFAULT_BUS
#define CONFIG_ENV_SPI_CS CONFIG_SF_DEFAULT_CS
#define CONFIG_ENV_SPI_MODE CONFIG_SF_DEFAULT_MODE
#define CONFIG_ENV_SPI_MAX_HZ CONFIG_SF_DEFAULT_SPEED
#elif defined(CONFIG_ENV_IS_IN_NAND)
#undef CONFIG_ENV_SIZE
#define CONFIG_ENV_OFFSET (60 << 20)
#define CONFIG_ENV_SECT_SIZE (128 << 10)
#define CONFIG_ENV_SIZE CONFIG_ENV_SECT_SIZE
#endif
/* USB Configs */
#define CONFIG_CMD_USB
#ifdef CONFIG_CMD_USB
#define CONFIG_USB_EHCI
#define CONFIG_USB_EHCI_MX6
#define CONFIG_USB_STORAGE
#define CONFIG_EHCI_HCD_INIT_AFTER_RESET
#define CONFIG_USB_HOST_ETHER
#define CONFIG_USB_ETHER_ASIX
#define CONFIG_MXC_USB_PORTSC (PORT_PTS_UTMI | PORT_PTS_PTW)
#define CONFIG_MXC_USB_FLAGS 0
#define CONFIG_USB_MAX_CONTROLLER_COUNT 2
#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 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
#define CONFIG_FEC_XCV_TYPE RMII
#endif
#define CONFIG_ETHPRIME "FEC"
#define CONFIG_PHYLIB
#define CONFIG_PHY_MICREL
#endif
#define CONFIG_IMX_THERMAL
#ifndef CONFIG_SPL_BUILD
#define CONFIG_VIDEO
#ifdef CONFIG_VIDEO
#define CONFIG_CFB_CONSOLE
#define CONFIG_VIDEO_MXS
#define CONFIG_VIDEO_LOGO
#define CONFIG_VIDEO_SW_CURSOR
#define CONFIG_VGA_AS_SINGLE_DEVICE
#define CONFIG_SYS_CONSOLE_IS_IN_ENV
#define CONFIG_SPLASH_SCREEN
#define CONFIG_SPLASH_SCREEN_ALIGN
#define CONFIG_CMD_BMP
#define CONFIG_BMP_16BPP
#define CONFIG_VIDEO_BMP_RLE8
#define CONFIG_VIDEO_BMP_LOGO
#define CONFIG_IMX_VIDEO_SKIP
#endif
#endif
#define CONFIG_IOMUX_LPSR
/*......这里省略一些东西,其他参考改一下可以了*/
#endif
具体操作还需要根据你所用开发板的教程,这个并不特别针对某一块板子。
这里,还需要添加板级文件。在board/freescale里面,复制 mx6ullevk,将其重命名为你喜欢的名字: mx6ull_xxx_emmc。
cd board/freescale/
cp mx6ullevk/ -r mx6ull_xxx_emmc
然后,进去修改以下文件名:
cd mx6ull_xxx_emmc
mv mx6ullevk.c mx6ull_xxx_emmc.c
修改makefile
打开makefile文件,修改内容:
# (C) Copyright 2015 Freescale Semiconductor, Inc.
#
# SPDX-License-Identifier: GPL-2.0+
#
obj-y := mx6ull_xxx_emmc.o
extra-$(CONFIG_USE_PLUGIN) := plugin.bin
$(obj)/plugin.bin: $(obj)/plugin.o
$(OBJCOPY) -O binary --gap-fill 0xff $< $@
obj-y那一行要注意,如果不把他修改就没办法编译mx6ull_xxx_emmc.c这个文件。
修改imximage.cfg
修改mx6ull_xxx_emmc 目录下的 imximage.cfg 文件。将 imximage.cfg 中的下面一句:
PLUGIN board/freescale/mx6ullevk/plugin.bin 0x00907000
修改为:
PLUGIN board/freescale/mx6ull_xxx_emmc /plugin.bin 0x00907000
修改imximage.cfg
修改如下:
if TARGET_MX6ULL_xxx_EMMC
config SYS_BOARD
default "mx6ull_xxx_emmc"
config SYS_VENDOR
default "freescale"
config SYS_SOC
default "mx6"
config SYS_CONFIG_NAME
default "mx6ull_xxx_emmc"
endif
修改MAINTAINERS
修改为:
MX6ULL_XXX_EMMC BOARD
M: Peng Fan <peng.fan@nxp.com>
S: Maintained
F: board/freescale/mx6ull_xxx_emmc/
F: include/configs/mx6ull_xxx_emmc.h
添加图形界面
在arch/arm/cpu/armv7/mx6/Kconfig里面修改一下:
config TARGET_MX6ULL_XXX_EMMC
bool "Support mx6ull_xxx_emmc"
select MX6ULL
select DM
select DM_THERMAL
/*........这里省略一下*/
/*在endif前面面加上以下这一句*/
source "board/freescale/mx6ull_xxx_emmc/Kconfig"
需要添加的而文件已经添加了好,剩下的就是开始修改配置,但是修改前需要编译一下。
修改配置内容
这里需要参考你的开发板进行修改,因为每一块板子的硬件部分都是不一样的。如果你插上LCD屏幕不能正常显示,说明你的uboot配置屏幕的端口不对。如果不能ping上网络,则说明你的uboot里面的网口部分引脚,驱动配置需要修改。
uboot的启动
上一部分讲了uboot是怎样编译,怎样烧写到SD卡上面的。这里讲一下uboot启动之后是长啥样的。在接了串口打印之后,给板子上电可以看到这样的东西。
记得!看到黄色标出的哪一行,数到0之前按一下回车。理论上是会跳转到不知道什么地方去的,因为你的板子EMMC或者NADA里面没有东西(跟你玩过单片机的bootloader一样【最后执行一个jump跳转到指定地址执行),如果后面没有写入任何东西,就会不知道跳到那里去】,但实际上我也没试过,反正我都摁了进入uboot。
下面大概讲一下里面说的是什么东西。第一行是uboot的版本号和编译时间。第二行是你用的板子主频。第三行是工作环境温度。第四行的是你用的哪一家板子对应自己修改过的uboot。然后就是描述了一下当前板子一些硬件的情况......
记得板子上拨码开关弄到SD卡启动。
uboot命令
在uboot里面,也有一些shell命令。和Linux一样,输出一个“help”就可以把全部有的命令用法发出来。因为uboot的功能是可以配置的,所以这一个命令是可以配置的。一般地,使用的命令都是很常见的命令,都包括在soc厂商提供的uboot里面。其实这里,只要关注这一些命令是怎样使用的即可。(uboot也可以使用TAB补全的功能)
打个比如,我想知道“bootm”这一个命令是怎样使用的。那么,我们只需要在命令行里面输入:
?bootm
信息查询命令:bdinfo\printenv\version
这一些都是常见的信息查询命令。
先看关于:bdinfo命令。这个命令用于查询板子的信息,包括DRAM的起始地址和大小、启动参数保存地址、波特率、sp(栈堆指针)起始地址。
关于:printenv命令。这个命令用于输出环境变量信息(很多),只节选了一部分。
注意,这些变量都是一些字符串。
关于:version就不说了,打印版本号。
环境变量操作命令:setenv\saveenv
这两个命令用于修改环境变量参数和保存环境变量参数的。
那么,需要修改一个叫“bootdelay”的变量时,应该这么用:
setenv bootdelay 5
上述为,把bootdelay修改为4。如果还要修改其他参数,就继续使用这个命令格式即可。当需要修改的环境变量参数都修改完成之后,需要保存。如果需要修改的环境变量有好几个参数,那么需要这样操作:
setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw'
bootargs这个变量有四个参数。每个参数之间用空格隔开(“console=ttymxc0,115200”、“root=/dev/mmcblk1p2”、“rootwait”和“rw”),然后在外边用一个单引号包括在一起。最后进行参数保存,执行:
saveenv
因为修改后的参数保存在SD卡里面,会标出MMC(0),如果保存到EMMC里面,则标出MMC(1),如果是保存到NADA里面,则显示保存在NADA。保存之后如下图所示。
复位一下板子,就会又打印出新的uboot信息。
另外可以新建一些变量(就是在boot打印出来的),例如作者名字。
setenv author xxx
saveenv
然后使用printenv命令就可以看到。如果需要把这个变量删除,那么把参数替换成空格保存即可完成删除。
内存操作命令:md\nm\mm\mw\cp\cmp
这一些命令是对DRAM里面的数据进行操作的命令。可能到这里你不明白,uboot不是在SD卡里面吗?为什么操作的是DRAM(我当时也想了一下)。这里要说一下,就类似与计算机原理的知识。这里的SD相当于存放程序指令的地方,要运行的时候是要把数据挪到内存里面存放的,我们修改的是内存里面的数据,而不是把卡里面的指令给改了。所以是操作内存。
就好像这个哈佛结构的框架,修改的是数据顶多是数据存储器里面的东西。总不能把自己的执行指令给改了,就算改对了算你走运,改错了直接跑飞。
md:用于显示内存里面的值,格式如下:
md[.b, .w, .l] address [# of objects]
其中:.b .w .l三个东西对应的是byte、word、long,也就是分别以1字节、2字节、4字节来显示内存值,后面很多参数也会用到,注意一下。address就是要查看的内存起始地址。[# of objects]例如这么用:
md.b 80000000 14
表示想看0x80000000开始的地方后面连续20(十进制的20,在十六进制里面显示为14)个byte显示的内容。下面三种方式查询如下:
md.b 80000000 10
md.w 80000000 10
md.l 80000000 10
结果出来的是这个:
大概就这样吧,这个我也不太理解这个功能有什么用(知道可以这么用,但是暂时还不知道这些命令存在的价是什么,可能调试的时候用的)。
nm:用于修改指定地址内存的数值,格式如下:
nm [.b, .w, .l] address
和上面一样,三个‘·字母’表示三个长度。具体用法:
nm.l 80000000
然后,回复如下:
在这里,”?“后面输入你要修改的数值即可,然后按下回车。再需要在”?“后面输入一个‘q’即可退出并且保存。其中,0500e031是原来这个地址里面的数据。
md一下就可以看到数值被修改了。
mm:如果需要批量修改指定内存里面的数值,用这个命令。格式如下:
因为l是3个内存块的(12个字节),所以一下子就修改了三个,其他如此类推。
mw:用于填充一段内存,格式如下:
mw [.b, .w, .l] address value [count]
例子如下:
mw.l 80000000 0A0A0A0A 10
l类型的修改,在0x80000000开始,10个.l长度的内存写入0A0A0A0A。然后修改之后是这样的:
cp命令:用于把一段数据拷贝到另外一段数据的命令,格式如下:
cp [.b, .w, .l] source target count
例子如下:
cp.l 80000000 80000100 10
结果如下:
这个开始懒得写了,后面有时间补几张图。
cmp:用来对比两段数据是不是一样的,格式如下:
cmp [.b, .w, .l] addr1 addr2 count
打个比如,我要对比0x80000000 和 0X80000100这两段数据是不是相同的,那么执行以下命令,且结果如下:
cmp.l 80000000 80000100 10
如果不一样会告诉你不一样的。
网络操作命令:ping\dhcp\nfs\tftp
重点来了,这一部分十分重要。uboot是支持网络的,这一个功能十分重要!!!!!!因为后面移植Linux内核的时候需要反复用到uboot的网络功能进行调试。接下来将会讲一下这几个常用的网络命令是怎样使用的。
前情提要:
有几个知识需要提前知道的。首先,你的板子上有一个以太网的端口。它的样子大概长成下面的样子 。常见的是RJ-45接口,就是路由器上面用的网线,那一种接口。
然后,你需要知道一个叫“局域网”的东西。因为使用uboot的网络功能,需要你的板子和你用来开发的主机处于同一个局域网。可能到这里你还不理解的话,可以看下面示意图:
如上图所示,你可以这样看。在局域网里面有两台开发主机和路由器相连,其中开发主机1和路由器通过WiFi连接(现在普遍路由器都有WiFi功能的),开发主机2和路由器通过网线相连,开发板和路由器通过网线相连。那么,开发板、开发主机1、开发主机2、路由器就处于同一个局域网里面。在这里,开发板、开发主机1、开发主机2都有自己的ip地址(开始涉及到tcp/ip协议的知识了,不用深入,想了解一下即可)。
假设,在这个局域网中,几个设备的性能如下:
设备 | 开发板 | 开发主机1 | 开发主机2 |
性能 | 弱 | 中 | 强 |
此时,你的Linux内核是在开发主机1上的。现在,你的开发板需要把Linux内核拷贝过来开发板上面调试,就需要你的开发板和开发主机1的网络是通的。如果你用的开发主机1性能特别拉跨,编译代码很慢,刚好你有一台性能厉害的开发主机2。那么,你可以通过File Zilla把要编译的代码拉到开发主机2上编译完成之后再拷回来。现实中很多公司都是这么干,公司里面配备一台很好的电脑(当成服务器来用),就是用来干这个活的。由于一些不可控因数,公司里面的局域网有可能和外网是断开的,于是就变成了这一个样子。当然,公司里面不可能只有你自己一台电脑和一块开发板。所以,务必确保局域网里面的每个设备都是ip唯一的。
ping:一个用来检测当前设备和目标设备网络是否畅通的命令,也就是人们口中说的“ping一下”,就是说的这一个命令。假如开发板需要看自己和开发主机1(假设其ip为:192.168.1.253)网络是否畅通,在uboot里面执行以下命令:
ping 192.168.1.253
如果通了,会有以下信息打印出来:
意思是说,你所ping的ip地址对应的设备是存活的。那就可以通过uboot,把主机上的代码拷贝过来调试了。注意一下,别拿你的主机ping在uboot里面的开发板,ping不通的。
dhcp:开发板用来查询路由器给自己分配了个什么ip。例如,执行之后可以看到:
你当前板子的ip是:192.168.1.137
注意一下,别把板子和你的开发主机直接用网线连在一起然后dhcp。没用的,你的电脑不是路由器,不会给你的板子分配ip。
到了超级重点的地方!!!!!!
nfs:通过网络用来把开发主机上的代码拷贝过来的命令。执行格式例子如下:
nfs 80800000 192.168.1.253:/home/Chapman/linux/nfs/zImage
对这个命令有如下解释:
nfs | 80800000 | 192.168.1.253 | : | /home/.../nfs/zImage |
nfs命令 | 代码拷贝到内存的起始地址 | 开发主机的ip | : | 需要从主机拷贝过来的文件所在位置以及名字 |
来到这里应该也知道这个命令是怎么用的吧?就这样了。注意:存放nfs共享资源的文件夹名字为:nfs。
nfs(Network File System)网络文件系统,通过nfs,不同设备之间可以通过网络进行资源共享。因为嵌入式Linux和单片机不一样(没有像JLINK等的专用下载器,也没有个像样的IDE,也没有很好的烧写算法),复杂多了,所以就使用这一种方式进行开发。
这里解释一下,zImage是Linux内核的一个镜像文件。
命令执行完成之后,有如下内容打印:
到这里,文件拷贝完成了。
tftp:它的作用和nfs是一样的,但是用的是nfs比较多,因为tftp太麻烦。首先你的开发主机里面的Ubuntu需要安装好tftp服务器,并且搭建好。执行以下命令安装(在主机Ubuntu里面):
sudo apt-get install tftp-hpa tftpd-hpa
sudo apt-get install xinetd
和nfs一样,tftp也要给弄一个文件夹,然后用来存放共享的资源。但是,记得把你新建用来存放共享资源的文件夹tftpboot(文件夹起这一个名字)给绿了(就是给这一个文件夹权限)。
mkdir /home/Chapman/linux/tftpboot
chmod 777 /home/Chapman/linux/tftpboot
最后,还要配置tftp(真麻烦),创建这一个文件夹:/etc/xinetd.d用来存放配置文件的。然后配置文件名字为:tftp,内容如下(用VIM创建文件并且编写):
server tftp
{
socket_type = dgram
protocol = udp
wait = yes
user = root
server = /usr/sbin/in.tftpd
server_args = -s /home/Chapman/linux/tftpboot/
disable = no
per_source = 11
cps = 100 2
flags = IPv4
}
然后启动tftp服务,使用下面命令:
sudo service tftpd-hpa start
然后就会生成一个文件:tftpd-hpa,这个时候需要进入/etc/default里面把tftpd-hpa这一个文件给修改了,内容如下:
# /etc/default/tftpd-hpa
TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/home/Chapman/linux/tftpboot"
TFTP_ADDRESS=":69"
TFTP_OPTIONS="-l -c -s"
TFTP_DIRECTORY里面描述的就是前面建立用来共享资源的文件夹。最后重启tftp服务器:
sudo service tftpd-hpa restart
tftp搭建好之后,把需要共享的文件放到tftpboot这一个文件夹里面,并且把他们给绿了。
cp zImage /home/Chapman/linux/tftpboot/ //拷贝文件到目标文件夹
cd /home/Chapman/linux/tftpboot/ //进入目标文件夹
chmod 777 zImage //把文件给绿了
然后,在开发板子的uboot里面执行命令:
tftp 80800000 zImage
完成文件拷贝!
nfs和tftp问题汇总:
1)如果没有给最大权限,会报错。
2)如果网络不通,也会报错。网络不通一般有两个情况:一个是真的物理上网络不通(网线不行或者wifi没信号),一个是ip地址冲突(ip重复)或者错了。
BOOT操作命令:bootz\bootm\boot
上一部分讲到怎样把开发主机上的Linux镜像文件拉到开发板子上面。uboot的本质是引导Linux启动,所以肯定需要有命令来引导Linux启动。注意,这里会引入两个个新的概念——设备树和根文件,但是先了解知道有这两个东西就可以。设备树、根文件后面会再讲,总之你先知道zImage(内核镜像)、设备树文件(通过网络共享nfs或者tftp从开发主机传输到开发板子上面)两个东西放一块组成启动Linux所需要的、最基础的资料。
我们先从从开发主机拷贝过来的文件是Linux内核镜像和设备树文件。首先,需要选择正确的设备树文件,把文件拖到开发主机的Ubuntu上面的共享文件夹存放,然后发送到开发板子上。因为我用的是EMMC版本的开发板子,查过了资料对应的是一个文件:
imx6ull-14x14-emmc-7-1024x600-c.dtb
nfs 80800000 192.168.1.253:/home/Chapman/linux/nfs/zImage
nfs 83000000 192.168.1.253:/home/Chapman/linux/nfs/imx6ull-14x14-emmc-7-1024x600-c.dtb
这时候,开发板子上的DRAM已经有了镜像和设备树文件了。其中镜像在DRAM的起始地址为:0x80800000,而设备树文件在DRAM的起始地址为:0x83000000。
bootz:一个uboot用于启动镜像文件的命令,使用命令格式例子如下:
bootz 80800000 - 83000000
命令的意思是,我需要启动地址在0x80800000到0x83000000的关于Linux启动的镜像文件(刚好把两个东西都包含进去。)有下面的图:
然后就是往下刷启动信息。这个时候,等待Linux启动完成。
假如,不是从网络传输过来的镜像启动Linux,而是从EMMC或者NADA Flash里面启动的话,需要使用fatls命令查看EMMC分区1有没有镜像文件和设备树文件。则使用fatload命令把文件拷贝到DRAM里面再启动即可。但是,这里不打算讲对EMMC、NADA、FAT、EXT的命令,主要原因有以下:
1)这些命令都是对存储数据的操作,怕你们把数据弄没了。
2)如果纯粹靠网络启动系统,没必要,上面这一些够用了。
所以有时间就补上,没有就算了。
bootm:这个和bootz作用类似,只不过用来启动非镜像文件的。假如存放在DRAM指定地址的文件不是镜像,则使用这个命令启动Linux。过程和bootz一样,这里不再赘述。
boot:这一个东西也是用来启动Linux系统的,只不过,这一个命令会根据你在uboot里面设置的环境变量进行系统启动。再这个命令下,uboot会根据bootcmd里面设置的环境变量参数启动系统。所以执行这个命令启动系统的时候,需要设置好系统环境变量。假设,已经在开发主机的网络共享文件夹里面存放好Linux系统启动需要的文件(并且已经把他们绿了)。执行bootcmd命令设置好变量,然后用boot启动系统。
setenv bootcmd 'tftp 80800000 zImage; tftp 83000000 imx6ull-14x14-emmc-7-1024x600-c.dtb;
bootz 80800000 - 83000000'
saveenv
boot
这个bootcmd是一次性把下载和启功一块执行的,所以比较省事。
uboot阶段性总结
恭喜你看到了这里,到了这里,你已经知道了至少一种如何在开发板上面启动Linux系统的方式(最适合在开发调试阶段的启动方式)。然后,我再重申一下这一个过程,让你有更加深入的理解。
在Ubuntu里面准备好全部所需要的文件,然后板子的uboot通过网络把文件拷贝到DRAM里面,再执行系统启动,就是这一个完整的启动过程。
注意一点,麻烦自己跑一遍,并且用官方提供的uboot和Linux镜像系统跑一下这个流程。下面是我自己跑的:
因为家里网线被扔了,我这个博客暂时写不下去了!真是要命!买了网线再更新。
系统不能完全启动
来到了这里,你会发现系统在boot命令之后启动运行几秒就卡死。是因为前面只是实现如何从uboot里面启动系统,在复制挂在镜像和设备树的时候,没有把根文件带上。而根文件是系统启动的基础之一,所以系统启动到一半就卡住了。后面会继续讲根文件和内核的知识。
Linux内核移植
前面讲了uboot是什么东西,而且怎么用。这一部分继续讲官方系统内核如何移植到你的开发板子上。如果你是一个有经验的工程师,你要设计一块电路板用到你从来没有接触过的芯片。那么关于这个芯片的外围电路设计,你最好的办法是参考(照搬,然后小改)官方方案。因为没人比官方更懂这个芯片。
同样,厂商官方每推出一款芯片,就肯定有这一块板的demo板。而几乎所有用户都是根据这一块demo板子进行小改,去开发自己的产品。在这里将会参照官方给的内核,对里面内容进行小改,以适应手上的板子,有时候可能甚至不用修改直接能用。
系统内核获得&编译
关于系统内核的获得方式,可以从你买的板子的找资料里面找,或者在芯片厂家官网里面找产出来。一般地,下载回来的系统内核是一个压缩包,这时候需要你把压缩包发送到Ubuntu里面并且解压,解压出来的是一整个文件夹。然后在vscode里面新建一个工程,并且把整个文件夹拉到里面。(VScode请自行安装,Linux版本的,不要钱的
在VScode里面打开整个文件夹,这样就可以了。
接下来需要对文件进行编译。不过需要有以下步骤:
1)在编译之前,还有一个事情要注意。就是建立一个件.vscode的文件夹,然后在文件夹里面创建一个settings.json文件。这一个文件适用于屏蔽你所用的电路板上用不到的框架相关文件、配置、设备树文件。具体操作,参照你所用的教程。
2)这一步看习惯,可以创建一个sh脚本,用于编译。
#!/bin/sh
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx_v7_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- all -j16
然后执行这一个脚本即可。
移植测试
编译完成以后,会生成一个zImage和设备树。然后把镜像和设备树挂在到板子上,用前面讲的uboot启动。因为我用的是emmc的6ull的板子,所以设备树选imx6ull-14x14-evk-emmc.dtb,否则选imx6ull-14x14-evk.dtb。
因为还没挂载根文件目录,所以还是启动到一半卡住。 能启动,说明系统和设备树设置匹配我现在用的EMMC&512MBDDR版本的板子。
系统配置文件准备
如果很不幸,你需要配置一下移植大的系统。那么,你需要准备几个配置文件。在工程文件夹里面的arch/arm/configs下,把imx_v7_mfg_defconfig复制一份,并且重新命名。假设命名为:
imx_v7_xxx_defconfig
以后你就在这一个文件里面配置你的开发板,并且使用以下命令来配置你的内核文件。
make imx_v7_xxx_defconfig
设备树文件准备
在arch/arm/boot/dts里面,复制一份imx6ull-14x14-evk.dts,然后命名为:
imx6ull-xxx.dts
然后打开这个为文件:arch/arm/boot/dts/Makefile,在:
dtb- $(CONFIG_SOC_IMX6ULL)
下面配置,在里面加入你前面弄的imx6ull-xxx.dts,内容如下:
dtb-$(CONFIG_SOC_IMX6ULL) += \
imx6ull-14x14-ddr3-arm2.dtb \
imx6ull-14x14-ddr3-arm2-adc.dtb \
imx6ull-14x14-ddr3-arm2-cs42888.dtb \
imx6ull-14x14-ddr3-arm2-ecspi.dtb \
imx6ull-14x14-ddr3-arm2-emmc.dtb \
imx6ull-14x14-ddr3-arm2-epdc.dtb \
imx6ull-14x14-ddr3-arm2-flexcan2.dtb \
imx6ull-14x14-ddr3-arm2-gpmi-weim.dtb \
imx6ull-14x14-ddr3-arm2-lcdif.dtb \
imx6ull-14x14-ddr3-arm2-ldo.dtb \
imx6ull-14x14-ddr3-arm2-qspi.dtb \
imx6ull-14x14-ddr3-arm2-qspi-all.dtb \
imx6ull-14x14-ddr3-arm2-tsc.dtb \
imx6ull-14x14-ddr3-arm2-uart2.dtb \
imx6ull-14x14-ddr3-arm2-usb.dtb \
imx6ull-14x14-ddr3-arm2-wm8958.dtb \
imx6ull-14x14-evk.dtb \
imx6ull-14x14-evk-btwifi.dtb \
imx6ull-14x14-evk-emmc.dtb \
imx6ull-14x14-evk-gpmi-weim.dtb \
imx6ull-14x14-evk-usb-certi.dtb \
imx6ull-xxx.dtb \
imx6ull-9x9-evk.dtb \
imx6ull-9x9-evk-btwifi.dtb \
imx6ull-9x9-evk-ldo.dtb
这样,编译的时候就可以编译出imx6ull-xxx.dtb了。
然后进行编译测试。
CPU主频、从存储器、网络修改
在这一部分主要间接主频、存储器、网络修改。修改主频,可以提高或者降低一下当前板子的工作频率。修改存储器,这里主要是EMMC,为了提高存储器读写速度。网络修改和之前的uboot修改网络接口差不多,因为使用的网口和官方demo板的网口不一样,因此需要修改网络驱动。最后重新编译即可,这里需要参照你所用的板子具体情况修改,这里不提供参考。
根文件构建
前面已经把uboot、Linux内核、设备树挂载到开发板子上了。这一部分主要讲一下之前一直没有讲的根文件系统。
在Linux启动的时候,系统们没有读取到根文件的时候,系统会直接崩溃,这就是之前已经见过的情况:系统启动到一半卡死了。根文件对于系统来说很重要(没有根文件,Linux系统就无法正常启动),例如在Linux终端里面,使用的一些shell命令,就是存放在根文件里面。如果没有根文件,这一些命令就没办法使用。
根文件结构
根文件,准确来说,是一个文件夹,里面放了很多很重要的文件。下图是一个根文件夹里面放的东西。
/bin
bin文件夹,主要放的是可执行文件,例如前面说的shell命令文件,所有用户都可以使用以下文件。
/dev
这一个文件夹里面,放的都是驱动类别(硬件驱动)文件。
/etc
这一个文件夹里面,放的都是配置文件。
/lib
这一个文件夹里面,放的都是库文件。
/mnt
这一个文件夹里面,放的的都是挂在文件(后面的nfs、tftp会用到)。
/proc
这一个文件夹是虚拟的,用来挂在临时运行的文件新信息。
/usr
这一个文件夹里面,放的是在Linux下安装的软件。
/var
这一个文件夹里面,放的是可改变的数据。
/sbin
这里也是存放一些可执行文件,只不过只能管理员使用。
/sys
这个文件夹和/proc类似,也是用于系统文件挂载。
/opt
一个可供用户自己选择的文件存放区。
一般地,根文件系统也叫rootfs。在后面的测试中通过网络(nfs、tftp),将主机上创建好的根文件系统挂载到开发板的系统上。
Busy Box
这一个工具用于生成Linux根文件系统的工具。一般你所用的开发板官方资料里面会提供,因为这个工具没有暂时国内官网,你可能下载不了(很慢)。下面讲一下这一个工具是怎样使用的。
编译Busy Box
当你拿到这一个工具的压缩包时候,使用File Zilla将压缩包发送到你的Ubuntu主机里面。然后,在先前搭建好的nfs文件夹里面新建一个叫“rootfs”的文件夹:
mkdir rootfs
假设我下载的是busybox-1.29.0.tar.bz2 ,那么对这个压缩包进行解压。
tar -vxjf busybox-1.29.0.tar.bz2
解压之后,会有这么多的文件:
和uboot和Linux内核一样,编译一个文件,需要修改其Makeflie。打开Makeflie文件,做以下修改(在164和190行修改)。
164 CROSS_COMPILE ?= /usr/local/arm/gcc-linaro-4.9.4-2017.01-
x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-
......
190 ARCH ?= arm
让Busy Box支持中文
打开/libbb/printable_string.c,找到一个叫printable_string的函数,在对应行对代码进行以下修改:
12 const char* FAST_FUNC printable_string(uni_stat_t *stats, const char
*str)
13 {
14 char *dst;
15 const char *s;
16
17 s = str;
18 while (1) {
......
30 if (c < ' ')
31 break;
32 /* 注释掉下面这个两行代码 */
33 /* if (c >= 0x7f)
34 break; */
35 s++;
36 }
37
38 #if ENABLE_UNICODE_SUPPORT
39 dst = unicode_conv_to_printable(stats, str);
40 #else
41 {
42 char *d = dst = xstrdup(str);
43 while (1) {
44 unsigned char c = *d;
45 if (c == '\0')
46 break;
47 /* 修改下面代码 */
48 /* if (c < ' ' || c >= 0x7f) */
49 if( c < ' ')
50 *d = '?';
51 d++;
52 }
......
59 #endif
60 return auto_string(dst);
61 }
接着,找到以下函数对其修改:
1003 static char* FAST_FUNC unicode_conv_to_printable2(uni_stat_t *stats, const char *src, unsigned width, int flags)
1004 {
1005 char *dst;
1006 unsigned dst_len;
1007 unsigned uni_count;
1008 unsigned uni_width;
1009
1010 if (unicode_status != UNICODE_ON) {
1011 char *d;
1012 if (flags & UNI_FLAG_PAD) {
1013 d = dst = xmalloc(width + 1);
......
1022 /* 修改下面一行代码 */
1023 /* *d++ = (c >= ' ' && c < 0x7f) ? c : '?'; */
1024 *d++ = (c >= ' ') ? c : '?';
1025 src++;
1026 }
1027 *d = '\0';
1028 } else {
1029 d = dst = xstrndup(src, width);
1030 while (*d) {
1031 unsigned char c = *d;
1032 /* 修改下面一行代码 */
1033 /* if (c < ' ' || c >= 0x7f) */
1034 if(c < ' ')
1035 *d = '?';
1036 d++;
1037 }
1038 }
......
1044 return dst;
1045 }
......
1047
1048 return dst;
1049 }
配置Busy Box
和前面的uboot、Linux内核一样,有一些选项需要另外配置修改。返回出去Busy Box的文件夹,使用以下命令对其进行配置:
make menuconfig
选择以下的路径:Location-> Settings -> Build static binary (no shared libs)。这一个选项是决定Busy Box是动态还是静态编译的。这里选择动态编译。
继续配置以下:Location-> Settings -> vi-style line editing commands。选择vi-style line editing commands 。
继续下面配置:Location-> Linux Module Utilities -> Simplified modutils。取消选中:Simplified modutils。
继续配置:Location-> Linux System Utilities -> mdev (16 kb)。需要全部选中:
最后使能unicode编码,找到以下选项:Location: -> Settings -> Support Unicode //选中 -> Check $LC_ALL, $LC_CTYPE and $LANG environment variables //选中。
如果你需要其他的功能的话,可以另作修改,建议跟住你所用开发板官方教程进行设置,以防出现一些bug。
编译Busy Box
编译之后,需要把文件放到之前建立的rootfs里面:
make
make install CONFIG_PREFIX=/home/Chapman/linux/nfs/rootf
编译完成后:
随后在nfs里面的rootfs里面找到编译好的文件了。
在这一个文件夹里面,有bin、sbin、usr三个目录和linuxrc这一个文件。当Linux内核启动完成之后,就会移动到用户态。如果前面uboot设置时,设置bootargs为init=/linuxrc,那么linuxrc将会作为用户的init程序。
添加lib库
Linux中的应用程序一般都需要动态库(如果你没编译成静态库的话),因为静态库编译出来太大了,时不建议使用的。在rootfs里面建立一个lib文件夹:
mkdir lib
文件夹建立好了,需要往里面添加一些库文件。lib的库文件是来自前面搭建好的交叉编译器环境下编译出来的文件。返回到rootfs目录下,在/usr/local/arm/这一个目录里面,全部文件拷贝到lib里面(因为里面东西太多了,而且还不知道哪个可以用,那个不需要)。进入这个目录:
/usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/libc/lib
在这个目录下面,有很多*so*、.a文件都是库文件,将它们全部拷贝到lib里面:
cp *so* *.a /home/zuozhongkai/linux/nfs/rootfs/lib/ -d
在lib里面有这么多文件:
向“usr/lkib”添加文件
在usr这个目录里面,添加一个lib目录。将下面目录的文件:
/usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/arm-linuxgnueabihf/libc/usr/lib
然后把所有库文件拷贝到usr/lib里面:
cp *so* *.a /home/zuozhongkai/linux/nfs/rootfs/usr/lib/ -d
这里默认你是使用EMMC的板子,因为可能不用EMMC你的板子容量不够大了。
挂载测试
到了这里,可以用nfs测试一下前面制作的根文件系统能不能使用。给板子插了网线,上电之后进入uboot。重新设置bootargs环境变量,执行下面的命令:
setenv bootargs 'console=ttymxc0,115200 root=/dev/nfs nfsroot=[<server-ip>:]<root-dir>[,<nfs-options>] ip=<client-ip>:<server-ip>:<gwip>:<netmask>:<hostname>:<device>:<autoconf>:<dns0-ip>:<dns1-ip>
其中<server-ip>是你的Ubuntu主机ip,<root-dir>是你存放根文件的路径,<nfs-options>设置为:proto=tcp rw,<client-ip>是你的板子ip,<server-ip>服务器ip,<gw-ip>网关IP,<netmask>子网掩码:255.255.255.0,<hostname>这个可以不设置,可以空着,<device>开发板网卡名,<autoconf>设置off,<dns0-ip>这个可以不设置,可以空着,<dns1-ip>这个可以不设置,可以空着。
然后保存:
saveenv
然后启动系统:
到了这里,问题应该是没什么问题了,但是这一个根文件还是不够完善,需要你按照你的教程里面修改添加不同的文件,这里不再赘述。下面是本部分的流程总结:
系统移植阶段性总结
来到这里,整个Linux系统的移植基本完成,下面就是要开始Linux驱动的学习。前面这么大篇幅的工作都是为了接下来学习Linux驱动学习而准备的。