stm32单片机个人学习笔记16(SPI通信协议)
前言
本篇文章属于stm32单片机(以下简称单片机)的学习笔记,来源于B站教学视频。下面是这位up主的视频链接。本文为个人学习笔记,只能做参考,细节方面建议观看视频,肯定受益匪浅。
STM32入门教程-2023版 细致讲解 中文字幕_哔哩哔哩_bilibili
一、SPI通信

1.硬件电路规定

2.移位示意图

3.软件时序规定




4.实际通信时序
区别于I2C是对寄存器进行操作,SPI通信的从机会定义一个指令集,起始条件后,主机给从机发一个指令完成相应任务即可

第一个字节为写指令0x02,第二三四个字节为地址,地址高位先行,第五个字节是写入的数据

第一个字节为写指令0x02,第二三四个字节为地址,第五个字节进行交换字节,主机发送一个0xFF或0x00把从机发送的数据交换回来
二、W25Q64
1.简介

2.硬件电路

3.W25Q64框图
把8MB的内存分为128个块(Block),每个块又分为16个扇(Sector),每个扇又分16页(Page) ,每页256B
4.Flash操作注意事项

5.具体细节详见W25Q64的参考手册
三、软件SPI读写W25Q64
1.MySPI.c
#include "stm32f10x.h" // Device headervoid MySPI_W_SS(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);
}void MySPI_W_MOSI(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_7, (BitAction)BitValue);
}uint8_t MySPI_R_MISO(void)
{return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6);
}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);MySPI_W_SCK(0);
}void MySPI_Start(void)
{MySPI_W_SS(0);
}void MySPI_Stop(void)
{MySPI_W_SS(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;
}
其中交换字节函数还可写为
uint8_t MySPI_SwapByte(uint8_t ByteSend) {uint8_t i;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; }这样效率更高,但会改变ByteSend的值
2.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(W25Q64_DUMMY_BYTE);*DID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);*DID <<= 8;*DID |= MySPI_SwapByte(W25Q64_DUMMY_BYTE);MySPI_Stop();
}void W25Q64_WriteEnable(void)
{MySPI_Start();MySPI_SwapByte(W25Q64_WRITE_ENABLE);MySPI_Stop();
}void W25Q64_WaitBusy(void)
{uint32_t TimeOut = 100000;MySPI_Start();MySPI_SwapByte(W25Q64_READ_STATUS_REGISTER_1);while((MySPI_SwapByte(W25Q64_DUMMY_BYTE) & 0x01) == 0x01){TimeOut--;if (TimeOut == 0){break;}}MySPI_Stop();
}void W25Q64_PageProgram(uint32_t Address, uint8_t *DataArray, uint16_t Count)
{uint16_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_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, uint32_t Count)
{uint32_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();
}
3.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[] = {0xB1, 0xB2, 0xB3, 0xB4};
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){}
}
四、硬件SPI读写W25Q64
1.SPI通信外设

2.SPI框图

3.SPI基本结构
4.主模式全双工连续传输(较为复杂,性能好)
5.非连续传输(较为简单,性能差一点)

6.代码部分
只需更改MySPI.c部分的内容即可
配置好硬件SPI
#include "stm32f10x.h" // Device headervoid MySPI_W_SS(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue);
}void MySPI_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);GPIO_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);GPIO_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);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);SPI_InitTypeDef SPI_InitStructure;SPI_InitStructure.SPI_Mode = SPI_Mode_Master;SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;SPI_InitStructure.SPI_CRCPolynomial = 7;SPI_Init(SPI1, &SPI_InitStructure);SPI_Cmd(SPI1, ENABLE);MySPI_W_SS(1);
}void MySPI_Start(void)
{MySPI_W_SS(0);
}void MySPI_Stop(void)
{MySPI_W_SS(1);
}uint8_t MySPI_SwapByte(uint8_t ByteSend)
{while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) != SET);SPI_I2S_SendData(SPI1, ByteSend);while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) != SET);return SPI_I2S_ReceiveData(SPI1);
}
相关文章:
stm32单片机个人学习笔记16(SPI通信协议)
前言 本篇文章属于stm32单片机(以下简称单片机)的学习笔记,来源于B站教学视频。下面是这位up主的视频链接。本文为个人学习笔记,只能做参考,细节方面建议观看视频,肯定受益匪浅。 STM32入门教程-2023版 细…...
Linux | RHEL / CentOS 中 YUM history / downgrade 命令回滚操作
注:英文引文,机翻未校。 在 RHEL/CentOS 系统上使用 YUM history 命令回滚升级操作 作者: 2daygeek 译者: LCTT DarkSun 为服务器打补丁是 Linux 系统管理员的一项重要任务,为的是让系统更加稳定,性能更加…...
BGP状态和机制
BGP邻居优化 为了增加稳定性,通常建议实验回环口来建立邻居。更新源:建立邻居和邻居所学习到的路由的下一跳。多跳:EBGP邻居建立默认选哟直连,因为TTL=1,如果非直连,必须修改TTL。命令备注peer 2.2.2.2 connect-interface lo1配置更新源peer 2.2.2.2 ebgp-max-hop 2配置T…...
温湿度监控设备融入智慧物联网
当医院的温湿度监控设备融入智慧物联网,将会带来许多新的体验,可以帮助医院温湿度监控设备智能化管理,实现设备之间的互联互通,方便医院对温湿度数据进行统一管理和分析。 添加智慧物联网技术,实现对医院温湿度的实时…...
smolagents学习笔记系列(五)Tools-in-depth-guide
这篇文章锁定官网教程中的 Tools-in-depth-guide 章节,主要介绍了如何详细构造自己的Tools,在之前的博文 smolagents学习笔记系列(二)Agents - Guided tour 中我初步介绍了下如何将一个函数或一个类声明成 smolagents 的工具&…...
前端面试真题 2025最新版
文章目录 写在前文CSS怪异盒模型JS闭包闭包的形成闭包注意点 CSS选择器及优先级优先级 说说flex布局及相关属性Flex 容器相关属性:Flex 项目相关属性 响应式布局如何实现是否用过tailwindcss,有哪些好处好处缺点 说说对象的 prototype属性及原型说说 pro…...
面试八股文--数据库基础知识总结(1)
1、数据库的定义 数据库(DataBase,DB)简单来说就是数据的集合数据库管理系统(Database Management System,DBMS)是一种操纵和管理数据库的大型软件,通常用于建立、使用和维护数据库。数据库系统…...
10. docker nginx官方镜像使用方法
本文介绍docker nginx官方镜像使用方法,因为第一次用,在加上对docker也不是很熟,中间踩了一些坑,为了避免下一次用又踩坑,因此记录如下,也希望能够帮到其它小伙伴。 官方镜像页面:https://hub.d…...
[Web 安全] PHP 反序列化漏洞 —— PHP 反序列化漏洞演示案例
关注这个专栏的其他相关笔记:[Web 安全] 反序列化漏洞 - 学习笔记-CSDN博客 PHP 反序列化漏洞产生原因 PHP 反序列化漏洞产生的原因就是因为在反序列化过程中,unserialize() 接收的值可控。 0x01:环境搭建 这里笔者是使用 PhpStudy 搭建的环…...
es-head(es库-谷歌浏览器插件)
1.下载es-head插件压缩包,并解压缩 2.谷歌浏览器添加插件 3.使用...
第二十:【路由的props配置】
作用:让路由组件更方便的收到参数(可以将路由参数作为props传给组件) {name:xiang,path:detail/:id/:title/:content,component:Detail, 第一种方法:// props的对象写法,作用:把对象中的每一组key-valu…...
Vue 2全屏滚动动画实战:结合fullpage-vue与animate.css打造炫酷H5页面
引言 在移动端H5开发中,全屏滚动效果因其沉浸式体验而广受欢迎。如何快速实现带有动态加载动画的全屏滚动页面?本文将手把手教你使用 Vue 2、全屏滚动插件 fullpage-vue 和动画库 animate.css 3.5.1,打造一个高效且视觉冲击力强的H5页面。通…...
AF3 DataPipeline类process_pdb 方法解读
DataPipeline 类中的 process_pdb 方法用于从 PDB 文件中生成特定蛋白质链的特征,作为 AlphaFold3 预测的输入。它的流程与 process_mmcif 类似,但输入来源是 PDB 文件而非 MmcifObject。 源代码: def process_pdb(self,pdb_path: str,alignment_dir: str,is_distillation:…...
抓包工具 wireshark
1.什么是抓包工具 抓包工具是什么?-CSDN博客 2.wireshark的安装 【抓包工具】win 10 / win 11:WireShark 下载、安装、使用_windows抓包工具-CSDN博客 3.wireshark的基础操作 Wireshark零基础使用教程(超详细) - 元宇宙-Meta…...
OpenBMC:BmcWeb app获取socket
OpenBMC:BmcWeb app.run-CSDN博客 app对象在run函数中调用了setupSocket() static std::vector<Acceptor> setupSocket() {std::vector<Acceptor> acceptors;char** names = nullptr;int listenFdCount = sd_listen_fds_with_names(0, &names);BMCWEB_LOG_DE…...
《深度学习实战》第2集-补充:卷积神经网络(CNN)与图像分类 实战代码解析和改进
以下是对《深度学习实战》第2集中 CIFAR-10 数据集 使用卷积神经网络进行图像分类实战 代码的详细分析,并增加数据探索环节,同时对数据探索、模型训练和评估的过程进行具体说明。所有代码都附上了运行结果配图,方便对比。 《深度学习实战》第…...
nodejs:express + js-mdict 作为后端,vue 3 + vite 作为前端,在线查询英汉词典
向 doubao.com/chat/ 提问: node.js js-mdict 作为后端,vue 3 vite 作为前端,编写在线查询英汉词典 后端部分(express js-mdict ) 1. 项目结构 首先,创建一个项目目录,结构如下࿱…...
《深度剖析Linux 系统 Shell 核心用法与原理_666》
1. 管道符的用法 查找当前目录下所有txt文件并统计行数 # 使用管道符将ls命令的结果传递给wc命令进行行数统计 ls *.txt | wc -l 在/etc目录下查找包含"network"的文件并统计数量 # 使用find命令查找文件,并通过grep查找包含特定字符串的文件…...
索提诺比率(Sortino Ratio):更精准的风险调整收益指标(中英双语)
索提诺比率(Sortino Ratio):更精准的风险调整收益指标 📉📊 📌 什么是索提诺比率? 在投资分析中,我们通常使用 夏普比率(Sharpe Ratio) 来衡量风险调整后的…...
minio作为K8S后端存储
docker部署minio mkdir -p /minio/datadocker run -d \-p 9000:9000 \-p 9001:9001 \--name minio \-v /minio/data:/data \-e "MINIO_ROOT_USERjbk" \-e "MINIO_ROOT_PASSWORDjbjbjb123" \quay.io/minio/minio server /data --console-address ":90…...
关于nvm与node.js
1 安装nvm 安装过程中手动修改 nvm的安装路径, 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解,但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后,通常在该文件中会出现以下配置&…...
Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器
第一章 引言:语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域,文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量,支撑着搜索引擎、推荐系统、…...
ElasticSearch搜索引擎之倒排索引及其底层算法
文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...
MySQL 8.0 OCP 英文题库解析(十三)
Oracle 为庆祝 MySQL 30 周年,截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始,将英文题库免费公布出来,并进行解析,帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...
pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...
如何理解 IP 数据报中的 TTL?
目录 前言理解 前言 面试灵魂一问:说说对 IP 数据报中 TTL 的理解?我们都知道,IP 数据报由首部和数据两部分组成,首部又分为两部分:固定部分和可变部分,共占 20 字节,而即将讨论的 TTL 就位于首…...
C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。
1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj,再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...
在 Spring Boot 项目里,MYSQL中json类型字段使用
前言: 因为程序特殊需求导致,需要mysql数据库存储json类型数据,因此记录一下使用流程 1.java实体中新增字段 private List<User> users 2.增加mybatis-plus注解 TableField(typeHandler FastjsonTypeHandler.class) private Lis…...
Xcode 16 集成 cocoapods 报错
基于 Xcode 16 新建工程项目,集成 cocoapods 执行 pod init 报错 ### Error RuntimeError - PBXGroup attempted to initialize an object with unknown ISA PBXFileSystemSynchronizedRootGroup from attributes: {"isa">"PBXFileSystemSynchro…...
门静脉高压——表现
一、门静脉高压表现 00:01 1. 门静脉构成 00:13 组成结构:由肠系膜上静脉和脾静脉汇合构成,是肝脏血液供应的主要来源。淤血后果:门静脉淤血会同时导致脾静脉和肠系膜上静脉淤血,引发后续系列症状。 2. 脾大和脾功能亢进 00:46 …...




