2
\$\begingroup\$

I am trying to write my own microsecond delay function for an STM32f030R8t6 by using the counter function on Timer 3.

The first time I power the microcontroller up after flashing the delay works correctly, however, when I cycle power, the delay shortens by approximately a factor of 10. This new shorter delay will persist until I flash a new program to the chip. I am using tim1 for PWM and I have had no issues with it.

Tim3 is set up in a stepper library I am writing. Here is the function that sets up tim3 and the delay function:

void StepperStartup() {
  // setup Servo PWM
  PWMSetup_CH4();

  // SETUP RIGHT STEPPER
  setPortAndPinForOutput(dirPortR, dirPinR);
  setPortAndPinForOutput(StepPortR, StepPinR);
  setPortAndPinForOutput(EnablePortR, EnablePinR);
  EnablePortR->BSRR |= (1 << EnablePinR); // disable stepper

  //SETUP LEFT STEPPER
  setPortAndPinForOutput(dirPortL, dirPinL);
  setPortAndPinForOutput(StepPortL, StepPinL);
  setPortAndPinForOutput(EnablePortL, EnablePinL);
  EnablePortL->BSRR |= (1 << EnablePinL); // disable stepper

  // TURN ON TIM3 FOR USE IN THE uS delays------------------------------------------
  // enable timer
  RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;

  // ENABLE COUNTER
  TIM3->CR1 |= TIM_CR1_CEN;
  // TIM3->CR1 |= TIM_CR1_CMS_0;

  // Set prescaler
  TIM3->PSC = floor(HSI_VALUE / 1000000);

  // set auto reload AS HIGH AS possible
  TIM3->ARR = 65535;

  CurrentLl = sqrt(pow(X_init, 2) + pow(Y_init, 2));
  CurrentLr = sqrt(pow(X_init-Ls, 2) + pow(Y_init, 2));  }

void Delay_uS(uint16_t us) {
  TIM3->CNT = 0x00000000; // set the counter value a 0
  while (TIM3->CNT < us) {
  } 
} // wait for the counter to reach the us input in the parameter

The main() then looks like:

int main(void) {
  HAL_Init();
  SystemClock_Config();
  StepperStartup();

  // some other code here

  while (1) {
    LinearMove(100, 130); // function which uses the delay
    LinearMove(200, 130)
  }
}

I'm pretty new to microcontrollers so this might be a noob question, but does anyone know why the delay function works the first time the chip is powers, but every successive time it runs extra fast?

Troubleshooting I've attempted:

  • Run the delay on TIM1 since its working for the PWM--> no difference
  • change the PSC --> This has an effect on the first power cycle, but successive cycle run at the same speed, regardless of the PSC
  • I tried using STM32CubeIDE's automated clock set up--> no help

I'm definitely open to some new ideas if yall have them!

Full code:

#include "main.h"
#include <math.h>

#define dirPinR 7
#define dirPortR GPIOC

#define StepPortR GPIOC
#define StepPinR 8

#define EnablePinR 9
#define EnablePortR GPIOC

#define dirPinL 14
#define dirPortL GPIOB

#define StepPortL GPIOB
#define StepPinL 15

#define EnablePinL 6
#define EnablePortL GPIOC

#define NumMicrosteps 1.0
#define StepsPerRad 31.831
#define Ls 300 //mm
#define SpoolRadius 15.75 //mm

extern float CurrentLl;
extern float CurrentLr;
extern float StepDelay; //in us
extern int CurrentStepl;
extern int CurrentStepr;

#define X_init 50
#define Y_init 130

float StepDelay = 5000; //in us

float CurrentLl = 0;
float CurrentLr = 0;

int CurrentStepl = 0;
int CurrentStepr = 0;

void setPortAndPinForOutput(GPIO_TypeDef *portOnMCU, int pinNumber) {
    //ENABLE PORTS
    if (portOnMCU == GPIOA) {
        RCC->AHBENR |= RCC_AHBENR_GPIOAEN;
    }
    if (portOnMCU == GPIOB) {
        RCC->AHBENR |= RCC_AHBENR_GPIOBEN;
    }
    if (portOnMCU == GPIOC) {
        RCC->AHBENR |= RCC_AHBENR_GPIOCEN;
    }
    if (portOnMCU == GPIOD) {
        RCC->AHBENR |= RCC_AHBENR_GPIODEN;
    }
    if (portOnMCU == GPIOF) {
        RCC->AHBENR |= RCC_AHBENR_GPIOFEN;
    }

    //SET PIN TO OUTPUT, HIGH SPEED, PP, NO PUPD
    portOnMCU->MODER |= 1 << (2 * pinNumber);
    portOnMCU->MODER &= ~(1 << (2 * pinNumber + 1));

    portOnMCU->OSPEEDR |= (1 << (2 * pinNumber)) | (1 << (2 * pinNumber + 1));

    portOnMCU->OTYPER &= ~(1 << (pinNumber));

    portOnMCU->PUPDR &= ~(1 << (pinNumber));

}

void Delay_uS(uint16_t us) {
    TIM3->CNT = 0x00000000;  // set the counter value a 0
    while (TIM3->CNT < us) {
    }  // wait for the counter to reach the us input in the parameter
}

void Delay_mS(uint16_t ms) {
    for (int i = 0; i < ms; i++) {
        Delay_uS(1000);
    }

}

void StepperStartup() {
    //SETUP RIGHT STEPPER
    setPortAndPinForOutput(dirPortR, dirPinR);
    setPortAndPinForOutput(StepPortR, StepPinR);
    setPortAndPinForOutput(EnablePortR, EnablePinR);
    EnablePortR->BSRR |= (1 << EnablePinR); //disable stepper

    //SETUP LEFT STEPPER
    setPortAndPinForOutput(dirPortL, dirPinL);
    setPortAndPinForOutput(StepPortL, StepPinL);
    setPortAndPinForOutput(EnablePortL, EnablePinL);
    EnablePortL->BSRR |= (1 << EnablePinL); //disable stepper

    // TURN ON TIM3 FOR USE IN THE uS delays------------------------------------------
    //enable timer
    RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;

    //ENABLE COUNTER
    TIM3->CR1 |= TIM_CR1_CEN;

    //TIM3->CR1 |= TIM_CR1_CMS_0;

    //Set prescaler
    TIM3->PSC = floor(HSI_VALUE / 1000000);

    //set auto reload AS HIGH AS possible
    TIM3->ARR = 65535;

    CurrentLl = sqrt(pow(X_init, 2) + pow(Y_init, 2));
    CurrentLr = sqrt(pow(X_init - Ls, 2) + pow(Y_init, 2));
}

void SetSpeed(float speedInput) {
    //StepDelay [us/step] = [min/rev][s/min][us/s][rev/deg][deg/step]
    //StepDelay = floor((1 / speedInput) * 60. * 1000000. * (1. / 360.) * (1.8 / NumMicrosteps));
    StepDelay = floor((300000 / (speedInput * NumMicrosteps)));
}

void LinearMove(int x, int y) {
//convert (x,y) coords to steps--------------------------------------------------
    float TargetLl = sqrt(pow(x, 2) + pow(y, 2));
    float TargetLr = sqrt(pow(Ls - x, 2) + pow(y, 2));

    int StepsToTakeL = floor(
            StepsPerRad * ((TargetLl - CurrentLl) / SpoolRadius));
    int StepsToTakeR = floor(
            StepsPerRad * ((TargetLr - CurrentLr) / SpoolRadius));

    int TargetStepL = StepsToTakeL + CurrentStepl;
    int TargetStepR = StepsToTakeR + CurrentStepr;

    int incrementerL = 0;
    int incrementerR = 0;

//Determine Directions for both steppers
    if (StepsToTakeL < 0) {
        dirPortL->BRR |= (1 << dirPinL);
        incrementerL = -1;
    } else {
        dirPortL->BSRR |= (1 << dirPinL);
        incrementerL = 1;
    }

    if (StepsToTakeR < 0) {
        dirPortR->BSRR |= (1 << dirPinR);
        incrementerR = -1;
    } else {
        dirPortR->BRR |= (1 << dirPinR);
        incrementerR = 1;
    }

    StepsToTakeL = abs(StepsToTakeL);
    StepsToTakeR = abs(StepsToTakeR);

//enable steppers
    EnablePortR->BRR |= (1 << EnablePinR);
    EnablePortL->BRR |= (1 << EnablePinL);

    int StepCounter = 0;
    int ScaleFactorL = 1;
    int ScaleFactorR = 1;

    if (StepsToTakeL > StepsToTakeR) {
        ScaleFactorR = floor(StepsToTakeL / StepsToTakeR);
    } else {
        ScaleFactorL = floor(StepsToTakeR / StepsToTakeL);
    }

    //WHILE EITHER STEPPER ISNT AT ITS TARGER
    while ((CurrentStepl != TargetStepL) | (CurrentStepr != TargetStepR)) {

        if ((CurrentStepl != TargetStepL)
                & ((StepCounter % ScaleFactorL) == 0)) {
            StepPortL->BSRR |= (1 << StepPinL);
            CurrentStepl += incrementerL;
        }

        if ((CurrentStepr != TargetStepR)
                & ((StepCounter % ScaleFactorR) == 0)) {
            StepPortR->BSRR |= (1 << StepPinR);
            CurrentStepr += incrementerR;
        }

        Delay_uS(StepDelay);
        StepPortL->BRR |= (1 << StepPinL);
        StepPortR->BRR |= (1 << StepPinR);
        Delay_uS(StepDelay);
        StepCounter++;
    }

//after move is complete update Ll and Lr
    CurrentLl = TargetLl;
    CurrentLr = TargetLr;

//disable steppers
    EnablePortR->BSRR |= (1 << EnablePinR);
    EnablePortL->BSRR |= (1 << EnablePinL);
}

void SystemClock_Config(void);

int main(void) {

    HAL_Init();
    SystemClock_Config();
    StepperStartup();
    SetSpeed(10);

    while (1) {
        LinearMove(100, 130);

        Delay_mS(1000);
        LinearMove(200, 130);

        LinearMove(200, 250);

        LinearMove(100, 250);

    }

}

/**
 * @brief System Clock Configuration
 * @retval None
 */
void SystemClock_Config(void) {
    RCC_OscInitTypeDef RCC_OscInitStruct = { 0 };
    RCC_ClkInitTypeDef RCC_ClkInitStruct = { 0 };

    /** Initializes the RCC Oscillators according to the specified parameters
     * in the RCC_OscInitTypeDef structure.
     */
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
    RCC_OscInitStruct.HSIState = RCC_HSI_ON;
    RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
    if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
        Error_Handler();
    }

    /** Initializes the CPU, AHB and APB buses clocks
     */
    RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
            | RCC_CLOCKTYPE_PCLK1;
    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
    RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;

    if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) {
        Error_Handler();
    }
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
 * @brief  This function is executed in case of error occurrence.
 * @retval None
 */
void Error_Handler(void) {
    /* USER CODE BEGIN Error_Handler_Debug */
    /* User can add his own implementation to report the HAL error return state */
    __disable_irq();
    while (1) {
    }
    /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

TLDR: Delay function works correctly the first time the chip is powered after flashing. After every successive power up the delay is significantly shorter.

\$\endgroup\$
7
  • 1
    \$\begingroup\$ so this might be a noob question ... you did not ask a question ... please add a clear, answerable question to your post \$\endgroup\$
    – jsotola
    Commented Jan 20 at 7:20
  • 1
    \$\begingroup\$ OK, so there is no full code shown, is this a minimal example? Have you debugged your code by single stepping or any other way? \$\endgroup\$
    – Justme
    Commented Jan 20 at 8:17
  • \$\begingroup\$ @jsotola Thought it was clear, but the question is why does the delay function work on the first power cycle. but then when I restart the chip, the delays are far too short. \$\endgroup\$
    – Eric14003
    Commented Jan 20 at 18:02
  • 1
    \$\begingroup\$ @Eric14003 There is still no question that needs an answer, in the question. Please edit in a question there. And the comments you reveive indicate it is not possible to answer with given info and how you have tried to debug it, if at all. \$\endgroup\$
    – Justme
    Commented Jan 20 at 18:09
  • 1
    \$\begingroup\$ @Justme I simplified the code down a bit and have all the functions in the main.c file (see full code in problem statement). Still getting the same behavior. I would simplify more, however I dont have an oscilloscope, so watching the stepper move is the best way for me to tell if its working atm without redoing my entire circuit. \$\endgroup\$
    – Eric14003
    Commented Jan 22 at 3:26

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.