摇杆+舵机+STM32F103=云台:构建一个简易的云台控制系统

展示视频

摇杆+舵机+STM32F103=云台:构建一个简易的云台控制

1.材料准备

  • STM32F103C8T6最小系统板*1                                 作为系统的核心控制单元
  • SG90舵机(180°)*2                                                 用于驱动云台的旋转
  • 摇杆按键*1                                                                  用于输入控制信号,分别对应X轴和Y轴
  • 舵机支架*1                                                                  用于组合两个舵机
  • 面包板*1(非必须)                                                    快速搭建和测试电路
  • 杜邦线若干                                                                  用于连接,模块元件和面包版
  •                                                          总约:40元

2. 系统架构

系统的整体架构如下:

  1. 摇杆模块:通过ADC(模数转换器)将摇杆的模拟信号转换为数字信号。
  2. STM32F103微控制器:接收摇杆的输入信号,并根据输入信号计算舵机的控制脉冲宽度。
  3. 舵机:根据STM32F103输出的PWM信号调整角度,实现云台的水平和垂直运动。

3. 软件实现

3.1 初始化

首先,我们需要初始化STM32F103的ADC和PWM模块。以下是初始化代码示例:

adc.c  文件


#include "adc.h"                  
 
uint16_t AD_Value[5];
 
void MY_ADC1_INIT(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;
    ADC_InitTypeDef ADC_InitStruct;
    DMA_InitTypeDef  DMA_InitStructure;
    
    //1、使能时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//DMA1时钟使能 
    
    //2、选择ADC分频
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);
    
    //3、初始化GPIO
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN;//模拟输入
    GPIO_InitStruct.GPIO_Pin =      GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    //4、选择ADC通道
    ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_55Cycles5);
    //ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3,     ADC_SampleTime_55Cycles5);
    //ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4,               ADC_SampleTime_55Cycles5);
     //ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 5,      ADC_SampleTime_55Cycles5);
    
    //5、初始化ADC
    ADC_InitStruct.ADC_Mode = ADC_Mode_Independent;//ADC工作模式。独立工作模式
    ADC_InitStruct.ADC_ScanConvMode = ENABLE;  //扫描模式(多个通道)    
    ADC_InitStruct.ADC_ContinuousConvMode =ENABLE;//连续转换
    //不使用外部触发,使用内部软件触发
    ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
    ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;//右对齐    
    ADC_InitStruct.ADC_NbrOfChannel =5;//需要扫描的通道数
    ADC_Init(ADC1, &ADC_InitStruct);//ADC初始化
    
    //6、初始化DMA
    //DMA外设地址,ADC数据储存地址
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;
    //外设非增量模式,只读取ADC储存地址里面的
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    //外设数据长度:16位
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)AD_Value;//存储器地址
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//存储器增量模式
    //接收到数据长度:16位
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//外设到储存器(数据传输方向)
    DMA_InitStructure.DMA_BufferSize = 5;//数据传输量 
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;// 循环扫描
    DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//中等优先级
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  //DMA通道x没有设置为内存到内存传输
    DMA_Init(DMA1_Channel1, &DMA_InitStructure);//初始化DMA 
    
    //6、使能DMA
    DMA_Cmd(DMA1_Channel1,ENABLE);
    //7、使能ADC触发DMA
    ADC_DMACmd(ADC1,ENABLE);
    //8、使能ADC
    ADC_Cmd(ADC1, ENABLE);
    
    //9、校准ADC数据
    ADC_ResetCalibration(ADC1);//开始复位校准
    while(ADC_GetResetCalibrationStatus(ADC1) == SET);//获取ADC重置校准寄存器的状态
    ADC_StartCalibration(ADC1);        //开始指定ADC的校准状态
    while(ADC_GetCalibrationStatus(ADC1));  //获取指定ADC的校准状态
    
    //使能指定的ADC1的软件转换启动功能    (连续转换时,初始化一次就行了)
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}

pwm.c


#include "pwm.h"
//TIM_SetCompare1(TIM3,500);  //发送占空比

// Time 通道PA6通道1 PA7通道2
void PWM_INIT(unsigned int Per, unsigned int Pre) 
{
    GPIO_InitTypeDef GPIO_InitStructure;
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructre;
    TIM_OCInitTypeDef TIM_OCInitStructre;  // 定时器通道结构体

    // 使能TIM3和GPIOA的时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);  // 复用通道

    // 配置GPIOA的引脚为复用推挽输出
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;  // PA6和PA7
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;         // 复用推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);  // 初始化IO

    // 配置TIM3的基本参数
    TIM_TimeBaseInitStructre.TIM_ClockDivision = TIM_CKD_DIV1;  // 不分频
    TIM_TimeBaseInitStructre.TIM_CounterMode = TIM_CounterMode_Up;  // 向上计数
    TIM_TimeBaseInitStructre.TIM_Period = Per;  // 自动重装值
    TIM_TimeBaseInitStructre.TIM_Prescaler = Pre;  // 预分频系数
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructre);  // 初始化定时器

    // 配置TIM3的输出比较通道
    TIM_OCInitStructre.TIM_OCMode = TIM_OCMode_PWM1;  // 设置PWM模式1
    TIM_OCInitStructre.TIM_OCPolarity = TIM_OCPolarity_High;  // 输出极性为高
    TIM_OCInitStructre.TIM_OCNPolarity = TIM_OCNPolarity_High;  // 互补输出极性为高
    TIM_OCInitStructre.TIM_OutputState = TIM_OutputState_Enable;  // 使能输出
    TIM_OCInitStructre.TIM_OutputNState = TIM_OutputNState_Disable;  // 禁用互补输出

    // 初始化通道1和通道2
    TIM_OC1Init(TIM3, &TIM_OCInitStructre);
    TIM_OC2Init(TIM3, &TIM_OCInitStructre);

    // 使能定时器通道1和通道2的输出比较预装载寄存器
    TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);
    TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);

    // 使能定时器
    TIM_Cmd(TIM3, ENABLE);
}

void get_pwm_DJ(unsigned int Angle1,unsigned int Angle2)
{
    u16  position1 = 500 + (2000 /180 * Angle1);
    u16  position2 = 500 + (2000 /180 * Angle2);   
    TIM_SetCompare1(TIM3,position1); 
    TIM_SetCompare2(TIM3,position2); 
}
 

3.2 主循环

在主循环中,我们读取摇杆的ADC值,并根据这些值计算舵机的PWM脉冲宽度。以下是主循环代码示例:

#include "pwm.h"
#include "adc.h"

extern uint16_t AD_Value[5];
u8 Angle1 = 90;
u8 Angle2 = 90;
u8 AngleSpeed = 10; // 每次改变舵机的角度步长

#define DEAD_ZONE 100 // 死区范围

int main(void)
{
    MY_ADC1_INIT();
    PWM_INIT(20000-1, 72-1);   // (Per+1)*(Pre+1)/时钟 20ms

    while(1)
    {
        // 处理X轴摇杆数据
        if (AD_Value[0] > (2048 + DEAD_ZONE)) // 左
        {
            if (Angle1 <= (180 - AngleSpeed))
                Angle1 += AngleSpeed;
        }
        else if (AD_Value[0] < (2048 - DEAD_ZONE)) // 右
        {
            if (Angle1 >= AngleSpeed)
                Angle1 -= AngleSpeed;
        }

        // 处理Y轴摇杆数据
        if (AD_Value[1] > (2048 + DEAD_ZONE)) // 上
        {
            if (Angle2 <= (180 - AngleSpeed))
                Angle2 += AngleSpeed;
        }
        else if (AD_Value[1] < (2048 - DEAD_ZONE)) // 下
        {
            if (Angle2 >= AngleSpeed)
                Angle2 -= AngleSpeed;
        }

    // 更新舵机位置
        get_pwm_DJ(Angle1, Angle2);

    }
}

4. 调试与优化

在实际应用中,可能需要根据具体情况进行调试和优化。以下是一些建议:

  • 死区处理:在摇杆的中心位置设置一个死区,避免不必要的抖动。
  • 角度步长控制:根据实际需求调整角度变化的步长,以控制舵机的响应速度。
  • PWM频率调整:根据舵机的规格调整PWM的频率和占空比。

5. 总结

通过结合摇杆、舵机和STM32F103微控制器,我们可以构建一个简易的云台控制系统。该系统能够根据摇杆的输入信号控制云台的水平和垂直运动,具有较高的灵活性和可扩展性。通过进一步的优化和调试,可以实现更加精确和稳定的控制效果。

希望本文能够帮助你理解如何使用STM32F103微控制器、摇杆和舵机来构建一个云台控制系统。如果你有任何问题或建议,欢迎在评论区留言讨论。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值