ArduboyI2C Library
Loading...
Searching...
No Matches
ArduboyI2C.h
Go to the documentation of this file.
1/*
2MIT License
3
4Copyright (c) 2024-2026 sub1inear
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
28#pragma once
29#include <avr/interrupt.h>
30#include <avr/power.h>
31#include <util/twi.h>
32#include <util/delay.h>
33#include <stdlib.h>
34#include <stdint.h>
35#include <stddef.h>
36#include <string.h>
37
38#ifndef I2C_FREQUENCY
45#define I2C_FREQUENCY 100000
46#elif I2C_FREQUENCY > 200000
47#error I2C_FREQUENCY is too high.
48#endif
49
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.
60#endif
61
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.
72#endif
73
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.
83#endif
84
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.
95#endif
96
97#ifndef I2C_SDA_BIT
103#define I2C_SDA_BIT PIND1
104#endif
105
106#ifndef I2C_SCL_BIT
112#define I2C_SCL_BIT PIND0
113#endif
114
115#ifndef I2C_PIN
123#define I2C_PIN PIND
124#endif
125
126#ifndef I2C_PORT
134#define I2C_PORT PORTD
135#endif
136
137#ifndef I2C_DDR
145#define I2C_DDR DDRD
146#endif
147
151#define I2C_VERSION_MAJOR 3
152
156#define I2C_VERSION_MINOR 0
157
161#define I2C_VERSION_PATCH 0
162
168#define I2C_VERSION (I2C_VERSION_MAJOR * 10000 + I2C_VERSION_MINOR * 100 + I2C_VERSION_PATCH)
169
171
174namespace i2c_detail {
175// minimal <type_traits> implementation (non-existent on avr-gcc)
176template <typename T> struct is_pointer { static const bool value = false; };
177template <typename T> struct is_pointer<T *> { static const bool value = true; };
178}
180
184class I2C {
185public:
189 enum class Mode : uint8_t {
190 Async = 0,
191 Sync = 1
192 };
193
197 enum class Error : uint8_t {
198 WriteAddrNack = TW_MT_SLA_NACK,
199 WriteDataNack = TW_MT_DATA_NACK,
200 ReadAddrNack = TW_MR_SLA_NACK,
201 Bus = TW_BUS_ERROR,
202 None = 0xFF
203 };
204
205 enum class Role : uint8_t {
207 Target = 1,
208 };
209
213 static constexpr uint8_t targetAddress = 0x08;
219 static constexpr uint8_t nullAddress = 0x09;
220
228 static void begin();
229
237 static void end();
238
245 static void setAddress(uint8_t address);
246
252 static uint8_t getAddress();
253
272 static void write(uint8_t address, const void *buffer, uint8_t size, I2C::Mode mode);
273
284 template<typename T>
285 static void write(uint8_t address, const T &object, 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);
289 }
290
302 template<typename T, size_t N>
303 static void write(uint8_t address, const T (&buffer)[N], I2C::Mode mode) {
304 // N must be size_t otherwise const T & variant captures overload with N > 255
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);
307 }
308
319 static void read(uint8_t address, void *buffer, uint8_t size);
320
330 template<typename T>
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));
335 }
336
347 template<typename T, size_t N>
348 static void read(uint8_t address, T (&buffer)[N]) {
349 // N must be size_t otherwise const T & variant captures overload with N > 255
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);
352 }
353
368 static void reply(const void *buffer, uint8_t size);
369
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.");
381 I2C::reply((const void *)&object, sizeof(T));
382 }
383
392 template<typename T, size_t N>
393 static void reply(const T (&buffer)[N]) {
394 // N must be size_t otherwise const T & variant captures overload with N > 255
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);
397 }
398
420 static void onRequest(void (*function)());
421
442 static void onReceive(void (*function)());
443
448 static Error getError();
449
457 static uint8_t *getBuffer();
458
466 static uint8_t getBufferSize();
467
475 static bool isActive();
476
498 static void checkCableFlipped(void (*startFunction)() = nullptr, void (*loopFunction)() = nullptr);
499
510 static I2C::Role handshake(void (*startFunction)() = nullptr, void (*loopFunction)() = nullptr);
511};
512
513#ifdef I2C_IMPLEMENTATION
515
518namespace i2c_detail {
519struct I2CData {
523 void (*onRequestFunction)() = nullptr;
524
528 void (*onReceiveFunction)() = nullptr;
529
534 volatile uint8_t *readBuffer;
535
539 uint8_t twiBuffer[I2C_BUFFER_CAPACITY];
540
544 volatile uint8_t bufferIdx;
545
549 volatile uint8_t bufferSize;
550
555 volatile bool active;
556
560 volatile uint8_t slaRW;
561
565 volatile I2C::Error error;
566} data;
567
572bool checkCableFlippedCore(bool disconnectFlip) {
573 uint8_t prev = I2C_PIN;
574 uint8_t sdaEdges = 0;
575 uint8_t sclEdges = 0;
576
577 // uint16_t will optimize to a uint8_t if I2C_CHECK_CABLE_FLIPPED_CHECKS < 256
578 for (uint16_t i = 0; i < I2C_CHECK_CABLE_FLIPPED_CHECKS; i++) {
579 uint8_t cur = I2C_PIN;
580 // calculates the change between the current and previous pin states
581 uint8_t diff = cur ^ prev;
582
583 // if the SDA line changed, increment the SDA edge counter
584 if (diff & _BV(I2C_SDA_BIT)) { sdaEdges++; }
585 // if the SCL line changed, increment the SCL edge counter
586 if (diff & _BV(I2C_SCL_BIT)) { sclEdges++; }
587
588 prev = cur;
589 // half-period delay otherwise too fast
590 _delay_us(1000000.0 / I2C_FREQUENCY / 2.0);
591 }
592
593 // if the total number of edges is very low (<= 2 to account for noise), the cable is disconnected or flipped
594 if (sdaEdges + sclEdges <= 2) {
595 // returns flipped (true) if disconnect is a flip, otherwise returns not flipped (false)
596 return disconnectFlip;
597 }
598 // otherwise, if the number of SDA edges is greater than the number of SCL edges, the cable is flipped
599 return sdaEdges > sclEdges;
600}
601
602void startReadWrite(uint8_t address, bool readWrite, uint8_t bufferSize) {
603 i2c_detail::data.active = true;
604 i2c_detail::data.error = I2C::Error::None;
605 i2c_detail::data.slaRW = address << 1 | readWrite;
606 i2c_detail::data.bufferIdx = 0;
607 i2c_detail::data.bufferSize = bufferSize;
608}
609
610void waitActive() {
611 while (i2c_detail::data.active) { }
612}
613
614void sendStart() {
615 TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWSTA) | _BV(TWINT);
616}
617
618}
620
621void I2C::begin() {
622 // power up TWI
623 // power_twi_disable() is called in arduboy.boot() (inside arduboy.begin())
624 power_twi_enable();
625
626 // enable TWI, interrupts, ACKing
627 TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA);
628 // set our clock frequency
629 TWBR = (F_CPU / I2C_FREQUENCY - 16) / 2;
630
631 // enable software pullups (needed for Arduboy FX-C)
632 I2C_DDR &= ~(_BV(I2C_SDA_BIT) | _BV(I2C_SCL_BIT));
633 I2C_PORT |= _BV(I2C_SDA_BIT) | _BV(I2C_SCL_BIT);
634}
635
636void I2C::end() {
637 // disable TWI
638 TWCR = 0;
639
640 // power down TWI
641 power_twi_disable();
642
643 // disable software pullups (needed for Arduboy FX-C)
644 // no need to change DDR
645 I2C_PORT &= ~(_BV(I2C_SDA_BIT) | _BV(I2C_SCL_BIT));
646}
647
648void I2C::setAddress(uint8_t address) {
649 // TWAR format: 7-bit address in bits 7-1, TWGCE in bit 0
650 TWAR = address << 1;
651}
652
653uint8_t I2C::getAddress() {
654 // TWAR format: 7-bit address in bits 7-1, TWGCE in bit 0
655 return TWAR >> 1;
656}
657
658void I2C::write(uint8_t address, const void *buffer, uint8_t size, I2C::Mode mode) {
659 i2c_detail::waitActive();
660
661 i2c_detail::startReadWrite(address, TW_WRITE, size);
662 memcpy(i2c_detail::data.twiBuffer, buffer, size);
663
664 i2c_detail::sendStart();
665
666 if (mode == I2C::Mode::Sync) {
667 i2c_detail::waitActive();
668 }
669}
670
671void I2C::read(uint8_t address, void *buffer, uint8_t size) {
672 i2c_detail::waitActive();
673
674 // ISR: TWCR = i2c_detail::bufferIdx < i2c_detail::bufferSize ? REPLY_ACK : REPLY_NACK;
675 // so we must set the buffer size to size - 1 so that the last byte is NACKed (I2C standard)
676 i2c_detail::startReadWrite(address, TW_READ, size - 1);
677 i2c_detail::data.readBuffer = (uint8_t *)buffer;
678
679 i2c_detail::sendStart();
680
681 i2c_detail::waitActive();
682}
683
684void I2C::reply(const void *buffer, uint8_t size) {
685 // save memory by not accumulating inside buffer (I2C::reply() may not be repeated)
686 memcpy(i2c_detail::data.twiBuffer, buffer, size);
687 i2c_detail::data.bufferSize = size;
688}
689
690void I2C::onRequest(void (*function)()) {
691 i2c_detail::data.onRequestFunction = function;
692}
693
694void I2C::onReceive(void (*function)()) {
695 i2c_detail::data.onReceiveFunction = function;
696}
697
699 return i2c_detail::data.error;
700}
701
702uint8_t *I2C::getBuffer() {
703 return i2c_detail::data.twiBuffer;
704}
705
706uint8_t I2C::getBufferSize() {
707 // i2c_detail::data.bufferSize is undefined during a target (slave) receive
708 // i2c_detail::data.bufferIdx is incremented and therefore represents the current size in the buffer
709 return i2c_detail::data.bufferIdx;
710}
711
712bool I2C::isActive() {
713 return i2c_detail::data.active;
714}
715
716void I2C::checkCableFlipped(void (*startFunction)(), void (*loopFunction)()) {
717 // disable TWI
718 TWCR = 0;
719
720 // check if cable is flipped
721 // pass false to indicate a disconnect is not a flip
722 // so it will return false and we will continue
723 if (i2c_detail::checkCableFlippedCore(false)) {
724 if (startFunction) {
725 startFunction();
726 }
727 // wait for the cable to be flipped back
728 // debounce the cable flip detection
729 // by ensuring I2C_CHECK_CABLE_FLIPPED_DEBOUNCE_CHECKS successive counts of success
730 // putting the cable back it can generate noise
731 // uint16_t will optimize to a uint8_t if I2C_CHECK_CABLE_FLIPPED_DEBOUNCE_CHECKS < 256
732 for (uint16_t i = 0; i < I2C_CHECK_CABLE_FLIPPED_DEBOUNCE_CHECKS; i++) {
733 // pass true to indicate a disconnect is a flip
734 // so we will wait while the cable is disconnected
735 if (i2c_detail::checkCableFlippedCore(true)) {
736 i = 0;
737 }
738 if (loopFunction) {
739 loopFunction();
740 }
741 }
742 }
743
744 // re-enable TWI
745 TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA);
746}
747
748I2C::Role I2C::handshake(void (*startFunction)(), void (*loopFunction)()) {
749 // check if the bus is free (SDA and SCL are high for 128 half-periods)
750 // if it is not, another controller (master) has gotten here before us
751 // uint16_t will optimize to a uint8_t if I2C_HANDSHAKE_BUSY_CHECKS < 256
752 for (uint16_t i = 0; i < I2C_HANDSHAKE_BUSY_CHECKS; i++) {
753 if ((I2C_PIN & (_BV(I2C_SDA_BIT) | _BV(I2C_SCL_BIT))) !=
754 (_BV(I2C_SDA_BIT) | _BV(I2C_SCL_BIT))) {
755 // set our address to the target address
757 // wait for twice the time it takes to send 18 bits
758 // (7 address bits + 1 RW + 1 ACK + 8 data bits + 1 ACK) to ensure we ACK the controller
759 // might have come across a controller that just started sending data,
760 // so we will NACK the first transmission and the ACK the second transmission
761 // so we need to wait for two transmissions
762 _delay_us((1000000.0 / I2C_FREQUENCY * 18.0 + 25.0) * 2.0);
763 // return `false` to indicate that this device is the target (slave)
764 return I2C::Role::Target;
765 }
766 // half-period delay otherwise too fast
767 _delay_us(1000000.0 / I2C_FREQUENCY / 2.0);
768 }
769 uint8_t zeros = 0b00000000;
770 if (startFunction) {
771 startFunction();
772 }
773 do {
774 // send all zeros to the target address to help with I2C::checkCableFlipped()
776 if (loopFunction) {
777 loopFunction();
778 }
779 // repeat until the write is successful (no NACKs)
780 } while (I2C::getError() != I2C::Error::None);
782}
783
784#if 1
785ISR(TWI_vect, ISR_NAKED) {
786 asm volatile (
787R"(
788; --------------------- defines ----------------------- ;
789.equ TWPTR, 0xB9
790.equ TWCR, 3
791.equ TWSR, 0
792.equ TWDR, 2
793
794.equ TWIE, 0
795.equ TWEN, 2
796.equ TWWC, 3
797.equ TWSTO, 4
798.equ TWSTA, 5
799.equ TWEA, 6
800.equ TWINT, 7
801
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)
805
806; -------------------- registers ---------------------- ;
807; r18 - TWSR (never used after function call)
808; r19 - general use
809; r20 - REPLY_ACK
810; r21 - REPLY_NACK
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 ---------------------- ;
818push r18
819in r18, __SREG__
820push r18
821; save and restore call-clobbered registers
822; target (slave) could call function pointer
823push r19
824push r20
825push r21
826push r22
827push r23
828push r24
829push r25
830push r26
831push r27
832push r28
833push r29
834push r30
835push r31
836; save and restore tmp and zero registers (could be used in function calls)
837push __tmp_reg__
838push __zero_reg__
839clr __zero_reg__
840; ----------------------------------------------------- ;
841; set up Y pointer (data)
842ldi r28, lo8(%[data])
843ldi r29, hi8(%[data])
844
845; set up Z pointer (TW registers)
846ldi r30, TWPTR
847clr r31
848
849; set up r20 and r21 (REPLY_ACK and REPLY_NACK)
850ldi r20, REPLY_ACK
851ldi r21, REPLY_NACK
852
853; switch (TWSR)
854ldd r18, Z + TWSR ; no mask needed because prescaler bits are cleared
855
856cpi r18, 0x08
857breq TW_START
858
859; MT_MR
860cpi r18, 0x18
861breq TW_MT_SLA_ACK
862cpi r18, 0x28
863breq TW_MT_DATA_ACK
864cpi r18, 0x40
865breq TW_MR_SLA_ACK
866cpi r18, 0x50
867breq TW_MR_DATA_ACK
868cpi r18, 0x58
869breq TW_MR_DATA_NACK
870
871; 64 instruction limit on branches
872rjmp SR_ST
873
874TW_START:
875 ; TWDR = i2c_detail::data.slaRW;
876 ldd r26, Y + %[slaRW]
877 std Z + TWDR, r26
878 ; TWCR = REPLY_NACK;
879 std Z + TWCR, r21
880 ; return;
881 rjmp pop_reti
882
883TW_MT_SLA_ACK:
884TW_MT_DATA_ACK:
885 ; if (i2c_detail::data.bufferIdx >= i2c_detail::data.bufferSize) {
886 ; stop();
887 ; return;
888 ; }
889 ldd r26, Y + %[bufferIdx]
890 ldd r27, Y + %[bufferSize]
891 cp r26, r27
892
893 brlo 1f ; 64 instruction limit on branches
894 rjmp stop_reti
895 1:
896
897 ; TWDR = i2c_detail::data.twiBuffer[i2c_detail::data.bufferIdx++];
898 rcall get_buffer_addr
899 ld r26, X
900 std Z + TWDR, r26
901
902 ; TWCR = REPLY_NACK;
903 std Z + TWCR, r21
904 ; return;
905 rjmp pop_reti
906; ----------------------------------------------------- ;
907
908TW_MR_DATA_NACK:
909TW_MR_DATA_ACK:
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
914
915 add r26, r19
916 adc r27, __zero_reg__
917
918 inc r19
919 std Y + %[bufferIdx], r19
920
921 ldd r19, Z + TWDR
922 st X, r19
923
924 ; if (TWSR == TW_MR_DATA_NACK) {
925 ; stop();
926 ; return;
927 ; }
928 ; r18 holds TWSR
929 cpi r18, 0x58
930 breq stop_reti
931; ------------------ fallthrough ---------------------- ;
932TW_MR_SLA_ACK:
933 ; if (i2c_detail::data.bufferIdx < i2c_detail::data.bufferSize) {
934 ; TWCR = REPLY_ACK;
935 ; } else {
936 ; TWCR = REPLY_NACK;
937 ; }
938 ; return;
939
940 ; r20 and r21 may be clobbered, do not use
941
942 ldd r26, Y + %[bufferIdx]
943 ldd r27, Y + %[bufferSize]
944 cp r26, r27
945 ldi r26, REPLY_ACK
946 brlo 1f
947 ldi r26, REPLY_NACK
948 1:
949 std Z + TWCR, r26
950 rjmp pop_reti
951; ----------------------------------------------------- ;
952SR_ST:
953cpi r18, 0x60
954breq TW_SR_SLA_ACK
955cpi r18, 0x80
956breq TW_SR_DATA_ACK
957cpi r18, 0xA0
958breq TW_SR_STOP
959cpi r18, 0xA8
960breq TW_ST_SLA_ACK
961cpi r18, 0xB8
962breq TW_ST_DATA_ACK
963cpi r18, 0xC0
964breq TW_ST_DATA_NACK
965cpi r18, 0xC8
966breq TW_ST_LAST_DATA
967
968rjmp default
969
970TW_SR_SLA_ACK:
971 ; i2c_detail::data.bufferIdx = 0;
972 std Y + %[bufferIdx], __zero_reg__
973 ; TWCR = REPLY_ACK;
974 std Z + TWCR, r20
975 ; return;
976 rjmp pop_reti
977
978TW_SR_DATA_ACK:
979 ; i2c_detail::data.twiBuffer[i2c_detail::data.bufferIdx++] = TWDR;
980 rcall get_buffer_addr
981 ldd r19, Z + TWDR
982 st X, r19
983
984 ; TWCR = REPLY_ACK;
985 std Z + TWCR, r20
986 ; return;
987 rjmp pop_reti
988TW_SR_STOP:
989 ; TWCR = REPLY_ACK;
990 std Z + TWCR, r20
991
992 ; if (i2c_detail::data.onReceiveFunction) {
993 ; i2c_detail::data.onReceiveFunction();
994 ; }
995
996 ldd r30, Y + %[onReceiveFunction]
997 ldd r31, Y + %[onReceiveFunction] + 1
998
999 cp r30, __zero_reg__
1000 cpc r31, __zero_reg__
1001 breq pop_reti
1002
1003 icall
1004 ; TW register pointer may be clobbered, do not use
1005 ; r20, r21, and r22 may be clobbered, do not use
1006
1007 ; return;
1008 rjmp pop_reti
1009; ----------------------------------------------------- ;
1010TW_ST_SLA_ACK:
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;
1015 ldi r26, 1
1016 std Y + %[bufferSize], r26
1017
1018 ; if (i2c_detail::data.onRequestFunction) {
1019 ; i2c_detail::data.onRequestFunction();
1020 ; }
1021 ; i2c_detail::data.onRequestFunction();
1022 ldd r30, Y + %[onRequestFunction]
1023 ldd r31, Y + %[onRequestFunction] + 1
1024
1025 cp r30, __zero_reg__
1026 cpc r31, __zero_reg__
1027 breq 1f
1028 icall
1029
1030 ; restore Z pointer
1031 ldi r30, TWPTR
1032 clr r31
1033 ; r20, r21, and r22 may be clobbered, do not use
1034 1:
1035
1036 ; ------------------ fallthrough ---------------------- ;
1037TW_ST_DATA_ACK:
1038 ; TWDR = i2c_detail::data.twiBuffer[i2c_detail::data.bufferIdx++];
1039 rcall get_buffer_addr
1040 ld r26, X
1041 std Z + TWDR, r26
1042
1043 ; if (i2c_detail::data.bufferIdx < i2c_detail::data.bufferSize) {
1044 ; TWCR = REPLY_ACK;
1045 ; } else {
1046 ; TWCR = REPLY_NACK;
1047 ; }
1048 ; return;
1049 ; (reuse code in MR)
1050 rjmp TW_MR_SLA_ACK
1051TW_ST_DATA_NACK:
1052TW_ST_LAST_DATA:
1053 ; TWCR = REPLY_ACK;
1054 ldi r26, REPLY_ACK
1055 std Z + TWCR, r26
1056 ; return;
1057 rjmp pop_reti
1058; ----------------------------------------------------- ;
1059default:
1060 ; i2c_detail::data.error = TWSR;
1061 std Y + %[error], r18
1062
1063 stop_reti:
1064
1065 ; TWCR = STOP;
1066 ldi r26, STOP
1067 std Z + TWCR, r26
1068
1069 ; while (TWCR & _BV(TWSTO)) {}
1070 1:
1071 ldd r26, Z + TWCR
1072 sbrc r26, TWSTO ; skip if bit in register clear
1073 rjmp 1b
1074
1075 active_false_reti:
1076
1077 ; i2c_detail::data.active = false;
1078 std Y + %[active], __zero_reg__
1079
1080; --------------------- epilogue ---------------------- ;
1081 pop_reti:
1082 pop __zero_reg__
1083 pop __tmp_reg__
1084 pop r31
1085 pop r30
1086 pop r29
1087 pop r28
1088 pop r27
1089 pop r26
1090 pop r25
1091 pop r24
1092 pop r23
1093 pop r22
1094 pop r21
1095 pop r20
1096 pop r19
1097 pop r18
1098 out __SREG__, r18
1099 pop r18
1100 reti
1101; -------------------- subroutines --------------------- ;
1102; output: X = &twiBuffer[bufferIdx++]
1103get_buffer_addr:
1104 ; TWDR = i2c_detail::data.twiBuffer[i2c_detail::data.bufferIdx++];
1105 ldd r26, Y + %[bufferIdx]
1106 inc r26
1107 std Y + %[bufferIdx], r26
1108
1109 ; use SUBI and SBCI as (non-existent) ADDI and (non-existent) ADCI
1110 ; bufferIdx is already incremented so decrement to compensate
1111
1112 clr r27
1113 subi r26, lo8(-(%[twiBuffer] - 1))
1114 sbci r27, hi8(-(%[twiBuffer] - 1))
1115 ret
1116)"
1117#if 0
1118R"(
1119debug_green:
1120 cbi 0x05, 7
1121 ret
1122debug_red:
1123 cbi 0x05, 6
1124 ret
1125debug_blue:
1126 cbi 0x05, 5
1127 ret
1128
1129)"
1130#endif // #if 0
1131 : // Output Operands
1132 [data] "=m" (i2c_detail::data),
1133 [twiBuffer] "=m" (i2c_detail::data.twiBuffer)
1134 : // Input Operands
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))
1143 );
1144}
1145#else
1146// C++ ISR version for reference
1147ISR(TWI_vect) {
1148 switch (TWSR) { // prescaler bits are cleared, no mask needed
1149 case TW_START:
1150 TWDR = i2c_detail::data.slaRW;
1151 TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWIE);
1152 break;
1153 // MT
1154 case TW_MT_SLA_ACK:
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);
1159 } else {
1160 TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWIE) | _BV(TWSTO) | _BV(TWEA);
1161 while (TWCR & _BV(TWSTO)) { }
1162 i2c_detail::data.active = false;
1163 }
1164 break;
1165 // MR
1166 case TW_MR_DATA_ACK:
1167 i2c_detail::data.readBuffer[i2c_detail::data.bufferIdx++] = TWDR;
1168 __attribute__((fallthrough));
1169 case TW_MR_SLA_ACK:
1170 if (i2c_detail::data.bufferIdx < i2c_detail::data.bufferSize) {
1171 TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWIE) | _BV(TWEA);
1172 } else {
1173 TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWIE);
1174 }
1175 break;
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;
1181 break;
1182 // ST
1183 case TW_ST_SLA_ACK:
1184 i2c_detail::data.bufferIdx = 0;
1185 i2c_detail::data.bufferSize = 1; // default to sending 1 junk byte if the user does not fill buffer
1186 if (i2c_detail::data.onRequestFunction) {
1187 i2c_detail::data.onRequestFunction();
1188 }
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);
1194 } else {
1195 TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWIE);
1196 }
1197 break;
1198 case TW_ST_DATA_NACK:
1199 case TW_ST_LAST_DATA: // last interrupt cleared TWEA
1200 TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWIE) | _BV(TWEA);
1201 break;
1202 // SR
1203 case TW_SR_SLA_ACK:
1204 i2c_detail::data.bufferIdx = 0;
1205 TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWIE) | _BV(TWEA);
1206 break;
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);
1210 break;
1211 case TW_SR_STOP:
1212 TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWIE) | _BV(TWEA);
1213 if (i2c_detail::data.onReceiveFunction) {
1214 i2c_detail::data.onReceiveFunction();
1215 }
1216 break;
1217 default:
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;
1222 break;
1223 }
1224}
1225#endif // #if 1
1226
1227#endif // #ifdef I2C_IMPLEMENTATION
#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