软件模拟SPI协议的理解和使用编写W25Q64
SPI软件模拟的时序
SPI协议中,NSS、SCK、MOSI由主机产生,MISO由从机产生,在SCK每个时钟周期MOSI、MISO传输一位数据,数据的输入输出是同时进行的,所以读写数据也可以视作交换数据。所以读写时对数据位的控制都是用同一个函数即可。
输出引脚为推挽输出,输入引脚为浮空或上拉输入
如上图所示:
初始状态下,
- CS需要拉高
- CLK模式0的时候拉低,模式3的时候拉高
然后读/写数据状态时
- CS拉低
- 如果需要写数据或读数据,先将数据写入DI线
- 拉高CLK电平
- 读DO线
- 拉低CLK电平
- 然后循环7次前面四步,则交换了一个字节数据
例程
#include "stm32f10x.h" // Device header
#include "hal_spi.h"void hal_SPI_W_SS(uint8_t BitValue) //写设备线
{GPIO_WriteBit(SPI_SS_PORT, SPI_SS_PIN, (BitAction)BitValue);
}void hal_SPI_W_SCK(uint8_t BitValue) //写时钟线
{GPIO_WriteBit(SPI_SCK_PORT, SPI_SCK_PIN, (BitAction)BitValue);
}void hal_SPI_W_MOSI(uint8_t BitValue) //写主机发数据线
{GPIO_WriteBit(SPI_MOSI_PORT, SPI_MOSI_PIN, (BitAction)BitValue);
}uint8_t hal_SPI_R_MISO(void) //读从机发数据线
{return GPIO_ReadInputDataBit(SPI_MISO_PORT, SPI_MISO_PIN);
}/****************************************************************************
*@*名称 : hal_SPI_Init
*@*功能 : 初始化spi的各个引脚
*@*形参 : 无
*@*返回值 : 无
****************************************************************************/
void hal_SPI_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);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;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;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);hal_SPI_W_SS(1);hal_SPI_W_SCK(0);
}/****************************************************************************
*@*名称 : hal_SPI_Start
*@*功能 : spi开始传输数据,设备线拉高
*@*形参 : 无
*@*返回值 : 无
****************************************************************************/
void hal_SPI_Start(void)
{hal_SPI_W_SS(0);
}/****************************************************************************
*@*名称 : hal_SPI_Stop
*@*功能 : spi停止传输数据,设备线拉低
*@*形参 : 无
*@*返回值 : 无
****************************************************************************/
void hal_SPI_Stop(void)
{hal_SPI_W_SS(1);
}/****************************************************************************
*@*名称 : hal_SPI_SwapByte
*@*功能 : spi交换数据,交换一个八位数据
*@*形参 : 无
*@*返回值 : 无
****************************************************************************/
uint8_t hal_SPI_SwapByte(uint8_t ByteSend) //交换一个八位数据
{uint8_t i, ByteReceive = 0x00;for (i = 0; i < 8; i ++){hal_SPI_W_MOSI(ByteSend & (0x80 >> i));hal_SPI_W_SCK(1);if (hal_SPI_R_MISO() == 1){ByteReceive |= (0x80 >> i);}hal_SPI_W_SCK(0);}return ByteReceive;
}
SPI在读取数据时,为什么我们必须发送虚拟字节Dummy_Bytes才能接收结果?
SPI必须生成时钟脉冲才能将数据移出。对于大多数(如果不是全部)SPI主机,产生时钟脉冲的唯一方式是发送字节。如果你仔细想想,这是有道理的。
总结:Dummy_Bytes无实际意义,只是为了产生时钟脉冲,这样才能读取数据。
W25Q64的通讯格式
FLASH操作注意事项
- 写入操作前,必须先进行写使能
- 每个数据位只能由1改写为0,不能由0改写为1
- 写入数据前必须先擦除,擦除后,所有数据位变为1
- 擦除必须按最小擦除单元进行(扇区擦除:4096个字节4KB)
- 连续写入多字节时,最多写入一页数据,超过页尾位置的数据会到页首覆盖(一页256个字节)
- 写入操作后芯片进入忙碌状态,不响应新的读写操作(看Busy寄存器是否为1)
W25Q64的读写数据帧结构
如上:起始信号+命令+地址+交换数据+结束
整体代码实现
综合上述两点要求,得以下代码思路
写操作
在每次写操作开始前都进行写使能,结束前进行等待写操作完成
则整个流程为:写使能>>起始信号>>发送写指令>>写入地址>>写入数据>>结束信号>>等待写操作完成
/****************************************************************************
*@*名称 : hal_W25Q64_WriteEnable
*@*功能 : spi写使能打开
*@*形参 : 无
*@*返回值 : 无
****************************************************************************/
void hal_W25Q64_WriteEnable(void) //spi写使能打开
{hal_SPI_Start(); hal_SPI_SwapByte(W25Q64_WRITE_ENABLE); //0x06指令码写使能打开hal_SPI_Stop();
}/****************************************************************************
*@*名称 : hal_W25Q64_WaitBusy
*@*功能 : 忙碌位寄存器,如果写寄存器在工作就等待,没有就很快退出
*@*形参 : 无
*@*返回值 : 无
****************************************************************************/
void hal_W25Q64_WaitBusy(void) //忙碌位寄存器,如果写寄存器在工作就等待,没有就很快退出
{uint32_t Timeout;hal_SPI_Start();hal_SPI_SwapByte(W25Q64_READ_STATUS_REGISTER_1); //W25Q64_READ_STATUS_REGISTER_1忙碌标志位地址Timeout = 100000;while ((hal_SPI_SwapByte(W25Q64_DUMMY_BYTE) & 0x01) == 0x01) //忙为1,不忙为0{Timeout --;if (Timeout == 0){break;}}hal_SPI_Stop();
}/****************************************************************************
*@*名称 : hal_W25Q64_PageProgram
*@*功能 : 页写入
*@*形参 : Address:写入的地址 DataArray:写入数据存放地址 Count:写入字节数
*@*返回值 : 无
****************************************************************************/
void hal_W25Q64_PageProgram(uint32_t Address, uint8_t *DataArray, uint16_t Count) //页写入
{uint16_t i;hal_W25Q64_WriteEnable();hal_SPI_Start();hal_SPI_SwapByte(W25Q64_PAGE_PROGRAM); //连续写指令hal_SPI_SwapByte(Address >> 16); //二十四位地址高八位hal_SPI_SwapByte(Address >> 8); //地址中间八位hal_SPI_SwapByte(Address); //地址低八位for (i = 0; i < Count; i ++){hal_SPI_SwapByte(DataArray[i]); //连续写入数据}hal_SPI_Stop();hal_W25Q64_WaitBusy(); //等待写入成功
}
不过调用写函数时记得先擦除原先的数据
擦除操作
操作步骤:写使能>>起始信号>>擦除命令>>擦除地址>>结束信号>>等待写完成
/****************************************************************************
*@*名称 : hal_W25Q64_SectorErase
*@*功能 : 扇区擦除操作
*@*形参 : Address:擦除扇区的地址
*@*返回值 : 无
****************************************************************************/
void hal_W25Q64_SectorErase(uint32_t Address) //扇区擦除操作
{hal_W25Q64_WriteEnable(); //写使能hal_SPI_Start(); hal_SPI_SwapByte(W25Q64_SECTOR_ERASE_4KB); //扇区擦除指令码hal_SPI_SwapByte(Address >> 16); //擦除的地址高8位hal_SPI_SwapByte(Address >> 8); //擦除的地址中间8位hal_SPI_SwapByte(Address);hal_SPI_Stop();hal_W25Q64_WaitBusy();
}
读操作
读操作要注意开头说的她必须交换数据,既读取同时要发送一个无用数据
操作步骤:起始信号>>读指令>>读地址>>读数据(并写入0xff)>>结束信号
/****************************************************************************
*@*名称 : hal_W25Q64_ReadData
*@*功能 : 连续读数据
*@*形参 : Address:读的首地址 DataArray:读出数据存放地址 Count:读的字节数
*@*返回值 : 无
****************************************************************************/
void hal_W25Q64_ReadData(uint32_t Address, uint8_t *DataArray, uint32_t Count) //连续读数据
{uint32_t i;hal_SPI_Start(); hal_SPI_SwapByte(W25Q64_READ_DATA); //读指令hal_SPI_SwapByte(Address >> 16); //读开始地址hal_SPI_SwapByte(Address >> 8); hal_SPI_SwapByte(Address);for (i = 0; i < Count; i ++){DataArray[i] = hal_SPI_SwapByte(W25Q64_DUMMY_BYTE); //连续读}hal_SPI_Stop();
}
相关文章:

软件模拟SPI协议的理解和使用编写W25Q64
SPI软件模拟的时序 SPI协议中,NSS、SCK、MOSI由主机产生,MISO由从机产生,在SCK每个时钟周期MOSI、MISO传输一位数据,数据的输入输出是同时进行的,所以读写数据也可以视作交换数据。所以读写时对数据位的控制都是用同一…...
SQLI手动注入和python sqlmap代码注入
sql教程: https://www.w3school.com.cn/sql/index.asp数据库: mysql oracle mssql常用方法 system_user() 系统用户名 user() 用户名 current_user() 当前用户名 session_user() 连接数据库的用户名 d…...

MemcachedRedis构建缓存服务器 (数据持久化,主从同步,哨兵模式)
Memcached/redis是高性能的分布式内存缓存服务器,通过缓存数据库查询结果,减少数据库访问次数,以提高动态Web等应用的速度、 提高可扩展性。降低数据库读的压力 Nsql的优点:高可扩展性,分布式计算,低成本,…...

Python语法基础(变量 注释 数据类型 输入与输出 运算符 缩进)
目录 变量变量命名规则变量的类型变量的创建变量的作用域 注释的方法数据类型对象和引用的概念Number(数字)数据转换 输入与输出输入函数输出函数输出函数的end参数输出格式多行语句 运算符算术运算符赋值运算符三目运算符运算符的优先级 缩进缩进格式注意事项层级嵌套 变量 标…...

linux espeak语音tts;pyttsx3 ubuntu使用
整体使用espeak声音很机械不太自然 1、linux espeak语音tts 安装: sudo apt install espeak使用: #中文男声 espeak -v zh 你好 #中文女声 espeak -v zhf3 你好 #粤语男声 espeak -v zhy 你好注意:espeak -v zh 你好 (Full d…...

小白该如何学习Linux操作系统?
💂 个人网站:【工具大全】【游戏大全】【神级源码资源网】🤟 前端学习课程:👉【28个案例趣学前端】【400个JS面试题】💅 寻找学习交流、摸鱼划水的小伙伴,请点击【摸鱼学习交流群】 Linux作为一种开源操作系…...

2023双十一:实体门店闯入,第二战场全面开战
“闺女,吃饺子了吗?”11月8日,立冬,忙碌一天的陈曦回家路上接到母亲电话,才想起来家里冷冻水饺没了,又不想再去超市,直接打开美团买菜买了两袋,回家就煮了吃。当然,最终她…...

操作系统·处理机调度死锁
3.1 处理机调度概述 3.1.1 处理机调度概述 高级调度 (High level Scheduling)决定把外存上哪些作业调入内存、创建进程、分配资源。高级调度又称作业调度、长程调度或宏观调度。只在批处理系统中有高级调度。 中级调度 (Middle level Scheduling)完成进程的部分或全部在内、…...

SQL第四次上机实验
1.查询借阅了计算机类或者文学类图书的读者的借书证号 USE TSGL GO SELECT DISTINCT Reader.Lno FROM Book,Lend,Reader WHERE Book.ISBNLend.ISBN AND Lend.LnoReader.Lno AND Class 计算机类 OR Class 文学类2.查询同时借阅了计算机类和文学类图书的读者的借书证号 USE T…...
读书笔记:彼得·德鲁克《认识管理》第11章 若干例外及经验教训
一、章节内容概述 例外的服务机构不仅表明服务机构实现卓越绩效不是天方夜谭,而 且指明了实现的方法。这一课,是美国电话电报公司给“自然垄断行业”上的;是19世纪后期处于创建阶段的美国现代大学给学校或医院类机构上的;是20世纪30年代的田纳西河流域管…...

JVM-虚拟机的故障处理与调优案例分析
案例1:大内存硬件上的程序部署策略 一个15万PV/日左右的在线文档类型网站最近更换了硬件系统,服务器的硬件为四路志强处理器、16GB物理内存,操作系统为64位CentOS 5.4,Resin作为Web服务器。整个服务器暂时没有部署别的应用&#…...

JMeter 相关的面试题
📢专注于分享软件测试干货内容,欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!📢交流讨论:加入1000人软件测试技术学习交流群📢资源分享:进了字节跳动之后,才…...

你在React项目中是如何使用Redux的? 项目结构是如何划分的?
一、背景 在前面文章了解中,我们了解到redux是用于数据状态管理,而react是一个视图层面的库 如果将两者连接在一起,可以使用官方推荐react-redux库,其具有高效且灵活的特性 react-redux将组件分成: 容器组件&#…...

[每周一更]-(第71期):DevOps 是什么?
Wiki的解释: DevOps(Development和Operations的混成词)是一种重视“软件开发人员(Dev)”和“IT运维技术人员(Ops)”之间沟通合作的文化、运动或惯例。 通过自动化“软件交付”和“架构变更”的…...

k8s的安装部署,详细过程展示(保姆级安装教程)
k8s应用部署方式演变 在部署应用程序的方式上,主要经历了三个时代: 传统部署:互联网早期,会直接将应用程序部署在物理机上 优点:简单,不需要其它技术的参与 缺点:不能为应用程序定义资源使用…...
基于windows、GDAL2.2.3版本和Java集成安装和使用GDAL库的方法
基于windows、GDAL2.2.3版本和Java集成安装和使用GDAL库的方法 一、下载gdal windows版本64位2.2.3版本 下载地址: https://www.gisinternals.com/archive.php 找到gdal-202-1911-x64-core.msi下载并安装 安装后默认目录为:C:\Program Files\GDAL 二、…...

AlphaControls控件TsRadioGroup的使用
通常使用AlphaControls控件中的TsRadioGroup时,往往使用默认值,会造成TsRadioGroup标题被TsRadioGroup的ITEMs占用,严重影响美观: 解决方案,通过对TsRadioGroup的ContentVOffset属性,设置为10。即可立即改善…...
安卓常见设计模式8------享元模式(Kotlin版)
1. W1 是什么,什么是享元模式? 享元模式(Flyweight Pattern)是一种结构型设计模式,用于有效地支持大量细粒度的对象共享。在 Android 中,享元模式可以用于减少内存使用和提高性能,特别是在需…...
day54 django中orm数据库增删改查
昨日内容回顾 三板斧问题 HttpResponse # 返回的是字符串 render # 渲染一个HTML静态文件,模板文件 redirect # 重定向的 """在视图文件中得视图函数必须要接收一个形参request,并且,视图…...

【js逆向实战】某sakura动漫视频逆向
写在前面 再写一个逆向实战,后面写点爬虫程序来实现一下。 网站简介与逆向目标 经典的一个视频网站,大多数视频网站走的是M3U8协议,就是一个分段传输,其实这里就有两个分支。 通过传统的m3u8协议,我们可以直接进行分…...

边缘计算医疗风险自查APP开发方案
核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...
mongodb源码分析session执行handleRequest命令find过程
mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程,并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令,把数据流转换成Message,状态转变流程是:State::Created 》 St…...

基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容
基于 UniApp + WebSocket实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...

dedecms 织梦自定义表单留言增加ajax验证码功能
增加ajax功能模块,用户不点击提交按钮,只要输入框失去焦点,就会提前提示验证码是否正确。 一,模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现
摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序,以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务,提供稳定高效的数据处理与业务逻辑支持;利用 uniapp 实现跨平台前…...

pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...
Python 包管理器 uv 介绍
Python 包管理器 uv 全面介绍 uv 是由 Astral(热门工具 Ruff 的开发者)推出的下一代高性能 Python 包管理器和构建工具,用 Rust 编写。它旨在解决传统工具(如 pip、virtualenv、pip-tools)的性能瓶颈,同时…...

(一)单例模式
一、前言 单例模式属于六大创建型模式,即在软件设计过程中,主要关注创建对象的结果,并不关心创建对象的过程及细节。创建型设计模式将类对象的实例化过程进行抽象化接口设计,从而隐藏了类对象的实例是如何被创建的,封装了软件系统使用的具体对象类型。 六大创建型模式包括…...
LangFlow技术架构分析
🔧 LangFlow 的可视化技术栈 前端节点编辑器 底层框架:基于 (一个现代化的 React 节点绘图库) 功能: 拖拽式构建 LangGraph 状态机 实时连线定义节点依赖关系 可视化调试循环和分支逻辑 与 LangGraph 的深…...

Visual Studio Code 扩展
Visual Studio Code 扩展 change-case 大小写转换EmmyLua for VSCode 调试插件Bookmarks 书签 change-case 大小写转换 https://marketplace.visualstudio.com/items?itemNamewmaurer.change-case 选中单词后,命令 changeCase.commands 可预览转换效果 EmmyLua…...