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!

Monday, January 21, 2019

New Brick Controller

I have been working on a new Brick Controller Design.  The intention is to upgrade the current design for
  • TYPE-C interface
  • More 9VDC power for the motors
  • New LED controller for up to 10 LEDs
  • BT Interface
Option add on board
  • MP3 Player with audio amp
  • USB Mass storage for storing the MP3 file.
After studying this  for a while, I became concerned that a PIC18F was not going to have the horsepower to do all of this.  Plus the latest Microchip libraries are pushing towards the PIC32.  So I started with a PIC32 development board, PIC32MX Curiosity and some Mikroe Click Boards.  First thing was tho get the development board to look like a USB MSD.  There was an example of this using a Click micro USB SD card.  This came up very easily and worked like it was supposed to.  But I didnt really want to put in an SD card when I only need about 5MB of storage for the MP3 file.

So I moved on to a Click Flash with a SST26VF064 on it.  This gives just under 8MB, which should be enough.  However the PIC32 Harmony framework only supports SST25 (now discontinued) on SPI. There is a SST26 driver for SQI.  However, the PIC32MX does not have inherent SQI.  Trying to get this working has been frustrating and from reading the forums, I am not alone.  I have the SST25 driver modified and I can see the correct initialization sequence on the scope, but reading has proven to be illusive.  I will say this, if I ever get this to work, I intend to post the project here so other people dont have to go through what I did.

Thursday, January 17, 2019

New Year Starts

After taking a few months off to do other things, it is a new year and time to start working on the next set of new devices.

So what am I attempting to do   Most of the work revolves around USB Type C power delivery.  Building new USB power modules that use Type C connectors and thus can deliver more than the 5-7W of a typical USB connection. This also applies to the Brick Controller which will be able to deliver more power to the motors.

Here is a 3D model of the test prototype of the Type C power module.  From this design it looks like I will be able to maintain the 1.5 x 1.5 inch size of the previous modules.  This is the area contained inside the four mounting holes.  I have added a small PIC18F area(to the right) so that I can monitor the STUSB4500 during the test process,   In the end the configuration will be programmed into NVM on the IC and this wont be needed.



The other project is the new brick controller.  Besides adding Type C, I want to add an MP3 player and storage for music that appears as a USB drive, as an option.  With USB HID and MSD, MP3 and BTLE interface, a PIC18F is not going to make it.  So I am moving this design to a PIC32.  Some what of learning experience, since the Harmony Configurator is taking some time to get use to.  I am prototyping the design using a PIC32MX470 Curiosity development board for now.  With two expansion slots it should make it easier to do.  Here is a stock picture of the development board.
 




Monday, October 8, 2018

Maker Faire San Diego

We had an outstanding experience at the San Diego Maker Faire (#makerfairesandiego).  We want to thank everyone who helped make it an event to remember.  Looking forward to next year. Don’t forget we are honoring the Maker Faire prices until Oct 15 at the store website, www.mymakertools.com .