单片机控制8路舵机DOC.docx
《单片机控制8路舵机DOC.docx》由会员分享,可在线阅读,更多相关《单片机控制8路舵机DOC.docx(20页珍藏版)》请在冰豆网上搜索。
单片机控制8路舵机DOC
单片机控制舵机
附:
单片机控制8路舵机程序,串口通讯上位机程序(C#)
一、设计内容综述
机械臂主要由手部和运动机构组成。
手部是用来抓持工件的部件,运动机构使手部完成各种转动、移动或复合运动来实现规定的动作。
为了抓取空间中任意位置和方位的物体,需有6个自由度,用六个舵机来控制。
由单片机产生六路占空比可调的PWM信号来控制机械手的运动。
利用上位机与单片机通信,改变占空比从而控制机械臂。
为了使机械手运动时保持一定的连贯性,同时刻到达指定位置,机械手不同部位运动的速度应该不同,转一个小角度时舵机的速度应该慢一些,从而达到柔性控制。
二、所使用的关键器件和基本参数
舵机TR213:
1.速度:
0.15秒/60度(4.8V);0.12秒/60度(6.0V)
2.扭矩:
13kg·cm
3.范围:
180°
4.控制精度:
0.5°
5.精度:
精度高,步进角度很小
MAX232:
1.符合所有的RS-232C技术标准
2.只需要单一+5V电源供电,片载电荷泵具有升压、电压极性反转能力,能够产生+10V和-10V电压V+、V-
3.功耗低,典型供电电流5mA
4.内部集成两个RS-232C驱动器,两个RS-232C接收器
三、工作原理说明及计算
1、PC机与单片机通信
在工业控制系统中,各种数据的采集和执行机构的控制都是由下位机来完成。
由于单片机具有体积小、价格低廉、可应用于恶劣工业环境的特点,在分布式控制系统中大多采用单片机作为下位机来进行数据采集和现场控制。
在这些应用中,单片机只是直接面向被控对象底层。
而对采集到的数据进行进一步分析和处理的工作是由功能强大的主控PC机来完成的。
因此,PC机和单片机之间就有着大量的数据交换。
通常PC机和单片机之间的通信是通过串行总线RS-232实现的。
因此采用一种以MAX232为核心的通信接口电路。
该接口电路适用于由一台PC机与多个单片机串行通信的设计,其原理和方法同样适用于PC机与其它单片机之间的串行数据通信。
该框图中,起着重要作用的是RS-232C通信接口电路。
它是上位机和下位机之间信息传递的枢纽,一切数据的传输必需由它完成,上位机利用它的RS-232串行口,为此,采用了RS-232串行通信来接收或上传数据和指令。
但RS-232信号的电平和单片机串口信号的电平不一致,必须进行二者之间的电平转换。
在此电路中,采用MAX232实现TTL逻辑电平和RS-232电平之间的相互转换。
MAX232由单一的+5V电源供电,只需配接5个高精度10μF/50V的钽电容即可完成电平转换。
转换后的串行信号TXD、RXD直接与PC机的串行口连接。
如此设计,既可发挥出PC强大的计算和显示功能,又可以体现出单片机灵活的控制功能,有利于对现场信号的实时采集、处理和监控。
串口通讯软件
2、舵机的结构
舵机简单的说就是集成了直流电机、电机控制器和减速器等,并封装在一个便于安装的外壳里的伺服单元。
能够利用简单的输入信号比较精确的转动给定角度的电机系统。
舵机安装了一个电位器(或其它角度传感器)检测输出轴转动角度,控制板根据电位器的信息能比较精确的控制和保持输出轴的角度。
这样的直流电机控制方式叫闭环控制,所以舵机更准确的说是伺服马达,英文servo。
舵机的主体结构有五个部分:
外壳、减速齿轮组、电机、电位器、控制电路。
工作原理:
控制电路接受来自信号线的控制信号,控制电机转动,电机带动一系列齿轮组,减速后传动至输出舵盘。
舵机的输出轴和位置反馈电位计是相连的,舵盘转动的同时,带动位置反馈电位计,电位计将输出一个电压信号到控制电路板,进行反馈,然后控制电路板,根据所在位置决定电机的转动方向和速度,从而达到目标停止。
舵机的输入线共有三条,红色中间,是电源线,一边黑色的是地线,这辆根线给舵机提供最基本的能源保证,主要是电机的转动消耗。
电源有两种规格,一是4.8V,一是6.0V,分别对应不同的转矩标准,即输出力矩不同,6.0V对应的要大一些;另外一根线是控制信号线,一般为桔黄色。
舵机的控制信号为周期是20ms的脉宽调制(PWM)信号,其中脉冲宽度从0.5ms-2.5ms,相对应舵盘的位置为0-180度,呈线性变化。
也就是说,给它提供一定的脉宽,它的输出轴就会保持在一个相对应的角度上,无论外界转矩怎样改变,直到给它提供一个另外宽度的脉冲信号,它才会改变输出角度到新的对应的位置上。
舵机内部有一个基准电路,产生周期20ms,宽度1.5ms的基准信号,有一个比较器,将外加信号与基准信号相比较,判断出方向和大小,从而产生电机的转动信号。
由此可见,舵机是一种位置伺服的驱动器,转动范围不能超过180度,适用于那些需要角度不断变化并可以保持的驱动当中。
舵机的外壳一般是塑料的,特殊的舵机可能会有金属铝合金外壳。
金属外壳能够提供更好的散热,可以让舵机内的电机运行在更高功率下,以提供更高的扭矩输出。
金属外壳也可以提供更牢固的固定位置。
FUTABA-S3003型舵机的内部电路
标准舵机示意图舵机输出转角与输入信号脉冲宽度的关系
正是因为舵机有很多优点,所以应用已经扩展到各种机电产品中来,在机器人控制中应用也越来越广泛。
3、用单片机来控制舵机
舵机的控制信号是一个脉宽调制信号,所以很方便和数字系统进行接口。
只要能产生标准的控制信号的数字设备都可以用来控制舵机。
我们使用单片机产生舵机的控制信号来进行控制的方法,编程语言为C51。
单片机有两个(或两个以上)内部计数器,我们就用它来产生周期20ms的脉冲信号,根据需要,改变输出脉宽。
脉冲信号的输出是靠定时器的溢出中断函数来处理,时间很短,因此在精度要求不高的场合可以忽略。
因此如果忽略中断时间,从另一个角度来讲就是主程序和脉冲输出是并行的,因此,只需要在主程序中按要求改变A值,例如让A从500变化到2500,就可以让舵机从0度变化到180度。
另外舵机的转动需要时间的,因此,程序中A值的变化不能太快,不然舵机跟不上程序。
根据需要,选择合适的延时,用一个递增循环,可以让舵机很流畅的转动,而不会产生像步进电机一样的脉动。
这些还需要实践中具体体会。
舵机的速度决定于你给它的信号脉宽的变化速度。
一般来讲,舵机最大转动速度在4.8V时为0.23s/60度,也就是说,如果你要求的速度比这个快的话,舵机就反应不过来了;如果要求速度比这个慢,可以将脉宽变化值线性到你要求的时间内,做一个循环,逐渐的增加脉宽值,就可以控制舵机的速度了。
当然,具体这个量到底是多少,就需要做试验了,不然的话,不合适的话,舵机就会向步进电机一样一跳一跳的转动了。
还有一点很重要,就是舵机在每一次脉宽值改变的时候总会有一个转速由零增加再减速为零的过程,这就是舵机会产生像步进电机一样运动的原因。
如果系统中需要控制几个舵机的准确转动,可以用单片机和计数器进行脉冲计数产生PWM信号。
脉冲计数可以利用51单片机的内部计数器来实现,由于时间及专业限制,我们暂时使用这种方法。
当系统的主要工作任务就是控制多个舵机的工作,并且使用的舵机工作周期均为20ms时。
要求硬件产生的多路PWM波的周期也相同。
使用51单片机的内部定时器产生脉冲计数。
一般工作正脉冲宽度小于周期的1/8。
这样可以在1个周期内分时启动各路PWM波的上升沿。
再利用定时器中断T0确定各路PWM波的输出宽度。
定时器中断T1控制20ms的基准时间。
第1次定时器中断T0按20ms的1/8(由于数比较好算,所以用1/8)设置初值,并设置输出I/O口,第1次T0定时中断响应后,将当前输出I/O口对应的引脚输出置高电平,设置该路输出正脉冲宽度,并启动第2次定时器中断,输出I/O口指向下一个输出口。
第2次定时器定时时间结束后,将当前输出引脚置低电平,设置此中断周期为20ms的1/8减去正脉冲的时间,此路PWM信号在该周期中输出完毕,往复输出。
在每次循环的第16次(2*8=16)中断实行关定时中断T0的操作,最后就可以实现8路(实际上六路就可以完成机械臂的控制)舵机控制信号的输出。
但是从软件系统的稳定性和程序结构的合理性看,宜使用外部的计数器,还可以提高CPU的工作效率。
采用单片机和8253、8254这样的计数器芯片的PWM信号产生电路是可靠的。
基于8253产生PWM信号的程序主要包括三方面内容:
一是定义8253寄存器的地址,二是控制字的写入,三是数据的写入软件。
所以当系统需要产生多路PWM信号时,使用上述方法可以减少电路降低成本,也可以达到较高的精度。
调试时注意到由于程序中脉冲宽度的调整是靠调整定时器的初值,中断程序也被分成了8个状态周期,并且需要严格的周期循环,而且运行其他中断程序代码的时间需要严格把握。
在实际应用中,采用51单片机简单方便地实现了舵机控制需要的PWM信号,对机器人舵机控制的测试表明舵机控制系统工作稳定,PWM占空比(0.5~2.5ms的正脉冲宽度)和舵机的转角(-90~90)线性度较好。
4、上位机
上位机是指人可以直接发出控制命令的计算机,一般是PC。
下位机是直接控制设备获取设备状况的计算机,一般是PLC/单片机之类的。
上位机发出的命令首先给下位机,下位机再根据此命令解释成相应时序信号直接控制相应设备。
下位机不时读取设备状态数据(一般为模拟量),转换成数字信号反馈给上位机。
为了在短时间内完成上位机编程,我们使用C#来制作串口通讯上位机。
C#(CSharp)是微软为.NETFramework量身订做的程序语言,C#拥有C/C++的强大功能以及VisualBasic简易使用的特性,是第一个组件导向的程序语言,和C++与Java一样亦为对象导向程序语言。
四、调试中遇到问题及解决方法
在进行串口调试过程中,我们发现电脑经常出现找不到串口的情况,无论重新安装驱动,还是重新启动,都只是维持很短暂的时间,然后又出现上述情况。
经检查应该是硬件的问题,我们估计是单片机学习板的串口连接脱焊导致的。
我们还发现学习板的P0及P5口被其他的原件占用,无法直接从这两个口输出PWM信号,所以控制机械臂的接口只能跳过这两个,选用其他的。
在调试控制舵机时,因为最开始用的舵机已经坏了,所以无论怎么调试,舵机都无法转动。
拿到新的舵机后,我们最先用信号发生器产生脉冲信号,测试舵机转动角度的范围,发现并不是0-180,而是150度。
虽然角度小了些,但还是能正常转动的。
作为初期的练习控制舵机,基本满足要求,所以就没再换新的。
由于机械臂的配合及机械物件连接不连贯,经常出现卡壳的状况,我们经过拧紧螺丝后,有所改善,但还是不够理想,希望以后可以买到更好的硬件。
在调试的过程中,发现舵机的扭矩不是很大,可能是因为舵机过多,电流太小,无法达到额定扭矩,所以电路最好是制作一个驱动电路,以提高机械臂性能。
在进行程序调试时经常会出现问题,这与我们对程序的学习不够,有着直接的关系。
在经过我们组成员一个学期的努力下,终于把程序完成,并不断改进。
五、个人体会及心得
本学期我们组在进行项目研究方面花费了很长时间,同时也遇到了很多困难,也学到了很多。
由于专业的差异,我们研究的范围注定只局限于项目之内,即使这样我们的进度也是不尽如人意。
主要原因还是在编程方面,虽然我们组成员都过了二级,但是还缺乏实际的动手编程经验,还需要加强这方面的能力。
和以前做作业不同,本学期的项目需要每个队员的合作。
有明确的分工,才会有效率,不能只等着别人。
希望在下学期的努力学习下,能够将我们组的项目成功的完成,学习到一些感兴趣的知识,不给自己留下遗憾。
单片机控制8路舵机程序:
;T1serialporttohighT2pwm
PWMOUT0BITP0.0
PWMOUT1BITP0.1
PWMOUT2BITP0.2
PWMOUT3BITP0.3
PWMOUT4BITP0.4
PWMOUT5BITP0.5
PWMOUT6BITP0.6
PWMOUT7BITP0.7
T2CONDATA0C8H
TF2BITT2CON.7
EXF2BITT2CON.6
RCLKBITT2CON.5
TCLKBITT2CON.4
EXEN2BITT2CON.3
TR2BITT2CON.2
C_T2BITT2CON.1
CP_RL2BITT2CON.0
T2BITP1.0
T2EXBITP1.1
RCAP2LDATA0CAH
RCAP2HDATA0CBH
TL2DATA0CCH
TH2DATA0CDH
ET2BITIE.5
PT2BITIP.5
//////////////////////////////////////////
PWMWIDEQU048H
;[16]048H~05FH
;Firstlow8bitsthenhigh8bits
channelEQU032H;
phaEQU033H;
//////////////////////////////////////////
ORG0000H
LJMPMAIN
ORG000BH
LJMPT0_ISR
ORG002BH
LJMPT2_ISR
ORG0100H
MAIN:
MOVSP,60H
MOV96H,#00H
MOV8EH,#00H
;Init
SETBEA
;enableinterrupt
SETBET2
CLRTCLK
CLRRCLK
MOVSCON,#050H
;8bitserial
MOVTL2,#000H
MOVTH2,#000H
MOVRCAP2L,#03CH
;2.5ms
MOVRCAP2H,#0F6H
CLRTF2
SETBTR2
CLRES
;disableserialinterrupt
CLRTI
;clearserialFLAG
MOVTMOD,#21H
MOVTH0,#000H
;maketimeforT0_ISRtorun
CLRTF0
SETBET0
CLRTR0
MOVTL1,#0F3H
MOVTH1,#0F3H
SETBTR1
CLRA
MOVpha,A
MOVA,#035H
MOVPWMWID+00H,A
MOVPWMWID+02H,A
MOVPWMWID+04H,A
MOVPWMWID+06H,A
MOVPWMWID+08H,A
MOVPWMWID+0AH,A
MOVPWMWID+0CH,A
MOVPWMWID+0EH,A
MOVA,#0FAH
MOVPWMWID+01H,A
MOVPWMWID+03H,A
MOVPWMWID+05H,A
MOVPWMWID+07H,A
MOVPWMWID+09H,A
MOVPWMWID+0BH,A
MOVPWMWID+0DH,A
MOVPWMWID+0FH,A
;Communication
COMMU:
JNBRI,COMMU
CLRRI
MOVchannel,SBUF
MOVSBUF,channel
MOVA,channel
JNBACC.7,COMMU
JBACC.6,COMMU
JNBRI,$
ANLchannel,#07FH
MOVA,#PWMWID
ADDA,channel
ADDA,channel
MOVR0,A
MOVR1,SBUF
CLRC
MOVA,#0FFH
SUBBA,R1
;ADDCA,#12H
MOVSBUF,A
MOV@R0,A
CLRRI
JNBRI,$
MOVR1,SBUF
MOVA,#0FFH
SUBBA,R1
INCR0
MOVSBUF,A
MOV@R0,A
CLRRI
SJMPCOMMU
T0_ISR:
PUSHACC
CLRTR0
MOVA,pha
RLA
MOVDPTR,#TABLE
JMP@A+DPTR
TABLE:
SJMPS0
SJMPS1
SJMPS2
SJMPS3
SJMPS4
SJMPS5
SJMPS6
SJMPS7
S0:
CLRPWMOUT0
POPACC
RETI
S1:
CLRPWMOUT1
POPACC
RETI
S2:
CLRPWMOUT2
POPACC
RETI
S3:
CLRPWMOUT3
POPACC
RETI
S4:
CLRPWMOUT4
POPACC
RETI
S5:
CLRPWMOUT5
POPACC
RETI
S6:
CLRPWMOUT6
POPACC
RETI
S7:
CLRPWMOUT7
POPACC
RETI
T2_ISR:
CLRTF2
PUSHACC
INCpha
ANLpha,#07H
MOVA,pha
RLA
MOVDPTR,#TABLE2
JMP@A+DPTR
TABLE2:
SJMPSS0
SJMPSS1
SJMPSS2
SJMPSS3
SJMPSS4
SJMPSS5
SJMPSS6
SJMPSS7
SS0:
MOVTH0,PWMWID+01H
MOVTL0,PWMWID+00H
SETBPWMOUT0
SETBTR0
POPACC
RETI
SS1:
MOVTH0,PWMWID+03H
MOVTL0,PWMWID+02H
SETBPWMOUT1
SETBTR0
POPACC
RETI
SS2:
MOVTH0,PWMWID+05H
MOVTL0,PWMWID+04H
SETBPWMOUT2
SETBTR0
POPACC
RETI
SS3:
MOVTH0,PWMWID+07H
MOVTL0,PWMWID+06H
SETBPWMOUT3
SETBTR0
POPACC
RETI
SS4:
MOVTH0,PWMWID+09H
MOVTL0,PWMWID+08H
SETBPWMOUT4
SETBTR0
POPACC
RETI
SS5:
MOVTH0,PWMWID+0BH
MOVTL0,PWMWID+0AH
SETBPWMOUT5
SETBTR0
POPACC
RETI
SS6:
MOVTH0,PWMWID+0DH
MOVTL0,PWMWID+0CH
SETBPWMOUT6
SETBTR0
POPACC
RETI
SS7:
MOVTH0,PWMWID+0FH
MOVTL0,PWMWID+0EH
SETBPWMOUT7
SETBTR0
POPACC
RETI
END
上位机程序(C#):
usingSystem;
usingSystem.Collections.Generic;
usingSystem.ComponentModel;
usingSystem.Drawing;
usingSystem.Text;
usingSystem.Windows.Forms;
namespaceCois
{
publicpartialclassCois:
Form
{
privatebyte[]P=newbyte[4];
privatebytechecker;
publicCois()
{
InitializeComponent();
}
privatevoidForm1_Load(objectsender,EventArgse)
{
init();
}
privatevoidMessage()
{thrownewNotImplementedException();
}
privatevoidinit()
{
GetPortName();
this.CBx_baud.Text=global:
:
Cois.Properties.Settings.Default.BaudRate;
}
privatevoidGetPortName()
{
string[]portNames=System.IO.Ports.SerialPort.GetPortNames();
CBx_port.Items.Clear();
foreach(stringnameinportNames)
{CBx_port.Items.Add(name);
}
if(!
CBx_port.Items.Contains(CBx_port.Text))
{try
{
this.CBx_port.Text=(string)CBx_port.Items[0];
}
catch
{
this.CBx_port.Text=global:
:
Cois.Properties.Settings.Default.PortName;
}
}
}
privatevoidCBx_port_SelectedIndexChanged(objectsender,EventArgse)
{
serialPort.PortName=CBx_port.Text;
}
privatevoidCBx_baud_SelectedIndexChanged(objectsender,EventArgse)
{
serialPort.BaudRate=Convert.ToInt32(CBx_baud.Text);
}
privatevoidwrite(intnumber)
{byte[]CMD=newbyte[3];
CMD[0]=(byte)number;
CMD[0]|=(byte)0x80;
switch(number)
{
case0:
CMD[1]=(byte)((Servo0.Value+17)%256);
CMD[2]=(byte)((Servo0.Value+17)/256);
serialP