jiameng18 发表于 2012-10-23 03:52:14

请教STC单片机内部EEPROM的两个问题

大家好,我想学下STC单片机内部的EEPROM使用,请教下各位高手这两个程序该怎么写呢?
1、有2位数码管显示,通过2个按键来设置数码管显示1-30的数值,比方设为20,保存到EEPROM里面,下次开机数码管显示20。
2、单片机程序运行中突然一个IO口检测到一个低电平,单片机程序在下次开机后不运行,呈现死机状态。只能重新烧录程序后才正常。
有朋友花时间能指导下的,包邮免费送一台7寸智能家居控制器玩玩,谢谢大家啦!
送的东西参考下面介绍链接,东西不贵,交个朋友咯:
http://www.amobbs.com/thread-5502045-1-1.html

Sullivan 发表于 2012-10-23 07:08:36

先给出代码。注意,凡是红色的部分都需要对照你的单片机型号仔细核对,因为不一样的型号这里不一样,确实很坑爹。
修改好之后用下面的读数据、写数据、扇区擦除三个函数就够了。前三个不用理会。注意写之前必须整个扇区擦除。
“1、有2位数码管显示,通过2个按键来设置数码管显示1-30的数值,比方设为20,保存到EEPROM里面,下次开机数码管显示20。”
在源代码基础上,每次显示完马上重写EEPROM把0”20“写进去。
修改main(),加上开机读取,直接显示。
“2、单片机程序运行中突然一个IO口检测到一个低电平,单片机程序在下次开机后不运行,呈现死机状态。只能重新烧录程序后才正常。”
这有几种方法实现,最简单的就是在eeprom中设置一个标志位,平常为0,检测到信号后写1并直接进入死循环。下次开机先检测这里,为1则继续死循环。
重新烧录时记得把eeprom区域一同清零即可。

不知道有没有帮助。
一大清早闲的慌,不是为了要赠品。
你真要给的话,给7楼的朋友吧。PS:我发帖时还不知道7楼是谁。
/*eeprom配置和设置*/
#define RdCommand 0x01
#define PrgCommand 0x02
#define EraseCommand 0x03
#define Error 1
#define Ok 0
#define WaitTime 0x01 //CPU delay time
sfr ISP_DATA=0xe2;
sfr ISP_ADDRH=0xe3;
sfr ISP_ADDRL=0xe4;
sfr ISP_CMD=0xe5;
sfr ISP_TRIG=0xe6;
sfr ISP_CONTR=0xe7;/*eeprom函数*/
void ISP_IAP_enable(void)
{
        EA=0;
        ISP_CONTR=ISP_CONTR&0x18;
        ISP_CONTR=ISP_CONTR|WaitTime;
        ISP_CONTR=ISP_CONTR|0x80;
}
void ISP_IAP_disable(void)
{
        ISP_CONTR=ISP_CONTR&0x7f;
        ISP_TRIG=0x00;
        EA=1;
}
void ISPgoon(void)
{
        ISP_IAP_enable();
        ISP_TRIG=0x46;
        ISP_TRIG=0xb9;        _nop_();
}
unsigned char EEP_byte_read(unsigned int byte_addr)//读数据
{
        ISP_ADDRH=(unsigned char)(byte_addr>>8);
        ISP_ADDRL=(unsigned char)(byte_addr&0x00ff);
        ISP_CMD=ISP_CMD&0xf8;
        ISP_CMD=ISP_CMD|RdCommand;
        ISPgoon();
        ISP_IAP_disable();
        return(ISP_DATA);
}
void EEP_SectorErase(unsigned int sector_addr)//整扇区擦除
{
        unsigned int iSectorAddr;
        iSectorAddr=(sector_addr&0xfe00);
        ISP_ADDRH=(unsigned char)(iSectorAddr>>8);
        ISP_ADDRL=0x00;
        ISP_CMD=ISP_CMD&0xf8;
        ISP_CMD=ISP_CMD|EraseCommand;
        ISPgoon();
        ISP_IAP_disable();
}
void EEP_byte_write(unsigned int byte_addr,unsigned char original_data)//写数据
{
        ISP_ADDRH=(unsigned char)(byte_addr>>8);
        ISP_ADDRL=(unsigned char)(byte_addr&0x00ff);
        ISP_CMD=ISP_CMD&0xf8;
        ISP_CMD=ISP_CMD|PrgCommand;
        ISP_DATA=original_data;
        ISPgoon();
        ISP_IAP_disable();
}

yklstudent 发表于 2012-10-23 07:56:11

我只是纯粹来收集程序的 好程序我都喜欢收集 说不定哪天就用到了

雨雪随行 发表于 2012-10-23 08:36:09

话说2楼挺给力 坐等七楼 lz有空也看看stc的资料 人家测试程序写的挺好你偏偏上论坛里来问。

话说,这个第二条不是随随便便用的 你存在eeprom中是吧,我来了先给你烧一段程序 把eeprom拷出来 如何搞的鬼我就知道了。这个单片机除了eeprom能改其他的不能改的。 出了问题肯定是他的问题。
想破解你的程序,很简单 搞到hex后,找到你判断的标志,改成永远有效。哇人家只改了一个字节,哈哈

yurinacn 发表于 2012-10-23 08:37:35

STC的EEPROM实际不是真正的EEPROM,是FLASH结构的。
清空只能按扇区擦除,擦除后该扇区内所有字节均为0xFF,然后再编程,每次可编程一个字节,一个字节地址在擦除后只能编程一次。
这样一个问题就是,如果一些数据放在一个扇区里,想改其中某个字节的话,需要先把数据拷贝出来,然后擦除,然后再全部写回去。

STC的东西官方有很多例程的。可参考datasheet或下载这个:
http://www.stcmcu.com/datasheet/stc/STC-ISP-V6.00/stc-isp-15xx-v6.20.exe
软件里面有些例程。

另第二个问题,我觉得先往EEPROM中写一段特定的数据,单片机每次开机都效验这个数据,然后在特定条件下,擦除这块EEPROM,下次启动时,单片机检测不到数据,就进入死循环。这样做比较好,可防止有人通过擦除EEPROM区进行破解。
不过STC的EEPROM都不大稳定的说,有时会丢数据。

pangzi0801 发表于 2012-10-23 08:41:50

我收集了代码,慢慢看慢慢理解。希望楼主的问题能早日得到解决。

newywx 发表于 2012-10-23 09:08:24

不会吧,难道我是7楼?

newywx 发表于 2012-10-23 09:24:45

不小心占了2楼兄弟提到的7楼,这阵子在倒腾STC12C5160S2,正好用到里面的eeprom保存参数,不知道楼主用的那个型号,各个型号不同里面所谓eeprom大小不一样,还有晶振不同里面部分参数也不同,就是说要根据具体电路来定,STC官方提供的资料里的相关例程还是挺详细的,参照2楼兄弟提供的再根据你自己的实际电路改改应该没多大问题,你这个关于eeprom部分就是2楼兄弟说的关注扇区擦除,写字节和读字节就可以了,另外还有就是数码管的显示,看你是动态还是静态,还有就是按键判断了!
顺便提醒一下,stc的所谓eeprom整个扇区擦除后是FF的^_^

lyg407 发表于 2012-10-23 10:50:45

楼主,现在STC 官方 都有了现成的 各部分,测试程序,你到官网就能找到。 很方便的。而且它现在的下载程序那个软件,也集成了很多示例。你看下图。。

dingdingri 发表于 2012-10-23 11:27:58

二楼厉害

BXAK 发表于 2012-10-23 12:03:42

使用STC单片机内部的EEPROM要注意:
1、看该型号手册的EEPROM说明(因为不同STC型号的EEPROM的触发命令、起始地址……有些许差异)
2、要高可靠:
    ①最好加入低压检测(如果确定你的电源很稳不会出现异常低压,可以不进行低压检测),低压时禁止EEPROM操作,
    ②操作区间最好关掉中断,
    ③写后校对 一次,不对则重写

javabean 发表于 2012-10-23 12:49:04

楼主把STC的具体型号和E2PROM相关的代码贴出来,相信很快就有答案了,光靠猜,很慢的

jiameng18 发表于 2012-10-23 13:20:33

Sullivan 发表于 2012-10-23 07:08
先给出代码。注意,凡是红色的部分都需要对照你的单片机型号仔细核对,因为不一样的型号这里不一样,确实很 ...

哈哈,谢谢您慷慨大方啊,型号是STC15F204EA,7寸不要那3.5寸寸屏用的着吗,发个地址过来给您邮一片

renwocai 发表于 2012-10-23 15:03:56

/*---------------------------------------------------------------------*/
/* --- STC MCU Limited ------------------------------------------------*/
/* --- STC15F4K60S4 系列 内部EEPROM举例--------------------------------*/
/* --- Mobile: (86)13922805190 ----------------------------------------*/
/* --- Fax: 86-755-82905966 -------------------------------------------*/
/* --- Tel: 86-755-82948412 -------------------------------------------*/
/* --- Web: www.STCMCU.com --------------------------------------------*/
/* 如果要在程序中使用此代码,请在程序中注明使使用了宏晶科技的资料及程序 */
/* 如果要在文章中应用此代码,请在文章中注明使使用了宏晶科技的资料及程序 */
/*---------------------------------------------------------------------*/

//本示例在Keil开发环境下请选择Intel的8058芯片型号进行编译
//假定测试芯片的工作频率为18.432MHz

#include "reg51.h"
#include "intrins.h"

typedef unsigned char BYTE;
typedef unsigned int WORD;

//-----------------------------------------------

sfr IAP_DATA    =   0xC2;         //IAP数据寄存器
sfr IAP_ADDRH   =   0xC3;         //IAP地址寄存器高字节
sfr IAP_ADDRL   =   0xC4;         //IAP地址寄存器低字节
sfr IAP_CMD   =   0xC5;         //IAP命令寄存器
sfr IAP_TRIG    =   0xC6;         //IAP命令触发寄存器
sfr IAP_CONTR   =   0xC7;         //IAP控制寄存器

#define CMD_IDLE    0               //空闲模式
#define CMD_READ    1               //IAP字节读命令
#define CMD_PROGRAM 2               //IAP字节编程命令
#define CMD_ERASE   3               //IAP扇区擦除命令

//#define ENABLE_IAP 0x80         //if SYSCLK<30MHz
//#define ENABLE_IAP 0x81         //if SYSCLK<24MHz
#define ENABLE_IAP0x82            //if SYSCLK<20MHz
//#define ENABLE_IAP 0x83         //if SYSCLK<12MHz
//#define ENABLE_IAP 0x84         //if SYSCLK<6MHz
//#define ENABLE_IAP 0x85         //if SYSCLK<3MHz
//#define ENABLE_IAP 0x86         //if SYSCLK<2MHz
//#define ENABLE_IAP 0x87         //if SYSCLK<1MHz

//测试地址
#define IAP_ADDRESS 0x0400

void Delay(BYTE n);
void IapIdle();
BYTE IapReadByte(WORD addr);
void IapProgramByte(WORD addr, BYTE dat);
void IapEraseSector(WORD addr);

void main()
{
    WORD i;

    P1 = 0xfe;                      //1111,1110 系统OK
    Delay(10);                      //延时
    IapEraseSector(IAP_ADDRESS);    //扇区擦除
    for (i=0; i<512; i++)         //检测是否擦除成功(全FF检测)
    {
      if (IapReadByte(IAP_ADDRESS+i) != 0xff)
            goto Error;             //如果出错,则退出
    }
    P1 = 0xfc;                      //1111,1100 擦除成功
    Delay(10);                      //延时
    for (i=0; i<512; i++)         //编程512字节
    {
      IapProgramByte(IAP_ADDRESS+i, (BYTE)i);
    }
    P1 = 0xf8;                      //1111,1000 编程完成
    Delay(10);                      //延时
    for (i=0; i<512; i++)         //校验512字节
    {
      if (IapReadByte(IAP_ADDRESS+i) != (BYTE)i)
            goto Error;             //如果校验错误,则退出
    }
    P1 = 0xf0;                      //1111,0000 测试完成
    while (1);
Error:
    P1 &= 0x7f;                     //0xxx,xxxx IAP操作失败
    while (1);
}

/*----------------------------
软件延时
----------------------------*/
void Delay(BYTE n)
{
    WORD x;

    while (n--)
    {
      x = 0;
      while (++x);
    }
}

/*----------------------------
关闭IAP
----------------------------*/
void IapIdle()
{
    IAP_CONTR = 0;                  //关闭IAP功能
    IAP_CMD = 0;                  //清除命令寄存器
    IAP_TRIG = 0;                   //清除触发寄存器
    IAP_ADDRH = 0x80;               //将地址设置到非IAP区域
    IAP_ADDRL = 0;
}

/*----------------------------
从ISP/IAP/EEPROM区域读取一字节
----------------------------*/
BYTE IapReadByte(WORD addr)
{
    BYTE 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)
    _nop_();                        //等待ISP/IAP/EEPROM操作完成
    dat = IAP_DATA;               //读ISP/IAP/EEPROM数据
    IapIdle();                      //关闭IAP功能

    return dat;                     //返回
}

/*----------------------------
写一字节数据到ISP/IAP/EEPROM区域
----------------------------*/
void IapProgramByte(WORD addr, BYTE 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)
    _nop_();                        //等待ISP/IAP/EEPROM操作完成
    IapIdle();
}

/*----------------------------
扇区擦除
----------------------------*/
void IapEraseSector(WORD 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)
    _nop_();                        //等待ISP/IAP/EEPROM操作完成
    IapIdle();
}

fmdz 发表于 2012-10-23 03:52:15

#define        uchar unsigned char
#define        uint unsigned int

void IAP_ERASE(uchar addrh,addrl)//addrh是高地址,addrl是低地址,下同   
{
        IAP_ADDRH=addrh;
        IAP_ADDRL=addrl;
        IAP_CONTR=0X80;
        IAP_CMD=0X03;                                           //块擦除
        IAP_TRIG=0X5A;
        IAP_TRIG=0XA5;
}

uchar IAP_READ(uchar addrh,addrl)
{
        IAP_ADDRH=addrh;
        IAP_ADDRL=addrl;
        IAP_CONTR=0X80;
        IAP_CMD=0X01;                                           //读取
        IAP_TRIG=0X5A;
        IAP_TRIG=0XA5;
        return IAP_DATA;
}

void IAP_PROGRAM(uchar addrh,addrl,iap_data)//iap_data是需要写入的数据
{       
        IAP_ERASE(addrh,addrl);
        IAP_DATA=iap_data;
        IAP_ADDRH=addrh;
        IAP_ADDRL=addrl;
        IAP_CONTR=0X80;
        IAP_CMD=0X02;                                           //编程
        IAP_TRIG=0X5A;
        IAP_TRIG=0XA5;
}
以上是程序段

思路是:在程序入口的地方读一下指定地址的数据,如果是初次程序运行里面应该是0xff,可以判断一下是否是有效数据(如果是跳转程序段建议用switch case,或者用if),无效丢弃使用默认值,有效送显示。
在需要保存数据的地方先擦除再编程。下次启动入口或者需要的地方直接读取。

fmdz 发表于 2012-11-8 00:28:47

我目前新手,还没有发消息的权限。
STC15L204EA这个芯片存在外部中断BUG问题,在多次触发后没有响应。在操作EEPROM的时候一定要关闭其他中断。建议指定地址存取数据,不要采取标志位。

jiaohaitao 发表于 2012-11-8 00:49:49

靠,这就是个技术贴啊,果断收藏{:lol:}

jiameng18 发表于 2012-11-8 00:50:01

fmdz 发表于 2012-11-8 00:28 static/image/common/back.gif
我目前新手,还没有发消息的权限。
STC15L204EA这个芯片存在外部中断BUG问题,在多次触发后没有响应。在操 ...

恩,有完整的例程吗,对EEPROM一窍不懂呢

fmdz 发表于 2012-11-8 00:52:31

你要汇编的还是C的?

jiameng18 发表于 2012-11-8 02:40:56

fmdz 发表于 2012-11-8 00:52 static/image/common/back.gif
你要汇编的还是C的?

C的还能看懂点,汇编的真头痛,惭愧

fmdz 发表于 2012-11-8 10:33:48

程序在附件里,你看一下

Super_C 发表于 2012-11-13 20:49:23

MARK.!!
页: [1]
查看完整版本: 请教STC单片机内部EEPROM的两个问题