使用stm32的模拟spi读取w25q128读取设备ID时一直出现0xFF

这篇具有很好参考价值的文章主要介绍了使用stm32的模拟spi读取w25q128读取设备ID时一直出现0xFF。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

 flash的id是0xffff不能正常使用,stm32,嵌入式硬件,单片机flash的id是0xffff不能正常使用,stm32,嵌入式硬件,单片机

上图是stm32所对应的引脚

flash的id是0xffff不能正常使用,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博客文章来源地址https://www.toymoban.com/news/detail-765976.html

到了这里,关于使用stm32的模拟spi读取w25q128读取设备ID时一直出现0xFF的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • [10min速通]STM32CubemMX配置W25Q128

    下载github开源驱动代码。 GitHub - nimaltd/w25qxx: w25qxx SPI FLASH driver for stm32 HAL 此处声明此工程不是本人所作。 这个工程的作者在Readme中提供的视频教程配置非常详细了,会魔法的同学可以直接去看视频进行配置,不用看此文。 如果访问不了github,可以直接下载文末的资料,打包

    2024年02月12日
    浏览(16)
  • STM32CUBUMX配置FLASH(W25Q128)--保姆级教程

    ———————————————————————————————————— ⏩ 大家好哇!我是小光,嵌入式爱好者,一个想要成为系统架构师的大三学生。 ⏩最近在开发一个STM32H723ZGT6的板子,使用STM32CUBEMX做了很多驱动,包括ADC、UART、RS485、EEPROM(IIC)、FLASH(SPI)等等。

    2024年02月15日
    浏览(23)
  • STM32CubeMX教程26 FatFs 文件系统 - W25Q128读写

    正点原子stm32f407探索者开发板V2.4 STM32CubeMX软件(Version 6.10.0) keil µVision5 IDE(MDK-Arm) ST-LINK/V2驱动 野火DAP仿真器 XCOM V2.6串口助手 使用STM32CubeMX软件配置STM32F407开发板 使用FatFs中间件通过SPI通信协议对W25Q128芯片进行读写等操作 关于STM32F407使用SPI通信协议对W25Q128 FLASH芯片读写

    2024年02月19日
    浏览(23)
  • W25Q128芯片手册精读

    之前写SPI通信的时候用到了W25Q128,其中对芯片手册的阅读我只写了我们所需要的的部分。 这篇博客就对这个芯片进行详细的阅读并记录,文章可能会比较长,但绝对是结合了自己的理解进行阐述。 芯片手册刚开始看的时候最大的拦路虎其实就是英文,看习惯了中文,直接看

    2024年02月07日
    浏览(18)
  • 【STM32篇】SPI时序驱动W25Q64(硬件SPI和模拟SPI)

            由于MCU的FLASH空间有限,在特殊使用场所中会存在FLASH存储不够使用的情况。例如上篇中驱动LCD屏,需要将一个中文字库保存到MCU的FLASH中是不太现实的(STM32F103ZET6内部FLASH大小512KB),为此可使用外部FLASH作为拓展。         W25Q64(64Mbit)是为系统提供一个最小的空

    2024年02月08日
    浏览(20)
  • STM32-SPI通信(W25Q64芯片简介,使用SPI读写W25Q64存储器芯片)

    ​  SPI(Serial Peripheral Interface)是由Motorola公司开发的一种通用数据总线四根通信线:SCK(Serial Clock)、MOSI(Master Output Slave Input)、MISO(Master Input Slave Output)、SS(Slave Select)。 ​SPI通信具有以下特点: 同步,全双工; 支持总线挂载多设备(SPI仅支持一主多从); 在不

    2024年02月08日
    浏览(19)
  • SPI FLASH(W25Q128BV) 包含SPI工作原理

    目录   一、SPI简介         1、全双工与半双工          2、同步与异步         3、SPI通信方式 二、SPI工作模式 三、W25Q128BV         1、读ID Read Manufacturer/Device ID(90h)                   2、读ID代码实现(硬件SPI)          3、IO口模拟SPI时序图实现 (软件SPI)  模式

    2024年02月14日
    浏览(15)
  • 【STM32】SPI初步使用 读写FLASH W25Q64

    (1) SS( Slave Select):从设备选择信号线,常称为片选信号线,每个从设备都有独立的这一条 NSS 信号线,当主机要选择从设备时,把该从设备的 NSS 信号线设置为低电平,该从设备即被选中,即片选有效,接着主机开始与被选中的从设备进行 SPI通讯。所以 SPI通讯以 NSS 线置低电

    2024年02月10日
    浏览(21)
  • stm32(SPI读写W25Q18)

    SPI是串行外设接口(Serial Peripheral Interface)的缩写,是一种 高速的,全双工,同步 的通信总 线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提 供方便,正是出于这种简单易用的特性,越来越多的芯片集成了这种通信协议,比如 A

    2024年02月16日
    浏览(32)
  • 2023版 STM32实战11 SPI总线读写W25Q

    英文全称:Serial peripheral Interface 串行外设接口 -1- 串行(逐bit传输) -2- 同步(共用时钟线) -3- 全双工(收发可同时进行) -4- 通信只能由主机发起(一主,多从机) -1- CS片选一般配置为软件控制 -2- 片选低电平有效,从器件CS引脚可直接连接GND -3- 从机不能主动给主机发数据 -4- 主机想要

    2024年02月08日
    浏览(14)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包