stm32 spi读写W25Q128实例

news/2024/11/16 22:06:23/

文章目录

  • 一、W25Q128芯片简介
  • 二、SPI初始化与配置
  • 三、W25Q128命令帧格式与操作
    • 写使能(0x06):
    • 读取状态寄存器(0x05):
    • 读取数据(0x03):
    • 页编程(0x02):
    • 扇区擦除(0x20):
  • 四、实例代码

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) {// 主循环
}

}


http://www.ppmy.cn/news/1547550.html

相关文章

面试:TCP、UDP如何解决丢包问题

文章目录 一、TCP丢包原因、解决办法1.1 TCP为什么会丢包1.2 TCP传输协议如何解决丢包问题1.3 其他丢包情况&#xff08;拓展&#xff09;1.4 补充1.4.1 TCP端口号1.4.2 多个TCP请求的逻辑1.4.3 处理大量TCP连接请求的方法1.4.4 总结 二、UDP丢包2.1 UDP协议2.1.1 UDP简介2.1.2…

使用@react-three/fiber,@mkkellogg/gaussian-splats-3d加载.splat,.ply,.ksplat文件

前言 假设您正在现有项目中集成这些包&#xff0c;而该项目的构建工具为 Webpack 或 Vite。同时&#xff0c;您对 Three.js 和 React 有一定的了解。如果您发现有任何错误或有更好的方法&#xff0c;请随时留言。 安装 npm install three types/three react-three/fiber rea…

Django数据写入MySQL数据库

将 Django 模型写入 MySQL 数据库的步骤与写入其他数据库类型基本相同。以下是详细的步骤和示例&#xff0c;帮助你在 Django 项目中配置 MySQL 数据库并写入数据。 1. 安装 MySQL 和相关依赖 首先&#xff0c;确保你已经安装了 MySQL 数据库服务器和 mysqlclient 包。mysqlc…

python实战(八)——情感识别(多分类)

一、任务目标 本文使用的是来自Kaggle的一个情感识别数据集&#xff0c;这个数据集的总数据量是5934条&#xff0c;标签为anger、fear、joy三种情感的其中一种&#xff0c;很明显是一个多分类任务。这里&#xff0c;我们将使用微调技巧进行深度学习建模&#xff0c;同时我们会比…

【C++学习(35)】在Linux中基于ucontext实现C++实现协程(Coroutine),基于C++20的co_await 协程的关键字实现协程

文章目录 为什么使用协程协程的理解协程优势协程的原语操作yield 与 resume 是一个switch操作&#xff08;三种实现方式&#xff09;&#xff1a; 基于 ucontext 的协程基于 XFiber 库的操作1 包装上下文2 XFiber 上下文调度器2.1 CreateFiber2.2 Dispatch 基于C20的co_return …

【免越狱】iOS砸壳 可下载AppStore任意版本 旧版本IPA下载

软件介绍 下载iOS旧版应用&#xff0c;简化繁琐的抓包流程。 一键生成去更新IPA&#xff08;手机安装后&#xff0c;去除App Store的更新检测&#xff09;。 软件界面 支持系统 Windows 10/Windows 8/Windows 7&#xff08;由于使用了Fiddler库&#xff0c;因此需要.Net环境…

CAP与BASE分布式理论

CAP理论 C&#xff1a;Consistency 一致性&#xff1a;指强一致性&#xff0c;分布式系统中的所有节点在同一时刻具有同样的值、都是最新的数据副本&#xff0c;一致性保证了不管向哪台服务器写入数据&#xff0c;其他的服务器能实时同步数据 强一致性&#xff1a;写入数据的时…

C++11新特性:lambda表达式,包装器,新的类功能

1. lambda表达式 1.1 基本语法 lambda表达式本质上是一个匿名函数对象&#xff0c;但是和普通函数不一样他可以定义在函数内部。 lambda表达式使用层而言没有类型&#xff0c;所以我们一般使用auto或者模板参数定义的对象去接收lambda对象。 lambda表达式的格式如下&#x…