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 diff --git a/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp b/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp index 45d9f3d0a..632c5845a 100644 --- a/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp +++ b/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp @@ -53,364 +53,394 @@ void Scheduler_start(void); 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 #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, - }; - - // 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, +/* 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 + */ - CHANNEL_NEGATED_FLAG = 8, +/* 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) + */ - CHANNEL_1_NEGATED = 1 | CHANNEL_NEGATED_FLAG, - CHANNEL_2_NEGATED = 2 | CHANNEL_NEGATED_FLAG, - CHANNEL_3_NEGATED = 3 | 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 */ + }; + + struct Config { + uint8_t timer_idx; }; - 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; + 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(); + &htim1, + &htim8 + }; - struct TimerDomain { - // There are 16 timers - static constexpr std::size_t max_instances = 16; +#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, - 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; - - // TODO: check what this really needs to be for each - case TimerAF::InputCapture: - return GPIODomain::OperationMode::OUTPUT_OPENDRAIN; - - 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; + 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::PWM: + return GPIODomain::Pull::None; + case TimerAF::Encoder: + return GPIODomain::Pull::Up; - // TODO: check what this really needs to be for each - case TimerAF::InputCapture: - return GPIODomain::Pull::Up; + case TimerAF::InputCapture: + return GPIODomain::Pull::None; - case TimerAF::BreakInput: - return GPIODomain::Pull::None; - case TimerAF::BreakInputCompare: - 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; + 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::PWM: + return GPIODomain::Speed::Low; + case TimerAF::Encoder: + return GPIODomain::Speed::Low; - // TODO: check what this really needs to be for each - case TimerAF::InputCapture: - return GPIODomain::Speed::Low; + case TimerAF::InputCapture: + return GPIODomain::Speed::Low; - case TimerAF::BreakInput: - return GPIODomain::Speed::Low; - case TimerAF::BreakInputCompare: - 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 @@ -428,332 +458,416 @@ TimerXList (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; + + // unordered remove + count_remaining_requests--; + remaining_requests[i] = remaining_requests[count_remaining_requests]; + } else { + i++; } + } + + // can use any CountingMode (32 bit timers can also but they are higher priority) + uint8_t up_down_updown_timers[] = {3, 4}; - return cfgs; + // 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; + + 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]; } - // Runtime object - struct Instance { - TIM_TypeDef* tim; - TIM_HandleTypeDef* hal_tim; - uint8_t timer_idx; - }; + 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 void (*callbacks[TimerDomain::max_instances])(void*); - static void* callback_data[TimerDomain::max_instances]; - - template struct Init { - static inline std::array instances{}; - - 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(); - - 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; - } + 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"); } - }; + 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; }; - 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, - }; + 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) { + Scheduler_global_timer = cmsis_timers[timer_idxmap[SCHEDULER_TIMER_DOMAIN]]; + rcc_enable_timer(Scheduler_global_timer); + + TimerDomain::callbacks[0] = TIM_Default_Callback; + 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; + + callbacks[ST_LIB::timer_idxmap[SCHEDULER_TIMER_DOMAIN]] = + Scheduler_global_timer_callback; + Scheduler_start(); - struct TimerPossiblePin { - ST_LIB::GPIODomain::Pin pin; - ST_LIB::GPIODomain::AlternateFunction af; - TimerAF_Use use; - }; + 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; + } + } + }; - // 4 capture-compare channels - // complementary output + 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; + } + } + + 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"); + } + + 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; + }; + + // 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}, \ @@ -796,7 +910,7 @@ TimerXList {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}, \ @@ -813,7 +927,7 @@ TimerXList {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}, \ @@ -830,7 +944,7 @@ TimerXList \ {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}, \ @@ -844,7 +958,7 @@ TimerXList \ {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}, \ @@ -852,15 +966,15 @@ TimerXList {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}, \ @@ -891,22 +1005,22 @@ TimerXList \ {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}, \ \ @@ -923,13 +1037,13 @@ TimerXList {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}, \ @@ -938,7 +1052,7 @@ TimerXList {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}, \ @@ -956,7 +1070,7 @@ TimerXList {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}, \ @@ -971,68 +1085,68 @@ TimerXList 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) && @@ -1041,18 +1155,17 @@ TimerXList ((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/InputCapture/InputCapture.hpp b/Inc/HALAL/Services/InputCapture/InputCapture.hpp new file mode 100644 index 000000000..d031c9c87 --- /dev/null +++ b/Inc/HALAL/Services/InputCapture/InputCapture.hpp @@ -0,0 +1,206 @@ +/* + * 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; + +/* 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::TimerChannel channel_falling> +class InputCapture { + friend struct TimerWrapper; + + TimerWrapper* timer = nullptr; + TimerDomain::InputCaptureInfo* info = nullptr; + bool is_on = false; + + 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][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 = ch_rising; + info->channel_falling = ch_falling; + + info->value_falling = 0; + info->duty_cycle = 0.0f; + info->value_rising = 0; + info->frequency = 0; + info->period = 0; + + timer->enable_nvic(); + + TIM_IC_InitTypeDef sConfigIC = { + .ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING, + .ICSelection = TIM_ICSELECTION_DIRECTTI, + .ICPrescaler = TIM_ICPSC_DIV1, + .ICFilter = 0, + }; + timer->template config_input_compare_channel(&sConfigIC); + + sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_FALLING; + sConfigIC.ICSelection = TIM_ICSELECTION_INDIRECTTI; + timer->template config_input_compare_channel(&sConfigIC); + } + +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 */ + 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* chx_1_state; + volatile HAL_TIM_ChannelStateTypeDef* chx_1_n_state; + uint32_t enableCCx_1; + { + chx_1_state = + &timer->instance->hal_tim + ->ChannelState[TimerDomain::get_channel_state_idx(pin_rising.channel)]; + 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)) { + ErrorHandler("Channels not ready"); + return; + } + + *chx_1_state = HAL_TIM_CHANNEL_STATE_BUSY; + *chx_1_n_state = HAL_TIM_CHANNEL_STATE_BUSY; + + timer->template enable_capture_compare_interrupt(); + 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_1); + } + + // 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)]; + 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)) [[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; + } + + *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 */ + SET_BIT(timer->instance->tim->CCER, enableCCx); + } + + if constexpr (timer->is_slave_instance) { + uint32_t tmpsmcr = timer->instance->tim->SMCR & TIM_SMCR_SMS; + if (!IS_TIM_SLAVEMODE_TRIGGER_ENABLED(tmpsmcr)) { + timer->counter_enable(); + } + } else { + timer->counter_enable(); + } + + is_on = true; + } + + void turn_off(void) { + 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)) + ); + + 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)]; + *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)) + ); + + volatile HAL_TIM_ChannelStateTypeDef* ch_state = + &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)]; + *ch_state = HAL_TIM_CHANNEL_STATE_READY; + *n_ch_state = HAL_TIM_CHANNEL_STATE_READY; + } + + if (timer->are_all_channels_free()) { + timer->counter_disable(); + } + + is_on = false; + } + + 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 + 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 f4a315535..b0121ec3c 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, @@ -114,18 +70,16 @@ 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); @@ -150,18 +104,16 @@ 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); @@ -188,11 +140,12 @@ 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()) { @@ -212,11 +165,13 @@ 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 9c3da2ff0..305e4251c 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, @@ -96,18 +52,16 @@ 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); @@ -134,11 +88,12 @@ 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..dea8fe595 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,32 @@ 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); + /* 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 || @@ -274,6 +301,75 @@ 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(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 */ + 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)) <= 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); } @@ -368,8 +464,32 @@ 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 +554,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 +709,108 @@ 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) & TIM_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) & TIM_CCMR1_IC2PSC); + } 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) & TIM_CCMR2_IC3PSC); + } 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) & TIM_CCMR2_IC4PSC); + } + + 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); diff --git a/Inc/MockedDrivers/stm32h7xx_hal_mock.h b/Inc/MockedDrivers/stm32h7xx_hal_mock.h index 5efcd616c..c4163a2a8 100644 --- a/Inc/MockedDrivers/stm32h7xx_hal_mock.h +++ b/Inc/MockedDrivers/stm32h7xx_hal_mock.h @@ -668,6 +668,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; @@ -717,7 +724,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 @@ -757,25 +763,41 @@ 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) #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_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_IC4PSC (3U << 10) #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) @@ -796,6 +818,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) @@ -804,6 +827,19 @@ 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_ICSELECTION_DIRECTTI TIM_CCMR1_CC1S_0 +#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 diff --git a/Src/HALAL/Models/TimerDomain/TimerDomain.cpp b/Src/HALAL/Models/TimerDomain/TimerDomain.cpp index cdafeeef2..b1ce1df1a 100644 --- a/Src/HALAL/Models/TimerDomain/TimerDomain.cpp +++ b/Src/HALAL/Models/TimerDomain/TimerDomain.cpp @@ -2,105 +2,200 @@ 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_SR_CC1IF | TIM_SR_CC2IF | TIM_SR_CC3IF | TIM_SR_CC4IF) + +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}; -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]]); +TimerDomain::InputCaptureInfo input_capture_info_dummy = {0}; + +TimerDomain::InputCaptureInfo* TimerDomain::input_capture_info[max_instances] + [input_capture_channels] = { + &input_capture_info_dummy +}; +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) { + TIM_HandleTypeDef* htim = TimerDomain::hal_handles[timer_idx]; + + 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 + uint32_t current = (*(((volatile 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; + } + info->value_rising = current; + } else if (info->channel_falling == channel) { + uint32_t falling_value = + *(((volatile 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"); + } } -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]]); -} +static void TIM_InterruptCallback(const uint32_t timer_idx) { + TIM_TypeDef* tim = TimerDomain::cmsis_timers[timer_idx]; + if (tim->SR & TIM_SR_UIF) { + CLEAR_BIT(tim->SR, TIM_SR_UIF); + TimerDomain::callbacks[timer_idx](TimerDomain::callback_data[timer_idx]); + } -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]]); + // 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); + } + } } -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]]); -} +extern "C" void TIM1_UP_IRQHandler(void) { TIM_InterruptCallback(timer_idxmap[1]); } -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]]); -} +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 TIM4_IRQHandler(void) { TIM_InterruptCallback(timer_idxmap[4]); } + +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 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]; + + if ((tim12->SR & TIM_SR_UIF) != 0) { + CLEAR_BIT(tim12->SR, TIM_SR_UIF); + TimerDomain::callbacks[tim12_idx](TimerDomain::callback_data[tim12_idx]); + } + // 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 ((TimerDomain::cmsis_timers[timer_idxmap[8]]->SR & TIM_SR_BIF) != 0) { - CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[8]]->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[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]; + + if ((tim13->SR & TIM_SR_UIF) != 0) { + CLEAR_BIT(tim13->SR, TIM_SR_UIF); + TimerDomain::callbacks[tim13_idx](TimerDomain::callback_data[tim13_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]]); + // 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]); + } + // 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) { - 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]; + + if ((tim14->SR & TIM_SR_UIF) != 0) { + CLEAR_BIT(tim14->SR, TIM_SR_UIF); + TimerDomain::callbacks[tim14_idx](TimerDomain::callback_data[tim14_idx]); } + // 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; - 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 ((tim8->SR & com_trg_flags) != 0) { + CLEAR_BIT(tim8->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]]); -} +extern "C" void TIM15_IRQHandler(void) { 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]]); -} +extern "C" void TIM16_IRQHandler(void) { 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]]); -} +extern "C" void TIM17_IRQHandler(void) { 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]]); -} +extern "C" void TIM23_IRQHandler(void) { 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]]); -} +extern "C" void TIM24_IRQHandler(void) { TIM_InterruptCallback(timer_idxmap[24]); }