1 固件基本组成1.1 硬件系统结构 嵌入式硬件基本组成包括:主控芯片(MCU),FLASH,RAM,外设等,我们提供的开发板MCU型号是RK3308,64MB的RAM,外挂一颗128M的SPI-Nand-FLASH。 RAM:也称内存,掉电数据不保存。 FLASH:非易失性存储器,掉电数据不会丢失,从总线访问的方式进行区分,FLASH可以分为SPI(串行)和CFI(也叫JEDEC或者Parallel,并行);从存储类型可以分为Nor(价格较高,存储空间一般比较小,但它可以不用初始化,可以在其内部运行程序,一般在其存储一些初始化内存的固件代码)和Nand(价格便宜,容量较大,改写速度快,适合用于大量的数据存储)。 外设:比如USB口,WiFI主控芯片,RGB灯,按键等都属于外设的范畴。 1.2 分区和我们电脑的硬盘一样,Flash也可以看成是一个硬盘,可以进行分区;NandFlash的分区需要注意,需要按照单位是sector进行分区,1个sector为512Bytes;同时为了达到最好的性能,每个分区需要和64个sectors进行对齐也就是32KB的整数倍。 比如1MB的分区的大小是: 1024*1024B / 512 = 2048 = 0x800 Sectors。 4MB分区大小:0x800 * 4 = 0x2000 Sectors。 1.3 软件结构一个完整的嵌入式产品的软件一般包括如下组成:
U-Boot(Bootloader), 芯片原厂要求的引导程序, Kernel(比如Linux), Rootfs(根文件系统,一般是只读文件系统), Userdata(放我们开发的应用程序和用户数据的地方,可以挂一个可读写的文件系统,比如UBIFS,Yaffs,安卓等,有机会会出一个篇幅专门讲嵌入式安卓系统的移植和应用); 还有些产品出于OTA(在线升级)考虑会设置Recovery分区。 补充说明一下Rootfs,也叫根文件系统,一套linux体系,只有内核本身是不能工作的,必须要rootfs,也就是:
根文件系统中 /etc,/dev,/bin,/lib,这几个目录是不可或缺的。 结合上一节的内容,我们可以把Flash进行分区,分成针对这几个固件的区域,比如起始大小2MB用于U-Boot的存放,以此类推放置其他的固件;软件涉及的时候或者烧录时,U-Boot是知道Kernel的起始地址的,因此可以跳转过去运行,同样Kernel也知道在哪里挂载Rootfs。
2 内核设备树Linux内核驱动程序和芯片型号以及架构是有关系的,因此需要由厂商(比如ARM架构由Rockchip设计的芯片,RK3308之类)进行适配,由于厂商和型号随着时间推移越来越多,因此在Linux 3.x之前版本大量的冗余代码充斥在/arch/arm/plat-xxx和/arch/arm/mach-xxx目录,难以维护;Linux之父Linus Torvalds对此大骂“this whole ARM thing is a f*cking pain in the ass”;自此之后,Linux内核引入了设备树机制以描述计算机板机底层硬件信息。 2.1 结构源码文件由后缀:.dts 和 .dtsi 两种类型组成,.dtsi类似于C/C++中的.h文件,可以被.dts进行include。编译时,通过DTC(Device Tree Compiler),将.dts和.dtsi文件生成 .dtb二进制文件给机器设别;DTS由如下几部分组成: * 1个root结点"/" * root结点下面含一系列子结点 * 子结点下又含有一系列子结点 * 各结点都有一系列属性,属性类型有 : 空属性:empty-property 字符串属性:string-property 字符串列表属性:string-list-property Cells(u32整型)属性:cell-property 二进制数属性:binary-property DTS的源文件可以在kernel目录下找到:arch/arm64/boot/dts或者arch/arm/boot/dts;比如如下一个片段代码(根节点):
2.2 节点语法如下是一个完整的节点定义:
- 节点名称:每个节点必须有一个“<名称>[@<设备地址>]”形式的名字:
<名称> 就是一个不超过31位的简单 ascii 字符串,节点的命名应该根据它所体现的是什么样的设备。 2. <设备地址>用来访问该设备的主地址,并且该地址也在节点的 reg 属性中列出,同级节点命名必须是唯一的,但只要地址不同,多个节点也可以使用一样的通用名称,当然设备地址也是可选的,可以有也可以没有。 3. 树中每个表示一个设备的节点都需要一个 compatible 属性。 2.3 属性语法属性采用的是简单的键-值对,有如下两种格式: Property格式1(没有值) : [label:] property-name; Property格式2(键值对) : [label:] property-name = value;; 在设备树源文件中任有几个基本的数据表示形式: 文本字符串(无结束符)可以用双引号表示:string-property = "a string" Cells是 32 位无符号整数,用尖括号限定: cell-property = <0xbeef 123 0xabcd1234> 二进制数据用方括号限定:binary-property = [01 23 45 67]; 不同表示形式的数据可以使用逗号连在一起: mixed-property = "a string", [01 23 45 67], <0x12345678>; 逗号也可用于创建字符串列表:string-list = "red fish", "blue fish"; 指定了系统的名称,包含了一个“<制造商>,<型号>”形式的字符串。比如上图中的 compatible = "rockchip,rk3308-uart", "snps,dw-apb-uart"。 reg =<address1 length1 [address2 length2] ... > 上面的每一个元素都代表设备的寻址地址及其寻址大小,每一个元素中的address值可以是一个或者多个无符号32位整形数据类型cell来表示,元素中的length可以为空也可以使一个或者多个无符号32位整形数据类型cell。 由于每个可寻址设备都会有reg属性可设置,而且reg属性元素也是灵活可选择的,那么谁来制定reg属性元素中每个元素也就是address和length的个数呢?在这里,要关注到期父节点的两个属性,其中#address-cells表示reg中address元素的个数,#size-cells用来表示length元素的个数。 - #address-cells和#size-cells属性
上图表示address元素个数为2,length元素个数也为2。 interrupt-parent - 设备结点透过它来指定它所依附的中断控制器的phandle,当结点没有指定interrupt-parent时,则从父级结点继承。 interrupt-controller - 一个空的属性定义该节点作为一个接收中断信号的设备。(见下图)
#interrupt-cells - 表明连接此中断控制器设备的interrupts属性的cell个数。 interrupts - 一个中断指示符的列表,对应于该设备上的每个中断输出信号,在ARM GIC中:当#interrupt-cells为3时,interrupts包含三个cells。 GIC_PPI代表中断类型,一般有两种: PPI中断:私有外设中断(Private Peripheral Interrupt),是每个CPU私有的中断。最多支持16个PPI中断,范围【0 - 15】。 SPI中断类型:公用外设中断(Shared Peripheral Interrupt),最多可以支持988个外设中断,范围【0 - 987】。 interrupts中第二个cell值为9其实就是指PPI中断类型的第9个中断。 IRQ_TYPE_LEVEL_HIGH代表中断触发标志,常见的有: 1 = 低- 至- 高边沿触发; 2 = 高- 到- 低边沿触发 4 = 活跃的高水平- 敏感; 8 = 低电平有效- 敏感 图中是高电平触发。 ========================================================================================================================== 如果觉得对您有帮助并想进一步深入学习交流可以扫描以下微信二维码或加入QQ群:928840648
欢迎共同学习成长,有一群爱学习的小伙伴一起勉励!!加油!!也可点击
笔者基于嵌入式系统框架内容如下整理编辑:
|