STM32固件抽象库中的Nand Flash函数的地址形成可能采用了某种型号的Nand Flash地址要求,与大多数的Nand Flash芯片的地址形成不一样。另外,设计的应用系统有两块S34ML08G201TFI000,一般开发板只有一块Nand Flash,为此对Nand Flash驱动程序进行了重新设计,下面的代码供感兴趣读者参考。
以下为驱动程序的说明文件
#ifndef __MYNAND__
#define __MYNAND__
#include "mySys.h"
#include "main.h"
/**********************************************************************************
本程序只供学习使用,未经作者许可,不得用于其它任何用途
太原数据源板用ST32F429核心板的NAND驱动代码
参考正点原子开发板修改而来
创建日期:2023/05/15
版本:V1.0
版权所有,盗版必究。
Copyright(C)
All rights reserved
********************************************************************************/
/********************************************************************************
升级说明
V1.1 20160520
1,新增硬件ECC支持(仅在以NAND_ECC_SECTOR_SIZE大小为单位进行读写时处理)
2,新增NAND_Delay函数,用于等待tADL/tWHR
3,新增NAND_WritePageConst函数,用于搜寻坏块
4.新增了NAND_HandleTypeDef句柄指针
V1.2 20160525
1,去掉NAND_SEC_SIZE宏定义,由NAND_ECC_SECTOR_SIZE替代
2,去掉nand_dev结构体里面的secbuf指针,用不到
**********************************************************************************/
#define NAND_MAX_PAGE_SIZE 4096 //定义NAND FLASH的最大的PAGE大小(不包括SPARE区),默认4096字节
#define NAND_ECC_SECTOR_SIZE 512 //执行ECC计算的单元大小,默认512字节
//NAND Flash读取延时时间
#define NAND_READ_TIMEOUT 0x01000000U
#define NAND_READ_ECC_TIMEOUT 0x01000000U
//NAND属性结构体
//typedef struct
//{
// uint32_t PageSize; /*!< NAND memory page (without spare area) size measured in bytes
// for 8 bits adressing or words for 16 bits addressing */
// uint32_t SpareAreaSize; /*!< NAND memory spare area size measured in bytes
// for 8 bits adressing or words for 16 bits addressing */
//
// uint32_t BlockSize; /*!< NAND memory block size measured in number of pages */
// uint32_t BlockNbr; /*!< NAND memory number of total blocks */
//
// uint32_t PlaneNbr; /*!< NAND memory number of planes */
// uint32_t PlaneSize; /*!< NAND memory plane size measured in number of blocks */
// FunctionalState ExtraCommandEnable; /*!< NAND extra command needed for Page reading mode. This
// parameter is mandatory for some NAND parts after the read
// command (NAND_CMD_AREA_TRUE1) and before DATA reading sequence.
// Example: Toshiba THTH58BYG3S0HBAI6.
// This parameter could be ENABLE or DISABLE
// Please check the Read Mode sequnece in the NAND device datasheet */
//}NAND_DeviceConfigTypeDef;
typedef struct __NAND_ATTRIBUTE__
{
NAND_HandleTypeDef *ptrNandHandle; //太原设备增加的成员
//以下成员与HAL库函数定义的NAND_DeviceConfigTypeDef数据类型相似
u16 page_totalsize; //每页总大小,main区和spare区总和
u16 page_mainsize; //每页的main区大小,不包含备用区
u16 page_sparesize; //每页的spare区大小
u8 block_pagenum; //每个块包含的页数量
u8 errCode; //太原设备增加的成员变量
u16 plane_blocknum; //每个plane包含的块数量
u16 block_totalnum; //一个芯片所包含的块数量
u16 good_blocknum; //好块数量
u16 valid_blocknum; //有效块数量(供文件系统使用的好块数量)
u32 id; //NAND FLASH ID
u16 *lut; //LUT表,用作逻辑块-物理块转换,
//数据类型由芯片的块的地址大小决定,存放物理块地址(物理块号包含位号),
//数组索引就是逻辑块号,逻辑块包含的字节数与物理块包含的字节数相同,即
//逻辑块大小与物理块大小相同
//参见芯片数据手册P12/76、P6/16和P16/132
u32 ecc_hard; //硬件计算出来的ECC值
u32 ecc_hdbuf[NAND_MAX_PAGE_SIZE/NAND_ECC_SECTOR_SIZE];//ECC硬件计算值缓冲区
u32 ecc_rdbuf[NAND_MAX_PAGE_SIZE/NAND_ECC_SECTOR_SIZE];//ECC读取值的缓冲区
u32 ecc_rdFlagBuf[NAND_MAX_PAGE_SIZE/NAND_ECC_SECTOR_SIZE];//读取ECC标识
u32 ecc_hdFlagBuf[NAND_MAX_PAGE_SIZE/NAND_ECC_SECTOR_SIZE];//读取ECC标识
u32 DevicePageAddr; //页地址,即由页码、面码和块地址构成
u16 DeviceColAddr; //列地址,
u16 ByteNum; //要读取的字节数
u8 *ptrBuf; //接受缓冲区
}Nand_AttributeTypeDef,*ptrNand_AttributeTypeDef;
typedef struct __NAND_COPY__
{
ptrNand_AttributeTypeDef ptrAttribute; //指定芯片参数
u32 Source_PageNum; //源页
u32 Dest_PageNum; //目标页
u16 ColNum; //页内偏移地址,用于修改
u16 ByteNum; //字节数,用于修改
u8 *ptrBuf; //源数据,用于修改
}NAND_CopyTypeDef,*ptrNAND_CopyTypeDef;
extern ptrNand_AttributeTypeDef ptrNand_Dev2; //nand重要参数结构体
extern ptrNand_AttributeTypeDef ptrNand_Dev3;
#define NAND_RB PDin(6) //NAND Flash的闲/忙引脚
#define NAND1_ADDRESS 0X70000000 //bank2上的nand flash芯片的首地址,首地址为:0X7000 0000,片选信号为NCE2
#define NAND2_ADDRESS ` 0X80000000 //bank3上的nand_flash芯片的首地址,首地址为:0x8000 0000,片选信号为NCE3
#define NAND_DATA 0 //芯片数据首地址
#define NAND_CMD 1<<16 //芯片命令首地址
#define NAND_ADDR 1<<17 //芯片地址首地址
//NAND FLASH命令
#define NAND_READID 0X90 //读ID指令
#define NAND_FEATURE 0XEF //设置特性指令
#define NAND_RESET 0XFF //复位NAND
#define NAND_READSTA 0X70 //读状态
/*************************************************
读页起始命令和读页结束命令之间是发送芯片地址命令
**************************************************/
#define NAND_AREA_A 0X00 //读页起始命令
#define NAND_AREA_TRUE1 0X30 //读页结束命令
#define NAND_WRITE0 0X80 //缓冲编程起始或继续开始命令
#define NAND_WRITE_TURE1 0X10 //缓冲编程结束命令
#define NAND_ERASE0 0X60 //块擦除起始命令
#define NAND_ERASE1 0XD0 //块擦除结束命令
#define NAND_MOVEDATA_CMD0 0X00 //拷贝回读起始命令
#define NAND_MOVEDATA_CMD1 0X35 //拷贝回读结束命令
#define NAND_MOVEDATA_CMD2 0X85 //拷贝编程起始命令
#define NAND_MOVEDATA_CMD3 0X10 //拷贝编程结束命令
#define NAND_CMD_RANDOM_READ 0X05
#define NAND_CMD_RANDOM_READ_TURE1 0XE0
#define NAND_CMD_MOVEDATA_0 0X00
#define NAND_CMD_MOVEDATA_1 0X35
#define NAND_CMD_MOVEDATA_2 0X85
#define NAND_CMD_MOVEDATA_3 0X10
#define NAND_CMD_RANDOM_WRITE 0X85 //
#define NAND_CMD_FEATURE 0XEF //设置特性指令
/**********************************************************
本驱动程序函数使用的NAND FLASH状态宏定义
***********************************************************/
#define NSTA_READY 0X40 //nand已经准备好
#define NSTA_SUCCESS 0X00
#define NSTA_ERROR 0X01 //nand错误
#define NSTA_TIMEOUT 0X02 //超时
#define NSTA_ECC1BITERR 0X03 //ECC 1bit错误
#define NSTA_ECC2BITERR 0X04 //ECC 2bit以上错误
#define NSTA_BUSY 0X05 //设备正在忙
#define NSTA_ERROR2 0X06
#define NSTA_ERROR3 0X03
/************************************************
NAND FLASH型号和对应的ID号
不同厂商同一型号芯片的ID号除生产厂商编号不同外,
其它都相同,因此不考虑生产厂家编号,只使用后面
4个字节,这4个字节的具体含义参见芯片数据手册,MT
厂家P36~P37,S厂家P26(1G~4G)和P7(8G)
*************************************************/
#define MT29F4G08ABADA 0XDC909556 //MT29F4G08ABADA
#define S34ML08G2 0XD3D1955A //S34ML08G2
#define MT29F16G08ABABA 0X48002689 //MT29F16G08ABABA
#define HAL_MT29F4G08ABADA 0X2CDC9095 //MT29F4G08ABADA
#define HAL_S34ML08G2 0X01D3D195 //S34ML08G2
#define HAL_MT29F16G08ABABA 0X2C480026 //MT29F16G08ABABA
//使用HAL库函数得到的型号与ID的对应关系
//#define MT29F4G08ABADA 0X9590DC2C //MT29F4G08ABADA
//#define MT29F16G08ABABA 0X2600482C //MT29F16G08ABABA
/************************************
NAND延时用函数
*************************************/
void NAND_Delay(vu32 i);
/************************************
ECC分离和修正函数,所谓分离就是把
FMC计算出的编码分成偶部和奇部等两
部分,然后由此可以判断是否发生错误
以及能否修改错误和恢复可修改的错误
数据
*************************************/
u16 NAND_ECC_Get_OE(u8 oe,u32 eccval);
u8 NAND_ECC_Correction(u8* data_buf,u32 eccrd,u32 ecccl);
/**********************************
初始化本模块全局变量函数
***********************************/
HAL_StatusTypeDef InitNandData(ptrNand_AttributeTypeDef ptr,NAND_HandleTypeDef *hnand);
HAL_StatusTypeDef InitAllNandData(void);
/*********************************************************
基于两个NAND芯片的专用函数定义
**********************************************************/
HAL_StatusTypeDef NAND_ReadID(ptrNand_AttributeTypeDef);
HAL_StatusTypeDef NAND_Init(ptrNand_AttributeTypeDef ptrNandAttribute,NAND_HandleTypeDef *hnand);
u8 NAND_WaitForReady(NAND_HandleTypeDef *hnand);
u16 NAND_EraseChip(NAND_HandleTypeDef *hnand);
u8 NAND_EraseBlock(NAND_HandleTypeDef *hnand,u32 BlockNum);
u8 NAND_ReadPage(ptrNand_AttributeTypeDef ptr);
HAL_StatusTypeDef NAND_ReadPageComp(ptrNand_AttributeTypeDef ptr,u32 CmpVal,u16 *NumByteEqual);
u8 NAND_ReadSpare(ptrNand_AttributeTypeDef ptr);
u8 NAND_WritePage(ptrNand_AttributeTypeDef ptr);
u8 NAND_WritePageConst(ptrNand_AttributeTypeDef ptr,u32 cval);
u8 NAND_WriteSpare(ptrNand_AttributeTypeDef ptr);
u8 NAND_CopyPageWithoutWrite(ptrNAND_CopyTypeDef ptr);
u8 NAND_CopyPageWithWrite(ptrNAND_CopyTypeDef ptr);
u8 NAND_CopyPageWithChangeByReadWrite(ptrNAND_CopyTypeDef ptr);
u8 NAND_CopyPageWithoutChangeByReadWrite(ptrNAND_CopyTypeDef ptr);
u8 NAND_ModeSet(ptrNand_AttributeTypeDef ptr,u8 mode);
u8 NAND_ReadPageWithOutECC(ptrNAND_CopyTypeDef ptr);
u8 NAND_WritePageWithOutECC(ptrNAND_CopyTypeDef ptr);
u8 NAND_WritePageTotalWithECC(ptrNAND_CopyTypeDef pt);
u8 NAND_GetECCByRead(ptrNAND_CopyTypeDef ptr);
u8 NAND_WriteECCOnly(ptrNAND_CopyTypeDef ptr);
#endif
//The following text is executting part
#include "myNand.h"
#include "main.h"
#include "fmc.h"
#include "malloc.h"
#include "part.h"
#include "myusart1.h"
//#define __no_init __attribute__((zero_init))
//__no_init
ptrNand_AttributeTypeDef ptrNand_Dev2=NULL; //nand重要参数结构体
ptrNand_AttributeTypeDef ptrNand_Dev3=NULL;
/************************************
NAND延时用函数
*************************************/
void NAND_Delay(vu32 i)
{
while(i>0)i--;
}
/************************************************
初始化本模块应用的全局变量nand_dev2和nand_dev3
nand_dev2芯片联接在CPU的bank2,nand_dev3联接在
CPU的bank3上,参见CPU手册P1607。初始化后变量的
所有成员全部复位,即都设置为0x00。本函数已经校正!
*************************************************/
HAL_StatusTypeDef InitAllNandData(void)
{
if(ptrNand_Dev2==NULL)
ptrNand_Dev2=mymallocEx(sizeof(Nand_AttributeTypeDef));
if(ptrNand_Dev2==NULL)
return HAL_ERROR;
mymemset(ptrNand_Dev2,0,sizeof(Nand_AttributeTypeDef));
if(InitNandData(ptrNand_Dev2,&hnand1)!=HAL_OK)
return HAL_ERROR;
if(ptrNand_Dev3==NULL)
ptrNand_Dev3=mymallocEx(sizeof(Nand_AttributeTypeDef));
if(ptrNand_Dev3==NULL)
return HAL_ERROR;
mymemset(ptrNand_Dev3,0,sizeof(Nand_AttributeTypeDef));
if(InitNandData(ptrNand_Dev3,&hnand2)!=HAL_OK)
return HAL_ERROR;
return HAL_OK;
}
/************************************************
入口参数:
ptr:Nand_attributeTypeDef类型指针;
hnand:NAND_HandleTypeDef类型指针
出口参数:
ptr:所指类型变量成员发生改变
返回参数:
无
*************************************************/
HAL_StatusTypeDef InitNandData(ptrNand_AttributeTypeDef ptr,NAND_HandleTypeDef *hnand)
{
if((hnand==NULL)||(ptr==NULL))
return HAL_ERROR;
ptr->DevicePageAddr=0x00000000;
ptr->DeviceColAddr=0x0000u;
ptr->ByteNum=0x0000;
ptr->ptrBuf=NULL;
ptr->ptrNandHandle=hnand;
while(NAND_ReadID(ptr)!=HAL_OK)
{
return HAL_ERROR;
}
NAND_ModeSet(ptr,4);
return HAL_OK;
}
/*********************************************
等待设备准备好,假如在延时一段时间期间设备还
没有准备好就认为设备超时,没有准备好。
入口参数:
Nand Flash设备句柄指针hnand;
出口参数:无
返回参数:NSTA_READY=设备准备好,否则设备未准备
好,即设备忙或错。
**********************************************/
u8 NAND_WaitForReady(NAND_HandleTypeDef *hnand)
{
u8 ret;
uint32_t tickstart=HAL_GetTick();
ret=HAL_NAND_Read_Status(hnand);
while(ret!=NAND_READY)
{
if(ret==NAND_ERROR)
return NSTA_ERROR;
else if((HAL_GetTick()-tickstart)>NAND_READ_TIMEOUT)
return NSTA_TIMEOUT;
else
ret=HAL_NAND_Read_Status(hnand);
}
return NSTA_READY;
}
/***********************************************
从给定的校验码eccval中分离出奇数位或偶数位
上数字,以便判断是否发生存储或读取的错误。
入口参数:
oe:0=提取校验码的偶数位上的数字,1=提取校验码的奇数
位上的数字
eccval:校验码,本CPU使用汉明码,为24位,用32位整
型数变量存储,对应于512字节数据的汉明码。
出口参数:无
返回参数:给定校验码上偶数位或奇数位上数字构成的16
位码
************************************************/
u16 NAND_ECC_Get_OE(u8 oe,u32 eccval)
{
u8 i;
u16 ecctemp=0;
for(i=0;i<24;i++)
{
if((i%2)==oe)
{
if((eccval>>i)&0X01)ecctemp+=1<<(i>>1);
}
}
return ecctemp;
}
/***********************************************************
根据FMC计算的前后ECC修正错误参数,注意只有前后ECC值不同时
才调用该函数!
入口参数:
eccrd:FMC第一次计算的ECC码,为保存的ECC值
ecccl:FMC最新计算的ECC码
data:FMC最新计算ECC码所用数据,为固定大小的数据,用宏
定义变量标识
出口参数:data
返回参数:0=错误的data数据已修正,1=错误的data数据无法修正
已经检查过,但是有待调试通过
************************************************************/
u8 NAND_ECC_Correction(u8* data_buf,u32 eccrd,u32 ecccl)
{
u16 eccrdo,eccrde,eccclo,ecccle;
u16 eccchk=0;
u16 errorpos=0;
u32 bytepos=0;
eccrdo=NAND_ECC_Get_OE(1,eccrd); //获取eccrd的奇数位
eccrde=NAND_ECC_Get_OE(0,eccrd); //获取eccrd的偶数位
eccclo=NAND_ECC_Get_OE(1,ecccl); //获取ecccl的奇数位
ecccle=NAND_ECC_Get_OE(0,ecccl); //获取ecccl的偶数位
eccchk=eccrdo^eccrde^eccclo^ecccle;
if(eccchk==0XFFF) //全1,说明只有1bit ECC错误
{
errorpos=eccrdo^eccclo; //获取错误位的位置
bytepos=errorpos/8; //获取错误位所在字节
data_buf[bytepos]^=1<<(errorpos%8); //修改错误位上的数字
}
else if(eccchk!=0) //不是全1,说明至少有2bit ECC错误,无法修复
{
return NSTA_ERROR; //原来返回1
}
return NSTA_SUCCESS; //原来返回0
}
/*******************************************************************
读取NAND Flash芯片的ID标识,共读取8个字节,只有前5个字节有意思。
并且根据读取的NAND FLASH芯片的ID初始化有关变量成员。
入口参数:
ptr:芯片属性参数型Nand_AttributeTypeDef指针,参见myNand.h头文
件定义,使用到的变量成员有:ptr->ptrNandHandle
出口参数:
ptr:根据读取的ID值初始化指针ptr所指Nand_AttributeTypeDef类型变量
的某些成员。主要有如下一些成员:
ptr->page_totalsize(一页存储器所含有的字节数,包含主字节数和
备用字节数);
ptr->page_mainsize(一页的主字节数)
ptr->page_sparesize(一页的备用字节数)
ptr->block_pagenum(一块存储单元所包含的页数)
ptr->plane_blocknum(一面存储单元所包含的块数)
ptr->block_toltalnum(存储单元总的块数)
ptr->ptrNandHandle->Config.PageSize(一页存储单元所包含的主字节数)
ptr->ptrNandHandle->Config.SpareAreaSize(一页存储单元包含的备用字节数)
ptr->ptrNandHandle->Config.BlockSize(一块存储单元所包含的页数)
ptr->ptrNandHandle->BlockNbr(存储单元包含的总块数)
ptr->ptrNandHandle->PlaneNbr(存储单元包含的面数)
ptr->ptrNandHandle->PlaneSize(一面存储单元所包含的块数)
返回参数:
HAL_OK=函数执行成功,其它=函数执行失败。
******************