diff --git a/.changesets/dfsdm-module-minor.md b/.changesets/dfsdm-module-minor.md new file mode 100644 index 000000000..d05f3156a --- /dev/null +++ b/.changesets/dfsdm-module-minor.md @@ -0,0 +1,2 @@ +release: minor +summary: Added module dfsdm tested diff --git a/CMakeLists.txt b/CMakeLists.txt index 6cd71cc43..409c22148 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -298,6 +298,7 @@ set(HALAL_CPP_NO_ETH ${CMAKE_CURRENT_LIST_DIR}/Src/HALAL/Services/Time/RTC.cpp ${CMAKE_CURRENT_LIST_DIR}/Src/HALAL/Services/Time/Scheduler.cpp ${CMAKE_CURRENT_LIST_DIR}/Src/HALAL/Services/Watchdog/Watchdog.cpp + ${CMAKE_CURRENT_LIST_DIR}/Src/HALAL/Services/DFSDM/DFSDM.cpp ) diff --git a/Inc/HALAL/HALAL.hpp b/Inc/HALAL/HALAL.hpp index 5ae67976d..e8cec2fdb 100644 --- a/Inc/HALAL/HALAL.hpp +++ b/Inc/HALAL/HALAL.hpp @@ -50,6 +50,8 @@ #include "HALAL/Models/Packets/MdmaPacket.hpp" #include "HALAL/Benchmarking_toolkit/DataWatchpointTrace/DataWatchpointTrace.hpp" + +#include "HALAL/Services/DFSDM/DFSDM.hpp" #include "HALAL/HardFault/HardfaultTrace.h" #include "HALAL/Services/Communication/Ethernet/NewEthernet.hpp" #ifdef STLIB_ETH diff --git a/Inc/HALAL/Models/DMA/DMA2.hpp b/Inc/HALAL/Models/DMA/DMA2.hpp index 4209e34e6..2afc5b89d 100644 --- a/Inc/HALAL/Models/DMA/DMA2.hpp +++ b/Inc/HALAL/Models/DMA/DMA2.hpp @@ -35,7 +35,11 @@ struct DMADomain { spi4, spi5, spi6, - fmac + fmac, + dfsdm_filter0, + dfsdm_filter1, + dfsdm_filter2, + dfsdm_filter3 }; enum class Stream : uint8_t { @@ -98,7 +102,7 @@ struct DMADomain { } return nullptr; } - + static inline consteval bool shares_dma(Peripheral p) { return is_dfsdm(p); } struct Entry { Peripheral instance; Stream stream; @@ -217,6 +221,16 @@ struct DMADomain { static consteval bool is_none(Peripheral instance) { return instance == Peripheral::none; } + static consteval bool is_dfsdm(Peripheral instance) { + return is_one_of( + instance, + Peripheral::dfsdm_filter0, + Peripheral::dfsdm_filter1, + Peripheral::dfsdm_filter2, + Peripheral::dfsdm_filter3 + ); + } + static consteval uint32_t get_Request(Peripheral instance, uint8_t i) { if (instance == Peripheral::none) return DMA_REQUEST_MEM2MEM; @@ -272,8 +286,20 @@ struct DMADomain { return DMA_REQUEST_FMAC_WRITE; if (instance == Peripheral::fmac && i == 2) return DMA_REQUEST_FMAC_READ; - + if (instance == Peripheral::dfsdm_filter0) { + return DMA_REQUEST_DFSDM1_FLT0; + } + if (instance == Peripheral::dfsdm_filter1) { + return DMA_REQUEST_DFSDM1_FLT1; + } + if (instance == Peripheral::dfsdm_filter2) { + return DMA_REQUEST_DFSDM1_FLT2; + } + if (instance == Peripheral::dfsdm_filter3) { + return DMA_REQUEST_DFSDM1_FLT3; + } compile_error("Invalid DMA request configuration"); + return 0; } @@ -303,7 +329,7 @@ struct DMADomain { static consteval uint32_t get_PeriphDataAlignment(Peripheral instance, uint8_t i) { if (is_spi(instance) || is_i2c(instance)) { return DMA_PDATAALIGN_BYTE; - } else if (is_none(instance)) { + } else if (is_none(instance) || (is_dfsdm(instance))) { return DMA_PDATAALIGN_WORD; } return DMA_PDATAALIGN_HALFWORD; diff --git a/Inc/HALAL/Models/Pin.hpp b/Inc/HALAL/Models/Pin.hpp index f4881abe3..a9407cbd9 100644 --- a/Inc/HALAL/Models/Pin.hpp +++ b/Inc/HALAL/Models/Pin.hpp @@ -25,8 +25,8 @@ constexpr GPIODomain::Pin PA14{A, GPIO_PIN_14, 0b1000000000000001}; constexpr GPIODomain::Pin PA15{A, GPIO_PIN_15, 0b1100111111010011}; // Port B -constexpr GPIODomain::Pin PB0{B, GPIO_PIN_0, 0b0111110011111011}; -constexpr GPIODomain::Pin PB1{B, GPIO_PIN_1, 0b0111110011111011}; +constexpr GPIODomain::Pin PB0{B, GPIO_PIN_0, 0b0111111011111011}; +constexpr GPIODomain::Pin PB1{B, GPIO_PIN_1, 0b0111111011111011}; constexpr GPIODomain::Pin PB2{B, GPIO_PIN_2, 0b0110111110111011}; constexpr GPIODomain::Pin PB3{B, GPIO_PIN_3, 0b0111110110111011}; constexpr GPIODomain::Pin PB4{B, GPIO_PIN_4, 0b0111111110110011}; @@ -35,26 +35,26 @@ constexpr GPIODomain::Pin PB6{B, GPIO_PIN_6, 0b0111110111111111}; constexpr GPIODomain::Pin PB7{B, GPIO_PIN_7, 0b0111110111111011}; constexpr GPIODomain::Pin PB8{B, GPIO_PIN_8, 0b0110111111111111}; constexpr GPIODomain::Pin PB9{B, GPIO_PIN_9, 0b0110111111111111}; -constexpr GPIODomain::Pin PB10{B, GPIO_PIN_10, 0b0111110111111111}; +constexpr GPIODomain::Pin PB10{B, GPIO_PIN_10, 0b0111111111111111}; constexpr GPIODomain::Pin PB11{B, GPIO_PIN_11, 0b0111110111111011}; -constexpr GPIODomain::Pin PB12{B, GPIO_PIN_12, 0b0111110111111011}; +constexpr GPIODomain::Pin PB12{B, GPIO_PIN_12, 0b0111111111111011}; constexpr GPIODomain::Pin PB13{B, GPIO_PIN_13, 0b0111110111111011}; -constexpr GPIODomain::Pin PB14{B, GPIO_PIN_14, 0b0111110011111011}; +constexpr GPIODomain::Pin PB14{B, GPIO_PIN_14, 0b0111111011111011}; constexpr GPIODomain::Pin PB15{B, GPIO_PIN_15, 0b0111110011111011}; // Port C -constexpr GPIODomain::Pin PC0{C, GPIO_PIN_0, 0b0110110110110011}; -constexpr GPIODomain::Pin PC1{C, GPIO_PIN_1, 0b0110111111111111}; +constexpr GPIODomain::Pin PC0{C, GPIO_PIN_0, 0b0110111110110011}; +constexpr GPIODomain::Pin PC1{C, GPIO_PIN_1, 0b0111111111111111}; constexpr GPIODomain::Pin PC2{C, GPIO_PIN_2, 0b0110111110110011}; -constexpr GPIODomain::Pin PC3{C, GPIO_PIN_3, 0b0110111110110011}; +constexpr GPIODomain::Pin PC3{C, GPIO_PIN_3, 0b0111111110110011}; constexpr GPIODomain::Pin PC4{C, GPIO_PIN_4, 0b0110110110111011}; -constexpr GPIODomain::Pin PC5{C, GPIO_PIN_5, 0b0110110110111111}; +constexpr GPIODomain::Pin PC5{C, GPIO_PIN_5, 0b0111110110111111}; constexpr GPIODomain::Pin PC6{C, GPIO_PIN_6, 0b0111110011111111}; constexpr GPIODomain::Pin PC7{C, GPIO_PIN_7, 0b0111110011111111}; constexpr GPIODomain::Pin PC8{C, GPIO_PIN_8, 0b0111110011011111}; constexpr GPIODomain::Pin PC9{C, GPIO_PIN_9, 0b0111110110111111}; constexpr GPIODomain::Pin PC10{C, GPIO_PIN_10, 0b0110111111111111}; -constexpr GPIODomain::Pin PC11{C, GPIO_PIN_11, 0b0110111111111111}; +constexpr GPIODomain::Pin PC11{C, GPIO_PIN_11, 0b0111111111111111}; constexpr GPIODomain::Pin PC12{C, GPIO_PIN_12, 0b0110111111011111}; constexpr GPIODomain::Pin PC13{C, GPIO_PIN_13, 0b1000000000000001}; constexpr GPIODomain::Pin PC14{C, GPIO_PIN_14, 0b1000000000000001}; @@ -64,14 +64,14 @@ constexpr GPIODomain::Pin PC15{C, GPIO_PIN_15, 0b1000000000000001}; constexpr GPIODomain::Pin PD0{D, GPIO_PIN_0, 0b0111110110011011}; constexpr GPIODomain::Pin PD1{D, GPIO_PIN_1, 0b0111110110011011}; constexpr GPIODomain::Pin PD2{D, GPIO_PIN_2, 0b1110110110011011}; -constexpr GPIODomain::Pin PD3{D, GPIO_PIN_3, 0b0110110111110011}; +constexpr GPIODomain::Pin PD3{D, GPIO_PIN_3, 0b0111110111110011}; constexpr GPIODomain::Pin PD4{D, GPIO_PIN_4, 0b0010110010010011}; constexpr GPIODomain::Pin PD5{D, GPIO_PIN_5, 0b0010110010010011}; -constexpr GPIODomain::Pin PD6{D, GPIO_PIN_6, 0b0111110110111011}; -constexpr GPIODomain::Pin PD7{D, GPIO_PIN_7, 0b0110110111111011}; +constexpr GPIODomain::Pin PD6{D, GPIO_PIN_6, 0b0111111110111011}; +constexpr GPIODomain::Pin PD7{D, GPIO_PIN_7, 0b0111110111111011}; constexpr GPIODomain::Pin PD8{D, GPIO_PIN_8, 0b0110110110010011}; -constexpr GPIODomain::Pin PD9{D, GPIO_PIN_9, 0b0110110110010011}; -constexpr GPIODomain::Pin PD10{D, GPIO_PIN_10, 0b0110110110010011}; +constexpr GPIODomain::Pin PD9{D, GPIO_PIN_9, 0b0111110110010011}; +constexpr GPIODomain::Pin PD10{D, GPIO_PIN_10, 0b0111110110010011}; constexpr GPIODomain::Pin PD11{D, GPIO_PIN_11, 0b0111100110110011}; constexpr GPIODomain::Pin PD12{D, GPIO_PIN_12, 0b0111110110110011}; constexpr GPIODomain::Pin PD13{D, GPIO_PIN_13, 0b0111110110110011}; @@ -83,7 +83,7 @@ constexpr GPIODomain::Pin PE0{E, GPIO_PIN_0, 0b0111110010110011}; constexpr GPIODomain::Pin PE1{E, GPIO_PIN_1, 0b0111110010011011}; constexpr GPIODomain::Pin PE2{E, GPIO_PIN_2, 0b0110110111110011}; constexpr GPIODomain::Pin PE3{E, GPIO_PIN_3, 0b0110110111110011}; -constexpr GPIODomain::Pin PE4{E, GPIO_PIN_4, 0b0110110111111111}; +constexpr GPIODomain::Pin PE4{E, GPIO_PIN_4, 0b0111110111111111}; constexpr GPIODomain::Pin PE5{E, GPIO_PIN_5, 0b0110110111111011}; constexpr GPIODomain::Pin PE6{E, GPIO_PIN_6, 0b0110110111111111}; constexpr GPIODomain::Pin PE7{E, GPIO_PIN_7, 0b0111110010010011}; @@ -110,7 +110,7 @@ constexpr GPIODomain::Pin PF9{F, GPIO_PIN_9, 0b0111110010111011}; constexpr GPIODomain::Pin PF10{F, GPIO_PIN_10, 0b0110110010110011}; constexpr GPIODomain::Pin PF11{F, GPIO_PIN_11, 0b0010110010010011}; constexpr GPIODomain::Pin PF12{F, GPIO_PIN_12, 0b0010010010010011}; -constexpr GPIODomain::Pin PF13{F, GPIO_PIN_13, 0b0110110010110011}; +constexpr GPIODomain::Pin PF13{F, GPIO_PIN_13, 0b0111110010110011}; constexpr GPIODomain::Pin PF14{F, GPIO_PIN_14, 0b0110110010110011}; constexpr GPIODomain::Pin PF15{F, GPIO_PIN_15, 0b0010110010010011}; diff --git a/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp b/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp index 45d9f3d0a..59f3b15c1 100644 --- a/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp +++ b/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp @@ -136,6 +136,32 @@ TimerXList Basic_6 = 6, Basic_7 = 7, }; + enum class SelectionTrigger1 : uint32_t { + Reset = TIM_TRGO_RESET, + Enable = TIM_TRGO_ENABLE, + Update = TIM_TRGO_UPDATE, + General_Compare = TIM_TRGO_OC1, + Compare_channel1 = TIM_TRGO_OC1REF, + Compare_channel2 = TIM_TRGO_OC2REF, + Compare_channel3 = TIM_TRGO_OC3REF, + Compare_channel4 = TIM_TRGO_OC4REF + }; + enum class SelectionTrigger2 : uint32_t { + Reset = TIM_TRGO2_RESET, + Enable = TIM_TRGO2_ENABLE, + Update = TIM_TRGO2_UPDATE, + General_Compare = TIM_TRGO2_OC1, + Compare_channel1 = TIM_TRGO2_OC1REF, + Compare_channel2 = TIM_TRGO2_OC2REF, + Compare_channel3 = TIM_TRGO2_OC3REF, + Compare_channel4 = TIM_TRGO2_OC4REF, + Compare_channel5 = TIM_TRGO2_OC5REF, + Compare_channel6 = TIM_TRGO2_OC6REF, + Compare_channel4_R_channel6_F = TIM_TRGO2_OC4REF_RISING_OC6REF_FALLING, + Compare_channel4_R_channel6_R = TIM_TRGO2_OC4REF_RISING_OC6REF_RISING, + Compare_channel5_R_channel6_F = TIM_TRGO2_OC5REF_RISING_OC6REF_FALLING, + Compare_channel5_R_channel6_R = TIM_TRGO2_OC5REF_RISING_OC6REF_RISING + }; // Alternate functions for timers enum class TimerAF { @@ -231,10 +257,14 @@ TimerXList TimerRequest request; uint8_t pin_count; std::array pins; /* this won't be read in Timer constructor */ + SelectionTrigger1 trgo1{SelectionTrigger1::Reset}; + SelectionTrigger2 trgo2{SelectionTrigger2::Reset}; }; struct Config { uint8_t timer_idx; + SelectionTrigger1 trgo1; + SelectionTrigger2 trgo2; }; static constexpr TIM_HandleTypeDef* hal_handles[16] = {// general purpose timers @@ -468,15 +498,17 @@ TimerXList : e(ent.name, ent.request, sizeof...(pinargs), - std::array( - {GetPinFromIdx(pinargs, 0), - GetPinFromIdx(pinargs, 1), - GetPinFromIdx(pinargs, 2), - GetPinFromIdx(pinargs, 3), - GetPinFromIdx(pinargs, 4), - GetPinFromIdx(pinargs, 5), - GetPinFromIdx(pinargs, 6)} - )), + std::array({ + GetPinFromIdx(pinargs, 0), + GetPinFromIdx(pinargs, 1), + GetPinFromIdx(pinargs, 2), + GetPinFromIdx(pinargs, 3), + GetPinFromIdx(pinargs, 4), + GetPinFromIdx(pinargs, 5), + GetPinFromIdx(pinargs, 6), + }), + ent.trgo1, + ent.trgo2), gpio0(GetGPIOFromIdx(pinargs, ent.request, 0)), gpio1(GetGPIOFromIdx(pinargs, ent.request, 1)), gpio2(GetGPIOFromIdx(pinargs, ent.request, 2)), @@ -521,6 +553,8 @@ TimerXList .request = e.request, .pin_count = e.pin_count, .pins = e.pins, + .trgo1 = e.trgo1, + .trgo2 = e.trgo2 }; ctx.template add(local_entry, this); } @@ -575,6 +609,8 @@ TimerXList Config cfg = { .timer_idx = timer_idxmap[reqint], + .trgo1 = requests[i].trgo1, + .trgo2 = requests[i].trgo2 }; cfgs[cfg_idx++] = cfg; @@ -607,6 +643,8 @@ TimerXList uint8_t reqint = remaining_32bit_timers[count_32bit_requests]; Config cfg = { .timer_idx = timer_idxmap[reqint], + .trgo1 = requests[i].trgo1, + .trgo2 = requests[i].trgo2 }; cfgs[cfg_idx++] = cfg; @@ -656,6 +694,8 @@ TimerXList uint8_t reqint = remaining_timers[i]; Config cfg = { .timer_idx = timer_idxmap[reqint], + .trgo1 = requests[i].trgo1, + .trgo2 = requests[i].trgo2 }; cfgs[cfg_idx++] = cfg; } @@ -667,6 +707,7 @@ TimerXList struct Instance { TIM_TypeDef* tim; TIM_HandleTypeDef* hal_tim; + TIM_MasterConfigTypeDef master{}; uint8_t timer_idx; }; @@ -688,6 +729,7 @@ TimerXList TIM_HandleTypeDef* handle = hal_handles[e.timer_idx]; TIM_TypeDef* tim = cmsis_timers[e.timer_idx]; + handle->Instance = tim; handle->Init.Period = 0; handle->Init.Prescaler = 0; @@ -717,6 +759,15 @@ TimerXList inst->tim = tim; inst->hal_tim = handle; inst->timer_idx = e.timer_idx; + TIM_MasterConfigTypeDef sMasterConfig = {}; + sMasterConfig.MasterOutputTrigger = static_cast(e.trgo1); + sMasterConfig.MasterOutputTrigger2 = static_cast(e.trgo2); + sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; + inst->master = sMasterConfig; + if (HAL_TIMEx_MasterConfigSynchronization(inst->hal_tim, &sMasterConfig) != + HAL_OK) { + ErrorHandler("Unable to configure master synch"); + } } } }; diff --git a/Inc/HALAL/Services/ADC/ADC.hpp b/Inc/HALAL/Services/ADC/ADC.hpp index 105a932b7..7356a89b1 100644 --- a/Inc/HALAL/Services/ADC/ADC.hpp +++ b/Inc/HALAL/Services/ADC/ADC.hpp @@ -189,15 +189,7 @@ struct ADCDomain { ClockPrescaler prescaler = ClockPrescaler::DIV1, uint32_t sample_rate_hz = 0 ) - : ADC( - pin, - resolution, - sample_time, - prescaler, - sample_rate_hz, - peripheral, - channel - ) {} + : ADC(pin, resolution, sample_time, prescaler, sample_rate_hz, peripheral, channel) {} template consteval std::size_t inscribe(Ctx& ctx) const { const auto gpio_idx = gpio.inscribe(ctx); diff --git a/Inc/HALAL/Services/DFSDM/DFSDM.hpp b/Inc/HALAL/Services/DFSDM/DFSDM.hpp new file mode 100644 index 000000000..a4abc1e56 --- /dev/null +++ b/Inc/HALAL/Services/DFSDM/DFSDM.hpp @@ -0,0 +1,1369 @@ +#pragma once + +#include "ErrorHandler/ErrorHandler.hpp" +#include "HALAL/Models/GPIO.hpp" +#include "HALAL/Models/Pin.hpp" +#include "HALAL/Models/DMA/DMA2.hpp" +#include "HALAL/Models/MPU.hpp" + +#define STLIB_DFSDM_DMA_BUFFER_ATTR D1_NC + +#define Oversampling_MAX 1024 +#define Oversampling_MAX_Filter_4 215 +#define Oversampling_MAX_Filter_5 73 +#define Possible_Pin_Channel 20 +#define OFFSET_MAX (1 << 23) - 1 +#define OFFSET_MIN -(1 << 23) +#define MAX_BUFFER_SIZE_TOTAL 1024 +using ST_LIB::DMA_Domain; +using ST_LIB::GPIODomain; +namespace ST_LIB { +using Callback = void (*)(void); +extern void compile_error(const char* msg); + +struct DFSDM_CHANNEL_DOMAIN { + /* Constant Values of Register*/ + // DatPack = 0 Standar + // DatMPx = 0 // External input + // ChinSel = 0 // It make sense for our use case. + // SPICKSel = ¿? + enum class SPICKSel : uint8_t { + CLK_IN = 0, + NORMAL_CLK_OUT = 1, + CLK_DIVIDED_2_RISING = 2, + CLK_DIVIDED_2_FALLING = 3 + }; + enum class SPI_Type : uint8_t { SPI_RISING = 0, SPI_FALLING = 1 }; + + enum class Filter_Type : uint8_t { + FastSinc = 0, + Sinc1 = 1, + Sinc2 = 2, + Sinc3 = 3, + Sinc4 = 4, + Sinc5 = 5 + }; + + enum Fast_Conversion : uint8_t { Disable = 0, Enable = 1 }; + + enum class Dma : uint8_t { Disable = 0, Enable = 1 }; + + enum class Sync_Conversion : uint8_t { Independent = 0, Sync_With_Flt0 = 1 }; + + enum class Regular_Mode : uint8_t { Single = 0, Continuous = 1 }; + + enum class Analog_Watchdog_Mode : uint8_t { + After_Filter = 0, // AWFSEL = 0 + Channel_Data = 1 // AWFSEL = 1 + }; + // AWFSEL = 0 high precision, slow speed <- Ideally for our case + // AWFSEL = 1 16 bits precision ultra high speed oversampling ratio 1..32 filter (1..3) 8 + // relojes de clock + + enum class Overrun : uint8_t { Enable = 0, Disable = 1 }; + enum class Clock_Absence : uint8_t { Disable = 0, Enable = 1 }; + enum class Short_Circuit : uint8_t { Disable = 0, Enable = 1 }; + enum class Extreme_Detector : uint8_t { Disable = 0, Enable = 1 }; + enum class Analog_Watchdog : uint8_t { Disable = 0, Enable = 1 }; + enum class Trigger_Timer_Source : uint8_t { + Unused, + Tim1, + Tim3, + Tim4, + Tim7, + Tim8, + Tim16, + Tim23, + Tim24, + Exti11, + Exti15, + Lptim1, + Lptim2, + Lptim3 + }; + enum class Type_Conversion : uint8_t { Regular, Injected }; + enum class Injected_Mode : uint8_t { Single, Scan }; + struct Config_Channel { + int32_t offset{0}; + uint32_t right_shift{0}; + SPICKSel spi_clock_sel{SPICKSel::CLK_DIVIDED_2_RISING}; + SPI_Type spi_type{SPI_Type::SPI_RISING}; + + /* -------- Runtime protections -------- */ + Clock_Absence clock_absence{Clock_Absence::Disable}; + Short_Circuit short_circuit{Short_Circuit::Disable}; + Extreme_Detector extreme_detector{Extreme_Detector::Disable}; + + uint8_t short_circuit_count{0xFF}; + + /* -------- Analog watchdog -------- */ + Analog_Watchdog watchdog{Analog_Watchdog::Disable}; + + // If Mode Watchdog == After filter + Filter_Type filter_watchdog{Filter_Type::Sinc2}; + uint8_t watchdog_oversampling{5}; + }; + struct Config_Filter { + uint8_t filter{0}; + Trigger_Timer_Source trigger_conv{Trigger_Timer_Source::Unused}; + Filter_Type filter_type{Filter_Type::FastSinc}; + uint16_t oversampling{32}; + uint16_t integrator{1}; + Type_Conversion type_conv{Type_Conversion::Regular}; + Dma dma{Dma::Disable}; + Fast_Conversion fast{Fast_Conversion::Disable}; + Sync_Conversion rsync{Sync_Conversion::Independent}; + Regular_Mode rcont{Regular_Mode::Single}; + Injected_Mode jscan{Injected_Mode::Scan}; + + Overrun overrun{Overrun::Disable}; + + Analog_Watchdog watchdog{Analog_Watchdog::Disable}; + Analog_Watchdog_Mode watchdog_mode{Analog_Watchdog_Mode::After_Filter}; + int32_t watchdog_low_threshold{0x10000000}; + int32_t watchdog_high_threshold{0x7FFFFFFF}; + // callbacks + Callback watchdog_callback{nullptr}; + Callback conversion_complete_callback{nullptr}; + Callback overrun_callback{nullptr}; + Callback clock_absence_callback{nullptr}; + Callback short_circuit_callback{nullptr}; + }; + static constexpr std::array, Possible_Pin_Channel> + pin_to_channel = {{{PE4, 3}, {PC0, 4}, {PC1, 0}, {PC3, 1}, {PC5, 2}, + {PB1, 1}, {PF13, 6}, {PE7, 2}, {PE10, 4}, {PE12, 5}, + {PB10, 7}, {PB12, 1}, {PB14, 2}, {PD9, 3}, {PC7, 3}, + {PC11, 5}, {PD1, 6}, {PD6, 1}, {PD7, 4}, {PB6, 5}}}; + + static consteval uint8_t get_channel(const GPIODomain::Pin& pin) { + for (int i = 0; i < Possible_Pin_Channel; i++) { + if (pin_to_channel[i].first == pin) { + return pin_to_channel[i].second; + } + } + compile_error("This pin cannot be used as a DFSDM_Channel"); + } + + static consteval bool is_correct_oversampling(Filter_Type f, uint16_t osr) { + switch (f) { + case Filter_Type::FastSinc: + case Filter_Type::Sinc1: + case Filter_Type::Sinc2: + case Filter_Type::Sinc3: + return osr >= 1 && osr <= Oversampling_MAX; + + case Filter_Type::Sinc4: + return osr >= 1 && osr <= Oversampling_MAX_Filter_4; + + case Filter_Type::Sinc5: + return osr >= 1 && osr <= Oversampling_MAX_Filter_5; + } + return false; + } + static consteval GPIODomain::AlternateFunction dfsdm_channel_af(const GPIODomain::Pin& pin) { + if ((pin.port == GPIODomain::Port::B && pin.pin == GPIO_PIN_1) || + (pin.port == GPIODomain::Port::B && pin.pin == GPIO_PIN_10) || + (pin.port == GPIODomain::Port::B && pin.pin == GPIO_PIN_12) || + (pin.port == GPIODomain::Port::B && pin.pin == GPIO_PIN_14) || + (pin.port == GPIODomain::Port::C && pin.pin == GPIO_PIN_0)) { + return GPIODomain::AlternateFunction::AF6; + } + if ((pin.port == GPIODomain::Port::C && pin.pin == GPIO_PIN_7) || + (pin.port == GPIODomain::Port::D && pin.pin == GPIO_PIN_6)) { + return GPIODomain::AlternateFunction::AF4; + } + if ((pin.port == GPIODomain::Port::B && pin.pin == GPIO_PIN_6)) { + return GPIODomain::AlternateFunction::AF11; + } + return GPIODomain::AlternateFunction::AF3; // In any other case + } + static consteval DMA_Domain::Peripheral get_dma_peripheral(uint8_t filter) { + switch (filter) { + case 0: + return DMA_Domain::Peripheral::dfsdm_filter0; + case 1: + return DMA_Domain::Peripheral::dfsdm_filter1; + case 2: + return DMA_Domain::Peripheral::dfsdm_filter2; + case 3: + return DMA_Domain::Peripheral::dfsdm_filter3; + } + compile_error("Cannot be different than 0,1,2,3"); + } + struct Entry { + Config_Channel config_channel; + Config_Filter config_filter; + uint8_t channel; + size_t gpio_idx; + size_t buffer_size; + }; + + static constexpr size_t max_instances{8}; + struct DFSDM_CHANNEL { + using domain = DFSDM_CHANNEL_DOMAIN; + const GPIODomain::Pin& pin; + GPIODomain::GPIO gpio; + const Config_Channel config_channel; + const Config_Filter config_filter; + uint8_t channel; + size_t buffer_size; + consteval DFSDM_CHANNEL( + const GPIODomain::Pin& pin, + const Config_Channel config_channel, + const Config_Filter config_filter, + std::size_t buffer_size + ) + : pin(pin), + gpio{ + pin, + GPIODomain::OperationMode::ALT_PP, + GPIODomain::Pull::None, + GPIODomain::Speed::High, + dfsdm_channel_af(pin) + }, + config_channel(config_channel), config_filter(config_filter), + buffer_size(buffer_size) { + channel = get_channel(pin); + if (config_filter.filter > 3) { + compile_error("Solo hay 4 filtros [0..3]"); + } + if (config_channel.offset > OFFSET_MAX || config_channel.offset < OFFSET_MIN) { + compile_error("Your offset is bigger than the maximum size"); + } + if (config_channel.right_shift > 0x000000FF) { + compile_error("Your right_shift is bigger than the maximum size"); + } + if (config_filter.integrator <= 0) { + compile_error("DFSDM_FILTER: Integrator out of range"); + } + if (!is_correct_oversampling(config_filter.filter_type, config_filter.oversampling)) { + compile_error("DFSDM_FILTER: invalid oversampling for selected filter type"); + } + if (config_channel.watchdog_oversampling > 32) { + compile_error("DFSDM_Watchdog oversampling is bigger than the maximum allowed"); + } + if (static_cast(config_channel.filter_watchdog) > 3) { + compile_error( + "Why would a sane person need a filter of the watchdog higher than sinc3" + ); + } + } + + template consteval std::size_t inscribe(Ctx& ctx) const { + const auto gpio_idx = gpio.inscribe(ctx); + Entry e{ + .config_channel = config_channel, + .config_filter = config_filter, + .channel = channel, + .gpio_idx = gpio_idx, + .buffer_size = buffer_size + }; + return ctx.template add(e, this); + } + }; + // I hate stm32,has volatile in the DFSDM structs. + struct FilterConfig { + uint32_t FLTCR1{}; + uint32_t FLTCR2{}; + uint32_t FLTFCR{}; + uint32_t FLTAWHTR{}; + uint32_t FLTAWLTR{}; + uint32_t FLTJCHGR{}; + }; + struct ChannelConfig { + uint32_t CHCFGR1{}; + uint32_t CHCFGR2{}; + uint32_t CHAWSCDR{}; + }; + struct Config { + size_t gpio_idx; + size_t dma_request; + FilterConfig init_data_filter; + ChannelConfig init_data_channel; + + uint32_t latency_cycles; + Type_Conversion type_conv; + Dma dma_enable; + + uint8_t filter; + uint8_t channel; + + size_t buffer_size; + size_t buffer_pos_ini; + + // callbacks + Callback watchdog_callback{nullptr}; + Callback conversion_complete_callback{nullptr}; + Callback overrun_callback{nullptr}; + Callback clock_absence_callback{nullptr}; + Callback short_circuit_callback{nullptr}; + }; + static consteval DMADomain::Peripheral dma_filter(uint8_t filter) { + switch (filter) { + case 0: + return DMADomain::Peripheral::dfsdm_filter0; + case 1: + return DMADomain::Peripheral::dfsdm_filter1; + case 2: + return DMADomain::Peripheral::dfsdm_filter2; + case 3: + return DMADomain::Peripheral::dfsdm_filter3; + default: + compile_error("There is not other filter"); + break; + } + return DMADomain::Peripheral::dfsdm_filter0; + } + + static consteval uint32_t dma_request(uint8_t filter) { + return DMADomain::get_Request(dma_filter(filter), 0); + } + struct BufferSizes { + std::size_t filter0 = 0; + std::size_t filter1 = 0; + std::size_t filter2 = 0; + std::size_t filter3 = 0; + }; + static consteval BufferSizes calculate_buffer_sizes(span cfgs) { + BufferSizes sizes; + for (const auto& cfg : cfgs) { + switch (cfg.filter) { + case 0: + if (cfg.type_conv == Type_Conversion::Regular) { + sizes.filter0 = std::max(cfg.buffer_size, sizes.filter0); + } else { + sizes.filter0 += cfg.buffer_size; + } + break; + case 1: + if (cfg.type_conv == Type_Conversion::Regular) { + sizes.filter1 = std::max(cfg.buffer_size, sizes.filter1); + } else { + sizes.filter1 += cfg.buffer_size; + } + break; + case 2: + if (cfg.type_conv == Type_Conversion::Regular) { + sizes.filter2 = std::max(cfg.buffer_size, sizes.filter2); + } else { + sizes.filter2 += cfg.buffer_size; + } + break; + case 3: + if (cfg.type_conv == Type_Conversion::Regular) { + sizes.filter3 = std::max(cfg.buffer_size, sizes.filter3); + } else { + sizes.filter3 += cfg.buffer_size; + } + break; + } + } + return sizes; + } + + static consteval uint32_t compute_latency(const Entry& e) { + const uint32_t fosr = e.config_filter.oversampling; + const uint32_t iosr = e.config_filter.integrator; + + if (e.config_filter.fast == Fast_Conversion::Enable && + e.config_filter.rcont == Regular_Mode::Continuous) { + return fosr * iosr; + } + + if (e.config_filter.filter_type == Filter_Type::FastSinc) { + return fosr * (iosr - 1 + 4) + 2; + } + + const uint32_t ford = static_cast(e.config_filter.filter_type); + return fosr * (iosr - 1 + ford) + ford; + } + static consteval uint32_t get_trigger(Trigger_Timer_Source trig) { + switch (trig) { + case Trigger_Timer_Source::Tim1: + return 0; + case Trigger_Timer_Source::Tim3: + return 4; + case Trigger_Timer_Source::Tim4: + return 5; + case Trigger_Timer_Source::Tim7: + return 8; + case Trigger_Timer_Source::Tim8: + return 2; + case Trigger_Timer_Source::Tim16: + return 6; + case Trigger_Timer_Source::Tim23: + return 11; + case Trigger_Timer_Source::Tim24: + return 12; + case Trigger_Timer_Source::Exti11: + return 24; + case Trigger_Timer_Source::Exti15: + return 25; + case Trigger_Timer_Source::Lptim1: + return 26; + case Trigger_Timer_Source::Lptim2: + return 27; + case Trigger_Timer_Source::Lptim3: + return 28; + case Trigger_Timer_Source::Unused: + break; + } + return 0; + } + // Filter registers + static consteval uint32_t make_fltfcr(const Entry& e) { + return (uint32_t(e.config_filter.filter_type) << DFSDM_FLTFCR_FORD_Pos) | + (uint32_t(e.config_filter.oversampling - 1) << DFSDM_FLTFCR_FOSR_Pos) | + (uint32_t(e.config_filter.integrator - 1) << DFSDM_FLTFCR_IOSR_Pos); + } + static consteval uint32_t make_fltcr2_global() { + // Activate the interrupt, to activate the continous detection, then the channel can + return (uint32_t)(DFSDM_FLTCR2_CKABIE | DFSDM_FLTCR2_SCDIE); + } + static consteval uint32_t make_fltcr2(const Entry& e) { + uint32_t v = 0; + if (e.config_filter.dma == Dma::Disable || + e.config_filter.conversion_complete_callback != nullptr) { + v |= DFSDM_FLTCR2_REOCIE; + v |= DFSDM_FLTCR2_JEOCIE; + } + if (e.config_filter.watchdog == Analog_Watchdog::Enable) { + v |= DFSDM_FLTCR2_AWDIE; + v |= 1 << (DFSDM_FLTCR2_AWDCH_Pos + e.channel); + } + if (e.config_channel.extreme_detector == Extreme_Detector::Enable) { + v |= 1 << (DFSDM_FLTCR2_EXCH_Pos + e.channel); + } + if (e.config_filter.overrun == Overrun::Enable) { + v |= DFSDM_FLTCR2_JOVRIE; + v |= DFSDM_FLTCR2_ROVRIE; + } + return v; + } + static consteval uint32_t make_fltcr1(const Entry& e, uint32_t filter) { + uint32_t v = 0; + + if (e.config_filter.type_conv == Type_Conversion::Regular) { + v |= (uint32_t(e.config_filter.dma) << DFSDM_FLTCR1_RDMAEN_Pos); + v |= (uint32_t(e.config_filter.fast) << DFSDM_FLTCR1_FAST_Pos); + v |= (uint32_t(e.config_filter.rsync) << DFSDM_FLTCR1_RSYNC_Pos); + v |= (uint32_t(e.config_filter.rcont) << DFSDM_FLTCR1_RCONT_Pos); + } else if (e.config_filter.type_conv == Type_Conversion::Injected) { + v |= uint32_t(e.config_filter.jscan) + << DFSDM_FLTCR1_JSCAN_Pos; // activate conversion of the entire group + v |= (uint32_t)(e.config_filter.dma) << DFSDM_FLTCR1_JDMAEN_Pos; + if (e.config_filter.trigger_conv != Trigger_Timer_Source::Unused) { + v |= DFSDM_FLTCR1_JEXTEN_0; // with the risings + if (filter == 0) { + v |= get_trigger(e.config_filter.trigger_conv) << DFSDM_FLTCR1_JEXTSEL_Pos; + } + if (e.config_filter.rsync == Sync_Conversion::Sync_With_Flt0) { + v |= DFSDM_FLTCR1_JSYNC; + + } else { + v |= get_trigger(e.config_filter.trigger_conv) << DFSDM_FLTCR1_JEXTSEL_Pos; + } + } + } + v |= uint32_t(e.config_filter.watchdog_mode) << DFSDM_FLTCR1_AWFSEL_Pos; + return v; + } + static consteval uint32_t make_fltawhtr(const Entry& e) { + uint32_t v = 0; + if (e.config_filter.watchdog_mode == Analog_Watchdog_Mode::Channel_Data) + v |= (e.config_filter.watchdog_high_threshold & 0xFFFF) + << (DFSDM_FLTAWHTR_AWHT_Pos + DFSDM_FLTAWHTR_AWHT_Pos); + else + v |= (e.config_filter.watchdog_high_threshold & 0xFFFFFF) << DFSDM_FLTAWHTR_AWHT_Pos; + return v; + } + static consteval uint32_t make_fltawltr(const Entry& e) { + uint32_t v = 0; + if (e.config_filter.watchdog_mode == Analog_Watchdog_Mode::Channel_Data) + v |= (e.config_filter.watchdog_low_threshold & 0xFFFF) + << (DFSDM_FLTAWHTR_AWHT_Pos + DFSDM_FLTAWHTR_AWHT_Pos); + else + v |= (e.config_filter.watchdog_low_threshold & 0xFFFFFF) << DFSDM_FLTAWHTR_AWHT_Pos; + return v; + } + // Channel + static consteval uint32_t make_chawscdr(const Entry& e) { + uint32_t v = 0; + v |= uint32_t(e.config_channel.short_circuit_count) << DFSDM_CHAWSCDR_SCDT_Pos; + if (e.config_channel.watchdog == Analog_Watchdog::Enable) { + v |= static_cast(e.config_channel.filter_watchdog) + << DFSDM_CHAWSCDR_AWFORD_Pos; + v |= static_cast((e.config_channel.watchdog_oversampling - 1) & 0xF) + << DFSDM_CHAWSCDR_AWFOSR_Pos; + } + return v; + } + static consteval uint32_t make_chcfgr1(const Entry& e) { + uint32_t v = 0; + // DATPACK = 0 -> Standard mode + // DATMPX = 0 -> Comes from an external serial input + // Chinsel = 0 -> channel input are taken from pin of the same channel y + v |= uint32_t(e.config_channel.spi_clock_sel) << DFSDM_CHCFGR1_SPICKSEL_Pos; + v |= uint32_t(e.config_channel.spi_type) << DFSDM_CHCFGR1_SITP_Pos; + v |= uint32_t(e.config_channel.clock_absence) << DFSDM_CHCFGR1_CKABEN_Pos; + v |= uint32_t(e.config_channel.short_circuit) << DFSDM_CHCFGR1_SCDEN_Pos; + + return v; + } + static consteval uint32_t make_chcfgr2(const Entry& e) { + uint32_t v = 0; + v |= (e.config_channel.offset & 0x00FFFFFF) << DFSDM_CHCFGR2_OFFSET_Pos; + v |= uint8_t(e.config_channel.right_shift & 0x0F) << DFSDM_CHCFGR2_DTRBS_Pos; + return v; + } + + template + static consteval std::array build(std::span entries) { + if (N == 0) + return {}; + std::array cfgs{}; + std::array channels_used{false}; + std::array filters_used{-1, -1, -1, -1}; + std::array channel_order{-1, -1, -1, -1, -1, -1, -1, -1}; + + for (size_t i = 0; i < N; ++i) { + const Entry& e = entries[i]; + + if (channels_used[e.channel] == true) { + compile_error("You have two pins using the same channel"); + } + channels_used[e.channel] = true; + Config& cfg = cfgs[i]; + + cfg.gpio_idx = e.gpio_idx; + cfg.dma_request = dma_request(e.config_filter.filter); + + cfg.channel = e.channel; + channel_order[cfg.channel] = i; + + cfg.buffer_size = e.buffer_size; + cfg.type_conv = e.config_filter.type_conv; + cfg.dma_enable = e.config_filter.dma; + cfg.filter = e.config_filter.filter; + + // add the callbacks + cfg.overrun_callback = e.config_filter.overrun_callback; + cfg.clock_absence_callback = e.config_filter.clock_absence_callback; + cfg.short_circuit_callback = e.config_filter.short_circuit_callback; + cfg.watchdog_callback = e.config_filter.watchdog_callback; + cfg.conversion_complete_callback = e.config_filter.conversion_complete_callback; + + cfg.init_data_filter.FLTCR1 |= make_fltcr1(e, cfg.filter); + cfg.init_data_filter.FLTCR2 |= make_fltcr2(e); + cfg.init_data_filter.FLTFCR |= make_fltfcr(e); + cfg.init_data_channel.CHCFGR1 |= make_chcfgr1(e); + cfg.init_data_channel.CHCFGR2 |= make_chcfgr2(e); + cfg.init_data_channel.CHAWSCDR |= make_chawscdr(e); + cfg.init_data_filter.FLTAWHTR |= make_fltawhtr(e); + cfg.init_data_filter.FLTAWLTR |= make_fltawltr(e); + if (e.config_filter.type_conv == Type_Conversion::Injected) + cfg.init_data_filter.FLTJCHGR |= 1 << e.channel; + if (cfg.filter == 0) + cfg.init_data_filter.FLTCR2 |= make_fltcr2_global(); + + cfg.latency_cycles = compute_latency(e); + if (filters_used[cfg.filter] != -1) { + if (cfgs[filters_used[cfg.filter]].init_data_filter.FLTCR1 != + cfg.init_data_filter.FLTCR1 || + (cfgs[filters_used[cfg.filter]].init_data_filter.FLTCR2 & 0xFF) != + (cfg.init_data_filter.FLTCR2 & 0xFF) || + cfgs[filters_used[cfg.filter]].init_data_filter.FLTFCR != + cfg.init_data_filter.FLTFCR || + cfgs[filters_used[cfg.filter]].init_data_filter.FLTAWLTR != + cfg.init_data_filter.FLTAWLTR || + cfgs[filters_used[cfg.filter]].init_data_filter.FLTAWHTR != + cfg.init_data_filter.FLTAWHTR || + cfgs[filters_used[cfg.filter]].dma_enable != cfg.dma_enable) { + compile_error("You have two channels that goes to the same filter with " + "different filter configuration"); + } + // have the same thing in every register of the filter + // Channel group conversion in injected mode + cfgs[filters_used[cfg.filter]].init_data_filter.FLTJCHGR |= + cfg.init_data_filter.FLTJCHGR; + cfg.init_data_filter.FLTJCHGR = + cfgs[filters_used[cfg.filter]].init_data_filter.FLTJCHGR; + // Watchdog and Extreme detector channel enabled + cfgs[filters_used[cfg.filter]].init_data_filter.FLTCR2 |= + cfg.init_data_filter.FLTCR2; + cfg.init_data_filter.FLTCR2 = + cfgs[filters_used[cfg.filter]].init_data_filter.FLTCR2; + // Watchdog and Extreme detector channel enabled + } + filters_used[cfg.filter] = i; + } + // check that dma buffer size is 1 if there is more than one channel per filter and add the + // buffer_pos_ini to every channel + std::array, 4> channels_filter = {}; + for (size_t i = 0; i < N; i++) { + channels_filter[cfgs[i].filter][cfgs[i].channel] = cfgs[i].buffer_size; + uint8_t active_channels = 0; + // active channels + for (size_t j = 0; j < 8; j++) { + if (channels_filter[cfgs[i].filter][j] != 0) { + active_channels++; + } + } + // validate buffers + if (active_channels > 1 && + entries[i].config_filter.type_conv == Type_Conversion::Regular) { + compile_error("Not allowed more than 1 channel per filter in Regular Mode"); + } + if (active_channels > 1 && cfgs[i].dma_enable == Dma::Enable) { + // Look the entry because the data that I want to check is easier to access + if (entries[i].config_filter.jscan == Injected_Mode::Single) { + compile_error("Not allowed more than 1 channel per filter + " + "Injected_Mode::Single_conversion"); + } + for (size_t j = 0; j < 8; j++) { + if (channels_filter[cfgs[i].filter][j] > 1) { + compile_error("Only allowed buffer_size = 1 when multiple DMA channels are " + "used in the same filter"); + } + } + } + } + // give the buffer_pos to every channel I'll give the pos by channel order starting from the + // low + std::array buffer_pos{0, 0, 0, 0}; + for (std::size_t i = 0; i < 8; i++) { + if (channel_order[i] == -1) + continue; + auto& cfg = cfgs[channel_order[i]]; + // If regular conversion give the whole buffer to the channels. + if (cfg.type_conv == Type_Conversion::Regular) { + cfg.buffer_pos_ini = 0; + } + cfg.buffer_pos_ini = buffer_pos[cfg.filter]; + buffer_pos[cfg.filter] += cfg.buffer_size; + } + return cfgs; + } + static consteval std::size_t + dma_entries_for_filter(uint8_t filter, span dma_entries) { + std::size_t count = 0; + for (const auto& entry : dma_entries) { + if (entry.instance != dma_filter(filter)) { + continue; + } + if (entry.id != 0U) { + compile_error("DFSDM: DMA for DFSDM filters must use stream id 0"); + } + ++count; + } + return count; + } + static consteval bool uses_filter_dma(uint8_t filter, span cfgs) { + for (const auto& cfg : cfgs) { + if (cfg.filter == filter && cfg.dma_enable == Dma::Enable) { + return true; + } + } + return false; + } + static consteval std::size_t + dma_contribution_count(span cfgs, span dma_entries) { + std::size_t count = 0; + for (uint8_t fidx = 0; fidx < 4U; ++fidx) { + if (!uses_filter_dma(fidx, cfgs)) { + continue; + } + + const auto existing = dma_entries_for_filter(fidx, dma_entries); + if (existing > 1U) { + compile_error("DFSDM: multiple DMA streams configured for the same DFSDM filter"); + } + if (existing == 0U) { + ++count; + } + } + return count; + } + template + static consteval std::array + build_dma_contributions(span dma_entries, span cfgs) { + std::array extra{}; + std::size_t cursor = 0; + + for (uint8_t fidx = 0; fidx < 4U; ++fidx) { + if (!uses_filter_dma(fidx, cfgs)) { + continue; + } + const auto existing = dma_entries_for_filter(fidx, dma_entries); + if (existing == 0U) { + extra[cursor++] = { + .instance = dma_filter(fidx), + .stream = DMADomain::Stream::none, + .irqn = static_cast(0), + .id = 0, + }; + } + } + + if (cursor != ExtraN) { + compile_error("DFSDM: DMA contribution size mismatch"); + } + + return extra; + } + static inline uint8_t channels_enabled{}; + static constexpr DFSDM_Filter_TypeDef* filter_hw[4] = + {DFSDM1_Filter0, DFSDM1_Filter1, DFSDM1_Filter2, DFSDM1_Filter3}; + static constexpr DFSDM_Channel_TypeDef* channel_hw[8] = { + DFSDM1_Channel0, + DFSDM1_Channel1, + DFSDM1_Channel2, + DFSDM1_Channel3, + DFSDM1_Channel4, + DFSDM1_Channel5, + DFSDM1_Channel6, + DFSDM1_Channel7 + }; + static void inline start_reg_conv_filter(uint8_t filter) { + if (filter > 3) + ErrorHandler("Only filters from 0..3"); + filter_hw[filter]->FLTCR1 |= DFSDM_FLTCR1_RSWSTART; // regular + } + static void inline start_inj_conv_filter(uint8_t filter) { + if (filter > 3) + ErrorHandler("Only filters from 0..3"); + filter_hw[filter]->FLTCR1 |= DFSDM_FLTCR1_JSWSTART; // injected + } + static DMADomain::Instance* + find_dma_instance(uint32_t request, std::span dma_peripherals) { + for (auto& dma_instance : dma_peripherals) { + if (dma_instance.dma.Init.Request == request) { + return &dma_instance; + } + } + return nullptr; + } + struct Instance { + GPIODomain::Instance* gpio_instance; + DMA_Domain::Instance* dma_instance; + DFSDM_Filter_TypeDef* filter_regs{}; + DFSDM_Channel_TypeDef* channel_regs{}; + + Callback watchdog_cb{}; + Callback short_circuit_cb{}; + Callback clock_absence_cb{}; + Callback overrun_cb{}; + Callback end_conversion_cb{}; + + uint32_t latency_cycles; + uint8_t channel; + uint8_t filter; + Type_Conversion type_conv; + Dma dma_enable; + + volatile int32_t* buffer = nullptr; + size_t length_buffer{}; + + private: + bool is_enabled_channel() const { return (channel_regs->CHCFGR1 & DFSDM_CHCFGR1_CHEN_Msk); } + bool is_enabled_filter() const { return (filter_regs->FLTCR1 & DFSDM_FLTCR1_DFEN_Msk); } + bool is_enabled_DFSDM() const { + return (DFSDM1_Channel0->CHCFGR1 & DFSDM_CHCFGR1_DFSDMEN_Msk); + } + void enable_filter() { filter_regs->FLTCR1 |= DFSDM_FLTCR1_DFEN; } + void enable_channel() { channel_regs->CHCFGR1 |= DFSDM_CHCFGR1_CHEN; } + void enable_DFSDM_Peripheral() { DFSDM1_Channel0->CHCFGR1 |= DFSDM_CHCFGR1_DFSDMEN; } + void disable_filter() { filter_regs->FLTCR1 &= ~(DFSDM_FLTCR1_DFEN_Msk); } + void disable_channel() { channel_regs->CHCFGR1 &= ~(DFSDM_CHCFGR1_CHEN); } + void disable_DFSDM_Peripheral() { DFSDM1_Channel0->CHCFGR1 &= ~(DFSDM_CHCFGR1_DFSDMEN); } + + public: + bool is_enabled() { + return is_enabled_DFSDM() && is_enabled_channel() && is_enabled_filter(); + } + void enable() { + // just in case enable everything to work + enable_DFSDM_Peripheral(); + enable_channel(); + enable_filter(); + } + + void disable() { + // only disable channel + channel_regs->CHCFGR1 &= ~(DFSDM_CHCFGR1_CHEN_Msk); + } + + void enable_clock_absence_detector() { channel_regs->CHCFGR1 |= DFSDM_CHCFGR1_CKABEN; } + void enable_short_circuit_detector() { channel_regs->CHCFGR1 |= DFSDM_CHCFGR1_SCDEN; } + void disable_clock_absence_detector() { channel_regs->CHCFGR1 &= (~DFSDM_CHCFGR1_CKABEN); } + void disable_short_circuit_detector() { channel_regs->CHCFGR1 &= (~DFSDM_CHCFGR1_SCDEN); } + void enable_overrun() { filter_regs->FLTCR2 |= DFSDM_FLTCR2_ROVRIE; } + void disable_overrun() { filter_regs->FLTCR2 &= (~DFSDM_FLTCR2_ROVRIE); } + void enable_watchdog() { filter_regs->FLTCR2 |= DFSDM_FLTCR2_AWDCH; } + void disable_watchdog() { filter_regs->FLTCR2 &= (~DFSDM_FLTCR2_AWDCH); } + /*channel functions */ + void change_offset(int32_t offset) { + channel_regs->CHCFGR2 &= ~(DFSDM_CHCFGR2_OFFSET_Msk); + channel_regs->CHCFGR2 |= (offset & 0x00FFFFFF) << DFSDM_CHCFGR2_OFFSET_Pos; + } + + /*Filter functions*/ + + void modify_sync_conversion(Sync_Conversion type) { + bool was_enabled_filter = is_enabled_filter(); + if (was_enabled_filter) + disable_filter(); + + filter_regs->FLTCR1 &= ~DFSDM_FLTCR1_RSYNC_Msk; + filter_regs->FLTCR1 |= (uint32_t(type) << DFSDM_FLTCR1_RSYNC_Pos); + + if (was_enabled_filter) + enable_filter(); + } + void modify_regular_mode(Regular_Mode mode) { + bool was_enabled_filter = is_enabled_filter(); + if (was_enabled_filter) + disable_filter(); + + filter_regs->FLTCR1 &= ~DFSDM_FLTCR1_RCONT_Msk; + filter_regs->FLTCR1 |= (uint32_t(mode) << DFSDM_FLTCR1_RCONT_Pos); + + if (was_enabled_filter) + enable_filter(); + } + + bool modify_oversampling(uint16_t oversampling) { + if (oversampling == 0) + return false; + + uint32_t ford = (filter_regs->FLTFCR & DFSDM_FLTFCR_FORD_Msk) >> DFSDM_FLTFCR_FORD_Pos; + + if (ford <= 3 && oversampling > Oversampling_MAX) + return false; + if (ford == 4 && oversampling > Oversampling_MAX_Filter_4) + return false; + if (ford == 5 && oversampling > Oversampling_MAX_Filter_5) + return false; + + bool was_enabled_filter = is_enabled_filter(); + if (was_enabled_filter) + disable_filter(); + + filter_regs->FLTFCR &= ~DFSDM_FLTFCR_FOSR_Msk; + filter_regs->FLTFCR |= ((uint32_t)(oversampling - 1) << DFSDM_FLTFCR_FOSR_Pos); + if (was_enabled_filter) + enable_filter(); + return true; + } + + bool modify_integrator(uint8_t integrator) { + + if (integrator == 0 || integrator > 256) + return false; + bool was_enabled_filter = is_enabled_filter(); + if (was_enabled_filter) + disable_filter(); + + filter_regs->FLTFCR &= ~DFSDM_FLTFCR_IOSR_Msk; + filter_regs->FLTFCR |= ((integrator - 1) << DFSDM_FLTFCR_IOSR_Pos); + + if (was_enabled_filter) + enable_filter(); + return true; + } + + bool modify_filter_order(Filter_Type type) { + + uint32_t fosr = + ((filter_regs->FLTFCR & DFSDM_FLTFCR_FOSR_Msk) >> DFSDM_FLTFCR_FOSR_Pos); + + if (type == Filter_Type::Sinc4 && fosr > Oversampling_MAX_Filter_4) + return false; + if (type == Filter_Type::Sinc5 && fosr > Oversampling_MAX_Filter_5) + return false; + + bool was_enabled_filter = is_enabled_filter(); + if (was_enabled_filter) + disable_filter(); + + filter_regs->FLTFCR &= ~DFSDM_FLTFCR_FORD_Msk; + filter_regs->FLTFCR |= (uint32_t(type) << DFSDM_FLTFCR_FORD_Pos); + + if (was_enabled_filter) + enable_filter(); + return true; + } + int32_t read(size_t pos) { + if (pos >= this->length_buffer) { + ErrorHandler("DFSDM: Trying to access to a memory section that is not from the " + "channel buffer"); + } + return ( + static_cast(this->buffer[pos] & DFSDM_FLTJDATAR_JDATA_Msk) >> + DFSDM_FLTJDATAR_JDATA_Pos + ); // The constants values are the same for regular than injected + } + int32_t read() { + return ( + static_cast(this->buffer[0] & DFSDM_FLTJDATAR_JDATA_Msk) >> + DFSDM_FLTJDATAR_JDATA_Pos + ); + } + uint32_t check_latency_cycles() { + return filter_regs->FLTCNVTIMR >> DFSDM_FLTCNVTIMR_CNVCNT_Pos; + } + + uint32_t check_min_extreme_detector() { + return filter_regs->FLTEXMIN >> DFSDM_FLTEXMIN_EXMIN_Pos; + } + + uint32_t check_max_extreme_detector() { + return filter_regs->FLTEXMAX >> DFSDM_FLTEXMAX_EXMAX_Pos; + } + void modify_watchdog_lth(uint32_t value) { + + filter_regs->FLTAWLTR &= ~DFSDM_FLTAWLTR_AWLT_Msk; + bool fast = (filter_regs->FLTCR1 & DFSDM_FLTCR1_AWFSEL); + + if (fast) + filter_regs->FLTAWLTR = + (value & 0xFFFF) + << (DFSDM_FLTAWLTR_AWLT_Pos + DFSDM_FLTAWLTR_AWLT_Pos); // Only 16 bits + else + filter_regs->FLTAWLTR = (value & 0xFFFFFF) << DFSDM_FLTAWLTR_AWLT_Pos; // 24 bits + } + + void modify_watchdog_hth(uint32_t value) { + filter_regs->FLTAWHTR &= ~DFSDM_FLTAWHTR_AWHT_Msk; + bool fast = (filter_regs->FLTCR1 & DFSDM_FLTCR1_AWFSEL); + + if (fast) + filter_regs->FLTAWHTR = (value & 0xFFFF) + << (DFSDM_FLTAWHTR_AWHT_Pos + DFSDM_FLTAWHTR_AWHT_Pos); + else + filter_regs->FLTAWHTR = (value & 0xFFFFFF) << DFSDM_FLTAWHTR_AWHT_Pos; + } + // get the last conversion from a filter + static uint8_t get_last_conversion_from_filter(uint8_t filter, Type_Conversion conv) { + uint8_t channel = 0xFF; + switch (filter) { + case 0: + if (conv == Type_Conversion::Injected) { + channel = (DFSDM1_Filter0->FLTJDATAR & 0x7); + } else { + channel = (DFSDM1_Filter0->FLTRDATAR & 0x7); + } + break; + case 1: + if (conv == Type_Conversion::Injected) { + channel = (DFSDM1_Filter1->FLTJDATAR & 0x7); + } else { + channel = (DFSDM1_Filter1->FLTRDATAR & 0x7); + } + break; + + case 2: + if (conv == Type_Conversion::Injected) { + channel = (DFSDM1_Filter2->FLTJDATAR & 0x7); + } else { + channel = (DFSDM1_Filter2->FLTRDATAR & 0x7); + } + break; + case 3: + if (conv == Type_Conversion::Injected) { + channel = (DFSDM1_Filter3->FLTJDATAR & 0x7); + } else { + channel = (DFSDM1_Filter3->FLTRDATAR & 0x7); + } + break; + default: + break; + } + return channel; + } + }; + __attribute__((section(".mpu_ram_d1_nc.buffer"))) alignas(32 + ) static inline int32_t DFSDM_Buffer_Pool[MAX_BUFFER_SIZE_TOTAL]; + static inline Instance* channel_instances[DFSDM_CHANNEL_DOMAIN::max_instances] = {nullptr}; + template cfgs> struct Init { + static constexpr auto sizes = calculate_buffer_sizes(cfgs); + // calculamos tamaño tanto para filtros con DMA como para los que no tienen + static constexpr std::size_t total_slots = + sizes.filter0 + sizes.filter1 + sizes.filter2 + sizes.filter3; + + // Filter Buffers + alignas(32) STLIB_DFSDM_DMA_BUFFER_ATTR + static inline int32_t Buffer_Filter0[sizes.filter0 > 0 ? sizes.filter0 : 1]{}; + alignas(32) STLIB_DFSDM_DMA_BUFFER_ATTR + static inline int32_t Buffer_Filter1[sizes.filter1 > 0 ? sizes.filter1 : 1]{}; + alignas(32) STLIB_DFSDM_DMA_BUFFER_ATTR + static inline int32_t Buffer_Filter2[sizes.filter2 > 0 ? sizes.filter2 : 1]{}; + alignas(32) STLIB_DFSDM_DMA_BUFFER_ATTR + static inline int32_t Buffer_Filter3[sizes.filter3 > 0 ? sizes.filter3 : 1]{}; + + static inline std::array instances{}; + + static constexpr std::size_t buffer_size_for(uint8_t filter) { + switch (filter) { + case 0: + return sizes.filter0; + case 1: + return sizes.filter1; + case 2: + return sizes.filter2; + case 3: + return sizes.filter3; + } + ErrorHandler("Filter cannot be bigger than 3"); + return 0; + } + static int32_t* get_buffer_filter(uint8_t filter) { + switch (filter) { + case 0: + return Buffer_Filter0; + case 1: + return Buffer_Filter1; + case 2: + return Buffer_Filter2; + case 3: + return Buffer_Filter3; + } + ErrorHandler("Filter cannot be bigger than 3"); + return 0; + } + + static int32_t* get_buffer_pointer(const Config cfg) { + return get_buffer_filter(cfg.filter) + cfg.buffer_pos_ini; + } + + static void init( + std::span gpio_instances, + std::span dma_instances + ) { + if (N == 0) + return; + std::array filters_configured = {false, false, false, false}; + RCC->APB2ENR |= RCC_APB2ENR_DFSDM1EN; // Activate the DFSDM clock + for (size_t i = 0; i < N; ++i) { + const Config& cfg = cfgs[i]; + filter_hw[cfg.filter]->FLTCR1 &= ~DFSDM_FLTCR1_DFEN; + channel_hw[cfg.channel]->CHCFGR1 &= ~DFSDM_CHCFGR1_CHEN; + } + for (std::size_t i = 0; i < N; ++i) { + const Config& cfg = cfgs[i]; + Instance& inst = instances[i]; + inst.gpio_instance = &gpio_instances[cfg.gpio_idx]; + if (cfg.dma_enable == Dma::Enable) { + inst.dma_instance = find_dma_instance(cfg.dma_request, dma_instances); + } else { + inst.dma_instance = nullptr; + } + + inst.filter_regs = filter_hw[cfg.filter]; + inst.channel_regs = channel_hw[cfg.channel]; + + inst.latency_cycles = cfg.latency_cycles; + inst.type_conv = cfg.type_conv; + inst.filter = cfg.filter; + inst.channel = cfg.channel; + inst.dma_enable = cfg.dma_enable; + + inst.length_buffer = cfg.buffer_size; + inst.buffer = get_buffer_pointer(cfg); + // callbacks + inst.overrun_cb = cfg.overrun_callback; + inst.short_circuit_cb = cfg.short_circuit_callback; + inst.watchdog_cb = cfg.watchdog_callback; + inst.end_conversion_cb = cfg.conversion_complete_callback; + + if (!filters_configured[cfg.filter]) { + // add everything to the register of the filter + inst.filter_regs->FLTCR1 |= cfg.init_data_filter.FLTCR1; + if (inst.type_conv == Type_Conversion::Regular) { + inst.filter_regs->FLTCR1 &= ~DFSDM_FLTCR1_RCH_Msk; + inst.filter_regs->FLTCR1 |= uint32_t(inst.channel) << DFSDM_FLTCR1_RCH_Pos; + } + inst.filter_regs->FLTCR2 |= cfg.init_data_filter.FLTCR2; + inst.filter_regs->FLTFCR |= cfg.init_data_filter.FLTFCR; + inst.filter_regs->FLTAWHTR |= cfg.init_data_filter.FLTAWHTR; + inst.filter_regs->FLTAWLTR |= cfg.init_data_filter.FLTAWLTR; + inst.filter_regs->FLTJCHGR = cfg.init_data_filter.FLTJCHGR; + + filters_configured[cfg.filter] = true; + + // add dma + if (cfg.dma_enable == Dma::Enable) { + uint32_t SrcAddress; + if (inst.type_conv == Type_Conversion::Regular) { + SrcAddress = (uint32_t)&filter_hw[inst.filter]->FLTRDATAR; + } else { + SrcAddress = (uint32_t)&filter_hw[inst.filter]->FLTJDATAR; + } + uint32_t DstAddress = + reinterpret_cast(get_buffer_filter(inst.filter) + ); // Transform the pointer to a value + inst.dma_instance + ->start(SrcAddress, DstAddress, buffer_size_for(inst.filter)); + } + } + // add everything to the channel register + inst.channel_regs->CHCFGR1 |= cfg.init_data_channel.CHCFGR1; + inst.channel_regs->CHCFGR2 |= cfg.init_data_channel.CHCFGR2; + inst.channel_regs->CHAWSCDR |= cfg.init_data_channel.CHAWSCDR; + + // update channel_instances + channel_instances[inst.channel] = &inst; + channels_enabled |= 1 << inst.channel; + } + if (N > 0) { + ; + // Activate the DFSDM GLOBAL Interface + DFSDM1_Channel0->CHCFGR1 |= DFSDM_CHCFGR1_DFSDMEN; + for (int i = 0; i < 8; i++) { + if (channels_enabled & (1 << i)) { + channel_hw[i]->CHCFGR1 |= DFSDM_CHCFGR1_CHEN; + } + } + for (int i = 0; i < 4; i++) { + if (filters_configured[i] == true) { + filter_hw[i]->FLTCR1 |= DFSDM_FLTCR1_DFEN; + } + } + // activate the NVIC + for (int i = 0; i < 4; i++) { + if (filters_configured[i] == true) { + switch (i) { + case 0: + NVIC_EnableIRQ(DFSDM1_FLT0_IRQn); + break; + case 1: + NVIC_EnableIRQ(DFSDM1_FLT1_IRQn); + break; + case 2: + NVIC_EnableIRQ(DFSDM1_FLT2_IRQn); + break; + case 3: + NVIC_EnableIRQ(DFSDM1_FLT3_IRQn); + break; + } + } + } + } + } + }; + static inline volatile size_t idx_filter[4] = {}; + static void handle_irq(uint8_t filter_index) { + + DFSDM_Filter_TypeDef* filter = filter_hw[filter_index]; + + uint32_t isr = filter->FLTISR; + + if (isr & DFSDM_FLTISR_REOCF_Msk) { + // Save it in the address provide by the user + int32_t data = filter->FLTRDATAR; + Instance* inst = channel_instances + [(data & DFSDM_FLTRDATAR_RDATACH_Msk) >> DFSDM_FLTRDATAR_RDATACH_Pos]; + if (inst != nullptr && inst->buffer != nullptr) { + if (inst->dma_enable == Dma::Disable) [[likely]] { + inst->buffer[idx_filter[inst->filter]] = data; + idx_filter[inst->filter] = (idx_filter[inst->filter] + 1) % inst->length_buffer; + } + if (inst->end_conversion_cb != nullptr) { + inst->end_conversion_cb(); + } + } + } + if (isr & DFSDM_FLTISR_JEOCF_Msk) { + // Save it in the address provide by the user + int32_t data = filter->FLTJDATAR; + uint8_t channel = (data & DFSDM_FLTJDATAR_JDATACH_Msk); + Instance* inst = channel_instances[channel]; + if (inst != nullptr && inst->buffer != nullptr) { + if (inst->dma_enable == Dma::Disable) [[likely]] { + inst->buffer[idx_filter[inst->filter]] = data; + idx_filter[inst->filter] = (idx_filter[inst->filter] + 1) % inst->length_buffer; + } + if (inst->end_conversion_cb != nullptr) { + inst->end_conversion_cb(); + } + } + } + if (isr & DFSDM_FLTISR_ROVRF_Msk) { + Instance* inst = channel_instances[filter->FLTRDATAR & DFSDM_FLTRDATAR_RDATACH_Msk]; + if (inst != nullptr && inst->overrun_cb != nullptr) + inst->overrun_cb(); + // clear + filter->FLTICR |= DFSDM_FLTICR_CLRROVRF_Msk; + } + if (isr & DFSDM_FLTISR_JOVRF_Msk) { + Instance* inst = channel_instances[filter->FLTJDATAR & DFSDM_FLTJDATAR_JDATACH_Msk]; + if (inst != nullptr && inst->overrun_cb != nullptr) + inst->overrun_cb(); + // clear + filter->FLTICR |= DFSDM_FLTICR_CLRJOVRF_Msk; + } + if (isr & (channels_enabled << DFSDM_FLTICR_CLRSCDF_Pos)) { + uint32_t ch = __builtin_ctz(isr & DFSDM_FLTISR_SCDF_Msk) >> DFSDM_FLTISR_SCDF_Pos; + if (channel_instances[ch] != nullptr && + channel_instances[ch]->short_circuit_cb != nullptr) + channel_instances[ch]->short_circuit_cb(); + // clear + filter->FLTICR |= DFSDM_FLTICR_CLRSCDF; + } + if (isr & (channels_enabled << DFSDM_FLTISR_CKABF_Pos)) { + uint32_t ch = __builtin_ctz(isr & DFSDM_FLTISR_CKABF_Msk) >> DFSDM_FLTISR_CKABF_Pos; + if (channel_instances[ch] != nullptr && + channel_instances[ch]->clock_absence_cb != nullptr) + channel_instances[ch]->clock_absence_cb(); + // clear + filter->FLTICR |= DFSDM_FLTICR_CLRCKABF; + } + // Analog watchdog + if (isr & (DFSDM_FLTISR_AWDF << DFSDM_FLTISR_AWDF_Pos)) { + if (filter->FLTAWSR & DFSDM_FLTAWSR_AWHTF_Msk) { + uint32_t ch = __builtin_ctz(filter->FLTAWSR & DFSDM_FLTAWSR_AWHTF_Msk); + if (channel_instances[ch] != nullptr && + channel_instances[ch]->watchdog_cb != nullptr) + channel_instances[ch]->watchdog_cb(); + // clear + filter->FLTAWCFR = DFSDM_FLTAWCFR_CLRAWHTF; + } + if (filter->FLTAWSR & DFSDM_FLTAWSR_AWLTF_Msk) { + uint32_t ch = __builtin_ctz(filter->FLTAWSR & DFSDM_FLTAWSR_AWLTF_Msk); + if (channel_instances[ch] != nullptr && + channel_instances[ch]->watchdog_cb != nullptr) + channel_instances[ch]->watchdog_cb(); + // clear + filter->FLTAWCFR = DFSDM_FLTAWCFR_CLRAWLTF; + } + } + } +}; +struct DFSDM_CLK_DOMAIN { + static constexpr GPIODomain::Pin valid_clk_pins[] = { + {GPIODomain::Port::C, GPIO_PIN_2}, + {GPIODomain::Port::B, GPIO_PIN_0}, + {GPIODomain::Port::E, GPIO_PIN_9}, + {GPIODomain::Port::D, GPIO_PIN_3}, + {GPIODomain::Port::D, GPIO_PIN_10} + }; + + static consteval bool is_valid_dfsdm_clk_pin(GPIODomain::Port port, uint32_t pin) { + bool found = false; + for (auto& p : valid_clk_pins) { + if (p.port == port && p.pin == pin) { + found = true; + break; + } + } + return found; + } + + static consteval GPIODomain::AlternateFunction dfsdm_clk_af(const GPIODomain::Pin& pin) { + if ((pin.port == GPIODomain::Port::C && pin.pin == GPIO_PIN_2) || + (pin.port == GPIODomain::Port::B && pin.pin == GPIO_PIN_0)) + return GPIODomain::AlternateFunction::AF6; + return GPIODomain::AlternateFunction::AF3; // In every other case + } + struct Entry { + size_t gpio_idx; + uint16_t clk_divider; + }; + + struct DFSDM_CLK { + using domain = DFSDM_CLK_DOMAIN; + GPIODomain::GPIO gpio; + GPIODomain::Pin pin; + uint8_t clk_divider; + consteval DFSDM_CLK(const GPIODomain::Pin& pin, uint8_t clk_divider = 100) + : // clk_divider = 100 -> 1Mhz + gpio{ + pin, + GPIODomain::OperationMode::ALT_PP, + GPIODomain::Pull::None, + GPIODomain::Speed::High, + dfsdm_clk_af(pin) + }, + pin(pin), clk_divider(clk_divider) {} + + template consteval std::size_t inscribe(Ctx& ctx) const { + const auto gpio_idx = gpio.inscribe(ctx); + if (!is_valid_dfsdm_clk_pin(pin.port, pin.pin)) { + compile_error("Invalid clk dfsdm pin used"); + } + if (clk_divider < 7 || clk_divider > 256) { + compile_error("The clk_divider has to be between 7 and 256"); + } + Entry e{.gpio_idx = gpio_idx, .clk_divider = clk_divider}; + return ctx.template add(e, this); + } + }; + static constexpr std::size_t max_instances{1}; + struct Config { + size_t gpio_idx; + uint16_t clk_divider; + }; + template + static consteval std::array build(std::span entries) { + std::array cfgs{}; + static_assert(N <= 1, "You can't have more than one clock_out"); + for (std::size_t i = 0; i < N; ++i) { + cfgs[i] = {.gpio_idx = entries[i].gpio_idx, .clk_divider = entries[i].clk_divider}; + } + return cfgs; + } + struct Instance { + GPIODomain::Instance* gpio_instance; + uint16_t clk_divider; + /*Already called in init()*/ + void init() { + RCC->APB2ENR |= RCC_APB2ENR_DFSDM1EN; // Activate the DFSDM Clock OUT in RCC by default + // it uses rcc_pclk2 + // Disable DFSDMEN to change parameters + DFSDM1_Channel0->CHCFGR1 &= ~DFSDM_CHCFGR1_DFSDMEN; + + // CKOUTSRC = 0 -> kernel clock (rcc_pclk2) It works 137,5 Mhz, + DFSDM1_Channel0->CHCFGR1 &= ~DFSDM_CHCFGR1_CKOUTSRC; + // CKOUT Divider. Divider = CKOUTDIV + 1 + DFSDM1_Channel0->CHCFGR1 &= ~DFSDM_CHCFGR1_CKOUTDIV; + + DFSDM1_Channel0->CHCFGR1 |= uint32_t(clk_divider - 1) << DFSDM_CHCFGR1_CKOUTDIV_Pos; + + // enable the DFSDM Global Interface + DFSDM1_Channel0->CHCFGR1 |= DFSDM_CHCFGR1_DFSDMEN; + } + bool disable() { + DFSDM1_Channel0->CHCFGR1 &= ~DFSDM_CHCFGR1_DFSDMEN; + return (DFSDM1_Channel0->CHCFGR1 & DFSDM_CHCFGR1_DFSDMEN) == 0; + } + bool enable() { + DFSDM1_Channel0->CHCFGR1 |= DFSDM_CHCFGR1_DFSDMEN; + return (DFSDM1_Channel0->CHCFGR1 & DFSDM_CHCFGR1_DFSDMEN); + } + bool change_divider(uint8_t div) { + if (div < 4) + return false; + clk_divider = div; + if (disable()) { + init(); + return true; + } + return false; + } + }; + template struct Init { + static inline std::array instances{}; + static void + init(std::span cfgs, std::span gpio_instances) { + if (N == 0) + return; + const auto& c = cfgs[0]; + auto& inst = instances[0]; + inst.gpio_instance = &gpio_instances[c.gpio_idx]; + inst.clk_divider = c.clk_divider; + inst.init(); + } + }; +}; + +}; // namespace ST_LIB diff --git a/Inc/MockedDrivers/stm32h7xx_hal_mock.h b/Inc/MockedDrivers/stm32h7xx_hal_mock.h index 5efcd616c..80ded8d38 100644 --- a/Inc/MockedDrivers/stm32h7xx_hal_mock.h +++ b/Inc/MockedDrivers/stm32h7xx_hal_mock.h @@ -421,6 +421,11 @@ struct __DMA_HandleTypeDef { #define DMA_REQUEST_FMAC_WRITE 0x016U #define DMA_REQUEST_FMAC_READ 0x017U +#define DMA_REQUEST_DFSDM1_FLT0 0x018U +#define DMA_REQUEST_DFSDM1_FLT1 0x019U +#define DMA_REQUEST_DFSDM1_FLT2 0x01AU +#define DMA_REQUEST_DFSDM1_FLT3 0x01BU + #define DMA_PERIPH_TO_MEMORY 0x000U #define DMA_MEMORY_TO_PERIPH 0x001U #define DMA_MEMORY_TO_MEMORY 0x002U @@ -720,12 +725,34 @@ typedef struct { #define TIM_ICSELECTION_DIRECTTI 0U #define TIM_ICPSC_DIV1 0U -#define TIM_TRGO_RESET 0U -#define TIM_TRGO2_RESET 0U #define TIM_MASTERSLAVEMODE_DISABLE 0U #define TIM_BREAK_ENABLE 1U #define TIM_LOCKLEVEL_OFF 0U +#define TIM_TRGO_RESET 0U +#define TIM_TRGO_ENABLE 0x00000010U +#define TIM_TRGO_UPDATE 0x00000020U +#define TIM_TRGO_OC1 0x00000030U +#define TIM_TRGO_OC1REF 0x00000040U +#define TIM_TRGO_OC2REF 0x00000050U +#define TIM_TRGO_OC3REF 0x00000060U +#define TIM_TRGO_OC4REF 0x00000070U + +#define TIM_TRGO2_RESET 0U +#define TIM_TRGO2_ENABLE 0x00000001U +#define TIM_TRGO2_UPDATE 0x00000002U +#define TIM_TRGO2_OC1 0x00000003U +#define TIM_TRGO2_OC1REF 0x00000004U +#define TIM_TRGO2_OC2REF 0x00000005U +#define TIM_TRGO2_OC3REF 0x00000006U +#define TIM_TRGO2_OC4REF 0x00000007U +#define TIM_TRGO2_OC5REF 0x00000008U +#define TIM_TRGO2_OC6REF 0x00000009U +#define TIM_TRGO2_OC4REF_RISING_OC6REF_FALLING 0x0000000AU +#define TIM_TRGO2_OC4REF_RISING_OC6REF_RISING 0x0000000BU +#define TIM_TRGO2_OC5REF_RISING_OC6REF_FALLING 0x0000000CU +#define TIM_TRGO2_OC5REF_RISING_OC6REF_RISING 0x0000000DU + #define TIM_CHANNEL_1 0x00000000U #define TIM_CHANNEL_2 0x00000004U #define TIM_CHANNEL_3 0x00000008U diff --git a/Inc/ST-LIB.hpp b/Inc/ST-LIB.hpp index 24c8c7a55..b2f75885c 100644 --- a/Inc/ST-LIB.hpp +++ b/Inc/ST-LIB.hpp @@ -96,7 +96,9 @@ using DomainsCtx = BuildCtx< SdDomain, EthernetDomain, ADCDomain, - EXTIDomain /* PWMDomain, ...*/>; + EXTIDomain, + DFSDM_CHANNEL_DOMAIN, + DFSDM_CLK_DOMAIN /* PWMDomain, ...*/>; namespace BuildUtils { @@ -166,14 +168,28 @@ template struct Board { ctx.template span(), std::span{adc_cfgs} ); + constexpr std::size_t dfsdmN = domain_size(); + constexpr auto dfsdm_cfgs = + DFSDM_CHANNEL_DOMAIN::template build(ctx.template span()); + constexpr std::size_t dfsdm_dma_extraN = DFSDM_CHANNEL_DOMAIN::dma_contribution_count( + std::span{dfsdm_cfgs}, + ctx.template span() + ); + constexpr auto dfsdm_dma_entries = + DFSDM_CHANNEL_DOMAIN::template build_dma_contributions( + ctx.template span(), + std::span{dfsdm_cfgs} + ); + constexpr std::size_t dfsdm_clkN = domain_size(); constexpr std::size_t spiN = domain_size(); constexpr std::size_t doutN = domain_size(); constexpr std::size_t dinN = domain_size(); constexpr std::size_t mdmaPacketN = domain_size(); constexpr std::size_t sdN = domain_size(); constexpr std::size_t ethN = domain_size(); - constexpr std::size_t dmaN = domain_size() + adc_dma_extraN; + constexpr std::size_t dmaN = domain_size() + adc_dma_extraN + dfsdm_dma_extraN; constexpr std::size_t extiN = domain_size(); + // ... struct ConfigBundle { @@ -189,6 +205,8 @@ template struct Board { std::array eth_cfgs; std::array adc_cfgs; std::array exti_cfgs; + std::array dfsdm_cfgs; + std::array dfsdm_clk_cfgs; // ... }; @@ -198,7 +216,8 @@ template struct Board { .tim_cfgs = TimerDomain::template build(ctx.template span()), .dma_cfgs = BuildUtils::build_dma_configs( ctx.template span(), - adc_dma_entries + adc_dma_entries, + dfsdm_dma_entries ), .spi_cfgs = SPIDomain::template build(ctx.template span()), .dout_cfgs = @@ -213,6 +232,9 @@ template struct Board { .eth_cfgs = EthernetDomain::template build(ctx.template span()), .adc_cfgs = adc_cfgs, .exti_cfgs = EXTIDomain::template build(ctx.template span()), + .dfsdm_cfgs = dfsdm_cfgs, + .dfsdm_clk_cfgs = + DFSDM_CLK_DOMAIN::template build(ctx.template span()) // ... }; } @@ -232,6 +254,8 @@ template struct Board { constexpr std::size_t ethN = domain_size(); constexpr std::size_t adcN = domain_size(); constexpr std::size_t extiN = domain_size(); + constexpr std::size_t dfsdmN = domain_size(); + constexpr std::size_t dfsdm_clkN = domain_size(); // ... #ifdef HAL_IWDG_MODULE_ENABLED @@ -271,8 +295,17 @@ template struct Board { GPIODomain::Init::instances, DMADomain::Init::instances ); - EXTIDomain::Init::init(cfg.exti_cfgs, - GPIODomain::Init::instances); // ... + EXTIDomain::Init::init(cfg.exti_cfgs, GPIODomain::Init::instances); + + DFSDM_CHANNEL_DOMAIN::Init::init( + GPIODomain::Init::instances, + DMADomain::Init::instances + ); + DFSDM_CLK_DOMAIN::Init::init( + cfg.dfsdm_clk_cfgs, + GPIODomain::Init::instances + ); + // ... } template @@ -299,6 +332,8 @@ template struct Board { return Domain::template Init::instances[idx]; } else if constexpr (std::is_same_v) { return Domain::template Init::instances[idx]; + } else if constexpr (std::is_same_v) { + return Domain::template Init::instances[idx]; } else { return Domain::template Init::instances[idx]; } diff --git a/Inc/ST-LIB_LOW/ST-LIB_LOW.hpp b/Inc/ST-LIB_LOW/ST-LIB_LOW.hpp index 80649b0b7..68c1f7163 100644 --- a/Inc/ST-LIB_LOW/ST-LIB_LOW.hpp +++ b/Inc/ST-LIB_LOW/ST-LIB_LOW.hpp @@ -2,6 +2,9 @@ #include "ST-LIB_LOW/DigitalOutput2.hpp" #include "ST-LIB_LOW/DigitalInput2.hpp" +#include "HALAL/Services/DFSDM/DFSDM.hpp" +// #include "Clocks/Counter.hpp" +// #include "Clocks/Stopwatch.hpp" #include "ST-LIB_LOW/Sd/Sd.hpp" diff --git a/Src/HALAL/Services/DFSDM/DFSDM.cpp b/Src/HALAL/Services/DFSDM/DFSDM.cpp new file mode 100644 index 000000000..eabbbd48a --- /dev/null +++ b/Src/HALAL/Services/DFSDM/DFSDM.cpp @@ -0,0 +1,11 @@ +#include "HALAL/Services/DFSDM/DFSDM.hpp" + +// ST_LIB::DFSDM_CHANNEL_DOMAIN::Init:: + +extern "C" { + +void DFSDM1_FLT0_IRQHandler(void) { ST_LIB::DFSDM_CHANNEL_DOMAIN::handle_irq(0); } +void DFSDM1_FLT1_IRQHandler(void) { ST_LIB::DFSDM_CHANNEL_DOMAIN::handle_irq(1); } +void DFSDM1_FLT2_IRQHandler(void) { ST_LIB::DFSDM_CHANNEL_DOMAIN::handle_irq(2); } +void DFSDM1_FLT3_IRQHandler(void) { ST_LIB::DFSDM_CHANNEL_DOMAIN::handle_irq(3); } +} diff --git a/toolchains/stm32.cmake b/toolchains/stm32.cmake index 95cc954a4..d3bf4609a 100644 --- a/toolchains/stm32.cmake +++ b/toolchains/stm32.cmake @@ -143,8 +143,8 @@ if(NOT STM32_CLT_ROOT) ) else() list(APPEND _stm32_clt_search_bases - "/opt/ST" - "$ENV{HOME}/ST" + "/opt/[Ss][Tt]" + "$ENV{HOME}/[Ss][Tt]" ) endif() @@ -162,8 +162,7 @@ if(NOT STM32_CLT_ROOT) ) file(GLOB _stm32_clt_globbed LIST_DIRECTORIES true - "${_base_dir}/STM32CubeCLT_${STM32_CLT_REQUIRED_VERSION}*" - "${_base_dir}/STM32CubeCLT-${STM32_CLT_REQUIRED_VERSION}*" + "${_base_dir}/[Ss][Tt][Mm]32[Cc][Uu][Bb][Ee][Cc][Ll][Tt][_-]${STM32_CLT_REQUIRED_VERSION}*" ) list(APPEND _stm32_clt_candidate_roots ${_stm32_clt_globbed}) endforeach()