STM32—TIM3输出PWM信号驱动MG996R舵机(按键控制)

一、前言

利用STM32的TIM3的通道1输出PWM信号,驱动MG996R舵机,按键控制。
相关知识:TIM定时器基本原理,TIM输出PWM信号、MG996R舵机驱动原理

二、MG996R舵机简介

MG996R舵机单线驱动,是一款360°舵机,180°舵机与360°舵机的区别就是:180°舵机可以直接控制舵机旋转的角度,但舵机只能够旋转180°;360°舵机无法直接控制其旋转角度,只能控制其转动方向和速度。
舵机的驱动信号由周期为20ms的脉冲来控制:

  1. 当高电平持续时间为0.5~1.5ms时,舵机正转,时间越小转动越快
  2. 当高电平持续时间为1.5~2.5ms时,舵机反转,时间越大转动越快
  3. 当高电平持续时间为1.5ms或者其他时间时,舵机停止转动

电机图片

三、通用定时器TIMx

通用TIMx定时器(TIM2、TIM3、TIM4和TIM5)功能主要包括如下:
16位向上、向下、向上/向下自动装载计数器
16位可编程(可以实时修改)预分频器,分频系数为1~65535
四种独立通道功能:
1.输入捕获
2.输出比较
3.PWM生成
4.单脉冲输出
使用外部信号控制定时器和定时器互连的同步电路
可以由如下事件触发中断或者DMA:
1.更新,即计数器溢出,或者计数器初始化
2.特定的触发事件,比如:计数器启动、停止、初始化等等
3.输入捕获
4.输出比较
支持针对定位的增量(正交)编码器和霍尔传感电路
触发输入作为外部时钟或者按周期的电流管理
可编程通用定时器的主要部分是一个16位计数器和其相关的自动装载寄存器,前面也说了,计数器可以向上、向下、双向计数。既然要计数,那就必须要知道计数的多少和每一次计数的时间。计数器的时钟由预分频器对时钟源分频得到。
时基单元包括:
计数器寄存器(TIMx_CNT)
预分频器寄存器(TIMx_PSC)
自动装载寄存器(TIMx_ARR)
计数器寄存器中存储的是当前计数的值,自动装载寄存器中存储的是目标计数值,当计数器溢出后,会重新装填目标计数值,而预分频器寄存器中的是对时钟的分频系数。
有专门的三个寄存器来控制PWM:
捕获/比较模式寄存器(TIMx_CCMR1/2)
捕获/比较使能寄存器(TIMx_CCER)
捕获/比较寄存器(TIMx_CCR1~4)

四、TIM3输出PWM信号代码详解

输出PWM用到的TIMx初始化结构体有:

1.时基初始化结构体TIM_TimeBaseInitTypeDef
2.输出比较初始化结构体TIM_OCInitTypeDef

typedef struct {
 uint16_t TIM_Prescaler;        // 预分频器
 uint16_t TIM_CounterMode;      // 计数模式
 uint32_t TIM_Period;           // 定时器周期
 uint16_t TIM_ClockDivision;    // 时钟分频
 uint8_t TIM_RepetitionCounter; // 重复计算器
 } TIM_TimeBaseInitTypeDef;

TIM_Prescaler:预分频器设置,只有经过预分频器后的时钟才是CK_CNT,计数器时钟频率 (fCK_CNT) 等于fCK_PSC
/ (PSC[15:0] + 1),可实现 1 至 65536 分频。
TIM_CounterMode:定时器计数模式,可设置向上计数、向下计数和中心对齐计数三种模式。
TIM_Period:设置的是自动重装寄存器ARR的值,ARR为要装载到影子寄存器的值,可设置 1 至 65536 。
TIM_ClockDivision:时钟分频,设置定时器时钟 CK_INT 频率与死区发生器以及数字滤波器采样时钟频率分频比。可以选择 1、
2、 4 分频。 TIM_RepetitionCounter:重复计数器,只有八位,只存在与高级定时器。

typedef struct {
 uint16_t TIM_OCMode;         // 比较输出模式
 uint16_t TIM_OutputState;    // 比较输出使能
 uint16_t TIM_OutputNState;   // 比较互补输出使能
 uint32_t TIM_Pulse;          // 脉冲宽度
 uint16_t TIM_OCPolarity;     // 输出极性
 uint16_t TIM_OCNPolarity;    // 互补输出极性
 uint16_t TIM_OCIdleState;    // 空闲状态下比较输出状态
 uint16_t TIM_OCNIdleState;   // 空闲状态下比较互补输出状态
 } TIM_OCInitTypeDef;

TIM_OCMode:比较输出模式选择,总共有八种,常用的为 PWM1/PWM2。它设定 CCMRx 寄存器 OCxM[2:0]位的值。
TIM_OutputState:比较输出使能,决定最终的输出比较信号 OCx 是否通过外部引脚输 出。它设定 TIMx_CCER 寄存器
CCxE/CCxNE 位的值。 TIM_OutputNState:比较互补输出使能,决定 OCx 的互补信号 OCxN 是否通过外部引脚
输出。它设定 CCER 寄存器 CCxNE 位的值。 TIM_Pulse:比较输出脉冲宽度,实际设定比较寄存器 CCR
的值,决定脉冲宽度。可 设置范围为 0 至 65535。 TIM_OCPolarity:比较输出极性,可选 OCx
为高电平有效或低电平有效。它决定着定 时器通道有效电平。它设定 CCER 寄存器的 CCxP 位的值。
TIM_OCNPolarity:比较互补输出极性,可选 OCxN 为高电平有效或低电平有效。它 设定 TIMx_CCER 寄存器的
CCxNP 位的值。 TIM_OCIdleState:空闲状态时通道输出电平设置,可选输出 1 或输出 0,即在空闲状 态(BDTR_MOE
位为 0)时,经过死区时间后定时器通道输出高电平或低电平。它设定 CR2 寄存器的 OISx 位的值。
TIM_OCNIdleState:空闲状态时互补通道输出电平设置,可选输出 1 或输出 0,即在 空闲状态(BDTR_MOE 位为
0)时,经过死区时间后定时器互补通道输出高电平或低电 平,设定值必须与 TIM_OCIdleState 相反。它设定是 CR2 寄存器的
OISxN 位的值。

当要输出PWM信号时只需要配置如下成员即可:

uint16_t TIM_OCMode; // 比较输出模式
uint16_t TIM_OCPolarity; // 输出极性
uint16_t TIM_OutputState; // 比较输出使能

输出极性决定了有效电平是高电平还是低电平
输出极性决定了有效电平是高电平还是低电平

void GENERAL_TIM_Init(void)
{
		GPIO_InitTypeDef GPIO_InitStructure;
	  TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    TIM_OCInitTypeDef  TIM_OCInitStructure;
	  RCC_APB2PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
	  GENERAL_TIM_APBxClock_FUN(GENERAL_TIM_CLK,ENABLE);
	GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_0;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOB, &GPIO_InitStructure);
	
//	TIM_DeInit( TIM3 );
	TIM_TimeBaseStructure.TIM_Period=GENERAL_TIM_PERIOD;	
	// 驱动CNT计数器的时钟 = Fck_int/(psc+1)
	TIM_TimeBaseStructure.TIM_Prescaler= GENERAL_TIM_PSC;	
	// 时钟分频因子 ,配置死区时间时需要用到
	TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;		
	// 计数器计数模式,设置为向上计数
	TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;		
	// 初始化定时器
	TIM_TimeBaseStructure.TIM_RepetitionCounter=0;	
	TIM_TimeBaseInit(GENERAL_TIM, &TIM_TimeBaseStructure);
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
	// 输出使能
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
	// 输出通道电平极性配置	
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
	TIM_OCInitStructure.TIM_Pulse = GENERAL_TIM_CCR3;
	TIM_OC3Init(GENERAL_TIM, &TIM_OCInitStructure);
	TIM_OC3PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable);
//	TIM_ARRPreloadConfig( TIM3, ENABLE );
	TIM_Cmd( TIM3, ENABLE );
}
``
```c
// 输出PWM的频率为 72M/{ (ARR+1)*(PSC+1) }
#define            GENERAL_TIM_PERIOD            (200-1)
#define            GENERAL_TIM_PSC               (7200-1)
#define            GENERAL_TIM_CCR3                0 

主函数

	while(1)
	{
			key=KEY_Scan(1);	//得到键值
	   	if(key)
		{		
			switch(key)
			{				 
				case KEY1_PRES:	//反向最大转速
					TIM_SetCompare3(TIM3,25);  
					break;
				case KEY0_PRES:	//正向最大转速 
					TIM_SetCompare3(TIM3,5);	
					break;
				case WE_UP_PRES:
					TIM_SetCompare3(TIM3,15);	//停止
					break;
			}
    }
 }
 TIM_SetCompare2(TIM_TypeDef* TIMx, uint16_t Compare2);
 函数改变ccr,从而改变PWM信号的占空比。
 /*                  
		20ms脉冲  
	5-14  正转,值越小,转的越快
	16-25 反转,值越大,转的越快
	*/

上传代码

https://download.csdn.net/download/meaning2/16745338

可以使用STM32F103C8T6的TIM1模块来控制PA10引脚的PWM信号,从而实现对MG996R舵机的控制。下面是一个简单的示例代码: ``` #include "stm32f10x.h" void TIM1_PWM_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1 | RCC_APB2Periph_GPIOA, ENABLE); // 配置PA10引脚为复用推挽输出 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); // 设置TIM1为PWM模式 TIM_TimeBaseStructure.TIM_Period = 20000 - 1; // 定时器周期为20ms,即50Hz TIM_TimeBaseStructure.TIM_Prescaler = 72 - 1; // 定时器分频系数为72,即计数频率为1MHz TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); // 配置TIM1_CH3通道为PWM输出模式 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = 1500; // 初始占空比为50% TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OC3Init(TIM1, &TIM_OCInitStructure); TIM_Cmd(TIM1, ENABLE); } void setServoPosition(int position) { // 将0-180度的角度值转换为500-2500us的脉冲宽度 int pulse_width = position * 10 + 500; TIM_SetCompare3(TIM1, pulse_width); } int main(void) { TIM1_PWM_Init(); while (1) { // 控制舵机旋转到0度 setServoPosition(0); Delay(1000); // 控制舵机旋转到90度 setServoPosition(90); Delay(1000); // 控制舵机旋转到180度 setServoPosition(180); Delay(1000); } } ``` 上述代码中,TIM1_PWM_Init()函数用于初始化PA10引脚和TIM1定时器,setServoPosition()函数用于设置舵机的位置。主函数中演示了如何将舵机从0度旋转到180度。你可以根据需要修改setServoPosition()函数来控制舵机旋转到特定的位置。 相关问题: 1. 如何使用STM32定时器控制PWM信号? 2. 如何将角度值转换为舵机控制信号的脉冲宽度? 3. MG996R舵机的工作原理是什么?
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值