21#ifndef I2C_DEVICE_UTILITIES_H
22#define I2C_DEVICE_UTILITIES_H
27#include "initializer_list.h"
37 template<
typename T,
typename FUNCTOR = functor::Identity<T>>
class WriteContent
39 using ARG_TYPE =
typename FUNCTOR::ARG_TYPE;
41 WriteContent() =
default;
42 WriteContent(uint8_t reg,
const ARG_TYPE& value)
43 : register_{reg}, value_{
functor::Functor<FUNCTOR>::call(value)} {}
81 template<
typename MANAGER,
typename T,
typename FUNCTOR = functor::Identity<T>>
84 using ARG_TYPE =
typename FUNCTOR::ARG_TYPE;
85 using PARENT =
typename MANAGER::template FUTURE<T, uint8_t>;
89 using ABSTRACT_FUTURE =
typename MANAGER::ABSTRACT_FUTURE;
93 return this->get_input();
107 : PARENT{reg, notification} {}
112 if (!PARENT::get(temp))
return false;
153 template<
typename MANAGER, u
int8_t REGISTER,
typename T,
typename FUNCTOR = functor::Identity<T>>
166 :
PARENT{REGISTER, notification} {}
170 PARENT::reset_(REGISTER);
201 template<
typename MANAGER,
typename T,
typename FUNCTOR = functor::Identity<T>>
204 using CONTENT = WriteContent<T, FUNCTOR>;
205 using ARG_TYPE =
typename FUNCTOR::ARG_TYPE;
206 using PARENT =
typename MANAGER::template FUTURE<void, CONTENT>;
210 using ABSTRACT_FUTURE =
typename MANAGER::ABSTRACT_FUTURE;
214 return this->get_input().reg();
228 uint8_t reg,
const ARG_TYPE& value,
230 : PARENT{CONTENT{reg, value}, notification} {}
268 template<
typename MANAGER, u
int8_t REGISTER,
typename T,
typename FUNCTOR = functor::Identity<T>>
271 using ARG_TYPE =
typename FUNCTOR::ARG_TYPE;
272 using CONTENT = WriteContent<T, FUNCTOR>;
284 : PARENT{REGISTER, value, notification} {}
286 void reset_(
const T& input = T{})
288 PARENT::reset_(CONTENT{REGISTER, input});
295 class WriteMultiContentBase
298 WriteMultiContentBase() =
default;
303 Pair(uint8_t reg, T value = T{}) : reg_{reg}, value_{value} {}
310 const T* val_ptr = values.
begin();
311 while (val_ptr != values.
end())
313 (*content).value_ = *val_ptr++;
319 template<
typename T, uint8_t... REGISTERS>
320 class WriteMultiContent :
public WriteMultiContentBase<T>
322 using PARENT = WriteMultiContentBase<T>;
326 PARENT::init(content_, values);
329 T value(uint8_t index)
const
331 return content_[index].value_;
335 typename PARENT::Pair content_[
sizeof...(REGISTERS)];
358 template<
typename MANAGER,
typename T, uint8_t... REGISTERS>
361 using CONTENT = WriteMultiContent<T, REGISTERS...>;
362 using PARENT =
typename MANAGER::template FUTURE<void, CONTENT>;
366 using ABSTRACT_FUTURE =
typename MANAGER::ABSTRACT_FUTURE;
395 : PARENT{CONTENT{values}, notification} {}
399 PARENT::reset_(CONTENT{values});
406 template<
typename MANAGER>
class I2CDevice;
408 I2CDevice<MANAGER>& device,
const uint8_t* buffer, uint8_t size);
412 template<
typename MANAGER>
class I2CFutureHelper
414 static_assert(I2CManager_trait<MANAGER>::IS_I2CMANAGER,
"MANAGER must be an I2C Manager");
417 using DEVICE = I2CDevice<MANAGER>;
418 using ABSTRACT_FUTURE =
typename MANAGER::ABSTRACT_FUTURE;
420 I2CFutureHelper() =
default;
423 bool check_error(
int error, ABSTRACT_FUTURE& target)
425 if (error == 0)
return true;
426 target.set_future_error_(error);
430 bool check_status(
const ABSTRACT_FUTURE& source,
future::FutureStatus status, ABSTRACT_FUTURE& target)
435 target.set_future_error_(source.error());
441 static constexpr I2CLightCommand read(uint8_t read_count = 0,
bool finish_future =
false,
bool stop =
false)
443 return DEVICE::read(read_count, finish_future, stop);
446 static constexpr I2CLightCommand write(uint8_t write_count = 0,
bool finish_future =
false,
bool stop =
false)
448 return DEVICE::write(write_count, finish_future, stop);
453 return device_->launch_commands(
future, commands);
456 void set_device(DEVICE& device)
468 DEVICE* device_ =
nullptr;
474 template<
typename MANAGER>
class AbstractI2CFuturesGroup :
476 public I2CFutureHelper<MANAGER>
478 using HELPER = I2CFutureHelper<MANAGER>;
482 using ABSTRACT_FUTURE =
typename MANAGER::ABSTRACT_FUTURE;
485 : GROUP{notification} {}
488 bool check_error(
int error)
490 return HELPER::check_error(error, *
this);
495 return HELPER::check_status(source, status, *
this);
553 public AbstractI2CFuturesGroup<MANAGER>
555 using PARENT = AbstractI2CFuturesGroup<MANAGER>;
556 using MANAGER_TRAIT = I2CManager_trait<MANAGER>;
557 using ABSTRACT_FUTURE =
typename PARENT::ABSTRACT_FUTURE;
570 : PARENT{notification}, futures_{futures}, size_{size} {}
586 bool start(
typename PARENT::DEVICE& device)
588 PARENT::set_device(device);
589 if (MANAGER_TRAIT::IS_ASYNC)
591 return next_future();
596 while (index_ != size_)
598 if (!next_future())
return false;
608 if (!is_own_future(
future))
return;
609 PARENT::on_status_change_pre_step(
future, status);
611 if (MANAGER_TRAIT::IS_ASYNC)
621 bool is_own_future(
const ABSTRACT_FUTURE&
future)
const
624 ABSTRACT_FUTURE** ptr = futures_;
627 if (*ptr == &
future)
return true;
640 ABSTRACT_FUTURE&
future = *futures_[index_++];
642 const bool stop = (index_ == size_);
643 const bool read =
future.get_future_value_size_();
644 const bool write =
future.get_storage_value_size_();
648 error = PARENT::launch_commands(
future, {PARENT::write(), PARENT::read(0,
false, stop)});
652 error = PARENT::launch_commands(
future, {PARENT::read(0,
false, stop)});
656 error = PARENT::launch_commands(
future, {PARENT::write(0,
false, stop)});
662 if (!group.start(PARENT::device()))
665 return PARENT::check_error(error);
668 ABSTRACT_FUTURE** futures_;
691 using PARENT = AbstractI2CFuturesGroup<MANAGER>;
692 using MANAGER_TRAIT = I2CManager_trait<MANAGER>;
693 using ABSTRACT_FUTURE =
typename PARENT::ABSTRACT_FUTURE;
695 using CONTENT = WriteContent<uint8_t>;
696 static constexpr uint8_t FUTURE_SIZE = F::IN_SIZE;
710 : PARENT{notification}, address_{address}, size_{size}
712 PARENT::init({&future_}, size / FUTURE_SIZE);
737 bool start(
typename PARENT::DEVICE& device)
739 PARENT::set_device(device);
740 if (MANAGER_TRAIT::IS_ASYNC)
742 return next_future();
749 if (!next_future())
return false;
762 uint8_t reg = next_byte();
763 uint8_t val = next_byte();
764 const bool stop = (size_ == 0);
765 future_.reset_(CONTENT{reg, val});
766 return PARENT::check_error(
767 PARENT::launch_commands(future_, {PARENT::write(0,
false, stop)}));
780 if (&
future != &future_)
return;
781 PARENT::on_status_change_pre_step(
future, status);
783 if (MANAGER_TRAIT::IS_ASYNC)
798 friend bool await_same_future_group<MANAGER>(I2CDevice<MANAGER>&,
const uint8_t*, uint8_t);
818 template<
typename MANAGER>
822 if (!
future.start(device))
return false;
826#ifdef EXPERIMENTAL_API
857 static constexpr uint8_t END = 0x00;
858 static constexpr uint8_t WRITE = 0x10;
859 static constexpr uint8_t READ = 0x20;
860 static constexpr uint8_t MARKER = 0x30;
861 static constexpr uint8_t LOOP = 0x31;
862 static constexpr uint8_t INCLUDE = 0x40;
864 static constexpr uint8_t STOP_MASK = 0x80;
865 static constexpr uint8_t ACTION_MASK = 0x70;
866 static constexpr uint8_t COUNT_MASK = 0x0F;
868 static constexpr uint8_t write(uint8_t count,
bool stop =
false)
870 return WRITE | (count & COUNT_MASK) | (stop ? STOP_MASK : 0x00);
872 static constexpr uint8_t read(uint8_t count,
bool stop =
false)
874 return READ | (count & COUNT_MASK) | (stop ? STOP_MASK : 0x00);
876 static constexpr bool is_write(uint8_t action)
878 return (action & ACTION_MASK) == WRITE;
880 static constexpr bool is_read(uint8_t action)
882 return (action & ACTION_MASK) == READ;
884 static constexpr bool is_stop(uint8_t action)
886 return action & STOP_MASK;
888 static constexpr uint8_t count(uint8_t action)
890 return action & COUNT_MASK;
895 template<
typename MANAGER>
class ComplexI2CFuturesGroup :
public AbstractI2CFuturesGroup<MANAGER>
897 using PARENT = AbstractI2CFuturesGroup<MANAGER>;
898 using MANAGER_TRAIT = I2CManager_trait<MANAGER>;
899 using ABSTRACT_FUTURE =
typename PARENT::ABSTRACT_FUTURE;
902 ComplexI2CFuturesGroup(uint16_t flash_config,
904 : PARENT{notification}, address_{flash_config} {}
906 enum class ProcessAction : uint8_t
916 ProcessAction process_action()
918 while ((action_ = next_byte()) == actions::LOOP)
923 if (action_ == actions::END)
926 PARENT::set_future_finish_();
927 return ProcessAction::DONE;
929 if (action_ == actions::MARKER)
return ProcessAction::MARKER;
930 if (action_ == actions::INCLUDE)
return ProcessAction::INCLUDE;
931 if (actions::is_read(action_))
return ProcessAction::READ;
932 if (actions::is_write(action_))
return ProcessAction::WRITE;
936 return ProcessAction::DONE;
953 return actions::is_stop(action_);
956 uint8_t count()
const
958 return actions::count(action_);
Abstract class to allow aggregation of several futures.
Base class for all I2C devices.
Abstract class to allow aggregation of several futures in relation to I2C transactions.
bool start(typename PARENT::DEVICE &device)
Start the I2C transactions needed by this group of futures.
I2CFuturesGroup(ABSTRACT_FUTURE **futures, uint8_t size, future::FutureNotification notification=future::FutureNotification::NONE)
Called by subclass constructor, this constructs a new I2CFuturesGroup instance with the provided list...
Class to allow dynamic creation of futures from values stored in flash memory, leading to launch of I...
I2CSameFutureGroup(uint16_t address, uint8_t size, future::FutureNotification notification=future::FutureNotification::NONE)
Construct a new I2CSameFutureGroup from an array of bytes stored in flash memory.
bool start(typename PARENT::DEVICE &device)
Start the I2C transactions needed by this group of futures.
General Future that can be used to read an I2C device register.
ReadRegisterFuture(uint8_t reg, future::FutureNotification notification=future::FutureNotification::NONE)
Create a ReadRegisterFuture future for a given device register reg.
Generic Future that can be used to read an I2C device register.
TReadRegisterFuture(future::FutureNotification notification=future::FutureNotification::NONE)
Create a TReadRegisterFuture future.
Generic Future that can be used to write to several I2C device registers.
static constexpr uint8_t WRITE_SIZE
The number of bytes to write for each register command in the I2C transaction.
TWriteMultiRegisterFuture(std::initializer_list< T > values, future::FutureNotification notification=future::FutureNotification::NONE)
Create a TWriteMultiRegisterFuture future.
static constexpr uint8_t NUM_WRITES
Number of write commands to use inside the complete I2C transaction in order to perform this multiple...
Generic Future that can be used to write to an I2C device register.
TWriteRegisterFuture(const ARG_TYPE &value=ARG_TYPE{}, future::FutureNotification notification=future::FutureNotification::NONE)
Create a TWriteRegisterFuture future.
General Future that can be used to write to an I2C device register.
WriteRegisterFuture(uint8_t reg, const ARG_TYPE &value, future::FutureNotification notification=future::FutureNotification::NONE)
Create a WriteRegisterFuture future for a given device register reg.
C++ standard support for "initializer_list".
constexpr const T * begin() const
The first element of this initializer_list.
constexpr const T * end() const
One past the last element of this initializer_list.
Iterable class that can embed arrays or initializer lists through implicit conversion.
Useful functors that can be used as template arguments, particularly in I2C device utilities.
Utility API to handle the concept of futures.
#define DECL_FUTURE_LISTENERS_FRIEND
This macro shall be used in a class containing a private callback method void on_status_change(const ...
Utilities to convert arrays into an iterable (usable if for x: list construct).
constexpr const int EILSEQ
Illegal byte sequence.
T * read_flash(uint16_t address, T *buffer, uint8_t size)
Read flash memory content at given address into buffer.
This namespace defines a few useful functors.
Contains the API around Future implementation.
FutureStatus
Status of a Future.
@ READY
The status of a Future once its output value has been fully set by a provider.
FutureNotification
Notification(s) dispatched by a Future.
@ STATUS
Notification is dispatched whenever the Future status changes.
@ NONE
No notification is dispatched by the Future.
Define API to define and manage I2C devices.
bool await_same_future_group(I2CDevice< MANAGER > &device, const uint8_t *buffer, uint8_t size)
Helper function that creates a I2CSameFutureGroup instance for the provided flash array,...
void register_handler(Handler &handler)
Register a class instance containing methods that shall be called back by an ISR.
void unregister_handler(Handler &handler)
Unregister a class instance that was previously registered with interrupt::register_handler.
void init()
This function must be called once in your program, before any use of an SPI device.
Utility to instantiate and execute a functor from its type.
General utilities API that have broad application in programs.