FastArduino  v1.9
C++ library to build fast but small Arduino/AVR projects
i2c_handler_atmega.h
Go to the documentation of this file.
1 // Copyright 2016-2022 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 
22 #ifndef I2C_HANDLER_ATMEGA_HH
23 #define I2C_HANDLER_ATMEGA_HH
24 
25 #include <util/delay_basic.h>
26 
27 #include "i2c.h"
28 #include "future.h"
29 #include "queue.h"
30 #include "interrupts.h"
31 #include "bits.h"
32 #include "utilities.h"
33 #include "i2c_handler_common.h"
34 
35 // Prevent direct inclusion (must be done through i2c_handler.h)
36 #ifndef I2C_HANDLER_HH
37 #error "i2c_handler_atmega.h shall not be directly included! Include 'i2c_handler.h' instead."
38 #endif
39 // Prevent inclusion for ATtiny architecture
40 #ifndef TWCR
41 #error "i2c_handler_atmega.h cannot be included in an ATtiny program!"
42 #endif
43 
49 #define I2C_TRUE_ASYNC 1
50 
57 #define REGISTER_I2C_ISR(MANAGER) \
58 ISR(TWI_vect) \
59 { \
60  i2c::isr_handler::i2c_change<MANAGER>(); \
61 }
62 
76 #define REGISTER_I2C_ISR_FUNCTION(MANAGER, CALLBACK) \
77 ISR(TWI_vect) \
78 { \
79  i2c::isr_handler::i2c_change_function<MANAGER, CALLBACK>(); \
80 }
81 
96 #define REGISTER_I2C_ISR_METHOD(MANAGER, HANDLER, CALLBACK) \
97 ISR(TWI_vect) \
98 { \
99  i2c::isr_handler::i2c_change_method<MANAGER, HANDLER, CALLBACK>(); \
100 }
101 
108 #define DECL_I2C_ISR_HANDLERS_FRIEND \
109  friend struct i2c::isr_handler; \
110  DECL_TWI_FRIENDS
111 
112 namespace i2c
113 {
119  enum class I2CErrorPolicy : uint8_t
120  {
125  DO_NOTHING,
126 
134 
140  };
141 
146  enum class I2CCallback : uint8_t
147  {
149  NONE = 0,
151  END_COMMAND,
155  ERROR
156  };
157 
159  // Generic support for LifeCycle resolution
160  template<I2CErrorPolicy POLICY = I2CErrorPolicy::DO_NOTHING> struct I2CErrorPolicySupport
161  {
162  I2CErrorPolicySupport() = default;
163  template<typename T>
164  void handle_error(UNUSED const I2CCommand<T>& current, UNUSED containers::Queue<I2CCommand<T>>& commands)
165  {
166  // Intentionally empty: do nothing in this policy
167  }
168  };
169  template<> struct I2CErrorPolicySupport<I2CErrorPolicy::CLEAR_ALL_COMMANDS>
170  {
171  I2CErrorPolicySupport() = default;
172  template<typename T>
173  void handle_error(UNUSED const I2CCommand<T>& current, containers::Queue<I2CCommand<T>>& commands)
174  {
175  commands.clear_();
176  }
177  };
178  template<> struct I2CErrorPolicySupport<I2CErrorPolicy::CLEAR_TRANSACTION_COMMANDS>
179  {
180  I2CErrorPolicySupport() = default;
181  template<typename T>
182  void handle_error(const I2CCommand<T>& current, containers::Queue<I2CCommand<T>>& commands)
183  {
184  // Clear command belonging to the same transaction (i.e. same future)
185  const auto future = current.future();
186  I2CCommand<T> command;
187  while (commands.peek_(command))
188  {
189  if (command.future() != future)
190  break;
191  commands.pull_(command);
192  }
193  }
194  };
196 
197  //==============
198  // Sync Handler
199  //==============
201  template<I2CMode MODE_, bool HAS_STATUS_ = false, typename STATUS_HOOK_ = I2C_STATUS_HOOK>
202  class ATmegaI2CSyncHandler
203  {
204  private:
205  using MODE_TRAIT = I2CMode_trait<MODE_>;
206  using I2C_TRAIT = board_traits::TWI_trait;
207  using REG8 = board_traits::REG8;
208  using STATUS = I2CStatusSupport<HAS_STATUS_, STATUS_HOOK_>;
209 
210  public:
211  explicit ATmegaI2CSyncHandler(STATUS_HOOK_ status_hook = nullptr) : status_hook_{status_hook} {}
212 
213  void begin_()
214  {
215  // 1. set SDA/SCL pullups
216  I2C_TRAIT::PORT |= I2C_TRAIT::SCL_SDA_MASK;
217  // 2. set I2C frequency
218  TWBR_ = MODE_TRAIT::FREQUENCY;
219  TWSR_ = 0;
220  // 3. Enable TWI
221  TWCR_ = bits::BV8(TWEN);
222  }
223 
224  void end_()
225  {
226  // 1. Disable TWI
227  TWCR_ = 0;
228  // 2. remove SDA/SCL pullups
229  I2C_TRAIT::PORT &= bits::COMPL(I2C_TRAIT::SCL_SDA_MASK);
230  }
231 
232  // Low-level methods to handle the bus in an asynchronous way
233  bool exec_start_()
234  {
235  TWCR_ = bits::BV8(TWEN, TWINT, TWSTA);
236  return wait_twint(Status::START_TRANSMITTED);
237  }
238 
239  bool exec_repeat_start_()
240  {
241  TWCR_ = bits::BV8(TWEN, TWINT, TWSTA);
242  return wait_twint(Status::REPEAT_START_TRANSMITTED);
243  }
244 
245  bool exec_send_slar_(uint8_t target)
246  {
247  send_byte(target | 0x01U);
248  return wait_twint(Status::SLA_R_TRANSMITTED_ACK);
249  }
250 
251  bool exec_send_slaw_(uint8_t target)
252  {
253  send_byte(target);
254  return wait_twint(Status::SLA_W_TRANSMITTED_ACK);
255  }
256 
257  bool exec_send_data_(uint8_t data)
258  {
259  send_byte(data);
260  return wait_twint(Status::DATA_TRANSMITTED_ACK);
261  }
262 
263  bool exec_receive_data_(bool last_byte, uint8_t& data)
264  {
265  const uint8_t twcr = (last_byte ? bits::BV8(TWEN, TWINT) : bits::BV8(TWEN, TWINT, TWEA));
266  const Status expected = (last_byte ? Status::DATA_RECEIVED_NACK : Status::DATA_RECEIVED_ACK);
267  TWCR_ = twcr;
268  if (wait_twint(expected))
269  {
270  data = TWDR_;
271  return true;
272  }
273  return false;
274  }
275 
276  void exec_stop_()
277  {
278  TWCR_ = bits::BV8(TWEN, TWINT, TWSTO);
279  }
280 
281  private:
282  static constexpr const REG8 TWBR_{TWBR};
283  static constexpr const REG8 TWSR_{TWSR};
284  static constexpr const REG8 TWCR_{TWCR};
285  static constexpr const REG8 TWDR_{TWDR};
286 
287  void send_byte(uint8_t data)
288  {
289  TWDR_ = data;
290  TWCR_ = bits::BV8(TWEN, TWINT);
291  }
292 
293  bool wait_twint(Status expected_status)
294  {
295  TWCR_.loop_until_bit_set(TWINT);
296  const Status status = Status(TWSR_ & bits::BV8(TWS3, TWS4, TWS5, TWS6, TWS7));
297  status_hook_.call_hook(expected_status, status);
298  return (status == expected_status);
299  }
300 
301  STATUS status_hook_;
302  };
304 
333  template<I2CMode MODE_, bool HAS_LC_,
334  bool HAS_STATUS_, typename STATUS_HOOK_, bool HAS_DEBUG_, typename DEBUG_HOOK_>
336  : public AbstractI2CSyncManager<ATmegaI2CSyncHandler<MODE_, HAS_STATUS_, STATUS_HOOK_>,
337  MODE_, HAS_LC_, STATUS_HOOK_, HAS_DEBUG_, DEBUG_HOOK_>
338  {
339  private:
341  MODE_, HAS_LC_, STATUS_HOOK_, HAS_DEBUG_, DEBUG_HOOK_>;
342 
343  public:
344  using ABSTRACT_FUTURE = typename PARENT::ABSTRACT_FUTURE;
345  template<typename T> using PROXY = typename PARENT::template PROXY<T>;
346  template<typename OUT, typename IN> using FUTURE = typename PARENT::template FUTURE<OUT, IN>;
347 
348  protected:
351  lifecycle::AbstractLifeCycleManager* lifecycle_manager = nullptr,
352  STATUS_HOOK_ status_hook = nullptr, DEBUG_HOOK_ debug_hook = nullptr)
353  : PARENT{lifecycle_manager, status_hook, debug_hook} {}
355 
356  template<typename> friend class I2CDevice;
357  };
358 
359  //===============
360  // Async Manager
361  //===============
392  template<I2CMode MODE_, I2CErrorPolicy POLICY_, bool HAS_LC_,
393  bool HAS_STATUS_, typename STATUS_HOOK_, bool HAS_DEBUG_, typename DEBUG_HOOK_>
395  {
396  private:
397  using MODE_TRAIT = I2CMode_trait<MODE_>;
398  using I2C_TRAIT = board_traits::TWI_trait;
399  using REG8 = board_traits::REG8;
400  using STATUS = I2CStatusSupport<HAS_STATUS_, STATUS_HOOK_>;
401  using DEBUG = I2CDebugSupport<HAS_DEBUG_, DEBUG_HOOK_>;
402  using POLICY = I2CErrorPolicySupport<POLICY_>;
403  using LC = I2CLifeCycleSupport<HAS_LC_>;
404 
405  public:
407  template<typename T> using PROXY = typename LC::template PROXY<T>;
408  template<typename OUT, typename IN> using FUTURE = future::Future<OUT, IN>;
409 
418  using FUTURE_PROXY = PROXY<ABSTRACT_FUTURE>;
419 
425 
433  void begin()
434  {
435  synchronized begin_();
436  }
437 
444  void end()
445  {
446  synchronized end_();
447  }
448 
456  void begin_()
457  {
458  // 1. set SDA/SCL pullups
459  I2C_TRAIT::PORT |= I2C_TRAIT::SCL_SDA_MASK;
460  // 2. set I2C frequency
461  TWBR_ = MODE_TRAIT::FREQUENCY;
462  TWSR_ = 0;
463  // 3. Enable TWI
464  TWCR_ = bits::BV8(TWEN);
465  }
466 
473  void end_()
474  {
475  // 1. Disable TWI
476  TWCR_ = 0;
477  // 2. remove SDA/SCL pullups
478  I2C_TRAIT::PORT &= bits::COMPL(I2C_TRAIT::SCL_SDA_MASK);
479  }
480 
481  protected:
483  template<uint8_t SIZE>
484  explicit AbstractI2CAsyncManager(
485  I2CCOMMAND (&buffer)[SIZE],
486  lifecycle::AbstractLifeCycleManager* lifecycle_manager = nullptr,
487  STATUS_HOOK_ status_hook = nullptr,
488  DEBUG_HOOK_ debug_hook = nullptr)
489  : commands_{buffer}, lc_{lifecycle_manager},
490  status_hook_{status_hook}, debug_hook_{debug_hook} {}
492 
493  private:
494  bool ensure_num_commands_(uint8_t num_commands) const
495  {
496  return commands_.free_() >= num_commands;
497  }
498 
499  bool push_command_(
500  I2CLightCommand command, uint8_t target, FUTURE_PROXY proxy)
501  {
502  return commands_.push_(I2CCOMMAND{command, target, proxy});
503  }
504 
505  void last_command_pushed_()
506  {
507  // Check if need to initiate transmission (i.e no current command is executed)
508  if (command_.type().is_none())
509  {
510  // Dequeue first pending command and start TWI operation
511  dequeue_command_(true);
512  }
513  }
514 
515  template<typename T> T& resolve(PROXY<T> proxy) const
516  {
517  return lc_.resolve(proxy);
518  }
519 
520  static constexpr const REG8 TWBR_{TWBR};
521  static constexpr const REG8 TWSR_{TWSR};
522  static constexpr const REG8 TWCR_{TWCR};
523  static constexpr const REG8 TWDR_{TWDR};
524 
525  // States of execution of an I2C command through ISR calls
526  enum class State : uint8_t
527  {
528  NONE = 0,
529  START,
530  SLAW,
531  SLAR,
532  SEND,
533  RECV,
534  RECV_LAST,
535  STOP
536  };
537 
538  ABSTRACT_FUTURE& current_future() const
539  {
540  return lc_.resolve(command_.future());
541  }
542 
543  FUTURE_PROXY current_proxy() const
544  {
545  //TODO shall we convert to a full Proxy<> or keep it as-is?
546  return command_.future();
547  }
548 
549  void send_byte(uint8_t data)
550  {
551  TWDR_ = data;
552  TWCR_ = bits::BV8(TWEN, TWIE, TWINT);
553  }
554 
555  // Dequeue the next command in the queue and process it immediately
556  void dequeue_command_(bool first)
557  {
558  if (!commands_.pull_(command_))
559  {
560  command_ = I2CCOMMAND{};
561  current_ = State::NONE;
562  // No more I2C command to execute
563  TWCR_ = bits::BV8(TWINT);
564  return;
565  }
566 
567  // Start new commmand
568  current_ = State::START;
569  if (first)
570  exec_start_();
571  else
572  exec_repeat_start_();
573  }
574 
575  // Method to compute next state
576  State next_state_()
577  {
578  switch (current_)
579  {
580  case State::START:
581  return (command_.type().is_write() ? State::SLAW : State::SLAR);
582 
583  case State::SLAR:
584  case State::RECV:
585  if (command_.byte_count() > 1)
586  return State::RECV;
587  else
588  return State::RECV_LAST;
589 
590  case State::RECV_LAST:
591  return State::STOP;
592 
593  case State::SLAW:
594  return State::SEND;
595 
596  case State::SEND:
597  if (command_.byte_count() >= 1)
598  return State::SEND;
599  else
600  return State::STOP;
601 
602  case State::STOP:
603  case State::NONE:
604  return State::NONE;
605  }
606  }
607 
608  // Low-level methods to handle the bus in an asynchronous way
609  void exec_start_()
610  {
611  debug_hook_.call_hook(DebugStatus::START);
612  expected_status_ = Status::START_TRANSMITTED;
613  TWCR_ = bits::BV8(TWEN, TWIE, TWINT, TWSTA);
614  }
615  void exec_repeat_start_()
616  {
617  debug_hook_.call_hook(DebugStatus::REPEAT_START);
618  expected_status_ = Status::REPEAT_START_TRANSMITTED;
619  TWCR_ = bits::BV8(TWEN, TWIE, TWINT, TWSTA);
620  }
621  void exec_send_slar_()
622  {
623  debug_hook_.call_hook(DebugStatus::SLAR, command_.target());
624  // Read device address from queue
625  expected_status_ = Status::SLA_R_TRANSMITTED_ACK;
626  send_byte(command_.target() | 0x01U);
627  }
628  void exec_send_slaw_()
629  {
630  debug_hook_.call_hook(DebugStatus::SLAW, command_.target());
631  // Read device address from queue
632  expected_status_ = Status::SLA_W_TRANSMITTED_ACK;
633  send_byte(command_.target());
634  }
635  void exec_send_data_()
636  {
637  // Determine next data byte
638  uint8_t data = 0;
639  ABSTRACT_FUTURE& future = current_future();
640  bool ok = future.get_storage_value_(data);
641  debug_hook_.call_hook(DebugStatus::SEND, data);
642  // This should only happen if there are 2 concurrent consumers for that Future
643  if (ok)
644  command_.decrement_byte_count();
645  else
646  future.set_future_error_(errors::EILSEQ);
647  debug_hook_.call_hook(ok ? DebugStatus::SEND_OK : DebugStatus::SEND_ERROR);
648  expected_status_ = Status::DATA_TRANSMITTED_ACK;
649  send_byte(data);
650  }
651  void exec_receive_data_()
652  {
653  // Is this the last byte to receive?
654  if (command_.byte_count() == 1)
655  {
656  debug_hook_.call_hook(DebugStatus::RECV_LAST);
657  // Send NACK for the last data byte we want
658  expected_status_ = Status::DATA_RECEIVED_NACK;
659  TWCR_ = bits::BV8(TWEN, TWIE, TWINT);
660  }
661  else
662  {
663  debug_hook_.call_hook(DebugStatus::RECV);
664  // Send ACK for data byte if not the last one we want
665  expected_status_ = Status::DATA_RECEIVED_ACK;
666  TWCR_ = bits::BV8(TWEN, TWIE, TWINT, TWEA);
667  }
668  }
669  void exec_stop_(bool error = false)
670  {
671  debug_hook_.call_hook(DebugStatus::STOP);
672  TWCR_ = bits::BV8(TWEN, TWINT, TWSTO);
673  if (!error)
674  expected_status_ = Status::OK;
675  command_ = I2CCOMMAND{};
676  current_ = State::NONE;
677  // If so then delay 4.0us + 4.7us (100KHz) or 0.6us + 1.3us (400KHz)
678  // (ATMEGA328P datasheet 29.7 Tsu;sto + Tbuf)
679  _delay_loop_1(MODE_TRAIT::DELAY_AFTER_STOP);
680  }
681 
682  bool is_end_transaction() const
683  {
684  return command_.type().is_end();
685  }
686 
687  bool handle_no_error(ABSTRACT_FUTURE& future, Status status)
688  {
689  if (check_no_error(future, status)) return true;
690  policy_.handle_error(command_, commands_);
691  // In case of an error, immediately send a STOP condition
692  exec_stop_(true);
693  dequeue_command_(true);
694  return false;
695  }
696 
697  I2CCallback i2c_change()
698  {
699  // Check status Vs. expected status
700  const Status status = Status(TWSR_ & bits::BV8(TWS3, TWS4, TWS5, TWS6, TWS7));
701  ABSTRACT_FUTURE& future = current_future();
702  if (!handle_no_error(future, status))
703  return I2CCallback::ERROR;
704 
705  // Handle TWI interrupt when data received
706  if ((current_ == State::RECV) || (current_ == State::RECV_LAST))
707  {
708  const uint8_t data = TWDR_;
709  bool ok = future.set_future_value_(data);
710  // This should only happen in case there are 2 concurrent providers for this future
711  if (ok)
712  command_.decrement_byte_count();
713  else
714  future.set_future_error_(errors::EILSEQ);
715  debug_hook_.call_hook(ok ? DebugStatus::RECV_OK : DebugStatus::RECV_ERROR, data);
716  }
717 
718  // Handle next step in current command
720  current_ = next_state_();
721  switch (current_)
722  {
723  case State::NONE:
724  case State::START:
725  // This cannot happen
726  break;
727 
728  case State::SLAR:
729  exec_send_slar_();
730  break;
731 
732  case State::RECV:
733  case State::RECV_LAST:
734  exec_receive_data_();
735  break;
736 
737  case State::SLAW:
738  exec_send_slaw_();
739  break;
740 
741  case State::SEND:
742  exec_send_data_();
743  break;
744 
745  case State::STOP:
746  // Check if we need to finish the current future
747  if (command_.type().is_finish())
748  future.set_future_finish_();
749  result = (is_end_transaction() ? I2CCallback::END_TRANSACTION : I2CCallback::END_COMMAND);
750  // Check if we need to STOP (no more pending commands in queue)
751  if (commands_.empty_())
752  exec_stop_();
753  // Check if we need to STOP or REPEAT START (current command requires STOP)
754  else if (command_.type().is_stop())
755  {
756  exec_stop_();
757  // Handle next command
758  dequeue_command_(true);
759  }
760  else
761  // Handle next command
762  dequeue_command_(false);
763  }
764  return result;
765  }
766 
767  bool check_no_error(ABSTRACT_FUTURE& future, Status status)
768  {
769  status_hook_.call_hook(expected_status_, status);
770  if (status == expected_status_) return true;
771  // Handle special case of last transmitted byte possibly not acknowledged by device
772  if ( (expected_status_ == Status::DATA_TRANSMITTED_ACK)
773  && (status == Status::DATA_TRANSMITTED_NACK)
774  && (command_.byte_count() == 0))
775  return true;
776 
777  // The future must be marked as error
778  if (future.status() != future::FutureStatus::ERROR)
779  future.set_future_error_(errors::EPROTO);
780  return false;
781  }
782 
783  // Status of current command processing
784  I2CCOMMAND command_;
785 
786  // Latest I2C status
787  Status expected_status_ = Status::OK;
788 
789  // Status of current command processing
790  State current_ = State::NONE;
791 
792  // Queue of commands to execute
794 
795  POLICY policy_{};
796  LC lc_;
797  STATUS status_hook_;
798  DEBUG debug_hook_;
799 
800  template<typename> friend class I2CDevice;
801  friend struct isr_handler;
802  };
803 
818  template<I2CMode MODE_, I2CErrorPolicy POLICY_ = I2CErrorPolicy::CLEAR_ALL_COMMANDS>
820  public AbstractI2CAsyncManager<MODE_, POLICY_, false, false, I2C_STATUS_HOOK, false, I2C_DEBUG_HOOK>
821  {
823  public:
832  template<uint8_t SIZE>
833  explicit I2CAsyncManager(typename PARENT::I2CCOMMAND (&buffer)[SIZE]) : PARENT{buffer}
834  {
836  }
837  };
838 
856  template<
857  I2CMode MODE_,
859  typename DEBUG_HOOK_ = I2C_DEBUG_HOOK>
861  public AbstractI2CAsyncManager<MODE_, POLICY_, false, false, I2C_STATUS_HOOK, true, DEBUG_HOOK_>
862  {
864  public:
875  template<uint8_t SIZE>
877  typename PARENT::I2CCOMMAND (&buffer)[SIZE], DEBUG_HOOK_ debug_hook)
878  : PARENT{buffer, nullptr, nullptr, debug_hook}
879  {
881  }
882  };
883 
901  template<
902  I2CMode MODE_,
904  typename STATUS_HOOK_ = I2C_STATUS_HOOK>
906  public AbstractI2CAsyncManager<MODE_, POLICY_, false, true, STATUS_HOOK_, false, I2C_DEBUG_HOOK>
907  {
909  public:
920  template<uint8_t SIZE>
922  typename PARENT::I2CCOMMAND (&buffer)[SIZE], STATUS_HOOK_ status_hook)
923  : PARENT{buffer, nullptr, status_hook}
924  {
926  }
927  };
928 
950  template<
951  I2CMode MODE_,
953  typename STATUS_HOOK_ = I2C_STATUS_HOOK,
954  typename DEBUG_HOOK_ = I2C_DEBUG_HOOK>
956  public AbstractI2CAsyncManager<MODE_, POLICY_, false, true, STATUS_HOOK_, true, DEBUG_HOOK_>
957  {
959  public:
972  template<uint8_t SIZE>
974  typename PARENT::I2CCOMMAND (&buffer)[SIZE], STATUS_HOOK_ status_hook, DEBUG_HOOK_ debug_hook)
975  : PARENT{buffer, nullptr, status_hook, debug_hook}
976  {
978  }
979  };
980 
995  template<I2CMode MODE_, I2CErrorPolicy POLICY_ = I2CErrorPolicy::CLEAR_ALL_COMMANDS>
997  public AbstractI2CAsyncManager<MODE_, POLICY_, true, false, I2C_STATUS_HOOK, false, I2C_DEBUG_HOOK>
998  {
1000  public:
1011  template<uint8_t SIZE>
1013  typename PARENT::I2CCOMMAND (&buffer)[SIZE], lifecycle::AbstractLifeCycleManager& lifecycle_manager)
1014  : PARENT{buffer, &lifecycle_manager}
1015  {
1017  }
1018  };
1019 
1037  template<
1038  I2CMode MODE_,
1040  typename DEBUG_HOOK_ = I2C_DEBUG_HOOK>
1042  public AbstractI2CAsyncManager<MODE_, POLICY_, true, false, I2C_STATUS_HOOK, true, DEBUG_HOOK_>
1043  {
1045  public:
1058  template<uint8_t SIZE>
1060  typename PARENT::I2CCOMMAND (&buffer)[SIZE],
1061  lifecycle::AbstractLifeCycleManager& lifecycle_manager,
1062  DEBUG_HOOK_ debug_hook)
1063  : PARENT{buffer, &lifecycle_manager, nullptr, debug_hook}
1064  {
1066  }
1067  };
1068 
1086  template<
1087  I2CMode MODE_,
1089  typename STATUS_HOOK_ = I2C_STATUS_HOOK>
1091  public AbstractI2CAsyncManager<MODE_, POLICY_, true, true, STATUS_HOOK_, false, I2C_DEBUG_HOOK>
1092  {
1094  public:
1107  template<uint8_t SIZE>
1109  typename PARENT::I2CCOMMAND (&buffer)[SIZE],
1110  lifecycle::AbstractLifeCycleManager& lifecycle_manager,
1111  STATUS_HOOK_ status_hook)
1112  : PARENT{buffer, &lifecycle_manager, status_hook}
1113  {
1115  }
1116  };
1117 
1138  template<
1139  I2CMode MODE_,
1141  typename STATUS_HOOK_ = I2C_STATUS_HOOK,
1142  typename DEBUG_HOOK_ = I2C_DEBUG_HOOK>
1144  public AbstractI2CAsyncManager<MODE_, POLICY_, true, true, STATUS_HOOK_, true, DEBUG_HOOK_>
1145  {
1147  public:
1162  template<uint8_t SIZE>
1164  typename PARENT::I2CCOMMAND (&buffer)[SIZE],
1165  lifecycle::AbstractLifeCycleManager& lifecycle_manager,
1166  STATUS_HOOK_ status_hook, DEBUG_HOOK_ debug_hook)
1167  : PARENT{buffer, &lifecycle_manager, status_hook, debug_hook}
1168  {
1170  }
1171  };
1172 
1181  template<I2CMode MODE_>
1183  public AbstractI2CSyncATmegaManager<MODE_, false, false, I2C_STATUS_HOOK, false, I2C_DEBUG_HOOK>
1184  {
1186  public:
1187  I2CSyncManager() : PARENT{} {}
1188  };
1189 
1202  template<I2CMode MODE_, typename STATUS_HOOK_ = I2C_STATUS_HOOK>
1204  public AbstractI2CSyncATmegaManager<MODE_, false, true, STATUS_HOOK_, false, I2C_DEBUG_HOOK>
1205  {
1207  public:
1208  explicit I2CSyncStatusManager(STATUS_HOOK_ status_hook) : PARENT{nullptr, status_hook} {}
1209  };
1210 
1222  template<I2CMode MODE_, typename DEBUG_HOOK_ = I2C_DEBUG_HOOK>
1224  public AbstractI2CSyncATmegaManager<MODE_, false, false, I2C_STATUS_HOOK, true, DEBUG_HOOK_>
1225  {
1227  public:
1228  explicit I2CSyncDebugManager(DEBUG_HOOK_ debug_hook) : PARENT{nullptr, nullptr, debug_hook} {}
1229  };
1230 
1246  template<I2CMode MODE_, typename STATUS_HOOK_ = I2C_STATUS_HOOK, typename DEBUG_HOOK_ = I2C_DEBUG_HOOK>
1248  public AbstractI2CSyncATmegaManager<MODE_, false, true, STATUS_HOOK_, true, DEBUG_HOOK_>
1249  {
1251  public:
1252  explicit I2CSyncStatusDebugManager(STATUS_HOOK_ status_hook, DEBUG_HOOK_ debug_hook)
1253  : PARENT{nullptr, status_hook, debug_hook} {}
1254  };
1255 
1264  template<I2CMode MODE_>
1266  public AbstractI2CSyncATmegaManager<MODE_, true, false, I2C_STATUS_HOOK, false, I2C_DEBUG_HOOK>
1267  {
1269  public:
1270  explicit I2CSyncLCManager(lifecycle::AbstractLifeCycleManager& lifecycle_manager)
1271  : PARENT{&lifecycle_manager} {}
1272  };
1273 
1285  template<I2CMode MODE_, typename STATUS_HOOK_>
1287  public AbstractI2CSyncATmegaManager<MODE_, true, true, STATUS_HOOK_, false, I2C_DEBUG_HOOK>
1288  {
1290  public:
1291  explicit I2CSyncLCStatusManager(
1292  lifecycle::AbstractLifeCycleManager& lifecycle_manager, STATUS_HOOK_ status_hook)
1293  : PARENT{&lifecycle_manager, status_hook} {}
1294  };
1295 
1307  template<I2CMode MODE_, typename DEBUG_HOOK_ = I2C_DEBUG_HOOK>
1309  public AbstractI2CSyncATmegaManager<MODE_, true, false, I2C_STATUS_HOOK, true, DEBUG_HOOK_>
1310  {
1312  public:
1313  explicit I2CSyncLCDebugManager(
1314  lifecycle::AbstractLifeCycleManager& lifecycle_manager, DEBUG_HOOK_ debug_hook)
1315  : PARENT{&lifecycle_manager, nullptr, debug_hook} {}
1316  };
1317 
1332  template<I2CMode MODE_, typename STATUS_HOOK_ = I2C_STATUS_HOOK, typename DEBUG_HOOK_ = I2C_DEBUG_HOOK>
1334  public AbstractI2CSyncATmegaManager<MODE_, true, true, STATUS_HOOK_, true, DEBUG_HOOK_>
1335  {
1337  public:
1338  explicit I2CSyncLCStatusDebugManager(
1339  lifecycle::AbstractLifeCycleManager& lifecycle_manager, STATUS_HOOK_ status_hook, DEBUG_HOOK_ debug_hook)
1340  : PARENT{&lifecycle_manager, status_hook, debug_hook} {}
1341  };
1342 
1344  // Specific traits for I2C Manager
1345  // Async managers first
1346  template<I2CMode MODE_, I2CErrorPolicy POLICY_>
1347  struct I2CManager_trait<I2CAsyncManager<MODE_, POLICY_>>
1348  : I2CManager_trait_impl<true, false, false, false, MODE_> {};
1349 
1350  template<I2CMode MODE_, I2CErrorPolicy POLICY_>
1351  struct I2CManager_trait<I2CAsyncLCManager<MODE_, POLICY_>>
1352  : I2CManager_trait_impl<true, true, false, false, MODE_> {};
1353 
1354  template<I2CMode MODE_, I2CErrorPolicy POLICY_, typename DEBUG_HOOK_>
1355  struct I2CManager_trait<I2CAsyncDebugManager<MODE_, POLICY_, DEBUG_HOOK_>>
1356  : I2CManager_trait_impl<true, false, false, true, MODE_> {};
1357 
1358  template<I2CMode MODE_, I2CErrorPolicy POLICY_, typename STATUS_HOOK_>
1359  struct I2CManager_trait<I2CAsyncStatusManager<MODE_, POLICY_, STATUS_HOOK_>>
1360  : I2CManager_trait_impl<true, false, true, false, MODE_> {};
1361 
1362  template<I2CMode MODE_, I2CErrorPolicy POLICY_, typename STATUS_HOOK_, typename DEBUG_HOOK_>
1363  struct I2CManager_trait<I2CAsyncStatusDebugManager<MODE_, POLICY_, STATUS_HOOK_, DEBUG_HOOK_>>
1364  : I2CManager_trait_impl<true, false, true, true, MODE_> {};
1365 
1366  template<I2CMode MODE_, I2CErrorPolicy POLICY_, typename DEBUG_HOOK_>
1367  struct I2CManager_trait<I2CAsyncLCDebugManager<MODE_, POLICY_, DEBUG_HOOK_>>
1368  : I2CManager_trait_impl<true, true, false, true, MODE_> {};
1369 
1370  template<I2CMode MODE_, I2CErrorPolicy POLICY_, typename STATUS_HOOK_>
1371  struct I2CManager_trait<I2CAsyncLCStatusManager<MODE_, POLICY_, STATUS_HOOK_>>
1372  : I2CManager_trait_impl<true, true, true, false, MODE_> {};
1373 
1374  template<I2CMode MODE_, I2CErrorPolicy POLICY_, typename STATUS_HOOK_, typename DEBUG_HOOK_>
1375  struct I2CManager_trait<I2CAsyncLCStatusDebugManager<MODE_, POLICY_, STATUS_HOOK_, DEBUG_HOOK_>>
1376  : I2CManager_trait_impl<true, true, true, true, MODE_> {};
1378 
1380  struct isr_handler
1381  {
1382  template<typename MANAGER>
1383  static void i2c_change()
1384  {
1385  static_assert(I2CManager_trait<MANAGER>::IS_I2CMANAGER, "MANAGER must be an I2C Manager");
1386  static_assert(I2CManager_trait<MANAGER>::IS_ASYNC, "MANAGER must be an asynchronous I2C Manager");
1387  interrupt::HandlerHolder<MANAGER>::handler()->i2c_change();
1388  }
1389 
1390  template<typename MANAGER, void (*CALLBACK_)(I2CCallback, typename MANAGER::FUTURE_PROXY)>
1391  static void i2c_change_function()
1392  {
1393  using interrupt::HandlerHolder;
1394  static_assert(I2CManager_trait<MANAGER>::IS_I2CMANAGER, "MANAGER must be an I2C Manager");
1395  static_assert(I2CManager_trait<MANAGER>::IS_ASYNC, "MANAGER must be an asynchronous I2C Manager");
1396  auto proxy = HandlerHolder<MANAGER>::handler()->current_proxy();
1397  I2CCallback callback = HandlerHolder<MANAGER>::handler()->i2c_change();
1398  if (callback != I2CCallback::NONE)
1399  {
1400  CALLBACK_(callback, proxy);
1401  }
1402  }
1403 
1404  template<typename MANAGER, typename HANDLER_,
1405  void (HANDLER_::*CALLBACK_)(I2CCallback, typename MANAGER::FUTURE_PROXY)>
1406  static void i2c_change_method()
1407  {
1408  using interrupt::HandlerHolder;
1409  using interrupt::CallbackHandler;
1410  static_assert(I2CManager_trait<MANAGER>::IS_I2CMANAGER, "MANAGER must be an I2C Manager");
1411  static_assert(I2CManager_trait<MANAGER>::IS_ASYNC, "MANAGER must be an asynchronous I2C Manager");
1412  auto proxy = HandlerHolder<MANAGER>::handler()->current_proxy();
1413  I2CCallback callback = HandlerHolder<MANAGER>::handler()->i2c_change();
1414  if (callback != I2CCallback::NONE)
1415  {
1416  using HANDLER =
1417  CallbackHandler<void (HANDLER_::*)(I2CCallback, typename MANAGER::FUTURE_PROXY), CALLBACK_>;
1418  HANDLER::call(callback, proxy);
1419  }
1420  }
1421  };
1423 }
1424 
1425 #endif /* I2C_HANDLER_ATMEGA_HH */
Useful bits manipulation utilities.
Queue of type T_ items.
Definition: queue.h:59
Base class for all FakeFutures.
Definition: future.h:1062
Base class for all Futures.
Definition: future.h:223
Represent a value to be obtained, in some asynchronous way, in the future.
Definition: future.h:702
Abstract asynchronous I2C Manager.
I2CCommand< FUTURE_PROXY > I2CCOMMAND
The type of I2CCommand to use in the buffer passed to the constructor of this AbstractI2CAsyncManager...
void end_()
Disable MCU I2C transmission.
void begin()
Prepare and enable the MCU for I2C transmission.
void end()
Disable MCU I2C transmission.
void begin_()
Prepare and enable the MCU for I2C transmission.
PROXY< ABSTRACT_FUTURE > FUTURE_PROXY
The type passed to callback functions registered alongside ISR.
Abstract synchronous I2C Manager for ATmega architecture.
Abstract synchronous I2C Manager for all MCU architectures.
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 with debug facility and support for dynamic proxies.
I2CAsyncLCDebugManager(typename PARENT::I2CCOMMAND(&buffer)[SIZE], lifecycle::AbstractLifeCycleManager &lifecycle_manager, DEBUG_HOOK_ debug_hook)
Create an asynchronous I2C Manager for ATmega MCUs.
Asynchronous I2C Manager for ATmega architecture with support for dynamic proxies.
I2CAsyncLCManager(typename PARENT::I2CCOMMAND(&buffer)[SIZE], lifecycle::AbstractLifeCycleManager &lifecycle_manager)
Create an asynchronous I2C Manager for ATmega MCUs.
Asynchronous I2C Manager for ATmega architecture with debug and status notification facilities and su...
I2CAsyncLCStatusDebugManager(typename PARENT::I2CCOMMAND(&buffer)[SIZE], lifecycle::AbstractLifeCycleManager &lifecycle_manager, 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 and support for dy...
I2CAsyncLCStatusManager(typename PARENT::I2CCOMMAND(&buffer)[SIZE], lifecycle::AbstractLifeCycleManager &lifecycle_manager, STATUS_HOOK_ status_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.
Definition: i2c_device.h:85
Synchronous I2C Manager for ATmega architecture with debug facility.
Synchronous I2C Manager for ATmega architecture with debug facility and support for dynamic proxies.
Synchronous I2C Manager for ATmega architecture with support for dynamic proxies.
Synchronous I2C Manager for ATmega architecture with status notification and debug facilities and sup...
Synchronous I2C Manager for ATmega architecture with status notification facility and support for dyn...
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.
The abstract base class of all LifeCycleManager.
Definition: lifecycle.h:132
#define UNUSED
Specific GCC attribute to declare an argument or variable unused, so that the compiler does not emit ...
Definition: defines.h:45
Utility API to handle the concept of futures.
I2C API common definitions.
Common I2C Manager API.
General API for handling AVR interrupt vectors.
@ NONE
No interrupt will be generated by the Anolog Comparator.
Defines utility methods for bits manipulation.
Definition: bits.h:34
static constexpr uint8_t COMPL(uint8_t value)
Return the uint8_t 2-complement of a byte.
Definition: bits.h:253
static constexpr uint8_t BV8(uint8_t bit)
Create a uint8_t bitmask for the given bit number.
Definition: bits.h:41
constexpr const int EPROTO
Protocol error.
Definition: errors.h:58
constexpr const int EILSEQ
Illegal byte sequence.
Definition: errors.h:65
Contains the API around Future implementation.
Definition: future.cpp:18
@ ERROR
The status of a Future once a value provider has reported an error to it.
STATUS
Indicate when status should be traced.
Definition: i2c_status.h:47
Define API to define and manage I2C devices.
Definition: i2c.cpp:18
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...
@ DO_NOTHING
Do nothing at all in case of an error; useful only with a synchronous I2C Manager.
I2CMode
I2C available transmission modes.
Definition: i2c.h:108
Status
Transmission status codes.
Definition: i2c.h:66
@ 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.
Definition: interrupts.h:157
Utility API to handle ring-buffer queue containers.
General utilities API that have broad application in programs.