STM32 软件SPI读写W25Q64

接线图

 功能函数

//写SS函数
void My_W_SS(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue);
}//写SCK函数
void My_W_SCK(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_5, (BitAction)BitValue);
}//写MOSI函数
void My_W_MOSI(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_7, (BitAction)BitValue);
}//读MISO函数
uint8_t My_R_MISO(void)
{return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6);
}

代码配置

1.开启时钟

//开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//开启GPIO A族的时钟

2.配置GPIO

        引脚配置,将输出引脚配置为推挽输出,输入引脚配置为上拉输入,这里接线图,DO对应从机输出,对应主机PA6就是输入,其他三个引脚配置为推挽输出。

GPIO_InitTypeDef GPIO_InitStructure;//定义GPIO结构体变量	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);

3.配置电平

My_W_SS(1);//置高电平,默认不选择从机
My_W_SCK(0);//默认选择低电平,使用SPI模式0

配置SPI时序基本单元

起始条件与终止条件

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

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

//起始条件
void My_SPI_Start(void)
{//置低电平My_W_SS(0);
}//终止条件
void My_SPI_Stop(void)
{//置高电平My_W_SS(1);
}

交换一个字节

方法一

使用掩码,以此提取数据的每一位

//交换一个字节
uint8_t My_SPI_SwapByte(uint8_t ByteSend)
{uint8_t i, ByteReceive = 0x00;for(i = 0; i < 8; i++){//写MOSIMy_W_MOSI(ByteSend & (0x80 >> i));//SCK上升沿 置高电平My_W_SCK(1);//读MISOif(My_R_MISO() == 1){ByteReceive |= (0x80 >> i);}//SCK产生下降沿 置低电平My_W_SCK(0);}return ByteReceive;}

方法二

用移位数据本身来进行操作,好处是效率高,但是ByteSend数据在移位的过程中改变了,for循环执行完,原始传入的参数就没有了

//交换一个字节
uint8_t My_SPI_SwapByte2(uint8_t ByteSend)
{uint8_t i;for(i = 0; i < 8; i++){//写MOSIMy_W_MOSI(ByteSend & 0x80 );ByteSend <<= 1;//SCK上升沿 置高电平My_W_SCK(1);//读MISOif(My_R_MISO() == 1){ByteSend |= 0x01;}//SCK产生下降沿 置低电平My_W_SCK(0);}return ByteSend;}

SPI配置完成,接下来建立W25Q64的驱动层

W25Q64配置

#include "stm32f10x.h"         
#include "MySPI.h"void W25Q64_Init(void)
{MySPI_Init();
}

接下来实现业务代码,拼接完整时序

 宏定义指令集

根据手册指令表将指令宏定义出来

//宏定义指令集
#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

实现获取ID号的时序

*MID输出8位的厂商ID

*DID输出16位的设备ID

//获取ID号的时序
void W25Q64_ReadID(uint8_t* MID, uint16_t* DID)
{//开始传输My_SPI_Start();//交换发送一个字节 9FMy_SPI_SwapByte(W25Q64_JEDEC_ID);//给从机一个东西,目的是将对方的数据置换过来*MID = My_SPI_SwapByte(W25Q64_DUMMY_BYTE);//返回设备ID的高8位*DID = My_SPI_SwapByte(W25Q64_DUMMY_BYTE);*DID <<= 8;	//把第一次读取的数据运到DID的高8位去//返回设备ID的低8位*DID |= My_SPI_SwapByte(W25Q64_DUMMY_BYTE);//结束时序My_SPI_Stop();
}

写使能

//写使能
void W25Q64_WriteEnable(void)
{My_SPI_Start();My_SPI_SwapByte(W25Q64_WRITE_ENABLE);My_SPI_Stop();}

读状态寄存器1

判断芯片忙不忙

//读状态寄存器1
//判断芯片是否处于忙状态
void W25Q64_WaitBusy(void)
{uint32_t Timeout;My_SPI_Start();My_SPI_SwapByte(W25Q64_READ_STATUS_REGISTER_1);//等待BUSYwhile ((My_SPI_SwapByte(W25Q64_DUMMY_BYTE) & 0x01) == 0x01){Timeout--;if(Timeout == 0){break;}}My_SPI_Stop();
}

页编程函数

根据Flash注意事项

        写入操作前必须先写使能,写入操作后等待忙状态,这里的页编程与扇形擦除进行了写入操作,需要注意事项。

//页编程
void W25Q64_PageProgram(uint32_t Address, uint8_t* DataArray, uint16_t Count)
{uint16_t i;W25Q64_WriteEnable();//写使能My_SPI_Start();My_SPI_SwapByte(W25Q64_PAGE_PROGRAM);//指定24位地址My_SPI_SwapByte(Address >> 16);My_SPI_SwapByte(Address >> 8);My_SPI_SwapByte(Address);for(i = 0; i < Count; i++){My_SPI_SwapByte(DataArray[i]);}My_SPI_Stop();W25Q64_WaitBusy();//等待BUSY(事后等待)
}

扇区擦除函数

//扇区擦除函数
void W25Q64_SectorErase(uint32_t Address)
{W25Q64_WriteEnable();//写使能My_SPI_Start();My_SPI_SwapByte(W25Q64_SECTOR_ERASE_4KB);//指定24位地址My_SPI_SwapByte(Address >> 16);My_SPI_SwapByte(Address >> 8);My_SPI_SwapByte(Address);My_SPI_Stop();W25Q64_WaitBusy();//等待BUSY(事后等待)}

读取数据

//读取数据
void W25Q64_ReadData(uint32_t Address, uint8_t* DataArray, uint32_t Count)
{uint32_t i;My_SPI_Start();My_SPI_SwapByte(W25Q64_READ_DATA);//指定24位地址My_SPI_SwapByte(Address >> 16);My_SPI_SwapByte(Address >> 8);My_SPI_SwapByte(Address);for(i = 0; i < Count; i++){DataArray[i] = My_SPI_SwapByte(W25Q64_DUMMY_BYTE);}My_SPI_Stop();}

主函数

#include "W25Q64.h"
int main(void)
{uint8_t MID;uint16_t DID;uint8_t ArraryWrite[] = {0x01, 0x02, 0x03, 0x04};uint8_t ArraryRead[4];OLED_Init();W25Q64_Init();OLED_ShowString(1, 1, "MID:");OLED_ShowString(1, 8, "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, ArraryWrite, 4);W25Q64_ReadData(0x000000, ArraryRead, 4);OLED_ShowHexNum(2, 3, ArraryWrite[0], 2);OLED_ShowHexNum(2, 6, ArraryWrite[1], 2);OLED_ShowHexNum(2, 9, ArraryWrite[2], 2);OLED_ShowHexNum(2, 12,ArraryWrite[3], 2);OLED_ShowHexNum(3, 3, ArraryRead[0], 2);OLED_ShowHexNum(3, 6, ArraryRead[1], 2);OLED_ShowHexNum(3, 9, ArraryRead[2], 2);OLED_ShowHexNum(3, 12,ArraryRead[3], 2);while(1){}}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

pytest-xdist 进行多进程并发测试

在自动化测试中&#xff0c;运行时间过长往往是令人头疼的问题。你是否遇到过执行 Pytest 测试用例时&#xff0c;整个测试流程缓慢得让人抓狂&#xff1f;别担心&#xff0c;pytest-xdist 正是解决这一问题的利器&#xff01;它支持多进程并发执行&#xff0c;能够显著加快测试…

CLion2024.3.2版中引入vector头文件报错

报错如下&#xff1a; 在MacBook端的CLion中引入#include <vector>报 vector file not found&#xff08;引入map、set等也看参考此方案&#xff09;&#xff0c;首先可以在Settings -> Build,Execution,Deployment -> Toolchains中修改C compiler和C compiler的路…

【RocketMQ 存储】- 同步刷盘和异步刷盘

文章目录 1. 前言2. 概述3. submitFlushRequest 提交刷盘请求4. FlushDiskWatcher 同步刷盘监视器5. 同步刷盘但是不需要等待刷盘结果6. 小结 本文章基于 RocketMQ 4.9.3 1. 前言 RocketMQ 存储部分系列文章&#xff1a; 【RocketMQ 存储】- RocketMQ存储类 MappedFile【Rock…

了解传输层TCP协议

目录 一、TCP协议段格式 二、TCP原理 1.确认应答 2.超时重传 3.连接管理 建立连接 断开连接 4.滑动窗口 5.流量控制 6.拥塞控制 7.延时应答 8.捎带应答 9.面向字节流 10.TCP异常情况 TCP&#xff0c;即Transmission Control Protocol&#xff0c;传输控制协议。人如…

第 26 场 蓝桥入门赛

3.电子舞龙【算法赛】 - 蓝桥云课 问题描述 话说这年头&#xff0c;连舞龙都得电子化&#xff01;这不&#xff0c;蓝桥村的老程序员王大爷突发奇想&#xff0c;用LED灯带和一堆传感器鼓捣出了一条“电子舞龙”&#xff0c;它能根据程序指令在村里的广场上“翩翩起舞”。 广…

老游戏回顾:TL2

TL2是一部ARPG游戏&#xff0c;是TL的续作游戏&#xff0c;由位于美国西雅图的Runic Games开发&#xff0c;游戏于2012年9月20日上市&#xff0c;简体中文版于2013年4月10日在国内上市。 2有非常独特的艺术风格&#xff0c;这些在1中就已经形成&#xff0c;经过升级将使这款游…

前端实现 GIF 图片循环播放

前言 使用 img 加载 GIF 图片&#xff0c;内容只会播放一次&#xff0c;之后就会自动暂停&#xff1b; 通过定时器在一段时间后重新加载图片的方式&#xff0c;会导致浏览器内存不断增大&#xff0c;并且可能会有闪烁、卡顿的问题&#xff1b; ImageDecoder WebCodecs API 的…

1-2 面向对象编程方法

1.0 面向对象编程思维 在面向对象风格中&#xff0c;结构体被看做数据&#xff08;data&#xff09;&#xff0c;而操作数据的函数称作方法&#xff08;method&#xff09;。目前函数 和数据是分离的&#xff0c;函数并不直接操作数据&#xff0c;我们需要拿到函数返回的结果&a…

LVGL4种输入设备详解(触摸、键盘、实体按键、编码器)

lvgl有触摸、键盘、实体按键、编码器四种输入设备 先来分析一下这四种输入设备有什么区别 &#xff08;1&#xff09;LV_INDEV_TYPE_POINTER 主要用于触摸屏 用到哪个输入设备保留哪个其他的也是&#xff0c;保留触摸屏输入的任务注册&#xff0c;其它几种种输入任务的注册&…

让文物“活”起来,以3D数字化技术传承文物历史文化!

文物&#xff0c;作为不可再生的宝贵资源&#xff0c;其任何毁损都是无法逆转的损失。然而&#xff0c;当前文物保护与修复领域仍大量依赖传统技术&#xff0c;同时&#xff0c;文物管理机构和专业团队的力量相对薄弱&#xff0c;亟需引入数字化管理手段以应对挑战。 积木易搭…

如何通过 ESPN API 获取 NBA 球队的赛程表

对于 NBA 爱好者和开发者来说&#xff0c;通过 API 获取球队赛程表是一项非常实用的功能&#xff0c;尤其是如果你正在构建一个应用或网站&#xff0c;需要自动化获取比赛安排的情况下。今天&#xff0c;我将为大家介绍如何通过 ESPN 提供的 API 获取 NBA 球队的赛程表。 1. ES…

LM Studio 部署本地大语言模型

一、下载安装 1.搜索&#xff1a;lm studio LM Studio - Discover, download, and run local LLMs 2.下载 3.安装 4.更改成中文 二、下载模型(软件内下载) 1.选择使用代理&#xff0c;否则无法下载 2.更改模型下载目录 默认下载位置 C:\Users\用户名\.lmstudio\models 3.搜…

【开源免费】基于SpringBoot+Vue.JS智能学习平台系统(JAVA毕业设计)

本文项目编号 T 181 &#xff0c;文末自助获取源码 \color{red}{T181&#xff0c;文末自助获取源码} T181&#xff0c;文末自助获取源码 目录 一、系统介绍二、数据库设计三、配套教程3.1 启动教程3.2 讲解视频3.3 二次开发教程 四、功能截图五、文案资料5.1 选题背景5.2 国内…

【R语言】环境空间

一、环境空间的特点 环境空间是一种特殊类型的变量&#xff0c;它可以像其它变量一样被分配和操作&#xff0c;还可以以参数的形式传递给函数。 R语言中环境空间具有如下3个特点&#xff1a; 1、对象名称唯一性 此特点指的是在不同的环境空间中可以有同名的变量出现&#x…

黑马 Linux零基础快速入门到精通 笔记

初识Linux Linux简介 提及操作系统&#xff0c;我们可能最先想到的是windows和mac&#xff0c;这两者都属于个人桌面操作系统领域&#xff0c;而Linux则属于服务器操作系统领域。无论是后端软件、大数据系统、网页服务等等都需要运行在Linux操作系统上。 Linux是一个开源的操作…

Golang:精通sync/atomic 包的Atomic 操作

在本指南中&#xff0c;我们将探索sync/atomic包的细节&#xff0c;展示如何编写更安全、更高效的并发代码。无论你是经验丰富的Gopher还是刚刚起步&#xff0c;你都会发现有价值的见解来提升Go编程技能。让我们一起开启原子运算的力量吧&#xff01; 理解Go中的原子操作 在快…

网络安全ITP是什么 网络安全产品ips

DS/IPS都是专门针对计算机病毒和黑客入侵而设计的网络安全设备 1、含义不同 IDS &#xff1a;入侵检测系统&#xff08;发现非法入侵只能报警不能自己过滤&#xff09; 做一个形象的比喻&#xff1a;假如防火墙是一幢大楼的门锁&#xff0c;那么IDS就是这幢大楼里的监视系统…

高速网络的未来:零拷贝Zero-Copy架构

在当今高速发展的信息技术领域&#xff0c;追求极致的性能和效率是永恒的主题。而当我们深入探索计算机系统的内部奥秘时&#xff0c;一个令人瞩目的概念 —— 零拷贝&#xff08;Zero-Copy&#xff09;架构&#xff0c;逐渐走入我们的视野。想象一下&#xff0c;在数据如洪流般…

备忘录模式

引言 当我们和朋友下棋的时候&#xff0c;我们很多情况下会发现下了一步臭棋&#xff0c;这时候就会和朋友开玩笑要悔棋&#xff0c;即撤回刚刚下的一步棋。在程序中&#xff0c;很多时候也会出错&#xff0c;我们也希望程序可以恢复出错前的状态&#xff0c;这就需要备忘录模式…

Element UI 表单源码原理

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…