51单片机教程 使用PWM对舵机进行控制

电子说

1.3w人已加入

描述

舵机的控制方式

舵机的种类有很多,下面我所展示的是非常常见的SG90舵机(下面简称“舵机”),也是我们这次实验室考核所使用的舵机。

篇幅有限,这里只简单介绍了SG90舵机,想深入研究的同学可以自己去查阅资料。

我们先来了解一下SG90舵机

舵机

SG90舵机有三根线:GND、VCC和信号线:

舵机

GND(负极)和VCC(正极)都是电源线,可以接在开发板相同标识的排针或与开发板相同的电源上,注意GND要与单片机共地(接在同一个负极上)。

信号线接在单片机的IO口上,我们使用IO口输出PWM来控制舵机。

在对舵机进行控制时,像控制LED灯直接对信号线输出1和0是不能够正常控制的,要使用PWM进行控制:

舵机

↑不正确的控制方法,可能是酱紫的

舵机

↑使用PWM控制,能随意改变角度

那么下面我们进入正题:使用PWM对舵机进行控制。

舵机能在其旋转范围内(0°~180°)旋转至任意角度,但注意,这不是指能够连续转圈圈,而是像一把量角器,假设设置了30°,那么舵机就会旋转至30°的位置并停下,而不是在原来的位置叠加上30°。

舵机的旋转角度是通过PWM的占空比来调节的,PWM的周期一般为20ms,它们的关系如下表所示:

舵机

可以发现,要舵机停在0°的位置并不能将IO口直接置0,180°也不能直接置1。

示例程序

舵机的控制程序其实和LED亮度调节、呼吸灯的程序基本一致,都是使用PWM控制,只不过占空比有范围而已。

下面的程序是在我之前发的PWM的程序上稍作修改得到的,PWM部分就不再解释了,只解释舵机的控制部分。

#include < reg52.h >


#define PWM_T 200                     //产生中断的时间,因为是24MHz,200即100微妙(0.1毫秒)


int PWM_count0 = 0;                    //进入中断的次数
int PWM_count1 = 0;                    //与上面的类似,用于计数
int PWM_F = 0;                        //一个用于计时的标志,作用类似定时器的TF0、TF1


sbit servo = P2^0;                    //舵机信号线所连接的IO口


void PWM_Start()                      //PWM初始化函数,打开了定时器0
{
  EA = 1;
  ET0 = 1;
  TMOD |= 0x01;
  TR0 = 1;


  TH0 = (65536-PWM_T)/256;
  TL0 = (65536-PWM_T)%256;
}


int PWM(int PWM_value)                //控制PWM输出的子函数
{
  if(PWM_count0 <= PWM_value)
  {
    return 1;
  }
  else
  {
    return 0;
  }
}


void main()
{
  PWM_Start();                        //PWM开始运行


  while(1)
  {
    while(1)
    {
      servo = PWM(5);                  //旋转至0°
      if(PWM_F == 1)                  //判断标志是否为1,是则代表着过了1秒
      {
        PWM_F = 0;                    //将标志归零
        break;                        //过了1秒,退出循环
      }
    }


    while(1)
    {
      servo = PWM(10);                //旋转至45°
      if(PWM_F == 1)
      {
        PWM_F = 0;
        break;
      }
    }


    while(1)
    {
      servo = PWM(15);                //旋转至90°
      if(PWM_F == 1)
      {
        PWM_F = 0;
        break;
      }
    }


    while(1)
    {
      servo = PWM(20);                //旋转至135°
      if(PWM_F == 1)
      {
        PWM_F = 0;
        break;
      }
    }


    while(1)
    {
      servo = PWM(25);                //旋转至180°
      if(PWM_F == 1)
      {
        PWM_F = 0;
        break;
      }
    }
  }
}


void Timer0() interrupt 1
{
  TH0 = (65536-PWM_T)/256;
  TL0 = (65536-PWM_T)%256;


  PWM_count0++;


  if(PWM_count0 == 200)                //过了20ms,完成了一个PWM周期
  {
    PWM_count0 = 0;                    //清零,重新开始计数
    PWM_count1++;
    if(PWM_count1 == 50)              //PWM_count0清零了50次,也就是说过了20ms*50=1000ms=1s
    {
      PWM_count1 = 0;                  //清零,重新开始计数
      PWM_F = 1;                      //标志置1,说明已经过了2s
    }
  }
}

我在程序中并没有使用软件延时(delay()函数),这在PWM那篇文章中也说过了,软件延时期间无法改变IO口的高低电平输出,PWM也就失效了。

我的做法是设置一个标志PWM_F(也可以直接判断PWM_count1的值),当达到设定的时间就退出循环,否则就回到循环进行PWM输出。

如果你觉得这样做比较麻烦,也可以调用另一个定时器来计时,不过定时器的数量是有限的(51只有两个,52、STC15F2K60S2有三个)。

在写PWM的程序时,可以参考我上面的做法,每0.1ms进入一次中断,进入200次(20ms)就完成一次PWM中断,对应着舵机所要求的PWM周期长度,控制起来也会方便很多。

另外,头文件建议使用与单片机型号所匹配的,上面例程中包含reg52.h是为了有较高的兼容性,但这样就有一些资源无法调用。例如STC15F2K60S2,P4引脚(如P47)只有在包含STC15F2K60S2.h时才能被调用。

假如你的舵机不能正常运行,可能是下面的原因:

1.单片机晶振或定时器没有设置正确,例如上面例程对应的是24MHz的频率,下载时选择了默认的11.0592MHz;
2.电源供电不足,可以使用充电宝供电试试。

总结

学会PWM后,舵机的控制其实非常容易,稍作一下改动就行。对舵机感兴趣的同学可以去了解一下其他类型的舵机,使用PWM控制的只是其中一种。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分