基于STM32的舵机控制系统(PWM控制)

一、舵机控制原理

1.1 舵机工作原理

  • 控制信号:周期20ms(50Hz)的PWM方波
  • 脉冲宽度:0.5ms2.5ms对应角度0°180°
  • 角度计算公式脉冲宽度(ms) = 0.5 + 角度/180 * 2.0

1.2 角度与占空比关系

角度 脉宽(ms) 占空比(20ms周期) 对应计数值(720MHz)
0.5ms 2.5% 1800
90° 1.5ms 7.5% 5400
180° 2.5ms 12.5% 9000

二、硬件连接(以STM32F103C8T6为例)

2.1 引脚连接

舵机线 STM32引脚 备注
信号线(橙色) PA0/PA6/PB6/PB8 任意定时器PWM输出
电源线(红色) 5V电源 外部5V供电
地线(棕色) GND 与STM32共地

2.2 供电方案

// 注意:舵机需独立供电,避免电流过大损坏MCU
// 推荐方案:
// 1. 外部5V电源供电
// 2. 通过LDO(如AMS1117-5V)转换
// 3. 每个舵机并联100uF电容滤波

三、PWM配置(通用定时器)

3.1 定时器选择

// STM32F103C8T6定时器资源
// TIM1/2/3/4 支持PWM输出
// 推荐使用高级定时器(TIM1)或通用定时器(TIM2-4)

// 常用PWM引脚映射
// TIM1_CH1: PA8
// TIM1_CH2: PA9
// TIM1_CH3: PA10
// TIM1_CH4: PA11
// TIM2_CH1: PA0/PA5/PA15
// TIM2_CH2: PA1/PB3
// TIM3_CH1: PA6/PB4/PC6
// TIM3_CH2: PA7/PB5/PC7
// TIM4_CH1: PB6
// TIM4_CH2: PB7
// TIM4_CH3: PB8
// TIM4_CH4: PB9

3.2 PWM频率计算

// 系统时钟:72MHz
// 定时器分频:7200
// 计数频率:72MHz / 7200 = 10kHz
// 计数周期:10kHz = 0.1ms/计数
// 20ms周期需要计数:20ms / 0.1ms = 200
// 自动重装载值:200-1 = 199
// 0.5ms脉宽计数:0.5ms / 0.1ms = 5
// 2.5ms脉宽计数:2.5ms / 0.1ms = 25

四、标准库实现

4.1 头文件定义

// servo.h
#ifndef __SERVO_H
#define __SERVO_H

#include "stm32f10x.h"

// 舵机结构体定义
typedef struct {
    TIM_TypeDef* TIMx;       // 定时器
    uint16_t TIM_Channel;    // 定时器通道
    float current_angle;     // 当前角度
    float target_angle;      // 目标角度
    float speed;            // 旋转速度(度/秒)
    uint8_t is_moving;      // 是否在运动
} Servo_TypeDef;

// 舵机初始化结构体
typedef struct {
    TIM_TypeDef* TIMx;       // 定时器
    uint16_t TIM_Channel;    // 定时器通道
    uint16_t min_pulse;      // 最小脉宽(计数)
    uint16_t max_pulse;      // 最大脉宽(计数)
    uint16_t init_angle;     // 初始角度
} Servo_InitTypeDef;

// 函数声明
void Servo_Init(Servo_TypeDef* servo, Servo_InitTypeDef* init);
void Servo_SetAngle(Servo_TypeDef* servo, float angle);
void Servo_SetAngleImmediate(Servo_TypeDef* servo, float angle);
void Servo_SetSpeed(Servo_TypeDef* servo, float speed);
void Servo_Update(Servo_TypeDef* servo);  // 更新运动
float Servo_GetAngle(Servo_TypeDef* servo);
uint8_t Servo_IsMoving(Servo_TypeDef* servo);

#endif

4.2 PWM初始化

// servo.c
#include "servo.h"
#include "delay.h"
#include "math.h"

// 全局变量
static uint16_t timer_arr = 199;  // 自动重装载值(20ms)
static uint16_t timer_psc = 7199; // 预分频系数(10kHz)

// 初始化舵机
void Servo_Init(Servo_TypeDef* servo, Servo_InitTypeDef* init)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_OCInitTypeDef TIM_OCInitStructure;
    
    // 1. 时钟使能
    if (init->TIMx == TIM1) {
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
    } else if (init->TIMx == TIM2) {
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    } else if (init->TIMx == TIM3) {
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
    } else if (init->TIMx == TIM4) {
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
    }
    
    // 2. GPIO初始化
    uint16_t pin = 0;
    GPIO_TypeDef* port = NULL;
    
    // 根据定时器和通道选择引脚
    if (init->TIMx == TIM2) {
        if (init->TIM_Channel == TIM_Channel_1) {
            port = GPIOA;
            pin = GPIO_Pin_0;
            RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
        } else if (init->TIM_Channel == TIM_Channel_2) {
            port = GPIOA;
            pin = GPIO_Pin_1;
            RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
        }
    } else if (init->TIMx == TIM3) {
        if (init->TIM_Channel == TIM_Channel_1) {
            port = GPIOA;
            pin = GPIO_Pin_6;
            RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
        } else if (init->TIM_Channel == TIM_Channel_2) {
            port = GPIOA;
            pin = GPIO_Pin_7;
            RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
        }
    } else if (init->TIMx == TIM4) {
        if (init->TIM_Channel == TIM_Channel_1) {
            port = GPIOB;
            pin = GPIO_Pin_6;
            RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
        } else if (init->TIM_Channel == TIM_Channel_2) {
            port = GPIOB;
            pin = GPIO_Pin_7;
            RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
        }
    }
    
    if (port != NULL) {
        GPIO_InitStructure.GPIO_Pin = pin;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  // 复用推挽输出
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(port, &GPIO_InitStructure);
    }
    
    // 3. 定时器时基初始化
    TIM_TimeBaseStructure.TIM_Period = timer_arr;         // 自动重装载值
    TIM_TimeBaseStructure.TIM_Prescaler = timer_psc;      // 预分频系数
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;          // 时钟分割
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  // 向上计数
    TIM_TimeBaseInit(init->TIMx, &TIM_TimeBaseStructure);
    
    // 4. PWM输出初始化
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;     // PWM模式1
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;  // 使能输出
    TIM_OCInitStructure.TIM_Pulse = 0;                    // 初始脉冲宽度
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;  // 输出极性高
    
    // 初始化对应通道
    switch(init->TIM_Channel) {
        case TIM_Channel_1:
            TIM_OC1Init(init->TIMx, &TIM_OCInitStructure);
            TIM_OC1PreloadConfig(init->TIMx, TIM_OCPreload_Enable);
            break;
        case TIM_Channel_2:
            TIM_OC2Init(init->TIMx, &TIM_OCInitStructure);
            TIM_OC2PreloadConfig(init->TIMx, TIM_OCPreload_Enable);
            break;
        case TIM_Channel_3:
            TIM_OC3Init(init->TIMx, &TIM_OCInitStructure);
            TIM_OC3PreloadConfig(init->TIMx, TIM_OCPreload_Enable);
            break;
        case TIM_Channel_4:
            TIM_OC4Init(init->TIMx, &TIM_OCInitStructure);
            TIM_OC4PreloadConfig(init->TIMx, TIM_OCPreload_Enable);
            break;
    }
    
    // 5. 使能定时器
    TIM_Cmd(init->TIMx, ENABLE);
    
    // 6. 如果是高级定时器,需要使能主输出
    if (init->TIMx == TIM1) {
        TIM_CtrlPWMOutputs(TIM1, ENABLE);
    }
    
    // 7. 初始化舵机结构体
    servo->TIMx = init->TIMx;
    servo->TIM_Channel = init->TIM_Channel;
    servo->current_angle = init->init_angle;
    servo->target_angle = init->init_angle;
    servo->speed = 60.0f;  // 默认60度/秒
    servo->is_moving = 0;
    
    // 8. 设置初始角度
    Servo_SetAngleImmediate(servo, init->init_angle);
}

4.3 设置舵机角度

// 设置舵机角度(平滑运动)
void Servo_SetAngle(Servo_TypeDef* servo, float angle)
{
    // 限制角度范围
    if (angle < 0) angle = 0;
    if (angle > 180) angle = 180;
    
    servo->target_angle = angle;
    servo->is_moving = 1;
}

// 立即设置舵机角度
void Servo_SetAngleImmediate(Servo_TypeDef* servo, float angle)
{
    // 限制角度范围
    if (angle < 0) angle = 0;
    if (angle > 180) angle = 180;
    
    // 计算脉冲宽度(计数)
    // 0.5ms = 5计数, 2.5ms = 25计数
    // 角度0-180对应计数5-25
    uint16_t pulse = 5 + (uint16_t)(angle * 20.0f / 180.0f);
    
    // 设置比较寄存器
    switch(servo->TIM_Channel) {
        case TIM_Channel_1:
            TIM_SetCompare1(servo->TIMx, pulse);
            break;
        case TIM_Channel_2:
            TIM_SetCompare2(servo->TIMx, pulse);
            break;
        case TIM_Channel_3:
            TIM_SetCompare3(servo->TIMx, pulse);
            break;
        case TIM_Channel_4:
            TIM_SetCompare4(servo->TIMx, pulse);
            break;
    }
    
    servo->current_angle = angle;
    servo->target_angle = angle;
    servo->is_moving = 0;
}

4.4 平滑运动控制

// 更新舵机位置(需周期性调用)
void Servo_Update(Servo_TypeDef* servo)
{
    static uint32_t last_time = 0;
    uint32_t current_time = GetTickCount();
    uint32_t delta_time = current_time - last_time;
    
    if (delta_time < 10)  // 10ms更新一次
        return;
    
    if (servo->is_moving) {
        float angle_diff = servo->target_angle - servo->current_angle;
        
        if (fabs(angle_diff) < 0.1f) {
            // 到达目标位置
            servo->current_angle = servo->target_angle;
            servo->is_moving = 0;
        } else {
            // 计算移动步长
            float step = (servo->speed * delta_time) / 1000.0f;  // 度/毫秒
            
            if (fabs(angle_diff) < step) {
                servo->current_angle = servo->target_angle;
            } else {
                servo->current_angle += (angle_diff > 0 ? step : -step);
            }
            
            // 更新PWM
            Servo_SetAngleImmediate(servo, servo->current_angle);
        }
    }
    
    last_time = current_time;
}

五、HAL库实现

5.1 使用CubeMX配置

// 配置步骤:
// 1. 开启对应定时器
// 2. 选择Channel为PWM Generation CHx
// 3. 参数配置:
//    Prescaler: 7199
//    Counter Period: 199
//    Pulse: 初始脉宽(如15对应1.5ms)
// 4. 生成代码

5.2 HAL库初始化

// servo_hal.h
#ifndef __SERVO_HAL_H
#define __SERVO_HAL_H

#include "main.h"
#include "tim.h"

typedef struct {
    TIM_HandleTypeDef* htim;   // 定时器句柄
    uint32_t channel;          // 定时器通道
    float current_angle;       // 当前角度
    float target_angle;        // 目标角度
    float speed;              // 旋转速度
    uint8_t is_moving;        // 是否在运动
} Servo_HAL_TypeDef;

// 函数声明
void Servo_HAL_Init(Servo_HAL_TypeDef* servo, TIM_HandleTypeDef* htim, uint32_t channel);
void Servo_HAL_SetAngle(Servo_HAL_TypeDef* servo, float angle);
void Servo_HAL_SetAngleImmediate(Servo_HAL_TypeDef* servo, float angle);
void Servo_HAL_Update(Servo_HAL_TypeDef* servo);

#endif

5.3 HAL库实现

// servo_hal.c
#include "servo_hal.h"
#include "math.h"

// 初始化舵机
void Servo_HAL_Init(Servo_HAL_TypeDef* servo, TIM_HandleTypeDef* htim, uint32_t channel)
{
    servo->htim = htim;
    servo->channel = channel;
    servo->current_angle = 90.0f;  // 默认90度
    servo->target_angle = 90.0f;
    servo->speed = 60.0f;         // 默认60度/秒
    servo->is_moving = 0;
    
    // 启动PWM
    HAL_TIM_PWM_Start(servo->htim, servo->channel);
    
    // 设置初始角度
    Servo_HAL_SetAngleImmediate(servo, 90.0f);
}

// 设置舵机角度
void Servo_HAL_SetAngle(Servo_HAL_TypeDef* servo, float angle)
{
    if (angle < 0) angle = 0;
    if (angle > 180) angle = 180;
    
    servo->target_angle = angle;
    servo->is_moving = 1;
}

// 立即设置舵机角度
void Servo_HAL_SetAngleImmediate(Servo_HAL_TypeDef* servo, float angle)
{
    if (angle < 0) angle = 0;
    if (angle > 180) angle = 180;
    
    // 计算脉宽(计数)
    uint16_t pulse = 5 + (uint16_t)(angle * 20.0f / 180.0f);
    
    // 设置比较寄存器
    __HAL_TIM_SET_COMPARE(servo->htim, servo->channel, pulse);
    
    servo->current_angle = angle;
    servo->target_angle = angle;
    servo->is_moving = 0;
}

六、多舵机控制

6.1 舵机控制数组

// multi_servo.c
#include "multi_servo.h"

#define MAX_SERVO_NUM 8

Servo_TypeDef servos[MAX_SERVO_NUM];
uint8_t servo_count = 0;

// 添加舵机
uint8_t Servo_Add(Servo_InitTypeDef* init)
{
    if (servo_count >= MAX_SERVO_NUM)
        return 0;
    
    Servo_Init(&servos[servo_count], init);
    servo_count++;
    
    return 1;
}

// 同时设置多个舵机角度
void Servo_SetMultipleAngles(uint8_t servo_indices[], float angles[], uint8_t count)
{
    for (int i = 0; i < count; i++) {
        if (servo_indices[i] < servo_count) {
            Servo_SetAngle(&servos[servo_indices[i]], angles[i]);
        }
    }
}

// 更新所有舵机
void Servo_UpdateAll(void)
{
    for (int i = 0; i < servo_count; i++) {
        Servo_Update(&servos[i]);
    }
}

6.2 舵机同步运动

// 舵机轨迹规划
typedef struct {
    float start_angle;
    float end_angle;
    float duration;  // 运动时间(秒)
    uint32_t start_time;
    uint8_t is_running;
} Servo_Trajectory;

void Servo_StartTrajectory(Servo_TypeDef* servo, float end_angle, float duration)
{
    static Servo_Trajectory traj;
    
    traj.start_angle = servo->current_angle;
    traj.end_angle = end_angle;
    traj.duration = duration;
    traj.start_time = HAL_GetTick();
    traj.is_running = 1;
    
    // 计算所需速度
    servo->speed = fabs(end_angle - servo->current_angle) / duration;
    servo->target_angle = end_angle;
    servo->is_moving = 1;
}

七、应用示例

7.1 机械臂控制

// robot_arm.c
#include "servo.h"
#include "math.h"

// 定义机械臂舵机
Servo_TypeDef base_servo;    // 底座
Servo_TypeDef shoulder_servo; // 肩部
Servo_TypeDef elbow_servo;    // 肘部
Servo_TypeDef wrist_servo;    // 腕部
Servo_TypeDef gripper_servo;  // 夹爪

// 初始化机械臂
void RobotArm_Init(void)
{
    Servo_InitTypeDef init;
    
    // 底座舵机 (TIM2_CH1 PA0)
    init.TIMx = TIM2;
    init.TIM_Channel = TIM_Channel_1;
    init.init_angle = 90;
    Servo_Init(&base_servo, &init);
    
    // 肩部舵机 (TIM2_CH2 PA1)
    init.TIMx = TIM2;
    init.TIM_Channel = TIM_Channel_2;
    init.init_angle = 45;
    Servo_Init(&shoulder_servo, &init);
    
    // 肘部舵机 (TIM3_CH1 PA6)
    init.TIMx = TIM3;
    init.TIM_Channel = TIM_Channel_1;
    init.init_angle = 90;
    Servo_Init(&elbow_servo, &init);
    
    // 腕部舵机 (TIM3_CH2 PA7)
    init.TIMx = TIM3;
    init.TIM_Channel = TIM_Channel_2;
    init.init_angle = 0;
    Servo_Init(&wrist_servo, &init);
    
    // 夹爪舵机 (TIM4_CH1 PB6)
    init.TIMx = TIM4;
    init.TIM_Channel = TIM_Channel_1;
    init.init_angle = 0;
    Servo_Init(&gripper_servo, &init);
}

// 控制夹爪
void RobotArm_Gripper(uint8_t state, float speed)
{
    if (state) {
        Servo_SetSpeed(&gripper_servo, speed);
        Servo_SetAngle(&gripper_servo, 30);  // 闭合
    } else {
        Servo_SetSpeed(&gripper_servo, speed);
        Servo_SetAngle(&gripper_servo, 0);   // 打开
    }
}

// 移动到指定位置
void RobotArm_MoveTo(float base_angle, float shoulder_angle, float elbow_angle, float wrist_angle)
{
    Servo_SetAngle(&base_servo, base_angle);
    Servo_SetAngle(&shoulder_servo, shoulder_angle);
    Servo_SetAngle(&elbow_servo, elbow_angle);
    Servo_SetAngle(&wrist_servo, wrist_angle);
}

// 更新机械臂
void RobotArm_Update(void)
{
    Servo_Update(&base_servo);
    Servo_Update(&shoulder_servo);
    Servo_Update(&elbow_servo);
    Servo_Update(&wrist_servo);
    Servo_Update(&gripper_servo);
}

7.2 云台控制

// gimbal.c
#include "servo.h"
#include "mpu6050.h"

Servo_TypeDef pitch_servo;  // 俯仰舵机
Servo_TypeDef yaw_servo;    // 偏航舵机

// PID控制器
typedef struct {
    float Kp, Ki, Kd;
    float error, last_error, integral;
    float output;
} PID_Controller;

PID_Controller pitch_pid, yaw_pid;

// 云台初始化
void Gimbal_Init(void)
{
    Servo_InitTypeDef init;
    
    // 俯仰舵机
    init.TIMx = TIM2;
    init.TIM_Channel = TIM_Channel_1;
    init.init_angle = 90;
    Servo_Init(&pitch_servo, &init);
    
    // 偏航舵机
    init.TIMx = TIM2;
    init.TIM_Channel = TIM_Channel_2;
    init.init_angle = 90;
    Servo_Init(&yaw_servo, &init);
    
    // PID参数
    pitch_pid.Kp = 1.0f;
    pitch_pid.Ki = 0.01f;
    pitch_pid.Kd = 0.1f;
    
    yaw_pid.Kp = 1.0f;
    yaw_pid.Ki = 0.01f;
    yaw_pid.Kd = 0.1f;
}

// 稳定云台
void Gimbal_Stabilize(float target_pitch, float target_yaw)
{
    float pitch, yaw;
    
    // 获取IMU数据
    MPU6050_GetAngle(&pitch, &yaw);
    
    // 计算PID
    float pitch_error = target_pitch - pitch;
    float yaw_error = target_yaw - yaw;
    
    // 俯仰控制
    pitch_pid.integral += pitch_error;
    float pitch_output = pitch_pid.Kp * pitch_error + 
                         pitch_pid.Ki * pitch_pid.integral + 
                         pitch_pid.Kd * (pitch_error - pitch_pid.last_error);
    
    // 偏航控制
    yaw_pid.integral += yaw_error;
    float yaw_output = yaw_pid.Kp * yaw_error + 
                       yaw_pid.Ki * yaw_pid.integral + 
                       yaw_pid.Kd * (yaw_error - yaw_pid.last_error);
    
    // 限制输出
    pitch_output = (pitch_output < -45) ? -45 : (pitch_output > 45) ? 45 : pitch_output;
    yaw_output = (yaw_output < -45) ? -45 : (yaw_output > 45) ? 45 : yaw_output;
    
    // 更新舵机
    Servo_SetAngleImmediate(&pitch_servo, 90 + pitch_output);
    Servo_SetAngleImmediate(&yaw_servo, 90 + yaw_output);
    
    pitch_pid.last_error = pitch_error;
    yaw_pid.last_error = yaw_error;
}

八、高级功能

8.1 舵机校准

// servo_calibration.c
#include "servo.h"

// 舵机校准参数
typedef struct {
    uint16_t pulse_0;     // 0度对应的脉冲宽度
    uint16_t pulse_180;   // 180度对应的脉冲宽度
    float offset;        // 角度偏移
} Servo_Calibration;

// 校准舵机
void Servo_Calibrate(Servo_TypeDef* servo, Servo_Calibration* cal)
{
    // 记录0度和180度时的实际脉冲宽度
    // 这需要手动测量
}

// 使用校准参数设置角度
void Servo_SetAngleCalibrated(Servo_TypeDef* servo, float angle, Servo_Calibration* cal)
{
    // 计算线性映射
    float pulse_range = cal->pulse_180 - cal->pulse_0;
    uint16_t pulse = cal->pulse_0 + (uint16_t)(angle * pulse_range / 180.0f);
    
    // 设置比较寄存器
    switch(servo->TIM_Channel) {
        case TIM_Channel_1:
            TIM_SetCompare1(servo->TIMx, pulse);
            break;
        // ... 其他通道
    }
}

8.2 舵机保护

// servo_protection.c
#include "servo.h"

// 舵机限位保护
void Servo_SetAngleWithLimit(Servo_TypeDef* servo, float angle, float min_limit, float max_limit)
{
    if (angle < min_limit) angle = min_limit;
    if (angle > max_limit) angle = max_limit;
    
    Servo_SetAngle(servo, angle);
}

// 舵机电流检测(需要硬件支持)
uint8_t Servo_CheckOverload(Servo_TypeDef* servo)
{
    // 通过ADC检测电流
    // 如果电流超过阈值,停止舵机
    
    return 0;  // 0表示正常
}

// 舵机温度保护
void Servo_ThermalProtection(Servo_TypeDef* servo)
{
    // 检测温度,过热时降低PWM占空比或停止
}

参考代码 基于stm32舵机控制 www.youwenfan.com/contentcnv/103078.html

九、调试与测试

9.1 测试程序

// test_servo.c
#include "servo.h"
#include "delay.h"

void Test_Servo_Sweep(Servo_TypeDef* servo)
{
    printf("Servo Sweep Test\n");
    
    // 从0度到180度扫描
    for (float angle = 0; angle <= 180; angle += 1) {
        Servo_SetAngleImmediate(servo, angle);
        printf("Angle: %.1f\n", angle);
        Delay_ms(20);
    }
    
    // 从180度到0度扫描
    for (float angle = 180; angle >= 0; angle -= 1) {
        Servo_SetAngleImmediate(servo, angle);
        printf("Angle: %.1f\n", angle);
        Delay_ms(20);
    }
}

void Test_Servo_Precision(Servo_TypeDef* servo)
{
    printf("Servo Precision Test\n");
    
    // 测试几个关键角度
    float test_angles[] = {0, 45, 90, 135, 180};
    
    for (int i = 0; i < 5; i++) {
        Servo_SetAngleImmediate(servo, test_angles[i]);
        printf("Set to: %.0f\n", test_angles[i]);
        Delay_ms(1000);
    }
}

9.2 串口控制

// uart_control.c
#include "servo.h"
#include "usart.h"

void UART_ControlServo(Servo_TypeDef* servo)
{
    char buffer[32];
    
    if (UART_Available()) {
        fgets(buffer, sizeof(buffer), stdin);
        
        if (strncmp(buffer, "set ", 4) == 0) {
            float angle = atof(buffer + 4);
            Servo_SetAngle(servo, angle);
            printf("Set angle to: %.1f\n", angle);
        } else if (strncmp(buffer, "get", 3) == 0) {
            printf("Current angle: %.1f\n", servo->current_angle);
        } else if (strncmp(buffer, "sweep", 5) == 0) {
            Test_Servo_Sweep(servo);
        }
    }
}

十、性能优化

10.1 使用DMA更新多个舵机

// servo_dma.c
#include "servo.h"

// DMA传输PWM数据
void Servo_DMA_Update(Servo_TypeDef servos[], uint8_t count, uint16_t pulses[])
{
    // 配置DMA传输多个PWM比较值
    // 可以减少CPU中断开销
}

10.2 定时器同步

// 多个定时器同步启动
void Servo_Timer_Sync(void)
{
    // 使用一个定时器作为主定时器
    // 其他定时器从模式
}

十一、常见问题解决

问题 可能原因 解决方案
舵机不转动 1. 电源不足
2. 信号线接错
3. PWM频率不对
1. 检查5V供电
2. 检查信号线连接
3. 检查PWM周期是否为20ms
舵机抖动 1. 电源纹波大
2. 信号不稳定
3. 机械阻力大
1. 增加滤波电容
2. 检查信号线长度
3. 检查机械结构
角度不准 1. 舵机精度问题
2. 机械间隙
3. 供电电压低
1. 进行校准
2. 调整机械结构
3. 确保5V供电
响应慢 1. 速度设置过慢
2. 更新频率低
3. 舵机扭矩不足
1. 增加speed参数
2. 提高Servo_Update调用频率
3. 检查负载是否过重

十二、完整工程示例

12.1 主程序

// main.c
#include "stm32f10x.h"
#include "servo.h"
#include "delay.h"
#include "usart.h"

Servo_TypeDef my_servo;

int main(void)
{
    // 系统初始化
    SystemInit();
    Delay_Init();
    UART_Init(115200);
    
    printf("Servo Control System Start\n");
    
    // 舵机初始化
    Servo_InitTypeDef servo_init;
    servo_init.TIMx = TIM2;
    servo_init.TIM_Channel = TIM_Channel_1;
    servo_init.init_angle = 90;
    
    Servo_Init(&my_servo, &servo_init);
    
    // 设置运动速度
    Servo_SetSpeed(&my_servo, 90.0f);  // 90度/秒
    
    while (1) {
        // 测试序列
        printf("Moving to 0 degrees\n");
        Servo_SetAngle(&my_servo, 0);
        
        while (Servo_IsMoving(&my_servo)) {
            Servo_Update(&my_servo);
            Delay_ms(10);
        }
        
        Delay_ms(1000);
        
        printf("Moving to 180 degrees\n");
        Servo_SetAngle(&my_servo, 180);
        
        while (Servo_IsMoving(&my_servo)) {
            Servo_Update(&my_servo);
            Delay_ms(10);
        }
        
        Delay_ms(1000);
    }
}
posted @ 2026-06-03 16:48  hczyydqq  阅读(31)  评论(0)    收藏  举报