STM32 舵机控制程序(基于标准外设库)

STM32 舵机控制程序,支持角度控制、速度控制、多舵机同步、按键控制等功能,适用于机器人、机械臂、云台等应用场景。

一、系统架构

复制代码
STM32 舵机控制系统架构:
├── 硬件层
│   ├── STM32F103C8T6/CBT6 微控制器
│   ├── SG90/MG996R 舵机 × 4
│   ├── 按键 × 4
│   ├── OLED 显示屏(可选)
│   └── 电源模块(5V/2A)
├── 驱动层
│   ├── TIM PWM 输出
│   ├── GPIO 按键扫描
│   ├── 延时函数
│   └── 中断管理
├── 控制层
│   ├── 舵机角度控制
│   ├── 舵机速度控制
│   ├── 多舵机同步
│   ├── 轨迹规划
│   └── 限位保护
└── 应用层
    ├── 手动控制模式
    ├── 自动控制模式
    ├── 预设动作序列
    └── 示教再现

二、硬件连接

2.1 舵机接口连接

复制代码
STM32F103    SG90/MG996R    功能说明
PA6 (TIM3_CH1) → 舵机1信号线  舵机1控制
PA7 (TIM3_CH2) → 舵机2信号线  舵机2控制
PB0 (TIM3_CH3) → 舵机3信号线  舵机3控制
PB1 (TIM3_CH4) → 舵机4信号线  舵机4控制
5V          → 舵机电源正极   舵机供电(需外接5V/2A电源)
GND         → 舵机电源负极   共地

2.2 按键接口连接

复制代码
STM32F103    按键    功能说明
PB12        → 按键1  舵机1角度增加
PB13        → 按键2  舵机1角度减少
PB14        → 按键3  舵机2角度增加
PB15        → 按键4  舵机2角度减少

三、核心代码实现

3.1 头文件定义 (servo_control.h)

c 复制代码
#ifndef SERVO_CONTROL_H
#define SERVO_CONTROL_H

#include "stm32f10x.h"
#include <stdint.h>

// 舵机配置参数
#define SERVO_NUM          4           // 舵机数量
#define SERVO_MIN_ANGLE    0           // 最小角度
#define SERVO_MAX_ANGLE    180         // 最大角度
#define SERVO_MIN_PULSE    500         // 最小脉冲宽度(us) - 0.5ms
#define SERVO_MAX_PULSE    2500        // 最大脉冲宽度(us) - 2.5ms
#define SERVO_PERIOD       20000       // PWM周期(us) - 20ms

// 舵机速度参数
#define SERVO_SPEED_MIN    1           // 最小速度(度/周期)
#define SERVO_SPEED_MAX    10          // 最大速度
#define SERVO_SPEED_DEFAULT 3          // 默认速度

// 舵机ID定义
typedef enum {
    SERVO_1 = 0,
    SERVO_2 = 1,
    SERVO_3 = 2,
    SERVO_4 = 3
} ServoID;

// 舵机状态结构
typedef struct {
    uint8_t id;                // 舵机ID
    uint16_t current_angle;    // 当前角度
    uint16_t target_angle;     // 目标角度
    uint16_t speed;            // 转动速度
    uint8_t enabled;           // 使能标志
    uint8_t moving;            // 正在移动标志
    uint16_t pulse_width;      // 当前脉冲宽度
    uint32_t last_update;      // 最后更新时间
} ServoStatus;

// 系统状态结构
typedef struct {
    uint8_t selected_servo;    // 当前选中的舵机
    uint8_t control_mode;      // 控制模式(0:手动,1:自动)
    uint8_t emergency_stop;    // 紧急停止标志
    uint32_t system_time;      // 系统时间
} SystemStatus;

// 函数声明
void Servo_Init(void);
void Servo_SetAngle(ServoID servo_id, uint16_t angle);
void Servo_SetPulseWidth(ServoID servo_id, uint16_t pulse_us);
void Servo_SetSpeed(ServoID servo_id, uint16_t speed);
void Servo_UpdateAll(void);
void Servo_EmergencyStop(void);
void Servo_MoveToPosition(uint16_t angles[SERVO_NUM]);
void Servo_ExecuteSequence(uint16_t sequence[][SERVO_NUM], uint8_t seq_len, uint16_t delay_ms);

// 按键处理函数
void Key_Scan(void);
void Key_Process(void);

// 工具函数
uint16_t AngleToPulse(uint16_t angle);
uint16_t PulseToAngle(uint16_t pulse);

#endif // SERVO_CONTROL_H

3.2 舵机驱动实现 (servo_control.c)

c 复制代码
#include "servo_control.h"
#include "delay.h"

// 全局变量
ServoStatus g_servos[SERVO_NUM];
SystemStatus g_system;

// 舵机PWM引脚配置
typedef struct {
    GPIO_TypeDef* gpio_port;
    uint16_t gpio_pin;
    uint8_t tim_channel;
} ServoPinConfig;

static ServoPinConfig servo_pins[SERVO_NUM] = {
    {GPIOA, GPIO_Pin_6, 1},   // PA6 - TIM3_CH1
    {GPIOA, GPIO_Pin_7, 2},   // PA7 - TIM3_CH2
    {GPIOB, GPIO_Pin_0, 3},   // PB0 - TIM3_CH3
    {GPIOB, GPIO_Pin_1, 4}    // PB1 - TIM3_CH4
};

// 初始化舵机
void Servo_Init(void)
{
    uint8_t i;
    
    // 初始化系统状态
    g_system.selected_servo = SERVO_1;
    g_system.control_mode = 0;
    g_system.emergency_stop = 0;
    g_system.system_time = 0;
    
    // 初始化舵机状态
    for(i = 0; i < SERVO_NUM; i++)
    {
        g_servos[i].id = i;
        g_servos[i].current_angle = 90;      // 默认90度
        g_servos[i].target_angle = 90;
        g_servos[i].speed = SERVO_SPEED_DEFAULT;
        g_servos[i].enabled = 1;
        g_servos[i].moving = 0;
        g_servos[i].pulse_width = AngleToPulse(90);
        g_servos[i].last_update = 0;
    }
    
    // 初始化GPIO
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);
    
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    
    // 配置舵机PWM引脚
    for(i = 0; i < SERVO_NUM; i++)
    {
        GPIO_InitStructure.GPIO_Pin = servo_pins[i].gpio_pin;
        GPIO_Init(servo_pins[i].gpio_port, &GPIO_InitStructure);
    }
    
    // 初始化定时器TIM3
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_OCInitTypeDef TIM_OCInitStructure;
    
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
    
    // 定时器基础配置
    TIM_TimeBaseStructure.TIM_Period = SERVO_PERIOD - 1;        // 20ms周期
    TIM_TimeBaseStructure.TIM_Prescaler = 72 - 1;              // 72MHz/72 = 1MHz,1us计数
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
    
    // PWM模式配置
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    TIM_OCInitStructure.TIM_Pulse = 0;
    
    // 配置4个通道
    TIM_OC1Init(TIM3, &TIM_OCInitStructure);
    TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);
    
    TIM_OC2Init(TIM3, &TIM_OCInitStructure);
    TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);
    
    TIM_OC3Init(TIM3, &TIM_OCInitStructure);
    TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);
    
    TIM_OC4Init(TIM3, &TIM_OCInitStructure);
    TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);
    
    TIM_ARRPreloadConfig(TIM3, ENABLE);
    TIM_Cmd(TIM3, ENABLE);
    
    // 初始化所有舵机到90度
    for(i = 0; i < SERVO_NUM; i++)
    {
        Servo_SetAngle((ServoID)i, 90);
    }
    
    Delay_ms(1000);  // 等待舵机稳定
}

// 角度转脉冲宽度
uint16_t AngleToPulse(uint16_t angle)
{
    // 限制角度范围
    if(angle < SERVO_MIN_ANGLE) angle = SERVO_MIN_ANGLE;
    if(angle > SERVO_MAX_ANGLE) angle = SERVO_MAX_ANGLE;
    
    // 线性映射:0° -> 500us, 180° -> 2500us
    return SERVO_MIN_PULSE + (angle * (SERVO_MAX_PULSE - SERVO_MIN_PULSE)) / SERVO_MAX_ANGLE;
}

// 脉冲宽度转角度
uint16_t PulseToAngle(uint16_t pulse)
{
    // 限制脉冲范围
    if(pulse < SERVO_MIN_PULSE) pulse = SERVO_MIN_PULSE;
    if(pulse > SERVO_MAX_PULSE) pulse = SERVO_MAX_PULSE;
    
    // 线性映射:500us -> 0°, 2500us -> 180°
    return ((pulse - SERVO_MIN_PULSE) * SERVO_MAX_ANGLE) / (SERVO_MAX_PULSE - SERVO_MIN_PULSE);
}

// 设置舵机角度
void Servo_SetAngle(ServoID servo_id, uint16_t angle)
{
    if(servo_id >= SERVO_NUM || g_system.emergency_stop) return;
    
    // 限制角度范围
    if(angle < SERVO_MIN_ANGLE) angle = SERVO_MIN_ANGLE;
    if(angle > SERVO_MAX_ANGLE) angle = SERVO_MAX_ANGLE;
    
    g_servos[servo_id].target_angle = angle;
    g_servos[servo_id].moving = 1;
}

// 设置舵机脉冲宽度
void Servo_SetPulseWidth(ServoID servo_id, uint16_t pulse_us)
{
    if(servo_id >= SERVO_NUM) return;
    
    uint16_t pulse = pulse_us;
    if(pulse < SERVO_MIN_PULSE) pulse = SERVO_MIN_PULSE;
    if(pulse > SERVO_MAX_PULSE) pulse = SERVO_MAX_PULSE;
    
    g_servos[servo_id].pulse_width = pulse;
    
    // 更新PWM输出
    switch(servo_pins[servo_id].tim_channel)
    {
        case 1: TIM_SetCompare1(TIM3, pulse); break;
        case 2: TIM_SetCompare2(TIM3, pulse); break;
        case 3: TIM_SetCompare3(TIM3, pulse); break;
        case 4: TIM_SetCompare4(TIM3, pulse); break;
    }
}

// 设置舵机速度
void Servo_SetSpeed(ServoID servo_id, uint16_t speed)
{
    if(servo_id >= SERVO_NUM) return;
    
    if(speed < SERVO_SPEED_MIN) speed = SERVO_SPEED_MIN;
    if(speed > SERVO_SPEED_MAX) speed = SERVO_SPEED_MAX;
    
    g_servos[servo_id].speed = speed;
}

// 更新所有舵机
void Servo_UpdateAll(void)
{
    uint8_t i;
    uint32_t current_time = g_system.system_time;
    
    for(i = 0; i < SERVO_NUM; i++)
    {
        if(!g_servos[i].enabled || !g_servos[i].moving) continue;
        
        // 检查是否需要更新(根据速度控制更新频率)
        if(current_time - g_servos[i].last_update < 20) continue;  // 每20ms更新一次
        
        g_servos[i].last_update = current_time;
        
        // 计算新的角度
        int16_t diff = g_servos[i].target_angle - g_servos[i].current_angle;
        
        if(abs(diff) <= g_servos[i].speed)
        {
            // 到达目标角度
            g_servos[i].current_angle = g_servos[i].target_angle;
            g_servos[i].moving = 0;
        }
        else
        {
            // 逐步接近目标角度
            if(diff > 0)
                g_servos[i].current_angle += g_servos[i].speed;
            else
                g_servos[i].current_angle -= g_servos[i].speed;
        }
        
        // 设置PWM输出
        uint16_t pulse = AngleToPulse(g_servos[i].current_angle);
        Servo_SetPulseWidth((ServoID)i, pulse);
    }
}

// 紧急停止
void Servo_EmergencyStop(void)
{
    uint8_t i;
    
    g_system.emergency_stop = 1;
    
    for(i = 0; i < SERVO_NUM; i++)
    {
        g_servos[i].moving = 0;
        Servo_SetPulseWidth((ServoID)i, 0);  // 停止PWM输出
    }
}

// 移动到指定位置
void Servo_MoveToPosition(uint16_t angles[SERVO_NUM])
{
    uint8_t i;
    
    for(i = 0; i < SERVO_NUM; i++)
    {
        Servo_SetAngle((ServoID)i, angles[i]);
    }
}

// 执行动作序列
void Servo_ExecuteSequence(uint16_t sequence[][SERVO_NUM], uint8_t seq_len, uint16_t delay_ms)
{
    uint8_t i;
    
    for(i = 0; i < seq_len; i++)
    {
        Servo_MoveToPosition(sequence[i]);
        
        // 等待所有舵机到位
        uint8_t all_arrived = 0;
        while(!all_arrived)
        {
            Servo_UpdateAll();
            Delay_ms(10);
            
            all_arrived = 1;
            for(uint8_t j = 0; j < SERVO_NUM; j++)
            {
                if(g_servos[j].moving)
                {
                    all_arrived = 0;
                    break;
                }
            }
        }
        
        Delay_ms(delay_ms);
    }
}

3.3 主程序 (main.c)

c 复制代码
#include "stm32f10x.h"
#include "servo_control.h"
#include "delay.h"
#include "key.h"
#include "oled.h"

// 预设动作序列
uint16_t action_sequence[][SERVO_NUM] = {
    // 初始位置
    {90, 90, 90, 90},
    // 动作1:挥手
    {45, 90, 90, 90},
    {135, 90, 90, 90},
    {45, 90, 90, 90},
    {135, 90, 90, 90},
    // 动作2:点头
    {90, 45, 90, 90},
    {90, 135, 90, 90},
    {90, 45, 90, 90},
    {90, 135, 90, 90},
    // 回到初始位置
    {90, 90, 90, 90}
};

int main(void)
{
    // 系统初始化
    SystemClock_Init();
    Delay_Init();
    Key_Init();
    OLED_Init();
    
    // 舵机初始化
    Servo_Init();
    
    // 显示欢迎信息
    OLED_ShowString(0, 0, "STM32 Servo Ctrl");
    OLED_ShowString(0, 2, "Select: Servo 1");
    OLED_ShowString(0, 4, "Angle: 90");
    OLED_ShowString(0, 6, "Mode: Manual");
    
    while(1)
    {
        // 更新系统时间
        g_system.system_time++;
        
        // 按键扫描和处理
        Key_Scan();
        Key_Process();
        
        // 更新舵机位置
        Servo_UpdateAll();
        
        // 显示当前状态
        OLED_ShowNum(80, 2, g_system.selected_servo + 1, 1);
        OLED_ShowNum(56, 4, g_servos[g_system.selected_servo].current_angle, 3);
        
        Delay_ms(1);
    }
}

3.4 按键控制 (key.c)

c 复制代码
#include "key.h"
#include "servo_control.h"
#include "delay.h"

// 按键引脚定义
#define KEY1_PIN    GPIO_Pin_12
#define KEY2_PIN    GPIO_Pin_13
#define KEY3_PIN    GPIO_Pin_14
#define KEY4_PIN    GPIO_Pin_15
#define KEY_PORT    GPIOB

// 按键状态
typedef enum {
    KEY_NONE = 0,
    KEY1_PRESS,
    KEY2_PRESS,
    KEY3_PRESS,
    KEY4_PRESS
} KeyStatus;

// 按键初始化
void Key_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    
    GPIO_InitStructure.GPIO_Pin = KEY1_PIN | KEY2_PIN | KEY3_PIN | KEY4_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;  // 上拉输入
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(KEY_PORT, &GPIO_InitStructure);
}

// 按键扫描
KeyStatus Key_Scan(void)
{
    static uint8_t key_lock = 0;
    static uint16_t key_delay = 0;
    
    if(key_lock == 0)
    {
        if((KEY_PORT & KEY1_PIN) == 0)
        {
            key_delay++;
            if(key_delay >= 10)  // 消抖
            {
                key_delay = 0;
                key_lock = 1;
                return KEY1_PRESS;
            }
        }
        else if((KEY_PORT & KEY2_PIN) == 0)
        {
            key_delay++;
            if(key_delay >= 10)
            {
                key_delay = 0;
                key_lock = 1;
                return KEY2_PRESS;
            }
        }
        else if((KEY_PORT & KEY3_PIN) == 0)
        {
            key_delay++;
            if(key_delay >= 10)
            {
                key_delay = 0;
                key_lock = 1;
                return KEY3_PRESS;
            }
        }
        else if((KEY_PORT & KEY4_PIN) == 0)
        {
            key_delay++;
            if(key_delay >= 10)
            {
                key_delay = 0;
                key_lock = 1;
                return KEY4_PRESS;
            }
        }
        else
        {
            key_delay = 0;
        }
    }
    else
    {
        // 等待按键释放
        if((KEY_PORT & (KEY1_PIN | KEY2_PIN | KEY3_PIN | KEY4_PIN)) == 
           (KEY1_PIN | KEY2_PIN | KEY3_PIN | KEY4_PIN))
        {
            key_lock = 0;
        }
    }
    
    return KEY_NONE;
}

// 按键处理
void Key_Process(void)
{
    KeyStatus key = Key_Scan();
    
    switch(key)
    {
        case KEY1_PRESS:  // 舵机1角度增加
            if(g_system.selected_servo == SERVO_1)
            {
                uint16_t new_angle = g_servos[SERVO_1].current_angle + 5;
                if(new_angle > SERVO_MAX_ANGLE) new_angle = SERVO_MAX_ANGLE;
                Servo_SetAngle(SERVO_1, new_angle);
            }
            break;
            
        case KEY2_PRESS:  // 舵机1角度减少
            if(g_system.selected_servo == SERVO_1)
            {
                uint16_t new_angle = g_servos[SERVO_1].current_angle - 5;
                if(new_angle < SERVO_MIN_ANGLE) new_angle = SERVO_MIN_ANGLE;
                Servo_SetAngle(SERVO_1, new_angle);
            }
            break;
            
        case KEY3_PRESS:  // 舵机2角度增加
            if(g_system.selected_servo == SERVO_2)
            {
                uint16_t new_angle = g_servos[SERVO_2].current_angle + 5;
                if(new_angle > SERVO_MAX_ANGLE) new_angle = SERVO_MAX_ANGLE;
                Servo_SetAngle(SERVO_2, new_angle);
            }
            break;
            
        case KEY4_PRESS:  // 舵机2角度减少
            if(g_system.selected_servo == SERVO_2)
            {
                uint16_t new_angle = g_servos[SERVO_2].current_angle - 5;
                if(new_angle < SERVO_MIN_ANGLE) new_angle = SERVO_MIN_ANGLE;
                Servo_SetAngle(SERVO_2, new_angle);
            }
            break;
    }
}

3.5 OLED显示 (oled.c)

c 复制代码
#include "oled.h"
#include "i2c.h"

// OLED初始化
void OLED_Init(void)
{
    I2C_Init();
    
    OLED_WriteCmd(0xAE);  // 关闭显示
    OLED_WriteCmd(0xD5);  // 设置显示时钟分频比/振荡器频率
    OLED_WriteCmd(0x80);
    OLED_WriteCmd(0xA8);  // 设置多路复用比率
    OLED_WriteCmd(0x3F);
    OLED_WriteCmd(0xD3);  // 设置显示偏移
    OLED_WriteCmd(0x00);
    OLED_WriteCmd(0x40);  // 设置显示起始行
    OLED_WriteCmd(0x8D);  // 电荷泵设置
    OLED_WriteCmd(0x14);
    OLED_WriteCmd(0x20);  // 内存地址模式
    OLED_WriteCmd(0x02);
    OLED_WriteCmd(0xA1);  // 段重映射
    OLED_WriteCmd(0xC8);  // 扫描方向
    OLED_WriteCmd(0xDA);  // 设置COM引脚硬件配置
    OLED_WriteCmd(0x12);
    OLED_WriteCmd(0x81);  // 对比度设置
    OLED_WriteCmd(0xCF);
    OLED_WriteCmd(0xD9);  // 预充电周期
    OLED_WriteCmd(0xF1);
    OLED_WriteCmd(0xDB);  // VCOMH取消选择级别
    OLED_WriteCmd(0x40);
    OLED_WriteCmd(0xA4);  // 整个显示开启
    OLED_WriteCmd(0xA6);  // 正常显示
    OLED_WriteCmd(0xAF);  // 开启显示
    
    OLED_Clear();
}

// 显示字符串
void OLED_ShowString(uint8_t x, uint8_t y, char *str)
{
    while(*str)
    {
        OLED_ShowChar(x, y, *str++);
        x += 8;
        if(x > 120)
        {
            x = 0;
            y += 2;
        }
    }
}

// 显示数字
void OLED_ShowNum(uint8_t x, uint8_t y, uint32_t num, uint8_t len)
{
    uint8_t i;
    for(i = 0; i < len; i++)
    {
        OLED_ShowChar(x + i*8, y, '0' + (num / (uint32_t)pow(10, len-1-i)) % 10);
    }
}

3.6 高级功能扩展 (advanced_features.c)

c 复制代码
#include "servo_control.h"

// 舵机校准功能
void Servo_Calibration(void)
{
    printf("舵机校准开始...\n");
    
    for(uint8_t i = 0; i < SERVO_NUM; i++)
    {
        printf("校准舵机 %d:\n", i+1);
        
        // 转到最小角度
        Servo_SetAngle((ServoID)i, SERVO_MIN_ANGLE);
        Delay_ms(2000);
        
        // 转到最大角度
        Servo_SetAngle((ServoID)i, SERVO_MAX_ANGLE);
        Delay_ms(2000);
        
        // 回到中间
        Servo_SetAngle((ServoID)i, 90);
        Delay_ms(2000);
    }
    
    printf("舵机校准完成!\n");
}

// 轨迹规划 - 平滑移动到目标位置
void Servo_SmoothMove(ServoID servo_id, uint16_t target_angle, uint16_t duration_ms)
{
    uint16_t start_angle = g_servos[servo_id].current_angle;
    uint16_t steps = duration_ms / 20;  // 每20ms一步
    float angle_step = (float)(target_angle - start_angle) / steps;
    
    for(uint16_t i = 0; i < steps; i++)
    {
        uint16_t current_angle = start_angle + (uint16_t)(angle_step * i);
        Servo_SetAngle(servo_id, current_angle);
        Delay_ms(20);
    }
    
    // 确保到达最终位置
    Servo_SetAngle(servo_id, target_angle);
}

// 多舵机同步运动
void Servo_SyncMove(uint16_t target_angles[SERVO_NUM], uint16_t duration_ms)
{
    uint16_t start_angles[SERVO_NUM];
    uint16_t steps = duration_ms / 20;
    
    // 记录起始角度
    for(uint8_t i = 0; i < SERVO_NUM; i++)
    {
        start_angles[i] = g_servos[i].current_angle;
    }
    
    // 逐步移动
    for(uint16_t step = 0; step < steps; step++)
    {
        for(uint8_t i = 0; i < SERVO_NUM; i++)
        {
            float progress = (float)step / steps;
            uint16_t current_angle = start_angles[i] + 
                                   (uint16_t)((target_angles[i] - start_angles[i]) * progress);
            Servo_SetAngle((ServoID)i, current_angle);
        }
        Delay_ms(20);
    }
    
    // 确保到达最终位置
    for(uint8_t i = 0; i < SERVO_NUM; i++)
    {
        Servo_SetAngle((ServoID)i, target_angles[i]);
    }
}

// 预设动作演示
void Servo_DemoActions(void)
{
    printf("开始演示预设动作...\n");
    
    // 动作1:波浪运动
    for(uint8_t i = 0; i < SERVO_NUM; i++)
    {
        Servo_SetAngle((ServoID)i, 45);
        Delay_ms(200);
    }
    
    for(uint8_t i = 0; i < SERVO_NUM; i++)
    {
        Servo_SetAngle((ServoID)i, 135);
        Delay_ms(200);
    }
    
    // 回到中间
    for(uint8_t i = 0; i < SERVO_NUM; i++)
    {
        Servo_SetAngle((ServoID)i, 90);
    }
    
    Delay_ms(1000);
    
    // 动作2:交替运动
    for(uint8_t cycle = 0; cycle < 3; cycle++)
    {
        for(uint8_t i = 0; i < SERVO_NUM; i += 2)
        {
            Servo_SetAngle((ServoID)i, 45);
            if(i+1 < SERVO_NUM)
                Servo_SetAngle((ServoID)(i+1), 135);
        }
        Delay_ms(500);
        
        for(uint8_t i = 0; i < SERVO_NUM; i += 2)
        {
            Servo_SetAngle((ServoID)i, 135);
            if(i+1 < SERVO_NUM)
                Servo_SetAngle((ServoID)(i+1), 45);
        }
        Delay_ms(500);
    }
    
    // 回到初始位置
    for(uint8_t i = 0; i < SERVO_NUM; i++)
    {
        Servo_SetAngle((ServoID)i, 90);
    }
    
    printf("演示完成!\n");
}

四、编译与部署

4.1 Keil工程配置

复制代码
Target: STM32F103C8
Device: STM32F103C8T6
C/C++:
  Include Paths: .\inc; .\User; .\Hardware
  Define: STM32F10X_MD, USE_STDPERIPH_DRIVER
Linker:
  Scatter File: STM32_FLASH.sct
Debug:
  Debugger: ST-Link Debugger

4.2 使用说明

  1. 上电初始化:所有舵机转到90度中间位置
  2. 按键控制
    • 按键1:选中舵机1,角度增加5°
    • 按键2:选中舵机1,角度减少5°
    • 按键3:选中舵机2,角度增加5°
    • 按键4:选中舵机2,角度减少5°
  3. OLED显示:实时显示当前舵机和角度
  4. 紧急停止:可通过代码设置紧急停止标志

4.3 舵机角度对照表

角度 脉冲宽度 说明
500μs 最小位置
45° 1000μs 1/4位置
90° 1500μs 中间位置
135° 2000μs 3/4位置
180° 2500μs 最大位置

参考代码 stm32控制舵机程序 www.youwenfan.com/contentcsv/72263.html

五、注意事项

  1. 电源供应:舵机工作时电流较大,建议使用独立5V/2A电源
  2. 共地连接:STM32地和舵机电源地必须连接在一起
  3. 舵机保护:避免舵机堵转时间过长,以免烧毁舵机
  4. 速度控制:通过速度参数控制舵机转动速度,避免冲击
  5. 限位保护:在代码中设置角度限位,防止舵机损坏

六、扩展功能建议

  1. 串口控制:通过串口接收指令控制舵机
  2. 蓝牙/WiFi控制:添加无线控制模块
  3. 电位器反馈:使用电位器实现舵机位置反馈
  4. EEPROM存储:保存舵机位置和配置
  5. Web控制界面:通过网络控制舵机
相关推荐
国科安芯6 小时前
航天器多路并联大功率电源系统设计与ASP4644均流特性分析
单片机·嵌入式硬件·fpga开发·安全性测试
茯苓gao8 小时前
嵌入式开发笔记:CANopen相关移位运算与通信协议术语详解
网络·嵌入式硬件·学习·信息与通信
梁朝辉11 小时前
STM32H750VBT6中ADCINP与INN什么区别
stm32·单片机·嵌入式硬件
省四收割者12 小时前
一文详解信号完整性(1)
python·嵌入式硬件·数学建模·信息与通信·信号处理·智能硬件
崇山峻岭之间13 小时前
单片机无感FOC驱动之ADC
单片机·嵌入式硬件
Ligocious13 小时前
1.点亮一颗小小的LED
单片机·嵌入式硬件
KaifuZeng17 小时前
通信与接口协议面试八、CAN通信
单片机·嵌入式硬件·面试·通信与接口协议
智源单片机设计18 小时前
基于单片机的直流电机双闭环调速系统设计
单片机·嵌入式硬件
kebidaixu18 小时前
两轮车换电 BMS 的模拟前端中颖 SH367306 AFE 芯片详解
stm32
kebidaixu19 小时前
两轮车换电 BMS 的模拟前端中颖 SH367306 AFE 芯片调试实录:四个关键问题与解决方案
stm32