【嵌入式】折腾荔枝派Nano的SPI-FLASH Linux(u-boot篇)
# 折腾荔枝派Nano的SPI-FLASH Linux(u-boot篇)为了实现我的个人项目——**“单片机”解H.264并用320x240 SPI屏幕显示**,在各路大佬的讨论之下,大家都建议我使用 **全志F1C100s** 来实现这一目标。
当然也有大佬告诉我说 **h264的话f1c100s基本没用。复杂的解不出来简单的卡。** 但我觉得无所谓。我已经有好几个亲戚朋友预订了我的单片机小电视,他们并不在乎卡顿,而我只需要给他们封装一个FFmpeg的GUI,作为给他们使用的PC端软件,自动帮我生成“简单的”H.264视频,然后让他们复制到TF卡里,能出一个大致会动的图像就行。
顺带一提,虽然但是,我知道 F1C100s 不是单片机,而是 SoC(System on Chip)。
## 网上搜了一堆教程,发现一个共同点
那就是:你需要以下这几件套:
* (https://gitee.com/LicheePiNano/u-boot/tree/nano-v2018.01/)
* (https://gitee.com/LicheePiNano/Linux.git)
* rootfs 或者 (https://gitee.com/LicheePiNano/buildroot-2017.08.git)
这几个东西从目录树上来看,都长得像 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”
```
这个看起来比[另一个教程](https://whycan.com/t_2179.html)的更合适一些,少了一个`overlay`的分区,这个`overlay`分区看起来是为了存储WiFi相关的内容,而我不需要WiFi。
顺带一提,官方教程和这个保姆级教程的环境是 Ubuntu。但我更想在 WSL2 Debian 的环境下进行编译,因为我可以使用 Windows 的文本编辑器来修改各种需要修改的部分,操控性更好一些。虽然 sunxi-fel 的 Linux 版不能在 WSL2 里面进行 USB 的读写,但有 (https://github.com/eperie/build-scripts/releases/download/v1.2/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`这个分支,这些引导参数的内容已经被(https://github.com/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 代码**,要把它[删掉](https://source.denx.de/u-boot/u-boot/-/commit/018921ee79d3f30893614b3b2b63b588d8544f73)。同样,`scripts/dtc/dtc-lexer.lex.c_shipped`里面的`YYLTYPE yylloc;`也要删掉。
我曾怀疑是gcc版本太高的缘故,因为[官方“初体验”教程](https://wiki.sipeed.com/soft/Lichee/zh/Nano-Doc-Backup/get_started/first_eat.html)推荐的是 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 重新试一下编译
他也是这样配置的三件套:
* (https://github.com/florpor/u-boot/tree/licheepi-nano-v2020.01)
* (https://github.com/florpor/linux/tree/licheepi-nano-v5.4.y)
* (https://github.com/florpor/licheepi-nano)
这人他对 u-boot 做了这样的处理:和我之前一样删除了多余的`YYLTYPE yylloc;`声明,将 SPI FLASH `xt25f128b`加入到驱动里。但他还做了更多、更细致的修改,比如把各种 sunxi 外设驱动和寄存器加入了进去,把 MMC 加入到了设备树里、设置启用了 MMC,以及修复了原有 u-boot 的若干问题。他其实是直接从 (https://github.com/u-boot/u-boot) fork 过来后进行修改,而我则是拿 (https://gitee.com/LicheePiNano/u-boot/tree/nano-v2018.01/) 来用,还发现不那么好用。
未完待续。
页:
[1]