STM32 Cubemx配置SPI编程(使用Flash模块)

文章目录

  • 前言
  • 一、W25Q64模块介绍
  • 二、STM32Cubemx配置SPI
  • 三、SPI HAL库操作函数分析
    • 3.1查询方式
    • 3.2中断方式
  • 四、Flash时序分析
    • 1.读器件ID指令
    • 2.写使能
    • 3.擦除扇区
    • 4.页编程
    • 5.读数据
    • 6.读状态寄存器
  • 五、Flash驱动程序编写
    • 1.代码编写测试
  • 总结


前言

本篇文章来为大家讲解一下Flash模块的使用方法,Flash模块这里选择W25Q64模块。

一、W25Q64模块介绍

W25Q64 是一款由 Winbond 公司生产的串行闪存存储器,属于其 W25Q 系列产品。以下是关于 W25Q64 模块的一些基本信息:

存储容量:W25Q64 模块通常具有 64 Megabit(Mb)的存储容量,相当于 8 Megabyte(MB)。

接口:W25Q64 采用串行外围接口(SPI)进行通信。SPI 接口是一种通用的、简单的串行通信协议,通常用于与微控制器、传感器和其他外设进行通信。

工作电压:W25Q64 通常支持 2.7V 至 3.6V 的工作电压范围。

主要特点:

高速串行接口:W25Q64 支持高达 104 MHz 的串行时钟速率,使得数据读写速度快。

大容量存储:64 Mb 的存储容量足够存储大量的数据。

低功耗:W25Q64 在读取和待机模式下具有低功耗特性,适合于要求电源效率的应用场景。

扇区擦除:支持扇区擦除,使得对存储器的擦除操作更加灵活。

写保护:W25Q64 具有写保护功能,可保护存储器中的数据不受意外写入。

应用领域:W25Q64 可广泛应用于嵌入式系统中,如智能家居设备、工业控制系统、网络设备、汽车电子等领域。它常用于存储固件、配置文件、日志数据等。

支持软件:通常,W25Q64 模块可以与多种嵌入式系统和开发平台兼容,并且厂商通常会提供相应的驱动程序和示例代码,以便开发人员轻松地集成和使用该模块。

二、STM32Cubemx配置SPI

SPI Flash模块一共有6根线,接线方式如下:

VCC3.3V
GNDGND
SCKSPI_SCK
DOSPI_MISO
DISPI_MOSI

在这里插入图片描述
配置PB9为输出模式,作为片选引脚。
在这里插入图片描述

三、SPI HAL库操作函数分析

3.1查询方式

HAL_SPI_TransmitReceive():用于同时发送和接收数据。它的原型为:

HAL_StatusTypeDef HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size, uint32_t Timeout);

返回值:返回一个HAL_StatusTypeDef类型的枚举值,表示函数执行的状态。通常用HAL_OK表示成功,其他值表示错误。

参数:

hspi:SPI句柄,包含了SPI的配置信息和状态信息。
pTxData:指向要发送数据的缓冲区的指针。
pRxData:指向接收数据的缓冲区的指针。
Size:要发送/接收的数据字节数。
Timeout:超时时间,以毫秒为单位。如果超过这个时间函数还没有完成,则返回超时错误。

发送数据和接收数据过程:
在这里插入图片描述
代码分析:

while ((hspi->TxXferCount > 0U) || (hspi->RxXferCount > 0U)): 这是一个 while 循环,其条件是直到所有的数据都传输完成,即直到 TxXferCount 和 RxXferCount 均为零时才退出循环。TxXferCount 表示待发送的数据数量,RxXferCount 表示待接收的数据数量。

if ((__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_TXE)) && (hspi->TxXferCount > 0U) && (txallowed == 1U)): 这是一个条件判断语句,检查是否满足以下条件:SPI 发送缓冲区为空 (SPI_FLAG_TXE)、仍有待发送的数据 (TxXferCount > 0U),并且允许发送数据 (txallowed == 1U)。如果条件成立,则将待发送数据写入到 SPI 数据寄存器 (DR) 中,并更新相关的变量,表示已发送了一个字节的数据。

txallowed = 0U;: 将 txallowed 置为 0,表示下一个操作将是接收操作,而不是发送操作。

if ((__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_RXNE)) && (hspi->RxXferCount > 0U)): 这是另一个条件判断语句,检查是否满足以下条件:SPI 接收缓冲区非空 (SPI_FLAG_RXNE),并且仍有待接收的数据 (RxXferCount > 0U)。如果条件成立,则从 SPI 数据寄存器 (DR) 中读取接收到的数据,并将其存储到接收缓冲区中。

txallowed = 1U;: 将 txallowed 置为 1,表示下一个操作将是发送操作,允许发送数据。

if ((((HAL_GetTick() - tickstart) >= Timeout) && ((Timeout != HAL_MAX_DELAY))) || (Timeout == 0U)): 这个条件判断语句用于检查是否超时。它检查了两个条件:一是从开始传输到当前时刻的时间是否超过了设定的超时时间 Timeout,并且 Timeout 不是设置为无限等待;二是如果 Timeout 被设置为 0,表示立即超时。如果超时条件满足,则会将错误码置为超时错误,并跳转到错误处理的标签 error。

HAL_SPI_Transmit():用于只发送数据而不接收。它的原型为:

HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout);

返回值:同样返回HAL_StatusTypeDef类型的枚举值,表示函数执行的状态。

参数:

hspi:SPI句柄,包含了SPI的配置信息和状态信息。
pData:指向要发送数据的缓冲区的指针。
Size:要发送的数据字节数。
Timeout:超时时间,以毫秒为单位。

在这里插入图片描述

HAL_SPI_Receive():用于只接收数据而不发送。它的原型为:

HAL_StatusTypeDef HAL_SPI_Receive(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout);

返回值:同样返回HAL_StatusTypeDef类型的枚举值,表示函数执行的状态。

参数:

hspi:SPI句柄,包含了SPI的配置信息和状态信息。
pData:指向接收数据的缓冲区的指针。
Size:要接收的数据字节数。
Timeout:超时时间,以毫秒为单位。

在这里插入图片描述

3.2中断方式

中断方式这里就只分析下面一个函数;

HAL_StatusTypeDef HAL_SPI_TransmitReceive_IT(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size)

参数:

hspi:指向 SPI 外设句柄的指针,其中包含了 SPI 外设的配置和状态信息。
pTxData:指向要发送数据的缓冲区的指针。
pRxData:指向用于接收数据的缓冲区的指针。
Size:要发送/接收的数据的数量。

函数的返回值是一个 HAL_StatusTypeDef 类型的枚举,表示函数执行的状态,可能的返回值包括 HAL_OK(成功)和各种错误代码,如 HAL_ERROR、HAL_BUSY 等。

这个函数的作用是在 SPI 通信中以中断的方式进行数据的传输和接收。它允许将发送和接收的数据分别存储在两个不同的缓冲区中,因此可以同时进行数据的发送和接收。

这个函数主要就是将SPI中断打开并且设置中断服务程序代码。
在这里插入图片描述
具体中断的调用流程:
在这里插入图片描述
当发生中断调用设置的SPI_2linesRxISR_8BIT和SPI_2linesTxISR_8BIT两个函数。

在这里插入图片描述

在这两个函数的内部会判断是否有数据可以接收或者是发送,当数据接收和发送都完成后会关闭中断。
在这里插入图片描述

在这里插入图片描述
在SPI_CloseRxTx_ISR函数中会调用到HAL_SPI_TxRxCpltCallback回调函数。
在这里插入图片描述

四、Flash时序分析

这里我们主要会实现四个提供给外部可以使用到的函数分别是:

在这里插入图片描述

1.读器件ID指令

读器件ID的时候首先需要先将片选引脚拉低,然后再写入9Fh指令,然后就能够读取到制造ID,存储器ID,兼容性ID。
在这里插入图片描述
在发送完9Fh指令后还需要再发送一个指令(可以随意),因为在DO数据线上第一个数据是没有意义的数据,又因为SPI通信发送多少个数据就会接收多少个数据,所以在这里需要再次发送一个数据出去,才能读取到器件的ID。
在这里插入图片描述

2.写使能

在进行页编程,扇区擦除,块区擦除,芯片擦除和写状态寄存器命令之前都需要进行写使能。

写使能的操作也不是特别难,首先拉低CS引脚,然后写入06h,再拉高CS引脚。
在这里插入图片描述

3.擦除扇区

在Flash模块中一个扇区的大小是4K字节。

在进行扇区擦除的时候需要先进行写使能,然后拉低CS引脚,再写入20h和24位的地址,擦除完成后BUSY位会变为0。
在这里插入图片描述

4.页编程

在进行页编程的时候,需要先进行写使能,然后把CS拉低,再写入02h指令,再把24位的地址写入,写完数据后把CS拉高,最后等待写入完成。
在这里插入图片描述
在这里插入图片描述

5.读数据

在读数据的时候首先把CS引脚拉低,然后写入03h指令,然后再写入要读取的24位地址,然后就可以读取到对应的数据,只要时钟线不停止就可以把整个芯片存储区的数据读取出来,最后拉低CS引脚。
在这里插入图片描述
在这里插入图片描述

6.读状态寄存器

在读状态寄存器时先将CS拉低,然后写入05h指令,当识别到是05h指令的时候芯片就会把状态寄存器的值输出,这个时候就可以读取到状态寄存器的值。
在这里插入图片描述
在这里插入图片描述
首先读取到的是BUSY位。
在这里插入图片描述

五、Flash驱动程序编写

1.代码编写测试

创建两个文件spiflash.c和spiflash.h用来管理SPI Flash的驱动程序:
spiflash.h

#ifndef __SPIFLASH_H__
#define __SPIFLASH_H__#include "main.h"int SPIFlash_ReadID(void);int SPIFlash_EraseSector(uint32_t addr);int SPIFlash_Write(uint16_t addr, uint8_t* datas, uint16_t len);int SPIFlash_Read(uint16_t addr, uint8_t* datas, uint16_t len);#endif

spiflash.c

#include "spiflash.h"#define TIMEOUT			1000//超时时间extern SPI_HandleTypeDef hspi1;extern void WaitTxCplt(int timeout);
extern void WaitTxRxCplt(int timeout);
extern void WaitRxCplt(int timeout);//选择从机拉低CS
static void CSEnable(void)
{HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_RESET);
}//拉高CS结束通信过程
static void CSDisable(void)
{HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_SET);
}//写使能
static void WriteEnable(void)
{uint8_t data = 0x06;CSEnable();//拉低CS引脚,选中设备HAL_SPI_Transmit_IT(&hspi1, &data, 1);WaitTxCplt(TIMEOUT);//等待发送完成数据CSDisable();//拉高CS引脚,失能设备
}//读状态寄存器
static int ReadStatusReg(void)
{uint8_t TxBuf[2] = {0x05, 0xff};//发送数据数组uint8_t RxBuf[2] = {0, 0};//接收数据数组CSEnable();//拉低CS引脚,选中设备HAL_SPI_TransmitReceive_IT(&hspi1, TxBuf, RxBuf, 2);WaitTxRxCplt(TIMEOUT);//等待发送接收完成数据CSDisable();//拉高CS引脚,失能设备return RxBuf[1];
}//等待Flash模块就绪
static int SPIFlash_WaitReady(void)
{while (ReadStatusReg() & 1 == 1);
}//读器件ID
int SPIFlash_ReadID(void)
{uint8_t TxBuf[2] = {0x9F, 0x00};//发送数据数组uint8_t RxBuf[2] = {0, 0};//接收数据数组CSEnable();//拉低CS引脚,选中设备HAL_SPI_TransmitReceive_IT(&hspi1, TxBuf, RxBuf, 2);WaitTxRxCplt(TIMEOUT);//等待发送接收完成数据CSDisable();//拉高CS引脚,失能设备return RxBuf[1];
}//擦除扇区
int SPIFlash_EraseSector(uint32_t addr)
{uint8_t TxBuf[4] = {0x20};//得到24bit地址TxBuf[1] = (addr >> 16) & 0xff;TxBuf[2] = (addr >> 8) & 0xff;TxBuf[3] = (addr >> 0) & 0xff;//写使能WriteEnable();CSEnable();//拉低CS引脚,选中设备HAL_SPI_Transmit_IT(&hspi1, TxBuf, 4);WaitTxCplt(TIMEOUT);//等待发送完成数据CSDisable();//拉高CS引脚,失能设备//等待就绪SPIFlash_WaitReady();return 0;
}//写数据
int SPIFlash_Write(uint16_t addr, uint8_t* datas, uint16_t len)
{uint8_t TxBuf[4] = {0x02};//得到24bit地址TxBuf[1] = (addr >> 16) & 0xff;TxBuf[2] = (addr >> 8) & 0xff;TxBuf[3] = (addr >> 0) & 0xff;//写使能WriteEnable();CSEnable();//拉低CS引脚,选中设备//写命令和写地址HAL_SPI_Transmit_IT(&hspi1, TxBuf, 4);WaitTxCplt(TIMEOUT);//等待发送完成数据//写数据HAL_SPI_Transmit_IT(&hspi1, datas, len);WaitTxCplt(TIMEOUT);//等待发送完成数据CSDisable();//拉高CS引脚,失能设备//等待就绪SPIFlash_WaitReady();return 0;
}//读数据
int SPIFlash_Read(uint16_t addr, uint8_t* datas, uint16_t len)
{uint8_t TxBuf[4] = {0x03};//得到24bit地址TxBuf[1] = (addr >> 16) & 0xff;TxBuf[2] = (addr >> 8) & 0xff;TxBuf[3] = (addr >> 0) & 0xff;CSEnable();//拉低CS引脚,选中设备//写命令和写地址HAL_SPI_Transmit_IT(&hspi1, TxBuf, 4);WaitTxCplt(TIMEOUT);//等待发送完成数据//读数据HAL_SPI_Receive_IT(&hspi1, datas, len);WaitRxCplt(TIMEOUT);//等待读取数据完成CSDisable();//拉高CS引脚,失能设备return 0;
}

这里会使用到中断的方式来处理发送和接收数据完成:

//标志位用于标志是否发送和接收完成数据
static volatile int g_spi1_tx_complete = 0;
static volatile int g_spi1_rx_complete = 0;
static volatile int g_spi1_txrx_complete = 0;//发送数据完成中断
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
{if (hspi == &hspi1){g_spi1_tx_complete = 1;//接收到数据后标志位置为1}
}//等待发送数据完成
void WaitTxCplt(int timeout)
{while (g_spi1_tx_complete == 0 && timeout--){HAL_Delay(1);}g_spi1_tx_complete = 0;
}//接收数据完成中断回调函数
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)
{if (hspi == &hspi1){g_spi1_rx_complete = 1;//发送完成数据后标志位置为1}
}//等待接收数据完成
void WaitRxCplt(int timeout)
{while (g_spi1_rx_complete == 0 && timeout--){HAL_Delay(1);}g_spi1_rx_complete = 0;
}//发送接收数据完成中断回调函数
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{if (hspi == &hspi1){g_spi1_txrx_complete = 1;//发送接收完成数据后标志位置为1}
}//等待发送接收数据完成
void WaitTxRxCplt(int timeout)
{while (g_spi1_txrx_complete == 0 && timeout--){HAL_Delay(1);}g_spi1_txrx_complete = 0;
}

总结

本篇文章主要讲解了STM32Cubemx配置SPI的流程以及Flash驱动程序的编写。

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

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

相关文章

安装极狐GitLab Runner并测试使用

本文继【新版极狐安装配置详细版】之后继续 1. 添加官方极狐GitLab 仓库: 对于 RHEL/CentOS/Fedora: curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.rpm.sh" | sudo bash2. 安装最新版本的极狐G…

STM32 4位数码管和74HC595

4位数码管 在使用一位数码管的时候,会用到8个IO口,那如果使用4位数码管,难道要使用32个IO口吗?肯定是不行的,太浪费了IO口了。把四个数码管全部接一起共用8个IO口,然后分别给他们一个片选。所以4位数码管共…

GO语言基础总结

多态: 定义一个父类的指针(接口),然后把指针指向子类的实例,再调用这个父类的指针,然后子类的方法被调用了,这就是多态现象。 Golang 高阶 goroutine 。。。。。 channel channel的定义 …

LeetCode59. 螺旋矩阵 II(C++)

LeetCode59. 螺旋矩阵 II 题目链接代码 题目链接 https://leetcode.cn/problems/spiral-matrix-ii/ 代码 class Solution { public:vector<vector<int>> generateMatrix(int n) {vector<vector<int>> res(n, vector<int>(n, 0));int startx …

用 Python 自动化处理无聊的事情

“编程最棒的部分就是看到机器做一些有用的事情而获得的胜利。用 Python 将无聊的事情自动化将所有编程视为这些小小的胜利&#xff1b;它让无聊变得有趣。” Hilary Mason&#xff0c;数据科学家兼 Fast Forward Labs 创始人 “我很享受打破东西然后把它们重新组合起来的乐趣…

Vue+SpringBoot打造音乐偏好度推荐系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、系统设计2.1 功能模块设计2.1.1 音乐档案模块2.1.2 我的喜好模块2.1.3 每日推荐模块2.1.4 通知公告模块 2.2 用例图设计2.3 实体类设计2.4 数据库设计 三、系统展示3.1 登录注册3.2 音乐档案模块3.3 音乐每日推荐模块3.4 通知公告模…

【python】网络爬虫与信息提取--scrapy爬虫框架介绍

一、scrapy爬虫框架介绍 scrapy是一个功能强大的网络爬虫框架&#xff0c;是python非常优秀的第三方库&#xff0c;也是基于python实现网络爬虫的重要技术路线。scrapy不是哟个函数功能库&#xff0c;而是一个爬虫框架。 爬虫框架&#xff1a;是实现爬虫功能的一个软件结构和功…

数据价值在线化丨TiDB 在企查查数据中台的应用及 v7.1 版本升级体验

本文介绍了企查查在数据中台建设中使用 TiDB 的经验和应用。通过从 MySQL 到 TiDB 的迁移&#xff0c;企查查构建了基于 TiDB Flink 的实时数仓框架 &#xff0c;充分利用了 TiDB 的分布式架构、MySQL 兼容性和完善的周边工具等特性&#xff0c;实现了数据的在线化处理。2023 年…

搭建 LNMP 架构

一 理论知识 &#xff08;一&#xff09;架构图 &#xff08;二&#xff09;CGI 由来 最早的Web服务器只能简单她响应浏览器发来的HTTP请求&#xff0c;并将存储在服务器上的HTML文件返回给浏览器&#xff0c;也就是静态html文件&#xff0c;但是后期随着网站功能增多网站开…

k8s service的概念以及创建方法

Service 的功能&#xff1a; Service主要用于提供网络服务&#xff0c;通过Service的定义&#xff0c;能够为客户端应用提供稳定的访问地址&#xff08;域名或IP地址&#xff09;和负载均衡功能&#xff0c;以及屏蔽后端Endpoint的变化&#xff0c;是K8s实现微服务的核心资源。…

java面试说自己的优势,2022必看

纯手打“RocketMQ笔记” 第一节&#xff1a;RocketMQ介绍 1.1 核心概念&#xff08;主题、生产者、消费者、消息&#xff09; 1.2 RocketMQ的设计理念和目标&#xff08;设计理念、设计目标&#xff09; 第二节&#xff1a;RocketMQ中消息的发送 2.1 单向[OneWay]发送&#…

深度学习 精选笔记(5)多层感知机

学习参考&#xff1a; 动手学深度学习2.0Deep-Learning-with-TensorFlow-bookpytorchlightning ①如有冒犯、请联系侵删。 ②已写完的笔记文章会不定时一直修订修改(删、改、增)&#xff0c;以达到集多方教程的精华于一文的目的。 ③非常推荐上面&#xff08;学习参考&#x…

加密与安全_探索常用编码算法

文章目录 概述什么是编码编码分类ASCII码 &#xff08;最多只能有128个字符&#xff09;Unicode &#xff08;用于表示世界上几乎所有的文字和符号&#xff09;URL编码 &#xff08;解决服务器只能识别ASCII字符的问题&#xff09;实现&#xff1a;编码_URLEncoder实现&#xf…

【大数据架构(1)】Lambda Architecture – Realtime Data Processing 论文重点翻译

文章目录 1. INTRODUCTION2. LAMBDA ARCHITECTUREA) BATCH LAYERB) SPEED LAYERC) SERVICE LAYER 3. LIMITATIONS OF THE TRADITIONAL LAMBDAARCHITECTURE4. A PROPOSED SOLUTION1. 架构说明2. 前后架构改进对比 1. INTRODUCTION Lambda架构背后的需求是由于虽然MR能够处理大数…

Gitflow:一种依据 Git 构建的分支管理工作流程模式

文章目录 前言Gitflow 背景Gitflow 中的分支模型Gitflow 的版本号管理简单模拟 Gitflow 工作流 前言 Gitflow 工作流是一种版本控制流程&#xff0c;主要适用于较大规模的团队。这个流程在团队中进行合作时可以避免冲突&#xff0c;并能快速地完成项目&#xff0c;因此在很多软…

Android res/values/locale_config.xml文件

Android res/values/locale_config.xml文件 各个国家/地区在android系统里面的缩写代码。最典型的用途是本地化。 <?xml version"1.0" encoding"utf-8"?> <!-- Copyright (C) 2015 The Android Open Source ProjectLicensed under the Apache L…

How to implement multiple file uploads based on Swagger 3.x in Spring boot 3.x

How to implement multiple file uploads based on Swagger 3.x in Spring boot 3.x Projectpom.xmlOpenAPIConfigFileUploadControllerapplication.yaml Project pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://…

安全防御-第六次

内容安全 攻击可能只是一个点&#xff0c;防御需要全方面进行 DFI和DPI技术--- 深度检测技术 DPI --- 深度包检测技术--- 主要针对完整的数据包&#xff08;数据包分片&#xff0c;分段需要重组&#xff09;&#xff0c;之后对数据包的内容进行识别。&#xff08;应用层&…

ROS 2基础概念#1:计算图(Compute Graph)| ROS 2学习笔记

在ROS中&#xff0c;计算图&#xff08;ROS Compute Graph&#xff09;是一个核心概念&#xff0c;它描述了ROS节点之间的数据流动和通信方式。它不仅仅是一个通信网络&#xff0c;它也反映了ROS设计哲学的核心——灵活性、模块化和可重用性。通过细致探讨计算图的高级特性和实…

使用 JMeter 生成测试数据对 MySQL 进行压力测试

博主历时三年精心创作的《大数据平台架构与原型实现&#xff1a;数据中台建设实战》一书现已由知名IT图书品牌电子工业出版社博文视点出版发行&#xff0c;点击《重磅推荐&#xff1a;建大数据平台太难了&#xff01;给我发个工程原型吧&#xff01;》了解图书详情&#xff0c;…