一. 瞎抄原理图与乱画PCB

因为墨云是基于高中物理水平的电路知识来学习、而且此前也就玩过树莓派、Esp8266之类的开发板,水平基础趋近于零,所以在写这个系列的时候抱着记录的心态、还望不足之处还望大佬们指正。

《论语》说:见贤思齐焉。所以现在墨云又开始瞎折腾了

为啥突然想做嵌入式开发呢,因为看见了下面两位牛人

【稚晖君】

https://www.bilibili.com/video/av65365123/

【在名片上运行的Business Card Linux】

https://www.thirtythreeforty.net/posts/2019/12/my-business-card-runs-linux/

于是躁动的心开始蠢蠢欲动。

先定个小目标:

  设计一个没啥作用,但是可以运行Linux的小板。

样子大概长这样:

  

所以有了初步想法,那开始吧

1. 工具与芯片说明#

立创EDA:电路图与PCB设计工具,本次硬件部分设计全部使用立创EDA完成

F1C100s :全志的一款基于Arm的小型Soc,自带32MB的内存,其升级版F1C200s为64MB内存,因为其极其便宜(当然最近因为芯片涨价影响),可以运行Linux,最典型的基于该芯片的荔枝派开发板。

CH340E:USB转串口通信芯片,是作为与外界通信的唯一接口 、CH340E个头小、外围电路少,使用简单。

原理图:先后参考了(baipiao)了Licheepi Nano、Business LinuxCard、LiShanwenGit(https://www.oshwhub.com/LSW12315)立创开源广场的一系列项目

2.原理图说明#

电源管理

电源部分使用USB方式供电,输入电压为5V,这里供电部分和串口电路共用相同USB端口。

通过查询F1c100s数据手册:

通过综合分析,我们可以大概把电源分为4类

Vdd-Core:1.1V

Vcc-Dram:2.5V

AVCC : 3.0V

UVCC/VCC-IO/TV-AVCC/TTL:3.3

这里主要使用 SY8088AAC 同步降压DC-DC稳压器,为SOT32-5的封装方式,通过使用外围电阻调整输出电压。

公式为:

Vout = 0.6 * (`1+Ra/Rb)

而AVCC使用XC6206P302MR-SOT23的封装方式,输入5V 输出为3.0V

因为 AVCC为模拟电源电压,为了避免引入电源干扰,通常需要把把模拟电路与数字电路分开隔离开(这个地方解释可能不对,欢迎指正)。

电源部分的原理图如下:

电压输入输出端都使用滤波电容进行处理,使用还要接入2.2uH的功率电感,注意一定要用功率电感,电流要求可以达到1A以及以上的才行

这个板子使用0805的功率电感,建议最好使用CD32类型的绕线功率电感。

核心原理图

对于核心部分的原理图如下,因为本次电路设计主要以验证为主,所以并没有做太多的外设电路。

除了常规的核心、外围、DRAM工单引脚,核心部分还引出了

\1. TF卡引脚,作为本板子唯一的系统加载电路,这是必须的

2.晶振,使用规格为24Mhz的有源晶振、加两个15pf的负载电容

3.串口调试 作为板子与外界唯一交互的通道,这个也是必须的,然而在做这个成功的给自己挖坑了。

4.LED灯,这是这个板子唯一的外设,也是用来学习驱动开发的第一步。

5.dram_vref、Var1、Var2 这是必须要接的、外围电路,我也不知道的干啥的 _

6.USB OTG 也是作为一个通信接口来使用,通过这个接口可以为板载Flash下载程序,但是因为本板没有做Flash,所以目前唯一的作用就是放到验证fel是否可以调通。

7.复位按钮

挖坑点

一直以来认为发光二极管也是二极管,所以就有下面的设计(乱画),于是后来感觉板子没问题,但是就是串口死活不显示数据、在众多大佬的帮助下,才发现了这个其妙(naocan)

的接线方法,于是将两个发光二极管位置放了两个0欧的电阻,一下子就成功调通了。

对于芯片电源输入端的滤波电容与Dram_vref接线如下:

对于滤波电容简单的说法就是,对于供电端的电压,因为电路设计或者外界干扰等等,其实不是完美的电压,总会存在高频或是低频的噪声,而用小容量的电容就可以降低这些干扰。

常规的容值就是 10uf 、1uf、100nf

通信电路

我们在核心原理图中看到了引出的串口线路,而串口的接口如下:

显然在你看看你超博的笔记本机身,并没有发现这个接口,偶尔还有一些老的笔记本上面可以看到类似的接口,对不起——那是显示屏的VGA接口。

那我们如何使用串口传输的信息呢,我们需要一个USB转串口的芯片,usb转串口的芯片很多,这里选择 CH340E 这个型号,因为其很小,接线也方便。

原理图如下:

前面提到,这个板子共用了电源与TTL共用了一套设计,也就是是说USB线插上就可以启动小板,并且开始进入串口调试。

原理图中的U5是一个自恢复保险丝。

这里需要注意一下:

根据CH340E官方的原理图,当VCC接入5V的时候,V3 需要接一个100nf的电容,但是此处在V3直接接入5V,也可以工作。

实际使用的时候最好不要这样做。

TF卡接口

和电脑主机在BIOS选择启动方式一样, F1C100s 支持多种方式的系统加载机制比如通过SPI接口加载Flash芯片中的镜像,或者通过TF卡接口加载镜像。

这里使用TF卡作为启动源,这样做是因为

1.TF卡容量可以自己控制。

2.系统烧写调试方便

这部分电路相对简单,原理图如下:

OTG 与唯一的外设LED灯

3.PCB绘制#

PCB尺寸为42mm*29mm ,可以说非常小了,为了便于焊接,所有容阻都是用0805的封装方式

电源走线为14mil ,信号线为8mil

4.PCB焊接#

焊接PCB是一项手艺活,尤其是QFN方式封装的F1C100s 更是难到发指,还好借助焊台和热风枪,完美的完成了焊接,当然放大镜、洗板水是不可缺少的。

并且因为板子时长需要在手上把玩(盘PCB)。所以选择了无铅稀浆进行焊接。

效果如下:

成功运行Linux,

因为还没开始着手做Linux移植,暂时使用LicheePi 的镜像,下一节开始做Linux的移植。

5. 后记[#](https://www.cnblogs.com/twzy/p/14714651.html# 5.-后记)

事实上世界上从来没有所谓轻易的成功,对于初次玩PCB的小白更是如此,现在这个小板能成功也是经历三四个月,五六次打板才成功的。以下是早期的趟雷PCB场景与先烈。 (右下角为成功的小板)

硬件资料包:[#](https://www.cnblogs.com/twzy/p/14714651.html# 硬件资料包:)

下载

二. u-boot移植

我们都知道,PC在启动的时候,首先是进入BIOS,再根据BIOS中配置信息引导后续的启动操作系统,比如配置Windows启动。

而对于嵌入式linux中,并没有BIOS,这时候就需要一种类似引导程序来处理。于是就有了BootLoader。

BootLoader是一段小程序,可以把它想象成PC机linux上的GRUB/LILO引导程序,可以直接从flash或TF卡中运行,来装载内核。它可以初始化硬件设备,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统做好准备。

1. 嵌入式开发板的启动过程#

一个嵌入式系统从软件角度来看分为三个层次:

  • 引导加载程序

​ 包括固化在芯片中的boot程序(可选)和BootLoader两大部分,对于固化的boot程序。主要是芯片通过外围电路连接的实际情况选择读入程序的位置,比如:通过TF卡或是SPI以及其他方式启动,至于优先顺序这就要具体看芯片的数据手册,个人没做过具体测试。

  • linux内核

​ 特定于嵌入式平台的定制内核

  • 文件系统

​ 包括了系统命令和应用程序

BootLoader Boot Parameters Kernel Root Filesystem

BootLoader启动过程可分为单阶段和多阶段(stage1、stage2),其中stage1完成初始化硬件,如CPU寄存器、内存控制器,为stage2准备内存空间。一般stage1是可以直接在nor flash中运行的,并将stage2复制到内存RAM中,设置堆栈,然后跳转到stage2(从这也可以看出stage2是在RAM中运行的,与stage1不同)

Boot Parameters 顾名思义,就是配置了要启动内核的参数,包含要加载系统内核相关文件的位置,要加载到内存中的位置,定位到文件系统的位置,相关输入输出的呈现等一系列参数。

kernel 在存放在bootloader之后,对于SoC来说,代码都需要在RAM中运行,这里与MCU不一样的地方就是引入了MMU(内存管理单元)。对于MCU而言,由于其执行速度低,因此运行代码都在ROM中直接运行,而对于Flash而言,其读取速度远不及RAM的速度,因此对于运行速度非常快的SoC而言,所有的代码都需要在RAM中运行。但是这里有一个问题,RAM掉电数据将会丢失,故代码保存不可能放在RAM中,当前所有的嵌入式设备而言,代码保存都是放在ROM中,因此在SoC中运行代码需要将代码搬运到RAM中然后再执行。

Root Filesystem 由于其执行过程需要对ROM进行读写操作,因此可以不用搬运到RAM中,但是实际过程中内核启动后会产生一个虚拟的文件系统,该文件系统是挂在根文件系统的关键所在,这里不详细讲解。整体来说,大致的过程为,嵌入式设备上电后将执行bootloader,对硬件进行硬件和堆栈初始化,然后搬运内核到RAM中并启动内核,紧接着挂载根文件系统。

2. 环境配置与参考项目#

系统:Ubuntu 16

编辑器:VSCode

参考项目:Lichee-Pi Nano

地址:https://wiki.sipeed.com/soft/Lichee/zh/Nano-Doc-Backup/index.html

Lichee-Pi Nano

需要注意的是一定要选择Nano版本,因为我们开发板使用的主控芯片和Nano的主控是一致的,所以后续我们要编译U-boot,内核都可以参考(bai piao)这里面的配置。

主控芯片:F1c100s/F1c200s,100s内置32MB DDR1内存,200s内置64MB DDR1内存,200s贵一点,他们都是QFN88封装。

ARM926ejs内核,主频默认408MHz,据了解做产品出货的一般在600M左右。

带有100M的SPI接口2个,SDIO接口1个,USB OTG接口,还有CSI摄像头接口,LCD RGB显示屏接口,音频接口,I2C I2S UART PWM等等。

还有就是他们不支持硬件浮点,所以浮点运算使用软浮点方式。

F1c100s/F1c200s芯片功能

3.交叉编译器#

我们通过PC版的Linux自带的gcc编译的程序只能在当前系统架构下的cpu架构(x86)下运行,如果我们想要编写的程序在嵌入式Linux下运行,那么就需要用到对应的编译器。

我们做的开发板主控芯片F1C200S,内核为ARM9,其架构使用的是ARMv5架构,所以我们也要选用对应的编译器,同样,这样的编译器很多,这里我们使用最常用的arm-linux-gnueabi- ,因为交叉编译器F1C200S必须高于6.0版本,这里我们使用7.2版本

点击下载

下载较慢时使用下载工具

下载完成后解压文件:

1
tar -vxjf gcc-linaro-7.2.1-2017.11-x86_64_arm-linux-gnueabi.tar.xz

然后在/usr/local目录下新建arm-linux-gcc目录

1
sudo mkdir /usr/local/arm-linux-gcc

进入解压目录下:

1
cd gcc-linaro-7.2.1-2017.11-x86_64_arm-linux-gnueabi/

将该目录下的所有文件复制到新建的目录下

1
sudo cp -rd * /usr/local/arm-linux-gcc/

最后需要添加该工具链的环境变量使其可以在任何目录下执行,打开/etc/profile文件

1
sudo nano /etc/profile

在文件末尾添加以下内容

1
PATH=$PATH:/usr/local/arm-linux-gcc/bin

添加完毕,使路径生效

1
source /etc/profile

接下来在终端输入:

1
arm-linux-

然后连按两次Tab键,如图在表示成功:

如果没有出现,则进行下面操作,安装必要的动态链接库

1
sudo apt-get install lib32ncurses5 lib32z1

至此,我们完成了编译工具的配置。

4. 编译U-boot#

当Arm开发板上电以后第一个要加载到内存并运行的程序就是**BootLoader,**BootLoader****的同类型程序很多,如U-boot、X-boot、Rt-Thread,这里我们依然选中最常用的U-boot作为目标(因为其他的我也不会呀),

最新版本的uboot几乎包含当前主流的SoC芯片,前面提到本开发板使用的芯片和licheePI nano相同,大部分硬件也是兼容的,为了快速移植该部分,这里采用licheePI nano的u-boot来进行移植。在终端输入如下命令克隆u-boot:

1
git clone https://github.com/Lichee-Pi/u-boot.git -b nano-v2018.01

克隆完毕文件会保存在当前目录下,进入该目录,*
*

1
cd u-boot

在该文件夹下有很多分支,我们可以查看所有分支,使用如下命令:

1
git branch -a

现在我们使用的是nano开发板,所以将当前分支切换到nano分支,命令如下:

1
git checkout nano-v2018.01u-boot

切换到Nano分支

默认的没有指定交叉工具链和架构,因此在编译之前需要指定交叉工具链和芯片架构,u-boot的交叉编译器在u-boot 的根目录下中的Makefile文件中定义了。打开文件找到CROSS_COMPILE变量,修改为如下:

1
2
ARCH=arm
CROSS_COMPILE=arm-linux-gnueabi-

配制交叉编译环境

这样我们就能使用我们指定的编译器来编译u-boot了。

在u-boot项目的config目录下存在对多种板子的配置描述文件,由于每个板子的外设不同,因此编译之前必须要对u-boot进行配置。然而配置是一件比较繁琐的事情,特别是像u-boot这种比较复杂的项目而言,初学者几乎无法完成。幸运的是对于大部分开发板而言,config目录下有其配置好的默认配置文件。进入config目录中,然后执行ls查看当前所有的配置文件

1
2
cd config
ls

查看配制文件

找到licheepi_nano_defconfiglicheepi_nano_spiflash_defconfig,前者表示为TF卡启动,后者表示从SPI 设备启动,因为我们做的小板只有从TF卡启动,所以我们需要使用 licheepi_nano_defconfig

现在回到上级目录,然后执行

1
make licheepi_nano_defconfig

这样我们把licheepi_nano_defconfig 作为默认配置项。

接下来我们就可以用图形界面进行配置了,执行

1
make menuconfig

此时出现图形配置选项,如下图所示

u-boot Menuconfig配制,注意红框中的配置,我们后续要用到。

至此我们的u-boot环境配置就完成了,但是我们还有个问题要解决:如何让u-boot引导系统

我们在PC端安装Windows系统的时候往往需要选择启动顺序,比如需要优先通过光驱或u盘启动等。

同样在u-boot中也需要这样的配置,当然u-boot比PC配置稍微复杂一丢丢。我们前面提到Linux嵌入式系统结构分布中有个Boot Parameters 部分,这部分就是做引导配置的,那怎么配置呢,总体来说可以分为两部分:

  1. bootcmd,主要用于描述控制Linux内核文件以及其他描述文件加载到内存中位置以及启动Linux内核系统等
  2. bootargs,用于配制文件系统、串口信息等。

bootcmd

在最开始提到过,内核一般不在flash中运行,这样就需要将内核搬运到内存中,这个过程需要u-boot来完成。对于mmc (TF卡)而言,在u-boot有专门的命令load mmc,该命令可以将mmc中的代码从flash搬运到指定的地址处。

当u-boot中环境变量bootdelay计数到0时,此时uboot就会开始执行bootcmd中的命令。

bootdelay这个环境变量是一个计数器,当u-boot主体运行完毕后,此时bootdelay该变量的值将会开始递减,递减时间为1s,当递减到0时,此时u-boot将会跳转到bootcmd处开始执行bootcmd命令,(你可以简单理解为PC启动后有一两秒时间等待,你可以可以通过F8或Enter键打断进入Bios设置的过程,这个等待时间就由u-boot中的bootdelay来控制)。

下面我们需要记住这句指令,这就是我们当前制作的开发板需要用到的bootcmd全部内容

1
load mmc 0:1 0x80008000 zImage;load mmc 0:1 0x80c08000 suniv-f1c100s-licheepi-nano.dtb;bootz 0x80008000 - 0x80c08000;

如果你需要详细了解这句话那就接着往下看,如果不需要则可以跳到 下面的u-boot参数配置环节

对于上面命令,我们根据分号拆分为3部分:

1
2
3
1> load mmc 0:1 0x80008000 zImage;
2> load mmc 0:1 0x80c08000 suniv-f1c100s-licheepi-nano.dtb;
3> bootz 0x80008000 - 0x80c08000;

其中两个 load mmc 命令、一个bootz 命令。

先看第一条:

1
load mmc 0:1 0x80008000 zImage

load mmc有三个参数:第一个参数是mmc(TF卡)分区,第二个参数是内存中目标地址,第三个参数是源文件。

即上面的命令意思是将mmc的0:1 分区中的zImage复制到内存中的0x80008000地址处。这里的zimage就是Linux内核,后续会提到该文件编译,0:1这个可以这样理解0表示TF卡(TF卡属于mmc存储器的一种),1这表示TF卡的第一个分区(boot分区)后面会提到。

而对于内存位置 0x80008000 地址位置,将其理解为默认值就行了。这样完成了zImage的加载。

下面分析第二条命令:

1
load mmc 0:1 0x80c08000 suniv-f1c100s-licheepi-nano.dtb

有了上面的加载zImage的说明,可以很轻松的理解上面的命令意思是将mmc的0:1分区中的suniv-f1c100s-licheepi-nano.dtb文件加载到内存中的0x80c08000地址处。对于suniv-f1c100s-licheepi-nano.dtb 这个文件,叫做设备树文件,简单来说就是当前开发板上面所有外设备描述文件,这部分将会在后续内核编译部分进行详细说明。

对于第三条命令:

1
bootz 0x80008000 - 0x80c08000

的意思是告诉内核镜像的起始地址为0x80008000,加载的设备树地址为0x80c08000。这里是告诉cpu从这里开始启动Linux, bootz命令的格式是:bootz空格0x80008000空格-空格0x80c08000,注意-左右有空格。

除了bootz 命令外,有些系统里面还可能存在一个叫做bootm命令,这是是对没有使用设备树内核的镜像启动命令,早期版本的内核没有引入设备树,因此对于早期的内核一般使用的是bootm,其命令格式为bootm内核地址,比如bootm x0x30008000,意思是从0x30008000开始启动内核,启动内核的过程其实是将pc指针指向该地址,这样处理器就会从该地址处运行代码。

这里我们就完成了bootcmd的说明,接下来我们看另外一个参数。

bootargs

bootargs也是u-boot环境变量中一个非常重要的变量,上面已经讲解了内核的启动可以通过bootcmd来完成,那接下来内核启动完毕后必须挂在根文件系统(rootfs)。但是内核并不知道根文件系统的具体位置,我们必须要告诉根文件的位置后内核才能将其挂载,这时就需要有bootargs变量。该变量的作用是告诉内核根文件系统的位置和属性以及必要的配置,

1
console=ttyS0,115200 panic=5 rootwait root=/dev/mmcblk0p2 earlyprintk rw

同上面分析的方法一样,我们依然将这部分命令拆成几部分来说明。这里需要说明的是,这部分配置信息是由u-boot 直接按照参数字符串方式提供给Linux内核,然后由Linux内核进行执行的,这也说明里为什么格式与bootcmd配置方式不一致。

console=ttyS0,115200 表示终端为ttyS0即串口0,波特率为115200;

panic=5 字面意思是恐慌,即linux内核恐慌,其实就是linux不知道怎么执行了,此时内核就需要做一些相关的处理,这里的5表示超时时间,当Linux卡住5秒后仍未成功就会执行Linux恐慌异常的一些操作。

rootwait 该参数是告诉内核挂在文件系统之前需要先加载相关驱动,这样做的目的是防止因mmc驱动还未加载就开始挂载驱动而导致文件系统挂载失败,所以一般bootargs中都要加上这个参数。

root=/dev/mmcblk0p2 表示根文件系统的位置在mmc的0:2分区处,**/dev是设备文件夹,内核在加载mmc中的时候就会在根文件系统中生成mmcblk0p2**设备文件,这个设备文件其实就是mmc的0:2分区(这里对应TF卡的第二个分区:rootfs),这样内核对文件系统的读写操作方式本质上就是读写/dev/mmcblk0p2该设备文件。

earlyprintk 参数是指在内核加载的过程中打印输出信息,这样内核在加载的时候终端就会输出相应的启动信息。rw表示文件系统的操作属性,此处rw表示可读可写。

5.u-boot参数配置 #

1
make menuconfig

选中 Enable boot arguments 按空格选中,下面会显示:() Boot arguments

然后选中Boot arguments ,按回车,进入配置窗口,接下来上面解释过的bootargs 参数信息:

1
console=ttyS0,115200 panic=5 rootwait root=/dev/mmcblk0p2 earlyprintk rw

配置bootargs信息

然后按Tab键选中****,保存并进入主菜单。

同理配置:Enable a default value for bootcmd 按空格选中,下面会显示:**() bootcmd value** 配置项,

选中bootcmd value 进入配置界面,输入bootcmd命令:

1
load mmc 0:1 0x80008000 zImage;load mmc 0:1 0x80c08000 suniv-f1c100s-licheepi-nano.dtb;bootz 0x80008000 - 0x80c08000;

配置bootcmd参数

Tab键选中****,保存并进入主菜单。

6.u-boot编译与烧录#

先保存图形配置界面后推出界面,在终端执行make -j4即可对整个u-boot进行编译。

1
make -j4

编译u-boot

make -j4后面的-j4表示4个核心进行编译,若电脑的处理器是2核心,请使用make -j2进行编译。

编译完成后会在当前目录生成u-boot-sunxi-with-spl.bin烧录文件。

根目录下找到 u-boot-sunxi-with-spl.bin 文件

该文件就是我们最终要烧录的二进制文件。

在当前目录下会有一个隐藏的文件.config,该文件是u-boot编译后根据各个选项产生的配置文件,这个配置文件记录了所有配置选项的宏开关,编译的时候是根据最终的.config文件来进行编译的,当然编译前是需要有脚本解析.config文件然后进行相应的编译。

烧录到TF卡

只要将u-boot-sunxi-with-spl.bin烧录到tf卡的8k偏移处地址就可以了,烧录步骤如下:使用dd命令进行块搬移:

1
sudo dd if=u-boot-sunxi-with-spl.bin of=/dev/sdb bs=1024 seek=8

该命令中:

if 文件名:输入文件名,缺省为标准输入。即指定源文件。< if=input file >

of 文件名:输出文件名,缺省为标准输出。即指定目的文件。< of=output file >

bs bytes:同时设置读入/输出的块大小为bytes个字节。

seek blocks:从输出文件开头跳过blocks个块后再开始复制。

这里的输出文件(of)为主机电脑的/dev/sdb文件,也就是TF卡,这里也体现了Linux一切皆文件的思想。

/dev/sdb 这个可以用gparted 软件查看,该软件可以直接用命令安装即可:

1
sudo apt-get install gparted

此时在Ubuntu下面可以看到如下软件:

安装好GParted软件

打开软件

GParted

在右上角可以看到两个硬盘,/dev/sda 为本地硬盘,/dev/sdb 是我们将要写数据的TF(当然这只是墨云自己的配置使然,具体情况请根据实际情况而定),因此这里的of=/dev/sdb 烧录到8k偏移地址处是指绝对地址,这个绝对地址指的是TF卡的物理地址。这8K的值是由F1C200S 中固化的启动代码决定的,所以照抄即可。

烧写u-boot

然后我们正常退出TF卡,然后插入我们自制的开发板,通过USB线连接电脑,

连接开发板

打开电脑中的命令行工具,我这里使用Xshell,

打开Xshell,新建连接:

配置名称 ,协议选择Serial,

配置串口

通过下拉选中com端口,波特率为115200,其他默认即可,点击确定,然后双击主界面左侧会话管理中的刚建立的会话,此时进入连接状态。

因为在你插入USB通电的时候开发板就已经启动了,所以当你打开串口连接的时候可能未必会看到信息,所以按一下重启键,就可以看到如下的输出信息了,这就是我们的u-boot,执行到u-bbot计数完成后会产生错误,那是因为我们还没有进行系统内核的移植,所以默认就会进入u-boot命令模式。

启动信息

输入pri命令打印环境变量的所有值,可以找到已经配置的bootcmd 和bootargs

pri命令结果

至此完成了u-boot移植的全部内容,对于u-boot的移植方法,在后续移植Linux内核和文件系统时都会用到,都是大同小异的,所以有了本篇的说明,之后操作将会非常简单。

而关于u-boot的内容事实上非常的复杂繁琐,有兴趣的可以自行去了解到,毕竟作为一个小白的我初衷只是先让小板先跑起来。

#

参考资料#

Lite200 (lishanwen) – https://lishanwen.cn/index.php/2021/07/03/lite200/

全志F1C200S F1C100S 介绍 ( 迪卡魏曼依奇君 ) https://blog.csdn.net/tunqimai9331/article/details/95938903

荔枝派Nano 全流程指南 (矽速科技) https://wiki.sipeed.com/soft/Lichee/zh/Nano-Doc-Backup/index.html

三. Linux内核与文件系统移植

1.Linux内核#

事实上对于F1C100S/F1C200S,Linux官方源码已经对licheepi nano进行支持。所以我们完全可以通过licheepi nano的配置文件进行移植。

1.1. 下载内核源码#

进入Linux系统官网:

https://www.kernel.org/

这里面列出的都是一些主要版本,如主线版本,上时间支持版本,个人推荐使用最新的长时间支持版本(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文件:

修改如下配置:

1
2
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根目录,输入命令:

1
make menuconfig

进入图形配置界面,如图所示:

该界面和u-boot配置一样,所以操作方式也是一样的,上下键移动选项,使用空格键进行选中或取消选择,同样通过空格键或回车键,进入子选项配置,通过Tab键选择保存和退出即可返回上级菜单或命令行界面,也可以直接双击Esc键返回上级目录。

1.3 配置TF卡设备树信息#

我们在完成内核配置后还需要配置TF卡的设备树配置,否则即便是能正常运行内核,在加载文件系统的时候还是会有问题,在这里配置很简单:

linux-5.7.1/arch/arm/boot/dts 目录下,分别修改suniv-f1c100s.dtsisuniv-f1c100s-licheepi-nano.dts 两个文件(记住这两个文件、以后我们修改的地方多了^_^)

修改suniv-f1c100s.dtsi文件

首先添加头文件(如果有则忽略)

1
2
#include <dt-bindings/clock/suniv-ccu-f1c100s.h>
#include <dt-bindings/reset/suniv-ccu-f1c100s.h>

在soc->pio 下添加如下代码

1
2
3
4
mmc0_pins: mmc0-pins {
pins = "PF0", "PF1", "PF2", "PF3", "PF4", "PF5";
function = "mmc0";
};

soc下添加如下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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文件,在根节点添加如下代码

1
2
3
4
5
6
reg_vcc3v3: vcc3v3 {
compatible = "regulator-fixed";
regulator-name = "vcc3v3";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
};

外部添加mmc0使能代码

1
2
3
4
5
6
&mmc0 {
vmmc-supply = <&reg_vcc3v3>;
bus-width = <4>;
broken-cd;
status = "okay";
};

位置如下图

接下来执行make命令开始编译内核和设备树相关的文件了

1
make

首次进行编译,通常会需要很长时间,编译完成后,就会在在arch/arm/boot目录下生成内核文件:zImage,在arch/arm/boot/dts目录下设备树文件:suniv-f1c100s-licheepi-nano.dtb

在编译过程中,因为所配置Ubuntu系统的差异,可能会因缺少某些组件导致编译报错,不要慌,将对应的错误关键信息复制到搜索引擎后安装即可,一下是作者碰到的两个编译报错,如果有必要可以提前安装:

linux-内核编译配置 lexer.lex.c错误

1
2
3
4
5
6
7
8
9
10
11
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

解决方法:

1
2
sudo apt-get install bison
sudo apt-get install flex

编译Linux内核时遇到:“error : openssl/bio.h :No such file or folder”

1
2
3
4
5
6
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:

1
sudo apt install libssl-dev

1.4 TF分区配置#

在上一篇中提到过u-boot 中的bootcmd 配置了Linux内核文件和设备树文件存放位置,即TF卡的0:1分区中,而且我们已经得到了对应的文件,那我们应该怎么操作呢。

还记得我们在上一篇中安装的Gparted软件吗,如果不记得,可以通过以下命令安装:

1
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启动完成后,自动进入了内核启动环节,但是启动后一会就报错了,因为挂载文件系统产生错误。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
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系统中,然后解压并进入源码目录中,输入清理命令。主要用于初始化一些设置,命令如下:

1
make clean

然后输入以下命令进入配置界面

1
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键】选择****进行保存,按【Esc键】回到上一级配置界面。

2.2 Build options配置#

进入第二个Build options选项,配置如图

按T【ab键】选择****进行保存,按【Esc键】回到上一级配置界面。

2.3 Toolchain配置#

进入第三个Toolchain选项,配置如图:

这里我们选择一些C\C++相关的库,这样我们就可以在开发板上直接编译程序了,保存返回。

2.4 System configuration配置[#](https://www.cnblogs.com/twzy/p/15355842.html#2.4 system-configuration配置)

对于System configuration选项,这里主要是配置一些系统登录时候显示的内容,配置如图

这里主要配置了登录时候显示的内容和root账号登录密码,接下来保存配置并且退回到命令行界面。

然后执行构建文件系统命令:

1
make

因为是首次编译,而且buildroot在制作文件系统的时候需要联网获取组件,所以会编译很久,那么“去和妲己玩耍吧”

当你终于被别人坑的自闭的时候,文件系统大概也许可能已经编译完毕了。

2.5 文件系统移植与执行#

此时在源码的output/images目录下有一个rootfs.tar,这个文件就是最终生成的根文件系统镜像,现在只需要将该镜像解压到TF卡的第二分区即可。插入TF卡到电脑端,进入out/images目录,然后输入

1
2
3
# 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 升级逼格[#](https://www.cnblogs.com/twzy/p/15355842.html#2.6 升级逼格)

我们发现登录进自制的Linux系统后,命令行前置无论怎样只显示一个**#**号,逼格略低呀,怎么处理呢?

修改/etc/profile文件

1
vi /etc/profile

写入

1
export PS1='[\u@\h: \w\a\]$'

重启小板,就可以看到与与常规Linux一样的操作体验了,只是root账号的时候还是显示 $ 符号

需要注意的是,在开发板运行过程中,如果想要重启,请先执行

1
poweroff

命令正常关闭系统后,在按重启按钮,否则有很大概率回造成文件系统损坏。

3. 点个灯吧#

还记得我们在第一篇中提到过的我们自制小开发板的唯一的那个外设——LED灯吗?

那我们就利用Linux提供的GPIO系统通过shell命令进行点灯实验吧。

我们首先需要回到文件系统制作菜单

1
2
3
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
2
3
4
5
6
7
8
9
10
11
12
# 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

输入如下命令:

1
2
3
4
5
6
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灵魂点灯是要通过驱动方式来实现的,但是谁让我是小白呢,以后再说吧。


相关链接(侵删)

  1. 小白自制Linux开发板(F1C200s)整理系列,持续更新中
  2. 瞎抄原理图与乱画PCB
  3. 二. u-boot移植
  4. 三. Linux内核与文件系统移植

=================我是分割线=================

欢迎到公众号来唠嗑: