1. 程式人生 > >【dsPIC33E】內部Flash讀寫

【dsPIC33E】內部Flash讀寫

基於某些安全考慮或者降成本,我們不希望使用外部儲存器件,但有時我們由需要記錄一下引數,確保斷電不丟失,這時,富餘的內部程式碼儲存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();
}