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); }效果如下:
可以看到,的确实现了预期效果!