SPI专题(二)——STM32驱动FLASH(W25Q64)

article/2025/8/22 1:16:37

前言:

为了方便查看博客,特意申请了一个公众号,附上二维码,有兴趣的朋友可以关注,和我一起讨论学习,一起享受技术,一起成长。

在这里插入图片描述



github:my github


注:博客所涉及的关于 stm32 的代码,均在仓库【stm32f013_study】下,包括底层驱动和应用测试代码。
本文设计的文件包含:
(1)hardware_spi.c:硬件 SPI 驱动实现
(2)drvsfspi.c:软件模拟 SPI 实现代码
(3)drvexflash.c:SPI FLASH 操作部分代码
(4)hal_spi.c:SPI 软件、硬件方式封装统一接口
(5)头文件:
hardware_spi.h :硬件 SPI 相关
drvsfspi.h :软件模拟 SPI 相关
drvexflash.hSPI FLASH 相关
hal_spi.h:软件、硬件 SPI 接口封装


1. 硬件连接

W25Q64 将 8M 的容量分为 128 个块(Block),每个块大小为 64K 字节,每个块又分为 16个扇区(Sector),每个扇区 4K 个字节

W25Q64 的**最少擦除单位为一个扇区,也就是每次必须擦除 4K 个字节。**操作需要给 W25Q64 开辟一个至少 4K 的缓存区,对 SRAM 要求比较高,要求芯片必须有 4K 以上 SRAM 才能很好的操作。

这里写图片描述

W25Q64 的擦写周期多达 10W 次,具有 20 年的数据保存期限,支持电压为 2.7~3.6V,W25Q64 支持标准的 SPI,还支持双输出/四输出的 SPI,最大 SPI 时钟可以到 80Mhz(双输出时相当于 160Mhz,四输出时相当于 320M)。

1.1 硬件连接

与 STM32 的引脚连接如下:这里是使用SPI1配置。

这里写图片描述

STM32引脚对应SPI功能
PA2片选CS
PA5时钟SCK
PA6MISO
PA7MOSI

STM32 的 SPI 功能很强大, SPI 时钟最多可以到 18Mhz,支持 DMA,可以配置为 SPI 协议或者 I2S 协议(仅大容量型号支持)。

1.2 SPI 通讯的通讯时序

SPI 协议定义了通讯的起始和停止信号、数据有效性、时钟同步等环节。

我们以读取 FLASH 的状态寄存器的时序图分析一下,时序图也是书写软件模拟时序的依据。

在这里插入图片描述
如上图,我们知道书写 FLASH (来自华邦 W25X 手册)支持的是模式 0 (CPOL = 0 && CPHA == 0) 和 模式 3(CPOL = 1 && CPHA == 1)

CS、SCK、MOSI 信号都由主机控制产生,而 MISO 的信号由从机产生,主机通过该信号线读取从机的数据。MOSI 与 MISO 的信号只在 CS 为低电平的时候才有效,在 SCK 的每个时钟周期 MOSI 和 MISO 传输一位数据。

1.2.1. 通讯的起始和停止信号

在上图,CS 信号线由高变低,为 SPI 通讯的起始信号。CS 是每个从机各自独占的信号线,当从机在自己的 CS 线检测到起始信号后,就知道自己被主机选中了,开始准备与主机通讯。当 CS 信号由低变高,为 SPI 通讯的停止信号,表示本次通讯结束,从机的选中状态被取消。

1.2.2. 数据有效性

SPI 使用 MOSI 及 MISO 信号线来传输数据,使用 SCK 信号线进行数据同步。

在这里插入图片描述
MOSI 及 MISO 数据线在 SCK 的每个时钟周期传输一位数据。数据传输时,MSB 先行或 LSB 先行并没有作硬性规定,但要保证两个 SPI 通讯设备之间使用同样的协定,一般都会采用图中的 MSB 先行模式。

观察上图,可知模式 0 和 3 都是在上升沿读取数据。

示例: FLASH 读取 JEDEC_ID (0x9F),SPI 模式 0,,频率 f = 1MHz。

在这里插入图片描述
读取 JEDEC_ID 时,FLASH 回复的一个字节:0xC8。

在这里插入图片描述

1.2.3 STM32 SPI外设

STM32 的 SPI 外设可用作通讯的主机及从机,支持最高的 SCK 时钟频率为 f pclk / 2 (STM32F103 型号的芯片默认 f pclk1 为 72MHz,f pclk2 为 36MHz),完全支持 SPI 协议的 4 种模式,数据帧长度可设置为 8 位或 16 位,可设置数据 MSB 先行或 LSB 先行。它还支持双线全双工、双线单向以及单线模式。

SPI架构:

这里写图片描述

通讯引脚 :

SPI 的所有硬件架构都从上图中左 MOSI、MISO、SCK及 NSS 线展开的。

STM32 芯片有多个 SPI 外设,它们的 SPI 通讯信号引出到不同的 GPIO 引脚上,使用时必须配置到这些指定的引脚。

2. 软件配置

这里使用 STM32 的 SPI1 的主模式,SPI 相关的库函数和定义分布在文件 stm32f10x_spi.c 以及头文件 stm32f10x_spi.h 中。

2.1 配置相关引脚的复用功能

第一步就要使能 SPI1 的时钟, SPI1 的时钟通过 APB2ENR 的第 12 位来设置。其次要设置 SPI1 的相关引脚为复用输出,这样才会连接到 SPI1 上否则这些 IO 口还是默认的状态,也就是标准输入输出口。这里我们使用的是 PA5、 PA6、 PA7 这 3 个(SCK、 MISO、 MOSI、CS 使用软件管理方式),所以设置这三个为复用 IO

宏定义:

#define SPIM1_GPIO_PORT	    GPIOA#define SPIM1_CLK_IO	(GPIO_Pin_5)
#define SPIM1_MISO_IO	(GPIO_Pin_6)
#define SPIM1_MOSI_IO	(GPIO_Pin_7)#define FLASH_CS_IO     		(GPIO_Pin_2)
#define FLASH_CS_0()			(GPIO_ResetBits(SPIM1_GPIO_PORT, FLASH_CS_IO))		
#define FLASH_CS_1() 			(GPIO_SetBits(SPIM1_GPIO_PORT, FLASH_CS_IO))#define RCC_PCLK_SPIM1_GPIO     RCC_APB2Periph_GPIOA
#define RCC_PCLK_SPIM1_HD       RCC_APB2Periph_SPI1

IO 配置:

//--------------------------------------------------------------------------------------------------------
//	函 数 名: spi_gpio_init
//	功能说明: SPI 硬件IO初始化
//	形    参: 	spi_chl:SPIM 通道
//	返 回 值: 无
//	日    期: 2020-03-12
//  备    注:采用 Unix like 方式
//	作    者: by 霁风AI
//--------------------------------------------------------------------------------------------------------
void spi_gpio_init(uint8_t spi_chl)
{GPIO_InitTypeDef gpio_config_init;if (spi_chl == 1){RCC_APB2PeriphClockCmd(RCC_PCLK_SPIM1_GPIO, ENABLE);		//开启SPIM1 GPIO时钟、//		gpio_config_init.GPIO_Pin 		= SPIM1_CLK_IO | SPIM1_MISO_IO | SPIM1_MOSI_IO;	//SPIM1_CLK_IO IO初始化gpio_config_init.GPIO_Pin 		= SPIM1_CLK_IO | SPIM1_MOSI_IO;gpio_config_init.GPIO_Mode 		= GPIO_Mode_AF_PP;  //复用推挽输出gpio_config_init.GPIO_Speed 	= GPIO_Speed_50MHz;GPIO_Init(SPIM1_GPIO_PORT, &gpio_config_init);gpio_config_init.GPIO_Pin 		= SPIM1_MISO_IO;	//SPIM1_MISO_IO IO初始化gpio_config_init.GPIO_Mode 		= GPIO_Mode_IN_FLOATING;  //MISO浮空输入gpio_config_init.GPIO_Speed 	= GPIO_Speed_50MHz;GPIO_Init(SPIM1_GPIO_PORT, &gpio_config_init);GPIO_SetBits(SPIM1_GPIO_PORT, SPIM1_CLK_IO | SPIM1_MISO_IO | SPIM1_MOSI_IO);	//IO初始状态都设置为高电平}		
}

2.2 初始化 SPI1,设置 SPI1 工作模式

接下来初始化 SPI1,设置 SPI1 为主机模式,设置数据格式为 8 位,然设置 SCK 时钟极性及采样方式。并设置 SPI1 的时钟频率(最大 18Mhz),以及数据的格式(MSB 在前还是 LSB 在前)。这在库函数中是通过 SPI_Init 函数来实现。
函数原型:

void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct)

第一个参数是 SPI 标号,第二个参数结构体类型 SPI_InitTypeDef 为相关属性设置。

SPI_InitTypeDef 的定义如下:

typedef struct
{
uint16_t SPI_Direction;
uint16_t SPI_Mode;
uint16_t SPI_DataSize;
uint16_t SPI_CPOL;
uint16_t SPI_CPHA;
uint16_t SPI_NSS;
uint16_t SPI_BaudRatePrescaler;
uint16_t SPI_FirstBit;
uint16_t SPI_CRCPolynomial;
}SPI_InitTypeDef;
参数解释
SPI_Direction设置 SPI 的通信方式,可以选择为半双工,全双工,以及串行发和串行收方式
SPI_Mode设置 SPI 的主从模式,主机模式 (SPI_Mode_Master),从机模式 (PI_Mode_Slave)。
SPI_DataSiz数据为 8 位还是 16 位帧格式选择项。SPI_DataSize_8b(8 位),SPI_DataSize_16b (16位)
SPI_CPOL设置时钟极性
SPI_CPHA设置时钟相位,也就是选择在串行同步时钟的第几个跳变沿(上升或下降)数据被采样,可以为第一个或者第二个条边沿采集
SPI_NSS设置 NSS 信号由硬件(NSS 管脚)还是软件控制
SPI_BaudRatePrescaler设置 SPI 波特率预分频值也就是决定 SPI 的时钟的参数 ,从不分频道 256 分频 8 个可选值 ,选择 256 分频值SPI_BaudRatePrescaler_256, 传输速度为 36M/256=140.625KHz。
SPI_FirstBit设置数据传输顺序是 MSB 位在前还是 LSB 位在前。SPI_FirstBit_MSB (高位在前)
SPI_CRCPolynomial设置 CRC 校验多项式,提高通信可靠性,大于 1 即可

初始化的范例格式为:

//--------------------------------------------------------------------------------------------------------
//	函 数 名: spi_master_init
//	功能说明: SPI 硬件配置参数初始化
//	形    参: 	spi_chl:SPIM 通道
//	返 回 值: 无
//	日    期: 2020-03-12
//  备    注:采用 Unix like 方式
//	作    者: by 霁风AI
//--------------------------------------------------------------------------------------------------------
void spi_master_init(uint8_t spi_chl)
{SPI_InitTypeDef  spi_config_init;
#if 1  if(spi_chl == 1){	spi_flash_gpio_init();	//spi flash cs 初始化
//		sd_gpio_init();	//spi sd cs 初始化
//		nrf24l01_gpio_init();//spi nrf24l01 cs 初始化spi_gpio_init(1);	//spi gpio 初始化RCC_APB2PeriphClockCmd(RCC_PCLK_SPIM1_HD, ENABLE);	//SPI1时钟使能spi_config_init.SPI_Direction 			= SPI_Direction_2Lines_FullDuplex;  //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工spi_config_init.SPI_Mode 				= SPI_Mode_Master;		//设置SPI工作模式:设置为主SPIspi_config_init.SPI_DataSize 			= SPI_DataSize_8b;		//设置SPI的数据大小:SPI发送接收8位帧结构spi_config_init.SPI_CPOL 				= SPI_CPOL_Low;		//选择了串行时钟的稳态:空闲时钟低spi_config_init.SPI_CPHA 				= SPI_CPHA_1Edge;	//数据捕获(采样)于第1个时钟沿spi_config_init.SPI_NSS					= SPI_NSS_Soft;//SPI_NSS_Soft;		//NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制spi_config_init.SPI_BaudRatePrescaler 	= SPI_BaudRatePrescaler_256;		//定义波特率预分频的值:波特率预分频值为256spi_config_init.SPI_FirstBit 			= SPI_FirstBit_MSB;	//指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始spi_config_init.SPI_CRCPolynomial 		= 7;	//CRC值计算的多项式SPI_Init(SPI1, &spi_config_init);  //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器SPI_Cmd(SPI1, ENABLE); //使能SPI外设//		spi_master_send_recv_byte(1, 0xFF);	//启动传输	}
#endif
} 

2.3 SPI 传输数据

通信接口需要有发送数据和接受数据的函数,固件库提供的发送数据函数原型为:

void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data)

往 SPIx 数据寄存器写入数据 Data,从而实现发送。

固件库提供的接受数据函数原型为:

uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx)

这从 SPIx 数据寄存器读出接收到的数据。

收发单个字节数据:

//--------------------------------------------------------------------------------------------------------
//	函 数 名: spi_master_send_recv_byte
//	功能说明: SPI 收发数据
//	形    参: 	spi_chl:SPIM 通道
//				send_byte:发送的数据
//	返 回 值: 无
//	日    期: 2020-03-14
//  备    注:采用 Unix like 方式
//	作    者: by 霁风AI
//--------------------------------------------------------------------------------------------------------
uint8_t spi_master_send_recv_byte(uint8_t spi_chl, uint8_t spi_byte)
{		uint8_t time = 0;if (spi_chl == 1)			    {while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) //检查指定的SPI标志位设置与否:发送缓存空标志位{time++;if(time>200){return false;}}			  SPI_I2S_SendData(SPI1, spi_byte); //通过外设SPIx发送一个数据time = 0;while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET)//检查指定的SPI标志位设置与否:接受缓存非空标志位{time++;if(time>200){return false;}}	  						    return SPI_I2S_ReceiveData(SPI1); //返回通过SPIx最近接收的数据	}else {return false;}
}

收发多个字节数据:

//--------------------------------------------------------------------------------------------------------
//	函 数 名: spi_master_send_some_bytes
//	功能说明: SPI 发送多个字节数据
//	形    参: 	spi_chl:SPIM 通道
//				pbdata:发送的数据首地址
//				send_length:发送数据长度
//	返 回 值: 无
//	日    期: 2020-03-12
//  备    注:采用 Unix like 方式
//	作    者: by 霁风AI
//--------------------------------------------------------------------------------------------------------
void spi_master_send_some_bytes(uint8_t spi_chl, uint8_t *pbdata, uint16_t send_length)
{uint16_t i = 0;for (i = 0; i < send_length; i++){spi_master_send_recv_byte(spi_chl, pbdata[i]);}//	while (send_length--)
//	{
//		spi_master_send_byte(spi_chl, *pbdata++);
//	}}//--------------------------------------------------------------------------------------------------------
//	函 数 名: spi_master_recv_some_bytes
//	功能说明: SPI 接收多个字节数据
//	形    参: 	spi_chl:SPIM 通道
//				pbdata:接收的数据首地址
//				send_length:接收数据长度
//	返 回 值: 无
//	日    期: 2020-03-12
//  备    注:采用 Unix like 方式
//	作    者: by 霁风AI
//--------------------------------------------------------------------------------------------------------
void spi_master_recv_some_bytes(uint8_t spi_chl, uint8_t *pbdata, uint16_t recv_length)
{uint8_t *temp_data = pbdata;while (recv_length--){*temp_data++ = spi_master_send_recv_byte(spi_chl, 0xFF);	//发送 0xff 为从设备提供时钟}}

2.4 查看 SPI 传输状态

在 SPI 传输过程中,要判断数据是否传输完成,发送区是否为空等等状态,
通过函数 SPI_I2S_GetFlagStatus 实现的,判断发送是否完成的方法是:

SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE)

3. SPI FLASH 操作

3.1 宏定义部分

#define  FLASH_WRITE_ENABLE_CMD 		0x06
#define  FLASH_WRITE_DISABLE_CMD		0x04
#define  FLASH_READ_SR_CMD				0x05
#define  FLASH_WRITE_SR_CMD				0x01
#define  FLASH_READ_DATA				0x03
#define  FLASH_FASTREAD_DATA			0x0b
#define  FLASH_WRITE_PAGE				0x02
#define  FLASH_ERASE_PAGE      			0x81
#define  FLASH_ERASE_SECTOR       		0x20
#define	 FLASH_ERASE_BLOCK				0xd8
#define	 FLASH_ERASE_CHIP				0xc7
#define  FLASH_POWER_DOWN				0xb9
#define  FLASH_RELEASE_POWER_DOWN       0xab
#define  FLASH_READ_DEVICE_ID      		0x90
#define  FLASH_READ_JEDEC_ID      		0x9f#define 	FLASH_SIZE	 (1*1024*1024)	// 1M字节
#define		PAGE_SIZE			8192	// 256 bytes
#define 	SECTOR_SIZE		512	 // 4-Kbyte
#define		BLOCK_SIZE		32	// 64-Kbyte	#define PAGE_LEN		255	 //一页256字节

3.2 中间层函数封装

注明: 此部分函数的封装是为了统一硬件 SPI 和软件模拟 SPI 接口。

//--------------------------------------------------------------------------------------------------------
//	函 数 名: hal_spi_send_bytes
//	功能说明: SPI 发送数据,包含软件和硬件通信方式
//	形    参: 	mode:通信方式选择(0:软件SPI;1:硬件SPI)
//				pbdata:发送数据的首地址
//				send_length:发送数据长度
//	返 回 值: 执行状态(true or false)
//	日    期: 2020-03-12
//  备    注: 中间层封装底层接口
//	作    者: by 霁风AI
//--------------------------------------------------------------------------------------------------------
uint8_t hal_spi_send_bytes(uint8_t mode, uint8_t *pbdata, uint16_t send_length)
{if (mode == 0){for (uint16_t i = 0; i < send_length; i++){Spi_WriteByte(pbdata[i]);}return true;}else if (mode == 1){spi_master_send_some_bytes(1, pbdata, send_length);//		for (uint16_t i = 0; i < send_length; i++)
//        {
//            spi_master_send_recv_byte(1, pbdata[i]);
//        }return true;}else {return false;}}//--------------------------------------------------------------------------------------------------------
//	函 数 名: hal_spi_recv_bytes
//	功能说明: SPI 接收数据,包含软件和硬件通信方式
//	形    参: 	mode:通信方式选择(0:软件SPI;1:硬件SPI)
//				pbdata:发送数据的首地址
//				send_length:发送数据长度
//	返 回 值: 执行状态(true or false)
//	日    期: 2020-03-12
//  备    注: 中间层封装底层接口
//	作    者: by 霁风AI
//--------------------------------------------------------------------------------------------------------
uint8_t hal_spi_recv_bytes(uint8_t mode, uint8_t *pbdata, uint16_t recv_length)
{if (mode == 0){for (uint16_t i = 0; i < recv_length; i++){*pbdata++ = Spi_ReadByte();	//软件模拟SPI}   return true;}else if (mode == 1){spi_master_recv_some_bytes(1, pbdata, recv_length);	//硬件SPI//		for (uint16_t i = 0; i < recv_length; i++)
//        {
//            *pbdata++ = spi_master_send_recv_byte(1, 0xFF);
//        }return true;}else {return false;}}

关于软件模拟 SPI 部分代码,参看:软件模拟SPI代码 。此处不再贴出。


3.3 FLASH 部分


__align(4) uint8_t g_DataTmpBuffer[0x1000] = {0};#define SectorBuf  g_DataTmpBuffer//--------------------------------------------------------------------------------------------------------
//	函 数 名: Flash_WriteEnable
//	功能说明: 写使能,置位 WEL 位 WEL 位(WEL-->1)
//	形    参: 无
//	返 回 值: 无
//	日    期: 2020-03-07
//  备    注: 
//	作    者: by 霁风AI
//--------------------------------------------------------------------------------------------------------
void Flash_WriteEnable(void)
{uint8_t command = FLASH_WRITE_ENABLE_CMD;FLASH_CS_LOW;hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);//开启写使能FLASH_CS_HIGH;
}//--------------------------------------------------------------------------------------------------------
//	函 数 名: Flash_WriteDisable
//	功能说明: 写失能,复位 WEL 位(WEL-->0)
//	形    参: 无
//	返 回 值: 无
//	日    期: 2020-03-07
//  备    注: 
//	作    者: by 霁风AI
//--------------------------------------------------------------------------------------------------------
void Flash_WriteDisable(void)
{uint8_t command = FLASH_WRITE_DISABLE_CMD;FLASH_CS_LOW;hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);// Spi_WriteByte(FLASH_WRITE_DISABLE_CMD);	//开启写失能 04hFLASH_CS_HIGH;
}//--------------------------------------------------------------------------------------------------------
//	函 数 名: Flash_WriteSR
//	功能说明: 读状态寄存器
//	形    参: 无
//	返 回 值: 无
//	日    期: 2020-03-07
//  备    注: 多用于检查 BUSY 位
//	作    者: by 霁风AI
//--------------------------------------------------------------------------------------------------------
uint8_t Flash_ReadSR(void)
{uint8_t ucTmpVal = 0;uint8_t command = FLASH_READ_SR_CMD;FLASH_CS_LOW;hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);	//05hhal_spi_recv_bytes(SPI_COMM_MODE, &ucTmpVal, 1);// ucTmpVal = Spi_ReadByte();FLASH_CS_HIGH;return ucTmpVal;
}//--------------------------------------------------------------------------------------------------------
//	函 数 名: Flash_WriteSR
//	功能说明: 写状态寄存器
//	形    参: 	_ucByte:写入状态寄存器的数值
//	返 回 值: no
//	日    期: 2020-03-07
//  备    注: 
//	作    者: by 霁风AI
//--------------------------------------------------------------------------------------------------------
void Flash_WriteSR(uint8_t _ucByte)
{uint8_t command = FLASH_WRITE_SR_CMD;Flash_WriteEnable();	Flash_WaitNobusy();FLASH_CS_LOW;hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);	//01hhal_spi_send_bytes(SPI_COMM_MODE, &_ucByte, 1);	//写入一个字节FLASH_CS_HIGH;
}//--------------------------------------------------------------------------------------------------------
//	函 数 名: Flash_WaitNobusy
//	功能说明: 检查 FLASH BUSY 位状态
//	形    参: no
//	返 回 值: no
//	日    期: 2020-03-07
//  备    注: 调用Flash_ReadSR(),判断状态寄存器的R0位,执行结束操作清零
//	作    者: by 霁风AI
//--------------------------------------------------------------------------------------------------------
void Flash_WaitNobusy(void)
{//FLASH_READ_SR_CMD 指令的发送,有的FLASH仅需发送一次,FLASH自动回复,有的FLASH无法自动回复,需要循环一直发送等待while(((Flash_ReadSR()) & 0x01)==0x01);	//等待BUSY位清空
}//--------------------------------------------------------------------------------------------------------
//	函 数 名: Flash_FastReadByte
//	功能说明: flash 都数据(快速读取:Fast read operate at the highest poossible frequency)
//	形    参: 	ucpBuffer:数据存储区首地址
//				_ulReadAddr: 要读出Flash的首地址
//				_usNByte: 要读出的字节数(最大65535B)
//	返 回 值: no
//	日    期: 2020-03-07
//  备    注: 从_ulReadAddr地址,连续读出_usNByte长度的字节
//	作    者: by 霁风AI
//--------------------------------------------------------------------------------------------------------
void Flash_ReadSomeBytes(uint8_t *ucpBuffer, uint32_t _ulReadAddr, uint16_t _usNByte)
{uint8_t command = FLASH_READ_DATA;uint8_t temp_buff[3] = {0};temp_buff[0] = (uint8_t)(_ulReadAddr >> 16);temp_buff[1] = (uint8_t)(_ulReadAddr >> 8);temp_buff[2] = (uint8_t)(_ulReadAddr >> 0);FLASH_CS_LOW;hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[0], 1);hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[1], 1);hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[2], 1);hal_spi_recv_bytes(SPI_COMM_MODE, ucpBuffer, _usNByte);// Spi_WriteByte(FLASH_READ_DATA);	//连续读取数据 03h// Spi_WriteByte((uint8_t)(_ulReadAddr>>16));	//写入24位地址// Spi_WriteByte((uint8_t)(_ulReadAddr>>8));// Spi_WriteByte((uint8_t)(_ulReadAddr>>0));// while(_usNByte--)// {// 	*ucpBuffer = Spi_ReadByte();// 	ucpBuffer++;// }FLASH_CS_HIGH;
}//--------------------------------------------------------------------------------------------------------
//	函 数 名: Flash_FastReadByte
//	功能说明: flash 都数据(快速读取:Fast read operate at the highest poossible frequency)
//	形    参: 	ucpBuffer:数据存储区首地址
//				_ulReadAddr: 要读出Flash的首地址
//				_usNByte: 要读出的字节数(最大65535B)
//	返 回 值: no
//	日    期: 2020-03-07
//  备    注: 从_ulReadAddr地址,连续读出_usNByte长度的字节
//	作    者: by 霁风AI
//--------------------------------------------------------------------------------------------------------
void Flash_FastReadByte(uint8_t *ucpBuffer, uint32_t _ulReadAddr, uint16_t _usNByte)
{uint8_t command = FLASH_FASTREAD_DATA;uint8_t temp_buff[3] = {0};temp_buff[0] = (uint8_t)(_ulReadAddr >> 16);temp_buff[1] = (uint8_t)(_ulReadAddr >> 8);temp_buff[2] = (uint8_t)(_ulReadAddr >> 0);FLASH_CS_LOW;hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[0], 1);hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[1], 1);hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[2], 1);hal_spi_recv_bytes(SPI_COMM_MODE, ucpBuffer, _usNByte);// Spi_WriteByte(FLASH_FASTREAD_DATA);//快速读取数据 0bh// Spi_WriteByte((uint8_t)(_ulReadAddr>>16));//写入24位地址// Spi_WriteByte((uint8_t)(_ulReadAddr>>8));// Spi_WriteByte((uint8_t)(_ulReadAddr>>0));// Spi_WriteByte(0xFF);//等待8个时钟(dummy byte)// while(_usNByte--)// {// 	*ucpBuffer = Spi_ReadByte();// 	ucpBuffer++;// }FLASH_CS_HIGH;
}//--------------------------------------------------------------------------------------------------------
//	函 数 名: Flash_WritePage
//	功能说明: flash 写数据(按页写入,一页256字节,写入之前FLASH地址上必须为0xFF)
//	形    参: 	ucpBuffer:数据存储区首地址
//				_ulWriteAddr: 要读写入Flash的首地址
//				_usNByte: 要写入的字节数(最大65535B = 64K 块)
//	返 回 值: no
//	日    期: 2020-03-07
//  备    注: _ulWriteAddr,连续写入_usNByte长度的字节
//	作    者: by 霁风AI
//--------------------------------------------------------------------------------------------------------
void Flash_WritePage(uint8_t *ucpBuffer, uint32_t _ulWriteAddr, uint16_t _usNByte)
{uint8_t command = FLASH_WRITE_PAGE;uint8_t temp_buff[3] = {0};temp_buff[0] = (uint8_t)(_ulWriteAddr >> 16);temp_buff[1] = (uint8_t)(_ulWriteAddr >> 8);temp_buff[2] = (uint8_t)(_ulWriteAddr >> 0);Flash_WriteEnable();	//写使能Flash_WaitNobusy();	//等待写入结束FLASH_CS_LOW;hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[0], 1);hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[1], 1);hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[2], 1);hal_spi_send_bytes(SPI_COMM_MODE, ucpBuffer, _usNByte);// Spi_WriteByte(FLASH_WRITE_PAGE);	//02h// Spi_WriteByte((uint8_t)(_ulWriteAddr>>16));	//写入24位地址// Spi_WriteByte((uint8_t)(_ulWriteAddr>>8));// Spi_WriteByte((uint8_t)(_ulWriteAddr>>0));// while(_usNByte--)// {// 	Spi_WriteByte(*ucpBuffer);	//SPI 写入单个字节// 	ucpBuffer++;// }FLASH_CS_HIGH;Flash_WaitNobusy();	//等待写入结束
}//--------------------------------------------------------------------------------------------------------
//	函 数 名: Flash_WriteNoCheck
//	功能说明: flash 写数据(不带擦除,写入之前必须确保写入部分FLASH的数据全为0xFf,否则写入失败)
//	形    参: 	ucpBuffer:数据存储区首地址
//				_ulWriteAddr: 要读写入Flash的首地址
//				_usNByte: 要写入的字节数(最大65535B = 64K 块)
//	返 回 值: no
//	日    期: 2020-03-07
//  备    注: _ulWriteAddr,连续写入_usNByte长度的字节,程序带FLASH数据检查写入
//	作    者: by 霁风AI
//--------------------------------------------------------------------------------------------------------
void Flash_WriteNoCheck(uint8_t *ucpBuffer, uint32_t _ulWriteAddr, uint16_t _usNByte)
{uint16_t PageByte = 256 - _ulWriteAddr % 256;//单页剩余可写字节数if(_usNByte <= PageByte)	//不大于256字节{PageByte = _usNByte;}while(1){Flash_WritePage(ucpBuffer, _ulWriteAddr, PageByte);if(_usNByte == PageByte)	//写入结束break;else{ucpBuffer += PageByte;	//下一页写入的数据_ulWriteAddr += PageByte;	//下一页写入的地址_usNByte -= PageByte;	//待写入的字节数递减if(_usNByte > 256){PageByte = 256;}else{PageByte = _usNByte;}}}
}//--------------------------------------------------------------------------------------------------------
//	函 数 名: Flash_WriteSomeBytes
//	功能说明: flash 写数据
//	形    参: 	ucpBuffer:数据存储区首地址
//				_ulWriteAddr: 要读写入Flash的首地址
//				_usNByte: 要写入的字节数(最大65535B = 64K 块)
//	返 回 值: no
//	日    期: 2020-03-07
//  备    注: _ulWriteAddr,连续写入_usNByte长度的字节,程序带FLASH数据检查写入
//	作    者: by 霁风AI
//--------------------------------------------------------------------------------------------------------
void Flash_WriteSomeBytes(uint8_t *ucpBuffer, uint32_t _ulWriteAddr, uint16_t _usNByte)
{uint32_t ulSecPos = 0;				//得到扇区位置uint16_t usSecOff = 0;				//扇区偏移uint16_t usSecRemain = 0;		//剩余扇区uint32_t i = 0;ulSecPos = _ulWriteAddr / 4096;//地址所在扇区(0--511)usSecOff = _ulWriteAddr % 4096;//扇区内地址偏移usSecRemain = 4096 - usSecOff;//扇区除去偏移,还剩多少字节if(_usNByte <= usSecRemain)	//写入数据大小 < 剩余扇区空间大小{usSecRemain = _usNByte;}while(1){Flash_ReadSomeBytes(SectorBuf, ulSecPos*4096, 4096);//读出整个扇区的内容for (i = 0; i < usSecRemain; i++)	//校验数据{if (SectorBuf[usSecOff + i] != 0xFF)//储存数据不为0xFF,需要擦除break;}if(i < usSecRemain)	//需要擦除{Flash_EraseSector(ulSecPos);	//擦除这个扇区for(i = 0; i < usSecRemain; i++)	//保存写入的数据{SectorBuf[usSecOff + i] = ucpBuffer[i];}Flash_WriteNoCheck(SectorBuf, ulSecPos*4096, 4096);	//写入整个扇区(扇区=老数据+新写入数据)}else{Flash_WriteNoCheck(ucpBuffer, _ulWriteAddr, usSecRemain);//不需要擦除,直接写入扇区}if(_usNByte == usSecRemain)	//写入结束{Flash_WriteDisable();break;}else{ulSecPos++;		//扇区地址增加1usSecOff = 0;		//扇区偏移归零ucpBuffer += usSecRemain;	//指针偏移_ulWriteAddr += usSecRemain;	//写地址偏移_usNByte -= usSecRemain;	//待写入的字节递减if(_usNByte > 4096){usSecRemain = 4096;	//待写入一扇区(4096字节大小)}else{usSecRemain = _usNByte;		//待写入少于一扇区的数据}}}}//--------------------------------------------------------------------------------------------------------
//	函 数 名: Flash_ErasePage
//	功能说明: flash erase page
//	形    参: no
//	返 回 值: no
//	日    期: 2020-03-07
//  备    注: 有的 FLASH 支持
//	作    者: by 霁风AI
//--------------------------------------------------------------------------------------------------------
void Flash_ErasePage(uint32_t _ulPageAddr)
{_ulPageAddr *= 256;Flash_WriteEnable();Flash_WaitNobusy();FLASH_CS_LOW;Spi_WriteByte(FLASH_ERASE_PAGE);	//页擦除指令Spi_WriteByte((uint8_t)(_ulPageAddr>>16));	//写入24位地址Spi_WriteByte((uint8_t)(_ulPageAddr>>8));Spi_WriteByte((uint8_t)(_ulPageAddr>>0));FLASH_CS_HIGH;Flash_WaitNobusy();	//等待写入结束
}//--------------------------------------------------------------------------------------------------------
//	函 数 名: Flash_EraseSector
//	功能说明: flash erase sector
//	形    参: no
//	返 回 值: no
//	日    期: 2020-03-07
//  备    注: 1扇区 = 4K Bytes
//	作    者: by 霁风AI
//--------------------------------------------------------------------------------------------------------
void Flash_EraseSector(uint32_t _ulSectorAddr)
{uint8_t command = FLASH_ERASE_SECTOR;uint8_t temp_buff[3] = {0};temp_buff[0] = (uint8_t)(_ulSectorAddr >> 16);temp_buff[1] = (uint8_t)(_ulSectorAddr >> 8);temp_buff[2] = (uint8_t)(_ulSectorAddr >> 0);_ulSectorAddr *= 4096;	//1个扇区 4 KBytesFlash_WriteEnable();Flash_WaitNobusy();FLASH_CS_LOW;hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[0], 1);hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[1], 1);hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[2], 1);//	Spi_WriteByte(FLASH_ERASE_SECTOR);	//20h
//	Spi_WriteByte((uint8_t)(_ulSectorAddr>>16));	//写入24位地址
//	Spi_WriteByte((uint8_t)(_ulSectorAddr>>8));
//	Spi_WriteByte((uint8_t)(_ulSectorAddr));FLASH_CS_HIGH;Flash_WaitNobusy();	//等待写入结束
}//--------------------------------------------------------------------------------------------------------
//	函 数 名: Flash_EraseBlock
//	功能说明: flash erase block 
//	形    参: no
//	返 回 值: no
//	日    期: 2020-03-07
//  备    注: 1块 = 64K Bytes
//	作    者: by 霁风AI
//--------------------------------------------------------------------------------------------------------
void Flash_EraseBlock(uint32_t _ulBlockAddr)
{uint8_t command = FLASH_ERASE_BLOCK;_ulBlockAddr *= 65536;	//块地址,一块64KFlash_WriteEnable();Flash_WaitNobusy();FLASH_CS_LOW;hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);hal_spi_send_bytes(SPI_COMM_MODE, (uint8_t *)(_ulBlockAddr>>16), 1);hal_spi_send_bytes(SPI_COMM_MODE, (uint8_t *)(_ulBlockAddr>>8), 1);hal_spi_send_bytes(SPI_COMM_MODE, (uint8_t *)(_ulBlockAddr>>0), 1);// Spi_WriteByte(FLASH_ERASE_BLOCK);	//d8h// Spi_WriteByte((uint8_t)(_ulBlockAddr>>16));	//写入24位地址// Spi_WriteByte((uint8_t)(_ulBlockAddr>>8));// Spi_WriteByte((uint8_t)(_ulBlockAddr));FLASH_CS_HIGH;Flash_WaitNobusy();	//等待写入结束
}//--------------------------------------------------------------------------------------------------------
//	函 数 名: Flash_EraseChip
//	功能说明: flash erase chip , it makes flash  recovery FF
//	形    参: no
//	返 回 值: no
//	日    期: 2020-03-07
//  备    注: 软件模拟SPI
//	作    者: by 霁风AI
//--------------------------------------------------------------------------------------------------------
void Flash_EraseChip(void)
{uint8_t command = FLASH_ERASE_CHIP;Flash_WriteEnable();	//flash芯片写使能Flash_WaitNobusy();	//等待写操作完成FLASH_CS_LOW;hal_spi_recv_bytes(SPI_COMM_MODE, &command, 1);// Spi_WriteByte(FLASH_ERASE_CHIP);	//c7hFLASH_CS_HIGH;Flash_WaitNobusy();	//等待写入结束
}//--------------------------------------------------------------------------------------------------------
//	函 数 名: Flash_PowerDown
//	功能说明: flash into power down mode 
//	形    参: no
//	返 回 值: no
//	日    期: 2020-03-07
//  备    注: 软件模拟SPI
//	作    者: by 霁风AI
//--------------------------------------------------------------------------------------------------------
void Flash_PowerDown(void)
{uint8_t command = FLASH_POWER_DOWN; FLASH_CS_LOW;hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);// Spi_WriteByte(FLASH_POWER_DOWN);	//b9hFLASH_CS_HIGH;Sys_delay_us(3);	// cs go high , need to delay 3us
}//--------------------------------------------------------------------------------------------------------
//	函 数 名: Flash_WakeUp
//	功能说明: wake up flash from power down mode or hign performance mode
//	形    参: no
//	返 回 值: no
//	日    期: 2020-03-07
//  备    注: 软件模拟SPI
//	作    者: by 霁风AI
//--------------------------------------------------------------------------------------------------------
void Flash_WakeUp(void)
{uint8_t command = FLASH_RELEASE_POWER_DOWN; FLASH_CS_LOW;hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);// Spi_WriteByte(FLASH_RELEASE_POWER_DOWN);//abhFLASH_CS_HIGH;Sys_delay_us(3);	//CS go high , need delay 3us
}//--------------------------------------------------------------------------------------------------------
//	函 数 名: Flash_ReadDeviceID
//	功能说明: 读取FLASH ID(manufacturer ID-1Byte + Device ID-2Byte:type+density)
//	形    参: 无
//	返 回 值: ulJedId:FLASH ID 3字节
//	日    期: 2020-03-06
//  备    注: 软件模拟SPI
//	作    者: by 霁风AI
//--------------------------------------------------------------------------------------------------------
uint16_t Flash_ReadDeviceID(void)
{uint8_t command = FLASH_READ_DEVICE_ID;uint16_t usFlashId = 0;uint8_t temp_buff[3] = {0};FLASH_CS_LOW;hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);	//90hhal_spi_send_bytes(SPI_COMM_MODE, temp_buff, 3);	//写入24位地址;假地址hal_spi_recv_bytes(SPI_COMM_MODE, temp_buff, 2);// Spi_WriteByte(FLASH_READ_DEVICE_ID);	//90h// Spi_WriteByte(0x00);//写入24位地址;假地址// Spi_WriteByte(0x00);// Spi_WriteByte(0x00);	//如果0x01,先输出 Device ID// usFlashId |= Spi_ReadByte()<<8;// usFlashId |= Spi_ReadByte();FLASH_CS_HIGH;usFlashId = (uint16_t)(temp_buff[0] << 8) | (temp_buff[1] << 0);return usFlashId;
}//--------------------------------------------------------------------------------------------------------
//	函 数 名: Flash_ReadJEDECID
//	功能说明: 读取FLASH ID(manufacturer ID-1Byte + Device ID-2Byte:type+density)
//	形    参: 无
//	返 回 值: ulJedId:FLASH ID 3字节
//	日    期: 2020-03-06
//  备    注: 软件模拟SPI
//	作    者: by 霁风AI
//--------------------------------------------------------------------------------------------------------
uint32_t Flash_ReadJEDECID(void)
{uint8_t command = FLASH_READ_JEDEC_ID;uint32_t flash_jed_id = 0;uint8_t recv_buff[3] = {0};FLASH_CS_LOW;hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);	//9fhhal_spi_recv_bytes(SPI_COMM_MODE, recv_buff, 3);FLASH_CS_HIGH;flash_jed_id = (recv_buff[0] << 16) | (recv_buff[1] << 8) | (recv_buff[2] << 0);return flash_jed_id;
}

参考:

1.原子库函数手册

2.SPI—读写串行 FLASH


http://chatgpt.dhexx.cn/article/kjyV2AD5.shtml

相关文章

STM32系列(HAL库)——F103C8T6通过SPI方式读写W25Q64—(Flash存储模块)

1.软件准备 (1)编程平台&#xff1a;Keil5 (2)CubeMX (3)XCOM(串口调试助手) 2.硬件准备 (1)W25Q64模块 (2)F1的板子&#xff0c;本例使用经典F103C8T6 (3)ST-link 下载器 (4)USB-TTL模块 (5)杜邦线若干 3.模块资料 (1)模块简介&#xff1a; W25Q64(64M-bit)&#xff0c…

SPI协议学习Cubmx——读写Flash W25Q64

这是最好的时代&#xff0c;这是最坏的时代&#xff1b; 这是智慧的时代&#xff0c;这是愚蠢的时代&#xff1b; 这是信仰的时期&#xff0c;这是怀疑的时期&#xff1b; 这是光明的季节&#xff0c;这是黑暗的季节&#xff1b; 这是希望之春&#xff0c;这是失望之冬&#xf…

STM32使用QUADSPI读写外部Nor Flash(以W25Q64为例)

使用QUADSPI读写W25Q64 QUADSPI介绍硬件连接双闪存模式禁止双闪存模式使能 QUADSPI命令序列指令阶段地址阶段交替字节阶段空指令周期阶段数据阶段 QUADSPI主要信号接口协议模式单线SPI模式双线SPI模式四线SPI模式 使用QUADSPI操作W25Q64发送命令函数状态轮询函数读ID函数QUADSP…

W25Q64内部结构

和STM32片上一样 我们分析 函数接口&#xff1a; 擦除1个扇区 【参数必须是4096的倍数】 void SPI_FLASH_SectorErase(uint32_t SectorAddr) 擦除芯片全部 void SPI_FLASH_BulkErase(void) 写入一页【数据比256小】 pBuffer&#xff1a;待写入数据的指针 WriteAddr&#xff…

基于STM32F401RET6字库烧录(SPIW25Q64驱动)

目录 一、SPI&W25Q64 1-SPI介绍 2-初始化SPI 3-SPI数据接收和发送函数 4-验证SPI是否配置正确&#xff08;读W25Q64的ID&#xff09; 二、W25Q64简介与API函数 1 - W25Q64芯片介绍 2- W25Q64芯片管脚说明 3- W25Q64芯片工作原理 4- W25Q64芯片操作时序 三、字库烧…

STM32CubeMx开发之路—13使用SPI读写W25Q64

!!! 本文已同步到码云 - 点击此链接获取最新 - 可进入码云提交修改 !!! 附件 源码已放到码云 ! ! ! ( 请点击文首链接进入仓库 ) 运行环境 Windows10STM32CubeMX Version 5.2.0Keil5(MDK5) Version 5.28.0.0 简介 本例程主要讲解如何使用硬件IIC读写24C02 STM32CubeMx基本配…

STM32CubeMX系列08——SPI通信(W25Q64、NRF24L01无线模块)

文章目录 1. 准备工作1.1. 所用硬件1.2. SPI 简介1.3. 生成工程1.3.1. 创建工程选择主控1.3.2. 系统配置1.3.3. 配置工程目录 2. 读写EEPROM实验&#xff08;W25Q64&#xff09;2.1. W25Q64 简介2.2. 代码实现 3. NRF24L01无线模块通信3.1. 模块简介3.2. SPI 配置3.2.1. SPI1 配…

STM32驱动W25Q64读写数据

STM32驱动W25Q64读写数据 一&#xff0e;基本参数二&#xff0e;通信协议1、时序&#xff1a;2、代码&#xff1a; 三&#xff0e;引脚定义与接线1、引脚定义2、对应接线 四&#xff0e;主要代码五&#xff0e;操作步骤与现象六&#xff0e;总结 一&#xff0e;基本参数 1.采用…

学习日记——W25Q64 FLASH—QSPI

W25Q64串行FLASH基础知识 大小&#xff1a;8M&#xff08;Byte&#xff09;&#xff08;128块&#xff08;Block&#xff09;&#xff0c;每块64K字节&#xff0c;每块16个扇区&#xff08;Sector&#xff09;&#xff0c;每个扇区4K字 节&#xff0c;每个扇区16页&#xff0c…

(STM32)W25Q64存储模块

1bit表示一个二进制位&#xff0c;1Byte表示8个二进制位。 每一个字节需要一个地址&#xff0c;所以24位地址&#xff0c;2^24次方最大寻址范围是16M。 W25Q256是特殊型号&#xff0c;切换4字节寻址模式才能使用后面的16M空间。 HOLD&#xff1a;芯片正常读写数据时&#xff…

SPIW25Q64(精华版)

1. SPI总线 1.1 W25Q64 1.1 W25Q64简介 W25Q64 是一种具有SPI接口的FlASH存储器&#xff0c;具有32768个可编程页(Page256B)&#xff0c;2048个可擦除扇区(Sector16*Page)&#xff0c;128个可擦除块(Block16*Sector)&#xff0c;容量为128*16*16*256B8MB(64bit)&#xff0c;…

W25Q64简介(译)

W25Q64是华邦公司推出的大容量SPI FLASH产品&#xff0c;其容量为64Mb。该25Q系列的器件在灵活性和性能方面远远超过普通的串行闪存器件。W25Q64将8M字节的容量分为128个块&#xff0c;每个块大小为64K字节&#xff0c;每个块又分为16个扇区&#xff0c;每个扇区4K个字节。W25Q…

W25Q64Flash芯片

W25Q64Flash芯片STM32操作 通讯方式&#xff1a;SPI通讯 大小&#xff1a;64是bit 换算字节是8M&#xff08;Byte&#xff09;&#xff08;128块&#xff08;Block&#xff09;&#xff0c;每块16个扇区&#xff08;Sector&#xff09;&#xff0c;每个扇区4K字节&#xff08;…

SPI通讯介绍 以及读写W25Q64(块,扇区,页的区别)

附工程百度网盘链接 链接&#xff1a;https://pan.baidu.com/s/1nCgNb5OyGpABAL657-gX0A?pwd6666 提取码&#xff1a;6666 介绍:摩托罗拉开发的一种通用数据总线, 四根通讯线SCK(串行时钟总线),MOSI(主机输出从机输入),MISO(主机输入从机输出),SS(从机选择)而且是同步全双…

W25Q64调试

简介 W25Q系列的器件在灵活性和性能方面远远超过普通的串行闪存器件。W25Q64将8M字节的容量分为128个块&#xff0c;每个块大小为64K字节&#xff0c;每个块又分为16个扇区&#xff0c;每个扇区4K个字节。 引脚介绍 串行数据输入、输出和 IOs&#xff08;DI、DO 和 IO0、IO1、…

软件SPI读写W25Q64硬件SPI读写W25Q64

目录 软件SPI读写W25Q64 MySPI W25Q64 主函数 硬件SPI读写W25Q64 软件SPI读写W25Q64 程序整体框架&#xff1a; SPI模块包含通信引脚封装&#xff0c;初始化&#xff0c;SPI三个基本时序单元&#xff08;起始&#xff0c;终止&#xff0c;交换一个字节&#xff09; W2…

W25Q64Flash芯片STM32操作

1、W25Q64Flash芯片介绍 通讯方式&#xff1a;SPI通讯 大小&#xff1a;8M&#xff08;Byte&#xff09; &#xff08;128块&#xff08;Block&#xff09;&#xff0c;每块64K字节&#xff0c;每块16个扇区&#xff08;Sector&#xff09;&#xff0c;每个扇区4K字节&…

STM32学习笔记(十一)丨SPI通信(W25Q64芯片简介,使用SPI读写W25Q64存储器芯片)

本篇文章包含的内容 一、SPI的通信协议及其原理1.1 SPI简介1.2 SPI通信的硬件连接1.3 SPI的时序基本单元1.3.1 起始条件和终止条件1.3.2 交换字节&#xff08;模式0&#xff0c;先移入&#xff0c;再移出&#xff09;1.3.3 交换字节&#xff08;模式1&#xff0c;先移出&#x…

看单片机原理图-外部FLASHW25Q64

系列文章目录 看单片机原理图-最小系统 看单片机原理图-最小系统电源电路 看单片机原理图-输入输出电路LED指示、按键输入 看单片机原理图-红外遥控、EEPROM 看单片机原理图-FLASH 文章目录 系列文章目录前言一、FLASH 前言 硬件&#xff1a;百问网100ASK_STM32F103_MINI开发…

W25Q64 Flash芯片原理与应用方案(含W25Q64中文数据手册)

W25Q64是华邦公司推出的大容量SPI FLASH产品&#xff0c;其容量为64Mb&#xff08;8MB&#xff09;&#xff0c;应用较为广泛。 W25Q系列的器件在灵活性和性能方面远远超过普通的串行闪存器件。W25Q64将8M字节的容量分为128个块&#xff0c;每个块大小为64K字节&#xff0c;每个…