舵机(servo motor)的控制
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
舵机(servo motor)的控制
基于单片机16f877a和proteus的仿真
舵机是一种位置伺服的驱动器,适用于那些需要角度不断变化并可以保持的控制系统。
(注意:如果你控制的舵机在不停的抖动,其中一个原因就是你给的脉冲有杂波,这点很重要。
舵机是一个物理器件,它的转动需要时间的,因此,程序中占空比的值变化不能太快,不然舵
机跟不上程序的响应时间。
)
一、舵机的结构
我们选的舵机型号是TowerPro MG995,实物如图:
它有三条线棕色、红色、黄色分别是GND、 V+ 、 S(信号)。
如下图:
二、舵机的单片机控制原理
1、我们得先了解舵机的工作原理:控制信号由舵机的信号通道进入信号调制芯片,获得直流偏置电压。
它内部有一个基准电路,产生周期为20ms,宽度为1.5ms的基准信号,将获得的直流偏置电压与电位器的电压比较,获得电压差输出。
最后,电压差的正负输出到电机驱动芯片决定电机的正反转。
当电机转速一定时,通过级联减速齿轮带动电位器旋转,使得电压差为0,电机停止转动。
它的控制要求如下图:
2、由上可知舵机的控制信号是PWM信号,利用占空比的变化改变舵机的位置。
我们用pic单片机的定时器1模块产生PWM信号,得到控制电机的占空比,也就如上图的占空比信号,周期是20Ms.下面我们来看看怎样产生上图的占空比,单片机的定时器1模块最大可以产生174ms的延时,也就是可以产生最大174ms的中断。
怎样设置Timer1来产生上述占空比的中断,可以参考具体资料书。
当系统中只需要实现一个舵机的控制,采用的控制方式是改变单片机的一个定时器中断的初值,将20ms分为两次中断执行,一次短定时中断和一次长定时中断。
这样既节省了硬件电路,也减少了软件开销,控制系统工作效率和控制精度都很高。
具体的设计过程:例如想让舵机转向左极限的角度,它的正脉冲为2ms,则负脉冲为20ms-2ms=18ms,所以开始时在控制口发送高电平,然后设置定时器在2ms 后发生中断,中断发生后,在中断程序里将控制口改为低电平,并将中断时间改为18ms,再过18ms进入下一次定时中断,再将控制口改为高电平,并将定时器初值改为2ms,等待下次中断到来,如此往复实现PWM信号输出到舵机。
用修改定时器中断初值的方法巧妙形成了脉冲信号,调整时间段的宽度便可使伺服机灵活运动。
但是我们要记住一点,要在中断中做事必须要在长中断中做事,可以避免不必要的麻烦。
了解了怎样用单片机控制舵机原理后我们就可以用proteus去仿真舵机的控制了。
三、proteus软件仿真舵机的控制
先在proteus中找到舵机的元件,就是那个英文单词是motor-pwmservo 的元件。
这里我们可以用按键的方式去控制单片机产生不同的占空比,来达到可以控制电机转到不同的角度。
电路如图所示:
四、具体的程序清单:
#include<pic.h>
unsigned char TL01;
unsigned char TH01;
unsigned char TL02;
unsigned char TH02;
int A,C=0;
bit FLAG=0;
void delay10ms(void)
{
unsigned char i,j;
for(i=20;i>0;i--)
for(j=248;j>0;j--) ;
}
//延时200ms子程序
void delay02s(void)
{
unsigned char i;
for(i=20;i>0;i--)
delay10ms();
}
/*主程序*/
void main(void)
{
INTCON=0;
GIE=1;// ;打开总中断
PEIE=1;// ;打开外部中断使能位
TMR1IE=1;// ;打开TMR1中断
TRISD=0X0F0;
PORTD=0X00;
T1CON=0x01;//设置TMR1的控制字
RD0=0; //脉冲波形起始状态
TMR1IF=0;
FLAG=0;
TL01=0X23; //TL01=0x2E; //舵机0 位,1.5ms定时初始值 TH01=0X0FA; //TH01=0x0FA;
TL02=0X0CC; //TL02=0x0B2; //8.5ms定时初始值
TH02=0X0DE; //TH02=0x0B7;
while(1)
{
if(RD7==0)
{
delay10ms(); //按下,延时,防抖
if(RD7==0)
{ //等待释放
delay02s();
A=TL01;
if(A!=0xFF) //判断是否到+90°
{
C=0;
A=A-20;
if(A<=0)
{A+=255;C=1;}
if(C==0)
{
TL01=A;
C=0;
A=TL02;
A=A+20;
if(A>=255)
{A-=255;C=1;}
if(C==0)
{TL02=A;}
else
{TH02++;TL02=A;C=0;}
}
else
{
TH01--;
C=0;
A=TL02;
A=A+20;
if(A>=255)
{A-=255;C=1;}
if(C==0)
{TL02=A;}
else
{TH02++;TL02=A;C=0;}
}
}
}
}
if(RD6==0)
{
delay10ms(); //按下,延时,防抖 if(RD6==0)
{ //等待释放
delay02s();
A=TL01;
if(A!=0x22) //判断是否到-90°
{
C=0;
A=A+20;
if(A>=255)
{A-=255;C=1;}
if(C==0)
{
TL01=A;
C=0;
A=TL02;
A=A-20;
if(A<=0)
{A+=255;C=1;}
if(C==0)
{TL02=A;}
else
{TH02--;TL02=A;C=0;}
}
else
{
TH01++;
TL01=A;
A=TL02;
A=A-20;
if(A<=0)
{A+=255;C=1;}
if(C==0)
{TL02=A;}
else
{TH02--;TL02=A;C=0;}
}
}
}
}
if(RD5==0)
{
delay10ms(); //按下,延时,防抖
if(RD5==0)
{ //等待释放
delay02s();
TL01=0X23; //TL01=0x2E; //舵机0 位,1.5ms定时初始值 TH01=0X0FA; //TH01=0x0FA;
TL02=0X0CC; //TL02=0x0B2; //8.5ms定时初始值
TH02=0X0DE; //TH02=0x0B7;
}
}
}
}
/*中断服务程序*/
void interrupt timer1(void)
{
TMR1IF=~TMR1IF;
FLAG=~FLAG;
RD0=~RD0; //输出高、低电平脉冲
if(FLAG==1)
{
TMR1L=TL01; //高电平脉冲定时常数
TMR1H=TH01;
}
else
{
TMR1L=TL02; //低电平脉冲定时常数
TMR1H=TH02;
}
}。