diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index d8d055afe..45a393abc 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -7,6 +7,6 @@ RUN apt-get update && apt-get -y install git git-lfs python3-colorama cmake g++ git lfs install # Install cubeclt -RUN mkdir /temp && cd /temp && git clone https://github.com/HyperloopUPV-H8/cubeclt.git && \ +RUN mkdir /temp && cd /temp && git clone https://github.com/Hyperloop-UPV/cubeclt.git && \ cd cubeclt && git lfs pull && chmod +x cubeclt_1.16.0_installer.sh && \ echo | LICENSE_ALREADY_ACCEPTED=1 ./cubeclt_1.16.0_installer.sh diff --git a/.vscode/settings.json b/.vscode/settings.json index e7e2e0a5b..46afc1dca 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,8 +7,10 @@ "C_Cpp.clang_format_sortIncludes": true, "C_Cpp.intelliSenseCacheSize": 0, "files.associations": { - "mem.h": "c", - "tinydir.h": "c", - "dirent.h": "c" + "*.embeddedhtml": "html", + "mem.h": "c", + "tinydir.h": "c", + "dirent.h": "c", + "initializer_list": "cpp" } -} \ No newline at end of file +} diff --git a/CMakeLists.txt b/CMakeLists.txt index 57955f586..a730486b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -242,6 +242,7 @@ add_library(${STLIB_LIBRARY} STATIC $<$:${CMAKE_CURRENT_LIST_DIR}/Src/ST-LIB.cpp> $<$>:${CMAKE_CURRENT_LIST_DIR}/Src/HALAL/Services/Time/Scheduler.cpp> + $<$>:${CMAKE_CURRENT_LIST_DIR}/Src/HALAL/Models/TimerDomain/TimerDomain.cpp> $<$>:${CMAKE_CURRENT_LIST_DIR}/Src/MockedDrivers/mocked_ll_tim.cpp> $<$>:${CMAKE_CURRENT_LIST_DIR}/Src/MockedDrivers/mocked_system_stm32h7xx.c> $<$>:${CMAKE_CURRENT_LIST_DIR}/Src/MockedDrivers/stm32h723xx_wrapper.c> diff --git a/Inc/HALAL/HALAL.hpp b/Inc/HALAL/HALAL.hpp index bc896cce3..9bea3a79f 100644 --- a/Inc/HALAL/HALAL.hpp +++ b/Inc/HALAL/HALAL.hpp @@ -20,7 +20,7 @@ #include "HALAL/Services/PWM/PhasedPWM/PhasedPWM.hpp" #include "HALAL/Services/PWM/DualPhasedPWM/DualPhasedPWM.hpp" -#include "HALAL/Services/Time/Time.hpp" +#include "HALAL/Services/Time/TimerWrapper.hpp" #include "HALAL/Services/Time/Scheduler.hpp" #include "HALAL/Services/Time/RTC.hpp" @@ -40,8 +40,6 @@ #include "HALAL/Services/InfoWarning/InfoWarning.hpp" #include "HALAL/Services/Watchdog/Watchdog.hpp" -#include "HALAL/Models/TimerPeripheral/TimerPeripheral.hpp" - #include "HALAL/Models/BoardID/BoardID.hpp" #include "HALAL/Models/Concepts/Concepts.hpp" diff --git a/Inc/HALAL/Models/PinModel/Pin.hpp b/Inc/HALAL/Models/PinModel/Pin.hpp index 579bf05bd..9a2ab4895 100644 --- a/Inc/HALAL/Models/PinModel/Pin.hpp +++ b/Inc/HALAL/Models/PinModel/Pin.hpp @@ -112,7 +112,7 @@ struct hash { using std::string; return ((hash()(k.gpio_pin) ^ - (hash()((uint32_t)(k.port)) << 1)) >> + (hash()((size_t)(k.port)) << 1)) >> 1); } }; diff --git a/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp b/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp new file mode 100644 index 000000000..c0a157983 --- /dev/null +++ b/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp @@ -0,0 +1,873 @@ +/* + * TimerDomain.hpp + * + * Created on: 3 dic. 2025 + * Author: victor + */ + +#pragma once + +#ifndef TESTING_ENV +#include "stm32h7xx_hal.h" +#else +#include "MockedDrivers/stm32h7xx_hal_wrapper.h" +#endif + +#ifdef HAL_TIM_MODULE_ENABLED + +#include "HALAL/Models/GPIO.hpp" +#include "HALAL/Models/Pin.hpp" + +#include +#include +#include + +#include "ErrorHandler/ErrorHandler.hpp" + +// NOTE: only works for static arrays +#define ARRAY_LENGTH(a) (sizeof(a)/sizeof(*a)) + +#define TimerXList \ + X(2, APB1LENR) \ + X(3, APB1LENR) \ + X(4, APB1LENR) \ + X(5, APB1LENR) \ + X(6, APB1LENR) \ + X(7, APB1LENR) \ + X(12, APB1LENR) \ + X(13, APB1LENR) \ + X(14, APB1LENR) \ + \ + X(23, APB1HENR) \ + X(24, APB1HENR) \ + \ + X(1, APB2ENR) \ + X(8, APB2ENR) \ + X(15, APB2ENR) \ + X(16, APB2ENR) \ + X(17, APB2ENR) + +#define X(n, b) extern TIM_HandleTypeDef htim##n; +TimerXList +#undef X + +/* Tim1 & Tim8 are advanced-control timers + * their ARR & prescaler are 16bit + * they have up to 6 independent channels for: + * - input capture (not channel 5 or 6) + * - output capture + * - PWM generation + * - One-pulse mode output + */ + +/* Timers {TIM2, TIM5, TIM23, TIM24} are the only 32-bit counter resolution timers, the rest are 16-bit */ +/* Timers 2, 3, 4, 5, 23, 24 are general-purpose timers + * Timers 12, 13, 14 are also general-purpose timers (but separate in the ref manual) + * Timers 15, 16, 17 are also general purpose timers (but separate in the ref manual) + */ + + +/* Tim6 & Tim7 are basic timers. Features: + - 16-bit ARR upcounter + - 16-bit PSC + - Synchronization circuit to trigger the DAC + - Interrupt/DMA generation on the update event +*/ + +/* advanced timer features: + - 16-bit ARR up/down counter + - 16-bit PSC + - Up to 6 independent channels for: + · Input capture (all channels but 5 and 6) + · Output compare + · PWM generation (Edge and Center aligned mode) + · One-pulse mode output + - Complementary outputs with programmable dead-time + - Synchronization circuit to control the timer with + external signals and to interconnect several timers together. + - Repetition counter to update the timer registers only after + a given number of cycles of the counter. + - 2 break inputs to put the timer’s output signals in a safe user selectable configuration. + - Interrupt/DMA generation on the following events: + · Update: counter overflow/underflow, counter initialization (by software or internal/external trigger) + · Trigger event (counter start, stop, initialization or count by internal/external trigger) + · Input capture + · Output compare + - Supports incremental (quadrature) encoder and Hall-sensor circuitry for positioning purposes + - Trigger input for external clock or cycle-by-cycle current management +*/ +namespace ST_LIB { +extern void compile_error(const char *msg); + +/* The number corresponds with the timer nº */ +enum TimerRequest : uint8_t { + AnyGeneralPurpose = 0, + Any32bit = 0xFF, + + Advanced_1 = 1, + Advanced_8 = 8, + + GeneralPurpose32bit_2 = 2, + GeneralPurpose32bit_5 = 5, + GeneralPurpose32bit_23 = 23, + GeneralPurpose32bit_24 = 24, + + GeneralPurpose_3 = 3, + GeneralPurpose_4 = 4, + + SlaveTimer_12 = 12, + SlaveTimer_13 = 13, + SlaveTimer_14 = 14, + + GeneralPurpose_15 = 15, + GeneralPurpose_16 = 16, + GeneralPurpose_17 = 17, + + Basic_6 = 6, + Basic_7 = 7, +}; + +// Alternate functions for timers +enum class TimerAF { + None, + PWM, + InputCapture, + BreakInput, + BreakInputCompare, +}; + +enum class TimerChannel : uint8_t { + CHANNEL_1 = 1, + CHANNEL_2 = 2, + CHANNEL_3 = 3, + CHANNEL_4 = 4, + CHANNEL_5 = 5, + CHANNEL_6 = 6, +}; + +struct TimerPin { + ST_LIB::TimerAF af; + ST_LIB::GPIODomain::Pin pin; + ST_LIB::TimerChannel channel; +}; + +constexpr std::array create_timer_idxmap() { + std::array result{}; + + // invalid timers that don't exist + result[0] = -1; + result[9] = -1; result[10] = -1; result[11] = -1; + result[18] = -1; result[19] = -1; result[20] = -1; result[21] = -1; result[22] = -1; + + // general-purpose timers + result[2] = 0; result[3] = 1; result[4] = 2; + result[5] = 3; result[23] = 4; result[24] = 5; + + // more general-purpose timers + result[12] = 6; result[13] = 7; result[14] = 8; + + // more general-purpose timers + result[15] = 9; result[16] = 10; result[17] = 11; + + // basic timers + result[6] = 12; result[7] = 13; + + // advanced control timers + result[1] = 14; result[8] = 15; + + return result; +} + +static constexpr std::array timer_idxmap = create_timer_idxmap(); + +struct TimerDomain { + // There are 16 timers + static constexpr std::size_t max_instances = 16; + + 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, + }; + + struct Entry { + std::array name; /* max length = 7 */ + TimerRequest request; + TimerDomain::CountingMode counting_mode; + uint32_t deadtime; + uint32_t polarity; + uint32_t negated_polarity; + uint8_t pin_count; + std::array pins; + }; + + struct Config { + uint16_t timer_idx; + TimerDomain::CountingMode counting_mode; + uint32_t deadtime; + uint32_t polarity; + uint32_t negated_polarity; + }; + + static constexpr TIM_HandleTypeDef *hal_handles[16] = { + // general purpose timers + &htim2, &htim3, &htim4, &htim5, &htim23, &htim24, + &htim12, &htim13, &htim14, + &htim15, &htim16, &htim17, + + // basic timers + &htim6, &htim7, + + // advanced control timers + &htim1, &htim8 + }; + +#ifdef TESTING_ENV + static TIM_TypeDef *cmsis_timers[16]; +#else + static constexpr TIM_TypeDef *cmsis_timers[16] = { + // general purpose timers + TIM2, TIM3, TIM4, TIM5, TIM23, TIM24, + TIM12, TIM13, TIM14, + TIM15, TIM16, TIM17, + + // basic timers + TIM6, TIM7, + + // advanced control timers + TIM1, TIM8 + }; +#endif + + static constexpr IRQn_Type timer_irqn[] = { + // general purpose timers 1 + TIM2_IRQn, TIM3_IRQn, TIM4_IRQn, TIM5_IRQn, TIM23_IRQn, TIM24_IRQn, + // slave timers + TIM8_BRK_TIM12_IRQn, TIM8_UP_TIM13_IRQn, TIM8_TRG_COM_TIM14_IRQn, + // general purpose timers 2 + TIM15_IRQn, TIM16_IRQn, TIM17_IRQn, + + // basic timers + TIM6_DAC_IRQn, /* TIM6 global and DAC1&2 underrun error interrupts */ + TIM7_IRQn, + + TIM1_UP_IRQn, TIM8_UP_TIM13_IRQn + }; + + static inline void rcc_enable_timer(TIM_TypeDef *tim) { +#define X(n, b) \ + else if(tim == TIM##n) { SET_BIT(RCC->b, RCC_##b##_TIM##n##EN); } + + if(false) {} + TimerXList + else { + ErrorHandler("Invalid timer given to rcc_enable_timer"); + } +#undef X + } + + static constexpr Config DoTimer(const Entry request, int reqint, int reqidx) { + Config cfg; + cfg.timer_idx = timer_idxmap[reqint]; + cfg.deadtime = request.deadtime; + cfg.polarity = request.polarity; + cfg.negated_polarity = request.negated_polarity; + + // Do any compile time checks needed for the timers... + if(!(reqint == 1 || reqint == 8 || + reqint == 2 || reqint == 5 || reqint == 23 || reqint == 24 || + reqint == 3 || reqint == 4)) + { + if(request.counting_mode != CountingMode::UP) { + ST_LIB::compile_error("Error: Timers other than {Advanced{TIM1, TIM8}, TIM2, TIM3, TIM4, TIM5, TIM23, TIM24} only support upcounting"); + } + } + + return cfg; + } + + static constexpr std::array EMPTY_TIMER_NAME = {0,0,0,0, 0,0,0,0}; + + struct Timer { + using domain = TimerDomain; + Entry e; + + consteval Timer(TimerRequest request = TimerRequest::AnyGeneralPurpose, + TimerDomain::CountingMode counting_mode = CountingMode::UP, + std::array name = EMPTY_TIMER_NAME, uint32_t deadtime = 0, + uint32_t polarity = TIM_OCPOLARITY_HIGH, uint32_t negated_polarity = TIM_OCPOLARITY_HIGH, + std::initializer_list pinargs = {}) + { + e.name = name; + e.request = request; + e.counting_mode = counting_mode; + e.deadtime = deadtime; + e.polarity = polarity; + e.negated_polarity = negated_polarity; + + e.pin_count = pinargs.size(); + if(pinargs.size() > 4) { + ST_LIB::compile_error("Max 4 pins per timer"); + } + int i = 0; + for(TimerPin p : pinargs) { + e.pins[i] = p; + i++; + } + } + + // anything not initialized will be 0 + consteval Timer(Entry e, std::initializer_list pinargs = {}) { + this->e.name = e.name; + this->e.request = e.request; + this->e.counting_mode = e.counting_mode; + this->e.deadtime = e.deadtime; + this->e.polarity = e.polarity; + this->e.negated_polarity = e.negated_polarity; + if(pinargs.size() == 0) { + this->e.pin_count = e.pin_count; + this->e.pins = e.pins; + } else { + e.pin_count = pinargs.size(); + if(pinargs.size() > 4) { + ST_LIB::compile_error("Max 4 pins per timer"); + } + int i = 0; + for(TimerPin p : pinargs) { + e.pins[i] = p; + i++; + } + } + } + + template + consteval void inscribe(Ctx &ctx) const { + ctx.template add(e, this); + } + }; + + template + static consteval std::array build(std::span requests) { + std::array cfgs{}; + uint16_t cfg_idx = 0; + bool used_timers[25] = {0}; + + if(requests.size() > max_instances) { + ST_LIB::compile_error("too many Timer requests, there are only 16 timers"); + } + + check_pins(requests); + + int remaining_requests[max_instances] = {}; + int count_remaining_requests = (int)requests.size(); + for(int i = 0; i < (int)requests.size(); i++) remaining_requests[i] = i; + + for(int i = 0; i < (int)requests.size(); i++) { + if(requests[i].request != TimerRequest::AnyGeneralPurpose && + (requests[i].request < 1 || requests[i].request > 24 || + (requests[i].request > 17 && requests[i].request < 23))) + { + ST_LIB::compile_error("Invalid TimerRequest value for timer"); + } + } + + // First find any that have requested a specific timer + for(std::size_t i = 0; i < N; i++) { + uint8_t reqint = static_cast(requests[i].request); + if((requests[i].request != TimerRequest::AnyGeneralPurpose) && + (requests[i].request != TimerRequest::Any32bit)) + { + if(used_timers[reqint]) { + ST_LIB::compile_error("Error: Timer already used"); + } + used_timers[reqint] = true; + + Config cfg = DoTimer(requests[i], reqint, i); + cfgs[cfg_idx++] = cfg; + + // unordered remove (remaining requests is not used here so these are ordered) + count_remaining_requests--; + remaining_requests[i] = remaining_requests[count_remaining_requests]; + } + } + + // 32 bit timers, very important for scheduler + uint8_t bits32_timers[] = {2, 5, 23, 24}; + uint8_t remaining_32bit_timers[4] = {0}; + uint8_t count_remaining_32bit_timers = 0; + uint8_t count_32bit_requests = 0; + + for(int i = 0; i < (int)ARRAY_LENGTH(bits32_timers); i++) { + if(!used_timers[bits32_timers[i]]) + remaining_32bit_timers[count_remaining_32bit_timers++] = bits32_timers[i]; + } + + for(int i = 0; i < count_remaining_requests; ) { + const Entry &e = requests[remaining_requests[i]]; + if(e.request == TimerRequest::Any32bit) { + if(count_remaining_32bit_timers <= count_32bit_requests) { + ST_LIB::compile_error("No remaining 32 bit timers, there are only 4. Timers {2, 5, 23, 24}"); + } + + uint8_t reqint = remaining_32bit_timers[count_32bit_requests]; + Config cfg = DoTimer(requests[i], reqint, i); + cfgs[cfg_idx++] = cfg; + + // unordered remove + count_remaining_requests--; + remaining_requests[i] = remaining_requests[count_remaining_requests]; + } else { + i++; + } + } + + // can use any CountingMode (32 bit timers can also but they are higher priority) + uint8_t up_down_updown_timers[] = {3, 4}; + + // 16 bit timers + /* NOTE: timers {TIM12, TIM13, TIM14} are also 16 bit but + * they are used as slave timers to tim8 + */ + uint8_t bits16_timers[] = {15, 16, 17}; + uint8_t remaining_timers[15] = {0}; + uint8_t count_remaining_timers = 0; + + for(int i = 0; i < (int)ARRAY_LENGTH(bits16_timers); i++) { + if(!used_timers[bits16_timers[i]]) + remaining_timers[count_remaining_timers++] = bits16_timers[i]; + } + + for(int i = 0; i < (int)ARRAY_LENGTH(up_down_updown_timers); i++) { + if(!used_timers[up_down_updown_timers[i]]) + remaining_timers[count_remaining_timers++] = up_down_updown_timers[i]; + } + + for(int i = 0; i < (int)ARRAY_LENGTH(bits32_timers); i++) { + if(!used_timers[bits32_timers[i]]) + remaining_timers[count_remaining_timers++] = bits32_timers[i]; + } + + if(count_remaining_requests > count_remaining_timers) { + ST_LIB::compile_error("This should not happen"); + } + + for(int i = 0; i < count_remaining_requests; i++) { + const Entry &e = requests[remaining_requests[i]]; + if(e.request != TimerRequest::AnyGeneralPurpose) { + ST_LIB::compile_error("This only processes TimerRequest::AnyGeneralPurpose"); + } + uint8_t reqint = remaining_timers[i]; + Config cfg = DoTimer(requests[i], reqint, i); + cfgs[cfg_idx++] = cfg; + } + + return cfgs; + } + + // Runtime object + struct Instance { + TIM_TypeDef *tim; + TIM_HandleTypeDef *hal_tim; + uint16_t timer_idx; + }; + + static void (*callbacks[TimerDomain::max_instances])(void*); + static void *callback_data[TimerDomain::max_instances]; + + template struct Init { + static inline std::array instances{}; + + static void init(std::span cfgs) { + for(std::size_t i = 0; i < N; i++) { + const Config &e = cfgs[i]; + + TIM_HandleTypeDef *handle = (TIM_HandleTypeDef*)&hal_handles[e.timer_idx]; + TIM_TypeDef *tim = cmsis_timers[e.timer_idx]; + handle->Instance = tim; + handle->Init.Period = 0; + handle->Init.Prescaler = 0; + handle->Init.CounterMode = TIM_COUNTERMODE_UP; + handle->Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; + handle->Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; + handle->Init.RepetitionCounter = 0; + + /* if(e.counting_mode == CountingMode::UP) { + CLEAR_BIT(tim->CR1, TIM_CR1_DIR); // upcounter + } else */ + if(e.counting_mode == CountingMode::DOWN) { + SET_BIT(tim->CR1, TIM_CR1_DIR); // downcounter + } else if(e.counting_mode == CountingMode::CENTER_ALIGNED_INTERRUPT_DOWN) { + MODIFY_REG(tim->CR1, TIM_CR1_CMS, TIM_CR1_CMS_0); + } else if(e.counting_mode == CountingMode::CENTER_ALIGNED_INTERRUPT_UP) { + MODIFY_REG(tim->CR1, TIM_CR1_CMS, TIM_CR1_CMS_1); + } else if(e.counting_mode == CountingMode::CENTER_ALIGNED_INTERRUPT_BOTH) { + MODIFY_REG(tim->CR1, TIM_CR1_CMS, (TIM_CR1_CMS_0 | TIM_CR1_CMS_1)); + } + + rcc_enable_timer(tim); + + // InputCapture stuff should be dome somewhere else.. + // PWM stuff should be done somewhere else.. + + Instance *inst = &instances[i]; + inst->tim = tim; + inst->hal_tim = handle; + inst->timer_idx = e.timer_idx; + __NOP(); + } + } + }; + + static consteval void check_pins(std::span requests) + { + enum TimerAF_Use { + Channel_1 = 1, + Channel_2 = 2, + Channel_3 = 3, + Channel_4 = 4, + ExternalTriggerFilter, /* ETR */ + BreakInput_1, + BreakInput_2, + BreakInputCompare_1, + BreakInputCompare_2, + }; + + struct TimerPossiblePin { + ST_LIB::GPIODomain::Pin pin; + ST_LIB::GPIODomain::AlternateFunction af; + TimerAF_Use use; + }; + TimerPossiblePin tim1pins[] = { + // 4 capture-compare channels + // complementary output + {ST_LIB::PE6, (ST_LIB::GPIODomain::AlternateFunction)1, BreakInput_2}, + {ST_LIB::PE6, (ST_LIB::GPIODomain::AlternateFunction)12, BreakInputCompare_2}, + + {ST_LIB::PA6, (ST_LIB::GPIODomain::AlternateFunction)1, BreakInput_1}, + {ST_LIB::PA6, (ST_LIB::GPIODomain::AlternateFunction)11, BreakInputCompare_1}, + + {ST_LIB::PA7, (ST_LIB::GPIODomain::AlternateFunction)1, Channel_1}, /* negated */ + {ST_LIB::PB0, (ST_LIB::GPIODomain::AlternateFunction)1, Channel_2}, /* negated */ + {ST_LIB::PB1, (ST_LIB::GPIODomain::AlternateFunction)1, Channel_3}, /* negated */ + {ST_LIB::PE7, (ST_LIB::GPIODomain::AlternateFunction)1, ExternalTriggerFilter}, + {ST_LIB::PE8, (ST_LIB::GPIODomain::AlternateFunction)1, Channel_1}, /* negated */ + {ST_LIB::PE9, (ST_LIB::GPIODomain::AlternateFunction)1, Channel_1}, + {ST_LIB::PE10, (ST_LIB::GPIODomain::AlternateFunction)1, Channel_2}, /* negated */ + {ST_LIB::PE11, (ST_LIB::GPIODomain::AlternateFunction)1, Channel_2}, + {ST_LIB::PE12, (ST_LIB::GPIODomain::AlternateFunction)1, Channel_3}, /* negated */ + {ST_LIB::PE13, (ST_LIB::GPIODomain::AlternateFunction)1, Channel_3}, + {ST_LIB::PE14, (ST_LIB::GPIODomain::AlternateFunction)1, Channel_4}, + + {ST_LIB::PE15, (ST_LIB::GPIODomain::AlternateFunction)1, BreakInput_1}, + {ST_LIB::PE15, (ST_LIB::GPIODomain::AlternateFunction)13, BreakInputCompare_1}, + + {ST_LIB::PB12, (ST_LIB::GPIODomain::AlternateFunction)1, BreakInput_1}, + {ST_LIB::PB12, (ST_LIB::GPIODomain::AlternateFunction)13, BreakInputCompare_1}, + + {ST_LIB::PB13, (ST_LIB::GPIODomain::AlternateFunction)1, Channel_1}, /* negated */ + {ST_LIB::PB14, (ST_LIB::GPIODomain::AlternateFunction)1, Channel_2}, /* negated */ + {ST_LIB::PB15, (ST_LIB::GPIODomain::AlternateFunction)1, Channel_3}, /* negated */ + + {ST_LIB::PG4, (ST_LIB::GPIODomain::AlternateFunction)1, BreakInput_2}, + {ST_LIB::PG4, (ST_LIB::GPIODomain::AlternateFunction)11, BreakInputCompare_2}, + + {ST_LIB::PG5, (ST_LIB::GPIODomain::AlternateFunction)1, ExternalTriggerFilter}, + + {ST_LIB::PA8, (ST_LIB::GPIODomain::AlternateFunction)1, Channel_1}, + {ST_LIB::PA9, (ST_LIB::GPIODomain::AlternateFunction)1, Channel_2}, + {ST_LIB::PA10, (ST_LIB::GPIODomain::AlternateFunction)1, Channel_3}, + {ST_LIB::PA11, (ST_LIB::GPIODomain::AlternateFunction)1, Channel_4}, + + {ST_LIB::PA12, (ST_LIB::GPIODomain::AlternateFunction)1, ExternalTriggerFilter}, + {ST_LIB::PA12, (ST_LIB::GPIODomain::AlternateFunction)12, BreakInput_2}, // comprobar + }; + TimerPossiblePin tim2pins[] = { + // 4 capture-compare channels + {ST_LIB::PA0, (ST_LIB::GPIODomain::AlternateFunction)1, Channel_1}, + {ST_LIB::PA0, (ST_LIB::GPIODomain::AlternateFunction)1, ExternalTriggerFilter}, + {ST_LIB::PA1, (ST_LIB::GPIODomain::AlternateFunction)1, Channel_2}, + {ST_LIB::PA2, (ST_LIB::GPIODomain::AlternateFunction)1, Channel_3}, + {ST_LIB::PA3, (ST_LIB::GPIODomain::AlternateFunction)1, Channel_4}, + + {ST_LIB::PA5, (ST_LIB::GPIODomain::AlternateFunction)1, Channel_1}, + {ST_LIB::PA5, (ST_LIB::GPIODomain::AlternateFunction)1, ExternalTriggerFilter}, + + {ST_LIB::PA15, (ST_LIB::GPIODomain::AlternateFunction)1, Channel_1}, + {ST_LIB::PA15, (ST_LIB::GPIODomain::AlternateFunction)1, ExternalTriggerFilter}, + {ST_LIB::PB3, (ST_LIB::GPIODomain::AlternateFunction)1, Channel_2}, + {ST_LIB::PB10, (ST_LIB::GPIODomain::AlternateFunction)1, Channel_3}, + {ST_LIB::PB11, (ST_LIB::GPIODomain::AlternateFunction)1, Channel_4}, + }; + TimerPossiblePin tim3pins[] = { + // 4 capture-compare channels + {ST_LIB::PA6, (ST_LIB::GPIODomain::AlternateFunction)2, Channel_1}, + {ST_LIB::PA7, (ST_LIB::GPIODomain::AlternateFunction)2, Channel_2}, + {ST_LIB::PB0, (ST_LIB::GPIODomain::AlternateFunction)2, Channel_3}, + {ST_LIB::PB1, (ST_LIB::GPIODomain::AlternateFunction)2, Channel_4}, + + {ST_LIB::PB4, (ST_LIB::GPIODomain::AlternateFunction)2, Channel_1}, + {ST_LIB::PB5, (ST_LIB::GPIODomain::AlternateFunction)2, Channel_2}, + + {ST_LIB::PC6, (ST_LIB::GPIODomain::AlternateFunction)2, Channel_1}, + {ST_LIB::PC7, (ST_LIB::GPIODomain::AlternateFunction)2, Channel_2}, + {ST_LIB::PC8, (ST_LIB::GPIODomain::AlternateFunction)2, Channel_3}, + {ST_LIB::PC9, (ST_LIB::GPIODomain::AlternateFunction)2, Channel_4}, + + {ST_LIB::PD2, (ST_LIB::GPIODomain::AlternateFunction)2, ExternalTriggerFilter}, + }; + TimerPossiblePin tim4pins[] = { + // 4 capture-compare channels + {ST_LIB::PB6, (ST_LIB::GPIODomain::AlternateFunction)2, Channel_1}, + {ST_LIB::PB7, (ST_LIB::GPIODomain::AlternateFunction)2, Channel_2}, + {ST_LIB::PB8, (ST_LIB::GPIODomain::AlternateFunction)2, Channel_3}, + {ST_LIB::PB9, (ST_LIB::GPIODomain::AlternateFunction)2, Channel_4}, + + {ST_LIB::PD12, (ST_LIB::GPIODomain::AlternateFunction)2, Channel_1}, + {ST_LIB::PD13, (ST_LIB::GPIODomain::AlternateFunction)2, Channel_2}, + {ST_LIB::PD14, (ST_LIB::GPIODomain::AlternateFunction)2, Channel_3}, + {ST_LIB::PD15, (ST_LIB::GPIODomain::AlternateFunction)2, Channel_4}, + + {ST_LIB::PE0, (ST_LIB::GPIODomain::AlternateFunction)2, ExternalTriggerFilter}, + }; + TimerPossiblePin tim5pins[] = { + // 4 capture-compare channels + {ST_LIB::PA0, (ST_LIB::GPIODomain::AlternateFunction)2, Channel_1}, + {ST_LIB::PA1, (ST_LIB::GPIODomain::AlternateFunction)2, Channel_2}, + {ST_LIB::PA2, (ST_LIB::GPIODomain::AlternateFunction)2, Channel_3}, + {ST_LIB::PA3, (ST_LIB::GPIODomain::AlternateFunction)2, Channel_4}, + {ST_LIB::PA4, (ST_LIB::GPIODomain::AlternateFunction)2, ExternalTriggerFilter}, + }; + TimerPossiblePin tim8pins[] = { + // 4 capture-compare channels + // complementary output + {ST_LIB::PA0, (ST_LIB::GPIODomain::AlternateFunction)3, ExternalTriggerFilter}, + {ST_LIB::PA5, (ST_LIB::GPIODomain::AlternateFunction)3, Channel_1}, /* negated */ + + {ST_LIB::PA6, (ST_LIB::GPIODomain::AlternateFunction)3, BreakInput_1}, + {ST_LIB::PA6, (ST_LIB::GPIODomain::AlternateFunction)10, BreakInputCompare_1}, + + {ST_LIB::PA7, (ST_LIB::GPIODomain::AlternateFunction)3, Channel_1}, /* negated */ + + {ST_LIB::PA8, (ST_LIB::GPIODomain::AlternateFunction)3, BreakInput_2}, + {ST_LIB::PA8, (ST_LIB::GPIODomain::AlternateFunction)12, BreakInputCompare_2}, + + {ST_LIB::PB0, (ST_LIB::GPIODomain::AlternateFunction)3, Channel_2}, /* negated */ + {ST_LIB::PB1, (ST_LIB::GPIODomain::AlternateFunction)3, Channel_3}, /* negated */ + {ST_LIB::PB14, (ST_LIB::GPIODomain::AlternateFunction)3, Channel_2}, /* negated */ + {ST_LIB::PB15, (ST_LIB::GPIODomain::AlternateFunction)3, Channel_3}, /* negated */ + + {ST_LIB::PC6, (ST_LIB::GPIODomain::AlternateFunction)3, Channel_1}, + {ST_LIB::PC7, (ST_LIB::GPIODomain::AlternateFunction)3, Channel_2}, + {ST_LIB::PC8, (ST_LIB::GPIODomain::AlternateFunction)3, Channel_3}, + {ST_LIB::PC9, (ST_LIB::GPIODomain::AlternateFunction)3, Channel_4}, + + {ST_LIB::PG2, (ST_LIB::GPIODomain::AlternateFunction)3, BreakInput_1}, + {ST_LIB::PG2, (ST_LIB::GPIODomain::AlternateFunction)11, BreakInputCompare_1}, + + {ST_LIB::PG3, (ST_LIB::GPIODomain::AlternateFunction)3, BreakInput_2}, + {ST_LIB::PG3, (ST_LIB::GPIODomain::AlternateFunction)11, BreakInputCompare_2}, + + {ST_LIB::PG8, (ST_LIB::GPIODomain::AlternateFunction)3, ExternalTriggerFilter}, + }; + TimerPossiblePin tim12pins[] = { + // 2 capture-compare channels + {ST_LIB::PB14, (ST_LIB::GPIODomain::AlternateFunction)2, Channel_1}, + {ST_LIB::PB15, (ST_LIB::GPIODomain::AlternateFunction)2, Channel_2}, + }; + TimerPossiblePin tim13pins[] = { + // 1 capture-compare channel + {ST_LIB::PA6, (ST_LIB::GPIODomain::AlternateFunction)9, Channel_1}, + {ST_LIB::PF8, (ST_LIB::GPIODomain::AlternateFunction)9, Channel_1}, + }; + TimerPossiblePin tim14pins[] = { + // 1 capture-compare channel + {ST_LIB::PA7, (ST_LIB::GPIODomain::AlternateFunction)9, Channel_1}, + {ST_LIB::PF9, (ST_LIB::GPIODomain::AlternateFunction)9, Channel_1}, + }; + TimerPossiblePin tim15pins[] = { + // 2 capture-compare channels + {ST_LIB::PA0, (ST_LIB::GPIODomain::AlternateFunction)4, BreakInput_1}, + + {ST_LIB::PA1, (ST_LIB::GPIODomain::AlternateFunction)4, Channel_1}, /* negated */ + {ST_LIB::PA2, (ST_LIB::GPIODomain::AlternateFunction)4, Channel_1}, + {ST_LIB::PA3, (ST_LIB::GPIODomain::AlternateFunction)4, Channel_2}, + + {ST_LIB::PC12, (ST_LIB::GPIODomain::AlternateFunction)2, Channel_1}, + {ST_LIB::PD2, (ST_LIB::GPIODomain::AlternateFunction)4, Channel_2}, + + {ST_LIB::PE3, (ST_LIB::GPIODomain::AlternateFunction)4, BreakInput_1}, + {ST_LIB::PE4, (ST_LIB::GPIODomain::AlternateFunction)4, BreakInput_1}, + + {ST_LIB::PE4, (ST_LIB::GPIODomain::AlternateFunction)4, Channel_1}, + {ST_LIB::PE4, (ST_LIB::GPIODomain::AlternateFunction)4, Channel_2}, + }; + TimerPossiblePin tim16pins[] = { + // 1 capture-compare channel + {ST_LIB::PF6, (ST_LIB::GPIODomain::AlternateFunction)1, Channel_1}, + {ST_LIB::PF8, (ST_LIB::GPIODomain::AlternateFunction)1, Channel_1}, /* negated */ + {ST_LIB::PF10, (ST_LIB::GPIODomain::AlternateFunction)1, BreakInput_1}, + }; + TimerPossiblePin tim17pins[] = { + // 1 capture-compare channel + {ST_LIB::PB5, (ST_LIB::GPIODomain::AlternateFunction)1, BreakInput_1}, + {ST_LIB::PB7, (ST_LIB::GPIODomain::AlternateFunction)1, Channel_1}, /* negated */ + {ST_LIB::PB9, (ST_LIB::GPIODomain::AlternateFunction)1, Channel_1}, + {ST_LIB::PF7, (ST_LIB::GPIODomain::AlternateFunction)1, Channel_1}, + {ST_LIB::PF9, (ST_LIB::GPIODomain::AlternateFunction)1, Channel_1}, /* negated */ + {ST_LIB::PG6, (ST_LIB::GPIODomain::AlternateFunction)1, BreakInput_1}, + }; + TimerPossiblePin tim23pins[] = { + // 4 capture-compare channels + {ST_LIB::PB2, (ST_LIB::GPIODomain::AlternateFunction)13, ExternalTriggerFilter}, + {ST_LIB::PF0, (ST_LIB::GPIODomain::AlternateFunction)13, Channel_1}, + {ST_LIB::PF1, (ST_LIB::GPIODomain::AlternateFunction)13, Channel_2}, + {ST_LIB::PF2, (ST_LIB::GPIODomain::AlternateFunction)13, Channel_3}, + {ST_LIB::PF3, (ST_LIB::GPIODomain::AlternateFunction)13, Channel_4}, + + {ST_LIB::PF6, (ST_LIB::GPIODomain::AlternateFunction)13, Channel_1}, + {ST_LIB::PF7, (ST_LIB::GPIODomain::AlternateFunction)13, Channel_2}, + {ST_LIB::PF8, (ST_LIB::GPIODomain::AlternateFunction)13, Channel_3}, + {ST_LIB::PF9, (ST_LIB::GPIODomain::AlternateFunction)13, Channel_4}, + + {ST_LIB::PG3, (ST_LIB::GPIODomain::AlternateFunction)13, ExternalTriggerFilter}, + {ST_LIB::PG12, (ST_LIB::GPIODomain::AlternateFunction)13, Channel_1}, + {ST_LIB::PG13, (ST_LIB::GPIODomain::AlternateFunction)13, Channel_2}, + {ST_LIB::PG14, (ST_LIB::GPIODomain::AlternateFunction)13, Channel_3}, + }; + TimerPossiblePin tim24pins[] = { + // 4 capture-compare channels + {ST_LIB::PB3, (ST_LIB::GPIODomain::AlternateFunction)14, ExternalTriggerFilter}, + {ST_LIB::PF11, (ST_LIB::GPIODomain::AlternateFunction)14, Channel_1}, + {ST_LIB::PF12, (ST_LIB::GPIODomain::AlternateFunction)14, Channel_2}, + {ST_LIB::PF13, (ST_LIB::GPIODomain::AlternateFunction)14, Channel_3}, + {ST_LIB::PF14, (ST_LIB::GPIODomain::AlternateFunction)14, Channel_4}, + {ST_LIB::PG2, (ST_LIB::GPIODomain::AlternateFunction)14, ExternalTriggerFilter}, + }; + + std::span tim_pins[25]; + tim_pins[TimerRequest::Advanced_1] = std::span(tim1pins); + tim_pins[TimerRequest::GeneralPurpose32bit_2] = std::span(tim2pins); + tim_pins[TimerRequest::GeneralPurpose_3] = std::span(tim3pins); + tim_pins[TimerRequest::GeneralPurpose_4] = std::span(tim4pins); + tim_pins[TimerRequest::GeneralPurpose32bit_5] = std::span(tim5pins); + /* TIM6, TIM7 have no associated pins */ + tim_pins[TimerRequest::Advanced_8] = std::span(tim8pins); + tim_pins[TimerRequest::SlaveTimer_12] = std::span(tim12pins); + tim_pins[TimerRequest::SlaveTimer_13] = std::span(tim13pins); + tim_pins[TimerRequest::SlaveTimer_14] = std::span(tim14pins); + tim_pins[TimerRequest::GeneralPurpose_15] = std::span(tim15pins); + tim_pins[TimerRequest::GeneralPurpose_16] = std::span(tim16pins); + tim_pins[TimerRequest::GeneralPurpose_17] = std::span(tim17pins); + tim_pins[TimerRequest::GeneralPurpose32bit_23] = std::span(tim23pins); + tim_pins[TimerRequest::GeneralPurpose32bit_24] = std::span(tim24pins); + + /* good luck n_n */ + for(std::size_t i = 0; i < requests.size(); i++) { + const Entry &e = requests[i]; + if(e.pin_count == 0) continue; + if(e.request == TimerRequest::AnyGeneralPurpose || + e.request == TimerRequest::Any32bit) + { + ST_LIB::compile_error("Any* timers can't use pins"); + } + if(e.request == TimerRequest::Basic_6 || + e.request == TimerRequest::Basic_7) + { + ST_LIB::compile_error("Basic timers can't use pins"); + } + + std::span curr_pins = tim_pins[(int)e.request]; + for(int j = 0; j < e.pin_count; j++) { + ST_LIB::TimerPin pin = e.pins[j]; + bool found = false; + for(TimerPossiblePin p : curr_pins) { + if(pin.af == ST_LIB::TimerAF::None) { + ST_LIB::compile_error("Error: Timers with pins must have associated TimerAF (alternate functions)"); + } else if(pin.af == ST_LIB::TimerAF::InputCapture || pin.af == ST_LIB::TimerAF::PWM) { + if((static_cast(pin.channel) == static_cast(p.use))) { + found = true; + break; + } + } else if(pin.af == ST_LIB::TimerAF::BreakInput) { + if(p.use == BreakInput_1 || p.use == BreakInput_2) { + found = true; + break; + } + } else if(pin.af == ST_LIB::TimerAF::BreakInputCompare) { + if(p.use == BreakInputCompare_1 || p.use == BreakInputCompare_2) { + found = true; + break; + } + } + } + if(!found) { + ST_LIB::compile_error("Error: Couldn't find any pins with the requested alternate function"); + } + } + } + } +}; +} // namespace ST_LIB + +#endif // HAL_TIM_MODULE_ENABLED + +/* Old init code from TimerPeripheral.cpp, some might be recycled + + TIM_MasterConfigTypeDef sMasterConfig = {0}; + TIM_IC_InitTypeDef sConfigIC = {0}; + TIM_OC_InitTypeDef sConfigOC = {0}; + TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0}; + + TIM_HandleTypeDef *handle = &hal_handles[e.timer_idx]; + handle->Instance = cmsis_timers[e.timer_idx]; + handle->Init.Prescaler = e.prescaler; + handle->Init.CounterMode = TIM_COUNTERMODE_UP; + handle->Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; + // PWM stuff should be done somewhere else.. + handle->Init.Period = e.period; + handle->Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; + handle->Init.RepetitionCounter = 0; + + if(e.type == TIM_TYPE::BASE) { + if(HAL_TIM_Base_Init(handle) != HAL_OK) { + // NOTE: In TimerPeripheral.cpp this is %d for a string ??? + ErrorHandler("Unable to init base timer on %s", e.name); + } + } + + // InputCapture stuff should be dome somewhere else.. + // PWM stuff should be done somewhere else.. + + sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; + sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET; + sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; + if(HAL_TIMEx_MasterConfigSynchronization(handle, &sMasterConfig) != HAL_OK) { + ErrorHandler("Unable to configure master synch on %s", e.name); + } + + // InputCapture stuff should be dome somewhere else.. + // PWM stuff should be done somewhere else.. + + sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE; + sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE; + sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF; + sBreakDeadTimeConfig.DeadTime = e.deadtime; + sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE; + sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH; + sBreakDeadTimeConfig.BreakFilter = 0; + sBreakDeadTimeConfig.Break2State = TIM_BREAK2_DISABLE; + sBreakDeadTimeConfig.Break2Polarity = TIM_BREAK2POLARITY_HIGH; + sBreakDeadTimeConfig.Break2Filter = 0; + sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE; + if(HAL_TIMEx_ConfigBreakDeadTime(handle, &sBreakDeadTimeConfig) != HAL_OK) { + ErrorHandler("Unable to configure break dead time on %s", e.name); + } +*/ \ No newline at end of file diff --git a/Inc/HALAL/Services/PWM/PWM/NewPWM.hpp b/Inc/HALAL/Services/PWM/PWM/NewPWM.hpp new file mode 100644 index 000000000..23618d94b --- /dev/null +++ b/Inc/HALAL/Services/PWM/PWM/NewPWM.hpp @@ -0,0 +1,183 @@ +/* + * NewPWM.hpp + * + * Created on: Dec 30, 2025 + * Author: victor + */ +#pragma once + +#ifdef HAL_TIM_MODULE_ENABLED +#include "HALAL/Models/TimerDomain/TimerDomain.hpp" +#include "HALAL/Models/GPIO.hpp" + +namespace ST_LIB { + +template +struct TimerWrapper; + +enum PWM_MODE : uint8_t { + NORMAL = 0, + PHASED = 1, + CENTER_ALIGNED = 2, +}; + +struct PWMData { + uint32_t channel; + PWM_MODE mode; +}; + +template +class PWM { + static consteval uint8_t get_channel_state_idx(const ST_LIB::TimerChannel ch) { + switch(ch) { + case TimerChannel::CHANNEL_1: + case TimerChannel::CHANNEL_2: + case TimerChannel::CHANNEL_3: + case TimerChannel::CHANNEL_4: + case TimerChannel::CHANNEL_5: + case TimerChannel::CHANNEL_6: + return static_cast(ch) - 1; + + default: ST_LIB::compile_error("unreachable"); + return 0; + } + } + + static consteval uint8_t get_channel_mul4(const ST_LIB::TimerChannel ch) { + switch(ch) { + case TimerChannel::CHANNEL_1: return 0x00; + case TimerChannel::CHANNEL_2: return 0x04; + case TimerChannel::CHANNEL_3: return 0x08; + case TimerChannel::CHANNEL_4: return 0x0C; + case TimerChannel::CHANNEL_5: return 0x10; + case TimerChannel::CHANNEL_6: return 0x14; + + default: ST_LIB::compile_error("unreachable"); + return 0; + } + } + + static constexpr float CLOCK_FREQ_MHZ_WITHOUT_PRESCALER = 275.0f; + static constexpr float clock_period_ns = (1.0f/CLOCK_FREQ_MHZ_WITHOUT_PRESCALER)*1000.0f; + + TimerWrapper &timer; + TimerPin pin; + uint32_t frequency; + float duty_cycle; + bool is_on = false; + bool is_center_aligned; + +public: + PWM(TimerWrapper &tim, TimerPin pin) : timer(tim) { + static_assert(dev.e.request != TimerRequest::Basic_6 && dev.e.request != TimerRequest::Basic_7, "These timers don't support pwm"); + this->is_center_aligned = ((tim.tim->CR1 & TIM_CR1_CMS) != 0); + this->pin = pin; + } + + void turn_on() { + if(is_on) return; + + // if(HAL_TIM_PWM_Start(timer.htim, channel) != HAL_OK) { ErrorHandler("", 0); } + HAL_TIM_ChannelStateTypeDef *state = &timer.htim.ChannelState[get_channel_state_idx(pin.channel)]; + if(*state != HAL_TIM_CHANNEL_STATE_READY) { + ErrorHandler("Channel not ready"); + } + + *state = HAL_TIM_CHANNEL_STATE_BUSY; + // enable CCx + uint32_t tmp = TIM_CCER_CC1E << (get_channel_mul4(pin.channel) & 0x1FU); /* 0x1FU = 31 bits max shift */ + + SET_BIT(timer.tim->CCER, (uint32_t)(TIM_CCx_ENABLE << (get_channel_mul4(pin.channel) & 0x1FU))); + + // if timer supports break + if constexpr ((dev.e.request == (TimerRequest)1) || (dev.e.request == (TimerRequest)8) || (dev.e.request == (TimerRequest)15) || (dev.e.request == (TimerRequest)16) || (dev.e.request == (TimerRequest)17)) + { + // Main Output Enable + SET_BIT(timer.tim->BDTR, TIM_BDTR_MOE); + } + + // if timer can be a slave timer + if constexpr ((dev.e.request == (TimerRequest)1) || (dev.e.request == (TimerRequest)2) || (dev.e.request == (TimerRequest)3) || (dev.e.request == (TimerRequest)4) || (dev.e.request == (TimerRequest)5) || (dev.e.request == (TimerRequest)8) || (dev.e.request == (TimerRequest)12) || (dev.e.request == (TimerRequest)15) || (dev.e.request == (TimerRequest)23) || (dev.e.request == (TimerRequest)24)) + { + uint32_t tmpsmcr = timer.tim->SMCR & TIM_SMCR_SMS; + if(!IS_TIM_SLAVEMODE_TRIGGER_ENABLED(tmpsmcr)) { + timer.counter_enable(); + } + } else { + timer.counter_enable(); + } + + is_on = true; + } + + void turn_off() { + if(!is_on) return; + // if(HAL_TIM_PWM_Stop(timer.htim, channel) != HAL_OK) { ErrorHandler("", 0); } + + SET_BIT(timer.tim->CCER, (uint32_t)(TIM_CCx_DISABLE << (get_channel_mul4(pin.channel) & 0x1FU))); + + // if timer supports break + if constexpr ((dev.e.request == (TimerRequest)1) || (dev.e.request == (TimerRequest)8) || (dev.e.request == (TimerRequest)15) || (dev.e.request == (TimerRequest)16) || (dev.e.request == (TimerRequest)17)) + { + // Disable Main Output Enable (MOE) + CLEAR_BIT(timer.tim->BDTR, TIM_BDTR_MOE); + } + + __HAL_TIM_DISABLE(timer.htim); + + HAL_TIM_ChannelStateTypeDef *state = &timer.htim.ChannelState[get_channel_state_idx(pin.channel)]; + *state = HAL_TIM_CHANNEL_STATE_READY; + + is_on = false; + } + + void set_duty_cycle(float duty_cycle) { + uint16_t raw_duty = (uint16_t)((float)timer->tim->ARR / 200.0f * duty_cycle); + //__HAL_TIM_SET_COMPARE(timer->htim, pin.channel, raw_duty); + *(uint16_t*)((uint8_t*)(timer->tim) + timer.get_CCR_offset(pin.channel)) = raw_duty; + this->duty_cycle = duty_cycle; + } + + void set_frequency(uint32_t frequency) { + if(is_center_aligned) { + frequency = 2*frequency; + } + this->frequency = frequency; + timer.tim->ARR = (HAL_RCC_GetPCLK1Freq() * 2 / (timer.tim->PSC + 1)) / frequency; + set_duty_cycle(duty_cycle); + } + + void set_dead_time(int64_t dead_time_ns) { + TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0}; + + int64_t time = dead_time_ns; + + if(time <= 127 * clock_period_ns) { + sBreakDeadTimeConfig.DeadTime = time / clock_period_ns; + } else if(time <= (2 * clock_period_ns * 127)) { + sBreakDeadTimeConfig.DeadTime = time / (2 * clock_period_ns) - 64 + 128; + } else if(time <= (8 * clock_period_ns * 127)) { + sBreakDeadTimeConfig.DeadTime = time / (8 * clock_period_ns) -32 + 192; + } else if(time <= (16 * clock_period_ns * 127)) { + sBreakDeadTimeConfig.DeadTime = time / (16 * clock_period_ns) -32 + 224; + } else { + ErrorHandler("Invalid dead time configuration"); + } + + sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE; + sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE; + sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF; + sBreakDeadTimeConfig.BreakState = TIM_BREAK_ENABLE; + sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH; + sBreakDeadTimeConfig.BreakFilter = 0; + sBreakDeadTimeConfig.Break2State = TIM_BREAK2_DISABLE; + sBreakDeadTimeConfig.Break2Polarity = TIM_BREAK2POLARITY_HIGH; + sBreakDeadTimeConfig.Break2Filter = 0; + sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE; + HAL_TIMEx_ConfigBreakDeadTime(timer->htim, &sBreakDeadTimeConfig); + SET_BIT(timer->tim->BDTR, TIM_BDTR_MOE); + } +}; +} // namespace ST_LIB + +#endif // HAL_TIM_MODULE_ENABLED diff --git a/Inc/HALAL/Services/Time/Scheduler.hpp b/Inc/HALAL/Services/Time/Scheduler.hpp index 3c15f27b1..0983199ef 100644 --- a/Inc/HALAL/Services/Time/Scheduler.hpp +++ b/Inc/HALAL/Services/Time/Scheduler.hpp @@ -22,8 +22,10 @@ # define SCHEDULER_TIMER_IDX 2 #endif +#ifndef glue #define glue_(a,b) a ## b #define glue(a,b) glue_(a,b) +#endif #define SCHEDULER_TIMER_BASE glue(TIM, glue(SCHEDULER_TIMER_IDX, _BASE)) // Used to reserve a TimerPeripheral diff --git a/Inc/HALAL/Services/Time/TimerWrapper.hpp b/Inc/HALAL/Services/Time/TimerWrapper.hpp new file mode 100644 index 000000000..71abc4f02 --- /dev/null +++ b/Inc/HALAL/Services/Time/TimerWrapper.hpp @@ -0,0 +1,214 @@ +/* + * TimerWrapper.hpp + * + * Created on: 30 dic. 2025 + * Author: victor + */ + +#pragma once + +#ifndef TESTING_ENV +#include "stm32h7xx_hal.h" +#else +#include "MockedDrivers/stm32h7xx_hal_wrapper.h" +#endif + +#ifdef HAL_TIM_MODULE_ENABLED + +#include "HALAL/Models/TimerDomain/TimerDomain.hpp" +#include "HALAL/Services/PWM/PWM/NewPWM.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; + TimerWrapper(TimerDomain::Instance& inst) : instance(inst) {} + + static constexpr bool bits32timer = ( + 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 + ); + + template + inline PWM get_pwm(uint8_t channel) { + 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) { + static_assert(dev.e.pins[0].af == TimerAF::PWM, "Pin must be configured in TimerWrapper as a PWM"); + return PWM(this, pin); + } + + static_assert(dev.e.pin_count > 1, "No pins passed to TimerWrapper are the same as the pins passed to get_pwm() [this method]"); + if constexpr(dev.e.pins[1].pin == pin.pin) { + static_assert(dev.e.pins[1].af == TimerAF::PWM, "Pin must be configured in TimerWrapper as a PWM"); + return PWM(this, pin); + } + + static_assert(dev.e.pin_count > 2, "No pins passed to TimerWrapper are the same as the pins passed to get_pwm() [this method]"); + if constexpr(dev.e.pins[2].pin == pin.pin) { + static_assert(dev.e.pins[2].af == TimerAF::PWM, "Pin must be configured in TimerWrapper as a PWM"); + return PWM(this, pin); + } + + static_assert(dev.e.pin_count == 4, "No pins passed to TimerWrapper are the same as the pins passed to get_pwm() [this method]"); + if constexpr(dev.e.pins[3].pin == pin.pin) { + static_assert(dev.e.pins[3].af == TimerAF::PWM, "Pin must be configured in TimerWrapper as a PWM"); + return PWM(this, pin); + } + } + + 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; + } + + template + inline void configure32bit(void (*callback)(void*), void *callback_data, uint32_t period) + { + static_assert(bits32timer, "Only timers {TIM2, TIM5, TIM23, TIM24} have a 32-bit resolution"); + + if constexpr (psc != 0) { + instance.tim->PSC = psc; + } + instance.tim->ARR = period; + TimerDomain::callbacks[instance.timer_idx] = callback; + TimerDomain::callback_data[instance.timer_idx] = callback_data; + this->counter_enable(); + } + + template + inline void configure16bit(void (*callback)(void*), void *callback_data, uint16_t period) + { + if constexpr (psc != 0) { + instance.tim->PSC = psc; + } + instance.tim->ARR = period; + TimerDomain::callbacks[instance.timer_idx] = callback; + TimerDomain::callback_data[instance.timer_idx] = callback_data; + this->counter_enable(); + } + + static consteval size_t get_CCR_offset(const ST_LIB::TimerChannel ch) { + switch(ch) { + case TimerChannel::CHANNEL_1: return offsetof(TIM_TypeDef, CCR1); + case TimerChannel::CHANNEL_2: return offsetof(TIM_TypeDef, CCR2); + case TimerChannel::CHANNEL_3: return offsetof(TIM_TypeDef, CCR3); + case TimerChannel::CHANNEL_4: return offsetof(TIM_TypeDef, CCR4); + case TimerChannel::CHANNEL_5: return offsetof(TIM_TypeDef, CCR5); + case TimerChannel::CHANNEL_6: return offsetof(TIM_TypeDef, CCR6); + + default: ST_LIB::compile_error("unreachable"); + return 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; + } + + /* WARNING: The counter _must_ be disabled to switch from edge-aligned to center-aligned mode */ + template + inline void set_mode(void) { + constexpr uint8_t reqint = static_cast(dev.e.request); + static_assert(!(reqint == 1 || reqint == 8 || + reqint == 2 || reqint == 5 || reqint == 23 || reqint == 24 || + reqint == 3 || reqint == 4), + "Error: In request reqidx: Timers other than {Advanced{TIM1, TIM8}, TIM2, TIM3, TIM4, TIM5, TIM23, TIM24} only support upcounting"); + + if constexpr (mode == TimerDomain::CountingMode::UP) { + MODIFY_REG(instance.tim->CR1, TIM_CR1_CMS, 0); + CLEAR_BIT(instance.tim->CR1, TIM_CR1_DIR); // upcounter + } else if constexpr (mode == TimerDomain::CountingMode::DOWN) { + MODIFY_REG(instance.tim->CR1, TIM_CR1_CMS, 0); + SET_BIT(instance.tim->CR1, TIM_CR1_DIR); // downcounter + } else if constexpr (mode == TimerDomain::CountingMode::CENTER_ALIGNED_INTERRUPT_DOWN) { + MODIFY_REG(instance.tim->CR1, TIM_CR1_CMS, TIM_CR1_CMS_0); + } else if constexpr (mode == TimerDomain::CountingMode::CENTER_ALIGNED_INTERRUPT_UP) { + MODIFY_REG(instance.tim->CR1, TIM_CR1_CMS, TIM_CR1_CMS_1); + } else if constexpr (mode == TimerDomain::CountingMode::CENTER_ALIGNED_INTERRUPT_BOTH) { + MODIFY_REG(instance.tim->CR1, TIM_CR1_CMS, (TIM_CR1_CMS_0 | TIM_CR1_CMS_1)); + } + } +}; +} // namespace ST_LIB + +#endif // HAL_TIM_MODULE_ENABLED diff --git a/Inc/MockedDrivers/NVIC.hpp b/Inc/MockedDrivers/NVIC.hpp index 4694f9011..bb85c5ee0 100644 --- a/Inc/MockedDrivers/NVIC.hpp +++ b/Inc/MockedDrivers/NVIC.hpp @@ -4,12 +4,34 @@ #include "MockedDrivers/common.hpp" #include "MockedDrivers/compiler_specific.hpp" +#include "MockedDrivers/Register.hpp" + +enum class NVICReg { + Reg_ISER, Reg_ICER, Reg_ISPR, Reg_ICPR, Reg_IABR, Reg_IP, +}; + +template +class NVICRegister : public RegisterBase { +public: + using RegisterBase::RegisterBase; + using RegisterBase::operator=; +}; + + class NVIC_Type { + struct ICER_Register : public RegisterBase { + ICER_Register& operator=(uint32_t val) { + volatile uint32_t *ISER_offset = (volatile uint32_t*)((volatile uint8_t*)&this->reg - offsetof(NVIC_Type, ICER)); + *ISER_offset = *ISER_offset & ~val; + return *this; + } + }; + public: volatile uint32_t ISER[8U]; /*!< Offset: 0x000 (R/W) Interrupt Set Enable Register */ uint32_t RESERVED0[24U]; - volatile uint32_t ICER[8U]; /*!< Offset: 0x080 (R/W) Interrupt Clear Enable Register */ + ICER_Register ICER[8U]; /*!< Offset: 0x080 (R/W) Interrupt Clear Enable Register */ uint32_t RESERVED1[24U]; volatile uint32_t ISPR[8U]; /*!< Offset: 0x100 (R/W) Interrupt Set Pending Register */ uint32_t RESERVED2[24U]; diff --git a/Inc/MockedDrivers/common.hpp b/Inc/MockedDrivers/common.hpp index def8838ec..57a62719d 100644 --- a/Inc/MockedDrivers/common.hpp +++ b/Inc/MockedDrivers/common.hpp @@ -1,17 +1,14 @@ #pragma once -#include "MockedDrivers/compiler_specific.hpp" -#ifdef __cplusplus - #define __I volatile /*!< Defines 'read only' permissions */ -#else - #define __I volatile const /*!< Defines 'read only' permissions */ + +#ifndef glue +#define glue_(a,b) a##b +#define glue(a,b) glue_(a,b) #endif -#define __O volatile /*!< Defines 'write only' permissions */ -#define __IO volatile /*!< Defines 'read / write' permissions */ + +#undef __I +#undef __I /* following defines should be used for structure members */ -#define __IM volatile const /*! Defines 'read only' structure member permissions */ -#define __OM volatile /*! Defines 'write only' structure member permissions */ -#define __IOM volatile /*! Defines 'read / write' structure member permissions */ #define IS_FUNCTIONAL_STATE(STATE) (((STATE) == DISABLE) || ((STATE) == ENABLE)) diff --git a/Inc/MockedDrivers/compiler_specific.hpp b/Inc/MockedDrivers/compiler_specific.hpp index bca6c858c..c841e35d3 100644 --- a/Inc/MockedDrivers/compiler_specific.hpp +++ b/Inc/MockedDrivers/compiler_specific.hpp @@ -1,5 +1,5 @@ #pragma once -#include +#include /* * This file contains implementations or alternate names for diff --git a/Inc/MockedDrivers/mocked_ll_tim.hpp b/Inc/MockedDrivers/mocked_ll_tim.hpp index eb5a8b3bf..e6901553e 100644 --- a/Inc/MockedDrivers/mocked_ll_tim.hpp +++ b/Inc/MockedDrivers/mocked_ll_tim.hpp @@ -1,11 +1,12 @@ #pragma once -#include +#include #include "MockedDrivers/stm32h723xx_wrapper.h" #include "stm32h7xx.h" #include "MockedDrivers/common.hpp" #include "MockedDrivers/NVIC.hpp" #include "MockedDrivers/Register.hpp" -#include +#include + enum class TimReg { Reg_CR1, Reg_CR2, Reg_SMCR, Reg_DIER, Reg_SR, Reg_EGR, Reg_CCMR1, Reg_CCMR2, Reg_CCER, Reg_CNT, Reg_PSC, Reg_ARR, Reg_RCR, @@ -14,8 +15,6 @@ enum class TimReg { }; using enum TimReg; - - template class TimerRegister : public RegisterBase { public: @@ -23,7 +22,6 @@ class TimerRegister : public RegisterBase { using RegisterBase::operator=; }; - static_assert(sizeof(TimerRegister) == sizeof(uint32_t) ); @@ -96,7 +94,7 @@ struct TIM_TypeDef{ // 1. Check if Counter is Enabled if (!(CR1 & CR1_CEN)) { - std::cout<<"TIMER IS NOT ENABLED!!\n"; + printf("TIMER IS NOT ENABLED!!\n"); return false; } // 2. Prescaler Logic @@ -134,16 +132,53 @@ struct RegisterTraits { }; */ -#define DECLARE_TIMER(TIM_IDX) \ - extern TIM_TypeDef* TIM_IDX##_BASE; \ - extern "C"{ \ - void TIM_IDX##_IRQHandler(void); \ - } -#define INSTANTIATE_TIMER(TIM_IDX) \ - TIM_TypeDef __htim##TIM_IDX{TIM_IDX##_IRQHandler,TIM_IDX##_IRQn}; \ - TIM_TypeDef* TIM_IDX##_BASE = &__htim##TIM_IDX; - -#undef TIM1_BASE -DECLARE_TIMER(TIM1) #undef TIM2_BASE -DECLARE_TIMER(TIM2) +#undef TIM3_BASE +#undef TIM4_BASE +#undef TIM5_BASE +#undef TIM6_BASE +#undef TIM7_BASE +#undef TIM12_BASE +#undef TIM13_BASE +#undef TIM14_BASE +#undef TIM23_BASE +#undef TIM24_BASE +#undef TIM1_BASE +#undef TIM8_BASE +#undef TIM15_BASE +#undef TIM16_BASE +#undef TIM17_BASE + +extern TIM_TypeDef* TIM2_BASE; +extern "C"{ void TIM2_IRQHandler(void); } +extern TIM_TypeDef* TIM3_BASE; +extern "C"{ void TIM3_IRQHandler(void); } +extern TIM_TypeDef* TIM4_BASE; +extern "C"{ void TIM4_IRQHandler(void); } +extern TIM_TypeDef* TIM5_BASE; +extern "C"{ void TIM5_IRQHandler(void); } +extern TIM_TypeDef* TIM6_BASE; +extern "C"{ void TIM6_DAC_IRQHandler(void); } +extern TIM_TypeDef* TIM7_BASE; +extern "C"{ void TIM7_IRQHandler(void); } + +extern TIM_TypeDef* TIM12_BASE; +extern "C"{ void TIM8_BRK_TIM12_IRQHandler(void); } +extern TIM_TypeDef* TIM13_BASE; +extern TIM_TypeDef* TIM14_BASE; +extern "C"{ void TIM8_TRG_COM_TIM14_IRQHandler(void); } + +extern TIM_TypeDef* TIM23_BASE; +extern "C"{ void TIM23_IRQHandler(void); } +extern TIM_TypeDef* TIM24_BASE; +extern "C"{ void TIM24_IRQHandler(void); } +extern TIM_TypeDef* TIM1_BASE; +extern "C"{ void TIM1_UP_IRQHandler(void); } +extern TIM_TypeDef* TIM8_BASE; +extern "C"{ void TIM8_UP_TIM13_IRQHandler(void); } +extern TIM_TypeDef* TIM15_BASE; +extern "C"{ void TIM15_IRQHandler(void); } +extern TIM_TypeDef* TIM16_BASE; +extern "C"{ void TIM16_IRQHandler(void); } +extern TIM_TypeDef* TIM17_BASE; +extern "C"{ void TIM17_IRQHandler(void); } diff --git a/Inc/MockedDrivers/stm32h723xx_wrapper.h b/Inc/MockedDrivers/stm32h723xx_wrapper.h index 6d5850340..f4a7d29c8 100644 --- a/Inc/MockedDrivers/stm32h723xx_wrapper.h +++ b/Inc/MockedDrivers/stm32h723xx_wrapper.h @@ -12,11 +12,18 @@ #endif #ifdef __cplusplus - #define __I volatile /*!< Defines 'read only' permissions */ +# define STM__I volatile /*!< Defines 'read only' permissions */ #else - #define __I volatile const /*!< Defines 'read only' permissions */ +# define STM__I volatile const /*!< Defines 'read only' permissions */ #endif -#define __IO volatile + +#define __I STM__I +#define __O volatile /*!< Defines 'write only' permissions */ +#define __IO volatile /*!< Defines 'read / write' permissions */ + +#define __IM volatile const /*! Defines 'read only' structure member permissions */ +#define __OM volatile /*! Defines 'write only' structure member permissions */ +#define __IOM volatile /*! Defines 'read / write' structure member permissions */ #define __RBIT __RBIT__CMSIS #define TIM_TypeDef TIM_TypeDef__CMSIS @@ -31,4 +38,6 @@ #undef RCC extern RCC_TypeDef *RCC; +#include "MockedDrivers/compiler_specific.hpp" + #endif // STM32H723xx_WRAPPER_H diff --git a/Inc/MockedDrivers/stm32h7xx_hal_wrapper.h b/Inc/MockedDrivers/stm32h7xx_hal_wrapper.h new file mode 100644 index 000000000..a7b546ed8 --- /dev/null +++ b/Inc/MockedDrivers/stm32h7xx_hal_wrapper.h @@ -0,0 +1,14 @@ +#ifndef STM32H7xx_HAL_WRAPPER_H +#define STM32H7xx_HAL_WRAPPER_H + +#include "MockedDrivers/stm32h723xx_wrapper.h" + +#include "MockedDrivers/stm32h7xx_ll_adc_wrapper.h" +#include "MockedDrivers/stm32h7xx_ll_tim_wrapper.h" + +#include "stm32h7xx_hal.h" + + +#include "MockedDrivers/common.hpp" + +#endif // STM32H7xx_HAL_WRAPPER_H \ No newline at end of file diff --git a/Inc/MockedDrivers/stm32h7xx_ll_adc_wrapper.h b/Inc/MockedDrivers/stm32h7xx_ll_adc_wrapper.h new file mode 100644 index 000000000..038455a4a --- /dev/null +++ b/Inc/MockedDrivers/stm32h7xx_ll_adc_wrapper.h @@ -0,0 +1,15 @@ +#ifndef STM32H7xx_LL_ADC_WRAPPER_H +#define STM32H7xx_LL_ADC_WRAPPER_H + +#include + +#include "MockedDrivers/stm32h723xx_wrapper.h" +//#include "MockedDrivers/mocked_ll_adc.hpp" + +#define uint32_t size_t +#include "stm32h7xx_ll_adc.h" +#undef uint32_t + +#include "MockedDrivers/common.hpp" + +#endif // STM32H7xx_LL_ADC_WRAPPER_H diff --git a/Inc/MockedDrivers/stm32h7xx_ll_tim_wrapper.h b/Inc/MockedDrivers/stm32h7xx_ll_tim_wrapper.h index 904f5bb5f..31e724d12 100644 --- a/Inc/MockedDrivers/stm32h7xx_ll_tim_wrapper.h +++ b/Inc/MockedDrivers/stm32h7xx_ll_tim_wrapper.h @@ -5,8 +5,6 @@ #include "MockedDrivers/stm32h723xx_wrapper.h" -#include "stm32h7xx.h" - #include "MockedDrivers/mocked_ll_tim.hpp" #define uint32_t size_t diff --git a/Inc/ST-LIB.hpp b/Inc/ST-LIB.hpp index 221035b08..61de272cd 100644 --- a/Inc/ST-LIB.hpp +++ b/Inc/ST-LIB.hpp @@ -83,7 +83,8 @@ template struct BuildCtx { } }; -using DomainsCtx = BuildCtx; template struct Board { @@ -101,12 +102,14 @@ template struct Board { static consteval auto build() { constexpr std::size_t gpioN = domain_size(); + constexpr std::size_t timN = domain_size(); constexpr std::size_t doutN = domain_size(); constexpr std::size_t dinN = domain_size(); // ... struct ConfigBundle { std::array gpio_cfgs; + std::array tim_cfgs; std::array dout_cfgs; std::array din_cfgs; // ... @@ -115,6 +118,8 @@ template struct Board { return ConfigBundle{ .gpio_cfgs = GPIODomain::template build(ctx.template span()), + .tim_cfgs = + TimerDomain::template build(ctx.template span()), .dout_cfgs = DigitalOutputDomain::template build( ctx.template span()), .din_cfgs = DigitalInputDomain::template build( @@ -127,11 +132,13 @@ template struct Board { static void init() { constexpr std::size_t gpioN = domain_size(); + constexpr std::size_t timN = domain_size(); constexpr std::size_t doutN = domain_size(); constexpr std::size_t dinN = domain_size(); // ... GPIODomain::Init::init(cfg.gpio_cfgs); + TimerDomain::Init::init(cfg.tim_cfgs); DigitalOutputDomain::Init::init(cfg.dout_cfgs, GPIODomain::Init::instances); DigitalInputDomain::Init::init(cfg.din_cfgs, diff --git a/Inc/ST-LIB_LOW/ErrorHandler/ErrorHandler.hpp b/Inc/ST-LIB_LOW/ErrorHandler/ErrorHandler.hpp index 2db36f0fd..dfbd14b95 100644 --- a/Inc/ST-LIB_LOW/ErrorHandler/ErrorHandler.hpp +++ b/Inc/ST-LIB_LOW/ErrorHandler/ErrorHandler.hpp @@ -8,6 +8,8 @@ #pragma once #include "C++Utilities/CppUtils.hpp" + +#ifndef TESTING_ENV #ifndef SIM_ON #include "HALAL/Services/Time/Time.hpp" #include "HALAL/Services/Communication/UART/UART.hpp" @@ -15,7 +17,8 @@ #include "HALALMock/Services/Time/Time.hpp" #include "HALALMock/Services/Communication/UART/UART.hpp" #include "HALALMock/Services/Logger/Logger.hpp" -#endif +#endif // SIM_ON +#endif // !defined(TESTING_ENV) class ErrorHandlerModel { private: diff --git a/Src/HALAL/Models/TimerDomain/TimerDomain.cpp b/Src/HALAL/Models/TimerDomain/TimerDomain.cpp new file mode 100644 index 000000000..90a3709ba --- /dev/null +++ b/Src/HALAL/Models/TimerDomain/TimerDomain.cpp @@ -0,0 +1,87 @@ +#include "HALAL/Models/TimerDomain/TimerDomain.hpp" + +using namespace ST_LIB; + +#define X(n, b) TIM_HandleTypeDef htim##n; +TimerXList +#undef X + +void (*TimerDomain::callbacks[TimerDomain::max_instances])(void*) = {nullptr}; +void *TimerDomain::callback_data[TimerDomain::max_instances] = {nullptr}; + +extern "C" void TIM1_UP_IRQHandler(void) { + CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[1]]->SR, TIM_SR_UIF); + TimerDomain::callbacks[timer_idxmap[1]](TimerDomain::callback_data[timer_idxmap[1]]); +} + +extern "C" void TIM2_IRQHandler(void) { + CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[2]]->SR, TIM_SR_UIF); + TimerDomain::callbacks[timer_idxmap[2]](TimerDomain::callback_data[timer_idxmap[2]]); +} + +extern "C" void TIM3_IRQHandler(void) { + CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[3]]->SR, TIM_SR_UIF); + TimerDomain::callbacks[timer_idxmap[3]](TimerDomain::callback_data[timer_idxmap[3]]); +} + +extern "C" void TIM4_IRQHandler(void) { + CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[4]]->SR, TIM_SR_UIF); + TimerDomain::callbacks[timer_idxmap[4]](TimerDomain::callback_data[timer_idxmap[4]]); +} + +extern "C" void TIM5_IRQHandler(void) { + CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[5]]->SR, TIM_SR_UIF); + TimerDomain::callbacks[timer_idxmap[5]](TimerDomain::callback_data[timer_idxmap[5]]); +} + +extern "C" void TIM6_DAC_IRQHandler(void) { + CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[6]]->SR, TIM_SR_UIF); + TimerDomain::callbacks[timer_idxmap[6]](TimerDomain::callback_data[timer_idxmap[6]]); +} + +extern "C" void TIM7_IRQHandler(void) { + CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[7]]->SR, TIM_SR_UIF); + TimerDomain::callbacks[timer_idxmap[7]](TimerDomain::callback_data[timer_idxmap[7]]); +} + +/* NOTE: If it is needed, make changes so there's a specific callback for + * tim8 which takes an int or something to know from which interrupt + * it was called / make 3 callbacks +*/ +extern "C" void TIM8_BRK_TIM12_IRQHandler(void) { + CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[8]]->SR, TIM_SR_UIF); + TimerDomain::callbacks[timer_idxmap[8]](TimerDomain::callback_data[timer_idxmap[8]]); +} +extern "C" void TIM8_UP_TIM13_IRQHandler(void) { + CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[8]]->SR, TIM_SR_UIF); + TimerDomain::callbacks[timer_idxmap[8]](TimerDomain::callback_data[timer_idxmap[8]]); +} +extern "C" void TIM8_TRG_COM_TIM14_IRQHandler(void) { + CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[8]]->SR, TIM_SR_UIF); + TimerDomain::callbacks[timer_idxmap[8]](TimerDomain::callback_data[timer_idxmap[8]]); +} + +extern "C" void TIM15_IRQHandler(void) { + CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[15]]->SR, TIM_SR_UIF); + TimerDomain::callbacks[timer_idxmap[15]](TimerDomain::callback_data[timer_idxmap[15]]); +} + +extern "C" void TIM16_IRQHandler(void) { + CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[16]]->SR, TIM_SR_UIF); + TimerDomain::callbacks[timer_idxmap[16]](TimerDomain::callback_data[timer_idxmap[16]]); +} + +extern "C" void TIM17_IRQHandler(void) { + CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[17]]->SR, TIM_SR_UIF); + TimerDomain::callbacks[timer_idxmap[17]](TimerDomain::callback_data[timer_idxmap[17]]); +} + +extern "C" void TIM23_IRQHandler(void) { + CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[23]]->SR, TIM_SR_UIF); + TimerDomain::callbacks[timer_idxmap[23]](TimerDomain::callback_data[timer_idxmap[23]]); +} + +extern "C" void TIM24_IRQHandler(void) { + CLEAR_BIT(TimerDomain::cmsis_timers[timer_idxmap[24]]->SR, TIM_SR_UIF); + TimerDomain::callbacks[timer_idxmap[24]](TimerDomain::callback_data[timer_idxmap[24]]); +} diff --git a/Src/HALAL/Services/Time/Scheduler.cpp b/Src/HALAL/Services/Time/Scheduler.cpp index c4909b3d5..81aa1f781 100644 --- a/Src/HALAL/Services/Time/Scheduler.cpp +++ b/Src/HALAL/Services/Time/Scheduler.cpp @@ -5,27 +5,21 @@ * Author: Victor (coauthor Stephan) */ #include "HALAL/Services/Time/Scheduler.hpp" +#include "HALAL/Models/TimerDomain/TimerDomain.hpp" +#include "ErrorHandler/ErrorHandler.hpp" -#ifndef TESTING_ENV - // This is needed to register a TimerPeripheral - #include "HALAL/Models/TimerPeripheral/TimerPeripheral.hpp" -#endif -#include -#include - +#include /* NOTE(vic): Pido perdón a Boris pero es la mejor manera que se me ha ocurrido hacer esto */ #define SCHEDULER_RCC_TIMER_ENABLE \ glue(glue(RCC_APB1LENR_TIM, SCHEDULER_TIMER_IDX), EN) #define SCHEDULER_GLOBAL_TIMER_IRQn \ glue(TIM, glue(SCHEDULER_TIMER_IDX, _IRQn)) -#define SCHEDULER_GLOBAL_TIMER_CALLBACK() \ - extern "C" void glue(TIM, glue(SCHEDULER_TIMER_IDX, _IRQHandler))(void) #define Scheduler_global_timer ((TIM_TypeDef*)SCHEDULER_TIMER_BASE) namespace { constexpr uint64_t kMaxIntervalUs = - static_cast(std::numeric_limits::max())/2 + 1ULL; + static_cast(UINT32_MAX)/2 + 1ULL; } std::array Scheduler::tasks_{}; @@ -70,6 +64,12 @@ inline void Scheduler::global_timer_enable() { } // ---------------------------- +void scheduler_global_timer_callback(void *raw) { + (void)raw; + Scheduler::on_timer_update(); +} +// ---------------------------- + void Scheduler::start() { static_assert((Scheduler::FREQUENCY % 1'000'000) == 0u, "frequenct must be a multiple of 1MHz"); @@ -114,20 +114,15 @@ void Scheduler::start() { // TODO: Fault when any of the next 2 static asserts happen (needs to be runtime bcos of SystemCoreClock) if(prescaler == 0 || prescaler > 0xFFFF) { - // error here + ErrorHandler("Invalid prescaler value: %u", prescaler); } //static_assert(prescaler < 0xFFFF, "Prescaler is 16 bit, so it must be in that range"); //static_assert(prescaler != 0, "Prescaler must be in the range [1, 65535]"); #ifndef TESTING_ENV - - // Register a TimerPeripheral so it's not used anywhere else - // hopefully we can move to something better than TimerPeripheral - TimerPeripheral::InitData init_data(TimerPeripheral::BASE); - TimerPeripheral perif_reserve(&SCHEDULER_HAL_TIM, std::move(init_data), (std::string)"timer2"); - RCC->APB1LENR |= SCHEDULER_RCC_TIMER_ENABLE; #endif + Scheduler_global_timer->PSC = (uint16_t)prescaler; Scheduler_global_timer->ARR = 0; Scheduler_global_timer->DIER |= LL_TIM_DIER_UIE; @@ -136,6 +131,9 @@ void Scheduler::start() { // LL_TIM_DisableExternalClock(Scheduler_global_timer); // |-> does this: Scheduler_global_timer->SMCR &= ~TIM_SMCR_ECE; /* Disable external clock */ + // Temporary solution for TimerDomain + ST_LIB::TimerDomain::callbacks[ST_LIB::timer_idxmap[SCHEDULER_TIMER_IDX]] = scheduler_global_timer_callback; + Scheduler_global_timer->CNT = 0; /* Clear counter value */ NVIC_EnableIRQ(SCHEDULER_GLOBAL_TIMER_IRQn); @@ -144,11 +142,6 @@ void Scheduler::start() { Scheduler::schedule_next_interval(); } -SCHEDULER_GLOBAL_TIMER_CALLBACK() { - CLEAR_BIT(Scheduler_global_timer->SR, TIM_SR_UIF); - Scheduler::on_timer_update(); -} - void Scheduler::update() { while(ready_bitmap_ != 0u) { uint32_t bit_index = static_cast(__builtin_ctz(ready_bitmap_)); diff --git a/Src/MockedDrivers/mocked_ll_tim.cpp b/Src/MockedDrivers/mocked_ll_tim.cpp index 2d1dd0edb..143237473 100644 --- a/Src/MockedDrivers/mocked_ll_tim.cpp +++ b/Src/MockedDrivers/mocked_ll_tim.cpp @@ -1,8 +1,39 @@ #include "MockedDrivers/mocked_ll_tim.hpp" -#include +#include -INSTANTIATE_TIMER(TIM2) +TIM_TypeDef __htim2{TIM2_IRQHandler, TIM2_IRQn}; +TIM_TypeDef *TIM2_BASE = &__htim2; +TIM_TypeDef __htim3{TIM3_IRQHandler, TIM3_IRQn}; +TIM_TypeDef *TIM3_BASE = &__htim3; +TIM_TypeDef __htim4{TIM4_IRQHandler, TIM4_IRQn}; +TIM_TypeDef *TIM4_BASE = &__htim4; +TIM_TypeDef __htim5{TIM5_IRQHandler, TIM5_IRQn}; +TIM_TypeDef *TIM5_BASE = &__htim5; +TIM_TypeDef __htim6{TIM6_DAC_IRQHandler, TIM6_DAC_IRQn}; +TIM_TypeDef *TIM6_BASE = &__htim6; +TIM_TypeDef __htim7{TIM7_IRQHandler, TIM7_IRQn}; +TIM_TypeDef *TIM7_BASE = &__htim7; +TIM_TypeDef __htim12{TIM8_BRK_TIM12_IRQHandler, TIM8_BRK_TIM12_IRQn}; +TIM_TypeDef *TIM12_BASE = &__htim12; +TIM_TypeDef __htim13{TIM8_UP_TIM13_IRQHandler, TIM8_UP_TIM13_IRQn}; +TIM_TypeDef *TIM13_BASE = &__htim13; +TIM_TypeDef __htim14{TIM8_TRG_COM_TIM14_IRQHandler, TIM8_TRG_COM_TIM14_IRQn}; +TIM_TypeDef *TIM14_BASE = &__htim14; +TIM_TypeDef __htim23{TIM23_IRQHandler, TIM23_IRQn}; +TIM_TypeDef *TIM23_BASE = &__htim23; +TIM_TypeDef __htim24{TIM24_IRQHandler, TIM24_IRQn}; +TIM_TypeDef *TIM24_BASE = &__htim24; +TIM_TypeDef __htim1{TIM1_UP_IRQHandler, TIM1_UP_IRQn}; +TIM_TypeDef *TIM1_BASE = &__htim1; +TIM_TypeDef __htim8{TIM8_UP_TIM13_IRQHandler, TIM8_UP_TIM13_IRQn}; +TIM_TypeDef *TIM8_BASE = &__htim8; +TIM_TypeDef __htim15{TIM15_IRQHandler, TIM15_IRQn}; +TIM_TypeDef *TIM15_BASE = &__htim15; +TIM_TypeDef __htim16{TIM16_IRQHandler, TIM16_IRQn}; +TIM_TypeDef *TIM16_BASE = &__htim16; +TIM_TypeDef __htim17{TIM17_IRQHandler, TIM17_IRQn}; +TIM_TypeDef *TIM17_BASE = &__htim17; void TIM_TypeDef::generate_update() { active_PSC = PSC; @@ -27,7 +58,7 @@ void simulate_ticks(TIM_TypeDef* tim){ // Check for Overflow if (tim->CNT > current_limit) { - std::cout<<"timer overflow\n"; + printf("timer overflow\n"); tim->CNT = 0; // Rollover main counter // 4. Repetition Counter & Update Event Logic diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index c06522432..628ec53cc 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -4,6 +4,7 @@ else() set(STLIB_TEST_EXECUTABLE st-lib-test) endif() +project( ${STLIB_TEST_EXECUTABLE}) include(FetchContent) FetchContent_Declare( googletest @@ -15,6 +16,8 @@ message(STATUS "Generating test executable for ST-LIB") add_executable(${STLIB_TEST_EXECUTABLE} ${CMAKE_CURRENT_LIST_DIR}/Time/scheduler_test.cpp + ${CMAKE_CURRENT_LIST_DIR}/Time/timer_wrapper_test.cpp + ${CMAKE_CURRENT_LIST_DIR}/Time/common_tests.cpp ) set_target_properties(${STLIB_TEST_EXECUTABLE} PROPERTIES @@ -34,6 +37,10 @@ if(MINGW OR CYGWIN) target_link_options(${STLIB_TEST_EXECUTABLE} PRIVATE -static) endif() +if(MINGW OR CYGWIN) + target_link_options(${STLIB_TEST_EXECUTABLE} PRIVATE -static) +endif() + target_compile_definitions(${STLIB_TEST_EXECUTABLE} PRIVATE TESTING_ENV ) diff --git a/Tests/Time/common_tests.cpp b/Tests/Time/common_tests.cpp new file mode 100644 index 000000000..d384f7d11 --- /dev/null +++ b/Tests/Time/common_tests.cpp @@ -0,0 +1,18 @@ +#include +#include "ErrorHandler/ErrorHandler.hpp" + +std::string ErrorHandlerModel::line; +std::string ErrorHandlerModel::func; +std::string ErrorHandlerModel::file; + +void ErrorHandlerModel::SetMetaData(int line, const char * func, const char * file){ + ErrorHandlerModel::line = to_string(line); + ErrorHandlerModel::func = string(func); + ErrorHandlerModel::file = string(file); +} + +void ErrorHandlerModel::ErrorHandlerTrigger(string format, ... ){ + EXPECT_EQ(1, 0); +} + +void ErrorHandlerModel::ErrorHandlerUpdate(){} diff --git a/Tests/Time/timer_wrapper_test.cpp b/Tests/Time/timer_wrapper_test.cpp new file mode 100644 index 000000000..681f7a904 --- /dev/null +++ b/Tests/Time/timer_wrapper_test.cpp @@ -0,0 +1,115 @@ +#include +//#include +//#include + +#include "HALAL/Models/TimerDomain/TimerDomain.hpp" +#include "HALAL/Services/Time/TimerWrapper.hpp" + +TIM_TypeDef *ST_LIB::TimerDomain::cmsis_timers[16] = { + [0] = TIM2_BASE, + [1] = TIM3_BASE, + [2] = TIM4_BASE, + [3] = TIM5_BASE, + [4] = TIM23_BASE, + [5] = TIM24_BASE, + [6] = TIM12_BASE, + [7] = TIM13_BASE, + [8] = TIM14_BASE, + [9] = TIM15_BASE, + [10] = TIM16_BASE, + [11] = TIM17_BASE, + [12] = TIM6_BASE, + [13] = TIM7_BASE, + [14] = TIM1_BASE, + [15] = TIM8_BASE, +}; + +class TimerWrapperTests : public ::testing::Test { +protected: + void SetUp() override { + // Reset Timer + TIM1_BASE->CNT = 0; + TIM1_BASE->ARR = 0; + TIM1_BASE->SR = 0; + TIM1_BASE->CR1 = 0; + TIM1_BASE->DIER = 0; + + } +}; + +constexpr ST_LIB::TimerDomain::Timer tim1_decl{{ + .request = ST_LIB::TimerRequest::Advanced_1, +}}; +ST_LIB::TimerDomain::Instance tim1_inst { + .tim = TIM1_BASE, + .hal_tim = 0, + .timer_idx = 14, +}; + +TEST_F(TimerWrapperTests, Counter_EnabledDisabled) { + ST_LIB::TimerWrapper tim1(tim1_inst); + + tim1.counter_enable(); + EXPECT_EQ(TIM1_BASE->CR1 & TIM_CR1_CEN, TIM_CR1_CEN); + tim1.counter_disable(); + EXPECT_EQ(TIM1_BASE->CR1 & TIM_CR1_CEN, 0); +} + +TEST_F(TimerWrapperTests, UpdateInterrupt_EnabledDisabled) { + ST_LIB::TimerWrapper tim1(tim1_inst); + + tim1.enable_update_interrupt(); + EXPECT_EQ(TIM1_BASE->DIER & TIM_DIER_UIE, TIM_DIER_UIE); + tim1.disable_update_interrupt(); + EXPECT_EQ(TIM1_BASE->DIER & TIM_DIER_UIE, 0); +} + +TEST_F(TimerWrapperTests, NVIC_EnabledDisabled) { + ST_LIB::TimerWrapper tim1(tim1_inst); + + tim1.enable_nvic(); + EXPECT_EQ(NVIC_GetEnableIRQ(TIM1_UP_IRQn), 1); + tim1.disable_nvic(); + EXPECT_EQ(NVIC_GetEnableIRQ(TIM1_UP_IRQn), 0); +} + +TEST_F(TimerWrapperTests, UpdateEvent_EnabledDisabled) { + ST_LIB::TimerWrapper tim1(tim1_inst); + + tim1.enable_update_event(); + EXPECT_EQ(TIM1_BASE->CR1 & TIM_CR1_UDIS, 0); + tim1.disable_update_event(); + EXPECT_EQ(TIM1_BASE->CR1 & TIM_CR1_UDIS, TIM_CR1_UDIS); +} + +TEST_F(TimerWrapperTests, BreakInterrupt_EnabledDisabled) { + ST_LIB::TimerWrapper tim1(tim1_inst); + + tim1.break_interrupt_enable(); + EXPECT_EQ(TIM1_BASE->DIER & TIM_DIER_BIE, TIM_DIER_BIE); + tim1.break_interrupt_disable(); + EXPECT_EQ(TIM1_BASE->DIER & TIM_DIER_BIE, 0); +} + +TEST_F(TimerWrapperTests, OnePulseMode_EnabledDisabled) { + ST_LIB::TimerWrapper tim1(tim1_inst); + + tim1.set_one_pulse_mode(); + EXPECT_EQ(TIM1_BASE->CR1 & TIM_CR1_OPM, TIM_CR1_OPM); + tim1.set_multi_interrupt_mode(); + EXPECT_EQ(TIM1_BASE->CR1 & TIM_CR1_OPM, 0); +} + +void callback(void *raw) {} + +TEST_F(TimerWrapperTests, ConfigureTimer) { + ST_LIB::TimerWrapper tim1(tim1_inst); + +#define PRESCALER_VAL 200 +#define PERIOD 1000 + tim1.configure16bit(callback, 0, PERIOD); + EXPECT_EQ(TIM1_BASE->PSC, PRESCALER_VAL); /* set prescaler */ + EXPECT_EQ(TIM1_BASE->ARR, PERIOD); /* set period */ + EXPECT_EQ(TIM1_BASE->CR1 & TIM_CR1_CEN, TIM_CR1_CEN); /* set counter enable */ +} +