目的
这篇文章中将测试在 NUC980 中运行Linux系统(基于SD1位置SD卡)。
本文相关的开发环境准备请查看下面这篇文章:
《新唐NUC980使用记录:开发环境准备与编译配置基础说明》
这篇文章中内容均在下面的开发板上进行测试(理论上也可以在新唐官方NUMAKER NUC980 IIOT开发板中进行测试):
《新唐NUC980使用记录:自制开发板(基于NUC980DK61YC)》
开始前请将将开发板的USB0和UART0都接到Windows电脑上,电脑上使用终端工具连接UART0。另外拨动拨动开关将PG[9:8]设置为非11的状态(比如01),以配合PG[1:0]=01使用SD1
SD卡分区
嵌入式Liunx系统通常分为Bootloader、Linux Kernel、RootFS三个部分,三个部分分别存储在三个位置中。具体对于SD卡而言存储分布如下:
- Bootloader放在开头1M以内位置上(这部分空间通常分区工具分区时会自动跳过);
通常还需跳过开头至少512Bytes,这部分用于保存SD卡分区信息; - Linux Kernel放在第一个分区上,通常使用FAT32(vfat)格式分区,通常不需要太大;
- RootFS放在第二个分区上,分区文件系统可以根据需求选择,这个可以大点,甚至分配剩下所有的空间;
SD卡可以通过读卡器在电脑上进行分区。Ubuntu上可以使用 GParted 工具进行分区:
安装方式: sudo apt install gparted
上面分区中唯一需要注意的是必须有一个fat32格式的分区(通常是第一个分区),用来存放内核和设备树等,不需要很大。
当然Ubuntu上也可以选择使用命令行方式进行分区:
# 先将SD卡插入Ubuntu中
# 使用 lsblk 查看SD卡设备号sdX
# 我这里显示为sdb,下面均以此进行说明
# 如果已经分过区了那么Ubuntu可能会自动挂载
# 逐条使用 sudo umount /dev/sdbn 进行卸载
# 对SD(TF)卡进行分区
sudo fdisk /dev/sdb
# 如果有分区的话可以输入 d 回车依次删除
# 输入 n 新建分区,分区大小根据需要设置即可
# 下面是我新建的两个分区的输入情况
# n回车 回车(p) 回车(1) 回车(2048) +32M回车 (如果有额外提示则Y回车)
# n回车 回车(p) 回车(2) 回车(67584) +200M回车 (如果有额外提示则Y回车)
# 输入 w 回车保存退出,输入使用 lsblk 查看分区情况
# 格式化分区建立文件系统
sudo mkfs.vfat /dev/sdb1
sudo mkfs.ext4 /dev/sdb2
U-Boot编译
NUC980 U-Boot针对不同启动源的修改调整具体参考BSP包中的文档《NUC980 U-Boot v2016_11 User Manual》。
我这里使用自制的开发板所以从nuc980_evb.h和nuc980_defconfig这对最基础的配置上进行修改:
cd ~/nuc980-sdk/
cd NUC970_U-Boot_v2016.11/
修改配置文件:
# cp include/configs/nuc980_evb.h include/configs/nuc980_evb.h.original
gedit include/configs/nuc980_evb.h
配置文件中找到对应位置进行修改:
/*#define CONFIG_SYS_USE_SPIFLASH */
/*#define CONFIG_SYS_USE_NANDFLASH */ // 屏蔽
/*#define CONFIG_ENV_IS_IN_NAND */ // 屏蔽
/*#define CONFIG_ENV_IS_IN_SPI_FLASH */
#define CONFIG_ENV_IS_IN_MMC // 51行 启用MMC
// include/configs/nuc980_evb.h 这个文件中还定义了很多位置和大小的参数
修改配置选项:
# make distclean
make nuc980_defconfig
make menuconfig
配置选项中找到对应位置进行设置:
进行编译:
export PATH=$PATH:/home/nx/nuc980-sdk/arm_linux_4.8/bin
make
编译完成后u-boot.bin位于当前目录下。将其拷贝至Windows中进行烧录。比如我的 /media/sf_common/ 是和Windows共享的目录,拷贝到这里即可:
# 拷贝至Windows电脑上
# sudo cp u-boot.bin /media/sf_common/
烧录U-Boot需要将SD卡插在开发板上。拨动拨动开关将PG[1:0]设置为00,按下复位键。在Windows中使用NuWriter进行烧录:
完成后拨动拨动开关将PG[1:0]设置为01,按下复位键。可以在终端界面中看到启动信息:
在这里可以识别到SD卡的分区情况,可以用fat系列指令查看或读取SD卡上的fat32格式分区中内容,后面U-Boot就是从这个分区中读取内核镜像到内存中执行的。
U-Boot环境变量
U-Boot最重要的是用来启动 Linux Kernel 的,比较常用的做法是使用U-Boot环境变量中设置的参数来读取内核镜像,然后启动内核。
环境变量可以写死在U-Boot程序中,可以使用U-Boot命令来设置,也可以通过烧录工具烧录到保存环境变量的存储区域。下面是最后一种方式的演示:
烧录U-Boot环境变量需要将SD卡插在开发板上。拨动拨动开关将PG[1:0]设置为00,按下复位键。在Windows中使用NuWriter进行烧录(如果选择时看不到文件,弹窗右下角选择All Files):
烧录完成后拨动拨动开关将PG[1:0]设置为01,按下复位键。可以在终端界面中看到启动信息:
上面烧录的环境变量如下:
bootdelay=3
baudrate=115200
stderr=serial
stdin=serial
stdout=serial
loadkernel=fatload mmc 0:1 0x7fc0 980uimage
bootcmd=run loadkernel;bootm 0x7fc0
上面环境变量中使用 fatload 命令从SD卡的第一个分区中读取 980uimage 文件到内存0x7fc0位置;然后启动位于内存0x7fc0开始的程序。当然因为现在还没有烧录内核所以启动会失败。
Linux编译
默认设置
NUC980 U-Boot针对不同启动源的修改调整具体参考BSP包中的文档《NUC980 Linux 4.4 BSP User Manual》。
我这里使用自制的开发板所以以nuc980_defconfig这个最基础的配置进行使用:
cd ~/nuc980-sdk/
cd NUC980-linux-4.4.y/
套用配置:
# make distclean
make nuc980_defconfig
进行编译:
export PATH=$PATH:/home/nx/nuc980-sdk/arm_linux_4.8/bin
make uImage
注意默认配置下编译时会将 NUC980-linux-4.4.y 同级的 rootfs 目录打包进内核(作为ramfs启动),同时会将编译出的镜像 980uimage 放到 NUC980-linux-4.4.y 同级的 image 目录中。将其拷贝至SD卡的第一个分区中即可。
我这里先将其拷贝至Windows中再拷贝到SD卡中。比如我的 /media/sf_common/ 是和Windows共享的目录,先拷贝到这里:
# 拷贝至Windows电脑上
# sudo cp ../image/980uimage /media/sf_common/
使用读卡器在Windows中将内核镜像拷贝到SD卡第一分区后,把SD卡插回开发板,重启后可以在终端界面中看到启动信息:
除了将内核镜像拷贝到Windows上后再拷贝到SD卡中,也可以直接在Ubuntu中拷贝到SD卡中:
可以在文件管理器中拷贝,也可以在终端中通过命令拷贝:
# cp ../image/980uimage /media/nx/BOOT/
使用SD卡剩余分区
上面默认编译出来的内核并没有使用SD卡的功能,没有把SD卡空间利用起来,如果想用的话需要进行调整。
修改配置选项:
# make distclean
make nuc980_defconfig
make menuconfig
开启内核对SD卡的支持:
因为第二分区文件系统使用的了EXT4格式,所以还需要开启内核相关支持:
重新编译拷贝内核就可以看到效果了:
# export PATH=$PATH:/home/nx/nuc980-sdk/arm_linux_4.8/bin
# make uImage
# cp ../image/980uimage /media/nx/BOOT/
使用SD卡分区存放rootfs
前面的使用中根据默认配置,用的是ramfs,这个对嵌入式设备来说是挺不错的,可以有效防止断电引起的文件系统出错的问题,缺点就是无法在烧录后运行时对rootfs修改保存。通常直接使用SD卡上剩余的分区存放rootfs是更加常见的做法。
在上节的基础上取消ramfs,然后设置从SD卡第二分区启动rootfs:
上面启动命令如下:
console=ttyS0,115200n8 noinitrd rootfstype=ext4 root=/dev/mmcblk0p2 rw rootwait mem=64M
重新编译拷贝内核:
# export PATH=$PATH:/home/nx/nuc980-sdk/arm_linux_4.8/bin
# make uImage
# cp ../image/980uimage /media/nx/BOOT/
接着需要把rootfs内容拷贝到SD卡第二分区:
# sudo rm -rf /media/nx/rootfs/*
sudo cp -r ../rootfs/* /media/nx/rootfs/
启动测试:
制作系统镜像
SD卡方式下制作系统镜像最简单的方式就是直接拷贝现成可用的SD卡数据。当然这种方式下SD卡分区建议不要设置太大,因为制作出来的原始镜像大小和SD卡上有用分区大小差不多。一般boot分区有个十几二十兆就够用了,rootfs分区可以根据需求来设置。
下面是制作镜像的过程(在Ubuntu中进行):
# 先将SD卡插入Ubuntu中
# 使用 lsblk 查看SD卡设备号sdX
# 我这里显示为sdb,下面均以此进行说明
# 如果已经分过区了那么Ubuntu可能会自动挂载
# 逐条使用 sudo umount /dev/sdbn 进行卸载
# 创建镜像文件
touch nuc980-system-image.bin
# 将SD卡中有用的数据写入到镜像文件中
# 大小为 1M(uboot) + 40M(bootfs) + 160M(rootfs) + 2M(多拷贝点,防止不明意外)
# 数据比较大会花点时间
sudo dd if=/dev/sdb of=nuc980-system-image.bin bs=1M count=203
# 压缩镜像文件
# zip nuc980-system-image.zip nuc980-system-image.bin
# sudo cp nuc980-system-image.zip /media/sf_common/
镜像文件使用时推荐用 BalenaEtcher ,这个工具支持windos、macos、linux,其官方页面和项目地址分别如下:
https://www.balena.io/etcher/
https://github.com/balena-io/etcher
BalenaEtcher可以将系统镜像(.zip或是.bin格式都行)直接烧录到SD卡上。
总结
根据上面的内容基本可以基于SD1位置使用SD卡在NUC980上运行起系统来了。接下来使用中更多的是根据需求对kernel、rootfs、application的调整编写等。
基本上使用这种方式开发的话后期要调整kernel、rootfs等,只需要通过拷贝修改后的内容到SD卡对应分区中,不需要再通过NuWriter工具了。