AceWire  0.3.2
Unified interface for selecting different I2C implementations on Arduino platforms
SimpleWireInterface.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_INTERFACE_H
26 #define ACE_WIRE_SIMPLE_WIRE_INTERFACE_H
27 
28 #include <stdint.h>
29 #include <Arduino.h> // pinMode(), digitalWrite()
30 
31 namespace ace_wire {
32 
57  public:
72  uint8_t dataPin, uint8_t clockPin, uint8_t delayMicros
73  ) :
74  mDataPin(dataPin),
75  mClockPin(clockPin),
76  mDelayMicros(delayMicros)
77  {}
78 
87  void begin() const {
88  digitalWrite(mClockPin, LOW);
89  digitalWrite(mDataPin, LOW);
90 
91  // Begin with both lines in INPUT mode to passively go HIGH.
92  clockHigh();
93  dataHigh();
94  }
95 
97  void end() const {
98  clockHigh();
99  dataHigh();
100  }
101 
108  uint8_t beginTransmission(uint8_t addr) const {
109  clockHigh();
110  dataHigh();
111 
112  dataLow();
113  clockLow();
114 
115  // Send I2C addr (7 bits) and the R/W bit set to "write" (0x00).
116  uint8_t effectiveAddr = (addr << 1) | 0x00;
117  uint8_t res = write(effectiveAddr);
118  return res ^ 0x1;
119  }
120 
131  uint8_t write(uint8_t data) const {
132  for (uint8_t i = 0; i < 8; ++i) {
133  if (data & 0x80) {
134  dataHigh();
135  } else {
136  dataLow();
137  }
138  clockHigh();
139  // An extra bitDelay() here would make the HIGH and LOW states symmetric
140  // in duration (if digitalWrite() is assumed to be infinitely fast,
141  // which it is definitely not). But actual devices that I have tested
142  // seem to support the absence of that extra delay. So let's ignore it
143  // to make the transfer speed faster.
144  clockLow();
145  data <<= 1;
146  }
147 
148  uint8_t ack = readAck();
149  return ack ^ 0x1;
150  }
151 
157  uint8_t endTransmission(bool sendStop = true) const {
158  // clock will always be LOW when this is called
159  if (sendStop) {
160  dataLow();
161  clockHigh();
162  dataHigh();
163  }
164 
165  return 0;
166  }
167 
175  uint8_t requestFrom(uint8_t addr, uint8_t quantity, bool sendStop = true) {
176  mQuantity = quantity;
177  mSendStop = sendStop;
178 
179  clockHigh();
180  dataHigh();
181 
182  dataLow();
183  clockLow();
184 
185  // Send I2C addr (7 bits) and the R/W bit set to "read" (0x01).
186  uint8_t effectiveAddr = (addr << 1) | 0x01;
187  uint8_t ack = write(effectiveAddr);
188 
189  return (ack == 1) ? quantity : 0;
190  }
191 
205  uint8_t read() {
206  // Caller should not call when mQuantity is 0, but guard against it.
207  if (! mQuantity) return 0xff;
208 
209  // Read one byte
210  dataHigh();
211  uint8_t data = 0;
212  for (uint8_t i = 0; i < 8; ++i) {
213  clockHigh();
214  data <<= 1;
215  uint8_t bit = digitalRead(mDataPin);
216  data |= (bit & 0x1);
217  clockLow();
218  }
219 
220  // Decrement quantity to determine if NACK or ACK should be sent.
221  mQuantity--;
222  if (mQuantity) {
223  sendAck();
224  } else {
225  sendNack();
226  if (mSendStop) {
227  endTransmission();
228  }
229  }
230 
231  return data;
232  }
233 
234  // Use default copy constructor and assignment operator.
235  SimpleWireInterface(const SimpleWireInterface&) = default;
236  SimpleWireInterface& operator=(const SimpleWireInterface&) = default;
237 
238  private:
245  uint8_t readAck() const {
246  // Go into INPUT mode, reusing dataHigh(), saving 10 flash bytes on AVR.
247  dataHigh();
248 
249  // Set the clock HIGH, because the I2C protocol says that SDA will not
250  // change when SCL is HIGH and we expect the slave to abide by that.
251  clockHigh();
252 
253  uint8_t ack = digitalRead(mDataPin);
254 
255  // Device releases SDA upon falling edge of the 9th CLK.
256  clockLow();
257  return ack;
258  }
259 
261  void sendAck() const {
262  dataLow();
263  clockHigh();
264  clockLow();
265  }
266 
268  void sendNack() const {
269  dataHigh();
270  clockHigh();
271  clockLow();
272  }
273 
274  void bitDelay() const { delayMicroseconds(mDelayMicros); }
275 
276  void clockHigh() const { pinMode(mClockPin, INPUT); bitDelay(); }
277 
278  void clockLow() const { pinMode(mClockPin, OUTPUT); bitDelay(); }
279 
280  void dataHigh() const { pinMode(mDataPin, INPUT); bitDelay(); }
281 
282  void dataLow() const { pinMode(mDataPin, OUTPUT); bitDelay(); }
283 
284  private:
285  uint8_t const mDataPin;
286  uint8_t const mClockPin;
287  uint8_t const mDelayMicros;
288  uint8_t mQuantity;
289  bool mSendStop;
290 };
291 
292 }
293 
294 #endif
ace_wire::SimpleWireInterface::end
void end() const
Set clock and data pins to INPUT mode.
Definition: SimpleWireInterface.h:97
ace_wire::SimpleWireInterface::requestFrom
uint8_t requestFrom(uint8_t addr, uint8_t quantity, bool sendStop=true)
Prepare to read bytes by sending I2C START condition.
Definition: SimpleWireInterface.h:175
ace_wire::SimpleWireInterface::read
uint8_t read()
Read byte.
Definition: SimpleWireInterface.h:205
ace_wire::SimpleWireInterface::write
uint8_t write(uint8_t data) const
Send the data byte on the data bus, with MSB first as specified by I2C.
Definition: SimpleWireInterface.h:131
ace_wire::SimpleWireInterface::endTransmission
uint8_t endTransmission(bool sendStop=true) const
Send the I2C STOP condition.
Definition: SimpleWireInterface.h:157
ace_wire::SimpleWireInterface::beginTransmission
uint8_t beginTransmission(uint8_t addr) const
Send the I2C START condition.
Definition: SimpleWireInterface.h:108
ace_wire::SimpleWireInterface
A software I2C implementation for sending LED segment patterns over I2C.
Definition: SimpleWireInterface.h:56
ace_wire::SimpleWireInterface::SimpleWireInterface
SimpleWireInterface(uint8_t dataPin, uint8_t clockPin, uint8_t delayMicros)
Constructor.
Definition: SimpleWireInterface.h:71
ace_wire::SimpleWireInterface::begin
void begin() const
Initialize the clock and data pins.
Definition: SimpleWireInterface.h:87