当前位置:   article > 正文

C51_内部Flash读写

C51_内部Flash读写

ISP与IAP

51单片机编程方式根据代码下载方法不同可分为两种,分别是在系统编程(ISP, In System Programming )和在程序中编程(IAP, In Application Programming)。

  • 使用STC-ISP 软件下载程序的编程方式,称为 ISP。 STC89 系列微控制器在完全断电后上电时(也称为冷启动),会自动执行存储在 Flash 中内置的一段程序(称为 BootLoader 程序)。
  • 而在程序中编写程序的编程方式,称为 IAP 。通过程序修改相关寄存器的值,并且写入相应的命令,即可触发 IAP 模式,实现对 Flash 的读写操作。

内部Flash存储结构

STC89C52RC 芯片内部 Flash 可分为程序区和 EEPROM 区。
在这里插入图片描述

IAP读写与Flash擦除的方法

相关寄存器

在这里插入图片描述

单字节读取操作

  • 定义缓存变量,存放将要读取的内容
unsigned char dat;
  • 1
  • 打开IAP功能,并写入读取命令
ISP_CONTR=0x81; // 打开 IAP 功能,允许编程改变 Flash ,设置 Flash 操作等待
时间
ISP_CMD = 0x02; // 允许对 "Data Flash/EEPROM 区 " 进行字节读取
  • 1
  • 2
  • 3
  • 写入IAP操作的地址
ISP_CONTR=0x81; // 打开 IAP 功能,允许编程改变 Flash ,设置 Flash 操作等待
时间
ISP_CMD = 0x02; // 允许对 "Data Flash/EEPROM 区 " 进行字节读取
  • 1
  • 2
  • 3
  • IAP功能触发
操作ISP_TRIG = 0x46; // 写入触发命令 0x46
ISP_TRIG = 0xB9; // 写入触发命令字 0xB9
  • 1
  • 2
  • 读取数据
操作ISP_TRIG = 0x46; // 写入触发命令 0x46
ISP_TRIG = 0xB9; // 写入触发命令字 0xB9
  • 1
  • 2
  • IAP功能禁用
ISP_CONTR = 0x00; // 禁用 IAP 读写 EEPROM
ISP_CMD = 0x00; // 待机模式,无 ISP 操作
ISP_TRIG = 0x00; // 关闭 IAP 功能
  • 1
  • 2
  • 3

扇区擦除

由于 Flash 存储介质的特性,只能对其写入“ 0” 而不能写入“ 1” ,因此需要对扇区内写入“ 1” ,即擦除操作。在首次对扇区执行写入操作时,必须先执行扇区擦除操作。

  • 打开IAP功能,写入擦除命令
  • 写入IAP操作的地址
  • IAP功能触发
  • IAP功能禁用

单字节写入

  • 打开IAP功能,并写入“写”命令
  • 写入IAP操作的地址
  • 写入数据
  • IAP功能触发
  • IAP功能禁用

Flash读写注意事项

  1. 必须先对扇区进行擦除,再写入。
  2. 在单片机工作电压偏低时,不建议进行 IAP 读写 Flash 操作。
  3. 由于 IAP 操作仅支持以字节方式读取或写入,建议需要同一次修改的数据放在同一个扇区中,不需要同一次修改的数据放在其他扇区,不需要把扇区中的 512 字节都用满。如果在一个扇区内存放了大量的数据,需要修改其中的一小部分时, 则需要先将该扇区中的数据读出至 RAM 中,然后擦除整个扇区,再将数据从 RAM 写入至 Flash 中。
  4. 在 STC-ISP 软件中,可以在下载程序时对 EEPROM 区进行擦除。在勾选“本次下载需要修改硬件选项”后,如果勾选“下次下载用户程序时擦除用户 EEPROM 区”选项,EEPROM 区中的所有内容将会被擦除。

实例与代码解析

编写EEPROM.h文件

  1. 定义ISP-IAP相关寄存器
sfr ISP_DATA  = 0XE2;    //定义ISP-IAP操作时的数据寄存器
sfr ISP_ADDRH = 0XE3;    //定义ISP-IAP操作地址寄存器低位
sfr ISP_ADDRL = 0XE4;    //定义ISP-IAP操作地址寄存器高位
sfr ISP_CMD   = 0XE5;    //定义ISP-IAP命令寄存器
sfr ISP_TRIG  = 0XE6;    //定义ISP-IAP命令触发寄存器
sfr ISP_CONTR = 0XE7;    //定义ISP-IAP命令寄存器
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  1. 声明API函数

编写EEPROM.c文件

  1. 包含头文件
#include <reg52.h>
#include <EEPROM.h>
  • 1
  • 2
  1. 编写内部函数
static void IAPTrigger();
static void IAPDisable();

static void IAPTrigger()
{
  ISP_TRIG = 0x46;          //对ISP-IAP命令触发寄存器写入触发命令0x46
  ISP_TRIG = 0xB9;          //对ISP-IAP命令触发寄存器写入触发命令0xB9
}

static void IAPDisable()
{
  ISP_CONTR = 0x00;          //禁用IAP读写EEPROM
  ISP_CMD   = 0x00;          //无IAP操作
  ISP_TRIG  = 0x00;          //关闭IAP功能
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  1. 编写API函数
unsigned char IAPByteRead(unsigned int addr)
{
  unsigned char dat;        //定义数据缓存变量
  ISP_CONTR = 0x81;         //打开IAP功能,允许编程改变Flash,设置Flash操作等待时间
  ISP_CMD   = 0x01;         //允许对"Data Flash/EEPROM区"进行字节读取
   
  ISP_ADDRL = addr;         //IAP操作地址寄存器低位
  ISP_ADDRH = addr >> 8;    //IAP操作地址寄存器高位
 
  IAPTrigger();             //触发IAP功能
  dat = ISP_DATA;           //将需要读出的数据放进缓存变量
  IAPDisable();             //禁用IAP功能
  return dat;               //将读取到的数据作为返回值
}

void IAPSectorErase(unsigned int addr)
{
  ISP_CONTR = 0x81;           //打开IAP功能,允许编程改变Flash,设置Flash操作等待时间
  ISP_CMD   = 0x03;           //允许对"Data Flash/EEPROM区"进行扇区擦除
 
  ISP_ADDRL = addr;           //写入IAP操作地址寄存器低位
  ISP_ADDRH = addr >> 8;      //写入IAP操作地址寄存器高位
 
  IAPTrigger();               //触发IAP功能
  IAPDisable();               //禁用IAP功能
}

void IAPByteWrite(unsigned int addr, unsigned char dat)
{
  ISP_CONTR = 0x81;          //打开IAP功能,允许编程改变Flash,设置Flash操作等待时间
  ISP_CMD   = 0x02;          //允许对"Data Flash/EEPROM区"进行字节写入
 
  ISP_ADDRL = addr;          //IAP操作地址寄存器低位
  ISP_ADDRH = addr >> 8;     //IAP操作地址寄存器高位
  ISP_DATA = dat;            //将需要写入的数据放进ISP_DATA
 
  IAPTrigger();              //触发IAP功能
  IAPDisable();              //禁用IAP功能
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
void IAPSectorErase(unsigned int addr);                      //擦除指定扇区函数
void IAPByteWrite(unsigned int addr, unsigned char dat);     //字节写入函数
unsigned char IAPByteRead(unsigned int addr);                //字节读取函数
  • 1
  • 2
  • 3

编写main.c文件

  1. 包含头文件
#include <reg52.h>
#include <EEPROM.h>
  • 1
  • 2
  1. 位定义LED按键
sbit LED1 = P2 ^ 4;    //位定义LED1
sbit LED2 = P2 ^ 5;    //位定义LED2
sbit LED3 = P2 ^ 6;    //位定义LED3
sbit KEY1 = P3 ^ 2;    //位定义KEY1
sbit KEY2 = P3 ^ 3;    //位定义KEY2
sbit KEY3 = P3 ^ 4;    //位定义KEY3
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  1. 编写延时函数
static void DelayNms(int nms);

static void DelayNms(int nms)
{
  unsigned int i,j;
  for(i = 0; i < nms; i++)
  {
    for(j = 0; j < 123; j++)
    {
      
    } 
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  1. 编写主函数
void main()
{
  P2 = IAPByteRead(0x2000);               //读取起始地址为0x2000的扇区中的值,赋值给P2寄存器,恢复LED灯状态
  while(1)  
  {
    if(0 == KEY1)                         //第一次检测到KEY1按键被按下
    {
      DelayNms(50);                       //等待约50ms后再次检测按键是否被按下,消除按键抖动带来的影响
      if(0 == KEY1)                       //第二次检测到KEY1按键被按下
      {
        LED1 = ~LED1;                     //对LED1状态取反,改变LED1的亮灭状态
        while(0 == KEY1);                 //等待按键弹起  
        IAPSectorErase(0x2000);           //擦除起始地址为0x2000的扇区
        IAPByteWrite(0x2000,P2);          //对起始地址为0x2000的扇区写入P2寄存器的值
      }
    }

    if(0 == KEY2)                         //第一次检测到KEY2按键被按下
    {                       
      DelayNms(50);                       //等待约50ms后再次检测按键是否被按下,消除按键抖动带来的影响
      if(0 == KEY2)                       //第二次检测到KEY2按键被按下
      {
        LED2 = ~LED2;                     //对LED2状态取反,改变LED2的亮灭状态
        while(0 == KEY2);                 //等待按键弹起
        IAPSectorErase(0x2000);           //擦除起始地址为0x2000的扇区
        IAPByteWrite(0x2000,P2);          //对起始地址为0x2000的扇区写入P2寄存器的值
      }
    }

    if(0 == KEY3)                         //第一次检测到KEY3按键被按下
    {
      DelayNms(50);                       //等待约50ms后再次检测按键是否被按下,消除按键抖动带来的影响
      if(0 == KEY3)                       //第二次检测到KEY3按键被按下
      {
        LED3 = ~LED3;                     //改变LED3的开关状态
        while(0 == KEY3);                 //等待按键弹起 
        IAPSectorErase(0x2000);           //擦除起始地址为0x2000的扇区
        IAPByteWrite(0x2000,P2);          //对起始地址为0x2000的扇区写入P2寄存器的值
      }
    }
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/437468
推荐阅读
相关标签
  

闽ICP备14008679号