Search code examples
ctimerstm32stepper

C Stepper Motor code behaving different 1/5 times


I have written this stepper code with a timer and I am getting different behaviour one out of five times. Maybe there is a good way I can monitor or record the consistency of pulses? But I think putting logs would clog up the code and cause the stepper to slow down like it does on Arduino, although I haven't tried that yet. Anyway, when I press a button it triggers the acceleration at the beginning of the movement and also to trigger starting the movement. I have hardcoded the values I want it to run at currently as I just wanted to get it working, but I can see it's behaving inconsistently. It will gather speed, plateaus for about 5 seconds, and then decelerates close to the end. But that one time out of five it will plateau for 1 second, maybe not even that. All help would be greatly appreciated, also hints and tips on Stm32CubeIDE would be great. For extra context, the counter period is set to 42 - 1

volatile static int moveTriggered = 0;
volatile static int step1 = 1;

volatile static int accelerating = 0;
volatile static int decelerating = 0;
static int accelCounter = 0;

static int currentPrescaler = 999;
static const int maxPrescaler = 999;
static const int minPrescaler = 99;

static const int travelSteps = 70000;
static int currentTravelled = 0;


void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
    if (moveTriggered) {
        if (step1) {
            HAL_GPIO_WritePin(STEP_OUT_GPIO_Port, STEP_OUT_Pin, GPIO_PIN_SET);
            step1 = 0;
        }
        else {
            HAL_GPIO_WritePin(STEP_OUT_GPIO_Port, STEP_OUT_Pin, GPIO_PIN_RESET);
            step1 = 1;
        }
    }
    if (accelerating) {
        if ( currentPrescaler > minPrescaler && accelCounter == 10) {
            __HAL_TIM_SET_PRESCALER(&htim2, currentPrescaler--);
            accelCounter = 0;
        } else if (currentPrescaler <= minPrescaler) {
            accelerating = 0;
        }
        accelCounter++;
    }
    if (decelerating) {
        if (currentPrescaler < maxPrescaler && accelCounter == 10) {
            __HAL_TIM_SET_PRESCALER(&htim2, currentPrescaler++);
            accelCounter = 0;
        } else if (currentPrescaler >= maxPrescaler) {
            decelerating = 0;
        }
        accelCounter++;
    }
    currentTravelled++;
    if (currentTravelled == 65000) decelerating = 1;
    else if (currentTravelled >= travelSteps) {
        currentTravelled = 0;
        moveTriggered = 0;
        accelerating = 0;
        decelerating = 0;
        accelCounter = 0;
        currentPrescaler = maxPrescaler;
        __HAL_TIM_SET_PRESCALER(&htim2, currentPrescaler);
    }
}

And the while loop

  while (1)
  {
      int stateOfPushButton = HAL_GPIO_ReadPin(MOTOR_BUTTON_GPIO_Port, MOTOR_BUTTON_Pin);
      if ( stateOfPushButton == 0 && accelerating == 0 ) {
          moveTriggered = 1;
          accelerating = 1;
      }
  }


Solution

  • The cause of your problem is that currentTravelled is being incremented on every invocation of the ISR, whether a move has been triggered or not. This means that it could be at any value between zero and 'travelSteps' when you press the button. So the duration of your move is a random number within that range.

    Most of the logic in the ISR doesn't need to happen if moveTriggered is zero. So your ISR could check for that first, and just return if there is no move happening. This will also stop you wasting time doing pointless things in the ISR.

    I'd also suggest getting rid of the essentially boolean moveTriggered, accelerating, and decelerating variables, and replace with a single variable of type:

    enum move_state_e {
        MOVE_NONE,
        MOVE_ACCELERATING,
        MOVE_PLATEAU,
        MOVE_DECELERATING
    };
    

    This will make your code simpler and easier to understand.