diff --git a/Sts1CobcSw/Hal/CMakeLists.txt b/Sts1CobcSw/Hal/CMakeLists.txt index 09634516..70934a07 100644 --- a/Sts1CobcSw/Hal/CMakeLists.txt +++ b/Sts1CobcSw/Hal/CMakeLists.txt @@ -1,6 +1,12 @@ -target_sources(Sts1CobcSw_Hal PRIVATE Uart.cpp Spi.cpp) +target_sources(Sts1CobcSw_Hal PRIVATE Uart.cpp) target_link_libraries( Sts1CobcSw_Hal PUBLIC etl::etl rodos::without_main_on_linux Sts1CobcSw_Utility Sts1CobcSw_Outcome ) target_compile_definitions(Sts1CobcSw_Hal PUBLIC HW_VERSION=${HW_VERSION}) + +if(CMAKE_SYSTEM_NAME STREQUAL Generic) + target_sources(Sts1CobcSw_Hal PRIVATE Spi.cpp) +else() + target_sources(Sts1CobcSw_Hal PRIVATE SpiMock.cpp) +endif() diff --git a/Sts1CobcSw/Hal/SpiMock.cpp b/Sts1CobcSw/Hal/SpiMock.cpp new file mode 100644 index 00000000..39f1867b --- /dev/null +++ b/Sts1CobcSw/Hal/SpiMock.cpp @@ -0,0 +1,209 @@ +#include +#include + +#include + +#include + +#include + + +namespace sts1cobcsw::hal +{ +class Spi::Impl +{ +public: + static constexpr auto maxNInstances = 3; + + // Since we cannot use dynamic memory allocation we use a static ETL vector to provide the + // storage for a fixed number of instances. This limitation is acceptable since we only need one + // global instance for each of the three SPIs that we use. + [[nodiscard]] static auto GetInstances() -> etl::vector &; + + Impl(RODOS::SPI_IDX spiIndex, + RODOS::GPIO_PIN sckPin, + RODOS::GPIO_PIN misoPin, + RODOS::GPIO_PIN mosiPin); + + auto Initialize(std::uint32_t baudRate) -> void; + auto Read(void * data, std::size_t nBytes, Duration timeout) -> void; + auto Write(void const * data, std::size_t nBytes, Duration timeout) -> void; + [[nodiscard]] auto TransferEnd() const -> RodosTime; + [[nodiscard]] auto BaudRate() const -> std::int32_t; + + +private: + mutable RODOS::CommBuffer transferEnd_; +}; + +auto doInitialize = empty::DoInitialize; +auto doRead = empty::DoRead; +auto doWrite = empty::DoWrite; +auto doTransferEnd = empty::DoTransferEnd; +auto doBaudRate = empty::DoBaudRate; + +auto Spi::Impl::GetInstances() -> etl::vector & +{ + static etl::vector instances; + return instances; +} + + +Spi::Impl::Impl(RODOS::SPI_IDX spiIndex, + RODOS::GPIO_PIN sckPin, + RODOS::GPIO_PIN misoPin, + RODOS::GPIO_PIN mosiPin) + : spi_(spiIndex, sckPin, misoPin, mosiPin, spiNssDummyPin) +{ + transferEnd_ = RodosTime(RODOS::END_OF_TIME); +} + + +auto Spi::Impl::Initialize(std::uint32_t baudRate) -> void +{ + return doInitialize(); +} + + +auto Spi::Impl::Read(void * data, std::size_t nBytes, Duration timeout) -> void +{ + return doRead(); +} + + +auto Spi::Impl::Write(void const * data, std::size_t nBytes, Duration timeout) -> void +{ + return doWrite(); +} + + +auto Spi::Impl::TransferEnd() const -> RodosTime +{ + return doTransferEnd(); +} + + +auto Spi::Impl::BaudRate() const -> std::int32_t +{ + return doBaudRate(); +} + + +// --- Set functions --- + +auto SetDoInitialize(void (*doInitializeFunction)()) -> void +{ + doInitialize = doInitializeFunction; +} + + +auto SetDoRead(void (*doReadFunction)()) -> void +{ + doRead = doReadFunction; +} + + +auto SetDoWtite(void (*doWriteFunction)()) -> void +{ + doWrite = doWriteFunction; +} + + +auto SetDoTransferEnd(RodosTime (*doTransferEndFunction)()) -> void +{ + doTransferEnd = doTransferEndFunction; +} + + +auto SetDoBaudRate(std::int32_t (*doBaudRateFunction)()) -> void +{ + doBaudRate = doBaudRateFunction; +} + + +// --- Predefined do functions --- + +namespace empty +{ +auto SetAllDoFunctions() -> void +{ + SetDoInitialize(DoInitialize); + SetDoRead(DoRead); + SetDoWrite(DoWrite); + SetDoTransferEnd(DoTransferEnd); + SetDoBaudRate(DoBaudRate); +} + + +auto DoInitialize([[maybe_unused]] std::uint32_t baudRate) -> void +{ +} + + +auto DoRead([[maybe_unused]] void * data, + [[maybe_unused]] std::size_t nBytes, + [[maybe_unused]] Duration timeout) -> void +{ +} + + +auto DoWrite([[maybe_unused]] void const * data, + [[maybe_unused]] std::size_t nBytes, + [[maybe_unused]] Duration timeout) -> void +{ +} + + +auto DoTransferEnd() -> RodosTime +{ + return endOfTime; +} + + +auto DoBaudRate() -> std::int32_t +{ + return 0; +} +} + + +Spi::Spi(RODOS::SPI_IDX spiIndex, + RODOS::GPIO_PIN sckPin, + RODOS::GPIO_PIN misoPin, + RODOS::GPIO_PIN mosiPin) +{ + // TODO: Check what happens if we exceed maxNInstances + Impl::GetInstances().emplace_back(spiIndex, sckPin, misoPin, mosiPin); + pimpl_ = &Impl::GetInstances().back(); // NOLINT(*prefer-member-initializer) +} + + +auto Spi::TransferEnd() const -> RodosTime +{ + return pimpl_->TransferEnd(); +} + + +auto Spi::BaudRate() const -> std::int32_t +{ + return pimpl_->BaudRate(); +} + + +auto Initialize(Spi * spi, std::uint32_t baudRate) -> void +{ + spi->pimpl_->Initialize(baudRate); +} + + +auto Spi::Read(void * data, std::size_t nBytes, Duration timeout) -> void +{ + pimpl_->Read(data, nBytes, timeout); +} + + +auto Spi::Write(void const * data, std::size_t nBytes, Duration timeout) -> void +{ + pimpl_->Write(data, nBytes, timeout); +} +} diff --git a/Sts1CobcSw/Hal/SpiMock.hpp b/Sts1CobcSw/Hal/SpiMock.hpp new file mode 100644 index 00000000..7b19b725 --- /dev/null +++ b/Sts1CobcSw/Hal/SpiMock.hpp @@ -0,0 +1,63 @@ +#pragma once + + +#include + +#include + +#include +#include +#include + + +namespace sts1cobcsw::hal +{ +class Spi +{ +public: + Spi() = delete; + Spi(RODOS::SPI_IDX spiIndex, + RODOS::GPIO_PIN sckPin, + RODOS::GPIO_PIN misoPin, + RODOS::GPIO_PIN mosiPin); + + // The following functions are friends and not members to keep the interface coherent with the + // rest of the low-level code. + friend auto Initialize(Spi * spi, std::uint32_t baudRate) -> void; + template + friend auto ReadFrom(Spi * spi, std::span data, Duration timeout) -> void; + template + friend auto WriteTo(Spi * spi, std::span data, Duration timeout) -> void; + + [[nodiscard]] auto TransferEnd() const -> RodosTime; + [[nodiscard]] auto BaudRate() const -> std::int32_t; + + +private: + class Impl; + Impl * pimpl_; + + auto Read(void * data, std::size_t nBytes, Duration timeout) -> void; + auto Write(void const * data, std::size_t nBytes, Duration timeout) -> void; +}; + +auto SetDoInitialize(void (*doInitializeFunction)()) -> void; +auto SetDoRead(void (*doReadFunction)()) -> void; +auto SetDoWrite(void (*doWriteFunction)()) -> void; +auto SetDoTransferEnd(RodosTime (*doTransferEndFunction)()) -> void; +auto SetDoBaudRate(std::int32_t (*doBaudRateFunction)()) -> void; + +namespace empty +{ +auto SetAllDoFunctions() -> void; + +auto DoInitialize(std::uint32_t baudRate) -> void; +auto DoRead(void * data, std::size_t nBytes, Duration timeout) -> void; +auto DoWrite(void const * data, std::size_t nBytes, Duration timeout) -> void; +auto DoTransferEnd() -> RodosTime; +auto DoBaudRate() -> std::int32_t; +} +} + + +#include // IWYU pragma: keep