lpcware

LPC17xx CAN on 100kbit/s on IRC

Discussion created by lpcware Employee on Jun 15, 2016
Latest reply on Jun 15, 2016 by lpcware
Content originally posted in LPCWare by harmjan on Wed Jul 25 07:12:16 MST 2012
Hello all,

I am writing a CAN bootloader for the LPC1769, to keep the code as generic as possible I don't want to use external components such as an external oscillator. The user manual says you should be able to get 100 kbit/s on the IRC oscillator. I am getting 100.45 kbit/s according to my logic analyzer while I get a "true" 100 kbit/s when using the external oscillator of the LPCXpresso.

I have been trying to get this working for a while but on my current setup I still get a deviation high enough to lose a bit over a long message. The following are my settings and my reasoning behind those settings:
For the CAN baudrate did I use a TESG1=15 and TESG2=6. These values come from the workings baudrate from the LPCXpresso with external oscillator and a driver library I found online. According to the user manual my bittime should now be:

t_{nominal bittime} = ( TESG1 + TESG2 + 3 ) * t_{CAN clock}

Since I want a nominal bittime of 1/100000 I derived i needed a CAN clock of 2.4Mhz.

Then I choose an f_{CCO} of 480Mhz. It was between 275Mhz and 550Mhz which the user manual said was necessary and it was easy to divide back to 2.4Mhz. I used the spreadsheet from http://ics.nxp.com/support/documents/microcontrollers/xls/lpc17xx.pll.calculator.xls to calculate an the PLL0 multiplier (60) and pre-divider (1). I used the advised CPU divider of 5 so I have a CPU clock of 96Mhz. The PCLKSEL0 for the CAN2 and CANAF is divide by 1. The CAN baudrate prescaler is then 40, which should give me a CAN clock of 2.4Mhz.

Is the internal oscillator not precise enough to give me a good CAN baudrate? If it is not what is the change of failed communications with this setup? I want to load about 170 nodes on a CAN bus with this baudrate. If the internal oscillators differ won't it work or will the difference be the same on all the nodes?

Here is the code I use to setup the clock:
/**
* Initialize the PLL0 to get a better frequency out
* of the internal oscillator.
*/
void initClock( void ) {
    // Do this here because of errata sheet of the processor,
    // you need to set the PCLKSEL before enabling PLL0
    LPC_SC->PCLKSEL0 &= ~(3<<28); // Clear the 2 CAN clock divider bits for can peripheral 2
    LPC_SC->PCLKSEL0 |= (1<<28);  // Set the clock divider for CAN peripheral 2 to 1,
                                  // so the main clock is the CAN clock
    LPC_SC->PCLKSEL0 &= ~(3<<30); // Clear the 2 clock divider bits for the can acceptance filter
    LPC_SC->PCLKSEL0 |= (1<<30);  // Set the clock divider for acceptance filter to 1

    // Folow the setup sequence from the user manual

    // Disconnect PLL0 if it was connected
    if( LPC_SC->PLL0STAT & (1<<25) )
    {
        LPC_SC->PLL0CON &= ~(1<<1);
        clockFeed();
    }

    // Disable PLL0
    LPC_SC->PLL0CON = 0;
    clockFeed();

    // Set the CPU divider to 1
    LPC_SC->CCLKCFG = 0;

    // Set the clock source for PLL0 to the IRC
    LPC_SC->CLKSRCSEL = 0;

    // Set the Multiplier for PLL0 and load it into
    // PLL0 via a feed
    LPC_SC->PLL0CFG  = 59;
    clockFeed();

    // Enable PLL0
    LPC_SC->PLL0CON  = (1<<0); // Enable the PLL0
    clockFeed();

    // Set the CPU divide to the divider you are going
    // to use with the PLL0 connected
    LPC_SC->CCLKCFG  = 4;

    // Wait for the PLL0 to lock on the frequency
    while( !(LPC_SC->PLL0STAT & (1<<26)) );

    // Connect PLL0 to the CPU block
    LPC_SC->PLL0CON  = (1<<0) | (1<<1); // Connect the PLL0 (and keep enabled)
    clockFeed();
}
And here is the code I use to initialize the CAN:
/**
* Setup the CAN peripheral.
*
* The pins for the 2th CAN peripheral are set to the right
* mode, the power and clock are setup for the CAN peripheral
* and CAN peripheral 2 is cleared and setup (timing and such).
*/
void initCan( void ) {
    // Set the processor pins to the correct mode
    LPC_PINCON->PINSEL0 &= ~(0x2<<8);  // Reset the function of Pin 0.4
    LPC_PINCON->PINSEL0 |= (0x2<<8);   // Set the function of Pin 0.4 to RD2
    LPC_PINCON->PINSEL0 &= ~(0x2<<10); // Reset the function of Pin 0.5
    LPC_PINCON->PINSEL0 |= (0x2<<10);  // Set the function of Pin 0.5 to TD2

    // Setup the power and clock for the peripheral
    LPC_SC->PCONP    |= (1<<14);  // Set the power bit for CAN peripheral 2

    // Setup peripheral related settings
    LPC_CAN2->MOD = 0x1; // Set the CAN peripheral in reset mode so you can change values
    LPC_CAN2->IER = 0x0; // Turn off all CAN related interrupts, they would not fire anyway
    LPC_CAN2->GSR = 0x0; // Set the error counters to 0
    // Clear everything you can via the command register
    LPC_CAN2->CMR = (0x1<<1) | // Abort transmission bit
                    (0x1<<2) | // Release receive buffer
                    (0x1<<3);  // Clear data overrun bit

    LPC_CAN2->BTR = 0xEFC027; // Set the bit rate of the CAN peripheral to 100kbit/s
                              // BRP   = 40
                              // SJW   = 3
                              // TESG1 = 15
                              // TESG2 = 6
                              // SAM   = 1

    LPC_CAN2->MOD = 0; // Enable the CAN peripheral again

    LPC_CANAF->AFMR |= (1<<1); // Set the acceptance filter in bypass mode
}
Thanks in advance.

Outcomes