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...
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.
@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.
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)];
}
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
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 ?
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.
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.
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.