MCU vs MPU
嵌入式系统的核心是处理器,分为两大类:
MCU (Microcontroller Unit) 将处理器、RAM、Flash、外设接口集成在单颗芯片上。适合对成本、功耗、实时性要求高的场景。典型代表:STM32F103C8T6 (Cortex-M3)、ESP32 (Xtensa LX6)。
MPU (Microprocessor Unit) 仅包含处理器核心,需要外部搭配 DDR、Flash、电源管理芯片。运行 Linux 或 Android,适合需要复杂多媒体、网络栈、UI 的应用。典型代表:Raspberry Pi 的 BCM2711、Allwinner V3s。
选择参考:纯传感器采集和简单控制用 MCU;需要摄像头、触摸屏、网络服务器用 MPU。
裸机编程
裸机编程指不依赖操作系统,在一个主循环中轮询或中断响应处理任务。
#include <stdio.h>
#include <stdint.h>
volatile uint32_t ms_counter = 0;
// 定时器中断服务函数(伪代码)
void SysTick_Handler(void) {
ms_counter++;
}
int main(void) {
uint32_t last_blink = 0;
// 初始化GPIO
GPIO_Init(LED_PIN, OUTPUT);
while (1) {
// 每500ms翻转LED
if (ms_counter - last_blink >= 500) {
GPIO_Toggle(LED_PIN);
last_blink = ms_counter;
}
// 其他任务轮询
uint16_t adc_val = ADC_Read(TEMP_SENSOR_CH);
ProcessTemperature(adc_val);
}
}
裸机编程的优点是完全可控、资源占用极少,缺点是多任务时需要手动管理状态机。
RTOS 实时操作系统
当任务数量增多时,裸机的轮询方式很快变得难以维护。RTOS 提供任务调度、信号量、消息队列等机制。
常用 RTOS:
- FreeRTOS — 开源、轻量、生态最广,ESP-IDF 和 STM32Cube 都内置支持
- RT-Thread — 国产,IoT 友好,支持设备框架和包管理
- Zephyr — Linux 基金会项目,模块化设计,支持 500+ 开发板
FreeRTOS 示例:
#include <FreeRTOS.h>
#include <task.h>
void vSensorTask(void *pvParameters) {
TickType_t xLastWakeTime = xTaskGetTickCount();
for (;;) {
// 每100ms读取传感器
float temp = read_temperature();
send_to_display(temp);
vTaskDelayUntil(&xLastWakeTime, pdMS_TO_TICKS(100));
}
}
void vCommsTask(void *pvParameters) {
for (;;) {
// 处理网络通信
if (mqtt_connected()) {
mqtt_loop();
}
vTaskDelay(pdMS_TO_TICKS(10));
}
}
int main(void) {
hardware_init();
xTaskCreate(vSensorTask, "Sensor", 1024, NULL, 1, NULL);
xTaskCreate(vCommsTask, "Comms", 2048, NULL, 2, NULL);
vTaskStartScheduler();
// 不会执行到这里
return 0;
}
设计原则:中断服务函数应尽可能短,只做标志设置或数据入队,具体处理交给任务。
ESP32 入门
ESP32 是乐鑫科技推出的 WiFi + BLE 双模芯片,IoT 原型首选的利器。
# MicroPython on ESP32
from machine import Pin, ADC
import time
import network
import urequests
# 连接WiFi
def connect_wifi():
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect("SSID", "PASSWORD")
while not wlan.isconnected():
time.sleep(1)
print("WiFi connected:", wlan.ifconfig())
# 读取模拟传感器
adc = ADC(Pin(34))
adc.atten(ADC.ATTN_11DB) # 0-3.3V范围
connect_wifi()
while True:
value = adc.read()
voltage = value * 3.3 / 4095
print(f"Sensor: {voltage:.2f}V")
try:
urequests.get(f"http://example.com/api?val={voltage}")
except:
pass
time.sleep(5)
STM32 入门
STM32 以丰富的产品线和成熟的生态著称。使用 STM32CubeIDE 配合 HAL 库可以快速开发。
// STM32使用HAL库读取内部温度传感器
#include "stm32f1xx_hal.h"
ADC_HandleTypeDef hadc1;
void MX_ADC1_Init(void) {
ADC_ChannelConfTypeDef sConfig = {0};
__HAL_RCC_ADC1_CLK_ENABLE();
hadc1.Instance = ADC1;
hadc1.Init.ScanConvMode = DISABLE;
hadc1.Init.ContinuousConvMode = ENABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.NbrOfConversion = 1;
HAL_ADC_Init(&hadc1);
sConfig.Channel = ADC_CHANNEL_TEMPSENSOR;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
}
调试技巧
- 串口输出 — 最简单的调试方法,printf 重定向到 UART
- LED 指示灯 — 不同闪烁模式表示不同系统状态
- 逻辑分析仪 — 抓取 I2C/SPI 总线波形验证时序
- JTAG/SWD 调试 — 断点、单步执行、查看变量
小结
裸机编程适合简单任务,RTOS 适合中等复杂度的多任务系统,Linux 适合功能丰富的产品。从 Arduino 或 MicroPython 快速验证原型,再用 ESP-IDF 或 STM32Cube 做量产版本,是一条高效的路径。