将Flash_API例程移植到程序代码流程及遇到的问题

这是一个延续了很久的工作,老头和我说的时候是去年年底,然后学习了一通ecc的内容,接着小论文回复评审意见然后放寒假就一直没有再动手。到了今年年初给他准备国自然的一些东西,然后断断续续又看了两天,接着学校给了毕业安排,就又开始写毕业论文,做ppt,然后修改修改修改,准备预答辩...中途也确实没有什么时间来处理ecc的问题。这几天正好师弟来北京准备中期答辩,我对于他项目的代码并不了解,然后两个人碰了一下,今天把这个事情完成了。也算研究生阶段做的最后一件和科研相关的事情了。

一、回顾TI提供的FLASH_API例程中代码的含义

1. 首先是引用使用FLASH_API需要的头文件

#include "driverlib.h"
#include "device.h"

// Include Flash API include file
#include "F021_F28004x_C28x.h"


// Include Flash API example header file
#include "flash_programming_f28004x.h"

2.定义一些后续会用到的常量、变量以及函数

// Length (in 16-bit words) of data buffer used for program
#define  WORDS_IN_FLASH_BUFFER    0xFF

//Data Buffers used for program operation using the flash API program function
#pragma  DATA_SECTION(Buffer,"DataBufferSection");
uint16   Buffer[WORDS_IN_FLASH_BUFFER + 1];
uint32   *Buffer32 = (uint32 *)Buffer;

// Prototype of the functions used in this example
void Example_Error(Fapi_StatusType status);
void Example_Done(void);
void Example_CallFlashAPI(void);
void FMSTAT_Fail(void);

3. 介绍main函数中完成flash的一系列操作:

3.1进行一系列的初始化操作,包括时钟、GPIO管脚、中断等等:


    // Initialize device clock and peripherals
	// Copy the Flash initialization code from Flash to RAM
    // Copy the Flash API from Flash to RAM
    // Configure Flash wait-states, fall back power mode, performance features and ECC
    Device_init();

    // Initialize GPIO
    Device_initGPIO();

    // Initialize PIE and clear PIE registers. Disables CPU interrupts.
    Interrupt_initModule();


    // Initialize the PIE vector table with pointers to the shell Interrupt
    // Service Routines (ISR).
    Interrupt_initVectorTable();

    // Enable Global Interrupt (INTM) and realtime interrupt (DBGM)
    EINT;
    ERTM;

3.2 进入Example_CallFlashAPI函数:

3.2.1 完成Flash的初始化:

使用Example_Error()函数的定义如下:

extern Fapi_StatusType Fapi_initializeAPI(
Fapi_FmcRegistersType *poFlashControlRegister, uint32 u32HclkFrequency);

可以参考例程中给出的方法使用它完成初始化工作,下面的if函数是用来判断是否正确完成了对FLASH进行的操作。这部分内容在加入到程序中后可以作为调试程序的参考节点。

    // Initialize the Flash API by providing the Flash register base address
    // and operating frequency.
    // This function is required to initialize the Flash API based on System frequency
    // before any other Flash API operation can be performed.
    // This function must also be called whenever System frequency or RWAIT is changed.
    oReturnCheck = Fapi_initializeAPI(F021_CPU0_BASE_ADDRESS, 100);

    if(oReturnCheck != Fapi_Status_Success)
    {
        // Check Flash API documentation for possible errors
        Example_Error(oReturnCheck);
    }

3.2.2 选择FLASH的扇区

使用的驱动板为F280049C,它的Flash被分为了bank0和bank1两部分,这两部分在使用时只能二选一,因此在对flash进行操作前需要明确所使用的bank是哪一个。在TI提供的例程中使用 Fapi_setActiveFlashBank来完成操作。

对于F280049C这个函数可以写入bank0和bank1两个参数,它的定义如下:extern Fapi_StatusType Fapi_setActiveFlashBank(Fapi_FlashBankType oFlashBank);

 // Initialize the Flash banks and FMC for erase and program operations.
    // Fapi_setActiveFlashBank() function sets the Flash banks and FMC for further
    // Flash operations to be performed on the banks.

    oReturnCheck = Fapi_setActiveFlashBank(Fapi_FlashBank0);

    if(oReturnCheck != Fapi_Status_Success)
    {
        // Check Flash API documentation for possible errors
        Example_Error(oReturnCheck);
    }

同样在完成操作后,通过oReturnCheck返回的数值来判断该操作是否正常进行。 

3.2.3 对FLASH特定扇区进行操作。

对一块FLASH扇区操作需要完成:擦除——写入——验证三个阶段。这三个步骤缺一不可,尤其是擦除的操作,即便flash中没有写入数据,也需要先进行擦除再写入数据,要不然会报错!

首先是对flash特定扇区中的数据进行擦除:

 // Erase Flash Bank0 sector6
    oReturnCheck = Fapi_issueAsyncCommandWithAddress(Fapi_EraseSector,
                                        (uint32 *)Bzero_Sector6_start);

    // Wait until FSM is done with erase sector operation
    while (Fapi_checkFsmForReady() != Fapi_Status_FsmReady){}

	if(oReturnCheck != Fapi_Status_Success)
	{
		// Check Flash API documentation for possible errors
		Example_Error(oReturnCheck);
	}

其中的while循环是在等待这个操作完成,才进行检验。

接下来是写入数据+验证,这段代码比较长,把它分成了几个部分来详细的解释一下,这个地方确实也是后面移植代码最需要理解的部分。因为example没有特定的意义,我们只是在这里定义了一个长度为0xff的数组,在里面写入数据,按照这个for循环的内容,Buffer数组中应该写入了0-255总共256个数据。

 for(i=0; i <= WORDS_IN_FLASH_BUFFER; i++)
    {
        Buffer[i] = i;
    }

然后使用FLASH_API中提供的Fapi_issueProgrammingCommand函数来完成写入数据的工作,首先看下对函数的定义能够帮助我们更好的理解这个函数的使用:

extern Fapi_StatusType Fapi_issueProgrammingCommand(
                                            uint32 *pu32StartAddress,
                                            uint16 *pu16DataBuffer,
                                            uint16  u16DataBufferSizeInWords,
                                            uint16 *pu16EccBuffer,
                                            uint16  u16EccBufferSizeInBytes,
                                            Fapi_FlashProgrammingCommandsType oMode
                                            );

使用这个函数需要写入6个变量,其中第六项mode的设定有4中选项:

typedef enum
{
   Fapi_AutoEccGeneration, /* This is the default mode for the command and will auto generate the ecc for the provided data buffer */
   Fapi_DataOnly,       /* Command will only process the data buffer */
   Fapi_EccOnly,        /* Command will only process the ecc buffer */
   Fapi_DataAndEcc         /* Command will process data and ecc buffers */
}  ATTRIBUTE_PACKED Fapi_FlashProgrammingCommandsType;

 一般使用Fapi_AutoEccGeneration自动生成ecc的数据,这是最常见的方式,手册和例程中也都使用了这种方式。在这种方式下不需要手动定义EccBuffer的相关内容,也就是说uint16 *pu16EccBuffer, uint16  u16EccBufferSizeInBytes,这两项参数直接写0即可。

例程中给出了一段对ECC操作的简单描述:

A data buffer of max 8 16-bit words can be supplied to the program function.
Each word is programmed until the whole buffer is programmed or a problem is found. However to program a buffer that has more than 8 words, program function can be called in a loop to program 8 words for each loop iteration until the whole buffer is programmed.
Remember that the main array flash programming must be aligned to 64-bit address boundaries and each 64 bit word may only be programmed once per write/erase cycle.  Meaning the length of the data buffer (3rd parameter for Fapi_issueProgrammingCommand() function) passed to the program function can only be either 4 or 8.
标红的位置大概意思是说: 1. 这个函数每次只能写入8个字,超过八个字要使用循环,每次循环写入八个字来实现更多内容的存储。2. 主阵列闪存编程必须与 64 位地址边界对齐,并且每个 64 位字在每个写入/擦除周期只能编程一次,这也就意味着第三个参数只能是4或者8。

例程中给的就是一次写入八个字,然后使用循环来完成256个字的写入:

    for(i=0, u32Index = Bzero_Sector6_start;
       (u32Index < (Bzero_Sector6_start + WORDS_IN_FLASH_BUFFER)) &&
       (oReturnCheck == Fapi_Status_Success); i+= 8, u32Index+= 8)
    {
		oReturnCheck = Fapi_issueProgrammingCommand((uint32 *)u32Index, Buffer+i, 8,
																				 0, 0, Fapi_AutoEccGeneration);

		// Wait until the Flash program operation is over
		while(Fapi_checkFsmForReady() == Fapi_Status_FsmBusy);

		if(oReturnCheck != Fapi_Status_Success)
		{
			// Check Flash API documentation for possible errors
			Example_Error(oReturnCheck);
		}

		// Read FMSTAT register contents to know the status of FSM after
		// program command to see if there are any program operation related errors
		oFlashStatus = Fapi_getFsmStatus();
		if(oFlashStatus != 0)
		{
			//Check FMSTAT and debug accordingly
			FMSTAT_Fail();
		}

		// Verify the programmed values.  Check for any ECC errors.
		// The program command itself does a verify as it goes.
		// Hence program verify by CPU reads (Fapi_doVerify()) is optional.
        oReturnCheck = Fapi_doVerify((uint32 *)u32Index,
                                     4, Buffer32+(i/2),
                                     &oFlashStatusWord);

		if(oReturnCheck != Fapi_Status_Success)
		{
			// Check Flash API documentation for possible errors
			Example_Error(oReturnCheck);
		}
    }

最后使用Fapi_doVerify函数来验证写入的数据是否正确,然后再根据关键字返回值来判断操作。

二、根据程序进行移植

大体介绍完例程的流程后,我们首先需要找到程序里面要写入flash的内容是什么,也就是哪部分内容需要进行ecc效验。对于我们的项目,需要保证数据在某些环境下的准确性,因此需要找到程序中的data部分,对这部分内容进行效验。

1. 怎么在代码中确定各个部分的存储位置?

主要有两个方法:

View——Memory Allocation里面直接找.data的存储位置:

找到project中debug文件夹下的.map文件(更推荐这种),有时候这个文件夹里面可能存在不止一个.map文件,这里一定要找到和我们程序名字对应的.map文件:

找到.map后在代码中可以找到具体的地址:

.data      1    0000aaaa    00000135     UNINITIALIZED
                  0000aaaa    00000078     rts2800_fpu32_eabi.lib : defs.c.obj (.data:_ftable)
                  0000ab22    00000048                            : host_device.c.obj (.data:_device)
                  0000ab6a    0000000c     Global_Variable.obj (.data:ePWM)
                  0000ab76    00000006     rts2800_fpu32_eabi.lib : exit.c.obj (.data)
                  0000ab7c    00000003     UsedInterrupt.obj (.data)
                  0000ab7f    00000001     SFO_v8_fpu_lib_build_c28_driverlib_eabi.lib : SFO_v8_fpu_lib_build_c28_driverlib.obj (.data)
                  0000ab80    0000002c     Global_Variable.obj (.data)
                  0000abac    00000028     rts2800_fpu32_eabi.lib : host_device.c.obj (.data:_stream)
                  0000abd4    00000002                            : _lock.c.obj (.data:_lock)
                  0000abd6    00000002                            : _lock.c.obj (.data:_unlock)
                  0000abd8    00000002     main.obj (.data)
                  0000abda    00000001     rts2800_fpu32_eabi.lib : defs.c.obj (.data)
                  0000abdb    00000001                            : errno.c.obj (.data)
                  0000abdc    00000001                            : fopen.c.obj (.data)
                  0000abdd    00000001                            : memory.c.obj (.data)
                  0000abde    00000001                            : open.c.obj (.data)

这也就确定了我们要将0x0000AAAA到0x0000ABDE处的数据写入到FLASH的某个扇区中。

2.选择一个合适的扇区,flash的bank0中提供了sector0-sector15这16个扇区,根据Memory Allocation的内存分配结果,我们要优先选择没有被占用的内存。

根据内存分配的结果,我最终选择了SEC6用于操作。

3. 按照例程的操作顺序来移植代码

初始化、定义操作区域的代码没有区别可以直接照搬,为了方便后面的调试,我添加了printf语句来显示代码进行到了哪个步骤:

Fapi_StatusType  oReturnCheck;
    Fapi_FlashStatusType  oFlashStatus;
    Fapi_FlashStatusWordType  oFlashStatusWord;
    uint16 i=0;
    uint32 u32Index = 0;

    oReturnCheck = Fapi_initializeAPI(F021_CPU0_BASE_ADDRESS, 100);

    if(oReturnCheck != Fapi_Status_Success)
    {
        // Check Flash API documentation for possible errors
        Example_Error(oReturnCheck);
    }
    else
    {
        printf("初始化成功\n");
    }


    oReturnCheck = Fapi_setActiveFlashBank(Fapi_FlashBank0);
    if(oReturnCheck != Fapi_Status_Success)
    {
        // Check Flash API documentation for possible errors
        Example_Error(oReturnCheck);
    }
    else
    {
            printf("Bank0成功激活\n");
    }

    // Erase Flash Bank0 sector6
    oReturnCheck = Fapi_issueAsyncCommandWithAddress(Fapi_EraseSector,
                                        (uint32 *)Bzero_Sector6_start);

    // Wait until FSM is done with erase sector operation
    while (Fapi_checkFsmForReady() != Fapi_Status_FsmReady){}

    if(oReturnCheck != Fapi_Status_Success)
    {
        // Check Flash API documentation for possible errors
        Example_Error(oReturnCheck);
    }
    else
    {
            printf("擦除扇区数据...\n");
    }

    oFlashStatus = Fapi_getFsmStatus();
    if(oFlashStatus != 0)
    {
        FMSTAT_Fail();
    }

    oReturnCheck = Fapi_doBlankCheck((uint32 *)Bzero_Sector6_start,
                   Sector8KB_u32length,
                   &oFlashStatusWord);

    if(oReturnCheck != Fapi_Status_Success)
    {
        // Check Flash API documentation for error info
        Example_Error(oReturnCheck);
    }
    else
    {
            printf("数据成功擦除\n");
    }

下面是写入数据的内容,我们要将0x0000AAAA处的数据写入到FLASH中来进行ecc效验,首先需要将这些数据送入缓冲区Buffer内,根据例程的操作,需要定义一些参数:

#define  WORDS_IN_FLASH_BUFFER    0xFF
uint16_t Buffer[WORDS_IN_FLASH_BUFFER + 1];
volatile uint16_t *data_ptr = (volatile uint16_t *)0x0000AAAA;
#define  DATA_SIZE_BYTES          0x0133
#define  DATA_SIZE_WORDS          (DATA_SIZE_BYTES+1)/2

将data的数据放入缓冲区内,这里也是使用了一个printf函数来方便查看程序的运行情况: 

   for (i = 0; i < DATA_SIZE_WORDS && i <= WORDS_IN_FLASH_BUFFER; i++) {
        Buffer[i] = data_ptr[i];
        printf("data_ptr addr = 0x%p, value addr = 0x%04X, value buff = 0x%04X\n", data_ptr + i, data_ptr[i], Buffer[i]);
    }
    printf("写入完成\n");

 然后仿照例程将buffer的内容写入sector6:

    for (i=0, u32Index = Bzero_Sector6_start;
        (u32Index < (Bzero_Sector6_start + WORDS_IN_FLASH_BUFFER)) &&
        (oReturnCheck == Fapi_Status_Success); i+= 8, u32Index+= 8)
    {
        //只能分块写入
            oReturnCheck = Fapi_issueProgrammingCommand((uint32 *)u32Index, Buffer+i, 8,
                                                        0, 0, Fapi_AutoEccGeneration);
            while(Fapi_checkFsmForReady() == Fapi_Status_FsmBusy);
    }

    if(oReturnCheck != Fapi_Status_Success)
    {
        // Check Flash API documentation for possible errors
        printf("Flash 写入失败,返回状态码 = %d\n", oReturnCheck);
        Example_Error(oReturnCheck);
    }
    else
    {
        printf("data字段成功写入\n");
    }

最后整体的函数如下:

void Example_CallFlashAPI(void)
{
    Fapi_StatusType  oReturnCheck;
    Fapi_FlashStatusType  oFlashStatus;
    Fapi_FlashStatusWordType  oFlashStatusWord;
    uint16 i=0;
    uint32 u32Index = 0;

    oReturnCheck = Fapi_initializeAPI(F021_CPU0_BASE_ADDRESS, 100);

    if(oReturnCheck != Fapi_Status_Success)
    {
        // Check Flash API documentation for possible errors
        Example_Error(oReturnCheck);
    }
    else
    {
        printf("初始化成功\n");
    }


    oReturnCheck = Fapi_setActiveFlashBank(Fapi_FlashBank0);
    if(oReturnCheck != Fapi_Status_Success)
    {
        // Check Flash API documentation for possible errors
        Example_Error(oReturnCheck);
    }
    else
    {
            printf("Bank0成功激活\n");
    }

    // Erase Flash Bank0 sector6
    oReturnCheck = Fapi_issueAsyncCommandWithAddress(Fapi_EraseSector,
                                        (uint32 *)Bzero_Sector6_start);

    // Wait until FSM is done with erase sector operation
    while (Fapi_checkFsmForReady() != Fapi_Status_FsmReady){}

    if(oReturnCheck != Fapi_Status_Success)
    {
        // Check Flash API documentation for possible errors
        Example_Error(oReturnCheck);
    }
    else
    {
            printf("擦除扇区数据...\n");
    }

    oFlashStatus = Fapi_getFsmStatus();
    if(oFlashStatus != 0)
    {
        FMSTAT_Fail();
    }

    oReturnCheck = Fapi_doBlankCheck((uint32 *)Bzero_Sector6_start,
                   Sector8KB_u32length,
                   &oFlashStatusWord);

    if(oReturnCheck != Fapi_Status_Success)
    {
        // Check Flash API documentation for error info
        Example_Error(oReturnCheck);
    }
    else
    {
            printf("数据成功擦除\n");
    }

    for (i = 0; i < DATA_SIZE_WORDS && i <= WORDS_IN_FLASH_BUFFER; i++) {
        Buffer[i] = data_ptr[i];
        printf("data_ptr addr = 0x%p, value addr = 0x%04X, value buff = 0x%04X\n", data_ptr + i, data_ptr[i], Buffer[i]);
    }
    printf("写入完成\n");

    for (i=0, u32Index = Bzero_Sector6_start;
        (u32Index < (Bzero_Sector6_start + WORDS_IN_FLASH_BUFFER)) &&
        (oReturnCheck == Fapi_Status_Success); i+= 8, u32Index+= 8)
    {
        //只能分块写入
            oReturnCheck = Fapi_issueProgrammingCommand((uint32 *)u32Index, Buffer+i, 8,
                                                        0, 0, Fapi_AutoEccGeneration);
            while(Fapi_checkFsmForReady() == Fapi_Status_FsmBusy);
    }

    if(oReturnCheck != Fapi_Status_Success)
    {
        // Check Flash API documentation for possible errors
        printf("Flash 写入失败,返回状态码 = %d\n", oReturnCheck);
        Example_Error(oReturnCheck);
    }
    else
    {
        printf("data字段成功写入\n");
    }
  // 可选:进一步 FSM 状态确认
    oFlashStatus = Fapi_getFsmStatus();
    if (oFlashStatus != 0)
    {
        FMSTAT_Fail();
    }

    Example_Done();
}

void Example_Error(Fapi_StatusType status)
{
    // 记录错误、重启、报警等
        printf("Flash Error! Status = %d\n", status);
        while(1);  // 或触发软复位、CAN上报等
}

void Example_Done(void)
{
    printf("Flash ECC效验完成\n");
}

void FMSTAT_Fail(void)
{
        printf("FMSTAT Flash 状态异常!\n");
        while(1);
}

 三、移植过程中遇到的问题

1. 在例程中使用的flash_programming_f28004x.h是之前程序里面没有的,在将它复制到include文件夹后,一定要记得在project中将include重新添加进路径,要不然程序无法识别flash函数中使用的一些参数。

添加路径的方法是右键项目选择Propertise——C2000 Compiler——Include Options,选择右上角处绿色的带有加号的图标,然后再工作区选择include文件夹,把他重新添加一下:

 2. 编译没有问题后,进行烧写,但是一到数据写入就出现报错,由于函数的引用没有问题,分析后认为这是因为内存的分配发生了冲突所导致的。再和例程对比后发现我没有定义:#pragma DATA_SECTION(Buffer, "DataBufferSection")

这行代码是帮助我们将Buffer定义到特定的位置,而不是随意去安排它的位置。flash要求数据必须从RAM中读取,如果随机取安排buffer的位置有可能会被写入放有.bss或.data的区域内,程序会出现错误。此时也需要再cmd文件中添加下面的代码:DataBufferSection : > RAMM0, PAGE = 0, ALIGN(4),来讲buffer定义在一个‘与世无争’RAM中。

3. 烧录后我又发现了问题,写入buffer的数据会突然全部变成32.这个问题困惑了我很久,知道我发现删除掉printf函数就可以正确的把data中的数值写入到buffer中,我认为这是由于printf函数再分配内存时影响到了buffer。我求助gpt后它给我的答案是:

printf() 在 F28004x 等 C2000 芯片中使用了内部 stdio 缓冲区,该缓冲区可能被默认放到了你程序中紧邻 Buffer[] 的区域,尤其当你用了 RAMM0这样小的 RAM 区。它覆盖了 Buffer[] 的内容,导致你看到了全部 0x32 的数据

根据它的建议,我将RAMM0修改为了RAMLS4,相应的cmd修改为:DataBufferSection : > RAMM0, PAGE = 0, ALIGN(4)。修改后再添加printf函数也不会对buffer的数据造成影响。

以上就是今天的全部内容,后续的一些细节还需要自己再进行梳理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值