Arduino nano code to arduino nano every code

Hello,

i have found a program for controlling a motor. Unfortunately, i have bought the wrong board and i want to "translate" my code:

//set as inputs
    DDRD &= ~(1<<LEFT_ENC_PIN_A);
    DDRD &= ~(1<<LEFT_ENC_PIN_B);
    DDRC &= ~(1<<RIGHT_ENC_PIN_A);
    DDRC &= ~(1<<RIGHT_ENC_PIN_B);
    
    //enable pull up resistors
    PORTD |= (1<<LEFT_ENC_PIN_A);
    PORTD |= (1<<LEFT_ENC_PIN_B);
    PORTC |= (1<<RIGHT_ENC_PIN_A);
    PORTC |= (1<<RIGHT_ENC_PIN_B);
    
    // tell pin change mask to listen to left encoder pins
    PCMSK2 |= (1 << LEFT_ENC_PIN_A)|(1 << LEFT_ENC_PIN_B);
    // tell pin change mask to listen to right encoder pins
    PCMSK1 |= (1 << RIGHT_ENC_PIN_A)|(1 << RIGHT_ENC_PIN_B);
    
    // enable PCINT1 and PCINT2 interrupt in the general interrupt mask
    PCICR |= (1 << PCIE1) | (1 << PCIE2);

I have done some research and here is the result:
The nano every seems to have a ATMega4809 as micro Controller and it does change the way to configure the register, i think.
I have read here and there that i have to put VPORTD.DIR instead of DDRD, for example, but i don't know if it's good and even if it's good, i don't know how to deal with PCMSK2, PCMSK1 and how to enable pullup resistor...

I add the message concerning the errors during build:

/home/franck/Documents/ros_arduino_bridge/ROSArduinoBridge/ROSArduinoBridge.ino: In function 'void setup()':
/home/franck/Documents/ros_arduino_bridge/ROSArduinoBridge/ROSArduinoBridge.ino:268:5: error: 'PCMSK2' was not declared in this scope
PCMSK2 |= (1 << LEFT_ENC_PIN_A)|(1 << LEFT_ENC_PIN_B);
^~~~~~
/home/franck/Documents/ros_arduino_bridge/ROSArduinoBridge/ROSArduinoBridge.ino:270:5: error: 'PCMSK1' was not declared in this scope
PCMSK1 |= (1 << RIGHT_ENC_PIN_A)|(1 << RIGHT_ENC_PIN_B);
^~~~~~
/home/franck/Documents/ros_arduino_bridge/ROSArduinoBridge/ROSArduinoBridge.ino:273:5: error: 'PCICR' was not declared in this scope
PCICR |= (1 << PCIE1) | (1 << PCIE2);
^~~~~
/home/franck/Documents/ros_arduino_bridge/ROSArduinoBridge/ROSArduinoBridge.ino:273:20: error: 'PCIE1' was not declared in this scope
PCICR |= (1 << PCIE1) | (1 << PCIE2);
^~~~~
/home/franck/Documents/ros_arduino_bridge/ROSArduinoBridge/ROSArduinoBridge.ino:273:20: note: suggested alternative: 'PIN1'
PCICR |= (1 << PCIE1) | (1 << PCIE2);
^~~~~
PIN1
/home/franck/Documents/ros_arduino_bridge/ROSArduinoBridge/ROSArduinoBridge.ino:273:35: error: 'PCIE2' was not declared in this scope
PCICR |= (1 << PCIE1) | (1 << PCIE2);
^~~~~
/home/franck/Documents/ros_arduino_bridge/ROSArduinoBridge/ROSArduinoBridge.ino:273:35: note: suggested alternative: 'PIN2'
PCICR |= (1 << PCIE1) | (1 << PCIE2);
^~~~~
PIN2
/home/franck/Documents/ros_arduino_bridge/ROSArduinoBridge/encoder_driver.ino: In function 'void PCINT2_vect()':
/home/franck/Documents/ros_arduino_bridge/ROSArduinoBridge/encoder_driver.ino:40:15: error: 'PIND' was not declared in this scope
enc_last |= (PIND & (3 << 2)) >> 2; //read the current state into lowest 2 bits
^~~~
/home/franck/Documents/ros_arduino_bridge/ROSArduinoBridge/encoder_driver.ino:40:15: note: suggested alternative: 'PIN0'
enc_last |= (PIND & (3 << 2)) >> 2; //read the current state into lowest 2 bits
^~~~
PIN0
/home/franck/Documents/ros_arduino_bridge/ROSArduinoBridge/encoder_driver.ino: In function 'void PCINT1_vect()':
/home/franck/Documents/ros_arduino_bridge/ROSArduinoBridge/encoder_driver.ino:50:15: error: 'PINC' was not declared in this scope
enc_last |= (PINC & (3 << 4)) >> 4; //read the current state into lowest 2 bits
^~~~
/home/franck/Documents/ros_arduino_bridge/ROSArduinoBridge/encoder_driver.ino:50:15: note: suggested alternative: 'PIN0'
enc_last |= (PINC & (3 << 4)) >> 4; //read the current state into lowest 2 bits
^~~~
PIN0

I manage to use ATMEGA328 registers emulation to clear the errors on DDRD, DDRC, PORTD and PORTC but there is still errors, as you can see...

Just placing this here for reference...

1 Like

This feels to me like unnecessary use of direct port manipulation. Some people use it when there is no genuine need to, because they think it makes them look clever.

Direct port manipulation can be useful when maximum execution speed is genuinely needed, but the downside is that the code is not portable between chips.

This can be replaced with pinMode(pin, INPUT_PULLUP). You will need to identify the correct Arduino pin numbers.

This code uses pin change interrupts probably because 4 are needed, and atmega328 has only 2 external interrupt pins. ATMega4809 probably has enough external interrupt pins that pin change interrupts do not need to be used.

EDIT: apparently Nano Every/ATMega4809 has external interrupt capability on every pin, so definitely no need to use pin change interrupts.

1 Like

@fpalaric if you want more than general advice, you are going to need to post your whole code, because more than just the lines you posted will need to be changed.

1 Like

Hello,

thank you for your help !

the whole code is available here:

The other part that is sending me errors is in encoder_driver.ino, especially this part:

  /* Interrupt routine for LEFT encoder, taking care of actual counting */
  ISR (PCINT2_vect){
  	static uint8_t enc_last=0;
        
	enc_last <<=2; //shift previous state two places
	enc_last |= (PIND & (3 << 2)) >> 2; //read the current state into lowest 2 bits
  
  	left_enc_pos += ENC_STATES[(enc_last & 0x0f)];
  }
  
  /* Interrupt routine for RIGHT encoder, taking care of actual counting */
  ISR (PCINT1_vect){
        static uint8_t enc_last=0;
          	
	enc_last <<=2; //shift previous state two places
	enc_last |= (PINC & (3 << 4)) >> 4; //read the current state into lowest 2 bits
  
  	right_enc_pos += ENC_STATES[(enc_last & 0x0f)];
  }

EDIT: This code has a bug, use version in post #16

This is too complicated to type on my phone, but something along these lines:

const byte encLeftAPin = 5;
const byte encLeftBPin = 6;
const byte encRightAPin = 7;
const byte encRightBPin = 8;

volatile long encLeftValue;
volatile long encRightValue;

void encLeftAInterrupt() {
  if (digitalRead(encLeftAPin) == HIGH)
    if (digitalRead(encLeftBPin) == LOW)
      encLeftValue++;
    else
      encLeftValue--;
  else
    if (digitalRead(encLeftBPin) == LOW)
      encLeftValue--;
    else
      encLeftValue++;
}

void encLeftBInterrupt()  {
  if (digitalRead(encLeftBPin) == HIGH)
    if (digitalRead(encLeftAPin) == LOW)
      encLeftValue++;
    else
      encLeftValue--;
  else
    if (digitalRead(encLeftAPin) == LOW)
      encLeftValue--;
    else
      encLeftValue++;
}

void encRightAInterrupt() {
  if (digitalRead(encRightAPin) == HIGH)
    if (digitalRead(encRightBPin) == LOW)
      encRightValue++;
    else
      encRightValue--;
  else
    if (digitalRead(encRightBPin) == LOW)
      encRightValue--;
    else
      encRightValue++;
}

void encRightBInterrupt() {
  if (digitalRead(encRightBPin) == HIGH)
    if (digitalRead(encRightAPin) == LOW)
      encRightValue++;
    else
      encRightValue--;
  else
    if (digitalRead(encRightAPin) == LOW)
      encRightValue--;
    else
      encRightValue++;
}

void setup() {
  ...
  pinMode(encLeftAPin, INPUT_PULLUP);
  pinMode(encLeftBPin, INPUT_PULLUP);
  pinMode(encRightAPin, INPUT_PULLUP);
  pinMode(encRightBPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(encLeftAPin), encLeftAInterrupt, CHANGE);
  attachInterrupt(digitalPinToInterrupt(encLeftBPin), encLeftBInterrupt, CHANGE);
  attachInterrupt(digitalPinToInterrupt(encRightAPin), encRightAInterrupt, CHANGE);
  attachInterrupt(digitalPinToInterrupt(encRightBPin), encRightBInterrupt, CHANGE);
  ...
}
1 Like

EDIT: don't use this code, it has a bug, and I decided I don't like it anyways!

Can shorten it a little also:

const byte encLeftAPin = 5;
const byte encLeftBPin = 6;
const byte encRightAPin = 7;
const byte encRightBPin = 8;

volatile long encLeftValue;
volatile long encRightValue;

void encInterrupt(byte pinChanged, byte pinUnchanged, long *encValue) {
  if (digitalRead(pinChanged) == HIGH)
    if (digitalRead(pinUnchanged) == LOW)
      (*encValue)++;
    else
      (*encValue)--;
  else
    if (digitalRead(pinUnchanged) == LOW)
      (*encValue)--;
    else
      (*encValue)++;
}

void encLeftAInterrupt() 
  encInterrupt(encLeftAPin, encLeftBPin, &encLeftValue);

void encLeftBInterrupt() 
  encInterrupt(encLeftBPin, encLeftAPin, &encLeftValue);

void encRightAInterrupt() 
  encInterrupt(encRightAPin, encRightBPin, &encRightValue);

void encRightBInterrupt() 
  encInterrupt(encRightBPin, encRightAPin, &encRightValue);

void setup() {
  ...
  pinMode(encLeftAPin, INPUT_PULLUP);
  pinMode(encLeftBPin, INPUT_PULLUP);
  pinMode(encRightAPin, INPUT_PULLUP);
  pinMode(encRightBPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(encLeftAPin), encLeftAInterrupt, CHANGE);
  attachInterrupt(digitalPinToInterrupt(encLeftBPin), encLeftBInterrupt, CHANGE);
  attachInterrupt(digitalPinToInterrupt(encRightAPin), encRightAInterrupt, CHANGE);
  attachInterrupt(digitalPinToInterrupt(encRightBPin), encRightBInterrupt, CHANGE);
  ...
}
1 Like

Yes it seems to build !

I have some questions:

Why you don't use this:

 static const int8_t ENC_STATES [] = {0,1,-1,0,-1,0,0,1,1,0,0,-1,0,-1,1,0};  //encoder lookup table

Are you sure of your "CHANGE" in:

attachInterrupt(digitalPinToInterrupt(encLeftAPin), encLeftAInterrupt, CHANGE);

If think i have to search in the datasheet of my encoder to answer at my questions but if you are sure of your answer, i am open to explanations :slight_smile:

Another question:
I have understand that the idea behind port manipulation, as it was done before, was to fast speed up the execution.
Do you think your proposition will not degrade too much the performance of the controller ?

Anyway, thank you very much for your help !

My suggested code performs the same logic (I hope). My code may be less efficient because it uses digitalRead(), but it will run on Nano Every, and other types of Arduino, not only atmega328.

Yes. The code you copied used pin-change interrupts, which fire on both rising and falling signal edges. CHANGE is the equivalent for external interrupts.

I'm not sure. My suggested code uses digitalRead(), which is slower than direct port manipulation. But my code is simpler, and simpler is often faster, and external interrupts can be faster than pin change interrupts, so maybe both codes run at similar speed. But this only matters depending on the speed of your motors and the frequency of the interrupts generated by your encoders. Maybe either code will be fast enough. Over to you for testing.

encoder

Going from left to right:
A: Rising, other low
B: Rising, other high
C: Falling, other high
D: Falling, other low

All combinations need to increase the value. I think you oversimplified your code too much.

Yes

Please explain. Which edges do you think it will miss?

For case B and D the code is decreasing encValue. I think when xxBPin changes, the logic needs to be inverted.

Ah, ok, do you think the code in post #7 has the same problem, or only the code in post #8?

Yes. The fix is easy. In encLeftBInterrupt() change the first comparission from HIGH to LOW.

The same for encRightBInterrupt().

1 Like

Corrected version of post #7:

const byte encLeftAPin = 5;
const byte encLeftBPin = 6;
const byte encRightAPin = 7;
const byte encRightBPin = 8;

volatile long encLeftValue;
volatile long encRightValue;

void encLeftAInterrupt() {
  if (digitalRead(encLeftAPin) == HIGH)
    if (digitalRead(encLeftBPin) == LOW)
      encLeftValue++;
    else
      encLeftValue--;
  else
    if (digitalRead(encLeftBPin) == LOW)
      encLeftValue--;
    else
      encLeftValue++;
}

void encLeftBInterrupt()  {
  if (digitalRead(encLeftBPin) == LOW)
    if (digitalRead(encLeftAPin) == LOW)
      encLeftValue++;
    else
      encLeftValue--;
  else
    if (digitalRead(encLeftAPin) == LOW)
      encLeftValue--;
    else
      encLeftValue++;
}

void encRightAInterrupt() {
  if (digitalRead(encRightAPin) == HIGH)
    if (digitalRead(encRightBPin) == LOW)
      encRightValue++;
    else
      encRightValue--;
  else
    if (digitalRead(encRightBPin) == LOW)
      encRightValue--;
    else
      encRightValue++;
}

void encRightBInterrupt() {
  if (digitalRead(encRightBPin) == LOW)
    if (digitalRead(encRightAPin) == LOW)
      encRightValue++;
    else
      encRightValue--;
  else
    if (digitalRead(encRightAPin) == LOW)
      encRightValue--;
    else
      encRightValue++;
}

void setup() {
  ...
  pinMode(encLeftAPin, INPUT_PULLUP);
  pinMode(encLeftBPin, INPUT_PULLUP);
  pinMode(encRightAPin, INPUT_PULLUP);
  pinMode(encRightBPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(encLeftAPin), encLeftAInterrupt, CHANGE);
  attachInterrupt(digitalPinToInterrupt(encLeftBPin), encLeftBInterrupt, CHANGE);
  attachInterrupt(digitalPinToInterrupt(encRightAPin), encRightAInterrupt, CHANGE);
  attachInterrupt(digitalPinToInterrupt(encRightBPin), encRightBInterrupt, CHANGE);
  ...
}

@Rintin better?

The more I think about the code in post #8, the less I like it. Sure, it's shorter, less repetition. But not that much shorter, and it's significantly more difficult to understand, especially for a beginner. It's also going to be less efficient because of the extra layer of function calls. So I won't bother to fix that, I'll just mark it as having a bug.

1 Like

Yes better.

I would probably use a lookup table now. Creating with the previous pin state and the current pin state an index into a lookup table:

//                  00, 01, 10, 11  -> New state
int8_t lookup[] =  { 0,  1, -1,  0   // Last state 00
                   ,-1,  0,  0,  1   // Last state 01
                   , 1,  0,  0, -1   // Last state 10
                   , 0, -1,  1,  0}; // Last state 11


curr = 0;
if (digitalRead(xxxAPin)) curr |= 0b10;
if (digitalRead(xxxBPin)) curr |= 0b01;

idx = (last << 2) | curr;
last = curr;

value += lookup[idx];
1 Like

Thanks for your help spotting & fixing that. Seems I didn't over-simplify the code, just made a logic error.

That's back to being like the original code. I didn't like that because it seemed over-complex and confusing for beginners, and I wanted to simplify it.

I'm not sure if nested if's are easier to understand.

@fpalaric can you help us here? What is easier to understand for handling rotary encoders?

  • nested if's
  • a lookup table

The nested if's is easier to understand, more intuitive but thank you for your help, i understand the lookup table way now !

I have one question @PaulRB

const byte encLeftAPin = 5;
const byte encLeftBPin = 6;
const byte encRightAPin = 7;
const byte encRightBPin = 8;

volatile long encLeftValue;
volatile long encRightValue;

void encLeftAInterrupt() {
  if (digitalRead(encLeftAPin) == HIGH)
    if (digitalRead(encLeftBPin) == LOW)
      encLeftValue++;
    else
      encLeftValue--;
  else
    if (digitalRead(encLeftBPin) == LOW)
      encLeftValue--;
    else
      encLeftValue++;
}

void encLeftBInterrupt()  {
  if (digitalRead(encLeftBPin) == LOW)
    if (digitalRead(encLeftAPin) == LOW)
      encLeftValue++;
    else
      encLeftValue--;
  else
    if (digitalRead(encLeftAPin) == LOW)
      encLeftValue--;
    else
      encLeftValue++;
}

void encRightAInterrupt() {
  if (digitalRead(encRightAPin) == HIGH)
    if (digitalRead(encRightBPin) == LOW)
      encRightValue++;
    else
      encRightValue--;
  else
    if (digitalRead(encRightBPin) == LOW)
      encRightValue--;
    else
      encRightValue++;
}

void encRightBInterrupt() {
  if (digitalRead(encRightBPin) == LOW)
    if (digitalRead(encRightAPin) == LOW)
      encRightValue++;
    else
      encRightValue--;
  else
    if (digitalRead(encRightAPin) == LOW)
      encRightValue--;
    else
      encRightValue++;
}

void setup() {
  ...
  pinMode(encLeftAPin, INPUT_PULLUP);
  pinMode(encLeftBPin, INPUT_PULLUP);
  pinMode(encRightAPin, INPUT_PULLUP);
  pinMode(encRightBPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(encLeftAPin), encLeftAInterrupt, CHANGE);
  attachInterrupt(digitalPinToInterrupt(encLeftBPin), encLeftBInterrupt, CHANGE);
  attachInterrupt(digitalPinToInterrupt(encRightAPin), encRightAInterrupt, CHANGE);
  attachInterrupt(digitalPinToInterrupt(encRightBPin), encRightBInterrupt, CHANGE);
  ...
}

Why you don't use pointers here ?

I would have done somethink like that:

const byte encLeftAPin = 5;
const byte encLeftBPin = 6;
const byte encRightAPin = 7;
const byte encRightBPin = 8;

volatile long encLeftValue;
volatile long encRightValue;

void encLeftAInterrupt() {
  if (digitalRead(encLeftAPin) == HIGH)
    if (digitalRead(encLeftBPin) == LOW)
      *encLeftValue++;
    else
      *encLeftValue--;
  else
    if (digitalRead(encLeftBPin) == LOW)
      *encLeftValue--;
    else
      *encLeftValue++;
}

void encLeftBInterrupt()  {
  if (digitalRead(encLeftBPin) == LOW)
    if (digitalRead(encLeftAPin) == LOW)
     *encLeftValue++;
    else
      *encLeftValue--;
  else
    if (digitalRead(encLeftAPin) == LOW)
      *encLeftValue--;
    else
      *encLeftValue++;
}

void encRightAInterrupt() {
  if (digitalRead(encRightAPin) == HIGH)
    if (digitalRead(encRightBPin) == LOW)
      *encRightValue++;
    else
      *encRightValue--;
  else
    if (digitalRead(encRightBPin) == LOW)
      *encRightValue--;
    else
      *encRightValue++;
}

void encRightBInterrupt() {
  if (digitalRead(encRightBPin) == LOW)
    if (digitalRead(encRightAPin) == LOW)
      *encRightValue++;
    else
      *encRightValue--;
  else
    if (digitalRead(encRightAPin) == LOW)
      *encRightValue--;
    else
      *encRightValue++;
}

void setup() {
  ...
  pinMode(encLeftAPin, INPUT_PULLUP);
  pinMode(encLeftBPin, INPUT_PULLUP);
  pinMode(encRightAPin, INPUT_PULLUP);
  pinMode(encRightBPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(encLeftAPin), encLeftAInterrupt, CHANGE);
  attachInterrupt(digitalPinToInterrupt(encLeftBPin), encLeftBInterrupt, CHANGE);
  attachInterrupt(digitalPinToInterrupt(encRightAPin), encRightAInterrupt, CHANGE);
  attachInterrupt(digitalPinToInterrupt(encRightBPin), encRightBInterrupt, CHANGE);
  ...
}