当前位置: 首页 新闻资讯 技术问答

SDNAND的读/写/擦除操作的函数

SD NAND-贴片式TF卡-贴片式SD卡-免费测试2025-06-1216

SDNAND 的读 / 写 / 擦除操作函数详解

SDNAND 作为一种采用 SD 接口的 NAND 闪存,其读 / 写 / 擦除操作与标准 SD 卡有相似之处,但也有其独特特性。下面详细介绍这些操作的函数实现:

读取操作

SDNAND 的读取操作通常支持按页读取和按块读取两种方式:

#include "sd_nand.h"

/* 读取单个页(通常为2KB) */
HAL_StatusTypeDef SDNAND_ReadPage(uint8_t *pData, uint32_t PageAddr, uint32_t Timeout)
{
  HAL_StatusTypeDef status;
  
  // 发送CMD17: 读取单个块命令
  status = HAL_SD_SendCmd(&hsd1, SD_CMD_READ_SINGLE_BLOCK, PageAddr, 0, Timeout);
  if(status != HAL_OK)
  {
    return status;
  }
  
  // 接收数据
  status = HAL_SD_ReadBlocks(&hsd1, pData, PageAddr, 1, Timeout);
  
  return status;
}

/* 读取多个页 */
HAL_StatusTypeDef SDNAND_ReadPages(uint8_t *pData, uint32_t PageAddr, uint32_t NumPages, uint32_t Timeout)
{
  HAL_StatusTypeDef status;
  
  // 发送CMD18: 读取多个块命令
  status = HAL_SD_SendCmd(&hsd1, SD_CMD_READ_MULT_BLOCK, PageAddr, 0, Timeout);
  if(status != HAL_OK)
  {
    return status;
  }
  
  // 接收数据
  status = HAL_SD_ReadBlocks(&hsd1, pData, PageAddr, NumPages, Timeout);
  
  return status;
}

/* 读取页的保留区域(用于存储ECC等信息) */
HAL_StatusTypeDef SDNAND_ReadSpareArea(uint8_t *pData, uint32_t PageAddr, uint32_t Timeout)
{
  HAL_StatusTypeDef status;
  uint8_t buffer[512]; // 假设使用512字节块大小
  
  // 首先读取整个页
  status = SDNAND_ReadPage(buffer, PageAddr, Timeout);
  if(status != HAL_OK)
  {
    return status;
  }
  
  // 从页数据中提取保留区域数据
  // 保留区域位置和大小取决于具体的SDNAND型号
  // 这里假设保留区域在页的最后部分
  memcpy(pData, &buffer[496], 16); // 假设保留区域为16字节
  
  return HAL_OK;
}

写入操作

SDNAND 的写入操作需要注意以下几点:

  1. 写入前必须确保目标页已被擦除

  2. 写入操作通常需要先写入缓冲区,然后执行编程命令

#include "sd_nand.h"

/* 写入单个页 */
HAL_StatusTypeDef SDNAND_WritePage(uint8_t *pData, uint32_t PageAddr, uint32_t Timeout)
{
  HAL_StatusTypeDef status;
  
  // 检查页是否已擦除
  if(!SDNAND_IsPageErased(PageAddr))
  {
    // 擦除页
    status = SDNAND_EraseBlock(PageAddr / PAGES_PER_BLOCK, Timeout);
    if(status != HAL_OK)
    {
      return status;
    }
  }
  
  // 发送CMD24: 写入单个块命令
  status = HAL_SD_SendCmd(&hsd1, SD_CMD_WRITE_BLOCK, PageAddr, 0, Timeout);
  if(status != HAL_OK)
  {
    return status;
  }
  
  // 发送数据
  status = HAL_SD_WriteBlocks(&hsd1, pData, PageAddr, 1, Timeout);
  
  return status;
}

/* 写入多个页 */
HAL_StatusTypeDef SDNAND_WritePages(uint8_t *pData, uint32_t PageAddr, uint32_t NumPages, uint32_t Timeout)
{
  HAL_StatusTypeDef status;
  uint32_t i;
  
  // 检查并擦除需要的块
  for(i = 0; i < NumPages; i++)
  {
    if(!SDNAND_IsPageErased(PageAddr + i))
    {
      status = SDNAND_EraseBlock((PageAddr + i) / PAGES_PER_BLOCK, Timeout);
      if(status != HAL_OK)
      {
        return status;
      }
    }
  }
  
  // 发送CMD25: 写入多个块命令
  status = HAL_SD_SendCmd(&hsd1, SD_CMD_WRITE_MULT_BLOCK, PageAddr, 0, Timeout);
  if(status != HAL_OK)
  {
    return status;
  }
  
  // 发送数据
  status = HAL_SD_WriteBlocks(&hsd1, pData, PageAddr, NumPages, Timeout);
  
  return status;
}

/* 写入页的保留区域 */
HAL_StatusTypeDef SDNAND_WriteSpareArea(uint8_t *pData, uint32_t PageAddr, uint32_t Timeout)
{
  HAL_StatusTypeDef status;
  uint8_t buffer[512]; // 假设使用512字节块大小
  
  // 首先读取整个页
  status = SDNAND_ReadPage(buffer, PageAddr, Timeout);
  if(status != HAL_OK)
  {
    return status;
  }
  
  // 更新保留区域数据
  memcpy(&buffer[496], pData, 16); // 假设保留区域为16字节
  
  // 写回整个页
  return SDNAND_WritePage(buffer, PageAddr, Timeout);
}

擦除操作

NAND 闪存的一个重要特性是必须先擦除才能写入,且擦除操作是以块为单位进行的:

#include "sd_nand.h"

/* 擦除单个块 */
HAL_StatusTypeDef SDNAND_EraseBlock(uint32_t BlockAddr, uint32_t Timeout)
{
  HAL_StatusTypeDef status;
  uint32_t StartAddr, EndAddr;
  
  // 计算块的起始和结束地址
  StartAddr = BlockAddr * BLOCK_SIZE;
  EndAddr = StartAddr + BLOCK_SIZE - 1;
  
  // 发送CMD32: 设置擦除块起始地址
  status = HAL_SD_SendCmd(&hsd1, SD_CMD_ERASE_WR_BLK_START_ADDR, StartAddr, 0, Timeout);
  if(status != HAL_OK)
  {
    return status;
  }
  
  // 发送CMD33: 设置擦除块结束地址
  status = HAL_SD_SendCmd(&hsd1, SD_CMD_ERASE_WR_BLK_END_ADDR, EndAddr, 0, Timeout);
  if(status != HAL_OK)
  {
    return status;
  }
  
  // 发送CMD38: 执行擦除
  status = HAL_SD_SendCmd(&hsd1, SD_CMD_ERASE, 0, 0, Timeout);
  
  return status;
}

/* 擦除多个块 */
HAL_StatusTypeDef SDNAND_EraseBlocks(uint32_t StartBlock, uint32_t NumBlocks, uint32_t Timeout)
{
  HAL_StatusTypeDef status;
  uint32_t i;
  
  for(i = 0; i < NumBlocks; i++)
  {
    status = SDNAND_EraseBlock(StartBlock + i, Timeout);
    if(status != HAL_OK)
    {
      return status;
    }
  }
  
  return HAL_OK;
}

/* 检查页是否已擦除 */
uint8_t SDNAND_IsPageErased(uint32_t PageAddr)
{
  uint8_t buffer[512]; // 假设使用512字节块大小
  uint32_t i;
  
  // 读取页数据
  if(HAL_SD_ReadBlocks(&hsd1, buffer, PageAddr, 1, SD_MAX_TIMEOUT) != HAL_OK)
  {
    return 0; // 读取失败,假设未擦除
  }
  
  // 检查是否所有位都是1(擦除状态)
  for(i = 0; i < 512; i++)
  {
    if(buffer[i] != 0xFF)
    {
      return 0; // 存在非擦除位
    }
  }
  
  return 1; // 页已擦除
}

特殊操作函数

SDNAND 通常还提供一些特殊操作函数,用于管理闪存特性:

#include "sd_nand.h"

/* 擦除单个块 */
HAL_StatusTypeDef SDNAND_EraseBlock(uint32_t BlockAddr, uint32_t Timeout)
{
  HAL_StatusTypeDef status;
  uint32_t StartAddr, EndAddr;
  
  // 计算块的起始和结束地址
  StartAddr = BlockAddr * BLOCK_SIZE;
  EndAddr = StartAddr + BLOCK_SIZE - 1;
  
  // 发送CMD32: 设置擦除块起始地址
  status = HAL_SD_SendCmd(&hsd1, SD_CMD_ERASE_WR_BLK_START_ADDR, StartAddr, 0, Timeout);
  if(status != HAL_OK)
  {
    return status;
  }
  
  // 发送CMD33: 设置擦除块结束地址
  status = HAL_SD_SendCmd(&hsd1, SD_CMD_ERASE_WR_BLK_END_ADDR, EndAddr, 0, Timeout);
  if(status != HAL_OK)
  {
    return status;
  }
  
  // 发送CMD38: 执行擦除
  status = HAL_SD_SendCmd(&hsd1, SD_CMD_ERASE, 0, 0, Timeout);
  
  return status;
}

/* 擦除多个块 */
HAL_StatusTypeDef SDNAND_EraseBlocks(uint32_t StartBlock, uint32_t NumBlocks, uint32_t Timeout)
{
  HAL_StatusTypeDef status;
  uint32_t i;
  
  for(i = 0; i < NumBlocks; i++)
  {
    status = SDNAND_EraseBlock(StartBlock + i, Timeout);
    if(status != HAL_OK)
    {
      return status;
    }
  }
  
  return HAL_OK;
}

/* 检查页是否已擦除 */
uint8_t SDNAND_IsPageErased(uint32_t PageAddr)
{
  uint8_t buffer[512]; // 假设使用512字节块大小
  uint32_t i;
  
  // 读取页数据
  if(HAL_SD_ReadBlocks(&hsd1, buffer, PageAddr, 1, SD_MAX_TIMEOUT) != HAL_OK)
  {
    return 0; // 读取失败,假设未擦除
  }
  
  // 检查是否所有位都是1(擦除状态)
  for(i = 0; i < 512; i++)
  {
    if(buffer[i] != 0xFF)
    {
      return 0; // 存在非擦除位
    }
  }
  
  return 1; // 页已擦除
}

使用注意事项

  1. 擦除操作限制

    • NAND 闪存的擦除操作有寿命限制 (通常为 10K-100K 次)

    • 应尽量减少不必要的擦除操作,实现磨损均衡算法

  2. 页大小与块大小

    • 不同 SDNAND 型号的页大小和块大小可能不同

    • 常见页大小为 2KB,块大小为 64KB 或 128KB

  3. 保留区域

    • 每个页通常包含保留区域,用于存储 ECC 信息和坏块标记

    • 写入数据时应妥善处理保留区域

  4. 错误处理

    • 实现 ECC 错误检测和纠正机制

    • 实现坏块管理功能,跳过已标记的坏块

通过以上函数,你可以实现 SDNAND 的基本操作。在实际应用中,还应根据具体的 SDNAND 型号和应用需求进行适当调整。

热门标签:SD NAND FLASH 贴片式TF卡 贴片式SD卡 SD FLASH NAND FLASH


SD NAND-贴片式TF卡-贴片式SD卡-免费测试

深圳市芯存者科技有限公司

售前咨询
售前咨询
售后服务
售后服务
联系我们

电话:176-6539-0767

Q Q:135-0379-986

邮箱:1350379986@qq.com

地址:深圳市南山区蛇口街道后海大道1021号C座C422W8

在线客服 在线客服 QQ客服 微信客服 淘宝店铺 联系我们 返回顶部