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.

Compiler/F28M35H52C: Using FatFs 0.13b Embedded String Functions

Part Number: F28M35H52C


Tool/software: TI C/C++ Compiler

I have updated FatFs from 0.04b to 0.13b and have ported the code from sd_card_m3 example project into my own code but it's not working properly and I need to do some more testing, what I really need is code that I can put into my main program that is known good code that calls on the updated FatFs routines.  Such code exists in the main.c program that's part of the LPC23xx folder in the sample code found here and there are a bunch of example calls for things like disk_status that use the FatFs embedded String Functions such as xputs() and putdump() that are explained here  

Rather than trying to rewrite all the example code, I'd like to be able to use the FatFs embedded string functions as-is but I'm not clear on how to direct the standard output to my UART.  

My main program uses UARTStdioIntHandler and my UARTprintf() commands are working just fine.  I'll need to add xprintf.c to my project and #include xprint.h to the c programs that call the xprintxxx functions but where/how do I tweak the code to tell the xprint functions to use my UART as output?

  • Here's my main program DSTR_m3.c - I'll share ff.c and ff.h when I get it working properly (or now if anyone wants to work it with me?)

    //###########################################################################
    // FILE:   DSTR_m3.c
    // TITLE:  DSTR M3 Setup program
    //  originally based on ControlSuite Example V220 setup_m3.c
    //
    //  This program is for the Master Subsystem.  It performs DSTR system
    //  setup that is required before executing a project on the Control Subsystem.
    //
    //  - PLL configuration
    //  - Sets up UART0 at 115,200 Baud to communicate with GPS system
    //  - Call PinoutSet to configure all GPIO pins for DSTR PCB
    //  - Map specific GPIO to the Control Subsystem
    //  - Release the Control Subsystem from reset
    //  - Loop forever but handling the UART requests via interrupts
    //
    //###########################################################################
    // Ted Mawson MB Electronic Design Nov 2017
    // 11/28/2017   First running version with GPIO setup, UART echo (115,200) and successful call to make c28 core run stand-alone
    // 12/11/2017   Version that gives control of shared RAM to C28 and runs an index count out to the UART
    //              This version also 'cured' the ramfuncs error by editing the F28M35x_generic_C28_FLASH.cmd file
    // 1/18/2018    Added command parsing system for UART data reception
    // 2/3/2018     Added extra commands for setting current range SR1, SR2, SR5, SR10, SR20 - now moved to C28, values are sent via m2cArray[] as suggestions
    //              Added verBose toggle controlled by command VB - fixed issue and tested OK
    // 4/21/2018    Different versions of data stream - added current but need 1) SysInfo 2) General A/V/Pf 3) Voltage 4) Current
    // 4/22/2018    Code clean up, removed IC15 control references as C28 controls these pins (in fact always would have done)
    // 4/25/2018    Added SV, SA commands Stream Volts/Amps - also VB command now toggles correctly
    // 5/6/2018     MAJOR CHANGE to add FatFs vn013b - port from older code by Ted Mawson
    
    #include <string.h>
    #include "inc/hw_ints.h"
    #include "inc/hw_memmap.h"
    #include "inc/hw_nvic.h"
    #include "inc/hw_sysctl.h"
    #include "inc/hw_types.h"
    #include "board_drivers/set_pinout_f28m35x.h"   // modified by TM Oct 2017
    #include "driverlib/gpio.h"                     // TM needed?
    #include "driverlib/interrupt.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/systick.h"
    #include "driverlib/flash.h"
    #include "utils/cmdline.h"
    #include "utils/uartstdio.h"
    #include "utils/ustdlib.h"
    #include "third_party/fatfs_v13/src/ff.h"       // TM new for FatFs v0.13
    #include "third_party/fatfs_v13/src/diskio13.h" // TM new for FatFs v0.13
    //#include "third_party/fatfs_v13/src/ffconf.h" // TM new for FatFs v0.13
    // TM includes below this line are not in sd_card.c
    #include "inc/hw_gpio.h"
    #include "driverlib/ipc.h"
    #include "driverlib/uart.h"                     // TM needed?
    #include "driverlib/debug.h"                    // TM needed?
    #include "driverlib/cpu.c"                      // TM needed?
    #include "driverlib/ram.h"                      // TM needed?
    #include <stdio.h>
    #include "shared.h"                             // TM my shared .h file that's common to m3 and C28
    
    extern void UARTStdioIntHandler(void);          // TMupdate - used as the IO handler
    
    #ifdef _FLASH
    // These are defined by the linker (see device linker command file)
    extern unsigned long RamfuncsLoadStart;
    extern unsigned long RamfuncsRunStart;
    extern unsigned long RamfuncsLoadSize;
    #endif
    
    #define PATH_BUF_SIZE   80                      // TMupdate - size of the buffers that hold the path, or temporary data from the SD card.
    #define CMD_BUF_SIZE    64                      // TMupdate - size of the buffer that holds the command line
    static char g_cCwdBuf[PATH_BUF_SIZE] = "/";     // TMupdate - buffer to hold the full path to the current working directory
    static char g_cTmpBuf[PATH_BUF_SIZE];           // TMupdate - temporary data buffer used when manipulating file paths, or reading data from SD card
    static char g_cCmdBuf[CMD_BUF_SIZE];            // TMupdate - buffer that holds the command line
    
    //*****************************************************************************
    // The following are data structures used by FatFs.
    //*****************************************************************************
    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) }   // TMupdate - needed to allow array below to be populated
    
    //*****************************************************************************
    // An array 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.  // TMupdate - updated list 0.04b to 0.13b
    //*****************************************************************************
    tFresultString g_sFresultStrings[] =    // TM I think this is a RAM array but could be stored in Flash as a constant which is done in
    {
        FRESULT_ENTRY(FR_OK),
        FRESULT_ENTRY(FR_DISK_ERR),
        FRESULT_ENTRY(FR_INT_ERR),
        FRESULT_ENTRY(FR_NOT_READY),
        FRESULT_ENTRY(FR_NO_FILE),
        FRESULT_ENTRY(FR_NO_PATH),
        FRESULT_ENTRY(FR_INVALID_NAME),
        FRESULT_ENTRY(FR_DENIED),
        FRESULT_ENTRY(FR_EXIST),
        FRESULT_ENTRY(FR_INVALID_OBJECT),
        FRESULT_ENTRY(FR_WRITE_PROTECTED),
        FRESULT_ENTRY(FR_INVALID_DRIVE),
        FRESULT_ENTRY(FR_NOT_ENABLED),
        FRESULT_ENTRY(FR_NO_FILESYSTEM),
        FRESULT_ENTRY(FR_MKFS_ABORTED),
        FRESULT_ENTRY(FR_TIMEOUT),
        FRESULT_ENTRY(FR_LOCKED),
        FRESULT_ENTRY(FR_NOT_ENOUGH_CORE),
        FRESULT_ENTRY(FR_TOO_MANY_OPEN_FILES),
        FRESULT_ENTRY(FR_INVALID_PARAMETER)
    };
    
    //*****************************************************************************
    // Error reasons returned by ChangeDirectory().
    //*****************************************************************************
    #define NAME_TOO_LONG_ERROR 1
    #define OPENDIR_ERROR       2
    
    //*****************************************************************************
    // A macro that holds the number of result codes.
    //*****************************************************************************
    #define NUM_FRESULT_CODES (sizeof(g_sFresultStrings) / sizeof(tFresultString))
    
    //*****************************************************************************
    // The number of SysTick ticks per second.
    //*****************************************************************************
    #define TICKS_PER_SECOND 100
    
    //*****************************************************************************
    // Storage for the filename listbox widget string table.
    //*****************************************************************************
    #define NUM_LIST_STRINGS 48
    const char *g_ppcDirListStrings[NUM_LIST_STRINGS];
    
    //*****************************************************************************
    // Storage for the names of the files in the current directory.  Filenames
    // are stored in format "(D) filename.ext" for directories or "(F) filename.ext"
    // for files.
    //*****************************************************************************
    #define MAX_FILENAME_STRING_LEN (4 + 8 + 1 + 3 + 1)
    char g_pcFilenames[NUM_LIST_STRINGS][MAX_FILENAME_STRING_LEN];
    
    //*****************************************************************************
    // Storage for the strings which appear in the status box at the bottom of the
    // display.
    //****************************************************************************
    #define NUM_STATUS_STRINGS 6
    #define MAX_STATUS_STRING_LEN (36 + 1)
    char g_pcStatus[NUM_STATUS_STRINGS][MAX_STATUS_STRING_LEN];
    
    //*****************************************************************************
    // Storage for the status listbox widget string table.
    //*****************************************************************************
    const char *g_ppcStatusStrings[NUM_STATUS_STRINGS] =
    {
        g_pcStatus[0],
        g_pcStatus[1],
        g_pcStatus[2],
        g_pcStatus[3],
        g_pcStatus[4],
        g_pcStatus[5]
    };
    unsigned long g_ulStatusStringIndex = 0;
    
    //*****************************************************************************
    // Forward declarations for functions called by the widgets used in the user
    // interface.
    //*****************************************************************************
    static FRESULT ChangeToDirectory(char *pcDirectory, unsigned long *pulReason);
    static const char *StringFromFresult(FRESULT fresult);
    
    //*****************************************************************************
    // 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.
    //*****************************************************************************
    static const char *
    StringFromFresult(FRESULT fresult)
    {
        unsigned int uIdx;
    
        // Enter a loop to search the error code table for a matching
        // error code.
        for(uIdx = 0; uIdx < NUM_FRESULT_CODES; uIdx++) {
            if(g_sFresultStrings[uIdx].fresult == fresult) {   // TM if match found, return corresponding error code
                return(g_sFresultStrings[uIdx].pcResultStr);
            }
        }
        return("UNKNOWN ERROR CODE");       // TM if not matching code found, return string that it was unknown error code
    }
    
    void
    SysTickHandler(void)                    // TM handler for the SysTick interrupt
    {
        // Call the FatFs tick timer.
        disk_timerproc();
    }
    
    //*****************************************************************************
    // 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[])
    {
        unsigned long ulTotalSize, ulItemCount, ulFileCount, ulDirCount;
        FRESULT fresult;
        FATFS *pFatFs;
    
        // 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);
        }
    
        ulTotalSize = 0;
        ulFileCount = 0;
        ulDirCount = 0;
        ulItemCount = 0;
    
        // Give an extra blank line before the listing.
        UARTprintf("\n");
    
        // Enter loop to enumerate through all directory entries.
        for(;;)
        {
            // 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;
            }
    
            // Print the entry information on a single line with formatting
            // to show the attributes, date, time, size, and name.
            UARTprintf("%c%c%c%c%c %u/%02u/%02u %02u:%02u %9u  %s\n",
                       (g_sFileInfo.fattrib & AM_DIR) ? 'D' : '-',
                       (g_sFileInfo.fattrib & AM_RDO) ? 'R' : '-',
                       (g_sFileInfo.fattrib & AM_HID) ? 'H' : '-',
                       (g_sFileInfo.fattrib & AM_SYS) ? 'S' : '-',
                       (g_sFileInfo.fattrib & AM_ARC) ? 'A' : '-',
                       (g_sFileInfo.fdate >> 9) + 1980,
                       (g_sFileInfo.fdate >> 5) & 15,
                       g_sFileInfo.fdate & 31,
                       (g_sFileInfo.ftime >> 11),
                       (g_sFileInfo.ftime >> 5) & 63,
                       g_sFileInfo.fsize,
                       g_sFileInfo.fname);
    
            // Add the information as a line in the listbox widget.
            if(ulItemCount < NUM_LIST_STRINGS)
            {
                usprintf(g_pcFilenames[ulItemCount], "(%c) %12s",
                         (g_sFileInfo.fattrib & AM_DIR) ? 'D' : 'F',
                         g_sFileInfo.fname);
            }
    
            // If the attribute is directory, then increment the directory count.
            if(g_sFileInfo.fattrib & AM_DIR)
            {
                ulDirCount++;
            }
    
            // Otherwise, it is a file.  Increment the file count, and
            // add in the file size to the total.
            else
            {
                ulFileCount++;
                ulTotalSize += g_sFileInfo.fsize;
            }
    
            // Move to the next entry in the item array we use to populate the
            // list box.
            ulItemCount++;
    
            // Wait for the UART transmit buffer to empty.
            UARTFlushTx(false);
    
        }   // endfor
    
        // Print summary lines showing the file, dir, and size totals.
        UARTprintf("\n%4u File(s),%10u bytes total\n%4u Dir(s)",
                   ulFileCount, ulTotalSize, ulDirCount);
    
        // Get the free space.
        fresult = f_getfree("/", &ulTotalSize, &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.
        UARTprintf(", %10uK bytes free\n", ulTotalSize * pFatFs->csize / 2); // TM sect_clust > csize
    
        // Wait for the UART transmit buffer to empty.
        UARTFlushTx(false);
    
        // 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.
    // In cases of error, the pulReason parameter will be written with one of
    // the following values:
    //*****************************************************************************
    static FRESULT
    ChangeToDirectory(char *pcDirectory, unsigned long *pulReason)
    {
        unsigned int uIdx;
        FRESULT fresult;
    
        // 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(pcDirectory[0] == '/')
        {
            // Make sure the new path is not bigger than the cwd buffer.
            if(strlen(pcDirectory) + 1 > sizeof(g_cCwdBuf))
            {
                *pulReason = NAME_TOO_LONG_ERROR;
                return(FR_OK);
            }
    
            // 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, pcDirectory, sizeof(g_cTmpBuf));
            }
        }
    
        // If the argument is .. then attempt to remove the lowest level
        // on the CWD.
        else if(!strcmp(pcDirectory, ".."))
        {
            // 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(pcDirectory) + 1 + 1 > sizeof(g_cCwdBuf))
            {
                *pulReason = NAME_TOO_LONG_ERROR;
                return(FR_INVALID_OBJECT);
            }
    
            // 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, pcDirectory);
            }
        }
    
        // 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 cant be opened, then it is a bad path.  Inform
        // user and return.
        if(fresult != FR_OK)
        {
            *pulReason = OPENDIR_ERROR;
            return(fresult);
        }
    
        // Otherwise, it is a valid new path, so copy it into the CWD and update
        // the screen.
        else
        {
            strncpy(g_cCwdBuf, g_cTmpBuf, sizeof(g_cCwdBuf));
        }
    
        // Return success.
        return(FR_OK);
    }
    
    //*****************************************************************************
    // 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 long ulReason;
        FRESULT fresult;
    
        // Try to change to the directory provided on the command line.
        fresult = ChangeToDirectory(argv[1], &ulReason);
    
        // If an error was reported, try to offer some helpful information.
        if(fresult != FR_OK)
        {
            switch(ulReason)
            {
            case OPENDIR_ERROR:
                UARTprintf("Error opening new directory.\n");
                break;
    
            case NAME_TOO_LONG_ERROR:
                UARTprintf("Resulting path name is too long.\n");
                break;
    
            default:
                UARTprintf("An unrecognized error was reported.\n");
                break;
            }
        }
    
        // Return the appropriate error code.
        return(fresult);
    }
    
    //*****************************************************************************
    // This function implements the "pwd" command.  It simply prints the
    // current working directory.
    //*****************************************************************************
    int
    Cmd_pwd(int argc, char *argv[])
    {
        // Print the CWD to the console.
        UARTprintf("%s\n", g_cCwdBuf);
    
        // Wait for the UART transmit buffer to empty.
        UARTFlushTx(false);
    
        // 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;
        UINT usBytesRead;  // TMupdate
    
        // 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))
        {
            UARTprintf("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)
            {
                UARTprintf("\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.
            UARTprintf("%s", g_cTmpBuf);
    
            // Wait for the UART transmit buffer to empty.
            UARTFlushTx(false);
    
            // 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.
        UARTprintf("\nAvailable commands\n");
        UARTprintf("------------------\n");
    
        // Point at the beginning of the command table.
        pEntry = &g_sCmdTable[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.
            UARTprintf("%s%s\n", pEntry->pcCmd, pEntry->pcHelp);
    
            // Advance to the next entry in the table.
            pEntry++;
    
            // Wait for the UART to catch up.
            UARTFlushTx(false);
        }
    
        // Return success.
        return(0);
    }
    
    static
    void Cmd_ds (     /* Show physical disk status */
        BYTE drv
    )
    {
        DWORD dw;
        BYTE ct, buf[64];
        WORD w;
        char *ty, *am;
    
    
        UARTprintf("rc=%d\n", disk_status(drv));
    
        if (disk_ioctl(drv, GET_SECTOR_COUNT, &dw) == RES_OK)
            UARTprintf("Drive size: %lu sectors\n", dw);
    
        if (disk_ioctl(drv, GET_BLOCK_SIZE, &dw) == RES_OK)
            UARTprintf("Erase block size: %lu sectors\n", dw);
    
        if (disk_ioctl(drv, MMC_GET_TYPE, &ct) == RES_OK) {
            ty = "Unknown"; am = "";
            if (ct & CT_MMC) ty = "MMC";
            if (ct & CT_SD1) ty = "SDv1";
            if (ct & CT_SD2) {
                ty = "SDv2";
                am = (ct & CT_BLOCK) ? "(Block)" : "(Byte)";
            }
            UARTprintf("Card type: %s%s\n", ty, am);
        }
    
        if (disk_ioctl(drv, MMC_GET_CSD, buf) == RES_OK) {
            UARTprintf(%s, "CSD:\n");
            put_dump(buf, 0, 16, DW_CHAR);
        }
    
        if (disk_ioctl(drv, MMC_GET_CID, buf) == RES_OK) {
            UARTprintf(%s, "CID:\n");
            put_dump(buf, 0, 16, DW_CHAR);
        }
    
        if (disk_ioctl(drv, MMC_GET_OCR, buf) == RES_OK) {
            UARTprintf(%s, "OCR:\n");
            put_dump(buf, 0, 4, DW_CHAR);
        }
    
        if ((ct & CT_SDC) && disk_ioctl(drv, MMC_GET_SDSTAT, buf) == RES_OK) {
            UARTprintf(%s, "SD Status:\n");
            for (w = 0; w < 64; w += 16)
                put_dump(buf+w, w, 16, DW_CHAR);
        }
    }
    //*****************************************************************************
    // This is the table that holds the command names, implementing functions,
    // and brief description.
    //*****************************************************************************
    tCmdLineEntry g_sCmdTable[] =
    {
        { "help",   Cmd_help,      " : Display list of commands" },
        { "h",      Cmd_help,   "    : alias for help" },
        { "?",      Cmd_help,   "    : alias for help" },
        { "ds",     Cmd_ds(0),      "   : Show Disk Status" },
        { "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 }
    };
    
    // TM prototypes
    void processCommand(void);
    char IsCharReady(void);
    char GetRxStr(void);
    
    //*****************************************************************************
    // The error routine that is called if the driver library encounters an error.
    //*****************************************************************************
    #ifdef DEBUG
    void
    __error__(char *pcFilename, unsigned long ulLine)
    {
    }
    
    #endif
    
    // ####### DECLARATION FOR VARIABLES USED IN THE M3 CODE ######
    // TM global user variables defined to go into specific memory locations, see OneNote on shared RAM
    #pragma DATA_SECTION(ssVoltsArray, ".vArray")  // set to shared RAM area
    unsigned short ssVoltsArray[0x3140];  // defines an array of 12,608 x 16 bit unsigned integers
    
    #pragma DATA_SECTION(ssAmpsArray, ".iArray")  // set to shared RAM area
    unsigned short ssAmpsArray[0x3140];  // defines an array of 12,608 x 16 bit unsigned integers
    
    #pragma DATA_SECTION(c2mArray, ".c2mRAM")  // set to shared RAM area
    unsigned short c2mArray[20];  // defines an array of 20 x 16 bit unsigned integers
    
    #pragma DATA_SECTION(m2cArray, ".m2cRAM")  // set to S7 shared RAM area
    unsigned short m2cArray[20];  // defines an array of 20 x 16 bit unsigned integers ### NB there's space for 4,096 Uint16s in S7 (8 kBytes)
    
    unsigned int verBose = 0;
    unsigned int streamVolts = 0;
    unsigned int streamAmps = 0;
    unsigned short myIndex;
    unsigned short tempUshort;
    
    
    int main(void){
    
        int i, nStatus;
        FRESULT fresult;
    
        // Allow writes to protected registers.
        HWREG(SYSCTL_MWRALLOW) = 0xA5A5A5A5;
    
        // Sets up PLL, M3 running at 75MHz and C28 running at 150MHz
        SysCtlClockConfigSet(SYSCTL_USE_PLL | (SYSCTL_SPLLIMULT_M & 0xF) |
                             SYSCTL_SYSDIV_1 | SYSCTL_M3SSDIV_2 |
                             SYSCTL_XCLKDIV_4);         // TM - THIS LAST XCLKDIV_4 is NOT USED IN SD_CARD.C, WHAT DOES IT DO???
    
    // TM assign S0 thru S6 of the shared ram for use by the c28 TM taken from RAM_Management ControlSuite example
    // Details of how c28 uses these memory sections is defined in the c28 linker file.(28M35H52C1_RAM_lnk.cmd)
    // TM needs #include "driverlib/ram.h"
        RAMMReqSharedMemAccess((S0_ACCESS | S1_ACCESS |  S2_ACCESS | S3_ACCESS | S4_ACCESS | S5_ACCESS | S6_ACCESS ), SX_C28MASTER);
    
    #ifdef _FLASH
    // Copy time critical code and Flash setup code to RAM
    // This includes the following functions:  InitFlash();
    // The  RamfuncsLoadStart, RamfuncsLoadSize, and RamfuncsRunStart
    // symbols are created by the linker. Refer to the device .cmd file.
        memcpy(&RamfuncsRunStart, &RamfuncsLoadStart, (size_t)&RamfuncsLoadSize);
    
    // Call Flash Initialization to setup flash waitstates
    // This function must reside in RAM
        FlashInit();
    #endif
    
        PinoutSet();    // TM this runs the pinout settings that correspond to the DSTR PCB
                        // settings are in set_pinout_f28m35x.c and set_pinout_f28m35x.h (modified by TM to match DSTR PCB)
    
        // Configure SysTick for a 100Hz interrupt.
        SysTickPeriodSet(SysCtlClockGet(SYSTEM_CLOCK_SPEED) / TICKS_PER_SECOND);
        SysTickEnable();
        SysTickIntEnable();
    
        // Register interrupt handlers in the RAM vector table
        IntRegister(FAULT_SYSTICK, SysTickHandler);
    
        //Enable Interrupts.
        IntMasterEnable();
    
        // Initialize the UART as a console for text I/O.
        UARTStdioInit(0);
        IntRegister(INT_UART0, UARTStdioIntHandler);
    
        // Print hello message to user.
        UARTprintf("\n\nTMupdated nSD Card Example Program\n");
        UARTprintf("Type \'help\' for help.\n");
        // Enable the UART and GPIO peripherals clock supply
    //    SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
        SysCtlPeripheralEnable(LED_0_PERIPH);     // LED0 is on PE5 (extracted from blinky_dc_m3.c example) and is controlled by C28
        SysCtlPeripheralEnable(RANGE2KA_PERIPH);
        SysCtlPeripheralEnable(RANGE5KA_PERIPH);
        SysCtlPeripheralEnable(RANGE20KA_PERIPH);
        SysCtlPeripheralEnable(RANGE10KA_PERIPH);
    
        m2cArray[AmpsRange] = 0; // ##TMWTF##  sets 1kA range but WE SHOULD THINK OF THIS AS THE 'SUGGESTED' RANGE FROM THE M3
                                 // RIGHT NOW THIS DOESN'T DO ANYTHING
    
        // Give C28 control of Port E pin 5 (LED0)
        GPIOPinConfigureCoreSelect(LED_0_BASE, LED_0_PIN, GPIO_PIN_C_CORE_SELECT);
    
        // Unlock the register to change the commit register value for Port B Pin 7
        // This disables the NMI functionality on the pin and allows other muxing
        // options to be used
        HWREG(GPIO_PORTB_BASE+GPIO_O_LOCK) = GPIO_LOCK_KEY_DD;
        // Write to commit register
        HWREG(GPIO_PORTB_BASE+GPIO_O_CR) |= 0x000000FF;
        // Delay
        for (i=0;i<20;i++){};
    
    #ifdef _STANDALONE
    #ifdef _FLASH
        //  Send boot command to allow the C28 application to begin execution
        IPCMtoCBootControlSystem(CBROM_MTOC_BOOTMODE_BOOT_FROM_FLASH);
    #else
        //  Send boot command to allow the C28 application to begin execution
        IPCMtoCBootControlSystem(CBROM_MTOC_BOOTMODE_BOOT_FROM_RAM);
    #endif
    #endif
    
        // Mount the file system, using logical disk 0.
        fresult = f_mount(&g_sFatFs, "", 0);  // TMupdate, need to check that this is correct
        if(fresult != FR_OK)
        {
            UARTprintf("f_mount error: %s\n", StringFromFresult(fresult));
            return(1);
        }
    
    
        // Main Code
        while(1) {
            // Print a prompt to the console.  Show the CWD.
            UARTprintf("\n%s> ", g_cCwdBuf);
    
            // Get a line of text from the user.
            UARTgets(g_cCmdBuf, sizeof(g_cCmdBuf));
    
            // Pass the line from the user to the command processor.
            // It will be parsed and valid commands executed.
            nStatus = CmdLineProcess(g_cCmdBuf);
    
            // Handle the case of bad command.
            if(nStatus == CMDLINE_BAD_CMD)
            {
                UARTprintf("Bad command!\n");
            }
    
            // Handle the case of too many arguments.
            else if(nStatus == CMDLINE_TOO_MANY_ARGS)
            {
                UARTprintf("Too many arguments for command processor!\n");
            }
    
            // Otherwise the command was executed.  Print the error
            // code if one was returned.
            else if(nStatus != 0)
            {
                UARTprintf("Command returned error code %s\n",
                           StringFromFresult((FRESULT)nStatus));
            }
        } // end of while(1)
    } // end of main
    
    
    

  • Ted,

    It sounds like you have made some progress!

    To me the critical lines from the example you referenced (and the others that i looked at) are the xdev_in() and xdev_out() functions. This is the place where you set your I/O device, the UART in this case. the "xprintf" functions take care of calling the supplied UART functions if they are set up properly. This also allows you to change your output pipe on the fly if you have set up an LCD monitor, or a different UART pipe. It appears that if you set up the your input and output functions for each pipe, you can just call xdev_in/out as needed to reassign the pipe to different endpoints.

    That's at least my interpretation of the xdev_.. and xprintf capabilities.

    Can you possibly explain to me how your port is not working? are the new sdcard functions not operating as expected? are you just trying to get the output pipes working so you can print useful information?

    Thanks,
    Mark
  • Mark,

    Thanks for the reply, right now I'm just trying to get the output pipes working. I believe that the ff.s changes that I've made are OK (excepyt there may be bugs) but my main program above has mostly functions in it that were lifted from the old sd_card_m3 example from v220 of the example files for ControlCard. Line 633 is a replacement for the Cmd_ds() function and that uses put_dump() function but there were some xsprintf() calls that I started to convert and then realized it would be better just to set up the program to use the low footprint commands that are already used in the FatFs examples - then I can just cut and paste them over as needed.

    I can see that there are xdev_out() (and xdev_in()) functions in the xprintf.h file but I don't understand their syntax, what they are doing, or how/if I need to call/modify them to make the xprintf functions work from within my m3 program.

    Ted
  • My problem is in understanding how I can direct the FatFs Embeded String Functions to send a Byte out over the UART.  What the FatFs documentation says is this...

    "The low-level output function is a user provided call-back function to send a byte to the device. Its address should be set to the pointer 'xfunc_out' to set the default output device prior to use it. e.g. xdev_out(uart2_putc); The output function will be called-back from the xputc function. Typically, this function puts the byte to UART, LCD or any output device. xsprintf/xfprintf function override the default output device with its argument."

    The xprintf.h file includes these lines..

    #define xdev_out(func) xfunc_out = (void(*)(unsigned char))(func)
    extern void (*xfunc_out)(unsigned char);

    Which I think is #defining xdev_out(funct) as a macro to be substituted by xfunct_out, is that what it's doing?  Should I put a command in place of the "xfunct_out"?   If yes, how does the byte to be sent get to the called function - I'm not clear on this and I'm not clear on what C function to use to send out the byte - is it one in usstdlib.c?

    Any help greatly appreciated.

  • Ted,

    You are correct that these are some unique macros that I personally have not seen up to this point. I will consult with some SW colleagues here to see if they have any inputs that might help. I'll try to get a response by no later than Wednesday.

    -Mark
  • Ted,

    Please read up on function pointers. I did a google search and this syntax has come up often enough. Basically, the xdev_out() and xdev_in() functions allow you to hot-swap an output pipe in this case while using the same command

    Reference the other device examples in the ffsample.zip folder. in the main functions, the xdev_out function is called and the function which is passed is actually the specific uart output function that will be used to put data out of the UART port. This can be changed at any time if you want the output data to go to some other pipe.

    In the xprintf.c file, you see that the only time that xfunc_out() is actually called is within the xputc() function. Understanding this and then going back to see what function was actually passed to the xdev_out function shows that the only HW specific function in here is the UART put char function.

    All of that is a sort of confusing way to say that for your SW, you want to have the UARTCharPut() and UARTCharGet() functions from the uart.c file from driverlib.
    i.e. in your main function:
    InitUART(); //init UART port
    xdev_out(UARTCharPut);// set output pipe to uart char put function
    xdev_in(UARTCharGet); // set input pipe to uart char get function.

    replace your UARTPrintf function calls with the xprintf function.

    I *think* that is really all that is needed here. let me know if that helps at all.
    -Mark
  • Mark, thanks for the detailed response, it was exactly what I was looking for. I believe this will solve the issue so I'll mark you post as the answer but I won't get to test it for a day or 2 as I'm travelling for the next 2 weeks.

    Ted
  • Great, Please keep us posted.

    -Mark