This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Problem with IAP on LPC2138

The IAP is driving me crazy. When I single step my code using ulink it works, but when I just let it run the micro reboots itself and the IAP hasn't worked. I've read the manual, AN10256, searched the forums and looked at the sample code.

Here's my code:

ErrorCode_t writeBufToOneFlashSector(uint8_t secNum)
{
    BOOL good = TRUE;
    ErrorCode_t retCode = ERR_NO_ERROR;
        unsigned int enables;
    BOOL pllStat;

    // Disable interrupts during erase and write operations
    // Keep a copy of what was enabled
    enables = VICIntEnable;
    command[0] = IAP_PrepareSectors;
    command[1] = (uint32_t)secNum;
    command[2] = (uint32_t)secNum;
    waitTXComplete();
    pauseTX(TRUE);
    pllStat = usePLL(FALSE);
    iap_entry(command, result);
    good = (BOOL)(IAP_CMD_SUCCESS == result[0]);
    if(!good)
    {
        usePLL(pllStat);
        retCode = ERR_IAP_ERASE_PREPARE | ERR_NUM_USED;
        errVal = (ParamType)result[0];
    }
    else
    {
        command[0] = IAP_EraseSectors;
        command[1] = (uint32_t)secNum;
        command[2] = (uint32_t)secNum;
        command[3] = XTAL_OSC_KHZ;
        // Disable interrupts during erase
        VICIntEnable = 0;
        iap_entry(command, result);
        VICIntEnable = enables;
        good = (BOOL)(IAP_CMD_SUCCESS == result[0]);
        if(!good)
        {
            usePLL(pllStat);
            retCode = ERR_IAP_ERASE | ERR_NUM_USED;
            errVal = (ParamType)result[0];
        }
    }

    // See about doing the write
    if(good)
    {
        command[0] = IAP_PrepareSectors;
        command[1] = (uint32_t)secNum;
        command[2] = (uint32_t)secNum;
        iap_entry(command, result);
        good = (BOOL)(IAP_CMD_SUCCESS == result[0]);
        if(!good)
        {
            usePLL(pllStat);
            retCode = ERR_IAP_WRITE_PREPARE | ERR_NUM_USED;
            errVal = (ParamType)result[0];
        }
    }

    // Ready to write at last
    if(good)
    {
        command[0] = IAP_CopyRAMtoFlash;
        command[1] = SectorStartAddr[secNum];
        command[2] = (unsigned int)nvBuf;
        command[3] = FLASH_NV_PAGE_SIZE8;
        command[4] = XTAL_OSC_KHZ;
        // Disable interrupts during erase
        VICIntEnable = 0;
        iap_entry(command, result);
        usePLL(pllStat);
        VICIntEnable = enables;
        good = (BOOL)(IAP_CMD_SUCCESS == result[0]);
        if(!good)
        {
            retCode = ERR_IAP_WRITE | ERR_NUM_USED;
            errVal = (ParamType)result[0];
        }
    }
    pauseTX(FALSE);
    return retCode;
}

Parents Reply Children
  • It works very well with the LPC23xx series, and I would expect it to work well with the 21xx series too. I haven't seen any information that the support in the 21xx series should be broken.

    You haven't mentioned at what place in the sequence that you get the reboot.

  • I haven't figured out where it reboots because when I single step using ULink it works. My serial comms is queued and interrupt driven, so scattering putchars through the code doesn't work. Wiggling port pins may be an option I suppose.

  • I don't know your chip, but can you do that thing with the PLL ("usePLL(pllStat);") just like that? what happens if you remove it at the end...?

  • The documentation is quite specific that you must use the raw oscillator and not the PLL oscillator. What I do is store whether the PLL was in use, turn the PLL off, and then restore the PLL state when I'm done.

  • but are you sure you follow the data sheet by the letter on how to do that?

  • That bit was lifted out of the published code snippet. It looks like this:

    BOOL usePLL (BOOL useIt)
    {
        BOOL pllConStat;
    
        // PLL must be enabled AND connected
        pllConStat = (BOOL)((PLL_STAT & PLLSTAT_PLLE_MASK) == PLLSTAT_PLLE_MASK);
        // Start the PLL if it's stopped
        if(useIt && !pllConStat)
        {
            start_pll();
        }
        pllConStat = pllConStat && (BOOL)((PLL_STAT & PLLSTAT_PLLC_MASK) == PLLSTAT_PLLC_MASK);
        // Connect or disconnect as required
        if(useIt)
        {
            connect_pll();
        }
        else
        {
            disconnect_pll();
        }
        return pllConStat;
    }
    
    void connect_pll (void)  {
      __asm  {
            LDR     R0, =PLL_BASE
            MOV     R1, #0xAA
            MOV     R2, #0x55
    
            // Switch to PLL Clock
            MOV     R2, #0x55
            MOV     R3, #(PLLCON_PLLE_ENA + PLLCON_PLLC_ENA)
            STR     R3, [R0, #PLLCON_OFS]
            STR     R1, [R0, #PLLFEED_OFS]
            STR     R2, [R0, #PLLFEED_OFS]
      }
    }
    
    void start_pll (void)  {
      __asm  {
            LDR     R0, =PLL_BASE
            MOV     R1, #0xAA
            MOV     R2, #0x55
    
            // Enable PLL
            MOV     R3, #(PLLCON_PLLE_ENA + PLLCON_PLLC_DIS)
            STR     R3, [R0, #PLLCON_OFS]
            STR     R1, [R0, #PLLFEED_OFS]
            STR     R2, [R0, #PLLFEED_OFS]
    
            // Wait until PLL Locked
            LDR     R2, =PLLSTAT_PLOCK
        PLL_Loop:
            LDR     R3, [R0, #PLLSTAT_OFS]
            CMP     R3, R2
            BEQ     PLL_Loop
      }
    }
    
    
    /*
     * Switch CPU to standard XTAL but leave PLL enabled
     */
    void disconnect_pll(void) __arm  {
      __asm  {
            LDR     R0, =PLL_BASE
            MOV     R1, #0xAA
            MOV     R2, #0x55
    
            // Disconnect PLL but leave it enabled
            MOV     R3, #(PLLCON_PLLE_ENA + PLLCON_PLLC_DIS)
            STR     R3, [R0, #PLLCON_OFS]
            STR     R1, [R0, #PLLFEED_OFS]
            STR     R2, [R0, #PLLFEED_OFS]
      }
    }
    
    /*
     * Switch CPU to standard XTAL and disable PLL
     */
    void stop_pll(void) __arm  {
      __asm  {
            LDR     R0, =PLL_BASE
            MOV     R1, #0xAA
            MOV     R2, #0x55
    
            // Disable PLL
            MOV     R3, #(PLLCON_PLLE_DIS + PLLCON_PLLC_DIS)
            STR     R3, [R0, #PLLCON_OFS]
            STR     R1, [R0, #PLLFEED_OFS]
            STR     R2, [R0, #PLLFEED_OFS]
      }
    }
    

  • Generate pulses on a single processor pin and use a digital scope to count the number of pulses before the reboot. That should allow you to pin-point where the reset is.

    Repeat a number of times to figure out if the reset point is fixed or is jittering. Jittering would often be caused by some interrupt source not being deactivated.

    Then add delays at different steps of the code and check how that affects the reset point. People who gets hit by the watchdog would quickly notice a big difference during this test.

    Are you sure that you don't have any DMA transfers ongoing?

    If I remember correctly, the IAP routine needs a number of bytes of RAM to operate - any collision with your memory use, for example with a stack?

    Have you tried to contact a NXP support engineer?

  • I added some code to bash a PIO pin up and down, so now I know the IAP call to erase the flash is where it gets lost. The start of main() then happens somewhere between 401msec and 401.2msec later.

    The LPC2138 doesn't seem to have a DMA capability, and I don't use it.

    I don't enable the watchdog.

  • Hi,

    I don't know much about this. But I am trying to do some IAP on my LPC2378.

    Have you downloaded the below source code?

    http://www.keil.com/download/docs/298.asp

    It should work on the MCB2130.

    And, how do you convert 'addr' to sector number?

    /*
     * Convert 'addr' to sector number
     */
    unsigned int get_secnum (void *addr)  {
      unsigned int n;
    
      n = ((unsigned int) addr >> 13) & 0x1F;        // pseudo sector number
    
      if (n >= (0x30000 >> 13))  {
        n -= 14;                                    // high small 8kB Sectors (
      }
      else if (n >= (0x10000 >> 13))  {
        n  = 7 + (n >> 3);                          // large 64kB Sectors
      }
      return (n);                                   // sector number
    }
    

  • Hi,

    Sorry, that source code is for CARM.

    But you should have a similar example installed on your KEIL PC.

  • Hi,

    I just did some checks; and found something strange.


    I've read the manual, AN10256, searched the forums and looked at the sample code.

    Where do you get that sample code?
    What is the size of your flash memory?
    How do you convert 'addr' to sector number?

  • I don't turn an address into a sector number, I turn a sector number into an address. I do that by lookup table. I'm using two of the 8kB sectors (one primary, one duplicate backup) as that's easily enough for my application.

    I can't specifically remember that I got the code snippets from anywhere other than the app notes.

  • Hi Oliver,

    Please refer to:

    UM10120
    LPC2131/2/4/6/8 User manual
    Rev. 02 - 25 July 2006 User manual LPC213x

    www.standardics.nxp.com/.../user.manual.lpc2131.lpc2132.lpc2134.lpc2136.lpc2138.pdf

    Page 242, Table 223. Flash sectors in LPC2131, LPC2132, LPC2134, LPC2136 and LPC2138

     0  4 0X0000 0000 - 0X0000 0FFF + + + + +
          [del]
     7  4 0X0000 7000 - 0X0000 7FFF + + + + +
     8 32 0x0000 8000 - 0X0000 FFFF + + + +
          [del]
    21 32 0x0007 0000 - 0X0007 7FFF +
    22  4 0x0007 8000 - 0X0007 8FFF +
          [del]
    26  4 0x0007 C000 - 0X0007 CFFF +
    

    It seems that, you don't have any 8K sectors.

  • Hi Oliver,

    I found something improper in the source code of the below URL.

    http://www.keil.com/download/docs/298.asp

    And, what I used was from somewhere like "C:\Keil\ARM\Boards\Keil\MCB2100\IAP"; it worked.

    I had to provide a correct get_secnum() function to this example. After some checks and tests, it worked.

    (Not so sure about how to integrate this IAP example to a project.)

  • You're right. I was confusing myself as I actually use 4k byte sectors.