Converting Nano Hardware Registers to Mega

I'm working on trying to implement code that has been working on my Uno/Nano, onto an Arduino Mega. The code in question is Kelvin Nelson's "Read PWM, Decode RC Receiver Input, and Apply Fail-Safe" which can be found in detail here.

It seems the port registers are coded for the Nano and Uno, instead of using the hardware abstraction layer to ensure compatibility. I'm trying to figure out the best way to translate these to a mega board in a way that maintains the higher speed interrupt service.

I believe it is this section here that is causing the mismatch, but I would greatly appreciate any help.

// FUNCTION USED TO TURN ON THE INTERRUPTS ON THE RELEVANT PINS
// code from http://playground.arduino.cc/Main/PinChangeInterrupt

void pciSetup(byte pin){
    *digitalPinToPCMSK(pin) |= bit (digitalPinToPCMSKbit(pin));  // enable pin
    PCIFR  |= bit (digitalPinToPCICRbit(pin));                   // clear any outstanding interrupt
    PCICR  |= bit (digitalPinToPCICRbit(pin));                   // enable interrupt for the group
}

// FUNCTION USED TO FIND THE PIN POSITION ON EACH PORT REGISTER: helps the interrupt service routines, ISR, run faster

void pwmPIN_to_port(){
  for (int i = 0; i < num_ch; i++){

    // determine which port and therefore ISR (PCINT0_vect, PCINT1_vect or PCINT2_vect) each pwmPIN belongs to.
                                                                  pwmPIN_port[i] = 1;    // pin belongs to PCINT1_vect (PORT C)
    if (pwmPIN[i] >= 0 && pwmPIN[i] <= 7)                         pwmPIN_port[i] = 2;    // pin belongs to PCINT2_vect (PORT D)
    else if (pwmPIN[i] >= 8 && pwmPIN[i] <= 13)                   pwmPIN_port[i] = 0;    // pin belongs to PCINT0_vect (PORT B)

    // covert the pin number (i.e. pin 11 or pin A0) to the pin position in the port register. There is most likely a better way of doing this using a macro...
    // (Reading the pin state directly from the port registers speeds up the code in the ISR)
    
    if(pwmPIN[i] == 0 || pwmPIN[i] == A0 || pwmPIN[i] == 8)         pwmPIN_reg[i] = 0b00000001;
    else if(pwmPIN[i] == 1 || pwmPIN[i] == A1 || pwmPIN[i] == 9)    pwmPIN_reg[i] = 0b00000010;
    else if(pwmPIN[i] == 2 || pwmPIN[i] == A2 || pwmPIN[i] == 10)   pwmPIN_reg[i] = 0b00000100;
    else if(pwmPIN[i] == 3 || pwmPIN[i] == A3 || pwmPIN[i] == 11)   pwmPIN_reg[i] = 0b00001000;
    else if(pwmPIN[i] == 4 || pwmPIN[i] == A4 || pwmPIN[i] == 12)   pwmPIN_reg[i] = 0b00010000;
    else if(pwmPIN[i] == 5 || pwmPIN[i] == A5 || pwmPIN[i] == 13)   pwmPIN_reg[i] = 0b00100000;
    else if(pwmPIN[i] == 6)                                         pwmPIN_reg[i] = 0b01000000;
    else if(pwmPIN[i] == 7)                                         pwmPIN_reg[i] = 0b10000000;
    
  }
}

// SETUP OF PIN CHANGE INTERRUPTS

void setup_pwmRead(){
  
  for(int i = 0; i < num_ch; i++){              // run through each input pin
    pciSetup(pwmPIN[i]);                        // enable pinchange interrupt for pin
  }
  pwmPIN_to_port();                             // determines the port for each input pin
                                                // pwmPIN_to_port() also coverts the pin number in pwmPIN[] (i.e. pin 11 or pin A0) to the pin position in the port register (i.e. 0b00000001) for use in the ISR.
  
  if(RC_inputs == 0 || RC_inputs > num_ch) RC_inputs = num_ch;    // define the number of pins connected to an RC receiver.                                          
} 

The code looks very inefficient....All this mess:

can be written in one line:

pwmPIN_reg[i] = 1 << ( pwmPIN[i] % 8);

This certainly cleans it up, I appreciate the input.

After swapping it out, it still seems as though the old Arduino Nano registers are being used. After looking at the ATmega datasheet, I believe that the correct interrupt port for Digital Input 10 is "B" on PCINT4 as show here:

Further down in the code is this interrupt section that I believe is pointing to the older values, though I'm not sure where to start on updating them, seeing as replacing the value with PCINT4 did not provide any output.

ISR(PCINT0_vect){                                                 // this function will run if a pin change is detected on portB
  
  pciTime = micros();                                             // Record the time of the PIN change in microseconds

  for (int i = 0; i < num_ch; i++){                               // run through each of the channels
    if (pwmPIN_port[i] == 0){                                     // if the current channel belongs to portB
      
      if(prev_pinState[i] == 0 && PINB & pwmPIN_reg[i]){          // and the pin state has changed from LOW to HIGH (start of pulse)
        prev_pinState[i] = 1;                                     // record pin state
        pwmPeriod[i] = pciTime - pwmTimer[i];                     // calculate the time period, micro sec, between the current and previous pulse
        pwmTimer[i] = pciTime;                                    // record the start time of the current pulse
      }
      else if (prev_pinState[i] == 1 && !(PINB & pwmPIN_reg[i])){ // or the pin state has changed from HIGH to LOW (end of pulse)
        prev_pinState[i] = 0;                                     // record pin state
        PW[i] = pciTime - pwmTimer[i];                            // calculate the duration of the current pulse
        pwmFlag[i] = HIGH;                                        // flag that new data is available
        if(i+1 == RC_inputs) RC_data_rdy = HIGH;                  
      }
    }
  }
}

Not all pins on the Mega and Mega 2560 boards support pin change interrupts. They can be enabled on 10, 11, 12, 13, 14, 15, 50, 51, 52, 53, A8 (62), A9 (63), A10 (64), A11 (65), A12 (66), A13 (67), A14 (68), A15 (69).

You will need to consider this limitation when trying to map the pins from the Nano.

After doing some additional hunting, it seems others have had similar issues translating these interrupts from the nano to the mega.

You are correct that different pins have to be used, and it seems I have found an interrupt code that works.

//Adding requied libraries
#include <Servo.h>
#include <Wire.h>

#define fromLow 900
#define fromHigh 1900
#define toLow 1000
#define toHigh 2000

#define esc_pin 6
#define servo_pin 5

byte last_channel_1, last_channel_2, last_channel_3, last_channel_4, last_channel_5, last_channel_6;
int receiver_input_channel_1, receiver_input_channel_2, receiver_input_channel_3, receiver_input_channel_4, receiver_input_channel_5, receiver_input_channel_6;
int channel_1, channel_2, channel_3, channel_4, channel_5, channel_6;
int esc, servo;
unsigned long timer_channel_1, timer_channel_2, timer_channel_3, timer_channel_4, timer_channel_5, timer_channel_6;
unsigned long timer_1, timer_2, timer_3, timer_4, timer_5, timer_6, current_time;

 
void setup ()
{

  
  //Set PCIE0 to enable PCMSK2 scan.
  PCICR |= (1 << PCIE2);
  
  PCMSK2 |= (1 << PCINT16); //Set PCINT16 (Analog input A8) to trigger an interrupt on state change.
  PCMSK2 |= (1 << PCINT17); //Set PCINT17 (Analog input A9) to trigger an interrupt on state change.
  PCMSK2 |= (1 << PCINT18); //Set PCINT18 (Analog input A10) to trigger an interrupt on state change.
  PCMSK2 |= (1 << PCINT19); //Set PCINT19 (Analog input A11) to trigger an interrupt on state change.
  PCMSK2 |= (1 << PCINT20); //Set PCINT20 (Analog input A12) to trigger an interrupt on state change.
  PCMSK2 |= (1 << PCINT21); //Set PCINT21 (Analog input A13) to trigger an interrupt on state change.

  Serial.begin(115200);
}

void loop ()
{
  channel_1 = map(receiver_input_channel_1, fromLow, fromHigh, toLow, toHigh);
  channel_2 = map(receiver_input_channel_2, fromLow, fromHigh, toLow, toHigh);
  channel_3 = map(receiver_input_channel_3, fromLow, fromHigh, toLow, toHigh);
  channel_4 = map(receiver_input_channel_4, fromLow, fromHigh, toLow, toHigh);
  channel_5 = map(receiver_input_channel_5, fromLow, fromHigh, toLow, toHigh);
  channel_6 = map(receiver_input_channel_6, fromLow, fromHigh, toLow, toHigh);

// debug
  Serial.print("Channel 1 = ");
  Serial.print(channel_1);
  Serial.print('\t');
  Serial.print("Channel 2 = ");
  Serial.print(channel_2);
  Serial.print('\t');
  Serial.print("Channel 3 = ");
  Serial.print(channel_3);
  Serial.print('\t');
  Serial.print("Channel 4 = ");
  Serial.print(channel_4);
  Serial.print('\t');
  Serial.print("Channel 5 = ");
  Serial.print(channel_5);
  Serial.print('\t');
  Serial.print("Channel 6 = ");
  Serial.print(channel_6);
  Serial.print('\n');

}


ISR(PCINT2_vect)
{
  current_time = micros();
  //Channel 1=========================================
  if(PINK & B00000001)
  {                                        //Is input 8 high?
  if(last_channel_1 == 0)
  {                                   //Input 8 changed from 0 to 1
    last_channel_1 = 1;                                      //Remember current input state
    timer_1 = current_time;                                  //Set timer_1 to current_time
  }
  }
  else if(last_channel_1 == 1)
  {                                //Input 8 is not high and changed from 1 to 0
    last_channel_1 = 0;                                        //Remember current input state
    receiver_input_channel_1 = current_time - timer_1;         //Channel 1 is current_time - timer_1
  }
  //Channel 2=========================================
  if(PINK & B00000010 )
  {                                       //Is input 9 high?
    if(last_channel_2 == 0)
    {                                   //Input 9 changed from 0 to 1
      last_channel_2 = 1;                                      //Remember current input state
      timer_2 = current_time;                                  //Set timer_2 to current_time
    }
  }
  else if(last_channel_2 == 1)
  {                                //Input 9 is not high and changed from 1 to 0
    last_channel_2 = 0;                                        //Remember current input state
    receiver_input_channel_2 = current_time - timer_2;         //Channel 2 is current_time - timer_2
  }
  //Channel 3=========================================
  if(PINK & B00000100 )
  {                                       //Is input 10 high?
    if(last_channel_3 == 0)
    {                                   //Input 10 changed from 0 to 1
      last_channel_3 = 1;                                      //Remember current input state
      timer_3 = current_time;                                  //Set timer_3 to current_time
    }
  }
  else if(last_channel_3 == 1)
  {                                //Input 10 is not high and changed from 1 to 0
    last_channel_3 = 0;                                        //Remember current input state
    receiver_input_channel_3 = current_time - timer_3;         //Channel 3 is current_time - timer_3

  }
  //Channel 4=========================================
  if(PINK & B00001000 )
  {                                       //Is input 11 high?
    if(last_channel_4 == 0)
    {                                   //Input 11 changed from 0 to 1
      last_channel_4 = 1;                                      //Remember current input state
      timer_4 = current_time;                                  //Set timer_4 to current_time
    }
  }
  else if(last_channel_4 == 1)
  {                                //Input 11 is not high and changed from 1 to 0
    last_channel_4 = 0;                                        //Remember current input state
    receiver_input_channel_4 = current_time - timer_4;         //Channel 4 is current_time - timer_4
  }
  //Channel 5=========================================
  if(PINK & B00010000)
  {
    if(last_channel_5 == 0)
    {
      last_channel_5 = 1;
      timer_5 = current_time;        
    }    
  }
  else if(last_channel_5 == 1)
  {
      last_channel_5 = 0;
      receiver_input_channel_5 = current_time - timer_5;
  }
  //Channel 6=========================================
  if(PINK & B00100000)
  {
    if(last_channel_6 == 0)
    {
      last_channel_6 = 1;
      timer_6 = current_time;
    }    
  }
  else if(last_channel_6 == 1)
  {
    last_channel_6 = 0;
    receiver_input_channel_6 = current_time - timer_6;    
  }
}

With this, what would your advice be on integrating these working interupt commands into my existing code?

Are you trying to replace the ISR posted in reply #3 with the ISR posted in reply #5?

You should probably rename your variables to be consistent with what is passed out of the ISR in #3 and used elsewhere in the program. You should probably rewrite the ISR from post#5 to be in array form.

PW[i]  // calculate the duration of the current pulse
pwmFlag[i]

I don't think that the HAL includes PCINT details. For more general code I'd suggest that you replace all hardware specific names and values by constants or #defines. Then use the controller ID from the compilation options to select the right set of names. I don't know where the controller IDs are listed, a look into the firmware sources should reveal more.

The idea with direct hardware addressing was to speed up the interrupt service requests since it doesn't have to parse the pin address from the HAL.

After parsing through the two ISR variations, I think this is the key to getting these to work on the Mega.

PCICR |= (1 << PCIE2);

From there, the IO pins being used can be assigned via their PCINT number, for example:

PCMSK2 |= (1 << PCINT16); //this being analog pin A8

Yeah I think at this rate it will be easier to create my own ISR routine than trying to adapt the existing one for the nano. Plus the array would work better like you said for passing those values

I think this is the key to getting these to work on the Mega.
PCICR |= (1 << PCIE2);

Yes, you did well to find 8 pins all on a common Port Register (K). This simplifies the task of determining the interrupted pin.

I think that you can certainly reconfigure your ISR to create the variables expected by the program.