1、 单 片 机 控 制 舵 机附: 单片机控制 8 路舵机程序,串口通讯上位机程序 (C#)一、 设计内容综述机械臂主要由手部和运动机构组成。手部是用来抓持工件的部件,运动机构使手部完成各种转动、移动或复合运动来实现规定的动作。为了抓取空间中任意位置和方位的物体,需有 6 个自由度,用六个舵机来控制。由单片机产生六路占空比可调的 PWM 信号来控制机械手的运动。利用上位机与单片机通信,改变占空比从而控制机械臂。为了使机械手运动时保持一定的连贯性,同时刻到达指定位置,机械手不同部位运动的速度应该不同,转一个小角度时舵机的速度应该慢一些,从而达到柔性控制。 二、所使用的关键器件和基本参数舵机 TR2
2、13:1.速度:0.15 秒/60 度(4.8V);0.12 秒/60 度(6.0V)2.扭矩:13kgcm3.范围:1804.控制精度:0.55.精度:精度高,步进角度很小MAX232:1.符合所有的 RS-232C 技术标准 2.只需要单一 +5V 电源供电,片载电荷泵具有升压、电压极性反转能力,能够产生+10V 和-10V 电压 V+、V-3.功耗低,典型供电电流 5mA 4.内部集成两个 RS-232C 驱动器,两个 RS-232C 接收器三、工作原理说明及计算1、PC 机与单片机通信在工业控制系统中,各种数据的采集和执行机构的控制都是由下位机来完成。由于单片机具有体积小、价格低廉、可
3、应用于恶劣工业环境的特点,在分布式控制系统中大多采用单片机作为下位机来进行数据采集和现场控制。在这些应用中,单片机只是直接面向被控对象底层。而对采集到的数据进行进一步分析和处理的工作是由功能强大的主控 PC 机来完成的。因此,PC 机和单片机之间就有着大量的数据交换。通常 PC 机和单片机之间的通信是通过串行总线 RS-232 实现的。因此采用一种以MAX232 为核心的通信接口电路。该接口电路适用于由一台 PC 机与多个单片机串行通信的设计,其原理和方法同样适用于 PC 机与其它单片机之间的串行数据通信。该框图中,起着重要作用的是 RS-232C 通信接口电路。它是上位机和下位机之间信息传递
4、的枢纽,一切数据的传输必需由它完成,上位机利用它的 RS-232 串行口,为此,采用了 RS-232 串行通信来接收或上传数据和指令。但 RS-232 信号的电平和单片机串口信号的电平不一致,必须进行二者之间的电平转换。在此电路中,采用 MAX232 实现 TTL 逻辑电平和 RS-232 电平之间的相互转换。 MAX232 由单一的+5V 电源供电,只需配接 5 个高精度 10F/50V 的钽电容即可完成电平转换。转换后的串行信号 TXD、RXD 直接与 PC 机的串行口连接。如此设计,既可发挥出 PC 强大的计算和显示功能,又可以体现出单片机灵活的控制功能,有利于对现场信号的实时采集、处理
5、和监控。串口通讯软件RS-232C2、舵机的结构舵机简单的说就是集成了直流电机、电机控制器和减速器等,并封装在一个便于安装的外壳里的伺服单元。能够利用简单的输入信号比较精确的转动给定角度的电机系统。舵机安装了一个电位器(或其它角度传感器)检测输出轴转动角度,控制板根据电位器的信息能比较精确的控制和保持输出轴的角度。这样的直流电机控制方式叫闭环控制,所以舵机更准确的说是伺服马达,英文servo。舵机的主体结构有五个部分:外壳、减速齿轮组、电机、电位器、控制电路。工作原理:控制电路接受来自信号线的控制信号,控制电机转动,电机带动一系列齿轮组,减速后传动至输出舵盘。舵机的输出轴和位置反馈电位计是相连
6、的,舵盘转动的同时,带动位置反馈电位计,电位计将输出一个电压信号到控制电路板,进行反馈,然后控制电路板,根据所在位置决定电机的转动方向和速度,从而达到目标停止。舵机的输入线共有三条,红色中间,是电源线,一边黑色的是地线,这辆根线给舵机提供最基本的能源保证,主要是电机的转动消耗。电源有两种规格,一是 4.8V,一是 6.0V,分别对应不同的转矩标准,即输出力矩不同,6.0V 对应的要大一些;另外一根线是控制信号线,一般为桔黄色。舵机的控制信号为周期是20ms 的脉宽调制(PWM)信号,其中脉冲宽度从 0.5ms-2.5ms,相对应舵盘的位置为 0180 度,呈线性变化。也就是说,给它提供一定的脉
7、宽,它的输出轴就会保持在一个相对应的角度上,无论外界转矩怎样改变,直到给它提供一个另外宽度的脉冲信号,它才会改变输出角度到新的对应的位置上。舵机内部有一个基准电路,产生周期 20ms,宽度 1.5ms 的基准信号,有一个比较器,将外加信号与基准信号相比较,判断出方向和大小,从而产生电机的转动信号。由此可见,舵机是一种位置伺服的驱动器,转动范围不能超过 180 度,适用于那些需要角度不断变化并可以保持的驱动当中。舵机的外壳一般是塑料的,特殊的舵机可能会有金属铝合金外壳。金属外壳能够提供更好的散热,可以让舵机内的电机运行在更高功率下,以提供更高的扭矩输出。金属外壳也可以提供更牢固的固定位置。FUT
8、ABA-S3003 型舵机的内部电路标准舵机示意图 舵机输出转角与输入信号脉冲宽度的关系正是因为舵机有很多优点,所以应用已经扩展到各种机电产品中来,在机器人控制中应用也越来越广泛。3、用单片机来控制舵机舵机的控制信号是一个脉宽调制信号,所以很方便和数字系统进行接口。只要能产生标准的控制信号的数字设备都可以用来控制舵机。我们使用单片机产生舵机的控制信号来进行控制的方法,编程语言为C51。单片机有两个(或两个以上)内部计数器,我们就用它来产生周期20 ms 的脉冲信号,根据需要,改变输出脉宽。脉冲信号的输出是靠定时器的溢出中断函数来处理,时间很短,因此在精度要求不高的场合可以忽略。因此如果忽略中断
9、时间,从另一个角度来讲就是主程序和脉冲输出是并行的,因此,只需要在主程序中按要求改变A值,例如让A从500 变化到2500,就可以让舵机从0 度变化到180 度。另外舵机的转动需要时间的,因此,程序中A值的变化不能太快,不然舵机跟不上程序。根据需要,选择合适的延时,用一个递增循环,可以让舵机很流畅的转动,而不会产生像步进电机一样的脉动。这些还需要实践中具体体会。舵机的速度决定于你给它的信号脉宽的变化速度。一般来讲,舵机最大转动速度在4.8V 时为0.23s/60 度,也就是说,如果你要求的速度比这个快的话,舵机就反应不过来了;如果要求速度比这个慢,可以将脉宽变化值线性到你要求的时间内,做一个循
10、环,逐渐的增加脉宽值,就可以控制舵机的速度了。当然,具体这个量到底是多少,就需要做试验了,不然的话,不合适的话,舵机就会向步进电机一样一跳一跳的转动了。还有一点很重要,就是舵机在每一次脉宽值改变的时候总会有一个转速由零增加再减速为零的过程,这就是舵机会产生像步进电机一样运动的原因。如果系统中需要控制几个舵机的准确转动,可以用单片机和计数器进行脉冲计数产生P W M 信号。脉冲计数可以利用51 单片机的内部计数器来实现,由于时间及专业限制,我们暂时使用这种方法。当系统的主要工作任务就是控制多个舵机的工作,并且使用的舵机工作周期均为20ms 时。要求硬件产生的多路P W M 波的周期也相同。使用5
11、1 单片机的内部定时器产生脉冲计数。一般工作正脉冲宽度小于周期的1/8 。这样可以在1个周期内分时启动各路P W M 波的上升沿。再利用定时器中断T0 确定各路PWM 波的输出宽度。定时器中断T1 控制20ms 的基准时间。第1 次定时器中断T0 按20ms 的1/8(由于数比较好算,所以用1/8)设置初值,并设置输出I/O 口,第1 次T0 定时中断响应后,将当前输出I/O 口对应的引脚输出置高电平,设置该路输出正脉冲宽度,并启动第2 次定时器中断,输出I/O口指向下一个输出口。第2 次定时器定时时间结束后,将当前输出引脚置低电平,设置此中断周期为20ms 的1/8减去正脉冲的时间,此路P
12、W M 信号在该周期中输出完毕,往复输出。在每次循环的第16次(2 *8=16)中断实行关定时中断T0的操作,最后就可以实现8路(实际上六路就可以完成机械臂的控制)舵机控制信号的输出。但是从软件系统的稳定性和程序结构的合理性看,宜使用外部的计数器,还可以提高CPU 的工作效率。采用单片机和8253、8254 这样的计数器芯片的PWM 信号产生电路是可靠的。基于8253 产生PWM 信号的程序主要包括三方面内容:一是定义8253寄存器的地址,二是控制字的写入,三是数据的写入软件。所以当系统需要产生多路P W M 信号时,使用上述方法可以减少电路降低成本,也可以达到较高的精度。调试时注意到由于程序
13、中脉冲宽度的调整是靠调整定时器的初值,中断程序也被分成了8个状态周期,并且需要严格的周期循环,而且运行其他中断程序代码的时间需要严格把握。在实际应用中,采用51单片机简单方便地实现了舵机控制需要的P W M 信号,对机器人舵机控制的测试表明舵机控制系统工作稳定,P W M 占空比(0.52.5ms 的正脉冲宽度)和舵机的转角(-90 90 )线性度较好。4、上位机上位机是指人可以直接发出控制命令的计算机,一般是PC。下位机是直接控制设备获取设备状况的计算机,一般是PLC/单片机之类的。上位机发出的命令首先给下位机,下位机再根据此命令解释成相应时序信号直接控制相应设备。下位机不时读取设备状态数据
14、(一般为模拟量),转换成数字信号反馈给上位机。为了在短时间内完成上位机编程,我们使用C#来制作串口通讯上位机。C#(C Sharp)是微软为.NET Framework量身订做的程序语言,C#拥有C/C+的强大功能以及Visual Basic简易使用的特性,是第一个组件导向的程序语言,和C+与Java一样亦为对象导向程序语言。四、调试中遇到问题及解决方法在进行串口调试过程中,我们发现电脑经常出现找不到串口的情况,无论重新安装驱动,还是重新启动,都只是维持很短暂的时间,然后又出现上述情况。经检查应该是硬件的问题,我们估计是单片机学习板的串口连接脱焊导致的。我们还发现学习板的 P0 及 P5 口被
15、其他的原件占用,无法直接从这两个口输出 PWM 信号,所以控制机械臂的接口只能跳过这两个,选用其他的。 在调试控制舵机时,因为最开始用的舵机已经坏了,所以无论怎么调试,舵机都无法转动。拿到新的舵机后,我们最先用信号发生器产生脉冲信号,测试舵机转动角度的范围,发现并不是 0-180,而是 150 度。虽然角度小了些,但还是能正常转动的。作为初期的练习控制舵机,基本满足要求,所以就没再换新的。由于机械臂的配合及机械物件连接不连贯,经常出现卡壳的状况,我们经过拧紧螺丝后,有所改善,但还是不够理想,希望以后可以买到更好的硬件。在调试的过程中,发现舵机的扭矩不是很大,可能是因为舵机过多,电流太小,无法达
16、到额定扭矩,所以电路最好是制作一个驱动电路,以提高机械臂性能。在进行程序调试时经常会出现问题,这与我们对程序的学习不够,有着直接的关系。在经过我们组成员一个学期的努力下,终于把程序完成,并不断改进。五、个人体会及心得本学期我们组在进行项目研究方面花费了很长时间,同时也遇到了很多困难,也学到了很多。由于专业的差异,我们研究的范围注定只局限于项目之内,即使这样我们的进度也是不尽如人意。主要原因还是在编程方面,虽然我们组成员都过了二级,但是还缺乏实际的动手编程经验,还需要加强这方面的能力。和以前做作业不同,本学期的项目需要每个队员的合作。有明确的分工,才会有效率,不能只等着别人。希望在下学期的努力学
17、习下,能够将我们组的项目成功的完成,学习到一些感兴趣的知识,不给自己留下遗憾。单片机控制8路舵机程序:; T1 serialport to high T2 pwmPWMOUT0 BIT P0.0PWMOUT1 BIT P0.1PWMOUT2 BIT P0.2PWMOUT3 BIT P0.3PWMOUT4 BIT P0.4PWMOUT5 BIT P0.5PWMOUT6 BIT P0.6PWMOUT7 BIT P0.7T2CON DATA 0C8HTF2 BIT T2CON.7EXF2 BIT T2CON.6RCLK BIT T2CON.5TCLK BIT T2CON.4EXEN2 BIT T2C
18、ON.3TR2 BIT T2CON.2C_T2 BIT T2CON.1CP_RL2 BIT T2CON.0T2 BIT P1.0T2EX BIT P1.1RCAP2L DATA 0CAHRCAP2H DATA 0CBHTL2 DATA 0CCHTH2 DATA 0CDHET2 BIT IE.5PT2 BIT IP.5/PWMWID EQU 048H; 16048H05FH; First low 8bitsthenhigh 8bitschannel EQU 032H;pha EQU 033H;/ORG 0000HLJMP MAINORG 000BHLJMP T0_ISRORG 002BHLJMP
19、 T2_ISRORG 0100HMAIN:MOV SP, 60HMOV 96H, #00HMOV 8EH, #00H; InitSETB EA; enable interruptSETB ET2CLR TCLKCLR RCLKMOV SCON, #050H; 8bitserialMOV TL2, #000H MOV TH2, #000HMOV RCAP2L, #03CH; 2.5msMOV RCAP2H, #0F6HCLR TF2SETB TR2CLR ES; disableserialinterruptCLR TI; clearserial FLAGMOV TMOD, #21HMOV TH0
20、, #000H; make time forT0_ISR torunCLR TF0SETB ET0CLR TR0MOV TL1,#0F3HMOV TH1,#0F3HSETB TR1CLR AMOV pha,AMOV A, #035HMOV PWMWID+00H, AMOV PWMWID+02H, AMOV PWMWID+04H, AMOV PWMWID+06H, AMOV PWMWID+08H, AMOV PWMWID+0AH, AMOV PWMWID+0CH, AMOV PWMWID+0EH, AMOV A, #0FAHMOV PWMWID+01H, AMOV PWMWID+03H, AMO
21、V PWMWID+05H, AMOV PWMWID+07H, AMOV PWMWID+09H, AMOV PWMWID+0BH, AMOV PWMWID+0DH, AMOV PWMWID+0FH, A; CommunicationCOMMU:JNB RI, COMMUCLR RIMOV channel,SBUFMOV SBUF,channelMOV A,channelJNB ACC.7, COMMUJB ACC.6, COMMUJNB RI, $ANL channel, #07FHMOV A, #PWMWIDADD A, channelADD A, channelMOV R0, AMOV R1
22、, SBUFCLR CMOV A, #0FFHSUBB A, R1; ADDC A, #12HMOV SBUF, AMOV R0, ACLR RIJNB RI, $MOV R1, SBUFMOV A, #0FFHSUBB A, R1INC R0MOV SBUF, AMOV R0, ACLR RISJMP COMMUT0_ISR:PUSH ACCCLR TR0MOV A,phaRL AMOV DPTR, #TABLEJMP A+DPTRTABLE:SJMP S0SJMP S1SJMP S2SJMP S3SJMP S4SJMP S5SJMP S6SJMP S7S0:CLR PWMOUT0POP A
23、CCRETIS1:CLR PWMOUT1POP ACCRETIS2:CLR PWMOUT2POP ACCRETIS3:CLR PWMOUT3POP ACCRETIS4:CLR PWMOUT4POP ACCRETIS5:CLR PWMOUT5POP ACCRETIS6:CLR PWMOUT6POP ACCRETIS7:CLR PWMOUT7POP ACCRETIT2_ISR:CLR TF2PUSH ACCINC phaANL pha, #07HMOV A,phaRL AMOV DPTR,#TABLE2JMP A+DPTRTABLE2:SJMP SS0SJMP SS1SJMP SS2SJMP SS
24、3SJMP SS4SJMP SS5SJMP SS6SJMP SS7SS0:MOV TH0,PWMWID+01HMOV TL0,PWMWID+00HSETB PWMOUT0SETB TR0POP ACCRETISS1:MOV TH0,PWMWID+03HMOV TL0,PWMWID+02HSETB PWMOUT1SETB TR0POP ACCRETISS2:MOV TH0,PWMWID+05HMOV TL0,PWMWID+04HSETB PWMOUT2SETB TR0POP ACCRETISS3:MOV TH0,PWMWID+07HMOV TL0,PWMWID+06HSETB PWMOUT3SE
25、TB TR0POP ACCRETISS4:MOV TH0, PWMWID+09HMOV TL0, PWMWID+08HSETB PWMOUT4SETB TR0POP ACCRETISS5:MOV TH0, PWMWID+0BHMOVTL0, PWMWID+0AHSETB PWMOUT5SETB TR0POP ACCRETISS6:MOV TH0, PWMWID+0DHMOV TL0, PWMWID+0CHSETB PWMOUT6SETB TR0POP ACCRETISS7:MOV TH0,PWMWID+0FHMOV TL0,PWMWID+0EHSETB PWMOUT7SETB TR0POP A
26、CCRETIEND上位机程序(C#):using System;using System.Collections.Generic;using System.ComponentModel;using System.Drawing;using System.Text;using System.Windows.Forms;namespace CoispublicpartialclassCois : Formprivatebyte P = newbyte4;privatebyte checker;public Cois()InitializeComponent();privatevoid Form1_
27、Load(object sender, EventArgs e)init();privatevoid Message() thrownewNotImplementedException();privatevoid init()GetPortName();this.CBx_baud.Text = global:Cois.Properties.Settings.Default.BaudRate;privatevoid GetPortName()string portNames = System.IO.Ports.SerialPort.GetPortNames();CBx_port.Items.Cl
28、ear();foreach (string name in portNames) CBx_port.Items.Add(name);if (!CBx_port.Items.Contains(CBx_port.Text) trythis.CBx_port.Text = (string)CBx_port.Items0;catchthis.CBx_port.Text = global:Cois.Properties.Settings.Default.PortName;privatevoid CBx_port_SelectedIndexChanged(object sender, EventArgs
29、e)serialPort.PortName = CBx_port.Text;privatevoid CBx_baud_SelectedIndexChanged(object sender, EventArgs e)serialPort.BaudRate =Convert.ToInt32( CBx_baud.Text);privatevoid write(int number) byte CMD = newbyte3;CMD0 = (byte)number;CMD0 |= (byte)0x80;switch(number)case 0: CMD1 = (byte) (Servo0.Value+1
30、7)%256);CMD2 = (byte)(Servo0.Value +17)/ 256);serialPort.Write(CMD, 0, 3);break;case 1: CMD1 = (byte)(Servo1.Value+17)%256);CMD2 = (byte)(Servo1.Value+17) / 256);serialPort.Write(CMD, 0, 3);break;case 2: CMD1 = (byte)(Servo2.Value+17)%256);CMD2 = (byte)(Servo2.Value +17)/ 256);serialPort.Write(CMD,
31、0, 3);break;case 3: CMD1 = (byte)(Servo3.Value+17)%256);CMD2 = (byte)(Servo3.Value+17) / 256);serialPort.Write(CMD, 0, 3);break;case 4: CMD1 = (byte)(Servo4.Value+17)%256);CMD2 = (byte)(Servo4.Value+17) / 256);serialPort.Write(CMD, 0, 3);break;case 5: CMD1 = (byte)(Servo5.Value+17)%256);CMD2 = (byte
32、)(Servo5.Value+17) / 256);serialPort.Write(CMD, 0, 3);break;case 6: CMD1 = (byte)(Servo6.Value+17)%256);CMD2 = (byte)(Servo6.Value+17) / 256);serialPort.Write(CMD, 0, 3);break;case 7: CMD1 = (byte)(Servo7.Value+17)%256);CMD2 = (byte)(Servo7.Value+17) / 256);serialPort.Write(CMD, 0, 3);break;privatev
33、oid Btn_connect_Click(object sender, EventArgs e)if (serialPort.IsOpen) Btn_connect.Text = “Unconnected“;Btn_connect.CheckState = System.Windows.Forms.CheckState.Unchecked;serialPort.Close();else try serialPort.Open();catch Btn_connect.CheckState = System.Windows.Forms.CheckState.Unchecked;if (seria
34、lPort.IsOpen) Btn_connect.Text = “Connected“;Btn_connect.CheckState = System.Windows.Forms.CheckState.Checked;write(0);write(1);write(2);write(3);write(4);write(5);write(6);write(7);privatevoid CBx_port_DropDown(object sender, EventArgs e)GetPortName();privatevoid Servo0_MouseUp(object sender, Mouse
35、EventArgs e)/if (serialPort.IsOpen) write(0);privatevoid Servo1_MouseUp(object sender, MouseEventArgs e)/if (serialPort.IsOpen) write(1);privatevoid Servo2_MouseUp(object sender, MouseEventArgs e) /if (serialPort.IsOpen) write(2);privatevoid Servo3_MouseUp(object sender, MouseEventArgs e) /if (seria
36、lPort.IsOpen) write(3);privatevoid Servo4_MouseUp(object sender, MouseEventArgs e)/if (serialPort.IsOpen) write(4);privatevoid Servo5_MouseUp(object sender, MouseEventArgs e)/if (serialPort.IsOpen) write(5); privatevoid Servo6_MouseUp(object sender, MouseEventArgs e)/if (serialPort.IsOpen) write(6);
37、 privatevoid Servo7_MouseUp(object sender, MouseEventArgs e) /if (serialPort.IsOpen) write(7); privatevoid Servo0_Scroll(object sender, EventArgs e) textBox1.Text = Servo0.Value.ToString();if (serialPort.IsOpen) write(0); privatevoid Servo1_Scroll(object sender, EventArgs e)if (serialPort.IsOpen) wr
38、ite(1);privatevoid Servo2_Scroll(object sender, EventArgs e)if (serialPort.IsOpen) write(2); privatevoid Servo3_Scroll(object sender, EventArgs e) if (serialPort.IsOpen) write(3);privatevoid Servo4_Scroll(object sender, EventArgs e) if (serialPort.IsOpen) write(4); privatevoid Servo5_Scroll(object sender, EventArgs e) if (serialPort.IsOpen) write(5); privatevoid Servo6_Scroll(object sender, EventArgs e) if (serialPort.IsOpen) write(6); privatevoid Servo7_Scroll(object sender, EventArgs e) if (serialPort.IsOpen) write(7);