guowenxue
4 days ago c6a1e7c00cd2946cef7716b205026a907136778b
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
/**********************************************************************
*   Copyright: (C)2024 LingYun IoT System Studio
*      Author: GuoWenxue<guowenxue@gmail.com>
* Description: SG90 Servo Motor Driver for ISKBoard
*
*   ChangeLog:
*        Version    Date       Author            Description
*        V1.0.0  2024.08.29    GuoWenxue      Release initial version
***********************************************************************/
 
/**
 * SG90 舵机控制(0° ~ 180°) : https://detail.tmall.com/item.htm?id=615779197448
 * - 正极(红色) <-> 5V
 * - 负极(棕色) <-> GND
 * - 信号(黄色) <-> PA1(PWM/TIM2_CH2)
 *
 * 舵机控制原理:
 * - 舵机的控制需要一个 PWM 信号,频率一般为 50Hz (周期 20ms)
 * - 脉宽范围约为 0.5ms ~ 2.5ms。以 180° 角为例,对应的控制关系为:
 *     角度  脉宽
 *     0°    500 µs
 *     45°   700 µs
 *     90°  1500 µs
 *    135°  2000 µs
 *    180°  2500 µs
 * - 角度与脉宽的线性关系公式为:pulse = 500 + angle * 2000 / 180
 *
 * STM32CubeMX 定时器 TIM 参数配置:
 *   预分频(htim.Init.Prescaler) = SYS_CLK-1 -> 定时器时钟 1 MHz (1 µs/tick)
 *   自动重装(htim.Init.Period)  = 20000-1 -> PWM 周期 20 ms (20000 µs)
 *   htim.Init.AutoReloadPreload = Enable
 *   脉宽范围(sConfigOC.Pulse)   = 1500 -> 默认 90° (1500 µs)
 */
 
#include "stm32l4xx_hal.h"
#include "miscdev.h"
#include "servo_sg90.h"
#include "tim.h"
 
/* SG90 舵机控制连接单片机引脚: PA1/TIM2_CH2 */
#define SERVO_TIMER       &htim2
#define SERVO_CHANNEL     TIM_CHANNEL_2
 
/**
 * @brief  初始化舵机, 启动 TIM PWM 输出。
 */
void Servo_Init(void)
{
    /* 启动前先设定一个明确的初始角度,避免上电盲目抖动 */
    Servo_SetAngle(SERVO_DEFAULT_ANG);
 
    HAL_TIM_PWM_Start(SERVO_TIMER, SERVO_CHANNEL);
}
 
/**
 * @brief  设置舵机角度
 * @param  angle  可传 0 ~ 180,但超出钳位值范围自动钳位
 */
void Servo_SetAngle(int angle)
{
    int pulse;
 
    angle = CLAMP_ANGLE(angle);
 
    /* 脉宽线性公式:利用 32 位 int 计算,彻底免除溢出隐患 */
    pulse = SERVO_MIN_PULSE + (angle * (SERVO_MAX_PULSE - SERVO_MIN_PULSE)) / SERVO_MAX_ANGLE;
 
    __HAL_TIM_SET_COMPARE(SERVO_TIMER, SERVO_CHANNEL, (uint16_t)pulse);
}
 
/**
 * @brief  直接停止 PWM 输出, 立即停止舵机。
 */
void Servo_Stop(void)
{
    HAL_TIM_PWM_Stop(SERVO_TIMER, SERVO_CHANNEL);
}
 
/**
 * @brief  转到安全角度后,再停止舵机
 * @param  safe_angle  停止前舵机转到的角度 (超出钳位值范围设置默认角度)
 */
void Servo_SafeStop(int safe_angle)
{
    /* 超出钳位值范围设置默认角度 */
    if (safe_angle <= SERVO_MIN_ANGLE || safe_angle > SERVO_MAX_ANGLE) {
        safe_angle = SERVO_DEFAULT_ANG;
    }
 
    Servo_SetAngle(safe_angle);
 
    /* SG90 空载约 100 ms/60°,转 180° 约需 300ms */
    mdelay(1000);
 
    Servo_Stop();
}
 
/**
 * @brief 舵机测试函数
 */
void Servo_Test(void)
{
    printf("Servo Start\r\n");
    Servo_Init();
    mdelay(2000);
 
    Servo_SetAngle(0);
    mdelay(2000);
 
    Servo_SetAngle(90);
    mdelay(2000);
 
    Servo_SetAngle(180);
    mdelay(2000);
 
    Servo_SetAngle(90);
    mdelay(2000);
 
    Servo_SetAngle(0);
    mdelay(2000);
 
    printf("Servo Stop\r\n");
    Servo_SafeStop(SERVO_DEFAULT_ANG);
}