From 0107d3e6e74d96c4ec35d7bfa2cf091ed353cb9f Mon Sep 17 00:00:00 2001 From: Erki Date: Mon, 24 Feb 2025 18:24:30 +0200 Subject: [PATCH] WIP7: Add async serial IO support --- Peripherals/Inc/peripherals_hal_st.hpp | 113 ++++++++++++++++++- coro/inc/skullc/coro/peripheral_awaiters.hpp | 39 +++++++ 2 files changed, 151 insertions(+), 1 deletion(-) diff --git a/Peripherals/Inc/peripherals_hal_st.hpp b/Peripherals/Inc/peripherals_hal_st.hpp index 8d3a105..20e369d 100644 --- a/Peripherals/Inc/peripherals_hal_st.hpp +++ b/Peripherals/Inc/peripherals_hal_st.hpp @@ -31,7 +31,7 @@ template using IsrCallbackFn = void (*)(Origin*); template + typename Tag = decltype([] {})> IsrCallbackFn createCallback(Handler& h_in) { static Handler* h = &h_in; @@ -200,6 +200,104 @@ struct SerialInterfaceAsync } }; +#ifdef SKULLC_WITH_CORO +template +struct SerialInterfaceCoro +{ +private: + void on_rx_complete() + { + if (rx_awaiter_) + rx_awaiter_->set_completed(); + } + + void on_tx_complete() + { + if (tx_awaiter_) + tx_awaiter_->set_completed(); + } + + std::optional rx_awaiter_ = std::nullopt; + std::optional tx_awaiter_ = std::nullopt; + +public: + using underlying_handle_type = T; + underlying_handle_type* handle; + + SerialInterfaceCoro() = delete; + + template + explicit SerialInterfaceCoro(underlying_handle_type* handle) + : handle(handle) + { + handle->RxCpltCallback = createCallback, + &std::decay_t::on_rx_complete>(*this); + + handle->TxCpltCallback = createCallback, + &std::decay_t::on_tx_complete>(*this); + } + + skullc::coro::Task transmit(std::uint8_t* data, const std::uint32_t data_len) + { + tx_awaiter_.emplace(); + const auto status = transmit_(handle, data, data_len); + if (status != HAL_StatusTypeDef::HAL_OK) + co_return false; + + co_await *tx_awaiter_; + tx_awaiter_ = std::nullopt; + + co_return true; + } + + template + skullc::coro::Task transmit(std::array& array) + { + static_assert(sizeof(Td) == sizeof(std::uint8_t), "Data is not a byte large."); + + return transmit(reinterpret_cast(array.data()), std::uint32_t(N)); + } + + skullc::coro::Task receive(std::uint8_t* data, const std::uint32_t data_len) + { + rx_awaiter_.emplace(); + const auto status = receive_(handle, data, data_len); + if (status != HAL_StatusTypeDef::HAL_OK) + co_return false; + + co_await *rx_awaiter_; + rx_awaiter_ = std::nullopt; + + co_return true; + } + + template + skullc::coro::Task receive(std::array& array) + { + static_assert(sizeof(Td) == sizeof(std::uint8_t), "Data is not a byte large."); + + return receive(reinterpret_cast(array.data()), std::uint32_t(N)); + } + + template + skullc::coro::Task> receive() + { + std::array data; + data.fill(0); + + co_await receive(data.data(), std::uint32_t(N)); + + co_return data; + } +}; +#endif// SKULLC_WITH_CORO + #ifdef HAL_SPI_MODULE_ENABLED using SpiInterface = @@ -296,6 +394,11 @@ inline HAL_StatusTypeDef uartTransmitDma(UART_HandleTypeDef* huart, std::uint8_t return HAL_UART_Transmit_DMA(huart, data, size); } +inline HAL_StatusTypeDef uartTransmitIt(UART_HandleTypeDef* huart, std::uint8_t* data, const std::uint16_t size) +{ + return HAL_UART_Transmit_IT(huart, data, size); +} + }// namespace _Details using UartInterface = @@ -303,6 +406,14 @@ using UartInterface = using UartInterfaceDMA = SerialInterfaceAsync; +#ifdef SKULLC_WITH_CORO +using UartInterfaceCoro = + SerialInterfaceCoro; +using UartInterfaceCoroDma = + SerialInterfaceCoro; +#endif// SKULLC_WITH_CORO #endif// HAL_UART_MODULE_ENABLED diff --git a/coro/inc/skullc/coro/peripheral_awaiters.hpp b/coro/inc/skullc/coro/peripheral_awaiters.hpp index f505d01..f5b7d3f 100644 --- a/coro/inc/skullc/coro/peripheral_awaiters.hpp +++ b/coro/inc/skullc/coro/peripheral_awaiters.hpp @@ -4,6 +4,7 @@ #pragma once +#include #include #include @@ -78,4 +79,42 @@ private: } }; +struct ISRAwaiter +{ + ISRAwaiter() = default; + + ~ISRAwaiter() + { + if (continuation) + this_coro::scheduler().remove(continuation); + } + + bool await_ready() + { + return isr_completed; + } + + void await_suspend(std::coroutine_handle<> h) + { + continuation = h; + suspended = true; + } + + auto await_resume() + { + continuation = {}; + } + + void set_completed() + { + isr_completed = true; + if (suspended == true) + this_coro::scheduler().schedule(continuation, 0); + } + + std::coroutine_handle<> continuation; + std::atomic isr_completed = false; + std::atomic suspended = false; +}; + }// namespace skullc::coro