如何通过软件SPI读写W25Q64

STM32F1之SPI通信·软件SPI代码编写-CSDN博客

目录

1.  W25Qxx系列简介

2.  W25Q64硬件电路

 3.  W25Q64框图

4.  Flash操作注意事项

5.  代码编写

5.1  初始化

5.2  W25Q64读取ID号

5.3  W25Q64写使能

5.4  W25Q64等待忙

5.5  W25Q64页编程

5.6  W25Q64扇区擦除(4KB)

5.7  W25Q64读取数据


1.  W25Qxx系列简介

        W25Qxx系列是一种低成本、小型化、使用简单的非易失性存储器,常应用于数据存储、字库存储、固件程序存储等场景。

非易失性存储器(Non-Volatile Memory, NVM)和易失性存储器(Volatile Memory)的主要区别在于电源断电后的数据保存能力:

  1. 非易失性存储器(NVM)

    • 数据持久性:即使在断电的情况下,数据也能保持不丢失。
    • 常见类型:闪存(如USB闪存盘、固态硬盘)、硬盘驱动器(HDD)、只读存储器(ROM)、磁带等。
    • 应用场景:适用于需要长期保存数据的场合,如计算机硬盘、手机存储、固态硬盘等。
  2. 易失性存储器(Volatile Memory)

    • 数据持久性:在断电时,存储在其中的数据会丢失。
    • 常见类型:随机存取存储器(RAM),如动态随机存取存储器(DRAM)和静态随机存取存储器(SRAM)。
    • 应用场景:适用于需要高速访问数据但不需要长期保存的场合,如计算机的主内存(RAM)等。

存储介质:Nor Flash(闪存)

时钟频率: 80MHz ;

                   160MHz (Dual SPI)(双重SPI);

                   320MHz (Quad SPI)(四重SPI)。

存储容量(24位地址):

W25Q40:      4Mbit / 512KByte     

W25Q80:      8Mbit / 1MByte     

W25Q16:      16Mbit / 2MByte     

W25Q32:      32Mbit / 4MByte     

W25Q64:      64Mbit / 8MByte     

W25Q128:  128Mbit / 16MByte     

W25Q256:  256Mbit / 32MByte

2.  W25Q64硬件电路

引脚功能
VCC、GND电源(2.7V~3.6V)
CS(SS)SPI片选
CLK(SCK)SPI时钟
DI(MOSI)SPI主机输出从机输入
DO(MISO)SPI主机输入从机输出
WP写保护
HOLD数据保持

 3.  W25Q64框图

4.  Flash操作注意事项

写入操作时:

写入操作前,必须先进行写使能;

每个数据位只能由1改写为0,不能由0改写为1;

写入数据前必须先擦除,擦除后,所有数据位变为1;

擦除必须按最小擦除单元进行 连续写入多字节时,最多写入一页的数据,超过页尾位置的数据,会回到页首覆盖写入;

写入操作结束后,芯片进入忙状态,不响应新的读写操作。

读取操作时:

直接调用读取时序,无需使能,无需额外操作,没有页的限制,读取操作结束后不会进入忙状态,但不能在忙状态时读取

5.  代码编写

5.1  初始化

        首先初始化SPI,可见本文顶部往期回顾链接,初始化SPI:

void W25Q64_Init(void)
{
	MySPI_Init();					//先初始化底层的SPI
}

5.2  W25Q64读取ID号

        对于下图规格书,我们现将其进行宏定义:

#ifndef __W25Q64_INS_H
#define __W25Q64_INS_H

#define W25Q64_WRITE_ENABLE							0x06
#define W25Q64_WRITE_DISABLE						0x04
#define W25Q64_READ_STATUS_REGISTER_1				0x05
#define W25Q64_READ_STATUS_REGISTER_2				0x35
#define W25Q64_WRITE_STATUS_REGISTER				0x01
#define W25Q64_PAGE_PROGRAM							0x02
#define W25Q64_QUAD_PAGE_PROGRAM					0x32
#define W25Q64_BLOCK_ERASE_64KB						0xD8
#define W25Q64_BLOCK_ERASE_32KB						0x52
#define W25Q64_SECTOR_ERASE_4KB						0x20
#define W25Q64_CHIP_ERASE							0xC7
#define W25Q64_ERASE_SUSPEND						0x75
#define W25Q64_ERASE_RESUME							0x7A
#define W25Q64_POWER_DOWN							0xB9
#define W25Q64_HIGH_PERFORMANCE_MODE				0xA3
#define W25Q64_CONTINUOUS_READ_MODE_RESET			0xFF
#define W25Q64_RELEASE_POWER_DOWN_HPM_DEVICE_ID		0xAB
#define W25Q64_MANUFACTURER_DEVICE_ID				0x90
#define W25Q64_READ_UNIQUE_ID						0x4B
#define W25Q64_JEDEC_ID								0x9F
#define W25Q64_READ_DATA							0x03
#define W25Q64_FAST_READ							0x0B
#define W25Q64_FAST_READ_DUAL_OUTPUT				0x3B
#define W25Q64_FAST_READ_DUAL_IO					0xBB
#define W25Q64_FAST_READ_QUAD_OUTPUT				0x6B
#define W25Q64_FAST_READ_QUAD_IO					0xEB
#define W25Q64_OCTAL_WORD_READ_QUAD_IO				0xE3

#define W25Q64_DUMMY_BYTE							0xFF

#endif

        找到其规格书,找到其框住的部分,可以看到其获取ID的指令为9F,并且需要接收到3个字节,我们可以将其流程看做:起始、先交换发送指令9F、随后连续交换接收3个字节、停止。

对于上面三个字节的解读:

        第一个字节是厂商的ID,表示是哪个厂家生产的。

        第二个字节是设备ID,设备ID高八位,表示存储器类型。

        第三个字节也是设备ID,设备ID低八位,表示容量。

void W25Q64_ReadID(uint8_t *MID, uint16_t *DID)
{
	MySPI_Start();								//SPI起始
	MySPI_SwapByte(W25Q64_JEDEC_ID);			//交换发送读取ID的指令
	*MID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);	//交换接收MID,通过输出参数返回,其中括号内的数据可以为任何数,通常我们使用0xFF,目的是将有用的数据置换过来。
	*DID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);	//交换接收DID高8位,其内数据同上
	*DID <<= 8;									//高8位移到高位
	*DID |= MySPI_SwapByte(W25Q64_DUMMY_BYTE);	//或上交换接收DID的低8位,通过输出参数返回,其内数据同上,
	MySPI_Stop();								//SPI终止
}

          其中,MID 工厂ID,使用输出参数的形式返回,DID 设备ID,使用输出参数的形式返回。

5.3  W25Q64写使能

void W25Q64_WriteEnable(void)
{
	MySPI_Start();								//SPI起始
	MySPI_SwapByte(W25Q64_WRITE_ENABLE);		//交换发送写使能的指令
	MySPI_Stop();								//SPI终止
}

5.4  W25Q64等待忙

void W25Q64_WaitBusy(void)
{
	uint32_t Timeout;
	MySPI_Start();								//SPI起始
	MySPI_SwapByte(W25Q64_READ_STATUS_REGISTER_1);				//交换发送读状态寄存器1的指令
	Timeout = 100000;							//给定超时计数时间
	while ((MySPI_SwapByte(W25Q64_DUMMY_BYTE) & 0x01) == 0x01)	//循环等待忙标志位
	{
		Timeout --;								//等待时,计数值自减
		if (Timeout == 0)						//自减到0后,等待超时
		{
			/*超时的错误处理代码,可以添加到此处*/
			break;								//跳出等待,不等了
		}
	}
	MySPI_Stop();								//SPI终止
}

5.5  W25Q64页编程

void W25Q64_PageProgram(uint32_t Address, uint8_t *DataArray, uint16_t Count)
{
	uint16_t i;
	
	W25Q64_WriteEnable();						//写使能
	
	MySPI_Start();								//SPI起始
	MySPI_SwapByte(W25Q64_PAGE_PROGRAM);		//交换发送页编程的指令
	MySPI_SwapByte(Address >> 16);				//交换发送地址23~16位
	MySPI_SwapByte(Address >> 8);				//交换发送地址15~8位
	MySPI_SwapByte(Address);					//交换发送地址7~0位
	for (i = 0; i < Count; i ++)				//循环Count次
	{
		MySPI_SwapByte(DataArray[i]);			//依次在起始地址后写入数据
	}
	MySPI_Stop();								//SPI终止
	
	W25Q64_WaitBusy();							//等待忙
}

5.6  W25Q64扇区擦除(4KB)

void W25Q64_SectorErase(uint32_t Address)
{
	W25Q64_WriteEnable();						//写使能
	
	MySPI_Start();								//SPI起始
	MySPI_SwapByte(W25Q64_SECTOR_ERASE_4KB);	//交换发送扇区擦除的指令
	MySPI_SwapByte(Address >> 16);				//交换发送地址23~16位
	MySPI_SwapByte(Address >> 8);				//交换发送地址15~8位
	MySPI_SwapByte(Address);					//交换发送地址7~0位
	MySPI_Stop();								//SPI终止
	
	W25Q64_WaitBusy();							//等待忙
}

5.7  W25Q64读取数据

void W25Q64_ReadData(uint32_t Address, uint8_t *DataArray, uint32_t Count)
{
	uint32_t i;
	MySPI_Start();								//SPI起始
	MySPI_SwapByte(W25Q64_READ_DATA);			//交换发送读取数据的指令
	MySPI_SwapByte(Address >> 16);				//交换发送地址23~16位
	MySPI_SwapByte(Address >> 8);				//交换发送地址15~8位
	MySPI_SwapByte(Address);					//交换发送地址7~0位
	for (i = 0; i < Count; i ++)				//循环Count次
	{
		DataArray[i] = MySPI_SwapByte(W25Q64_DUMMY_BYTE);	//依次在起始地址后读取数据
	}
	MySPI_Stop();								//SPI终止
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/641493.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

521源码-在线客服-CRMChat网页版客服系统 UNIAPP 全方位在线客服系统源码与管理体系平台

CRMChat客服系统&#xff1a;基于Swoole4Tp6RedisVueMysql构建的高效沟通桥梁 CRMChat是一款独立且高性能的在线客服系统&#xff0c;它结合了Swoole4、Tp6、Redis、Vue以及Mysql等先进技术栈&#xff0c;为用户提供了卓越的在线沟通体验。该系统不仅支持在Pc端、移动端、小程…

16.线性回归代码实现

线性回归的实操与理解 介绍 线性回归是一种广泛应用的统计方法&#xff0c;用于建模一个或多个自变量&#xff08;特征&#xff09;与因变量&#xff08;目标&#xff09;之间的线性关系。在机器学习和数据科学中&#xff0c;线性回归是许多入门者的第一个模型&#xff0c;它…

【机器学习】机器学习基础概念与初步探索

❀机器学习 &#x1f4d2;1. 引言&#x1f4d2;2. 机器学习概述&#x1f4d2;3. 机器学习基础概念&#x1f389;2.1 机器学习的分类&#x1f389;2.2 数据预处理&#x1f308;数据清洗与整合&#x1f308; 特征选择和特征工程&#x1f308;数据标准化与归一化 &#x1f4d2;4. …

Android Studio 所有历史版本下载

一、官网链接 https://developer.android.google.cn/studio/archive 操作 二、AndroidDevTools地址 https://www.androiddevtools.cn/ 参考 https://blog.csdn.net/qq_27623455/article/details/103008937

电表远传抄表是什么?

1.电表远传抄表&#xff1a;简述 电表远传抄表&#xff0c;又称为远程控制自动抄表系统&#xff0c;是电力行业的智能化技术运用&#xff0c;它通过无线或通信网络技术&#xff0c;完成对电表数据信息的远程收集解决。此项技术不仅提升了抄水表高效率&#xff0c;降低了人工偏…

RK3568平台(UART篇)uart应用编程读取模块数据

一.串口介绍 串口设备是嵌入式开发中最常用的外设之一&#xff0c;通过串口打印信息可以调试程序的运行&#xff0c;通 过串口也可以链接很多种外设&#xff0c;比如串口打印机&#xff0c;蓝牙&#xff0c;wifi&#xff0c;GPS&#xff0c;GPRS 等等。 数据传输方式&#xf…

C++ | Leetcode C++题解之第97题交错字符串

题目&#xff1a; 题解&#xff1a; class Solution { public:bool isInterleave(string s1, string s2, string s3) {auto f vector <int> (s2.size() 1, false);int n s1.size(), m s2.size(), t s3.size();if (n m ! t) {return false;}f[0] true;for (int i …

全同态加密生态项目盘点:FHE技术的崛起以及应用

撰文&#xff1a;Chris&#xff0c;Techub News 在当今数字化的时代&#xff0c;隐私保护已成为一个全球性的焦点话题&#xff0c;特别是在加密货币和区块链技术快速发展的背景下。虽然当前的隐私技术在保护数据安全方面多有欠缺&#xff0c;引发了广泛的关注和批评&#xff0c…

Java枚举的本质

目录 1.枚举简介 1.1.规范 1.2.枚举类真实的样子 1.3.枚举类的特点 1.4.枚举可以使用的方法 1.4.1.toString()方法 1.4.2.valueOf方法 1.4.3.values方法 1.4.4.ordinal方法 1.5.枚举的用法 1.5.1.常量 1.5.2.switch 1.5.3.枚举中增加方法 1.5.4.覆盖枚举方法 1.5…

热题系列章节1

22. 括号生成 数字 n 代表生成括号的对数&#xff0c;请你设计一个函数&#xff0c;用于能够生成所有可能的并且 有效的 括号组合。 示例 1&#xff1a; 输入&#xff1a;n 3 输出&#xff1a;[“((()))”,“(()())”,“(())()”,“()(())”,“()()()”] 示例 2&#xff1a…

LeetCode/NowCoder-链表经典算法OJ练习3

孜孜不倦&#xff1a;孜孜&#xff1a;勤勉&#xff0c;不懈怠。指工作或学习勤奋不知疲倦。&#x1f493;&#x1f493;&#x1f493; 目录 说在前面 题目一&#xff1a;返回倒数第k个节点 题目二&#xff1a;链表的回文结构 题目三&#xff1a;相交链表 SUMUP结尾 说在前…

两篇文章讲透数据结构之堆(一)!

目录 1.堆的概念 2.堆的实现方式 3.堆的功能 4.堆的声明 5.堆的实现 5.1堆的初始化 5.2堆的插入 5.2.1向上调整算法 5.2.2堆的插入 5.3堆的删除 5.3.1向下调整算法 5.3.2堆的删除 5.4获取堆顶元素 5.5获取堆的元素个数 5.6判断堆是否为空 5.7打印堆 5.8建堆 …

SQL开窗函数

文章目录 概念&#xff1a;语法&#xff1a;常用的窗口函数及示例&#xff1a;求平均值&#xff1a;AVG() &#xff1a;求和&#xff1a;SUM():求排名&#xff1a;移动平均计数COUNT():求最大MXA()/小MIN()值求分区内的最大/最小值求当前行的前/后一个值 概念&#xff1a; 开窗…

算法题1:电路开关(HW)

题目描述 实验室对一个设备进行通断测试,实验员可以操控开关进行通断,有两种情况: ps,图没记下来,凭印象画了类似的 初始时,3个开关的状态均为断开;现给定实验员操控记录的数组 records ,records[i] = [time, switchId],表示在时刻 time 更改了开关 switchId 的状态…

多线程(C++11)

多线程&#xff08;C&#xff09; 文章目录 多线程&#xff08;C&#xff09;前言一、std::thread类1.线程的创建1.1构造函数1.2代码演示 2.公共成员函数2.1 get_id()2.2 join()2.3 detach()2.4 joinable()2.5 operator 3.静态函数4.类的成员函数作为子线程的任务函数 二、call…

AOP编程

AOP编程 AOP&#xff0c;面向切面编程&#xff0c;一种编程范式&#xff0c;指导开发者如何组织程序结构。 OOP&#xff0c;面向对象编程&#xff0c;一种编程思想。 AOP&#xff0c;提供了一种机制,可以将一些横切系统中多个模块的共同逻辑(如日志记录、事务管理、安全控制等…

SQL面试题练习 —— 波峰波谷

来源&#xff1a;字节今日头条 目录 1 题目2 建表语句3 题解 1 题目 有如下数据&#xff0c;记录每天每只股票的收盘价格&#xff0c;请查出每只股票的波峰和波谷的日期和价格&#xff1b; 波峰定义&#xff1a;股票价格高于前一天和后一天价格时为波峰 波谷定义&#xff1a;股…

MoE 系列论文解读:Gshard、FastMoE、Tutel、MegaBlocks 等

节前&#xff0c;我们组织了一场算法岗技术&面试讨论会&#xff0c;邀请了一些互联网大厂朋友、今年参加社招和校招面试的同学。 针对大模型技术趋势、大模型落地项目经验分享、新手如何入门算法岗、该如何准备面试攻略、面试常考点等热门话题进行了深入的讨论。 总结链接…

Unity在Windows平台播放HEVC/H.265格式视频的底层原理

相关术语、概念 HEVC/H.265 HEVC&#xff08;High Efficiency Video Coding&#xff09;是一种视频压缩标准&#xff0c;也被称为H.265。它是一种高效的视频编码标准&#xff0c;可以提供比之前的标准&#xff08;如H.264&#xff09;更高的压缩率&#xff0c;同时保持较高的…

力扣HOT100 - 31. 下一个排列

解题思路&#xff1a; 数字是逐步增大的 步骤如下&#xff1a; class Solution {public void nextPermutation(int[] nums) {int i nums.length - 2;while (i > 0 && nums[i] > nums[i 1]) i--;if (i > 0) {int j nums.length - 1;while (j > 0 &&…