SoC
  • Home
  • Arm
  • Arm Cortex M0/M0+
  • Arm Cortex M4
  • Arm Cortex M3
  • Contact
Reading: Techniques for Dealing with SysTick’s 24-bit Counter (Cortex-M4)
SUBSCRIBE
SoCSoC
Font ResizerAa
  • Home
  • Arm
  • Arm Cortex M0/M0+
  • Arm Cortex M4
Search
  • Home
  • Arm
  • Arm Cortex M0/M0+
  • Arm Cortex M4
Have an existing account? Sign In
Follow US
  • Looking for Something?
  • Privacy Policy
  • About Us
  • Sitemap
  • Contact Us
© S-O-C.ORG, All Rights Reserved.
Arm Cortex M4

Techniques for Dealing with SysTick’s 24-bit Counter (Cortex-M4)

Graham Kruk
Last updated: October 5, 2023 10:08 am
Graham Kruk 6 Min Read
Share
SHARE

The 24-bit SysTick counter in Cortex-M4 can be tricky to handle due to its limited range. Here are some techniques to deal with the counter wrapping around at 224:

Contents
1. Use a Larger Variable to Store the Counter Value2. Reset the Counter Periodically3. Use Two Variables to Extend the Range4. Use Tickless Mode5. Trigger an Event on Overflow6. Use a Circular Buffer7. Use a Timebase Counter8. Use Double Buffering9. Use a Bigger SysTick10. Use an External CounterSummary

1. Use a Larger Variable to Store the Counter Value

The simplest way is to use a 32-bit or 64-bit variable to store the SysTick counter value. This gives you a much larger range before wrapping around. For example:

uint32_t systickCounter;

// In SysTick handler
systickCounter += 1; 

Now the counter will overflow after 232 ticks instead of 224 ticks. Just make sure to initialize it properly on startup.

2. Reset the Counter Periodically

If you need to use the 24-bit counter directly, you can reset it periodically before it overflows. For example, reset it every 223 ticks:

// In SysTick handler
if(systickCounter == 0x7FFFFF) {
  systickCounter = 0;
} else {
  systickCounter++;
}

This will reset the counter once it reaches the max value, preventing overflow.

3. Use Two Variables to Extend the Range

Another method is to use two variables – one to count the 24-bit wraps, and another to store the current 24-bit value. For example:

uint32_t systickWraps = 0;
uint32_t systickCounter = 0;

// In SysTick handler
if(systickCounter == 0xFFFFFF) {
  systickWraps++;
  systickCounter = 0;
} else {
  systickCounter++; 
}

// Current tick value = systickWraps * 224 + systickCounter

This extends the range to 232 ticks while still using the 24-bit counter.

4. Use Tickless Mode

In tickless mode, the SysTick counter is stopped when the processor enters sleep. When it wakes up, the SysTick reload value is reinitialized based on the sleep duration to maintain the correct counter value.

This prevents the counter from overflowing while the processor is sleeping for long periods.

// Enter sleep 
systickCounter = /* current value */;

// On wake, reinit counter
systickCounter += /* sleep duration */; 

This is useful for low power applications where the processor may sleep for hours.

5. Trigger an Event on Overflow

Instead of handling the overflow directly, you can trigger an event or call a function when the 24-bit counter overflows:

// In SysTick handler
if(systickCounter == 0xFFFFFF) {
  handleSysTickOverflow();
  systickCounter = 0;
} else {
  systickCounter++;
}

void handleSysTickOverflow() {
  // Handle overflow here
}

This way, the event handler encapsulates the overflow logic separately from the main counter logic.

6. Use a Circular Buffer

For counting periodic events, a circular buffer stores events even if the counter overflows. For example:

#define BUF_SIZE 100
uint8_t eventBuf[BUF_SIZE];
uint8_t head = 0, tail = 0;

// In SysTick handler
eventBuf[head++] = /* event */; 
if(head >= BUF_SIZE)
  head = 0; 

// Read events   
while(tail != head) {
  processEvent(eventBuf[tail++]);
  if(tail >= BUF_SIZE)
    tail = 0;
}

This buffers the last 100 events regardless of counter value.

7. Use a Timebase Counter

A timebase counter runs at a slower frequency than SysTick but has a larger bit width. For example, a 64-bit timebase counter incremented every 223 SysTick cycles:

uint64_t timebase = 0;
uint32_t systickCounter = 0;

// In SysTick handler
systickCounter++;
if(systickCounter == 0xFFFFFF) {
  timebase++;
  systickCounter = 0; 
}

Now timebase can keep track of time over a much longer period. The tradeoff is reduced time resolution.

8. Use Double Buffering

With double buffering, you alternate between two counters so that when one wraps around, you can swap to the other one:

uint32_t systickCounter1 = 0;
uint32_t systickCounter2 = 0;
uint32_t activeCounter = 1;

// In systick handler
if(activeCounter == 1) {
  systickCounter1++;
  if(systickCounter1 == 0xFFFFFF) {
    activeCounter = 2;
  }
} else {
  systickCounter2++;
  if(systickCounter2 == 0xFFFFFF) {    
    activeCounter = 1; 
  }
}   

This lets you extend the range while still using 24-bit counters.

9. Use a Bigger SysTick

Some Cortex-M4 MCUs allow configuring SysTick to be 32 bits or even 64 bits wide. This greatly extends the overflow period. Check your MCU datasheet to see if a wider SysTick mode is available.

// Initialize bigger systick
systick_init(64); 

uint64_t systickCounter;

// systickCounter now has 64-bit range

The downside is increased memory usage if you don’t need the extended range.

10. Use an External Counter

For very long time periods, an additional external counter chip can be used. For example:

ExternalCounter externalCount;

// In SysTick handler  
externalCount.tick();

// externalCount stores a very large counter value

This frees the MCU from counter size limitations. Tradeoffs are increased cost, board space, and power consumption.

Summary

In summary, here are some effective techniques to handle SysTick’s 24-bit counter wrapping in Cortex-M4:

  • Use a larger variable to store the tick count
  • Reset the counter periodically
  • Use two variables to extend the range
  • Use tickless mode when sleeping
  • Trigger an event/function on overflow
  • Buffer ticks in a circular buffer
  • Use a lower frequency timebase
  • Double buffering with two counters
  • Use a wider SysTick if available
  • Add an external counter chip

Each method has tradeoffs between complexity, memory usage, accuracy, and power. Choose the technique most suitable for your application requirements.

Newsletter Form (#3)

More ARM insights right in your inbox

 


Share This Article
Facebook Twitter Email Copy Link Print
Previous Article Differences between LDR and STR
Next Article What is the Current Program Status Register (CPSR) in Arm Cortex-M?
Leave a comment Leave a comment

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

2k Followers Like
3k Followers Follow
10.1k Followers Pin
- Sponsored-
Ad image

You Might Also Like

Pipelining Instructions After LDR vs STR on Cortex M4

When executing load (LDR) and store (STR) instructions on the…

6 Min Read

Demystifying Cortex M4 LDR/STR Instruction Timing

The Cortex-M4 processor implements the ARMv7E-M architecture. One of the…

6 Min Read

Reducing Load/Store Instruction Latency on Cortex M4

The Cortex-M4 processor is designed to provide high performance and…

7 Min Read

Understanding Pipeline Hazards in Cortex-M4 Microcontrollers

The Cortex-M4 processor implements a 3-stage pipeline to improve performance…

8 Min Read
SoCSoC
  • Looking for Something?
  • Privacy Policy
  • About Us
  • Sitemap
  • Contact Us
Welcome Back!

Sign in to your account