stm32 controls DC brushed motor Hall encoder (transplantation without principle)

For basic configuration, see the previous serial port interrupt configuration.

  1. Configure pwm

  2. Configure encoder

  3. Configure timer

  4. Initialization configuration

  5. Add pid file

  6. Modify interrupt function

I want to control the position and speed of dual motors in cascade closed loop

pwm encoder Enable pin
Motor a tim1 (PE9 PE12) tim3 PE10
Motor b tim8 (PC6 PC7) tim4

PC8

first step

Wherever there are no screenshots, the default settings are

tim8 is the same as tim1

Step 2

The same goes for tim4 and tim3

third step

Configure the timer interrupt with the period you want. I made it 100hz, which means a timer interrupt every 10ms.

After the configuration is completed and the code is generated, if there is a problem compiling the new project, there is a solution in the previous article.

the fourth step

Add these before main loop

 /*Turn on PWM output*/
HAL_TIM_PWM_Start( & amp;htim1,TIM_CHANNEL_1);
HAL_TIM_PWM_Start( & amp;htim1,TIM_CHANNEL_2);
HAL_TIM_PWM_Start( & amp;htim8,TIM_CHANNEL_1);
HAL_TIM_PWM_Start( & amp;htim8,TIM_CHANNEL_2);
/*Enable timer interrupt*/
HAL_TIM_Base_Start_IT( & amp;htim6);
/*Open encoder capture*/
HAL_TIM_Encoder_Start( & amp;htim3,TIM_CHANNEL_ALL);
HAL_TIM_Encoder_Start( & amp;htim4,TIM_CHANNEL_ALL);
\t
PID_param_init();
set_motor1_enable();
set_motor2_enable();
  is_motor_en = 1;
\t
motor1_go(-30);
motor2_go(30);

the fifth step

Add the c and h files of pid and the c and h files of control

pid c file

#include "bsp_pid.h"


//Define global variables

_pid pid_location1;
_pid pid_speed1;
_pid pid_location2;
_pid pid_speed2;

/**
  * @brief PID parameter initialization
* @note None
  * @retval None
  */
void PID_param_init(void)
{
/* 111 position related initialization parameters */
    pid_location1.target_val=0;
    pid_location1.actual_val=0.0;
    pid_location1.err=0.0;
    pid_location1.err_last=0.0;
    pid_location1.integral=0.0;
  
pid_location1.Kp = 2;
pid_location1.Ki = 0.0;
pid_location1.Kd = 0.3;
  
  /* 111 speed related initialization parameters */
    pid_speed1.target_val=0.0;
    pid_speed1.actual_val=0.0;
    pid_speed1.err=0.0;
    pid_speed1.err_last=0.0;
    pid_speed1.integral=0.0;
  
pid_speed1.Kp = 3;
pid_speed1.Ki = 0.4;
pid_speed1.Kd = 0.01;
\t\t
/* 222 position related initialization parameters */
pid_location2.target_val=0;
    pid_location2.actual_val=0.0;
    pid_location2.err=0.0;
    pid_location2.err_last=0.0;
    pid_location2.integral=0.0;
  
pid_location2.Kp = 2;
pid_location2.Ki = 0.0;
pid_location2.Kd = 0.3;
  
  /* 222 speed related initialization parameters */
    pid_speed2.target_val=0.0;
    pid_speed2.actual_val=0.0;
    pid_speed2.err=0.0;
    pid_speed2.err_last=0.0;
    pid_speed2.integral=0.0;
  
pid_speed2.Kp = 3;
pid_speed2.Ki = 0.4;
pid_speed2.Kd = 0.01;
}

/**
  * @brief Set target value
  * @param val target value
* @note none
  * @retval None
  */
void set_pid_target(_pid *pid, float temp_val)
{
  pid->target_val = temp_val; //Set the current target value
}

/**
  * @brief Get the target value
  * @param None
* @note none
  * @retval target value
  */
float get_pid_target(_pid *pid)
{
  return pid->target_val; // current target value
}

/**
  * @brief Set proportion, integral and differential coefficients
  * @param p: proportional coefficient P
  * @param i: integral coefficient i
  * @param d: Differential coefficient d
* @note none
  * @retval None
  */
void set_p_i_d(_pid *pid, float p, float i, float d)
{
  pid->Kp = p; //Set the proportion coefficient P
pid->Ki = i; //Set the integral coefficient I
pid->Kd = d; //Set the differential coefficient D
}

/**
  * @brief Position PID algorithm implementation
  * @param actual_val: actual value
* @note none
  * @retval Output calculated by PID
  */
float location_pid_realize_Motor1(_pid *pid, float actual_val)
{
/*Calculate the error between target value and actual value*/
    pid->err=pid->target_val-actual_val;
  
    /* Set closed-loop dead zone */
    if((pid->err >= -20) & amp; & amp; (pid->err <= 20))
    {
      pid->err = 0;
      pid->integral = 0;
    }
    
    pid->integral + = pid->err; // Error accumulation

/*PID algorithm implementation*/
    pid->actual_val = pid->Kp*pid->err + pid->Ki*pid->integral + pid->Kd*(pid->err-pid->err_last);
  
/*Error propagation*/
    pid->err_last=pid->err;
    
/*Return the current actual value*/
    return pid->actual_val;
}

float location_pid_realize_Motor2(_pid *pid, float actual_val)
{
/*Calculate the error between target value and actual value*/
    pid->err=pid->target_val-actual_val;
  
    /* Set closed-loop dead zone */
    if((pid->err >= -20) & amp; & amp; (pid->err <= 20))
    {
      pid->err = 0;
      pid->integral = 0;
    }
    
    pid->integral + = pid->err; // Error accumulation

/*PID algorithm implementation*/
    pid->actual_val = pid->Kp*pid->err + pid->Ki*pid->integral + pid->Kd*(pid->err-pid->err_last);
  
/*Error propagation*/
    pid->err_last=pid->err;
    
/*Return the current actual value*/
    return pid->actual_val;
}

/**
  * @brief Speed PID algorithm implementation
  * @param actual_val: actual value
* @note None
  * @retval Output calculated by PID
  */
float speed_pid_realize(_pid *pid, float actual_val)
{
/*Calculate the error between target value and actual value*/
    pid->err=pid->target_val-actual_val;

// if((pid->err<0.2f ) & amp; & amp; (pid->err>-0.2f))
// pid->err = 0.0f;

    pid->integral + = pid->err; // Error accumulation

/*PID algorithm implementation*/
    pid->actual_val = pid->Kp*pid->err + pid->Ki*pid->integral + pid->Kd*(pid->err-pid->err_last);
  
/*Error propagation*/
    pid->err_last=pid->err;
    
/*Return the current actual value*/
    return pid->actual_val;
}

/************************************ END OF FIER ********** *********************************************/

h file of pid

#ifndef __BSP_PID_H
#define __BSP_PID_H
#include "stm32f4xx.h"
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define PID_ASSISTANT_EN

typedef struct
{
    float target_val; //Target value
    float actual_val; //actual value
    float err; //Define the deviation value
    float err_last; //Define the previous deviation value
    float Kp,Ki,Kd; //Define proportion, integral and differential coefficients
    float integral; //define integral value
}_pid;

extern _pid pid_location1;
extern _pid pid_speed1;
extern _pid pid_location2;
extern _pid pid_speed2;

extern void PID_param_init(void);
extern void set_pid_target(_pid *pid, float temp_val);
extern float get_pid_target(_pid *pid);
extern void set_p_i_d(_pid *pid, float p, float i, float d);
extern float location_pid_realize_Motor1(_pid *pid, float actual_val);
extern float location_pid_realize_Motor2(_pid *pid, float actual_val);
extern float speed_pid_realize(_pid *pid, float actual_val);

#endif

contral c file

#include "tim.h"
#include "gpio.h"
#include "bsp_pid.h"
#include "contral.h"
#include <stdio.h>
#include <math.h>
#include "stdlib.h"


int mode1_flag=0,mode4_flag=1,mode4=0,num_jishu=1;

unsigned char location_control_count_Motor1 = 0; //If the execution frequency does not need to be so high, use this event count and use it in interrupts
unsigned char location_control_count_Motor2 = 0;
float Motor1PWM =0.0, Motor2PWM =0.0;
float speed_Outval_Motor1, speed_Outval_Motor2, location_Outval_Motor1, location_Outval_Motor2;

static motor_dir_t direction = MOTOR_FWD; // Record motor direction
static uint16_t dutyfactor = 0; // Record motor duty cycle
u8 is_motor_en = 0; // Motor enable

long g_lMotor1PulseSigma =0;
long g_lMotor2PulseSigma =0;
short g_nMotor1Pulse=0;//Global variable, saves the motor pulse value
short g_nMotor2Pulse=0;//Global variable, saves the motor pulse value


/************************Read the encoder value during actual operation************************ **/
void GetMotor1Pulse(void)//Read motor pulse
{
g_nMotor1Pulse = (short)(__HAL_TIM_GET_COUNTER( & amp;htim3));//Get the counter value
\t
__HAL_TIM_SET_COUNTER( & amp;htim3,0); //TIM3 counter clear
\t
g_lMotor1PulseSigma + = g_nMotor1Pulse;//Pulse accumulation used by the position outer loop
}

void GetMotor2Pulse(void)//Read motor pulse
{
g_nMotor2Pulse = (short)(__HAL_TIM_GET_COUNTER( & amp;htim4));//Get the counter value
\t
__HAL_TIM_SET_COUNTER( & amp;htim4,0);//TIM4 counter clear
\t
g_lMotor2PulseSigma + = g_nMotor2Pulse;//Pulse accumulation used by the position outer loop
}


/*Add here a function that directly programs the input of the position ring into distance (cm)*/ //This is used for all ordinary straight lines
void motor1_go(int32_t location_cm) //Straight travel function //Two consecutive straight travels seem to invalidate the line patrol compensation
{
float motor_location;

motor_location = ( location_cm / (6.5 * 3.14) ) * (45*4*13) ; //motor_location converts location_cm into the corresponding number of pulses g_fTargetJourney = how many turns * number of pulses in 1 turn

  set_pid_target( & amp;pid_location1, motor_location);
 
  set_motor1_enable(); //Enable motor control PWM output
 
// g_lMotorPulseSigma = 0; //It was not cleared before so it cannot be used.
}

void motor2_go(int32_t location_cm) //Straight travel function //Two consecutive straight travels seem to invalidate the line patrol compensation
{
float motor_location;

motor_location = ( location_cm / (6.5 * 3.14) ) * (45*4*13) ; //motor_location converts location_cm into the corresponding number of pulses g_fTargetJourney = how many turns * number of pulses in 1 turn

  set_pid_target( & amp;pid_location2, motor_location);
 
  set_motor2_enable(); //Enable motor control PWM output
 
// g_lMotorPulseSigma = 0; //It was not cleared before so it cannot be used.
}

/****Speed loop position loop cascade PID control*****/
void Location_Speed_control_Motor1(void)
{
if (is_motor_en == 1) // The motor is controlled only when it is enabled.
{
location_control_count_Motor1 + + ;
if(location_control_count_Motor1 >= 2)
{
location_control_count_Motor1 = 0;
location_Outval_Motor1 = location_pid_control_Motor1();
}
set_pid_target(&pid_speed1, location_Outval_Motor1); //There must be a position ring value every time
          speed_Outval_Motor1 = speed_pid_control_Motor1(); //If the motor rotation is not as expected, take the inverse value in these two sentences
}
}

void Location_Speed_control_Motor2(void)
{
if (is_motor_en == 1) // The motor is controlled only when it is enabled.
{
location_control_count_Motor2 + + ;
if(location_control_count_Motor2 >= 2)
{
location_control_count_Motor2 = 0;
location_Outval_Motor2 = location_pid_control_Motor2();
}
set_pid_target( & amp;pid_speed2, location_Outval_Motor2); //There must be a position ring value every time
          speed_Outval_Motor2 = speed_pid_control_Motor2(); //If the motor rotation is not as expected, take the inverse value in these two sentences
}
}

float location_pid_control_Motor1(void)
{
float cont_val = 0.0;
int32_t actual_location;
\t
actual_location = g_lMotor1PulseSigma; //1 circle = 2340 pulses = 45*13*4 //The position here is replaced by the number of circles.

    cont_val = location_pid_realize_Motor1( & amp;pid_location1, actual_location);
 
     /* Target speed upper limit processing */
      if (cont_val > TARGET_SPEED_MAX)
      {
        cont_val = TARGET_SPEED_MAX;
      }
      else if (cont_val < -TARGET_SPEED_MAX)
      {
        cont_val = -TARGET_SPEED_MAX;
      }
return cont_val;
}

float location_pid_control_Motor2(void)
{
float cont_val = 0.0;
int32_t actual_location;
\t
actual_location = g_lMotor2PulseSigma; //1 circle = 2340 pulses = 45*13*4 //The position here is replaced by the number of circles.

    cont_val = location_pid_realize_Motor2( & amp;pid_location2, actual_location);
 
     /* Target speed upper limit processing */
      if (cont_val > TARGET_SPEED_MAX)
      {
        cont_val = TARGET_SPEED_MAX;
      }
      else if (cont_val < -TARGET_SPEED_MAX)
      {
        cont_val = -TARGET_SPEED_MAX;
      }
return cont_val;
}


float speed_pid_control_Motor1(void)
{
    float cont_val = 0.0; // current control value
    int32_t actual_speed;

actual_speed = ((float)g_nMotor1Pulse*1000.0*60.0)/(4 * 13 * 45 * 20);

    cont_val = speed_pid_realize( & amp;pid_speed1, actual_speed); // Perform PID calculation
\t
return cont_val;
}

float speed_pid_control_Motor2(void)
{
    float cont_val = 0.0; // current control value
    int32_t actual_speed;

actual_speed = ((float)g_nMotor2Pulse*1000.0*60.0)/(4 * 13 * 45 * 20);

    cont_val = speed_pid_realize( & amp;pid_speed2, actual_speed); // Perform PID calculation
\t
return cont_val;
}


/************************Motor control function******************/
void Motor1Output(int nMotorPwm)//Set motor voltage and direction
{
if (nMotorPwm >= 0) // Determine the motor direction
{
set_motor1_direction(MOTOR_FWD); //The positive direction must correspond
}
else
{
nMotorPwm = -nMotorPwm;
set_motor1_direction(MOTOR_REV); //The positive direction must correspond
}
nMotorPwm = (nMotorPwm > PWM_MAX_PERIOD_COUNT) ? PWM_MAX_PERIOD_COUNT : nMotorPwm; // Speed upper limit processing

set_motor1_speed(nMotorPwm); //Set PWM duty cycle
 }

 void Motor2Output(int nMotorPwm)//Set motor voltage and direction
{
if (nMotorPwm >= 0) // Determine the motor direction
{
set_motor2_direction(MOTOR_FWD); //The positive direction must correspond
}
else
{
nMotorPwm = -nMotorPwm;
set_motor2_direction(MOTOR_REV); //The positive direction must correspond
}
nMotorPwm = (nMotorPwm > PWM_MAX_PERIOD_COUNT) ? PWM_MAX_PERIOD_COUNT : nMotorPwm; // Speed upper limit processing

set_motor2_speed(nMotorPwm); //Set PWM duty cycle
 }

/**
  * @brief Set motor speed
  * @param v: speed (duty cycle)
  * @retval None
  */
void set_motor1_speed(uint16_t v)
{
  dutyfactor = v;
  
  if (direction == MOTOR_FWD)
  {
    SET_FWD_COMPAER_Motor1(dutyfactor); // Set speed
SET_REV_COMPAER_Motor1(0);
  }
  else
  {
SET_FWD_COMPAER_Motor1(0);
    SET_REV_COMPAER_Motor1(dutyfactor); // Set speed
  }
}

void set_motor2_speed(uint16_t v)
{
  dutyfactor = v;
  
  if (direction == MOTOR_FWD)
  {
    SET_FWD_COMPAER_Motor2(dutyfactor); // Set speed
SET_REV_COMPAER_Motor2(0);
  }
  else
  {
SET_FWD_COMPAER_Motor2(0);
    SET_REV_COMPAER_Motor2(dutyfactor); // Set speed
  }
}

/**
  * @brief Set motor direction
  * @param None
  * @retval None
  */
void set_motor1_direction(motor_dir_t dir)
{
  direction = dir;
  
  if (direction == MOTOR_FWD)
  {
SET_FWD_COMPAER_Motor1(dutyfactor); // Set speed
    SET_REV_COMPAER_Motor1(0); // Set speed
  }
  else
  {
SET_FWD_COMPAER_Motor1(0); // Set speed
    SET_REV_COMPAER_Motor1(dutyfactor); // Set speed
  }
}

void set_motor2_direction(motor_dir_t dir)
{
  direction = dir;
  
  if (direction == MOTOR_FWD)
  {
SET_FWD_COMPAER_Motor2(dutyfactor); // Set speed
    SET_REV_COMPAER_Motor2(0); // Set speed
  }
  else
  {
SET_FWD_COMPAER_Motor2(0); // Set speed
    SET_REV_COMPAER_Motor2(dutyfactor); // Set speed
  }
}

/**
  * @brief enable motor
  * @param None
  * @retval None
  */
void set_motor1_enable(void) //Are these two enable and disable functions still valid for bipolar control?
{
is_motor_en = 1;
  MOTOR1_ENABLE_SD();
  MOTOR1_FWD_ENABLE();
  MOTOR1_REV_ENABLE();
}

void set_motor2_enable(void) //Are these two enable and disable functions still valid for bipolar control?
{
is_motor_en = 1;
  MOTOR2_ENABLE_SD();
  MOTOR2_FWD_ENABLE();
  MOTOR2_REV_ENABLE();
}

/**
  * @brief disable motor
  * @param None
  * @retval None
  */
void set_motor1_disable(void)
{
   is_motor_en = 0;
  MOTOR1_DISABLE_SD();
  MOTOR1_FWD_DISABLE();
  MOTOR1_REV_DISABLE();
}

void set_motor2_disable(void)
{
   is_motor_en = 0;
  MOTOR2_DISABLE_SD();
  MOTOR2_FWD_DISABLE();
  MOTOR2_REV_DISABLE();
}

contral h file

#ifndef __CONTROL_H
#define __CONTROL_H

#include "main.h"
#include <stdio.h>
#include <stdlib.h>
#include "stm32f4xx.h"
#include "tim.h"
#include "bsp_pid.h"

extern _pid pid_location1, pid_location2;
extern float speed_Outval_Motor1, speed_Outval_Motor2, location_Outval_Motor1, location_Outval_Motor2;
extern u8 is_motor_en;
extern float Motor1PWM, Motor2PWM;

/* Motor SD or EN enable pin */
#define MOTOR1_ENABLE_SD() HAL_GPIO_WritePin(MOTOR1EN_GPIO_Port, MOTOR1EN_Pin, GPIO_PIN_SET) // High level on - high level enable
#define MOTOR1_DISABLE_SD() HAL_GPIO_WritePin(MOTOR1EN_GPIO_Port, MOTOR1EN_Pin, GPIO_PIN_RESET) // Low level shutdown - low level disable
#define MOTOR2_ENABLE_SD() HAL_GPIO_WritePin(MOTOR2EN_GPIO_Port, MOTOR2EN_Pin, GPIO_PIN_SET) // High level on - high level enable
#define MOTOR2_DISABLE_SD() HAL_GPIO_WritePin(MOTOR2EN_GPIO_Port, MOTOR2EN_Pin, GPIO_PIN_RESET) // Low level shutdown - low level disable

/* After accumulating TIM_Period times, an update or interrupt is generated*/
/* When the timer counts from 0 to PWM_PERIOD_COUNT, it is PWM_PERIOD_COUNT + 1 times, which is a timing period */
#define PWM_PERIOD_COUNT (4200) //You can try to make this larger so that PID control can be smoother
#define PWM2_PERIOD_COUNT (4200)

/* Maximum comparison value */
#define PWM_MAX_PERIOD_COUNT (PWM_PERIOD_COUNT - 100) //If the PWM becomes full, some driver boards will have problems (hardware reasons)
#define PWM2_MAX_PERIOD_COUNT (PWM2_PERIOD_COUNT - 100)

/****************Motor pin initialization******************/
/* Set speed (duty cycle) */
#define SET_FWD_COMPAER_Motor1(ChannelPulse) __HAL_TIM_SET_COMPARE( & amp;htim1,TIM_CHANNEL_1,ChannelPulse) // Set the value of the comparison register //AIN1
#define SET_REV_COMPAER_Motor1(ChannelPulse) __HAL_TIM_SET_COMPARE( & amp;htim1,TIM_CHANNEL_2,ChannelPulse) // Set the value of the comparison register //AIN2
#define SET_FWD_COMPAER_Motor2(ChannelPulse) __HAL_TIM_SET_COMPARE( & amp;htim8,TIM_CHANNEL_1,ChannelPulse) // Set the value of the comparison register //AIN1
#define SET_REV_COMPAER_Motor2(ChannelPulse) __HAL_TIM_SET_COMPARE( & amp;htim8,TIM_CHANNEL_2,ChannelPulse) // Set the value of the comparison register //AIN2

/* Enable output */
#define MOTOR1_FWD_ENABLE() HAL_TIM_PWM_Start( & amp;htim1,TIM_CHANNEL_1);
#define MOTOR1_REV_ENABLE() HAL_TIM_PWM_Start( & amp;htim1,TIM_CHANNEL_2);
#define MOTOR2_FWD_ENABLE() HAL_TIM_PWM_Start( & amp;htim8,TIM_CHANNEL_1);
#define MOTOR2_REV_ENABLE() HAL_TIM_PWM_Start( & amp;htim8,TIM_CHANNEL_2);

/* Disable output */
#define MOTOR1_FWD_DISABLE() HAL_TIM_PWM_Stop( & amp;htim1,TIM_CHANNEL_1);
#define MOTOR1_REV_DISABLE() HAL_TIM_PWM_Stop( & amp;htim1,TIM_CHANNEL_2);
#define MOTOR2_FWD_DISABLE() HAL_TIM_PWM_Stop( & amp;htim8,TIM_CHANNEL_1);
#define MOTOR2_REV_DISABLE() HAL_TIM_PWM_Stop( & amp;htim8,TIM_CHANNEL_2);

#define TARGET_SPEED_MAX 100 60rpm can cover 60cm in 3 seconds

/* Motor direction control enumeration */
typedef enum
{
  MOTOR_FWD = 0,
  MOTOR_REV,
}motor_dir_t;

void Location_Speed_control_Motor1(void);
void Location_Speed_control_Motor2(void);
float location_pid_control_Motor1(void);
float location_pid_control_Motor2(void);
float speed_pid_control_Motor1(void);
float speed_pid_control_Motor2(void);
void Motor1Output(int nMotorPwm);
void Motor2Output(int nMotorPwm);
void set_motor1_speed(uint16_t v);
void set_motor2_speed(uint16_t v);
void set_motor1_direction(motor_dir_t dir);
void set_motor2_direction(motor_dir_t dir);
void set_motor1_enable(void);
void set_motor1_disable(void);
void set_motor2_enable(void);
void set_motor2_disable(void);
void motor1_go(int32_t location_cm);
void motor2_go(int32_t location_cm);
void GetMotor1Pulse(void);
void GetMotor2Pulse(void);

#endif

I’m pretty rough with double, but in actual testing it works just fine.

Step 6

Modify the interrupt file as follows

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  if(htim==( & amp;htim3))
  {

  }
else if(htim==( & amp;htim4))
  {

  }
  else if(htim==( & amp;htim6))
  {
  GetMotor1Pulse();
GetMotor2Pulse();
     if(is_motor_en == 1){
     Location_Speed_control_Motor1();
Location_Speed_control_Motor2();
     Motor1PWM = speed_Outval_Motor1;
Motor2PWM = speed_Outval_Motor2;
Motor1Output(Motor1PWM);
Motor2Output(Motor2PWM);
}
  }
}

Then you can use this function to close the loop of the motor during initialization.

motor1_go(-30);
motor2_go(30);

There are a lot of notes in the project, and I have uploaded them all. If you need them, you can download them by yourself. You can find related services very cheaply by searching on a certain website.