出自:无量寿经大侠的《51单片机轻松入门—基于STC15W4K系列》:http://www.51hei.com/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无需作任何更改。
复制粘贴使用就行了,想理解程序模块内部每条语句意义需要看书才行,因为内部无需任何修改,也可以不管内部原理。
|