出自:無量壽經大俠的《51單片機輕松入門—基于STC15W4K系列》:http://www.zg4o1577.cn/bbs/dpj-37954-1.html
7.2.2 DataFlash操作實例(斷電瞬間存儲數據)
例7.3 STC15F2K60S2單片機內部DataFlash讀寫測試
本程序上電時先擦除DataFlash的第1個扇區,然后將前半扇區與后半扇區分別寫入數據0~256,然后讀出數據并判斷與寫入的數據是否一致,并通過串口助手顯示程序運行過程中的數據與最終結果是否正常,R/C時鐘頻率22.1184MHz,串口通信波特率9600。
程序主要使用到2個模塊文件FLASH.H與FLASH.C,在程序移植過程中,FLASH.H里需要定義晶振頻率與FLASH存儲單元地址,FLASH.C無需作任何更改。
////////////////////////////////// FLASH.H /////////////////////////////////
#ifndef __FLASH_H__
#define __FLASH_H__
#include "STC15W4K.H " // FLASH操作要控制中斷開關EA
#include <intrins.h> // FLASH讀寫要用到 _nop_();
// FLASH讀寫擦除延時等待時間需要用到晶振頻率
#define SYSclk 22118400L // 定義CPU實際運行的系統時鐘
#define EEP_address 0x0000 // 主程序從0000地址開始讀寫數據
/******************** 寫N個字節函數 最多255字節一次 *****************/
void EEPROM_write_n(unsigned int EE_address,unsigned char *DataAddress,unsigned char lenth);
/******************** 讀N個字節函數 最多255字節一次 *****************/
void EEPROM_read_n(unsigned int EE_address,unsigned char *DataAddress,unsigned char lenth);
/******************** 扇區擦除函數 *****************/
void EEPROM_SectorErase(unsigned int EE_address);
#endif
【傳說】無量壽經(347305156) 16:51:18
////////////////////////////////////// FLASH.C ////////////////////////////////////////
// 此文件直接復制使用,用戶無需任何更改。
#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 操作延時等待參數 ////////////////////////////
#if (SYSclk >= 24000000L)
#define ISP_WAIT_FREQUENCY 0
#elif (SYSclk >= 20000000L)
#define ISP_WAIT_FREQUENCY 1
#elif (SYSclk >= 12000000L)
#define ISP_WAIT_FREQUENCY2
#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命令誤觸發
ISP_ADDRH = 0xff; // 指向非EEPROM區,防止誤操作
ISP_ADDRL = 0xff; // 指向非EEPROM區,防止誤操作
}
/******************** 寫N個字節函數 最多255字節一次( 固定不變 ) *****************/
void EEPROM_write_n(unsigned int EE_address,unsigned char *DataAddress,unsigned char lenth)
{
EA = 0; // 禁止中斷
ISP_CONTR = 0x80 + ISP_WAIT_FREQUENCY; // 允許操作FLASH + 延時等待時間,送一次就夠
ISP_CMD = 2 ; // 字節寫命令,命令不需改變時,不需重新送命令
do
{
ISP_ADDRH = EE_address / 256; // 送地址高字節(地址需要改變時才需重新送地址)
ISP_ADDRL = EE_address % 256; // 送地址低字節
ISP_DATA = *DataAddress; // 送數據到ISP_DATA,只有數據改變時才需重新送
ISP_TRIG = 0x5A;// ISP觸發命令,先送5AH,再送A5H到ISP/IAP觸發寄存器,每次都需要如此
ISP_TRIG = 0xA5;// ISP觸發命令,寫字節最長需要55uS,因此本行語句會暫停55uS以上的時間
_nop_();
EE_address++; // 下一個地址
DataAddress++; // 下一個數據
}while(--lenth); // 直到結束
DisableEEPROM();
EA = 1; // 重新允許中斷
}
【傳說】無量壽經(347305156) 16:51:34
/******************** 讀N個字節函數 最多255字節一次 ( 固定不變 )*****************/
void EEPROM_read_n(unsigned int EE_address,unsigned char *DataAddress,unsigned char lenth)
{
EA = 0; // 禁止中斷
ISP_CONTR = 0x80 + ISP_WAIT_FREQUENCY; // 允許操作FLASH + 延時等待時間,送一次就夠
ISP_CMD = 1 ; // 字節讀命令,命令不需改變時,不需重新送命令
do
{
ISP_ADDRH = EE_address / 256; // 送地址高字節(地址需要改變時才需重新送地址)
ISP_ADDRL = EE_address % 256; // 送地址低字節
ISP_TRIG = 0x5A; // ISP觸發命令
ISP_TRIG = 0xA5;
// ISP觸發命令,讀一個字節最長需要2個時鐘,因此本行語句會暫停2個時鐘以上的時間
_nop_();
*DataAddress = ISP_DATA; // 讀出的數據送往外部變量地址
EE_address++;
DataAddress++;
}while(--lenth);
DisableEEPROM();
EA = 1; // 重新允許中斷
}
/******************** 扇區擦除函數( 固定不變 ) *****************/
void EEPROM_SectorErase(unsigned int EE_address)
{
EA = 0; // 禁止中斷
// 只有扇區擦除,沒有字節擦除,512字節/扇區。扇區中任意一個字節地址都是扇區地址。
ISP_ADDRH = EE_address / 256; // 送扇區地址高字節(地址需要改變時才需重新送地址)
ISP_ADDRL = EE_address % 256; // 送扇區地址低字節
ISP_CONTR = 0x80 + ISP_WAIT_FREQUENCY; // 允許操作FLASH + 延時等待時間,送一次就夠
ISP_CMD = 3; // 送扇區擦除命令,命令不需改變時,不需重新送命令
ISP_TRIG = 0x5A; // ISP觸發命令
ISP_TRIG = 0xA5; // ISP觸發命令,擦除最長需要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); // 扇區擦除
UART_Send_Str("擦除完畢\n");
for (i=0; i<512; i++) // 檢測是否擦除成功(全FF檢測)
{
EEPROM_read_n(EEP_address+i,&a,1); // 地址、數據、長度
UART_Send_StrNum("擦除值:",a) ;
if (a!=0xff) goto Error; // 如果校驗錯誤,則退出
}
UART_Send_Str("開始寫入\n");
for (i=0; i<512; i++) // 編程512字節
{
a=i;
EEPROM_write_n(EEP_address+i,&a,1); // 地址、數據、長度
}
UART_Send_Str("寫入完畢\n");
for (i=0; i<512; i++) // 校驗512字節
{
EEPROM_read_n(EEP_address+i,&a,1); // 地址、數據、長度
UART_Send_StrNum("數據:",a);
if (a!=i%256) goto Error; // 如果校驗錯誤,則退出
}
UART_Send_Str("讀出結束,測試正常");
while (1);
Error:
UART_Send_Str("數據錯誤"); //0xxx,xxxx IAP操作失敗
while (1);
}
本程序使用“丁丁版本的串口調試助手”在電腦上顯示接收到的數據,文本模式,9600波特率,測試結果正常,由于接收的數據量較大,其它串口助手可能會出現亂碼或開始接收到的數據被后來的數據覆蓋掉而不能完整顯示的問題。
在程序移植過程中,FLASH.H里需要定義晶振頻率與FLASH存儲單元地址(地址可以不改),FLASH.C無需作任何更改。
復制粘貼使用就行了,想理解程序模塊內部每條語句意義需要看書才行,因為內部無需任何修改,也可以不管內部原理。
|