STM32HAL库 -- SPI 读写 FLASH实验(速记版)

本章使用 STM32F429 的 SPI 功能,实现对外部 FLASH 的读写并把结果显示在 TFTLCD 模块。

SPI 介绍

SPI 框图

        SPI 是英语 Serial Peripheral interface 缩写,顾名思义就是串行外围设备接口。SPI 通信协议是 Motorola 公司首先在其 MC68HCXX 系列处理器上定义的。SPI 接口是一种高速的全双工同步的通信总线,已经广泛应用在众多 MCU、存储芯片、AD 转换器和 LCD 之间。大部分 STM32 有 3 个SPI 接口,本实验使用的是 SPI5。

SPI 的引脚信息

MISO(Master In / Slave Out)主设备数据输入,从设备数据输出。
MOSI(Master Out / Slave In)主设备数据输出,从设备数据输入。
SCLK(Serial Clock)时钟信号,由主设备产生。
CS(Chip Select)从设备片选信号,由主设备产生。

SPI 的工作原理

        在主机和从机都有一个串行移位寄存器,主机通过向它的 SPI 串行寄存器写入一个字节来发起一次传输。串行移位寄存器通过 MOSI 信号线将字节传送给从机,从机也将自己的串行移位寄存器中的内容通过 MISO 信号线返回给主机。这样,两个移位寄存器中的内容就被交换。外设的写操作和读操作是同步完成的。如果只是进行写操作,主机只需忽略接收到的字节。反之,若主机要读取从机的一个字节,就必须发送一个空字节引发从机传输

SPI 的传输方式

        SPI 总线具有三种传输方式:全双工、单工以及半双工传输方式。
        全双工通信:在任何时刻,主机与从机之间都可以同时进行数据的发送和接收。
        单工通信:可以双向传输,但同一时刻只能为一个方向发送或接收。
        半双工通信:任何时刻,只能单向传输。

SPI 工作模式

        STM32 要与具有 SPI 接口的器件进行通信,就必须遵循 SPI 的通信协议。也就是 SPI 的读写数据时序。SPI 通信协议具备 4 种工作模式,在讲这 4 种工作模式前,首先先知道两个单词 CPOL 和 CPHA。
        CPOL,详称 Clock Polarity,就是时钟极性假如空闲状态 SCL 是高电平,CPOL=1;若空闲状态时 SCL 低电平,那么 CPOL = 0。CPHA,详称 Clock Phase,就是时钟相位。同步通信时,数据的变化和采样都是在时钟边沿上进行的,每一个时钟周期都会有上升沿和下降沿两个边沿。CPHA 实质指的是数据的采样时刻CPHA= 0 的情况就表示数据的采样是从第 1 个时钟边沿信号上即奇数边沿,具体是上升沿还是下降沿的问题,是由 CPOL 决定的。 CPHA = 1 的情况就是表示数据采样是从第 2 个边沿即偶数边沿

        数据的输出有两种情况,一种是 CS 使能的边沿,另一种是上一帧数据的最后一个时钟沿。

        由于 CPOL 和 CPHA 都有两种不同状态,所以 SPI 分成了 4 种模式。我们在开发的时候,使用比较多的是模式 0 和模式 3。 

SPI 寄存器

SPI 控制寄存器 1(SPI_CR1)

        该寄存器控制着 SPI 很多相关信息,包括主设备模式选择,传输方向,数据格式,时钟极性时钟相位和使能等。

SPI 状态寄存器(SPI_SR)

        该寄存器是查询当前 SPI 的状态的,我们在实验中用到的是 TXE 位和 RXNE 位,即发送完成接收完成是否的标记

SPI 数据寄存器(SPI_DR)

        SPI 数据寄存器,是一个双寄存器,包括了发送缓存和接收缓存。当向该寄存器写数据的时候,SPI 就会自动发送,当收到数据的时候,也是存在该寄存器内。

NOR Flash 简介

Flash 简介

        FLASH 具有容量大、可重复擦写、按“扇区/块”擦除、掉电后数据可继续保存的特性。

        常见的Flash主要有 NOR FlashNand Flash 两种类型。

        NOR 和 NAND 是两种数字门电路(或非门与非门),可以简单地认为 Flash 内部存储单元使用哪种门作存储单元就是哪类型的 Flash。U 盘,SSD,eMMC 等为 NAND 型,而 NOR Flash 则根据设计需要灵活应用于各类 PCB 上,如 BIOS,手机等。

        NOR FLASH 容量小,基于字节读写,读快写慢价格高。

        NAND FLASH 容量大,基于块读写,读慢写快价格低。

        NOR 与 NAND 在数据写入前都需要有擦除操作,但实际上 NOR Flash 的一个 bit 可以从 1 变成 0,而要从 0 变 1 就要擦除后再写入,NAND Flash 这两种情况都需要擦除。擦除操作的最小单位为“扇区/块”,这意味着有时候即使只写一字节的数据,则这个“扇区/块”上之前的数据都可能会被擦除。

        NOR 的地址线和数据线分开,它可以按“字节”读写数据,符合 CPU 的指令译码执行要求,所以假如 NOR 上存储了代码指令,CPU 给 NOR 一个地址,NOR 就能向 CPU 返回一个数据让CPU执行,中间不需要额外的处理操作,这体现于表39.1.2.1.1中的支持XIP特性(eXecute In Place)。因此可以用 NOR Flash 直接作为嵌入式 MCU 的程序存储空间

        NAND 的数据和地址线共用,只能按“块”来读写数据,假如 NAND 上存储了代码指令,CPU 给 NAND 地址后,它无法直接返回该地址的数据,所以不符合指令译码要求。

        若代码存储在 NAND 上,可以把它先加载到 RAM 存储器上,再由 CPU 执行。所以在功能上可以认为 NOR 是一种断电后数据不丢失的 RAM,但它的擦除单位与 RAM 有区别,且读写速度比 RAM 要慢得多。

        Flash 也有对应的缺点,我们在使用过程中需要尽量去规避这些问题:一是 Flash 的使用寿命,另一个是可能的位反转

        位反转是数据位写入时为 1,但经过一定时间的环境变化后可能实际变为 0 的情况,反之亦然。位反转的原因很多,可能是器件特性也可能与环境、干扰有关,由于位反转的的问题可能存在,所以 FLASH 存储器需要“探测/错误更正(EDC/ECC)”算法来确保数据的正确性。

        FLASH 芯片有很多种芯片型号,在我们的 norflash.h 头文件中有定义芯片 ID 的宏定义,对应的就是不同型号的 NOR FLASH 芯片,比如有:W25Q128、W25Q256、BY25Q128、NM25Q128,它们是来自不同的厂商的同种规格的 NOR FLASH 芯片,内存空间都是 128M 字,即 16M 字节。

FLASH 特性

        下面我们以 W25Q256 为例,认识一下具体的 NOR Flash 的特性。

        W25Q256 是一款大容量 SPI FLASH 产品,其容量为 16M。它将 16M 字节的容量分为 256 个块(Block),每个块大小为 64K 字节,每个块又分为 16 个扇区(Sector),每一个扇区 16 页,每页 256 个字节,即每个扇区 4K 个字节。W25Q256 的最小擦除单位为一个扇区,也就是每次必须擦除 4K个字节。这样我们需要给 W25Q256开辟一个至少 4K的缓存区,这样对 SRAM 要求比较高,要求芯片必须有 4K 以上 SRAM 才能很好的操作。

NOR FLASH 工作时序

        W25Q256 有写入、读取还有擦除的功能,下面就对这三种操作的时序进行分析,在后面通过代码的形式驱动它。

读操作:

        发起读操作时,先把 CS 片选管脚拉低,然后通过 MOSI 引脚把读数据指令 03H 发送给芯片,之后发送要读取的 24 位地址芯片接收完地址后把数据通过 MISO 引脚送出去。

写操作:

        发起页写指令时,先发送写使能指令,然后主机拉低片选 CS 引脚,然后通过 MOSI 引脚写指令 02H 发送到芯片,接着发送要 24 位地址,发送要写的字节数据。最后拉高 CS 引脚,停止数据传输。

擦除操作:

        W25Q256 的扇区大小是 4K 字节。擦除扇区后,扇区的位全置 1,即扇区字节为 0xFF

        发起擦除操作时,先发送写使能指令。主机先拉低 CS 引脚,然后通过 MOSI 引脚发送指令代码 20h 到芯片,然后把 24 位扇区地址发送到芯片,拉高 CS引脚。通过 SR 寄存器等待删除擦除操作完成。

SPI 的 HAL 库驱动

SPI 的初始化函数 SPI_Init,其声明如下:

HAL_StatusTypeDef HAL_SPI_Init(SPI_HandleTypeDef *hspi);
typedef struct __SPI_HandleTypeDef
{
 SPI_TypeDef *Instance; /* SPI 寄存器基地址 */
 SPI_InitTypeDef Init; /* SPI 通信参数 */
 uint8_t *pTxBuffPtr; /* SPI 的发送缓存 */
 uint16_t TxXferSize; /* SPI 的发送数据大小 */
 __IO uint16_t TxXferCount; /* SPI 发送端计数器 */
 uint8_t *pRxBuffPtr; /* SPI 的接收缓存 */
 uint16_t RxXferSize; /* SPI 的接收数据大小 */
 __IO uint16_t RxXferCount; /* SPI 接收端计数器 */
 void (*RxISR)(struct __SPI_HandleTypeDef *hspi); /* SPI 的接收端中断服务函数 */
 void (*TxISR)(struct __SPI_HandleTypeDef *hspi); /* SPI 的发送端中断服务函数 */
 DMA_HandleTypeDef *hdmatx; /* SPI 发送参数设置(DMA) */
 DMA_HandleTypeDef *hdmarx; /* SPI 接收参数设置(DMA) */
 HAL_LockTypeDef Lock; /* SPI 锁对象 */
 __IO HAL_SPI_StateTypeDef State; /* SPI 传输状态 */
 __IO uint32_t ErrorCode; /* SPI 操作错误代码 */
} SPI_HandleTypeDef;
typedef struct
{
 uint32_t Mode; /* 模式:主:SPI_MODE_MASTER 从:SPI_MODE_SLAVE */
 uint32_t Direction; /* 方向: 只接收模式 单线双向通信数据模式 全双工 */
 uint32_t DataSize; /* 数据帧格式: 8 位/16 位 */
 uint32_t CLKPolarity; /* 时钟极性 CPOL 高/低电平 */
 uint32_t CLKPhase; /* 时钟相位 奇/偶数边沿采集 */
 uint32_t NSS; /* SS 信号由硬件(NSS)管脚控制还是软件控制 */
 uint32_t BaudRatePrescaler; /* 设置 SPI 波特率预分频值*/
 uint32_t FirstBit; /* 起始位是 MSB 还是 LSB */
 uint32_t TIMode; /* 帧格式 SPI motorola 模式还是 TI 模式 */
 uint32_t CRCCalculation; /* 硬件 CRC 是否使能 */
 uint32_t CRCPolynomial; /* 设置 CRC 多项式 */
} SPI_InitTypeDef;

SPI数据传输配置步骤

1) SPI 参数初始化(工作模式、数据时钟极性、时钟相位等)。

2)使能 SPI 时钟和配置相关引脚的复用功能。

3)使能 SPI。

        通过__HAL_SPI_ENABLE 函数使能 SPI,便可进行数据传输。

4)SPI 传输数据。

        通过 HAL_SPI_Transmit 函数进行发送数据。
        通过 HAL_SPI_Receive 函数进行接收数据。
        也可以通过 HAL_SPI_TransmitReceive 函数进行发送与接收操作。

5)设置 SPI 传输速度。

        SPI 初始化结构体 SPI_InitTypeDef 有一个成员变量是 BaudRatePrescaler,该成员变量用来设置 SPI 的预分频系数,从而决定 SPI 的传输速度。但是 HAL 库并没有提供单独的 SPI 分频系数修改函数,如果需要在程序中偶尔修改速度,要通过设置 SPI_CR1 寄存器来修改。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大象荒野

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值