上一篇完成了uboot的移植,但是想要愉快的在開發板上玩耍還需要移植Linux內核和文件系統。
1.Linux內核
事實上對於F1C100S/F1C200S,Linux官方源碼已經對licheepi nano進行支持。所以我們完全可以通過licheepi nano的配置文件進行移植。
1.1. 下載內核源碼
進入Linux系統官網:
這里面列出的都是一些主要版本,如主線版本,上時間支持版本,個人推薦使用最新的長時間支持版本(5.10.69)。但是因為我這個項目是在參考一位大神的文檔的基礎上構建的,所以使用的是5.7.1版本,接下來就給一個選擇其他版本的方式。
選擇任意一項點擊 [browse]
在新打開頁面選擇 【summary】點擊【tag】中的【…】切換下載
如果想要直接下載5.7.1版本,請直接使用下面的連接
https://mirrors.edge.kernel.org/pub/linux/kernel/v5.x/linux-5.7.1.tar.gz
下載后完成后,將代碼復制到Ubuntu虛擬機並解壓源碼。
1.2.配置編譯
與上一篇中編譯u-boot一樣,我們也需要配置對Linux編譯進行配置:
- 指定架構類型
- 指定交叉編譯工具
- 項目配置
指定架構,就這個很好理解,就是指定CPU類型,就是配置為Arm就行,交叉編譯工具即為上一篇已經安裝好的編譯工具。
用VS打開Linux內核代碼,找到Makefile文件:
修改如下配置:
ARCH ?= arm
CROSS_COMPILE ?=arm-linux-gnueabi-
如果沒找到ARCH 或 CROSS_COMPILE字段,自己手動添加也行,如下圖所示:
事實上這兩個字段可以不用指定,在進行make的時候加上對應的參數就行,這里為了避免麻煩,所以直接放到了makefile文件中
接下來就是指定項目配置了,這個操作就是讓Linux內核認F1C100S/F1C200S這顆soc。
進入內核源碼中的arch/arm/configs目錄中,可以看到有很多開發板的配置文件,其中sunxi_defconfig是全志的配置文件,但是該配置文件非常不全,需要額外配置大量的選項,一般選項多大上千個,這里先使用licheepi_nano的配置文件。
https://files.cnblogs.com/files/twzy/linux-licheepi_nano_defconfig.zip
下載該文件,解壓出linux-licheepi_nano_defconfig,然后將其放到arch/arm/configs/目錄下
然后通過終端進入Linux-5.7.1根目錄,輸入命令:
make menuconfig
進入圖形配置界面,如圖所示:
該界面和u-boot配置一樣,所以操作方式也是一樣的,上下鍵移動選項,使用空格鍵進行選中或取消選擇,同樣通過空格鍵或回車鍵,進入子選項配置,通過Tab鍵選擇保存和退出即可返回上級菜單或命令行界面,也可以直接雙擊Esc鍵返回上級目錄。
1.3 配置TF卡設備樹信息
我們在完成內核配置后還需要配置TF卡的設備樹配置,否則即便是能正常運行內核,在加載文件系統的時候還是會有問題,在這里配置很簡單:
在linux-5.7.1/arch/arm/boot/dts 目錄下,分別修改suniv-f1c100s.dtsi、suniv-f1c100s-licheepi-nano.dts 兩個文件(記住這兩個文件、以后我們修改的地方多了^_^)
修改suniv-f1c100s.dtsi文件
在soc->pio 下添加如下代碼
mmc0_pins: mmc0-pins { pins = "PF0", "PF1", "PF2", "PF3", "PF4", "PF5"; function = "mmc0"; };
soc下添加如下代碼
mmc0: mmc@1c0f000 { compatible = "allwinner,suniv-f1c100s-mmc", "allwinner,sun7i-a20-mmc"; reg = <0x01c0f000 0x1000>; clocks = <&ccu CLK_BUS_MMC0>, <&ccu CLK_MMC0>, <&ccu CLK_MMC0_OUTPUT>, <&ccu CLK_MMC0_SAMPLE>; clock-names = "ahb", "mmc", "output", "sample"; resets = <&ccu RST_BUS_MMC0>; reset-names = "ahb"; interrupts = <23>; pinctrl-names = "default"; pinctrl-0 = <&mmc0_pins>; status = "disabled"; #address-cells = <1>; #size-cells = <0>; };
如圖,圖中的配置可能與讀者實際內容不一致,這是因為我改了很多東西,忽略即可,只需要關注紅色框中的內容即可。
修改suniv-f1c100s-licheepi-nano.dts文件,添加如下代碼
&mmc0 { vmmc-supply = <®_vcc3v3>; bus-width = <4>; broken-cd; status = "okay"; };
接下來執行make命令開始編譯內核和設備樹相關的文件了
make
首次進行編譯,通常會需要很長時間,編譯完成后,就會在在arch/arm/boot目錄下生成內核文件:zImage,在arch/arm/boot/dts目錄下設備樹文件:suniv-f1c100s-licheepi-nano.dtb 。
在編譯過程中,因為所配置Ubuntu系統的差異,可能會因缺少某些組件導致編譯報錯,不要慌,將對應的錯誤關鍵信息復制到搜索引擎后安裝即可,一下是作者碰到的兩個編譯報錯,如果有必要可以提前安裝:
linux-內核編譯配置 lexer.lex.c錯誤
wu@ubuntu:~/linux-5.4.8$ make exynos_defconfig
HOSTCC scripts/basic/fixdep HOSTCC scripts/kconfig/conf.o HOSTCC scripts/kconfig/confdata.o HOSTCC scripts/kconfig/expr.o LEX scripts/kconfig/lexer.lex.c /bin/sh: 1: flex: not found scripts/Makefile.host:9: recipe for target ‘scripts/kconfig/lexer.lex.c’ failed make[1]: * [scripts/kconfig/lexer.lex.c] Error 127 Makefile:567: recipe for target ‘exynos_defconfig’ failed make: * [exynos_defconfig] Error 2
解決方法:
sudo apt-get install bison
sudo apt-get install flex
編譯Linux內核時遇到:“error : openssl/bio.h :No such file or folder”
scripts/extract-cert.c:21:25: fatal error: openssl/bio.h: No such file or directory
compilation terminated.
scripts/Makefile.host:90: recipe for target 'scripts/extract-cert' failed make[1]: * [scripts/extract-cert] Error 1 Makefile:556: recipe for target 'scripts' failed make: * [scripts] Error 2
安裝openssl:
sudo apt install libssl-dev
1.4 TF分區配置
在上一篇中提到過u-boot 中的bootcmd 配置了Linux內核文件和設備樹文件存放位置,即TF卡的0:1分區中,而且我們已經得到了對應的文件,那我們應該怎么操作呢。
還記得我們在上一篇中安裝的Gparted軟件嗎,如果不記得,可以通過以下命令安裝:
sudo apt-get install gparted
把需要寫入系統的TF卡插到電腦的USB上,打開該軟件,可以看到此時有兩個存儲設備,一個是sda另一個是sdb,其中sdb就是我們的TF卡。如圖:
選中sdb,我們可以看到分區表中顯示為未分配,對於常規Linux嵌入式系統我們需要分兩個區,一個是存放zImage和dtb文件,即在bootcmd中配置的0:1分區,另一個區存放根文件系統。對於第一個分區,格式為fat16格式,因為u-boot只能識別這個格式,對於第二個區,一般為ext4格式,為Linux內核識別的格式。下面開始分區吧。
選中未分配空間並右擊鼠標,點擊[新建],然后填寫相關屬性,然后點擊[添加],所示。
需要注意【之前的空余空間】選擇1M,這是給u-boot預留的(u-boot在分區表中是無法看到的),【新大小】選擇32M ,【文件系統】選擇fat16,【卷標】輸入boot。
我們這里可以用相同的方式新建第二分區——ext4分區,如下圖
這里我們設置為100M,文件系統為ext4,卷標為rootfs,然后添加添加。
配置好分區表后,點擊工具來中的【對鈎】使配置的分區表生效。
配置分區完畢后,我們就可以在文件管理器中看到掛載的兩個分區,如圖
1.5 內核復制與執行
那么,我們將剛才生成好的zImage和dtb文件復制到TF卡的BOOT分區中
退出TF卡,插入開發板,上電,按重啟,我們就可以看到u-boot啟動完成后,自動進入了內核啟動環節,但是啟動后一會就報錯了,因為掛載文件系統產生錯誤。
U-Boot SPL 2018.01-05679-g013ca457fd-dirty (Sep 28 2021 - 15:29:32) DRAM: 32 MiB Trying to boot from MMC1 U-Boot 2018.01-05679-g013ca457fd-dirty (Sep 28 2021 - 15:29:32 +0800) Allwinner Technology CPU: Allwinner F Series (SUNIV) Model: Snail Card DRAM: 32 MiB MMC: SUNXI SD/MMC: 0 *** Warning - bad CRC, using default environment In: serial@1c25000 Out: serial@1c25000 Err: serial@1c25000 Net: No ethernet found. starting USB... No controllers found Hit any key to stop autoboot: 0 reading zImage 4515448 bytes read in 231 ms (18.6 MiB/s) reading suniv-f1c100s-licheepi-nano.dtb 6105 bytes read in 26 ms (228.5 KiB/s) ## Flattened Device Tree blob at 80c00000 Booting using the fdt blob at 0x80c00000 Loading Device Tree to 816fb000, end 816ff7d8 ... OK Starting kernel ... [ 0.000000] Booting Linux on physical CPU 0x0 [ 0.000000] Linux version 5.7.1 (twzy@ubuntu) (gcc version 7.2.1 20171011 (Linaro GCC 7.2-2017.11), GNU ld (Linaro_Binutils-2017.11) 2.28.2.20170706) #55 Tue Sep 28 21:04:24 CST 2021 [ 0.000000] CPU: ARM926EJ-S [41069265] revision 5 (ARMv5TEJ), cr=0005317f [ 0.000000] CPU: VIVT data cache, VIVT instruction cache [ 0.000000] OF: fdt: Machine model: LinuxCard by Kevin [ 0.000000] Memory policy: Data cache writeback [ 0.000000] Built 1 zonelists, mobility grouping on. Total pages: 8128 [ 0.000000] Kernel command line: console=tty0 console=ttyS0,115200 panic=5 rootwait root=/dev/mmcblk0p2 rw [ 0.000000] Dentry cache hash table entries: 4096 (order: 2, 16384 bytes, linear) [ 0.000000] Inode-cache hash table entries: 2048 (order: 1, 8192 bytes, linear) [ 0.000000] mem auto-init: stack:off, heap alloc:off, heap free:off [ 0.000000] Memory: 21496K/32768K available (7168K kernel code, 403K rwdata, 1664K rodata, 1024K init, 246K bss, 11272K reserved, 0K cma-reserved, 0K highmem) [ 0.000000] SLUB: HWalign=32, Order=0-3, MinObjects=0, CPUs=1, Nodes=1 ………………… [ 6.598874] Run /etc/init as init process [ 6.603993] Run /bin/init as init process [ 6.609078] Run /bin/sh as init process [ 6.613763] Kernel panic - not syncing: No working init found. Try passing init= option to kernel. See Linux Documentation/admin-guide/init.rst for guidance. [ 6.629985] CPU: 0 PID: 1 Comm: swapper Not tainted 5.7.1 #55 [ 6.636727] Hardware name: Allwinner suniv Family [ 6.642216] [<c010d604>] (unwind_backtrace) from [<c010ab60>] (show_stack+0x10/0x14) [ 6.651031] [<c010ab60>] (show_stack) from [<c01165a4>] (panic+0xe8/0x2e4) [ 6.658951] [<c01165a4>] (panic) from [<c071d080>] (kernel_init+0xd8/0x110) [ 6.666960] [<c071d080>] (kernel_init) from [<c0100140>] (ret_from_fork+0x14/0x34) [ 6.675547] Exception stack(0xc1835fb0 to 0xc1835ff8) [ 6.681293] 5fa0: 00000000 00000000 00000000 00000000 [ 6.690714] 5fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 [ 6.700102] 5fe0: 00000000 00000000 00000000 00000000 00000013 00000000 [ 6.707633] Rebooting in 5 seconds.. [ 12.687513] Reboot failed -- System halted
內核移植就基本結束了,要想讓小板真正的運行起來,還需要Linux的文件系統,那開始吧。
2.文件系統移植
根文件系統(rootfs)是內核啟動后掛載的第一個文件系統,如果沒有根文件系統,內核將無法開啟shell以及其他進程。
實際上內核啟動后會先掛載一個虛擬的文件系統,這個虛擬文件系統是在內存中運行的,其主要運行核心進程,虛擬文件系統掛載之后才掛載硬盤(TF卡或者emmc)上的根文件系統。
制作文件系統也有很多方式,如通過busyBox、Buildroot等工具制作。
本次使用Buildroot,制作過程相對簡單,兼容性好,由於根文件系統制作比較簡單。
進入buildroot官網
https://buildroot.org/downloads
這里選擇buildroot2018.2.11版本,將下載好軟件包傳入Ubuntu系統中,然后解壓並進入源碼目錄中,輸入清理命令。主要用於初始化一些設置,命令如下:
make clean
然后輸入以下命令進入配置界面
make menuconfig
此時會終端進入圖形配置界面,如圖:
2.1 Target options配置
先選擇Target options選項,進行對應芯片soc相關的配置,如圖:
配置如圖所示,下面是對其的解釋
- 第一個選項為架構選擇,這里選擇ARM架構小端模式,
- 第二個為輸出的二進制文件格式,這里選擇EFL格式,
- 第三個為架構體系,這里選擇arm926t,因為F1C200S/F1C100S的架構就是這個架構,
- 第四個為矢量浮點處理器,這里不勾選,因為對於F1C200S/F1C100S而言,其內部沒有浮點運算單元,只能進行軟浮點運算,也就是模擬浮點預運算。
- 第五個為應用程序二進制接口,這里選擇EABI,原因是該格式支持軟件浮點和硬件實現浮點功能混用。
- 第六個為浮點運算規則,這里使用軟件浮點
- 第七個選擇指令集,這里選擇ARM指令集,因為thumb主要針對Cortex M系列而言的,對於運行操作系統的A系列以及ARM9和ARM11而言,使用的都是32位的ARM指令集。
按【Tab鍵】選擇<save>進行保存,按【Esc鍵】回到上一級配置界面。
2.2 Build options配置
進入第二個Build options選項,配置如圖
按T【ab鍵】選擇<save>進行保存,按【Esc鍵】回到上一級配置界面。
2.3 Toolchain配置
進入第三個Toolchain選項,配置如圖:
這里我們選擇一些C\C++相關的庫,這樣我們就可以在開發板上直接編譯程序了,保存返回。
2.4 System configuration配置
對於System configuration選項,這里主要是配置一些系統登錄時候顯示的內容,配置如圖
這里主要配置了登錄時候顯示的內容和root賬號登錄密碼,接下來保存配置並且退回到命令行界面。
然后執行構建文件系統命令:
make
因為是首次編譯,而且buildroot在制作文件系統的時候需要聯網獲取組件,所以會編譯很久,那么“去和妲己玩耍吧”
當你終於被別人坑的自閉的時候,文件系統大概也許可能已經編譯完畢了。
2.5 文件系統移植與執行
此時在源碼的output/images目錄下有一個rootfs.tar,這個文件就是最終生成的根文件系統鏡像,現在只需要將該鏡像解壓到TF卡的第二分區即可。插入TF卡到電腦端,進入out/images目錄,然后輸入
# sudo tar -xvf rootfs.tar -C /media/<你的用戶名>/rootfs/ # 墨雲的賬號是twzy sudo tar -xvf rootfs.tar -C /media/twzy/rootfs/
此時可以看到TF卡的rootfs分區中有文件系統了
插入開發板,連接好串口,打開串口助手或者其他串口終端軟件,可以看到根文件系統成功掛載,同時進入shell交互,用戶名默認為root,密碼:123456,進入root賬號后
那么恭喜,你已經擁有了自己的Linux發行版。
至此我們完成了全部的系統移植任務,從下一篇開始我們將會升級我們的硬件設備和做一些更加有意義的東西,期待嗎?
2.6 升級逼格
我們發現登錄進自制的Linux系統后,命令行前置無論怎樣只顯示一個#號,逼格略低呀,怎么處理呢?
修改/etc/profile文件
vi /etc/profile
寫入
export PS1='[\u@\h: \w\a\]$'
重啟小板,就可以看到與與常規Linux一樣的操作體驗了,只是root賬號的時候還是顯示 $ 符號
需要注意的是,在開發板運行過程中,如果想要重啟,請先執行
poweroff
命令正常關閉系統后,在按重啟按鈕,否則有很大概率回造成文件系統損壞。
3. 點個燈吧
還記得我們在第一篇中提到過的我們自制小開發板的唯一的那個外設——LED燈嗎?
那我們就利用Linux提供的GPIO系統通過shell命令進行點燈實驗吧。
我們首先需要回到文件系統制作菜單
Device Drivers ->
GPIO Support ->
/sys/class/gpio/… (sysfs interface)。
按如下方式進行配置,然后編譯完rootfs,重新寫入小板
通過硬件可知LED燈連接的是PE6接口,低電平亮燈
這里我們先要了解一下GPIO編號和值的計算方式
引腳編號 = 控制引腳的寄存器基數 + 控制引腳寄存器位數
批注:
引腳編號是gpiochipxxx下的base + 第幾個GPIO,也就是base加偏移,偏移的是位數。
例如gpiochip34 下的第1個GPIO那么編號就是34 + 1 = 35
對於F1C200S/F1C100S這里:A=0、B=1、C=2D=3、E=4 ……、32是固定值、6就是偏移量
舉個栗子(如果使想用 PE6,那么引腳編號就可能等於 4 x 32 + 6 = 134。
這是一些參考命令
# 1、導出 echo 134 > /sys/class/gpio/export # 2、設置方向 echo out > /sys/class/gpio/gpio134/direction # 3、查看方向 cat /sys/class/gpio/gpio134/direction # 4、設置輸出(對於LED 設置1 為高電平即LED燈滅,設置0 為低電平,LED燈亮) echo 1 > /sys/class/gpio/gpio134/value # 5、查看輸出值 cat /sys/class/gpio/gpio134/value # 6、取消導出 echo 134 > /sys/class/gpio/unexport
輸入如下命令:
echo 134 > /sys/class/gpio/export echo out > /sys/class/gpio/gpio134/direction # 燈亮 (默認設置為高電平) echo 0 > /sys/class/gpio/gpio134/value # 燈滅 echo 1 > /sys/class/gpio/gpio134/value # 燈亮 echo 134 > /sys/class/gpio/unexport
效果如下:
我們這里借助Linux內建的GPIO子系統進行了電燈實驗,但是真正Linux靈魂點燈是要通過驅動方式來實現的,但是誰讓我是小白呢,以后再說吧。