FastArduino  v1.8
C++ library to build fast but small Arduino/AVR projects
i2c_device.h
Go to the documentation of this file.
1 // Copyright 2016-2021 Jean-Francois Poilpret
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
16 
21 #ifndef I2C_DEVICE_HH
22 #define I2C_DEVICE_HH
23 
24 #include "array.h"
25 #include "initializer_list.h"
26 #include "iterator.h"
27 #include "errors.h"
28 #include "i2c.h"
29 #include "future.h"
30 #include "lifecycle.h"
31 #include "i2c_handler.h"
32 #include "utilities.h"
33 
34 namespace i2c
35 {
37  // Forward declaration
38  template<typename MANAGER> class I2CFutureHelper;
40 
41  // Trick to support MODE as I2CDevice constructor template argument (deducible)
43  template<I2CMode MODE> struct Mode {};
45 
47  static constexpr Mode I2C_STANDARD = Mode<I2CMode::STANDARD>{};
49  static constexpr Mode I2C_FAST = Mode<I2CMode::FAST>{};
50 
51  // Internal (private) utility class to start/end synchronization or do nothing at all
53  template<bool DISABLE = true> class DisableInterrupts
54  {
55  DisableInterrupts()
56  {
57  cli();
58  }
59  ~DisableInterrupts()
60  {
61  SREG = sreg_;
62  }
63 
64  const uint8_t sreg_ = SREG;
65 
66  template<typename> friend class I2CDevice;
67  };
68  template<> class DisableInterrupts<false>
69  {
70  DisableInterrupts() = default;
71 
72  template<typename> friend class I2CDevice;
73  };
75 
84  template<typename MANAGER_>
85  class I2CDevice
86  {
87  public:
89  using MANAGER = MANAGER_;
90 
91  private:
92  using THIS = I2CDevice<MANAGER>;
93  using MANAGER_TRAIT = I2CManager_trait<MANAGER>;
94  // Ensure MANAGER is an accepted I2C Manager type
95  static_assert(
96  MANAGER_TRAIT::IS_I2CMANAGER, "MANAGER_ must be a valid I2C Manager type");
97 
98  protected:
104  template<typename T> using PROXY = typename MANAGER::template PROXY<T>;
105 
111  using ABSTRACT_FUTURE = typename MANAGER::ABSTRACT_FUTURE;
112 
118  template<typename OUT, typename IN> using FUTURE = typename MANAGER::template FUTURE<OUT, IN>;
119 
137  template<I2CMode MODE>
138  I2CDevice(MANAGER& manager, uint8_t device, UNUSED Mode<MODE> mode, bool auto_stop)
139  : device_{device}, handler_{manager}, auto_stop_flags_{I2CCommandType::flags(auto_stop, true, true)}
140  {
141  // Ensure that MANAGER I2C mode is compliant with the best mode for this device
142  static_assert((MODE == I2CMode::FAST) || (MODE == MANAGER_TRAIT::MODE),
143  "MANAGER_ I2CMode must be compliant with this device best mode");
144  }
145 
146  I2CDevice(const I2CDevice&) = delete;
147  I2CDevice& operator=(const I2CDevice&) = delete;
148 
156  void set_device(uint8_t device)
157  {
158  device_ = device;
159  }
160 
183  static constexpr I2CLightCommand read(uint8_t read_count = 0, bool finish_future = false, bool stop = false)
184  {
185  const I2CCommandType type{false, stop, finish_future, false};
186  return I2CLightCommand{type, read_count};
187  }
188 
212  static constexpr I2CLightCommand write(uint8_t write_count = 0, bool finish_future = false, bool stop = false)
213  {
214  const I2CCommandType type{true, stop, finish_future, false};
215  return I2CLightCommand{type, write_count};
216  }
217 
257  {
258  uint8_t num_commands = commands.size();
259  if (num_commands == 0) return errors::EINVAL;
260  {
261  // Truly asynchronous mode (ATmega only)
262  // The whole code block in launch_commands() must be synchronized
263  UNUSED auto outer_sync = DisableInterrupts<MANAGER_TRAIT::IS_ASYNC>{};
264  uint8_t max_read;
265  uint8_t max_write;
266  {
267  // Synchronous mode
268  // Only the next few method calls shall be synchronized
269  UNUSED auto inner_sync = DisableInterrupts<!MANAGER_TRAIT::IS_ASYNC>{};
270  // pre-conditions (must be synchronized)
271  if (!handler_.ensure_num_commands_(num_commands)) return errors::EAGAIN;
272  ABSTRACT_FUTURE& future = resolve(proxy);
273  max_read = future.get_future_value_size_();
274  max_write = future.get_storage_value_size_();
275  }
276 
277  // That check is normally usefull only in debug mode
278  if (MANAGER_TRAIT::IS_DEBUG && (!check_commands(max_write, max_read, commands)))
279  return errors::EINVAL;
280 
281  // Now push each command to the I2C Manager
282  int error = 0;
283  for (I2CLightCommand command : commands)
284  {
285  // update command.byte_count if 0
286  command.update_byte_count(max_read, max_write);
287  // force future finish for last command in transaction
288  --num_commands;
289  if (num_commands == 0)
290  command.type().add_flags(auto_stop_flags_);
291  // Note: on ATtiny, this method blocks until I2C command is finished!
292  if (!handler_.push_command_(command, device_, proxy))
293  {
294  error = errors::EPROTO;
295  break;
296  }
297  }
298  // Notify handler that transaction is complete
299  handler_.last_command_pushed_();
300  return error;
301  }
302  }
303 
322  template<typename F> int async_read(PROXY<F> future, bool stop = true)
323  {
324  return launch_commands(future, {write(), read(0, false, stop)});
325  }
326 
347  template<typename F, typename T = uint8_t> bool sync_read(T& result)
348  {
349  F future{};
350  if (async_read<F>(future) != 0) return false;
351  return future.get(result);
352  }
353 
370  template<typename F> int async_write(PROXY<F> future, bool stop = true)
371  {
372  return launch_commands(future, {write(0, false, stop)});
373  }
374 
393  template<typename F, typename T = uint8_t> bool sync_write(const T& value)
394  {
395  F future{value};
396  if (async_write<F>(future) != 0) return false;
397  return (future.await() == future::FutureStatus::READY);
398  }
399 
407  template<typename T> T& resolve(PROXY<T> proxy) const
408  {
409  return handler_.resolve(proxy);
410  }
411 
418  template<typename T> static PROXY<T> make_proxy(const T& target)
419  {
420  return MANAGER::LC::make_proxy(target);
421  }
422 
423  private:
424  static bool check_commands(
425  uint8_t max_write, uint8_t max_read, const utils::range<I2CLightCommand>& commands)
426  {
427  // Limit total number of bytes read or written in a transaction to 255
428  uint8_t total_read = 0;
429  uint8_t total_write = 0;
430  for (const I2CLightCommand& command : commands)
431  {
432  // Count number of bytes read and written
433  if (command.type().is_write())
434  total_write += (command.byte_count() ? command.byte_count() : max_write);
435  else
436  total_read += (command.byte_count() ? command.byte_count() : max_read);
437  }
438  // check sum of read commands byte_count matches future output size
439  // check sum of write commands byte_count matches future input size
440  return (total_write == max_write) && (total_read == max_read);
441  }
442 
443  uint8_t device_ = 0;
444  MANAGER& handler_;
445  const uint8_t auto_stop_flags_;
446  friend class I2CFutureHelper<MANAGER>;
447  };
448 }
449 
450 #endif /* I2C_DEVICE_HH */
451 
i2c::I2CDevice::sync_read
bool sync_read(T &result)
Helper method that launches I2C commands for a simple Future performing one write followed by one rea...
Definition: i2c_device.h:347
i2c::I2CDevice::read
static constexpr I2CLightCommand read(uint8_t read_count=0, bool finish_future=false, bool stop=false)
Build a read I2CLightCommand that can be later pushed to the I2C Manager for proper handling.
Definition: i2c_device.h:183
future
Contains the API around Future implementation.
Definition: future.cpp:18
i2c::I2CDevice
Base class for all I2C devices.
Definition: i2c_device.h:86
i2c::I2CDevice::write
static constexpr I2CLightCommand write(uint8_t write_count=0, bool finish_future=false, bool stop=false)
Build a write I2CLightCommand that can be later pushed to the I2C Manager for proper handling.
Definition: i2c_device.h:212
i2c::I2CDevice::async_write
int async_write(PROXY< F > future, bool stop=true)
Helper method that asynchronously launches I2C commands for a simple Future performing only one write...
Definition: i2c_device.h:370
future.h
Utility API to handle the concept of futures.
future::FutureStatus::READY
@ READY
The status of a Future once its output value has been fully set by a provider.
i2c_handler.h
Common I2C Manager API.
errors::EAGAIN
constexpr const int EAGAIN
Try again.
Definition: errors.h:46
i2c::I2CDevice::MANAGER
MANAGER_ MANAGER
the type of I2C Manager that can handle this device.
Definition: i2c_device.h:89
errors.h
Common errors definition.
i2c::I2CDevice::I2CDevice
I2CDevice(MANAGER &manager, uint8_t device, UNUSED Mode< MODE > mode, bool auto_stop)
Create a new I2C device.
Definition: i2c_device.h:138
i2c::I2CDevice::make_proxy
static PROXY< T > make_proxy(const T &target)
Create a PROXY from target.
Definition: i2c_device.h:418
i2c::I2CDevice< MANAGER >::PROXY
typename MANAGER::template PROXY< T > PROXY
The actual type used for all proxies; may be lifecycle::LightProxy or lifecycle::DirectProxy.
Definition: i2c_device.h:104
spi::Mode
Mode
SPI transmission mode.
Definition: spi.h:110
i2c::I2CDevice::async_read
int async_read(PROXY< F > future, bool stop=true)
Helper method that asynchronously launches I2C commands for a simple Future performing one write foll...
Definition: i2c_device.h:322
i2c::I2CDevice< MANAGER >::ABSTRACT_FUTURE
typename MANAGER::ABSTRACT_FUTURE ABSTRACT_FUTURE
The abstract base class of all futures to be defined for a device.
Definition: i2c_device.h:111
iterator.h
Utilities to convert arrays into an iterable (usable if for x: list construct).
UNUSED
#define UNUSED
Specific GCC attribute to declare an argument or variable unused, so that the compiler does not emit ...
Definition: defines.h:45
errors::EINVAL
constexpr const int EINVAL
Invalid argument or invalid Future.
Definition: errors.h:49
utilities.h
General utilities API that have broad application in programs.
i2c::I2CDevice::launch_commands
int launch_commands(PROXY< ABSTRACT_FUTURE > proxy, utils::range< I2CLightCommand > commands)
Launch execution (asynchronously or synchronously, depending on MANAGER) of a chain of I2CLightComman...
Definition: i2c_device.h:255
i2c::I2CDevice::resolve
T & resolve(PROXY< T > proxy) const
Resolve proxy to an actual T (typically a Future).
Definition: i2c_device.h:407
utils::range
Iterable class that can embed arrays or initializer lists through implicit conversion.
Definition: iterator.h:51
i2c::I2CDevice::sync_write
bool sync_write(const T &value)
Helper method that launches I2C commands for a simple Future performing only one write (typically for...
Definition: i2c_device.h:393
i2c
Define API to define and manage I2C devices.
Definition: i2c.cpp:18
i2c::I2CDevice::set_device
void set_device(uint8_t device)
Change the I2C address of this device.
Definition: i2c_device.h:156
lifecycle::make_proxy
Proxy< T > make_proxy(const T &dest)
Utility template function to create a Proxy<T> from dest without the need to speicify T.
Definition: lifecycle.h:593
lifecycle.h
Utility API to handle lifecycle of objects so that:
i2c::I2C_STANDARD
static constexpr Mode I2C_STANDARD
Constant determining that best supported I2C mode for an I2CDevice is STANDARD (100kHz).
Definition: i2c_device.h:47
F
#define F(ptr)
Force string constant to be stored as flash storage.
Definition: flash.h:118
i2c::I2CLightCommand
Light atomic I2C command as prepared by an I2C device.
Definition: i2c_handler_common.h:176
i2c::I2CDevice< MANAGER >::FUTURE
typename MANAGER::template FUTURE< OUT, IN > FUTURE
The template base class of all futures to be defined for a device.
Definition: i2c_device.h:118
i2c.h
I2C API common definitions.
i2c::I2CMode::STANDARD
@ STANDARD
I2C Standard mode, less than 100KHz.
errors::EPROTO
constexpr const int EPROTO
Protocol error.
Definition: errors.h:58
i2c::I2C_FAST
static constexpr Mode I2C_FAST
Constant determining that best supported I2C mode for an I2CDevice is FAST (400kHz).
Definition: i2c_device.h:49