From bc856749bd359459245be7ea4dc3fcdcc46f3a10 Mon Sep 17 00:00:00 2001 From: victhor Date: Sat, 21 Feb 2026 19:25:57 +0100 Subject: [PATCH 01/44] Start of new InputCapture, need to process capture compare interrupts diff --git a/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp b/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp index 98af03b9..658a2004 100644 --- a/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp +++ b/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp @@ -146,9 +146,11 @@ TimerXList CHANNEL_NEGATED_FLAG = 8, - CHANNEL_1_NEGATED = 1 | CHANNEL_NEGATED_FLAG, - CHANNEL_2_NEGATED = 2 | CHANNEL_NEGATED_FLAG, - CHANNEL_3_NEGATED = 3 | CHANNEL_NEGATED_FLAG, + CHANNEL_1_NEGATED = CHANNEL_1 | CHANNEL_NEGATED_FLAG, + CHANNEL_2_NEGATED = CHANNEL_2 | CHANNEL_NEGATED_FLAG, + CHANNEL_3_NEGATED = CHANNEL_3 | CHANNEL_NEGATED_FLAG, + // STM32h723xx does not have a negated channel 4, but other stm microcontrollers do + CHANNEL_4_NEGATED = CHANNEL_4 | CHANNEL_NEGATED_FLAG, }; struct TimerPin { @@ -699,6 +701,53 @@ TimerXList } } }; + + static consteval uint8_t get_channel_state_idx(const ST_LIB::TimerChannel ch) { + switch (ch) { + case TimerChannel::CHANNEL_1: + case TimerChannel::CHANNEL_1_NEGATED: + case TimerChannel::CHANNEL_2: + case TimerChannel::CHANNEL_2_NEGATED: + case TimerChannel::CHANNEL_3: + case TimerChannel::CHANNEL_3_NEGATED: + case TimerChannel::CHANNEL_4: + case TimerChannel::CHANNEL_4_NEGATED: + case TimerChannel::CHANNEL_5: + case TimerChannel::CHANNEL_6: + return (static_cast(ch) & + ~static_cast(TimerChannel::CHANNEL_NEGATED_FLAG)) - + 1; + + default: + ST_LIB::compile_error("unreachable"); + return 0; + } + } + + static consteval uint8_t get_channel_mul4(const ST_LIB::TimerChannel ch) { + switch (ch) { + case TimerChannel::CHANNEL_1: + case TimerChannel::CHANNEL_1_NEGATED: + return 0x00; + case TimerChannel::CHANNEL_2: + case TimerChannel::CHANNEL_2_NEGATED: + return 0x04; + case TimerChannel::CHANNEL_3: + case TimerChannel::CHANNEL_3_NEGATED: + return 0x08; + case TimerChannel::CHANNEL_4: + case TimerChannel::CHANNEL_4_NEGATED: + return 0x0C; + case TimerChannel::CHANNEL_5: + return 0x10; + case TimerChannel::CHANNEL_6: + return 0x14; + + default: + ST_LIB::compile_error("unreachable"); + return 0; + } + } }; consteval GPIODomain::AlternateFunction TimerDomain::Timer::get_gpio_af( diff --git a/Inc/HALAL/Services/InputCapture/InputCapture.hpp b/Inc/HALAL/Services/InputCapture/InputCapture.hpp new file mode 100644 index 00000000..0100b08e --- /dev/null +++ b/Inc/HALAL/Services/InputCapture/InputCapture.hpp @@ -0,0 +1,160 @@ +/* + * InputCapture.hpp + * + * Created on: 17 feb. 2026 + * Author: victor + */ +#pragma once + +#include "HALAL/Models/TimerDomain/TimerDomain.hpp" +#ifdef HAL_TIM_MODULE_ENABLED +#include "HALAL/Models/GPIO.hpp" + +namespace ST_LIB { + +template struct TimerWrapper; + +template < + const ST_LIB::TimerDomain::Timer& dev, + const ST_LIB::TimerPin pin_rising, + const ST_LIB::TimerPin pin_falling> +class InputCapture { + TimerWrapper* timer; + uint32_t frequency; + uint8_t duty_cycle; + bool is_on; + +public: + InputCapture(TimerWrapper* tim) : timer(tim) { + TIM_IC_InitTypeDef sConfigIC = { + .ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING, + .ICPrescaler = TIM_ICPSC_DIV1, + .ICFilter = 0, + .ICSelection = TIM_ICSELECTION_DIRECTTI, + }; + timer->template config_input_compare_channel(&sConfigIC); + + sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_FALLING; + sConfigIC.ICSelection = TIM_ICSELECTION_INDIRECTTI; + timer->template config_input_compare_channel(&sConfigIC); + } + + static void turn_on(void) { + if (this->is_on) + return; + + // HAL_TIM_IC_Start_IT(instance.peripheral->handle, instance.channel_rising) + { + volatile HAL_TIM_ChannelStateTypeDef* ch_state = + &timer->instance->hal_tim->ChannelState[TimerDomain::get_channel_state_idx(pin_rising.channel)]; + volatile HAL_TIM_ChannelStateTypeDef* n_ch_state = + &timer->instance->hal_tim->ChannelNState[TimerDomain::get_channel_state_idx(pin_rising.channel)]; + if ((*ch_state != HAL_TIM_CHANNEL_STATE_READY) || (*n_ch_state != HAL_TIM_CHANNEL_STATE_READY)) { + ErrorHandler("Channels not ready"); + } + + *ch_state = HAL_TIM_CHANNEL_STATE_BUSY; + *n_ch_state = HAL_TIM_CHANNEL_STATE_BUSY; + + timer->template enable_capture_compare_interrupt(); + uint32_t enableCCx = TIM_CCER_CC1E + << (TimerDomain::get_channel_mul4(pin_rising.channel) & 0x1FU + ); /* 0x1FU = 31 bits max shift */ + SET_BIT(timer->instance->tim->CCER, enableCCx); + } + + // HAL_TIM_IC_Start(instance.peripheral->handle, instance.channel_falling) + { + volatile HAL_TIM_ChannelStateTypeDef* ch_state = + &timer->instance->hal_tim->ChannelState[TimerDomain::get_channel_state_idx(pin_falling.channel)]; + volatile HAL_TIM_ChannelStateTypeDef* n_ch_state = + &timer->instance->hal_tim->ChannelNState[TimerDomain::get_channel_state_idx(pin_falling.channel)]; + if ((*ch_state != HAL_TIM_CHANNEL_STATE_READY) || (*n_ch_state != HAL_TIM_CHANNEL_STATE_READY)) { + ErrorHandler("Channels not ready"); + } + + *ch_state = HAL_TIM_CHANNEL_STATE_BUSY; + *n_ch_state = HAL_TIM_CHANNEL_STATE_BUSY; + + uint32_t enableCCx = TIM_CCER_CC1E + << (TimerDomain::get_channel_mul4(pin_falling.channel) & 0x1FU + ); /* 0x1FU = 31 bits max shift */ + SET_BIT(timer->instance->tim->CCER, enableCCx); + } + + if constexpr (timer->is_slave_instance) { + uint32_t tmpsmcr = timer->instance->tim->SMCR & SMCR_SMS; + if (!IS_TIM_SLAVEMODE_TRIGGER_ENABLED(tmpsmcr)) { + timer->counter_enable(); + } + } else { + timer->counter_enable(); + } + + this->is_on = true; + } + + static void turn_off(void) { + if(!this->is_on) + return; + + // HAL_TIM_IC_Stop_IT(instance.peripheral->handle, instance.channel_rising) + { + timer->template disable_capture_compare_interrupt(); + + CLEAR_BIT( + timer->instance->tim->CCER, + (uint32_t)(TIM_CCER_CC1E << (TimerDomain::get_channel_mul4(pin_rising.channel) & 0x1FU)) + ); + + volatile HAL_TIM_ChannelStateTypeDef* ch_state = + &timer->instance->hal_tim->ChannelState[TimerDomain::get_channel_state_idx(pin_falling.channel)]; + volatile HAL_TIM_ChannelStateTypeDef* n_ch_state = + &timer->instance->hal_tim->ChannelNState[TimerDomain::get_channel_state_idx(pin_falling.channel)]; + *ch_state = HAL_TIM_CHANNEL_STATE_READY; + *ch_n_state = HAL_TIM_CHANNEL_STATE_READY; + } + + // HAL_TIM_IC_Stop(instance.peripheral->handle, instance.channel_falling) + { + CLEAR_BIT( + timer->instance->tim->CCER, + (uint32_t)(TIM_CCER_CC1E << (TimerDomain::get_channel_mul4(pin_rising.channel) & 0x1FU)) + ); + + volatile HAL_TIM_ChannelStateTypeDef* ch_state = + &timer->instance->hal_tim->ChannelState[TimerDomain::get_channel_state_idx(pin_falling.channel)]; + volatile HAL_TIM_ChannelStateTypeDef* n_ch_state = + &timer->instance->hal_tim->ChannelNState[TimerDomain::get_channel_state_idx(pin_falling.channel)]; + *ch_state = HAL_TIM_CHANNEL_STATE_READY; + *ch_n_state = HAL_TIM_CHANNEL_STATE_READY; + } + + if (timer->are_all_channels_free()) { + timer->counter_disable(); + } + +#error TODO + if (HAL_TIM_IC_Stop(instance.peripheral->handle, instance.channel_falling) != HAL_OK) { + ErrorHandler( + "Unable to stop the %s Input Capture measurement", + instance.peripheral->name.c_str() + ); + } + + + this->is_on = false; + } + + static uint32_t read_frequency(void) { + return this->frequency; + } + + static uint8_t read_duty_cycle(void) { + return this->duty_cycle; + } +}; + +}; + +#endif // HAL_TIM_MODULE_ENABLED diff --git a/Inc/HALAL/Services/PWM/DualPWM.hpp b/Inc/HALAL/Services/PWM/DualPWM.hpp index 26c36d07..84f99b02 100644 --- a/Inc/HALAL/Services/PWM/DualPWM.hpp +++ b/Inc/HALAL/Services/PWM/DualPWM.hpp @@ -19,51 +19,6 @@ template < const ST_LIB::TimerPin pin, const ST_LIB::TimerPin negated_pin> class DualPWM { - static consteval uint8_t get_channel_state_idx(const ST_LIB::TimerChannel ch) { - switch (ch) { - case TimerChannel::CHANNEL_1: - case TimerChannel::CHANNEL_1_NEGATED: - case TimerChannel::CHANNEL_2: - case TimerChannel::CHANNEL_2_NEGATED: - case TimerChannel::CHANNEL_3: - case TimerChannel::CHANNEL_3_NEGATED: - case TimerChannel::CHANNEL_4: - case TimerChannel::CHANNEL_5: - case TimerChannel::CHANNEL_6: - return (static_cast(ch) & - ~static_cast(TimerChannel::CHANNEL_NEGATED_FLAG)) - - 1; - - default: - ST_LIB::compile_error("unreachable"); - return 0; - } - } - - static consteval uint8_t get_channel_mul4(const ST_LIB::TimerChannel ch) { - switch (ch) { - case TimerChannel::CHANNEL_1: - case TimerChannel::CHANNEL_1_NEGATED: - return 0x00; - case TimerChannel::CHANNEL_2: - case TimerChannel::CHANNEL_2_NEGATED: - return 0x04; - case TimerChannel::CHANNEL_3: - case TimerChannel::CHANNEL_3_NEGATED: - return 0x08; - case TimerChannel::CHANNEL_4: - return 0x0C; - case TimerChannel::CHANNEL_5: - return 0x10; - case TimerChannel::CHANNEL_6: - return 0x14; - - default: - ST_LIB::compile_error("unreachable"); - return 0; - } - } - TimerWrapper* timer; uint32_t* frequency; float* duty_cycle = nullptr; @@ -112,18 +67,15 @@ public: if (this->is_on_positive) return; - // if(HAL_TIM_PWM_Start(timer->instance->hal_tim, channel) != HAL_OK) { ErrorHandler("", 0); - // } volatile HAL_TIM_ChannelStateTypeDef* state = - &timer->instance->hal_tim->ChannelState[get_channel_state_idx(pin.channel)]; + &timer->instance->hal_tim->ChannelState[TimerDomain::get_channel_state_idx(pin.channel)]; if (*state != HAL_TIM_CHANNEL_STATE_READY) { ErrorHandler("Channel not ready"); } *state = HAL_TIM_CHANNEL_STATE_BUSY; - // enable CCx uint32_t enableCCx = TIM_CCER_CC1E - << (get_channel_mul4(pin.channel) & 0x1FU + << (TimerDomain::get_channel_mul4(pin.channel) & 0x1FU ); /* 0x1FU = 31 bits max shift */ SET_BIT(timer->instance->tim->CCER, enableCCx); @@ -148,18 +100,15 @@ public: if (this->is_on_negative) return; - // if(HAL_TIM_PWM_Start(timer->instance->hal_tim, channel) != HAL_OK) { ErrorHandler("", 0); - // } volatile HAL_TIM_ChannelStateTypeDef* state = - &timer->instance->hal_tim->ChannelNState[get_channel_state_idx(negated_pin.channel)]; + &timer->instance->hal_tim->ChannelNState[TimerDomain::get_channel_state_idx(negated_pin.channel)]; if (*state != HAL_TIM_CHANNEL_STATE_READY) { ErrorHandler("Channel not ready"); } *state = HAL_TIM_CHANNEL_STATE_BUSY; - // enable CCNx uint32_t enableCCNx = TIM_CCER_CC1NE - << (get_channel_mul4(negated_pin.channel) & 0x1FU + << (TimerDomain::get_channel_mul4(negated_pin.channel) & 0x1FU ); /* 0x1FU = 31 bits max shift */ SET_BIT(timer->instance->tim->CCER, enableCCNx); @@ -186,11 +135,11 @@ public: CLEAR_BIT( timer->tim->CCER, - (uint32_t)(TIM_CCER_CC1E << (get_channel_mul4(pin.channel) & 0x1FU)) + (uint32_t)(TIM_CCER_CC1E << (TimerDomain::get_channel_mul4(pin.channel) & 0x1FU)) ); HAL_TIM_ChannelStateTypeDef* state = - &timer->instance->hal_tim.ChannelState[get_channel_state_idx(pin.channel)]; + &timer->instance->hal_tim.ChannelState[TimerDomain::get_channel_state_idx(pin.channel)]; *state = HAL_TIM_CHANNEL_STATE_READY; if (timer->are_all_channels_free()) { @@ -210,11 +159,11 @@ public: CLEAR_BIT( timer->tim->CCER, - (uint32_t)(TIM_CCER_CC1NE << (get_channel_mul4(negated_pin.channel) & 0x1FU)) + (uint32_t)(TIM_CCER_CC1NE << (TimerDomain::get_channel_mul4(negated_pin.channel) & 0x1FU)) ); HAL_TIM_ChannelStateTypeDef* state = - &timer->instance->hal_tim.ChannelState[get_channel_state_idx(negated_pin.channel)]; + &timer->instance->hal_tim.ChannelState[TimerDomain::get_channel_state_idx(negated_pin.channel)]; *state = HAL_TIM_CHANNEL_STATE_READY; if (timer->are_all_channels_free()) { diff --git a/Inc/HALAL/Services/PWM/PWM.hpp b/Inc/HALAL/Services/PWM/PWM.hpp index b1d4cdb3..0c60f464 100644 --- a/Inc/HALAL/Services/PWM/PWM.hpp +++ b/Inc/HALAL/Services/PWM/PWM.hpp @@ -14,52 +14,10 @@ namespace ST_LIB { template struct TimerWrapper; -template class PWM { - static consteval uint8_t get_channel_state_idx(const ST_LIB::TimerChannel ch) { - switch (ch) { - case TimerChannel::CHANNEL_1: - case TimerChannel::CHANNEL_1_NEGATED: - case TimerChannel::CHANNEL_2: - case TimerChannel::CHANNEL_2_NEGATED: - case TimerChannel::CHANNEL_3: - case TimerChannel::CHANNEL_3_NEGATED: - case TimerChannel::CHANNEL_4: - case TimerChannel::CHANNEL_5: - case TimerChannel::CHANNEL_6: - return (static_cast(ch) & - ~static_cast(TimerChannel::CHANNEL_NEGATED_FLAG)) - - 1; - - default: - ST_LIB::compile_error("unreachable"); - return 0; - } - } - - static consteval uint8_t get_channel_mul4(const ST_LIB::TimerChannel ch) { - switch (ch) { - case TimerChannel::CHANNEL_1: - case TimerChannel::CHANNEL_1_NEGATED: - return 0x00; - case TimerChannel::CHANNEL_2: - case TimerChannel::CHANNEL_2_NEGATED: - return 0x04; - case TimerChannel::CHANNEL_3: - case TimerChannel::CHANNEL_3_NEGATED: - return 0x08; - case TimerChannel::CHANNEL_4: - return 0x0C; - case TimerChannel::CHANNEL_5: - return 0x10; - case TimerChannel::CHANNEL_6: - return 0x14; - - default: - ST_LIB::compile_error("unreachable"); - return 0; - } - } - +template < + const TimerDomain::Timer& dev, + const ST_LIB::TimerPin pin> +class PWM { TimerWrapper* timer; uint32_t* frequency; float* duty_cycle = nullptr; @@ -94,18 +52,15 @@ public: if (this->is_on) return; - // if(HAL_TIM_PWM_Start(timer->instance->hal_tim, channel) != HAL_OK) { ErrorHandler("", 0); - // } volatile HAL_TIM_ChannelStateTypeDef* state = - &timer->instance->hal_tim->ChannelState[get_channel_state_idx(pin.channel)]; + &timer->instance->hal_tim->ChannelState[TimerDomain::get_channel_state_idx(pin.channel)]; if (*state != HAL_TIM_CHANNEL_STATE_READY) { ErrorHandler("Channel not ready"); } *state = HAL_TIM_CHANNEL_STATE_BUSY; - // enable CCx uint32_t enableCCx = TIM_CCER_CC1E - << (get_channel_mul4(pin.channel) & 0x1FU + << (TimerDomain::get_channel_mul4(pin.channel) & 0x1FU ); /* 0x1FU = 31 bits max shift */ SET_BIT(timer->instance->tim->CCER, enableCCx); @@ -132,11 +87,11 @@ public: CLEAR_BIT( timer->instance->tim->CCER, - (uint32_t)(TIM_CCER_CC1E << (get_channel_mul4(pin.channel) & 0x1FU)) + (uint32_t)(TIM_CCER_CC1E << (TimerDomain::get_channel_mul4(pin.channel) & 0x1FU)) ); volatile HAL_TIM_ChannelStateTypeDef* state = - &timer->instance->hal_tim->ChannelState[get_channel_state_idx(pin.channel)]; + &timer->instance->hal_tim->ChannelState[TimerDomain::get_channel_state_idx(pin.channel)]; *state = HAL_TIM_CHANNEL_STATE_READY; if (timer->are_all_channels_free()) { diff --git a/Inc/HALAL/Services/Time/TimerWrapper.hpp b/Inc/HALAL/Services/Time/TimerWrapper.hpp index 0a81056a..4ee79c19 100644 --- a/Inc/HALAL/Services/Time/TimerWrapper.hpp +++ b/Inc/HALAL/Services/Time/TimerWrapper.hpp @@ -59,6 +59,18 @@ template struct TimerWrapper { dev.e.request == TimerRequest::GeneralPurpose_15 || dev.e.request == TimerRequest::GeneralPurpose_16 || dev.e.request == TimerRequest::GeneralPurpose_17); + /* at least 2 capture/compare channels (see IS_TIM_CC2_INSTANCE for macro definition) */ + static constexpr bool is_CC2_instance = + (dev.e.request == TimerRequest::Advanced_1 || + dev.e.request == TimerRequest::GeneralPurpose32bit_2 || + dev.e.request == TimerRequest::GeneralPurpose_3 || + dev.e.request == TimerRequest::GeneralPurpose_4 || + dev.e.request == TimerRequest::GeneralPurpose32bit_5 || + dev.e.request == TimerRequest::Advanced_8 || + dev.e.request == TimerRequest::SlaveTimer_12 || + dev.e.request == TimerRequest::GeneralPurpose_15 || + dev.e.request == TimerRequest::GeneralPurpose32bit_23 || + dev.e.request == TimerRequest::GeneralPurpose32bit_24); static constexpr bool is_slave_instance = (dev.e.request == TimerRequest::Advanced_1 || dev.e.request == TimerRequest::GeneralPurpose32bit_2 || @@ -368,8 +380,34 @@ template struct TimerWrapper { } } - /////////////////////////////////////////// - // Below are methods used by other objects + /////////////////////////////////////////////////////// + // Below are methods used by other objects (internals) + + template + inline void enable_capture_compare_interrupt(void) { + if constexpr((ch == TimerChannel::CHANNEL_1) || (ch == TimerChannel::CHANNEL_1_NEGATED)) { + SET_BIT(instance->tim->DIER, TIM_IT_CC1); + } else if constexpr((ch == TimerChannel::CHANNEL_2) || (ch == TimerChannel::CHANNEL_2_NEGATED)) { + SET_BIT(instance->tim->DIER, TIM_IT_CC2); + } else if constexpr((ch == TimerChannel::CHANNEL_3) || (ch == TimerChannel::CHANNEL_3_NEGATED)) { + SET_BIT(instance->tim->DIER, TIM_IT_CC3); + } else if constexpr((ch == TimerChannel::CHANNEL_4) || (ch == TimerChannel::CHANNEL_4_NEGATED)) { + SET_BIT(instance->tim->DIER, TIM_IT_CC4); + } + } + + template + inline void disable_capture_compare_interrupt(void) { + if constexpr((ch == TimerChannel::CHANNEL_1) || (ch == TimerChannel::CHANNEL_1_NEGATED)) { + CLEAR_BIT(instance->tim->DIER, TIM_IT_CC1); + } else if constexpr((ch == TimerChannel::CHANNEL_2) || (ch == TimerChannel::CHANNEL_2_NEGATED)) { + CLEAR_BIT(instance->tim->DIER, TIM_IT_CC2); + } else if constexpr((ch == TimerChannel::CHANNEL_3) || (ch == TimerChannel::CHANNEL_3_NEGATED)) { + CLEAR_BIT(instance->tim->DIER, TIM_IT_CC3); + } else if constexpr((ch == TimerChannel::CHANNEL_4) || (ch == TimerChannel::CHANNEL_4_NEGATED)) { + CLEAR_BIT(instance->tim->DIER, TIM_IT_CC4); + } + } template void set_pwm_frequency(uint32_t frequency) { @@ -434,6 +472,8 @@ template struct TimerWrapper { } } + /* NOTE(vic): Both config_output_compare_channel and config_input_compare_channel + * Could probably be done better if not using TIM_[OC/IC]_InitTypeDef structures */ template inline void config_output_compare_channel(const TIM_OC_InitTypeDef* OC_Config) { if constexpr (!((ch == TimerChannel::CHANNEL_1) || (ch == TimerChannel::CHANNEL_2) || @@ -587,6 +627,107 @@ template struct TimerWrapper { instance->tim->CCER = tmpccer; } + /* NOTE(vic): Both config_output_compare_channel and config_input_compare_channel + * Could probably be done better if not using TIM_[OC/IC]_InitTypeDef structures */ + template inline void config_input_compare_channel(TIM_IC_InitTypeDef* sConfig) { + if constexpr (!((ch == TimerChannel::CHANNEL_1) || (ch == TimerChannel::CHANNEL_2) || + (ch == TimerChannel::CHANNEL_3) || (ch == TimerChannel::CHANNEL_4))) { + ST_LIB::compile_error("Only channels 1 to 4 can be configured as input compare"); + return; + } + + uint32_t tmpccmrx; + uint32_t tmpccer; + + tmpccer = instance->tim->CCER; + if constexpr (ch == TimerChannel::CHANNEL_1 || ch == TimerChannel::CHANNEL_2) { + tmpccmrx = instance->tim->CCMR1; + } else if constexpr (ch == TimerChannel::CHANNEL_3 || ch == TimerChannel::CHANNEL_4) { + tmpccmrx = instance->tim->CCMR2; + } + + if constexpr((ch == TimerChannel::CHANNEL_1) || (ch == TimerChannel::CHANNEL_1_NEGATED)) { + CLEAR_BIT(tmpccer, TIM_CCER_CC1E); + + /* Select the input */ + if constexpr(this->is_CC2_instance) { + CLEAR_BIT(tmpccmrx, TIM_CCMR1_CC1S); + SET_BIT(tmpccmrx, sConfig->ICSelection); + } else { + SET_BIT(tmpccmrx, TIM_CCMR1_CC1S_0); + } + + /* Set the filter */ + CLEAR_BIT(tmpccmrx, TIM_CCMR1_IC1F); + SET_BIT(tmpccmrx, ((sConfig->ICFilter << 4U) & TIMM_CCMR1_IC1F)); + + /* Select the Polarity and set the CC1E Bit */ + CLEAR_BIT(tmpccer, TIM_CCER_CC1P | TIM_CCER_CC1NP); + SET_BIT(tmpccer, sConfig->ICPolarity & (TIM_CCER_CC1P | TIM_CCER_CC1NP)); + + CLEAR_BIT(tmpccmrx, TIM_CCMR1_IC1PSC); + SET_BIT(tmpccmrx, sConfig->ICPrescaler & TIM_CCMR1_IC1PSC); + } else if constexpr((ch == TimerChannel::CHANNEL_2) || (ch == TimerChannel::CHANNEL_2_NEGATED)) { + CLEAR_BIT(tmpccer, TIM_CCER_CC2E); + + /* Select the input */ + CLEAR_BIT(tmpccmrx, TIM_CCMR1_CC2S); + SET_BIT(tmpccmrx, sConfig->ICSelection << 8U); + + /* Set the filter */ + CLEAR_BIT(tmpccmrx, TIM_CCMR1_IC2F); + SET_BIT(tmpccmrx, (sConfig->ICFilter << 12U) & TIM_CCMR1_IC2F); + + /* Select the Polarity and set the CC2E Bit */ + CLEAR_BIT(tmpccer, TIM_CCER_CC2P | TIM_CCER_CC2NP); + SET_BIT(tmpccer, (sConfig->ICPolarity >> 4U) & (TIM_CCER_CC2P | TIM_CCER_CC2NP)); + + CLEAR_BIT(tmpccmrx, TIM_CCMR1_IC2PSC); + SET_BIT(tmpccmrx, sConfig->ICPrescaler << 8U); + } else if constexpr((ch == TimerChannel::CHANNEL_3) || (ch == TimerChannel::CHANNEL_3_NEGATED)) { + CLEAR_BIT(tmpccer, TIM_CCER_CC3E); + + /* Select the Input */ + CLEAR_BIT(tmpccmrx, TIM_CCMR2_CC3S); + SET_BIT(tmpccmrx, sConfig->ICSelection); + + /* Set the filter */ + CLEAR_BIT(tmpccmrx, TIM_CCMR2_IC3F); + SET_BIT(tmpccmrx, (sConfig->ICFilter << 4U) & TIM_CCMR2_IC3F); + + /* Select the polarity and set the CC3E Bit */ + CLEAR_BIT(tmpccer, TIM_CCER_CC3P | TIM_CCER_CC3NP); + SET_BIT(tmpccer, (sConfig->ICPolarity << 8U) & (TIM_CCER_CC3P | TIM_CCER_CC3NP)); + + CLEAR_BIT(tmpccmrx, TIM_CCMR2_IC3PSC); + SET_BIT(tmpccmrx, sConfig->ICPrescaler); + } else if constexpr(ch == TimerChannel::CHANNEL_4) { + CLEAR_BIT(tmpccer, TIM_CCER_CC4E); + + /* Select the Input */ + CLEAR_BIT(tmpccmrx, TIM_CCMR2_CC4S); + SET_BIT(tmpccmrx, sConfig->ICSelection << 8U); + + /* Set the filter */ + CLEAR_BIT(tmpccmrx, TIM_CCMR2_IC4F); + SET_BIT(tmpccmrx, (sConfig->ICFilter << 12U) & TIM_CCMR2_IC4F); + + /* Select the polarity and set the CC4E Bit */ + CLEAR_BIT(tmpccer, TIM_CCER_CC4P | TIM_CCER_CC4NP); + SET_BIT(tmpccer, (sConfig->ICPolarity << 12U) & (TIM_CCER_CC4P | TIM_CCER_CC4NP)); + + CLEAR_BIT(tmpccmrx, TIM_CCMR2_IC4PSC); + SET_BIT(tmpccmrx, sConfig->ICPrescaler << 8U); + } + + if constexpr (ch == TimerChannel::CHANNEL_1 || ch == TimerChannel::CHANNEL_2) { + instance->tim->CCMR1 = tmpccmrx; + } else if constexpr (ch == TimerChannel::CHANNEL_3 || ch == TimerChannel::CHANNEL_4) { + instance->tim->CCMR2 = tmpccmrx; + } + instance->tim->CCER = tmpccer; + } + template inline void set_output_compare_preload_enable() { if constexpr ((ch == TimerChannel::CHANNEL_1) || (ch == TimerChannel::CHANNEL_1_NEGATED)) { SET_BIT(instance->tim->CCMR1, TIM_CCMR1_OC1PE); --- Inc/HALAL/Models/TimerDomain/TimerDomain.hpp | 55 +++++- .../Services/InputCapture/InputCapture.hpp | 160 ++++++++++++++++++ Inc/HALAL/Services/PWM/DualPWM.hpp | 67 +------- Inc/HALAL/Services/PWM/PWM.hpp | 61 +------ Inc/HALAL/Services/Time/TimerWrapper.hpp | 145 +++++++++++++++- 5 files changed, 371 insertions(+), 117 deletions(-) create mode 100644 Inc/HALAL/Services/InputCapture/InputCapture.hpp diff --git a/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp b/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp index 98af03b94..658a20041 100644 --- a/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp +++ b/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp @@ -146,9 +146,11 @@ TimerXList CHANNEL_NEGATED_FLAG = 8, - CHANNEL_1_NEGATED = 1 | CHANNEL_NEGATED_FLAG, - CHANNEL_2_NEGATED = 2 | CHANNEL_NEGATED_FLAG, - CHANNEL_3_NEGATED = 3 | CHANNEL_NEGATED_FLAG, + CHANNEL_1_NEGATED = CHANNEL_1 | CHANNEL_NEGATED_FLAG, + CHANNEL_2_NEGATED = CHANNEL_2 | CHANNEL_NEGATED_FLAG, + CHANNEL_3_NEGATED = CHANNEL_3 | CHANNEL_NEGATED_FLAG, + // STM32h723xx does not have a negated channel 4, but other stm microcontrollers do + CHANNEL_4_NEGATED = CHANNEL_4 | CHANNEL_NEGATED_FLAG, }; struct TimerPin { @@ -699,6 +701,53 @@ TimerXList } } }; + + static consteval uint8_t get_channel_state_idx(const ST_LIB::TimerChannel ch) { + switch (ch) { + case TimerChannel::CHANNEL_1: + case TimerChannel::CHANNEL_1_NEGATED: + case TimerChannel::CHANNEL_2: + case TimerChannel::CHANNEL_2_NEGATED: + case TimerChannel::CHANNEL_3: + case TimerChannel::CHANNEL_3_NEGATED: + case TimerChannel::CHANNEL_4: + case TimerChannel::CHANNEL_4_NEGATED: + case TimerChannel::CHANNEL_5: + case TimerChannel::CHANNEL_6: + return (static_cast(ch) & + ~static_cast(TimerChannel::CHANNEL_NEGATED_FLAG)) - + 1; + + default: + ST_LIB::compile_error("unreachable"); + return 0; + } + } + + static consteval uint8_t get_channel_mul4(const ST_LIB::TimerChannel ch) { + switch (ch) { + case TimerChannel::CHANNEL_1: + case TimerChannel::CHANNEL_1_NEGATED: + return 0x00; + case TimerChannel::CHANNEL_2: + case TimerChannel::CHANNEL_2_NEGATED: + return 0x04; + case TimerChannel::CHANNEL_3: + case TimerChannel::CHANNEL_3_NEGATED: + return 0x08; + case TimerChannel::CHANNEL_4: + case TimerChannel::CHANNEL_4_NEGATED: + return 0x0C; + case TimerChannel::CHANNEL_5: + return 0x10; + case TimerChannel::CHANNEL_6: + return 0x14; + + default: + ST_LIB::compile_error("unreachable"); + return 0; + } + } }; consteval GPIODomain::AlternateFunction TimerDomain::Timer::get_gpio_af( diff --git a/Inc/HALAL/Services/InputCapture/InputCapture.hpp b/Inc/HALAL/Services/InputCapture/InputCapture.hpp new file mode 100644 index 000000000..0100b08eb --- /dev/null +++ b/Inc/HALAL/Services/InputCapture/InputCapture.hpp @@ -0,0 +1,160 @@ +/* + * InputCapture.hpp + * + * Created on: 17 feb. 2026 + * Author: victor + */ +#pragma once + +#include "HALAL/Models/TimerDomain/TimerDomain.hpp" +#ifdef HAL_TIM_MODULE_ENABLED +#include "HALAL/Models/GPIO.hpp" + +namespace ST_LIB { + +template struct TimerWrapper; + +template < + const ST_LIB::TimerDomain::Timer& dev, + const ST_LIB::TimerPin pin_rising, + const ST_LIB::TimerPin pin_falling> +class InputCapture { + TimerWrapper* timer; + uint32_t frequency; + uint8_t duty_cycle; + bool is_on; + +public: + InputCapture(TimerWrapper* tim) : timer(tim) { + TIM_IC_InitTypeDef sConfigIC = { + .ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING, + .ICPrescaler = TIM_ICPSC_DIV1, + .ICFilter = 0, + .ICSelection = TIM_ICSELECTION_DIRECTTI, + }; + timer->template config_input_compare_channel(&sConfigIC); + + sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_FALLING; + sConfigIC.ICSelection = TIM_ICSELECTION_INDIRECTTI; + timer->template config_input_compare_channel(&sConfigIC); + } + + static void turn_on(void) { + if (this->is_on) + return; + + // HAL_TIM_IC_Start_IT(instance.peripheral->handle, instance.channel_rising) + { + volatile HAL_TIM_ChannelStateTypeDef* ch_state = + &timer->instance->hal_tim->ChannelState[TimerDomain::get_channel_state_idx(pin_rising.channel)]; + volatile HAL_TIM_ChannelStateTypeDef* n_ch_state = + &timer->instance->hal_tim->ChannelNState[TimerDomain::get_channel_state_idx(pin_rising.channel)]; + if ((*ch_state != HAL_TIM_CHANNEL_STATE_READY) || (*n_ch_state != HAL_TIM_CHANNEL_STATE_READY)) { + ErrorHandler("Channels not ready"); + } + + *ch_state = HAL_TIM_CHANNEL_STATE_BUSY; + *n_ch_state = HAL_TIM_CHANNEL_STATE_BUSY; + + timer->template enable_capture_compare_interrupt(); + uint32_t enableCCx = TIM_CCER_CC1E + << (TimerDomain::get_channel_mul4(pin_rising.channel) & 0x1FU + ); /* 0x1FU = 31 bits max shift */ + SET_BIT(timer->instance->tim->CCER, enableCCx); + } + + // HAL_TIM_IC_Start(instance.peripheral->handle, instance.channel_falling) + { + volatile HAL_TIM_ChannelStateTypeDef* ch_state = + &timer->instance->hal_tim->ChannelState[TimerDomain::get_channel_state_idx(pin_falling.channel)]; + volatile HAL_TIM_ChannelStateTypeDef* n_ch_state = + &timer->instance->hal_tim->ChannelNState[TimerDomain::get_channel_state_idx(pin_falling.channel)]; + if ((*ch_state != HAL_TIM_CHANNEL_STATE_READY) || (*n_ch_state != HAL_TIM_CHANNEL_STATE_READY)) { + ErrorHandler("Channels not ready"); + } + + *ch_state = HAL_TIM_CHANNEL_STATE_BUSY; + *n_ch_state = HAL_TIM_CHANNEL_STATE_BUSY; + + uint32_t enableCCx = TIM_CCER_CC1E + << (TimerDomain::get_channel_mul4(pin_falling.channel) & 0x1FU + ); /* 0x1FU = 31 bits max shift */ + SET_BIT(timer->instance->tim->CCER, enableCCx); + } + + if constexpr (timer->is_slave_instance) { + uint32_t tmpsmcr = timer->instance->tim->SMCR & SMCR_SMS; + if (!IS_TIM_SLAVEMODE_TRIGGER_ENABLED(tmpsmcr)) { + timer->counter_enable(); + } + } else { + timer->counter_enable(); + } + + this->is_on = true; + } + + static void turn_off(void) { + if(!this->is_on) + return; + + // HAL_TIM_IC_Stop_IT(instance.peripheral->handle, instance.channel_rising) + { + timer->template disable_capture_compare_interrupt(); + + CLEAR_BIT( + timer->instance->tim->CCER, + (uint32_t)(TIM_CCER_CC1E << (TimerDomain::get_channel_mul4(pin_rising.channel) & 0x1FU)) + ); + + volatile HAL_TIM_ChannelStateTypeDef* ch_state = + &timer->instance->hal_tim->ChannelState[TimerDomain::get_channel_state_idx(pin_falling.channel)]; + volatile HAL_TIM_ChannelStateTypeDef* n_ch_state = + &timer->instance->hal_tim->ChannelNState[TimerDomain::get_channel_state_idx(pin_falling.channel)]; + *ch_state = HAL_TIM_CHANNEL_STATE_READY; + *ch_n_state = HAL_TIM_CHANNEL_STATE_READY; + } + + // HAL_TIM_IC_Stop(instance.peripheral->handle, instance.channel_falling) + { + CLEAR_BIT( + timer->instance->tim->CCER, + (uint32_t)(TIM_CCER_CC1E << (TimerDomain::get_channel_mul4(pin_rising.channel) & 0x1FU)) + ); + + volatile HAL_TIM_ChannelStateTypeDef* ch_state = + &timer->instance->hal_tim->ChannelState[TimerDomain::get_channel_state_idx(pin_falling.channel)]; + volatile HAL_TIM_ChannelStateTypeDef* n_ch_state = + &timer->instance->hal_tim->ChannelNState[TimerDomain::get_channel_state_idx(pin_falling.channel)]; + *ch_state = HAL_TIM_CHANNEL_STATE_READY; + *ch_n_state = HAL_TIM_CHANNEL_STATE_READY; + } + + if (timer->are_all_channels_free()) { + timer->counter_disable(); + } + +#error TODO + if (HAL_TIM_IC_Stop(instance.peripheral->handle, instance.channel_falling) != HAL_OK) { + ErrorHandler( + "Unable to stop the %s Input Capture measurement", + instance.peripheral->name.c_str() + ); + } + + + this->is_on = false; + } + + static uint32_t read_frequency(void) { + return this->frequency; + } + + static uint8_t read_duty_cycle(void) { + return this->duty_cycle; + } +}; + +}; + +#endif // HAL_TIM_MODULE_ENABLED diff --git a/Inc/HALAL/Services/PWM/DualPWM.hpp b/Inc/HALAL/Services/PWM/DualPWM.hpp index 26c36d073..84f99b029 100644 --- a/Inc/HALAL/Services/PWM/DualPWM.hpp +++ b/Inc/HALAL/Services/PWM/DualPWM.hpp @@ -19,51 +19,6 @@ template < const ST_LIB::TimerPin pin, const ST_LIB::TimerPin negated_pin> class DualPWM { - static consteval uint8_t get_channel_state_idx(const ST_LIB::TimerChannel ch) { - switch (ch) { - case TimerChannel::CHANNEL_1: - case TimerChannel::CHANNEL_1_NEGATED: - case TimerChannel::CHANNEL_2: - case TimerChannel::CHANNEL_2_NEGATED: - case TimerChannel::CHANNEL_3: - case TimerChannel::CHANNEL_3_NEGATED: - case TimerChannel::CHANNEL_4: - case TimerChannel::CHANNEL_5: - case TimerChannel::CHANNEL_6: - return (static_cast(ch) & - ~static_cast(TimerChannel::CHANNEL_NEGATED_FLAG)) - - 1; - - default: - ST_LIB::compile_error("unreachable"); - return 0; - } - } - - static consteval uint8_t get_channel_mul4(const ST_LIB::TimerChannel ch) { - switch (ch) { - case TimerChannel::CHANNEL_1: - case TimerChannel::CHANNEL_1_NEGATED: - return 0x00; - case TimerChannel::CHANNEL_2: - case TimerChannel::CHANNEL_2_NEGATED: - return 0x04; - case TimerChannel::CHANNEL_3: - case TimerChannel::CHANNEL_3_NEGATED: - return 0x08; - case TimerChannel::CHANNEL_4: - return 0x0C; - case TimerChannel::CHANNEL_5: - return 0x10; - case TimerChannel::CHANNEL_6: - return 0x14; - - default: - ST_LIB::compile_error("unreachable"); - return 0; - } - } - TimerWrapper* timer; uint32_t* frequency; float* duty_cycle = nullptr; @@ -112,18 +67,15 @@ class DualPWM { if (this->is_on_positive) return; - // if(HAL_TIM_PWM_Start(timer->instance->hal_tim, channel) != HAL_OK) { ErrorHandler("", 0); - // } volatile HAL_TIM_ChannelStateTypeDef* state = - &timer->instance->hal_tim->ChannelState[get_channel_state_idx(pin.channel)]; + &timer->instance->hal_tim->ChannelState[TimerDomain::get_channel_state_idx(pin.channel)]; if (*state != HAL_TIM_CHANNEL_STATE_READY) { ErrorHandler("Channel not ready"); } *state = HAL_TIM_CHANNEL_STATE_BUSY; - // enable CCx uint32_t enableCCx = TIM_CCER_CC1E - << (get_channel_mul4(pin.channel) & 0x1FU + << (TimerDomain::get_channel_mul4(pin.channel) & 0x1FU ); /* 0x1FU = 31 bits max shift */ SET_BIT(timer->instance->tim->CCER, enableCCx); @@ -148,18 +100,15 @@ class DualPWM { if (this->is_on_negative) return; - // if(HAL_TIM_PWM_Start(timer->instance->hal_tim, channel) != HAL_OK) { ErrorHandler("", 0); - // } volatile HAL_TIM_ChannelStateTypeDef* state = - &timer->instance->hal_tim->ChannelNState[get_channel_state_idx(negated_pin.channel)]; + &timer->instance->hal_tim->ChannelNState[TimerDomain::get_channel_state_idx(negated_pin.channel)]; if (*state != HAL_TIM_CHANNEL_STATE_READY) { ErrorHandler("Channel not ready"); } *state = HAL_TIM_CHANNEL_STATE_BUSY; - // enable CCNx uint32_t enableCCNx = TIM_CCER_CC1NE - << (get_channel_mul4(negated_pin.channel) & 0x1FU + << (TimerDomain::get_channel_mul4(negated_pin.channel) & 0x1FU ); /* 0x1FU = 31 bits max shift */ SET_BIT(timer->instance->tim->CCER, enableCCNx); @@ -186,11 +135,11 @@ class DualPWM { CLEAR_BIT( timer->tim->CCER, - (uint32_t)(TIM_CCER_CC1E << (get_channel_mul4(pin.channel) & 0x1FU)) + (uint32_t)(TIM_CCER_CC1E << (TimerDomain::get_channel_mul4(pin.channel) & 0x1FU)) ); HAL_TIM_ChannelStateTypeDef* state = - &timer->instance->hal_tim.ChannelState[get_channel_state_idx(pin.channel)]; + &timer->instance->hal_tim.ChannelState[TimerDomain::get_channel_state_idx(pin.channel)]; *state = HAL_TIM_CHANNEL_STATE_READY; if (timer->are_all_channels_free()) { @@ -210,11 +159,11 @@ class DualPWM { CLEAR_BIT( timer->tim->CCER, - (uint32_t)(TIM_CCER_CC1NE << (get_channel_mul4(negated_pin.channel) & 0x1FU)) + (uint32_t)(TIM_CCER_CC1NE << (TimerDomain::get_channel_mul4(negated_pin.channel) & 0x1FU)) ); HAL_TIM_ChannelStateTypeDef* state = - &timer->instance->hal_tim.ChannelState[get_channel_state_idx(negated_pin.channel)]; + &timer->instance->hal_tim.ChannelState[TimerDomain::get_channel_state_idx(negated_pin.channel)]; *state = HAL_TIM_CHANNEL_STATE_READY; if (timer->are_all_channels_free()) { diff --git a/Inc/HALAL/Services/PWM/PWM.hpp b/Inc/HALAL/Services/PWM/PWM.hpp index b1d4cdb3a..0c60f464e 100644 --- a/Inc/HALAL/Services/PWM/PWM.hpp +++ b/Inc/HALAL/Services/PWM/PWM.hpp @@ -14,52 +14,10 @@ namespace ST_LIB { template struct TimerWrapper; -template class PWM { - static consteval uint8_t get_channel_state_idx(const ST_LIB::TimerChannel ch) { - switch (ch) { - case TimerChannel::CHANNEL_1: - case TimerChannel::CHANNEL_1_NEGATED: - case TimerChannel::CHANNEL_2: - case TimerChannel::CHANNEL_2_NEGATED: - case TimerChannel::CHANNEL_3: - case TimerChannel::CHANNEL_3_NEGATED: - case TimerChannel::CHANNEL_4: - case TimerChannel::CHANNEL_5: - case TimerChannel::CHANNEL_6: - return (static_cast(ch) & - ~static_cast(TimerChannel::CHANNEL_NEGATED_FLAG)) - - 1; - - default: - ST_LIB::compile_error("unreachable"); - return 0; - } - } - - static consteval uint8_t get_channel_mul4(const ST_LIB::TimerChannel ch) { - switch (ch) { - case TimerChannel::CHANNEL_1: - case TimerChannel::CHANNEL_1_NEGATED: - return 0x00; - case TimerChannel::CHANNEL_2: - case TimerChannel::CHANNEL_2_NEGATED: - return 0x04; - case TimerChannel::CHANNEL_3: - case TimerChannel::CHANNEL_3_NEGATED: - return 0x08; - case TimerChannel::CHANNEL_4: - return 0x0C; - case TimerChannel::CHANNEL_5: - return 0x10; - case TimerChannel::CHANNEL_6: - return 0x14; - - default: - ST_LIB::compile_error("unreachable"); - return 0; - } - } - +template < + const TimerDomain::Timer& dev, + const ST_LIB::TimerPin pin> +class PWM { TimerWrapper* timer; uint32_t* frequency; float* duty_cycle = nullptr; @@ -94,18 +52,15 @@ template class PWM { if (this->is_on) return; - // if(HAL_TIM_PWM_Start(timer->instance->hal_tim, channel) != HAL_OK) { ErrorHandler("", 0); - // } volatile HAL_TIM_ChannelStateTypeDef* state = - &timer->instance->hal_tim->ChannelState[get_channel_state_idx(pin.channel)]; + &timer->instance->hal_tim->ChannelState[TimerDomain::get_channel_state_idx(pin.channel)]; if (*state != HAL_TIM_CHANNEL_STATE_READY) { ErrorHandler("Channel not ready"); } *state = HAL_TIM_CHANNEL_STATE_BUSY; - // enable CCx uint32_t enableCCx = TIM_CCER_CC1E - << (get_channel_mul4(pin.channel) & 0x1FU + << (TimerDomain::get_channel_mul4(pin.channel) & 0x1FU ); /* 0x1FU = 31 bits max shift */ SET_BIT(timer->instance->tim->CCER, enableCCx); @@ -132,11 +87,11 @@ template class PWM { CLEAR_BIT( timer->instance->tim->CCER, - (uint32_t)(TIM_CCER_CC1E << (get_channel_mul4(pin.channel) & 0x1FU)) + (uint32_t)(TIM_CCER_CC1E << (TimerDomain::get_channel_mul4(pin.channel) & 0x1FU)) ); volatile HAL_TIM_ChannelStateTypeDef* state = - &timer->instance->hal_tim->ChannelState[get_channel_state_idx(pin.channel)]; + &timer->instance->hal_tim->ChannelState[TimerDomain::get_channel_state_idx(pin.channel)]; *state = HAL_TIM_CHANNEL_STATE_READY; if (timer->are_all_channels_free()) { diff --git a/Inc/HALAL/Services/Time/TimerWrapper.hpp b/Inc/HALAL/Services/Time/TimerWrapper.hpp index 0a81056a9..4ee79c194 100644 --- a/Inc/HALAL/Services/Time/TimerWrapper.hpp +++ b/Inc/HALAL/Services/Time/TimerWrapper.hpp @@ -59,6 +59,18 @@ template struct TimerWrapper { dev.e.request == TimerRequest::GeneralPurpose_15 || dev.e.request == TimerRequest::GeneralPurpose_16 || dev.e.request == TimerRequest::GeneralPurpose_17); + /* at least 2 capture/compare channels (see IS_TIM_CC2_INSTANCE for macro definition) */ + static constexpr bool is_CC2_instance = + (dev.e.request == TimerRequest::Advanced_1 || + dev.e.request == TimerRequest::GeneralPurpose32bit_2 || + dev.e.request == TimerRequest::GeneralPurpose_3 || + dev.e.request == TimerRequest::GeneralPurpose_4 || + dev.e.request == TimerRequest::GeneralPurpose32bit_5 || + dev.e.request == TimerRequest::Advanced_8 || + dev.e.request == TimerRequest::SlaveTimer_12 || + dev.e.request == TimerRequest::GeneralPurpose_15 || + dev.e.request == TimerRequest::GeneralPurpose32bit_23 || + dev.e.request == TimerRequest::GeneralPurpose32bit_24); static constexpr bool is_slave_instance = (dev.e.request == TimerRequest::Advanced_1 || dev.e.request == TimerRequest::GeneralPurpose32bit_2 || @@ -368,8 +380,34 @@ template struct TimerWrapper { } } - /////////////////////////////////////////// - // Below are methods used by other objects + /////////////////////////////////////////////////////// + // Below are methods used by other objects (internals) + + template + inline void enable_capture_compare_interrupt(void) { + if constexpr((ch == TimerChannel::CHANNEL_1) || (ch == TimerChannel::CHANNEL_1_NEGATED)) { + SET_BIT(instance->tim->DIER, TIM_IT_CC1); + } else if constexpr((ch == TimerChannel::CHANNEL_2) || (ch == TimerChannel::CHANNEL_2_NEGATED)) { + SET_BIT(instance->tim->DIER, TIM_IT_CC2); + } else if constexpr((ch == TimerChannel::CHANNEL_3) || (ch == TimerChannel::CHANNEL_3_NEGATED)) { + SET_BIT(instance->tim->DIER, TIM_IT_CC3); + } else if constexpr((ch == TimerChannel::CHANNEL_4) || (ch == TimerChannel::CHANNEL_4_NEGATED)) { + SET_BIT(instance->tim->DIER, TIM_IT_CC4); + } + } + + template + inline void disable_capture_compare_interrupt(void) { + if constexpr((ch == TimerChannel::CHANNEL_1) || (ch == TimerChannel::CHANNEL_1_NEGATED)) { + CLEAR_BIT(instance->tim->DIER, TIM_IT_CC1); + } else if constexpr((ch == TimerChannel::CHANNEL_2) || (ch == TimerChannel::CHANNEL_2_NEGATED)) { + CLEAR_BIT(instance->tim->DIER, TIM_IT_CC2); + } else if constexpr((ch == TimerChannel::CHANNEL_3) || (ch == TimerChannel::CHANNEL_3_NEGATED)) { + CLEAR_BIT(instance->tim->DIER, TIM_IT_CC3); + } else if constexpr((ch == TimerChannel::CHANNEL_4) || (ch == TimerChannel::CHANNEL_4_NEGATED)) { + CLEAR_BIT(instance->tim->DIER, TIM_IT_CC4); + } + } template void set_pwm_frequency(uint32_t frequency) { @@ -434,6 +472,8 @@ template struct TimerWrapper { } } + /* NOTE(vic): Both config_output_compare_channel and config_input_compare_channel + * Could probably be done better if not using TIM_[OC/IC]_InitTypeDef structures */ template inline void config_output_compare_channel(const TIM_OC_InitTypeDef* OC_Config) { if constexpr (!((ch == TimerChannel::CHANNEL_1) || (ch == TimerChannel::CHANNEL_2) || @@ -587,6 +627,107 @@ template struct TimerWrapper { instance->tim->CCER = tmpccer; } + /* NOTE(vic): Both config_output_compare_channel and config_input_compare_channel + * Could probably be done better if not using TIM_[OC/IC]_InitTypeDef structures */ + template inline void config_input_compare_channel(TIM_IC_InitTypeDef* sConfig) { + if constexpr (!((ch == TimerChannel::CHANNEL_1) || (ch == TimerChannel::CHANNEL_2) || + (ch == TimerChannel::CHANNEL_3) || (ch == TimerChannel::CHANNEL_4))) { + ST_LIB::compile_error("Only channels 1 to 4 can be configured as input compare"); + return; + } + + uint32_t tmpccmrx; + uint32_t tmpccer; + + tmpccer = instance->tim->CCER; + if constexpr (ch == TimerChannel::CHANNEL_1 || ch == TimerChannel::CHANNEL_2) { + tmpccmrx = instance->tim->CCMR1; + } else if constexpr (ch == TimerChannel::CHANNEL_3 || ch == TimerChannel::CHANNEL_4) { + tmpccmrx = instance->tim->CCMR2; + } + + if constexpr((ch == TimerChannel::CHANNEL_1) || (ch == TimerChannel::CHANNEL_1_NEGATED)) { + CLEAR_BIT(tmpccer, TIM_CCER_CC1E); + + /* Select the input */ + if constexpr(this->is_CC2_instance) { + CLEAR_BIT(tmpccmrx, TIM_CCMR1_CC1S); + SET_BIT(tmpccmrx, sConfig->ICSelection); + } else { + SET_BIT(tmpccmrx, TIM_CCMR1_CC1S_0); + } + + /* Set the filter */ + CLEAR_BIT(tmpccmrx, TIM_CCMR1_IC1F); + SET_BIT(tmpccmrx, ((sConfig->ICFilter << 4U) & TIMM_CCMR1_IC1F)); + + /* Select the Polarity and set the CC1E Bit */ + CLEAR_BIT(tmpccer, TIM_CCER_CC1P | TIM_CCER_CC1NP); + SET_BIT(tmpccer, sConfig->ICPolarity & (TIM_CCER_CC1P | TIM_CCER_CC1NP)); + + CLEAR_BIT(tmpccmrx, TIM_CCMR1_IC1PSC); + SET_BIT(tmpccmrx, sConfig->ICPrescaler & TIM_CCMR1_IC1PSC); + } else if constexpr((ch == TimerChannel::CHANNEL_2) || (ch == TimerChannel::CHANNEL_2_NEGATED)) { + CLEAR_BIT(tmpccer, TIM_CCER_CC2E); + + /* Select the input */ + CLEAR_BIT(tmpccmrx, TIM_CCMR1_CC2S); + SET_BIT(tmpccmrx, sConfig->ICSelection << 8U); + + /* Set the filter */ + CLEAR_BIT(tmpccmrx, TIM_CCMR1_IC2F); + SET_BIT(tmpccmrx, (sConfig->ICFilter << 12U) & TIM_CCMR1_IC2F); + + /* Select the Polarity and set the CC2E Bit */ + CLEAR_BIT(tmpccer, TIM_CCER_CC2P | TIM_CCER_CC2NP); + SET_BIT(tmpccer, (sConfig->ICPolarity >> 4U) & (TIM_CCER_CC2P | TIM_CCER_CC2NP)); + + CLEAR_BIT(tmpccmrx, TIM_CCMR1_IC2PSC); + SET_BIT(tmpccmrx, sConfig->ICPrescaler << 8U); + } else if constexpr((ch == TimerChannel::CHANNEL_3) || (ch == TimerChannel::CHANNEL_3_NEGATED)) { + CLEAR_BIT(tmpccer, TIM_CCER_CC3E); + + /* Select the Input */ + CLEAR_BIT(tmpccmrx, TIM_CCMR2_CC3S); + SET_BIT(tmpccmrx, sConfig->ICSelection); + + /* Set the filter */ + CLEAR_BIT(tmpccmrx, TIM_CCMR2_IC3F); + SET_BIT(tmpccmrx, (sConfig->ICFilter << 4U) & TIM_CCMR2_IC3F); + + /* Select the polarity and set the CC3E Bit */ + CLEAR_BIT(tmpccer, TIM_CCER_CC3P | TIM_CCER_CC3NP); + SET_BIT(tmpccer, (sConfig->ICPolarity << 8U) & (TIM_CCER_CC3P | TIM_CCER_CC3NP)); + + CLEAR_BIT(tmpccmrx, TIM_CCMR2_IC3PSC); + SET_BIT(tmpccmrx, sConfig->ICPrescaler); + } else if constexpr(ch == TimerChannel::CHANNEL_4) { + CLEAR_BIT(tmpccer, TIM_CCER_CC4E); + + /* Select the Input */ + CLEAR_BIT(tmpccmrx, TIM_CCMR2_CC4S); + SET_BIT(tmpccmrx, sConfig->ICSelection << 8U); + + /* Set the filter */ + CLEAR_BIT(tmpccmrx, TIM_CCMR2_IC4F); + SET_BIT(tmpccmrx, (sConfig->ICFilter << 12U) & TIM_CCMR2_IC4F); + + /* Select the polarity and set the CC4E Bit */ + CLEAR_BIT(tmpccer, TIM_CCER_CC4P | TIM_CCER_CC4NP); + SET_BIT(tmpccer, (sConfig->ICPolarity << 12U) & (TIM_CCER_CC4P | TIM_CCER_CC4NP)); + + CLEAR_BIT(tmpccmrx, TIM_CCMR2_IC4PSC); + SET_BIT(tmpccmrx, sConfig->ICPrescaler << 8U); + } + + if constexpr (ch == TimerChannel::CHANNEL_1 || ch == TimerChannel::CHANNEL_2) { + instance->tim->CCMR1 = tmpccmrx; + } else if constexpr (ch == TimerChannel::CHANNEL_3 || ch == TimerChannel::CHANNEL_4) { + instance->tim->CCMR2 = tmpccmrx; + } + instance->tim->CCER = tmpccer; + } + template inline void set_output_compare_preload_enable() { if constexpr ((ch == TimerChannel::CHANNEL_1) || (ch == TimerChannel::CHANNEL_1_NEGATED)) { SET_BIT(instance->tim->CCMR1, TIM_CCMR1_OC1PE); From 4ee5b316bf1f750f4a096c7ec26d2e31645c431d Mon Sep 17 00:00:00 2001 From: victhor Date: Mon, 23 Feb 2026 15:52:43 +0100 Subject: [PATCH 02/44] feat: Capture compare interrupt for timers diff --git a/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp b/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp index 658a2004..62d35fe0 100644 --- a/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp +++ b/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp @@ -42,9 +42,22 @@ X(16, APB2ENR) \ X(17, APB2ENR) -#define X(n, b) extern TIM_HandleTypeDef htim##n; -TimerXList -#undef X +extern TIM_HandleTypeDef htim1; +extern TIM_HandleTypeDef htim2; +extern TIM_HandleTypeDef htim3; +extern TIM_HandleTypeDef htim4; +extern TIM_HandleTypeDef htim5; +extern TIM_HandleTypeDef htim6; +extern TIM_HandleTypeDef htim7; +extern TIM_HandleTypeDef htim8; +extern TIM_HandleTypeDef htim12; +extern TIM_HandleTypeDef htim13; +extern TIM_HandleTypeDef htim14; +extern TIM_HandleTypeDef htim15; +extern TIM_HandleTypeDef htim16; +extern TIM_HandleTypeDef htim17; +extern TIM_HandleTypeDef htim23; +extern TIM_HandleTypeDef htim24; #if !defined(glue) #define glue_(a, b) a##b @@ -214,8 +227,21 @@ TimerXList static constexpr std::array timer_idxmap = create_timer_idxmap(); struct TimerDomain { - // There are 16 timers static constexpr std::size_t max_instances = 16; + static constexpr std::size_t input_capture_channels = 4; + + struct InputCaptureInfo { + uint8_t channel_rising; + uint8_t channel_falling; + + float value_rising; + + float duty_cycle; + uint32_t frequency; + }; + /* 2x as big as necessary but this makes indexing easier */ + static InputCaptureInfo* input_capture_info[max_instances][input_capture_channels]; + static InputCaptureInfo input_capture_info_backing[max_instances][input_capture_channels]; struct Entry { std::array name; /* max length = 7 */ @@ -748,8 +774,47 @@ TimerXList return 0; } } + + static inline uint32_t get_timer_frequency(TIM_TypeDef* tim) { + uint32_t result; + switch(tim) { + case TIM2: + case TIM3: + case TIM4: + case TIM5: + case TIM6: + case TIM7: + case TIM12: + case TIM13: + case TIM14: + result = HAL_RCC_GetPCLK1Freq(); + if ((RCC->D2CFGR & RCC_D2CFGR_D2PPRE1) != RCC_HCLK_DIV1) { + result *= 2; + } + break; + + case TIM1: + case TIM8: + case TIM15: + case TIM16: + case TIM17: + case TIM23: + case TIM24: + result = HAL_RCC_GetPCLK2Freq(); + if ((RCC->D2CFGR & RCC_D2CFGR_D2PPRE2) != RCC_HCLK_DIV1) { + result *= 2; + } + break; + default: + ErrorHandler("Invalid timer ptr"); + break; + } + return result; + } }; + + consteval GPIODomain::AlternateFunction TimerDomain::Timer::get_gpio_af( ST_LIB::TimerRequest req, ST_LIB::TimerPin pin diff --git a/Inc/HALAL/Services/InputCapture/InputCapture.hpp b/Inc/HALAL/Services/InputCapture/InputCapture.hpp index 0100b08e..32f4cf15 100644 --- a/Inc/HALAL/Services/InputCapture/InputCapture.hpp +++ b/Inc/HALAL/Services/InputCapture/InputCapture.hpp @@ -14,18 +14,33 @@ namespace ST_LIB { template struct TimerWrapper; +/* NOTE: STM32h7 supports using 2 different pins for rising/falling + * To do this, you keep TIM_ICSELECTION_DIRECTTI when configuring both + * Not an option given in this interface but easily extended if necessary + */ template < const ST_LIB::TimerDomain::Timer& dev, const ST_LIB::TimerPin pin_rising, - const ST_LIB::TimerPin pin_falling> + const ST_LIB::TimerChannel channel_falling> class InputCapture { TimerWrapper* timer; - uint32_t frequency; - uint8_t duty_cycle; + TimerDomain::InputCaptureInfo *info = nullptr; bool is_on; public: InputCapture(TimerWrapper* tim) : timer(tim) { + // Setup TimerDomain + this->info = + &TimerDomain::input_capture_info_backing[tim->instance->timer_idx][pin_rising.channel]; + TimerDomain::input_capture_info[tim->instance->timer_idx][pin_rising.channel] = info; + TimerDomain::input_capture_info[tim->instance->timer_idx][channel_falling] = info; + + this->info->channel_rising = static_cast(pin_rising.channel) - 1; + this->info->channel_falling = static_cast(channel_falling) - 1; + this->info->value_rising = 0.0f; + this->info->duty_cycle = 0.0f; + this->info->frequency = 0; + TIM_IC_InitTypeDef sConfigIC = { .ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING, .ICPrescaler = TIM_ICPSC_DIV1, @@ -36,7 +51,7 @@ public: sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_FALLING; sConfigIC.ICSelection = TIM_ICSELECTION_INDIRECTTI; - timer->template config_input_compare_channel(&sConfigIC); + timer->template config_input_compare_channel(&sConfigIC); } static void turn_on(void) { @@ -66,9 +81,9 @@ public: // HAL_TIM_IC_Start(instance.peripheral->handle, instance.channel_falling) { volatile HAL_TIM_ChannelStateTypeDef* ch_state = - &timer->instance->hal_tim->ChannelState[TimerDomain::get_channel_state_idx(pin_falling.channel)]; + &timer->instance->hal_tim->ChannelState[TimerDomain::get_channel_state_idx(channel_falling)]; volatile HAL_TIM_ChannelStateTypeDef* n_ch_state = - &timer->instance->hal_tim->ChannelNState[TimerDomain::get_channel_state_idx(pin_falling.channel)]; + &timer->instance->hal_tim->ChannelNState[TimerDomain::get_channel_state_idx(channel_falling)]; if ((*ch_state != HAL_TIM_CHANNEL_STATE_READY) || (*n_ch_state != HAL_TIM_CHANNEL_STATE_READY)) { ErrorHandler("Channels not ready"); } @@ -77,7 +92,7 @@ public: *n_ch_state = HAL_TIM_CHANNEL_STATE_BUSY; uint32_t enableCCx = TIM_CCER_CC1E - << (TimerDomain::get_channel_mul4(pin_falling.channel) & 0x1FU + << (TimerDomain::get_channel_mul4(channel_falling) & 0x1FU ); /* 0x1FU = 31 bits max shift */ SET_BIT(timer->instance->tim->CCER, enableCCx); } @@ -108,9 +123,9 @@ public: ); volatile HAL_TIM_ChannelStateTypeDef* ch_state = - &timer->instance->hal_tim->ChannelState[TimerDomain::get_channel_state_idx(pin_falling.channel)]; + &timer->instance->hal_tim->ChannelState[TimerDomain::get_channel_state_idx(channel_falling)]; volatile HAL_TIM_ChannelStateTypeDef* n_ch_state = - &timer->instance->hal_tim->ChannelNState[TimerDomain::get_channel_state_idx(pin_falling.channel)]; + &timer->instance->hal_tim->ChannelNState[TimerDomain::get_channel_state_idx(channel_falling)]; *ch_state = HAL_TIM_CHANNEL_STATE_READY; *ch_n_state = HAL_TIM_CHANNEL_STATE_READY; } @@ -123,9 +138,9 @@ public: ); volatile HAL_TIM_ChannelStateTypeDef* ch_state = - &timer->instance->hal_tim->ChannelState[TimerDomain::get_channel_state_idx(pin_falling.channel)]; + &timer->instance->hal_tim->ChannelState[TimerDomain::get_channel_state_idx(channel_falling)]; volatile HAL_TIM_ChannelStateTypeDef* n_ch_state = - &timer->instance->hal_tim->ChannelNState[TimerDomain::get_channel_state_idx(pin_falling.channel)]; + &timer->instance->hal_tim->ChannelNState[TimerDomain::get_channel_state_idx(channel_falling)]; *ch_state = HAL_TIM_CHANNEL_STATE_READY; *ch_n_state = HAL_TIM_CHANNEL_STATE_READY; } @@ -146,12 +161,12 @@ public: this->is_on = false; } - static uint32_t read_frequency(void) { - return this->frequency; + static uint32_t get_frequency(void) { + return this->info->frequency; } - static uint8_t read_duty_cycle(void) { - return this->duty_cycle; + static float get_duty_cycle(void) { + return this->info->duty_cycle; } }; diff --git a/Src/HALAL/Models/TimerDomain/TimerDomain.cpp b/Src/HALAL/Models/TimerDomain/TimerDomain.cpp index cdafeeef..bbf65990 100644 --- a/Src/HALAL/Models/TimerDomain/TimerDomain.cpp +++ b/Src/HALAL/Models/TimerDomain/TimerDomain.cpp @@ -2,105 +2,188 @@ using namespace ST_LIB; -#define X(n, b) TIM_HandleTypeDef htim##n; -TimerXList -#undef X - - void (*TimerDomain::callbacks[TimerDomain::max_instances])(void*) = {nullptr}; +#define CaptureCompareInterruptMask \ + (TIM_DIER_CC1IE | TIM_DIER_CC2IE | TIM_DIER_CC3IE | TIM_DIER_CC4IE) + +TIM_HandleTypeDef htim1; +TIM_HandleTypeDef htim2; +TIM_HandleTypeDef htim3; +TIM_HandleTypeDef htim4; +TIM_HandleTypeDef htim5; +TIM_HandleTypeDef htim6; +TIM_HandleTypeDef htim7; +TIM_HandleTypeDef htim8; +TIM_HandleTypeDef htim12; +TIM_HandleTypeDef htim13; +TIM_HandleTypeDef htim14; +TIM_HandleTypeDef htim15; +TIM_HandleTypeDef htim16; +TIM_HandleTypeDef htim17; +TIM_HandleTypeDef htim23; +TIM_HandleTypeDef htim24; + +void (*TimerDomain::callbacks[TimerDomain::max_instances])(void*) = {nullptr}; void* TimerDomain::callback_data[TimerDomain::max_instances] = {nullptr}; +TimerDomain::InputCaptureInfo input_capture_info_backing[TimerDomain::max_instances][TimerDomain::input_capture_channels/2] = {}; +TimerDomain::InputCaptureInfo* input_capture_info[TimerDomain::max_instances][TimerDomain::input_capture_channels] = {nullptr}; +#error TODO: In InputCapture.hpp associate the input_capture_info to input_capture_info_backing + +static void TIM_IC_CaptureCallback(const uint32_t timer_idx, uint32_t channel) +{ + TIM_HandleTypeDef* htim = TimerDomain::hal_handles[timer_idx]; + htim->Instance->CNT = 0; + + InputCaptureInfo* info = input_capture_info[timer_idx][channel]; + if(info->channel_rising == channel) { + // NOTE: CCR1 - CCR4 are contiguous + info->value_rising = (float)(*(((uint32_t*)&htim->Instance->CCR1) + channel)); + + uint32_t ref_clock = TimerDomain::get_timer_frequency(htim->Instance) / (htim->Instance->PSC + 1); + info->frequency = (uint32_t)((ref_clock / info->value_rising) + 0.5f); + } else if(info->channel_falling == channel) { + uint32_t falling_value = *(((uint32_t*)&htim->Instance->CCR1) + channel); + + info->duty_cycle = ((float)falling_value * 100.0f) / info->value_rising; + } else [[unlikely]] { + ErrorHandler("TimerDomain::input_capture_info was modified"); + } +} + +static void TIM_InterruptCallback(const uint32_t timer_idx) +{ + TIM_TypeDef* tim = TimerDomain::cmsis_timers[timer_idx]; + uint32_t cc_channel = (tim->SR & CaptureCompareInterruptMask) - 1; + if(tim->SR & TIM_SR_UIF) { + CLEAR_BIT(tim->SR, TIM_SR_UIF); + TimerDomain::callbacks[timer_idx](TimerDomain::callback_data[timer_idx]); + } + + if(cc_channel != 0) { + TIM_IC_CaptureCallback(timer_idx, cc_channel); + } +} extern "C" void TIM1_UP_IRQHandler(void) { - CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[1]]->SR, TIM_SR_UIF); - TimerDomain::callbacks[timer_idxmap[1]](TimerDomain::callback_data[timer_idxmap[1]]); + TIM_InterruptCallback(timer_idxmap[1]); } extern "C" void TIM2_IRQHandler(void) { - CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[2]]->SR, TIM_SR_UIF); - TimerDomain::callbacks[timer_idxmap[2]](TimerDomain::callback_data[timer_idxmap[2]]); + TIM_InterruptCallback(timer_idxmap[2]); } extern "C" void TIM3_IRQHandler(void) { - CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[3]]->SR, TIM_SR_UIF); - TimerDomain::callbacks[timer_idxmap[3]](TimerDomain::callback_data[timer_idxmap[3]]); + TIM_InterruptCallback(timer_idxmap[3]); } extern "C" void TIM4_IRQHandler(void) { - CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[4]]->SR, TIM_SR_UIF); - TimerDomain::callbacks[timer_idxmap[4]](TimerDomain::callback_data[timer_idxmap[4]]); + TIM_InterruptCallback(timer_idxmap[4]); } extern "C" void TIM5_IRQHandler(void) { - CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[5]]->SR, TIM_SR_UIF); - TimerDomain::callbacks[timer_idxmap[5]](TimerDomain::callback_data[timer_idxmap[5]]); + TIM_InterruptCallback(timer_idxmap[5]); } extern "C" void TIM6_DAC_IRQHandler(void) { + // NOTE: Basic timers have no capture compare channels CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[6]]->SR, TIM_SR_UIF); TimerDomain::callbacks[timer_idxmap[6]](TimerDomain::callback_data[timer_idxmap[6]]); } extern "C" void TIM7_IRQHandler(void) { + // NOTE: Basic timers have no capture compare channels CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[7]]->SR, TIM_SR_UIF); TimerDomain::callbacks[timer_idxmap[7]](TimerDomain::callback_data[timer_idxmap[7]]); } extern "C" void TIM8_BRK_TIM12_IRQHandler(void) { - if ((TimerDomain::cmsis_timers[timer_idxmap[12]]->SR & TIM_SR_UIF) != 0) { - CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[12]]->SR, TIM_SR_UIF); - TimerDomain::callbacks[timer_idxmap[12]](TimerDomain::callback_data[timer_idxmap[12]]); + constexpr uint32_t tim8_idx = timer_idxmap[8]; + constexpr uint32_t tim12_idx = timer_idxmap[12]; + + TIM_TypeDef* tim8 = TimerDomain::cmsis_timers[tim8_idx]; + TIM_TypeDef* tim12 = TimerDomain::cmsis_timers[tim12_idx]; + + uint32_t tim12_cc_channel = (tim12->SR & CaptureCompareInterruptMask) - 1; + if ((tim12->SR & TIM_SR_UIF) != 0) { + CLEAR_BIT(TimerDomain::cmsis_timers[tim12_idx]->SR, TIM_SR_UIF); + TimerDomain::callbacks[tim12_idx](TimerDomain::callback_data[tim12_idx]); + } + if (tim12_cc_channel != 0) { + TIM_IC_CaptureCallback(tim12_idx, tim12_cc_channel); } - if ((TimerDomain::cmsis_timers[timer_idxmap[8]]->SR & TIM_SR_BIF) != 0) { - CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[8]]->SR, TIM_SR_BIF); + + if ((TimerDomain::cmsis_timers[tim8_idx]->SR & TIM_SR_BIF) != 0) { + CLEAR_BIT(TimerDomain::cmsis_timers[tim8_idx]->SR, TIM_SR_BIF); /* this could probably have some other callback */ - TimerDomain::callbacks[timer_idxmap[8]](TimerDomain::callback_data[timer_idxmap[8]]); + TimerDomain::callbacks[tim8_idx](TimerDomain::callback_data[tim8_idx]); } } extern "C" void TIM8_UP_TIM13_IRQHandler(void) { - if ((TimerDomain::cmsis_timers[timer_idxmap[13]]->SR & TIM_SR_UIF) != 0) { - CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[13]]->SR, TIM_SR_UIF); - TimerDomain::callbacks[timer_idxmap[13]](TimerDomain::callback_data[timer_idxmap[13]]); + constexpr uint32_t tim8_idx = timer_idxmap[8]; + constexpr uint32_t tim13_idx = timer_idxmap[13]; + + TIM_TypeDef* tim8 = TimerDomain::cmsis_timers[tim8_idx]; + TIM_TypeDef* tim13 = TimerDomain::cmsis_timers[tim13_idx]; + + uint32_t tim8_cc_channel = (tim8->SR & CaptureCompareInterruptMask) - 1; + uint32_t tim13_cc_channel = (tim13->SR & CaptureCompareInterruptMask) - 1; + if ((tim13->SR & TIM_SR_UIF) != 0) { + CLEAR_BIT(TimerDomain::cmsis_timers[tim13_idx]->SR, TIM_SR_UIF); + TimerDomain::callbacks[tim13_idx](TimerDomain::callback_data[tim13_idx]); + } + if (tim13_cc_channel != 0) { + TIM_IC_CaptureCallback(tim13_idx, tim13_cc_channel); + } + + if ((TimerDomain::cmsis_timers[tim8_idx]->SR & TIM_SR_UIF) != 0) { + CLEAR_BIT(TimerDomain::cmsis_timers[tim8_idx]->SR, TIM_SR_UIF); + TimerDomain::callbacks[tim8_idx](TimerDomain::callback_data[tim8_idx]); } - if ((TimerDomain::cmsis_timers[timer_idxmap[8]]->SR & TIM_SR_UIF) != 0) { - CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[8]]->SR, TIM_SR_UIF); - TimerDomain::callbacks[timer_idxmap[8]](TimerDomain::callback_data[timer_idxmap[8]]); + if (tim8_cc_channel != 0) { + TIM_IC_CaptureCallback(tim8_idx, tim8_cc_channel); } } extern "C" void TIM8_TRG_COM_TIM14_IRQHandler(void) {e if ne - if ((TimerDomain::cmsis_timers[timer_idxmap[14]]->SR & TIM_SR_UIF) != 0) { - CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[14]]->SR, TIM_SR_UIF); - TimerDomain::callbacks[timer_idxmap[14]](TimerDomain::callback_data[timer_idxmap[14]]); + constexpr uint32_t tim8_idx = timer_idxmap[8]; + constexpr uint32_t tim14_idx = timer_idxmap[14]; + + TIM_TypeDef* tim8 = TimerDomain::cmsis_timers[tim8_idx]; + TIM_TypeDef* tim14 = TimerDomain::cmsis_timers[tim14_idx]; + + uint32_t tim14_cc_channel = (tim14->SR & CaptureCompareInterruptMask) - 1; + if ((tim14->SR & TIM_SR_UIF) != 0) { + CLEAR_BIT(TimerDomain::cmsis_timers[tim14_idx]->SR, TIM_SR_UIF); + TimerDomain::callbacks[tim14_idx](TimerDomain::callback_data[tim14_idx]); } + if (tim14_cc_channel != 0) { + TIM_IC_CaptureCallback(tim14_idx, tim14_cc_channel); + } + constexpr uint32_t com_trg_flags = TIM_SR_TIF | TIM_SR_COMIF; - if ((TimerDomain::cmsis_timers[timer_idxmap[14]]->SR & com_trg_flags) != 0) { - CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[8]]->SR, com_trg_flags); + if ((TimerDomain::cmsis_timers[tim8_idx]->SR & com_trg_flags) != 0) { + CLEAR_BIT(TimerDomain::cmsis_timers[tim8_idx]->SR, com_trg_flags); /* this could probably have some other callback */ - TimerDomain::callbacks[timer_idxmap[8]](TimerDomain::callback_data[timer_idxmap[8]]); + TimerDomain::callbacks[tim8_idx](TimerDomain::callback_data[tim8_idx]); } } extern "C" void TIM15_IRQHandler(void) { - CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[15]]->SR, TIM_SR_UIF); - TimerDomain::callbacks[timer_idxmap[15]](TimerDomain::callback_data[timer_idxmap[15]]); + TIM_InterruptCallback(timer_idxmap[15]); } extern "C" void TIM16_IRQHandler(void) { - CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[16]]->SR, TIM_SR_UIF); - TimerDomain::callbacks[timer_idxmap[16]](TimerDomain::callback_data[timer_idxmap[16]]); + TIM_InterruptCallback(timer_idxmap[16]); } extern "C" void TIM17_IRQHandler(void) { - CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[17]]->SR, TIM_SR_UIF); - TimerDomain::callbacks[timer_idxmap[17]](TimerDomain::callback_data[timer_idxmap[17]]); + TIM_InterruptCallback(timer_idxmap[17]); } extern "C" void TIM23_IRQHandler(void) { - CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[23]]->SR, TIM_SR_UIF); - TimerDomain::callbacks[timer_idxmap[23]](TimerDomain::callback_data[timer_idxmap[23]]); + TIM_InterruptCallback(timer_idxmap[23]); } extern "C" void TIM24_IRQHandler(void) { - CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[24]]->SR, TIM_SR_UIF); - TimerDomain::callbacks[timer_idxmap[24]](TimerDomain::callback_data[timer_idxmap[24]]); + TIM_InterruptCallback(timer_idxmap[24]); } --- Inc/HALAL/Models/TimerDomain/TimerDomain.hpp | 73 +++++++- .../Services/InputCapture/InputCapture.hpp | 45 +++-- Src/HALAL/Models/TimerDomain/TimerDomain.cpp | 169 +++++++++++++----- 3 files changed, 225 insertions(+), 62 deletions(-) diff --git a/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp b/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp index 658a20041..62d35fe0e 100644 --- a/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp +++ b/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp @@ -42,9 +42,22 @@ X(16, APB2ENR) \ X(17, APB2ENR) -#define X(n, b) extern TIM_HandleTypeDef htim##n; -TimerXList -#undef X +extern TIM_HandleTypeDef htim1; +extern TIM_HandleTypeDef htim2; +extern TIM_HandleTypeDef htim3; +extern TIM_HandleTypeDef htim4; +extern TIM_HandleTypeDef htim5; +extern TIM_HandleTypeDef htim6; +extern TIM_HandleTypeDef htim7; +extern TIM_HandleTypeDef htim8; +extern TIM_HandleTypeDef htim12; +extern TIM_HandleTypeDef htim13; +extern TIM_HandleTypeDef htim14; +extern TIM_HandleTypeDef htim15; +extern TIM_HandleTypeDef htim16; +extern TIM_HandleTypeDef htim17; +extern TIM_HandleTypeDef htim23; +extern TIM_HandleTypeDef htim24; #if !defined(glue) #define glue_(a, b) a##b @@ -214,8 +227,21 @@ TimerXList static constexpr std::array timer_idxmap = create_timer_idxmap(); struct TimerDomain { - // There are 16 timers static constexpr std::size_t max_instances = 16; + static constexpr std::size_t input_capture_channels = 4; + + struct InputCaptureInfo { + uint8_t channel_rising; + uint8_t channel_falling; + + float value_rising; + + float duty_cycle; + uint32_t frequency; + }; + /* 2x as big as necessary but this makes indexing easier */ + static InputCaptureInfo* input_capture_info[max_instances][input_capture_channels]; + static InputCaptureInfo input_capture_info_backing[max_instances][input_capture_channels]; struct Entry { std::array name; /* max length = 7 */ @@ -748,8 +774,47 @@ TimerXList return 0; } } + + static inline uint32_t get_timer_frequency(TIM_TypeDef* tim) { + uint32_t result; + switch(tim) { + case TIM2: + case TIM3: + case TIM4: + case TIM5: + case TIM6: + case TIM7: + case TIM12: + case TIM13: + case TIM14: + result = HAL_RCC_GetPCLK1Freq(); + if ((RCC->D2CFGR & RCC_D2CFGR_D2PPRE1) != RCC_HCLK_DIV1) { + result *= 2; + } + break; + + case TIM1: + case TIM8: + case TIM15: + case TIM16: + case TIM17: + case TIM23: + case TIM24: + result = HAL_RCC_GetPCLK2Freq(); + if ((RCC->D2CFGR & RCC_D2CFGR_D2PPRE2) != RCC_HCLK_DIV1) { + result *= 2; + } + break; + default: + ErrorHandler("Invalid timer ptr"); + break; + } + return result; + } }; + + consteval GPIODomain::AlternateFunction TimerDomain::Timer::get_gpio_af( ST_LIB::TimerRequest req, ST_LIB::TimerPin pin diff --git a/Inc/HALAL/Services/InputCapture/InputCapture.hpp b/Inc/HALAL/Services/InputCapture/InputCapture.hpp index 0100b08eb..32f4cf15e 100644 --- a/Inc/HALAL/Services/InputCapture/InputCapture.hpp +++ b/Inc/HALAL/Services/InputCapture/InputCapture.hpp @@ -14,18 +14,33 @@ namespace ST_LIB { template struct TimerWrapper; +/* NOTE: STM32h7 supports using 2 different pins for rising/falling + * To do this, you keep TIM_ICSELECTION_DIRECTTI when configuring both + * Not an option given in this interface but easily extended if necessary + */ template < const ST_LIB::TimerDomain::Timer& dev, const ST_LIB::TimerPin pin_rising, - const ST_LIB::TimerPin pin_falling> + const ST_LIB::TimerChannel channel_falling> class InputCapture { TimerWrapper* timer; - uint32_t frequency; - uint8_t duty_cycle; + TimerDomain::InputCaptureInfo *info = nullptr; bool is_on; public: InputCapture(TimerWrapper* tim) : timer(tim) { + // Setup TimerDomain + this->info = + &TimerDomain::input_capture_info_backing[tim->instance->timer_idx][pin_rising.channel]; + TimerDomain::input_capture_info[tim->instance->timer_idx][pin_rising.channel] = info; + TimerDomain::input_capture_info[tim->instance->timer_idx][channel_falling] = info; + + this->info->channel_rising = static_cast(pin_rising.channel) - 1; + this->info->channel_falling = static_cast(channel_falling) - 1; + this->info->value_rising = 0.0f; + this->info->duty_cycle = 0.0f; + this->info->frequency = 0; + TIM_IC_InitTypeDef sConfigIC = { .ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING, .ICPrescaler = TIM_ICPSC_DIV1, @@ -36,7 +51,7 @@ class InputCapture { sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_FALLING; sConfigIC.ICSelection = TIM_ICSELECTION_INDIRECTTI; - timer->template config_input_compare_channel(&sConfigIC); + timer->template config_input_compare_channel(&sConfigIC); } static void turn_on(void) { @@ -66,9 +81,9 @@ class InputCapture { // HAL_TIM_IC_Start(instance.peripheral->handle, instance.channel_falling) { volatile HAL_TIM_ChannelStateTypeDef* ch_state = - &timer->instance->hal_tim->ChannelState[TimerDomain::get_channel_state_idx(pin_falling.channel)]; + &timer->instance->hal_tim->ChannelState[TimerDomain::get_channel_state_idx(channel_falling)]; volatile HAL_TIM_ChannelStateTypeDef* n_ch_state = - &timer->instance->hal_tim->ChannelNState[TimerDomain::get_channel_state_idx(pin_falling.channel)]; + &timer->instance->hal_tim->ChannelNState[TimerDomain::get_channel_state_idx(channel_falling)]; if ((*ch_state != HAL_TIM_CHANNEL_STATE_READY) || (*n_ch_state != HAL_TIM_CHANNEL_STATE_READY)) { ErrorHandler("Channels not ready"); } @@ -77,7 +92,7 @@ class InputCapture { *n_ch_state = HAL_TIM_CHANNEL_STATE_BUSY; uint32_t enableCCx = TIM_CCER_CC1E - << (TimerDomain::get_channel_mul4(pin_falling.channel) & 0x1FU + << (TimerDomain::get_channel_mul4(channel_falling) & 0x1FU ); /* 0x1FU = 31 bits max shift */ SET_BIT(timer->instance->tim->CCER, enableCCx); } @@ -108,9 +123,9 @@ class InputCapture { ); volatile HAL_TIM_ChannelStateTypeDef* ch_state = - &timer->instance->hal_tim->ChannelState[TimerDomain::get_channel_state_idx(pin_falling.channel)]; + &timer->instance->hal_tim->ChannelState[TimerDomain::get_channel_state_idx(channel_falling)]; volatile HAL_TIM_ChannelStateTypeDef* n_ch_state = - &timer->instance->hal_tim->ChannelNState[TimerDomain::get_channel_state_idx(pin_falling.channel)]; + &timer->instance->hal_tim->ChannelNState[TimerDomain::get_channel_state_idx(channel_falling)]; *ch_state = HAL_TIM_CHANNEL_STATE_READY; *ch_n_state = HAL_TIM_CHANNEL_STATE_READY; } @@ -123,9 +138,9 @@ class InputCapture { ); volatile HAL_TIM_ChannelStateTypeDef* ch_state = - &timer->instance->hal_tim->ChannelState[TimerDomain::get_channel_state_idx(pin_falling.channel)]; + &timer->instance->hal_tim->ChannelState[TimerDomain::get_channel_state_idx(channel_falling)]; volatile HAL_TIM_ChannelStateTypeDef* n_ch_state = - &timer->instance->hal_tim->ChannelNState[TimerDomain::get_channel_state_idx(pin_falling.channel)]; + &timer->instance->hal_tim->ChannelNState[TimerDomain::get_channel_state_idx(channel_falling)]; *ch_state = HAL_TIM_CHANNEL_STATE_READY; *ch_n_state = HAL_TIM_CHANNEL_STATE_READY; } @@ -146,12 +161,12 @@ class InputCapture { this->is_on = false; } - static uint32_t read_frequency(void) { - return this->frequency; + static uint32_t get_frequency(void) { + return this->info->frequency; } - static uint8_t read_duty_cycle(void) { - return this->duty_cycle; + static float get_duty_cycle(void) { + return this->info->duty_cycle; } }; diff --git a/Src/HALAL/Models/TimerDomain/TimerDomain.cpp b/Src/HALAL/Models/TimerDomain/TimerDomain.cpp index cdafeeef2..bbf659901 100644 --- a/Src/HALAL/Models/TimerDomain/TimerDomain.cpp +++ b/Src/HALAL/Models/TimerDomain/TimerDomain.cpp @@ -2,105 +2,188 @@ using namespace ST_LIB; -#define X(n, b) TIM_HandleTypeDef htim##n; -TimerXList -#undef X - - void (*TimerDomain::callbacks[TimerDomain::max_instances])(void*) = {nullptr}; +#define CaptureCompareInterruptMask \ + (TIM_DIER_CC1IE | TIM_DIER_CC2IE | TIM_DIER_CC3IE | TIM_DIER_CC4IE) + +TIM_HandleTypeDef htim1; +TIM_HandleTypeDef htim2; +TIM_HandleTypeDef htim3; +TIM_HandleTypeDef htim4; +TIM_HandleTypeDef htim5; +TIM_HandleTypeDef htim6; +TIM_HandleTypeDef htim7; +TIM_HandleTypeDef htim8; +TIM_HandleTypeDef htim12; +TIM_HandleTypeDef htim13; +TIM_HandleTypeDef htim14; +TIM_HandleTypeDef htim15; +TIM_HandleTypeDef htim16; +TIM_HandleTypeDef htim17; +TIM_HandleTypeDef htim23; +TIM_HandleTypeDef htim24; + +void (*TimerDomain::callbacks[TimerDomain::max_instances])(void*) = {nullptr}; void* TimerDomain::callback_data[TimerDomain::max_instances] = {nullptr}; +TimerDomain::InputCaptureInfo input_capture_info_backing[TimerDomain::max_instances][TimerDomain::input_capture_channels/2] = {}; +TimerDomain::InputCaptureInfo* input_capture_info[TimerDomain::max_instances][TimerDomain::input_capture_channels] = {nullptr}; +#error TODO: In InputCapture.hpp associate the input_capture_info to input_capture_info_backing + +static void TIM_IC_CaptureCallback(const uint32_t timer_idx, uint32_t channel) +{ + TIM_HandleTypeDef* htim = TimerDomain::hal_handles[timer_idx]; + htim->Instance->CNT = 0; + + InputCaptureInfo* info = input_capture_info[timer_idx][channel]; + if(info->channel_rising == channel) { + // NOTE: CCR1 - CCR4 are contiguous + info->value_rising = (float)(*(((uint32_t*)&htim->Instance->CCR1) + channel)); + + uint32_t ref_clock = TimerDomain::get_timer_frequency(htim->Instance) / (htim->Instance->PSC + 1); + info->frequency = (uint32_t)((ref_clock / info->value_rising) + 0.5f); + } else if(info->channel_falling == channel) { + uint32_t falling_value = *(((uint32_t*)&htim->Instance->CCR1) + channel); + + info->duty_cycle = ((float)falling_value * 100.0f) / info->value_rising; + } else [[unlikely]] { + ErrorHandler("TimerDomain::input_capture_info was modified"); + } +} + +static void TIM_InterruptCallback(const uint32_t timer_idx) +{ + TIM_TypeDef* tim = TimerDomain::cmsis_timers[timer_idx]; + uint32_t cc_channel = (tim->SR & CaptureCompareInterruptMask) - 1; + if(tim->SR & TIM_SR_UIF) { + CLEAR_BIT(tim->SR, TIM_SR_UIF); + TimerDomain::callbacks[timer_idx](TimerDomain::callback_data[timer_idx]); + } + + if(cc_channel != 0) { + TIM_IC_CaptureCallback(timer_idx, cc_channel); + } +} extern "C" void TIM1_UP_IRQHandler(void) { - CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[1]]->SR, TIM_SR_UIF); - TimerDomain::callbacks[timer_idxmap[1]](TimerDomain::callback_data[timer_idxmap[1]]); + TIM_InterruptCallback(timer_idxmap[1]); } extern "C" void TIM2_IRQHandler(void) { - CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[2]]->SR, TIM_SR_UIF); - TimerDomain::callbacks[timer_idxmap[2]](TimerDomain::callback_data[timer_idxmap[2]]); + TIM_InterruptCallback(timer_idxmap[2]); } extern "C" void TIM3_IRQHandler(void) { - CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[3]]->SR, TIM_SR_UIF); - TimerDomain::callbacks[timer_idxmap[3]](TimerDomain::callback_data[timer_idxmap[3]]); + TIM_InterruptCallback(timer_idxmap[3]); } extern "C" void TIM4_IRQHandler(void) { - CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[4]]->SR, TIM_SR_UIF); - TimerDomain::callbacks[timer_idxmap[4]](TimerDomain::callback_data[timer_idxmap[4]]); + TIM_InterruptCallback(timer_idxmap[4]); } extern "C" void TIM5_IRQHandler(void) { - CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[5]]->SR, TIM_SR_UIF); - TimerDomain::callbacks[timer_idxmap[5]](TimerDomain::callback_data[timer_idxmap[5]]); + TIM_InterruptCallback(timer_idxmap[5]); } extern "C" void TIM6_DAC_IRQHandler(void) { + // NOTE: Basic timers have no capture compare channels CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[6]]->SR, TIM_SR_UIF); TimerDomain::callbacks[timer_idxmap[6]](TimerDomain::callback_data[timer_idxmap[6]]); } extern "C" void TIM7_IRQHandler(void) { + // NOTE: Basic timers have no capture compare channels CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[7]]->SR, TIM_SR_UIF); TimerDomain::callbacks[timer_idxmap[7]](TimerDomain::callback_data[timer_idxmap[7]]); } extern "C" void TIM8_BRK_TIM12_IRQHandler(void) { - if ((TimerDomain::cmsis_timers[timer_idxmap[12]]->SR & TIM_SR_UIF) != 0) { - CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[12]]->SR, TIM_SR_UIF); - TimerDomain::callbacks[timer_idxmap[12]](TimerDomain::callback_data[timer_idxmap[12]]); + constexpr uint32_t tim8_idx = timer_idxmap[8]; + constexpr uint32_t tim12_idx = timer_idxmap[12]; + + TIM_TypeDef* tim8 = TimerDomain::cmsis_timers[tim8_idx]; + TIM_TypeDef* tim12 = TimerDomain::cmsis_timers[tim12_idx]; + + uint32_t tim12_cc_channel = (tim12->SR & CaptureCompareInterruptMask) - 1; + if ((tim12->SR & TIM_SR_UIF) != 0) { + CLEAR_BIT(TimerDomain::cmsis_timers[tim12_idx]->SR, TIM_SR_UIF); + TimerDomain::callbacks[tim12_idx](TimerDomain::callback_data[tim12_idx]); + } + if (tim12_cc_channel != 0) { + TIM_IC_CaptureCallback(tim12_idx, tim12_cc_channel); } - if ((TimerDomain::cmsis_timers[timer_idxmap[8]]->SR & TIM_SR_BIF) != 0) { - CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[8]]->SR, TIM_SR_BIF); + + if ((TimerDomain::cmsis_timers[tim8_idx]->SR & TIM_SR_BIF) != 0) { + CLEAR_BIT(TimerDomain::cmsis_timers[tim8_idx]->SR, TIM_SR_BIF); /* this could probably have some other callback */ - TimerDomain::callbacks[timer_idxmap[8]](TimerDomain::callback_data[timer_idxmap[8]]); + TimerDomain::callbacks[tim8_idx](TimerDomain::callback_data[tim8_idx]); } } extern "C" void TIM8_UP_TIM13_IRQHandler(void) { - if ((TimerDomain::cmsis_timers[timer_idxmap[13]]->SR & TIM_SR_UIF) != 0) { - CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[13]]->SR, TIM_SR_UIF); - TimerDomain::callbacks[timer_idxmap[13]](TimerDomain::callback_data[timer_idxmap[13]]); + constexpr uint32_t tim8_idx = timer_idxmap[8]; + constexpr uint32_t tim13_idx = timer_idxmap[13]; + + TIM_TypeDef* tim8 = TimerDomain::cmsis_timers[tim8_idx]; + TIM_TypeDef* tim13 = TimerDomain::cmsis_timers[tim13_idx]; + + uint32_t tim8_cc_channel = (tim8->SR & CaptureCompareInterruptMask) - 1; + uint32_t tim13_cc_channel = (tim13->SR & CaptureCompareInterruptMask) - 1; + if ((tim13->SR & TIM_SR_UIF) != 0) { + CLEAR_BIT(TimerDomain::cmsis_timers[tim13_idx]->SR, TIM_SR_UIF); + TimerDomain::callbacks[tim13_idx](TimerDomain::callback_data[tim13_idx]); + } + if (tim13_cc_channel != 0) { + TIM_IC_CaptureCallback(tim13_idx, tim13_cc_channel); + } + + if ((TimerDomain::cmsis_timers[tim8_idx]->SR & TIM_SR_UIF) != 0) { + CLEAR_BIT(TimerDomain::cmsis_timers[tim8_idx]->SR, TIM_SR_UIF); + TimerDomain::callbacks[tim8_idx](TimerDomain::callback_data[tim8_idx]); } - if ((TimerDomain::cmsis_timers[timer_idxmap[8]]->SR & TIM_SR_UIF) != 0) { - CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[8]]->SR, TIM_SR_UIF); - TimerDomain::callbacks[timer_idxmap[8]](TimerDomain::callback_data[timer_idxmap[8]]); + if (tim8_cc_channel != 0) { + TIM_IC_CaptureCallback(tim8_idx, tim8_cc_channel); } } extern "C" void TIM8_TRG_COM_TIM14_IRQHandler(void) { - if ((TimerDomain::cmsis_timers[timer_idxmap[14]]->SR & TIM_SR_UIF) != 0) { - CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[14]]->SR, TIM_SR_UIF); - TimerDomain::callbacks[timer_idxmap[14]](TimerDomain::callback_data[timer_idxmap[14]]); + constexpr uint32_t tim8_idx = timer_idxmap[8]; + constexpr uint32_t tim14_idx = timer_idxmap[14]; + + TIM_TypeDef* tim8 = TimerDomain::cmsis_timers[tim8_idx]; + TIM_TypeDef* tim14 = TimerDomain::cmsis_timers[tim14_idx]; + + uint32_t tim14_cc_channel = (tim14->SR & CaptureCompareInterruptMask) - 1; + if ((tim14->SR & TIM_SR_UIF) != 0) { + CLEAR_BIT(TimerDomain::cmsis_timers[tim14_idx]->SR, TIM_SR_UIF); + TimerDomain::callbacks[tim14_idx](TimerDomain::callback_data[tim14_idx]); } + if (tim14_cc_channel != 0) { + TIM_IC_CaptureCallback(tim14_idx, tim14_cc_channel); + } + constexpr uint32_t com_trg_flags = TIM_SR_TIF | TIM_SR_COMIF; - if ((TimerDomain::cmsis_timers[timer_idxmap[14]]->SR & com_trg_flags) != 0) { - CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[8]]->SR, com_trg_flags); + if ((TimerDomain::cmsis_timers[tim8_idx]->SR & com_trg_flags) != 0) { + CLEAR_BIT(TimerDomain::cmsis_timers[tim8_idx]->SR, com_trg_flags); /* this could probably have some other callback */ - TimerDomain::callbacks[timer_idxmap[8]](TimerDomain::callback_data[timer_idxmap[8]]); + TimerDomain::callbacks[tim8_idx](TimerDomain::callback_data[tim8_idx]); } } extern "C" void TIM15_IRQHandler(void) { - CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[15]]->SR, TIM_SR_UIF); - TimerDomain::callbacks[timer_idxmap[15]](TimerDomain::callback_data[timer_idxmap[15]]); + TIM_InterruptCallback(timer_idxmap[15]); } extern "C" void TIM16_IRQHandler(void) { - CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[16]]->SR, TIM_SR_UIF); - TimerDomain::callbacks[timer_idxmap[16]](TimerDomain::callback_data[timer_idxmap[16]]); + TIM_InterruptCallback(timer_idxmap[16]); } extern "C" void TIM17_IRQHandler(void) { - CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[17]]->SR, TIM_SR_UIF); - TimerDomain::callbacks[timer_idxmap[17]](TimerDomain::callback_data[timer_idxmap[17]]); + TIM_InterruptCallback(timer_idxmap[17]); } extern "C" void TIM23_IRQHandler(void) { - CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[23]]->SR, TIM_SR_UIF); - TimerDomain::callbacks[timer_idxmap[23]](TimerDomain::callback_data[timer_idxmap[23]]); + TIM_InterruptCallback(timer_idxmap[23]); } extern "C" void TIM24_IRQHandler(void) { - CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[24]]->SR, TIM_SR_UIF); - TimerDomain::callbacks[timer_idxmap[24]](TimerDomain::callback_data[timer_idxmap[24]]); + TIM_InterruptCallback(timer_idxmap[24]); } From 49eade87deed4d672ee94552a36872db586d286a Mon Sep 17 00:00:00 2001 From: victhor Date: Mon, 23 Feb 2026 16:00:34 +0100 Subject: [PATCH 03/44] enable interrupt for falling channel too --- Inc/HALAL/Services/InputCapture/InputCapture.hpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Inc/HALAL/Services/InputCapture/InputCapture.hpp b/Inc/HALAL/Services/InputCapture/InputCapture.hpp index 32f4cf15e..3b2b0b7ac 100644 --- a/Inc/HALAL/Services/InputCapture/InputCapture.hpp +++ b/Inc/HALAL/Services/InputCapture/InputCapture.hpp @@ -78,7 +78,7 @@ class InputCapture { SET_BIT(timer->instance->tim->CCER, enableCCx); } - // HAL_TIM_IC_Start(instance.peripheral->handle, instance.channel_falling) + // HAL_TIM_IC_Start_IT(instance.peripheral->handle, instance.channel_falling) { volatile HAL_TIM_ChannelStateTypeDef* ch_state = &timer->instance->hal_tim->ChannelState[TimerDomain::get_channel_state_idx(channel_falling)]; @@ -91,6 +91,7 @@ class InputCapture { *ch_state = HAL_TIM_CHANNEL_STATE_BUSY; *n_ch_state = HAL_TIM_CHANNEL_STATE_BUSY; + timer->template enable_capture_compare_interrupt(); uint32_t enableCCx = TIM_CCER_CC1E << (TimerDomain::get_channel_mul4(channel_falling) & 0x1FU ); /* 0x1FU = 31 bits max shift */ @@ -130,8 +131,10 @@ class InputCapture { *ch_n_state = HAL_TIM_CHANNEL_STATE_READY; } - // HAL_TIM_IC_Stop(instance.peripheral->handle, instance.channel_falling) + // HAL_TIM_IC_Stop_IT(instance.peripheral->handle, instance.channel_falling) { + timer->template disable_capture_compare_interrupt(); + CLEAR_BIT( timer->instance->tim->CCER, (uint32_t)(TIM_CCER_CC1E << (TimerDomain::get_channel_mul4(pin_rising.channel) & 0x1FU)) From 033a7bd6342e63cd9864f06c76b67aa842f90609 Mon Sep 17 00:00:00 2001 From: victhor Date: Mon, 23 Feb 2026 16:07:29 +0100 Subject: [PATCH 04/44] Fix some compile errors diff --git a/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp b/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp index 62d35fe0..82e745eb 100644 --- a/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp +++ b/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp @@ -777,38 +777,27 @@ extern TIM_HandleTypeDef htim24; static inline uint32_t get_timer_frequency(TIM_TypeDef* tim) { uint32_t result; - switch(tim) { - case TIM2: - case TIM3: - case TIM4: - case TIM5: - case TIM6: - case TIM7: - case TIM12: - case TIM13: - case TIM14: - result = HAL_RCC_GetPCLK1Freq(); - if ((RCC->D2CFGR & RCC_D2CFGR_D2PPRE1) != RCC_HCLK_DIV1) { - result *= 2; - } - break; - - case TIM1: - case TIM8: - case TIM15: - case TIM16: - case TIM17: - case TIM23: - case TIM24: - result = HAL_RCC_GetPCLK2Freq(); - if ((RCC->D2CFGR & RCC_D2CFGR_D2PPRE2) != RCC_HCLK_DIV1) { - result *= 2; - } - break; - default: - ErrorHandler("Invalid timer ptr"); - break; + if((tim == TIM2) || (tim == TIM3) || (tim == TIM4) || (tim == TIM5) || + (tim == TIM6) || (tim == TIM7) || (tim == TIM12) || (tim == TIM13) || + (tim == TIM14)) + { + result = HAL_RCC_GetPCLK1Freq(); + if ((RCC->D2CFGR & RCC_D2CFGR_D2PPRE1) != RCC_HCLK_DIV1) { + result *= 2; + } } + else if((tim == TIM1) || (tim == TIM8) || (tim == TIM15) || (tim == TIM16) || + (tim == TIM17) || (tim == TIM23) || (tim == TIM24)) + { + result = HAL_RCC_GetPCLK2Freq(); + if ((RCC->D2CFGR & RCC_D2CFGR_D2PPRE2) != RCC_HCLK_DIV1) { + result *= 2; + } + } + else { + ErrorHandler("Invalid timer ptr"); + } + return result; } }; diff --git a/Inc/HALAL/Services/Time/TimerWrapper.hpp b/Inc/HALAL/Services/Time/TimerWrapper.hpp index 4ee79c19..6b5a4cc2 100644 --- a/Inc/HALAL/Services/Time/TimerWrapper.hpp +++ b/Inc/HALAL/Services/Time/TimerWrapper.hpp @@ -659,7 +659,7 @@ template struct TimerWrapper { /* Set the filter */ CLEAR_BIT(tmpccmrx, TIM_CCMR1_IC1F); - SET_BIT(tmpccmrx, ((sConfig->ICFilter << 4U) & TIMM_CCMR1_IC1F)); + SET_BIT(tmpccmrx, ((sConfig->ICFilter << 4U) & TIM_CCMR1_IC1F)); /* Select the Polarity and set the CC1E Bit */ CLEAR_BIT(tmpccer, TIM_CCER_CC1P | TIM_CCER_CC1NP); diff --git a/Src/HALAL/Models/TimerDomain/TimerDomain.cpp b/Src/HALAL/Models/TimerDomain/TimerDomain.cpp index bbf65990..8779375f 100644 --- a/Src/HALAL/Models/TimerDomain/TimerDomain.cpp +++ b/Src/HALAL/Models/TimerDomain/TimerDomain.cpp @@ -26,14 +26,13 @@ void (*TimerDomain::callbacks[TimerDomain::max_instances])(void*) = {nullptr}; void* TimerDomain::callback_data[TimerDomain::max_instances] = {nullptr}; TimerDomain::InputCaptureInfo input_capture_info_backing[TimerDomain::max_instances][TimerDomain::input_capture_channels/2] = {}; TimerDomain::InputCaptureInfo* input_capture_info[TimerDomain::max_instances][TimerDomain::input_capture_channels] = {nullptr}; -#error TODO: In InputCapture.hpp associate the input_capture_info to input_capture_info_backing static void TIM_IC_CaptureCallback(const uint32_t timer_idx, uint32_t channel) { TIM_HandleTypeDef* htim = TimerDomain::hal_handles[timer_idx]; htim->Instance->CNT = 0; - InputCaptureInfo* info = input_capture_info[timer_idx][channel]; + TimerDomain::InputCaptureInfo* info = input_capture_info[timer_idx][channel]; if(info->channel_rising == channel) { // NOTE: CCR1 - CCR4 are contiguous info->value_rising = (float)(*(((uint32_t*)&htim->Instance->CCR1) + channel)); @@ -104,15 +103,15 @@ extern "C" void TIM8_BRK_TIM12_IRQHandler(void) { uint32_t tim12_cc_channel = (tim12->SR & CaptureCompareInterruptMask) - 1; if ((tim12->SR & TIM_SR_UIF) != 0) { - CLEAR_BIT(TimerDomain::cmsis_timers[tim12_idx]->SR, TIM_SR_UIF); + CLEAR_BIT(tim12->SR, TIM_SR_UIF); TimerDomain::callbacks[tim12_idx](TimerDomain::callback_data[tim12_idx]); } if (tim12_cc_channel != 0) { TIM_IC_CaptureCallback(tim12_idx, tim12_cc_channel); } - if ((TimerDomain::cmsis_timers[tim8_idx]->SR & TIM_SR_BIF) != 0) { - CLEAR_BIT(TimerDomain::cmsis_timers[tim8_idx]->SR, TIM_SR_BIF); + if ((tim8->SR & TIM_SR_BIF) != 0) { + CLEAR_BIT(tim8->SR, TIM_SR_BIF); /* this could probably have some other callback */ TimerDomain::callbacks[tim8_idx](TimerDomain::callback_data[tim8_idx]); } @@ -128,15 +127,15 @@ extern "C" void TIM8_UP_TIM13_IRQHandler(void) { uint32_t tim8_cc_channel = (tim8->SR & CaptureCompareInterruptMask) - 1; uint32_t tim13_cc_channel = (tim13->SR & CaptureCompareInterruptMask) - 1; if ((tim13->SR & TIM_SR_UIF) != 0) { - CLEAR_BIT(TimerDomain::cmsis_timers[tim13_idx]->SR, TIM_SR_UIF); + CLEAR_BIT(tim13->SR, TIM_SR_UIF); TimerDomain::callbacks[tim13_idx](TimerDomain::callback_data[tim13_idx]); } if (tim13_cc_channel != 0) { TIM_IC_CaptureCallback(tim13_idx, tim13_cc_channel); } - if ((TimerDomain::cmsis_timers[tim8_idx]->SR & TIM_SR_UIF) != 0) { - CLEAR_BIT(TimerDomain::cmsis_timers[tim8_idx]->SR, TIM_SR_UIF); + if ((tim8->SR & TIM_SR_UIF) != 0) { + CLEAR_BIT(tim8->SR, TIM_SR_UIF); TimerDomain::callbacks[tim8_idx](TimerDomain::callback_data[tim8_idx]); } if (tim8_cc_channel != 0) { @@ -153,7 +152,7 @@ extern "C" void TIM8_TRG_COM_TIM14_IRQHandler(void) { uint32_t tim14_cc_channel = (tim14->SR & CaptureCompareInterruptMask) - 1; if ((tim14->SR & TIM_SR_UIF) != 0) { - CLEAR_BIT(TimerDomain::cmsis_timers[tim14_idx]->SR, TIM_SR_UIF); + CLEAR_BIT(tim14->SR, TIM_SR_UIF); TimerDomain::callbacks[tim14_idx](TimerDomain::callback_data[tim14_idx]); } if (tim14_cc_channel != 0) { @@ -161,8 +160,8 @@ extern "C" void TIM8_TRG_COM_TIM14_IRQHandler(void) { } constexpr uint32_t com_trg_flags = TIM_SR_TIF | TIM_SR_COMIF; - if ((TimerDomain::cmsis_timers[tim8_idx]->SR & com_trg_flags) != 0) { - CLEAR_BIT(TimerDomain::cmsis_timers[tim8_idx]->SR, com_trg_flags); + if ((tim8->SR & com_trg_flags) != 0) { + CLEAR_BIT(tim8->SR, com_trg_flags); /* this could probably have some other callback */ TimerDomain::callbacks[tim8_idx](TimerDomain::callback_data[tim8_idx]); } --- Inc/HALAL/Models/TimerDomain/TimerDomain.hpp | 51 ++++++++------------ Inc/HALAL/Services/Time/TimerWrapper.hpp | 2 +- Src/HALAL/Models/TimerDomain/TimerDomain.cpp | 21 ++++---- 3 files changed, 31 insertions(+), 43 deletions(-) diff --git a/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp b/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp index 62d35fe0e..82e745eb1 100644 --- a/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp +++ b/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp @@ -777,38 +777,27 @@ extern TIM_HandleTypeDef htim24; static inline uint32_t get_timer_frequency(TIM_TypeDef* tim) { uint32_t result; - switch(tim) { - case TIM2: - case TIM3: - case TIM4: - case TIM5: - case TIM6: - case TIM7: - case TIM12: - case TIM13: - case TIM14: - result = HAL_RCC_GetPCLK1Freq(); - if ((RCC->D2CFGR & RCC_D2CFGR_D2PPRE1) != RCC_HCLK_DIV1) { - result *= 2; - } - break; - - case TIM1: - case TIM8: - case TIM15: - case TIM16: - case TIM17: - case TIM23: - case TIM24: - result = HAL_RCC_GetPCLK2Freq(); - if ((RCC->D2CFGR & RCC_D2CFGR_D2PPRE2) != RCC_HCLK_DIV1) { - result *= 2; - } - break; - default: - ErrorHandler("Invalid timer ptr"); - break; + if((tim == TIM2) || (tim == TIM3) || (tim == TIM4) || (tim == TIM5) || + (tim == TIM6) || (tim == TIM7) || (tim == TIM12) || (tim == TIM13) || + (tim == TIM14)) + { + result = HAL_RCC_GetPCLK1Freq(); + if ((RCC->D2CFGR & RCC_D2CFGR_D2PPRE1) != RCC_HCLK_DIV1) { + result *= 2; + } } + else if((tim == TIM1) || (tim == TIM8) || (tim == TIM15) || (tim == TIM16) || + (tim == TIM17) || (tim == TIM23) || (tim == TIM24)) + { + result = HAL_RCC_GetPCLK2Freq(); + if ((RCC->D2CFGR & RCC_D2CFGR_D2PPRE2) != RCC_HCLK_DIV1) { + result *= 2; + } + } + else { + ErrorHandler("Invalid timer ptr"); + } + return result; } }; diff --git a/Inc/HALAL/Services/Time/TimerWrapper.hpp b/Inc/HALAL/Services/Time/TimerWrapper.hpp index 4ee79c194..6b5a4cc25 100644 --- a/Inc/HALAL/Services/Time/TimerWrapper.hpp +++ b/Inc/HALAL/Services/Time/TimerWrapper.hpp @@ -659,7 +659,7 @@ template struct TimerWrapper { /* Set the filter */ CLEAR_BIT(tmpccmrx, TIM_CCMR1_IC1F); - SET_BIT(tmpccmrx, ((sConfig->ICFilter << 4U) & TIMM_CCMR1_IC1F)); + SET_BIT(tmpccmrx, ((sConfig->ICFilter << 4U) & TIM_CCMR1_IC1F)); /* Select the Polarity and set the CC1E Bit */ CLEAR_BIT(tmpccer, TIM_CCER_CC1P | TIM_CCER_CC1NP); diff --git a/Src/HALAL/Models/TimerDomain/TimerDomain.cpp b/Src/HALAL/Models/TimerDomain/TimerDomain.cpp index bbf659901..8779375f2 100644 --- a/Src/HALAL/Models/TimerDomain/TimerDomain.cpp +++ b/Src/HALAL/Models/TimerDomain/TimerDomain.cpp @@ -26,14 +26,13 @@ void (*TimerDomain::callbacks[TimerDomain::max_instances])(void*) = {nullptr}; void* TimerDomain::callback_data[TimerDomain::max_instances] = {nullptr}; TimerDomain::InputCaptureInfo input_capture_info_backing[TimerDomain::max_instances][TimerDomain::input_capture_channels/2] = {}; TimerDomain::InputCaptureInfo* input_capture_info[TimerDomain::max_instances][TimerDomain::input_capture_channels] = {nullptr}; -#error TODO: In InputCapture.hpp associate the input_capture_info to input_capture_info_backing static void TIM_IC_CaptureCallback(const uint32_t timer_idx, uint32_t channel) { TIM_HandleTypeDef* htim = TimerDomain::hal_handles[timer_idx]; htim->Instance->CNT = 0; - InputCaptureInfo* info = input_capture_info[timer_idx][channel]; + TimerDomain::InputCaptureInfo* info = input_capture_info[timer_idx][channel]; if(info->channel_rising == channel) { // NOTE: CCR1 - CCR4 are contiguous info->value_rising = (float)(*(((uint32_t*)&htim->Instance->CCR1) + channel)); @@ -104,15 +103,15 @@ extern "C" void TIM8_BRK_TIM12_IRQHandler(void) { uint32_t tim12_cc_channel = (tim12->SR & CaptureCompareInterruptMask) - 1; if ((tim12->SR & TIM_SR_UIF) != 0) { - CLEAR_BIT(TimerDomain::cmsis_timers[tim12_idx]->SR, TIM_SR_UIF); + CLEAR_BIT(tim12->SR, TIM_SR_UIF); TimerDomain::callbacks[tim12_idx](TimerDomain::callback_data[tim12_idx]); } if (tim12_cc_channel != 0) { TIM_IC_CaptureCallback(tim12_idx, tim12_cc_channel); } - if ((TimerDomain::cmsis_timers[tim8_idx]->SR & TIM_SR_BIF) != 0) { - CLEAR_BIT(TimerDomain::cmsis_timers[tim8_idx]->SR, TIM_SR_BIF); + if ((tim8->SR & TIM_SR_BIF) != 0) { + CLEAR_BIT(tim8->SR, TIM_SR_BIF); /* this could probably have some other callback */ TimerDomain::callbacks[tim8_idx](TimerDomain::callback_data[tim8_idx]); } @@ -128,15 +127,15 @@ extern "C" void TIM8_UP_TIM13_IRQHandler(void) { uint32_t tim8_cc_channel = (tim8->SR & CaptureCompareInterruptMask) - 1; uint32_t tim13_cc_channel = (tim13->SR & CaptureCompareInterruptMask) - 1; if ((tim13->SR & TIM_SR_UIF) != 0) { - CLEAR_BIT(TimerDomain::cmsis_timers[tim13_idx]->SR, TIM_SR_UIF); + CLEAR_BIT(tim13->SR, TIM_SR_UIF); TimerDomain::callbacks[tim13_idx](TimerDomain::callback_data[tim13_idx]); } if (tim13_cc_channel != 0) { TIM_IC_CaptureCallback(tim13_idx, tim13_cc_channel); } - if ((TimerDomain::cmsis_timers[tim8_idx]->SR & TIM_SR_UIF) != 0) { - CLEAR_BIT(TimerDomain::cmsis_timers[tim8_idx]->SR, TIM_SR_UIF); + if ((tim8->SR & TIM_SR_UIF) != 0) { + CLEAR_BIT(tim8->SR, TIM_SR_UIF); TimerDomain::callbacks[tim8_idx](TimerDomain::callback_data[tim8_idx]); } if (tim8_cc_channel != 0) { @@ -153,7 +152,7 @@ extern "C" void TIM8_TRG_COM_TIM14_IRQHandler(void) { uint32_t tim14_cc_channel = (tim14->SR & CaptureCompareInterruptMask) - 1; if ((tim14->SR & TIM_SR_UIF) != 0) { - CLEAR_BIT(TimerDomain::cmsis_timers[tim14_idx]->SR, TIM_SR_UIF); + CLEAR_BIT(tim14->SR, TIM_SR_UIF); TimerDomain::callbacks[tim14_idx](TimerDomain::callback_data[tim14_idx]); } if (tim14_cc_channel != 0) { @@ -161,8 +160,8 @@ extern "C" void TIM8_TRG_COM_TIM14_IRQHandler(void) { } constexpr uint32_t com_trg_flags = TIM_SR_TIF | TIM_SR_COMIF; - if ((TimerDomain::cmsis_timers[tim8_idx]->SR & com_trg_flags) != 0) { - CLEAR_BIT(TimerDomain::cmsis_timers[tim8_idx]->SR, com_trg_flags); + if ((tim8->SR & com_trg_flags) != 0) { + CLEAR_BIT(tim8->SR, com_trg_flags); /* this could probably have some other callback */ TimerDomain::callbacks[tim8_idx](TimerDomain::callback_data[tim8_idx]); } From 16b498a421e19acadae6dc1c97904b74e6ae832e Mon Sep 17 00:00:00 2001 From: victhor Date: Mon, 23 Feb 2026 16:30:02 +0100 Subject: [PATCH 05/44] Fix more compile errors --- .../Services/InputCapture/InputCapture.hpp | 53 ++++++++----------- Inc/HALAL/Services/Time/TimerWrapper.hpp | 33 ++++++++++++ 2 files changed, 56 insertions(+), 30 deletions(-) diff --git a/Inc/HALAL/Services/InputCapture/InputCapture.hpp b/Inc/HALAL/Services/InputCapture/InputCapture.hpp index 3b2b0b7ac..7f6f51da1 100644 --- a/Inc/HALAL/Services/InputCapture/InputCapture.hpp +++ b/Inc/HALAL/Services/InputCapture/InputCapture.hpp @@ -23,23 +23,25 @@ template < const ST_LIB::TimerPin pin_rising, const ST_LIB::TimerChannel channel_falling> class InputCapture { - TimerWrapper* timer; + TimerWrapper* timer = nullptr; TimerDomain::InputCaptureInfo *info = nullptr; bool is_on; public: - InputCapture(TimerWrapper* tim) : timer(tim) { + InputCapture(TimerWrapper* tim) { + timer = tim; + // Setup TimerDomain - this->info = + info = &TimerDomain::input_capture_info_backing[tim->instance->timer_idx][pin_rising.channel]; TimerDomain::input_capture_info[tim->instance->timer_idx][pin_rising.channel] = info; TimerDomain::input_capture_info[tim->instance->timer_idx][channel_falling] = info; - this->info->channel_rising = static_cast(pin_rising.channel) - 1; - this->info->channel_falling = static_cast(channel_falling) - 1; - this->info->value_rising = 0.0f; - this->info->duty_cycle = 0.0f; - this->info->frequency = 0; + info->channel_rising = static_cast(pin_rising.channel) - 1; + info->channel_falling = static_cast(channel_falling) - 1; + info->value_rising = 0.0f; + info->duty_cycle = 0.0f; + info->frequency = 0; TIM_IC_InitTypeDef sConfigIC = { .ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING, @@ -54,8 +56,8 @@ class InputCapture { timer->template config_input_compare_channel(&sConfigIC); } - static void turn_on(void) { - if (this->is_on) + void turn_on(void) { + if (is_on) return; // HAL_TIM_IC_Start_IT(instance.peripheral->handle, instance.channel_rising) @@ -99,7 +101,7 @@ class InputCapture { } if constexpr (timer->is_slave_instance) { - uint32_t tmpsmcr = timer->instance->tim->SMCR & SMCR_SMS; + uint32_t tmpsmcr = timer->instance->tim->SMCR & TIM_SMCR_SMS; if (!IS_TIM_SLAVEMODE_TRIGGER_ENABLED(tmpsmcr)) { timer->counter_enable(); } @@ -107,11 +109,11 @@ class InputCapture { timer->counter_enable(); } - this->is_on = true; + is_on = true; } - static void turn_off(void) { - if(!this->is_on) + void turn_off(void) { + if(!is_on) return; // HAL_TIM_IC_Stop_IT(instance.peripheral->handle, instance.channel_rising) @@ -128,7 +130,7 @@ class InputCapture { volatile HAL_TIM_ChannelStateTypeDef* n_ch_state = &timer->instance->hal_tim->ChannelNState[TimerDomain::get_channel_state_idx(channel_falling)]; *ch_state = HAL_TIM_CHANNEL_STATE_READY; - *ch_n_state = HAL_TIM_CHANNEL_STATE_READY; + *n_ch_state = HAL_TIM_CHANNEL_STATE_READY; } // HAL_TIM_IC_Stop_IT(instance.peripheral->handle, instance.channel_falling) @@ -145,31 +147,22 @@ class InputCapture { volatile HAL_TIM_ChannelStateTypeDef* n_ch_state = &timer->instance->hal_tim->ChannelNState[TimerDomain::get_channel_state_idx(channel_falling)]; *ch_state = HAL_TIM_CHANNEL_STATE_READY; - *ch_n_state = HAL_TIM_CHANNEL_STATE_READY; + *n_ch_state = HAL_TIM_CHANNEL_STATE_READY; } if (timer->are_all_channels_free()) { timer->counter_disable(); } -#error TODO - if (HAL_TIM_IC_Stop(instance.peripheral->handle, instance.channel_falling) != HAL_OK) { - ErrorHandler( - "Unable to stop the %s Input Capture measurement", - instance.peripheral->name.c_str() - ); - } - - - this->is_on = false; + is_on = false; } - static uint32_t get_frequency(void) { - return this->info->frequency; + uint32_t get_frequency(void) { + return info->frequency; } - static float get_duty_cycle(void) { - return this->info->duty_cycle; + float get_duty_cycle(void) { + return info->duty_cycle; } }; diff --git a/Inc/HALAL/Services/Time/TimerWrapper.hpp b/Inc/HALAL/Services/Time/TimerWrapper.hpp index 6b5a4cc25..ff677e3c4 100644 --- a/Inc/HALAL/Services/Time/TimerWrapper.hpp +++ b/Inc/HALAL/Services/Time/TimerWrapper.hpp @@ -12,6 +12,7 @@ #ifdef HAL_TIM_MODULE_ENABLED #include "HALAL/Models/TimerDomain/TimerDomain.hpp" +#include "HALAL/Services/InputCapture/InputCapture.hpp" #include "HALAL/Services/Encoder/Encoder.hpp" #include "HALAL/Services/PWM/DualPWM.hpp" #include "HALAL/Services/PWM/PWM.hpp" @@ -59,6 +60,7 @@ template struct TimerWrapper { dev.e.request == TimerRequest::GeneralPurpose_15 || dev.e.request == TimerRequest::GeneralPurpose_16 || dev.e.request == TimerRequest::GeneralPurpose_17); + /* at least 2 capture/compare channels (see IS_TIM_CC2_INSTANCE for macro definition) */ static constexpr bool is_CC2_instance = (dev.e.request == TimerRequest::Advanced_1 || @@ -71,6 +73,19 @@ template struct TimerWrapper { dev.e.request == TimerRequest::GeneralPurpose_15 || dev.e.request == TimerRequest::GeneralPurpose32bit_23 || dev.e.request == TimerRequest::GeneralPurpose32bit_24); + /* at least 3 capture/compare channels (see IS_TIM_CC3_INSTANCE for macro definition) */ + static constexpr bool is_CC3_instance = + (dev.e.request == TimerRequest::Advanced_1 || + dev.e.request == TimerRequest::GeneralPurpose32bit_2 || + dev.e.request == TimerRequest::GeneralPurpose_3 || + dev.e.request == TimerRequest::GeneralPurpose_4 || + dev.e.request == TimerRequest::GeneralPurpose32bit_5 || + dev.e.request == TimerRequest::Advanced_8 || + dev.e.request == TimerRequest::GeneralPurpose32bit_23 || + dev.e.request == TimerRequest::GeneralPurpose32bit_24); + /* at least 4 capture/compare channels (see IS_TIM_CC4_INSTANCE for macro definition) */ + static constexpr bool is_CC4_instance = is_CC3_instance; + static constexpr bool is_slave_instance = (dev.e.request == TimerRequest::Advanced_1 || dev.e.request == TimerRequest::GeneralPurpose32bit_2 || @@ -286,6 +301,24 @@ template struct TimerWrapper { inline Encoder get_encoder() { return Encoder(this); } + template + inline InputCapture get_input_capture(void) + { + static_assert(rising_pin.channel != channel_falling, "Rising and falling channels must be different"); + static_assert(rising_pin.af == TimerAF::InputCapture, "Pin must be configured as input capture"); + static_assert((static_cast(channel_falling) - 1) <= 4, "Channel must be 1 to 4 for inputcapture"); + static_assert(this->is_CC2_instance, "Timer must have 2 or more Capture compare channels"); + + if constexpr(channel_falling >= TimerChannel::CHANNEL_3 && !this->is_CC3_instance) { + ST_LIB::compile_error("Error: This timer does not have 3 or more Capture compare channels"); + } + if constexpr(channel_falling == TimerChannel::CHANNEL_4 && !this->is_CC4_instance) { + ST_LIB::compile_error("Error: This timer does not have 4 or more Capture compare channels"); + } + + return InputCapture(this); + } + inline void counter_enable() { SET_BIT(instance->tim->CR1, TIM_CR1_CEN); } inline void counter_disable() { CLEAR_BIT(instance->tim->CR1, TIM_CR1_CEN); } From f796d5afeca20f3feb9d7f0563f6df99321906c3 Mon Sep 17 00:00:00 2001 From: victhor Date: Mon, 23 Feb 2026 19:00:52 +0100 Subject: [PATCH 06/44] feat: mode, pull, speed for InputCapture af diff --git a/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp b/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp index 82e745eb..354f9b0d 100644 --- a/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp +++ b/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp @@ -370,10 +370,10 @@ extern TIM_HandleTypeDef htim24; case TimerAF::Encoder: return GPIODomain::OperationMode::ALT_PP; - // TODO: check what this really needs to be for each case TimerAF::InputCapture: - return GPIODomain::OperationMode::OUTPUT_OPENDRAIN; + return GPIODomain::OperationMode::ALT_PP; + // TODO: check what this really needs to be for each case TimerAF::BreakInput: return GPIODomain::OperationMode::OUTPUT_OPENDRAIN; case TimerAF::BreakInputCompare: @@ -391,10 +391,10 @@ extern TIM_HandleTypeDef htim24; case TimerAF::Encoder: return GPIODomain::Pull::Up; - // TODO: check what this really needs to be for each case TimerAF::InputCapture: - return GPIODomain::Pull::Up; - + return GPIODomain::Pull::None; + + // TODO: check what this really needs to be for each case TimerAF::BreakInput: return GPIODomain::Pull::None; case TimerAF::BreakInputCompare: @@ -412,10 +412,10 @@ extern TIM_HandleTypeDef htim24; case TimerAF::Encoder: return GPIODomain::Speed::Low; - // TODO: check what this really needs to be for each case TimerAF::InputCapture: return GPIODomain::Speed::Low; - + + // TODO: check what this really needs to be for each case TimerAF::BreakInput: return GPIODomain::Speed::Low; case TimerAF::BreakInputCompare: --- Inc/HALAL/Models/TimerDomain/TimerDomain.hpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp b/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp index 82e745eb1..354f9b0df 100644 --- a/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp +++ b/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp @@ -370,10 +370,10 @@ extern TIM_HandleTypeDef htim24; case TimerAF::Encoder: return GPIODomain::OperationMode::ALT_PP; - // TODO: check what this really needs to be for each case TimerAF::InputCapture: - return GPIODomain::OperationMode::OUTPUT_OPENDRAIN; + return GPIODomain::OperationMode::ALT_PP; + // TODO: check what this really needs to be for each case TimerAF::BreakInput: return GPIODomain::OperationMode::OUTPUT_OPENDRAIN; case TimerAF::BreakInputCompare: @@ -391,10 +391,10 @@ extern TIM_HandleTypeDef htim24; case TimerAF::Encoder: return GPIODomain::Pull::Up; - // TODO: check what this really needs to be for each case TimerAF::InputCapture: - return GPIODomain::Pull::Up; - + return GPIODomain::Pull::None; + + // TODO: check what this really needs to be for each case TimerAF::BreakInput: return GPIODomain::Pull::None; case TimerAF::BreakInputCompare: @@ -412,10 +412,10 @@ extern TIM_HandleTypeDef htim24; case TimerAF::Encoder: return GPIODomain::Speed::Low; - // TODO: check what this really needs to be for each case TimerAF::InputCapture: return GPIODomain::Speed::Low; - + + // TODO: check what this really needs to be for each case TimerAF::BreakInput: return GPIODomain::Speed::Low; case TimerAF::BreakInputCompare: From 0c8a811cdb3c1a4317bdb4c7fb04604e75fbece5 Mon Sep 17 00:00:00 2001 From: victhor Date: Mon, 23 Feb 2026 22:03:15 +0100 Subject: [PATCH 07/44] partial fix of things diff --git a/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp b/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp index 354f9b0d..7564d06c 100644 --- a/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp +++ b/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp @@ -239,7 +239,7 @@ extern TIM_HandleTypeDef htim24; float duty_cycle; uint32_t frequency; }; - /* 2x as big as necessary but this makes indexing easier */ + /* 2x as big as necessary but this makes indexing easier & faster */ static InputCaptureInfo* input_capture_info[max_instances][input_capture_channels]; static InputCaptureInfo input_capture_info_backing[max_instances][input_capture_channels]; @@ -689,7 +689,25 @@ extern TIM_HandleTypeDef htim24; template struct Init { static inline std::array instances{}; + static void TIM_Default_Callback(void* raw) { (void)raw; } + static void init(std::span cfgs) { + TimerDomain::callbacks[1] = TIM_Default_Callback; + TimerDomain::callbacks[2] = TIM_Default_Callback; + TimerDomain::callbacks[3] = TIM_Default_Callback; + TimerDomain::callbacks[4] = TIM_Default_Callback; + TimerDomain::callbacks[5] = TIM_Default_Callback; + TimerDomain::callbacks[6] = TIM_Default_Callback; + TimerDomain::callbacks[7] = TIM_Default_Callback; + TimerDomain::callbacks[8] = TIM_Default_Callback; + TimerDomain::callbacks[9] = TIM_Default_Callback; + TimerDomain::callbacks[10] = TIM_Default_Callback; + TimerDomain::callbacks[11] = TIM_Default_Callback; + TimerDomain::callbacks[12] = TIM_Default_Callback; + TimerDomain::callbacks[13] = TIM_Default_Callback; + TimerDomain::callbacks[14] = TIM_Default_Callback; + TimerDomain::callbacks[15] = TIM_Default_Callback; + for (std::size_t i = 0; i < N; i++) { const Config& e = cfgs[i]; diff --git a/Inc/HALAL/Services/InputCapture/InputCapture.hpp b/Inc/HALAL/Services/InputCapture/InputCapture.hpp index 7f6f51da..23bb6f5f 100644 --- a/Inc/HALAL/Services/InputCapture/InputCapture.hpp +++ b/Inc/HALAL/Services/InputCapture/InputCapture.hpp @@ -25,29 +25,33 @@ template < class InputCapture { TimerWrapper* timer = nullptr; TimerDomain::InputCaptureInfo *info = nullptr; - bool is_on; + bool is_on = false; public: InputCapture(TimerWrapper* tim) { timer = tim; // Setup TimerDomain + uint8_t ch_rising = static_cast(pin_rising.channel) - 1; + uint8_t ch_falling = static_cast(channel_falling) - 1; info = - &TimerDomain::input_capture_info_backing[tim->instance->timer_idx][pin_rising.channel]; - TimerDomain::input_capture_info[tim->instance->timer_idx][pin_rising.channel] = info; - TimerDomain::input_capture_info[tim->instance->timer_idx][channel_falling] = info; + &TimerDomain::input_capture_info_backing[tim->instance->timer_idx][ch_rising]; + TimerDomain::input_capture_info[tim->instance->timer_idx][ch_rising] = info; + TimerDomain::input_capture_info[tim->instance->timer_idx][ch_falling] = info; - info->channel_rising = static_cast(pin_rising.channel) - 1; - info->channel_falling = static_cast(channel_falling) - 1; + info->channel_rising = ch_rising; + info->channel_falling = ch_falling; info->value_rising = 0.0f; info->duty_cycle = 0.0f; info->frequency = 0; - + + timer->enable_nvic(); + TIM_IC_InitTypeDef sConfigIC = { .ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING, + .ICSelection = TIM_ICSELECTION_DIRECTTI, .ICPrescaler = TIM_ICPSC_DIV1, .ICFilter = 0, - .ICSelection = TIM_ICSELECTION_DIRECTTI, }; timer->template config_input_compare_channel(&sConfigIC); diff --git a/Src/HALAL/Models/TimerDomain/TimerDomain.cpp b/Src/HALAL/Models/TimerDomain/TimerDomain.cpp index 8779375f..caf40c29 100644 --- a/Src/HALAL/Models/TimerDomain/TimerDomain.cpp +++ b/Src/HALAL/Models/TimerDomain/TimerDomain.cpp @@ -24,15 +24,16 @@ TIM_HandleTypeDef htim24; void (*TimerDomain::callbacks[TimerDomain::max_instances])(void*) = {nullptr}; void* TimerDomain::callback_data[TimerDomain::max_instances] = {nullptr}; -TimerDomain::InputCaptureInfo input_capture_info_backing[TimerDomain::max_instances][TimerDomain::input_capture_channels/2] = {}; -TimerDomain::InputCaptureInfo* input_capture_info[TimerDomain::max_instances][TimerDomain::input_capture_channels] = {nullptr}; +TimerDomain::InputCaptureInfo* TimerDomain::input_capture_info[max_instances][input_capture_channels]; +TimerDomain::InputCaptureInfo TimerDomain::input_capture_info_backing[max_instances][input_capture_channels]; -static void TIM_IC_CaptureCallback(const uint32_t timer_idx, uint32_t channel) +static void TIM_IC_CaptureCallback(const uint32_t timer_idx, uint32_t cc_channel) { TIM_HandleTypeDef* htim = TimerDomain::hal_handles[timer_idx]; htim->Instance->CNT = 0; - TimerDomain::InputCaptureInfo* info = input_capture_info[timer_idx][channel]; + uint32_t channel = __builtin_ffs(cc_channel) - 2; + TimerDomain::InputCaptureInfo* info = TimerDomain::input_capture_info[timer_idx][channel]; if(info->channel_rising == channel) { // NOTE: CCR1 - CCR4 are contiguous info->value_rising = (float)(*(((uint32_t*)&htim->Instance->CCR1) + channel)); @@ -51,12 +52,12 @@ static void TIM_IC_CaptureCallback(const uint32_t timer_idx, uint32_t channel) static void TIM_InterruptCallback(const uint32_t timer_idx) { TIM_TypeDef* tim = TimerDomain::cmsis_timers[timer_idx]; - uint32_t cc_channel = (tim->SR & CaptureCompareInterruptMask) - 1; if(tim->SR & TIM_SR_UIF) { CLEAR_BIT(tim->SR, TIM_SR_UIF); TimerDomain::callbacks[timer_idx](TimerDomain::callback_data[timer_idx]); } - + + uint32_t cc_channel = tim->SR & CaptureCompareInterruptMask; if(cc_channel != 0) { TIM_IC_CaptureCallback(timer_idx, cc_channel); } @@ -101,7 +102,7 @@ extern "C" void TIM8_BRK_TIM12_IRQHandler(void) { TIM_TypeDef* tim8 = TimerDomain::cmsis_timers[tim8_idx]; TIM_TypeDef* tim12 = TimerDomain::cmsis_timers[tim12_idx]; - uint32_t tim12_cc_channel = (tim12->SR & CaptureCompareInterruptMask) - 1; + uint32_t tim12_cc_channel = tim12->SR & CaptureCompareInterruptMask; if ((tim12->SR & TIM_SR_UIF) != 0) { CLEAR_BIT(tim12->SR, TIM_SR_UIF); TimerDomain::callbacks[tim12_idx](TimerDomain::callback_data[tim12_idx]); @@ -124,8 +125,8 @@ extern "C" void TIM8_UP_TIM13_IRQHandler(void) { TIM_TypeDef* tim8 = TimerDomain::cmsis_timers[tim8_idx]; TIM_TypeDef* tim13 = TimerDomain::cmsis_timers[tim13_idx]; - uint32_t tim8_cc_channel = (tim8->SR & CaptureCompareInterruptMask) - 1; - uint32_t tim13_cc_channel = (tim13->SR & CaptureCompareInterruptMask) - 1; + uint32_t tim8_cc_channel = tim8->SR & CaptureCompareInterruptMask; + uint32_t tim13_cc_channel = tim13->SR & CaptureCompareInterruptMask; if ((tim13->SR & TIM_SR_UIF) != 0) { CLEAR_BIT(tim13->SR, TIM_SR_UIF); TimerDomain::callbacks[tim13_idx](TimerDomain::callback_data[tim13_idx]); @@ -150,7 +151,7 @@ extern "C" void TIM8_TRG_COM_TIM14_IRQHandler(void) { TIM_TypeDef* tim8 = TimerDomain::cmsis_timers[tim8_idx]; TIM_TypeDef* tim14 = TimerDomain::cmsis_timers[tim14_idx]; - uint32_t tim14_cc_channel = (tim14->SR & CaptureCompareInterruptMask) - 1; + uint32_t tim14_cc_channel = tim14->SR & CaptureCompareInterruptMask; if ((tim14->SR & TIM_SR_UIF) != 0) { CLEAR_BIT(tim14->SR, TIM_SR_UIF); TimerDomain::callbacks[tim14_idx](TimerDomain::callback_data[tim14_idx]); --- Inc/HALAL/Models/TimerDomain/TimerDomain.hpp | 20 +++++++++++++++++- .../Services/InputCapture/InputCapture.hpp | 20 +++++++++++------- Src/HALAL/Models/TimerDomain/TimerDomain.cpp | 21 ++++++++++--------- 3 files changed, 42 insertions(+), 19 deletions(-) diff --git a/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp b/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp index 354f9b0df..7564d06c4 100644 --- a/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp +++ b/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp @@ -239,7 +239,7 @@ extern TIM_HandleTypeDef htim24; float duty_cycle; uint32_t frequency; }; - /* 2x as big as necessary but this makes indexing easier */ + /* 2x as big as necessary but this makes indexing easier & faster */ static InputCaptureInfo* input_capture_info[max_instances][input_capture_channels]; static InputCaptureInfo input_capture_info_backing[max_instances][input_capture_channels]; @@ -689,7 +689,25 @@ extern TIM_HandleTypeDef htim24; template struct Init { static inline std::array instances{}; + static void TIM_Default_Callback(void* raw) { (void)raw; } + static void init(std::span cfgs) { + TimerDomain::callbacks[1] = TIM_Default_Callback; + TimerDomain::callbacks[2] = TIM_Default_Callback; + TimerDomain::callbacks[3] = TIM_Default_Callback; + TimerDomain::callbacks[4] = TIM_Default_Callback; + TimerDomain::callbacks[5] = TIM_Default_Callback; + TimerDomain::callbacks[6] = TIM_Default_Callback; + TimerDomain::callbacks[7] = TIM_Default_Callback; + TimerDomain::callbacks[8] = TIM_Default_Callback; + TimerDomain::callbacks[9] = TIM_Default_Callback; + TimerDomain::callbacks[10] = TIM_Default_Callback; + TimerDomain::callbacks[11] = TIM_Default_Callback; + TimerDomain::callbacks[12] = TIM_Default_Callback; + TimerDomain::callbacks[13] = TIM_Default_Callback; + TimerDomain::callbacks[14] = TIM_Default_Callback; + TimerDomain::callbacks[15] = TIM_Default_Callback; + for (std::size_t i = 0; i < N; i++) { const Config& e = cfgs[i]; diff --git a/Inc/HALAL/Services/InputCapture/InputCapture.hpp b/Inc/HALAL/Services/InputCapture/InputCapture.hpp index 7f6f51da1..23bb6f5f3 100644 --- a/Inc/HALAL/Services/InputCapture/InputCapture.hpp +++ b/Inc/HALAL/Services/InputCapture/InputCapture.hpp @@ -25,29 +25,33 @@ template < class InputCapture { TimerWrapper* timer = nullptr; TimerDomain::InputCaptureInfo *info = nullptr; - bool is_on; + bool is_on = false; public: InputCapture(TimerWrapper* tim) { timer = tim; // Setup TimerDomain + uint8_t ch_rising = static_cast(pin_rising.channel) - 1; + uint8_t ch_falling = static_cast(channel_falling) - 1; info = - &TimerDomain::input_capture_info_backing[tim->instance->timer_idx][pin_rising.channel]; - TimerDomain::input_capture_info[tim->instance->timer_idx][pin_rising.channel] = info; - TimerDomain::input_capture_info[tim->instance->timer_idx][channel_falling] = info; + &TimerDomain::input_capture_info_backing[tim->instance->timer_idx][ch_rising]; + TimerDomain::input_capture_info[tim->instance->timer_idx][ch_rising] = info; + TimerDomain::input_capture_info[tim->instance->timer_idx][ch_falling] = info; - info->channel_rising = static_cast(pin_rising.channel) - 1; - info->channel_falling = static_cast(channel_falling) - 1; + info->channel_rising = ch_rising; + info->channel_falling = ch_falling; info->value_rising = 0.0f; info->duty_cycle = 0.0f; info->frequency = 0; - + + timer->enable_nvic(); + TIM_IC_InitTypeDef sConfigIC = { .ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING, + .ICSelection = TIM_ICSELECTION_DIRECTTI, .ICPrescaler = TIM_ICPSC_DIV1, .ICFilter = 0, - .ICSelection = TIM_ICSELECTION_DIRECTTI, }; timer->template config_input_compare_channel(&sConfigIC); diff --git a/Src/HALAL/Models/TimerDomain/TimerDomain.cpp b/Src/HALAL/Models/TimerDomain/TimerDomain.cpp index 8779375f2..caf40c298 100644 --- a/Src/HALAL/Models/TimerDomain/TimerDomain.cpp +++ b/Src/HALAL/Models/TimerDomain/TimerDomain.cpp @@ -24,15 +24,16 @@ TIM_HandleTypeDef htim24; void (*TimerDomain::callbacks[TimerDomain::max_instances])(void*) = {nullptr}; void* TimerDomain::callback_data[TimerDomain::max_instances] = {nullptr}; -TimerDomain::InputCaptureInfo input_capture_info_backing[TimerDomain::max_instances][TimerDomain::input_capture_channels/2] = {}; -TimerDomain::InputCaptureInfo* input_capture_info[TimerDomain::max_instances][TimerDomain::input_capture_channels] = {nullptr}; +TimerDomain::InputCaptureInfo* TimerDomain::input_capture_info[max_instances][input_capture_channels]; +TimerDomain::InputCaptureInfo TimerDomain::input_capture_info_backing[max_instances][input_capture_channels]; -static void TIM_IC_CaptureCallback(const uint32_t timer_idx, uint32_t channel) +static void TIM_IC_CaptureCallback(const uint32_t timer_idx, uint32_t cc_channel) { TIM_HandleTypeDef* htim = TimerDomain::hal_handles[timer_idx]; htim->Instance->CNT = 0; - TimerDomain::InputCaptureInfo* info = input_capture_info[timer_idx][channel]; + uint32_t channel = __builtin_ffs(cc_channel) - 2; + TimerDomain::InputCaptureInfo* info = TimerDomain::input_capture_info[timer_idx][channel]; if(info->channel_rising == channel) { // NOTE: CCR1 - CCR4 are contiguous info->value_rising = (float)(*(((uint32_t*)&htim->Instance->CCR1) + channel)); @@ -51,12 +52,12 @@ static void TIM_IC_CaptureCallback(const uint32_t timer_idx, uint32_t channel) static void TIM_InterruptCallback(const uint32_t timer_idx) { TIM_TypeDef* tim = TimerDomain::cmsis_timers[timer_idx]; - uint32_t cc_channel = (tim->SR & CaptureCompareInterruptMask) - 1; if(tim->SR & TIM_SR_UIF) { CLEAR_BIT(tim->SR, TIM_SR_UIF); TimerDomain::callbacks[timer_idx](TimerDomain::callback_data[timer_idx]); } - + + uint32_t cc_channel = tim->SR & CaptureCompareInterruptMask; if(cc_channel != 0) { TIM_IC_CaptureCallback(timer_idx, cc_channel); } @@ -101,7 +102,7 @@ extern "C" void TIM8_BRK_TIM12_IRQHandler(void) { TIM_TypeDef* tim8 = TimerDomain::cmsis_timers[tim8_idx]; TIM_TypeDef* tim12 = TimerDomain::cmsis_timers[tim12_idx]; - uint32_t tim12_cc_channel = (tim12->SR & CaptureCompareInterruptMask) - 1; + uint32_t tim12_cc_channel = tim12->SR & CaptureCompareInterruptMask; if ((tim12->SR & TIM_SR_UIF) != 0) { CLEAR_BIT(tim12->SR, TIM_SR_UIF); TimerDomain::callbacks[tim12_idx](TimerDomain::callback_data[tim12_idx]); @@ -124,8 +125,8 @@ extern "C" void TIM8_UP_TIM13_IRQHandler(void) { TIM_TypeDef* tim8 = TimerDomain::cmsis_timers[tim8_idx]; TIM_TypeDef* tim13 = TimerDomain::cmsis_timers[tim13_idx]; - uint32_t tim8_cc_channel = (tim8->SR & CaptureCompareInterruptMask) - 1; - uint32_t tim13_cc_channel = (tim13->SR & CaptureCompareInterruptMask) - 1; + uint32_t tim8_cc_channel = tim8->SR & CaptureCompareInterruptMask; + uint32_t tim13_cc_channel = tim13->SR & CaptureCompareInterruptMask; if ((tim13->SR & TIM_SR_UIF) != 0) { CLEAR_BIT(tim13->SR, TIM_SR_UIF); TimerDomain::callbacks[tim13_idx](TimerDomain::callback_data[tim13_idx]); @@ -150,7 +151,7 @@ extern "C" void TIM8_TRG_COM_TIM14_IRQHandler(void) { TIM_TypeDef* tim8 = TimerDomain::cmsis_timers[tim8_idx]; TIM_TypeDef* tim14 = TimerDomain::cmsis_timers[tim14_idx]; - uint32_t tim14_cc_channel = (tim14->SR & CaptureCompareInterruptMask) - 1; + uint32_t tim14_cc_channel = tim14->SR & CaptureCompareInterruptMask; if ((tim14->SR & TIM_SR_UIF) != 0) { CLEAR_BIT(tim14->SR, TIM_SR_UIF); TimerDomain::callbacks[tim14_idx](TimerDomain::callback_data[tim14_idx]); From bc05884f9c4664e903eafd3b090ce00ddc8b39a0 Mon Sep 17 00:00:00 2001 From: victhor Date: Sat, 7 Mar 2026 10:45:01 +0100 Subject: [PATCH 08/44] Remove get_channel_state_idx, get_channel_mul4 from pwm, dualpwm --- Inc/HALAL/Services/PWM/DualPWM.hpp | 46 +----------------------------- Inc/HALAL/Services/PWM/PWM.hpp | 46 +----------------------------- 2 files changed, 2 insertions(+), 90 deletions(-) diff --git a/Inc/HALAL/Services/PWM/DualPWM.hpp b/Inc/HALAL/Services/PWM/DualPWM.hpp index 91e71922a..4f8f60b9c 100644 --- a/Inc/HALAL/Services/PWM/DualPWM.hpp +++ b/Inc/HALAL/Services/PWM/DualPWM.hpp @@ -21,57 +21,13 @@ template < class DualPWM { friend TimerWrapper; - static consteval uint8_t get_channel_state_idx(const ST_LIB::TimerChannel ch) { - switch (ch) { - case TimerChannel::CHANNEL_1: - case TimerChannel::CHANNEL_1_NEGATED: - case TimerChannel::CHANNEL_2: - case TimerChannel::CHANNEL_2_NEGATED: - case TimerChannel::CHANNEL_3: - case TimerChannel::CHANNEL_3_NEGATED: - case TimerChannel::CHANNEL_4: - case TimerChannel::CHANNEL_5: - case TimerChannel::CHANNEL_6: - return (static_cast(ch) & - ~static_cast(TimerChannel::CHANNEL_NEGATED_FLAG)) - - 1; - - default: - ST_LIB::compile_error("unreachable"); - return 0; - } - } - - static consteval uint8_t get_channel_mul4(const ST_LIB::TimerChannel ch) { - switch (ch) { - case TimerChannel::CHANNEL_1: - case TimerChannel::CHANNEL_1_NEGATED: - return 0x00; - case TimerChannel::CHANNEL_2: - case TimerChannel::CHANNEL_2_NEGATED: - return 0x04; - case TimerChannel::CHANNEL_3: - case TimerChannel::CHANNEL_3_NEGATED: - return 0x08; - case TimerChannel::CHANNEL_4: - return 0x0C; - case TimerChannel::CHANNEL_5: - return 0x10; - case TimerChannel::CHANNEL_6: - return 0x14; - - default: - ST_LIB::compile_error("unreachable"); - return 0; - } - } - TimerWrapper* timer; uint32_t* frequency; float* duty_cycle = nullptr; bool is_on_positive = false; bool is_on_negative = false; + /* This constructor is private for a reason. Use TimerWrapper::get_dual_pwm */ DualPWM( TimerWrapper* tim, uint32_t polarity, diff --git a/Inc/HALAL/Services/PWM/PWM.hpp b/Inc/HALAL/Services/PWM/PWM.hpp index 6e4d8c14d..f85adad7f 100644 --- a/Inc/HALAL/Services/PWM/PWM.hpp +++ b/Inc/HALAL/Services/PWM/PWM.hpp @@ -17,56 +17,12 @@ template struct TimerWrapper; template class PWM { friend TimerWrapper; - static consteval uint8_t get_channel_state_idx(const ST_LIB::TimerChannel ch) { - switch (ch) { - case TimerChannel::CHANNEL_1: - case TimerChannel::CHANNEL_1_NEGATED: - case TimerChannel::CHANNEL_2: - case TimerChannel::CHANNEL_2_NEGATED: - case TimerChannel::CHANNEL_3: - case TimerChannel::CHANNEL_3_NEGATED: - case TimerChannel::CHANNEL_4: - case TimerChannel::CHANNEL_5: - case TimerChannel::CHANNEL_6: - return (static_cast(ch) & - ~static_cast(TimerChannel::CHANNEL_NEGATED_FLAG)) - - 1; - - default: - ST_LIB::compile_error("unreachable"); - return 0; - } - } - - static consteval uint8_t get_channel_mul4(const ST_LIB::TimerChannel ch) { - switch (ch) { - case TimerChannel::CHANNEL_1: - case TimerChannel::CHANNEL_1_NEGATED: - return 0x00; - case TimerChannel::CHANNEL_2: - case TimerChannel::CHANNEL_2_NEGATED: - return 0x04; - case TimerChannel::CHANNEL_3: - case TimerChannel::CHANNEL_3_NEGATED: - return 0x08; - case TimerChannel::CHANNEL_4: - return 0x0C; - case TimerChannel::CHANNEL_5: - return 0x10; - case TimerChannel::CHANNEL_6: - return 0x14; - - default: - ST_LIB::compile_error("unreachable"); - return 0; - } - } - TimerWrapper* timer; uint32_t* frequency; float* duty_cycle = nullptr; bool is_on = false; + /* This constructor is private for a reason. Use TimerWrapper::get_pwm */ PWM(TimerWrapper* tim, uint32_t polarity, uint32_t negated_polarity, From 0deab809dcd45422e6700fbcda34114f1786af7a Mon Sep 17 00:00:00 2001 From: victhor Date: Sat, 7 Mar 2026 19:41:12 +0100 Subject: [PATCH 09/44] Add return after ErrorHandler --- Inc/HALAL/Services/InputCapture/InputCapture.hpp | 2 ++ Src/HALAL/Models/TimerDomain/TimerDomain.cpp | 1 + 2 files changed, 3 insertions(+) diff --git a/Inc/HALAL/Services/InputCapture/InputCapture.hpp b/Inc/HALAL/Services/InputCapture/InputCapture.hpp index 23bb6f5f3..ba381ffb5 100644 --- a/Inc/HALAL/Services/InputCapture/InputCapture.hpp +++ b/Inc/HALAL/Services/InputCapture/InputCapture.hpp @@ -72,6 +72,7 @@ class InputCapture { &timer->instance->hal_tim->ChannelNState[TimerDomain::get_channel_state_idx(pin_rising.channel)]; if ((*ch_state != HAL_TIM_CHANNEL_STATE_READY) || (*n_ch_state != HAL_TIM_CHANNEL_STATE_READY)) { ErrorHandler("Channels not ready"); + return; } *ch_state = HAL_TIM_CHANNEL_STATE_BUSY; @@ -92,6 +93,7 @@ class InputCapture { &timer->instance->hal_tim->ChannelNState[TimerDomain::get_channel_state_idx(channel_falling)]; if ((*ch_state != HAL_TIM_CHANNEL_STATE_READY) || (*n_ch_state != HAL_TIM_CHANNEL_STATE_READY)) { ErrorHandler("Channels not ready"); + return; } *ch_state = HAL_TIM_CHANNEL_STATE_BUSY; diff --git a/Src/HALAL/Models/TimerDomain/TimerDomain.cpp b/Src/HALAL/Models/TimerDomain/TimerDomain.cpp index caf40c298..6b53e0847 100644 --- a/Src/HALAL/Models/TimerDomain/TimerDomain.cpp +++ b/Src/HALAL/Models/TimerDomain/TimerDomain.cpp @@ -31,6 +31,7 @@ static void TIM_IC_CaptureCallback(const uint32_t timer_idx, uint32_t cc_channel { TIM_HandleTypeDef* htim = TimerDomain::hal_handles[timer_idx]; htim->Instance->CNT = 0; + htim->Instance->SR &= ~cc_channel; uint32_t channel = __builtin_ffs(cc_channel) - 2; TimerDomain::InputCaptureInfo* info = TimerDomain::input_capture_info[timer_idx][channel]; From bb14e8ce20118b7efe694c4359f438116debc1d8 Mon Sep 17 00:00:00 2001 From: victhor Date: Sat, 7 Mar 2026 19:42:18 +0100 Subject: [PATCH 10/44] Make TimerWrapper a friend and make constructor private --- Inc/HALAL/Services/InputCapture/InputCapture.hpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Inc/HALAL/Services/InputCapture/InputCapture.hpp b/Inc/HALAL/Services/InputCapture/InputCapture.hpp index ba381ffb5..067b1a3af 100644 --- a/Inc/HALAL/Services/InputCapture/InputCapture.hpp +++ b/Inc/HALAL/Services/InputCapture/InputCapture.hpp @@ -23,11 +23,12 @@ template < const ST_LIB::TimerPin pin_rising, const ST_LIB::TimerChannel channel_falling> class InputCapture { + friend struct TimerWrapper; + TimerWrapper* timer = nullptr; TimerDomain::InputCaptureInfo *info = nullptr; bool is_on = false; -public: InputCapture(TimerWrapper* tim) { timer = tim; @@ -59,6 +60,7 @@ class InputCapture { sConfigIC.ICSelection = TIM_ICSELECTION_INDIRECTTI; timer->template config_input_compare_channel(&sConfigIC); } +public: void turn_on(void) { if (is_on) From 00bee2cd0af74a07b05dfff246e249b4146fedc4 Mon Sep 17 00:00:00 2001 From: victhor Date: Sun, 8 Mar 2026 11:59:29 +0100 Subject: [PATCH 11/44] Added a couple of guards just in case --- Inc/HALAL/Services/Time/TimerWrapper.hpp | 6 +++--- Src/HALAL/Models/TimerDomain/TimerDomain.cpp | 11 +++++++++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/Inc/HALAL/Services/Time/TimerWrapper.hpp b/Inc/HALAL/Services/Time/TimerWrapper.hpp index ff677e3c4..b2009c630 100644 --- a/Inc/HALAL/Services/Time/TimerWrapper.hpp +++ b/Inc/HALAL/Services/Time/TimerWrapper.hpp @@ -716,7 +716,7 @@ template struct TimerWrapper { SET_BIT(tmpccer, (sConfig->ICPolarity >> 4U) & (TIM_CCER_CC2P | TIM_CCER_CC2NP)); CLEAR_BIT(tmpccmrx, TIM_CCMR1_IC2PSC); - SET_BIT(tmpccmrx, sConfig->ICPrescaler << 8U); + SET_BIT(tmpccmrx, (sConfig->ICPrescaler << 8U) & TIM_CCMR1_IC2PSC); } else if constexpr((ch == TimerChannel::CHANNEL_3) || (ch == TimerChannel::CHANNEL_3_NEGATED)) { CLEAR_BIT(tmpccer, TIM_CCER_CC3E); @@ -733,7 +733,7 @@ template struct TimerWrapper { SET_BIT(tmpccer, (sConfig->ICPolarity << 8U) & (TIM_CCER_CC3P | TIM_CCER_CC3NP)); CLEAR_BIT(tmpccmrx, TIM_CCMR2_IC3PSC); - SET_BIT(tmpccmrx, sConfig->ICPrescaler); + SET_BIT(tmpccmrx, (sConfig->ICPrescaler) & TIM_CCMR2_IC3PSC); } else if constexpr(ch == TimerChannel::CHANNEL_4) { CLEAR_BIT(tmpccer, TIM_CCER_CC4E); @@ -750,7 +750,7 @@ template struct TimerWrapper { SET_BIT(tmpccer, (sConfig->ICPolarity << 12U) & (TIM_CCER_CC4P | TIM_CCER_CC4NP)); CLEAR_BIT(tmpccmrx, TIM_CCMR2_IC4PSC); - SET_BIT(tmpccmrx, sConfig->ICPrescaler << 8U); + SET_BIT(tmpccmrx, (sConfig->ICPrescaler << 8U) & TIM_CCMR2_IC4PSC); } if constexpr (ch == TimerChannel::CHANNEL_1 || ch == TimerChannel::CHANNEL_2) { diff --git a/Src/HALAL/Models/TimerDomain/TimerDomain.cpp b/Src/HALAL/Models/TimerDomain/TimerDomain.cpp index 6b53e0847..582e3b410 100644 --- a/Src/HALAL/Models/TimerDomain/TimerDomain.cpp +++ b/Src/HALAL/Models/TimerDomain/TimerDomain.cpp @@ -3,7 +3,10 @@ using namespace ST_LIB; #define CaptureCompareInterruptMask \ - (TIM_DIER_CC1IE | TIM_DIER_CC2IE | TIM_DIER_CC3IE | TIM_DIER_CC4IE) + (TIM_SR_CC1IF | TIM_SR_CC2IF | TIM_SR_CC3IF | TIM_SR_CC4IF) + +#define CaptureCompareOvercaptureMask \ + (TIM_SR_CC1IF | TIM_SR_CC2IF | TIM_SR_CC3IF | TIM_SR_CC4IF) TIM_HandleTypeDef htim1; TIM_HandleTypeDef htim2; @@ -31,14 +34,18 @@ static void TIM_IC_CaptureCallback(const uint32_t timer_idx, uint32_t cc_channel { TIM_HandleTypeDef* htim = TimerDomain::hal_handles[timer_idx]; htim->Instance->CNT = 0; - htim->Instance->SR &= ~cc_channel; uint32_t channel = __builtin_ffs(cc_channel) - 2; TimerDomain::InputCaptureInfo* info = TimerDomain::input_capture_info[timer_idx][channel]; if(info->channel_rising == channel) { // NOTE: CCR1 - CCR4 are contiguous + // NOTE: CCxIF flag is cleared by software by reading the captured data in CCRx info->value_rising = (float)(*(((uint32_t*)&htim->Instance->CCR1) + channel)); + if((htim->Instance->SR & CaptureCompareOvercaptureMask) != 0) [[unlikely]] { + CLEAR_BIT(htim->Instance->SR, CaptureCompareOvercaptureMask); + } + uint32_t ref_clock = TimerDomain::get_timer_frequency(htim->Instance) / (htim->Instance->PSC + 1); info->frequency = (uint32_t)((ref_clock / info->value_rising) + 0.5f); } else if(info->channel_falling == channel) { From 0b2202822b15c91de5a8218b6a9b1b43848900df Mon Sep 17 00:00:00 2001 From: victhor Date: Sun, 8 Mar 2026 12:53:54 +0100 Subject: [PATCH 12/44] fix copypasta error --- Inc/HALAL/Services/InputCapture/InputCapture.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Inc/HALAL/Services/InputCapture/InputCapture.hpp b/Inc/HALAL/Services/InputCapture/InputCapture.hpp index 067b1a3af..36c3d0c26 100644 --- a/Inc/HALAL/Services/InputCapture/InputCapture.hpp +++ b/Inc/HALAL/Services/InputCapture/InputCapture.hpp @@ -134,9 +134,9 @@ class InputCapture { ); volatile HAL_TIM_ChannelStateTypeDef* ch_state = - &timer->instance->hal_tim->ChannelState[TimerDomain::get_channel_state_idx(channel_falling)]; + &timer->instance->hal_tim->ChannelState[TimerDomain::get_channel_state_idx(pin_rising.channel)]; volatile HAL_TIM_ChannelStateTypeDef* n_ch_state = - &timer->instance->hal_tim->ChannelNState[TimerDomain::get_channel_state_idx(channel_falling)]; + &timer->instance->hal_tim->ChannelNState[TimerDomain::get_channel_state_idx(pin_rising.channel)]; *ch_state = HAL_TIM_CHANNEL_STATE_READY; *n_ch_state = HAL_TIM_CHANNEL_STATE_READY; } @@ -147,7 +147,7 @@ class InputCapture { CLEAR_BIT( timer->instance->tim->CCER, - (uint32_t)(TIM_CCER_CC1E << (TimerDomain::get_channel_mul4(pin_rising.channel) & 0x1FU)) + (uint32_t)(TIM_CCER_CC1E << (TimerDomain::get_channel_mul4(channel_falling) & 0x1FU)) ); volatile HAL_TIM_ChannelStateTypeDef* ch_state = From 776bb575f5f987df4dea937ebbd1163bd3ec2719 Mon Sep 17 00:00:00 2001 From: victhor Date: Sun, 8 Mar 2026 13:45:41 +0100 Subject: [PATCH 13/44] Add a couple assertions and an explenation in the interrupt callback --- Inc/HALAL/Services/Time/TimerWrapper.hpp | 14 ++++++++++++++ Src/HALAL/Models/TimerDomain/TimerDomain.cpp | 7 +++++++ 2 files changed, 21 insertions(+) diff --git a/Inc/HALAL/Services/Time/TimerWrapper.hpp b/Inc/HALAL/Services/Time/TimerWrapper.hpp index b2009c630..17827ac49 100644 --- a/Inc/HALAL/Services/Time/TimerWrapper.hpp +++ b/Inc/HALAL/Services/Time/TimerWrapper.hpp @@ -301,10 +301,24 @@ template struct TimerWrapper { inline Encoder get_encoder() { return Encoder(this); } + /* {rising channel, falling channel} must be {1,2} or {3,4} (any order) */ template inline InputCapture get_input_capture(void) { static_assert(rising_pin.channel != channel_falling, "Rising and falling channels must be different"); + + /* channel 1 can only go with channel 2 */ + static_assert(!((rising_pin.channel == TimerChannel::CHANNEL_1) && + (channel_falling != TimerChannel::CHANNEL_2)), "Channel 1 must go with channel 2 for inputcapture"); + static_assert(!((channel_falling == TimerChannel::CHANNEL_1) && + (rising_pin.channel != TimerChannel::CHANNEL_2)), "Channel 1 must go with channel 2 for inputcapture"); + + /* channel 3 can only go with channel 4 */ + static_assert(!((rising_pin.channel == TimerChannel::CHANNEL_3) && + (channel_falling != TimerChannel::CHANNEL_4)), "Channel 3 must go with channel 4 for inputcapture"); + static_assert(!((channel_falling == TimerChannel::CHANNEL_3) && + (rising_pin.channel != TimerChannel::CHANNEL_4)), "Channel 3 must go with channel 4 for inputcapture"); + static_assert(rising_pin.af == TimerAF::InputCapture, "Pin must be configured as input capture"); static_assert((static_cast(channel_falling) - 1) <= 4, "Channel must be 1 to 4 for inputcapture"); static_assert(this->is_CC2_instance, "Timer must have 2 or more Capture compare channels"); diff --git a/Src/HALAL/Models/TimerDomain/TimerDomain.cpp b/Src/HALAL/Models/TimerDomain/TimerDomain.cpp index 582e3b410..f3a0d76b3 100644 --- a/Src/HALAL/Models/TimerDomain/TimerDomain.cpp +++ b/Src/HALAL/Models/TimerDomain/TimerDomain.cpp @@ -35,6 +35,13 @@ static void TIM_IC_CaptureCallback(const uint32_t timer_idx, uint32_t cc_channel TIM_HandleTypeDef* htim = TimerDomain::hal_handles[timer_idx]; htim->Instance->CNT = 0; + /* reasoning: + * 1. __builtin_ffs(x) returns first set bit + 1 + * 2. channels from 1 to 4 in SR are in bits 1,2,3,4 + * 3. ffs(channel_bits) = 2,3,4,5 + * 4. ffs(channel_bits) - 2 = 0,1,2,3 + * 5. use {ffs(channel_bits) - 2} as an index + */ uint32_t channel = __builtin_ffs(cc_channel) - 2; TimerDomain::InputCaptureInfo* info = TimerDomain::input_capture_info[timer_idx][channel]; if(info->channel_rising == channel) { From 2fbe657c7e958111c2e5e584f8762b8f3415f5ea Mon Sep 17 00:00:00 2001 From: victhor Date: Wed, 11 Mar 2026 15:14:22 +0100 Subject: [PATCH 14/44] Fix interrupt callback --- Inc/HALAL/Services/Encoder/Encoder.hpp | 8 ++- Src/HALAL/Models/TimerDomain/TimerDomain.cpp | 55 ++++++++++---------- 2 files changed, 34 insertions(+), 29 deletions(-) diff --git a/Inc/HALAL/Services/Encoder/Encoder.hpp b/Inc/HALAL/Services/Encoder/Encoder.hpp index 24f3fa30b..1f8aea2ae 100644 --- a/Inc/HALAL/Services/Encoder/Encoder.hpp +++ b/Inc/HALAL/Services/Encoder/Encoder.hpp @@ -62,8 +62,12 @@ template class Encoder { return; } - tim->instance->tim->PSC = 5; - tim->instance->tim->ARR = 55000; + //tim->instance->tim->PSC = 5; + if constexpr(tim->is_32bit_instance) { + tim->instance->tim->ARR = 55000; + } else { + tim->instance->tim->ARR = UINT32_MAX; + } timer = tim; } diff --git a/Src/HALAL/Models/TimerDomain/TimerDomain.cpp b/Src/HALAL/Models/TimerDomain/TimerDomain.cpp index f3a0d76b3..1ec92d684 100644 --- a/Src/HALAL/Models/TimerDomain/TimerDomain.cpp +++ b/Src/HALAL/Models/TimerDomain/TimerDomain.cpp @@ -30,19 +30,11 @@ void* TimerDomain::callback_data[TimerDomain::max_instances] = {nullptr}; TimerDomain::InputCaptureInfo* TimerDomain::input_capture_info[max_instances][input_capture_channels]; TimerDomain::InputCaptureInfo TimerDomain::input_capture_info_backing[max_instances][input_capture_channels]; -static void TIM_IC_CaptureCallback(const uint32_t timer_idx, uint32_t cc_channel) +static void TIM_IC_CaptureCallback(const uint32_t timer_idx, uint32_t channel) { TIM_HandleTypeDef* htim = TimerDomain::hal_handles[timer_idx]; - htim->Instance->CNT = 0; - - /* reasoning: - * 1. __builtin_ffs(x) returns first set bit + 1 - * 2. channels from 1 to 4 in SR are in bits 1,2,3,4 - * 3. ffs(channel_bits) = 2,3,4,5 - * 4. ffs(channel_bits) - 2 = 0,1,2,3 - * 5. use {ffs(channel_bits) - 2} as an index - */ - uint32_t channel = __builtin_ffs(cc_channel) - 2; + //htim->Instance->CNT = 0; + TimerDomain::InputCaptureInfo* info = TimerDomain::input_capture_info[timer_idx][channel]; if(info->channel_rising == channel) { // NOTE: CCR1 - CCR4 are contiguous @@ -73,8 +65,10 @@ static void TIM_InterruptCallback(const uint32_t timer_idx) } uint32_t cc_channel = tim->SR & CaptureCompareInterruptMask; - if(cc_channel != 0) { - TIM_IC_CaptureCallback(timer_idx, cc_channel); + while (cc_channel != 0) { + uint32_t channel = __builtin_ctz(cc_channel); + TIM_IC_CaptureCallback(timer_idx, channel - 1); // first bit is UIF + CLEAR_BIT(cc_channel, 1U << channel); } } @@ -117,13 +111,15 @@ extern "C" void TIM8_BRK_TIM12_IRQHandler(void) { TIM_TypeDef* tim8 = TimerDomain::cmsis_timers[tim8_idx]; TIM_TypeDef* tim12 = TimerDomain::cmsis_timers[tim12_idx]; - uint32_t tim12_cc_channel = tim12->SR & CaptureCompareInterruptMask; if ((tim12->SR & TIM_SR_UIF) != 0) { CLEAR_BIT(tim12->SR, TIM_SR_UIF); TimerDomain::callbacks[tim12_idx](TimerDomain::callback_data[tim12_idx]); } - if (tim12_cc_channel != 0) { - TIM_IC_CaptureCallback(tim12_idx, tim12_cc_channel); + uint32_t tim12_cc_channel = tim12->SR & CaptureCompareInterruptMask; + while (tim12_cc_channel != 0) { + uint32_t channel = __builtin_ctz(tim12_cc_channel); + TIM_IC_CaptureCallback(tim12_idx, channel - 1); // first bit is UIF + CLEAR_BIT(tim12_cc_channel, 1U << channel); } if ((tim8->SR & TIM_SR_BIF) != 0) { @@ -140,24 +136,27 @@ extern "C" void TIM8_UP_TIM13_IRQHandler(void) { TIM_TypeDef* tim8 = TimerDomain::cmsis_timers[tim8_idx]; TIM_TypeDef* tim13 = TimerDomain::cmsis_timers[tim13_idx]; - uint32_t tim8_cc_channel = tim8->SR & CaptureCompareInterruptMask; - uint32_t tim13_cc_channel = tim13->SR & CaptureCompareInterruptMask; if ((tim13->SR & TIM_SR_UIF) != 0) { CLEAR_BIT(tim13->SR, TIM_SR_UIF); TimerDomain::callbacks[tim13_idx](TimerDomain::callback_data[tim13_idx]); } - if (tim13_cc_channel != 0) { - TIM_IC_CaptureCallback(tim13_idx, tim13_cc_channel); + uint32_t tim13_cc_channel = tim13->SR & CaptureCompareInterruptMask; + while (tim13_cc_channel != 0) { + uint32_t channel = __builtin_ctz(tim13_cc_channel); + TIM_IC_CaptureCallback(tim13_idx, channel - 1); // first bit is UIF + CLEAR_BIT(tim13_cc_channel, 1U << channel); } if ((tim8->SR & TIM_SR_UIF) != 0) { CLEAR_BIT(tim8->SR, TIM_SR_UIF); TimerDomain::callbacks[tim8_idx](TimerDomain::callback_data[tim8_idx]); } - if (tim8_cc_channel != 0) { - TIM_IC_CaptureCallback(tim8_idx, tim8_cc_channel); - } -} + uint32_t tim8_cc_channel = tim8->SR & CaptureCompareInterruptMask; + while (tim8_cc_channel != 0) { + uint32_t channel = __builtin_ctz(tim8_cc_channel); + TIM_IC_CaptureCallback(tim8_idx, channel - 1); // first bit is UIF + CLEAR_BIT(tim8_cc_channel, 1U << channel); + }} extern "C" void TIM8_TRG_COM_TIM14_IRQHandler(void) { constexpr uint32_t tim8_idx = timer_idxmap[8]; @@ -166,13 +165,15 @@ extern "C" void TIM8_TRG_COM_TIM14_IRQHandler(void) { TIM_TypeDef* tim8 = TimerDomain::cmsis_timers[tim8_idx]; TIM_TypeDef* tim14 = TimerDomain::cmsis_timers[tim14_idx]; - uint32_t tim14_cc_channel = tim14->SR & CaptureCompareInterruptMask; if ((tim14->SR & TIM_SR_UIF) != 0) { CLEAR_BIT(tim14->SR, TIM_SR_UIF); TimerDomain::callbacks[tim14_idx](TimerDomain::callback_data[tim14_idx]); } - if (tim14_cc_channel != 0) { - TIM_IC_CaptureCallback(tim14_idx, tim14_cc_channel); + uint32_t tim14_cc_channel = tim14->SR & CaptureCompareInterruptMask; + while (tim14_cc_channel != 0) { + uint32_t channel = __builtin_ctz(tim14_cc_channel); + TIM_IC_CaptureCallback(tim14_idx, channel - 1); // first bit is UIF + CLEAR_BIT(tim14_cc_channel, 1U << channel); } constexpr uint32_t com_trg_flags = TIM_SR_TIF | TIM_SR_COMIF; From 79ce85e2ea18d088002c3a9fad4989c090638084 Mon Sep 17 00:00:00 2001 From: victhor Date: Wed, 11 Mar 2026 15:15:16 +0100 Subject: [PATCH 15/44] revert PSC = 0 in Encoder, less energy usage --- Inc/HALAL/Services/Encoder/Encoder.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Inc/HALAL/Services/Encoder/Encoder.hpp b/Inc/HALAL/Services/Encoder/Encoder.hpp index 1f8aea2ae..0b6364595 100644 --- a/Inc/HALAL/Services/Encoder/Encoder.hpp +++ b/Inc/HALAL/Services/Encoder/Encoder.hpp @@ -62,7 +62,7 @@ template class Encoder { return; } - //tim->instance->tim->PSC = 5; + tim->instance->tim->PSC = 5; if constexpr(tim->is_32bit_instance) { tim->instance->tim->ARR = 55000; } else { From a897304aeb130f6ba1fbadccc8b4c58e61e5e19a Mon Sep 17 00:00:00 2001 From: victhor Date: Wed, 11 Mar 2026 21:42:50 +0100 Subject: [PATCH 16/44] fix all wierd stuff with timer interrupts for now --- Inc/HALAL/Models/TimerDomain/TimerDomain.hpp | 14 +++---- .../Services/InputCapture/InputCapture.hpp | 5 ++- Src/HALAL/Models/TimerDomain/TimerDomain.cpp | 37 ++++++++++--------- 3 files changed, 31 insertions(+), 25 deletions(-) diff --git a/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp b/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp index d12f531f7..7590011bd 100644 --- a/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp +++ b/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp @@ -244,8 +244,10 @@ extern TIM_HandleTypeDef htim24; uint8_t channel_rising; uint8_t channel_falling; - float value_rising; - + uint32_t value_rising; + uint32_t value_falling; + uint32_t period; + float duty_cycle; uint32_t frequency; }; @@ -812,7 +814,7 @@ extern TIM_HandleTypeDef htim24; static inline uint32_t get_timer_frequency(TIM_TypeDef* tim) { uint32_t result; - if((tim == TIM2) || (tim == TIM3) || (tim == TIM4) || (tim == TIM5) || + if ((tim == TIM2) || (tim == TIM3) || (tim == TIM4) || (tim == TIM5) || (tim == TIM6) || (tim == TIM7) || (tim == TIM12) || (tim == TIM13) || (tim == TIM14)) { @@ -820,16 +822,14 @@ extern TIM_HandleTypeDef htim24; if ((RCC->D2CFGR & RCC_D2CFGR_D2PPRE1) != RCC_HCLK_DIV1) { result *= 2; } - } - else if((tim == TIM1) || (tim == TIM8) || (tim == TIM15) || (tim == TIM16) || + } else if ((tim == TIM1) || (tim == TIM8) || (tim == TIM15) || (tim == TIM16) || (tim == TIM17) || (tim == TIM23) || (tim == TIM24)) { result = HAL_RCC_GetPCLK2Freq(); if ((RCC->D2CFGR & RCC_D2CFGR_D2PPRE2) != RCC_HCLK_DIV1) { result *= 2; } - } - else { + } else { ErrorHandler("Invalid timer ptr"); } diff --git a/Inc/HALAL/Services/InputCapture/InputCapture.hpp b/Inc/HALAL/Services/InputCapture/InputCapture.hpp index 36c3d0c26..37859a567 100644 --- a/Inc/HALAL/Services/InputCapture/InputCapture.hpp +++ b/Inc/HALAL/Services/InputCapture/InputCapture.hpp @@ -42,9 +42,12 @@ class InputCapture { info->channel_rising = ch_rising; info->channel_falling = ch_falling; - info->value_rising = 0.0f; + + info->value_falling = 0; info->duty_cycle = 0.0f; + info->value_rising = 0; info->frequency = 0; + info->period = 0; timer->enable_nvic(); diff --git a/Src/HALAL/Models/TimerDomain/TimerDomain.cpp b/Src/HALAL/Models/TimerDomain/TimerDomain.cpp index 1ec92d684..913691c34 100644 --- a/Src/HALAL/Models/TimerDomain/TimerDomain.cpp +++ b/Src/HALAL/Models/TimerDomain/TimerDomain.cpp @@ -33,24 +33,24 @@ TimerDomain::InputCaptureInfo TimerDomain::input_capture_info_backing[max_instan static void TIM_IC_CaptureCallback(const uint32_t timer_idx, uint32_t channel) { TIM_HandleTypeDef* htim = TimerDomain::hal_handles[timer_idx]; - //htim->Instance->CNT = 0; TimerDomain::InputCaptureInfo* info = TimerDomain::input_capture_info[timer_idx][channel]; if(info->channel_rising == channel) { // NOTE: CCR1 - CCR4 are contiguous // NOTE: CCxIF flag is cleared by software by reading the captured data in CCRx - info->value_rising = (float)(*(((uint32_t*)&htim->Instance->CCR1) + channel)); - - if((htim->Instance->SR & CaptureCompareOvercaptureMask) != 0) [[unlikely]] { - CLEAR_BIT(htim->Instance->SR, CaptureCompareOvercaptureMask); + uint32_t current = (*(((uint32_t*)&htim->Instance->CCR1) + channel)); + uint32_t period = current - info->value_rising; + + if((period != 0) && (info->value_falling < period)) { + uint32_t ref_clock = TimerDomain::get_timer_frequency(htim->Instance) / (htim->Instance->PSC + 1); + info->period = period; + info->frequency = ref_clock / period; + info->duty_cycle = ((float)info->value_falling * 100.0f) / (float)period; } - - uint32_t ref_clock = TimerDomain::get_timer_frequency(htim->Instance) / (htim->Instance->PSC + 1); - info->frequency = (uint32_t)((ref_clock / info->value_rising) + 0.5f); + info->value_rising = current; } else if(info->channel_falling == channel) { - uint32_t falling_value = *(((uint32_t*)&htim->Instance->CCR1) + channel); - - info->duty_cycle = ((float)falling_value * 100.0f) / info->value_rising; + uint32_t falling_value = *(((uint32_t*)&htim->Instance->CCR1) + channel) - info->value_rising; + if(falling_value < info->period) info->value_falling = falling_value; } else [[unlikely]] { ErrorHandler("TimerDomain::input_capture_info was modified"); } @@ -63,12 +63,15 @@ static void TIM_InterruptCallback(const uint32_t timer_idx) CLEAR_BIT(tim->SR, TIM_SR_UIF); TimerDomain::callbacks[timer_idx](TimerDomain::callback_data[timer_idx]); } - - uint32_t cc_channel = tim->SR & CaptureCompareInterruptMask; - while (cc_channel != 0) { - uint32_t channel = __builtin_ctz(cc_channel); - TIM_IC_CaptureCallback(timer_idx, channel - 1); // first bit is UIF - CLEAR_BIT(cc_channel, 1U << channel); + + // NOTE: possible optimization: only do the channels possible for timer + // Bit 0 = UIF, bits 1 - 4 = CCxIF + for (uint32_t ch = 1; ch < 5; ch++) { + uint32_t flag_mask = 1U << ch; + if(tim->SR & flag_mask) { + TIM_IC_CaptureCallback(timer_idx, ch - 1); + CLEAR_BIT(tim->SR, flag_mask); + } } } From 2bbc2615b42703ab55fb594eebeb7c37ba314fb4 Mon Sep 17 00:00:00 2001 From: victhor Date: Thu, 12 Mar 2026 23:15:02 +0100 Subject: [PATCH 17/44] Add default prescaler value for inputcapture --- Inc/HALAL/Services/Time/TimerWrapper.hpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Inc/HALAL/Services/Time/TimerWrapper.hpp b/Inc/HALAL/Services/Time/TimerWrapper.hpp index 17827ac49..651ae90d6 100644 --- a/Inc/HALAL/Services/Time/TimerWrapper.hpp +++ b/Inc/HALAL/Services/Time/TimerWrapper.hpp @@ -301,10 +301,22 @@ template struct TimerWrapper { inline Encoder get_encoder() { return Encoder(this); } +/* InputCapture prescaler values: + * PSC = 0, ARR = 0: frequency range = [16800, 610'000] + * PSC = 1, ARR = 0: frequency range = [15000, 610'000] + * PSC = 10, ARR = 0: frequency range = [880, 610'000] + * PSC = 40, ARR = 0: frequency range = [280, 610'000] + * PSC = 400, ARR = 0: frequency range = [18, 100'000] takes long to get good precision + * PSC = 2000, ARR = 0: frequency range = [8, 60'000] takes long to get good precision + **/ +#define IMD_IC_PRESCALER_VALUE 2000 + /* {rising channel, falling channel} must be {1,2} or {3,4} (any order) */ template - inline InputCapture get_input_capture(void) + inline InputCapture get_input_capture(uint16_t prescaler = IMD_IC_PRESCALER_VALUE) { + instance->tim->PSC = prescaler; + static_assert(rising_pin.channel != channel_falling, "Rising and falling channels must be different"); /* channel 1 can only go with channel 2 */ From 06aa4fc0d08d6fd1e3ffe52ff4e1b22600d2711a Mon Sep 17 00:00:00 2001 From: victhor Date: Thu, 12 Mar 2026 23:17:10 +0100 Subject: [PATCH 18/44] apply changes to tim8 irq handlers --- Src/HALAL/Models/TimerDomain/TimerDomain.cpp | 53 ++++++++++++-------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/Src/HALAL/Models/TimerDomain/TimerDomain.cpp b/Src/HALAL/Models/TimerDomain/TimerDomain.cpp index 913691c34..c3cecb3ec 100644 --- a/Src/HALAL/Models/TimerDomain/TimerDomain.cpp +++ b/Src/HALAL/Models/TimerDomain/TimerDomain.cpp @@ -68,7 +68,7 @@ static void TIM_InterruptCallback(const uint32_t timer_idx) // Bit 0 = UIF, bits 1 - 4 = CCxIF for (uint32_t ch = 1; ch < 5; ch++) { uint32_t flag_mask = 1U << ch; - if(tim->SR & flag_mask) { + if (tim->SR & flag_mask) { TIM_IC_CaptureCallback(timer_idx, ch - 1); CLEAR_BIT(tim->SR, flag_mask); } @@ -118,11 +118,13 @@ extern "C" void TIM8_BRK_TIM12_IRQHandler(void) { CLEAR_BIT(tim12->SR, TIM_SR_UIF); TimerDomain::callbacks[tim12_idx](TimerDomain::callback_data[tim12_idx]); } - uint32_t tim12_cc_channel = tim12->SR & CaptureCompareInterruptMask; - while (tim12_cc_channel != 0) { - uint32_t channel = __builtin_ctz(tim12_cc_channel); - TIM_IC_CaptureCallback(tim12_idx, channel - 1); // first bit is UIF - CLEAR_BIT(tim12_cc_channel, 1U << channel); + // Bit 0 = UIF, bits 1 - 4 = CCxIF + for (uint32_t ch = 1; ch < 5; ch++) { + uint32_t flag_mask = 1U << ch; + if (tim12->SR & flag_mask) { + TIM_IC_CaptureCallback(tim12_idx, ch - 1); + CLEAR_BIT(tim12->SR, flag_mask); + } } if ((tim8->SR & TIM_SR_BIF) != 0) { @@ -143,23 +145,28 @@ extern "C" void TIM8_UP_TIM13_IRQHandler(void) { CLEAR_BIT(tim13->SR, TIM_SR_UIF); TimerDomain::callbacks[tim13_idx](TimerDomain::callback_data[tim13_idx]); } - uint32_t tim13_cc_channel = tim13->SR & CaptureCompareInterruptMask; - while (tim13_cc_channel != 0) { - uint32_t channel = __builtin_ctz(tim13_cc_channel); - TIM_IC_CaptureCallback(tim13_idx, channel - 1); // first bit is UIF - CLEAR_BIT(tim13_cc_channel, 1U << channel); + // Bit 0 = UIF, bits 1 - 4 = CCxIF + for (uint32_t ch = 1; ch < 5; ch++) { + uint32_t flag_mask = 1U << ch; + if (tim13->SR & flag_mask) { + TIM_IC_CaptureCallback(tim13_idx, ch - 1); + CLEAR_BIT(tim13->SR, flag_mask); + } } if ((tim8->SR & TIM_SR_UIF) != 0) { CLEAR_BIT(tim8->SR, TIM_SR_UIF); TimerDomain::callbacks[tim8_idx](TimerDomain::callback_data[tim8_idx]); } - uint32_t tim8_cc_channel = tim8->SR & CaptureCompareInterruptMask; - while (tim8_cc_channel != 0) { - uint32_t channel = __builtin_ctz(tim8_cc_channel); - TIM_IC_CaptureCallback(tim8_idx, channel - 1); // first bit is UIF - CLEAR_BIT(tim8_cc_channel, 1U << channel); - }} + // Bit 0 = UIF, bits 1 - 4 = CCxIF + for (uint32_t ch = 1; ch < 5; ch++) { + uint32_t flag_mask = 1U << ch; + if (tim8->SR & flag_mask) { + TIM_IC_CaptureCallback(tim8_idx, ch - 1); + CLEAR_BIT(tim8->SR, flag_mask); + } + } +} extern "C" void TIM8_TRG_COM_TIM14_IRQHandler(void) { constexpr uint32_t tim8_idx = timer_idxmap[8]; @@ -172,11 +179,13 @@ extern "C" void TIM8_TRG_COM_TIM14_IRQHandler(void) { CLEAR_BIT(tim14->SR, TIM_SR_UIF); TimerDomain::callbacks[tim14_idx](TimerDomain::callback_data[tim14_idx]); } - uint32_t tim14_cc_channel = tim14->SR & CaptureCompareInterruptMask; - while (tim14_cc_channel != 0) { - uint32_t channel = __builtin_ctz(tim14_cc_channel); - TIM_IC_CaptureCallback(tim14_idx, channel - 1); // first bit is UIF - CLEAR_BIT(tim14_cc_channel, 1U << channel); + // Bit 0 = UIF, bits 1 - 4 = CCxIF + for (uint32_t ch = 1; ch < 5; ch++) { + uint32_t flag_mask = 1U << ch; + if (tim14->SR & flag_mask) { + TIM_IC_CaptureCallback(tim14_idx, ch - 1); + CLEAR_BIT(tim14->SR, flag_mask); + } } constexpr uint32_t com_trg_flags = TIM_SR_TIF | TIM_SR_COMIF; From 708bac0cf77e7cb1a2907cc6c000b235e63bc471 Mon Sep 17 00:00:00 2001 From: victhor Date: Thu, 12 Mar 2026 23:19:21 +0100 Subject: [PATCH 19/44] Add code that was in main.cpp to st-lib --- Inc/HALAL/Services/InputCapture/InputCapture.hpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Inc/HALAL/Services/InputCapture/InputCapture.hpp b/Inc/HALAL/Services/InputCapture/InputCapture.hpp index 37859a567..901ca343e 100644 --- a/Inc/HALAL/Services/InputCapture/InputCapture.hpp +++ b/Inc/HALAL/Services/InputCapture/InputCapture.hpp @@ -68,7 +68,12 @@ class InputCapture { void turn_on(void) { if (is_on) return; - + + /* STMicroelectronics' recommendation: Avoid undefined behaviour due to + * first interrupt being falling instead of rising by clearing CNT and SR */ + timer->instance->tim->CNT = 0; + timer->instance->tim->SR = 0; + // HAL_TIM_IC_Start_IT(instance.peripheral->handle, instance.channel_rising) { volatile HAL_TIM_ChannelStateTypeDef* ch_state = From 0a5dd07f585158a87aeb4d081482916ff082b56d Mon Sep 17 00:00:00 2001 From: victhor Date: Thu, 12 Mar 2026 23:21:46 +0100 Subject: [PATCH 20/44] Add trash value check --- Inc/HALAL/Services/InputCapture/InputCapture.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Inc/HALAL/Services/InputCapture/InputCapture.hpp b/Inc/HALAL/Services/InputCapture/InputCapture.hpp index 901ca343e..fdf5976c4 100644 --- a/Inc/HALAL/Services/InputCapture/InputCapture.hpp +++ b/Inc/HALAL/Services/InputCapture/InputCapture.hpp @@ -178,7 +178,8 @@ class InputCapture { } float get_duty_cycle(void) { - return info->duty_cycle; + // often you get a trash value in duty_cycle but the frequency is 0 so it's easily identifiable + return (info->frequency == 0) ? 0.0f : info->duty_cycle; } }; From 94fdf94a164f4177627b40bb0715a466902e0e22 Mon Sep 17 00:00:00 2001 From: victhor Date: Fri, 13 Mar 2026 22:00:06 +0100 Subject: [PATCH 21/44] Resolve possibly uninitialized variable --- Inc/HALAL/Models/TimerDomain/TimerDomain.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp b/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp index 7590011bd..dc08417ae 100644 --- a/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp +++ b/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp @@ -813,7 +813,7 @@ extern TIM_HandleTypeDef htim24; } static inline uint32_t get_timer_frequency(TIM_TypeDef* tim) { - uint32_t result; + uint32_t result = 0; if ((tim == TIM2) || (tim == TIM3) || (tim == TIM4) || (tim == TIM5) || (tim == TIM6) || (tim == TIM7) || (tim == TIM12) || (tim == TIM13) || (tim == TIM14)) From 283c374bf60bac656e737544174fe2de53f81d94 Mon Sep 17 00:00:00 2001 From: victhor Date: Fri, 13 Mar 2026 22:08:55 +0100 Subject: [PATCH 22/44] Fix tests --- Inc/MockedDrivers/stm32h7xx_hal_mock.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Inc/MockedDrivers/stm32h7xx_hal_mock.h b/Inc/MockedDrivers/stm32h7xx_hal_mock.h index 8ae94e74f..be1c68e33 100644 --- a/Inc/MockedDrivers/stm32h7xx_hal_mock.h +++ b/Inc/MockedDrivers/stm32h7xx_hal_mock.h @@ -647,6 +647,13 @@ typedef struct { uint32_t OCNIdleState; } TIM_OC_InitTypeDef; +typedef struct { + uint32_t ICPolarity; + uint32_t ICSelection; + uint32_t ICPrescaler; + uint32_t ICFilter; +} TIM_IC_InitTypeDef; + typedef struct { uint32_t EncoderMode; uint32_t IC1Polarity; From 1e4abdbaf4bd0c2cfe1e3cb49b9a6aa8c36a178b Mon Sep 17 00:00:00 2001 From: victhor Date: Fri, 13 Mar 2026 22:12:45 +0100 Subject: [PATCH 23/44] fix tests (part 2) --- Inc/MockedDrivers/stm32h7xx_hal_mock.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Inc/MockedDrivers/stm32h7xx_hal_mock.h b/Inc/MockedDrivers/stm32h7xx_hal_mock.h index be1c68e33..1b8f50a38 100644 --- a/Inc/MockedDrivers/stm32h7xx_hal_mock.h +++ b/Inc/MockedDrivers/stm32h7xx_hal_mock.h @@ -790,6 +790,10 @@ typedef struct { (TIM_CCER_CC1E | TIM_CCER_CC2E | TIM_CCER_CC3E | TIM_CCER_CC4E | TIM_CCER_CC5E | TIM_CCER_CC6E) #define TIM_CCER_CCxNE_MASK (TIM_CCER_CC1NE | TIM_CCER_CC2NE | TIM_CCER_CC3NE) +#define TIM_INPUTCHANNELPOLARITY_RISING 0x00000000U +#define TIM_INPUTCHANNELPOLARITY_FALLING TIM_CCER_CC1P +#define TIM_INPUTCHANNELPOLARITY_BOTHEDGE (TIM_CCER_CC1P | TIM_CCER_CC1NP) + #define TIM_BDTR_MOE (1U << 15) #define LL_TIM_DIER_UIE TIM_DIER_UIE From 546a0061c846f5a4ad4dbfd989d9b31179fbccdb Mon Sep 17 00:00:00 2001 From: victhor Date: Fri, 13 Mar 2026 22:17:18 +0100 Subject: [PATCH 24/44] fix tests (part 3) --- Inc/MockedDrivers/stm32h7xx_hal_mock.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Inc/MockedDrivers/stm32h7xx_hal_mock.h b/Inc/MockedDrivers/stm32h7xx_hal_mock.h index 1b8f50a38..13ec29d10 100644 --- a/Inc/MockedDrivers/stm32h7xx_hal_mock.h +++ b/Inc/MockedDrivers/stm32h7xx_hal_mock.h @@ -794,6 +794,10 @@ typedef struct { #define TIM_INPUTCHANNELPOLARITY_FALLING TIM_CCER_CC1P #define TIM_INPUTCHANNELPOLARITY_BOTHEDGE (TIM_CCER_CC1P | TIM_CCER_CC1NP) +#define TIM_ICSELECTION_DIRECTTI TIM_CCMR1_CC1S_0 +#define TIM_ICSELECTION_INDIRECTTI TIM_CCMR1_CC1S_1 +#define TIM_ICSELECTION_TRC TIM_CCMR1_CC1S + #define TIM_BDTR_MOE (1U << 15) #define LL_TIM_DIER_UIE TIM_DIER_UIE From c234d88b52a8865dbaab963b71e29e852b194034 Mon Sep 17 00:00:00 2001 From: victhor Date: Fri, 13 Mar 2026 22:21:48 +0100 Subject: [PATCH 25/44] remove other definition of TIM_ICSELECTION_DIRECTTI --- Inc/MockedDrivers/stm32h7xx_hal_mock.h | 1 - 1 file changed, 1 deletion(-) diff --git a/Inc/MockedDrivers/stm32h7xx_hal_mock.h b/Inc/MockedDrivers/stm32h7xx_hal_mock.h index 13ec29d10..a6a5c4349 100644 --- a/Inc/MockedDrivers/stm32h7xx_hal_mock.h +++ b/Inc/MockedDrivers/stm32h7xx_hal_mock.h @@ -703,7 +703,6 @@ typedef struct { #define TIM_ENCODERMODE_TI12 0U #define TIM_ICPOLARITY_RISING 0U -#define TIM_ICSELECTION_DIRECTTI 0U #define TIM_ICPSC_DIV1 0U #define TIM_TRGO_RESET 0U From a0658a5eb2e8410ade13abea99799964d10b0868 Mon Sep 17 00:00:00 2001 From: victhor Date: Fri, 13 Mar 2026 22:25:07 +0100 Subject: [PATCH 26/44] Add more defines --- Inc/MockedDrivers/stm32h7xx_hal_mock.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Inc/MockedDrivers/stm32h7xx_hal_mock.h b/Inc/MockedDrivers/stm32h7xx_hal_mock.h index a6a5c4349..edc26e753 100644 --- a/Inc/MockedDrivers/stm32h7xx_hal_mock.h +++ b/Inc/MockedDrivers/stm32h7xx_hal_mock.h @@ -755,6 +755,9 @@ typedef struct { #define TIM_CCMR1_OC2PE (1U << 11) #define TIM_CCMR1_OC2M (7U << 12) +#define TIM_CCMR1_CC1S_0 (0x1UL << TIM_CCMR1_CC1S_Pos) +#define TIM_CCMR1_CC1S_1 (0x2UL << TIM_CCMR1_CC1S_Pos) + #define TIM_CCMR2_CC3S (3U << 0) #define TIM_CCMR2_OC3PE (1U << 3) #define TIM_CCMR2_OC3M (7U << 4) From aa055db4c5bec2a7de9e05c730a23e806f7740bf Mon Sep 17 00:00:00 2001 From: victhor Date: Fri, 13 Mar 2026 22:27:12 +0100 Subject: [PATCH 27/44] ... --- Inc/MockedDrivers/stm32h7xx_hal_mock.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Inc/MockedDrivers/stm32h7xx_hal_mock.h b/Inc/MockedDrivers/stm32h7xx_hal_mock.h index edc26e753..9cd8e75be 100644 --- a/Inc/MockedDrivers/stm32h7xx_hal_mock.h +++ b/Inc/MockedDrivers/stm32h7xx_hal_mock.h @@ -755,8 +755,8 @@ typedef struct { #define TIM_CCMR1_OC2PE (1U << 11) #define TIM_CCMR1_OC2M (7U << 12) -#define TIM_CCMR1_CC1S_0 (0x1UL << TIM_CCMR1_CC1S_Pos) -#define TIM_CCMR1_CC1S_1 (0x2UL << TIM_CCMR1_CC1S_Pos) +#define TIM_CCMR1_CC1S_0 (0x1UL << 0) +#define TIM_CCMR1_CC1S_1 (0x2UL << 0) #define TIM_CCMR2_CC3S (3U << 0) #define TIM_CCMR2_OC3PE (1U << 3) From cf86a8d536f2c46260893f79c816d22be9eb8354 Mon Sep 17 00:00:00 2001 From: victhor Date: Fri, 13 Mar 2026 22:29:35 +0100 Subject: [PATCH 28/44] etc --- Inc/MockedDrivers/stm32h7xx_hal_mock.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Inc/MockedDrivers/stm32h7xx_hal_mock.h b/Inc/MockedDrivers/stm32h7xx_hal_mock.h index 9cd8e75be..419c770f6 100644 --- a/Inc/MockedDrivers/stm32h7xx_hal_mock.h +++ b/Inc/MockedDrivers/stm32h7xx_hal_mock.h @@ -800,6 +800,11 @@ typedef struct { #define TIM_ICSELECTION_INDIRECTTI TIM_CCMR1_CC1S_1 #define TIM_ICSELECTION_TRC TIM_CCMR1_CC1S +#define TIM_IT_CC1 TIM_DIER_CC1IE +#define TIM_IT_CC2 TIM_DIER_CC2IE +#define TIM_IT_CC3 TIM_DIER_CC3IE +#define TIM_IT_CC4 TIM_DIER_CC4IE + #define TIM_BDTR_MOE (1U << 15) #define LL_TIM_DIER_UIE TIM_DIER_UIE From c077444e8e6209dc72a643ae93cafed17716755e Mon Sep 17 00:00:00 2001 From: victhor Date: Fri, 13 Mar 2026 22:36:52 +0100 Subject: [PATCH 29/44] Add more defines... --- Inc/MockedDrivers/stm32h7xx_hal_mock.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Inc/MockedDrivers/stm32h7xx_hal_mock.h b/Inc/MockedDrivers/stm32h7xx_hal_mock.h index 419c770f6..1723ba65c 100644 --- a/Inc/MockedDrivers/stm32h7xx_hal_mock.h +++ b/Inc/MockedDrivers/stm32h7xx_hal_mock.h @@ -742,7 +742,12 @@ typedef struct { #define TIM_SMCR_ECE (1U << 14) #define TIM_DIER_UIE (1U << 0) +#define TIM_DIER_CC1IE (1UL << 1) +#define TIM_DIER_CC2IE (1UL << 2) +#define TIM_DIER_CC3IE (1UL << 3) +#define TIM_DIER_CC4IE (1UL << 4) #define TIM_DIER_BIE (1U << 7) + #define TIM_SR_UIF (1U << 0) #define TIM_SR_COMIF (1U << 5) #define TIM_SR_TIF (1U << 6) From f38e02e191959321e4fc0dc6622ff27000df0203 Mon Sep 17 00:00:00 2001 From: victhor Date: Fri, 13 Mar 2026 22:40:02 +0100 Subject: [PATCH 30/44] etc... --- Inc/MockedDrivers/stm32h7xx_hal_mock.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Inc/MockedDrivers/stm32h7xx_hal_mock.h b/Inc/MockedDrivers/stm32h7xx_hal_mock.h index 1723ba65c..fa020100e 100644 --- a/Inc/MockedDrivers/stm32h7xx_hal_mock.h +++ b/Inc/MockedDrivers/stm32h7xx_hal_mock.h @@ -754,7 +754,9 @@ typedef struct { #define TIM_SR_BIF (1U << 7) #define TIM_CCMR1_CC1S (3U << 0) +#define TIM_CCMR1_IC1PSC (3U << 2) #define TIM_CCMR1_OC1PE (1U << 3) +#define TIM_CCMR1_IC1F (0xFUL << 4) #define TIM_CCMR1_OC1M (7U << 4) #define TIM_CCMR1_CC2S (3U << 8) #define TIM_CCMR1_OC2PE (1U << 11) From 9825c94269f3b99606a6d2e4ee6ec6c0a4ff41fd Mon Sep 17 00:00:00 2001 From: victhor Date: Fri, 13 Mar 2026 22:43:41 +0100 Subject: [PATCH 31/44] ...etc --- Inc/MockedDrivers/stm32h7xx_hal_mock.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Inc/MockedDrivers/stm32h7xx_hal_mock.h b/Inc/MockedDrivers/stm32h7xx_hal_mock.h index fa020100e..3e015b191 100644 --- a/Inc/MockedDrivers/stm32h7xx_hal_mock.h +++ b/Inc/MockedDrivers/stm32h7xx_hal_mock.h @@ -759,18 +759,23 @@ typedef struct { #define TIM_CCMR1_IC1F (0xFUL << 4) #define TIM_CCMR1_OC1M (7U << 4) #define TIM_CCMR1_CC2S (3U << 8) +#define TIM_CCMR1_IC2PSC (3U << 10) #define TIM_CCMR1_OC2PE (1U << 11) #define TIM_CCMR1_OC2M (7U << 12) +#define TIM_CCMR1_IC2F (0xFUL << 12) #define TIM_CCMR1_CC1S_0 (0x1UL << 0) #define TIM_CCMR1_CC1S_1 (0x2UL << 0) #define TIM_CCMR2_CC3S (3U << 0) +#define TIM_CCMR2_IC3PSC (3U << 2) #define TIM_CCMR2_OC3PE (1U << 3) #define TIM_CCMR2_OC3M (7U << 4) +#define TIM_CCMR2_IC3F (0xFUL << 4) #define TIM_CCMR2_CC4S (3U << 8) #define TIM_CCMR2_OC4PE (1U << 11) #define TIM_CCMR2_OC4M (7U << 12) +#define TIM_CCMR2_IC4F (0xFUL << 12) #define TIM_CCMR3_OC5PE (1U << 3) #define TIM_CCMR3_OC5M (7U << 4) From e6cf69f4547b91fe75bb8a62ea5ef5a71428c2df Mon Sep 17 00:00:00 2001 From: victhor Date: Fri, 13 Mar 2026 22:51:01 +0100 Subject: [PATCH 32/44] final? please --- Inc/MockedDrivers/stm32h7xx_hal_mock.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Inc/MockedDrivers/stm32h7xx_hal_mock.h b/Inc/MockedDrivers/stm32h7xx_hal_mock.h index 3e015b191..7e1cf6f25 100644 --- a/Inc/MockedDrivers/stm32h7xx_hal_mock.h +++ b/Inc/MockedDrivers/stm32h7xx_hal_mock.h @@ -773,6 +773,7 @@ typedef struct { #define TIM_CCMR2_OC3M (7U << 4) #define TIM_CCMR2_IC3F (0xFUL << 4) #define TIM_CCMR2_CC4S (3U << 8) +#define TIM_CCMR2_IC4PSC (3U << 10) #define TIM_CCMR2_OC4PE (1U << 11) #define TIM_CCMR2_OC4M (7U << 12) #define TIM_CCMR2_IC4F (0xFUL << 12) @@ -796,6 +797,7 @@ typedef struct { #define TIM_CCER_CC3NP (1U << 11) #define TIM_CCER_CC4E (1U << 12) #define TIM_CCER_CC4P (1U << 13) +#define TIM_CCER_CC4NP (1U << 15) #define TIM_CCER_CC5E (1U << 16) #define TIM_CCER_CC5P (1U << 17) #define TIM_CCER_CC6E (1U << 20) From 97decbf0d44732ebd2c817190e845fb45348acd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20S=C3=A1ez?= Date: Fri, 13 Mar 2026 23:00:34 +0100 Subject: [PATCH 33/44] applied formatter --- Inc/HALAL/Models/TimerDomain/TimerDomain.hpp | 1546 ++++++++--------- Inc/HALAL/Services/Encoder/Encoder.hpp | 2 +- .../Services/InputCapture/InputCapture.hpp | 74 +- Inc/HALAL/Services/PWM/DualPWM.hpp | 12 +- Inc/HALAL/Services/PWM/PWM.hpp | 6 +- Inc/HALAL/Services/Time/TimerWrapper.hpp | 102 +- Src/HALAL/Models/TimerDomain/TimerDomain.cpp | 81 +- 7 files changed, 917 insertions(+), 906 deletions(-) diff --git a/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp b/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp index dc08417ae..04d707472 100644 --- a/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp +++ b/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp @@ -74,372 +74,372 @@ extern TIM_HandleTypeDef htim24; #define glue(a, b) glue_(a, b) #endif // !defined(glue) - /* Tim1 & Tim8 are advanced-control timers - * their ARR & prescaler are 16bit - * they have up to 6 independent channels for: - * - input capture (not channel 5 or 6) - * - output capture - * - PWM generation - * - One-pulse mode output - */ - - /* Timers {TIM2, TIM5, TIM23, TIM24} are the only 32-bit counter resolution timers, the rest are - 16-bit */ - /* Timers 2, 3, 4, 5, 23, 24 are general-purpose timers - * Timers 12, 13, 14 are also general-purpose timers (but separate in the ref manual) - * Timers 15, 16, 17 are also general purpose timers (but separate in the ref manual) - */ - - /* Tim6 & Tim7 are basic timers. Features: - - 16-bit ARR upcounter - - 16-bit PSC - - Synchronization circuit to trigger the DAC - - Interrupt/DMA generation on the update event - */ - - /* advanced timer features: - - 16-bit ARR up/down counter - - 16-bit PSC - - Up to 6 independent channels for: - · Input capture (all channels but 5 and 6) - · Output compare - · PWM generation (Edge and Center aligned mode) - · One-pulse mode output - - Complementary outputs with programmable dead-time - - Synchronization circuit to control the timer with - external signals and to interconnect several timers together. - - Repetition counter to update the timer registers only after - a given number of cycles of the counter. - - 2 break inputs to put the timer’s output signals in a safe user selectable configuration. - - Interrupt/DMA generation on the following events: - · Update: counter overflow/underflow, counter initialization (by software or - internal/external trigger) · Trigger event (counter start, stop, initialization or count by - internal/external trigger) · Input capture · Output compare - - Supports incremental (quadrature) encoder and Hall-sensor circuitry for positioning purposes - - Trigger input for external clock or cycle-by-cycle current management - */ - namespace ST_LIB { - extern void compile_error(const char* msg); - - /* The number corresponds with the timer nº */ - enum TimerRequest : uint8_t { - AnyGeneralPurpose = 0, - Any32bit = 0xFF, - - Advanced_1 = 1, - Advanced_8 = 8, - - GeneralPurpose32bit_2 = 2, - GeneralPurpose32bit_5 = 5, - GeneralPurpose32bit_23 = 23, - GeneralPurpose32bit_24 = 24, - - GeneralPurpose_3 = 3, - GeneralPurpose_4 = 4, - - SlaveTimer_12 = 12, - SlaveTimer_13 = 13, - SlaveTimer_14 = 14, - - GeneralPurpose_15 = 15, - GeneralPurpose_16 = 16, - GeneralPurpose_17 = 17, - - Basic_6 = 6, - Basic_7 = 7, - }; +/* Tim1 & Tim8 are advanced-control timers + * their ARR & prescaler are 16bit + * they have up to 6 independent channels for: + * - input capture (not channel 5 or 6) + * - output capture + * - PWM generation + * - One-pulse mode output + */ - // Alternate functions for timers - enum class TimerAF { - None, - PWM, - Encoder, - InputCapture, - BreakInput, - BreakInputCompare, - }; +/* Timers {TIM2, TIM5, TIM23, TIM24} are the only 32-bit counter resolution timers, the rest are + 16-bit */ +/* Timers 2, 3, 4, 5, 23, 24 are general-purpose timers + * Timers 12, 13, 14 are also general-purpose timers (but separate in the ref manual) + * Timers 15, 16, 17 are also general purpose timers (but separate in the ref manual) + */ - enum class TimerChannel : uint8_t { - CHANNEL_1 = 1, - CHANNEL_2 = 2, - CHANNEL_3 = 3, - CHANNEL_4 = 4, - CHANNEL_5 = 5, - CHANNEL_6 = 6, - - CHANNEL_NEGATED_FLAG = 8, - - CHANNEL_1_NEGATED = CHANNEL_1 | CHANNEL_NEGATED_FLAG, - CHANNEL_2_NEGATED = CHANNEL_2 | CHANNEL_NEGATED_FLAG, - CHANNEL_3_NEGATED = CHANNEL_3 | CHANNEL_NEGATED_FLAG, - // STM32h723xx does not have a negated channel 4, but other stm microcontrollers do - CHANNEL_4_NEGATED = CHANNEL_4 | CHANNEL_NEGATED_FLAG, - }; +/* Tim6 & Tim7 are basic timers. Features: + - 16-bit ARR upcounter + - 16-bit PSC + - Synchronization circuit to trigger the DAC + - Interrupt/DMA generation on the update event +*/ - struct TimerPin { - ST_LIB::TimerAF af; - ST_LIB::GPIODomain::Pin pin; - ST_LIB::TimerChannel channel; - }; +/* advanced timer features: + - 16-bit ARR up/down counter + - 16-bit PSC + - Up to 6 independent channels for: + · Input capture (all channels but 5 and 6) + · Output compare + · PWM generation (Edge and Center aligned mode) + · One-pulse mode output + - Complementary outputs with programmable dead-time + - Synchronization circuit to control the timer with + external signals and to interconnect several timers together. + - Repetition counter to update the timer registers only after + a given number of cycles of the counter. + - 2 break inputs to put the timer’s output signals in a safe user selectable configuration. + - Interrupt/DMA generation on the following events: + · Update: counter overflow/underflow, counter initialization (by software or + internal/external trigger) · Trigger event (counter start, stop, initialization or count by + internal/external trigger) · Input capture · Output compare + - Supports incremental (quadrature) encoder and Hall-sensor circuitry for positioning purposes + - Trigger input for external clock or cycle-by-cycle current management +*/ +namespace ST_LIB { +extern void compile_error(const char* msg); + +/* The number corresponds with the timer nº */ +enum TimerRequest : uint8_t { + AnyGeneralPurpose = 0, + Any32bit = 0xFF, + + Advanced_1 = 1, + Advanced_8 = 8, + + GeneralPurpose32bit_2 = 2, + GeneralPurpose32bit_5 = 5, + GeneralPurpose32bit_23 = 23, + GeneralPurpose32bit_24 = 24, + + GeneralPurpose_3 = 3, + GeneralPurpose_4 = 4, + + SlaveTimer_12 = 12, + SlaveTimer_13 = 13, + SlaveTimer_14 = 14, + + GeneralPurpose_15 = 15, + GeneralPurpose_16 = 16, + GeneralPurpose_17 = 17, + + Basic_6 = 6, + Basic_7 = 7, +}; + +// Alternate functions for timers +enum class TimerAF { + None, + PWM, + Encoder, + InputCapture, + BreakInput, + BreakInputCompare, +}; + +enum class TimerChannel : uint8_t { + CHANNEL_1 = 1, + CHANNEL_2 = 2, + CHANNEL_3 = 3, + CHANNEL_4 = 4, + CHANNEL_5 = 5, + CHANNEL_6 = 6, + + CHANNEL_NEGATED_FLAG = 8, + + CHANNEL_1_NEGATED = CHANNEL_1 | CHANNEL_NEGATED_FLAG, + CHANNEL_2_NEGATED = CHANNEL_2 | CHANNEL_NEGATED_FLAG, + CHANNEL_3_NEGATED = CHANNEL_3 | CHANNEL_NEGATED_FLAG, + // STM32h723xx does not have a negated channel 4, but other stm microcontrollers do + CHANNEL_4_NEGATED = CHANNEL_4 | CHANNEL_NEGATED_FLAG, +}; + +struct TimerPin { + ST_LIB::TimerAF af; + ST_LIB::GPIODomain::Pin pin; + ST_LIB::TimerChannel channel; +}; #ifndef DEFAULT_PWM_FREQUENCY_MODE #define DEFAULT_PWM_FREQUENCY_MODE ST_LIB::PWM_Frequency_Mode::PRECISION #endif - enum class PWM_Frequency_Mode { - PRECISION, - SPEED, +enum class PWM_Frequency_Mode { + PRECISION, + SPEED, +}; + +constexpr std::array create_timer_idxmap() { + std::array result{}; + + // invalid timers that don't exist + result[0] = -1; + result[9] = -1; + result[10] = -1; + result[11] = -1; + result[18] = -1; + result[19] = -1; + result[20] = -1; + result[21] = -1; + result[22] = -1; + + // general-purpose timers + result[2] = 0; + result[3] = 1; + result[4] = 2; + result[5] = 3; + result[23] = 4; + result[24] = 5; + + // more general-purpose timers + result[12] = 6; + result[13] = 7; + result[14] = 8; + + // more general-purpose timers + result[15] = 9; + result[16] = 10; + result[17] = 11; + + // basic timers + result[6] = 12; + result[7] = 13; + + // advanced control timers + result[1] = 14; + result[8] = 15; + + return result; +} + +static constexpr std::array timer_idxmap = create_timer_idxmap(); + +struct TimerDomain { + static constexpr std::size_t max_instances = 16; + static constexpr std::size_t input_capture_channels = 4; + + struct InputCaptureInfo { + uint8_t channel_rising; + uint8_t channel_falling; + + uint32_t value_rising; + uint32_t value_falling; + uint32_t period; + + float duty_cycle; + uint32_t frequency; + }; + /* 2x as big as necessary but this makes indexing easier & faster */ + static InputCaptureInfo* input_capture_info[max_instances][input_capture_channels]; + static InputCaptureInfo input_capture_info_backing[max_instances][input_capture_channels]; + + struct Entry { + std::array name; /* max length = 7 */ + TimerRequest request; + uint8_t pin_count; + std::array pins; /* this won't be read in Timer constructor */ }; - constexpr std::array create_timer_idxmap() { - std::array result{}; - - // invalid timers that don't exist - result[0] = -1; - result[9] = -1; - result[10] = -1; - result[11] = -1; - result[18] = -1; - result[19] = -1; - result[20] = -1; - result[21] = -1; - result[22] = -1; - - // general-purpose timers - result[2] = 0; - result[3] = 1; - result[4] = 2; - result[5] = 3; - result[23] = 4; - result[24] = 5; - - // more general-purpose timers - result[12] = 6; - result[13] = 7; - result[14] = 8; - - // more general-purpose timers - result[15] = 9; - result[16] = 10; - result[17] = 11; + struct Config { + uint8_t timer_idx; + }; + + static constexpr TIM_HandleTypeDef* hal_handles[16] = { // general purpose timers + &htim2, + &htim3, + &htim4, + &htim5, + &htim23, + &htim24, + &htim12, + &htim13, + &htim14, + &htim15, + &htim16, + &htim17, // basic timers - result[6] = 12; - result[7] = 13; + &htim6, + &htim7, // advanced control timers - result[1] = 14; - result[8] = 15; - - return result; - } - - static constexpr std::array timer_idxmap = create_timer_idxmap(); - - struct TimerDomain { - static constexpr std::size_t max_instances = 16; - static constexpr std::size_t input_capture_channels = 4; - - struct InputCaptureInfo { - uint8_t channel_rising; - uint8_t channel_falling; + &htim1, + &htim8 + }; - uint32_t value_rising; - uint32_t value_falling; - uint32_t period; +#ifdef SIM_ON + static TIM_TypeDef* cmsis_timers[16]; +#else + static constexpr TIM_TypeDef* cmsis_timers[16] = { // general purpose timers + TIM2, + TIM3, + TIM4, + TIM5, + TIM23, + TIM24, + TIM12, + TIM13, + TIM14, + TIM15, + TIM16, + TIM17, - float duty_cycle; - uint32_t frequency; - }; - /* 2x as big as necessary but this makes indexing easier & faster */ - static InputCaptureInfo* input_capture_info[max_instances][input_capture_channels]; - static InputCaptureInfo input_capture_info_backing[max_instances][input_capture_channels]; - - struct Entry { - std::array name; /* max length = 7 */ - TimerRequest request; - uint8_t pin_count; - std::array pins; /* this won't be read in Timer constructor */ - }; + // basic timers + TIM6, + TIM7, - struct Config { - uint8_t timer_idx; - }; + // advanced control timers + TIM1, + TIM8 + }; +#endif - static constexpr TIM_HandleTypeDef* hal_handles[16] = {// general purpose timers - &htim2, - &htim3, - &htim4, - &htim5, - &htim23, - &htim24, - &htim12, - &htim13, - &htim14, - &htim15, - &htim16, - &htim17, - - // basic timers - &htim6, - &htim7, - - // advanced control timers - &htim1, - &htim8 - }; + static constexpr IRQn_Type timer_irqn[] = { + // general purpose timers 1 + TIM2_IRQn, + TIM3_IRQn, + TIM4_IRQn, + TIM5_IRQn, + TIM23_IRQn, + TIM24_IRQn, + // slave timers + TIM8_BRK_TIM12_IRQn, + TIM8_UP_TIM13_IRQn, + TIM8_TRG_COM_TIM14_IRQn, + // general purpose timers 2 + TIM15_IRQn, + TIM16_IRQn, + TIM17_IRQn, -#ifdef SIM_ON - static TIM_TypeDef* cmsis_timers[16]; -#else - static constexpr TIM_TypeDef* cmsis_timers[16] = {// general purpose timers - TIM2, - TIM3, - TIM4, - TIM5, - TIM23, - TIM24, - TIM12, - TIM13, - TIM14, - TIM15, - TIM16, - TIM17, - - // basic timers - TIM6, - TIM7, - - // advanced control timers - TIM1, - TIM8 - }; -#endif + // basic timers + TIM6_DAC_IRQn, /* TIM6 global and DAC1&2 underrun error interrupts */ + TIM7_IRQn, - static constexpr IRQn_Type timer_irqn[] = { - // general purpose timers 1 - TIM2_IRQn, - TIM3_IRQn, - TIM4_IRQn, - TIM5_IRQn, - TIM23_IRQn, - TIM24_IRQn, - // slave timers - TIM8_BRK_TIM12_IRQn, - TIM8_UP_TIM13_IRQn, - TIM8_TRG_COM_TIM14_IRQn, - // general purpose timers 2 - TIM15_IRQn, - TIM16_IRQn, - TIM17_IRQn, - - // basic timers - TIM6_DAC_IRQn, /* TIM6 global and DAC1&2 underrun error interrupts */ - TIM7_IRQn, - - TIM1_UP_IRQn, - TIM8_UP_TIM13_IRQn - }; + TIM1_UP_IRQn, + TIM8_UP_TIM13_IRQn + }; - static inline void rcc_enable_timer(TIM_TypeDef* tim) { + static inline void rcc_enable_timer(TIM_TypeDef* tim) { #define X(n, b) \ else if (tim == TIM##n) { \ SET_BIT(RCC->b, RCC_##b##_TIM##n##EN); \ } - if (false) { - } - TimerXList else { - ErrorHandler("Invalid timer given to rcc_enable_timer"); - } -#undef X + if (false) { } + TimerXList else { + ErrorHandler("Invalid timer given to rcc_enable_timer"); + } +#undef X + } - static constexpr std::array EMPTY_TIMER_NAME = {0, 0, 0, 0, 0, 0, 0, 0}; - - struct Timer { - using domain = TimerDomain; - // GPIODomain::GPIO gpios[4]; - Entry e; - GPIODomain::GPIO gpio0; - GPIODomain::GPIO gpio1; - GPIODomain::GPIO gpio2; - GPIODomain::GPIO gpio3; - GPIODomain::GPIO gpio4; - GPIODomain::GPIO gpio5; - GPIODomain::GPIO gpio6; - - static consteval GPIODomain::AlternateFunction - get_gpio_af(ST_LIB::TimerRequest req, ST_LIB::TimerPin pin); - - static consteval GPIODomain::OperationMode get_operation_mode(ST_LIB::TimerAF af) { - switch (af) { - case TimerAF::None: - return GPIODomain::OperationMode::INPUT; - - case TimerAF::PWM: - return GPIODomain::OperationMode::ALT_PP; - case TimerAF::Encoder: - return GPIODomain::OperationMode::ALT_PP; - - case TimerAF::InputCapture: - return GPIODomain::OperationMode::ALT_PP; - - // TODO: check what this really needs to be for each - case TimerAF::BreakInput: - return GPIODomain::OperationMode::OUTPUT_OPENDRAIN; - case TimerAF::BreakInputCompare: - return GPIODomain::OperationMode::OUTPUT_OPENDRAIN; - } + static constexpr std::array EMPTY_TIMER_NAME = {0, 0, 0, 0, 0, 0, 0, 0}; + + struct Timer { + using domain = TimerDomain; + // GPIODomain::GPIO gpios[4]; + Entry e; + GPIODomain::GPIO gpio0; + GPIODomain::GPIO gpio1; + GPIODomain::GPIO gpio2; + GPIODomain::GPIO gpio3; + GPIODomain::GPIO gpio4; + GPIODomain::GPIO gpio5; + GPIODomain::GPIO gpio6; + + static consteval GPIODomain::AlternateFunction + get_gpio_af(ST_LIB::TimerRequest req, ST_LIB::TimerPin pin); + + static consteval GPIODomain::OperationMode get_operation_mode(ST_LIB::TimerAF af) { + switch (af) { + case TimerAF::None: + return GPIODomain::OperationMode::INPUT; + + case TimerAF::PWM: + return GPIODomain::OperationMode::ALT_PP; + case TimerAF::Encoder: + return GPIODomain::OperationMode::ALT_PP; + + case TimerAF::InputCapture: + return GPIODomain::OperationMode::ALT_PP; + + // TODO: check what this really needs to be for each + case TimerAF::BreakInput: + return GPIODomain::OperationMode::OUTPUT_OPENDRAIN; + case TimerAF::BreakInputCompare: + return GPIODomain::OperationMode::OUTPUT_OPENDRAIN; } + } - static consteval GPIODomain::Pull get_pull(ST_LIB::TimerAF af) { - switch (af) { - case TimerAF::None: - return GPIODomain::Pull::None; - - case TimerAF::PWM: - return GPIODomain::Pull::None; - case TimerAF::Encoder: - return GPIODomain::Pull::Up; - - case TimerAF::InputCapture: - return GPIODomain::Pull::None; - - // TODO: check what this really needs to be for each - case TimerAF::BreakInput: - return GPIODomain::Pull::None; - case TimerAF::BreakInputCompare: - return GPIODomain::Pull::None; - } + static consteval GPIODomain::Pull get_pull(ST_LIB::TimerAF af) { + switch (af) { + case TimerAF::None: + return GPIODomain::Pull::None; + + case TimerAF::PWM: + return GPIODomain::Pull::None; + case TimerAF::Encoder: + return GPIODomain::Pull::Up; + + case TimerAF::InputCapture: + return GPIODomain::Pull::None; + + // TODO: check what this really needs to be for each + case TimerAF::BreakInput: + return GPIODomain::Pull::None; + case TimerAF::BreakInputCompare: + return GPIODomain::Pull::None; } + } - static consteval GPIODomain::Speed get_speed(ST_LIB::TimerAF af) { - switch (af) { - case TimerAF::None: - return GPIODomain::Speed::Low; - - case TimerAF::PWM: - return GPIODomain::Speed::Low; - case TimerAF::Encoder: - return GPIODomain::Speed::Low; - - case TimerAF::InputCapture: - return GPIODomain::Speed::Low; - - // TODO: check what this really needs to be for each - case TimerAF::BreakInput: - return GPIODomain::Speed::Low; - case TimerAF::BreakInputCompare: - return GPIODomain::Speed::Low; - } + static consteval GPIODomain::Speed get_speed(ST_LIB::TimerAF af) { + switch (af) { + case TimerAF::None: + return GPIODomain::Speed::Low; + + case TimerAF::PWM: + return GPIODomain::Speed::Low; + case TimerAF::Encoder: + return GPIODomain::Speed::Low; + + case TimerAF::InputCapture: + return GPIODomain::Speed::Low; + + // TODO: check what this really needs to be for each + case TimerAF::BreakInput: + return GPIODomain::Speed::Low; + case TimerAF::BreakInputCompare: + return GPIODomain::Speed::Low; } + } - static constexpr ST_LIB::TimerPin empty_pin = { - .af = TimerAF::None, - .pin = ST_LIB::PA0, - .channel = TimerChannel::CHANNEL_1, - }; + static constexpr ST_LIB::TimerPin empty_pin = { + .af = TimerAF::None, + .pin = ST_LIB::PA0, + .channel = TimerChannel::CHANNEL_1, + }; #define GetPinFromIdx(pinargs, idx) \ sizeof...(pinargs) > idx ? (ST_LIB::TimerPin[]){pinargs...}[idx] : empty_pin @@ -457,420 +457,411 @@ extern TIM_HandleTypeDef htim24; (sizeof...(pinargs) > idx ? get_gpio_af(request, (ST_LIB::TimerPin[]){pinargs...}[idx]) \ : GPIODomain::AlternateFunction::NO_AF) - template - consteval Timer( - TimerRequest request = TimerRequest::AnyGeneralPurpose, - std::array name = EMPTY_TIMER_NAME, - T... pinargs - ) - : e(name, - request, - sizeof...(pinargs), - std::array( - {GetPinFromIdx(pinargs, 0), - GetPinFromIdx(pinargs, 1), - GetPinFromIdx(pinargs, 2), - GetPinFromIdx(pinargs, 3), - GetPinFromIdx(pinargs, 4), - GetPinFromIdx(pinargs, 5), - GetPinFromIdx(pinargs, 6)} - )), - gpio0(GetGPIOFromIdx(pinargs, request, 0)), - gpio1(GetGPIOFromIdx(pinargs, request, 1)), - gpio2(GetGPIOFromIdx(pinargs, request, 2)), - gpio3(GetGPIOFromIdx(pinargs, request, 3)), - gpio4(GetGPIOFromIdx(pinargs, request, 4)), - gpio5(GetGPIOFromIdx(pinargs, request, 5)), - gpio6(GetGPIOFromIdx(pinargs, request, 6)) { - static_assert( - (std::is_same_v && ...), - "All template arguments must be of type TimerPin" - ); - if (sizeof...(pinargs) > 7) { - ST_LIB::compile_error("Max 7 pins per timer"); - } + template + consteval Timer( + TimerRequest request = TimerRequest::AnyGeneralPurpose, + std::array name = EMPTY_TIMER_NAME, + T... pinargs + ) + : e(name, + request, + sizeof...(pinargs), + std::array( + {GetPinFromIdx(pinargs, 0), + GetPinFromIdx(pinargs, 1), + GetPinFromIdx(pinargs, 2), + GetPinFromIdx(pinargs, 3), + GetPinFromIdx(pinargs, 4), + GetPinFromIdx(pinargs, 5), + GetPinFromIdx(pinargs, 6)} + )), + gpio0(GetGPIOFromIdx(pinargs, request, 0)), + gpio1(GetGPIOFromIdx(pinargs, request, 1)), + gpio2(GetGPIOFromIdx(pinargs, request, 2)), + gpio3(GetGPIOFromIdx(pinargs, request, 3)), + gpio4(GetGPIOFromIdx(pinargs, request, 4)), + gpio5(GetGPIOFromIdx(pinargs, request, 5)), + gpio6(GetGPIOFromIdx(pinargs, request, 6)) { + static_assert( + (std::is_same_v && ...), + "All template arguments must be of type TimerPin" + ); + if (sizeof...(pinargs) > 7) { + ST_LIB::compile_error("Max 7 pins per timer"); } + } - // anything uninitialized will be 0 - template - consteval Timer(Entry ent, T... pinargs) - : e(ent.name, - ent.request, - sizeof...(pinargs), - std::array( - {GetPinFromIdx(pinargs, 0), - GetPinFromIdx(pinargs, 1), - GetPinFromIdx(pinargs, 2), - GetPinFromIdx(pinargs, 3), - GetPinFromIdx(pinargs, 4), - GetPinFromIdx(pinargs, 5), - GetPinFromIdx(pinargs, 6)} - )), - gpio0(GetGPIOFromIdx(pinargs, ent.request, 0)), - gpio1(GetGPIOFromIdx(pinargs, ent.request, 1)), - gpio2(GetGPIOFromIdx(pinargs, ent.request, 2)), - gpio3(GetGPIOFromIdx(pinargs, ent.request, 3)), - gpio4(GetGPIOFromIdx(pinargs, ent.request, 4)), - gpio5(GetGPIOFromIdx(pinargs, ent.request, 5)), - gpio6(GetGPIOFromIdx(pinargs, ent.request, 6)) { - static_assert( - (std::is_same_v && ...), - "All template arguments must be of type TimerPin" - ); - if (sizeof...(pinargs) > 7) { - ST_LIB::compile_error("Max 7 pins per timer"); - } + // anything uninitialized will be 0 + template + consteval Timer(Entry ent, T... pinargs) + : e(ent.name, + ent.request, + sizeof...(pinargs), + std::array( + {GetPinFromIdx(pinargs, 0), + GetPinFromIdx(pinargs, 1), + GetPinFromIdx(pinargs, 2), + GetPinFromIdx(pinargs, 3), + GetPinFromIdx(pinargs, 4), + GetPinFromIdx(pinargs, 5), + GetPinFromIdx(pinargs, 6)} + )), + gpio0(GetGPIOFromIdx(pinargs, ent.request, 0)), + gpio1(GetGPIOFromIdx(pinargs, ent.request, 1)), + gpio2(GetGPIOFromIdx(pinargs, ent.request, 2)), + gpio3(GetGPIOFromIdx(pinargs, ent.request, 3)), + gpio4(GetGPIOFromIdx(pinargs, ent.request, 4)), + gpio5(GetGPIOFromIdx(pinargs, ent.request, 5)), + gpio6(GetGPIOFromIdx(pinargs, ent.request, 6)) { + static_assert( + (std::is_same_v && ...), + "All template arguments must be of type TimerPin" + ); + if (sizeof...(pinargs) > 7) { + ST_LIB::compile_error("Max 7 pins per timer"); } + } - template consteval void inscribe(Ctx& ctx) const { - if (e.pin_count > 0) { - gpio0.inscribe(ctx); - } - if (e.pin_count > 1) { - gpio1.inscribe(ctx); - } - if (e.pin_count > 2) { - gpio2.inscribe(ctx); - } - if (e.pin_count > 3) { - gpio3.inscribe(ctx); - } - if (e.pin_count > 4) { - gpio4.inscribe(ctx); - } - if (e.pin_count > 5) { - gpio5.inscribe(ctx); - } - if (e.pin_count > 6) { - gpio6.inscribe(ctx); - } - - TimerDomain::Entry local_entry = { - .name = e.name, - .request = e.request, - .pin_count = e.pin_count, - .pins = e.pins, - }; - ctx.template add(local_entry, this); + template consteval void inscribe(Ctx& ctx) const { + if (e.pin_count > 0) { + gpio0.inscribe(ctx); } - }; - - template - static consteval std::array build(std::span requests) { - std::array cfgs{}; - uint16_t cfg_idx = 0; - bool used_timers[25] = {0}; - - if (requests.size() > max_instances) { - ST_LIB::compile_error("too many Timer requests, there are only 16 timers"); + if (e.pin_count > 1) { + gpio1.inscribe(ctx); + } + if (e.pin_count > 2) { + gpio2.inscribe(ctx); + } + if (e.pin_count > 3) { + gpio3.inscribe(ctx); + } + if (e.pin_count > 4) { + gpio4.inscribe(ctx); + } + if (e.pin_count > 5) { + gpio5.inscribe(ctx); + } + if (e.pin_count > 6) { + gpio6.inscribe(ctx); } - int remaining_requests[max_instances] = {}; - int count_remaining_requests = (int)requests.size(); - for (int i = 0; i < (int)requests.size(); i++) - remaining_requests[i] = i; + TimerDomain::Entry local_entry = { + .name = e.name, + .request = e.request, + .pin_count = e.pin_count, + .pins = e.pins, + }; + ctx.template add(local_entry, this); + } + }; - for (int i = 0; i < (int)requests.size(); i++) { - if (static_cast(requests[i].request) == SCHEDULER_TIMER_DOMAIN) { - ST_LIB::compile_error("This timer is used by the scheduler"); - } + template + static consteval std::array build(std::span requests) { + std::array cfgs{}; + uint16_t cfg_idx = 0; + bool used_timers[25] = {0}; - if (requests[i].request != TimerRequest::AnyGeneralPurpose && - (requests[i].request < 1 || requests[i].request > 24 || - (requests[i].request > 17 && requests[i].request < 23))) { - ST_LIB::compile_error("Invalid TimerRequest value for timer"); - } + if (requests.size() > max_instances) { + ST_LIB::compile_error("too many Timer requests, there are only 16 timers"); + } - uint32_t used_channels = 0; - for (uint16_t pi = 0; pi < requests[i].pin_count; pi++) { - uint32_t channel_bit = - (1 << static_cast(requests[i].pins[pi].channel)); - if (used_channels & channel_bit) { - ST_LIB::compile_error("Only one pin per channel for each timer"); - } - used_channels |= channel_bit; - } - } + int remaining_requests[max_instances] = {}; + int count_remaining_requests = (int)requests.size(); + for (int i = 0; i < (int)requests.size(); i++) + remaining_requests[i] = i; - // First find any that have requested a specific timer - for (std::size_t i = 0; i < N; i++) { - uint8_t reqint = static_cast(requests[i].request); - if ((requests[i].request != TimerRequest::AnyGeneralPurpose) && - (requests[i].request != TimerRequest::Any32bit)) { - if (used_timers[reqint]) { - ST_LIB::compile_error("Error: Timer already used"); - } - used_timers[reqint] = true; - - Config cfg = { - .timer_idx = timer_idxmap[reqint], - }; - cfgs[cfg_idx++] = cfg; - - // unordered remove (remaining requests is not used here so these are ordered) - count_remaining_requests--; - remaining_requests[i] = remaining_requests[count_remaining_requests]; - } + for (int i = 0; i < (int)requests.size(); i++) { + if (static_cast(requests[i].request) == SCHEDULER_TIMER_DOMAIN) { + ST_LIB::compile_error("This timer is used by the scheduler"); } - // 32 bit timers, very important for scheduler - uint8_t bits32_timers[] = {2, 5, 23, 24}; - uint8_t remaining_32bit_timers[4] = {0}; - uint8_t count_remaining_32bit_timers = 0; - uint8_t count_32bit_requests = 0; - - for (int i = 0; i < (int)ARRAY_LENGTH(bits32_timers); i++) { - if (!used_timers[bits32_timers[i]] && (bits32_timers[i] != SCHEDULER_TIMER_DOMAIN)) - remaining_32bit_timers[count_remaining_32bit_timers++] = bits32_timers[i]; + if (requests[i].request != TimerRequest::AnyGeneralPurpose && + (requests[i].request < 1 || requests[i].request > 24 || + (requests[i].request > 17 && requests[i].request < 23))) { + ST_LIB::compile_error("Invalid TimerRequest value for timer"); } - for (int i = 0; i < count_remaining_requests;) { - const Entry& e = requests[remaining_requests[i]]; - if (e.request == TimerRequest::Any32bit) { - if (count_remaining_32bit_timers <= count_32bit_requests) { - ST_LIB::compile_error( - "No remaining 32 bit timers, there are only 4. Timers {2, 5, 23, 24}" - ); - } - - uint8_t reqint = remaining_32bit_timers[count_32bit_requests]; - Config cfg = { - .timer_idx = timer_idxmap[reqint], - }; - cfgs[cfg_idx++] = cfg; - - // unordered remove - count_remaining_requests--; - remaining_requests[i] = remaining_requests[count_remaining_requests]; - } else { - i++; + uint32_t used_channels = 0; + for (uint16_t pi = 0; pi < requests[i].pin_count; pi++) { + uint32_t channel_bit = (1 << static_cast(requests[i].pins[pi].channel)); + if (used_channels & channel_bit) { + ST_LIB::compile_error("Only one pin per channel for each timer"); } + used_channels |= channel_bit; } + } - // can use any CountingMode (32 bit timers can also but they are higher priority) - uint8_t up_down_updown_timers[] = {3, 4}; - - // 16 bit timers - /* NOTE: timers {TIM12, TIM13, TIM14} are also 16 bit but - * they are used as slave timers to tim8 - */ - uint8_t bits16_timers[] = {15, 16, 17}; - uint8_t remaining_timers[15] = {0}; - uint8_t count_remaining_timers = 0; + // First find any that have requested a specific timer + for (std::size_t i = 0; i < N; i++) { + uint8_t reqint = static_cast(requests[i].request); + if ((requests[i].request != TimerRequest::AnyGeneralPurpose) && + (requests[i].request != TimerRequest::Any32bit)) { + if (used_timers[reqint]) { + ST_LIB::compile_error("Error: Timer already used"); + } + used_timers[reqint] = true; - for (int i = 0; i < (int)ARRAY_LENGTH(bits16_timers); i++) { - if (!used_timers[bits16_timers[i]]) - remaining_timers[count_remaining_timers++] = bits16_timers[i]; - } + Config cfg = { + .timer_idx = timer_idxmap[reqint], + }; + cfgs[cfg_idx++] = cfg; - for (int i = 0; i < (int)ARRAY_LENGTH(up_down_updown_timers); i++) { - if (!used_timers[up_down_updown_timers[i]]) - remaining_timers[count_remaining_timers++] = up_down_updown_timers[i]; + // unordered remove (remaining requests is not used here so these are ordered) + count_remaining_requests--; + remaining_requests[i] = remaining_requests[count_remaining_requests]; } + } - for (int i = 0; i < (int)ARRAY_LENGTH(bits32_timers); i++) { - if (!used_timers[bits32_timers[i]]) - remaining_timers[count_remaining_timers++] = bits32_timers[i]; - } + // 32 bit timers, very important for scheduler + uint8_t bits32_timers[] = {2, 5, 23, 24}; + uint8_t remaining_32bit_timers[4] = {0}; + uint8_t count_remaining_32bit_timers = 0; + uint8_t count_32bit_requests = 0; - if (count_remaining_requests > count_remaining_timers) { - ST_LIB::compile_error("This should not happen"); - } + for (int i = 0; i < (int)ARRAY_LENGTH(bits32_timers); i++) { + if (!used_timers[bits32_timers[i]] && (bits32_timers[i] != SCHEDULER_TIMER_DOMAIN)) + remaining_32bit_timers[count_remaining_32bit_timers++] = bits32_timers[i]; + } - for (int i = 0; i < count_remaining_requests; i++) { - const Entry& e = requests[remaining_requests[i]]; - if (e.request != TimerRequest::AnyGeneralPurpose) { - ST_LIB::compile_error("This only processes TimerRequest::AnyGeneralPurpose"); + for (int i = 0; i < count_remaining_requests;) { + const Entry& e = requests[remaining_requests[i]]; + if (e.request == TimerRequest::Any32bit) { + if (count_remaining_32bit_timers <= count_32bit_requests) { + ST_LIB::compile_error( + "No remaining 32 bit timers, there are only 4. Timers {2, 5, 23, 24}" + ); } - uint8_t reqint = remaining_timers[i]; + + uint8_t reqint = remaining_32bit_timers[count_32bit_requests]; Config cfg = { .timer_idx = timer_idxmap[reqint], }; cfgs[cfg_idx++] = cfg; - } - return cfgs; + // unordered remove + count_remaining_requests--; + remaining_requests[i] = remaining_requests[count_remaining_requests]; + } else { + i++; + } } - // Runtime object - struct Instance { - TIM_TypeDef* tim; - TIM_HandleTypeDef* hal_tim; - uint8_t timer_idx; - }; + // can use any CountingMode (32 bit timers can also but they are higher priority) + uint8_t up_down_updown_timers[] = {3, 4}; - static void (*callbacks[TimerDomain::max_instances])(void*); - static void* callback_data[TimerDomain::max_instances]; - - template struct Init { - static inline std::array instances{}; - - static void TIM_Default_Callback(void* raw) { (void)raw; } - - static void init(std::span cfgs) { - TIM_TypeDef* sched_timer = cmsis_timers[timer_idxmap[SCHEDULER_TIMER_DOMAIN]]; - rcc_enable_timer(sched_timer); - - TimerDomain::callbacks[1] = TIM_Default_Callback; - TimerDomain::callbacks[2] = TIM_Default_Callback; - TimerDomain::callbacks[3] = TIM_Default_Callback; - TimerDomain::callbacks[4] = TIM_Default_Callback; - TimerDomain::callbacks[5] = TIM_Default_Callback; - TimerDomain::callbacks[6] = TIM_Default_Callback; - TimerDomain::callbacks[7] = TIM_Default_Callback; - TimerDomain::callbacks[8] = TIM_Default_Callback; - TimerDomain::callbacks[9] = TIM_Default_Callback; - TimerDomain::callbacks[10] = TIM_Default_Callback; - TimerDomain::callbacks[11] = TIM_Default_Callback; - TimerDomain::callbacks[12] = TIM_Default_Callback; - TimerDomain::callbacks[13] = TIM_Default_Callback; - TimerDomain::callbacks[14] = TIM_Default_Callback; - TimerDomain::callbacks[15] = TIM_Default_Callback; - - for (std::size_t i = 0; i < N; i++) { - const Config& e = cfgs[i]; - - TIM_HandleTypeDef* handle = hal_handles[e.timer_idx]; - TIM_TypeDef* tim = cmsis_timers[e.timer_idx]; - handle->Instance = tim; - handle->Init.Period = 0; - handle->Init.Prescaler = 0; - handle->Init.CounterMode = TIM_COUNTERMODE_UP; - handle->Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; - handle->Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; - handle->Init.RepetitionCounter = 0; - - handle->ChannelState[0] = HAL_TIM_CHANNEL_STATE_READY; - handle->ChannelState[1] = HAL_TIM_CHANNEL_STATE_READY; - handle->ChannelState[2] = HAL_TIM_CHANNEL_STATE_READY; - handle->ChannelState[3] = HAL_TIM_CHANNEL_STATE_READY; - handle->ChannelState[4] = HAL_TIM_CHANNEL_STATE_READY; - handle->ChannelState[5] = HAL_TIM_CHANNEL_STATE_READY; - - handle->ChannelNState[0] = HAL_TIM_CHANNEL_STATE_READY; - handle->ChannelNState[1] = HAL_TIM_CHANNEL_STATE_READY; - handle->ChannelNState[2] = HAL_TIM_CHANNEL_STATE_READY; - handle->ChannelNState[3] = HAL_TIM_CHANNEL_STATE_READY; - handle->DMABurstState = HAL_DMA_BURST_STATE_READY; - handle->Lock = HAL_UNLOCKED; - handle->State = HAL_TIM_STATE_READY; - - rcc_enable_timer(tim); - - Instance* inst = &instances[i]; - inst->tim = tim; - inst->hal_tim = handle; - inst->timer_idx = e.timer_idx; - } - } - }; + // 16 bit timers + /* NOTE: timers {TIM12, TIM13, TIM14} are also 16 bit but + * they are used as slave timers to tim8 + */ + uint8_t bits16_timers[] = {15, 16, 17}; + uint8_t remaining_timers[15] = {0}; + uint8_t count_remaining_timers = 0; - static consteval uint8_t get_channel_state_idx(const ST_LIB::TimerChannel ch) { - switch (ch) { - case TimerChannel::CHANNEL_1: - case TimerChannel::CHANNEL_1_NEGATED: - case TimerChannel::CHANNEL_2: - case TimerChannel::CHANNEL_2_NEGATED: - case TimerChannel::CHANNEL_3: - case TimerChannel::CHANNEL_3_NEGATED: - case TimerChannel::CHANNEL_4: - case TimerChannel::CHANNEL_4_NEGATED: - case TimerChannel::CHANNEL_5: - case TimerChannel::CHANNEL_6: - return (static_cast(ch) & - ~static_cast(TimerChannel::CHANNEL_NEGATED_FLAG)) - - 1; - - default: - ST_LIB::compile_error("unreachable"); - return 0; - } + for (int i = 0; i < (int)ARRAY_LENGTH(bits16_timers); i++) { + if (!used_timers[bits16_timers[i]]) + remaining_timers[count_remaining_timers++] = bits16_timers[i]; } - static consteval uint8_t get_channel_mul4(const ST_LIB::TimerChannel ch) { - switch (ch) { - case TimerChannel::CHANNEL_1: - case TimerChannel::CHANNEL_1_NEGATED: - return 0x00; - case TimerChannel::CHANNEL_2: - case TimerChannel::CHANNEL_2_NEGATED: - return 0x04; - case TimerChannel::CHANNEL_3: - case TimerChannel::CHANNEL_3_NEGATED: - return 0x08; - case TimerChannel::CHANNEL_4: - case TimerChannel::CHANNEL_4_NEGATED: - return 0x0C; - case TimerChannel::CHANNEL_5: - return 0x10; - case TimerChannel::CHANNEL_6: - return 0x14; - - default: - ST_LIB::compile_error("unreachable"); - return 0; - } + for (int i = 0; i < (int)ARRAY_LENGTH(up_down_updown_timers); i++) { + if (!used_timers[up_down_updown_timers[i]]) + remaining_timers[count_remaining_timers++] = up_down_updown_timers[i]; } - static inline uint32_t get_timer_frequency(TIM_TypeDef* tim) { - uint32_t result = 0; - if ((tim == TIM2) || (tim == TIM3) || (tim == TIM4) || (tim == TIM5) || - (tim == TIM6) || (tim == TIM7) || (tim == TIM12) || (tim == TIM13) || - (tim == TIM14)) - { - result = HAL_RCC_GetPCLK1Freq(); - if ((RCC->D2CFGR & RCC_D2CFGR_D2PPRE1) != RCC_HCLK_DIV1) { - result *= 2; - } - } else if ((tim == TIM1) || (tim == TIM8) || (tim == TIM15) || (tim == TIM16) || - (tim == TIM17) || (tim == TIM23) || (tim == TIM24)) - { - result = HAL_RCC_GetPCLK2Freq(); - if ((RCC->D2CFGR & RCC_D2CFGR_D2PPRE2) != RCC_HCLK_DIV1) { - result *= 2; - } - } else { - ErrorHandler("Invalid timer ptr"); + for (int i = 0; i < (int)ARRAY_LENGTH(bits32_timers); i++) { + if (!used_timers[bits32_timers[i]]) + remaining_timers[count_remaining_timers++] = bits32_timers[i]; + } + + if (count_remaining_requests > count_remaining_timers) { + ST_LIB::compile_error("This should not happen"); + } + + for (int i = 0; i < count_remaining_requests; i++) { + const Entry& e = requests[remaining_requests[i]]; + if (e.request != TimerRequest::AnyGeneralPurpose) { + ST_LIB::compile_error("This only processes TimerRequest::AnyGeneralPurpose"); } - - return result; + uint8_t reqint = remaining_timers[i]; + Config cfg = { + .timer_idx = timer_idxmap[reqint], + }; + cfgs[cfg_idx++] = cfg; } + + return cfgs; + } + + // Runtime object + struct Instance { + TIM_TypeDef* tim; + TIM_HandleTypeDef* hal_tim; + uint8_t timer_idx; }; + static void (*callbacks[TimerDomain::max_instances])(void*); + static void* callback_data[TimerDomain::max_instances]; + + template struct Init { + static inline std::array instances{}; + + static void TIM_Default_Callback(void* raw) { (void)raw; } + + static void init(std::span cfgs) { + TIM_TypeDef* sched_timer = cmsis_timers[timer_idxmap[SCHEDULER_TIMER_DOMAIN]]; + rcc_enable_timer(sched_timer); + + TimerDomain::callbacks[1] = TIM_Default_Callback; + TimerDomain::callbacks[2] = TIM_Default_Callback; + TimerDomain::callbacks[3] = TIM_Default_Callback; + TimerDomain::callbacks[4] = TIM_Default_Callback; + TimerDomain::callbacks[5] = TIM_Default_Callback; + TimerDomain::callbacks[6] = TIM_Default_Callback; + TimerDomain::callbacks[7] = TIM_Default_Callback; + TimerDomain::callbacks[8] = TIM_Default_Callback; + TimerDomain::callbacks[9] = TIM_Default_Callback; + TimerDomain::callbacks[10] = TIM_Default_Callback; + TimerDomain::callbacks[11] = TIM_Default_Callback; + TimerDomain::callbacks[12] = TIM_Default_Callback; + TimerDomain::callbacks[13] = TIM_Default_Callback; + TimerDomain::callbacks[14] = TIM_Default_Callback; + TimerDomain::callbacks[15] = TIM_Default_Callback; + for (std::size_t i = 0; i < N; i++) { + const Config& e = cfgs[i]; + + TIM_HandleTypeDef* handle = hal_handles[e.timer_idx]; + TIM_TypeDef* tim = cmsis_timers[e.timer_idx]; + handle->Instance = tim; + handle->Init.Period = 0; + handle->Init.Prescaler = 0; + handle->Init.CounterMode = TIM_COUNTERMODE_UP; + handle->Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; + handle->Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; + handle->Init.RepetitionCounter = 0; + + handle->ChannelState[0] = HAL_TIM_CHANNEL_STATE_READY; + handle->ChannelState[1] = HAL_TIM_CHANNEL_STATE_READY; + handle->ChannelState[2] = HAL_TIM_CHANNEL_STATE_READY; + handle->ChannelState[3] = HAL_TIM_CHANNEL_STATE_READY; + handle->ChannelState[4] = HAL_TIM_CHANNEL_STATE_READY; + handle->ChannelState[5] = HAL_TIM_CHANNEL_STATE_READY; + + handle->ChannelNState[0] = HAL_TIM_CHANNEL_STATE_READY; + handle->ChannelNState[1] = HAL_TIM_CHANNEL_STATE_READY; + handle->ChannelNState[2] = HAL_TIM_CHANNEL_STATE_READY; + handle->ChannelNState[3] = HAL_TIM_CHANNEL_STATE_READY; + handle->DMABurstState = HAL_DMA_BURST_STATE_READY; + handle->Lock = HAL_UNLOCKED; + handle->State = HAL_TIM_STATE_READY; + + rcc_enable_timer(tim); + + Instance* inst = &instances[i]; + inst->tim = tim; + inst->hal_tim = handle; + inst->timer_idx = e.timer_idx; + } + } + }; - consteval GPIODomain::AlternateFunction TimerDomain::Timer::get_gpio_af( - ST_LIB::TimerRequest req, - ST_LIB::TimerPin pin - ) { - enum TimerAF_Use { - Channel_1 = 1, - Channel_2 = 2, - Channel_3 = 3, - Channel_4 = 4, + static consteval uint8_t get_channel_state_idx(const ST_LIB::TimerChannel ch) { + switch (ch) { + case TimerChannel::CHANNEL_1: + case TimerChannel::CHANNEL_1_NEGATED: + case TimerChannel::CHANNEL_2: + case TimerChannel::CHANNEL_2_NEGATED: + case TimerChannel::CHANNEL_3: + case TimerChannel::CHANNEL_3_NEGATED: + case TimerChannel::CHANNEL_4: + case TimerChannel::CHANNEL_4_NEGATED: + case TimerChannel::CHANNEL_5: + case TimerChannel::CHANNEL_6: + return (static_cast(ch) & + ~static_cast(TimerChannel::CHANNEL_NEGATED_FLAG)) - + 1; + + default: + ST_LIB::compile_error("unreachable"); + return 0; + } + } - NegatedChannelFlag = 8, + static consteval uint8_t get_channel_mul4(const ST_LIB::TimerChannel ch) { + switch (ch) { + case TimerChannel::CHANNEL_1: + case TimerChannel::CHANNEL_1_NEGATED: + return 0x00; + case TimerChannel::CHANNEL_2: + case TimerChannel::CHANNEL_2_NEGATED: + return 0x04; + case TimerChannel::CHANNEL_3: + case TimerChannel::CHANNEL_3_NEGATED: + return 0x08; + case TimerChannel::CHANNEL_4: + case TimerChannel::CHANNEL_4_NEGATED: + return 0x0C; + case TimerChannel::CHANNEL_5: + return 0x10; + case TimerChannel::CHANNEL_6: + return 0x14; + + default: + ST_LIB::compile_error("unreachable"); + return 0; + } + } - Channel_1_Negated = 1 | NegatedChannelFlag, - Channel_2_Negated = 2 | NegatedChannelFlag, - Channel_3_Negated = 3 | NegatedChannelFlag, - Channel_4_Negated = 4 | NegatedChannelFlag, + static inline uint32_t get_timer_frequency(TIM_TypeDef* tim) { + uint32_t result = 0; + if ((tim == TIM2) || (tim == TIM3) || (tim == TIM4) || (tim == TIM5) || (tim == TIM6) || + (tim == TIM7) || (tim == TIM12) || (tim == TIM13) || (tim == TIM14)) { + result = HAL_RCC_GetPCLK1Freq(); + if ((RCC->D2CFGR & RCC_D2CFGR_D2PPRE1) != RCC_HCLK_DIV1) { + result *= 2; + } + } else if ((tim == TIM1) || (tim == TIM8) || (tim == TIM15) || (tim == TIM16) || (tim == TIM17) || (tim == TIM23) || (tim == TIM24)) { + result = HAL_RCC_GetPCLK2Freq(); + if ((RCC->D2CFGR & RCC_D2CFGR_D2PPRE2) != RCC_HCLK_DIV1) { + result *= 2; + } + } else { + ErrorHandler("Invalid timer ptr"); + } - ExternalTriggerFilter, /* ETR */ - BreakInput_1, - BreakInput_2, - BreakInputCompare_1, - BreakInputCompare_2, - }; + return result; + } +}; + +consteval GPIODomain::AlternateFunction +TimerDomain::Timer::get_gpio_af(ST_LIB::TimerRequest req, ST_LIB::TimerPin pin) { + enum TimerAF_Use { + Channel_1 = 1, + Channel_2 = 2, + Channel_3 = 3, + Channel_4 = 4, + + NegatedChannelFlag = 8, + + Channel_1_Negated = 1 | NegatedChannelFlag, + Channel_2_Negated = 2 | NegatedChannelFlag, + Channel_3_Negated = 3 | NegatedChannelFlag, + Channel_4_Negated = 4 | NegatedChannelFlag, + + ExternalTriggerFilter, /* ETR */ + BreakInput_1, + BreakInput_2, + BreakInputCompare_1, + BreakInputCompare_2, + }; - struct TimerPossiblePin { - ST_LIB::GPIODomain::Pin pin; - ST_LIB::GPIODomain::AlternateFunction af; - TimerAF_Use use; - }; + struct TimerPossiblePin { + ST_LIB::GPIODomain::Pin pin; + ST_LIB::GPIODomain::AlternateFunction af; + TimerAF_Use use; + }; - // 4 capture-compare channels - // complementary output + // 4 capture-compare channels + // complementary output #define Tim1PinsMacro \ {ST_LIB::PE6, ST_LIB::GPIODomain::AlternateFunction::AF1, BreakInput_2}, \ {ST_LIB::PE6, ST_LIB::GPIODomain::AlternateFunction::AF12, BreakInputCompare_2}, \ @@ -913,7 +904,7 @@ extern TIM_HandleTypeDef htim24; {ST_LIB::PA12, ST_LIB::GPIODomain::AlternateFunction::AF1, ExternalTriggerFilter}, \ {ST_LIB::PA12, ST_LIB::GPIODomain::AlternateFunction::AF12, BreakInput_2}, - // 4 capture-compare channels + // 4 capture-compare channels #define Tim2PinsMacro \ {ST_LIB::PA0, ST_LIB::GPIODomain::AlternateFunction::AF1, Channel_1}, \ {ST_LIB::PA0, ST_LIB::GPIODomain::AlternateFunction::AF1, ExternalTriggerFilter}, \ @@ -930,7 +921,7 @@ extern TIM_HandleTypeDef htim24; {ST_LIB::PB10, ST_LIB::GPIODomain::AlternateFunction::AF1, Channel_3}, \ {ST_LIB::PB11, ST_LIB::GPIODomain::AlternateFunction::AF1, Channel_4}, - // 4 capture-compare channels + // 4 capture-compare channels #define Tim3PinsMacro \ {ST_LIB::PA6, ST_LIB::GPIODomain::AlternateFunction::AF2, Channel_1}, \ {ST_LIB::PA7, ST_LIB::GPIODomain::AlternateFunction::AF2, Channel_2}, \ @@ -947,7 +938,7 @@ extern TIM_HandleTypeDef htim24; \ {ST_LIB::PD2, ST_LIB::GPIODomain::AlternateFunction::AF2, ExternalTriggerFilter}, - // 4 capture-compare channels + // 4 capture-compare channels #define Tim4PinsMacro \ {ST_LIB::PB6, ST_LIB::GPIODomain::AlternateFunction::AF2, Channel_1}, \ {ST_LIB::PB7, ST_LIB::GPIODomain::AlternateFunction::AF2, Channel_2}, \ @@ -961,7 +952,7 @@ extern TIM_HandleTypeDef htim24; \ {ST_LIB::PE0, ST_LIB::GPIODomain::AlternateFunction::AF2, ExternalTriggerFilter}, - // 4 capture-compare channels + // 4 capture-compare channels #define Tim5PinsMacro \ {ST_LIB::PA0, ST_LIB::GPIODomain::AlternateFunction::AF2, Channel_1}, \ {ST_LIB::PA1, ST_LIB::GPIODomain::AlternateFunction::AF2, Channel_2}, \ @@ -969,15 +960,15 @@ extern TIM_HandleTypeDef htim24; {ST_LIB::PA3, ST_LIB::GPIODomain::AlternateFunction::AF2, Channel_4}, \ {ST_LIB::PA4, ST_LIB::GPIODomain::AlternateFunction::AF2, ExternalTriggerFilter}, - // anything invalid, this doesn't get checked for basic timers because they have no pins + // anything invalid, this doesn't get checked for basic timers because they have no pins #define Tim6PinsMacro \ { ST_LIB::PA0, ST_LIB::GPIODomain::AlternateFunction::NO_AF, Channel_1 } - // anything invalid, this doesn't get checked for basic timers because they have no pins + // anything invalid, this doesn't get checked for basic timers because they have no pins #define Tim7PinsMacro \ { ST_LIB::PA0, ST_LIB::GPIODomain::AlternateFunction::NO_AF, Channel_1 } - // 4 capture-compare channels - // complementary output + // 4 capture-compare channels + // complementary output #define Tim8PinsMacro \ {ST_LIB::PA0, ST_LIB::GPIODomain::AlternateFunction::AF3, ExternalTriggerFilter}, \ {ST_LIB::PA5, ST_LIB::GPIODomain::AlternateFunction::AF3, Channel_1_Negated}, \ @@ -1008,22 +999,22 @@ extern TIM_HandleTypeDef htim24; \ {ST_LIB::PG8, ST_LIB::GPIODomain::AlternateFunction::AF3, ExternalTriggerFilter}, - // 2 capture-compare channels + // 2 capture-compare channels #define Tim12PinsMacro \ {ST_LIB::PB14, ST_LIB::GPIODomain::AlternateFunction::AF2, Channel_1}, \ {ST_LIB::PB15, ST_LIB::GPIODomain::AlternateFunction::AF2, Channel_2}, - // 1 capture-compare channel + // 1 capture-compare channel #define Tim13PinsMacro \ {ST_LIB::PA6, ST_LIB::GPIODomain::AlternateFunction::AF9, Channel_1}, \ {ST_LIB::PF8, ST_LIB::GPIODomain::AlternateFunction::AF9, Channel_1}, - // 1 capture-compare channel + // 1 capture-compare channel #define Tim14PinsMacro \ {ST_LIB::PA7, ST_LIB::GPIODomain::AlternateFunction::AF9, Channel_1}, \ {ST_LIB::PF9, ST_LIB::GPIODomain::AlternateFunction::AF9, Channel_1}, - // 2 capture-compare channels + // 2 capture-compare channels #define Tim15PinsMacro \ {ST_LIB::PA0, ST_LIB::GPIODomain::AlternateFunction::AF4, BreakInput_1}, \ \ @@ -1040,13 +1031,13 @@ extern TIM_HandleTypeDef htim24; {ST_LIB::PE4, ST_LIB::GPIODomain::AlternateFunction::AF4, Channel_1}, \ {ST_LIB::PE4, ST_LIB::GPIODomain::AlternateFunction::AF4, Channel_2}, - // 1 capture-compare channel + // 1 capture-compare channel #define Tim16PinsMacro \ {ST_LIB::PF6, ST_LIB::GPIODomain::AlternateFunction::AF1, Channel_1}, \ {ST_LIB::PF8, ST_LIB::GPIODomain::AlternateFunction::AF1, Channel_1_Negated}, \ {ST_LIB::PF10, ST_LIB::GPIODomain::AlternateFunction::AF1, BreakInput_1}, - // 1 capture-compare channel + // 1 capture-compare channel #define Tim17PinsMacro \ {ST_LIB::PB5, ST_LIB::GPIODomain::AlternateFunction::AF1, BreakInput_1}, \ {ST_LIB::PB7, ST_LIB::GPIODomain::AlternateFunction::AF1, Channel_1_Negated}, \ @@ -1055,7 +1046,7 @@ extern TIM_HandleTypeDef htim24; {ST_LIB::PF9, ST_LIB::GPIODomain::AlternateFunction::AF1, Channel_1_Negated}, \ {ST_LIB::PG6, ST_LIB::GPIODomain::AlternateFunction::AF1, BreakInput_1}, - // 4 capture-compare channels + // 4 capture-compare channels #define Tim23PinsMacro \ {ST_LIB::PB2, ST_LIB::GPIODomain::AlternateFunction::AF13, ExternalTriggerFilter}, \ {ST_LIB::PF0, ST_LIB::GPIODomain::AlternateFunction::AF13, Channel_1}, \ @@ -1073,7 +1064,7 @@ extern TIM_HandleTypeDef htim24; {ST_LIB::PG13, ST_LIB::GPIODomain::AlternateFunction::AF13, Channel_2}, \ {ST_LIB::PG14, ST_LIB::GPIODomain::AlternateFunction::AF13, Channel_3}, - // 4 capture-compare channels + // 4 capture-compare channels #define Tim24PinsMacro \ {ST_LIB::PB3, ST_LIB::GPIODomain::AlternateFunction::AF14, ExternalTriggerFilter}, \ {ST_LIB::PF11, ST_LIB::GPIODomain::AlternateFunction::AF14, Channel_1}, \ @@ -1088,68 +1079,68 @@ extern TIM_HandleTypeDef htim24; constexpr std::size_t glue(tim, glue(timx, pin_count)) = \ ARRAY_LENGTH(glue(tim, glue(timx, pins))); - TimerXList + TimerXList #undef X - struct TimerPossPins { - TimerPossiblePin pins[31]; - std::size_t pin_count; - }; - constexpr TimerPossPins empty_pins = {.pins = {}, .pin_count = 0}; - constexpr TimerPossPins tim_pins[25] = { - empty_pins, /* 0 */ - {{Tim1PinsMacro}, tim1pin_count}, /* TIM1 */ - {{Tim2PinsMacro}, tim2pin_count}, /* TIM2 */ - {{Tim3PinsMacro}, tim3pin_count}, /* TIM3 */ - {{Tim4PinsMacro}, tim4pin_count}, /* TIM4 */ - {{Tim5PinsMacro}, tim5pin_count}, /* TIM5 */ - {{Tim6PinsMacro}, tim6pin_count - }, /* TIM6 - won't get checked since they have no associated pins */ - {{Tim7PinsMacro}, tim7pin_count - }, /* TIM7 - won't get checked since they have no associated pins */ - {{Tim8PinsMacro}, tim8pin_count}, /* TIM8 */ - empty_pins, /* 9 */ - empty_pins, /* 10 */ - empty_pins, /* 11 */ - {{Tim12PinsMacro}, tim12pin_count}, /* TIM12 */ - {{Tim13PinsMacro}, tim13pin_count}, /* TIM13 */ - {{Tim14PinsMacro}, tim14pin_count}, /* TIM14 */ - {{Tim15PinsMacro}, tim15pin_count}, /* TIM15 */ - {{Tim16PinsMacro}, tim16pin_count}, /* TIM16 */ - {{Tim17PinsMacro}, tim17pin_count}, /* TIM17 */ - empty_pins, /* 18 */ - empty_pins, /* 19 */ - empty_pins, /* 20 */ - empty_pins, /* 21 */ - empty_pins, /* 22 */ - {{Tim23PinsMacro}, tim23pin_count}, /* TIM23 */ - {{Tim24PinsMacro}, tim24pin_count}, /* TIM24 */ - }; + struct TimerPossPins { + TimerPossiblePin pins[31]; + std::size_t pin_count; + }; + constexpr TimerPossPins empty_pins = {.pins = {}, .pin_count = 0}; + constexpr TimerPossPins tim_pins[25] = { + empty_pins, /* 0 */ + {{Tim1PinsMacro}, tim1pin_count}, /* TIM1 */ + {{Tim2PinsMacro}, tim2pin_count}, /* TIM2 */ + {{Tim3PinsMacro}, tim3pin_count}, /* TIM3 */ + {{Tim4PinsMacro}, tim4pin_count}, /* TIM4 */ + {{Tim5PinsMacro}, tim5pin_count}, /* TIM5 */ + {{Tim6PinsMacro}, tim6pin_count + }, /* TIM6 - won't get checked since they have no associated pins */ + {{Tim7PinsMacro}, tim7pin_count + }, /* TIM7 - won't get checked since they have no associated pins */ + {{Tim8PinsMacro}, tim8pin_count}, /* TIM8 */ + empty_pins, /* 9 */ + empty_pins, /* 10 */ + empty_pins, /* 11 */ + {{Tim12PinsMacro}, tim12pin_count}, /* TIM12 */ + {{Tim13PinsMacro}, tim13pin_count}, /* TIM13 */ + {{Tim14PinsMacro}, tim14pin_count}, /* TIM14 */ + {{Tim15PinsMacro}, tim15pin_count}, /* TIM15 */ + {{Tim16PinsMacro}, tim16pin_count}, /* TIM16 */ + {{Tim17PinsMacro}, tim17pin_count}, /* TIM17 */ + empty_pins, /* 18 */ + empty_pins, /* 19 */ + empty_pins, /* 20 */ + empty_pins, /* 21 */ + empty_pins, /* 22 */ + {{Tim23PinsMacro}, tim23pin_count}, /* TIM23 */ + {{Tim24PinsMacro}, tim24pin_count}, /* TIM24 */ + }; - if (req == TimerRequest::AnyGeneralPurpose || req == TimerRequest::Any32bit) { - ST_LIB::compile_error("Any* timers can't use pins"); - return GPIODomain::AlternateFunction::NO_AF; - } - if (req == TimerRequest::Basic_6 || req == TimerRequest::Basic_7) { - ST_LIB::compile_error("Basic timers can't use pins"); - return GPIODomain::AlternateFunction::NO_AF; - } + if (req == TimerRequest::AnyGeneralPurpose || req == TimerRequest::Any32bit) { + ST_LIB::compile_error("Any* timers can't use pins"); + return GPIODomain::AlternateFunction::NO_AF; + } + if (req == TimerRequest::Basic_6 || req == TimerRequest::Basic_7) { + ST_LIB::compile_error("Basic timers can't use pins"); + return GPIODomain::AlternateFunction::NO_AF; + } + + if (pin.channel == TimerChannel::CHANNEL_NEGATED_FLAG) { + ST_LIB::compile_error( + "You're not supporsed to use TimerChannel::CHANNEL_NEGATED_FLAG as a channel nº" + ); + return GPIODomain::AlternateFunction::NO_AF; + } - if (pin.channel == TimerChannel::CHANNEL_NEGATED_FLAG) { + bool found = false; + for (std::size_t j = 0; j < tim_pins[(int)req].pin_count; j++) { + if (pin.af == ST_LIB::TimerAF::None) { ST_LIB::compile_error( - "You're not supporsed to use TimerChannel::CHANNEL_NEGATED_FLAG as a channel nº" + "Error: Timers with pins must have associated TimerAF (alternate functions)" ); - return GPIODomain::AlternateFunction::NO_AF; - } - - bool found = false; - for (std::size_t j = 0; j < tim_pins[(int)req].pin_count; j++) { - if (pin.af == ST_LIB::TimerAF::None) { - ST_LIB::compile_error( - "Error: Timers with pins must have associated TimerAF (alternate functions)" - ); - } else if(((pin.af == ST_LIB::TimerAF::InputCapture || pin.af == ST_LIB::TimerAF::PWM || pin.af == ST_LIB::TimerAF::Encoder) && + } else if(((pin.af == ST_LIB::TimerAF::InputCapture || pin.af == ST_LIB::TimerAF::PWM || pin.af == ST_LIB::TimerAF::Encoder) && (static_cast(pin.channel) == static_cast(tim_pins[(int)req].pins[j].use))) || ((pin.af == ST_LIB::TimerAF::BreakInput) && @@ -1158,18 +1149,17 @@ extern TIM_HandleTypeDef htim24; ((pin.af == ST_LIB::TimerAF::BreakInputCompare) && (tim_pins[(int)req].pins[j].use == BreakInputCompare_1 || tim_pins[(int)req].pins[j].use == BreakInputCompare_2))) { - found = true; - return tim_pins[(int)req].pins[j].af; - } - } - if (!found) { - ST_LIB::compile_error( - "Error: Couldn't find any pins with the requested alternate function" - ); + found = true; + return tim_pins[(int)req].pins[j].af; } - - return GPIODomain::AlternateFunction::NO_AF; } + if (!found) { + ST_LIB::compile_error("Error: Couldn't find any pins with the requested alternate function" + ); + } + + return GPIODomain::AlternateFunction::NO_AF; +} } // namespace ST_LIB diff --git a/Inc/HALAL/Services/Encoder/Encoder.hpp b/Inc/HALAL/Services/Encoder/Encoder.hpp index 0b6364595..35bc424f8 100644 --- a/Inc/HALAL/Services/Encoder/Encoder.hpp +++ b/Inc/HALAL/Services/Encoder/Encoder.hpp @@ -63,7 +63,7 @@ template class Encoder { } tim->instance->tim->PSC = 5; - if constexpr(tim->is_32bit_instance) { + if constexpr (tim->is_32bit_instance) { tim->instance->tim->ARR = 55000; } else { tim->instance->tim->ARR = UINT32_MAX; diff --git a/Inc/HALAL/Services/InputCapture/InputCapture.hpp b/Inc/HALAL/Services/InputCapture/InputCapture.hpp index fdf5976c4..756f96e71 100644 --- a/Inc/HALAL/Services/InputCapture/InputCapture.hpp +++ b/Inc/HALAL/Services/InputCapture/InputCapture.hpp @@ -26,7 +26,7 @@ class InputCapture { friend struct TimerWrapper; TimerWrapper* timer = nullptr; - TimerDomain::InputCaptureInfo *info = nullptr; + TimerDomain::InputCaptureInfo* info = nullptr; bool is_on = false; InputCapture(TimerWrapper* tim) { @@ -35,8 +35,7 @@ class InputCapture { // Setup TimerDomain uint8_t ch_rising = static_cast(pin_rising.channel) - 1; uint8_t ch_falling = static_cast(channel_falling) - 1; - info = - &TimerDomain::input_capture_info_backing[tim->instance->timer_idx][ch_rising]; + info = &TimerDomain::input_capture_info_backing[tim->instance->timer_idx][ch_rising]; TimerDomain::input_capture_info[tim->instance->timer_idx][ch_rising] = info; TimerDomain::input_capture_info[tim->instance->timer_idx][ch_falling] = info; @@ -63,24 +62,27 @@ class InputCapture { sConfigIC.ICSelection = TIM_ICSELECTION_INDIRECTTI; timer->template config_input_compare_channel(&sConfigIC); } -public: +public: void turn_on(void) { if (is_on) return; - /* STMicroelectronics' recommendation: Avoid undefined behaviour due to - * first interrupt being falling instead of rising by clearing CNT and SR */ + /* STMicroelectronics' recommendation: Avoid undefined behaviour due to + * first interrupt being falling instead of rising by clearing CNT and SR */ timer->instance->tim->CNT = 0; timer->instance->tim->SR = 0; // HAL_TIM_IC_Start_IT(instance.peripheral->handle, instance.channel_rising) { volatile HAL_TIM_ChannelStateTypeDef* ch_state = - &timer->instance->hal_tim->ChannelState[TimerDomain::get_channel_state_idx(pin_rising.channel)]; + &timer->instance->hal_tim + ->ChannelState[TimerDomain::get_channel_state_idx(pin_rising.channel)]; volatile HAL_TIM_ChannelStateTypeDef* n_ch_state = - &timer->instance->hal_tim->ChannelNState[TimerDomain::get_channel_state_idx(pin_rising.channel)]; - if ((*ch_state != HAL_TIM_CHANNEL_STATE_READY) || (*n_ch_state != HAL_TIM_CHANNEL_STATE_READY)) { + &timer->instance->hal_tim + ->ChannelNState[TimerDomain::get_channel_state_idx(pin_rising.channel)]; + if ((*ch_state != HAL_TIM_CHANNEL_STATE_READY) || + (*n_ch_state != HAL_TIM_CHANNEL_STATE_READY)) { ErrorHandler("Channels not ready"); return; } @@ -90,18 +92,21 @@ class InputCapture { timer->template enable_capture_compare_interrupt(); uint32_t enableCCx = TIM_CCER_CC1E - << (TimerDomain::get_channel_mul4(pin_rising.channel) & 0x1FU - ); /* 0x1FU = 31 bits max shift */ + << (TimerDomain::get_channel_mul4(pin_rising.channel) & 0x1FU + ); /* 0x1FU = 31 bits max shift */ SET_BIT(timer->instance->tim->CCER, enableCCx); } // HAL_TIM_IC_Start_IT(instance.peripheral->handle, instance.channel_falling) { volatile HAL_TIM_ChannelStateTypeDef* ch_state = - &timer->instance->hal_tim->ChannelState[TimerDomain::get_channel_state_idx(channel_falling)]; + &timer->instance->hal_tim + ->ChannelState[TimerDomain::get_channel_state_idx(channel_falling)]; volatile HAL_TIM_ChannelStateTypeDef* n_ch_state = - &timer->instance->hal_tim->ChannelNState[TimerDomain::get_channel_state_idx(channel_falling)]; - if ((*ch_state != HAL_TIM_CHANNEL_STATE_READY) || (*n_ch_state != HAL_TIM_CHANNEL_STATE_READY)) { + &timer->instance->hal_tim + ->ChannelNState[TimerDomain::get_channel_state_idx(channel_falling)]; + if ((*ch_state != HAL_TIM_CHANNEL_STATE_READY) || + (*n_ch_state != HAL_TIM_CHANNEL_STATE_READY)) { ErrorHandler("Channels not ready"); return; } @@ -111,8 +116,8 @@ class InputCapture { timer->template enable_capture_compare_interrupt(); uint32_t enableCCx = TIM_CCER_CC1E - << (TimerDomain::get_channel_mul4(channel_falling) & 0x1FU - ); /* 0x1FU = 31 bits max shift */ + << (TimerDomain::get_channel_mul4(channel_falling) & 0x1FU + ); /* 0x1FU = 31 bits max shift */ SET_BIT(timer->instance->tim->CCER, enableCCx); } @@ -124,48 +129,54 @@ class InputCapture { } else { timer->counter_enable(); } - + is_on = true; } void turn_off(void) { - if(!is_on) + if (!is_on) return; - + // HAL_TIM_IC_Stop_IT(instance.peripheral->handle, instance.channel_rising) { timer->template disable_capture_compare_interrupt(); CLEAR_BIT( timer->instance->tim->CCER, - (uint32_t)(TIM_CCER_CC1E << (TimerDomain::get_channel_mul4(pin_rising.channel) & 0x1FU)) + (uint32_t)(TIM_CCER_CC1E + << (TimerDomain::get_channel_mul4(pin_rising.channel) & 0x1FU)) ); volatile HAL_TIM_ChannelStateTypeDef* ch_state = - &timer->instance->hal_tim->ChannelState[TimerDomain::get_channel_state_idx(pin_rising.channel)]; + &timer->instance->hal_tim + ->ChannelState[TimerDomain::get_channel_state_idx(pin_rising.channel)]; volatile HAL_TIM_ChannelStateTypeDef* n_ch_state = - &timer->instance->hal_tim->ChannelNState[TimerDomain::get_channel_state_idx(pin_rising.channel)]; + &timer->instance->hal_tim + ->ChannelNState[TimerDomain::get_channel_state_idx(pin_rising.channel)]; *ch_state = HAL_TIM_CHANNEL_STATE_READY; *n_ch_state = HAL_TIM_CHANNEL_STATE_READY; } - + // HAL_TIM_IC_Stop_IT(instance.peripheral->handle, instance.channel_falling) { timer->template disable_capture_compare_interrupt(); CLEAR_BIT( timer->instance->tim->CCER, - (uint32_t)(TIM_CCER_CC1E << (TimerDomain::get_channel_mul4(channel_falling) & 0x1FU)) + (uint32_t)(TIM_CCER_CC1E + << (TimerDomain::get_channel_mul4(channel_falling) & 0x1FU)) ); volatile HAL_TIM_ChannelStateTypeDef* ch_state = - &timer->instance->hal_tim->ChannelState[TimerDomain::get_channel_state_idx(channel_falling)]; + &timer->instance->hal_tim + ->ChannelState[TimerDomain::get_channel_state_idx(channel_falling)]; volatile HAL_TIM_ChannelStateTypeDef* n_ch_state = - &timer->instance->hal_tim->ChannelNState[TimerDomain::get_channel_state_idx(channel_falling)]; + &timer->instance->hal_tim + ->ChannelNState[TimerDomain::get_channel_state_idx(channel_falling)]; *ch_state = HAL_TIM_CHANNEL_STATE_READY; *n_ch_state = HAL_TIM_CHANNEL_STATE_READY; } - + if (timer->are_all_channels_free()) { timer->counter_disable(); } @@ -173,16 +184,15 @@ class InputCapture { is_on = false; } - uint32_t get_frequency(void) { - return info->frequency; - } + uint32_t get_frequency(void) { return info->frequency; } float get_duty_cycle(void) { - // often you get a trash value in duty_cycle but the frequency is 0 so it's easily identifiable + // often you get a trash value in duty_cycle but the frequency is 0 so it's easily + // identifiable return (info->frequency == 0) ? 0.0f : info->duty_cycle; } }; -}; +}; // namespace ST_LIB #endif // HAL_TIM_MODULE_ENABLED diff --git a/Inc/HALAL/Services/PWM/DualPWM.hpp b/Inc/HALAL/Services/PWM/DualPWM.hpp index 4f8f60b9c..54fb30fe0 100644 --- a/Inc/HALAL/Services/PWM/DualPWM.hpp +++ b/Inc/HALAL/Services/PWM/DualPWM.hpp @@ -71,7 +71,8 @@ class DualPWM { return; volatile HAL_TIM_ChannelStateTypeDef* state = - &timer->instance->hal_tim->ChannelState[TimerDomain::get_channel_state_idx(pin.channel)]; + &timer->instance->hal_tim + ->ChannelState[TimerDomain::get_channel_state_idx(pin.channel)]; if (*state != HAL_TIM_CHANNEL_STATE_READY) { ErrorHandler("Channel not ready"); } @@ -104,7 +105,8 @@ class DualPWM { return; volatile HAL_TIM_ChannelStateTypeDef* state = - &timer->instance->hal_tim->ChannelNState[TimerDomain::get_channel_state_idx(negated_pin.channel)]; + &timer->instance->hal_tim + ->ChannelNState[TimerDomain::get_channel_state_idx(negated_pin.channel)]; if (*state != HAL_TIM_CHANNEL_STATE_READY) { ErrorHandler("Channel not ready"); } @@ -162,11 +164,13 @@ class DualPWM { CLEAR_BIT( timer->tim->CCER, - (uint32_t)(TIM_CCER_CC1NE << (TimerDomain::get_channel_mul4(negated_pin.channel) & 0x1FU)) + (uint32_t)(TIM_CCER_CC1NE + << (TimerDomain::get_channel_mul4(negated_pin.channel) & 0x1FU)) ); HAL_TIM_ChannelStateTypeDef* state = - &timer->instance->hal_tim.ChannelState[TimerDomain::get_channel_state_idx(negated_pin.channel)]; + &timer->instance->hal_tim + .ChannelState[TimerDomain::get_channel_state_idx(negated_pin.channel)]; *state = HAL_TIM_CHANNEL_STATE_READY; if (timer->are_all_channels_free()) { diff --git a/Inc/HALAL/Services/PWM/PWM.hpp b/Inc/HALAL/Services/PWM/PWM.hpp index f85adad7f..305e4251c 100644 --- a/Inc/HALAL/Services/PWM/PWM.hpp +++ b/Inc/HALAL/Services/PWM/PWM.hpp @@ -53,7 +53,8 @@ template class PWM { return; volatile HAL_TIM_ChannelStateTypeDef* state = - &timer->instance->hal_tim->ChannelState[TimerDomain::get_channel_state_idx(pin.channel)]; + &timer->instance->hal_tim + ->ChannelState[TimerDomain::get_channel_state_idx(pin.channel)]; if (*state != HAL_TIM_CHANNEL_STATE_READY) { ErrorHandler("Channel not ready"); } @@ -91,7 +92,8 @@ template class PWM { ); volatile HAL_TIM_ChannelStateTypeDef* state = - &timer->instance->hal_tim->ChannelState[TimerDomain::get_channel_state_idx(pin.channel)]; + &timer->instance->hal_tim + ->ChannelState[TimerDomain::get_channel_state_idx(pin.channel)]; *state = HAL_TIM_CHANNEL_STATE_READY; if (timer->are_all_channels_free()) { diff --git a/Inc/HALAL/Services/Time/TimerWrapper.hpp b/Inc/HALAL/Services/Time/TimerWrapper.hpp index 651ae90d6..3a083772f 100644 --- a/Inc/HALAL/Services/Time/TimerWrapper.hpp +++ b/Inc/HALAL/Services/Time/TimerWrapper.hpp @@ -60,7 +60,7 @@ template struct TimerWrapper { dev.e.request == TimerRequest::GeneralPurpose_15 || dev.e.request == TimerRequest::GeneralPurpose_16 || dev.e.request == TimerRequest::GeneralPurpose_17); - + /* at least 2 capture/compare channels (see IS_TIM_CC2_INSTANCE for macro definition) */ static constexpr bool is_CC2_instance = (dev.e.request == TimerRequest::Advanced_1 || @@ -313,33 +313,58 @@ template struct TimerWrapper { /* {rising channel, falling channel} must be {1,2} or {3,4} (any order) */ template - inline InputCapture get_input_capture(uint16_t prescaler = IMD_IC_PRESCALER_VALUE) - { + inline InputCapture + get_input_capture(uint16_t prescaler = IMD_IC_PRESCALER_VALUE) { instance->tim->PSC = prescaler; - static_assert(rising_pin.channel != channel_falling, "Rising and falling channels must be different"); + static_assert( + rising_pin.channel != channel_falling, + "Rising and falling channels must be different" + ); /* channel 1 can only go with channel 2 */ - static_assert(!((rising_pin.channel == TimerChannel::CHANNEL_1) && - (channel_falling != TimerChannel::CHANNEL_2)), "Channel 1 must go with channel 2 for inputcapture"); - static_assert(!((channel_falling == TimerChannel::CHANNEL_1) && - (rising_pin.channel != TimerChannel::CHANNEL_2)), "Channel 1 must go with channel 2 for inputcapture"); + static_assert( + !((rising_pin.channel == TimerChannel::CHANNEL_1) && + (channel_falling != TimerChannel::CHANNEL_2)), + "Channel 1 must go with channel 2 for inputcapture" + ); + static_assert( + !((channel_falling == TimerChannel::CHANNEL_1) && + (rising_pin.channel != TimerChannel::CHANNEL_2)), + "Channel 1 must go with channel 2 for inputcapture" + ); /* channel 3 can only go with channel 4 */ - static_assert(!((rising_pin.channel == TimerChannel::CHANNEL_3) && - (channel_falling != TimerChannel::CHANNEL_4)), "Channel 3 must go with channel 4 for inputcapture"); - static_assert(!((channel_falling == TimerChannel::CHANNEL_3) && - (rising_pin.channel != TimerChannel::CHANNEL_4)), "Channel 3 must go with channel 4 for inputcapture"); + static_assert( + !((rising_pin.channel == TimerChannel::CHANNEL_3) && + (channel_falling != TimerChannel::CHANNEL_4)), + "Channel 3 must go with channel 4 for inputcapture" + ); + static_assert( + !((channel_falling == TimerChannel::CHANNEL_3) && + (rising_pin.channel != TimerChannel::CHANNEL_4)), + "Channel 3 must go with channel 4 for inputcapture" + ); - static_assert(rising_pin.af == TimerAF::InputCapture, "Pin must be configured as input capture"); - static_assert((static_cast(channel_falling) - 1) <= 4, "Channel must be 1 to 4 for inputcapture"); + static_assert( + rising_pin.af == TimerAF::InputCapture, + "Pin must be configured as input capture" + ); + static_assert( + (static_cast(channel_falling) - 1) <= 4, + "Channel must be 1 to 4 for inputcapture" + ); static_assert(this->is_CC2_instance, "Timer must have 2 or more Capture compare channels"); - if constexpr(channel_falling >= TimerChannel::CHANNEL_3 && !this->is_CC3_instance) { - ST_LIB::compile_error("Error: This timer does not have 3 or more Capture compare channels"); + if constexpr (channel_falling >= TimerChannel::CHANNEL_3 && !this->is_CC3_instance) { + ST_LIB::compile_error( + "Error: This timer does not have 3 or more Capture compare channels" + ); } - if constexpr(channel_falling == TimerChannel::CHANNEL_4 && !this->is_CC4_instance) { - ST_LIB::compile_error("Error: This timer does not have 4 or more Capture compare channels"); + if constexpr (channel_falling == TimerChannel::CHANNEL_4 && !this->is_CC4_instance) { + ST_LIB::compile_error( + "Error: This timer does not have 4 or more Capture compare channels" + ); } return InputCapture(this); @@ -442,28 +467,26 @@ template struct TimerWrapper { /////////////////////////////////////////////////////// // Below are methods used by other objects (internals) - template - inline void enable_capture_compare_interrupt(void) { - if constexpr((ch == TimerChannel::CHANNEL_1) || (ch == TimerChannel::CHANNEL_1_NEGATED)) { + template inline void enable_capture_compare_interrupt(void) { + if constexpr ((ch == TimerChannel::CHANNEL_1) || (ch == TimerChannel::CHANNEL_1_NEGATED)) { SET_BIT(instance->tim->DIER, TIM_IT_CC1); - } else if constexpr((ch == TimerChannel::CHANNEL_2) || (ch == TimerChannel::CHANNEL_2_NEGATED)) { + } else if constexpr ((ch == TimerChannel::CHANNEL_2) || (ch == TimerChannel::CHANNEL_2_NEGATED)) { SET_BIT(instance->tim->DIER, TIM_IT_CC2); - } else if constexpr((ch == TimerChannel::CHANNEL_3) || (ch == TimerChannel::CHANNEL_3_NEGATED)) { + } else if constexpr ((ch == TimerChannel::CHANNEL_3) || (ch == TimerChannel::CHANNEL_3_NEGATED)) { SET_BIT(instance->tim->DIER, TIM_IT_CC3); - } else if constexpr((ch == TimerChannel::CHANNEL_4) || (ch == TimerChannel::CHANNEL_4_NEGATED)) { + } else if constexpr ((ch == TimerChannel::CHANNEL_4) || (ch == TimerChannel::CHANNEL_4_NEGATED)) { SET_BIT(instance->tim->DIER, TIM_IT_CC4); } } - template - inline void disable_capture_compare_interrupt(void) { - if constexpr((ch == TimerChannel::CHANNEL_1) || (ch == TimerChannel::CHANNEL_1_NEGATED)) { + template inline void disable_capture_compare_interrupt(void) { + if constexpr ((ch == TimerChannel::CHANNEL_1) || (ch == TimerChannel::CHANNEL_1_NEGATED)) { CLEAR_BIT(instance->tim->DIER, TIM_IT_CC1); - } else if constexpr((ch == TimerChannel::CHANNEL_2) || (ch == TimerChannel::CHANNEL_2_NEGATED)) { + } else if constexpr ((ch == TimerChannel::CHANNEL_2) || (ch == TimerChannel::CHANNEL_2_NEGATED)) { CLEAR_BIT(instance->tim->DIER, TIM_IT_CC2); - } else if constexpr((ch == TimerChannel::CHANNEL_3) || (ch == TimerChannel::CHANNEL_3_NEGATED)) { + } else if constexpr ((ch == TimerChannel::CHANNEL_3) || (ch == TimerChannel::CHANNEL_3_NEGATED)) { CLEAR_BIT(instance->tim->DIER, TIM_IT_CC3); - } else if constexpr((ch == TimerChannel::CHANNEL_4) || (ch == TimerChannel::CHANNEL_4_NEGATED)) { + } else if constexpr ((ch == TimerChannel::CHANNEL_4) || (ch == TimerChannel::CHANNEL_4_NEGATED)) { CLEAR_BIT(instance->tim->DIER, TIM_IT_CC4); } } @@ -688,7 +711,8 @@ template struct TimerWrapper { /* NOTE(vic): Both config_output_compare_channel and config_input_compare_channel * Could probably be done better if not using TIM_[OC/IC]_InitTypeDef structures */ - template inline void config_input_compare_channel(TIM_IC_InitTypeDef* sConfig) { + template + inline void config_input_compare_channel(TIM_IC_InitTypeDef* sConfig) { if constexpr (!((ch == TimerChannel::CHANNEL_1) || (ch == TimerChannel::CHANNEL_2) || (ch == TimerChannel::CHANNEL_3) || (ch == TimerChannel::CHANNEL_4))) { ST_LIB::compile_error("Only channels 1 to 4 can be configured as input compare"); @@ -704,12 +728,12 @@ template struct TimerWrapper { } else if constexpr (ch == TimerChannel::CHANNEL_3 || ch == TimerChannel::CHANNEL_4) { tmpccmrx = instance->tim->CCMR2; } - - if constexpr((ch == TimerChannel::CHANNEL_1) || (ch == TimerChannel::CHANNEL_1_NEGATED)) { + + if constexpr ((ch == TimerChannel::CHANNEL_1) || (ch == TimerChannel::CHANNEL_1_NEGATED)) { CLEAR_BIT(tmpccer, TIM_CCER_CC1E); /* Select the input */ - if constexpr(this->is_CC2_instance) { + if constexpr (this->is_CC2_instance) { CLEAR_BIT(tmpccmrx, TIM_CCMR1_CC1S); SET_BIT(tmpccmrx, sConfig->ICSelection); } else { @@ -723,10 +747,10 @@ template struct TimerWrapper { /* Select the Polarity and set the CC1E Bit */ CLEAR_BIT(tmpccer, TIM_CCER_CC1P | TIM_CCER_CC1NP); SET_BIT(tmpccer, sConfig->ICPolarity & (TIM_CCER_CC1P | TIM_CCER_CC1NP)); - + CLEAR_BIT(tmpccmrx, TIM_CCMR1_IC1PSC); SET_BIT(tmpccmrx, sConfig->ICPrescaler & TIM_CCMR1_IC1PSC); - } else if constexpr((ch == TimerChannel::CHANNEL_2) || (ch == TimerChannel::CHANNEL_2_NEGATED)) { + } else if constexpr ((ch == TimerChannel::CHANNEL_2) || (ch == TimerChannel::CHANNEL_2_NEGATED)) { CLEAR_BIT(tmpccer, TIM_CCER_CC2E); /* Select the input */ @@ -743,13 +767,13 @@ template struct TimerWrapper { CLEAR_BIT(tmpccmrx, TIM_CCMR1_IC2PSC); SET_BIT(tmpccmrx, (sConfig->ICPrescaler << 8U) & TIM_CCMR1_IC2PSC); - } else if constexpr((ch == TimerChannel::CHANNEL_3) || (ch == TimerChannel::CHANNEL_3_NEGATED)) { + } else if constexpr ((ch == TimerChannel::CHANNEL_3) || (ch == TimerChannel::CHANNEL_3_NEGATED)) { CLEAR_BIT(tmpccer, TIM_CCER_CC3E); /* Select the Input */ CLEAR_BIT(tmpccmrx, TIM_CCMR2_CC3S); SET_BIT(tmpccmrx, sConfig->ICSelection); - + /* Set the filter */ CLEAR_BIT(tmpccmrx, TIM_CCMR2_IC3F); SET_BIT(tmpccmrx, (sConfig->ICFilter << 4U) & TIM_CCMR2_IC3F); @@ -760,7 +784,7 @@ template struct TimerWrapper { CLEAR_BIT(tmpccmrx, TIM_CCMR2_IC3PSC); SET_BIT(tmpccmrx, (sConfig->ICPrescaler) & TIM_CCMR2_IC3PSC); - } else if constexpr(ch == TimerChannel::CHANNEL_4) { + } else if constexpr (ch == TimerChannel::CHANNEL_4) { CLEAR_BIT(tmpccer, TIM_CCER_CC4E); /* Select the Input */ diff --git a/Src/HALAL/Models/TimerDomain/TimerDomain.cpp b/Src/HALAL/Models/TimerDomain/TimerDomain.cpp index c3cecb3ec..252868261 100644 --- a/Src/HALAL/Models/TimerDomain/TimerDomain.cpp +++ b/Src/HALAL/Models/TimerDomain/TimerDomain.cpp @@ -2,11 +2,9 @@ using namespace ST_LIB; -#define CaptureCompareInterruptMask \ - (TIM_SR_CC1IF | TIM_SR_CC2IF | TIM_SR_CC3IF | TIM_SR_CC4IF) +#define CaptureCompareInterruptMask (TIM_SR_CC1IF | TIM_SR_CC2IF | TIM_SR_CC3IF | TIM_SR_CC4IF) -#define CaptureCompareOvercaptureMask \ - (TIM_SR_CC1IF | TIM_SR_CC2IF | TIM_SR_CC3IF | TIM_SR_CC4IF) +#define CaptureCompareOvercaptureMask (TIM_SR_CC1IF | TIM_SR_CC2IF | TIM_SR_CC3IF | TIM_SR_CC4IF) TIM_HandleTypeDef htim1; TIM_HandleTypeDef htim2; @@ -27,44 +25,47 @@ TIM_HandleTypeDef htim24; void (*TimerDomain::callbacks[TimerDomain::max_instances])(void*) = {nullptr}; void* TimerDomain::callback_data[TimerDomain::max_instances] = {nullptr}; -TimerDomain::InputCaptureInfo* TimerDomain::input_capture_info[max_instances][input_capture_channels]; -TimerDomain::InputCaptureInfo TimerDomain::input_capture_info_backing[max_instances][input_capture_channels]; +TimerDomain::InputCaptureInfo* TimerDomain::input_capture_info[max_instances] + [input_capture_channels]; +TimerDomain::InputCaptureInfo TimerDomain::input_capture_info_backing[max_instances] + [input_capture_channels]; -static void TIM_IC_CaptureCallback(const uint32_t timer_idx, uint32_t channel) -{ +static void TIM_IC_CaptureCallback(const uint32_t timer_idx, uint32_t channel) { TIM_HandleTypeDef* htim = TimerDomain::hal_handles[timer_idx]; TimerDomain::InputCaptureInfo* info = TimerDomain::input_capture_info[timer_idx][channel]; - if(info->channel_rising == channel) { + if (info->channel_rising == channel) { // NOTE: CCR1 - CCR4 are contiguous // NOTE: CCxIF flag is cleared by software by reading the captured data in CCRx uint32_t current = (*(((uint32_t*)&htim->Instance->CCR1) + channel)); uint32_t period = current - info->value_rising; - if((period != 0) && (info->value_falling < period)) { - uint32_t ref_clock = TimerDomain::get_timer_frequency(htim->Instance) / (htim->Instance->PSC + 1); + if ((period != 0) && (info->value_falling < period)) { + uint32_t ref_clock = + TimerDomain::get_timer_frequency(htim->Instance) / (htim->Instance->PSC + 1); info->period = period; info->frequency = ref_clock / period; info->duty_cycle = ((float)info->value_falling * 100.0f) / (float)period; } info->value_rising = current; - } else if(info->channel_falling == channel) { - uint32_t falling_value = *(((uint32_t*)&htim->Instance->CCR1) + channel) - info->value_rising; - if(falling_value < info->period) info->value_falling = falling_value; + } else if (info->channel_falling == channel) { + uint32_t falling_value = + *(((uint32_t*)&htim->Instance->CCR1) + channel) - info->value_rising; + if (falling_value < info->period) + info->value_falling = falling_value; } else [[unlikely]] { ErrorHandler("TimerDomain::input_capture_info was modified"); } } -static void TIM_InterruptCallback(const uint32_t timer_idx) -{ +static void TIM_InterruptCallback(const uint32_t timer_idx) { TIM_TypeDef* tim = TimerDomain::cmsis_timers[timer_idx]; - if(tim->SR & TIM_SR_UIF) { + if (tim->SR & TIM_SR_UIF) { CLEAR_BIT(tim->SR, TIM_SR_UIF); TimerDomain::callbacks[timer_idx](TimerDomain::callback_data[timer_idx]); } - // NOTE: possible optimization: only do the channels possible for timer + // NOTE: possible optimization: only do the channels possible for timer // Bit 0 = UIF, bits 1 - 4 = CCxIF for (uint32_t ch = 1; ch < 5; ch++) { uint32_t flag_mask = 1U << ch; @@ -75,25 +76,15 @@ static void TIM_InterruptCallback(const uint32_t timer_idx) } } -extern "C" void TIM1_UP_IRQHandler(void) { - TIM_InterruptCallback(timer_idxmap[1]); -} +extern "C" void TIM1_UP_IRQHandler(void) { TIM_InterruptCallback(timer_idxmap[1]); } -extern "C" void TIM2_IRQHandler(void) { - TIM_InterruptCallback(timer_idxmap[2]); -} +extern "C" void TIM2_IRQHandler(void) { TIM_InterruptCallback(timer_idxmap[2]); } -extern "C" void TIM3_IRQHandler(void) { - TIM_InterruptCallback(timer_idxmap[3]); -} +extern "C" void TIM3_IRQHandler(void) { TIM_InterruptCallback(timer_idxmap[3]); } -extern "C" void TIM4_IRQHandler(void) { - TIM_InterruptCallback(timer_idxmap[4]); -} +extern "C" void TIM4_IRQHandler(void) { TIM_InterruptCallback(timer_idxmap[4]); } -extern "C" void TIM5_IRQHandler(void) { - TIM_InterruptCallback(timer_idxmap[5]); -} +extern "C" void TIM5_IRQHandler(void) { TIM_InterruptCallback(timer_idxmap[5]); } extern "C" void TIM6_DAC_IRQHandler(void) { // NOTE: Basic timers have no capture compare channels @@ -113,7 +104,7 @@ extern "C" void TIM8_BRK_TIM12_IRQHandler(void) { TIM_TypeDef* tim8 = TimerDomain::cmsis_timers[tim8_idx]; TIM_TypeDef* tim12 = TimerDomain::cmsis_timers[tim12_idx]; - + if ((tim12->SR & TIM_SR_UIF) != 0) { CLEAR_BIT(tim12->SR, TIM_SR_UIF); TimerDomain::callbacks[tim12_idx](TimerDomain::callback_data[tim12_idx]); @@ -140,7 +131,7 @@ extern "C" void TIM8_UP_TIM13_IRQHandler(void) { TIM_TypeDef* tim8 = TimerDomain::cmsis_timers[tim8_idx]; TIM_TypeDef* tim13 = TimerDomain::cmsis_timers[tim13_idx]; - + if ((tim13->SR & TIM_SR_UIF) != 0) { CLEAR_BIT(tim13->SR, TIM_SR_UIF); TimerDomain::callbacks[tim13_idx](TimerDomain::callback_data[tim13_idx]); @@ -196,22 +187,12 @@ extern "C" void TIM8_TRG_COM_TIM14_IRQHandler(void) { } } -extern "C" void TIM15_IRQHandler(void) { - TIM_InterruptCallback(timer_idxmap[15]); -} +extern "C" void TIM15_IRQHandler(void) { TIM_InterruptCallback(timer_idxmap[15]); } -extern "C" void TIM16_IRQHandler(void) { - TIM_InterruptCallback(timer_idxmap[16]); -} +extern "C" void TIM16_IRQHandler(void) { TIM_InterruptCallback(timer_idxmap[16]); } -extern "C" void TIM17_IRQHandler(void) { - TIM_InterruptCallback(timer_idxmap[17]); -} +extern "C" void TIM17_IRQHandler(void) { TIM_InterruptCallback(timer_idxmap[17]); } -extern "C" void TIM23_IRQHandler(void) { - TIM_InterruptCallback(timer_idxmap[23]); -} +extern "C" void TIM23_IRQHandler(void) { TIM_InterruptCallback(timer_idxmap[23]); } -extern "C" void TIM24_IRQHandler(void) { - TIM_InterruptCallback(timer_idxmap[24]); -} +extern "C" void TIM24_IRQHandler(void) { TIM_InterruptCallback(timer_idxmap[24]); } From fd2e6d3886098ea93c03cf02dab543f3e20fdb8b Mon Sep 17 00:00:00 2001 From: victhor Date: Mon, 16 Mar 2026 11:01:46 +0100 Subject: [PATCH 34/44] fix indentation --- Inc/HALAL/Models/TimerDomain/TimerDomain.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp b/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp index 10570036a..0ae3f8160 100644 --- a/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp +++ b/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp @@ -705,9 +705,9 @@ struct TimerDomain { static void TIM_Default_Callback(void* raw) { (void)raw; } - static void init(std::span cfgs) { - Scheduler_global_timer = cmsis_timers[timer_idxmap[SCHEDULER_TIMER_DOMAIN]]; - rcc_enable_timer(Scheduler_global_timer); + static void init(std::span cfgs) { + Scheduler_global_timer = cmsis_timers[timer_idxmap[SCHEDULER_TIMER_DOMAIN]]; + rcc_enable_timer(Scheduler_global_timer); TimerDomain::callbacks[1] = TIM_Default_Callback; TimerDomain::callbacks[2] = TIM_Default_Callback; From 4dedc7046994b409b4cd7f07e91b1d33bd89cd86 Mon Sep 17 00:00:00 2001 From: victhor Date: Sat, 21 Mar 2026 00:37:11 +0100 Subject: [PATCH 35/44] add changeset --- .changesets/inputcapture.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .changesets/inputcapture.md diff --git a/.changesets/inputcapture.md b/.changesets/inputcapture.md new file mode 100644 index 000000000..143412f19 --- /dev/null +++ b/.changesets/inputcapture.md @@ -0,0 +1,4 @@ +release: minor +summary: input capture implementation + +Also changed how the timer interrupts are handled since this was required for inputcapture From b860847bf1958f4fa771b34d61fb371bc1cd749b Mon Sep 17 00:00:00 2001 From: victhor Date: Sat, 21 Mar 2026 00:38:08 +0100 Subject: [PATCH 36/44] removed CaptureCompareOvercaptureMask since it wasn't being used --- Src/HALAL/Models/TimerDomain/TimerDomain.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/Src/HALAL/Models/TimerDomain/TimerDomain.cpp b/Src/HALAL/Models/TimerDomain/TimerDomain.cpp index 252868261..a8d8bfa2f 100644 --- a/Src/HALAL/Models/TimerDomain/TimerDomain.cpp +++ b/Src/HALAL/Models/TimerDomain/TimerDomain.cpp @@ -4,8 +4,6 @@ using namespace ST_LIB; #define CaptureCompareInterruptMask (TIM_SR_CC1IF | TIM_SR_CC2IF | TIM_SR_CC3IF | TIM_SR_CC4IF) -#define CaptureCompareOvercaptureMask (TIM_SR_CC1IF | TIM_SR_CC2IF | TIM_SR_CC3IF | TIM_SR_CC4IF) - TIM_HandleTypeDef htim1; TIM_HandleTypeDef htim2; TIM_HandleTypeDef htim3; From c864fedb30b6cb80ef0c9de885721f9c3c86664f Mon Sep 17 00:00:00 2001 From: victhor Date: Sat, 21 Mar 2026 00:40:15 +0100 Subject: [PATCH 37/44] fix off by one 'error' --- Inc/HALAL/Services/Time/TimerWrapper.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Inc/HALAL/Services/Time/TimerWrapper.hpp b/Inc/HALAL/Services/Time/TimerWrapper.hpp index 3a083772f..5cedbf28a 100644 --- a/Inc/HALAL/Services/Time/TimerWrapper.hpp +++ b/Inc/HALAL/Services/Time/TimerWrapper.hpp @@ -351,7 +351,7 @@ template struct TimerWrapper { "Pin must be configured as input capture" ); static_assert( - (static_cast(channel_falling) - 1) <= 4, + (static_cast(channel_falling)) <= 4, "Channel must be 1 to 4 for inputcapture" ); static_assert(this->is_CC2_instance, "Timer must have 2 or more Capture compare channels"); From 89b2d027b2afcbba9d3cb349ba147f38a27b1db5 Mon Sep 17 00:00:00 2001 From: victhor Date: Sat, 21 Mar 2026 00:42:59 +0100 Subject: [PATCH 38/44] fix typo --- Inc/HALAL/Services/Time/TimerWrapper.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Inc/HALAL/Services/Time/TimerWrapper.hpp b/Inc/HALAL/Services/Time/TimerWrapper.hpp index 5cedbf28a..dea8fe595 100644 --- a/Inc/HALAL/Services/Time/TimerWrapper.hpp +++ b/Inc/HALAL/Services/Time/TimerWrapper.hpp @@ -763,7 +763,7 @@ template struct TimerWrapper { /* Select the Polarity and set the CC2E Bit */ CLEAR_BIT(tmpccer, TIM_CCER_CC2P | TIM_CCER_CC2NP); - SET_BIT(tmpccer, (sConfig->ICPolarity >> 4U) & (TIM_CCER_CC2P | TIM_CCER_CC2NP)); + SET_BIT(tmpccer, (sConfig->ICPolarity << 4U) & (TIM_CCER_CC2P | TIM_CCER_CC2NP)); CLEAR_BIT(tmpccmrx, TIM_CCMR1_IC2PSC); SET_BIT(tmpccmrx, (sConfig->ICPrescaler << 8U) & TIM_CCMR1_IC2PSC); From 5464ec1400f41b2a8551d6f4709b7b4343bafe1f Mon Sep 17 00:00:00 2001 From: victhor Date: Sat, 21 Mar 2026 00:48:24 +0100 Subject: [PATCH 39/44] fix error handling for something that should not happen ever --- .../Services/InputCapture/InputCapture.hpp | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/Inc/HALAL/Services/InputCapture/InputCapture.hpp b/Inc/HALAL/Services/InputCapture/InputCapture.hpp index 756f96e71..702ad852b 100644 --- a/Inc/HALAL/Services/InputCapture/InputCapture.hpp +++ b/Inc/HALAL/Services/InputCapture/InputCapture.hpp @@ -74,24 +74,25 @@ class InputCapture { timer->instance->tim->SR = 0; // HAL_TIM_IC_Start_IT(instance.peripheral->handle, instance.channel_rising) + volatile HAL_TIM_ChannelStateTypeDef* chx_1_state; + volatile HAL_TIM_ChannelStateTypeDef* chx_1_n_state; + uint32_t enableCCx_1; { - volatile HAL_TIM_ChannelStateTypeDef* ch_state = - &timer->instance->hal_tim + chx_1_state = &timer->instance->hal_tim ->ChannelState[TimerDomain::get_channel_state_idx(pin_rising.channel)]; - volatile HAL_TIM_ChannelStateTypeDef* n_ch_state = - &timer->instance->hal_tim + chx_1_n_state = &timer->instance->hal_tim ->ChannelNState[TimerDomain::get_channel_state_idx(pin_rising.channel)]; - if ((*ch_state != HAL_TIM_CHANNEL_STATE_READY) || - (*n_ch_state != HAL_TIM_CHANNEL_STATE_READY)) { + if ((*chx_1_state != HAL_TIM_CHANNEL_STATE_READY) || + (*chx_1_n_state != HAL_TIM_CHANNEL_STATE_READY)) { ErrorHandler("Channels not ready"); return; } - *ch_state = HAL_TIM_CHANNEL_STATE_BUSY; - *n_ch_state = HAL_TIM_CHANNEL_STATE_BUSY; + *chx_1_state = HAL_TIM_CHANNEL_STATE_BUSY; + *chx_1_n_state = HAL_TIM_CHANNEL_STATE_BUSY; timer->template enable_capture_compare_interrupt(); - uint32_t enableCCx = TIM_CCER_CC1E + enableCCx_1 = TIM_CCER_CC1E << (TimerDomain::get_channel_mul4(pin_rising.channel) & 0x1FU ); /* 0x1FU = 31 bits max shift */ SET_BIT(timer->instance->tim->CCER, enableCCx); @@ -106,8 +107,13 @@ class InputCapture { &timer->instance->hal_tim ->ChannelNState[TimerDomain::get_channel_state_idx(channel_falling)]; if ((*ch_state != HAL_TIM_CHANNEL_STATE_READY) || - (*n_ch_state != HAL_TIM_CHANNEL_STATE_READY)) { + (*n_ch_state != HAL_TIM_CHANNEL_STATE_READY)) [[unlikely]] { ErrorHandler("Channels not ready"); + + timer->template disable_capture_compare_interrupt(); + CLEAR_BIT(timer->instance->tim->CCER, enableCCx_1); + *chx_1_state = HAL_TIM_CHANNEL_STATE_READY; + *chx_1_n_state = HAL_TIM_CHANNEL_STATE_READY; return; } From cc5f75b3274b41f76cd2f294245dc4f4b31dd403 Mon Sep 17 00:00:00 2001 From: victhor Date: Sat, 21 Mar 2026 00:49:28 +0100 Subject: [PATCH 40/44] reads to CCRx are done through volatile uint32_t now --- Src/HALAL/Models/TimerDomain/TimerDomain.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Src/HALAL/Models/TimerDomain/TimerDomain.cpp b/Src/HALAL/Models/TimerDomain/TimerDomain.cpp index a8d8bfa2f..73ea23f6c 100644 --- a/Src/HALAL/Models/TimerDomain/TimerDomain.cpp +++ b/Src/HALAL/Models/TimerDomain/TimerDomain.cpp @@ -35,7 +35,7 @@ static void TIM_IC_CaptureCallback(const uint32_t timer_idx, uint32_t channel) { if (info->channel_rising == channel) { // NOTE: CCR1 - CCR4 are contiguous // NOTE: CCxIF flag is cleared by software by reading the captured data in CCRx - uint32_t current = (*(((uint32_t*)&htim->Instance->CCR1) + channel)); + uint32_t current = (*(((volatile uint32_t*)&htim->Instance->CCR1) + channel)); uint32_t period = current - info->value_rising; if ((period != 0) && (info->value_falling < period)) { @@ -48,7 +48,7 @@ static void TIM_IC_CaptureCallback(const uint32_t timer_idx, uint32_t channel) { info->value_rising = current; } else if (info->channel_falling == channel) { uint32_t falling_value = - *(((uint32_t*)&htim->Instance->CCR1) + channel) - info->value_rising; + *(((volatile uint32_t*)&htim->Instance->CCR1) + channel) - info->value_rising; if (falling_value < info->period) info->value_falling = falling_value; } else [[unlikely]] { From 99dbd3f9107ccb568b93de2bfbecfb85f0d4308c Mon Sep 17 00:00:00 2001 From: victhor Date: Sat, 21 Mar 2026 00:52:42 +0100 Subject: [PATCH 41/44] fix typo --- Inc/HALAL/Services/PWM/DualPWM.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Inc/HALAL/Services/PWM/DualPWM.hpp b/Inc/HALAL/Services/PWM/DualPWM.hpp index 54fb30fe0..b3fed4132 100644 --- a/Inc/HALAL/Services/PWM/DualPWM.hpp +++ b/Inc/HALAL/Services/PWM/DualPWM.hpp @@ -144,7 +144,7 @@ class DualPWM { ); HAL_TIM_ChannelStateTypeDef* state = - &timer->instance->hal_tim.ChannelState[TimerDomain::get_channel_state_idx(pin.channel)]; + &timer->instance->hal_tim->ChannelState[TimerDomain::get_channel_state_idx(pin.channel)]; *state = HAL_TIM_CHANNEL_STATE_READY; if (timer->are_all_channels_free()) { From d580723059896ad84e9ce6f2226e62f2cda6a9a6 Mon Sep 17 00:00:00 2001 From: victhor Date: Sat, 21 Mar 2026 00:56:24 +0100 Subject: [PATCH 42/44] fix compilation error --- Inc/HALAL/Services/InputCapture/InputCapture.hpp | 2 +- Src/HALAL/Models/TimerDomain/TimerDomain.cpp | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Inc/HALAL/Services/InputCapture/InputCapture.hpp b/Inc/HALAL/Services/InputCapture/InputCapture.hpp index 702ad852b..ecc78346f 100644 --- a/Inc/HALAL/Services/InputCapture/InputCapture.hpp +++ b/Inc/HALAL/Services/InputCapture/InputCapture.hpp @@ -95,7 +95,7 @@ class InputCapture { enableCCx_1 = TIM_CCER_CC1E << (TimerDomain::get_channel_mul4(pin_rising.channel) & 0x1FU ); /* 0x1FU = 31 bits max shift */ - SET_BIT(timer->instance->tim->CCER, enableCCx); + SET_BIT(timer->instance->tim->CCER, enableCCx_1); } // HAL_TIM_IC_Start_IT(instance.peripheral->handle, instance.channel_falling) diff --git a/Src/HALAL/Models/TimerDomain/TimerDomain.cpp b/Src/HALAL/Models/TimerDomain/TimerDomain.cpp index 73ea23f6c..3c4b3fada 100644 --- a/Src/HALAL/Models/TimerDomain/TimerDomain.cpp +++ b/Src/HALAL/Models/TimerDomain/TimerDomain.cpp @@ -23,8 +23,11 @@ TIM_HandleTypeDef htim24; void (*TimerDomain::callbacks[TimerDomain::max_instances])(void*) = {nullptr}; void* TimerDomain::callback_data[TimerDomain::max_instances] = {nullptr}; + +TimerDomain::InputCaptureInfo input_capture_info_dummy = {0}; + TimerDomain::InputCaptureInfo* TimerDomain::input_capture_info[max_instances] - [input_capture_channels]; + [input_capture_channels] = {&input_capture_info_dummy}; TimerDomain::InputCaptureInfo TimerDomain::input_capture_info_backing[max_instances] [input_capture_channels]; From eb38381185528d0d68fcfa4359ec76eebff5642b Mon Sep 17 00:00:00 2001 From: victhor Date: Sat, 21 Mar 2026 01:01:19 +0100 Subject: [PATCH 43/44] formatting --- Inc/HALAL/Models/TimerDomain/TimerDomain.hpp | 7 ++++--- Inc/HALAL/Services/InputCapture/InputCapture.hpp | 10 ++++++---- Inc/HALAL/Services/PWM/DualPWM.hpp | 5 +++-- Src/HALAL/Models/TimerDomain/TimerDomain.cpp | 4 +++- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp b/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp index 44a33ede9..632c5845a 100644 --- a/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp +++ b/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp @@ -709,10 +709,7 @@ struct TimerDomain { static void init(std::span cfgs) { Scheduler_global_timer = cmsis_timers[timer_idxmap[SCHEDULER_TIMER_DOMAIN]]; - callbacks[ST_LIB::timer_idxmap[SCHEDULER_TIMER_DOMAIN]] = - Scheduler_global_timer_callback; rcc_enable_timer(Scheduler_global_timer); - Scheduler_start(); TimerDomain::callbacks[0] = TIM_Default_Callback; TimerDomain::callbacks[1] = TIM_Default_Callback; @@ -731,6 +728,10 @@ struct TimerDomain { TimerDomain::callbacks[14] = TIM_Default_Callback; TimerDomain::callbacks[15] = TIM_Default_Callback; + callbacks[ST_LIB::timer_idxmap[SCHEDULER_TIMER_DOMAIN]] = + Scheduler_global_timer_callback; + Scheduler_start(); + for (std::size_t i = 0; i < N; i++) { const Config& e = cfgs[i]; diff --git a/Inc/HALAL/Services/InputCapture/InputCapture.hpp b/Inc/HALAL/Services/InputCapture/InputCapture.hpp index ecc78346f..8b3908e57 100644 --- a/Inc/HALAL/Services/InputCapture/InputCapture.hpp +++ b/Inc/HALAL/Services/InputCapture/InputCapture.hpp @@ -78,9 +78,11 @@ class InputCapture { volatile HAL_TIM_ChannelStateTypeDef* chx_1_n_state; uint32_t enableCCx_1; { - chx_1_state = &timer->instance->hal_tim + chx_1_state = + &timer->instance->hal_tim ->ChannelState[TimerDomain::get_channel_state_idx(pin_rising.channel)]; - chx_1_n_state = &timer->instance->hal_tim + chx_1_n_state = + &timer->instance->hal_tim ->ChannelNState[TimerDomain::get_channel_state_idx(pin_rising.channel)]; if ((*chx_1_state != HAL_TIM_CHANNEL_STATE_READY) || (*chx_1_n_state != HAL_TIM_CHANNEL_STATE_READY)) { @@ -93,8 +95,8 @@ class InputCapture { timer->template enable_capture_compare_interrupt(); enableCCx_1 = TIM_CCER_CC1E - << (TimerDomain::get_channel_mul4(pin_rising.channel) & 0x1FU - ); /* 0x1FU = 31 bits max shift */ + << (TimerDomain::get_channel_mul4(pin_rising.channel) & 0x1FU + ); /* 0x1FU = 31 bits max shift */ SET_BIT(timer->instance->tim->CCER, enableCCx_1); } diff --git a/Inc/HALAL/Services/PWM/DualPWM.hpp b/Inc/HALAL/Services/PWM/DualPWM.hpp index b3fed4132..b0121ec3c 100644 --- a/Inc/HALAL/Services/PWM/DualPWM.hpp +++ b/Inc/HALAL/Services/PWM/DualPWM.hpp @@ -144,7 +144,8 @@ class DualPWM { ); HAL_TIM_ChannelStateTypeDef* state = - &timer->instance->hal_tim->ChannelState[TimerDomain::get_channel_state_idx(pin.channel)]; + &timer->instance->hal_tim + ->ChannelState[TimerDomain::get_channel_state_idx(pin.channel)]; *state = HAL_TIM_CHANNEL_STATE_READY; if (timer->are_all_channels_free()) { @@ -170,7 +171,7 @@ class DualPWM { HAL_TIM_ChannelStateTypeDef* state = &timer->instance->hal_tim - .ChannelState[TimerDomain::get_channel_state_idx(negated_pin.channel)]; + ->ChannelState[TimerDomain::get_channel_state_idx(negated_pin.channel)]; *state = HAL_TIM_CHANNEL_STATE_READY; if (timer->are_all_channels_free()) { diff --git a/Src/HALAL/Models/TimerDomain/TimerDomain.cpp b/Src/HALAL/Models/TimerDomain/TimerDomain.cpp index 3c4b3fada..b1ce1df1a 100644 --- a/Src/HALAL/Models/TimerDomain/TimerDomain.cpp +++ b/Src/HALAL/Models/TimerDomain/TimerDomain.cpp @@ -27,7 +27,9 @@ void* TimerDomain::callback_data[TimerDomain::max_instances] = {nullptr}; TimerDomain::InputCaptureInfo input_capture_info_dummy = {0}; TimerDomain::InputCaptureInfo* TimerDomain::input_capture_info[max_instances] - [input_capture_channels] = {&input_capture_info_dummy}; + [input_capture_channels] = { + &input_capture_info_dummy +}; TimerDomain::InputCaptureInfo TimerDomain::input_capture_info_backing[max_instances] [input_capture_channels]; From 03e49c82764d6def08ea46e67db7f77356c67fee Mon Sep 17 00:00:00 2001 From: victhor Date: Sat, 21 Mar 2026 01:03:07 +0100 Subject: [PATCH 44/44] formatting --- Inc/HALAL/Services/InputCapture/InputCapture.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Inc/HALAL/Services/InputCapture/InputCapture.hpp b/Inc/HALAL/Services/InputCapture/InputCapture.hpp index 8b3908e57..d031c9c87 100644 --- a/Inc/HALAL/Services/InputCapture/InputCapture.hpp +++ b/Inc/HALAL/Services/InputCapture/InputCapture.hpp @@ -95,8 +95,8 @@ class InputCapture { timer->template enable_capture_compare_interrupt(); enableCCx_1 = TIM_CCER_CC1E - << (TimerDomain::get_channel_mul4(pin_rising.channel) & 0x1FU - ); /* 0x1FU = 31 bits max shift */ + << (TimerDomain::get_channel_mul4(pin_rising.channel) & 0x1FU + ); /* 0x1FU = 31 bits max shift */ SET_BIT(timer->instance->tim->CCER, enableCCx_1); }