cancel
Showing results for 
Search instead for 
Did you mean: 

C Stepper Motor code behaving different 1/5 times using timers and state from while loop.

LHuss.1
Associate II

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;
      }
  }

7 REPLIES 7

What STM32?

Show TIM initialization code.

Is TIM2 in the part you're using 32-bit?

Why change the Prescaler? It's hard to reset the internal count for it.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

STM32F411RE

I am not at my computer right now, but as I understand the TIM is set up correctly and this callback is ​called at the correct timings.

I am changing the prescaler because I want to accelerate, but I suppose this isn't the right way of doing it?

Should I then do similar to what I did for the accelCounter? Where I can miss 10 steps every counter period up to N steps, then decrease the missed steps to 9 until N steps, repeating until no missed steps?

Piranha
Chief II

Use the timer's output compare channel for STEP impulses and change the period, not the prescaler.

So output compare with a callback function to handle this logic?

But how can I change the counter period at runtime? I did not see a function for this in the documentation, only a function to change the prescaler.

I saw a function only to reset the counter to 0​

Semer CHERNI
ST Employee

Hello @LHuss.1​ 

To be able to change the duty cycle for the PWM signal you can use this command (found under stm32h7xx_hal_tim.h) which change the CCR register value on runtime:

  * @brief  Set the TIM Capture Compare Register value on runtime without calling another time ConfigChannel function.
  * @param  __HANDLE__ TIM handle.
  * @param  __CHANNEL__ TIM Channels to be configured.
  *          This parameter can be one of the following values:
  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
  *            @arg TIM_CHANNEL_5: TIM Channel 5 selected
  *            @arg TIM_CHANNEL_6: TIM Channel 6 selected
  * @param  __COMPARE__ specifies the Capture Compare register new value.
  * @retval None
 
__HAL_TIM_SET_COMPARE(__HANDLE__, __CHANNEL__, __COMPARE__)

Hope this solution help you.

Kind regards,

Semer.

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

Read the reference manual - you have to change the ARR register. If you want to use the HAL/Cube broken bloatware, then look at it's code and find out which function or macro can do it.

And no, you don't need a callback on output compare event. Just let the output compare hardware toggle the channel pin - generate the impulse.

Some additional comments... I would recommend to use PWM mode 1, counter as a downcounter and do not enable the register preload feature, because the downcounter mode does not need it. In such a configuration the update/period interrupt will happen at the end of the previous step pulse, not simultaneously with the current one. That gives the software a chance to calculate it's things and, for example, stop the timer before the next pulse happens. To maximize the time for software, minimize (reasonably) the step pulse width. Note that in downcounter mode, to get the pulse width N, the CCRx register must also be set to N-1. And, after disabling the counter (and in initialization), set the CNT register to N. That way the output channel will be held in reset state and, when enabling the timer, the first pulse will happen after just a single counter tick. And on top of it all, if the pulses are for microsteps or you just do not want a callback for every step, use the repetition counter to set the desired step division factor.

Pulse period or width - it's all the same for some of the ST's "competent" support stuff...