diff --git a/CMakeLists.txt b/CMakeLists.txt index 23a2c3a9..c1a6b967 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -96,6 +96,7 @@ if(CMAKE_CROSSCOMPILING) # SD ${HAL_DRIVER_SRC_DIR}/stm32h7xx_hal_sd.c ${HAL_DRIVER_SRC_DIR}/stm32h7xx_hal_sd_ex.c + ${HAL_DRIVER_SRC_DIR}/stm32h7xx_ll_sdmmc.c # CAN / FDCAN ${HAL_DRIVER_SRC_DIR}/stm32h7xx_hal_fdcan.c diff --git a/Inc/ST-LIB.hpp b/Inc/ST-LIB.hpp index a9acb61f..cdf5e670 100644 --- a/Inc/ST-LIB.hpp +++ b/Inc/ST-LIB.hpp @@ -85,7 +85,8 @@ template struct BuildCtx { using DomainsCtx = BuildCtx; + DigitalInputDomain, + SdDomain /*, ADCDomain, PWMDomain, ...*/>; template struct Board { static consteval auto build_ctx() { @@ -101,23 +102,27 @@ template struct Board { } static consteval auto build() { + 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 doutN = domain_size(); constexpr std::size_t dinN = domain_size(); - constexpr std::size_t mpuN = domain_size(); + constexpr std::size_t sdN = domain_size(); // ... struct ConfigBundle { + std::array mpu_cfgs; std::array gpio_cfgs; std::array tim_cfgs; std::array dout_cfgs; std::array din_cfgs; - std::array mpu_cfgs; + std::array sd_cfgs; // ... }; return ConfigBundle{ + .mpu_cfgs = MPUDomain::template build( + ctx.template span()), .gpio_cfgs = GPIODomain::template build(ctx.template span()), .tim_cfgs = @@ -126,8 +131,8 @@ template struct Board { ctx.template span()), .din_cfgs = DigitalInputDomain::template build( ctx.template span()), - .mpu_cfgs = MPUDomain::template build( - ctx.template span()) + .sd_cfgs = SdDomain::template build( + ctx.template span()), // ... }; } @@ -135,20 +140,24 @@ template struct Board { static constexpr auto cfg = build(); static void init() { + 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 doutN = domain_size(); constexpr std::size_t dinN = domain_size(); - constexpr std::size_t mpuN = domain_size(); + constexpr std::size_t sdN = domain_size(); // ... + MPUDomain::Init::init(); GPIODomain::Init::init(cfg.gpio_cfgs); TimerDomain::Init::init(cfg.tim_cfgs); DigitalOutputDomain::Init::init(cfg.dout_cfgs, GPIODomain::Init::instances); DigitalInputDomain::Init::init(cfg.din_cfgs, GPIODomain::Init::instances); - MPUDomain::Init::init(); + SdDomain::Init::init(cfg.sd_cfgs, + MPUDomain::Init::instances, + DigitalInputDomain::Init::instances); // ... } diff --git a/Inc/ST-LIB_LOW/ST-LIB_LOW.hpp b/Inc/ST-LIB_LOW/ST-LIB_LOW.hpp index b73165c2..bc25ac7e 100644 --- a/Inc/ST-LIB_LOW/ST-LIB_LOW.hpp +++ b/Inc/ST-LIB_LOW/ST-LIB_LOW.hpp @@ -3,6 +3,8 @@ #include "ST-LIB_LOW/DigitalOutput2.hpp" #include "ST-LIB_LOW/DigitalInput2.hpp" +#include "ST-LIB_LOW/Sd/Sd.hpp" + #include "Clocks/Counter.hpp" #include "Clocks/Stopwatch.hpp" #include "Sensors/LinearSensor/LinearSensor.hpp" diff --git a/Inc/ST-LIB_LOW/Sd/Sd.hpp b/Inc/ST-LIB_LOW/Sd/Sd.hpp new file mode 100644 index 00000000..f946b98e --- /dev/null +++ b/Inc/ST-LIB_LOW/Sd/Sd.hpp @@ -0,0 +1,446 @@ +/* + * Sd.hpp + * + * Created on: 13 dec. 2025 + * Author: Boris + */ + +#ifndef SD_HPP +#define SD_HPP + +#include "stm32h7xx_hal.h" + +#include "ErrorHandler/ErrorHandler.hpp" +#include "HALAL/HALAL.hpp" +#include "ST-LIB_LOW/DigitalInput2.hpp" + +using ST_LIB::DigitalInputDomain; +using ST_LIB::GPIODomain; + +extern SD_HandleTypeDef* g_sdmmc1_handle; +extern SD_HandleTypeDef* g_sdmmc2_handle; +extern void* g_sdmmc1_instance_ptr; +extern void* g_sdmmc2_instance_ptr; + +namespace ST_LIB { +struct SdDomain { + + enum class Peripheral : uint32_t { + sdmmc1 = SDMMC1_BASE, + sdmmc2 = SDMMC2_BASE, + }; + + + struct Entry { + Peripheral peripheral; + std::size_t mpu_buffer0_idx; + std::size_t mpu_buffer1_idx; + std::optional> cd_pin_idx; // Card Detect pin index in GPIO domain, if any + std::optional> wp_pin_idx; // Write Protect pin index in GPIO domain, if any + std::size_t cmd_pin_idx; + std::size_t ck_pin_idx; + std::size_t d0_pin_idx; // Fixed for SDMMC2, configurable for SDMMC1 + std::size_t d1_pin_idx; // Fixed for SDMMC2, configurable for SDMMC1 + std::size_t d2_pin_idx; + std::size_t d3_pin_idx; + }; + + + template + struct SdCard { + using domain = SdDomain; + Entry e; + + Peripheral peripheral; + + MPUDomain::Buffer> buffer0; // Alignment of 32-bit for SDMMC DMA + MPUDomain::Buffer> buffer1; // Alignment of 32-bit for SDMMC DMA + + std::optional> cd; // Card Detect, if any, and its active state + std::optional> wp; // Write Protect, if any, and its active state + + GPIODomain::GPIO cmd; + GPIODomain::GPIO ck; + GPIODomain::GPIO d0; + GPIODomain::GPIO d1; + GPIODomain::GPIO d2; + GPIODomain::GPIO d3; + + /** + * @brief Construct a new SdCard + * @tparam buffer_blocks Number of 512-byte blocks for the MPU buffer + * @param sdmmc_peripheral The SDMMC peripheral to use (Peripheral::sdmmc1 or Peripheral::sdmmc2) + * @param card_detect_config Optional Card Detect pin (DigitalInputDomain::DigitalInput) and its active state, or nullopt for none + * @param write_protect_config Optional Write Protect pin (DigitalInputDomain::DigitalInput) and its active state, or nullopt for none + * @param d0_pin_for_sdmmc1 D0 pin to use if using SDMMC1 (default PC8, can also be PB13). + * @note The other pins (CMD, CK, D1, D2, D3) are fixed for each peripheral. + */ + consteval SdCard(Peripheral sdmmc_peripheral, + std::optional> card_detect_config, std::optional> write_protect_config, + GPIODomain::Pin d0_pin_for_sdmmc1 = ST_LIB::PC8) : + e{.peripheral = sdmmc_peripheral}, + peripheral(sdmmc_peripheral), + buffer0(MPUDomain::Buffer>(MPUDomain::MemoryType::NonCached, MPUDomain::MemoryDomain::D1)), + buffer1(MPUDomain::Buffer>(MPUDomain::MemoryType::NonCached, MPUDomain::MemoryDomain::D1)), + cd(card_detect_config), + wp(write_protect_config), + cmd((sdmmc_peripheral == Peripheral::sdmmc1) ? + GPIODomain::GPIO(ST_LIB::PD2, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12) : + GPIODomain::GPIO(ST_LIB::PD7, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF11)), + ck((sdmmc_peripheral == Peripheral::sdmmc1) ? + GPIODomain::GPIO(ST_LIB::PC12, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12) : + GPIODomain::GPIO(ST_LIB::PD6, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF11)), + d0((sdmmc_peripheral == Peripheral::sdmmc1) ? + GPIODomain::GPIO(d0_pin_for_sdmmc1, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12) : + GPIODomain::GPIO(ST_LIB::PB14, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF9)), + d1((sdmmc_peripheral == Peripheral::sdmmc1) ? + GPIODomain::GPIO(ST_LIB::PC9, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12) : + GPIODomain::GPIO(ST_LIB::PB15, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF9)), + d2((sdmmc_peripheral == Peripheral::sdmmc1) ? + GPIODomain::GPIO(ST_LIB::PC10, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12) : + GPIODomain::GPIO(ST_LIB::PG11, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF10)), + d3((sdmmc_peripheral == Peripheral::sdmmc1) ? + GPIODomain::GPIO(ST_LIB::PC11, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12) : + GPIODomain::GPIO(ST_LIB::PG12, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF10)) + { + if (sdmmc_peripheral != Peripheral::sdmmc1 && sdmmc_peripheral != Peripheral::sdmmc2) { + ST_LIB::compile_error("Invalid SDMMC peripheral"); + } + if ((d0_pin_for_sdmmc1.pin != ST_LIB::PC8.pin || d0_pin_for_sdmmc1.port != ST_LIB::PC8.port) + && + (d0_pin_for_sdmmc1.pin != ST_LIB::PB13.pin || d0_pin_for_sdmmc1.port != ST_LIB::PB13.port)) { + ST_LIB::compile_error("D0 pin can only be PC8 or PB13 for SDMMC1"); + } + if (buffer_blocks == 0) { + ST_LIB::compile_error("Buffer blocks must be greater than 0"); + } else if (buffer_blocks > 15) { + ST_LIB::compile_error("Buffer blocks must be less than or equal to 15"); + } + } + + + template + consteval std::size_t inscribe(Ctx &ctx) const { + Entry local_e = e; + + local_e.mpu_buffer0_idx = buffer0.inscribe(ctx); + local_e.mpu_buffer1_idx = buffer1.inscribe(ctx); + + if (cd.has_value()) { + local_e.cd_pin_idx = {cd.value().first.inscribe(ctx), cd.value().second}; + } + if (wp.has_value()) { + local_e.wp_pin_idx = {wp.value().first.inscribe(ctx), wp.value().second}; + } + + local_e.cmd_pin_idx = cmd.inscribe(ctx); + local_e.ck_pin_idx = ck.inscribe(ctx); + local_e.d0_pin_idx = d0.inscribe(ctx); + local_e.d1_pin_idx = d1.inscribe(ctx); + local_e.d2_pin_idx = d2.inscribe(ctx); + local_e.d3_pin_idx = d3.inscribe(ctx); + + return ctx.template add(local_e, this); + } + }; + + + static constexpr std::size_t max_instances = 2; + + + struct Config { + Peripheral peripheral; + std::size_t mpu_buffer0_idx; + std::size_t mpu_buffer1_idx; + std::optional> cd_pin_idx; + std::optional> wp_pin_idx; + std::size_t cmd_pin_idx; + std::size_t ck_pin_idx; + std::size_t d0_pin_idx; + std::size_t d1_pin_idx; + std::size_t d2_pin_idx; + std::size_t d3_pin_idx; + }; + + + template + static consteval std::array build(std::span entries) { + std::array cfgs{}; + if (N == 0 ) { + return cfgs; + } + bool peripheral_used[2] = {false, false}; // SDMMC1, SDMMC2 + + for (std::size_t i = 0; i < N; i++) { + const Entry &e = entries[i]; + + // Verify uniqueness of peripheral usage + std::size_t peripheral_index = (e.peripheral == Peripheral::sdmmc1) ? 0 : 1; + if (peripheral_used[peripheral_index]) ST_LIB::compile_error("SDMMC peripheral used multiple times in SdDomain"); + peripheral_used[peripheral_index] = true; + + cfgs[i].peripheral = e.peripheral; + cfgs[i].mpu_buffer0_idx = e.mpu_buffer0_idx; + cfgs[i].mpu_buffer1_idx = e.mpu_buffer1_idx; + cfgs[i].cd_pin_idx = e.cd_pin_idx; + cfgs[i].wp_pin_idx = e.wp_pin_idx; + cfgs[i].cmd_pin_idx = e.cmd_pin_idx; + cfgs[i].ck_pin_idx = e.ck_pin_idx; + cfgs[i].d0_pin_idx = e.d0_pin_idx; + cfgs[i].d1_pin_idx = e.d1_pin_idx; + cfgs[i].d2_pin_idx = e.d2_pin_idx; + cfgs[i].d3_pin_idx = e.d3_pin_idx; + } + + return cfgs; + } + + enum class BufferSelect : bool { + Buffer0 = false, + Buffer1 = true + }; + + template struct SdCardWrapper; + template struct Init; + + // State holder, logic is in SdCardWrapper + struct Instance { + template friend struct SdCardWrapper; + template friend struct Init; + + bool* operation_flag = nullptr; // External flag to indicate that an operation has finished. Only public so that it can be set in the public handlers below. + + // Public handlers called from C HAL callbacks. Don't use them, don't even think about using them. + void on_dma_read_complete(); + void on_dma_write_complete(); + void on_abort(); + void on_error(); + + private: + SD_HandleTypeDef hsd; + + MPUDomain::Instance* mpu_buffer0_instance; + MPUDomain::Instance* mpu_buffer1_instance; + + std::optional> cd_instance; + std::optional> wp_instance; + + bool card_initialized; + BufferSelect current_buffer; // The one that is currently available for CPU access and not being used by IDMA + + HAL_SD_CardInfoTypeDef card_info; + + // Functions + bool is_card_present(); + bool is_write_protected(); + bool is_busy(); + bool initialize_card(); + bool deinitialize_card(); + void switch_buffer(); + bool configure_idma(); + // Variation of HAL_SDEx_ReadBlocksDMAMultiBuffer to fit our needs + HAL_StatusTypeDef Not_HAL_SDEx_ReadBlocksDMAMultiBuffer(uint32_t BlockAdd, uint32_t NumberOfBlocks); + HAL_StatusTypeDef Not_HAL_SDEx_WriteBlocksDMAMultiBuffer(uint32_t BlockAdd, uint32_t NumberOfBlocks); + }; + + template + struct SdCardWrapper{ + static constexpr bool has_cd = card_request.cd.has_value(); + static constexpr bool has_wp = card_request.wp.has_value(); + + SdCardWrapper(Instance& instance) : instance(instance) { + check_cd_wp(); + }; + + void init_card() { + check_cd_wp(); + bool success = instance.initialize_card(); + if (!success) { + ErrorHandler("SD Card initialization failed"); + } + } + + void deinit_card() { + check_cd_wp(); + bool success = instance.deinitialize_card(); + if (!success) { + ErrorHandler("SD Card deinitialization failed"); + } + } + + bool is_card_initialized() { + return instance.card_initialized; + } + + bool read_blocks(uint32_t start_block, uint32_t num_blocks, bool* operation_complete_flag) { + check_cd_wp(); + if (!instance.card_initialized) { + ErrorHandler("SD Card not initialized"); + } + if (num_blocks > instance.mpu_buffer0_instance->size / 512) { + ErrorHandler("Too many blocks requested to read from SD"); + } + + if (HAL_SD_GetCardState(&instance.hsd) != HAL_SD_CARD_TRANSFER) { + return false; // Card not ready for data transfer + } + + // Won't use HAL_SDEx_ReadBlocksDMAMultiBuffer because it doesn't support double buffering the way we want + HAL_StatusTypeDef status = instance.Not_HAL_SDEx_ReadBlocksDMAMultiBuffer(start_block, num_blocks); + + if (status != HAL_OK) { + ErrorHandler("SD Card read operation failed"); + } + + instance.operation_flag = operation_complete_flag; + *operation_complete_flag = false; + + return true; + } + + bool write_blocks(uint32_t start_block, uint32_t num_blocks, bool* operation_complete_flag) { + check_cd_wp(); + if (!instance.card_initialized) { + ErrorHandler("SD Card not initialized"); + } + if (num_blocks > instance.mpu_buffer0_instance->size / 512) { + ErrorHandler("Too many blocks requested to write in SD"); + } + + if (HAL_SD_GetCardState(&instance.hsd) != HAL_SD_CARD_TRANSFER) { + return false; // Card not ready for data transfer + } + // Won't use HAL_SDEx_WriteBlocksDMAMultiBuffer because it doesn't support double buffering the way we want + HAL_StatusTypeDef status = instance.Not_HAL_SDEx_WriteBlocksDMAMultiBuffer(start_block, num_blocks); + + if (status != HAL_OK) { + ErrorHandler("SD Card write operation failed"); + } + + instance.operation_flag = operation_complete_flag; + *operation_complete_flag = false; + + return true; + } + + auto* get_current_buffer() { + if (instance.current_buffer == BufferSelect::Buffer0) { + return instance.mpu_buffer0_instance->template as(); + } else { + return instance.mpu_buffer1_instance->template as(); + } + } + + bool is_busy() { + return instance.is_busy(); + } + + private: + Instance& instance; // Actual State + + void check_cd_wp() { + if constexpr (has_cd) { + if (!instance.is_card_present()) { ErrorHandler("SD Card not present"); } + } + if constexpr (has_wp) { + if (instance.is_write_protected()) { ErrorHandler("SD Card is write-protected"); } + } + } + }; + + + template + struct Init { + static inline std::array instances{}; + + static void init(std::span cfgs, + std::span mpu_buffer_instances, + std::span digital_input_instances) { + + if (N == 0) { + return; + } + + for (std::size_t i = 0; i < N; i++) { + const auto &cfg = cfgs[i]; + auto &inst = instances[i]; + + inst.mpu_buffer0_instance = &mpu_buffer_instances[cfg.mpu_buffer0_idx]; + inst.mpu_buffer1_instance = &mpu_buffer_instances[cfg.mpu_buffer1_idx]; + + if (cfg.cd_pin_idx.has_value()) { + inst.cd_instance = {&digital_input_instances[cfg.cd_pin_idx.value().first], cfg.cd_pin_idx.value().second}; + } + if (cfg.wp_pin_idx.has_value()) { + inst.wp_instance = {&digital_input_instances[cfg.wp_pin_idx.value().first], cfg.wp_pin_idx.value().second}; + } + + if (cfg.peripheral == Peripheral::sdmmc1) { + inst.hsd.Instance = SDMMC1; + } else if (cfg.peripheral == Peripheral::sdmmc2) { + inst.hsd.Instance = SDMMC2; + } + inst.hsd.Init.ClockEdge = SDMMC_CLOCK_EDGE_FALLING; + inst.hsd.Init.ClockPowerSave = SDMMC_CLOCK_POWER_SAVE_DISABLE; + inst.hsd.Init.BusWide = SDMMC_BUS_WIDE_4B; + inst.hsd.Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_ENABLE; + uint32_t target_freq = 50000000; // Target frequency 50 MHz + + #ifdef SD_DEBUG_ENABLE + inst.hsd.Init.BusWide = SDMMC_BUS_WIDE_1B; // For debugging, use 1-bit bus + target_freq = 400000; // For debugging, use 400 kHz + #endif // SD_DEBUG_ENABLE + + PLL1_ClocksTypeDef pll1_clock; + HAL_RCCEx_GetPLL1ClockFreq(&pll1_clock); + uint32_t sdmmc_clk = pll1_clock.PLL1_Q_Frequency; + uint32_t target_div = (sdmmc_clk + target_freq - 1) / target_freq; // Target divider rounded up (target_freq is the maximum frequency) + uint32_t translated_clock_div; // sdmmc_ker_ck / [2 * CLKDIV] + if (target_div == 0) translated_clock_div = 0; // Special case for 0 (no division) + else translated_clock_div = (target_div+1) / 2; // Round up to ensure frequency is not higher than target + + if (translated_clock_div > 1023) { + ErrorHandler("SDMMC clock divider too high, cannot achieve target frequency with current PLL1 Q clock"); + } + + inst.hsd.Init.ClockDiv = translated_clock_div; + + if (cfg.peripheral == Peripheral::sdmmc1) { + g_sdmmc1_handle = &inst.hsd; + g_sdmmc1_instance_ptr = &inst; + } else if (cfg.peripheral == Peripheral::sdmmc2) { + g_sdmmc2_handle = &inst.hsd; + g_sdmmc2_instance_ptr = &inst; + } + + inst.card_initialized = false; + inst.current_buffer = BufferSelect::Buffer0; + } + + // Initialize HAL SD + RCC_PeriphCLKInitTypeDef RCC_PeriphCLKInitStruct; + RCC_PeriphCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SDMMC; + RCC_PeriphCLKInitStruct.SdmmcClockSelection = RCC_SDMMCCLKSOURCE_PLL; + if (HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphCLKInitStruct) != HAL_OK) { + ErrorHandler("SDMMC clock configuration failed, maybe try with a slower clock or higher divider?"); + } + + // Ensure PLL1Q output is enabled + __HAL_RCC_PLLCLKOUT_ENABLE(RCC_PLL1_DIVQ); + + if (HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SDMMC) == 0) { + ErrorHandler("SDMMC clock frequency is 0"); + } + } + }; +}; + +static inline SdDomain::Instance* get_sd_instance(SD_HandleTypeDef* hsd) { + if (hsd == g_sdmmc1_handle) return static_cast(g_sdmmc1_instance_ptr); + if (hsd == g_sdmmc2_handle) return static_cast(g_sdmmc2_instance_ptr); + return nullptr; +} + +} // namespace ST_LIB + +#endif // SD_HPP \ No newline at end of file diff --git a/Src/ST-LIB_LOW/Sd/Sd.cpp b/Src/ST-LIB_LOW/Sd/Sd.cpp new file mode 100644 index 00000000..e337fd0f --- /dev/null +++ b/Src/ST-LIB_LOW/Sd/Sd.cpp @@ -0,0 +1,291 @@ +#include "ST-LIB_LOW/Sd/Sd.hpp" + +SD_HandleTypeDef* g_sdmmc1_handle = nullptr; +SD_HandleTypeDef* g_sdmmc2_handle = nullptr; +void* g_sdmmc1_instance_ptr = nullptr; +void* g_sdmmc2_instance_ptr = nullptr; + +using namespace ST_LIB; + +void SdDomain::Instance::on_dma_read_complete() { + if (operation_flag) { + *operation_flag = true; + operation_flag = nullptr; + } + SDMMC_CmdStopTransfer(hsd.Instance); +} + +void SdDomain::Instance::on_dma_write_complete() { + if (operation_flag) { + *operation_flag = true; + operation_flag = nullptr; + } + SDMMC_CmdStopTransfer(hsd.Instance); +} + +void SdDomain::Instance::on_abort() { ErrorHandler("SD Card operation aborted"); } + +void SdDomain::Instance::on_error() { ErrorHandler("SD Card error occurred"); } + +bool SdDomain::Instance::is_card_present() { return cd_instance->first->read() == cd_instance->second; } +bool SdDomain::Instance::is_write_protected() { return wp_instance->first->read() == wp_instance->second; } + +bool SdDomain::Instance::is_busy() { + if (!card_initialized) return false; + return (hsd.State == HAL_SD_STATE_BUSY) || (hsd.State == HAL_SD_STATE_PROGRAMMING) || (hsd.State == HAL_SD_STATE_RECEIVING); +} + +bool SdDomain::Instance::initialize_card() { + if (card_initialized) { return true; } // Already initialized + + HAL_StatusTypeDef status = HAL_SD_Init(&hsd); + if (status != HAL_OK) { + return false; + } + + if (HAL_SD_GetCardInfo(&hsd, &card_info) != HAL_OK) { + return false; + } + + if (!configure_idma()) { + return false; + } + + if (HAL_SD_ConfigSpeedBusOperation(&hsd, SDMMC_SPEED_MODE_AUTO) != HAL_OK) { + return false; + } + + card_initialized = true; + return true; +} + +bool SdDomain::Instance::deinitialize_card() { + if (!card_initialized) { return true; } // Already deinitialized + + HAL_StatusTypeDef status = HAL_SD_DeInit(&hsd); + if (status != HAL_OK) { + return false; + } + + card_initialized = false; + return true; +} + +void SdDomain::Instance::switch_buffer() { + current_buffer = (current_buffer == BufferSelect::Buffer0) ? BufferSelect::Buffer1 : BufferSelect::Buffer0; +} + +bool SdDomain::Instance::configure_idma() { + HAL_StatusTypeDef status = HAL_SDEx_ConfigDMAMultiBuffer(&hsd, + reinterpret_cast(mpu_buffer0_instance->ptr), + reinterpret_cast(mpu_buffer1_instance->ptr), + mpu_buffer0_instance->size / 512); // Number of 512B-blocks + + if (status != HAL_OK) { return false; } + return true; +} + +HAL_StatusTypeDef SdDomain::Instance::Not_HAL_SDEx_ReadBlocksDMAMultiBuffer(uint32_t BlockAdd, uint32_t NumberOfBlocks) { + SDMMC_DataInitTypeDef config; + uint32_t errorstate; + uint32_t DmaBase0_reg; + uint32_t DmaBase1_reg; + uint32_t add = BlockAdd; + + if (hsd.State == HAL_SD_STATE_READY) { + if ((add + NumberOfBlocks) > (hsd.SdCard.LogBlockNbr)) { + hsd.ErrorCode |= HAL_SD_ERROR_ADDR_OUT_OF_RANGE; + return HAL_ERROR; + } + + DmaBase0_reg = hsd.Instance->IDMABASE0; + DmaBase1_reg = hsd.Instance->IDMABASE1; + + if ((hsd.Instance->IDMABSIZE == 0U) || (DmaBase0_reg == 0U) || (DmaBase1_reg == 0U)) { + hsd.ErrorCode = HAL_SD_ERROR_ADDR_OUT_OF_RANGE; + return HAL_ERROR; + } + + /* Initialize data control register */ + hsd.Instance->DCTRL = 0; + /* Clear old Flags*/ + __HAL_SD_CLEAR_FLAG(&hsd, SDMMC_STATIC_DATA_FLAGS); + + hsd.ErrorCode = HAL_SD_ERROR_NONE; + hsd.State = HAL_SD_STATE_BUSY; + + if (hsd.SdCard.CardType != CARD_SDHC_SDXC) { + add *= 512U; + } + + /* Configure the SD DPSM (Data Path State Machine) */ + config.DataTimeOut = SDMMC_DATATIMEOUT; + config.DataLength = BLOCKSIZE * NumberOfBlocks; + config.DataBlockSize = SDMMC_DATABLOCK_SIZE_512B; + config.TransferDir = SDMMC_TRANSFER_DIR_TO_SDMMC; + config.TransferMode = SDMMC_TRANSFER_MODE_BLOCK; + config.DPSM = SDMMC_DPSM_DISABLE; + (void)SDMMC_ConfigData(hsd.Instance, &config); + + // hsd.Instance->DCTRL |= SDMMC_DCTRL_FIFORST; + + __SDMMC_CMDTRANS_ENABLE(hsd.Instance); + if (current_buffer == BufferSelect::Buffer1) { + hsd.Instance->IDMACTRL = SDMMC_ENABLE_IDMA_DOUBLE_BUFF1; + } else { + hsd.Instance->IDMACTRL = SDMMC_ENABLE_IDMA_DOUBLE_BUFF0; + } + switch_buffer(); + + /* Read Blocks in DMA mode */ + hsd.Context = (SD_CONTEXT_READ_MULTIPLE_BLOCK | SD_CONTEXT_DMA); + + /* Read Multi Block command */ + errorstate = SDMMC_CmdReadMultiBlock(hsd.Instance, add); + if (errorstate != HAL_SD_ERROR_NONE) { + hsd.State = HAL_SD_STATE_READY; + hsd.ErrorCode |= errorstate; + return HAL_ERROR; + } + + __HAL_SD_ENABLE_IT(&hsd, (SDMMC_IT_DCRCFAIL | SDMMC_IT_DTIMEOUT | SDMMC_IT_RXOVERR | SDMMC_IT_DATAEND | + SDMMC_IT_IDMABTC)); + + return HAL_OK; + } + else { + return HAL_BUSY; + } +} + +HAL_StatusTypeDef SdDomain::Instance::Not_HAL_SDEx_WriteBlocksDMAMultiBuffer(uint32_t BlockAdd, uint32_t NumberOfBlocks) { + SDMMC_DataInitTypeDef config; + uint32_t errorstate; + uint32_t DmaBase0_reg; + uint32_t DmaBase1_reg; + uint32_t add = BlockAdd; + + if (hsd.State == HAL_SD_STATE_READY) { + if ((add + NumberOfBlocks) > (hsd.SdCard.LogBlockNbr)) { + hsd.ErrorCode |= HAL_SD_ERROR_ADDR_OUT_OF_RANGE; + return HAL_ERROR; + } + + DmaBase0_reg = hsd.Instance->IDMABASE0; + DmaBase1_reg = hsd.Instance->IDMABASE1; + if ((hsd.Instance->IDMABSIZE == 0U) || (DmaBase0_reg == 0U) || (DmaBase1_reg == 0U)) { + hsd.ErrorCode = HAL_SD_ERROR_ADDR_OUT_OF_RANGE; + return HAL_ERROR; + } + + /* Initialize data control register */ + hsd.Instance->DCTRL = 0; + + hsd.ErrorCode = HAL_SD_ERROR_NONE; + + hsd.State = HAL_SD_STATE_BUSY; + if (hsd.SdCard.CardType != CARD_SDHC_SDXC) { + add *= 512U; + } + + /* Configure the SD DPSM (Data Path State Machine) */ + config.DataTimeOut = SDMMC_DATATIMEOUT; + config.DataLength = BLOCKSIZE * NumberOfBlocks; + config.DataBlockSize = SDMMC_DATABLOCK_SIZE_512B; + config.TransferDir = SDMMC_TRANSFER_DIR_TO_CARD; + config.TransferMode = SDMMC_TRANSFER_MODE_BLOCK; + config.DPSM = SDMMC_DPSM_DISABLE; + (void)SDMMC_ConfigData(hsd.Instance, &config); + + // hsd.Instance->DCTRL |= SDMMC_DCTRL_FIFORST; + + __SDMMC_CMDTRANS_ENABLE(hsd.Instance); + + if (current_buffer == BufferSelect::Buffer1) { + hsd.Instance->IDMACTRL = SDMMC_ENABLE_IDMA_DOUBLE_BUFF1; + } else { + hsd.Instance->IDMACTRL = SDMMC_ENABLE_IDMA_DOUBLE_BUFF0; + } + switch_buffer(); + + /* Write Blocks in DMA mode */ + hsd.Context = (SD_CONTEXT_WRITE_MULTIPLE_BLOCK | SD_CONTEXT_DMA); + + /* Write Multi Block command */ + errorstate = SDMMC_CmdWriteMultiBlock(hsd.Instance, add); + if (errorstate != HAL_SD_ERROR_NONE) { + hsd.State = HAL_SD_STATE_READY; + hsd.ErrorCode |= errorstate; + return HAL_ERROR; + } + + __HAL_SD_ENABLE_IT(&hsd, (SDMMC_IT_DCRCFAIL | SDMMC_IT_DTIMEOUT | SDMMC_IT_TXUNDERR | SDMMC_IT_DATAEND | + SDMMC_IT_IDMABTC)); + + return HAL_OK; + } + else { + return HAL_BUSY; + } +} + + +extern "C" { + +void HAL_SD_MspInit(SD_HandleTypeDef* hsd) { + if (hsd->Instance == SDMMC1) { + __HAL_RCC_SDMMC1_CLK_ENABLE(); + __HAL_RCC_SDMMC1_FORCE_RESET(); + __HAL_RCC_SDMMC1_RELEASE_RESET(); + HAL_NVIC_SetPriority(SDMMC1_IRQn, 5, 0); + HAL_NVIC_EnableIRQ(SDMMC1_IRQn); + } else { + __HAL_RCC_SDMMC2_CLK_ENABLE(); + __HAL_RCC_SDMMC2_FORCE_RESET(); + __HAL_RCC_SDMMC2_RELEASE_RESET(); + HAL_NVIC_SetPriority(SDMMC2_IRQn, 5, 0); + HAL_NVIC_EnableIRQ(SDMMC2_IRQn); + } +} + +void SDMMC1_IRQHandler(void) { + if (g_sdmmc1_handle != nullptr) { + HAL_SD_IRQHandler(g_sdmmc1_handle); + } +} + +void SDMMC2_IRQHandler(void) { + if (g_sdmmc2_handle != nullptr) { + HAL_SD_IRQHandler(g_sdmmc2_handle); + } +} + +void HAL_SD_TxCpltCallback(SD_HandleTypeDef* hsd) { + if (auto sd_instance = ST_LIB::get_sd_instance(hsd)) { + sd_instance->on_dma_write_complete(); + } +} + +void HAL_SD_RxCpltCallback(SD_HandleTypeDef* hsd) { + if (auto sd_instance = ST_LIB::get_sd_instance(hsd)) { + sd_instance->on_dma_read_complete(); + } +} + +void HAL_SD_AbortCallback(SD_HandleTypeDef* hsd) { + if (auto sd_instance = ST_LIB::get_sd_instance(hsd)) { + sd_instance->on_abort(); + } else { + ErrorHandler("SD Card operation aborted"); + } +} + +void HAL_SD_ErrorCallback(SD_HandleTypeDef* hsd) { + if (auto sd_instance = ST_LIB::get_sd_instance(hsd)) { + sd_instance->on_error(); + } else { + ErrorHandler("SD Card error occurred"); + } +} + +} // extern "C" \ No newline at end of file