0. 引言
因为之前linux的经验基本都在应用层,内核和驱动这块涉及的不是很多,直接做实验感觉还是有很多不很清楚和没有准备好的地方,在这里做一个总结,更像一个流水账的备忘,也可以帮助同样入门经历的朋友,做一些借鉴吧。
也可能会存在一些错误,对于我来说,也是边收集边验证边总结,请大家不吝指教,后面也会持续更新。
这部分我可能会尽量写的比较细碎,请大神们无视。
1. 开发环境配置
1.1 硬件环境
1.1.1 开发板
开发板用的正点原子 ALTHA 阿尔法 开发板,配置应该是原子主推的一个配置
- 主控: I.MX6ULL (MCIMX6Y2CVM05AB/MX6Y2CVM05AB)
- EMMC 8G
- DDR3 512M
- 屏幕 【480*272分辨率:RGB接口】正点原子4.3寸RGB电容触摸液晶屏
1.1.2 网络环境
家里有一个不怎么用的笔记本,被我改成了Linux服务器,做了SSH(SFTP)的端口映射到外网,用来做自己的编译服务器(这部分实现可以参见我的 免费内网穿透方案)
- Ubuntu 服务器在内网的IP是192.168.0.110(有线网卡) 和 192.168.0.111(无线网卡)
nfs目录是/home/tao/smb/nfs
tftp目录是/home/tao/smb/tftp - windows 开发笔记本是192.168.0.100、192.168.0.101、192.168.0.102
- 阿尔法开发板被我分在 192.168.0.200 (eth0网卡,开发板ENET2接口)
1.2 软件环境
1.2.1 交叉工具链
1.2.1.1 工具链选择
主要是配置交叉工具链。
这里刚开始遇到一个疑惑,正点原子书《Linux驱动开发指南 v1.0》第四章里是让用 gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf。在《用户快速体验v1.0》,又让用的是arm-poky-linux-gnueabi-。
其实两个编译器,用哪个都可以,但是推荐看什么文档就先用什么编译器。
这个带了poky字眼的编译器,是yocto 编译产生的arm-poky-linux-gnueabi-gcc,可以说是freescale的IMX6平台用的交叉编译
arm-none-linux-gnueabi-gcc:是 Codesourcery 公司(目前已经被Mentor收购)基于GCC推出的的ARM交叉编译工具。可用于交叉编译ARM(32位)系统中所有环节的代码,包括裸机程序、u-boot、Linux kernel、filesystem和App应用程序。
arm-linux-gnueabihf-gcc:是由 Linaro 公司基于GCC推出的的ARM交叉编译工具。可用于交叉编译ARM(32位)系统中所有环节的代码,包括裸机程序、u-boot、Linux kernel、filesystem和App应用程序。
aarch64-linux-gnu-gcc:是由 Linaro 公司基于GCC推出的的ARM交叉编译工具。可用于交叉编译ARMv8 64位目标中的裸机程序、u-boot、Linux kernel、filesystem和App应用程序。
arm-none-elf-gcc:是 Codesourcery 公司(目前已经被Mentor收购)基于GCC推出的的ARM交叉编译工具。可用于交叉编译ARM MCU(32位)芯片,如ARM7、ARM9、Cortex-M/R芯片程序。
arm-none-eabi-gcc:是 GNU 推出的的ARM交叉编译工具。可用于交叉编译ARM MCU(32位)芯片,如ARM7、ARM9、Cortex-M/R芯片程序。
不同工具链具体对比可以参见结尾的参考链接,不过没有找到poky的说明,倒是觉得
Linaro 的 arm-linux-gnueabihf-gcc 和 aarch64-linux-gnu-gcc基本够用了。
这里我们就用 arm-linux-gnueabihf-gcc。
1.2.1.2 工具链安装
这个比较简单,网上教程很多,把下载到的工具链放到一个路径下,添加到环境变量里能找到即可,就像windows下的添加:
linux也是一样,不过不是图形化的。
要把交叉工具链的bin路径加到系统中。
简单说一下,几个文件的区别:
- etc 和 ~ 目录区别
etc下的是公共的配置
- /etc/bashrc:为每一个运行bash shell的用户执行此文件.当bash shell被打开时,该文件被读取。有些linux版本中的/etc目录下已经没有了bashrc文件。
- /etc/profile:此文件为系统的每个用户设置环境信息,当第一个用户登录时,该文件被执行.
~下的是个人配置
- ~/. profile每个用户都可使用该文件输入专用于自己使用的shell信息,当用户登录时,该文件仅仅执行一次!默认情况下,它设置一些环境变量,然后执行用户的.bashrc文件.
- ~/.bashrc:该文件包含专用于某个用户的bash shell的bash信息,当该用户登录时以及每次打开新的shell时,该文件被读取.
- profile 和 bashrc的区别
- 当shell是交互式登录shell时,读取.bash_profile文件,如在系统启动、远程登录或使用su -切换用户时;
- 当shell是交互式登录和非登录shell时都会读取.bashrc文件,如:在图形界面中打开新终端或使用su切换用户时,均属于非登录shell的情况。
即 . bashrc是在系统启动后就会自动运行。 . profile是在用户登录后才会运行
就我个人,我是直接写在 /etc/profile 下的。
1.2.2 TFTP服务器
有时候要从PC向开发板传输文件,这时候,TFTP用的就比较多。在PC上架一个TFTP的服务器,开发板就可以通过TFTP命令来获取文件。
命令:
//Uboot中,下载zImage到内存0x80800000处(已经配置过severip这个环境变量)
tftp 80800000 zImage
//linux中,下载zImage文件到当前目录
tftp 192.168.0.111 -g -r zImage
1.2.2.1 windows
windows自带了tftp的客户端,服务器可以使用tftpd32软件来实现。
1.2.2.2 Linux
在linux中搭建tftp服务器的步骤网上介绍的很多,就是通过tftp-hpa、tftpd-hpa 和 xinetd来实现。
具体可以参考结尾的参考链接。
- 安装方法:
安装xinetd 和 tftp软件。
sudo apt-get install xinetd
sudo apt-get install tftp-hpa tftpd-hpa
配置各自的配置文件,配置内容参见参考链接。
sudo vi /etc/xinetd.conf
vi /etc/default/tftpd-hpa
- 验证方法:
在Ubuntu服务器输入 netstat -a | grep tftp,出现upd 0 0 *:tftp :,则说明TFTP正常服务。
在板端测试一下TFTP传输功能。
1.2.3 NFS
NFS也是一种网络传输文件的常用方法,同时也可以作为根文件系统的挂载方式。
安装方法在结尾的参考链接。
//Uboot 传输文件
nfs [loadAddress] [[hostIPaddr:]bootfilename]
nfs 80800000 192.168.0.110:/home/tao/smb/nfs/zImage
//挂载根文件系统
root=/dev/nfs nfsroot=[<server-ip>:]<root-dir>[,<nfs-options>] ip=<client-ip>:<server-ip>:<gwip>:<netmask>:<hostname>:<device>:<autoconf>:<dns0-ip>:<dns1-ip>
setenv bootargs 'console=ttymxc0,115200 root=/dev/nfs rw nfsroot=192.168.0.110:/home/tao/smb/tftp/rootfs ip=192.168.0.200:192.168.0.110:192.168.0.1:255.255.255.0::eth0:off'
1.3 资料选择
目前使用的是原子的阿尔法开发板,配置资料用的是原子的资料包。
- uBoot & Linux 源码用的是:
驱动学习:单片夹 arm imx6 正点原子\开发板光盘A-基础资料V1.2\1、例程源码\11、开发板教程对应的uboot和linux源码.zip
(3/4/11 三个文件夹很绕,就像教程里介绍了不同的交叉工具链一样。)
2. 编译
2.1 编译uboot
2.1.1 编译指令
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
三句话的意思就是:
- 清理
- 设置配置文件 mx6ull_14x14_ddr512_emmc_defconfig(这个配置文件在 uboot-imxrel_imx_4.1.15_2.1.0_ga_alientek /configs)
- 开始编译(12线程)
其中 v=1 ,就是显示比较全的信息,类似
如果是v=2,就是类似CC xxxx 这样的打印。
编译结束之后,uboot会在当前目录生成。
编译出来的一个uboot.imx 是我们烧录进去的镜像。
一般都是uboot.bin , imx文件是NXP在IMX6系统中为bin文件加了头。
2.1.2 Uboot环境变量
Uboot中最重要的环境变量就是bootcmd 和 bootargs。可以说,其他所有的变量都是为这两个变量服务
- bootcmd 是 uboot会自动运行的命令,主要的流程写在这里
- bootargs 是 uBoot 传递给内核的命令,挂载之类的功能写在这里。
一些指令
- && 和 ; 的区别
2.2 编译内核
2.2.1 编译指令
#!/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
- menuconfig如果报错,还需要安装库支持
- 配置文件imx_v7_defconfig在 \arch\arm\configs
- 编译出来的文件在
设备树文件在 linux-imx-4.1.15-2.1.0-g52f6b26-v1.2/arch/arm/boot/dts
内核文件Image 和 zImage 在 linux-imx-4.1.15-2.1.0-g52f6b26-v1.2/arch/arm/boot
如果编译完没有生成zImage,则改用单线程编译,看下报错信息。
如图需要 sudo apt-get install lzop 安装lzop
2.2.2 zImage & uImage 区别
- Image 编译出来的最原始的内核文件,未压缩。
- zImage 是vmlinux经过gzip压缩后的文件,使用bootz启动。
- uImage U-boot专用的映像文件,它是在zImage之前加上一个长度为0x40(64)的tag。在生成的时候,需要添加“LOADADDR=0x8000”。它是在zImage之前加上一个长度为64字节的“头”,说明这个内核的版本、加载位置、生成时间、大小等信息;其0x40之后与zImage没区别,使用bootm启动。
2.2.3 bootm和bootz区别
- bootm用于加载uImage和ramdisk
bootm ${kernel_load_address} ${ramdisk_load_address} ${devicetree_load_address};
- bootz用于加载zImage和ext4文件系统
bootz ${kernel_load_address} - ${devicetree_load_address}
2.3 根文件系统
2.3.1 编译
这部分流程比较多,使用busybox的话,就按照《I.MX6U嵌入式Linux驱动开发指南V1.0》第三十八章。
书里的附录也给了buildroot 、 yocto 等方式。
2.3.2 格式
根文件系统有挺多种格式的,这里一开始有点晕的。
jffs2/yaffs/…
- 文件夹格式:就是正常的文件夹,可以使用nfs挂载。
- 二进制镜像:jffs2 、yaffs2、ramdisk,用于直接下载到ddr或者flash等等
对应的格式的文件系统的镜像,比如用mk.jffs2制作成jffs2的镜像,用mkyaffs2image制作成yaffs2的镜像等等
具体可以看尾部参考链接。 - 压缩包格式:rootfs.tar.bz2。 可以使用NXP的官方工具来烧录到EMMC
cd rootfs/
tar -vcjf rootfs.tar.bz2
2.4 memory分配
看I.MK6ULL的memory map,0x80000000以上是DDR。
目前使用的是原子自带的系统,
内存分配是
- zImage 8080 0000
- dtb 8300 0000
3. 开发方式
3.1 调试环境
目前有这么几种调试方式:
- 烧到SD卡,从SD卡启动
- 烧到EMMC,从EMMC启动
- NFS
- tftp
- …
- uBoot是一定要烧录到本地的(EMMC/SD),Uboot下载一定要通过 mfg 等工具的USB烧到本地的nand或者emmc等存储介质,或者烧到tf卡通过tf卡启动。
- rootfs/Image/dtb这些可以通过USB等 和Uboot一样烧录到本地、tf卡,也可以依靠Uboot的功能从nfs启动或者tftp更新到本地
- 在EMMC是一篇空白,什么都没有的时候,只能通过芯片自带的USB启动更新功能和mfgtools工具来把最初的uboot等数据烧录进去,才能拥有后面的nfs、tftp等功能。
总结一下:
Uboot | zImage | dtb | rootfs | |
---|---|---|---|---|
EMMC | √ | √ | √ | √ |
tf卡 | √ | √ | √ | √ |
tftp | X | √ | √ | √ |
nfs | X | √ | √ | √ |
其他 | … | … | … | … |
注意:rootfs没法通过TFTP的方式,把压缩包tar.bz2下载到内存某个地址来运行,压缩包我目前只看到mfgtools使用了。
常用的方法主要有:
- 本地Uboot启动 + TFTP zImage + TFTP dtb + nfs rootfs
- 本地Uboot启动 + nfs zImage + nfs dtb + nfs rootfs
3.1.1 本地Uboot启动 + TFTP zImage + TFTP dtb + nfs rootfs
如果uboot 和 zImage从tftp下载,rootfs从nfs启动
root=/dev/nfs nfsroot=[<server-ip>:]<root-dir>[,<nfs-options>] ip=<client-ip>:<server-ip>:<gwip>:<netmask>:<hostname>:<device>:<autoconf>:<dns0-ip>:<dns1-ip>
setenv bootargs 'console=ttymxc0,115200 root=/dev/nfs rw nfsroot=192.168.0.110:/home/tao/smb/tftp/rootfs ip=192.168.0.200:192.168.0.110:192.168.0.1:255.255.255.0::eth0:off'
tftp 80800000 zImage
tftp 83000000 imx6ull-14x14-evk.dtb
bootz 80800000 - 83000000
完整写起来就是
setenv bootcmd 'tftp 80800000 zImage;tftp 83000000 imx6ull-14x14-evk.dtb;bootz 80800000 - 83000000;'
setenv bootargs 'console=ttymxc0,115200 root=/dev/nfs rw nfsroot=192.168.0.110:/home/tao/smb/tftp/rootfs ip=192.168.0.200:192.168.0.110:192.168.0.1:255.255.255.0::eth0:off'
boot
3.1.2 本地Uboot启动 + nfs zImage + nfs dtb + nfs rootfs
如果需要zImage、dtb、rootfs这些都从nfs启动。
即先通过nfs获取zImage和dtb,然后挂载nfs的文件系统
setenv bootargs 'console=ttymxc0,115200 root=/dev/nfs rw nfsroot=192.168.0.110:/home/tao/smb/nfs/rootfs ip=192.168.0.200:192.168.0.110:192.168.0.1:255.255.255.0::eth0:off'
nfs 80800000 192.168.0.110:/home/tao/smb/nfs/zImage
nfs 83000000 192.168.0.110:/home/tao/smb/nfs/imx6ull-14x14-emmc-4.3-480x272-c.dtb
bootz 80800000 - 83000000
完整写起来就是
setenv bootcmd 'nfs 80800000 192.168.0.110:/home/tao/smb/nfs/zImage;nfs 83000000 192.168.0.110:/home/tao/smb/nfs/imx6ull-14x14-emmc-4.3-480x272-c.dtb;bootz 80800000 - 83000000;'
setenv bootargs 'console=ttymxc0,115200 root=/dev/nfs rw nfsroot=192.168.0.110:/home/tao/smb/nfs/rootfs ip=192.168.0.200:192.168.0.110:192.168.0.1:255.255.255.0::eth0:off'
boot