STM32 SPI

目录

软件SPI读写W25Q64

硬件SPI读写W25Q64


CPHA为时钟相位,决定是第几个边沿采样,并不是规定上升沿采样还是下降沿采样(还要看CPOL极性选择)。

W25Q64框图

软件SPI读写W25Q64

MySPI.c

#include "stm32f10x.h"                  // Device header/*** @brief  对选线进行写操作* @param  BitValue:对选线进行选中或不选(低电平有效)* @retval 无*/
void MySPI_W_SS(uint8_t BitValue)
{GPIO_WriteBit(GPIOA,GPIO_Pin_4,(BitAction)BitValue);
}/*** @brief  对SCK时钟线进行写操作* @param  BitValue:写入高低电平* @retval 无*/
void MySPI_W_SCK(uint8_t BitValue)
{GPIO_WriteBit(GPIOA,GPIO_Pin_5,(BitAction)BitValue);
}/*** @brief  对MOSI进行写操作(主机为输出)* @param  BitValue:输出的数据位* @retval 无*/
void MySPI_W_MOSI(uint8_t BitValue)
{GPIO_WriteBit(GPIOA,GPIO_Pin_7,(BitAction)BitValue);
}/*** @brief  对从机进行读操作* @param  无* @retval 读到的数据*/
uint8_t MySPI_R_MISO(void)
{return GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6);
}/*** @brief  初始化SPI及其通信引脚* @param  无* @retval 无*/
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_SS(1);//将SS初始化为高电平,不选中从机//使用模式0MySPI_W_SCK(0);//默认为低电平
}/*** @brief  生成SPI起始条件* @param  无* @retval 无*/
void MySPI_Start(void)
{MySPI_W_SS(0);//起始条件
}/*** @brief  生成SPI结束条件* @param  无* @retval 无*/
void MySPI_Stop(void)
{MySPI_W_SS(1);//结束条件
}/*** @brief  使用SPI协议交换一个字节* @param  ByteSend:发送的字节* @retval 交换得到的字节*/
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;
}//uint8_t MySPI_SwapByte(uint8_t ByteSend)
//{
//    for(i = 0;i < 8;i ++)
//    {
//        MySPI_W_MOSI(ByteSend & 0x80);//主机移出最高位数据
//        ByteSend << 1;  
//        MySPI_W_SCK(1);//拉高进行读取
//        if(MySPI_R_MISO() == 1)//主机接收从机发出的数据
//            {
//                ByteSend |= 0x01;//将读入的直接放在低位
//            }
//        MySPI_W_SCK(0);//拉低进行移位
//    }
//    return ByteSend;
//}

W25Q64.c

#include "stm32f10x.h"                  // Device header
#include "MySPI.h"
#include "W25Q64_Ins.h"/*** @brief  初始化SPI协议及引脚* @param  无* @retval 无*/
void W25Q64_Init(void)
{MySPI_Init();
}/*** @brief  读取厂商以及设备ID* @param  MID:厂商ID* @param  DID:设备ID* @retval 无*/
void W25Q64_ReadID(uint8_t *MID,uint16_t *DID)
{MySPI_Start();//开启SPI时序MySPI_SwapByte(W25Q64_JEDEC_ID);//0x9F命令字下一次交换返回厂商以及设备ID*MID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);//交换出来的是厂商ID*DID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);//交换出来的是设备ID的高八位*DID <<= 8;//将其移至高八位*DID |= MySPI_SwapByte(W25Q64_DUMMY_BYTE);//交换出来的是设备ID的低八位并进行合并MySPI_Stop();//结束SPI时序
}/*** @brief  完成写使能时序* @param  无* @retval 无*/
void W25Q64_WriteEnable(void)
{MySPI_Start();//开启SPI时序MySPI_SwapByte(W25Q64_WRITE_ENABLE);//发送写使能指令MySPI_Stop();//结束SPI时序
}/*** @brief  等待状态寄存器Busy位置0* @param  无* @retval 无*/
void W25Q64_WaitBusy(void)
{uint32_t Timeout;MySPI_Start();//开启SPI时序MySPI_SwapByte(W25Q64_READ_STATUS_REGISTER_1);//发送读状态寄存器1指令Timeout = 100000;while((MySPI_SwapByte(W25Q64_DUMMY_BYTE) & 0x01) == 0x01)//返回回来为寄存器1状态{//接收数据Busy位并进行判断,若是Busy则进行定时退出Timeout--;if(Timeout == 0){break;}}MySPI_Stop();//结束SPI时序
}/*** @brief  拼接时序使用页编程(0~256KB)* @param  Address:写入的地址* @param  DataArray:写入的数组* @param  Count:写入数据个数* @retval 无*/
void W25Q64_PageProgram(uint32_t Address,uint8_t *DataArray,uint16_t Count)
{W25Q64_WriteEnable();//写入操作需先进行写使能uint16_t i;MySPI_Start();//开启SPI时序MySPI_SwapByte(W25Q64_PAGE_PROGRAM);//发送页编程指令//需要传输24位地址,需要六个字节,这里一次只能发两个字节(8位数据)MySPI_SwapByte(Address >> 16);//高两位字节先发送MySPI_SwapByte(Address >> 8);//中间两位(高位舍弃)MySPI_SwapByte(Address);//最后两位for(i = 0;i < Count;i ++){MySPI_SwapByte(DataArray[i]);//发送写入数据}MySPI_Stop();//结束SPI时序W25Q64_WaitBusy();//进行事后等待(写入后芯片进入忙状态)//事后最保险,事前效率高
}/*** @brief  指定地址擦除扇区时序(4KB)* @param  Address:擦除的位置地址* @retval 无*/
void W25Q64_SectorErase(uint32_t Address)
{W25Q64_WriteEnable();//写入操作需先进行写使能MySPI_Start();//开启SPI时序MySPI_SwapByte(W25Q64_SECTOR_ERASE_4KB);//发送扇区擦除指令(4KB)//需要传输24位地址,需要六个字节,这里一次只能发两个字节(8位数据)MySPI_SwapByte(Address >> 16);//高两位字节先发送MySPI_SwapByte(Address >> 8);//中间两位(高位舍弃)MySPI_SwapByte(Address);//最后两位MySPI_Stop();//结束SPI时序W25Q64_WaitBusy();//进行事后等待(写入后芯片进入忙状态)//事后最保险,事前效率高
}/*** @brief  指定地址读取数据时序* @param  Address:读取的位置地址* @param  DataArray:读取的数组* @param  Count:读取数据个数*/
void W25Q64_ReadData(uint32_t Address,uint8_t *DataArray,uint32_t Count)
{uint32_t i;MySPI_Start();//开启SPI时序MySPI_SwapByte(W25Q64_READ_DATA);//发送读取数据指令//需要传输24位地址,需要六个字节,这里一次只能发两个字节(8位数据)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();//结束SPI时序
}

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

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[] = {0x55,0x66,0x77,0x88};
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);//写入数据前先要擦除扇区否则写入数据可能出错(擦除后数据为0xFF)W25Q64_PageProgram(0x0000FF,ArrayWrite,4);//将数据写入到扇区(只有1->0,没有0->1)W25Q64_ReadData(0x0000FF,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){}}

硬件SPI读写W25Q64

MySPI.c

#include "stm32f10x.h"                  // Device header/*** @brief  对选线进行写操作* @param  BitValue:对选线进行选中或不选(低电平有效)* @retval 无*/
void MySPI_W_SS(uint8_t BitValue)
{GPIO_WriteBit(GPIOA,GPIO_Pin_4,(BitAction)BitValue);
}/*** @brief  初始化SPI及其通信引脚* @param  无* @retval 无*/
void MySPI_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);//打开SPI1外设时钟GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//SS使用推挽输出GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//SCK,MOSI输出使用复用推挽输出(使用了引脚的复用功能)GPIO_InitStructure.GPIO_Pin = 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;//MISO输入使用上拉输入GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);SPI_InitTypeDef SPI_InitStructure;SPI_InitStructure.SPI_Mode = SPI_Mode_Master;//SPI模式(主机/从机)SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//SPI裁剪引脚功能(双线全双工)SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;//配置数据帧(8位)SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;//配置高位先行还是低位先行(高位先行)SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;//配置SCK频率(72MHz / 128)SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;//时钟极性(默认高电平)配置模式0SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;//对第几个边沿进行采样(从第一个边沿进行采样)SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;//软件NSSSPI_InitStructure.SPI_CRCPolynomial = 7;//配置CRC校验多项式SPI_Init(SPI1,&SPI_InitStructure);SPI_Cmd(SPI1,ENABLE);//开启硬件SPI1MySPI_W_SS(1);//默认高电平
}/*** @brief  生成SPI起始条件* @param  无* @retval 无*/
void MySPI_Start(void)
{MySPI_W_SS(0);//起始条件
}/*** @brief  生成SPI结束条件* @param  无* @retval 无*/
void MySPI_Stop(void)
{MySPI_W_SS(1);//结束条件
}/*** @brief  使用SPI协议交换一个字节* @param  ByteSend:发送的字节* @retval 交换得到的字节*/
uint8_t MySPI_SwapByte(uint8_t ByteSend)
{while (SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE) != SET);//等待TXE为1(为空)SPI_I2S_SendData(SPI1,ByteSend);//将数据写入到TDR,ByteSend转入移位寄存器while (SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE) != SET);//等待RXNE为1,表示接收到一个字节return SPI_I2S_ReceiveData(SPI1);//发送完成读取RDR接收到的数据
}

其他文件并未修改,与之前一样,效果与软件SPI一致。

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

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

相关文章

.NET Core WebAPI中封装Swagger配置

一、创建相关文件 创建一个Utility/SwaggerExt文件夹&#xff0c;添加一个类 二、在Program中找到Swagger相关配置信息 三、添加方法&#xff0c;在Program中调用 在SwaggerExt类中添加方法&#xff0c;将相关配置添写入 /// <summary> /// swagger配置 /// </sum…

软件架构设计--操作系统

常见I/O类型:缓冲I/O和直接I/O 缓冲I/O是C语言提供的库函数 直接I/O是Linux系统API 应用程序内存:代码中用malloc/free,new/delete 等分配出来的内存 用户缓冲区:C语言的FILE结构体里面的bufer. 内核缓冲区:Linux操作系统的Page Cache. 1Page 一般为4K 缓冲I/O的读操作有3次数…

XMall 开源商城 SQL注入漏洞复现(CVE-2024-24112)

0x01 产品简介 XMall 开源电商商城 是开发者Exrick的一款基于SOA架构的分布式电商购物商城 前后端分离 前台商城:Vue全家桶 后台管理:Dubbo/SSM/Elasticsearch/Redis/MySQL/ActiveMQ/Shiro/Zookeeper等。 0x02 漏洞概述 XMall 开源商城 /item/list、/item/listSearch、/sys/…

GPT-4影响高度创新思维的领域(一)

GPT-4的应用范围不再局限于对现有信息的检索、整理和复述&#xff0c;而是进一步拓展到了诸如文学创作、科学假设生成、教育辅导、商业策略建议等需要高度创新思维的领域。这种独立思考和创新能力赋予了GPT-4作为虚拟助手时更加丰富多元的角色定位&#xff0c;使其成为一种强大…

Git简单了解

文章目录 1、Git概述2、Git下载与安装3、Git代码托管服务3.1、使用码云托管服务 1、Git概述 什么是Git Git是一个分布式版本控制工具&#xff0c;主要用于管理开发过程中的源代码文件&#xff08;Java类、xml文件、html页面等&#xff09;&#xff0c;在软件开发过程中被广泛使…

一、ActiveMQ介绍

ActiveMQ介绍 一、JMS1.jms介绍2.jms消息传递模式3.jms编码总体架构 二、消息中间件三、ActiveMQ介绍1.引入的原因1.1 原因1.2 遇到的问题1.3 解决思路 2.定义3.特点3.1 异步处理3.2 应用系统之间解耦3.3 实际-整体架构 4.作用 一、JMS 1.jms介绍 jms是java消息服务接口规范&…

【微服务】skywalking自定义告警规则使用详解

目录 一、前言 二、SkyWalking告警功能介绍 2.1 SkyWalking告警是什么 2.2 为什么需要SkyWalking告警功能 2.2.1 及时发现系统异常 2.2.2 保障和提升系统稳定性 2.2.3 避免数据丢失 2.2.4 提高故障处理效率 三、 SkyWalking告警规则 3.1 SkyWalking告警规则配置 3.2 …

构建智慧交通平台:架构设计与实现

随着城市交通的不断发展和智能化技术的迅速进步&#xff0c;智慧交通平台作为提升城市交通管理效率和水平的重要手段备受关注。本文将探讨如何设计和实现智慧交通平台的系统架构&#xff0c;以应对日益增长的城市交通需求&#xff0c;并提高交通管理的智能化水平。 ### 1. 智慧…

高级数据结构与算法 | 布谷鸟过滤器(Cuckoo Filter):原理、实现、LSM Tree 优化

文章目录 Cuckoo Filter基本介绍布隆过滤器局限变体 布谷鸟哈希布谷鸟过滤器 实现数据结构优化项Victim Cache备用位置计算半排序桶 插入查找删除 应用场景&#xff1a;LSM 优化 Cuckoo Filter 基本介绍 如果对布隆过滤器不太了解&#xff0c;可以看看往期博客&#xff1a;海量…

Netty应用(一) 之 NIO概念 基本编程

目录 第一章 概念引入 1.分布式概念引入 第二章 Netty基础 - NIO 1.引言 1.1 什么是Netty&#xff1f; 1.2 为什么要学习Netty&#xff1f; 2.NIO编程 2.1 传统网络通信中开发方式及问题&#xff08;BIO&#xff09; 2.1.1 多线程版网络编程 2.1.2 线程池版的网络编程…

操作 Docker 存储卷的常用指令汇总

1. 什么是存储卷&#xff1f; 存储卷就是将宿主机的本地文件系统中存在的某个目录直接与容器内部的文件系统上的某一目录建立绑定关系。使得可以在宿主机和容器内共享数据库内容&#xff0c;让容器直接访问宿主机中的内容&#xff0c;也可以宿主机向容器写入内容&#xff0c;容…

元宇宙专题:元宇宙概念娱乐应用场景案例研究报告 - 体验驱动篇

今天分享的是元宇宙系列深度研究报告&#xff1a;《元宇宙专题&#xff1a;元宇宙概念娱乐应用场景案例研究报告 - 体验驱动篇》。 &#xff08;报告出品方&#xff1a;艾瑞咨询&#xff09; 报告共计&#xff1a;51页 避免刻舟求剑地探索元宇宙概念产品 对于任何一个宏大而…

建筑工程如何找答案? #职场发展#笔记#职场发展

这些软件以其强大的搜索引擎和智能化的算法&#xff0c;为广大大学生提供了便捷、高效的解题方式。下面&#xff0c;让我们一起来了解几款备受大学生欢迎的搜题软件吧&#xff01; 1.易解题 这是一个网站 是我在百度搜题&#xff0c;经常会出现的一个网站&#xff0c;它里面…

Ubuntu Desktop - Disks

Ubuntu Desktop - Disks 1. Search your computer -> DisksReferences 1. Search your computer -> Disks ​ References [1] Yongqiang Cheng, https://yongqiang.blog.csdn.net/

Spring Boot 笔记 021 项目部署

1.1 引入坐标&#xff0c;并双击package打包成jar包 1.2 在服务器上运行jar包 1.3 使用postman测试 2.1 运行配置 2.1.1 命令更改端口 java -jar big-event-1.0-SNAPSHOT.jar --server.port7777 2.1.2 环境变量更新&#xff08;略&#xff09; 2.1.3 外部配置文件&#xff0c…

H12-821_48

48.下面是台路由器输出的BGP信息,关于这段信息描述措误的是 A.路由器的Router ID是1.1.1.9 B.display bgp network命令来显示BGP通过network ( BGP)的通告的路由信息 C.该路由器所在AS号是10 D.该路由器通过import-route命今引入了4.4.4.0/24的网段 答案&#xff1a;D 注释&am…

【剪辑必备】今天我教你如何手动去下载苹果官网4K预告片 完全免费

&#x1f680; 个人主页 极客小俊 ✍&#x1f3fb; 作者简介&#xff1a;web开发者、设计师、技术分享博主 &#x1f40b; 希望大家多多支持一下, 我们一起学习和进步&#xff01;&#x1f604; &#x1f3c5; 如果文章对你有帮助的话&#xff0c;欢迎评论 &#x1f4ac;点赞&a…

thinkphp+vue企业产品展示网站f7enu

本文首先介绍了企业产品展示网站管理技术的发展背景与发展现状&#xff0c;然后遵循软件常规开发流程&#xff0c;首先针对系统选取适用的语言和开发平台&#xff0c;根据需求分析制定模块并设计数据库结构&#xff0c;再根据系统总体功能模块的设计绘制系统的功能模块图&#…

【leetcode热题100】恢复二叉搜索树

给你二叉搜索树的根节点 root &#xff0c;该树中的 恰好 两个节点的值被错误地交换。请在不改变其结构的情况下&#xff0c;恢复这棵树 。 示例 1&#xff1a; 输入&#xff1a;root [1,3,null,null,2] 输出&#xff1a;[3,1,null,null,2] 解释&#xff1a;3 不能是 1 的左孩…

【C深度解剖】取模与取余

简介&#xff1a;本系列博客为C深度解剖系列内容&#xff0c;以某个点为中心进行相关详细拓展 适宜人群&#xff1a;已大体了解C语法同学 作者留言&#xff1a;本博客相关内容如需转载请注明出处&#xff0c;本人学疏才浅&#xff0c;难免存在些许错误&#xff0c;望留言指正 作…