stm32驱动直流电机实现启动/加速/减速/倒车/停车等功能

不积跬步,无以至千里;不积小流,无以成江海。
大家好,我是闲鹤,公众号 xxh_zone,十多年开发、架构经验,先后在华为、迅雷服役过,也在高校从事教学3年;目前已创业了7年多,主要从事物联网/车联网相关领域和业务。
喜欢交友、骑行、写毛笔字、弹吉他、折腾硬件和写代码。

ps:
如对以下内容有疑问或者咨询,可进行留言 或 添加我的微信公众号留言

原理

  1. 使用 PWM 输出脉冲信号
  2. 直流电机驱动TB6612
    通过驱动模块,实现直流电机的启动、停止、加速、减速和倒车

演示视频
https://www.bilibili.com/video/BV1Ai421e7sv/?vd_source=f1d43113e0df5436df9402483a18e193

电路图:
file

主要实现的功能:

  1. 启动 MC_Start
  2. 停止 MC_Stop
  3. 加速 MC_ACC
  4. 减速 MC_DEC
  5. 倒车 MC_Back

模块封装

代码层封装成了4部分:

  1. PWM 驱动
    这里主要封装了 OC 相关的配置,比如:ARR、PSC等,同时向外提供了修改 CCR 值的函数,以实现 PWM 脉宽的改变(占空比),从而实现电机的旋转速度
  2. 直流电机驱动
    1. 对 PWM 的封装
    2. 驱动 TB6612 模块
    3. 提供了 Motor_InitMotor_StartMotor_StopMotor_ForwardMotor_ReverseMotor_Accelerate 函数
  3. 直流电机控制
    1. 对 直流电机驱动 封装
    2. 提供5个按钮功能,分别实现:启动(MC_Start)、停止(MC_Stop)、加速(MC_ACC)、加速(MC_DEC)、倒车(MC_Back) 和 就绪(MC_Ready)
      就绪(MC_Ready)又是对前面5个接口的封装,在主函数内部只需要调用这个一个函数即可
  4. 运行主入口
    程序的入口,初始化了直流电机控制,调用了 MC_Ready 函数,并且把相关信息输出到 OLED 屏

整体代码结构图:
file

源码

PWM.c

#include "stm32f10x.h"                  // Device header

void PWM_Init(void)
{
	// 时基
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct;
	TIM_TimeBaseStruct.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseStruct.TIM_Period = 100 - 1;
	TIM_TimeBaseStruct.TIM_Prescaler = 36 - 1;
	TIM_TimeBaseStruct.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStruct);
	
	// OC
	TIM_OCInitTypeDef TIM_OCInitStruct;
	TIM_OCStructInit(&TIM_OCInitStruct);
	TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;
	TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
	TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
	TIM_OCInitStruct.TIM_Pulse = 0;
	TIM_OC1Init(TIM2, &TIM_OCInitStruct);
	
	// 输出
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStruct);
	
	TIM_Cmd(TIM2, ENABLE);
}

void PWM_SetCompare(uint8_t compare)
{
	TIM_SetCompare1(TIM2, compare);
}

Motor.c


#include "Motor.h"
#include "PWM.h"

#define IN1 GPIO_Pin_10
#define IN2 GPIO_Pin_11

void Motor_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Pin = IN1 | IN2;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStruct);
	
	PWM_Init();
}

/**
 启动
**/
void Motor_Start(void)
{
	Motor_Forward();
	Motor_Accelerate(30);
}

/*
	停止
*/
void Motor_Stop(void)
{
	GPIO_ResetBits(GPIOA, IN1);
	GPIO_ResetBits(GPIOA, IN2);
}


/**
 正转 前进
**/
void Motor_Forward(void)
{
	GPIO_SetBits(GPIOA, IN1);
	GPIO_ResetBits(GPIOA, IN2);
}

/**
 反转 后退
**/
void Motor_Reverse(void)
{
	GPIO_SetBits(GPIOA, IN2);
	GPIO_ResetBits(GPIOA, IN1);
}

/**
 加速
**/
void Motor_Accelerate(uint8_t speed)
{
	PWM_SetCompare(speed);
}

MotorControl.c

#include "stm32f10x.h"                  // Device header
#include "MotorControl.h"
#include "Delay.h"
#include "Motor.h"

/*
	提供
	1. 启动按键 B12
	2. 停止按键 B13
	3. 加速按键 B14
	4. 减速按键 B15
	5. 倒车按键 B11
*/

#define MC_KEY_GPIO			GPIOB

#define MC_START_KEY_PIN	GPIO_Pin_11
#define MC_START_KEY_GPIO	GPIOB

#define MC_STOP_KEY_PIN		GPIO_Pin_10
#define MC_STOP_KEY_GPIO	GPIOB

#define MC_ACC_KEY_PIN		GPIO_Pin_0
#define MC_ACC_KEY_GPIO		GPIOB

#define MC_DEC_KEY_PIN		GPIO_Pin_6
#define MC_DEC_KEY_GPIO		GPIOA

#define MC_BACK_KEY_PIN		GPIO_Pin_3
#define MC_BACK_KEY_GPIO	GPIOA

int8_t speed = 0;
uint8_t is_running = 0;

void MC_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOA, ENABLE);
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU; // 低电平有效
	GPIO_InitStruct.GPIO_Pin = MC_START_KEY_PIN | MC_STOP_KEY_PIN | MC_ACC_KEY_PIN | MC_DEC_KEY_PIN | MC_BACK_KEY_PIN;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStruct);
	GPIO_Init(GPIOA, &GPIO_InitStruct);
	
	Motor_Init();
}

/**
	启动
**/
void MC_Start(void)
{
	if(GPIO_ReadInputDataBit(MC_START_KEY_GPIO, MC_START_KEY_PIN) == 0) {
		Delay_ms(20);
		while(GPIO_ReadInputDataBit(MC_START_KEY_GPIO, MC_START_KEY_PIN) == 0);
		Delay_ms(20);
		speed = 30;
		is_running = 1;
		Motor_Forward();
		Motor_Accelerate(speed);
	}
}

/**
	停止
**/
void MC_Stop(void)
{
	if(GPIO_ReadInputDataBit(MC_STOP_KEY_GPIO, MC_STOP_KEY_PIN) == 0) {
		Delay_ms(20);
		while(GPIO_ReadInputDataBit(MC_STOP_KEY_GPIO, MC_STOP_KEY_PIN) == 0);
		Delay_ms(20);
		speed = 0;
		is_running = 0;
		Motor_Stop();
	}
}

/**
	加速
**/
void MC_ACC(void)
{
	if(is_running == 0) return;
	if(GPIO_ReadInputDataBit(MC_ACC_KEY_GPIO, MC_ACC_KEY_PIN) == 0) {
		Delay_ms(20);
		while(GPIO_ReadInputDataBit(MC_ACC_KEY_GPIO, MC_ACC_KEY_PIN) == 0);
		Delay_ms(20);
		speed += 20;
		if(speed >= 100) speed = 100;
		Motor_Accelerate(speed);
	}
}

/**
	减速
**/
void MC_DEC(void)
{
	if(is_running == 0) return;
	if(GPIO_ReadInputDataBit(MC_DEC_KEY_GPIO, MC_DEC_KEY_PIN) == 0) {
		Delay_ms(20);
		while(GPIO_ReadInputDataBit(MC_DEC_KEY_GPIO, MC_DEC_KEY_PIN) == 0);
		Delay_ms(20);
		speed -= 20;
		if(speed <= 0) speed = 0;
		Motor_Accelerate(speed);
	}
}

/**
	倒车
**/
void MC_Back(void)
{
	if(GPIO_ReadInputDataBit(MC_BACK_KEY_GPIO, MC_BACK_KEY_PIN) == 0) {
		Delay_ms(20);
		while(GPIO_ReadInputDataBit(MC_BACK_KEY_GPIO, MC_BACK_KEY_PIN) == 0);
		Delay_ms(20);
		is_running = 1;
		Motor_Reverse();
	}
}

/**
	准备就绪
**/
void MC_Ready(void) 
{
	MC_Start();
	MC_Stop();
	MC_ACC();
	MC_DEC();
	MC_Back();
}

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include <stdio.h>
#include "MotorControl.h"

char loginfo[100] = {0};

extern uint8_t speed;
extern uint8_t is_running;

void Log(uint8_t Line, uint8_t Column)
{
	OLED_ShowString(Line, Column, loginfo);
}

int main() 
{
	OLED_Init();
	MC_Init();
	
	while(1) {
		
		MC_Ready();
		
		sprintf(loginfo, "Run: %s  ", (is_running == 1 ? "Y" : "N"));
		Log(1, 1);
		sprintf(loginfo, "Speed: %d  ", speed);
		Log(2, 1);

	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/687980.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

UML交互图-协作图

概述 协作图和序列图都表示出了对象间的交互作用&#xff0c;但是它们侧重点不同。序列图清楚地表示了交互作用中的时间顺序&#xff0c;但没有明确表示对象间的关系。协作图则清楚地表示了对象间的关系&#xff0c;但时间顺序必须从顺序号获得。序列图常常用于表示方案&#…

使用紫铜管制作半波天线的折合振子

一、概述 半波天线是一种简单而有效的天线类型&#xff0c;其长度约为工作波长的一半。它具有较好的辐射特性和较高的增益&#xff0c;广泛应用于业余无线电、电视接收等领域。使用紫铜管制作折合振子&#xff0c;不仅可以提高天线的机械强度&#xff0c;还能增强其导电性能。 …

k8s——Pod容器中的存储方式及PV、PVC

一、Pod容器中的存储方式 需要存储方式前提&#xff1a;容器磁盘上的文件的生命周期是短暂的&#xff0c;这就使得在容器中运行重要应用时会出现一些问题。 首先&#xff0c;当容器崩溃时&#xff0c;kubelet 会重启它&#xff0c;但是容器中的文件将丢失——容器以干净的状态&…

如何让tracert命令的显示信息显示*星号

tracert命令如果在中间某一个节点超时&#xff0c;只会在显示信息中标识此节点信息超时“ * * * ”&#xff0c;不影响整个tracert命令操作。 如上图所示&#xff0c;在DeviceA上执行tracert 10.1.2.2命令&#xff0c;缺省情况下&#xff0c;DeviceA上的显示信息为&#xff1a;…

Xamarin.Android实现通知推送功能(1)

目录 1、背景说明1.1 开发环境1.2 实现效果1.2.1 推送的界面1.2.2 推送的设置1.2.3 推送的功能实现1.2.3.1、Activity的设置【重要】1.2.3.2、代码的实现 2、源码下载3、总结4、参考资料 1、背景说明 在App开发中&#xff0c;通知&#xff08;或消息&#xff09;的推送&#x…

k8s小型实验模拟

&#xff08;1&#xff09;Kubernetes 区域可采用 Kubeadm 方式进行安装。&#xff08;5分&#xff09; &#xff08;2&#xff09;要求在 Kubernetes 环境中&#xff0c;通过yaml文件的方式&#xff0c;创建2个Nginx Pod分别放置在两个不同的节点上&#xff0c;Pod使用hostPat…

Gradio 案例——将文本文件转为词云图

文章目录 Gradio 案例——将文本文件转为词云图界面截图依赖安装项目目录结构代码 Gradio 案例——将文本文件转为词云图 利用 word_cloud 库&#xff0c;将文本文件转为词云图更完整、丰富的示例项目见 GitHub - AlionSSS/wordcloud-webui: The web UI for word_cloud(text t…

AI产品经理岗位需求量大吗?好找工作吗?

前言 在当今这个科技日新月异的时代&#xff0c;人工智能&#xff08;AI&#xff09;已不再仅仅是一个遥远的概念&#xff0c;而是深深嵌入到我们生活的方方面面&#xff0c;从日常的语音助手到复杂的自动驾驶系统&#xff0c;AI的触角无处不在。随着AI技术的广泛应用和持续进…

kettle列转行(行扁平化)的使用

kettle行扁平化节点是将多行数据合并为一行数据如&#xff0c;其行为类似于css中的float排列 将上表格数据转换为下表格数据 namecategorynumjack语文10jack数学20jack英语40rose英语50 name语文数学英语jack102040rose50 使用行扁平化节点配置需要扁平化的字段&#xff0c…

泛微开发修炼之旅--09Ecology作为所有异构系统的待办中心,实现与kafka对接源码及示例

文章链接&#xff1a;泛微开发修炼之旅--09Ecology作为所有异构系统的待办中心&#xff0c;实现与kafka对接源码及示例

龙迅LT6911GX HDMI 2.1桥接到四 PORT MIPI或者LVDS,支持图像处理以及旋转,内置MCU以及LPDDR4

龙迅LT6911GX描述&#xff1a; LT6911GX是一款高性能的HDMI2.1到MIPI或LVDS芯片&#xff0c;用于VR/显示器应用。HDCP RX作为HDCP中继器的上游端&#xff0c;可以与其他芯片的HDCP TX协同工作&#xff0c;实现中继器的功能。对于HDMI2.1输入&#xff0c;LT6911GX可配置为3/4车…

JavaScript的核心语法

JavaScript JavaScript&#xff1a;JavaScript的组成&#xff1a;核心语法&#xff1a;Hello&#xff1a;变量&#xff1a;JS的基本数据类型&#xff1a;特殊点&#xff1a; 数组&#xff1a;流程控制语句&#xff1a;函数&#xff1a;函数的重载&#xff1a;函数的递归:预定义…

python-windows10普通笔记本跑bert mrpc数据样例0.1.001

python-windows10普通笔记本跑bert mrpc数据样例0.1.000 背景参考章节获取数据下载bert模型下载bert代码windows10的cpu执行结果注意事项TODOLIST背景 看了介绍说可以在gpu或者tpu上去微调,当前没环境,所以先在windows10上跑一跑,看是否能顺利进行,目标就是训练的过程中没…

WWDC24即将到来,ios18放大招

苹果公司即将在下周开全球开发者大会(WWDC)&#xff0c;大会上将展示其人工智能技术整合到设备和软件中的重大进展,包括与OpenAI的历史性合作。随着大会的临近,有关iOS 18及其据称采用AI技术支持的应用程序和功能的各种泄露信息已经浮出水面。 据报道,苹果将利用其自主研发的大…

WPF国际化的最佳实践

WPF国际化的最佳实践 1.创建项目资源文件 如果你的项目没有Properties文件夹和Resources.resx文件&#xff0c;可以通过右键项目-资源-常规-添加创建或打开程序集资源 2.添加国际化字符串 打开Resources.resx文件&#xff0c;添加需要翻译的文本字符&#xff0c;并将访问修…

深入了解 Postman 中的变量

在我们进行 API 开发和测试时&#xff0c;使用诸如 Postman 之类的工具可以极大地简化工作流程&#xff0c;提高效率。Postman 的一个强大功能就是变量&#xff08;Variables&#xff09;。利用变量&#xff0c;我们可以使我们的请求变得更加动态和灵活&#xff0c;避免重复输入…

计算机组成实验---Cache的实现

直接映射 先看懂cache的映射原理&#xff0c;根据cache大小与主存大小来计算各个信号线的位数 各个信号线位数 主存地址在逻辑上分为区号、块号、块内地址 Cache结构 Cache访问原理 基本过程 状态机&#xff1a;“三段式”实现 6.3 Verilog 状态机 | 菜鸟教程 (runoob.com) …

问题:当频点数大于载波数时,() #学习方法#知识分享

问题&#xff1a;当频点数大于载波数时&#xff0c;&#xff08;&#xff09; A.基带跳频可以执行&#xff0c;混合跳频可以执行 B.基带跳频不可以执行&#xff0c;混合跳频可以执行 C.基带跳频可以执行&#xff0c;混合跳频不可以执行 D.基带跳频不可以执行&#xff0c;混…

2024-06-07 Unity 编辑器开发之编辑器拓展8 —— Scene 窗口拓展

文章目录 1 Handles 类1.1 Scene 响应函数1.2 自定义窗口中监听 Scene1.3 Handles 常用 API2.2.1 颜色控制2.2.2 文本2.2.3 线段2.2.4 虚线2.2.5 圆弧2.2.6 圆2.2.7 立方体2.2.8 几何体2.2.9 移动、旋转、缩放2.2.10 自由移动 / 旋转 2 Scene 窗口中显示 GUI3 HandleUtility4 G…

React 18

创建 React 18 脚手架项目 全局安装 create-react-app npm install -g create-react-app yarn global add create-react-app . 确认是否已安装 create-react-app npm list -g create-react-app yarn global list | grep create-react-app . 如果安装失败 有时&#xff0…