基于STM32F103ZET6实现6路舵机控制

一、硬件连接

1. 电路连接
定时器 通道 对应引脚 舵机编号
TIM2 CH1 PA0 舵机1
TIM2 CH2 PA1 舵机2
TIM2 CH3 PA2 舵机3
TIM2 CH4 PA3 舵机4
TIM3 CH1 PC6 舵机5
TIM3 CH2 PC7 舵机6
2. 电源要求
  • 每个舵机需独立供电(建议5V/2A)
  • 逻辑部分供电3.3V

二、软件实现代码

1. 定时器初始化(TIM2+TIM3)
c 复制代码
#include "stm32f10x.h"

#define SERVO_PWM_FREQ 50    // 50Hz
#define SERVO_PULSE_MIN 500  // 0.5ms
#define SERVO_PULSE_MAX 2500 // 2.5ms

void TIM2_TIM3_PWM_Init() {
    GPIO_InitTypeDef GPIO_InitStruct;
    TIM_TimeBaseInitTypeDef TIM_InitStruct;
    TIM_OCInitTypeDef TIM_OCInitStruct;

    // 使能时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 | RCC_APB1Periph_TIM3, ENABLE);

    // GPIO配置(复用推挽输出)
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    
    // TIM2通道配置 (PA0-PA3)
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    // TIM3通道配置 (PC6-PC7)
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_Init(GPIOC, &GPIO_InitStruct);

    // 定时器基础配置
    TIM_InitStruct.TIM_Prescaler = 72 - 1;          // 预分频 1MHz
    TIM_InitStruct.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_InitStruct.TIM_Period = 20000 - 1;          // 20ms周期
    TIM_InitStruct.TIM_ClockDivision = TIM_CKD_DIV1;

    TIM_TimeBaseInit(TIM2, &TIM_InitStruct);
    TIM_TimeBaseInit(TIM3, &TIM_InitStruct);

    // PWM模式配置
    TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStruct.TIM_Pulse = SERVO_PULSE_MIN;
    TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;

    // 配置TIM2通道
    TIM_OC1Init(TIM2, &TIM_OCInitStruct);
    TIM_OC2Init(TIM2, &TIM_OCInitStruct);
    TIM_OC3Init(TIM2, &TIM_OCInitStruct);
    TIM_OC4Init(TIM2, &TIM_OCInitStruct);

    // 配置TIM3通道
    TIM_OC1Init(TIM3, &TIM_OCInitStruct);
    TIM_OC2Init(TIM3, &TIM_OCInitStruct);

    // 使能定时器
    TIM_Cmd(TIM2, ENABLE);
    TIM_Cmd(TIM3, ENABLE);
}
2. 舵机控制核心代码
c 复制代码
typedef struct {
    uint16_t target_pulse;  // 目标脉宽
    uint16_t current_pulse; // 当前脉宽
    float angle;            // 当前角度
} ServoCtrl;

ServoCtrl servos[6] = {0};

// 角度转脉宽
uint16_t AngleToPulse(float angle) {
    return SERVO_PULSE_MIN + (angle / 180.0f) * (SERVO_PULSE_MAX - SERVO_PULSE_MIN);
}

// 初始化舵机参数
void Servo_Init() {
    for(int i=0; i<6; i++) {
        servos[i].angle = 0.0f;
        servos[i].current_pulse = AngleToPulse(0.0f);
        servos[i].target_pulse = AngleToPulse(0.0f);
    }
}

// 设置舵机角度
void Servo_SetAngle(uint8_t index, float angle) {
    if(index >= 6) return;
    angle = angle > 180.0f ? 180.0f : angle < 0.0f ? 0.0f : angle;
    servos[index].target_pulse = AngleToPulse(angle);
}

// PWM中断服务函数
void TIM2_IRQHandler() {
    if(TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) {
        static uint8_t channel = 0;
        
        // 更新各通道占空比
        TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
        
        switch(channel) {
            case 0: TIM_SetCompare1(TIM2, servos[0].current_pulse); break;
            case 1: TIM_SetCompare2(TIM2, servos[1].current_pulse); break;
            case 2: TIM_SetCompare3(TIM2, servos[2].current_pulse); break;
            case 3: TIM_SetCompare4(TIM2, servos[3].current_pulse); break;
            case 4: TIM_SetCompare1(TIM3, servos[4].current_pulse); break;
            case 5: TIM_SetCompare2(TIM3, servos[5].current_pulse); break;
        }
        
        channel = (channel + 1) % 6;
    }
}

三、关键参数配置

1. 定时器计算
c 复制代码
系统时钟:72MHz
预分频:72-1 → 1MHz计数频率
周期值:20000 → 20ms周期
占空比范围:500-2500(对应0.5ms-2.5ms)
2. 角度控制算法
c 复制代码
// 平滑角度过渡(增量控制)
void Servo_Update() {
    for(int i=0; i<6; i++) {
        float error = servos[i].target_pulse - servos[i].current_pulse;
        float step = error * 0.1f;  // 调整步长(0.1-0.5)
        
        if(step > 0) {
            servos[i].current_pulse += (uint16_t)step;
            if(servos[i].current_pulse > servos[i].target_pulse)
                servos[i].current_pulse = servos[i].target_pulse;
        } else {
            servos[i].current_pulse += (uint16_t)step;
            if(servos[i].current_pulse < servos[i].target_pulse)
                servos[i].current_pulse = servos[i].target_pulse;
        }
    }
}

四、主程序流程

c 复制代码
int main() {
    // 系统初始化
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    Delay_Init();
    Servo_Init();
    TIM2_TIM3_PWM_Init();
    
    // 设置初始角度
    Servo_SetAngle(0, 90.0f);  // 舵机1→90°
    Servo_SetAngle(3, 45.0f);  // 舵机4→45°
    
    while(1) {
        Servo_Update();  // 更新角度
        Delay_ms(20);    // 20ms刷新周期
    }
}

五、扩展

1. 多机同步控制
c 复制代码
// 同步更新所有通道
void Servo_SyncUpdate() {
    TIM_SetCompare1(TIM2, servos[0].current_pulse);
    TIM_SetCompare2(TIM2, servos[1].current_pulse);
    TIM_SetCompare3(TIM2, servos[2].current_pulse);
    TIM_SetCompare4(TIM2, servos[3].current_pulse);
    TIM_SetCompare1(TIM3, servos[4].current_pulse);
    TIM_SetCompare2(TIM3, servos[5].current_pulse);
}
2. 连续旋转模式
c 复制代码
// 设置连续旋转速度(0-200rpm)
void Servo_SetSpeed(uint8_t index, int16_t speed) {
    if(speed > 0) {
        servos[index].target_pulse = SERVO_PULSE_MIN + (speed * 2000 / 200);
    } else {
        servos[index].target_pulse = SERVO_PULSE_MAX + (speed * 2000 / 200);
    }
}

参考代码 STM32F103ZET6同时控制6个舵机的程序 www.youwenfan.com/contentcsj/71392.html

六、完整工程结构

c 复制代码
├── Drivers/
│   ├── CMSIS/
│   └── STM32F10x_HAL_Driver/
├── Middlewares/
│   └── Servo_Control/      # 舵机控制库
│       ├── servo.c
│       └── servo.h
├── Applications/
│   └── main.c            # 主程序入口
└── Projects/
    └── STM32F103ZET6/
        ├── startup_stm32f10x_hd.s
        └── system_stm32f10x.c
相关推荐
jianqiang.xue3 小时前
系统服务层设计:沉淀通用能力,让业务层只专注业务
stm32·单片机·嵌入式硬件·mcu·物联网·esp32
无垠的广袤4 小时前
【RA-Eco-RA2T1开发板】介绍、环境搭建、工程测试
单片机·嵌入式硬件·串口通信·uart·开发环境·blink
画面无声5 小时前
STM32的SPI通信原理与练习记录
c语言·stm32·单片机·嵌入式硬件·学习
weixin_456808385 小时前
【沁恒蓝牙开发】IAP升级流程
c语言·单片机·嵌入式硬件
czhaii6 小时前
RTL8019AS、555,DS3231等芯片的跳线方式和引脚定义
嵌入式硬件
czhaii8 小时前
基本电器元件 电阻 resistance R 电容 capacitance C 电感 inductance L
嵌入式硬件
万亿少女的梦1689 小时前
基于STM32、RFID与OneNET的开放性实验室电源节点设计
stm32·物联网·onenet·rfid·esp8266
1560820721910 小时前
飞腾D2000接入外部时钟HYM8563芯片调试
驱动开发·嵌入式硬件·mcu
殷忆枫10 小时前
基于STM32+OV2640+ESP8266的无线图像采集系统设计与实现
stm32·单片机·嵌入式硬件
llilian_1611 小时前
便携式相位测量模块应用方案的核心优势
网络·功能测试·单片机