【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 存储器的功能,为嵌入式系统的开发提供了便利。

相关推荐
Kisorge1 小时前
【C语言】指针数组、数组指针、函数指针、指针函数、函数指针数组、回调函数
c语言·开发语言
森旺电子3 小时前
51单片机仿真摇号抽奖机源程序 12864液晶显示
单片机·嵌入式硬件·51单片机
爱吃西瓜的小菜鸡5 小时前
【C语言】判断回文
c语言·学习·算法
不过四级不改名6775 小时前
蓝桥杯嵌入式备赛教程(1、led,2、lcd,3、key)
stm32·嵌入式硬件·蓝桥杯
小A1595 小时前
STM32完全学习——SPI接口的FLASH(DMA模式)
stm32·嵌入式硬件·学习
Rorsion5 小时前
各种电机原理介绍
单片机·嵌入式硬件
FeboReigns7 小时前
C++简明教程(文章要求学过一点C语言)(1)
c语言·开发语言·c++
FeboReigns7 小时前
C++简明教程(文章要求学过一点C语言)(2)
c语言·开发语言·c++
_小柏_8 小时前
C/C++基础知识复习(43)
c语言·开发语言·c++
yoyobravery8 小时前
c语言大一期末复习
c语言·开发语言·算法