Linux系统移植主要是bootloader(U-Boot)、Linux kernel和rootfs移植,这三者一起构成了一个完整的Linux系统。
(1)bootloader代码用于启动Linux内核;
(2)移植Linux kernel;
(3)移植一个根文件系统(rootfs),这个里面包含了一些常用命令和文件。
芯片上电后,先运行一段bootloader程序,这段程序先初始化DDR等外设,然后将Linux内核从flash(NAND、SD、MMC)拷贝到DDR中,最后启动Linux内核。
uboot是一个裸机代码,相当于STM32F103开发板最后的一个大的裸机综合例程,能够支持各种外设,像液晶屏、网络等高级功能。
比如我们自己的板子参照的是NXP的evk板,此时,我们就选用NXP厂商提供的uboot,来适配我们自己的板子。
新建一个shell脚本,用于编译移植好的uboot。
//文件名为:mx6ull_alientek_emmc.sh
#!/bin/bash
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_14x14_ddr512_emmc_defconfig //在编译之前要使用defconfig来配置uboot
make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j12
sudo apt-get install libncurses5-dev //安装 ncurses 库
tar -vxjf uboot-imx-2016.03-2.1.0-g8b546e4.tar.bz2 //解压
./mx6ull_alientek_emmc.sh //编译uboot
chomd 777 imxdownload
./imxdownload u-boot.bin /dev/sdd //烧写到 SD 卡
编译完成后,u-boot.bin是编译出来的二进制文件,由于uboot是个裸机程序,因此需要在其前面加上头部(IVT、DCD)才能在I.MX6U上执行。
u-boot.imx文件就是添加头部信息以后的u-boot.bin,也是最终要烧写到开发板的文件。
打开SecureCRT,找到板子所对应的串口。
print
uboot 中的环境变量都是字符串。
setenv bootdelay 5
saveenv
修改完的环境变量一定要保存。因为:环境变量存放在外部flash中,uboot启动的时候会将环境变量从flash读取到DRAM中。而setenv修改的是DRAM中的环境变量值,修改后需要使用saveenv将修改后的环境变量保存到flash中。
setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw'
saveenv
修改多个值,需要用空格隔开,需要用单引号括起来。
要确保Ubuntu主机与开发板的IP地址在同一个网段,有2种方法:
方法一:用网线将开发板网口与电脑主机网口连一起;
方法二:将开发板、电脑连接在同一个路由器上;
在Ubuntu终端使用ifconfig命令查看Ubuntu的IP地址,对其进行设置
在Ubuntu终端Ping一个同网段的IP,例如:192.168.217.120
在SecureCRT中
setenv ipaddr 192.168.217.120
setenv ethaddr b8:ae:1d:01:00:00
setenv gatewayip 192.168.217.1
setenv netmask 255.255.255.0
setenv serverip 192.168.217.129
saveenv
pingUbuntu的主机IP的时候,显示is alive的时候,说明网络通了,在这个过程中,发现一直ping不同,然后修改了虚拟机的设置
此时,Ubuntu不能联网,需要将Ubuntu中的System Setting–>Network–>options–>IPv4Setting设置为自动模式,然后就可以联网了。
dhcp 用于从路由器获取 IP 地址,前提是得把开发板连接到路由器上的。
网络文件系统,通过这个可以在计算机之间通过网络来分享资源。主要方便调试。
把Linux镜像、设备树文件放到Ubuntu中,然后在uboot中使用nfs命令将Ubuntu 中的 linux 镜像和设备树下载到开发板的 DRAM 中。
(1)安装NFS服务
sudo apt-get install nfs-kernel-server rpcbind
在用户根目录下创建linux/nfs文件,供nfs服务器使用。
(2)配置nfs
sudo vi /etc/exports //打开 nfs 配置文件/etc/exports
在最后一行添加
/home/yang/linux/nfs *(rw,sync,no_root_squash)
(3)重启NFS服务
sudo /etc/init.d/nfs-kernel-server restart
(4)使用NFS服务
nfs 80800000 192.168.217.129:/home/yang/linux/nfs/zImage
与nfs一样,都是用于通过网络下载东西到 DRAM 中。
tftp明明使用的是TFTP协议,Ubuntu主机作为TFTP服务器,需要在Ubuntu搭建TFTP服务器。
(1)安装tftp-hpa和 tftpd-hpa
sudo apt-get install tftp-hpa tftpd-hpa
sudo apt-get install xinetd
在用户根目录下创建linux/tftpboot文件,供nfs服务器使用。
注意!要给 tftpboot 文件夹权限,否则的话 uboot 不能从tftpboot 文件夹里面下载文件。
(2)配置tftp
新建文件/etc/xinetd.d/tftp;
/etc/xinetd.d/tftp 文件内容
server tftp
{
socket_type = dgram
protocol = udp
wait = yes
user = root
server = /usr/sbin/in.tftpd
server_args = -s /home/zuozhongkai/linux/tftpboot/
disable = no
per_source = 11
cps = 100 2
flags = IPv4
}
(3)启动tftp服务
sudo service tftpd-hpa start
(4)修改/etc/default/tftpd-hpa文件
1 # /etc/default/tftpd-hpa
2
3 TFTP_USERNAME="tftp"
4 TFTP_DIRECTORY="/home/yang/linux/tftpboot"
5 TFTP_ADDRESS=":69"
6 TFTP_OPTIONS="-l -c -s"
(5)重启tftp服务
sudo service tftpd-hpa restart
(6)将zImage 镜像文件拷贝到 tftpboot 文件夹
cp zImage /home/yang/linux/tftpboot/
cd /home/yang/linux/tftpboot/
chmod 777 zImage
chmod 777 tftboot //给予 tftboot 相应的权限
(7)使用tftp服务
tftp 80800000 zImage
tftp 80800000 zImage
tftp 83000000 imx6ull-14x14-emmc-7-1024x600-c.dtb
bootz 80800000 - 83000000
uboot重启
(1)arch文件:
与架构有关的文件。
我们只需要关注arm这个文件即可。因为用的是I.MX6ULL,所以关注:arm中的imx-common、cpu/armv7、cpu/u-boot.lds连接脚本。
(2)board文件
board/freescale/mx6ull
(3)config文件
这些半导体厂商或者开发板厂商制作好的配置文件统一命名为“xxx_defconfig”,xxx 表示开发板名字,这些 defconfig 文件都存放在configs文件夹。
(4).u-boot.xxx_cmd文件
都是编译生成的文件
(5)Makefile文件
Makefile 支持嵌套的。顶层 Makefile 可以调用子目录中的 Makefile 文件。
(6)u-boot.xxx文件
(7).config文件
uboot 配置文件,使用命令“make xxx_defconfig”配置 uboot 以后就会自动生成。
(8)README
README 文件描述了 uboot 的详细信息,包括 uboot 该如何编译、uboot 中各文件夹的含义、相应的命令等等。建议大家详细的阅读此文件,可以进一步增加对 uboot 的认识。
用于配置 uboot,这个命令最主要的目的就是生成.config 文件。
用于编译 uboot,这个命令的主要工作就是生成二进制的 u-boot.bin 文件和其他的一些与 uboot 有关的文件,比如 u-boot.imx 等等
编译 u-boot 以后才会在根目录下出现 u-boot.lds 文件。在这个文件中,可以找到uboot的启动流程的入口,
//u-boot.lds 文件代码
3 ENTRY(_start)
第 3 行为代码当前入口点:_start,_start 在文件 arch/arm/lib/vectors.S 中有定义。
//vectors.S 代码段
48 _start:
49
50 #ifdef CONFIG_SYS_DV_NOR_BOOT_CFG
51 .word CONFIG_SYS_DV_NOR_BOOT_CFG
52 #endif
53
54 b reset
55 ldr pc, _undefined_instruction
56 ldr pc, _software_interrupt
57 ldr pc, _prefetch_abort
58 ldr pc, _data_abort
59 ldr pc, _not_used
60 ldr pc, _irq
61 ldr pc, _fiq
54 行跳转到 reset 函数里面,reset 函数在 arch/arm/cpu/armv7/start.S 里面…
详解后面有空了再总结!!!
一般流程:
(1)在uboot中找到参考的开发平台,一般是:原厂的开发板;
(2)参考原厂开发板移植 uboot 到我们所使用的开发板上。
configs 目录下有很多跟 I.MX6UL/6ULL 有关的配置。找到与板子相关的。正点原子的 I.MX6ULL 有 EMMC 和NAND 两个版本的,因此我们最终只需要关注 mx6ull_14x14_evk_emmc_defconfig 和mx6ull_14x14_evk_nand_defconfig 这两个配置文件就行了。
方法一:开启Ubuntu终端,进入对应的目录,使用如下命令编译 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
方法二:在顶层Makefile中直接给ARCH和CROSS_COMPILE赋值
ifeq ($(HOSTARCH),$(ARCH))
CROSS_COMPILE ?=
endif
ARCH = arm
CROSS_COMPILE = arm-linux-gnueabihf-
然后在Ubuntu终端中输入如下命令:
make mx6ull_14x14_evk_emmc_defconfig
make V=1 -j16
方法三:直接创建一个shell脚本,shell 脚本名为 mx6ull_14x14_emmc.sh,然后在 shell 脚本里面输入如下内容:
//mx6ull_14x14_emmc.sh 文件
1 #!/bin/bash
2 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
3 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihfmx6ull_14x14_evk_emmc_defconfig
4 make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j16
在Ubuntu中给与shell文件777权限。使用脚本编译 uboot 的时候每次都会清理一下工程,然后全部重新编译,编译的时候直接执行这个脚本就行了,命令如下:
./mx6ull_14x14_evk_emmc.sh
将 imxdownload 软件拷贝到 uboot 源码根目录下,然后使用imxdownload 软件将 u-boot.bin烧写到 SD 卡中,烧写命令如下:
chmod 777 imxdownload //给予 imxdownload 可执行权限
./imxdownload u-boot.bin /dev/sdd //烧写到 SD 卡中,
(1)SD 卡和 EMMC 驱动检查:
=> mmc list //列出当前MMC设备
=> mmc dev 0 //先检查 MMC 设备 0
=> mmc info
=> mmc dev 1 //先检查 MMC 设备 1
=> mmc info
(2)LCD 驱动检查、网络驱动检查:
复制 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 文件
1CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=board/freescale/mx6ull_alientek_emmc/imximage.cfg,MX6ULL_EVK_EMMC_REWORK"
2 CONFIG_ARM=y
3 CONFIG_ARCH_MX6=y
4 CONFIG_TARGET_MX6ULL_ALIENTEK_EMMC=y
5 CONFIG_CMD_GPIO=y
将里面的文件名字改成修改后的即可。
在 目 录 include/configs 下 添 加 I.MX6ULL-ALPHA 开 发 板 对 应 的 头 文 件 , 复 制include/configs/mx6ullevk.h,并重命名为mx6ull_alientek_emmc.h,命令如下:
cp include/configs/mx6ullevk.h mx6ull_alientek_emmc.h
拷贝完成以后修改头文件名:
//修改前的头文件
#ifndef __MX6ULLEVK_CONFIG_H
#define __MX6ULLEVK_CONFIG_H
//修改后的头文件
#ifndef __MX6ULL_ALIENTEK_EMMC_CONFIG_H
#define __MX6ULL_ALIENTEK_EMMC_CONFIG_H
uboot 中每个板子都有一个对应的文件夹来存放板级文件,比如开发板上外设驱动文件等等。
NXP 的 I.MX 系列芯片的所有板级文件夹都存放在 board/freescale 目录下,在这个目录下有个名为 mx6ullevk 的文件夹,这个文件夹就是 NXP 官方 I.MX6ULL EVK 开发板的板级文件夹。复制 mx6ullevk,将其重命名为 mx6ull_alientek_emmc,
cd board/freescale/
cp mx6ullevk/ -r mx6ull_alientek_emmc
进 入 mx6ull_alientek_emmc 目 录 中 , 将 其 中 的 mx6ullevk.c 文 件 重 命 名 为mx6ull_alientek_emmc.c,
cd mx6ull_alientek_emmc
mv mx6ullevk.c mx6ull_alientek_emmc.c
(1)修改 mx6ull_alientek_emmc 目录下的 Makefile 文件:
1 # (C) Copyright 2015 Freescale Semiconductor, Inc.
2 #
3 # SPDX-License-Identifier: GPL-2.0+
4 #
5
6 obj-y := mx6ull_alientek_emmc.o
7
8 extra-$(CONFIG_USE_PLUGIN) := plugin.bin
9 $(obj)/plugin.bin: $(obj)/plugin.o
10 $(OBJCOPY) -O binary --gap-fill 0xff $< $@
主要修改文件名字。
(2)修改 mx6ull_alientek_emmc 目录下的 imximage.cfg 文件
//修改前
PLUGIN board/freescale/mx6ullevk/plugin.bin 0x00907000
//修改后
PLUGIN board/freescale/mx6ull_alientek_emmc /plugin.bin 0x00907000
主要修改文件名字。
(3)修改 mx6ull_alientek_emmc 目录下的 Kconfig 文件
1 if TARGET_MX6ULL_ALIENTEK_EMMC
2
3 config SYS_BOARD
4 default "mx6ull_alientek_emmc"
5
6 config SYS_VENDOR
7 default "freescale"
8
9 config SYS_SOC
10 default "mx6"
11
12 config SYS_CONFIG_NAME
13 default "mx6ull_alientek_emmc"
14
15 endi
(4)修改 mx6ull_alientek_emmc 目录下的 MAINTAINERS 文件
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
修改文件arch/arm/cpu/armv7/mx6/Kconfig(如果用的 I.MX6UL 的话,应该修改 arch/arm/Kconfig 这个文件),在 207 行加入如下内容:
1 config TARGET_MX6ULL_ALIENTEK_EMMC
2 bool "Support mx6ull_alientek_emmc"
3 select MX6ULL
4 select DM
5 select DM_THERMAL
//在最后一行的 endif 的前一行添加如下内容:
1 source "board/freescale/mx6ull_alientek_emmc/Kconfig"
在 uboot 根目录下新建一个名为 mx6ull_alientek_emmc.sh 的 shell 脚本,与上面所述脚本类似,只不过文件名字不同。
等待编译完成,编译完成以后输入如下命令,查看一下 33.2.2 小节中 添加 的mx6ull_alientek_emmc.h 这个头文件有没有被引用。
grep -nR "mx6ull_alientek_emmc.h"
编译完成以后就使用 imxdownload 将新编译出来的 u-boot.bin 烧写到 SD 卡中测试。
一般 uboot 中修改驱动基本都是在 xxx.h 和 xxx.c 这两个文件中进行的,xxx 为板子名称,比如 mx6ull_alientek_emmc.h 和 mx6ull_alientek_emmc.c 这两个文件。
一般修改 LCD 驱动重点注意以下几点:
①、LCD 所使用的 GPIO,查看 uboot 中 LCD 的 IO 配置是否正确。
②、LCD 背光引脚 GPIO 的配置。
③、LCD 配置参数是否正确。
LCD配置参数在:struct display_info_t const displays[] = { … }
重启以后 LCD 驱动一般就会工作正常了,LCD 上回显示 NXP 的 logo。但是有可能会遇到LCD 并没有工作,还是黑屏,在uboot命令模式下输入print来查看panel的值。修改为 TFT7016
setenv panel TFT7016
saveenv
(1)查看原理图,确定修改的部分:
查看原理图,NXP官方的EVK与正点原子的开发板有什么不同,发现只是复位引脚不同。
查看LAN8720A这个PHY网络芯片的使用方法。
ENET1主要修改:
①、ENET1 复位引脚初始化。
②、LAN8720A 的器件 ID。
③、LAN8720 驱动
ENET2主要修改:
①、ENET2 的复位引脚,ENET2 的复位引脚 ENET2_RST 接到了I.MX6ULL 的 SNVS_TAMPER8 上。
②、ENET2 所使用的 PHY 芯片器件地址,PHY 器件地址为 0X1。
③、LAN8720 驱动,ENET1 和 ENET2 都使用的 LAN8720,所以驱动肯定是一样的。
(2)网络PHY的修改
打开 mx6ull_alientek_emmc.h,找到325行左右关于网络默认ID配置参数
主要修改:
①、修改 ENET1 网络 PHY 的地址。
②、修改 ENET2 网络 PHY 的地址。
③、使能 SMSC 公司的 PHY 驱动。
(3)删除uboot 中 74LV595 的驱动代码:
打开mx6ull_alientek_emmc.c。
①、修改网络复位引脚,将 74LV595相关的引脚删除,改成我们现在使用的。
#define ENET1_RESET IMX_GPIO_NR(5, 7)
#define ENET2_RESET IMX_GPIO_NR(5, 8)
②、删除74LV595 引脚配置、函数 iox74lv_init()、iox74lv_set(),删除board_init 函数中调用的iox74lv_init();
(4)添加 I.MX6U-ALPHA 开发板网络复位引脚驱动
打开mx6ull_alientek_emmc.c,找到
static iomux_v3_cfg_t const fec1_pads[] = { ...... }
......
static iomux_v3_cfg_t const fec2_pads[] = { ...... }
结构体数组 fec1_pads 和 fec2_pads 是 ENET1 和 ENET2 这两个网口的 IO 配置参数,在这两个数组中添加两个网口的复位 IO 配置参数。
找到函数 setup_iomux_fec( )。函数 setup_iomux_fec 就是根据 fec1_pads 和 fec2_pads 这两个网络 IO 配置数组来初始化I.MX6ULL 的网络 IO。我们需要在其中添加网络复位 IO 的初始化代码,并且复位一下 PHY 芯片,修改后的 setup_iomux_fec 函数如下:
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,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,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 }
(5)修改 drivers/net/phy/phy.c 文件中的函数 genphy_update_link
这是个通用 PHY 驱动函数,此函数用于更新 PHY 的连接状态和速度。
(1)修改板子名字:
打开mx6ull_alientek_emmc.c,找到函数checkboard( ),将其改为我们自己的板子的名字。
在启动之前先了解一下uboot中的两个非常重要的环境变量,在后续的内核移植中会用到。
文 件 mx6ull_alientek_emmc.h 中的CONFIG_EXTRA_ENV_SETTINGS 保存着这些环境变量的默认值。
(1)环境变量 bootcmd
bootcmd 保存着 uboot 默认命令。
uboot 倒计时结束以后就会执行 bootcmd 中的命令。这些命令一般都是用来启动 Linux 内核的,比如:读取 EMMC 或者 NAND Flash 中的 Linux 内核镜像文件和设备树文件到 DRAM 中,然后启动 Linux 内核。
一般在uboot启动后进入命令行来设置bootcmd环境变量的值。
(2)环境变量 bootargs
bootargs 保存着 uboot 传递给 Linux 内核的参数。
传递的参数有:
①、console:用来设置 linux 终端(或者叫控制台),
例如:console=ttymxc0,115200 综合起来就是设置 ttymxc0(也就是串口 1)作为 Linux 的终端,并且串口波特率设置为 115200。
②、root:root 用来设置根文件系统的位置,
例如:root=/dev/mmcblk1p2 用于指明根文件系统存放在mmcblk1 设备的分区 2 中。
root 后面有“rootwait rw”,rootwait 表示等待 mmc 设备初始化完成以后再挂载,否则的话mmc 设备还没初始化完成就挂载根文件系统会出错的。
③、rootfstype:用于指定根文件系统类型。
有2种启动方式:种是直接从 EMMC 启动,一种是从网络启动。
(1)从EMMC启动Linux系统(产品最终的启动方式):
编译出来的 Linux 镜像文件 zImage 和设备树文件保存在 EMMC中,uboot 从 EMMC 中读取这两个文件并启动。
在uboot种设置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-alientek-emmc.dtb; bootz 80800000 - 83000000;'
=> saveenv
=> boot 或者run bootcmd 即可启动Linux内核
(2)从网络启动Linux系统(调试用):
将 linux 镜像文件和根文件系统都放到 Ubuntu 下某个指定的文件夹中,这样每次重新编译 linux 内核或者某个 linux 驱动以后只需要使用 cp 命令将其拷贝到这个指定的文件夹中即可。
通过 nfs 或者 tftp 从 Ubuntu 中下载 zImage 和设备树文件。
①、先将zImage和设备树文件放到Ubuntu下的tftp目录中。
②、在uboot命令中设置 bootargs 和 bootcmd 这两个环境变量。
=> 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
在 Ubuntu 中安装 ncurses 库,命令如下:
sudo apt-get install build-essential
sudo apt-get install libncurses5-dev
make mx6ull_alientek_emmc_defconfig //在打开图形化配置界面之前,要先使用“make xxx_defconfig”对 uboot 进行一次默认配置
make menuconfig
menuconfig 重点会用到两个文件:.config 和 Kconfig。
.config保存着 uboot 的配置项。
Kconfig文件是图形界面的描述文件,也就是描述界面应该有什么内容,很多目录下都会有 Kconfig 文件。
注意:在使用图形化配置后,编译uboot时,不能使用如下命令:
./mx6ull_alientek_emmc.sh
要使用如下命令:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j16
因为 mx6ull_alientek_emmc.sh 在编译之前会清理工程,会删除掉.config 文件!通过图形化界面配置所有配置项都会被删除,图形化配置的内容保存在.config中。
当输入 make menuconfig 以后会匹配到顶层 Makefile的代码,进而会用到Makefile.build文件,此文件会读取scripts/kconfig/Makefile 中的内容。
目标 menuconfig 依赖 scripts/kconfig/mconf,因此 scripts/kconfig/mconf.c 这个文件会被编
译,生成 mconf 这个可执行文件。目标 menuconfig 对应的规则为 scripts/kconfig/mconf Kconfig,也就是说 mconf 会调用 uboot 根目录下的 Kconfig 文件开始构建图形配置界面。