写在前面:
这是一个新的栏目,用于记录硬件方面学习的历程。
之前硬件学习是比较零散的,没有系统性的整理过,希望这个栏目的开设能让我的学习更加系统。
注:本栏目是在学习进程中新增的,内容无任何顺序、关联。
仅是个人记录用,不适合学习者观看。
准备:
知识储备:
- 定时器中断相关知识
- PWM电机控制相关知识
- 编码器计数器相关知识
系统组成:
- STM32F103C8T6
- L298N或TB6612电机控制模块
- 光电编码器电机
电机接口定义:
电机接口为6PIN 排线,左右两根为电机正负极,接至L298N的一个电机口
中间四根为编码器线路,外两根为编码器电源,最中间两根为编码器脉冲输出
L298N使用:
12V供电
L298N使用PWM控制时需要拔掉跳线帽,并通过BIN1和BIN2的电平高低控制正反转(4根线连接至F103,分别为PWM、GND、BIN1、BIN2)
编码器使用:
ams1117 -5.0给予5V供电
3根线接至F103,分别为脉冲2条、GND
定时器配置:
F103总共有4个定时器,配置如下:
TIM1——空
TIM2——转速采样,100HZ
TIM3——2频道,PWM信号时钟,10kHZ ,对应PA7输出PWM
TIM4——编码器计数模式,上升沿触发,对应PB6/7接编码器
代码逻辑:
当TIM2计时,回调函数获取电机脉冲并换算为速度,输入到PID控制器中,PID控制器输出占空比到L298N
PID闭环控制逻辑(重点学习):
相关笔记:
此处图片存在错误,增量式PID中的OUT无需+=,因为后面的return已经加上原始值。
注意增量PID和位置PID输出值的区别!!
代码实现(部分)
control.h
#ifndef __CONTROL_H
#define __CONTROL_H
//全局变量
extern unsigned int MotorSpeed;
extern int SpeedTarget;
extern int MotorOutput;
//函数声明
void GetMotorPulse(void);
int SpeedInnerControl(int Speed,int Target);
void SetMotorVoltageAndDirection(int Pwm);
#endif
control.c
#include "control.h"
#include "tim.h"
#include "main.h"
#include "math.h"
unsigned int MotorSpeed;//全局变量,电机当前速度数值,在encoder.c中获取
int SpeedTarget = 300; //全局变量,速度目标值
int MotorOutput; //全局变量,电机输出
//1.通过TIM4读取电机脉冲并计算速度
void GetMotorPulse(void)
{
MotorSpeed = (short)(__HAL_TIM_GET_COUNTER(&htim4)/10);//TIM4计数器获得电机脉冲,该电机在10ms采样的脉冲/10则为实际转速的rpm
__HAL_TIM_SET_COUNTER(&htim4,0);//计数器清零
}
//2.增量式PID控制器
int Error_Last,Error_Prev;//上一次偏差值,上上次误差
int Pwm_add,Pwm;//PWM增量,PWM输出占空比
float Kp = 10.0, Ki = 0.9, Kd = 0.0;//PID系数
int SpeedInnerControl(int Speed,int Target)//速度内环控制
{
int Error = Speed - Target; //偏差 = 目标速度 - 实际速度
Pwm_add = Kp * (Error - Error_Last) + //比例
Ki * Error + //积分
Kd * (Error - 2.0f * Error_Last + Error_Prev); //微分
Pwm += Pwm_add; //原始量+增量=输出量
Error_Prev = Error_Last; //保存上上次误差
Error_Last = Error; //保存上一次偏差
if(Pwm > 100) Pwm = 100; //限制上下限,防止超出PWM量程
if(Pwm <-100) Pwm =-100;
return Pwm; //返回输出值
}
//3.电机电压和方向控制函数
void SetMotorVoltageAndDirection(int Pwm)
{
if(Pwm < 0)//如果是反转
{
HAL_GPIO_WritePin(BIN1_GPIO_Port, BIN1_Pin, GPIO_PIN_SET);//L298N反向
HAL_GPIO_WritePin(BIN2_GPIO_Port, BIN2_Pin, GPIO_PIN_RESET);
Pwm = (-Pwm);//如果计算值是负值,先取负得正,因为PWM寄存器只能是正值
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, Pwm);//输出
} else
{
HAL_GPIO_WritePin(BIN1_GPIO_Port, BIN1_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(BIN2_GPIO_Port, BIN2_Pin, GPIO_PIN_SET);
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, Pwm);
}
}
TIM2回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim == &htim2)
{
//1.获取电机速度
GetMotorPulse();
//2.PID控制器,取回占空比
MotorOutput = SpeedInnerControl(MotorSpeed,SpeedTarget);
//3.将占空比导入至电机控制函数
SetMotorVoltageAndDirection(MotorOutput);
}
}
实际调试:
使用Keil5自带Debug调试
使用STM32Studio调试
用手尝试让电机停止,观察控制效果
按住RST键在放开,可以观察到电机启动的曲线
CubeMX、HAL库是个好东西
能不用杜邦线不用杜邦线
磨刀不误砍柴工
兴趣是最好的老师
懂得放弃,但有时可以捡回来
每天锻炼一小时,幸福生活一辈子
版权声明:本文为qq_23211951原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。