博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
STM32笔记之 SPI(硬件 or 模拟实现)
阅读量:779 次
发布时间:2019-03-24

本文共 19307 字,大约阅读时间需要 64 分钟。

写在前面:

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

 

目录


 

一、SPI协议

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

关于 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由低电平到高电平的跳变,所以数据采样是在上升沿,数据发送是在下降沿。 * MODE 1:CPOL=0,CPHA=1:此时空闲态时,SCLK处于低电平,数据发送是在第 2个边沿,	\ * 			也就是 SCLK由高电平到低电平的跳变,所以数据采样是在下降沿,数据发送是在上升沿。 * MODE 2:CPOL=1,CPHA=0:此时空闲态时,SCLK处于高电平,数据采集是在第 1个边沿,	\ * 			也就是 SCLK由高电平到低电平的跳变,所以数据采集是在下降沿,数据发送是在上升沿。 * MODE 3:CPOL=1,CPHA=1:此时空闲态时,SCLK处于高电平,数据发送是在第 2个边沿,	\ * 			也就是 SCLK由低电平到高电平的跳变,所以数据采集是在上升沿,数据发送是在下降沿。*/

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 ----------------------------*/

 

代码:

转载地址:http://wgtkk.baihongyu.com/

你可能感兴趣的文章