软件模拟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协议,我们可以直接进行分…...
三维GIS开发cesium智慧地铁教程(5)Cesium相机控制
一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点: 路径验证:确保相对路径.…...
跨链模式:多链互操作架构与性能扩展方案
跨链模式:多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈:模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展(H2Cross架构): 适配层…...
python执行测试用例,allure报乱码且未成功生成报告
allure执行测试用例时显示乱码:‘allure’ �����ڲ����ⲿ���Ҳ���ǿ�&am…...
Reasoning over Uncertain Text by Generative Large Language Models
https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...
STM32---外部32.768K晶振(LSE)无法起振问题
晶振是否起振主要就检查两个1、晶振与MCU是否兼容;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容(CL)与匹配电容(CL1、CL2)的关系 2. 如何选择 CL1 和 CL…...
Ubuntu系统复制(U盘-电脑硬盘)
所需环境 电脑自带硬盘:1块 (1T) U盘1:Ubuntu系统引导盘(用于“U盘2”复制到“电脑自带硬盘”) U盘2:Ubuntu系统盘(1T,用于被复制) !!!建议“电脑…...
《信号与系统》第 6 章 信号与系统的时域和频域特性
目录 6.0 引言 6.1 傅里叶变换的模和相位表示 6.2 线性时不变系统频率响应的模和相位表示 6.2.1 线性与非线性相位 6.2.2 群时延 6.2.3 对数模和相位图 6.3 理想频率选择性滤波器的时域特性 6.4 非理想滤波器的时域和频域特性讨论 6.5 一阶与二阶连续时间系统 6.5.1 …...
CppCon 2015 学习:Time Programming Fundamentals
Civil Time 公历时间 特点: 共 6 个字段: Year(年)Month(月)Day(日)Hour(小时)Minute(分钟)Second(秒) 表示…...
C++--string的模拟实现
一,引言 string的模拟实现是只对string对象中给的主要功能经行模拟实现,其目的是加强对string的底层了解,以便于在以后的学习或者工作中更加熟练的使用string。本文中的代码仅供参考并不唯一。 二,默认成员函数 string主要有三个成员变量,…...
ArcPy扩展模块的使用(3)
管理工程项目 arcpy.mp模块允许用户管理布局、地图、报表、文件夹连接、视图等工程项目。例如,可以更新、修复或替换图层数据源,修改图层的符号系统,甚至自动在线执行共享要托管在组织中的工程项。 以下代码展示了如何更新图层的数据源&…...
