中穎單片機79Fxxx系列
SH79Fxxx Flash use as EEPROM 79Fxxx的flash可以当EEPROM用 原理:79Fxxx的flash rom是由数个sector组成,每个sector大小为2kB 如 79F161,有16K rom, 则有8个sector 可以划分一个sector作为flash eeprom,节省外围的eeprom器件,如24C02 从而节省成本。
79Fxxx Flash ROM的擦写寿命最少有1万次,若每次只需要保存8个字节, 可以把整个sector 2k空间划分为256页,每页8个字节, 当写满256页才进行擦除,这样理论上寿命可以达到128万次,大大提高可靠性能
由于每次写保存到flash rom的地址都不一样,例如采用sector 6 第一次保存数据是保存到 0x3000~0x3007,第二次写flash后数据则保存到0x3008~0x300f, 例子中通过二分法算法查询上次保存的那8个数据保存到第几页,以便读出最新写入的数据,或者继续写数据到后续空页处, 二分法算法大大提高索引效率。
程序提供2个函数,方便对flash读写操作 void WriteFlash(Byte *ptr) //ptr is the head pointer of the write buffer void ReadFlash(Byte *ptr) //ptr is the head pointer of the read buffer
注意的地方: Flash ROM的第一个sector和最后一个sector不能用作flash eeprom 因为第一个sector(即程序code区0x0000~0x0fff)包含中断向量入口, 最后一个sector的最后64个Bytes被硬件屏蔽, eg:对于79F161,Sector 1,2,3,4,5,6可以用作flash eeprom,而Sector0和Sector7则不能用作flash eeprom
振荡器频率为8M,写周期为30us(写入一个Byte),擦周期为60ms(擦除整个Sector) 也可以根据需要使用其它振荡器频率,擦Flash和写Flash周期设置为符合规格书要求即可
具体说明及设置请查看源程序注释 程序解釋 SSP實現Flash當EEPROM驅動: 79Fxxx的flash rom是由数个sector组成,每个sector大小为2kB,79F161有16K rom, 共8个sector 程序中劃分sector6作为flash eeprom,节省外围的eeprom器件,如24C02,从而节省成本。 Flash ROM的第一个sector和最后一个sector不能用作flash eeprom用途 因为第一个sector包含中断向量入口,最后一个sector的最后64个Bytes被硬件屏蔽,对于79F161,Sector 1,2,3,4,5,6可以用作flash eeprom,而Sector0和Sector7则不能用作flash eeprom #define SECTOR1 0x0800 //0800~0fff #define SECTOR2 0x1000 //1000~17ff #define SECTOR3 0x1800 //1800~1fff #define SECTOR4 0x2000 //2000~27ff #define SECTOR5 0x2800 //2800~2fff #define SECTOR6 0x3000 //3000~37ff #define SECTOR SECTOR6 //劃分SECTOR6作為EEPROM 79Fxxx Flash ROM的擦写寿命有1萬次,程序中把整个sector 2k空间划分为256页,每页8个字节,当写满256页才进行擦除,这样理论上數據保存可以达到128万次,大大提高壽命及可靠性能 #define FLASH_PAGE_BYTES 8 // this value divide exactly by 2048 #define FLASH_PAGE_COUNT 2048/FLASH_PAGE_BYTES //allocate flash eeprom area at ROM sector 6 (C:0x3000~C:0x37ff) typedef struct { Byte ROM[FLASH_PAGE_BYTES]; }FLASH_MEMORY; //flash Page datastructure FLASH_MEMORY code Flash[FLASH_PAGE_COUNT] _at_ SECTOR; //flash eeprom datastructure 每次寫數據即寫一個Byte時間必須設定在30us左右,每次擦除即整個Sector清零時間必須設定在60ms左右 #define PROGRAM_CLK (65536 - 30) //*** 30us/(8*125ns) = 30us #define ERASE_CLK (65536 - 60000) //*** 60ms/(8*125ns) = 60000us 每次寫時寫到上次寫的那頁的后一頁,每次讀時返回最新寫的那一頁的數據,由于每次写保存到flash rom的地址都不一样,采用sector 6,第一次保存数据是保存到 0x3000~0x3007,第二次写flash后数据则保存到0x3008~0x300f,通过二分法算法求得上次保存的数据保存到第几页,以便读出最新写入的数据,或者继续写数据到后續的空页处,二分法算法大大提供索引效率。 判斷某一頁是否為NULL bit CheckFlashPageNull(Byte index) { Byte i; for(i=0;i<FLASH_PAGE_BYTES;i++) { if(Flash[index].ROM) { return 1; } } return 0; }
二分法求得有效數據的頁索引號,并返回有效數據是否為NULL標記 bit GetFlashPageIndex(Byte *index) { Byte low = 0; Byte high = FLASH_PAGE_COUNT-1; Byte mid; bit flag; while(1) { mid = low+(high-low)/2; flag = CheckFlashPageNull(mid); if(mid == low) { break; } if(flag) { low = mid; } else { high = mid; } } if(CheckFlashPageNull(high)) { *index = high; return 1; } else { *index = low; return flag; } } SSP寫Flash函數 void ProgramFlash(Byte pageIndex,Byte *ptr) { Word FlashAddr=Flash; Byte i; Bit EA_BAK = EA; //--------------------------------------- for(i=0;i<pageIndex;i++) { FlashAddr +=FLASH_PAGE_BYTES; } EA = 0; //step 1 IB_CLK1 = (PROGRAM_CLK>>8)&0xff; //step 2 IB_CLK0 = (PROGRAM_CLK)&0xff; for(i=0;i<FLASH_PAGE_BYTES;i++) { XPAGE = HIBYTE(FlashAddr); //step 3 IB_OFFSET= LOBYTE(FlashAddr); IB_DATA = *(ptr+i); //step 4 IB_CON1 = 0x6E; //step 5 IB_CON2 = 0x05; IB_CON3 = 0x0A; IB_CON4 = 0x09; IB_CON5 = 0x06; _nop_(); //step 6 _nop_(); _nop_(); _nop_(); FlashAddr ++; //step 7 } //--------------------------------------- EA = EA_BAK; } SSP擦除Flash函數 void EraseFlash(void) { Word FlashAddr=Flash; Bit EA_BAK = EA; //--------------------------------------- EA = 0; //step 1 IB_CLK1 = (ERASE_CLK>>8)&0xff; IB_CLK0 = (ERASE_CLK)&0xff; //step 2 XPAGE = HIBYTE(FlashAddr); //step 3 IB_OFFSET= LOBYTE(FlashAddr); IB_CON1 = 0xE6; //step 4 IB_CON2 = 0x05; IB_CON3 = 0x0A; IB_CON4 = 0x09; IB_CON5 = 0x06; _nop_(); //step 5 _nop_(); _nop_(); _nop_(); //--------------------------------------- EA = EA_BAK; } 寫數據 第1次寫數據時,由于之前從未寫過數據,返回的索引號為0,且該頁為NULL,于是便把數據寫到第0頁。 第2次寫數據時,返回的索引號為0,該頁非NULL,于是便把數據保存到第1頁 …. 第256次寫數據時,返回的頁索引號為0xff,該頁非NULL,先擦除整個SECTOR,再把數據寫到第0頁 void Driver_WriteFlash(Byte *ptr) { bit flag; Byte index; flag = GetFlashPageIndex(&index); if(index == (FLASH_PAGE_COUNT-1)) { EraseFlash(); index =0; } else if(flag) { index ++; } ProgramFlash(index,ptr); } 讀數據 根據索引值讀出有效數據,有待后續校驗 void Driver_ReadFlash(Byte *ptr) { Byte i; Byte index; GetFlashPageIndex(&index); for(i=0;i<FLASH_PAGE_BYTES;i++) { *ptr = Flash[index].ROM; ptr ++; } } |