tags: [source-summary] type: source source: "尚硅谷嵌入式技术之STM32单片机(进阶篇)V1.0.1 — SPI/FSMC章节 + 配套代码36~42" author: "尚硅谷研究院" date: 2026-07-15
用生活理解:SPI 是全双工的同步通信,就像两个人用两根线同时说话和听。四条线各司其职:SCK(节奏)、MOSI(你说)、MISO(我听)、NSS(点名)。FSMC 就是芯片的外部数据总线,把外部设备(Flash/SRAM/LCD)映射到 STM32 的地址空间,像访问内部变量一样访问外部设备。
SPI = Serial Peripheral Interface(串行外设接口)。
| 信号 | 功能 |
|---|---|
| SCK | 时钟线(由主机产生) |
| MOSI | 主机输出/从机输入 |
| MISO | 主机输入/从机输出 |
| NSS/CS | 从机选择(低电平有效) |
| 模式 | CPOL | CPHA | 特性 |
|---|---|---|---|
| 0 | 0 | 0 | SCK空闲低,第一个边沿采集 |
| 1 | 0 | 1 | SCK空闲低,第二个边沿采集 |
| 2 | 1 | 0 | SCK空闲高,第一个边沿采集 |
| 3 | 1 | 1 | SCK空闲高,第二个边沿采集 |
与 W25Q64 Flash 通信常用模式 0(CPOL=0, CPHA=0)或模式 3(CPOL=1, CPHA=3)
| 寄存器 | 功能 |
|---|---|
| CR1 | 控制(CPOL/CPHA、BR波特率分频、MSTR主从、SPE使能、LSBFIRST) |
| CR2 | 控制(SSOE、TXEIE/RXNEIE中断使能) |
| SR | 状态(BSY忙、TXE发送空、RXNE接收非空) |
| DR | 数据寄存器(发送/接收共用) |
参考:参考手册 §23(SPI 寄存器描述)
| 参数 | 值 |
|---|---|
| 容量 | 8MB(64Mbit) |
| 接口 | SPI |
| 页 | 256 字节 |
| 扇区 | 4KB(16 页) |
| 擦除 | 扇区擦除(20ms)、块擦除、全擦除 |
| 写寿命 | 100,000 次 |
常用指令: | 指令 | 代码 | 功能 | |------|------|------| | WREN | 0x06 | 写使能 | | RDSR | 0x05 | 读状态寄存器 | | PAGE_PROG | 0x02 | 页编程(256字节) | | READ | 0x03 | 读数据 | | SECTOR_ERASE | 0xD8 | 扇区擦除(4KB) |
文件:stm32_base/36_spi_software_register/Hardware/SPI/spi.h
#define SCK_HIGH (GPIOA->ODR |= GPIO_ODR_ODR5)
#define SCK_LOW (GPIOA->ODR &= ~GPIO_ODR_ODR5)
#define MOSI_HIGH (GPIOA->ODR |= GPIO_ODR_ODR7)
#define MOSI_LOW (GPIOA->ODR &= ~GPIO_ODR_ODR7)
#define MISO_READ (GPIOA->IDR & GPIO_IDR_IDR6)
#define CS_HIGH (GPIOA->ODR |= GPIO_ODR_ODR4)
#define CS_LOW (GPIOA->ODR &= ~GPIO_ODR_ODR4)
// SPI 模式0:CPOL=0, CPHA=0
// 空闲时SCK=低, 上升沿采集数据
void SPI_Init(void)
{
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
// PA5(SCK)+PA7(MOSI): 推挽输出
// PA6(MISO): 浮空输入
// PA4(CS): 推挽输出
}
uint8_t SPI_SendByte(uint8_t byte)
{
for (uint8_t i = 0; i < 8; i++)
{
if (byte & 0x80) MOSI_HIGH; else MOSI_LOW; // MSB first
byte <<= 1;
SCK_HIGH; // 上升沿 → 从机采集
if (MISO_READ) byte |= 1; // 读取MISO
SCK_LOW; // 下降沿 → 从机改变数据
}
return byte;
}
FSMC = Flexible Static Memory Controller(灵活的静态存储器控制器)。
| Bank | 地址范围 | 目标设备 |
|---|---|---|
| Bank1 | 0x60000000~0x6FFFFFFF | NOR Flash / PSRAM / SRAM / LCD |
| Bank2 | 0x70000000~0x7FFFFFFF | NAND Flash |
| Bank3 | 0x80000000~0x8FFFFFFF | NAND Flash |
| Bank4 | 0x90000000~0x9FFFFFFF | PC Card |
Bank1 又分为 4 个子区(NE1~NE4),每个 64MB:
NE1: 0x60000000~0x63FFFFFF
NE2: 0x64000000~0x67FFFFFF
NE3: 0x68000000~0x6BFFFFFF
NE4: 0x6C000000~0x6FFFFFFF
| 寄存器 | 功能 |
|---|---|
| BC1~4 | 存储片控制寄存器(MTYP设备类型、MWID数据宽度) |
| BT1~4 | 存储片时序寄存器(ADDSET地址建立、DATAST数据保持) |
LCD 应用:将 LCD 的 D0~D15 数据线接 FSMC_D0~D15,RS 接 A0(地址线), 这样:
*(uint16_t*)0x60000000 = data → 写命令(RS=0)*(uint16_t*)0x60020000 = data → 写数据(RS=1,A0=1)参考:参考手册 §19(FSMC 寄存器描述)
| SPI操作 | 代码 |
|---|---|
| 使能SPI | `CR1 |
| 发送1字节 | DR = byte; while(!(SR & TXE)) |
| 接收1字节 | while(!(SR & RXNE)); byte = DR |
| 波特率分频 | CR1.BR[2:0] = fPCLK/2~(fPCLK/256) |
| FSMC操作 | 代码 |
|---|---|
| 写SRAM | *(uint16_t*)0x60000000 = data |
| 读SRAM | data = *(uint16_t*)0x60000000 |
| 写LCD命令 | *(uint16_t*)0x60000000 = cmd |
| 写LCD数据 | *(uint16_t*)0x60020000 = data |