STC内部EEPROM的应用

来源:互联网 发布:如何免费开淘宝店 编辑:程序博客网 时间:2024/04/25 08:19
由于最近的一个项目里需要保存几组状态参数,本着硬件简洁的原则,开水打起了STC内部rom的主意,嘻嘻~~~
早就听说过STC内部有一大把空间的ROM可用(之前开水还以为90C516RD+是1*4K空间,后来才醒悟是16*4K的空间......),看到这么大的空间,令以节俭为美的开水反而不敢下手,怕是浪费了这么多空间。上网查资料和看郭天祥的新概念后,发现其驱动函数不长,不过坑爹的郭天祥只留下程序,一点解释也没有,弄得开水糊里糊涂的,幸好还有这两篇文章看下~
STC单片机内部EEPROM的应用 百度空间_应用平台
关于STC系列51单片机的EEPROM应用 - G-one's Blog



#ifndef __EEPROM_H__
#define __EEPROM_H__

#include <reg51.h>
#include <intrins.h>

sfr ISP_DATA = 0XE2;
sfr ISP_ADDH = 0XE3;
sfr ISP_ADDL = 0XE4;
sfr ISP_CMD = 0XE5;
sfr ISP_TRIG = 0XE6;
sfr ISP_CONTR= 0XE7;


#define unchar unsigned char
#define unint unsigned int


#define Read_ISP 0x01

#define Write_ISP 0x02

#define Erase_ISP 0x03

#define Error 1

#define Ok 0

#define WaitTime 0x01

#define PerSector 512


/*打开ISP,IAP 功能*/

extern void ISP_Enable(void)
{

EA=0;/* 关中断*/

//开水封印1

ISP_CONTR&=0x18;/*0001,1000*/

//开水封印2

ISP_CONTR|=WaitTime;/*写入硬件延时*/
//开水封印3

ISP_CONTR|=0x80;/*ISPEN=1*/

}

/*关闭ISP,IAP 功能*/

extern void ISP_Disable(void)

{

ISP_CONTR&=0x7f;/* ISPEN = 0 */

ISP_TRIG=0x00;

EA=1;/* 开中断*/

}

/*公用的触发代码*/

extern void ISP_Trigger(void)

{

ISP_Enable();/* 打开ISP,IAP 功能*/

ISP_TRIG=0x46;/* 触发ISP_IAP 命令字节1 */

ISP_TRIG=0xb9;/* 触发ISP_IAP 命令字节2 */

_nop_();

}

/*字节读*/

extern unchar Byte_Read(unint byte_addr)

{

ISP_ADDH=(unchar)(byte_addr>>8); /* 地址赋值*/

ISP_ADDL=(unchar)(byte_addr&0x00ff);

ISP_CMD&=0xf8; /* 清除低3 位*/

ISP_CMD|=Read_ISP;/* 写入读命令*/

ISP_Trigger();/* 触发执行*/

//开水封印4
ISP_Disable();/* 关闭ISP,IAP 功能*/

//开水封印5
return ISP_DATA;/* 返回读到的数据*/

}

/*扇区擦除*/

extern void Sector_Erase(unint sector_addr)

{

unint in_addr;

in_addr=(sector_addr&0xfe00);/* 取扇区地址*/

ISP_ADDH=(unchar)(in_addr>>8);

ISP_ADDL=0x00;

ISP_CMD&=0xf8;/* 清空低3 位*/

ISP_CMD|=Erase_ISP;/* 擦除命令3*/

ISP_Trigger();/* 触发执行*/

ISP_Disable();/* 关闭ISP,IAP 功能*/

}

/*字节写*/

extern void Byte_Write(unint byte_addr, unchar original_data)

{

ISP_ADDH=(unchar)(byte_addr>>8); /* 取地址*/

ISP_ADDL=(unchar)(byte_addr & 0x00ff);

ISP_CMD&=0xf8;/* 清低3 位*/

ISP_CMD|=Write_ISP;/* 写命令2*/

ISP_DATA=original_data;/* 写入数据准备*/

ISP_Trigger();/* 触发执行*/

ISP_Disable();/* 关闭IAP 功能*/

}

/*字节写并校验*/

extern unchar Byte_Write_Verify(unint byte_addr, unchar original_data)

{

ISP_ADDH=(unchar)(byte_addr>>8); /* 取地址*/

ISP_ADDL=(unchar)(byte_addr&0xff);

ISP_CMD&=0xf8;/* 清低3 位*/

ISP_CMD|=Write_ISP;/* 写命令2*/

ISP_DATA=original_data;

ISP_Trigger();/* 触发执行*/

/* 开始读,没有在此重复给地址,地址不会被自动改变*/

ISP_DATA=0x00;/* 清数据传递寄存器*/

ISP_CMD&=0xf8;/* 清低3 位*/

ISP_CMD|=Read_ISP;/* 读命令1*/

ISP_TRIG=0x46;/* 触发ISP_IAP 命令字节1 */

ISP_TRIG=0xb9;/* 触发ISP_IAP 命令字节2 */

_nop_();/* 延时*/

ISP_Disable();/* 关闭IAP 功能*/

if(ISP_DATA==original_data)/* 读写数据校验*/

return Ok;/* 返回校验结果*/

else

return Error;

}

/*数组写入*/

extern unchar Array_Write(unint begin_addr, unint len, unchar *array)

{

unint i;

unint in_addr;

/* 判是否是有效范围,此函数不允许跨扇区操作*/

if(len > PerSector)

return Error;

in_addr = begin_addr & 0x01ff;/* 扇区内偏移量*/

//开水封印6
if((in_addr+len)>PerSector)

return Error;

in_addr = begin_addr;

/* 逐个写入并校对*/

ISP_Enable();/* 打开IAP 功能*/

for(i=0;i<len;i++)

{

/* 写一个字节*/

ISP_ADDH=(unchar)(in_addr >> 8);

ISP_ADDL=(unchar)(in_addr & 0x00ff);

ISP_DATA=array[i]; /* 取数据*/

ISP_CMD&=0xf8;/* 清低3 位*/

ISP_CMD|=Write_ISP;/* 写命令2 */

ISP_TRIG=0x46;/* 触发ISP_IAP 命令字节1 */

ISP_TRIG=0xb9;/* 触发ISP_IAP 命令字节2 */

_nop_();

/* 读回来*/

ISP_DATA=0x00;

ISP_CMD&=0xf8;/* 清低3 位*/

ISP_CMD|=Read_ISP;/* 读命令1*/

ISP_TRIG=0x46;/* 触发ISP_IAP 命令字节1 */

ISP_TRIG=0xb9;/* 触发ISP_IAP 命令字节2 */

_nop_();
/* 比较对错*/

if(ISP_DATA!=array[i])

{

ISP_Disable();

return Error;

}
in_addr++;/* 指向下一个字节*/

}

ISP_Disable();

return Ok;

}

/*扇区读出*/

extern unchar Array_Read(unint begin_addr , unchar len ,unchar *array)

{

unint in_addr;

unint i;

if(len > PerSector)

return Error;

in_addr = begin_addr & 0x01ff;/* 扇区内偏移量*/

if((in_addr+len)>PerSector)

return Error;

in_addr = begin_addr; // & 0xfe00; /* 取扇区地址*/

ISP_Enable();

for(i=0;i<len;i++)

{

ISP_ADDH=(unchar)(in_addr>>8);

ISP_ADDL=(unchar)(in_addr & 0x00ff);

ISP_CMD&=0xf8;/* 清低3 位*/

ISP_CMD|=Read_ISP;/* 读命令1*/

ISP_DATA=0;

ISP_TRIG=0x46;/* 触发ISP_IAP 命令字节1 */

ISP_TRIG=0xb9;/* 触发ISP_IAP 命令字节2 */

_nop_();

array[i]=ISP_DATA;

in_addr++;
}

ISP_Disable();/* 关闭IAP 功能*/

return Ok;
}

#endif

上面这段代码主要是参考STC单片机内部EEPROM的应用 百度空间_应用平台    这篇文章的(其实基本就是搬过来~),如果只是贴代码,那这篇文章也太无趣了.........
这里主要介绍开水再次遇挫的经历.......囧    
在看到这篇文章的时候,我试过通读一遍代码,再结合STC官方的datasheet(89和90的)STC89C5XX  STC90C5XX (不得不吐糟其实stc89跟90基本都是一样的资料......),还是有些地方不懂,先封印好先,到时再找高手问问。
满心欢喜以为可以直接用上,结果测试的时候发现,用来测试的数组并没有显示在数码管上.........好大一盆冷水........T.T
一怒之下,决定给程序挖坑,看它栽在哪里了STC内部EEPROM的应用 - Z白开水H - 以技术自娱的Geeker

一、大坑
因为在测试的时候主函数用的是while(Array_Write),while(Array_Read), 当子函数返回OK时才会跳出循环,所以在每个while前后都设置一个数码管显示语句

Duan = duan[0];

while(Array_Write);

Duan = duan[1];

while(Array_Read);

Duan = duan[9];

由于习惯OK代表为1,Error代表为0,所以开始开水把作者的开头改了一下,这也就埋下了挫折的伏笔.......
测试结果出现1,说明在读数组里出问题

二、深坑
同理,在Array_Read函数中的每个return语句之前都放置显示语句,最终发现在程序一直在里面打转,出不来,原来是在最后没有返回OK,此时发现当利用while语句时,OK需等于0才可以脱离循环,所以又把OK和Error值改回去了。再次调试,又出问题了,坑爹........
这次是直接在写函数里出问题,写函数里有个查错语句,程序在那里跳出了,用显示语句查看DATA和array[i]的值,发现DATA数据居然是空的!! 数码管没有任何显示!!原来罪魁祸首在这里!哼哼!


   推论
这样看来,数据根本就没有写入到rom里,所以才不会有显示。现在有几种可能:
1. 单片机坏了(很小可能,暂时不考虑)
2.驱动函数有错(对比了网上几个版本,基本都是差不多的,所以应该没错)
3.rom地址不对(查看了datasheet,STC里没有516rd+的起始扇区,但是又宣传说有EEPROM,换块   89c52试了下,也是同样的问题,排除地址问题)
4.管脚定义错误
  由于之前在用IIC读写24c02的时候,犯过这种低级错误,导致信心大受打击,这次特意留了个心眼,看了下程序寄存器的定义,再看datasheet里提供的寄存器地址,开水嘴角扬起了45度,果然就是你!源程序里寄存器地址都是0XC开头的,但是在89c51和90c51里都是以0XE开头的!!!
改了定义之后,成功通过!!!!

真心好坑爹!!! 现在回想一下,在搬砖的时候,开水看到第二篇文章没有明显可以复制的定义,所以就搬了第一篇文章的寄存器定义,不想竟是这么小的地方出大问题,粗心粗心啊!!
总结下,粗心加偷懒是这次遇挫的主要原因,不过这次用上了之前看到的调试技巧——挖陷阱,在可能出错的入口和出口设置陷阱,从而锁定问题关键,小小安慰下自己.......
原创粉丝点击