lkl0305

STC/IAP15 MCU中FLASH的擦除/编程/读取

0
阅读(7476)

在宏晶公司的STC15系列单片机单片机中,有专门的EEPROM可以用于存放掉电时需要保存的数据。这些数据FLASH(EEPROM)和程序FLASH是分开的。

QQ截图20170311135707.png

而在IAP15和IRC15系列中,以下系列特殊,用户可以在用户程序区直接修改用户程序,所有注:没有专门的数据Flash,但是用户可以将用户程序区的程序Flash当作数据Flash使用。

为了擦除/编程/读取这些数据FLASH,需要使用相关的特殊功能寄存器。

分别是:

ISP/IAP控制寄存器——IAP_CONTR

ISP/IAP命令寄存器——IAP_CMD

ISP/IAP地址寄存器——IAP_ADDRH和IAP_ADDRL

ISP/IAP数据寄存器——IAP_DATA

IAP/ISP命令触发寄存器——IAP_TRIG

使用的步骤基本是:

(1)设置IAP_CONTR,允许操作

(2)设置IAP_CMD,确定什么操作

(3)设置IAP_ADDRH和IAP_ADDRL,确定操作地址

如果是写数据的话:

(4)设置IAP_DATA,写入数据

(5)设置IAP_TRIG,分别向该寄存器写入0x5a和0xa5数据,触发写操作。

如果是读数据的话:

(4)设置IAP_TRIG,分别向该寄存器写入0x5a和0xa5数据,触发读操作。

(5)从IAP_DATA读出数据

如果是擦除扇区的话:

(4)设置IAP_TRIG,分别向该寄存器写入0x5a和0xa5数据,触发擦除操作。

这里注意MCU的扇区是512字节为一个扇区,一次擦除一个扇区,写入数据时可以一个字节一个字节进行。

下面给出自己写的一个字节操作小例子,实现向FLASH首先擦除一个扇区,然后向该扇区写入256个字节,然后读出进行判断。

#include <STC15F2K60S2.H>
#include "stdio.h"
#include "intrins.h"

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

#define ENABLE_IAP  0x82            //if SYSCLK<20MHz

//测试地址
#define IAP_ADDRESS 0x1000

#define BUFFERSIZE 256
unsigned char xdata send_Data[BUFFERSIZE] = {0};
unsigned char xdata receive_Data[BUFFERSIZE] = {0};

void GPIO_Init(void);
void Iap_Idle(void);
unsigned char Iap_ReadByte(unsigned int addr);
void Iap_ProgramByte(unsigned int addr, unsigned char dat);
void Iap_EraseSector(unsigned int addr);
void Uart_Init(void);
char putchar(char c);
void Delaynms(unsigned int n);
void Delay1ms(void);
    
void main(void)
{
    signed int i = 0;
    GPIO_Init();
    Uart_Init();
    
    Delaynms(10000);
    
    printf("This is a test of read/write/erase Flash...\r\n");
    
    
    printf("\r\n>>1.Erase sector testing...\r\n");
    Iap_EraseSector(IAP_ADDRESS);
    for(i = 0; i < BUFFERSIZE; i++)
    {
        if(Iap_ReadByte(IAP_ADDRESS + i) != 0xff)
        {
            printf("Erase sector error!\r\n");
            while(1)
            {
                ;
            }
        }
    }
    printf("Erase sector OK!\r\n");
    
    printf("\r\n>>2.The Data of sending is:\r\n");
    
    for(i = 0; i < BUFFERSIZE; i++)
    {
        send_Data[i] = i;
        printf("%02x ", (unsigned int)send_Data[i]);
        if((i % 16) == 0x0f)
        {
            printf("\r\n");
        }
    }
    
    printf("\r\n>>3.Program byte testing...\r\n");
    for(i = 0; i < BUFFERSIZE; i++)
    {
        Iap_ProgramByte(IAP_ADDRESS + i, send_Data[i]);
    }
    
    printf("\r\n>>4.Read byte testing...\r\n");
    for(i = 0; i < BUFFERSIZE; i++)
    {
        receive_Data[i] = Iap_ReadByte(IAP_ADDRESS + i);
        printf("%02x ", (unsigned int)receive_Data[i]);
        if((i % 16) == 0x0f)
        {
            printf("\r\n");
        }

    }
    
    printf("\r\n>>5.Comparison is consistent...\r\n");
    for(i = 0; i < BUFFERSIZE; i++)
    {
        if(receive_Data[i] != send_Data[i])
        {
            printf("Program/Read byte error!\r\n");
        }
    }
    printf("\r\nProgram/Read byte OK!\r\n");
    
    printf("\r\n>>TEST OK<<\r\n");
    
    for(;;)
    {
        ;
    }
}

void GPIO_Init(void)
{
    P0M0 = 0x00;
    P0M1 = 0x00;
    
    P1M0 = 0x00;
    P1M1 = 0x00;
    
    P2M0 = 0x00;
    P2M1 = 0x00;
    
    P3M0 = 0x00;
    P3M1 = 0x00;
    
    P4M0 = 0x00;
    P4M1 = 0x00;
}

/*----------------------------
关闭IAP
----------------------------*/
void Iap_Idle(void)
{
    IAP_CONTR = 0;
    IAP_CMD = 0;
    IAP_TRIG = 0;
    IAP_ADDRH = 0x80;
    IAP_ADDRL = 0x00;
}

/*----------------------------
从ISP/IAP/EEPROM区域读取一字节
----------------------------*/
unsigned char Iap_ReadByte(unsigned int addr)
{
    unsigned char dat;                       //数据缓冲区

    IAP_CONTR = ENABLE_IAP; 
    IAP_CMD = CMD_READ;
    IAP_ADDRL = addr;
    IAP_ADDRH = addr >> 8;
    IAP_TRIG = 0x5a;
    IAP_TRIG = 0xa5;
    _nop_();
    dat = IAP_DATA;
    Iap_Idle();

    return dat;
}

/*----------------------------
写一字节数据到ISP/IAP/EEPROM区域
----------------------------*/
void Iap_ProgramByte(unsigned int addr, unsigned char dat)
{
    IAP_CONTR = ENABLE_IAP;
    IAP_CMD = CMD_PROGRAM;
    IAP_ADDRL = addr;
    IAP_ADDRH = addr >> 8;
    IAP_DATA = dat;
    IAP_TRIG = 0x5a;
    IAP_TRIG = 0xa5;
    _nop_();
    Iap_Idle();
}

/*----------------------------
扇区擦除
----------------------------*/
void Iap_EraseSector(unsigned int addr)
{
    IAP_CONTR = ENABLE_IAP;
    IAP_CMD = CMD_ERASE;
    IAP_ADDRL = addr;
    IAP_ADDRH = addr >> 8;
    IAP_TRIG = 0x5a;
    IAP_TRIG = 0xa5;
    _nop_();
    Iap_Idle();
}

void Uart_Init(void)        //115200bps@11.0592MHz
{
    SCON = 0x50;        //8位数据,可变波特率
    AUXR |= 0x01;        //串口1选择定时器2为波特率发生器
    AUXR |= 0x04;        //定时器2时钟为Fosc,即1T
    T2L = 0xE8;        //设定定时初值
    T2H = 0xFF;        //设定定时初值
    AUXR |= 0x10;        //启动定时器2
}

char putchar(char c)
{
    SBUF = c;
    while(TI == 0)
    {
        ;
    }
    TI = 0;
    return c;
}

void Delay1ms(void)        //@11.0592MHz
{
    unsigned char i, j;

    _nop_();
    _nop_();
    _nop_();
    i = 11;
    j = 190;
    do
    {
        while (--j);
    } while (--i);
}

void Delaynms(unsigned int n)
{
    do
    {
        Delay1ms();
    }while(--n);
}
效果如下:

可以看到,的确实现了预期效果!