当你拿到一台全新出厂的未激活笔记本、刚从华强北淘到的“主板+CPU+内存条+电源”四件套、或者在开发者社区看到的“RISC-V裸机开发入门教程”时,大概率会频繁接触到“裸机”这个词,很多人对它的第一印象停留在“没有操作系统的电脑/手机”,但实际上,“裸机”的定义在消费者端、计算机硬件维修/DIY端、嵌入式/底层系统开发端有着截然不同的内涵边界;它既可以是一台无法直接运行QQ的硬件空壳,也可以是开发者手中可以精准控制每一个引脚电平的高效开发平台;甚至在智能手机领域,还存在“预装系统但无任何第三方软件的半裸机(纯净版系统设备)”和“彻底没有系统引导分区的全裸机”之分。
本文将从裸机的三重核心定义切入,结合BIOS点亮屏幕的PC裸机初始化过程、树莓派4B裸机点亮LED灯的具体代码与操作、小米10 Ultra从官方MIUI到全裸机(清空所有分区)再到刷入LineageOS原生纯净系统的完整流程三个跨领域的计算机实例,详细拆解裸机的底层逻辑、应用场景、以及它对整个信息技术产业的底层支撑意义;最后我会结合自己在嵌入式开发和PC硬件领域的10年经验,发表对“裸机开发未来趋势”和“消费电子领域‘裸机风潮’回归必要性”的个人观点,全文预计3500字,适合计算机入门爱好者、DIY硬件玩家、嵌入式开发初学者阅读。
先搞懂裸机的三重身份:跨领域的内涵差异
在开始具体的实例解析前,我们必须先明确:“裸机”从来不是一个单一、绝对的术语,它的边界完全取决于使用者的身份和使用场景的需求——这一点很多入门教程都会忽略,导致读者在不同场景下看到“裸机”时产生认知混乱,以下是我整理的、目前信息技术产业中最主流的三重定义:
1 消费者端的“伪裸机”与“半裸机”:从营销话术到用户需求
伪裸机:品牌商常玩的营销陷阱
很多智能手机、笔记本电脑品牌商在宣传新机型时,会打出“出厂自带纯净版系统,接近裸机体验”的口号——这里的“裸机体验”其实是完全误导消费者的伪裸机概念。
所谓的“纯净版系统”,本质上只是预装了少量甚至没有第三方合作APP的官方定制系统,它依然包含完整的操作系统内核(如Windows 11 Home的NT内核、iOS 17的XNU内核)、系统底层驱动、厂商自研的应用框架(如小米的MIUI Framework、华为的HarmonyOS NEXT的分布式软总线)、甚至还有无法删除的系统内置APP(如Windows的Edge浏览器、苹果的Safari浏览器、小米的小米钱包/小米商城APP——这些APP在普通消费者端无法通过正常途径卸载,但在开发者模式或Root权限下可以操作)。
半裸机:部分用户追求的“轻量使用设备”
与伪裸机相对的是部分DIY硬件玩家、商务人士、程序员追求的半裸机——它的定义是“只安装了必要的操作系统和底层驱动,没有安装任何官方定制系统之外的第三方APP、甚至没有激活系统自带的部分非必要功能”的设备。
举个我自己的例子:我用来做嵌入式开发的工作笔记本是2022款MacBook Pro 14寸 M2 Pro,刚拿到手时我做的第一件事不是激活FaceTime、iCloud这些苹果云服务,而是进入“系统设置-通用-存储空间-管理-系统数据-开发者工具”手动关闭了Xcode之外的所有iOS/MacOS模拟器、关闭了Spotlight的“ Siri建议与搜索”(用Alfred替代)、卸载了所有自带的非开发类APP(如Safari浏览器换成Chrome、Pages换成Typora+Gitbook、Numbers换成Google Sheets、Keynote换成PPT)——这台MacBook Pro在我眼中就是一台“半裸机嵌入式开发专用机”,它的启动速度比刚出厂的满装状态快了30%左右,电池续航也提升了15%左右(这是Alfred的效率和关闭非必要后台服务带来的双重效果)。
2 计算机硬件维修/DIY端的“全裸机硬件空壳”:最原始的硬件集合
这是“裸机”最早期、最原始的定义——它指的是没有安装任何操作系统、甚至没有安装任何存储介质(如硬盘、U盘、SD卡)的硬件集合体;如果是台式PC的话,它的组成一般是“主板+CPU+CPU散热器+内存条+电源+显示器连接线”(显示器本身不算裸机的一部分,它只是输出设备);如果是嵌入式开发板的话,它的组成一般是“开发板本体(包含CPU、RAM、ROM闪存、GPIO引脚、电源接口、USB接口等)”(ROM闪存里可能有出厂自带的Bootloader,但如果清空ROM的话,那就是彻底的嵌入式全裸机)。
为什么台式PC全裸机需要这些硬件才能点亮?
很多入门DIY玩家可能会问:“我能不能只拿主板+CPU+电源就能点亮屏幕?”答案是不能——因为CPU本身没有“记忆”功能,它的启动需要从“非易失性存储介质”(如早期的BIOS ROM芯片、现在的UEFI SPI Flash芯片)中读取基本输入输出系统(BIOS) 或统一可扩展固件接口(UEFI) ,而BIOS/UEFI的运行需要依赖随机存取存储器(RAM,也就是内存条) 作为临时的工作空间——如果没有内存条,CPU就无法加载BIOS/UEFI,屏幕自然不会亮(这也是很多入门DIY玩家第一次装机时遇到“点不亮屏幕”问题的最常见原因)。
3 嵌入式/底层系统开发端的“裸机平台”:没有操作系统内核的开发环境
这是“裸机”在信息技术产业中最核心、最有技术含量的定义——它指的是开发者在没有任何操作系统内核(如Linux、FreeRTOS、RT-Thread、Zephyr)支持的情况下,直接对硬件寄存器进行操作的开发平台;在这个平台上,开发者的代码就是“唯一的软件”,它会直接在CPU的指令集上运行,没有任何中间层的干扰(这意味着代码的执行效率极高,但也意味着开发者需要自己处理所有的硬件初始化、中断响应、内存管理、外设控制等底层工作)。
举个简单的例子:有OS vs 无OS(裸机)点亮LED灯的代码复杂度差异
假设我们要在STM32F103C8T6最小系统板(这是嵌入式开发领域最常用的入门级裸机/RTOS开发板之一)上点亮一个连接在PC13引脚的LED灯:
如果用RT-Thread实时操作系统开发,只需要几行代码就能完成(因为RT-Thread已经帮我们封装好了GPIO引脚的初始化、控制等底层函数);
如果用裸机开发,则需要手动配置STM32F103C8T6的RCC时钟控制器、GPIOx端口的配置寄存器(CRH/CRL)、输出数据寄存器(ODR)等至少3个硬件寄存器,代码行数会超过30行(如果要做到“精准延时闪烁”,还需要手动配置SysTick定时器或TIMx通用定时器,代码行数会进一步增加)。
我会在本文的第三部分“裸机开发的具体实例:树莓派4B裸机点亮LED灯” 中,详细展示裸机开发的代码和操作步骤——这部分内容虽然有一定的技术门槛,但对于理解“裸机的底层逻辑”非常重要。
PC裸机的最基础操作:从BIOS点亮屏幕到进入DOS系统
在讲完裸机的三重定义后,我们先从最贴近普通用户的PC硬件维修/DIY端全裸机入手,通过一个具体的实例来理解“裸机是如何从硬件空壳变成可以输入指令的设备的”。
1 实例一:2023款联想拯救者Y9000P 2023全裸机点亮屏幕并进入DOS系统
实验背景
假设我是一名刚入职的PC硬件维修师,客户送来了一台“无法开机”的2023款联想拯救者Y9000P 2023游戏本——经过初步检测,我发现这台游戏本的SSD硬盘已经完全损坏(分区表丢失、ROM闪存颗粒报废),电池也已经完全放电;我现在需要做的第一件事是“清空所有可能的外部干扰,把它变成一台全裸机硬件空壳,然后验证主板、CPU、内存条、电源、屏幕是否正常”。
实验前的准备工作
硬件部分:
2023款联想拯救者Y9000P 2023游戏本的“核心硬件集合”:拆下来的主板(包含i9-13900HX CPU、RTX 4090 Laptop GPU、2条16GB DDR5 5600MHz内存条)、原装230W氮化镓电源适配器、原装Type-C转DP1.4a连接线、外接的27英寸4K 144Hz显示器(原装游戏本的屏幕排线我们也保留,作为备用输出)。
清空的外部干扰:完全拆下损坏的SSD硬盘、拆下原装电池、断开所有USB外设(如键盘、鼠标、U盘)。
软件部分(后续进入DOS系统需要):
一张8GB以上的空白U盘;
Rufus 4.4(这是目前最常用的U盘启动盘制作工具);
FreeDOS 1.3镜像文件(这是目前最流行的开源DOS系统,支持UEFI启动)。
实验步骤1:把核心硬件集合组装成“临时全裸机平台”并点亮屏幕
联想拯救者Y9000P 2023的主板设计比较特殊,它的电源接口、Type-C接口、DP接口都集成在一个“IO扩展板”上——我们需要先把IO扩展板用螺丝固定在主板的对应位置,然后插入原装230W氮化镓电源适配器,最后连接外接显示器。
我们需要用“金属镊子短接主板上的Power Switch(电源开关)引脚”来启动临时全裸机平台——联想拯救者Y9000P 2023的Power Switch引脚在主板靠近键盘的一侧,标注为“PWR_SW”,一般是两个相邻的金色引脚(不同批次的标注可能略有不同,建议先查官方的维修手册)。
短接Power Switch引脚后,我们会看到以下现象(如果核心硬件正常的话):
主板上的“电源指示灯”(一般是红色或绿色的小LED灯)会亮起;
CPU散热器的风扇会开始高速转动(几秒钟后会自动降速到正常转速);
外接显示器会首先显示“Lenovo LOGO”(大概持续2-3秒),然后会进入“UEFI BIOS设置界面”(因为没有安装任何存储介质,UEFI BIOS找不到可以启动的设备,所以会自动进入设置界面)。
如果能看到UEFI BIOS设置界面,就说明这台游戏本的主板、CPU、CPU散热器、内存条、电源、屏幕(或外接显示器)都是正常的——接下来我们就可以更换新的SSD硬盘,然后安装操作系统了。
实验步骤2:制作FreeDOS U盘启动盘并进入DOS系统
虽然UEFI BIOS设置界面已经可以验证核心硬件的正常性,但如果我们想要更深入地检测硬件(比如检测内存条的兼容性、检测新更换的SSD硬盘的读写速度),就需要进入一个可以输入指令的轻量级操作系统——FreeDOS就是最好的选择。
以下是制作FreeDOS U盘启动盘并进入DOS系统的具体步骤:
插入8GB以上的空白U盘到另一台正常的电脑上;
打开Rufus 4.4,在“设备”下拉菜单中选择我们插入的空白U盘;
在“引导类型选择”下拉菜单中选择“FreeDOS”;
其他设置保持默认(分区类型选择“GPT”,目标系统选择“UEFI(非CSM)”,文件系统选择“FAT32”,簇大小选择“默认”);
点击“开始”按钮,Rufus会自动格式化U盘并安装FreeDOS系统;
制作完成后,把FreeDOS U盘启动盘插入到我们的临时全裸机平台的USB接口上;
再次用金属镊子短接Power Switch引脚启动临时全裸机平台,在显示“Lenovo LOGO”的时候快速按下“F12”键(联想拯救者系列的快速启动菜单快捷键都是F12);
在快速启动菜单中选择我们的FreeDOS U盘启动盘(标注为“UEFI: [U盘品牌型号]”),然后按下回车键;
几秒钟后,我们就会进入FreeDOS系统的命令行界面,显示的提示符是“C:>”——这时候我们就可以输入FreeDOS的指令来检测硬件了,比如输入“mem”指令可以检测内存条的容量和兼容性,输入“format D:”指令可以格式化新更换的SSD硬盘(假设新更换的SSD硬盘被识别为D盘)。
嵌入式裸机开发的具体实例:树莓派4B裸机点亮LED灯
在讲完PC全裸机的基础操作后,我们再进入最有技术含量的嵌入式/底层系统开发端裸机平台,通过一个具体的实例来理解“裸机开发是如何直接对硬件寄存器进行操作的”。
1 实例二:树莓派4B裸机点亮连接在GPIO18引脚的LED灯
实验背景
树莓派4B是目前最流行的入门级单板计算机(SBC)之一——它既可以运行Linux、Android等完整的操作系统(作为“迷你PC”使用),也可以作为“裸机开发平台”使用(因为它的CPU架构是ARMv8-A,资料非常丰富)。
很多入门级嵌入式开发教程都会用“树莓派4B运行Raspbian Linux系统点亮LED灯”作为第一个实验——但那个实验只是调用了Raspbian Linux系统的GPIO库(如wiringPi、RPi.GPIO),并没有真正接触到硬件的底层逻辑;今天我们要做的是“树莓派4B裸机点亮LED灯”,直接对Broadcom BCM2711 SoC的硬件寄存器进行操作。
实验前的准备工作
硬件部分:
树莓派4B单板计算机(任何版本都可以,我用的是8GB RAM版本);
树莓派4B官方电源适配器(5V 3A USB-C);
一张8GB以上的空白Micro SD卡;
一个红色LED灯;
一个220Ω的限流电阻(LED灯的工作电流一般是10-20mA,树莓派4B的GPIO引脚输出电压是3.3V,所以需要220Ω的限流电阻来保护LED灯和GPIO引脚);
两根杜邦线(公对母);
面包板(可选,用来方便地连接LED灯、限流电阻和杜邦线)。
软件部分(需要在另一台正常的电脑上安装,我用的是MacOS Ventura 13.6.7,Windows和Linux的安装步骤类似):
ARM GCC交叉编译工具链(用来把我们写的C语言裸机代码编译成树莓派4B可以识别的ARMv8-A机器码);
GNU Make(用来自动化编译过程);
balenaEtcher(用来把我们编译好的裸机镜像文件烧录到Micro SD卡上);
一个文本编辑器(比如Visual Studio Code、Sublime Text,我用的是Visual Studio Code)。
实验步骤1:硬件连接
我们需要把LED灯、限流电阻和树莓派4B的GPIO引脚连接起来——树莓派4B的GPIO引脚编号有两种:物理引脚编号(从左上角开始数,1、2、3...40)和BCM编号(Broadcom公司给BCM2711 SoC的GPIO引脚定义的编号);我们在裸机开发中必须使用BCM编号,因为硬件寄存器的操作是基于BCM编号的。
以下是具体的硬件连接方式:
把红色LED灯的长脚(阳极) 连接到220Ω限流电阻的一端;
把220Ω限流电阻的另一端连接到树莓派4B的BCM18引脚(物理引脚编号是12);
把红色LED灯的短脚(阴极) 连接到树莓派4B的GND引脚(物理引脚编号是14)。
硬件连接完成后,我们可以先检查一下连接是否正确——如果有条件的话,可以用万用表测量BCM18引脚和GND引脚之间的电压(树莓派4B未启动时,电压应该是0V)。
实验步骤2:安装必要的软件工具
(1)安装ARM GCC交叉编译工具链和GNU Make
MacOS用户:可以用Homebrew安装,打开终端输入以下指令:/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" # 如果已经安装了Homebrew,可以跳过这一步
brew install arm-none-eabi-gcc make
Windows用户:可以从ARM官方网站下载GNU Arm Embedded Toolchain(https://developer.arm.com/downloads/-/gnu-arm-embedded-toolchain),然后安装到默认路径(C:\Program Files (x86)\GNU Arm Embedded Toolchain\10 2021.10);GNU Make可以从MinGW-w64官网下载(https://www.mingw-w64.org/),或者直接安装Visual Studio的“C++桌面开发” workload(里面包含了GNU Make)。
Linux用户:可以用系统自带的包管理器安装,打开终端输入以下指令:# Ubuntu/Debian用户
sudo apt update
sudo apt install gcc-arm-none-eabi make
# CentOS/RHEL用户
sudo yum install gcc-arm-none-eabi make
(2)安装balenaEtcher
可以从balenaEtcher官方网站下载(https://www.balena.io/etcher/),然后安装到默认路径即可。
实验步骤3:编写裸机代码
树莓派4B的裸机代码需要包含三个部分:
启动汇编代码(start.S):用来初始化CPU的栈指针、设置CPU的异常向量表、然后跳转到C语言的main函数;
硬件寄存器定义头文件(gpio.h):用来定义BCM2711 SoC的GPIO相关硬件寄存器的地址和位掩码;
主程序代码(main.c):用来初始化GPIO18引脚为输出模式、然后循环点亮和熄灭LED灯。
以下是三个部分的具体代码:
(1)启动汇编代码(start.S)
/* 启动汇编代码:start.S
* 功能:初始化CPU栈指针、设置异常向量表、跳转到C语言main函数
* 架构:ARMv8-A (AArch64)
*/
/* 定义栈顶地址:树莓派4B的RAM起始地址是0x00000000,我们把栈顶设置在0x80000(512KB)的位置 */
.equ STACK_TOP, 0x80000
/* 定义代码段入口:树莓派4B的裸机代码必须从0x80000的位置开始执行 */
.section .text.boot
.global _start
_start:
/* 1. 初始化所有CPU核心的栈指针:树莓派4B有4个CPU核心,我们只使用核心0,其他核心进入无限循环 */
mrs x0, mpidr_el1 /* 读取MPIDR_EL1寄存器,获取当前CPU核心的编号 */
and x0, x0, #0xFF /* 只保留MPIDR_EL1寄存器的最低8位(Aff0),也就是核心编号 */
cbz x0, core0_init /* 如果核心编号是0,跳转到core0_init标签 */
core_hang: /* 其他核心进入无限循环 */
b core_hang
core0_init:
/* 2. 初始化核心0的栈指针:把栈顶地址设置到SP_EL1寄存器 */
ldr x1, =STACK_TOP
mov sp, x1
/* 3. 跳转到C语言的main函数 */
bl main
/* 4. 如果main函数返回,进入无限循环 */
b core_hang
(2)硬件寄存器定义头文件(gpio.h)
/* 硬件寄存器定义头文件:gpio.h
* 功能:定义BCM2711 SoC的GPIO相关硬件寄存器的地址和位掩码
* 参考资料:BCM2711 ARM Peripherals Documentation(https://datasheets.raspberrypi.com/bcm2711/bcm2711-peripherals.pdf)
*/
#ifndef GPIO_H
#define GPIO_H
/* BCM2711 SoC的外设寄存器起始地址:树莓派4B启动后,外设寄存器会被映射到0xFE000000的位置 */
#define PERIPHERAL_BASE 0xFE000000
/* GPIO寄存器的偏移地址:从PERIPHERAL_BASE加上这个偏移地址就是GPIO寄存器的实际地址 */
#define GPIO_BASE (PERIPHERAL_BASE + 0x200000)
/* GPIO功能选择寄存器(GPFSELn)的偏移地址:
* 每个GPFSELn寄存器可以控制10个GPIO引脚的功能(输入、输出、或者其他复用功能)
* GPFSEL0控制GPIO0-GPIO9,GPFSEL1控制GPIO10-GPIO19,以此类推
*/
#define GPFSEL1 (GPIO_BASE + 0x04) /* 我们要控制的GPIO18在GPFSEL1寄存器中 */
/* GPIO输出置位寄存器(GPSETn)的偏移地址:
* 向GPSETn寄存器的某一位写1,可以把对应的GPIO引脚置为高电平
* 写0没有任何效果
*/
#define GPSET0 (GPIO_BASE + 0x1C)
/* GPIO输出清零寄存器(GPCLRn)的偏移地址:
* 向GPCLRn寄存器的某一位写1,可以把对应的GPIO引脚置为低电平
* 写0没有任何效果
*/
#define GPCLR0 (GPIO_BASE + 0x28)
/* GPIO18引脚的位掩码:
* 我们要控制的GPIO18在GPFSEL1寄存器的第24-26位(每个引脚占3位)
* 在GPSET0和GPCLR0寄存器的第18位(每个引脚占1位)
*/
#define GPIO18_FUNC_MASK (0x7 << 24) /* 清零GPFSEL1寄存器的第24-26位 */
#define GPIO18_FUNC_OUT (0x1 << 24) /* 把GPFSEL1寄存器的第24-26位设置为001(输出模式) */
#define GPIO18_BIT (0x1 << 18) /* 控制GPSET0和GPCLR0寄存器的第18位 */
/* 软件延时函数的声明:在main.c中实现 */
void delay(unsigned int cycles);
/* 硬件寄存器的指针定义:
* 我们需要用volatile关键字来修饰这些指针,因为编译器不知道硬件寄存器的值会在什么时候变化
* 如果不用volatile关键字,编译器可能会优化掉一些对硬件寄存器的读写操作
*/
#define REG32(addr) (*(volatile unsigned int *)(addr))
#endif /* GPIO_H */
(3)主程序代码(main.c)
/* 主程序代码:main.c
* 功能:初始化GPIO18引脚为输出模式、然后循环点亮和熄灭LED灯
*/
#include "gpio.h"
/* 软件延时函数:
* 功能:通过循环执行空指令来实现延时
* 参数:cycles - 循环的次数(次数越多,延时越长)
* 注意:这是一个非常粗糙的软件延时函数,延时时间不准确;如果要实现精准延时,需要配置SysTick定时器或TIMx通用定时器
*/
void delay(unsigned int cycles)
{
while (cycles--)
{
asm volatile("nop"); /* 执行一条空指令,消耗一个CPU周期 */
}
}
/* C语言的main函数:
* 功能:程序的入口点
*/
int main(void)
{
/* 1. 初始化GPIO18引脚为输出模式 */
REG32(GPFSEL1) &= ~GPIO18_FUNC_MASK; /* 先清零GPFSEL1寄存器的第24-26位 */
REG32(GPFSEL1) |= GPIO18_FUNC_OUT; /* 再把GPFSEL1寄存器的第24-26位设置为001(输出模式) */
/* 2. 循环点亮和熄灭LED灯 */
while (1)
{
REG32(GPSET0) = GPIO18_BIT; /* 把GPIO18引脚置为高电平,点亮LED灯 */
delay(0x800000); /* 延时一段时间(大概1秒) */
REG32(GPCLR0) = GPIO18_BIT; /* 把GPIO18引脚置为低电平,熄灭LED灯 */
delay(0x800000); /* 再延时一段时间(大概1秒) */
}
/* 3. 程序永远不会到达这里 */
return 0;
}
实验步骤4:编写Makefile自动化编译过程
为了避免每次编译都要输入一大堆指令,我们可以编写一个Makefile来自动化编译过程——Makefile是GNU Make工具的配置文件,它可以定义“目标文件”、“依赖文件”和“编译指令”之间的关系。
以下是具体的Makefile代码:
# Makefile:自动化编译树莓派4B裸机代码
# 架构:ARMv8-A (AArch64)
# 定义交叉编译工具链的前缀
CROSS_COMPILE = aarch64-none-eabi-
# 定义编译器、汇编器、链接器、目标文件复制工具的名称
CC = $(CROSS_COMPILE)gcc
AS = $(CROSS_COMPILE)as
LD = $(CROSS_COMPILE)ld
OBJCOPY = $(CROSS_COMPILE)objcopy
# 定义编译选项
CFLAGS = -Wall -Wextra -O2 -ffreestanding -nostdlib -nostartfiles -march=armv8-a -mtune=cortex-a72
# -Wall -Wextra:开启所有警告
# -O2:开启二级优化
# -ffreestanding:告诉编译器这是一个独立的程序,不需要依赖标准库
# -nostdlib:不链接标准库
# -nostartfiles:不链接启动文件(我们自己写了start.S)
# -march=armv8-a:指定CPU架构为ARMv8-A
# -mtune=cortex-a72:指定CPU调优为Cortex-A72(树莓派4B的CPU核心是Cortex-A72)
# 定义链接选项
LDFLAGS = -T linker.ld -nostdlib -nostartfiles
# -T linker.ld:指定链接脚本为linker.ld
# 定义目标文件
OBJS = start.o main.o
# 定义最终的裸机镜像文件名称
TARGET = kernel8.img
# 默认目标:编译出kernel8.img
all: $(TARGET)
# 编译kernel8.img的规则:先链接出elf文件,再用objcopy转换成二进制镜像文件
$(TARGET): $(OBJS) linker.ld
$(LD) $(LDFLAGS) $(OBJS) -o kernel8.elf
$(OBJCOPY) -O binary kernel8.elf $(TARGET)
# 编译start.o的规则:把start.S汇编成start.o
start.o: start.S
$(AS) -o $@ $<
# 编译main.o的规则:把main.c编译成main.o
main.o: main.c gpio.h
$(CC) $(CFLAGS) -c -o $@ $<
# 清理目标:删除所有编译生成的文件
clean:
rm -f $(OBJS) kernel8.elf $(TARGET)
# 声明伪目标:这些目标不是文件,只是一些操作
.PHONY: all clean
实验步骤5:编写链接脚本(linker.ld)
链接脚本(linker.ld)是GNU LD链接器的配置文件,它可以定义“代码段”、“数据段”、“BSS段”等内存段的位置——树莓派4B的裸机代码必须把代码段放在0x80000的位置,否则无法启动。
以下是具体的链接脚本代码:
/* 链接脚本:linker.ld
* 功能:定义内存段的位置
* 架构:ARMv8-A (AArch64)
*/
/* 定义内存区域:树莓派4B的RAM起始地址是0x00000000,我们只使用前512KB(0x00000000-0x00080000) */
MEMORY
{
ram (rwx) : ORIGIN = 0x00000000, LENGTH = 0x00080000
}
/* 定义内存段的位置 */
SECTIONS
{
/* 代码段(.text):从0x80000的位置开始 */
.text 0x80000 : ALIGN(4)
{
*(.text.boot) /* 先放启动汇编代码的.text.boot段 */
*(.text) /* 再放其他代码的.text段 */
} > ram
/* 数据段(.data):紧跟在代码段后面 */
.data : ALIGN(4)
{
*(.data)
} > ram
/* BSS段(.bss):紧跟在数据段后面,初始化为0 */
.bss : ALIGN(4)
{
__bss_start = .;
*(.bss)
__bss_end = .;
} > ram
/* 堆段(.heap)和栈段(.stack):紧跟在BSS段后面(我们在start.S中已经设置了栈顶地址) */
}
实验步骤6:编译裸机代码并烧录到Micro SD卡上
现在我们已经完成了所有的代码编写工作,接下来需要编译裸机代码并烧录到Micro SD卡上:
打开终端,进入我们存放所有代码的文件夹(比如我存放在~/Desktop/RPi4B_Blink/文件夹下);
输入“make”指令,GNU Make会自动编译所有的代码,生成kernel8.img镜像文件;
输入“make clean”指令可以删除所有编译生成的文件(如果需要重新编译的话);
打开balenaEtcher,点击“Flash from file”按钮,选择我们刚刚生成的kernel8.img镜像文件;
点击“Select target”按钮,选择我们插入的空白Micro SD卡;
点击“Flash!”按钮,balenaEtcher会自动格式化Micro SD卡并烧录kernel8.img镜像文件;
烧录完成后,把Micro SD卡从另一台正常的电脑上拔下来,插入到树莓派4B的Micro SD卡插槽中;
把树莓派4B的官方电源适配器插入到USB-C接口中,启动树莓派4B——几秒钟后,我们就会看到连接在GPIO18引脚的LED灯开始循环点亮和熄灭(大概每秒一次)。
如果LED灯没有亮,我们可以检查以下几个方面:
硬件连接是否正确(LED灯的长脚和短脚是否接反了、BCM18引脚和GND引脚是否接对了、限流电阻是否接好了);
Micro SD卡是否烧录正确(有没有烧录成kernel7.img或者其他镜像文件、有没有格式化Micro SD卡为FAT32文件系统);
代码是否正确(有没有修改硬件寄存器的地址、有没有修改GPIO引脚的位掩码)。
消费电子领域的裸机延伸:小米10 Ultra从官方MIUI到全裸机再到刷入LineageOS原生纯净系统
在讲完PC全裸机和嵌入式裸机开发的实例后,我们再回到消费者端,通过一个具体的智能手机实例来理解“全裸机”和“半裸机(纯净版系统设备)”在消费电子领域的应用。
1 实例三:小米10 Ultra从官方MIUI到全裸机再到刷入LineageOS原生纯净系统
实验背景
我有一台2020年购买的小米10 Ultra 5G手机——这台手机的硬件配置非常强大(骁龙865 Plus CPU、12GB LPDDR5 RAM、512GB UFS 3.0 ROM、120倍潜望式长焦镜头),但官方MIUI系统(MIUI 14)的广告太多、内置APP太多、后台服务太多,导致手机的启动速度变慢、电池续航变短、甚至偶尔会出现卡顿的情况。
我现在想要做的是:清空小米10 Ultra的所有分区(变成彻底的全裸机),然后刷入LineageOS 20原生纯净系统(变成一台半裸机)——LineageOS是目前最流行的开源Android原生定制系统之一,它没有任何广告、内置APP非常少(只有电话、短信、相机、设置等基本APP)、后台服务非常少,所以运行速度非常快、电池续航也非常长。
实验前的准备工作
硬件部分:
小米10 Ultra 5G手机(必须已经解锁Bootloader,否则无法刷入第三方Recovery和第三方系统);
一根原装USB-C转USB-A数据线;
一台正常的电脑(Windows、MacOS、Linux都可以,我用的是Windows 11 Home)。
软件部分:
小米10 Ultra的官方USB驱动程序(https://www.miui.com/unlock/download.html);
ADB和Fastboot工具(可以从Android官方网站下载Platform Tools:https://developer.android.com/studio/releases/platform-tools);
小米10 Ultra的第三方Recovery(TWRP 3.7.0_12-0:https://twrp.me/xiaomi/xiaomimi10ultra.html);
小米10 Ultra的LineageOS 20原生纯净系统镜像文件(https://download.lineageos.org/cmi);
小米10 Ultra的Google Apps(MindTheGapps-13.0.0-arm64-20230808.zip:https://wiki.lineageos.org/gapps/,如果不需要Google服务的话可以跳过)。
实验步骤1:备份手机上的所有重要数据
在清空所有分区之前,我们必须先备份手机上的所有重要数据(比如照片、视频、联系人、短信、微信聊天记录等)——因为清空所有分区后,手机上的所有数据都会永久丢失,无法恢复。
备份数据的方法有很多种:
可以用小米官方的“小米云服务”备份(需要开通小米云会员,否则存储空间只有5GB);
可以用“微信电脑版”备份微信聊天记录;
可以用ADB工具备份整个手机的数据(适合高级用户);
可以把手机上的照片、视频等文件复制到电脑上。
实验步骤2:进入Fastboot模式并清空所有分区
备份完所有重要数据后,我们需要进入Fastboot模式并清空所有分区:
把小米10 Ultra手机关机;
同时按住“音量减键”和“电源键”,直到手机屏幕上显示“FASTBOOT”的兔子图标,然后松开手;
用原装USB-C转USB-A数据线把手机连接到电脑上;
在电脑上打开命令提示符(Windows)或终端(MacOS/Linux),进入存放ADB和Fastboot工具的文件夹(比如我存放在C:\platform-tools\文件夹下);
输入“fastboot devices”指令,检查电脑是否识别到了手机——如果显示了手机的序列号,就说明识别成功了;
输入“fastboot erase boot”指令,清空Boot分区;
输入“fastboot erase system”指令,清空System分区;
输入“fastboot erase vendor”指令,清空Vendor分区;
输入“fastboot erase product”指令,清空Product分区;
输入“fastboot erase system_ext”指令,清空System_ext分区;
输入“fastboot erase odm”指令,清空Odm分区;
输入“fastboot erase cache”指令,清空Cache分区;
输入“fastboot erase userdata”指令,清空Userdata分区(这是最重要的一步,会清空手机上的所有个人数据);
输入“fastboot erase metadata”指令,清空Metadata分区;
输入“fastboot erase dtbo”指令,清空Dtbo分区;
小米10 Ultra手机已经变成了彻底的全裸机——没有任何引导分区、没有任何系统分区、没有任何个人数据分区,无法启动,只能进入Fastboot模式或Recovery模式。
实验步骤3:刷入第三方Recovery(TWRP)并格式化Data分区
清空所有分区后,我们需要刷入第三方Recovery(TWRP)并格式化Data分区:
把下载好的TWRP 3.7.0_12-0镜像文件(twrp-3.7.0_12-0-cmi.img)复制到存放ADB和Fastboot工具的文件夹下;
在命令提示符或终端中输入“fastboot flash recovery twrp-3.7.0_12-0-cmi.img”指令,刷入TWRP Recovery;
刷入完成后,输入“fastboot reboot recovery”指令,重启手机进入TWRP Recovery模式;
第一次进入TWRP Recovery模式时,会显示“Unmodified System Partition”的提示——选择“Keep Read Only”,然后滑动滑块确认;
进入TWRP Recovery的主界面后,点击“Wipe”按钮;
点击“Format Data”按钮;
输入“yes”,然后滑动滑块确认格式化Data分区(这一步是为了解除Data分区的加密,因为LineageOS原生纯净系统默认不支持小米官方的Data分区加密);
格式化完成后,点击“Back”按钮返回Wipe界面;
点击“Advanced Wipe”按钮;
勾选“Dalvik / ART Cache”、“Cache”、“System”、“Vendor”、“Data”(注意不要勾选“Internal Storage”,因为我们后面需要把系统镜像文件复制到Internal Storage中),然后滑动滑块确认擦除;
擦除完成后,点击“Back”按钮返回TWRP Recovery的主界面。
实验步骤4:复制系统镜像文件到Internal Storage并刷入
格式化Data分区和擦除其他分区后,我们需要复制系统镜像文件到Internal Storage并刷入:
在TWRP Recovery的主界面点击“Mount”按钮;
勾选“Internal Storage”,然后点击“Enable MTP”按钮——这样电脑就可以识别到手机的Internal Storage了;
把下载好的LineageOS 20原生纯净系统镜像文件(lineage-20.0-20231001-nightly-cmi-signed.zip)和Google Apps镜像文件(MindTheGapps-13.0.0-arm64-20230808.zip)复制到手机的Internal Storage中;
复制完成后,点击“Disable MTP”按钮,然后点击“Back”按钮返回TWRP Recovery的主界面;
点击“Install”按钮;
选择我们刚刚复制到Internal Storage中的LineageOS 20原生纯净系统镜像文件;
滑动滑块确认刷入;
刷入完成后,如果需要刷入Google Apps的话,点击“Add More Zips”按钮,选择Google Apps镜像文件,然后滑动滑块确认刷入;
刷入完成后,点击“Wipe Cache/Dalvik”按钮,滑动滑块确认擦除;
擦除完成后,点击“Reboot System”按钮,重启手机;
第一次启动LineageOS 20原生纯净系统需要较长的时间(大概5-10分钟),因为系统需要初始化所有的硬件和应用——耐心等待即可;
启动完成后,按照提示设置语言、地区、Wi-Fi、Google账号(如果刷入了Google Apps的话)等——小米10 Ultra手机已经变成了一台半裸机LineageOS原生纯净系统设备,没有任何广告、内置APP非常少、后台服务非常少,运行速度非常快、电池续航也非常长(我用了之后,电池续航从原来的一天一充变成了一天半一充)。
个人观点:裸机开发的未来趋势与消费电子领域“裸机风潮”的回归必要性
作为一名在嵌入式开发和PC硬件领域有10年经验的从业者,我想结合自己的经验发表两个个人观点:
1 个人观点一:裸机开发不会被RTOS/嵌入式Linux取代,反而会在“特定场景”下迎来新的发展机遇
很多人可能会问:“现在RTOS(如FreeRTOS、RT-Thread、Zephyr)和嵌入式Linux已经这么成熟了,为什么还要学裸机开发?裸机开发会不会被取代?”
我的答案是:裸机开发永远不会被取代,反而会在“低功耗、高实时性、极低成本、极小存储空间”的特定场景下迎来新的发展机遇——原因如下:
低功耗场景:RTOS/嵌入式Linux需要运行很多后台服务和任务调度算法,这会消耗大量的电能;而裸机开发没有任何中间层,代码的执行效率极高,电能消耗也极低——比如智能手表的心率传感器、体温