Wednesday, 30 July 2014

The PLL and various Clock sources of a LPC810


Previously we found out that using the default CMSIS startup routines that  the LPC810's System core clock is 12 MHZ upon entry to main() and that the MAINCLKSEL is 0x00 meaning that the PLL is not engaged so the LPC810 is being driven directly by the IRC Oscillator which is actually the default starting condition of the LPC810 anyway.

Since saving bytes is clearly important on a device like this the call to the CMSIS SystemInit() can be removed from cr_startup_lpc8xx.c entirely. Find the following lines of code and remove them


#if defined (__USE_CMSIS) || defined (__USE_LPCOPEN)
    SystemInit();
#endif

Since we know the SystemCoreClock is 12 MHZ we can also remove the call to the CMSIS SystemCoreClockUpdate() function in main.c and replace it simply with:


uint32_t SystemCoreClock = 12000000;


By my calculations this will save a whopping 268 bytes which might just save the day.


Diagram from lpc8xx pll calculator.xlsx

From the diagram above the SystemCoreClock (Just System Clock in the diagram) is the Main Clock divided by SYSAHBCLKDIV. The SystemCoreClock is also drives the peripheral clocks, the exception here is the UART's that have there own divider UARTCLKDIV and are clocked from the Main Clock divided by UART(CLKDIV and also IOCON is clocked from the Main Clock divided by IOCONCLKDIV. The mistake in the first version of the Uart init routine was to assume it was driven from the SystemCoreClock like most of the other peripherals.

The LM810's phase locked loop

What if we would like the SystemCoreClock to be more that 12MHZ? The LPC810 is specified to run up to 30MHZ in this case we can use the PLL to increase the IRC frequency.With a PLL the input frequency is multiplied to a higher frequency and then divided down to provide the actual clock frequency used by the CPU .

On reset the Internal RC oscillator is selected as input to the PLL.The value of SYSPLLCTRL will control the multiplication factor. The lower 4 bits of this register (MSEL) plus 1 contain the PLL multiplier.

MSELPLL out clock MHZ
012
124
236
348
460
572
684
796
8108
9120

The PLL output clock must be less than or equal to 100MHZ hence the last two MSEL values are out of specification.

Were not quite there yet, the bits D5 and D6 together make the  PSEL value that we can use to calculate the VCO frequency.
The value of PSEL signifies how many bits we shift 0x1 left by to use as a multiplier for the PLL output clock.

VCO FREQ = 2 *  (1 << PSEL ) * (PLL out clock)

The VCO frequency must be between 156MHZ and 320MHZ so we have to select a PSEL that constrains the VCO frequency within this range.The table below shows the values of SYSPLLCTRL needed for the corresponding PLL output clock.

MSELPSELPLL out clock MHZSYSPLLCTRL
0312 0x60
12240x41
22360x42
31480x23
41600x24
51720x25
60840x06
70960x07
801080x08
901200x09

You will then need to use  SYSAHBCLKDIV to divide the output of the PLL down in order to get the required SystemCoreClock

Finally we set  MAINCLKSEL to 0x03 to select the PLL output as the Main Clock rather than the IRC Clock.

We should also note that we must wait for the PLL to lock and also wait for the PLL output to be selected as the Main Clock see the code for further details. The code will also allow you to select the Watchdog Clock if speed is not necessary with substantial power savings.


#include "LPC8xx.h"
#include "xprintf.h"

#define BAUDRATE 57600
#define UART_CFG_DATALEN_8      (0x01 << 2)  /* UART 8 bit length mode */
#define UART_CFG_PARITY_NONE    (0x00 << 4)  /* No parity */
#define UART_CFG_STOPLEN_1      (0x00 << 6)  /* UART One Stop Bit Select */
#define UART_STAT_RXRDY         (0x01 << 0)  /* Receiver ready */
#define UART_STAT_TXRDY         (0x01 << 2)  /* Transmitter ready for data */
#define UART_CFG_ENABLE         (0x01 << 0)

/*Which Clock source ?, if both are 0 then Internal RC Oscillator is used */
#define USE_PLL  (1)
#define USE_WATCHDOG  (0)

#define WATCHDOG_300KHZ ((1 << 5) | (0))
#define WATCHDOG_9375HZ ((1 << 5) | (0x1f))
#define WATCHDOG_2300KHZ ((0xf << 5) | (0))
#define PLL_60MHZ (0x24)
#define PLL_24MHZ (0x41)
#define PLL_96MHZ (0x07)

uint32_t SystemCoreClock=12000000; /* Value @ Reset */
uint32_t SystemMainClock=12000000; /* Value @ Reset */

void uart0putc(unsigned char c){
 /* Wait until we're ready to send */
  while (!(LPC_USART0->STAT & UART_STAT_TXRDY));
   LPC_USART0->TXDATA = c;
}

unsigned char uart0getc(){
 /* Wait until we're ready to receive */
 while (!(LPC_USART0->STAT & UART_STAT_RXRDY));
  return LPC_USART0->RXDATA;
}

short uart0PollChar() {
 if (!(LPC_USART0->STAT & UART_STAT_RXRDY))
  return -1;
 return LPC_USART0->RXDATA;
}


int main(void) {

 /* Enable AHB clock to the Switch Matrix , UART0 , IOCON */
 LPC_SYSCON->SYSAHBCLKCTRL |= (1 << 7) | (1 << 14) | (1 << 18);

#if (USE_PLL)
 /* Enable the PLL Clock for a maximum 30MHZ SystemCoreClock */
 LPC_SYSCON->SYSPLLCTRL    = PLL_60MHZ; /* 60 MHZ pll output clock*/
 LPC_SYSCON->PDRUNCFG     &= ~(0x1 << 7);        /* Power-up SYSPLL          */
 while (!(LPC_SYSCON->SYSPLLSTAT & 0x01));       /* Wait Until PLL Locked    */

 LPC_SYSCON->MAINCLKSEL    = 3;     /* Select PLL Clock Output  */
 LPC_SYSCON->MAINCLKUEN    = 0x01;      /* Update MCLK Clock Source */
 while (!(LPC_SYSCON->MAINCLKUEN & 0x01));       /* Wait Until Updated       */

 LPC_SYSCON->SYSAHBCLKDIV  =  2; /* Divide by 2 to get the maximum 30MHZ SystemCoreClock */

 SystemMainClock = 12000000 * ((LPC_SYSCON->SYSPLLCTRL & 0x01F) + 1); /* Calculate SystemMainClock for IRC directed to the PLL*/
 SystemCoreClock = SystemMainClock / LPC_SYSCON->SYSAHBCLKDIV; /*Calculate SystemCoreClock */

#elif (USE_WATCHDOG)
 /* Enable the Watchdog Oscillator for minimum power consumption*/
 LPC_SYSCON->WDTOSCCTRL = WATCHDOG_2300KHZ;
 LPC_SYSCON->PDRUNCFG &= ~(0x1 << 6); /* Power-up Watchdog          */
 LPC_SYSCON->MAINCLKSEL = 2; /* Select Watchdog Clock Output  */
 LPC_SYSCON->MAINCLKUEN = 0x01; /* Update MCLK Clock Source */
 while (!(LPC_SYSCON->MAINCLKUEN & 0x01)); /* Wait Until Updated       */

 if ((LPC_SYSCON->WDTOSCCTRL & (0x0f << 5)) == (0x01 << 5))
 SystemMainClock = 600000 / (2 * (1 + (LPC_SYSCON->WDTOSCCTRL & 0x1f)));
 else
 SystemMainClock = 4600000 / (2 * (1 + (LPC_SYSCON->WDTOSCCTRL & 0x1f)));

 SystemCoreClock = SystemMainClock / LPC_SYSCON->SYSAHBCLKDIV; /* Calculate SystemCoreClock */
 LPC_SYSCON->PDRUNCFG |= (1 << 7) | (1 << 1) | (1 << 0); /* Power down IRC & PLL as not needed now */
#endif

 /* Switch matrix , select uart */
 LPC_SWM->PINASSIGN0 = 0xffff0004UL;
 
 /* Set the UART clock divisor to 1  */
 LPC_SYSCON->UARTCLKDIV = 1; // note on reset the value is 0. i.e clock is disabled
 /* Configure UART0 baud rate */
 LPC_USART0->BRG = ((SystemMainClock / LPC_SYSCON->UARTCLKDIV) >> 4) / BAUDRATE - 1;
 LPC_SYSCON->UARTFRGMULT = ((((SystemMainClock / LPC_SYSCON->UARTCLKDIV) >> 4) * (0x100)) / (BAUDRATE * (LPC_USART0->BRG + 1))) - (0x100);
 LPC_SYSCON->UARTFRGDIV = 0xFF; //note on reset the value is 0

 /* Set UART0 to 8N1 and finally enable */
 LPC_USART0->CFG = UART_CFG_DATALEN_8 | UART_CFG_PARITY_NONE | UART_CFG_STOPLEN_1 | UART_CFG_ENABLE;

 /* Init Chan's Embedded String Functions (xprintf ect) */
 xdev_out(uart0putc);
 xdev_in(uart0getc);

    // Enter an infinite loop testing for the '0' and '1' keys
    while(1) {
     switch( uart0PollChar() ){
      case '0':
          xprintf("System Core Clock is %d hz\n\rSystem Main Clock is %d\n\r",SystemCoreClock,SystemMainClock);
      break;
      case '1':
          xprintf("Main clock select is %0x\n\rSYSAHBCLKDIV is %d\n\r",LPC_SYSCON->MAINCLKSEL,LPC_SYSCON->SYSAHBCLKDIV);
         break;
     }
    }
    return 0 ;
}

Chan's xprintf on the LPC810

Continuing on from the last post, now that the Uart is working and outputting some text we can add much more functionality to the debugging output with the excellent pint sized micro printf library from Chan.

You can get this library from http://elm-chan.org/fsw/strf/xprintf.html. When you have downloaded it then extract the xprintf.c and place in your src folder and also extract xprintf.h and place it in the inc folder.

In order to integrate this file we will need two functions that get and put characters to the Uart (or indeed any other device such as a LCD). The putc function must return void and take an unsigned char as an argument while the getc function must return an unsigned char and takes no arguments. These functions are shown below for the LPC810:


void uart0putc(unsigned char c){
 /* Wait until we're ready to send */
  while (!(LPC_USART0->STAT & UART_STAT_TXRDY));
   LPC_USART0->TXDATA = c;
}

unsigned char uart0getc(){
 /* Wait until we're ready to receive */
 while (!(LPC_USART0->STAT & UART_STAT_RXRDY));
  return LPC_USART0->RXDATA;
}

Now in our main loop we tell xprintf about them by using the xdev_out and xdev_in macros:


/* Init Chan's Embedded String Functions (xprintf ect) */
 xdev_out(uart0putc);
 xdev_in(uart0getc);

These can also be used again to switch the output to another device assuming the corresponding getc and putc functions exist for it.

Putting this all together we get a new main.c which now allows us to investigate the default clock settings configured by CMSIS (note that the Uart setup code has changed slightly in order to fix it taking the wrong clock signal which will become apparent if the main clock divisor in not one).


#include "LPC8xx.h"
#include "xprintf.h"

#define BAUDRATE 57600
#define UART_CFG_DATALEN_8      (0x01 << 2)  /* UART 8 bit length mode */
#define UART_CFG_PARITY_NONE    (0x00 << 4)  /* No parity */
#define UART_CFG_STOPLEN_1      (0x00 << 6)  /* UART One Stop Bit Select */
#define UART_STAT_RXRDY         (0x01 << 0)  /* Receiver ready */
#define UART_STAT_TXRDY         (0x01 << 2)  /* Transmitter ready for data */
#define UART_CFG_ENABLE         (0x01 << 0)

void uart0putc(unsigned char c){
 /* Wait until we're ready to send */
  while (!(LPC_USART0->STAT & UART_STAT_TXRDY));
   LPC_USART0->TXDATA = c;
}

unsigned char uart0getc(){
 /* Wait until we're ready to receive */
 while (!(LPC_USART0->STAT & UART_STAT_RXRDY));
  return LPC_USART0->RXDATA;
}

short uart0PollChar() {
 if (!(LPC_USART0->STAT & UART_STAT_RXRDY))
  return -1;
 return LPC_USART0->RXDATA;
}

int main(void) {
 /* Calculate and set SystemCoreClock */
 SystemCoreClockUpdate();

 /* Init Chan's Embedded String Functions (xprintf ect) */
 xdev_out(uart0putc);
 xdev_in(uart0getc);

 /* Enable AHB clock to the Switch Matrix and UART0 */
 LPC_SYSCON->SYSAHBCLKCTRL |= (1 << 7) | (1 << 14);

 /* Configure Switch Matrix so that Pin2 = U0_TXD and Pin3 = U0_RXD */
 LPC_SWM->PINASSIGN0 = 0xffff0004UL;

 /* Set the UART clock divisor to 1  */
 LPC_SYSCON->UARTCLKDIV = 1; // note on reset the value is 0. i.e clock is disabled
 int MainClock = SystemCoreClock * LPC_SYSCON->SYSAHBCLKDIV;

 /* Configure UART0 baud rate */
 LPC_USART0->BRG = ((MainClock / LPC_SYSCON->UARTCLKDIV) >> 4) / BAUDRATE - 1;
 LPC_SYSCON->UARTFRGMULT = ((((MainClock / LPC_SYSCON->UARTCLKDIV) >> 4) * (0x100)) / (BAUDRATE * (LPC_USART0->BRG + 1))) - (0x100);
 LPC_SYSCON->UARTFRGDIV = 0xFF; //note on reset the value is 0

 /* Set UART0 to 8N1 and finally enable */
 LPC_USART0->CFG = UART_CFG_DATALEN_8 | UART_CFG_PARITY_NONE | UART_CFG_STOPLEN_1 | UART_CFG_ENABLE;

    // Enter an infinite loop testing for the '0' and '1' keys
    while(1) {
     switch( uart0PollChar() ){
      case '0':
          xprintf("System Core Clock is %d hz\n\r",SystemCoreClock);
      break;
      case '1':
          xprintf("Main clock select is %0x\n\r",LPC_SYSCON->MAINCLKSEL);
      break;
     }
    }
    return 0 ;
}










Monday, 28 July 2014

A First program for the LPC810

Previously Flash Magic was used to talk to the ISP of a LPC810. Now we can look at how to program one. NXP provides the free eclipse based IDE LPCXpresso. With this you can compile up to 8k of executable without registering or up to 256k by registering and activating the free version.

You can get LPCXpresso from http://www.lpcware.com/lpcxpresso/download once you have installed it we can proceed and set up a project. Select File->New->Project and set the project up as below.
  • In the First wizard page select "LCPXpresso C Project"
  • In the second LPC8xx -> C Project
  • In the next give it a project Name
  • In the forth LPC8xx -> LPC810
  • In the next wizard page follow the instructions and install CMSIS. This only needs to be done once. Note that you only need the CMSIS_CORE_LPC8xx one. Now at this point there seems to be a bug and you have to exit the wizard after installing CMSIS and start over again until you get to this point in the wizard again in order to continue.
  • In the Sixth wizard page leave DSP Library as none
  • In the next uncheck enable Micro Trace Buffer
  • In the next uncheck enable CRP and check use INC folder

Finally we can click on finish.

Now we switch to the CMSIS project that's been included and build CMSIS for both debug and release.

Switch back to our project and set the active configuration to release.

There is one more this to do and that is to add the highlighted line below in Properties->C/C++ build->Settings->Build Steps->Post Build Steps

arm-none-eabi-size "${BuildArtifactFileName}"
arm-none-eabi-objcopy -O ihex ${BuildArtifactFileName} ${BuildArtifactFileBaseName}.hex
# arm-none-eabi-objcopy -v -O binary "${BuildArtifactFileName}""${BuildArtifactFileBaseName}.bin"
# checksum -p ${TargetChip} -d "${BuildArtifactFileBaseName}.bin"

which will generate a Hex file for Flash Magic when the program is built.

Now you should be able to run build and a Hex file should appear in your release directory. The current main.c does not do much at the moment it's just a infinite loop.

Since we have the UART already connected lets get some output , below is a minimal program to get you going and to connect to a terminal emulator:


#include "LPC8xx.h"

#define BAUDRATE 57600
#define UART_CFG_DATALEN_8      (0x01 << 2)  /* UART 8 bit length mode */
#define UART_CFG_PARITY_NONE    (0x00 << 4)  /* No parity */
#define UART_CFG_STOPLEN_1      (0x00 << 6)  /* UART One Stop Bit Select */
#define UART_STAT_RXRDY         (0x01 << 0)  /* Receiver ready */
#define UART_STAT_TXRDY         (0x01 << 2)  /* Transmitter ready for data */
#define UART_CFG_ENABLE         (0x01 << 0)

int uart0PollChar() {
 if (!(LPC_USART0->STAT & UART_STAT_RXRDY))
  return -1;
 return LPC_USART0->RXDATA;
}

void uart0SendString(char *buffer) {
 while (*buffer != 0) {
  while (!(LPC_USART0->STAT & UART_STAT_TXRDY));
  LPC_USART0->TXDATA = *buffer;
  buffer++;
 }
}

int main(void) {
 /* Calculate and set SystemCoreClock */
 SystemCoreClockUpdate();

 /* Enable AHB clock to the Switch Matrix and UART0 */
 LPC_SYSCON->SYSAHBCLKCTRL |= (1 << 7) | (1 << 14);

 /* Configure Switch Matrix so that Pin2 = U0_TXD and Pin3 = U0_RXD */
 LPC_SWM->PINASSIGN0 = 0xffff0004UL;

 /* Set the UART clock divisor to 1  */
 LPC_SYSCON->UARTCLKDIV = 1; // note on reset the value is 0. i.e clock is disabled

 /* Configure UART0 baud rate */
 LPC_USART0->BRG = (SystemCoreClock >> 4) / BAUDRATE - 1;
 LPC_SYSCON->UARTFRGMULT = (((SystemCoreClock >> 4) * (0x100)) / (BAUDRATE * (LPC_USART0->BRG + 1))) - (0x100);
 LPC_SYSCON->UARTFRGDIV = 0xFF; //note on reset the value is 0

 /* Set UART0 to 8N1 and finally enable */
 LPC_USART0->CFG = UART_CFG_DATALEN_8 | UART_CFG_PARITY_NONE | UART_CFG_STOPLEN_1 | UART_CFG_ENABLE;

    // Enter an infinite loop testing for the '0' and '1' keys
    while(1) {
     switch( uart0PollChar() ){
      case '0':
          uart0SendString("Hello\n\r");
      break;
      case '1':
          uart0SendString("World\n\r");
      break;
     }
    }
    return 0 ;
}


If you cut & paste the above in place of the existing main.c you should be able to get "hello" printed when you press the '0' key and "world" printed when you press the '1' key on a terminal emulator.

For a terminal emulator I use Realterm available from http://realterm.sourceforge.net/ remember to toggle the "open button" to recapture the Uart from flash magic after you have flashed an image else nothing will happen.




LPC810 a little 8 pin DIP ARM MCU

Recently I've got hold of some of NXP's dinky LPC810's. A 32 Bit ARM MCU in a 8 pin dual inline package that cost under £1 individually and less that 33p in quantity. These are incredibly easy to use and can simply be connected directly to a USB serial adapter and programmed via the inbuilt ISP. That is all you need, just 4 wires connecting the serial adapter and another grounding pin 5 on power up in order to switch to the ISP mode. No other components are necessary which makes this device a extremely simple way to get into ARM Cortex M0 programming.


The LPC810 can run up to 30Mhz on its internal RC oscillator and has 4k of flash and 1k of ram. That's not a lot for a 32bit mcu but my initial experiments suggest much can still be done with that. 

As for IO the 6 available pins can be configured for SPI, I2C and UART as well as their default GPIO. There is also a Comparator and a very flexible timer among other goodies.

It's maximum supply voltage is 4.6v but fortunately most of the LPC810's io pins are 5v tolerant. Running @ 30mhz 3.3v it will consume about 3ma for a continuous while loop and much less in its sleep modes.

The free program Flash Magic available from http://www.flashmagictool.com/ can be used to access the ISP functionality erase and program the chip. A simple way to test if you have wired it to the USB uart adapter correctly is to use Flash Magic to find the device ID.


.
Now you are ready to program the LPC810 but you will need  a Hex file first. In the Next post I will look at how to create programs for the LPC810.


.