第二章 嵌入式系统基础:嵌入式处理器选型与操作系统
大家好,我是你们这门课的主讲。今天咱们聊聊嵌入式系统里最基础、也最绕不开的几个话题——处理器怎么选、操作系统怎么搭、硬件抽象层怎么设计。
说实话,我刚开始做嵌入式那会儿,也踩过不少坑。选型选错了,后面整个项目都得重来。所以这一章,我把自己这些年积累的经验,掰开了揉碎了讲给你听。
2.1 ARM Cortex-M/R/A系列:三兄弟各有所长
ARM Cortex系列,说白了就是三兄弟:M系列、R系列、A系列。它们虽然都姓ARM,但脾气秉性完全不同。
Cortex-M系列,主打低功耗、低成本。你想想看,智能手表、传感器节点、电机控制,这些对功耗敏感的场景,M系列是首选。我做过一个智能水表项目,用的就是Cortex-M4,电池撑了两年多没换。
Cortex-R系列,实时性要求高的场合。比如汽车刹车系统、工业机器人控制。R系列有硬件实时响应能力,中断延迟极低。嗯,这里要注意,R系列不是跑操作系统的首选,它更适合裸机或RTOS。
Cortex-A系列,性能怪兽。跑Linux、Android都没问题。手机、平板、路由器,都是A系列的天下。我有个朋友做智能摄像头,一开始想用M系列,结果发现视频编解码根本跑不动,最后换了A系列才搞定。
| 系列 | 典型应用 | 功耗 | 性能 | 典型主频 |
|---|---|---|---|---|
| Cortex-M | 传感器、电机控制、IoT终端 | 极低 | 中等 | 几十MHz~几百MHz |
| Cortex-R | 汽车电子、工业控制 | 较低 | 较高 | 几百MHz~1GHz |
| Cortex-A | 手机、平板、网关 | 较高 | 高 | 1GHz~3GHz |
选型口诀:M系列省电,R系列实时,A系列性能。别搞混了。
2.2 嵌入式操作系统:FreeRTOS vs uCOS-II
操作系统这块,我个人的习惯是:小项目用FreeRTOS,大项目或商业产品用uCOS-II。为什么?
FreeRTOS,开源免费,社区活跃。你随便搜个STM32的例程,十有八九都是FreeRTOS。它的任务调度、信号量、消息队列,该有的都有。我做过一个智能家居网关,跑了8个任务,FreeRTOS稳得很。
uCOS-II,商业授权,代码质量极高。它的内核设计非常严谨,任务切换时间可预测。我在军工项目里用过uCOS-II,客户要求必须通过安全认证,uCOS-II有DO-178B认证,FreeRTOS就没有。
举个例子,任务创建在FreeRTOS里是这样的:
// FreeRTOS 任务创建
xTaskCreate(
vTaskFunction, // 任务函数
"Task1", // 任务名称
128, // 栈大小(字)
NULL, // 参数
1, // 优先级
NULL // 任务句柄
);
而在uCOS-II里,写法略有不同:
// uCOS-II 任务创建
OSTaskCreate(
(void (*)(void *))TaskFunction, // 任务函数
(void *)0, // 参数
(OS_STK *)&TaskStk[TaskStkSize-1], // 栈顶指针
5 // 优先级
);
我的建议:如果你刚开始学嵌入式,先从FreeRTOS入手。等理解了任务调度、中断管理这些概念,再去看uCOS-II的源码,会有更深的理解。
2.3 硬件抽象层设计:让代码跑在任意芯片上
硬件抽象层,英文叫HAL(Hardware Abstraction Layer)。说白了,就是给上层软件一个统一的接口,底层硬件怎么变,上层代码不用改。
我曾经接手过一个项目,原来用的是STM32F103,后来要换成GD32F103。如果没有HAL层,那得把所有GPIO、UART、SPI的代码全部重写。但有了HAL层,只需要改底层驱动,上层逻辑纹丝不动。
HAL层设计的原则很简单:
- 接口统一:比如所有GPIO的初始化,都叫
HAL_GPIO_Init() - 参数抽象:用枚举或结构体,不要直接传寄存器地址
- 错误处理:每个函数返回状态码,方便调试
举个例子,一个简单的HAL层GPIO接口:
// hal_gpio.h
typedef enum {
GPIO_MODE_INPUT,
GPIO_MODE_OUTPUT,
GPIO_MODE_AF, // 复用功能
GPIO_MODE_ANALOG
} GPIO_Mode_t;
typedef struct {
uint8_t pin;
GPIO_Mode_t mode;
uint8_t pull_up_down; // 0:无, 1:上拉, 2:下拉
} GPIO_Config_t;
// 统一的初始化接口
int32_t HAL_GPIO_Init(GPIO_Config_t *config);
底层实现时,再根据具体芯片去写:
// hal_gpio_stm32.c
int32_t HAL_GPIO_Init(GPIO_Config_t *config) {
// STM32 特有的寄存器操作
RCC->AHB1ENR |= (1 << 0); // 使能GPIOA时钟
GPIOA->MODER &= ~(3 << (config->pin * 2));
GPIOA->MODER |= (config->mode << (config->pin * 2));
return 0;
}
避坑指南:我曾经在HAL层里直接用了芯片特有的寄存器名字,结果换芯片时改得想哭。记住,HAL层接口里不要出现任何芯片相关的宏或寄存器名,全部封装在底层实现里。
2.4 选型实战:一个智能温控器的故事
讲个真实案例吧。去年我帮朋友设计一个智能温控器,需求是这样的:
- 采集温度传感器数据(DS18B20)
- 控制继电器开关加热器
- 通过WiFi上传数据到云端
- 电池供电,要求续航6个月以上
处理器选型时,我首先排除了Cortex-A系列——功耗太高,电池扛不住。Cortex-R系列又太贵,杀鸡用牛刀。最后选了Cortex-M4,具体型号是STM32L4系列,低功耗模式能到几十微安。
操作系统呢?因为任务不多(采集、控制、通信),用FreeRTOS就够了。任务优先级这样分配:
- 最高优先级:温度采集(每5秒一次)
- 中等优先级:WiFi通信(非实时)
- 最低优先级:继电器控制(可延迟)
硬件抽象层,我设计了三个模块:hal_sensor、hal_relay、hal_wifi。后来客户想换成国产芯片,我只改了底层驱动,上层代码一行没动。
总结一下:选型不是越贵越好,也不是越便宜越好。关键是匹配需求。M系列+FreeRTOS+HAL层,是绝大多数嵌入式项目的黄金组合。
好了,这一章就聊到这儿。下一章咱们深入讲讲中断管理和任务调度,那才是RTOS的核心玩法。
公众号:蓝海资料掘金营,微信deep3321