29#include <avr/interrupt.h>
32#include <util/delay.h>
45#define I2C_FREQUENCY 100000
46#elif I2C_FREQUENCY > 200000
47#error I2C_FREQUENCY is too high.
50#ifndef I2C_BUFFER_CAPACITY
57#define I2C_BUFFER_CAPACITY 32
58#elif I2C_BUFFER_CAPACITY > 255
59#error I2C_BUFFER_CAPACITY is too big.
62#ifndef I2C_HANDSHAKE_BUSY_CHECKS
69#define I2C_HANDSHAKE_BUSY_CHECKS 128
70#elif I2C_HANDSHAKE_BUSY_CHECKS > 65535
71#error I2C_HANDSHAKE_BUSY_CHECKS is too big.
74#ifndef I2C_CHECK_CABLE_FLIPPED_CHECKS
80#define I2C_CHECK_CABLE_FLIPPED_CHECKS 128
81#elif I2C_CHECK_CABLE_FLIPPED_CHECKS > 65535
82#error I2C_CHECK_CABLE_FLIPPED_CHECKS is too big.
85#ifndef I2C_CHECK_CABLE_FLIPPED_DEBOUNCE_CHECKS
92#define I2C_CHECK_CABLE_FLIPPED_DEBOUNCE_CHECKS 128
93#elif I2C_CHECK_CABLE_FLIPPED_DEBOUNCE_CHECKS > 65535
94#error I2C_CHECK_CABLE_FLIPPED_DEBOUNCE_CHECKS is too big.
103#define I2C_SDA_BIT PIND1
112#define I2C_SCL_BIT PIND0
134#define I2C_PORT PORTD
151#define I2C_VERSION_MAJOR 3
156#define I2C_VERSION_MINOR 0
161#define I2C_VERSION_PATCH 0
168#define I2C_VERSION (I2C_VERSION_MAJOR * 10000 + I2C_VERSION_MINOR * 100 + I2C_VERSION_PATCH)
174namespace i2c_detail {
176template <
typename T>
struct is_pointer {
static const bool value =
false; };
177template <
typename T>
struct is_pointer<T *> {
static const bool value =
true; };
272 static void write(uint8_t address,
const void *buffer, uint8_t size,
I2C::Mode mode);
286 static_assert(!i2c_detail::is_pointer<T>::value,
"T cannot be a pointer.");
287 static_assert(
sizeof(T) <=
I2C_BUFFER_CAPACITY,
"Size of T must be less than or equal to I2C_BUFFER_CAPACITY.");
288 I2C::write(address, (
const void *)&
object,
sizeof(T), mode);
302 template<
typename T,
size_t N>
305 static_assert(
sizeof(T) * N <=
I2C_BUFFER_CAPACITY,
"Size of T * N must be less than or equal to I2C_BUFFER_CAPACITY.");
306 I2C::write(address, (
const void *)buffer,
sizeof(T) * N, mode);
319 static void read(uint8_t address,
void *buffer, uint8_t size);
331 static void read(uint8_t address, T &
object) {
332 static_assert(!i2c_detail::is_pointer<T>::value,
"T cannot be a pointer.");
333 static_assert(
sizeof(T) < 255,
"Size of T must be less than 255.");
334 I2C::read(address, (
void *)&
object,
sizeof(T));
347 template<
typename T,
size_t N>
348 static void read(uint8_t address, T (&buffer)[N]) {
350 static_assert(
sizeof(T) * N < 255,
"Size of T * N must be less than 255.");
351 I2C::read(address, (
void *)buffer,
sizeof(T) * N);
368 static void reply(
const void *buffer, uint8_t size);
377 template <
typename T>
378 static void reply(
const T &
object) {
379 static_assert(!i2c_detail::is_pointer<T>::value,
"T cannot be a pointer.");
380 static_assert(
sizeof(T) <=
I2C_BUFFER_CAPACITY,
"Size of T must be less than or equal to I2C_BUFFER_CAPACITY.");
392 template<
typename T,
size_t N>
393 static void reply(
const T (&buffer)[N]) {
395 static_assert(
sizeof(T) * N <=
I2C_BUFFER_CAPACITY,
"Size of T * N must be less than or equal to I2C_BUFFER_CAPACITY.");
396 I2C::reply((
const void *)buffer,
sizeof(T) * N);
498 static void checkCableFlipped(
void (*startFunction)() =
nullptr,
void (*loopFunction)() =
nullptr);
513#ifdef I2C_IMPLEMENTATION
518namespace i2c_detail {
523 void (*onRequestFunction)() =
nullptr;
528 void (*onReceiveFunction)() =
nullptr;
534 volatile uint8_t *readBuffer;
544 volatile uint8_t bufferIdx;
549 volatile uint8_t bufferSize;
555 volatile bool active;
560 volatile uint8_t slaRW;
572bool checkCableFlippedCore(
bool disconnectFlip) {
574 uint8_t sdaEdges = 0;
575 uint8_t sclEdges = 0;
581 uint8_t diff = cur ^ prev;
594 if (sdaEdges + sclEdges <= 2) {
596 return disconnectFlip;
599 return sdaEdges > sclEdges;
602void startReadWrite(uint8_t address,
bool readWrite, uint8_t bufferSize) {
603 i2c_detail::data.active =
true;
605 i2c_detail::data.slaRW = address << 1 | readWrite;
606 i2c_detail::data.bufferIdx = 0;
607 i2c_detail::data.bufferSize = bufferSize;
611 while (i2c_detail::data.active) { }
615 TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWSTA) | _BV(TWINT);
627 TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA);
659 i2c_detail::waitActive();
661 i2c_detail::startReadWrite(address, TW_WRITE, size);
662 memcpy(i2c_detail::data.twiBuffer, buffer, size);
664 i2c_detail::sendStart();
667 i2c_detail::waitActive();
671void I2C::read(uint8_t address,
void *buffer, uint8_t size) {
672 i2c_detail::waitActive();
676 i2c_detail::startReadWrite(address, TW_READ, size - 1);
677 i2c_detail::data.readBuffer = (uint8_t *)buffer;
679 i2c_detail::sendStart();
681 i2c_detail::waitActive();
684void I2C::reply(
const void *buffer, uint8_t size) {
686 memcpy(i2c_detail::data.twiBuffer, buffer, size);
687 i2c_detail::data.bufferSize = size;
691 i2c_detail::data.onRequestFunction = function;
695 i2c_detail::data.onReceiveFunction = function;
699 return i2c_detail::data.error;
703 return i2c_detail::data.twiBuffer;
709 return i2c_detail::data.bufferIdx;
713 return i2c_detail::data.active;
723 if (i2c_detail::checkCableFlippedCore(
false)) {
735 if (i2c_detail::checkCableFlippedCore(
true)) {
745 TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA);
769 uint8_t zeros = 0b00000000;
785ISR(TWI_vect, ISR_NAKED) {
788; --------------------- defines ----------------------- ;
802.equ REPLY_ACK, (1 << TWINT) | (1 << TWEN) | (1 << TWIE) | (1 << TWEA)
803.equ REPLY_NACK, (1 << TWINT) | (1 << TWEN) | (1 << TWIE)
804.equ STOP, (1 << TWINT) | (1 << TWEN) | (1 << TWIE) | (1 << TWSTO) | (1 << TWEA)
806; -------------------- registers ---------------------- ;
807; r18 - TWSR (never used after function call)
811; r26 (X) - general use
812; r27 (X) - general use
813; r28 (Y) - data pointer
814; r29 (Y) - data pointer
815; r30 (Z) - TW register pointer
816; r31 (Z) - TW register pointer
817; --------------------- prologue ---------------------- ;
821; save and restore call-clobbered registers
822; target (slave) could call function pointer
836; save and restore tmp and zero registers (could be used in function calls)
840; ----------------------------------------------------- ;
841; set up Y pointer (data)
845; set up Z pointer (TW registers)
849; set up r20 and r21 (REPLY_ACK and REPLY_NACK)
854ldd r18, Z + TWSR ; no mask needed because prescaler bits are cleared
871; 64 instruction limit on branches
875 ; TWDR = i2c_detail::data.slaRW;
876 ldd r26, Y + %[slaRW]
885 ; if (i2c_detail::data.bufferIdx >= i2c_detail::data.bufferSize) {
889 ldd r26, Y + %[bufferIdx]
890 ldd r27, Y + %[bufferSize]
893 brlo 1f ; 64 instruction limit on branches
897 ; TWDR = i2c_detail::data.twiBuffer[i2c_detail::data.bufferIdx++];
898 rcall get_buffer_addr
906; ----------------------------------------------------- ;
910 ; i2c_detail::data.readBuffer[i2c_detail::data.bufferIdx++] = TWDR;
911 ldd r19, Y + %[bufferIdx]
912 ldd r26, Y + %[readBuffer]
913 ldd r27, Y + %[readBuffer] + 1
916 adc r27, __zero_reg__
919 std Y + %[bufferIdx], r19
924 ; if (TWSR == TW_MR_DATA_NACK) {
931; ------------------ fallthrough ---------------------- ;
933 ; if (i2c_detail::data.bufferIdx < i2c_detail::data.bufferSize) {
940 ; r20 and r21 may be clobbered, do not use
942 ldd r26, Y + %[bufferIdx]
943 ldd r27, Y + %[bufferSize]
951; ----------------------------------------------------- ;
971 ; i2c_detail::data.bufferIdx = 0;
972 std Y + %[bufferIdx], __zero_reg__
979 ; i2c_detail::data.twiBuffer[i2c_detail::data.bufferIdx++] = TWDR;
980 rcall get_buffer_addr
992 ; if (i2c_detail::data.onReceiveFunction) {
993 ; i2c_detail::data.onReceiveFunction();
996 ldd r30, Y + %[onReceiveFunction]
997 ldd r31, Y + %[onReceiveFunction] + 1
1000 cpc r31, __zero_reg__
1004 ; TW register pointer may be clobbered, do not use
1005 ; r20, r21, and r22 may be clobbered, do not use
1009; ----------------------------------------------------- ;
1011 ; i2c_detail::data.bufferIdx = 0;
1012 std Y + %[bufferIdx], __zero_reg__
1013 ; default to sending 1 junk byte if the user does not fill buffer
1014 ; i2c_detail::data.bufferSize = 1;
1016 std Y + %[bufferSize], r26
1018 ; if (i2c_detail::data.onRequestFunction) {
1019 ; i2c_detail::data.onRequestFunction();
1021 ; i2c_detail::data.onRequestFunction();
1022 ldd r30, Y + %[onRequestFunction]
1023 ldd r31, Y + %[onRequestFunction] + 1
1025 cp r30, __zero_reg__
1026 cpc r31, __zero_reg__
1033 ; r20, r21, and r22 may be clobbered, do not use
1036 ; ------------------ fallthrough ---------------------- ;
1038 ; TWDR = i2c_detail::data.twiBuffer[i2c_detail::data.bufferIdx++];
1039 rcall get_buffer_addr
1043 ; if (i2c_detail::data.bufferIdx < i2c_detail::data.bufferSize) {
1046 ; TWCR = REPLY_NACK;
1049 ; (reuse code in MR)
1058; ----------------------------------------------------- ;
1060 ; i2c_detail::data.error = TWSR;
1061 std Y + %[error], r18
1069 ; while (TWCR & _BV(TWSTO)) {}
1072 sbrc r26, TWSTO ; skip if bit in register clear
1077 ; i2c_detail::data.active = false;
1078 std Y + %[active], __zero_reg__
1080; --------------------- epilogue ---------------------- ;
1101; -------------------- subroutines --------------------- ;
1102; output: X = &twiBuffer[bufferIdx++]
1104 ; TWDR = i2c_detail::data.twiBuffer[i2c_detail::data.bufferIdx++];
1105 ldd r26, Y + %[bufferIdx]
1107 std Y + %[bufferIdx], r26
1109 ; use SUBI and SBCI as (non-existent) ADDI and (non-existent) ADCI
1110 ; bufferIdx is already incremented so decrement to compensate
1113 subi r26, lo8(-(%[twiBuffer] - 1))
1114 sbci r27, hi8(-(%[twiBuffer] - 1))
1132 [data]
"=m" (i2c_detail::data),
1133 [twiBuffer]
"=m" (i2c_detail::data.twiBuffer)
1135 [error]
"i" (offsetof(i2c_detail::I2CData, error)),
1136 [active]
"i" (offsetof(i2c_detail::I2CData, active)),
1137 [bufferIdx]
"i" (offsetof(i2c_detail::I2CData, bufferIdx)),
1138 [readBuffer]
"i" (offsetof(i2c_detail::I2CData, readBuffer)),
1139 [onRequestFunction]
"i" (offsetof(i2c_detail::I2CData, onRequestFunction)),
1140 [onReceiveFunction]
"i" (offsetof(i2c_detail::I2CData, onReceiveFunction)),
1141 [bufferSize]
"i" (offsetof(i2c_detail::I2CData, bufferSize)),
1142 [slaRW]
"i" (offsetof(i2c_detail::I2CData, slaRW))
1150 TWDR = i2c_detail::data.slaRW;
1151 TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWIE);
1155 case TW_MT_DATA_ACK:
1156 if (i2c_detail::data.bufferIdx < i2c_detail::data.bufferSize) {
1157 TWDR = i2c_detail::data.twiBuffer[i2c_detail::data.bufferIdx++];
1158 TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWIE);
1160 TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWIE) | _BV(TWSTO) | _BV(TWEA);
1161 while (TWCR & _BV(TWSTO)) { }
1162 i2c_detail::data.active =
false;
1166 case TW_MR_DATA_ACK:
1167 i2c_detail::data.readBuffer[i2c_detail::data.bufferIdx++] = TWDR;
1168 __attribute__((fallthrough));
1170 if (i2c_detail::data.bufferIdx < i2c_detail::data.bufferSize) {
1171 TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWIE) | _BV(TWEA);
1173 TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWIE);
1176 case TW_MR_DATA_NACK:
1177 i2c_detail::data.readBuffer[i2c_detail::data.bufferIdx++] = TWDR;
1178 TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWIE) | _BV(TWSTO) | _BV(TWEA);
1179 while (TWCR & _BV(TWSTO)) { }
1180 i2c_detail::data.active =
false;
1184 i2c_detail::data.bufferIdx = 0;
1185 i2c_detail::data.bufferSize = 1;
1186 if (i2c_detail::data.onRequestFunction) {
1187 i2c_detail::data.onRequestFunction();
1189 __attribute__((fallthrough));
1190 case TW_ST_DATA_ACK:
1191 TWDR = i2c_detail::data.twiBuffer[i2c_detail::data.bufferIdx++];
1192 if (i2c_detail::data.bufferIdx < i2c_detail::data.bufferSize) {
1193 TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWIE) | _BV(TWEA);
1195 TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWIE);
1198 case TW_ST_DATA_NACK:
1199 case TW_ST_LAST_DATA:
1200 TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWIE) | _BV(TWEA);
1204 i2c_detail::data.bufferIdx = 0;
1205 TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWIE) | _BV(TWEA);
1207 case TW_SR_DATA_ACK:
1208 i2c_detail::data.twiBuffer[i2c_detail::data.bufferIdx++] = TWDR;
1209 TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWIE) | _BV(TWEA);
1212 TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWIE) | _BV(TWEA);
1213 if (i2c_detail::data.onReceiveFunction) {
1214 i2c_detail::data.onReceiveFunction();
1218 i2c_detail::data.error = TWSR;
1219 TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWIE) | _BV(TWSTO) | _BV(TWEA);
1220 while (TWCR & _BV(TWSTO)) { }
1221 i2c_detail::data.active =
false;
#define I2C_SCL_BIT
The bit of the pin on which the SCL line is connected.
Definition ArduboyI2C.h:112
#define I2C_CHECK_CABLE_FLIPPED_DEBOUNCE_CHECKS
The number of consecutive cable flip checks that pass before confirming the cable has been flipped ba...
Definition ArduboyI2C.h:92
#define I2C_SDA_BIT
The bit of the pin on which the SDA line is connected.
Definition ArduboyI2C.h:103
#define I2C_DDR
The data direction register for the SDA and SCL lines.
Definition ArduboyI2C.h:145
#define I2C_PIN
The pin on which the SDA and SCL lines are connected.
Definition ArduboyI2C.h:123
#define I2C_BUFFER_CAPACITY
The capacity of the buffer used for writes/target (slave) operations.
Definition ArduboyI2C.h:57
#define I2C_PORT
The port on which the SDA and SCL lines are connected.
Definition ArduboyI2C.h:134
#define I2C_HANDSHAKE_BUSY_CHECKS
Number of times to check for a busy bus during a handshake.
Definition ArduboyI2C.h:69
#define I2C_FREQUENCY
The initial I2C frequency in Hz.
Definition ArduboyI2C.h:45
#define I2C_CHECK_CABLE_FLIPPED_CHECKS
The total number of checks to perform when checking for a flipped cable.
Definition ArduboyI2C.h:80
Definition ArduboyI2C.h:184
static void reply(const void *buffer, uint8_t size)
Replies back to the controller (master).
static void onRequest(void(*function)())
Sets up/disables the callback to be called when data is requested from the device's address (a read).
Mode
The mode of an I2C write operation.
Definition ArduboyI2C.h:189
@ Async
Asynchronous write. The function will return immediately, and the write will be completed in the back...
Definition ArduboyI2C.h:190
@ Sync
Synchronous write. The function will block until the write is complete.
Definition ArduboyI2C.h:191
static void checkCableFlipped(void(*startFunction)()=nullptr, void(*loopFunction)()=nullptr)
Checks if the I2C cable is flipped, calling a function if it is and waiting for it to be flipped back...
static void setAddress(uint8_t address)
Sets the 7-bit address of this device.
static void reply(const T &object)
Definition ArduboyI2C.h:378
static uint8_t * getBuffer()
Gets a pointer to the internal buffer used for I2C communication.
static bool isActive()
Gets whether or not the controller (master) is currently transmitting or receiving data.
static uint8_t getAddress()
Gets the 7-bit address of this device.
static uint8_t getBufferSize()
Gets the size of the data in the internal buffer.
static void begin()
Initializes I2C hardware.
static void end()
Deinitializes I2C hardware.
static void read(uint8_t address, void *buffer, uint8_t size)
Attempts to become the bus controller (master) and reads data over I2C from the specified address.
static void write(uint8_t address, const T &object, I2C::Mode mode)
Definition ArduboyI2C.h:285
static void write(uint8_t address, const T(&buffer)[N], I2C::Mode mode)
Definition ArduboyI2C.h:303
static Error getError()
Gets the hardware error which happened in a previous read or write.
static void reply(const T(&buffer)[N])
Definition ArduboyI2C.h:393
static void onReceive(void(*function)())
Sets up/disables the callback to be called when data is sent to the device's address (a write).
Error
The error code of the last I2C controller (master) operation.
Definition ArduboyI2C.h:197
@ WriteDataNack
The controller (master) sent data and the target (slave) did not acknowledge it.
Definition ArduboyI2C.h:199
@ WriteAddrNack
The controller (master) sent a write address and the target (slave) did not acknowledge it.
Definition ArduboyI2C.h:198
@ None
No error. The last operation was successful.
Definition ArduboyI2C.h:202
@ ReadAddrNack
The controller (master) sent a read address and the target (slave) did not acknowledge it.
Definition ArduboyI2C.h:200
@ Bus
An illegal start or stop condition was detected on the bus.
Definition ArduboyI2C.h:201
static void read(uint8_t address, T(&buffer)[N])
Definition ArduboyI2C.h:348
static void write(uint8_t address, const void *buffer, uint8_t size, I2C::Mode mode)
Attempts to become the bus controller (master) and sends data over I2C to the specified address.
static constexpr uint8_t targetAddress
The 7-bit address of the target (slave) device, set by I2C::handshake().
Definition ArduboyI2C.h:213
static I2C::Role handshake(void(*startFunction)()=nullptr, void(*loopFunction)()=nullptr)
Waits for another device and returns whether this device is the controller (master) or target (slave)...
Role
Definition ArduboyI2C.h:205
@ Controller
The device is the controller (master) and can read/write to the target (slave).
Definition ArduboyI2C.h:206
@ Target
The device is the target (slave) and can only read/write when requested by the controller (master).
Definition ArduboyI2C.h:207
static constexpr uint8_t nullAddress
The 7-bit address used to indicate a null or invalid address.
Definition ArduboyI2C.h:219
static void read(uint8_t address, T &object)
Definition ArduboyI2C.h:331