基于STM32CUBEMX的NAND FLASH

1.STM32CUBEMX配置

NAND FLASH配置(这里的RB脚我是使用的模拟信号)

参数配置

时钟配置

2.驱动模块

/*
 * dNAND.c
 *
 *  Created on: 2023/12/5
 *      Author: WZX
 */
 
 
#include "dNAND.h"
#include <stddef.h>


static NAND_TypeDef_t *nand;
static uint8_t dNAND_WaitForReady(void);
static uint8_t dNAND_WaitRB(volatile uint8_t rb);
static uint8_t dNAND_ECC_Correction(uint8_t *data_buf, uint32_t eccrd, uint32_t ecccl);


/*********************************************************************
 * @fn      dNAND_Init
 *
 * @brief   nand初始化.
 *
 * @param   nand_t:nand参数
 *
 * @return  none
 */
void dNAND_Init(NAND_TypeDef_t *nand_t)
{
	if(nand_t == NULL)
	{
		return;
	}
	nand = nand_t;
	
	nand->MPU_Init();
	nand->Reset();
	nand->Delay(100);
	nand->Nand_Dev.id = nand->Read_ID();
	dNAND_ModeSet(4);
	switch(nand->Nand_Dev.id)
	{
		case MT29F4G08ABADA:
			break;
		
		case MT29F16G08ABABA:
			nand->Nand_Dev.page_totalsize=2112;
			nand->Nand_Dev.page_mainsize=2048;
			nand->Nand_Dev.page_sparesize=64;
			nand->Nand_Dev.block_pagenum=64;
			nand->Nand_Dev.plane_blocknum=1024;
			nand->Nand_Dev.block_totalnum=2048; 
			break;
		
		case W29N01GVSIAA:
			nand->Nand_Dev.page_totalsize=2112;
			nand->Nand_Dev.page_mainsize=2048;
			nand->Nand_Dev.page_sparesize=64;
			nand->Nand_Dev.block_pagenum=64;
			nand->Nand_Dev.plane_blocknum=1024;
			nand->Nand_Dev.block_totalnum=2048;
			break;
		
		case W29N01HVSINA:
			nand->Nand_Dev.page_totalsize=2112;
			nand->Nand_Dev.page_mainsize=2048;
			nand->Nand_Dev.page_sparesize=64;
			nand->Nand_Dev.block_pagenum=64;
			nand->Nand_Dev.plane_blocknum=1024;
			nand->Nand_Dev.block_totalnum=1024; 
			break;
		
		default:
			break;
	}
}


/*********************************************************************
 * @fn      dNAND_ModeSet
 *
 * @brief   nand模式选择.
 *
 * @param   mode:模式
 *
 * @return  none
 */
void dNAND_ModeSet(uint8_t mode)
{   
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_CMD) = NAND_FEATURE;
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = 0X01;
 	*(volatile uint8_t *)NAND_ADDRESS = mode;
	*(volatile uint8_t *)NAND_ADDRESS = 0;
	*(volatile uint8_t *)NAND_ADDRESS = 0;
	*(volatile uint8_t *)NAND_ADDRESS = 0;
    dNAND_WaitForReady();
}

/*********************************************************************
 * @fn      dNAND_ReadSpare
 *
 * @brief   nand读取备用区.
 *
 * @param   PageNum:页地址,范围0-block_pagenum*block_totalnum-1
 *
 * @param	ColNum:区域地址,范围0-page_sparesize-1
 *
 * @param	pBuffer:数据缓冲区
 *
 * @param	NumByteToRead:读取的字节数
 *
 * @return  none
 */
void dNAND_ReadSpare(uint32_t PageNum, uint16_t ColNum, uint8_t *pBuffer, uint16_t NumByteToRead)
{
    uint8_t remainbyte = nand->Nand_Dev.page_sparesize - ColNum;
	
    if(NumByteToRead > remainbyte)
	{
		NumByteToRead = remainbyte;
	}
    dNAND_ReadPage(PageNum, ColNum + nand->Nand_Dev.page_mainsize, pBuffer, NumByteToRead);
} 


/*********************************************************************
 * @fn      dNAND_WriteSpare
 *
 * @brief   nand写入备用区.
 *
 * @param   PageNum:页地址,范围0-block_pagenum*block_totalnum-1
 *
 * @param	ColNum:区域地址,范围0-page_sparesize-1
 *
 * @param	pBuffer:数据缓冲区
 *
 * @param	NumByteToRead:写入的字节数
 *
 * @return  none
 */
void dNAND_WriteSpare(uint32_t PageNum, uint16_t ColNum, uint8_t *pBuffer, uint16_t NumByteToWrite)
{
    uint8_t remainbyte = nand->Nand_Dev.page_sparesize - ColNum;;
	
    if(NumByteToWrite > remainbyte)
	{
		NumByteToWrite = remainbyte;
	}
    dNAND_WritePage(PageNum, ColNum + nand->Nand_Dev.page_mainsize, pBuffer, NumByteToWrite);
} 


/*********************************************************************
 * @fn      dNAND_EraseBlock
 *
 * @brief   nand块擦除.
 *
 * @param   BlockNum:块编号,范围0-block_totalnum-1
 *
 * @return  none
 */
void dNAND_EraseBlock(uint32_t BlockNum)
{
	if(nand->Nand_Dev.id == MT29F16G08ABABA) {
		BlockNum <<= 7;
	} else if(nand->Nand_Dev.id == MT29F4G08ABADA) {
		BlockNum <<= 6;
	} else if(nand->Nand_Dev.id == W29N01HVSINA) {
		BlockNum <<= 6;
	}
  
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_CMD) = NAND_ERASE0;
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)BlockNum;
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)(BlockNum>>8);
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)(BlockNum>>16);
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_CMD) = NAND_ERASE1;
	dNAND_WaitForReady();  
}


/*********************************************************************
 * @fn      dNAND_EraseChip
 *
 * @brief   nand芯片擦除.
 *
 * @param   none
 *
 * @return  none
 */
void dNAND_EraseChip(void)
{
    for(uint16_t i = 0; i<nand->Nand_Dev.block_totalnum; i++)
    {
        dNAND_EraseBlock(i);
    }
}


/*********************************************************************
 * @fn      dNAND_ReadPage
 *
 * @brief   nand读取页数据.
 *
 * @param   PageNum:页地址,范围0-block_pagenum*block_totalnum-1
 *
 * @param	ColNum:区域地址,范围0-page_sparesize-1
 *
 * @param	pBuffer:数据缓冲区
 *
 * @param	NumByteToRead:读取的字节数
 *
 * @return  none
 */
void dNAND_ReadPage(uint32_t PageNum, uint16_t ColNum, uint8_t *pBuffer, uint16_t NumByteToRead)
{
	volatile uint16_t i = 0;
	uint8_t res = 0;
	uint8_t eccnum = 0;
	uint8_t eccstart = 0;
	uint8_t *p;
	
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_CMD) = NAND_AREA_A;
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)ColNum;
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)(ColNum>>8);
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)PageNum;
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)(PageNum>>8);
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)(PageNum>>16);
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_CMD) = NAND_AREA_TRUE1;

	res = dNAND_WaitRB(0);
    if(res) {
		return;
	}
	res = dNAND_WaitRB(1);
    if(res) {
		return;
	}
	
	if(NumByteToRead % NAND_ECC_SECTOR_SIZE) { 
		for(i = 0; i < NumByteToRead; i++) {
			*(volatile uint8_t *)pBuffer++ = *(volatile uint8_t *)NAND_ADDRESS;
		}
	} else {
		eccnum = NumByteToRead / NAND_ECC_SECTOR_SIZE;
		eccstart = ColNum / NAND_ECC_SECTOR_SIZE;
		p = pBuffer;
		
		for(res = 0; res < eccnum; res++) {
			nand->ECC_Enable();
			for(i = 0; i < NAND_ECC_SECTOR_SIZE; i++) {
				*(volatile uint8_t *)pBuffer++ = *(volatile uint8_t *)NAND_ADDRESS;
			}		
			nand->ECC_Fifo_Empty();
			nand->Nand_Dev.ecc_hdbuf[res + eccstart] = nand->ECC_Get();
			nand->ECC_Disable();
		} 
		i = nand->Nand_Dev.page_mainsize + 0X10 + eccstart*4;
		nand->Delay(30);
		
		*(volatile uint8_t *)(NAND_ADDRESS | NAND_CMD) = 0X05;
		*(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)i;
		*(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)(i>>8);
		*(volatile uint8_t *)(NAND_ADDRESS | NAND_CMD) = 0XE0;
		nand->Delay(30);
		
		pBuffer = (uint8_t*)&nand->Nand_Dev.ecc_rdbuf[eccstart];
		for(i = 0; i < 4*eccnum; i++) {
			*(volatile uint8_t *)pBuffer++ = *(volatile uint8_t *)NAND_ADDRESS;
		}			
		for(i = 0; i < eccnum; i++) {
			if(nand->Nand_Dev.ecc_rdbuf[i + eccstart] != nand->Nand_Dev.ecc_hdbuf[i + eccstart]) {
				dNAND_ECC_Correction(p + NAND_ECC_SECTOR_SIZE*i, \
									nand->Nand_Dev.ecc_rdbuf[i + eccstart], \
									nand->Nand_Dev.ecc_hdbuf[i + eccstart]);
			} 
		} 		
	}
    dNAND_WaitForReady();
} 


/*********************************************************************
 * @fn      dNAND_ReadPageComp
 *
 * @brief   nand读取对比页数据,main区和spare区都可以用.
 *
 * @param   PageNum:页地址,范围0-block_pagenum*block_totalnum-1
 *
 * @param	ColNum:区域地址,范围0-page_sparesize-1
 *
 * @param	CmpVal:对比的值,32bit位单位对比
 *
 * @param	NumByteToRead:读取的字节数
 *
 * @param	NumByteEqual:从初始位置和CmpVal相同的值个数
 *
 * @return  none
 */
void dNAND_ReadPageComp(uint32_t PageNum, uint16_t ColNum, uint32_t CmpVal, uint16_t NumByteToRead, uint16_t *NumByteEqual)
{
    uint16_t i = 0;
	uint8_t res = 0;
	
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_CMD) = NAND_AREA_A;
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)ColNum;
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)(ColNum>>8);
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)PageNum;
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)(PageNum>>8);
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)(PageNum>>16);
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_CMD) = NAND_AREA_TRUE1;

	res = dNAND_WaitRB(0);
    if(res) {
		return;
	}
	res = dNAND_WaitRB(1);
    if(res) {
		return;
	}
	
    for(i = 0; i < NumByteToRead; i++) {
		if(*(volatile uint32_t *)NAND_ADDRESS != CmpVal) {
			break;
		}
    }
	
	*NumByteEqual = i;
    dNAND_WaitForReady();
} 


/*********************************************************************
 * @fn      dNAND_WritePage
 *
 * @brief   nand写入页数据.
 *
 * @param   PageNum:页地址,范围0-block_pagenum*block_totalnum-1
 *
 * @param	ColNum:区域地址,范围0-page_sparesize-1
 *
 * @param	pBuffer:数据缓冲区
 *
 * @param	NumByteToWrite:写入的字节数
 *
 * @return  none
 */
void dNAND_WritePage(uint32_t PageNum, uint16_t ColNum, uint8_t *pBuffer, uint16_t NumByteToWrite)
{
	volatile uint16_t i = 0;  
	uint8_t res = 0;
	uint8_t eccnum = 0;
	uint8_t eccstart = 0;
	
	*(volatile uint8_t *)(NAND_ADDRESS | NAND_CMD) = NAND_WRITE0;
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)ColNum;
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)(ColNum>>8);
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)PageNum;
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)(PageNum>>8);
  
	nand->Delay(30);
	if(NumByteToWrite % NAND_ECC_SECTOR_SIZE) {  
		for(i = 0; i < NumByteToWrite; i++) {
			*(volatile uint8_t *)NAND_ADDRESS = *(volatile uint8_t *)pBuffer++;
		}
	} else {
		eccnum = NumByteToWrite / NAND_ECC_SECTOR_SIZE;
		eccstart = ColNum / NAND_ECC_SECTOR_SIZE; 
		
 		for(res = 0; res < eccnum; res++) {
			nand->ECC_Enable();
			for(i = 0; i < NAND_ECC_SECTOR_SIZE; i++) {
				*(volatile uint8_t *)NAND_ADDRESS = *(volatile uint8_t *)pBuffer++;
			}		
			nand->ECC_Fifo_Empty();
			nand->Nand_Dev.ecc_hdbuf[res + eccstart] = nand->ECC_Get();
  			nand->ECC_Disable();
		}  
		i = nand->Nand_Dev.page_mainsize + 0X10 + eccstart*4;
		nand->Delay(30);
		
		*(volatile uint8_t *)(NAND_ADDRESS | NAND_CMD) = 0X85;
		*(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)i;
		*(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)(i>>8);
		nand->Delay(30);
		
		pBuffer = (uint8_t *)&nand->Nand_Dev.ecc_hdbuf[eccstart];
		for(i = 0; i < eccnum; i++) { 
			for(res = 0; res < 4; res++) {
				*(volatile uint8_t *)NAND_ADDRESS = *(volatile uint8_t *)pBuffer++;
			}
		} 		
	}
  
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_CMD) = NAND_WRITE_TURE1; 
    dNAND_WaitForReady();
}


/*********************************************************************
 * @fn      dNAND_WritePageConst
 *
 * @brief   nand写入页指定长指定数据.
 *
 * @param   PageNum:页地址,范围0-block_pagenum*block_totalnum-1
 *
 * @param	ColNum:区域地址,范围0-page_sparesize-1
 *
 * @param	cval:写入数据
 *
 * @param	NumByteToWrite:写入的字节数
 *
 * @return  none
 */
void dNAND_WritePageConst(uint32_t PageNum, uint16_t ColNum, uint32_t cval, uint16_t NumByteToWrite)
{
    uint16_t i = 0; 
	
	*(volatile uint8_t *)(NAND_ADDRESS | NAND_CMD) = NAND_WRITE0;
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)ColNum;
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)(ColNum>>8);
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)PageNum;
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)(PageNum>>8);
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)(PageNum>>16);
	nand->Delay(30);
	
	for(i = 0; i < NumByteToWrite; i++) {
		*(volatile uint32_t*)NAND_ADDRESS = cval;
	} 
	
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_CMD) = NAND_WRITE_TURE1; 
    dNAND_WaitForReady(); 
}


/*********************************************************************
 * @fn      dNAND_CopyPageWithoutWrite
 *
 * @brief   nand拷贝数据不写新的数据.
 *
 * @param   Source_PageNum:源地址,范围0-block_pagenum*block_totalnum-1
 *
 * @param	Dest_PageNum:目标地址,范围0-block_pagenum*block_totalnum-1
 *
 * @return  none
 */
void dNAND_CopyPageWithoutWrite(uint32_t Source_PageNum, uint32_t Dest_PageNum)
{
	uint8_t res = 0;
    uint16_t source_block = 0, dest_block = 0;  

    source_block = Source_PageNum / nand->Nand_Dev.block_pagenum;
    dest_block = Dest_PageNum / nand->Nand_Dev.block_pagenum;
    if((source_block % 2) != (dest_block % 2)) {
		return;
	}		
	
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_CMD) = NAND_MOVEDATA_CMD0;
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)0;
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)0;
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)Source_PageNum;
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)(Source_PageNum>>8);
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)(Source_PageNum>>16);
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_CMD) = NAND_MOVEDATA_CMD1;

	res = dNAND_WaitRB(0);
    if(res) {
		return;
	}
	res = dNAND_WaitRB(1);
    if(res) {
		return;
	} 
	
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_CMD) = NAND_MOVEDATA_CMD2;
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)0;
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)0;
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)Dest_PageNum;
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)(Dest_PageNum>>8);
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)(Dest_PageNum>>16);
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_CMD) = NAND_MOVEDATA_CMD3; 
    dNAND_WaitForReady();
}


/*********************************************************************
 * @fn      dNAND_CopyPageWithWrite
 *
 * @brief   nand拷贝数据写入新的数据.
 *
 * @param   Source_PageNum:源地址,范围0-block_pagenum*block_totalnum-1
 *
 * @param	Dest_PageNum:目标地址,范围0-block_pagenum*block_totalnum-1
 *
 * @param	ColNum:区域地址,范围0-page_sparesize-1
 *
 * @param	pBuffer:数据缓存区
 *
 * @param	NumByteToWrite:写入数据字节数
 *
 * @return  none
 */
void dNAND_CopyPageWithWrite(uint32_t Source_PageNum, uint32_t Dest_PageNum, uint16_t ColNum, uint8_t *pBuffer, uint16_t NumByteToWrite)
{
	uint8_t res = 0;
	volatile uint16_t i = 0;
	uint16_t source_block = 0, dest_block = 0;  
	uint8_t eccnum = 0;
	uint8_t eccstart = 0;

    source_block = Source_PageNum / nand->Nand_Dev.block_pagenum;
    dest_block = Dest_PageNum / nand->Nand_Dev.block_pagenum;
    if((source_block % 2) != (dest_block % 2)) {
		return;
	}
	
	*(volatile uint8_t *)(NAND_ADDRESS | NAND_CMD) = NAND_MOVEDATA_CMD0;
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)0;
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)0;
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)Source_PageNum;
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)(Source_PageNum>>8);
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)(Source_PageNum>>16);
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_CMD) = NAND_MOVEDATA_CMD1;
    

	res = dNAND_WaitRB(0);
    if(res) {
		return;
	}
	res = dNAND_WaitRB(1);
    if(res) {
		return;
	} 
	
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_CMD) = NAND_MOVEDATA_CMD2;
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)ColNum;
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)(ColNum>>8);
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)Dest_PageNum;
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)(Dest_PageNum>>8);
    *(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)(Dest_PageNum>>16); 
	nand->Delay(30);
	
	if(NumByteToWrite % NAND_ECC_SECTOR_SIZE) {  
		for(i = 0; i < NumByteToWrite; i++) {
			*(volatile uint8_t *)NAND_ADDRESS = *(volatile uint8_t *)pBuffer++;
		}
	} else {
		eccnum = NumByteToWrite / NAND_ECC_SECTOR_SIZE;
		eccstart = ColNum / NAND_ECC_SECTOR_SIZE;
 		
		for(res = 0; res < eccnum; res++) {
			nand->ECC_Enable();
			for(i = 0; i < NAND_ECC_SECTOR_SIZE; i++) {
				*(volatile uint8_t *)NAND_ADDRESS = *(volatile uint8_t *)pBuffer++;
			}		
			nand->ECC_Fifo_Empty();
			nand->Nand_Dev.ecc_hdbuf[res + eccstart] = nand->ECC_Get();
 			nand->ECC_Disable();
		}  
		i = nand->Nand_Dev.page_mainsize + 0X10 + eccstart*4;
		nand->Delay(30);
		
		*(volatile uint8_t *)(NAND_ADDRESS | NAND_CMD) = 0X85;
		*(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)i;
		*(volatile uint8_t *)(NAND_ADDRESS | NAND_ADDR) = (uint8_t)(i>>8);
		nand->Delay(30);		
		pBuffer = (uint8_t *)&nand->Nand_Dev.ecc_hdbuf[eccstart];
		
		for(i = 0; i < eccnum; i++) { 
			for(res = 0; res < 4; res++) {
				*(volatile uint8_t *)NAND_ADDRESS = *(volatile uint8_t *)pBuffer++;
			}
		} 		
	}
	
    *(volatile uint8_t*)(NAND_ADDRESS|NAND_CMD)=NAND_MOVEDATA_CMD3;
    dNAND_WaitForReady();
} 


/*********************************************************************
 * @fn      dNAND_WaitForReady
 *
 * @brief   nand等待信号.
 *
 * @param   none
 *
 * @return  0--正确,1--超时
 */
static uint8_t dNAND_WaitForReady(void)
{
	uint8_t status = 0;
    volatile uint32_t time = 0;

	while(1)
	{
		status = nand->Read_Status();
		if(status & 0x01)
		{
			break;
		}
		if(time++ >= 0x1FFFF)
		{
			return NSTA_TIMEOUT;
		}
	}
	return NSTA_READY;
}


/*********************************************************************
 * @fn      dNAND_WaitRB
 *
 * @brief   nand等待rb信号.
 *
 * @param   rb:rb信号线
 *
 * @return  0--正确,1--超时
 */
static uint8_t dNAND_WaitRB(volatile uint8_t rb)
{
	volatile uint16_t time = 0;
	
	while(time < 10000)
	{
		time++;
		if(nand->Get_RB() == rb)
		{
			return 0;
		}
	}
	return 1;
}


/*********************************************************************
 * @fn      dNAND_ECC_Get_OE
 *
 * @brief   nand获取ecc奇偶数.
 *
 * @param   oe:0--偶数位,1--奇数位
 *
 * @param	eccval:输入ecc值
 *
 * @return  返回计算的ecc值
 */
static uint16_t dNAND_ECC_Get_OE(uint8_t oe, uint32_t eccval)
{
	uint8_t i;
	uint16_t ecctemp = 0;
	
	for(i = 0; i < 24; i++)
	{
		if((i%2) == oe)
		{
			if((eccval>>i) & 0X01)
			{
				ecctemp += 1<<(i>>1); 
			}
		}
	}
	return ecctemp;
}


/*********************************************************************
 * @fn      dNAND_ECC_Correction
 *
 * @brief   nand ecc校正.
 *
 * @param   eccrd:保存的ecc值
 *
 * @param	ecccl:硬件ecc值
 *
 * @return  0--错误已修正,1--大于2个bit错误无法修正
 */
static uint8_t dNAND_ECC_Correction(uint8_t *data_buf, uint32_t eccrd, uint32_t ecccl)
{
	uint16_t eccrdo, eccrde, eccclo, ecccle;
	uint16_t eccchk = 0;
	uint16_t errorpos = 0; 
	uint32_t bytepos = 0;
	
	eccrdo = dNAND_ECC_Get_OE(1, eccrd);
	eccrde = dNAND_ECC_Get_OE(0, eccrd);
	eccclo = dNAND_ECC_Get_OE(1, ecccl);
	ecccle = dNAND_ECC_Get_OE(0, 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
	{
		return 1;
	} 
	return 0;
}


/*
 * dNAND.h
 *
 *  Created on: 2023/12/5
 *      Author: WZX
 */
 
 
#ifndef _DNAND_H_
#define _DNAND_H_


#include <stdint.h>


#define NAND_MAX_PAGE_SIZE			2048		//定义NAND FLASH的最大的PAGE大小(不包括SPARE区),默认4096字节
#define NAND_ECC_SECTOR_SIZE		512			//执行ECC计算的单元大小,默认512字节

//NAND FLASH型号和对应的ID号
#define MT29F4G08ABADA				0XDC909556	//MT29F4G08ABADA
#define MT29F16G08ABABA				0X48002689	//MT29F16G08ABABA
#define W29N01GVSIAA				0XEFF18095	//W29N01GVSIAA
#define W29N01HVSINA				0XEFF10095	//W29N01HVSINA

#define NAND_ADDRESS				0X80000000	//nand flash的访问地址,接NCE3,地址为:0X8000 0000
#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

//NAND FLASH状态
#define NSTA_READY       	   		0X40		//nand已经准备好
#define NSTA_ERROR					0X01		//nand错误
#define NSTA_TIMEOUT        		0X02		//超时
#define NSTA_ECC1BITERR       		0X03		//ECC 1bit错误
#define NSTA_ECC2BITERR       		0X04		//ECC 2bit以上错误


#define NAND_REGION_NUMBER      	MPU_REGION_NUMBER3	    //NAND FLASH使用region0
#define NAND_ADDRESS_START      	0X80000000              //NAND FLASH区的首地址
#define NAND_REGION_SIZE        	MPU_REGION_SIZE_256MB   //NAND FLASH区大小


typedef struct
{
    uint16_t page_totalsize;     				//每页总大小,main区和spare区总和
    uint16_t page_mainsize;      				//每页的main区大小
    uint16_t page_sparesize;     				//每页的spare区大小
    uint8_t  block_pagenum;      				//每个块包含的页数量
    uint16_t plane_blocknum;     				//每个plane包含的块数量
    uint16_t block_totalnum;     				//总的块数量
    uint16_t good_blocknum;      				//好块数量    
    uint16_t valid_blocknum;     				//有效块数量(供文件系统使用的好块数量)
    uint32_t id;             					//NAND FLASH ID
    uint16_t *lut;      			   			//LUT表,用作逻辑块-物理块转换
	uint32_t ecc_hard;							//硬件计算出来的ECC值
	uint32_t ecc_hdbuf[NAND_MAX_PAGE_SIZE/NAND_ECC_SECTOR_SIZE];//ECC硬件计算值缓冲区  	
	uint32_t ecc_rdbuf[NAND_MAX_PAGE_SIZE/NAND_ECC_SECTOR_SIZE];//ECC读取的值缓冲区
} Nand_Attriute; 


typedef struct
{
	Nand_Attriute Nand_Dev;
	uint8_t (*MPU_Init)(void);
	uint8_t (*Reset)(void);
	uint8_t (*Delay)(volatile uint16_t ms);
	uint32_t (*Read_ID)(void);
	uint8_t (*Get_RB)(void);
	uint8_t (*ECC_Enable)(void);
	uint8_t (*ECC_Disable)(void);
	uint32_t (*ECC_Get)(void);
	uint32_t (*Read_Status)(void);
	uint8_t (*ECC_Fifo_Empty)(void);
} NAND_TypeDef_t;


void dNAND_Init(NAND_TypeDef_t *nand_t);
void dNAND_ModeSet(uint8_t mode);
void dNAND_ReadSpare(uint32_t PageNum, uint16_t ColNum, uint8_t *pBuffer, uint16_t NumByteToRead);
void dNAND_WriteSpare(uint32_t PageNum, uint16_t ColNum, uint8_t *pBuffer, uint16_t NumByteToWrite);
void dNAND_EraseBlock(uint32_t BlockNum);
void dNAND_EraseChip(void);
void dNAND_ReadPage(uint32_t PageNum, uint16_t ColNum, uint8_t *pBuffer, uint16_t NumByteToRead);
void dNAND_ReadPageComp(uint32_t PageNum, uint16_t ColNum, uint32_t CmpVal, uint16_t NumByteToRead, uint16_t *NumByteEqual);
void dNAND_WritePage(uint32_t PageNum, uint16_t ColNum, uint8_t *pBuffer, uint16_t NumByteToWrite);
void dNAND_WritePageConst(uint32_t PageNum, uint16_t ColNum, uint32_t cval, uint16_t NumByteToWrite);
void dNAND_CopyPageWithoutWrite(uint32_t Source_PageNum, uint32_t Dest_PageNum);
void dNAND_CopyPageWithWrite(uint32_t Source_PageNum, uint32_t Dest_PageNum, uint16_t ColNum,uint8_t *pBuffer, uint16_t NumByteToWrite);


#endif


3.初始化

/*
 * Cmd_NAND.c
 *
 *  Created on: 2023/12/5
 *      Author: WZX
 */
 
 
#include "Cmd_NAND.h"
#include "dNAND.h"
#include "stm32h7xx.h"


extern NAND_HandleTypeDef hnand1;
static uint8_t nand_reset(void);
static uint8_t nand_mpu_init(void);
static uint8_t nand_delay(volatile uint16_t ms);
static uint32_t nand_read_id(void);
static uint8_t nand_get_rb(void);
static uint8_t nand_ecc_enable(void);
static uint8_t nand_ecc_disable(void);
static uint32_t nand_ecc_get(void);
static uint32_t nand_read_status(void);
static uint8_t nand_wait_fifo_empty(void);


/*********************************************************************
 * @fn      Cmd_NAND_Init
 *
 * @brief   nand初始化.
 *
 * @param   none
 *
 * @return  none
 */
void Cmd_NAND_Init(void)
{
	static NAND_TypeDef_t nand_temp;

	nand_temp.Reset = nand_reset;
	nand_temp.MPU_Init = nand_mpu_init;
	nand_temp.Delay = nand_delay;
	nand_temp.Read_ID = nand_read_id;
	nand_temp.Get_RB = nand_get_rb;
	nand_temp.ECC_Enable = nand_ecc_enable; 
	nand_temp.ECC_Disable = nand_ecc_disable;
	nand_temp.ECC_Get = nand_ecc_get;
	nand_temp.Read_Status = nand_read_status;
	nand_temp.ECC_Fifo_Empty = nand_wait_fifo_empty;
	dNAND_Init(&nand_temp);
}


//复位
static uint8_t nand_reset(void)
{
	if(HAL_NAND_Reset(&hnand1) == HAL_OK)
	{
		return 0;
	}
	return 1;
}

static uint8_t nand_mpu_init(void)
{
	MPU_Region_InitTypeDef MPU_Initure;
	
	HAL_MPU_Disable();
	
	MPU_Initure.Enable = MPU_REGION_ENABLE;
	MPU_Initure.Number = NAND_REGION_NUMBER;
	MPU_Initure.BaseAddress = NAND_ADDRESS_START;
	MPU_Initure.Size = NAND_REGION_SIZE;
	MPU_Initure.SubRegionDisable = 0X00;
	MPU_Initure.TypeExtField = MPU_TEX_LEVEL0;
	MPU_Initure.AccessPermission = MPU_REGION_FULL_ACCESS;
	MPU_Initure.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
	MPU_Initure.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
	MPU_Initure.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
	MPU_Initure.IsBufferable = MPU_ACCESS_BUFFERABLE;
	HAL_MPU_ConfigRegion(&MPU_Initure);
	
	HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
	return 0;
}

static uint8_t nand_delay(volatile uint16_t ms)
{
	//HAL_Delay(ms);
	volatile uint32_t index = 0; 
	
	for(index = ms; index != 0; index--)
	{
		
	}
	return 0;
}

static uint32_t nand_read_id(void)
{
	NAND_IDTypeDef nand_id;
	uint32_t nandid;
	
	if(HAL_NAND_Read_ID(&hnand1, &nand_id) == HAL_OK)
	{
		nandid = (nand_id.Maker_Id << 24) | (nand_id.Device_Id << 16) | \
					(nand_id.Third_Id << 8) | (nand_id.Fourth_Id);
		return nandid;
	}
	return 0xFFFFFFFF;
}

static uint8_t nand_get_rb(void)
{
	return HAL_GPIO_ReadPin(GPIOD, GPIO_PIN_6);
}

static uint8_t nand_ecc_enable(void)
{
	if(HAL_NAND_ECC_Enable(&hnand1) == HAL_OK)
	{
		return 0;
	}
	return 1;
}

static uint8_t nand_ecc_disable(void)
{
	if(HAL_NAND_ECC_Disable(&hnand1) == HAL_OK)
	{
		return 0;
	}
	return 1;
}

static uint32_t nand_ecc_get(void)
{
	uint32_t ECCval;
	
	if(HAL_NAND_GetECC(&hnand1, &ECCval, 1) == HAL_OK)
	{
		return ECCval;
	}
	return 1;
}

static uint32_t nand_read_status(void)
{
	return HAL_NAND_Read_Status(&hnand1);
}

static uint8_t nand_wait_fifo_empty(void)
{
	while(!(FMC_Bank3_R->SR&(1<<6)));
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值