AceWire  0.2.0
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 
85  void beginTransmission(uint8_t addr) const {
86  clockHigh();
87  dataHigh();
88 
89  dataLow();
90  clockLow();
91 
92  // Send I2C addr (7 bits) and the R/W bit set to "write" (0x00).
93  uint8_t effectiveAddr = (addr << 1) | 0x00;
94  write(effectiveAddr);
95  }
96 
107  uint8_t write(uint8_t data) const {
108  for (uint8_t i = 0; i < 8; ++i) {
109  if (data & 0x80) {
110  dataHigh();
111  } else {
112  dataLow();
113  }
114  clockHigh();
115  clockLow();
116  data <<= 1;
117  }
118 
119  return readAck();
120  }
121 
123  void endTransmission() const {
124  // clock will always be LOW when this is called
125  dataLow();
126  clockHigh();
127  dataHigh();
128  }
129 
131  uint8_t requestFrom(uint8_t addr, uint8_t quantity, bool stop = true) {
132  mQuantity = quantity;
133  mStop = stop;
134 
135  clockHigh();
136  dataHigh();
137 
138  dataLow();
139  clockLow();
140 
141  // Send I2C addr (7 bits) and the R/W bit set to "read" (0x01).
142  uint8_t effectiveAddr = (addr << 1) | 0x01;
143  write(effectiveAddr);
144 
145  return quantity;
146  }
147 
154  uint8_t read() {
155  // Read one byte
156  dataHigh();
157  uint8_t data = 0;
158  for (uint8_t i = 0; i < 8; ++i) {
159  clockHigh();
160  data <<= 1;
161  uint8_t bit = digitalReadFast(T_DATA_PIN);
162  data |= (bit & 0x1);
163  clockLow();
164  }
165 
166  // Decrement quantity to determine if NACK or ACK should be sent.
167  mQuantity--;
168  if (mQuantity) {
169  sendAck();
170  } else {
171  sendNack();
172  }
173 
174  return data;
175  }
176 
178  void endRequest() {
179  if (mStop) {
180  endTransmission();
181  }
182  }
183 
184  // Use default copy constructor and assignment operator.
187  default;
188 
189  private:
194  static uint8_t readAck() {
195  // Go into INPUT mode, reusing dataHigh(), saving 10 flash bytes on AVR.
196  dataHigh();
197  uint8_t ack = digitalReadFast(T_DATA_PIN);
198 
199  // Device releases SDA upon falling edge of the 9th CLK.
200  clockHigh();
201  clockLow();
202  return ack;
203  }
204 
206  static void sendAck() {
207  dataLow();
208  clockHigh();
209  clockLow();
210  }
211 
213  static void sendNack() {
214  dataHigh();
215  clockHigh();
216  clockLow();
217  }
218 
219  static void bitDelay() { delayMicroseconds(T_DELAY_MICROS); }
220 
221  static void clockHigh() { pinModeFast(T_CLOCK_PIN, INPUT); bitDelay(); }
222 
223  static void clockLow() { pinModeFast(T_CLOCK_PIN, OUTPUT); bitDelay(); }
224 
225  static void dataHigh() { pinModeFast(T_DATA_PIN, INPUT); bitDelay(); }
226 
227  static void dataLow() { pinModeFast(T_DATA_PIN, OUTPUT); bitDelay(); }
228 
229  private:
230  bool mStop;
231  uint8_t mQuantity;
232 };
233 
234 }
235 
236 #endif
ace_wire::SimpleWireFastInterface::beginTransmission
void beginTransmission(uint8_t addr) const
Send I2C START condition.
Definition: SimpleWireFastInterface.h:85
ace_wire::SimpleWireFastInterface::begin
void begin() const
Initialize the clock and data pins.
Definition: SimpleWireFastInterface.h:66
ace_wire::SimpleWireFastInterface::endRequest
void endRequest()
End requestFrom() by sending I2C STOP condition if 'stop' is 'true'.
Definition: SimpleWireFastInterface.h:178
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:107
ace_wire::SimpleWireFastInterface
A version of SimpleWireInterface that uses one of the <digitalWriteFast.h> libraries.
Definition: SimpleWireFastInterface.h:52
ace_wire::SimpleWireFastInterface::requestFrom
uint8_t requestFrom(uint8_t addr, uint8_t quantity, bool stop=true)
Prepare to read bytes by sending I2C START condition.
Definition: SimpleWireFastInterface.h:131
ace_wire::SimpleWireFastInterface::read
uint8_t read()
Read byte.
Definition: SimpleWireFastInterface.h:154
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::endTransmission
void endTransmission() const
Send I2C STOP condition.
Definition: SimpleWireFastInterface.h:123