LPC2100: Entering ISP mode from user code

Skip to main content (skip navigation menu)
Letterhead logo






LPC2100: Entering ISP mode from user code

 

"Entering ISP mode from user code" is the title of NXP application note AN10356. Here, I explain the trick and simplify the code from the application note. The focus is on an "in-field" firmware update procedure.

The LPC2100 microcontroller series is built on the ARM7 core and provides a generous assortment of on-chip peripherals. To load the firmware into its internal Flash ROM, the LPC2100 series support In-System Programming (ISP) via a bootloader and In-Application Programming for updating firmware from code that runs in SRAM. In-System Programming is an attractive option for in-field firmware update, because the bootloader supports code uploading via an RS232.

The standard way to start ISP is to hold pin P0.14 low while issuing a processor reset. Many designs provide two switches (or a jumpers) for the P0.14 and reset lines. For an in-field update, this may be inconvienent ("cumbersome" is the word NXP uses in relation to this procedure) because you will have to ask the customer to push these switches (and to release the reset switch before releasing the P0.14 switch). The "LPC2000 Flash ISP Utility" and Flash Magic support a design (which they describe in the application note AN10302) where the RTS signal (from the RS232 plug) is wired to the reset and DTR is wired to P0.14. The "LPC2000 Flash ISP Utility" and "Flash Magic" can then toggle the RTS and DTR lines in the appropriate sequence. This, in turn, may also become cumbersome if you want to use the serial port of the LPC2000 microcontroller for other purposes too (i.e. other than just firmware update). In that case, however, you cannot simply assume that the external hardware (e.g. a simple terminal, a barcode scanner, a ticket printer) does not use the DTR and RTS signals; if it does, it might inadvertently reset the microcontroller or toggle the P0.14 pin which is likely to be used for a completely unrelated purpose in field operation. The assertion of NXP that the RTS and DTR signals are "unused" refers only to the serial port design of the LPC2000 series. Computer systems and peripherals that communicates through RS232 interfaces do toggle the RTS and DTR signals. Obviously, if your design hard-wires these signals to reset and P0.14, this will lead to "interesting" support calls.

In brief, the two options for firmware updates discussed so far are both undesirable:

There has always existed a third option, "In Application Programming", but this is quite a bit more difficult to implement, especially if your microcontroller has far more Flash ROM than SRAM (e.g. the LPC2138).

Then came application note AN10356: a programmatic switch to ISP. The advantage of this design is that your firmware can use the RS232 itself for any particular purpose, and decide to switch to ISP (for a firmware update) on receiving a special instruction. No switches need to be pressed by the customer doing the firmware update, and no danger exists in external hardware to toggle "reserved" lines. No special circuitry needs to be attached to the microcontroller either. And the use of the bootloader in ISP mode allows a simple firmware update program; you can even use the existing utilities, such as the "LPC2000 Flash ISP Utility" and Flash Magic.

How it works

The NXP manuals and the application note AN10302 describe in detail the operation of the LPC2000 after a reset. In brief, after approximately 3 ms it checks the status of pin P0.14 and decides whether to branch to ISP mode or to proceed with the user code (i.e. "firmware") already in Flash ROM. (I am ignoring the special case where there is no valid user program in Flash ROM and the case of a reset invoked from the watchdog timer.) If you jump, from inside the user code, to the reset address, the bootloader code of the microcontroller will behave as if it came from a reset, and check pin P0.14 to optionally enter ISP. The trick is to configure pin P0.14 as "output" and to drive it low. The bootloader code will then still see it low 3 ms later.

As a side note: processors from the LPC2300 series use GPIO pin P2.10 instead of P0.14. Moreover, the "bootloader" firmware of the newer processor types in the LPC2000 series provide an IAP command to invoke ISP, regardless of the state of P0.14 (or P2.10).

On a true reset, all gpio pins will have been toggled to "input" and the only way that pin P0.14 can be down is through a switch, a jumper or external logic. Jumping to the bootloader address is not a true reset, however, and if your firmware configures pin P0.14 as "output" (and drives it low), it will still be a "low output" when the bootloader code checks the pin. That is, the bootloader does not re-configure the pin's I/O direction. Note that you can still read a gpio pin's value if it is configured as output: the iopin register will just return the same value that you wrote to the pin earlier.

The remainder of the necessary code to enter ISP mode is to return the microcontroller to its state as if it came from a reset. This is needed because the bootloader code assumes that the microcontroller is in that state. So the code snippet below configures all pins to gpio, powers-up all devices, disconnects the Phase Locked Loop (PLL) and sets the peripheral bus to low speed, and then jumps to the reset address.

StartISP()
void StartISP(unsigned long wdticks)
{
    void (*bootloader_entry)(void) = (void*)0;

    /* reset PINSEL (set all pins to GPIO) */
    PCB_PINSEL0 = 0x00000000;
    PCB_PINSEL1 = 0x00000000;

    /* reset GPIO, but drive P0.14 low (output) */
    GPIO_IODIR = 0x00004000;    /* P0.14 = output, others are input */
    GPIO_IOCLR = 0x00004000;

    /* power up all peripherals */
    SCB_PCONP = 0x000003be;     /* for LPC2104/5/6
                                 * use 0x001817be for LPC2131/2/8 */

    /* disconnect PLL */
    SCB_PLLCON = 0x00;
    SCB_PLLFEED = 0xaa; SCB_PLLFEED = 0x55;

    /* set peripheral bus to 1/4th of the system clock */
    SCB_VPBDIV = 0x00;

    /* map bootloader vectors */
    SCB_MEMMAP = 0;

    /* optionally set up a watchdog timer to exit ISP mode */
    if (wdticks != 0) {
        WD_WDTC = wdticks;
        WD_WDMOD = 0x03;
        WD_WDFEED = 0xaa; WD_WDFEED = 0x55;
    } /* if */

    /* jump to the bootloader address */
    bootloader_entry();
}

Your firmware can now enter ISP mode by simply calling StartISP(). While running user code, the firmware also needs to check for some criterion to start ISP mode. A straigtforward solution is to accept a special request on the serial port, possibly with a challenge/handshake protocol to avoid unintentional activation of ISP mode.

Apart from setting the watchdog timer, which I will come to in a moment, the code snippet on this page is quite in line with that in application note AN10356. My version resets a few more registers (which makes my version a little more robust) and I do not bother keeping some pins or registers in a non-reset state (which makes the code shorter and simpler).

Obviously, if you want to add all this in a stream-lined firmware update procedure, the "LPC2000 Flash ISP Utility" by itself will not do: it does not know how to enter ISP mode from your code. You can, of course, make a tiny program that starts ISP mode and then launches the NXP/Flash Magic utility. Both these utiilities have a command line interface. Alternatively, you can build your own upload utility —the protocol is fairly simple.

The "bootloader" firmware of current LPC2000 processors provide an IAP command to invoke ISP, regardless of the state of P0.14 (or P2.10 for the LPC2300 series). Instead of jumping to address zero, you can therefore issue IAP command 57 to start ISP, and you may also skip pulling pin P0.14 (or P2.10) low. You still need to reset the PLL and peripheral clock.

Exiting ISP mode

There is one slight complication in the procedure described so far: after having refreshed your firmware, you will want to restart it, and run automatically with the new code. The LPC2100 bootloader has a special command for jumping to an address that you specify: "G <address>". What the lpc21isp utility does is to jump to address 0; this re-invokes the bootloader which, in the normal case, finds the correct signature and pin P0.14 up, so that it jumps to the user code. After our trick, however, jumping to address 0 is not going to work: pin P0.14 is still down. The only option that I have found that works reliably is to cause a reset from the Watchdog Timer. The bootloader does not "pulse" the watchdog timer and therefore the watchdog timer will "fire" after the configured number of ticks and reset the microcontroller: a true reset, which toggles pin P0.14 to input mode, so that the bootloader will see it "up" and proceed with the brand new firmware.

I have left it to the caller of the code to determine the number of ticks that the watchdog timer must delay before resetting the chip. It is often easiest to configure this count from the firmware update program, e.g. during the handshaking protocol to enter ISP mode that I mentioned earlier. To set the appropriate value, you need to determine the tick rate of the watchdog timer, and the time that you need to load the new firmware in the microcontroller. Probably you will want to add a savety margin to avoid resetting the chip before the update has completed.

The watchdog timer ticks at a rate of 1/4th of the peripheral clock, StartISP() has just reset to the peripheral clock to run at 1/4th of the CPU clock, and the CPU clock is reset to the frequency of the crystal. Therefore, the watchdog timer tick rate is the crystal frequency divided by 16. Note that your firmware update program will need to know the crystal frequency anyway, because it is part of the handshaking protocol of the bootloader itself.

The other required estimate is the time needed to update the firmware, which depends on the size of the firmware and the serial protocol —notably the baud rate. The boot loader uses a frame with 8 data bits, 1 stop bit and no parity; thus, including the start bit, the serial frame is 10 bytes. At 9600 Baud, you will transmit 960 bytes per second. The data must be in UU-encoded format and this format expands the binary image data by approximately a factor 1.37 (4 ASCII characters for every three bytes, plus a length byte for every 45 bytes and a checksum for every 20 lines, or 900 bytes). However, since the upload utility also has to issue instructions to prepare and erase sectors (before writing them), to sail on the safe side, a factor of 1.6 or 1.7 is more suitable. I recommend that you measure the required upload time at the Baud rate that you wish to use, and add a few seconds for the handshaking protocol for the bootloader and the occasional lost packet.

Further reading & references

AN10302 - Using the Philips LPC2000 Flash utility with the Keil MCB2100 and IAR LPC210x Kickstart evaluation boards
A description of the wiring for using RTS/DTR to switch to the bootloader.
LPC2000 Flash ISP Utility
The "LPC2000 Flash ISP Utility"; this program is no longer maintained and it may not support the newer processors in the LPC2000 series. NXP now advises to use the Flash Magic tool (see below).
Flash Magic
An ISP program developed by Embedded Systems Academy, sponsored by NXP.
lpc21isp
An Open Source alternative Flash uploader utility for, amongst others, the LPC2100 series, which runs under Windows and Linux; by Martin Maurer. The latest versions of the utility are only available on a Yahoo group; to download it, you must join the group.