Sketch works on Duemilanove but not Mega2560

I have a sketch that works perfectly on the Duemilanove, but does not work on a Mega2560. It’s the same sketch and the same pins are used. So, the question is, how is the Mega2560 different in such a way that it would cause this sketch not to work?

The project involves interfacing with a little breakout board called the Rotary Encoder LED Ring. This is a little board with a rotary switch on it that lights up different LEDs depending on the position of the knob. The device is here: http://mayhewlabs.com/products/rotary-encoder-led-ring

I am using their example sketch and wiring it exactly the way they say to. Works great on my Duemilanove, but when I set it up in an identical fashion, using the same pins, on my Mega2560 (which is where I really need it for this project), the LEDs in the ring do not light up. The sketch compiles and downloads to the Mega2560 with out error, and the one LED below the switch does come on, but none of the LEDs in the ring come on. I’m stumped why I can’t get it to work on the Mega2560. I assume there must be some difference in the two boards but I can’t figure out what it is. Mayhew Labs didn’t know, but they said that perhaps the pins “have different names” (not sure what that means) or “use a different port.” The code references pins 2, 3, 4, 8, 9, and 10 are used (which are the pins I’ve plugged the wires into). And it appears that 8,9,10 correspond to “PINB” in the sketch, but I don’t know if the Mega2560 uses that same port or not.

Here is the example code I’m using, which was provided by the company:

/*
          Rotary_Encoder_LED_Ring_Example
          www.mayhewlabs.com/products/rotary-encoder-led-ring 
          Copyright (c) 2011 Mayhew Labs.
          Written by Mark Mayhew
          
This example shows 3 sequences that are possible on the ring of LEDs around the encoder based on rotation of an encoder.  The 3 sequences are 
selected by entering 1, 2, or 3 in the serial command prompt.  The serial window shows the current rotary encoder count.  As the encoder is turned, 
the serial display shows a raw count of the rotary encoder's value and the LEDs show a scaled version of the value.  If the button on the rotary 
encoder is pushed, the bottom LED will come on.  Each section of code below discusses the process required to do this. 

A note on setting the output sequence:
Think of each LED as a single bit in a 16-bit long binary string.  If a bit is 1, the LED is on, if a bit is 0, the LED is off.  
By making a string of ones and zeros, we choose which LEDs to have on and off, and then send this string to the shift register to display it.
For example 1000000000000001 binary (0x8001 in hex) will have the fist and last LEDs on, the rest off.  

CREDIT:
Reading the rotary encoder is performed with Oleg's example code:a
http://www.circuitsathome.com/mcu/reading-rotary-encoder-on-arduino
*/

//These are the pins that will talk to the shift register through SPI
#define SDI    2   //Data 2
#define CLK    3    //Clock 3
#define LE     4    //Latch 4

//These are the rotary encoder pins A, B, and switch
#define ENC_A    8  // 8
#define ENC_B    9  //9 
#define ENC_SW   10  // 10
#define ENC_PORT PINB  //The port that the rotary encoder is on (see http://www.arduino.cc/en/Reference/PortManipulation)

// Global variables
int scaledCounter = 0;  //The LED output is based on a scaled veryson of the rotary encoder counter
int sequenceNumber=0;   //The output sequence for the LEDs
int incomingByte = 0;   //Serial input to select LED output sequence


/*This is a 2 dimensional array with 3 LED sequences.  The outer array is the sequence; 
  the inner arrays are the values to output at each step of each sequence.  The output values
  are 16 bit hex values (hex math is actually easier here!).  An LED will be on if its 
  corresponding binary bit is a one, for example: 0x7 = 0000000000000111 and the first 3 LEDs 
  will be on.
  
  The data type must be 'unsigned int' if the sequence uses the bottom LED since it's value is 0x8000 (out of range for signed int).
*/
unsigned int sequence[3][16] = {{0x0,0x1,0x2,0x4,0x8,0x10,0x20,0x40,0x80,0x100,0x200,0x400,0x800,0x1000,0x2000,0x4000},
                                {0x0,0x1,0x3,0x7,0xf,0x1f,0x3f,0x7f,0xff,0x1ff,0x3ff,0x7ff,0xfff,0x1fff,0x3fff,0x7fff},
                                {0x0,0x7fff,0x3ffe,0x1ffc,0xff8,0x7f0,0x3e0,0x1c0,0x80,0x1c0,0x3e0,0x7f0,0xff8,0x1ffC,0x3ffe,0x7fff},
                               };

void setup()
{
  //Set SPI pins to output
  pinMode(SDI, OUTPUT);
  pinMode(CLK, OUTPUT);
  pinMode(LE,OUTPUT);
  //Set encoder pins to input, turn internal pull-ups on
  pinMode(ENC_A, INPUT);
  digitalWrite(ENC_A, HIGH);
  pinMode(ENC_B, INPUT);
  digitalWrite(ENC_B, HIGH);
  pinMode(ENC_SW, INPUT);
  digitalWrite(ENC_SW, HIGH);
  //Set serial rate, prompt for desired sequence
  Serial.begin(115200);
  Serial.println("Enter 1, 2, or 3 to change the LED sequence");
}


void loop()
{ 
  //Local Variables
  static uint8_t counter = 0;      //this variable will be changed by encoder input
  int8_t tmpdata;
  
  //Get any serial input
  if (Serial.available() > 0) 
  {
    incomingByte = Serial.read();
  }
  
  //if the serial input is valid, set the LED output sequence appropriately 
  if (incomingByte == '1')
    sequenceNumber=0;
  if (incomingByte == '2')
    sequenceNumber=1;
  if (incomingByte == '3')
    sequenceNumber=2;
    
  //Call read_encoder() function    
  tmpdata = read_encoder();
  
  //if the encoder has moved
  if(tmpdata) 
    {
      //Print out the counter value
      //Serial.print("Counter value: ");
      //Serial.println(counter, DEC);
      //Set the new counter value of the encoder
      
      counter += tmpdata;      
      
     // if (counter==64) {counter = 0;}  // This will cause it to always show only one light
     if (counter==128) {counter = 0;}  // This will cause it to always show only one light
     if (counter==255) {counter = 127;}
      
      
      //Scale the counter value for referencing the LED sequence
      //***NOTE: Change the map() function to suit the limits of your rotary encoder application.
      //         The first two numbers are the lower, upper limit of the rotary encoder, the 
      //         second two numbers 0 and 14 are limits of LED sequence arrays.  This is done
      //         so that the LEDs can use a different scaling than the encoder value. 


      scaledCounter = map(counter,0,100,0,25);  // provides one for one match up

      Serial.print(counter,DEC);
      Serial.print("  ");      
      Serial.println(scaledCounter, DEC);
      
      //scaledCounter = map(counter,0,100,0,15); original
      
      //Send the LED output to the shift register 
      digitalWrite(LE,LOW);
      shiftOut(SDI,CLK,MSBFIRST,(sequence[sequenceNumber][scaledCounter] >> 8));    //High byte first
      shiftOut(SDI,CLK,MSBFIRST,sequence[sequenceNumber][scaledCounter]);           //Low byte second
      digitalWrite(LE,HIGH);
    }
  
  //If the encoder switch is pushed, this will turn on the bottom LED.  The bottom LED is turned
  //on by 'OR-ing' the current display with 0x8000 (1000000000000000 in binary)
  if (!digitalRead(ENC_SW))
  {
    digitalWrite(LE,LOW);  
    shiftOut(SDI,CLK,MSBFIRST,((sequence[sequenceNumber][scaledCounter]|0x8000) >> 8));
    shiftOut(SDI,CLK,MSBFIRST,sequence[sequenceNumber][scaledCounter]);              
    digitalWrite(LE,HIGH);   
  }
}



/*************************************************************************
*    read_encoder() function as provided by Oleg:                        *
*    http://www.circuitsathome.com/mcu/reading-rotary-encoder-on-arduino *
*                                                                        *
*    Returns change in encoder state (-1,0,1)                            *
************************************************************************ */
int8_t read_encoder()
{
  int8_t enc_states[] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0};
  static uint8_t old_AB = 0;
  /**/
  old_AB <<= 2;                   //remember previous state
  old_AB |= ( ENC_PORT & 0x03 );  //add current state
  return ( enc_states[( old_AB & 0x0f )]);
}

Port B0-B7 on the Mega2560 is pins 53, 52, 51, 50, 10,11,12,13

Try moving your connections?

The use of direct port access in the ISR of that sketch:

#define ENC_PORT PINB //The port that the rotary encoder is on
.
.

/*************************************************************************

  • read_encoder() function as provided by Oleg: *
  • http://www.circuitsathome.com/mcu/reading-rotary-encoder-on-arduino *
  • Returns change in encoder state (-1,0,1) *
    ************************************************************************ */
    int8_t read_encoder()
    {
    int8_t enc_states = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0};
    static uint8_t old_AB = 0;
    /**/
    old_AB <<= 2; //remember previous state
    old_AB |= ( ENC_PORT & 0x03 ); //add current state
    return ( enc_states[( old_AB & 0x0f )]);
    }

Causes the sketch to not be portable between 328p chips and mega1280/2560 chips. The sketch can be modified to work on a mega board by changing the port names to the pins being used on the mega board.

Lefty

Crossroads and Retroleft:

It seems like we're on the right track, but I don't have a clear understanding of ports, pins, and their relationship to the source code. I've tried a couple of things that I thought would work, but they did not, which means I don't understand it yet. I have an open Mega2560 so I'm flexible with pin placement. With your understanding of this subject, could you recommend the pins I should use and the corresponding changes to the source code so that I use the right pins and match them up properly in the source code? I would greatly appreciate it.

OK, try this. Change:

//These are the rotary encoder pins A, B, and switch
#define ENC_A    8  // 8
#define ENC_B    9  //9 
#define ENC_SW   10  // 10
#define ENC_PORT PINB  //The port that the rotary encoder is on (see http://www.arduino.cc/en/Reference/PortManipulation)

To:

//These are the rotary encoder pins A, B, and switch
#define ENC_A    22  // wire channel a to arduino pin 22
#define ENC_B    23  //  wire channel b to arduino pin 23 
#define ENC_SW   10  // 10
#define ENC_PORT PINA  //The port that the rotary encoder is on (see http://www.arduino.cc/en/Reference/PortManipulation)

I think that is all that is required, but of course untested.

Lefty

And there was much rejoicing! :)

It works perfectly! Thank you.

rb451: And there was much rejoicing! :)

It works perfectly! Thank you.

Great.

For extra credit, there is a way of course to use conditional statements and make the sketch up-loadable and runnable for either a 328p or 1280/2560 based board, and leave comments about what pins to wire to depending on which chip is used. I typically develop on a arduino 328p based board and once working, add the conditional changes and comment to make it work also on my Seeeduino meg board.

Lefty