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

而在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);
}效果如下:

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