以下內(nèi)容來源于《51單片機輕松入門-基于STC15W4K系列》,一般不用修改即可正常使用。
7.2.2DataFlash操作實例(斷電瞬間存儲數(shù)據(jù)) 例7.3 STC15F2K60S2單片機內(nèi)部DataFlash讀寫測試 本程序上電時先擦除DataFlash的第1個扇區(qū),然后將前半扇區(qū)與后半扇區(qū)分別寫入數(shù)據(jù)0~255,然后讀出數(shù)據(jù)并判斷與寫入的數(shù)據(jù)是否一致,并通過串口助手顯示程序運行過程中的數(shù)據(jù)與最終結(jié)果是否正常,R/C時鐘頻率22.1184MHz,串口通信波特率9600。 程序主要使用到2個模塊文件FLASH.H與FLASH.C,在程序移植過程中,F(xiàn)LASH.H里需要定義R/C時鐘頻率和FLASH存儲單元地址,F(xiàn)LASH.C無需作任何更改。 ////////////////////////////////// FLASH.H ///////////////////////////////// #ifndef __FLASH_H__ #define __FLASH_H__ #include "STC15W4K.H" // FLASH操作要控制中斷開關(guān)EA #include <intrins.h> // FLASH讀寫要用到 _nop_(); // FLASH讀寫擦除延時等待時間需要用到R/C時鐘頻率 #define SYSclk 22118400L //定義CPU實際運行的系統(tǒng)時鐘,可以修改 #define EEP_address 0x0000 // 主程序從0000地址開始讀寫數(shù)據(jù),可以修改 /******************** 寫N個字節(jié)函數(shù)最多255字節(jié)一次 *****************/ void EEPROM_write_n(unsignedint EE_address,unsigned char *DataAddress,unsigned char lenth); /******************** 讀N個字節(jié)函數(shù)最多255字節(jié)一次 *****************/ void EEPROM_read_n(unsignedint EE_address,unsigned char *DataAddress,unsigned char lenth); /******************** 扇區(qū)擦除函數(shù)*****************/ voidEEPROM_SectorErase(unsigned int EE_address); #endif ////////////////////////////////////// FLASH.C //////////////////////////////////////// // 此文件直接復(fù)制使用,用戶無需任何更改。 #include"FLASH.h" // 寄存器定義,雖然頭文件已有定義,但不會沖突,這里列出來方便理解程序。 sfr ISP_DATA = 0xC2; sfr ISP_ADDRH = 0xC3; sfr ISP_ADDRL = 0xC4; sfr ISP_CMD = 0xC5; sfr ISP_TRIG = 0xC6; sfr ISP_CONTR = 0xC7; /////////////////////////////////FLASH 操作延時等待參數(shù) //////////////////////////// #if (SYSclk >= 24000000L) #define ISP_WAIT_FREQUENCY 0 #elif (SYSclk >= 20000000L) #define ISP_WAIT_FREQUENCY 1 #elif (SYSclk >= 12000000L) #define ISP_WAIT_FREQUENCY 2 #elif (SYSclk >= 6000000L) #define ISP_WAIT_FREQUENCY 3 #elif (SYSclk >= 3000000L) #define ISP_WAIT_FREQUENCY 4 #elif (SYSclk >= 2000000L) #define ISP_WAIT_FREQUENCY 5 #elif (SYSclk >= 1000000L) #define ISP_WAIT_FREQUENCY 6 #else #define ISP_WAIT_FREQUENCY 7 #endif /*************************禁止操作FLASH ( 固定不變 )*******************************/ void DisableEEPROM(void) // 以下語句可以不用,只是出于安全考慮而已 { ISP_CONTR = 0; //禁止ISP/IAP操作 ISP_CMD = 0; // 去除ISP/IAP命令 ISP_TRIG = 0; // 防止ISP/IAP命令誤觸發(fā) ISP_ADDRH = 0xff; //指向非EEPROM區(qū),防止誤操作 ISP_ADDRL = 0xff; //指向非EEPROM區(qū),防止誤操作 } /******************** 寫N個字節(jié)函數(shù)最多255字節(jié)一次( 固定不變 ) *****************/ void EEPROM_write_n(unsignedint EE_address,unsigned char *DataAddress,unsigned char lenth) { EA = 0; // 禁止中斷 ISP_CONTR = 0x80 + ISP_WAIT_FREQUENCY; // 允許操作FLASH + 延時等待時間,送一次就夠 ISP_CMD = 2 ; // 字節(jié)寫命令,命令不需改變時,不需重新送命令 do { ISP_ADDRH = EE_address / 256; //送地址高字節(jié)(地址需要改變時才需重新送地址) ISP_ADDRL = EE_address % 256; //送地址低字節(jié) ISP_DATA =*DataAddress; // 送數(shù)據(jù)到ISP_DATA,只有數(shù)據(jù)改變時才需重新送 ISP_TRIG = 0x5A;// ISP觸發(fā)命令,先送5AH,再送A5H到ISP/IAP觸發(fā)寄存器,每次都需要如此 ISP_TRIG = 0xA5;// ISP觸發(fā)命令,寫字節(jié)最長需要55uS,因此本行語句會暫停55uS以上的時間 _nop_(); EE_address++; //下一個地址 DataAddress++; //下一個數(shù)據(jù) }while(--lenth); //直到結(jié)束 DisableEEPROM(); EA = 1; // 重新允許中斷 } /******************** 讀N個字節(jié)函數(shù)最多255字節(jié)一次 ( 固定不變 )*****************/ void EEPROM_read_n(unsignedint EE_address,unsigned char *DataAddress,unsigned char lenth) { EA = 0; // 禁止中斷 ISP_CONTR = 0x80 + ISP_WAIT_FREQUENCY; // 允許操作FLASH + 延時等待時間,送一次就夠 ISP_CMD = 1 ; // 字節(jié)讀命令,命令不需改變時,不需重新送命令 do { ISP_ADDRH = EE_address / 256; //送地址高字節(jié)(地址需要改變時才需重新送地址) ISP_ADDRL = EE_address % 256; //送地址低字節(jié) ISP_TRIG = 0x5A; //ISP觸發(fā)命令 ISP_TRIG = 0xA5; // ISP觸發(fā)命令,讀一個字節(jié)最長需要2個時鐘,因此本行語句會暫停2個時鐘以上的時間 _nop_(); *DataAddress = ISP_DATA; //讀出的數(shù)據(jù)送往外部變量地址 EE_address++; DataAddress++; }while(--lenth); DisableEEPROM(); EA = 1; // 重新允許中斷 } /******************** 扇區(qū)擦除函數(shù)( 固定不變 ) *****************/ voidEEPROM_SectorErase(unsigned int EE_address) { EA = 0; // 禁止中斷 // 只有扇區(qū)擦除,沒有字節(jié)擦除,512字節(jié)/扇區(qū)。扇區(qū)中任意一個字節(jié)地址都是扇區(qū)地址。 ISP_ADDRH = EE_address / 256; //送扇區(qū)地址高字節(jié)(地址需要改變時才需重新送地址) ISP_ADDRL = EE_address % 256; //送扇區(qū)地址低字節(jié) ISP_CONTR = 0x80 + ISP_WAIT_FREQUENCY; // 允許操作FLASH + 延時等待時間,送一次就夠 ISP_CMD = 3; // 送扇區(qū)擦除命令,命令不需改變時,不需重新送命令 ISP_TRIG = 0x5A; //ISP觸發(fā)命令 ISP_TRIG = 0xA5; // ISP觸發(fā)命令,擦除最長需要21mS,因此本行語句會暫停21mS以上的時間 _nop_(); DisableEEPROM(); //禁止命令 EA = 1; // 重新允許中斷 } /////////////////////////////////// 主程序:Flash_Test.C ///////////////////////////////// #include "FLASH.H" #include"uart_debug.h" void main() { unsigned char a; unsigned int i; UART_init(); // 占用定時器1,波特率:9600/22.1184MHZ UART_Send_Str("開始擦除\n"); EEPROM_SectorErase(EEP_address); // 扇區(qū)擦除 UART_Send_Str("擦除完畢\n"); for (i=0; i<512; i++) // 檢測是否擦除成功(全FF檢測) { EEPROM_read_n(EEP_address+i,&a,1); // 地址、數(shù)據(jù)、長度 UART_Send_StrNum("擦除值:",a) ; if (a!=0xff) goto Error; // 如果校驗錯誤,則退出 } UART_Send_Str("開始寫入\n"); for (i=0; i<512; i++) // 編程512字節(jié) { a=i; EEPROM_write_n(EEP_address+i,&a,1); // 地址、數(shù)據(jù)、長度 } UART_Send_Str("寫入完畢\n"); for (i=0; i<512; i++) // 校驗512字節(jié) { EEPROM_read_n(EEP_address+i,&a,1); // 地址、數(shù)據(jù)、長度 UART_Send_StrNum("數(shù)據(jù):",a); if (a!=i%256) gotoError; // 如果校驗錯誤,則退出 } UART_Send_Str("讀出結(jié)束,測試正常"); while (1); Error: UART_Send_Str("數(shù)據(jù)錯誤"); // IAP操作失敗 while (1); } 本程序使用“丁丁版本的串口調(diào)試助手”在電腦上顯示接收到的數(shù)據(jù),文本模式,9600波特率,測試結(jié)果正常,由于接收的數(shù)據(jù)量較大,其它串口助手可能會出現(xiàn)亂碼或開始接收到的數(shù)據(jù)被后來的數(shù)據(jù)覆蓋掉而不能完整顯示的問題。
|