/** * @file microsecond_timer_stm32.cpp * * A single upcounting timer (currently TIM5) is used as a single timebase both for time * measurement and event scheduling. This helps reduce jitter by not making another time * measurement at the time of scheduling. * * This implementation only works on stm32 because it sets hardware registers directly. * ChibiOS doesn't support using timers in output compare mode, only PMW, so we have to * manually configure the timer in outupt compare mode. * * @date Dec 1, 2020 * @author Matthew Kennedy, (c) 2012-2020 */ #include "global.h" #if EFI_PROD_CODE && HAL_USE_PWM #include "port_microsecond_timer.h" void portSetHardwareSchedulerTimer(efitick_t nowNt, efitick_t setTimeNt) { // This implementation doesn't need the current time, only the target time UNUSED(nowNt); pwm_lld_enable_channel(&SCHEDULER_PWM_DEVICE, 0, setTimeNt); pwmEnableChannelNotificationI(&SCHEDULER_PWM_DEVICE, 0); } static void hwTimerCallback(PWMDriver*) { pwmDisableChannelNotificationI(&SCHEDULER_PWM_DEVICE, 0); portMicrosecondTimerCallback(); } static constexpr PWMConfig timerConfig = { .frequency = SCHEDULER_TIMER_FREQ, /* wanted timer period = 2^32 counts, * but driver set (period - 1) value to register * also period is uint32_t * So set it to zero so it will overlap to 0xffffffff when writen to register */ .period = 0, .callback = nullptr, // No update callback .channels = { {PWM_OUTPUT_DISABLED, hwTimerCallback}, // Channel 0 = timer callback, others unused {PWM_OUTPUT_DISABLED, nullptr}, {PWM_OUTPUT_DISABLED, nullptr}, {PWM_OUTPUT_DISABLED, nullptr} }, .cr2 = 0, #if STM32_PWM_USE_ADVANCED .bdtr = 0, #endif .dier = 0 }; void portInitMicrosecondTimer() { pwmStart(&SCHEDULER_PWM_DEVICE, &timerConfig); // ChibiOS doesn't let you configure timers in output compare mode, only PWM mode. // We want to be able to set the compare register without waiting for an update event // (which would take 358 seconds at 12mhz timer speed), so we have to use normal upcounting // output compare mode instead. SCHEDULER_TIMER_DEVICE->CCMR1 = STM32_TIM_CCMR1_OC1M(1); } uint32_t getTimeNowLowerNt() { // Using the same timer for measurement and scheduling improves // precision and reduces jitter. return SCHEDULER_TIMER_DEVICE->CNT; } #endif // EFI_PROD_CODE