软件模拟SPI协议的理解和使用编写W25Q64

news2024/11/23 15:13:16

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();
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

输出自然数-第10届蓝桥杯国赛Python真题精选

[导读]&#xff1a;超平老师的Scratch蓝桥杯真题解读系列在推出之后&#xff0c;受到了广大老师和家长的好评&#xff0c;非常感谢各位的认可和厚爱。作为回馈&#xff0c;超平老师计划推出《Python蓝桥杯真题解析100讲》&#xff0c;这是解读系列的第7讲。 输出自然数&#x…

CSS3 多媒体查询、网格布局

一、CSS3多媒体查询&#xff1a; CSS3 多媒体查询继承了CSS2多媒体类型的所有思想&#xff0c;取代了查找设备的类型。CSS3根据设置自适应显示。 多媒体查询语法&#xff1a; media not|only mediatype and (expressions) { CSS 代码...; } not: not是用来排除掉某些特定…

一文5个步骤从0到1实现Jmeter分布式压力测试(建议收藏)

之前写过用jmeter做接口测试的文章&#xff0c;本篇我们继续介绍下用jmeter做分布式压力测试的例子。 用jmeter做压力测试&#xff0c;如果只用一台机器&#xff0c;有鉴于线程数的限制和一台机器的性能&#xff0c;可能无法满足压力测试的实际需求&#xff0c;解决这个问题&a…

Postman使用json提取器和正则表达式实现接口的关联

近期在复习Postman的基础知识&#xff0c;在小破站上跟着百里老师系统复习了一遍&#xff0c;也做了一些笔记&#xff0c;希望可以给大家一点点启发。 一&#xff09;使用json提取器实现接口关联 实际项目场景&#xff0c;在财务信息页面&#xff0c;需要上传一个营业执照&…

【已验证】php配置连接sql server中文乱码(解决方法)更改utf-8格式

解决数据库中的中文数据在页面显示乱码的问题 在连接的$connectionInfo中设置"CharacterSet" > "UTF-8"&#xff0c;指定编码方式即可 $connectionInfo array("UID">$uid, "PWD">$pwd, "Database">$database…

黑马程序员微服务SpringCloud实用篇01

SpringCloud01 1.认识微服务 随着互联网行业的发展&#xff0c;对服务的要求也越来越高&#xff0c;服务架构也从单体架构逐渐演变为现在流行的微服务架构。这些架构之间有怎样的差别呢&#xff1f; 1.0.学习目标 了解微服务架构的优缺点 1.1.单体架构 单体架构&#xff…

ChatGPT 上新,效果炸裂,知识平台才是大模型的最佳狩猎场

数新网络_让每个人享受数据的价值浙江数新网络有限公司是一家开源开放、专注于云数据智能操作系统和数据价值流通的服务商。公司自主研发的DataCyber云数据智能操作系统&#xff0c;主要包括数据平台CyberData、人工智能平台CyberAI、数据智能引擎CyberEngine、数据安全平台Cyb…

希亦ACE和石头m1这两款内衣洗衣机哪一款更好?高性价比内衣洗衣机测评

内衣洗衣机可以说是近两年很火爆的小家电了&#xff0c;给大家带了一种全新的时尚体验&#xff0c;越来越内衣裤也可以用手洗&#xff01;而且还比手洗得干净&#xff01;不过现在市面上关于内衣洗衣机的品牌越来越多&#xff0c;小伙伴们想要挑选一款性价比高的内衣洗衣机看得…

python之SPC:计算Cpk

目录 1、Ca、Cp和Cpk的理解 2、python计算Cp,Cpk与Pp,Ppk 3、总结 1、Ca、Cp和Cpk的理解 Ca、Cp和Cpk是制程能力指数&#xff0c;它们分别代表制程准确度、制程精密度和制程能力指数。 制程准确度&#xff08;Ca&#xff09;反映实际平均值与规格中心值之一致性。对于单边…

C#中.NET 7.0不再支持ADO.NET,.NET Framwork依旧支持

目录 一、.NET 7.0框架下任何应用不再支持ADO.NET 二、.NET Framwork框架下Windows窗体应用支持ADO.NET 三、.NET 7.0不支持ADO.NET的真正原因 经过一阵折腾&#xff0c;终于可以确证C#中.NET框架不再支持用户通过ADO.NET的实体框架模型访问数据库&#xff0c;无论是.NET 7…

供应原厂电流继电器 - HBDLX-21/3 整定电流范围0.1-1.09A AC220V

HBDLX系列型号&#xff1a; HBDLX-20/1零序过电压继电器&#xff1b;HBDLX-20/2零序过电压继电器 HBDLX-20/3零序过电压继电器&#xff1b;HBDLX-20/4零序过电压继电器 HBDLX-20/5零序过电压继电器&#xff1b;HBDLX-21/1零序过电压继电器 HBDLX-21/2零序过电压继电器&#xf…

【Python】pact-python模块进行契约测试

Pact是一个契约测试框架&#xff0c;有多种语言实现&#xff0c;本文主要介绍模块pact-python进行契约测。 官网&#xff1a;https://docs.pact.io/implementation_guides/python/readme 安装命令&#xff1a;pip install pact-python 安装过程中如果报错&#xff0c;安装失…

Deepsort从入门到精通

1 &#xff0c;sort和Deepsort算法 在目标检测领域&#xff0c;sort&#xff08;Simple Online and Realtime Tracking&#xff09;算法和 DeepSORT&#xff08;Deep Learning for Multi-Object Tracking&#xff09;算法是两种常用的目标追踪算法&#xff0c;它们通常与目标检…

京东数据分析:2023年Q3户外鞋服市场分析报告(冲锋衣行业销售数据分析)

从露营、骑行、徒步、桨板、垂钓、飞盘、滑雪到如今的city walk&#xff0c;近两年户外运动已经成为了年轻人新的生活方式。户外运动的爆发也刺激了人们对于鞋服在穿搭、场景化、专业性功能等方向的需求&#xff0c;户外鞋服市场迎来增长。 而全国性的降温则带给目前的户外鞋服…

嵌入式养成计划-52----ARM--开发板介绍--相关硬件基础内容介绍--GPIO讲解

一百三十一、开发板介绍 131.1 核心板介绍 131.2 拓展板 一百三十二、相关硬件基础内容介绍 132.1 PCB PCB&#xff08; Printed Circuit Board&#xff09;&#xff0c;中文名称为印制电路板&#xff0c;又称印刷线路板&#xff0c; 是重要的电子部件&#xff0c;是电子元器…

WPS的JS宏基础(一)

基础知识 1、简单的第一个宏 //注意function只能全部用小写 function demo(){alert("你好!") }2、录制宏生成工资条 function 使用录制宏自动生成代码以JS宏为例()//使用相对引用 {Selection.Copy(undefined);ActiveCell.Offset(5, 0).Range("A1:M4").I…

【C++】new和delete深度解析

文章目录 一、new/delete是什么&#xff1f;1.new2.delete 二、new/delete怎么用&#xff1f;1.new2.delete3.new[]4.[]delete 三、new/delete为什么&#xff1f;1.为什么有operator new/operator delete?2.为什么要匹配使用new和delete? new/delete测试环境&#xff1a;visu…

使用Pytorch的一些小细节(一)

文章目录 前言数据结构-张量max函数索引函数赋值函数拼接函数 前言 由于不经常动手写代码&#xff0c;所以对于python语言中的常见数据结构的用法也不是很熟悉&#xff0c;对于pytorch中的数据结构就更加不熟悉了。之前的代码基础是基于C语言的&#xff0c;属性都是自己定义&a…

vue3使用element plus时遇到的问题

1.el-form中input无法输入 问题描述&#xff1a;在el-form中的el-input中输入数字或字母时出现卡顿&#xff0c;输入不进去的现象 问题原因&#xff1a;el-form的ref和model的名称写成了一样的单词 问题解决&#xff1a;两个不能一样 2.input去除边框 问题描述&#xff1a;…

【python海洋专题三十九】海洋指数画法--折线图样式三--不同颜色的线条

【python海洋专题三十九】海洋指数画法–折线图样式三–不同颜色的线条 数据:AMO_index 图像展示: 图片 往期推荐 图片 【python海洋专题一】查看数据nc文件的属性并输出属性到txt文件 【python海洋专题二】读取水深nc文件并水深地形图 【python海洋专题三】图像修饰之画…