-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathWireUtils.hpp
More file actions
175 lines (159 loc) · 6.03 KB
/
WireUtils.hpp
File metadata and controls
175 lines (159 loc) · 6.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
/*
* WireUtils.hpp
*
* Assumes, that Wire timeout is enabled e.g. with:
* Wire.setWireTimeout(); // Sets default timeout of 25 ms.
*
* Copyright (C) 2023 Armin Joachimsmeyer
* Email: [email protected]
*
* This file is part of Arduino-Utils https://github.com/ArminJo/Arduino-Utils.
*
* Arduino-Utils is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/gpl.html>.
*
*/
#include <Arduino.h>
#include <Wire.h>
#ifndef _WIRE_UTILS_HPP
#define _WIRE_UTILS_HPP
void printWireError(Print *aSerial, uint8_t aWireReturnCode);
#define I2C_SCAN_NO_DEVICE -1
#define I2C_SCAN_TIMEOUT -5
#define I2C_SCAN_FIRST_ERROR_CODE I2C_SCAN_NO_DEVICE
int8_t scanForAttachedI2CDevice(Print *aSerial, uint8_t aI2CAddressToStartWith = 0, uint8_t aNumberOfAddresseToScan = 0);
bool checkForAttachedI2CDevice(Print *aSerial, uint8_t aI2CDeviceAddress);
unsigned int sScanCount = 0;
uint8_t sGlobalWireReturnCode; // global return code / error flag set by all endTransmission() commands
uint8_t writeI2CByte(uint8_t aI2CDeviceAddress, uint8_t aByteToWrite) {
Wire.beginTransmission(aI2CDeviceAddress);
Wire.write(aByteToWrite);
sGlobalWireReturnCode = Wire.endTransmission(); // send stop
return sGlobalWireReturnCode;
}
uint16_t readI2CWordLSBFirst(uint8_t aI2CDeviceAddress) {
Wire.requestFrom(aI2CDeviceAddress, (uint8_t) 2);
uint8_t tLSB = Wire.read();
uint8_t tMSB = Wire.read();
return (uint16_t) tLSB | (((uint16_t) tMSB) << 8);
}
uint16_t readI2CWordMSBFirst(uint8_t aI2CDeviceAddress) {
Wire.requestFrom(aI2CDeviceAddress, (uint8_t) 2);
uint8_t tMSB = Wire.read();
uint8_t tLSB = Wire.read();
return (uint16_t) tLSB | (((uint16_t) tMSB) << 8);
}
/*
* Output 0 .. success
* 1 .. length to long for buffer
* 2 .. address send, NACK received
* 3 .. data send, NACK received
* 4 .. other twi error (lost bus arbitration, bus error, ..)
* 5 .. timeout
* @return true if error happened
*/
void printWireError(Print *aSerial, uint8_t aWireReturnCode) {
switch (aWireReturnCode) {
case 1: // too long for transmit buffer
aSerial->print(F("Too long for transmit buffer"));
break;
case 2: // received NACK on transmit of address
aSerial->print(F("Address sent, NACK received. Device not connected?"));
break;
case 3: // received NACK on transmit of data
aSerial->print(F("Address sent, NACK received."));
break;
case 4: // other error
aSerial->print(F("Other error"));
break;
case 5: // other error
aSerial->print(F("Timeout while waiting until twi is ready"));
break;
}
}
/*
* Check if I2C device with given address is attached.
* @return true if attached device found
*/
bool checkForAttachedI2CDevice(Print *aSerial, uint8_t aI2CDeviceAddress) {
do {
Wire.beginTransmission(aI2CDeviceAddress);
if (Wire.getWireTimeoutFlag()) {
aSerial->println(F("Timeout accessing I2C bus. Wait for bus becoming available"));
Wire.clearWireTimeoutFlag();
delay(100);
} else {
break;
}
} while (true);
sGlobalWireReturnCode = Wire.endTransmission();
bool tRetCode;
if (sGlobalWireReturnCode == 0) {
aSerial->print(F("Found attached"));
tRetCode = true;
} else {
aSerial->print(F("Transmission error code: \""));
aSerial->print(sGlobalWireReturnCode);
aSerial->print(F(" | "));
printWireError(&Serial, sGlobalWireReturnCode);
aSerial->print(F("\", while checking for"));
tRetCode = false;
}
aSerial->print(F(" I2C device at address 0x"));
aSerial->println(aI2CDeviceAddress, HEX);
aSerial->flush();
return tRetCode;
}
/*
* Scans address 0 to 127
* @param aNumberOfAddresseToScan - 0 means scan all I2C addresses
* @return I2C address from 0 to 127, where transmission was successful
* -1, I2C_SCAN_NO_DEVICE
* -5, I2C_SCAN_TIMEOUT
*/
int8_t scanForAttachedI2CDevice(Print *aSerial, uint8_t aI2CAddressToStartWith, uint8_t aNumberOfAddresseToScan) {
// the next 2 statements disable TWI hangup, if SDA and SCL are connected and disconnected from ground.
#if defined(TWCR)
TWCR = 0;
#endif
Wire.begin();
auto tStartMillis = millis();
if (aNumberOfAddresseToScan == 0) {
aNumberOfAddresseToScan = 128; // 0 means scan all I2C addresses
}
uint8_t tI2CAddress = aI2CAddressToStartWith;
// We cannot use uint_fast8_t here, since it is ambiguous parameter for beginTransmission() on 16/32 bit CPU
for (uint_fast8_t i = 0; i < aNumberOfAddresseToScan; i++) {
Wire.beginTransmission(tI2CAddress);
uint8_t tOK = Wire.endTransmission(true);
if (tOK == 0) {
aSerial->print(F("Found I2C device attached at address 0x"));
aSerial->println(tI2CAddress, HEX);
return tI2CAddress;
}
tI2CAddress++;
}
sScanCount++;
if (millis() - tStartMillis > 2000) {
aSerial->print(F("I2C Scan timeout. It seems that at least one of SCA or SCL is connected to ground"));
return I2C_SCAN_TIMEOUT;
}
aSerial->print(F("Scan of "));
aSerial->print(aNumberOfAddresseToScan);
aSerial->print(F(" addresses starting at 0x"));
aSerial->print(aI2CAddressToStartWith);
aSerial->print(F(" found no attached I2C device. Count="));
aSerial->println(sScanCount);
return I2C_SCAN_NO_DEVICE;
}
#endif // _WIRE_UTILS_HPP