嵌入式系统开发可以分为 裸机(Bare Metal) 和 RTOS(实时操作系统) 两种方式。
裸机开发意味着 没有 RTOS,程序直接运行在 MCU 上,所有任务调度 完全由开发者手动管理。裸机开发适用于 简单任务,低功耗系统,小型嵌入式设备,但当任务复杂到一定程度,就需要 RTOS 来管理。
裸机编程是 不依赖 RTOS,直接操作 MCU 硬件 的开发方式,通常采用:✅ 主循环(Super Loop) ✅ 中断驱动(Interrupt Driven) ✅ 状态机(State Machine)
int main() {
hardware_init(); // 硬件初始化(GPIO、UART、I2C、SPI)
while (1) { // 主循环
task1(); // 处理传感器数据
task2(); // 处理按键输入
task3(); // 处理显示屏刷新
}
}
这个编程起来就非常的简单,如程序所示。
说说裸机的问题->
这里总结了一些场景和用途
✅ 简单的单任务/少任务嵌入式系统 → 适合裸机 ✅ 复杂的多任务、实时性要求高的系统 → 需要 RTOS
void main() {
hardware_init(); // 硬件初始化
while (1) {
led_task();
button_task();
uart_task();
}
}
这种代码最喜欢🌶
void USART_IRQHandler() {
char data = USART_ReceiveData();
process_data(data);
}
✅ 减少 CPU 负担,数据来了才处理 ❌ 任务间通信难,如果多个中断抢占,容易丢失数据,因为中断来的 时候,当前的数据是放在栈里面。
void TIM2_IRQHandler() {
if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) {
sensor_read_task();
display_update_task();
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
✅ 定期任务调度,提高实时性 ❌ 任务增多时,定时器调度难以管理。也就是说定时器的时间戳给每个子任务使用。
typedef enum { IDLE, PROCESSING, ERROR } SystemState;
SystemState current_state = IDLE;
void main() {
while (1) {
switch (current_state) {
case IDLE:
if (button_pressed()) current_state = PROCESSING;
break;
case PROCESSING:
process_task();
current_state = IDLE;
break;
case ERROR:
error_handle();
break;
}
}
}
✅ 适用于复杂逻辑(如工控、状态机管理) ❌ 状态增多时,代码可读性下降
void Task1(void *argument) {
while (1) {
printf("任务 1 运行\n");
vTaskDelay(1000);
}
}
void Task2(void *argument) {
while (1) {
printf("任务 2 运行\n");
vTaskDelay(2000);
}
}
void main() {
xTaskCreate(Task1, "Task1", 512, NULL, 1, NULL);
xTaskCreate(Task2, "Task2", 512, NULL, 2, NULL);
vTaskStartScheduler();
}
然后我们就可以写这种任务
RTC外设有什么用? 昨天不是写了一个RTC吗?我一直对定时器情有独钟,那我们就分析一下设计一个时钟怎么做?
闹钟应用通常需要 精准的时间管理、低功耗运行、定时唤醒,最佳选择是 MCU 内部的 RTC(Real-Time Clock)外设。
如果是设计一个引爆器,就是裸机了,感觉港剧里面的起爆器太low了,剪线?笑死,我把所有的传感器都加上,复杂的判断条件,精通的定时,谁都别想活。
流程大概就是这样吧?
#include "stm32f4xx.h"
void RTC_Alarm_IRQHandler(void) {
if (RTC->ISR & RTC_ISR_ALRAF) {
RTC->ISR &= ~RTC_ISR_ALRAF; // 清除闹钟标志位
printf("闹钟响了!\n");
buzzer_on(); // 打开蜂鸣器
}
}
void RTC_SetAlarm(uint8_t hour, uint8_t min, uint8_t sec) {
RTC->ALRMAR = (hour << 16) | (min << 8) | sec; // 设置闹钟时间
RTC->CR |= RTC_CR_ALRAIE; // 使能闹钟中断
}
void main() {
RTC_Init(); // 初始化 RTC
RTC_SetAlarm(7, 30, 0); // 设置闹钟 7:30:00
enter_low_power_mode(); // 进入低功耗模式
}
✅ 闹钟触发后 MCU 唤醒,执行 buzzer_on()
响铃
❌ 只能管理一个闹钟,多个闹钟需要手动写代码管理
我们来看看RTOS,
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
TaskHandle_t AlarmTaskHandle;
typedef struct {
uint8_t hour;
uint8_t min;
uint8_t sec;
} AlarmTime_t;
QueueHandle_t xAlarmQueue; // 闹钟队列
void RTC_Alarm_IRQHandler(void) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xTaskNotifyFromISR(AlarmTaskHandle, 0, eNoAction, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
void AlarmTask(void *pvParameters) {
AlarmTime_t alarm;
while (1) {
if (xQueueReceive(xAlarmQueue, &alarm, portMAX_DELAY)) {
printf("闹钟响了!时间:%02d:%02d:%02d\n", alarm.hour, alarm.min, alarm.sec);
buzzer_on();
}
}
}
void main() {
xAlarmQueue = xQueueCreate(5, sizeof(AlarmTime_t)); // 创建闹钟队列
xTaskCreate(AlarmTask, "AlarmTask", 512, NULL, 1, &AlarmTaskHandle);
vTaskStartScheduler();
}
✅ 支持多个闹钟,闹钟时间可以存入队列
✅ 任务管理,优先级可控,实时性高
✅ 可以在 buzzer_on()
之后,延迟一定时间自动关闭蜂鸣器
总结一下