Author Topic: ICP programmer for obsolete LPC9xxx MCU  (Read 2053 times)

0 Members and 1 Guest are viewing this topic.

Offline grg183Topic starter

  • Contributor
  • Posts: 41
  • Country: mt
    • Salitronic
ICP programmer for obsolete LPC9xxx MCU
« on: December 23, 2019, 10:47:37 pm »
I've come across an old board with the LPC9361 which I need to re-program. The interface available is ICP but I am not sure which programmer I need. All online searches point to the FDI USB-ICP-LPC9xx but this has long been obsolete. Is there any current programmer that can do ICP programming on the LPC9361?


Note: removing the chip from the board is not an option and only the ICP interface is available.



 

Offline coromonadalix

  • Super Contributor
  • ***
  • Posts: 5895
  • Country: ca
Re: ICP programmer for obsolete LPC9xxx MCU
« Reply #1 on: December 25, 2019, 01:36:46 am »
Maybe  send them an email  ?,  you have some docs and softwares there :
https://www.teamfdi.com/product-details/usb-icp-lpc9xx

 Oh    i see you asked the same thing  on NXP forums  and had an answer

good luck
« Last Edit: December 25, 2019, 01:39:35 am by coromonadalix »
 

Offline grg183Topic starter

  • Contributor
  • Posts: 41
  • Country: mt
    • Salitronic
Re: ICP programmer for obsolete LPC9xxx MCU
« Reply #2 on: December 25, 2019, 11:56:09 am »
Yeah I contacted FDI and they just confirmed it is obsolete with no replacement, my understanding is this was based on an old LPC which is itself also obsolete.
Similarly NXP pointed me to a bunch of obsolete stuff.

I think my best bet is to build an adapter for a universal chip programmer such as the Galep-5 which supports the LPC9xxx and from there interface to the ICP pads on the PCB. I was hoping I could find a simple programmer.

If anyone has one of those FDI programmers and is interested in selling it please let me know.
 

Offline coromonadalix

  • Super Contributor
  • ***
  • Posts: 5895
  • Country: ca
Re: ICP programmer for obsolete LPC9xxx MCU
« Reply #3 on: December 25, 2019, 02:08:36 pm »
My programmer can do it by isp  I have an Elnec beeprog+

I dont think your near me  loll  You should complete your profile ?
 

Offline pdove

  • Newbie
  • Posts: 1
  • Country: us
Re: ICP programmer for obsolete LPC9xxx MCU
« Reply #4 on: December 30, 2019, 01:40:20 pm »
I made my own programmer with an Arduino let me know if you are interested and I’ll tell you how. I was using it to program an  NXP P89LPC938.
 

Offline grg183Topic starter

  • Contributor
  • Posts: 41
  • Country: mt
    • Salitronic
Re: ICP programmer for obsolete LPC9xxx MCU
« Reply #5 on: December 30, 2019, 03:28:55 pm »
@coromonadalix Unfortunately ISP is not an option, I only have ICP programming pins accessible.

@pdove If you can share any info on how you did it that would be great. I was thinking of trying to implement it myself based on the ICP spec document https://www.teamfdi.com/wp-content/uploads/ICP_LPC9xx_v24.pdf
 

Offline jesuscf

  • Frequent Contributor
  • **
  • Posts: 499
  • Country: ca
Re: ICP programmer for obsolete LPC9xxx MCU
« Reply #6 on: December 30, 2019, 08:25:29 pm »
@pdove If you can share any info on how you did it that would be great. I was thinking of trying to implement it myself based on the ICP spec document https://www.teamfdi.com/wp-content/uploads/ICP_LPC9xx_v24.pdf

A few years back I wrote a program to ICP an LPC9351 from another LPC9351.  It should be portable to something that is less obsolete like an Arduino.  The ICP documentation I used was more recent than the one you linked, but I can not find it with google.  The one I used has the title "P89LPC900 In-Circuit Programming (ICP) Specifications" and the date is "2006 Apr 10".  Here is the C program.  Sorry for the mixing of tabs and spaces...

Code: [Select]
/*  ISP2ICP.c: ISP bridge for NXP LPC900 microcontrollers.  Based on
    "P89LPC900 In-Circuit Programming (ICP) Specifications" (2006 Apr 10)

    by jesuscf

    This program is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by the
    Free Software Foundation; either version 2, or (at your option) any
    later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */

#include <stdio.h>
#include <p89lpc9351.h>

#define XTAL 7373000L
#define BAUD 19200L
#define BRGR ((XTAL/BAUD)-16)

#define TIMER0_1ms_RELOAD (65536L-((XTAL)/(2*1000L)))
#define TIMER0_15us_RELOAD (65536L-((XTAL)/(2*66667L))) // 1/66667Hz = 15us

#define VDD    P0_0
#define PDA    P0_1
#define PCL    P0_2
#define RESET  P0_3
#define LED    P0_4
#define CLONE  P0_5

// Table 3: Programming commands
#define NOP          0x00 // NOP
#define WR_FMADRL    0x08 // Write address low command
#define RD_FMADRL    0x09 // Read address low command
#define WR_FMADRH    0x0A // Write address high command
#define RD_FMADRH    0x0B // Read address high command
#define WR_FMCON     0x0E // Write a command to FMCON
#define RD_FMCON     0x0F // Read a command from FMCON
#define WR_FMDATA_PG 0x14 // Write a command to FMDATA and increment address
#define RD_FMDATA_PG 0x15 // Read a command from FMDATA and increment address
#define WR_FMDATA    0x0C // Write a command to FMDATA
#define RD_FMDATA    0x0D // Read a command from FMDATA
#define WR_FMDATA_I  0x04 // Write a command to FMDATA and increment address
#define RD_FMDATA_I  0x05 // Read a command from FMDATA and increment address

// Table 4: FMCON Write commands
#define LOAD  0x00 // Clear and then load the page register
#define PROG  0x48 // Program page with page register command
#define ERS_S 0x71 // Erase sector command
#define ERS_P 0x70 // Erase page command
#define CONF  0x6C // Acces user configuration information addressed by FMADRL
#define CRC_G 0x1A // Calculate CRC on the entire user code command
#define CRC_S 0x19 // Calculate CRC on a sector command
#define CCP   0x67 // Clear configuration protection

#define MAXBUFF 64
unsigned char buff[MAXBUFF];
unsigned char size, type, checksum;
unsigned int address;
char echo;
bit autobaud_done=0;
   
// ISP record types.  Check (for example) NXP's manual UM10308
// table 132: "In-system Programming (ISP) hex record formats"
enum ISP_FUNCTION {
    PROGRAM,
    READ_VERSION,
    MISC_WRITE,
    MISC_READ,
    ERASE,
    SECTOR_CRC,
    GLOBAL_CRC,
    LOAD_BAUD,
    RESET_MCU
} ISP_Function;

unsigned char _c51_external_startup (void)
{
// Set ports 0 and 1 to bidirectional mode.
    P0M1 = 0;
    P0M2 = 0;
    P1M1 = 0;
    P1M2 = 0;
    // Initialize ICP signals
    VDD=1; // 1:0ff, 0:On
    RESET=0;
    PDA=0;
    PCL=0;
    // Initialize push button and led pins
    LED=1;
    CLONE=1; // Used as input
    // Initialize UART and baud rate generator
    BRGCON=0;
    BRGR0=BRGR%0x100;
    BRGR1=BRGR/0x100;
    BRGCON=0x03;
    SCON=0x52;
   
    return 0;
}

void putchar (char c)
{
    while (!TI);
    TI=0;
    SBUF=c;
}

char getchar(void) // With echo!
{
    char c;
    while (!RI);
    RI=0;
    c=SBUF;
    putchar(c);
    return c;
}

void Send_echo (void)
{
    putchar(echo);
    putchar('\r');
    putchar('\n');
}

void Autobaud (void)
{
    unsigned int j;

    RI=0;
    // Uses timer 0 to measure the bit width and then
    // computes the baud rate using BRGR=[TH0,TL0]*2-16;
    TMOD=0x01; // Timer 0 in 16-bit mode
    TR0=0; // Stop Timer 0
    TH0=0;
    TL0=0;
    while(RXD==0);
    while(RXD==1);
    TR0=1; // Start Timer 0
    while(RXD==0);
    TR0=0; // Stop Timer 0
    //Set the new baud rate
    j=(TH0*0x100+TL0)*2-16;
    BRGCON=0;
    BRGR0=j%0x100;
    BRGR1=j/0x100;
    BRGCON=0x03;
}

void Wait_ms (unsigned int n) // Wait 'n' milliseconds
{
    TR0=0;
TMOD=(TMOD&0xF0)|0x01; // Timer 0 in 16-bit mode
    while(n!=0)
    {
        // 1 msec delay using timer 0
        TR0=0;
        TH0=TIMER0_1ms_RELOAD/0x100;
        TL0=TIMER0_1ms_RELOAD%0x100;
        TF0=0;
        TR0=1;
        while(!TF0);
        n--;
    }
}

void Wait_15us (void)
{
    // 15us delay using timer 0
    TR0=0;
TMOD=(TMOD&0xF0)|0x01; // Timer 0 in 16-bit mode
    TH0=TIMER0_15us_RELOAD/0x100;
    TL0=TIMER0_15us_RELOAD%0x100;
    TF0=0;
    TR0=1;
    while(!TF0);
}

unsigned char chartohex (char c)
{
    if(c & 0x40) c+=9; // a to f or A to F
    return (c & 0xf);
}

// Get a byte from the serial port
unsigned char getbyte (void)
{
    unsigned char j;
    j=chartohex(getchar())*0x10;
    j|=chartohex(getchar());
    return j;
}

void Print_Hex (unsigned char c)
{
    code char ASCII2HEX[]="0123456789ABCDEF";
    putchar(ASCII2HEX[c/0x10]);
    putchar(ASCII2HEX[c%0x10]);
}

// IMPORTANT! For ICP_Send() and ICP_Get():
//     Serial clock high > 80 ns
//     Serial clock low > 80 ns
//     Serial clock Period > 200 ns
//
//See Figure 37: Writing data
void ICP_Send (unsigned char value)
{
    unsigned char mask=0x01;
   
    do {
        PCL=0;
        PDA=(value & mask)?1:0;
        mask*=2;
        PCL=1;
    } while (mask!=0); //80H * 2 = 0x100 -> 0
    PDA=1;
}

// See Figure 38: Reading data
unsigned char ICP_Get (void)
{
    unsigned char mask=0x01, j;

    PDA=1; // Use pin as input
    j=0;
    do {
        PCL=0;
        if(PDA) j|=mask;
        mask*=2;   
        PCL=1;
    } while (mask!=0); //80H * 2 = 0x100 -> 0
    return j;
}

//See section 3.0: Checking status information
void Check_Status (void)
{
    unsigned char j;
    do
    {
      ICP_Send(RD_FMCON);
      j=ICP_Get();
    }
    while((j & 0x80)==0x80); // Check the BUSY flag: Set during a program or erase.
    if(j&0x0f) echo='X'; // Any errors?
}

// See Figure 36: Getting into the programming mode
// RESET delay from VDD active 50us Minimum
// RESET HIGH time Min=1 Max=32 us
// RESET LOW time 1 minimum us
void Activate_ICP (void)
{
    unsigned char j = 0;

    RESET=0;

    PDA=0;
    PCL=0;
    VDD=1; // Make sure VDD is off
    Wait_ms(100);

    PDA=0;
    PCL=1;
    VDD=0; // Turn on VDD
    Wait_ms(100);

    for(j=0; j<7; j++) // Pulse reset 7 times
    {
        RESET=1;
        Wait_15us();
        RESET=0;
        Wait_15us();
    }
    RESET=1;
    Wait_ms(10);
}

// See Figure 26: Loading page register partially.
// See Figure 27: Programming page register.
void Program (void)
{
    unsigned char j, k;
   
    // NOTE: There is a "page register" of 64 bytes for some
    // ICs and 16 for some other ICs.  The "page register" is just
    // a block of RAM which we load with the data to be flashed. Then
    // we apply the "PROG" command to execute the actual flashing.
    // We can start loading the "page register" at any address in
    // the buffer.  Also note that the "LOAD" command sets all the
    // bytes in the "page register" to zero. This turns out to be a
    // good thing as in LPC900 microcontrollers zero is the erased
    // or blank state of flash bytes.  Therefore we can flash pieces
    // of the current page without affecting bytes we have not set
    // in the same page. Also, there is an error in figure 26 as
    // noted bellow.
   
    //The data to flash must fit into one page (without roll over!)
    j=(address/0x100)&0x3f;
    k=((address+size)/0x100)&0x3f;
    if(j>k)
    {
        echo='X'; // Error: data is not page aligned
        return;
    }

    // Load page register
    ICP_Send(WR_FMCON);
    ICP_Send(LOAD);
    ICP_Send(WR_FMADRL);
    ICP_Send(address%0x100);
    for(j=0; j<size; j++)
    {
        ICP_Send(WR_FMDATA_I); // Outside the loop in figure 26
        ICP_Send(buff[j]);
    }
   
    // Program the page register   
    ICP_Send(WR_FMADRL);
    ICP_Send(address%0x100);
    ICP_Send(WR_FMADRH);
    ICP_Send(address/0x100);
    ICP_Send(WR_FMCON);
    ICP_Send(PROG);
    Check_Status();
}

void Read_Version (void)
{
    Print_Hex(0x04); // ISP version
    Print_Hex(0x04); // ICP version
}

// See Figure 34: Writing config bytes
void Misc_Write (void)
{
    if(buff[0]<0x20)
    {
    if(buff[0]==0x10)
    {
        // Clear configuration protection
        ICP_Send(WR_FMCON);
        ICP_Send(CCP);
        ICP_Send(WR_FMDATA);
        ICP_Send(0x96);
        Check_Status(); // Not in the ISP specifications, but makes sense!
        }
        else
        {
            ICP_Send(WR_FMCON);
            ICP_Send(CONF);
            ICP_Send(WR_FMADRL);
            ICP_Send(buff[0]); // Subfunction code
            ICP_Send(WR_FMDATA);
            ICP_Send(buff[1]); // Data
            Check_Status();
        }
    }
    else echo='X'; // There are only 1FH configuration bytes... so far!
}

// See Figure 33: Reading config bytes
void Misc_Read (void)
{
    if(buff[0]<0x20)
    {
        ICP_Send(WR_FMCON);
        ICP_Send(CONF);
        ICP_Send(WR_FMADRL);
        ICP_Send(buff[0]); // Subfunction code
        ICP_Send(RD_FMDATA);
        Print_Hex(ICP_Get());
    }
    else echo='X'; // There are only 1FH configuration bytes... so far!
}

void Erase (void)
{
    if (buff[0]==0) // See Figure 30: Erasing single page
    {
        ICP_Send(WR_FMADRL);
        ICP_Send(buff[2]);
        ICP_Send(WR_FMADRH);
        ICP_Send(buff[1]);
        ICP_Send(WR_FMCON);
        ICP_Send(ERS_P);
        Check_Status();
    }
    else if (buff[0]==1) // See Figure 29: Erasing single sector
    {
        ICP_Send(WR_FMADRH);
        ICP_Send(buff[1]);
        ICP_Send(WR_FMCON);
        ICP_Send(ERS_S);
        Check_Status();
    }
    else echo='X'; // Erase what?
}

// See Figure 32: Sector CRC
void Sector_CRC (void)
{
    unsigned char j;
    ICP_Send(WR_FMADRH);
    ICP_Send(buff[0]);
    ICP_Send(WR_FMCON);
    ICP_Send(CRC_S);
    Check_Status();

    for(j=0; j<4; j++) // The CRC is a 32-bit number (4 bytes)
    {
        ICP_Send(RD_FMDATA_I);
        buff[j]=ICP_Get();
    }

    Print_Hex(buff[3]);
    Print_Hex(buff[2]);
    Print_Hex(buff[1]);
    Print_Hex(buff[0]);
}

// See Figure 31: Global CRC
void Global_CRC (void)
{
    unsigned char j;
    ICP_Send(WR_FMCON);
    ICP_Send(CRC_G);
    Check_Status();

    for(j=0; j < 4; j++) // The CRC is a 32-bit number (4 bytes)
    {
        ICP_Send(RD_FMDATA_I);
        buff[j]=ICP_Get();
    }

    Print_Hex(buff[3]);
    Print_Hex(buff[2]);
    Print_Hex(buff[1]);
    Print_Hex(buff[0]);
}

void Load_Baud (void)
{
Send_echo();
while (!TI);
Wait_ms(10);
    BRGCON=0;
    BRGR1 = buff[0];
    BRGR0 = buff[1];
    BRGCON = 0x03;
}

void Reset_MCU (void)
{
Send_echo();
while (!TI);
    AUXR1|=SRST;
}

// Sets a P89LPC9xxx (8k variants only!) to its default factory
// setup by:
//   a) copying the bootloader of this microcontroller into the ICP-ed microcontroller.
//   b) Setting the default values for some registers in the ICP-ed microcontroller.
//
// This code is executed after the "CLONE" push button is pressed.
void Factory_Default (void)
{
    #define READ_CODE(X) (*((unsigned char code *) X))
unsigned int j;
unsigned char k;

LED=0;

// Change to default baud rate
    BRGCON=0;
    BRGR0=BRGR%0x100;
    BRGR1=BRGR/0x100;
    BRGCON=0x03;
autobaud_done=0; // So ISP2ICP bridge can be called againg

puts("Activating ICP...\r");
Activate_ICP();

puts("Erasing...\r");
// Erase all sectors
for(j=0x0000; j<0x2000; j+=0x400)
{
buff[0]=1; // Erase sector
buff[1]=j/0x100;
Erase();
}

puts("Copying...\r");
// Copy this P89LPC9351 bootloader to the ICP-ed P89LPC99351
for(j=0x1e00; j<0x2000; j+=MAXBUFF) // The bootloader starts at address 0x1e00
{
address=j;
size=MAXBUFF;
for(k=0; k<MAXBUFF; k++)
    buff[k]=READ_CODE(j+k);
Program();
}

puts("Clearing Configuration Protection...\r");
buff[0]=0x10; // Just in case is set, before writing to UCFG1, UCFG1, etc.
Misc_Write();

puts("Setting UCFG1...\r");
buff[0]=0x00;
buff[1]=0x63; // Default value
Misc_Write();

puts("Setting UCFG2...\r");
buff[0]=0x01;
buff[1]=0x00; // Default value
Misc_Write();

puts("Setting boot vector...\r");
buff[0]=0x02;
buff[1]=0x1f; // Default entry point of ISP bootloader (0x1f00)
Misc_Write();

puts("Setting boot vector start...\r");
buff[0]=0x03;
buff[1]=0x01; //0x01: Start with the boot vector address
Misc_Write();

puts("Done!\r");
LED=1;
VDD=1;
}

void main (void)
{
    unsigned char j, n;
    char c;

    while(1)
    {
        if(RI)
        {
        c=getchar();
       
if(autobaud_done==0)
{
        if (c!='U') Autobaud();
            autobaud_done=1;
Activate_ICP();     
}

        // Read and process Intel hex records
        if(c==':')
        {
        echo='.'; // If everything works out, send a period.

        size=getbyte();
        checksum=size;

        address=getbyte();
        checksum+=address;
        address*=0x100;
        n=getbyte();
        checksum+=n;
        address+=n;

        type=getbyte();
        checksum+=type;

        for(j=0; j<size; j++)
        {
            n=getbyte();
            if(j<MAXBUFF) buff[j]=n; //  Don't overrun the buffer!
            checksum+=n;
        }

        checksum+=getbyte();
        if(size>MAXBUFF) checksum=1; // Force a checksum error

        if(checksum==0)
        {
        LED=0;
            switch(type)
            {
                case PROGRAM:      Program();      break;
                case READ_VERSION: Read_Version(); break;
                case MISC_WRITE:   Misc_Write();   break;
                case MISC_READ:    Misc_Read();    break;
                case ERASE:        Erase();        break;
                case SECTOR_CRC:   Sector_CRC();   break;
                case GLOBAL_CRC:   Global_CRC();   break;
                case LOAD_BAUD:    Load_Baud();    break;
                case RESET_MCU:    Reset_MCU();    break;
                default:           echo='X';       break; // Unknown record type
            }
            LED=1;
        }
        else echo='X'; // Checksum error
        if (type!=LOAD_BAUD) Send_echo();
    }
}
if(CLONE==0)
{
Wait_ms(50); // Debounce
if(CLONE==0)
{
while(CLONE==0);
Factory_Default();
}
}
    }
}


Edit: Found "P89LPC900 In-Circuit Programming (ICP) Specifications" from "2006 Apr 10" here:

https://bbs.21ic.com/UploadFiles/2010-7/172328177724.pdf
« Last Edit: December 31, 2019, 04:55:58 am by jesuscf »
Homer: Kids, there's three ways to do things; the right way, the wrong way and the Max Power way!
Bart: Isn't that the wrong way?
Homer: Yeah, but faster!
 
The following users thanked this post: mikerj

Offline coromonadalix

  • Super Contributor
  • ***
  • Posts: 5895
  • Country: ca
 

Offline grg183Topic starter

  • Contributor
  • Posts: 41
  • Country: mt
    • Salitronic
Re: ICP programmer for obsolete LPC9xxx MCU
« Reply #8 on: December 31, 2019, 11:12:23 am »
@jesuscf Thanks! That code looks very usable and portable, will definitely give it a go.
 

Online PCB.Wiz

  • Super Contributor
  • ***
  • Posts: 1539
  • Country: au
Re: ICP programmer for obsolete LPC9xxx MCU
« Reply #9 on: December 31, 2019, 07:35:59 pm »
I've come across an old board with the LPC9361 which I need to re-program. The interface available is ICP but I am not sure which programmer I need. All online searches point to the FDI USB-ICP-LPC9xx but this has long been obsolete. Is there any current programmer that can do ICP programming on the LPC9361?


Note: removing the chip from the board is not an option and only the ICP interface is available.

Google finds posts like this (in German) - does the device still contain the bootloader it ships with ?
https://www.mikrocontroller.net/topic/94307
 

Offline jesuscf

  • Frequent Contributor
  • **
  • Posts: 499
  • Country: ca
Re: ICP programmer for obsolete LPC9xxx MCU
« Reply #10 on: January 03, 2020, 08:51:27 pm »
Google finds posts like this (in German) - does the device still contain the bootloader it ships with ?
https://www.mikrocontroller.net/topic/94307

There several ways of loading the flash of LPC9xx micro controllers:

- In some parts, parallel programming.  Option not available for OP as the IC is soldered to the board, and the particular part OP has may not even support it.
- In some parts, In-System Programming (ISP).  This is done via the serial port and a bootloader.  Although possible in the LPC9361 assuming the bootloader had not been erased, is also not available to OP as the serial port pins are probably not available.
- In all parts, In-Circuit Programming (ICP).  This is done via dedicated pins in the IC.  This is the only option available to OP as the ICP pins are the ones provided in the board.
Homer: Kids, there's three ways to do things; the right way, the wrong way and the Max Power way!
Bart: Isn't that the wrong way?
Homer: Yeah, but faster!
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf