|
总结下结贴吧,又找不到编辑帖子的地方了,,,
目前flash写的最小单位修改为页(256字节),flash的写顺序只能按顺序从低位地址向高位地址写数据,擦除扇区操作仅在写入地址在扇区首地址时才执行,
常规条件下,若写入的数据正好是页首地址,且写入数据大小为256字节,则直接写入;少于256字节时则先读出页内原始数据,更新数据后重新写入该页,完成数据更新
本部分驱动api接口如下所示,提供个参考,欠妥之处还望多拍砖!
- /*
- *********************************************************************************************************
- *
- * 模块名称 : SPI接口串行FLASH 读写模块
- * 文件名称 : bsp_spi_flash.c
- * 版 本 : V1.0
- * 说 明 : 支持 SST25VF016B、MX25L1606E 和 W25Q32DWSSIG、W25Q32BVSSIG、W25Q64BVSSIG,W25Q128BVSSIG
- * SST25VF016B 的写操作采用AAI指令,可以提高写入效率。
- *
- * 缺省使用STM32F1的硬件SPI2接口,时钟频率最高为 9MHz
- *
- * 本模块的使用方法:
- * 1、初始化模块:sf_Init();
- * 2、调用写数据函数向FLASH写入数据:sf_WriteByIndex(_pBuf, NumOfSector, NumofPage, _usWriteFixSize);
- * 3、调用读数据函数从FLASH读取数据:sf_ReadByIndex(_pBuf, NumOfSector, NumofPage, _usReadFixSize);
- *
- * 以W25Q32BVSSIG为例,以下代码用于写满W25Q32BVSSIG:
- uint16_t j=0,k=0; // 扇区与页的索引
- uint8_t i;
- char buf[32] = {0}; // 写入数据缓存区
- uint8_t temp[128]; // 读出数据缓冲区
- sf_Init(); // 初始化SPIFLASH
- sf_EraseChip(); // 擦除整个FLASH
- // 写满W25Q32BV
- // W25Q32BV总容量为4M,总共有1024个扇区,每个扇区由16个256字节的页构成
- // 这里自定义每页大小为32字节,则每页由128个页构成
- for(j=0; j<1024; j++)
- {
- for(k=0; k<128; k++)
- {
- for(i=0; i<32; i++)
- {
- buf[i] = k;
- }
-
- if(0 == sf_WriteByIndex((uint8_t *)buf, j, k, 32)) // 如果写入失败则打印失败信息
- {
- printf("%s\n","向SPIFLASH中写入数据失败!");
- }
- sf_ReadByIndex((uint8_t *)temp, j, k, 32); // 读取数据存储于temp临时数组中
- }
- }
- *
- *********************************************************************************************************
- */
- /*
- *********************************************************************************************************
- * 函 数 名: sf_AutoWriteSmallPage
- * 功能说明: 写1个SmallPAGE并校验,如果不正确则再重写两次。本函数自动完成擦除操作。
- * 形 参: _pBuf : 数据源缓冲区;
- * _uiWriteAddr :目标区域首地址
- * _usSize :数据个数,不能超过页面大小
- * 返 回 值: 0 : 错误, 1 : 成功
- *********************************************************************************************************
- */
- static uint8_t sf_AutoWriteSmallPage(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 > 256)
- {
- return 0;
- }
- /* 判断是否需要先擦除扇区 */
- /* 如果写入地址为扇区首地址, 则需进行擦除 */
- ucNeedErase = 0;
- i = _uiWrAddr % g_tSF.PageSize; // 页面偏移量
- if(0 == i)
- {
- ucNeedErase = 1;
- }
-
- uiFirstAddr = _uiWrAddr & (~(256 - 1));
- if (_usWrLen == 256) /* 整个页面都改写 */
- {
- for (i = 0; i < 256; i++)
- {
- s_spiBuf[i] = _ucpSrc[i];
- }
- }
- else /* 改写部分数据 */
- {
- /* 先将整个页面的数据读出 */
- sf_ReadBuffer(s_spiBuf, uiFirstAddr, 256);
- /* 再用新数据覆盖 */
- i = _uiWrAddr & (256 - 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, 256);
- 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_WriteSmallBuffer
- * 功能说明: 写1个页面并校验,如果不正确则再重写两次。本函数自动完成擦除操作。
- * 形 参: _pBuf : 数据源缓冲区;
- * _uiWrAddr :目标区域首地址
- * _usSize :数据个数
- * 返 回 值: 1 : 成功, 0 : 失败
- *********************************************************************************************************
- */
- uint8_t sf_WriteSmallBuffer(uint8_t* _pBuf, uint32_t _uiWriteAddr, uint16_t _usWriteSize)
- {
- uint16_t NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;
- uint8_t reason = 0;
-
- Addr = _uiWriteAddr % 256; // 页面偏移量
- count = 256 - Addr; // 最后一页写完数据后能够剩余多少空间
- NumOfPage = _usWriteSize / 256; // 计算需要写入的数据需要使用多少页的空间
- NumOfSingle = _usWriteSize % 256; // 计算最后一页还需写入多少数据
- if (Addr == 0) /* 起始地址是页面首地址 */
- {
- if (NumOfPage == 0) /* 数据长度小于页面大小 */
- {
- if (sf_AutoWriteSmallPage(_pBuf, _uiWriteAddr, _usWriteSize) == 0)
- {
- reason = 1;
- return 0;
- }
- }
- else /* 数据长度大于等于页面大小 */
- {
- while (NumOfPage--)
- {
- if (sf_AutoWriteSmallPage(_pBuf, _uiWriteAddr, g_tSF.PageSize) == 0)
- {
- return 0;
- }
- _uiWriteAddr += 256;
- _pBuf += 256;
- }
- if (sf_AutoWriteSmallPage(_pBuf, _uiWriteAddr, NumOfSingle) == 0)
- {
- return 0;
- }
- }
- }
- else /* 起始地址不是页面首地址 */
- {
- if (NumOfPage == 0) /* 数据长度小于页面大小 */
- {
- if (NumOfSingle > count) /* (_usWriteSize + _uiWriteAddr) > SPI_FLASH_PAGESIZE --> 当前页从指定的首地址至扇区末尾剩余的空间不足,需要在填满当前页空间后继续在下一页写入数据 */
- {
- temp = NumOfSingle - count;
- if (sf_AutoWriteSmallPage(_pBuf, _uiWriteAddr, count) == 0)
- {
- return 0;
- }
- _uiWriteAddr += count;
- _pBuf += count;
- if (sf_AutoWriteSmallPage(_pBuf, _uiWriteAddr, temp) == 0)
- {
- return 0;
- }
- }
- else // 指定的首地址到页尾的空间足够写入需要写入的数据
- {
- if (sf_AutoWriteSmallPage(_pBuf, _uiWriteAddr, _usWriteSize) == 0)
- {
- return 0;
- }
- }
- }
- else /* 数据长度大于等于页面大小 */
- {
- _usWriteSize -= count;
- NumOfPage = _usWriteSize / 256;
- NumOfSingle = _usWriteSize % 256;
- if (sf_AutoWriteSmallPage(_pBuf, _uiWriteAddr, count) == 0) // 1、写入count字节
- {
- return 0;
- }
- _uiWriteAddr += count;
- _pBuf += count;
- while (NumOfPage--)
- {
- if (sf_AutoWriteSmallPage(_pBuf, _uiWriteAddr, 256) == 0)
- {
- return 0;
- }
- _uiWriteAddr += g_tSF.PageSize;
- _pBuf += g_tSF.PageSize;
- }
- if (NumOfSingle != 0)
- {
- if (sf_AutoWriteSmallPage(_pBuf, _uiWriteAddr, NumOfSingle) == 0)
- {
- return 0;
- }
- }
- }
- }
- return 1; /* 成功 */
- }
复制代码
|
|