Chenyj Space
返回博客
·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-MSTM32, NXP高性能,生态完善
AVRATmega328PArduino 常用,易上手
PICPIC16/18Microchip 出品,工业应用多
RISC-VESP32-C3开源架构,新兴力量
8051STC89C52经典架构,教学常用

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 语言编程技巧,包括位操作、内存管理和代码优化等内容。