5#include <initializer_list>
14#include "driver/gptimer.h"
17#include <hardware/timer.h>
25template<
typename T> ESPHOME_ALWAYS_INLINE
void atomic_set(std::atomic<T> &atom, T value) {
26 atom.store(value, std::memory_order_release);
28template<
typename T> ESPHOME_ALWAYS_INLINE T
atomic_get(
const std::atomic<T> &atom) {
29 return atom.load(std::memory_order_acquire);
47 ESPHOME_ALWAYS_INLINE
size_t size()
const {
return this->
size_; }
48 std::span<const uint8_t>
bytes()
const {
return {this->
data_.data(), this->
size_}; }
50 std::span<const uint8_t>
payload()
const {
return this->
bytes().subspan(1); }
53 ESPHOME_ALWAYS_INLINE
bool push_back(uint8_t data) {
54 if (this->
size_ == MAX_LENGTH) {
62 std::array<uint8_t, MAX_LENGTH>
data_{};
71 static_assert((SIZE & (SIZE - 1)) == 0,
"SIZE must be a power of two");
81 ESPHOME_ALWAYS_INLINE uint8_t
count_()
const {
85 ESPHOME_ALWAYS_INLINE
void advance_(std::atomic<uint8_t> &inx) {
100 void setup()
override;
102 void loop()
override;
106#ifdef HDMI_CEC_MESSAGE_CALLBACK_COUNT
115 bool send(uint8_t source, uint8_t destination, std::span<const uint8_t> payload);
116 bool send(uint8_t destination, std::span<const uint8_t> payload) {
117 return this->
send(this->
address_, destination, payload);
120 bool send(uint8_t destination, std::initializer_list<uint8_t> payload) {
121 return this->
send(destination, std::span<const uint8_t>(payload.begin(), payload.size()));
144 static bool ack_timer_isr_s(gptimer_handle_t timer,
const gptimer_alarm_event_data_t *edata,
void *arg);
176#ifdef HDMI_CEC_MESSAGE_CALLBACK_COUNT
177 StaticCallbackManager<HDMI_CEC_MESSAGE_CALLBACK_COUNT, void(uint8_t, uint8_t, std::span<const uint8_t>)>
Copy of GPIOPin that is safe to use from ISRs (with no virtual functions)
void digital_write(bool value)
void pin_mode(gpio::Flags flags)
CallbackManager backed by StaticVector for compile-time-known callback counts.
One CEC bus message: a header byte with the initiator and destination addresses, then up to 15 payloa...
ESPHOME_ALWAYS_INLINE bool push_back(uint8_t data)
False when the frame is full already; the bus allows no more.
std::span< const uint8_t > bytes() const
static constexpr size_t MAX_LENGTH
static constexpr size_t MAX_PAYLOAD_LENGTH
std::span< const uint8_t > payload() const
The bytes after the header, starting with the opcode.
ESPHOME_ALWAYS_INLINE uint8_t initiator_addr() const
ESPHOME_ALWAYS_INLINE size_t size() const
ESPHOME_ALWAYS_INLINE bool is_broadcast() const
ESPHOME_ALWAYS_INLINE uint8_t destination_addr() const
std::array< uint8_t, MAX_LENGTH > data_
ESPHOME_ALWAYS_INLINE void clear()
A queue of frames between one producer and one consumer, for instance the receive interrupt and loop(...
std::atomic< uint8_t > front_inx_
ESPHOME_ALWAYS_INLINE void pop_front()
ESPHOME_ALWAYS_INLINE void advance_(std::atomic< uint8_t > &inx)
ESPHOME_ALWAYS_INLINE Frame * back()
std::atomic< uint8_t > back_inx_
ESPHOME_ALWAYS_INLINE void push_back()
ESPHOME_ALWAYS_INLINE Frame * slot_(const std::atomic< uint8_t > &inx)
ESPHOME_ALWAYS_INLINE uint8_t count_() const
ESPHOME_ALWAYS_INLINE Frame * front()
ESPHOME_ALWAYS_INLINE bool is_empty() const
std::array< Frame, SIZE > store_
The HDMI-CEC bus on one GPIO pin.
std::atomic< uint32_t > last_edge_us_
void dump_config() override
FrameRingBuffer< QUEUE_SIZE > send_queue_
uint8_t reported_dropped_frames_
static void gpio_isr_s(HDMICEC *self)
std::atomic< uint8_t > dropped_frames_
uint32_t last_falling_edge_us_
bool send(uint8_t source, uint8_t destination, std::span< const uint8_t > payload)
Queue a frame for transmission; false when the queue is full or in monitor mode.
gptimer_handle_t ack_timer_
static void ack_timer_isr_s()
StaticCallbackManager< HDMI_CEC_MESSAGE_CALLBACK_COUNT, void(uint8_t, uint8_t, std::span< const uint8_t >)> message_callbacks_
uint8_t transmit_attempts_
uint8_t recv_bit_counter_
ISRInternalGPIOPin isr_pin_
bool send(uint8_t destination, std::initializer_list< uint8_t > payload)
For lambdas: id(cec).send(0x0, {0x44, 0x41});.
void add_on_message_callback(F &&callback)
Called with the initiator, the destination and the payload (opcode first) of every received frame; th...
void send_ack_()
Pull the line low for the acknowledge bit of a frame addressed to us.
ESPHOME_ALWAYS_INLINE void set_pin_output_low_()
static constexpr size_t QUEUE_SIZE
void finish_attempt_(bool sent_ok)
Keep the frame for another attempt, or drop it once the attempts are used up.
ReceiverState receiver_state_
uint16_t required_idle_period_
void set_promiscuous_mode(bool promiscuous)
bool send(uint8_t destination, std::span< const uint8_t > payload)
void set_monitor_mode(bool monitor_mode)
uint32_t last_attempt_us_
std::atomic< bool > eom_received_
HDMICEC(InternalGPIOPin *pin, uint8_t address)
uint8_t n_bytes_received_
bool send_bit_(bool bit_value)
Send one bit; false when a logical 1 found the line held low by another initiator.
@ RECEIVER_STATE_WAITING_FOR_ACK
@ RECEIVER_STATE_RECEIVING_BYTE
@ RECEIVER_STATE_WAITING_FOR_EOM
ESPHOME_ALWAYS_INLINE void set_pin_input_high_()
FrameRingBuffer< QUEUE_SIZE > frames_queue_
bool transmit_frame_(const Frame *frame)
Drive the frame onto the line; false when another initiator sent at the same time.
ESPHOME_ALWAYS_INLINE void atomic_set(std::atomic< T > &atom, T value)
ESPHOME_ALWAYS_INLINE T atomic_get(const std::atomic< T > &atom)