I'm trying to read the RPM of a 6V micro geared motor that has a 298:1 gearbox (Micro Metal Gearmotor (Extended back shaft) - 298:1), using a magnetic encoder from Pololu (Pololu - Magnetic Encoder Pair Kit for Micro Metal Gearmotors, 12 CPR, 2.7-18V)
Motor datasheet: https://tinyurl.com/ybrmdaqk
I'm driving the motor using a DRV8833 also from pololu.
The motor power is supplied via 2x lithium-ion batteries, that when charged output 7.4v. Power to the motor is about 7.2v due to the driver dropout.
The magnetic encoder is attached to the motor shaft and produces 12 counts per revolution. Because of the 298:1 gearbox on the output shaft there should be 12*298 pulses per revolution of the output shaft, 3576 pulses per revolution.
I'm using an STM32 BluePill with the Official Arduino Core from STM (GitHub - stm32duino/Arduino_Core_STM32: STM32 core support for Arduino)
This is the code I have at the moment to measure the pulses and read out the RPM of two of these motors:
// MOTOR & ENCODER TEST
// References:
// Timer interrupt: http://stm32duino.com/viewtopic.php?f=48&t=4120
// Maximum PWM Value based on 16 bit timer
const int MAX_PWM = 65535;
// DRV8833 motor driver pins
const int M_L_in1Pin = PB8;
const int M_L_in2Pin = PB9;
const int M_R_in1Pin = PB6;
const int M_R_in2Pin = PB7;
const int M_SleepPin = PB5;
// Pololu magnetic encoder pins
const int M_R_EncoderA = PB12;
const int M_R_EncoderB = PB13;
const int M_L_EncoderA = PB14;
const int M_L_EncoderB = PB15;
// Pololu magnetic encoder variables
static stimer_t TimHandle; // Handler for stimer
const float M_Encoder_PulsesPerTurn = 3576.0; // 12 pulses per turn of magnetic disk on motor, 298:1 gearbox, 3576 turns of magnetic disk per 1 turn of output shaft
volatile unsigned long M_L_Count = 0; // Variable to hold encoder counts
volatile unsigned long M_R_Count = 0; // Variable to hold encoder counts
float M_L_RPM = 0; // Variable to hold left motor RPM
float M_R_RPM = 0; // Variable to hold right motor RPM
// FUNCTIONS
void M_L_Encoder_Event(){
// Left Motor
if (digitalRead(M_L_EncoderA) == HIGH) {
if (digitalRead(M_L_EncoderB) == LOW) {
M_L_Count++;
} else {
M_L_Count--;
}
} else {
if (digitalRead(M_L_EncoderB) == LOW) {
M_L_Count--;
} else {
M_L_Count++;
}
}
}
void M_R_Encoder_Event(){
// Right Motor
if (digitalRead(M_R_EncoderA) == HIGH) {
if (digitalRead(M_R_EncoderB) == LOW) {
M_R_Count--;
} else {
M_R_Count++;
}
} else {
if (digitalRead(M_R_EncoderB) == LOW) {
M_R_Count++;
} else {
M_R_Count--;
}
}
}
void calculate_RPM(stimer_t *htim){ // Ran every 10milliseconds
unsigned int M_L_Pulses;
unsigned int M_R_Pulses;
noInterrupts(); // Disable interrupts
M_L_Pulses = M_L_Count;
M_R_Pulses = M_R_Count;
M_L_Count = 0;
M_R_Count = 0;
interrupts(); // Re-Enable interrupts
// Pulses multiplied by 100 because interrupt every 10milliseconds (Convert to pulses per second)
// Divide by pulses per turn of output shaft.
// Multiply by 60 to go from seconds to minutes
M_L_RPM = ((M_L_Pulses * 100.0) / M_Encoder_PulsesPerTurn) * 60.0;
M_R_RPM = ((M_R_Pulses * 100.0) / M_Encoder_PulsesPerTurn) * 60.0;
}
void setup() {
// Start serial connection
Serial.begin(115200);
// pinModes
pinMode(LED_BUILTIN, OUTPUT);
pinMode(M_L_EncoderA, INPUT);
pinMode(M_L_EncoderB, INPUT);
pinMode(M_R_EncoderA, INPUT);
pinMode(M_R_EncoderB, INPUT);
pinMode(M_L_in1Pin, OUTPUT);
pinMode(M_L_in2Pin, OUTPUT);
pinMode(M_R_in1Pin, OUTPUT);
pinMode(M_R_in2Pin, OUTPUT);
pinMode(M_SleepPin, OUTPUT);
// Set resolutions based on STM32F103
analogWriteResolution(16); // Set to 16-bit timer
// Disable motor controller sleep
digitalWrite(M_SleepPin, HIGH);
// Encoder Interrupts
attachInterrupt(M_L_EncoderA, M_L_Encoder_Event, CHANGE);
attachInterrupt(M_R_EncoderA, M_R_Encoder_Event, CHANGE);
// Setup timer
TimHandle.timer = TIM1;
// Timer set to 10ms
TimerHandleInit(&TimHandle, 10000 - 1, ((uint32_t)(getTimerClkFreq(TIM1) / (1000000)) - 1));
attachIntHandle(&TimHandle, calculate_RPM);
}
void loop() {
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); // Toggle pin each loop to indicate code is still running, Pin = PC13
// Motor left forward
analogWrite(M_L_in1Pin, MAX_PWM);
digitalWrite(M_L_in2Pin, LOW);
// Motor right forward
analogWrite(M_R_in1Pin, MAX_PWM);
digitalWrite(M_R_in2Pin, LOW);
// Print everything to serial port.
Serial.print("R:"); Serial.print(M_R_RPM); Serial.print(" L:"); Serial.println(M_L_RPM);
}
The code basically increments/decrements a counter each time an interrupt is triggered on the encoder output and then there's a timer interrupt every 10ms which counts the pulses since its last run and converts that to an RPM value.
The output of my oscilloscope is:
Yellow and Light blue are the encoder outputs and pink is pin PC13 which toggles each loop() cycle.
This shows that the magnetic encoder is working.
The motor datasheet says the no-load speed of the output should be 75+-10% RPM.
However I'm getting outputs of around 45RPM for both motors.
I know these are Chinese motors and quality can sometimes be questionable but from 75RPM to 45RPM seems too much difference.
Am I doing something wrong here?