AceWire  0.4.0
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 
58  public:
73  uint8_t dataPin, uint8_t clockPin, uint8_t delayMicros
74  ) :
75  mDataPin(dataPin),
76  mClockPin(clockPin),
77  mDelayMicros(delayMicros)
78  {}
79 
88  void begin() const {
89  digitalWrite(mClockPin, LOW);
90  digitalWrite(mDataPin, LOW);
91 
92  // Begin with both lines in INPUT mode to passively go HIGH.
93  clockHigh();
94  dataHigh();
95  }
96 
98  void end() const {
99  clockHigh();
100  dataHigh();
101  }
102 
109  uint8_t beginTransmission(uint8_t addr) const {
110  clockHigh();
111  dataHigh();
112 
113  dataLow();
114  clockLow();
115 
116  // Send I2C addr (7 bits) and the R/W bit set to "write" (0x00).
117  uint8_t effectiveAddr = (addr << 1) | 0x00;
118  uint8_t res = write(effectiveAddr);
119  return res ^ 0x1;
120  }
121 
132  uint8_t write(uint8_t data) const {
133  for (uint8_t i = 0; i < 8; ++i) {
134  if (data & 0x80) {
135  dataHigh();
136  } else {
137  dataLow();
138  }
139  clockHigh();
140  // An extra bitDelay() here would make the HIGH and LOW states symmetric
141  // in duration (if digitalWrite() is assumed to be infinitely fast,
142  // which it is definitely not). But actual devices that I have tested
143  // seem to support the absence of that extra delay. So let's ignore it
144  // to make the transfer speed faster.
145  clockLow();
146  data <<= 1;
147  }
148 
149  uint8_t ack = readAck();
150  return ack ^ 0x1;
151  }
152 
158  uint8_t endTransmission(bool sendStop = true) const {
159  // clock will always be LOW when this is called
160  if (sendStop) {
161  dataLow();
162  clockHigh();
163  dataHigh();
164  }
165 
166  return 0;
167  }
168 
176  uint8_t requestFrom(
177  uint8_t addr, uint8_t quantity, bool sendStop = true) const {
178  mQuantity = quantity;
179  mSendStop = sendStop;
180 
181  clockHigh();
182  dataHigh();
183 
184  dataLow();
185  clockLow();
186 
187  // Send I2C addr (7 bits) and the R/W bit set to "read" (0x01).
188  uint8_t effectiveAddr = (addr << 1) | 0x01;
189  uint8_t status = write(effectiveAddr);
190 
191  return (status == 0) ? 0 : quantity;
192  }
193 
207  uint8_t read() const {
208  // Caller should not call when mQuantity is 0, but let's guard against it.
209  if (! mQuantity) return 0xff;
210 
211  // Read one byte
212  dataHigh();
213  uint8_t data = 0;
214  for (uint8_t i = 0; i < 8; ++i) {
215  clockHigh();
216  data <<= 1;
217  uint8_t bit = digitalRead(mDataPin);
218  data |= (bit & 0x1);
219  clockLow();
220  }
221 
222  // Decrement quantity to determine if NACK or ACK should be sent.
223  mQuantity--;
224  if (mQuantity) {
225  sendAck();
226  } else {
227  sendNack();
228  endTransmission(mSendStop);
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 
289  mutable uint8_t mQuantity;
290  mutable bool mSendStop;
291 };
292 
293 }
294 
295 #endif
ace_wire::SimpleWireInterface::read
uint8_t read() const
Read byte.
Definition: SimpleWireInterface.h:207
ace_wire::SimpleWireInterface::requestFrom
uint8_t requestFrom(uint8_t addr, uint8_t quantity, bool sendStop=true) const
Prepare to read bytes by sending I2C START condition.
Definition: SimpleWireInterface.h:176
ace_wire::SimpleWireInterface::end
void end() const
Set clock and data pins to INPUT mode.
Definition: SimpleWireInterface.h:98
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:132
ace_wire::SimpleWireInterface::endTransmission
uint8_t endTransmission(bool sendStop=true) const
Send the I2C STOP condition.
Definition: SimpleWireInterface.h:158
ace_wire::SimpleWireInterface::beginTransmission
uint8_t beginTransmission(uint8_t addr) const
Send the I2C START condition.
Definition: SimpleWireInterface.h:109
ace_wire::SimpleWireInterface
A software I2C implementation for sending LED segment patterns over I2C.
Definition: SimpleWireInterface.h:57
ace_wire::SimpleWireInterface::SimpleWireInterface
SimpleWireInterface(uint8_t dataPin, uint8_t clockPin, uint8_t delayMicros)
Constructor.
Definition: SimpleWireInterface.h:72
ace_wire::SimpleWireInterface::begin
void begin() const
Initialize the clock and data pins.
Definition: SimpleWireInterface.h:88