WIP7: Add async serial IO support
This commit is contained in:
parent
105a387efc
commit
0107d3e6e7
@ -31,7 +31,7 @@ template<typename Origin>
|
||||
using IsrCallbackFn = void (*)(Origin*);
|
||||
|
||||
template<typename Origin, typename Handler, void (Handler::*func)(),
|
||||
typename Tag>
|
||||
typename Tag = decltype([] {})>
|
||||
IsrCallbackFn<Origin> createCallback(Handler& h_in)
|
||||
{
|
||||
static Handler* h = &h_in;
|
||||
@ -200,6 +200,104 @@ struct SerialInterfaceAsync
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef SKULLC_WITH_CORO
|
||||
template<typename T,
|
||||
HAL_StatusTypeDef (*transmit_)(T*, std::uint8_t* data,
|
||||
std::uint16_t data_len),
|
||||
HAL_StatusTypeDef (*receive_)(T*, std::uint8_t* data,
|
||||
std::uint16_t data_len)>
|
||||
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<skullc::coro::ISRAwaiter> rx_awaiter_ = std::nullopt;
|
||||
std::optional<skullc::coro::ISRAwaiter> tx_awaiter_ = std::nullopt;
|
||||
|
||||
public:
|
||||
using underlying_handle_type = T;
|
||||
underlying_handle_type* handle;
|
||||
|
||||
SerialInterfaceCoro() = delete;
|
||||
|
||||
template<typename = decltype([] {})>
|
||||
explicit SerialInterfaceCoro(underlying_handle_type* handle)
|
||||
: handle(handle)
|
||||
{
|
||||
handle->RxCpltCallback = createCallback<T,
|
||||
std::decay_t<decltype(*this)>,
|
||||
&std::decay_t<decltype(*this)>::on_rx_complete>(*this);
|
||||
|
||||
handle->TxCpltCallback = createCallback<T,
|
||||
std::decay_t<decltype(*this)>,
|
||||
&std::decay_t<decltype(*this)>::on_tx_complete>(*this);
|
||||
}
|
||||
|
||||
skullc::coro::Task<bool> 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<typename Td, std::size_t N>
|
||||
skullc::coro::Task<bool> transmit(std::array<Td, N>& array)
|
||||
{
|
||||
static_assert(sizeof(Td) == sizeof(std::uint8_t), "Data is not a byte large.");
|
||||
|
||||
return transmit(reinterpret_cast<std::uint8_t*>(array.data()), std::uint32_t(N));
|
||||
}
|
||||
|
||||
skullc::coro::Task<bool> 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<typename Td, std::size_t N>
|
||||
skullc::coro::Task<bool> receive(std::array<Td, N>& array)
|
||||
{
|
||||
static_assert(sizeof(Td) == sizeof(std::uint8_t), "Data is not a byte large.");
|
||||
|
||||
return receive(reinterpret_cast<std::uint8_t*>(array.data()), std::uint32_t(N));
|
||||
}
|
||||
|
||||
template<std::size_t N>
|
||||
skullc::coro::Task<std::array<std::uint8_t, N>> receive()
|
||||
{
|
||||
std::array<std::uint8_t, N> 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<UART_HandleTypeDef, _Details::uartTransmitDma,
|
||||
HAL_UART_Receive_DMA>;
|
||||
#ifdef SKULLC_WITH_CORO
|
||||
using UartInterfaceCoro =
|
||||
SerialInterfaceCoro<UART_HandleTypeDef, _Details::uartTransmitIt,
|
||||
HAL_UART_Receive_IT>;
|
||||
using UartInterfaceCoroDma =
|
||||
SerialInterfaceCoro<UART_HandleTypeDef, _Details::uartTransmitDma,
|
||||
HAL_UART_Receive_DMA>;
|
||||
#endif// SKULLC_WITH_CORO
|
||||
|
||||
#endif// HAL_UART_MODULE_ENABLED
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <coroutine>
|
||||
#include <optional>
|
||||
|
||||
@ -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<bool> isr_completed = false;
|
||||
std::atomic<bool> suspended = false;
|
||||
};
|
||||
|
||||
}// namespace skullc::coro
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user