12-SPI通信与FSMC总线.md 5.2 KB


tags: [source-summary] type: source source: "尚硅谷嵌入式技术之STM32单片机(进阶篇)V1.0.1 — SPI/FSMC章节 + 配套代码36~42" author: "尚硅谷研究院" date: 2026-07-15

created: 2026-07-15

SPI通信与FSMC总线

用生活理解:SPI 是全双工的同步通信,就像两个人用两根线同时说话和听。四条线各司其职:SCK(节奏)、MOSI(你说)、MISO(我听)、NSS(点名)。FSMC 就是芯片的外部数据总线,把外部设备(Flash/SRAM/LCD)映射到 STM32 的地址空间,像访问内部变量一样访问外部设备。


SPI 通信协议

SPI = Serial Peripheral Interface(串行外设接口)。

四线制

信号 功能
SCK 时钟线(由主机产生)
MOSI 主机输出/从机输入
MISO 主机输入/从机输出
NSS/CS 从机选择(低电平有效)

四种模式(CPOL/CPHA)

模式 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)

SPI 关键寄存器

寄存器 功能
CR1 控制(CPOL/CPHA、BR波特率分频、MSTR主从、SPE使能、LSBFIRST)
CR2 控制(SSOE、TXEIE/RXNEIE中断使能)
SR 状态(BSY忙、TXE发送空、RXNE接收非空)
DR 数据寄存器(发送/接收共用)

参考:参考手册 §23(SPI 寄存器描述)


W25Q64 Flash

参数
容量 8MB(64Mbit)
接口 SPI
256 字节
扇区 4KB(16 页)
擦除 扇区擦除(20ms)、块擦除、全擦除
写寿命 100,000 次

常用指令: | 指令 | 代码 | 功能 | |------|------|------| | WREN | 0x06 | 写使能 | | RDSR | 0x05 | 读状态寄存器 | | PAGE_PROG | 0x02 | 页编程(256字节) | | READ | 0x03 | 读数据 | | SECTOR_ERASE | 0xD8 | 扇区擦除(4KB) |


软件设计(软件模拟 SPI 写 Flash)

文件: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 总线

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

FSMC 关键寄存器

寄存器 功能
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

常见问题与避坑

  1. SPI 接收数据为 0xFF → 检查 MISO 连接、从机是否选中(CS)、时钟是否匹配
  2. W25Q64 写入失败 → 必须先发 WREN(0x06)、扇区必须先擦除(不能写覆盖)
  3. FSMC 读写时序不对 → 参考 SRAM/LCD 数据手册的时序参数配置 ADDSET/DATAST
  4. 多个 SPI 设备冲突 → 各设备独立 CS、SCK/MOSI/MISO 共享
  5. FSMC 地址线错位 → 16位模式下 HADDR[0] 不连接到 FSMC_A[0](内部是对齐的)