硬汉嵌入式论坛

 找回密码
 立即注册
查看: 144|回复: 11
收起左侧

[SPI/QSPI] sf_AutoWriteSector 函数疑问

[复制链接]

1

主题

6

回帖

9

积分

新手上路

积分
9
QQ
发表于 2025-9-18 15:45:24 | 显示全部楼层 |阅读模式



1,   实际编程了一个扇区, 为什么 sf_CmpData 不是比较整个扇区 ?
2, 如果 sf_CmpData  比较失败不需要执行擦除扇区操作吗 ?
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
118335
QQ
发表于 2025-9-18 17:18:54 | 显示全部楼层
我们的驱动只有个sf_AutoWritePage

[C] 纯文本查看 复制代码
/*
*********************************************************************************************************
*
*	模块名称 : SPI接口串行FLASH 读写模块
*	文件名称 : bsp_spi_flash.c
*	版    本 : V1.0
*	说    明 : 支持 SST25VF016B、MX25L1606E 和 W25Q64BVSSIG
*			   SST25VF016B 的写操作采用AAI指令,可以提高写入效率。
*
*			   缺省使用STM32F4的硬件SPI1接口,时钟频率最高为 42MHz (超频使用)
*
*	修改记录 :
*		版本号  日期        作者     说明
*		V1.0    2013-02-01 armfly  正式发布
*
*	Copyright (C), 2013-2014, 安富莱电子 [url]www.armfly.com[/url]
*
*********************************************************************************************************
*/

#include "bsp.h"

/* 串行Flsh的片选GPIO端口, PD13  */
#define SF_CS_CLK_ENABLE() 			__HAL_RCC_GPIOD_CLK_ENABLE()
#define SF_CS_GPIO					GPIOD
#define SF_CS_PIN					GPIO_PIN_13
#define SF_CS_0()					SF_CS_GPIO->BSRRH = SF_CS_PIN
#define SF_CS_1()					SF_CS_GPIO->BSRRL = SF_CS_PIN
	
#define CMD_AAI       0xAD  	/* AAI 连续编程指令(FOR SST25VF016B) */
#define CMD_DISWR	  0x04		/* 禁止写, 退出AAI状态 */
#define CMD_EWRSR	  0x50		/* 允许写状态寄存器的命令 */
#define CMD_WRSR      0x01  	/* 写状态寄存器命令 */
#define CMD_WREN      0x06		/* 写使能命令 */
#define CMD_READ      0x03  	/* 读数据区命令 */
#define CMD_RDSR      0x05		/* 读状态寄存器命令 */
#define CMD_RDID      0x9F		/* 读器件ID命令 */
#define CMD_SE        0x20		/* 擦除扇区命令 */
#define CMD_BE        0xC7		/* 批量擦除命令 */
#define DUMMY_BYTE    0xA5		/* 哑命令,可以为任意值,用于读操作 */

#define WIP_FLAG      0x01		/* 状态寄存器中的正在编程标志(WIP) */

SFLASH_T g_tSF;

#if 0
static void sf_WriteStatus(uint8_t _ucValue);
#endif

static void sf_WriteEnable(void);
static void sf_WaitForWriteEnd(void);
static uint8_t sf_NeedErase(uint8_t * _ucpOldBuf, uint8_t *_ucpNewBuf, uint16_t _uiLen);
static uint8_t sf_CmpData(uint32_t _uiSrcAddr, uint8_t *_ucpTar, uint32_t _uiSize);
static uint8_t sf_AutoWritePage(uint8_t *_ucpSrc, uint32_t _uiWrAddr, uint16_t _usWrLen);

static uint8_t s_spiBuf[4*1024];	/* 用于写函数,先读出整个page,修改缓冲区后,再整个page回写 */

/*
*********************************************************************************************************
*	函 数 名: bsp_InitSFlash
*	功能说明: 串行falsh硬件初始化。 配置CS GPIO, 读取ID。
*	形    参: 无
*	返 回 值: 无
*********************************************************************************************************
*/
void bsp_InitSFlash(void)
{
	/* 配置CS GPIO */
	{
		GPIO_InitTypeDef gpio_init;

		/* 打开GPIO时钟 */
		SF_CS_CLK_ENABLE();
		
		gpio_init.Mode = GPIO_MODE_OUTPUT_PP;		/* 设置推挽输出 */
		gpio_init.Pull = GPIO_NOPULL;				/* 上下拉电阻不使能 */
		gpio_init.Speed = GPIO_SPEED_FREQ_HIGH;  	/* GPIO速度等级 */	
		gpio_init.Pin = SF_CS_PIN;	
		HAL_GPIO_Init(SF_CS_GPIO, &gpio_init);	
	}
	
	/* 读取芯片ID, 自动识别芯片型号 */
	sf_ReadInfo();
}	

/*
*********************************************************************************************************
*	函 数 名: sf_SetCS
*	功能说明: 串行FALSH片选控制函数
*	形    参: 无
*	返 回 值: 无
*********************************************************************************************************
*/
void sf_SetCS(uint8_t _Level)
{
	if (_Level == 0)
	{
		bsp_SpiBusEnter();	
		bsp_InitSPIParam(SPI_BAUDRATEPRESCALER_8, SPI_PHASE_1EDGE, SPI_POLARITY_LOW);		
		SF_CS_0();
	}
	else
	{		
		SF_CS_1();	
		bsp_SpiBusExit();		
	}
}

/*
*********************************************************************************************************
*	函 数 名: sf_EraseSector
*	功能说明: 擦除指定的扇区
*	形    参:  _uiSectorAddr : 扇区地址
*	返 回 值: 无
*********************************************************************************************************
*/
void sf_EraseSector(uint32_t _uiSectorAddr)
{
	sf_WriteEnable();								/* 发送写使能命令 */

	/* 擦除扇区操作 */
	sf_SetCS(0);									/* 使能片选 */
	g_spiLen = 0;
	g_spiTxBuf[g_spiLen++] = CMD_SE;						/* 发送擦除命令 */
	g_spiTxBuf[g_spiLen++] = ((_uiSectorAddr & 0xFF0000) >> 16);	/* 发送扇区地址的高8bit */
	g_spiTxBuf[g_spiLen++] = ((_uiSectorAddr & 0xFF00) >> 8);		/* 发送扇区地址中间8bit */
	g_spiTxBuf[g_spiLen++] = (_uiSectorAddr & 0xFF);				/* 发送扇区地址低8bit */	
	bsp_spiTransfer();
	sf_SetCS(1);									/* 禁能片选 */

	sf_WaitForWriteEnd();							/* 等待串行Flash内部写操作完成 */
}

/*
*********************************************************************************************************
*	函 数 名: sf_EraseChip
*	功能说明: 擦除整个芯片
*	形    参:  无
*	返 回 值: 无
*********************************************************************************************************
*/
void sf_EraseChip(void)
{	
	sf_WriteEnable();								/* 发送写使能命令 */

	/* 擦除扇区操作 */
	sf_SetCS(0);		/* 使能片选 */
	g_spiLen = 0;
	g_spiTxBuf[g_spiLen++] = CMD_BE;				/* 发送整片擦除命令 */
	bsp_spiTransfer();
	sf_SetCS(1);									/* 禁能片选 */

	sf_WaitForWriteEnd();							/* 等待串行Flash内部写操作完成 */
}

/*
*********************************************************************************************************
*	函 数 名: sf_PageWrite
*	功能说明: 向一个page内写入若干字节。字节个数不能超出页面大小(4K)
*	形    参:  	_pBuf : 数据源缓冲区;
*				_uiWriteAddr :目标区域首地址
*				_usSize :数据个数,不能超过页面大小
*	返 回 值: 无
*********************************************************************************************************
*/
void sf_PageWrite(uint8_t * _pBuf, uint32_t _uiWriteAddr, uint16_t _usSize)
{
	uint32_t i, j;

	if (g_tSF.ChipID == SST25VF016B_ID)
	{
		/* AAI指令要求传入的数据个数是偶数 */
		if ((_usSize < 2) && (_usSize % 2))
		{
			return ;
		}

		sf_WriteEnable();								/* 发送写使能命令 */

		sf_SetCS(0);									/* 使能片选 */
		g_spiLen = 0;
		g_spiTxBuf[g_spiLen++] = CMD_AAI;								/* 发送AAI命令(地址自动增加编程) */
		g_spiTxBuf[g_spiLen++] = ((_uiWriteAddr & 0xFF0000) >> 16);		/* 发送扇区地址的高8bit */
		g_spiTxBuf[g_spiLen++] = ((_uiWriteAddr & 0xFF00) >> 8);		/* 发送扇区地址中间8bit */
		g_spiTxBuf[g_spiLen++] = (_uiWriteAddr & 0xFF);					/* 发送扇区地址低8bit */		
		g_spiTxBuf[g_spiLen++] = (*_pBuf++);							/* 发送第1个数据 */
		g_spiTxBuf[g_spiLen++] = (*_pBuf++);							/* 发送第2个数据 */
		bsp_spiTransfer();
		sf_SetCS(1);									/* 禁能片选 */

		sf_WaitForWriteEnd();							/* 等待串行Flash内部写操作完成 */

		_usSize -= 2;									/* 计算剩余字节数 */

		for (i = 0; i < _usSize / 2; i++)
		{
			sf_SetCS(0);	/* 使能片选 */	
			g_spiLen = 0;
			g_spiTxBuf[g_spiLen++] = (CMD_AAI);						/* 发送AAI命令(地址自动增加编程) */
			g_spiTxBuf[g_spiLen++] = (*_pBuf++);					/* 发送数据 */
			g_spiTxBuf[g_spiLen++] = (*_pBuf++);					/* 发送数据 */
			bsp_spiTransfer();
			sf_SetCS(1);								/* 禁能片选 */
			sf_WaitForWriteEnd();						/* 等待串行Flash内部写操作完成 */
		}

		/* 进入写保护状态 */
		sf_SetCS(0);
		g_spiLen = 0;
		g_spiTxBuf[g_spiLen++] = (CMD_DISWR);
		bsp_spiTransfer();
		sf_SetCS(1);

		sf_WaitForWriteEnd();							/* 等待串行Flash内部写操作完成 */
	}
	else	/* for MX25L1606E 、 W25Q64BV */
	{
		for (j = 0; j < _usSize / 256; j++)
		{
			sf_WriteEnable();								/* 发送写使能命令 */

			sf_SetCS(0);									/* 使能片选 */
			g_spiLen = 0;
			g_spiTxBuf[g_spiLen++] = (0x02);								/* 发送AAI命令(地址自动增加编程) */
			g_spiTxBuf[g_spiLen++] = ((_uiWriteAddr & 0xFF0000) >> 16);		/* 发送扇区地址的高8bit */
			g_spiTxBuf[g_spiLen++] = ((_uiWriteAddr & 0xFF00) >> 8);		/* 发送扇区地址中间8bit */
			g_spiTxBuf[g_spiLen++] = (_uiWriteAddr & 0xFF);					/* 发送扇区地址低8bit */
			for (i = 0; i < 256; i++)
			{
				g_spiTxBuf[g_spiLen++] = (*_pBuf++);		/* 发送数据 */
			}
			bsp_spiTransfer();
			sf_SetCS(1);								/* 禁止片选 */

			sf_WaitForWriteEnd();						/* 等待串行Flash内部写操作完成 */

			_uiWriteAddr += 256;
		}

		/* 进入写保护状态 */
		sf_SetCS(0);
		g_spiLen = 0;
		g_spiTxBuf[g_spiLen++] = (CMD_DISWR);
		bsp_spiTransfer();
		sf_SetCS(1);

		sf_WaitForWriteEnd();							/* 等待串行Flash内部写操作完成 */
	}
}

/*
*********************************************************************************************************
*	函 数 名: sf_ReadBuffer
*	功能说明: 连续读取若干字节。字节个数不能超出芯片容量。
*	形    参:  	_pBuf : 数据源缓冲区;
*				_uiReadAddr :首地址
*				_usSize :数据个数, 可以大于PAGE_SIZE,但是不能超出芯片总容量
*	返 回 值: 无
*********************************************************************************************************
*/
void sf_ReadBuffer(uint8_t * _pBuf, uint32_t _uiReadAddr, uint32_t _uiSize)
{
	uint16_t rem;
	uint16_t i;
	
	/* 如果读取的数据长度为0或者超出串行Flash地址空间,则直接返回 */
	if ((_uiSize == 0) ||(_uiReadAddr + _uiSize) > g_tSF.TotalSize)
	{
		return;
	}

	/* 擦除扇区操作 */
	sf_SetCS(0);									/* 使能片选 */
	g_spiLen = 0;
	g_spiTxBuf[g_spiLen++] = (CMD_READ);							/* 发送读命令 */
	g_spiTxBuf[g_spiLen++] = ((_uiReadAddr & 0xFF0000) >> 16);	/* 发送扇区地址的高8bit */
	g_spiTxBuf[g_spiLen++] = ((_uiReadAddr & 0xFF00) >> 8);		/* 发送扇区地址中间8bit */
	g_spiTxBuf[g_spiLen++] = (_uiReadAddr & 0xFF);				/* 发送扇区地址低8bit */
	bsp_spiTransfer();
	
	/* 开始读数据,疑问底层DMA缓冲区有限,必须分包读 */
	for (i = 0; i < _uiSize / SPI_BUFFER_SIZE; i++)
	{
		g_spiLen = SPI_BUFFER_SIZE;
		bsp_spiTransfer();
		
		memcpy(_pBuf, g_spiRxBuf, SPI_BUFFER_SIZE);
		_pBuf += SPI_BUFFER_SIZE;
	}
	
	rem = _uiSize % SPI_BUFFER_SIZE;	/* 剩余字节 */
	if (rem > 0)
	{
		g_spiLen = rem;
		bsp_spiTransfer();
		
		memcpy(_pBuf, g_spiRxBuf, rem);
	}
	
	sf_SetCS(1);									/* 禁能片选 */
}

/*
*********************************************************************************************************
*	函 数 名: sf_CmpData
*	功能说明: 比较Flash的数据.
*	形    参:  	_ucpTar : 数据缓冲区
*				_uiSrcAddr :Flash地址
*				_uiSize :数据个数, 可以大于PAGE_SIZE,但是不能超出芯片总容量
*	返 回 值: 0 = 相等, 1 = 不等
*********************************************************************************************************
*/
static uint8_t sf_CmpData(uint32_t _uiSrcAddr, uint8_t *_ucpTar, uint32_t _uiSize)
{
	uint16_t i, j;
	uint16_t rem;

	/* 如果读取的数据长度为0或者超出串行Flash地址空间,则直接返回 */
	if ((_uiSrcAddr + _uiSize) > g_tSF.TotalSize)
	{
		return 1;
	}

	if (_uiSize == 0)
	{
		return 0;
	}

	sf_SetCS(0);									/* 使能片选 */
	g_spiLen = 0;
	g_spiTxBuf[g_spiLen++] = (CMD_READ);							/* 发送读命令 */
	g_spiTxBuf[g_spiLen++] = ((_uiSrcAddr & 0xFF0000) >> 16);		/* 发送扇区地址的高8bit */
	g_spiTxBuf[g_spiLen++] = ((_uiSrcAddr & 0xFF00) >> 8);			/* 发送扇区地址中间8bit */
	g_spiTxBuf[g_spiLen++] = (_uiSrcAddr & 0xFF);					/* 发送扇区地址低8bit */
	bsp_spiTransfer();
	
	/* 开始读数据,因为底层DMA缓冲区有限,必须分包读 */
	for (i = 0; i < _uiSize / SPI_BUFFER_SIZE; i++)
	{
		g_spiLen = SPI_BUFFER_SIZE;
		bsp_spiTransfer();
		
		for (j = 0; j < SPI_BUFFER_SIZE; j++)
		{
			if (g_spiRxBuf[j] != *_ucpTar++)
			{
				goto NOTEQ;		/* 不相等 */
			}
		}
	}
	
	rem = _uiSize % SPI_BUFFER_SIZE;	/* 剩余字节 */
	if (rem > 0)
	{
		g_spiLen = rem;
		bsp_spiTransfer();
		
		for (j = 0; j < rem; j++)
		{
			if (g_spiRxBuf[j] != *_ucpTar++)
			{
				goto NOTEQ;		/* 不相等 */
			}
		}
	}
	sf_SetCS(1);
	return 0;		/* 相等 */
	
NOTEQ:	
	sf_SetCS(1);	/* 不相等 */
	return 1;
}

/*
*********************************************************************************************************
*	函 数 名: sf_NeedErase
*	功能说明: 判断写PAGE前是否需要先擦除。
*	形    参:   _ucpOldBuf : 旧数据
*			   _ucpNewBuf : 新数据
*			   _uiLen :数据个数,不能超过页面大小
*	返 回 值: 0 : 不需要擦除, 1 :需要擦除
*********************************************************************************************************
*/
static uint8_t sf_NeedErase(uint8_t * _ucpOldBuf, uint8_t *_ucpNewBuf, uint16_t _usLen)
{
	uint16_t i;
	uint8_t ucOld;

	/*
	算法第1步:old 求反, new 不变
	      old    new
		  1101   0101
	~     1111
		= 0010   0101

	算法第2步: old 求反的结果与 new 位与
		  0010   old
	&	  0101   new
		 =0000

	算法第3步: 结果为0,则表示无需擦除. 否则表示需要擦除
	*/

	for (i = 0; i < _usLen; i++)
	{
		ucOld = *_ucpOldBuf++;
		ucOld = ~ucOld;

		/* 注意错误的写法: if (ucOld & (*_ucpNewBuf++) != 0) */
		if ((ucOld & (*_ucpNewBuf++)) != 0)
		{
			return 1;
		}
	}
	return 0;
}

/*
*********************************************************************************************************
*	函 数 名: sf_AutoWritePage
*	功能说明: 写1个PAGE并校验,如果不正确则再重写两次。本函数自动完成擦除操作。
*	形    参:  	_pBuf : 数据源缓冲区;
*				_uiWriteAddr :目标区域首地址
*				_usSize :数据个数,不能超过页面大小
*	返 回 值: 0 : 错误, 1 : 成功
*********************************************************************************************************
*/
static uint8_t sf_AutoWritePage(uint8_t *_ucpSrc, uint32_t _uiWrAddr, uint16_t _usWrLen)
{
	uint16_t i;
	uint16_t j;					/* 用于延时 */
	uint32_t uiFirstAddr;		/* 扇区首址 */
	uint8_t ucNeedErase;		/* 1表示需要擦除 */
	uint8_t cRet;

	/* 长度为0时不继续操作,直接认为成功 */
	if (_usWrLen == 0)
	{
		return 1;
	}

	/* 如果偏移地址超过芯片容量则退出 */
	if (_uiWrAddr >= g_tSF.TotalSize)
	{
		return 0;
	}

	/* 如果数据长度大于扇区容量,则退出 */
	if (_usWrLen > g_tSF.PageSize)
	{
		return 0;
	}

	/* 如果FLASH中的数据没有变化,则不写FLASH */
	sf_ReadBuffer(s_spiBuf, _uiWrAddr, _usWrLen);
	if (memcmp(s_spiBuf, _ucpSrc, _usWrLen) == 0)
	{
		return 1;
	}

	/* 判断是否需要先擦除扇区 */
	/* 如果旧数据修改为新数据,所有位均是 1->0 或者 0->0, 则无需擦除,提高Flash寿命 */
	ucNeedErase = 0;
	if (sf_NeedErase(s_spiBuf, _ucpSrc, _usWrLen))
	{
		ucNeedErase = 1;
	}

	uiFirstAddr = _uiWrAddr & (~(g_tSF.PageSize - 1));

	if (_usWrLen == g_tSF.PageSize)		/* 整个扇区都改写 */
	{
		for	(i = 0; i < g_tSF.PageSize; i++)
		{
			s_spiBuf[i] = _ucpSrc[i];
		}
	}
	else						/* 改写部分数据 */
	{
		/* 先将整个扇区的数据读出 */
		sf_ReadBuffer(s_spiBuf, uiFirstAddr, g_tSF.PageSize);

		/* 再用新数据覆盖 */
		i = _uiWrAddr & (g_tSF.PageSize - 1);
		memcpy(&s_spiBuf[i], _ucpSrc, _usWrLen);
	}

	/* 写完之后进行校验,如果不正确则重写,最多3次 */
	cRet = 0;
	for (i = 0; i < 3; i++)
	{

		/* 如果旧数据修改为新数据,所有位均是 1->0 或者 0->0, 则无需擦除,提高Flash寿命 */
		if (ucNeedErase == 1)
		{
			sf_EraseSector(uiFirstAddr);		/* 擦除1个扇区 */
		}

		/* 编程一个PAGE */
		sf_PageWrite(s_spiBuf, uiFirstAddr, g_tSF.PageSize);

		if (sf_CmpData(_uiWrAddr, _ucpSrc, _usWrLen) == 0)
		{
			cRet = 1;
			break;
		}
		else
		{
			if (sf_CmpData(_uiWrAddr, _ucpSrc, _usWrLen) == 0)
			{
				cRet = 1;
				break;
			}

			/* 失败后延迟一段时间再重试 */
			for (j = 0; j < 10000; j++);
		}
	}

	return cRet;
}

/*
*********************************************************************************************************
*	函 数 名: sf_WriteBuffer
*	功能说明: 写1个扇区并校验,如果不正确则再重写两次。本函数自动完成擦除操作。
*	形    参:  	_pBuf : 数据源缓冲区;
*				_uiWrAddr :目标区域首地址
*				_usSize :数据个数,不能超过页面大小
*	返 回 值: 1 : 成功, 0 : 失败
*********************************************************************************************************
*/
uint8_t sf_WriteBuffer(uint8_t* _pBuf, uint32_t _uiWriteAddr, uint16_t _usWriteSize)
{
	uint16_t NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;

	Addr = _uiWriteAddr % g_tSF.PageSize;
	count = g_tSF.PageSize - Addr;
	NumOfPage =  _usWriteSize / g_tSF.PageSize;
	NumOfSingle = _usWriteSize % g_tSF.PageSize;

	if (Addr == 0) /* 起始地址是页面首地址  */
	{
		if (NumOfPage == 0) /* 数据长度小于页面大小 */
		{
			if (sf_AutoWritePage(_pBuf, _uiWriteAddr, _usWriteSize) == 0)
			{
				return 0;
			}
		}
		else 	/* 数据长度大于等于页面大小 */
		{
			while (NumOfPage--)
			{
				if (sf_AutoWritePage(_pBuf, _uiWriteAddr, g_tSF.PageSize) == 0)
				{
					return 0;
				}
				_uiWriteAddr +=  g_tSF.PageSize;
				_pBuf += g_tSF.PageSize;
			}
			if (sf_AutoWritePage(_pBuf, _uiWriteAddr, NumOfSingle) == 0)
			{
				return 0;
			}
		}
	}
	else  /* 起始地址不是页面首地址  */
	{
		if (NumOfPage == 0) /* 数据长度小于页面大小 */
		{
			if (NumOfSingle > count) /* (_usWriteSize + _uiWriteAddr) > SPI_FLASH_PAGESIZE */
			{
				temp = NumOfSingle - count;

				if (sf_AutoWritePage(_pBuf, _uiWriteAddr, count) == 0)
				{
					return 0;
				}

				_uiWriteAddr +=  count;
				_pBuf += count;

				if (sf_AutoWritePage(_pBuf, _uiWriteAddr, temp) == 0)
				{
					return 0;
				}
			}
			else
			{
				if (sf_AutoWritePage(_pBuf, _uiWriteAddr, _usWriteSize) == 0)
				{
					return 0;
				}
			}
		}
		else	/* 数据长度大于等于页面大小 */
		{
			_usWriteSize -= count;
			NumOfPage =  _usWriteSize / g_tSF.PageSize;
			NumOfSingle = _usWriteSize % g_tSF.PageSize;

			if (sf_AutoWritePage(_pBuf, _uiWriteAddr, count) == 0)
			{
				return 0;
			}

			_uiWriteAddr +=  count;
			_pBuf += count;

			while (NumOfPage--)
			{
				if (sf_AutoWritePage(_pBuf, _uiWriteAddr, g_tSF.PageSize) == 0)
				{
					return 0;
				}
				_uiWriteAddr +=  g_tSF.PageSize;
				_pBuf += g_tSF.PageSize;
			}

			if (NumOfSingle != 0)
			{
				if (sf_AutoWritePage(_pBuf, _uiWriteAddr, NumOfSingle) == 0)
				{
					return 0;
				}
			}
		}
	}
	return 1;	/* 成功 */
}

/*
*********************************************************************************************************
*	函 数 名: sf_ReadID
*	功能说明: 读取器件ID
*	形    参:  无
*	返 回 值: 32bit的器件ID (最高8bit填0,有效ID位数为24bit)
*********************************************************************************************************
*/
uint32_t sf_ReadID(void)
{
	uint32_t uiID;
	uint8_t id1, id2, id3;

	sf_SetCS(0);									/* 使能片选 */
	g_spiLen = 0;
	g_spiTxBuf[0] = (CMD_RDID);								/* 发送读ID命令 */
	g_spiLen = 4;
	bsp_spiTransfer();
	
	id1 = g_spiRxBuf[1];					/* 读ID的第1个字节 */
	id2 = g_spiRxBuf[2];					/* 读ID的第2个字节 */
	id3 = g_spiRxBuf[3];					/* 读ID的第3个字节 */
	sf_SetCS(1);									/* 禁能片选 */

	uiID = ((uint32_t)id1 << 16) | ((uint32_t)id2 << 8) | id3;

	return uiID;
}

/*
*********************************************************************************************************
*	函 数 名: sf_ReadInfo
*	功能说明: 读取器件ID,并填充器件参数
*	形    参:  无
*	返 回 值: 无
*********************************************************************************************************
*/
void sf_ReadInfo(void)
{
	/* 自动识别串行Flash型号 */
	{
		g_tSF.ChipID = sf_ReadID();	/* 芯片ID */

		switch (g_tSF.ChipID)
		{
			case SST25VF016B_ID:
				strcpy(g_tSF.ChipName, "SST25VF016B");
				g_tSF.TotalSize = 2 * 1024 * 1024;	/* 总容量 = 2M */
				g_tSF.PageSize = 4 * 1024;			/* 页面大小 = 4K */
				break;

			case MX25L1606E_ID:
				strcpy(g_tSF.ChipName, "MX25L1606E");
				g_tSF.TotalSize = 2 * 1024 * 1024;	/* 总容量 = 2M */
				g_tSF.PageSize = 4 * 1024;			/* 页面大小 = 4K */
				break;

			case W25Q64BV_ID:
				strcpy(g_tSF.ChipName, "W25Q64");
				g_tSF.TotalSize = 8 * 1024 * 1024;	/* 总容量 = 8M */
				g_tSF.PageSize = 4 * 1024;			/* 页面大小 = 4K */
				break;
			
			case W25Q128_ID:
				strcpy(g_tSF.ChipName, "W25Q128");
				g_tSF.TotalSize = 16 * 1024 * 1024;	/* 总容量 = 8M */
				g_tSF.PageSize = 4 * 1024;			/* 页面大小 = 4K */
				break;			

			default:
				strcpy(g_tSF.ChipName, "Unknow Flash");
				g_tSF.TotalSize = 2 * 1024 * 1024;
				g_tSF.PageSize = 4 * 1024;
				break;
		}
	}
}

/*
*********************************************************************************************************
*	函 数 名: sf_WriteEnable
*	功能说明: 向器件发送写使能命令
*	形    参:  无
*	返 回 值: 无
*********************************************************************************************************
*/
static void sf_WriteEnable(void)
{
	sf_SetCS(0);									/* 使能片选 */
	g_spiLen = 0;
	g_spiTxBuf[g_spiLen++] = (CMD_WREN);								/* 发送命令 */
	bsp_spiTransfer();
	sf_SetCS(1);									/* 禁能片选 */
}

#if 0
/*
*********************************************************************************************************
*	函 数 名: sf_WriteStatus
*	功能说明: 写状态寄存器
*	形    参:  _ucValue : 状态寄存器的值
*	返 回 值: 无
*********************************************************************************************************
*/
static void sf_WriteStatus(uint8_t _ucValue)
{
	if (g_tSF.ChipID == SST25VF016B_ID)
	{
		/* 第1步:先使能写状态寄存器 */
		sf_SetCS(0);									/* 使能片选 */
		g_spiLen = 0;
		g_spiTxBuf[g_spiLen++] = (CMD_EWRSR);							/* 发送命令, 允许写状态寄存器 */
		bsp_spiTransfer();
		sf_SetCS(1);									/* 禁能片选 */

		/* 第2步:再写状态寄存器 */
		sf_SetCS(0);									/* 使能片选 */
		g_spiLen = 0;
		g_spiTxBuf[g_spiLen++] = (CMD_WRSR);							/* 发送命令, 写状态寄存器 */
		g_spiTxBuf[g_spiLen++] = (_ucValue);							/* 发送数据:状态寄存器的值 */
		bsp_spiTransfer();
		sf_SetCS(1);									/* 禁能片选 */
	}
	else
	{
		sf_SetCS(0);									/* 使能片选 */
		g_spiLen = 0;
		g_spiTxBuf[g_spiLen++] = (CMD_WRSR);							/* 发送命令, 写状态寄存器 */
		g_spiTxBuf[g_spiLen++] = (_ucValue);							/* 发送数据:状态寄存器的值 */
		bsp_spiTransfer();
		sf_SetCS(1);									/* 禁能片选 */
	}
}
#endif

/*
*********************************************************************************************************
*	函 数 名: sf_WaitForWriteEnd
*	功能说明: 采用循环查询的方式等待器件内部写操作完成
*	形    参:  无
*	返 回 值: 无
*********************************************************************************************************
*/
static void sf_WaitForWriteEnd(void)
{
	sf_SetCS(0);									/* 使能片选 */
	g_spiTxBuf[0] = (CMD_RDSR);							/* 发送命令, 读状态寄存器 */
	g_spiLen = 2;
	bsp_spiTransfer();	
	sf_SetCS(1);									/* 禁能片选 */
	
	while(1)
	{
		sf_SetCS(0);									/* 使能片选 */
		g_spiTxBuf[0] = (CMD_RDSR);						/* 发送命令, 读状态寄存器 */
		g_spiTxBuf[1] = 0;		/* 无关数据 */
		g_spiLen = 2;
		bsp_spiTransfer();	
		sf_SetCS(1);									/* 禁能片选 */
		
		if ((g_spiRxBuf[1] & WIP_FLAG) != SET)	/* 判断状态寄存器的忙标志位 */
		{
			break;
		}		
	}	
}

/***************************** 安富莱电子 [url]www.armfly.com[/url] (END OF FILE) *********************************/
回复

使用道具 举报

1

主题

6

回帖

9

积分

新手上路

积分
9
QQ
 楼主| 发表于 2025-9-18 17:23:32 | 显示全部楼层
V7-028_串行SPI+Flash+W25QXX读写例程(查询方式+V1.1)    我是看的这个例程
回复

使用道具 举报

1

主题

6

回帖

9

积分

新手上路

积分
9
QQ
 楼主| 发表于 2025-9-18 17:24:02 | 显示全部楼层
V7-028_串行SPI+Flash+W25QXX读写例程(查询方式+V1.1)
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
118335
QQ
发表于 7 天前 | 显示全部楼层
freshmen36 发表于 2025-9-18 17:24
V7-028_串行SPI+Flash+W25QXX读写例程(查询方式+V1.1)

1、看了下这个例子,是我把这个函数名改成sector,这个是比较的4KB,最小扇区单位。那个SPI_BUFFER_SIZE大小就是开的1个扇区4KB
2、这个函数前面已经比较检测过了。
        /* 如果FLASH中的数据没有变化,则不写FLASH */
        sf_ReadBuffer(s_spiBuf, _uiWrAddr, _usWrLen);
        if (memcmp(s_spiBuf, _ucpSrc, _usWrLen) == 0)
        {
                return 1;
        }

你说的这个地方是擦除后,再检测是否正常擦除并编程成功了。
回复

使用道具 举报

1

主题

6

回帖

9

积分

新手上路

积分
9
QQ
 楼主| 发表于 7 天前 | 显示全部楼层
        /* 写完之后进行校验,如果不正确则重写,最多3次 */
        cRet = 0;
        for (i = 0; i < 3; i++)
        {

                /* 如果旧数据修改为新数据,所有位均是 1->0 或者 0->0, 则无需擦除,提高Flash寿命 */
                if (ucNeedErase == 1)
                {
                        sf_EraseSector(uiFirstAddr);                /* 擦除1个扇区 */
                }

                /* 编程一个扇区 */
                sf_PageWrite(s_spiBuf, uiFirstAddr, g_tSF.SectorSize);

                if (sf_CmpData(_uiWrAddr, _ucpSrc, _usWrLen) == 0)
                {
                        cRet = 1;
                        break;
                }
                else
                {
                        if (sf_CmpData(_uiWrAddr, _ucpSrc, _usWrLen) == 0)
                        {
                                cRet = 1;
                                break;
                        }

                        /* 失败后延迟一段时间再重试 */
                        for (j = 0; j < 10000; j++);
                }
        }

上述代码中 sf_PageWrite(s_spiBuf, uiFirstAddr, g_tSF.SectorSize)  编程了一个扇区 ,  但 sf_CmpData(_uiWrAddr, _ucpSrc, _usWrLen)  不是验证整个扇区是否写成功,   我的理解是否正确?   
回复

使用道具 举报

1

主题

6

回帖

9

积分

新手上路

积分
9
QQ
 楼主| 发表于 7 天前 | 显示全部楼层
eric2013 发表于 2025-9-19 08:46
1、看了下这个例子,是我把这个函数名改成sector,这个是比较的4KB,最小扇区单位。那个SPI_BUFFER_SIZE ...

/* 写完之后进行校验,如果不正确则重写,最多3次 */
        cRet = 0;
        for (i = 0; i < 3; i++)
        {

                /* 如果旧数据修改为新数据,所有位均是 1->0 或者 0->0, 则无需擦除,提高Flash寿命 */
                if (ucNeedErase == 1)
                {
                        sf_EraseSector(uiFirstAddr);                /* 擦除1个扇区 */
                }

                /* 编程一个扇区 */
                sf_PageWrite(s_spiBuf, uiFirstAddr, g_tSF.SectorSize);

                if (sf_CmpData(_uiWrAddr, _ucpSrc, _usWrLen) == 0)
                {
                        cRet = 1;
                        break;
                }
                else
                {
                        if (sf_CmpData(_uiWrAddr, _ucpSrc, _usWrLen) == 0)
                        {
                                cRet = 1;
                                break;
                        }

                        /* 失败后延迟一段时间再重试 */
                        for (j = 0; j < 10000; j++);
                }
        }


这段代码 sf_PageWrite(s_spiBuf, uiFirstAddr, g_tSF.SectorSize)编程了一个扇区, 但(sf_CmpData(_uiWrAddr, _ucpSrc, _usWrLen) 不是验证整个扇区是否写成功了, 我的理解是否正确?
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
118335
QQ
发表于 6 天前 | 显示全部楼层
freshmen36 发表于 2025-9-19 09:47
/* 写完之后进行校验,如果不正确则重写,最多3次 */
        cRet = 0;
        for (i = 0; i < 3; i++)

对,校验这里是校验的写入数值吗,不是扇区校验。
回复

使用道具 举报

1

主题

6

回帖

9

积分

新手上路

积分
9
QQ
 楼主| 发表于 5 天前 | 显示全部楼层
eric2013 发表于 2025-9-20 10:55
对,校验这里是校验的写入数值吗,不是扇区校验。

既然编程整个扇区,  为什么只担心写入值出错而进行校验,  不用担心扇区的其它值出错吗?  我测试一颗国产芯片ZD25Q16时遇到了写入值正确但扇区的其它值出错的情况。
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
118335
QQ
发表于 5 天前 | 显示全部楼层
freshmen36 发表于 2025-9-21 00:21
既然编程整个扇区,  为什么只担心写入值出错而进行校验,  不用担心扇区的其它值出错吗?  我测试一颗国 ...

扇区内未编程的都不做校验,不在需要校验的范围内。
回复

使用道具 举报

1

主题

6

回帖

9

积分

新手上路

积分
9
QQ
 楼主| 发表于 5 天前 | 显示全部楼层
eric2013 发表于 2025-9-21 12:29
扇区内未编程的都不做校验,不在需要校验的范围内。

你说的没错,  问题是static uint8_t sf_AutoWriteSector(uint8_t *_ucpSrc, uint32_t _uiWrAddr, uint16_t _usWrLen) 这个函数中调用sf_PageWrite(s_spiBuf, uiFirstAddr, g_tSF.SectorSize) 是不是编程了整个扇区?  g_tSF.SectorSize 是不是扇区大小?
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
118335
QQ
发表于 5 天前 | 显示全部楼层
freshmen36 发表于 2025-9-21 15:03
你说的没错,  问题是static uint8_t sf_AutoWriteSector(uint8_t *_ucpSrc, uint32_t _uiWrAddr, uint16 ...

确实应该整个扇区校验更合理。

非常感谢交流讨论。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2025-9-26 19:30 , Processed in 0.060344 second(s), 25 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表