SPI 在微控制器与非易失性存储器通信中的应用详解(附源码)
SPI接口在嵌入式系统中广泛应用于MCU与存储器的数据交互,具有高速、全双工、简单可靠的特点。文章详细介绍了SPI通信架构、协议时序及典型存储器指令集,重点解析了数据读写、扇区擦除等关键操作的代码实现,并提出了速率优化、数据校验、电源管理等可靠性设计方案。SPI接口特别适合固件存储、参数配置等需要高速稳定数据存取的应用场景。
文章目录
概要
在嵌入式系统中,SPI(Serial Peripheral Interface)凭借其高速率、全双工、简单可靠的特性,成为微控制器(MCU)与非易失性存储器(如 AT25 系列 EEPROM、NOR FLASH)之间数据交互的首选接口。这类存储器广泛用于存储固件、配置参数和关键数据,SPI 接口则为它们提供了高效的读写通道。
一、SPI 通信系统架构
1. 基本连接方式
微控制器与非易失性存储器的 SPI 连接采用主从架构,典型硬件连接如下:
核心信号线功能:
SCK:串行时钟线,由主设备产生,控制数据传输时序
MOSI:主设备输出 / 从设备输入线,主设备向存储器发送命令和数据
MISO:主设备输入 / 从设备输出线,存储器向主设备返回数据
CS:片选线,主设备通过拉低该引脚选中特定存储器(多设备时用于地址区分)
2. 多设备扩展
当系统需要多个 SPI 存储器时,通过独立的 CS 线实现设备选择:
二、SPI 通信协议与存储器指令集
1. 基本通信时序
SPI 通信采用同步时钟机制,数据在 SCK 的边沿(上升沿或下降沿)被采样,常见时序模式有四种(由 CPOL 和 CPHA 决定),存储器通常支持其中 1-2 种:
CPOL=0,CPHA=0:SCK 空闲时为低电平,数据在 SCK 上升沿被采样
CPOL=0,CPHA=1:SCK 空闲时为低电平,数据在 SCK 下降沿被采样
CPOL=1,CPHA=0:SCK 空闲时为高电平,数据在 SCK 下降沿被采样
CPOL=1,CPHA=1:SCK 空闲时为高电平,数据在 SCK 上升沿被采样
AT25 系列 EEPROM 和 NOR FLASH 通常采用 CPOL=0,CPHA=0 模式。
2. 标准通信流程
MCU 与 SPI 存储器的通信遵循 “命令 - 地址 - 数据” 的交互模式:
主设备拉低 CS 引脚,选中存储器
主设备通过 MOSI 发送操作命令(1-4 字节,如读、写、擦除等)
主设备发送操作地址(根据存储器容量,通常为 2-4 字节)
数据传输阶段(读操作时从设备通过 MISO 返回数据,写操作时主设备通过 MOSI 发送数据)
操作完成后,主设备拉高 CS 引脚,结束通信
3. 常用存储器指令集
SPI 非易失性存储器定义了标准化的指令集,以下为典型指令:
三、关键操作实现详解
1. 数据读取操作
以 AT25DF081 NOR FLASH 为例,读取数据的具体实现:
// 从SPI存储器读取数据
// addr: 起始地址
// buf: 接收缓冲区
// len: 读取长度
void spi_memory_read(uint32_t addr, uint8_t *buf, uint32_t len) {
// 1. 拉低片选,开始通信
SPI_CS_LOW();
// 2. 发送读命令(0x03)
spi_send_byte(0x03);
// 3. 发送3字节地址(高位在前)
spi_send_byte((addr >> 16) & 0xFF); // 地址高8位
spi_send_byte((addr >> 8) & 0xFF); // 地址中8位
spi_send_byte(addr & 0xFF); // 地址低8位
// 4. 读取数据
for (uint32_t i = 0; i < len; i++) {
buf[i] = spi_receive_byte();
}
// 5. 拉高片选,结束通信
SPI_CS_HIGH();
}
// 快速读实现(适用于高速读取,如固件加载)
void spi_memory_fast_read(uint32_t addr, uint8_t *buf, uint32_t len) {
SPI_CS_LOW();
// 发送快速读命令(0x0B)
spi_send_byte(0x0B);
// 发送3字节地址
spi_send_byte((addr >> 16) & 0xFF);
spi_send_byte((addr >> 8) & 0xFF);
spi_send_byte(addr & 0xFF);
// 发送1字节dummy数据(为了满足时序要求)
spi_send_byte(0x00);
// 读取数据
for (uint32_t i = 0; i < len; i++) {
buf[i] = spi_receive_byte();
}
SPI_CS_HIGH();
}
2. 数据写入操作
非易失性存储器的写入需要遵循特定流程(写使能→写入数据→等待完成):
// 等待存储器空闲
void spi_memory_wait_idle(void) {
uint8_t status;
do {
SPI_CS_LOW();
spi_send_byte(0x05); // 读状态寄存器命令
status = spi_receive_byte();
SPI_CS_HIGH();
} while (status & 0x01); // 检查忙标志位(bit0)
}
// 发送写使能命令
void spi_memory_write_enable(void) {
SPI_CS_LOW();
spi_send_byte(0x06); // 写使能命令
SPI_CS_HIGH();
}
// 页写入操作(注意:不能跨页写入)
// AT25系列EEPROM通常每页256字节
void spi_memory_page_write(uint32_t addr, const uint8_t *buf, uint32_t len) {
// 1. 检查长度是否超过一页
if (len > 256) len = 256;
// 2. 发送写使能命令
spi_memory_write_enable();
// 3. 开始页写入
SPI_CS_LOW();
// 4. 发送页写命令(0x02)
spi_send_byte(0x02);
// 5. 发送3字节地址
spi_send_byte((addr >> 16) & 0xFF);
spi_send_byte((addr >> 8) & 0xFF);
spi_send_byte(addr & 0xFF);
// 6. 发送数据
for (uint32_t i = 0; i < len; i++) {
spi_send_byte(buf[i]);
}
// 7. 结束写入
SPI_CS_HIGH();
// 8. 等待写入完成
spi_memory_wait_idle();
}
// 多页写入(自动处理跨页情况)
void spi_memory_write(uint32_t addr, const uint8_t *buf, uint32_t len) {
uint32_t page_remain; // 当前页剩余空间
while (len > 0) {
// 计算当前页剩余字节数
page_remain = 256 - (addr % 256);
if (page_remain > len) {
page_remain = len;
}
// 写入一页数据
spi_memory_page_write(addr, buf, page_remain);
// 更新地址、缓冲区和剩余长度
addr += page_remain;
buf += page_remain;
len -= page_remain;
}
}
3. 扇区擦除操作(NOR FLASH 特有)
NOR FLASH 在写入前需要先擦除(擦除后所有位为 1):
// 擦除指定扇区(NOR FLASH)
// 不同型号扇区大小不同,AT25DF081为4KB/扇区
void spi_memory_sector_erase(uint32_t sector_addr) {
// 1. 发送写使能命令
spi_memory_write_enable();
// 2. 发送扇区擦除命令(0xD8)
SPI_CS_LOW();
spi_send_byte(0xD8);
// 3. 发送扇区地址
spi_send_byte((sector_addr >> 16) & 0xFF);
spi_send_byte((sector_addr >> 8) & 0xFF);
spi_send_byte(sector_addr & 0xFF);
// 4. 结束命令
SPI_CS_HIGH();
// 5. 等待擦除完成(扇区擦除可能需要几百毫秒)
spi_memory_wait_idle();
}
// 芯片全擦除
void spi_memory_chip_erase(void) {
spi_memory_write_enable();
SPI_CS_LOW();
spi_send_byte(0xC7); // 全擦除命令
SPI_CS_HIGH();
spi_memory_wait_idle(); // 全擦除可能需要几秒
}
四、性能优化与可靠性设计
1. 速率优化
SPI 通信速率可根据存储器支持范围调整,优化传输效率:
AT25 系列 EEPROM 通常支持最高 10MHz
现代 NOR FLASH 可支持 50MHz 甚至 100MHz
固件加载等场景使用最高速率,配置参数读写可使用低速率
2. 数据完整性保障
校验机制:重要数据附加 CRC 或校验和,确保读写正确性
重试机制:通信失败时重试(最多 3 次),提高可靠性
写保护:通过硬件引脚或软件指令设置写保护,防止误操作
3. 电源管理
空闲时可通过指令使存储器进入低功耗模式(如 AT25 的 Deep Power-Down 模式)
上电后等待稳定再进行通信(通常延迟 10ms)
掉电检测:检测到电源异常时立即停止写入操作
五、典型应用场景
1. 固件存储与升级
NOR FLASH 常用于存储嵌入式系统固件:
系统启动时,MCU 通过 SPI 从 NOR FLASH 读取固件并加载到 RAM
支持固件在线升级(通过 SPI 写入新固件镜像)
通常采用双分区设计(运行区 + 升级区),保证升级安全性
2. 配置参数存储
EEPROM 适合存储频繁读写的配置参数:
设备 ID、网络配置、用户偏好等
相比 FLASH,EEPROM 无需擦除即可写入,且擦写次数更高(可达 100 万次)
采用磨损均衡算法,延长使用寿命
3. 数据日志记录
在低速率场景下,SPI 存储器可用于记录系统日志:
温度、湿度等环境数据
设备运行状态和错误信息
采用循环存储方式,满了自动覆盖最早数据
六、与其他接口的对比

SPI 在平衡速率、布线复杂度和可靠性方面表现优异,成为现代嵌入式系统中存储器接口的主流选择。
总结
SPI 接口为微控制器与非易失性存储器(EEPROM、NOR FLASH)提供了高效可靠的数据交互方式,其核心优势在于:
高速率传输,适合固件加载等大数据量场景
简单的硬件实现,仅需 4 根线即可完成通信
灵活的多设备扩展能力,通过片选线区分不同设备
全双工通信,支持同时收发数据
在实际应用中,需根据存储器特性(页大小、擦除粒度、速率支持)设计相应的读写算法,并关注数据完整性和可靠性保障,以构建稳定的嵌入式存储系统。
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐
所有评论(0)