杂谈

自己拿到这一模块是也挺迷茫的,后来看了一些资料,也渐渐积累了些自己的理解,很多博文并没有将舵机讲明白,至少你待把PWM与角度如何换算讲清楚吧,所以笔者写这篇博文供大家学习掌握。

如果你拿到一个舵机,该咋办?莫慌,往下看

第一步先要区分这个舵机是数字舵机还是模拟舵机。

以为两者的控制方式有些许不同,模拟舵机需要给它不停的发送PWM信号,才能让它保持在规定的位置或者让它按照某个速度转动,数字舵机则只需要发送一次PWM信号就能保持在规定的某个位置。

换句话说,模拟舵机想要它转到某一位置,程序上就要放在while等循环中,数字舵机只要给一次PWM值就能以一定速度转到某一位置。

第二步是看清自己的需要

大多数舵机都是180度的舵机,顾名思义就是旋转的范围就是0-180度,这类舵机是控制舵机转到某一角度的。

而有的舵机是360度的舵机,这类舵机则是而一个PWM信号,舵机会以一个特定的速度转动,类似与电机。但与电机不同的是,360舵机是闭环控制,速度控制稳定。

第三步是了解舵机的内部结构

舵机内部长啥样?不如自己拆一个来的直接。

在这里插入图片描述

主要是有一个电机、角度传感器,驱动电路、传动齿轮组成,四者形成一个闭环系统,实现角度(或者称作位置)的精准控制(见下图)。这些有些了解就好,不必深究。
在这里插入图片描述

在这里插入图片描述

第四步是弄清舵机的工作原理

这一步很是关键!!!

首先舵机是用PWM波来驱动的,有PWM信号大哥的地方,就有周期占空比两个小弟跟着。

接下来以数字舵机为例,毕竟用的最多。

舵机的控制原理就是向控制端(就是信号线,一般是黄色或者白色)输入一个PWM值,舵机就会转到某一个角度,所以,给定的PWM值与角度是对应关系。

那么,如何确定这个关系就是关键所在!

既然是输出一路PWM波,那么就需要先确定PWM的频率(一般为50HZ),进而就确定了PWM的输出周期(一般为20ms)。对于这点不清楚的可以见我以前的博文。PWM相关要点

一般舵机都会给出下面几个参数:

高电平0.5-2.5ms对应0-180度。(2.5%<占空比<12.5%)
也就是这张图

在这里插入图片描述如何确定呢?来个公式

设高电平时间为t,那对应的角度angle=(t-0.5)*180/(2.5-0.5),(0.5<=t<=2.5)。这应该很好理解了。
举例如下:高电平t=0.5ms,带入公式,angle=0度。

第五步掌握PWM输出舵机的定时器配置

假如使用的系统主频为72M,得到50HZ的PWM,则有下面一个公式

50=72000 000/(arr+1)/(psc+1)

设arr=9999,则psc=143,写程序时输出比较配置好这两个参数,从而确定计时器计一个数时间为0.002ms,0.002*(arr+1)=20ms,刚好是PWM波的周期。

接下来要确定舵机转动角度与PWM值之间的关系。

一般我们直接使用TIM_SetCompare1(TIM1,PWM);这个函数修改输出的PWM值,也就是直接写值到CCR寄存器。

在这里插入图片描述
根据上图公式,我们就确定了PWM值的范围[250,1250]对应舵机[0,180]。

第六步编写驱动程序

void TIM2_PWM_Init(u16 arr, u16 psc)
{
	GPIO_InitTypeDef GPIO_InitStructure; //定义一个引脚初始化的结构体
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStrue; //定义一个定时中断的结构体	
	TIM_OCInitTypeDef TIM_OCInitTypeStrue; //定义一个PWM输出的结构体
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能GPIOA时钟,在STM32中使用IO口前都要使能对应时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //使能通用定时器2时钟,A0引脚对应TIM2CHN1
	
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;//引脚0
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; //复用推挽输出模式,定时器功能为A0引脚复用功能
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; //定义该引脚输出速度为50MHZ
  GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化引脚GPIOA0
	 
	TIM_TimeBaseInitStrue.TIM_Period=arr; //计数模式为向上计数时,定时器从0开始计数,计数超过到arr时触发定时中断服务函数
	TIM_TimeBaseInitStrue.TIM_Prescaler=psc; //预分频系数,决定每一个计数的时长
	TIM_TimeBaseInitStrue.TIM_CounterMode=TIM_CounterMode_Up; //计数模式:向上计数
	TIM_TimeBaseInitStrue.TIM_ClockDivision=0; //一般不使用,默认TIM_CKD_DIV1
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStrue); //根据TIM_TimeBaseInitStrue的参数初始化定时器TIM2
	
	TIM_OCInitTypeStrue.TIM_OCMode=TIM_OCMode_PWM1; //PWM模式1,当定时器计数小于TIM_Pulse时,定时器对应IO输出有效电平
	TIM_OCInitTypeStrue.TIM_OCPolarity=TIM_OCNPolarity_High; //输出有效电平为高电平
	TIM_OCInitTypeStrue.TIM_OutputState=TIM_OutputState_Enable; //使能PWM输出
	TIM_OCInitTypeStrue.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值
	TIM_OC1Init(TIM2, &TIM_OCInitTypeStrue); //根TIM_OCInitTypeStrue参数初始化定时器2通道1

	TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Disable); //CH1预装载使能
	
	TIM_ARRPreloadConfig(TIM2, ENABLE); //CH1预装载使能
	
	TIM_Cmd(TIM2, ENABLE); //使能定时器TIM2
}

//main文件调用方式

int main(void)
{
	delay_init();
  TIM2_PWM_Init(9999, 143); //TIM2_Int_Init(u16 arr, u16 psc),初始化定时器TIM2
	                       
	u16 PWM=750;
	u8  Direction=1;
  while(1)
	{
		delay_ms(100);
		if(Direction)PWM=PWM+50;
		else PWM=PWM-50;
		if (PWM>1250) Direction=0;  //PWM值1250代表舵机位置接近180度
		if (PWM<250)    Direction=1; //PWM值250代表舵机位置接近0度
		TIM_SetCompare1(TIM2, PWM); //设置待装入捕获比较寄存器的脉冲值,相当于不断设置TIM_Pulse
		                      
	}
}



到此,舵机基本的控制方式就应该掌握了。

注意事项:舵机可别直接接在32等核心板上,容易烧,需要外部供电,只要把信号线接到对应PWM输出引脚上就可以。

有帮助的不妨点个赞,哈哈。