22#ifndef I2C_HANDLER_COMMON_HH
23#define I2C_HANDLER_COMMON_HH
26#include "boards/board_traits.h"
86 template<
bool IS_DEBUG_ = false,
typename DEBUG_HOOK_ = I2C_DEBUG_HOOK>
struct I2CDebugSupport
88 explicit I2CDebugSupport(
UNUSED DEBUG_HOOK_ hook =
nullptr) {}
94 template<
typename DEBUG_HOOK_>
struct I2CDebugSupport<true, DEBUG_HOOK_>
96 explicit I2CDebugSupport(DEBUG_HOOK_ hook) : hook_{hook} {}
97 void call_hook(DebugStatus status, uint8_t data = 0)
121 template<
bool IS_STATUS_ = false,
typename STATUS_HOOK_ = I2C_STATUS_HOOK>
struct I2CStatusSupport
123 explicit I2CStatusSupport(
UNUSED STATUS_HOOK_ hook =
nullptr) {}
129 template<
typename STATUS_HOOK_>
struct I2CStatusSupport<true, STATUS_HOOK_>
131 explicit I2CStatusSupport(STATUS_HOOK_ hook) : hook_{hook} {}
132 void call_hook(Status expected, Status actual)
134 hook_(expected, actual);
174 constexpr I2CCommandType() =
default;
175 constexpr I2CCommandType(
const I2CCommandType&) =
default;
176 I2CCommandType& operator=(
const I2CCommandType&) =
default;
180 return value_ == NONE;
182 bool is_write()
const
184 return value_ & WRITE;
188 return value_ &
STOP;
190 bool is_finish()
const
192 return value_ & FINISH;
200 explicit constexpr I2CCommandType(uint8_t value) : value_{value} {}
201 constexpr I2CCommandType(
bool write,
bool stop,
bool finish,
bool end)
202 : value_{value(write, stop, finish, end)} {}
204 void add_flags(uint8_t value)
209 static constexpr uint8_t flags(
bool stop,
bool finish,
bool end)
211 return bits::ORIF8(stop, STOP, finish, FINISH, end, END);
214 static constexpr const uint8_t
NONE = 0;
215 static constexpr const uint8_t NOT_NONE =
bits::BV8(0);
216 static constexpr const uint8_t WRITE =
bits::BV8(1);
218 static constexpr const uint8_t FINISH =
bits::BV8(3);
219 static constexpr const uint8_t END =
bits::BV8(4);
221 static constexpr uint8_t value(
bool write,
bool stop,
bool finish,
bool end)
223 return bits::ORIF8(
true, NOT_NONE, write, WRITE, stop, STOP, finish, FINISH, end, END);
226 uint8_t value_ =
NONE;
228 template<
typename>
friend class I2CDevice;
229 friend bool operator==(
const I2CCommandType&,
const I2CCommandType&);
230 friend bool operator!=(
const I2CCommandType&,
const I2CCommandType&);
233 template<
typename OSTREAM> OSTREAM& operator<<(OSTREAM& out,
const I2CCommandType& t)
235 if (t.is_none())
return out <<
F(
"NONE");
236 out << (t.is_write() ?
F(
"WRITE") :
F(
"READ"));
240 out <<
F(
"[FINISH]");
245 bool operator==(
const I2CCommandType& a,
const I2CCommandType& b);
246 bool operator!=(
const I2CCommandType& a,
const I2CCommandType& b);
267 I2CCommandType type()
const
271 I2CCommandType& type()
275 uint8_t byte_count()
const
282 constexpr I2CLightCommand(I2CCommandType type, uint8_t byte_count) : type_{type}, byte_count_{byte_count} {}
284 void decrement_byte_count()
289 void update_byte_count(uint8_t read_count, uint8_t write_count)
291 if (byte_count_ == 0)
292 byte_count_ = (type_.is_write() ? write_count : read_count);
296 I2CCommandType type_ = I2CCommandType{};
298 uint8_t byte_count_ = 0;
300 template<
typename>
friend class I2CDevice;
327 uint8_t target()
const
345 T* future_ =
nullptr;
351 template<
typename OSTREAM,
typename T> OSTREAM& operator<<(OSTREAM& out,
const I2CCommand<T>& c)
353 out <<
'{' << c.type() <<
',';
362 template<I2CMode MODE_ = I2CMode::STANDARD>
struct I2CMode_trait
364 static constexpr I2CMode MODE = MODE_;
365 static constexpr uint32_t RATE = 100'000UL;
366 static constexpr uint32_t FREQUENCY = ((F_CPU / RATE) - 16UL) / 2;
377 static constexpr I2CMode MODE = I2CMode::FAST;
378 static constexpr uint32_t RATE = 400'000UL;
379 static constexpr uint32_t FREQUENCY = ((F_CPU / RATE) - 16UL) / 2;
418 template<
typename ARCH_HANDLER_, I2CMode MODE_,
typename STATUS_HOOK_,
bool HAS_DEBUG_,
typename DEBUG_HOOK_>
423 using ARCH_HANDLER = ARCH_HANDLER_;
424 using MODE_TRAIT = I2CMode_trait<MODE_>;
425 using I2C_TRAIT = board_traits::TWI_trait;
426 using REG8 = board_traits::REG8;
427 using DEBUG = I2CDebugSupport<HAS_DEBUG_, DEBUG_HOOK_>;
492 STATUS_HOOK_ status_hook =
nullptr,
493 DEBUG_HOOK_ debug_hook =
nullptr)
494 : handler_{status_hook}, debug_hook_{debug_hook} {}
498 bool ensure_num_commands_(
UNUSED uint8_t num_commands)
const
507 const I2CCommandType type = command.type();
508 if (type.is_none())
return true;
509 if (clear_commands_)
return false;
511 bool ok = (no_stop_ ? exec_repeat_start_() : exec_start_());
512 stopped_already_ =
false;
514 return handle_error(
future);
519 if (!exec_send_slaw_(target))
520 return handle_error(
future);
522 while (command.byte_count() > 0)
524 if ((!exec_send_data_(command,
future)) && (command.byte_count() > 0))
525 return handle_error(
future);
530 if (!exec_send_slar_(target))
531 return handle_error(
future);
533 while (command.byte_count() > 0)
534 if (!exec_receive_data_(command,
future))
535 return handle_error(
future);
539 if (type.is_finish())
540 future.set_future_finish_();
545 no_stop_ = !type.is_stop();
549 void last_command_pushed_()
552 if ((!no_stop_) && (!stopped_already_) && (!clear_commands_))
557 clear_commands_ =
false;
558 stopped_already_ =
false;
565 return handler_.exec_start_();
568 bool exec_repeat_start_()
571 return handler_.exec_repeat_start_();
574 bool exec_send_slar_(uint8_t target)
577 return handler_.exec_send_slar_(target);
580 bool exec_send_slaw_(uint8_t target)
583 return handler_.exec_send_slaw_(target);
590 bool ok =
future.get_storage_value_(data);
599 command.decrement_byte_count();
600 return handler_.exec_send_data_(data);
606 const bool last_byte = (command.byte_count() == 1);
608 debug_hook_.call_hook(debug);
611 if (handler_.exec_receive_data_(last_byte, data))
613 const bool ok =
future.set_future_value_(data);
618 command.decrement_byte_count();
632 handler_.exec_stop_();
635 _delay_loop_1(MODE_TRAIT::DELAY_AFTER_STOP);
636 stopped_already_ =
true;
648 clear_commands_ =
true;
655 bool no_stop_ =
false;
656 bool clear_commands_ =
false;
657 bool stopped_already_ =
false;
659 ARCH_HANDLER handler_;
662 template<
typename>
friend class I2CDevice;
667 template<
typename T>
struct I2CManager_trait
669 static constexpr bool IS_I2CMANAGER =
false;
670 static constexpr bool IS_ASYNC =
false;
671 static constexpr bool IS_DEBUG =
false;
672 static constexpr bool IS_STATUS =
false;
673 static constexpr I2CMode MODE = I2CMode::STANDARD;
676 template<
bool IS_ASYNC_,
bool IS_STATUS_,
bool IS_DEBUG_, I2CMode MODE_>
677 struct I2CManager_trait_impl
679 static constexpr bool IS_I2CMANAGER =
true;
680 static constexpr bool IS_ASYNC = IS_ASYNC_;
681 static constexpr bool IS_DEBUG = IS_DEBUG_;
682 static constexpr bool IS_STATUS = IS_STATUS_;
683 static constexpr I2CMode MODE = MODE_;
Useful bits manipulation utilities.
Base class for all FakeFutures.
Actual FakeFuture, it has the exact same API as Future and can be used in lieu of Future.
Abstract asynchronous I2C Manager.
Abstract synchronous I2C Manager for all MCU architectures.
void begin()
Prepare and enable the MCU for I2C transmission.
void end()
Disable MCU I2C transmission.
future::AbstractFakeFuture ABSTRACT_FUTURE
The abstract base class of all futures to be defined for this I2C Manager.
void begin_()
Prepare and enable the MCU for I2C transmission.
void end_()
Disable MCU I2C transmission.
Atomic I2C command as used internally by an asynchronous I2C Manager.
Base class for all I2C devices.
Light atomic I2C command as prepared by an I2C device.
static constexpr fmtflags basefield
Bitmask constant used with setf(fmtflags, fmtflags) when changing the output base format.
static constexpr fmtflags hex
Read or write integral values using hexadecimal (0..9,A..F) base format.
#define UNUSED
Specific GCC attribute to declare an argument or variable unused, so that the compiler does not emit ...
#define F(ptr)
Force string constant to be stored as flash storage.
Utility API to handle the concept of futures.
I2C API common definitions.
C++-like std::iostream facilities.
@ NONE
No interrupt will be generated by the Anolog Comparator.
static constexpr uint8_t BV8(uint8_t bit)
Create a uint8_t bitmask for the given bit number.
static constexpr uint8_t ORIF8(bool flag1, uint8_t val1, bool flag2, uint8_t val2)
Create a uint8_t bitwise OR boolean operation between uint8_t operands, conditioned by bool flags.
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.
DEBUG
Indicate what in I2C protocol shall be debugged.
Define API to define and manage I2C devices.
void(*)(Status expected, Status actual) I2C_STATUS_HOOK
The default status observer hook type.
DebugStatus
List of debug states that are reported by the I2C Manager in debug mode.
@ 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.
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...
@ DO_NOTHING
Do nothing at all in case of an error; useful only with a synchronous I2C Manager.
I2CMode
I2C available transmission modes.
@ FAST
I2C Fast mode, less than 400KHz.
Status
Transmission status codes.
void hex(FSTREAM &stream)
Manipulator for an output or input stream, which will set the base, used to represent (output) or int...
bool operator==(const RTTTime &a, const RTTTime &b)
Compare 2 RTTTime instances.
bool operator!=(const RTTTime &a, const RTTTime &b)
Compare 2 RTTTime instances.
constexpr uint8_t calculate_delay1_count(float time_us)
Calculate the count to pass to delay1() in order to reach time_us microseconds delay.
Utility API to handle ring-buffer queue containers.
General utilities API that have broad application in programs.