SPI协议——对外部SPI Flash操作
目录
1. W25Q32JVSSIQ背景知识
1.1 64个可擦除块
1.2 1024个扇区(每个块有16个扇区)
1.3 页
1. W25Q32JVSSIQ背景知识

W25Q32JV阵列被组织成16,384个可编程页,每页有256字节。一次最多可以编程256个字节。页面可分为16组(4KB扇区清除)、128组(32KB块删除)、256组(64KB块删除)或整个芯片(芯片清除)。W25Q32JV分别有1,024个可擦除扇区和64个可擦除块。小型的4KB扇区允许在需要数据和参数存储的应用程序中具有更大的灵活性。
1.1 64个可擦除块
8M的空间被切割成128块,每块64kb

1.2 1024个扇区(每个块有16个扇区)
每块64kb又每切割成16个扇区,每扇区4kb(16*4=64kb)

1.3 页
一扇区是4KB,划分成16份每份256字节这就是页,而且擦除数据也只能按照扇区或者块来擦除,下图的00FF00H-00FFFFH刚好256字节,000000h-0000ff之间也是256字节

2. Flash操作注意事项
2.1 写入操作
1. 写入操作之前必须先进行写使能;
2. 每个数据只能由1改写为0,不能由0改写为1是因为FLASH芯片自身的限制决定,它没有完全任意修改的能力,所以芯片内部无数据的时候默认为0XFF,表示为空。;
3. 写入数据之前必须先进行擦除,擦除后所有的数据位为1;
4. 进行擦除时必须按照最小擦除单元进行擦除(最小擦除单位为扇区);
5.连续写入多字节数据时,最多写入一页的数据,如果超过一页则会覆盖前面写的内容;
6. 写入操作结束后, 芯片进入忙状态(寄存器中有一个busy位),不影响新的读写操作;
2.2 读操作
进行读操作时,直接调用读取操作时序,无需使能,但是别忘了拉低电平,没有页的限制,读取之后也不会进入到忙状态,但是不能在忙状态时读取数据;
3.代码编写
此次对SPI Flash进行读写操作还是采用SPI1,具体的配置请见上篇文章,在这里不做详细概述。
3.1 spi_flash.c添加代码
/* Flash 写使能 */
static void SPI1_FLASH_WriteEnable(void)
{cs_low();SPI_FLASH_SendByte( 0x06 ); //需要查芯片的datasheet来看具体的cs_high();
}/* Flash 等待写结束 */
static void SPI1_FLASH_WaitEnd(void)
{uint8_t state = 0;cs_low();SPI_FLASH_SendByte(0x05); //需要查芯片的datasheet来看具体的do{state = SPI1_FLASH_ReadByte();}while( (state & 0x01) == SET );cs_high();
}// 扇区擦除验证函数
int SPI_FLASH_VerifyErase(uint32_t addr, uint32_t length)
{uint8_t buffer[16];uint32_t bytesRead;for (bytesRead = 0; bytesRead < length; bytesRead += sizeof(buffer)){uint32_t toRead = sizeof(buffer);if (bytesRead + toRead > length) {toRead = length - bytesRead;}// 读取地址开始的若干字节数据SPI_FLASH_BufferRead(addr + bytesRead, buffer, toRead);// 检查读取的数据是否为0xFFfor (uint32_t i = 0; i < toRead; i++){if (buffer[i] != 0xFF){return -1; // 如果不是0xFF,返回false,表示擦除失败}}}return 0; // 如果所有数据都是0xFF,返回true,表示擦除成功
}/*清空扇区*/
int SPI_FLASH_SectorErase(uint32_t addr)
{cs_low();/* 开始的时候要先发送写使能信号 */SPI1_FLASH_WriteEnable();/* 发送扇区擦除命令 */SPI_FLASH_SendByte(0x20);/* 发送扇区地址,高位先行 */SPI_FLASH_SendByte( (addr & 0xff0000) >> 16 );SPI_FLASH_SendByte( (addr & 0xff00) >> 8 );SPI_FLASH_SendByte( addr & 0xff );cs_high();/* 最后等待Flash 处理完这次信号之后退出 */SPI1_FLASH_WaitEnd();if (!SPI_FLASH_VerifyErase(addr, 4096)) // 通常扇区大小为4KB{printf("Sector erase failed at address 0x%06X\n", addr);}else{printf("Sector erase successful at address 0x%06X\n", addr);}return 0;}/* 按页写数据,在写数据之前要先擦除 */void SPI_FLASH_PageWrite(uint32_t addr, uint8_t *pBuffer, uint8_t size)
{/* 发送使能信号 */SPI1_FLASH_WriteEnable();cs_low();/* 发送页写入命令 */SPI_FLASH_SendByte(0x02);/* 发送要写入的地址,高位先行 */SPI_FLASH_SendByte( (addr & 0xff0000) >> 16 );SPI_FLASH_SendByte( ( addr & 0xff00) >> 8 );SPI_FLASH_SendByte(addr & 0xff );printf("Writing to address 0x%06X: ", addr);for (uint8_t i = 0; i < size; i++){SPI_FLASH_SendByte(pBuffer[i]); // 发送数据printf("%02X ", pBuffer[i]);}printf("\n");cs_high();SPI1_FLASH_WaitEnd();
}/* 读取数据,读取指定地址制定长度的数据 */
void SPI_FLASH_BufferRead(uint32_t addr, uint8_t *pBuffer, uint16_t size)
{cs_low();/* 发送读取命令 */SPI_FLASH_SendByte(0x03);/* 发送要读取的地址,高位先行*/SPI_FLASH_SendByte( (addr & 0xff0000) >> 16 );SPI_FLASH_SendByte( (addr & 0xff00 ) >> 8 );SPI_FLASH_SendByte(addr & 0xff );printf("Reading to address 0x%06X: ", addr);/* 逐位读取数据到指针上 */for (uint8_t i = 0; i < size; i++){pBuffer[i] = SPI_FLASH_SendByte(Dummy_Byte); // 发送数据}cs_high();}
3.2 详细分析
1. 地址分解
假设 addr 是一个24位的地址(0xFFFFFF范围内)。发送前需要将addr分解成三个8位字节,因为SPI通常以字节为单位发送数据。
SPI_SendData((addr & 0xff0000) >> 16);
addr & 0xff0000:使用位与操作(&)保留addr的高8位,其余位清零。>> 16:将结果右移16位,使得高8位移到最低8位的位置。SPI_SendData():将移位后的结果发送出去,这发送的是addr的高8位。- 例如:如果
addr= 0x123456,那么(addr & 0xff0000)= 0x120000,右移16位得到 0x12,即发送的第一个字节是 0x12。
其他同理
2. SPI1_FLASH_WaitEnd(void)函数

读取寄存器最低位BUSY的状态,如果为1表示忙,0表示空闲;
3.3 main.c函数
uint8_t Rx[100];
uint8_t Tx[] = "Hello!", n;
SPI_FLASH_SectorErase(0x00000);
n=sizeof(Tx) -1 ;
SPI_FLASH_PageWrite(0x00000 ,Tx ,n);
SPI_FLASH_BufferRead(0x00000 ,Rx ,n);
printf("the data is %s\n", Rx);
3.4 运行结果
相关文章:
SPI协议——对外部SPI Flash操作
目录 1. W25Q32JVSSIQ背景知识 1.1 64个可擦除块 1.2 1024个扇区(每个块有16个扇区) 1.3 页 1. W25Q32JVSSIQ背景知识 W25Q32JV阵列被组织成16,384个可编程页,每页有256字节。一次最多可以编程256个字节。页面可分为16组(4KB扇区清除&…...
kotlin类型检测与类型转换
一、is与!is操作符 1、使用 is 操作符或其否定形式 !is 在运行时检测对象是否符合给定类型。 fun main() {var a "1"if(a is String) {println("a是字符串类型:${a.length}")}// 或val b a is Stringprintln(b) } 二、"不安全的"转换操作符…...
【JDBC】Oracle数据库连接问题记录
Failed to load driver class oracle.jdbc.driver.OracleDriver in either of HikariConfig class oracle驱动包未正确加载,可以先尝试使用下面方式加载检查类是否存在,如果不存在需要手动下载odbc包 try {Class.forName("oracle.jdbc.driver.Ora…...
leetcode45 跳跃游戏II
题目 给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。 每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i j] 处: 0 < j < nums[i] i j < n 返回到达 nums[n - 1]…...
【数学】什么是方法矩估计?和最大似然估计是什么关系?
背景 方法矩估计(Method of Moments Estimation)和最大似然估计(Maximum Likelihood Estimation, MLE)是两种常用的参数估计方法。方法矩估计基于样本矩与总体矩的关系,通过样本数据计算样本矩来估计总体参数。最大似…...
C++初学者指南第一步---10.内存(基础)
C初学者指南第一步—10.内存(基础) 文章目录 C初学者指南第一步---10.内存(基础)1.内存模型1.1 纸上谈兵:C的抽象内存模型1.2 实践:内存的实际处理 2. 自动存储3.动态存储:std::vector3.1 动态内…...
扩散模型详细推导过程——编码与解码
符号表 符号含义 x ( i ) z 0 ( i ) \boldsymbol{x}^{(i)}\boldsymbol{z}_0^{(i)} x(i)z0(i)第 i i i个训练数据,其为长度为 d d d的向量 z t ( i ) \boldsymbol{z}_t^{(i)} zt(i)第 i i i个训练数据在第 t t t时刻的加噪版本 ϵ t ( i ) \boldsymbol{\epsilo…...
js如何实现开屏弹窗
开屏弹窗是什么,其实就是第一次登录后进入页面给你的一种公告提示,此后再回到当前这个页面时弹窗是不会再出现的。也就是说这个弹窗只会出现一次。 <!DOCTYPE html> <html><head><meta charset"utf-8"><title>…...
C#——文件读取Directory类详情
文件读取Directory类 Durectory提供了目录以及子目录进行创建移动和列举操作方法 Directory和Directorylnfo类(主要操作文件目录属性列如文件是否隐藏的 或者只读等这些属性) Directory对目录进行复制、移动、重命名、创建和删除等操作DirectoryInfo用于对目录属性执行操作 …...
Ruby on Rails Post项目设置网站初始界面
在构建了Ruby的Web服务器后,第三步就可以去掉框架的官方页面,设置自己的网页初始页了。 Linux系统安装Ruby语言-CSDN博客 、在Ubuntu中创建Ruby on Rails项目并搭建数据库-CSDN博客、 Ruby语言建立Web服务器-CSDN博客 了解Ruby onRails项目中的主要文件…...
03-QTWebEngine中使用qtvirtualkeyboard
qt提供了 virtualKeyboard 虚拟键盘模块,只需要在在main函数中最开始加入这样一句就可以了 qputenv("QT_IM_MODULE", QByteArray("qtvirtualkeyboard")); 但是在使用的时候遇到了一些问题: 1、中文输入的时候没有输入提示 Qvirt…...
leetcode3无重复字符的最长字串(重点讲滑动窗口)
本文主要讲解无重复字符的最长字串的要点与细节,根据步骤一步步走更方便理解 c与java代码如下,末尾 具体要点: 1. 区分一下子串和子序列 子串:要求元素在母串中是连续地出现 子序列:不要求连续 2. 题目中有两个核心…...
Gobject tutorial 八
The GObject base class Object memory management Gobject的内存管理相关的API很复杂,但其目标是提供一个基于引用计数的灵活的内存管理模式。 下面我们来介绍一下,与管理引用计数相关的函数。 Reference Count 函数g_object_ref和g_object_unref的…...
DDMA信号处理以及数据处理的流程---cfar检测
Hello,大家好,我是Xiaojie,好久不见,欢迎大家能够和Xiaojie一起学习毫米波雷达知识,Xiaojie准备连载一个系列的文章—DDMA信号处理以及数据处理的流程,本系列文章将从目标生成、信号仿真、测距、测速、cfar检测、测角、目标聚类、目标跟踪这几个模块逐步介绍,这个系列的…...
【机器学习】从理论到实践:决策树算法在机器学习中的应用与实现
📝个人主页:哈__ 期待您的关注 目录 📕引言 ⛓决策树的基本原理 1. 决策树的结构 2. 信息增益 熵的计算公式 信息增益的计算公式 3. 基尼指数 4. 决策树的构建 🤖决策树的代码实现 1. 数据准备 2. 决策树模型训练 3.…...
Zookeeper 集群节点故障剔除、切换、恢复原理
Zookeeper 集群节点故障剔除、切换、恢复原理 zookeeper 集群节点故障时,如何剔除节点,如果为领导节点如何处理,如何进行故障恢 复的,实现原理? 在 Zookeeper 集群中,当节点故障时,集群需要自动剔除故障节点并进行故障恢复,确保集群的高 可用性和一致性。具体来说,…...
解决帝国cms栏目管理拼音乱码的问题
帝国CMS7.5版本utf-8版网站后台增加栏目生成乱码的问题怎么解决 1、需要改一个函数,并且增加一个处理文件,方法如下: 修改e/class/connect.php文件,找到ReturnPinyinFun函数,如未修改文件在4533-4547行,将…...
Git快速入门
一 快速使用 1.1 初始化 什么是版本库呢?版本库又名仓库,可以简单理解成一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改、删除,Git都能跟踪,以便任何时刻都可以追踪历史࿰…...
【18.0】JavaScript---事件案例
【18.0】JavaScript—事件案例 【一】开关灯事件 【介绍】设置一个按钮,按下按钮触发事件,来回切换圆形图片的颜色 【分析】 图片设置:设置成圆形的图片背景颜色:设置红绿两个颜色,来回切换按钮设置:点击…...
推荐系统三十六式学习笔记:原理篇.矩阵分解12|如果关注排序效果,那么这个模型可以帮到你
目录 矩阵分解的不足贝叶斯个性化排序AUC构造样本目标函数训练方法 总结 矩阵分解在推荐系统中的地位非常崇高。它既有协同过滤的血统,又有机器学习的基因,可以说是非常优秀了;但即便如此,传统的矩阵分解无论是在处理显式反馈&…...
第19节 Node.js Express 框架
Express 是一个为Node.js设计的web开发框架,它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用,和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...
Android Wi-Fi 连接失败日志分析
1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分: 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析: CTR…...
云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地
借阿里云中企出海大会的东风,以**「云启出海,智联未来|打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办,现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...
遍历 Map 类型集合的方法汇总
1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...
蓝桥杯 2024 15届国赛 A组 儿童节快乐
P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡,轻快的音乐在耳边持续回荡,小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下,六一来了。 今天是六一儿童节,小蓝老师为了让大家在节…...
Golang——9、反射和文件操作
反射和文件操作 1、反射1.1、reflect.TypeOf()获取任意值的类型对象1.2、reflect.ValueOf()1.3、结构体反射 2、文件操作2.1、os.Open()打开文件2.2、方式一:使用Read()读取文件2.3、方式二:bufio读取文件2.4、方式三:os.ReadFile读取2.5、写…...
Chrome 浏览器前端与客户端双向通信实战
Chrome 前端(即页面 JS / Web UI)与客户端(C 后端)的交互机制,是 Chromium 架构中非常核心的一环。下面我将按常见场景,从通道、流程、技术栈几个角度做一套完整的分析,特别适合你这种在分析和改…...
前端中slice和splic的区别
1. slice slice 用于从数组中提取一部分元素,返回一个新的数组。 特点: 不修改原数组:slice 不会改变原数组,而是返回一个新的数组。提取数组的部分:slice 会根据指定的开始索引和结束索引提取数组的一部分。不包含…...
【Linux手册】探秘系统世界:从用户交互到硬件底层的全链路工作之旅
目录 前言 操作系统与驱动程序 是什么,为什么 怎么做 system call 用户操作接口 总结 前言 日常生活中,我们在使用电子设备时,我们所输入执行的每一条指令最终大多都会作用到硬件上,比如下载一款软件最终会下载到硬盘上&am…...
水泥厂自动化升级利器:Devicenet转Modbus rtu协议转换网关
在水泥厂的生产流程中,工业自动化网关起着至关重要的作用,尤其是JH-DVN-RTU疆鸿智能Devicenet转Modbus rtu协议转换网关,为水泥厂实现高效生产与精准控制提供了有力支持。 水泥厂设备众多,其中不少设备采用Devicenet协议。Devicen…...
