TIM输出比较之PWM驱动直流电机应用案例

2024-08-24 05:20

本文主要是介绍TIM输出比较之PWM驱动直流电机应用案例,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 前言
  • 一、应用案例演示
  • 二、电路接线图
  • 三、应用案例代码
  • 四、应用案例分析
    • 4.1 初始化PWM模块
      • 4.1.1 RCC开启时钟
      • 4.1.2 配置时基单元
      • 4.1.3 配置输出比较单元
      • 4.1.4 配置GPIO
      • 4.1.5 运行控制
    • 4.2 PWM输出模块
    • 4.3 电机模块
      • 4.3.1 Motor初始化模块
      • 4.3.2 电机调速模块
    • 4.4 主程序


前言

提示:本文主要用作在学习江科大自化协STM32入门教程后做的归纳总结笔记,旨在学习记录,如有侵权请联系作者
本案例实现了一个利用输出占空比可调的PWM信号来驱动直流电机的功能。每按一次按键电机按照增量或减量的速度正反转动,比如按一下,OLED上显示当前的速度值为+20,再按一下,+40,以此类推。其中正转显示为+,反转显示为-。


一、应用案例演示

TIM输出比较之PWM驱动直流电机

二、电路接线图

这里红色的模块是TB6612电机驱动模块,它的第一个引脚VM为电机电源,同样的我们可以把它接到STLINK的5V引脚上。第二个VCC逻辑电源,接面包板3.3V正极。第三个GND电源负极,接面包板的负极。之后AO1、AO2电机输出端,接电机的两根线。STBY待机控制脚,不需要待机,直接接逻辑电源3.3V正极。剩下的三个是控制引脚,AIN1和AIN2是方向控制,任意接两个GPIO就行了,这里我接的是PA4和PA5两个引脚。PWMA是速度控制,需要接PWM的输出脚,这里我接的是PA2这个引脚。最后在PB1接了一个按键用于控制电机。
在这里插入图片描述
在这里插入图片描述

三、应用案例代码

PWM.h文件:

#ifndef __PWM_H
#define __PWM_Hvoid PWM_Init(void);
void PWM_SetCompare3(uint16_t Compare);#endif

PWM.c实现文件:

#include "stm32f10x.h"                  // Device headervoid PWM_Init(void)
{RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);TIM_InternalClockConfig(TIM2);TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;		//ARRTIM_TimeBaseInitStructure.TIM_Prescaler = 36 - 1;		//PSCTIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);TIM_OCInitTypeDef TIM_OCInitStructure;TIM_OCStructInit(&TIM_OCInitStructure);TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;TIM_OCInitStructure.TIM_Pulse = 0;		//CCRTIM_OC3Init(TIM2, &TIM_OCInitStructure);TIM_Cmd(TIM2, ENABLE);
}void PWM_SetCompare3(uint16_t Compare)
{TIM_SetCompare3(TIM2, Compare);
}

电机头文件Motor.h:

#ifndef __MOTOR_H
#define __MOTOR_Hvoid Motor_Init(void);
void Motor_SetSpeed(int8_t Speed);#endif

电机实现文件Motor.c:

#include "stm32f10x.h"                  // Device header
#include "PWM.h"void Motor_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);PWM_Init();
}void Motor_SetSpeed(int8_t Speed)
{if (Speed >= 0){GPIO_SetBits(GPIOA, GPIO_Pin_4);GPIO_ResetBits(GPIOA, GPIO_Pin_5);PWM_SetCompare3(Speed);}else{GPIO_ResetBits(GPIOA, GPIO_Pin_4);GPIO_SetBits(GPIOA, GPIO_Pin_5);PWM_SetCompare3(-Speed);}
}

主程序main.c:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Motor.h"
#include "Key.h"uint8_t KeyNum;
int8_t Speed;int main(void)
{OLED_Init();Motor_Init();Key_Init();OLED_ShowString(1, 1, "Speed:");while (1){KeyNum = Key_GetNum();if (KeyNum == 1){Speed += 20;if (Speed > 100){Speed = -100;}}Motor_SetSpeed(Speed);OLED_ShowSignedNum(1, 7, Speed, 3);}
}

完整工程:TIM输出比较之PWM驱动直流电机应用案例

四、应用案例分析

整体思路与LED呼吸灯那一章节基本是一致的,在那一章里已经讲得非常详细了,这里就不再累述了,不懂的可以回过头去看一看。

文章传送门在此:TIM输出比较之PWM驱动LED呼吸灯应用案例

在这里插入图片描述

这里需要注意的是,本案例换了一个GPIO口,所以对应的定时器的通道也要更换。如下表所示,可以看到PA2对应的是TIM2的CH3通道。

在这里插入图片描述

4.1 初始化PWM模块

4.1.1 RCC开启时钟

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

4.1.2 配置时基单元

TIM_InternalClockConfig(TIM2);TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;		//ARR
TIM_TimeBaseInitStructure.TIM_Prescaler = 36 - 1;		//PSC
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);

TIM_InternalClockConfig(TIM2);//选择时基单元的时钟源,选择内部时钟。若不调用这个函数,系统上电后默认也是内部时钟。

计算公式如下:
PWM频率:Freq = CK_PSC / (PSC + 1) / (ARR + 1)
PWM占空比:Duty = CCR / (ARR + 1)
PWM分辨率:Reso = 1 / (ARR + 1)
换算公式:1 MHz = 1,000 KHz = 1,000,000 Hz

假设我要输出一个频率为1KHz,占空比为50%,分辨率为1%的PWM波形,时钟源选择内部时钟,也就是说CK_PSC=72MHz。

代入公式计算可得:
Freq =1000 = 72000000 / 720 / 100
那么可以推算出PSC为719,ARR为99
同样的道理,Duty = 50% = CCR / 100,推算出CCR为50。
同样也可以推算出周期 T = 1 / 1000 = 0.001秒,也就是1毫秒。(频率是周期的倒数 f = 1 / T)

输出频率为1KHz,占空比为50%(CCR设置为50),分辨率为1%(受占空比变化影响)的PWM波形代码如下:

TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;		//ARR
TIM_TimeBaseInitStructure.TIM_Prescaler = 720- 1;		//PSC
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);

那如果我们设置频率为1KHz的话就会出现一个问题,就是这个电机会发出蜂鸣器的响声,在堵转的时候很明显。因为电机里面也是线圈和磁铁,所以在PWM的驱动下会发出蜂鸣器的声音,这是正常现象。那有什么办法可以避免呢?研究表明,人耳能听到的范围是20Hz到20KHz,那这样的话我们可以把频率调到人耳能接受的范围就可以了。

加大频率我们可以通过减小预分频器来完成,这样不会影响占空比。所以我们给这个预分频器去掉一个0,那就是10KHz了。再减半为36,那就是20KHz了。

那么输出频率为20KHz的PWM波形代码如下:

TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;		//ARR
TIM_TimeBaseInitStructure.TIM_Prescaler = 36 - 1;		//PSC
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);

4.1.3 配置输出比较单元

TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0;		//CCR
TIM_OC3Init(TIM2, &TIM_OCInitStructure);

4.1.4 配置GPIO

GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

4.1.5 运行控制

TIM_Cmd(TIM2, ENABLE);

4.2 PWM输出模块

void PWM_SetCompare3(uint16_t Compare)
{TIM_SetCompare3(TIM2, Compare);
}

4.3 电机模块

4.3.1 Motor初始化模块

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

电机模块的初始化包括两个部分,分别是控制电机正反转的两个GPIO(PA4和PA5)以及PWM模块。

4.3.2 电机调速模块

void Motor_SetSpeed(int8_t Speed)
{if (Speed >= 0){GPIO_SetBits(GPIOA, GPIO_Pin_4);GPIO_ResetBits(GPIOA, GPIO_Pin_5);PWM_SetCompare3(Speed);}else{GPIO_ResetBits(GPIOA, GPIO_Pin_4);GPIO_SetBits(GPIOA, GPIO_Pin_5);PWM_SetCompare3(-Speed);}
}

GPIO_SetBits()和GPIO_ResetBits()用于设置电机正反转,PWM_SetCompare3函数用于设置电机速度。

在这里插入图片描述

4.4 主程序

主程序在while(1)主循环里通过获取按键按下的状态对电机进行调速,当速度超过100的时候反方向运行。

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Motor.h"
#include "Key.h"uint8_t KeyNum;
int8_t Speed;int main(void)
{OLED_Init();Motor_Init();Key_Init();OLED_ShowString(1, 1, "Speed:");while (1){KeyNum = Key_GetNum();if (KeyNum == 1){Speed += 20;if (Speed > 100){Speed = -100;}}Motor_SetSpeed(Speed);OLED_ShowSignedNum(1, 7, Speed, 3);}
}

这篇关于TIM输出比较之PWM驱动直流电机应用案例的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


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

相关文章

Python多线程应用中的卡死问题优化方案指南

《Python多线程应用中的卡死问题优化方案指南》在利用Python语言开发某查询软件时,遇到了点击搜索按钮后软件卡死的问题,本文将简单分析一下出现的原因以及对应的优化方案,希望对大家有所帮助... 目录问题描述优化方案1. 网络请求优化2. 多线程架构优化3. 全局异常处理4. 配置管理优化优化效果1.

MySQL 临时表与复制表操作全流程案例

《MySQL临时表与复制表操作全流程案例》本文介绍MySQL临时表与复制表的区别与使用,涵盖生命周期、存储机制、操作限制、创建方法及常见问题,本文结合实例代码给大家介绍的非常详细,感兴趣的朋友跟随小... 目录一、mysql 临时表(一)核心特性拓展(二)操作全流程案例1. 复杂查询中的临时表应用2. 临时

MySQL 数据库表与查询操作实战案例

《MySQL数据库表与查询操作实战案例》本文将通过实际案例,详细介绍MySQL中数据库表的设计、数据插入以及常用的查询操作,帮助初学者快速上手,感兴趣的朋友跟随小编一起看看吧... 目录mysql 数据库表操作与查询实战案例项目一:产品相关数据库设计与创建一、数据库及表结构设计二、数据库与表的创建项目二:员

java -jar example.jar 产生的日志输出到指定文件的方法

《java-jarexample.jar产生的日志输出到指定文件的方法》这篇文章给大家介绍java-jarexample.jar产生的日志输出到指定文件的方法,本文给大家介绍的非常详细,对大家的... 目录怎么让 Java -jar example.jar 产生的日志输出到指定文件一、方法1:使用重定向1、

从基础到高阶详解Python多态实战应用指南

《从基础到高阶详解Python多态实战应用指南》这篇文章主要从基础到高阶为大家详细介绍Python中多态的相关应用与技巧,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、多态的本质:python的“鸭子类型”哲学二、多态的三大实战场景场景1:数据处理管道——统一处理不同数据格式

C#中的Drawing 类案例详解

《C#中的Drawing类案例详解》文章解析WPF与WinForms的Drawing类差异,涵盖命名空间、继承链、常用类及应用场景,通过案例展示如何创建带阴影圆角矩形按钮,强调WPF的轻量、可动画特... 目录一、Drawing 是什么?二、典型用法三、案例:画一个“带阴影的圆角矩形按钮”四、WinForm

Java Stream 的 Collectors.toMap高级应用与最佳实践

《JavaStream的Collectors.toMap高级应用与最佳实践》文章讲解JavaStreamAPI中Collectors.toMap的使用,涵盖基础语法、键冲突处理、自定义Map... 目录一、基础用法回顾二、处理键冲突三、自定义 Map 实现类型四、处理 null 值五、复杂值类型转换六、处理

setsid 命令工作原理和使用案例介绍

《setsid命令工作原理和使用案例介绍》setsid命令在Linux中创建独立会话,使进程脱离终端运行,适用于守护进程和后台任务,通过重定向输出和确保权限,可有效管理长时间运行的进程,本文给大家介... 目录setsid 命令介绍和使用案例基本介绍基本语法主要特点命令参数使用案例1. 在后台运行命令2.

分布式锁在Spring Boot应用中的实现过程

《分布式锁在SpringBoot应用中的实现过程》文章介绍在SpringBoot中通过自定义Lock注解、LockAspect切面和RedisLockUtils工具类实现分布式锁,确保多实例并发操作... 目录Lock注解LockASPect切面RedisLockUtils工具类总结在现代微服务架构中,分布

Spring Boot集成/输出/日志级别控制/持久化开发实践

《SpringBoot集成/输出/日志级别控制/持久化开发实践》SpringBoot默认集成Logback,支持灵活日志级别配置(INFO/DEBUG等),输出包含时间戳、级别、类名等信息,并可通过... 目录一、日志概述1.1、Spring Boot日志简介1.2、日志框架与默认配置1.3、日志的核心作用