【dsPIC33E】内部Flash读写

版权声明:本文为博主原创文章,转载注明出处 https://blog.csdn.net/u010875635/article/details/84673935

基于某些安全考虑或者降成本,我们不希望使用外部存储器件,但有时我们由需要记录一下参数,确保断电不丢失,这时,富余的内部代码存储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 <xc.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>

#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<dataLength/2*2;i+=2)
    {
        //数组偶数位为指令字高16位(高8位为虚拟字节,仅低8位有效),奇数位为指令字低16位
        //每个指令字存入一个实际16位数据,高8位为0
        dataTmp[0]= data[i];
        dataTmp[1] = data[i+1];
        //每次写入2个指令字,偏移4个地址,每2个地址表示一个指令字
        MemWriteDoubleInstructionWords(flashAddr.Uint16Addr.HighAddr, flashAddr.Uint16Addr.LowAddr+i*2, dataTmp);
    }
    if(dataLength%2==1)
    {
        dataTmp[0]= data[i];
        dataTmp[1].UINT32 = 0xFFFFFF;
        //每次写入2个指令字,偏移4个地址,每2个地址表示一个指令字
        MemWriteDoubleInstructionWords(flashAddr.Uint16Addr.HighAddr, flashAddr.Uint16Addr.LowAddr+i*2, dataTmp);
    }
        
}

再封装一层,设置特殊地址为参数存储地址,存储起始地址为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 <xc.h>
#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<length;i++)
    {
        //读取对应地址数据,若数据与要写入的一致,则不再写入
        flashAddr.Uint16Addr.LowAddr = DATA_RECORD_START_ADDR+i*2;
        pageData[i] = InnerFlash_ReadOneInstruction(flashAddr);
        if(pageData[i].UINT32!=data[i-index].UINT32) //只要有一个数据不同,则需要重新写入
        {
            cmpNZ = 1;
            break;
        }
    }
    
    if(cmpNZ==0) //数据完全一致,不写入
        return 0xffff;
    
    
    //先读取一页,再擦除,修改读取数据,写入一页
    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);
    
    for(i=index;i<length;i++)
    {
        //修改数据,每1024个数据pageData索引要从0重新开始
        pageData[index-(index/1024)*1024+i].UINT32 = data[i].UINT32;
    }
    
    //将修改后的数据写回该页
    InnerFlash_WriteInstructionsToFlash(flashAddr,pageData,1024);

    return 0;
}


//擦除一大页flash
void EraseLargePage(uint16_t pageIndex)
{
    uint16_t offset;
    FlashAddr_t flashAddr;
    flashAddr.Uint16Addr.HighAddr = pageIndex;
    switch(pageIndex)
    {
        case 0:
                //第零大页
        for(offset=0x8000;offset<=0xF800;offset+=0x800)
        {
            flashAddr.Uint16Addr.LowAddr = offset;
            InnerFlash_EraseFlashPage(flashAddr);
            if(offset>=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();
}

 

猜你喜欢

转载自blog.csdn.net/u010875635/article/details/84673935