Tuesday, February 26, 2019

HID Problem Solved - For Now

I found the issue keeping the HID interface from working.  Every time you receive a HID report, you need to rearm the read interface, which I knew and thought was implemented.  This also needs to happen at the beginning in initialization.  There were several calls to his in the switch statements, but either the code never passed through these case statements or did so at the wrong time.  I put printf statements in the HID event handler and the USB task case statements so I could watch the startup process.  The only HID report that was happening was Set IDLE and then the interface would hang.  So I added the rearm in that event and it all works now.  The PC program and the PIC32 device exchange messages and the PIC32 device is initialized.  Not sure that is the correct place for it, but that is where it is for now.

I coded the MP3 player portion and ran some tests.  When commanded the, PIC32 loads the file, reads it in and sends it to the VS1053.  When it is done, the file is closed and the USB is reconnected.The player code was hung up trying to stop the VS1053, but for a first run, not bad.  Now I need to verify that VS1053 is initialized correctly and the data is going to the right ports.  Time for the logic analyzer.

Observation:
I have noticed that the demo apps are not consistent in implementation.  I have been through about 5 or 6 of them now as I worked on getting the SPI to work, SST26 FAT, a basic HID and then HID and MSD.  They are good at showing functionality, may not be as good at robust production code.  And when you combine two different apps to get combination functionality, you can run into some quirks.

Sunday, February 24, 2019

Mostly Working MP3 Player (VS1053) and HID Problems

In the last week I have made good progress on the VS1053/1063 MP3 Harmony style driver.  I am talking to it, have completed the software initialization and load the patches.  The next step is to implement the player.  At first this is going to be direct processor control.  Eventually down the road, I will convert to DMA, but I need working software first.  Once I am finished with this I will publish the driver here and on the Microchip Forums so other people can get head start on implementing a hardware version of an MP3 player.

For the next step of getting the player to actually work, I need to bring the file system on line and temporarily disconnect the USB MSD (for now).  So to do this I decided to use the HID interface and the PC program I have.  When I last looked at this, Windows had recognized the HID device, the generic HID test I have could read all the configuration data.  My PC program saw it was connected and would send a message.  Some crazy output started, but I had seen that before when the PC and the HID device got into a loop of not recognizing the data they were sending.  So I moved on to the MP3 driver because I thought that was more important.

Well there was more to it.  There always is.  The HID events are not firing after the first one.  I went back to the simple HID test program I did and it works as expected.  The HID events fire and data moves between them.  So the first thought is the MSD portion is getting in the way somehow.  The MSD works fine.  I have compared the HID test program and the HID_MSD program I am now testing and cannot find a difference.  I may have to start from scratch again, except build the HID interface fist and then add the MSD.  Not sure what I am going to do yet, but the next few days are going to be interesting.


Thursday, February 14, 2019

Basic Brick Controller

Last night I integrated the HID device and my basic packet dispatcher routines.  My basic software structure is as HID packets come in, they are loaded into processing queue.  The HID packet size is set to 64, but in my protocol, the packet size is limited to 20.  (Does not apply to text strings coming back from the Brick Controller.)  These are just control packets except for when a script is downloaded, but that is a maximum of 256 bytes.  Then serial queue is processed and the commands are executed.  All of this seems to work now.

Anywho, the development board connected to the PC, the Brick Buddy PC software recognized the device and started downloading configuration data from the Brick Controller.  At the same time the MSD SPI Flash drive connected and was available for use.  So I have basic HID/Brick Controller functionality and a USB Mass Storage Device.

My next task is to see if I can get the VS1053 MP3 player to work.

Tuesday, February 12, 2019

Working USB Mass Storage Device on SST26

I have a working example of the USB MSD on SST26 SPI Flash.  It is right at 8MB.  I have done read and write testing, while it is a little slow (SPI clock is 200KHz) it is completely functional.  Next step is see if I can raise the SPI clock speed on the PIC32MX470 Curiosity board.  While I do this I am also working on implementing the HID functionality.  Once I have HID functionality, I should be able to implement the current Brick Controller functionality on the Curiosity board.

Here is a more of detailed list of what I changed in the SST25 driver to make it work with SST26 flash.  A zip file with the changes is located here, drv_sst25.zip.  GoDaddy, who is the website host, changes the file name to some random number string to make it unique.  Rename to the link name, will make it easier to track the file.

You should also diff this with the framework one to see if I changed anything else and forgot about it.  More than likely that happened.   I have not included the .h file,  drv_sst25_local.h  that was modified either.  There were additions to the states and commands opcodes.  The big change that caused major havoc for days was cmdParamsLen in the DRV_SST25_OBJ structure.  It was cast as a byte and with 4 byte write commands (command plus 3 address bytes) and 256 byte page writes, it was rolling over to just 4 commands bytes and thus nothing happened.  I have changed this to uint32_t.  Again diff the one in the zip file with the framework one.

1.  I changed this table to include the Flash Type SST25 or SST26, as determined from the JEDEC ID read.

/***********************************************************************************************************/
/* Table mapping the supported Flash ID's to their sizes. All parts must support the JEDEC-ID Read command
   The SST25VF010A is included for backwards compatibility reasons, but is not supported since
   the 25VF010A flash part does not support the JEDEC-ID Read command.  Thus there is no way to read
   ID the part. */
/***********************************************************************************************************/
const uint32_t gSstFlashIdSizeTable [DRV_SST25_NUM_DEVICE_SUPPORTED][3] = {
    {0x25, 0x49, 0x020000}, /* SST25VF010A - 1 MBit */
    {0x25, 0x8C, 0x040000}, /* SST25VF020B - 2 MBit */
    {0x25, 0x8D, 0x080000}, /* SST25VF040B - 4 MBit */
    {0x25, 0x8E, 0x100000}, /* SST25VF080B - 8 MBit */
    {0x25, 0x41, 0x200000}, /* SST25VF016B - 16 MBit */
    {0x25, 0x4B, 0x800000}, /* SST25VF064C - 64 MBit */
   
    {0x26, 0x41, 0x200000}, /* SST26VF016B    - 16 MBit */
    {0x26, 0x42, 0x400000}, /* SST26VF032B/BA - 32 MBit */
    {0x26, 0x43, 0x800000}  /* SST26VF064b/BA - 64 MBit */
};

2.  I changed the GetFlashSize function to look at the flash type first. The JEDEC ID read function loads all three bytes of the JEDEC ID into the FlashID variable of the DRV_SST25_OBJ.  The GetGeometry function then sets a Boolean in this object to indicate SST26 or SST25.  That variable is passed to this function which reads the table above to get the size of the flash. 

/* This function returns the flash size in bytes for the specified deviceId. A
 * zero is returned if the device id is not supported. */
static uint32_t DRV_SST25_GetFlashSize
(
    uint8_t deviceId,
    bool isSST26Device
)
{
    uint8_t i, beginIndex,endIndex;

    if (isSST26Device == true)
    {
        beginIndex = DRV_SST25_NUM_DEVICE_SUPPORTED - SST26_DEVICE_COUNT;
        endIndex = DRV_SST25_NUM_DEVICE_SUPPORTED;
    }
    else
    {
        beginIndex = 0;
        endIndex = SST25_DEVICE_COUNT;
    }
    for (i = beginIndex; i < endIndex; i++)
    {
        if (deviceId == gSstFlashIdSizeTable[i][1])
        {
            return gSstFlashIdSizeTable[i][2];
        }
    }

    return 0;
}




3.  This change is one of the bigger ones.  Right now it rejects anything that is not a MicroChip FLASH.  Then it checks for SST26 or SST25 and stores this in the DRV_SST25_OBJ boolean isSST26.  Then it checks the table for the flash size.  The next sections are switch statements that populate the geometry table based on the Flash found.  I did a case statement for each flash device so there would be no question of the parameters used.  This also provides for future expansion.  The remaining code is unchanged.  For now I left in the old code,commented out.

/* This function updates the driver object's geometry information for the flash
 * device. */
static bool DRV_SST25_UpdateGeometry
(
    DRV_SST25_OBJ *dObj,
    uint8_t deviceId
)
{
    uint32_t flashSize;
   
    if (dObj->flashId[0] != 0xBF)
    {
        return false;  // chip is not a microchip SST
    }
    dObj->isSST26 = (dObj->flashId[1] == 0x26);  // is this a SST26 type device
    deviceId = dObj->flashId[2];
    flashSize = DRV_SST25_GetFlashSize (deviceId, dObj->isSST26);  // get flash size
    if (flashSize == 0)
    {
        return false;  // chip was not in table
    }

    /* Read block size and number of blocks */
    /* All Flash memories are 1 byte in read block size */
    dObj->mediaGeometryTable[0].blockSize = 1;
    dObj->mediaGeometryTable[0].numBlocks = flashSize;

    /* Build switch statements with what each flash device needs for writing */
    if (dObj->isSST26)
    {
        dObj->flashFunctions.unlock = DRV_SST26_UnlockFlash;
        switch (deviceId)
        {
            case 0x41:  // SST26VF016B
            {
                dObj->mediaGeometryTable[1].blockSize = DRV_SST25_PAGE_SIZE;
                dObj->mediaGeometryTable[1].numBlocks = flashSize >> 8;
                dObj->flashFunctions.write = DRV_SST25_WritePageProgram;
                dObj->opCodes.write = DRV_SST25_CMD_PAGE_PROGRAM;
                break;
            }
            case 0x42:  // SST26VF032B
            {
                dObj->mediaGeometryTable[1].blockSize = DRV_SST25_PAGE_SIZE;
                dObj->mediaGeometryTable[1].numBlocks = flashSize >> 8;
                dObj->flashFunctions.write = DRV_SST25_WritePageProgram;
                dObj->opCodes.write = DRV_SST25_CMD_PAGE_PROGRAM;
                break;
            }
            case 0x43:  // SST26VF064B
            {
                dObj->mediaGeometryTable[1].blockSize = DRV_SST25_PAGE_SIZE;
                dObj->mediaGeometryTable[1].numBlocks = flashSize >> 8;
                dObj->flashFunctions.write = DRV_SST25_WritePageProgram;
                dObj->opCodes.write = DRV_SST25_CMD_PAGE_PROGRAM;
                break;
            }
            default:
                break;
        }
    }
    else // flash part is SST25
    {
        dObj->flashFunctions.unlock = DRV_SST25_UnlockFlash;
        switch (deviceId)
        {
            case 0x8C:  // SST25VF020B
            {
                dObj->mediaGeometryTable[1].blockSize = 2;
                dObj->mediaGeometryTable[1].numBlocks = flashSize >> 1;
                dObj->flashFunctions.write = DRV_SST25_WriteAutoAddressIncrement;
                dObj->opCodes.write = DRV_SST25_CMD_AAI_PROGRAM;
                break;
            }
            case 0x8D:  // SST25VF040B
            {
                dObj->mediaGeometryTable[1].blockSize = 2;
                dObj->mediaGeometryTable[1].numBlocks = flashSize >> 1;
                dObj->flashFunctions.write = DRV_SST25_WriteAutoAddressIncrement;
                dObj->opCodes.write = DRV_SST25_CMD_AAI_PROGRAM;
                break;
            }
            case 0x8E:  // SST25VF080B
            {
                dObj->mediaGeometryTable[1].blockSize = 2;
                dObj->mediaGeometryTable[1].numBlocks = flashSize >> 1;
                dObj->flashFunctions.write = DRV_SST25_WriteAutoAddressIncrement;
                dObj->opCodes.write = DRV_SST25_CMD_AAI_PROGRAM;
                break;
            }
            case 0x41:  // SST25VF016B
            {
                dObj->mediaGeometryTable[1].blockSize = 2;
                dObj->mediaGeometryTable[1].numBlocks = flashSize >> 1;
                dObj->flashFunctions.write = DRV_SST25_WriteAutoAddressIncrement;
                dObj->opCodes.write = DRV_SST25_CMD_AAI_PROGRAM;
                break;
            }
            case 0x4B:  // SST25VF064C
            {
                dObj->mediaGeometryTable[1].blockSize = DRV_SST25_PAGE_SIZE;
                dObj->mediaGeometryTable[1].numBlocks = flashSize >> 8;
                dObj->flashFunctions.write = DRV_SST25_WritePageProgram;
                dObj->opCodes.write = DRV_SST25_CMD_PAGE_PROGRAM;
                break;
            }
            default:
                break;
        }
    }

//    if (deviceId == 0x4B)
//    {
//        /* SST25VF064C */
//        /* Write block size and number of blocks */
//        dObj->mediaGeometryTable[1].blockSize = DRV_SST25_PAGE_SIZE;
//        dObj->mediaGeometryTable[1].numBlocks = flashSize >> 8;
//        dObj->flashFunctions.write = DRV_SST25_WritePageProgram;
//    }
//    else
//    {
//        /* Write block size and number of blocks */
//        dObj->mediaGeometryTable[1].blockSize = 2;
//        dObj->mediaGeometryTable[1].numBlocks = flashSize >> 1;
//
//        dObj->flashFunctions.write = DRV_SST25_WriteAutoAddressIncrement;
//        if (deviceId == 0x49)
//        {
//            /* SST25VF010A */
//            dObj->opCodes.write = DRV_SST25_CMD_AAI_PROGRAM1;
//        }
//        else
//        {
//            dObj->opCodes.write = DRV_SST25_CMD_AAI_PROGRAM;
//        }
//    }

    /* Erase block size and number of blocks */
    dObj->mediaGeometryTable[2].blockSize = DRV_SST25_ERASE_SECTOR_SIZE;
    dObj->mediaGeometryTable[2].numBlocks = flashSize >> 12;

    /* Update the Media Geometry Main Structure */
    dObj->mediaGeometryObj.mediaProperty = (SYS_FS_MEDIA_READ_IS_BLOCKING | SYS_FS_MEDIA_WRITE_IS_BLOCKING),

    /* Number of read, write and erase entries in the table */
    dObj->mediaGeometryObj.numReadRegions = 1,
    dObj->mediaGeometryObj.numWriteRegions = 1,
    dObj->mediaGeometryObj.numEraseRegions = 1,
    dObj->mediaGeometryObj.geometryTable = (SYS_FS_MEDIA_REGION_GEOMETRY *)&dObj->mediaGeometryTable;

    return true;
}

NOTE: From the above three changes it should be easy to add any flash to the table by adjust the size the of the table.  Then the GetGeometry function switch/case statements would be modified to accommodate the discovered flash. 

4.  Finally are the case statements that control the state machine. 

This function

static DRV_SPI_BUFFER_EVENT DRV_SST25_ReadFlashId
 

I changed the size of the read to 3 to conform to the JEDEC ID read.  I also changed the write to 4 just so I could see the ID come back on the logic analyzer.  It is not necessary, but it is a nice debug tool.

This function

static DRV_SPI_BUFFER_EVENT DRV_SST25_UnlockFlash

I did not change.  I had no way to check it, but it should still work with SST25 Flash.  It is selected when the state machine determines it is SST25.

This function I added and is selected when the state machine determines it is SST26

static DRV_SPI_BUFFER_EVENT DRV_SST26_UnlockFlash
(
    void *driverObj
)
{
    DRV_SPI_BUFFER_EVENT event = DRV_SPI_BUFFER_EVENT_ERROR;
    DRV_SST25_OBJ *dObj = (DRV_SST25_OBJ *)driverObj;

    switch (dObj->subState)
    {
        case DRV_SST26_WREN_CMD_SQIORST:
            {
                dObj->cmdParams[0] = dObj->opCodes.writeEnable;
                dObj->disableCs = true;
                DRV_SPI_BufferAddWrite2(dObj->spiDriverHandle, &dObj->cmdParams[0], 1, 0, dObj, &dObj->spiBufferHandle);

                if (dObj->spiBufferHandle != DRV_SPI_BUFFER_HANDLE_INVALID)
                {
                    dObj->subState = DRV_SST26_WREN_STATUS_CHECK_SQIORST;  // and fall through
                    event = DRV_SPI_BUFFER_EVENT_PENDING;
                }
                else
                {
                    /* Failed to queue the write transfer request. */
                    break;
                }
            }

        case DRV_SST26_WREN_STATUS_CHECK_SQIORST:
            {
                event = DRV_SPI_BufferStatus(dObj->spiBufferHandle);
                if (event == DRV_SPI_BUFFER_EVENT_COMPLETE)
                {
                    dObj->subState = DRV_SST26_SEND_CMD_SQIORST;  // and fall through
                    event = DRV_SPI_BUFFER_EVENT_PENDING;                   
                }
                else if (event == DRV_SPI_BUFFER_EVENT_ERROR)
                    {
                        /* Error will be handled in the caller function. */
                    }
                    else
                    {
                        /* Continue to remain in the same state. */
                        event = DRV_SPI_BUFFER_EVENT_PENDING;
                        break;
                    }
            }

        case DRV_SST26_SEND_CMD_SQIORST:
            {
                dObj->cmdParams[0] = dObj->opCodes.sqioReset;
                dObj->disableCs = true;
                DRV_SPI_BufferAddWrite2(dObj->spiDriverHandle, &dObj->cmdParams[0], 1, 0, dObj, &dObj->spiBufferHandle);
                if (dObj->spiBufferHandle != DRV_SPI_BUFFER_HANDLE_INVALID)
                {
                    dObj->subState = DRV_SST26_CMD_STATUS_CHECK_SQIORST;  // and fall through
                    event = DRV_SPI_BUFFER_EVENT_PENDING;
                }
                else
                {
                    /* Failed to queue the write transfer request. */
                    break;
                }
            }

        case DRV_SST26_CMD_STATUS_CHECK_SQIORST:
            {
                event = DRV_SPI_BufferStatus(dObj->spiBufferHandle);
                if (event == DRV_SPI_BUFFER_EVENT_COMPLETE)
                {
                    dObj->subState = DRV_SST26_WREN_CMD_UNLOCK;  // and fall through
                    event = DRV_SPI_BUFFER_EVENT_PENDING;                   
                }
                else if (event == DRV_SPI_BUFFER_EVENT_ERROR)
                    {
                        /* Error will be handled in the caller function. */
                    }
                    else
                    {
                        /* Continue to remain in the same state. */
                        event = DRV_SPI_BUFFER_EVENT_PENDING;
                        break;
                    }
            }
       
        case DRV_SST26_WREN_CMD_UNLOCK:
            {
                dObj->cmdParams[0] = dObj->opCodes.writeEnable;
                dObj->disableCs = true;
                DRV_SPI_BufferAddWrite2(dObj->spiDriverHandle, &dObj->cmdParams[0], 1, 0, dObj, &dObj->spiBufferHandle);

                if (dObj->spiBufferHandle != DRV_SPI_BUFFER_HANDLE_INVALID)
                {
                    dObj->subState = DRV_SST26_WREN_STATUS_CHECK_UNLOCK;  // and fall through
                    event = DRV_SPI_BUFFER_EVENT_PENDING;
                }
                else
                {
                    /* Failed to queue the write transfer request. */
                    break;
                }
            }

        case DRV_SST26_WREN_STATUS_CHECK_UNLOCK:
            {
                event = DRV_SPI_BufferStatus(dObj->spiBufferHandle);
                if (event == DRV_SPI_BUFFER_EVENT_COMPLETE)
                {
                    dObj->subState = DRV_SST26_SEND_CMD_UNLOCK;  // and fall through
                    event = DRV_SPI_BUFFER_EVENT_PENDING;                   
                }
                else if (event == DRV_SPI_BUFFER_EVENT_ERROR)
                    {
                        /* Error will be handled in the caller function. */
                    }
                    else
                    {
                        /* Continue to remain in the same state. */
                        event = DRV_SPI_BUFFER_EVENT_PENDING;
                        break;
                    }
            }

        case DRV_SST26_SEND_CMD_UNLOCK:
            {
                dObj->cmdParams[0] = dObj->opCodes.globalUnlock;
                dObj->disableCs = true;
                DRV_SPI_BufferAddWrite2(dObj->spiDriverHandle, &dObj->cmdParams[0], 1, 0, dObj, &dObj->spiBufferHandle);
                if (dObj->spiBufferHandle != DRV_SPI_BUFFER_HANDLE_INVALID)
                {
                    dObj->subState = DRV_SST26_CMD_STATUS_CHECK_UNLOCK;  // and fall through
                    event = DRV_SPI_BUFFER_EVENT_PENDING;
                }
                else
                {
                    /* Failed to queue the write transfer request. */
                    break;
                }
            }

        case DRV_SST26_CMD_STATUS_CHECK_UNLOCK:
            {
                event = DRV_SPI_BufferStatus(dObj->spiBufferHandle);
                break;
            }
    }

    return event;
}



So what this does differently than the SST25 unlock function.  First it executes a SQIO Reset, ensuring that the device is in SPI mode and not SQIO mode.  Then it does a Global Unlock command.  Each of these is preceded by a write enable command.

Finally this function starts it off 

void DRV_SST25_Tasks

This opens the SPI driver, then reads the flash to determine if it is SST25 or SST26.  The UpdateGeometry function picks the correct unlock sequence and this is then executed.  Then it follows the normal state machine from then on.


Hoepfully this will make it easier for others to follow how the modifications were made.  Again I did not test the SST25 portion, so I may have done something to break this without knowing.  If you find it pass it along and I will update this section.









Tuesday, February 5, 2019

Moving on to USB Device MSD

I started the next phase of design implementing the USB device mass storage device.  This went unusually fast, especially as compared to the last few weeks.  My first attempt enumerated the drive on my PC, but the media failed to mount.  I went back to my FAT Test project just to make sure nothing else changed and that works just fine.

Next step is to integrate the console feature so I can send debug messages out of the serial port to the PC.  Also have a strange event in that once the PC enumerated the drive the starter kit onboard debugger disconnects.   A web search shows this can happen for several reasons, one being wrong configuration descriptors.  Not critical yet, but something to keep an eye on.

Monday, February 4, 2019

I have a working FAT

Well it has been 3 weeks of work on and off to get here.  But I finally have a working file allocation table using a SST26 Flash.  I have made some big changes to the driver so that it will continue to work with both SST25 and SST26 Flash.  Unfortunately I have no way right now to test it with SST25.

Things I changed:
1. Updated the array of supported parts to include all of the SST26 parts
2. Updated the geometry function to be expandable to just about any flash IC by using a large switch statement.  Probably is a better way to do this, but at the time this was easiest.
3.  Changed the flash detect to first find the JEDEC ID and then populate all of the needed functions.  This change eliminated all of the older SST25 parts that did not support the JEDEC ID command.
4.  Separate unlock processes for SST25 and SST26 devices.
5.  Finally had to change the type of one variable, since the SST26 can program in page size.  An 8bit unsigned int was not going to work.

There are other items that were changed and it is best if you do a diff on the original files and the ones I eventually post, just so you know waht was changed.

What I have working is the SST25FAT example.  Not the best test of the driver changes.  Next I am going to integrate the USB MSD drivers so that I can test the drive with a PC.  That way I can write a PC program to read and write the drive with various size files to verify that it works.  Once I know that it is working I will post the firmware here, probably  will be a link back to the website.

More to come!