【零基础学STM32】CubeMX+HAL玩转电机控制

2023-05-16

Motor

  • 主要内容
  • 前置知识
  • CubeMX配置
  • 代码
  • 出现的问题
  • 参考文献

主要内容

基于被我鸽了的电控作业
主控 STM32F429IGT6 电机TT小黄 模拟小车所以两路编码器
前置知识包括 PID PWM 定时器 LM2596 L298N等

前置知识

PID可以看我之前的这一篇PID简单算法
PWM可以看我之前的这一篇PWM
LM2596可调的降压模块 网上资料蛮多 也比较好用 拿表笔对着测一下转一下旋钮就行 注意不要接反 否则直接击穿就不能再用了
简单介绍一下L298N电机驱动模块 原理图如图
在这里插入图片描述1脚接入12V及以下电压 3脚就能输出5V电压 给单片机供电 5 6由单片机接入PWM波 8 9端接电机 就能按照需要的或者调好的转速控制电机了
下图为真值表 通过信号接入也可以控制正反转
在这里插入图片描述
当然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
在这里插入图片描述
(按照上述步骤开就行 这个图里还有别的引脚被别的功能用了 除了定时器开启状态外不具有参考性)
一个小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;//rpm   *50*60与定时器频率匹配
	//倍频 刻度 减速比 定时器中断周期 换算成分钟
	//记录角度
	motor->LastAngle=motor->Angle;
}

前面if里面是对角度的越界处理 比如你的LastAngle有点大 一步跨到ARR之外了 就自动从0开始了 所以做这个判断
速度的计算公式 因为用了AB相 相当于四倍频 电机用的TT 13个刻度 减速比1:48 因电机而异 定时器周期前面设置的对应值 *60换成分钟

//设置马达pwm
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);			//电机pwm
	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();
}
/***************内部函数********************/
//PID初始化
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);
  }
}

在回调函数里面分别实现这两个功能 按设定的周期走的
主函数都不用动 除了调用底盘函数

/* USER CODE BEGIN 2 */
  Adv_init();
  /* USER CODE END 2 */

如果后续还有功能要加 比如巡线等等 直接在底盘里面修改就行

出现的问题

没啥问题 改了几个参数自己又跑了一遍 效果挺好 就是F4有点大材小用 用F1C8T6最小系统就能实现

参考文献

github暂时不太好使 后续补完整工程
1.野火零基础教程&电机教程 野火论坛或者资料下载中心可以下载 东西可太全了 百度一搜就行
2.吉甲电控yyds 学到不少东西 可惜上岁数了打不了rm寄

2022.7.16 加个广告 欢迎各位来看新写的文章 关于简单控制GM6020电机 上硬件实测好用
RM_EE_Note 1

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【零基础学STM32】CubeMX+HAL玩转电机控制 的相关文章

  • 2.1.2 进程的状态与转换

    目录 知识总览 1 三种基本状态 xff08 1 xff09 运行态 xff08 Running xff09 xff08 2 xff09 就绪态 xff08 Ready xff09 xff08 3 xff09 阻塞态 xff08 Waiti
  • 合并排序与快速排序算法

    文章目录 算法介绍代码实现1 合并排序2 快速排序 总结 算法介绍 合并排序与快速排序是排序算法中常用的两种排序算法 xff0c 合并排序把数据分为两段 xff0c 从两段中逐个选最小的元素移入新数据的末尾 xff1b 快速排序是在序列中挑
  • 2进制、8进制、10进制、16进制相互转换

    文章目录 介绍1 二进制转其他进制2 八进制转其他进制3 十进制转其他进制4 十六进制转其他进制总结 介绍 百度百科 xff1a 进制也就是进位计数制 xff0c 是人为定义的带进位的计数方法 xff08 有不带进位的计数方法 xff0c
  • Hadoop学习笔记--HBase相关操作指令

    文章目录 引言0 HBase的启动与停止1 创建表2 删除表3 表的其他操作3 1 修改表结构3 2 查看表结构3 3 显示所有用户定义的表3 4 查询表是否存在3 5 查询表是否可用3 6 查询表中记录数 4 插入数据4 1 插入单条数据
  • Hadoop学习笔记--MapReduce

    文章目录 引言1 经典案例分析 xff1a WordCount1 1 Map阶段 xff1a TokenizerMapper类1 2 Reduce阶段 xff1a IntSumReduce类1 3 Driver阶段 xff1a main x
  • Hadoop学习笔记--YARN

    文章目录 引言1 YARN的优势1 1 可扩展性 xff08 Scalability xff09 1 2 可用性 xff08 Availability xff09 1 3 利用率 xff08 Utilization xff09 1 4 多租
  • scala编程--计算级数&统计学生成绩

    目录 案例1 xff1a 计算级数1 1 案例要求1 2 实现代码 案例2 xff1a 统计学生成绩1 1 案例要求1 2 实现代码 总结 案例1 xff1a 计算级数 1 1 案例要求 级数计算函数实现较为简单 xff0c 这里不再赘述
  • RDD本地文件系统读入显示文件不存在fileNotExit

    问题 在从本地文件系统读入数据创建RDD时 xff0c 执行行动操作 xff0c 显示文件不存在 解决方案 从本地文件系统读入时 xff0c 需在所有节点的相同目录下存在该文件 xff0c 如读取文件在 file span class to
  • spark-shell报错:java.io.IOException: Failed to replace a bad datanode on the existing pipeline due to

    目录 问题报错信息原因解决方案 问题 spark shell在执行一段时间后报错 xff1a java io IOException Failed to replace a bad datanode on the existing pipe
  • DPDK-VPP 学习笔记-06 bond 链路聚合 LACP

    DPDK VPP 学习笔记 06 bond 链路聚合 LACP 本笔记主要测试VPP与交换机链路聚合配置 amp 状态 Switch HW CE6880NIC Mellanox CX5 链路聚合简介 以太网链路聚合 Eth Trunk 简称
  • 基于Linux的小项目-在线词典

    接触到的基于Linux的第一个实战小项目 xff0c 在众多大牛的协助下完美实现 在线词典功能的实现 1 用户的注册 2 用户登录 3 单词的查询 4 历史记录的查看 5 用户在登录后修改密码 头文件 span class token ma
  • 树莓派串口通信(C语言wiringPi库)

    串口通信 配置gpio串口wringPi库串口函数代码测试 配置gpio串口 开启gpio串口 输入命令sudo raspi config选择Interfacing找到serial并打开选择OK 树莓派包含两个串口 硬件串口 xff08 d
  • 滑模控制问题总结

    设计切换函数 滑模面S xff08 x xff09 线性 xff1a 滑模面参数确定方法 xff1a Hurwitz 保证多项式为Hurwitz xff0c 即特征值在左半平面 主要适用于速度和精度要求都不高的非线性系统 非线性 滑模变结构
  • ESP8266的测试

    实际测试esp8266 一 线路连接 工具 xff1a WIFI模块1个 xff0c 串口软件1个 xff0c usb转串口工具一个 xff0c 杜邦线若干 1 找到模块的相关资料 xff0c 按照说明连接引脚 先看一下原理图和引脚说明 x
  • 【bug】弹窗列表,每次打开数据回显的选中项不对,已解决

    一个小bug xff0c 还是记录下吧 弹窗列表 xff0c 勾选数据后回显到表单里 xff0c 再次打开要选中状态 其实就是调弹窗数据的时候 xff0c 处理下 获取列表数据 getsalesdata span class token p
  • 【bug】vue-screenfull全屏时页面高度未自适应,已解决

    这个bug花了我下午快3个小时的时间去查找原因以及半个小时左右的时间去解决 xff0c 花了这么多精力 xff0c 值得记录下来 xff0c 避免以后再踩坑 一 vue screenfull是一款插件 xff0c 算是比较常用的插件了 xf
  • vue+el-form,表单内实现@keyup上下左右箭头移动聚焦

    需求是年前提的 xff0c 因为不着急 xff0c 就一直搁置着 xff0c 放在最后弄 年前快放假的时候 xff0c 抽空搜了下 xff0c 在表单内实现上下箭头移动聚焦的没搜到 xff0c 倒是有在表格内如何实现的 xff0c 但没有细
  • 邮件发送,正文内容格式h5,模板字符串拼接使用及赋值,原生js

    一 邮件发送 需求不难 xff0c 传指定的参数给后端 xff0c 调接口就直接发送了 就是参数里有个正文部分 xff0c 一定要用h5写 写vue写习惯了 xff0c 原生的都快忘光了 xff0c 走了些弯路 二 h5 在页面上有个邮件发
  • js如何按某个字符分割字符串,js截取字符串

    每次遇到截取字段串 xff0c 都感觉没那么顺心应手 js的方法要多记录 截取指定位置后的内容 1 假设str 61 aaabbbcccddd 我想要拿到ddd var str span class token operator 61 sp
  • 【bug】el-drowdown报错Cannot read properties of undefined (reading ‘0‘)“

    报错定位不到具体原因的时候 xff0c 就很抓狂 el dropdown一直报错 xff0c Cannot read properties of undefined reading 0 34 这种最不好排查了 span class toke

随机推荐