基于某些安全考虑或者降成本,我们不希望使用外部存储器件,但有时我们由需要记录一下参数,确保断电不丢失,这时,富余的内部代码存储Flash就派上用场了。
不同于外部存储器,几乎所有的内部Flash读写都十分麻烦,甚至需要使用到汇编。
下面我们将讲述dsPIC33E如何读写内部Flash,此处以dsPIC33EP256GP506为例。
示例代码下载:https://download.csdn.net/download/u010875635/10821230
在操作Flash之前,我们有必要下了解一下Flash的结构,注意以下几点:
1、dsPIC33E/PIC24E闪存程序存储器被分段为页和行,每页1024个指令字,每行128个指令字,部分器件不支持行
2、dsPIC33E/PIC24E闪存程序存储器支持128/2个保持寄存器以一次对存储器的一行/双指令字进行编程
3、dsPIC33E/PIC24E每个指令字为24位,占2个16为,最高8位为虚字节
4、 dsPIC33E/PIC24E每个指令字占2个地址,例如0x000000和0x000001都是表示第一个指令字
5、对于大多数应用,24位中的高8位不用于存储数据,建议便成为NOP指令或者非法操作码值
6、地址总是偶数呈现,读取奇数地址获取到的与前一个偶数时一致的,所以读取奇数地址没有意义
dsPIC33E/PIC24E操作Flash相关的几个寄存器。
1、TBLPAG:表地址页位,8位表地址页位与W寄存器组组合形成23位有效程序存储器地址加上一个字节选择位
2、 每个地址数据由2个字合并而成,由以下四个寄存器操作
TBLRDL:表读低位字
TBLWTL:表写低位字
TBLRDH:表读高位字
TBLWTH:表写高位字
程序存储器分为很多个表页,表页地址为TBLPAG,占据地址高8位(共24位),表页不同于Flash的擦除页。表页内部地址为16位的有效地址,占据低16位,所以一个表页大小为0x010000
表操作地址生成,有TBLPAG和来自Wn的16位组合成24位的有效地址,7-->0 15------>0
读相关操作较为简单,只需要写入TBLPAB和TBLRDL或TBLRDH然后读取即可
写相关操作较为复杂,允许一次擦除1页(8行),运行一次编程1行,或者编程2个指令字字。
注意擦除和编程都是边沿对齐的,从存储器起始开始,分别以1024指令字和128或2指令字作为边界,即,擦除的地址单位长度为0x400,例如擦除0-0x0003ff,0x000400-0x0007ff;编程的地址单位长度为0x80或者0x04,例如,编程0-0x00007f,0x000080-0x0000ff或者0x000080-0x000083
在程序存储器的最后一页上执行页擦除操作会清零闪存配置字,从而使能代码保护。因此,用户应避免在程序存储器的最后一页上执行页擦除操作。
要执行编程操作,必须先将其对应的整页(1024个指令字)全部擦除,然后重新编程,所以一般操作流程如下:
1、读取闪存程序存储器的一页,并将其作为数据镜像存储到RAM中,RAM镜像必须从1024字程序存储器的偶地址边界读取
2、用新的数据更新RAM中的数据镜像
3、擦除闪存存储器页
a)设置NVMCON寄存器以擦除一页
b)禁止中断
c)将要擦除页的地址写入NVMADRU和NVMADR寄存器(可以是该页任意地址)
d)将秘钥序列吸入NVMKEY寄存器,以使能擦除
e)将NVMCON<15>的WR位置1,启动擦除周期
f)擦除周期结束时WR位清零
g)允许中断
4、用表写操作将128或者2个指令字的一行或双指令字从RAM装入写锁存器
5、对一行或者2个指令字进行编程
a)设置NVMCON以编程一行
b)禁止中断
c)将要编程行或第一个字的地址写入NVMADRU和NVMADR寄存器
d)将秘钥序列吸入NVMKEY寄存器,以使能编程周期
e)将NVMCON<15>的WR位置1,启动编程周期
f)编程周期结束时WR位清零
g)允许中断
6、重复4-6步骤,对多个数据进行编程,直到一页编程完成
7、根据需要,重复1-6,对多页进行编程
关联寄存器如下:
NVMCON:主控制寄存器,选择执行擦出还是编程操作
NVMKEY:只写寄存器,防止闪存被误写或者误擦除,编程或擦除前必须执行解锁序列,解锁期间禁止中断,①将0x55写入NVMKEY;②将0xAA写入NVMKEY;③执行2条NOP指令④一个周期中写入NVMCON寄存器
写锁存器长度与器件有关,要参考对应器件的Datasheet。
dsPIC33E通用flash存储结构如下:
dsPIC33EP256GP506存储结构如下:
所有可用的flash操作。
dsPIC33EP256GP506可用操作。
汇编级读写Flash底层代码,dsPICflash.s
.include "xc.inc"
;C Called Function
.global _MemRead
.global _MemReadHigh
.global _MemReadLow
.global _MemEraseOnePage
.global _MemWriteDoubleInstructionWords
.section .text
;************************
; Function _MemRead:
; W0 = TBLPAG value
; W1 = Table Offset
; Return: Data in W1:W0
;************************
_MemRead:
MOV W0, TBLPAG
NOP
TBLRDL [W1], W0
TBLRDH [W1], W1
RETURN
;************************
; Function _MemRead:
; W0 = TBLPAG value
; W1 = Table Offset
; Return: Data in W0
;************************
_MemReadHigh:
MOV W0, TBLPAG
NOP
TBLRDH [W1], W0
RETURN
;************************
; Function _MemRead:
; W0 = TBLPAG value
; W1 = Table Offset
; Return: Data in W0
;************************
_MemReadLow:
MOV W0, TBLPAG
NOP
TBLRDL [W1], W0
RETURN
;************************
; Function _MemErasePage:
; W0 = TBLPAG value
; W1 = Table Offset
;************************
_MemEraseOnePage:
MOV W0,NVMADRU
MOV W1,NVMADR
;TBLWTL w2,[w1]
; Setup NVMCON to erase one page of Program Memory
MOV #0x4003,W0
MOV W0,NVMCON
; Disable interrupts while the KEY sequence is written
PUSH SR
;MOV #0x00E0,W0
;IOR SR
; Write the KEY Sequence
MOV #0x55,W0
MOV W0,NVMKEY
MOV #0xAA,W0
MOV W0,NVMKEY
; Start the erase operation
BSET NVMCON,#15
; Insert two NOPs after the erase cycle (required)
NOP
NOP
;Re-enable interrupts, if needed
POP SR
RETURN
; ************************
; Error , no use
; Function _MemWriteDoubleInstructionWords:
; All PIC device support Double Instructions program
; Write four 16-bit data to Double Instructions to Flash
; W0 = TBLPAG value
; W1 = Table Offset
; W2 = data
;************************
_MemWriteDoubleInstructionWords_Error:
; Load the destination address to be written
MOV W0,NVMADRU
MOV W1,NVMADR
; Load the two words into the latches
; W2 points to the address of the data to write to the latches
; Set up a pointer to the first latch location to be written
MOV #0xFA,W0
MOV W0,TBLPAG
MOV #0,W1
; Perform the TBLWT instructions to write the latches
TBLWTL [W2++],[W1]
TBLWTH [W2++],[W1++]
TBLWTL [W2++],[W1]
TBLWTH [W2++],[W1++]
; Setup NVMCON for word programming
MOV #0x4001,W0
MOV W0,NVMCON
; Disable interrupts < priority 7 for next 5 instructions
; Assumes no level 7 peripheral interrupts
;DISI #06
PUSH SR
; Write the key sequence
MOV #0x55,W0
MOV W0,NVMKEY
MOV #0xAA,W0
MOV W0,NVMKEY
; Start the write cycle
BSET NVMCON,#15
NOP
NOP
POP SR
NOP
NOP
RETURN
;************************
; Function _MemWriteDoubleInstructionWords:
; All PIC device support Double Instructions program
; Write four 16-bit data to Double Instructions to Flash
; W0 = TBLPAG value
; W1 = Table Offset
; W2 = data
;************************
_MemWriteDoubleInstructionWords:
; Define the address from where the programming has to start
;.equ PROG_ADDR, 0x01800;
; Load the destination address to be written
;MOV #tblpage(PROG_ADDR),W9
;MOV #tbloffset(PROG_ADDR),W8
MOV W0,NVMADRU
MOV W1,NVMADR
;MOV W9,NVMADRU
;MOV W8,NVMADR;
; Load the two words into the latches
; W2 points to the address of the data to write to the latches
; Set up a pointer to the first latch location to be written
MOV #0xFA,W0
MOV W0,TBLPAG
MOV #0,W1
; Perform the TBLWT instructions to write the latches
TBLWTL [W2++],[W1]
TBLWTH [W2++],[W1++]
TBLWTL [W2++],[W1]
TBLWTH [W2++],[W1++]
; Setup NVMCON for word programming
MOV #0x4001,W0
MOV W0,NVMCON
; Disable interrupts < priority 7 for next 5 instructions
; Assumes no level 7 peripheral interrupts
DISI #06
; Write the key sequence
MOV #0x55,W0
MOV W0,NVMKEY
MOV #0xAA,W0
MOV W0,NVMKEY
; Start the write cycle
BSET NVMCON,#15
NOP
NOP
return
C语言封装一层为InnerFlash:
InnerFlash.h
#ifndef _MCU_DRIVERS_INNERFLASH_H_
#define _MCU_DRIVERS_INNERFLASH_H_
//一个指令占2个16位,其中高16位的高8位为虚字节
typedef union OneInstruction {
uint32_t UINT32;
struct {
uint16_t LowWord; //低16位
uint16_t HighWord; //高16位
} HighLowUINT16s;
} OneInstruction_t;
//一个地址占2个16位,其中高16位为Flash大页面
typedef union FlashAddr {
uint32_t Uint32Addr;
struct {
uint16_t LowAddr; //低16位
uint16_t HighAddr; //高16位
} Uint16Addr;
} FlashAddr_t;
//读取一个指令字
OneInstruction_t InnerFlash_ReadOneInstruction(FlashAddr_t flashAddr);
//读取低16位
unsigned int InnerFlash_ReadInstructionLow(FlashAddr_t flashAddr);
//读取高16位,仅低8位有效,高8位始终为0
unsigned int InnerFlash_ReadInstructionHigh(FlashAddr_t flashAddr);
//擦除0x000800倍数起始的1024个字(2048个地址,一页)
unsigned int InnerFlash_EraseFlashPage(FlashAddr_t flashAddr);
//需要先擦除,再写入
//写入Flash,2个指令字为一组,若为奇数,最后一个填充为0xFFFFFF
//每个指令字的低16位存储一个数据,即2个地址存储1个16位数据
//例如 data[2]={0xaa05,0xaa06};,存入0x004000,即0x004000存入0x00aa05,0x004002存入0x00aa06
void InnerFlash_WriteInstructionsToFlash(volatile FlashAddr_t source_addr,volatile OneInstruction_t *data,volatile uint16_t dataLength);
#endif
InnerFlash.c
#include
#include
#include
#include
#include "InnerFlash.h"
extern unsigned int MemReadHigh (unsigned int TablePage, unsigned int TableOffset);
extern unsigned int MemReadLow (unsigned int TablePage, unsigned int TableOffset);
extern unsigned int MemEraseOnePage (unsigned int TablePage, unsigned int TableOffset);
//写入2个指令字,即4个16bits,注意奇数位为高,仅8bits
//data[0]为第一个指令字的低16位,data[1]的低8位为第一个指令字的高8位,一个指令字宽度为32,最高8位为虚拟字节,所以实际宽度为24
extern void MemWriteDoubleInstructionWords(volatile unsigned int TablePage,volatile unsigned int TableOffset, volatile OneInstruction_t *temp);
//每个指令字占2个地址,例如0x000000和0x000001都是表示第一个指令字
//flash分为多个表页,每一表页有0x10000/2个指令字,例如地址0x004000,为第0x00页,页内地址为:0x4000
//每个偶数地址包含2个16位数据,但实际上只有24位,高8位数据是虚拟的,此函数读取低八位
//可通过Mplab的PIC存储器视图->程序,查看hex文件对应地址的数据,然后使用下面方法读取测试比对
//读取一个指令字
OneInstruction_t InnerFlash_ReadOneInstruction(FlashAddr_t flashAddr)
{
unsigned int Data1;
OneInstruction_t Data;
//方法一
//TBLPAG = TablePage;
//Data1 = __builtin_tblrdl(TableOffset);
//方法二
Data1 = MemReadLow (flashAddr.Uint16Addr.HighAddr, flashAddr.Uint16Addr.LowAddr);
Data.HighLowUINT16s.LowWord = Data1;
Data1 = MemReadHigh (flashAddr.Uint16Addr.HighAddr, flashAddr.Uint16Addr.LowAddr);
Data.HighLowUINT16s.HighWord = Data1;
return Data;
}
//每个指令字占2个地址,例如0x000000和0x000001都是表示第一个指令字
//flash分为多个表页,每一表页有0x10000/2个指令字,例如地址0x004000,为第0x00页,页内地址为:0x4000
//每个偶数地址包含2个16位数据,但实际上只有24位,高8位数据是虚拟的,此函数读取低八位
//可通过Mplab的PIC存储器视图->程序,查看hex文件对应地址的数据,然后使用下面方法读取测试比对
unsigned int InnerFlash_ReadInstructionLow(FlashAddr_t flashAddr)
{
unsigned int Data1;
//方法一
//TBLPAG = TablePage;
//Data1 = __builtin_tblrdl(TableOffset);
//方法二
Data1 = MemReadLow (flashAddr.Uint16Addr.HighAddr, flashAddr.Uint16Addr.LowAddr);
return Data1;
}
//每个指令字占2个地址,例如0x000000和0x000001都是表示第一个指令字
//flash分为多个表页,每一表页有0x10000/2个指令字,例如地址0x004000,为第0x00页,页内地址为:0x4000
//每个偶数地址包含2个16位数据,但实际上只有24位,高8位数据是虚拟的,此函数读取高八位
//可通过Mplab的PIC存储器视图->程序,查看hex文件对应地址的数据,然后使用下面方法读取测试比对
unsigned int InnerFlash_ReadInstructionHigh(FlashAddr_t flashAddr)
{
unsigned int Data1;
//方法一
//TBLPAG = TablePage;
//Data1 = __builtin_tblrdh(TableOffset);
//方法二
Data1 = MemReadHigh (flashAddr.Uint16Addr.HighAddr, flashAddr.Uint16Addr.LowAddr);
return Data1;
}
//每个指令字占2个地址,例如0x000000和0x000001都是表示第一个指令字
//flash分为多个表页,每一表页有0x10000/2个指令字,例如地址0x004000,为第0x00页,页内地址为:0x4000
//每个偶数地址包含2个16位数据,但实际上只有24位,高8位数据是虚拟的,此函数读取高八位
//可通过Mplab的PIC存储器视图->程序,查看hex文件对应地址的数据,然后使用下面方法读取测试比对
//擦除必须一次性擦除1024(实际地址偏移会*2,0x400*2,因为一组奇数和偶数表示一个指令字)个指令字,从0开始,边沿对齐,输入任意地址,会擦除包含这个地址在内的一页
//配置字在最后一页,不允许擦除最后一页,否则会导致代码保护,全部置0
//并非所有器件都支持配置字编程,若是支持,一般配置字不需要擦除,可直接编程,但是要求时钟为FRC,不能带PLL,具体是否可编程配置字,查看NVMOP<3:0>
unsigned int InnerFlash_EraseFlashPage(FlashAddr_t flashAddr)
{
MemEraseOnePage (flashAddr.Uint16Addr.HighAddr, flashAddr.Uint16Addr.LowAddr);
return 1;
}
//需要先擦除,再写入
//写入Flash,2个指令字为一组,若为奇数,最后一个填充为0xFFFFFF
//每个指令字的低16位存储一个数据,即2个地址存储1个16位数据
//例如 data[2]={0xaa05,0xaa06};,存入0x004000,即0x004000存入0x00aa05,0x004002存入0x00aa06
void InnerFlash_WriteInstructionsToFlash(volatile FlashAddr_t flashAddr,volatile OneInstruction_t *data,volatile uint16_t dataLength)
{
volatile OneInstruction_t dataTmp[2];
volatile uint16_t i;
for(i=0;i
再封装一层,设置特殊地址为参数存储地址,存储起始地址为0x02A000。
DataRecord.h
#ifndef _MCU_DRIVERS_DATARECORD_H_
#define _MCU_DRIVERS_DATARECORD_H_
#include "InnerFlash.h"
//index要小于4074,目前开辟的最大的用于存储数量的空间索引为4073
//读取flash内容
uint16_t DataRecord_ReadData(uint16_t index);
void DataRecord_ErasePage();
//index要小于4074,目前开辟的最大的用于存储数量的空间索引为4073
//往flash写入内容
uint16_t DataRecord_WriteData(uint16_t index, volatile OneInstruction_t data);
uint16_t DataRecord_WriteDataArray(uint16_t index, volatile OneInstruction_t *data, uint16_t length);
//擦除一大页flash
void EraseLargePage(uint16_t pageIndex);
//填充一大页flash
void FillLagrePage(uint16_t pageIndex);
#endif
DataRecord.c
/*****************************************************
* 本文件函数用于记录标定参数,断电不丢失
*****************************************************/
#include
#include "DataRecord.h"
//dsPIC33EP256GP50X, dsPIC33EP256MC20X/50X, PIC24EP256GP/MC20X这类256K的器件
//用户编程flash存储有88K个指令字空间,地址范围为:0x000200-0x02AFEA
//写锁存器占2个指令字,地址为:0xFA0000和0xFA0002
//USERID起始为0x800FF8,结束0x800FFE
//DEVID起始0xFF0000,结束0xFF0002
//定义存储数据所在空间的起始地址,0x02A000,结束地址为0x2AFE8,可存储数据量为:(0x2AFE8-0x2A000+2)/2=4074
#define DATA_RECORD_START_PAGE 0x0002
#define DATA_RECORD_START_ADDR 0xA000
//index要小于4074,目前开辟的最大的用于存储数量的空间索引为4073
//读取flash内容
uint16_t DataRecord_ReadData(uint16_t index)
{
FlashAddr_t flashAddr;
flashAddr.Uint16Addr.HighAddr = DATA_RECORD_START_PAGE;
flashAddr.Uint16Addr.LowAddr = DATA_RECORD_START_ADDR+index*2;
return InnerFlash_ReadInstructionLow(flashAddr);
}
void DataRecord_ErasePage()
{
FlashAddr_t flashAddr;
flashAddr.Uint16Addr.HighAddr = DATA_RECORD_START_PAGE;
flashAddr.Uint16Addr.LowAddr = DATA_RECORD_START_ADDR;
InnerFlash_EraseFlashPage(flashAddr);
}
//可写范围为0x2A000-0x2AF7E,总共一页,长度为0x800,每2个地址一个指令字,总共1024个指令字
//往flash写入内容
uint16_t DataRecord_WriteData(uint16_t index, volatile OneInstruction_t data)
{
if(index>1023) //超过一页
return 0xa5a5;
FlashAddr_t flashAddr;
flashAddr.Uint16Addr.HighAddr = DATA_RECORD_START_PAGE;
flashAddr.Uint16Addr.LowAddr = DATA_RECORD_START_ADDR+index*2;
//读取对应地址数据,若数据与要写入的一致,则不再写入
OneInstruction_t dataTmp = InnerFlash_ReadOneInstruction(flashAddr);
if(dataTmp.UINT32==data.UINT32) //写入与实际的一样,不用写入
return 0xffff;
//先读取一页,再擦除,修改读取数据,写入一页
OneInstruction_t pageData[1024];
uint16_t i;
//先将对应页的数据全部读出来,0x800个地址,0x400个字
for(i=0;i<1024;i++)
{
flashAddr.Uint16Addr.LowAddr = DATA_RECORD_START_ADDR+i*2;
pageData[i] = InnerFlash_ReadOneInstruction(flashAddr);
}
//擦除整页
flashAddr.Uint16Addr.LowAddr = DATA_RECORD_START_ADDR;
InnerFlash_EraseFlashPage(flashAddr);
//修改数据,每1024个数据pageData索引要从0重新开始
pageData[index] = data;
//将修改后的数据写回该页
InnerFlash_WriteInstructionsToFlash(flashAddr,pageData,1024);
return 0;
}
//不允许数组超过一页
uint16_t DataRecord_WriteDataArray(uint16_t index, volatile OneInstruction_t *data, uint16_t length)
{
//超过一页
if((index+length)>1023)
return 0xa5a5;
FlashAddr_t flashAddr;
uint16_t i,cmpNZ=0;
OneInstruction_t pageData[1024];
//取对应页的首地址
flashAddr.Uint16Addr.HighAddr = DATA_RECORD_START_PAGE;
flashAddr.Uint16Addr.LowAddr = DATA_RECORD_START_ADDR+index*2;
//比对数组
for(i=index;i=0xF800)
break;
}
break;
case 1:
//第一大页
for(offset=0;offset<=0xF800;offset+=0x800)
{
flashAddr.Uint16Addr.LowAddr = offset;
InnerFlash_EraseFlashPage(flashAddr);
if(offset>=0xF800)
break;
}
break;
case 2:
//第二大页
for(offset=0;offset<0xA800;offset+=0x800)
{
flashAddr.Uint16Addr.LowAddr = offset;
InnerFlash_EraseFlashPage(flashAddr);
}
break;
default:break;
}
}
//填充一大页flash
void FillLagrePage(uint16_t pageIndex)
{
volatile uint16_t offset=0;
volatile FlashAddr_t source_addr;
volatile OneInstruction_t data[2];
switch(pageIndex)
{
case 0:
for(offset=0x8000;offset<=0xFFFC;offset+=4)
{
source_addr.Uint16Addr.HighAddr = 0;
source_addr.Uint16Addr.LowAddr = offset;
data[0].HighLowUINT16s.HighWord = 0xF0;
data[0].HighLowUINT16s.LowWord = offset;
data[1].HighLowUINT16s.HighWord = 0xF0;
data[1].HighLowUINT16s.LowWord = offset+2;
InnerFlash_WriteInstructionsToFlash(source_addr,data,2);
//由于最大值为0xFFFF,超过会从0开始,0xFFFC+4=0x0000;
if(offset>=0xFFFC)
break; //跳出循环
}
break;
case 1:
for(offset=0;offset<=0xFFFC;offset+=4)
{
source_addr.Uint16Addr.HighAddr = 1;
source_addr.Uint16Addr.LowAddr = offset;
data[0].HighLowUINT16s.HighWord = 0xF1;
data[0].HighLowUINT16s.LowWord = offset;
data[1].HighLowUINT16s.HighWord = 0xF1;
data[1].HighLowUINT16s.LowWord = offset+2;
InnerFlash_WriteInstructionsToFlash(source_addr,data,2);
//由于最大值为0xFFFF,超过会从0开始,0xFFFC+4=0x0000;
if(offset>=0xFFFC)
break; //跳出循环
}
break;
case 2:
//InnerFlash_EraseFlashPage(2,0xA800);
for(offset=0;offset<0xA800;offset+=4)
{
source_addr.Uint16Addr.HighAddr = 2;
source_addr.Uint16Addr.LowAddr = offset;
data[0].HighLowUINT16s.HighWord = 0xF2;
data[0].HighLowUINT16s.LowWord = offset;
data[1].HighLowUINT16s.HighWord = 0xF2;
data[1].HighLowUINT16s.LowWord = offset+2;
InnerFlash_WriteInstructionsToFlash(source_addr,data,2);
}
break;
default:break;
}
}
使用范例,main.c
#include "McuDrivers/uart/uart.h"
#include "McuDrivers/system/system.h"
#include "McuDrivers/flash/DataRecord.h"
#include "McuDrivers/gpio/gpio.h"
#include "McuDrivers/timer/timer.h"
uint16_t count = 0x0;
int main(void)
{
/******** Initial ********/
System_Initialize_g();
Timer1_Initial_g();
GPIO_Initial_g();
Timer1_Start_g(10);
//DataEEInit();
Uart_Initial_g(115200);
//InnerEEPromDataInit();
System_Delay_MS_g(100);
Uart_Printf_g("start!\n");
//DataRecord_ErasePage(2,0xA000);
//连续跨TBLPAG编程会出现后面的TBLPAG全部清零
EraseLargePage(0);
EraseLargePage(1);
EraseLargePage(2);
// FillLagrePage(0);
// FillLagrePage(1);
// FillLagrePage(2);
OneInstruction_t data[29];
uint16_t i;
for(i=0;i<29;i++)
data[i].UINT32 = 0xF20000+i;
DataRecord_WriteDataArray(0,data,29);
GPIO_LED_D3_Toggle_g();
while(1)
{
System_Delay_MS_g(1000);
//Uart_Printf_g("addr:%06X,read:%04X%04X\r\n",count,InnerFlash_ReadFlashHigh(0x00,count),ReadFlashLow(0x00,count));
//Uart_Printf_g("realaddr:%X, data:%X\r\n",0xa000+count*2,DataRecord_ReadData(count));
}
return 0;
}
uint16_t timerCount = 0;
void Timer1CallBack()
{
timerCount++;
if(timerCount>50)
{
timerCount=0;
GPIO_LED_D4_Toggle_g();
}
}
uint16_t interruptCount=0;
void Int1Function(void)
{
EraseLargePage(interruptCount);
count = 0;
interruptCount++;
GPIO_LED_D3_Toggle_g();
}