STM32F1 - SPI读写Flash

Serial peripheral interface

  • 1> 实验概述
  • 2> SPI硬件框图
    • 初始化程序
  • 3> STM32的SPI通信时序
    • 3.1> 时序图
    • 3.2> 文字描述
    • 3.3> 注意事项
    • 3.4> 流程图表示
    • 3.5> 程序表示
      • 接收程序:
      • 发送程序:
  • 4> SPI的4种模式
  • 5> W25Q128存储结构
    • 块 > 扇区 > 页
  • 6> W25Q128常用命令
    • 6.1> 读状态寄存器
      • 检测忙程序
    • 6.2> 写使能
      • 写使能-程序
    • 6.3> 擦除1个扇区
      • 擦除1个扇区-程序
    • 6.4> 写入1页Page数据
      • 写1页数据-程序
    • 6.5> 读数据
  • 7> 测试程序
    • 7.1> 逻辑分析仪 抓波形


1> 实验概述

使用STM32的SPI硬件模块,读写Flash


2> SPI硬件框图

2

MOSI : Master Output Slave Input;
MISO: Master Input Slave Output;

初始化程序

/*** @brief SPI硬件模块配置,全双工, 高位优先* @note  SPI2, CS-PB12, SCK-PB13,  MISO-PB14, MOSI-PB15;*/
void NorFLASH_Init(void)
{GPIO_InitTypeDef GPIO_InitStruct;SPI_InitTypeDef SPI_InitStruct;/* 首先开启时钟 */RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);/* GPIO参数配置 */// CS-PB12GPIO_InitStruct.GPIO_Pin = GPIO_Pin_12;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStruct);// SCK-PB13GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStruct);// MISO-PB14GPIO_InitStruct.GPIO_Pin = GPIO_Pin_14;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStruct);// MOSI-PB15GPIO_InitStruct.GPIO_Pin = GPIO_Pin_15;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStruct);/* SPI2参数配置 */SPI_InitStruct.SPI_Mode = SPI_Mode_Master;SPI_InitStruct.SPI_CPOL = SPI_CPOL_High;SPI_InitStruct.SPI_CPHA = SPI_CPHA_2Edge; // Mode 3;SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;SPI_InitStruct.SPI_NSS = SPI_NSS_Soft;SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;SPI_InitStruct.SPI_CRCPolynomial = 0x07; // 复位值,无用SPI_Init(SPI2, &SPI_InitStruct);SPI_CalculateCRC(SPI2, DISABLE);	//  关闭硬件CRC校验/* 使能SPI2 */SPI_Cmd(SPI2, ENABLE);
}

3> STM32的SPI通信时序


3.1> 时序图

3


3.2> 文字描述

Step 1> 写【第1】字节数据到SPI_DR;
Step 2> 等待【TXE == 1】, 写【第2】字节到SPI_DR;
Step 3> 等待【RXNE == 1】, 读SPI_DR, 得到【第1】字节数据;
Step 4> 如果要读写多个字节【循环重复】第2步和第3步;
Step 5> 等待【RXNE == 1】, 读SPI_DR, 得到【最后】字节数据;
Step 6> 等待【TXE == 1】,完成读写;


3.3> 注意事项

1> 接收数据时,SPI也必须发送数据,这样才能产生SCK时钟;(这点设计的感觉不好)
2> 在MISO线上输出完8bit数据后,才会存储到SPI_DR, 所以他相等于落后了一个字节;
3> TXE的标志由硬件置1,写SPI_DR可以清除;
4> RXNE 标志是由硬件置1,读SPI_DR可以清除;


3.4> 流程图表示

32


3.5> 程序表示


接收程序:

接收数据,也需要发送数据,通常发送无意义的0xFF

/*** @brief 接收多字节数据* @param pRxData 接收数据缓冲区* @param size 接收size字节数据*/
static void SPI2_Receive(uint8_t *pRxData, uint16_t size)
{// Step 1> 发送第1字节数据SPI_I2S_ReceiveData(SPI2); 	  // 清除RXNE标志, 清空接收缓冲区数据SPI_I2S_SendData(SPI2, 0xFF); // 发送任意值, 目的只是产生CLKwhile (size > 1) { // Step 2>  /* 等待TXE==1,然后写入第2字节, 要发送的数据 */while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) != SET) {/*wati*/;}SPI_I2S_SendData(SPI2, 0xFF);/* Step 3> 等待RXNE==1, 读出SPI_DR寄存器, 得到第1字节数据, 读的同时会清除RXNE标志 */while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) != SET) {/* 等待RXNE标志为1, 接收数据 */;}*pRxData = SPI_I2S_ReceiveData(SPI2);*pRxData++;size--;}/* Step 4> 等待RXNE==1, 读出SPI_DR寄存器, 得到最后1字节数据 */while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) != SET) {/* 等待RXNE标志为1, 接收数据 */;}*pRxData = SPI_I2S_ReceiveData(SPI2);/ *Step 5> 等待发送完成*/ while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) != SET) {/* 等待最后1字节发送完成,方便片选信号拉高 */;}
}

发送程序:

1

/*** @brief 发送多字节数据, 轮询方式* @param pData, 发送数据缓冲区* @param size 发送size字节数据*/
static void SPI2_Transmit(uint8_t *pData, uint16_t Size)
{	while (Size > 0) {SPI_I2S_SendData(SPI2, *pData);while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) != SET) {/* 等待TXE标志为1,发送数据 */;}	pData++;Size--;	}	
}

4> SPI的4种模式


4种模式表格:
4


4种模式-时序图:
42


5> W25Q128存储结构

5
Block(块):64KByte;
Sector(扇区): 4KByte;
Page(页):256Byte;

128Mbit = 16MByte = 256个Block = 4096个Sector;

块 > 扇区 > 页

ff

1个块 = 16个扇区;
1个扇区 = 16个页;


6> W25Q128常用命令

Flash存储器:写之前要先擦除;

写入时只能写0, 不能写1;
写1是靠擦除命令实现的。

5


6.1> 读状态寄存器

51

主机读写过程:

Step 1> 主机发送0x05命令, 从机无数据;
Step 2> 主机发送任意值,目的是产生CLK时钟,从机才能回数据;

BUSY位:
522


检测忙程序

/*** @brief 检测Flash忙不忙*/
void NorFLASH_ReadBusy(void)
{uint8_t cmd = 0x05;uint8_t reg = 0x00;GPIO_ResetBits(GPIOB, GPIO_Pin_12); 	// CS拉低SPI2_Transmit(&cmd, 1);SPI2_Receive(&reg, 1);while ((reg & 0x01) == 0x01) {SPI2_Receive(&reg, 1);	// 等待busy}GPIO_SetBits(GPIOB, GPIO_Pin_12);		// CS拉高	
}

6.2> 写使能

62

63


写使能-程序

/*** @brief 写使能*/
void NorFLASH_WriteEnable(void)
{uint8_t cmd = 0x06;NorFLASH_ReadBusy();					// 忙检测GPIO_ResetBits(GPIOB, GPIO_Pin_12); 	// CS拉低SPI2_Transmit(&cmd, 1);GPIO_SetBits(GPIOB, GPIO_Pin_12);		// CS拉高
}

6.3> 擦除1个扇区

52

硬件设计,最少只能擦除1个扇区4KByte;

24位地址:3字节;

擦除0#扇区, Adress 为【0x00 00 00】;
擦除4080#扇区,Adress为【0xFF 00 00】;


擦除1个扇区-程序

/*** @brief 擦除1个扇区数据, 4KByte* @param num 扇区序号*/
void NorFLASH_EraseSector(uint32_t num)
{uint8_t cmd[4];uint32_t addr;addr = num * 4096;// 构建数据cmd[0] = 0x20;cmd[1] = addr >> 16;cmd[2] = addr >> 8;cmd[3] = addr >> 0;NorFLASH_WriteEnable();					// 写使能NorFLASH_ReadBusy();					// 忙检测GPIO_ResetBits(GPIOB, GPIO_Pin_12); 	// CS拉低SPI2_Transmit(cmd, 4);GPIO_SetBits(GPIOB, GPIO_Pin_12);		// CS拉高
}

6.4> 写入1页Page数据

63

1页Page = 256Byte;

64

循环写入,超过256字节,会覆盖开始的字节;


写1页数据-程序

/*** @brief 写1页数据,256Byte* @param PData 发送数据缓冲区* @param num 页序号*/
void NorFLASH_WritePage(uint8_t *pData, uint32_t num)
{uint8_t cmd[4];uint32_t addr;addr = num * 256;	// 1页256个字节// 构建数据cmd[0] = 0x02;cmd[1] = addr >> 16;cmd[2] = addr >> 8;cmd[3] = addr >> 0;NorFLASH_WriteEnable();					// 写使能NorFLASH_ReadBusy();					// 忙检测GPIO_ResetBits(GPIOB, GPIO_Pin_12); 	// CS拉低SPI2_Transmit(cmd, 4);					// 写命令SPI2_Transmit(pData, 256);				// 写数据GPIO_SetBits(GPIOB, GPIO_Pin_12);		// CS拉高
}

6.5> 读数据

64
存储器地址范围内,任意地址都可以读数据;

/*** @brief 读Flash数据* @param PRxData 接收数据缓冲区* @param addr Flash起始地址* @param size 读size字节数据*/
void NorFLASH_Read(uint8_t *pRxData, uint32_t addr, uint32_t size)
{uint8_t cmd[4];// 构建数据cmd[0] = 0x03;cmd[1] = addr >> 16;cmd[2] = addr >> 8;cmd[3] = addr >> 0;NorFLASH_ReadBusy();					// 忙检测GPIO_ResetBits(GPIOB, GPIO_Pin_12); 	// CS拉低SPI2_Transmit(cmd, 4);					// 写命令SPI2_Receive(pRxData, size);GPIO_SetBits(GPIOB, GPIO_Pin_12);		// CS拉高
}

7> 测试程序

int main(void)
{ 	uint32_t i = 0;USART1_Init();NorFLASH_Init();GPIO_SetBits(GPIOB, GPIO_Pin_12);delay_ms();// 擦NorFLASH_EraseSector(0);for (i = 0; i < 4096; i++) {Wbuf[i] = 0x11;}// 写NorFLASH_WritePage(Wbuf, 0); // 写1个扇区,16页// 读NorFLASH_Read(Rbuf, 0x00, 256);// 串口打印for (i = 0; i < 256; i++) {UART_Putchar(Rbuf[i]);}while ( 1 ) {/* Nothing */;}}

7.1> 逻辑分析仪 抓波形

71

理解使用1个新外设时,看手册描述,例程,实验调试;
这把 逻辑分析仪 立了头功;

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

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

相关文章

【学习心得】网站运行时间轴(爬虫逆向)

一、网站运行时间轴 掌握网站运行时间轴&#xff0c;有助于我们对“请求参数加密”和“响应数据加密”这两种反爬手段的深入理解。 二、从网站运行的时间轴角度来理解两种反爬手段 1、加载HTML&#xff1a; 这是浏览器访问网站时的第一步&#xff0c;服务器会返回基础…

docker基线安全修复和容器逃逸修复

一、docker安全基线存在的问题和修复建议 1、将容器的根文件系统挂载为只读 修复建议&#xff1a; 添加“ --read-only”标志&#xff0c;以允许将容器的根文件系统挂载为只读。 可以将其与卷结合使用&#xff0c;以强制容器的过程仅写入要保留的位置。 可以使用命令&#x…

基于Springboot的助农管理系统(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的助农管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&…

台式电脑电源各线的电压和电流输出和输出电流

台式电脑电源是电脑硬件的重要组成部分。 它为计算机的各个部件提供所需的电压和电流。 不同的硬件设备和组件有不同的电压和电流输出。 下面详细介绍台式电脑电源各线的电压&#xff0c;包括3.3V、5V、12V、-12V、-5V和5VSB&#xff0c;以及它们的输出电流和用途。 3.3V&#…

Java递归生成本地文件目录树形结构

Java递归生成本地文件目录(树行结构) 1.读取txt文件保存的文件目录结构 2.递归生成本地文件目录树形结构&#xff0c;并修改目录文件前缀进行递增 3.结果截图 4.代码 package com.zfi.server.device;import io.swagger.annotations.Api; import org.springframework.web.bind…

Leaflet 加载高德地图

前言 在前面的文章中&#xff0c;我们学习了如何使用 Leaflet 创建一个基本的地图。在本文中&#xff0c;我们将学习如何在 Leaflet 中加载高德地图&#xff0c;并结合实际应用构建地图点击事件。 一、介绍 高德地图是一款由高德软件提供的数字地图服务&#xff0c;在国内使用…

数学建模【灰色关联分析】

一、灰色关联分析简介 一般的抽象系统,如社会系统、经济系统、农业系统、生态系统、教育系统等都包含有许多种因素&#xff0c;多种因素共同作用的结果决定了该系统的发展态势。人们常常希望知道在众多的因素中&#xff0c;哪些是主要因素&#xff0c;哪些是次要因素;哪些因素…

Apache SeaTunnel 2.3.4 版本发布:功能升级,性能提升

​Apache SeaTunnel团队自豪地宣布2.3.4版本正式发布&#xff01;本次更新聚焦于增强核心功能&#xff0c;改善用户体验&#xff0c;并进一步优化文档质量。 此次版本发布带来了多项重要更新和功能增强&#xff0c;包括核心与API的修复、文档的全面优化、Catalog支持的引入&…

【S32DS报错】-7-程序进入HardFault_Handler,无法正常运行

【S32K3_MCAL从入门到精通】合集&#xff1a; S32K3_MCAL从入门到精通https://blog.csdn.net/qfmzhu/category_12519033.html 问题背景&#xff1a; 在S32DS IDE中使用PEmicro&#xff08;Multilink ACP&#xff0c;Multilink Universal&#xff0c;Multilink FX&#xff09…

【Kotlin】函数

1 常规函数 1.1 无参函数 fun main() {myFun() }fun myFun() {println("myFun") // 打印: myFun } 1.2 有参函数 1&#xff09;常规调用 fun main() {myFun("myFun") // 打印: myFun }fun myFun(str: String) {println(str) } 2&#xff09;形参指定默…

Doris实战——美联物业数仓

目录 一、背景 1.1 企业背景 1.2 面临的问题 二、早期架构 三、新数仓架构 3.1 技术选型 3.2 运行架构 3.2.1 数据模型 纵向分域 横向分层 数据同步策略 3.2.2 数据同步策略 增量策略 全量策略 四、应用实践 4.1 业务模型 4.2 具体应用 五、实践经验 5.1 数据…

张宇30讲学习笔记

初等数学 x \sqrt{x} x ​是算数平方根&#xff0c;一定≥0&#xff1b; x 2 \sqrt{x^2} x2 ​|x| x2|x2||x|2 x3≠|x3||x|3 不等式 a>0&#xff0c;b>0&#xff0c;则ab≥2 a b \sqrt{ab} ab ​ 对数 ln a b \frac{a}{b} ba​lna-lnb 高等数学 单调性 线性代数

【六袆 - MySQL】MySQL 5.5及更高版本中,InnoDB是新表的默认存储引擎;

InnoDB 这是一个MySQL组件&#xff0c;结合了高性能和事务处理能力&#xff0c;以确保可靠性、健壮性和并发访问。它体现了ACID设计哲学。它作为一个存储引擎存在&#xff0c;处理使用ENGINEINNODB子句创建的或修改的表。请参阅第14章“InnoDB存储引擎”以获取有关架构细节和管…

Android14之解决编译报错:bazel: no such file or directory(一百八十九)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

【暗月安全】2021年渗透测试全套培训视频

参与培训需要遵守国家法律法规&#xff0c;相关知识只做技术研究&#xff0c;请勿用于违法用途&#xff0c;造成任何后果自负与本人无关。 中华人民共和国网络安全法&#xff08;2017 年 6 月 1 日起施行&#xff09; 第二十二条 任何个人和组织不得从事入侵他人网络、干扰他…

12 权重衰退【李沐动手学深度学习v2课程笔记】

1.权重衰退 使用均方范数来进行权重衰退 柔性限制 柔性限制对最优解的影响 参数更新法则 总结 2.代码实现 %matplotlib inline import torch from torch import nn from d2l import torch as d2l 生成数据 n_train, n_test, num_inputs, batch_size 20, 100, 200, 5 true_w, t…

42、网络编程/多点通信和域套接字通信模型20240304

一、多点通信之广播的收发端实现 1.广播发送端代码&#xff1a; #include<myhead.h>int main(int argc, const char *argv[]) {int sfdsocket(AF_INET,SOCK_DGRAM,0);//创建套接字if(sfd-1){perror("socket,error");return -1;}int broadcast1;//设置套接字广…

Java解决杨辉三角

Java解决杨辉三角 01 题目 给定一个非负整数 *numRows&#xff0c;*生成「杨辉三角」的前 numRows 行。 在「杨辉三角」中&#xff0c;每个数是它左上方和右上方的数的和。 示例 1: 输入: numRows 5 输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]]示例 2: 输入: numRo…

Python字符串中%d,%.f的用法

在字符串中&#xff0c;我们经常需要改变其中的一些几个字符&#xff0c;比如 第一个d%就是epoch1&#xff0c;会随着epoch的变化而变化。同理&#xff0c;后面的也是。打印出的结果就是 其中&#xff0c;epoch迭代了3次&#xff0c;从0开始。会发现 d%&#xff0c;是整数&…

【Python】外网远程登录访问jupyter notebook+pycharm使用ipython

第一步&#xff1a;创建python虚拟环境 conda create -n py3610 python3.6.10第二步&#xff1a;安装ipython pip install ipython pip install ipython notebook第三步&#xff1a;创建 IPython Notebook 服务器配置文件 # 进入python交互shell&#xff0c;设置密码 >&…