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
56 class SimpleWireInterface {
57 public:
71 explicit SimpleWireInterface(
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