STM32F103 SPI 与W25Q64BV norFlash 驱动

STM32F103 SPI 与W25Q64BV norFlash 驱动

简介

SPI,是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。SPI接口主要应用在 EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信协议。

SPI 具有信号线少,协议简单,数据率高等优点。数据传送速率达几MB/s

Pin 脚介绍

标准的 SPI 使用 4 Pin 进行数据传送:

(1)MOSI – 主器设备数据输出,从器件数据输入

(2)MISO – 主器设备件数据输入,从器件数据输出

(3)SCLK – 时钟信号,由主器件产生, 最大为fPCLK/2,从模式频率最大为fCPU/2

(4)NSS – 从器件使能信号,由主器件控制,有的 IC 会标注为 CS (Chip select)

数据时在 CLK 时钟的驱动下,在数据线上按照一个 bit 一个 bit 的进行传送,数据可以在时钟的上升沿或者下降沿改变(或者采样)。

SPI 传输的缺点是,没有数据完整性校验,也没有流控机制。

既然称 SPI 为总线,则 SPI 就可以支持多个设备相连接,通过 CS 片选信号来指定期望通讯的设备。(多机通讯)

SPI 模式

SPI 通讯有 4 中不同的通讯模式,通信双方需要配置成为一样的模式,才能够进行正常的数据传输,这里有两个概念:

CPOL:时钟极性

CPHA:时钟相位

CPOL:(时钟极性)控制在没有数据传输时,SPI 时钟的空闲状态电平。即,定义了总线空闲的工作状态(注意,和 UART 不同,SPI 是通过 CLK 的状态来表征当前的总线状态,即不发生任何数据交互的时候,时钟信号总是没有进行翻转的)

CPOL=0,表示当SCLK=0时处于空闲态

CPOL=1,表示当SCLK=1时处于空闲态

CPHA:(时钟相位)是用来配置数据采样是在第几个边沿。

CPHA=0,表示数据采样是在第1个边沿

CPHA=1,表示数据采样是在第2个边沿

所以 CPOL 和 CPHA 的不同组合,成为了 SPI 的四种传输模式:

Mode Defination

CPOL Value

CPHA Value

Mode0

0

0

Mode1

0

1

Mode2

1

0

Mode3

1

1

SPI Mode

四种传输模式,定义了不同时刻的总线启动,以及数据发送和采样时间:

CPOL=0,CPHA=0:此时空闲态时,SCLK处于低电平,数据采样是在第1个边沿,也就是 SCLK 由低电平到高电平的跳变,所以数据采样是在上升沿,数据发送是在下降沿。

CPOL=0,CPHA=1:此时空闲态时,SCLK处于低电平,数据发送是在第1个边沿,也就是 SCLK 由低电平到高电平的跳变,所以数据采样是在下降沿,数据发送是在上升沿。

CPOL=1,CPHA=0:此时空闲态时,SCLK处于高电平,数据采集是在第1个边沿,也就是 SCLK 由高电平到低电平的跳变,所以数据采集是在下降沿,数据发送是在上升沿。

CPOL=1,CPHA=1:此时空闲态时,SCLK处于高电平,数据发送是在第1个边沿,也就是 SCLK 由高电平到低电平的跳变,所以数据采集是在上升沿,数据发送是在下降沿。

对应到波形上:

STM32 SPI 特性

STM32 上支持 3 路 SPI:

    1. 可以支持全双工的通信
    1. 支持硬件 CRC
    1. 可编程的数据顺序,MSB在前或LSB在前
    1. 主模式和从模式的快速通信
    1. 可编程的时钟极性和相位(CPOL,CPHA)
    1. 可触发中断的专用发送和接收标志
    1. 可触发中断的主模式故障、过载以及CRC错误标志
    1. 支持DMA功能的1字节发送和接收缓冲器:产生发送和接受请求

SPI 时钟

单板上使用 SPI2 进行 SPI FLASH 的操作,使用的是 APB1 的时钟,最大配置为 36MHz。

硬件连接

硬件上,通过单板的 SPI2 引脚,连接到外部的 SPI FLASH (W25Q64)

所以,在配置的时候,需要针对 SPI2 进行配置。

SPI Flash 简介

硬件单板上,连接的是 WinBond 的 W25Q64BV 的 SPI Flash,此款 Flash 的特性如下:

    1. 大小:64M-bit / 8M-byte
    1. 页 : 256B
    1. 支持 80MHz 的时钟
    1. 支持扇区擦除:Sector Erase (4K-bytes)
    1. 支持块擦除:Block Erase (32K and 64K-bytes)
    1. 支持页写入:0~256-bytes
    1. 软件/硬件写保护

由于暂时不需要硬件写保护和Hold功能,故,直接将 WP和HOLD引脚接到 VCC(3.3V)

(此款SPI FLASH 还支持双线和4线 QSPI 的读写,由于 STM32 不支持,所以不在多说)

根据 W25Q64BV 的 Datasheet 描述,在操作这块 FLASH 的时候,需要配置主机为:

    1. SPI Mode 0 或者 Mode 3
    1. MSB 先传输

故,在 SPI2 配置的时候,需要进行对应的配置,才能够继续正常数据通信。

W25Q64BV 存在两个寄存器可以被访问,为 Status Register-1 和 Status Register-2,其中描述的关于 Write-Protect 的部分,暂时不管。与读写相关的就是 BUSY 位了,因为对 SPI Flash 编程后,Flash 需要内部的 cylce 进行数据的写入,内部program的时候会将 BUSY 置成 1,写入完成后,会将 BUSY 位置 0,故,每次对 Flash 进行写(包括擦除)之前,均要进行 BUSY 位的判断。

好啦,现在就开始按照 DateSheet 进行配置我们的 STM32 了。

STM32 SPI2 配置

配置过程主要分为两步:GPIO 的配置,SPI2 的配置(如原理图所示,PB_12 的 GPIO 用于了 CS 片选,我们需要将其配置成为输出的 GPIO,拉低的时候,选中 Flash,拉高的时候释放 Flash 信号)

1. 开启 GPIO B 组的时钟

2. 开启 SPI2 的时钟

3. 按照 STM32 手册,配置 SCK 、MOSI 和 NSS 为复用推挽输出、MISO为浮空输入(有的代码将 MISO 配置成为的输出,虽然也可以运行,不过,您不觉得别扭么?还是遵循 Spec 的来吧)。同时将 GPIO_B _12配置为输出(CS信号)

4. 配置 SPI2 为全双工模式

5. SPI2 为 Master

6. SPI2 运行在 MODE3(按照 W25Q64BV 的 Timing 要求 )

7. SPI2 NSS 为软件模式(根本没用)

8. 预分频系数为 4 分频(APB1 为 36M,则 SPI2 的 SCK 为 9 MHz)

9. SPI2 传输 MSB(按照 W25Q64BV 的 Timing 要求)

10. 不启用 CRC

11. 开启 SPI 功能

此刻 SPI 的配置就基本完成了。接下来就是 按照 W25Q64BV 的 Timing 要求,写 FLASH 驱动咯.....

    1. #define SK_SPI_FLASH_CS_HIGH() GPIO_SetBits(GPIOB, GPIO_Pin_12)
    1. #define SK_SPI_FLASH_CS_LOW() GPIO_ResetBits(GPIOB, GPIO_Pin_12)
    1. /*******************************************************************************
    1. * Function Name : SK_SPIPortInit
    1. * Description : Configure the I/O port for SPI2.
    1. * Input : None
    1. * Output : None
    1. * Return : None
    1. *******************************************************************************/
    1. static void _SK_SPI2PortInit(void)
    1. {
    1. GPIO_InitTypeDef stGpioInit;
    1. /*!< Configure pins: SCK */
    1. stGpioInit.GPIO_Pin = GPIO_Pin_13;
    1. stGpioInit.GPIO_Speed = GPIO_Speed_50MHz;
    1. stGpioInit.GPIO_Mode = GPIO_Mode_AF_PP;
    1. GPIO_Init(GPIOB, &stGpioInit);
    1. /*!< Configure pins: MISO */
    1. stGpioInit.GPIO_Pin = GPIO_Pin_14;
    1. stGpioInit.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    1. GPIO_Init(GPIOB, &stGpioInit);
    1. /*!< Configure pins: MOSI */
    1. stGpioInit.GPIO_Pin = GPIO_Pin_15;
    1. stGpioInit.GPIO_Speed = GPIO_Speed_50MHz;
    1. stGpioInit.GPIO_Mode = GPIO_Mode_AF_PP;
    1. GPIO_Init(GPIOB, &stGpioInit);
    1. /*!< Configure pins: CS */
    1. stGpioInit.GPIO_Pin = GPIO_Pin_12;
    1. stGpioInit.GPIO_Speed = GPIO_Speed_50MHz;
    1. stGpioInit.GPIO_Mode = GPIO_Mode_Out_PP;
    1. GPIO_Init(GPIOB, &stGpioInit);
    1. }
    1. /*******************************************************************************
    1. * Function Name : _SK_SPI2BusInit
    1. * Description : Configure the SPI2 Bus to adpte the W25Q64 Flash.
    1. * Input : None
    1. * Output : None
    1. * Return : None
    1. *******************************************************************************/
    1. static void _SK_SPI2BusInit(void)
    1. {
    1. SPI_InitTypeDef SPI_InitStructure;
    1. SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    1. SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
    1. SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
    1. SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
    1. SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
    1. SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
    1. SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; // PCLK = 36M, SPI2 CLK = PCLK/4 = 9M
    1. SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
    1. SPI_InitStructure.SPI_CRCPolynomial = 7;
    1. SPI_Init(SPI2, &SPI_InitStructure);
    1. SPI_Cmd(SPI2, ENABLE);
    1. }
    1. /*******************************************************************************
    1. * Function Name : SK_SPIInit
    1. * Description : Initializes the peripherals used by the SPI FLASH driver.
    1. * Input : None
    1. * Output : None
    1. * Return : None
    1. *******************************************************************************/
    1. void SK_SPIFlashInit(void)
    1. {
    1. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    1. RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
    1. _SK_SPI2PortInit();
    1. SK_SPI_FLASH_CS_HIGH();
    1. _SK_SPI2BusInit();
    1. }

W25Q64BV Flash 驱动

W25Q64BV 是 Flash 嘛,最主要的就是读和写。当然,除了这些,还有擦除功能,读 ID 等等。不着急,一步一步来,都是套路。

STM32 在进行 SPI 全双工数据传输的时候,在通过 MOSI 写出去数据后,可以立马进行 MISO 数据读取,通过轮询 TXE 标志来判断数据已经全部加载到移位寄存器,通过轮询 RXE 标志来得知本次 MISO 的数据已经全部到账。

针对 W25Q64BV Flash,Datasheet 中列出了支持的多种不同的命令:

对应上述表格,有不同的 Timing 进行描述,比如:Opcode 为 0x9F 的时候,是读取一个叫 JEDEC ID 的东西:

可以看到,主机首先将 CS 拉低,然后主机处在 Mode 0 或者 Mode 3 的时候,在 MOSI 信号上输出 0x9F 的数据(命令),然后接着写入 Dummy Data (随便写点东西),然后再 MISO 信号上就能够收到 manufacture ID 的信息(为 0xEF),在继续写入 Dummy Data,继续接收,继续写入,继续接收,这样便可以得到期望的数据了。

获取 Flash ID 信息

根据上述方式,便可以获取 ID 信息:

    1. /********************** W25Q64 Flash Command Defination ***********************/
    1. #define SPI_FLASH_PerWritePageSize 256
    1. #define W25X_WriteEnable 0x06
    1. #define W25X_WriteDisable 0x04
    1. #define W25X_ReadStatusReg_1 0x05
    1. #define W25X_ReadStatusReg_2 0x35
    1. #define W25X_WriteStatusReg 0x01
    1. #define W25X_ReadData 0x03
    1. #define W25X_FastReadData 0x0B
    1. #define W25X_FastReadDual 0x3B
    1. #define W25X_PageProgram 0x02
    1. #define W25X_64K_BlockErase 0xD8
    1. #define W25X_32K_BlockErase 0x52
    1. #define W25X_4K_SectorErase 0x20
    1. #define W25X_ChipErase 0xC7
    1. #define W25X_PowerDown 0xB9
    1. #define W25X_ReleasePowerDown 0xAB
    1. #define W25X_DeviceID 0xAB
    1. #define W25X_ManufactDeviceID 0x90
    1. #define W25X_JedecDeviceID 0x9F
    1. #define Busy_Flag 0x01 /* Write In Progress (WIP) flag */
    1. #define Dummy_Byte 0xFF /* Dummy Data */
    1. typedef struct {
    1. uint8_t manufacturer_id;
    1. uint8_t memory_type_id;
    1. uint8_t capacity_id;
    1. uint8_t device_id;
    1. } W25Q64_ID_t;
    1. /*******************************************************************************
    1. * Function Name : SPI_FLASH_SendByte
    1. * Description : Sends a byte through the SPI interface and return the byte
    1. * received from the SPI bus.
    1. * Input : byte : byte to send.
    1. * Output : None
    1. * Return : The value of the received byte.
    1. *******************************************************************************/
    1. static uint8_t SPI_FLASH_SendByte(uint8_t byte)
    1. {
    1. /* Loop while DR register in not emplty */
    1. while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET);
    1. /* Send byte through the SPI2 peripheral */
    1. SPI_I2S_SendData(SPI2, byte);
    1. /* Wait to receive a byte */
    1. while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET);
    1. /* Return the byte read from the SPI bus */
    1. return SPI_I2S_ReceiveData(SPI2);
    1. }
    1. /*******************************************************************************
    1. * Function Name : SK_GetFlashID
    1. * Description : Reads FLASH identification.
    1. * Input : W25Q64_ID_t pointer
    1. * Output : None
    1. * Return : FLASH identification
    1. *******************************************************************************/
    1. void SK_GetFlashID(W25Q64_ID_t *stW25Q64_ID)
    1. {
    1. SK_SPI_FLASH_CS_LOW();
    1. SPI_FLASH_SendByte(W25X_JedecDeviceID);
    1. stW25Q64_ID->manufacturer_id = SPI_FLASH_SendByte(Dummy_Byte);
    1. stW25Q64_ID->memory_type_id = SPI_FLASH_SendByte(Dummy_Byte);
    1. stW25Q64_ID->capacity_id = SPI_FLASH_SendByte(Dummy_Byte);
    1. SK_SPI_FLASH_CS_HIGH();
    1. }
    1. /*******************************************************************************
    1. * Function Name : SK_GetFlashDeviceID
    1. * Description : Reads FLASH Device identification.
    1. * Input : W25Q64_ID_t pointer
    1. * Output : None
    1. * Return : FLASH identification
    1. *******************************************************************************/
    1. void SK_GetFlashDeviceID(W25Q64_ID_t *stW25Q64_ID)
    1. {
    1. SK_SPI_FLASH_CS_LOW();
    1. SPI_FLASH_SendByte(W25X_DeviceID);
    1. SPI_FLASH_SendByte(Dummy_Byte);
    1. SPI_FLASH_SendByte(Dummy_Byte);
    1. SPI_FLASH_SendByte(Dummy_Byte);
    1. stW25Q64_ID->device_id = SPI_FLASH_SendByte(Dummy_Byte);
    1. SK_SPI_FLASH_CS_HIGH();
    1. }

擦除 Flash

Flash 的擦除分为三种,Sector 擦除(4K 为单位),Block擦除(32K or 64K 为单位)以及 Chip 擦除(全部擦除)

基本的方式都是一样的,支持命令不一样而已:

先拉低 CS,然后传输擦除的命令(0x20 or 0xD8 or 0x52),在传输期望擦除的地址起始值:24 bits Address,进行操作后,需要等待 BUSY 为 0 ,方可退出。

针对 BUSY 位的判断,是通过读取 Status Register-1 的最后一位来确定的:

    1. /*******************************************************************************
    1. * Function Name : SK_SPI_FLASH_WaitBusy
    1. * Description : Polls the status of the Write In Progress (WIP) flag in the
    1. * FLASH's status register and loop until write opertaion
    1. * has completed.
    1. * Input : None
    1. * Output : None
    1. * Return : None
    1. *******************************************************************************/
    1. void SK_SPI_FLASH_WaitBusy(void)
    1. {
    1. u8 FLASH_Status = 0;
    1. /* Select the FLASH: Chip Select low */
    1. SK_SPI_FLASH_CS_LOW();
    1. /* Send "Read Status Register" instruction */
    1. SPI_FLASH_SendByte(W25X_ReadStatusReg_1);
    1. /* Loop as long as the memory is busy with a write cycle */
    1. do
    1. {
    1. /* Send a dummy byte to generate the clock needed by the FLASH
    1. and put the value of the status register in FLASH_Status variable */
    1. FLASH_Status = SPI_FLASH_SendByte(Dummy_Byte);
    1. }
    1. while ((FLASH_Status & Busy_Flag) == SET); /* Write in progress */
    1. /* Deselect the FLASH: Chip Select high */
    1. SK_SPI_FLASH_CS_HIGH();
    1. }
    1. /*******************************************************************************
    1. * Function Name : SK_SPI_FLASH_Erase
    1. * Description : Erases the specified FLASH
    1. * Input : SectorAddr: address of the sector to erase.
    1. * Input : EraseType : The type of erase.
    1. * : W25X_4K_SectorErase (4KB Erase)
    1. * : W25X_32K_BlockErase (32KB Erase)
    1. * : W25X_64K_BlockErase (64KB Erase)
    1. * Output : None
    1. * Return : None
    1. *******************************************************************************/
    1. void SK_SPI_FLASH_Erase(uint32_t SectorAddr, uint8_t EraseType)
    1. {
    1. /* Send write enable instruction */
    1. SK_SPI_FLASH_WriteEnable();
    1. SK_SPI_FLASH_WaitBusy();
    1. /* Sector Erase */
    1. /* Select the FLASH: Chip Select low */
    1. SK_SPI_FLASH_CS_LOW();
    1. /* Send Sector Erase instruction */
    1. SPI_FLASH_SendByte(EraseType);
    1. /* Send SectorAddr high nibble address byte */
    1. SPI_FLASH_SendByte((SectorAddr & 0xFF0000) >> 16);
    1. /* Send SectorAddr medium nibble address byte */
    1. SPI_FLASH_SendByte((SectorAddr & 0xFF00) >> 8);
    1. /* Send SectorAddr low nibble address byte */
    1. SPI_FLASH_SendByte(SectorAddr & 0xFF);
    1. /* Deselect the FLASH: Chip Select high */
    1. SK_SPI_FLASH_CS_HIGH();
    1. /* Wait the end of Flash writing */
    1. SK_SPI_FLASH_WaitBusy();
    1. }

写 Flash

注意,在写 W25Q64BV Flash 的时候,Datesheet 规定,需要先写入 Write Enable 命令:

    1. /*******************************************************************************
    1. * Function Name : SK_SPI_FLASH_WriteEnable
    1. * Description : Enables the write access to the FLASH.
    1. * Input : None
    1. * Output : None
    1. * Return : None
    1. *******************************************************************************/
    1. void SK_SPI_FLASH_WriteEnable(void)
    1. {
    1. /* Select the FLASH: Chip Select low */
    1. SK_SPI_FLASH_CS_LOW();
    1. /* Send "Write Enable" instruction */
    1. SPI_FLASH_SendByte(W25X_WriteEnable);
    1. /* Deselect the FLASH: Chip Select high */
    1. SK_SPI_FLASH_CS_HIGH();
    1. }

此刻代表 SPI Flash 已经处在可被写的状态,然后对 SPI FLASH 的写操作,支持 0~256-bytes的写操作:

当然,写完同样需要 Check Buys 位:

    1. /*******************************************************************************
    1. * Function Name : SK_SPI_FLASH_PageWrite
    1. * Description : Writes more than one byte to the FLASH with a single WRITE
    1. * cycle(Page WRITE sequence). The number of byte can't exceed
    1. * the FLASH page size.
    1. * Input : - pBuffer : pointer to the buffer containing the data to be
    1. * written to the FLASH.
    1. * - WriteAddr : FLASH's internal address to write to.
    1. * - NumByteToWrite : number of bytes to write to the FLASH,
    1. * must be equal or less than "SPI_FLASH_PageSize" value.
    1. * Output : None
    1. * Return : None
    1. *******************************************************************************/
    1. void SK_SPI_FLASH_PageWrite(uint8_t *pBuffer, uint32_t WriteAddr, uint32_t NumByteToWrite)
    1. {
    1. /* Enable the write access to the FLASH */
    1. SK_SPI_FLASH_WriteEnable();
    1. /* Select the FLASH: Chip Select low */
    1. SK_SPI_FLASH_CS_LOW();
    1. /* Send "Write to Memory " instruction */
    1. SPI_FLASH_SendByte(W25X_PageProgram);
    1. /* Send WriteAddr high nibble address byte to write to */
    1. SPI_FLASH_SendByte((WriteAddr & 0xFF0000) >> 16);
    1. /* Send WriteAddr medium nibble address byte to write to */
    1. SPI_FLASH_SendByte((WriteAddr & 0xFF00) >> 8);
    1. /* Send WriteAddr low nibble address byte to write to */
    1. SPI_FLASH_SendByte(WriteAddr & 0xFF);
    1. if(NumByteToWrite > SPI_FLASH_PerWritePageSize)
    1. {
    1. NumByteToWrite = SPI_FLASH_PerWritePageSize;
    1. }
    1. /* while there is data to be written on the FLASH */
    1. while (NumByteToWrite--)
    1. {
    1. /* Send the current byte */
    1. SPI_FLASH_SendByte(*pBuffer);
    1. /* Point on the next byte to be written */
    1. pBuffer++;
    1. }
    1. /* Deselect the FLASH: Chip Select high */
    1. SK_SPI_FLASH_CS_HIGH();
    1. /* Wait the end of Flash writing */
    1. SK_SPI_FLASH_WaitBusy();
    1. }

读 Flash

读数据的过程,同样也是先 CS 拉低,在写入读数据的指令(0x03),接着跟 24-bit 的地址信息。然后一直发送 Dummy Data,同时读到指定的数据到 BUFFER

    1. /*******************************************************************************
    1. * Function Name : SK_SPI_FLASH_BufferRead
    1. * Description : Reads a block of data from the FLASH.
    1. * Input : - pBuffer : pointer to the buffer that receives the data read
    1. * from the FLASH.
    1. * - ReadAddr : FLASH's internal address to read from.
    1. * - NumByteToRead : number of bytes to read from the FLASH.
    1. * Output : None
    1. * Return : None
    1. *******************************************************************************/
    1. void SK_SPI_FLASH_BufferRead(uint8_t* pBuffer, uint32_t ReadAddr, uint32_t NumByteToRead)
    1. {
    1. /* Select the FLASH: Chip Select low */
    1. SK_SPI_FLASH_CS_LOW();
    1. /* Send "Read from Memory " instruction */
    1. SPI_FLASH_SendByte(W25X_ReadData);
    1. /* Send ReadAddr high nibble address byte to read from */
    1. SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16);
    1. /* Send ReadAddr medium nibble address byte to read from */
    1. SPI_FLASH_SendByte((ReadAddr& 0xFF00) >> 8);
    1. /* Send ReadAddr low nibble address byte to read from */
    1. SPI_FLASH_SendByte(ReadAddr & 0xFF);
    1. while (NumByteToRead--)
    1. {
    1. *pBuffer = SPI_FLASH_SendByte(Dummy_Byte);
    1. pBuffer++;
    1. }
    1. /* Deselect the FLASH: Chip Select high */
    1. SK_SPI_FLASH_CS_HIGH();
    1. }

测试方式

在 main 函数中,初始化 SPI 后,进行 Flash 的 ID 读取,扇区擦除,数据读写的操作,同时,也可以进行数据写入后,断电,然后再次读出的操作,看看数据是否正确保存。

    1. #define SPI_FLASH_DATA_LEN 100
    1. #define SPI_FLASH_START_ADDR 0x00
    1. W25Q64_ID_t g_stW25Q64_ID;
    1. uint8_t g_spi_flash_rd_data[SPI_FLASH_DATA_LEN] = {0};
    1. uint8_t g_spi_flash_wr_data[SPI_FLASH_DATA_LEN] = {0};
    1. int main(void)
    1. {
    1. uint32_t i = 0;
    1. /* SPI FLASH TEST */
    1. SK_SPIFlashInit();
    1. SK_GetFlashID(&g_stW25Q64_ID);
    1. SK_GetFlashDeviceID(&g_stW25Q64_ID);
    1. SK_SPI_FLASH_Erase(SPI_FLASH_START_ADDR, W25X_4K_SectorErase);
    1. SK_SPI_FLASH_BufferRead(g_spi_flash_rd_data, SPI_FLASH_START_ADDR, SPI_FLASH_DATA_LEN);
    1. for (i = 0; i < SPI_FLASH_DATA_LEN; i++)
    1. g_spi_flash_wr_data[i] = i;
    1. SK_SPI_FLASH_PageWrite(g_spi_flash_wr_data, SPI_FLASH_START_ADDR, SPI_FLASH_DATA_LEN);
    1. SK_SPI_FLASH_BufferRead(g_spi_flash_rd_data, SPI_FLASH_START_ADDR, SPI_FLASH_DATA_LEN);
    1. }
posted @ 2025-11-06 11:19  张大帅哥  阅读(3)  评论(0)    收藏  举报