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

相关文章

Golang操作DuckDB实战案例分享

《Golang操作DuckDB实战案例分享》DuckDB是一个嵌入式SQL数据库引擎,它与众所周知的SQLite非常相似,但它是为olap风格的工作负载设计的,DuckDB支持各种数据类型和SQL特性... 目录DuckDB的主要优点环境准备初始化表和数据查询单行或多行错误处理和事务完整代码最后总结Duck

C++中实现调试日志输出

《C++中实现调试日志输出》在C++编程中,调试日志对于定位问题和优化代码至关重要,本文将介绍几种常用的调试日志输出方法,并教你如何在日志中添加时间戳,希望对大家有所帮助... 目录1. 使用 #ifdef _DEBUG 宏2. 加入时间戳:精确到毫秒3.Windows 和 MFC 中的调试日志方法MFC

将Python应用部署到生产环境的小技巧分享

《将Python应用部署到生产环境的小技巧分享》文章主要讲述了在将Python应用程序部署到生产环境之前,需要进行的准备工作和最佳实践,包括心态调整、代码审查、测试覆盖率提升、配置文件优化、日志记录完... 目录部署前夜:从开发到生产的心理准备与检查清单环境搭建:打造稳固的应用运行平台自动化流水线:让部署像

Python使用Colorama库美化终端输出的操作示例

《Python使用Colorama库美化终端输出的操作示例》在开发命令行工具或调试程序时,我们可能会希望通过颜色来区分重要信息,比如警告、错误、提示等,而Colorama是一个简单易用的Python库... 目录python Colorama 库详解:终端输出美化的神器1. Colorama 是什么?2.

MySQL不使用子查询的原因及优化案例

《MySQL不使用子查询的原因及优化案例》对于mysql,不推荐使用子查询,效率太差,执行子查询时,MYSQL需要创建临时表,查询完毕后再删除这些临时表,所以,子查询的速度会受到一定的影响,本文给大家... 目录不推荐使用子查询和JOIN的原因解决方案优化案例案例1:查询所有有库存的商品信息案例2:使用EX

Linux中Curl参数详解实践应用

《Linux中Curl参数详解实践应用》在现代网络开发和运维工作中,curl命令是一个不可或缺的工具,它是一个利用URL语法在命令行下工作的文件传输工具,支持多种协议,如HTTP、HTTPS、FTP等... 目录引言一、基础请求参数1. -X 或 --request2. -d 或 --data3. -H 或

在Ubuntu上部署SpringBoot应用的操作步骤

《在Ubuntu上部署SpringBoot应用的操作步骤》随着云计算和容器化技术的普及,Linux服务器已成为部署Web应用程序的主流平台之一,Java作为一种跨平台的编程语言,具有广泛的应用场景,本... 目录一、部署准备二、安装 Java 环境1. 安装 JDK2. 验证 Java 安装三、安装 mys

Python中构建终端应用界面利器Blessed模块的使用

《Python中构建终端应用界面利器Blessed模块的使用》Blessed库作为一个轻量级且功能强大的解决方案,开始在开发者中赢得口碑,今天,我们就一起来探索一下它是如何让终端UI开发变得轻松而高... 目录一、安装与配置:简单、快速、无障碍二、基本功能:从彩色文本到动态交互1. 显示基本内容2. 创建链

Node.js 中 http 模块的深度剖析与实战应用小结

《Node.js中http模块的深度剖析与实战应用小结》本文详细介绍了Node.js中的http模块,从创建HTTP服务器、处理请求与响应,到获取请求参数,每个环节都通过代码示例进行解析,旨在帮... 目录Node.js 中 http 模块的深度剖析与实战应用一、引言二、创建 HTTP 服务器:基石搭建(一

java中VO PO DTO POJO BO DO对象的应用场景及使用方式

《java中VOPODTOPOJOBODO对象的应用场景及使用方式》文章介绍了Java开发中常用的几种对象类型及其应用场景,包括VO、PO、DTO、POJO、BO和DO等,并通过示例说明了它... 目录Java中VO PO DTO POJO BO DO对象的应用VO (View Object) - 视图对象