stm32 spi读写W25Q128实例

一、W25Q128芯片简介

W25Q128是一款具有128Mb(即16MB)存储容量的Flash闪存芯片。它支持SPI和QSPI接口,能够在QSPI模式下实现高达104MHz的时钟频率,提供快速的读取和写入性能。此外,W25Q128还支持页编程和块擦除功能,具有良好的写入效率,并且具有高达100,000次的擦写周期和超过20年的数据保持能力。

二、SPI初始化与配置

在进行SPI通信之前,需要对STM32的SPI外设进行初始化和配置。以下是一个典型的SPI初始化代码示例:
SPI_InitTypeDef SPI_InitStructure;
// 配置SPI为主模式,2线全双工通信,数据宽度为8位
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
// 配置SPI为高位先行,波特率分频为128,SPI极性为低,相位为第一个时钟边沿采样
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
// NSS引脚由软件控制,CRC多项式暂时用不到,给默认值7
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_CRCPolynomial = 7;
// 将配置结构体交给SPI_Init函数,配置SPI1
SPI_Init(SPI1, &SPI_InitStructure);
// 使能SPI1,开始运行
SPI_Cmd(SPI1, ENABLE);

三、W25Q128命令帧格式与操作

W25Q128通过一系列指令来控制其读写操作。这些指令通常包括写使能、读取状态寄存器、读取数据、页编程和扇区擦除等。以下是一些常用指令及其命令帧格式:

写使能(0x06):

将状态寄存器中的WEL位设置为1,以允许后续的写操作。
命令帧格式:拉低CS片选 -> 发送命令0x06 -> 拉高CS片选。
W25Qx_WriteEnable(void)
{
uint8_t cmd[] = {0x06};
/*Select the FLASH: Chip Select low /
W25Qx_Enable();
/
Send the read ID command */
HAL_SPI_Transmit(&hspi1, cmd, 1, 2000);
/*Deselect the FLASH: Chip Select high */
W25Qx_Disable();
}

读取状态寄存器(0x05):

读取8位状态寄存器的值,以检查芯片状态。
命令帧格式:拉低CS片选 -> 发送命令0x05 -> 接收状态寄存器SR1的返回值 -> 拉高CS片选。
W25Qx_GetStatus(void)
{
uint8_t cmd[] = {0x05};
uint8_t status;
W25Qx_Enable();
/* Send the read status command /
HAL_SPI_Transmit(&hspi1, cmd, 1, W25Qx_TIMEOUT_VALUE);
/
Reception of the data */
HAL_SPI_Receive(&hspi1,&status, 1, W25Qx_TIMEOUT_VALUE);
W25Qx_Disable();
}

读取数据(0x03):

从指定的寄存器地址顺序读取一个或多个数据。
命令帧格式:拉低CS片选 -> 发送命令0x03 -> 发送寄存器地址24位 -> 连续接收n个DataOUT -> 拉高CS片选。
uint8_t W25Qx_Read(uint8_t* pData, uint32_t ReadAddr, uint32_t Size)
{
uint8_t cmd[4];
/* Configure the command /
cmd[0] = 0x03;
cmd[1] = (uint8_t)(ReadAddr >> 16);
cmd[2] = (uint8_t)(ReadAddr >> 8);
cmd[3] = (uint8_t)(ReadAddr);
W25Qx_Enable();
/
Send the read ID command /
HAL_SPI_Transmit(&hspi1, cmd, 4, 2000);
/
Reception of the data */
if (HAL_SPI_Receive(&hspi1, pData,Size,200) != HAL_OK)
{
return HAL_ERROR;
}
W25Qx_Disable();
return HAL_OK;
}

页编程(0x02):

从指定的寄存器地址连续写入数据。
命令帧格式:拉低CS片选 -> 发送命令0x02 -> 发送寄存器地址24位 -> 连续发送n个DataByte -> 拉高CS片选(注意:在拉低CS片选之前需要先执行写使能指令)。
uint8_t W25Qx_Write(uint8_t* pData, uint32_t WriteAddr, uint32_t Size)
{
uint8_t cmd[4];
uint32_t end_addr, current_size, current_addr;
uint32_t tickstart = HAL_GetTick();
/* Calculation of the size between the write address and the end of the page */
current_addr = 0;
while (current_addr <= WriteAddr)//判断地址属于哪一扇区开始
{
current_addr += W25Q128FV_PAGE_SIZE;//0x100-> 256 bytes
}
current_size = current_addr - WriteAddr;

/* Check if the size of the data is less than the remaining place in the page /
if (current_size > Size) {
current_size = Size;
}
/
Initialize the adress variables ///写入地址大小范围
current_addr = WriteAddr;
end_addr = WriteAddr + Size;
/
Perform the write page by page /
do{
/
Configure the command /
cmd[0] = 0x02;
cmd[1] = (uint8_t)(current_addr >> 16);
cmd[2] = (uint8_t)(current_addr >> 8);
cmd[3] = (uint8_t)(current_addr);
/
Enable write operations /
W25Qx_WriteEnable();
W25Qx_Enable();
/
Send the command /
if (HAL_SPI_Transmit(&hspi2,cmd, 4, 2000) != HAL_OK)
{
return HAL_ERROR;
}
/
Transmission of the data /
if (HAL_SPI_Transmit(&hspi2, pData,current_size, 2000) != HAL_OK)
{
return HAL_ERROR;
}
W25Qx_Disable();
/
Wait the end of Flash writing /
while(W25Qx_GetStatus() == W25Qx_BUSY)
{
/
Check for the Timeout /
if((HAL_GetTick() - tickstart) > W25Qx_TIMEOUT_VALUE)
{
return W25Qx_TIMEOUT;
}
}
/
Update the address and size variables for next page programming */
current_addr += current_size;
pData += current_size;
current_size = ((current_addr + W25Q128FV_PAGE_SIZE) > end_addr) ? (end_addr - current_addr) : W25Q128FV_PAGE_SIZE;
} while (current_addr < end_addr);
return W25Qx_OK;
}

扇区擦除(0x20):

将寄存器地址所在的扇区全部置为1。
命令帧格式:拉低CS片选 -> 发送命令0x20 -> 发送寄存器地址24位 -> 拉高CS片选(注意:在拉低CS片选之前需要先执行写使能指令)。
uint8_t W25Qx_Erase_Block(uint32_t Address)
{
uint8_t cmd[4];
uint32_t tickstart = HAL_GetTick();
cmd[0] = 0x20;
cmd[1] = (uint8_t)(Address >> 16);
cmd[2] = (uint8_t)(Address >> 8);
cmd[3] = (uint8_t)(Address);
/* Enable write operations */
W25Qx_WriteEnable();
/*Select the FLASH: Chip Select low /
W25Qx_Enable();
/
Send the read ID command */
HAL_SPI_Transmit(&hspi2, cmd, 4, W25Qx_TIMEOUT_VALUE);
/*Deselect the FLASH: Chip Select high /
W25Qx_Disable();
/
Wait the end of Flash writing /
while(W25Qx_GetStatus() == W25Qx_BUSY) {
/
Check for the Timeout */
if((HAL_GetTick() - tickstart) > W25Q128FV_SECTOR_ERASE_MAX_TIME){
return W25Qx_TIMEOUT;
}
}
return W25Qx_OK;
}

四、实例代码

以下是一个使用STM32通过SPI读写W25Q128的实例代码:
#include “stm32f10x.h” // STM32设备头文件
#include “W25Q128.h” // W25Q128操作头文件
// 初始化GPIO和SPI
void Flash_Init(void) {
// 开启外设时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
// 配置GPIO引脚
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7; // PA4(NSS), PA5(SCK), PA7(MOSI)
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; // PA6(MISO)
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置SPI
SPI_InitTypeDef SPI_InitStructure;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI1, &SPI_InitStructure);
SPI_Cmd(SPI1, ENABLE);
}
// 主程序
int main(void) {
// 初始化Flash
Flash_Init();
// 准备写入的数据
uint8_t dataToWrite[] = {0x01, 0x02, 0x03, 0x04};

// 页编程写入数据
W25Qx_Write(dataToWrite, 0x000000, sizeof(dataToWrite));
// 读取数据
uint8_t readData[4];
W25Qx_Read(readData, 0x000000, sizeof(readData));
for(uint8_t i =0;i<4;i++){
	printf("%d,\n",readData[i]);
}
printf("\n");
while (1) {
    // 主循环
}

}

11-17 12:22