From ce4688dcc692df1378d0ebaaedb272464e6466b1 Mon Sep 17 00:00:00 2001 From: Cantonplas Date: Wed, 4 Mar 2026 17:55:50 +0100 Subject: [PATCH 01/11] Prescaler was not initialized in init --- Inc/HALAL/Services/Encoder/Encoder.hpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Inc/HALAL/Services/Encoder/Encoder.hpp b/Inc/HALAL/Services/Encoder/Encoder.hpp index b41370031..4a36663e8 100644 --- a/Inc/HALAL/Services/Encoder/Encoder.hpp +++ b/Inc/HALAL/Services/Encoder/Encoder.hpp @@ -38,12 +38,6 @@ template class Encoder { TIM_Encoder_InitTypeDef sConfig = {0}; TIM_MasterConfigTypeDef sMasterConfig = {0}; - tim->instance->hal_tim->Init.Prescaler = 5; - tim->instance->hal_tim->Init.CounterMode = TIM_COUNTERMODE_UP; - tim->instance->hal_tim->Init.Period = 55000; - tim->instance->hal_tim->Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; - tim->instance->hal_tim->Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; - sConfig.EncoderMode = TIM_ENCODERMODE_TI12; sConfig.IC1Polarity = TIM_ICPOLARITY_RISING; sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI; @@ -64,6 +58,9 @@ template class Encoder { HAL_OK) { ErrorHandler("Unable to config master synchronization in encoder"); } + + tim->instance->tim->PSC = 5; + tim->instance->tim->ARR = 55000; } static void turn_on() { From 4e1c6c8545db36bf4753e8c7fa4de71606000366 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20L=C3=B3pez?= <120128034+victor-Lopez25@users.noreply.github.com> Date: Wed, 4 Mar 2026 18:13:32 +0100 Subject: [PATCH 02/11] formatting: Remove spaces --- Inc/HALAL/Services/Encoder/Encoder.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Inc/HALAL/Services/Encoder/Encoder.hpp b/Inc/HALAL/Services/Encoder/Encoder.hpp index 4a36663e8..e3ce5eee7 100644 --- a/Inc/HALAL/Services/Encoder/Encoder.hpp +++ b/Inc/HALAL/Services/Encoder/Encoder.hpp @@ -60,7 +60,7 @@ template class Encoder { } tim->instance->tim->PSC = 5; - tim->instance->tim->ARR = 55000; + tim->instance->tim->ARR = 55000; } static void turn_on() { @@ -109,3 +109,4 @@ template class Encoder { } // namespace ST_LIB #endif + From eca65443dc4c837bc6a4cc8d4971637303f78e19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20L=C3=B3pez?= <120128034+victor-Lopez25@users.noreply.github.com> Date: Wed, 4 Mar 2026 18:15:23 +0100 Subject: [PATCH 03/11] formatting --- Inc/HALAL/Services/Encoder/Encoder.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Inc/HALAL/Services/Encoder/Encoder.hpp b/Inc/HALAL/Services/Encoder/Encoder.hpp index e3ce5eee7..e288da8a1 100644 --- a/Inc/HALAL/Services/Encoder/Encoder.hpp +++ b/Inc/HALAL/Services/Encoder/Encoder.hpp @@ -109,4 +109,3 @@ template class Encoder { } // namespace ST_LIB #endif - From 93b4c5ad0c1a28ae62405a7be7fe8a167e858bec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20S=C3=A1ez?= Date: Wed, 4 Mar 2026 19:34:00 +0100 Subject: [PATCH 04/11] hardened a lot the encoder, being sure that instances are properly set --- Inc/HALAL/Services/Encoder/Encoder.hpp | 215 +++++++++++++++++-------- 1 file changed, 150 insertions(+), 65 deletions(-) diff --git a/Inc/HALAL/Services/Encoder/Encoder.hpp b/Inc/HALAL/Services/Encoder/Encoder.hpp index e288da8a1..24faff2b3 100644 --- a/Inc/HALAL/Services/Encoder/Encoder.hpp +++ b/Inc/HALAL/Services/Encoder/Encoder.hpp @@ -11,7 +11,7 @@ namespace ST_LIB { template struct TimerWrapper; -template class Encoder { +template class Encoder { static_assert( dev.e.pin_count == 2, "Encoder must have exactly 2 encoder pins, as it uses the whole timer" @@ -23,20 +23,57 @@ template class Encoder { "Pins must be of different channels" ); - inline static TimerWrapper* timer; - inline static bool is_on = false; - -public: - Encoder(TimerWrapper* tim) { - if (timer == nullptr) { - init(tim); - } - } - - static void init(TimerWrapper* tim) { - timer = tim; - TIM_Encoder_InitTypeDef sConfig = {0}; - TIM_MasterConfigTypeDef sMasterConfig = {0}; + inline static TimerWrapper* timer; + inline static bool is_on = false; + + static TimerWrapper* require_timer_wrapper(const char* error_message) { + if (timer == nullptr) { + ErrorHandler(error_message); + return nullptr; + } + return timer; + } + + static TimerDomain::Instance* require_counter_instance(const char* error_message) { + TimerWrapper* timer_wrapper = require_timer_wrapper(error_message); + if (timer_wrapper == nullptr) { + return nullptr; + } + if (timer_wrapper->instance == nullptr || timer_wrapper->instance->tim == nullptr) { + ErrorHandler(error_message); + return nullptr; + } + return timer_wrapper->instance; + } + + static TimerDomain::Instance* require_hal_instance(const char* error_message) { + TimerDomain::Instance* instance = require_counter_instance(error_message); + if (instance == nullptr) { + return nullptr; + } + if (instance->hal_tim == nullptr) { + ErrorHandler(error_message); + return nullptr; + } + return instance; + } + +public: + Encoder(TimerWrapper* tim) { + if (timer == nullptr) { + init(tim); + } + } + + static void init(TimerWrapper* tim) { + if (tim == nullptr || tim->instance == nullptr || tim->instance->tim == nullptr || + tim->instance->hal_tim == nullptr) { + ErrorHandler("Unable to init encoder, timer is null"); + return; + } + + TIM_Encoder_InitTypeDef sConfig = {0}; + TIM_MasterConfigTypeDef sMasterConfig = {0}; sConfig.EncoderMode = TIM_ENCODERMODE_TI12; sConfig.IC1Polarity = TIM_ICPOLARITY_RISING; @@ -47,63 +84,111 @@ template class Encoder { sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI; sConfig.IC2Prescaler = TIM_ICPSC_DIV1; sConfig.IC2Filter = 0; - - if (HAL_TIM_Encoder_Init(tim->instance->hal_tim, &sConfig) != HAL_OK) { - ErrorHandler("Unable to init encoder"); - } + + if (HAL_TIM_Encoder_Init(tim->instance->hal_tim, &sConfig) != HAL_OK) { + ErrorHandler("Unable to init encoder"); + return; + } sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; - if (HAL_TIMEx_MasterConfigSynchronization(tim->instance->hal_tim, &sMasterConfig) != - HAL_OK) { - ErrorHandler("Unable to config master synchronization in encoder"); - } - - tim->instance->tim->PSC = 5; - tim->instance->tim->ARR = 55000; - } - - static void turn_on() { - if (is_on) - return; - - if (HAL_TIM_Encoder_GetState(timer->instance->hal_tim) == HAL_TIM_STATE_RESET) { - ErrorHandler("Unable to get state from encoder"); - return; - } - if (HAL_TIM_Encoder_Start(timer->instance->hal_tim, TIM_CHANNEL_ALL) != HAL_OK) { - ErrorHandler("Unable to start encoder"); - return; - } + if (HAL_TIMEx_MasterConfigSynchronization(tim->instance->hal_tim, &sMasterConfig) != + HAL_OK) { + ErrorHandler("Unable to config master synchronization in encoder"); + return; + } + + tim->instance->tim->PSC = 5; + tim->instance->tim->ARR = 55000; + timer = tim; + } + + static void turn_on() { + if (is_on) + return; + TimerDomain::Instance* instance = + require_hal_instance("Unable to start encoder, timer is not initialized"); + if (instance == nullptr) { + return; + } + + if (HAL_TIM_Encoder_GetState(instance->hal_tim) == HAL_TIM_STATE_RESET) { + ErrorHandler("Unable to get state from encoder"); + return; + } + if (HAL_TIM_Encoder_Start(instance->hal_tim, TIM_CHANNEL_ALL) != HAL_OK) { + ErrorHandler("Unable to start encoder"); + return; + } is_on = true; reset(); } - static void turn_off() { - if (!is_on) - return; - if (HAL_TIM_Encoder_Stop(timer->instance->hal_tim, TIM_CHANNEL_ALL) != HAL_OK) { - ErrorHandler("Unable to stop encoder"); - } - is_on = false; - } - - static inline void reset() { timer->instance->tim->CNT = UINT32_MAX / 2; } - - static inline uint32_t get_counter() { return timer->instance->tim->CNT; } - - static inline bool get_direction() { return ((timer->instance->tim->CR1 & 0b10000) >> 4); } - - static inline uint32_t get_initial_counter_value() { return timer->instance->tim->ARR / 2; } - - static int64_t get_delta_clock(uint64_t clock_time, uint64_t last_clock_time) { - int64_t delta_clock = clock_time - last_clock_time; - if (clock_time < last_clock_time) { // overflow handle - delta_clock = clock_time + - CLOCK_MAX_VALUE * NANO_SECOND / timer->get_clock_frequency() - - last_clock_time; - } - return delta_clock; + static void turn_off() { + if (!is_on) + return; + TimerDomain::Instance* instance = + require_hal_instance("Unable to stop encoder, timer is not initialized"); + if (instance == nullptr) { + return; + } + if (HAL_TIM_Encoder_Stop(instance->hal_tim, TIM_CHANNEL_ALL) != HAL_OK) { + ErrorHandler("Unable to stop encoder"); + return; + } + is_on = false; + } + + static inline void reset() { + TimerDomain::Instance* instance = + require_counter_instance("Unable to reset encoder, timer is not initialized"); + if (instance == nullptr) { + return; + } + instance->tim->CNT = get_initial_counter_value(); + } + + static inline uint32_t get_counter() { + TimerDomain::Instance* instance = + require_counter_instance("Unable to read encoder counter, timer is not initialized"); + if (instance == nullptr) { + return 0; + } + return instance->tim->CNT; + } + + static inline bool get_direction() { + TimerDomain::Instance* instance = + require_counter_instance("Unable to read encoder direction, timer is not initialized"); + if (instance == nullptr) { + return false; + } + return ((instance->tim->CR1 & 0b10000) >> 4); + } + + static inline uint32_t get_initial_counter_value() { + TimerDomain::Instance* instance = require_counter_instance( + "Unable to read encoder initial counter, timer is not initialized" + ); + if (instance == nullptr) { + return 0; + } + return instance->tim->ARR / 2; + } + + static int64_t get_delta_clock(uint64_t clock_time, uint64_t last_clock_time) { + TimerWrapper* timer_wrapper = + require_timer_wrapper("Unable to compute encoder delta clock, timer is not initialized"); + if (timer_wrapper == nullptr) { + return 0; + } + int64_t delta_clock = clock_time - last_clock_time; + if (clock_time < last_clock_time) { // overflow handle + delta_clock = clock_time + + CLOCK_MAX_VALUE * NANO_SECOND / timer_wrapper->get_clock_frequency() - + last_clock_time; + } + return delta_clock; } }; From 621c376f4c5db57635888acad7ed40d415892949 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20S=C3=A1ez?= Date: Wed, 4 Mar 2026 19:34:26 +0100 Subject: [PATCH 05/11] minor fix, not setting properly the encoder start --- .../EncoderSensor/NewEncoderSensor.hpp | 81 +++++++++---------- 1 file changed, 40 insertions(+), 41 deletions(-) diff --git a/Inc/ST-LIB_LOW/Sensors/EncoderSensor/NewEncoderSensor.hpp b/Inc/ST-LIB_LOW/Sensors/EncoderSensor/NewEncoderSensor.hpp index f8d52c1bf..21d8ccbcd 100644 --- a/Inc/ST-LIB_LOW/Sensors/EncoderSensor/NewEncoderSensor.hpp +++ b/Inc/ST-LIB_LOW/Sensors/EncoderSensor/NewEncoderSensor.hpp @@ -6,19 +6,19 @@ */ #pragma once -#include "HALAL/HALAL.hpp" -// #include "HALAL/Services/Encoder/NewEncoder.hpp" +#include "C++Utilities/CppUtils.hpp" namespace ST_LIB { -template struct EncoderSensor { - enum Direction : uint8_t { FORWARD = 0, BACKWARDS = 1 }; - -private: - constexpr static int64_t START_COUNTER{UINT32_MAX / 2}; - - const double counter_distance_m; - const double sample_time_s; +template struct EncoderSensor { + enum Direction : uint8_t { FORWARD = 0, BACKWARDS = 1 }; + +private: + constexpr static size_t WINDOW_SIZE{(SAMPLES / 2) * 2}; + static_assert(WINDOW_SIZE >= 2, "EncoderSensor requires at least two samples"); + + const double counter_distance_m; + const double sample_time_s; EncoderType& encoder; @@ -30,54 +30,53 @@ template struct EncoderSensor { double* acceleration; public: - EncoderSensor( - EncoderType& enc, - const double counter_distance_m, + EncoderSensor( + EncoderType& enc, + const double counter_distance_m, const double sample_time_s, Direction* direction, double* position, double* speed, double* acceleration - ) - : counter_distance_m(counter_distance_m), sample_time_s(sample_time_s), encoder(enc), - direction(direction), position(position), speed(speed), acceleration(acceleration) { - for (size_t i{0}; i < SAMPLES; i++) - past_delta_counters.push(0); - } + ) + : counter_distance_m(counter_distance_m), sample_time_s(sample_time_s), encoder(enc), + direction(direction), position(position), speed(speed), acceleration(acceleration) { + for (size_t i{0}; i < WINDOW_SIZE; i++) + past_delta_counters.push(0); + } void turn_on() { encoder.turn_on(); } void turn_off() { encoder.turn_off(); } - void reset() { - encoder.reset(); - for (size_t i{0}; i < SAMPLES; ++i) - past_delta_counters.push_pop(0); - } + void reset() { + encoder.reset(); + for (size_t i{0}; i < WINDOW_SIZE; ++i) + past_delta_counters.push_pop(0); + } // must be called on equally spaced time periods - void read() { - uint32_t counter{encoder.get_counter()}; - - int64_t delta_counter{(int64_t)counter - START_COUNTER}; - const int64_t& previous_delta_counter{ - past_delta_counters[past_delta_counters.size() / 2 - 1] - }; + void read() { + uint32_t counter{encoder.get_counter()}; + + int64_t delta_counter{(int64_t)counter - (int64_t)encoder.get_initial_counter_value()}; + const int64_t& previous_delta_counter{ + past_delta_counters[past_delta_counters.size() / 2 - 1] + }; const int64_t& previous_previous_delta_counter{ past_delta_counters[past_delta_counters.size() - 1] }; *position = delta_counter * counter_distance_m; - // https://en.wikipedia.org/wiki/Finite_difference_coefficient#Backward_finite_difference - *speed = ((3.0 * delta_counter / 2.0) - (2.0 * previous_delta_counter) + - (previous_previous_delta_counter / 2.0)) * - counter_distance_m / (sample_time_s * past_delta_counters.size() / 2); - - *acceleration = - (delta_counter - (2.0 * previous_delta_counter) + previous_previous_delta_counter) * - counter_distance_m / - ((sample_time_s * past_delta_counters.size() / 2) * - (sample_time_s * past_delta_counters.size() / 2)); + // https://en.wikipedia.org/wiki/Finite_difference_coefficient#Backward_finite_difference + *speed = ((3.0 * delta_counter / 2.0) - (2.0 * previous_delta_counter) + + (previous_previous_delta_counter / 2.0)) * + counter_distance_m / (sample_time_s * WINDOW_SIZE / 2); + + *acceleration = + (delta_counter - (2.0 * previous_delta_counter) + previous_previous_delta_counter) * + counter_distance_m / + ((sample_time_s * WINDOW_SIZE / 2) * (sample_time_s * WINDOW_SIZE / 2)); *direction = encoder.get_direction() ? FORWARD : BACKWARDS; From fefe358fbd7ad4f37aa6136d6637b550bbbed780 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20S=C3=A1ez?= Date: Wed, 4 Mar 2026 19:34:38 +0100 Subject: [PATCH 06/11] added tests for the encoder --- Tests/CMakeLists.txt | 1 + Tests/Time/encoder_test.cpp | 158 ++++++++++++++++++++++++++++++++++++ 2 files changed, 159 insertions(+) create mode 100644 Tests/Time/encoder_test.cpp diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 0c882b8a7..845f52058 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -19,6 +19,7 @@ message(STATUS "Generating test executable for ST-LIB") add_executable(${STLIB_TEST_EXECUTABLE} ${CMAKE_CURRENT_LIST_DIR}/../Src/HALAL/Models/SPI/SPI2.cpp ${CMAKE_CURRENT_LIST_DIR}/../Src/HALAL/Models/DMA/DMA2.cpp + ${CMAKE_CURRENT_LIST_DIR}/Time/encoder_test.cpp ${CMAKE_CURRENT_LIST_DIR}/Time/scheduler_test.cpp ${CMAKE_CURRENT_LIST_DIR}/Time/timer_wrapper_test.cpp ${CMAKE_CURRENT_LIST_DIR}/adc_test.cpp diff --git a/Tests/Time/encoder_test.cpp b/Tests/Time/encoder_test.cpp new file mode 100644 index 000000000..bd2b2accf --- /dev/null +++ b/Tests/Time/encoder_test.cpp @@ -0,0 +1,158 @@ +#include + +#include "HALAL/Services/Time/TimerWrapper.hpp" +#include "ST-LIB_LOW/Sensors/EncoderSensor/NewEncoderSensor.hpp" + +namespace ST_LIB::TestErrorHandler { +void reset(); +void set_fail_on_error(bool enabled); +extern int call_count; +} // namespace ST_LIB::TestErrorHandler + +namespace { + +constexpr ST_LIB::TimerDomain::Timer encoder_timer_decl{ + ST_LIB::TimerRequest::GeneralPurpose32bit_2, + ST_LIB::TimerDomain::EMPTY_TIMER_NAME, + ST_LIB::TimerPin{ + .af = ST_LIB::TimerAF::Encoder, + .pin = ST_LIB::PA0, + .channel = ST_LIB::TimerChannel::CHANNEL_1, + }, + ST_LIB::TimerPin{ + .af = ST_LIB::TimerAF::Encoder, + .pin = ST_LIB::PA1, + .channel = ST_LIB::TimerChannel::CHANNEL_2, + }, +}; + +struct MockEncoder { + uint32_t counter = 1234; + uint32_t initial_counter = 1234; + bool direction = true; + int turn_on_calls = 0; + int turn_off_calls = 0; + int reset_calls = 0; + + void turn_on() { ++turn_on_calls; } + void turn_off() { ++turn_off_calls; } + void reset() { + ++reset_calls; + counter = initial_counter; + } + uint32_t get_counter() { return counter; } + bool get_direction() { return direction; } + uint32_t get_initial_counter_value() { return initial_counter; } +}; + +using MockSensor = ST_LIB::EncoderSensor; + +} // namespace + +class EncoderTest : public ::testing::Test { +protected: + TIM_HandleTypeDef hal_tim{}; + ST_LIB::TimerDomain::Instance instance{}; + ST_LIB::TimerWrapper wrapper{}; + + void SetUp() override { + ST_LIB::TestErrorHandler::reset(); + + TIM2_BASE->CNT = 0U; + TIM2_BASE->ARR = 0U; + TIM2_BASE->PSC = 0U; + TIM2_BASE->CR1 = 0U; + + hal_tim = {}; + hal_tim.Instance = TIM2_BASE; + hal_tim.State = HAL_TIM_STATE_READY; + + instance.tim = TIM2_BASE; + instance.hal_tim = &hal_tim; + instance.timer_idx = 0U; + + wrapper = ST_LIB::TimerWrapper(&instance); + ST_LIB::Encoder::init(&wrapper); + ST_LIB::Encoder::turn_off(); + } +}; + +TEST_F(EncoderTest, ResetUsesConfiguredInitialCounterValue) { + TIM2_BASE->CNT = 17U; + + ST_LIB::Encoder::reset(); + + EXPECT_EQ(TIM2_BASE->ARR, 55000U); + EXPECT_EQ( + TIM2_BASE->CNT, + ST_LIB::Encoder::get_initial_counter_value() + ); +} + +TEST_F(EncoderTest, TurnOffKeepsTryingIfHALStopFails) { + ST_LIB::TestErrorHandler::set_fail_on_error(false); + ST_LIB::Encoder::turn_on(); + + instance.hal_tim = nullptr; + + ST_LIB::Encoder::turn_off(); + ST_LIB::Encoder::turn_off(); + + EXPECT_EQ(ST_LIB::TestErrorHandler::call_count, 2); +} + +TEST(EncoderSensorTest, ReadTreatsEncoderInitialCounterAsZeroPosition) { + MockEncoder encoder{}; + double position = -1.0; + double speed = -1.0; + double acceleration = -1.0; + MockSensor::Direction direction = MockSensor::BACKWARDS; + + MockSensor sensor( + encoder, + 0.5, + 0.1, + &direction, + &position, + &speed, + &acceleration + ); + + sensor.read(); + + EXPECT_DOUBLE_EQ(position, 0.0); + EXPECT_DOUBLE_EQ(speed, 0.0); + EXPECT_DOUBLE_EQ(acceleration, 0.0); + EXPECT_EQ(direction, MockSensor::FORWARD); +} + +TEST(EncoderSensorTest, ResetForwardsToEncoderAndClearsHistory) { + MockEncoder encoder{}; + encoder.counter = 1240U; + + double position = 0.0; + double speed = 0.0; + double acceleration = 0.0; + MockSensor::Direction direction = MockSensor::BACKWARDS; + + MockSensor sensor( + encoder, + 1.0, + 1.0, + &direction, + &position, + &speed, + &acceleration + ); + + sensor.read(); + ASSERT_NE(position, 0.0); + + sensor.reset(); + sensor.read(); + + EXPECT_EQ(encoder.reset_calls, 1); + EXPECT_DOUBLE_EQ(position, 0.0); + EXPECT_DOUBLE_EQ(speed, 0.0); + EXPECT_DOUBLE_EQ(acceleration, 0.0); +} From 891f219f804a147e5e0e15fd769baed42026f03f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20S=C3=A1ez?= Date: Wed, 4 Mar 2026 20:09:35 +0100 Subject: [PATCH 07/11] reverted nullptr checks, and made builder private --- Inc/HALAL/Services/Encoder/Encoder.hpp | 102 +++---------------------- 1 file changed, 11 insertions(+), 91 deletions(-) diff --git a/Inc/HALAL/Services/Encoder/Encoder.hpp b/Inc/HALAL/Services/Encoder/Encoder.hpp index 24faff2b3..806b2bce8 100644 --- a/Inc/HALAL/Services/Encoder/Encoder.hpp +++ b/Inc/HALAL/Services/Encoder/Encoder.hpp @@ -26,52 +26,16 @@ template class Encoder { inline static TimerWrapper* timer; inline static bool is_on = false; - static TimerWrapper* require_timer_wrapper(const char* error_message) { - if (timer == nullptr) { - ErrorHandler(error_message); - return nullptr; - } - return timer; - } - - static TimerDomain::Instance* require_counter_instance(const char* error_message) { - TimerWrapper* timer_wrapper = require_timer_wrapper(error_message); - if (timer_wrapper == nullptr) { - return nullptr; - } - if (timer_wrapper->instance == nullptr || timer_wrapper->instance->tim == nullptr) { - ErrorHandler(error_message); - return nullptr; - } - return timer_wrapper->instance; - } - - static TimerDomain::Instance* require_hal_instance(const char* error_message) { - TimerDomain::Instance* instance = require_counter_instance(error_message); - if (instance == nullptr) { - return nullptr; - } - if (instance->hal_tim == nullptr) { - ErrorHandler(error_message); - return nullptr; - } - return instance; - } - -public: Encoder(TimerWrapper* tim) { if (timer == nullptr) { init(tim); } } - static void init(TimerWrapper* tim) { - if (tim == nullptr || tim->instance == nullptr || tim->instance->tim == nullptr || - tim->instance->hal_tim == nullptr) { - ErrorHandler("Unable to init encoder, timer is null"); - return; - } + friend struct TimerWrapper; +public: + static void init(TimerWrapper* tim) { TIM_Encoder_InitTypeDef sConfig = {0}; TIM_MasterConfigTypeDef sMasterConfig = {0}; @@ -106,17 +70,12 @@ template class Encoder { static void turn_on() { if (is_on) return; - TimerDomain::Instance* instance = - require_hal_instance("Unable to start encoder, timer is not initialized"); - if (instance == nullptr) { - return; - } - if (HAL_TIM_Encoder_GetState(instance->hal_tim) == HAL_TIM_STATE_RESET) { + if (HAL_TIM_Encoder_GetState(timer->instance->hal_tim) == HAL_TIM_STATE_RESET) { ErrorHandler("Unable to get state from encoder"); return; } - if (HAL_TIM_Encoder_Start(instance->hal_tim, TIM_CHANNEL_ALL) != HAL_OK) { + if (HAL_TIM_Encoder_Start(timer->instance->hal_tim, TIM_CHANNEL_ALL) != HAL_OK) { ErrorHandler("Unable to start encoder"); return; } @@ -127,65 +86,26 @@ template class Encoder { static void turn_off() { if (!is_on) return; - TimerDomain::Instance* instance = - require_hal_instance("Unable to stop encoder, timer is not initialized"); - if (instance == nullptr) { - return; - } - if (HAL_TIM_Encoder_Stop(instance->hal_tim, TIM_CHANNEL_ALL) != HAL_OK) { + if (HAL_TIM_Encoder_Stop(timer->instance->hal_tim, TIM_CHANNEL_ALL) != HAL_OK) { ErrorHandler("Unable to stop encoder"); return; } is_on = false; } - static inline void reset() { - TimerDomain::Instance* instance = - require_counter_instance("Unable to reset encoder, timer is not initialized"); - if (instance == nullptr) { - return; - } - instance->tim->CNT = get_initial_counter_value(); - } + static inline void reset() { timer->instance->tim->CNT = get_initial_counter_value(); } - static inline uint32_t get_counter() { - TimerDomain::Instance* instance = - require_counter_instance("Unable to read encoder counter, timer is not initialized"); - if (instance == nullptr) { - return 0; - } - return instance->tim->CNT; - } + static inline uint32_t get_counter() { return timer->instance->tim->CNT; } - static inline bool get_direction() { - TimerDomain::Instance* instance = - require_counter_instance("Unable to read encoder direction, timer is not initialized"); - if (instance == nullptr) { - return false; - } - return ((instance->tim->CR1 & 0b10000) >> 4); - } + static inline bool get_direction() { return ((timer->instance->tim->CR1 & 0b10000) >> 4); } - static inline uint32_t get_initial_counter_value() { - TimerDomain::Instance* instance = require_counter_instance( - "Unable to read encoder initial counter, timer is not initialized" - ); - if (instance == nullptr) { - return 0; - } - return instance->tim->ARR / 2; - } + static inline uint32_t get_initial_counter_value() { return timer->instance->tim->ARR / 2; } static int64_t get_delta_clock(uint64_t clock_time, uint64_t last_clock_time) { - TimerWrapper* timer_wrapper = - require_timer_wrapper("Unable to compute encoder delta clock, timer is not initialized"); - if (timer_wrapper == nullptr) { - return 0; - } int64_t delta_clock = clock_time - last_clock_time; if (clock_time < last_clock_time) { // overflow handle delta_clock = clock_time + - CLOCK_MAX_VALUE * NANO_SECOND / timer_wrapper->get_clock_frequency() - + CLOCK_MAX_VALUE * NANO_SECOND / timer->get_clock_frequency() - last_clock_time; } return delta_clock; From 637e10753e0feb0940dfaa5cca2a9c60dfaae67a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20S=C3=A1ez?= Date: Wed, 4 Mar 2026 20:10:34 +0100 Subject: [PATCH 08/11] applied formatter --- Inc/HALAL/Services/Encoder/Encoder.hpp | 92 +++++++++---------- .../EncoderSensor/NewEncoderSensor.hpp | 80 ++++++++-------- Tests/Time/encoder_test.cpp | 25 +---- 3 files changed, 89 insertions(+), 108 deletions(-) diff --git a/Inc/HALAL/Services/Encoder/Encoder.hpp b/Inc/HALAL/Services/Encoder/Encoder.hpp index 806b2bce8..13961e845 100644 --- a/Inc/HALAL/Services/Encoder/Encoder.hpp +++ b/Inc/HALAL/Services/Encoder/Encoder.hpp @@ -1,28 +1,28 @@ -#pragma once - -#include "HALAL/Models/TimerDomain/TimerDomain.hpp" - -#ifdef HAL_TIM_MODULE_ENABLED - -#define NANO_SECOND 1000000000.0 -#define CLOCK_MAX_VALUE 4294967295 // here goes the tim23 counter period - -namespace ST_LIB { - -template struct TimerWrapper; - +#pragma once + +#include "HALAL/Models/TimerDomain/TimerDomain.hpp" + +#ifdef HAL_TIM_MODULE_ENABLED + +#define NANO_SECOND 1000000000.0 +#define CLOCK_MAX_VALUE 4294967295 // here goes the tim23 counter period + +namespace ST_LIB { + +template struct TimerWrapper; + template class Encoder { - static_assert( - dev.e.pin_count == 2, - "Encoder must have exactly 2 encoder pins, as it uses the whole timer" - ); - static_assert(dev.e.pins[0].af == TimerAF::Encoder, "Pin 0 must be declared as encoder"); - static_assert(dev.e.pins[1].af == TimerAF::Encoder, "Pin 1 must be declared as encoder"); - static_assert( - dev.e.pins[0].channel != dev.e.pins[1].channel, - "Pins must be of different channels" - ); - + static_assert( + dev.e.pin_count == 2, + "Encoder must have exactly 2 encoder pins, as it uses the whole timer" + ); + static_assert(dev.e.pins[0].af == TimerAF::Encoder, "Pin 0 must be declared as encoder"); + static_assert(dev.e.pins[1].af == TimerAF::Encoder, "Pin 1 must be declared as encoder"); + static_assert( + dev.e.pins[0].channel != dev.e.pins[1].channel, + "Pins must be of different channels" + ); + inline static TimerWrapper* timer; inline static bool is_on = false; @@ -38,24 +38,24 @@ template class Encoder { static void init(TimerWrapper* tim) { TIM_Encoder_InitTypeDef sConfig = {0}; TIM_MasterConfigTypeDef sMasterConfig = {0}; - - sConfig.EncoderMode = TIM_ENCODERMODE_TI12; - sConfig.IC1Polarity = TIM_ICPOLARITY_RISING; - sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI; - sConfig.IC1Prescaler = TIM_ICPSC_DIV1; - sConfig.IC1Filter = 0; - sConfig.IC2Polarity = TIM_ICPOLARITY_RISING; - sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI; - sConfig.IC2Prescaler = TIM_ICPSC_DIV1; - sConfig.IC2Filter = 0; + + sConfig.EncoderMode = TIM_ENCODERMODE_TI12; + sConfig.IC1Polarity = TIM_ICPOLARITY_RISING; + sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI; + sConfig.IC1Prescaler = TIM_ICPSC_DIV1; + sConfig.IC1Filter = 0; + sConfig.IC2Polarity = TIM_ICPOLARITY_RISING; + sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI; + sConfig.IC2Prescaler = TIM_ICPSC_DIV1; + sConfig.IC2Filter = 0; if (HAL_TIM_Encoder_Init(tim->instance->hal_tim, &sConfig) != HAL_OK) { ErrorHandler("Unable to init encoder"); return; } - - sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; - sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; + + sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; + sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; if (HAL_TIMEx_MasterConfigSynchronization(tim->instance->hal_tim, &sMasterConfig) != HAL_OK) { ErrorHandler("Unable to config master synchronization in encoder"); @@ -79,10 +79,10 @@ template class Encoder { ErrorHandler("Unable to start encoder"); return; } - is_on = true; - reset(); - } - + is_on = true; + reset(); + } + static void turn_off() { if (!is_on) return; @@ -109,8 +109,8 @@ template class Encoder { last_clock_time; } return delta_clock; - } -}; - -} // namespace ST_LIB -#endif + } +}; + +} // namespace ST_LIB +#endif diff --git a/Inc/ST-LIB_LOW/Sensors/EncoderSensor/NewEncoderSensor.hpp b/Inc/ST-LIB_LOW/Sensors/EncoderSensor/NewEncoderSensor.hpp index 21d8ccbcd..7bcf98eb7 100644 --- a/Inc/ST-LIB_LOW/Sensors/EncoderSensor/NewEncoderSensor.hpp +++ b/Inc/ST-LIB_LOW/Sensors/EncoderSensor/NewEncoderSensor.hpp @@ -6,19 +6,19 @@ */ #pragma once -#include "C++Utilities/CppUtils.hpp" +#include "C++Utilities/CppUtils.hpp" namespace ST_LIB { -template struct EncoderSensor { - enum Direction : uint8_t { FORWARD = 0, BACKWARDS = 1 }; - -private: - constexpr static size_t WINDOW_SIZE{(SAMPLES / 2) * 2}; - static_assert(WINDOW_SIZE >= 2, "EncoderSensor requires at least two samples"); - - const double counter_distance_m; - const double sample_time_s; +template struct EncoderSensor { + enum Direction : uint8_t { FORWARD = 0, BACKWARDS = 1 }; + +private: + constexpr static size_t WINDOW_SIZE{(SAMPLES / 2) * 2}; + static_assert(WINDOW_SIZE >= 2, "EncoderSensor requires at least two samples"); + + const double counter_distance_m; + const double sample_time_s; EncoderType& encoder; @@ -30,53 +30,53 @@ template struct EncoderSensor { double* acceleration; public: - EncoderSensor( - EncoderType& enc, - const double counter_distance_m, + EncoderSensor( + EncoderType& enc, + const double counter_distance_m, const double sample_time_s, Direction* direction, double* position, double* speed, double* acceleration - ) - : counter_distance_m(counter_distance_m), sample_time_s(sample_time_s), encoder(enc), - direction(direction), position(position), speed(speed), acceleration(acceleration) { - for (size_t i{0}; i < WINDOW_SIZE; i++) - past_delta_counters.push(0); - } + ) + : counter_distance_m(counter_distance_m), sample_time_s(sample_time_s), encoder(enc), + direction(direction), position(position), speed(speed), acceleration(acceleration) { + for (size_t i{0}; i < WINDOW_SIZE; i++) + past_delta_counters.push(0); + } void turn_on() { encoder.turn_on(); } void turn_off() { encoder.turn_off(); } - void reset() { - encoder.reset(); - for (size_t i{0}; i < WINDOW_SIZE; ++i) - past_delta_counters.push_pop(0); - } + void reset() { + encoder.reset(); + for (size_t i{0}; i < WINDOW_SIZE; ++i) + past_delta_counters.push_pop(0); + } // must be called on equally spaced time periods - void read() { - uint32_t counter{encoder.get_counter()}; - - int64_t delta_counter{(int64_t)counter - (int64_t)encoder.get_initial_counter_value()}; - const int64_t& previous_delta_counter{ - past_delta_counters[past_delta_counters.size() / 2 - 1] - }; + void read() { + uint32_t counter{encoder.get_counter()}; + + int64_t delta_counter{(int64_t)counter - (int64_t)encoder.get_initial_counter_value()}; + const int64_t& previous_delta_counter{ + past_delta_counters[past_delta_counters.size() / 2 - 1] + }; const int64_t& previous_previous_delta_counter{ past_delta_counters[past_delta_counters.size() - 1] }; *position = delta_counter * counter_distance_m; - // https://en.wikipedia.org/wiki/Finite_difference_coefficient#Backward_finite_difference - *speed = ((3.0 * delta_counter / 2.0) - (2.0 * previous_delta_counter) + - (previous_previous_delta_counter / 2.0)) * - counter_distance_m / (sample_time_s * WINDOW_SIZE / 2); - - *acceleration = - (delta_counter - (2.0 * previous_delta_counter) + previous_previous_delta_counter) * - counter_distance_m / - ((sample_time_s * WINDOW_SIZE / 2) * (sample_time_s * WINDOW_SIZE / 2)); + // https://en.wikipedia.org/wiki/Finite_difference_coefficient#Backward_finite_difference + *speed = ((3.0 * delta_counter / 2.0) - (2.0 * previous_delta_counter) + + (previous_previous_delta_counter / 2.0)) * + counter_distance_m / (sample_time_s * WINDOW_SIZE / 2); + + *acceleration = + (delta_counter - (2.0 * previous_delta_counter) + previous_previous_delta_counter) * + counter_distance_m / + ((sample_time_s * WINDOW_SIZE / 2) * (sample_time_s * WINDOW_SIZE / 2)); *direction = encoder.get_direction() ? FORWARD : BACKWARDS; diff --git a/Tests/Time/encoder_test.cpp b/Tests/Time/encoder_test.cpp index bd2b2accf..9d73cf27c 100644 --- a/Tests/Time/encoder_test.cpp +++ b/Tests/Time/encoder_test.cpp @@ -83,10 +83,7 @@ TEST_F(EncoderTest, ResetUsesConfiguredInitialCounterValue) { ST_LIB::Encoder::reset(); EXPECT_EQ(TIM2_BASE->ARR, 55000U); - EXPECT_EQ( - TIM2_BASE->CNT, - ST_LIB::Encoder::get_initial_counter_value() - ); + EXPECT_EQ(TIM2_BASE->CNT, ST_LIB::Encoder::get_initial_counter_value()); } TEST_F(EncoderTest, TurnOffKeepsTryingIfHALStopFails) { @@ -108,15 +105,7 @@ TEST(EncoderSensorTest, ReadTreatsEncoderInitialCounterAsZeroPosition) { double acceleration = -1.0; MockSensor::Direction direction = MockSensor::BACKWARDS; - MockSensor sensor( - encoder, - 0.5, - 0.1, - &direction, - &position, - &speed, - &acceleration - ); + MockSensor sensor(encoder, 0.5, 0.1, &direction, &position, &speed, &acceleration); sensor.read(); @@ -135,15 +124,7 @@ TEST(EncoderSensorTest, ResetForwardsToEncoderAndClearsHistory) { double acceleration = 0.0; MockSensor::Direction direction = MockSensor::BACKWARDS; - MockSensor sensor( - encoder, - 1.0, - 1.0, - &direction, - &position, - &speed, - &acceleration - ); + MockSensor sensor(encoder, 1.0, 1.0, &direction, &position, &speed, &acceleration); sensor.read(); ASSERT_NE(position, 0.0); From 1accdd81f47dff9bc60e7a34c81d10d0506955b9 Mon Sep 17 00:00:00 2001 From: Cantonplas Date: Wed, 4 Mar 2026 20:12:19 +0100 Subject: [PATCH 09/11] Make constructors private in Encoder, PWM, DualPWM --- Inc/HALAL/Services/Encoder/Encoder.hpp | 191 ++++++++----------------- Inc/HALAL/Services/PWM/DualPWM.hpp | 4 +- Inc/HALAL/Services/PWM/PWM.hpp | 4 +- 3 files changed, 65 insertions(+), 134 deletions(-) diff --git a/Inc/HALAL/Services/Encoder/Encoder.hpp b/Inc/HALAL/Services/Encoder/Encoder.hpp index 24faff2b3..0b0863a81 100644 --- a/Inc/HALAL/Services/Encoder/Encoder.hpp +++ b/Inc/HALAL/Services/Encoder/Encoder.hpp @@ -1,97 +1,59 @@ -#pragma once - -#include "HALAL/Models/TimerDomain/TimerDomain.hpp" - -#ifdef HAL_TIM_MODULE_ENABLED - -#define NANO_SECOND 1000000000.0 -#define CLOCK_MAX_VALUE 4294967295 // here goes the tim23 counter period - -namespace ST_LIB { - -template struct TimerWrapper; - -template class Encoder { - static_assert( - dev.e.pin_count == 2, - "Encoder must have exactly 2 encoder pins, as it uses the whole timer" - ); - static_assert(dev.e.pins[0].af == TimerAF::Encoder, "Pin 0 must be declared as encoder"); - static_assert(dev.e.pins[1].af == TimerAF::Encoder, "Pin 1 must be declared as encoder"); - static_assert( - dev.e.pins[0].channel != dev.e.pins[1].channel, - "Pins must be of different channels" - ); - - inline static TimerWrapper* timer; - inline static bool is_on = false; +#pragma once - static TimerWrapper* require_timer_wrapper(const char* error_message) { - if (timer == nullptr) { - ErrorHandler(error_message); - return nullptr; - } - return timer; - } +#include "HALAL/Models/TimerDomain/TimerDomain.hpp" - static TimerDomain::Instance* require_counter_instance(const char* error_message) { - TimerWrapper* timer_wrapper = require_timer_wrapper(error_message); - if (timer_wrapper == nullptr) { - return nullptr; - } - if (timer_wrapper->instance == nullptr || timer_wrapper->instance->tim == nullptr) { - ErrorHandler(error_message); - return nullptr; - } - return timer_wrapper->instance; - } +#ifdef HAL_TIM_MODULE_ENABLED - static TimerDomain::Instance* require_hal_instance(const char* error_message) { - TimerDomain::Instance* instance = require_counter_instance(error_message); - if (instance == nullptr) { - return nullptr; - } - if (instance->hal_tim == nullptr) { - ErrorHandler(error_message); - return nullptr; - } - return instance; - } +#define NANO_SECOND 1000000000.0 +#define CLOCK_MAX_VALUE 4294967295 // here goes the tim23 counter period + +namespace ST_LIB { + +template struct TimerWrapper; + +template class Encoder { + friend TimerWrapper; + + static_assert( + dev.e.pin_count == 2, + "Encoder must have exactly 2 encoder pins, as it uses the whole timer" + ); + static_assert(dev.e.pins[0].af == TimerAF::Encoder, "Pin 0 must be declared as encoder"); + static_assert(dev.e.pins[1].af == TimerAF::Encoder, "Pin 1 must be declared as encoder"); + static_assert( + dev.e.pins[0].channel != dev.e.pins[1].channel, + "Pins must be of different channels" + ); + + inline static TimerWrapper* timer; + inline static bool is_on = false; -public: Encoder(TimerWrapper* tim) { - if (timer == nullptr) { - init(tim); - } + init(tim); } +public: static void init(TimerWrapper* tim) { - if (tim == nullptr || tim->instance == nullptr || tim->instance->tim == nullptr || - tim->instance->hal_tim == nullptr) { - ErrorHandler("Unable to init encoder, timer is null"); - return; - } - TIM_Encoder_InitTypeDef sConfig = {0}; TIM_MasterConfigTypeDef sMasterConfig = {0}; - - sConfig.EncoderMode = TIM_ENCODERMODE_TI12; - sConfig.IC1Polarity = TIM_ICPOLARITY_RISING; - sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI; - sConfig.IC1Prescaler = TIM_ICPSC_DIV1; - sConfig.IC1Filter = 0; - sConfig.IC2Polarity = TIM_ICPOLARITY_RISING; - sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI; - sConfig.IC2Prescaler = TIM_ICPSC_DIV1; - sConfig.IC2Filter = 0; + + sConfig.EncoderMode = TIM_ENCODERMODE_TI12; + sConfig.IC1Polarity = TIM_ICPOLARITY_RISING; + sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI; + sConfig.IC1Prescaler = TIM_ICPSC_DIV1; + sConfig.IC1Filter = 0; + sConfig.IC2Polarity = TIM_ICPOLARITY_RISING; + sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI; + sConfig.IC2Prescaler = TIM_ICPSC_DIV1; + sConfig.IC2Filter = 0; if (HAL_TIM_Encoder_Init(tim->instance->hal_tim, &sConfig) != HAL_OK) { ErrorHandler("Unable to init encoder"); return; } - - sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; - sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; + + sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; + sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; if (HAL_TIMEx_MasterConfigSynchronization(tim->instance->hal_tim, &sMasterConfig) != HAL_OK) { ErrorHandler("Unable to config master synchronization in encoder"); @@ -106,33 +68,24 @@ template class Encoder { static void turn_on() { if (is_on) return; - TimerDomain::Instance* instance = - require_hal_instance("Unable to start encoder, timer is not initialized"); - if (instance == nullptr) { - return; - } - if (HAL_TIM_Encoder_GetState(instance->hal_tim) == HAL_TIM_STATE_RESET) { + if (HAL_TIM_Encoder_GetState(timer->instance->hal_tim) == HAL_TIM_STATE_RESET) { ErrorHandler("Unable to get state from encoder"); return; } - if (HAL_TIM_Encoder_Start(instance->hal_tim, TIM_CHANNEL_ALL) != HAL_OK) { + if (HAL_TIM_Encoder_Start(timer->instance->hal_tim, TIM_CHANNEL_ALL) != HAL_OK) { ErrorHandler("Unable to start encoder"); return; } - is_on = true; - reset(); - } - + is_on = true; + reset(); + } + static void turn_off() { if (!is_on) return; - TimerDomain::Instance* instance = - require_hal_instance("Unable to stop encoder, timer is not initialized"); - if (instance == nullptr) { - return; - } - if (HAL_TIM_Encoder_Stop(instance->hal_tim, TIM_CHANNEL_ALL) != HAL_OK) { + + if (HAL_TIM_Encoder_Stop(timer->hal_tim, TIM_CHANNEL_ALL) != HAL_OK) { ErrorHandler("Unable to stop encoder"); return; } @@ -140,57 +93,31 @@ template class Encoder { } static inline void reset() { - TimerDomain::Instance* instance = - require_counter_instance("Unable to reset encoder, timer is not initialized"); - if (instance == nullptr) { - return; - } - instance->tim->CNT = get_initial_counter_value(); + timer->instance->tim->CNT = get_initial_counter_value(); } static inline uint32_t get_counter() { - TimerDomain::Instance* instance = - require_counter_instance("Unable to read encoder counter, timer is not initialized"); - if (instance == nullptr) { - return 0; - } - return instance->tim->CNT; + return timer->instance->tim->CNT; } static inline bool get_direction() { - TimerDomain::Instance* instance = - require_counter_instance("Unable to read encoder direction, timer is not initialized"); - if (instance == nullptr) { - return false; - } - return ((instance->tim->CR1 & 0b10000) >> 4); + return ((timer->instance->tim->CR1 & 0b10000) >> 4); } static inline uint32_t get_initial_counter_value() { - TimerDomain::Instance* instance = require_counter_instance( - "Unable to read encoder initial counter, timer is not initialized" - ); - if (instance == nullptr) { - return 0; - } - return instance->tim->ARR / 2; + return timer->instance->tim->ARR / 2; } static int64_t get_delta_clock(uint64_t clock_time, uint64_t last_clock_time) { - TimerWrapper* timer_wrapper = - require_timer_wrapper("Unable to compute encoder delta clock, timer is not initialized"); - if (timer_wrapper == nullptr) { - return 0; - } int64_t delta_clock = clock_time - last_clock_time; if (clock_time < last_clock_time) { // overflow handle delta_clock = clock_time + - CLOCK_MAX_VALUE * NANO_SECOND / timer_wrapper->get_clock_frequency() - + CLOCK_MAX_VALUE * NANO_SECOND / timer->get_clock_frequency() - last_clock_time; } return delta_clock; - } -}; - -} // namespace ST_LIB -#endif + } +}; + +} // namespace ST_LIB +#endif diff --git a/Inc/HALAL/Services/PWM/DualPWM.hpp b/Inc/HALAL/Services/PWM/DualPWM.hpp index 26c36d073..d706528e8 100644 --- a/Inc/HALAL/Services/PWM/DualPWM.hpp +++ b/Inc/HALAL/Services/PWM/DualPWM.hpp @@ -19,6 +19,8 @@ template < const ST_LIB::TimerPin pin, const ST_LIB::TimerPin negated_pin> class DualPWM { + friend TimerWrapper; + static consteval uint8_t get_channel_state_idx(const ST_LIB::TimerChannel ch) { switch (ch) { case TimerChannel::CHANNEL_1: @@ -70,7 +72,6 @@ class DualPWM { bool is_on_positive = false; bool is_on_negative = false; -public: DualPWM( TimerWrapper* tim, uint32_t polarity, @@ -97,6 +98,7 @@ class DualPWM { timer->template config_output_compare_channel(&sConfigOC); timer->template set_output_compare_preload_enable(); } +public: inline void turn_on() { turn_on_positive(); diff --git a/Inc/HALAL/Services/PWM/PWM.hpp b/Inc/HALAL/Services/PWM/PWM.hpp index b1d4cdb3a..bba5e0a43 100644 --- a/Inc/HALAL/Services/PWM/PWM.hpp +++ b/Inc/HALAL/Services/PWM/PWM.hpp @@ -15,6 +15,8 @@ namespace ST_LIB { template struct TimerWrapper; template class PWM { + friend TimerWrapper; + static consteval uint8_t get_channel_state_idx(const ST_LIB::TimerChannel ch) { switch (ch) { case TimerChannel::CHANNEL_1: @@ -65,7 +67,6 @@ template class PWM { float* duty_cycle = nullptr; bool is_on = false; -public: PWM(TimerWrapper* tim, uint32_t polarity, uint32_t negated_polarity, @@ -89,6 +90,7 @@ template class PWM { timer->template config_output_compare_channel(&sConfigOC); timer->template set_output_compare_preload_enable(); } +public: void turn_on() { if (this->is_on) From a421a42041abb2bc1f3322e47aa49f03d8e07fcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20S=C3=A1ez?= Date: Wed, 4 Mar 2026 20:19:53 +0100 Subject: [PATCH 10/11] applied formatter --- Inc/HALAL/Services/Encoder/Encoder.hpp | 108 ++++++++++++------------- Inc/HALAL/Services/PWM/DualPWM.hpp | 2 +- Inc/HALAL/Services/PWM/PWM.hpp | 2 +- 3 files changed, 53 insertions(+), 59 deletions(-) diff --git a/Inc/HALAL/Services/Encoder/Encoder.hpp b/Inc/HALAL/Services/Encoder/Encoder.hpp index 7c57be33f..5211daf23 100644 --- a/Inc/HALAL/Services/Encoder/Encoder.hpp +++ b/Inc/HALAL/Services/Encoder/Encoder.hpp @@ -28,11 +28,9 @@ template class Encoder { inline static TimerWrapper* timer; inline static bool is_on = false; - Encoder(TimerWrapper* tim) { - init(tim); - } -public: + Encoder(TimerWrapper* tim) { init(tim); } +public: static void init(TimerWrapper* tim) { TIM_Encoder_InitTypeDef sConfig = {0}; TIM_MasterConfigTypeDef sMasterConfig = {0}; @@ -70,57 +68,53 @@ template class Encoder { return; if (HAL_TIM_Encoder_GetState(timer->instance->hal_tim) == HAL_TIM_STATE_RESET) { - if (HAL_TIM_Encoder_GetState(timer->instance->hal_tim) == HAL_TIM_STATE_RESET) { - ErrorHandler("Unable to get state from encoder"); - return; - } - if (HAL_TIM_Encoder_Start(timer->instance->hal_tim, TIM_CHANNEL_ALL) != HAL_OK) { - if (HAL_TIM_Encoder_Start(timer->instance->hal_tim, TIM_CHANNEL_ALL) != HAL_OK) { - ErrorHandler("Unable to start encoder"); - return; - } - is_on = true; - reset(); - } - - static void turn_off() { - if (!is_on) - return; - - if (HAL_TIM_Encoder_Stop(timer->hal_tim, TIM_CHANNEL_ALL) != HAL_OK) { - ErrorHandler("Unable to stop encoder"); - return; - } - is_on = false; - } - - static inline void reset() { - timer->instance->tim->CNT = get_initial_counter_value(); - } - - static inline uint32_t get_counter() { - return timer->instance->tim->CNT; - } - - static inline bool get_direction() { - return ((timer->instance->tim->CR1 & 0b10000) >> 4); - } - - static inline uint32_t get_initial_counter_value() { - return timer->instance->tim->ARR / 2; - } - - static int64_t get_delta_clock(uint64_t clock_time, uint64_t last_clock_time) { - int64_t delta_clock = clock_time - last_clock_time; - if (clock_time < last_clock_time) { // overflow handle - delta_clock = clock_time + - CLOCK_MAX_VALUE * NANO_SECOND / timer->get_clock_frequency() - - CLOCK_MAX_VALUE * NANO_SECOND / timer->get_clock_frequency() - - last_clock_time; - } - return delta_clock; - } -}; - -} // namespace ST_LIB + if (HAL_TIM_Encoder_GetState(timer->instance->hal_tim) == HAL_TIM_STATE_RESET) { + ErrorHandler("Unable to get state from encoder"); + return; + } + if (HAL_TIM_Encoder_Start(timer->instance->hal_tim, TIM_CHANNEL_ALL) != HAL_OK) { + if (HAL_TIM_Encoder_Start(timer->instance->hal_tim, TIM_CHANNEL_ALL) != HAL_OK) { + ErrorHandler("Unable to start encoder"); + return; + } + is_on = true; + reset(); + } + + static void turn_off() { + if (!is_on) + return; + + if (HAL_TIM_Encoder_Stop(timer->hal_tim, TIM_CHANNEL_ALL) != HAL_OK) { + ErrorHandler("Unable to stop encoder"); + return; + } + is_on = false; + } + + static inline void reset() { timer->instance->tim->CNT = get_initial_counter_value(); } + + static inline uint32_t get_counter() { return timer->instance->tim->CNT; } + + static inline bool get_direction() { + return ((timer->instance->tim->CR1 & 0b10000) >> 4); + } + + static inline uint32_t get_initial_counter_value() { + return timer->instance->tim->ARR / 2; + } + + static int64_t get_delta_clock(uint64_t clock_time, uint64_t last_clock_time) { + int64_t delta_clock = clock_time - last_clock_time; + if (clock_time < last_clock_time) { // overflow handle + delta_clock = clock_time + + CLOCK_MAX_VALUE * NANO_SECOND / timer->get_clock_frequency() - + CLOCK_MAX_VALUE * NANO_SECOND / timer->get_clock_frequency() - + last_clock_time; + } + return delta_clock; + } + }; + + } // namespace ST_LIB #endif diff --git a/Inc/HALAL/Services/PWM/DualPWM.hpp b/Inc/HALAL/Services/PWM/DualPWM.hpp index d706528e8..f4a315535 100644 --- a/Inc/HALAL/Services/PWM/DualPWM.hpp +++ b/Inc/HALAL/Services/PWM/DualPWM.hpp @@ -98,8 +98,8 @@ class DualPWM { timer->template config_output_compare_channel(&sConfigOC); timer->template set_output_compare_preload_enable(); } -public: +public: inline void turn_on() { turn_on_positive(); turn_on_negative(); diff --git a/Inc/HALAL/Services/PWM/PWM.hpp b/Inc/HALAL/Services/PWM/PWM.hpp index bba5e0a43..9c3da2ff0 100644 --- a/Inc/HALAL/Services/PWM/PWM.hpp +++ b/Inc/HALAL/Services/PWM/PWM.hpp @@ -90,8 +90,8 @@ template class PWM { timer->template config_output_compare_channel(&sConfigOC); timer->template set_output_compare_preload_enable(); } -public: +public: void turn_on() { if (this->is_on) return; From 36670f264f469b84a4b77728b4da4cd9d4bf07ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20S=C3=A1ez?= Date: Wed, 4 Mar 2026 20:38:34 +0100 Subject: [PATCH 11/11] fixed merge mess --- Inc/HALAL/Services/Encoder/Encoder.hpp | 99 +++++++++++++------------- 1 file changed, 48 insertions(+), 51 deletions(-) diff --git a/Inc/HALAL/Services/Encoder/Encoder.hpp b/Inc/HALAL/Services/Encoder/Encoder.hpp index 5211daf23..24f3fa30b 100644 --- a/Inc/HALAL/Services/Encoder/Encoder.hpp +++ b/Inc/HALAL/Services/Encoder/Encoder.hpp @@ -12,7 +12,7 @@ namespace ST_LIB { template struct TimerWrapper; template class Encoder { - friend TimerWrapper; + friend struct TimerWrapper; static_assert( dev.e.pin_count == 2, @@ -28,7 +28,11 @@ template class Encoder { inline static TimerWrapper* timer; inline static bool is_on = false; - Encoder(TimerWrapper* tim) { init(tim); } + Encoder(TimerWrapper* tim) { + if (timer == nullptr) { + init(tim); + } + } public: static void init(TimerWrapper* tim) { @@ -68,53 +72,46 @@ template class Encoder { return; if (HAL_TIM_Encoder_GetState(timer->instance->hal_tim) == HAL_TIM_STATE_RESET) { - if (HAL_TIM_Encoder_GetState(timer->instance->hal_tim) == HAL_TIM_STATE_RESET) { - ErrorHandler("Unable to get state from encoder"); - return; - } - if (HAL_TIM_Encoder_Start(timer->instance->hal_tim, TIM_CHANNEL_ALL) != HAL_OK) { - if (HAL_TIM_Encoder_Start(timer->instance->hal_tim, TIM_CHANNEL_ALL) != HAL_OK) { - ErrorHandler("Unable to start encoder"); - return; - } - is_on = true; - reset(); - } - - static void turn_off() { - if (!is_on) - return; - - if (HAL_TIM_Encoder_Stop(timer->hal_tim, TIM_CHANNEL_ALL) != HAL_OK) { - ErrorHandler("Unable to stop encoder"); - return; - } - is_on = false; - } - - static inline void reset() { timer->instance->tim->CNT = get_initial_counter_value(); } - - static inline uint32_t get_counter() { return timer->instance->tim->CNT; } - - static inline bool get_direction() { - return ((timer->instance->tim->CR1 & 0b10000) >> 4); - } - - static inline uint32_t get_initial_counter_value() { - return timer->instance->tim->ARR / 2; - } - - static int64_t get_delta_clock(uint64_t clock_time, uint64_t last_clock_time) { - int64_t delta_clock = clock_time - last_clock_time; - if (clock_time < last_clock_time) { // overflow handle - delta_clock = clock_time + - CLOCK_MAX_VALUE * NANO_SECOND / timer->get_clock_frequency() - - CLOCK_MAX_VALUE * NANO_SECOND / timer->get_clock_frequency() - - last_clock_time; - } - return delta_clock; - } - }; - - } // namespace ST_LIB + ErrorHandler("Unable to get state from encoder"); + return; + } + if (HAL_TIM_Encoder_Start(timer->instance->hal_tim, TIM_CHANNEL_ALL) != HAL_OK) { + ErrorHandler("Unable to start encoder"); + return; + } + is_on = true; + reset(); + } + + static void turn_off() { + if (!is_on) + return; + + if (HAL_TIM_Encoder_Stop(timer->instance->hal_tim, TIM_CHANNEL_ALL) != HAL_OK) { + ErrorHandler("Unable to stop encoder"); + return; + } + is_on = false; + } + + static inline void reset() { timer->instance->tim->CNT = get_initial_counter_value(); } + + static inline uint32_t get_counter() { return timer->instance->tim->CNT; } + + static inline bool get_direction() { return ((timer->instance->tim->CR1 & 0b10000) >> 4); } + + static inline uint32_t get_initial_counter_value() { return timer->instance->tim->ARR / 2; } + + static int64_t get_delta_clock(uint64_t clock_time, uint64_t last_clock_time) { + int64_t delta_clock = clock_time - last_clock_time; + if (clock_time < last_clock_time) { // overflow handle + delta_clock = clock_time + + CLOCK_MAX_VALUE * NANO_SECOND / timer->get_clock_frequency() - + last_clock_time; + } + return delta_clock; + } +}; + +} // namespace ST_LIB #endif