嵌入式 考点总结
嵌入式 考点总结
覆盖实验:实验1 搭建开发环境 | 实验2 GPIO与ARM裸机开发 | 实验3 中断 | 实验4 Uboot与Linux系统 | 实验5 设备驱动
覆盖课件:CH1 嵌入式系统基础 | CH2 ARM硬件平台 | CH3 交叉开发环境
📗 CH1 嵌入式系统基础
1. 嵌入式系统定义 ⭐⭐
IEEE定义: 嵌入式系统是”用于控制、监视或者辅助操作机器和设备的装置”(devices used to control, monitor, or assist the operation of equipment, machinery or plants)。
通俗定义: 嵌入式系统是以应用为中心,以计算机技术为基础,并且软硬件可裁剪,适用于应用系统对功能、可靠性、成本、体积、功耗有严格要求的专用计算机系统。
嵌入式系统本身是一个相对模糊的定义。一个手持的MP3和一个PC104工控计算机都可以认为是嵌入式系统。
2. 嵌入式系统特点 ⭐⭐⭐
| 特点 | 说明 |
|---|---|
| 专用性 | 为固定用途而定制 |
| 可裁剪 | 资源有限,够用即可 |
| 可靠性 | 长期工作,故障率低 |
| 实时性 | 强实时 / 弱实时 |
| 低功耗 | 首要目标不是高性能而是低功耗 |
3. PC与嵌入式系统对比 ⭐⭐
硬件对比:
| 对比项 | PC | 嵌入式系统 |
|---|---|---|
| CPU | Intel 和 AMD | ARM、MIPS、Motorola |
| 内存 | 内存条 | 内存芯片 |
| 存储设备 | 硬盘 | Flash 芯片 |
| 输入设备 | 键盘、鼠标 | 按键、触摸屏 |
| 输出设备 | 显示器 | LCD、无、控制设备 |
| 接口 | 卡 | 芯片 |
软件对比:
| 对比项 | PC | 嵌入式系统 |
|---|---|---|
| 引导代码 | 主板的 BIOS | BootLoader(要移植) |
| OS | Windows、Linux | WindowsCE、VxWorks、Linux(要移植) |
| 驱动程序 | OS自带或下载 | 自己开发或移植 |
| 开发环境 | 本机开发和调试 | 借助服务器进行交叉编译 |
| 仿真器 | 不需要 | 需要 |
4. 嵌入式系统组成结构 ⭐⭐
1 | 嵌入式系统 = 硬件系统 + 软件系统 |
- 最小系统: 处理器 + 最少的外围电路(时钟、复位、电源)
- 简单嵌入式系统: 硬件 + 监控程序(无OS)
- 复杂嵌入式系统: 硬件 + 嵌入式OS + 驱动 + 应用
5. 嵌入式处理器分类 ⭐⭐⭐
| 类型 | 全称 | 特点 | 典型代表 |
|---|---|---|---|
| MPU | Micro Processor Unit | 功能强,需外部扩展存储器和I/O | ARM系列、MIPS系列、PowerPC |
| MCU | Micro Controller Unit | 片内集成CPU+RAM+Flash+I/O | 8051系列、STM32系列 |
| DSP | Digital Signal Processor | 专用于数字信号处理 | TI的TMS320系列 |
| SoC | System on Chip | 系统级芯片,高度集成 | RK3399、Exynos4412 |
MCU基本含义: 在一块芯片上集成了CPU、存储器(RAM/ROM)、定时器/计数器及多种I/O接口的比较完整的系统。
6. 嵌入式操作系统 ⭐⭐
| OS | 特点 |
|---|---|
| VxWorks | 稳定,异常处理强,商业系统 |
| Windows CE | 界面漂亮 |
| μC/OS-II | 最佳学习型,开源,实时性好 |
| 嵌入式Linux | 开源,可裁剪,应用广泛 |
| Android / iOS | 移动智能终端 |
| HarmonyOS | 华为全场景分布式OS |
嵌入式Linux(Embedded Linux)是指对Linux经过小型化裁剪后,能够固化在容量为几百KB到几十MB的存储芯片中,应用于特定嵌入式系统的专用Linux操作系统。
7. 交叉开发环境 ⭐⭐⭐
交叉开发(Cross Developing): 在PC机(宿主机/上位机)上完成程序的编辑、编译、链接等工作;程序的运行在嵌入式设备(目标机)上。
| 环境 | 组成 |
|---|---|
| 宿主机 | PC + Linux + 交叉编译工具链 |
| 目标机 | 实验箱 + 引导程序 + 嵌入式操作系统 |
使用交叉开发的原因: 嵌入式系统的硬件资源有限,编译所需资源大、耗时长,且嵌入式设备编辑不方便。
在线调试方式:
- JTAG(Joint Test Action Group):IBM和TI公司提出
- BDM(Background Debugging Method):Motorola公司提出
8. GCC编译过程 ⭐⭐⭐
C源程序 hello.c 编译成可执行文件,要依次经过四个阶段:
1 | hello.c →[预处理 -E]→ hello.i →[编译 -S]→ hello.S →[汇编 -c]→ hello.o →[链接]→ a.out |
| 阶段 | 选项 | 功能 |
|---|---|---|
| 预处理 | -E |
处理 #include 头文件、#define 宏替换,生成 .i 文件 |
| 编译 | -S |
语法检查 → 转换为汇编语言,生成 .s 文件 |
| 汇编 | -c |
汇编代码 → 目标代码(机器码),生成 .o 文件 |
| 链接 | 目标代码 + 库文件 → 可执行文件 |
GCC编译过程演示:
1 | # 预处理 → 编译 → 汇编 → 链接(一步步执行) |
GCC多文件编译实例:
1 | /* exam1.c */ |
1 | gcc exam1.c exam2.c -o exam # 多个源文件编译为一个可执行文件 |
GCC编译选项:
| 选项 | 功能 | 示例 |
|---|---|---|
-o FileName |
指定输出文件名(默认 a.out) |
gcc test.c -o test |
-c |
只编译生成目标文件 .o |
gcc -c test.c |
-g |
包含标准调试信息 | gcc -g test.c -o test |
-O |
优化编译,提高执行效率 | gcc -O test.c -o test |
-l |
链接库文件(如 -lpthread) |
gcc pthread.c -lpthread -o pthread |
GCC优化效果对比:
1 | # 不优化 |
GCC链接库示例(多线程程序):
1 | # 错误:未指定线程库 |
多文件编译:
gcc exam1.c exam2.c -o exam
9. Buildroot与SDK层次架构 ⭐
Buildroot: Linux平台上构建嵌入式Linux系统的框架,由大量Makefile脚本和Kconfig配置文件构成。可通过 make menuconfig 配置,编译出完整系统(Uboot + kernel + rootfs + 应用程序)。
SDK层次架构:
1 | ┌─────────────────────────┐ |
编译命令:
1 | ./build.sh uboot # 编译Uboot |
📘 CH2 ARM硬件平台
1. ARM处理器概述 ⭐⭐
ARM处理器特点:
- 功耗低(以mW计,首要目标不是高性能而是低功耗)
- 集成丰富的外设接口(尽可能做到SoC)
- 对实时多任务有很强的支持能力
典型ARM系列:
| 体系结构 | 典型内核 | 特点 |
|---|---|---|
| ARMv4T | ARM7TDMI | 3级流水线,无Cache无MMU |
| ARMv5 | ARM920T | 5级流水线,16KB Cache,有MMU |
| ARMv7-A | Cortex-A8/A9/A15 | 有Cache有MMU |
| ARMv7-M | Cortex-M3 | 高速公路ETC读卡机 |
| ARMv8 | Cortex-A53/A72 | 64位(RK3399使用) |
2. ARM处理器工作模式 ⭐⭐⭐
ARM处理器有 7种 工作模式:
| 模式 | 缩写 | 说明 |
|---|---|---|
| 用户模式 | usr | 正常程序执行模式,不能直接切换到其他模式 |
| 快速中断模式 | fiq | 高速数据传输或通道处理 |
| 外部中断模式 | irq | 通用中断处理 |
| 管理模式 | svc | 操作系统使用的保护模式(复位后默认进入) |
| 中止模式 | abt | 虚拟内存或存储器保护 |
| 未定义指令模式 | und | 支持硬件协处理器的软件仿真 |
| 系统模式 | sys | 运行具有特权的操作系统任务(ARMv4以上) |
其中 usr 为非特权模式,其余6种为特权模式;fiq、irq、svc、abt、und 有独立的寄存器组,称为异常模式。
3. ARM寄存器 ⭐⭐⭐
通用寄存器: R0~R15
| 寄存器 | 别名 | 用途 |
|---|---|---|
| R0~R3 | — | 参数传递 / 返回值 / 临时变量 |
| R4~R11 | — | 局部变量(被调用函数需保存) |
| R12 | ip | 过程间临时寄存器(子程序调用时的scratch寄存器) |
| R13 | SP | 栈指针(Stack Pointer) |
| R14 | LR | 链接寄存器(Link Register),保存函数返回地址 |
| R15 | PC | 程序计数器(Program Counter) |
状态寄存器:
| 寄存器 | 用途 |
|---|---|
| CPSR | 当前程序状态寄存器,保存条件标志、中断禁止位、当前处理器模式 |
| SPSR | 程序状态保存寄存器,异常发生时保存CPSR的副本(5种异常模式各有1个SPSR) |
CPSR中关键位:低5位为模式位(如SVC=0x13),bit6为FIQ禁止位,bit7为IRQ禁止位
4. ARM指令分类 ⭐⭐⭐
数据处理指令
| 指令 | 功能 | 示例 |
|---|---|---|
MOV |
数据传送 | MOV R0, #0xFF |
MVN |
数据取反传送 | MVN R0, #0 |
ADD |
加法 | ADD R0, R1, R2 |
SUB |
减法 | SUB R0, R1, #5 |
AND |
按位与 | AND R0, R0, #0xFF |
ORR |
按位或 | ORR R0, R0, #0x01 |
BIC |
位清零 | BIC R0, R0, #0x1F |
EOR |
按位异或 | EOR R0, R0, R0 |
CMP |
比较(设置CPSR标志,不保存结果) | CMP R1, #0 |
MRS |
读状态寄存器 | MRS R0, CPSR |
MSR |
写状态寄存器 | MSR CPSR_c, R0 |
跳转指令
| 指令 | 功能 | 示例 |
|---|---|---|
B |
无条件跳转 | B Label |
BL |
带链接跳转(调用子程序,返回地址存入LR) | BL main |
BX |
跳转到寄存器中的地址(常用于函数返回) | BX LR |
BEQ |
条件跳转(Z=1时跳转) | BEQ Label |
存储器访问指令(LDR/STR系列)
ARM除了寄存器(R0~R15)外没有别的存储单元。所有外围模块都看作是ARM的不同地址单元。
| 指令 | 位数 | 功能 | 示例 |
|---|---|---|---|
LDR |
32位(字) | 从存储器加载到寄存器 | LDR R3, [R4] |
LDRB |
8位(字节) | 加载字节,高24位清零 | LDRB R3, [R1, #8] |
LDRH |
16位(半字) | 加载半字,高16位清零 | LDRH R3, [R1, R2] |
STR |
32位(字) | 从寄存器存储到存储器 | STR R3, [R1], #8 |
STRB |
8位(字节) | 存储低8位 | STRB R3, [R1] |
STRH |
16位(半字) | 存储低16位 | STRH R3, [R1] |
LDR寻址方式:
| 方式 | 语法 | 说明 |
|---|---|---|
| 零偏移 | LDR Rd, [Rn] |
Rn即为地址 |
| 前索引偏移 | LDR Rd, [Rn, #0x04]! |
先更新地址,再访问(!表示写回Rn) |
| 后索引偏移 | LDR Rd, [Rn], #0x04 |
先访问,再更新地址 |
| 程序相对偏移 | LDR Rd, label |
基于PC的偏移(加载标号地址) |
批量加载/存储指令(LDM/STM):
| 堆栈类型 | 后缀 | 说明 |
|---|---|---|
| 满递减堆栈 | FD | 栈满时地址递减(常用) |
| 空递减堆栈 | ED | 栈空时地址递减 |
| 满递增堆栈 | FA | 栈满时地址递增 |
| 空递增堆栈 | EA | 栈空时地址递增 |
1 | STMFD SP!, {R0, R4-R12, LR} @ 多寄存器入栈(保存现场) |
PUSH=STMFD SP!,POP=LDMFD SP!(PUSH/POP是STMFD/LDMFD的别名)
数据交换指令:
| 指令 | 功能 |
|---|---|
SWP |
字交换:R2↔[R3] |
SWPB |
字节交换:R2低8位↔[R3]字节 |
5. RK3399 SoC详解 ⭐⭐⭐
RK3399 架构:
- Big.Little架构:双核 Cortex-A72(大核)+ 四核 Cortex-A53(小核),64位
- CPU频率 > 1.8GHz(大核簇)
- L1 Cache:A72 各48KB I + 32KB D;A53 各32KB I + 32KB D
- L2 Cache:大核簇1024KB,小核簇512KB
- 内部SRAM:总计192KB(启动时bootrom使用4KB)
RK3399 GPIO四步设置(以 GPIO0_A2 引脚为例):
| 步骤 | 名称 | 寄存器地址 | 操作 |
|---|---|---|---|
| 1 | 使能时钟 | 0xFF750000 + 0x0104 |
PMUCRU_CLKGATE_CON1 置位19,使能GPIO0时钟 |
| 2 | 设置功能模式 | 0xFF310000 + 0x0000 |
PMUGRF_GPIO0A_IOMUX,第21:20位设为11(GPIO模式) |
| 3 | 设置方向 | 0xFF720000 + 0x0004 |
GPIO_SWPORTA_DDR,第2位置1=输出,0=输入 |
| 4 | 设置电平 | 0xFF720000 + 0x0000 |
GPIO_SWPORTA_DR,第2位置1=高电平,0=低电平 |
完整ARM汇编代码(四步合一):
1 | .global _start |
RK3399 C语言LED控制程序:
1 |
|
GPIO操作的核心是**”读-改-写”**:先从寄存器读取当前值(
ldr),修改特定位(orr),再写回寄存器(str)。
Exynos4412 GPIO寄存器组织方式(对比):
| 寄存器类型 | 命名规则 | 功能 |
|---|---|---|
| 端口控制寄存器 | GPA0CON ~ GPZCON | 配置引脚复用功能 |
| 端口数据寄存器 | GPA0DAT ~ GPZDAT | 读/写引脚电平 |
| 端口上拉寄存器 | GPA0PUD ~ GPZPUD | 配置上拉/下拉电阻 |
| 驱动能力寄存器 | GPA0DRV ~ GPZDRV | 配置驱动强度 |
📙 CH3 交叉开发环境(BootLoader与Linux系统)
1. BootLoader概述 ⭐⭐⭐
BootLoader定义: 系统加电后运行的第一段代码(通常从地址 0x00000000 开始执行),核心任务是启动操作系统。相当于PC中的 BIOS + MBR。
BootLoader通用结构:
| 阶段 | 语言 | 功能 |
|---|---|---|
| Stage1 | 汇编 | 硬件设备初始化 → 为Stage2准备RAM → 复制Stage2到RAM → 设置堆栈 → 跳转Stage2入口 |
| Stage2 | C语言 | 初始化硬件 → 检测内存映射 → 将kernel和rootfs从Flash读到RAM → 设置启动参数 → 引导内核 |
常用BootLoader: U-Boot(Universal BootLoader)、Blob、ARMboot、RedBoot、vivi
2. U-Boot目录结构 ⭐⭐
| 目录 | 存放内容 |
|---|---|
arch |
体系结构相关代码(arm、x86等) |
board |
核心板相关支持文件(samsung、rockchip等) |
common |
U-Boot命令(bootm、tftp等) |
configs |
各板配置文件(rk3399_defconfig) |
driver |
驱动程序(网卡、串口、USB等) |
fs |
文件系统支持(ext4、nfs、jffs2等) |
include |
头文件(硬件平台支持、系统配置等) |
net |
网络协议栈(bootp、tftp、rarp) |
tools |
工具(mkimage 生成U-Boot镜像) |
配置命令:
1 | make rk3399_defconfig # 加载默认配置 |
3. Linux内核启动流程 ⭐⭐
Image的生成过程:
1 | vmlinux(原始内核,~23MB)→[objcopy去掉符号表]→ Image(纯内核镜像,~18MB) |
Linux启动流程:
1 | arch/arm64/kernel/head.S(汇编入口) |
vmlinux.lds是内核链接脚本,决定了各段在内存中的位置
4. 根文件系统(rootfs) ⭐⭐⭐
rootfs 与 kernel 的关系:
- 根文件系统和内核是完全独立的两个部分
- 单独的Linux内核没法正常工作,必须搭配根文件系统
- rootfs包含:内核模块(.ko)、
/etc/fstab(自动挂载列表)、/etc/inittab(init配置)、应用程序、库文件等
Linux关键目录文件:
| 文件 | 用途 |
|---|---|
/etc/fstab |
自动挂载的文件系统列表 |
/etc/inittab |
init进程的配置文件 |
/bin |
系统必备命令(cp、ls、mount、rm) |
/sbin |
系统管理命令(insmod、fdisk、reboot) |
Linux文件系统格式(补充):
| 类型 | 格式 |
|---|---|
| 传统硬盘 | ext2、ext3、ext4、XFS |
| 闪存 | JFFS2、YAFFS |
| 网络 | NFS |
| 虚拟/特殊 | swap、procfs、sysfs、debugfs、devfs |
📗 实验1 搭建开发环境
1. eAIOT实验平台 ⭐
平台组成:
- 左侧:高性能嵌入式主板(核心板),采用瑞芯微 RK3399(64位ARM SoC),标配 4GB 内存 + 16GB 闪存
- 右侧:无线传感器网络扩展板(Zigbee、BLE、LORA、NB-IoT、RFID、10+传感器)
- 核心板以金手指形式插入底板,板载 2×2 MIMO 双天线 WiFi 模组
支持运行的OS: Android 8.1、Ubuntu 18.04、Armbian、Buildroot、Linux
开机流程(按顺序):
- 将各类天线、网线正确接入
- 将 Type-C 数据线插入 debug 或 download 口
- 确保主板电源开关处于靠左关闭状态
- 插入白色 12V 电源插头
- 向右拨动电源开关启动系统
⚠️ 请严格按照以上步骤开机,减少瞬时电涌对主板接口的损害。Type-C 接口较为脆弱,请勿暴力抽拽。
2. Linux文件系统 ⭐⭐
文件系统类型:
| 文件系统 | 特点 |
|---|---|
| ext2 | 早期文件系统,非日志文件系统,已不推荐 |
| ext3 | 在 ext2 基础上发展,日志文件系统,完全兼容 ext2 |
| ext4 | 在 ext3 基础上发展,性能和可靠性更佳,向下兼容 ext3 和 ext2 |
Linux根目录重要文件夹:
| 目录 | 用途 |
|---|---|
/bin |
二进制可执行命令文件 |
/sbin |
系统命令 |
/root |
超级用户 root 的根目录 |
/home |
普通用户默认目录 |
/boot |
系统内核和启动文件 |
/dev |
设备文件 |
/etc |
系统管理配置文件 |
/lib |
系统运行所需库文件 |
/proc |
虚拟目录,保存系统信息和进程信息 |
/tmp |
临时文件,所有用户可读写 |
/var |
变化文件,如日志 |
/usr |
应用程序和库文件 |
/sys |
系统设备和文件层次结构 |
查询当前文件系统版本:
df -T -h
3. 常用Shell命令 ⭐⭐
Shell命令格式: command -options [argument]
基础命令:
| 命令 | 语法示例 | 功能 |
|---|---|---|
ls |
ls -l /home |
列出目录内容(-l详细列表,-a显示隐藏文件) |
cd |
cd /home/xuelitec |
切换目录(cd ~回用户目录,cd ..返回上级) |
pwd |
pwd |
显示当前所在目录的完整路径 |
uname |
uname -a |
显示系统信息(-a显示全部内核信息) |
sudo |
sudo apt-get install xxx |
以超级用户权限执行命令 |
man |
man ls |
查看命令手册(按 q 退出) |
df |
df -T -h |
查看磁盘使用情况(-T显示文件系统类型,-h人性化显示) |
文件操作命令:
| 命令 | 语法示例 | 功能 |
|---|---|---|
touch |
touch test1.c |
创建空文件 / 更新文件时间戳 |
mkdir |
mkdir -p dir/subdir |
创建目录(-p递归创建多级目录) |
rm |
rm -rf dir/ |
删除文件(-r递归删除目录,-f强制) |
rmdir |
rmdir empty_dir/ |
删除空目录(目录非空会报错) |
cp |
cp file1.c /home/backup/ |
复制文件 |
mv |
mv oldname.c newname.c |
移动文件 / 重命名 |
chmod |
chmod 755 test1.c |
修改文件权限 |
find |
find /home -name "*.c" |
按条件查找文件 |
grep |
grep "include" main.c |
在文件中搜索字符串 |
压缩解压命令:
| 命令 | 语法示例 | 功能 |
|---|---|---|
tar |
tar -zxvf file.tar.gz |
解压 .tar.gz(-zgzip,-x解压,-v显示过程,-f指定文件) |
tar |
tar -cvzf dir.tar.gz dir/ |
压缩为 .tar.gz(-c创建) |
zip |
zip archive.zip file1 file2 |
压缩为 .zip |
unzip |
unzip archive.zip |
解压 .zip |
文件权限详解(chmod):
1 | # 查询权限 |
4. 开发环境搭建 ⭐
Ubuntu和Windows文件互传: FTP服务
NFS服务安装:
1 | sudo apt-get install nfs-kernel-server |
SSH服务安装:
1 | sudo apt-get install openssh-server |
交叉编译工具链: arm-linux-gnueabihf-gcc(用于编译裸机程序)/ aarch64-linux-gnu-gcc(用于编译64位程序)
📘 实验2 GPIO与ARM裸机开发
1. ARM处理器与GPIO ⭐⭐⭐
RK3399 SoC 双处理器:
- Cortex-A53(64位,小核)
- Cortex-A72(64位,大核)
GPIO操作步骤(裸机):
- 使能GPIO时钟
- 设置GPIO复用功能(配置MUX寄存器)
- 设置GPIO方向(输入/输出)
- 设置GPIO输出电平(高低)
RK3399关键寄存器:
- PMUGRF_GPIO0A_IOMUX(地址
0xFF310000):GPIO复用功能配置 - GPIO0_SWPORTA_DR:GPIO数据寄存器(写数据)
- GPIO0_SWPORTA_DDR:GPIO方向寄存器(0=输入,1=输出)
2. ARM启动文件(start.S) ⭐⭐⭐
start.S完成的主要功能:
- 设置异常向量表(Exception Vector)
- 关闭 IRQ、FIQ,设置 SVC 模式
- 关闭 L1 cache,设置 L2 cache,关闭 MMU
- 根据 OM 引脚确定启动方式
- 在 SoC 内部 SRAM 中设置栈
- 执行
lowlevel_init函数(初始化系统时钟、SDRAM、串口等) - 设置 SDRAM 中的栈
⚠️ 异常向量表必须从
0x00000000或0xFFFF0000开始,每条向量占4字节
3. ARM汇编与C语言混合编程 ⭐⭐
关键汇编指令:
| 指令 | 语法示例 | 功能 |
|---|---|---|
LDR |
LDR R0, =0xFF310000 |
将地址/数据加载到寄存器(=表示伪指令,加载立即数到R0) |
STR |
STR R0, [R1] |
将寄存器的值存储到内存地址 |
MOV |
MOV R0, #0xFF |
将立即数移动到寄存器 |
BL |
BL main |
带链接的跳转(调用函数,将返回地址存入LR) |
B |
B loop |
无条件跳转(不保存返回地址) |
BX |
BX LR |
跳转到寄存器中的地址(常用于函数返回,LR存放返回地址) |
PUSH |
PUSH {R0, R1, LR} |
将寄存器压栈(保存现场) |
POP |
POP {R0, R1, PC} |
从栈中弹出寄存器(恢复现场,PC为程序计数器) |
MRS |
MRS R0, CPSR |
读取状态寄存器CPSR到通用寄存器 |
MSR |
MSR CPSR_c, R0 |
写入通用寄存器的值到状态寄存器 |
汇编中调用C函数: 使用 BL main 跳转到C语言入口点,函数返回时通过 BX LR 回到汇编
实际应用示例(start.S中设置SVC模式并关中断):
1 | MRS R0, CPSR @ 读取当前状态寄存器 |
栈设置与调用C函数示例:
1 | LDR SP, =0x00200000 @ 设置栈指针SP(SDRAM顶部往下) |
4. 交叉编译与烧写 ⭐⭐
编译步骤:
1 | # 编译 start.S |
烧写命令(Uboot下):
1 | # 下载到内存 |
Makefile编写要点:
CROSS_COMPILE = arm-linux-gnueabihf-- 使用
-march=armv7-a指定架构 - 链接脚本
.ld指定各段在内存中的位置
5. Makefile 编写 ⭐⭐⭐
Makefile 是工程管理工具,通过依赖关系自动判断哪些文件需要重新编译,大幅提高编译效率。
基本语法
1 | 目标(target): 依赖(prerequisites) |
规则:
目标:要生成的文件,或是一个”伪目标”(如clean)依赖:生成目标所依赖的文件列表命令:生成目标的具体操作(必须用 Tab 开头)
变量与自动变量
Makefile变量分为三类:
| 变量/符号 | 含义 | 示例 |
|---|---|---|
$(CC) |
编译器 | CC = arm-linux-gnueabihf-gcc |
$(LD) |
链接器 | LD = arm-linux-gnueabihf-ld |
$(OBJCOPY) |
格式转换工具 | OBJCOPY = arm-linux-gnueabihf-objcopy |
$@ |
当前目标名 | led.bin: led.elf 中 $@ = led.bin |
$< |
第一个依赖名 | led.o: led.S 中 $< = led.S |
$^ |
所有依赖名(空格分隔) | app: a.o b.o 中 $^ = a.o b.o |
?= |
若未定义则赋值 | CROSS_COMPILE ?= arm-linux-gnueabihf- |
Makefile预定义变量(Make自带,可直接引用):
| 变量 | 默认值 | 用途 |
|---|---|---|
AR |
ar |
库文件维护程序 |
AS |
as |
汇编程序 |
CC |
cc |
C编译器 |
CPP |
$(CC) -E |
C预编译器 |
CXX |
g++ |
C++编译器 |
RM |
rm -f |
文件删除命令 |
CFLAGS |
无 | C编译器选项 |
LDFLAGS |
无 | 链接器选项 |
常用编译变量
| 变量 | 用途 | 常用值 |
|---|---|---|
CFLAGS |
C编译选项 | -march=armv7-a -mfloat-abi=hard -mfpu=neon |
LDFLAGS |
链接选项 | -Ttext 0x00000000 -Tlink.ld |
CROSS_COMPILE |
交叉编译前缀 | arm-linux-gnueabihf- |
TARGET |
最终目标文件名 | led.bin |
伪目标 .PHONY
1 |
|
为什么需要
.PHONY? 若目录下存在一个名为clean的文件,make clean会认为目标已最新而跳过。.PHONY告诉 make 无论是否有同名文件都执行该规则。
裸机开发完整 Makefile 示例
1 | # 交叉编译工具链前缀 |
内核模块 Makefile(实验5 用)
1 | # 内核模块 Makefile(放在模块源码同一目录) |
关键点: 内核模块 Makefile 与普通 Makefile 不同,
obj-m表示编译为模块,-C指定内核源码目录,M=$(PWD)指定模块源码目录。
RK3399实际交叉编译 Makefile(课件原版)
1 | # RK3399 裸机交叉编译 Makefile(带链接脚本) |
与简化版Makefile的区别: 使用
-nostdlib不链接标准库(裸机无OS)、-Trk3399.lds指定链接脚本控制内存布局、OBJDUMP生成反汇编文件用于调试
Make 常用命令
| 命令 | 功能 |
|---|---|
make |
执行 Makefile 中第一个目标(通常是 all) |
make clean |
执行 clean 目标,清理编译产物 |
make -j4 |
4个线程并行编译,加快速度 |
make V=1 |
显示详细的编译命令(调试用) |
📙 实验3 中断
1. 中断基本概念 ⭐⭐⭐
中断定义: CPU在执行程序的过程中,当出现某些突发事件时,CPU暂停当前程序,转去处理该事件,处理完毕后再返回原来被中断的位置继续执行。
中断类型:
| 类型 | 来源 | 特点 |
|---|---|---|
| IRQ | 外设中断 | 普通中断,可屏蔽 |
| FIQ | 快速中断 | 优先级高于IRQ,用于高速数据传输 |
| SWI/SVC | 软件中断 | 用户态切换到内核态 |
| Reset | 复位 | 最高优先级 |
| Undefined Instruction | 未定义指令 | 执行未定义指令时触发 |
| Data Abort | 数据访问异常 | 数据读写出错 |
| Prefetch Abort | 指令预取异常 | 指令读取出错 |
2. RK3399 GPIO中断实验 ⭐⭐
GPIO中断配置步骤:
- 使能GPIO时钟
- 设置GPIO复用功能为GPIO模式
- 设置GPIO方向为输入
- 配置GPIO中断触发方式(上升沿/下降沿/高电平/低电平)
- 使能GPIO中断
- 注册中断服务函数(ISR)
按键消抖: 软件延时消抖(约10~20ms)
3. ARM异常处理 ⭐⭐
异常向量表:
| 地址偏移 | 异常类型 |
|---|---|
| 0x00 | Reset |
| 0x04 | Undefined Instruction |
| 0x08 | SWI/SVC |
| 0x0C | Prefetch Abort |
| 0x10 | Data Abort |
| 0x14 | 未使用 |
| 0x18 | IRQ |
| 0x1C | FIQ |
中断处理流程:
- 保存现场(将寄存器压栈)
- 执行中断服务程序(ISR)
- 恢复现场(寄存器出栈)
- 中断返回
4. GIC中断控制器 ⭐⭐
GIC(Generic Interrupt Controller)功能:
- 中断优先级管理
- 中断路由(分发到哪个CPU核心)
- 中断屏蔽与使能
- 中断状态跟踪(active/pending/inactive)
📕 实验4 Uboot与Linux系统
1. RK3399启动过程 ⭐⭐⭐
完整启动流程:
1 | 上电复位 → romcode → BL1(SRAM)→ BL2(DDR)→ OS Kernel → Rootfs |
详细步骤:
- RK3399系统上电复位后,Cortex-A53从地址
0xFFFF0000执行romcode - romcode依次按以下顺序查找介质上的ID BLOCK信息:
- SPI NOR FLASH → SPI NAND FLASH → eMMC → SD/MMC → USB
- romcode功能:查找ID BLOCK → 确定启动方式 → 初始化硬件 → 将BL1加载到内部SRAM
- BL1执行DDR初始化,将BL2复制到DDR,跳转到BL2
- BL2初始化硬件和软件,将OS复制到DDR,跳转到OS
- 启动操作系统
2. Bootloader (Uboot) ⭐⭐⭐
Bootloader定义: 系统启动后、OS内核运行之前运行的一段小程序,用于初始化硬件设备、建立内存映射,为调用OS内核准备好环境。
Bootloader两种操作模式:
| 模式 | 说明 |
|---|---|
| 启动加载模式(自主模式) | 从固态存储设备将OS加载到RAM运行,无需用户介入。是产品发布时的正常工作模式 |
| 下载模式 | 通过串口/网络从主机下载内核映像和根文件系统。用于第一次安装和系统更新,提供命令行接口 |
BL1阶段(Stage1,汇编实现):
| 步骤 | 功能 |
|---|---|
| 1 | 硬件设备初始化 |
| 2 | 为加载Stage2准备RAM空间 |
| 3 | 复制Stage2到RAM空间 |
| 4 | 设置堆栈(为执行C语言代码做准备) |
| 5 | 跳转到Stage2的C入口点 |
BL2阶段(Stage2,C语言实现):
| 步骤 | 功能 |
|---|---|
| 1 | 初始化本阶段使用的硬件设备 |
| 2 | 检测系统内存映射(memory map) |
| 3 | 将内核映像和根文件系统从Flash读到RAM |
| 4 | 为内核设置启动参数 |
| 5 | 调用内核 |
3. Uboot源码启动流程 ⭐⭐
start.S 文件(BL1阶段):
- 指定Uboot入口
- 设置异常向量
- 关闭IRQ、FIQ,设置SVC模式
- 关闭L1 cache,设置L2 cache,关闭MMU
- 根据OM引脚确定启动方式
- 在SoC内部SRAM中设置栈
- 执行
lowlevel_init(初始化系统时钟、SDRAM、串口) - 设置开发板供电锁存
- 设置SDRAM中的栈
_main 函数(BL1→BL2过渡):
- 将Uboot从存储介质复制到SDRAM(BL2加载到RAM并跳转)
- 设置并开启MMU
- 在SDRAM中合适位置设置栈
- 清除BSS段,BL1阶段完毕
board_init_r 函数(BL2阶段):
- 规划Uboot内存使用
- 遍历调用
init_sequence初始化函数数组 - 初始化堆管理器
- 初始化SD/MMC控制器
- 环境变量重定位
- 目标机硬件设备初始化
- 控制台初始化
- 网卡芯片初始化
- 进入主循环
main_loop(出现Uboot提示符)
4. Linux内核 ⭐⭐
Linux内核主要功能:
| 功能 | 说明 |
|---|---|
| 进程管理 | 进程创建/销毁,调度策略,处理器资源共享,进程间通信 |
| 内存管理 | 虚拟地址空间,地址映射,多进程安全共享内存 |
| 文件管理 | 虚拟文件系统(VFS),支持ext3/ext4等数十种文件系统 |
| 设备管理 | 设备驱动程序,每种外设对应特定驱动代码 |
| 网络管理 | 网络协议栈 + 网络设备驱动程序 |
Linux内核目录结构:
| 目录 | 内容 |
|---|---|
arch |
架构相关代码(ARM、x86等) |
block |
块设备(SD卡、eMMC、NAND、硬盘等) |
drivers |
驱动程序(drivers/i2c、drivers/gpio等) |
fs |
文件系统 |
init |
初始化代码(含 main.c 的 start_kernel) |
ipc |
进程间通信 |
kernel |
调度器等核心代码 |
mm |
内存管理 |
net |
网络协议栈 |
Linux内核启动顺序:
1 | 内核引导 → 运行init → 系统初始化 → 建立终端 → 用户登录系统 |
内核编译命令:
1 | # 编译uboot |
内核配置命令:
1 | # 加载实验平台默认配置 |
📓 实验5 设备驱动(一)
1. 应用程序与驱动的关系 ⭐⭐⭐
调用流程:
1 | 应用程序(用户空间) → C库函数(open/read/write) → 系统调用 → 内核空间 → 驱动程序 |
- 应用程序运行在用户空间
- 驱动程序运行在内核空间
- 用户空间不能直接操作内核,必须通过系统调用陷入内核空间
- open、close、write、read 等函数由 C 库提供
2. file_operations 结构体 ⭐⭐⭐
定义位置: include/linux/fs.h
file_operations 是Linux内核驱动操作函数集合
| 成员 | 功能 |
|---|---|
owner |
拥有该结构体的模块指针,设为 THIS_MODULE |
read |
读取设备文件 |
write |
向设备文件写入数据 |
open |
打开设备文件 |
release |
关闭设备文件(对应应用层 close) |
开发字符设备驱动最主要的工作就是实现
file_operations中的函数
3. 驱动模块加载与卸载 ⭐⭐⭐
驱动运行方式:
- 编译进Linux内核:内核启动时自动运行
- 编译为模块(
.ko):内核启动后使用命令加载(调试时推荐)
模块注册函数:
1 | module_init(xxx_init); // 注册模块加载函数,insmod时调用xxx_init |
模块加载命令:
1 | insmod drv.ko # 加载指定模块 |
4. 字符设备注册与注销 ⭐⭐
1 | // 注册字符设备 |
设备号:
- 每个设备有一个设备号,分为主设备号和次设备号
- 应用程序通过设备号使用设备驱动
- 主设备号:标识驱动程序(同类驱动共用)
- 次设备号:标识具体设备实例
创建设备文件:
1 | mknod /dev/chrdev c 200 0 # c=字符设备 200=主设备号 0=次设备号 |
5. 内核空间与用户空间数据传递 ⭐⭐⭐
两个空间不能直接互相访问,必须使用专用函数:
1 | // 内核空间 → 用户空间 |
| 参数 | 含义 |
|---|---|
to |
传递的目标地址 |
from |
传递的起始地址 |
count |
传递的数据长度 |
| 返回值 | 未成功传递的数据长度 |
6. 地址映射(MMU) ⭐⭐⭐
MMU(Memory Manage Unit)功能:
- 虚拟空间到物理空间的映射
- 内存保护,设置访问权限和缓冲特性
关键概念:
- 虚拟地址(VA):CPU访问的地址
- 物理地址(PA):实际硬件地址
- Linux内核启动时初始化MMU,CPU访问的都是虚拟地址
地址映射函数:
1 | // 物理地址 → 虚拟地址 |
示例:
1 | // 映射 |
7. I/O内存访问函数 ⭐⭐
ARM体系下只有I/O内存(无I/O端口概念)
读操作函数:
| 函数 | 位数 |
|---|---|
readb(addr) |
8-bit |
readw(addr) |
16-bit |
readl(addr) |
32-bit |
写操作函数:
| 函数 | 位数 |
|---|---|
writeb(value, addr) |
8-bit |
writew(value, addr) |
16-bit |
writel(value, addr) |
32-bit |
寄存器操作示例:
1 | // 读-改-写操作 |
8. 字符设备驱动开发完整步骤 ⭐⭐
- 编写驱动代码:实现
file_operations中的 open/release/read/write - 编写Makefile:使用内核构建系统(
KDIR指向内核源码目录) - 编译驱动:
make生成.ko模块文件 - 编译测试程序:
aarch64-linux-gnu-gcc chrdevApp.c -o chrdevApp - 加载模块:
insmod chrdev.ko - 创建设备文件:
mknod /dev/chrdev c 200 0 - 运行测试:
./chrdevApp
📝 重点题型总结
简答题:嵌入式系统定义与特点
定义要点: 以应用为中心、计算机技术为基础、软硬件可裁剪、专用计算机系统
特点记忆: 专裁可实低(专用性、可裁剪、可靠性、实时性、低功耗)
简答题:嵌入式处理器分类
MPU(微处理器)需外扩存储器;MCU(微控制器)片上集成CPU+RAM+Flash+I/O;DSP(数字信号处理器)专用于信号处理;SoC(片上系统)高度集成
简答题:ARM处理器工作模式
7种模式: usr(用户)、fiq(快速中断)、irq(外部中断)、svc(管理/复位默认)、abt(中止)、und(未定义指令)、sys(系统)
特权模式: 除usr外的6种;异常模式: fiq、irq、svc、abt、und(各有独立SPSR)
简答题:ARM寄存器
R13=SP(栈指针)、R14=LR(链接寄存器,保存返回地址)、R15=PC(程序计数器)
CPSR(当前状态寄存器)、SPSR(异常模式下保存CPSR副本,5种异常模式各1个)
程序分析题:ARM汇编指令
掌握指令功能识别:MOV(传送)、ADD/SUB(加减)、AND/ORR/BIC/EOR(位运算)、CMP(比较,设标志)、LDR/STR(存储器访问)、B/BL/BX(跳转)、MRS/MSR(状态寄存器访问)、PUSH/POP(栈操作)
LDR寻址辨析: 零偏移[Rn]、前索引[Rn,#off]!、后索引[Rn],#off
简答题:交叉开发环境
宿主机(PC + Linux + 交叉编译工具链)负责编辑编译;目标机(实验箱 + BootLoader + OS)负责运行
在线调试: JTAG(IBM/TI)、BDM(Motorola)
简答题:GCC编译过程
四阶段: 预处理(-E→.i) → 编译(-S→.s) → 汇编(-c→.o) → 链接(→可执行文件)
简答题:rootfs与kernel的关系
完全独立的两个部分。 单独内核无法工作,必须搭配rootfs(包含内核模块、/etc/fstab、/etc/inittab、应用程序、库文件等)
简答题:RK3399启动过程
记忆线索: 上电 → romcode(0xFFFF0000) → 查ID BLOCK(SPI NOR→NAND→eMMC→SD→USB) → BL1(SRAM, DDR初始化) → BL2(DDR, 加载OS) → 启动OS
简答题:Bootloader两种模式
启动加载模式 = 自主模式,从存储设备加载OS,产品发布用;下载模式 = 从主机下载,首次安装和更新用
简答题:BL1/BL2阶段功能
**BL1(汇编)**:硬件初始化 → 准备RAM → 复制BL2 → 设栈 → 跳转C入口
**BL2(C)**:初始化硬件 → 检测内存 → 读内核/根文件系统 → 设启动参数 → 调用内核
简答题:Linux内核主要功能
记忆:进程、内存、文件、设备、网络
简答题:内核空间与用户空间数据传递
copy_to_user = 内核→用户;copy_from_user = 用户→内核;两个空间不能直接访问
简答题:地址映射
ioremap = 物理地址→虚拟地址;iounmap = 释放映射;因为MMU开启后CPU只能访问虚拟地址
简答题:file_operations结构体
是Linux内核驱动操作函数集合,包含 open/read/write/release 等函数指针
简答题:设备号与设备文件
mknod /dev/chrdev c 200 0:c=字符设备,200=主设备号,0=次设备号;应用通过设备号使用驱动
💡 考试提示:
- 嵌入式系统定义与特点(专用性、可裁剪、可靠性、实时性、低功耗)可能考填空/简答
- 嵌入式处理器分类(MPU/MCU/DSP/SoC)需区分
- ARM工作模式(7种,特别是usr/svc/irq/fiq)可能考简答或选择
- ARM寄存器(R13=SP, R14=LR, R15=PC, CPSR/SPSR)必考
- ARM指令(MOV/ADD/SUB/CMP/LDR/STR/B/BL/BX/PUSH/POP/MRS/MSR)必考程序分析
- LDR寻址方式(零偏移、前索引、后索引)可能考辨析
- RK3399启动过程(romcode地址、ID BLOCK查找顺序)必考简答
- RK3399 GPIO四步(CRU→GRF→方向→电平)必考
- Bootloader两种模式和BL1/BL2功能必考简答
- GCC编译过程(预处理→编译→汇编→链接)及编译选项(-c/-o/-g/-O/-l)可能考
- Linux内核五大功能必考简答
- 字符设备驱动开发步骤(加载/卸载、注册/注销、数据传递、地址映射)为实验5重难点
- Makefile编写(变量、自动变量、伪目标、内核模块Makefile)可能考简答或写Makefile
- 根文件系统与内核的独立性可能考简答
- ARM异常向量表(7种异常)需记忆地址偏移




