STM-32:SPI通信协议/W25Q64简介—软件SPI读写W25Q64

2023-11-18

一、SPI简介

SPI是串行外设接口(Serial Peripheral Interface)的缩写,是美国摩托罗拉公司(Motorola)最先推出的一种同步串行传输规范,也是一种单片机外设芯片串行扩展接口,是一种高速、全双工、同步通信总线,所以可以在同一时间发送和接收数据,SPI没有定义速度限制,通常能达到甚至超过10M/bps。

SPI有主、从两种模式,通常由一个主模块和一个或多个从模块组成(SPI不支持多主机),主模块选择一个从模块进行同步通信,从而完成数据的交换。提供时钟的为主设备(Master),接收时钟的设备为从设备(Slave),SPI接口的读写操作,都是由主设备发起,当存在多个从设备时,通过各自的片选信号进行管理。

SPI通信原理很简单,需要至少4根线,单向传输时3根线,它们是MISO(主设备数据输入)、MOSI(主设备数据输出)、SCLK(时钟)和CS/SS(片选):

MISO( Master Input Slave Output):主设备数据输入,从设备数据输出;
MOSI(Master Output Slave Input):主设备数据输出,从设备数据输入;
SCLK(Serial Clock):时钟信号,由主设备产生;
CS/SS(Chip Select/Slave Select):从设备使能信号,由主设备控制,一主多从时,CS/SS是从芯片是否被主芯片选中的控制信号,只有片选信号为预先规定的使能信号时(高电位或低电位),主芯片对此从芯片的操作才有效。
在这里插入图片描述

1.1电路模式

采用一主多从的模式、同步,全双工
在这里插入图片描述
所有SPI设备的SCK、MOSI、MISO分别连在一起
主机另外引出多条SS控制线,分别接到各从机的SS引脚
输出引脚配置为推挽输出,输入引脚配置为浮空或上拉输入
推挽输出:高低电平都有很强的驱动能力,使得SPI引脚信号的下降沿和上升沿非常迅速
(IIC因为要实现半双工,经常切换输出输入,IIC又要实现多主机的时钟同步和总线仲裁,若使用推挽输出任意电源短路)
SPI的MISO可能有冲突,一位内主机是输入,三个从机都是输出,若三个从机始终是推挽输出,势必会导致冲突。
故SPI有个规定:
当从机的SS引脚为高电平时,即从机未被选中,其MISO引脚必须切换成高阻态,高阻态相当于引脚断开,不输出任何电平,这样可以防止一条线有多个输出,导致电平冲突问题
SS为低电平时,MISO才允许变为推挽输出(切换在从机中,不需要关注)

1.2通信原理

SPI主设备和从设备都有一个串行移位寄存器,主设备通过向它的SPI串行寄存器写入一个字节来发起一次传输。
在这里插入图片描述
SPI数据通信的流程可以分为以下几步:

1、主设备发起信号,将CS/SS拉低,启动通信。

2、主设备通过发送时钟信号,来告诉从设备进行写数据或者读数据操作(采集时机可能是时钟信号的上升沿(从低到高)或下降沿(从高到低),因为SPI有四种模式,后面会讲到),它将立即读取数据线上的信号,这样就得到了一位数据(1bit)。

3、主机(Master)将要发送的数据写到发送数据缓存区(Menory),缓存区经过移位寄存器(缓存长度不一定,看单片机配置),串行移位寄存器通过MOSI信号线将字节一位一位的移出去传送给从机,同时MISO接口接收到的数据经过移位寄存器一位一位的移到接收缓存区。

4、从机(Slave)也将自己的串行移位寄存器(缓存长度不一定,看单片机配置)中的内容通过MISO信号线返回给主机。同时通过MOSI信号线接收主机发送的数据,这样,两个移位寄存器中的内容就被交换。

1.3SPI时序基本单元

1.3.1起始和终止

起始条件:SS从高电平切换到低电平
终止条件:SS从低电平切换到高电平

在这里插入图片描述

1.3.2交换字节

Mode0:CPOL=0,CPHA =0:当空闲态时,SCK处于低电平,数据采样是在第1个边沿,也就是SCK由低电平到高电平的跳变,所以数据采样是在上升沿(准备数据),(发送数据)数据发送是在下降沿。
在这里插入图片描述
Mode1:CPOL=0,CPHA=1:当空闲态时,SCK处于低电平,数据发送是在第2个边沿,也就是SCK由低电平到高电平的跳变,所以数据采样是在下降沿,数据发送是在上升沿。
在这里插入图片描述
Mode2:CPOL=1,CPHA=0:当空闲态时,SCK处于高电平,数据采集是在第1个边沿,也就是SCK由高电平到低电平的跳变,所以数据采集是在下降沿,数据发送是在上升沿。
在这里插入图片描述
Mode3:CPOL=1,CPHA=1:当空闲态时,SCK处于高电平,数据发送是在第2个边沿,也就是SCK由高电平到低电平的跳变,所以数据采集是在上升沿,数据发送是在下降沿。
在这里插入图片描述

二、W25Q64

2.1W25Q64简介

W25Qxx系列是一种低成本、小型化、使用简单的非易失性存储器,常应用于数据存储、字库存储、固件程序存储等场景
存储介质:Nor Flash(闪存)
时钟频率:80MHz / 160MHz (Dual SPI) / 320MHz (Quad SPI)
存储容量(24位地址):
W25Q40: 4Mbit / 512KByte
W25Q80: 8Mbit / 1MByte
W25Q16: 16Mbit / 2MByte
W25Q32: 32Mbit / 4MByte
W25Q64: 64Mbit / 8MByte
W25Q128: 128Mbit / 16MByte
W25Q256: 256Mbit / 32MByte

在这里插入图片描述

2.2W25Q64硬件电路

在这里插入图片描述

2.3W25Q64框图

在这里插入图片描述

2.4Flash操作注意事项

写入操作时:
写入操作前,必须先进行写使能
每个数据位只能由1改写为0,不能由0改写为1
写入数据前必须先擦除,擦除后,所有数据位变为1
擦除必须按最小擦除单元进行
连续写入多字节时,最多写入一页的数据,超过页尾位置的数据,会回到页首覆盖写入
写入操作结束后,芯片进入忙状态,不响应新的读写操作

读取操作时:
直接调用读取时序,无需使能,无需额外操作,没有页的限制,读取操作结束后不会进入忙状态,但不能在忙状态时读取

三、软件SPI读写W25Q64

3.1接线图

在这里插入图片描述

3.2程序代码

MySPI.c

#include "stm32f10x.h"                  // Device header

void MySPI_W_CS(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue);
}

void MySPI_W_SCK(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOA, GPIO_Pin_5, (BitAction)BitValue);
}

uint8_t MySPI_R_MISO(void)
{
	return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6);
}

void MySPI_W_MOSI(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOA, GPIO_Pin_7, (BitAction)BitValue);
}

void MySPI_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);
	
	MySPI_W_CS(1);
	MySPI_W_SCK(0);
}

void MySPI_Start(void)
{
	MySPI_W_CS(0);
}

void MySPI_Stop(void)
{
	MySPI_W_CS(1);
}

uint8_t MySPI_SwapByte(uint8_t ByteSend)
{
	uint8_t i, ByteReceive = 0x00;
	
	for (i = 0; i < 8; i ++)
	{
		MySPI_W_MOSI(ByteSend & (0x80 >> i));
		MySPI_W_SCK(1);
		if (MySPI_R_MISO() == 1){ByteReceive |= (0x80 >> i);}
		MySPI_W_SCK(0);
	}
	
	return ByteReceive;
}

MySPI.h

#ifndef __MYSPI_H
#define __MYSPI_H

void MySPI_Init(void);
void MySPI_Start(void);
void MySPI_Stop(void);
uint8_t MySPI_SwapByte(uint8_t ByteSend);

#endif

W25Q64.c

#include "stm32f10x.h"                  // Device header
#include "MySPI.h"
#include "W25Q64_Ins.h"

void W25Q64_Init(void)
{
	MySPI_Init();
}

void W25Q64_ReadID(uint8_t *MID, uint16_t *DID)
{
	MySPI_Start();
	MySPI_SwapByte(W25Q64_JEDEC_ID);
	*MID = MySPI_SwapByte(0xFF);
	*DID = MySPI_SwapByte(0xFF);
	*DID <<= 8;
	*DID |= MySPI_SwapByte(0xFF);
	MySPI_Stop();
}

void W25Q64_WriteEnable(void)
{
	MySPI_Start();
	MySPI_SwapByte(W25Q64_WRITE_ENABLE);
	MySPI_Stop();
}

void W25Q64_WaitBusy(void)
{
	uint32_t Timeout;
	MySPI_Start();
	MySPI_SwapByte(W25Q64_READ_STATUS_REGISTER_1);
	Timeout = 100000;
	while ((MySPI_SwapByte(0xFF) & 0x01) == 0x01)
	{
		Timeout --;
		if (Timeout == 0)
		{
			break;
		}
	}
	MySPI_Stop();
}

void W25Q64_SectorErase(uint32_t Address)
{
	W25Q64_WriteEnable();
	MySPI_Start();
	MySPI_SwapByte(W25Q64_SECTOR_ERASE_4KB);
	MySPI_SwapByte(Address >> 16);
	MySPI_SwapByte(Address >> 8);
	MySPI_SwapByte(Address);
	MySPI_Stop();
	W25Q64_WaitBusy();
}

void W25Q64_ChipErase(void)
{
	W25Q64_WriteEnable();
	MySPI_Start();
	MySPI_SwapByte(W25Q64_CHIP_ERASE);
	MySPI_Stop();
	W25Q64_WaitBusy();
}

void W25Q64_PageProgram(uint32_t Address, uint8_t *DataArray, uint32_t Count)
{
	uint8_t i;
	W25Q64_WriteEnable();
	MySPI_Start();
	MySPI_SwapByte(W25Q64_PAGE_PROGRAM);
	MySPI_SwapByte(Address >> 16);
	MySPI_SwapByte(Address >> 8);
	MySPI_SwapByte(Address);
	for (i = 0; i < Count; i ++)
	{
		 MySPI_SwapByte(DataArray[i]);
	}
	MySPI_Stop();
	W25Q64_WaitBusy();
}

void W25Q64_ReadData(uint32_t Address, uint8_t *DataArray, uint32_t Count)
{
	uint8_t i;
	MySPI_Start();
	MySPI_SwapByte(W25Q64_READ_DATA);
	MySPI_SwapByte(Address >> 16);
	MySPI_SwapByte(Address >> 8);
	MySPI_SwapByte(Address);
	for (i = 0; i < Count; i ++)
	{
		DataArray[i] = MySPI_SwapByte(0xFF);
	}
	MySPI_Stop();
}

W25Q64.h

#ifndef __W25Q64_H
#define __W25Q64_H

void W25Q64_Init(void);
void W25Q64_ReadID(uint8_t *MID, uint16_t *DID);
void W25Q64_SectorErase(uint32_t Address);
void W25Q64_ChipErase(void);
void W25Q64_PageProgram(uint32_t Address, uint8_t *DataArray, uint32_t Count);
void W25Q64_ReadData(uint32_t Address, uint8_t *DataArray, uint32_t Count);

#endif

W25Q64_Ins.h

#ifndef __W25Q64_INS_H
#define __W25Q64_INS_H

#define W25Q64_WRITE_ENABLE							0x06
#define W25Q64_WRITE_DISABLE						0x04
#define W25Q64_READ_STATUS_REGISTER_1				0x05
#define W25Q64_READ_STATUS_REGISTER_2				0x35
#define W25Q64_WRITE_STATUS_REGISTER				0x01
#define W25Q64_PAGE_PROGRAM							0x02
#define W25Q64_QUAD_PAGE_PROGRAM					0x32
#define W25Q64_BLOCK_ERASE_64KB						0xD8
#define W25Q64_BLOCK_ERASE_32KB						0x52
#define W25Q64_SECTOR_ERASE_4KB						0x20
#define W25Q64_CHIP_ERASE							0xC7
#define W25Q64_ERASE_SUSPEND						0x75
#define W25Q64_ERASE_RESUME							0x7A
#define W25Q64_POWER_DOWN							0xB9
#define W25Q64_HIGH_PERFORMANCE_MODE				0xA3
#define W25Q64_CONTINUOUS_READ_MODE_RESET			0xFF
#define W25Q64_RELEASE_POWER_DOWN_HPM_DEVICE_ID		0xAB
#define W25Q64_MANUFACTURER_DEVICE_ID				0x90
#define W25Q64_READ_UNIQUE_ID						0x4B
#define W25Q64_JEDEC_ID								0x9F
#define W25Q64_READ_DATA							0x03
#define W25Q64_FAST_READ							0x0B
#define W25Q64_FAST_READ_DUAL_OUTPUT				0x3B
#define W25Q64_FAST_READ_DUAL_IO					0xBB
#define W25Q64_FAST_READ_QUAD_OUTPUT				0x6B
#define W25Q64_FAST_READ_QUAD_IO					0xEB
#define W25Q64_OCTAL_WORD_READ_QUAD_IO				0xE3

#endif

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "W25Q64.h"

uint8_t MID;
uint16_t DID;

uint8_t ArrayWrite[] = {0x01, 0x02, 0x03, 0x04};
uint8_t ArrayRead[4];

int main(void)
{
	OLED_Init();
	W25Q64_Init();
	
	OLED_ShowString(1, 1, "MID:   DID:");
	OLED_ShowString(2, 1, "W:");
	OLED_ShowString(3, 1, "R:");
	
	W25Q64_ReadID(&MID, &DID);
	OLED_ShowHexNum(1, 5, MID, 2);
	OLED_ShowHexNum(1, 12, DID, 4);
	
	W25Q64_SectorErase(0x000000);
	W25Q64_PageProgram(0x000000, ArrayWrite, 4);
	
	W25Q64_ReadData(0x000000, ArrayRead, 4);
	
	OLED_ShowHexNum(2, 3, ArrayWrite[0], 2);
	OLED_ShowHexNum(2, 6, ArrayWrite[1], 2);
	OLED_ShowHexNum(2, 9, ArrayWrite[2], 2);
	OLED_ShowHexNum(2, 12, ArrayWrite[3], 2);
	
	OLED_ShowHexNum(3, 3, ArrayRead[0], 2);
	OLED_ShowHexNum(3, 6, ArrayRead[1], 2);
	OLED_ShowHexNum(3, 9, ArrayRead[2], 2);
	OLED_ShowHexNum(3, 12, ArrayRead[3], 2);
	
	while (1)
	{
		
	}
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STM-32:SPI通信协议/W25Q64简介—软件SPI读写W25Q64 的相关文章

  • c++基础:循环练习案例展示

    1 猜数字 题目 系统随机生成一个1到100的数字 玩家进行猜测 如果猜错 提示玩家数字过大或过小 如果猜对恭喜玩家胜利 并且退出游戏 代码 include
  • Portainer -- Docker可视化管理工具

    http blog csdn net A632189007 article details 78779920 https portainer io install html https portainer readthedocs io en
  • vue3 computed

    前言 import computed from vue let aa computed gt 传回调函数 let aa computed 传对象 返回的类似一个ref包装的响应式对象 如果值是基本数据类型 需要 value进行拆箱 一 常规
  • Kaggle猫狗分类Pytorch CNN

    介绍 猫狗分类来源于Kaggle上的一个入门竞赛 https www kaggle com competitions dogs vs cats redux kernels edition overview 代码及解释 首先 导入一系列的库

随机推荐

  • python如何输出文字和变量_python中print怎么输出文字和变量

    本帖最后由 ButcherRabbit 于 2017 6 27 13 06 编辑 你说的知识点是 字符串拼接 n 3 print 还有多少 str n 机会 用加号拼接的话 记得拼接的元素必须同一个类型 如 还有多少 和 机会 属于字符串类
  • Cocos2dx-OpenGL ES2.0教程:编写自己的shader(2)

    在上篇文章中 我给大家介绍了如何在cocos2d x里面绘制一个三角形 当时我们使用的是cocos2d x引擎自带的shader和一些辅助函数 在本文中 我将演示一下如何编写自己的shader 同时 我们还会介绍VBO 顶点缓冲区对象 和V
  • 确知信号分析

    写在前面 现代通信原理是电子系的一门专业必修课 介绍了通信原理系统的基本概念 基本原理 基本技术以及设计和分析的方法 俗话说得好 实践出真知 要想学好这门课 必要的动手实践是必不可少的 实践是基于Matlab上实现仿真的 Matlab具有函
  • Access denied for user XX

    解决方法 将pom xml中的mysql connector java降低版本 直接去maven仓库复制 https mvnrepository com 比如
  • python+flask+mysql实现数据可视化

    准备工作 1 Python3 2 Echarts 下载地址 echarts 3 Flask 项目结构 创建DB 使用sqlite数据库 coding utf 8 author liudinglong software pycharm fil
  • 1.R语言基础知识

    目录 一 R语言的介绍 R语言的下载与按照 Rstudio R包的安装 工作空间管理 基本运算 二 R的数据结构 1 数据类型 2 数据类型的转换和判断 3 数据结构 三 导入 导出数据 获取内置数据集 获取其他格式的数据 四 数据框的常用
  • AI绘画指南:在CentOS7中安装Stable Diffusion WebUI

    一 安装GPU驱动 1 1 购买云服务器 在腾讯云选购一台GPU型的服务器 最好的境外的 境外的服务器后面关于镜像加速的步骤都可以跳过 购买成功之后腾讯的站内信会发送初始的密码给你 登录成功后会自动帮我们安装相关的驱动 提示整个过程大概需要
  • LeetCode-283. 移动零【数组,双指针】

    LeetCode 283 移动零 数组 双指针 题目描述 解题思路一 首先想到的是双指针 但是不行 非零元素的位置会改变 解题思路二 改进的是每次从当前0元素的位置取后面第一个非0元素替换过来 替换之和那个break非常重要 解题思路三 优
  • Future 和 Callable

    一 Runnable 缺陷 不能返回一个返回值 不能抛出 checked Execption 二 Callable接口 类似于Runnable 被其他线程执行的任务 实现call方法 有返回值 三 Future的作用 Callable和Fu
  • 【infiniband】 MAD、 uMAD、Verbs、RDMACM

    1 MAD Management Datagram MAD是InfiniBand网络中用于管理和配置的数据报文 它包含了各种类型的管理操作 如查询端口状态 配置端口参数等 MAD通常用于执行网络管理任务 2 uMAD User MAD uM
  • 模板类 通用数组的实现

    实现自定义数组 重载 lt lt 运算符 并且数组可以使用自定义类 头文件 ifndef MYARRAY H define MYARRAY H include
  • pandas datetime与时间戳互相转换,字符串转换datetime

    参考pandas to datetime的api 字符串转换为pandas datetime 通过to datetime函数可以把字符串转换为pandas datetime df pd DataFrame date 2011 04 24 0
  • python的xlrd、xlwt模块/pymsql使用

    xlrd模块 https www cnblogs com machangwei 8 p 10736528 html label0 xlwt模块https www cnblogs com machangwei 8 p 10738244 htm
  • Filter过滤器实现权限拦截

    一 要求 用户登陆之后才能进入主页 用户注销之后不能进入首页 二 思路 用户登陆之后 向session中放入用户的数据 进入主页的时候要判断用户是否已经登陆 在过滤器中实现 public void doFilter ServletReque
  • 电脑怎样连接打印机?分享4个简单操作!

    为了更方便学习 我买了一个打印机来打印需要用的资料 但是操作了半天还是没连接上 想请问一下有经验的朋友是怎么将打印机与电脑进行连接的呢 在现代人的工作和生活中 打印机是一个重要的设备 我们可以利用打印机进行资料 文件等的打印 但是也会有很多
  • SpringBoot调用PageHelper.startPage(Object params)报错:分页查询缺少必要的参数:XXX

    问题描述 项目中使用了MyBatis分页插件 调用以下方法实现分页 无论传入JavaBean还是Map都报错 分页查询缺少必要的参数 XXX Map
  • 【Docker】云原生利用Docker确保环境安全、部署的安全性、安全问题的主要表现和新兴技术产生

    前言 Docker 是一个开源的应用容器引擎 让开发者可以打包他们的应用以及依赖包到一个可移植的容器中 然后发布到任何流行的Linux或Windows操作系统的机器上 也可以实现虚拟化 容器是完全使用沙箱机制 相互之间不会有任何接口 云原生
  • Python学习 第二章 数据类型

    Python学习 第二章 数据类型上 1 数字 1 1 整型 int 1 2 浮点型 float 1 3 布尔类型 bool 1 4 代码实现 1 5 复数 2 字符串 string 2 1 如果字符串内容中出现了引号 2 2 代码实现 2
  • pandas生成excel文件

    可以使用pandas中的to excel 函数将DataFrame数据写入Excel文件 例如 import pandas as pd 创建测试数据 data name Mike John Bob age 25 32 45 city New
  • STM-32:SPI通信协议/W25Q64简介—软件SPI读写W25Q64

    目录 一 SPI简介 1 1电路模式 1 2通信原理 1 3SPI时序基本单元 1 3 1起始和终止 1 3 2交换字节 二 W25Q64 2 1W25Q64简介 2 2W25Q64硬件电路 2 3W25Q64框图 2 4Flash操作注意