悬挂运动控制系统
参赛队员:吴亚军 吴怀恩 季宿儒
摘要:本系统以单片机89C52为核心,采用了步进电机控制装置、红外寻迹传感装置来实现一个对悬挂物体运动控制系统。友好的操作界面和灵活的监控方式是本
的两大特色,增强了系统的实用性。
关键词:步进电机 ;红外传感器 ;悬挂运动。
一、
论证与选择
1.
目任务要求及相关指标的分析
题目的基本要求中,要求在150秒内到达设定的一个坐标点以及300秒内画圆或按照设定的轨迹运动,这要求我们对左右两个电机进行灵活、准确、快速的控制,并且有和谐的人机交换功能,才能够满足要求。因此选择合适的电机以及运用有效的算法来对电机进行控制是必需的,我们通过建立合理的数学模型,使两电机控制的绳长依照我们建立的
关系变化,来实现悬挂物体在板上以各种方式运动。
发挥部分,要求控制物体沿板上标出的任意曲线运动,通过红外传感器寻迹,实现跟踪运动。而显示画笔的位置坐标,则可通过两段绳长变化与原点坐标之间的函数关系来计算,求出当前点的横纵坐标,并在LCD上显示出来。
2.方案的比较与选择
(1)电机的选择
方案一:直流电机
采用单片机和A/D转换构成系统,控制普通电机的步数和旋转方向,可以考虑达林管组成的H型PWM电路。用单片机控制达林管使之工作在占空比可调的开关状态,精确调整电机转速,减小因惯性,速度,步距角过大而引起的调整误差,达到改变绳长的要求,缺点是通过控制直流电机驱动模块将脉冲信号转化为模拟信号,需要将单片机输出的序列脉冲转换,延长了控制的时间,导致控制精度差;关键是直流电机不能即停,使直流电机不能精确定位,满足不了控制误差范围为设定值的要求。
方案二:步进电机
用单片机产生脉冲信号,脉冲信号的占空比为0.5左右,脉冲信号经过功率放大控制步进电机,控制信号为数字信号,不再需要数/模转换;步进电机的速度易于控制,具有快速启/停能力,可在一刹那间实现启动或停止;它的转动角度严格可控,一般情况步距角可以降低到
以下,而采用专用驱动块,可以更加的精确。步进电机延时短,定位准确,精度高,可控制性强。这样我们就可以根据驱动脉冲总数来计算绳子所走过的路程,从而得出坐标和时间。
根据本课题技术指标要求,物体质量大于100克即可,步进电机的输出功率足以满足负载要求;同时根据题目要求运动轨迹与预期轨迹偏差不超过4cm,这对电机有精确的要求,而直流电机难以达到这一指标。综上方案的比较分析,故而本设计采用方案二。
(2)悬挂物体定位方式的选择
方案一:坐标查询方式
在80cm×100cm的白色底板上,等分为8000个方格,视其为8000个点,每个点都有横纵坐标(a,b)两个数据,即共有16000个数据,而底板左右对称,所以只需记录一半的数据,即8000个,这样我们只需事先计算出悬挂物体在每一点对应的两侧绳长(x,y),将这8000个数据,即4000个确定的绳长数组记录在单片机中,这样就可以对所设定的坐标,采用查询的方式,找到相应点所对应的数组(x,y),与当前点对应的绳长做差,得到所需的绳长改变量,从而控制电机使物体到达该点。
此方案的优点在于查询方便快捷,准确性高,动作运动迅速,但准备工作的计算量太大,而且需要通过一块片外的ROM来存储如此庞大的数组集,不方便实现。
方案二:公式计算方式
根据题目中给出的各段长度,可以很容易的得到底板上各点坐标与两电机分别对应的绳长之间的函数关系,这样可以根据给定点的坐标求出各绳长或根据绳长求出点的坐标,能够实现各项指标,实现悬挂物体的精确定位,满足题目的要求。
该方案数学模型清晰,可以在较短时间内完成绳长或坐标的计算,使电机快速动作,无须片外ROM存储,而且占用单片机内存少,空间利用率较高,可操作性强。
综上方案的比较分析,根据本课题技术指标要求,用公式计算的方法比较容易实现,故而本设计采用方案二。
(3)画圆方案的选择
方案一:切分拟合法
首先通过周长计算,将圆等分为3000等份,然后依次计算初始点到下一点的坐标增量,通过增量来控制两个电机的运动,从而调整绳长,实现点对点的精确移动,也就是说所画的圆是由3000段小线段与之拟合连接起来的。这需要我们有足够快的运算速度,与很高的精度,依次精确计算出每一次步进的距离,横纵坐标和两段绳子的改变量,占用内存很大,计算时间较长,反应到电机上就是电机的运动时断时续,画圆的用时很长,线段相对光滑,但是累积误差比较大。
方案二:查询计算法
第一步要求我们能够画出一个题中要求的
圆,然后将这条轨迹对应的各点坐标与相邻坐标的增量处理后详细记录,并且存储到单片机内,当我们需要画圆的时候,只需要调用这些标准增量,通过公式计算出绳长的改变量,就可以很精准的得到:左右电机从当前点运动到相邻点的脉冲数目与方向。这样就可以实现快速、准确、美观的画圆。
上述两种方案中,方案二可以避免复杂的计算,缩短电机的等待时间,没有误差的累积,反应快速、灵活,画面连续、美观,实现起来也比较方便。因此,我们画圆采用上述的方案二。
(4)寻迹传感器的选择
发挥部分寻迹传感器可供选择的种类较多,主要有以下两种:
方案一:光电传感器
光电传感器有多种类型,按其外表结构有对射型、回归反射型、扩散反射型;按光源色分类又有红色、绿色、蓝色、红外以及半导体雷射等,其自身带有内置的或分离的放大器。根据题目的要求,要使所选传感器可以很好区分白色与黑色,有多种光电传感器可供选择,某些光源色为红色或绿色的光电传感器由于其对黑色与白色的反射率相差很大,所以可以很容易的识别。但是其价格昂贵,结构偏大,外围机械部分比较复杂。
方案二:红外传感器
红外传感器是我们非常常用的一种传感器,也具有很强的对黑色与白色的辨识能力。由于红外光波长比可见光长,因此受可见光的影响较小。同时红外系统还具有以下优点:尺寸小、质量轻,能有效的抗可见光波段的伪装,对辅助装置要求最少,对人眼无伤害。当然红外光也有一定的缺点,如大气、潮湿的天气、雾和云对它有衰减作用。它具有体积小、灵敏度高、线性好等特点,外围电路简单,安装起来方便,电源要求不高。用它作为近距离传感器是最理想的,电路设计简单、性能稳定可靠。
综上方案的比较分析,根据本课题技术指标要求,故而本设计采用方案二。
(4)寻迹传感器布局的选择
方案一:5传感器梅花状分布
采用上、中、下、左、右各装一个红外传感器来探测黑线,常态让中间的传感器保持在黑线上面,其余四个方向的传感器用来告知控制系统:该传感器所在的位置,是否为黑线上方。若在,则控制画笔向该传感器所在的方位移动,否则保持原来的移动方向不变,直到某个传感器探测到黑线,并发出信号为止。
这种方法虽然所用器件少,但是可靠性相对较差,精度较低。
方案二: 8传感器九宫图分布
在九宫格中间的右上、右、上、右下、左上、左下、左、下这八个方向装上红外传感器,并按照上面的顺序设置优先级。优先级的设定,取决于坐标原点相对于运动区域的所在位置。这样的话,我们就可以很容易的探测出黑线的前进趋向,依照优先级来控制电机的运动,可以准确快速实现寻迹功能,能够精准追踪1.5cm~1.8cm宽的黑色轨迹。
比较以上两种方案,方案一的探测范围显然要小,一旦传感器组处于黑线的同一边时,就很难重新自纠正,尤其是碰到有间断的曲线时,就很难控制它的运动;而方案二则可以探测较大的范围,实现多方位探测,控制思想较之方案一要先进的多,便于实现PID控制。
(5) 小结
经过仔细比较与论证,我们确定了整个控制系统各个模块的最终方案如下:
电机的选择:步进电机,带专用驱动模块,通过单片机输出脉冲信号来控制;
悬挂物体定位方式的选择:公式计算方式,求出点坐标对应的绳长,用步进电机实现绳长的改变。
画圆的算法:存储、查询计算,预先存储标准圆的相应增量,以便随时调用。
发挥部分寻迹传感器的选择:红外传感器
发挥部分寻迹传感器布局方法的选择:九宫图分布
二、系统总体设计方案及实现方框图
系统以89C52单片机为核心,加以其他外围电路实现较高精度定位。由于只需控制左右两个电机,所以其外围电路较简单,由于要实现高精度的控制,而硬件上的误差影响太多,所以需采用合理程序实现优良性能。
系统原理图
三、理论分析与计算
该设计以单片机控制的两个步进电机为基本构架,两电机控制各自的绳长实现悬挂物体的二维移动。根据题目将滑轮近似为一点(误差在最后单片机做出补偿),我们根据如图3.1中各线之间的关系,得到关系式如下:
(1)
其中:x 为左电机控制的滑轮右侧绳长;
y 为右电机控制的滑轮左侧绳长;
a为以图中标出的原点为原点物体所对应的横坐标; 图3.1
b 为物体所对应的纵坐标。
这样通过输入设定值,便可在单片机中依式(1)计算得出绳长x和y,与所在点的坐标所对应的绳长做差,便可得到电机所需运动的绳长及转动方向,便可通过单片机对电机驱动, 图3.1
实现对悬挂物体的精确定位。
在实现控制物体沿板上标出的任意曲线运动的过程中,我们实现了系统每移动到一个坐标便在LCD上显示这点的坐标,这就要求根据绳长的变化得到坐标的变化,同样在单片机内部可依据式(1)进行逆变换,得到如下的函数,式(2)
(2)
依次可算出a、b,得到所在点的坐标,从而显示出来。
四、主要功能电路的设计
1.基本说明
由于本系统设计所要求的精度较高,我们用单片机输出脉冲控制的步进电机,每前进一个步距的距离为0.046mm,速度易于控制,具有快速启/停能力,可以达到精度高、速度快、定位准这些优良的性能,足以很好的满足各项性能要求。
反射式红外传感器,其工作原理为:红外发射管发射红外光,接收管是否接收到红外光,对应了接收管的导通与截止两种状态,导通时有一个变化的通光电流,我们只需对微弱的通光电流进行上拉,变为电压信号,再对这个电压信号进行比较,输出开关脉冲,由单片机的I/O口将检测到的开关信号送到单片机进行识别、分析,然后发出相应的控制指令。整体电路的设计简单合理、性能稳定可靠。
2.关键电路单元的考虑和计算
红外寻迹传感器的电路设计如右图4.1,传感器型号采用反射式红外发射—接收管ST188,比较器采用LM339,用两片以满足8个红外传感器的需求。
查资料得,红外发射管的极限电流为50mA,我们取中间态计算:
,
红外接收管的通光电流最大为
=0.25mA,可以计算其导通截止两种状态的电平所需电阻最小为:
图4.1 红外寻迹电路
,其中
为导通管压降,实际电路中取大于
,取
=
。
五、系统软件的设计
本设计以合理的定位计算和优良的修正补偿为主要特色,程序中这两点的合理性在系统性能指标实现中起着很大的作用。
主程序:
主程序主要由四块模式组成,即画直线,画圆,通过键盘控制物体上下左右运动,以及写字(通过程序——本程序为“电”)。
图5.1 主程序流程图
直线运动核心算法
假设E(x0,y0),F(x1,y1)为给定平面范围上的任意两点,作辅助线(图中虚线部分),在直角三角形⊿ABE中
a02=(x0+15)2+(115-y0)2 (5.1)
在直角三角形⊿CDE中:
b02=(95-x0)2+(115-y0)2 (5.2)
同理对于F点,两拉线长分别为:
a12=(x1+15)2+(115-y1)2 (5.3)
b12=(95-x1)2+(115-y1)2 (5.4)
因此当悬挂物从E点运动到F点时:
电机1的收放线长度为c(当c<0,电机正转(或拉线伸长);c>0时,电机反转(或拉线收缩))
c=a0-a1(5.5)
电机2的收放线长度为d(当d<0,电机反转(或拉线收缩),当d>0时,电机正转(或拉线伸长))
d=d0-d1(5.6)
根据c,d的正负分别确定电机1,电机2的正反转向。设绳索位移1mmAD变化值为P,而根据c,d的绝对值来确定电位器1,电位器2所要变化的值:
电位器1所分配的数值:
m=|c|*p (5.7)
电位器2所分配的数值:
n=|d|*p (5.8)
画圆子程序:
根据圆的参数方程的计算圆上点的坐标,通过调用画圆程序来实现,画圆程序流程图如图4.4所示。
图4.4 画圆子程序流程图
/**画圆*********************/
void draw_circularity(float rr,float x0,float y0) //rr:半径,(x0,y0):圆心坐标
{float L_l,L_r,L_l0,L_r0,inc_Ll,inc_Lr,xx,yy,xxx ;
int count_l,count_r,i ;
L_l0=sqrt((x0+rr+15)*(x0+rr+15)+(115-y0)*(115-y0)) ;//(x0+rr,y0):画圆起点坐标
L_r0=sqrt((95-x0-rr)*(95-x0-rr)+(115-y0)*(115-y0)) ;
for(i=1;i<=360;i++)
{xx=rr*cos(pai*i/180)+x0 ;
xxx=xx;
yy=rr*sin(pai*i/180)+y0 ;
L_l=sqrt((xx+15)*(xx+15)+(115-yy)*(115-yy)) ;
L_r=sqrt((95-xx)*(95-xx)+(115-yy)*(115-yy)) ;
inc_Ll=L_l-L_l0 ;
inc_Lr=L_r-L_r0 ;
count_l=inc_Ll/step_l+(inc_Ll>=0 ?0.5:-0.5) ; // 左步进电机每步弧长(厘米)
count_r=inc_Lr/step_r+(inc_Lr>=0 ?0.5:-0.5) ; // 右步进电机每步弧长(厘米)
drive_motor(count_l,count_r) ;
delay(50) ; //延时50ms
L_l0=L_l ;
L_r0=L_r ;
}
}
键盘:
键盘流程图如图4.5所示。
4.5键盘模块程序流程图
1
2↑
3
方式
4
←
5
6
→
右移
7
8↓
9
左移
0
启动
设置
确认
图4.3.7 4×4键盘功能图
设置键:手动对位或任意设定坐标点参数键,按下后用上、下、左、右键可进行手动对位控制,然后按确认键确认,图4.3.8所示。
图4.3.8设置键操作图
六 结束语:
本系统采用STC89C52RC单片机作为悬挂控制系统的检测和控制核心,实现通过人机界面对物体所作运动进行设定,通过LCD实时显示此时画笔所在的坐标值;系统具有可画出相应的运动轨迹功能。运动参数的设定通过无线键盘输入,系统通过比较当前画笔所在位置与设定位置的差异以及运动类型,控制步进电机完成相应运动;采用了L298驱动芯片和PWM技术实现电机控制;在设计过程中,力求硬件电路简单,充分发挥软件设计的优势,编程灵活方便来满足系统的要求。
附录:
程序:
#include
#include
#include
#define Lcd_Command 0
#define Lcd_Data 1
#define uchar unsigned char
#define uint unsigned int
#define pai 3.14159
//圆周率
#define step_l 0.0049
//左步进电机每步弧长(厘米)
#define step_r 0.0049
//右步进电机每步弧长(厘米)
sbit rs =P1^0;
//1602液晶
sbit rw =P1^1;
sbit en =P1^2;
sbit bf=P2^7;
bit key_flag;
void delay(uint z);
void draw_line(float x0,float y0,float x1,float y1);
void draw_circularity(float rr,float x0,float y0);
void drive_motor(int c_l,int c_r);
void motor_r_frap();
void motor_r_loosen();
void motor_l_loosen();
void motor_l_frap();
sbit a = P3^0;
//电机串口P3
sbit b = P3^1;
sbit c = P3^2;
sbit d = P3^3;
sbit e = P3^4;
sbit f = P3^5;
sbit g = P3^6;
sbit h = P3^7;
uchar B1,B2;
uchar x_b,x_b1,x_s,x_s1,x_g,x_g1,y_s,y_s1,y_g,y_g1,r_s;r_g;//x值,y值的百、十、个位
uchar key_value,mode;
// key_value 按键值;mode选择模式值;
uchar mode1_shape;
// 模式1 选择图形值
uchar mode3_x,mode3_y,mode3_r;
//模式3,圆心坐标(mode3_x,mode3_y,mode3_r)
uchar mode4_star_x,mode4_end_x,mode4_star_y,mode4_end_y;
//模式4 起、终点坐标(x1,y1)(x2,y2)
uchar *str1 = "Mode1: Line Move";
//1画直线运动
uchar *str2 = " Mode2: Circle ";
//2画圆运动
uchar *str3 = " Mode3: Measure " ;
//3 键盘控制运动
uchar *str4 = " Mode4: Xie zhi " ;
//4写字“电”
void wait();
void Lcd_Write(bit style,unsigned char input);
void Setpos(uchar x,uchar y);
void print_char(uchar *s,uchar length);
void write_strdata(uchar *s,uchar length);
void print_char1(uchar a);
void Lcd_Init(void);
void ClrScreen(void);
uchar key();
void Mode_Select();
//模式选择函数
void Mode2_ParameterSet();
void Mode1_ParameterSet();
void Mode3_ParameterSet();
void Mode4_ParameterSet();
void delay_ms(uint z)
{
uint x;
uchar y;
for (x=z;x>0;x--)
for (y=140;y>0;y--);
}
void delay(uint z)
{
uint x;
uchar y;
for (x=z;x>0;x--)
for (y=110;y>0;y--);
}
/**画直线*********************/
void draw_line(float x0,float y0,float x1,float y1) //(x0,y0):起点坐标,(x0,y0):停止点坐标
{
idata float ll,inc_x,inc_y,xx,yy,L_l0,L_r0,L_l,L_r,inc_Ll,inc_Lr;
idata int count_ll,count_l,count_r,i;
uchar xx1[3],yy1[2];
ll=sqrt((x1-x0)*(x1-x0)+(y1-y0)*(y1-y0)) ; //ll:斜边长度
count_ll=ll/0.5+0.5 ; //count_ll:步数
inc_x=(x1-x0)/count_ll ; //inc_x:x步长
inc_y=(y1-y0)/count_ll ; //inc_y:y步长
L_l0=sqrt((x0+15)*(x0+15)+(115-y0)*(115-y0)) ;
L_r0=sqrt((95-x0)*(95-x0)+(115-y0)*(115-y0)) ;
Setpos(0,1);
//实时显示
write_strdata(" New: xxx,yy ",16);
for(i=1; i<=count_ll; i++)
{
xx=inc_x*i+x0 ;
xx1[0] = (uchar)xx/100;
xx1[1] = (uchar)xx%100/10;
xx1[2] = (uchar)xx%10;
Setpos(7,1);
print_char(xx1,3);
yy=inc_y*i+y0 ;
yy1[0] = (uint)yy/10;
yy1[1] = (uint)yy%10;
Setpos(11,1);
print_char(yy1,2);
L_l=sqrt((xx+15)*(xx+15)+(115-yy)*(115-yy)) ;
L_r=sqrt((95-xx)*(95-xx)+(115-yy)*(115-yy)) ;
inc_Ll=L_l-L_l0 ;
inc_Lr=L_r-L_r0 ;
count_l=inc_Ll/step_l+(inc_Ll>=0 ?0.5:-0.5) ;
count_r=inc_Lr/step_r+(inc_Lr>=0 ?0.5:-0.5) ;
drive_motor(count_r,count_l);
//
delay(50) ; //延时50ms test: x0,y0,x1,y1=10,10,40,40
L_l0=L_l ;
L_r0=L_r ;
}
}
/**画圆*********************/
void draw_circularity(float rr,float x0,float y0) //rr:半径,(x0,y0):圆心坐标
{
idata float L_l,L_r,L_l0,L_r0,inc_Ll,inc_Lr,xx,yy ;
idata int count_l,count_r,i ;
uchar xx1[3],yy1[2];
L_l0=sqrt((x0+rr+15)*(x0+rr+15)+(115-y0)*(115-y0)) ;
L_r0=sqrt((95-x0-rr)*(95-x0-rr)+(115-y0)*(115-y0)) ;
Setpos(0,1);
//实时显示
write_strdata(" New: xxx,yy ",16);
for(i=1;i<=360;i++)
{
xx=rr*cos(pai*i/180)+x0;
xx1[0] = (uchar)xx/100;
xx1[1] = (uchar)xx%100/10;
xx1[2] = (uchar)xx%10;
Setpos(7,1);
print_char(xx1,3);
yy=rr*sin(pai*i/180)+y0 ;
yy1[0] = (uchar)yy/10;
yy1[1] = (uchar)yy%10;
Setpos(11,1);
print_char(yy1,2);
L_l=sqrt((xx+15)*(xx+15)+(115-yy)*(115-yy)) ;
L_r=sqrt((95-xx)*(95-xx)+(115-yy)*(115-yy)) ;
inc_Ll=L_l-L_l0 ;
inc_Lr=L_r-L_r0 ;
count_l=inc_Ll/step_l+(inc_Ll>=0 ?0.5:-0.5) ;
count_r=inc_Lr/step_r+(inc_Lr>=0 ?0.5:-0.5) ;
drive_motor(count_l,count_r) ;
// delay(50) ; //延时50ms
L_l0=L_l ;
L_r0=L_r ;
}
}
/***驱动电机**************************/
void drive_motor(int c_l,int c_r)
{
int j;
for(j=0;j0) {
motor_l_frap(); }//左马达松开线
if(c_l<0) { motor_l_loosen();}//左马达收紧线
}
for(j=0;j0){ motor_r_loosen(); } //右马达松开线
if(c_r<0){ motor_r_frap() ; } //右马达收紧线
}
}
/******左马达收紧线*********************/
void motor_r_frap()
{
B2++; if(B2==5) B2=1;
switch (B2)
{
case 1: a=1;b=0;c=0;d=0;delay(15);break;
case 2: a=0;b=1;c=0;d=0;delay(15);break;
case 3: a=0;b=0;c=1;d=0;delay(15);break;
case 4: a=0;b=0;c=0;d=1;delay(15);break;
}
}
/******左马达松开线*********************/
void motor_r_loosen()
{
B2--;if(B2==0) B2=4;
switch (B2)
{
case 1: a=1;b=0;c=0;d=0;delay(15);break;
case 2: a=0;b=1;c=0;d=0;delay(15);break;
case 3: a=0;b=0;c=1;d=0;delay(15);break;
case 4: a=0;b=0;c=0;d=1;delay(15);break;
}
}
/*****右马达收线**************************/
void motor_l_loosen()
{
B1++; if(B1==5) B1=1;
switch (B1)
{
case 1: e=1;f=0;g=0;h=0;delay(15);break;
case 2: e=0;f=1;g=0;h=0;delay(15);break;
case 3: e=0;f=0;g=1;h=0;delay(15);break;
case 4: e=0;f=0;g=0;h=1;delay(15);break;
}
}
/******右马达放线*************************/
void motor_l_frap()
{
B1--;if(B1==0) B1=4;
switch (B1)
{
case 1: e=1;f=0;g=0;h=0;delay(15);break;
case 2: e=0;f=1;g=0;h=0;delay(15);break;
case 3: e=0;f=0;g=1;h=0;delay(15);break;
case 4: e=0;f=0;g=0;h=1;delay(15);break;
}
}
void main()
{
uchar i;
B1=4;B2=4;
Lcd_Init();
Setpos(16,0);
write_strdata("www.hnnu.edu.cn",15);
//欢迎语 淮南师范学院
www.hnnu.edu.cn
Setpos(16,1);
write_strdata("xgydkz: kxhappy",16);
// 欢迎语 悬挂运动控制 kx工作室
for(i = 0;i < 16;i++)
{
Lcd_Write(Lcd_Command,0x18);//光标和显示字符一起移动
delay_ms(200);
}
while(1)
{
key_value = key();
if(key_flag == 1)
{
key_flag = 0;
if( key_value == 10)
//模式键按下
{
Mode_Select();
}
if(key_value ==14)//设定键按下;
{
if(mode == 2)//模式2 画圆
{
Mode2_ParameterSet();
}
if(mode == 1)//模式1 画直线
{
Mode1_ParameterSet();
}
if(mode == 3)
{
Mode3_ParameterSet();
}
if(mode == 4)
{
Mode4_ParameterSet();
}
}
if(key_value ==13) //开始键按下
{
if(mode == 2)
{
ClrScreen();
write_strdata(str2,16);
draw_circularity(mode3_r,mode3_x,mode3_y);
while(1);
}
if(mode == 1)
{
ClrScreen();
write_strdata(str1,16);
draw_line(mode4_star_x,mode4_star_y,mode4_end_x,mode4_end_y); //出发
delay(30000) ;
//暂停
draw_line(mode4_end_x,mode4_end_y,mode4_star_x,mode4_star_y); //返回
while(1);
}
}
}
}
}
//1602 忙否
void wait()
{
while(1)
{
P2= 0xff;
rs = 0;
rw = 1;
en =0;
en =1;
if(bf == 0)
break;
}
}
/**************************************************************
**功能:写函数
**
**参数:style为写命令/数据,0-命令,1-数据;input为写入的8位 **
**
命令/数据
**
**************************************************************/
void Lcd_Write(bit style,unsigned char input)
{
wait();
P2= input;
rs = style;
rw = 0;
en = 1;
en = 0;
}
/*显示位置*/
void Setpos(uchar x,uchar y)
{
if(y==0)
Lcd_Write(Lcd_Command,0x80+x);
//第一行
else
Lcd_Write(Lcd_Command,0xc0+x);
//第二行
}
//写字符串
void write_strdata(uchar *s,uchar length)
{
uchar i;
for(i= 0;i= 0 && a <= 9 )
Lcd_Write(Lcd_Data ,a + '0');
else if(a >= 0x0a &&a <= 0x0f)
Lcd_Write(Lcd_Data,a+0x37);
s++;
}
}
void print_char1(uchar a)
{
if( a >= 0 && a <= 9 )
Lcd_Write(Lcd_Data ,a + '0');
else if(a >= 0x0a &&a <= 0x0f)
Lcd_Write(Lcd_Data,a+0x37);
}
void ClrScreen(void)
{
Lcd_Write(Lcd_Command,0x01);
}
void Lcd_Init(void)
{
Lcd_Write(Lcd_Command,0x01);//清屏
Lcd_Write(Lcd_Command,0x38);//设置显示模式
Lcd_Write(Lcd_Command,0x0C);//打开显示,不显示光标,光标不闪烁
Lcd_Write(Lcd_Command,0x80);//第一行第一个字符显示
}
/*按键检测 P0*/
uchar key()
{
uchar temp,num;
P0 = 0Xfe;
temp = P0 & 0xf0;
if(temp != 0xf0)
{
delay_ms(5);
if(temp != 0xf0)
{
key_flag = 1;
temp = P0;
switch( temp )
{
case 0xee:num = 1;
break;
case 0xde:num = 2;
break;
case 0xbe:num = 3;
break;
case 0x7e:num = 10;
break;
}
do
{
temp = P0;
temp = temp & 0xf0;
}
while(temp != 0xf0);
}
}
P0 = 0Xfd;
temp = P0 & 0xf0;
if(temp != 0xf0)
{
delay_ms(5);
if(temp != 0xf0)
{
key_flag = 1;
temp = P0;
switch( temp )
{
case 0xed:num = 4;
break;
case 0xdd:num = 5;
break;
case 0xbd:num = 6;
break;
case 0x7d:num = 11;
break;
}
do
{
temp = P0;
temp = temp & 0xf0;
}
while(temp != 0xf0);
}
}
P0 = 0Xfb;
temp = P0 & 0xf0;
if(temp != 0xf0)
{
delay_ms(5);
if(temp != 0xf0)
{
key_flag = 1;
temp = P0;
switch( temp )
{
case 0xeb:num = 7;
break;
case 0xdb:num = 8;
break;
case 0xbb:num = 9;
break;
case 0x7b:num = 12;
break;
}
do
{
temp = P0;
temp = temp & 0xf0;
}
while(temp != 0xf0);
}
}
P0 = 0Xf7;
temp = P0 & 0xf0;
if(temp != 0xf0)
{
delay_ms(5);
if(temp != 0xf0)
{
key_flag = 1;
temp = P0;
switch(temp)
{
case 0xe7:num = 0;
break;
case 0xd7:num = 13;
break;
case 0xb7:num = 14;
break;
case 0x77:num = 15;
break;
}
do
{
temp = P0;
temp = temp & 0xf0;
}
while(temp != 0xf0);
}
}
if(key_flag == 1)
{
return num;;
}
}
void Mode_Select()
{
ClrScreen();
write_strdata("Mode Select: ",16);
Setpos(0,1);
write_strdata("Please input:1-4",16);
Setpos(12,0);
Lcd_Write(Lcd_Command,0x0f);//开显示,显示光标,闪烁
do
///选择模式
{
key_value = key();
if(key_flag == 1)
{
key_flag = 0;
if((key_value != 15) && (key_value > 0) && (key_value < 5))
{
mode = key_value;
Setpos(12,0);
print_char1(key_value);
Setpos(0,1);
write_strdata("Please Enter! ",16);
Setpos(12,0);
}
}
}
while(key_value != 15);
Lcd_Write(Lcd_Command,0x0C);
ClrScreen();
if(mode == 2)
write_strdata(str2,16);
if(mode == 1)
write_strdata(str1,16);
if(mode == 3)
write_strdata(str3,16);
if(mode == 4)
write_strdata(str4,16);
Setpos(0,1);
write_strdata("Please Setting! ",16);
}
void Mode2_ParameterSet()
{
uchar i;
ClrScreen();
write_strdata(str2,16);
Setpos(0,1);
write_strdata("O:xxx:yy R:xx cm",16);
Setpos(2,1);
Lcd_Write(Lcd_Command,0x0f);//开显示,显示光标,闪烁
i = 0;
do
///输入圆心坐标和半径
{
key_value = key();
if(key_flag == 1)
{
key_flag = 0;
if((key_value != 15) && (key_value >= 0) && (key_value <= 9))
{
if( i>=1)
print_char1(key_value);
if((i == 0) && (key_value <2))
{
x_b = key_value;
if(key_value == 0)
write_strdata(" ",1);
//如果是0消隐
else print_char1(key_value);
}
if(i == 1)
x_s = key_value;
if(i == 2)
x_g =key_value;
if(i == 4)
y_s = key_value;
if(i == 5)
y_g = key_value;
if(i == 9)
r_s = key_value;
if(i == 10)
r_g = key_value;
i++;
if(i == 3)
i=4;
if(i == 6)
i = 9;
if(i == 11)
i =10;
Setpos(2+i,1);
}
if(key_value == 11)/////光标右移
{
i++;
if(i == 3)
i=4;
if(i == 6)
i = 9;
if(i == 11)
i =10;
Setpos(2+i,1);
}
if(key_value == 12)/////光标左移
{
if(i!=0)
i--;
if(i == 0)
i=0;
if(i == 8)
i = 5;
if(i == 3)
i =2;
Setpos(2+i,1);
}
}
}
while(key_value != 15);
i = 0;
mode3_x = x_b * 100 + x_s * 10 + x_g;
mode3_y = x_s * 10 + x_g ;
mode3_r = r_s * 10 + r_g;
x_b = 0;x_s = 0;x_g = 0;
y_s = 0;y_g = 0;
r_s = 0;r_g = 0;
Lcd_Write(Lcd_Command,0x0C);//关光标,关闪烁
ClrScreen();
write_strdata(str2,16);
Setpos(0,1);
write_strdata("Please Starting!",16);
}
void Mode1_ParameterSet()
{
uchar i;
ClrScreen();
write_strdata(str1,16);
Setpos(0,1);
write_strdata("Sxxx,yy Exxx,yy ",16);
Setpos(1,1);
Lcd_Write(Lcd_Command,0x0f);//开显示,显示光标,闪烁
i = 0;
do
{
key_value = key();
if(key_flag == 1)
{
key_flag = 0;
if((key_value != 15) && (key_value >= 0) && (key_value <= 9))
{
if( (i>=1)&&(i!= 8))
print_char1(key_value);
if((i == 0) && (key_value <2))
{
x_b = key_value;
if(key_value == 0)
write_strd