Entering ISP Mode from User Code

02 Aug 2010 . Edited: 02 Aug 2010

I found this technote(ics.nxp.com/support/documents/microcontrollers/pdf/an10356.pdf) at NXP site, showing ISP entry from user code for the LPC21xx series. I've tried to adapt the following to existing code to the LPC1768 using the online compiler. I think my problem is remapping the interrupt vectors can anyone shed some light on this. Should this still be possible?

The steps involved in entering ISP mode from user code are as follows: (LPC21xx)

• Configure the RXD0 pin (UART 0 receive) as input. This is done by entering the appropriate values in the PINSEL0 and IODIR0 registers. 
• Configure P0.14 as an output pin. 
• Clear P0.14 (Set low)  
• Disable interrupts by setting the corresponding bits in the VIC Interrupt Enable Clear register (VICIntEnClear at address 0xFFFF F014) for anyinterrupt source that was previously enabled for interrupts. 
• If the PLL is connected, disconnect it (Recommended).  
• Set the Peripheral Bus Divider to ¼ if needed.  
• If the UART is equipped with a Fractional Baud Rate generator then the Fractional Divider register (FDR) should be set to its reset value (This is not shown in the code below). 
• Restore Timer1 to its reset state. Timer 1 is used by the ISP to auto baud. 
• Re-Map the interrupt vectors to the boot block. 
• Invoke the bootloader by calling a function that is located at the bootloader entry point, i.e. the reset vector at 0x00.

 

My current LPC1768 version

#include "mbed.h"
#include "LPC17xx.h" 

#define MEMMAP   (*((volatile unsigned int *) 0x400FC040))
#define VICINTENCLR  (*((volatile unsigned int *) 0xffffffff))

void (*bootloader_entry)(void);
unsigned long temp;

DigitalOut myled(LED_1);
DigitalOut myled2(LED_2);
Serial pc(P0_2, P0_3); // tx, rx

void init(void) {
   
    temp = LPC_PINCON->PINSEL0;
    /* Connect RXD0 & TXD0 pins to GPIO */
    LPC_PINCON->PINSEL0 = temp & 0xFFFFFFF3;

    /* Select P2.10 as an output and P0.1 as an input */   
    temp = LPC_GPIO2->FIODIR;//LPC_PINCON->IODIR0;
    temp = temp | 0x400;
    temp = temp & 0xFFFFFFFD;
    LPC_GPIO2->FIODIR  = temp;// LPC_PINCON->IODIR0 = temp;

    /* Clear P2.10 */
    LPC_GPIO2->FIOCLR =  0x400; // 1<<10;// IOCLR0 = 0x4000;

    /* Disable Interrupts in the VIC*/
    VICINTENCLR =0x0;
    
    /*
       Disconnect PLL if you want to do ISP at crystal frequency.
       Otherwise you need to pass the PLL freq when bootloader goes in
       ISP mode.
       cclk = crystal when PLL is disconnected
       cclk = PLL freq when PLL is connected.

       Disconnecting the PLL is recommended.  */

    LPC_SC->PLL0CON = 0x0;
    LPC_SC->PLL0FEED = 0xAA;
    LPC_SC->PLL0FEED= 0x55;

    /*
       Set the VPB divider to 1/4 if your application changes the VPBDIV value.
       The bootloader is hard-coded to use the reset value of VPBDIV register
       VPBDIV = 0x0;
    */

    /* Restore reset state of Timer1 */
    LPC_TIM1->PR =0x0;//TIMER1_PR=0x0;
    LPC_TIM1->MCR =0x0;//TIMER1_MCR=0x0;
    LPC_TIM1->CCR =0x0;//TIMER1_CCR=0x0;

    /* Map bootloader vectors */
    MEMMAP = 0x0;

    /* Point to bootloader entry point i.e. reset vector 0x0 */
    bootloader_entry = (void (*)(void))(0x0);
}

int main() {
    int ten = 60;
    pc.baud(9600);
    while (1) {
        myled = 1;
        myled2 = !myled2;
        wait(0.2);
        myled = 0;
        wait(0.2);
        pc.printf("Count Down to ISP! %d", ten);
        ten=ten-1;
        if (ten==0) {
            ten=60;
            init();
            bootloader_entry();
        }

    }
}
-deleted-
26 Aug 2010

Any luck with this - I am going to take a stab at it.

 

Sam

-deleted-
27 Aug 2010 . Edited: 27 Aug 2010

 To use the ISP Bootloader you need to re-invoke it through the IAP.  I decided to use the factory default cclk setting rather than the PLL at 96MHz (either should work).

Re-InvokeISP

This class could easily be updated to change the cclk and make the reinvoke call.

http://mbed.org/users/okano/programs/IAP_internal_flash_write/5ypjo/docs/

The LPC17xx usermanual has all the details needed for ISP and IAP communication in chapter 32 (mainly section 7 & 8)

http://www.keil.com/dd/docs/datashts/philips/lpc17xx_um.pdf

Good Luck

Sam

 

27 Aug 2010

awesome work Sam, ill give it a try tonight!

23 Jul 2011

Sam,

Just posting to say, thanks for the code, it works for me.

For anyone else curious about the details of the code... some further explanation of the basic steps:

  • disable IRQs (so an interrupt won't disrupt the sequential loads to the PLL0FEED registers, see user manual section 4.5.8)
  • set PLL0CON = 0x01 to take the system off PLL (with mbed default divider of /3, core speed is now 4MHz - see this thread for details about the clock configuration). Wait for the flag to read back the update.
  • set PLL0CON = 0x00 to power down the PLL unit. Wait for the flag to read back the update.
  • set FLASHCFG = 0x5??? (the bottom 12 bits must be preserved, hence the read). This tells the flash interface to wait 6 cycles between operations, the "safe" setting for all CPU clocks.
  • set CCLKCFG = 0x00, sets the clock input postscaler to divide by 1. Core is now running at the input clock of 12MHz for the moment, since we haven't moved to int RC yet... but we will on the next instruction.
  • set CLKSRCSEL = 0x00, chip is now using the internal RC oscillator (default 4MHz)
  • set SCS = 0x00, tells it that the external clock signal should not be used
  • sets up a function pointer to the pre-defined location for the IAP code. This is because, as Sam mentioned above, you can't get directly into ISP: rather, you have to call the IAP function with an argument of 57 (dec) which will then put you in ISP.

One question: my application makes use of the on-board watchdog timer. However, it seems that the WDT is not disabled when the system goes into ISP mode, so the WDT keeps running and the board resets after some time. I suppose there's no way to disable the WDT... (which is probably by design, heh) before entering ISP? Seems kind of unfortunate...

23 Jul 2011

Can you not manually disable the watchdog timer before entering ISP?

I think this code might disable it:

LPC_WDT->WDMOD = 0x0;
25 Jul 2011

Adam Green wrote:

Can you not manually disable the watchdog timer before entering ISP?

I think this code might disable it:

LPC_WDT->WDMOD = 0x0;

Regarding WDMOD, Section 28.4.1 of the user manual states:

Quote:

Once the WDEN and/or WDRESET bits are set they can not be cleared by software. Both flags are cleared by an external reset or a Watchdog timer underflow.

So, this is not possible, but that's by design - the intent is that once you've set the WDT, under no circumstances can it be disabled by rogue code.

My intended solution to the problem makes use of an external EEPROM. Once my application receives a serial command to enter bootloader mode, it will do the following:

  • write a special code to a given location in EEPROM
  • force a reset in software
  • during the boot sequence, before the WDT is initialized, it will check this EEPROM location for the code. If found, it will clear the code and enter bootloader mode (skipping WDT init altogether).

The net effect is the same... Haven't implemented it yet but I don't expect any problems.

25 Jul 2011

Yeah, I missed that even though that is the exact part of the document I was reading :)

I guess you could also set the WDTC to 0xFFFFFFFF, feed the watchdog and then enter ISP but it would still timeout but after a much longer period of time.

26 Jul 2011

Instead of a special code in EEPROM, you might consider writing to the few words of battery backed memory related to the RTC chip (if you have a battery backup). You could put a small signature in there to detect on the next power cycle. In the LPC data sheet this is in section 7.28 and it says there are 20 bytes of battery backed space.

26 Jul 2011

Does the static RAM get modified by a watchdog timeout? I know that the CRT startup code will initialize statics but what about the rest of RAM? If they weren't modified by the LPC hardware during reset then maybe you could check the 'Reset Source Identification Register' to see if the cause was the watchdog and if so, check for the value in RAM instead. Using the highest word in one of the upper banks should be safe since the stack and heap don't go there and you can always check to make sure that the compiler/linker didn't fill in these regions.