当前位置:   article > 正文

STM32入门笔记(02):F103C8T6 舵机PWM控制实验(SPL库函数版)_hwz020舵机

hwz020舵机

舵机的结构

舵机简单的说就是集成了直流电机、 电机控制器和减速器等, 并封装在一个便于安装的外壳里的伺服单元。 能够利用简单的输入信号比较精确的转动给定角度的电机系统。 舵机安装了一个电位器(或其它角度传感器) 检测输出轴转动角度, 控制板根据电位器的信息能比较精确的控制和保持输出轴的角度。 这样的直流电机控制方式叫闭环控制, 所以舵机更准确的说是伺服马达, 英文 servo。

在这里插入图片描述
主要有几个部分:

  • 外壳
  • 减速齿轮组
  • 电机
  • 电位器
  • 控制电路

简单的工作原理是控制电路接收信号源的控制信号, 并驱动电机转动; 齿轮组将电机的速度成大倍数缩, 并将电机的输出扭矩放大响应倍数,然后输出; 电位器和齿轮组的末级一起转动, 测量舵机轴转动角度; 检测并根据电位器判断舵机转动角度, 然后控制舵机转动到目标角度或保持在目标角度。

舵机的外壳一般是塑料的, 特殊的舵机可能会有金属铝合金外壳。 金属外壳能够提供更好的散热, 可以让舵机里面的电机运行在更高功率下, 以提供更高的扭矩输出。

金属外壳也可以提供更牢固的固定位置。 舵机的齿轮箱有塑料齿轮、 混合齿轮、 金属齿轮的差别。

塑料齿轮成本底, 噪音小, 但是强度较低; 金属齿轮强度高,但是成本也会增加, 在装配精度一般的情况下会有很大的噪音。

小扭矩舵机、 微舵、扭矩大但功率密度小的舵机一般都用塑料齿轮, 如 Futaba 3003, 辉盛的 9g微舵。 金属齿轮一般用于功率密度较高的舵机上, 比如辉盛的 MG995 舵机, 在和 3003 一样体积的情况下却能提供 13KG 的扭矩。 Hitec 甚至用钛合金作为齿轮材料, 其高强度能保证 3003 大小的舵机能提供 20 几公斤的扭矩。 混合齿轮在金属齿轮和塑料齿轮间做了折中, 在电机输出减速箱扭矩不大的部位, 用塑料齿轮。

舵机的参数

  • 响应速度
  • 扭矩
  • 工作电压
  • 精度

转速由舵机无负载的情况下转过 60° 角所需时间来衡量, 常见舵机的速度一般在 0.11s/60° -0.21s/60° 之间。

在这里插入图片描述
舵机扭矩的单位是 KG· CM, 这是一个扭矩单位, 和我们的常规的扭矩单位N· m 可以换算, 换算关系为 1/10。 KG*CM 可以理解为在舵盘上距舵机轴中心水平距离 1CM 处, 当摆臂与负载垂直的时候舵机能够带动的物体重量。
在这里插入图片描述

常规来说舵机的驱动电压都可以用 5V 驱动, 但是实际上我们舵机都有一个驱动电压的范围, 这个我们可以根据参数表获取。 舵机在驱动范围内不同的驱动电压下可能会表现出不同的性能。 这个在我们设计电路的时候就可以充分考虑利用,使舵机的性能达到最大化。

在这里插入图片描述

上图我们可以看到 HWZ020、 996、 WH060 三种规格的舵机都有不同的参数4.8~8.4 不等。 996 舵机明确注明在 4.8V 与 6V 驱动下有不同的反应速度。

在图中我们看到最后一项为工作死区, 是以 us 来给定的参数。

这个工作死区实际是根据我们舵机的工作原理来进行描述的, 他在一定程度上表现了舵机的精度。 但是在大负载、 高阻尼的工况下应根据实际的情况去进行分析。 工作死区多少 us 实际是根据这个数值换算成我们对应的舵机角度与一个角度对应。

以上述的 3us 为例, 一般舵机有 50HZ0.5~2.5ms 占空比对-90~90° 的转角位置。 那么就是 2.5-0.5=2ms 的高电平时差对应 180° 的角度。 那么 3us 对应角度就是 180/2000*3=0.27° 这个精度是很高的, 实际上这个只是从直流电机的死区电压获得的数据, 还要结合我们舵机的生产工艺、 装配精度、 使用工况的情况来综合判断, 一般综合之后精度有 1° 左右。

舵机的工作原理

工作原理是控制电路接收信号源的控制脉冲, 并驱动电机转动; 齿轮组将电机的速度大倍数缩小, 并将电机的输出扭矩放大响应倍数, 然后输出; 电位器和齿轮组的末级一起转动, 测量舵机轴转动角度; 电路板检测并根据电位器判断舵机转动角度, 然后控制舵机转动到目标角度或保持在目标角度。

模拟舵机需要一个外部控制器(遥控器的接收机或者单片机) 产生脉宽调制信号来告诉舵机转动角度, 脉冲宽度是舵机控制器所需的编码信息。 舵机的控制脉冲周期 20ms,脉宽从 0.5ms-2.5ms, 分别对应-90 度到+90 度的位置(对于 180°舵机)。

在这里插入图片描述

需要强调的是一般来说舵机需要的电源一般都在 1A 以上, 所以我们在使用的时候一般都需要给舵机的电路单独的做一个稳压电源保证舵机的工作。

舵机PID控制框图

在这里插入图片描述

舵机控制实验

在这里插入图片描述

TIMER.h文件

#ifndef __TIMER_H
#define __TIMER_H	
#include "sys.h"
#include "stm32f10x_tim.h"

void TIM2_Int_Init(u16 arr, u16 psc);
void TIM2_PWM_Init(u16 arr, u16 psc);

#endif

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

TIMER.c文件

#include "TIMER.h"

//通用定时器2初始化函数,arr:自动重装载值,psc:预分频系数,默认定时时钟为72MHZ时,两者共同决定定时中断时间
void TIM2_Int_Init(u16 arr, u16 psc)
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStrue; //定义一个定时中断的结构体
	NVIC_InitTypeDef NVIC_InitStrue; //定义一个中断优先级初始化的结构体
	
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //使能通用定时器2时钟
	
	TIM_TimeBaseInitStrue.TIM_Period=arr; //计数模式为向上计数时,定时器从0开始计数,计数超过到arr时触发定时中断服务函数
	TIM_TimeBaseInitStrue.TIM_Prescaler=psc; //预分频系数,决定每一个计数的时长
	TIM_TimeBaseInitStrue.TIM_CounterMode=TIM_CounterMode_Up; //计数模式:向上计数
	TIM_TimeBaseInitStrue.TIM_ClockDivision=TIM_CKD_DIV1; //一般不使用,默认TIM_CKD_DIV1
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStrue); //根据TIM_TimeBaseInitStrue的参数初始化定时器TIM2
	
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //使能TIM2中断,中断模式为更新中断:TIM_IT_Update
	
	NVIC_InitStrue.NVIC_IRQChannel=TIM2_IRQn; //属于TIM2中断
	NVIC_InitStrue.NVIC_IRQChannelCmd=ENABLE; //中断使能
	NVIC_InitStrue.NVIC_IRQChannelPreemptionPriority=1; //抢占优先级为1级,值越小优先级越高,0级优先级最高
	NVIC_InitStrue.NVIC_IRQChannelSubPriority=1; //响应优先级为1级,值越小优先级越高,0级优先级最高
	NVIC_Init(&NVIC_InitStrue); //根据NVIC_InitStrue的参数初始化VIC寄存器,设置TIM2中断
	
	TIM_Cmd(TIM2, ENABLE); //使能定时器TIM2
}

void TIM2_IRQHandler()
{
  if(TIM_GetITStatus(TIM2, TIM_IT_Update)==1) //当发生中断时状态寄存器(TIMx_SR)的bit0会被硬件置1
	{
	  PCout(13)=!PCout(13); //LED灯(PC.13引脚)状态取反,该函数封装在库函数"sys.h"中
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update); //状态寄存器(TIMx_SR)的bit0置0
	}
}

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
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69

main.c文件

#include "TIMER.h"
#include "delay.h"
#include "led.h" 

int main(void)
{
	u16 PWM=750;//PWM累加计数
	u8  Direction=1;//标识变量
	
	delay_init();//初始化延时
	LED_Init();//初始化LED
	TIM2_Int_Init(5000-1,7200-1); //初始化 Timer2定时器中断Tout500ms  
	TIM2_PWM_Init(10000-1, 144-1);//初始化 Timer2PWM输出 Tout=20ms=(arr+1)(psc+1)/Tclk,Tclk为内部通用定时器时钟,本例程默认设置为72MHZ
  while(1)
	{
		delay_ms(50);
		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
		                            //也即设置占空比,输出的PWM值
//在本例程中PWM值即为一个周期(20ms)内引脚A0输出的高电平时长(单位2^-3ms)
	}
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

资料下载:

参考资料

补充说明:

STM32F407芯片的时钟为84MHZ(假设时钟频率位84MHz,可以理解为stm32时钟1秒计数84000000次),预分频器寄存器 (TIMx_PSC)为16位寄存器,用于对计数器时钟频率进行分频(即运行时修改),分频系数介于 1 和 65536 之间,也就是2的16次方。为了便于计算,这里把参数psc设为8399(8399的原因是预分频寄存器从0开始计数,到8399溢出)。

分频后:
由 1/84 000 000 = t/8400
得 t=0.0001s

说明:1秒钟计数84000 000次分频后为1秒钟计数8400次。
周期变为t=0.1ms (意思为分频之后的时钟周期为0.0001us)

而我们需要PWM周期为50HZ也就是20ms 所以自动重装载值arr=200(根据公式计算得出)。

问题:PWM50hz为什么等于20ms?
答案:频率是f=50Hz,那么周期T=1/f=1/50=0.02s=20ms

公式:Tout= ((arr+1)*(psc+1))/Tclk

  • Tout 周期/溢出时间 单位us
  • Tclk 系统时钟 单位Mhz (这里是84MHZ)

已知psc=8400,Tclk=84,Tout=20ms=20000us,求arr?
那么根据公式 ((arr-1+1)*(8400-1+1))/84 可得PWM周期arr=200。

又已知psc=8400,Tclk=84,Tout=0.5ms(即500us),求arr?
根据((arr-1+1)*(8400-1+1))/84 =500us 可得自动重装载值arr=5。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/302094
推荐阅读
相关标签
  

闽ICP备14008679号