天天看点

stm32 spi slave_SPI专题(二)——STM32驱动FLASH(W25Q64)

stm32 spi slave_SPI专题(二)——STM32驱动FLASH(W25Q64)

我的 github:

https://github.com/lovewinds13/stm32f013_study。

STM32F103 相关代码均提交到此仓库。

1. 硬件连接

W25Q64 将 8M 的容量分为 128 个块(Block),每个块大小为 64K 字节,每个块又分为 16个扇区(Sector),每个扇区 4K 个字节。

W25Q64 的最少擦除单位为一个扇区,也就是每次必须擦除 4K 个字节。操作需要给 W25Q64 开辟一个至少 4K 的缓存区,对 SRAM 要求比较高,要求芯片必须有 4K 以上 SRAM 才能很好的操作。

stm32 spi slave_SPI专题(二)——STM32驱动FLASH(W25Q64)

W25Q64 的擦写周期多达 10W 次,具有 20 年的数据保存期限,支持电压为 2.7~3.6V,W25Q64 支持标准的 SPI,还支持双输出/四输出的 SPI,最大 SPI 时钟可以到 80Mhz(双输出时相当于 160Mhz,四输出时相当于 320M)。

1.1  硬件连接

与 STM32 的引脚连接如下:这里是使用SPI1配置。

stm32 spi slave_SPI专题(二)——STM32驱动FLASH(W25Q64)
STM32引脚 对应SPI功能
PA2 片选CS
PA5 时钟SCK
PA6 MISO
PA7 MOSI

STM32 的 SPI 功能很强大, SPI 时钟最多可以到 18Mhz,支持 DMA,可以配置为 SPI 协议或者 I2S 协议(仅大容量型号支持)。

1.2 SPI 通讯的通讯时序

SPI 协议定义了通讯的起始和停止信号、数据有效性、时钟同步等环节。

我们以读取 FLASH 的状态寄存器的时序图分析一下,时序图也是书写软件模拟时序的依据。

stm32 spi slave_SPI专题(二)——STM32驱动FLASH(W25Q64)

如上图,我们知道书写 FLASH (来自华邦 W25X 手册)支持的是模式 0 (CPOL = 0 && CPHA == 0) 和 模式 3(CPOL = 1 && CPHA == 1)

CS、SCK、MOSI 信号都由主机控制产生,而 MISO 的信号由从机产生,主机通过该信号线读取从机的数据。MOSI 与 MISO 的信号只在 CS 为低电平的时候才有效,在 SCK 的每个时钟周期 MOSI 和 MISO 传输一位数据。

1.2.1.  通讯的起始和停止信号

在上图,CS 信号线由高变低,为 SPI 通讯的起始信号。CS 是每个从机各自独占的信号线,当从机在自己的 CS 线检测到起始信号后,就知道自己被主机选中了,开始准备与主机通讯。当 CS 信号由低变高,为 SPI 通讯的停止信号,表示本次通讯结束,从机的选中状态被取消。

1.2.2. 数据有效性

SPI 使用 MOSI 及 MISO 信号线来传输数据,使用 SCK 信号线进行数据同步。

stm32 spi slave_SPI专题(二)——STM32驱动FLASH(W25Q64)

MOSI 及 MISO 数据线在 SCK 的每个时钟周期传输一位数据。数据传输时,MSB 先行或 LSB 先行并没有作硬性规定,但要保证两个 SPI 通讯设备之间使用同样的协定,一般都会采用图中的 MSB 先行模式。

观察上图,可知模式 0 和 3 都是在上升沿读取数据。

示例:FLASH 读取 JEDEC_ID (0x9F),SPI 模式 0,,频率 f = 1MHz。

stm32 spi slave_SPI专题(二)——STM32驱动FLASH(W25Q64)

读取 JEDEC_ID  时,FLASH 回复的一个字:0xC8。

stm32 spi slave_SPI专题(二)——STM32驱动FLASH(W25Q64)

1.2.3 STM32 SPI外设

STM32 的 SPI 外设可用作通讯的主机及从机,支持最高的 SCK 时钟频率为 f pclk  / 2 (STM32F103 型号的芯片默认 f pclk1 为 72MHz,f pclk2 为 36MHz),完全支持 SPI 协议的 4 种模式,数据帧长度可设置为 8 位或 16 位,可设置数据 MSB 先行或 LSB 先行。它还支持双线全双工、双线单向以及单线模式。

SPI架构:

stm32 spi slave_SPI专题(二)——STM32驱动FLASH(W25Q64)

通讯引脚 :

SPI 的所有硬件架构都从上图中左 MOSI、MISO、SCK及 NSS 线展开的。

STM32 芯片有多个 SPI 外设,它们的 SPI 通讯信号引出到不同的 GPIO 引脚上,使用时必须配置到这些指定的引脚。

2. 软件配置

这里使用 STM32 的 SPI1 的主模式,SPI 相关的库函数和定义分布在文件 stm32f10x_spi.c 以及头文件 stm32f10x_spi.h 中。

2.1 配置相关引脚的复用功能

第一步就要使能 SPI1 的时钟, SPI1 的时钟通过 APB2ENR 的第 12 位来设置。其次要设置 SPI1 的相关引脚为复用输出,这样才会连接到 SPI1 上否则这些 IO 口还是默认的状态,也就是标准输入输出口。这里我们使用的是 PA5、 PA6、 PA7  这 3 个(SCK、 MISO、 MOSI、CS 使用软件管理方式),所以设置这三个为复用 IO。

宏定义:

IO 配置:

2.2 初始化 SPI1,设置 SPI1 工作模式

接下来初始化 SPI1,设置 SPI1 为主机模式,设置数据格式为 8 位,然设置 SCK 时钟极性及采样方式。并设置 SPI1 的时钟频率(最大 18Mhz),以及数据的格式(MSB 在前还是 LSB 在前)。这在库函数中是通过 SPI_Init 函数来实现。

函数原型:

void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct);
           

第一个参数是 SPI 标号,第二个参数结构体类型 SPI_InitTypeDef 为相关属性设置。

SPI_InitTypeDef 的定义如下:

参数 解释
SPI_Direction 设置 SPI 的通信方式,可以选择为半双工,全双工,以及串行发和串行收方式
SPI_Mode 设置 SPI 的主从模式,主机模式 (SPI_Mode_Master),从机模式 (PI_Mode_Slave)。
SPI_DataSiz 数据为 8 位还是 16 位帧格式选择项。SPI_DataSize_8b(8 位),SPI_DataSize_16b (16位)
SPI_CPOL 设置时钟极性
SPI_CPHA 设置时钟相位,也就是选择在串行同步时钟的第几个跳变沿(上升或下降)数据被采样,可以为第一个或者第二个条边沿采集
SPI_NSS 设置 NSS 信号由硬件(NSS 管脚)还是软件控制
SPI_BaudRatePrescaler 设置 SPI 波特率预分频值也就是决定 SPI 的时钟的参数 ,从不分频道 256 分频 8 个可选值 ,选择 256 分频值SPI_BaudRatePrescaler_256, 传输速度为 36M/256=140.625KHz。
SPI_FirstBit 设置数据传输顺序是 MSB 位在前还是 LSB 位在前。SPI_FirstBit_MSB (高位在前)
SPI_CRCPolynomial 设置 CRC 校验多项式,提高通信可靠性,大于 1 即可

初始化的范例格式为:

2.3 SPI 传输数据

通信接口需要有发送数据和接受数据的函数,固件库提供的发送数据函数原型为:

void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);
           

往 SPIx 数据寄存器写入数据 Data,从而实现发送。

固件库提供的接受数据函数原型为:

这从 SPIx 数据寄存器读出接收到的数据。

收发单个字节数据:

收发多个字节数据:

2.4 查看 SPI 传输状态

在 SPI 传输过程中,要判断数据是否传输完成,发送区是否为空等等状态,

通过函数 SPI_I2S_GetFlagStatus 实现的,判断发送是否完成的方法是:

3. SPI FLASH 操作

3.1 宏定义部分

3.2 中间层函数封装

注明: 此部分函数的封装是为了统一硬件 SPI 和软件模拟 SPI 接口。

//--------------------------------------------------------------------------------------------------------//    函 数 名: hal_spi_send_bytes//    功能说明: SPI 发送数据,包含软件和硬件通信方式//    形    参:     mode:通信方式选择(0:软件SPI;1:硬件SPI)//                pbdata:发送数据的首地址//                send_length:发送数据长度//    返 回 值: 执行状态(true or false)//    日    期: 2020-03-12//    备    注: 中间层封装底层接口//    作    者: by 霁风AI//--------------------------------------------------------------------------------------------------------uint8_t hal_spi_send_bytes(uint8_t mode, uint8_t *pbdata, uint16_t send_length)
{if (mode == 0)
    {for (uint16_t i = 0; i         {
            Spi_WriteByte(pbdata[i]);
        }return true;
    }else if (mode == 1)
    {
        spi_master_send_some_bytes(1, pbdata, send_length);//        for (uint16_t i = 0; i //        {//            spi_master_send_recv_byte(1, pbdata[i]);//        }return true;
    }else 
    {return false;
    }
}//--------------------------------------------------------------------------------------------------------//    函 数 名: hal_spi_recv_bytes//    功能说明: SPI 接收数据,包含软件和硬件通信方式//    形    参:     mode:通信方式选择(0:软件SPI;1:硬件SPI)//                pbdata:发送数据的首地址//                send_length:发送数据长度//    返 回 值: 执行状态(true or false)//    日    期: 2020-03-12//    备    注: 中间层封装底层接口//    作    者: by 霁风AI//--------------------------------------------------------------------------------------------------------uint8_t hal_spi_recv_bytes(uint8_t mode, uint8_t *pbdata, uint16_t recv_length)
{if (mode == 0)
    {for (uint16_t i = 0; i         {
             *pbdata++ = Spi_ReadByte();    //软件模拟SPI
        }   return true;
    }else if (mode == 1)
    {
        spi_master_recv_some_bytes(1, pbdata, recv_length);    //硬件SPI//        for (uint16_t i = 0; i //        {//            *pbdata++ = spi_master_send_recv_byte(1, 0xFF);//        }return true;
    }else 
    {return false;
    }
}
           

关于软件模拟 SPI 部分代码,参看:

软件模拟SPI代码:

(https://github.com/lovewinds13/stm32f013_study/blob/master/Driver/src/drvsfspi.c) 。

此处不再贴出。

3.3 FLASH 部分