Hi, I am working on a project in which I need to measure angular velocity (RPM) of two DC motors (with a 5:1 gear ratio) using two magnetic encoders (12 counts per revolution) and an Arduino Nano. The project requires a sampling period as low as possible, for this reason I decided to set it to 10 ms. To realize this, I have developed a code handling the registers of the microcontroller, which should:
- Count the edge changes in the two channels of both encoders via PCINT on ports PC4, PC5, PD4 and PD5.
- Calculate the angular velocity of both motors (the encoder performs 60 counts per revolution with a transmission ratio of 5:1) and send them by USB serial communication every 10 ms through Timer2.
volatile unsigned int changes[2] = {0, 0};
unsigned int rpm[2] = {0, 0};
unsigned int previousRpm[2] = {0, 0} ;
ISR(PCINT2_vect) {
// PD PCINT
changes[1]++;
}
ISR(PCINT1_vect) {
// PC PCINT
changes[0]++;
}
ISR(TIMER2_COMPA_vect) {
for (int i = 0; i < 2; i++) {
rpm[i] = calculateRpm(previousRpm[i], changes[i]);
previousRpm[i] = rpm[i];
changes[i] = 0;
if (i == 0) {
sendRpm('C', changes[i]);
}
if (i == 1) {
sendRpm('D', changes[i]);
}
}
Serial.println();
}
int calculateAverage(int x, int y) {
return (int) (x + y) / 2;
}
int calculateRpm(int pPreviousRPm, int pChanges) {
return (pPreviousRPm == 0) ? (pChanges * 99.5) : calculateAverage(pChanges * 99.5, pPreviousRPm);
}
void sendRpm(char pPort, int pRpm) {
Serial.print(pPort); Serial.print(":"); Serial.print(pRpm); Serial.print(", ");
}
int main(void) {
cli();
//---------------------------------------------------------------------------
// When TCCR2B is non-PWM mode, FOC2A must be set
TCCR2B |= (1 << FOC2A);
//---------------------------------------------------------------------------
// Enable compare match A
TIMSK2 |= (1 << OCIE2A);
//---------------------------------------------------------------------------
// Set CTC mode
TCCR2A |= (1 << WGM21);
//---------------------------------------------------------------------------
// Set prescaler 1024
TCCR2B |= (1 << CS22) | (1 << CS21) | (1 << CS20);
//---------------------------------------------------------------------------
// Set compare register A to 156 to count 10 ms
OCR2A = 156;
//---------------------------------------------------------------------------
// Enable PCMSK1 (PCINT8 to PCINT14) and PCMSK2 (PCINT16 to PCINT23) and
PCICR |= (1 << PCIE2) | (1 << PCIE1);
//---------------------------------------------------------------------------
// PD4, PD5, PC4(A4) and PC5(A5) will trigger interrupt
PCMSK2 |= (1 << PCINT21) | (1 << PCINT20);
PCMSK1 |= (1 << PCINT13) | (1 << PCINT12);
//---------------------------------------------------------------------------
// PD4, PD5, PC4(A4) and PC5(A5) as INPUT
DDRD &= ~(B00110000);
DDRC &= ~(B00110000);
//---------------------------------------------------------------------------
sei();
//---------------------------------------------------------------------------
Serial.begin(9600);
//---------------------------------------------------------------------------
while(1);
}
To perform the tests, I supply the motors with a 6V voltage, driving them to an angular velocity at around 3000 RPM. The problem is that the code gives me this output.
...
20:40:11.444 -> C:0, D:0,
20:40:11.477 -> C:0, D:0,
20:40:11.477 -> C:0, D:0,
20:40:11.477 -> C:0, D:0,
20:40:11.510 -> C:0, D:0,
20:40:11.510 -> C:0, D:0,
20:40:11.510 -> C:0, D:0,
20:40:11.544 -> C:0, D:0,
20:40:11.544 -> C:0, D:0,
20:40:11.577 -> C:0, D:0,
20:40:11.577 -> C:0, D:0,
20:40:11.577 -> C:0, D:0,
20:40:11.610 -> C:0, D:0,
20:40:11.610 -> C:0, D:0,
20:40:11.610 -> C:0, D:0,
20:40:11.643 -> C:0, D:0,
20:40:11.643 -> C:0, D:0,
20:40:11.676 -> C:0, D:0,
20:40:11.676 -> C:0, D:0,
20:40:11.676 -> C:0, D:0,
20:40:11.709 -> C:0, D:0,
...
Initially, I set the sampling period using the Timer0, but the same problem occurred, but looking at forums I found that altering the Timer0, could affect the USB serial communication, so I decided to do it with the Timer2.
PS: I can' t set the sampling period with Timer1, because with this Timer I am generating two PWM signals for a dual-channel DC motor driver.
