2、固件基础概念:什么是固件、固件与软件的区别、MTK固件的典型架构

好,咱们正式开始聊固件。很多刚入行的朋友问我:“固件到底是个啥?跟软件有啥区别?” 嗯,这个问题问得好。我当年刚接触嵌入式时,也傻傻分不清。说白了,固件就是“固化在硬件里的软件”。它不像你电脑上装的QQ、微信,想删就删,想装就装。固件是焊死在Flash里的,是硬件和上层应用之间的“翻译官”。

2.1 什么是固件?

我个人习惯把固件理解为“硬件的灵魂”。没有固件,芯片就是一块死气沉沉的硅片。固件负责初始化硬件、管理内存、调度任务,最终把硬件能力暴露给上层操作系统。

举个例子。你手机按下电源键,屏幕亮了——这背后就是固件在干活。它先让CPU跑起来,再初始化内存,然后加载内核。每一步都离不开固件。

核心定义:固件(Firmware)是存储在只读存储器(ROM、Flash)中的程序,它直接操作硬件寄存器,是硬件与操作系统之间的桥梁。

我在项目中遇到过一件事。有次客户说设备启动不了,查了半天,发现是Flash里的固件被意外擦除了。你看,没有固件,硬件就是一堆废铁。

2.2 固件与软件的区别

这个问题,我经常在面试时问候选人。很多人答不上来。其实区别很明显,我列个表你就懂了。

对比维度 固件 软件
存储位置 ROM、Flash、EEPROM 硬盘、SSD、内存
更新频率 极低(甚至终身不更新) 频繁(随时可升级)
与硬件关系 直接操作寄存器、中断 通过操作系统API间接访问
开发语言 C、汇编为主 C++、Java、Python等
调试难度 高(依赖JTAG、串口) 低(IDE断点、日志)
典型大小 几十KB到几MB 几百MB到几十GB

你想想看,固件出问题,设备直接变砖。软件出问题,最多重启一下。这就是为什么固件开发要格外小心。我曾经因为一个GPIO初始化顺序写反了,导致整批设备无法进入下载模式。嗯,那教训够我记一辈子。

我的经验:判断一个程序是固件还是软件,最简单的方法——看它能不能直接操作硬件寄存器。能,就是固件;不能,就是软件。

2.3 MTK固件的典型架构

好了,咱们聚焦到MTK平台。MTK的固件架构,说白了就是“四层结构”:Preloader → LK → Kernel → RootFS。每一层都有明确分工,缺一不可。

2.3.1 Preloader(启动加载器)

Preloader是芯片上电后第一个执行的程序。它小得可怜,通常只有几十KB。但你别小看它,它干的事可不少:

  • 初始化最基本的硬件(时钟、PLL、DDR控制器)
  • 检测启动模式(正常启动、下载模式、恢复模式)
  • 验证并加载下一阶段(LK)的镜像

我记得有一次,客户反馈设备偶尔无法开机。查了半个月,最后发现是Preloader里DDR时序配置参数跟实际硬件有偏差。温度一高,DDR初始化就失败。你看,几十KB的代码,能让你折腾半个月。

避坑指南:Preloader阶段不要做复杂操作。我曾经试图在Preloader里加日志打印,结果导致启动时间超了200ms,触发了硬件看门狗复位。Preloader的原则是:能少做就少做,越快越好。

2.3.2 LK(Little Kernel)

LK是MTK平台的小型内核,它接在Preloader后面跑。LK的主要职责是:

  • 初始化更复杂的硬件(显示、触摸、存储)
  • 显示开机Logo(你看到的品牌Logo就是这步)
  • 验证并加载真正的Linux Kernel

为什么要有LK?直接加载Kernel不行吗?嗯,这里有个历史原因。早期MTK平台没有LK,Preloader直接加载Kernel。但后来发现,Preloader太小,做不了复杂校验。加个LK,相当于多了一层安全把关。

我建议你在调试LK时,多用串口输出。LK阶段是排查启动问题的黄金窗口。Preloader太早,Kernel又太复杂。LK刚刚好。

2.3.3 Kernel(Linux内核)

到了Kernel这一步,才算是真正进入了操作系统。MTK使用的是标准Linux内核,但做了大量定制:

  • 电源管理(MTK的PMIC驱动)
  • 多媒体框架(Mediatek的Codec、ISP)
  • 通信协议栈(Modem、WiFi、蓝牙)

Kernel的启动过程,说白了就是“设备驱动排队初始化”。每个驱动都注册自己的probe函数,内核按顺序调用。我遇到过最头疼的问题,就是两个驱动因为资源竞争导致死锁。查了三天,最后发现是I2C总线被两个驱动同时访问了。

关键点:MTK的Kernel启动参数里,通常会指定console=ttyMT0(串口终端)和root=/dev/ram0(根文件系统在内存中)。这两个参数搞错了,系统就起不来。

2.3.4 RootFS(根文件系统)

RootFS是固件的最后一层。它包含了系统运行所需的所有文件:

  • init进程(系统第一个用户态进程)
  • 系统库(libc、libm等)
  • 配置文件(init.rc、fstab等)
  • 应用程序(拨号、短信、设置等)

MTK的RootFS通常采用SquashFS格式。为什么?因为它是只读的,压缩率高,适合嵌入式设备。但要注意,SquashFS是只读的,所以系统运行时需要挂载一个可写的分区(比如data分区)来存用户数据。

我曾经犯过一个低级错误——在RootFS里放了一个超大日志文件,导致系统启动时挂载失败。嗯,从那以后我养成了习惯:RootFS里只放必要文件,多余的一个都不要。

2.4 四层架构的启动流程

咱们把四层串起来,看看完整的启动流程:

  1. 上电 → CPU从ROM执行固化代码,跳转到Preloader
  2. Preloader → 初始化DDR,检测启动模式,加载LK到内存
  3. LK → 初始化显示和存储,显示Logo,加载Kernel
  4. Kernel → 初始化所有驱动,挂载RootFS,启动init进程
  5. RootFS → init启动系统服务,最终进入桌面或应用

整个过程,从按下电源键到看到桌面,通常控制在10秒以内。如果超过15秒,用户就会觉得“这手机好慢”。

调试技巧:如果设备启动卡住了,先看串口日志。Preloader阶段输出“BROM”开头的信息,LK阶段输出“LK”开头,Kernel阶段输出“Linux version”。看日志停在哪个阶段,问题就定位到哪一层。

好了,固件基础概念就聊到这儿。下一章咱们深入讲讲MTK的固件分区布局,以及每个分区里到底放了什么。到时候我会分享一个我亲手搞砸的分区表案例——嗯,那故事可有意思了。