279 lines
6.3 KiB
C++
279 lines
6.3 KiB
C++
#ifndef FREERTOS_ISR_DRIVERS_HPP
|
|
#define FREERTOS_ISR_DRIVERS_HPP
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <type_traits>
|
|
|
|
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
|