//***************************************************************************** // // usb_stick_update.c - Example to update flash from a USB memory stick. // // Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. // Software License Agreement // // Texas Instruments (TI) is supplying this software for use solely and // exclusively on TI's microcontroller products. The software is owned by // TI and/or its suppliers, and is protected under applicable copyright // laws. You may not combine this software with "viral" open-source // software in order to form a larger program. // // THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. // NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT // NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY // CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL // DAMAGES, FOR ANY REASON WHATSOEVER. // // This is part of revision 2.1.0.12573 of the DK-TM4C129X Firmware Package. // //***************************************************************************** #include #include #include #include "inc/hw_flash.h" #include "inc/hw_gpio.h" #include "inc/hw_memmap.h" #include "inc/hw_nvic.h" #include "inc/hw_sysctl.h" #include "inc/hw_types.h" #include "driverlib/gpio.h" #include "driverlib/pin_map.h" #include "driverlib/rom.h" #include "driverlib/rom_map.h" #include "driverlib/sysctl.h" #include "driverlib/udma.h" #include "usblib/usblib.h" #include "usblib/usbmsc.h" #include "usblib/host/usbhost.h" #include "usblib/host/usbhmsc.h" #include "simple_fs.h" //***************************************************************************** // //! \addtogroup example_list //!

USB Memory Stick Updater (usb_stick_update)

//! //! This example application behaves the same way as a boot loader. It resides //! at the beginning of flash, and will read a binary file from a USB memory //! stick and program it into another location in flash. Once the user //! application has been programmed into flash, this program will always start //! the user application until requested to load a new application. //! //! When this application starts, if there is a user application already in //! flash (at \b APP_START_ADDRESS), then it will just run the user application. //! It will attempt to load a new application from a USB memory stick under //! the following conditions: //! //! - no user application is present at \b APP_START_ADDRESS //! - the user application has requested an update by transferring control //! to the updater //! - the user holds down the eval board push button when the board is reset //! //! When this application is attempting to perform an update, it will wait //! forever for a USB memory stick to be plugged in. Once a USB memory stick //! is found, it will search the root directory for a specific file name, which //! is \e FIRMWARE.BIN by default. This file must be a binary image of the //! program you want to load (the .bin file), linked to run from the correct //! address, at \b APP_START_ADDRESS. //! //! The USB memory stick must be formatted as a FAT16 or FAT32 file system //! (the normal case), and the binary file must be located in the root //! directory. Other files can exist on the memory stick but they will be //! ignored. // //***************************************************************************** //***************************************************************************** // // Defines the number of times to call to check if the attached device is // ready. // //***************************************************************************** #define USBMSC_DRIVE_RETRY 4 //***************************************************************************** // // The name of the binary firmware file on the USB stick. This is the user // application that will be searched for and loaded into flash if it is found. // Note that the name of the file must be 11 characters total, 8 for the base // name and 3 for the extension. If the actual file name has fewer characters // then it must be padded with spaces. This macro should not contain the dot // "." for the extension. // // Examples: firmware.bin --> "FIRMWAREBIN" // myfile.bn --> "MYFILE BN " // //***************************************************************************** #define USB_UPDATE_FILENAME "FIRMWAREBIN" //***************************************************************************** // // The size of the flash for this microcontroller. // //***************************************************************************** #define FLASH_SIZE (1 * 1024 * 1024) //***************************************************************************** // // The starting address for the application that will be loaded into flash // memory from the USB stick. This address must be high enough to be above // the USB stick updater, and must be on a 1K boundary. // Note that the application that will be loaded must also be linked to run // from this address. // //***************************************************************************** #define APP_START_ADDRESS 0x8000 //***************************************************************************** // // A memory location and value that is used to indicate that the application // wants to force an update. // //***************************************************************************** #define FORCE_UPDATE_ADDR 0x20004000 #define FORCE_UPDATE_VALUE 0x1234cdef //***************************************************************************** // // The prototype for the function that is used to call the user application. // //***************************************************************************** void CallApplication(uint_fast32_t ui32StartAddr); //***************************************************************************** // // The control table used by the uDMA controller. This table must be aligned // to a 1024 byte boundary. In this application uDMA is only used for USB, // so only the first 6 channels are needed. // //***************************************************************************** #if defined(ewarm) #pragma data_alignment=1024 tDMAControlTable g_sDMAControlTable[6]; #elif defined(ccs) #pragma DATA_ALIGN(g_sDMAControlTable, 1024) tDMAControlTable g_sDMAControlTable[6]; #else tDMAControlTable g_sDMAControlTable[6] __attribute__ ((aligned(1024))); #endif //***************************************************************************** // // 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 }; //***************************************************************************** // // 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. // //***************************************************************************** volatile enum { // // No device is present. // STATE_NO_DEVICE, // // Mass storage device is being enumerated. // STATE_DEVICE_ENUM, } g_eState; //***************************************************************************** // // The instance data for the MSC driver. // //***************************************************************************** tUSBHMSCInstance *g_psMSCInstance = 0; //***************************************************************************** // // 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]; //***************************************************************************** // // A buffer for holding sectors read from the storage device // //***************************************************************************** static uint8_t g_ui8SectorBuf[512]; //***************************************************************************** // // Global to hold the clock rate. Set once read many. // //***************************************************************************** uint32_t g_ui32SysClock; //***************************************************************************** // // The error routine that is called if the driver library encounters an error. // //***************************************************************************** #ifdef DEBUG void __error__(char *pcFilename, uint32_t ui32Line) { } #endif //***************************************************************************** // // Read a sector from the USB mass storage device. // // \param ui32Sector is the sector to read from the connected USB mass storage // device (memory stick) // \param pui8Buf is a pointer to the buffer where the sector data should be // stored // // This is the application-specific implementation of a function to read // sectors from a storage device, in this case a USB mass storage device. // This function is called from the \e simple_fs.c file when it needs to read // data from the storage device. // // \return Non-zero if data was read from the device, 0 if no data was read. // //***************************************************************************** uint32_t SimpleFsReadMediaSector(uint_fast32_t ui32Sector, uint8_t *pui8Buf) { // // Return the requested sector from the connected USB mass storage // device. // return(USBHMSCBlockRead(g_psMSCInstance, ui32Sector, pui8Buf, 1)); } //***************************************************************************** // // 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 \b MSC_EVENT_OPEN and \b 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. // //***************************************************************************** static void MSCCallback(tUSBHMSCInstance *psMSCInstance, uint32_t ui32Event, void *pvData) { // // 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; } } } //***************************************************************************** // // Read the application image from the file system and program it into flash. // // This function will attempt to open and read the firmware image file from // the mass storage device. If the file is found it will be programmed into // flash. The name of the file to be read is configured by the macro // \b USB_UPDATE_FILENAME. It will be programmed into flash starting at the // address specified by APP_START_ADDRESS. // // \return Zero if successful or non-zero if the file cannot be read or // programmed. // //***************************************************************************** uint32_t ReadAppAndProgram(void) { uint_fast32_t ui32FlashEnd; uint_fast32_t ui32FileSize; uint_fast32_t ui32DataSize; uint_fast32_t ui32Remaining; uint_fast32_t ui32ProgAddr; uint_fast32_t ui32SavedRegs[2]; volatile uint_fast32_t ui32Idx; uint_fast32_t ui32DriveTimeout; // // Initialize the drive timeout. // ui32DriveTimeout = USBMSC_DRIVE_RETRY; // // Check to see if the mass storage device is ready. Some large drives // take a while to be ready, so retry until they are ready. // while(USBHMSCDriveReady(g_psMSCInstance)) { // // Wait about 500ms before attempting to check if the // device is ready again. // SysCtlDelay(g_ui32SysClock/(3*2)); // // Decrement the retry count. // ui32DriveTimeout--; // // If the timeout is hit then return with a failure. // if(ui32DriveTimeout == 0) { return(1); } } // // Initialize the file system and return if error. // if(SimpleFsInit(g_ui8SectorBuf)) { return(1); } // // Attempt to open the firmware file, retrieving the file/image size. // A file size of error means the file was not there, or there was an // error. // ui32FileSize = SimpleFsOpen(USB_UPDATE_FILENAME); if(ui32FileSize == 0) { return(1); } // // Get the size of flash. This is the ending address of the flash. // If reserved space is configured, then the ending address is reduced // by the amount of the reserved block. // ui32FlashEnd = FLASH_SIZE; #ifdef FLASH_RSVD_SPACE ui32FlashEnd -= FLASH_RSVD_SPACE; #endif // // If flash code protection is not used, then change the ending address // to be the ending of the application image. This will be the highest // flash page that will be erased and so only the necessary pages will // be erased. If flash code protection is enabled, then all of the // application area pages will be erased. // #ifndef FLASH_CODE_PROTECTION ui32FlashEnd = ui32FileSize + APP_START_ADDRESS; #endif // // Check to make sure the file size is not too large to fit in the flash. // If it is too large, then return an error. // if((ui32FileSize + APP_START_ADDRESS) > ui32FlashEnd) { return(1); } // // Enter a loop to erase all the requested flash pages beginning at the // application start address (above the USB stick updater). // for(ui32Idx = APP_START_ADDRESS; ui32Idx < ui32FlashEnd; ui32Idx += 1024) { ROM_FlashErase(ui32Idx); } // // Enter a loop to read sectors from the application image file and // program into flash. Start at the user app start address (above the USB // stick updater). // ui32ProgAddr = APP_START_ADDRESS; ui32Remaining = ui32FileSize; while(SimpleFsReadFileSector()) { // // Compute how much data was read from this sector and adjust the // remaining amount. // ui32DataSize = ui32Remaining >= 512 ? 512 : ui32Remaining; ui32Remaining -= ui32DataSize; // // Special handling for the first block of the application. // This block contains as the first two location the application's // initial stack pointer and instruction pointer. The USB updater // relied on the values in these locations to determine if a valid // application is present. When there is a valid application the // updater runs the user application. Otherwise the updater attempts // to load a new application. // In order to prevent a partially programmed imaged (due to some // error occurring while programming), the first two locations are // not programmed until all of the rest of the image has been // successfully loaded into the flash. This way if there is some error, // the updater will detect that a user application is not present and // will not attempt to run it. // // For the first block, do not program the first two word locations // (8 bytes). These two words will be programmed later, after // everything else. // if(ui32ProgAddr == APP_START_ADDRESS) { uint32_t *pui32Temp; pui32Temp = (uint32_t *)g_ui8SectorBuf; ui32SavedRegs[0] = pui32Temp[0]; ui32SavedRegs[1] = pui32Temp[1]; // // Call the function to program a block of flash. Skip the first // two words (8 bytes) since these contain the initial SP and PC. // ROM_FlashProgram((uint32_t *)&g_ui8SectorBuf[8], ui32ProgAddr + 8, ((ui32DataSize - 8) + 3) & ~3); } // // All other blocks except the first block // else { // // Call the function to program a block of flash. The length of the // block passed to the flash function must be divisible by 4. // ROM_FlashProgram((uint32_t *)g_ui8SectorBuf, ui32ProgAddr, (ui32DataSize + 3) & ~3); } // // If there is more image to program, then update the programming // address. Progress will continue to the next iteration of // the while loop. // if(ui32Remaining) { ui32ProgAddr += ui32DataSize; } // // Otherwise we are done programming so perform final steps. // Program the first two words of the image that were saved earlier, // and return a success indication. // else { ROM_FlashProgram((uint32_t *)ui32SavedRegs, APP_START_ADDRESS, 8); return(0); } } // // If we make it here, that means that an attempt to read a sector of // data from the device was not successful. That means that the complete // user app has not been programmed into flash, so just return an error // indication. // return(1); } //***************************************************************************** // // This is the main routine for performing an update from a mass storage // device. // // This function forms the main loop of the USB stick updater. It polls for // a USB mass storage device to be connected, Once a device is connected // it will attempt to read a firmware image from the device and load it into // flash. // // \return None. // //***************************************************************************** void UpdaterUSB(void) { // // Loop forever, running the USB host driver. // while(1) { USBHCDMain(); // // Check for a state change from the USB driver. // switch(g_eState) { // // This state means that a mass storage device has been // plugged in and enumerated. // case STATE_DEVICE_ENUM: { // // Attempt to read the application image from the storage // device and load it into flash memory. // if(ReadAppAndProgram()) { // // There was some error reading or programming the app, // so reset the state to no device which will cause a // wait for a new device to be plugged in. // g_eState = STATE_NO_DEVICE; } else { // // User app load and programming was successful, so reboot // the micro. Perform a software reset request. This // will cause the microcontroller to reset; no further // code will be executed. // HWREG(NVIC_APINT) = NVIC_APINT_VECTKEY | NVIC_APINT_SYSRESETREQ; // // The microcontroller should have reset, so this should // never be reached. Just in case, loop forever. // while(1) { } } break; } // // This state means that there is no device present, so just // do nothing until something is plugged in. // case STATE_NO_DEVICE: { break; } } } } //***************************************************************************** // // Configure the USB controller and power the bus. // // This function configures the USB controller for host operation. // It is assumed that the main system clock has been configured at this point. // // \return None. // //***************************************************************************** void ConfigureUSBInterface(void) { // // Enable the uDMA controller and set up the control table base. // This is required by usblib. // SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA); SysCtlDelay(80); uDMAEnable(); uDMAControlBaseSet(g_sDMAControlTable); // // Enable the USB controller. // ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_USB0); // // Set the USB pins to be controlled by the USB controller. // ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB); ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD); ROM_GPIOPinConfigure(GPIO_PD6_USB0EPEN); ROM_GPIOPinTypeUSBDigital(GPIO_PORTD_BASE, GPIO_PIN_6); ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOL); ROM_GPIOPinTypeUSBAnalog(GPIO_PORTL_BASE, GPIO_PIN_6 | GPIO_PIN_7); ROM_GPIOPinTypeUSBAnalog(GPIO_PORTB_BASE, GPIO_PIN_0 | GPIO_PIN_1); // // Register the host class driver // USBHCDRegisterDrivers(0, g_ppHostClassDrivers, NUM_CLASS_DRIVERS); // // Open an instance of the mass storage class driver. // g_psMSCInstance = USBHMSCDriveOpen(0, 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); // // Force the USB mode to host with no callback on mode changes since // there should not be any. // USBStackModeSet(0, eUSBModeForceHost, 0); // // Wait 10ms for the pin to go low. // SysCtlDelay(g_ui32SysClock/100); // // Initialize the host controller. // USBHCDInit(0, g_pHCDPool, HCD_MEMORY_SIZE); } //***************************************************************************** // // Generic configuration is handled in this function. // // This function is called by the start up code to perform any configuration // necessary before calling the update routine. It is responsible for setting // the system clock to the expected rate and setting flash programming // parameters prior to calling ConfigureUSBInterface() to set up the USB // hardware. // // \return None. // //***************************************************************************** void UpdaterMain(void) { // // Make sure NVIC points at the correct vector table. // HWREG(NVIC_VTABLE) = 0; // // Run from the PLL at 120 MHz. // g_ui32SysClock = MAP_SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ | SYSCTL_OSC_MAIN | SYSCTL_USE_PLL | SYSCTL_CFG_VCO_480), 120000000); // // Enable lazy stacking for interrupt handlers. This allows floating-point // instructions to be used within interrupt handlers, but at the expense of // extra stack usage. // ROM_FPULazyStackingEnable(); // // Configure the USB interface and power the bus. // ConfigureUSBInterface(); // // Call the updater function. This will attempt to load a new image // into flash from a USB memory stick. // UpdaterUSB(); } //***************************************************************************** // // Main entry point for the USB stick update example. // // This function will check to see if a flash update should be performed from // the USB memory stick, or if the user application should just be run without // any update. // // The following checks are made, any of which mean that an update should be // performed: // - the PC and SP for the user app do not appear to be valid // - a memory location contains a certain value, meaning the user app wants // to force an update // - the user button on the eval board is being pressed, meaning the user wants // to force an update even if there is a valid user app in memory // // If any of the above checks are true, then that means that an update should // be attempted. The USB stick updater will then wait for a USB stick to be // plugged in, and once it is look for a firmware update file. // // If none of the above checks are true, then the user application that is // already in flash is run and no update is performed. // // \return None. // //***************************************************************************** int main(void) { uint32_t *pui32App; // // See if the first location is 0xfffffffff or something that does not // look like a stack pointer, or if the second location is 0xffffffff or // something that does not look like a reset vector. // pui32App = (uint32_t *)APP_START_ADDRESS; if((pui32App[0] == 0xffffffff) || ((pui32App[0] & 0xfff00000) != 0x20000000) || (pui32App[1] == 0xffffffff) || ((pui32App[1] & 0xfff00001) != 0x00000001)) { // // App starting stack pointer or PC is not valid, so force an update. // UpdaterMain(); } // // Check to see if the application has requested an update // if(HWREG(FORCE_UPDATE_ADDR) == FORCE_UPDATE_VALUE) { HWREG(FORCE_UPDATE_ADDR) = 0; UpdaterMain(); } // // Enable the GPIO input for the SEL button. // ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOP); ROM_GPIODirModeSet(GPIO_PORTP_BASE, GPIO_PIN_1, GPIO_DIR_MODE_IN); MAP_GPIOPadConfigSet(GPIO_PORTP_BASE, GPIO_PIN_1, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPU); // // Check if the button is pressed, if so then force an update. // if(ROM_GPIOPinRead(GPIO_PORTP_BASE, GPIO_PIN_1) == 0) { UpdaterMain(); } // // If we get to here that means that none of the conditions that should // cause an update are true. Therefore, call the application. // CallApplication(APP_START_ADDRESS); } //***************************************************************************** // // This function is called from the application to request an update. The // address of this function is stored in the SVC vector at offset 0x2C, so // the user application can call it by using a statement like this: // // (*((void (*)(void))(*(uint32_t *)0x2c)))(); // //***************************************************************************** void AppForceUpdate(void) { // // Set a value in a memory location to indicate that the app requests // an update. Then cause the processor to reset. // HWREG(FORCE_UPDATE_ADDR) = FORCE_UPDATE_VALUE; HWREG(NVIC_APINT) = NVIC_APINT_VECTKEY | NVIC_APINT_SYSRESETREQ; } //***************************************************************************** // // This function is used to call the user application. It will set the NVIC // to point at the user app's vector table, load up the user app's stack // pointer, and then jump to the application. // // This function must be programmed in assembly since it needs to directly // manipulate the value in the stack pointer, and because it needs to perform // a direct branch to the user app and not a function call (bl). // //***************************************************************************** #if defined(codered) || defined(gcc) || defined(sourcerygxx) void __attribute__((naked)) CallApplication(uint_fast32_t ui32StartAddr) { // // Set the vector table to the beginning of the app in flash. // HWREG(NVIC_VTABLE) = ui32StartAddr; // // Load the stack pointer from the application's vector table. // __asm(" ldr r1, [r0]\n" " mov sp, r1"); // // Load the initial PC from the application's vector table and branch to // the application's entry point. // __asm(" ldr r0, [r0, #4]\n" " bx r0\n"); } #elif defined(ewarm) void CallApplication(uint_fast32_t ui32StartAddr) { // // Set the vector table to the beginning of the app in flash. // HWREG(NVIC_VTABLE) = ui32StartAddr; // // Load the stack pointer from the application's vector table. // __asm(" ldr r1, [r0]\n" " mov sp, r1"); // // Load the initial PC from the application's vector table and branch to // the application's entry point. // __asm(" ldr r0, [r0, #4]\n" " bx r0\n"); } #elif defined(rvmdk) || defined(__ARMCC_VERSION) __asm void CallApplication(uint_fast32_t ui32StartAddr) { // // Set the vector table address to the beginning of the application. // ldr r1, =0xe000ed08 str r0, [r1] // // Load the stack pointer from the application's vector table. // ldr r1, [r0] mov sp, r1 // // Load the initial PC from the application's vector table and branch to // the application's entry point. // ldr r0, [r0, #4] bx r0 } #elif defined(ccs) void CallApplication(uint_fast32_t ui32StartAddr) { // // Set the vector table to the beginning of the app in flash. // HWREG(NVIC_VTABLE) = ui32StartAddr; // // Load the stack pointer from the application's vector table. // __asm(" ldr r1, [r0]\n" " mov sp, r1\n"); // // Load the initial PC from the application's vector table and branch to // the application's entry point. // __asm(" ldr r0, [r0, #4]\n" " bx r0\n"); } #else #error Undefined compiler! #endif