通信

SPI通信协议深度解析

SPI(Serial Peripheral Interface)是一种广泛应用的高速、全双工、同步串行通信协议。本质上是主从结构的总线协议,适用于MCU与各种外设间的短距离高速通信。 SPI协议基础 硬件连接 SPI通信需要四根信号线: 信号 全称 方向 说明 SCLK Serial Clock Master→Slave 时钟信号,由主机产生 MOSI Master Out Slave In Master→Slave 主机发送数据 MISO Master In Slave Out Slave→Master 主机接收数据 CS/SS Chip Select / Slave Select Master→Slave 片选信号,选择通信的从设备 MCU (Master) Slave Device ========== ============= +-------------+ +-------------+ | SCLK|------------->|SCLK | | MOSI|------------->|MOSI | | MISO|<-------------|MISO | | CS |------------->|CS | +-------------+ +-------------+ 多从设备连接 MCU (Master) ========== +---------------+ | SCLK|-----------------------------------+ | MOSI|-----------------------------------+ | MISO|<----------------------------------+ | CS0 |-------------------+ | | CS1 |--------------------------+ | | CS2 |-----------------------------+ | +---------------+ | | | | | | | | v v v v v v +-----+-----+-----+ +-----+-----+-----+ | CS0 | CS1 | CS2 | | SS0 | SS1 | SS2 | +-----+-----+-----+ +-----+-----+-----+ 独立片选模式:每从设备独占一个CS引脚,最多可连接n个从设备 级联模式:多个从设备共享同一CS,数据线串联 SPI工作模式 SPI有四种工作模式,由时钟极性(CPOL)和时钟相位(CPHA)共同决定: ...

March 25, 2024 · 7 min · Embedded Engineer
内存

嵌入式系统内存管理详解

在嵌入式系统中,内存资源通常非常有限,合理的内存管理策略直接影响系统的稳定性、可靠性和性能。本文将深入探讨嵌入式系统中的各种内存管理技术,帮助你设计出更高效的内存使用方案。 嵌入式内存架构 在深入了解内存管理之前,先明确嵌入式系统的内存布局: +-------------------+ 0x00000000 | Flash | (代码和常量) | (只读存储器) | +-------------------+ 0xXXXXXXXX | RAM | (读写数据) | - .data | (已初始化全局变量) | - .bss | (未初始化全局变量) | - Heap | (动态分配) | - Stack | (函数调用、局部变量) +-------------------+ 0xXXXXXXXX 静态内存分配 静态内存分配在编译时确定,不会分配失败,无内存碎片,是嵌入式开发的首选方案。 全局变量与静态变量 /* * 全局变量 - 整个程序生命周期内存在 * 位于 .data 或 .bss 段 */ // 已初始化全局变量 (.data段) uint32_t system_tick = 0; char device_name[] = "STM32F407"; // 未初始化全局变量 (.bss段,系统自动清零) static uint32_t error_count; uint8_t buffer[256]; // 静态局部变量 - 函数退出后保留值 uint32_t get_sequence_id(void) { static uint32_t id = 0; // 只初始化一次 return ++id; } 常量与只读数据 /* * const数据通常放在Flash中,节省RAM空间 */ // 字符串常量 - 存储在Flash const char *HELLO_MSG = "Hello, Embedded World!"; const char *VERSION = "V1.0.0"; // 查表法 - 将计算结果预先存储 const uint16_t sin_table[361] = { 0, 28, 57, 85, 114, ... // sin(angle) * 1000 }; // 配置表 - 存储设备参数 const DeviceConfig default_config = { .baudrate = 115200, .parity = 0, .stop_bits = 1, .timeout_ms = 1000 }; 静态内存池 预分配固定大小的内存块,分配和释放速度快,无碎片: ...

March 20, 2024 · 7 min · Embedded Engineer
状态机

状态机在嵌入式开发中的应用

状态机(FSM - Finite State Machine)是嵌入式开发中最常用的设计模式之一。它能够帮助开发者将复杂的逻辑分解为清晰的状态和转换,特别适合处理按键检测、协议解析、模式切换等场景。 什么是状态机? 状态机是一种抽象的计算模型,由以下要素组成: 要素 说明 状态 (State) 系统在某一时刻的稳定条件或工作模式 事件 (Event) 触发状态转换的外部信号或内部条件 转换 (Transition) 状态之间的有向转移 动作 (Action) 状态转换时执行的操作 状态转移图 状态机可以用状态转移图直观地表示: +-----------------+ | | | IDLE |<---------------+ | | | +--------+--------+ | | | 事件:EV_KEY_PRESS | | | v | +--------+--------+ | | | | | PRESSED |-------+ | | | | | +-----------------+ | | | | 定时器超时 | | 按键释放 (消抖完成) | | | | v | +-----------------+ +--------+------>+ | | | | RELEASED |---------------------->+ | | 消抖完成,进入IDLE +-----------------+ 核心要素详解 状态定义 typedef enum { STATE_IDLE, // 空闲状态 STATE_PRESSED, // 按键按下(消抖中) STATE_ACTIVE, // 按键确认按下 STATE_RELEASED, // 按键释放(消抖中) } KeyState; 事件定义 typedef enum { EV_KEY_PRESS, // 按键按下 EV_KEY_RELEASE, // 按键释放 EV_TIMEOUT, // 定时器超时 } KeyEvent; 状态表 typedef struct { KeyState current_state; KeyEvent event; KeyState next_state; void (*action)(void); // 转换时执行的动作 } StateTransition; // 状态转换表 static const StateTransition g_trans_table[] = { {STATE_IDLE, EV_KEY_PRESS, STATE_PRESSED, Start_DebounceTimer}, {STATE_PRESSED, EV_TIMEOUT, STATE_ACTIVE, On_KeyConfirmed}, {STATE_ACTIVE, EV_KEY_RELEASE,STATE_RELEASED, Start_DebounceTimer}, {STATE_RELEASED, EV_TIMEOUT, STATE_IDLE, On_KeyReleased}, }; 实现方式 1. Switch-Case 实现 最简单直观的方式: ...

March 10, 2024 · 5 min · Embedded Engineer
电路板

嵌入式裸机程序架构设计

在嵌入式开发中,良好的架构设计是项目可维护性和可扩展性的基础。没有操作系统的情况下,如何组织代码结构显得尤为重要。本文将介绍几种常见的裸机架构模式,帮助你选择适合自己项目的方案。 常见的裸机架构模式 1. 超级循环(Super Loop) 超级循环是最简单也最直接的架构模式。主循环依次调用各任务处理函数,顺序执行所有业务逻辑。 代码示例: int main(void) { System_Init(); while (1) { Task_KeyScan(); // 按键扫描 Task_Display(); // 显示屏刷新 Task_Sensor(); // 传感器采集 Task_Communication(); // 通信处理 // 可选:加入延时控制循环频率 delay_ms(10); } } 优点: 代码结构简单,易于理解和实现 无额外资源消耗,无需调度器 中断响应确定性好 缺点: 任务耦合严重,修改一个任务可能影响其他任务 实时性差,所有任务必须等待循环执行 任务数量和复杂度受循环时间限制 适用场景: 任务简单、实时性要求不高的简单产品,如玩具、小家电控制板。 2. 前后台系统(Foreground-Background) 将紧急任务放在中断前台处理,非紧急任务在主循环后台执行。这是最常用的裸机架构模式。 代码示例: // 前台:中断服务程序 void EXTI0_IRQHandler(void) { if (EXTI_GetITStatus(EXTI_Line0) != RESET) { // 紧急事件处理,如按键按下、外部报警 g_key_pressed = 1; g_system_error = ERROR_NONE; EXTI_ClearITPendingBit(EXTI_Line0); } } void TIM2_IRQHandler(void) { if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) { // 定时器中断,标记时间基准 g_timer_flag = 1; TIM_ClearITPendingBit(TIM2, TIM_IT_Update); } } // 后台:主循环 int main(void) { System_Init(); while (1) { if (g_timer_flag) { g_timer_flag = 0; Task_10ms(); // 10ms周期任务 } if (g_key_pressed) { g_key_pressed = 0; Task_KeyProcess(); // 按键处理 } Task_Display(); // 显示屏刷新 Task_Communication(); // 通信处理 } } 优点: ...

March 5, 2024 · 3 min · Embedded Engineer