關於STC微控制器的內部EEPROM操作問題日常分享

超級網咖發表於2018-07-11

        

        最近再用stc15f2K60s2做專案,其中用到了stc內部的EEPROM操作(大小1K,兩個扇區,詳細如上圖),翻看其器件手冊借鑑其例程寫了一個測試程式。但是發現了一個問題,在第一次測試程式時好用,再在原地址重新寫別的資料時就不好用了。因為之前沒怎麼用過EEPROM,所以使我比較迷惑。

        當我改變其寫資料的地址,再寫資料時,發現好用,但是再次在這個地址寫其他資料竟然又不好使了。我就開始考慮是不是EEPROM每個地址資料只能寫入一次(那就太悲催了,寫錯一次就要換一塊微控制器),經過查閱資料發現並非如此(放下心來)。

        經過多次測試發現,每次想向已有資料的地址寫其他資料時需清除此地址的資料後才可再次寫入。然而stc15f2K60s2只有扇區資料清除操作,沒有地址資料清除指令,所以每次寫資料時就應將整個扇區的資料清除,加上stc15f2K60s2只有兩個扇區,這就產生了另一個令人頭疼的問題:當此扇區存有幾個或多個不同資料,而只想改變其中一個的資料時,就要把整個扇區的資料清除,相應的其他資料也就丟失,需重新寫入。

        為了解決此問題,許多大神就想出來了許多的解決辦法,最常見的有:

        1.掉電瞬間儲存資料(最優)

         在微控制器上電時,先讀取EEPROM內的資料,並儲存下來,再清除扇區資料,程式執行期間不向EEPROM寫資料,只改變儲存的。當微控制器掉電時,通過相應的硬體手段,保證微控制器再保持短暫的有電狀態,再此短暫的時間內微控制器將需要儲存的資料存入EEPROM內,再掉電。

        2.實時儲存(不建議用)

        在微控制器執行期間就不停的執行扇區擦除,重新寫入的操作

最後貼上我根據stc官方手冊整理的EEPROM驅動程式碼

EEPROM.c

#include "stc15f2k60s2.h"
#include "intrins.h"
#include "eeprom.h"
/*************************************此檔案只針對stc15f2k60s2編寫,其他型號不通用**********************************************************/
#define CMD_IDLE 0              //空閒模式
#define CMD_READ 1              //IAP位元組讀命令
#define CMD_PROGRAM 2           //IAP位元組程式設計命令
#define CMD_ERASE 3             //IAP扇區擦除命令
//#define ENABLE_IAP 0x80	//如果系統時鐘<30MHz選這個
//#define ENABLE_IAP 0x81	//如果系統時鐘<24MHz選這個
#define ENABLE_IAP 0x82	        //如果系統時鐘<20MHz選這個
//#define ENABLE_IAP 0x83	//如果系統時鐘<12MHz選這個
//#define ENABLE_IAP 0x84	//如果系統時鐘<6MHz選這個
//#define ENABLE_IAP 0x85	//如果系統時鐘<3MHz選這個
//#define ENABLE_IAP 0x86	//如果系統時鐘<2MHz選這個
//#define ENABLE_IAP 0x87	//如果系統時鐘<1MHz選這個
//關閉IAP函式(禁止對EEPROM操作)
void IapIdle(void)
{
IAP_CONTR = 0 ;         //關閉IAP功能
IAP_CMD = 0 ;	        //清除命令暫存器
IAP_TRIG = 0 ;	        //清除觸發暫存器
IAP_ADDRH = 0x80 ;	//將地址設定到非IAP地址區域
IAP_ADDRL = 0 ;
}
//從ISP/IAP/EEPROM區域讀取1位元組
unsigned char IapReadByte(unsigned int addr)
{
unsigned char dat;
IAP_CONTR = ENABLE_IAP;	//使能IAP
IAP_CMD = CMD_READ;	//設定IAP命令為“讀”
IAP_ADDRL = addr ;	//設定IAP低位地址
IAP_ADDRH = addr>>8 ;	//設定IAP高位地址
IAP_TRIG = 0x5a;	//寫觸發命令(0x5a)
IAP_TRIG = 0xa5;	//寫觸發命令(0xa5)[觸發命令必須先送0x5a 再送0xa5才能成功觸發]
_nop_();	        //等待ISP/IAP/EEPROM操作完成
dat = IAP_DATA;	        //讀取ISP/IAP/EEPROM資料
IapIdle();	        //關閉IAP功能
return dat;
}
//寫1位元組資料到從ISP/IAP/EEPROM區域
void IapProgramByte(unsigned int addr,unsigned char dat)
{
IAP_CONTR = ENABLE_IAP;	//使能IAP
IAP_CMD = CMD_PROGRAM;	//設定IAP命令為“寫”
IAP_ADDRL = addr ;	//設定IAP低位地址
IAP_ADDRH = addr>>8 ;	//設定IAP高位地址
IAP_DATA = dat;	        //寫ISP/IAP/EEPROM資料
IAP_TRIG = 0x5a;	//寫觸發命令(0x5a)
IAP_TRIG = 0xa5;	//寫觸發命令(0xa5)[觸發命令必須先送0x5a 再送0xa5才能成功觸發]
_nop_();	        //等待ISP/IAP/EEPROM操作完成
IapIdle();	        //關閉IAP功能
}
//扇區擦除(使用擦除扇區命令擦除整個扇區,stc15f2k60s2總共兩個扇區,0x0000-0x01FF和0x0200-0x03FF,故addr=0x0000 或 0x0200)
//注意!在扇區地址存有資料後不可再次向此地址寫資料,需全部清空所在扇區資料才可重新向此地址寫入資料
void IapEraseSector(unsigned int addr)
{
IAP_CONTR = ENABLE_IAP;	//使能IAP
IAP_CMD = CMD_ERASE;	//設定IAP命令為“擦除”
IAP_ADDRL = addr ;	//設定IAP低位地址
IAP_ADDRH = addr>>8 ;	//設定IAP高位地址
IAP_TRIG = 0x5a;	//寫觸發命令(0x5a)
IAP_TRIG = 0xa5;	//寫觸發命令(0xa5)[觸發命令必須先送0x5a 再送0xa5才能成功觸發]
_nop_();	        //等待ISP/IAP/EEPROM操作完成
IapIdle();	        //關閉IAP功能
}


EEPROM.h

#ifndef __EEPROM__
#define __EEPROM__
void IapIdle(void);	                                            //關閉IAP函式(禁止對EEPROM操作)
unsigned char IapReadByte(unsigned int addr);	                    //從ISP/IAP/EEPROM區域讀取1位元組
void IapProgramByte(unsigned int addr,unsigned char dat);           //寫1位元組資料到從ISP/IAP/EEPROM區域
void IapEraseSector(unsigned int addr);	                            //扇區擦除
#endif


相關文章