diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bdd4e5f38..9988a9d13 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -54,13 +54,3 @@ jobs: cmake --preset ${{ inputs.preset }} cmake --build --preset ${{ inputs.preset }} shell: bash - - - name: Upload Build - id: upload-build-artifact - uses: actions/upload-artifact@v4 - with: - name: ${{ inputs.preset }} - path: out/build/${{ inputs.preset }}/libst-lib.a - retention-days: 7 - compression-level: 0 - if-no-files-found: error diff --git a/CMakeLists.txt b/CMakeLists.txt index ee361c9c8..1af94da3d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -243,6 +243,8 @@ set(HALAL_CPP_NO_ETH ${CMAKE_CURRENT_LIST_DIR}/Src/HALAL/Models/MDMA/MDMA.cpp ${CMAKE_CURRENT_LIST_DIR}/Src/HALAL/Models/MPUManager/MPUManager.cpp ${CMAKE_CURRENT_LIST_DIR}/Src/HALAL/Models/Packets/SPIOrder.cpp + ${CMAKE_CURRENT_LIST_DIR}/Src/HALAL/Models/SPI/SPI2.cpp + ${CMAKE_CURRENT_LIST_DIR}/Src/HALAL/Models/DMA/DMA2.cpp ${CMAKE_CURRENT_LIST_DIR}/Src/HALAL/Models/PinModel/Pin.cpp ${CMAKE_CURRENT_LIST_DIR}/Src/HALAL/Models/TimerDomain/TimerDomain.cpp ${CMAKE_CURRENT_LIST_DIR}/Src/HALAL/Models/TimerPeripheral/TimerPeripheral.cpp @@ -374,7 +376,7 @@ set(STLIB_HIGH_CPP_NO_ETH # Librería STLIB_LIBRARY # ============================ -add_library(${STLIB_LIBRARY} STATIC +add_library(${STLIB_LIBRARY} OBJECT $<$:${HAL_SOURCES_COMMON}> $<$,$>:${HAL_SOURCES_ETH}> $<$,$>:${LWIP_SOURCES}> diff --git a/Inc/HALAL/HALAL.hpp b/Inc/HALAL/HALAL.hpp index 70f7a33e4..42166c99b 100644 --- a/Inc/HALAL/HALAL.hpp +++ b/Inc/HALAL/HALAL.hpp @@ -3,7 +3,7 @@ #include "HALAL/Models/GPIO.hpp" #include "HALAL/Models/Pin.hpp" -#include "HALAL/Models/DMA/DMA.hpp" +#include "HALAL/Models/DMA/DMA2.hpp" #include "HALAL/Models/HALconfig/HALconfig.hpp" #include "HALAL/Services/DigitalInputService/DigitalInputService.hpp" @@ -35,6 +35,7 @@ #include "HALAL/Services/Communication/FDCAN/FDCAN.hpp" #include "HALAL/Services/Communication/I2C/I2C.hpp" +#include "HALAL/Models/SPI/SPI2.hpp" #include "HALAL/Services/Communication/SPI/SPI.hpp" #include "HALAL/Services/Communication/UART/UART.hpp" diff --git a/Inc/HALAL/HardFault/HardfaultTrace.h b/Inc/HALAL/HardFault/HardfaultTrace.h index 6fe45e4b0..669a51c3f 100644 --- a/Inc/HALAL/HardFault/HardfaultTrace.h +++ b/Inc/HALAL/HardFault/HardfaultTrace.h @@ -4,6 +4,7 @@ #include "stm32h7xx_ll_gpio.h" #include "stm32h7xx_ll_bus.h" #include "stm32h7xx_ll_tim.h" +#include #define METADATA_FLASH_ADDR (0x080DFD00) //Metadata pool flash address #define HF_FLASH_ADDR (0x080C0000U) //Hard_fault_flash address #define HF_FLAG_VALUE (0xFF00FF00U) //Flag to know if already is written information in the flash diff --git a/Inc/HALAL/Models/DMA/DMA.hpp b/Inc/HALAL/Models/DMA/DMA.hpp index 74d7864aa..a9799cbb9 100644 --- a/Inc/HALAL/Models/DMA/DMA.hpp +++ b/Inc/HALAL/Models/DMA/DMA.hpp @@ -39,4 +39,4 @@ class DMA { private: static vector available_streams; static vector inscribed_streams; -}; +}; \ No newline at end of file diff --git a/Inc/HALAL/Models/DMA/DMA2.hpp b/Inc/HALAL/Models/DMA/DMA2.hpp new file mode 100644 index 000000000..3b33c2c30 --- /dev/null +++ b/Inc/HALAL/Models/DMA/DMA2.hpp @@ -0,0 +1,401 @@ +#pragma once +#include "C++Utilities/CppUtils.hpp" +#include "stm32h7xx_hal.h" +#include "main.h" +#include "HALAL/Models/MPUManager/MPUManager.hpp" +#include +#include +#include +#include +#include + +using std::array; +using std::size_t; +using std::span; +using std::tuple; + +#define MAX_STREAMS 16 + + +extern "C" { + inline DMA_HandleTypeDef *dma_irq_table[16] = {nullptr}; +} + + +namespace ST_LIB { + extern void compile_error(const char *msg); + struct DMA_Domain { + + enum class Peripheral : uint8_t {none, adc1, adc2, adc3, + i2c1, i2c2, i2c3, i2c5, + spi1, spi2, spi3, spi4, spi5, spi6, + fmac}; + + enum class Stream : uint8_t {none, dma1_stream0, dma1_stream1, dma1_stream2, dma1_stream3, + dma1_stream4, dma1_stream5, dma1_stream6, dma1_stream7, + dma2_stream0, dma2_stream1, dma2_stream2, dma2_stream3, + dma2_stream4, dma2_stream5, dma2_stream6, dma2_stream7}; + + + static inline DMA_Stream_TypeDef* stream_to_DMA_StreamTypeDef(Stream s) { + switch (s) { + case Stream::dma1_stream0: return DMA1_Stream0; + case Stream::dma1_stream1: return DMA1_Stream1; + case Stream::dma1_stream2: return DMA1_Stream2; + case Stream::dma1_stream3: return DMA1_Stream3; + case Stream::dma1_stream4: return DMA1_Stream4; + case Stream::dma1_stream5: return DMA1_Stream5; + case Stream::dma1_stream6: return DMA1_Stream6; + case Stream::dma1_stream7: return DMA1_Stream7; + + case Stream::dma2_stream0: return DMA2_Stream0; + case Stream::dma2_stream1: return DMA2_Stream1; + case Stream::dma2_stream2: return DMA2_Stream2; + case Stream::dma2_stream3: return DMA2_Stream3; + case Stream::dma2_stream4: return DMA2_Stream4; + case Stream::dma2_stream5: return DMA2_Stream5; + case Stream::dma2_stream6: return DMA2_Stream6; + case Stream::dma2_stream7: return DMA2_Stream7; + case Stream::none: return nullptr; + } + return nullptr; + } + + struct Entry { + Peripheral instance; + Stream stream; + IRQn_Type irqn; + uint8_t id; + }; + + template + struct DMA { + using domain = DMA_Domain; + + std::array e{}; + + + consteval DMA(Peripheral instance) { + static_assert(sizeof...(Ss) <= 3, "Máximo 3 streams"); + + Stream streams[] = { Ss... }; + constexpr uint8_t n = sizeof...(Ss); + + for (uint8_t j = 0; j < n; j++) { + e[j].instance = instance; + e[j].stream = streams[j]; + if (streams[j] != Stream::none) { + e[j].irqn = get_irqn(streams[j]); + } else { + e[j].irqn = (IRQn_Type)0; // Dummy value + } + e[j].id = j; + } + + } + + template + consteval array inscribe(Ctx &ctx) const { + array indices{}; + for (size_t i = 0; i < sizeof...(Ss); i++) { + indices[i] = ctx.template add(e[i], this); + } + return indices; + } + }; + + static constexpr std::size_t max_instances {MAX_STREAMS}; + static_assert(max_instances > 0, "The number of instances must be greater than 0"); + + static inline constexpr IRQn_Type get_irqn(Stream stream) { + if (stream == Stream::dma1_stream0) return DMA1_Stream0_IRQn; + else if (stream == Stream::dma1_stream1) return DMA1_Stream1_IRQn; + else if (stream == Stream::dma1_stream2) return DMA1_Stream2_IRQn; + else if (stream == Stream::dma1_stream3) return DMA1_Stream3_IRQn; + else if (stream == Stream::dma1_stream4) return DMA1_Stream4_IRQn; + else if (stream == Stream::dma1_stream5) return DMA1_Stream5_IRQn; + else if (stream == Stream::dma1_stream6) return DMA1_Stream6_IRQn; + else if (stream == Stream::dma1_stream7) return DMA1_Stream7_IRQn; + + else if (stream == Stream::dma2_stream0) return DMA2_Stream0_IRQn; + else if (stream == Stream::dma2_stream1) return DMA2_Stream1_IRQn; + else if (stream == Stream::dma2_stream2) return DMA2_Stream2_IRQn; + else if (stream == Stream::dma2_stream3) return DMA2_Stream3_IRQn; + else if (stream == Stream::dma2_stream4) return DMA2_Stream4_IRQn; + else if (stream == Stream::dma2_stream5) return DMA2_Stream5_IRQn; + else if (stream == Stream::dma2_stream6) return DMA2_Stream6_IRQn; + else if (stream == Stream::dma2_stream7) return DMA2_Stream7_IRQn; + else if (stream == Stream::none) return (IRQn_Type)0; + else compile_error("No tiene que llegar aqui nunca, creo"); + } + + static constexpr inline bool is_one_of(Peripheral instance, auto... bases) { + return ((instance == bases) || ...); + } + + static constexpr inline bool is_spi(Peripheral instance) { + return is_one_of(instance, Peripheral::spi1, Peripheral::spi2, + Peripheral::spi3, Peripheral::spi4, Peripheral::spi5, Peripheral::spi6); + } + + static constexpr inline bool is_i2c(Peripheral instance) { + return is_one_of(instance, Peripheral::i2c1, Peripheral::i2c2, + Peripheral::i2c3, Peripheral::i2c5); + } + + static constexpr inline bool is_adc(Peripheral instance) { + return is_one_of(instance, Peripheral::adc1, Peripheral::adc2, Peripheral::adc3); + } + + static constexpr inline bool is_fmac(Peripheral instance) { + return instance == Peripheral::fmac; + } + + static constexpr inline bool is_none(Peripheral instance){ + return instance == Peripheral::none; + } + + static consteval inline uint32_t get_Request(Peripheral instance, uint8_t i) { + if (instance == Peripheral::none) return DMA_REQUEST_MEM2MEM; + + if (instance == Peripheral::adc1) return DMA_REQUEST_ADC1; + if (instance == Peripheral::adc2) return DMA_REQUEST_ADC2; + if (instance == Peripheral::adc3) return DMA_REQUEST_ADC3; + + if (instance == Peripheral::i2c1 && i == 0) return DMA_REQUEST_I2C1_RX; + if (instance == Peripheral::i2c1 && i == 1) return DMA_REQUEST_I2C1_TX; + if (instance == Peripheral::i2c2 && i == 0) return DMA_REQUEST_I2C2_RX; + if (instance == Peripheral::i2c2 && i == 1) return DMA_REQUEST_I2C2_TX; + if (instance == Peripheral::i2c3 && i == 0) return DMA_REQUEST_I2C3_RX; + if (instance == Peripheral::i2c3 && i == 1) return DMA_REQUEST_I2C3_TX; + if (instance == Peripheral::i2c5 && i == 0) return DMA_REQUEST_I2C5_RX; + if (instance == Peripheral::i2c5 && i == 1) return DMA_REQUEST_I2C5_TX; + + if (instance == Peripheral::spi1 && i == 0) return DMA_REQUEST_SPI1_RX; + if (instance == Peripheral::spi1 && i == 1) return DMA_REQUEST_SPI1_TX; + if (instance == Peripheral::spi2 && i == 0) return DMA_REQUEST_SPI2_RX; + if (instance == Peripheral::spi2 && i == 1) return DMA_REQUEST_SPI2_TX; + if (instance == Peripheral::spi3 && i == 0) return DMA_REQUEST_SPI3_RX; + if (instance == Peripheral::spi3 && i == 1) return DMA_REQUEST_SPI3_TX; + if (instance == Peripheral::spi4 && i == 0) return DMA_REQUEST_SPI4_RX; + if (instance == Peripheral::spi4 && i == 1) return DMA_REQUEST_SPI4_TX; + if (instance == Peripheral::spi5 && i == 0) return DMA_REQUEST_SPI5_RX; + if (instance == Peripheral::spi5 && i == 1) return DMA_REQUEST_SPI5_TX; + + if (instance == Peripheral::fmac && i == 0) return DMA_REQUEST_MEM2MEM; + if (instance == Peripheral::fmac && i == 1) return DMA_REQUEST_FMAC_WRITE; + if (instance == Peripheral::fmac && i == 2) return DMA_REQUEST_FMAC_READ; + + compile_error("Invalid DMA request configuration"); + return 0; + } + + static consteval inline uint32_t get_Direction(Peripheral instance, uint8_t i) { + if ((is_fmac(instance) && i == 0) || instance == Peripheral::none) { + return DMA_MEMORY_TO_MEMORY; + } + else if ((is_i2c(instance) && i == 1) || + (is_spi(instance) && i == 1) || + (is_fmac(instance) && i == 1)){ + return DMA_MEMORY_TO_PERIPH; + } + return DMA_PERIPH_TO_MEMORY; + } + + static consteval inline uint32_t get_PeriphInc(Peripheral instance, uint8_t i) { + if ((is_fmac(instance) && i == 0) || is_none(instance)){ + return DMA_PINC_ENABLE; + } + return DMA_PINC_DISABLE; + } + + static consteval inline uint32_t get_MemInc(Peripheral instance, uint8_t i) { + if (is_fmac(instance) && i == 0){ + return DMA_MINC_DISABLE; + } + return DMA_MINC_ENABLE; + } + + static consteval inline 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)){ + return DMA_PDATAALIGN_WORD; + } + return DMA_PDATAALIGN_HALFWORD; + } + + static consteval inline uint32_t get_MemDataAlignment(Peripheral instance, uint8_t i) { + if (is_i2c(instance)){ + return DMA_MDATAALIGN_WORD; + } + else if (is_spi(instance)){ + return DMA_MDATAALIGN_BYTE; + } + + return DMA_MDATAALIGN_HALFWORD; + } + + static consteval inline uint32_t get_Mode(Peripheral instance, uint8_t i) { + if (is_spi(instance) || is_fmac(instance) || is_none(instance)){ + return DMA_NORMAL; + } + + return DMA_CIRCULAR; + } + + static consteval inline uint32_t get_Priority(Peripheral instance, uint8_t i) { + if (is_fmac(instance)){ + return DMA_PRIORITY_HIGH; + } + + return DMA_PRIORITY_LOW; + } + + static consteval inline uint32_t get_FIFOMode(Peripheral instance, uint8_t i) { + if (is_fmac(instance)){ + return DMA_FIFOMODE_ENABLE; + } + return DMA_FIFOMODE_DISABLE; + } + + static consteval inline uint32_t get_FIFOThreshold(Peripheral instance, uint8_t i) { + if (is_spi(instance)){ + return DMA_FIFO_THRESHOLD_FULL; + } + return DMA_FIFO_THRESHOLD_HALFFULL; + } + + static consteval inline uint32_t get_MemBurst(Peripheral instance, uint8_t i) { + return DMA_MBURST_SINGLE; + } + + static consteval inline uint32_t get_PeriphBurst(Peripheral instance, uint8_t i) { + return DMA_PBURST_SINGLE; + } + + struct Config { + std::tuple + init_data{}; + }; + + template + static consteval std::array build(span instances) { + std::array cfgs{}; + std::array ents; + for(size_t i=0; i used_streams{}; // Defaults to false + + // First pass: process user-specified streams + for (std::size_t i = 0; i < N; ++i) { + const auto &e = ents[i]; + if (e.stream != Stream::none) { + uint8_t stream_idx = static_cast(e.stream) - 1; + if (used_streams[stream_idx]) { + compile_error("DMA stream already in use"); + } + used_streams[stream_idx] = true; + } + } + + // Second pass: assign streams for entries with Stream::none + for (std::size_t i = 0; i < N; ++i) { + auto &e = ents[i]; + if (e.stream == Stream::none) { + bool assigned = false; + for (uint8_t j = 0; j < MAX_STREAMS; ++j) { + if (!used_streams[j]) { + e.stream = static_cast(j + 1); + e.irqn = get_irqn(e.stream); + used_streams[j] = true; + assigned = true; + break; + } + } + if (!assigned) { + compile_error("Not enough DMA streams available"); + } + } + } + + + for (std::size_t i = 0; i < N; ++i){ + const auto &e = ents[i]; + + for (std::size_t j = 0; j < i; ++j){ + const auto &prev = ents[j]; + if (prev.stream == e.stream){ + compile_error("DMA stream already in use"); + } + } + + DMA_InitTypeDef DMA_InitStruct; + DMA_InitStruct.Request = get_Request(e.instance, e.id); + DMA_InitStruct.Direction = get_Direction(e.instance, e.id); + DMA_InitStruct.PeriphInc = get_PeriphInc(e.instance, e.id); + DMA_InitStruct.MemInc = get_MemInc(e.instance, e.id); + DMA_InitStruct.PeriphDataAlignment = get_PeriphDataAlignment(e.instance, e.id); + DMA_InitStruct.MemDataAlignment = get_MemDataAlignment(e.instance, e.id); + DMA_InitStruct.Mode = get_Mode(e.instance, e.id); + DMA_InitStruct.Priority = get_Priority(e.instance, e.id); + DMA_InitStruct.FIFOMode = get_FIFOMode(e.instance, e.id); + DMA_InitStruct.FIFOThreshold = get_FIFOThreshold(e.instance, e.id); + DMA_InitStruct.MemBurst = get_MemBurst(e.instance, e.id); + DMA_InitStruct.PeriphBurst = get_PeriphBurst(e.instance, e.id); + + + cfgs[i].init_data = std::make_tuple(e.instance, + DMA_InitStruct, + e.stream, + e.irqn, + e.id); + } + return cfgs; + } + + + + struct Instance { + DMA_HandleTypeDef dma; + + void start(uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength){ + HAL_DMA_Start_IT(&dma, SrcAddress, DstAddress, DataLength); + } + }; + + + template struct Init { + static inline std::array instances{}; + + static void init(std::span cfgs) { + if (N == 0) return; + __HAL_RCC_DMA1_CLK_ENABLE(); + __HAL_RCC_DMA2_CLK_ENABLE(); + for (std::size_t i = 0; i < N; ++i) { + const auto &e = cfgs[i]; + auto [instance, dma_init, stream, irqn, id] = e.init_data; + + instances[i].dma = {}; + instances[i].dma.Instance = stream_to_DMA_StreamTypeDef(stream); + instances[i].dma.Init = dma_init; + + if (HAL_DMA_Init(&instances[i].dma) != HAL_OK) { + ErrorHandler("DMA Init failed"); + } + else{ + HAL_NVIC_SetPriority(irqn, 0, 0); + HAL_NVIC_EnableIRQ(irqn); + dma_irq_table[static_cast(stream) - 1] = &instances[i].dma; + } + } + } + }; + }; +} + diff --git a/Inc/HALAL/Models/Pin.hpp b/Inc/HALAL/Models/Pin.hpp index 089acb41c..f4881abe3 100644 --- a/Inc/HALAL/Models/Pin.hpp +++ b/Inc/HALAL/Models/Pin.hpp @@ -22,7 +22,7 @@ constexpr GPIODomain::Pin PA11{A, GPIO_PIN_11, 0b0101111110010011}; constexpr GPIODomain::Pin PA12{A, GPIO_PIN_12, 0b0011111110010111}; constexpr GPIODomain::Pin PA13{A, GPIO_PIN_13, 0b1000000000000001}; constexpr GPIODomain::Pin PA14{A, GPIO_PIN_14, 0b1000000000000001}; -constexpr GPIODomain::Pin PA15{A, GPIO_PIN_15, 0b0111010111111011}; +constexpr GPIODomain::Pin PA15{A, GPIO_PIN_15, 0b1100111111010011}; // Port B constexpr GPIODomain::Pin PB0{B, GPIO_PIN_0, 0b0111110011111011}; diff --git a/Inc/HALAL/Models/SPI/SPI2.hpp b/Inc/HALAL/Models/SPI/SPI2.hpp new file mode 100644 index 000000000..dbcd23507 --- /dev/null +++ b/Inc/HALAL/Models/SPI/SPI2.hpp @@ -0,0 +1,1134 @@ +/* + * SPI2.hpp + * + * Created on: 27 dec. 2025 + * Author: Boris + */ + +#ifndef SPI2_HPP +#define SPI2_HPP + +#include "C++Utilities/CppImports.hpp" +#include "HALAL/Models/GPIO.hpp" +#include "HALAL/Models/Pin.hpp" +#include "ErrorHandler/ErrorHandler.hpp" +#include "HALAL/Models/DMA/DMA2.hpp" +#include "HALAL/Models/SPI/SPIConfig.hpp" + +using ST_LIB::GPIODomain; +using ST_LIB::DMA_Domain; +using ST_LIB::SPIConfigTypes; + +// Forward declaration of IRQ handlers and HAL callbacks +extern "C" { + void SPI1_IRQHandler(void); + void SPI2_IRQHandler(void); + void SPI3_IRQHandler(void); + void SPI4_IRQHandler(void); + void SPI5_IRQHandler(void); + void SPI6_IRQHandler(void); + + void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi); + void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi); + void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi); + void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi); +} + +namespace ST_LIB { +extern void compile_error(const char *msg); + +struct SPIDomain { + +/** + * ========================================= + * Internal working things + * ========================================= + */ + + // Configuration types + using ClockPolarity = SPIConfigTypes::ClockPolarity; + using ClockPhase = SPIConfigTypes::ClockPhase; + using BitOrder = SPIConfigTypes::BitOrder; + using NSSMode = SPIConfigTypes::NSSMode; + using DataSize = SPIConfigTypes::DataSize; + using Direction = SPIConfigTypes::Direction; + using FIFOThreshold = SPIConfigTypes::FIFOThreshold; + using NSSPolarity = SPIConfigTypes::NSSPolarity; + using CRCLength = SPIConfigTypes::CRCLength; + using SPIConfig = SPIConfigTypes::SPIConfig; + + enum class SPIPeripheral : uintptr_t { + spi1 = SPI1_BASE, + spi2 = SPI2_BASE, + spi3 = SPI3_BASE, + spi4 = SPI4_BASE, + spi5 = SPI5_BASE, + spi6 = SPI6_BASE, + }; + + enum class SPIMode : bool { + MASTER = true, + SLAVE = false, + }; + + static consteval bool compare_pin(const GPIODomain::Pin &p1, const GPIODomain::Pin &p2) { + return (p1.port == p2.port) && (p1.pin == p2.pin); + } + + static consteval GPIODomain::AlternateFunction get_af(const GPIODomain::Pin &pin, SPIPeripheral peripheral) { + + if (peripheral == SPIPeripheral::spi2) { + if (compare_pin(pin, PB4)) return GPIODomain::AlternateFunction::AF7; + } + if (peripheral == SPIPeripheral::spi3) { + if (compare_pin(pin, PA4)) return GPIODomain::AlternateFunction::AF6; + if (compare_pin(pin, PA15)) return GPIODomain::AlternateFunction::AF6; + if (compare_pin(pin, PB2)) return GPIODomain::AlternateFunction::AF7; + if (compare_pin(pin, PB3)) return GPIODomain::AlternateFunction::AF6; + if (compare_pin(pin, PB4)) return GPIODomain::AlternateFunction::AF6; + if (compare_pin(pin, PB5)) return GPIODomain::AlternateFunction::AF7; + if (compare_pin(pin, PC10)) return GPIODomain::AlternateFunction::AF6; + if (compare_pin(pin, PC11)) return GPIODomain::AlternateFunction::AF6; + if (compare_pin(pin, PC12)) return GPIODomain::AlternateFunction::AF6; + } + if (peripheral == SPIPeripheral::spi6) { + if (compare_pin(pin, PA4)) return GPIODomain::AlternateFunction::AF8; + if (compare_pin(pin, PA5)) return GPIODomain::AlternateFunction::AF8; + if (compare_pin(pin, PA6)) return GPIODomain::AlternateFunction::AF8; + if (compare_pin(pin, PA7)) return GPIODomain::AlternateFunction::AF8; + if (compare_pin(pin, PA15)) return GPIODomain::AlternateFunction::AF7; + if (compare_pin(pin, PB3)) return GPIODomain::AlternateFunction::AF8; + if (compare_pin(pin, PB4)) return GPIODomain::AlternateFunction::AF8; + if (compare_pin(pin, PB5)) return GPIODomain::AlternateFunction::AF8; + } + + return GPIODomain::AlternateFunction::AF5; // Default AF for everything else + } + + static constexpr uint32_t get_prescaler_flag(uint32_t prescaler) { + switch (prescaler) { + case 2: return SPI_BAUDRATEPRESCALER_2; + case 4: return SPI_BAUDRATEPRESCALER_4; + case 8: return SPI_BAUDRATEPRESCALER_8; + case 16: return SPI_BAUDRATEPRESCALER_16; + case 32: return SPI_BAUDRATEPRESCALER_32; + case 64: return SPI_BAUDRATEPRESCALER_64; + case 128: return SPI_BAUDRATEPRESCALER_128; + case 256: return SPI_BAUDRATEPRESCALER_256; + default: + if consteval { + compile_error("Invalid prescaler value"); + } else { + ErrorHandler("Invalid prescaler value"); + return SPI_BAUDRATEPRESCALER_256; + } + } + } + + // Forward declaration + static uint32_t calculate_prescaler(uint32_t src_freq, uint32_t max_baud); + + static constexpr std::size_t max_instances{6}; + + struct Entry { + SPIPeripheral peripheral; + SPIMode mode; + + std::size_t sck_gpio_idx; + std::size_t miso_gpio_idx; + std::size_t mosi_gpio_idx; + std::optional nss_gpio_idx; + + std::size_t dma_rx_idx; + std::size_t dma_tx_idx; + + uint32_t max_baudrate; // Will set the baudrate as fast as possible under this value + SPIConfig config; // User-defined SPI configuration + }; + + struct Config { + SPIPeripheral peripheral; + SPIMode mode; + + std::size_t sck_gpio_idx; + std::size_t miso_gpio_idx; + std::size_t mosi_gpio_idx; + std::optional nss_gpio_idx; + + std::size_t dma_rx_idx; + std::size_t dma_tx_idx; + + uint32_t max_baudrate; // Will set the baudrate as fast as possible under this value + SPIConfig config; // User-defined SPI configuration + }; + + +/** + * ========================================= + * Request Object + * ========================================= + */ + template + struct Device { + using domain = SPIDomain; + + SPIPeripheral peripheral; + SPIMode mode; + uint32_t max_baudrate; // Will set the baudrate as fast as possible under this value + SPIConfig config; // User-defined SPI configuration + + GPIODomain::GPIO sck_gpio; + GPIODomain::GPIO miso_gpio; + GPIODomain::GPIO mosi_gpio; + std::optional nss_gpio; + + DMA_Domain::DMA dma_rx_tx; + + consteval Device(SPIMode mode, SPIPeripheral peripheral, uint32_t max_baudrate, + GPIODomain::Pin sck_pin, GPIODomain::Pin miso_pin, + GPIODomain::Pin mosi_pin, GPIODomain::Pin nss_pin, + SPIConfig config = SPIConfig{}) + : peripheral{peripheral}, mode{mode}, max_baudrate{max_baudrate}, + config{config}, + sck_gpio(sck_pin, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, get_af(sck_pin, peripheral)), + miso_gpio(miso_pin, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, get_af(miso_pin, peripheral)), + mosi_gpio(mosi_pin, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, get_af(mosi_pin, peripheral)), + nss_gpio(GPIODomain::GPIO(nss_pin, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, get_af(nss_pin, peripheral))), + dma_rx_tx(dma_peripheral(peripheral)) + { + config.validate(); + + if (config.nss_mode == NSSMode::SOFTWARE) { + compile_error("Use NSSMode::SOFTWARE, and omit NSS pin for software NSS management, it is handled externally"); + } + + validate_nss_pin(peripheral, nss_pin); + + validate_spi_pins(peripheral, sck_pin, miso_pin, mosi_pin); + } + + // Constructor without NSS pin (for software NSS mode) + consteval Device(SPIMode mode, SPIPeripheral peripheral, uint32_t max_baudrate, + GPIODomain::Pin sck_pin, GPIODomain::Pin miso_pin, + GPIODomain::Pin mosi_pin, + SPIConfig config) + : peripheral{peripheral}, mode{mode}, max_baudrate{max_baudrate}, + config{config}, + sck_gpio(sck_pin, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, get_af(sck_pin, peripheral)), + miso_gpio(miso_pin, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, get_af(miso_pin, peripheral)), + mosi_gpio(mosi_pin, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, get_af(mosi_pin, peripheral)), + nss_gpio(std::nullopt), // No NSS GPIO + dma_rx_tx(dma_peripheral(peripheral)) + { + config.validate(); + + if (config.nss_mode == NSSMode::HARDWARE) { + compile_error("NSS pin must be provided for hardware NSS mode, or use NSSMode::SOFTWARE"); + } + + validate_spi_pins(peripheral, sck_pin, miso_pin, mosi_pin); + } + + template + consteval std::size_t inscribe(Ctx &ctx) const { + auto dma_indices = dma_rx_tx.inscribe(ctx); + + // Conditionally add NSS GPIO if provided + std::optional nss_idx = std::nullopt; + if (nss_gpio.has_value()) { + nss_idx = nss_gpio.value().inscribe(ctx); + } + + Entry e{ + .peripheral = peripheral, + .mode = mode, + .sck_gpio_idx = sck_gpio.inscribe(ctx), + .miso_gpio_idx = miso_gpio.inscribe(ctx), + .mosi_gpio_idx = mosi_gpio.inscribe(ctx), + .nss_gpio_idx = nss_idx, + .dma_rx_idx = dma_indices[0], + .dma_tx_idx = dma_indices[1], + .max_baudrate = max_baudrate, + .config = config + }; + + return ctx.template add(e, this); + } + + private: + // Helper function to validate SPI pins (SCK, MISO, MOSI only) + static consteval void validate_spi_pins(SPIPeripheral peripheral, + GPIODomain::Pin sck_pin, + GPIODomain::Pin miso_pin, + GPIODomain::Pin mosi_pin) { + switch (peripheral) { + case SPIPeripheral::spi1: + if (!compare_pin(sck_pin, PB3) && + !compare_pin(sck_pin, PG11) && + !compare_pin(sck_pin, PA5)) { + compile_error("Invalid SCK pin for SPI1"); + } + if (!compare_pin(miso_pin, PB4) && + !compare_pin(miso_pin, PG9) && + !compare_pin(miso_pin, PA6)) { + compile_error("Invalid MISO pin for SPI1"); + } + if (!compare_pin(mosi_pin, PB5) && + !compare_pin(mosi_pin, PD7) && + !compare_pin(mosi_pin, PA7)) { + compile_error("Invalid MOSI pin for SPI1"); + } + break; + + case SPIPeripheral::spi2: + if (!compare_pin(sck_pin, PD3) && + !compare_pin(sck_pin, PA12) && + !compare_pin(sck_pin, PA9) && + !compare_pin(sck_pin, PB13) && + !compare_pin(sck_pin, PB10)) { + compile_error("Invalid SCK pin for SPI2"); + } + if (!compare_pin(miso_pin, PC2) && + !compare_pin(miso_pin, PB14)) { + compile_error("Invalid MISO pin for SPI2"); + } + if (!compare_pin(mosi_pin, PC3) && + !compare_pin(mosi_pin, PC1) && + !compare_pin(mosi_pin, PB15)) { + compile_error("Invalid MOSI pin for SPI2"); + } + break; + + case SPIPeripheral::spi3: + if (!compare_pin(sck_pin, PB3) && + !compare_pin(sck_pin, PC10)) { + compile_error("Invalid SCK pin for SPI3"); + } + if (!compare_pin(miso_pin, PB4) && + !compare_pin(miso_pin, PC11)) { + compile_error("Invalid MISO pin for SPI3"); + } + if (!compare_pin(mosi_pin, PB5) && + !compare_pin(mosi_pin, PD6) && + !compare_pin(mosi_pin, PC12) && + !compare_pin(mosi_pin, PB2)) { + compile_error("Invalid MOSI pin for SPI3"); + } + break; + + case SPIPeripheral::spi4: + if (!compare_pin(sck_pin, PE2) && + !compare_pin(sck_pin, PE12)) { + compile_error("Invalid SCK pin for SPI4"); + } + if (!compare_pin(miso_pin, PE5) && + !compare_pin(miso_pin, PE13)) { + compile_error("Invalid MISO pin for SPI4"); + } + if (!compare_pin(mosi_pin, PE6) && + !compare_pin(mosi_pin, PE14)) { + compile_error("Invalid MOSI pin for SPI4"); + } + break; + + case SPIPeripheral::spi5: + if (!compare_pin(sck_pin, PF7)) { + compile_error("Invalid SCK pin for SPI5"); + } + if (!compare_pin(miso_pin, PF8)) { + compile_error("Invalid MISO pin for SPI5"); + } + if (!compare_pin(mosi_pin, PF9) && + !compare_pin(mosi_pin, PF11)) { + compile_error("Invalid MOSI pin for SPI5"); + } + break; + + case SPIPeripheral::spi6: + if (!compare_pin(sck_pin, PB3) && + !compare_pin(sck_pin, PG13) && + !compare_pin(sck_pin, PC10) && + !compare_pin(sck_pin, PA7)) { + compile_error("Invalid SCK pin for SPI6"); + } + if (!compare_pin(miso_pin, PB4) && + !compare_pin(miso_pin, PG12) && + !compare_pin(miso_pin, PA6)) { + compile_error("Invalid MISO pin for SPI6"); + } + if (!compare_pin(mosi_pin, PB5) && + !compare_pin(mosi_pin, PG14) && + !compare_pin(mosi_pin, PA7)) { + compile_error("Invalid MOSI pin for SPI6"); + } + break; + + default: + compile_error("Invalid SPI peripheral specified in SPIDomain::Device"); + } + } + + // Helper function to validate NSS pin (only called for hardware NSS mode) + static consteval void validate_nss_pin(SPIPeripheral peripheral, GPIODomain::Pin nss_pin) { + switch (peripheral) { + case SPIPeripheral::spi1: + if (!compare_pin(nss_pin, PA4) && + !compare_pin(nss_pin, PA15) && + !compare_pin(nss_pin, PG10)) { + compile_error("Invalid NSS pin for SPI1"); + } + break; + + case SPIPeripheral::spi2: + if (!compare_pin(nss_pin, PA11) && + !compare_pin(nss_pin, PB9) && + !compare_pin(nss_pin, PB4) && + !compare_pin(nss_pin, PB12)) { + compile_error("Invalid NSS pin for SPI2"); + } + break; + + case SPIPeripheral::spi3: + if (!compare_pin(nss_pin, PA15) && + !compare_pin(nss_pin, PA4)) { + compile_error("Invalid NSS pin for SPI3"); + } + break; + + case SPIPeripheral::spi4: + if (!compare_pin(nss_pin, PE4) && + !compare_pin(nss_pin, PE11)) { + compile_error("Invalid NSS pin for SPI4"); + } + break; + + case SPIPeripheral::spi5: + if (!compare_pin(nss_pin, PF6)) { + compile_error("Invalid NSS pin for SPI5"); + } + break; + + case SPIPeripheral::spi6: + if (!compare_pin(nss_pin, PA0) && + !compare_pin(nss_pin, PA15) && + !compare_pin(nss_pin, PG8) && + !compare_pin(nss_pin, PA4)) { + compile_error("Invalid NSS pin for SPI6"); + } + break; + + default: + compile_error("Invalid SPI peripheral specified in SPIDomain::Device"); + } + } + + static consteval DMA_Domain::Peripheral dma_peripheral(SPIPeripheral peripheral) { + switch (peripheral) { + case SPIPeripheral::spi1: + return DMA_Domain::Peripheral::spi1; + case SPIPeripheral::spi2: + return DMA_Domain::Peripheral::spi2; + case SPIPeripheral::spi3: + return DMA_Domain::Peripheral::spi3; + case SPIPeripheral::spi4: + return DMA_Domain::Peripheral::spi4; + case SPIPeripheral::spi5: + return DMA_Domain::Peripheral::spi5; + case SPIPeripheral::spi6: + return DMA_Domain::Peripheral::spi6; + default: + compile_error("Invalid SPI peripheral specified in SPIDomain::Device"); + } + } + }; + + +/** + * ========================================= + * Instance (state holder) + * ========================================= + */ + template struct Init; // Forward declaration + template struct SPIWrapper; // Forward declaration + struct Instance { + template friend struct Init; + template friend struct SPIWrapper; + friend void ::SPI1_IRQHandler(void); + friend void ::SPI2_IRQHandler(void); + friend void ::SPI3_IRQHandler(void); + friend void ::SPI4_IRQHandler(void); + friend void ::SPI5_IRQHandler(void); + friend void ::SPI6_IRQHandler(void); + friend void ::HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi); + friend void ::HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi); + friend void ::HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi); + friend void ::HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi); + + private: + SPI_HandleTypeDef hspi; + SPI_TypeDef* instance; + + volatile bool* operation_flag = nullptr; + }; + + + static inline Instance* spi_instances[max_instances]; +/** + * ========================================= + * Wrapper, public API + * ========================================= + */ + + // SPI Wrapper primary template + template + struct SPIWrapper; + + /** + * @brief SPI Wrapper for Master mode operations. + */ + template + struct SPIWrapper { + static constexpr uint32_t data_bits = static_cast(device_request.config.data_size); + static constexpr uint32_t frame_size = (data_bits <= 8) ? 1 : ((data_bits <= 16) ? 2 : 4); + + SPIWrapper(Instance &instance) : spi_instance{instance} {} + + /** + * @brief Sends data over SPI in blocking mode. + */ + template + bool send(span data) { + if (data.size_bytes() % frame_size != 0) { + ErrorHandler("SPI data size (%d) not aligned to frame size (%d)", data.size_bytes(), frame_size); + return false; + } + auto error_code = HAL_SPI_Transmit(&spi_instance.hspi, (uint8_t*)data.data(), data.size_bytes() / frame_size, 10); + return check_error_code(error_code); + } + + /** + * @brief Sends a trivially copyable data type over SPI in blocking mode. + */ + template + bool send(const T& data) requires std::is_trivially_copyable_v { + if (sizeof(T) % frame_size != 0) { + ErrorHandler("SPI data type size (%d) not aligned to frame size (%d)", sizeof(T), frame_size); + return false; + } + auto error_code = HAL_SPI_Transmit(&spi_instance.hspi, reinterpret_cast(&data), sizeof(T) / frame_size, 10); + return check_error_code(error_code); + } + + /** + * @brief Receives data over SPI in blocking mode. + */ + template + bool receive(span data) { + if (data.size_bytes() % frame_size != 0) { + ErrorHandler("SPI data size (%d) not aligned to frame size (%d)", data.size_bytes(), frame_size); + return false; + } + auto error_code = HAL_SPI_Receive(&spi_instance.hspi, (uint8_t*)data.data(), data.size_bytes() / frame_size, 10); + return check_error_code(error_code); + } + + /** + * @brief Receives a trivially copyable data type over SPI in blocking mode. + */ + template + bool receive(T& data) requires std::is_trivially_copyable_v { + if (sizeof(T) % frame_size != 0) { + ErrorHandler("SPI data type size (%d) not aligned to frame size (%d)", sizeof(T), frame_size); + return false; + } + auto error_code = HAL_SPI_Receive(&spi_instance.hspi, reinterpret_cast(&data), sizeof(T) / frame_size, 10); + return check_error_code(error_code); + } + + /** + * @brief Sends and receives data over SPI in blocking mode. + */ + template + bool transceive(span tx_data, span rx_data) { + size_t size = std::min(tx_data.size_bytes(), rx_data.size_bytes()); + if (size % frame_size != 0) { + ErrorHandler("SPI transaction size (%d) not aligned to frame size (%d)", size, frame_size); + return false; + } + auto error_code = HAL_SPI_TransmitReceive(&spi_instance.hspi, (uint8_t*)tx_data.data(), (uint8_t*)rx_data.data(), size / frame_size, 10); + return check_error_code(error_code); + } + + /** + * @brief Sends and receives a trivially copyable data type over SPI in blocking mode. + */ + template + bool transceive(span tx_data, T& rx_data) requires std::is_trivially_copyable_v { + size_t size = std::min(tx_data.size_bytes(), sizeof(T)); + if (size % frame_size != 0) { + ErrorHandler("SPI transaction size (%d) not aligned to frame size (%d)", size, frame_size); + return false; + } + auto error_code = HAL_SPI_TransmitReceive(&spi_instance.hspi, (uint8_t*)tx_data.data(), reinterpret_cast(&rx_data), size / frame_size, 10); + return check_error_code(error_code); + } + + /** + * @brief Sends and receives a trivially copyable data type over SPI in blocking mode. + */ + template + bool transceive(const T& tx_data, span rx_data) requires std::is_trivially_copyable_v { + size_t size = std::min(sizeof(T), rx_data.size_bytes()); + if (size % frame_size != 0) { + ErrorHandler("SPI transaction size (%d) not aligned to frame size (%d)", size, frame_size); + return false; + } + auto error_code = HAL_SPI_TransmitReceive(&spi_instance.hspi, reinterpret_cast(&tx_data), (uint8_t*)rx_data.data(), size / frame_size, 10); + return check_error_code(error_code); + } + + /** + * @brief Sends and receives a trivially copyable data type over SPI in blocking mode. + */ + template + bool transceive(const T1& tx_data, T2& rx_data) requires (std::is_trivially_copyable_v && std::is_trivially_copyable_v) { + size_t size = std::min(sizeof(T1), sizeof(T2)); + if (size % frame_size != 0) { + ErrorHandler("SPI transaction size (%d) not aligned to frame size (%d)", size, frame_size); + return false; + } + auto error_code = HAL_SPI_TransmitReceive(&spi_instance.hspi, reinterpret_cast(&tx_data), reinterpret_cast(&rx_data), size / frame_size, 10); + return check_error_code(error_code); + } + + /** + * @brief Sends data over SPI using DMA, uses an optional operation flag to signal completion. + */ + template + bool send_DMA(span data, volatile bool* operation_flag = nullptr) { + spi_instance.operation_flag = operation_flag; + if (data.size_bytes() % frame_size != 0) { + ErrorHandler("SPI data size (%d) not aligned to frame size (%d)", data.size_bytes(), frame_size); + return false; + } + auto error_code = HAL_SPI_Transmit_DMA(&spi_instance.hspi, (uint8_t*)data.data(), data.size_bytes() / frame_size); + return check_error_code(error_code); + } + + /** + * @brief Sends a trivially copyable data type over SPI using DMA, uses an optional operation flag to signal completion. + */ + template + bool send_DMA(const T& data, volatile bool* operation_flag = nullptr) requires std::is_trivially_copyable_v { + spi_instance.operation_flag = operation_flag; + if (sizeof(T) % frame_size != 0) { + ErrorHandler("SPI data size (%d) not aligned to frame size (%d)", sizeof(T), frame_size); + return false; + } + auto error_code = HAL_SPI_Transmit_DMA(&spi_instance.hspi, reinterpret_cast(&data), sizeof(T) / frame_size); + return check_error_code(error_code); + } + + /** + * @brief Receives data over SPI using DMA, uses an optional operation flag to signal completion. + */ + template + bool receive_DMA(span data, volatile bool* operation_flag = nullptr) { + spi_instance.operation_flag = operation_flag; + if (data.size_bytes() % frame_size != 0) { + ErrorHandler("SPI data size (%d) not aligned to frame size (%d)", data.size_bytes(), frame_size); + return false; + } + auto error_code = HAL_SPI_Receive_DMA(&spi_instance.hspi, (uint8_t*)data.data(), data.size_bytes() / frame_size); + return check_error_code(error_code); + } + + /** + * @brief Receives a trivially copyable data type over SPI using DMA, uses an optional operation flag to signal completion. + */ + template + bool receive_DMA(T& data, volatile bool* operation_flag = nullptr) requires std::is_trivially_copyable_v { + spi_instance.operation_flag = operation_flag; + if (sizeof(T) % frame_size != 0) { + ErrorHandler("SPI data size (%d) not aligned to frame size (%d)", sizeof(T), frame_size); + return false; + } + auto error_code = HAL_SPI_Receive_DMA(&spi_instance.hspi, reinterpret_cast(&data), sizeof(T) / frame_size); + return check_error_code(error_code); + } + + /** + * @brief Sends and receives data over SPI using DMA, uses an optional operation flag to signal completion. + */ + template + bool transceive_DMA(span tx_data, span rx_data, volatile bool* operation_flag = nullptr) { + spi_instance.operation_flag = operation_flag; + auto size = std::min(tx_data.size_bytes(), rx_data.size_bytes()); + if (size % frame_size != 0) { + ErrorHandler("SPI transaction size (%d) not aligned to frame size (%d)", size, frame_size); + return false; + } + auto error_code = HAL_SPI_TransmitReceive_DMA(&spi_instance.hspi, (uint8_t*)tx_data.data(), (uint8_t*)rx_data.data(), size / frame_size); + return check_error_code(error_code); + } + + /** + * @brief Sends a span and receives a trivially copyable type over SPI using DMA, uses an optional operation flag to signal completion. + */ + template + bool transceive_DMA(span tx_data, T& rx_data, volatile bool* operation_flag = nullptr) requires std::is_trivially_copyable_v { + spi_instance.operation_flag = operation_flag; + auto size = std::min(tx_data.size_bytes(), sizeof(T)); + if (size % frame_size != 0) { + ErrorHandler("SPI transaction size (%d) not aligned to frame size (%d)", size, frame_size); + return false; + } + auto error_code = HAL_SPI_TransmitReceive_DMA(&spi_instance.hspi, (uint8_t*)tx_data.data(), reinterpret_cast(&rx_data), size / frame_size); + return check_error_code(error_code); + } + + /** + * @brief Sends a trivially copyable type and receives a span over SPI using DMA, uses an optional operation flag to signal completion. + */ + template + bool transceive_DMA(const T& tx_data, span rx_data, volatile bool* operation_flag = nullptr) requires std::is_trivially_copyable_v { + spi_instance.operation_flag = operation_flag; + auto size = std::min(sizeof(T), rx_data.size_bytes()); + if (size % frame_size != 0) { + ErrorHandler("SPI transaction size (%d) not aligned to frame size (%d)", size, frame_size); + return false; + } + auto error_code = HAL_SPI_TransmitReceive_DMA(&spi_instance.hspi, reinterpret_cast(&tx_data), (uint8_t*)rx_data.data(), size / frame_size); + return check_error_code(error_code); + } + + /** + * @brief Sends and receives a trivially copyable data type over SPI using DMA, uses an optional operation flag to signal completion. + */ + template + bool transceive_DMA(const T1& tx_data, T2& rx_data, volatile bool* operation_flag = nullptr) requires (std::is_trivially_copyable_v && std::is_trivially_copyable_v) { + spi_instance.operation_flag = operation_flag; + auto size = std::min(sizeof(T1), sizeof(T2)); + if (size % frame_size != 0) { + ErrorHandler("SPI transaction size (%d) not aligned to frame size (%d)", size, frame_size); + return false; + } + auto error_code = HAL_SPI_TransmitReceive_DMA(&spi_instance.hspi, reinterpret_cast(&tx_data), reinterpret_cast(&rx_data), size / frame_size); + return check_error_code(error_code); + } + + private: + Instance& spi_instance; + bool check_error_code(HAL_StatusTypeDef error_code) { + if (error_code == HAL_OK) { + return true; + } else if (error_code == HAL_BUSY) { + return false; + } else { + ErrorHandler("SPI transmit error: %u", static_cast(error_code)); + return false; + } + } + }; + + /** + * @brief SPI Wrapper for Slave mode operations. Doesn't allow for blocking operations. + */ + template + struct SPIWrapper { + static constexpr uint32_t data_bits = static_cast(device_request.config.data_size); + static constexpr uint32_t frame_size = (data_bits <= 8) ? 1 : ((data_bits <= 16) ? 2 : 4); + + SPIWrapper(Instance &instance) : spi_instance{instance} {} + + void set_software_nss(bool selected) requires (device_request.config.nss_mode == SPIConfigTypes::NSSMode::SOFTWARE) { + if (selected) { + CLEAR_BIT(spi_instance.instance->CR1, SPI_CR1_SSI); + } else { + SET_BIT(spi_instance.instance->CR1, SPI_CR1_SSI); + } + } + + /** + * @brief Listens for data over SPI using DMA, uses an optional operation flag to signal completion. + */ + template + bool listen(span data, volatile bool* operation_flag = nullptr) { + spi_instance.operation_flag = operation_flag; + if (data.size_bytes() % frame_size != 0) { + ErrorHandler("SPI data size (%d) not aligned to frame size (%d)", data.size_bytes(), frame_size); + return false; + } + auto error_code = HAL_SPI_Receive_DMA(&spi_instance.hspi, (uint8_t*)data.data(), data.size_bytes() / frame_size); + return check_error_code(error_code); + } + + /** + * @brief Listens for trivially copyable data type over SPI using DMA, uses an optional operation flag to signal completion. + */ + template + bool listen(T& data, volatile bool* operation_flag = nullptr) requires std::is_trivially_copyable_v { + spi_instance.operation_flag = operation_flag; + if (sizeof(T) % frame_size != 0) { + ErrorHandler("SPI data size (%d) not aligned to frame size (%d)", sizeof(T), frame_size); + return false; + } + auto error_code = HAL_SPI_Receive_DMA(&spi_instance.hspi, reinterpret_cast(&data), sizeof(T) / frame_size); + return check_error_code(error_code); + } + + /** + * @brief Arms the SPI to send data over DMA when requested, uses an optional operation flag to signal completion. + */ + template + bool arm(span tx_data, volatile bool* operation_flag = nullptr) { + spi_instance.operation_flag = operation_flag; + if (tx_data.size_bytes() % frame_size != 0) { + ErrorHandler("SPI data size (%d) not aligned to frame size (%d)", tx_data.size_bytes(), frame_size); + return false; + } + auto error_code = HAL_SPI_Transmit_DMA(&spi_instance.hspi, (uint8_t*)tx_data.data(), tx_data.size_bytes() / frame_size); + return check_error_code(error_code); + } + + /** + * @brief Arms the SPI to send a trivially copyable data type over DMA when requested, uses an optional operation flag to signal completion. + */ + template + bool arm(const T& data, volatile bool* operation_flag = nullptr) requires std::is_trivially_copyable_v { + spi_instance.operation_flag = operation_flag; + if (sizeof(T) % frame_size != 0) { + ErrorHandler("SPI data size (%d) not aligned to frame size (%d)", sizeof(T), frame_size); + return false; + } + auto error_code = HAL_SPI_Transmit_DMA(&spi_instance.hspi, reinterpret_cast(&data), sizeof(T) / frame_size); + return check_error_code(error_code); + } + + /** + * @brief Sends and receives data over SPI using DMA, uses an optional operation flag to signal completion. + */ + template + bool transceive(span tx_data, span rx_data, volatile bool* operation_flag = nullptr) { + spi_instance.operation_flag = operation_flag; + auto size = std::min(tx_data.size_bytes(), rx_data.size_bytes()); + if (size % frame_size != 0) { + ErrorHandler("SPI transaction size (%d) not aligned to frame size (%d)", size, frame_size); + return false; + } + auto error_code = HAL_SPI_TransmitReceive_DMA(&spi_instance.hspi, (uint8_t*)tx_data.data(), (uint8_t*)rx_data.data(), size / frame_size); + return check_error_code(error_code); + } + + /** + * @brief Sends a span and receives a trivially copyable type over SPI using DMA, uses an optional operation flag to signal completion. + */ + template + bool transceive(span tx_data, T& rx_data, volatile bool* operation_flag = nullptr) requires std::is_trivially_copyable_v { + spi_instance.operation_flag = operation_flag; + auto size = std::min(tx_data.size_bytes(), sizeof(T)); + if (size % frame_size != 0) { + ErrorHandler("SPI transaction size (%d) not aligned to frame size (%d)", size, frame_size); + return false; + } + auto error_code = HAL_SPI_TransmitReceive_DMA(&spi_instance.hspi, (uint8_t*)tx_data.data(), reinterpret_cast(&rx_data), size / frame_size); + return check_error_code(error_code); + } + + /** + * @brief Sends a trivially copyable type and receives a span over SPI using DMA, uses an optional operation flag to signal completion. + */ + template + bool transceive(const T& tx_data, span rx_data, volatile bool* operation_flag = nullptr) requires std::is_trivially_copyable_v { + spi_instance.operation_flag = operation_flag; + auto size = std::min(sizeof(T), rx_data.size_bytes()); + if (size % frame_size != 0) { + ErrorHandler("SPI transaction size (%d) not aligned to frame size (%d)", size, frame_size); + return false; + } + auto error_code = HAL_SPI_TransmitReceive_DMA(&spi_instance.hspi, reinterpret_cast(&tx_data), (uint8_t*)rx_data.data(), size / frame_size); + return check_error_code(error_code); + } + + /** + * @brief Sends and receives a trivially copyable data type over SPI using DMA, uses an optional operation flag to signal completion. + */ + template + bool transceive(const T1& tx_data, T2& rx_data, volatile bool* operation_flag = nullptr) requires (std::is_trivially_copyable_v && std::is_trivially_copyable_v) { + spi_instance.operation_flag = operation_flag; + auto size = std::min(sizeof(T1), sizeof(T2)); + if (size % frame_size != 0) { + ErrorHandler("SPI transaction size (%d) not aligned to frame size (%d)", size, frame_size); + return false; + } + auto error_code = HAL_SPI_TransmitReceive_DMA(&spi_instance.hspi, reinterpret_cast(&tx_data), reinterpret_cast(&rx_data), size / frame_size); + return check_error_code(error_code); + } + + private: + Instance& spi_instance; + bool check_error_code(HAL_StatusTypeDef error_code) { + if (error_code == HAL_OK) { + return true; + } else if (error_code == HAL_BUSY) { + return false; + } else { + ErrorHandler("SPI transmit error: %u", static_cast(error_code)); + return false; + } + } + }; + + +/** + * ========================================= + * Internal working things + * ========================================= + */ + template + static consteval array build(span entries) { + array cfgs{}; + + if (N == 0) { + return cfgs; + } + + bool used_peripherals[6] = {false}; + + for (std::size_t i = 0; i < N; ++i) { + cfgs[i].peripheral = entries[i].peripheral; + cfgs[i].mode = entries[i].mode; + cfgs[i].sck_gpio_idx = entries[i].sck_gpio_idx; + cfgs[i].miso_gpio_idx = entries[i].miso_gpio_idx; + cfgs[i].mosi_gpio_idx = entries[i].mosi_gpio_idx; + cfgs[i].nss_gpio_idx = entries[i].nss_gpio_idx; + cfgs[i].dma_rx_idx = entries[i].dma_rx_idx; + cfgs[i].dma_tx_idx = entries[i].dma_tx_idx; + cfgs[i].max_baudrate = entries[i].max_baudrate; + cfgs[i].config = entries[i].config; + + auto peripheral = entries[i].peripheral; + + if (peripheral == SPIPeripheral::spi1) { + if (used_peripherals[0]) { + compile_error("SPI1 peripheral already used"); + } + used_peripherals[0] = true; + } else if (peripheral == SPIPeripheral::spi2) { + if (used_peripherals[1]) { + compile_error("SPI2 peripheral already used"); + } + used_peripherals[1] = true; + } else if (peripheral == SPIPeripheral::spi3) { + if (used_peripherals[2]) { + compile_error("SPI3 peripheral already used"); + } + used_peripherals[2] = true; + } else if (peripheral == SPIPeripheral::spi4) { + if (used_peripherals[3]) { + compile_error("SPI4 peripheral already used"); + } + used_peripherals[3] = true; + } else if (peripheral == SPIPeripheral::spi5) { + if (used_peripherals[4]) { + compile_error("SPI5 peripheral already used"); + } + used_peripherals[4] = true; + } else if (peripheral == SPIPeripheral::spi6) { + if (used_peripherals[5]) { + compile_error("SPI6 peripheral already used"); + } + used_peripherals[5] = true; + } + } + + return cfgs; + } + + template + struct Init { + static inline std::array instances{}; + + static void init(std::span cfgs, + std::span gpio_instances, + std::span dma_peripherals) { + for (std::size_t i = 0; i < N; ++i) { + const auto &e = cfgs[i]; + + SPIPeripheral peripheral = e.peripheral; + instances[i].instance = reinterpret_cast(e.peripheral); + + // Configure clock and store handle + RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0}; + uint8_t spi_number = 0; + if (peripheral == SPIPeripheral::spi1) { + PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SPI1; + PeriphClkInitStruct.Spi123ClockSelection = RCC_SPI123CLKSOURCE_PLL; + if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) { + ErrorHandler("Unable to configure SPI1 clock"); + } + __HAL_RCC_SPI1_CLK_ENABLE(); + spi_number = 1; + } else if (peripheral == SPIPeripheral::spi2) { + PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SPI2; + PeriphClkInitStruct.Spi123ClockSelection = RCC_SPI123CLKSOURCE_PLL; + if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) { + ErrorHandler("Unable to configure SPI2 clock"); + } + __HAL_RCC_SPI2_CLK_ENABLE(); + spi_number = 2; + } else if (peripheral == SPIPeripheral::spi3) { + PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SPI3; + PeriphClkInitStruct.Spi123ClockSelection = RCC_SPI123CLKSOURCE_PLL; + if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) { + ErrorHandler("Unable to configure SPI3 clock"); + } + __HAL_RCC_SPI3_CLK_ENABLE(); + spi_number = 3; + } else if (peripheral == SPIPeripheral::spi4) { + PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SPI4; + PeriphClkInitStruct.Spi45ClockSelection = RCC_SPI45CLKSOURCE_PLL2; + if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) { + ErrorHandler("Unable to configure SPI4 clock"); + } + __HAL_RCC_SPI4_CLK_ENABLE(); + spi_number = 4; + } else if (peripheral == SPIPeripheral::spi5) { + PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SPI5; + PeriphClkInitStruct.Spi45ClockSelection = RCC_SPI45CLKSOURCE_PLL2; + if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) { + ErrorHandler("Unable to configure SPI5 clock"); + } + __HAL_RCC_SPI5_CLK_ENABLE(); + spi_number = 5; + } else if (peripheral == SPIPeripheral::spi6) { + PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SPI6; + PeriphClkInitStruct.Spi6ClockSelection = RCC_SPI6CLKSOURCE_PLL2; + if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) { + ErrorHandler("Unable to configure SPI6 clock"); + } + __HAL_RCC_SPI6_CLK_ENABLE(); + spi_number = 6; + } + + auto& hspi = instances[i].hspi; + hspi.Instance = instances[i].instance; + + auto& dma_rx = dma_peripherals[e.dma_rx_idx]; + auto& dma_tx = dma_peripherals[e.dma_tx_idx]; + + // DMA handles are already configured and initialized by DMA_Domain + hspi.hdmarx = &dma_rx.dma; + hspi.hdmatx = &dma_tx.dma; + + // Link back from DMA to SPI (required by HAL) + dma_rx.dma.Parent = &hspi; + dma_tx.dma.Parent = &hspi; + + if (e.config.data_size > DataSize::SIZE_16BIT) { + dma_rx.dma.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD; + dma_rx.dma.Init.MemDataAlignment = DMA_MDATAALIGN_WORD; + dma_tx.dma.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD; + dma_tx.dma.Init.MemDataAlignment = DMA_MDATAALIGN_WORD; + } else if (e.config.data_size > DataSize::SIZE_8BIT) { + dma_rx.dma.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; + dma_rx.dma.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; + dma_tx.dma.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; + dma_tx.dma.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; + } + + HAL_DMA_Init(hspi.hdmarx); + HAL_DMA_Init(hspi.hdmatx); + + auto& init = hspi.Init; + if (e.mode == SPIMode::MASTER) { + init.Mode = SPI_MODE_MASTER; + // Baudrate prescaler calculation + uint32_t pclk_freq; + if (peripheral == SPIPeripheral::spi1 || + peripheral == SPIPeripheral::spi2 || + peripheral == SPIPeripheral::spi3) { + pclk_freq = HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SPI123); + } else if (peripheral == SPIPeripheral::spi4 || + peripheral == SPIPeripheral::spi5) { + pclk_freq = HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SPI45); + } else { + pclk_freq = HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SPI6); + } + init.BaudRatePrescaler = calculate_prescaler(pclk_freq, e.max_baudrate); + } else { + init.Mode = SPI_MODE_SLAVE; + } + + init.NSS = SPIConfigTypes::translate_nss_mode(e.config.nss_mode, e.mode == SPIMode::MASTER); + init.Direction = SPIConfigTypes::translate_direction(e.config.direction); + init.DataSize = SPIConfigTypes::translate_data_size(e.config.data_size); + init.CLKPolarity = SPIConfigTypes::translate_clock_polarity(e.config.polarity); + init.CLKPhase = SPIConfigTypes::translate_clock_phase(e.config.phase); + init.FirstBit = SPIConfigTypes::translate_bit_order(e.config.bit_order); + init.TIMode = SPIConfigTypes::translate_ti_mode(e.config.ti_mode); + init.CRCCalculation = SPIConfigTypes::translate_crc_calculation(e.config.crc_calculation); + if (e.config.crc_calculation) { + init.CRCPolynomial = e.config.crc_polynomial; + init.CRCLength = SPIConfigTypes::translate_crc_length(e.config.crc_length); + } + init.NSSPMode = SPIConfigTypes::translate_nss_pulse(e.config.nss_pulse); + init.NSSPolarity = SPIConfigTypes::translate_nss_polarity(e.config.nss_polarity); + init.FifoThreshold = SPIConfigTypes::translate_fifo_threshold(e.config.fifo_threshold); + init.TxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN; + init.RxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN; + init.MasterSSIdleness = SPIConfigTypes::translate_ss_idleness(e.config.master_ss_idleness); + init.MasterInterDataIdleness = SPIConfigTypes::translate_interdata_idleness(e.config.master_interdata_idleness); + init.MasterReceiverAutoSusp = SPIConfigTypes::translate_rx_autosusp(e.config.master_rx_autosusp); + init.MasterKeepIOState = SPIConfigTypes::translate_keep_io_state(e.config.keep_io_state); + init.IOSwap = SPIConfigTypes::translate_io_swap(e.config.io_swap); + + if (HAL_SPI_Init(&hspi) != HAL_OK) { + ErrorHandler("Unable to init SPI%u", spi_number); + return; + } + + // Enable NVIC + if (peripheral == SPIPeripheral::spi1) { + HAL_NVIC_SetPriority(SPI1_IRQn, 1, 0); + HAL_NVIC_EnableIRQ(SPI1_IRQn); + } else if (peripheral == SPIPeripheral::spi2) { + HAL_NVIC_SetPriority(SPI2_IRQn, 1, 0); + HAL_NVIC_EnableIRQ(SPI2_IRQn); + } else if (peripheral == SPIPeripheral::spi3) { + HAL_NVIC_SetPriority(SPI3_IRQn, 1, 0); + HAL_NVIC_EnableIRQ(SPI3_IRQn); + } else if (peripheral == SPIPeripheral::spi4) { + HAL_NVIC_SetPriority(SPI4_IRQn, 1, 0); + HAL_NVIC_EnableIRQ(SPI4_IRQn); + } else if (peripheral == SPIPeripheral::spi5) { + HAL_NVIC_SetPriority(SPI5_IRQn, 1, 0); + HAL_NVIC_EnableIRQ(SPI5_IRQn); + } else if (peripheral == SPIPeripheral::spi6) { + HAL_NVIC_SetPriority(SPI6_IRQn, 1, 0); + HAL_NVIC_EnableIRQ(SPI6_IRQn); + } + + // Map instance pointer + if (peripheral == SPIPeripheral::spi1) { + spi_instances[0] = &instances[i]; + } else if (peripheral == SPIPeripheral::spi2) { + spi_instances[1] = &instances[i]; + } else if (peripheral == SPIPeripheral::spi3) { + spi_instances[2] = &instances[i]; + } else if (peripheral == SPIPeripheral::spi4) { + spi_instances[3] = &instances[i]; + } else if (peripheral == SPIPeripheral::spi5) { + spi_instances[4] = &instances[i]; + } else if (peripheral == SPIPeripheral::spi6) { + spi_instances[5] = &instances[i]; + } + } + } + }; +}; + +} // namespace ST_LIB + + +#endif // SPI2_HPP \ No newline at end of file diff --git a/Inc/HALAL/Models/SPI/SPIConfig.hpp b/Inc/HALAL/Models/SPI/SPIConfig.hpp new file mode 100644 index 000000000..0215de56d --- /dev/null +++ b/Inc/HALAL/Models/SPI/SPIConfig.hpp @@ -0,0 +1,295 @@ +/* + * SPIConfig.hpp + * + * Created on: 19 jan. 2026 + * Author: Boris + */ + +#ifndef SPICONFIG_HPP +#define SPICONFIG_HPP + +#include "C++Utilities/CppImports.hpp" +#include "ErrorHandler/ErrorHandler.hpp" + +namespace ST_LIB { + +struct SPIConfigTypes { + + enum class ClockPolarity : bool { + LOW = false, // Clock idle state is low (CPOL=0) + HIGH = true // Clock idle state is high (CPOL=1) + }; + + enum class ClockPhase : bool { + FIRST_EDGE = false, // Data sampled on first clock edge (CPHA=0) + SECOND_EDGE = true // Data sampled on second clock edge (CPHA=1) + }; + + enum class BitOrder : bool { + MSB_FIRST = false, + LSB_FIRST = true + }; + + enum class NSSMode { + SOFTWARE, // Software NSS management (manual control) + HARDWARE // Hardware NSS management (automatic) + }; + + enum class DataSize : uint8_t { + SIZE_4BIT = 4, + SIZE_5BIT = 5, + SIZE_6BIT = 6, + SIZE_7BIT = 7, + SIZE_8BIT = 8, + SIZE_9BIT = 9, + SIZE_10BIT = 10, + SIZE_11BIT = 11, + SIZE_12BIT = 12, + SIZE_13BIT = 13, + SIZE_14BIT = 14, + SIZE_15BIT = 15, + SIZE_16BIT = 16, + SIZE_17BIT = 17, + SIZE_18BIT = 18, + SIZE_19BIT = 19, + SIZE_20BIT = 20, + SIZE_21BIT = 21, + SIZE_22BIT = 22, + SIZE_23BIT = 23, + SIZE_24BIT = 24, + SIZE_25BIT = 25, + SIZE_26BIT = 26, + SIZE_27BIT = 27, + SIZE_28BIT = 28, + SIZE_29BIT = 29, + SIZE_30BIT = 30, + SIZE_31BIT = 31, + SIZE_32BIT = 32 + }; + + enum class Direction { + FULL_DUPLEX, // 2-line bidirectional (most common) + HALF_DUPLEX, // 1-line bidirectional + SIMPLEX_TX_ONLY, // Transmit only + SIMPLEX_RX_ONLY // Receive only + }; + + enum class FIFOThreshold : uint8_t { + THRESHOLD_01DATA = 1, + THRESHOLD_02DATA = 2, + THRESHOLD_03DATA = 3, + THRESHOLD_04DATA = 4, + THRESHOLD_05DATA = 5, + THRESHOLD_06DATA = 6, + THRESHOLD_07DATA = 7, + THRESHOLD_08DATA = 8, + THRESHOLD_09DATA = 9, + THRESHOLD_10DATA = 10, + THRESHOLD_11DATA = 11, + THRESHOLD_12DATA = 12, + THRESHOLD_13DATA = 13, + THRESHOLD_14DATA = 14, + THRESHOLD_15DATA = 15, + THRESHOLD_16DATA = 16 + }; + + enum class NSSPolarity { + ACTIVE_LOW = false, + ACTIVE_HIGH = true + }; + + enum class CRCLength : uint8_t { + DATASIZE = 0, // CRC length matches data size + CRC_4BIT = 4, + CRC_5BIT = 5, + CRC_6BIT = 6, + CRC_7BIT = 7, + CRC_8BIT = 8, + CRC_9BIT = 9, + CRC_10BIT = 10, + CRC_11BIT = 11, + CRC_12BIT = 12, + CRC_13BIT = 13, + CRC_14BIT = 14, + CRC_15BIT = 15, + CRC_16BIT = 16, + CRC_17BIT = 17, + CRC_18BIT = 18, + CRC_19BIT = 19, + CRC_20BIT = 20, + CRC_21BIT = 21, + CRC_22BIT = 22, + CRC_23BIT = 23, + CRC_24BIT = 24, + CRC_25BIT = 25, + CRC_26BIT = 26, + CRC_27BIT = 27, + CRC_28BIT = 28, + CRC_29BIT = 29, + CRC_30BIT = 30, + CRC_31BIT = 31, + CRC_32BIT = 32 + }; + + /** + * @brief User-facing SPI configuration options + */ + struct SPIConfig { + // Core settings + ClockPolarity polarity = ClockPolarity::LOW; + ClockPhase phase = ClockPhase::FIRST_EDGE; + BitOrder bit_order = BitOrder::MSB_FIRST; + NSSMode nss_mode = NSSMode::HARDWARE; + + // Data format + DataSize data_size = DataSize::SIZE_8BIT; + Direction direction = Direction::FULL_DUPLEX; + FIFOThreshold fifo_threshold = FIFOThreshold::THRESHOLD_01DATA; + + // NSS settings + bool nss_pulse = false; // NSS pulse between data frames (master only) + NSSPolarity nss_polarity = NSSPolarity::ACTIVE_LOW; + + // Master timing settings + uint8_t master_ss_idleness = 0; // Cycles (0-15) between NSS and first data + uint8_t master_interdata_idleness = 0; // Cycles (0-15) between data frames + + // Advanced options + bool keep_io_state = true; // Keep pin states when idle (prevents floating) + bool master_rx_autosusp = false; // Auto-suspend in master RX mode to prevent overrun + bool io_swap = false; // Swap MISO/MOSI pins + + // Protocol options + bool ti_mode = false; // Enable TI synchronous serial frame format (Microwire compatible) + + // CRC options (hardware CRC calculation) + bool crc_calculation = false; // Enable hardware CRC calculation + uint32_t crc_polynomial = 0x07; // CRC polynomial (must be odd, default for CRC-8) + CRCLength crc_length = CRCLength::DATASIZE; // CRC length (default: matches data size) + + constexpr SPIConfig() = default; + + constexpr SPIConfig(ClockPolarity pol, ClockPhase ph, + BitOrder order = BitOrder::MSB_FIRST, + NSSMode nss = NSSMode::HARDWARE) + : polarity(pol), phase(ph), bit_order(order), nss_mode(nss) {} + + // Compile-time validation + constexpr void validate() const { + // Validate CRC polynomial if CRC is enabled + if (crc_calculation) { + if (crc_polynomial == 0 || crc_polynomial > 0xFFFF) { + compile_error("CRC polynomial must be between 1 and 65535"); + } + if ((crc_polynomial & 1) == 0) { + compile_error("CRC polynomial must be odd"); + } + } + + // Validate timing parameters + if (master_ss_idleness > 15) { + compile_error("master_ss_idleness must be 0-15"); + } + if (master_interdata_idleness > 15) { + compile_error("master_interdata_idleness must be 0-15"); + } + } + }; + + static constexpr uint32_t translate_nss_mode(NSSMode mode, bool is_master) { + if (mode == NSSMode::SOFTWARE) { + return SPI_NSS_SOFT; + } + // Hardware mode - depends on master/slave + return (is_master) ? SPI_NSS_HARD_OUTPUT : SPI_NSS_HARD_INPUT; + } + + // Translation functions from custom config to HAL constants + static constexpr uint32_t translate_clock_polarity(ClockPolarity pol) { + return (pol == ClockPolarity::LOW) ? SPI_POLARITY_LOW : SPI_POLARITY_HIGH; + } + + static constexpr uint32_t translate_clock_phase(ClockPhase phase) { + return (phase == ClockPhase::FIRST_EDGE) ? SPI_PHASE_1EDGE : SPI_PHASE_2EDGE; + } + + static constexpr uint32_t translate_bit_order(BitOrder order) { + return (order == BitOrder::MSB_FIRST) ? SPI_FIRSTBIT_MSB : SPI_FIRSTBIT_LSB; + } + + static constexpr uint32_t translate_data_size(DataSize size) { + // HAL uses SIZE-1 (e.g., 8 bits = 0x07) + return static_cast(size) - 1; + } + + static constexpr uint32_t translate_direction(Direction dir) { + switch (dir) { + case Direction::FULL_DUPLEX: + return SPI_DIRECTION_2LINES; + case Direction::HALF_DUPLEX: + return SPI_DIRECTION_1LINE; + case Direction::SIMPLEX_TX_ONLY: + return SPI_DIRECTION_2LINES_TXONLY; + case Direction::SIMPLEX_RX_ONLY: + return SPI_DIRECTION_2LINES_RXONLY; + } + return SPI_DIRECTION_2LINES; + } + + static constexpr uint32_t translate_fifo_threshold(FIFOThreshold threshold) { + // HAL uses (value-1) << 5 for FIFO threshold + return (static_cast(threshold) - 1) << 5; + } + + static constexpr uint32_t translate_nss_polarity(NSSPolarity pol) { + return (pol == NSSPolarity::ACTIVE_LOW) ? SPI_NSS_POLARITY_LOW : SPI_NSS_POLARITY_HIGH; + } + + static constexpr uint32_t translate_nss_pulse(bool enable) { + return enable ? SPI_NSS_PULSE_ENABLE : SPI_NSS_PULSE_DISABLE; + } + + static constexpr uint32_t translate_ss_idleness(uint8_t cycles) { + // Clamp to valid range 0-15 + if (cycles > 15) cycles = 15; + return cycles; + } + + static constexpr uint32_t translate_interdata_idleness(uint8_t cycles) { + // Clamp to valid range 0-15, shift to proper position + if (cycles > 15) cycles = 15; + return (cycles << 4); // These are in bits 7:4 of CFG2 + } + + static constexpr uint32_t translate_keep_io_state(bool enable) { + return enable ? SPI_MASTER_KEEP_IO_STATE_ENABLE : SPI_MASTER_KEEP_IO_STATE_DISABLE; + } + + static constexpr uint32_t translate_rx_autosusp(bool enable) { + return enable ? SPI_MASTER_RX_AUTOSUSP_ENABLE : SPI_MASTER_RX_AUTOSUSP_DISABLE; + } + + static constexpr uint32_t translate_io_swap(bool enable) { + return enable ? SPI_IO_SWAP_ENABLE : SPI_IO_SWAP_DISABLE; + } + + static constexpr uint32_t translate_ti_mode(bool enable) { + return enable ? SPI_TIMODE_ENABLE : SPI_TIMODE_DISABLE; + } + + static constexpr uint32_t translate_crc_calculation(bool enable) { + return enable ? SPI_CRCCALCULATION_ENABLE : SPI_CRCCALCULATION_DISABLE; + } + + static constexpr uint32_t translate_crc_length(CRCLength length) { + if (length == CRCLength::DATASIZE) { + return SPI_CRC_LENGTH_DATASIZE; + } + // HAL uses (value-1) << 16 for CRC length + return (static_cast(length) - 1) << 16; + } +}; + +} // namespace ST_LIB + +#endif // SPICONFIG_HPP diff --git a/Inc/HALAL/Services/ADC/ADC.hpp b/Inc/HALAL/Services/ADC/ADC.hpp index 633dba883..100dba5a6 100644 --- a/Inc/HALAL/Services/ADC/ADC.hpp +++ b/Inc/HALAL/Services/ADC/ADC.hpp @@ -10,7 +10,7 @@ #include "HALAL/Models/PinModel/Pin.hpp" #include "HALAL/Models/LowPowerTimer/LowPowerTimer.hpp" -#include "HALAL/Models/DMA/DMA.hpp" +#include "HALAL/Models/DMA/DMA2.hpp" #if defined(HAL_ADC_MODULE_ENABLED) && defined(HAL_LPTIM_MODULE_ENABLED) @@ -42,11 +42,11 @@ class ADC { uint32_t resolution; uint32_t external_trigger; vector channels; - DMA::Stream dma_stream; + ST_LIB::DMA_Domain::Instance *dma_instance; string name; InitData() = default; - InitData(ADC_TypeDef* adc, uint32_t resolution, uint32_t external_trigger, vector& channels, DMA::Stream dma_stream, string name); + InitData(ADC_TypeDef* adc, uint32_t resolution, uint32_t external_trigger, vector& channels, ST_LIB::DMA_Domain::Instance *dma_instance, string name); }; class Peripheral { @@ -155,4 +155,4 @@ class ADC { static void init(Peripheral& peripheral); }; -#endif +#endif \ No newline at end of file diff --git a/Inc/HALAL/Services/Communication/I2C/I2C.hpp b/Inc/HALAL/Services/Communication/I2C/I2C.hpp index 53c57388a..68a05da68 100644 --- a/Inc/HALAL/Services/Communication/I2C/I2C.hpp +++ b/Inc/HALAL/Services/Communication/I2C/I2C.hpp @@ -151,4 +151,4 @@ class I2C { static void init(I2C::Instance *i2c); }; -#endif +#endif \ No newline at end of file diff --git a/Inc/HALAL/Services/Communication/SPI/SPI.hpp b/Inc/HALAL/Services/Communication/SPI/SPI.hpp index 99000a78f..4770e63de 100644 --- a/Inc/HALAL/Services/Communication/SPI/SPI.hpp +++ b/Inc/HALAL/Services/Communication/SPI/SPI.hpp @@ -348,4 +348,4 @@ class SPI{ static void init(SPI::Instance* spi); }; -#endif +#endif \ No newline at end of file diff --git a/Inc/HALAL/Services/FMAC/FMAC.hpp b/Inc/HALAL/Services/FMAC/FMAC.hpp index 9348dd94c..484898a16 100644 --- a/Inc/HALAL/Services/FMAC/FMAC.hpp +++ b/Inc/HALAL/Services/FMAC/FMAC.hpp @@ -132,4 +132,4 @@ class MultiplierAccelerator{ int16_t *FeedbackInValues, uint8_t FeedbackInSize, int16_t *FInValues, uint8_t FInSize, int16_t *OutValues, uint8_t OutSize, FMACmodes mode); }; -#endif +#endif \ No newline at end of file diff --git a/Inc/ST-LIB.hpp b/Inc/ST-LIB.hpp index 727abe9fe..fc7c2d200 100644 --- a/Inc/ST-LIB.hpp +++ b/Inc/ST-LIB.hpp @@ -84,7 +84,7 @@ template struct BuildCtx { }; using DomainsCtx = - BuildCtx; @@ -105,6 +105,8 @@ template struct Board { constexpr std::size_t mpuN = domain_size(); constexpr std::size_t gpioN = domain_size(); constexpr std::size_t timN = domain_size(); + constexpr std::size_t dmaN = 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(); @@ -116,6 +118,8 @@ template struct Board { std::array mpu_cfgs; std::array gpio_cfgs; std::array tim_cfgs; + std::array dma_cfgs; + std::array spi_cfgs; std::array dout_cfgs; std::array din_cfgs; std::array mdma_packet_cfgs; @@ -131,6 +135,10 @@ template struct Board { GPIODomain::template build(ctx.template span()), .tim_cfgs = TimerDomain::template build(ctx.template span()), + .dma_cfgs = DMA_Domain::template build( + ctx.template span()), + .spi_cfgs = SPIDomain::template build( + ctx.template span()), .dout_cfgs = DigitalOutputDomain::template build( ctx.template span()), .din_cfgs = DigitalInputDomain::template build( @@ -150,6 +158,8 @@ template struct Board { constexpr std::size_t mpuN = domain_size(); constexpr std::size_t gpioN = domain_size(); constexpr std::size_t timN = domain_size(); + constexpr std::size_t dmaN = 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(); @@ -167,6 +177,10 @@ template struct Board { MPUDomain::Init::init(); GPIODomain::Init::init(cfg.gpio_cfgs); TimerDomain::Init::init(cfg.tim_cfgs); + DMA_Domain::Init::init(cfg.dma_cfgs); + SPIDomain::Init::init(cfg.spi_cfgs, + GPIODomain::Init::instances, + DMA_Domain::Init::instances); DigitalOutputDomain::Init::init(cfg.dout_cfgs, GPIODomain::Init::instances); DigitalInputDomain::Init::init(cfg.din_cfgs, diff --git a/Src/HALAL/HALAL.cpp b/Src/HALAL/HALAL.cpp index 86e8fe40b..cffc6de1f 100644 --- a/Src/HALAL/HALAL.cpp +++ b/Src/HALAL/HALAL.cpp @@ -33,7 +33,7 @@ static void common_start(UART::Peripheral &printf_peripheral) { #endif #ifdef HAL_DMA_MODULE_ENABLED - DMA::start(); + // DMA::start(); #endif #ifdef HAL_MDMA_MODULE_ENABLED diff --git a/Src/HALAL/Models/DMA/DMA.cpp b/Src/HALAL/Models/DMA/DMA.cpp index e9982734d..d0f9a95f5 100644 --- a/Src/HALAL/Models/DMA/DMA.cpp +++ b/Src/HALAL/Models/DMA/DMA.cpp @@ -2,8 +2,9 @@ * DMA.cpp * * Created on: 10 dic. 2022 - * Author: aleja - */ + * Author: aleja */ + + #include "HALAL/Models/DMA/DMA.hpp" #include "ErrorHandler/ErrorHandler.hpp" @@ -38,4 +39,4 @@ void DMA::start() { HAL_NVIC_SetPriority( (IRQn_Type)dma_stream, 0, 0); HAL_NVIC_EnableIRQ( (IRQn_Type)dma_stream); } -} +} \ No newline at end of file diff --git a/Src/HALAL/Models/DMA/DMA2.cpp b/Src/HALAL/Models/DMA/DMA2.cpp new file mode 100644 index 000000000..27e1c9ac4 --- /dev/null +++ b/Src/HALAL/Models/DMA/DMA2.cpp @@ -0,0 +1,66 @@ +#include "HALAL/Models/DMA/DMA2.hpp" + + +extern "C" void DMA1_Stream0_IRQHandler(void) { + HAL_DMA_IRQHandler(dma_irq_table[0]); +} + +extern "C" void DMA1_Stream1_IRQHandler(void) { + HAL_DMA_IRQHandler(dma_irq_table[1]); +} + +extern "C" void DMA1_Stream2_IRQHandler(void) { + HAL_DMA_IRQHandler(dma_irq_table[2]); +} + +extern "C" void DMA1_Stream3_IRQHandler(void) { + HAL_DMA_IRQHandler(dma_irq_table[3]); +} + +extern "C" void DMA1_Stream4_IRQHandler(void) { + HAL_DMA_IRQHandler(dma_irq_table[4]); +} + +extern "C" void DMA1_Stream5_IRQHandler(void) { + HAL_DMA_IRQHandler(dma_irq_table[5]); +} + +extern "C" void DMA1_Stream6_IRQHandler(void) { + HAL_DMA_IRQHandler(dma_irq_table[6]); +} + +extern "C" void DMA1_Stream7_IRQHandler(void) { + HAL_DMA_IRQHandler(dma_irq_table[7]); +} + +extern "C" void DMA2_Stream0_IRQHandler(void) { + HAL_DMA_IRQHandler(dma_irq_table[8]); +} + +extern "C" void DMA2_Stream1_IRQHandler(void) { + HAL_DMA_IRQHandler(dma_irq_table[9]); +} + +extern "C" void DMA2_Stream2_IRQHandler(void) { + HAL_DMA_IRQHandler(dma_irq_table[10]); +} + +extern "C" void DMA2_Stream3_IRQHandler(void) { + HAL_DMA_IRQHandler(dma_irq_table[11]); +} + +extern "C" void DMA2_Stream4_IRQHandler(void) { + HAL_DMA_IRQHandler(dma_irq_table[12]); +} + +extern "C" void DMA2_Stream5_IRQHandler(void) { + HAL_DMA_IRQHandler(dma_irq_table[13]); +} + +extern "C" void DMA2_Stream6_IRQHandler(void) { + HAL_DMA_IRQHandler(dma_irq_table[14]); +} + +extern "C" void DMA2_Stream7_IRQHandler(void) { + HAL_DMA_IRQHandler(dma_irq_table[15]); +} diff --git a/Src/HALAL/Models/SPI/SPI2.cpp b/Src/HALAL/Models/SPI/SPI2.cpp new file mode 100644 index 000000000..f83ea553c --- /dev/null +++ b/Src/HALAL/Models/SPI/SPI2.cpp @@ -0,0 +1,139 @@ +#include "HALAL/Models/SPI/SPI2.hpp" +uint32_t ST_LIB::SPIDomain::calculate_prescaler(uint32_t src_freq, uint32_t max_baud) { + uint32_t prescaler = 2; // Smallest prescaler available + + while ((src_freq / prescaler) > max_baud) { + prescaler *= 2; // Prescaler doubles each step (it must be a power of 2) + + if (prescaler > 256) { + ErrorHandler("Cannot achieve desired baudrate, speed is too low"); + } + } + + return get_prescaler_flag(prescaler); +} + +extern "C" { + +/** + * ========================================= + * IRQ Handlers + * ========================================= + */ + +void SPI1_IRQHandler(void) { + auto inst = ST_LIB::SPIDomain::spi_instances[0]; + if (inst == nullptr) { + ErrorHandler("SPI1 IRQ Handler called but instance is null"); + return; + } + HAL_SPI_IRQHandler(&inst->hspi); +} +void SPI2_IRQHandler(void) { + auto inst = ST_LIB::SPIDomain::spi_instances[1]; + if (inst == nullptr) { + ErrorHandler("SPI2 IRQ Handler called but instance is null"); + return; + } + HAL_SPI_IRQHandler(&inst->hspi); +} +void SPI3_IRQHandler(void) { + auto inst = ST_LIB::SPIDomain::spi_instances[2]; + if (inst == nullptr) { + ErrorHandler("SPI3 IRQ Handler called but instance is null"); + return; + } + HAL_SPI_IRQHandler(&inst->hspi); +} +void SPI4_IRQHandler(void) { + auto inst = ST_LIB::SPIDomain::spi_instances[3]; + if (inst == nullptr) { + ErrorHandler("SPI4 IRQ Handler called but instance is null"); + return; + } + HAL_SPI_IRQHandler(&inst->hspi); +} +void SPI5_IRQHandler(void) { + auto inst = ST_LIB::SPIDomain::spi_instances[4]; + if (inst == nullptr) { + ErrorHandler("SPI5 IRQ Handler called but instance is null"); + return; + } + HAL_SPI_IRQHandler(&inst->hspi); +} +void SPI6_IRQHandler(void) { + auto inst = ST_LIB::SPIDomain::spi_instances[5]; + if (inst == nullptr) { + ErrorHandler("SPI6 IRQ Handler called but instance is null"); + return; + } + HAL_SPI_IRQHandler(&inst->hspi); +} + + +/** + * ========================================= + * HAL Callbacks + * ========================================= + */ + +void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi) { + auto& spi_instances = ST_LIB::SPIDomain::spi_instances; + + ST_LIB::SPIDomain::Instance* inst = nullptr; + if (spi_instances[0] != nullptr && hspi == &spi_instances[0]->hspi) { + inst = spi_instances[0]; + } else if (spi_instances[1] != nullptr && hspi == &spi_instances[1]->hspi) { + inst = spi_instances[1]; + } else if (spi_instances[2] != nullptr && hspi == &spi_instances[2]->hspi) { + inst = spi_instances[2]; + } else if (spi_instances[3] != nullptr && hspi == &spi_instances[3]->hspi) { + inst = spi_instances[3]; + } else if (spi_instances[4] != nullptr && hspi == &spi_instances[4]->hspi) { + inst = spi_instances[4]; + } else if (spi_instances[5] != nullptr && hspi == &spi_instances[5]->hspi) { + inst = spi_instances[5]; + } else { + ErrorHandler("SPI IRQ Callback called but instance is null"); + return; + } + + if (inst->operation_flag != nullptr) { + *(inst->operation_flag) = true; + } +} + +void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi) { + HAL_SPI_TxCpltCallback(hspi); // Same logic +} + +void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi) { + HAL_SPI_TxCpltCallback(hspi); // Same logic +} + +void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi) { + auto& spi_instances = ST_LIB::SPIDomain::spi_instances; + + uint32_t error_code = hspi->ErrorCode; + uint32_t inst_idx = 0; + if (spi_instances[0] != nullptr && hspi == &spi_instances[0]->hspi) { + inst_idx = 0; + } else if (spi_instances[1] != nullptr && hspi == &spi_instances[1]->hspi) { + inst_idx = 1; + } else if (spi_instances[2] != nullptr && hspi == &spi_instances[2]->hspi) { + inst_idx = 2; + } else if (spi_instances[3] != nullptr && hspi == &spi_instances[3]->hspi) { + inst_idx = 3; + } else if (spi_instances[4] != nullptr && hspi == &spi_instances[4]->hspi) { + inst_idx = 4; + } else if (spi_instances[5] != nullptr && hspi == &spi_instances[5]->hspi) { + inst_idx = 5; + } else { + ErrorHandler("SPI IRQ Callback called but instance is null"); + return; + } + + ErrorHandler("SPI%i failed with error number %u", inst_idx + 1, error_code); +} + +} \ No newline at end of file diff --git a/Src/HALAL/Services/ADC/ADC.cpp b/Src/HALAL/Services/ADC/ADC.cpp index d63e3b920..4abc262f1 100644 --- a/Src/HALAL/Services/ADC/ADC.cpp +++ b/Src/HALAL/Services/ADC/ADC.cpp @@ -16,8 +16,8 @@ uint8_t ADC::id_counter = 0; unordered_map ADC::active_instances = {}; -ADC::InitData::InitData(ADC_TypeDef* adc, uint32_t resolution, uint32_t external_trigger, vector& channels, DMA::Stream dma_stream, string name) : - adc(adc), resolution(resolution), external_trigger(external_trigger), channels(channels), dma_stream(dma_stream), name(name) {} +ADC::InitData::InitData(ADC_TypeDef* adc, uint32_t resolution, uint32_t external_trigger, vector& channels, ST_LIB::DMA_Domain::Instance *dma_instance, string name) : + adc(adc), resolution(resolution), external_trigger(external_trigger), channels(channels), dma_instance(dma_instance), name(name) {} ADC::Peripheral::Peripheral(ADC_HandleTypeDef* handle, LowPowerTimer& timer, InitData& init_data) : handle(handle), timer(timer), init_data(init_data) { @@ -42,7 +42,7 @@ uint8_t ADC::inscribe(Pin pin) { InitData& init_data = active_instances[id_counter].peripheral->init_data; - DMA::inscribe_stream(init_data.dma_stream); + //DMA::inscribe_stream(init_data.dma_stream); active_instances[id_counter].rank = init_data.channels.size(); init_data.channels.push_back(active_instances[id_counter].channel); return id_counter++; @@ -68,7 +68,7 @@ void ADC::turn_on(uint8_t id){ uint32_t buffer_length = peripheral->init_data.channels.size(); if (HAL_ADC_Start_DMA(peripheral->handle, (uint32_t*) peripheral->dma_data_buffer, buffer_length) != HAL_OK) { - ErrorHandler("DMA - %d - of ADC - %d - did not start correctly", peripheral->init_data.dma_stream, id); + ErrorHandler("DMA - %d - of ADC - %d - did not start correctly", peripheral->init_data.dma_instance, id); return; } @@ -146,6 +146,8 @@ void ADC::init(Peripheral& peripheral) { } } + __HAL_LINKDMA(&adc_handle, DMA_Handle, init_data.dma_instance->dma); + uint8_t counter = 0; for(uint32_t channel : peripheral.init_data.channels) { sConfig.Channel = channel; @@ -169,4 +171,4 @@ void ADC::init(Peripheral& peripheral) { void HAL_ADC_ConvCpltCallback (ADC_HandleTypeDef *hadc){ } -#endif +#endif \ No newline at end of file diff --git a/Src/HALAL/Services/Communication/I2C/I2C.cpp b/Src/HALAL/Services/Communication/I2C/I2C.cpp index 61655d304..1af2a9784 100644 --- a/Src/HALAL/Services/Communication/I2C/I2C.cpp +++ b/Src/HALAL/Services/Communication/I2C/I2C.cpp @@ -272,4 +272,4 @@ void I2C::init(I2C::Instance *i2c) { } i2c->is_initialized = true; } -#endif +#endif \ No newline at end of file diff --git a/Src/HALAL/Services/Communication/SPI/SPI.cpp b/Src/HALAL/Services/Communication/SPI/SPI.cpp index 9b498b12b..975474a83 100644 --- a/Src/HALAL/Services/Communication/SPI/SPI.cpp +++ b/Src/HALAL/Services/Communication/SPI/SPI.cpp @@ -433,130 +433,130 @@ void SPI::init(SPI::Instance* spi) { spi->initialized = true; } -void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef* hspi) { - if (!SPI::registered_spi_by_handler.contains(hspi)) { - ErrorHandler("Used SPI protocol without the HALAL SPI interface"); - return; - } - - SPI::Instance* spi = SPI::registered_spi_by_handler[hspi]; - switch (spi->state) { - case SPI::IDLE: - // Does nothing as there is no Order handling on a direct send - break; - case SPI::STARTING_ORDER: { - SPIBaseOrder* Order = - SPIBaseOrder::SPIOrdersByID[*(spi->SPIOrderID)]; - if (spi->mode == - SPI_MODE_MASTER) { // checks if the Order is ready on slave - if (*(spi->available_end) == *(spi->SPIOrderID)) { - spi->state = SPI::PROCESSING_ORDER; - Order->master_prepare_buffer(spi->tx_buffer); - SPI::turn_off_chip_select(spi); - - SPI::spi_communicate_order_data( - spi, spi->tx_buffer, spi->rx_buffer, - Order->payload_size - PAYLOAD_OVERHEAD); - } else { - spi->try_count++; - switch (*(spi->available_end)) { - case NO_ORDER_ID: { - spi->last_end_check = Time::get_global_tick(); - SPI::turn_on_chip_select(spi); - } break; - default: - case ERROR_ORDER_ID: { - spi->last_end_check = Time::get_global_tick(); - spi->error_count++; - SPI::turn_on_chip_select(spi); - } break; - } - } - } else { - ErrorHandler("Used master transmit Order on a slave spi"); - } - break; - } - case SPI::WAITING_ORDER: { - SPIBaseOrder* Order = - SPIBaseOrder::SPIOrdersByID[*(spi->SPIOrderID)]; - if (Order == 0x0) { - SPI::spi_recover(spi, hspi); - return; - } else if (spi->mode == - SPI_MODE_SLAVE) { // prepares the Order on the slave - Order->slave_prepare_buffer(spi->tx_buffer); - SPI::spi_communicate_order_data( - spi, spi->tx_buffer, spi->rx_buffer, Order->payload_size); - SPI::mark_slave_ready(spi); - spi->state = SPI::PROCESSING_ORDER; - } else { - ErrorHandler("Used slave process Orders on a master spi"); - } - break; - } - case SPI::PROCESSING_ORDER: { - SPIBaseOrder* Order = - SPIBaseOrder::SPIOrdersByID[*(spi->SPIOrderID)]; - - if (spi->mode == SPI_MODE_MASTER) { // ends communication - if (*(uint16_t*)&spi - ->rx_buffer[Order->CRC_index - PAYLOAD_OVERHEAD] != - *spi->SPIOrderID) { - spi->state = SPI::STARTING_ORDER; - SPI::master_check_available_end(spi); - return; - } - spi->Order_count++; - SPI::turn_on_chip_select(spi); - Order->master_process_callback(spi->rx_buffer); - spi->state = SPI::IDLE; - } else { // prepares the next received Order - if (*(uint16_t*)&spi->rx_buffer[Order->CRC_index] != - *spi->SPIOrderID) { - SPI::spi_recover(spi, hspi); - return; - } - spi->Order_count++; - SPI::mark_slave_waiting(spi); - *(spi->SPIOrderID) = NO_ORDER_ID; - *(spi->available_end) = NO_ORDER_ID; - Order->slave_process_callback(spi->rx_buffer); - SPI::slave_check_packet_ID(spi); - spi->state = SPI::WAITING_ORDER; - } - break; - } - case SPI::ERROR_RECOVERY: { - if (spi->mode == SPI_MODE_MASTER) { - // TODO - } else { - SPI::mark_slave_waiting(spi); - spi->state = - SPI::WAITING_ORDER; // prepares the next received Order - *(spi->SPIOrderID) = NO_ORDER_ID; - *(spi->available_end) = NO_ORDER_ID; - SPI::slave_check_packet_ID(spi); - } - break; - } - default: - ErrorHandler("Unknown spi state: %d", spi->state); - break; - } -} - -void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef* hspi) {} - -void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef* hspi) {} - -void HAL_SPI_ErrorCallback(SPI_HandleTypeDef* hspi) { - if ((hspi->ErrorCode & HAL_SPI_ERROR_UDR) != 0) { - SPI::spi_recover(SPI::registered_spi_by_handler[hspi], hspi); - } else { - ErrorHandler("SPI error number %u", hspi->ErrorCode); - } -} +// void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef* hspi) { +// if (!SPI::registered_spi_by_handler.contains(hspi)) { +// ErrorHandler("Used SPI protocol without the HALAL SPI interface"); +// return; +// } + +// SPI::Instance* spi = SPI::registered_spi_by_handler[hspi]; +// switch (spi->state) { +// case SPI::IDLE: +// // Does nothing as there is no Order handling on a direct send +// break; +// case SPI::STARTING_ORDER: { +// SPIBaseOrder* Order = +// SPIBaseOrder::SPIOrdersByID[*(spi->SPIOrderID)]; +// if (spi->mode == +// SPI_MODE_MASTER) { // checks if the Order is ready on slave +// if (*(spi->available_end) == *(spi->SPIOrderID)) { +// spi->state = SPI::PROCESSING_ORDER; +// Order->master_prepare_buffer(spi->tx_buffer); +// SPI::turn_off_chip_select(spi); + +// SPI::spi_communicate_order_data( +// spi, spi->tx_buffer, spi->rx_buffer, +// Order->payload_size - PAYLOAD_OVERHEAD); +// } else { +// spi->try_count++; +// switch (*(spi->available_end)) { +// case NO_ORDER_ID: { +// spi->last_end_check = Time::get_global_tick(); +// SPI::turn_on_chip_select(spi); +// } break; +// default: +// case ERROR_ORDER_ID: { +// spi->last_end_check = Time::get_global_tick(); +// spi->error_count++; +// SPI::turn_on_chip_select(spi); +// } break; +// } +// } +// } else { +// ErrorHandler("Used master transmit Order on a slave spi"); +// } +// break; +// } +// case SPI::WAITING_ORDER: { +// SPIBaseOrder* Order = +// SPIBaseOrder::SPIOrdersByID[*(spi->SPIOrderID)]; +// if (Order == 0x0) { +// SPI::spi_recover(spi, hspi); +// return; +// } else if (spi->mode == +// SPI_MODE_SLAVE) { // prepares the Order on the slave +// Order->slave_prepare_buffer(spi->tx_buffer); +// SPI::spi_communicate_order_data( +// spi, spi->tx_buffer, spi->rx_buffer, Order->payload_size); +// SPI::mark_slave_ready(spi); +// spi->state = SPI::PROCESSING_ORDER; +// } else { +// ErrorHandler("Used slave process Orders on a master spi"); +// } +// break; +// } +// case SPI::PROCESSING_ORDER: { +// SPIBaseOrder* Order = +// SPIBaseOrder::SPIOrdersByID[*(spi->SPIOrderID)]; + +// if (spi->mode == SPI_MODE_MASTER) { // ends communication +// if (*(uint16_t*)&spi +// ->rx_buffer[Order->CRC_index - PAYLOAD_OVERHEAD] != +// *spi->SPIOrderID) { +// spi->state = SPI::STARTING_ORDER; +// SPI::master_check_available_end(spi); +// return; +// } +// spi->Order_count++; +// SPI::turn_on_chip_select(spi); +// Order->master_process_callback(spi->rx_buffer); +// spi->state = SPI::IDLE; +// } else { // prepares the next received Order +// if (*(uint16_t*)&spi->rx_buffer[Order->CRC_index] != +// *spi->SPIOrderID) { +// SPI::spi_recover(spi, hspi); +// return; +// } +// spi->Order_count++; +// SPI::mark_slave_waiting(spi); +// *(spi->SPIOrderID) = NO_ORDER_ID; +// *(spi->available_end) = NO_ORDER_ID; +// Order->slave_process_callback(spi->rx_buffer); +// SPI::slave_check_packet_ID(spi); +// spi->state = SPI::WAITING_ORDER; +// } +// break; +// } +// case SPI::ERROR_RECOVERY: { +// if (spi->mode == SPI_MODE_MASTER) { +// // TODO +// } else { +// SPI::mark_slave_waiting(spi); +// spi->state = +// SPI::WAITING_ORDER; // prepares the next received Order +// *(spi->SPIOrderID) = NO_ORDER_ID; +// *(spi->available_end) = NO_ORDER_ID; +// SPI::slave_check_packet_ID(spi); +// } +// break; +// } +// default: +// ErrorHandler("Unknown spi state: %d", spi->state); +// break; +// } +// } + +// // void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef* hspi) {} + +// // void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef* hspi) {} + +// // void HAL_SPI_ErrorCallback(SPI_HandleTypeDef* hspi) { +// // if ((hspi->ErrorCode & HAL_SPI_ERROR_UDR) != 0) { +// // SPI::spi_recover(SPI::registered_spi_by_handler[hspi], hspi); +// // } else { +// // ErrorHandler("SPI error number %u", hspi->ErrorCode); +// // } +// // } void SPI::spi_communicate_order_data(SPI::Instance* spi, uint8_t* value_to_send, uint8_t* value_to_receive, @@ -612,4 +612,4 @@ void SPI::spi_check_bus_collision(SPI::Instance* spi) { } } -#endif +#endif \ No newline at end of file diff --git a/Src/HALAL/Services/FMAC/FMAC.cpp b/Src/HALAL/Services/FMAC/FMAC.cpp index 6b07bb19f..1076a69b0 100644 --- a/Src/HALAL/Services/FMAC/FMAC.cpp +++ b/Src/HALAL/Services/FMAC/FMAC.cpp @@ -168,4 +168,3 @@ void HAL_FMAC_ErrorCallback(FMAC_HandleTypeDef *hfmac){ ErrorHandler("Error while running FMAC"); } -