打印

SST89系列单片机内部非易失存储器的使用

[复制链接]
2453|2
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
icecoffelin|  楼主 | 2009-4-27 23:45 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
SST公司的SST89c554、SST89c564芯片的FLASH分为BLOCK0和BLOCK1两大块。其中BLOCK0为32K (564为64K),BLOCK0为8K(564为8K、58为4K)。芯片内部集成了可以对Flash进行操作的功能模块,通过对Flash的分区切放实现在应用中可编程的功能,达到保存程序临时数据目的,从而取代EEPROM。
IAP是指在用户的应用程序中对Flash块、Flash寄存器、加密位等实现擦降和编程等操作。SST89C54/58通过对SFCF.6的置位和清0而启动和介绍IAP功能。程序在block0时可以启动IAP对block1进行编程,也可以在block1对block0进行编程,但是每均都不能对自己进行编程。
SST89C54/58有256字节的内部数据存储器,其中低128字节RAM(00H~FFH)与8052完全相同;在高128字节中增加了一些针对IAP功能的特殊功能寄存器(SFR)。新增加的SFR的定义及设置如表1所列。

标识符 名   称 地   址 初始化值
SFST Flash状态 B6H xxx00000b
SFCF Flash定义 B1H 000000xxb
SFCM Flash命令 B2H 00h
SFDT Flash数据 B5H 00h
SFAL Flash地址低字节 B3H 00h
SFAH Flash地址高字节 B4H 00h

SFCF     IAP定义寄存器
SFCF^6   IAP 使能位
0   开启IAP功能
1   关闭IAP功能

SFCM     IAP命令寄存器
0000 0001    芯片擦除命令
0000 1011    扇区擦除命令
0000 1101    块擦除命令
0000 1100    字节读取命令
0000 1110    字节写命令



SFST     IAP状态寄存器
SFST^2   IAP状态标志位

0                                IAP空闲状态
1                                IAP执行状态

IAP功能的实现具体分为以下步骤
1.    开启IAP功能
2.    把地址传给 高字节传给SFAH,低字节传给SFAL
3.    把待执行的命令传给SFCM
4.    等到命令执行完后关闭IAP功能
按以上步骤严格执行,绝对OK!

难道IAP功能就这么容易,取掉EEPROM就这么简单?也许有人会发问。非也!非也!!以上只是提供了IAP功能的实现,而要具体应用中去就不那么简单了。值得注意的是我们的字节写命令 ,仅能把地址单元中的1改写成0,而不能把地址单元中的0改写成1(芯片出厂时默认全为FF)。按这样说芯片在出厂时是能够被改写的,但地址单元在使用过一次以后就不能再用?这个问题提得相当好。如果按常规地址单元在使用过一次以后就不能再用,但是我们可爱的554芯片提供了擦除这个命令,这样在你再次想改写地址单元的内容时只要先擦除就能改写数据了。不知各位有没有发现?在众多的IAP命令中,是没有提供 字节擦除 命令的,仅提供了扇区擦除命令。怎么办?这样岂不在改写一个数据的同时把其他数据给丢失了?这就需要我们在写数据的时候程序进行处理了。我们是这样对它进行处理的:在写数据之前,先把要改写的地址单元所在的扇区的数据128个字节全部读到一缓冲区中,然后把数据写入相对应的缓冲区,在擦除该扇区后最后把缓冲区的内容全部写入该扇区。听起来好象很复杂,但用起来还是能够达到要求的,而且还可以连续写一串数据,不知各位有没有其他更好的算法。


现在虽然能实现IAP功能,但有几个地方是值得注意的:

1. 一般FLASH的擦写寿命是有限的,554芯片是保证10万次不被擦坏。但我做过实验,我连续足足擦了200万次才把它擦坏(564擦166万就擦坏了),而且,在它擦坏一段时间后,让它休息两三天,它竟能自行恢复过来,虽然外部编程器已不能再擦除它,但芯片内部擦除指令是能够擦除的,也就是说,如果不是连续擦的话,擦写次数应该是能够超过我做实验的数据的。

2.如果在擦除或擦除后写数据的过程中临时掉电,那么整个扇区的数据都得丢失。我在程序中写数据较多的变量采用地址偏移法保存数据,因为在扇区擦除后整个扇区都能够进行字节写命令,我在写数据时只要把数据写在紧跟上次该变量的地址的空白单元,每写一次,自动跳到下一空白单元就可以了。这样就减少了擦除扇区的次数,不仅减少了掉电数据丢失的概率,而且延长了FLASH的使用寿命。另外,我还做过一个实验,精确地算出:用FLASH字节写一个数据仅需0.25ms,而写一个扇区要3ms,这样擦写全过程时间仅为以前的十分之一,进一步减少了因掉电而丢失数据的风险。但此法有一致命弱点,因为这是以牺牲空间为代价的,这样,一个扇区仅能存储一个变量的值。因此,只能用在少数的需要经常改写又非常重要的变量数据上。

3.因为擦除命令的操作对象是以扇区为单位的,所以在写一组数据时务必保证该组数据的存储地址在同一扇区,而且最好是连续的。



附录

IAP原代码

sfr16 DPTR = 0x82;

/*    新增特殊功能寄存器定义    */

sfr   SFCF      =     0xb1;      /*    SFCF      DATA      0B1H;     汇编程序       */

sfr   SFCM     =     0xb2;      /*    SFCM     DATA      0B2H;     汇编程序       */   

sfr   SFAL      =     0xb3;      /*    SFAL      DATA      0B3H;     汇编程序       */   

sfr   SFAH      =     0xb4;      /*    SFAH      DATA      0B4H;     汇编程序       */   

sfr   SFDT     =     0xb5;      /*    SFDT     DATA      0B5H;     汇编程序       */   

sfr   SFST      =     0xb6;      /*    SFST      DATA      0B6H;     汇编程序       */   



/* 调试控制项 */

//#define DEBUG_SST89E_V564RD

#define DEBUG_SST89E_V554RC


#ifdef DEBUG_SST89E_V564RD

       #define DEBUG_base_addr    0x10000

#endif

#ifdef DEBUG_SST89E_V554RC

       #define DEBUG_base_addr    0xe000

#endif


/* 定义命令 */

#define SFCM_Sector_Erase 0x0b       /* 扇区擦除 IAP command */

#define SFCM_Byte_Program       0x0e       /* 字节编程 IAP command */

#define SFCM_Byte_Verify        0x0c       /* 字节校验 IAP command */

typedef    unsigned char INT8U;          /* 8 bit 无符号整型 */

typedef    unsigned int    INT16U;        /* 16 bit 无符号整型 */



INT8U xdata protect_buffer[128];


/* 关闭 IAP 功能 */

void iap_disable(void)

{

       SFCF      =     SFCF & 0xbf; /* 1011,1111 */

       SFDT     =     0x00;

}



/* 扇区擦除 */

INT8U sector_erase(INT16U sector_addr)

{

       SFCF      =     SFCF | 0x40; /* 打开 IAP 功能 */

       DPTR     =     sector_addr;

       SFAH      =     DPH;

       SFAL      =     DPL;

       SFCM     =     SFCM_Sector_Erase;

       while(((ACC = SFST) & 0x04) != 0); // 0000,0100

       iap_disable();

       return OK;

}


/* 字节编程 */

INT8U byte_program(INT16U byte_addr, INT8U original_data)

{

       SFCF      =     SFCF | 0x40; /* 打开 IAP 功能 */

       DPTR     =     byte_addr;

       SFAH      =     DPH;

       SFAL      =     DPL;

       SFDT     =     original_data;

       SFCM     =     SFCM_Byte_Program;

       while(((ACC = SFST) & 0x04) != 0); // 0000,0100

       iap_disable();

       return OK;

}



/* 字节校验 */

INT8U byte_verify(INT16U byte_addr)

{

       INT8U    verify_data     =     0;

       SFCF      =     SFCF | 0x40;

       DPTR     =     byte_addr;

       SFAH      =     DPH;

       SFAL      =     DPL;

       SFCM     =     SFCM_Byte_Verify;

       _nop_();

       verify_data     =     SFDT;

       iap_disable();

       return (verify_data);

}



/* 写数据进 Flash存储器 而不破坏同一扇区中不需修改的数据 */

INT8U write_flash_with_protect(INT16U begin_addr, INT8U counter, INT8U array[])

{

       INT8U i = 0, safety_begin_addr = 0;

       INT8U    addr_high = 0x10, addr_low = 0x00;

       INT16U sector_addr = 0;

      

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

       if(counter > 128)   

              return ERROR;

       safety_begin_addr = (INT8U)(begin_addr & 0x007f); /* 0000,0000,0111,1111 */

     if( (safety_begin_addr + counter) > 128 )

              return ERROR;

      

       /* 将该扇区数据128字节数据读入缓冲区保护 */

       sector_addr = (begin_addr & 0xff80); /* 1111,1111,1000,0000; 取扇区地址 */

      

       SFCF      =     SFCF | 0x40; /* iap_enable */

       addr_high       = (INT8U)(sector_addr >> 8);            

       addr_low = (INT8U)(sector_addr & 0x00ff);

       SFAH      =     addr_high;

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

       {

              SFAL      =     addr_low;

              SFCM     =     SFCM_Byte_Verify;

              _nop_();

              protect_buffer    =     SFDT;

              addr_low++;

       }

//     iap_disable();


       /* 将要写入的数据写入缓冲区的相应区域,其余部分保护 */

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

       {

              protect_buffer[safety_begin_addr] = array;

              safety_begin_addr++;

       }

      

       /* 檫除 要修改/写入 的扇区 */

       sector_erase(sector_addr);

      

       /* 将缓冲区的数据写入 要修改/写入 的扇区 */

       SFCF      =     SFCF | 0x40;

       addr_high       = (INT8U)(sector_addr >> 8);

       addr_low = (INT8U)(sector_addr & 0x00ff);

       SFAH      =     addr_high;

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

       {

              SFAL      =     addr_low;

              SFDT     =     protect_buffer;

              SFCM     =     SFCM_Byte_Program;

              addr_low++;

              while(((ACC = SFST) & 0x04) != 0); // 0000,0100

       }

       iap_disable();

       return OK;

}

相关帖子

沙发
aihe| | 2009-4-29 20:50 | 只看该作者

不错给顶一下

使用特权

评论回复
板凳
htxic2009| | 2009-11-10 15:58 | 只看该作者
楼主辛苦了

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

9

主题

19

帖子

0

粉丝