开始之前我们需要了解的是完成闭环控制输出任务前提是必须要有printf输出功能和PWM输出功能。
1)其中printf功能是为了在最后阶段,监测实时的PWM值,检测是否实现闭环控制。
关于printf功能的实现可参考教程4的内容:
STM32CubeIDE自平衡小车教程4.配置串口并实现字符的输出
2)PWM输出功能是为了实现最后车轮能按照设定的转速转动。
关于PWM输出功能的实现可参考教程5的内容:
STM32CubeIDE自平衡小车教程5.直流电机转速开环控制
以下是实现电机转速闭环控制的步骤:
1.打开上节的工程文件,在工程文件中新加一个User文件夹,在文件夹目录下新建Src和Inc文件夹,并分别添加.c文件和.h文件并命名为motor_control.c和motor_control.h
2.在motor_control.c文件中加入以下代码:
- #include "main.h"
-
- float SpeedKp = 10.0, SpeedKi = 1, SpeedKd = 0;
-
- int Motor1SpeedClosedControl(int Encoder,int Target)
- {
- static float Error,ErrorPrev,ErrorLast,PWM;
-
- Error = Target - Encoder;
-
- PWM += SpeedKp*(Error - ErrorPrev) + SpeedKi*Error + SpeedKd*(Error - 2*ErrorPrev + ErrorLast);
-
- ErrorLast = ErrorPrev;
- ErrorPrev = Error;
-
- if(PWM >= 100) PWM = 100;
- if(PWM <= -100) PWM = -100;
-
- return PWM;
- }
-
- void SetMotor1Direction(int Pwm)//设置电机方向
- {
- if(Pwm < 0)//反转
- {
- HAL_GPIO_WritePin(BIN1_GPIO_Port, BIN1_Pin, GPIO_PIN_RESET);
- HAL_GPIO_WritePin(BIN2_GPIO_Port, BIN2_Pin, GPIO_PIN_SET);
- Pwm = (-Pwm);//如果计算值是负值,先取负得正,因为PWM寄存器只能是正值
- __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_4, Pwm);
- }else
- {
- HAL_GPIO_WritePin(BIN1_GPIO_Port, BIN1_Pin, GPIO_PIN_SET);
- HAL_GPIO_WritePin(BIN2_GPIO_Port, BIN2_Pin, GPIO_PIN_RESET);
- __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_4, Pwm);
- }
- }
-
- int Motor2SpeedClosedControl(int Encoder,int Target)
- {
- static float Error,ErrorPrev,ErrorLast,PWM;
-
- Error = Target - Encoder;
-
- PWM += SpeedKp*(Error - ErrorPrev) + SpeedKi*Error + SpeedKd*(Error - 2*ErrorPrev + ErrorLast);
-
- ErrorLast = ErrorPrev;
- ErrorPrev = Error;
-
- if(PWM >= 100) PWM = 100;
- if(PWM <= -100) PWM = -100;
-
- return PWM;
- }
-
- void SetMotor2Direction(int Pwm)//设置电机方向
- {
- if(Pwm < 0)//反转
- {
- HAL_GPIO_WritePin(AIN1_GPIO_Port, AIN1_Pin, GPIO_PIN_SET);
- HAL_GPIO_WritePin(AIN2_GPIO_Port, AIN2_Pin, GPIO_PIN_RESET);
- Pwm = (-Pwm);//如果计算值是负值,先取负得正,因为PWM寄存器只能是正值
- __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_3, Pwm);
- }
- else
- {
- HAL_GPIO_WritePin(AIN1_GPIO_Port, AIN1_Pin, GPIO_PIN_RESET);
- HAL_GPIO_WritePin(AIN2_GPIO_Port, AIN2_Pin, GPIO_PIN_SET);
- __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_3, Pwm);
- }
- }
复制代码
3.在motor_control.h文件中加入以下代码:
- int Motor1SpeedClosedControl;
- int Motor2SpeedClosedControl;
- void SetMotor1Direction(int Pwm);
- void SetMotor2Direction(int Pwm);
复制代码
4.在main.c文件中在上节代码的基础中添加如下代码:
- HAL_TIM_Encoder_Start(&htim3,TIM_CHANNEL_ALL);
- HAL_TIM_Encoder_Start(&htim4,TIM_CHANNEL_ALL);
复制代码
5.打开在Core文件夹下Src文件夹中的stm32f1xx_it.c文件,下拉找到void SysTick_Handler(void),并加入以下代码(要加在CODE BEGIN和CODE END中间):
- static unsigned char Timer5msCounter;
- static int Motor1Speed;
- static int Motor1PWM;
- static int Motor2Speed;
- static int Motor2PWM;
- Timer5msCounter++;
- if(Timer5msCounter >=5)//每5s一个周期
- {
- Timer5msCounter = 0;
- Motor2Speed = GetTim4Encoder();
- Motor1Speed = GetTim3Encoder();
- printf("M1Speed = %d \n", Motor1Speed);
- printf("M2Speed = %d \n", Motor2Speed);
- Motor1PWM = Motor1SpeedClosedControl(Motor1Speed,-20);
- Motor2PWM = Motor2SpeedClosedControl(Motor2Speed,30);
- SetMotor1Direction(Motor1PWM);
- SetMotor2Direction(Motor2PWM);
- }
复制代码
6.在工程文件目录下的Core文件夹下添加Inc和Src文件夹,并添加encoder.c文件和encoder.h文件。
在encoder.c文件下添加以下代码:
- #include "tim.h"//包含tim头文件
- #include "encoder.h"
- int iTim4Encoder;//存放从TIM4定时器读出来的编码器脉冲
- int iTim3Encoder;
- int GetTim4Encoder(void)//获取TIM4定时器读出来的编码器脉冲
- {
- iTim4Encoder = (short)(__HAL_TIM_GET_COUNTER(&htim4));//先读取脉冲数
- __HAL_TIM_SET_COUNTER(&htim4,0);//再计数器清零
- return iTim4Encoder;//返回脉冲数
- }
- int GetTim3Encoder(void)//获取TIM4定时器读出来的编码器脉冲
- {
- iTim3Encoder = (short)(__HAL_TIM_GET_COUNTER(&htim3));//先读取脉冲数
- __HAL_TIM_SET_COUNTER(&htim3,0);//再计数器清零
- return iTim3Encoder;//返回脉冲数
- }
复制代码
在encoder.h文件下添加以下代码:
- int GetTim4Encoder(void);//声明函数
- int GetTim3Encoder(void);
复制代码
7.点击编译并进行烧录,完成PID速度闭环控制任务。可在在stm32f1xx_it.c中更改小车M1和M2电机的脉冲速度,正数转向为正向,负数转向为反向,并在调试器中接收小车的脉冲速度。(需要打开小车电源开关)
例如此时设置M1电机脉冲速度为-20,M2电机脉冲速度为40。
打开串口数据调试工具,可查看小车实时输出的PWM值:
|