FastArduino v1.10
C++ library to build fast but small Arduino/AVR projects
Loading...
Searching...
No Matches
i2c_handler_attiny.h
Go to the documentation of this file.
1// Copyright 2016-2023 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
49namespace 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
277 template<I2CMode MODE_, bool HAS_STATUS_, typename STATUS_HOOK_, bool HAS_DEBUG_, typename DEBUG_HOOK_>
279 : public AbstractI2CSyncManager<ATtinyI2CSyncHandler<MODE_, HAS_STATUS_, STATUS_HOOK_>,
280 MODE_, STATUS_HOOK_, HAS_DEBUG_, DEBUG_HOOK_>
281 {
282 private:
284 MODE_, STATUS_HOOK_, HAS_DEBUG_, DEBUG_HOOK_>;
285
286 public:
289 template<typename OUT, typename IN> using FUTURE = typename PARENT::template FUTURE<OUT, IN>;
291
292 protected:
295 STATUS_HOOK_ status_hook = nullptr, DEBUG_HOOK_ debug_hook = nullptr)
296 : PARENT{status_hook, debug_hook} {}
298
299 template<typename> friend class I2CDevice;
300 };
301
310 template<I2CMode MODE_>
311 class I2CSyncManager :
312 public AbstractI2CSyncATtinyManager<MODE_, false, I2C_STATUS_HOOK, false, I2C_DEBUG_HOOK>
313 {
315 public:
317 I2CSyncManager() : PARENT{} {}
319 };
320
332 template<I2CMode MODE_, typename STATUS_HOOK_ = I2C_STATUS_HOOK>
333 class I2CSyncStatusManager :
334 public AbstractI2CSyncATtinyManager<MODE_, true, STATUS_HOOK_, false, I2C_DEBUG_HOOK>
335 {
336 using PARENT = AbstractI2CSyncATtinyManager<MODE_, true, STATUS_HOOK_, false, I2C_DEBUG_HOOK>;
337 public:
339 explicit I2CSyncStatusManager(STATUS_HOOK_ status_hook) : PARENT{status_hook} {}
341 };
342
354 template<I2CMode MODE_, typename DEBUG_HOOK_ = I2C_DEBUG_HOOK>
355 class I2CSyncDebugManager :
356 public AbstractI2CSyncATtinyManager<MODE_, false, I2C_STATUS_HOOK, true, DEBUG_HOOK_>
357 {
358 using PARENT = AbstractI2CSyncATtinyManager<MODE_, false, I2C_STATUS_HOOK, true, DEBUG_HOOK_>;
359 public:
361 explicit I2CSyncDebugManager(DEBUG_HOOK_ debug_hook) : PARENT{nullptr, debug_hook} {}
363 };
364
380 template<I2CMode MODE_, typename STATUS_HOOK_ = I2C_STATUS_HOOK, typename DEBUG_HOOK_ = I2C_DEBUG_HOOK>
381 class I2CSyncStatusDebugManager :
382 public AbstractI2CSyncATtinyManager<MODE_, true, STATUS_HOOK_, true, DEBUG_HOOK_>
383 {
384 using PARENT = AbstractI2CSyncATtinyManager<MODE_, true, STATUS_HOOK_, true, DEBUG_HOOK_>;
385 public:
387 explicit I2CSyncStatusDebugManager(STATUS_HOOK_ status_hook, DEBUG_HOOK_ debug_hook)
388 : PARENT{status_hook, debug_hook} {}
390 };
391}
392
393#endif /* I2C_HANDLER_ATTINY_HH */
Useful bits manipulation utilities.
Base class for all FakeFutures.
Definition: future.h:1142
Actual FakeFuture, it has the exact same API as Future and can be used in lieu of Future.
Definition: future.h:1300
Abstract synchronous I2C Manager for ATtiny architecture.
Abstract synchronous I2C Manager for all MCU architectures.
future::AbstractFakeFuture ABSTRACT_FUTURE
The abstract base class of all futures to be defined for this I2C Manager.
Base class for all I2C devices.
Definition: i2c_device.h:84
Synchronous I2C Manager for ATmega architecture.
Utility API to handle the concept of futures.
I2C API common definitions.
Common I2C Manager API.
static constexpr uint8_t CBV8(uint8_t bit)
Create a uint8_t inverted bitmask for the given bit number.
Definition: bits.h:137
static constexpr uint8_t BV8(uint8_t bit)
Create a uint8_t bitmask for the given bit number.
Definition: bits.h:41
Define API to define and manage I2C devices.
Definition: i2c.h:51
General utilities API that have broad application in programs.