📄 fat12.c
字号:
{
NAND_Operation_ReturnValue = NAND_FAIL;
return NULL;
}
else
{
NAND_Operation_ReturnValue = NAND_PASS;
return buffer;
}
}
}
/**************** 函数 WORD WriteLgSector(DWORD LogicSectorNumber,BYTE* buffer) *********************/
/* 功能:将Buffer中的数据写入由逻辑扇区号确定的页中,该页的实际物理地址在该函数内自动计算; */
/* 在写入时如果发现某一扇区损坏该函数将自动寻找一个空块写入,同时将原块中的数据拷贝到新块中。 */
/* 并将旧块标记为坏块;找到新块后逻辑映射表将被更改。无论是否改变物理块,该块中该扇 */
/* 区的C区中都将写入逻辑块号 */
/* 注意:调用此函数前Buffer中要事先存放要写入的数据 */
/* 入口参数:DWORD LogicSectorNumber----逻辑扇区号,整个FLASH连续统一编号,逻辑扇区与逻辑块对应 */
/* BYTE* buffer----准备写到逻辑扇区的数据缓冲区,512字节长 */
/* 返回值:写入的物理块号; 0xffff表示找不到空块,盘已满; */
/* 说明:非用户使用函数 */
/****************************************************************************************************/
WORD WriteLgSector(DWORD LogicSectorNumber,BYTE* buffer)//OK
{
WORD oldphblocknum,newphblocknum;
DWORD phaddress;
BYTE tempSectorSpare[16];
WORD LogicBlocknum;
BYTE pageoffsetnum;
NAND_Ret Return_value;
if(LogicSectorNumber >= (FAT_SIZEOFWORD<<5))
{ return 0xffff; }
LogicBlocknum = SectorNumToClusterNum(LogicSectorNumber);
pageoffsetnum = LogicSectorNumber % SectorsPerCluster;
oldphblocknum = lgToph[LogicBlocknum];
if(oldphblocknum == 0xffff)
{
oldphblocknum = Find_BlankPhBlock(0);
lgToph[LogicBlocknum] = oldphblocknum;
}
phaddress = (oldphblocknum<<14) | (pageoffsetnum<<9); //求新块中对应于旧块原偏移扇区的位置
//首先判断该页是否为空
NAND_SpareRead(phaddress+2, &tempSectorSpare[2], 1);
if(tempSectorSpare[2] != 0xff) //该页非空
{
newphblocknum = Process_PageErrorOrFull(LogicSectorNumber,buffer);
return newphblocknum;
}
else //该页空
{
tempSectorSpare[2] = 0x0f;
NAND_SpareProgram(phaddress + 2, &tempSectorSpare[2], 1); //在FLASH中将该新扇区的数据起始标记写入
Return_value = NAND_PageProgram(phaddress, buffer, BytesPerSector);
if( Return_value == NAND_FAIL)
{
newphblocknum = Process_PageErrorOrFull(LogicSectorNumber,buffer);
if(newphblocknum == 0xffff)
{ return 0xffff; }
else
{ return newphblocknum; }
}
else
{
Process_Spare(LogicSectorNumber,buffer); //写入逻辑号和ECC码
tempSectorSpare[3] = 0x00;
Return_value = NAND_SpareProgram(phaddress + 3, &tempSectorSpare[3], 1);//在FLASH中将该新扇区的数据结束标记写入
return oldphblocknum;
}
}
}
/*********** 函数 WORD Process_PageErrorOrFull(DWORD LogicSectorNumber,BYTE* buffer) ****************/
/* 功能:在写页时该页非空或出错时进行处理,具体过程是寻找一个空物理块,将对应新页以前的旧块中的扇 */
/* 区内容拷贝到新块中,再将新的数据写到入口扇区中,同时将旧块标记为无效。另外更改逻辑映射表 */
/* 入口参数:DWORD LogicSectorNumber-----准备写入的逻辑扇区号 */
/* BYTE* buffer----准备写到逻辑扇区的数据缓冲区,512字节长 */
/* 返回值:找到的空物理块号. 0xffff表示找不到空块,盘已满 */
/*注意:此函数仅被WriteLgSector函数调用 */
/* 说明:非用户使用函数 */
/****************************************************************************************************/
WORD Process_PageErrorOrFull(DWORD LogicSectorNumber,BYTE* buffer)//OK
{
WORD newphblocknum,oldphblocknum,i,j;
WORD LogicBlocknum;
BYTE pageoffsetnum;
DWORD phaddress;
BYTE tempsectorbuffer[512];
BYTE tempSectorSpare[16];
NAND_Ret Return_value,fail;
LogicBlocknum = SectorNumToClusterNum(LogicSectorNumber);
pageoffsetnum = LogicSectorNumber % SectorsPerCluster;
oldphblocknum = lgToph[LogicBlocknum];
do
{
//寻找一个空块,逻辑地址对应的块号即为实际物理位置
newphblocknum = Find_BlankPhBlock(oldphblocknum+1);
if(newphblocknum != 0xffff) //找到了空块,改变逻辑映射表
{
lgToph[LogicBlocknum] = newphblocknum;
phaddress = (newphblocknum<<14) | (pageoffsetnum<<9); //求新块中对应于旧块原偏移扇区的位置
}
else
{ return 0xffff; } //FLASH满,无法找到空块写入扇区
//此时更新了块,需要把旧块中其他扇区的内容复制到新块中对应的扇区中
for(j=1; j<4; j++) //连续写三次,不成功则表明该块确实坏
{
fail = NAND_PASS;
tempSectorSpare[2] = 0x0f;
Return_value = NAND_SpareProgram((newphblocknum<<14)|2, &tempSectorSpare[2], 1);//在FLASH中将该新块0扇区的数据起始标记写入
if(Return_value != NAND_PASS)
{ fail = NAND_FAIL; }
for(i=1; i<SectorsPerCluster; i++)
{
if(i!=pageoffsetnum)
{
//首先判断该页是否为空
NAND_SpareRead((oldphblocknum<<14)|(i<<9), tempSectorSpare, 16);
if(tempSectorSpare[2] != 0xff) //该页非空
{
NAND_PageRead((oldphblocknum<<14)|(i<<9), tempsectorbuffer, BytesPerSector);
Return_value = NAND_PageProgram((newphblocknum<<14)|(i<<9), tempsectorbuffer, BytesPerSector);
NAND_SpareProgram((newphblocknum<<14)|(i<<9), tempSectorSpare, 16);
//Return_value = NAND_CopyBack((oldphblocknum<<14)|(i<<9), (newphblocknum<<14)|(i<<9));//该函数有问题
if(Return_value != NAND_PASS)
{ fail = NAND_FAIL; }
}
}
}
//开始向准备写入的扇区写新的数据
if(pageoffsetnum != 0)
{
tempSectorSpare[2] = 0x0f;
NAND_SpareProgram(phaddress + 2, &tempSectorSpare[2], 1); //在FLASH中将该新扇区的数据起始标记写入
if(Return_value != NAND_PASS)
{ fail = NAND_FAIL; }
}
Return_value = NAND_PageProgram(phaddress, buffer, BytesPerSector);
if(Return_value != NAND_PASS)
{ fail = NAND_FAIL; }
Return_value = Process_Spare(LogicSectorNumber,buffer); //生成并写入ECC码和逻辑块号信息
if(Return_value != NAND_PASS)
{ fail = NAND_FAIL; }
tempSectorSpare[3] = 0x00;
Return_value = NAND_SpareProgram(phaddress + 3, &tempSectorSpare[3], 1); //在FLASH中将该新扇区的数据结束标记写入
if(Return_value != NAND_PASS)
{ fail = NAND_FAIL; }
//开始拷贝块内0扇区的数据
if(pageoffsetnum != 0)
{
NAND_PageRead(oldphblocknum<<14, tempsectorbuffer, BytesPerSector);
NAND_SpareRead(oldphblocknum<<14, tempSectorSpare, 16);
Return_value = NAND_PageProgram(newphblocknum<<14, tempsectorbuffer, BytesPerSector);
if(Return_value != NAND_PASS)
{ fail = NAND_FAIL; }
Return_value = NAND_SpareProgram((newphblocknum<<14)|6, &tempSectorSpare[6], 10);//在FLASH中将该新块的0扇区的其他信息写入
if(Return_value != NAND_PASS)
{ fail = NAND_FAIL; }
tempSectorSpare[3] = 0x00;
Return_value = NAND_SpareProgram((newphblocknum<<14)|3, &tempSectorSpare[3], 1);//在FLASH中将该新块0扇区的数据结束标记写入
if(Return_value != NAND_PASS)
{ fail = NAND_FAIL; }
}
if(fail == NAND_FAIL)
{ NAND_BlockErase(newphblocknum); }
else
{ break; }
}
if(fail == NAND_FAIL) //连续三次写入失败,表明该新块是坏块
{
tempSectorSpare[5] = 0;
NAND_SpareProgram((newphblocknum<<14)|5, &tempSectorSpare[5], 1); //在FLASH中将该块的坏块标记信息写入
}
}while(fail == NAND_FAIL);
//以上拷贝成功完成,返回新找到的物理块号
//tempSectorSpare[4] = 0xf0;
//NAND_SpareProgram((oldphblocknum<<14)|4, &tempSectorSpare[4], 1); //在FLASH中将旧块的数据无效标记信息写入
NAND_BlockErase(oldphblocknum);
return newphblocknum;
}
/****************** 函数 NAND_Ret Process_Spare(DWORD LogicSectorNumber,BYTE* buffer) ***************/
/* 功能:生成并写入ECC码和逻辑块号信息 */
/* 入口参数:DWORD LogicSectorNumber----准备写入的逻辑扇区号 */
/* BYTE* buffer----即将生成ECC码的源数据缓冲区,512字节长 */
/* 返回值:NAND_PASS----成功;NAND_FAIL----表示逻辑号操作错误 */
/*注意:此函数仅被Process_PageErrorOrFull和WriteLgSector函数调用 */
/* 说明:非用户使用函数 */
/****************************************************************************************************/
NAND_Ret Process_Spare(DWORD LogicSectorNumber,BYTE* buffer)//OK
{
WORD LogicBlocknum;
BYTE pageoffsetnum;
WORD phblocknum;
DWORD phaddress ;
BYTE tempSectorSpare[16];
//BYTE *ptemp;
LogicBlocknum = SectorNumToClusterNum(LogicSectorNumber);
pageoffsetnum = LogicSectorNumber % SectorsPerCluster;
phblocknum = lgToph[LogicBlocknum];
phaddress = (phblocknum<<14) | (pageoffsetnum<<9);
//如果写入的扇区号是0,首先需要读该扇区的逻辑号信息,解决此类情况: 在调用AllocLgCluster()函数为某个文件寻找下一个
//空逻辑块时将同时找一个空物理块,并将该物理块写入对应的逻辑号信息,之后该块准备用于文件数据的写入。此时该块虽然全空,
//但实际已经分配给了某个逻辑块。这种情况下在进入本函数做写文件数据操作时入口参数逻辑号应该与读出的逻辑号相同(否则就
//是操作或分配错误),所以首先判断这两个逻辑号是否相同,在相同的情况下只更新C区的其他信息。否则出错返回。
if(pageoffsetnum == 0)
{
NAND_SpareRead(phaddress, &tempSectorSpare[6], 7);
/*首先生成C区字节信息
ptemp = ECC_Generate(buffer); //生成Buffer中前256字节的ECC码
tempSectorSpare[13] = *ptemp;
tempSectorSpare[14] = *(ptemp+1);
tempSectorSpare[15] = *(ptemp+2);
ptemp = ECC_Generate(buffer+256); //生成Buffer中后256字节的ECC码
tempSectorSpare[8] = *ptemp;
tempSectorSpare[9] = *(ptemp+1);
tempSectorSpare[10] = *(ptemp+2);
*/
//首先需要判断该块在写0扇区时是否属于上述情况,因为也存在写0扇区时该块还没有被分配逻辑号的情况,此时不存在上述问题
if((tempSectorSpare[6]<<8 | tempSectorSpare[7]) != 0xffff) //该块已分配逻辑号,确实为被占用的空块
{
if(LogicBlocknum != (tempSectorSpare[6]<<8 | tempSectorSpare[7]))
{ return NAND_FAIL; }
else
{
//NAND_SpareProgram(phaddress+8, &tempSectorSpare[8], 3); //在FLASH中将该块后256字节的ECC码写入
//NAND_SpareProgram(phaddress+13, &tempSectorSpare[13], 3);//在FLASH中将该块前256字节的ECC码写入
}
}
else //否则该块还未分配逻辑号
{
tempSectorSpare[6] = LogicBlocknum& 0xff; //写入该块的逻辑号
tempSectorSpare[7] = LogicBlocknum>>8;
tempSectorSpare[11] = tempSectorSpare[6];
tempSectorSpare[12] = tempSectorSpare[7];
NAND_SpareProgram(phaddress+6, &tempSectorSpare[6], 7); //在FLASH中将该块冗余信息写入
}
}
else //如果写入的不是0扇区则不存在上述问题
{
//首先生成C区字节信息
/*ptemp = ECC_Generate(buffer); //生成Buffer中前256字节的ECC码
tempSectorSpare[13] = *ptemp;
tempSectorSpare[14] = *(ptemp+1);
tempSectorSpare[15] = *(ptemp+2);
ptemp = ECC_Generate(buffer+256); //生成Buffer中后256字节的ECC码
tempSectorSpare[8] = *ptemp;
tempSectorSpare[9] = *(ptemp+1);
tempSectorSpare[10] = *(ptemp+2);*/
tempSectorSpare[6] = LogicBlocknum & 0xff; //写入该块的逻辑号
tempSectorSpare[7] = LogicBlocknum>>8;
tempSectorSpare[11] = tempSectorSpare[6];
tempSectorSpare[12] = tempSectorSpare[7];
NAND_SpareProgram(phaddress+6, &tempSectorSpare[6], 7); //在FLASH中将该块冗余信息写入
}
return NAND_PASS;
}
/************************* 函数 WORD Find_BlankPhBlock(WORD startPhblock) ***************************/
/* 功能:从输入的物理块号开始,寻找一个空块,找到之后返回新的物理块号 */
/* 寻找策略:先从输入的块号开始向后找,如果直到最后没有空镞,再从表头开始寻找 */
/* 入口参数:开始搜寻的起始块号 */
/* 返回值:找到的空物理块号. 0xffff表示找不到空块,盘已满 */
/* 说明:非用户使用函数 */
/****************************************************************************************************/
WORD Find_BlankPhBlock(WORD startPhblock)//OK
{
WORD newblocknum,i;
BYTE tempSectorSpare[16];
BYTE full = 0xff;
if(startPhblock < (FAT_SIZEOFWORD-1)) //如果startPhblock==4095,则直接从前找
{
for(i=startPhblock; i<FAT_SIZEOFWORD; i++) //先向后找
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -