cancel
Showing results for 
Search instead for 
Did you mean: 

Why is my SysTick timer not incrementing, causing no RPM calculation?

romi
Associate III

Hi all,

I am working on a motor control project using STM32, and I am trying to calculate the RPM of a BLDC motor using Hall sensor pulses. I have set up a SysTick timer to trigger an interrupt every 1 millisecond, which should increment a counter (sys_tick_count). This counter is then used in my millis() function to calculate elapsed time.

However, I am facing an issue where the RPM calculation doesn't seem to trigger because the time difference (time_diff) remains zero throughout the execution. It seems like the millis() function is not returning an incrementing system time, so the RPM is never calculated.

Here's the relevant portion of my code:

 

// SysTick timer initialization
void SysTick_Init(void)
{
  SysTick_Config(SystemCoreClock / 1000); // Interrupt every 1ms
}

// SysTick Handler
void SysTick_Handler(void)
{
  sys_tick_count++;
}

// Simple function to get the time in milliseconds
uint32_t millis(void)
{
  return sys_tick_count; // Return system uptime in milliseconds
}

// Function to calculate speed (RPM)
float calculate_speed(void)
{
  static uint32_t last_time = 0;
  uint32_t current_time = millis();
  float rpm = 0;

  uint32_t time_diff = current_time - last_time;
  printf("Time difference: %lu ms\n", time_diff); // Debugging output

  if (time_diff >= 1000) {
    rpm = (hall_pulse_count / 6.0) * 60.0; // Assuming 6 pulses per revolution
    printf("RPM calculation, hall_pulse_count: %lu, RPM: %f\n",
           hall_pulse_count,
           rpm);
    hall_pulse_count = 0;
    last_time = current_time;
  }
  else
  {
    printf("Waiting to calculate RPM, time remaining: %lu ms\n", 1000 - time_diff);
  }
  return rpm;
}

 

My Observations:

  • The SysTick_Handler does not seem to be updating sys_tick_count properly, as time_diff remains zero.
  • I am unsure if I am configuring the SysTick timer or handling the SysTick interrupt correctly.

Question:

  • Is there something wrong with my SysTick_Handler or the SysTick_Init setup?
  • Could there be something interfering with the SysTick timer, or is there a better way to handle this kind of timing?

Any help would be greatly appreciated!

 

11 REPLIES 11
Karl Yamashita
Lead III

Use the </> when posting code so it's formatted and readable.

Tips and Tricks with TimerCallback https://www.youtube.com/@eebykarl
If you find my solution useful, please click the Accept as Solution so others see the solution.
Karl Yamashita
Lead III

Maybe you didn't define sys_tick_count as volatile. Don't know because you didn't show how you declared it.

Tips and Tricks with TimerCallback https://www.youtube.com/@eebykarl
If you find my solution useful, please click the Accept as Solution so others see the solution.
TDK
Guru

Is sys_tick_count defined volatile? Are you inside of another interrupt with same or higher priority?

Make the example simpler, remove all the code and just observe sys_tick_count in the debugger.

If you feel a post has answered your question, please click "Accept as Solution".
romi
Associate III

Thank you for your suggestions. I followed your advice and made sure that sys_tick_count is defined as volatile. I also checked if I was inside another interrupt of the same or higher priority, but everything seems correctly prioritized.

I simplified the code as you suggested, removing other logic, and monitored the sys_tick_count.  The SysTick seems to be incrementing correctly, but the actual speed is still showing as zero, even though the motor is running and the Hall sensors appear to be functioning correctly. Could there be something I am missing with the Hall sensor signal counting or the timing? I appreciate any further insights you might have.  

romi
Associate III

Thanks for your input! I ensured that sys_tick_count is declared as volatile. I followed the steps you outlined, and now the SysTick is functioning correctly and incrementing as expected.

However, the actual speed is still showing as zero, even though the motor is running. The Hall sensors seem to be working (I verified the signals). Could there be an issue with the logic in the way the speed is being calculated or the pulse counting? Any further advice would be very helpful.

Sarra.S
ST Employee

Hello @romi

Maybe add a debugging statement to print the "hall_pulse_count" value each time the interrupt handler is called, to see if the pulses are being counted correctly 

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.

You're not giving us enough info to determine that. When is calculate_speed called? We have no idea. Maybe it's never called. Maybe it's called too often. Maybe not enough, maybe at the wrong times. Verify it's getting called at the expected rate. What speeds do you expect?

If you feel a post has answered your question, please click "Accept as Solution".
romi
Associate III

The calculate_speed() function is called within the main loop, which includes a delay_ms(100) at the end. This means the loop, and therefore calculate_speed(), is called roughly every 100 milliseconds. However, within the calculate_speed() function itself, speed is only updated every 200 ms because I’ve included a condition that ensures it only recalculates speed if 200 ms has passed (if (current_time - last_time >= 200)).

I verified the Hall sensor output using USART, and it is functioning correctly, so the issue might be related to either the timing of the speed calculation or insufficient pulses within the 200 ms window. below is this is code:

<<<<<

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "lcd.h"
#include "adc.h"
#include "key.h"
#include "pwmdac.h"

#define TARGET_RPM 120
#define MAX_PWM 115
#define MIN_PWM 0
#define N_MAX 5000
#define POLE_PAIRS 2
#define IPBCLK 60000000
#define QT_PRESCALER 128
#define PERIOD_TO_SPEED (IPBCLK * 60 / (N_MAX * QT_PRESCALER * POLE_PAIRS))

#define HALL_A_PIN GPIO_Pin_6
#define HALL_B_PIN GPIO_Pin_7
#define HALL_C_PIN GPIO_Pin_8
#define HALL_PORT GPIOB

volatile uint32_t hall_pulse_count = 0;
float actual_rpm = 0;
int error_flag = 0;
extern volatile uint32_t sys_tick_counter;

int last_pin_state_A = -1;
int last_pin_state_B = -1;
int last_pin_state_C = -1;
int hall_sensor_error = 0;

float Kp = 0.8, Ki = 0.15, Kd = 0.1;
float integral = 0, previous_error = 0;
float target_speed = TARGET_RPM;
uint16_t pwm_output = 0;

void GPIO_HallSensor_Init(void);
void EXTI9_5_IRQHandler(void);
float calculate_speed(void);
void display_status(float speed, int error_flag);
uint16_t PID_Controller(float setpoint, float measured_speed);
void TIM3_PWM_Init(uint32_t arr, uint32_t psc);
void set_pwm_duty_cycle(uint16_t pwm_value);
uint32_t millis(void);
void check_hall_sensor_wires(void);

int main(void) {
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
delay_init(168);
uart_init(115200);
LED_Init();
LCD_Init();
KEY_Init();
TIM3_PWM_Init(255, 0);

GPIO_HallSensor_Init();

SysTick_Config(SystemCoreClock / 1000);

POINT_COLOR = RED;
LCD_ShowString(30, 50, 200, 16, 16, "STM32 Motor Control");
LCD_ShowString(30, 70, 200, 16, 16, "Target Speed: 120 RPM");
POINT_COLOR = BLUE;

while (1) {
check_hall_sensor_wires();
actual_rpm = calculate_speed();
pwm_output = PID_Controller(target_speed, actual_rpm);
set_pwm_duty_cycle(pwm_output);
display_status(actual_rpm, hall_sensor_error);
delay_ms(100);
}
}

void TIM3_PWM_Init(uint32_t arr, uint32_t psc) {
GPIO_InitTypeDef GPIO_InitStruct;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct;
TIM_OCInitTypeDef TIM_OCInitStruct;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);

GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOC, &GPIO_InitStruct);

GPIO_PinAFConfig(GPIOC, GPIO_PinSource6, GPIO_AF_TIM3);

TIM_TimeBaseStruct.TIM_Period = arr;
TIM_TimeBaseStruct.TIM_Prescaler = psc;
TIM_TimeBaseStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStruct);

TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStruct.TIM_Pulse = arr / 2;
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM3, &TIM_OCInitStruct);

TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM3, ENABLE);
TIM_Cmd(TIM3, ENABLE);
}

void set_pwm_duty_cycle(uint16_t pwm_value) {
if (pwm_value > MAX_PWM) pwm_value = MAX_PWM;
TIM_SetCompare1(TIM3, pwm_value);
}

void GPIO_HallSensor_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct;
EXTI_InitTypeDef EXTI_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);

GPIO_InitStruct.GPIO_Pin = HALL_A_PIN | HALL_B_PIN | HALL_C_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(HALL_PORT, &GPIO_InitStruct);

SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource6);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource7);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource8);

EXTI_InitStruct.EXTI_Line = EXTI_Line6 | EXTI_Line7 | EXTI_Line8;
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStruct);

NVIC_InitStruct.NVIC_IRQChannel = EXTI9_5_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0x00;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0x00;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
}

void EXTI9_5_IRQHandler(void) {
if (EXTI_GetITStatus(EXTI_Line6) != RESET || EXTI_GetITStatus(EXTI_Line7) != RESET || EXTI_GetITStatus(EXTI_Line8) != RESET) {
hall_pulse_count++;
EXTI_ClearITPendingBit(EXTI_Line6 | EXTI_Line7 | EXTI_Line8);
}
}

void check_hall_sensor_wires(void) {
int pin_state_A = GPIO_ReadInputDataBit(HALL_PORT, HALL_A_PIN);
int pin_state_B = GPIO_ReadInputDataBit(HALL_PORT, HALL_B_PIN);
int pin_state_C = GPIO_ReadInputDataBit(HALL_PORT, HALL_C_PIN);

if (last_pin_state_A == pin_state_A && last_pin_state_B == pin_state_B && last_pin_state_C == pin_state_C) {
hall_sensor_error = 1;
} else {
hall_sensor_error = 0;
}

last_pin_state_A = pin_state_A;
last_pin_state_B = pin_state_B;
last_pin_state_C = pin_state_C;
}

float calculate_speed(void) {
static uint32_t last_time = 0;
uint32_t current_time = millis();
float rpm = 0;

if (current_time - last_time >= 200) {
if (hall_pulse_count > 0) {
rpm = (hall_pulse_count / 6.0) * 60.0;
}
hall_pulse_count = 0;
last_time = current_time;
}

return rpm;
}

uint16_t PID_Controller(float setpoint, float measured_speed) {
float error = setpoint - measured_speed;
float derivative;
float output;

integral += error;
derivative = error - previous_error;
output = Kp * error + Ki * integral + Kd * derivative;
previous_error = error;

if (output > MAX_PWM) output = MAX_PWM;
if (output < MIN_PWM) output = MIN_PWM;

return (uint16_t)output;
}

uint32_t millis(void) {
return sys_tick_counter;
}

void display_status(float speed, int error_flag) {
char buffer[16];

sprintf(buffer, "Speed: %0.1f RPM", speed);
LCD_ShowString(30, 100, 200, 16, 16, (u8*)buffer);

if (error_flag == 0) {
LCD_ShowString(30, 130, 200, 16, 16, "Sensors: OK");
} else {
LCD_ShowString(30, 130, 200, 16, 16, "Sensors: ERROR");
}
}

>>>>

romi
Associate III

thanks for your response. I’ve already verified that the Hall sensor is functioning correctly by toggling an external LED with each interrupt, which shows that the pulses are being detected at different motor speeds. The motor can run at various speeds as expected, but the issue is that the actual speed displayed on both the LCD and USART always reads as zero RPM.

While I haven’t added a debug statement to print hall_pulse_count each time the interrupt handler is called, I did confirm via the LED that the pulses are being generated consistently when the motor is running. So, it seems like the pulses are being counted correctly in the interrupt handler.

Given that the sensor is working fine, the problem likely lies with how the speed is being calculated or processed in the calculate_speed() function. Specifically, the Hall sensor pulses are being registered, but they are not translating into a correct RPM value, which is always displayed as zero.