Motor
- 主要内容
- 前置知识
- CubeMX配置
- 代码
- 出现的问题
- 参考文献
主要内容
基于被我鸽了的电控作业
主控 STM32F429IGT6 电机TT小黄 模拟小车所以两路编码器
前置知识包括 PID PWM 定时器 LM2596 L298N等
前置知识
PID可以看我之前的这一篇PID简单算法
PWM可以看我之前的这一篇PWM
LM2596可调的降压模块 网上资料蛮多 也比较好用 拿表笔对着测一下转一下旋钮就行 注意不要接反 否则直接击穿就不能再用了
简单介绍一下L298N电机驱动模块 原理图如图
1脚接入12V及以下电压 3脚就能输出5V电压 给单片机供电 5 6由单片机接入PWM波 8 9端接电机 就能按照需要的或者调好的转速控制电机了
下图为真值表 通过信号接入也可以控制正反转
![在这里插入图片描述](https://img-blog.csdnimg.cn/6deeea3566374ef8b0bc605482896cf1.png)
当然TB6612也行 性能可能更佳 但我用过的时候有一点bug 还烧过一次钽电容 用单独模块和降压电路安全电压下直接芯片烧了 后来画PCB集成之后好了 TB6612一块要十几块钱 L298N也就几块钱 性能也没什么差别 再带一块LM2596一样的效果
CubeMX配置
在单片机控制的时候注意 用定时器1和2 开编码器模式 由于输出量是路程 所以需要单开两个基础定时器6 7做计时 一个用来求转速 一个用来调PID 后续代码处可以看到 有一个电机结构体 把算好的速度作为结构体变量之一赋值
新建CubeMX工程 开时钟 TIM1和TIM2开编码器模式 TIM3开四路PWM TIM6和TIM7开计时 并且根据自己设定的频率算出来需要的定时器周期(PSC&ARR)并且开TIM6和TIM7中断 设置抢占优先级为1 如果6.5.x及以上的CubeMX记得调Systick优先级为0
![在这里插入图片描述](https://img-blog.csdnimg.cn/711837855b86472aa6e4155ceeb17aa7.png)
(按照上述步骤开就行 这个图里还有别的引脚被别的功能用了 除了定时器开启状态外不具有参考性)
一个小tip:当你自己画板子或者你拿到的板子有些引脚没引出来 可以在搜索栏搜对应功能 一般都有别的引脚一样的功能(复用)改到那个引脚就好
代码
新建PID文件,Motor电机文件 adv底盘文件 名字不重要
PID的看前面那篇文章就有 结构体啥的基本不变 我就不多写了
motor.h:
typedef struct
{
int16_t Angle;
int32_t LastAngle;
float Speed;
int16_t TargetSpeed;
int32_t TargetAngle;
int32_t TotalAngle;
PID SpeedPID;
CascadePID AnglePID;
}Motor;
一些类似于#ifndef #endif的C语言背景知识这里就不多介绍了 少bug操作而已
motor.c:
void Motor_Cal(Motor *motor)
{
int32_t dAngle=0;
if(motor->Angle-motor->LastAngle<-30000)
dAngle=motor->Angle+(65535-motor->LastAngle);
else if(motor->Angle-motor->LastAngle>30000)
dAngle=-motor->LastAngle-(65535-motor->Angle);
else
dAngle=motor->Angle-motor->LastAngle;
motor->TotalAngle+=dAngle;
motor->Speed = (float)dAngle/(4*13*48)*50*60;
motor->LastAngle=motor->Angle;
}
前面if里面是对角度的越界处理 比如你的LastAngle有点大 一步跨到ARR之外了 就自动从0开始了 所以做这个判断
速度的计算公式 因为用了AB相 相当于四倍频 电机用的TT 13个刻度 减速比1:48 因电机而异 定时器周期前面设置的对应值 *60换成分钟
void Motor_SetPWM(float PWM1,float PWM2)
{
if(PWM1>0)
{
__HAL_TIM_SetCompare(&htim3, TIM_CHANNEL_1, PWM1);
__HAL_TIM_SetCompare(&htim3, TIM_CHANNEL_2, 0);
}
else
{
__HAL_TIM_SetCompare(&htim3, TIM_CHANNEL_1, 0);
__HAL_TIM_SetCompare(&htim3, TIM_CHANNEL_2,ABS(PWM1));
}
if(PWM2>0)
{
__HAL_TIM_SetCompare(&htim3, TIM_CHANNEL_3, PWM2);
__HAL_TIM_SetCompare(&htim3, TIM_CHANNEL_4, 0);
}
else
{
__HAL_TIM_SetCompare(&htim3, TIM_CHANNEL_3, 0);
__HAL_TIM_SetCompare(&htim3, TIM_CHANNEL_4, ABS(PWM2));
}
}
通过你设置的PWM来规定正反转 写到对应通道 用上面L298N的真值表对应
adv.h
typedef struct
{
uint8_t mode;
float averge;
Motor motors[2];
}ADV;
void Adv_init()
{
HAL_TIM_Base_Start_IT(&htim6);
HAL_TIM_Base_Start_IT(&htim7);
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_2);
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_3);
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_4);
HAL_TIM_Encoder_Start(&htim1,TIM_CHANNEL_ALL);
HAL_TIM_Encoder_Start(&htim2,TIM_CHANNEL_ALL);
Adv_PIDInit();
}
void Adv_PIDInit()
{
PID_Init(&chassis.motor[0].SpeedPID,200,0.005,0,10000,10000);
PID_Init(&chassis.motor[1].SpeedPID,200,0.006,1,10000,10000);
PID_Init(&chassis.motor[0].AnglePID.inner,200,0.005,0,10000,10000);
PID_Init(&chassis.motor[1].AnglePID.inner,200,0.006,1,10000,10000);
PID_Init(&chassis.motor[0].AnglePID.outer,0.2,0,0.5,40,40);
PID_Init(&chassis.motor[1].AnglePID.outer,0.2,0,0.5,40,40);
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef*htim)
{
if(htim==&htim6)
{
Adv.motor[0].Angle=__HAL_TIM_GET_COUNTER(&htim1);
Adv.motor[1].Angle=__HAL_TIM_GET_COUNTER(&htim2);
Motor_Cal(&Adv.motor[0]);
Motor_Cal(&Adv.motor[1]);
}
if(htim==&htim7)
{
for(uint8_t i=0;i<2;i++)
{
PID_SingleCalc(&Adv.motor[i].SpeedPID,Adv.motor[i].TargetSpeed,Adv.motor[i].Speed);
}
Motor_SetPWM(Adv.motor[0].SpeedPID.output,Adv.motor[1].SpeedPID.output);
}
}
在回调函数里面分别实现这两个功能 按设定的周期走的
主函数都不用动 除了调用底盘函数
Adv_init();
如果后续还有功能要加 比如巡线等等 直接在底盘里面修改就行
出现的问题
没啥问题 改了几个参数自己又跑了一遍 效果挺好 就是F4有点大材小用 用F1C8T6最小系统就能实现
参考文献
github暂时不太好使 后续补完整工程
1.野火零基础教程&电机教程 野火论坛或者资料下载中心可以下载 东西可太全了 百度一搜就行
2.吉甲电控yyds 学到不少东西 可惜上岁数了打不了rm寄
2022.7.16 加个广告 欢迎各位来看新写的文章 关于简单控制GM6020电机 上硬件实测好用
RM_EE_Note 1
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)