舵机最早用于船舶上实现其转向功能,这就是舵机最早的由来。

1.舵机的组成:

直流电机减速齿轮组传感器(可变电阻)控制电路组成的一套自动控制系统

2.舵机与普通直流电机的区别:

直流电机是一圈圈转动的,舵机只能在一定角度内转动(有最大旋转角度比如:180度),不能一圈圈转。普通直流电机无法反馈转动的角度信息(如果带编码器就可以反馈角度),而舵机可以。用途也不同,普通直流电机一般是整圈转动做动力用,舵机是控制某物体转动一定角度用(比如机器人的关节)。

3.舵机的自动控制系统介绍

自控制电路板接收来自信号线的控制信号,控制电机转动,电机带动一系列齿轮组,减速后传动至输出舵盘。舵机的输出轴和位置反馈电位计是相连的,舵盘转动的同时,带动位置反馈电位计,电位计将输出一个电压信号到控制电路板,进行反馈,然后控制电路板根据所在位置决定电机转动的方向和速度,从而达到目标停止。 其工作流程为: 控制信号→控制电路板→电机转动→齿轮组减速→舵盘转动→位置反馈电位计→控制电路板反馈→电机转动 … … 。 类似PID闭环控制。

 4.舵机的接线

舵机的输入线共有三条,如图所示,红色中间,是电源正线一根棕色(有些是黑色)是电源地线,这两根线给舵机提供最基本的能源保证,主要是电机的转动消耗。电源有两种规格,一是4.8V,一是6.0V,分别对应不同的转矩标准,即输出力矩不同,6.0V对应的要大一些,具体看应用条件;另外一根线是控制信号线,一般为桔黄色(有些舵机为白色,主要是不同厂家可能采用不同颜色)。

5.舵机的控制原理:

舵机的信号线是做为输入线就是接收PWM信号(定时器产生)。一般PWM的周期是20ms,那么对应的频率是50hz那么改变不同的占空比就可以控制转动的角度。其中占空比从0.5-2.5ms,相对应的舵盘位置为0-180度,呈线性变化。如图所示:

 给它提供一定的脉宽,它的输出轴就会保持一定对应角度上,无论外界转矩怎么改变(只要目标不变就维持在这个角度类似PID自动控制算法),直到给它提供一个另外宽度的脉冲信号,它才会改变输出角度到新的对应位置上如所求。

舵机内部有一个基准电路,产生周期为20ms,宽度1.5ms的基准信号,有一个比较器,将外加信号与基准信号相比较,判断出方向和大小,从而生产电机的转动信号。由此可见,舵机是一种位置伺服驱动器,转动范围不能超过180度,适用于那些需要不断变化并可以保持的驱动器中,比如说机器人的关节、飞机的舵面等。

6.代码列子

用通用定时器来产生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也只会在最大角度。

Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐