Hello! I am having trouble getting an encoder and stepper motor working together smoothly (for cartpole, so the encoder measures the pole angle, not the motor angle).
The stepper motor uses the FastAccelStepper library, and takes time to get up to speed. With the speeds I'm hoping for it to reach, it can't just jump to the desired speed without stalling.
The encoder function is constantly triggered by two interrupt pins on CHANGE. The interrupt function run is as short as I could get it... it uses an encoder state table, and then increments a counter depending on a clockwise or counterclockwise rotation.
However, when the stepper is spinning at high speed, and the encoder is spinning, sometimes the stepper will stall. I believe that once it misses a step, it starts to stall, and then cannot jump back into the high-velocity without starting over at velocity = 0 and doing the acceleration process again. So the stepper motor isn't just missing steps, but completely stalling during the encoder's interrupt.
My next best idea is to add a second Arduino, that handles the encoder and sends its position over UART. Before I do that, is there anything I am missing that will allow for this to work? Thank you!
#include "Stepper.h"
#include "Rotary.h"
#define aChannel 2
#define bChannel 3
FastAccelStepperController stepper = FastAccelStepperController();
Rotary rotary1 = Rotary(aChannel, bChannel);
TMC2209Stepper uartDriver = TMC2209Stepper(SW_RX, SW_TX, R_SENSE, DRIVER_ADDRESS);
float acceleration;
float encoderPosition = 0; // volatile?
void setup() {
stepper.setup();
uartDriver.begin();
uartDriver.microsteps(16);
uartDriver.en_spreadCycle(1);
attachInterrupt(digitalPinToInterrupt(aChannel), rotateEncoder, CHANGE); // shouldn't these interrupts be attached to 2 and 3
attachInterrupt(digitalPinToInterrupt(bChannel), rotateEncoder, CHANGE);
}
void loop() {
stepper.update(encoderPosition * 1000, 0, 0); // will eventually implement PID or something here.
}
void rotateEncoder() {
unsigned char result = rotary1.process();
if (result == DIR_CW) {
encoderPosition++;
} else if (result == DIR_CCW) {
encoderPosition--;
}
}
and the rotary function...
unsigned char Rotary::process() {
unsigned char pinstate = (digitalRead(pin2) << 1) | digitalRead(pin1); // | is bitwise inclusive OR. << shifts bits left. So 1 and 1 becomes 11.
state = ttable[state & 0xf][pinstate]; // & is bitwise AND. & 0xf is same as & 1111. So it is masking "state" to the lowest 4 bits.
return state & 0x30; // 0x30 is 00110000. So masks all except 5th and 6th bits.
}
and lastly the motor update function...
void FastAccelStepperController::update(float requested_acceleration, float requested_velocity, float requested_position) {
stepper->moveByAcceleration(requested_acceleration);
}