diff --git a/config/mbed_app.json b/config/mbed_app.json index f9bb933cae..d65055a6ce 100644 --- a/config/mbed_app.json +++ b/config/mbed_app.json @@ -4,6 +4,14 @@ "USE_HAL_JPEG_REGISTER_CALLBACKS": { "macro_name": "USE_HAL_JPEG_REGISTER_CALLBACKS", "value": "1U" + }, + "USE_HAL_TIM_REGISTER_CALLBACKS": { + "macro_name": "USE_HAL_TIM_REGISTER_CALLBACKS", + "value": "1U" + }, + "USE_HAL_DAC_REGISTER_CALLBACKS": { + "macro_name": "USE_HAL_DAC_REGISTER_CALLBACKS", + "value": "1U" } }, "target_overrides": { diff --git a/drivers/CMakeLists.txt b/drivers/CMakeLists.txt index 4282cae5cf..daa3090cc5 100644 --- a/drivers/CMakeLists.txt +++ b/drivers/CMakeLists.txt @@ -6,6 +6,7 @@ add_subdirectory(${DRIVERS_DIR}/CoreBufferedSerial) add_subdirectory(${DRIVERS_DIR}/CoreEventFlags) add_subdirectory(${DRIVERS_DIR}/CoreEventQueue) +add_subdirectory(${DRIVERS_DIR}/CoreDAC) add_subdirectory(${DRIVERS_DIR}/CoreI2C) add_subdirectory(${DRIVERS_DIR}/CoreInterruptIn) add_subdirectory(${DRIVERS_DIR}/CoreLL) diff --git a/drivers/CoreDAC/CMakeLists.txt b/drivers/CoreDAC/CMakeLists.txt new file mode 100644 index 0000000000..c2e26193d8 --- /dev/null +++ b/drivers/CoreDAC/CMakeLists.txt @@ -0,0 +1,28 @@ +# Leka - LekaOS +# Copyright 2024 APF France handicap +# SPDX-License-Identifier: Apache-2.0 + +add_library(CoreDAC STATIC) + +target_include_directories(CoreDAC + PUBLIC + include +) + +target_sources(CoreDAC + PRIVATE + source/CoreSTM32HalBasicTimer.cpp + source/CoreDAC.cpp +) + +if(NOT(${CMAKE_PROJECT_NAME} STREQUAL "LekaOSUnitTests")) + target_sources(CoreDAC + PUBLIC + source/HAL_IRQHandlers.cpp + ) +endif() + +target_link_libraries(CoreDAC + mbed-os + CoreSTM32Hal +) diff --git a/drivers/CoreDAC/include/CoreDAC.h b/drivers/CoreDAC/include/CoreDAC.h new file mode 100644 index 0000000000..a04818033f --- /dev/null +++ b/drivers/CoreDAC/include/CoreDAC.h @@ -0,0 +1,49 @@ +// Leka - LekaOS +// Copyright 2024 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include +#include + +#include "interface/STM32HalBasicTimer.h" +#include "interface/drivers/DAC.h" +#include "interface/drivers/STM32Hal.h" + +namespace leka { + +class CoreDAC : public interface::DACBase +{ + public: + CoreDAC(interface::STM32Hal &hal, interface::STM32HalBasicTimer &hal_timer); + + [[nodiscard]] auto getHandle() -> DAC_HandleTypeDef & final; + + void initialize() final; + void terminate() final; + + void registerDataToPlay(std::span data); + void registerDMACallbacks(std::function const &on_half_transfer, + std::function const &on_complete_transfer); + + void start() final; + void stop() final; + + private: + void _registerMspCallbacks(); + void _initializeDMA(); + + interface::STM32Hal &_hal; + interface::STM32HalBasicTimer &_hal_timer; + + DAC_HandleTypeDef _hdac {}; + DMA_HandleTypeDef _hdma {}; + + std::span _data; + std::function _on_half_transfer {}; + std::function _on_complete_transfer {}; +}; + +} // namespace leka diff --git a/drivers/CoreDAC/include/CoreSTM32HalBasicTimer.h b/drivers/CoreDAC/include/CoreSTM32HalBasicTimer.h new file mode 100644 index 0000000000..94c3b3f1d6 --- /dev/null +++ b/drivers/CoreDAC/include/CoreSTM32HalBasicTimer.h @@ -0,0 +1,41 @@ +// Leka - LekaOS +// Copyright 2024 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include + +#include "interface/STM32HalBasicTimer.h" + +namespace leka { + +class CoreSTM32HalBasicTimer : public interface::STM32HalBasicTimer +{ + static constexpr float DEFAULT_AUDIO_FILE_SAMPLE_RATE = 44'100; + + public: + CoreSTM32HalBasicTimer(interface::STM32Hal &hal); + + [[nodiscard]] auto getHandle() -> TIM_HandleTypeDef & final; + + void registerCallback(std::function const &callback); + void linkDACTimer(DAC_ChannelConfTypeDef *config) final; + + void initialize(float frequency = DEFAULT_AUDIO_FILE_SAMPLE_RATE) final; + void terminate() final; + + void start() final; + void stop() final; + + private: + void _registerMspCallbacks(); + + interface::STM32Hal &_hal; + + TIM_HandleTypeDef _htim {}; + + std::function _callback {}; +}; + +} // namespace leka diff --git a/drivers/CoreDAC/include/interface/STM32HalBasicTimer.h b/drivers/CoreDAC/include/interface/STM32HalBasicTimer.h new file mode 100644 index 0000000000..73fee09ee3 --- /dev/null +++ b/drivers/CoreDAC/include/interface/STM32HalBasicTimer.h @@ -0,0 +1,27 @@ +// Leka - LekaOS +// Copyright 2024 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "interface/drivers/STM32Hal.h" + +namespace leka::interface { + +class STM32HalBasicTimer +{ + public: + virtual ~STM32HalBasicTimer() = default; + + [[nodiscard]] virtual auto getHandle() -> TIM_HandleTypeDef & = 0; + + virtual void linkDACTimer(DAC_ChannelConfTypeDef *config) = 0; + + virtual void initialize(float frequency) = 0; + virtual void terminate() = 0; + + virtual void start() = 0; + virtual void stop() = 0; +}; + +} // namespace leka::interface diff --git a/drivers/CoreDAC/source/CoreDAC.cpp b/drivers/CoreDAC/source/CoreDAC.cpp new file mode 100644 index 0000000000..c3a28f899e --- /dev/null +++ b/drivers/CoreDAC/source/CoreDAC.cpp @@ -0,0 +1,116 @@ +// Leka - LekaOS +// Copyright 2024 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#include "CoreDAC.h" + +using namespace leka; + +CoreDAC::CoreDAC(interface::STM32Hal &hal, interface::STM32HalBasicTimer &hal_timer) : _hal(hal), _hal_timer(hal_timer) +{ + _hdac.Instance = DAC; +} + +auto CoreDAC::getHandle() -> DAC_HandleTypeDef & +{ + return _hdac; +} + +void CoreDAC::initialize() +{ + _registerMspCallbacks(); + + _hal.HAL_DAC_Init(&_hdac); + + DAC_ChannelConfTypeDef config = {}; + config.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE; + _hal_timer.linkDACTimer(&config); + _hal.HAL_DAC_ConfigChannel(&_hdac, &config, DAC_CHANNEL_1); + + static const auto &self = *this; + _hal.HAL_DAC_RegisterCallback(&_hdac, HAL_DAC_CH1_HALF_COMPLETE_CB_ID, + []([[maybe_unused]] DAC_HandleTypeDef *hdac) { + if (self._on_half_transfer != nullptr) { + self._on_half_transfer(); + } + }); + _hal.HAL_DAC_RegisterCallback(&_hdac, HAL_DAC_CH1_COMPLETE_CB_ID, []([[maybe_unused]] DAC_HandleTypeDef *hdac) { + if (self._on_complete_transfer != nullptr) { + self._on_complete_transfer(); + } + }); +} + +void CoreDAC::terminate() +{ + _hal.HAL_DAC_DeInit(&_hdac); +} + +void CoreDAC::registerDataToPlay(std::span data) +{ + _data = data; +} + +void CoreDAC::registerDMACallbacks(std::function const &on_half_transfer, + std::function const &on_complete_transfer) +{ + _on_half_transfer = on_half_transfer; + _on_complete_transfer = on_complete_transfer; +} + +void CoreDAC::start() +{ + _hal_timer.start(); + _hal.HAL_DAC_Start_DMA(&_hdac, DAC_CHANNEL_1, reinterpret_cast(_data.data()), _data.size(), + DAC_ALIGN_12B_R); +} + +void CoreDAC::stop() +{ + _hal.HAL_DAC_Stop_DMA(&_hdac, DAC_CHANNEL_1); +} + +void CoreDAC::_registerMspCallbacks() +{ + static auto &self = *this; + + _hal.HAL_DAC_RegisterCallback(&_hdac, HAL_DAC_MSPINIT_CB_ID, []([[maybe_unused]] DAC_HandleTypeDef *hdac) { + __HAL_LINKDMA(&self._hdac, DMA_Handle1, self._hdma); + self._initializeDMA(); + + self._hal.HAL_RCC_DAC_CLK_ENABLE(); + }); + + _hal.HAL_DAC_RegisterCallback(&_hdac, HAL_DAC_MSPDEINIT_CB_ID, []([[maybe_unused]] DAC_HandleTypeDef *hdac) { + self._hal.HAL_DMA_DeInit(&self._hdma); + + self._hal.HAL_RCC_DAC_CLK_DISABLE(); + }); +} + +void CoreDAC::_initializeDMA() +{ + _hal.HAL_RCC_DMA1_CLK_ENABLE(); + + _hal.HAL_NVIC_SetPriority(DMA1_Stream5_IRQn, 3, 0); + _hal.HAL_NVIC_EnableIRQ(DMA1_Stream5_IRQn); + + _hdma.Instance = DMA1_Stream5; // DMA1_Stream5 is the only DMA channel for DAC + + _hdma.Init.Channel = DMA_CHANNEL_7; + _hdma.Init.Direction = DMA_MEMORY_TO_PERIPH; + _hdma.Init.PeriphInc = DMA_PINC_DISABLE; + _hdma.Init.MemInc = DMA_MINC_ENABLE; + _hdma.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; + _hdma.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; + _hdma.Init.Mode = DMA_CIRCULAR; + _hdma.Init.Priority = DMA_PRIORITY_LOW; + _hdma.Init.FIFOMode = DMA_FIFOMODE_ENABLE; + _hdma.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_HALFFULL; + _hdma.Init.MemBurst = DMA_MBURST_SINGLE; + // Single mem burst is more ressource consuming than 4 burst or more + // However the buffer apparently needs to be of a size multiple of the burst mode chosen + _hdma.Init.PeriphBurst = DMA_PBURST_SINGLE; + + _hal.HAL_DMA_Init(&_hdma); +} diff --git a/drivers/CoreDAC/source/CoreSTM32HalBasicTimer.cpp b/drivers/CoreDAC/source/CoreSTM32HalBasicTimer.cpp new file mode 100644 index 0000000000..bb54da1113 --- /dev/null +++ b/drivers/CoreDAC/source/CoreSTM32HalBasicTimer.cpp @@ -0,0 +1,86 @@ +// Leka - LekaOS +// Copyright 2024 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#include "CoreSTM32HalBasicTimer.h" +#include + +using namespace leka; + +CoreSTM32HalBasicTimer::CoreSTM32HalBasicTimer(interface::STM32Hal &hal) : _hal(hal) +{ + _htim.Instance = TIM6; +} + +auto CoreSTM32HalBasicTimer::getHandle() -> TIM_HandleTypeDef & +{ + return _htim; +} + +void CoreSTM32HalBasicTimer::registerCallback(std::function const &callback) +{ + _callback = callback; +} + +void CoreSTM32HalBasicTimer::initialize(float frequency) +{ + _registerMspCallbacks(); + + // CK_Timer = CK_INT / ((Prescaler + 1) * (Period + 1)) + const auto CK_INT = float {108'000'000.0}; + auto divider = static_cast(std::round(CK_INT / frequency)); + + _htim.Init.Prescaler = 0; + _htim.Init.Period = divider - 1; // ? min 1 + _htim.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; + _hal.HAL_TIM_Base_Init(&_htim); + + auto timerMasterConfig = TIM_MasterConfigTypeDef {}; + timerMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE; + _hal.HAL_TIMEx_MasterConfigSynchronization(&_htim, &timerMasterConfig); + + static const auto &self = *this; + _hal.HAL_TIM_RegisterCallback(&_htim, HAL_TIM_PERIOD_ELAPSED_CB_ID, []([[maybe_unused]] TIM_HandleTypeDef *htim) { + if (self._callback != nullptr) { + self._callback(); + } + }); +} + +void CoreSTM32HalBasicTimer::_registerMspCallbacks() +{ + static const auto &self = *this; + + _hal.HAL_TIM_RegisterCallback(&_htim, HAL_TIM_BASE_MSPINIT_CB_ID, []([[maybe_unused]] TIM_HandleTypeDef *htim) { + self._hal.HAL_RCC_TIM6_CLK_ENABLE(); + + self._hal.HAL_NVIC_SetPriority(TIM6_DAC_IRQn, 0x00, 0x00); + self._hal.HAL_NVIC_EnableIRQ(TIM6_DAC_IRQn); + }); + + _hal.HAL_TIM_RegisterCallback(&_htim, HAL_TIM_BASE_MSPDEINIT_CB_ID, []([[maybe_unused]] TIM_HandleTypeDef *htim) { + self._hal.HAL_RCC_TIM6_CLK_DISABLE(); + }); +} + +void CoreSTM32HalBasicTimer::linkDACTimer(DAC_ChannelConfTypeDef *config) +{ + if (config != nullptr) { + config->DAC_Trigger = DAC_TRIGGER_T6_TRGO; + } +} + +void CoreSTM32HalBasicTimer::terminate() +{ + _hal.HAL_TIM_Base_DeInit(&_htim); +} + +void CoreSTM32HalBasicTimer::start() +{ + _hal.HAL_TIM_Base_Start_IT(&_htim); +} + +void CoreSTM32HalBasicTimer::stop() +{ + _hal.HAL_TIM_Base_Stop_IT(&_htim); +} diff --git a/drivers/CoreDAC/source/HAL_IRQHandlers.cpp b/drivers/CoreDAC/source/HAL_IRQHandlers.cpp new file mode 100644 index 0000000000..31ae2946df --- /dev/null +++ b/drivers/CoreDAC/source/HAL_IRQHandlers.cpp @@ -0,0 +1,24 @@ +#include "CoreDAC.h" +#include "CoreSTM32HalBasicTimer.h" + +extern "C" { + +extern leka::CoreSTM32HalBasicTimer hal_timer; +extern leka::CoreDAC coredac; + +void TIM6_DAC_IRQHandler() +{ + HAL_TIM_IRQHandler(&hal_timer.getHandle()); +} + +void TIM7_DAC_IRQHandler() +{ + HAL_TIM_IRQHandler(&hal_timer.getHandle()); +} + +void DMA1_Stream5_IRQHandler() +{ + HAL_DMA_IRQHandler(coredac.getHandle().DMA_Handle1); +} + +} // extern "C" diff --git a/drivers/CoreDAC/tests/mocks/STM32HalTimer.h b/drivers/CoreDAC/tests/mocks/STM32HalTimer.h new file mode 100644 index 0000000000..c374de18ac --- /dev/null +++ b/drivers/CoreDAC/tests/mocks/STM32HalTimer.h @@ -0,0 +1,26 @@ +// Leka - LekaOS +// Copyright 2024 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "gmock/gmock.h" +#include "interface/STM32HalBasicTimer.h" + +namespace leka::mock { + +class STM32HalTimer : public interface::STM32HalBasicTimer +{ + public: + MOCK_METHOD(TIM_HandleTypeDef &, getHandle, (), (override)); + + MOCK_METHOD(void, linkDACTimer, (DAC_ChannelConfTypeDef *), (override)); + + MOCK_METHOD(void, initialize, (float), (override)); + MOCK_METHOD(void, terminate, (), (override)); + + MOCK_METHOD(void, start, (), (override)); + MOCK_METHOD(void, stop, (), (override)); +}; + +} // namespace leka::mock diff --git a/drivers/CoreSTM32Hal/include/CoreSTM32Hal.h b/drivers/CoreSTM32Hal/include/CoreSTM32Hal.h index 7daa41772a..2453a066f8 100644 --- a/drivers/CoreSTM32Hal/include/CoreSTM32Hal.h +++ b/drivers/CoreSTM32Hal/include/CoreSTM32Hal.h @@ -22,10 +22,19 @@ class CoreSTM32Hal : public interface::STM32Hal void HAL_RCC_GPIOI_CLK_ENABLE() final; void HAL_RCC_GPIOJ_CLK_ENABLE() final; + void HAL_RCC_TIM6_CLK_ENABLE() final; + void HAL_RCC_TIM6_CLK_DISABLE() final; + void HAL_RCC_TIM7_CLK_ENABLE() final; + void HAL_RCC_TIM7_CLK_DISABLE() final; + void HAL_RCC_FMC_CLK_ENABLE() final; + void HAL_RCC_DMA1_CLK_ENABLE() final; void HAL_RCC_DMA2_CLK_ENABLE() final; + void HAL_RCC_DAC_CLK_ENABLE() final; + void HAL_RCC_DAC_CLK_DISABLE() final; + void HAL_RCC_JPEG_CLK_ENABLE() final; void HAL_RCC_JPEG_FORCE_RESET() final; void HAL_RCC_JPEG_RELEASE_RESET() final; @@ -104,6 +113,25 @@ class CoreSTM32Hal : public interface::STM32Hal auto HAL_JPEG_Pause(JPEG_HandleTypeDef *hjpeg, uint32_t XferSelection) -> HAL_StatusTypeDef final; auto HAL_JPEG_Resume(JPEG_HandleTypeDef *hjpeg, uint32_t XferSelection) -> HAL_StatusTypeDef final; + + auto HAL_TIM_Base_Init(TIM_HandleTypeDef *htim) -> HAL_StatusTypeDef final; + auto HAL_TIMEx_MasterConfigSynchronization(TIM_HandleTypeDef *htim, TIM_MasterConfigTypeDef *sMasterConfig) + -> HAL_StatusTypeDef final; + auto HAL_TIM_RegisterCallback(TIM_HandleTypeDef *htim, HAL_TIM_CallbackIDTypeDef CallbackID, + pTIM_CallbackTypeDef pCallback) -> HAL_StatusTypeDef final; + auto HAL_TIM_Base_Start_IT(TIM_HandleTypeDef *htim) -> HAL_StatusTypeDef final; + auto HAL_TIM_Base_Stop_IT(TIM_HandleTypeDef *htim) -> HAL_StatusTypeDef final; + auto HAL_TIM_Base_DeInit(TIM_HandleTypeDef *htim) -> HAL_StatusTypeDef final; + + auto HAL_DAC_Init(DAC_HandleTypeDef *hdac) -> HAL_StatusTypeDef final; + auto HAL_DAC_ConfigChannel(DAC_HandleTypeDef *hdac, DAC_ChannelConfTypeDef *sConfig, uint32_t Channel) + -> HAL_StatusTypeDef final; + auto HAL_DAC_RegisterCallback(DAC_HandleTypeDef *hdac, HAL_DAC_CallbackIDTypeDef CallbackID, + pDAC_CallbackTypeDef pCallback) -> HAL_StatusTypeDef final; + auto HAL_DAC_Start_DMA(DAC_HandleTypeDef *hdac, uint32_t Channel, uint32_t *pData, uint32_t Length, + uint32_t Alignment) -> HAL_StatusTypeDef final; + auto HAL_DAC_Stop_DMA(DAC_HandleTypeDef *hdac, uint32_t Channel) -> HAL_StatusTypeDef final; + auto HAL_DAC_DeInit(DAC_HandleTypeDef *hdac) -> HAL_StatusTypeDef final; }; } // namespace leka diff --git a/drivers/CoreSTM32Hal/source/CoreSTM32Hal.cpp b/drivers/CoreSTM32Hal/source/CoreSTM32Hal.cpp index c9352501ad..2beb724e2a 100644 --- a/drivers/CoreSTM32Hal/source/CoreSTM32Hal.cpp +++ b/drivers/CoreSTM32Hal/source/CoreSTM32Hal.cpp @@ -41,16 +41,51 @@ void CoreSTM32Hal::HAL_RCC_GPIOJ_CLK_ENABLE() __HAL_RCC_GPIOJ_CLK_ENABLE(); // NOLINT } +void CoreSTM32Hal::HAL_RCC_TIM6_CLK_ENABLE() +{ + __HAL_RCC_TIM6_CLK_ENABLE(); // NOLINT +} + +void CoreSTM32Hal::HAL_RCC_TIM6_CLK_DISABLE() +{ + __HAL_RCC_TIM6_CLK_DISABLE(); // NOLINT +} + +void CoreSTM32Hal::HAL_RCC_TIM7_CLK_ENABLE() +{ + __HAL_RCC_TIM7_CLK_ENABLE(); // NOLINT +} + +void CoreSTM32Hal::HAL_RCC_TIM7_CLK_DISABLE() +{ + __HAL_RCC_TIM7_CLK_DISABLE(); // NOLINT +} + void CoreSTM32Hal::HAL_RCC_FMC_CLK_ENABLE() { __HAL_RCC_FMC_CLK_ENABLE(); // NOLINT } +void CoreSTM32Hal::HAL_RCC_DMA1_CLK_ENABLE() +{ + __HAL_RCC_DMA1_CLK_ENABLE(); // NOLINT +} + void CoreSTM32Hal::HAL_RCC_DMA2_CLK_ENABLE() { __HAL_RCC_DMA2_CLK_ENABLE(); // NOLINT } +void CoreSTM32Hal::HAL_RCC_DAC_CLK_ENABLE() +{ + __HAL_RCC_DAC_CLK_ENABLE(); // NOLINT +} + +void CoreSTM32Hal::HAL_RCC_DAC_CLK_DISABLE() +{ + __HAL_RCC_DAC_CLK_DISABLE(); // NOLINT +} + void CoreSTM32Hal::HAL_RCC_JPEG_CLK_ENABLE() { __HAL_RCC_JPEG_CLK_ENABLE(); // NOLINT @@ -311,4 +346,69 @@ auto CoreSTM32Hal::HAL_JPEG_Resume(JPEG_HandleTypeDef *hjpeg, uint32_t XferSelec return ::HAL_JPEG_Resume(hjpeg, XferSelection); } +auto CoreSTM32Hal::HAL_TIM_Base_Init(TIM_HandleTypeDef *htim) -> HAL_StatusTypeDef +{ + return ::HAL_TIM_Base_Init(htim); +} + +auto CoreSTM32Hal::HAL_TIMEx_MasterConfigSynchronization(TIM_HandleTypeDef *htim, + TIM_MasterConfigTypeDef *sMasterConfig) -> HAL_StatusTypeDef +{ + return ::HAL_TIMEx_MasterConfigSynchronization(htim, sMasterConfig); +} + +auto CoreSTM32Hal::HAL_TIM_RegisterCallback(TIM_HandleTypeDef *htim, HAL_TIM_CallbackIDTypeDef CallbackID, + pTIM_CallbackTypeDef pCallback) -> HAL_StatusTypeDef +{ + return ::HAL_TIM_RegisterCallback(htim, CallbackID, pCallback); +} + +auto CoreSTM32Hal::HAL_TIM_Base_Start_IT(TIM_HandleTypeDef *htim) -> HAL_StatusTypeDef +{ + return ::HAL_TIM_Base_Start_IT(htim); +} + +auto CoreSTM32Hal::HAL_TIM_Base_Stop_IT(TIM_HandleTypeDef *htim) -> HAL_StatusTypeDef +{ + return ::HAL_TIM_Base_Stop_IT(htim); +} + +auto CoreSTM32Hal::HAL_TIM_Base_DeInit(TIM_HandleTypeDef *htim) -> HAL_StatusTypeDef +{ + return ::HAL_TIM_Base_DeInit(htim); +} + +auto CoreSTM32Hal::HAL_DAC_Init(DAC_HandleTypeDef *hdac) -> HAL_StatusTypeDef +{ + return ::HAL_DAC_Init(hdac); +} + +auto CoreSTM32Hal::HAL_DAC_ConfigChannel(DAC_HandleTypeDef *hdac, DAC_ChannelConfTypeDef *sConfig, uint32_t Channel) + -> HAL_StatusTypeDef +{ + return ::HAL_DAC_ConfigChannel(hdac, sConfig, Channel); +} + +auto CoreSTM32Hal::HAL_DAC_RegisterCallback(DAC_HandleTypeDef *hdac, HAL_DAC_CallbackIDTypeDef CallbackID, + pDAC_CallbackTypeDef pCallback) -> HAL_StatusTypeDef +{ + return ::HAL_DAC_RegisterCallback(hdac, CallbackID, pCallback); +} + +auto CoreSTM32Hal::HAL_DAC_Start_DMA(DAC_HandleTypeDef *hdac, uint32_t Channel, uint32_t *pData, uint32_t Length, + uint32_t Alignment) -> HAL_StatusTypeDef +{ + return ::HAL_DAC_Start_DMA(hdac, Channel, pData, Length, Alignment); +} + +auto CoreSTM32Hal::HAL_DAC_Stop_DMA(DAC_HandleTypeDef *hdac, uint32_t Channel) -> HAL_StatusTypeDef +{ + return ::HAL_DAC_Stop_DMA(hdac, Channel); +} + +auto CoreSTM32Hal::HAL_DAC_DeInit(DAC_HandleTypeDef *hdac) -> HAL_StatusTypeDef +{ + return ::HAL_DAC_DeInit(hdac); +} + } // namespace leka diff --git a/include/interface/drivers/DAC.h b/include/interface/drivers/DAC.h new file mode 100644 index 0000000000..d9a4707664 --- /dev/null +++ b/include/interface/drivers/DAC.h @@ -0,0 +1,24 @@ +// Leka - LekaOS +// Copyright 2024 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "interface/drivers/STM32Hal.h" + +namespace leka::interface { +class DACBase +{ + public: + virtual ~DACBase() = default; + + [[nodiscard]] virtual auto getHandle() -> DAC_HandleTypeDef & = 0; + + virtual void initialize() = 0; + virtual void terminate() = 0; + + virtual void start() = 0; + virtual void stop() = 0; +}; + +} // namespace leka::interface diff --git a/include/interface/drivers/STM32Hal.h b/include/interface/drivers/STM32Hal.h index b70089f827..4b5f56ad86 100644 --- a/include/interface/drivers/STM32Hal.h +++ b/include/interface/drivers/STM32Hal.h @@ -21,10 +21,20 @@ class STM32Hal virtual void HAL_RCC_GPIOI_CLK_ENABLE() = 0; virtual void HAL_RCC_GPIOJ_CLK_ENABLE() = 0; + virtual void HAL_RCC_TIM6_CLK_ENABLE() = 0; + virtual void HAL_RCC_TIM6_CLK_DISABLE() = 0; + + virtual void HAL_RCC_TIM7_CLK_ENABLE() = 0; + virtual void HAL_RCC_TIM7_CLK_DISABLE() = 0; + virtual void HAL_RCC_FMC_CLK_ENABLE() = 0; + virtual void HAL_RCC_DMA1_CLK_ENABLE() = 0; virtual void HAL_RCC_DMA2_CLK_ENABLE() = 0; + virtual void HAL_RCC_DAC_CLK_ENABLE() = 0; + virtual void HAL_RCC_DAC_CLK_DISABLE() = 0; + virtual void HAL_RCC_JPEG_CLK_ENABLE() = 0; virtual void HAL_RCC_JPEG_FORCE_RESET() = 0; virtual void HAL_RCC_JPEG_RELEASE_RESET() = 0; @@ -106,6 +116,25 @@ class STM32Hal virtual auto HAL_JPEG_Pause(JPEG_HandleTypeDef *hjpeg, uint32_t XferSelection) -> HAL_StatusTypeDef = 0; virtual auto HAL_JPEG_Resume(JPEG_HandleTypeDef *hjpeg, uint32_t XferSelection) -> HAL_StatusTypeDef = 0; + + virtual auto HAL_TIM_Base_Init(TIM_HandleTypeDef *htim) -> HAL_StatusTypeDef = 0; + virtual auto HAL_TIMEx_MasterConfigSynchronization(TIM_HandleTypeDef *htim, TIM_MasterConfigTypeDef *sMasterConfig) + -> HAL_StatusTypeDef = 0; + virtual auto HAL_TIM_RegisterCallback(TIM_HandleTypeDef *htim, HAL_TIM_CallbackIDTypeDef CallbackID, + pTIM_CallbackTypeDef pCallback) -> HAL_StatusTypeDef = 0; + virtual auto HAL_TIM_Base_Start_IT(TIM_HandleTypeDef *htim) -> HAL_StatusTypeDef = 0; + virtual auto HAL_TIM_Base_Stop_IT(TIM_HandleTypeDef *htim) -> HAL_StatusTypeDef = 0; + virtual auto HAL_TIM_Base_DeInit(TIM_HandleTypeDef *htim) -> HAL_StatusTypeDef = 0; + + virtual auto HAL_DAC_Init(DAC_HandleTypeDef *hdac) -> HAL_StatusTypeDef = 0; + virtual auto HAL_DAC_ConfigChannel(DAC_HandleTypeDef *hdac, DAC_ChannelConfTypeDef *sConfig, uint32_t Channel) + -> HAL_StatusTypeDef = 0; + virtual auto HAL_DAC_RegisterCallback(DAC_HandleTypeDef *hdac, HAL_DAC_CallbackIDTypeDef CallbackID, + pDAC_CallbackTypeDef pCallback) -> HAL_StatusTypeDef = 0; + virtual auto HAL_DAC_Start_DMA(DAC_HandleTypeDef *hdac, uint32_t Channel, uint32_t *pData, uint32_t Length, + uint32_t Alignment) -> HAL_StatusTypeDef = 0; + virtual auto HAL_DAC_Stop_DMA(DAC_HandleTypeDef *hdac, uint32_t Channel) -> HAL_StatusTypeDef = 0; + virtual auto HAL_DAC_DeInit(DAC_HandleTypeDef *hdac) -> HAL_StatusTypeDef = 0; }; } // namespace leka::interface diff --git a/include/interface/platform/File.h b/include/interface/platform/File.h index b991d7e40b..df16153fc1 100644 --- a/include/interface/platform/File.h +++ b/include/interface/platform/File.h @@ -21,6 +21,8 @@ struct File { virtual auto read(std::span buffer) -> std::size_t = 0; virtual auto write(std::span data) -> std::size_t = 0; + virtual auto read(std::span buffer) -> std::size_t = 0; + virtual auto read(std::span buffer) -> std::size_t = 0; virtual auto write(std::span data) -> std::size_t = 0; diff --git a/libs/FileManagerKit/include/FileManagerKit.h b/libs/FileManagerKit/include/FileManagerKit.h index b519adeddc..1b42d8cad1 100644 --- a/libs/FileManagerKit/include/FileManagerKit.h +++ b/libs/FileManagerKit/include/FileManagerKit.h @@ -25,12 +25,16 @@ struct File : public interface::File, public mbed::NonCopyable { auto read(std::span buffer) -> std::size_t final; auto write(std::span data) -> std::size_t final; + auto read(std::span buffer) -> std::size_t final; + auto read(std::span buffer) -> std::size_t final; auto write(std::span data) -> std::size_t final; auto read(uint8_t *buffer, uint32_t size) -> std::size_t final; auto write(const uint8_t *data, uint32_t size) -> std::size_t final; + auto read(int16_t *buffer, uint32_t size) -> std::size_t; + auto read(char *buffer, uint32_t size) -> std::size_t final; auto write(const char *data, uint32_t size) -> std::size_t final; diff --git a/libs/FileManagerKit/source/File.cpp b/libs/FileManagerKit/source/File.cpp index ba30698850..c4be239c15 100644 --- a/libs/FileManagerKit/source/File.cpp +++ b/libs/FileManagerKit/source/File.cpp @@ -60,6 +60,11 @@ auto File::read(std::span buffer) -> std::size_t return std::fread(buffer.data(), sizeof(uint8_t), buffer.size(), _file.get()); } +auto File::read(std::span buffer) -> std::size_t +{ + return std::fread(buffer.data(), sizeof(int16_t), buffer.size(), _file.get()); +} + auto File::write(std::span data) -> std::size_t { return std::fwrite(data.data(), sizeof(uint8_t), data.size(), _file.get()); @@ -80,6 +85,11 @@ auto File::read(uint8_t *buffer, uint32_t size) -> std::size_t return std::fread(buffer, sizeof(uint8_t), size, _file.get()); } +auto File::read(int16_t *buffer, uint32_t size) -> std::size_t +{ + return std::fread(buffer, sizeof(int16_t), size, _file.get()); +} + auto File::write(const uint8_t *data, uint32_t size) -> std::size_t { return std::fwrite(data, sizeof(uint8_t), size, _file.get()); diff --git a/spikes/CMakeLists.txt b/spikes/CMakeLists.txt index cc3bd94479..23f862caa5 100644 --- a/spikes/CMakeLists.txt +++ b/spikes/CMakeLists.txt @@ -13,6 +13,7 @@ add_subdirectory(${SPIKES_DIR}/lk_command_kit) add_subdirectory(${SPIKES_DIR}/lk_config_kit) add_subdirectory(${SPIKES_DIR}/lk_coreled) add_subdirectory(${SPIKES_DIR}/lk_core_touch_sensor) +add_subdirectory(${SPIKES_DIR}/lk_dac) add_subdirectory(${SPIKES_DIR}/lk_event_queue) add_subdirectory(${SPIKES_DIR}/lk_file_reception) add_subdirectory(${SPIKES_DIR}/lk_file_manager_kit) @@ -57,6 +58,7 @@ add_dependencies(spikes_leka spike_lk_command_kit spike_lk_coreled spike_lk_core_touch_sensor + spike_lk_dac spike_lk_event_queue spike_lk_file_reception spike_lk_file_manager_kit diff --git a/spikes/lk_audio/AudioKit.cpp b/spikes/lk_audio/AudioKit.cpp new file mode 100644 index 0000000000..fdac1d50b2 --- /dev/null +++ b/spikes/lk_audio/AudioKit.cpp @@ -0,0 +1 @@ +#include "AudioKit.h" diff --git a/spikes/lk_audio/AudioKit.h b/spikes/lk_audio/AudioKit.h new file mode 100644 index 0000000000..273e74391c --- /dev/null +++ b/spikes/lk_audio/AudioKit.h @@ -0,0 +1,5 @@ +// Leka - LekaOS +// Copyright 2024 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once diff --git a/spikes/lk_audio/CMakeLists.txt b/spikes/lk_audio/CMakeLists.txt index afa028ed84..ec8bf8170f 100644 --- a/spikes/lk_audio/CMakeLists.txt +++ b/spikes/lk_audio/CMakeLists.txt @@ -12,10 +12,18 @@ target_include_directories(spike_lk_audio target_sources(spike_lk_audio PRIVATE main.cpp + WavFile.cpp + WavReader.cpp + AudioKit.cpp ) target_link_libraries(spike_lk_audio + CoreSTM32Hal + CoreDAC FileManagerKit + CoreEventQueue + CoreEventFlags + BLEKit ) target_link_custom_leka_targets(spike_lk_audio) diff --git a/spikes/lk_audio/WavFile.cpp b/spikes/lk_audio/WavFile.cpp new file mode 100644 index 0000000000..e6f774d598 --- /dev/null +++ b/spikes/lk_audio/WavFile.cpp @@ -0,0 +1,55 @@ +#include "WavFile.h" + +// { +// log_info("Header: "); +// rtos::ThisThread::sleep_for(100ms); +// for (auto &elem: header_array) { +// printf("%x ", elem); +// } +// printf("\n"); + +// rtos::ThisThread::sleep_for(1s); + +// file.read(data_file); +// log_info("Content: "); +// rtos::ThisThread::sleep_for(1s); +// for (auto i = 0; i < data_file_size; i++) { +// int16_t file_value = data_file.at(i); +// printf("At %3i: %d\n", i * 2 + 44, file_value); +// // uint16_t normalized_value = static_cast(data_file.at(i) + 0x8000) >> 4; +// // printf("%x ", normalized_value); +// } +// printf("\n"); + +// rtos::ThisThread::sleep_for(1s); +// return 0; +// } // Normalization + +// { +// setData(0); +// log_info("Data play (first half loaded):"); +// rtos::ThisThread::sleep_for(1s); +// for (auto i = 0; i < data_play.size(); i++) { +// printf("At %3i: %x\n", i * 2 + 44, data_play.at(i)); +// } +// printf("\n"); + +// setData(size / 2); +// log_info("Data play (second half loaded): "); +// rtos::ThisThread::sleep_for(1s); +// for (auto i = 0; i < data_play.size(); i++) { +// printf("At %3i: %x\n", i * 2 + 44, data_play.at(i)); +// } +// printf("\n"); + +// setData(0); +// log_info("Data play (next chunk): "); +// rtos::ThisThread::sleep_for(1s); +// for (auto i = 0; i < data_play.size() / 2; i++) { +// printf("At %3i: %x\n", i * 2 + 44 + size * 2, data_play.at(i)); +// } +// printf("\n"); + +// rtos::ThisThread::sleep_for(1s); +// return 0; +// } // Correctly filled diff --git a/spikes/lk_audio/WavFile.h b/spikes/lk_audio/WavFile.h new file mode 100644 index 0000000000..273e74391c --- /dev/null +++ b/spikes/lk_audio/WavFile.h @@ -0,0 +1,5 @@ +// Leka - LekaOS +// Copyright 2024 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once diff --git a/spikes/lk_audio/WavReader.cpp b/spikes/lk_audio/WavReader.cpp new file mode 100644 index 0000000000..21637eed4c --- /dev/null +++ b/spikes/lk_audio/WavReader.cpp @@ -0,0 +1 @@ +#include "WavReader.h" diff --git a/spikes/lk_audio/WavReader.h b/spikes/lk_audio/WavReader.h new file mode 100644 index 0000000000..273e74391c --- /dev/null +++ b/spikes/lk_audio/WavReader.h @@ -0,0 +1,5 @@ +// Leka - LekaOS +// Copyright 2024 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once diff --git a/spikes/lk_audio/main.cpp b/spikes/lk_audio/main.cpp index 3483f70263..29a0493e0e 100644 --- a/spikes/lk_audio/main.cpp +++ b/spikes/lk_audio/main.cpp @@ -1,16 +1,20 @@ // Leka - LekaOS -// Copyright 2022 APF France handicap +// Copyright 2024 APF France handicap // SPDX-License-Identifier: Apache-2.0 -#include -#include +#include -#include "drivers/AnalogOut.h" -#include "drivers/DigitalOut.h" -#include "platform/mbed_wait_api.h" #include "rtos/ThisThread.h" -#include "rtos/Thread.h" +#include "BLEKit.h" +#include "BLEServiceConfig.h" + +#include "CoreDAC.h" +#include "CoreEventFlags.h" +#include "CoreEventQueue.h" +#include "CoreSTM32Hal.h" +#include "CoreSTM32HalBasicTimer.h" +#include "DigitalOut.h" #include "FATFileSystem.h" #include "FileManagerKit.h" #include "LogKit.h" @@ -19,50 +23,103 @@ using namespace leka; using namespace std::chrono_literals; -auto sd_bd = SDBlockDevice {SD_SPI_MOSI, SD_SPI_MISO, SD_SPI_SCK}; -auto fatfs = FATFileSystem {"fs"}; +auto hal = CoreSTM32Hal {}; +auto hal_timer = CoreSTM32HalBasicTimer {hal}; +auto coredac = CoreDAC {hal, hal_timer}; -const auto sound_file_path = std::filesystem::path {"/fs/home/wav/fur-elise.wav"}; -auto file = FileManagerKit::File {sound_file_path}; +auto audio_enable = mbed::DigitalOut {SOUND_ENABLE, 1}; -auto thread_audio = rtos::Thread {}; +constexpr uint32_t sample_rate_hz = 44'100; -auto audio_enable = mbed::DigitalOut {SOUND_ENABLE, 1}; -auto audio_output = mbed::AnalogOut {MCU_SOUND_OUT}; +auto event_queue = CoreEventQueue {}; +auto event_queue_converted = CoreEventQueue {}; + +auto sd_blockdevice = SDBlockDevice {SD_SPI_MOSI, SD_SPI_MISO, SD_SPI_SCK}; +auto fatfs = FATFileSystem {"fs"}; + +const auto sound_directory_path = std::filesystem::path {"/fs/home/wav"}; +auto sound_file_path = sound_directory_path / std::filesystem::path {"440.wav"}; +auto file = FileManagerKit::File {}; + +auto event_flags = CoreEventFlags {}; + +struct flag { + static constexpr uint32_t START = (1UL << 1); +}; + +auto service_config = BLEServiceConfig {}; +auto services = std::to_array({&service_config}); +auto blekit = BLEKit {}; void initializeSD() { - constexpr auto default_sd_bd_frequency = uint64_t {25'000'000}; + constexpr auto default_sd_blockdevice_frequency = uint64_t {25'000'000}; - sd_bd.init(); - sd_bd.frequency(default_sd_bd_frequency); + sd_blockdevice.init(); + sd_blockdevice.frequency(default_sd_blockdevice_frequency); - fatfs.mount(&sd_bd); + fatfs.mount(&sd_blockdevice); } -void playSound() +// constexpr auto size = 128; +// constexpr auto size = 256; +// constexpr auto size = 512; +constexpr auto size = 5'000; +// constexpr auto size = 1'024; +// constexpr auto size = 2'048; +// constexpr auto size = 4'096; +// constexpr auto size = 8'192; +// constexpr auto size = 16'384; +// constexpr auto size = 32'768; +// constexpr auto size = 65'536; // NOK +constexpr auto coefficient = 10; // Related to ARR | ARR*coefficient ~= 2448 +constexpr auto data_file_size = size / coefficient / 2; // /2 for half buffer and *2 +std::array data_file {}; +// std::array data_file {}; +std::array data_converted {}; +std::array data_play {}; + +auto is_eof = false; + +void convertData(uint32_t offset) { - static const auto _n_bytes_to_read = int {512}; // arbitrary - auto _buffer = std::array {0}; - - auto _ns_sample_rate = uint32_t {22676}; // 1,000,000,000 / 44,100 (in ns) - auto _ns_sample_rate_adapted = _ns_sample_rate * 1.7; // arbitrary, 1s in MCU is not exactly 1s in real life - auto bytesread = uint32_t {_n_bytes_to_read}; - - /* START READ WAV */ - while (bytesread == _n_bytes_to_read) { - // Read "_n_bytes_to_read" from file at each iteration. Real bytes read is given by "bytesread" - if (bytesread = file.read(_buffer.data(), _n_bytes_to_read); bytesread != 0) { - // Play every 2-bytes (sound encoded in 16 bits) - for (uint32_t j = 0; j < bytesread; j += 4) { // Play one channel, data for stereo are alternate - audio_output.write_u16((_buffer.at(j + 1) + 0x8000) >> - 1); // offset for int16 data (0x8000) and volume 50% (>>1) - - wait_ns(_ns_sample_rate_adapted); // adjust play speed - } - } + auto bytes_read = file.read(data_file); + + for (auto i = 0; i < data_file_size; i++) { + auto normalized_value = static_cast((data_file.at(i) + 0x8000) >> 4); + std::fill_n(data_converted.begin() + offset + i * coefficient, coefficient, normalized_value); } - /* END READ WAV*/ + is_eof = bytes_read != data_file_size; + + log_info("bytes_read %d", bytes_read); // Better than sleep_for +} + +void setData(uint32_t offset) +{ + if (is_eof) { + coredac.stop(); + return; + } + + std::copy(data_converted.begin() + offset, data_converted.begin() + offset + size / 2, data_play.begin() + offset); + + event_queue_converted.call([offset] { convertData(offset); }); + + // log_info(""); // Better than sleep_for + rtos::ThisThread::sleep_for(1ms); +} + +void onHalfTransfer() +{ + // Fill first half + setData(0); + // std::fill_n(data_play.begin(), size / 2, 0x0); +} +void onCompleteTransfer() +{ + // Fill second half + setData(size / 2); + // std::fill_n(data_play.begin() + size / 2, size / 2, 0xFFF); } auto main() -> int @@ -70,17 +127,69 @@ auto main() -> int logger::init(); log_info("Hello, World!\n\n"); + rtos::ThisThread::sleep_for(1s); - initializeSD(); + event_queue_converted.dispatch_forever(); + event_queue.dispatch_forever(); + + blekit.setServices(services); + blekit.init(); + initializeSD(); if (FileManagerKit::file_is_missing(sound_file_path)) { return 1; } + service_config.onRobotNameUpdated([](const std::array &robot_name) { + const auto *end_index = std::find(robot_name.begin(), robot_name.end(), '\0'); + auto filename = std::string {robot_name.begin(), end_index} + ".wav"; + sound_file_path = sound_directory_path / std::filesystem::path {filename}; + + log_info("Play file: %s", sound_file_path.c_str()); + event_flags.set(flag::START); + }); + + // BEGIN -- NEW CODE + + log_info("Initialize"); + rtos::ThisThread::sleep_for(1s); + + coredac.registerDMACallbacks([] { event_queue.call(onHalfTransfer); }, + [] { event_queue.call(onCompleteTransfer); }); + + hal_timer.initialize(44'100 * coefficient); + coredac.initialize(); + + event_flags.set(flag::START); + while (true) { - file.open(sound_file_path); - playSound(); - file.close(); + event_flags.wait_any(flag::START); + + { + file.open(sound_file_path); + auto header_array = std::array {}; + file.read(header_array); // header + + is_eof = false; + + convertData(0); + rtos::ThisThread::sleep_for(300ms); + convertData(size / 2); + rtos::ThisThread::sleep_for(300ms); + + setData(0); + rtos::ThisThread::sleep_for(300ms); + setData(size / 2); + rtos::ThisThread::sleep_for(300ms); + + coredac.registerDataToPlay(data_play); + rtos::ThisThread::sleep_for(100ms); + } // Setup new file + + { + coredac.start(); + while (!is_eof) rtos::ThisThread::sleep_for(1s); + } // Play on audio rtos::ThisThread::sleep_for(1s); } diff --git a/spikes/lk_dac/CMakeLists.txt b/spikes/lk_dac/CMakeLists.txt new file mode 100644 index 0000000000..134729c224 --- /dev/null +++ b/spikes/lk_dac/CMakeLists.txt @@ -0,0 +1,23 @@ +# Leka - LekaOS +# Copyright 2024 APF France handicap +# SPDX-License-Identifier: Apache-2.0 + +add_mbed_executable(spike_lk_dac) + +target_include_directories(spike_lk_dac + PRIVATE + . +) + +target_sources(spike_lk_dac + PRIVATE + main.cpp +) + +target_link_libraries(spike_lk_dac + CoreSTM32Hal + CoreDAC + CoreEventQueue +) + +target_link_custom_leka_targets(spike_lk_dac) diff --git a/spikes/lk_dac/main.cpp b/spikes/lk_dac/main.cpp new file mode 100644 index 0000000000..80c89a1412 --- /dev/null +++ b/spikes/lk_dac/main.cpp @@ -0,0 +1,96 @@ +// Leka - LekaOS +// Copyright 2024 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#include + +#include "rtos/ThisThread.h" + +#include "CoreDAC.h" +#include "CoreEventQueue.h" +#include "CoreSTM32Hal.h" +#include "CoreSTM32HalBasicTimer.h" +#include "DigitalOut.h" +#include "LogKit.h" + +using namespace leka; +using namespace std::chrono_literals; + +auto hal = CoreSTM32Hal {}; +auto hal_timer = CoreSTM32HalBasicTimer {hal}; +auto coredac = CoreDAC {hal, hal_timer}; + +auto audio_enable = mbed::DigitalOut {SOUND_ENABLE, 0}; + +auto event_queue = CoreEventQueue {}; + +constexpr uint32_t sample_rate_hz = 44'100; +constexpr auto coefficient = 10; + +void fillBufferWithSinWave(uint16_t *buffer, uint32_t samples_per_period, uint16_t maxValue, uint16_t minValue) +{ + auto resolution = 2.0 * M_PI / samples_per_period; + + auto sin0_1 = [](double value) { return (sin(value) + 1.0) / 2.0; }; + auto normalization = [maxValue, minValue](double standard_value) { + return standard_value * (maxValue - minValue) + minValue; + }; + + for (uint32_t sample = 0; sample < samples_per_period; sample += coefficient) { + if (sample < samples_per_period / 2) { + std::fill_n(buffer + sample, coefficient, maxValue); + } else { + std::fill_n(buffer + sample, coefficient, minValue); + } + + // auto standard_value = sin0_1(sample * resolution); + // auto normalized_value = normalization(standard_value); + // std::fill_n(buffer + sample, coefficient, static_cast(normalized_value)); + } +} + +constexpr auto played_buffer_size = 256; +std::array played_buffer {}; + +void setData(uint16_t offset, uint16_t value) +{ + std::fill_n(played_buffer.begin() + offset, played_buffer_size / 2, value); +} + +auto main() -> int +{ + logger::init(); + + log_info("Hello, World!\n\n"); + + hal_timer.initialize(sample_rate_hz * coefficient); + coredac.initialize(); + + const uint32_t frequency = 440; + const uint16_t maxVal = 0xFFF; + const uint16_t minVal = 0x000; + std::array buffer {}; + fillBufferWithSinWave(buffer.data(), buffer.size(), maxVal, minVal); + + event_queue.dispatch_forever(); + setData(0, minVal); + setData(played_buffer_size / 2, maxVal); + + coredac.registerDMACallbacks([] { event_queue.call([] { setData(0, minVal); }); }, + [] { event_queue.call([] { setData(played_buffer_size / 2, maxVal); }); }); + + coredac.registerDataToPlay(played_buffer); + + // log_info("buffer size: %d", buffer.size()); + log_info("Start sound"); + coredac.start(); + + rtos::ThisThread::sleep_for(4min); + + log_info("Stop sound"); + coredac.stop(); + + while (true) { + rtos::ThisThread::sleep_for(1min); + } +} diff --git a/tests/unit/headers/mbed/mbed_config.h b/tests/unit/headers/mbed/mbed_config.h index e7fee6c006..5b057607c0 100644 --- a/tests/unit/headers/mbed/mbed_config.h +++ b/tests/unit/headers/mbed/mbed_config.h @@ -288,6 +288,8 @@ #define SMP_DB_MAX_DEVICES 3 // set by library:cordio #define TARGET_LSE_DRIVE_LOAD_LEVEL RCC_LSEDRIVE_LOW // set by target:MCU_STM32F7 #define USE_HAL_JPEG_REGISTER_CALLBACKS 1U // set by application +#define USE_HAL_TIM_REGISTER_CALLBACKS 1U // set by application +#define USE_HAL_DAC_REGISTER_CALLBACKS 1U // set by application // Macros #define WSF_MS_PER_TICK 1 // defined by library:cordio #define _RTE_ // defined by library:rtos diff --git a/tests/unit/mocks/mocks/leka/CoreSTM32Hal.h b/tests/unit/mocks/mocks/leka/CoreSTM32Hal.h index 2d950de3e7..5751098106 100644 --- a/tests/unit/mocks/mocks/leka/CoreSTM32Hal.h +++ b/tests/unit/mocks/mocks/leka/CoreSTM32Hal.h @@ -19,9 +19,15 @@ class CoreSTM32Hal : public interface::STM32Hal MOCK_METHOD(void, HAL_RCC_GPIOH_CLK_ENABLE, (), (override)); MOCK_METHOD(void, HAL_RCC_GPIOI_CLK_ENABLE, (), (override)); MOCK_METHOD(void, HAL_RCC_GPIOJ_CLK_ENABLE, (), (override)); - + MOCK_METHOD(void, HAL_RCC_TIM6_CLK_ENABLE, (), (override)); + MOCK_METHOD(void, HAL_RCC_TIM6_CLK_DISABLE, (), (override)); + MOCK_METHOD(void, HAL_RCC_TIM7_CLK_ENABLE, (), (override)); + MOCK_METHOD(void, HAL_RCC_TIM7_CLK_DISABLE, (), (override)); MOCK_METHOD(void, HAL_RCC_FMC_CLK_ENABLE, (), (override)); + MOCK_METHOD(void, HAL_RCC_DMA1_CLK_ENABLE, (), (override)); MOCK_METHOD(void, HAL_RCC_DMA2_CLK_ENABLE, (), (override)); + MOCK_METHOD(void, HAL_RCC_DAC_CLK_ENABLE, (), (override)); + MOCK_METHOD(void, HAL_RCC_DAC_CLK_DISABLE, (), (override)); MOCK_METHOD(void, HAL_RCC_JPEG_CLK_ENABLE, (), (override)); MOCK_METHOD(void, HAL_RCC_JPEG_FORCE_RESET, (), (override)); MOCK_METHOD(void, HAL_RCC_JPEG_RELEASE_RESET, (), (override)); @@ -113,6 +119,27 @@ class CoreSTM32Hal : public interface::STM32Hal MOCK_METHOD(HAL_StatusTypeDef, HAL_JPEG_Pause, (JPEG_HandleTypeDef *, uint32_t), (override)); MOCK_METHOD(HAL_StatusTypeDef, HAL_JPEG_Resume, (JPEG_HandleTypeDef *, uint32_t), (override)); + MOCK_METHOD(HAL_StatusTypeDef, HAL_TIM_Base_Init, (TIM_HandleTypeDef * htim), (override)); + MOCK_METHOD(HAL_StatusTypeDef, HAL_TIMEx_MasterConfigSynchronization, + (TIM_HandleTypeDef * htim, TIM_MasterConfigTypeDef *sMasterConfig), (override)); + MOCK_METHOD(HAL_StatusTypeDef, HAL_TIM_RegisterCallback, + (TIM_HandleTypeDef * htim, HAL_TIM_CallbackIDTypeDef CallbackID, pTIM_CallbackTypeDef pCallback), + (override)); + MOCK_METHOD(HAL_StatusTypeDef, HAL_TIM_Base_Start_IT, (TIM_HandleTypeDef * htim), (override)); + MOCK_METHOD(HAL_StatusTypeDef, HAL_TIM_Base_Stop_IT, (TIM_HandleTypeDef * htim), (override)); + MOCK_METHOD(HAL_StatusTypeDef, HAL_TIM_Base_DeInit, (TIM_HandleTypeDef * htim), (override)); + + MOCK_METHOD(HAL_StatusTypeDef, HAL_DAC_Init, (DAC_HandleTypeDef * hdac), (override)); + MOCK_METHOD(HAL_StatusTypeDef, HAL_DAC_ConfigChannel, + (DAC_HandleTypeDef * hdac, DAC_ChannelConfTypeDef *sConfig, uint32_t Channel), (override)); + MOCK_METHOD(HAL_StatusTypeDef, HAL_DAC_RegisterCallback, + (DAC_HandleTypeDef * hdac, HAL_DAC_CallbackIDTypeDef CallbackID, pDAC_CallbackTypeDef pCallback), + (override)); + MOCK_METHOD(HAL_StatusTypeDef, HAL_DAC_Start_DMA, + (DAC_HandleTypeDef * hdac, uint32_t Channel, uint32_t *pData, uint32_t Length, uint32_t Alignment), + (override)); + MOCK_METHOD(HAL_StatusTypeDef, HAL_DAC_Stop_DMA, (DAC_HandleTypeDef * hdac, uint32_t Channel), (override)); + MOCK_METHOD(HAL_StatusTypeDef, HAL_DAC_DeInit, (DAC_HandleTypeDef * hdac), (override)); }; } // namespace leka::mock