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:
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.