基于STM32F1的舵机控制

一、SG90舵机介绍

SG90是有三个版本的,90度版、180度版和360度版,前两个只是舵机旋转角度的范围不一样,可以在这个范围内任意的控制舵机所转的角度,而360版本的是一直旋转的,我们不能控制它旋转的角度,只能控制它旋转的速度,这里我们在买的时候就要注意一点,根据自己的需求选取不同的版本,以免买错。这里我们介绍180度版本的。三个控制原理基本相同。

单片机系统实现对舵机输出转角的控制,必须首先完成两个任务:首先是产生基本的PWM周期信号,本设计是产生20ms的周期信号;其次是脉宽的调整,即单片机模拟PWM信号的输出,并且调整占空比。

脉冲的高电平部分一般为0.5ms~2.5ms范围内的角度控制脉冲部分,所以是利用一个占空比来控制舵机转动的角度。PWM占空比是指在一个周期内,信号处于高电平的时间占据整个信号周期的百分比。占空比 = t / T 相关参数如下:

t = 0.5ms——————-舵机会转动 0 °

t = 1.0ms——————-舵机会转动 45°

t = 1.5ms——————-舵机会转动 90°

t = 2.0ms——————-舵机会转动 135°

t = 2.5ms——————-舵机会转动180°

当你需要转动135°时,他的占空比就是2.0ms/20ms=10%,所以 TIMx 捕获比较寄存器值就为200-200*10% = 180。

二、与单片机的连接:

SG90分别有三根线。棕色,红色,橙色

棕色接地(GND)

红色接电源(+5V)

橙色接输出PWM信号的引脚

三、PWM

因为舵机的控制主要用到的就是定时器的pwm功能,所以我们先聊一聊pwm

PWM的输出其实就是对外输出脉宽可调(即占空比调节)的方波信号,信号频率是由自动重装寄存器 ARR 的值决定,占空比由比较寄存器CCR的值决定。

  当TIMx_CR1寄存器中的 DIR 位为低时执行递增计数,计数器CNT从0计数到自动重载值(TIMx_ARR 寄存器的内容),然后重新从 0 开始数并生成计数器上溢事件。

 以PWM 模式 1 为例。只要TIMx_CNT < TIMx_CCRx, PWM 参考信号OCxREF 便为有高电平,否则为无效的低电平。如果 TIMx_CCRx 中的比较值大于自动重载值(TIMx_ARR 中),则 OCxREF 保持为“ 1”。果比较值为 0, 则 OCxREF 保持为“ 0”。

  我们控制舵机主要是产生一个以20ms为周期的pwm信号,然后通过改变pwm的占空比就可以控制舵机的转角。

四、程序设计

主函数如下:

int main() 

{

u8 i;

SysTick_Init(72);

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);  //中断优先级分组 分2组

LED_Init();

Sg90_Init();

USART1_Init();

while(1)

   {

   i++;

// TIM_SetCompare2(TIM3, 195);  // 0度    t = 0.5ms           修改TIMx_CCRx的值控制占空比

// delay_ms(1000);

// TIM_SetCompare2(TIM3, 190);  //45度   t = 1.0ms——————-舵机会转动 45°  占空比就应该为1ms/20ms = 5%,所以TIM_SetCompare1的 TIMx 捕获比较 1 寄存器值就为200-200*5% = 190

// delay_ms(1000);

// TIM_SetCompare2(TIM3, 185);  //90度      t = 1.5ms

// delay_ms(1000);

// TIM_SetCompare2(TIM3, 180);  //135度        t = 2.0ms

// delay_ms(1000);

// TIM_SetCompare2(TIM3, 175);  //180度         t = 2.5ms

// delay_ms(1000);

// Sg90_Return(0);

// delay_ms(1000);

//    Sg90_Return(45);

// delay_ms(1000);

//    Sg90_Return(90);

// delay_ms(1000);

//    Sg90_Return(135);

// delay_ms(1000);

//    Sg90_Return(180);

// delay_ms(1000);

   

   if(i%20==0)

   {

   led1=!led1;

   }

   delay_ms(10);

   

   }

}

在主函数中我们控制舵机以此转过45、90、135、180度,这里之所以会注销掉这些,是因为我在工程文件中增添了通过串口控制舵机的功能,我们可以直接在串口中向单片机发送控制数据,舵机就会转过相应的角度。

  先看看sg90.c文件:

void Sg90_Init()

{

TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;

TIM_OCInitTypeDef TIM_OCInitStructure;     //输出比较结构体

GPIO_InitTypeDef GPIO_InitStructure; 

/* 开启时钟 */

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);//初始化GPIOB端口使能

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);//开启定时器3的时钟

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);

/*  配置GPIO的模式和IO口 */

GPIO_InitStructure.GPIO_Pin=GPIO_Pin_7;                   // 选择你要设置的IO口

GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;          //选择输出模式(复用推挽输出)

GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;

GPIO_Init(GPIOC,&GPIO_InitStructure);

GPIO_PinRemapConfig(GPIO_FullRemap_TIM3,ENABLE);  //设置管脚复用映射(部分重映射)

TIM_TimeBaseInitStructure.TIM_Period=199;    //自动装载值      定时器定时时间计算公式

TIM_TimeBaseInitStructure.TIM_Prescaler=7199; //分频系数      ((per)*(psc+1))/Tclk

TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;//时钟分频

TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //计数模式(向上计数模式)

TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);

TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;

TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_Low; //设置输出极性(底)

TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable; // 比较输出使能

TIM_OC2Init(TIM3,&TIM_OCInitStructure);   //输出比较通道2初始化

TIM_Cmd(TIM3,ENABLE); //开启定时器

TIM_OC2PreloadConfig(TIM3,TIM_OCPreload_Enable); //使能TIM3在CCR2上的预装载寄存器

TIM_ARRPreloadConfig(TIM3,ENABLE); //使能TIM3在ARR上的预装载寄存器允许位

}

void Sg90_Return(u8 angle)

{

u8 per;

if(angle==45)

{

per=190;

TIM_SetCompare2(TIM3, per);

}

else if(angle==90)

{

per=185;

TIM_SetCompare2(TIM3, per);

}

else if(angle==135)

{

per=180;

TIM_SetCompare2(TIM3, per);

}

else if(angle==180)

{

per=175;

TIM_SetCompare2(TIM3, per);

}

else

{

per=195;

TIM_SetCompare2(TIM3, per);

}

}

程序比较简单,就一个定时器初始化和直接输入角度的控制函数,(第二个是函数是我自己写的,比较烂,有能力的可以完善完善一起学习)主要思想还是通过TIM_SetCompare2()设置CCR的数值。

在看看如何通过串口控制:

void USART1_IRQHandler(void)     //中断服务函数

      { 

  

  u8 r;

u16 num;

static char pwm[4];

    static int i=0;

if(USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET)  //检查串口1的接收中断是否发生

{

r=USART_ReceiveData(USART1);      //返回 USART1 最近接收到的数据

if(r!='g'&&r!='m')

{

pwm[i]=r;

i++;

}

else

{

i=0;

num=(atoi(pwm));

TIM_SetCompare2(TIM3, num);

}

USART_SendData(USART1,r);         //通过外设 USART1 发送单个数据

while (USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);   //检查 USART1的 发送完成标志位 设置与否

}

USART_ClearFlag(USART1,USART_FLAG_TC);

       }

这里主要就是数据转化的问题,通过atoi()函数将字符串转化为整数然后再用于控制舵机。串口传输数据的时候要特别注意不同数据格式之间的转化。

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

搞机佬

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值