背景:

        在使用DSP28335进行开发时,一些项目中需要将数据信息存储到Flash中,在掉电后能够对数据进行保存,以便再次上电时能够取出使用。为了完成上述操作,TI官方提供了Flash_API(application program interface),本文基于TI官网给出的技术文件"Flash2833x_API_Readme",对官网提供的Flash_API的使用方式进行简要的说明。

一、相关的库文件与工程文件

    在TI官网上搜索SPR539,下载TMS320F2833x Flash APIs (v2.00),解压后安装setup_Flash2833x_API_V210,根据现有的硬件选择文件,我这里使用Flash28335_API_V210。

    将lib文件夹中的库文件添加入已有工程的lib中,将include文件夹中的两个头文件:"Flash2833x_API_Config.h"与"Flash2833x_API_Library.h"添加入已有工程的include中。并在CCS中添加lib与头文件的路径。

将库文件与头文件都包含入工程中后,开始对程序进行修改。

二、程序配置

1、修改Flash2833x_API_Config.h头文件

在Flash2833x_API_Config.h中选择系统CPU的主频,我这里的主频为150MHz。给定头文件中列出了几种常见的主频,也可以根据自己硬件设备修改程序,比如125MHz时CPU_RATE为8.00L。

Flash2833x_API_Config.h头文件中还给出了Flash分频系数,该部分不需要修改。

注意!要求原工程中PLL配置正确,避免设备进入limp 模式。

2、将Flash_API中的程序复制至内部SARAM中运行

由于28335只有一个Flash array,无法在擦除、读写flash的同时执行flash中的程序,因此需要先将Flash_API中的程序复制至SARAM中,从SARAM中执行程序。此外,Flash_API函数中有严格的时序要求,复制至SARAM中运行的程序是无延时的,能够使得运行更准确。

将程序复制至SARAM中的具体操作如下:

首先,修改cmd文件,在cmd文件中加入一个名为flash28_API的SECTIONS,也可以直接加入在原工程的SECTIONS中。

SECTIONS
{
Flash28_API:
   {
        -lFlash28335_API_V210.lib(.econst)
        -lFlash28335_API_V210.lib(.text)
   }
                       LOAD = FLASHA,

                       RUN = RAML0,
                       LOAD_START(_Flash28_API_LoadStart),
                       LOAD_END(_Flash28_API_LoadEnd),
                       RUN_START(_Flash28_API_RunStart),
                       PAGE = 0
}

这部分SECTIONS包含了Flash_API的程序(.text)和常数参数(.econst),并且定义了Flash_API程序的加载开始地址、结束地址与运行开始地址,并且已经将这些定义写入了Flash2833x_API_Library.h中供用户使用。

其次,在已有工程的主函数中包含Flash2833x_API_Library.h,并使用MemCopy函数进行复制。

MemCopy(&Flash28_API_LoadStart, &Flash28_API_LoadEnd, &Flash28_API_RunStart); 

MemCopy函数在DSP2833x.MemCopy.c中,该函数也可以用于其他需要从Flash复制至SARAM中运行的情况。

void MemCopy(Uint16 *SourceAddr, Uint16* SourceEndAddr, Uint16* DestAddr)
{
    while(SourceAddr < SourceEndAddr)
    { 
       *DestAddr++ = *SourceAddr++;
    }
    return;

完成上述两步后,Flash_API中的函数就被复制至SARAM中了。

3、初始化Flash_API时钟分频系数

Flash_API函数中有许多特定的延时,因此正确地配置Flash_API的时钟非常重要,将下列语句加入至已有工程的主函数中能够实现Flash_API的分频,该语句左右两边的变量在Flash2833x_API_Library.h 和 Flash2833x_API_Config.h中已经被定义。

EALLOW;
   Flash_CPUScaleFactor = SCALE_FACTOR;
EDIS;

4、初始化回调函数指针

回调函数指针能够在Flash_API执行擦除、读写操作的同时安全地执行某个函数。要执行回调函数,必须首先初始化函数指针Flash_CallbackPtr。如果不打算使用回调函数指针,则将指针设置为NULL,在这里我不使用回调函数指针。(写为NULL时需要include <stdio.h>)

#include <stdio.h>   
Flash_CallbackPtr = NULL;

5、禁止全局中断、禁止看门狗

在使用Flash_API之前,有以下注意事项:

(1)  由于Flash_API中的函数有严格的时序要求,在使用Flash_API中的函数时,必须关闭全局中断与看门狗,以免打乱时序。在进行擦除、读写操作之前,使用DINT语句关闭全局中断,在操作完成后再使用EINT语句开启中断,DINT与EINT在DSP2833x_Device.h中已被定义。

(2)  同一时刻只能调用一个Flash_API函数,例如:首先调用擦除Flash函数,在擦除完成之前,不能直接调用写入Flash的函数。

(3)  代码安全模块(CSM)保护F2833x的Flash、ROM、SARAM。若Flash_API在非安全存储区调用,则调用前应先给CSM进行解锁。

三、常用Flash_API函数及使用方式

1、Flash_ToggleTest

       该函数用于测试Flash_API时钟配置是否正确,若系统时钟与Flash_API的分频系数都正确配置,则该函数能够在指定引脚生成一个10kHz的脉冲方波。

extern  void  Flash_ToggleTest(volatile Uint32 *ToggleReg, Uint32 Mask);

其中*ToggleReg表示指定GPIO的指针,Mask为指定GPIO的掩码值,在进行ToogleTest前,需要先将目标GPIO进行初始化,并配置为GPIO输出模式。

下面以GPIO32为例进行ToogleTest。


GpioCtrlRegs.GPBMUX1.bit.GPIO32 = 0;     //设置GPIO32为通用I/O口 
GpioCtrlRegs.GPBDIR.bit.GPIO32 = 1;      //设置GPIO32为输出引脚

Flash_ToggleTest((volatile Uint32*)0x00006FCE,(Uint32)0x00000001);//GPIO32的ToggleTest

若生成脉冲方波频率不为10kHz,则时钟配置有误,需仔细检查。

2、Flash_Erase

该函数用于指定区域Flash的擦除,擦除操作执行后返回一个状态值。

extern Uint16  Flash_Erase(Uint16 SectorMask, FLASH_ST *FEraseStat);

其中SectorMask为指定擦除的扇区。28335片内Flash分为A~H共8个扇区,其中B扇区被分配至数据空间,用来存放数据,其余扇区被分配至程序空间,用来存放程序,我们进行数据擦写时一般在B扇区中进行,其地址范围为0x330000~0x337FFF,共32K*16位。*FEraseStat为结构体指针,如果擦除过程失败,API会把错误信息存储在这个指针所指的结构体变量。

下为FLASH_ST结构体定义,该定义在Flash2833x_API_Library.h中。

typedef struct {
    Uint32  FirstFailAddr;
    Uint16  ExpectedData;
    Uint16  ActualData;
}FLASH_ST;

另外,Flash2833x_API_Library.h还对扇区进行了定义,在使用时只需要将指定扇区写入即可。

#define SECTORA   (Uint16)0x0001
#define SECTORB   (Uint16)0x0002
#define SECTORC   (Uint16)0x0004
#define SECTORD   (Uint16)0x0008
#define SECTORE   (Uint16)0x0010
#define SECTORF   (Uint16)0x0020
#define SECTORG   (Uint16)0x0040
#define SECTORH   (Uint16)0x0080

下面以擦除B扇区为例进行Flash_Erase的应用

FLASH_ST EraseStatus;
             
Erase_Status = Flash_Erase(SECTORB,&EraseStatus);//擦除Flash的SectorB

若Erase_Status返回值为0,则擦除成功。

3、Flash_Program

该函数用于Flash的写入,操作执行后返回一个状态值。

extern Uint16  Flash_Program(Uint16 *FlashAddr, Uint16 *BufAddr, Uint32 Length, FLASH_ST *FProgStatus);

其中*FlashAddr为要写入Flash的地址位置,例如扇区B则为0x330000~0x337FFF,*BufAddr为要写入的数组的指针,Length为写入数组的长度,*FProgStatus为结构体指针,如果写入过程失败,API会把错误信息存储在这个指针所指的结构体变量。

下面进行写操作的应用,在扇区B的头部写入一个数组。

FLASH_ST ProgStatus;
Uint16 Buffer[4] = {1,2,3,4};
Program_Status = Flash_Program((Uint16*)0x330000,Buffer,sizeof(Buffer),&ProgStatus);

若Program_Status返回值为0,则写入成功。

4、Flash_Verify

该函数用于Flash的校验,将一定地址中Flash的数据与指定数组进行比较,若两者相同则返回状态值0,若两者不同则返回状态值40。

extern Uint16  Flash_Verify(Uint16 *StartAddr, Uint16 *BufAddr, Uint32 Length, FLASH_ST *FVerifyStat);

其中*StartAddr为要比对的Flash地址的开始,其余与Flash_Program相同,不再赘述。

下面进行校验操作的应用,将给定数组与B扇区头部的数据进行比较。

Uint16 Buffer1[4] = {1,2,3,4};
Verify_Status = Flash_Verify((Uint16*)0x330000,Buffer1,sizeof(Buffer1),&VerifyStat);

若Verify_Status返回值为0则Flash中的数据为{1,2,3,4 },若返回值为40则不是。

5、函数返回值

返回值表示各函数执行的状态,其具体的取值定义在Flash2833x_API_Library.h中,如下:

/*---------------------------------------------------------------------------
 API Status Messages

 The following status values are returned from the API to the calling
 program.  These can be used to determine if the API function passed
 or failed.  
---------------------------------------------------------------------------*/
 // Operation passed, no errors were flagged
#define STATUS_SUCCESS                        0   

// The CSM is preventing the function from performing its operation
#define STATUS_FAIL_CSM_LOCKED               10

// Device REVID does not match that required by the API
#define STATUS_FAIL_REVID_INVALID            11
    
// Invalid address passed to the API
#define STATUS_FAIL_ADDR_INVALID             12

// Incorrect PARTID
// For example the F2806 API was used on a F2808 device. 
#define STATUS_FAIL_INCORRECT_PARTID         13

// API/Silicon missmatch.  An old version of the
// API is being used on silicon it is not valid for
// Please update to the latest API. 
#define STATUS_FAIL_API_SILICON_MISMATCH     14

// ---- Erase Specific errors ---- 
#define STATUS_FAIL_NO_SECTOR_SPECIFIED      20
#define STATUS_FAIL_PRECONDITION             21
#define STATUS_FAIL_ERASE                    22
#define STATUS_FAIL_COMPACT                  23
#define STATUS_FAIL_PRECOMPACT               24

// ---- Program Specific errors ----  
#define STATUS_FAIL_PROGRAM                  30
#define STATUS_FAIL_ZERO_BIT_ERROR           31

// ---- Verify Specific errors ----
#define STATUS_FAIL_VERIFY                   40

// Busy is set by each API function before it determines
// a pass or fail condition for that operation.  
// The calling function will will not receive this 
// status condition back from the API
#define STATUS_BUSY                999    

其中擦除、写入和校验的状态值定义如下

Program时的报错返回值较为常见,对返回值30、31进行详解:

返回值为30:每次操作写入16位的字节,直到Buffer中的数据全部写入,若写入的数据位超过16位时则返回值30。

返回值为31:表示尝试将已有数据位上的0写成1。一般情况下,在Program操作之前,会将指定部分中的数据进行擦除;但擦除函数执行时间较久,且只能以扇区为单位进行擦除,因此也可以在已有数据的基础上再进行Program,但无法将已有数据的位中的0值写成1。

例如:Program写入0x0001,然后尝试对相同的位置Program写入0x0002,则返回值31。因为单个比特不可以从0写成1;Program写入0xFFFF,然后对相同位置Program写入0xFFFC,则成功。

另外,28335的Flash_API中似乎没有直接将Flash中的数据读取出来的函数,因此在需要读取时,只能采用Verify函数将Flash中的数据与已有数组进行比较,比较返回值为0则Flash中的数据为该数组中的数据。

参考:TI官网Flash2833x_API_Readme

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐