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


前言

提示:本文主要用作在学习江科大自化协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_H

void PWM_Init(void);
void PWM_SetCompare3(uint16_t Compare);

#endif

PWM.c实现文件:

#include "stm32f10x.h"                  // Device header

void 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;		//ARR
	TIM_TimeBaseInitStructure.TIM_Prescaler = 36 - 1;		//PSC
	TIM_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;		//CCR
	TIM_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_H

void 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);
	}
}
  • 15
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于使用STM32F103C8T6来驱动直流电机,您可以使用PWM信号来控制电机的速度。下面是一个简单的步骤来实现这个功能: 1. 初始化TIM定时器:选择一个合适的定时器(如TIM2),配置定时器的时钟和计数周期,并启动定时器。 2. 配置GPIO引脚:选择一个合适的GPIO引脚作为PWM输出引脚,并将其设置为复用功能模式。 3. 配置PWM模式:选择PWM模式,设置PWM的频率和占空比。 4. 启动PWM输出:启动PWM输出,使定时器开始生成PWM信号。 下面是一个示例代码,用于配置TIM2为PWM输出引脚PA0控制直流电机的速度: ```c #include "stm32f10x.h" void PWM_init(void) { // 使能TIM2时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 初始化TIM2定时器 TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct; TIM_TimeBaseInitStruct.TIM_Period = 1000; // 设置计数周期为1000 TIM_TimeBaseInitStruct.TIM_Prescaler = 720; // 设置时钟预分频为720(72MHz / 720 = 100kHz) TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct); // 配置TIM2通道1为PWM模式 TIM_OCInitTypeDef TIM_OCInitStruct; TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStruct.TIM_Pulse = 500; // 设置占空比为50%(1000 * 50% = 500) TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OC1Init(TIM2, &TIM_OCInitStruct); // 配置GPIO引脚为复用功能模式 GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStruct); // 启动TIM2定时器 TIM_Cmd(TIM2, ENABLE); } int main(void) { // 初始化PWM PWM_init(); while (1) { // 可以在这里通过改变占空比来改变电机的速度 TIM2->CCR1 = 250; // 设置占空比为25%(1000 * 25% = 250) } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值