SG90 伺服电机驱动
舵机是通俗的叫法,它的本质是一个伺服电机,也可以叫做位置(角度)伺服驱动器。一般被应用在那些需要控制角度变化的系统中,可以方便的实现转动任意的角度,实现控制角度的变化。
工作原理
sg90 舵机模块内有控制电路,控制信号通过信号线输入到内部的控制电路中,调制芯片将输入的信号进行调制,获得直流偏置电压。然后再由内部的基准电路产生周期为 20ms,宽度为 1.5ms 的基准信号,将直流偏置电压和电位器电压进行比较,从而获得输出的电压差。由电压差控制舵机的转动,这个电压差的正负控制舵机正反转。
舵机的控制信号是脉宽调制(PWM)信号,其中脉冲宽度从最小到最大,相对应舵盘的位置为 0—180 度,呈线性变化。给它提供一定的脉宽,它的输出轴就会保持在一个相对应的角度上,无论外界转矩怎样改变,直到给它提供一个另外宽度的脉冲信号,它才会改变输出角度到新的对应的位置上。
控制电路板接受来自信号线相应的 PWM 控制信号,进而控制电机转动,电机带动一系列齿轮组,减速后传动至输出舵盘。舵机的输出轴和位置反馈电位计是相连的,舵盘转动的同时,带动位置反馈电位计,电位计将输出一个电压信号到控制电路板,进行反馈,然后控制电路板根据所在位置决定电机的转动方向和速度,从而达到目标停止。
注意:每个舵机的这个脉冲宽度要根据实际的调节测试得出。
接线方式
这里的 SG90 舵机使用的是 180 度的数字舵机,只需要给信号线发送一个固定脉宽的信号,即可旋转到指定的位置。
驱动程序
driver_sg90.h
#ifndef __DRIVER_SG90_H__
#define __DRIVER_SG90_H__
#include "main.h"
#include "tim.h"
#ifdef __cplusplus
extern "C" {
#endif
uint8_t sg90_init(TIM_HandleTypeDef *tim_handle);
uint8_t sg90_set_angle(uint8_t angle);
#ifdef __cplusplus
}
#endif
#endif
driver_sg90.c
#include "driver_sg90.h"
/**
* @brief Timer handle definition
*/
static TIM_HandleTypeDef sg90_tim_handle;
#define SG90_GPIO_Port GPIOB
#define SG90_GPIO_Pin GPIO_PIN_15
#define SG90_IN1 TIM_CHANNEL_2
#define SG90_MIN_PULSE_WIDTH 200 /* 测试得到 0° 对应的脉冲宽度单位为 200 us */
#define SG90_MAX_PULSE_WIDTH 1200 /* 测试得到 180° 对应的脉冲宽度单位为 1200 us */
#define SG90_MIN_ANGLE 0 /* 最小角度 */
#define SG90_MAX_ANGLE 180 /* 最大角度 */
#define TIMER_FREQUENCY 1000000 /* 定时器计数器频率为 1 MHz */
/**
* @brief 舵机初始化
* @param tim_handle 定时器
* @return
*/
uint8_t sg90_init(TIM_HandleTypeDef *tim_handle)
{
sg90_tim_handle = *tim_handle;
TIM_OC_InitTypeDef sConfig = {0};
/* Configure timer for 1 MHz frequency */
sg90_tim_handle.Init.Prescaler = (SystemCoreClock / TIMER_FREQUENCY) - 1;
sg90_tim_handle.Init.CounterMode = TIM_COUNTERMODE_UP;
sg90_tim_handle.Init.Period = 20000 - 1; /* Provides 20ms PWM cycle */
sg90_tim_handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
sg90_tim_handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_PWM_Init(&sg90_tim_handle) != HAL_OK)
{
return 1; /* Initialization failed */
}
/* Configure PWM channel */
sConfig.OCMode = TIM_OCMODE_PWM1;
sConfig.Pulse = SG90_MIN_PULSE_WIDTH; /* Default Starting at 0° */
sConfig.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfig.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&sg90_tim_handle, &sConfig, SG90_IN1) != HAL_OK)
{
return 2; /* Channel configuration failed */
}
HAL_TIM_PWM_Start(&sg90_tim_handle, SG90_IN1);
/* Initialization success */
return 0;
}
/**
* @brief 设置舵机旋转角度
* @param angle 目标角度
* @return
*/
uint8_t sg90_set_angle(uint8_t angle)
{
if (angle < SG90_MIN_ANGLE)
{
angle = SG90_MIN_ANGLE;
}
else if(angle > SG90_MAX_ANGLE)
{
angle = SG90_MAX_ANGLE;
}
/* Calculate the corresponding pulse width */
/*
(SG90_MAX_PULSE_WIDTH - SG90_MIN_PULSE_WIDTH) / (SG90_MAX_ANGLE - SG90_MIN_ANGLE)
equals to the pulse width corresponding to per degree
*/
uint32_t pulse_width = SG90_MIN_PULSE_WIDTH +
((SG90_MAX_PULSE_WIDTH - SG90_MIN_PULSE_WIDTH) / (SG90_MAX_ANGLE - SG90_MIN_ANGLE)) * angle;
/* Set the pulse width */
__HAL_TIM_SET_COMPARE(&sg90_tim_handle, SG90_IN1, pulse_width);
return 0;
}
driver_sg90_test.h
#ifndef __DRIVER_SG90_TEST_H__
#define __DRIVER_SG90_TEST_H__
#include "main.h"
#include "driver_sg90.h"
#ifdef __cplusplus
extern "C" {
#endif
void sg90_test(void);
#ifdef __cplusplus
}
#endif
#endif
driver_sg90_test.c
#include "driver_sg90_test.h"
#include "tim.h"
void sg90_test(void)
{
sg90_init(&htim12);
while (1)
{
/************* 功能测试 *****************/
for (uint8_t i = 0; i < 3; i++)
{
sg90_set_angle(0);
HAL_Delay(200);
sg90_set_angle(180);
HAL_Delay(200);
// 进行测试调节
for (uint8_t angle = 0; angle <= 180; angle += 30)
{
sg90_set_angle(angle);
HAL_Delay(300);
}
}
/************* 进阶测试 ******************/
// 正弦插值
for (uint16_t i = 0; i < 360; i += 2)
{
// i为弧度值 振幅为 120 度,中心在 0 度
float rad = (float)i * 3.14159f / 180.0f;
uint8_t angle = 0 + (int)(120 * sinf(rad));
sg90_set_angle(angle);
HAL_Delay(200);
}
HAL_Delay(200);
// 随机平滑变化
uint8_t current_angle = 90;
uint8_t target_angle;
for (uint8_t i = 0; i < 3; i++)
{
target_angle = 0 + (rand() % 180); // 随机角度范围在0-180之间
// 平滑过渡到目标角度
if (target_angle > current_angle)
{
for (uint8_t angle = current_angle; angle <= target_angle; angle++)
{
sg90_set_angle(angle);
HAL_Delay(100);
}
}
else
{
for (uint8_t angle = current_angle; angle >= target_angle; angle--)
{
sg90_set_angle(angle);
HAL_Delay(100);
}
}
current_angle = target_angle;
HAL_Delay(300);
}
}
}
HAL 配置
这里使用定时器 TIM12 通道2 配置为 PWM 输出,参数设置如图所示。
自动生成的 HAL 程序如下,这个不重要,因为驱动程序初始化时会修改,这里只要确保打开定时器的 PWM 输出即可。
tim.c
/* TIM12 init function */
void MX_TIM12_Init(void)
{
/* USER CODE BEGIN TIM12_Init 0 */
/* USER CODE END TIM12_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
/* USER CODE BEGIN TIM12_Init 1 */
/* USER CODE END TIM12_Init 1 */
htim12.Instance = TIM12;
htim12.Init.Prescaler = 84-1;
htim12.Init.CounterMode = TIM_COUNTERMODE_UP;
htim12.Init.Period = 20000;
htim12.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim12.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_Base_Init(&htim12) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim12, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_PWM_Init(&htim12) != HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&htim12, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM12_Init 2 */
/* USER CODE END TIM12_Init 2 */
HAL_TIM_MspPostInit(&htim12);
}