SysTick - glennlopez/EmbeddedSystems.Playground GitHub Wiki

SysTick Address Definitions

The code snippet below are address definitions used for the SysTick examples in this guide. Visit the STCTRL, STRELOAD, and the STCURRENT section of the TM4C123G datasheet for more info.

SysTick is a timer/counter feature available on all Cortex-M microcontrollers
  • SysTick Base Address: 0xE000.E000
    • STCTRL offset: 0x010
    • STRELOAD offset: 0x014
    • STCURRENT offset: 0x018
#define NVIC_ST_CTRL_R          (*((volatile unsigned long *)0xE000E010))
#define NVIC_ST_RELOAD_R        (*((volatile unsigned long *)0xE000E014))
#define NVIC_ST_CURRENT_R       (*((volatile unsigned long *)0xE000E018))

#define NVIC_ST_CTRL_COUNT      0x00010000  // Count flag
#define NVIC_ST_CTRL_INTEN      0x00000002  // Interrupt enable

SysTick Initialization

  1. Clear the enable bit in the STCTRL register to turn off SysTick during initialization:
    • ` NVIC_ST_CTRL_R = 0x00; `
  2. Set the reload (STRELOAD) register to your desired specifications (this value gets passed/copied to STCURRENT when STCURRENT's value is fully decremented to 0). Setting the STRELOAD value to its maximum value allows it to behave like a clock:
    • ` NVIC_ST_RELOAD_R = 0x00FFFFFF `
  3. Write any value to the STCURRENT register to clear the counter (this value is decremented once every bus cycle):
    • ` NVIC_ST_CURRENT_R = 0; `
  4. Write to your desired specification in the STCTRL register (check datasheet). In this example I will set [CLK_SRC] = 1 so the counter runs off the 16Mhz system clock, and set [ENABLE] = 1 to enable the SysTick.
    • ` NVIC_ST_CTRL_R = 0x01 + 0x04; ` or ` NVIC_ST_CTRL_R = 0x05; `
void SysTick_Init(void){
  NVIC_ST_CTRL_R = 0x00;                // disable SysTick during setup
  NVIC_ST_RELOAD_R = 0x00FFFFFF;  		// maximum reload value
  NVIC_ST_CURRENT_R = 0;                // any write to current clears it         
  NVIC_ST_CTRL_R = 0x01 + 0x04;			// enable SysTick with core clock
}

Using SysTick to determine elapse Time

Note: When measuring elapse time with SysTick, the system can only be acurate as long as the elapse time is less than 0.209 seconds (at 80MHz), 1.05 seconds (at 16MHz), and 0.336 seconds (at 50MHz). Acuracy can be calculated by: 2^24 * 12.5ns (at 80MHz).

  1. Calculate how long it would take (in seconds) for the value of STCURRENT to decrement once every bus cycle.
    • 1 tick = (1/System Clock) * 10^9 nano seconds
      • PLL activated: (1/80MHz) * 10^9 = 12.5 nano seconds
      • PLL not activated: (1/16MHz) * 10^9 = 62.5 nano seconds
      • Core Clock: (1/50MHz) * 10^9 = 20 nano seconds
  2. Once we know how long 1 tick takes, we can calculate elapse time using simple math: (Last - Now)*1tick
    • 5 ticks at 80MHz: (0x0000.0008 - 0x0000.0003) * 12.5ns = 62.5 nano seconds
unsigned long Now;      // 24-bit time at this call (12.5ns)
unsigned long Last;     // 24-bit time at previous call (12.5ns)
unsigned long Elapsed;  // 24-bit time between calls (12.5ns)
void Action(void){      // function under test
  Now = NVIC_ST_CURRENT_R;         // what time is it now?
  Elapsed = (Last-Now)&0x00FFFFFF; // 24-bit difference
  Last = Now;                      // set up for next...
}

Using SysTick for Accurate Delays

Using the SysTick feature as a delay requires you to create 2 functions. The first function will be used to create a SysTick delay routine we can utilise for creating a 1ms busy-wait loop. The second function will be used to iterate the first function as per the second functions argument.

  1. Instantiate a variable for elapsedTime to store the sum of startTime and STCURRENT:
    • ` volatile unsigned long elapsedTime; `
  2. Instantiate a variable for startTime and set it to the curent value of the STCURRENT register:
    • `unsigned long startTime = NVIC_ST_CURRENT_R; `
  3. Set the elapse time variable to the diffrence of the start time and the current value of STCURRENT. Be sure to mask the STCURRENT with 0x00FF.FFFF since we only need the first 24 bites of the register.
    • ` elapsedTime = (startTime - NVIC_ST_CURRENT_R) & 0x00FFFFFF; `
  4. Iterate into a loop until the elapsed time meets the 'delay' parameters:
elapsedTime = (startTime - NVIC_ST_CURRENT_R) & 0x00FFFFFF;
while(elapsedTime <= delay){
		elapsedTime = (startTime - NVIC_ST_CURRENT_R) & 0x00FFFFFF;
	}
Our SysTick_Wait( 'argument' ) function below will be used in the next function. But first we need to calculate how many iterations our SysTick_Wait() 'argument' must take in order to delay the uC proccess for 1ms.
void SysTick_Wait(unsigned long delay){
  volatile unsigned long elapsedTime;
  unsigned long startTime = NVIC_ST_CURRENT_R;
	
	elapsedTime = (startTime - NVIC_ST_CURRENT_R) & 0x00FFFFFF;
	
	while(elapsedTime <= delay){
		elapsedTime = (startTime - NVIC_ST_CURRENT_R) & 0x00FFFFFF;
	}
}
  1. Algebraically calculate the value for the argument that SysTick_Wait('arg') must take in order to delay the process for 1ms (this argument gets passed to the STRELOAD register on initilization):
    • STREALOAD * [(1/System Clock)] = 1ms
      • STREALOAD * [(1/80MHz)] = 1ms
        • STREALOAD * 12.5ns = 1ms
          • STREALOAD = 1ms/12.5ns
          • STREALOAD = (1^-3)/(12.5^-9)
          • STREALOAD = 80,000

6) Create a new function that will iterate the first function as per "unsigned long delay" argument:

void SysTick_Wait1ms(unsigned long delay){
  unsigned long i;
  for(i=0; i<delay; i++){
    SysTick_Wait(50000);  // wait 1ms (assumes 50 MHz clock)
  }
}
⚠️ **GitHub.com Fallback** ⚠️