AceWire  0.4.1
Unified interface for selecting different I2C implementations on Arduino platforms
SimpleWireFastInterface.h
1 /*
2 MIT License
3 
4 Copyright (c) 2021 Brian T. Park
5 
6 Permission is hereby granted, free of charge, to any person obtaining a copy
7 of this software and associated documentation files (the "Software"), to deal
8 in the Software without restriction, including without limitation the rights
9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 copies of the Software, and to permit persons to whom the Software is
11 furnished to do so, subject to the following conditions:
12 
13 The above copyright notice and this permission notice shall be included in all
14 copies or substantial portions of the Software.
15 
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 SOFTWARE.
23 */
24 
25 #ifndef ACE_WIRE_SIMPLE_WIRE_FAST_INTERFACE_H
26 #define ACE_WIRE_SIMPLE_WIRE_FAST_INTERFACE_H
27 
28 #include <stdint.h>
29 #include <Arduino.h> // delayMicroseconds()
30 
31 namespace ace_wire {
32 
47 template <
48  uint8_t T_DATA_PIN,
49  uint8_t T_CLOCK_PIN,
50  uint8_t T_DELAY_MICROS
51 >
53  public:
55  explicit SimpleWireFastInterface() = default;
56 
66  void begin() const {
67  digitalWriteFast(T_CLOCK_PIN, LOW);
68  digitalWriteFast(T_DATA_PIN, LOW);
69 
70  // Begin with both lines in INPUT mode to passively go HIGH.
71  clockHigh();
72  dataHigh();
73  }
74 
76  void end() const {
77  clockHigh();
78  dataHigh();
79  }
80 
87  uint8_t beginTransmission(uint8_t addr) const {
88  clockHigh();
89  dataHigh();
90 
91  dataLow();
92  clockLow();
93 
94  // Send I2C addr (7 bits) and the R/W bit set to "write" (0x00).
95  uint8_t effectiveAddr = (addr << 1) | 0x00;
96  uint8_t res = write(effectiveAddr);
97  return res ^ 0x1;
98  }
99 
110  uint8_t write(uint8_t data) const {
111  for (uint8_t i = 0; i < 8; ++i) {
112  if (data & 0x80) {
113  dataHigh();
114  } else {
115  dataLow();
116  }
117  clockHigh();
118  // An extra bitDelay() here would make the HIGH and LOW states symmetric
119  // in duration (if digitalWriteFast() is assumed to be infinitely fast).
120  // But actual devices that I have tested seem to support the absence of
121  // that extra delay. So let's ignore it to make the transfer speed
122  // faster.
123  clockLow();
124  data <<= 1;
125  }
126 
127  uint8_t ack = readAck();
128  return ack ^ 0x1;
129  }
130 
136  uint8_t endTransmission(bool sendStop = true) const {
137  // clock will always be LOW when this is called
138  if (sendStop) {
139  dataLow();
140  clockHigh();
141  dataHigh();
142  }
143 
144  return 0;
145  }
146 
154  uint8_t requestFrom(
155  uint8_t addr, uint8_t quantity, bool sendStop = true) const {
156  mQuantity = quantity;
157  mSendStop = sendStop;
158 
159  clockHigh();
160  dataHigh();
161 
162  dataLow();
163  clockLow();
164 
165  // Send I2C addr (7 bits) and the R/W bit set to "read" (0x01).
166  uint8_t effectiveAddr = (addr << 1) | 0x01;
167  uint8_t status = write(effectiveAddr);
168 
169  return (status == 0) ? 0 : quantity;
170  }
171 
185  uint8_t read() const {
186  // Caller should not call when mQuantity is 0, but let's guard against it.
187  if (! mQuantity) return 0xff;
188 
189  // Read one byte
190  dataHigh();
191  uint8_t data = 0;
192  for (uint8_t i = 0; i < 8; ++i) {
193  clockHigh();
194  data <<= 1;
195  uint8_t bit = digitalReadFast(T_DATA_PIN);
196  data |= (bit & 0x1);
197  clockLow();
198  }
199 
200  // Decrement quantity to determine if NACK or ACK should be sent.
201  mQuantity--;
202  if (mQuantity) {
203  sendAck();
204  } else {
205  sendNack();
206  endTransmission(mSendStop);
207  }
208 
209  return data;
210  }
211 
212  // Use default copy constructor and assignment operator.
215  default;
216 
217  private:
224  static uint8_t readAck() {
225  // Go into INPUT mode, reusing dataHigh(), saving 10 flash bytes on AVR.
226  dataHigh();
227 
228  // Set the clock HIGH, because the I2C protocol says that SDA will not
229  // change when SCL is HIGH and we expect the slave to abide by that.
230  clockHigh();
231 
232  uint8_t ack = digitalReadFast(T_DATA_PIN);
233 
234  // Device releases SDA upon falling edge of the 9th CLK.
235  clockLow();
236  return ack;
237  }
238 
240  static void sendAck() {
241  dataLow();
242  clockHigh();
243  clockLow();
244  }
245 
247  static void sendNack() {
248  dataHigh();
249  clockHigh();
250  clockLow();
251  }
252 
253  static void bitDelay() { delayMicroseconds(T_DELAY_MICROS); }
254 
255  static void clockHigh() { pinModeFast(T_CLOCK_PIN, INPUT); bitDelay(); }
256 
257  static void clockLow() { pinModeFast(T_CLOCK_PIN, OUTPUT); bitDelay(); }
258 
259  static void dataHigh() { pinModeFast(T_DATA_PIN, INPUT); bitDelay(); }
260 
261  static void dataLow() { pinModeFast(T_DATA_PIN, OUTPUT); bitDelay(); }
262 
263  private:
264  mutable bool mSendStop;
265  mutable uint8_t mQuantity;
266 };
267 
268 }
269 
270 #endif
ace_wire::SimpleWireFastInterface::beginTransmission
uint8_t beginTransmission(uint8_t addr) const
Send I2C START condition.
Definition: SimpleWireFastInterface.h:87
ace_wire::SimpleWireFastInterface::read
uint8_t read() const
Read byte.
Definition: SimpleWireFastInterface.h:185
ace_wire::SimpleWireFastInterface::begin
void begin() const
Initialize the clock and data pins.
Definition: SimpleWireFastInterface.h:66
ace_wire::SimpleWireFastInterface::write
uint8_t write(uint8_t data) const
Send the data byte on the data bus, with MSB first as specified by I2C.
Definition: SimpleWireFastInterface.h:110
ace_wire::SimpleWireFastInterface::endTransmission
uint8_t endTransmission(bool sendStop=true) const
Send the I2C STOP condition.
Definition: SimpleWireFastInterface.h:136
ace_wire::SimpleWireFastInterface
A version of SimpleWireInterface that uses one of the <digitalWriteFast.h> libraries.
Definition: SimpleWireFastInterface.h:52
ace_wire::SimpleWireFastInterface::SimpleWireFastInterface
SimpleWireFastInterface()=default
Constructor.
ace_wire::SimpleWireFastInterface::end
void end() const
Set clock and data pins to INPUT mode.
Definition: SimpleWireFastInterface.h:76
ace_wire::SimpleWireFastInterface::requestFrom
uint8_t requestFrom(uint8_t addr, uint8_t quantity, bool sendStop=true) const
Prepare to read bytes by sending I2C START condition.
Definition: SimpleWireFastInterface.h:154