TIM输出比较之PWM驱动舵机应用案例


前言

提示:本文主要用作在学习江科大自化协STM32入门教程后做的归纳总结笔记,旨在学习记录,如有侵权请联系作者

本案例利用输出占空比可调的PWM波形来驱动舵机,实现了一个通过按键控制舵机按照一定的角度与方向进行转动的功能。另外OLED上也显示了当前的角度,每按一下,角度变量就变一次,舵机也会随即跟随变化到的角度固定下来。


一、应用案例演示

TIM输出比较之PWM驱动舵机

二、电路接线图

舵机型号为SG90,它一共有三根线,其中棕色线接GND,红色线为电源线接5V正极,可以接在STLINK的5V输出引脚,黄色线为PWM信号线,接在PA1引脚,最后再在PB1接一个按键用来控制舵机。

在这里插入图片描述

三、应用案例代码

PWM.h文件:

#ifndef __PWM_H
#define __PWM_H

void PWM_Init(void);
void PWM_SetCompare2(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_1;
	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 = 20000 - 1;		//ARR
	TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 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_OC2Init(TIM2, &TIM_OCInitStructure);
	
	TIM_Cmd(TIM2, ENABLE);
}

void PWM_SetCompare2(uint16_t Compare)
{
	TIM_SetCompare2(TIM2, Compare);
}

舵机头文件Servo.h:

#ifndef __SERVO_H
#define __SERVO_H

void Servo_Init(void);
void Servo_SetAngle(float Angle);

#endif

舵机实现文件Servo.c:

#include "stm32f10x.h"                  // Device header
#include "PWM.h"

void Servo_Init(void)
{
	PWM_Init();
}

void Servo_SetAngle(float Angle)
{
	PWM_SetCompare2(Angle / 180 * 2000 + 500);
}

主程序main.c:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Servo.h"
#include "Key.h"

uint8_t KeyNum;
float Angle;

int main(void)
{
	OLED_Init();
	Servo_Init();
	Key_Init();
	
	OLED_ShowString(1, 1, "Angle:");
	
	while (1)
	{
		KeyNum = Key_GetNum();
		if (KeyNum == 1)
		{
			Angle += 30;
			if (Angle > 180)
			{
				Angle = 0;
			}
		}
		Servo_SetAngle(Angle);
		OLED_ShowNum(1, 7, Angle, 3);
	}
}

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

四、应用案例分析

通过下图可以看到,驱动舵机的关键在于输出一个如下要求的PWM信号,也就是说脉冲的周期为20ms,占空比为0.5/20、1/20、1.5/20、2/20、2.5/20这样的PWM信号。

在这里插入图片描述

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

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

在这里插入图片描述

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

在这里插入图片描述

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 = 20000 - 1;		//ARR
TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 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

舵机要求的周期是20ms,也就是0.02s。那么频率就是f = 1 / T = 1 / 0.02 = 50Hz。
根据 Freq = CK_PSC / (PSC + 1) / (ARR + 1) ,即 50 = 72000000 / (PSC + 1) / (ARR + 1) 。

经过尝试,设置PSC + 1 = 72,ARR + 1 = 20000 刚好满足等式,也就是说PSC = 72 - 1 ,ARR = 20000 -1。

舵机要求高电平时间是0.5ms-2.5ms,也就是说占空比依次为0.5/20、1/20、1.5/20、2/20、2.5/20。

根据公式可以发现一些对应关系:Duty = CCR / (ARR + 1)

同时20k对应20ms,那CCR设置500,就是0.5ms。CCR设置2500,就是2.5ms。

所以按照这样的对应关系我们就可以推断出CCR的设置依次为500、1000、1500、2000、2500即可满足要求。

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_OC2Init(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_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

4.1.5 运行控制

TIM_Cmd(TIM2, ENABLE);

4.2 PWM输出模块

void PWM_SetCompare2(uint16_t Compare)
{
	TIM_SetCompare2(TIM2, Compare);
}

4.3 舵机模块

设置舵机角度函数:

void Servo_SetAngle(float Angle)
{
	PWM_SetCompare2(Angle / 180 * 2000 + 500);
}

4.4 主程序

在主程序while(1)循环里不断检测按键按下,检测到每按下一次,角度+30,加到180后返回0度,如此循环。

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Servo.h"
#include "Key.h"

uint8_t KeyNum;
float Angle;

int main(void)
{
	OLED_Init();
	Servo_Init();
	Key_Init();
	
	OLED_ShowString(1, 1, "Angle:");
	
	while (1)
	{
		KeyNum = Key_GetNum();
		if (KeyNum == 1)
		{
			Angle += 30;
			if (Angle > 180)
			{
				Angle = 0;
			}
		}
		Servo_SetAngle(Angle);
		OLED_ShowNum(1, 7, Angle, 3);
	}
}
  • 16
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
首先,需要在 CubeMX 中将 PB13 配置为 TIM1_CH1 的复用功能。然后,使用以下代码初始化 TIM1 和 PB13 引脚并输出 PWM 信号: ```c #include "stm32f4xx_hal.h" TIM_HandleTypeDef htim1; void initPWM() { // 初始化 TIM1 TIM_MasterConfigTypeDef sMasterConfig = {0}; TIM_OC_InitTypeDef sConfigOC = {0}; htim1.Instance = TIM1; htim1.Init.Prescaler = 84 - 1; // 定时器时钟为 84MHz,分频系数为 84,计数器时钟为 1MHz htim1.Init.CounterMode = TIM_COUNTERMODE_UP; htim1.Init.Period = 20000 - 1; // PWM 周期为 20ms,计数器计数到 20000 重载 htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; if (HAL_TIM_Base_Init(&htim1) != HAL_OK) { Error_Handler(); } // 配置 TIM1_CH1 为 PWM 模式 sConfigOC.OCMode = TIM_OCMODE_PWM1; sConfigOC.Pulse = 1500; // 设置初始占空比为 1500us / 20000us = 7.5% sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1) != HAL_OK) { Error_Handler(); } // 启动 PWM 输出 HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1); } int main(void) { // 初始化 HAL 库 HAL_Init(); // 初始化时钟 SystemClock_Config(); // 初始化 PWM initPWM(); while (1) { // 可以在这里修改 PWM 的占空比,例如设置为 1000us / 20000us = 5% // 注意占空比的取值范围为 1000us ~ 2000us HAL_Delay(1000); } } ``` 在上面的代码中,我们使用 TIM1 定时器和 PB13 引脚输出 PWM 信号,占空比的初始值为 7.5%。你可以在 `while` 循环中使用 `HAL_Delay` 函数来修改 PWM 的占空比。需要注意的是,占空比的取值范围为 1000us ~ 2000us,对应的占空比范围为 5% ~ 10%。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值