diff --git a/hardware/arduino/cores/arduino/pins_arduino.h b/hardware/arduino/cores/arduino/pins_arduino.h index bc931c597..63f425771 100644 --- a/hardware/arduino/cores/arduino/pins_arduino.h +++ b/hardware/arduino/cores/arduino/pins_arduino.h @@ -49,6 +49,10 @@ #define TIMER5B 15 #define TIMER5C 16 +#ifndef INLINED +#define INLINED static __attribute__((always_inline)) inline +#endif + #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) const static uint8_t SS = 53; const static uint8_t MOSI = 51; @@ -72,17 +76,447 @@ extern const uint8_t PROGMEM digital_pin_to_port_PGM[]; extern const uint8_t PROGMEM digital_pin_to_bit_mask_PGM[]; extern const uint8_t PROGMEM digital_pin_to_timer_PGM[]; +// inlined versions of lookup-table-based pin mappings. Don't use directly. + +// Don't use PA, so on, might clash with sketch + +#define PORT_INDEX_PA 1 +#define PORT_INDEX_PB 2 +#define PORT_INDEX_PC 3 +#define PORT_INDEX_PD 4 +#define PORT_INDEX_PE 5 +#define PORT_INDEX_PF 6 +#define PORT_INDEX_PG 7 +#define PORT_INDEX_PH 8 +#define PORT_INDEX_PJ 10 +#define PORT_INDEX_PK 11 +#define PORT_INDEX_PL 12 + +__attribute__((error("Invalid pin specified. This pin does not map to any I/O port"))) static void invalidPinSpecified(void); + + +#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) + +INLINED volatile uint8_t *inlined_portModeRegister(uint8_t port_index) +{ + switch (port_index) { + case 1: return &DDRA; + case 2: return &DDRB; + case 3: return &DDRC; + case 4: return &DDRD; + case 5: return &DDRE; + case 6: return &DDRF; + case 7: return &DDRG; + case 8: return &DDRH; + case 10: return &DDRJ; + case 11: return &DDRK; + case 12: return &DDRL; + default: return NOT_A_PORT; + } +} + +INLINED volatile uint8_t *inlined_portOutputRegister(uint8_t port_index) +{ + switch (port_index) { + case 1: return &PORTA; + case 2: return &PORTB; + case 3: return &PORTC; + case 4: return &PORTD; + case 5: return &PORTE; + case 6: return &PORTF; + case 7: return &PORTG; + case 8: return &PORTH; + case 10: return &PORTJ; + case 11: return &PORTK; + case 12: return &PORTL; + default: return NOT_A_PORT; + } +} + +INLINED volatile uint8_t *inlined_portInputRegister(uint8_t port_index) +{ + switch (port_index) { + case 1: return &PINA; + case 2: return &PINB; + case 3: return &PINC; + case 4: return &PIND; + case 5: return &PINE; + case 6: return &PINF; + case 7: return &PING; + case 8: return &PINH; + case 10: return &PINJ; + case 11: return &PINK; + case 12: return &PINL; + default: return NOT_A_PIN; + } +}; + +INLINED uint8_t inlined_digitalPinToPort(uint8_t pin) +{ + switch (pin) { + case 0: // PE 0 ** 0 ** USART0_RX + case 1: // PE 1 ** 1 ** USART0_TX + case 2: // PE 4 ** 2 ** PWM2 + case 3: // PE 5 ** 3 ** PWM3 + return PORT_INDEX_PE; + + case 4: // PG 5 ** 4 ** PWM4 + return PORT_INDEX_PG; + + case 5: // PE 3 ** 5 ** PWM5 + return PORT_INDEX_PE; + + case 6: // PH 3 ** 6 ** PWM6 + case 7: // PH 4 ** 7 ** PWM7 + case 8: // PH 5 ** 8 ** PWM8 + case 9: // PH 6 ** 9 ** PWM9 + return PORT_INDEX_PH; + + case 10: // PB 4 ** 10 ** PWM10 + case 11: // PB 5 ** 11 ** PWM11 + case 12: // PB 6 ** 12 ** PWM12 + case 13: // PB 7 ** 13 ** PWM13 + return PORT_INDEX_PB; + + case 14: // PJ 1 ** 14 ** USART3_TX + case 15: // PJ 0 ** 15 ** USART3_RX + return PORT_INDEX_PJ; + + case 16: // PH 1 ** 16 ** USART2_TX + case 17: // PH 0 ** 17 ** USART2_RX + return PORT_INDEX_PH; + + case 18: // PD 3 ** 18 ** USART1_TX + case 19: // PD 2 ** 19 ** USART1_RX + case 20: // PD 1 ** 20 ** I2C_SDA + case 21: // PD 0 ** 21 ** I2C_SCL + return PORT_INDEX_PD; + + case 22: // PA 0 ** 22 ** D22 + case 23: // PA 1 ** 23 ** D23 + case 24: // PA 2 ** 24 ** D24 + case 25: // PA 3 ** 25 ** D25 + case 26: // PA 4 ** 26 ** D26 + case 27: // PA 5 ** 27 ** D27 + case 28: // PA 6 ** 28 ** D28 + case 29: // PA 7 ** 29 ** D29 + return PORT_INDEX_PA; + + case 30: // PC 7 ** 30 ** D30 + case 31: // PC 6 ** 31 ** D31 + case 32: // PC 5 ** 32 ** D32 + case 33: // PC 4 ** 33 ** D33 + case 34: // PC 3 ** 34 ** D34 + case 35: // PC 2 ** 35 ** D35 + case 36: // PC 1 ** 36 ** D36 + case 37: // PC 0 ** 37 ** D37 + return PORT_INDEX_PC; + + case 38: // PD 7 ** 38 ** D38 + return PORT_INDEX_PD; + + case 39: // PG 2 ** 39 ** D39 + case 40: // PG 1 ** 40 ** D40 + case 41: // PG 0 ** 41 ** D41 + return PORT_INDEX_PG; + + case 42: // PL 7 ** 42 ** D42 + case 43: // PL 6 ** 43 ** D43 + case 44: // PL 5 ** 44 ** D44 + case 45: // PL 4 ** 45 ** D45 + case 46: // PL 3 ** 46 ** D46 + case 47: // PL 2 ** 47 ** D47 + case 48: // PL 1 ** 48 ** D48 + case 49: // PL 0 ** 49 ** D49 + return PORT_INDEX_PL; + + case 50: // PB 3 ** 50 ** SPI_MISO + case 51: // PB 2 ** 51 ** SPI_MOSI + case 52: // PB 1 ** 52 ** SPI_SCK + case 53: // PB 0 ** 53 ** SPI_SS + return PORT_INDEX_PB; + + case 54: // PF 0 ** 54 ** A0 + case 55: // PF 1 ** 55 ** A1 + case 56: // PF 2 ** 56 ** A2 + case 57: // PF 3 ** 57 ** A3 + case 58: // PF 4 ** 58 ** A4 + case 59: // PF 5 ** 59 ** A5 + case 60: // PF 6 ** 60 ** A6 + case 61: // PF 7 ** 61 ** A7 + return PORT_INDEX_PF; + + case 62: // PK 0 ** 62 ** A8 + case 63: // PK 1 ** 63 ** A9 + case 64: // PK 2 ** 64 ** A10 + case 65: // PK 3 ** 65 ** A11 + case 66: // PK 4 ** 66 ** A12 + case 67: // PK 5 ** 67 ** A13 + case 68: // PK 6 ** 68 ** A14 + case 69: // PK 7 ** 69 ** A15 + return PORT_INDEX_PK; + default: + invalidPinSpecified(); + } +} + +INLINED uint8_t inlined_digitalPinToBitMask(uint8_t pin) +{ + switch(pin) { + case 0: return _BV( 0 ); // PE 0 ** 0 ** USART0_RX + case 1: return _BV( 1 ); // PE 1 ** 1 ** USART0_TX + case 2: return _BV( 4 ); // PE 4 ** 2 ** PWM2 + case 3: return _BV( 5 ); // PE 5 ** 3 ** PWM3 + case 4: return _BV( 5 ); // PG 5 ** 4 ** PWM4 + case 5: return _BV( 3 ); // PE 3 ** 5 ** PWM5 + case 6: return _BV( 3 ); // PH 3 ** 6 ** PWM6 + case 7: return _BV( 4 ); // PH 4 ** 7 ** PWM7 + case 8: return _BV( 5 ); // PH 5 ** 8 ** PWM8 + case 9: return _BV( 6 ); // PH 6 ** 9 ** PWM9 + case 10: return _BV( 4 ); // PB 4 ** 10 ** PWM10 + case 11: return _BV( 5 ); // PB 5 ** 11 ** PWM11 + case 12: return _BV( 6 ); // PB 6 ** 12 ** PWM12 + case 13: return _BV( 7 ); // PB 7 ** 13 ** PWM13 + case 14: return _BV( 1 ); // PJ 1 ** 14 ** USART3_TX + case 15: return _BV( 0 ); // PJ 0 ** 15 ** USART3_RX + case 16: return _BV( 1 ); // PH 1 ** 16 ** USART2_TX + case 17: return _BV( 0 ); // PH 0 ** 17 ** USART2_RX + case 18: return _BV( 3 ); // PD 3 ** 18 ** USART1_TX + case 19: return _BV( 2 ); // PD 2 ** 19 ** USART1_RX + case 20: return _BV( 1 ); // PD 1 ** 20 ** I2C_SDA + case 21: return _BV( 0 ); // PD 0 ** 21 ** I2C_SCL + case 22: return _BV( 0 ); // PA 0 ** 22 ** D22 + case 23: return _BV( 1 ); // PA 1 ** 23 ** D23 + case 24: return _BV( 2 ); // PA 2 ** 24 ** D24 + case 25: return _BV( 3 ); // PA 3 ** 25 ** D25 + case 26: return _BV( 4 ); // PA 4 ** 26 ** D26 + case 27: return _BV( 5 ); // PA 5 ** 27 ** D27 + case 28: return _BV( 6 ); // PA 6 ** 28 ** D28 + case 29: return _BV( 7 ); // PA 7 ** 29 ** D29 + case 30: return _BV( 7 ); // PC 7 ** 30 ** D30 + case 31: return _BV( 6 ); // PC 6 ** 31 ** D31 + case 32: return _BV( 5 ); // PC 5 ** 32 ** D32 + case 33: return _BV( 4 ); // PC 4 ** 33 ** D33 + case 34: return _BV( 3 ); // PC 3 ** 34 ** D34 + case 35: return _BV( 2 ); // PC 2 ** 35 ** D35 + case 36: return _BV( 1 ); // PC 1 ** 36 ** D36 + case 37: return _BV( 0 ); // PC 0 ** 37 ** D37 + case 38: return _BV( 7 ); // PD 7 ** 38 ** D38 + case 39: return _BV( 2 ); // PG 2 ** 39 ** D39 + case 40: return _BV( 1 ); // PG 1 ** 40 ** D40 + case 41: return _BV( 0 ); // PG 0 ** 41 ** D41 + case 42: return _BV( 7 ); // PL 7 ** 42 ** D42 + case 43: return _BV( 6 ); // PL 6 ** 43 ** D43 + case 44: return _BV( 5 ); // PL 5 ** 44 ** D44 + case 45: return _BV( 4 ); // PL 4 ** 45 ** D45 + case 46: return _BV( 3 ); // PL 3 ** 46 ** D46 + case 47: return _BV( 2 ); // PL 2 ** 47 ** D47 + case 48: return _BV( 1 ); // PL 1 ** 48 ** D48 + case 49: return _BV( 0 ); // PL 0 ** 49 ** D49 + case 50: return _BV( 3 ); // PB 3 ** 50 ** SPI_MISO + case 51: return _BV( 2 ); // PB 2 ** 51 ** SPI_MOSI + case 52: return _BV( 1 ); // PB 1 ** 52 ** SPI_SCK + case 53: return _BV( 0 ); // PB 0 ** 53 ** SPI_SS + case 54: return _BV( 0 ); // PF 0 ** 54 ** A0 + case 55: return _BV( 1 ); // PF 1 ** 55 ** A1 + case 56: return _BV( 2 ); // PF 2 ** 56 ** A2 + case 57: return _BV( 3 ); // PF 3 ** 57 ** A3 + case 58: return _BV( 4 ); // PF 4 ** 58 ** A4 + case 59: return _BV( 5 ); // PF 5 ** 59 ** A5 + case 60: return _BV( 6 ); // PF 6 ** 60 ** A6 + case 61: return _BV( 7 ); // PF 7 ** 61 ** A7 + case 62: return _BV( 0 ); // PK 0 ** 62 ** A8 + case 63: return _BV( 1 ); // PK 1 ** 63 ** A9 + case 64: return _BV( 2 ); // PK 2 ** 64 ** A10 + case 65: return _BV( 3 ); // PK 3 ** 65 ** A11 + case 66: return _BV( 4 ); // PK 4 ** 66 ** A12 + case 67: return _BV( 5 ); // PK 5 ** 67 ** A13 + case 68: return _BV( 6 ); // PK 6 ** 68 ** A14 + case 69: return _BV( 7 ); // PK 7 ** 69 ** A15 + default: + // TODO: add error here + invalidPinSpecified(); + } +} + +INLINED uint8_t inlined_digitalPinToTimer(uint8_t pin) +{ + switch(pin) { + case 2: return TIMER3B; // PE 4 ** 2 ** PWM2 + case 3: return TIMER3C; // PE 5 ** 3 ** PWM3 + case 4: return TIMER0B; // PG 5 ** 4 ** PWM4 + case 5: return TIMER3A; // PE 3 ** 5 ** PWM5 + case 6: return TIMER4A; // PH 3 ** 6 ** PWM6 + case 7: return TIMER4B; // PH 4 ** 7 ** PWM7 + case 8: return TIMER4C; // PH 5 ** 8 ** PWM8 + case 9: return TIMER2B; // PH 6 ** 9 ** PWM9 + case 10: return TIMER2A; // PB 4 ** 10 ** PWM10 + case 11: return TIMER1A; // PB 5 ** 11 ** PWM11 + case 12: return TIMER1B; // PB 6 ** 12 ** PWM12 + case 13: return TIMER0A; // PB 7 ** 13 ** PWM13 + case 44: return TIMER5C; // PL 5 ** 44 ** D44 + case 45: return TIMER5B; // PL 4 ** 45 ** D45 + case 46: return TIMER5A; // PL 3 ** 46 ** D46 + default: invalidPinSpecified(); + } +} + +#else // defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) + +INLINED volatile uint8_t *inlined_portModeRegister(uint8_t port_index) +{ + switch (port_index) { + case 2: return &DDRB; + case 3: return &DDRC; + case 4: return &DDRD; + default: invalidPinSpecified(); + } +} + +INLINED volatile uint8_t *inlined_portOutputRegister(uint8_t port_index) +{ + switch (port_index) { + case 2: return &PORTB; + case 3: return &PORTC; + case 4: return &PORTD; + default: invalidPinSpecified(); + } +} + +INLINED volatile uint8_t *inlined_portInputRegister(uint8_t port_index) +{ + switch (port_index) { + case 2: return &PINB; + case 3: return &PINC; + case 4: return &PIND; + default: invalidPinSpecified(); + } +} + +INLINED uint8_t inlined_digitalPinToPort(uint8_t pin) +{ + switch(pin) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + return PORT_INDEX_PD; + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + return PORT_INDEX_PB; + case 14: + case 15: + case 16: + case 17: + case 18: + case 19: + return PORT_INDEX_PC; + default: + invalidPinSpecified(); + } +} + +INLINED uint8_t inlined_digitalPinToBitMask(uint8_t pin) +{ + switch(pin) { + case 0: return _BV(0); /* 0, port D */ + case 1: return _BV(1); + case 2: return _BV(2); + case 3: return _BV(3); + case 4: return _BV(4); + case 5: return _BV(5); + case 6: return _BV(6); + case 7: return _BV(7); + case 8: return _BV(0); /* 8, port B */ + case 9: return _BV(1); + case 10: return _BV(2); + case 11: return _BV(3); + case 12: return _BV(4); + case 13: return _BV(5); + case 14: return _BV(0); /* 14, port C */ + case 15: return _BV(1); + case 16: return _BV(2); + case 17: return _BV(3); + case 18: return _BV(4); + case 19: return _BV(5); + default: + // TODO: throw error here + invalidPinSpecified(); + } +} + +INLINED uint8_t inlined_digitalPinToTimer(uint8_t pin) +{ + switch(pin) { +#if defined(__AVR_ATmega8__) + case 11: return TIMER2; +#else + case 3: return TIMER2B; + case 5: return TIMER0B; + case 6: return TIMER0A; + case 11: return TIMER2A; +#endif + case 9: return TIMER1A; + case 10: return TIMER1B; + default: invalidPinSpecified(); + } +} + +#endif // defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) + // Get the bit location within the hardware port of the given virtual pin. // This comes from the pins_*.c file for the active board configuration. -// -// These perform slightly better as macros compared to inline functions -// -#define digitalPinToPort(P) ( pgm_read_byte( digital_pin_to_port_PGM + (P) ) ) -#define digitalPinToBitMask(P) ( pgm_read_byte( digital_pin_to_bit_mask_PGM + (P) ) ) -#define digitalPinToTimer(P) ( pgm_read_byte( digital_pin_to_timer_PGM + (P) ) ) + #define analogInPinToBit(P) (P) -#define portOutputRegister(P) ( (volatile uint8_t *)( pgm_read_word( port_to_output_PGM + (P))) ) -#define portInputRegister(P) ( (volatile uint8_t *)( pgm_read_word( port_to_input_PGM + (P))) ) -#define portModeRegister(P) ( (volatile uint8_t *)( pgm_read_word( port_to_mode_PGM + (P))) ) + +INLINED uint8_t digitalPinToPort(uint8_t pin) { + if (__builtin_constant_p(pin)) + return inlined_digitalPinToPort(pin); + else + return pgm_read_byte( digital_pin_to_port_PGM + pin ); +} + +INLINED uint8_t digitalPinToBitMask(uint8_t pin) { + if (__builtin_constant_p(pin)) + return inlined_digitalPinToBitMask(pin); + else + return pgm_read_byte( digital_pin_to_bit_mask_PGM + pin ); +} + +INLINED uint8_t digitalPinToTimer(uint8_t pin) { + if (__builtin_constant_p(pin)) + return inlined_digitalPinToTimer(pin); + else + return pgm_read_byte( digital_pin_to_timer_PGM + pin ); +} + +INLINED volatile uint8_t *portOutputRegister(uint8_t index) { + if (__builtin_constant_p(index)) + return inlined_portOutputRegister(index); + else + return (volatile uint8_t *)( pgm_read_word( port_to_output_PGM + index ) ); +} + +INLINED volatile uint8_t* portInputRegister(uint8_t index) { + if (__builtin_constant_p(index)) + return inlined_portInputRegister(index); + else + return (volatile uint8_t *)( pgm_read_word( port_to_input_PGM + index) ); +} + +INLINED volatile uint8_t* portModeRegister(uint8_t index) { + if (__builtin_constant_p(index)) + return inlined_portModeRegister(index); + else + return (volatile uint8_t *)( pgm_read_word( port_to_mode_PGM + index) ); +} #endif diff --git a/hardware/arduino/cores/arduino/wiring.h b/hardware/arduino/cores/arduino/wiring.h index e29959b86..433c87ec4 100755 --- a/hardware/arduino/cores/arduino/wiring.h +++ b/hardware/arduino/cores/arduino/wiring.h @@ -26,8 +26,10 @@ #define Wiring_h #include +#include #include #include "binary.h" +#include "pins_arduino.h" #ifdef __cplusplus extern "C"{ @@ -106,9 +108,9 @@ typedef uint8_t byte; void init(void); -void pinMode(uint8_t, uint8_t); -void digitalWrite(uint8_t, uint8_t); -int digitalRead(uint8_t); +void pinMode_lookup(uint8_t, uint8_t); +void digitalWrite_lookup(uint8_t, uint8_t); +int digitalRead_lookup(uint8_t); int analogRead(uint8_t); void analogReference(uint8_t mode); void analogWrite(uint8_t, int); @@ -128,6 +130,71 @@ void detachInterrupt(uint8_t); void setup(void); void loop(void); +/* + * Check if a given pin requires locking. + * When accessing lower 32 IO ports we can use SBI/CBI instructions, which are atomic. However + * other IO ports require load+modify+store and we need to make them atomic by disabling + * interrupts. + */ +INLINED int portWriteNeedsLocking(uint8_t pin) +{ + /* SBI/CBI instructions only work on lower 32 IO ports */ + if (inlined_portOutputRegister(inlined_digitalPinToPort(pin)) > (volatile uint8_t*)&_SFR_IO8(0x1F)) { + return 1; + } + return 0; +} + +/* + * These functions will perform OR/AND on a given register, and are atomic. + */ +extern void __digitalWriteOR_locked(volatile uint8_t*out, uint8_t bit); +extern void __digitalWriteAND_locked(volatile uint8_t*out, uint8_t bit); + +INLINED void digitalWrite(uint8_t pin, uint8_t value) +{ + if (__builtin_constant_p(pin)) { + if (portWriteNeedsLocking(pin)) { + if (value==LOW) { + __digitalWriteAND_locked(inlined_portOutputRegister(inlined_digitalPinToPort(pin)),~inlined_digitalPinToBitMask(pin)); + } else { + __digitalWriteOR_locked(inlined_portOutputRegister(inlined_digitalPinToPort(pin)),inlined_digitalPinToBitMask(pin)); + } + } else { + if (value==LOW) { + *inlined_portOutputRegister(inlined_digitalPinToPort(pin)) &= ~(inlined_digitalPinToBitMask(pin)); + } else { + *inlined_portOutputRegister(inlined_digitalPinToPort(pin)) |= inlined_digitalPinToBitMask(pin); + } + } + } else { + digitalWrite_lookup(pin,value); + } +} + +INLINED void pinMode(uint8_t pin, uint8_t mode) +{ + if (__builtin_constant_p(pin)) { + if (mode==INPUT) { + *inlined_portModeRegister(inlined_digitalPinToPort(pin)) &= ~(inlined_digitalPinToBitMask(pin)); + } else { + *inlined_portModeRegister(inlined_digitalPinToPort(pin)) |= inlined_digitalPinToBitMask(pin); + } + } else { + pinMode_lookup(pin,mode); + } +} + +INLINED int digitalRead(uint8_t pin) +{ + if (__builtin_constant_p(pin)) { + return !! *inlined_portInputRegister(inlined_digitalPinToPort(pin)); + } else { + return digitalRead_lookup(pin); + } +} + + #ifdef __cplusplus } // extern "C" #endif diff --git a/hardware/arduino/cores/arduino/wiring_digital.c b/hardware/arduino/cores/arduino/wiring_digital.c index 0949da42d..95666b19d 100755 --- a/hardware/arduino/cores/arduino/wiring_digital.c +++ b/hardware/arduino/cores/arduino/wiring_digital.c @@ -27,7 +27,7 @@ #include "wiring_private.h" #include "pins_arduino.h" -void pinMode(uint8_t pin, uint8_t mode) +void pinMode_lookup(uint8_t pin, uint8_t mode) { uint8_t bit = digitalPinToBitMask(pin); uint8_t port = digitalPinToPort(pin); @@ -121,7 +121,23 @@ static void turnOffPWM(uint8_t timer) } } -void digitalWrite(uint8_t pin, uint8_t val) +void __digitalWriteOR_locked(volatile uint8_t*out, uint8_t bit) +{ + uint8_t oldSREG = SREG; + cli(); + *out |= bit; + SREG=oldSREG; +} + +void __digitalWriteAND_locked(volatile uint8_t*out, uint8_t bit) +{ + uint8_t oldSREG = SREG; + cli(); + *out &= bit; // NOTE - no inversion here, invert before calling!!! + SREG=oldSREG; +} + +void digitalWrite_lookup(uint8_t pin, uint8_t val) { uint8_t timer = digitalPinToTimer(pin); uint8_t bit = digitalPinToBitMask(pin); @@ -136,20 +152,19 @@ void digitalWrite(uint8_t pin, uint8_t val) out = portOutputRegister(port); + uint8_t oldSREG = SREG; + cli(); + if (val == LOW) { - uint8_t oldSREG = SREG; - cli(); *out &= ~bit; - SREG = oldSREG; } else { - uint8_t oldSREG = SREG; - cli(); *out |= bit; - SREG = oldSREG; } + + SREG = oldSREG; } -int digitalRead(uint8_t pin) +int digitalRead_lookup(uint8_t pin) { uint8_t timer = digitalPinToTimer(pin); uint8_t bit = digitalPinToBitMask(pin);