#ifndef FREERTOS_ISR_DRIVERS_HPP #define FREERTOS_ISR_DRIVERS_HPP #include #include #include extern "C" { #include "FreeRTOS.h" #include "queue.h" #include "task.h" } namespace freertos { class IsrContext final { public: void merge_wake_request( BaseType_t local_request ) noexcept { if( local_request != pdFALSE ) { higher_priority_task_woken_ = pdTRUE; } } [[nodiscard]] bool wake_requested() const noexcept { return higher_priority_task_woken_ != pdFALSE; } [[nodiscard]] std::uint32_t yield_calls() const noexcept { return yield_calls_; } [[nodiscard]] bool yield_if_needed() noexcept { ++yield_calls_; bool const requested = wake_requested(); portYIELD_FROM_ISR( higher_priority_task_woken_ ); return requested; } private: BaseType_t higher_priority_task_woken_{ pdFALSE }; std::uint32_t yield_calls_{ 0U }; }; enum class IsrSendResult { queued, dropped_newest }; template< typename T, std::size_t Capacity > class StaticIsrQueue final { static_assert( Capacity > 0U ); static_assert( std::is_trivially_copyable_v< T > ); public: StaticIsrQueue( StaticQueue_t & control, std::uint8_t * bytes ) noexcept : handle_( xQueueCreateStatic( static_cast< UBaseType_t >( Capacity ), static_cast< UBaseType_t >( sizeof( T ) ), bytes, &control ) ) { } StaticIsrQueue( const StaticIsrQueue & ) = delete; StaticIsrQueue & operator=( const StaticIsrQueue & ) = delete; [[nodiscard]] bool valid() const noexcept { return handle_ != nullptr; } [[nodiscard]] IsrSendResult send_from_isr( const T & item, IsrContext & context ) noexcept { BaseType_t local_wake = pdFALSE; BaseType_t const result = xQueueSendFromISR( handle_, &item, &local_wake ); context.merge_wake_request( local_wake ); return result == pdPASS ? IsrSendResult::queued : IsrSendResult::dropped_newest; } [[nodiscard]] bool receive( T & item, TickType_t timeout ) noexcept { return xQueueReceive( handle_, &item, timeout ) == pdPASS; } private: QueueHandle_t handle_; }; class TaskNotificationRef final { public: explicit constexpr TaskNotificationRef( TaskHandle_t task ) noexcept : task_( task ) { } [[nodiscard]] bool give_from_isr( IsrContext & context ) const noexcept { BaseType_t local_wake = pdFALSE; vTaskNotifyGiveFromISR( task_, &local_wake ); context.merge_wake_request( local_wake ); return true; } private: TaskHandle_t task_; }; struct UartRegisters { volatile std::uint32_t status; volatile std::uint32_t rx_data[ 2 ]; volatile std::uint32_t rx_count; volatile std::uint32_t control; }; class UartTaskView final { public: explicit constexpr UartTaskView( UartRegisters & registers ) noexcept : registers_( ®isters ) { } void enable_rx_irq() const noexcept { registers_->control = 1U; } void inject_rx_pair_for_test( std::uint8_t first, std::uint8_t second ) const noexcept { registers_->rx_data[ 0 ] = first; registers_->rx_data[ 1 ] = second; registers_->rx_count = 2U; registers_->status = 1U; } private: UartRegisters * registers_; }; class UartIsrView final { public: explicit constexpr UartIsrView( UartRegisters & registers ) noexcept : registers_( ®isters ) { } [[nodiscard]] bool rx_ready_from_isr() const noexcept { return ( registers_->status & 1U ) != 0U && registers_->rx_count != 0U; } [[nodiscard]] std::uint8_t read_rx_from_isr() const noexcept { configASSERT( rx_ready_from_isr() ); std::uint8_t const byte = static_cast< std::uint8_t >( registers_->rx_data[ 0 ] ); registers_->rx_data[ 0 ] = registers_->rx_data[ 1 ]; --registers_->rx_count; if( registers_->rx_count == 0U ) { registers_->status = 0U; } return byte; } private: UartRegisters * registers_; }; class Uart final { public: explicit constexpr Uart( UartRegisters & registers ) noexcept : registers_( ®isters ) {} [[nodiscard]] UartTaskView task_view() const noexcept { return UartTaskView{ *registers_ }; } [[nodiscard]] UartIsrView isr_view() const noexcept { return UartIsrView{ *registers_ }; } private: UartRegisters * registers_; }; struct GpioRegisters { volatile std::uint32_t direction; volatile std::uint32_t data; volatile std::uint32_t edge_status; }; class GpioTaskView final { public: explicit constexpr GpioTaskView( GpioRegisters & registers ) noexcept : registers_( ®isters ) {} void configure_output( std::uint32_t mask ) const noexcept { registers_->direction |= mask; } void write_output( std::uint32_t mask, bool high ) const noexcept { if( high ) { registers_->data |= mask; } else { registers_->data &= ~mask; } } void inject_edge_for_test( std::uint32_t mask ) const noexcept { registers_->edge_status |= mask; } private: GpioRegisters * registers_; }; class GpioIsrView final { public: explicit constexpr GpioIsrView( GpioRegisters & registers ) noexcept : registers_( ®isters ) {} [[nodiscard]] bool edge_pending_from_isr( std::uint32_t mask ) const noexcept { return ( registers_->edge_status & mask ) != 0U; } void clear_edge_from_isr( std::uint32_t mask ) const noexcept { registers_->edge_status &= ~mask; } private: GpioRegisters * registers_; }; class GpioPin final { public: explicit constexpr GpioPin( GpioRegisters & registers ) noexcept : registers_( ®isters ) {} [[nodiscard]] GpioTaskView task_view() const noexcept { return GpioTaskView{ *registers_ }; } [[nodiscard]] GpioIsrView isr_view() const noexcept { return GpioIsrView{ *registers_ }; } private: GpioRegisters * registers_; }; } // namespace freertos #endif