【STM32 CubeMX】SPI W25Q64功能实现

文章目录

  • 前言
  • 一、内部函数的实现
    • [1.1 选中和取消选中SPI Flash](#1.1 选中和取消选中SPI Flash)
    • [1.2 写使能函数](#1.2 写使能函数)
    • [1.3 获取读状态](#1.3 获取读状态)
    • [1.4 等待就绪状态](#1.4 等待就绪状态)
  • 二、Flash读写函数实现
    • [2.1 读Flash ID](#2.1 读Flash ID)
    • [2.2 擦除某个扇区](#2.2 擦除某个扇区)
    • [2.3 写扇区](#2.3 写扇区)
    • [2.4 读数据](#2.4 读数据)
  • 三、测试代码
  • 总结

前言

SPI Flash 存储器在嵌入式系统中扮演着重要角色,它可以为微控制器提供额外的存储空间,并且具有快速的读写速度和较大的存储容量。W25Q64 是一款常见的 SPI Flash 存储器,容量为64Mb,采用 SPI 接口进行通信。在 STM32 微控制器上实现对 W25Q64 的功能使用,可以通过 STM32 CubeMX 和相关的库函数轻松完成。本文将介绍如何利用 STM32 CubeMX 和 SPI 库来实现对 W25Q64 的基本功能。


一、内部函数的实现

1.1 选中和取消选中SPI Flash

当CS引脚为GPIO_PIN_RESET为选中该设备,当CS引脚为GPIO_PIN_SET表示取消选中该设备,所以这个函数非常好实现,只需要使用HAL_GPIO_WritePin函数进行写pin即可

c 复制代码
static void SPIFlash_Select(void)
{
	HAL_GPIO_WritePin(GPIOC,GPIO_PIN_4,GPIO_PIN_RESET);
}

static void SPIFlash_DeSelect(void)
{
	HAL_GPIO_WritePin(GPIOC,GPIO_PIN_4,GPIO_PIN_SET);
}

1.2 写使能函数

如果你需要写使能,你只需要发送命令0x06即可,你可以使用查询方式来发送,也可以使用中断函数来发送。

在发送命令的时候,你需要选中该Flash设备,当你写完命令后,需要取消选中Flash设备。

我们可以这样实现他:

c 复制代码
static volatile int g_spi1_tx_complete = 0;
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
{
	if(hspi == &hspi1)
	{
		g_spi1_tx_complete = 1;
	}
}

void Wait_SPI_TxCplt(int timeout)
{
	while(g_spi1_tx_complete == 0 && timeout--)
	{
		HAL_Delay(1);
	}
	g_spi1_tx_complete = 0;
}

static int SPIFlash_WriteEnable(void)
{
	uint8_t buf[1] = {0x06};
	SPIFlash_Select();
	HAL_SPI_Transmit_IT(&hspi1,buf,1);
	Wait_SPI_TxCplt(SPI_FLASH_TIMEOUT);
	SPIFlash_DeSelect();
}

1.3 获取读状态

当我们擦除扇区或者写扇区的时候,他并不是发送数据完成,这个数据就写进去的,我们需要等待他内部编程完。我们可以使用命令,把里面的状态拿出来

在这里,我们需要发送2个命令,但是有用的只有命令1(发送的命令为0x05),因为发送了2个命令,所以我们需要接收两个数据,但是有用的只有接收到的数据2。

c 复制代码
static volatile int g_spi1_txrx_complete = 0;
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
	if(hspi == &hspi1)
	{
		g_spi1_txrx_complete = 1;
	}
}

void Wait_SPI_TxRxCplt(int timeout)
{
	while(g_spi1_txrx_complete == 0 && timeout--)
	{
		HAL_Delay(1);
	}
	g_spi1_txrx_complete = 0;
}

static int SPIFlash_ReadStatus(void)
{
		uint8_t txbuf[2] = {0x05,0xff};
		uint8_t rxbuf[2] = {0,0};
		SPIFlash_Select();
		HAL_SPI_TransmitReceive_IT(&hspi1, txbuf, rxbuf, 2);
		Wait_SPI_TxRxCplt(SPI_FLASH_TIMEOUT);
		SPIFlash_DeSelect();
		
		return rxbuf[1];
}

1.4 等待就绪状态

所谓的等待就绪状态其实就是当SPIFlash_ReadStatus函数&上1他还是等于1,就代表擦除扇区或者写扇区已经完成了

c 复制代码
static int SPIFlash_WaitReady(void)
{
	while(SPIFlash_ReadStatus() & 1 == 1);
}

二、Flash读写函数实现

2.1 读Flash ID

读Flash ID可以让我们知道这个SPI Flash是否有用

和前面的获取读状态一样。在这里,我们需要发送2个命令,但是有用的只有命令1(发送的命令为0x9F),因为发送了2个命令,所以我们需要接收两个数据,但是有用的只有接收到的数据2。

c 复制代码
int SPIFlash_ReadID(void)
{
	uint8_t txbuf[2] = {0x9F,0xff};
	uint8_t rxbuf[2] = {0,0};
	
	SPIFlash_Select();
	HAL_SPI_TransmitReceive_IT(&hspi1, txbuf, rxbuf, 2);
	Wait_SPI_TxRxCplt(SPI_FLASH_TIMEOUT);
	SPIFlash_DeSelect();
	
	return rxbuf[1];
}

当rxbuf[1]的值为0xEF时,代码这个SPI Flash没有问题

2.2 擦除某个扇区

首先,我们要发送命令加上3个字节的地址,所以我们需要4字节的buf

如果你想擦除某个扇区,你需要使用0x20命令。

接下来,我们往buf的后3字节填充地址,地址是先发高位再发低位的。

再完成发送之后,我们还需要等待就绪,即调用SPIFlash_WaitReady函数

c 复制代码
int SPIFlash_EraseSector(uint32_t addr)
{
	SPIFlash_WriteEnable();
	
	uint8_t txbuf[4] = {0x20};
	
	txbuf[1] = addr>>16 & 0xff;
	txbuf[2] = addr>>8 & 0xff;
	txbuf[3] = addr>>0 & 0xff;
	
	SPIFlash_Select();
	HAL_SPI_Transmit_IT(&hspi1,txbuf,4);
	Wait_SPI_TxCplt(SPI_FLASH_TIMEOUT);
	SPIFlash_DeSelect();
	
	SPIFlash_WaitReady();
	
	return 0;
}

2.3 写扇区

写操作在发送命令+地址的和我们的擦除某个扇区的是一样的,只不过我们的写扇区的命令为0x02

再发送完命令+地址之后,我们就可以直接调用HAL库的发送函数进行datas的发送即可。

c 复制代码
int SPIFlash_Write(uint32_t addr,uint8_t *datas,uint32_t len)
{
	SPIFlash_WriteEnable();
	
	uint8_t txbuf[4] = {0x02};
	
	txbuf[1] = addr>>16 & 0xff;
	txbuf[2] = addr>>8 & 0xff;
	txbuf[3] = addr>>0 & 0xff;
	
	SPIFlash_Select();
	HAL_SPI_Transmit_IT(&hspi1,txbuf,4);
	Wait_SPI_TxCplt(SPI_FLASH_TIMEOUT);
	
	HAL_SPI_Transmit_IT(&hspi1,datas,len);
	Wait_SPI_TxCplt(SPI_FLASH_TIMEOUT);
	
	SPIFlash_DeSelect();
	
	SPIFlash_WaitReady();
	
	return 0;
}

2.4 读数据

读操作在发送命令+地址的和我们的擦除某个扇区的是一样的,只不过我们的写扇区的命令为0x03

在写完上面这些数据之后,我们需要等待写完,接下来我们就可以去调用HAL库的读SPI函数了

c 复制代码
static volatile int g_spi1_rx_complete = 0;

void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)
{
	if(hspi == &hspi1)
	{
		g_spi1_rx_complete = 1;
	}
}

void Wait_SPI_RxCplt(int timeout)
{
	while(g_spi1_rx_complete == 0 && timeout--)
	{
		HAL_Delay(1);
	}
	g_spi1_rx_complete = 0;
}

int SPIFlash_Read(uint32_t addr,uint8_t *datas,uint32_t len)
{
	uint8_t txbuf[4] = {0x03};
	
	txbuf[1] = addr>>16 & 0xff;
	txbuf[2] = addr>>8 & 0xff;
	txbuf[3] = addr>>0 & 0xff;
	
	SPIFlash_Select();
	HAL_SPI_Transmit_IT(&hspi1,txbuf,4);
	Wait_SPI_TxCplt(SPI_FLASH_TIMEOUT);
	
	HAL_SPI_Receive_IT(&hspi1,datas,len);
	Wait_SPI_RxCplt(SPI_FLASH_TIMEOUT);
	
	SPIFlash_DeSelect();
	
	return 0;
}

三、测试代码

c 复制代码
char *str = "www.csdn.net\r\n";
	
r = SPIFlash_ReadID();
SPIFlash_EraseSector(4096);
SPIFlash_Write(4096,(uint8_t*)str,strlen(str)+1);
SPIFlash_Read(4096,(uint8_t*)flash_buf,20);

总结

通过本文的介绍,我们了解了如何在 STM32 CubeMX 中配置并利用 SPI 库来实现对 W25Q64 SPI Flash 存储器的功能。首先,我们通过 CubeMX 配置了 STM32 的 SPI 外设,包括时钟分频、数据大小、模式等参数。然后,我们编写了初始化代码,将 SPI 外设与 W25Q64 进行连接,并实现了基本的读写功能。在编写代码时,我们充分利用了 STM32 的 HAL 库提供的函数,简化了通信过程的实现。最后,我们在主函数中调用了相应的读写函数,并通过调试工具验证了功能的正确性。通过本文的学习,读者可以掌握在 STM32 微控制器上使用 CubeMX 和 SPI 库来实现对 W25Q64 SPI Flash 存储器的功能,为嵌入式系统的开发提供了便利。

相关推荐
阿容1234562 小时前
stm32两轮平衡小车 -04
stm32·嵌入式硬件
chengpei1473 小时前
I²C协议简介
c语言·开发语言
silno5 小时前
图解 STM32 USB CDC虚拟串口 的实现
stm32·单片机·stm32f103c8t6·cdc虚拟串口
say_fall5 小时前
C语言编程实战:每日一题:随机链表的复制
c语言·开发语言·链表
Silicore_Emma5 小时前
芯谷科技—D8227 双通道音频功率放大集成电路产品简介与应用推广
单片机·音视频·功率放大器·芯谷科技·便携式音频设备·双通道音频·车载音频系统
唐·柯里昂7986 小时前
野火鲁班猫5使用正点原子 RTL8188EUS Wifi模块驱动移植(Linux5.10 Debian系统) 解决zsh报错
linux·c语言·mcu·物联网·ubuntu·硬件工程·软件构建
魂梦翩跹如雨6 小时前
P8615 [蓝桥杯 2014 国 C] 拼接平方数——Java解答
java·c语言·蓝桥杯
Darken037 小时前
单片机的库函数和HAL库有什么区别?还有那些库函数?
单片机·hal库·ai学习
皓月盈江7 小时前
STC12、STC15、STM32系列单片机控制16*64LED点阵屏显示,修改显示内容
单片机·嵌入式硬件·keil·stm32f103c8t6·stc12c5a60s2·stc15w4k32s4·led点阵屏程序源码
[J] 一坚7 小时前
华为OD、微软、Google、神州数码、腾讯、中兴、网易有道C/C++字符串、数组、链表、树等笔试真题精粹
c语言·数据结构·c++·算法·链表