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 >
52 class SimpleWireFastInterface {
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.
214 SimpleWireFastInterface(const SimpleWireFastInterface&) = default;
215 SimpleWireFastInterface& operator=(const SimpleWireFastInterface&) =
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