6 years, 10 months ago.

Can mbed jump to ISP rom like the autoisp example shown in AN11305?

Is there a way to automate entry of USB ISP like NXP describeds in AN11305? NXP calls has example called autoisp. Can mbed jump to ISP rom like the autoisp example shown below? /media/uploads/andersonj55126/an11305v.1.pdf /media/uploads/andersonj55126/autoisp_main.c

AN11305 "NXP LPC11UXX microcontrollers enter ISP mode by default when no firmware is programmed, or they can be placed in ISP mode by pulling a pin low (PIO0.1 on the LPC11U3X/LPC11U2X family) and resetting or power-cycling the device. Sometimes it is not desired to require a user to press a button while connecting the device to invoke ISP mode for firmware updates. In this case, the firmware can be designed to allow programmatic entry into ISP mode. Programmatic entry into ISP mode is accomplished with a call to the In-Application Programming API in the LPC11Uxx on-chip ROM. After the firmware is updated, the user will need to power-cycle the device in order to start the new firmware. Alternately the firmware can pre-configure the watchdog timer to reset the LPC11Uxx after the new firmware has been downloaded. A flow chart is printed below showing the device-side automated ISP process. A software example is provided called “autoisp” to demonstrate this technique."

autoisp from NXP

/****************************************************************************
 *   $Id:: autoisp_main.c 4528 2010-08-24 23:32:22Z nxp21346                        $
 *   Project: NXP LPC13xx autoisp example
 *
 *   Description:
 *     This file contains the main routine for the autoisp code sample
 *     which puts an LPC134x part in USB ISP mode with the watchdog configured
 *     to reboot the chip after 15 seconds so it can run the new firmware.
 *
 ****************************************************************************
 * Software that is described herein is for illustrative purposes only
 * which provides customers with programming information regarding the
 * products. This software is supplied "AS IS" without any warranties.
 * NXP Semiconductors assumes no responsibility or liability for the
 * use of the software, conveys no license or title under any patent,
 * copyright, or mask work right to the product. NXP Semiconductors
 * reserves the right to make changes in the software without
 * notification. NXP Semiconductors also make no representation or
 * warranty that such application will be suitable for the specified
 * use without further testing or modification.
****************************************************************************/


#include "LPC11Uxx.h"
#include "gpio.h"
#include "clkconfig.h"
#include "config.h"
#include "wdt.h"

volatile uint32_t TimeTick = 0;

#define SYSTICK_TICKSPERSEC 100
#define SYSTICK_DELAY_CYCLES (SystemCoreClock/SYSTICK_TICKSPERSEC)
#define INTERNAL_RC_HZ 12000000

#define WDTCLK_SRC_IRC_OSC          0
#define WDTCLK_SRC_WDT_OSC          1


/* SysTick interrupt happens every 10 ms */
/* Note- The SysTick timer is in the Cortex CPU core and does not
 * run during sleep mode.
 */
void SysTick_Handler(void)
{
  /* Count 10ms periods */
  TimeTick++;
}


/* This data must be global so it is not read from the stack */
typedef void (*IAP)(uint32_t [], uint32_t []);
IAP iap_entry = (IAP)0x1fff1ff1;
uint32_t command[5], result[4];
#define init_msdstate() *((uint32_t *)(0x10000054)) = 0x0

/* This function resets some microcontroller peripherals to reset
   hardware configuration to ensure that the USB In-System Programming module
   will work properly. It is normally called from reset and assumes some reset
   configuration settings for the MCU.
   Some of the peripheral configurations may be redundant in your specific
   project.
*/
void ReinvokeISP(void)
{
  /* make sure USB clock is turned on before calling ISP */
  LPC_SYSCON->SYSAHBCLKCTRL |= 0x04000;
  /* make sure 32-bit Timer 1 is turned on before calling ISP */
  LPC_SYSCON->SYSAHBCLKCTRL |= 0x00400;
  /* make sure GPIO clock is turned on before calling ISP */
  LPC_SYSCON->SYSAHBCLKCTRL |= 0x00040;
  /* make sure IO configuration clock is turned on before calling ISP */
  LPC_SYSCON->SYSAHBCLKCTRL |= 0x10000;

  /* make sure AHB clock divider is 1:1 */
  LPC_SYSCON->SYSAHBCLKDIV = 1;

  /* Send Reinvoke ISP command to ISP entry point*/
  command[0] = 57;

  init_msdstate();					 /* Initialize Storage state machine */
  /* Set stack pointer to ROM value (reset default) This must be the last
     piece of code executed before calling ISP, because most C expressions
     and function returns will fail after the stack pointer is changed. */
  __set_MSP(*((uint32_t *)0x00000000));

  /* Enter ISP. We call "iap_entry" to enter ISP because the ISP entry is done
     through the same command interface as IAP. */
  iap_entry(command, result);
  // Not supposed to come back!
}

/* Main Program */
int main()
{
  /* Basic chip initialization is taken care of in SystemInit() called
   * from the startup code. Chip settings are defined in the CMSIS
   * system_<part family>.c file.
   */
  WDT_CLK_Setup(WDTCLK_SRC_WDT_OSC);

  /* Initialize GPIO (sets up clock) */
  GPIOInit();

  /* Configure SYSTICK timer to generate interrupt every 10ms */
  SysTick_Config(SYSTICK_DELAY_CYCLES);

  GPIOSetDir( LED_PORT, LED_BIT, 1);
  /*Turn on LED to indicate the user application is active*/
  GPIOSetBitValue( LED_PORT, LED_BIT, LED_ON);

  while(1)
  {

	GPIOSetBitValue( LED_PORT, LED_BIT, (TimeTick/(SYSTICK_TICKSPERSEC/2))&1);
    if(TimeTick >= PRE_ISP_DELAY_SECS*SYSTICK_TICKSPERSEC)
    {

      /* Disable SYSTICK timer and interrupt before calling into ISP */
      SysTick->CTRL &= ~(SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk);

      /*Initialize WDT*/
      WDTInit();

      /* Turn off LED to indicate ISP is active */
      GPIOSetBitValue( LED_PORT, LED_BIT, LED_OFF);

      /* Start ISP */
      ReinvokeISP();
    }
    /* Go to sleep to save power between SYSTICK interrupts */
    __WFI();
  }
}

1 Answer

6 years, 10 months ago.

Please put your code in code tags for readability. The LPC11UXX mbed's can do this for sure, I'm using it myself. Here's the code I ported from C:

IAP.h

#ifndef IAP_H
#define IAP_H

#include "mbed.h"

/** UID - 128-bit Unique ID Typedef */
typedef struct {
    unsigned int word0; /**< Word 0 of 128-bit UID (bits 31 to 0) */
    unsigned int word1; /**< Word 1 of 128-bit UID (bits 63 to 32) */
    unsigned int word2; /**< Word 2 of 128-bit UID (bits 95 to 64) */
    unsigned int word3; /**< Word 3 of 128-bit UID (bits 127 to 96) */
} UID_Type;

//IAP return code enumeration
typedef enum IAP_RETURN_CODE {
    IAP_CMD_SUCCESS = 0,
    IAP_INVALID_COMMAND,
    IAP_SRC_ADDR_ERROR,
    IAP_DST_ADDR_ERROR,
    IAP_SRC_ADDR_NOT_MAPPED,
    IAP_DST_ADDR_NOT_MAPPED,
    IAP_COUNT_ERROR,
    IAP_INVALID_SECTOR,
    IAP_SECTOR_NOT_BLANK,
    IAP_SECTOR_NOT_PREPARED_FOR_WRITE_OPERATION,
    IAP_COMPARE_ERROR,
    IAP_BUSY
} IAP_RETURN_CODE_T;

//IAP copy length enumeration
typedef enum IAP_COPY_LENGTH {
    IAP_COPY_256_BYTES = 256,
    IAP_COPY_512_BYTES = 512,
    IAP_COPY_1024_BYTES = 1024,
    IAP_COPY_4096_BYTES = 4096
} IAP_COPY_LENGTH_T;

//Function prototypes
IAP_RETURN_CODE_T IAP_PrepareSectors(unsigned int sector_start, unsigned int sector_end);
IAP_RETURN_CODE_T IAP_CopyRAMToFlash(void* ram_address, void* flash_address, IAP_COPY_LENGTH_T length);
IAP_RETURN_CODE_T IAP_EraseSectors(unsigned int sector_start, unsigned int sector_end);
IAP_RETURN_CODE_T IAP_BlankCheckSectors(unsigned int sector_start, unsigned int sector_end);
unsigned int IAP_ReadPartID();
unsigned short IAP_ReadBootCodeVersion();
IAP_RETURN_CODE_T IAP_Compare(void* address1, void* address2, unsigned int bytes);
void IAP_ReinvokeISP();
UID_Type IAP_ReadUID();
IAP_RETURN_CODE_T IAP_ErasePage(unsigned int page_start, unsigned int page_end);
IAP_RETURN_CODE_T IAP_WriteEEPROM(unsigned int ee_address, char* buffer, unsigned int length);
IAP_RETURN_CODE_T IAP_ReadEEPROM(unsigned int ee_address, char* buffer, unsigned int length);

#endif

IAP.cpp

#include "IAP.h"

namespace
{
//This data must be global so it is not read from the stack
unsigned int m_Command[5], m_Result[5];
typedef void (*IAP)(unsigned int [], unsigned int []);
const IAP IAP_Entry = (IAP)0x1FFF1FF1;
}

static inline void _iap_CriticalEntry()
{
    //Disable interrupts
    __disable_irq();

    //Safely perform IAP entry
    IAP_Entry(m_Command, m_Result);

    //Enable interrupts
    __enable_irq();
}

IAP_RETURN_CODE_T IAP_PrepareSectors(unsigned int sector_start, unsigned int sector_end)
{
    //Prepare the command array
    m_Command[0] = 50;
    m_Command[1] = sector_start;             //The start of the sector to be prepared
    m_Command[2] = sector_end;               //The end of the sector to be prepared

    //Invoke critical IAP call...
    _iap_CriticalEntry();

    //Extract the result code
    IAP_RETURN_CODE_T ret = (IAP_RETURN_CODE_T)m_Result[0];

    //Return the result code
    return ret;
}

IAP_RETURN_CODE_T IAP_CopyRAMToFlash(void* ram_address, void* flash_address, IAP_COPY_LENGTH_T length)
{
    //Prepare the command array
    m_Command[0] = 51;
    m_Command[1] = (unsigned int)flash_address;   //Flash address where the contents are to be copied (it should be within 256bytes boundary)
    m_Command[2] = (unsigned int)ram_address;     //RAM address to be copied (it should be in word boundary)
    m_Command[3] = (unsigned int)length;          //Number of data to be copied in bytes: 256, 512, 1024, or 4096
    m_Command[4] = SystemCoreClock / 1000;

    //Invoke critical IAP call...
    _iap_CriticalEntry();

    //Extract the result code
    IAP_RETURN_CODE_T ret = (IAP_RETURN_CODE_T)m_Result[0];

    //Return the result code
    return ret;
}

IAP_RETURN_CODE_T IAP_EraseSectors(unsigned int sector_start, unsigned int sector_end)
{
    //Prepare the command array
    m_Command[0] = 52;
    m_Command[1] = sector_start;             //The start of the sector to be erased
    m_Command[2] = sector_end;               //The end of the sector to be erased
    m_Command[3] = SystemCoreClock / 1000;   //System Clock Frequency (CCLK) in kHz

    //Invoke critical IAP call...
    _iap_CriticalEntry();

    //Extract the result code
    IAP_RETURN_CODE_T ret = (IAP_RETURN_CODE_T)m_Result[0];

    //Return the result code
    return ret;
}

IAP_RETURN_CODE_T IAP_BlankCheckSectors(unsigned int sector_start, unsigned int sector_end)
{
    //Prepare the command array
    m_Command[0] = 53;
    m_Command[1] = sector_start;                 //The start of the sector to be checked
    m_Command[2] = sector_end;                   //The end of the sector to be checked

    //Invoke critical IAP call...
    _iap_CriticalEntry();

    //Extract the result code
    IAP_RETURN_CODE_T ret = (IAP_RETURN_CODE_T)m_Result[0];

    //Return the result code
    return ret;
}

unsigned int IAP_ReadPartID()
{
    //Prepare the command array
    m_Command[0] = 54;

    //Invoke critical IAP call...
    _iap_CriticalEntry();

    //Extract the part ID
    unsigned int ret = m_Result[1];

    //Return the part ID
    return ret;
}

unsigned short IAP_ReadBootCodeVersion()
{
    //Prepare the command array
    m_Command[0] = 55;

    //Invoke critical IAP call...
    _iap_CriticalEntry();

    //Extract the boot code version
    unsigned int ret = (unsigned short)m_Result[1];
    
    //Return the boot code version
    return ret;
}

IAP_RETURN_CODE_T IAP_Compare(void* address1, void* address2, unsigned int bytes)
{
    //Prepare the command array
    m_Command[0] = 56;
    m_Command[1] = (unsigned int)address1;   //Starting flash or RAM address of data bytes to be compared. This address should be a word boundary.
    m_Command[2] = (unsigned int)address2;   //Starting flash or RAM address of data bytes to be compared. This address should be a word boundary.
    m_Command[3] = bytes;                    //Number of bytes to be compared; should be a multiple of 4.

    //Invoke critical IAP call...
    _iap_CriticalEntry();

    //Extract the result code
    IAP_RETURN_CODE_T ret = (IAP_RETURN_CODE_T)m_Result[0];
    
    //Return the result code
    return ret;
}

/* This function resets some microcontroller peripherals to reset
 * hardware configuration to ensure that the USB In-System Programming module
 * will work properly. It is normally called from reset and assumes some reset
 * configuration settings for the MCU.
 * Some of the peripheral configurations may be redundant in your specific
 * project.
 */
void IAP_ReinvokeISP()
{
    //Make sure USB clock is turned on before calling ISP
    LPC_SYSCON->SYSAHBCLKCTRL |= 0x04000;

    //Make sure 32-bit Timer 1 is turned on before calling ISP
    LPC_SYSCON->SYSAHBCLKCTRL |= 0x00400;

    //Make sure GPIO clock is turned on before calling ISP
    LPC_SYSCON->SYSAHBCLKCTRL |= 0x00040;

    //Make sure IO configuration clock is turned on before calling ISP
    LPC_SYSCON->SYSAHBCLKCTRL |= 0x10000;

    //Make sure AHB clock divider is 1:1
    LPC_SYSCON->SYSAHBCLKDIV = 1;

    //Prepare the command array
    m_Command[0] = 57;

    //Initialize the storage state machine
    *((unsigned int *)(0x10000054)) = 0x0;

    //Set stack pointer to ROM value (reset default)
    //This must be the last piece of code executed before calling ISP,
    //because most C expressions and function returns will fail after the stack pointer is changed.
    __set_MSP(*((unsigned int *)0x00000000));

    //Invoke IAP call...
    IAP_Entry(m_Command, m_Result);

    //Shouldn't return
    while(1);
}

UID_Type IAP_ReadUID()
{
    //Prepare the command array
    m_Command[0] = 58;

    //Invoke critical IAP call...
    _iap_CriticalEntry();

    //Extract the UID
    UID_Type ret = {
        m_Result[1],
        m_Result[2],
        m_Result[3],
        m_Result[4]
    };

    //Return the UID
    return ret;
}

IAP_RETURN_CODE_T IAP_ErasePage(unsigned int page_start, unsigned int page_end)
{
    //Prepare the command array
    m_Command[0] = 59;
    m_Command[1] = page_start;               //The start of the page to be erased
    m_Command[2] = page_end;                 //The end of the page to be erased
    m_Command[3] = SystemCoreClock / 1000;   //System Clock Frequency (CCLK) in kHz

    //Invoke critical IAP call...
    _iap_CriticalEntry();

    //Extract the result code
    IAP_RETURN_CODE_T ret = (IAP_RETURN_CODE_T)m_Result[0];

    //Return the result code
    return ret;
}

IAP_RETURN_CODE_T IAP_WriteEEPROM(unsigned int ee_address, char* buffer, unsigned int length)
{
    //Prepare the command array
    m_Command[0] = 61;
    m_Command[1] = ee_address;               //EEPROM address (byte, half-word or word aligned)
    m_Command[2] = (unsigned int)buffer;     //RAM address (byte, half-word or word aligned)
    m_Command[3] = length;                   //Number of bytes to be written (byte, half-word writes are ok)
    m_Command[4] = SystemCoreClock / 1000;   //System Clock Frequency (CCLK) in kHz

    //Invoke critical IAP call...
    _iap_CriticalEntry();

    //Extract the result code
    IAP_RETURN_CODE_T ret = (IAP_RETURN_CODE_T)m_Result[0];

    //Return the result code
    return ret;
}

IAP_RETURN_CODE_T IAP_ReadEEPROM(unsigned int ee_address, char* buffer, unsigned int length)
{
    //Prepare the command array
    m_Command[0] = 62;
    m_Command[1] = ee_address;               //EEPROM address (byte, half-word or word aligned)
    m_Command[2] = (unsigned int)buffer;     //RAM address (byte, half-word or word aligned)
    m_Command[3] = length;                   //Number of bytes to be read (byte, half-word reads are ok)
    m_Command[4] = SystemCoreClock / 1000;   //System Clock Frequency (CCLK) in kHz

    //Invoke critical IAP call...
    _iap_CriticalEntry();

    //Extract the result code
    IAP_RETURN_CODE_T ret = (IAP_RETURN_CODE_T)m_Result[0];

    //Return the result code
    return ret;
}


NOTE: Be careful with the flash programming commands, as they can corrupt the top 32B of RAM (which is used by the stack). A lot of people use them without issue, so I'm not sure of the circumstances under which it happens, but the datasheet warns about it, and Code Red has instructions for offsetting the stack to prevent it.

Accepted Answer