·20 分钟
嵌入式系统基础:从 MCU 到 RTOS
嵌入式MCURTOS单片机ARM嵌入式
什么是嵌入式系统
嵌入式系统是一种专用的计算机系统,通常嵌入到更大的设备中,执行特定的任务。与通用计算机不同,嵌入式系统通常具有以下特点:
- 专用性:针对特定任务设计
- 实时性:对时间有严格要求
- 资源受限:内存、处理能力有限
- 可靠性:长时间稳定运行
MCU 基础
1. 什么是 MCU
MCU(Microcontroller Unit,微控制器)是将 CPU、内存、外设集成在单个芯片上的微型计算机。
code
┌─────────────────────────────────────┐
│ MCU 芯片 │
│ ┌─────────┐ ┌─────────────────┐ │
│ │ CPU │ │ Flash │ │
│ │ (ARM │ │ (程序存储) │ │
│ │ Cortex) │ │ │ │
│ └─────────┘ └─────────────────┘ │
│ ┌─────────┐ ┌─────────────────┐ │
│ │ RAM │ │ 外设 │ │
│ │ (数据 │ │ (GPIO, UART, │ │
│ │ 存储) │ │ SPI, I2C...) │ │
│ └─────────┘ └─────────────────┘ │
└─────────────────────────────────────┘
2. 常见 MCU 架构
| 架构 | 代表芯片 | 特点 |
|---|---|---|
| ARM Cortex-M | STM32, NXP | 高性能,生态完善 |
| AVR | ATmega328P | Arduino 常用,易上手 |
| PIC | PIC16/18 | Microchip 出品,工业应用多 |
| RISC-V | ESP32-C3 | 开源架构,新兴力量 |
| 8051 | STC89C52 | 经典架构,教学常用 |
3. STM32 系列介绍
STM32 是 ST 公司推出的基于 ARM Cortex-M 内核的 MCU 系列:
c
// STM32 命名规则示例
STM32F103C8T6
│ │ │ │ │ │
│ │ │ │ │ └── 封装类型
│ │ │ │ └──── Flash 大小 (8 = 64KB)
│ │ │ └────── 引脚数 (C = 48脚)
│ │ └──────── 产品类型 (03 = 基础型)
│ └──────────── 内核 (F = Cortex-M3)
└──────────────── 系列前缀
开发环境搭建
1. 硬件准备
- 开发板:STM32F103C8T6 最小系统板
- 调试器:ST-Link V2 或 J-Link
- 连接线:杜邦线、USB 线
2. 软件环境
bash
# Keil MDK 安装
# 1. 下载 Keil MDK-ARM
# 2. 安装 STM32F1xx 设备包
# 3. 注册 license
# STM32CubeMX 安装
# 1. 下载 STM32CubeMX
# 2. 安装固件包
# 3. 配置工程
3. 工程创建流程
code
1. STM32CubeMX 配置
├── 选择芯片型号
├── 配置时钟树
├── 配置外设
└── 生成代码
2. Keil MDK 开发
├── 打开工程
├── 编写业务代码
├── 编译下载
└── 调试测试
GPIO 编程
1. GPIO 工作模式
c
// GPIO 模式定义
typedef enum {
GPIO_Mode_AIN = 0x00, // 模拟输入
GPIO_Mode_IN_FLOATING = 0x04, // 浮空输入
GPIO_Mode_IPD = 0x28, // 下拉输入
GPIO_Mode_IPU = 0x48, // 上拉输入
GPIO_Mode_Out_OD = 0x14, // 开漏输出
GPIO_Mode_Out_PP = 0x10, // 推挽输出
GPIO_Mode_AF_OD = 0x1C, // 复用开漏
GPIO_Mode_AF_PP = 0x18 // 复用推挽
} GPIOMode_TypeDef;
2. LED 闪烁示例
c
#include "stm32f10x.h"
// LED 连接在 PC13
#define LED_PIN GPIO_Pin_13
#define LED_PORT GPIOC
void LED_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct;
// 使能 GPIOC 时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
// 配置 GPIO
GPIO_InitStruct.GPIO_Pin = LED_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(LED_PORT, &GPIO_InitStruct);
}
void Delay_ms(uint32_t ms) {
uint32_t i;
while(ms--) {
i = 7200; // 72MHz 时钟
while(i--);
}
}
int main(void) {
LED_Init();
while(1) {
GPIO_SetBits(LED_PORT, LED_PIN); // LED 亮
Delay_ms(500);
GPIO_ResetBits(LED_PORT, LED_PIN); // LED 灭
Delay_ms(500);
}
}
中断系统
1. 中断优先级
c
// 中断优先级配置
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1; // 抢占优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0; // 子优先级
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
2. 外部中断示例
c
// 按键外部中断配置
void EXTI_Key_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct;
EXTI_InitTypeDef EXTI_InitStruct;
// 使能时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
// 配置 GPIO 为上拉输入
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOA, &GPIO_InitStruct);
// 配置中断线
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
EXTI_InitStruct.EXTI_Line = EXTI_Line0;
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStruct);
}
// 中断服务函数
void EXTI0_IRQHandler(void) {
if(EXTI_GetITStatus(EXTI_Line0) != RESET) {
// 按键处理逻辑
GPIO_ToggleBits(GPIOC, GPIO_Pin_13);
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
定时器
1. 定时器分类
| 定时器类型 | 功能 | 典型应用 |
|---|---|---|
| 基本定时器 | 定时、计数 | 延时、周期任务 |
| 通用定时器 | 输入捕获、输出比较 | PWM、编码器 |
| 高级定时器 | 互补输出、死区控制 | 电机控制 |
2. 定时器中断示例
c
// TIM2 定时 1ms 中断配置
void TIM2_Init(void) {
TIM_TimeBaseInitTypeDef TIM_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
// 使能时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
// 定时器配置
TIM_InitStruct.TIM_Period = 1000 - 1; // 自动重装载值
TIM_InitStruct.TIM_Prescaler = 72 - 1; // 预分频器
TIM_InitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_InitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_InitStruct);
// 使能更新中断
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
// NVIC 配置
NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
// 启动定时器
TIM_Cmd(TIM2, ENABLE);
}
// 定时器中断服务函数
void TIM2_IRQHandler(void) {
if(TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) {
// 1ms 定时任务
// ...
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
串口通信
1. UART 配置
c
// USART1 初始化
void USART1_Init(uint32_t baudrate) {
GPIO_InitTypeDef GPIO_InitStruct;
USART_InitTypeDef USART_InitStruct;
// 使能时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
// TX - PA9
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct);
// RX - PA10
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStruct);
// USART 配置
USART_InitStruct.USART_BaudRate = baudrate;
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
USART_InitStruct.USART_StopBits = USART_StopBits_1;
USART_InitStruct.USART_Parity = USART_Parity_No;
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStruct);
USART_Cmd(USART1, ENABLE);
}
// 发送字符
void USART_SendChar(USART_TypeDef* USARTx, char c) {
USART_SendData(USARTx, c);
while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET);
}
// 发送字符串
void USART_SendString(USART_TypeDef* USARTx, const char* str) {
while(*str) {
USART_SendChar(USARTx, *str++);
}
}
下一篇
接下来我们将深入探讨嵌入式 C 语言编程技巧,包括位操作、内存管理和代码优化等内容。