写在前面:
本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。

目录

一、SPI协议

二、W25Qxx芯片

三、硬件的 SPI

四、模拟的 SPI

五、对 W25Q64的实际应用


一、SPI协议

在实现 SPI功能之前,我们必选先了解好 SPI协议是怎么实现的,当我们知道它是怎么来实现,那么这个就好办,只要跟着对应模式的协议走就行了

关于 SPI协议的了解,这里就不多说,可以看之前的文章:UART/ USRAT、I2C、SPI通信方式扫盲

二、W25Qxx芯片

理论总是枯燥的,实验则是愉悦的;在正式使用 SPI之前,我们需要一个载体来进行实验以加深印象,这里就用 Flash存储芯片 W25Q64来进行实验;然后在这里简单介绍一下:

SPI flash W25Qxx:

W25Q系列的 spiflash。每页(Page)256B,每16个page为一个sector(扇区=4KB),每16个扇区为一个block(块=64KB)

W25Q64 = 64Mb = 8MB = 8192KB = 128block = 2048sector = 32768page;
然后它是可以用 SPI mode 0跟 SPI mode 3来进行通讯的

它的指令表如下:

最后,放一些它的操作时序,这里就放两个吧,嗯嗯,实在是太多了,就放两个,也是最重要、最简单的

最后的最后,下面例程是用 Mode 3来进行对 W25Qxx通讯

三、硬件的 SPI

先来说硬件的 SPI,顺便了解一下 STM32的硬件结构

1、特征:

● 3线全双工同步传输

● 带或不带第三根双向数据线的双线单工同步传输

● 8或16位传输帧格式选择

● 主或从操作

● 支持多主模式

● 8个主模式波特率预分频系数(最大为fPCLK/2)

● 从模式频率 (最大为fPCLK/2)

● 主模式和从模式的快速通信

● 主模式和从模式下均可以由软件或硬件进行NSS管理:主/从操作模式的动态改变

● 可编程的时钟极性和相位

● 可编程的数据顺序,MSB在前或LSB在前

● 可触发中断的专用发送和接收标志

● SPI总线忙状态标志

● 支持可靠通信的硬件CRC

─ 在发送模式下,CRC值可以被作为最后一个字节发送

─ 在全双工模式中对接收到的最后一个字节自动进行CRC校验

● 可触发中断的主模式故障、过载以及CRC错误标志

● 支持DMA功能的1字节发送和接收缓冲器:产生发送和接受请求

2、框图结构

3、一般的单主单从接线方式

4、模式配置

  • 主模式

  • 从模式

5、硬件 SPI配置的部分代码

/************************************************
函数名称 : W25Qxx_Config
功    能 : W25Qxx配置
参    数 : 无
返 回 值 : 无
*************************************************/
void W25Qxx_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	SPI_InitTypeDef SPI_InitStructure;

	/* W25Q_SPIx IO Periph clock enable */
	W25Q_IO_APBxClock_FUN(W25Q_CS_CLK | W25Q_SCK_CLK 	
								| W25Q_MISO_CLK | W25Q_MOSI_CLK, ENABLE);
	
	/* W25Q_SPIx Periph clock enable */
	W25Q_SPI_APBxClock_FUN(W25Q_SPI_CLK, ENABLE);

	/* Configure W25Q_SPIx pins: CS, SCK, MISO and MOSI */	
	/* Confugure CS pin as Output Push Pull */
	GPIO_InitStructure.GPIO_Pin = W25Q_CS_PINS;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_Init(W25Q_CS_PORT, &GPIO_InitStructure);

	/* Confugure SCK and MOSI pins as Alternate Function Push Pull */
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	
	GPIO_InitStructure.GPIO_Pin = W25Q_SCK_PINS;
	GPIO_Init(W25Q_SCK_PORT, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = W25Q_MOSI_PINS;
	GPIO_Init(W25Q_MOSI_PORT, &GPIO_InitStructure);
	
	/* Confugure MISO pin as Input Floating */
	GPIO_InitStructure.GPIO_Pin = W25Q_MISO_PINS;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(W25Q_MISO_PORT, &GPIO_InitStructure);
	
	/* ---------- END ---------- */
	
	W25Q_CS(HIGH);
	
	/* W25Q_SPIx configuration */
	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
	SPI_InitStructure.SPI_CRCPolynomial = 7;
	SPI_Init(W25Q_SPIx, &SPI_InitStructure);
	
	/* Disable W25Q_SPIx CRC calculation */
	SPI_CalculateCRC(W25Q_SPIx, DISABLE);

	/* Enable W25Q_SPIx */
	SPI_Cmd(SPI1, ENABLE);
}
/************************************************
函数名称 : SPI_Flash_SendByte
功    能 : 使用SPI发送/ 返回一个字节的数据
参    数 : wData ---- 写数据
返 回 值 : rData ---- 读数据
*************************************************/
static uint8_t SPI_Flash_SendByte( uint8_t wData )
{
	W25Q_TimeOut = MAX_TIME_OUT;
	
	/* Wait for W25Q_SPIx Tx buffer empty */
	while(SPI_I2S_GetFlagStatus(W25Q_SPIx, SPI_I2S_FLAG_TXE) == RESET)
	{
		if(0 == (W25Q_TimeOut--))
			return TimeOut_Callback(0);
	}

	/* Send byte through the W25Q_SPIx peripheral */
	SPI_I2S_SendData(W25Q_SPIx, wData);
	
	W25Q_TimeOut = MAX_TIME_OUT;

	/* Wait for W25Q_SPIx data reception */
	while(SPI_I2S_GetFlagStatus(W25Q_SPIx, SPI_I2S_FLAG_RXNE) == RESET)
	{
		if(0 == (W25Q_TimeOut--))
			return TimeOut_Callback(1);
	}
	
	/* Return the byte read from the W25Q_SPIx bus */
	return SPI_I2S_ReceiveData(W25Q_SPIx);
}

四、模拟的 SPI

模拟的 SPI其实就是我们自己利用 I/O管脚去实现电平的变化,以仿照出 SPI协议中的时序状态

因为 SPI目前有 4种模式,对于每种模式,它的时序电平都是不一样,但总体来说还是差不多

/*
 * MODE 0:CPOL=0,CPHA=0:此时空闲态时,SCLK处于低电平,数据采样是在第 1个边沿,	\
 * 			也就是 SCLK由低电平到高电平的跳变,所以数据采样是在上升沿,数据切换是在下降沿(第 2个边沿)。

 * MODE 1:CPOL=0,CPHA=1:此时空闲态时,SCLK处于低电平,数据采样是在第 2个边沿,	\
 * 			也就是 SCLK由高电平到低电平的跳变,所以数据采样是在下降沿,数据切换是在上升沿(第 1个边沿)。

 * MODE 2:CPOL=1,CPHA=0:此时空闲态时,SCLK处于高电平,数据采样是在第 1个边沿,	\
 * 			也就是 SCLK由高电平到低电平的跳变,所以数据采集是在下降沿,数据切换是在上升沿(第 2个边沿)。

 * MODE 3:CPOL=1,CPHA=1:此时空闲态时,SCLK处于高电平,数据采样是在第 2个边沿,	\
 * 			也就是 SCLK由低电平到高电平的跳变,所以数据采集是在上升沿,数据切换是在下降沿(第 1个边沿)。
*/
Leading EdgeTrailing EdgeSPI Mode
CPOL = 0, CPHA = 0Sample (rising)Setup (falling)Mode 0
CPOL = 0, CPHA = 1Setup (rising)Sample (falling)Mode 1
CPOL = 1, CPHA = 0Sample (falling)Setup (rising)Mode 2
CPOL = 1, CPHA = 1Setup (falling)Setup (rising)Mode 3

1、因为我们是用模拟的 SPI,所以不用纠结于必须用硬件上的 SPI接口,只要是个能正常输出电平的 I/O就可以了,这样也使得我们方便移植

/************************************************
函数名称 : Simulate_SPI_Config
功    能 : 模拟 SPI IO配置
参    数 : 无
返 回 值 : 无
*************************************************/
void Simulate_SPI_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	
	SL_SPI_SCK_APBxClock_FUN(SL_SPI_SCK_CLK, ENABLE);
	SL_SPI_MOSI_APBxClock_FUN(SL_SPI_MOSI_CLK, ENABLE);
	SL_SPI_MISO_APBxClock_FUN(SL_SPI_MISO_CLK, ENABLE);
	
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

	/* SCK */
	GPIO_InitStructure.GPIO_Pin = SL_SPI_SCK_PINS;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_Init(SL_SPI_SCK_PORT, &GPIO_InitStructure);
	
	/* MISO */
	GPIO_InitStructure.GPIO_Pin = SL_SPI_MISO_PINS;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(SL_SPI_MISO_PORT, &GPIO_InitStructure);
	
	/* MOSI */
	GPIO_InitStructure.GPIO_Pin = SL_SPI_MOSI_PINS;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_Init(SL_SPI_MOSI_PORT, &GPIO_InitStructure);
}

2、Mode 3下的模拟时序实现

#elif (1 == _CPOL && 1 == _CPHA) /* ----- MODE 3 ----- */
/************************************************
函数名称 : Write_SPI_Byte
功    能 : SPI写读一个字节
参    数 : Byte ---- 数据
返 回 值 : Byte ---- 数据
*************************************************/
uint8_t Write_SPI_Byte( uint8_t Byte )
{
	uint8_t i;

	SPI_SCK(HIGH);
	
	for(i = 0;i < 8;i++)
	{
		SPI_SCK(LOW);
		SPI_Delay_us(WAIT_TIME);       // 空等待
		
	#if 0
        SPI_MOSI((Byte & 0x80) >> 7);
		
	#else
		if(Byte & 0x80)
		{
			SPI_MOSI(HIGH);
		}
		else
		{
			SPI_MOSI(LOW);
		}
		
	#endif
		Byte <<= 1;
		SPI_Delay_us(WAIT_TIME);       // 空等待
		SPI_SCK(HIGH);
		SPI_Delay_us(WAIT_TIME);       // 空等待
		Byte |= SPI_MISO;
	}
	
	return Byte;
}

/************************************************
函数名称 : Read_SPI_Byte
功    能 : SPI只读一个字节
参    数 : 无
返 回 值 : temp ---- 数据
*************************************************/
uint8_t Read_SPI_Byte(void)
{
	uint8_t i;
	uint8_t temp = 0;

	SPI_SCK(HIGH);
	
	for(i = 0;i < 8;i++)
	{
		SPI_SCK(LOW);
		SPI_Delay_us(WAIT_TIME);       // 空等待
		temp <<= 1;
		
	#if 1
		temp |= SPI_MISO;
		
	#else
		if(SPI_MISO)
		{
			temp++;
		}
		
	#endif
		SPI_SCK(HIGH);
		SPI_Delay_us(WAIT_TIME);       // 空等待
	}
	
	return temp;
}

#endif /* SPI MODE */

然后你可以通过对宏的控制来实现模式选择操作

#define _CPOL     1
#define _CPHA     1

五、对 W25Q64的实际应用

这里就直接贴相关代码了(相当于驱动编写了)

#include "w25qxx.h"
#include "bsp_spi.h"
#include "bsp_uart.h"


/* 是否启用模拟 SPI */
#define USE_SIMULATE_SPI		0

#define MAX_TIME_OUT	((uint32_t)0x1000)

static __IO uint32_t W25Q_TimeOut = MAX_TIME_OUT;

/************************************************
函数名称 : TimeOut_Callback
功    能 : 等待超时回调函数
参    数 : ErrorCode ---- 错误代号
返 回 值 : 错误值 0
*************************************************/

#if (0 == USE_SIMULATE_SPI)
static uint8_t TimeOut_Callback( char ErrorCode )
{
	/* 等待超时后的处理,输出错误信息 */
	W25Q_DUBUG_PRINTF("SPI 等待超时!	  EerrorCode = %d\n",ErrorCode);

	return 0;
}

#endif /* USE_SIMULATE_SPI */

/************************************************
函数名称 : SPI_Flash_SendByte
功    能 : 使用SPI发送/ 返回一个字节的数据
参    数 : wData ---- 写数据
返 回 值 : rData ---- 读数据
*************************************************/
static uint8_t SPI_Flash_SendByte( uint8_t wData )
{
	
#if USE_SIMULATE_SPI
	return Write_SPI_Byte(wData);
	
#else
	W25Q_TimeOut = MAX_TIME_OUT;
	
	/* Wait for W25Q_SPIx Tx buffer empty */
	while(SPI_I2S_GetFlagStatus(W25Q_SPIx, SPI_I2S_FLAG_TXE) == RESET)
	{
		if(0 == (W25Q_TimeOut--))
			return TimeOut_Callback(0);
	}

	/* Send byte through the W25Q_SPIx peripheral */
	SPI_I2S_SendData(W25Q_SPIx, wData);
	
	W25Q_TimeOut = MAX_TIME_OUT;

	/* Wait for W25Q_SPIx data reception */
	while(SPI_I2S_GetFlagStatus(W25Q_SPIx, SPI_I2S_FLAG_RXNE) == RESET)
	{
		if(0 == (W25Q_TimeOut--))
			return TimeOut_Callback(1);
	}
	
	/* Return the byte read from the W25Q_SPIx bus */
	return SPI_I2S_ReceiveData(W25Q_SPIx);
		
#endif /* USE_SIMULATE_SPI */
}

/************************************************
函数名称 : W25Qxx_Busy_Wait
功    能 : W25Qxx忙等待
参    数 : 无
返 回 值 : 无
*************************************************/
static void W25Qxx_Busy_Wait(void)
{
	uint8_t flash_status = 0;
	
    W25Q_CS(LOW);
	
	SPI_Flash_SendByte(W25Q_STATUS_REG1);
	
	do
	{
		flash_status = SPI_Flash_SendByte(W25Q_DUMMY_BYTE);
	}while(flash_status & BIT_BUSY);
	
    W25Q_CS(HIGH);
}

/************************************************
函数名称 : W25Qxx_Read_JEDECID
功    能 : 读 W25QxxJEDEC_ID(制造商、类型、容量)
参    数 : 无
返 回 值 : temp[0] ---- JEDEC_ID
*************************************************/
uint32_t W25Qxx_Read_JEDECID(void)
{
	uint32_t temp[4] = {0};

    W25Q_CS(LOW);
	
	SPI_Flash_SendByte(W25Q_JEDEC_ID);
	temp[1] = SPI_Flash_SendByte(W25Q_DUMMY_BYTE);		// 制造商
	temp[2] = SPI_Flash_SendByte(W25Q_DUMMY_BYTE);		// 类型
	temp[3] = SPI_Flash_SendByte(W25Q_DUMMY_BYTE);		// 容量
	temp[0] = (temp[1] << 16) | (temp[2] << 8) | temp[3];
	
    W25Q_CS(HIGH);
	
	return temp[0];
}

/************************************************
函数名称 : W25Qxx_Read_Manufacturer_ID
功    能 : 读 W25Qxx制造商 ID
参    数 : 无
返 回 值 : id_num ---- 制造商 ID
*************************************************/
uint16_t W25Qxx_Read_Manufacturer_ID(void)
{
	uint16_t id_num = 0;

    W25Q_CS(LOW);
	
	SPI_Flash_SendByte(W25Q_MANUFACTURER_ID);
	SPI_Flash_SendByte(W25Q_DUMMY_BYTE);
	SPI_Flash_SendByte(W25Q_DUMMY_BYTE);
	SPI_Flash_SendByte(0x00);
	id_num |= SPI_Flash_SendByte(W25Q_DUMMY_BYTE) << 8;
	id_num |= SPI_Flash_SendByte(W25Q_DUMMY_BYTE);
	
    W25Q_CS(HIGH);
	
	return id_num;
}

/************************************************
函数名称 : W25Qxx_Read_DeviceID
功    能 : 读 W25Qxx设备 ID
参    数 : 无
返 回 值 : id_num ---- 设备 ID
*************************************************/
uint8_t W25Qxx_Read_DeviceID(void)
{
	uint8_t id_num = 0;

    W25Q_CS(LOW);
	
	SPI_Flash_SendByte(W25Q_DEVICE_ID);
	SPI_Flash_SendByte(W25Q_DUMMY_BYTE);
	SPI_Flash_SendByte(W25Q_DUMMY_BYTE);
	SPI_Flash_SendByte(W25Q_DUMMY_BYTE);
	id_num = SPI_Flash_SendByte(W25Q_DUMMY_BYTE);
	
    W25Q_CS(HIGH);
	
	return id_num;
}

/************************************************
函数名称 : W25Qxx_Page_Program
功    能 : W25Qxx页编程(调用本函数写入数据前需要先擦除扇区)
参    数 : pBuffer ---- 数据
			Address ---- 地址
			Len ---- 长度
返 回 值 : 无
*************************************************/
void W25Qxx_Page_Program( uint8_t *pBuffer, uint32_t Address, uint16_t Len )
{
	W25Qxx_Write_Enable();
	
    W25Q_CS(LOW);
	
	SPI_Flash_SendByte(W25Q_PAGE_PROGRAM);
	SPI_Flash_SendByte((Address & 0xFF0000) >> 16);
	SPI_Flash_SendByte((Address & 0xFF00) >> 8);
	SPI_Flash_SendByte(Address & 0xFF);
	
	if(Len > W25Q_PAGE_SIZE)
	{
		Len = W25Q_PAGE_SIZE;
		W25Q_DUBUG_PRINTF("W25Qxx Page Program data too large!\n"); 
	}
	while(Len--)
	{
		SPI_Flash_SendByte(*pBuffer);
		pBuffer++;
	}
	
    W25Q_CS(HIGH);
	
	W25Qxx_Busy_Wait();
}

/************************************************
函数名称 : W25Qxx_Write_Flash
功    能 : 写 W25Qxx闪存数据(调用本函数写入数据前需要先擦除扇区)
参    数 : pBuffer ---- 数据
			Address ---- 地址
			Len ---- 长度
返 回 值 : 无
*************************************************/
void W25Qxx_Write_Flash( uint8_t *pBuffer, uint32_t Address, uint16_t Len )
{
	uint8_t NumOfPage = 0, NumOfSingle = 0;
	uint8_t Addr = 0, count = 0, temp = 0;
	
	/* mod运算求余,若 Address是 W25Q_PAGE_SIZE整数倍,运算结果 Addr值为 0 */
	Addr = Address % W25Q_PAGE_SIZE;
	
	/* 差count个数据值,刚好可以对齐到页地址 */
	count = W25Q_PAGE_SIZE - Addr;
	
	/* 计算出要写多少整数页 */
	NumOfPage =  Len / W25Q_PAGE_SIZE;
	
	/* 计算出剩余不满一页的字节数 */
	NumOfSingle = Len % W25Q_PAGE_SIZE;
	
	/* Addr = 0,则 Address刚好按页对齐 */
	if(0 == Addr)
	{
		/* Len <= W25Q_PAGE_SIZE */
		if(0 == NumOfPage) 
		{
			/* 不到一页 or 刚好一页 */
			W25Qxx_Page_Program(pBuffer, Address, Len);
		}
		else /* Len > W25Q_PAGE_SIZE */
		{ 
			/* 先把整数页的都写了 */
			while(NumOfPage--)
			{
				W25Qxx_Page_Program(pBuffer, Address, W25Q_PAGE_SIZE);
				Address += W25Q_PAGE_SIZE;
				pBuffer += W25Q_PAGE_SIZE;
			}
			/* 若有多余的不满一页的数据,下一页把它写完 */
			if(NumOfSingle != 0)
			{
				W25Qxx_Page_Program(pBuffer, Address, NumOfSingle);
			}
		}
	}
	/* 若地址与 W25Q_PAGE_SIZE不对齐  */
	else 
	{
		/* Len < W25Q_PAGE_SIZE */
		if(0 == NumOfPage)
		{
			/* 当前页剩余的 count个位置比 NumOfSingle小,一页写不完 */
			if(NumOfSingle > count) 
			{
				/* 先写满当前页 */
				W25Qxx_Page_Program(pBuffer, Address, count);
						
				temp = NumOfSingle - count;
				Address += count;
				pBuffer += count;
				/* 再写剩余的数据 */
				W25Qxx_Page_Program(pBuffer, Address, temp);
			}
			else /* 当前页剩余的 count个位置能写完 NumOfSingle个数据 */
			{
				W25Qxx_Page_Program(pBuffer, Address, Len);
			}
		}
		else /* Len > W25Q_PAGE_SIZE */
		{
			/* 地址不对齐多出的 count分开处理,不加入这个运算 */
			Len -= count;
			NumOfPage =  Len / W25Q_PAGE_SIZE;
			NumOfSingle = Len % W25Q_PAGE_SIZE;
			
			if(count != 0)
			{
				/* 先写完count个数据,为的是让下一次要写的地址对齐 */
				W25Qxx_Page_Program(pBuffer, Address, count);
					
				/* 接下来就重复地址对齐的情况 */
				Address +=  count;
				pBuffer += count;
			}
			
			/* 把整数页都写了 */
			while(NumOfPage--)
			{
				W25Qxx_Page_Program(pBuffer, Address, W25Q_PAGE_SIZE);
				Address +=  W25Q_PAGE_SIZE;
				pBuffer += W25Q_PAGE_SIZE;
			}
			/* 若有多余的不满一页的数据,把它写完*/
			if(NumOfSingle != 0)
			{
				W25Qxx_Page_Program(pBuffer, Address, NumOfSingle);
			}
		}
	}
}

/************************************************
函数名称 : W25Qxx_Read_Flash
功    能 : 读 W25Qxx闪存数据
参    数 : pBuffer ---- 数据
			Address ---- 地址
			Len ---- 长度
返 回 值 : 无
*************************************************/
void W25Qxx_Read_Flash( uint8_t *pBuffer, uint32_t Address, uint16_t Len )
{
    W25Q_CS(LOW);
	
	SPI_Flash_SendByte(W25Q_READ_DATA);
	SPI_Flash_SendByte((Address & 0xFF0000) >> 16);
	SPI_Flash_SendByte((Address & 0xFF00) >> 8);
	SPI_Flash_SendByte(Address & 0xFF);
	
	/* 读取数据 */
	while(Len--)
	{
		*pBuffer = SPI_Flash_SendByte(W25Q_DUMMY_BYTE);
		pBuffer++;
	}
	
    W25Q_CS(HIGH);
}

/************************************************
函数名称 : W25Qxx_Sector_Erase
功    能 : FLASH扇区擦除
参    数 : Address ---- 擦除地址
返 回 值 : 无
*************************************************/
void W25Qxx_Sector_Erase( uint32_t Address )
{
	W25Qxx_Write_Enable();
	
    W25Q_CS(LOW);
	
	SPI_Flash_SendByte(W25Q_SECTOR_ERASE);
	SPI_Flash_SendByte((Address & 0xFF0000) >> 16);
	SPI_Flash_SendByte((Address & 0xFF00) >> 8);
	SPI_Flash_SendByte(Address & 0xFF);

    W25Q_CS(HIGH);
	
	W25Qxx_Busy_Wait();
}

/************************************************
函数名称 : W25Qxx_Chip_Erase
功    能 : FLASH整片擦除(为了安全起见,若要调用,请先调用 W25Qxx_Write_Enable函数)
参    数 : 无
返 回 值 : 无
*************************************************/
void W25Qxx_Chip_Erase(void)
{
    W25Q_CS(LOW);
	
	SPI_Flash_SendByte(W25Q_CHIP_ERASE);
	
    W25Q_CS(HIGH);
	
	W25Qxx_Busy_Wait();
}

/************************************************
函数名称 : W25Qxx_Write_Enable
功    能 : W25Qxx写使能
参    数 : 无
返 回 值 : 无
*************************************************/
void W25Qxx_Write_Enable(void)
{
	uint8_t flash_status = 0;

    W25Q_CS(LOW);
    SPI_Flash_SendByte(W25Q_WRITE_ENABLE);
    W25Q_CS(HIGH);
		
    W25Q_CS(LOW);
	/* 等待写使能位置 1 */
	do
	{
		flash_status = SPI_Flash_SendByte(W25Q_STATUS_REG1);
	}while(!(flash_status & BIT_WEL));
    W25Q_CS(HIGH);
}

/************************************************
函数名称 : W25Qxx_Write_Disable
功    能 : W25Qxx写失能
参    数 : 无
返 回 值 : 无
*************************************************/
void W25Qxx_Write_Disable(void)
{
	uint8_t flash_status = 0;

    W25Q_CS(LOW);
    SPI_Flash_SendByte(W25Q_WRITE_DISABLE);
    W25Q_CS(HIGH);
	
    W25Q_CS(LOW);
	/* 等待写使能清 0 */
	do
	{
		flash_status = SPI_Flash_SendByte(W25Q_STATUS_REG1);
	}while(!(flash_status & BIT_WEL));
    W25Q_CS(HIGH);

}

/************************************************
函数名称 : W25Qxx_Power_Down
功    能 : W25Qxx掉电
参    数 : 无
返 回 值 : 无
*************************************************/
void W25Qxx_Power_Down(void)
{
    W25Q_CS(LOW);
    SPI_Flash_SendByte(W25Q_POWER_DOWN);
    W25Q_CS(HIGH);
}

/************************************************
函数名称 : W25Qxx_Release_PowerDown
功    能 : W25Qxx唤醒
参    数 : 无
返 回 值 : 无
*************************************************/
void W25Qxx_Release_PowerDown(void)
{
    W25Q_CS(LOW);
    SPI_Flash_SendByte(W25Q_RELEASE_POWER_DOWN);
    W25Q_CS(HIGH);
}

/************************************************
函数名称 : W25Qxx_Config
功    能 : W25Qxx配置
参    数 : 无
返 回 值 : 无
*************************************************/
void W25Qxx_Config(void)
{
	
#if USE_SIMULATE_SPI
	GPIO_InitTypeDef GPIO_InitStructure;
	
	W25Q_CS_APBxClock_FUN(W25Q_CS_CLK, ENABLE);
	
	/* Confugure CS pin as Output Push Pull */
	GPIO_InitStructure.GPIO_Pin = W25Q_CS_PINS;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_Init(W25Q_CS_PORT, &GPIO_InitStructure);

	/* Smiulate IO Config */
	Simulate_SPI_Config();
	
	W25Q_CS(HIGH);
	SPI_SCK(HIGH);
	SPI_MOSI(HIGH);

#else
	GPIO_InitTypeDef GPIO_InitStructure;
	SPI_InitTypeDef SPI_InitStructure;

	/* W25Q_SPIx IO Periph clock enable */
	W25Q_IO_APBxClock_FUN(W25Q_CS_CLK | W25Q_SCK_CLK 	
								| W25Q_MISO_CLK | W25Q_MOSI_CLK, ENABLE);
	
	/* W25Q_SPIx Periph clock enable */
	W25Q_SPI_APBxClock_FUN(W25Q_SPI_CLK, ENABLE);

	/* Configure W25Q_SPIx pins: CS, SCK, MISO and MOSI */	
	/* Confugure CS pin as Output Push Pull */
	GPIO_InitStructure.GPIO_Pin = W25Q_CS_PINS;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_Init(W25Q_CS_PORT, &GPIO_InitStructure);

	/* Confugure SCK and MOSI pins as Alternate Function Push Pull */
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	
	GPIO_InitStructure.GPIO_Pin = W25Q_SCK_PINS;
	GPIO_Init(W25Q_SCK_PORT, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = W25Q_MOSI_PINS;
	GPIO_Init(W25Q_MOSI_PORT, &GPIO_InitStructure);
	
	/* Confugure MISO pin as Input Floating */
	GPIO_InitStructure.GPIO_Pin = W25Q_MISO_PINS;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(W25Q_MISO_PORT, &GPIO_InitStructure);
	
	/* ---------- END ---------- */
	
	W25Q_CS(HIGH);
	
	/* W25Q_SPIx configuration */
	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
	SPI_InitStructure.SPI_CRCPolynomial = 7;
	SPI_Init(W25Q_SPIx, &SPI_InitStructure);
	
	/* Disable W25Q_SPIx CRC calculation */
	SPI_CalculateCRC(W25Q_SPIx, DISABLE);

	/* Enable W25Q_SPIx */
	SPI_Cmd(SPI1, ENABLE);
	
#endif /* USE_SIMULATE_SPI */
}

/************************************************
函数名称 : W25Qxx_Init
功    能 : W25Qxx初始化
参    数 : 无
返 回 值 : 无
*************************************************/
void W25Qxx_Init(void)
{
	uint32_t FlashID = 0;
	
	W25Qxx_Config();
	
#if(_W25Q_DUBUG)
	FlashID = W25Qxx_Read_JEDECID();
	W25Q_DUBUG_PRINTF("FlashID is 0x%X,Manufacturer Device ID is 0x%X\r\n",	\
				FlashID, W25Qxx_Read_DeviceID());
	if(FlashID != JEDEC_ID)
	{
		/* 读取错误处理 */
		W25Q_DUBUG_PRINTF("SPI read-write Error, please check the connection between MCU and SPI Flash\n");
	}
	else
	{
		/* 读取成功处理 */
		W25Q_DUBUG_PRINTF("SPI read-write succeed\n");
		
//		uint8_t Tx_buff[] = "FLASH读写测试实验\r\n";
//		uint8_t Rx_buff[] = "FLASH读写测试实验\r\n";

//		W25Qxx_Sector_Erase(0x0000);
//		W25Qxx_Write_Flash(Tx_buff, 0x0000, (sizeof(Tx_buff) / sizeof(*(Tx_buff))));
//		W25Qxx_Read_Flash(Rx_buff, 0x0000, (sizeof(Tx_buff) / sizeof(*(Tx_buff))));
//		W25Q_DUBUG_PRINTF("读出的数据:%s\n", Rx_buff);
	}

#endif /* _W25Q_DUBUG */
}


/*---------------------------- END OF FILE ----------------------------*/


代码:BasicDriver/W25Qxx at master · Arachnid-97/BasicDriver · GitHub

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐