FastArduino  v1.8
C++ library to build fast but small Arduino/AVR projects
i2c_handler_attiny.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_ATTINY_HH
23 #define I2C_HANDLER_ATTINY_HH
24 
25 #include <util/delay_basic.h>
26 
27 #include "i2c.h"
28 #include "future.h"
29 #include "bits.h"
30 #include "utilities.h"
31 #include "i2c_handler_common.h"
32 
33 // Prevent direct inclusion (must be done through i2c_handler.h)
34 #ifndef I2C_HANDLER_HH
35 #error "i2c_handler_attiny.h shall not be directly included! Include 'i2c_handler.h' instead."
36 #endif
37 // Prevent inclusion for ATmega architecture
38 #ifdef TWCR
39 #error "i2c_handler_attiny.h cannot be included in an ATmega program!"
40 #endif
41 
47 #define I2C_TRUE_ASYNC 0
48 
49 namespace i2c
50 {
51  //==============
52  // Sync Handler
53  //==============
55  template<I2CMode MODE_, bool HAS_STATUS_ = false, typename STATUS_HOOK_ = I2C_STATUS_HOOK>
56  class ATtinyI2CSyncHandler
57  {
58  private:
59  using MODE_TRAIT = I2CMode_trait<MODE_>;
60  using I2C_TRAIT = board_traits::TWI_trait;
61  using REG8 = board_traits::REG8;
62  using STATUS = I2CStatusSupport<HAS_STATUS_, STATUS_HOOK_>;
63 
64  public:
65  explicit ATtinyI2CSyncHandler(STATUS_HOOK_ status_hook = nullptr) : status_hook_{status_hook}
66  {
67  // set SDA/SCL default directions
68  I2C_TRAIT::PORT |= I2C_TRAIT::SCL_SDA_MASK;
69  I2C_TRAIT::DDR |= I2C_TRAIT::SCL_SDA_MASK;
70  }
71 
72  void begin_()
73  {
74  // 1. Force 1 to data
75  USIDR_ = UINT8_MAX;
76  // 2. Enable TWI
77  // Set USI I2C mode, enable software clock strobe (USITC)
78  USICR_ = bits::BV8(USIWM1, USICS1, USICLK);
79  // Clear all interrupt flags
80  USISR_ = bits::BV8(USISIF, USIOIF, USIPF, USIDC);
81  // 3. Set SDA as output
82  SDA_OUTPUT();
83  }
84 
85  void end_()
86  {
87  // Disable TWI
88  USICR_ = 0;
89  // Set SDA back to INPUT
90  SDA_INPUT();
91  }
92 
93  // Low-level methods to handle the bus in an asynchronous way
94  bool exec_start_()
95  {
96  return notify_status(send_start_(),
97  Status::START_TRANSMITTED, Status::ARBITRATION_LOST);
98  }
99 
100  bool exec_repeat_start_()
101  {
102  return notify_status(send_start_(),
103  Status::REPEAT_START_TRANSMITTED, Status::ARBITRATION_LOST);
104  }
105 
106  bool exec_send_slar_(uint8_t target)
107  {
108  return notify_status(send_byte_impl(target | 0x01U),
109  Status::SLA_R_TRANSMITTED_ACK, Status::SLA_R_TRANSMITTED_NACK);
110  }
111 
112  bool exec_send_slaw_(uint8_t target)
113  {
114  return notify_status(send_byte_impl(target),
115  Status::SLA_W_TRANSMITTED_ACK, Status::SLA_W_TRANSMITTED_NACK);
116  }
117 
118  bool exec_send_data_(uint8_t data)
119  {
120  return notify_status(send_byte_impl(data),
121  Status::DATA_TRANSMITTED_ACK, Status::DATA_TRANSMITTED_NACK);
122  }
123 
124  bool exec_receive_data_(bool last_byte, uint8_t& data)
125  {
126  SDA_INPUT();
127  data = transfer(USISR_DATA);
128  // Send ACK (or NACK if last byte)
129  USIDR_ = (last_byte ? UINT8_MAX : 0x00);
130  transfer(USISR_ACK);
131  //TODO return ((transfer(USISR_ACK) & 0x01U) == 0);
132  return notify_status(true, Status::DATA_RECEIVED_ACK, Status::DATA_RECEIVED_NACK);
133  }
134 
135  void exec_stop_()
136  {
137  // Pull SDA low
138  SDA_LOW();
139  // Release SCL
140  SCL_HIGH();
141  _delay_loop_1(MODE_TRAIT::T_SU_STO);
142  // Release SDA
143  SDA_HIGH();
144  _delay_loop_1(MODE_TRAIT::T_BUF);
145  }
146 
147  private:
148  static constexpr const REG8 USIDR_{USIDR};
149  static constexpr const REG8 USISR_{USISR};
150  static constexpr const REG8 USICR_{USICR};
151 
152  // Constant values for USISR
153  // For byte transfer, we set counter to 0 (16 ticks => 8 clock cycles)
154  static constexpr const uint8_t USISR_DATA = bits::BV8(USISIF, USIOIF, USIPF, USIDC);
155  // For acknowledge bit, we start counter at 0E (2 ticks: 1 raising and 1 falling edge)
156  static constexpr const uint8_t USISR_ACK = USISR_DATA | (0x0E << USICNT0);
157 
158  bool notify_status(bool as_expected, Status good, Status bad)
159  {
160  status_hook_.call_hook(good, (as_expected ? good : bad));
161  return as_expected;
162  }
163 
164  void SCL_HIGH()
165  {
166  I2C_TRAIT::PORT |= bits::BV8(I2C_TRAIT::BIT_SCL);
167  I2C_TRAIT::PIN.loop_until_bit_set(I2C_TRAIT::BIT_SCL);
168  }
169 
170  void SCL_LOW()
171  {
172  I2C_TRAIT::PORT &= bits::CBV8(I2C_TRAIT::BIT_SCL);
173  }
174 
175  void SDA_HIGH()
176  {
177  I2C_TRAIT::PORT |= bits::BV8(I2C_TRAIT::BIT_SDA);
178  }
179 
180  void SDA_LOW()
181  {
182  I2C_TRAIT::PORT &= bits::CBV8(I2C_TRAIT::BIT_SDA);
183  }
184 
185  void SDA_INPUT()
186  {
187  I2C_TRAIT::DDR &= bits::CBV8(I2C_TRAIT::BIT_SDA);
188  }
189 
190  void SDA_OUTPUT()
191  {
192  I2C_TRAIT::DDR |= bits::BV8(I2C_TRAIT::BIT_SDA);
193  }
194 
195  bool send_start_()
196  {
197  // Ensure SCL is HIGH
198  SCL_HIGH();
199  // Wait for Tsu-sta
200  _delay_loop_1(MODE_TRAIT::T_SU_STA);
201  // Now we can generate start condition
202  // Force SDA low for Thd-sta
203  SDA_LOW();
204  _delay_loop_1(MODE_TRAIT::T_HD_STA);
205  // Pull SCL low
206  SCL_LOW();
207  // Release SDA (force high)
208  SDA_HIGH();
209  // Check START transmission with USISIF flag
210  return USISR_ & bits::BV8(USISIF);
211  }
212 
213  bool send_byte_impl(uint8_t data)
214  {
215  // Set SCL low
216  SCL_LOW();
217  // Transfer address byte
218  USIDR_ = data;
219  transfer(USISR_DATA);
220  // For acknowledge, first set SDA as input
221  SDA_INPUT();
222  return ((transfer(USISR_ACK) & 0x01U) == 0);
223  }
224 
225  uint8_t transfer(uint8_t USISR_count)
226  {
227  //Rework according to AVR310
228  // Init counter (8 bits or 1 bit for acknowledge)
229  USISR_ = USISR_count;
230  do
231  {
232  _delay_loop_1(MODE_TRAIT::T_LOW);
233  // clock strobe (SCL raising edge)
234  USICR_ = bits::BV8(USIWM1, USICS1, USICLK, USITC);
235  I2C_TRAIT::PIN.loop_until_bit_set(I2C_TRAIT::BIT_SCL);
236  _delay_loop_1(MODE_TRAIT::T_HIGH);
237  // clock strobe (SCL falling edge)
238  USICR_ = bits::BV8(USIWM1, USICS1, USICLK, USITC);
239  }
240  while ((USISR_ & bits::BV8(USIOIF)) == 0);
241  _delay_loop_1(MODE_TRAIT::T_LOW);
242  // Read data
243  uint8_t data = USIDR_;
244  USIDR_ = UINT8_MAX;
245  // Release SDA
246  SDA_OUTPUT();
247  return data;
248  }
249 
250  STATUS status_hook_;
251  };
253 
280  template<I2CMode MODE_, bool HAS_LC_,
281  bool HAS_STATUS_, typename STATUS_HOOK_, bool HAS_DEBUG_, typename DEBUG_HOOK_>
283  : public AbstractI2CSyncManager<ATtinyI2CSyncHandler<MODE_, HAS_STATUS_, STATUS_HOOK_>,
284  MODE_, HAS_LC_, STATUS_HOOK_, HAS_DEBUG_, DEBUG_HOOK_>
285  {
286  private:
288  MODE_, HAS_LC_, STATUS_HOOK_, HAS_DEBUG_, DEBUG_HOOK_>;
289 
290  public:
291  using ABSTRACT_FUTURE = typename PARENT::ABSTRACT_FUTURE;
292  template<typename T> using PROXY = typename PARENT::template PROXY<T>;
293  template<typename OUT, typename IN> using FUTURE = typename PARENT::template FUTURE<OUT, IN>;
294 
295  protected:
298  lifecycle::AbstractLifeCycleManager* lifecycle_manager = nullptr,
299  STATUS_HOOK_ status_hook = nullptr, DEBUG_HOOK_ debug_hook = nullptr)
300  : PARENT{lifecycle_manager, status_hook, debug_hook} {}
302 
303  template<typename> friend class I2CDevice;
304  };
305 
314  template<I2CMode MODE_>
315  class I2CSyncManager :
316  public AbstractI2CSyncATtinyManager<MODE_, false, false, I2C_STATUS_HOOK, false, I2C_DEBUG_HOOK>
317  {
319  public:
320  I2CSyncManager() : PARENT{} {}
321  };
322 
334  template<I2CMode MODE_, typename STATUS_HOOK_ = I2C_STATUS_HOOK>
335  class I2CSyncStatusManager :
336  public AbstractI2CSyncATtinyManager<MODE_, false, true, STATUS_HOOK_, false, I2C_DEBUG_HOOK>
337  {
338  using PARENT = AbstractI2CSyncATtinyManager<MODE_, false, true, STATUS_HOOK_, false, I2C_DEBUG_HOOK>;
339  public:
340  explicit I2CSyncStatusManager(STATUS_HOOK_ status_hook) : PARENT{nullptr, status_hook} {}
341  };
342 
354  template<I2CMode MODE_, typename DEBUG_HOOK_ = I2C_DEBUG_HOOK>
355  class I2CSyncDebugManager :
356  public AbstractI2CSyncATtinyManager<MODE_, false, false, I2C_STATUS_HOOK, true, DEBUG_HOOK_>
357  {
358  using PARENT = AbstractI2CSyncATtinyManager<MODE_, false, false, I2C_STATUS_HOOK, true, DEBUG_HOOK_>;
359  public:
360  explicit I2CSyncDebugManager(DEBUG_HOOK_ debug_hook) : PARENT{nullptr, nullptr, debug_hook} {}
361  };
362 
378  template<I2CMode MODE_, typename STATUS_HOOK_ = I2C_STATUS_HOOK, typename DEBUG_HOOK_ = I2C_DEBUG_HOOK>
379  class I2CSyncStatusDebugManager :
380  public AbstractI2CSyncATtinyManager<MODE_, false, true, STATUS_HOOK_, true, DEBUG_HOOK_>
381  {
382  using PARENT = AbstractI2CSyncATtinyManager<MODE_, false, true, STATUS_HOOK_, true, DEBUG_HOOK_>;
383  public:
384  explicit I2CSyncStatusDebugManager(STATUS_HOOK_ status_hook, DEBUG_HOOK_ debug_hook)
385  : PARENT{nullptr, status_hook, debug_hook} {}
386  };
387 
396  template<I2CMode MODE_>
397  class I2CSyncLCManager :
398  public AbstractI2CSyncATtinyManager<MODE_, true, false, I2C_STATUS_HOOK, false, I2C_DEBUG_HOOK>
399  {
400  using PARENT = AbstractI2CSyncATtinyManager<MODE_, true, false, I2C_STATUS_HOOK, false, I2C_DEBUG_HOOK>;
401  public:
402  explicit I2CSyncLCManager(lifecycle::AbstractLifeCycleManager& lifecycle_manager)
403  : PARENT{&lifecycle_manager} {}
404  };
405 
417  template<I2CMode MODE_, typename STATUS_HOOK_>
418  class I2CSyncLCStatusManager :
419  public AbstractI2CSyncATtinyManager<MODE_, true, true, STATUS_HOOK_, false, I2C_DEBUG_HOOK>
420  {
421  using PARENT = AbstractI2CSyncATtinyManager<MODE_, true, true, STATUS_HOOK_, false, I2C_DEBUG_HOOK>;
422  public:
423  explicit I2CSyncLCStatusManager(
424  lifecycle::AbstractLifeCycleManager& lifecycle_manager, STATUS_HOOK_ status_hook)
425  : PARENT{&lifecycle_manager, status_hook} {}
426  };
427 
439  template<I2CMode MODE_, typename DEBUG_HOOK_ = I2C_DEBUG_HOOK>
440  class I2CSyncLCDebugManager :
441  public AbstractI2CSyncATtinyManager<MODE_, true, false, I2C_STATUS_HOOK, true, DEBUG_HOOK_>
442  {
443  using PARENT = AbstractI2CSyncATtinyManager<MODE_, true, false, I2C_STATUS_HOOK, true, DEBUG_HOOK_>;
444  public:
445  explicit I2CSyncLCDebugManager(
446  lifecycle::AbstractLifeCycleManager& lifecycle_manager, DEBUG_HOOK_ debug_hook)
447  : PARENT{&lifecycle_manager, nullptr, debug_hook} {}
448  };
449 
464  template<I2CMode MODE_, typename STATUS_HOOK_ = I2C_STATUS_HOOK, typename DEBUG_HOOK_ = I2C_DEBUG_HOOK>
465  class I2CSyncLCStatusDebugManager :
466  public AbstractI2CSyncATtinyManager<MODE_, true, true, STATUS_HOOK_, true, DEBUG_HOOK_>
467  {
468  using PARENT = AbstractI2CSyncATtinyManager<MODE_, true, true, STATUS_HOOK_, true, DEBUG_HOOK_>;
469  public:
470  explicit I2CSyncLCStatusDebugManager(
471  lifecycle::AbstractLifeCycleManager& lifecycle_manager, STATUS_HOOK_ status_hook, DEBUG_HOOK_ debug_hook)
472  : PARENT{&lifecycle_manager, status_hook, debug_hook} {}
473  };
474 }
475 
476 #endif /* I2C_HANDLER_ATTINY_HH */
477 
i2c::I2CDevice
Base class for all I2C devices.
Definition: i2c_device.h:86
future.h
Utility API to handle the concept of futures.
i2c::I2CMode
I2CMode
I2C available transmission modes.
Definition: i2c.h:108
i2c_handler_common.h
Common I2C Manager API.
i2c::AbstractI2CSyncManager
Abstract synchronous I2C Manager for all MCU architectures.
Definition: i2c_handler_common.h:413
i2c::I2CSyncManager
Synchronous I2C Manager for ATmega architecture.
Definition: i2c_handler_atmega.h:1184
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::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
lifecycle::AbstractLifeCycleManager
The abstract base class of all LifeCycleManager.
Definition: lifecycle.h:132
i2c::AbstractI2CSyncATtinyManager
Abstract synchronous I2C Manager for ATtiny architecture.
Definition: i2c_handler_attiny.h:285
utilities.h
General utilities API that have broad application in programs.
bits::CBV8
static constexpr uint8_t CBV8(uint8_t bit)
Create a uint8_t inverted bitmask for the given bit number.
Definition: bits.h:137
future::AbstractFakeFuture
Base class for all FakeFutures.
Definition: future.h:1062
i2c
Define API to define and manage I2C devices.
Definition: i2c.cpp:18
i2c::status::STATUS
STATUS
Indicate when status should be traced.
Definition: i2c_status.h:47
i2c.h
I2C API common definitions.