STM32SPI通信外设并读写W25Q64

文章目录

  • 前言
  • 介绍SPI外设
  • SPI框图
    • 简化框图
  • 时序
    • 主模式全双工连续传输
    • 非连续传输
  • 软硬件波形对比
  • 硬件SPI读写W25Q64
    • 接线图
    • 代码规划
    • 代码实现


前言

本文介绍STM32中自带的SPI外设,在大容量产品和互联型产品上,SPI接口可以配置为支持SPI协议或者支持I2S音频协议。SPI接口默认工作在SPI方式,可以通过软件把功能从SPI模式切换到I2S模式。
在小容量和中容量产品上,不支持I2S音频协议。
串行外设接口(SPI)允许芯片与外部设备以半/全双工、同步、串行方式通信。此接口可以被配置成主模式,并为外部从设备提供通信时钟(SCK)。接口还能以多主配置方式工作。它可用于多种用途,包括使用一条双向数据线的双线单工同步传输,还可使用CRC校验的可靠通信。
软件SPI读写W25Q64、SPI详解可见此专栏。


介绍SPI外设

常用8位数据帧,高位先行。
外设总线APB1、APB2的频率不一样(APB1 36MHz、APB2 72MHz),所以SPI1与SPI2的频率也不一样。SPI1挂载于APB2上,SPI2挂载于APB1上。
注意:选择时钟频率时分频系数是必选的,即SPI1的最大频率等于72MHz/2为36MHz。
I2S为数字音频传输协议,是用于把音乐信号发送到DAC的协议。

在这里插入图片描述

SPI框图

此框图移位寄存器是低位先行,移出到MOSI,从MISO移入 。
当此SPI做从机时,就走MOSI、MISO交叉的路线。
NSS用于多主机模型,由多个设备连接NSS,当NSS配置为输出时,输出低电平,这些设备都变为从机,当NSS配置为输入时,主机拉低NSS,此SPI和其他连接NSS的设备就不能变为主机 由SPI_CR2的SSOE控制NSS输出还是输入。
波特发生器用于产生时钟。

主机从地址和数据总线写入数据到发送缓冲区,然后经过移位寄存器完成数据交换,当移位寄存器还没有数据时,发送缓存区传入数据后会置状态寄存器的TXE为1,表示发送寄存器(缓冲区)为空,此时可以提前准备下一个数据的写入,同时移位寄存器也会自动交换一个数据,然后数据进入接收缓冲区,此时状态寄存器的RXNE=1,表示接收寄存器(缓冲区)非空,在RXNE=1时,要尽快把接收缓冲区的数据读出来,反复执行即可完成连续的数据流。

在这里插入图片描述
SPI_CR1寄存器描述如下
在这里插入图片描述
在这里插入图片描述

简化框图

最主要的结构部分,方便更清晰的理解实现过程。
在这里插入图片描述

时序

主模式全双工连续传输

传输效率高,提前写入数据。
在这里插入图片描述

非连续传输

效率低,没有提前写入,比较好操作,但是频率很快时,由于没有提前写入数据导致的等待时间会较大的影响传输速率。
在这里插入图片描述
例如256分频时,看不出字节间的间隙。
在这里插入图片描述
128分频时,有较小间隙。
在这里插入图片描述
64分频时,间隙逐渐变大。
在这里插入图片描述

软硬件波形对比

在这里插入图片描述
在这里插入图片描述

硬件SPI读写W25Q64

接线图

在这里插入图片描述

代码规划

使用ThisSPI.c来用于书写SPI基本时序单元的内容,然后使用ThisW25Q64.c来基于ThisSPI.c来调用这些SPI基本时序单元来完成读写的功能,以下头文件声明省略。

代码实现

ThisSPI.c

#include "stm32f10x.h"                  // Device header// 封装写入CS位的函数
void ThisSPI_W_CS(uint8_t BitValue){GPIO_WriteBit(GPIOA,GPIO_Pin_4,(BitAction)BitValue);
}void ThisSPI_Init(void){// 初始化引脚配置RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);// CSGPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;		// 推挽输出GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);// CLK、MOSIGPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;		// 复用推挽输出GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);// MISOGPIO_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);// 初始化SPI外设SPI_InitTypeDef SPI_InitStructure;SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;	// 分频系数SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;	// 0SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;	// 0SPI_InitStructure.SPI_CRCPolynomial = 7;	// CRC校验,暂时不用SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;	// 八位数据帧SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;	// 双线全双工SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;	// 高位先行SPI_InitStructure.SPI_Mode = SPI_Mode_Master;	// 主机模式SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;	// 暂时不使用SPI_Init(SPI1,&SPI_InitStructure);SPI_Cmd(SPI1,ENABLE);ThisSPI_W_CS(1);
}
/*** @brief  SPI起始时序单元* @param 	无* @retval 无*/
void ThisSPI_Start(void){ThisSPI_W_CS(0);
}
/*** @brief  SPI结束时序单元* @param 	无* @retval 无*/
void ThisSPI_End(void){ThisSPI_W_CS(1);
}
/*** @brief  SPI交换字节* @param 	Byte 字节内容* @retval 接收的数据*/
uint8_t ThisSPI_SwapByte(uint8_t Byte){// 等待TXE=1while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE)==RESET);// 写入发送寄存器,同时会清除TXE标志SPI_I2S_SendData(SPI1,Byte);// 等待RXNE=1while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE)==RESET);// 读入接收寄存器,同时会清除RXNE标志return SPI_I2S_ReceiveData(SPI1);
}

ThisW25Q64.C

#include "stm32f10x.h"                  // Device header
#include "ThisSPI.h"#define JEDEC_ID 				0X9F
#define Write_Enable 			0X06
#define Read_Status_Register_1 	0X05
#define Page_Program 			0X02
#define Sector_Erase 			0X20
#define Read_Data 				0X03// 初始化
void ThisW25Q64_Init(void){ThisSPI_Init();}
// 参考指令集来完成SPI时序单元的拼接
/*** @brief  	读取产品ID* @param 	MID 产品ID,DID 设备ID* @retval 	无*/
void W25Q64_ReadID(uint8_t *MID,uint16_t *DID){ThisSPI_Start();ThisSPI_SwapByte(JEDEC_ID);*MID = ThisSPI_SwapByte(0XFF);*DID = ThisSPI_SwapByte(0XFF);*DID <<= 8;*DID |= ThisSPI_SwapByte(0XFF);ThisSPI_End();
}
/*** @brief  	写使能* @param 	无* @retval 	无*/
void W25Q64_WriteEnable(void){ThisSPI_Start();ThisSPI_SwapByte(Write_Enable);ThisSPI_End();
}
/*** @brief  	等待写入完成(通过状态寄存器1的BUSY位)* @param 	无* @retval 	无*/
void W25Q64_WaitBUSY(void){uint32_t Timeout = 10000;ThisSPI_Start();ThisSPI_SwapByte(Read_Status_Register_1);while((ThisSPI_SwapByte(0XFF) & 0x01) == 0x01){Timeout--;if(Timeout == 0) break;}ThisSPI_End();
}
/*** @brief  	页写入* @param 	Address,页地址* @param 	DataArr,写入的数据* @param 	Count,一次写入的大小* @retval 	无*/
void W25Q64_PageProgram(uint32_t Address,uint8_t *DataArr,uint16_t Count){uint8_t i;W25Q64_WriteEnable();ThisSPI_Start();ThisSPI_SwapByte(Page_Program);ThisSPI_SwapByte(Address>>16);ThisSPI_SwapByte(Address>>8);ThisSPI_SwapByte(Address);for(i=0;i<Count;i++){ThisSPI_SwapByte(DataArr[i]);}ThisSPI_End();W25Q64_WaitBUSY();
}
/*** @brief  	扇区擦除* @param 	Address,页地址* @retval 	无*/
void W25Q64_SectorErase(uint32_t Address){W25Q64_WriteEnable();ThisSPI_Start();ThisSPI_SwapByte(Sector_Erase);ThisSPI_SwapByte(Address>>16);ThisSPI_SwapByte(Address>>8);ThisSPI_SwapByte(Address);ThisSPI_End();W25Q64_WaitBUSY();
}
/*** @brief  	读取页内数据* @param 	Address,页地址* @param 	DataArr,用于存取读取的数据* @param 	Count,读取数据的大小* @retval 	无*/
void W25Q64_ReadData(uint32_t Address,uint8_t *DataArr,uint32_t Count){uint32_t i;ThisSPI_Start();ThisSPI_SwapByte(Read_Data);ThisSPI_SwapByte(Address>>16);ThisSPI_SwapByte(Address>>8);ThisSPI_SwapByte(Address);for(i=0;i<Count;i++){DataArr[i] = ThisSPI_SwapByte(0XFF);}ThisSPI_End();
}

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "ThisW25Q64.h"uint8_t MID;
uint16_t DID;
uint8_t ArrWrite[] = {0x11,0x92,0x73,0x24};
uint8_t ArrRead[4];int main(void)
{OLED_Init();OLED_ShowString(1,1,"MID:");OLED_ShowString(1,8,"DID:");ThisW25Q64_Init();W25Q64_ReadID(&MID,&DID);OLED_ShowHexNum(1,5,MID,2);OLED_ShowHexNum(1,12,DID,4);OLED_ShowString(2,1,"W:");OLED_ShowString(3,1,"R:");W25Q64_SectorErase(0X000000);W25Q64_PageProgram(0X000000,ArrWrite,4);W25Q64_ReadData(0X000000,ArrRead,4);OLED_ShowHexNum(2,3,ArrWrite[0],2);OLED_ShowHexNum(2,5,ArrWrite[1],2);OLED_ShowHexNum(2,7,ArrWrite[2],2);OLED_ShowHexNum(2,9,ArrWrite[3],2);OLED_ShowHexNum(3,3,ArrRead[0],2);OLED_ShowHexNum(3,5,ArrRead[1],2);OLED_ShowHexNum(3,7,ArrRead[2],2);OLED_ShowHexNum(3,9,ArrRead[3],2);while (1){}
}

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

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

相关文章

前端实现自动获取农历日期:探索JavaScript的跨文化编程

&#x1f31f; 前言 欢迎来到我的技术小宇宙&#xff01;&#x1f30c; 这里不仅是我记录技术点滴的后花园&#xff0c;也是我分享学习心得和项目经验的乐园。&#x1f4da; 无论你是技术小白还是资深大牛&#xff0c;这里总有一些内容能触动你的好奇心。&#x1f50d; &#x…

Verilog语法——按位取反“~“和位宽扩展的优先级

前言 先说结论&#xff0c;如下图所示&#xff0c;在Verilog中“~ ”按位取反的优先级是最高的&#xff0c;但是在等式计算时&#xff0c;有时候会遇到位宽扩展&#xff0c;此时需要注意的是位宽扩展的优先级高于“~”。 验证 仿真代码&#xff0c;下面代码验证的是“~”按位取…

【canvas】canvas基础使用(七):绘制图像

简言 学习canvas如何绘制图片或视频。 绘制图像 给定一个图像&#xff0c;一般使用drawImage()方法绘制。 drawImage 绘制图像 Canvas 2D API 中的 CanvasRenderingContext2D.drawImage() 方法提供了多种在画布&#xff08;Canvas&#xff09;上绘制图像的方式。 语法&…

四川易点慧电子商务抖音小店安全:护航您的在线交易之旅

在数字化浪潮席卷全球的今天&#xff0c;电子商务已经成为人们日常生活的重要组成部分。四川易点慧电子商务抖音小店作为新兴的电商平台&#xff0c;以其便捷、高效的特点吸引了众多消费者的目光。然而&#xff0c;随着网络交易的日益频繁&#xff0c;安全问题也日益凸显。本文…

揭秘!这些部门对六西格玛技术情有独钟

当下&#xff0c;企业为了提升产品质量、降低成本、提高效率&#xff0c;纷纷寻求先进的管理方法和技术手段。其中&#xff0c;六西格玛技术因其卓越的绩效改善能力&#xff0c;受到了众多企业的青睐。那么&#xff0c;哪些部门对六西格玛技术情有独钟呢&#xff1f;天行健六西…

如何使用Docker部署WPS Office服务并实现无公网IP远程处理文档表格

文章目录 1. 拉取WPS Office镜像2. 运行WPS Office镜像容器3. 本地访问WPS Office4. 群晖安装Cpolar5. 配置WPS Office远程地址6. 远程访问WPS Office小结 7. 固定公网地址 wps-office是一个在Linux服务器上部署WPS Office的镜像。它基于WPS Office的Linux版本&#xff0c;通过…

每日一题(leetcode765):情侣牵手--并查集和哈希表

代码技巧&#xff0c;在读取列表时都除2&#xff0c;符合情理并且节省了空间。 class Solution { private:int get(vector<int>& f,int x){if(f[x]x){return x;}int newxget(f,f[x]);return newx;}void add(vector<int>& f,int a,int b){int zuaget(f,a);…

EPSON的RX8900CE适合用于安防摄像头产品

安防摄像头产品可以实现视频监控&#xff0c;运动检测&#xff0c;人脸识别等功能&#xff0c;并且可以支持远程访问&#xff0c;成了用户的“千里眼”。之前安防摄像头的价格比较高&#xff0c;一般比较重要的场合才会使用&#xff0c;目前随着安防摄像头价格逐渐降低&#xf…

一进四出学生宿舍智能电表

一进四出学生宿舍电表功能特点** 一、多路电能计量** 该电表采用先进的电路设计&#xff0c;能够同时实现四路电能的独立计量。每路输出都可以单独控制和管理&#xff0c;方便对学生宿舍中各个用电设备的电能消耗进行精确统计。 二、实时电量显示** 电表配备有高清LED显示屏…

用四种方法实现轮播图

1、css3动画实现的轮播图 实现原理如下&#xff1a; 1、设置大的div a)设置绝对定位&#xff0c;定位位置&#xff1b; b)设置图片展示出来的高度和宽度&#xff08;height和width&#xff09;&#xff1b; c)设置overflow:hidden;设置超出部分隐藏&#xff1b;使得图片只能在…

代码对比同步辅助工具,提高开发效率

一开始只是开发抖音的程序&#xff0c;后面复制代码去改成快手、小红书、拼多多等平台的程序。 然后一有些修改调整&#xff0c;可能需要同步到其他平台的代码中&#xff1b; 每次需要打开一个或者多个开发工具IDE&#xff0c;每个IDE打开一个项目&#xff0c;再去对照路径翻…

【Java探索之旅】数组概念与初始化指南:动静结合

&#x1f3a5; 屿小夏 &#xff1a; 个人主页 &#x1f525;个人专栏 &#xff1a; Java编程秘籍 &#x1f304; 莫道桑榆晚&#xff0c;为霞尚满天&#xff01; 文章目录 &#x1f4d1;前言一、初识数组1.1 为什么要有数组&#xff1f;1.2 数组的的概念 二、数组的创建及初始化…

python学习之:数据类型

大纲&#xff1a; 一、列表list的定义语法 1、 """" 演示数据类型&#xff1a;list列表 语法&#xff1a;变量 [元素1&#xff0c;元素2&#xff0c;元素3&#xff0c;......] """ # 定义一个列表list name_list [itheima, itcast, pyt…

8款AI绘画工具推荐,让你绘画更加生动有趣

大家好&#xff0c;我是你们的AI绘画导购员小助手&#xff01;今天我给大家带来了8款超级厉害的AI绘画工具推荐&#xff0c;它们不仅能让你的绘画更加生动有趣&#xff0c;还能让你的创作达到一个新的高度&#xff01; "爱制作AI"---这是一款非常好用的 AI 写作工具&…

TypeScript-官方基础模板创建的小程序,如何创建js文件

如何创建JS文件&#xff0c;不需要寻找“js”文件类型&#xff0c;只需要创建一个新的“文件”即可。 第一步:先删除 ts文件;如 index.ts 第二步:右键点击项目&#xff0c;选择“新建”&#xff0c;然后选择“文件”。 第三步:在弹出的界面中&#xff0c;在“文件名”中输入“…

ssm051网上医院预约挂号系统+jsp

网上医院预约挂号系统设计与实现 摘 要 如今的信息时代&#xff0c;对信息的共享性&#xff0c;信息的流通性有着较高要求&#xff0c;因此传统管理方式就不适合。为了让医院预约挂号信息的管理模式进行升级&#xff0c;也为了更好的维护医院预约挂号信息&#xff0c;网上医院…

pycharm配置anaconda环境时找不到python.exe解决办法

在一台新电脑上配置anaconda环境时&#xff0c;发现pycharm在设置解释器时&#xff0c;在conda环境中找不到anaconda已经创建好的python解释器可执行文件python.exe&#xff0c;其显示如下&#xff1a; 上图只显示创建的虚拟环境中的那些文件夹&#xff0c;但是没有显示这个虚拟…

【Java】SpringBoot快速整合mongoDB

目录 1.什么是mongoDB&#xff1f; 2.Docker安装mongoDB 3.SpringBoot整合mongoDB步骤 4.验证 1.什么是mongoDB&#xff1f; MongoDB是一种非关系型数据库&#xff0c;被广泛用于大型数据存储和分布式系统的构建。MongoDB支持的数据模型比传统的关系型数据库更加灵活&#x…

【Node.js】Express学习笔记(黑马)

目录 初识 ExpressExpress 简介Express 的基本使用托管静态资源nodemon Express 路由路由的概念路由的使用 Express 中间件中间件的概念Express 中间件的初体验中间件的分类 初识 Express Express 简介 什么是 Express&#xff1f; 官方给出的概念&#xff1a;Express 是基于…

这个Python神器,能让你摸半天鱼!

今天给大家分享一个炒鸡炒鸡简单又好用的神器——pampy。 我敢以我的荣誉保证&#xff0c;用了它之后&#xff0c;你写代码的效率可以蹭蹭蹭地提升&#xff01; Pampy 是哪路神仙 首先普及一下模式匹配。 模式匹配即给定某种模式&#xff0c;用这种模式去检查序列或字符串是…