【STM32+HAL】直流电机PID控制

一、准备工作:

有关CUBEMX的初始化配置,参见我的另一篇blog:【STM32+HAL】CUBEMX初始化配置

 有关LCD的初始化配置,参见我的另一篇blog:【STM32+HAL】LCD显示及触摸初始化配置​​​​​​

二、所用工具:

1、芯片: STM32F407ZGT6

2、STM32CubeMx软件

3、显示屏:正点原子4.3寸TFT LCD MCU电阻屏 480*800

4、驱动器:L298N

5、电机:MG310电机(GMR编码器)

三、实现功能:

实现对直流减速电机的PID控制及转速显示

四、HAL配置步骤:

1、定时器配置

2、编码器配置

编码器用于读取电机转速,原理详见下附链接

3、PWM配置

4、中断配置

至此,HAL库配置完成

五、硬件连接:

根据芯片原理图,

L298N:PA7(PWM输出IO口)接L298N的IN1,IN2接地,12V供电接输入电压源正极,电压源负极接单片机GND,GND接单片机GND,输出A接电机正负极

编码器:PA6,PC7(编码器IO口)分别接编码器A、B相

六、KEIL填写代码:

1、初始化

    HAL_TIM_Encoder_Start(&htim3, TIM_CHANNEL_ALL);//开启编码器
	HAL_TIM_Base_Start_IT(&htim3);//开启编码器中断
	HAL_TIM_Base_Start_IT(&htim2);//开启定时器中断
	HAL_TIM_PWM_Start(&htim14,TIM_CHANNEL_1);//开启PWM波
    HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer, 1);//串口接收初始化


    LCD_Init();           			
	POINT_COLOR=RED;     			//LCD显示初始化
	LCD_Clear(WHITE);
	tp_dev.init();                //LCD触摸初始化

2、PWM波生成模块

void Generate(void) 
{
	if(flag_pwm){  //PWM开关
		if(Bias<-3||Bias>3)//偏差过小时不开启改变
			Cnt+=Bias;
		Cnt=(Cnt>9992)?9992:Cnt;//防溢出
		TIM14->CCR1=Cnt;
	}
}

3、读取电机速度模块

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) 
{
  UNUSED(htim);
	if(htim->Instance==TIM2){
		Encode = (short)(__HAL_TIM_GET_COUNTER(&htim3));
		__HAL_TIM_SET_COUNTER(&htim3,0);
		Bias=PID(Encode,SetSpeed);
	}
}

4、PID偏差计算模块

int PID(int current,int target)  //PID速度控制
{
	static int      LastError;				//Error[-1]
	static int      PrevError; 			    //Error[-2]
	int iError,Outpid;						//当前误差
	target=target/0.15;						//将速度值转换为编码值

	iError=target-current;		 			//增量计算
	Outpid=(Proportion * iError)  			//E[k]项
			  -(Integral * LastError) 		//E[k-1]项
			  +(Derivative * PrevError);	//E[k-2]项

	PrevError=LastError; 					//存储误差,用于下次计算
	LastError=iError;
	return Outpid;
}

七、原理讲解

L298N、电机、单片机的线路连接(51、stm32程序)

STM32Cube HAL库——霍尔编码器测速(电机转速测量)

HAL库版STM32双轮自平衡车(一) ———代码思路和PID基础精讲

八、源码提供

【STM32+HAL】直流电机PID控制

九、更多细节

1、编码器检测电机正反转:
dir = __HAL_TIM_IS_TIM_COUNTING_DOWN(&htim3);

2、L298N正反转:

需配置两路PWM输入IN1,IN2

void PWM_Select(int x)
{
	if(x==0) 	PWMA=0,PWMB=0;
	else if(x==1)	PWMA=Moto,PWMB=0;
	else if(x==2)	PWMA=0,PWMB=Moto;
}

好的,以下是基于Arduino Mega2560的代码实现: 首先,需要定义编码器的引脚和电机控制的引脚: ``` // 编码器引脚定义 const int encoder1A = 2; const int encoder1B = 3; const int encoder2A = 18; const int encoder2B = 19; // 电机控制引脚定义 const int motor1PWM = 5; const int motor1Dir = 6; const int motor2PWM = 7; const int motor2Dir = 8; ``` 然后,需要定义一些变量来存储编码器读数和电机控制的方向和速度: ``` // 编码器读数变量 volatile long encoder1Pos = 0; volatile long encoder2Pos = 0; // 电机控制变量 int motor1Speed = 0; int motor2Speed = 0; int motor1Direction = 0; int motor2Direction = 0; ``` 接着,需要定义一些函数来读取编码器和控制电机: ``` // 读取编码器1的函数 void readEncoder1() { if (digitalRead(encoder1A) == digitalRead(encoder1B)) { encoder1Pos++; } else { encoder1Pos--; } } // 读取编码器2的函数 void readEncoder2() { if (digitalRead(encoder2A) == digitalRead(encoder2B)) { encoder2Pos++; } else { encoder2Pos--; } } // 控制电机1的函数 void controlMotor1(int speed, int direction) { analogWrite(motor1PWM, abs(speed)); digitalWrite(motor1Dir, direction); } // 控制电机2的函数 void controlMotor2(int speed, int direction) { analogWrite(motor2PWM, abs(speed)); digitalWrite(motor2Dir, direction); } ``` 在 `setup()` 函数中,需要初始化引脚和编码器的中断: ``` void setup() { // 初始化引脚 pinMode(encoder1A, INPUT); pinMode(encoder1B, INPUT); pinMode(encoder2A, INPUT); pinMode(encoder2B, INPUT); pinMode(motor1PWM, OUTPUT); pinMode(motor1Dir, OUTPUT); pinMode(motor2PWM, OUTPUT); pinMode(motor2Dir, OUTPUT); // 初始化编码器中断 attachInterrupt(digitalPinToInterrupt(encoder1A), readEncoder1, CHANGE); attachInterrupt(digitalPinToInterrupt(encoder2A), readEncoder2, CHANGE); } ``` 最后,在 `loop()` 函数中,需要控制小车按照正方形路线行驶: ``` void loop() { // 第一边 while (abs(encoder1Pos) < 500 * 30 / (PI * 65) && abs(encoder2Pos) < 500 * 30 / (PI * 65)) { controlMotor1(100, HIGH); controlMotor2(100, HIGH); } encoder1Pos = 0; encoder2Pos = 0; // 第二边 while (abs(encoder1Pos) < 500 * 30 / (PI * 65) && abs(encoder2Pos) < 500 * 30 / (PI * 65)) { controlMotor1(-100, HIGH); controlMotor2(100, HIGH); } encoder1Pos = 0; encoder2Pos = 0; // 第三边 while (abs(encoder1Pos) < 500 * 30 / (PI * 65) && abs(encoder2Pos) < 500 * 30 / (PI * 65)) { controlMotor1(-100, HIGH); controlMotor2(-100, HIGH); } encoder1Pos = 0; encoder2Pos = 0; // 第四边 while (abs(encoder1Pos) < 500 * 30 / (PI * 65) && abs(encoder2Pos) < 500 * 30 / (PI * 65)) { controlMotor1(100, HIGH); controlMotor2(-100, HIGH); } encoder1Pos = 0; encoder2Pos = 0; } ``` 其中,小车按照正方形路线行驶的代码,是通过控制两个电机的速度和方向来实现的。第一边和第三边,两个电机的速度和方向相同,第二边和第四边,两个电机的速度相同但方向相反。在每一条边行驶完成后,需要将编码器的读数清零,以便下一次行驶的时候重新计数。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值