软件模拟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();
}

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

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

相关文章

ke10javaweb停更

<jsp:getProperty property"isval" name "username"/> property来修改属性,name是类 所以如果调用tip在前面那么就是没有设置值 证明是先到java里面去运转

AI编程工具:一站式编程解决方案,引领AI编程新时代

在人工智能的巨浪之下&#xff0c;编程领域正在经历一场深刻的转型。这场转型的核心&#xff0c;就是AI智能编程工具的出现。它们为开发者提供了一种全新的编程方式&#xff0c;极大地提高了编程效率。在这场变革中&#xff0c;DevChat无疑是引领者之一。 一、DevChat&#xf…

计算机毕业设计 基于SpringBoot高校毕业与学位资格审核系统的设计与实现 Javaweb项目 Java实战项目 前后端分离 文档报告 代码讲解 安装调试

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

AMD发布大小核 CPU,6核心直接砍成单核了

2022年 Intel 第12代酷睿发布&#xff0c;PE 大小核设计被正式带到了 PC 上。 P-Core 也就是传统的大核有着高性能、高功耗&#xff0c;而 E-Core 小核则是更讲究能效比以更低频率运行。 虽说小蝾也曾有对 Windows 调度方面的怀疑&#xff0c;但多线程性能确实实打实证明了其优…

2023年最受欢迎的9款产品原型工具大揭秘!

1.Pop (Prototyping on Paper) 价格&#xff1a;免费试用30天专业版960RMB/人/年 学习曲线&#xff1a;低 简介&#xff1a;POP是设计界面的原型工具&#xff0c;适用于iOS和Android平台。在POP的帮助下&#xff0c;开发人员或设计师只需在纸上简单地描述创意或想法&#xff…

SPI简介及FPGA通用MOSI模块实现

简介 SPI&#xff08;Serial Peripheral Interface&#xff0c;串行外围设备接口&#xff09;通讯协议&#xff0c;是Motorola公司提出的一种同步串行接口技术。是一种高速、全双工、同步通信总线。在芯片中只占用四根管脚用来控制及数据传输。 优缺点&#xff1a; SPI通讯协…

向量数据库:释放数据潜能,重塑信息世界

前言 想必各位开发者一定使用过关系型数据库MySQL去存储我们的项目的数据&#xff0c;也有部分人使用过非关系型数据库Redis去存储我们的一些热点数据作为缓存&#xff0c;提高我们系统的响应速度&#xff0c;减小我们MySQL的压力。那么你有听说过向量数据库吗&#xff1f;知道…

白嫖阿里云服务器教程来了,薅秃阿里云!

白嫖阿里云服务器攻略来了&#xff0c;在阿里云免费试用中心可以申请免费服务器&#xff0c;但是阿里云百科不建议选择免费的&#xff0c;只有3个月使用时长&#xff0c;选择99元服务器不是更香&#xff0c;2核2G配置3M固定带宽&#xff0c;一年99元&#xff0c;重点是新老用户…

【Linux】-文件操作(重定向、缓冲区以及Linux下一切皆文件的详解)

&#x1f496;作者&#xff1a;小树苗渴望变成参天大树&#x1f388; &#x1f389;作者宣言&#xff1a;认真写好每一篇博客&#x1f4a4; &#x1f38a;作者gitee:gitee✨ &#x1f49e;作者专栏&#xff1a;C语言,数据结构初阶,Linux,C 动态规划算法&#x1f384; 如 果 你 …

如何自己实现一个丝滑的流程图绘制工具(九) 自定义连接线

背景 产品又有更近的想法了&#xff0c;bpmn-js的连接线你用的时候是看不到的&#xff0c;也就是你从左侧点击连接线的没有线随鼠标移动. 但是产品想要看得见的连接线移动拖拽。 咩有办法&#xff0c;不能换框架&#xff0c;那就只能自己实现啦&#xff01; 思路&#xff1a; …

06-MySQL-进阶-视图存储函数存储过程触发器

涉及资料 链接&#xff1a;https://pan.baidu.com/s/1M1oXN_pH3RGADx90ZFbfLQ?pwdCoke 提取码&#xff1a;Coke 一、视图 数据准备 create table student(id int auto_increment comment 主键ID primary key,name varchar(10) null comment 姓名,no varchar(10) null co…

JDBC(一)

第1章&#xff1a;JDBC概述 1.1 数据的持久化 持久化(persistence)&#xff1a;把数据保存到可掉电式存储设备中以供之后使用。大多数情况下&#xff0c;特别是企业级应用&#xff0c;数据持久化意味着将内存中的数据保存到硬盘上**&#xff0c;而持久化的实现过程大多通过各种…

利用中断做数码表

功能要求:1.按下KEY1&#xff0c;显示数字开始每0.5秒加1&#xff0c;加到&#xff08;10学号&#xff09;返回0&#xff0c;0显示2秒后继续开始重复加1。 2. 任何时候按下KEY2数字清零&#xff0c;并停止加1。 3. KEY1和KEY2分别采用查询和外部中断方式。 要求程序中有硬件…

概念解析 | 高光谱图像:揭开自然世界的神秘面纱

注1:本文系“概念解析”系列之一,致力于简洁清晰地解释、辨析复杂而专业的概念。本次辨析的概念是:高光谱图像 高光谱图像:揭开自然世界的神秘面纱 Hyperspectral imaging - Wikipedia 背景介绍 我们生活的世界充满了丰富多彩的颜色。这些颜色来源于各种物体反射或吸收不同波长…

PM - 项目管理 产品管理区别

产品管理和项目管理是两个在企业中至关重要的职能部门&#xff0c;它们各自承担着不同的职责和任务。虽然两者在某些方面存在重叠&#xff0c;但它们的核心目标和方法有很大的不同。本文将对产品管理和项目管理进行详细的比较和分析。 “项目管理和产品管理有什么区别&#xff…

微服务架构下如何使用多环境多服务联合调试

在 微服务 架构中&#xff0c;项目被分解成多个独立的模块&#xff0c;每个模块对应一个微服务。这些微服务各自承担不同的任务&#xff0c;例如用户管理、支付处理或订单管理。它们可以使用不同的技术栈&#xff0c;独立开发、测试和部署。微服务之间通过 API 等方式进行通信&…

Node.js中的child_process模块的作用

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…

【Python报错合集】Python元组tuple、张量tensor(IndexError、TypeError、RuntimeError……)~持续更新

文章目录 IndexError1. tuple index out of rangea. 示例代码b.报错原因c.解决方案 TypeError1. len() of a 0-d tensora. 示例代码b.报错原因c.解决方案 RuntimeError1. output with shape … doesnt match the broadcast shape …a. 示例代码b.报错原因c.解决方案 2. Cant ca…

apollo docker搭建

1 mysql搭建 先需要一个mysql&#xff0c;mysql我使用的是5.7&#xff0c;搭建过程忽略 2 数据导入 我们需要从github上下载apolloportaldb.sql, apolloconfigdb.sql 2个sql 随后cp apolloconfigdb.sql apolloconfigdbUat.sql cp apolloconfigdb.sql apolloconfigdbDev.sq…

内存取证分析

内存取证会临时存储一些有价值的信息 查看内存进程的信息等等&#xff0c;对溯源这种事情有帮助。不过要用到专门的工具获取信息 运行exe文件&#xff0c;输入y将一个系统的镜像完整的下载下来&#xff0c; 这就是保存下来的文件。 视频上别的工具搞不来&#xff0c;要不就是…