2024-10-24 04:13 AM - last edited on 2024-10-24 05:53 AM by SofLit
Hi,
I’m working on a project using STM32 to control two BLDC motors with Hall sensors. The motors are controlled via CAN commands, and everything works as expected in terms of control. However, I have an issue with displaying the RPM of Motor 1. Motor 2's RPM is tracked and displayed accurately on the LCD, but Motor 1's RPM always shows as zero, even though both motors are running and controlled successfully.
I’m using Hall sensors to calculate the speed of both motors, and the speed is displayed on the LCD. Here’s the part of the code responsible for calculating and displaying the speed for both motors::
<
/>
I verified that the EXTI handler is triggering for Motor 1, and I also confirmed that the CAN commands are controlling both motors correctly. The issue seems to be that hall_pulse_count_m1 is not updating properly, or the speed is not being calculated as expected for Motor 1, resulting in zero RPM on the display.
Any advice on why this might be happening and how to fix it? I've attached the full code below for reference.
Thanks!
float calculate_speed_m1(void) {
if (hall_pulse_count_m1 > 0) {
float rpm_m1 = (hall_pulse_count_m1 / 6.0) * 60.0;
hall_pulse_count_m1 = 0;
return rpm_m1;
}
return 0;
}
void EXTI9_5_IRQHandler(void) {
if (EXTI_GetITStatus(EXTI_Line6) != RESET || EXTI_GetITStatus(EXTI_Line7) != RESET || EXTI_GetITStatus(EXTI_Line8) != RESET) {
hall_pulse_count_m1++; // Increment the pulse count for motor 1
EXTI_ClearITPendingBit(EXTI_Line6 | EXTI_Line7 | EXTI_Line8);
}
}
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "lcd.h"
#include "key.h"
#include "can.h"
#include "adc.h"
#include "timer.h"
#include "stm32f4xx_syscfg.h"
#include "stm32f4xx_exti.h"
#include "misc.h"
#define CAN_SJW_DEFAULT CAN_SJW_1TQ
#define CAN_BS1_DEFAULT CAN_BS1_7TQ
#define CAN_BS2_DEFAULT CAN_BS2_6TQ
#define CAN_BAUD_PRESCALER 6
#define TARGET_RPM_M1 230
#define TARGET_RPM_M2 100
#define MAX_PWM 165
#define MIN_PWM 0
#define N_MAX 5000
#define POLE_PAIRS 3
#define IPBCLK 60000000
#define QT_PRESCALER 128
#define PERIOD_TO_SPEED (IPBCLK * 60 / (N_MAX * QT_PRESCALER * POLE_PAIRS))
#define HALL_A_PIN_M1 GPIO_Pin_6
#define HALL_B_PIN_M1 GPIO_Pin_7
#define HALL_C_PIN_M1 GPIO_Pin_8
#define HALL_PORT_M1 GPIOB
#define HALL_A_PIN_M2 GPIO_Pin_0
#define HALL_B_PIN_M2 GPIO_Pin_1
#define HALL_C_PIN_M2 GPIO_Pin_2
#define HALL_PORT_M2 GPIOG
volatile uint32_t hall_pulse_count_m1 = 0;
volatile uint32_t hall_pulse_count_m2 = 0;
float actual_rpm_m1 = 0;
float actual_rpm_m2 = 0;
int error_flag_m1 = 0;
int error_flag_m2 = 0;
extern volatile uint32_t sys_tick_counter;
int last_pin_state_A1 = -1, last_pin_state_B1 = -1, last_pin_state_C1 = -1;
int last_pin_state_A2 = -1, last_pin_state_B2 = -1, last_pin_state_C2 = -1;
int hall_sensor_error_m1 = 0, hall_sensor_error_m2 = 0;
uint8_t motor_running_m1 = 0;
uint8_t motor_running_m2 = 0;
float Kp_m1 = 0.8, Ki_m1 = 0.15, Kd_m1 = 0.1;
float integral_m1 = 0, previous_error_m1 = 0;
float target_speed_m1 = TARGET_RPM_M1;
float Kp_m2 = 0.7, Ki_m2 = 0.12, Kd_m2 = 0.08;
float integral_m2 = 0, previous_error_m2 = 0;
float target_speed_m2 = TARGET_RPM_M2;
uint16_t pwm_output_m1 = 0;
uint16_t pwm_output_m2 = 0;
void GPIO_HallSensor_Init(void);
void EXTI9_5_IRQHandler(void);
float calculate_speed_m1(void);
float calculate_speed_m2(void);
void display_status(float speed_m1, int error_flag_m1, float speed_m2, int error_flag_m2);
uint16_t PID_Controller(float setpoint, float measured_speed, float *Kp, float *Ki, float *Kd, float *integral, float *previous_error);
void TIM3_PWM_Init(uint32_t arr, uint32_t psc);
void set_pwm_duty_cycle_m1(uint16_t pwm_value);
void set_pwm_duty_cycle_m2(uint16_t pwm_value);
uint32_t millis(void);
void check_hall_sensor_wires_m1(void);
void check_hall_sensor_wires_m2(void);
void handle_CAN_command(uint8_t *data);
int main(void) {
uint8_t can_rx_buf[8];
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
delay_init(168);
uart_init(115200);
LED_Init();
LCD_Init();
KEY_Init();
TIM3_PWM_Init(255, 0);
TIM3_PWM_Init(255, 0);
CAN1_Mode_Init(CAN_SJW_DEFAULT, CAN_BS2_DEFAULT, CAN_BS1_DEFAULT, CAN_BAUD_PRESCALER, CAN_Mode_Normal);
GPIO_HallSensor_Init();
SysTick_Config(SystemCoreClock / 1000);
POINT_COLOR = RED;
LCD_ShowString(30, 50, 200, 16, 16, "STM32 Motor Control");
POINT_COLOR = BLUE;
while (1) {
check_hall_sensor_wires_m1();
check_hall_sensor_wires_m2();
if (CAN1_Receive_Msg(can_rx_buf)) {
handle_CAN_command(can_rx_buf);
}
if (motor_running_m1) {
actual_rpm_m1 = calculate_speed_m1();
pwm_output_m1 = PID_Controller(target_speed_m1, actual_rpm_m1, &Kp_m1, &Ki_m1, &Kd_m1, &integral_m1, &previous_error_m1);
set_pwm_duty_cycle_m1(pwm_output_m1);
} else {
set_pwm_duty_cycle_m1(0);
}
if (motor_running_m2) {
actual_rpm_m2 = calculate_speed_m2();
pwm_output_m2 = PID_Controller(target_speed_m2, actual_rpm_m2, &Kp_m2, &Ki_m2, &Kd_m2, &integral_m2, &previous_error_m2);
set_pwm_duty_cycle_m2(pwm_output_m2);
} else {
set_pwm_duty_cycle_m2(0);
}
display_status(actual_rpm_m1, hall_sensor_error_m1, actual_rpm_m2, hall_sensor_error_m2);
delay_ms(100);
}
}
void LCD_ClearString(uint16_t x, uint16_t y, uint16_t width, uint16_t height) {
POINT_COLOR = WHITE;
LCD_Fill(x, y, x + width, y + height, WHITE);
POINT_COLOR = BLUE;
}
void handle_CAN_command(uint8_t *data) {
if (data[0] == 0x1F) {
motor_running_m1 = 1;
} else if (data[0] == 0x2F) {
motor_running_m1 = 0;
} else if (data[0] == 0x3F) {
motor_running_m2 = 1;
} else if (data[0] == 0x4F) {
motor_running_m2 = 0;
}
}
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_Pin_8;
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);
GPIO_PinAFConfig(GPIOC, GPIO_PinSource8, 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_OC3Init(TIM3, &TIM_OCInitStruct);
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);
TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM3, ENABLE);
TIM_Cmd(TIM3, ENABLE);
}
void set_pwm_duty_cycle_m1(uint16_t pwm_value) {
if (pwm_value > MAX_PWM) pwm_value = MAX_PWM;
TIM_SetCompare1(TIM3, pwm_value);
}
void set_pwm_duty_cycle_m2(uint16_t pwm_value) {
if (pwm_value > MAX_PWM) pwm_value = MAX_PWM;
TIM_SetCompare3(TIM3, pwm_value);
}
void EXTI9_5_IRQHandler(void) {
if (EXTI_GetITStatus(EXTI_Line6) != RESET || EXTI_GetITStatus(EXTI_Line7) != RESET || EXTI_GetITStatus(EXTI_Line8) != RESET) {
hall_pulse_count_m1++;
EXTI_ClearITPendingBit(EXTI_Line6 | EXTI_Line7 | EXTI_Line8);
}
}
void check_hall_sensor_wires_m1(void) {
int pin_state_A = GPIO_ReadInputDataBit(HALL_PORT_M1, HALL_A_PIN_M1);
int pin_state_B = GPIO_ReadInputDataBit(HALL_PORT_M1, HALL_B_PIN_M1);
int pin_state_C = GPIO_ReadInputDataBit(HALL_PORT_M1, HALL_C_PIN_M1);
if (last_pin_state_A1 == pin_state_A && last_pin_state_B1 == pin_state_B && last_pin_state_C1 == pin_state_C) {
hall_sensor_error_m1 = 1;
} else {
hall_sensor_error_m1 = 0;
}
last_pin_state_A1 = pin_state_A;
last_pin_state_B1 = pin_state_B;
last_pin_state_C1 = pin_state_C;
}
void check_hall_sensor_wires_m2(void) {
int pin_state_A = GPIO_ReadInputDataBit(HALL_PORT_M2, HALL_A_PIN_M2);
int pin_state_B = GPIO_ReadInputDataBit(HALL_PORT_M2, HALL_B_PIN_M2);
int pin_state_C = GPIO_ReadInputDataBit(HALL_PORT_M2, HALL_C_PIN_M2);
if (last_pin_state_A2 == pin_state_A && last_pin_state_B2 == pin_state_B && last_pin_state_C2 == pin_state_C) {
hall_sensor_error_m2 = 1;
} else {
hall_sensor_error_m2 = 0;
}
last_pin_state_A2 = pin_state_A;
last_pin_state_B2 = pin_state_B;
last_pin_state_C2 = pin_state_C;
}
float calculate_speed_m1(void) {
static uint32_t last_time_m1 = 0;
uint32_t current_time_m1 = millis();
float rpm_m1 = 0;
if (hall_pulse_count_m1 > 0) {
rpm_m1 = (hall_pulse_count_m1 / 6.0) * 60.0;
hall_pulse_count_m1 = 0;
}
return rpm_m1;
}
float calculate_speed_m2(void) {
static uint32_t last_time_m2 = 0;
uint32_t current_time_m2 = millis();
float rpm_m2 = 0;
if (hall_pulse_count_m2 > 0) {
rpm_m2 = (hall_pulse_count_m2 / 6.0) * 60.0;
hall_pulse_count_m2 = 0;
}
return rpm_m2;
}
uint16_t PID_Controller(float setpoint, float measured_speed, float *Kp, float *Ki, float *Kd, float *integral, float *previous_error) {
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;
}
void EXTI0_IRQHandler(void) {
if (EXTI_GetITStatus(EXTI_Line0) != RESET) {
hall_pulse_count_m2++;
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
void EXTI1_IRQHandler(void) {
if (EXTI_GetITStatus(EXTI_Line1) != RESET) {
hall_pulse_count_m2++;
EXTI_ClearITPendingBit(EXTI_Line1);
}
}
void EXTI2_IRQHandler(void) {
if (EXTI_GetITStatus(EXTI_Line2) != RESET) {
hall_pulse_count_m2++;
EXTI_ClearITPendingBit(EXTI_Line2);
}
}
void GPIO_HallSensor_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct;
EXTI_InitTypeDef EXTI_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOG, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
GPIO_InitStruct.GPIO_Pin = HALL_A_PIN_M1 | HALL_B_PIN_M1 | HALL_C_PIN_M1;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(HALL_PORT_M1, &GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = HALL_A_PIN_M2 | HALL_B_PIN_M2 | HALL_C_PIN_M2;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(HALL_PORT_M2, &GPIO_InitStruct);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOG, EXTI_PinSource0);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOG, EXTI_PinSource1);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOG, EXTI_PinSource2);
EXTI_InitStruct.EXTI_Line = EXTI_Line0 | EXTI_Line1 | EXTI_Line2;
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 = EXTI0_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0x00;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0x00;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
NVIC_InitStruct.NVIC_IRQChannel = EXTI1_IRQn;
NVIC_Init(&NVIC_InitStruct);
NVIC_InitStruct.NVIC_IRQChannel = EXTI2_IRQn;
NVIC_Init(&NVIC_InitStruct);
}
void display_status(float speed_m1, int error_flag_m1, float speed_m2, int error_flag_m2) {
char buffer_m1[16], buffer_m2[16];
sprintf(buffer_m1, "M1 Speed: %0.1f RPM", speed_m1);
LCD_ShowString(30, 100, 200, 16, 16, buffer_m1);
sprintf(buffer_m2, "M2 Speed: %0.1f RPM", speed_m2);
LCD_ShowString(30, 120, 200, 16, 16, buffer_m2);
LCD_ClearString(30, 150, 200, 16);
if (error_flag_m1 == 0) {
LCD_ShowString(30, 150, 200, 16, 16, "M1 Sensors: OK");
} else {
LCD_ShowString(30, 150, 200, 16, 16, "M1 Sensors: ERROR");
}
if (error_flag_m2 == 0) {
LCD_ShowString(30, 170, 200, 16, 16, "M2 Sensors: OK");
} else {
LCD_ShowString(30, 170, 200, 16, 16, "M2 Sensors: ERROR");
}
}
uint32_t millis(void) {
return sys_tick_counter;
}
2024-10-24 05:48 AM
Hello @romi,
Do you have the same NVIC priority level for the EXT interrupts?
You can also use the provided API MC_GetMecSpeedAverageMotor1()/2().
2024-10-24 05:55 AM
NVIC Priority Levels: I’ve checked the NVIC priority levels for both Motor 1 and Motor 2 EXTI interrupts, and they are configured as follows:
Both motors are using the same NVIC priority levels, so there should not be a conflict in handling the interrupts.
Using MC_GetMecSpeedAverageMotor1()/2(): I will try using the MC_GetMecSpeedAverageMotor1() and MC_GetMecSpeedAverageMotor2() APIs to read the mechanical speed of both motors. I haven’t integrated the Motor Control API yet, but I will look into using these functions instead of manually calculating the speed from the Hall sensor pulses.