/********************************************************************<br />函数功能:获取下一个可用的备用块。<br />入口参数:无。<br />返 回:找到的块地址。<br />备 注:在该函数中会先擦除,只有成功擦除的才被返回。<br />********************************************************************/<br />uint32 FlashGetNewRemapBlock(void)<br />{<br /> uint32 i,Addr;<br /> for(i=0;i<FLASH_BAD_BLOCKS_REMAP;i++)<br /> {<br /> if(FLASH_BLOCK_OK==FlashRemapBlockStatus) //如果该块还未用<br /> {<br /> Addr=FLASH_BAD_BLOCK_REMAP_ADDR+i*FLASH_BLOCK_SIZE; //计算地址<br /> if(0x01==(FlashEraseBlock(Addr)&0x01)) //如果擦除失败<br /> {<br /> FlashRemapBlockStatus=FLASH_BLOCK_BAD; //标志该块为已经损坏<br /> }<br /> else //否则,擦除成功<br /> {<br /> FlashRemapBlockStatus=FLASH_BLOCK_USED; //标志为该块已被使用 <br /> return Addr; //返回地址<br /> }<br /> }<br /> }<br /> return -1; //如果找不到,则返回-1。<br />}<br />/////////////////////////End of function/////////////////////////////<br /><br />/********************************************************************<br />函数功能:标志当前重影射的块为坏块。<br />入口参数:Addr:要标志的块的地址。<br />返 回:无。<br />备 注:无。<br />********************************************************************/<br />void FlashMarkRemapBlockBad(uint32 Addr)<br />{<br /> uint32 i;<br /> i=(Addr-FLASH_BAD_BLOCK_REMAP_ADDR)/FLASH_BLOCK_SIZE; //计算偏移量<br /> if(i>=FLASH_BAD_BLOCKS_REMAP)return; //出错<br /> FlashRemapBlockStatus=FLASH_BLOCK_BAD; //标志为已经损坏<br />}<br />/////////////////////////End of function/////////////////////////////<br /><br />/********************************************************************<br />函数功能:地址重新影射,坏块管理用。<br />入口参数:Addr:需要影射的字节地址。<br />返 回:影射后的字节地址。<br />备 注:无。<br />********************************************************************/<br />uint32 FlashAddrRemap(uint32 Addr)<br />{<br /> static uint32 CurrentRemapBlockAddr;<br /> uint32 i,j;<br /> <br /> if(0==FlashBadBlocksCount) //如果坏块数量为0,则不需要处理,直接返回地址<br /> {<br /> return Addr;<br /> }<br /> <br /> //如果最后一次访问的地址和本次访问的地址属于同一个块地址,那么不需要重新影射<br /> if(0==((Addr-FlashLastAccessAddr)&(FLASH_BLOCK_SIZE-1)))<br /> {<br /> return CurrentRemapBlockAddr+(Addr&(FLASH_BLOCK_SIZE-1)); //由当前块地址加上块内偏移得到完整地址<br /> }<br /> <br /> FlashLastAccessAddr=Addr; //保存最后一次访问过的地址<br /> <br /> if(1==FlashBadBlocksCount) //如果坏块数量为1,则直接影射<br /> {<br /> if((Addr&(~(FLASH_BLOCK_SIZE-1)))==FlashBadBlockTable[0][0]) //如果块地址相等,则直接影射<br /> {<br /> CurrentRemapBlockAddr=FlashBadBlockTable[1][0];<br /> return CurrentRemapBlockAddr+(Addr&(FLASH_BLOCK_SIZE-1)); //由当前块地址加上块内偏移得到完整地址<br /> }<br /> else //不用影射<br /> {<br /> CurrentRemapBlockAddr=Addr&(~(FLASH_BLOCK_SIZE-1)); //获取当前块地址<br /> return Addr; //直接返回原来的地址<br /> }<br /> }<br /> else //坏块数量大于1<br /> {<br /> //如果地址比第一个坏块的地址还小或者比最后一个坏块的地址还大,<br /> //那么肯定不会是坏快,不需要重新影射<br /> if((Addr<FlashBadBlockTable[0][0])<br /> ||((Addr&(FLASH_BLOCK_SIZE-1))>FlashBadBlockTable[0][FlashBadBlocksCount-1]))<br /> {<br /> CurrentRemapBlockAddr=Addr&(~(FLASH_BLOCK_SIZE-1)); //获取当前块地址<br /> return Addr; //直接返回原来的地址<br /> }<br /> else //属于坏块区间,使用二分查表法决定是否需要影射<br /> {<br /> i=0;<br /> j=FlashBadBlocksCount-1;<br /> while(1)<br /> {<br /> if((Addr&(~(FLASH_BLOCK_SIZE-1)))==FlashBadBlockTable[0][(i+j)/2]) //如果相等,则影射<br /> {<br /> CurrentRemapBlockAddr=FlashBadBlockTable[1][(i+j)/2];<br /> return CurrentRemapBlockAddr+(Addr&(FLASH_BLOCK_SIZE-1)); //由当前块地址加上块内偏移得到完整地址<br /> }<br /> if(i==j)break; //如果i和j相等,则退出查找<br /> if((Addr&(~(FLASH_BLOCK_SIZE-1)))<FlashBadBlockTable[0][(i+j)/2]) //如果小于<br /> {<br /> j=(i+j)/2-1; //搜索前半段<br /> }<br /> else //如果大于<br /> {<br /> i=(i+j)/2+1; //搜索后半段<br /> }<br /> }<br /> }<br /> }<br /> //没有在坏块表中找到,则说明不是坏块<br /> CurrentRemapBlockAddr=Addr&(~(FLASH_BLOCK_SIZE-1)); //获取当前块地址<br /> return Addr; //直接返回原来的地址<br />}<br />/////////////////////////End of function/////////////////////////////<br /><br />/********************************************************************<br />函数功能:从FLASH的特定位置加载坏块表。<br />入口参数:无。<br />返 回:无。<br />备 注:无。<br />********************************************************************/<br />void FlashLoadBadBlockTable(void)<br />{<br /> uint32 i,j,k,Sum,Ok;<br /> uint8 Data;<br /><br /> Ok=0; //设置为不成功<br /> for(i=0;i<FLASH_BLOCKS_TABLE;i++) //查找没有准备擦除的块<br /> {<br /> //从该块中最后一页读回第一字节,看是否为0xFF,如果为0xFF,表示该块没有准备擦除<br /> FlashWriteCommand(0x00);<br /> FlashWriteAddr4Byte(FLASH_BLOCK_TABLE_ADDR + FLASH_BLOCK_SIZE*(i+1) - FLASH_PAGE_SIZE);<br /> FlashWriteCommand(0x30);<br /> FlashWait(); //等待数据读回<br /> FlashReadByte(Data);<br /> if(Data==0xFF) //表示该块数据还未准备擦除<br /> {<br /> //从该块中倒数第二页读回第一字节,看是否为0,如果为0,表示该块已经写入了数据<br /> FlashWriteCommand(0x00);<br /> FlashWriteAddr4Byte(FLASH_BLOCK_TABLE_ADDR + FLASH_BLOCK_SIZE*(i+1) - 2*FLASH_PAGE_SIZE);<br /> FlashWriteCommand(0x30);<br /> FlashWait(); //等待数据读回<br /> FlashReadByte(Data);<br /> if(Data==0) //表示数据有效<br /> {<br /> FlashReadByte(Data); //读出校验和<br /> Sum=Data;<br /> FlashReadByte(Data); //读出校验和<br /> Sum=(Sum<<8)+Data;<br /> FlashReadByte(Data); //读出校验和<br /> Sum=(Sum<<8)+Data;<br /> FlashReadByte(Data); //读出校验和<br /> Sum=(Sum<<8)+Data;<br /> //从该块开始位置读<br /> FlashWriteCommand(0x00);<br /> FlashWriteAddr4Byte(FLASH_BLOCK_TABLE_ADDR + FLASH_BLOCK_SIZE*i);<br /> FlashWriteCommand(0x30);<br /> FlashWait(); //等待数据读回<br /> //检查第1字节是否为0<br /> FlashReadByte(Data);<br /> if(Data!=0)continue;<br /> //检查第2字节是否为0x55<br /> FlashReadByte(Data);<br /> if(Data!=0x55)continue;<br /> //检查第3字节是否为0xAA<br /> FlashReadByte(Data);<br /> if(Data!=0xAA)continue;<br /> //检查第4字节是否为0xFF<br /> FlashReadByte(Data);<br /> if(Data!=0xFF)continue;<br /> Sum+=0x1FE;<br /> <br /> //读坏块数量<br /> FlashReadByte(Data);<br /> FlashBadBlocksCount=Data;<br /> Sum+=Data;<br /> FlashReadByte(Data);<br /> FlashBadBlocksCount=(FlashBadBlocksCount<<8)+Data;<br /> Sum+=Data;<br /> FlashReadByte(Data);<br /> FlashBadBlocksCount=(FlashBadBlocksCount<<8)+Data;<br /> Sum+=Data;<br /> FlashReadByte(Data);<br /> FlashBadBlocksCount=(FlashBadBlocksCount<<8)+Data;<br /> Sum+=Data;<br /> j=8;<br /> //读回坏块表<br /> for(k=0;k<sizeof(FlashBadBlockTable[0][0])*FLASH_BAD_BLOCKS_REMAP*2;k++)<br /> {<br /> if(0==(j&(FLASH_PAGE_SIZE-1))) //如果超过了页,则需要重新读新页<br /> {<br /> FlashWriteCommand(0x00);<br /> FlashWriteAddr4Byte(FLASH_BLOCK_TABLE_ADDR + FLASH_BLOCK_SIZE*i+j);<br /> FlashWriteCommand(0x30);<br /> FlashWait(); //等待数据读回<br /> }<br /> FlashReadByte(Data);<br /> Sum+=Data; //求校验和<br /> ((uint8 *)FlashBadBlockTable)[k]=Data; //读回一字节到坏块表中<br /> j++;<br /> }<br /> //读回重影射区的状态表<br /> for(k=0;k<sizeof(FlashRemapBlockStatus[0])*FLASH_BAD_BLOCKS_REMAP;k++)<br /> {<br /> if(0==(j&(FLASH_PAGE_SIZE-1))) //如果超过了页,则需要重新读新页<br /> {<br /> FlashWriteCommand(0x00);<br /> FlashWriteAddr4Byte(FLASH_BLOCK_TABLE_ADDR + FLASH_BLOCK_SIZE*i+j);<br /> FlashWriteCommand(0x30);<br /> FlashWait(); //等待数据读回<br /> }<br /> FlashReadByte(Data);<br /> Sum+=Data; //求校验和<br /> ((uint8 *)FlashRemapBlockStatus)[k]=Data; //读回一字节到重影射区状态表中<br /> j++;<br /> }<br /> if(Sum==0) //如果校验成功,则说明数据正确<br /> {<br /> Ok=0xFF; //设置为成功<br /> break; //并退出循环<br /> }<br /> }<br /> }<br /> }<br /> <br /> if(Ok==0) //如果在已写入的表中找不到好的坏块表,再去准备擦除的中去找<br /> {<br /> for(i=0;i<FLASH_BLOCKS_TABLE;i++) //查找准备擦除的块<br /> {<br /> //从该块中最后一页读回第一字节,看是否为0,如果为0,表示该块已经准备擦除了<br /> FlashWriteCommand(0x00);<br /> FlashWriteAddr4Byte(FLASH_BLOCK_TABLE_ADDR + FLASH_BLOCK_SIZE*(i+1) - FLASH_PAGE_SIZE);<br /> FlashWriteCommand(0x30);<br /> FlashWait(); //等待数据读回<br /> FlashReadByte(Data);<br /> if(Data==0x00) //表示该块数据准备擦除<br /> {<br /> //从该块中倒数第二页读回第一字节,看是否为0,如果为0,表示该块已经写入了数据<br /> FlashWriteCommand(0x00);<br /> FlashWriteAddr4Byte(FLASH_BLOCK_TABLE_ADDR + FLASH_BLOCK_SIZE*(i+1) - 2*FLASH_PAGE_SIZE);<br /> FlashWriteCommand(0x30);<br /> FlashWait(); //等待数据读回<br /> FlashReadByte(Data);<br /> if(Data==0) //表示数据有效<br /> {<br /> FlashReadByte(Data); //读出校验和<br /> Sum=Data;<br /> FlashReadByte(Data); //读出校验和<br /> Sum=(Sum<<8)+Data;<br /> FlashReadByte(Data); //读出校验和<br /> Sum=(Sum<<8)+Data;<br /> FlashReadByte(Data); //读出校验和<br /> Sum=(Sum<<8)+Data;<br /> //从该块开始位置读<br /> FlashWriteCommand(0x00);<br /> FlashWriteAddr4Byte(FLASH_BLOCK_TABLE_ADDR + FLASH_BLOCK_SIZE*i);<br /> FlashWriteCommand(0x30);<br /> FlashWait(); //等待数据读回<br /> //检查第1字节是否为0<br /> FlashReadByte(Data);<br /> if(Data!=0)continue;<br /> //检查第2字节是否为0x55<br /> FlashReadByte(Data);<br /> if(Data!=0x55)continue;<br /> //检查第3字节是否为0xAA<br /> FlashReadByte(Data);<br /> if(Data!=0xAA)continue;<br /> //检查第4字节是否为0xFF<br /> FlashReadByte(Data);<br /> if(Data!=0xFF)continue;<br /> Sum+=0x1FE;<br /> <br /> //读坏块数量<br /> FlashReadByte(Data);<br /> FlashBadBlocksCount=Data;<br /> Sum+=Data;<br /> FlashReadByte(Data);<br /> FlashBadBlocksCount=(FlashBadBlocksCount<<8)+Data;<br /> Sum+=Data;<br /> FlashReadByte(Data);<br /> FlashBadBlocksCount=(FlashBadBlocksCount<<8)+Data;<br /> Sum+=Data;<br /> FlashReadByte(Data);<br /> FlashBadBlocksCount=(FlashBadBlocksCount<<8)+Data;<br /> Sum+=Data;<br /> j=8;<br /> //读回坏块表<br /> for(k=0;k<sizeof(FlashBadBlockTable[0][0])*FLASH_BAD_BLOCKS_REMAP*2;k++)<br /> {<br /> if(0==(j&(FLASH_PAGE_SIZE-1))) //如果超过了页,则需要重新读新页<br /> {<br /> FlashWriteCommand(0x00);<br /> FlashWriteAddr4Byte(FLASH_BLOCK_TABLE_ADDR + FLASH_BLOCK_SIZE*i+j);<br /> FlashWriteCommand(0x30);<br /> FlashWait(); //等待数据读回<br /> }<br /> FlashReadByte(Data);<br /> Sum+=Data; //求校验和<br /> ((uint8 *)FlashBadBlockTable)[k]=Data; //读回一字节到坏块表中<br /> j++;<br /> }<br /> //读回重影射区的状态表<br /> for(k=0;k<sizeof(FlashRemapBlockStatus[0])*FLASH_BAD_BLOCKS_REMAP;k++)<br /> {<br /> if(0==(j&(FLASH_PAGE_SIZE-1))) //如果超过了页,则需要重新读新页<br /> {<br /> FlashWriteCommand(0x00);<br /> FlashWriteAddr4Byte(FLASH_BLOCK_TABLE_ADDR + FLASH_BLOCK_SIZE*i+j);<br /> FlashWriteCommand(0x30);<br /> FlashWait(); //等待数据读回<br /> }<br /> FlashReadByte(Data);<br /> Sum+=Data; //求校验和<br /> ((uint8 *)FlashRemapBlockStatus)[k]=Data; //读回一字节到重影射区状态表中<br /> j++;<br /> }<br /> if(Sum==0) //如果校验成功,则说明数据正确<br /> {<br /> FlashSaveBadBlockTable(); //将其保存到FLASH中 <br /> Ok=0xFF; //设置为成功<br /> break; //并退出循环<br /> }<br /> }<br /> }<br /> }<br /> }<br /> <br /> if(Ok==0) //如果还是没找到,那么只好重新初始化了<br /> {<br /> FlashBadBlocksCount=0; //坏块数设置为0<br /> for(i=0;i<FLASH_BAD_BLOCKS_REMAP;i++)<br /> {<br /> //所有影射块都设置为好块<br /> FlashRemapBlockStatus=FLASH_BLOCK_OK;<br /> //所有影射关系设置为-1<br /> FlashBadBlockTable[0]=-1;<br /> FlashBadBlockTable[1]=-1;<br /> }<br /> //设置好之后保存起来<br /> FlashSaveBadBlockTable();<br /> }<br /> //设置当前访问过的地址为无效值<br /> FlashLastAccessAddr=-1;<br />}<br />/////////////////////////End of function/////////////////////////////<br /><br />/********************************************************************<br />函数功能:保存坏块表到FLASH的特定位置。<br />入口参数:无。<br />返 回:无。<br />备 注:无。<br />********************************************************************/<br />void FlashSaveBadBlockTable(void)<br />{<br /> uint32 i,j,k,Sum;<br /> <br /> for(i=0;i<FLASH_BLOCKS_TABLE;i++) //标志为准备擦除<br /> {<br /> FlashWriteCommand(0x80);<br /> FlashWriteAddr4Byte(FLASH_BLOCK_TABLE_ADDR + FLASH_BLOCK_SIZE*(i+1) - FLASH_PAGE_SIZE);<br /> FlashSetPortAsOut(); //总线设置为输出口<br /> FlashWriteByte(0x00); //将第一字节设置为0,表示准备擦除<br /> //剩余字节写0xFF<br /> for(j=1;j<FLASH_PAGE_SIZE;j++)<br /> {<br /> FlashWriteByte(0xFF);<br /> }<br /> FlashWritePage(); //写页<br /> }<br /> <br /> for(i=0;i<FLASH_BLOCKS_TABLE;i++) //将坏块表写入这三块<br /> {<br /> FlashEraseBlock(FLASH_BLOCK_TABLE_ADDR + FLASH_BLOCK_SIZE*i); //擦除一块<br /> FlashWriteCommand(0x80);<br /> FlashWriteAddr4Byte(FLASH_BLOCK_TABLE_ADDR + FLASH_BLOCK_SIZE*i); //写入第一块的开始位置<br /> FlashSetPortAsOut(); //总线设置为输出口<br /> FlashWriteByte(0x00); //将第1字节设置为0<br /> FlashWriteByte(0x55); //将第2字节设置为0x55<br /> FlashWriteByte(0xAA); //将第3字节设置为0xAA<br /> FlashWriteByte(0xFF); //将第4字节设置为0xFF<br /> Sum=0x1FE;<br /> //接着写坏块数量,并统计校验和<br /> FlashWriteByte((FlashBadBlocksCount>>24)&0xFF);<br /> Sum+=(FlashBadBlocksCount>>24)&0xFF;<br /> FlashWriteByte((FlashBadBlocksCount>>16)&0xFF);<br /> Sum+=(FlashBadBlocksCount>>16)&0xFF;<br /> FlashWriteByte((FlashBadBlocksCount>>8)&0xFF);<br /> Sum+=(FlashBadBlocksCount>>8)&0xFF;<br /> FlashWriteByte((FlashBadBlocksCount)&0xFF);<br /> Sum+=(FlashBadBlocksCount)&0xFF;<br /> j=8; //写了8字节<br /> //保存坏块表<br /> for(k=0;k<sizeof(FlashBadBlockTable[0][0])*FLASH_BAD_BLOCKS_REMAP*2;k++)<br /> {<br /> if(0==(j&(FLASH_PAGE_SIZE-1))) //如果超过了页,则需要重新写新页<br /> {<br /> FlashWritePage(); //写页<br /> FlashWriteCommand(0x80);<br /> FlashWriteAddr4Byte(FLASH_BLOCK_TABLE_ADDR + FLASH_BLOCK_SIZE*i + j);<br /> FlashSetPortAsOut(); //总线设置为输出口<br /> }<br /> Sum+=((uint8 *)FlashBadBlockTable)[k]; //求校验和<br /> FlashWriteByte(((uint8 *)FlashBadBlockTable)[k]); //写一字节<br /> j++;<br /> }<br /> //保存重影射区的状态表<br /> for(k=0;k<sizeof(FlashRemapBlockStatus[0])*FLASH_BAD_BLOCKS_REMAP;k++)<br /> {<br /> if(0==(j&(FLASH_PAGE_SIZE-1))) //如果超过了页,则需要重新写新页<br /> {<br /> FlashWritePage(); //写页<br /> FlashWriteCommand(0x80);<br /> FlashWriteAddr4Byte(FLASH_BLOCK_TABLE_ADDR + FLASH_BLOCK_SIZE*i + j);<br /> FlashSetPortAsOut(); //总线设置为输出口<br /> }<br /> Sum+=((uint8 *)FlashRemapBlockStatus)[k]; //求校验和<br /> FlashWriteByte(((uint8 *)FlashRemapBlockStatus)[k]); //写一字节<br /> j++;<br /> }<br /> for(;0!=(j&(FLASH_PAGE_SIZE-1));j++) //将剩余部分写入0xFF<br /> {<br /> FlashWriteByte(0xFF);<br /> }<br /> FlashWritePage(); //写页<br /> <br /> //已完成写状态及校验和写入到该块的倒数第二页<br /> FlashWriteCommand(0x80);<br /> FlashWriteAddr4Byte(FLASH_BLOCK_TABLE_ADDR + FLASH_BLOCK_SIZE*(i+1) - 2*FLASH_PAGE_SIZE);<br /> FlashSetPortAsOut(); //总线设置为输出口<br /> FlashWriteByte(0x00); //将第一字节设置为0,表示已经写入<br /> //将校验和取反加1,这样累加结果就为0<br /> Sum=(~Sum)+1;<br /> //写校验和<br /> FlashWriteByte((Sum>>24)&0xFF);<br /> FlashWriteByte((Sum>>16)&0xFF);<br /> FlashWriteByte((Sum>>8)&0xFF);<br /> FlashWriteByte((Sum)&0xFF);<br /> //剩余字节写0xFF<br /> for(j=5;j<FLASH_PAGE_SIZE;j++)<br /> {<br /> FlashWriteByte(0xFF);<br /> }<br /> FlashWritePage(); //写页<br /> }<br />}<br />/////////////////////////End of function/////////////////////////////<br /><br />/********************************************************************<br />函数功能:更新坏块表。<br />入口参数:OldAddr:旧地址;NewAddr:影射之后的地址。<br />返 回:无。<br />备 注:无。<br />********************************************************************/<br />void FlashUpdateBadBlockTable(uint32 OldAddr,uint32 NewAddr)<br />{<br /> uint32 i,j;<br /> OldAddr&=~(FLASH_BLOCK_SIZE-1); //求得一块内起始地址<br /> NewAddr&=~(FLASH_BLOCK_SIZE-1);<br /> if(OldAddr>FLASH_MAX_SECTOR_ADDR) //如果比能够达到的最大地址还大,说明坏块地址本来就是被重新影射过的<br /> {<br /> //先要找到它原来影射的位置<br /> for(i=0;i<FlashBadBlocksCount;i++)<br /> {<br /> if(OldAddr==FlashBadBlockTable[1]) //如果与某个地址吻合,则说明就是该地址了<br /> {<br /> FlashBadBlockTable[1]=NewAddr; //重新影射到新的地址<br /> //并将原来的交换块设置为已损坏<br /> FlashMarkRemapBlockBad(OldAddr);<br /> break;<br /> }<br /> }<br /> }<br /> else //说明坏块地址是没有被影射过的<br /> {<br /> //查找比它大的块地址,将它插入到前面,排好序,方便二分查表<br /> for(i=0;i<FlashBadBlocksCount;i++) <br /> {<br /> if(OldAddr<FlashBadBlockTable[0]) //找到比它大的地址<br /> {<br /> break;<br /> }<br /> }<br /> for(j=FlashBadBlocksCount;j>i;j--) //将上面的部分往上移动,腾出一个空位<br /> {<br /> FlashBadBlockTable[0][j]=FlashBadBlockTable[0][j-1];<br /> FlashBadBlockTable[1][j]=FlashBadBlockTable[1][j-1];<br /> }<br /> //将当前块的影射写入<br /> FlashBadBlockTable[0][j]=OldAddr;<br /> FlashBadBlockTable[1][j]=NewAddr;<br /> FlashBadBlocksCount++; //增加一个坏块计数<br /> }<br /> FlashSaveBadBlockTable(); //存储坏块表<br /> //修改当前访问过的地址为无效地址,这样下次操作时就会重新影射<br /> FlashLastAccessAddr=-1;<br />}<br />/////////////////////////End of function/////////////////////////////<br /><br />/********************************************************************<br />函数功能:FLASH坏块处理。<br />入口参数:Addr: 字节地址。<br />返 回:无。<br />备 注:处理后的地址。<br />********************************************************************/<br />uint32 FlashDealBadBlock(uint32 Addr, uint32 Type)<br />{<br /> uint32 i;<br /> uint32 RemapBlockAddr;<br /> uint32 SwapBlockAddr;<br /> while(1)<br /> {<br /> RemapBlockAddr=FlashGetNewRemapBlock();<br /> if(RemapBlockAddr==-1) //如果已经找不到新的可用的替换品,那只好直接返回了。<br /> {<br /> return Addr;<br /> }<br /> switch(Type)<br /> {<br /> //擦除时遇到的坏块,不需要将当前页缓冲区数据写回<br /> //只需要返回新的地址即可。地址统一在最后返回,这里不用处理<br /> case 1:<br /> goto Exit;<br /> break;<br /> <br /> //复制页时遇到的坏块,需要将该块中前面的页及当前页从交换区中重新复制<br /> case 2:<br /> //从交换区去复制前面页以及当前页的数据<br /> SwapBlockAddr=FlashGetCurrentSwapBlock(); //获取当前所使用的交换块<br /> //复制前面以及当前页<br /> for(i=0;i<(Addr&(FLASH_BLOCK_SIZE-1))/FLASH_PAGE_SIZE+1;i++)<br /> {<br /> if(0x01==(FlashCopyPage(SwapBlockAddr+i*FLASH_PAGE_SIZE,RemapBlockAddr+i*FLASH_PAGE_SIZE)&0x01))<br /> {<br /> //如果复制失败,则说明该块有问题,需要找新的块<br /> goto BadRemapBlock;<br /> }<br /> }<br /> //复制完毕,则退出循环<br /> goto Exit;<br /> break;<br /> <br /> //写数据时遇到的坏块,需要将该块中前面的页从交换区中重新复制,<br /> //还需要将当前页从交换区中复制并将缓冲区的输入写入到当前页中<br /> //这里无法再获取到缓冲区的数据了,只好直接从原来的页复制数据<br /> case 3: <br /> //从交换区去复制前面页数据<br /> SwapBlockAddr=FlashGetCurrentSwapBlock(); //获取当前所使用的交换块<br /> //复制前面的页<br /> for(i=0;i<(Addr&(FLASH_BLOCK_SIZE-1))/FLASH_PAGE_SIZE;i++)<br /> {<br /> if(0x01==(FlashCopyPage(SwapBlockAddr+i*FLASH_PAGE_SIZE,RemapBlockAddr+i*FLASH_PAGE_SIZE)&0x01))<br /> {<br /> //如果复制失败,则说明该块有问题,需要找新的块<br /> goto BadRemapBlock;<br /> }<br /> }<br /> //对于当前页,只好从刚刚写入的错误地址去复制<br /> if(0x01==(FlashCopyPage(Addr,RemapBlockAddr+i*FLASH_PAGE_SIZE)&0x01))<br /> {<br /> //如果复制失败,则说明该块有问题,需要找新的块<br /> goto BadRemapBlock;<br /> } <br /> //复制完毕,则退出循环<br /> goto Exit;<br /> break;<br /> <br /> default:<br /> break;<br /> }<br /> BadRemapBlock:<br /> //如果操作过程中失败,则要标志该块已经损坏<br /> FlashMarkRemapBlockBad(RemapBlockAddr);<br /> }<br /> Exit:<br /> //更新坏块表<br /> FlashUpdateBadBlockTable(Addr,RemapBlockAddr);<br /> return RemapBlockAddr+(Addr&(FLASH_BLOCK_SIZE-1));<br />}<br />/////////////////////////End of function/////////////////////////////<br /><br />/********************************************************************<br />函数功能:管理可用的交换块地址。<br />入口参数:Op:对应的操作。<br />返 回:下一个可用的交换块的地址。<br />备 注:无。<br />********************************************************************/<br />uint32 FlashManageSwapBlock(uint32 Op)<br />{<br /> static uint32 Current;<br /> static uint8 FlashSwapBlockStatus[FLASH_SWAP_BLOCKS];<br /> uint32 i;<br /> <br /> switch(Op)<br /> {<br /> case 0: //如果操作为1,表示初始化<br /> Current=0;<br /> for(i=0;i<FLASH_SWAP_BLOCKS;i++)<br /> {<br /> FlashSwapBlockStatus=0; //初始化所有交换块为好的<br /> }<br /> break;<br /> <br /> case 1: //如果操作为1,表示获取下一个可用的交换区<br /> while(1)//一直尝试,如果交换区都用完(坏)了,那么就死循环了,<br /> {<br /> Current++;<br /> if(Current>=FLASH_SWAP_BLOCKS)<br /> {<br /> Current=0;<br /> }<br /> if(FlashSwapBlockStatus[Current]==0)break; //如果该块标志为0,则说明未损坏<br /> }<br /> break;<br /> <br /> case 2: //如果操作为2,说明获取当前交换区地址<br /> break;<br /> <br /> case 3: //如果操作为3,设置当前交换块为坏块<br /> FlashSwapBlockStatus[Current]=FLASH_BLOCK_BAD;<br /> break;<br /> <br /> default:<br /> break;<br /> }<br /> return FLASH_SWAP_BLOCK_ADDR+Current*FLASH_BLOCK_SIZE; //返回可用的交换块地址<br />}<br />/////////////////////////End of function/////////////////////////////<br /><br />/********************************************************************<br />函数功能:将一块数据复制到交换区。同时将原来的块删除,<br /> 并将该块内Addr所在页前面的页面复制回原来的块。<br />入口参数:Addr:要复制出来的块地址。<br />返 回:原来块的地址。<br />备 注:如果在复制回去的过程中,出现错误,<br /> 那么说明原来的块已经损坏,需要重新影射到一个好的块。<br /> 这时返回的地址就是重新影射过后的地址。<br />********************************************************************/<br />uint32 FlashCopyBlockToSwap(uint32 Addr)<br />{<br /> uint32 SwapAddr;<br /> uint32 i;<br /> uint32 BlockStartAddr;<br /> <br /> BlockStartAddr=(Addr)&(~(FLASH_BLOCK_SIZE-1)); //计算块起始地址<br /> <br /> while(1)<br /> {<br /> SwapAddr=FlashGetNextSwapBlock(); //获取下一个交换区<br /> if(0x00==(FlashEraseBlock(SwapAddr)&0x01)) //如果擦除成功<br /> {<br /> for(i=0;i<FLASH_BLOCK_SIZE/FLASH_PAGE_SIZE;i++) //将对应块中所有页复制到交换区中<br /> {<br /> //复制一页<br /> if(0x01&FlashCopyPage(BlockStartAddr+i*FLASH_PAGE_SIZE,SwapAddr+i*FLASH_PAGE_SIZE))<br /> {<br /> //如果复制失败,则说明该交换块已经损坏,查找下一个可用的交换块<br /> goto BadSwapBlock;<br /> }<br /> }<br /> //全部复制完毕,则擦除掉原来的块<br /> if(0x01==(FlashEraseBlock(BlockStartAddr)&0x01)) //如果擦除失败<br /> {<br /> Addr=FlashDealBadBlock(Addr,1); //处理擦除时遇到的坏块<br /> BlockStartAddr=(Addr)&(~(FLASH_BLOCK_SIZE-1)); //计算块起始地址<br /> }<br /> //将前面部分不会写到的页复制回去<br /> for(i=0;i<(Addr-BlockStartAddr)/FLASH_PAGE_SIZE;i++)<br /> {<br /> //复制一页<br /> if(0x01&FlashCopyPage(SwapAddr+i*FLASH_PAGE_SIZE,BlockStartAddr+i*FLASH_PAGE_SIZE))<br /> {<br /> //如果复制失败,则处理该坏块<br /> //注意FlashDealBadBlock返回的是当前正在操作的扇区地址,<br /> //需要取出其块地址加上Addr原来的扇区地址合成新的扇区地址<br /> Addr=(FlashDealBadBlock(BlockStartAddr+i*FLASH_PAGE_SIZE,2)&(~(FLASH_BLOCK_SIZE-1)))<br /> +(Addr&(FLASH_BLOCK_SIZE-1));<br /> BlockStartAddr=(Addr)&(~(FLASH_BLOCK_SIZE-1)); //计算块起始地址<br /> }<br /> }<br /> return Addr; //复制完毕,返回<br /> }<br /> else //否则,擦除失败<br /> {<br /> BadSwapBlock:<br /> //标志该块擦除时被损坏<br /> FlashMarkBadCurrentSwapBlock();<br /> }<br /> }<br /> return Addr;<br />}<br />/////////////////////////End of function/////////////////////////////<br /><br />/********************************************************************<br />函数功能:往FLASH中写一个扇区(FLASH_SECTOR_SIZE字节)。<br />入口参数:Addr: 字节地址;pBuf:保存数据的缓冲区;Remain:预计接下来还需要写多少扇区<br />返 回:写入的状态。0:成功。非0:失败。<br />备 注:当Remain不为0时,当前页以及该块内剩余部分将不会回写!<br /> 如果数据传输结束,应该将Remain置0,将数据写回。<br />********************************************************************/<br />uint32 FlashWriteOneSector(uint32 Addr, uint8 * pBuf, uint32 Remain)<br />{<br /> uint32 i;<br /> uint32 SwapPageAddr;<br /> <br /> FlashClrCe(); //选中芯片<br /> if(Addr>FLASH_MAX_SECTOR_ADDR)return 1; //如果地址超出范围,则返回失败代码1,越界<br /> Addr=FlashAddrRemap(Addr); //重新影射地址<br /> if((Addr&(~(FLASH_PAGE_SIZE-1)))!=(FlashCurrentWriteSectorAddr&(~(FLASH_PAGE_SIZE-1)))) //如果跨page<br /> {<br /> if(FlashNeedWriteBack) //如果前面写了数据,则需要将当前读出的page写回<br /> {<br /> if(FlashWritePage()&0x01) //写入失败<br /> {<br /> Addr=FlashDealBadBlock(Addr-FLASH_PAGE_SIZE,3)+FLASH_PAGE_SIZE; //坏块处理<br /> }<br /> }<br /> if((Addr&(~(FLASH_BLOCK_SIZE-1)))!=(FlashCurrentWriteSectorAddr&(~(FLASH_BLOCK_SIZE-1)))) //如果跨block,则需要擦除新的块,<br /> {<br /> //在擦除之前,要先将原来的块复制到交换区,并且将该块前面部分数据写回<br /> //该函数除了将整块数据复制到交换区以外,并且还将擦除掉原来的块,然后将前面部分复制回原来的块<br /> Addr=FlashCopyBlockToSwap(Addr);<br /> }<br /> //从交换区中读出对应的一页<br /> FlashWriteCommand(0x00);<br /> FlashWriteAddr4Byte(FlashGetCurrentSwapBlock()+(Addr&(FLASH_BLOCK_SIZE-1)));<br /> FlashWriteCommand(0x35);<br /> FlashWait();<br /> //随机写<br /> FlashWriteCommand(0x85);<br /> FlashWriteAddr4Byte(Addr); //写4字节地址<br /> FlashSetPortAsOut(); //总线设置为输出口<br /> for(i=0;i<FLASH_SECTOR_SIZE;i++)<br /> {<br /> FlashWriteByte(pBuf);<br /> }<br /> FlashSetPortAsIn(); //总线设置为输入口<br /> FlashNeedWriteBack=1; //需要写回<br /> }<br /> else //没有超过一页地址,则直接写数据<br /> {<br /> //随机写<br /> FlashWriteCommand(0x85);<br /> FlashWriteAddr2Byte(Addr);<br /> FlashSetPortAsOut(); //总线设置为输出口<br /> for(i=0;i<FLASH_SECTOR_SIZE;i++)<br /> {<br /> FlashWriteByte(pBuf);<br /> }<br /> FlashSetPortAsIn(); //总线设置为输入口<br /> FlashNeedWriteBack=1; //需要写回<br /> }<br /> FlashCurrentWriteSectorAddr=Addr; //保存本次地址 <br /> if(Remain==0) //剩余扇区数为0,不会再写了,需要写回<br /> {<br /> if(FlashNeedWriteBack) //如果前面写了数据,则需要将当前读出的page写回<br /> {<br /> if(FlashWritePage()&0x01) //写入失败<br /> {<br /> Addr=FlashDealBadBlock(Addr,3); //坏块处理<br /> }<br /> }<br /> //计算剩余页数<br /> Remain=(((Addr+FLASH_BLOCK_SIZE)&(~(FLASH_BLOCK_SIZE-1)))-(Addr&(~(FLASH_PAGE_SIZE-1))))/FLASH_PAGE_SIZE-1;<br /> //计算在交换块中的起始页地址<br /> SwapPageAddr=FlashGetCurrentSwapBlock()+(Addr&(FLASH_BLOCK_SIZE-1));<br /> <br /> for(i=0;i<Remain;i++) //将该块内保存在交换块中剩余部分页的数据复制回该块<br /> {<br /> Addr+=FLASH_PAGE_SIZE; //从下一页开始写<br /> SwapPageAddr+=FLASH_PAGE_SIZE; <br /> if(0x01==(FlashCopyPage(SwapPageAddr,Addr)&0x01)) //如果复制失败<br /> {<br /> Addr=FlashDealBadBlock(Addr,2); //处理坏块<br /> }<br /> }<br /> FlashNeedWriteBack=0; //清除需要写回标志<br /> FlashCurrentWriteSectorAddr=-1;<br /> }<br /> FlashSetCe(); //释放FLASH芯片<br /> return 0;<br />}<br />/////////////////////////End of function/////////////////////////////<br /><br />/********************************************************************<br />函数功能:从FLASH中读出一扇区数据保存到缓冲区中。<br />入口参数:Addr: 字节地址;pBuf:保存数据的缓冲区;Remain:预计接下来还需要读多少扇区<br />返 回:读取的状态。0:成功。非0:失败。<br />备 注:当Remain不为0时,将保存当前地址以备后面的继续读当前页,当不为0时,<br /> 设置当前读地址为无效,从而下次读时必须重新使用读命令将数据从flash中读入到页缓存。<br />********************************************************************/<br />uint32 FlashReadOneSector(uint32 Addr, uint8 * pBuf, uint32 Remain)<br />{<br /> uint32 i;<br /> FlashClrCe(); //选中芯片<br /> if(Addr>FLASH_MAX_SECTOR_ADDR)return 1; //如果地址超出范围,则返回失败代码1,越界<br /> Addr=FlashAddrRemap(Addr); //重新影射地址<br /> if((Addr&(~(FLASH_PAGE_SIZE-1)))<br /> !=(FlashCurrentReadSectorAddr&(~(FLASH_PAGE_SIZE-1)))) //如果跨page<br /> {<br /> //如果跨页的,则写读数据命令<br /> FlashWriteCommand(0x00);<br /> FlashWriteAddr4Byte(Addr);<br /> FlashWriteCommand(0x30);<br /> FlashWait(); //等待数据读回<br /> }<br /> else<br /> {<br /> //如果没有跨页,则可以直接读<br /> FlashWriteCommand(0x05);<br /> FlashWriteAddr2Byte(Addr);<br /> FlashWriteCommand(0xE0);<br /> FlashWait(); //等待数据读回<br /> }<br /> for(i=0;i<FLASH_SECTOR_SIZE;i++)<br /> {<br /> FlashReadByte(pBuf); //读一字节数据<br /> }<br /> FlashCurrentReadSectorAddr=Addr; //保存当前操作的地址<br /> if(Remain==0) //如果不会接着读,那么就设置当前读过的地址为无效值<br /> {<br /> FlashCurrentReadSectorAddr=-1;<br /> }<br /> FlashSetCe(); //释放总线<br /> return 0;<br />}<br />/////////////////////////End of function/////////////////////////////
|