From 33acc0d65702492a039eb3820a13244a7e410735 Mon Sep 17 00:00:00 2001 From: Sagah123 Date: Sat, 17 Oct 2026 01:55:04 +0200 Subject: [PATCH 01/19] Modify pulse init value with duty_cycle instead of 0 --- Inc/HALAL/Services/PWM/PWM.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Inc/HALAL/Services/PWM/PWM.hpp b/Inc/HALAL/Services/PWM/PWM.hpp index 79e6b7fd7..8abd18622 100644 --- a/Inc/HALAL/Services/PWM/PWM.hpp +++ b/Inc/HALAL/Services/PWM/PWM.hpp @@ -42,7 +42,7 @@ template class PWM { } TIM_OC_InitTypeDef sConfigOC = { .OCMode = TIM_OCMODE_PWM1, - .Pulse = 0, + .Pulse = (uint32_t) this->get_duty_cycle(), .OCPolarity = polarity, .OCNPolarity = negated_polarity, From 6683848badca37589032f25513fca1ca4fd0757a Mon Sep 17 00:00:00 2001 From: Sagah123 Date: Sat, 17 Oct 2026 02:39:51 +0200 Subject: [PATCH 02/19] Fix PWM duty cycle being overwritten on init Add a function that converts a duty cycle percentage into duty cycle ticks that helps to initialize correctly .Pulse without hardcoding it --- Inc/HALAL/Services/PWM/PWM.hpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Inc/HALAL/Services/PWM/PWM.hpp b/Inc/HALAL/Services/PWM/PWM.hpp index 8abd18622..e4127d6a5 100644 --- a/Inc/HALAL/Services/PWM/PWM.hpp +++ b/Inc/HALAL/Services/PWM/PWM.hpp @@ -42,7 +42,7 @@ template class PWM { } TIM_OC_InitTypeDef sConfigOC = { .OCMode = TIM_OCMODE_PWM1, - .Pulse = (uint32_t) this->get_duty_cycle(), + .Pulse = this->get_ticks_duty_cycle(), .OCPolarity = polarity, .OCNPolarity = negated_polarity, @@ -56,6 +56,7 @@ template class PWM { is_initialized = true; } + public: void turn_on() { if (this->is_initialized == false) { @@ -153,6 +154,13 @@ template class PWM { inline uint32_t get_frequency() const { return *(this->frequency); } inline float get_duty_cycle() const { return *(this->duty_cycle); } + inline uint32_t get_ticks_duty_cycle() { + if constexpr (timer->is_32bit_instance) { + return (uint32_t)((float)(timer->instance->tim->ARR + 1) * (*(this->duty_cycle) / 100.0f)); + } else { + return (uint16_t)((float)(timer->instance->tim->ARR + 1) * (*(this->duty_cycle) / 100.0f)); + } + } }; } // namespace ST_LIB From 33beac951e0a3d6db2ad5e981285c767b610236d Mon Sep 17 00:00:00 2001 From: Sagah123 Date: Sat, 17 Oct 2026 03:06:07 +0200 Subject: [PATCH 03/19] Add changeset and apply clangformat --- .changesets/fix-pwm-overwrite-on-init.md | 4 ++++ Inc/HALAL/Services/PWM/PWM.hpp | 7 ++++--- 2 files changed, 8 insertions(+), 3 deletions(-) create mode 100644 .changesets/fix-pwm-overwrite-on-init.md diff --git a/.changesets/fix-pwm-overwrite-on-init.md b/.changesets/fix-pwm-overwrite-on-init.md new file mode 100644 index 000000000..4740ba77f --- /dev/null +++ b/.changesets/fix-pwm-overwrite-on-init.md @@ -0,0 +1,4 @@ +release: patch +summary: Fix PWM duty cycle being overwritten with 0 on init by computing .Pulse in timer ticks from the stored duty cycle + +PWM::init() used to pass Pulse = 0 to config_output_compare_channel, which overwrote CCRx after the duty cycle had been set. Added get_ticks_duty_cycle() to convert the duty cycle percentage into timer ticks and use it as Pulse. diff --git a/Inc/HALAL/Services/PWM/PWM.hpp b/Inc/HALAL/Services/PWM/PWM.hpp index e4127d6a5..2a7388da8 100644 --- a/Inc/HALAL/Services/PWM/PWM.hpp +++ b/Inc/HALAL/Services/PWM/PWM.hpp @@ -56,7 +56,6 @@ template class PWM { is_initialized = true; } - public: void turn_on() { if (this->is_initialized == false) { @@ -156,9 +155,11 @@ template class PWM { inline float get_duty_cycle() const { return *(this->duty_cycle); } inline uint32_t get_ticks_duty_cycle() { if constexpr (timer->is_32bit_instance) { - return (uint32_t)((float)(timer->instance->tim->ARR + 1) * (*(this->duty_cycle) / 100.0f)); + return (uint32_t)((float)(timer->instance->tim->ARR + 1) * + (*(this->duty_cycle) / 100.0f)); } else { - return (uint16_t)((float)(timer->instance->tim->ARR + 1) * (*(this->duty_cycle) / 100.0f)); + return (uint16_t)((float)(timer->instance->tim->ARR + 1) * + (*(this->duty_cycle) / 100.0f)); } } }; From 6f8c60bcffc20cb206e492545fbc14e475219d00 Mon Sep 17 00:00:00 2001 From: Sagah123 Date: Sat, 17 Oct 2026 15:13:14 +0200 Subject: [PATCH 04/19] Implement get_ccrx and add it to .Pulse initialization in pwm and dual_pwm --- Inc/HALAL/Services/PWM/DualPWM.hpp | 2 +- Inc/HALAL/Services/PWM/PWM.hpp | 13 +------------ Inc/HALAL/Services/Time/TimerWrapper.hpp | 18 ++++++++++++++++++ 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/Inc/HALAL/Services/PWM/DualPWM.hpp b/Inc/HALAL/Services/PWM/DualPWM.hpp index 09de02378..8afab44e2 100644 --- a/Inc/HALAL/Services/PWM/DualPWM.hpp +++ b/Inc/HALAL/Services/PWM/DualPWM.hpp @@ -51,7 +51,7 @@ class DualPWM { } TIM_OC_InitTypeDef sConfigOC = { .OCMode = TIM_OCMODE_PWM1, - .Pulse = 0, + .Pulse = timer->template get_ccrx(), .OCPolarity = polarity, .OCNPolarity = negated_polarity, diff --git a/Inc/HALAL/Services/PWM/PWM.hpp b/Inc/HALAL/Services/PWM/PWM.hpp index 2a7388da8..9e42db67f 100644 --- a/Inc/HALAL/Services/PWM/PWM.hpp +++ b/Inc/HALAL/Services/PWM/PWM.hpp @@ -42,11 +42,9 @@ template class PWM { } TIM_OC_InitTypeDef sConfigOC = { .OCMode = TIM_OCMODE_PWM1, - .Pulse = this->get_ticks_duty_cycle(), - + .Pulse = timer->template get_ccrx(), .OCPolarity = polarity, .OCNPolarity = negated_polarity, - .OCFastMode = TIM_OCFAST_DISABLE, .OCIdleState = TIM_OCIDLESTATE_RESET, .OCNIdleState = TIM_OCNIDLESTATE_RESET, @@ -153,15 +151,6 @@ template class PWM { inline uint32_t get_frequency() const { return *(this->frequency); } inline float get_duty_cycle() const { return *(this->duty_cycle); } - inline uint32_t get_ticks_duty_cycle() { - if constexpr (timer->is_32bit_instance) { - return (uint32_t)((float)(timer->instance->tim->ARR + 1) * - (*(this->duty_cycle) / 100.0f)); - } else { - return (uint16_t)((float)(timer->instance->tim->ARR + 1) * - (*(this->duty_cycle) / 100.0f)); - } - } }; } // namespace ST_LIB diff --git a/Inc/HALAL/Services/Time/TimerWrapper.hpp b/Inc/HALAL/Services/Time/TimerWrapper.hpp index 051849bb4..4847094ed 100644 --- a/Inc/HALAL/Services/Time/TimerWrapper.hpp +++ b/Inc/HALAL/Services/Time/TimerWrapper.hpp @@ -715,6 +715,24 @@ template struct TimerWrapper { instance->tim->CCER = tmpccer; } + template inline uint32_t get_ccrx() { + if constexpr ((ch == TimerChannel::CHANNEL_1) || (ch == TimerChannel::CHANNEL_1_NEGATED)) + return instance->tim->CCR1; + else if constexpr ((ch == TimerChannel::CHANNEL_2) || (ch == TimerChannel::CHANNEL_2_NEGATED)) + return instance->tim->CCR2; + else if constexpr ((ch == TimerChannel::CHANNEL_3) || (ch == TimerChannel::CHANNEL_3_NEGATED)) + return instance->tim->CCR3; + else if constexpr (ch == TimerChannel::CHANNEL_4) + return instance->tim->CCR4; + else if constexpr (ch == TimerChannel::CHANNEL_5) + return instance->tim->CCR5; + else if constexpr (ch == TimerChannel::CHANNEL_6) + return instance->tim->CCR6; + else { + ST_LIB::compile_error("Unknown timer channel, there are only 6 channels [1..6]"); + return 0; + } + } /* 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 */ From 0de3a89e38d24a8cec27421b2f4e73eaf7ad3644 Mon Sep 17 00:00:00 2001 From: Aleksandr Globa <106732173+Sagah123@users.noreply.github.com> Date: Sat, 17 Oct 2026 17:15:31 +0200 Subject: [PATCH 05/19] Update TimerWrapper.hpp --- Inc/HALAL/Services/Time/TimerWrapper.hpp | 1769 +++++++++++----------- 1 file changed, 885 insertions(+), 884 deletions(-) diff --git a/Inc/HALAL/Services/Time/TimerWrapper.hpp b/Inc/HALAL/Services/Time/TimerWrapper.hpp index 4847094ed..d2351f0e7 100644 --- a/Inc/HALAL/Services/Time/TimerWrapper.hpp +++ b/Inc/HALAL/Services/Time/TimerWrapper.hpp @@ -1,884 +1,885 @@ -/* - * TimerWrapper.hpp - * - * Created on: 30 dic. 2025 - * Author: victor - */ - -#pragma once - -#include "hal_wrapper.h" - -#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" -#include "HALAL/Models/GPIO.hpp" - -#include "ErrorHandler/ErrorHandler.hpp" - -#define get_timer_instance(board, timer_type) \ - ST_LIB::TimerWrapper(&board::instance_of()) - -namespace ST_LIB { - -template struct TimerWrapper { - TimerDomain::Instance* instance = nullptr; - TimerWrapper() = default; - TimerWrapper(TimerDomain::Instance* inst) : instance(inst) {} - - static constexpr int MAX_pwm_channel_duties = 4; - float pwm_channel_duties[MAX_pwm_channel_duties] = {0.0f, 0.0f, 0.0f, 0.0f}; - uint32_t pwm_frequency = 0; - - enum CountingMode : uint8_t { - UP = 0, - DOWN = 1, - /* center-aligned = counter counts up and down alternatively */ - /* Output compare interrupt flags of channels configured in output (CCxS=00 in TIMx_CCMRx - register) are set only when the counter is counting down */ - CENTER_ALIGNED_INTERRUPT_DOWN = 2, - /* Output compare interrupt flags of channels configured in output (CCxS=00 in TIMx_CCMRx - register) are set only when the counter is counting up */ - CENTER_ALIGNED_INTERRUPT_UP = 3, - /* both up and down */ - CENTER_ALIGNED_INTERRUPT_BOTH = 4, - }; - - static constexpr bool is_32bit_instance = - (dev.e.request == TimerRequest::GeneralPurpose32bit_2 || - dev.e.request == TimerRequest::GeneralPurpose32bit_5 || - dev.e.request == TimerRequest::GeneralPurpose32bit_23 || - dev.e.request == TimerRequest::GeneralPurpose32bit_24 || - dev.e.request == TimerRequest::Any32bit); - /* supports break input */ - static constexpr bool is_break_instance = - (dev.e.request == TimerRequest::Advanced_1 || dev.e.request == TimerRequest::Advanced_8 || - 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 || - 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 only_supports_upcounting = - (dev.e.request == TimerRequest::Basic_6 || dev.e.request == TimerRequest::Basic_7 || - dev.e.request == TimerRequest::SlaveTimer_12 || - dev.e.request == TimerRequest::SlaveTimer_13 || - dev.e.request == TimerRequest::SlaveTimer_14 || - dev.e.request == TimerRequest::GeneralPurpose_15 || - dev.e.request == TimerRequest::GeneralPurpose_16 || - dev.e.request == TimerRequest::GeneralPurpose_17); - static constexpr bool is_on_APB1 = - (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::Basic_6 || dev.e.request == TimerRequest::Basic_7 || - dev.e.request == TimerRequest::SlaveTimer_12 || - dev.e.request == TimerRequest::SlaveTimer_13 || - dev.e.request == TimerRequest::SlaveTimer_14); - - /* returns if the channel can be negated or not {See TimerDomain get_gpio_af()} */ - static consteval bool is_ccxn_instance(ST_LIB::TimerChannel ch) { - switch (dev.e.request) { - case TimerRequest::Advanced_1: - case TimerRequest::Advanced_8: - return (ch == TimerChannel::CHANNEL_1) || (ch == TimerChannel::CHANNEL_1_NEGATED) || - (ch == TimerChannel::CHANNEL_2) || (ch == TimerChannel::CHANNEL_2_NEGATED) || - (ch == TimerChannel::CHANNEL_3) || (ch == TimerChannel::CHANNEL_3_NEGATED); - - case TimerRequest::GeneralPurpose_15: - case TimerRequest::GeneralPurpose_16: - case TimerRequest::GeneralPurpose_17: - return (ch == TimerChannel::CHANNEL_1) || (ch == TimerChannel::CHANNEL_1_NEGATED); - - default: - return false; - } - } - - inline uint32_t get_clock_frequency() { - uint32_t result; - if constexpr (this->is_on_APB1) { - result = HAL_RCC_GetPCLK1Freq(); - if ((RCC->D2CFGR & RCC_D2CFGR_D2PPRE1) != RCC_HCLK_DIV1) { - result *= 2; - } - } else { - result = HAL_RCC_GetPCLK2Freq(); - if ((RCC->D2CFGR & RCC_D2CFGR_D2PPRE2) != RCC_HCLK_DIV1) { - result *= 2; - } - } - return result; - } - - template - inline PWM get_pwm( - uint32_t polarity = TIM_OCPOLARITY_HIGH, - uint32_t negated_polarity = TIM_OCNPOLARITY_HIGH - ) { - static_assert(dev.e.pin_count > 0, "Need at least one pin to get a pwm"); - - if constexpr (dev.e.pins[0].pin == pin.pin && dev.e.pins[0].channel == pin.channel) { - if constexpr (dev.e.pins[0].af != TimerAF::PWM) { - ST_LIB::compile_error("Pin must be configured in TimerDomain as a PWM"); - } - return PWM( - this, - polarity, - negated_polarity, - &pwm_channel_duties - [(static_cast(pin.channel) & - ~static_cast(TimerChannel::CHANNEL_NEGATED_FLAG)) - - 1], - &pwm_frequency - ); - } else if constexpr (dev.e.pin_count > 1 && dev.e.pins[1].pin == pin.pin && dev.e.pins[1].channel == pin.channel) { - if constexpr (dev.e.pins[1].af != TimerAF::PWM) { - ST_LIB::compile_error("Pin must be configured in TimerDomain as a PWM"); - } - return PWM( - this, - polarity, - negated_polarity, - &pwm_channel_duties - [(static_cast(pin.channel) & - ~static_cast(TimerChannel::CHANNEL_NEGATED_FLAG)) - - 1], - &pwm_frequency - ); - } else if constexpr (dev.e.pin_count > 2 && dev.e.pins[2].pin == pin.pin && dev.e.pins[2].channel == pin.channel) { - if constexpr (dev.e.pins[2].af != TimerAF::PWM) { - ST_LIB::compile_error("Pin must be configured in TimerDomain as a PWM"); - } - return PWM( - this, - polarity, - negated_polarity, - &pwm_channel_duties - [(static_cast(pin.channel) & - ~static_cast(TimerChannel::CHANNEL_NEGATED_FLAG)) - - 1], - &pwm_frequency - ); - } else if constexpr (dev.e.pin_count > 3 && dev.e.pins[3].pin == pin.pin && dev.e.pins[3].channel == pin.channel) { - if constexpr (dev.e.pins[3].af != TimerAF::PWM) { - ST_LIB::compile_error("Pin must be configured in TimerDomain as a PWM"); - } - return PWM( - this, - polarity, - negated_polarity, - &pwm_channel_duties - [(static_cast(pin.channel) & - ~static_cast(TimerChannel::CHANNEL_NEGATED_FLAG)) - - 1], - &pwm_frequency - ); - } else if constexpr (dev.e.pin_count > 4 && dev.e.pins[4].pin == pin.pin && dev.e.pins[4].channel == pin.channel) { - if constexpr (dev.e.pins[4].af != TimerAF::PWM) { - ST_LIB::compile_error("Pin must be configured in TimerDomain as a PWM"); - } - return PWM( - this, - polarity, - negated_polarity, - &pwm_channel_duties - [(static_cast(pin.channel) & - ~static_cast(TimerChannel::CHANNEL_NEGATED_FLAG)) - - 1], - &pwm_frequency - ); - } else if constexpr (dev.e.pin_count > 5 && dev.e.pins[5].pin == pin.pin && dev.e.pins[5].channel == pin.channel) { - if constexpr (dev.e.pins[5].af != TimerAF::PWM) { - ST_LIB::compile_error("Pin must be configured in TimerDomain as a PWM"); - } - return PWM( - this, - polarity, - negated_polarity, - &pwm_channel_duties - [(static_cast(pin.channel) & - ~static_cast(TimerChannel::CHANNEL_NEGATED_FLAG)) - - 1], - &pwm_frequency - ); - } else if constexpr (dev.e.pin_count > 6 && dev.e.pins[6].pin == pin.pin && dev.e.pins[6].channel == pin.channel) { - if constexpr (dev.e.pins[6].af != TimerAF::PWM) { - ST_LIB::compile_error("Pin must be configured in TimerDomain as a PWM"); - } - return PWM( - this, - polarity, - negated_polarity, - &pwm_channel_duties - [(static_cast(pin.channel) & - ~static_cast(TimerChannel::CHANNEL_NEGATED_FLAG)) - - 1], - &pwm_frequency - ); - } else if constexpr (dev.e.pin_count == 7 && dev.e.pins[7].pin == pin.pin && dev.e.pins[7].channel == pin.channel) { - if constexpr (dev.e.pins[7].af != TimerAF::PWM) { - ST_LIB::compile_error("Pin must be configured in TimerDomain as a PWM"); - } - return PWM( - this, - polarity, - negated_polarity, - &pwm_channel_duties - [(static_cast(pin.channel) & - ~static_cast(TimerChannel::CHANNEL_NEGATED_FLAG)) - - 1], - &pwm_frequency - ); - } else { - ST_LIB::compile_error("No pins passed to TimerWrapper are the same as the pins passed " - "to get_pwm() [this method]"); - } - } - - template - inline DualPWM get_dual_pwm( - uint32_t polarity = TIM_OCPOLARITY_HIGH, - uint32_t negated_polarity = TIM_OCNPOLARITY_HIGH - ) { - static_assert(dev.e.pin_count > 1, "Need at least two pins on one timer to get a dual pwm"); - - // NOTE: No voy a comprobar todo esto, no hará falta comprobarlo con la siguiente versión - // del pwm, dualpwm - static_assert( - (static_cast(pin.channel) | - static_cast(TimerChannel::CHANNEL_NEGATED_FLAG)) == - static_cast(negated_pin.channel) - ); - static_assert(pin.af == TimerAF::PWM && negated_pin.af == TimerAF::PWM); - return DualPWM( - this, - polarity, - negated_polarity, - &pwm_channel_duties[static_cast(pin.channel) - 1], - &pwm_frequency - ); - } - - 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); } - - inline void clear_update_interrupt_flag() { CLEAR_BIT(instance->tim->SR, TIM_SR_UIF); } - - /* Disabled by default */ - inline void enable_update_interrupt() { SET_BIT(instance->tim->DIER, TIM_DIER_UIE); } - inline void disable_update_interrupt() { CLEAR_BIT(instance->tim->DIER, TIM_DIER_UIE); } - - /* Disabled by default */ - inline void enable_nvic() { NVIC_EnableIRQ(TimerDomain::timer_irqn[instance->timer_idx]); } - inline void disable_nvic() { NVIC_DisableIRQ(TimerDomain::timer_irqn[instance->timer_idx]); } - - /* Enable UEV. The Update (UEV) event is generated by one of the following events: - * – Counter overflow/underflow - * – Setting the UG bit - * – Update generation through the slave mode controller - * Enabled by default - */ - inline void enable_update_event() { CLEAR_BIT(instance->tim->CR1, TIM_CR1_UDIS); } - /* Disable UEV. The Update event is not generated, shadow registers keep their value - * (ARR, PSC, CCRx). However the counter and the prescaler are reinitialized if the UG bit - * is set or if a hardware reset is received from the slave mode controller. - */ - inline void disable_update_event() { SET_BIT(instance->tim->CR1, TIM_CR1_UDIS); } - - /* default: disabled */ - inline void break_interrupt_enable() { - static_assert( - dev.e.request == TimerRequest::Advanced_1 || dev.e.request == TimerRequest::Advanced_8, - "Error: Break interrupt enable only allowed in advanced timers {TIM1, TIM8}" - ); - SET_BIT(instance->tim->DIER, TIM_DIER_BIE); - } - inline void break_interrupt_disable() { - static_assert( - dev.e.request == TimerRequest::Advanced_1 || dev.e.request == TimerRequest::Advanced_8, - "Error: Break interrupt enable only allowed in advanced timers {TIM1, TIM8}" - ); - CLEAR_BIT(instance->tim->DIER, TIM_DIER_BIE); - } - - /* interrupt gets called only once, counter needs to be reenabled */ - inline void set_one_pulse_mode() { SET_BIT(instance->tim->CR1, TIM_CR1_OPM); } - inline void set_multi_interrupt_mode() { CLEAR_BIT(instance->tim->CR1, TIM_CR1_OPM); } - - inline TIM_HandleTypeDef* get_hal_handle() { return instance->hal_tim; } - inline TIM_TypeDef* get_cmsis_handle() { return instance->tim; } - - inline void set_prescaler(uint16_t psc) { instance->tim->PSC = psc; } - // TODO: 16 bit and 32 bit version (?) - inline void set_limit_value(uint32_t arr) { instance->tim->ARR = arr; } - - inline void set_callback(void (*callback)(void*), void* callback_data) { - TimerDomain::callback_data[instance->timer_idx] = callback_data; - TimerDomain::callbacks[instance->timer_idx] = callback; - } - - inline void configure32bit(void (*callback)(void*), void* callback_data, uint32_t period) { - static_assert( - this->is_32bit_instance, - "Only timers {TIM2, TIM5, TIM23, TIM24} have a 32-bit resolution" - ); - - instance->tim->ARR = period; - TimerDomain::callback_data[instance->timer_idx] = callback_data; - TimerDomain::callbacks[instance->timer_idx] = callback; - this->counter_enable(); - } - - inline void configure16bit(void (*callback)(void*), void* callback_data, uint16_t period) { - instance->tim->ARR = period; - TimerDomain::callbacks[instance->timer_idx] = callback; - TimerDomain::callback_data[instance->timer_idx] = callback_data; - this->counter_enable(); - } - - /* WARNING: The counter _must_ be disabled to switch from edge-aligned to center-aligned mode */ - template inline void set_counting_mode(void) { - constexpr uint8_t reqint = static_cast(dev.e.request); - static_assert( - !this->only_supports_upcounting, - "Error: In request reqidx: Timers other than {Advanced{TIM1, TIM8}, TIM2, TIM3, TIM4, " - "TIM5, TIM23, TIM24} only support upcounting" - ); - - if constexpr (mode == CountingMode::UP) { - MODIFY_REG(instance->tim->CR1, TIM_CR1_CMS, 0); - CLEAR_BIT(instance->tim->CR1, TIM_CR1_DIR); // upcounter - } else if constexpr (mode == CountingMode::DOWN) { - MODIFY_REG(instance->tim->CR1, TIM_CR1_CMS, 0); - SET_BIT(instance->tim->CR1, TIM_CR1_DIR); // downcounter - } else if constexpr (mode == CountingMode::CENTER_ALIGNED_INTERRUPT_DOWN) { - MODIFY_REG(instance->tim->CR1, TIM_CR1_CMS, TIM_CR1_CMS_0); - } else if constexpr (mode == CountingMode::CENTER_ALIGNED_INTERRUPT_UP) { - MODIFY_REG(instance->tim->CR1, TIM_CR1_CMS, TIM_CR1_CMS_1); - } else if constexpr (mode == CountingMode::CENTER_ALIGNED_INTERRUPT_BOTH) { - MODIFY_REG(instance->tim->CR1, TIM_CR1_CMS, (TIM_CR1_CMS_0 | TIM_CR1_CMS_1)); - } - } - - /////////////////////////////////////////////////////// - // 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) { - /* If it's center aligned duplicate the frequency */ - if ((instance->tim->CR1 & TIM_CR1_CMS) != 0) { - frequency = 2 * frequency; - } - pwm_frequency = frequency; - - if constexpr (mode == ST_LIB::PWM_Frequency_Mode::SPEED) { - instance->tim->ARR = - (this->get_clock_frequency() / (instance->tim->PSC + 1)) / frequency; - } else if constexpr (mode == ST_LIB::PWM_Frequency_Mode::PRECISION) { - float psc_plus_1_mul_freq = (float)(instance->tim->PSC + 1) * (float)frequency; - instance->tim->ARR = - (uint32_t)((float)get_clock_frequency() / psc_plus_1_mul_freq - 0.5f); - } - - if (pwm_channel_duties[0] != 0.0f) { - if constexpr (this->is_32bit_instance) { - uint32_t raw_duty = - (uint32_t)((float)(instance->tim->ARR + 1) * (pwm_channel_duties[0] / 100.0f)); - instance->tim->CCR1 = raw_duty; - } else { - uint16_t raw_duty = - (uint16_t)((float)(instance->tim->ARR + 1) * (pwm_channel_duties[0] / 100.0f)); - instance->tim->CCR1 = raw_duty; - } - } - if (pwm_channel_duties[1] != 0.0f) { - if constexpr (this->is_32bit_instance) { - uint32_t raw_duty = - (uint32_t)((float)(instance->tim->ARR + 1) * (pwm_channel_duties[1] / 100.0f)); - instance->tim->CCR2 = raw_duty; - } else { - uint16_t raw_duty = - (uint16_t)((float)(instance->tim->ARR + 1) * (pwm_channel_duties[1] / 100.0f)); - instance->tim->CCR2 = raw_duty; - } - } - if (pwm_channel_duties[2] != 0.0f) { - if constexpr (this->is_32bit_instance) { - uint32_t raw_duty = - (uint32_t)((float)(instance->tim->ARR + 1) * (pwm_channel_duties[2] / 100.0f)); - instance->tim->CCR3 = raw_duty; - } else { - uint16_t raw_duty = - (uint16_t)((float)(instance->tim->ARR + 1) * (pwm_channel_duties[2] / 100.0f)); - instance->tim->CCR3 = raw_duty; - } - } - if (pwm_channel_duties[3] != 0.0f) { - if constexpr (this->is_32bit_instance) { - uint32_t raw_duty = - (uint32_t)((float)(instance->tim->ARR + 1) * (pwm_channel_duties[3] / 100.0f)); - instance->tim->CCR4 = raw_duty; - } else { - uint16_t raw_duty = - (uint16_t)((float)(instance->tim->ARR + 1) * (pwm_channel_duties[3] / 100.0f)); - instance->tim->CCR4 = raw_duty; - } - } - } - - /* 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) || - (ch == TimerChannel::CHANNEL_3) || (ch == TimerChannel::CHANNEL_4) || - (ch == TimerChannel::CHANNEL_5) || (ch == TimerChannel::CHANNEL_6))) { - ST_LIB::compile_error("config_output_compare_channel should only be called with " - "channels [1..6] (not negated)"); - } - - uint32_t tmpccmrx; - uint32_t tmpccer; - uint32_t tmpcr2; - - tmpccer = instance->tim->CCER; - - tmpcr2 = instance->tim->CR2; - - 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; - } else if constexpr (ch == TimerChannel::CHANNEL_5 || ch == TimerChannel::CHANNEL_6) { - tmpccmrx = instance->tim->CCMR3; - } - - if constexpr (ch == TimerChannel::CHANNEL_1) { - CLEAR_BIT(instance->tim->CCER, TIM_CCER_CC1E); - - CLEAR_BIT(tmpccmrx, TIM_CCMR1_OC1M); - CLEAR_BIT(tmpccmrx, TIM_CCMR1_CC1S); - - SET_BIT(tmpccmrx, OC_Config->OCMode); - CLEAR_BIT(tmpccer, TIM_CCER_CC1P); - SET_BIT(tmpccer, OC_Config->OCPolarity); - } else if constexpr (ch == TimerChannel::CHANNEL_2) { - CLEAR_BIT(instance->tim->CCER, TIM_CCER_CC2E); - - CLEAR_BIT(tmpccmrx, TIM_CCMR1_OC2M); - CLEAR_BIT(tmpccmrx, TIM_CCMR1_CC2S); - - SET_BIT(tmpccmrx, OC_Config->OCMode << 8U); - CLEAR_BIT(tmpccer, TIM_CCER_CC2P); - SET_BIT(tmpccer, OC_Config->OCPolarity << 4U); - } else if constexpr (ch == TimerChannel::CHANNEL_3) { - CLEAR_BIT(instance->tim->CCER, TIM_CCER_CC3E); - - CLEAR_BIT(tmpccmrx, TIM_CCMR2_OC3M); - CLEAR_BIT(tmpccmrx, TIM_CCMR2_CC3S); - - SET_BIT(tmpccmrx, OC_Config->OCMode); - CLEAR_BIT(tmpccer, TIM_CCER_CC3P); - SET_BIT(tmpccer, OC_Config->OCPolarity << 8U); - } else if constexpr (ch == TimerChannel::CHANNEL_4) { - CLEAR_BIT(instance->tim->CCER, TIM_CCER_CC4E); - - CLEAR_BIT(tmpccmrx, TIM_CCMR2_OC4M); - CLEAR_BIT(tmpccmrx, TIM_CCMR2_CC4S); - - SET_BIT(tmpccmrx, OC_Config->OCMode << 8U); - CLEAR_BIT(tmpccer, TIM_CCER_CC4P); - SET_BIT(tmpccer, OC_Config->OCPolarity << 12U); - } else if constexpr (ch == TimerChannel::CHANNEL_5) { - CLEAR_BIT(instance->tim->CCER, TIM_CCER_CC5E); - - CLEAR_BIT(tmpccmrx, TIM_CCMR3_OC5M); - - SET_BIT(tmpccmrx, OC_Config->OCMode); - CLEAR_BIT(tmpccer, TIM_CCER_CC5P); - SET_BIT(tmpccer, OC_Config->OCPolarity << 16U); - } else if constexpr (ch == TimerChannel::CHANNEL_6) { - CLEAR_BIT(instance->tim->CCER, TIM_CCER_CC6E); - - CLEAR_BIT(tmpccmrx, TIM_CCMR3_OC6M); - - SET_BIT(tmpccmrx, OC_Config->OCMode); - CLEAR_BIT(tmpccer, TIM_CCER_CC6P); - SET_BIT(tmpccer, OC_Config->OCPolarity << 20U); - } - - if (this->is_ccxn_instance(ch)) { - assert_param(IS_TIM_OCN_POLARITY(OC_Config->OCNPolarity)); - - if constexpr (ch == TimerChannel::CHANNEL_1) { - CLEAR_BIT(tmpccer, TIM_CCER_CC1NP); - SET_BIT(tmpccer, OC_Config->OCNPolarity); - CLEAR_BIT(tmpccer, TIM_CCER_CC1NE); - } else if constexpr (ch == TimerChannel::CHANNEL_2) { - CLEAR_BIT(tmpccer, TIM_CCER_CC2NP); - SET_BIT(tmpccer, OC_Config->OCNPolarity << 4U); - CLEAR_BIT(tmpccer, TIM_CCER_CC2NE); - } else if constexpr (ch == TimerChannel::CHANNEL_3) { - CLEAR_BIT(tmpccer, TIM_CCER_CC3NP); - SET_BIT(tmpccer, OC_Config->OCNPolarity << 8U); - CLEAR_BIT(tmpccer, TIM_CCER_CC3NE); - } - } - - if (this->is_break_instance) { - assert_param(IS_TIM_OCNIDLE_STATE(OC_Config->OCNIdleState)); - assert_param(IS_TIM_OCIDLE_STATE(OC_Config->OCIdleState)); - - if constexpr (ch == TimerChannel::CHANNEL_1) { - CLEAR_BIT(tmpcr2, TIM_CR2_OIS1); - CLEAR_BIT(tmpcr2, TIM_CR2_OIS1N); - SET_BIT(tmpcr2, OC_Config->OCNIdleState); - SET_BIT(tmpcr2, OC_Config->OCIdleState); - } else if constexpr (ch == TimerChannel::CHANNEL_2) { - CLEAR_BIT(tmpcr2, TIM_CR2_OIS2); - CLEAR_BIT(tmpcr2, TIM_CR2_OIS2N); - SET_BIT(tmpcr2, OC_Config->OCNIdleState << 2U); - SET_BIT(tmpcr2, OC_Config->OCIdleState << 2U); - } else if constexpr (ch == TimerChannel::CHANNEL_3) { - CLEAR_BIT(tmpcr2, TIM_CR2_OIS3); - CLEAR_BIT(tmpcr2, TIM_CR2_OIS3N); - SET_BIT(tmpcr2, OC_Config->OCNIdleState << 4U); - SET_BIT(tmpcr2, OC_Config->OCIdleState << 4U); - } else if constexpr (ch == TimerChannel::CHANNEL_4) { - CLEAR_BIT(tmpcr2, TIM_CR2_OIS4); - SET_BIT(tmpcr2, OC_Config->OCIdleState << 6U); - } else if constexpr (ch == TimerChannel::CHANNEL_5) { - CLEAR_BIT(tmpcr2, TIM_CR2_OIS5); - SET_BIT(tmpcr2, OC_Config->OCIdleState << 8U); - } else if constexpr (ch == TimerChannel::CHANNEL_6) { - CLEAR_BIT(tmpcr2, TIM_CR2_OIS6); - SET_BIT(tmpcr2, OC_Config->OCIdleState << 10U); - } - } - - instance->tim->CR2 = tmpcr2; - 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; - } else if constexpr (ch == TimerChannel::CHANNEL_5 || ch == TimerChannel::CHANNEL_6) { - instance->tim->CCMR3 = tmpccmrx; - } - - if constexpr (ch == TimerChannel::CHANNEL_1) - instance->tim->CCR1 = OC_Config->Pulse; - else if constexpr (ch == TimerChannel::CHANNEL_2) - instance->tim->CCR2 = OC_Config->Pulse; - else if constexpr (ch == TimerChannel::CHANNEL_3) - instance->tim->CCR3 = OC_Config->Pulse; - else if constexpr (ch == TimerChannel::CHANNEL_4) - instance->tim->CCR4 = OC_Config->Pulse; - else if constexpr (ch == TimerChannel::CHANNEL_5) - instance->tim->CCR5 = OC_Config->Pulse; - else if constexpr (ch == TimerChannel::CHANNEL_6) - instance->tim->CCR6 = OC_Config->Pulse; - - instance->tim->CCER = tmpccer; - } - template inline uint32_t get_ccrx() { - if constexpr ((ch == TimerChannel::CHANNEL_1) || (ch == TimerChannel::CHANNEL_1_NEGATED)) - return instance->tim->CCR1; - else if constexpr ((ch == TimerChannel::CHANNEL_2) || (ch == TimerChannel::CHANNEL_2_NEGATED)) - return instance->tim->CCR2; - else if constexpr ((ch == TimerChannel::CHANNEL_3) || (ch == TimerChannel::CHANNEL_3_NEGATED)) - return instance->tim->CCR3; - else if constexpr (ch == TimerChannel::CHANNEL_4) - return instance->tim->CCR4; - else if constexpr (ch == TimerChannel::CHANNEL_5) - return instance->tim->CCR5; - else if constexpr (ch == TimerChannel::CHANNEL_6) - return instance->tim->CCR6; - else { - ST_LIB::compile_error("Unknown timer channel, there are only 6 channels [1..6]"); - return 0; - } - } - - /* 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); - } else if constexpr ((ch == TimerChannel::CHANNEL_2) || (ch == TimerChannel::CHANNEL_2_NEGATED)) { - SET_BIT(instance->tim->CCMR1, TIM_CCMR1_OC2PE); - } else if constexpr ((ch == TimerChannel::CHANNEL_3) || (ch == TimerChannel::CHANNEL_3_NEGATED)) { - SET_BIT(instance->tim->CCMR2, TIM_CCMR2_OC3PE); - } else if constexpr (ch == TimerChannel::CHANNEL_4) { - SET_BIT(instance->tim->CCMR2, TIM_CCMR2_OC4PE); - } else if constexpr (ch == TimerChannel::CHANNEL_5) { - SET_BIT(instance->tim->CCMR3, TIM_CCMR3_OC5PE); - } else if constexpr (ch == TimerChannel::CHANNEL_6) { - SET_BIT(instance->tim->CCMR3, TIM_CCMR3_OC6PE); - } else - ST_LIB::compile_error("Unknown timer channel, there are only 6 channels [1..6]"); - } - - template inline void set_capture_compare(uint32_t val) { - if constexpr ((ch == TimerChannel::CHANNEL_1) || (ch == TimerChannel::CHANNEL_1_NEGATED)) { - instance->tim->CCR1 = val; - } else if constexpr ((ch == TimerChannel::CHANNEL_2) || (ch == TimerChannel::CHANNEL_2_NEGATED)) { - instance->tim->CCR2 = val; - } else if constexpr ((ch == TimerChannel::CHANNEL_3) || (ch == TimerChannel::CHANNEL_3_NEGATED)) { - instance->tim->CCR3 = val; - } else if constexpr (ch == TimerChannel::CHANNEL_4) { - instance->tim->CCR4 = val; - } else if constexpr (ch == TimerChannel::CHANNEL_5) { - instance->tim->CCR5 = val; - } else if constexpr (ch == TimerChannel::CHANNEL_6) { - instance->tim->CCR6 = val; - } else - ST_LIB::compile_error("Unknown timer channel, there are only 6 channels [1..6]"); - } - - inline bool are_all_channels_free() { - return ((instance->tim->CCER & TIM_CCER_CCxE_MASK) == 0) && - ((instance->tim->CCER & TIM_CCER_CCxNE_MASK) == 0); - } - - // leftover from old TimerPeripheral, maybe this was useful? - inline uint16_t get_prescaler() { return instance->tim->PSC; } - inline uint32_t get_period() { return instance->tim->ARR; } -}; -} // namespace ST_LIB - -#endif // HAL_TIM_MODULE_ENABLED +/* + * TimerWrapper.hpp + * + * Created on: 30 dic. 2025 + * Author: victor + */ + +#pragma once + +#include "hal_wrapper.h" + +#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" +#include "HALAL/Models/GPIO.hpp" + +#include "ErrorHandler/ErrorHandler.hpp" + +#define get_timer_instance(board, timer_type) \ + ST_LIB::TimerWrapper(&board::instance_of()) + +namespace ST_LIB { + +template struct TimerWrapper { + TimerDomain::Instance* instance = nullptr; + TimerWrapper() = default; + TimerWrapper(TimerDomain::Instance* inst) : instance(inst) {} + + static constexpr int MAX_pwm_channel_duties = 4; + float pwm_channel_duties[MAX_pwm_channel_duties] = {0.0f, 0.0f, 0.0f, 0.0f}; + uint32_t pwm_frequency = 0; + + enum CountingMode : uint8_t { + UP = 0, + DOWN = 1, + /* center-aligned = counter counts up and down alternatively */ + /* Output compare interrupt flags of channels configured in output (CCxS=00 in TIMx_CCMRx + register) are set only when the counter is counting down */ + CENTER_ALIGNED_INTERRUPT_DOWN = 2, + /* Output compare interrupt flags of channels configured in output (CCxS=00 in TIMx_CCMRx + register) are set only when the counter is counting up */ + CENTER_ALIGNED_INTERRUPT_UP = 3, + /* both up and down */ + CENTER_ALIGNED_INTERRUPT_BOTH = 4, + }; + + static constexpr bool is_32bit_instance = + (dev.e.request == TimerRequest::GeneralPurpose32bit_2 || + dev.e.request == TimerRequest::GeneralPurpose32bit_5 || + dev.e.request == TimerRequest::GeneralPurpose32bit_23 || + dev.e.request == TimerRequest::GeneralPurpose32bit_24 || + dev.e.request == TimerRequest::Any32bit); + /* supports break input */ + static constexpr bool is_break_instance = + (dev.e.request == TimerRequest::Advanced_1 || dev.e.request == TimerRequest::Advanced_8 || + 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 || + 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 only_supports_upcounting = + (dev.e.request == TimerRequest::Basic_6 || dev.e.request == TimerRequest::Basic_7 || + dev.e.request == TimerRequest::SlaveTimer_12 || + dev.e.request == TimerRequest::SlaveTimer_13 || + dev.e.request == TimerRequest::SlaveTimer_14 || + dev.e.request == TimerRequest::GeneralPurpose_15 || + dev.e.request == TimerRequest::GeneralPurpose_16 || + dev.e.request == TimerRequest::GeneralPurpose_17); + static constexpr bool is_on_APB1 = + (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::Basic_6 || dev.e.request == TimerRequest::Basic_7 || + dev.e.request == TimerRequest::SlaveTimer_12 || + dev.e.request == TimerRequest::SlaveTimer_13 || + dev.e.request == TimerRequest::SlaveTimer_14); + + /* returns if the channel can be negated or not {See TimerDomain get_gpio_af()} */ + static consteval bool is_ccxn_instance(ST_LIB::TimerChannel ch) { + switch (dev.e.request) { + case TimerRequest::Advanced_1: + case TimerRequest::Advanced_8: + return (ch == TimerChannel::CHANNEL_1) || (ch == TimerChannel::CHANNEL_1_NEGATED) || + (ch == TimerChannel::CHANNEL_2) || (ch == TimerChannel::CHANNEL_2_NEGATED) || + (ch == TimerChannel::CHANNEL_3) || (ch == TimerChannel::CHANNEL_3_NEGATED); + + case TimerRequest::GeneralPurpose_15: + case TimerRequest::GeneralPurpose_16: + case TimerRequest::GeneralPurpose_17: + return (ch == TimerChannel::CHANNEL_1) || (ch == TimerChannel::CHANNEL_1_NEGATED); + + default: + return false; + } + } + + inline uint32_t get_clock_frequency() { + uint32_t result; + if constexpr (this->is_on_APB1) { + result = HAL_RCC_GetPCLK1Freq(); + if ((RCC->D2CFGR & RCC_D2CFGR_D2PPRE1) != RCC_HCLK_DIV1) { + result *= 2; + } + } else { + result = HAL_RCC_GetPCLK2Freq(); + if ((RCC->D2CFGR & RCC_D2CFGR_D2PPRE2) != RCC_HCLK_DIV1) { + result *= 2; + } + } + return result; + } + + template + inline PWM get_pwm( + uint32_t polarity = TIM_OCPOLARITY_HIGH, + uint32_t negated_polarity = TIM_OCNPOLARITY_HIGH + ) { + static_assert(dev.e.pin_count > 0, "Need at least one pin to get a pwm"); + + if constexpr (dev.e.pins[0].pin == pin.pin && dev.e.pins[0].channel == pin.channel) { + if constexpr (dev.e.pins[0].af != TimerAF::PWM) { + ST_LIB::compile_error("Pin must be configured in TimerDomain as a PWM"); + } + return PWM( + this, + polarity, + negated_polarity, + &pwm_channel_duties + [(static_cast(pin.channel) & + ~static_cast(TimerChannel::CHANNEL_NEGATED_FLAG)) - + 1], + &pwm_frequency + ); + } else if constexpr (dev.e.pin_count > 1 && dev.e.pins[1].pin == pin.pin && dev.e.pins[1].channel == pin.channel) { + if constexpr (dev.e.pins[1].af != TimerAF::PWM) { + ST_LIB::compile_error("Pin must be configured in TimerDomain as a PWM"); + } + return PWM( + this, + polarity, + negated_polarity, + &pwm_channel_duties + [(static_cast(pin.channel) & + ~static_cast(TimerChannel::CHANNEL_NEGATED_FLAG)) - + 1], + &pwm_frequency + ); + } else if constexpr (dev.e.pin_count > 2 && dev.e.pins[2].pin == pin.pin && dev.e.pins[2].channel == pin.channel) { + if constexpr (dev.e.pins[2].af != TimerAF::PWM) { + ST_LIB::compile_error("Pin must be configured in TimerDomain as a PWM"); + } + return PWM( + this, + polarity, + negated_polarity, + &pwm_channel_duties + [(static_cast(pin.channel) & + ~static_cast(TimerChannel::CHANNEL_NEGATED_FLAG)) - + 1], + &pwm_frequency + ); + } else if constexpr (dev.e.pin_count > 3 && dev.e.pins[3].pin == pin.pin && dev.e.pins[3].channel == pin.channel) { + if constexpr (dev.e.pins[3].af != TimerAF::PWM) { + ST_LIB::compile_error("Pin must be configured in TimerDomain as a PWM"); + } + return PWM( + this, + polarity, + negated_polarity, + &pwm_channel_duties + [(static_cast(pin.channel) & + ~static_cast(TimerChannel::CHANNEL_NEGATED_FLAG)) - + 1], + &pwm_frequency + ); + } else if constexpr (dev.e.pin_count > 4 && dev.e.pins[4].pin == pin.pin && dev.e.pins[4].channel == pin.channel) { + if constexpr (dev.e.pins[4].af != TimerAF::PWM) { + ST_LIB::compile_error("Pin must be configured in TimerDomain as a PWM"); + } + return PWM( + this, + polarity, + negated_polarity, + &pwm_channel_duties + [(static_cast(pin.channel) & + ~static_cast(TimerChannel::CHANNEL_NEGATED_FLAG)) - + 1], + &pwm_frequency + ); + } else if constexpr (dev.e.pin_count > 5 && dev.e.pins[5].pin == pin.pin && dev.e.pins[5].channel == pin.channel) { + if constexpr (dev.e.pins[5].af != TimerAF::PWM) { + ST_LIB::compile_error("Pin must be configured in TimerDomain as a PWM"); + } + return PWM( + this, + polarity, + negated_polarity, + &pwm_channel_duties + [(static_cast(pin.channel) & + ~static_cast(TimerChannel::CHANNEL_NEGATED_FLAG)) - + 1], + &pwm_frequency + ); + } else if constexpr (dev.e.pin_count > 6 && dev.e.pins[6].pin == pin.pin && dev.e.pins[6].channel == pin.channel) { + if constexpr (dev.e.pins[6].af != TimerAF::PWM) { + ST_LIB::compile_error("Pin must be configured in TimerDomain as a PWM"); + } + return PWM( + this, + polarity, + negated_polarity, + &pwm_channel_duties + [(static_cast(pin.channel) & + ~static_cast(TimerChannel::CHANNEL_NEGATED_FLAG)) - + 1], + &pwm_frequency + ); + } else if constexpr (dev.e.pin_count == 7 && dev.e.pins[7].pin == pin.pin && dev.e.pins[7].channel == pin.channel) { + if constexpr (dev.e.pins[7].af != TimerAF::PWM) { + ST_LIB::compile_error("Pin must be configured in TimerDomain as a PWM"); + } + return PWM( + this, + polarity, + negated_polarity, + &pwm_channel_duties + [(static_cast(pin.channel) & + ~static_cast(TimerChannel::CHANNEL_NEGATED_FLAG)) - + 1], + &pwm_frequency + ); + } else { + ST_LIB::compile_error("No pins passed to TimerWrapper are the same as the pins passed " + "to get_pwm() [this method]"); + } + } + + template + inline DualPWM get_dual_pwm( + uint32_t polarity = TIM_OCPOLARITY_HIGH, + uint32_t negated_polarity = TIM_OCNPOLARITY_HIGH + ) { + static_assert(dev.e.pin_count > 1, "Need at least two pins on one timer to get a dual pwm"); + + // NOTE: No voy a comprobar todo esto, no hará falta comprobarlo con la siguiente versión + // del pwm, dualpwm + static_assert( + (static_cast(pin.channel) | + static_cast(TimerChannel::CHANNEL_NEGATED_FLAG)) == + static_cast(negated_pin.channel) + ); + static_assert(pin.af == TimerAF::PWM && negated_pin.af == TimerAF::PWM); + return DualPWM( + this, + polarity, + negated_polarity, + &pwm_channel_duties[static_cast(pin.channel) - 1], + &pwm_frequency + ); + } + + 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); } + + inline void clear_update_interrupt_flag() { CLEAR_BIT(instance->tim->SR, TIM_SR_UIF); } + + /* Disabled by default */ + inline void enable_update_interrupt() { SET_BIT(instance->tim->DIER, TIM_DIER_UIE); } + inline void disable_update_interrupt() { CLEAR_BIT(instance->tim->DIER, TIM_DIER_UIE); } + + /* Disabled by default */ + inline void enable_nvic() { NVIC_EnableIRQ(TimerDomain::timer_irqn[instance->timer_idx]); } + inline void disable_nvic() { NVIC_DisableIRQ(TimerDomain::timer_irqn[instance->timer_idx]); } + + /* Enable UEV. The Update (UEV) event is generated by one of the following events: + * – Counter overflow/underflow + * – Setting the UG bit + * – Update generation through the slave mode controller + * Enabled by default + */ + inline void enable_update_event() { CLEAR_BIT(instance->tim->CR1, TIM_CR1_UDIS); } + /* Disable UEV. The Update event is not generated, shadow registers keep their value + * (ARR, PSC, CCRx). However the counter and the prescaler are reinitialized if the UG bit + * is set or if a hardware reset is received from the slave mode controller. + */ + inline void disable_update_event() { SET_BIT(instance->tim->CR1, TIM_CR1_UDIS); } + + /* default: disabled */ + inline void break_interrupt_enable() { + static_assert( + dev.e.request == TimerRequest::Advanced_1 || dev.e.request == TimerRequest::Advanced_8, + "Error: Break interrupt enable only allowed in advanced timers {TIM1, TIM8}" + ); + SET_BIT(instance->tim->DIER, TIM_DIER_BIE); + } + inline void break_interrupt_disable() { + static_assert( + dev.e.request == TimerRequest::Advanced_1 || dev.e.request == TimerRequest::Advanced_8, + "Error: Break interrupt enable only allowed in advanced timers {TIM1, TIM8}" + ); + CLEAR_BIT(instance->tim->DIER, TIM_DIER_BIE); + } + + /* interrupt gets called only once, counter needs to be reenabled */ + inline void set_one_pulse_mode() { SET_BIT(instance->tim->CR1, TIM_CR1_OPM); } + inline void set_multi_interrupt_mode() { CLEAR_BIT(instance->tim->CR1, TIM_CR1_OPM); } + + inline TIM_HandleTypeDef* get_hal_handle() { return instance->hal_tim; } + inline TIM_TypeDef* get_cmsis_handle() { return instance->tim; } + + inline void set_prescaler(uint16_t psc) { instance->tim->PSC = psc; } + // TODO: 16 bit and 32 bit version (?) + inline void set_limit_value(uint32_t arr) { instance->tim->ARR = arr; } + + inline void set_callback(void (*callback)(void*), void* callback_data) { + TimerDomain::callback_data[instance->timer_idx] = callback_data; + TimerDomain::callbacks[instance->timer_idx] = callback; + } + + inline void configure32bit(void (*callback)(void*), void* callback_data, uint32_t period) { + static_assert( + this->is_32bit_instance, + "Only timers {TIM2, TIM5, TIM23, TIM24} have a 32-bit resolution" + ); + + instance->tim->ARR = period; + TimerDomain::callback_data[instance->timer_idx] = callback_data; + TimerDomain::callbacks[instance->timer_idx] = callback; + this->counter_enable(); + } + + inline void configure16bit(void (*callback)(void*), void* callback_data, uint16_t period) { + instance->tim->ARR = period; + TimerDomain::callbacks[instance->timer_idx] = callback; + TimerDomain::callback_data[instance->timer_idx] = callback_data; + this->counter_enable(); + } + + /* WARNING: The counter _must_ be disabled to switch from edge-aligned to center-aligned mode */ + template inline void set_counting_mode(void) { + constexpr uint8_t reqint = static_cast(dev.e.request); + static_assert( + !this->only_supports_upcounting, + "Error: In request reqidx: Timers other than {Advanced{TIM1, TIM8}, TIM2, TIM3, TIM4, " + "TIM5, TIM23, TIM24} only support upcounting" + ); + + if constexpr (mode == CountingMode::UP) { + MODIFY_REG(instance->tim->CR1, TIM_CR1_CMS, 0); + CLEAR_BIT(instance->tim->CR1, TIM_CR1_DIR); // upcounter + } else if constexpr (mode == CountingMode::DOWN) { + MODIFY_REG(instance->tim->CR1, TIM_CR1_CMS, 0); + SET_BIT(instance->tim->CR1, TIM_CR1_DIR); // downcounter + } else if constexpr (mode == CountingMode::CENTER_ALIGNED_INTERRUPT_DOWN) { + MODIFY_REG(instance->tim->CR1, TIM_CR1_CMS, TIM_CR1_CMS_0); + } else if constexpr (mode == CountingMode::CENTER_ALIGNED_INTERRUPT_UP) { + MODIFY_REG(instance->tim->CR1, TIM_CR1_CMS, TIM_CR1_CMS_1); + } else if constexpr (mode == CountingMode::CENTER_ALIGNED_INTERRUPT_BOTH) { + MODIFY_REG(instance->tim->CR1, TIM_CR1_CMS, (TIM_CR1_CMS_0 | TIM_CR1_CMS_1)); + } + } + + /////////////////////////////////////////////////////// + // 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) { + /* If it's center aligned duplicate the frequency */ + if ((instance->tim->CR1 & TIM_CR1_CMS) != 0) { + frequency = 2 * frequency; + } + pwm_frequency = frequency; + + if constexpr (mode == ST_LIB::PWM_Frequency_Mode::SPEED) { + instance->tim->ARR = + (this->get_clock_frequency() / (instance->tim->PSC + 1)) / frequency; + } else if constexpr (mode == ST_LIB::PWM_Frequency_Mode::PRECISION) { + float psc_plus_1_mul_freq = (float)(instance->tim->PSC + 1) * (float)frequency; + instance->tim->ARR = + (uint32_t)((float)get_clock_frequency() / psc_plus_1_mul_freq - 0.5f); + } + + if (pwm_channel_duties[0] != 0.0f) { + if constexpr (this->is_32bit_instance) { + uint32_t raw_duty = + (uint32_t)((float)(instance->tim->ARR + 1) * (pwm_channel_duties[0] / 100.0f)); + instance->tim->CCR1 = raw_duty; + } else { + uint16_t raw_duty = + (uint16_t)((float)(instance->tim->ARR + 1) * (pwm_channel_duties[0] / 100.0f)); + instance->tim->CCR1 = raw_duty; + } + } + if (pwm_channel_duties[1] != 0.0f) { + if constexpr (this->is_32bit_instance) { + uint32_t raw_duty = + (uint32_t)((float)(instance->tim->ARR + 1) * (pwm_channel_duties[1] / 100.0f)); + instance->tim->CCR2 = raw_duty; + } else { + uint16_t raw_duty = + (uint16_t)((float)(instance->tim->ARR + 1) * (pwm_channel_duties[1] / 100.0f)); + instance->tim->CCR2 = raw_duty; + } + } + if (pwm_channel_duties[2] != 0.0f) { + if constexpr (this->is_32bit_instance) { + uint32_t raw_duty = + (uint32_t)((float)(instance->tim->ARR + 1) * (pwm_channel_duties[2] / 100.0f)); + instance->tim->CCR3 = raw_duty; + } else { + uint16_t raw_duty = + (uint16_t)((float)(instance->tim->ARR + 1) * (pwm_channel_duties[2] / 100.0f)); + instance->tim->CCR3 = raw_duty; + } + } + if (pwm_channel_duties[3] != 0.0f) { + if constexpr (this->is_32bit_instance) { + uint32_t raw_duty = + (uint32_t)((float)(instance->tim->ARR + 1) * (pwm_channel_duties[3] / 100.0f)); + instance->tim->CCR4 = raw_duty; + } else { + uint16_t raw_duty = + (uint16_t)((float)(instance->tim->ARR + 1) * (pwm_channel_duties[3] / 100.0f)); + instance->tim->CCR4 = raw_duty; + } + } + } + + /* 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) || + (ch == TimerChannel::CHANNEL_3) || (ch == TimerChannel::CHANNEL_4) || + (ch == TimerChannel::CHANNEL_5) || (ch == TimerChannel::CHANNEL_6))) { + ST_LIB::compile_error("config_output_compare_channel should only be called with " + "channels [1..6] (not negated)"); + } + + uint32_t tmpccmrx; + uint32_t tmpccer; + uint32_t tmpcr2; + + tmpccer = instance->tim->CCER; + + tmpcr2 = instance->tim->CR2; + + 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; + } else if constexpr (ch == TimerChannel::CHANNEL_5 || ch == TimerChannel::CHANNEL_6) { + tmpccmrx = instance->tim->CCMR3; + } + + if constexpr (ch == TimerChannel::CHANNEL_1) { + CLEAR_BIT(instance->tim->CCER, TIM_CCER_CC1E); + + CLEAR_BIT(tmpccmrx, TIM_CCMR1_OC1M); + CLEAR_BIT(tmpccmrx, TIM_CCMR1_CC1S); + + SET_BIT(tmpccmrx, OC_Config->OCMode); + CLEAR_BIT(tmpccer, TIM_CCER_CC1P); + SET_BIT(tmpccer, OC_Config->OCPolarity); + } else if constexpr (ch == TimerChannel::CHANNEL_2) { + CLEAR_BIT(instance->tim->CCER, TIM_CCER_CC2E); + + CLEAR_BIT(tmpccmrx, TIM_CCMR1_OC2M); + CLEAR_BIT(tmpccmrx, TIM_CCMR1_CC2S); + + SET_BIT(tmpccmrx, OC_Config->OCMode << 8U); + CLEAR_BIT(tmpccer, TIM_CCER_CC2P); + SET_BIT(tmpccer, OC_Config->OCPolarity << 4U); + } else if constexpr (ch == TimerChannel::CHANNEL_3) { + CLEAR_BIT(instance->tim->CCER, TIM_CCER_CC3E); + + CLEAR_BIT(tmpccmrx, TIM_CCMR2_OC3M); + CLEAR_BIT(tmpccmrx, TIM_CCMR2_CC3S); + + SET_BIT(tmpccmrx, OC_Config->OCMode); + CLEAR_BIT(tmpccer, TIM_CCER_CC3P); + SET_BIT(tmpccer, OC_Config->OCPolarity << 8U); + } else if constexpr (ch == TimerChannel::CHANNEL_4) { + CLEAR_BIT(instance->tim->CCER, TIM_CCER_CC4E); + + CLEAR_BIT(tmpccmrx, TIM_CCMR2_OC4M); + CLEAR_BIT(tmpccmrx, TIM_CCMR2_CC4S); + + SET_BIT(tmpccmrx, OC_Config->OCMode << 8U); + CLEAR_BIT(tmpccer, TIM_CCER_CC4P); + SET_BIT(tmpccer, OC_Config->OCPolarity << 12U); + } else if constexpr (ch == TimerChannel::CHANNEL_5) { + CLEAR_BIT(instance->tim->CCER, TIM_CCER_CC5E); + + CLEAR_BIT(tmpccmrx, TIM_CCMR3_OC5M); + + SET_BIT(tmpccmrx, OC_Config->OCMode); + CLEAR_BIT(tmpccer, TIM_CCER_CC5P); + SET_BIT(tmpccer, OC_Config->OCPolarity << 16U); + } else if constexpr (ch == TimerChannel::CHANNEL_6) { + CLEAR_BIT(instance->tim->CCER, TIM_CCER_CC6E); + + CLEAR_BIT(tmpccmrx, TIM_CCMR3_OC6M); + + SET_BIT(tmpccmrx, OC_Config->OCMode); + CLEAR_BIT(tmpccer, TIM_CCER_CC6P); + SET_BIT(tmpccer, OC_Config->OCPolarity << 20U); + } + + if (this->is_ccxn_instance(ch)) { + assert_param(IS_TIM_OCN_POLARITY(OC_Config->OCNPolarity)); + + if constexpr (ch == TimerChannel::CHANNEL_1) { + CLEAR_BIT(tmpccer, TIM_CCER_CC1NP); + SET_BIT(tmpccer, OC_Config->OCNPolarity); + CLEAR_BIT(tmpccer, TIM_CCER_CC1NE); + } else if constexpr (ch == TimerChannel::CHANNEL_2) { + CLEAR_BIT(tmpccer, TIM_CCER_CC2NP); + SET_BIT(tmpccer, OC_Config->OCNPolarity << 4U); + CLEAR_BIT(tmpccer, TIM_CCER_CC2NE); + } else if constexpr (ch == TimerChannel::CHANNEL_3) { + CLEAR_BIT(tmpccer, TIM_CCER_CC3NP); + SET_BIT(tmpccer, OC_Config->OCNPolarity << 8U); + CLEAR_BIT(tmpccer, TIM_CCER_CC3NE); + } + } + + if (this->is_break_instance) { + assert_param(IS_TIM_OCNIDLE_STATE(OC_Config->OCNIdleState)); + assert_param(IS_TIM_OCIDLE_STATE(OC_Config->OCIdleState)); + + if constexpr (ch == TimerChannel::CHANNEL_1) { + CLEAR_BIT(tmpcr2, TIM_CR2_OIS1); + CLEAR_BIT(tmpcr2, TIM_CR2_OIS1N); + SET_BIT(tmpcr2, OC_Config->OCNIdleState); + SET_BIT(tmpcr2, OC_Config->OCIdleState); + } else if constexpr (ch == TimerChannel::CHANNEL_2) { + CLEAR_BIT(tmpcr2, TIM_CR2_OIS2); + CLEAR_BIT(tmpcr2, TIM_CR2_OIS2N); + SET_BIT(tmpcr2, OC_Config->OCNIdleState << 2U); + SET_BIT(tmpcr2, OC_Config->OCIdleState << 2U); + } else if constexpr (ch == TimerChannel::CHANNEL_3) { + CLEAR_BIT(tmpcr2, TIM_CR2_OIS3); + CLEAR_BIT(tmpcr2, TIM_CR2_OIS3N); + SET_BIT(tmpcr2, OC_Config->OCNIdleState << 4U); + SET_BIT(tmpcr2, OC_Config->OCIdleState << 4U); + } else if constexpr (ch == TimerChannel::CHANNEL_4) { + CLEAR_BIT(tmpcr2, TIM_CR2_OIS4); + SET_BIT(tmpcr2, OC_Config->OCIdleState << 6U); + } else if constexpr (ch == TimerChannel::CHANNEL_5) { + CLEAR_BIT(tmpcr2, TIM_CR2_OIS5); + SET_BIT(tmpcr2, OC_Config->OCIdleState << 8U); + } else if constexpr (ch == TimerChannel::CHANNEL_6) { + CLEAR_BIT(tmpcr2, TIM_CR2_OIS6); + SET_BIT(tmpcr2, OC_Config->OCIdleState << 10U); + } + } + + instance->tim->CR2 = tmpcr2; + 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; + } else if constexpr (ch == TimerChannel::CHANNEL_5 || ch == TimerChannel::CHANNEL_6) { + instance->tim->CCMR3 = tmpccmrx; + } + + if constexpr (ch == TimerChannel::CHANNEL_1) + instance->tim->CCR1 = OC_Config->Pulse; + else if constexpr (ch == TimerChannel::CHANNEL_2) + instance->tim->CCR2 = OC_Config->Pulse; + else if constexpr (ch == TimerChannel::CHANNEL_3) + instance->tim->CCR3 = OC_Config->Pulse; + else if constexpr (ch == TimerChannel::CHANNEL_4) + instance->tim->CCR4 = OC_Config->Pulse; + else if constexpr (ch == TimerChannel::CHANNEL_5) + instance->tim->CCR5 = OC_Config->Pulse; + else if constexpr (ch == TimerChannel::CHANNEL_6) + instance->tim->CCR6 = OC_Config->Pulse; + + 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); + } else if constexpr ((ch == TimerChannel::CHANNEL_2) || (ch == TimerChannel::CHANNEL_2_NEGATED)) { + SET_BIT(instance->tim->CCMR1, TIM_CCMR1_OC2PE); + } else if constexpr ((ch == TimerChannel::CHANNEL_3) || (ch == TimerChannel::CHANNEL_3_NEGATED)) { + SET_BIT(instance->tim->CCMR2, TIM_CCMR2_OC3PE); + } else if constexpr (ch == TimerChannel::CHANNEL_4) { + SET_BIT(instance->tim->CCMR2, TIM_CCMR2_OC4PE); + } else if constexpr (ch == TimerChannel::CHANNEL_5) { + SET_BIT(instance->tim->CCMR3, TIM_CCMR3_OC5PE); + } else if constexpr (ch == TimerChannel::CHANNEL_6) { + SET_BIT(instance->tim->CCMR3, TIM_CCMR3_OC6PE); + } else + ST_LIB::compile_error("Unknown timer channel, there are only 6 channels [1..6]"); + } + + template inline void set_capture_compare(uint32_t val) { + if constexpr ((ch == TimerChannel::CHANNEL_1) || (ch == TimerChannel::CHANNEL_1_NEGATED)) { + instance->tim->CCR1 = val; + } else if constexpr ((ch == TimerChannel::CHANNEL_2) || (ch == TimerChannel::CHANNEL_2_NEGATED)) { + instance->tim->CCR2 = val; + } else if constexpr ((ch == TimerChannel::CHANNEL_3) || (ch == TimerChannel::CHANNEL_3_NEGATED)) { + instance->tim->CCR3 = val; + } else if constexpr (ch == TimerChannel::CHANNEL_4) { + instance->tim->CCR4 = val; + } else if constexpr (ch == TimerChannel::CHANNEL_5) { + instance->tim->CCR5 = val; + } else if constexpr (ch == TimerChannel::CHANNEL_6) { + instance->tim->CCR6 = val; + } else + ST_LIB::compile_error("Unknown timer channel, there are only 6 channels [1..6]"); + } + + template inline uint32_t get_capture_compare() { + if constexpr ((ch == TimerChannel::CHANNEL_1) || (ch == TimerChannel::CHANNEL_1_NEGATED)) + return instance->tim->CCR1; + else if constexpr ((ch == TimerChannel::CHANNEL_2) || (ch == TimerChannel::CHANNEL_2_NEGATED)) + return instance->tim->CCR2; + else if constexpr ((ch == TimerChannel::CHANNEL_3) || (ch == TimerChannel::CHANNEL_3_NEGATED)) + return instance->tim->CCR3; + else if constexpr (ch == TimerChannel::CHANNEL_4) + return instance->tim->CCR4; + else if constexpr (ch == TimerChannel::CHANNEL_5) + return instance->tim->CCR5; + else if constexpr (ch == TimerChannel::CHANNEL_6) + return instance->tim->CCR6; + else { + ST_LIB::compile_error("Unknown timer channel, there are only 6 channels [1..6]"); + return 0; + } + } + + inline bool are_all_channels_free() { + return ((instance->tim->CCER & TIM_CCER_CCxE_MASK) == 0) && + ((instance->tim->CCER & TIM_CCER_CCxNE_MASK) == 0); + } + + // leftover from old TimerPeripheral, maybe this was useful? + inline uint16_t get_prescaler() { return instance->tim->PSC; } + inline uint32_t get_period() { return instance->tim->ARR; } +}; +} // namespace ST_LIB + +#endif // HAL_TIM_MODULE_ENABLED From 8936ec3c2765f080288889ae6da3646ff24b5b1a Mon Sep 17 00:00:00 2001 From: Sagah123 Date: Sat, 17 Oct 2026 22:03:04 +0200 Subject: [PATCH 06/19] Change method's name and location --- Inc/HALAL/Services/PWM/DualPWM.hpp | 4 +-- Inc/HALAL/Services/PWM/PWM.hpp | 2 +- Inc/HALAL/Services/Time/TimerWrapper.hpp | 37 ++++++++++++------------ 3 files changed, 20 insertions(+), 23 deletions(-) diff --git a/Inc/HALAL/Services/PWM/DualPWM.hpp b/Inc/HALAL/Services/PWM/DualPWM.hpp index 8afab44e2..d196b2ad0 100644 --- a/Inc/HALAL/Services/PWM/DualPWM.hpp +++ b/Inc/HALAL/Services/PWM/DualPWM.hpp @@ -51,11 +51,9 @@ class DualPWM { } TIM_OC_InitTypeDef sConfigOC = { .OCMode = TIM_OCMODE_PWM1, - .Pulse = timer->template get_ccrx(), - + .Pulse = timer->template get_capture_compare(), .OCPolarity = polarity, .OCNPolarity = negated_polarity, - .OCFastMode = TIM_OCFAST_DISABLE, .OCIdleState = TIM_OCIDLESTATE_RESET, .OCNIdleState = TIM_OCNIDLESTATE_RESET, diff --git a/Inc/HALAL/Services/PWM/PWM.hpp b/Inc/HALAL/Services/PWM/PWM.hpp index 9e42db67f..660aceecc 100644 --- a/Inc/HALAL/Services/PWM/PWM.hpp +++ b/Inc/HALAL/Services/PWM/PWM.hpp @@ -42,7 +42,7 @@ template class PWM { } TIM_OC_InitTypeDef sConfigOC = { .OCMode = TIM_OCMODE_PWM1, - .Pulse = timer->template get_ccrx(), + .Pulse = timer->template get_capture_compare(), .OCPolarity = polarity, .OCNPolarity = negated_polarity, .OCFastMode = TIM_OCFAST_DISABLE, diff --git a/Inc/HALAL/Services/Time/TimerWrapper.hpp b/Inc/HALAL/Services/Time/TimerWrapper.hpp index 4847094ed..9637a9250 100644 --- a/Inc/HALAL/Services/Time/TimerWrapper.hpp +++ b/Inc/HALAL/Services/Time/TimerWrapper.hpp @@ -715,24 +715,6 @@ template struct TimerWrapper { instance->tim->CCER = tmpccer; } - template inline uint32_t get_ccrx() { - if constexpr ((ch == TimerChannel::CHANNEL_1) || (ch == TimerChannel::CHANNEL_1_NEGATED)) - return instance->tim->CCR1; - else if constexpr ((ch == TimerChannel::CHANNEL_2) || (ch == TimerChannel::CHANNEL_2_NEGATED)) - return instance->tim->CCR2; - else if constexpr ((ch == TimerChannel::CHANNEL_3) || (ch == TimerChannel::CHANNEL_3_NEGATED)) - return instance->tim->CCR3; - else if constexpr (ch == TimerChannel::CHANNEL_4) - return instance->tim->CCR4; - else if constexpr (ch == TimerChannel::CHANNEL_5) - return instance->tim->CCR5; - else if constexpr (ch == TimerChannel::CHANNEL_6) - return instance->tim->CCR6; - else { - ST_LIB::compile_error("Unknown timer channel, there are only 6 channels [1..6]"); - return 0; - } - } /* 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 */ @@ -869,7 +851,24 @@ template struct TimerWrapper { } else ST_LIB::compile_error("Unknown timer channel, there are only 6 channels [1..6]"); } - + template inline uint32_t get_capture_compare() { + if constexpr ((ch == TimerChannel::CHANNEL_1) || (ch == TimerChannel::CHANNEL_1_NEGATED)) + return instance->tim->CCR1; + else if constexpr ((ch == TimerChannel::CHANNEL_2) || (ch == TimerChannel::CHANNEL_2_NEGATED)) + return instance->tim->CCR2; + else if constexpr ((ch == TimerChannel::CHANNEL_3) || (ch == TimerChannel::CHANNEL_3_NEGATED)) + return instance->tim->CCR3; + else if constexpr (ch == TimerChannel::CHANNEL_4) + return instance->tim->CCR4; + else if constexpr (ch == TimerChannel::CHANNEL_5) + return instance->tim->CCR5; + else if constexpr (ch == TimerChannel::CHANNEL_6) + return instance->tim->CCR6; + else { + ST_LIB::compile_error("Unknown timer channel, there are only 6 channels [1..6]"); + return 0; + } + } inline bool are_all_channels_free() { return ((instance->tim->CCER & TIM_CCER_CCxE_MASK) == 0) && ((instance->tim->CCER & TIM_CCER_CCxNE_MASK) == 0); From 0c77e8923cbba37d550446b3009e394dd842df83 Mon Sep 17 00:00:00 2001 From: Sagah123 Date: Wed, 21 Oct 2026 18:02:18 +0200 Subject: [PATCH 07/19] Initial commit --- .../Protections/ProtectionMessages.hpp | 31 +++++++++++++++++++ .../Protections/ProtectionMessages.cpp | 0 2 files changed, 31 insertions(+) create mode 100644 Inc/ST-LIB_HIGH/Protections/ProtectionMessages.hpp create mode 100644 Src/ST-LIB_HIGH/Protections/ProtectionMessages.cpp diff --git a/Inc/ST-LIB_HIGH/Protections/ProtectionMessages.hpp b/Inc/ST-LIB_HIGH/Protections/ProtectionMessages.hpp new file mode 100644 index 000000000..7737cd109 --- /dev/null +++ b/Inc/ST-LIB_HIGH/Protections/ProtectionMessages.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include "ST-LIB_HIGH/Protections/SampleSource.hpp" +#include "HALAL/Services/Diagnostics/Diagnostics.hpp" +#include "HALAL/Services/Time/RTC.hpp" +#include "ST-LIB_HIGH/Protections/FaultController.hpp" +#include "ST-LIB_HIGH/Protections/Protection.hpp" +#include "ST-LIB_HIGH/Protections/ProtectionTypes.hpp" + +namespace ST_LIB { +#pragma pack(push, 1) +template struct ProtectionMessagePacket { + // Comprobar si es de 1 4 8 16 32 o 64 bytes(permitido) + static_assert(sizeof(T) <= 64); + uint16_t id; + uint8_t hours; + uint8_t minutes; + uint8_t seconds; + uint8_t reserved; + T value; + ProtectionMessagePacket(uint16_t measurement_id, Protections::RuleKind protection, T val) + : id((static_cast(static_cast(protection) << 13)) | + (measurement_id & 0X1FFF)), + hours(Global_RTC::get_rtc_timestamp().hour), + minutes(Global_RTC::get_rtc_timestamp().minute), + seconds(Global_RTC::get_rtc_timestamp().second), reserved(0), value(val) {} + // Protection detection + // TCP +}; // struct ProtectionMessagePacket +#pragma pack(pop) +}; // namespace ST_LIB diff --git a/Src/ST-LIB_HIGH/Protections/ProtectionMessages.cpp b/Src/ST-LIB_HIGH/Protections/ProtectionMessages.cpp new file mode 100644 index 000000000..e69de29bb From ac73f61f77f911e2ea1429d04c631565a03a63dc Mon Sep 17 00:00:00 2001 From: Sagah123 Date: Wed, 21 Oct 2026 21:31:11 +0200 Subject: [PATCH 08/19] a lot of stuff to do --- .../Protections/ProtectionMessages.hpp | 87 ++++++++++++++----- .../Protections/ProtectionMessages.cpp | 1 + 2 files changed, 68 insertions(+), 20 deletions(-) diff --git a/Inc/ST-LIB_HIGH/Protections/ProtectionMessages.hpp b/Inc/ST-LIB_HIGH/Protections/ProtectionMessages.hpp index 7737cd109..15c07780a 100644 --- a/Inc/ST-LIB_HIGH/Protections/ProtectionMessages.hpp +++ b/Inc/ST-LIB_HIGH/Protections/ProtectionMessages.hpp @@ -7,25 +7,72 @@ #include "ST-LIB_HIGH/Protections/Protection.hpp" #include "ST-LIB_HIGH/Protections/ProtectionTypes.hpp" +// Forward declaration of LwIP packet buffer +struct pbuf; + +/* value is an address / pointer to data */ +#define MakeProtectionMessagePacket(value) \ + ST_LIB::ProtectionMessagePacket::make((value), sizeof(*(value))) + namespace ST_LIB { -#pragma pack(push, 1) -template struct ProtectionMessagePacket { - // Comprobar si es de 1 4 8 16 32 o 64 bytes(permitido) - static_assert(sizeof(T) <= 64); - uint16_t id; - uint8_t hours; - uint8_t minutes; - uint8_t seconds; - uint8_t reserved; - T value; - ProtectionMessagePacket(uint16_t measurement_id, Protections::RuleKind protection, T val) - : id((static_cast(static_cast(protection) << 13)) | - (measurement_id & 0X1FFF)), - hours(Global_RTC::get_rtc_timestamp().hour), - minutes(Global_RTC::get_rtc_timestamp().minute), - seconds(Global_RTC::get_rtc_timestamp().second), reserved(0), value(val) {} - // Protection detection - // TCP + +struct ProtectionMessagePacket { + // Header containing fault metadata and timestamp + struct Package_Data { + uint16_t id; + uint8_t hours; + uint8_t minutes; + uint8_t seconds; + uint8_t reserved; + }; + + // Universal message packet + struct Package { + void* valuePtr; + size_t packageSize; + + struct pbuf* pbuf; + + Package_Data data; + // Maximum payload is 8 bytes (uint8, uint16, uint32, float, double, uint64) + uint8_t value[8]; + }; + + static Package make(void* value, size_t valueSize) + { + Package pack = { + .valuePtr = value, + .packageSize = sizeof(Package_Data) + valueSize, + .pbuf = nullptr, + .data = {}, + .value = {0}, + }; + + // TODO [Step 1]: Fill pack.data with protection ID and RTC timestamp: + // auto rtc = Global_RTC::get_rtc_timestamp(); + // pack.data.hours = rtc.hour; + // pack.data.minutes = rtc.minute; + // pack.data.seconds = rtc.second; + + // TODO [Step 2]: Copy the value bytes into pack.value array (up to 8 bytes): + // if (value != nullptr && valueSize > 0) { + // size_t len = (valueSize > 8) ? 8 : valueSize; + // memcpy(pack.value, value, len); + // } + + return pack; + } + + static void send(Package pack) + { + // TODO [Step 3]: Call TCP transmission logic: + // Option A: If using LwIP raw API: + // tcp_write(pcb, &pack.data, pack.packageSize, TCP_WRITE_FLAG_COPY); + // tcp_output(pcb); + // + // Option B: If using ST-LIB HALAL Socket: + // socket.send_order(...) / socket.send_packet(...) + } + }; // struct ProtectionMessagePacket -#pragma pack(pop) -}; // namespace ST_LIB +}; // namespace ST_LIB \ No newline at end of file diff --git a/Src/ST-LIB_HIGH/Protections/ProtectionMessages.cpp b/Src/ST-LIB_HIGH/Protections/ProtectionMessages.cpp index e69de29bb..e6a1da98b 100644 --- a/Src/ST-LIB_HIGH/Protections/ProtectionMessages.cpp +++ b/Src/ST-LIB_HIGH/Protections/ProtectionMessages.cpp @@ -0,0 +1 @@ +#include From 484556fb19563c03a6119d68995ed5391afb2ac9 Mon Sep 17 00:00:00 2001 From: Sagah123 Date: Thu, 22 Oct 2026 01:56:43 +0200 Subject: [PATCH 09/19] feat: add ProtectionMessage over raw lwIP TCP --- .../Protections/ProtectionMessages.hpp | 85 ++++++++++--------- 1 file changed, 46 insertions(+), 39 deletions(-) diff --git a/Inc/ST-LIB_HIGH/Protections/ProtectionMessages.hpp b/Inc/ST-LIB_HIGH/Protections/ProtectionMessages.hpp index 15c07780a..6edd4cbb0 100644 --- a/Inc/ST-LIB_HIGH/Protections/ProtectionMessages.hpp +++ b/Inc/ST-LIB_HIGH/Protections/ProtectionMessages.hpp @@ -1,32 +1,32 @@ #pragma once - +/* + * ProtectionMessages.hpp + * "Hello there" + * Created on: Oct 22, 2026 + * Author: Aleks + */ #include "ST-LIB_HIGH/Protections/SampleSource.hpp" #include "HALAL/Services/Diagnostics/Diagnostics.hpp" #include "HALAL/Services/Time/RTC.hpp" #include "ST-LIB_HIGH/Protections/FaultController.hpp" #include "ST-LIB_HIGH/Protections/Protection.hpp" #include "ST-LIB_HIGH/Protections/ProtectionTypes.hpp" - -// Forward declaration of LwIP packet buffer -struct pbuf; - -/* value is an address / pointer to data */ -#define MakeProtectionMessagePacket(value) \ - ST_LIB::ProtectionMessagePacket::make((value), sizeof(*(value))) +#include "lwip/tcp.h" namespace ST_LIB { - struct ProtectionMessagePacket { - // Header containing fault metadata and timestamp + struct Package_Data { + uint16_t id; + uint8_t hours; uint8_t minutes; uint8_t seconds; - uint8_t reserved; + uint8_t reserved; }; - // Universal message packet + struct Package { void* valuePtr; size_t packageSize; @@ -34,45 +34,52 @@ struct ProtectionMessagePacket { struct pbuf* pbuf; Package_Data data; - // Maximum payload is 8 bytes (uint8, uint16, uint32, float, double, uint64) + // Maximum is 8 bytes, not all will be sent always uint8_t value[8]; }; - static Package make(void* value, size_t valueSize) + template + static Package make(uint16_t measurement_id, Protections::RuleKind prot, T* value) + { + static_assert(sizeof(T) <= 8, "Value size must be <= 8 bytes"); + return make(measurement_id, prot, static_cast(value), sizeof(T)); + } + static Package make(uint16_t measurement_id, Protections::RuleKind prot, void* value, size_t valueSize) { Package pack = { .valuePtr = value, .packageSize = sizeof(Package_Data) + valueSize, .pbuf = nullptr, - .data = {}, - .value = {0}, - }; - - // TODO [Step 1]: Fill pack.data with protection ID and RTC timestamp: - // auto rtc = Global_RTC::get_rtc_timestamp(); - // pack.data.hours = rtc.hour; - // pack.data.minutes = rtc.minute; - // pack.data.seconds = rtc.second; - - // TODO [Step 2]: Copy the value bytes into pack.value array (up to 8 bytes): - // if (value != nullptr && valueSize > 0) { - // size_t len = (valueSize > 8) ? 8 : valueSize; - // memcpy(pack.value, value, len); - // } + .data = {} + }; + pack.data.id = (measurement_id & 0x1FFF) | static_cast (static_cast(prot) << 13); + #if defined(HAL_RTC_MODULE_ENABLED) && !defined(SIM_ON) + RTCData rtc = Global_RTC::get_rtc_timestamp(); + pack.data.hours = rtc.hour; + pack.data.minutes = rtc.minute; + pack.data.seconds = rtc.second; + #else + pack.data.hours = 0; pack.data.minutes = 0; pack.data.seconds = 0; + #endif + pack.data.reserved = 0; return pack; } - - static void send(Package pack) + //TCP STUFF starts here + tpcb = tcp_new(); + static void tcp_client_init() + { + static inline struct tcp_pcb* tpcb; + if (tpcb != NULL) { + ip_addr_t destIPADDR; + IP_ADDR4(&destIPADDR, 192, 168, 0, 100); //IP + tcp_connect(tpcb, &destIPADDR, 31, connected); + } + } + static void tcp_client_stuff(Package pack) { - // TODO [Step 3]: Call TCP transmission logic: - // Option A: If using LwIP raw API: - // tcp_write(pcb, &pack.data, pack.packageSize, TCP_WRITE_FLAG_COPY); - // tcp_output(pcb); - // - // Option B: If using ST-LIB HALAL Socket: - // socket.send_order(...) / socket.send_packet(...) + pack.pbuf = pbuf_alloc(PBUF_TRANSPORT, pack.packageSize, PBUF_RAM); } }; // struct ProtectionMessagePacket -}; // namespace ST_LIB \ No newline at end of file +}; // namespace ST_LIB From d1d8c147a349b37592516bc21fee00a6ddf3af4b Mon Sep 17 00:00:00 2001 From: Sagah123 Date: Thu, 22 Oct 2026 02:00:01 +0200 Subject: [PATCH 10/19] Delete useless pointer --- .../Protections/ProtectionMessages.hpp | 37 +++++++++++++------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/Inc/ST-LIB_HIGH/Protections/ProtectionMessages.hpp b/Inc/ST-LIB_HIGH/Protections/ProtectionMessages.hpp index 6edd4cbb0..0cdb49bb5 100644 --- a/Inc/ST-LIB_HIGH/Protections/ProtectionMessages.hpp +++ b/Inc/ST-LIB_HIGH/Protections/ProtectionMessages.hpp @@ -28,16 +28,12 @@ struct ProtectionMessagePacket { struct Package { - void* valuePtr; size_t packageSize; - - struct pbuf* pbuf; - Package_Data data; // Maximum is 8 bytes, not all will be sent always uint8_t value[8]; }; - + // According to AI and Google it makes no difference use Macros to calculate Size or Template in this case template static Package make(uint16_t measurement_id, Protections::RuleKind prot, T* value) { @@ -47,13 +43,14 @@ struct ProtectionMessagePacket { static Package make(uint16_t measurement_id, Protections::RuleKind prot, void* value, size_t valueSize) { Package pack = { - .valuePtr = value, .packageSize = sizeof(Package_Data) + valueSize, - .pbuf = nullptr, .data = {} }; pack.data.id = (measurement_id & 0x1FFF) | static_cast (static_cast(prot) << 13); + if (value != nullptr && valueSize > 0) { + memcpy(pack.value, value, valueSize); + } #if defined(HAL_RTC_MODULE_ENABLED) && !defined(SIM_ON) RTCData rtc = Global_RTC::get_rtc_timestamp(); pack.data.hours = rtc.hour; @@ -66,19 +63,35 @@ struct ProtectionMessagePacket { return pack; } //TCP STUFF starts here - tpcb = tcp_new(); + static inline struct tcp_pcb* tpcb; + static inline bool is_connected = false; + //This code probably can be refactored + static err_t tcp_client_connected(void* arg, tcp_pcb* tcpb, err_t err) + { + LWIP_UNUSED_ARG(tcpb); + LWIP_UNUSED_ARG(arg); + if (err == ERR_OK) { + is_connected = true; + } + return ERR_OK; + } static void tcp_client_init() { - static inline struct tcp_pcb* tpcb; + tpcb = tcp_new(); if (tpcb != NULL) { ip_addr_t destIPADDR; IP_ADDR4(&destIPADDR, 192, 168, 0, 100); //IP - tcp_connect(tpcb, &destIPADDR, 31, connected); + tcp_connect(tpcb, &destIPADDR, 31, tcp_client_connected); //PORT } } - static void tcp_client_stuff(Package pack) + static void tcp_client_send(const Package& pack) //add some timer?? { - pack.pbuf = pbuf_alloc(PBUF_TRANSPORT, pack.packageSize, PBUF_RAM); + if (tpcb != nullptr && is_connected){ + //pack.pbuf = pbuf_alloc(PBUF_TRANSPORT, pack.packageSize, PBUF_RAM); im suprized we dont need it + tcp_write(tpcb, &pack.data, pack.packageSize, TCP_WRITE_FLAG_COPY); + + tcp_output(tpcb); + } } }; // struct ProtectionMessagePacket From 0e6e52651e5567db3e35cdcb24c50f94358b3cfb Mon Sep 17 00:00:00 2001 From: Sagah123 Date: Thu, 22 Oct 2026 14:29:08 +0200 Subject: [PATCH 11/19] Add subseconds and comments --- .../Protections/ProtectionMessages.hpp | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/Inc/ST-LIB_HIGH/Protections/ProtectionMessages.hpp b/Inc/ST-LIB_HIGH/Protections/ProtectionMessages.hpp index 0cdb49bb5..c84a65145 100644 --- a/Inc/ST-LIB_HIGH/Protections/ProtectionMessages.hpp +++ b/Inc/ST-LIB_HIGH/Protections/ProtectionMessages.hpp @@ -19,11 +19,11 @@ struct ProtectionMessagePacket { struct Package_Data { uint16_t id; - uint8_t hours; uint8_t minutes; uint8_t seconds; - uint8_t reserved; + uint8_t subseconds; + uint16_t reserved; // Just to not forget about it exists }; @@ -33,7 +33,6 @@ struct ProtectionMessagePacket { // Maximum is 8 bytes, not all will be sent always uint8_t value[8]; }; - // According to AI and Google it makes no difference use Macros to calculate Size or Template in this case template static Package make(uint16_t measurement_id, Protections::RuleKind prot, T* value) { @@ -47,7 +46,7 @@ struct ProtectionMessagePacket { .data = {} }; - pack.data.id = (measurement_id & 0x1FFF) | static_cast (static_cast(prot) << 13); + pack.data.id = (measurement_id & 0x1FFF) | static_cast (prot) << 13; // Bit packing of protection into 3 most significant bits of id (Look adjv3 format) if (value != nullptr && valueSize > 0) { memcpy(pack.value, value, valueSize); } @@ -56,10 +55,12 @@ struct ProtectionMessagePacket { pack.data.hours = rtc.hour; pack.data.minutes = rtc.minute; pack.data.seconds = rtc.second; - #else - pack.data.hours = 0; pack.data.minutes = 0; pack.data.seconds = 0; + pack.data.subseconds = static_cast((rtc.counter * 256) / (RTC_MAX_COUNTER + 1)); + #else // if HAL_RTC was not enabled initialize all values a 0 + pack.data.hours = 0; pack.data.minutes = 0; pack.data.seconds = 0; pack.data.subseconds = 0; + #endif - pack.data.reserved = 0; + pack.data.reserved = 0; // however it exists return pack; } //TCP STUFF starts here @@ -84,13 +85,19 @@ struct ProtectionMessagePacket { tcp_connect(tpcb, &destIPADDR, 31, tcp_client_connected); //PORT } } - static void tcp_client_send(const Package& pack) //add some timer?? + static void tcp_client_send(const Package& pack) { if (tpcb != nullptr && is_connected){ //pack.pbuf = pbuf_alloc(PBUF_TRANSPORT, pack.packageSize, PBUF_RAM); im suprized we dont need it - tcp_write(tpcb, &pack.data, pack.packageSize, TCP_WRITE_FLAG_COPY); - - tcp_output(tpcb); + err_t err = ERR_OK; + err = tcp_write(tpcb, &pack.data, pack.packageSize, TCP_WRITE_FLAG_COPY); + if (err != ERR_OK) { + //ERROR + } + err = tcp_output(tpcb); + if (err != ERR_OK) { + //ERROR + } } } From 5086b87b57e6a618adbf0583f228b31cd8ac70c3 Mon Sep 17 00:00:00 2001 From: Sagah123 Date: Thu, 22 Oct 2026 18:36:55 +0200 Subject: [PATCH 12/19] Add comments and subseconds' --- .vscode/c_cpp_properties.json | 36 ++++++-- CMakeLists.txt | 4 +- .../Protections/ProtectionMessages.hpp | 86 ++++--------------- .../Protections/ProtectionMessages.cpp | 77 ++++++++++++++++- 4 files changed, 129 insertions(+), 74 deletions(-) diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 1d6f1d63c..80dbc88f8 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -1,12 +1,38 @@ { "configurations": [ { - "name": "CMake", + "name": "ST-LIB-Dev", + "includePath": [ + "${workspaceFolder}/**", + "${workspaceFolder}/Inc", + "${workspaceFolder}/Inc/ST-LIB_LOW", + "${workspaceFolder}/Inc/ST-LIB_HIGH", + "${workspaceFolder}/Inc/HALAL", + "${workspaceFolder}/Inc/C++Utilities", + "${workspaceFolder}/Inc/MockedDrivers", + "${workspaceFolder}/Inc/STM32CubeH7Wrapper", + "${workspaceFolder}/STM32CubeH7/Drivers/STM32H7xx_HAL_Driver/Inc", + "${workspaceFolder}/STM32CubeH7/Drivers/CMSIS/Device/ST/STM32H7xx/Include", + "${workspaceFolder}/STM32CubeH7/Drivers/CMSIS/Include", + "${workspaceFolder}/STM32CubeH7/Middlewares/Third_Party/LwIP/src/include", + "${workspaceFolder}/STM32CubeH7/Middlewares/Third_Party/LwIP/src/include/lwip", + "${workspaceFolder}/STM32CubeH7/Middlewares/Third_Party/LwIP/contrib/ports/win32/include", + "${workspaceFolder}/LWIP/App", + "${workspaceFolder}/LWIP/Target" + ], + "defines": [ + "SIM_ON", + "STM32H723xx", + "USE_HAL_DRIVER", + "CORE_CM7", + "HAL_ETH_MODULE_ENABLED", + "HAL_RTC_MODULE_ENABLED", + "HAL_UART_MODULE_ENABLED" + ], "cStandard": "c17", - "cppStandard": "c++20", - "configurationProvider": "ms-vscode.cmake-tools", - "compileCommands": "compile_commands.json" - } + "cppStandard": "c++23", + "intelliSenseMode": "windows-gcc-x64" + } ], "version": 4 } diff --git a/CMakeLists.txt b/CMakeLists.txt index 9b20398d1..1766293ef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -384,7 +384,9 @@ set(STLIB_LOW_CPP_NO_ETH set(STLIB_HIGH_C_ETH) -set(STLIB_HIGH_CPP_ETH) +set(STLIB_HIGH_CPP_ETH + ${CMAKE_CURRENT_LIST_DIR}/Src/ST-LIB_HIGH/Protections/ProtectionMessages.cpp +) set(STLIB_HIGH_C_NO_ETH) diff --git a/Inc/ST-LIB_HIGH/Protections/ProtectionMessages.hpp b/Inc/ST-LIB_HIGH/Protections/ProtectionMessages.hpp index c84a65145..ef24f8ab2 100644 --- a/Inc/ST-LIB_HIGH/Protections/ProtectionMessages.hpp +++ b/Inc/ST-LIB_HIGH/Protections/ProtectionMessages.hpp @@ -7,99 +7,51 @@ */ #include "ST-LIB_HIGH/Protections/SampleSource.hpp" #include "HALAL/Services/Diagnostics/Diagnostics.hpp" -#include "HALAL/Services/Time/RTC.hpp" #include "ST-LIB_HIGH/Protections/FaultController.hpp" #include "ST-LIB_HIGH/Protections/Protection.hpp" #include "ST-LIB_HIGH/Protections/ProtectionTypes.hpp" -#include "lwip/tcp.h" +#include +#include +#include + +// Forward declaration of LwIP tcp_pcb +struct tcp_pcb; namespace ST_LIB { + +#pragma pack(push, 1) struct ProtectionMessagePacket { struct Package_Data { - uint16_t id; uint8_t hours; uint8_t minutes; uint8_t seconds; uint8_t subseconds; - uint16_t reserved; // Just to not forget about it exists + uint16_t reserved; // Just to not forget about it exists (align / array index in ADJv3) }; - struct Package { size_t packageSize; Package_Data data; // Maximum is 8 bytes, not all will be sent always uint8_t value[8]; }; +#pragma pack(pop) + template static Package make(uint16_t measurement_id, Protections::RuleKind prot, T* value) { static_assert(sizeof(T) <= 8, "Value size must be <= 8 bytes"); return make(measurement_id, prot, static_cast(value), sizeof(T)); } - static Package make(uint16_t measurement_id, Protections::RuleKind prot, void* value, size_t valueSize) - { - Package pack = { - .packageSize = sizeof(Package_Data) + valueSize, - .data = {} - }; - pack.data.id = (measurement_id & 0x1FFF) | static_cast (prot) << 13; // Bit packing of protection into 3 most significant bits of id (Look adjv3 format) - if (value != nullptr && valueSize > 0) { - memcpy(pack.value, value, valueSize); - } - #if defined(HAL_RTC_MODULE_ENABLED) && !defined(SIM_ON) - RTCData rtc = Global_RTC::get_rtc_timestamp(); - pack.data.hours = rtc.hour; - pack.data.minutes = rtc.minute; - pack.data.seconds = rtc.second; - pack.data.subseconds = static_cast((rtc.counter * 256) / (RTC_MAX_COUNTER + 1)); - #else // if HAL_RTC was not enabled initialize all values a 0 - pack.data.hours = 0; pack.data.minutes = 0; pack.data.seconds = 0; pack.data.subseconds = 0; - - #endif - pack.data.reserved = 0; // however it exists - return pack; - } - //TCP STUFF starts here - static inline struct tcp_pcb* tpcb; - static inline bool is_connected = false; - //This code probably can be refactored - static err_t tcp_client_connected(void* arg, tcp_pcb* tcpb, err_t err) - { - LWIP_UNUSED_ARG(tcpb); - LWIP_UNUSED_ARG(arg); - if (err == ERR_OK) { - is_connected = true; - } - return ERR_OK; - } - static void tcp_client_init() - { - tpcb = tcp_new(); - if (tpcb != NULL) { - ip_addr_t destIPADDR; - IP_ADDR4(&destIPADDR, 192, 168, 0, 100); //IP - tcp_connect(tpcb, &destIPADDR, 31, tcp_client_connected); //PORT - } - } - static void tcp_client_send(const Package& pack) - { - if (tpcb != nullptr && is_connected){ - //pack.pbuf = pbuf_alloc(PBUF_TRANSPORT, pack.packageSize, PBUF_RAM); im suprized we dont need it - err_t err = ERR_OK; - err = tcp_write(tpcb, &pack.data, pack.packageSize, TCP_WRITE_FLAG_COPY); - if (err != ERR_OK) { - //ERROR - } - err = tcp_output(tpcb); - if (err != ERR_OK) { - //ERROR - } - } - } + static Package make(uint16_t measurement_id, Protections::RuleKind prot, void* value, size_t valueSize); + static void tcp_client_init(); + static bool tcp_client_send(const Package& pack); + + static struct tcp_pcb* tpcb; + static bool is_connected; +}; -}; // struct ProtectionMessagePacket -}; // namespace ST_LIB +} // namespace ST_LIB diff --git a/Src/ST-LIB_HIGH/Protections/ProtectionMessages.cpp b/Src/ST-LIB_HIGH/Protections/ProtectionMessages.cpp index e6a1da98b..1a94a0110 100644 --- a/Src/ST-LIB_HIGH/Protections/ProtectionMessages.cpp +++ b/Src/ST-LIB_HIGH/Protections/ProtectionMessages.cpp @@ -1 +1,76 @@ -#include +/* + * ProtectionMessages.cpp + * + * Created on: Oct 22, 2026 + * Author: Aleks + */ +#include "ST-LIB_HIGH/Protections/ProtectionMessages.hpp" +#include "HALAL/Services/Time/RTC.hpp" +#include "lwip/tcp.h" + +namespace ST_LIB { + +struct tcp_pcb* ProtectionMessagePacket::tpcb = nullptr; +bool ProtectionMessagePacket::is_connected = false; + +static err_t tcp_client_connected_callback(void* arg, struct tcp_pcb* pcb, err_t err) +{ + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(pcb); + if (err == ERR_OK) { + ProtectionMessagePacket::is_connected = true; + } + return ERR_OK; +} + +ProtectionMessagePacket::Package ProtectionMessagePacket::make(uint16_t measurement_id, Protections::RuleKind prot, void* value, size_t valueSize) +{ + Package pack = { + .packageSize = sizeof(Package_Data) + valueSize, + .data = {} + }; + + // Bit packing of protection into 3 most significant bits of id (Look adjv3 format) + pack.data.id = (measurement_id & 0x1FFF) | (static_cast(prot) << 13); + + if (value != nullptr && valueSize > 0) { + memcpy(pack.value, value, valueSize > 8 ? 8 : valueSize); + } + +#if defined(HAL_RTC_MODULE_ENABLED) && !defined(SIM_ON) + RTCData rtc = Global_RTC::get_rtc_timestamp(); + pack.data.hours = rtc.hour; + pack.data.minutes = rtc.minute; + pack.data.seconds = rtc.second; + pack.data.subseconds = static_cast((rtc.counter * 256) / (RTC_MAX_COUNTER + 1)); +#else // if HAL_RTC was not enabled initialize all values to 0 + pack.data.hours = 0; pack.data.minutes = 0; pack.data.seconds = 0; pack.data.subseconds = 0; +#endif + pack.data.reserved = 0; + return pack; +} + +void ProtectionMessagePacket::tcp_client_init() +{ + tpcb = tcp_new(); + if (tpcb != nullptr) { + ip_addr_t destIPADDR; + IP_ADDR4(&destIPADDR, 192, 168, 0, 100); // IP Control Station + tcp_connect(tpcb, &destIPADDR, 31, tcp_client_connected_callback); // PORT + } +} + +bool ProtectionMessagePacket:: tcp_client_send(const Package& pack) +{ + if (tpcb != nullptr && is_connected) { + err_t err = tcp_write(tpcb, &pack.data, pack.packageSize, TCP_WRITE_FLAG_COPY); + if (err != ERR_OK) { + return false; + } + err = tcp_output(tpcb); + return (err == ERR_OK); + } + return false; +} + +} // namespace ST_LIB From 367603fb5b053521efa4a136dc4e0cfb0dd8a13e Mon Sep 17 00:00:00 2001 From: Sagah123 Date: Fri, 23 Oct 2026 03:49:40 +0200 Subject: [PATCH 13/19] Change IP to the recommended one --- Src/ST-LIB_HIGH/Protections/ProtectionMessages.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Src/ST-LIB_HIGH/Protections/ProtectionMessages.cpp b/Src/ST-LIB_HIGH/Protections/ProtectionMessages.cpp index 1a94a0110..38284b72a 100644 --- a/Src/ST-LIB_HIGH/Protections/ProtectionMessages.cpp +++ b/Src/ST-LIB_HIGH/Protections/ProtectionMessages.cpp @@ -55,12 +55,12 @@ void ProtectionMessagePacket::tcp_client_init() tpcb = tcp_new(); if (tpcb != nullptr) { ip_addr_t destIPADDR; - IP_ADDR4(&destIPADDR, 192, 168, 0, 100); // IP Control Station + IP_ADDR4(&destIPADDR, 192, 168, 0, 9); // IP PC tcp_connect(tpcb, &destIPADDR, 31, tcp_client_connected_callback); // PORT } } -bool ProtectionMessagePacket:: tcp_client_send(const Package& pack) +bool ProtectionMessagePacket::tcp_client_send(const ProtectionMessagePacket::Package& pack) { if (tpcb != nullptr && is_connected) { err_t err = tcp_write(tpcb, &pack.data, pack.packageSize, TCP_WRITE_FLAG_COPY); From 7fb805fdffa47946d0059a1765b4696ac4c4de62 Mon Sep 17 00:00:00 2001 From: Sagah123 Date: Fri, 23 Oct 2026 10:57:25 +0200 Subject: [PATCH 14/19] Modify hardcoded IP and Port to argumentos of a function. Add better error detection. Change provisional name to ProtectionADJV3Packet --- .../Protections/ProtectionMessages.hpp | 14 ++-- .../Protections/ProtectionMessages.cpp | 84 +++++++++++-------- 2 files changed, 54 insertions(+), 44 deletions(-) diff --git a/Inc/ST-LIB_HIGH/Protections/ProtectionMessages.hpp b/Inc/ST-LIB_HIGH/Protections/ProtectionMessages.hpp index ef24f8ab2..acdea3960 100644 --- a/Inc/ST-LIB_HIGH/Protections/ProtectionMessages.hpp +++ b/Inc/ST-LIB_HIGH/Protections/ProtectionMessages.hpp @@ -20,7 +20,7 @@ struct tcp_pcb; namespace ST_LIB { #pragma pack(push, 1) -struct ProtectionMessagePacket { +struct ProtectionADJV3Packet { struct Package_Data { uint16_t id; @@ -28,7 +28,7 @@ struct ProtectionMessagePacket { uint8_t minutes; uint8_t seconds; uint8_t subseconds; - uint16_t reserved; // Just to not forget about it exists (align / array index in ADJv3) + uint16_t reserved; // Just to not forget about it exists (align / array index in ADJv3) }; struct Package { @@ -40,15 +40,15 @@ struct ProtectionMessagePacket { #pragma pack(pop) template - static Package make(uint16_t measurement_id, Protections::RuleKind prot, T* value) - { + static Package make(uint16_t measurement_id, Protections::RuleKind prot, T* value) { static_assert(sizeof(T) <= 8, "Value size must be <= 8 bytes"); return make(measurement_id, prot, static_cast(value), sizeof(T)); } - static Package make(uint16_t measurement_id, Protections::RuleKind prot, void* value, size_t valueSize); - static void tcp_client_init(); - static bool tcp_client_send(const Package& pack); + static Package + make(uint16_t measurement_id, Protections::RuleKind prot, void* value, size_t valueSize); + static bool tcp_client_init(const char* ip, uint16_t port); + static bool tcp_client_send(Package& pack); static struct tcp_pcb* tpcb; static bool is_connected; diff --git a/Src/ST-LIB_HIGH/Protections/ProtectionMessages.cpp b/Src/ST-LIB_HIGH/Protections/ProtectionMessages.cpp index 38284b72a..7aaa36df9 100644 --- a/Src/ST-LIB_HIGH/Protections/ProtectionMessages.cpp +++ b/Src/ST-LIB_HIGH/Protections/ProtectionMessages.cpp @@ -10,25 +10,25 @@ namespace ST_LIB { -struct tcp_pcb* ProtectionMessagePacket::tpcb = nullptr; -bool ProtectionMessagePacket::is_connected = false; +struct tcp_pcb* ProtectionADJV3Packet::tpcb = nullptr; +bool ProtectionADJV3Packet::is_connected = false; -static err_t tcp_client_connected_callback(void* arg, struct tcp_pcb* pcb, err_t err) -{ +static err_t tcp_client_connected_callback(void* arg, struct tcp_pcb* pcb, err_t err) { LWIP_UNUSED_ARG(arg); LWIP_UNUSED_ARG(pcb); if (err == ERR_OK) { - ProtectionMessagePacket::is_connected = true; + ProtectionADJV3Packet::is_connected = true; } return ERR_OK; } -ProtectionMessagePacket::Package ProtectionMessagePacket::make(uint16_t measurement_id, Protections::RuleKind prot, void* value, size_t valueSize) -{ - Package pack = { - .packageSize = sizeof(Package_Data) + valueSize, - .data = {} - }; +ProtectionADJV3Packet::Package ProtectionADJV3Packet::make( + uint16_t measurement_id, + Protections::RuleKind prot, + void* value, + size_t valueSize +) { + Package pack = {.packageSize = sizeof(Package_Data) + valueSize, .data = {}}; // Bit packing of protection into 3 most significant bits of id (Look adjv3 format) pack.data.id = (measurement_id & 0x1FFF) | (static_cast(prot) << 13); @@ -36,41 +36,51 @@ ProtectionMessagePacket::Package ProtectionMessagePacket::make(uint16_t measurem if (value != nullptr && valueSize > 0) { memcpy(pack.value, value, valueSize > 8 ? 8 : valueSize); } - -#if defined(HAL_RTC_MODULE_ENABLED) && !defined(SIM_ON) - RTCData rtc = Global_RTC::get_rtc_timestamp(); - pack.data.hours = rtc.hour; - pack.data.minutes = rtc.minute; - pack.data.seconds = rtc.second; - pack.data.subseconds = static_cast((rtc.counter * 256) / (RTC_MAX_COUNTER + 1)); -#else // if HAL_RTC was not enabled initialize all values to 0 - pack.data.hours = 0; pack.data.minutes = 0; pack.data.seconds = 0; pack.data.subseconds = 0; -#endif + pack.data.hours = 0; + pack.data.minutes = 0; + pack.data.seconds = 0; + pack.data.subseconds = 0; pack.data.reserved = 0; return pack; } -void ProtectionMessagePacket::tcp_client_init() -{ +bool ProtectionADJV3Packet::tcp_client_init(const char* ip, uint16_t port) { + if (tpcb != nullptr) { // it means we already have one tpcb initialized + return false; + } + ip_addr_t destIPADDR; + if (!ip4addr_aton(ip, &destIPADDR)) { + return false; + } tpcb = tcp_new(); - if (tpcb != nullptr) { - ip_addr_t destIPADDR; - IP_ADDR4(&destIPADDR, 192, 168, 0, 9); // IP PC - tcp_connect(tpcb, &destIPADDR, 31, tcp_client_connected_callback); // PORT + if (tpcb == nullptr) { + return false; // tpcb was not initialized } + err_t err = tcp_connect(tpcb, &destIPADDR, port, tcp_client_connected_callback); + if (err != ERR_OK) { // we got an error while connecting + tcp_abort(tpcb); + tpcb = nullptr; + return false; + } + return true; } -bool ProtectionMessagePacket::tcp_client_send(const ProtectionMessagePacket::Package& pack) -{ - if (tpcb != nullptr && is_connected) { - err_t err = tcp_write(tpcb, &pack.data, pack.packageSize, TCP_WRITE_FLAG_COPY); - if (err != ERR_OK) { - return false; - } - err = tcp_output(tpcb); - return (err == ERR_OK); +bool ProtectionADJV3Packet::tcp_client_send(ProtectionADJV3Packet::Package& pack) { + if (tpcb == nullptr || !is_connected) { + return false; + } +#if defined(HAL_RTC_MODULE_ENABLED) && !defined(SIM_ON) + RTCData rtc = Global_RTC::get_rtc_timestamp(); + pack.data.hours = rtc.hour; + pack.data.minutes = rtc.minute; + pack.data.seconds = rtc.second; + pack.data.subseconds = static_cast((rtc.counter * 256) / (RTC_MAX_COUNTER + 1)); +#endif // if HAL_RTC was not enabled all values were initialized before to 0 + err_t err = tcp_write(tpcb, &pack.data, pack.packageSize, TCP_WRITE_FLAG_COPY); + if (err != ERR_OK) { + return false; } - return false; + return tcp_output(tpcb) == ERR_OK; } } // namespace ST_LIB From c040c24d9c0c80a10134beedd258f2df52a9874b Mon Sep 17 00:00:00 2001 From: Sagah123 Date: Fri, 23 Oct 2026 11:02:55 +0200 Subject: [PATCH 15/19] Add changeset for ADJV3 protections --- .changesets/adjv3-protections-tcp.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .changesets/adjv3-protections-tcp.md diff --git a/.changesets/adjv3-protections-tcp.md b/.changesets/adjv3-protections-tcp.md new file mode 100644 index 000000000..afd5a0688 --- /dev/null +++ b/.changesets/adjv3-protections-tcp.md @@ -0,0 +1,2 @@ +release: minor +summary: Add ProtectionADJV3Packet TCP client with IP passed as an argument, plus improved error handling From 41a53e57ca5653c118a298d1feb13b7edfa1b87a Mon Sep 17 00:00:00 2001 From: Sagah123 Date: Fri, 23 Oct 2026 12:34:40 +0200 Subject: [PATCH 16/19] Revert c_cpp_properties and add inline to template --- .vscode/c_cpp_properties.json | 36 +++++------------------------------ 1 file changed, 5 insertions(+), 31 deletions(-) diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 80dbc88f8..1d6f1d63c 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -1,38 +1,12 @@ { "configurations": [ { - "name": "ST-LIB-Dev", - "includePath": [ - "${workspaceFolder}/**", - "${workspaceFolder}/Inc", - "${workspaceFolder}/Inc/ST-LIB_LOW", - "${workspaceFolder}/Inc/ST-LIB_HIGH", - "${workspaceFolder}/Inc/HALAL", - "${workspaceFolder}/Inc/C++Utilities", - "${workspaceFolder}/Inc/MockedDrivers", - "${workspaceFolder}/Inc/STM32CubeH7Wrapper", - "${workspaceFolder}/STM32CubeH7/Drivers/STM32H7xx_HAL_Driver/Inc", - "${workspaceFolder}/STM32CubeH7/Drivers/CMSIS/Device/ST/STM32H7xx/Include", - "${workspaceFolder}/STM32CubeH7/Drivers/CMSIS/Include", - "${workspaceFolder}/STM32CubeH7/Middlewares/Third_Party/LwIP/src/include", - "${workspaceFolder}/STM32CubeH7/Middlewares/Third_Party/LwIP/src/include/lwip", - "${workspaceFolder}/STM32CubeH7/Middlewares/Third_Party/LwIP/contrib/ports/win32/include", - "${workspaceFolder}/LWIP/App", - "${workspaceFolder}/LWIP/Target" - ], - "defines": [ - "SIM_ON", - "STM32H723xx", - "USE_HAL_DRIVER", - "CORE_CM7", - "HAL_ETH_MODULE_ENABLED", - "HAL_RTC_MODULE_ENABLED", - "HAL_UART_MODULE_ENABLED" - ], + "name": "CMake", "cStandard": "c17", - "cppStandard": "c++23", - "intelliSenseMode": "windows-gcc-x64" - } + "cppStandard": "c++20", + "configurationProvider": "ms-vscode.cmake-tools", + "compileCommands": "compile_commands.json" + } ], "version": 4 } From 60e47341269a53cf7c5ff8f7593040458faac866 Mon Sep 17 00:00:00 2001 From: Sagah123 Date: Fri, 23 Oct 2026 13:30:31 +0200 Subject: [PATCH 17/19] Add make raw make() private (FoolProof) and read value at send time --- .../Protections/ProtectionMessages.hpp | 19 +++++++++++-------- .../Protections/ProtectionMessages.cpp | 5 ++++- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/Inc/ST-LIB_HIGH/Protections/ProtectionMessages.hpp b/Inc/ST-LIB_HIGH/Protections/ProtectionMessages.hpp index acdea3960..2eb4aa089 100644 --- a/Inc/ST-LIB_HIGH/Protections/ProtectionMessages.hpp +++ b/Inc/ST-LIB_HIGH/Protections/ProtectionMessages.hpp @@ -14,12 +14,9 @@ #include #include -// Forward declaration of LwIP tcp_pcb struct tcp_pcb; namespace ST_LIB { - -#pragma pack(push, 1) struct ProtectionADJV3Packet { struct Package_Data { @@ -28,7 +25,7 @@ struct ProtectionADJV3Packet { uint8_t minutes; uint8_t seconds; uint8_t subseconds; - uint16_t reserved; // Just to not forget about it exists (align / array index in ADJv3) + uint16_t reserved; // Just to not forget about it exists }; struct Package { @@ -36,17 +33,23 @@ struct ProtectionADJV3Packet { Package_Data data; // Maximum is 8 bytes, not all will be sent always uint8_t value[8]; + const void* source; }; -#pragma pack(pop) template - static Package make(uint16_t measurement_id, Protections::RuleKind prot, T* value) { + static inline Package make(uint16_t measurement_id, Protections::RuleKind prot, T* value) { static_assert(sizeof(T) <= 8, "Value size must be <= 8 bytes"); return make(measurement_id, prot, static_cast(value), sizeof(T)); } - static Package - make(uint16_t measurement_id, Protections::RuleKind prot, void* value, size_t valueSize); +private: + static Package make( + uint16_t measurement_id, + Protections::RuleKind prot, + void* value, + size_t valueSize + ); // Foolproof +public: static bool tcp_client_init(const char* ip, uint16_t port); static bool tcp_client_send(Package& pack); diff --git a/Src/ST-LIB_HIGH/Protections/ProtectionMessages.cpp b/Src/ST-LIB_HIGH/Protections/ProtectionMessages.cpp index 7aaa36df9..5535337b5 100644 --- a/Src/ST-LIB_HIGH/Protections/ProtectionMessages.cpp +++ b/Src/ST-LIB_HIGH/Protections/ProtectionMessages.cpp @@ -34,7 +34,7 @@ ProtectionADJV3Packet::Package ProtectionADJV3Packet::make( pack.data.id = (measurement_id & 0x1FFF) | (static_cast(prot) << 13); if (value != nullptr && valueSize > 0) { - memcpy(pack.value, value, valueSize > 8 ? 8 : valueSize); + pack.source = value; // could it be a dangling pointer ? } pack.data.hours = 0; pack.data.minutes = 0; @@ -69,6 +69,9 @@ bool ProtectionADJV3Packet::tcp_client_send(ProtectionADJV3Packet::Package& pack if (tpcb == nullptr || !is_connected) { return false; } + if (pack.source != nullptr) { + memcpy(pack.value, pack.source, pack.packageSize - sizeof(Package_Data)); + } #if defined(HAL_RTC_MODULE_ENABLED) && !defined(SIM_ON) RTCData rtc = Global_RTC::get_rtc_timestamp(); pack.data.hours = rtc.hour; From c31f7907a27e8286409be1a6dc2a45b38828355b Mon Sep 17 00:00:00 2001 From: Sagah123 Date: Fri, 23 Oct 2026 17:04:38 +0200 Subject: [PATCH 18/19] test --- Src/ST-LIB_HIGH/Protections/ProtectionMessages.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/ST-LIB_HIGH/Protections/ProtectionMessages.cpp b/Src/ST-LIB_HIGH/Protections/ProtectionMessages.cpp index 5535337b5..903635aae 100644 --- a/Src/ST-LIB_HIGH/Protections/ProtectionMessages.cpp +++ b/Src/ST-LIB_HIGH/Protections/ProtectionMessages.cpp @@ -80,7 +80,7 @@ bool ProtectionADJV3Packet::tcp_client_send(ProtectionADJV3Packet::Package& pack pack.data.subseconds = static_cast((rtc.counter * 256) / (RTC_MAX_COUNTER + 1)); #endif // if HAL_RTC was not enabled all values were initialized before to 0 err_t err = tcp_write(tpcb, &pack.data, pack.packageSize, TCP_WRITE_FLAG_COPY); - if (err != ERR_OK) { + if (err != ERR_MEM) { return false; } return tcp_output(tpcb) == ERR_OK; From 13fc4b75d9cdb235c7e8588db2983c2d0b5e2c72 Mon Sep 17 00:00:00 2001 From: Sagah123 Date: Wed, 28 Oct 2026 11:36:24 +0100 Subject: [PATCH 19/19] Change make implementation and modify structure names --- .../Protections/ProtectionMessages.hpp | 24 ++----- .../Protections/ProtectionMessages.cpp | 63 +++++++++---------- 2 files changed, 37 insertions(+), 50 deletions(-) diff --git a/Inc/ST-LIB_HIGH/Protections/ProtectionMessages.hpp b/Inc/ST-LIB_HIGH/Protections/ProtectionMessages.hpp index 2eb4aa089..ac8341431 100644 --- a/Inc/ST-LIB_HIGH/Protections/ProtectionMessages.hpp +++ b/Inc/ST-LIB_HIGH/Protections/ProtectionMessages.hpp @@ -17,9 +17,9 @@ struct tcp_pcb; namespace ST_LIB { -struct ProtectionADJV3Packet { +struct ProtectionADJv3Packet { - struct Package_Data { + struct Data { uint16_t id; uint8_t hours; uint8_t minutes; @@ -30,28 +30,16 @@ struct ProtectionADJV3Packet { struct Package { size_t packageSize; - Package_Data data; + Data data; // Maximum is 8 bytes, not all will be sent always uint8_t value[8]; - const void* source; }; - template - static inline Package make(uint16_t measurement_id, Protections::RuleKind prot, T* value) { - static_assert(sizeof(T) <= 8, "Value size must be <= 8 bytes"); - return make(measurement_id, prot, static_cast(value), sizeof(T)); - } - -private: - static Package make( - uint16_t measurement_id, - Protections::RuleKind prot, - void* value, - size_t valueSize - ); // Foolproof public: + static Package + make(uint16_t measurement_id, Protections::RuleKind protection, size_t value_size); static bool tcp_client_init(const char* ip, uint16_t port); - static bool tcp_client_send(Package& pack); + static bool tcp_client_send(Package& pack, const void* value); static struct tcp_pcb* tpcb; static bool is_connected; diff --git a/Src/ST-LIB_HIGH/Protections/ProtectionMessages.cpp b/Src/ST-LIB_HIGH/Protections/ProtectionMessages.cpp index 903635aae..f07f79dec 100644 --- a/Src/ST-LIB_HIGH/Protections/ProtectionMessages.cpp +++ b/Src/ST-LIB_HIGH/Protections/ProtectionMessages.cpp @@ -1,6 +1,6 @@ /* * ProtectionMessages.cpp - * + * "Hello there" * Created on: Oct 22, 2026 * Author: Aleks */ @@ -10,32 +10,14 @@ namespace ST_LIB { -struct tcp_pcb* ProtectionADJV3Packet::tpcb = nullptr; -bool ProtectionADJV3Packet::is_connected = false; - -static err_t tcp_client_connected_callback(void* arg, struct tcp_pcb* pcb, err_t err) { - LWIP_UNUSED_ARG(arg); - LWIP_UNUSED_ARG(pcb); - if (err == ERR_OK) { - ProtectionADJV3Packet::is_connected = true; - } - return ERR_OK; -} - -ProtectionADJV3Packet::Package ProtectionADJV3Packet::make( - uint16_t measurement_id, - Protections::RuleKind prot, - void* value, - size_t valueSize -) { - Package pack = {.packageSize = sizeof(Package_Data) + valueSize, .data = {}}; +struct tcp_pcb* ProtectionADJv3Packet::tpcb = nullptr; +bool ProtectionADJv3Packet::is_connected = false; +ProtectionADJv3Packet::Package +ProtectionADJv3Packet::make(uint16_t measurement_id, Protections::RuleKind prot, size_t valueSize) { + Package pack = {.packageSize = sizeof(Data) + valueSize, .data = {}}; // Bit packing of protection into 3 most significant bits of id (Look adjv3 format) pack.data.id = (measurement_id & 0x1FFF) | (static_cast(prot) << 13); - - if (value != nullptr && valueSize > 0) { - pack.source = value; // could it be a dangling pointer ? - } pack.data.hours = 0; pack.data.minutes = 0; pack.data.seconds = 0; @@ -44,18 +26,34 @@ ProtectionADJV3Packet::Package ProtectionADJV3Packet::make( return pack; } -bool ProtectionADJV3Packet::tcp_client_init(const char* ip, uint16_t port) { +static err_t tcp_client_connected_callback(void* arg, struct tcp_pcb* pcb, err_t err) { + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(pcb); + if (err == ERR_OK) { + ProtectionADJv3Packet::is_connected = true; + } + return ERR_OK; +} +static void tcp_client_err_callback(void* arg, err_t err) { + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(err); + ProtectionADJv3Packet::tpcb = nullptr; // lwip already has freed the tpcb, only pointer is left + ProtectionADJv3Packet::is_connected = false; +} + +bool ProtectionADJv3Packet::tcp_client_init(const char* ip, uint16_t port) { if (tpcb != nullptr) { // it means we already have one tpcb initialized return false; } ip_addr_t destIPADDR; if (!ip4addr_aton(ip, &destIPADDR)) { - return false; + return false; // ip was not converted } tpcb = tcp_new(); if (tpcb == nullptr) { return false; // tpcb was not initialized } + tcp_err(tpcb, tcp_client_err_callback); err_t err = tcp_connect(tpcb, &destIPADDR, port, tcp_client_connected_callback); if (err != ERR_OK) { // we got an error while connecting tcp_abort(tpcb); @@ -64,14 +62,15 @@ bool ProtectionADJV3Packet::tcp_client_init(const char* ip, uint16_t port) { } return true; } - -bool ProtectionADJV3Packet::tcp_client_send(ProtectionADJV3Packet::Package& pack) { +bool ProtectionADJv3Packet::tcp_client_send( + ProtectionADJv3Packet::Package& pack, + const void* value +) { if (tpcb == nullptr || !is_connected) { return false; } - if (pack.source != nullptr) { - memcpy(pack.value, pack.source, pack.packageSize - sizeof(Package_Data)); - } + + memcpy(pack.value, value, pack.packageSize - sizeof(Data)); #if defined(HAL_RTC_MODULE_ENABLED) && !defined(SIM_ON) RTCData rtc = Global_RTC::get_rtc_timestamp(); pack.data.hours = rtc.hour; @@ -80,7 +79,7 @@ bool ProtectionADJV3Packet::tcp_client_send(ProtectionADJV3Packet::Package& pack pack.data.subseconds = static_cast((rtc.counter * 256) / (RTC_MAX_COUNTER + 1)); #endif // if HAL_RTC was not enabled all values were initialized before to 0 err_t err = tcp_write(tpcb, &pack.data, pack.packageSize, TCP_WRITE_FLAG_COPY); - if (err != ERR_MEM) { + if (err != ERR_OK) { return false; } return tcp_output(tpcb) == ERR_OK;