Best of both worlds -- direct port manipulation and portability

I need the best of both worlds... the speed of direct port manipulation and the portability of pin-number based I/O. The task is to accurately receive data from a rotary encoder which generates a quadrature encoded signal. The signal is received on two Arduino pins programmed as inputs, and each fires an interrupt on either edge of the signal.

My current code uses direct port manipulation, because speed matters in interrupt handlers. Moreover, I want to read both signals at the same instant, and that can only be done by direct port manipulation.

The code currently works just fine on the Arduino Mega2560 using a direct port read inside the interrupt routine, but I'd like it to work on other types of Arduinos without changing the source code. (I will, however, change configuration data which I store in EEPROM to determine the input pins).

The Arduino ecosystem provides digitalPinToInterrupt() to aid portability. Is there something like digitalPinToPort() and digitalPinToBitmask()?

With these I could (1) read the pin numbers for the encoder from configuration memory in EEPROM, (2) get the port and bitmask for each pin, and (3) perform direct port IO -- without recompiling the source code.

There is another catch, though. Direct port manipulation relies on the magic Arduino pre-processor to convert PINx, PORTx, DDRx, etc. into (I assume) function calls. So I'd need those calls, too.

If someone has already done this work, I'd like to leverage it. Otherwise, I'm looking for general ideas on how to accomplish this. Thanks in advance.

For basic arduinos have a look at Arduino.h at line 140

// 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))) )

#define NOT_A_PIN 0
#define NOT_A_PORT 0

Have a look also at the various variant for pins_arduino.h

There are two different means of accomplishing this. If you intended to use the same source and recompile for different platforms, use pre-processor directives to selectively compile based on processor specification.

The other option, quite a bit trickier as there is no method of identifying the processor automatically, is to immediately bring up a configuration request upon power up. I use this method, together with a cleverly hidden secret code (top of EEPROM) to decide if the installation has been configured or not. If not, it falls through a bunch of queries (processor type, pin specifications, other parameters...) and makes all assignments accordingly. Once complete, change the top secret hidden code to signal that you're done and the next time you power up you go straight to your code.

kenomadic:
My current code uses direct port manipulation, because speed matters in interrupt handlers. Moreover, I want to read both signals at the same instant, and that can only be done by direct port manipulation.

If it is not necessary to read both pins at the exact same time the digitaWriteFast library gets quite close to the performance of port manipulation without sacrificing convenience and portability. However it does require the identity of the pin to be known at compile time,

...R

Thanks all, excellent information.

That thanks came from the OP (me)... logged into the wrong account.