EmbeddedSoftwareLearn
欢迎来到本项目,这是一份面向中文社区的系统、全面且贴近实战的嵌入式软件开发学习路线和知识点总结。涵盖范围包括 C/C++、嵌入式开发、驱动开发、计算机网络原理、RTOS、嵌入式 Linux、网络通信与物联网、常用工具链等相关知识点。收集全…
git clone https://github.com/0voice/EmbeddedSoftwareLearn.git0voice/EmbeddedSoftwareLearn📘 2025 嵌入式软件开发全景笔记 EmbeddedSoftwareLearn
嵌入式软件学习路线图 · C语言 / RTOS / Linux驱动 · 从入门到进阶
概述
欢迎来到本项目,这里拥有系统、全面且贴近实战的2025年嵌入式软件开发学习路线和知识点总结。
涵盖包括C语言、驱动开发、RTOS、嵌入式 Linux、网络通信与物联网、常用工具链等嵌入式软件开发所需知识点
以及嵌入式开发相关的必读书籍、面试题以及面经。
跟随目录点击选择想学习的部分即可跳转至相应知识点。
爆肝两周只为给大家呈现最新开源免费的嵌入式软件学习资料!无任何套路!
能够帮助到你的话请点个star收藏一下推给更多有需要的人就是最大鼓励,不胜感激!
目录
- C语言基础
- 嵌入式系统基础知识
- 驱动开发与外设编程
- RTOS
- 嵌入式 Linux 开发基础
- 网络通信与物联网协议
- 调试与性能优化
- 项目实战与工具链
- 嵌入式平台 Qt 开发
- 2025 嵌入式软件新趋势
- 电子书资源
- 面试题与面经
🔵 第一层:C/C++ 语言基础与进阶(必修)
✅ 变量 / 数据类型 / 关键字 / 常量
📌 变量(Variable)
- 用于在程序中存储数据的具名内存区域。
- 声明格式:
类型 变量名 [= 初始值];
int count = 10; float temperature; char c = 'A';
- 局部变量:函数内声明,仅在函数内部可用。
- 全局变量:函数外声明,整个文件或项目中可见(根据作用域)。
📌 数据类型(Data Types)
- 整型:
int,short,long,long long,unsigned - 浮点型:
float,double - 字符型:
char - 派生类型:指针、数组、结构体等
unsigned int u = 100; long long big_number = 12345678900LL;
📌 关键字(Keywords)
常用 C 关键字解释如下:
| 关键字 | 说明 |
|---|---|
const |
定义只读变量 |
volatile |
防止编译器优化,常用于寄存器 |
static |
变量作用域或函数仅在本文件可见 |
extern |
声明外部变量/函数 |
typedef |
为数据类型取别名 |
static int counter = 0; // 内部链接 extern int g_value; // 声明外部变量 volatile uint32_t *reg = (uint32_t *)0x40021000; // 用于寄存器访问
📌 常量(Constant)
- 字面常量:如
10,3.14,'a',"abc" - 符号常量:用
#define或const定义
#define PI 3.14159 const int MAX_SIZE = 100;
✅ 栈 和 堆(内存管理)
栈 (stack):自动分配内存,函数退出即释放。
- 核心特性
- 自动分配与释放:由编译器自动管理,函数调用时分配栈帧,函数返回时自动释放。
- 后进先出(LIFO):类似一摞盘子,最后放入的最先取出。
- 高速访问:栈内存访问效率高(通常通过寄存器直接操作)。
- 空间有限:栈空间通常较小(如 Linux 默认 8MB),过大的局部变量可能导致栈溢出。
- 存储内容
- 局部变量:函数内部定义的变量。
- 返回地址:函数执行完毕后返回的位置。
- 函数参数:调用函数时传递的参数。
- 寄存器值:保存调用前的寄存器状态,以便恢复。
- 工作原理
- 栈指针(ESP):指向当前栈顶的内存地址。
- 栈帧(Stack Frame):每个函数调用在栈上分配的独立空间,包含局部变量和参数。
- 示例代码:
void func(int a, int b) {
int sum = a + b; // sum存储在栈上
// ...
} // 函数返回时,sum和参数a、b自动释放
- 优缺点
- 优点:无需手动管理内存,速度快,不会内存泄漏。
- 缺点:生命周期固定(函数结束即释放),空间有限。
堆 (heap):使用 malloc / free 手动分配和释放
- 核心特性
- 手动分配与释放:使用malloc/calloc/realloc分配,free释放。
- 动态生命周期:内存块的生命周期由程序员控制,可跨函数使用。
- 碎片化问题:频繁分配和释放可能导致内存碎片,降低空间利用率。
- 慢速访问:需通过指针间接访问,效率低于栈。
- 存储内容
- 动态分配的对象:如malloc返回的内存块。
- 大型数据结构:如数组、链表、树等需要动态调整大小的结构。
- 跨函数数据:需要在函数调用结束后继续存在的数据。
- 工作原理
- 内存管理器:操作系统提供的堆管理器负责分配和回收内存。
- 空闲链表:堆管理器维护空闲内存块列表,分配时查找合适大小的块。
- 示例代码:
void createArray() {
int* arr = (int*)malloc(10 * sizeof(int)); // 从堆分配内存
if (arr != NULL) {
arr[0] = 100; // 使用堆内存
// ...
}
free(arr); // 手动释放内存
}
- 常见函数
- malloc(size_t size):分配指定字节的内存,不初始化。
- calloc(size_t num, size_t size):分配内存并初始化为 0。
- realloc(void* ptr, size_t new_size):调整已分配内存的大小。
- free(void* ptr):释放内存,必须与malloc配对使用。
栈 vs 堆的对比
| 特性 | 栈(Stack) | 堆(Heap) |
|---|---|---|
| 分配方式 | 自动(由编译器管理) | 手动(如 malloc/free 或 new/delete) |
| 生命周期 | 函数调用期间自动创建和销毁 | 程序员控制,需手动释放 |
| 内存空间 | 连续、有限(通常几 MB) | 不连续、较大(受限于物理内存) |
| 访问速度 | 快(通过寄存器快速访问) | 慢(通过指针间接访问) |
| 内存碎片 | 不存在(先进后出结构) | 可能产生(频繁分配和释放) |
| 使用场景 | 局部变量、函数调用栈帧 | 动态数据结构、跨函数共享数据 |
指针
指针的基本概念
指针的作用: 可以通过指针间接访问内存
- 内存编号是从0开始记录的,一般用十六进制数字表示
- 可以利用指针变量保存地址
指针变量的定义和使用
指针变量定义语法: 数据类型 * 变量名;
示例:
int main() {
//1、指针的定义
int a = 10; //定义整型变量a
//指针定义语法: 数据类型 * 变量名 ;
int * p;
//指针变量赋值
p = &a; //指针指向变量a的地址
cout << &a << endl; //打印数据a的地址
cout << p << endl; //打印指针变量p
//2、指针的使用
//通过*操作指针变量指向的内存
cout << "*p = " << *p << endl;
system("pause");
return 0;
}
指针变量和普通变量的区别
- 普通变量存放的是数据,指针变量存放的是地址
- 指针变量可以通过" * "操作符,操作指针变量指向的内存空间,这个过程称为解引用
总结1: 我们可以通过 & 符号 获取变量的地址
总结2:利用指针可以记录地址
总结3:对指针变量解引用,可以操作指针指向的内存
指针所占内存空间
提问:指针也是种数据类型,那么这种数据类型占用多少内存空间?
示例:
int main() {
int a = 10;
int * p;
p = &a; //指针指向数据a的地址
cout << *p << endl; //* 解引用
cout << sizeof(p) << endl;
cout << sizeof(char *) << endl;
cout << sizeof(float *) << endl;
cout << sizeof(double *) << endl;
system("pause");
return 0;
}
总结:所有指针类型在32位操作系统下是4个字节,64位操作系统为8个字节
空指针和野指针
空指针:指针变量指向内存中编号为0的空间
用途: 初始化指针变量
注意: 空指针指向的内存是不可以访问的
示例1:空指针
int main() {
//指针变量p指向内存地址编号为0的空间
int * p = NULL;
//访问空指针报错
//内存编号0 ~255为系统占用内存,不允许用户访问
cout << *p << endl;
system("pause");
return 0;
}
野指针:指针变量指向非法的内存空间
示例2:野指针
int main() {
//指针变量p指向内存地址编号为0x1100的空间
int * p = (int *)0x1100;
//访问野指针报错
cout << *p << endl;
system("pause");
return 0;
}
总结:空指针和野指针都不是我们申请的空间,因此不要访问。
const修饰指针
const修饰指针有三种情况
-
const修饰指针 --- 常量指针
-
const修饰常量 --- 指针常量
-
const既修饰指针,又修饰常量
示例:
int main() {
int a = 10;
int b = 10;
//const修饰的是指针,指针指向可以改,指针指向的值不可以更改
const int * p1 = &a;
p1 = &b; //正确
//*p1 = 100; 报错
//const修饰的是常量,指针指向不可以改,指针指向的值可以更改
int * const p2 = &a;
//p2 = &b; //错误
*p2 = 100; //正确
//const既修饰指针又修饰常量
const int * const p3 = &a;
//p3 = &b; //错误
//*p3 = 100; //错误
system("pause");
return 0;
}
技巧:看const右侧紧跟着的是指针还是常量, 是指针就是常量指针,是常量就是指针常量
指针和数组
核心概念
- 数组本质:连续存储多个指针变量。
- 用途:常用于处理多个字符串或动态分配的内存块。
作用: 利用指针访问数组中元素
示例:
int main() {
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int * p = arr; //指向数组的指针
cout << "第一个元素: " << arr[0] << endl;
cout << "指针访问第一个元素: " << *p << endl;
for (int i = 0; i < 10; i++)
{
//利用指针遍历数组
cout << *p << endl;
p++;
}
system("pause");
return 0;
}
指针和函数
作用: 利用指针作函数参数,可以修改实参的值(和前边形参相反)
示例:
//值传递
void swap1(int a ,int b)
{
int temp = a;
a = b;
b = temp;
}
//地址传递
void swap2(int * p1, int *p2)
{
int temp = *p1;
*p1 = *p2;
*p2 = temp;
}
int main() {
int a = 10;
int b = 20;
swap1(a, b); // 值传递不会改变实参
swap2(&a, &b); //地址传递会改变实参
cout << "a = " << a << endl;
cout << "b = " << b << endl;
system("pause");
return 0;
}
总结:如果不想修改实参,就用值传递,如果想修改实参,就用地址传递
指针、数组、函数
- 函数指针声明:
int (*fp)(int)表示指向返回int的函数的指针 - 函数指针数组:用于策略模式或注册多个处理函数
案例描述: 封装一个函数,利用冒泡排序,实现对整型数组的升序排序
例如数组:int arr[10] = { 4,3,6,9,1,2,10,8,7,5 };
示例:
//冒泡排序函数
void bubbleSort(int * arr, int len) //int * arr 也可以写为int arr[]
{
for (int i = 0; i < len - 1; i++)
{
for (int j = 0; j < len - 1 - i; j++)
{
if (arr[j] > arr[j + 1])
{
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
//打印数组函数
void printArray(int arr[], int len)
{
for (int i = 0; i < len; i++)
{
cout << arr[i] << endl;
}
}
int main() {
int arr[10] = { 4,3,6,9,1,2,10,8,7,5 };
int len = sizeof(arr) / sizeof(int);
bubbleSort(arr, len);
printArray(arr, len);
system("pause");
return 0;
}
✅ 表达式、语句、运算符
- 表达式:
a + b,x++,p[i] - 运算符:
+,-,*,/,%,&&,||,!,==,!= - 语句:控制流程结构
if,for,while,switch
✅ 数组 / 字符串
数组(Array)
- 数组是一组相同类型数据的有序集合,在内存中连续存储。
- 一维数组定义:
类型 数组名[大小];
int arr[5] = {1, 2, 3, 4, 5};
char str[10] = "Hello";
- 数组索引从 0 开始,访问方式如:arr[2] 表示第三个元素。
- 多维数组:int matrix[3][4]; 表示 3 行 4 列矩阵。
字符串(String)
- 字符串是以空字符 '\0' 结尾的字符数组。
- 定义方式:
char str1[] = "Hello"; // 自动添加 '\0'
char str2[6] = {'H','e','l','l','o','\0'}; // 手动指定
- 常用字符串函数
// 需包含头文件 <string.h>
strlen(str); // 计算长度(不含 '\0')
strcpy(dest, str); // 拷贝
strcmp(a, b); // 比较字符串
strcat(a, b); // 将 b 拼接到 a 后面
#include <string.h> char msg[20]; strcpy(msg, "Hi"); // msg: "Hi" strcat(msg, " there"); // msg: "Hi there"
✅ 结构体 / 共用体 / 枚举 / 位域
结构体
定义:
结构体是将多个不同类型的数据组合在一起的复合数据类型,用于表示实体的多个属性。
使用场景:
-
表示传感器、外设状态、网络包头等复杂数据
-
多变量统一传参,提升代码组织性
语法:
struct StructName {
int id;
float value;
char name[20];
};
示例:
struct SensorData {
int id;
float temperature;
char location[20];
};
struct SensorData s1 = {1, 36.5, "room_1"};
printf("Sensor: %d, Temp: %.1f\n", s1.id, s1.temperature);
共用体
定义:
共用体中的所有成员共享同一块内存,任何时刻只能使用其中一个成员。
使用场景:
-
节省内存:如嵌入式协议帧解析
-
多种数据格式的重解释
语法:
union UnionName {
int i;
float f;
char c;
};
示例:
union Data {
int i;
float f;
};
union Data d;
d.i = 10;
printf("i = %d\n", d.i);
d.f = 3.14;
printf("f = %.2f\n", d.f); // 修改 f 会破坏 i
注意事项:
-
占用内存大小为最大成员的大小
-
修改一个成员后,其他成员的值不可预测
枚举(Enumeration)
定义:
枚举是一种用户自定义的数据类型,用于定义一组命名的整数常量。
使用场景:
-
定义状态机状态
-
表示设备运行模式、错误码
语法:
enum Color { RED, GREEN, BLUE };
enum Color color = GREEN;
示例:
enum State {
STATE_IDLE,
STATE_ACTIVE,
STATE_ERROR = 100, // 可手动赋值
STATE_SLEEP
};
enum State current = STATE_ACTIVE;
printf("State: %d\n", current); // 输出 1
特性:
-
默认从 0 开始递增
-
可强制设定起始值
位域(Bit Field)
定义:
位域用于结构体中,定义每个字段占用的比特位数,实现更细粒度的内存控制。
使用场景:
-
配置寄存器映射
-
网络协议比特位字段解析
-
内存空间紧张场景
语法:
struct Flags {
unsigned int ready : 1;
unsigned int error : 1;
unsigned int mode : 2;
};
示例:
struct Flags f;
f.ready = 1;
f.error = 0;
f.mode = 3; // 占2位,最大为11(二进制)即3
printf("ready = %d, mode = %d\n", f.ready, f.mode);
注意事项:
-
位域不能取地址(&f.ready 不合法)
-
字段数值不能超过位数范围(2^n - 1)
-
与具体编译器实现密切相关(跨平台需小心)
✅ 位操作
- 嵌入式开发中用于设置寄存器位、控制硬件
#define LED_PIN (1 << 2) PORT |= LED_PIN; // 置位 PORT &= ~LED_PIN; // 清零 PORT ^= LED_PIN; // 翻转
✅ 关键语义 & 修饰符
const(只读限定符)
const int a = 10; void print(const char* msg); // msg 不可修改
volatile(防止优化)
volatile int *reg = (int *)0x40021000; // 硬件寄存器访问
static(静态变量/内部链接)
static int count = 0; // 静态变量,函数调用间保留值
extern(外部变量声明)
extern int global_var;
register(提示变量存放寄存器)
register int speed;
auto(默认局部变量)
auto int a = 10; // 一般可省略 auto
✅ 内存存储类型与生命周期
| 存储类型 | 生命周期 | 作用域 | 关键字 |
|---|---|---|---|
| 栈(stack) | 函数调用期间 | 局部变量 | auto |
| 静态区 | 程序全程 | 局部/全局 | static |
| 堆(heap) | 手动管理 | 全局 | malloc/free |
| 寄存器 | 函数调用期间 | 局部 | register |
✅ 编译与调试基础
C 编译四阶段(以 GCC 为例)
gcc -E main.c -o main.i # 预处理 gcc -S main.c -o main.s # 编译为汇编 gcc -c main.c -o main.o # 汇编为目标文件 gcc main.o -o main # 链接生成可执行文件
Makefile 示例
CC = gcc TARGET = app OBJS = main.o utils.o $(TARGET): $(OBJS) $(CC) -o $@ $^ %.o: %.c $(CC) -c $< -o $@ clean: rm -f *.o $(TARGET)
GCC 编译参数
| 参数 | 含义 |
|---|---|
-Wall |
开启所有警告 |
-g |
含调试信息 |
-O2 |
优化等级 |
-I |
头文件路径 |
-L/-l |
库路径和链接 |
-D |
宏定义 |
GDB 基础调试
gdb ./main (gdb) break main (gdb) run (gdb) next / step (gdb) print var (gdb) continue
内联汇编
int result;
__asm__ __volatile__ (
"movl $5, %%eax;"
"movl $3, %%ebx;"
"addl %%ebx, %%eax;"
"movl %%eax, %0;"
: "=r"(result)
:
: "%eax", "%ebx"
);
printf("result = %d\n", result); // 输出 8
排序算法
冒泡排序(Bubble Sort)
原理:
相邻元素两两比较,把最大的“冒”到最后。
时间复杂度:
- 最坏/平均:O(n²)
- 最好:O(n)(加优化判断)
适用场景:
数据量小、逻辑简单、嵌入式环境友好
示例代码:
void bubble_sort(int arr[], int n) {
for (int i = 0; i < n - 1; ++i) {
int swapped = 0;
for (int j = 0; j < n - i - 1; ++j) {
if (arr[j] > arr[j+1]) {
int t = arr[j]; arr[j] = arr[j+1]; arr[j+1] = t;
swapped = 1;
}
}
if (!swapped) break; // 优化:已排序
}
}
选择排序(Selection Sort)
原理:
每轮从未排序区间中选择最小值放到前面。
时间复杂度:
- 所有情况:O(n²)
适用场景:
嵌入式设备中内存访问代价高,交换少
示例代码:
void selection_sort(int arr[], int n) {
for (int i = 0; i < n - 1; ++i) {
int min = i;
for (int j = i + 1; j < n; ++j) {
if (arr[j] < arr[min])
min = j;
}
if (min != i) {
int t = arr[i]; arr[i] = arr[min]; arr[min] = t;
}
}
}
插入排序(Insertion Sort)
原理:
每次将一个元素插入到已排序部分的合适位置。
时间复杂度:
- 最坏/平均:O(n²)
- 最好:O(n)
适用场景:
数据量小、数据基本有序时表现好
示例代码:
void insertion_sort(int arr[], int n) {
for (int i = 1; i < n; ++i) {
int key = arr[i], j = i - 1;
while (j >= 0 && arr[j] > key) {
arr[j+1] = arr[j];
j--;
}
arr[j+1] = key;
}
}
快速排序(Quick Sort)
原理:
分治法。选定基准,左边小于它,右边大于它。
时间复杂度:
- 最坏:O(n²)
- 平均:O(n log n)
适用场景:
高性能需求、数据量较大(慎用递归栈)
示例代码:
int partition(int arr[], int low, int high) {
int pivot = arr[high], i = low - 1;
for (int j = low; j < high; ++j) {
if (arr[j] < pivot) {
++i;
int t = arr[i]; arr[i] = arr[j]; arr[j] = t;
}
}
int t = arr[i+1]; arr[i+1] = arr[high]; arr[high] = t;
return i + 1;
}
void quick_sort(int arr[], int low, int high) {
if (low < high) {
int p = partition(arr, low, high);
quick_sort(arr, low, p - 1);
quick_sort(arr, p + 1, high);
}
}
归并排序(Merge Sort)
原理:
分治法。将数组分成两半排序后合并。
时间复杂度:
- 所有情况:O(n log n)
适用场景:
追求稳定排序、高精度处理、实时传感器数据等(需额外内存)
示例代码:
void merge(int arr[], int l, int m, int r) {
int n1 = m-l+1, n2 = r-m;
int L[n1], R[n2];
for (int i = 0; i < n1; ++i) L[i] = arr[l+i];
for (int j = 0; j < n2; ++j) R[j] = arr[m+1+j];
int i = 0, j = 0, k = l;
while (i < n1 && j < n2)
arr[k++] = (L[i] <= R[j]) ? L[i++] : R[j++];
while (i < n1) arr[k++] = L[i++];
while (j < n2) arr[k++] = R[j++];
}
void merge_sort(int arr[], int l, int r) {
if (l < r) {
int m = (l + r) / 2;
merge_sort(arr, l, m);
merge_sort(arr, m+1, r);
merge(arr, l, m, r);
}
}
堆排序(Heap Sort)
原理:
利用堆结构(大根堆),反复取出最大元素构造有序序列。
时间复杂度:
- 所有情况:O(n log n)
适用场景:
嵌入式中对最值处理(最大温度等)、优先级调度
示例代码:
void heapify(int arr[], int n, int i) {
int largest = i, l = 2*i + 1, r = 2*i + 2;
if (l < n && arr[l] > arr[largest]) largest = l;
if (r < n && arr[r] > arr[largest]) largest = r;
if (largest != i) {
int t = arr[i]; arr[i] = arr[largest]; arr[largest] = t;
heapify(arr, n, largest);
}
}
void heap_sort(int arr[], int n) {
for (int i = n/2 - 1; i >= 0; i--) heapify(arr, n, i);
for (int i = n-1; i > 0; i--) {
int t = arr[0]; arr[0] = arr[i]; arr[i] = t;
heapify(arr, i, 0);
}
}
📌 总结对比表
| 排序算法 | 时间复杂度(平均) | 空间复杂度 | 稳定性 | 嵌入式适用性 |
|---|---|---|---|---|
| 冒泡排序 | O(n²) | O(1) | ✅ | ✅(小数据) |
| 选择排序 | O(n²) | O(1) | ❌ | ✅ |
| 插入排序 | O(n²) | O(1) | ✅ | ✅(近似有序) |
| 快速排序 | O(n log n) | O(log n) | ❌ | ⚠️(递归栈) |
| 归并排序 | O(n log n) | O(n) | ✅ | ⚠️(额外内存) |
| 堆排序 | O(n log n) | O(1) | ❌ | ✅ |
第二层:嵌入式系统基础知识
🔹 嵌入式系统概览
📌 嵌入式系统定义与特点
定义:
- 专用性:针对特定任务优化,如汽车 ABS 防抱死系统仅负责刹车控制。
- 嵌入性:隐藏于设备内部,用户通常意识不到其存在(如微波炉中的控制系统)。
- 计算系统:包含硬件(处理器、传感器)和软件(固件、驱动)。
特点:
- 资源受限(低功耗、内存小)
- 实时性要求高
- 高可靠性与稳定性
- 通常运行裸机或 RTOS
📌 系统构成(MCU、存储器、传感器、外设)
| 模块 | 功能说明 |
|---|---|
| MCU | 核心控制器,如 ARM Cortex-M |
| Flash/SRAM | 存储程序代码与运行数据 |
| 外设 | GPIO、UART、SPI、I2C、ADC、PWM 等 |
| 传感器 | 温湿度、光照、姿态等 |
| 通信模块 | WiFi、BLE、LoRa、CAN、NB-IoT 等 |
| 电源管理 | 电池、LDO、DC-DC,支持低功耗模式 |
+--------------------------+
| MCU |
| +----------------------+ |
| | Flash / RAM | |
| | GPIO / UART / ADC | |
| +----------------------+ |
+-----------|--------------+
|
+------+------+
| 外设 / 传感器 |
+-------------+
嵌入式系统的硬件构成是一个有机整体,各模块协同工作实现特定功能。以下从核心组件到通信架构进行深度解析:
MCU(微控制器):嵌入式系统的心脏
1. 核心功能
- 运算与控制:执行程序指令,处理传感器数据,控制外设。
- 典型架构:
- ARM Cortex-M:主流低功耗MCU(如STM32、Nordic nRF系列)。
- RISC-V:开源架构,灵活定制(如SiFive、平头哥玄铁系列)。
- 8051/AVR:传统8位MCU,低成本(如Arduino Uno基于ATmega328P)。
2. 关键参数
- 主频:决定运算速度(如STM32F103主频72MHz,STM32H7主频480MHz)。
- 内核位数:8位(适合简单控制)、32位(主流)、64位(高性能应用)。
- 片上外设:集成ADC、DAC、PWM等功能模块,减少外部芯片依赖。
存储器:程序与数据的载体
1. Flash存储器
- 功能:存储程序代码(固件),掉电不丢失。
- 分类:
- NOR Flash:读取速度快,支持XIP(就地执行),适合代码存储。
- NAND Flash:容量大、成本低,适合存储大量数据(如SSD、SD卡)。
- 典型应用:
- MCU内置Flash(如STM32F407含1MB Flash)。
- 外部SPI Flash(如W25Q系列,用于存储文件系统或配置参数)。
2. SRAM(静态随机存取存储器)
- 功能:运行时数据存储(如变量、堆栈)。
- 特点:速度快(纳秒级访问),但成本高、容量小。
- 容量配置:
- 小型MCU:几KB~几十KB(如Arduino Uno含2KB SRAM)。
- 高性能MCU:数百KB~MB级(如STM32H7含1MB SRAM)。
3. 其他存储类型
- EEPROM:可擦写可编程只读存储器,适合存储少量关键参数(如设备ID)。
- FRAM:铁电随机存储器,读写速度快、寿命长(10^12次擦写),用于数据记录。
外设接口:与外部世界的桥梁
1. GPIO(通用输入输出)
- 功能:数字信号输入/输出(如控制LED、读取按键状态)。
- 特性:
- 可配置上拉/下拉电阻。
- 支持中断触发(如外部按键按下时唤醒MCU)。
2. 通信接口
| 接口 | 特点 | 典型应用 |
|---|---|---|
| UART | 全双工,异步,2线(TX/RX) | 调试信息输出、与模块通信(如GPS) |
| SPI | 全双工,同步,4线(SCK/MOSI/MISO/CS) | 高速数据传输(如OLED屏幕) |
| I2C | 半双工,同步,2线(SCL/SDA) | 多设备通信(如连接多个传感器) |
| CAN | 差分信号,抗干扰强,多主模式 | 汽车电子(如ECU间通信) |
3. 模拟接口
- ADC(模拟-to-数字转换器):
- 将模拟信号(如电压、电流)转换为数字值。
- 精度通常为10~16位(如STM32的12位ADC,分辨率4096级)。
- DAC(数字-to-模拟转换器):
- 将数字值转换为模拟电压输出(如音频信号生成)。
4. 定时控制
- PWM(脉冲宽度调制):
- 通过占空比控制输出电压平均值,用于电机调速、LED调光。
- 频率范围:几Hz~MHz(如舵机控制需50Hz PWM)。
传感器:感知物理世界的窗口
1. 常见类型
- 环境传感器:
- 温湿度:DHT22、SHT30(精度±0.3°C)。
- 光照:BH1750(测量范围1~65535 lux)。
- 气压:BMP280(海拔测量误差±1m)。
- 运动传感器:
- 加速度计:ADXL345(检测倾斜、振动)。
- 陀螺仪:MPU6050(测量角速度,用于姿态解算)。
- 其他:
- 气体传感器:MQ-2(检测烟雾、液化气)。
- 红外传感器:HC-SR501(人体感应)。
2. 接口方式
- 数字接口:I2C(如SHT30)、SPI(如ADXL345)。
- 模拟接口:输出电压值,需通过MCU的ADC转换(如模拟光照传感器)。
通信模块:连接万物的纽带
1. 短距离通信
- WiFi:
- 标准:802.11b/g/n(如ESP8266、ESP32)。
- 应用:智能家居(如智能插座)、数据上传至云端。
- BLE(蓝牙低功耗):
- 传输距离:10~100m,功耗极低(如Nordic nRF52系列)。
- 应用:可穿戴设备(如心率带)、Beacon定位。
2. 中长距离通信
- LoRa:
- 扩频技术,传输距离5~15km,低功耗(如Semtech SX1278)。
- 应用:物联网广域覆盖(如智能抄表)。
- NB-IoT:
- 蜂窝网络,覆盖全国,功耗极低(如Quectel BC660K)。
- 应用:远程监控(如井盖状态监测)。
3. 工业总线
- CAN总线:
- 传输速率:500kbps~1Mbps,抗干扰强。
- 应用:汽车电子(如车身控制模块)、工业自动化。
- Modbus:
- 主从协议,支持RS-232/RS-485,广泛用于工业设备通信。
电源管理:续航与稳定性的保障
1. 电源转换
- LDO(低压差线性稳压器):
- 优点:电路简单,输出纹波小(如AMS1117-3.3)。
- 缺点:效率低(输入输出压差越大,效率越低)。
- DC-DC转换器:
- 升压/降压,效率高(可达90%以上,如TPS62160)。
- 适合电池供电设备(如充电宝)。
2. 低功耗设计
- 休眠模式:
- MCU进入休眠,关闭非必要外设,仅保留唤醒源(如RTC时钟)。
- 典型功耗:STM32L4系列休眠电流低至0.5μA。
- 动态电压调整:
- 根据工作负载动态降低MCU电压(如ARM Cortex-M4的FlexPowerControl)。
系统集成与典型架构
1. 最小系统
- MCU + 晶振 + 复位电路 + 电源。
- 示例:STM32最小系统板(核心板)。
2. 扩展架构
┌─────────────────────────────────────────┐
│ 应用层 │
│ (用户逻辑:如温湿度采集、数据处理) │
├─────────────────────────────────────────┤
│ 驱动层 │
│ (传感器驱动、通信协议栈、外设控制) │
├─────────────────────────────────────────┤
│ 硬件层 │
│ MCU ── 存储器 ── 外设 ── 传感器 ── 通信 │
└─────────────────────────────────────────┘
3. 低功耗设计案例
more like this