ArduboyI2C Library
Loading...
Searching...
No Matches
ArduboyI2C.h
Go to the documentation of this file.
1/*
2MIT License
3
4Copyright (c) 2024 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 <stdint.h>
33#include <stddef.h>
34
35#ifndef I2C_FREQUENCY
44#define I2C_FREQUENCY 100000
45#endif
46
47#ifndef I2C_BUFFER_SIZE
53#define I2C_BUFFER_SIZE 32
54#elif I2C_BUFFER_SIZE > 256
55#error "I2C_BUFFER_SIZE is too big."
56#endif
57
58#ifndef I2C_BUS_BUSY_CHECKS
69#define I2C_BUS_BUSY_CHECKS 16
70#endif
71
72
73#ifndef I2C_SCL_PIN
79#define I2C_SCL_PIN PIND
80#endif
81
82#ifndef I2C_SCL_BIT
88#define I2C_SCL_BIT PIND0
89#endif
90
91#ifndef I2C_SDA_PIN
97#define I2C_SDA_PIN PIND
98#endif
99
100#ifndef I2C_SDA_BIT
106#define I2C_SDA_BIT PIND1
107#endif
108
109#ifdef __DOXYGEN__
110
116#define I2C_MAX_PLAYERS
117
121#define I2C_CUSTOM_HANDSHAKE
122
123#endif
124
125
131#define TW_SUCCESS 0xFF
132
138#define I2C_HANDSHAKE_FAILED 0xFE
139
147#define I2C_MAX_ADDRESSES 112
148
154#define I2C_LIB_VER 20102
155
159class I2C {
160public:
167 static void init();
168
178 static void setAddress(uint8_t address, bool generalCall = false);
179
196 static void write(uint8_t address, const void *buffer, uint8_t size, bool wait);
197
213 template<typename T>
214 static void write(uint8_t address, const T *object, bool wait);
215
227 static void read(uint8_t address, void *buffer, uint8_t size);
228
241 template<typename T>
242 static void read(uint8_t address, T *object);
243
258 static void transmit(const void *buffer, uint8_t size);
259
274 template <typename T>
275 static void transmit(const T *object);
276
298 static void onRequest(void (*function)());
299
318 static void onReceive(void (*function)());
319
325 static uint8_t getTWError();
326
333 static uint8_t *getBuffer();
334
339 static bool detectEmulator();
340
348 static uint8_t getAddressFromId(uint8_t id);
349
358 static uint8_t handshake();
359
360};
361
362#ifdef I2C_IMPLEMENTATION
366namespace i2c_detail {
367struct i2c_data_t {
368 void (*onRequestFunction)();
369 void (*onReceiveFunction)();
370
371 volatile uint8_t *rxBuffer;
372 uint8_t twiBuffer[I2C_BUFFER_SIZE];
373 volatile uint8_t bufferIdx;
374 volatile uint8_t bufferSize;
375
376 volatile bool active;
377 volatile uint8_t slaRW;
378 volatile uint8_t error;
379
380} data;
381#ifdef I2C_MAX_PLAYERS
382
383#if I2C_MAX_PLAYERS > I2C_MAX_ADDRESSES
384#error "Too many players. Max is I2C_MAX_ADDRESSES."
385#endif // #if I2C_MAX_PLAYERS > I2C_MAX_ADDRESSES
386
387#ifndef I2C_CUSTOM_HANDSHAKE
388volatile uint8_t handshakeState;
389
390void handshakeOnReceive() {
391 return;
392}
393
394void handshakeOnRequest() {
395 handshakeState++;
396 I2C::transmit(&handshakeState);
397}
398#endif // #ifndef I2C_CUSTOM_HANDSHAKE
399#endif // #ifdef I2C_MAX_PLAYERS
400
401}
402
403void I2C::init() {
404 power_twi_enable();
405 TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA);
406 TWSR = 0; // clear prescaler bits
407 TWBR = (F_CPU / I2C_FREQUENCY - 16) / 2;
408}
409
410void I2C::setAddress(uint8_t address, bool generalCall) {
411 TWAR = address << 1 | generalCall;
412}
413
414void I2C::write(uint8_t address, const void *buffer, uint8_t size, bool wait) {
415 while (i2c_detail::data.active) {}
416
417 for (uint8_t i = 0; i < size; i++) {
418 i2c_detail::data.twiBuffer[i] = ((const uint8_t *)buffer)[i];
419 }
420 i2c_detail::data.bufferIdx = 0;
421 i2c_detail::data.bufferSize = size;
422
423 i2c_detail::data.error = TW_SUCCESS;
424
425 i2c_detail::data.active = true;
426 i2c_detail::data.slaRW = address << 1 | TW_WRITE;
427
428 uint8_t busyChecks = I2C_BUS_BUSY_CHECKS;
429 while (busyChecks) {
430 if ((I2C_SCL_PIN & _BV(I2C_SCL_BIT)) && (I2C_SDA_PIN & _BV(I2C_SDA_BIT))) {
431 busyChecks--;
432 } else {
433 i2c_detail::data.error = TW_MT_ARB_LOST;
434 return;
435 }
436 }
437
438 TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWSTA);
439 if (wait) {
440 while (i2c_detail::data.active) {}
441 }
442}
443
444template<typename T>
445void I2C::write(uint8_t address, const T *buffer, bool wait) {
446 static_assert(sizeof(T) <= I2C_BUFFER_SIZE, "Size of T must be less than or equal to I2C_BUFFER_SIZE.");
447 I2C::write(address, (const void *)buffer, sizeof(T), wait);
448}
449
450void I2C::read(uint8_t address, void *buffer, uint8_t size) {
451 while (i2c_detail::data.active) {}
452
453 i2c_detail::data.rxBuffer = (uint8_t *)buffer;
454
455 i2c_detail::data.bufferIdx = 0;
456 i2c_detail::data.bufferSize = size - 1;
457
458 i2c_detail::data.error = TW_SUCCESS;
459
460 i2c_detail::data.active = true;
461 i2c_detail::data.slaRW = address << 1 | TW_READ;
462
463 uint8_t busyChecks = I2C_BUS_BUSY_CHECKS;
464 while (busyChecks) {
465 if ((I2C_SCL_PIN & _BV(I2C_SCL_BIT)) && (I2C_SDA_PIN & _BV(I2C_SDA_BIT))) {
466 busyChecks--;
467 } else {
468 i2c_detail::data.error = TW_MR_ARB_LOST;
469 return;
470 }
471 }
472
473 TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWSTA);
474 while (i2c_detail::data.active) {}
475}
476
477template<typename T>
478void I2C::read(uint8_t address, T *object) {
479 static_assert(sizeof(T) < 256, "Size of T must be less than 256.");
480 I2C::read(address, (void *)object, sizeof(T));
481}
482
483
484void I2C::transmit(const void *buffer, uint8_t size) {
485 for (uint8_t i = 0; i < size; i++) {
486 i2c_detail::data.twiBuffer[i] = ((uint8_t *)buffer)[i];
487 }
488 i2c_detail::data.bufferIdx = 0;
489 i2c_detail::data.bufferSize = size;
490}
491
492template <typename T>
493void I2C::transmit(const T *object) {
494 static_assert(sizeof(T) <= I2C_BUFFER_SIZE, "Size of T must be less than or equal to I2C_BUFFER_SIZE.");
495 I2C::transmit((const void *)object, sizeof(T));
496}
497
498void I2C::onRequest(void (*function)()) {
499 i2c_detail::data.onRequestFunction = function;
500}
501void I2C::onReceive(void (*function)()) {
502 i2c_detail::data.onReceiveFunction = function;
503}
504
505inline uint8_t I2C::getTWError() {
506 return i2c_detail::data.error;
507}
508
509inline uint8_t *I2C::getBuffer() {
510 return i2c_detail::data.twiBuffer;
511}
512
513inline bool I2C::detectEmulator() {
514 // TWWC is set when TWDR is written to without TWINT being set
515 // Not done in emulator
516 TWDR = 0;
517 return !(TWCR & _BV(TWWC));
518}
519
520#ifdef I2C_MAX_PLAYERS
521
522inline uint8_t I2C::getAddressFromId(uint8_t id) {
523 return 0x8 + id;
524}
525
526#ifndef I2C_CUSTOM_LOBBY
527
528uint8_t I2C::handshake() {
529 for (int8_t i = I2C_MAX_PLAYERS - 1; i >= 0; ) {
530 uint8_t dummy;
531
532 I2C::read(I2C::getAddressFromId(i), &dummy, 1);
533
534 switch (I2C::getTWError()) {
535 case TW_MR_SLA_NACK:
537 I2C::onReceive(i2c_detail::handshakeOnReceive);
538 I2C::onRequest(i2c_detail::handshakeOnRequest);
539
540 // handshakeState is the number of times the callback has been called.
541 // When the callback has been called i times, the final Arduboy has joined.
542 while (i2c_detail::handshakeState < i) { }
543
544 return i;
545 case TW_SUCCESS:
546 i--;
547 break;
548 }
549 }
551}
552
553#endif // #ifndef I2C_CUSTOM_LOBBY
554
555#endif // #ifdef I2C_MAX_PLAYERS
556ISR(TWI_vect, ISR_NAKED) {
557 asm volatile (
558R"(
559; --------------------- defines ----------------------- ;
560.equ TWPTR, 0xB9
561.equ TWCR, 3
562.equ TWSR, 0
563.equ TWDR, 2
564
565.equ TWIE, 0
566.equ TWEN, 2
567.equ TWWC, 3
568.equ TWSTO, 4
569.equ TWSTA, 5
570.equ TWEA, 6
571.equ TWINT, 7
572
573.equ REPLY_ACK, (1 << TWINT) | (1 << TWEN) | (1 << TWIE) | (1 << TWEA)
574.equ REPLY_NACK, (1 << TWINT) | (1 << TWEN) | (1 << TWIE)
575.equ STOP, (1 << TWINT) | (1 << TWEN) | (1 << TWIE) | (1 << TWSTO) | (1 << TWEA)
576
577; -------------------- registers ---------------------- ;
578; r18 - TWSR (never used after function call)
579; r19 - general use
580; r26 (X) - general use
581; r27 (X) - general use
582; r28 (Y) - data pointer
583; r29 (Y) - data pointer
584; r30 (Z) - TW register pointer
585; r31 (Z) - TW register pointer
586; --------------------- prologue ---------------------- ;
587push r18
588in r18, __SREG__
589push r18
590; save and restore call-clobbered registers
591; target (slave) could call function pointer
592push r19
593push r20
594push r21
595push r22
596push r23
597push r24
598push r25
599push r26
600push r27
601push r28
602push r29
603push r30
604push r31
605; save and restore tmp and zero registers (could be used in function calls)
606push __tmp_reg__
607push __zero_reg__
608clr __zero_reg__
609; ----------------------------------------------------- ;
610; set up Y pointer (data)
611ldi r28, lo8(%[data])
612ldi r29, hi8(%[data])
613
614; set up Z pointer (TW registers)
615ldi r30, TWPTR
616clr r31
617
618; switch (TWSR)
619ldd r18, Z + TWSR ; no mask needed because prescaler bits are cleared
620
621cpi r18, 0x08
622breq TW_START
623
624; MT_MR
625cpi r18, 0x18
626breq TW_MT_SLA_ACK
627cpi r18, 0x28
628breq TW_MT_DATA_ACK
629cpi r18, 0x38
630breq TW_MT_ARB_LOST ; same as TW_MR_ARB_LOST
631cpi r18, 0x40
632breq TW_MR_SLA_ACK
633cpi r18, 0x50
634breq TW_MR_DATA_ACK
635cpi r18, 0x58
636breq TW_MR_DATA_NACK
637
638; 64 instruction limit on branches
639rjmp SR_ST
640
641TW_START:
642 ; TWDR = i2c_detail::data.slaRW;
643 ldd r26, Y + %[slaRW]
644 std Z + TWDR, r26
645 ; TWCR = REPLY_NACK;
646 ldi r26, REPLY_NACK
647 std Z + TWCR, r26
648 ; return;
649 rjmp pop_reti
650
651TW_MT_SLA_ACK:
652TW_MT_DATA_ACK:
653 ; if (i2c_detail::data.bufferIdx >= i2c_detail::data.bufferSize) { stop(); return; }
654 ldd r26, Y + %[bufferIdx]
655 ldd r27, Y + %[bufferSize]
656 cp r26, r27
657
658 brlt 1f ; 64 instruction limit on branches
659 rjmp stop_reti
660 1:
661
662 ; TWDR = i2c_detail::data.twiBuffer[i2c_detail::data.bufferIdx++];
663 inc r26
664 std Y + %[bufferIdx], r26
665
666 ; Use SUBI and SBCI as (non-existant) ADDI and (non-existant) ADCI
667 ; bufferIdx is already incremented so decrement to compensate
668
669 clr r27
670 subi r26, lo8(-(%[twiBuffer] - 1))
671 sbci r27, hi8(-(%[twiBuffer] - 1))
672 ld r26, X
673 std Z + TWDR, r26
674
675 ; TWCR = REPLY_NACK;
676 ldi r26, REPLY_NACK
677 std Z + TWCR, r26
678 ; return;
679 rjmp pop_reti
680
681TW_MT_ARB_LOST:
682 ; TWCR = REPLY_ACK;
683 ldi r26, REPLY_ACK
684 std Z + TWCR, r26
685 ; i2c_detail::data.error = TW_MT_ARB_LOST;
686 ldi r26, 0x38
687 std Y + %[error], r26
688 ; active = false;
689 ; return;
690 rjmp active_false_reti
691; ----------------------------------------------------- ;
692
693TW_MR_DATA_NACK:
694TW_MR_DATA_ACK:
695 ; i2c_detail::data.rxBuffer[i2c_detail::data.bufferIdx++] = TWDR;
696 ldd r19, Y + %[bufferIdx]
697 inc r19
698 std Y + %[bufferIdx], r19
699 dec r19
700
701 ldd r26, Y + %[rxBuffer]
702 ldd r27, Y + %[rxBuffer] + 1
703
704 add r26, r19
705 adc r27, __zero_reg__
706
707 ldd r19, Z + TWDR
708 st X, r19
709
710 ; if (TWSR == TW_MR_DATA_NACK) { stop(); return; }
711 ; r18 holds TWSR
712 cpi r18, 0x58
713 brne 1f ; 64 instruction limit on branches
714 rjmp stop_reti
715 1:
716; ------------------ fallthrough ---------------------- ;
717TW_MR_SLA_ACK:
718 ; if (i2c_detail::data.bufferIdx < i2c_detail::data.bufferSize) {
719 ; TWCR = REPLY_ACK;
720 ; } else {
721 ; TWCR = REPLY_NACK;
722 ; }
723 ; return;
724
725 ldd r26, Y + %[bufferIdx]
726 ldd r27, Y + %[bufferSize]
727 cp r26, r27
728 ldi r26, REPLY_ACK
729 brlt 1f
730 ldi r26, REPLY_NACK
731 1:
732 std Z + TWCR, r26
733 rjmp pop_reti
734; ----------------------------------------------------- ;
735SR_ST:
736cpi r18, 0x60
737breq TW_SR_SLA_ACK
738cpi r18, 0x68
739breq TW_SR_ARB_LOST_SLA_ACK
740cpi r18, 0x70
741breq TW_SR_GCALL_ACK
742cpi r18, 0x78
743breq TW_SR_ARB_LOST_GCALL_ACK
744cpi r18, 0x80
745breq TW_SR_DATA_ACK
746cpi r18, 0x90
747breq TW_SR_GCALL_DATA_ACK
748cpi r18, 0xA0
749breq TW_SR_STOP
750cpi r18, 0xA8
751breq TW_ST_SLA_ACK
752cpi r18, 0xB0
753breq TW_ST_ARB_LOST_SLA_ACK
754cpi r18, 0xB8
755breq TW_ST_DATA_ACK
756cpi r18, 0xC0
757breq TW_ST_DATA_NACK
758cpi r18, 0xC8
759breq TW_ST_LAST_DATA
760
761rjmp default
762
763TW_SR_SLA_ACK:
764TW_SR_ARB_LOST_SLA_ACK:
765TW_SR_GCALL_ACK:
766TW_SR_ARB_LOST_GCALL_ACK:
767 ; i2c_detail::data.active = TWSR; (true)
768 std Y + %[active], r18 ; r18 holds TWSR
769 ; i2c_detail::data.bufferIdx = 0;
770 std Y + %[bufferIdx], __zero_reg__
771 ; TWCR = REPLY_ACK;
772 ldi r26, REPLY_ACK
773 std Z + TWCR, r26
774 ; return;
775 rjmp pop_reti
776
777TW_SR_DATA_ACK:
778TW_SR_GCALL_DATA_ACK:
779 ; i2c_detail::data.twiBuffer[i2c_detail::data.bufferIdx++] = TWDR;
780 ldd r26, Y + %[bufferIdx]
781 inc r26
782 std Y + %[bufferIdx], r26
783
784 ; Use SUBI and SBCI as (non-existant) ADDI and (non-existant) ADCI
785 ; bufferIdx is already incremented so decrement to compensate
786
787 clr r27
788 subi r26, lo8(-(%[twiBuffer] - 1))
789 sbci r27, hi8(-(%[twiBuffer] - 1))
790 ldd r19, Z + TWDR
791 st X, r19
792
793 ; TWCR = REPLY_ACK;
794 ldi r26, REPLY_ACK
795 std Z + TWCR, r26
796 ; return;
797 rjmp pop_reti
798TW_SR_STOP:
799 ; TWCR = REPLY_ACK;
800 ldi r26, REPLY_ACK
801 std Z + TWCR, r26
802 ; i2c_detail::data.onReceiveFunction();
803 ldd r30, Y + %[onReceiveFunction]
804 ldd r31, Y + %[onReceiveFunction] + 1
805 icall
806 ; i2c_detail::data.active = false;
807 ; return;
808 rjmp active_false_reti;
809
810; ----------------------------------------------------- ;
811TW_ST_ARB_LOST_SLA_ACK:
812TW_ST_SLA_ACK:
813 ; i2c_detail::data.active = TWSR; (true)
814 std Y + %[active], r18
815 ; i2c_detail::data.onRequestFunction();
816 ldd r30, Y + %[onRequestFunction]
817 ldd r31, Y + %[onRequestFunction] + 1
818 icall
819 ; restore Z pointer
820 ldi r30, TWPTR
821 clr r31
822; ------------------ fallthrough ---------------------- ;
823TW_ST_DATA_ACK:
824 ; TWDR = i2c_detail::data.twiBuffer[i2c_detail::data.bufferIdx++];
825 ldd r26, Y + %[bufferIdx]
826 inc r26
827 std Y + %[bufferIdx], r26
828
829 ; Use SUBI and SBCI as (non-existant) ADDI and (non-existant) ADCI
830 ; bufferIdx is already incremented so decrement to compensate
831
832 clr r27
833 subi r26, lo8(-(%[twiBuffer] - 1))
834 sbci r27, hi8(-(%[twiBuffer] - 1))
835 ld r26, X
836 std Z + TWDR, r26
837
838 ; if (i2c_detail::data.bufferIdx < i2c_detail::data.bufferSize) {
839 ; TWCR = REPLY_ACK;
840 ; } else {
841 ; TWCR = REPLY_NACK;
842 ; }
843 ; return;
844 ; (reuse code in MR)
845 rjmp TW_MR_SLA_ACK
846TW_ST_DATA_NACK:
847TW_ST_LAST_DATA:
848 ; TWCR = REPLY_ACK;
849 ldi r26, REPLY_ACK
850 std Z + TWCR, r26
851 ; i2c_detail::data.active = false;
852 ; return;
853 rjmp active_false_reti
854; ----------------------------------------------------- ;
855default:
856 ; i2c_detail::data.error = TWSR;
857 std Y + %[error], r18
858
859 stop_reti:
860
861 ; TWCR = STOP;
862 ldi r26, STOP
863 std Z + TWCR, r26
864
865 ; while (TWCR & _BV(TWSTO)) {}
866 1:
867 ldd r26, Z + TWCR
868 sbrc r26, TWSTO ; skip if bit in register clear
869 rjmp 1b
870
871 active_false_reti:
872 ; i2c_detail::data.active = false;
873 std Y + %[active], __zero_reg__
874
875; --------------------- epilogue ---------------------- ;
876 pop_reti:
877 pop __zero_reg__
878 pop __tmp_reg__
879 pop r31
880 pop r30
881 pop r29
882 pop r28
883 pop r27
884 pop r26
885 pop r25
886 pop r24
887 pop r23
888 pop r22
889 pop r21
890 pop r20
891 pop r19
892 pop r18
893 out __SREG__, r18
894 pop r18
895 reti
896)"
897 : // Output Operands
898 [data] "=m" (i2c_detail::data),
899 [twiBuffer] "=m" (i2c_detail::data.twiBuffer)
900 : // Input Operands
901 [error] "i" (offsetof(i2c_detail::i2c_data_t, error)),
902 [active] "i" (offsetof(i2c_detail::i2c_data_t, active)),
903 [bufferIdx] "i" (offsetof(i2c_detail::i2c_data_t, bufferIdx)),
904 [rxBuffer] "i" (offsetof(i2c_detail::i2c_data_t, rxBuffer)),
905 [onRequestFunction] "i" (offsetof(i2c_detail::i2c_data_t, onRequestFunction)),
906 [onReceiveFunction] "i" (offsetof(i2c_detail::i2c_data_t, onReceiveFunction)),
907 [bufferSize] "i" (offsetof(i2c_detail::i2c_data_t, bufferSize)),
908 [slaRW] "i" (offsetof(i2c_detail::i2c_data_t, slaRW))
909 );
910}
911
912#if 0
913ISR(TWI_vect) {
914 switch (TWSR) { // prescaler bits are cleared, no mask needed
915 case TW_START:
916 TWDR = i2c_detail::data.slaRW;
917 TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWIE);
918 break;
919 // MT
920 case TW_MT_SLA_ACK:
921 case TW_MT_DATA_ACK:
922 if (i2c_detail::data.bufferIdx < i2c_detail::data.bufferSize) {
923 TWDR = i2c_detail::data.twiBuffer[i2c_detail::data.bufferIdx++];
924 TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWIE);
925 } else {
926 TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWIE) | _BV(TWSTO) | _BV(TWEA);
927 while (TWCR & _BV(TWSTO)) { }
928 i2c_detail::data.active = false;
929 }
930 break;
931 case TW_MT_ARB_LOST: // same as TW_MR_ARB_LOST
932 i2c_detail::data.active = false;
933 i2c_detail::data.error = TW_MT_ARB_LOST;
934 TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWIE) | _BV(TWEA);
935 break;
936 // MR
937 case TW_MR_DATA_ACK:
938 i2c_detail::data.rxBuffer[i2c_detail::data.bufferIdx++] = TWDR;
939 __attribute__((fallthrough));
940 case TW_MR_SLA_ACK:
941 if (i2c_detail::data.bufferIdx < i2c_detail::data.bufferSize) {
942 TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWIE) | _BV(TWEA);
943 } else {
944 TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWIE);
945 }
946 break;
947 case TW_MR_DATA_NACK:
948 i2c_detail::data.rxBuffer[i2c_detail::data.bufferIdx++] = TWDR;
949 TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWIE) | _BV(TWSTO) | _BV(TWEA);
950 while (TWCR & _BV(TWSTO)) { }
951 i2c_detail::data.active = false;
952 break;
953 // ST
954 case TW_ST_SLA_ACK:
955 case TW_ST_ARB_LOST_SLA_ACK:
956 i2c_detail::data.active = true;
957 i2c_detail::data.onRequestFunction();
958 __attribute__((fallthrough));
959 case TW_ST_DATA_ACK:
960 TWDR = i2c_detail::data.twiBuffer[i2c_detail::data.bufferIdx++];
961 if (i2c_detail::data.bufferIdx < i2c_detail::data.bufferSize) {
962 TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWIE) | _BV(TWEA);
963 } else {
964 TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWIE);
965 }
966 break;
967 case TW_ST_DATA_NACK:
968 case TW_ST_LAST_DATA: // last interrupt cleared TWEA
969 TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWIE) | _BV(TWEA);
970 i2c_detail::data.active = false;
971 break;
972 // SR
973 case TW_SR_SLA_ACK:
974 case TW_SR_GCALL_ACK:
975 case TW_SR_ARB_LOST_SLA_ACK:
976 case TW_SR_ARB_LOST_GCALL_ACK:
977 i2c_detail::data.bufferIdx = 0;
978 i2c_detail::data.active = true;
979 TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWIE) | _BV(TWEA);
980 break;
981 case TW_SR_GCALL_DATA_ACK:
982 case TW_SR_DATA_ACK:
983 i2c_detail::data.twiBuffer[i2c_detail::data.bufferIdx++] = TWDR;
984 TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWIE) | _BV(TWEA);
985 break;
986 case TW_SR_STOP:
987 TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWIE) | _BV(TWEA);
988 i2c_detail::data.onReceiveFunction(i2c_detail::data.twiBuffer);
989 i2c_detail::data.active = false;
990 break;
991 default:
992 i2c_detail::data.error = TWSR;
993 TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWIE) | _BV(TWSTO) | _BV(TWEA);
994 while (TWCR & _BV(TWSTO)) { }
995 i2c_detail::data.active = false;
996 break;
997 }
998}
999#endif // #if 0
1000
1001#endif // #ifdef I2C_IMPLEMENTATION
#define I2C_SCL_PIN
The pin on which the SCL line is connected.
Definition ArduboyI2C.h:79
#define I2C_SDA_PIN
The pin on which the SDA line is connected.
Definition ArduboyI2C.h:97
#define I2C_BUS_BUSY_CHECKS
The amount of times the bus is checked before continuing with a read/write operation.
Definition ArduboyI2C.h:69
#define I2C_HANDSHAKE_FAILED
Error code RETURNED by I2C::handshake, meaning the handshake has already been completed.
Definition ArduboyI2C.h:138
#define I2C_SCL_BIT
The bit of the pin on which the SCL line is connected.
Definition ArduboyI2C.h:88
#define I2C_BUFFER_SIZE
The size of the buffer used for writes/target (slave) operations.
Definition ArduboyI2C.h:53
#define I2C_MAX_PLAYERS
The maxmimum number of players in the handshake/lobby.
Definition ArduboyI2C.h:116
#define TW_SUCCESS
Error code used to mean success, returned by I2C::getTWError().
Definition ArduboyI2C.h:131
#define I2C_SDA_BIT
The bit of the pin on which the sda line is connected.
Definition ArduboyI2C.h:106
#define I2C_FREQUENCY
The initial I2C frequency.
Definition ArduboyI2C.h:44
Definition ArduboyI2C.h:159
static void onRequest(void(*function)())
Sets up the callback to be called when data is requested from the device's address (a read).
static uint8_t getTWError()
Gets the hardware error which happened in a previous read or write.
static void init()
Initalizes I2C hardware.
static bool detectEmulator()
Checks if an emulator without I2C support is being used to run the code.
static void transmit(const T *object)
Transmits data back to the controller (master).
static uint8_t * getBuffer()
Gets a pointer to the I2C buffer holding received data.
static void transmit(const void *buffer, uint8_t size)
Transmits data back to the controller (master).
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 uint8_t handshake()
Handshakes with other devices and returns a unique id once complete.
static void setAddress(uint8_t address, bool generalCall=false)
Set the address of the device and enable/disable general calls on the I2C bus.
static void onReceive(void(*function)())
Sets up the callback to be called when data is sent to the device's address (a write)
static void write(uint8_t address, const T *object, bool wait)
Attempts to become the bus controller (master) and sends data over I2C to the specified address.
static uint8_t getAddressFromId(uint8_t id)
Gets the address from a provided id.
static void read(uint8_t address, T *object)
Attempts to become the bus controller (master) and reads data over I2C from the specified address.
static void write(uint8_t address, const void *buffer, uint8_t size, bool wait)
Attempts to become the bus controller (master) and sends data over I2C to the specified address.