AceWire  0.3.2
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(uint8_t addr, uint8_t quantity, bool sendStop = true) {
155  mQuantity = quantity;
156  mSendStop = sendStop;
157 
158  clockHigh();
159  dataHigh();
160 
161  dataLow();
162  clockLow();
163 
164  // Send I2C addr (7 bits) and the R/W bit set to "read" (0x01).
165  uint8_t effectiveAddr = (addr << 1) | 0x01;
166  uint8_t ack = write(effectiveAddr);
167 
168  return (ack == 1) ? quantity : 0;
169  }
170 
184  uint8_t read() {
185  // Caller should not call when mQuantity is 0, but guard against it.
186  if (! mQuantity) return 0xff;
187 
188  // Read one byte
189  dataHigh();
190  uint8_t data = 0;
191  for (uint8_t i = 0; i < 8; ++i) {
192  clockHigh();
193  data <<= 1;
194  uint8_t bit = digitalReadFast(T_DATA_PIN);
195  data |= (bit & 0x1);
196  clockLow();
197  }
198 
199  // Decrement quantity to determine if NACK or ACK should be sent.
200  mQuantity--;
201  if (mQuantity) {
202  sendAck();
203  } else {
204  sendNack();
205  if (mSendStop) {
206  endTransmission();
207  }
208  }
209 
210  return data;
211  }
212 
213  // Use default copy constructor and assignment operator.
216  default;
217 
218  private:
225  static uint8_t readAck() {
226  // Go into INPUT mode, reusing dataHigh(), saving 10 flash bytes on AVR.
227  dataHigh();
228 
229  // Set the clock HIGH, because the I2C protocol says that SDA will not
230  // change when SCL is HIGH and we expect the slave to abide by that.
231  clockHigh();
232 
233  uint8_t ack = digitalReadFast(T_DATA_PIN);
234 
235  // Device releases SDA upon falling edge of the 9th CLK.
236  clockLow();
237  return ack;
238  }
239 
241  static void sendAck() {
242  dataLow();
243  clockHigh();
244  clockLow();
245  }
246 
248  static void sendNack() {
249  dataHigh();
250  clockHigh();
251  clockLow();
252  }
253 
254  static void bitDelay() { delayMicroseconds(T_DELAY_MICROS); }
255 
256  static void clockHigh() { pinModeFast(T_CLOCK_PIN, INPUT); bitDelay(); }
257 
258  static void clockLow() { pinModeFast(T_CLOCK_PIN, OUTPUT); bitDelay(); }
259 
260  static void dataHigh() { pinModeFast(T_DATA_PIN, INPUT); bitDelay(); }
261 
262  static void dataLow() { pinModeFast(T_DATA_PIN, OUTPUT); bitDelay(); }
263 
264  private:
265  bool mSendStop;
266  uint8_t mQuantity;
267 };
268 
269 }
270 
271 #endif
ace_wire::SimpleWireFastInterface::beginTransmission
uint8_t beginTransmission(uint8_t addr) const
Send I2C START condition.
Definition: SimpleWireFastInterface.h:87
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::requestFrom
uint8_t requestFrom(uint8_t addr, uint8_t quantity, bool sendStop=true)
Prepare to read bytes by sending I2C START condition.
Definition: SimpleWireFastInterface.h:154
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::read
uint8_t read()
Read byte.
Definition: SimpleWireFastInterface.h:184
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