【STM32系列教程】0x0A SPI

通过SPI外设操作W25Qxx片外Flash

介绍

概述

教程:

https://www.bilibili.com/video/BV1th411z7sn?p=36

  • 四根通信线:SCK, MOSI, MISO, SS/CS/NSS
  • 所有从机共享主机引出的同一根SCK, MOSI, MISO, 每个设备独享一个CS
  • 同步,全双工,无应答机制

通信过程

  • 开始条件:主机将SS置为低电平
  • 只有一个操作:主机和从机的移位寄存器交换数据,在一个时钟边沿(上升沿或下降沿,可配置)上,两方的移位寄存器同时把最高位输出到数据线上;在另一个时钟边沿时再同时从对方发送的数据线上接收1bit数据存到移位寄存器的最高位。
  • 如果芯片是半双工通信,那么主机仅发送时忽略接收的数据,主机仅接收时发送0x000xff
  • 停止条件:主机将SS置为高电平

SPI外设

image-20240702135634948
  • 一个寄存器:DR。实际写入时写到TDR,读取时从RDR读取
  • 每个时钟将最高位移出到MOSI,从MISO移入最低位
  • 在结束时,将移位寄存器中接收到的字节移入RDR,从TDR移入下一个要发送的字节
  • TDR数据移入移位寄存器时:置TXE=1,供中断、DMA使用
  • 移位寄存器数据移入RDR时:置RXNE=1,供中断、DMA使用

HAL库

使用方法和之前I2C的差不多,看命名就能看出来,这里不多做介绍

记得无论是调用Transmit函数还是Receive函数,实际上都是发生移位操作(TransmitReceive

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Polling mode 
HAL_SPI_Transmit()
HAL_SPI_Receive()
HAL_SPI_TransmitReceive()

// Interrupt mode
HAL_SPI_Transmit_IT()
HAL_SPI_Receive_IT()
HAL_SPI_TransmitReceive_IT()

// DMA mode
HAL_SPI_Transmit_DMA()
HAL_SPI_Receive_DMA()
HAL_SPI_TransmitReceive_DMA()

// Callbacks for BOTH interrupt mode and dma mode
HAL_SPI_TxCpltCallback()
HAL_SPI_RxCpltCallback()
HAL_SPI_TxRxCpltCallback()
HAL_SPI_TxHalfCpltCallback()
HAL_SPI_RxHalfCpltCallback()
HAL_SPI_TxRxHalfCpltCallback()
HAL_SPI_ErrorCallback()
HAL_SPI_AbortCpltCallback()

实践:25Qxx的读写擦

W25QXX FLASH芯片

https://www.bilibili.com/video/BV1th411z7sn?p=37

https://www.winbond.com/hq/support/documentation/downloadV2022.jsp?__locale=en&xmlPath=/support/resources/.content/item/DA00-W25Q32JV_1.html&level=1

简介

  • NOR Flash 储存芯片
  • 24bit 地址位宽

储存空间

  • 芯片分为64KB的Block,Block分为4KB的Sector
  • 256Byte的写入缓冲区,传输结束后进入忙碌状态并且将数据写入Flash
  • 额外的3个Status Register,包括忙碌状态、允许写入、写保护等功能
image-20240702114527545

指令

  • 在datasheet中的Instruction Set Table章节,描述的非常详细

  • 不同品牌的FLASH指令有少许不同,但是主要的指令是一样的。比如我使用的NM25Q128没有Unique ID

  • 分为普通SPI指令和DSPI、QSPI指令,DSPI、QSPI使用更多的数据线来传输地址和数据

写入

  • 每次写入、擦除FLASH前都需要写使能
  • 每个块写入前需要被擦除(填充1),擦除的最小单位为Sector
  • 每个数据只能由1改为0,不能由0改为1
  • 写入超过页尾的数据会回到页首覆写
  • 写入结束后芯片进入忙碌状态,将数据写入Flash,不响应除了读取状态寄存器以外的操作

读取

  • 直接读取,没有页的限制,无需使能
  • 有普通读取和快速读取两种指令。普通读取需要

状态和配置寄存器

三个状态/配置寄存器

  • R1

    • BUSY: FLASH正忙,只会接受读寄存器写入/擦除挂起指令

    • WEL: 启用写入指令置1,执行写入、擦除、禁用指令后置0

    • BP0,BP1,BP2: FLASH写入保护

    • TB: FLASH写入保护

    • SEC: FLASH写入保护

    • CMP: FLASH写入保护

    • SRP: 状态寄存器写入保护

  • R2

    • SRL: 状态寄存器写入保护
    • QE: 将引脚复用至IO2&IO3,并允许使用QSPI模式
    • LB1, LB2, LB3
    • CMP: FLASH写入保护
    • SUS: 有1个被挂起的擦除或写入任务
  • R3

    • WPS: FLASH写入保护
    • DRV1, DRV2: 输出驱动能力

有一些保留的位没有使用,数据手册建议读取时忽略其值,写入时写0。

状态寄存器写入保护功能:SRP, SRL

  • SRL=0, SRP=0: 状态寄存器在WEL=1时可写入
  • SRL=0, SRP=1: 由WP引脚控制状态寄存器是否可写入
  • SRL=1: 状态寄存器在下次上电前不能写入,或永久不能写入

闪存写入保护功能:BPx, SEC, CMP, WPS,具体查看数据手册中的表

  • WPS: 0-使用寄存器中的配置进行保护; 1: 使用独立保护锁,上电默认为上锁
  • TB: 0-保护顶部(默认); 1-保护底部
  • SEC: 0-保护64KB块; 1-保护4KB扇区
  • CMP: 0-正常保护(默认); 1-反转保护,认为只读的块为可写,可写的块为只读
  • BPx: 选择保护的块

SPI模式*

https://docs.espressif.com/projects/esptool/en/latest/esp32/advanced-topics/spi-flash-modes.html

https://www.jblopen.com/qspi-nor-flash-part-3-the-quad-spi-protocol/

由于一般SPI在读写FLASH的时候太慢,所以扩展出了 Dual SPI 和 Quad SPI

Option Mode Name Pins Used Speed (ESP device)
qio Quad I/O 4 pins used for address & data Fastest.
qout Quad Output 4 pins used for data. Approx 15% slower than qio.
dio Dual I/O 2 pins used for address & data Approx 45% slower than qio.
dout Dual Output 2 pins used for data. Approx 50% slower than qio.
  • 通常用在FLASH读写上
  • 由于其不会全双工运行,所以MOSI/MISO可以被复用为IO0、IO1
  • 其原理是在传输地址和数据时使用更多的IO接口同时发送/接收,但是指令只会在一条线上被发送

CubeMX配置

  • 启用SPI,选择PIN
  • 配置SPI外设参数
  • 启用两条DMA通道
  • 启用NVIC SPI中断和DMA中断

Todo…

操作过程和之前差不多,相信可以自己完成的…