Embedded Freaks..

August 10, 2009

Cortex-M3 SysTick peripheral

Filed under: Cortex-M — Tags: — kunilkuda @ 5:10 pm

The interesting part of Cortex-M3 is SysTick, an additional timer, provided by ARM, to switch the process context on RTOS. ARM was hoping that by using SysTick, most of RTOS will be portable from vendor to vendors.

But I’m a bit curious on how to use it. Hence, here’s the simple demo on how to use SysTick on Cortex-M3.main.c

/*
* main.c
*
*  Created on: 10-Aug-2009
*      Author: kunil
*/

#include “LPC17xx.h”
#include “core_cm3.h”

/*
* Linker file variables
*/
extern void __sidata;
extern void __sdata;
extern void __edata;
extern void __sbss;
extern void __ebss;

static int main(void);
static void setup_SysTick(void);
static void setup_LEDs(void);
static void display_current_time(void);

static unsigned int u32_current_time;

/*
* Override the standard Reset_Handler() on startup_LPC1766
*/
void Reset_Handler(void) {
unsigned int *source, *destination;

// Copy data section from ROM to RAM
source = (unsigned int *) &__sidata;
for (destination = (unsigned int *) &__sdata; destination
< (unsigned int *) &__edata; destination++) {
*destination = *source;
}

// Clear the BSS section
for (destination = (unsigned int *) &__sbss; destination
< (unsigned int *) &__ebss; destination++) {
*destination = 0;
}

main();

// If main() ever return, this will hold it
while(1);
}

/*
* Override the standard SysTick_Handler() on startup_LPC1766
*/
void SysTick_Handler(void) {
u32_current_time++;
}

int main(void) {
setup_SysTick();
setup_LEDs();
while (1) {
/* Wait for interrupt..a.k.a sleep until interrupt */
asm volatile (“wfi”);
display_current_time();
}
}

static void setup_SysTick(void) {
/* The upper limit of SysTick
*
* Since the MCU clock will be equal to internal RC (assumming the PLL is not
* activated), 4000000 value will generate 1s interrupt
*
* The value is counted at 1 (not zero). Hence -1 offset is introduced
*/
SysTick->LOAD = (4000000 – 1);
/* SysTick control register
*
* SYSTICK_CLKSOURCE = 1 means the timer will use CPU (core)’s clock
* SYSTICK_TICKINT = 1 means the SysTick interrupt is activated on NVIC
* SYSTICK_ENABLE = 1 means enable SysTick
*/
SysTick->CTRL = (1 << SYSTICK_CLKSOURCE) | (1< }

static void setup_LEDs(void) {
// Enable P1.28, P1.29, and P1.31 as output
GPIO1->FIODIR = (1 << 28) | (1 << 29) | (1 << 31);
// Enable P2.2 – P2.6 as output
GPIO2->FIODIR = (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6);
}

static void display_current_time(void) {
/* Accessing the pins directly
*
* u32_current_time[0] = P1.28
* u32_current_time[1] = P1.29
* u32_current_time[2] = P1.31
* u32_current_time[3] = P2.2
* u32_current_time[4] = P2.3
* u32_current_time[5] = P2.4
* u32_current_time[6] = P2.5
* u32_current_time[7] = P2.6
*/
GPIO1->FIOPIN = 0;
GPIO2->FIOPIN = 0;

GPIO1->FIOPIN |= (((u32_current_time & 0×03) >> 0) << 28);
GPIO1->FIOPIN |= (((u32_current_time & 0×04) >> 2) << 31);
GPIO2->FIOPIN |= (((u32_current_time & 0xF8) >> 3) <<  2);
}

To use the source code, you will need the ‘cm3_core.h’, ’system_LPC17xx.h and ‘LPC17xx.h’ file from CMSIS website. I’m using ver 1.20 source code from their site, and also my own C start up / linker file.

startup_LPC17xx.c

/*
* startup_LPC1766.c – Startup file for LPC1766 Cortex-M3
*
* @author: kunilkuda_at_gmail_dot_com
*/
extern void __cs3_stack; ///< This is global variable from linker script

/* We gave ‘weak’ attribute, so these functions can be aliased into a single
* function
*/
void __attribute__((weak)) Reset_Handler(void);     /* Reset Handler */
void __attribute__((weak)) NMI_Handler(void);       /* NMI Handler */
void __attribute__((weak)) HardFault_Handler(void); /* Hard Fault Handler */
void __attribute__((weak)) MemManage_Handler(void); /* MPU Fault Handler */
void __attribute__((weak)) BusFault_Handler(void);  /* Bus Fault Handler */
void __attribute__((weak)) UsageFault_Handler(void);/* Usage Fault Handler */
void __attribute__((weak)) SVC_Handler(void);       /* SVCall Handler */
void __attribute__((weak)) DebugMon_Handler(void);  /* Debug Monitor Handler */
void __attribute__((weak)) PendSV_Handler(void);    /* PendSV Handler */
void __attribute__((weak)) SysTick_Handler(void);   /* SysTick Handler */

/*
* Ask GCC to put this array into .cs3.interrupt_vector section
*/
__attribute__ ((section(“.cs3.interrupt_vector”)))
void (* const g_pfnVectors[])(void) = {
&__cs3_stack,       // The initial stack pointer
Reset_Handler,      // The reset handler
NMI_Handler,        // The NMI handler
HardFault_Handler,  // The hard fault handler
MemManage_Handler,  // The MPU fault handler
BusFault_Handler,   // The bus fault handler
UsageFault_Handler, // The usage fault handler
0,                  // Reserved
0,                  // Reserved
0,                  // Reserved
0,                  // Reserved
SVC_Handler,        // SVCall handler
DebugMon_Handler,   // Debug monitor handler
0,                  // Reserved
PendSV_Handler,     // The PendSV handler
SysTick_Handler,    // The SysTick handler
};

/* Here’s how to alias multiple functions into single function
*/
void Default_Handler(void);
#pragma weak NMI_Handler        = Default_Handler /* NMI handler */
#pragma weak HardFault_Handler  = Default_Handler /* Hard Fault handler */
#pragma weak MemManage_Handler  = Default_Handler /* MPU Fault Handler */
#pragma weak BusFault_Handler   = Default_Handler /* Bus Fault Handler */
#pragma weak UsageFault_Handler = Default_Handler /* Usage Fault Handler */
#pragma weak SVC_Handler        = Default_Handler /* SVCall Handler */
#pragma weak DebugMon_Handler   = Default_Handler /* Debug Monitor Handler */
#pragma weak PendSV_Handler     = Default_Handler /* PendSV Handler */
#pragma weak SysTick_Handler    = Default_Handler /* SysTick Handler */

void Default_Handler(void) {
while (1);
}

extern int main(void);
void Reset_Handler(void) {
main();

// If main() ever exit, this should hold MCU from running wild
while(1);
}

LPC1766.ld

/*
* Define the supported output formats – elf32-littlearm is the
*  default
*/  
OUTPUT_FORMAT(“elf32-littlearm”, “elf32-bigarm”, “elf32-littlearm”)   

/* Define the target architecture */  
OUTPUT_ARCH(arm)   

/* Define the system entry point */  
ENTRY(Reset_Handler)   

/* Define the memory layout for the board */
SECTIONS  
{  
/* LPC1766 flash is at 0×0000_0000 */
.flash 0×00000000 :
{
/* Tell the linker to collect any .cs3.interrupt_vector section to this
address */  
KEEP(*(.cs3.interrupt_vector));

/* Collect the executable parts here */
*(.text .text.*);
/* Collect the const variables here */
*(.rodata .rodata.*);

__sidata = .;
}

/*
* This section refers to variables with init value. The init values are
* stored on ROM, but addressed on RAM (otherwise the variables cannot be
* manipulated).
*
* This section will be loaded at 0×10000000, the start of LPC1766 RAM
*/
.data_at_ram 0×10000000: AT (LOADADDR(.flash) + SIZEOF(.flash))
{
__sdata = .;
*(.data .data.*);
__edata = .;
}

.ram :
{
__sbss = .;
/* Collect the global / static variables, without init value here */
*(.bss .bss.*);
__ebss = .;
}

/* Set __cs3_stack to point the end of memory
* This one depends on your MCU. I’m using LPC1766 with 256kB flash and
* 32kB RAM
*/  

__cs3_stack = 0×10000000 + (32 * 1024);
}

To compile the files:

# arm-none-eabi-gcc -O2 -ffunction-sections -fdata-sections -g3 -Wall -c -mcpu=cortex-m3 -mthumb -omain.o main.c
# arm-none-eabi-gcc -O2 -ffunction-sections -fdata-sections -g3 -Wall -c -mcpu=cortex-m3 -mthumb -ostartup_LPC1766.o startup_LPC1766.c
# arm-none-eabi-gcc -Wl,–gc-sections -Wl,-Map,LPC1766_Systick_Interrupt.map -Wl,-T,/home/kunil/workspace/LPC1766_Systick_Interrupt/LPC1766.ld -oLPC1766_Systick_Interrupt.elf  main.o startup_LPC1766.o

To generate the HEX file:

# arm-none-eabi-objcopy -j .flash -j .data_at_ram -O ihex LPC1766_Systick_Interrupt.elf LPC1766_Systick_Interrupt.hex

The eclipse project file can be downloaded from here.

No Comments Yet »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a comment

Blog at WordPress.com.