折腾荔枝派Nano的SPI-FLASH Linux(u-boot篇)
为了实现我的个人项目——“单片机”解H.264并用320x240 SPI屏幕显示,在各路大佬的讨论之下,大家都建议我使用 全志F1C100s 来实现这一目标。
当然也有大佬告诉我说 h264的话f1c100s基本没用。复杂的解不出来简单的卡。 但我觉得无所谓。我已经有好几个亲戚朋友预订了我的单片机小电视,他们并不在乎卡顿,而我只需要给他们封装一个FFmpeg的GUI,作为给他们使用的PC端软件,自动帮我生成“简单的”H.264视频,然后让他们复制到TF卡里,能出一个大致会动的图像就行。
顺带一提,虽然但是,我知道 F1C100s 不是单片机,而是 SoC(System on Chip)。
网上搜了一堆教程,发现一个共同点
那就是:你需要以下这几件套:
这几个东西从目录树上来看,都长得像 Linux,每一个都包含 Linux 里面提取出来的代码,或者干脆就是 Linux 的代码;每一个都需要使用一个make menuconfig
的命令,冒出一个灰蓝灰蓝的文本菜单用于配置各项参数。它给我一种感觉:我使用一个 Linux 来 boot 另一个 Linux,然后这个 Linux 被用来 boot 我的第三个 Linux,而我的第三个 Linux 它有文件系统和 shell,我可以弄自己的脚本,从而让“单片机”能运行我自己的东西了。我需要在仅有的 16 MB SPI-FLASH 里面部署三个 Linux,感觉十分奇怪。
网友 大能猫 告诉我第一个主要用于 boot,它编译出来的体积能降低到 1 MB 以内,甚至 250 KB。第二个是 Linux 内核提供驱动,它提供你的“单片机”的外设 API,第三个是应用层的东西,有文件系统,你写脚本。
我总觉得这三个干嘛不合三为一呢,这真是奇怪。但我只是想想而已,谁提出想法谁去实践。所以只能是我提出想法后我自己去实践。呃!懒得弄了。
某“保姆级教程”看起来不错,我决定照着他说的做
保姆级教程链接:https://whycan.com/t_7558.html
分区规划
分区序号 分区大小 分区作用 地址空间及分区名
mtd0 1MB (0x100000) spl+uboot 0x0000000-0x0100000 : “uboot”
mtd1 64KB (0x10000) dtb文件 0x0100000-0x0110000 : “dtb”
mtd2 4MB (0x400000) linux内核 0x0110000-0x0510000 : “kernel”
mtd3 剩余 (0xAF0000) 根文件系统 0x0510000-0x1000000 : “rootfs”
这个看起来比另一个教程的更合适一些,少了一个overlay
的分区,这个overlay
分区看起来是为了存储WiFi相关的内容,而我不需要WiFi。
顺带一提,官方教程和这个保姆级教程的环境是 Ubuntu。但我更想在 WSL2 Debian 的环境下进行编译,因为我可以使用 Windows 的文本编辑器来修改各种需要修改的部分,操控性更好一些。虽然 sunxi-fel 的 Linux 版不能在 WSL2 里面进行 USB 的读写,但有 sunxi-fel.exe 这个 Windows 版的工具(使用mingw32-w64编译)。我选择使用 sunxi-fel.exe 来进行 SPI-FLASH 的烧写。
在 Windows 下你需要在使用 sunxi-fel.exe 之前,先使用 http://zadig.akeo.ie/ 提供的工具找到你的 Allwinner Soc in FEL mode
设备,给它安装一个 WinUSB 的驱动,你才能进行烧写,否则你会看到ERROR: Allwinner USB FEL device not found!
。请看此教程:https://linux-sunxi.org/FEL/USBBoot
另外,你需要你的设备在进入 FEL 模式后,你才能用 sunxi-fel 对其 SPI-FLASH 进行烧写,这里有个捷径:使用一个特殊的TF卡系统,插入即进入 FEL,不用短接 CS 也能进入 USB FEL 模式:
wget https://github.com/linux-sunxi/sunxi-tools/raw/master/bin/fel-sdboot.sunxi
dd if=fel-sdboot.sunxi of=/dev/sdX bs=1024 seek=8 #sdX换成你的TF卡实际设备!
虽然以防万一我还是装了个 Debian 的虚拟机,但根据多次尝试,我发现这个虚拟机主要的优势就是能运行 sunxi-fel 进行直接的烧写(除此以外就是文件系统区分大小写),但需要我设置虚拟机把荔枝派Nano的USB接入虚拟机。虚拟机如果抽风,你就会听到电脑死循环播放“发现新USB设备”和“USB设备断开”的音效,隔壁的无关的 Windows 虚拟机也突然疯狂提示“正在查找新设备”、“正在安装设备驱动”,与此同时虚拟机界面上显示的USB图标也一闪一闪的,看着令人感到血压飙升。
那个保姆级教程只告诉你如何配置,却没有告诉你怎么编译
可能保姆大佬觉得这没必要教,make的时候设置CROSS_COMPILE编译工具前缀就行了。
但这样就不够保姆了。所以我决定一边抄它那个教程,一边补充一些内容。
u-boot 配置
先安装必要的工具:(这个里面没有多余的库,缺一不可)
sudo apt-get install git build-essential swig libncurses-dev python2 python-dev device-tree-compiler
下载 u-boot 的荔枝派Nano分支。注意:这里使用nano-lcd800480
的分支,和保姆式教程不同。
git clone https://gitee.com/LicheePiNano/u-boot.git -b nano-lcd800480
打开配置菜单。
cd u-boot
make licheepi_nano_spiflash_defconfig
make menuconfig
原“保姆级教程”里提到的设置引导命令参数的 Enable a default value for bootcmd
和 Enable boot arguments
已经不再需要去设置了。因为在nano-lcd800480
这个分支,这些引导参数的内容已经被RobotFly加入到include/configs/suniv.h
文件里:
#define CONFIG_BOOTCOMMAND "sf probe 0 50000000; " \
"sf read 0x80C00000 0x100000 0x4000; " \
"sf read 0x80008000 0x110000 0x400000; " \
"bootz 0x80008000 - 0x80C00000"
所以已经不需要自己再把这个东西再设置一遍了。
在nano-lcd800480
分支里xt25f128b
已经被加入到设备树里,无需修改设备树
原“保姆级教程”里提到你需要把xt25f128b
加入到设备树里使 SPI-FLASH 得到支持,但nano-lcd800480
分支已经把它加入了进去。
编译 u-boot
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- -j
你的编译不会顺利通过。因为你会遇到YYLTYPE yylloc;
重定义的问题。所以其实你不必现在就急着编译,而是先看下面的章节,处理这些问题后再用上面的命令进行编译。
删除YYLTYPE yylloc;
这句bug代码
在scripts/dtc/dtc-lexer.lex.l
源码文件里有一句YYLTYPE yylloc;
这一句是一个 bug 代码,要把它删掉。同样,scripts/dtc/dtc-lexer.lex.c_shipped
里面的YYLTYPE yylloc;
也要删掉。
我曾怀疑是gcc版本太高的缘故,因为官方“初体验”教程推荐的是 gcc-7 而且直接提供了编译好了的 gcc-7 的 bin,下载后配置PATH即可使用。官方教程原文是用 wget 下载一个 gcc 的 bin,然后解压到 /opt
里面,再在.bashrc
里设置 PATH,这对于我而言,是一种往看不到的地方拉屎的行为,令我感到十分不舒服,因为以后如果忘记了今天的这一操作,那就一辈子只能用这个被遗忘的 gcc-7 了。时间一长,可能连硬盘是怎么被用掉的都忘了。于是我把这个 gcc-7 解压到工作目录下,写了个 bash 负责临时设置 PATH。把屎拉在看得到的地方令我感到舒适。试了一下 gcc -v
它的输出没有问题。但好像其实不需要装 gcc-7。官方说“此处为获取7.2.1版本,您可获取其他版本或者通过链接直接下载。”
实际上,gcc-10默认开启-fno-common
编译选项,而旧gcc则默认开启-fcommon
选项。其中,-fcommon
使gcc自动合并不同的编译单元导出的同名的全局变量,而-fno-common
则是一种语法严谨的设计,会告诉你“符号冲突”从而让链接器在lto过程中拒绝生成代码。合理的做法是把scripts/dtc/dtc-lexer.lex.l
和scripts/dtc/dtc-lexer.lex.c_shipped
的bug代码删掉,然后使用 gcc-10 编译,而非改用旧gcc或者使用-fcommon
选项。
删除scripts/Makefile.lib
第321行的多余的转义符号
当上面那个bug问题解决后,我还在make
的时候遇到了另一个bug问题:
LD u-boot
OBJCOPY u-boot-nodtb.bin
OBJCOPY u-boot.srec
SYM u-boot.sym
DTC arch/arm/dts/suniv-f1c100s-licheepi-nano.dtb
Error: arch/arm/dts/.suniv-f1c100s-licheepi-nano.dtb.pre.tmp:59.1-10 syntax error
FATAL ERROR: Unable to parse input tree
检查得知生成的 .suniv-f1c100s-licheepi-nano.dtb.pre.tmp
最后一行竟然出现了一个反斜杠:
...
&usbphy {
usb0_id_det-gpio = <&pio 4 2 GPIO_ACTIVE_HIGH>; /* PE2 */
status = "okay";
};
\#include "sunxi-u-boot.dtsi"
在经过各种“文件内查找”后,我终于找到了.suniv-f1c100s-licheepi-nano.dtb.pre.tmp
的来源,在scripts/Makefile.lib
里面第321行:
(cat $<; $(if $(u_boot_dtsi),echo '\#include "$(u_boot_dtsi)"')) > $(pre-tmp); \
可以看到里面多了个转义符号。将其删除,得到以下的正确代码:
(cat $<; $(if $(u_boot_dtsi),echo '#include "$(u_boot_dtsi)"')) > $(pre-tmp); \
然后删除.suniv-f1c100s-licheepi-nano.dtb.pre.tmp
并重新编译。
u-boot的编译需要你的python
命令是python2
很坑爹吧!你需要使用update-alternatives
来切换你的默认python
命令的版本:
sudo update-alternatives --install /usr/bin/python python /usr/bin/python3 3
sudo update-alternatives --install /usr/bin/python python /usr/bin/python2 2
sudo update-alternatives --config python
运行后,它提示如下:
There are 2 choices for the alternative python (providing /usr/bin/python).
Selection Path Priority Status
------------------------------------------------------------
* 0 /usr/bin/python3 3 auto mode
1 /usr/bin/python2 2 manual mode
2 /usr/bin/python3 3 manual mode
Press <enter> to keep the current choice, or type selection number:
要选择python2,需要输入1,然后按下回车。作为 python 用户,我实在接受不了自己的默认 python 变成了2,这种难受的感觉堪比你的 vcxproj 文件被 VC6 关联、CMD、BAT 批处理被 git bash 关联了一样难受。
试了一下发现这样不行,因为我有安装过pyenv
,所以update-alternatives
不起作用(它让/usr/bin/python
变为一个符号链接来指定你的python版本,但pyenv
让python
命令变为/home/username/.pyenv/shims/python
)。于是只好用最简单粗暴的办法:
alias python=python2
重新编译时这个问题解决。
就快要编译成功了,结果提示:No rule to make target 'checkarmreloc', needed by 'all'. Stop.
我在顶层Makefile里发现了checkarmreloc: u-boot
这句。它依赖u-boot
文件,而这个文件也确实存在,为什么就 No rule to make target 了呢?
网上搜到似乎和荔枝派 Nano 毫无关系的内容:“如何移植u-boot”。发现搜到的内容套用到荔枝派 Nano 似乎有效。
打开u-boot/arch/arm/Kconfig
,找到config ARCH_SUNXI
,在下面插入一行select SUPPORT_SPL
。
然后重新编译。
如何重新编译
每次编译时,若遇到bug问题停止了编译,在修改了bug文件后,应当运行make clean
来删除编译过程中产生的中间文件,然后重新运行make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- -j
来编译,以确保你对bug文件的修改能产生效果。但make clean
会提示“无法删除SPL
,它是个目录”。此时你可以手动把u-boot的spl目录删除,再重新运行make clean
,就可以清理干净,然后重新编译了。
经过多次编译、一顿操作后,python2无法引用_libfdk
的问题依然不好解决
我选择去世。
换另一个人的 u-boot 重新试一下编译
他也是这样配置的三件套:
这人他对 u-boot 做了这样的处理:和我之前一样删除了多余的YYLTYPE yylloc;
声明,将 SPI FLASH xt25f128b
加入到驱动里。但他还做了更多、更细致的修改,比如把各种 sunxi 外设驱动和寄存器加入了进去,把 MMC 加入到了设备树里、设置启用了 MMC,以及修复了原有 u-boot 的若干问题。他其实是直接从 u-boot/u-boot fork 过来后进行修改,而我则是拿 LicheePiNano/u-boot 来用,还发现不那么好用。
未完待续。