Saturday, 2 August 2014

Using the SysTick timer of the LPC810

Every Cortex M0 Mcu such as the LPC810 has a system timer know as the SysTick timer which is a simple 24 bit timer. The SysTick timer is intended to provide a regular interrupt that can be used to update a system tick count, the code below provides a SysTick_Handler which overrides the WEAK reference to SysTick_Handler in cr_startup.c

/* SysTick Handler to be interupted every 1ms */
uint32_t __tick;
void SysTick_Handler(void) {
 __tick++;
}

The following lines of code enable a SysTick interrupt to occur every 1 Millisecond

SysTick->LOAD = (SystemCoreClock / 1000 * 1/*# ms*/) - 1; /* Every 1 ms */
SysTick->VAL = 0;
__tick = 0;
SysTick->CTRL = 0x7; /* d2:ClkSrc=SystemCoreClock, d1:Interrupt=enabled, d0:SysTick=enabled */

We Load the System Timer with the repeat count in the LOAD field then set the initial value to zero in the VAL field rather  than leave it at some random value and also reset the __tick count to zero.
The CTRL field is set to enable Interrupts in bit d1 (Our SysTick_Handler above) and to use the SystemCoreClock rather than SystemCoreClock / 2 by setting bit d2, Then finally setting bit d0 enables the System Timer.

The Internal RC Clock of the LPC810 is specified to be accurate to within 1% which is about 14 Minutes a day or 6 seconds very 10 Minutes. The device I tried appears to be doing slightly better and losing 3 seconds every 10 Minutes.

The updated code for the example is below:


#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    0
#define USE_WATCHDOG   0

#define USE_SYSTICK  1

#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 */

#if (USE_SYSTICK)
uint32_t __tick;
void SysTick_Handler(void) {
 __tick++;
}
#endif

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

#if (USE_SYSTICK)
 SysTick->LOAD = (SystemCoreClock / 1000 * 1/*# ms*/) - 1; /* Every 1 ms */
 SysTick->VAL = 0;
 __tick = 0;
 SysTick->CTRL = 0x7; /* d2:ClkSrc=SystemCoreClock, d1:Interrupt=enabled, d0:SysTick=enabled */
#endif

 /* 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;

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

 /* Some variables for Clock logic */
 char buf[9];
 char err = 0;
 long refh, refm, refs; /* ref time */
 int reftick;
 int reftime; // ref time in seconds
 // Enter an infinite loop testing for a keypress
 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("current _tick is %d\n\r", __tick);
   break;
  case 's':
   //set clock time hh:mm:ss
   xprintf("Please enter current time hh:mm:ss > ");
   xgets(buf, 9);
   reftick = __tick;
   if (buf[2] == ':' && buf[5] == ':') {
    buf[2] = ' ';
    buf[5] = ' ';
    char *p = &buf[0];
    if (!(xatoi(&p, &refh) && xatoi(&p, &refm) && xatoi(&p, &refs)))
     err = 1;
    else
     reftime = ((refh * 60) + refm) * 60 + refs;
   } else {
    err = 1;
   }
   if (err == 1)
    xprintf("\n\rError the time format is incorrect,  You entered %s\n\r",buf);
   break;
  case 'c':
   //get clock time
  {
   int secs = (__tick - reftick) / 1000; //number of seconds since time set
   int current = (reftime + secs) % (60 * 60 * 60);
   int h = (current / (60 * 60)) % 24;
   int m = (current / 60) % 60;
   int s = current % 60;
   xprintf("Current time is %d:%d:%d  The number of seconds passed is %d\n\r", h, m, s, secs);
  }
   break;
 }
 }
 return 0;
}


No comments:

Post a Comment