当前位置: 首页 > news >正文

平衡车 - 电机调速

🌈个人主页:羽晨同学

💫个人格言:“成为自己未来的主人~” 

在我们的这篇文章当中,我们主要想要实现的功能的是电机调速功能。在我们的这篇文章中,主要实现的是开环的功能,而非闭环,也就是不加入PID对电机速度进行控制。

首先,我们需要将STBY引脚设置为高电平,因为STBY为休眠引脚,当STBY为低电平的时候,电机处于休眠状态,此时电机是不会进行运动的。

由电路图可知,STBY引脚,也就是PA1引脚,所以,我们需要将PA1设置为高电平。

在这个之前,我们先实现按键的功能,通过按键控制电机速度。

static Button_TypeDef userKey;//用户按钮
static void OnUserKey_Clicked(uint8_t clicks);
//
// @简介: 按键任务的初始化函数
//
void App_Button_Init(void)
{Button_InitTypeDef Button_InitStruct = {0};Button_InitStruct.GPIOx = GPIOA;Button_InitStruct.GPIO_Pin = GPIO_Pin_11;My_Button_Init(&userKey,&Button_InitStruct);My_Button_SetClickCb(&userKey,OnUserKey_Clicked);
}

通过回调函数来控制按键按下之后的操作。

//
// @简介: 按键回调函数
//
static void OnUserKey_Clicked(uint8_t clicks)
{}

所以,接下来,我们通过回调函数来实现对应的功能

首先,我们先设置STBY,来控制是休眠状态还是启动状态,也就是说,我们需要先配置PA1引脚。

//
// @简介: 初始化STBY引脚 PA1 -- Out -PP
//
static void STBY_Pin_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);GPIO_InitTypeDef GPIO_InitStruct = {0};GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;GPIO_Init(GPIOA,&GPIO_InitStruct);}

然后,我们设置一个函数来控制STBY的状态

//
// @简介: 控制TB6612进入休眠状态或者活动状态
// @参数: on  0 - 休眠状态,向STBY写L
// 					 非零 - 活动状态,向STBY写H
//
void App_Pwm_Cmd(uint8_t on)
{if(on == 0){GPIO_WriteBit(GPIOA,GPIO_Pin_1,Bit_RESET);//休眠}else{GPIO_WriteBit(GPIOA,GPIO_Pin_1,Bit_SET);//觉醒}
}

这样的话,我们就可以在按键回调函数当中,通过按下按键来改变对应的状态

//
// @简介: 按键回调函数
//
static void OnUserKey_Clicked(uint8_t clicks)
{if(clicks == 1){pwm_on^=1;App_Pwm_Cmd(pwm_on);}
}

所以,这个时候来说,我们就实现了,只要按下按键,就可以控制STBY的状态。

然后,我们在main中对PWM进行初始化

#include "stm32f10x.h"
#include "delay.h"
#include "app_bat.h"
#include "app_button.h"
int main(void)
{NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);App_Bat_Init();App_Button_Init();App_Pwm_Init();while(1){App_Bat_Proc();App_Button_Proc();}
}

这样,就可以实现通过按键来控制STBY的状态了。

接下来,我们就来实现电机调速的具体功能。

这个是电机控制的具体的引脚图,我们可以看到在这个H桥的结构当中,AIN1为正,AIN2为负的时候,是正转,否则反转,所以,我们可以通过控制AIN1和AIN2来控制电机转动的方向,通过PWMA和PWMB来控制速度,而对应的AIN1和AIN2为PA10和PA9,同理,BIN1和BIN2为PB5和PB7,所以,首先,我们需要对这些引脚进行初始化。

//
// @简介: 左电机的初始化
//
static void Motor_L_Init(void)
{//初始化 PA9 PA10为Out_PPRCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);GPIO_InitTypeDef GPIO_InitStruct = {0};GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;GPIO_Init(GPIOA,&GPIO_InitStruct);
}
//
// @简介: 右电机初始化
//
static void Motor_R_Init(void)
{//初始化PB5和PB7RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);GPIO_InitTypeDef GPIO_InitStruct = {0};GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;GPIO_Init(GPIOB,&GPIO_InitStruct);
}

这样,我们就对对应的引脚进行了初始化

接下来,我们需要对对应的PWM进行初始化,一个是PA8一个是PB6,我们应该设置为AF_PP模式,因为PA8和PB6均被定时器所占用,所以为复用模式,又因为要输出高低电平,所以为推挽模式。

//
// @简介: 左电机的初始化
//
static void Motor_L_Init(void)
{//初始化 PA9 PA10为Out_PPRCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);GPIO_InitTypeDef GPIO_InitStruct = {0};GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;GPIO_Init(GPIOA,&GPIO_InitStruct);//对PWM进行初始化,PA8 - AF_PPGPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;GPIO_Init(GPIOA,&GPIO_InitStruct);
}
//
// @简介: 右电机初始化
//
static void Motor_R_Init(void)
{//初始化PB5和PB7RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);GPIO_InitTypeDef GPIO_InitStruct = {0};GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;GPIO_Init(GPIOB,&GPIO_InitStruct);//对PWM进行初始化,PB6 - AF_PPGPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;GPIO_Init(GPIOB,&GPIO_InitStruct);}

初始化完成之后,接下来,让我们完成对应的PWM的操作

这个是定时器内部的结构框图。

我们先来完成时基单元的参数设置,我们想要实现的效果为1000级可调,我们输入的是72MHz,为了让周期尽可能的小,所以我们的PSC为0,为了实现1000级可调,PWM是由CCR寄存器实现的,它的范围是0-ARR寄存器,所以,ARR应该为999,重复计数器RCR为0就好了,接下来,我们在代码中实现这个功能:

所以,我们对定时器进行初始化

	//对定时器1进行初始化RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);//设置时基单元的参数TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct = {0};TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInitStruct.TIM_Period = 999;TIM_TimeBaseInitStruct.TIM_Prescaler = 0;TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;TIM_TimeBaseInit(TIM1,&TIM_TimeBaseInitStruct);
	//设置时基单元的参数TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct = {0};TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInitStruct.TIM_Period = 999;TIM_TimeBaseInitStruct.TIM_Prescaler = 0;TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStruct);	

初始化时钟之后,我们对PWM进行设置,PWM是在CCR中进行设置,通过输出比较产生PWM波

	//配置输出比较TIM_OCInitTypeDef TIM_OCInitStruct = {0};TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;TIM_OCInitStruct.TIM_OutputState = ENABLE;TIM_OCInitStruct.TIM_Pulse = 0;TIM_OC1Init(TIM1,&TIM_OCInitStruct);//配置MOE的开关TIM_CtrlPWMOutputs(TIM1,ENABLE);//闭合定时器的总开关TIM_Cmd(TIM1,ENABLE);
	//配置输出比较TIM_OCInitTypeDef TIM_OCInitStruct = {0};TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;TIM_OCInitStruct.TIM_OutputState = ENABLE;TIM_OCInitStruct.TIM_Pulse = 0;TIM_OC1Init(TIM4,&TIM_OCInitStruct);//配置MOE的开关TIM_CtrlPWMOutputs(TIM4,ENABLE);//闭合定时器的总开关TIM_Cmd(TIM4,ENABLE);

接下来,我们来设置左右电机的占空比

//
// @简介: 控制左电机的PWM
// @参数: duty 占空比的具体指,-100.0f - 100.0f
//
void App_Pwm_Set_L(float duty)
{float sign;//符号,正数 - +1,负数, - 1if(duty >= 0) sign = 1;else sign = -1;duty = fabs(duty);if(sign >= 0)//正传{GPIO_WriteBit(GPIOA,GPIO_Pin_9,Bit_SET);// AIN1 - 高GPIO_WriteBit(GPIOA,GPIO_Pin_10,Bit_RESET);//AIN2 - 低}else//反转{GPIO_WriteBit(GPIOA,GPIO_Pin_10,Bit_SET);// AIN1 - 高GPIO_WriteBit(GPIOA,GPIO_Pin_9,Bit_RESET);//AIN2 - 低	}uint16_t ccr = duty/100.0f*999;TIM_SetCompare1(TIM1,ccr);
}
//
// @简介: 控制右电机的PWM
// @参数: duty 占空比的具体指,-100.0f - 100.0f
//
void App_Pwm_Set_R(float duty)
{float sign;//符号,正数 - +1,负数, - 1if(duty >= 0) sign = 1;else sign = -1;duty = fabs(duty);if(sign >= 0)//正传{GPIO_WriteBit(GPIOB,GPIO_Pin_5,Bit_SET);// AIN1 - 高GPIO_WriteBit(GPIOB,GPIO_Pin_7,Bit_RESET);//AIN2 - 低}else//反转{GPIO_WriteBit(GPIOB,GPIO_Pin_7,Bit_SET);// AIN1 - 高GPIO_WriteBit(GPIOB,GPIO_Pin_5,Bit_RESET);//AIN2 - 低	}uint16_t ccr = duty/100.0f*999;TIM_SetCompare1(TIM4,ccr);
}

http://www.dtcms.com/a/364376.html

相关文章:

  • 基于单片机车内换气温度检测空气质量检测系统Proteus仿真(含全部资料)
  • 单片机点灯
  • Linux 网络编程中核心函数`recv`。
  • zynq 开发系列 新手入门:GPIO 连接 MIO 控制 LED 闪烁(SDK 端代码编写详解)
  • Spring Boot 实现数据库表变更监听的 Redis 消息队列方案
  • 单片机控制两只直流电机正反转C语言
  • 变频器实习DAY42 VF与IF电机启动方式
  • Excel 电影名匹配图片路径教程:自动查找并写入系统全路径
  • wpf 自定义控件,只能输入小数点,并且能控制小数点位数
  • 机器学习从入门到精通 - Python环境搭建与Jupyter魔法:机器学习起航必备
  • 如何在modelscope上上传自己的MCP服务
  • 【收藏】2025 前端开发者必备 SVG 资源大全
  • 【2025ICCV-持续学习方向】一种用于提示持续学习(Prompt-based Continual Learning, PCL)的新方法
  • 【CouponHub开发记录】SpringAop和分布式锁进行自定义注解实现防止重复提交
  • RAG|| LangChain || LlamaIndex || RAGflow
  • kafka概念之间关系梳理
  • mac idea 配置了Gitlab的远程地址,但是每次pull 或者push 都要输入密码,怎么办
  • 项目中常用的git命令
  • python基础案例-数据可视化
  • Streamlit 数据看板模板:非前端选手快速搭建 Python 数据可视化交互看板的实用工具
  • 【Linux】为什么死循环卡不死 Linux?3 个核心逻辑看懂进程优先级与 CPU 调度密码
  • Langchain4j 整合MongoDB 实现会话持久化存储详解
  • 电表连网不用跑现场!耐达讯自动化RS485转Profinet网关 远程配置+技术支持,真能做到!
  • 单元测试数据库回滚问题
  • 如何在FastAPI中巧妙隔离依赖项,让单元测试不再头疼?
  • 10 分钟掌握 Selenium 8 大元素定位法:从踩坑到精通
  • Python分布式任务队列:万级节点集群的弹性调度实践
  • 深入剖析Spring Boot中Spring MVC的请求处理流程
  • 电脑接入企业中的网线,为啥网卡上面显示AD域名
  • 智能电视小米电视浏览器兼容性踩坑电视黑屏或者电视白屏,Vue项目从Axios到Fetch的避坑指南