This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

TMS320F28075: issue of writing data to USB flash disk

Part Number: TMS320F28075
Other Parts Discussed in Thread: C2000WARE

Hi Expert,

 

My customer are developing USB of F28027 function to store the data in USB flash disk, it seem we did not have example to write data to USB flash disk, so they modify the example code usb_ex7_host_msc.c from C2000ware to implement this function.

 They will read ADC value, and use an array to store these value, which is 32 bytes long, for each 1ms, they will write these 32byte data into USB flash disk by using f_write, The data can be written successfully, but they face the issue that it will take too long time to call the function "USBHCDPipeRead" after running the code tens of seconds. They use time to record the time and find that it will take 200ms~2s to call this function "USBHCDPipeRead" , which is so long time that buffer data in RAM that require to write into the USB flash disk will be overwritten because they need to write 32 bytes for each 1ms.

 The sequence of the function they call are below:

f_write àdisk_writeàUSBHMSCBlockWriteà USBHSCSIWrite10à USBHSCSISendCommandà USBHCDPipeWriteàUSBHCDPipeRead

 The issue is why it will take so long time to call "USBHCDPipeRead" function? Attached are the code that customer do the modification based on the example usb_ex7_host_msc.

Any problem for the code or any example to write data to USB flash disk that we verified successfully that could be shared to customer?

//##############################################################################
//
// FILE:   usb_ex7_host_msc.c
//
// TITLE:  Main routines for the USB Host MSC example.
//
//! \addtogroup driver_example_list
//! <h1>USB Mass Storage Class Host </h1>
//!
//! This example application demonstrates reading a file system from a USB mass
//! storage class device.  It makes use of FatFs, a FAT file system driver.  It
//! provides a simple command console via the SCI for issuing commands to view
//! and navigate the file system on the mass storage device.
//!
//! The first SCI, which is connected to the FTDI virtual serial port on the
//! controlCARD board, is configured for 115200 bits per second, and 8-N-1
//! mode.  When the program is started a message will be printed to the
//! terminal.  Type ``help'' for command help.
//!
//! After loading and running the example, open a serial terminal with the
//! above settings to open the command prompt.  Then connect a USB MSC device
//! to the microUSB port on the top of the controlCARD.
//!
//! For additional details about FatFs, see the following site:
//! http://elm-chan.org/fsw/ff/00index_e.html
//!
//
//##############################################################################
// $TI Release: F2807x Support Library v3.11.00.00 $
// $Release Date: Sun Oct  4 15:52:19 IST 2020 $
// $Copyright:
// Copyright (C) 2014-2020 Texas Instruments Incorporated - http://www.ti.com/
//
// Redistribution and use in source and binary forms, with or without 
// modification, are permitted provided that the following conditions 
// are met:
// 
//   Redistributions of source code must retain the above copyright 
//   notice, this list of conditions and the following disclaimer.
// 
//   Redistributions in binary form must reproduce the above copyright
//   notice, this list of conditions and the following disclaimer in the 
//   documentation and/or other materials provided with the   
//   distribution.
// 
//   Neither the name of Texas Instruments Incorporated nor the names of
//   its contributors may be used to endorse or promote products derived
//   from this software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// $
//##############################################################################

//
// Included Files
//
#include "driverlib.h"
#include "device.h"
#include <string.h>
#include "usb_hal.h"
#include "usblib.h"
#include "usbmsc.h"
#include "host/usbhost.h"
#include "host/usbhmsc.h"
#include "cmdline.h"
#include "scistdio.h"
#include "fatfs/src/ff.h"
//
// Globals
//
uint32_t printfflag = 0;
uint16_t cpuTimer0IntCount;
extern uint32_t RW10_flag;
//uint16_t cpuTimer1IntCount;
//uint16_t cpuTimer2IntCount;

//******************************************************************************
//
// Defines the size of the buffers that hold the path, or temporary data from
// the memory card.  There are two buffers allocated of this size.  The buffer
// size must be large enough to hold the longest expected full path name,
// including the file name, and a trailing null character.
//
//******************************************************************************
#define PATH_BUF_SIZE   80

//******************************************************************************
//
// Defines the size of the buffer that holds the command line.
//
//******************************************************************************
#define CMD_BUF_SIZE    64

//******************************************************************************
//
// This buffer holds the full path to the current working directory.  Initially
// it is root ("/").
//
//******************************************************************************
static char g_cCwdBuf[PATH_BUF_SIZE] = "/";

//******************************************************************************
//
// A temporary data buffer used when manipulating file paths, or reading data
// from the memory card.
//
//******************************************************************************
static char g_cTmpBuf[PATH_BUF_SIZE];

//******************************************************************************
//
// The buffer that holds the command line.
//
//******************************************************************************
static char g_cCmdBuf[CMD_BUF_SIZE];

//******************************************************************************
//
// Current FAT fs state.
//
//******************************************************************************
static FATFS g_sFatFs;
static DIR g_sDirObject;
static FILINFO g_sFileInfo;
static FIL g_sFileObject;

//******************************************************************************
//
// A structure that holds a mapping between an FRESULT numerical code,
// and a string representation.  FRESULT codes are returned from the FatFs
// FAT file system driver.
//
//******************************************************************************
typedef struct
{
    FRESULT fresult;
    char *pcResultStr;
}
tFresultString;

//******************************************************************************
//
// A macro to make it easy to add result codes to the table.
//
//******************************************************************************
#define FRESULT_ENTRY(f)        { (f), (#f) }

//******************************************************************************
//
// A table that holds a mapping between the numerical FRESULT code and
// it's name as a string.  This is used for looking up error codes for
// printing to the console.
//
//******************************************************************************
tFresultString g_sFresultStrings[] =
{
    FRESULT_ENTRY(FR_OK),
    FRESULT_ENTRY(FR_NOT_READY),
    FRESULT_ENTRY(FR_NO_FILE),
    FRESULT_ENTRY(FR_NO_PATH),
    FRESULT_ENTRY(FR_INVALID_NAME),
    FRESULT_ENTRY(FR_INVALID_DRIVE),
    FRESULT_ENTRY(FR_DENIED),
    FRESULT_ENTRY(FR_EXIST),
    FRESULT_ENTRY(FR_RW_ERROR),
    FRESULT_ENTRY(FR_WRITE_PROTECTED),
    FRESULT_ENTRY(FR_NOT_ENABLED),
    FRESULT_ENTRY(FR_NO_FILESYSTEM),
    FRESULT_ENTRY(FR_INVALID_OBJECT),
    FRESULT_ENTRY(FR_MKFS_ABORTED)
};

//******************************************************************************
//
// A macro that holds the number of result codes.
//
//******************************************************************************
#define NUM_FRESULT_CODES (sizeof(g_sFresultStrings) / sizeof(tFresultString))

//******************************************************************************
//
// The size of the host controller's memory pool in bytes.
//
//******************************************************************************
#define HCD_MEMORY_SIZE         128

//******************************************************************************
//
// The memory pool to provide to the Host controller driver.
//
//******************************************************************************
uint8_t g_pHCDPool[HCD_MEMORY_SIZE];

//******************************************************************************
//
// The instance data for the MSC driver.
//
//******************************************************************************
tUSBHMSCInstance *g_psMSCInstance = 0;

//******************************************************************************
//
// Declare the USB Events driver interface.
//
//******************************************************************************
DECLARE_EVENT_DRIVER(g_sUSBEventDriver, 0, 0, USBHCDEvents);

//******************************************************************************
//
// The global that holds all of the host drivers in use in the application.
// In this case, only the MSC class is loaded.
//
//******************************************************************************
static tUSBHostClassDriver const * const g_ppHostClassDrivers[] =
{
    &g_sUSBHostMSCClassDriver,
    &g_sUSBEventDriver
};

//******************************************************************************
//
// This global holds the number of class drivers in the g_ppHostClassDrivers
// list.
//
//******************************************************************************
#define NUM_CLASS_DRIVERS       (sizeof(g_ppHostClassDrivers)                 /\
                                 sizeof(g_ppHostClassDrivers[0]))

//******************************************************************************
//
// Hold the current state for the application.
//
//******************************************************************************
typedef enum
{
    //
    // No device is present.
    //
    STATE_NO_DEVICE,

    //
    // Mass storage device is being enumerated.
    //
    STATE_DEVICE_ENUM,

    //
    // Mass storage device is ready.
    //
    STATE_DEVICE_READY,

    //
    // An unsupported device has been attached.
    //
    STATE_UNKNOWN_DEVICE,

    //
    // A power fault has occurred.
    //
    STATE_POWER_FAULT
}
tState;
volatile tState g_eState;
volatile tState g_eUIState;

#define _C char
#define _UI uint16_t


#define setbit(x,y)  (x)|=(1<<(y))
#define clrbit(x,y)  (x)&=~(1<<(y))
#define getbit(x,y)  (((x)>>(y))&1)

// ����ʹ��bit�ķ�ʽ
#define setflag(x,y) setbit(x[y>>4], y&0xF)
#define clrflag(x,y) clrbit(x[y>>4], y&0xF)
#define getflag(x,y) getbit(x[y>>4], y&0xF)

#define USBWRITE 1

#if USBWRITE == 1
#pragma DATA_SECTION(usb_save_buff,"usb_save_ram");
_C usb_save_buff[0x3000];
//_C usb_save_buff[80];
typedef struct{
    _UI row;
    _UI line;
}usb_save_buff_arry;

const usb_save_buff_arry usb_save_buff_arry_1ms = {.row = 4, .line = 0x3000/4};
const usb_save_buff_arry usb_save_buff_arry_2ms = {.row = 8, .line = 0x3000/8};
const usb_save_buff_arry usb_save_buff_arry_4ms = {.row = 16, .line = 0x3000/16};

const usb_save_buff_arry* const usb_save_buff_arry_table[4] = {
    &usb_save_buff_arry_1ms, // 0-4
    &usb_save_buff_arry_2ms, // 5-8
    &usb_save_buff_arry_4ms, // 9-12
    &usb_save_buff_arry_4ms, // 12-16
};

const _C hex_ASCII[] = "0123456789ABCDEF";
const _C file_head[] = "GD350,USB,64,Hex,";
const _C file_name_0[] = "0000.csv";
const _C begin_ASCII[] = "[BEGIN]\r";
const _C end_ASCII[] = "[END]\r";

// һ���ֽ���16λ
// ���ݣ�һ���ֽڵ����ݣ���ASCII����ʾ��16����������Ҫ4���ֽڴ���
#define ASCII_DATA_BYTE (4*sizeof('F'))
// ����ѡ����ͨ����16��
#define CHANNEL_MAX 16
// д�����ݣ������ŷָ���ͻ��к�
#define WRITE_BUFF ((CHANNEL_MAX)*(ASCII_DATA_BYTE+sizeof(',')) + sizeof('\r'))

typedef struct{
    _UI enable:1; // ʹ��usb flash����
    _UI flash_ready:1;  // flash��ʼ�����
    _UI file_head_writed:1; // �ļ�̧ͷ�Ѿ�д�ã����Կ�ʼ��¼����
    _UI begin_write_data:1; // ��ʼ��¼����
    _UI reserve_1:4;

    //next 8bit hide for display
    _UI flash_inited:1; // ����ʼ��һ��usb�ı�־
    _UI file_opened:1; // �Ѿ����ļ�
    _UI data_head_writed:1; // �Ѿ�д������̧ͷ
    _UI disconnect_now:1; // ����
    _UI cancel_file_bak:1; // ��һ�ĵ���Ҫע���ļ���־������ֻע��һ���ļ�
    _UI reserve:3;
}usb_flash_flag;

// ͬ�ϣ�ָ�������ָ��
typedef struct test_cnt_type{
    uint32_t data_loss; // ����������µĶ�����
    _UI execute; // ����ִ�д�������������˱�ʾ���濨ס��
    _UI flash_disconnect; // ���usb�Ͽ��������������
    uint32_t write_cnt; // д�����ݼ���
    _UI write_less; // д�����ݲ��㣬���������������ˣ���Ҫ���⴦���ˣ����ڻ�û������
    _UI write_fail; // д��ʧ�ܣ����������Ҫ����usb��
    uint32_t sample_cnt; // ��������
    _UI time_flag;
    uint32_t time_tim[64]; // CPU-Timer Counter Registers
    uint32_t time_tim_min; // CPU-Timer Counter Registers
    uint32_t time_tim_max1; // CPU-Timer Counter Registers
    uint32_t time_tim_max; // CPU-Timer Counter Registers

}test_cnt;

typedef struct{
    _UI  sava_condition;   /*P90_02; u�������*/
    _UI  channel_num_max;  /*P90_03; д��ͨ������*/
    _UI  file_num_max;     /*P90_04; �������*/
    _UI  file_size_max;    /*P90_05; �������*/
    _UI  file_num;         /*P90_06; ��ǰ�ļ����*/
    _UI  file_size;        /*P90_07; ��ǰ�ļ����*/
}usb_flash_para;

typedef struct{
    FIL* object; // �����
    WORD write_byte; // �Ѿ�д�����
//  _UI save_line_num;
    _UI save_buff_num; // ��ǰ�洢������
    _UI write_buff_num; // ��ǰд�뻺����
    // �������Ƿ������ݵı�־����,һ������ռһ��bit����������������飬���Լ�¼���вű��һ�Σ���0x1800�Ļ���̫���ˣ�������������Ҳûʲô����
    _UI save_buff_flag[0x3000>>4];
    const usb_save_buff_arry* buff_arry; // ��������нṹ
    usb_flash_flag flag; // flag
    FRESULT file_result; // file�ļ������˳���������������ɹ��Ľ����0��ʾ�ɹ�
    usb_flash_para* para_p; // һ�㹦����ָ��
    test_cnt* cnt_p; // ����������ָ��
    _UI* channel_p; // ͨ��������ָ��
//  _C* write_begin_pointer; // һ��д��������ʱʹ��
    _C file_name[sizeof("0000.csv")]; // ���
    //FIL* object; // �����
    _C* save_buff; // ���ݻ���ָ�룬ʵ��������LSRAM����
    _C write_buff[WRITE_BUFF]; // Ҫд�����ݵĻ��棬����f_writeֱ��д
}usb_flash_type;

FIL file_obj;

test_cnt usb_test_cnt;
usb_flash_para usb_para;
usb_flash_type usb_flash;
usb_flash_type *p_usb_flash;
#endif

// ��ȡa�е�num��4bit�֣�num��0��ʼ������һ����תʮ�����Ƶ�ASCII��ʾ
#define get_hex(a, num) ((a>>4*num)&0xF)

// the source is string, not copy the end \0
#define STR_COPY(target, source) \
    memcpy(target, source, sizeof(source) - 1); \
    target += sizeof(source) - 1


// һ����תʮ�����Ƶ�ASCII��ʾ������ 0x1abc ת "1abc"����pointer���д4��������д����ָ��
_C* hex_to_ASCII(_C* pointer, _UI var)
{
    *pointer++ = hex_ASCII[get_hex(var, 3)];
    *pointer++ = hex_ASCII[get_hex(var, 2)];
    *pointer++ = hex_ASCII[get_hex(var, 1)];
    *pointer++ = hex_ASCII[get_hex(var, 0)];
    return pointer;
}

#if USBWRITE == 2

// д������̧ͷ��ÿ��ͨ����Ӧ��ͨ���ţ���������ʱ��
// ���ֻ���ڻ��ļ���ʱ��д�����Բ�����д��������л�ͨ��
context_type* write_data_head(usb_flash_type* p)
{
    if(p->flag.data_head_writed==0){
        int i;
        _C* addr_now = p->save_buff;
        _C* addr_begin;
        addr_begin = addr_now;

        STR_COPY(addr_now, begin_ASCII);
        addr_now = hex_to_ASCII(addr_now, (usb_clock_p->day<<8) + usb_clock_p->hour);
        addr_now = hex_to_ASCII(addr_now, (usb_clock_p->min<<8) + usb_clock_p->sec);
        *addr_now++ = ',';

        // ͨ�����תASCII
        // ���ж���ֵ��Ŀǰ��ûд
        for(i=0; i<p->para_p->channel_num_max; i++)
        {
            _UI channel = p->channel_p[i];
            *addr_now++ = hex_ASCII[get_hex(channel, 1)];
            *addr_now++ = hex_ASCII[get_hex(channel, 0)];
            *addr_now++ = ',';
        }
        *addr_now++ = '\r';
        STR_COPY(addr_now, end_ASCII);

        p->file_result = f_write(p->object, addr_begin, addr_now - addr_begin, &p->write_byte);
        p->flag.data_head_writed = p->file_result == FR_OK;
    }
    return context_p;
}
#endif

#if USBWRITE == 1
void time_recv0(void)
{
////    if(usb_flash.cnt_p->time_flag == 0)
////    {
//    CPUTimer_startTimer(CPUTIMER1_BASE);
////    usb_flash.cnt_p->time_flag = 1;
////    }
}
void time_recv1(void *p)
{
//    uint8_t i;
//    uint32_t time_tim;
//    usb_flash_type *p_usb_flash = (usb_flash_type *)p;
////    if(usb_flash.cnt_p->time_flag)
////    {
//        time_tim = 0xffffffff - CPUTimer_getTimerCount(CPUTIMER1_BASE);
//        if(time_tim <usb_flash.cnt_p->time_tim_min)
//            usb_flash.cnt_p->time_tim_min = time_tim;
//
//        if(time_tim > usb_flash.cnt_p->time_tim_max)
//            usb_flash.cnt_p->time_tim_max = time_tim;
//        else  if(time_tim > usb_flash.cnt_p->time_tim_max1)
//            usb_flash.cnt_p->time_tim_max1 = time_tim;
//
//        for(i = 1;i < 64;i++)
//        {
//            usb_flash.cnt_p->time_tim[i-1] = usb_flash.cnt_p->time_tim[i];
//        }
//        usb_flash.cnt_p->time_tim[63] = time_tim;
////        usb_flash.cnt_p->time_flag = 0;
////        if(time_tim > 480000)
////        {
////            SCIprintf("time1111\n");
////        }
//
//    CPUTimer_stopTimer(CPUTIMER1_BASE);
//    CPUTimer_reloadTimerCounter(CPUTIMER1_BASE);
////    }
}

void time_sample(void)
{
    cpuTimer0IntCount++;
    get_sample_data(p_usb_flash);

   if((cpuTimer0IntCount%1000) == 0)
   {
       printfflag++;
   }
   if(cpuTimer0IntCount == 65000)
   {
//       usb_flash.cnt_p->time_flag = 1;
       cpuTimer0IntCount = 0;
   }
}

void usb_init(void)
{
    p_usb_flash = &usb_flash;
    memset(&usb_flash, 0, sizeof(usb_flash));
    memset(&usb_para, 0, sizeof(usb_flash_para));
    memset(&usb_test_cnt, 0, sizeof(test_cnt));

    usb_test_cnt.time_tim_min = 0xffffffff;
    usb_test_cnt.time_tim_max = 0;
    usb_test_cnt.time_tim_max1 = 0;

    usb_para.sava_condition = 1;
    usb_para.channel_num_max = 16;
    usb_para.file_num_max = 16;
    usb_para.file_size_max = 1024;
    usb_para.file_num = 0;
    usb_para.file_size = 1024;

    usb_flash.cnt_p = &usb_test_cnt;
    usb_flash.para_p = &usb_para;
    usb_flash.object = &file_obj;
    usb_flash.save_buff = usb_save_buff;
    usb_flash.buff_arry = &usb_save_buff_arry_4ms;
    memcpy(usb_flash.file_name, file_name_0, sizeof(file_name_0));
}

// ��ɾ��ͬ���ļ����ٴ�����ֻд�����Ǵ���
uint8_t open_file(usb_flash_type* p)
{
    if(p->flag.file_opened==0){
        // �ļ���������0000��FFFF,��׺������
        hex_to_ASCII(p->file_name, p->para_p->file_num);
        f_unlink((const char*)p->file_name); // ��֮ǰ��ɾ��ԭ���ļ���������û�У����Բ���¼ʧ��
        p->file_result = f_open(p->object, (const char*)p->file_name, FA_OPEN_ALWAYS | FA_WRITE); // �������򿪣�д����
        if(p->file_result == FR_OK)
        {
            //p->file_result = f_lseek(p->object,p->object->fptr+p->object->fsize);
            //if(p->file_result == FR_OK)
            //{
                p->flag.file_opened = 1;
                return 1;
            //}
            //else return 0;
        }
        else return 0;
    }
    else return 1;
}

// д���ļ�̧ͷ
uint8_t write_file_head(usb_flash_type* p)
{
    if(p->flag.file_head_writed == 0){
        _C* addr_now = p->save_buff;
        _C* addr_begin;
        addr_begin = addr_now;
        STR_COPY(addr_now, file_head);
        STR_COPY(addr_now, p->file_name);
        *addr_now++ = '\r';

        p->file_result = f_write(p->object, addr_begin, addr_now - addr_begin, &p->write_byte);
        if(p->file_result == FR_OK)
        {
            p->flag.file_head_writed = 1;
            p->flag.begin_write_data= 1;
            //p->file_result = f_close(p->object);
            //close_file(&usb_flash);
            return 1;
        }
        else return 0;
    }
    else return 1;
}

// д���¼�����ݣ��ڴ˴�ת��ASCII
void write_data(usb_flash_type* p)
{
    static _UI channel_data = 0;
    if(getflag(p->save_buff_flag, p->write_buff_num)) // �жϴ�λ���Ƿ�������
    {
        //usb_flash.cnt_p->time_flag = 1;
        // �ڴ˴�ת��ASCII
        _UI channel_num_max = p->para_p->channel_num_max;
        _C* save_buff = p->save_buff + p->write_buff_num * channel_num_max;
        _C* write_position = p->write_buff;
        channel_data++;
        int i;
        for(i=0; i<channel_num_max; i++){
//            write_position = hex_to_ASCII(write_position, save_buff[i]);
            write_position = hex_to_ASCII(write_position, channel_data);
            *write_position++ = ',';
        }
        *write_position++ = '\r';
        _UI write_length = write_position - p->write_buff;
        p->file_result = f_write(p->object, p->write_buff, write_length, &p->write_byte);

        p->cnt_p->write_less += write_length != p->write_byte; // û��ȫ��д�룬��û�г��ֹ��������

        if(p->file_result == FR_OK){
            p->cnt_p->write_cnt++;
            clrflag(p->save_buff_flag, p->write_buff_num); // �����λ�������ݵı�־
            if(++p->write_buff_num >= p->buff_arry->line) p->write_buff_num = 0;
        }
        else p->cnt_p->write_fail++;
        if((p->cnt_p->write_cnt%5000) == 0)
        {
        #if !_FS_READONLY
            p->file_result = f_sync(p->object);

//            SCIprintf("f_write_sync\n");

            if(p->file_result == FR_OK)
            {

            }
            else p->cnt_p->write_fail++;

                //usb_flash.cnt_p->time_flag = 1;
        #endif
        }
    }
}
// ��������ܸ��ӵģ����ǻ��ļ���ͷ��ʼ�Ļ�����ûʲô��д����
uint8_t change_file(usb_flash_type* p)
{
    p->para_p->file_size = p->object->fsize>>20; // ���M
    // ��һ���ļ�����С������ʱ��ʧ�ܣ��Ͳ�������һ����
    p->file_result = p->para_p->file_size >= p->para_p->file_size_max? FR_OK:FR_NOT_READY;
    if(p->para_p->file_size >= p->para_p->file_size_max)
    {
        return 1;
    }
    else return 0;
}

// �ر��ļ������ܳɹ������Ϊ��֪���ز�����ô������ص���ļ����Զ���1������������ζ�������ļ���ʼ
void close_file(usb_flash_type* p)
{
    p->file_result = f_close(p->object);
    p->flag.file_opened = 0;
    p->flag.data_head_writed = 0;
    p->flag.file_head_writed = 0;
    p->para_p->file_num++; // ������Σ��ر�һ���ļ������һ���ļ���ʼд��
}

// �ر��ļ������ܳɹ������Ϊ��֪���ز�����ô������ص���ļ����Զ���1������������ζ�������ļ���ʼ
void usb_file_add(void)
{
    //uint8_t i;
    //uint32_t time_tim; // CPU-Timer Counter Registers
   if(g_eState == STATE_DEVICE_READY)
   {
       if(open_file(&usb_flash) == 0 )
       {
           SCIprintf("\nopen_file err\n");
       }
       else if(write_file_head(&usb_flash) == 1 )
       {
//           time_recv0();
           write_data(&usb_flash);
//           time_recv1((void *)10000);
           if(change_file(&usb_flash) == 0 )
           {

           }
           else
           {
               close_file(&usb_flash);
               SCIprintf("\change_file\n");
           }
       }
       //time_recv1();
   }
}
// �ر��ļ������ܳɹ������Ϊ��֪���ز�����ô������ص���ļ����Զ���1������������ζ�������ļ���ʼ
void usb_file_add_time(void)
{
    //uint8_t i;
    //uint32_t time_tim; // CPU-Timer Counter Registers
   if(g_eState == STATE_DEVICE_READY)
   {
//       if(open_file(&usb_flash) == 0 )
//       {
//           SCIprintf("\nopen_file err\n");
//       }
//       else if(write_file_head(&usb_flash) == 1 )
//       {
//           //time_recv0();
//           write_data(&usb_flash);
//           if(change_file(&usb_flash) == 0 )
//           {
//
//           }
//           else
//           {
//               close_file(&usb_flash);
//               SCIprintf("\change_file\n");
//           }
//       }

       if(usb_flash.flag.file_head_writed == 1)
       {
           write_data(&usb_flash);

           if(change_file(&usb_flash) == 0 )
          {

          }
          else
          {
              close_file(&usb_flash);
              SCIprintf("\change_file\n");
          }
       }
       if(usb_flash.flag.file_opened== 1)
       {
           write_file_head(&usb_flash);
       }
       open_file(&usb_flash);
       //time_recv1();
   }
}

// ��ȡ�������ݣ�һ�β���ռһ�еĿռ䣬��������������
void get_sample_data(usb_flash_type* p)
{
    static uint16_t sampleCount = 0;
    if(p->flag.begin_write_data==0) return; // ����Ҫ��¼����
    int k;
//    for(k=0; k<32; k++)
    {
        if(getflag(p->save_buff_flag, p->save_buff_num)){ // Ҫд���ݵ����Ƿ���������
            p->cnt_p->data_loss++;
            RW10_flag = 5;
            //printfflag = 1;
//            continue;
            return;
        }

        // ��ȡͨ������
        _UI channel_num_max = usb_para.channel_num_max;
        _C* position = p->save_buff + p->save_buff_num * channel_num_max;

        int i;
        for(i=0; i<channel_num_max; i++)
        {
//            *position++ = (_UI)cpuTimer0IntCount;
//            *position++ = (_UI)sampleCount;
        }
        sampleCount++;
        p->cnt_p->sample_cnt++;
        setflag(p->save_buff_flag, p->save_buff_num); // ������������
        if(++p->save_buff_num >= p->buff_arry->line)
        {
            //printfflag = 1;
            p->save_buff_num=0; // Ҫд������ݴ���һ�п�ʼ
        }
    }
}

#endif


//******************************************************************************
//
// The current USB operating mode - Host, Device or unknown.
//
//******************************************************************************
tUSBMode g_eCurrentUSBMode;

//******************************************************************************
//
// USB Mode callback
//
// \param ulIndex is the zero-based index of the USB controller making the
//        callback.
// \param eMode indicates the new operating mode.
//
// This function is called by the USB library whenever an OTG mode change
// occurs and, if a connection has been made, informs us of whether we are to
// operate as a host or device.
//
// \return None.
//
//******************************************************************************
void
ModeCallback(uint32_t ui32Index, tUSBMode eMode)
{
    //
    // Save the new mode.
    //

    g_eCurrentUSBMode = eMode;
}

//******************************************************************************
//
// This function returns a string representation of an error code that was
// returned from a function call to FatFs.  It can be used for printing human
// readable error messages.
//
//******************************************************************************
const char *
StringFromFresult(FRESULT fresult)
{
    uint16_t ui16dx;

    //
    // Enter a loop to search the error code table for a matching error code.
    //
    for(ui16dx = 0; ui16dx < NUM_FRESULT_CODES; ui16dx++)
    {
        //
        // If a match is found, then return the string name of the error code.
        //
        if(g_sFresultStrings[ui16dx].fresult == fresult)
        {
            return(g_sFresultStrings[ui16dx].pcResultStr);
        }
    }

    //
    // At this point no matching code was found, so return a string indicating
    // unknown error.
    //
    return("UNKNOWN ERROR CODE");
}

//******************************************************************************
//
// This function implements the "ls" command.  It opens the current directory
// and enumerates through the contents, and prints a line for each item it
// finds.  It shows details such as file attributes, time and date, and the
// file size, along with the name.  It shows a summary of file sizes at the end
// along with free space.
//
//******************************************************************************
int
Cmd_ls(int argc, char *argv[])
{
    uint32_t ui32TotalSize;
    uint32_t ui32FileCount;
    uint32_t ui32DirCount;
    FRESULT fresult;
    FATFS *pFatFs;

    //
    // Do not attempt to do anything if there is not a drive attached.
    //
    if(g_eState != STATE_DEVICE_READY)
    {
        return(FR_NOT_READY);
    }

    //
    // Open the current directory for access.
    //
    fresult = f_opendir(&g_sDirObject, g_cCwdBuf);

    //
    // Check for error and return if there is a problem.
    //
    if(fresult != FR_OK)
    {
        return(fresult);
    }

    ui32TotalSize = 0;
    ui32FileCount = 0;
    ui32DirCount = 0;

    //
    // Enter loop to enumerate through all directory entries.
    //
    while(1)
    {
        //
        // Read an entry from the directory.
        //
        fresult = f_readdir(&g_sDirObject, &g_sFileInfo);

        //
        // Check for error and return if there is a problem.
        //
        if(fresult != FR_OK)
        {
            return(fresult);
        }

        //
        // If the file name is blank, then this is the end of the listing.
        //
        if(!g_sFileInfo.fname[0])
        {
            break;
        }

        //
        // If the attribute is directory, then increment the directory count.
        //
        if(g_sFileInfo.fattrib & AM_DIR)
        {
            ui32DirCount++;
        }

        //
        // Otherwise, it is a file.  Increment the file count, and add in the
        // file size to the total.
        //
        else
        {
            ui32FileCount++;
            ui32TotalSize += g_sFileInfo.fsize;
        }

        //
        // Print the entry information on a single line with formatting to show
        // the attributes, date, time, size, and name.
        //
        SCIprintf("%c%c%c%c%c %u/%02u/%02u %02u:%02u %9u  %s\n",
                 (g_sFileInfo.fattrib & AM_DIR) ? (uint32_t)'D' : (uint32_t)'-',
                 (g_sFileInfo.fattrib & AM_RDO) ? (uint32_t)'R' : (uint32_t)'-',
                 (g_sFileInfo.fattrib & AM_HID) ? (uint32_t)'H' : (uint32_t)'-',
                 (g_sFileInfo.fattrib & AM_SYS) ? (uint32_t)'S' : (uint32_t)'-',
                 (g_sFileInfo.fattrib & AM_ARC) ? (uint32_t)'A' : (uint32_t)'-',
                 (uint32_t)((g_sFileInfo.fdate >> 9) + 1980),
                 (uint32_t)((g_sFileInfo.fdate >> 5) & 15),
                 (uint32_t)(g_sFileInfo.fdate & 31),
                 (uint32_t)((g_sFileInfo.ftime >> 11)),
                 (uint32_t)((g_sFileInfo.ftime >> 5) & 63),
                 (uint32_t)(g_sFileInfo.fsize),
                 g_sFileInfo.fname);
    }

    //
    // Print summary lines showing the file, dir, and size totals.
    //
    SCIprintf("\n%4u File(s),%10u bytes total\n%4u Dir(s)",
               ui32FileCount, ui32TotalSize, ui32DirCount);

    //
    // Get the free space.
    //
    fresult = f_getfree("/", &ui32TotalSize, &pFatFs);

    //
    // Check for error and return if there is a problem.
    //
    if(fresult != FR_OK)
    {
        return(fresult);
    }

    //
    // Display the amount of free space that was calculated.
    //
    SCIprintf(", %10uK bytes free\n", ui32TotalSize * pFatFs->sects_clust / 2);

    //
    // Made it to here, return with no errors.
    //
    return(0);
}

//******************************************************************************
//
// This function implements the "cd" command.  It takes an argument that
// specifies the directory to make the current working directory.  Path
// separators must use a forward slash "/".  The argument to cd can be one of
// the following:
//
// * root ("/")
// * a fully specified path ("/my/path/to/mydir")
// * a single directory name that is in the current directory ("mydir")
// * parent directory ("..")
//
// It does not understand relative paths, so don't try something like this:
// ("../my/new/path")
//
// Once the new directory is specified, it attempts to open the directory to
// make sure it exists.  If the new path is opened successfully, then the
// current working directory (cwd) is changed to the new path.
//
//******************************************************************************
int
Cmd_cd(int argc, char *argv[])
{
    unsigned int uIdx;
    FRESULT fresult;

    //
    // Do not attempt to do anything if there is not a drive attached.
    //
    if(g_eState != STATE_DEVICE_READY)
    {
        return(FR_NOT_READY);
    }

    //
    // Copy the current working path into a temporary buffer so it can be
    // manipulated.
    //
    strcpy(g_cTmpBuf, g_cCwdBuf);

    //
    // If the first character is /, then this is a fully specified path, and it
    // should just be used as-is.
    //
    if(argv[1][0] == '/')
    {
        //
        // Make sure the new path is not bigger than the cwd buffer.
        //
        if(strlen(argv[1]) + 1 > sizeof(g_cCwdBuf))
        {
            SCIprintf("Resulting path name is too long\n");
            return(0);
        }

        //
        // If the new path name (in argv[1])  is not too long, then copy it
        // into the temporary buffer so it can be checked.
        //
        else
        {
            strncpy(g_cTmpBuf, argv[1], sizeof(g_cTmpBuf));
        }
    }

    //
    // If the argument is .. then attempt to remove the lowest level on the
    // CWD.
    //
    else if(!strcmp(argv[1], ".."))
    {
        //
        // Get the index to the last character in the current path.
        //
        uIdx = strlen(g_cTmpBuf) - 1;

        //
        // Back up from the end of the path name until a separator (/) is
        // found, or until we bump up to the start of the path.
        //
        while((g_cTmpBuf[uIdx] != '/') && (uIdx > 1))
        {
            //
            // Back up one character.
            //
            uIdx--;
        }

        //
        // Now we are either at the lowest level separator in the current path,
        // or at the beginning of the string (root).  So set the new end of
        // string here, effectively removing that last part of the path.
        //
        g_cTmpBuf[uIdx] = 0;
    }

    //
    // Otherwise this is just a normal path name from the current directory,
    // and it needs to be appended to the current path.
    //
    else
    {
        //
        // Test to make sure that when the new additional path is added on to
        // the current path, there is room in the buffer for the full new path.
        // It needs to include a new separator, and a trailing null character.
        //
        if(strlen(g_cTmpBuf) + strlen(argv[1]) + 1 + 1 > sizeof(g_cCwdBuf))
        {
            SCIprintf("Resulting path name is too long\n");
            return(0);
        }

        //
        // The new path is okay, so add the separator and then append the new
        // directory to the path.
        //
        else
        {
            //
            // If not already at the root level, then append a /
            //
            if(strcmp(g_cTmpBuf, "/"))
            {
                strcat(g_cTmpBuf, "/");
            }

            //
            // Append the new directory to the path.
            //
            strcat(g_cTmpBuf, argv[1]);
        }
    }

    //
    // At this point, a candidate new directory path is in chTmpBuf.  Try to
    // open it to make sure it is valid.
    //
    fresult = f_opendir(&g_sDirObject, g_cTmpBuf);

    //
    // If it can't be opened, then it is a bad path.  Inform user and return.
    //
    if(fresult != FR_OK)
    {
        SCIprintf("cd: %s\n", g_cTmpBuf);
        return(fresult);
    }

    //
    // Otherwise, it is a valid new path, so copy it into the CWD.
    //
    else
    {
        strncpy(g_cCwdBuf, g_cTmpBuf, sizeof(g_cCwdBuf));
    }

    //
    // Return success.
    //
    return(0);
}

//******************************************************************************
//
// This function implements the "pwd" command.  It simply prints the current
// working directory.
//
//******************************************************************************
int
Cmd_pwd(int argc, char *argv[])
{
    //
    // Do not attempt to do anything if there is not a drive attached.
    //
    if(g_eState != STATE_DEVICE_READY)
    {
        return(FR_NOT_READY);
    }

    //
    // Print the CWD to the console.
    //
    SCIprintf("%s\n", g_cCwdBuf);

    //
    // Return success.
    //
    return(0);
}

//******************************************************************************
//
// This function implements the "cat" command.  It reads the contents of a file
// and prints it to the console.  This should only be used on text files.  If
// it is used on a binary file, then a bunch of garbage is likely to printed on
// the console.
//
//******************************************************************************
int
Cmd_cat(int argc, char *argv[])
{
    FRESULT fresult;
    unsigned short usBytesRead;

    //
    // Do not attempt to do anything if there is not a drive attached.
    //
    if(g_eState != STATE_DEVICE_READY)
    {
        return(FR_NOT_READY);
    }

    //
    // First, check to make sure that the current path (CWD), plus the file
    // name, plus a separator and trailing null, will all fit in the temporary
    // buffer that will be used to hold the file name.  The file name must be
    // fully specified, with path, to FatFs.
    //
    if(strlen(g_cCwdBuf) + strlen(argv[1]) + 1 + 1 > sizeof(g_cTmpBuf))
    {
        SCIprintf("Resulting path name is too long\n");
        return(0);
    }

    //
    // Copy the current path to the temporary buffer so it can be manipulated.
    //
    strcpy(g_cTmpBuf, g_cCwdBuf);

    //
    // If not already at the root level, then append a separator.
    //
    if(strcmp("/", g_cCwdBuf))
    {
        strcat(g_cTmpBuf, "/");
    }

    //
    // Now finally, append the file name to result in a fully specified file.
    //
    strcat(g_cTmpBuf, argv[1]);

    //
    // Open the file for reading.
    //
    fresult = f_open(&g_sFileObject, g_cTmpBuf, FA_READ);

    //
    // If there was some problem opening the file, then return an error.
    //
    if(fresult != FR_OK)
    {
        return(fresult);
    }

    //
    // Enter a loop to repeatedly read data from the file and display it, until
    // the end of the file is reached.
    //
    do
    {
        //
        // Read a block of data from the file.  Read as much as can fit in the
        // temporary buffer, including a space for the trailing null.
        //
        fresult = f_read(&g_sFileObject, g_cTmpBuf, sizeof(g_cTmpBuf) - 1,
                         &usBytesRead);

        //
        // If there was an error reading, then print a newline and return the
        // error to the user.
        //
        if(fresult != FR_OK)
        {
            SCIprintf("\n");
            return(fresult);
        }

        //
        // Null terminate the last block that was read to make it a null
        // terminated string that can be used with printf.
        //
        g_cTmpBuf[usBytesRead] = 0;

        //
        // Print the last chunk of the file that was received.
        //
        SCIprintf("%s", g_cTmpBuf);

        //
        // Continue reading until less than the full number of bytes are read.
        // That means the end of the buffer was reached.
        //
    }
    while(usBytesRead == sizeof(g_cTmpBuf) - 1);

    //
    // Return success.
    //
    return(0);
}

//******************************************************************************
//
// This function implements the "help" command.  It prints a simple list of the
// available commands with a brief description.
//
//******************************************************************************
int
Cmd_help(int argc, char *argv[])
{
    tCmdLineEntry *pEntry;

    //
    // Print some header text.
    //
    SCIprintf("\nAvailable commands\n");
    SCIprintf("------------------\n");

    //
    // Point at the beginning of the command table.
    //
    pEntry = &g_psCmdTable[0];

    //
    // Enter a loop to read each entry from the command table.  The end of the
    // table has been reached when the command name is NULL.
    //
    while(pEntry->pcCmd)
    {
        //
        // Print the command name and the brief description.
        //
        SCIprintf("%s%s\n", pEntry->pcCmd, pEntry->pcHelp);

        //
        // Advance to the next entry in the table.
        //
        pEntry++;
    }

    //
    // Return success.
    //
    return(0);
}

//******************************************************************************
//
// This is the table that holds the command names, implementing functions, and
// brief description.
//
//******************************************************************************
tCmdLineEntry g_psCmdTable[] =
{
    { "help",   Cmd_help,      " : Display list of commands" },
    { "h",      Cmd_help,   "    : alias for help" },
    { "?",      Cmd_help,   "    : alias for help" },
    { "ls",     Cmd_ls,      "   : Display list of files" },
    { "chdir",  Cmd_cd,         ": Change directory" },
    { "cd",     Cmd_cd,      "   : alias for chdir" },
    { "pwd",    Cmd_pwd,      "  : Show current working directory" },
    { "cat",    Cmd_cat,      "  : Show contents of a text file" },
    { 0, 0, 0 }
};

//******************************************************************************
//
// This is the callback from the MSC driver.
//
// \param ui32Instance is the driver instance which is needed when communicating
// with the driver.
// \param ui32Event is one of the events defined by the driver.
// \param pvData is a pointer to data passed into the initial call to register
// the callback.
//
// This function handles callback events from the MSC driver.  The only events
// currently handled are the MSC_EVENT_OPEN and MSC_EVENT_CLOSE.  This allows
// the main routine to know when an MSC device has been detected and
// enumerated and when an MSC device has been removed from the system.
//
// \return Returns \e true on success or \e false on failure.
//
//******************************************************************************
void
MSCCallback(tUSBHMSCInstance *psMSCInstance, uint32_t ui32Event,
            void *pvEventData)
{
    //
    // Determine the event.
    //
    switch(ui32Event)
    {
        //
        // Called when the device driver has successfully enumerated an MSC
        // device.
        //
    case MSC_EVENT_OPEN:
    {
        //
        // Proceed to the enumeration state.
        //
        g_eState = STATE_DEVICE_ENUM;
        break;
    }

    //
    // Called when the device driver has been unloaded due to error or
    // the device is no longer present.
    //
    case MSC_EVENT_CLOSE:
    {
        //
        // Go back to the "no device" state and wait for a new connection.
        //
        g_eState = STATE_NO_DEVICE;

        break;
    }

    default:
    {
        break;
    }
    }
}

//******************************************************************************
//
// This is the generic callback from host stack.
//
// \param pvData is actually a pointer to a tEventInfo structure.
//
// This function will be called to inform the application when a USB event has
// occurred that is outside those related to the mass storage device.  At this
// point this is used to detect unsupported devices being inserted and removed.
// It is also used to inform the application when a power fault has occurred.
// This function is required when the g_USBGenericEventDriver is included in
// the host controller driver array that is passed in to the
// USBHCDRegisterDrivers() function.
//
// \return None.
//
//******************************************************************************
void
USBHCDEvents(void *pvData)
{
    tEventInfo *pEventInfo;

    //
    // Cast this pointer to its actual type.
    //
    pEventInfo = (tEventInfo *)pvData;

    switch(pEventInfo->ui32Event)
    {
        //
        // New keyboard detected.
        //
    case USB_EVENT_UNKNOWN_CONNECTED:
    {
        //
        // An unknown device was detected.
        //
        g_eState = STATE_UNKNOWN_DEVICE;

        break;
    }

    //
    // Keyboard has been unplugged.
    //
    case USB_EVENT_DISCONNECTED:
    {
        //
        // Unknown device has been removed.
        //
        g_eState = STATE_NO_DEVICE;

        break;
    }

    case USB_EVENT_POWER_FAULT:
    {
        //
        // No power means no device is present.
        //
        g_eState = STATE_POWER_FAULT;

        break;
    }

    default:
    {
        break;
    }
    }
}

//******************************************************************************
//
// This function reads a line of text from the SCI console.  The USB host main
// function is called throughout this process to keep USB alive and well.
//
//******************************************************************************
uint32_t data_test;
void
ReadLine(void)
{
    uint32_t ulIdx, ulPrompt;
    uint8_t ui8Char;
    tState eStateCopy;

    //
    // Start reading at the beginning of the command buffer and print a prompt.
    //
    g_cCmdBuf[0] = '\0';
    ulIdx = 0;
    ulPrompt = 1;

    //
    // Loop forever.  This loop will be explicitly broken out of when the line
    // has been fully read.
    //
    while(1)
    {
        usb_file_add();
//        usb_file_add_time();
        if(printfflag == 1)
        {
            printfflag = 0;
            data_test = usb_flash.cnt_p->data_loss;
            SCIprintf("_l %u s %u w %u\n", usb_flash.cnt_p->data_loss,usb_flash.cnt_p->sample_cnt,usb_flash.cnt_p->write_cnt);
        }
        else if(printfflag > 1)
        {
            data_test = usb_flash.cnt_p->data_loss;
            SCIprintf("_l %u s %u w %u\n", usb_flash.cnt_p->data_loss,usb_flash.cnt_p->sample_cnt,usb_flash.cnt_p->write_cnt);
            SCIprintf("printfflag %u\n",printfflag);
            printfflag = 0;
        }
        //
        // See if a mass storage device has been enumerated.
        //
        if(g_eState == STATE_DEVICE_ENUM)
        {
            //
            // Take it easy on the Mass storage device if it is slow to
            // start up after connecting.
            //
            if(USBHMSCDriveReady(g_psMSCInstance) != 0)
            {
                //
                // Wait about 100ms before attempting to check if the
                // device is ready again.
                //
                SysCtl_delay(SysCtl_getClock(DEVICE_OSCSRC_FREQ)/30);

                break;
            }

            //
            // Reset the working directory to the root.
            //
            g_cCwdBuf[0] = '/';
            g_cCwdBuf[1] = '\0';

            //
            // Attempt to open the directory.  Some drives take longer to
            // start up than others, and this may fail (even though the USB
            // device has enumerated) if it is still initializing.
            //
            f_mount(0, &g_sFatFs);
            if(f_opendir(&g_sDirObject, g_cCwdBuf) == FR_OK)
            {
                //
                // The drive is fully ready, so move to that state.
                //
                g_eState = STATE_DEVICE_READY;
            }
        }

        //
        // See if the state has changed.  We make a copy of g_eUIState to
        // prevent a compiler warning about undefined order of volatile
        // accesses.
        //
        eStateCopy = g_eUIState;
        if(g_eState != eStateCopy)
        {
            //
            // Determine the new state.
            //
            switch(g_eState)
            {
                //
                // A previously connected device has been disconnected.
                //
            case STATE_NO_DEVICE:
            {
                if(g_eUIState == STATE_UNKNOWN_DEVICE)
                {
                    SCIprintf("\nUnknown device disconnected.\n");
                }
                else
                {
                    SCIprintf("\nMass storage device disconnected.\n");
                }
                ulPrompt = 1;
                break;
            }

            //
            // A mass storage device is being enumerated.
            //
            case STATE_DEVICE_ENUM:
            {
                break;
            }

            //
            // A mass storage device has been enumerated and initialized.
            //
            case STATE_DEVICE_READY:
            {
                SCIprintf("\nMass storage device connected.\n");
                ulPrompt = 1;
                break;
            }

            //
            // An unknown device has been connected.
            //
            case STATE_UNKNOWN_DEVICE:
            {
                SCIprintf("\nUnknown device connected.\n");
                ulPrompt = 1;
                break;
            }

            //
            // A power fault has occurred.
            //
            case STATE_POWER_FAULT:
            {
                SCIprintf("\nPower fault.\n");
                ulPrompt = 1;
                break;
            }
            }

            //
            // Save the current state.
            //
            g_eUIState = g_eState;
        }

        //
        // Print a prompt if necessary.
        //
        if(ulPrompt)
        {
            //
            // Print the prompt based on the current state.
            //
            if(g_eState == STATE_DEVICE_READY)
            {
                SCIprintf("%s> %s", g_cCwdBuf, g_cCmdBuf);
            }
            else if(g_eState == STATE_UNKNOWN_DEVICE)
            {
                SCIprintf("UNKNOWN> %s", g_cCmdBuf);
            }
            else
            {
                SCIprintf("NODEV> %s", g_cCmdBuf);
            }

            //
            // The prompt no longer needs to be printed.
            //
            ulPrompt = 0;
        }

        //
        // Loop while there are characters that have been received from the
        // SCI.
        //
//        while(SCI_isDataAvailableNonFIFO(SCIA_BASE))
//        {
//            //
//            // Read the next character from the SCI.
//            //
//            ui8Char = SCI_readCharBlockingNonFIFO(SCIA_BASE);
//
//            //
//            // See if this character is a backspace and there is at least one
//            // character in the input line.
//            //
//            if((ui8Char == '\b') && (ulIdx != 0))
//            {
//                //
//                // Erase the last character from the input line.
//                //
//                SCIprintf("\b \b");
//                ulIdx--;
//                g_cCmdBuf[ulIdx] = '\0';
//            }
//
//            //
//            // See if this character is a newline.
//            //
//            else if((ui8Char == '\r') || (ui8Char == '\n'))
//            {
//                //
//                // Return to the caller.
//                //
//                SCIprintf("\n");
//                return;
//            }
//
//            //
//            // See if this character is an escape or Ctrl-U.
//            //
//            else if((ui8Char == 0x1b) || (ui8Char == 0x15))
//            {
//                //
//                // Erase all characters in the input buffer.
//                //
//                while(ulIdx)
//                {
//                    SCIprintf("\b \b");
//                    ulIdx--;
//                }
//                g_cCmdBuf[0] = '\0';
//            }
//
//            //
//            // See if this is a printable ASCII character.
//            //
//            else if((ui8Char >= ' ') && (ui8Char <= '~') &&
//                    (ulIdx < (sizeof(g_cCmdBuf) - 1)))
//            {
//                //
//                // Add this character to the input buffer.
//                //
//                g_cCmdBuf[ulIdx++] = ui8Char;
//                g_cCmdBuf[ulIdx] = '\0';
//                SCIprintf("%c", (uint32_t)ui8Char);
//            }
//        }

        //
        // Run the main routine of the Host controller driver.
        //
        USBHCDMain();
    }
}

//******************************************************************************
//
// Configure the SCI and its pins.  This must be called before SCIprintf().
//
//******************************************************************************
void
ConfigureSCI(void)
{
    //
    // GPIO28 is the SCI Rx pin.
    //
    GPIO_setMasterCore(28, GPIO_CORE_CPU1);
    GPIO_setPinConfig(GPIO_28_SCIRXDA);
    GPIO_setDirectionMode(28, GPIO_DIR_MODE_IN);
    GPIO_setPadConfig(28, GPIO_PIN_TYPE_STD);
    GPIO_setQualificationMode(28, GPIO_QUAL_ASYNC);

    //
    // GPIO29 is the SCI Tx pin.
    //
    GPIO_setMasterCore(29, GPIO_CORE_CPU1);
    GPIO_setPinConfig(GPIO_29_SCITXDA);
    GPIO_setDirectionMode(29, GPIO_DIR_MODE_OUT);
    GPIO_setPadConfig(29, GPIO_PIN_TYPE_STD);
    GPIO_setQualificationMode(29, GPIO_QUAL_ASYNC);

    //
    // Initialize the SCI for console I/O.
    //
    SCIStdioConfig(SCIA_BASE, 115200,
                   SysCtl_getLowSpeedClock(DEVICE_OSCSRC_FREQ));

}

//
// cpuTimer0ISR - Counter for CpuTimer0
//
__interrupt void
cpuTimer0ISR(void)
{
//    cpuTimer0IntCount++;
//
//    get_sample_data(p_usb_flash);
//
//    if((cpuTimer0IntCount%1000) == 0)
//    {
//        printfflag++;
//    }
//    if(cpuTimer0IntCount == 65000)
//        cpuTimer0IntCount = 0;
//    usb_file_add_time();
    //
    // Acknowledge this interrupt to receive more interrupts from group 1
    //
    Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP1);
}

//
// initCPUTimers - This function initializes all three CPU timers
// to a known state.
//
void
initCPUTimers(void)
{
    //
    // Initialize timer period to maximum
    //
    CPUTimer_setPeriod(CPUTIMER0_BASE, 0xFFFFFFFF);
    CPUTimer_setPeriod(CPUTIMER1_BASE, 0xFFFFFFFF);
    //CPUTimer_setPeriod(CPUTIMER2_BASE, 0xFFFFFFFF);

    //
    // Initialize pre-scale counter to divide by 1 (SYSCLKOUT)
    //
    CPUTimer_setPreScaler(CPUTIMER0_BASE, 0);
    CPUTimer_setPreScaler(CPUTIMER1_BASE, 0);
    //CPUTimer_setPreScaler(CPUTIMER2_BASE, 0);

    //
    // Make sure timer is stopped
    //
    CPUTimer_stopTimer(CPUTIMER0_BASE);
    CPUTimer_stopTimer(CPUTIMER1_BASE);
    //CPUTimer_stopTimer(CPUTIMER2_BASE);

    //
    // Reload all counter register with period value
    //
    CPUTimer_reloadTimerCounter(CPUTIMER0_BASE);
    CPUTimer_reloadTimerCounter(CPUTIMER1_BASE);
    //CPUTimer_reloadTimerCounter(CPUTIMER2_BASE);

    //
    // Reset interrupt counter
    //
    cpuTimer0IntCount = 0;
    //cpuTimer1IntCount = 0;
    //cpuTimer2IntCount = 0;
}

//
// configCPUTimer - This function initializes the selected timer to the
// period specified by the "freq" and "period" parameters. The "freq" is
// entered as Hz and the period in uSeconds. The timer is held in the stopped
// state after configuration.
//
void
configCPUTimer(uint32_t cpuTimer, float freq, float period)
{
    uint32_t temp;

    //
    // Initialize timer period:
    //
    temp = (uint32_t)(freq / 1000000 * period);
    CPUTimer_setPeriod(cpuTimer, temp);

    //
    // Set pre-scale counter to divide by 1 (SYSCLKOUT):
    //
    CPUTimer_setPreScaler(cpuTimer, 0);

    //
    // Initializes timer control register. The timer is stopped, reloaded,
    // free run disabled, and interrupt enabled.
    // Additionally, the free and soft bits are set
    //
    CPUTimer_stopTimer(cpuTimer);
    CPUTimer_reloadTimerCounter(cpuTimer);
    CPUTimer_setEmulationMode(cpuTimer,
                              CPUTIMER_EMULATIONMODE_STOPAFTERNEXTDECREMENT);
    CPUTimer_enableInterrupt(cpuTimer);

    //
    // Resets interrupt counters for the three cpuTimers
    //
    if (cpuTimer == CPUTIMER0_BASE)
    {
        cpuTimer0IntCount = 0;
    }
    else if(cpuTimer == CPUTIMER1_BASE)
    {
        //cpuTimer1IntCount = 0;
    }
    else if(cpuTimer == CPUTIMER2_BASE)
    {
        //cpuTimer2IntCount = 0;
    }
}

//******************************************************************************
//
// This is the main loop that runs the application.
//
//******************************************************************************
int
main(void)
{
    int iStatus;

    //
    // Initialize device clock and peripherals
    //
    Device_init();

    //
    // Initialize GPIO and configure GPIO pins for USB.
    //
    Device_initGPIO();

    //
    // Set the clocking to run from the PLL at 60MHz
    //
    SysCtl_setAuxClock(DEVICE_AUXSETCLOCK_CFG_USB);

    //
    // Initially wait for device connection.
    //
    g_eState = STATE_NO_DEVICE;
    g_eUIState = STATE_NO_DEVICE;

    //
    // 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();
    //
    // ISRs for each CPU Timer interrupt
    //
//    Interrupt_register(INT_TIMER0, &cpuTimer0ISR);
    //
    // Initializes the Device Peripheral. For this example, only initialize the
    // Cpu Timers.
    //
//    initCPUTimers();
    //
    // Configure CPU-Timer 0, 1, and 2 to interrupt every second:
    // 1 second Period (in uSeconds)
    //
//    configCPUTimer(CPUTIMER0_BASE, DEVICE_SYSCLK_FREQ, 100);
    //
    // To ensure precise timing, use write-only instructions to write to the
    // entire register. Therefore, if any of the configuration bits are changed
    // in configCPUTimer and initCPUTimers, the below settings must also
    // be updated.
    //
//    CPUTimer_enableInterrupt(CPUTIMER0_BASE);
    //
    // Enables CPU int1, int13, and int14 which are connected to CPU-Timer 0,
    // CPU-Timer 1, and CPU-Timer 2 respectively.
    // Enable TINT0 in the PIE: Group 1 interrupt 7
    //
//    Interrupt_enable(INT_TIMER0);
    //
    // Starts CPU-Timer 0, CPU-Timer 1, and CPU-Timer 2.
    //
//    CPUTimer_startTimer(CPUTIMER0_BASE);
    //
    // Enable Global Interrupt (INTM) and realtime interrupt (DBGM)
    //
    EINT;
    ERTM;

    //
    // Configure the required pins for USB operation.
    //
    USBGPIOEnable();

    //
    // Register the interrupt handler for USB Interrupts.
    //
    Interrupt_register(INT_USBA, f28x_USB0HostIntHandler);

    //
    // Configure SCIA for debug output.
    //
    ConfigureSCI();

    SCIprintf("\n\nUSB Mass Storage Host program\n");
    SCIprintf("Type \'help\' for help.\n\n");

    //
    // Enable Interrupts
    //
    Interrupt_enableMaster();

    //
    // Initialize the USB stack mode and pass in a mode callback.
    //
    USBStackModeSet(0, eUSBModeForceHost, ModeCallback);

    //
    // Register the host class drivers.
    //
    USBHCDRegisterDrivers(0, g_ppHostClassDrivers, NUM_CLASS_DRIVERS);

    //
    // Open an instance of the mass storage class driver.
    //
    g_psMSCInstance = USBHMSCDriveOpen(0, (tUSBHMSCCallback)MSCCallback);

    //
    // Initialize the power configuration. This sets the power enable signal
    // to be active high and does not enable the power fault.
    //
    USBHCDPowerConfigInit(0, USBHCD_VBUS_AUTO_HIGH | USBHCD_VBUS_FILTER);

    //
    // Initialize the USB controller for OTG operation with a 2ms polling
    // rate.
    //
    USBHCDInit(0,g_pHCDPool, HCD_MEMORY_SIZE);

    //
    // Initialize the file system.
    //
    f_mount(0, &g_sFatFs);

    //
    // Enter an infinite loop for reading and processing commands from the
    // user.
    //
    usb_init();
    while(1)
    {
        //
        // Get a line of text from the user.
        //
        ReadLine();
        if(g_cCmdBuf[0] == '\0')
        {
            continue;
        }

        //
        // Pass the line from the user to the command processor.
        // It will be parsed and valid commands executed.
        //
//        iStatus = CmdLineProcess(g_cCmdBuf);

        //
        // Handle the case of bad command.
        //
        if(iStatus == CMDLINE_BAD_CMD)
        {
            SCIprintf("Bad command!\n");
        }

        //
        // Handle the case of too many arguments.
        //
        else if(iStatus == CMDLINE_TOO_MANY_ARGS)
        {
            SCIprintf("Too many arguments for command processor!\n");
        }

        //
        // Otherwise the command was executed.  Print the error
        // code if one was returned.
        //
        else if(iStatus != 0)
        {
            SCIprintf("Command returned error code %s\n",
                       StringFromFresult((FRESULT)iStatus));
        }
    }
}

//
// End of file
//

  • Hi Strong,

    Will take a look at the code and see if any optimizations can be done.

    We don't have any other example . 'usb_ex7_host_msc.c' is the only USB example which uses FatFS to read/write to a USB mass storage device.

    Best Regards

    Siddharth

  • Hi Siddharth,

    Any updated for the issue? and another issue is that there is why a lot of functions in usbdma.c is empty? is it available for to use dma for usb function?

    //#############################################################################
    // FILE: usbdma.c
    // TITLE: USB Library DMA handling functions.
    //#############################################################################
    // $TI Release: F2807x Support Library v3.12.00.00 $
    // $Release Date: Fri Feb 12 19:00:22 IST 2021 $
    // $Copyright:
    // Copyright (C) 2014-2021 Texas Instruments Incorporated - http://www.ti.com/
    //
    // Redistribution and use in source and binary forms, with or without 
    // modification, are permitted provided that the following conditions 
    // are met:
    // 
    //   Redistributions of source code must retain the above copyright 
    //   notice, this list of conditions and the following disclaimer.
    // 
    //   Redistributions in binary form must reproduce the above copyright
    //   notice, this list of conditions and the following disclaimer in the 
    //   documentation and/or other materials provided with the   
    //   distribution.
    // 
    //   Neither the name of Texas Instruments Incorporated nor the names of
    //   its contributors may be used to endorse or promote products derived
    //   from this software without specific prior written permission.
    // 
    // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
    // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
    // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
    // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
    // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
    // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
    // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
    // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    // $
    //#############################################################################
    
    
    #include <stdbool.h>
    #include <stdint.h>
    #include "inc/hw_memmap.h"
    #include "inc/hw_types.h"
    #include "inc/hw_ints.h"
    #include "debug.h"
    #include "interrupt.h"
    #include "usb.h"
    #include "include/usblib.h"
    #include "include/usblibpriv.h"
    
    //*****************************************************************************
    //
    //! \addtogroup usblib_dma_api Internal USB DMA functions
    //! @{
    //
    //*****************************************************************************
    
    static tUSBDMAInstance g_psUSBDMAInst[1];
    
    //*****************************************************************************
    //
    // Macros used to determine if a uDMA endpoint configuration is used for
    // receive or transmit.
    //
    //*****************************************************************************
    #define UDMAConfigIsRx(ui32Config)                                            \
            ((ui32Config & UDMA_SRC_INC_NONE) == UDMA_SRC_INC_NONE)
    #define UDMAConfigIsTx(ui32Config)                                            \
            ((ui32Config & UDMA_DEST_INC_NONE) == UDMA_DEST_INC_NONE)
    
    //*****************************************************************************
    //
    // USBLibDMAChannelStatus() for USB controllers that use the uDMA for DMA.
    //
    //*****************************************************************************
    static uint32_t
    uDMAUSBChannelStatus(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Channel)
    {
        uint32_t ui32Status;
    
        //
        // Initialize the current status to no events.
        //
        ui32Status = USBLIBSTATUS_DMA_IDLE;
    
    
        return(ui32Status);
    }
    
    //*****************************************************************************
    //
    // USBLibDMAIntStatus() for USB controllers that use uDMA.
    //
    //*****************************************************************************
    static uint32_t
    uDMAUSBIntStatus(tUSBDMAInstance *psUSBDMAInst)
    {
    
    	return(0);
    }
    
    //*****************************************************************************
    //
    // USBLibDMAIntStatusClear() for USB controllers that use uDMA for DMA.
    //
    //*****************************************************************************
    static void
    DMAUSBIntStatusClear(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Status)
    {
        //
        // Clear out the requested interrupts.  Since the USB interface does not
        // have a true interrupt clear, this clears the current completed
        // status for the requested channels.
        //
        psUSBDMAInst->ui32Complete &= ~ui32Status;
    
        return;
    }
    
    //*****************************************************************************
    //
    // USBLibDMAIntHandler() for USB controllers that use uDMA for DMA.
    //
    //*****************************************************************************
    static void
    DMAUSBIntHandler(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32DMAIntStatus)
    {
        uint32_t ui32Channel;
    
        if(ui32DMAIntStatus == 0)
        {
            return;
        }
    
        //
        // Determine if the uDMA is used or the USB DMA controller.
        //
        for(ui32Channel = 0; ui32Channel < USB_MAX_DMA_CHANNELS; ui32Channel++)
        {
            //
            // Mark any pending interrupts as completed.
            //
            if(ui32DMAIntStatus & 1)
            {
                psUSBDMAInst->ui32Pending &= ~((uint32_t)1 << ui32Channel);
                psUSBDMAInst->ui32Complete |= ((uint32_t)1 << ui32Channel);
            }
    
            //
            // Check the next channel.
            //
            ui32DMAIntStatus >>= 1;
    
            //
            // Break if there are no more pending DMA interrupts.
            //
            if(ui32DMAIntStatus == 0)
            {
                break;
            }
        }
    }
    
    //*****************************************************************************
    //
    // USBLibDMAChannelEnable() for USB controllers that use uDMA.
    //
    //*****************************************************************************
    static void
    uDMAUSBChannelEnable(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Channel)
    {
    
    }
    
    //*****************************************************************************
    //
    // USBLibDMAChannelDisable() for USB controllers that use uDMA.
    //
    //*****************************************************************************
    static void
    uDMAUSBChannelDisable(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Channel)
    {
    
    }
    
    //*****************************************************************************
    //
    // USBLibDMAChannelIntEnable() for USB controllers that use uDMA.
    //
    //*****************************************************************************
    static void
    uDMAUSBChannelIntEnable(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Channel)
    {
        //
        // There is no way to Enable channel interrupts when using uDMA.
        //
    }
    
    //*****************************************************************************
    //
    // USBLibDMAChannelIntDisable() for USB controllers that use uDMA.
    //
    //*****************************************************************************
    static void
    uDMAUSBChannelIntDisable(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Channel)
    {
        //
        // There is no way to Disable channel interrupts when using uDMA.
        //
    }
    
    //*****************************************************************************
    //
    // USBLibDMATransfer() for USB controllers that use the uDMA controller.
    //
    //*****************************************************************************
    static uint32_t
    uDMAUSBTransfer(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Channel,
                    void *pvBuffer, uint32_t ui32Size)
    {
    
            return(0);
    
    }
    
    //*****************************************************************************
    //
    // USBLibDMAChannelAllocate() for USB controllers that use uDMA for DMA.
    //
    //*****************************************************************************
    static uint32_t
    uDMAUSBChannelAllocate(tUSBDMAInstance *psUSBDMAInst, uint8_t ui8Endpoint,
                           uint32_t ui32MaxPacketSize, uint32_t ui32Config)
    {
    
        return(0);
    }
    
    //*****************************************************************************
    //
    // USBLibDMAChannelRelease() for USB controllers that use uDMA for DMA.
    //
    //*****************************************************************************
    static void
    uDMAUSBChannelRelease(tUSBDMAInstance *psUSBDMAInst, uint8_t ui32Channel)
    {
    
    }
    
    //*****************************************************************************
    //
    // USBLibDMAUnitSizeSet() for USB controllers that use uDMA for DMA.
    //
    //*****************************************************************************
    static void
    uDMAUSBUnitSizeSet(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Channel,
                       uint32_t ui32BitSize)
    {
    
    }
    
    //*****************************************************************************
    //
    // USBLibDMAArbSizeSet() for USB controllers that use uDMA for DMA.
    //
    //*****************************************************************************
    static void
    uDMAUSBArbSizeSet(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Channel,
                      uint32_t ui32ArbSize)
    {
    
    }
    
    //*****************************************************************************
    //
    // USBLibDMAStatus() for USB controllers that use uDMA for DMA.
    //
    //*****************************************************************************
    static uint32_t
    DMAUSBStatus(tUSBDMAInstance *psUSBDMAInst)
    {
        return(0);
    }
    
    //*****************************************************************************
    //
    //! This function is used to initialize the DMA interface for a USB instance.
    //!
    //! \param ui32Index is the index of the USB controller for this instance.
    //!
    //! This function performs any initialization and configuration of the DMA
    //! portions of the USB controller.  This function returns a pointer that
    //! is used with the remaining USBLibDMA APIs or the function returns zero
    //! if the requested controller cannot support DMA.  If this function is called
    //! when already initialized it will not reinitialize the DMA controller and
    //! will instead return the previously initialized DMA instance.
    //!
    //! \return A pointer to use with USBLibDMA APIs.
    //
    //*****************************************************************************
    tUSBDMAInstance *
    USBLibDMAInit(uint32_t ui32Index)
    {
        uint32_t ui32Channel;
    
        ASSERT(ui32Index == USB_BASE);
    
        //
        // Save the base address of the USB controller.
        //
        g_psUSBDMAInst[0].ui32Base = ui32Index;
    
        //
        // Save the interrupt number for the USB controller.
        //
        g_psUSBDMAInst[0].ui32IntNum = INT_USB;
    
        //
        // Initialize the function pointers.
        //
        g_psUSBDMAInst[0].pfnArbSizeSet = uDMAUSBArbSizeSet;
        g_psUSBDMAInst[0].pfnChannelAllocate = uDMAUSBChannelAllocate;
        g_psUSBDMAInst[0].pfnChannelDisable = uDMAUSBChannelDisable;
        g_psUSBDMAInst[0].pfnChannelEnable = uDMAUSBChannelEnable;
        g_psUSBDMAInst[0].pfnChannelIntEnable = uDMAUSBChannelIntEnable;
        g_psUSBDMAInst[0].pfnChannelIntDisable = uDMAUSBChannelIntDisable;
        g_psUSBDMAInst[0].pfnChannelRelease = uDMAUSBChannelRelease;
        g_psUSBDMAInst[0].pfnChannelStatus = uDMAUSBChannelStatus;
        g_psUSBDMAInst[0].pfnIntHandler = DMAUSBIntHandler;
        g_psUSBDMAInst[0].pfnIntStatus = uDMAUSBIntStatus;
        g_psUSBDMAInst[0].pfnIntStatusClear = DMAUSBIntStatusClear;
        g_psUSBDMAInst[0].pfnStatus = DMAUSBStatus;
        g_psUSBDMAInst[0].pfnTransfer = uDMAUSBTransfer;
        g_psUSBDMAInst[0].pfnUnitSizeSet = uDMAUSBUnitSizeSet;
    
        //
        // Clear out the endpoint and the current configuration.
        //
        for(ui32Channel = 0; ui32Channel < USB_MAX_DMA_CHANNELS; ui32Channel++)
        {
            g_psUSBDMAInst[0].pui8Endpoint[ui32Channel] = 0;
            g_psUSBDMAInst[0].pui32Config[ui32Channel] = 0;
            g_psUSBDMAInst[0].ui32Pending = 0;
            g_psUSBDMAInst[0].ui32Complete = 0;
        }
        return(&g_psUSBDMAInst[0]);
    }
    
    //*****************************************************************************
    //
    // Close the Doxygen group.
    //! @}
    //
    //*****************************************************************************
    

  • Hi Strong ,

    Unfortunately, there is a hardware bug related to the DMA triggering from USB, hence DMA cannot be used. What is the optimization level currently enabled for the compiler?  

    Also FatFS is not built for speed.  Some FatFs functions take a lot of time for execution.

    Best Regards

    Siddharth

  • Siddharth,

    customer have use optimization level 2 and set opt_for_speed to 0, but the result is the same, still have the issue.

  • Hi Siddharth,

    Any suggestion for this issue?

  • Hi Strong,

    Unfortunately don't have any suggestions for this issue.  The f_write function goes over multiple function calls to write the data and will take a  lot of time for execution.

    Best Regards

    Siddharth