FastArduino  v1.7
C++ library to build fast but small Arduino/AVR projects
i2c_handler_atmega.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 
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 
133  CLEAR_ALL_COMMANDS,
134 
140  };
141 
146  enum class I2CCallback : uint8_t
147  {
149  NONE = 0,
151  END_COMMAND,
153  END_TRANSACTION,
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  using ABSTRACT_FUTURE = typename PARENT::ABSTRACT_FUTURE;
343  template<typename T> using PROXY = typename PARENT::template PROXY<T>;
344  template<typename OUT, typename IN> using FUTURE = typename PARENT::template FUTURE<OUT, IN>;
345 
346  protected:
349  lifecycle::AbstractLifeCycleManager* lifecycle_manager = nullptr,
350  STATUS_HOOK_ status_hook = nullptr, DEBUG_HOOK_ debug_hook = nullptr)
351  : PARENT{lifecycle_manager, status_hook, debug_hook} {}
353 
354  template<typename> friend class I2CDevice;
355  };
356 
357  //===============
358  // Async Manager
359  //===============
390  template<I2CMode MODE_, I2CErrorPolicy POLICY_, bool HAS_LC_,
391  bool HAS_STATUS_, typename STATUS_HOOK_, bool HAS_DEBUG_, typename DEBUG_HOOK_>
393  {
394  private:
395  using MODE_TRAIT = I2CMode_trait<MODE_>;
396  using I2C_TRAIT = board_traits::TWI_trait;
397  using REG8 = board_traits::REG8;
398  using STATUS = I2CStatusSupport<HAS_STATUS_, STATUS_HOOK_>;
399  using DEBUG = I2CDebugSupport<HAS_DEBUG_, DEBUG_HOOK_>;
400  using POLICY = I2CErrorPolicySupport<POLICY_>;
401  using LC = I2CLifeCycleSupport<HAS_LC_>;
403  template<typename T> using PROXY = typename LC::template PROXY<T>;
404  template<typename OUT, typename IN> using FUTURE = future::Future<OUT, IN>;
405 
406  public:
415  using FUTURE_PROXY = PROXY<ABSTRACT_FUTURE>;
416 
422 
430  void begin()
431  {
432  synchronized begin_();
433  }
434 
441  void end()
442  {
443  synchronized end_();
444  }
445 
453  void begin_()
454  {
455  // 1. set SDA/SCL pullups
456  I2C_TRAIT::PORT |= I2C_TRAIT::SCL_SDA_MASK;
457  // 2. set I2C frequency
458  TWBR_ = MODE_TRAIT::FREQUENCY;
459  TWSR_ = 0;
460  // 3. Enable TWI
461  TWCR_ = bits::BV8(TWEN);
462  }
463 
470  void end_()
471  {
472  // 1. Disable TWI
473  TWCR_ = 0;
474  // 2. remove SDA/SCL pullups
475  I2C_TRAIT::PORT &= bits::COMPL(I2C_TRAIT::SCL_SDA_MASK);
476  }
477 
478  protected:
480  template<uint8_t SIZE>
481  explicit AbstractI2CAsyncManager(
482  I2CCOMMAND (&buffer)[SIZE],
483  lifecycle::AbstractLifeCycleManager* lifecycle_manager = nullptr,
484  STATUS_HOOK_ status_hook = nullptr,
485  DEBUG_HOOK_ debug_hook = nullptr)
486  : commands_{buffer}, lc_{lifecycle_manager},
487  status_hook_{status_hook}, debug_hook_{debug_hook} {}
489 
490  private:
491  bool ensure_num_commands_(uint8_t num_commands) const
492  {
493  return commands_.free_() >= num_commands;
494  }
495 
496  bool push_command_(
497  I2CLightCommand command, uint8_t target, FUTURE_PROXY proxy)
498  {
499  return commands_.push_(I2CCOMMAND{command, target, proxy});
500  }
501 
502  void last_command_pushed_()
503  {
504  // Check if need to initiate transmission (i.e no current command is executed)
505  if (command_.type().is_none())
506  {
507  // Dequeue first pending command and start TWI operation
508  dequeue_command_(true);
509  }
510  }
511 
512  template<typename T> T& resolve(PROXY<T> proxy) const
513  {
514  return lc_.resolve(proxy);
515  }
516 
517  static constexpr const REG8 TWBR_{TWBR};
518  static constexpr const REG8 TWSR_{TWSR};
519  static constexpr const REG8 TWCR_{TWCR};
520  static constexpr const REG8 TWDR_{TWDR};
521 
522  // States of execution of an I2C command through ISR calls
523  enum class State : uint8_t
524  {
525  NONE = 0,
526  START,
527  SLAW,
528  SLAR,
529  SEND,
530  RECV,
531  RECV_LAST,
532  STOP
533  };
534 
535  ABSTRACT_FUTURE& current_future() const
536  {
537  return lc_.resolve(command_.future());
538  }
539 
540  FUTURE_PROXY current_proxy() const
541  {
542  //TODO shall we convert to a full Proxy<> or keep it as-is?
543  return command_.future();
544  }
545 
546  void send_byte(uint8_t data)
547  {
548  TWDR_ = data;
549  TWCR_ = bits::BV8(TWEN, TWIE, TWINT);
550  }
551 
552  // Dequeue the next command in the queue and process it immediately
553  void dequeue_command_(bool first)
554  {
555  if (!commands_.pull_(command_))
556  {
557  command_ = I2CCOMMAND{};
558  current_ = State::NONE;
559  // No more I2C command to execute
560  TWCR_ = bits::BV8(TWINT);
561  return;
562  }
563 
564  // Start new commmand
565  current_ = State::START;
566  if (first)
567  exec_start_();
568  else
569  exec_repeat_start_();
570  }
571 
572  // Method to compute next state
573  State next_state_()
574  {
575  switch (current_)
576  {
577  case State::START:
578  return (command_.type().is_write() ? State::SLAW : State::SLAR);
579 
580  case State::SLAR:
581  case State::RECV:
582  if (command_.byte_count() > 1)
583  return State::RECV;
584  else
585  return State::RECV_LAST;
586 
587  case State::RECV_LAST:
588  return State::STOP;
589 
590  case State::SLAW:
591  return State::SEND;
592 
593  case State::SEND:
594  if (command_.byte_count() >= 1)
595  return State::SEND;
596  else
597  return State::STOP;
598 
599  case State::STOP:
600  case State::NONE:
601  return State::NONE;
602  }
603  }
604 
605  // Low-level methods to handle the bus in an asynchronous way
606  void exec_start_()
607  {
608  debug_hook_.call_hook(DebugStatus::START);
609  expected_status_ = Status::START_TRANSMITTED;
610  TWCR_ = bits::BV8(TWEN, TWIE, TWINT, TWSTA);
611  }
612  void exec_repeat_start_()
613  {
614  debug_hook_.call_hook(DebugStatus::REPEAT_START);
615  expected_status_ = Status::REPEAT_START_TRANSMITTED;
616  TWCR_ = bits::BV8(TWEN, TWIE, TWINT, TWSTA);
617  }
618  void exec_send_slar_()
619  {
620  debug_hook_.call_hook(DebugStatus::SLAR, command_.target());
621  // Read device address from queue
622  expected_status_ = Status::SLA_R_TRANSMITTED_ACK;
623  send_byte(command_.target() | 0x01U);
624  }
625  void exec_send_slaw_()
626  {
627  debug_hook_.call_hook(DebugStatus::SLAW, command_.target());
628  // Read device address from queue
629  expected_status_ = Status::SLA_W_TRANSMITTED_ACK;
630  send_byte(command_.target());
631  }
632  void exec_send_data_()
633  {
634  // Determine next data byte
635  uint8_t data = 0;
636  ABSTRACT_FUTURE& future = current_future();
637  bool ok = future.get_storage_value_(data);
638  debug_hook_.call_hook(DebugStatus::SEND, data);
639  // This should only happen if there are 2 concurrent consumers for that Future
640  if (ok)
641  command_.decrement_byte_count();
642  else
643  future.set_future_error_(errors::EILSEQ);
644  debug_hook_.call_hook(ok ? DebugStatus::SEND_OK : DebugStatus::SEND_ERROR);
645  expected_status_ = Status::DATA_TRANSMITTED_ACK;
646  send_byte(data);
647  }
648  void exec_receive_data_()
649  {
650  // Is this the last byte to receive?
651  if (command_.byte_count() == 1)
652  {
653  debug_hook_.call_hook(DebugStatus::RECV_LAST);
654  // Send NACK for the last data byte we want
655  expected_status_ = Status::DATA_RECEIVED_NACK;
656  TWCR_ = bits::BV8(TWEN, TWIE, TWINT);
657  }
658  else
659  {
660  debug_hook_.call_hook(DebugStatus::RECV);
661  // Send ACK for data byte if not the last one we want
662  expected_status_ = Status::DATA_RECEIVED_ACK;
663  TWCR_ = bits::BV8(TWEN, TWIE, TWINT, TWEA);
664  }
665  }
666  void exec_stop_(bool error = false)
667  {
668  debug_hook_.call_hook(DebugStatus::STOP);
669  TWCR_ = bits::BV8(TWEN, TWINT, TWSTO);
670  if (!error)
671  expected_status_ = Status::OK;
672  command_ = I2CCOMMAND{};
673  current_ = State::NONE;
674  // If so then delay 4.0us + 4.7us (100KHz) or 0.6us + 1.3us (400KHz)
675  // (ATMEGA328P datasheet 29.7 Tsu;sto + Tbuf)
676  _delay_loop_1(MODE_TRAIT::DELAY_AFTER_STOP);
677  }
678 
679  bool is_end_transaction() const
680  {
681  return command_.type().is_end();
682  }
683 
684  bool handle_no_error(ABSTRACT_FUTURE& future, Status status)
685  {
686  if (check_no_error(future, status)) return true;
687  policy_.handle_error(command_, commands_);
688  // In case of an error, immediately send a STOP condition
689  exec_stop_(true);
690  dequeue_command_(true);
691  return false;
692  }
693 
694  I2CCallback i2c_change()
695  {
696  // Check status Vs. expected status
697  const Status status = Status(TWSR_ & bits::BV8(TWS3, TWS4, TWS5, TWS6, TWS7));
698  ABSTRACT_FUTURE& future = current_future();
699  if (!handle_no_error(future, status))
700  return I2CCallback::ERROR;
701 
702  // Handle TWI interrupt when data received
703  if ((current_ == State::RECV) || (current_ == State::RECV_LAST))
704  {
705  const uint8_t data = TWDR_;
706  bool ok = future.set_future_value_(data);
707  // This should only happen in case there are 2 concurrent providers for this future
708  if (ok)
709  command_.decrement_byte_count();
710  else
711  future.set_future_error_(errors::EILSEQ);
712  debug_hook_.call_hook(ok ? DebugStatus::RECV_OK : DebugStatus::RECV_ERROR, data);
713  }
714 
715  // Handle next step in current command
717  current_ = next_state_();
718  switch (current_)
719  {
720  case State::NONE:
721  case State::START:
722  // This cannot happen
723  break;
724 
725  case State::SLAR:
726  exec_send_slar_();
727  break;
728 
729  case State::RECV:
730  case State::RECV_LAST:
731  exec_receive_data_();
732  break;
733 
734  case State::SLAW:
735  exec_send_slaw_();
736  break;
737 
738  case State::SEND:
739  exec_send_data_();
740  break;
741 
742  case State::STOP:
743  // Check if we need to finish the current future
744  if (command_.type().is_finish())
745  future.set_future_finish_();
746  result = (is_end_transaction() ? I2CCallback::END_TRANSACTION : I2CCallback::END_COMMAND);
747  // Check if we need to STOP (no more pending commands in queue)
748  if (commands_.empty_())
749  exec_stop_();
750  // Check if we need to STOP or REPEAT START (current command requires STOP)
751  else if (command_.type().is_stop())
752  {
753  exec_stop_();
754  // Handle next command
755  dequeue_command_(true);
756  }
757  else
758  // Handle next command
759  dequeue_command_(false);
760  }
761  return result;
762  }
763 
764  bool check_no_error(ABSTRACT_FUTURE& future, Status status)
765  {
766  status_hook_.call_hook(expected_status_, status);
767  if (status == expected_status_) return true;
768  // Handle special case of last transmitted byte possibly not acknowledged by device
769  if ( (expected_status_ == Status::DATA_TRANSMITTED_ACK)
770  && (status == Status::DATA_TRANSMITTED_NACK)
771  && (command_.byte_count() == 0))
772  return true;
773 
774  // The future must be marked as error
775  if (future.status() != future::FutureStatus::ERROR)
776  future.set_future_error_(errors::EPROTO);
777  return false;
778  }
779 
780  // Status of current command processing
781  I2CCOMMAND command_;
782 
783  // Latest I2C status
784  Status expected_status_ = Status::OK;
785 
786  // Status of current command processing
787  State current_ = State::NONE;
788 
789  // Queue of commands to execute
791 
792  POLICY policy_{};
793  LC lc_;
794  STATUS status_hook_;
795  DEBUG debug_hook_;
796 
797  template<typename> friend class I2CDevice;
798  friend struct isr_handler;
799  };
800 
815  template<I2CMode MODE_, I2CErrorPolicy POLICY_ = I2CErrorPolicy::CLEAR_ALL_COMMANDS>
817  public AbstractI2CAsyncManager<MODE_, POLICY_, false, false, I2C_STATUS_HOOK, false, I2C_DEBUG_HOOK>
818  {
820  public:
829  template<uint8_t SIZE>
830  explicit I2CAsyncManager(typename PARENT::I2CCOMMAND (&buffer)[SIZE]) : PARENT{buffer}
831  {
833  }
834  };
835 
853  template<
854  I2CMode MODE_,
855  I2CErrorPolicy POLICY_ = I2CErrorPolicy::CLEAR_ALL_COMMANDS,
856  typename DEBUG_HOOK_ = I2C_DEBUG_HOOK>
858  public AbstractI2CAsyncManager<MODE_, POLICY_, false, false, I2C_STATUS_HOOK, true, DEBUG_HOOK_>
859  {
861  public:
872  template<uint8_t SIZE>
874  typename PARENT::I2CCOMMAND (&buffer)[SIZE], DEBUG_HOOK_ debug_hook)
875  : PARENT{buffer, nullptr, nullptr, debug_hook}
876  {
878  }
879  };
880 
898  template<
899  I2CMode MODE_,
900  I2CErrorPolicy POLICY_ = I2CErrorPolicy::CLEAR_ALL_COMMANDS,
901  typename STATUS_HOOK_ = I2C_STATUS_HOOK>
903  public AbstractI2CAsyncManager<MODE_, POLICY_, false, true, STATUS_HOOK_, false, I2C_DEBUG_HOOK>
904  {
906  public:
917  template<uint8_t SIZE>
919  typename PARENT::I2CCOMMAND (&buffer)[SIZE], STATUS_HOOK_ status_hook)
920  : PARENT{buffer, nullptr, status_hook}
921  {
923  }
924  };
925 
947  template<
948  I2CMode MODE_,
949  I2CErrorPolicy POLICY_ = I2CErrorPolicy::CLEAR_ALL_COMMANDS,
950  typename STATUS_HOOK_ = I2C_STATUS_HOOK,
951  typename DEBUG_HOOK_ = I2C_DEBUG_HOOK>
953  public AbstractI2CAsyncManager<MODE_, POLICY_, false, true, STATUS_HOOK_, true, DEBUG_HOOK_>
954  {
956  public:
969  template<uint8_t SIZE>
971  typename PARENT::I2CCOMMAND (&buffer)[SIZE], STATUS_HOOK_ status_hook, DEBUG_HOOK_ debug_hook)
972  : PARENT{buffer, nullptr, status_hook, debug_hook}
973  {
975  }
976  };
977 
992  template<I2CMode MODE_, I2CErrorPolicy POLICY_ = I2CErrorPolicy::CLEAR_ALL_COMMANDS>
994  public AbstractI2CAsyncManager<MODE_, POLICY_, true, false, I2C_STATUS_HOOK, false, I2C_DEBUG_HOOK>
995  {
997  public:
1008  template<uint8_t SIZE>
1010  typename PARENT::I2CCOMMAND (&buffer)[SIZE], lifecycle::AbstractLifeCycleManager& lifecycle_manager)
1011  : PARENT{buffer, &lifecycle_manager}
1012  {
1014  }
1015  };
1016 
1034  template<
1035  I2CMode MODE_,
1036  I2CErrorPolicy POLICY_ = I2CErrorPolicy::CLEAR_ALL_COMMANDS,
1037  typename DEBUG_HOOK_ = I2C_DEBUG_HOOK>
1039  public AbstractI2CAsyncManager<MODE_, POLICY_, true, false, I2C_STATUS_HOOK, true, DEBUG_HOOK_>
1040  {
1042  public:
1055  template<uint8_t SIZE>
1057  typename PARENT::I2CCOMMAND (&buffer)[SIZE],
1058  lifecycle::AbstractLifeCycleManager& lifecycle_manager,
1059  DEBUG_HOOK_ debug_hook)
1060  : PARENT{buffer, &lifecycle_manager, nullptr, debug_hook}
1061  {
1063  }
1064  };
1065 
1083  template<
1084  I2CMode MODE_,
1085  I2CErrorPolicy POLICY_ = I2CErrorPolicy::CLEAR_ALL_COMMANDS,
1086  typename STATUS_HOOK_ = I2C_STATUS_HOOK>
1088  public AbstractI2CAsyncManager<MODE_, POLICY_, true, true, STATUS_HOOK_, false, I2C_DEBUG_HOOK>
1089  {
1091  public:
1104  template<uint8_t SIZE>
1106  typename PARENT::I2CCOMMAND (&buffer)[SIZE],
1107  lifecycle::AbstractLifeCycleManager& lifecycle_manager,
1108  STATUS_HOOK_ status_hook)
1109  : PARENT{buffer, &lifecycle_manager, status_hook}
1110  {
1112  }
1113  };
1114 
1135  template<
1136  I2CMode MODE_,
1137  I2CErrorPolicy POLICY_ = I2CErrorPolicy::CLEAR_ALL_COMMANDS,
1138  typename STATUS_HOOK_ = I2C_STATUS_HOOK,
1139  typename DEBUG_HOOK_ = I2C_DEBUG_HOOK>
1141  public AbstractI2CAsyncManager<MODE_, POLICY_, true, true, STATUS_HOOK_, true, DEBUG_HOOK_>
1142  {
1144  public:
1159  template<uint8_t SIZE>
1161  typename PARENT::I2CCOMMAND (&buffer)[SIZE],
1162  lifecycle::AbstractLifeCycleManager& lifecycle_manager,
1163  STATUS_HOOK_ status_hook, DEBUG_HOOK_ debug_hook)
1164  : PARENT{buffer, &lifecycle_manager, status_hook, debug_hook}
1165  {
1167  }
1168  };
1169 
1178  template<I2CMode MODE_>
1180  public AbstractI2CSyncATmegaManager<MODE_, false, false, I2C_STATUS_HOOK, false, I2C_DEBUG_HOOK>
1181  {
1183  public:
1184  I2CSyncManager() : PARENT{} {}
1185  };
1186 
1199  template<I2CMode MODE_, typename STATUS_HOOK_ = I2C_STATUS_HOOK>
1201  public AbstractI2CSyncATmegaManager<MODE_, false, true, STATUS_HOOK_, false, I2C_DEBUG_HOOK>
1202  {
1204  public:
1205  explicit I2CSyncStatusManager(STATUS_HOOK_ status_hook) : PARENT{nullptr, status_hook} {}
1206  };
1207 
1219  template<I2CMode MODE_, typename DEBUG_HOOK_ = I2C_DEBUG_HOOK>
1221  public AbstractI2CSyncATmegaManager<MODE_, false, false, I2C_STATUS_HOOK, true, DEBUG_HOOK_>
1222  {
1224  public:
1225  explicit I2CSyncDebugManager(DEBUG_HOOK_ debug_hook) : PARENT{nullptr, nullptr, debug_hook} {}
1226  };
1227 
1243  template<I2CMode MODE_, typename STATUS_HOOK_ = I2C_STATUS_HOOK, typename DEBUG_HOOK_ = I2C_DEBUG_HOOK>
1245  public AbstractI2CSyncATmegaManager<MODE_, false, true, STATUS_HOOK_, true, DEBUG_HOOK_>
1246  {
1248  public:
1249  explicit I2CSyncStatusDebugManager(STATUS_HOOK_ status_hook, DEBUG_HOOK_ debug_hook)
1250  : PARENT{nullptr, status_hook, debug_hook} {}
1251  };
1252 
1261  template<I2CMode MODE_>
1263  public AbstractI2CSyncATmegaManager<MODE_, true, false, I2C_STATUS_HOOK, false, I2C_DEBUG_HOOK>
1264  {
1266  public:
1267  explicit I2CSyncLCManager(lifecycle::AbstractLifeCycleManager& lifecycle_manager)
1268  : PARENT{&lifecycle_manager} {}
1269  };
1270 
1282  template<I2CMode MODE_, typename STATUS_HOOK_>
1284  public AbstractI2CSyncATmegaManager<MODE_, true, true, STATUS_HOOK_, false, I2C_DEBUG_HOOK>
1285  {
1287  public:
1288  explicit I2CSyncLCStatusManager(
1289  lifecycle::AbstractLifeCycleManager& lifecycle_manager, STATUS_HOOK_ status_hook)
1290  : PARENT{&lifecycle_manager, status_hook} {}
1291  };
1292 
1304  template<I2CMode MODE_, typename DEBUG_HOOK_ = I2C_DEBUG_HOOK>
1306  public AbstractI2CSyncATmegaManager<MODE_, true, false, I2C_STATUS_HOOK, true, DEBUG_HOOK_>
1307  {
1309  public:
1310  explicit I2CSyncLCDebugManager(
1311  lifecycle::AbstractLifeCycleManager& lifecycle_manager, DEBUG_HOOK_ debug_hook)
1312  : PARENT{&lifecycle_manager, nullptr, debug_hook} {}
1313  };
1314 
1329  template<I2CMode MODE_, typename STATUS_HOOK_ = I2C_STATUS_HOOK, typename DEBUG_HOOK_ = I2C_DEBUG_HOOK>
1331  public AbstractI2CSyncATmegaManager<MODE_, true, true, STATUS_HOOK_, true, DEBUG_HOOK_>
1332  {
1334  public:
1335  explicit I2CSyncLCStatusDebugManager(
1336  lifecycle::AbstractLifeCycleManager& lifecycle_manager, STATUS_HOOK_ status_hook, DEBUG_HOOK_ debug_hook)
1337  : PARENT{&lifecycle_manager, status_hook, debug_hook} {}
1338  };
1339 
1341  // Specific traits for I2C Manager
1342  // Async managers first
1343  template<I2CMode MODE_, I2CErrorPolicy POLICY_>
1344  struct I2CManager_trait<I2CAsyncManager<MODE_, POLICY_>>
1345  : I2CManager_trait_impl<true, false, false, false, MODE_> {};
1346 
1347  template<I2CMode MODE_, I2CErrorPolicy POLICY_>
1348  struct I2CManager_trait<I2CAsyncLCManager<MODE_, POLICY_>>
1349  : I2CManager_trait_impl<true, true, false, false, MODE_> {};
1350 
1351  template<I2CMode MODE_, I2CErrorPolicy POLICY_, typename DEBUG_HOOK_>
1352  struct I2CManager_trait<I2CAsyncDebugManager<MODE_, POLICY_, DEBUG_HOOK_>>
1353  : I2CManager_trait_impl<true, false, false, true, MODE_> {};
1354 
1355  template<I2CMode MODE_, I2CErrorPolicy POLICY_, typename STATUS_HOOK_>
1356  struct I2CManager_trait<I2CAsyncStatusManager<MODE_, POLICY_, STATUS_HOOK_>>
1357  : I2CManager_trait_impl<true, false, true, false, MODE_> {};
1358 
1359  template<I2CMode MODE_, I2CErrorPolicy POLICY_, typename STATUS_HOOK_, typename DEBUG_HOOK_>
1360  struct I2CManager_trait<I2CAsyncStatusDebugManager<MODE_, POLICY_, STATUS_HOOK_, DEBUG_HOOK_>>
1361  : I2CManager_trait_impl<true, false, true, true, MODE_> {};
1362 
1363  template<I2CMode MODE_, I2CErrorPolicy POLICY_, typename DEBUG_HOOK_>
1364  struct I2CManager_trait<I2CAsyncLCDebugManager<MODE_, POLICY_, DEBUG_HOOK_>>
1365  : I2CManager_trait_impl<true, true, false, true, MODE_> {};
1366 
1367  template<I2CMode MODE_, I2CErrorPolicy POLICY_, typename STATUS_HOOK_>
1368  struct I2CManager_trait<I2CAsyncLCStatusManager<MODE_, POLICY_, STATUS_HOOK_>>
1369  : I2CManager_trait_impl<true, true, true, false, MODE_> {};
1370 
1371  template<I2CMode MODE_, I2CErrorPolicy POLICY_, typename STATUS_HOOK_, typename DEBUG_HOOK_>
1372  struct I2CManager_trait<I2CAsyncLCStatusDebugManager<MODE_, POLICY_, STATUS_HOOK_, DEBUG_HOOK_>>
1373  : I2CManager_trait_impl<true, true, true, true, MODE_> {};
1375 
1377  struct isr_handler
1378  {
1379  template<typename MANAGER>
1380  static void i2c_change()
1381  {
1382  static_assert(I2CManager_trait<MANAGER>::IS_I2CMANAGER, "MANAGER must be an I2C Manager");
1383  static_assert(I2CManager_trait<MANAGER>::IS_ASYNC, "MANAGER must be an asynchronous I2C Manager");
1384  interrupt::HandlerHolder<MANAGER>::handler()->i2c_change();
1385  }
1386 
1387  template<typename MANAGER, void (*CALLBACK_)(I2CCallback, typename MANAGER::FUTURE_PROXY)>
1388  static void i2c_change_function()
1389  {
1390  using interrupt::HandlerHolder;
1391  static_assert(I2CManager_trait<MANAGER>::IS_I2CMANAGER, "MANAGER must be an I2C Manager");
1392  static_assert(I2CManager_trait<MANAGER>::IS_ASYNC, "MANAGER must be an asynchronous I2C Manager");
1393  auto proxy = HandlerHolder<MANAGER>::handler()->current_proxy();
1394  I2CCallback callback = HandlerHolder<MANAGER>::handler()->i2c_change();
1395  if (callback != I2CCallback::NONE)
1396  {
1397  CALLBACK_(callback, proxy);
1398  }
1399  }
1400 
1401  template<typename MANAGER, typename HANDLER_,
1402  void (HANDLER_::*CALLBACK_)(I2CCallback, typename MANAGER::FUTURE_PROXY)>
1403  static void i2c_change_method()
1404  {
1405  using interrupt::HandlerHolder;
1406  using interrupt::CallbackHandler;
1407  static_assert(I2CManager_trait<MANAGER>::IS_I2CMANAGER, "MANAGER must be an I2C Manager");
1408  static_assert(I2CManager_trait<MANAGER>::IS_ASYNC, "MANAGER must be an asynchronous I2C Manager");
1409  auto proxy = HandlerHolder<MANAGER>::handler()->current_proxy();
1410  I2CCallback callback = HandlerHolder<MANAGER>::handler()->i2c_change();
1411  if (callback != I2CCallback::NONE)
1412  {
1413  using HANDLER =
1414  CallbackHandler<void (HANDLER_::*)(I2CCallback, typename MANAGER::FUTURE_PROXY), CALLBACK_>;
1415  HANDLER::call(callback, proxy);
1416  }
1417  }
1418  };
1420 }
1421 
1422 #endif /* I2C_HANDLER_ATMEGA_HH */
1423 
i2c::I2CAsyncDebugManager::I2CAsyncDebugManager
I2CAsyncDebugManager(typename PARENT::I2CCOMMAND(&buffer)[SIZE], DEBUG_HOOK_ debug_hook)
Create an asynchronous I2C Manager for ATmega MCUs.
Definition: i2c_handler_atmega.h:873
i2c::I2CCallback::NONE
@ NONE
An I2C command is being processed (intermediate step).
future
Contains the API around Future implementation.
Definition: future.cpp:18
i2c::I2CDevice
Base class for all I2C devices.
Definition: i2c_device.h:78
future.h
Utility API to handle the concept of futures.
i2c::I2CAsyncLCManager
Asynchronous I2C Manager for ATmega architecture with support for dynamic proxies.
Definition: i2c_handler_atmega.h:995
containers::Queue
Queue of type T_ items.
Definition: queue.h:59
i2c::I2CAsyncLCStatusDebugManager::I2CAsyncLCStatusDebugManager
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.
Definition: i2c_handler_atmega.h:1160
i2c::AbstractI2CSyncATmegaManager
Abstract synchronous I2C Manager for ATmega architecture.
Definition: i2c_handler_atmega.h:338
i2c::I2CAsyncStatusManager::I2CAsyncStatusManager
I2CAsyncStatusManager(typename PARENT::I2CCOMMAND(&buffer)[SIZE], STATUS_HOOK_ status_hook)
Create an asynchronous I2C Manager for ATmega MCUs.
Definition: i2c_handler_atmega.h:918
i2c::I2CMode
I2CMode
I2C available transmission modes.
Definition: i2c.h:108
i2c::DebugStatus::SLAW
@ SLAW
A slave address has just been sent for writing.
i2c::I2CAsyncStatusDebugManager::I2CAsyncStatusDebugManager
I2CAsyncStatusDebugManager(typename PARENT::I2CCOMMAND(&buffer)[SIZE], STATUS_HOOK_ status_hook, DEBUG_HOOK_ debug_hook)
Create an asynchronous I2C Manager for ATmega MCUs.
Definition: i2c_handler_atmega.h:970
future::AbstractFuture
Base class for all Futures.
Definition: future.h:223
i2c_handler_common.h
Common I2C Manager API.
i2c::I2CSyncDebugManager
Synchronous I2C Manager for ATmega architecture with debug facility.
Definition: i2c_handler_atmega.h:1222
i2c::I2CErrorPolicy
I2CErrorPolicy
I2C Manager policy to use in case of an error during I2C transaction.
Definition: i2c_handler_atmega.h:120
future::Future
Represent a value to be obtained, in some asynchronous way, in the future.
Definition: future.h:691
errors::EILSEQ
constexpr const int EILSEQ
Illegal byte sequence.
Definition: errors.h:65
i2c::AbstractI2CSyncManager
Abstract synchronous I2C Manager for all MCU architectures.
Definition: i2c_handler_common.h:413
bits::COMPL
static constexpr uint8_t COMPL(uint8_t value)
Return the uint8_t 2-complement of a byte.
Definition: bits.h:253
i2c::I2CAsyncLCStatusDebugManager
Asynchronous I2C Manager for ATmega architecture with debug and status notification facilities and su...
Definition: i2c_handler_atmega.h:1142
i2c::AbstractI2CAsyncManager
Abstract asynchronous I2C Manager.
Definition: i2c_handler_atmega.h:393
i2c::I2CAsyncLCDebugManager::I2CAsyncLCDebugManager
I2CAsyncLCDebugManager(typename PARENT::I2CCOMMAND(&buffer)[SIZE], lifecycle::AbstractLifeCycleManager &lifecycle_manager, DEBUG_HOOK_ debug_hook)
Create an asynchronous I2C Manager for ATmega MCUs.
Definition: i2c_handler_atmega.h:1056
i2c::I2CSyncLCStatusManager
Synchronous I2C Manager for ATmega architecture with status notification facility and support for dyn...
Definition: i2c_handler_atmega.h:1285
i2c::I2CSyncManager
Synchronous I2C Manager for ATmega architecture.
Definition: i2c_handler_atmega.h:1181
i2c::DebugStatus::STOP
@ STOP
A stop condition has just been sent.
i2c::DebugStatus::REPEAT_START
@ REPEAT_START
A repeat start condition has just been sent.
i2c::DebugStatus::START
@ START
A start condition has just been sent.
bits::BV8
static constexpr uint8_t BV8(uint8_t bit)
Create a uint8_t bitmask for the given bit number.
Definition: bits.h:41
bits.h
Useful bits manipulation utilities.
i2c::I2CAsyncLCDebugManager
Asynchronous I2C Manager for ATmega architecture with debug facility and support for dynamic proxies.
Definition: i2c_handler_atmega.h:1040
i2c::DebugStatus::SLAR
@ SLAR
A slave address has just been sent for reading.
i2c::I2CSyncLCManager
Synchronous I2C Manager for ATmega architecture with support for dynamic proxies.
Definition: i2c_handler_atmega.h:1264
i2c::Status::OK
@ OK
Code indicating the last called method executed as expected without any issue.
i2c::Status
Status
Transmission status codes.
Definition: i2c.h:66
i2c::I2CAsyncLCStatusManager
Asynchronous I2C Manager for ATmega architecture with status notification facility and support for dy...
Definition: i2c_handler_atmega.h:1089
i2c::I2CCommand< FUTURE_PROXY >
board::Port::PORT_A
@ PORT_A
Port A (8 IO)
i2c::I2CAsyncManager::I2CAsyncManager
I2CAsyncManager(typename PARENT::I2CCOMMAND(&buffer)[SIZE])
Create an asynchronous I2C Manager for ATmega MCUs.
Definition: i2c_handler_atmega.h:830
i2c::I2CAsyncManager
Asynchronous I2C Manager for ATmega architecture.
Definition: i2c_handler_atmega.h:818
i2c::AbstractI2CAsyncManager< MODE_, I2CErrorPolicy::CLEAR_ALL_COMMANDS, true, false, I2C_STATUS_HOOK, true, I2C_DEBUG_HOOK >::FUTURE_PROXY
PROXY< ABSTRACT_FUTURE > FUTURE_PROXY
The type passed to callback functions registered alongside ISR.
Definition: i2c_handler_atmega.h:415
i2c::AbstractI2CAsyncManager::end
void end()
Disable MCU I2C transmission.
Definition: i2c_handler_atmega.h:441
i2c::I2CSyncStatusManager
Synchronous I2C Manager for ATmega architecture wit status notification facility.
Definition: i2c_handler_atmega.h:1202
lifecycle::AbstractLifeCycleManager
The abstract base class of all LifeCycleManager.
Definition: lifecycle.h:132
UNUSED
#define UNUSED
Specific GCC attribute to declare an argument or variable unused, so that the compiler does not emit ...
Definition: defines.h:45
queue.h
Utility API to handle ring-buffer queue containers.
utilities.h
General utilities API that have broad application in programs.
i2c::I2CAsyncStatusManager
Asynchronous I2C Manager for ATmega architecture with status notification facility.
Definition: i2c_handler_atmega.h:904
i2c::I2CAsyncStatusDebugManager
Asynchronous I2C Manager for ATmega architecture with debug and status notification facilities.
Definition: i2c_handler_atmega.h:954
i2c::I2CSyncLCDebugManager
Synchronous I2C Manager for ATmega architecture with debug facility and support for dynamic proxies.
Definition: i2c_handler_atmega.h:1307
bits
Defines utility methods for bits manipulation.
Definition: bits.h:34
i2c::I2CSyncStatusDebugManager
Synchronous I2C Manager for ATmega architecture with status notification and debug facility.
Definition: i2c_handler_atmega.h:1246
i2c::I2CAsyncDebugManager
Asynchronous I2C Manager for ATmega architecture with debug facility.
Definition: i2c_handler_atmega.h:859
i2c::AbstractI2CAsyncManager::begin_
void begin_()
Prepare and enable the MCU for I2C transmission.
Definition: i2c_handler_atmega.h:453
i2c::AbstractI2CAsyncManager::end_
void end_()
Disable MCU I2C transmission.
Definition: i2c_handler_atmega.h:470
i2c::I2CCallback
I2CCallback
Type passed to I2C ISR registered callbacks (asynchronous I2C Manager only) when an asynchronous I2C ...
Definition: i2c_handler_atmega.h:147
i2c::DebugStatus::RECV
@ RECV
A byte is being received from the slave.
future::AbstractFakeFuture
Base class for all FakeFutures.
Definition: future.h:1018
i2c::AbstractI2CAsyncManager::I2CCOMMAND
I2CCommand< FUTURE_PROXY > I2CCOMMAND
The type of I2CCommand to use in the buffer passed to the constructor of this AbstractI2CAsyncManager...
Definition: i2c_handler_atmega.h:421
i2c::I2C_DEBUG_HOOK
void(*)(DebugStatus status, uint8_t data) I2C_DEBUG_HOOK
The default debugging hook type.
Definition: i2c_handler_common.h:83
i2c::DebugStatus::RECV_LAST
@ RECV_LAST
The last byte is being received from the slave.
i2c
Define API to define and manage I2C devices.
Definition: i2c.cpp:18
interrupts.h
General API for handling AVR interrupt vectors.
i2c::I2CAsyncLCManager::I2CAsyncLCManager
I2CAsyncLCManager(typename PARENT::I2CCOMMAND(&buffer)[SIZE], lifecycle::AbstractLifeCycleManager &lifecycle_manager)
Create an asynchronous I2C Manager for ATmega MCUs.
Definition: i2c_handler_atmega.h:1009
interrupt::register_handler
void register_handler(Handler &handler)
Register a class instance containing methods that shall be called back by an ISR.
Definition: interrupts.h:157
i2c::I2CErrorPolicy::DO_NOTHING
@ DO_NOTHING
Do nothing at all in case of an error; useful only with a synchronous I2C Manager.
i2c::status::STATUS
STATUS
Indicate when status should be traced.
Definition: i2c_status.h:47
i2c::DebugStatus::SEND
@ SEND
A byte has just be sent to the slave.
i2c::I2CSyncLCStatusDebugManager
Synchronous I2C Manager for ATmega architecture with status notification and debug facilities and sup...
Definition: i2c_handler_atmega.h:1332
i2c.h
I2C API common definitions.
i2c::I2C_STATUS_HOOK
void(*)(Status expected, Status actual) I2C_STATUS_HOOK
The default status observer hook type.
Definition: i2c_handler_common.h:96
i2c::I2CAsyncLCStatusManager::I2CAsyncLCStatusManager
I2CAsyncLCStatusManager(typename PARENT::I2CCOMMAND(&buffer)[SIZE], lifecycle::AbstractLifeCycleManager &lifecycle_manager, STATUS_HOOK_ status_hook)
Create an asynchronous I2C Manager for ATmega MCUs.
Definition: i2c_handler_atmega.h:1105
i2c::AbstractI2CAsyncManager::begin
void begin()
Prepare and enable the MCU for I2C transmission.
Definition: i2c_handler_atmega.h:430
errors::EPROTO
constexpr const int EPROTO
Protocol error.
Definition: errors.h:58