STM32--SPI通信与W25Q64(1)

news2024/9/21 20:43:09

文章目录

  • 前言
  • SPI通信
    • 硬件电路
    • 移位过程
  • SPI时序
    • 起始与终止条件
    • 交换一个字节
  • W25Q64
    • 硬件电路
    • 框图
  • FLASH操作注意事项
  • 软件SPI读写W25Q64

前言

USART串口链接入口

I2C通信链接入口

SPI通信

SPI(Serial Peripheral Interface)是一种高速的、全双工、同步的串行通信协议。通常用于连接主控芯片和外围设备,比如传感器、存储器、显示屏等。SPI使用简单,只需要几根线就可以实现进行通信。
在这里插入图片描述

硬件电路

在这里插入图片描述
主要线路:

SCLK(时钟信号):由主设备产生,用于同步数据传输的时钟信号。
MOSI(主设备输出从设备输入):主设备将数据发送给从设备的数据线。
MISO(主设备输入从设备输出):从设备将数据发送给主设备的数据线。
SS/CS(片选信号):由主设备控制,用于选择要进行通信的特定设备。

上图中,主机连接着多个从机,但在通信时,只能对一个从机进行SPI通信,会通过选定的从机的片选信号SS从高电平置于低电平(其他没有选中的保持高电平)让主机与其通信。

移位过程

在这里插入图片描述
由于有两条传输数据线,所以SPI通信能做到同时进行发送数据和接收数据的特点。

主机和从机都由主机的波特率发生器控制着时钟信号,实现同步的传输。

首先主机会将移位寄存器的高位通过MOSI数据线传送到从机的移位寄存器的最低位;同时,从机的移位寄存器的最高位会通过MISO数据线传送到主机移位寄存器的最低位。两个移位寄存器将最高位的数据传出之后,移位寄存器就会进行向右移位,因此最低位也会腾出空间,让主机的最高位数据放到从机的最低位,从机的最低位数据放到主机的最低位。以此循环八次,就能将一个字节的数据进行转换了
在这里插入图片描述

SPI时序

起始与终止条件

起始条件:SS从高电平切换到低电平
终止条件:SS从低电平切换到高电平
在这里插入图片描述
这是片选信号,高低电平的切换代表SPI时序的开始和结束。

交换一个字节

交换一个字节(模式0)
CPOL=0:空闲状态时,SCK为低电平
CPHA=0:SCK第一个边沿移入数据,第二个边沿移出数据
在这里插入图片描述
对于SPI通信,由于是同时进行数据传输,所以称之为字节的交换。
交换字节有4个模式,不同之处就在于空闲状态SCK是高电平还是低电平;还有一个从SCK的第一个边沿还是第二个边沿移入数据,这里将介绍模式0的交换,其他同理。

首先这里说的移入数据和移出数据,是指数据的移出会先放在MOSI数据线或者是MISO数据线上,通过一定的时间再把数据放入对方的最低位。所以,只有先移出数据,才能移入数据。
而这里的却从SCK的第一个边沿就移入数据,是因为主机和从机在SS的低边沿就进行将数据移出到MOSI和MISO上,所以会在SCK的高边沿就进行数据的移入,到了SCK的低边沿就将数据移出,依次重复八次,就将一个字节交换成功了

其他模式
交换一个字节(模式1)
CPOL=0:空闲状态时,SCK为低电平
CPHA=1:SCK第一个边沿移出数据,第二个边沿移入数据
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
这是主机向选定的从机发送一个0x06的信号,由于对于从机发送的内容不关心,所以默认为0xFF。所以一般情况下,只有我们选择读取从机的数据,MISO的数据线才会有波形变化。

W25Q64

W25Q64是一款由华邦公司推出的大容量SPI FLASH产品,其容量为64Mb(8MB)。它属于W25Q系列器件,相比普通的串行闪存硬件,在灵活性和性能方面也有更出色的表现。
W25Q64可以用于存储图片数据,字库数据、音频数据以及保存设备运行日志文件等。
该芯片将8M字节的容量分为128块,每个块包含16个扇区,每个扇区有4K字节。支持双路和四路SPI接口,具有较高的数据传输速率。
在这里插入图片描述
存储介质:Nor Flash(闪存)
时钟频率:80MHz / 160MHz (Dual SPI) / 320MHz (Quad SPI)

硬件电路

在这里插入图片描述

引脚功能
VCC、GND电源(2.7~3.6V)
CS(SS)SPI片选
CLK(SCK)SPI时钟
DI(MOSI)SPI主机输出从机输入
DO(MISO)SPI主机输入从机输出
WP写保护
HOLD数据保持

看黄色部分即可,左边是外部引脚接口,右边是芯片电路;

在引脚名上加上一横线表示接通时默认为低电平,VCC与GND连接时会有一个滤波电容进行滤波,还并联一个指示灯表示是否已经通电

HOLD数据保持:相当一个暂停键;当你写入数据一半时,要在别的设备使用SPI通信,那么在当前设备你就可以触发HOLD,当前设备的SPI时序就会保持静止,你就可以使用SPI对别的设备进行使用,当回到当前设备时,HOLD解除,会从禁止的SPI时序进行恢复。

WP写保护:可以通过设置特殊的写保护位来防止数据被修改。有助于保护重要数据免受意外的写操作。

框图

在这里插入图片描述
上面一大部分就是存储区间,将8M字节的容量分为128块,每个块包含16个扇区,每个扇区有4K字节。每个扇区还包括16个的页区,每个页区有256字节,页是最小单位。

而写入和读取都由左下角的SPI命令与控制逻辑的黑盒进行控制;
接着看到上面,是写逻辑和状态寄存器,可以通过状态寄存器来判断是否已经写入数据;
通过高压发电机来对数据进行擦除;
下面是页地址锁存器和字节地址锁存器,会对块区间通过行解码和列解码,可以判定你在哪个页区进行写入和读出;
块区域的下面是一个256字节页缓冲区,数据写入需要一定的时间,会通过缓冲区来进行缓冲。

FLASH操作注意事项

写入操作时
写入操作前,必须先进行写使能
每个数据位只能由1改写为0,不能由0改写为1
写入数据前必须先擦除,擦除后,所有数据位变为1
擦除必须按最小擦除单元进行(扇区)
连续写入多字节时,最多写入一页的数据,超过页尾位置的数据,会回到页首覆盖写入
写入操作结束后,芯片进入忙状态,不响应新的读写操作
读取操作时
直接调用读取时序,无需使能,无需额外操作,没有页的限制,读取操作结束后不会进入忙状态,但不能在忙状态时读取

软件SPI读写W25Q64

OLED代码链接入口

连接方式:
在这里插入图片描述
将数据存储在W25Q64中,通过断电测试它的存储功能;

大体思路:实现SPI通信的时序条件,接着利用SPI通信实现W25Q64时序,最后在主程序实现对FLASH的测试

MySPI.c

#include "stm32f10x.h"                  // Device header

//片选电平
void MySPI_W_SS(uint8_t Byte)
{
    GPIO_WriteBit(GPIOA,GPIO_Pin_4,(BitAction)Byte);
}
//时钟电平
void MySPI_W_SCK(uint8_t Byte)
{
    GPIO_WriteBit(GPIOA,GPIO_Pin_5,(BitAction)Byte);
}
//主机发送到从机
void MySPI_W_MOSI(uint8_t Byte)
{
    GPIO_WriteBit(GPIOA,GPIO_Pin_7,(BitAction)Byte);
}
//从机发送到主机
uint8_t MySPI_R_MISO()
{
    return GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6);
}

//初始化
void MySPI_Init()
{
    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_SS(1);
    MySPI_W_SCK(0);
}
//开始
void MySPI_Start()
{
    MySPI_W_SS(0);
}
//结束
void MySPI_Stop()
{
    MySPI_W_SS(1);
}
//交换字节
uint8_t MySPI_SwapByte(uint8_t SendByte)
{
    uint8_t ReceiveByte=0x00,i;
    for(i=0;i<8;i++)
    {
        MySPI_W_MOSI(SendByte&(0x80>>i)); //主发送字节
        MySPI_W_SCK(1);
        if(MySPI_R_MISO()==1)ReceiveByte|=(0x80>>i); //主接收字节
        MySPI_W_SCK(0);
        
    }
    return ReceiveByte;
}

MySPI.h

#ifndef __MYSPI_H__
#define __MYSPI_H__

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

#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

#define W25Q64_DUMMY_BYTE							0xFF

#endif

W25Q64.h

#ifndef __W25Q64_H__
#define __W25Q64_H__

void W25Q64_Init();
void W25Q64_ReadID(uint8_t* HID,uint16_t* SID);
void W25Q64_ReadData(uint32_t Address,uint8_t* DataArray,uint16_t Count);
void W25Q64_SectorErase(uint32_t Address);
void W25Q64_PageProgram(uint32_t Address,uint8_t* DataArray,uint16_t Count);

#endif

W25Q64.c

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

//初始化
void W25Q64_Init()
{
    MySPI_Init();
}
//读ID
void W25Q64_ReadID(uint8_t* HID,uint16_t* SID)
{
    MySPI_Start();
    MySPI_SwapByte(W25Q64_JEDEC_ID);
    *HID=MySPI_SwapByte(W25Q64_DUMMY_BYTE);
    *SID=MySPI_SwapByte(W25Q64_DUMMY_BYTE);
    *SID<<=8;
    *SID|=MySPI_SwapByte(W25Q64_DUMMY_BYTE);
    MySPI_Stop();
}
//写使能
void W25Q64_WriteEnable()
{
    MySPI_Start();
    MySPI_SwapByte(W25Q64_WRITE_ENABLE);
    MySPI_Stop();
}
//等待忙状态
void W25Q64_WaitBusy()
{
    MySPI_Start();
    MySPI_SwapByte(W25Q64_READ_STATUS_REGISTER_1);
    uint32_t count=10000;
    while((MySPI_SwapByte(W25Q64_DUMMY_BYTE)&0x01)==0x01||count)
    {
        count--;
       
    }
    
    MySPI_Stop();
}
//页编程
void W25Q64_PageProgram(uint32_t Address,uint8_t* DataArray,uint16_t Count)
{
    W25Q64_WriteEnable();
    uint16_t i;
    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_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_ReadData(uint32_t Address,uint8_t* DataArray,uint16_t Count)
{
    uint16_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(W25Q64_DUMMY_BYTE);
    }
    
    MySPI_Stop();
}

    


对于W25Q64来说,需要先对不同的操作先写入对应的地址,
在这里插入图片描述
在这里插入图片描述

然后根据手册,写入地址和内容;

main.c

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

uint8_t HID;
uint16_t SID;

uint8_t ArrayWrite[]={0xAA,0xBB,0xCC,0xDD};
uint8_t ArrayRead[4];
int main()
{
	OLED_Init();
	W25Q64_Init();
    
    OLED_ShowString(1, 1, "MID:   DID:");
	OLED_ShowString(2, 1, "W:");
	OLED_ShowString(3, 1, "R:");
    
    W25Q64_ReadID(&HID,&SID);
    OLED_ShowHexNum(1,5,HID,2);
    OLED_ShowHexNum(1,12,SID,4);
    
    W25Q64_SectorErase(0x000100);
    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)
    {
        
    }
}

可以通过改变擦除的地址和页编程的地址,以及存储的内容;来进行验证FLASH的注意事项。

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

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

相关文章

免费图床配置:PicGo + Github + jsDelivr

免费图床配置&#xff1a;PicGo Github jsDelivr 前言PicGo Github配置过程jsdelivr加速踩坑注意事项参考资料 觉得文章有收获&#xff0c;欢迎关注公众号鼓励一下作者呀~ 在学习的过程中&#xff0c;也搜集了一些量化、技术的视频及书籍资源&#xff0c;欢迎大家关注公众号…

记一次ActiveMQ漏洞利用

ActiveMQ介绍&#xff1a; Apache ActiveMQ 是 Apache 软件基金会所研发的一套开源的消息中间件&#xff0c;它支持 Java 消息服务、集群、Spring Framework 等。随着中间件的启动&#xff0c;会打开两个端口&#xff0c;61616 是工作端口&#xff0c;消息在这个端口进行传递&…

Sketchup软件安装包分享(附安装教程)

目录 一、软件简介 二、软件下载 一、软件简介 SketchUp是一款由Trimble公司开发的3D建模软件&#xff0c;广泛应用于建筑、室内设计、城市规划等领域。它以直观的用户界面和强大的功能而闻名&#xff0c;让用户能够轻松地创建和修改三维模型。 1、SketchUp的主要特点 用户…

BIO到NIO、多路复用器, 从理论到实践, 结合实际案例对比各自效率与特点(下)

文章目录 多路复用器简介多路复用器的两个阶段Java中的多路复用器封装测试代码压测结果总结 本篇文章是BIO到NIO、多路复用器, 从理论到实践, 结合实际案例对比各自效率与特点(上)的下一篇, 如果没有看的小伙伴, 可以先看下, 不然可能会不连贯. 多路复用器简介 多路复用器是对…

STM32 CAN 波特率计算分析

这里写目录标题 前言时钟分析时钟元到BIT 前言 CubeMX中配置CAN波特率的这个界面刚用的时候觉得非常难用&#xff0c;怎么都配置不到想要的波特率。接下来为大家做一下简单的分析。 时钟分析 STM32F4的CAN时钟来自APB1 在如下界面配置&#xff0c;最好配置为1个整一点的数。…

c语言练习题32:模拟实现库函数strlen并求字符串长度

模拟实现库函数strlen&#xff0c;读取字符个数。 思路&#xff1a;利用指针遍历字符串&#xff0c;从而获得字符串中的字符个数。 代码&#xff1a; //模拟实现库函数strlen #include<stdio.h> int Strlen(const char* str) {int count 0;//利⽤指针遍历字符串while…

详解CAS

CAS 全称compare and swap,字面意思"比较和交换" 能够比较和交换某个寄存器中的值和内存中的值,看是否相等,如果相等,则把另外一个寄存器中的值和内存中的值进行交换 伪代码 这个就给我们编写线程安全代码,打开了新世界的大门,基于CAS又能衍生出一套"无锁编程…

权限提升-数据库权限到web权限+后台权限到web权限

权限提升基础信息 1、具体有哪些权限需要我们了解掌握的&#xff1f; 后台权限&#xff0c;网站权限&#xff0c;数据库权限&#xff0c;接口权限&#xff0c;系统权限&#xff0c;域控权限等 2、以上常见权限获取方法简要归类说明&#xff1f; 后台权限&#xff1a;SQL注入,数…

本地镜像管理

查看 用户可以通过docker images命令查看本地所有镜像&#xff0c;如下&#xff1a; 这里一共有五个参数&#xff0c;含义分别如下&#xff1a; REPOSITORY 仓库名称&#xff0c;仓库一般用来存放同一类型的镜像。仓库的名称由其创建者指定。如果没有指定则为<none>。…

学生宿舍人走自动断电设备的功能要求

学生宿舍人走自动断电系统石家庄光大远通电气有限公司断电系统由人员探测模块&#xff08;安装在房间内部&#xff09;宿舍用电控制模块&#xff08;安装在房间供电线路&#xff09;&#xff0c;智能数据网关及后台服务器组成&#xff0c;每个房间安装一台探测器&#xff0c;实…

高并发编程-3. Amdahl(阿姆达尔)定律与Gustafson定律

此文章为笔记&#xff0c;为阅读其他文章的感受、补充、记录、练习、汇总&#xff0c;非原创&#xff0c;感谢每个知识分享者。 前言 有关为什么要使用并行程序的问题前面已经进行了简单的探讨。总的来说&#xff0c;最重要的应该是处于两个目的。 第一&#xff0c;为了获得更…

SpringBoot+mybatis+pgsql多个数据源配置

一、配置文件 jdk环境&#xff1a;1.8 配置了双数据源springbootdruidpgsql&#xff0c;application.properties配置修改如下&#xff1a; #当前入库主数据库 spring.primary.datasource.typecom.alibaba.druid.pool.DruidDataSource spring.primary.datasource.driver-class…

太阳能光伏产业链特征

从太阳能光伏产业链上来看&#xff0c;上游产业是原材料的生产环节&#xff0c;主要是对硅矿石和高纯度硅料的开采、提炼和生产&#xff0c;如冶金硅提纯、多晶硅提纯、单晶/多晶硅片加工与切割等环节。此外还包括太阳能电背板、电池用玻璃等系统配件的制造。 中游产业是技术核…

Day979.OAuth2.0可能存在的安全问题 -OAuth 2.0

OAuth2.0可能存在的安全问题 Hi&#xff0c;我是阿昌&#xff0c;今天学习记录的是关于OAuth2.0可能存在的安全问题的内容。 “OAuth 2.0 不是一种安全协议吗&#xff0c;不是保护 Web API 的吗&#xff1f;为啥 OAuth 2.0 自己还有安全的问题了呢&#xff1f;” 首先&#x…

积跬步至千里 || 数学基础、算法与编程

数学基础、算法与编程 1. BAP 技能 BAP 技能是指基础(Basic)、算法(Algorithm)和编程(Programm)三种基本技能的深度融合。理工科以数学、算法与编程为根基&#xff0c;这三个相辅相成又各有区别。 &#xff08;1&#xff09;数学以线性代数为主要研究工具和部分微积分技术为手…

Spring boot 集成单元测试

1.引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency> 2. 3.编写测试类 package com.enterprise;import com.enterpr…

网络安全02-C段扫描、开放端口

查询网站IP https://seo.chinaz.com/hetianlab.com 扫描指定IP&#xff1a;例&#xff1a;nmap -A -T4 ww.hetianlab.com -oX out.html 扫描指定段&#xff1a;例&#xff1a;nmap -O -Pn -A 192.168.113.1-200 扫描整个C段&#xff1a;例&#xff1a;nmap -O -Pn -A 192.168.…

ChatGPT在医疗系统的应用探索动态

注意&#xff1a;本信息仅供参考&#xff0c;发布该内容旨在传递更多信息的目的&#xff0c;并不意味着赞同其观点或证实其说法。 生成式人工智能&#xff0c;如OpenAI开发的ChatGPT&#xff0c;被认为是可以颠覆医疗行业的工具。尽管该技术刚刚起步&#xff0c;但已有许多医…

python 面试题--2(15题)

目录 1.解释Python中的 GIL&#xff08;全局解释器锁&#xff09;是什么&#xff0c;它对多线程编程有什么影响&#xff1f; 2.Python中的装饰器是什么&#xff1f;如何使用装饰器&#xff1f; 3.解释Python中的迭代器和生成器的区别。 4.什么是Python中的列表解析&#xf…

redis报错WRONGTYPE Operation against a key holding the wrong kind of value

在redis中我们一般存储string、list、hash类型的值&#xff0c;对应的方法分别为 db.StringGet(“key”)、db.ListRange、db.HashGetAll 如果取list类型值时使用了string的方法就会报WRONGTYPE Operation against a key holding the wrong kind of value错误。 redis-cli命令窗…