解决STM32模拟SPI读取W25Q128设备ID时持续出现0xFF问题

由于公司的电路是前辈画的,只能使用模拟spi中如图所示

 

上图是stm32所对应的引脚

 上图是w25q128的引脚

当读取的时候ID号一直是0xffff,在网上查了各种方法都试过了都不行,我这个情况稍微特殊,就是使用了PB3、PB4这两个引脚上电复位默认是作为调试端口使用的。所以得先关闭JTAG功能才行

GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);//关闭pb3、4,pa15的JTAG功能,打开sw调试功能

初始化时,PB3、PB4做普通io需打开复用功能

RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);   //pb3,4做普通io需打开复用功能

#include "spi_flash.h"


#define W25Q32_PageSize 256
//指令表
#define W25X_PowerDown 0xb9					//休眠
#define W25X_ReleasePowerDown 0xab	//唤醒

#define W25X_ReadStatusReg 0x05			//读控制和状态寄存器
#define W25X_WriteStatusReg 0x01		//写控制与状态寄存器
#define W25X_WriteEnable 0x06        //写使能    
#define W25X_WriteDisable 0x04       //写禁用
#define W25X_ReadData 0x03					//读数据
#define W25X_PageProgram 0x02					//页编程
#define W25X_ChipErase 0x60					//整页擦除
#define W25X_SectorErase 0x20				//扇区擦除

#define DO_H() (GPIO_SetBits(GPIOB, GPIO_Pin_3))
#define DO_L() (GPIO_ResetBits(GPIOB, GPIO_Pin_3))

#define SCK_H() (GPIO_SetBits(GPIOD, GPIO_Pin_2))
#define SCK_L() (GPIO_ResetBits(GPIOD, GPIO_Pin_2))

#define DI_STATE() (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_5))

#define SPIFLASH_CS_LOW() (GPIO_ResetBits(GPIOB, GPIO_Pin_4))
#define SPIFLASH_CS_HIGH() (GPIO_SetBits(GPIOB, GPIO_Pin_4))


static void SpiWriteByte(uint8_t data)    //写字节
{

	register unsigned char i;

	SCK_L();
	for (i = 0; i < 8; i++)
	{
		delay_us(4);
		// MOSI
		if (data & 0x80)
			DO_H();
		else
			DO_L();
		delay_us(4);
		SCK_H();
		delay_us(4);
		data <<= 1;
		SCK_L();
		delay_us(4);
	}
	//delay_ms(1);
}

static uint8_t SpiReadByte(void)    //读字节
{

	register unsigned char i, out;

	out = 0;

	for (i = 0; i < 8; i++)
	{
		delay_us(4);
		SCK_H();
		out <<= 1;
		delay_us(4);
		if (DI_STATE())
			out |= 0x01;
		delay_us(4);
		SCK_L();
		delay_us(4);
	}
	return out;
}


void SPI_Flash_Init(void)
{

	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOD, ENABLE);	//使能USART1,GPIOA时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);   //pb3,4做普通io需打开复用功能
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_Init(GPIOB, &GPIO_InitStructure);

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_Init(GPIOD, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	SPIFLASH_CS_HIGH();
	SPI_Flash_WAKEUP();    //唤醒
//W25QXX_Flash_ReadID(); //读ID
	
}



//读取W25QXX的状态寄存器
//BIT7  6   5   4   3   2   1   0
//SPR   RV  TB BP2 BP1 BP0 WEL BUSY
//SPR:默认0,状态寄存器保护位,配合WP使用
//TB,BP2,BP1,BP0:FLASH区域写保护设置
//WEL:写使能锁定
//BUSY:忙标记位(1,忙;0,空闲)
//默认:0x00
uint8_t W25QXX_Flash_ReadSR(void)     //读控制和状态寄存器
{
	uint8_t byte = 0;
	SPIFLASH_CS_LOW();				  //
	SpiWriteByte(W25X_ReadStatusReg); //
	byte = SpiReadByte();			  //
	SPIFLASH_CS_HIGH();				  //
	return byte;
}

/*static void SPI_FLASH_Write_SR(uint8_t sr)
{
	SPIFLASH_CS_LOW();// 
	SpiWriteByte(W25X_WriteStatusReg);// 
	SpiWriteByte(sr);// 
	SPIFLASH_CS_HIGH();// 
}*/


//写W25QXX状态寄存器
//只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!
static void SPI_FLASH_Write_Enable(void)
{
	SPIFLASH_CS_LOW();				//
	SpiWriteByte(W25X_WriteEnable); //
	SPIFLASH_CS_HIGH();				//
}

/*static void SPI_FLASH_Write_Disable(void)
{
	SPIFLASH_CS_LOW();// 
	SpiWriteByte(W25X_WriteDisable);//
	SPIFLASH_CS_HIGH();//
}*/


//读取芯片ID
//返回值如下:				   
//0XEF13,表示芯片型号为W25Q80  
//0XEF14,表示芯片型号为W25Q16    
//0XEF15,表示芯片型号为W25Q32  
//0XEF16,表示芯片型号为W25Q64 
//0XEF17,表示芯片型号为W25Q128

uint16_t W25QXX_Flash_ReadID(void)
{
	uint16_t Temp = 0;
	SPIFLASH_CS_LOW();
	SpiWriteByte(0x90);
	SpiWriteByte(0x00);
	SpiWriteByte(0x00);
	SpiWriteByte(0x00);
	
	Temp |= SpiReadByte() << 8;
	Temp |= SpiReadByte();
	SPIFLASH_CS_HIGH();
	return Temp;
}
//读取SPI FLASH  
//在指定地址开始读取指定长度的数据
//pBuffer:数据存储区
//ReadAddr:开始读取的地址(24bit)
//NumByteToRead:要读取的字节数(最大65535)
void SPI_Flash_Read(uint32_t ReadAddr, uint16_t *pBuffer, uint16_t NumByteToRead)
{
	uint16_t i;
	SPIFLASH_CS_LOW();						   //
	SpiWriteByte(W25X_ReadData);			   //
	SpiWriteByte((uint8_t)((ReadAddr) >> 16)); //
	SpiWriteByte((uint8_t)((ReadAddr) >> 8));
	SpiWriteByte((uint8_t)ReadAddr);
	for (i = 0; i < NumByteToRead; i++)
	{
		pBuffer[i] = (SpiReadByte() << 8) & 0xff00; //
		pBuffer[i] |= (SpiReadByte() & 0xff);
	}
	SPIFLASH_CS_HIGH();
}
//SPI在一页(0~65535)内写入少于256个字节的数据
//在指定地址开始写入最大256字节的数据
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!	
void SPI_Flash_Write_Page(uint16_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
	uint16_t i;
	SPI_FLASH_Write_Enable();					//SET WEL
	SPIFLASH_CS_LOW();							//
	SpiWriteByte(W25X_PageProgram);				//
	SpiWriteByte((uint8_t)((WriteAddr) >> 16)); //
	SpiWriteByte((uint8_t)((WriteAddr) >> 8));
	SpiWriteByte((uint8_t)WriteAddr);
	for (i = 0; i < NumByteToWrite; i++)
	{
		SpiWriteByte((pBuffer[i] >> 8) & 0xff); //
		SpiWriteByte(pBuffer[i] & 0xff);		//
	}
	SPIFLASH_CS_HIGH();	   //
	SPI_Flash_Wait_Busy(); //
}


//无检验写SPI FLASH 
//必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
//具有自动换页功能 
//在指定地址开始写入指定长度的数据,但是要确保地址不越界!
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(最大65535)
//CHECK OK
 void SPI_Flash_Write_NoCheck(uint16_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
	uint16_t pageremain;
	pageremain = 128 - (WriteAddr % 256) / 2;
	if (NumByteToWrite <= pageremain)
		pageremain = NumByteToWrite; //
	while (1)
	{
		SPI_Flash_Write_Page(pBuffer, WriteAddr, pageremain);
		if (NumByteToWrite == pageremain)
			break; //
		else	   //NumByteToWrite>pageremain
		{
			pBuffer += pageremain;
			WriteAddr += pageremain * 2;

			NumByteToWrite -= pageremain; //
			if (NumByteToWrite > 128)
				pageremain = 128; //
			else
				pageremain = NumByteToWrite; //
		}
	};
}



//写SPI FLASH  
//在指定地址开始写入指定长度的数据
//该函数带擦除操作!
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)						
//NumByteToWrite:要写入的字节数(最大65535) 
#define SPIFLASH_SEC_SIZE (4096)
#define SPIFLASH_BASE (0)
#define SPIFLASH_SIZE (128 / 8 * 1024 * 1024) //16MBytes
uint16_t SPI_FLASH_BUFFER[SPIFLASH_SEC_SIZE / 2];
void SPI_Flash_Write(uint32_t WriteAddr, uint16_t *pBuffer, uint16_t NumByteToWrite)
{
	uint32_t secpos;
	uint16_t secoff;
	uint16_t secremain;
	uint16_t i;
	uint32_t offaddr;

	

	if (WriteAddr >= SPIFLASH_SIZE)
		return;
	offaddr = WriteAddr - SPIFLASH_BASE;
	secpos = offaddr / SPIFLASH_SEC_SIZE;			//扇区地址 
	secoff = (offaddr % SPIFLASH_SEC_SIZE) / 2;//在扇区内的偏移
	secremain = SPIFLASH_SEC_SIZE / 2 - secoff;//扇区剩余空间大小
	if (NumByteToWrite <= secremain)  //不大于4096个字节
		secremain = NumByteToWrite;
	while (1)
	{
//		IWDG_ReloadCounter();
		SPI_Flash_Read(secpos * SPIFLASH_SEC_SIZE + SPIFLASH_BASE, SPI_FLASH_BUFFER, SPIFLASH_SEC_SIZE / 2);//读出整个扇区的内容
		for (i = 0; i < secremain; i++)//校验数据
		{
			if (SPI_FLASH_BUFFER[secoff + i] != 0XFFFF)
				break;
		}
		if (i < secremain)
		{
			SPI_Flash_Erase_Sector_addr((secpos * SPIFLASH_SEC_SIZE + SPIFLASH_BASE) / SPIFLASH_SEC_SIZE);

			for (i = 0; i < secremain; i++)
			{
				SPI_FLASH_BUFFER[i + secoff] = pBuffer[i];
			}
			SPI_Flash_Write_NoCheck(SPI_FLASH_BUFFER, secpos * SPIFLASH_SEC_SIZE + SPIFLASH_BASE, SPIFLASH_SEC_SIZE / 2);
		}
		else
			SPI_Flash_Write_NoCheck(pBuffer, WriteAddr, secremain);

		if (NumByteToWrite == secremain)
			break;
		else
		{
			secpos++;
			secoff = 0;
			pBuffer += secremain;
			WriteAddr += secremain;
			NumByteToWrite -= secremain;
			if (NumByteToWrite > (SPIFLASH_SEC_SIZE / 2))
				secremain = SPIFLASH_SEC_SIZE / 2;
			else
				secremain = SPIFLASH_SEC_SIZE;
		}
	};
}
//擦除整个芯片		  
//等待时间超长...
void SPI_Flash_Erase_Chip(void)  //擦除芯片
{
	SPI_FLASH_Write_Enable(); //SET WEL
	SPI_Flash_Wait_Busy();
	SPIFLASH_CS_LOW();			  //
	SpiWriteByte(W25X_ChipErase); //
	SPIFLASH_CS_HIGH();			  //
	SPI_Flash_Wait_Busy();		  //
}

//擦除一个扇区
//Dst_Addr:扇区地址 根据实际容量设置
//擦除一个扇区的最少时间:150ms
void SPI_Flash_Erase_Sector_addr(uint32_t Dst_Addr)    //擦除扇区地址
{
	Dst_Addr *= 4096;
	SPI_FLASH_Write_Enable(); //SET WEL
	SPI_Flash_Wait_Busy();
	SPIFLASH_CS_LOW();						   //
	SpiWriteByte(W25X_SectorErase);			   //
	SpiWriteByte((uint8_t)((Dst_Addr) >> 16)); //
	SpiWriteByte((uint8_t)((Dst_Addr) >> 8));
	SpiWriteByte((uint8_t)Dst_Addr);
	SPIFLASH_CS_HIGH();	//
	SPI_Flash_Wait_Busy(); //
}

FLASH_Status SPI_Flash_Erase_Sector(uint32_t Dst_Addr)//擦除扇区
{
	//Dst_Addr *= 4096;
	SPI_FLASH_Write_Enable(); //SET WEL
	SPI_Flash_Wait_Busy();
	SPIFLASH_CS_LOW();						   //
	SpiWriteByte(W25X_SectorErase);			   //
	SpiWriteByte((uint8_t)((Dst_Addr) >> 16)); //
	SpiWriteByte((uint8_t)((Dst_Addr) >> 8));
	SpiWriteByte((uint8_t)Dst_Addr);
	SPIFLASH_CS_HIGH();	   //
	SPI_Flash_Wait_Busy(); //
	return FLASH_COMPLETE;
}
//等待空闲
void SPI_Flash_Wait_Busy(void)
{
	uint16_t ii = 0;
	while ((W25QXX_Flash_ReadSR() & 0x01) == 0x01)
	{
		ii++; //
		if (ii > 10000)
		{
			ii = 10001;
		}
		else
		{
			delay_ms(1);
//			IWDG_ReloadCounter();
		}
	}
}
//进入掉电模式
void SPI_Flash_PowerDown(void)
{
	SPIFLASH_CS_LOW();			  //
	SpiWriteByte(W25X_PowerDown); //
	SPIFLASH_CS_HIGH();			  //
	delay_us(3);				  //
}
//唤醒
void SPI_Flash_WAKEUP(void)
{
	SPIFLASH_CS_LOW();					 //
	SpiWriteByte(W25X_ReleasePowerDown); //
	SPIFLASH_CS_HIGH();					 //
	delay_us(3);						 //
}

详细配置看这位大佬:STM32F10××× PB3,PB4,PA13,PA14,PA15的使用_RZA的博客-CSDN博客

物联沃分享整理
物联沃-IOTWORD物联网 » 解决STM32模拟SPI读取W25Q128设备ID时持续出现0xFF问题

发表评论