Microsecond (µs) Delay Implementation for STM32H743 Using SysTick


Here's a complete, optimized implementation for the STM32H743 (Cortex-M7) with 480MHz clock capability, including advanced techniques for maximum precision.
1. SysTick Configuration for STM32H743
Basic Polling Implementation
c
#include "stm32h7xx.h"
// Initialize SysTick for µs delays (no interrupt)
void SysTick_Init(void) {
// STM32H743 runs at up to 480MHz, but check your actual clock
uint32_t clock = SystemCoreClock;
// Configure for 1µs ticks (adjust if clock not multiple of 1MHz)
SysTick->LOAD = (clock / 1000000) - 1;
SysTick->VAL = 0;
SysTick->CTRL = SysTick_CTRL_ENABLE_Msk |
SysTick_CTRL_CLKSOURCE_Msk; // Use processor clock
}
// Blocking delay in microseconds
void delay_us(uint32_t us) {
SysTick->VAL = 0; // Reset counter
while (us--) {
while (!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk));
}
}
Key Points for STM32H743
Clock Warning: The H7 can run up to 480MHz. Verify your actual clock with:
c
printf("System clock: %lu Hz\n", SystemCoreClock);
Division Handling: If
SystemCoreClock
isn't a multiple of 1MHz (e.g., 400MHz), use:c
SysTick->LOAD = (SystemCoreClock / 1000000) - 1; // Truncates remainder
2. Advanced: DWT Cycle Counter (Nanosecond Precision)
The STM32H743's Data Watchpoint and Trace (DWT) unit provides cycle-accurate timing.
DWT Implementation
c
#define DEMCR (*((volatile uint32_t *)0xE000EDFC))
#define DWT_CTRL (*((volatile uint32_t *)0xE0001000))
#define DWT_CYCCNT (*((volatile uint32_t *)0xE0001004))
void DWT_Init(void) {
DEMCR |= 1 << 24; // Enable DWT
DWT_CYCCNT = 0; // Reset counter
DWT_CTRL |= 1 << 0; // Enable cycle counting
}
// Delay in microseconds using CPU cycles
void delay_us(uint32_t us) {
uint32_t start = DWT_CYCCNT;
uint32_t cycles = us * (SystemCoreClock / 1000000);
while ((DWT_CYCCNT - start) < cycles);
}
Advantages
Sub-µs precision: At 480MHz, 1 cycle = 2.08ns.
No timer configuration: Uses built-in debug hardware.
3. Hybrid Approach (SysTick + DWT)
Combine both methods for flexibility:
c
void delay_us(uint32_t us) {
// Use DWT if available (more precise)
if (DWT_CTRL & 1) {
uint32_t start = DWT_CYCCNT;
uint32_t cycles = us * (SystemCoreClock / 1000000);
while ((DWT_CYCCNT - start) < cycles);
}
// Fallback to SysTick
else {
SysTick->VAL = 0;
while (us--) {
while (!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk));
}
}
}
4. STM32H743-Specific Optimizations
Cache Considerations
The H7 has L1 cache. For consistent timing:
c
SCB_EnableICache(); // Enable instruction cache
SCB_EnableDCache(); // Enable data cache (if needed)
Clock Tree Verification
Confirm your clock settings in SystemClock_Config()
:
c
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 5; // Input divider
RCC_OscInitStruct.PLL.PLLN = 192; // VCO multiplier
RCC_OscInitStruct.PLL.PLLP = 2; // System clock divider
RCC_OscInitStruct.PLL.PLLQ = 4; // For peripherals
HAL_RCC_OscConfig(&RCC_OscInitStruct);
5. Testing & Validation
Oscilloscope Test
Toggle a GPIO pin before/after delay:
c
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET); delay_us(10); // Should show 10µs pulse HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
Measure pulse width to verify accuracy.
Serial Debug Output
c
uint32_t start = DWT_CYCCNT;
delay_us(100);
uint32_t elapsed = DWT_CYCCNT - start;
printf("Actual delay: %lu cycles (%lu us)\n",
elapsed, elapsed / (SystemCoreClock / 1000000));
6. Performance Comparison
Method | Precision | CPU Overhead | Max Delay |
SysTick Polling | ±1µs | 100% | ~89ms @480MHz |
DWT Counter | ±2.08ns | 0% | Unlimited |
TIM Hardware | ±10ns | 0% | Unlimited |
7. Troubleshooting STM32H743
Issue | Solution |
No delay occurs | Check SystemCoreClock value and clock tree configuration. |
Delay too long/short | Verify PLL settings in SystemClock_Config() . |
DWT not working | Ensure DEMCR bit 24 is set before enabling DWT. |
Inconsistent timing | Disable interrupts or use __disable_irq() during critical delays. |
8. Recommended Approach
For general use: SysTick polling (simple and reliable).
For extreme precision: DWT cycle counter.
For long delays (>100ms): Combine with HAL's
HAL_Delay()
.
c
// Example: 100µs delay with DWT fallback to SysTick
void delay_us(uint32_t us) {
if (DWT_CTRL & 1) { // DWT available
uint32_t start = DWT_CYCCNT;
uint32_t cycles = us * (SystemCoreClock / 1000000);
while ((DWT_CYCCNT - start) < cycles);
} else { // SysTick fallback
SysTick->VAL = 0;
while (us--) {
while (!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk));
}
}
}
Subscribe to my newsletter
Read articles from ampheo directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
