舵机最早用于船舶上实现其转向功能,这就是舵机最早的由来。
由直流电机、减速齿轮组、传感器(可变电阻)和控制电路组成的一套自动控制系统。
直流电机是一圈圈转动的,舵机只能在一定角度内转动(有最大旋转角度比如:180度),不能一圈圈转。普通直流电机无法反馈转动的角度信息(如果带编码器就可以反馈角度),而舵机可以。用途也不同,普通直流电机一般是整圈转动做动力用,舵机是控制某物体转动一定角度用(比如机器人的关节)。
自控制电路板接收来自信号线的控制信号,控制电机转动,电机带动一系列齿轮组,减速后传动至输出舵盘。舵机的输出轴和位置反馈电位计是相连的,舵盘转动的同时,带动位置反馈电位计,电位计将输出一个电压信号到控制电路板,进行反馈,然后控制电路板根据所在位置决定电机转动的方向和速度,从而达到目标停止。 其工作流程为: 控制信号→控制电路板→电机转动→齿轮组减速→舵盘转动→位置反馈电位计→控制电路板反馈→电机转动 … … 。 类似PID闭环控制。
舵机的输入线共有三条,如图所示,红色中间,是电源正线,一根棕色(有些是黑色)是电源地线,这两根线给舵机提供最基本的能源保证,主要是电机的转动消耗。电源有两种规格,一是4.8V,一是6.0V,分别对应不同的转矩标准,即输出力矩不同,6.0V对应的要大一些,具体看应用条件;另外一根线是控制信号线,一般为桔黄色(有些舵机为白色,主要是不同厂家可能采用不同颜色)。
舵机的信号线是做为输入线就是接收PWM信号(定时器产生)。一般PWM的周期是20ms,那么对应的频率是50hz。那么改变不同的占空比就可以控制转动的角度。其中占空比从0.5-2.5ms,相对应的舵盘位置为0-180度,呈线性变化。如图所示:
给它提供一定的脉宽,它的输出轴就会保持一定对应角度上,无论外界转矩怎么改变(只要目标不变就维持在这个角度类似PID自动控制算法),直到给它提供一个另外宽度的脉冲信号,它才会改变输出角度到新的对应位置上如所求。
舵机内部有一个基准电路,产生周期为20ms,宽度1.5ms的基准信号,有一个比较器,将外加信号与基准信号相比较,判断出方向和大小,从而生产电机的转动信号。由此可见,舵机是一种位置伺服驱动器,转动范围不能超过180度,适用于那些需要不断变化并可以保持的驱动器中,比如说机器人的关节、飞机的舵面等。
用通用定时器来产生PWM(CH1)
定时器.h
#ifndef __GENERAL_TIM_H__
#define __GENERAL_TIM_H__
#include "stm32f4xx_hal.h"
#define GENERAL_TIMx TIM2
#define GENERAL_TIM_RCC_CLK_ENABLE() __HAL_RCC_TIM2_CLK_ENABLE()
#define GENERAL_TIM_RCC_CLK_DISABLE() __HAL_RCC_TIM2_CLK_DISABLE()
#define GENERAL_TIM_GPIO_RCC_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()
#define GENERAL_TIM_CH1_PORT GPIOA
#define GENERAL_TIM_CH1_PIN GPIO_PIN_15
// 定义定时器预分频,定时器实际时钟频率为:84MHz
#define GENERAL_TIM_PRESCALER 840-1 // 实际时钟频率为:100KHz
// 定义定时器周期,当定时器开始计数到GENERAL_TIMx_PERIOD值是更新定时器并生成对应事件和中断
#define GENERAL_TIM_PERIOD 1999 // 定时器产生中断频率为:100KHz/2000=50Hz
/* 扩展变量 ------------------------------------------------------------------*/
extern TIM_HandleTypeDef htimx;
/* 函数声明 ------------------------------------------------------------------*/
void GENERAL_TIMx_Init(void);
#endif /* __GENERAL_TIM_H__ */
根据配置定时器预分频值与ARR值可以设置输出的PWM的周期是20ms,频率 = 50hz
定时器.c
#include "GeneralTIM/bsp_GeneralTIM.h"
TIM_HandleTypeDef htimx;
void HAL_TIM_MspPostInit(TIM_HandleTypeDef* htim)
{
GPIO_InitTypeDef GPIO_InitStruct;
if(htim->Instance==GENERAL_TIMx)
{
/* 定时器通道功能引脚端口时钟使能 */
GENERAL_TIM_GPIO_RCC_CLK_ENABLE();
/* 定时器通道1功能引脚IO初始化 */
GPIO_InitStruct.Pin = GENERAL_TIM_CH1_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF1_TIM2;
HAL_GPIO_Init(GENERAL_TIM_CH1_PORT, &GPIO_InitStruct);
}
}
void GENERAL_TIMx_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig;
TIM_MasterConfigTypeDef sMasterConfig;
TIM_OC_InitTypeDef sConfigOC;
htimx.Instance = GENERAL_TIMx;
htimx.Init.Prescaler = GENERAL_TIM_PRESCALER;
htimx.Init.CounterMode = TIM_COUNTERMODE_UP;
htimx.Init.Period = GENERAL_TIM_PERIOD;
htimx.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;
HAL_TIM_Base_Init(&htimx);
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
HAL_TIM_ConfigClockSource(&htimx, &sClockSourceConfig);
HAL_TIM_PWM_Init(&htimx);
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
HAL_TIMEx_MasterConfigSynchronization(&htimx, &sMasterConfig);
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 50; // 捕获比较寄存器的值
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
HAL_TIM_PWM_ConfigChannel(&htimx, &sConfigOC, TIM_CHANNEL_1);
HAL_TIM_MspPostInit(&htimx);
}
/**
* 函数功能: 基本定时器硬件初始化配置
* 输入参数: htim_base:基本定时器句柄类型指针
* 返 回 值: 无
* 说 明: 该函数被HAL库内部调用
*/
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* htim_base)
{
if(htim_base->Instance==GENERAL_TIMx)
{
/* 基本定时器外设时钟使能 */
GENERAL_TIM_RCC_CLK_ENABLE();
}
}
由于ARR设置为2000时对应是20ms。那么捕获比较寄存器的值设置为50.那么对应的占空比就是0.5ms。所有一启动就会在-90度的位置。
main.c
#include "stm32f4xx_hal.h"
#include "GeneralTIM/bsp_GeneralTIM.h"
#include "key/bsp_key.h"
/**
* 函数功能: 系统时钟配置
* 输入参数: 无
* 返 回 值: 无
* 说 明: 无
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_ClkInitTypeDef RCC_ClkInitStruct;
__HAL_RCC_PWR_CLK_ENABLE(); // 使能PWR时钟
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); // 设置调压器输出电压级别1
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; // 外部晶振,8MHz
RCC_OscInitStruct.HSEState = RCC_HSE_ON; // 打开HSE
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; // 打开PLL
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; // PLL时钟源选择HSE
RCC_OscInitStruct.PLL.PLLM = 8; // 8分频MHz
RCC_OscInitStruct.PLL.PLLN = 336; // 336倍频
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; // 2分频,得到168MHz主时钟
RCC_OscInitStruct.PLL.PLLQ = 7; // USB/SDIO/随机数产生器等的主PLL分频系数
HAL_RCC_OscConfig(&RCC_OscInitStruct);
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; // 系统时钟:168MHz
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; // AHB时钟: 168MHz
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; // APB1时钟:42MHz
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; // APB2时钟:84MHz
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5);
HAL_RCC_EnableCSS(); // 使能CSS功能,优先使用外部晶振,内部时钟源为备用
// HAL_RCC_GetHCLKFreq()/1000 1ms中断一次
// HAL_RCC_GetHCLKFreq()/100000 10us中断一次
// HAL_RCC_GetHCLKFreq()/1000000 1us中断一次
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000); // 配置并启动系统滴答定时器
/* 系统滴答定时器时钟源 */
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
/* 系统滴答定时器中断优先级配置 */
HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}
/**
* 函数功能: 主函数.
* 输入参数: 无
* 返 回 值: 无
* 说 明: 无
*/
int main(void)
{
/* 复位所有外设,初始化Flash接口和系统滴答定时器 */
HAL_Init();
/* 配置系统时钟 */
SystemClock_Config();
KEY_GPIO_Init();
/* 通用定时器初始化并配置PWM输出功能 */
GENERAL_TIMx_Init();
/* 启动通道PWM输出,其实脉宽:0.5ms,对应舵机的0度 */
HAL_TIM_PWM_Start(&htimx,TIM_CHANNEL_1);
/* 无限循环 */
while (1)
{
if( KEY1_StateRead() == KEY_DOWN)
{
__HAL_TIM_SET_COMPARE(&htimx,TIM_CHANNEL_1,100);// 脉宽:1ms, // -45
}
if( KEY2_StateRead() == KEY_DOWN)
{
__HAL_TIM_SET_COMPARE(&htimx,TIM_CHANNEL_1,150);// 脉宽:1.5ms, // 0
}
if( KEY3_StateRead() == KEY_DOWN)
{
__HAL_TIM_SET_COMPARE(&htimx,TIM_CHANNEL_1,200);// 脉宽:2.0ms, // 45
}
if( KEY4_StateRead() == KEY_DOWN)
{
__HAL_TIM_SET_COMPARE(&htimx,TIM_CHANNEL_1,250);// 脉宽:2.5ms, // 90
}
HAL_Delay(200); // 延时200ms 舵机会有一个反应转速
}
}
通过按键(按键代码没列出)改变占空比从而控制舵机的角度。如果占空比大于2.5ms也只会在最大角度。