22#ifndef I2C_HANDLER_ATMEGA_HH
23#define I2C_HANDLER_ATMEGA_HH
25#include <util/delay_basic.h>
37#error "i2c_handler_atmega.h shall not be directly included! Include 'i2c_handler.h' instead."
41#error "i2c_handler_atmega.h cannot be included in an ATtiny program!"
49#define I2C_TRUE_ASYNC 1
57#define REGISTER_I2C_ISR(MANAGER) \
60 i2c::isr_handler::i2c_change<MANAGER>(); \
76#define REGISTER_I2C_ISR_FUNCTION(MANAGER, CALLBACK) \
79 i2c::isr_handler::i2c_change_function<MANAGER, CALLBACK>(); \
96#define REGISTER_I2C_ISR_METHOD(MANAGER, HANDLER, CALLBACK) \
99 i2c::isr_handler::i2c_change_method<MANAGER, HANDLER, CALLBACK>(); \
108#define DECL_I2C_ISR_HANDLERS_FRIEND \
109 friend struct i2c::isr_handler; \
131 template<I2CErrorPolicy POLICY = I2CErrorPolicy::DO_NOTHING>
struct I2CErrorPolicySupport
133 I2CErrorPolicySupport() =
default;
142 I2CErrorPolicySupport() =
default;
151 I2CErrorPolicySupport() =
default;
153 void handle_error(
const I2CCommand<T>& current,
containers::Queue<I2CCommand<T>>& commands)
156 const auto future = current.future();
157 I2CCommand<T> command;
158 while (commands.peek_(command))
160 if (command.future() !=
future)
162 commands.pull_(command);
172 template<I2CMode MODE_,
bool HAS_STATUS_ = false,
typename STATUS_HOOK_ = I2C_STATUS_HOOK>
173 class ATmegaI2CSyncHandler
176 using MODE_TRAIT = I2CMode_trait<MODE_>;
177 using I2C_TRAIT = board_traits::TWI_trait;
178 using REG8 = board_traits::REG8;
179 using STATUS = I2CStatusSupport<HAS_STATUS_, STATUS_HOOK_>;
182 explicit ATmegaI2CSyncHandler(STATUS_HOOK_ status_hook =
nullptr) : status_hook_{status_hook} {}
187 I2C_TRAIT::PORT |= I2C_TRAIT::SCL_SDA_MASK;
189 TWBR_ = MODE_TRAIT::FREQUENCY;
200 I2C_TRAIT::PORT &=
bits::COMPL(I2C_TRAIT::SCL_SDA_MASK);
207 return wait_twint(Status::START_TRANSMITTED);
210 bool exec_repeat_start_()
213 return wait_twint(Status::REPEAT_START_TRANSMITTED);
216 bool exec_send_slar_(uint8_t target)
218 send_byte(target | 0x01U);
219 return wait_twint(Status::SLA_R_TRANSMITTED_ACK);
222 bool exec_send_slaw_(uint8_t target)
225 return wait_twint(Status::SLA_W_TRANSMITTED_ACK);
228 bool exec_send_data_(uint8_t data)
231 return wait_twint(Status::DATA_TRANSMITTED_ACK);
234 bool exec_receive_data_(
bool last_byte, uint8_t& data)
236 const uint8_t twcr = (last_byte ?
bits::BV8(TWEN, TWINT) :
bits::BV8(TWEN, TWINT, TWEA));
237 const Status expected = (last_byte ? Status::DATA_RECEIVED_NACK : Status::DATA_RECEIVED_ACK);
239 if (wait_twint(expected))
253 static constexpr const REG8 TWBR_{TWBR};
254 static constexpr const REG8 TWSR_{TWSR};
255 static constexpr const REG8 TWCR_{TWCR};
256 static constexpr const REG8 TWDR_{TWDR};
258 void send_byte(uint8_t data)
264 bool wait_twint(Status expected_status)
266 TWCR_.loop_until_bit_set(TWINT);
268 status_hook_.call_hook(expected_status, status);
269 return (status == expected_status);
301 template<I2CMode MODE_,
bool HAS_STATUS_,
typename STATUS_HOOK_,
bool HAS_DEBUG_,
typename DEBUG_HOOK_>
304 MODE_, STATUS_HOOK_, HAS_DEBUG_, DEBUG_HOOK_>
308 MODE_, STATUS_HOOK_, HAS_DEBUG_, DEBUG_HOOK_>;
319 STATUS_HOOK_ status_hook =
nullptr, DEBUG_HOOK_ debug_hook =
nullptr)
320 :
PARENT{status_hook, debug_hook} {}
323 template<
typename>
friend class I2CDevice;
357 bool HAS_STATUS_,
typename STATUS_HOOK_,
bool HAS_DEBUG_,
typename DEBUG_HOOK_>
361 using MODE_TRAIT = I2CMode_trait<MODE_>;
362 using I2C_TRAIT = board_traits::TWI_trait;
363 using REG8 = board_traits::REG8;
364 using STATUS = I2CStatusSupport<HAS_STATUS_, STATUS_HOOK_>;
365 using DEBUG = I2CDebugSupport<HAS_DEBUG_, DEBUG_HOOK_>;
366 using POLICY = I2CErrorPolicySupport<POLICY_>;
420 I2C_TRAIT::PORT |= I2C_TRAIT::SCL_SDA_MASK;
422 TWBR_ = MODE_TRAIT::FREQUENCY;
439 I2C_TRAIT::PORT &=
bits::COMPL(I2C_TRAIT::SCL_SDA_MASK);
444 template<u
int8_t SIZE>
447 STATUS_HOOK_ status_hook =
nullptr,
448 DEBUG_HOOK_ debug_hook =
nullptr)
450 status_hook_{status_hook}, debug_hook_{debug_hook} {}
454 bool ensure_num_commands_(uint8_t num_commands)
const
456 return commands_.free_() >= num_commands;
465 void last_command_pushed_()
468 if (command_.type().is_none())
471 dequeue_command_(
true);
475 static constexpr const REG8 TWBR_{TWBR};
476 static constexpr const REG8 TWSR_{TWSR};
477 static constexpr const REG8 TWCR_{TWCR};
478 static constexpr const REG8 TWDR_{TWDR};
481 enum class State : uint8_t
495 return command_.future();
498 void send_byte(uint8_t data)
505 void dequeue_command_(
bool first)
507 if (!commands_.pull_(command_))
510 current_ = State::NONE;
517 current_ = State::START;
521 exec_repeat_start_();
530 return (command_.type().is_write() ? State::SLAW : State::SLAR);
534 if (command_.byte_count() > 1)
537 return State::RECV_LAST;
539 case State::RECV_LAST:
546 if (command_.byte_count() >= 1)
562 TWCR_ =
bits::BV8(TWEN, TWIE, TWINT, TWSTA);
564 void exec_repeat_start_()
568 TWCR_ =
bits::BV8(TWEN, TWIE, TWINT, TWSTA);
570 void exec_send_slar_()
575 send_byte(command_.target() | 0x01U);
577 void exec_send_slaw_()
582 send_byte(command_.target());
584 void exec_send_data_()
589 bool ok =
future.get_storage_value_(data);
593 command_.decrement_byte_count();
600 void exec_receive_data_()
603 if (command_.byte_count() == 1)
615 TWCR_ =
bits::BV8(TWEN, TWIE, TWINT, TWEA);
618 void exec_stop_(
bool error =
false)
625 current_ = State::NONE;
628 _delay_loop_1(MODE_TRAIT::DELAY_AFTER_STOP);
631 bool is_end_transaction()
const
633 return command_.type().is_end();
638 if (check_no_error(
future, status))
return true;
639 policy_.handle_error(command_, commands_);
642 dequeue_command_(
true);
651 if (!handle_no_error(
future, status))
655 if ((current_ == State::RECV) || (current_ == State::RECV_LAST))
657 const uint8_t data = TWDR_;
658 bool ok =
future.set_future_value_(data);
661 command_.decrement_byte_count();
669 current_ = next_state_();
682 case State::RECV_LAST:
683 exec_receive_data_();
696 if (command_.type().is_finish())
697 future.set_future_finish_();
700 if (commands_.empty_())
703 else if (command_.type().is_stop())
707 dequeue_command_(
true);
711 dequeue_command_(
false);
718 status_hook_.call_hook(expected_status_, status);
719 if (status == expected_status_)
return true;
723 && (command_.byte_count() == 0))
739 State current_ = State::NONE;
748 template<
typename>
friend class I2CDevice;
749 friend struct isr_handler;
766 template<I2CMode MODE_, I2CErrorPolicy POLICY_ = I2CErrorPolicy::CLEAR_ALL_COMMANDS>
780 template<u
int8_t SIZE>
823 template<u
int8_t SIZE>
826 :
PARENT{buffer, nullptr, debug_hook}
868 template<u
int8_t SIZE>
871 :
PARENT{buffer, status_hook}
920 template<u
int8_t SIZE>
922 typename PARENT::I2CCOMMAND (&buffer)[SIZE], STATUS_HOOK_ status_hook, DEBUG_HOOK_ debug_hook)
923 :
PARENT{buffer, status_hook, debug_hook}
937 template<I2CMode MODE_>
960 template<I2CMode MODE_,
typename STATUS_HOOK_ = I2C_STATUS_HOOK>
982 template<I2CMode MODE_,
typename DEBUG_HOOK_ = I2C_DEBUG_HOOK>
1008 template<I2CMode MODE_,
typename STATUS_HOOK_ = I2C_STATUS_HOOK,
typename DEBUG_HOOK_ = I2C_DEBUG_HOOK>
1016 :
PARENT{status_hook, debug_hook} {}
1023 template<I2CMode MODE_, I2CErrorPolicy POLICY_>
1025 : I2CManager_trait_impl<true, false, false, MODE_> {};
1027 template<I2CMode MODE_, I2CErrorPolicy POLICY_,
typename DEBUG_HOOK_>
1028 struct I2CManager_trait<I2CAsyncDebugManager<MODE_, POLICY_, DEBUG_HOOK_>>
1029 : I2CManager_trait_impl<true, false, true, MODE_> {};
1031 template<I2CMode MODE_, I2CErrorPolicy POLICY_,
typename STATUS_HOOK_>
1032 struct I2CManager_trait<I2CAsyncStatusManager<MODE_, POLICY_, STATUS_HOOK_>>
1033 : I2CManager_trait_impl<true, true, false, MODE_> {};
1035 template<I2CMode MODE_, I2CErrorPolicy POLICY_,
typename STATUS_HOOK_,
typename DEBUG_HOOK_>
1036 struct I2CManager_trait<I2CAsyncStatusDebugManager<MODE_, POLICY_, STATUS_HOOK_, DEBUG_HOOK_>>
1037 : I2CManager_trait_impl<true, true, true, MODE_> {};
1043 template<
typename MANAGER>
1044 static void i2c_change()
1046 static_assert(I2CManager_trait<MANAGER>::IS_I2CMANAGER,
"MANAGER must be an I2C Manager");
1047 static_assert(I2CManager_trait<MANAGER>::IS_ASYNC,
"MANAGER must be an asynchronous I2C Manager");
1048 interrupt::HandlerHolder<MANAGER>::handler()->i2c_change();
1051 template<
typename MANAGER,
void (*CALLBACK_)(I2CCallback,
typename MANAGER::ABSTRACT_FUTURE&)>
1052 static void i2c_change_function()
1054 using interrupt::HandlerHolder;
1055 static_assert(I2CManager_trait<MANAGER>::IS_I2CMANAGER,
"MANAGER must be an I2C Manager");
1056 static_assert(I2CManager_trait<MANAGER>::IS_ASYNC,
"MANAGER must be an asynchronous I2C Manager");
1057 typename MANAGER::ABSTRACT_FUTURE&
future = HandlerHolder<MANAGER>::handler()->current_future();
1058 I2CCallback callback = HandlerHolder<MANAGER>::handler()->i2c_change();
1059 if (callback != I2CCallback::NONE)
1061 CALLBACK_(callback,
future);
1065 template<
typename MANAGER,
typename HANDLER_,
1066 void (HANDLER_::*CALLBACK_)(
I2CCallback,
typename MANAGER::ABSTRACT_FUTURE&)>
1067 static void i2c_change_method()
1069 using interrupt::HandlerHolder;
1070 using interrupt::CallbackHandler;
1071 static_assert(I2CManager_trait<MANAGER>::IS_I2CMANAGER,
"MANAGER must be an I2C Manager");
1072 static_assert(I2CManager_trait<MANAGER>::IS_ASYNC,
"MANAGER must be an asynchronous I2C Manager");
1073 typename MANAGER::ABSTRACT_FUTURE&
future = HandlerHolder<MANAGER>::handler()->current_future();
1074 I2CCallback callback = HandlerHolder<MANAGER>::handler()->i2c_change();
1075 if (callback != I2CCallback::NONE)
1078 CallbackHandler<void (HANDLER_::*)(
I2CCallback,
typename MANAGER::ABSTRACT_FUTURE&), CALLBACK_>;
1079 HANDLER::call(callback,
future);
Useful bits manipulation utilities.
Base class for all FakeFutures.
Base class for all Futures.
Actual FakeFuture, it has the exact same API as Future and can be used in lieu of Future.
Represent a value to be obtained, in some asynchronous way, in the future.
Abstract asynchronous I2C Manager.
I2CCommand< ABSTRACT_FUTURE > I2CCOMMAND
The type of I2CCommand to use in the buffer passed to the constructor of this AbstractI2CAsyncManager...
void begin_()
Prepare and enable the MCU for I2C transmission.
void begin()
Prepare and enable the MCU for I2C transmission.
future::AbstractFuture ABSTRACT_FUTURE
The abstract base class of all futures to be defined for this I2C Manager.
void end()
Disable MCU I2C transmission.
void end_()
Disable MCU I2C transmission.
Abstract synchronous I2C Manager for ATmega architecture.
Abstract synchronous I2C Manager for all MCU architectures.
future::AbstractFakeFuture ABSTRACT_FUTURE
The abstract base class of all futures to be defined for this I2C Manager.
Asynchronous I2C Manager for ATmega architecture with debug facility.
I2CAsyncDebugManager(typename PARENT::I2CCOMMAND(&buffer)[SIZE], DEBUG_HOOK_ debug_hook)
Create an asynchronous I2C Manager for ATmega MCUs.
Asynchronous I2C Manager for ATmega architecture.
I2CAsyncManager(typename PARENT::I2CCOMMAND(&buffer)[SIZE])
Create an asynchronous I2C Manager for ATmega MCUs.
Asynchronous I2C Manager for ATmega architecture with debug and status notification facilities.
I2CAsyncStatusDebugManager(typename PARENT::I2CCOMMAND(&buffer)[SIZE], STATUS_HOOK_ status_hook, DEBUG_HOOK_ debug_hook)
Create an asynchronous I2C Manager for ATmega MCUs.
Asynchronous I2C Manager for ATmega architecture with status notification facility.
I2CAsyncStatusManager(typename PARENT::I2CCOMMAND(&buffer)[SIZE], STATUS_HOOK_ status_hook)
Create an asynchronous I2C Manager for ATmega MCUs.
Base class for all I2C devices.
Synchronous I2C Manager for ATmega architecture with debug facility.
Synchronous I2C Manager for ATmega architecture.
Synchronous I2C Manager for ATmega architecture with status notification and debug facility.
Synchronous I2C Manager for ATmega architecture wit status notification facility.
#define UNUSED
Specific GCC attribute to declare an argument or variable unused, so that the compiler does not emit ...
Utility API to handle the concept of futures.
I2C API common definitions.
General API for handling AVR interrupt vectors.
@ NONE
No interrupt will be generated by the Anolog Comparator.
Defines utility methods for bits manipulation.
static constexpr uint8_t COMPL(uint8_t value)
Return the uint8_t 2-complement of a byte.
static constexpr uint8_t BV8(uint8_t bit)
Create a uint8_t bitmask for the given bit number.
constexpr const int EPROTO
Protocol error.
constexpr const int EILSEQ
Illegal byte sequence.
Contains the API around Future implementation.
@ ERROR
The status of a Future once a value provider has reported an error to it.
STATUS
Indicate when status should be traced.
Define API to define and manage I2C devices.
void(*)(Status expected, Status actual) I2C_STATUS_HOOK
The default status observer hook type.
@ SEND_OK
The latest sent byte has been acknowledged by the slave.
@ SEND_ERROR
The latest sent byte has not been acknowledged by the slave.
@ SLAR
A slave address has just been sent for reading.
@ RECV_OK
I2C Manager has acknowledged the latest received byte from the slave.
@ SEND
A byte has just be sent to the slave.
@ STOP
A stop condition has just been sent.
@ REPEAT_START
A repeat start condition has just been sent.
@ RECV_LAST
The last byte is being received from the slave.
@ START
A start condition has just been sent.
@ RECV
A byte is being received from the slave.
@ RECV_ERROR
I2C Manager has not acknowledged the latest received byte from the slave.
@ SLAW
A slave address has just been sent for writing.
I2CCallback
Type passed to I2C ISR registered callbacks (asynchronous I2C Manager only) when an asynchronous I2C ...
@ END_TRANSACTION
The last I2C command in a transaction has just been finished executing.
@ END_COMMAND
An I2C command has just been finished executed.
@ NONE
An I2C command is being processed (intermediate step).
@ ERROR
An error has occurred during I2C transaction execution.
void(*)(DebugStatus status, uint8_t data) I2C_DEBUG_HOOK
The default debugging hook type.
I2CErrorPolicy
I2C Manager policy to use in case of an error during I2C transaction.
@ CLEAR_ALL_COMMANDS
In case of an error during I2C transaction, then all I2CCommand currently in queue will be removed.
@ CLEAR_TRANSACTION_COMMANDS
In case of an error during I2C transaction, then all pending I2CCommand of the current transaction wi...
I2CMode
I2C available transmission modes.
Status
Transmission status codes.
@ START_TRANSMITTED
[Transmitter/Receiver modes] A START condition has been transmitted.
@ REPEAT_START_TRANSMITTED
[Transmitter/Receiver modes] A repeated START condition has been transmitted.
@ DATA_TRANSMITTED_ACK
[Transmitter mode] Data byte has been transmitted; ACK has been received.
@ SLA_W_TRANSMITTED_ACK
[Transmitter mode] SLA+W has been transmitted; ACK has been received.
@ DATA_TRANSMITTED_NACK
[Transmitter mode] Data byte has been transmitted; NOT ACK has been received.
@ DATA_RECEIVED_ACK
[Receiver mode] Data byte has been transmitted; ACK has been returned.
@ DATA_RECEIVED_NACK
[Receiver mode] Data byte has been transmitted; NOT ACK has been returned.
@ SLA_R_TRANSMITTED_ACK
[Receiver mode] SLA+R has been transmitted; ACK has been received.
@ OK
Code indicating the last called method executed as expected without any issue.
void register_handler(Handler &handler)
Register a class instance containing methods that shall be called back by an ISR.
Utility API to handle ring-buffer queue containers.
General utilities API that have broad application in programs.