|
悬挂运动控制系统
本系统采用AT89S52作为控制中心,由直流步进电机、发光二极管、光敏电阻、4*4键盘等构成的悬挂运动控制系统。实现自由控制悬挂物体完成自行设定运动、画圆运动、沿黑线运动等。
用步进电机实现物体的精确定位和方向控制。步进电机是一种脉冲控制电机,它是一种能将脉冲信号转换为角位移的数模转换器,可广泛用于无需反馈控制但要求有精确位置的场合。
根据计算机图形学中直线的显示方法改变而来,基本原理也是"逐点比较",执行机构根据当前位置和轨迹位置的关系,从而确定下一步的进给方向,但是数据的处理过程不同。在直线插补法中,一次循环只能确定一个走向(X向或Y向),而在直线简易算法中,一次循环可以走两步,这样可以大大提高效率。同时,直线插补法要考虑象限的问题,不同的象限有不同的计算公式,而直线简易算法绕开了象限的问题,可以节省很多代码。
采用多路阵列式光敏电阻组成的光电探测器。因为光敏电阻探测到黑线时,黑线上方的电阻值发生变化,经过电压比较器比较将信号送给单片机处理,从而控制物体做相应的动作。光敏电阻对环境光的识别,要求考虑外界环境光的影响,测试时可能在室内或室外,为了消除外界光照强度的干扰,在每个光敏电阻旁边加了一个高亮度发光二极管,这样每个光敏电阻的环境一样,即使在黑暗的条件下也可以正常工作。测试结果表明使用这种方法就可以消除外界光的干扰。
采用LED数码管显示器。LED 数码管亮度高,醒目,程序设计简单。
系统的总体设计方案:
电机驱动电路的设计与实现:
该电路采用L298驱动芯片,L298驱动芯片是性能优越的小型直流电机驱动芯片之一。它可被用来驱动两个直流电机或者是一个步进电机。在4--46V的电压下,可以提供2A的驱动电流。L298还有过热自动关断功能,并有反馈电流检测功能,符合电机驱动的需要。
由于采用的是步进电机,所以对电机的驱动必须是采用脉冲控制。
本作品中的控制系统采用5V电源,电机驱动L298的电源也使用5V。基于稳定性考虑,我们运用了TLP521光耦集成块,将主控制部分电源与电机驱动部分的电源隔离开来,这样减少电机对主控制电路的干扰。
黑线探测设计与实现:
利用该模块探测板面黑线的原理是:光线照射到板面并反射,由于黑线和白纸的反射系数不同,黑线上方的电阻值发生变化,经过电压比器比较将信号送给单片机处理。
1,3为光敏电阻
黑色引导线 2,4为光敏电阻
利用光敏电阻在不同的光照的条件下电阻变化的原理。根据第几路的光敏检测到黑线来控制步进电机的转向。将光敏电阻分为前、后、左和右四个方向,设计为'+'字形。 采用一组两个探测头,当出现一个探测头的误判时,可以通过软件禁止物体跑出轨迹。当探测头1检测到黑线时,物体左走,同时禁止物体右转防止跑出黑线,直到中间的探测头2或探测头4再次检测到黑线证明物体已经回到黑线上才向前走,这样就可以保证物体不会跑出黑线。
由于在正常状态下每个光敏电阻感光量相同,通过调节电位器,使得电压比较器输出为零,当内侧(黑线两侧)的光敏电阻进入黑色引导带时,感光量大大改变,电压比较器翻转电压为高电平。将电平变化送到单片机控制物体的调整方向。用这种方法即使板面受到不同程度的光照射,比较器正向输入端和反向输入端的变化值相等,比较器输出端不变。只有黑色引导线进入内侧一组光敏电阻区域才能引起感光量大大改变,比较器才翻转,这种方法抗干扰能力强。
电路图:
本人写的程序:(目前只实现了部分功能)
#include "reg52.h"
#include "math.h"
sbit P1_0=P1^0;//A步进电机端口
sbit P1_1=P1^1;//A步进电机端口
sbit P1_2=P1^2;//A步进电机端口
sbit P1_3=P1^3;//A步进电机端口
sbit P1_4=P1^4;//B步进电机端口
sbit P1_5=P1^5;//B步进电机端口
sbit P1_6=P1^6;//B步进电机端口
sbit P1_7=P1^7;//B步进电机端口
bit atzqd=0; //A电机停止启动 默认停止
bit btzqd=0; //B电机停止启动 默认停止
bit fz=0; //是否给x,y赋值 默认否
bit qr=0; //赋值后是否确认 默认否
unsigned char m,n,k,g,u=1,v=1;
float a=0; //计算得到的A电机步数
float b=0; //计算得到的B步进电机的步数
float L1,L2,L3,L4; //定义线的长度
bit azf=0; //A运转方向
bit bzf=0; //B运转方向
bit yx=1; //按键重复允许 默认否
float r; //给x,y赋值的中间变量
unsigned char p=0,q=0; //赋值和确认中间标志
float x1=0,x2=0; //x坐标
float y1=0,y2=0; //y坐标
int s=20;//定义转动速度,数值越大电机转速越慢反之则快
code unsigned char arunz[4]={0xef,0xdf,0xbf,0x7f}; //A电机正转
code unsigned char arunf[4]={0x7f,0xbf,0xdf,0xef}; //A电机反转
code unsigned char brunf[4]={0xfe,0xfd,0xfb,0xf7}; //B电机正转
code unsigned char brunz[4]={0xf7,0xfb,0xfd,0xfe}; //B电机反转
//unsigned char code[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
void delay10ms(void) //延时程序 键盘的延时
{
unsigned char i,j;
for(i=20;i>0;i--)
for(j=248;j>0;j--);
}
void delay(m)//延时函数 步进电机的延时
{
for(n=0;n
for(k=0;k<50;k++);
}
void azrun()// A正转运行
{
for(g=0;g<4;g++)
{
P1=arunz[g];
delay(s);
}
}
void afrun()// A反转运行
{
for(g=0;g<4;g++)
{
P1=arunf[g];
delay(s);
}
}
void bzrun()// B正转运行
{
for(g=0;g<4;g++)
{
P1=brunz[g];
delay(s);
}
}
void bfrun()// B反转运行
{
for(g=0;g<4;g++)
{
P1=brunf[g];
delay(s);
}
}
void fuzhi() //x,y赋值函数
{
if(q==0) //起点x的值
{
if(p==0) x1=r; //一位数
if(p==1) x1=x1*10+r; //两位数
p++;
}
if(q==1) //起点y的值
{
if(p==0) y1=r; //一位数
if(p==1) y1=y1*10+r; //两位数
L1=sqrt((x1+3)*(x1+3)+(43-y1)*(43-y1)); //求L1长度
L2=sqrt((43-x1)*(43-x1)+(43-y1)*(43-y1)); //求L2长度
p++;
}
if(q==2) //终点x的值
{
if(p==0) x2=r; //一位数
if(p==1) x2=x2*10+r; //两位数
p++;
}
if(q==3) //终点y的值
{
if(p==0) y2=r; //一位数
if(p==1) y2=y2*10+r; //两位数
L3=sqrt((x2+3)*(x2+3)+(43-y2)*(43-y2)); //求L3长度
L4=sqrt((43-x2)*(43-x2)+(43-y2)*(43-y2)); //求L4长度
if(L3>L1) {azf=1;a=12*(L3-L1);} //
if(L3
if(L4>L2) {bzf=1;b=12*(L4-L2);} //
if(L4
p++;
}
fz=0; //运行完毕后跳出
}
void queren() //确认函数 每输入一个坐标点的x或y值就确认一下
{
p=0;
q++;
if(q==4) q=0;
qr=0; //运行完毕后跳出
}
void Getch() //矩阵键盘程序
{
unsigned char X,Y,Z;
P2=0xff;
P2=0x0f; //先对P2置数 行扫描
if(P2!=0x0f) //判断是否有键按下
{delay10ms(); //延时,软件去干扰
if(P2!=0x0f) //确认按键按下X = P2;
{
X=P2; //保存行扫描时有键按下时状态
P2=0xf0; //列扫描
Y=P2; //保存列扫描时有键按下时状态
Z=X|Y; //取出键值
switch(Z) //判断键值(那一个键按下)
{
case 0xee: {if(yx==1){r=0;fz=1;yx=0;}} ; break; //0对键值赋值
case 0xde: {if(yx==1){r=1;fz=1;yx=0;}} ; break; //1
case 0xbe: {if(yx==1){r=2;fz=1;yx=0;}} ; break; //2
case 0x7e: {if(yx==1){r=3;fz=1;yx=0;}} ; break; //3
case 0xed: {if(yx==1){r=4;fz=1;yx=0;}} ; break; //4
case 0xdd: {if(yx==1){r=5;fz=1;yx=0;}} ; break; //5
case 0xbd: {if(yx==1){r=6;fz=1;yx=0;}} ; break; //6
case 0x7d: {if(yx==1){r=7;fz=1;yx=0;}} ; break; //7
case 0xeb: {if(yx==1){r=8;fz=1;yx=0;}} ; break; //8
case 0xdb: {if(yx==1){r=9;fz=1;yx=0;}} ; break; //9
case 0xbb: {if(yx==1){qr=1;yx=0;}} ; break; //A
case 0x7b: {if((yx==1)&&(q==0)){atzqd=1;btzqd=1;yx=0;}} ; break; //B
case 0xe7: {azf=0;azrun();P1=0Xff;}; break; //C
case 0xd7: {azf=1;afrun();P1=0Xff;}; break; //D
case 0xb7: {bzf=0;bfrun();P1=0Xff;}; break; //E
case 0x77: {bzf=1;bzrun();P1=0Xff;}; break; //F
}
}
}
else yx=1;
}
void yunxing() //步进电机运行函数
{
unsigned char i,j;
if(atzqd==1) //A步进电机
{
if(a>0) //步骤未走完
for(i=u;i>0;i--){
if(azf==0) azrun(); //正转
if(azf==1) afrun(); //反转
a--;
}
else { //步骤已走完
atzqd=0;
P1=0xff; //端口置1,防止电流过大
}
}
if(btzqd==1) //B步进电机
{
if(b>0) //步骤未走完
for(j=v;j>0;j--){
if(bzf==0) bzrun(); //正转
if(bzf==1) bfrun(); //反转
b--;
}
else { //步骤已走完
btzqd=0;
P1=0xff; //端口置1,防止电流过大
}
}
if(a>b){u=a/b;v=1;}
if(a
}
void main(void)
{
while(1)
{
Getch();
if(fz==1) fuzhi();
if(qr==1) queren();
yunxing();
}
}
[ 本帖最后由 TSB33 于 2009-6-7 11:40 编辑 ]
|
|