开场白:
前面三节讲了独立按键操控跑马灯的各种状况,这一节咱们要做一个机械手操控程序,这个机械手能够左右移动,最左面有一个开关感应器,最右边也有一个开关感应器。它也能够上下移动,最下面有一个开关感应器。左右移动是通过一个气缸操控,上下移动也是通过一个气缸操控。而单片机操控气缸,本质上是通过三极管把信号扩大,然后操控气缸上的电磁阀。这个体系机械手驱动部分的输出和输入信号如下:
2个输出IO口,别离操控2个气缸。关于左右移动的气缸,当IO口为0时往左面跑,当IO口为1时往右边跑。关于上下移动的气缸,当IO口为0时往上边跑,当IO口为1时往下边跑。
3个输入IO口,别离检测3个开关感应器。感应器没有被触发时,IO口检测为高电平1。被触发时,IO口检测为低电平0。
这一节持续要教会咱们两个知识点:
榜首点:如何用软件进行开关感应器的抗干扰处理。
第二点:如何用Switch句子建立工业主动操控的程序结构。仍是那句话,咱们只要以Switch句子为支点,再杂乱再繁琐的程序都能够轻松地编写出来。
具体内容,请看源代码解说。
(1)硬件渠道:根据朱兆祺51单片机学习板。用矩阵键盘中的S1键作为发动独立按键,用S5按键模仿左面的开关感应器,用S9按键模仿右边的开关感应器,用S13按键模仿下边的开关感应器。记得把输出线P0.4一向输出低电平,模仿独立按键的触发地GND。
(2)完成功用:
开机默许机械手在左上方的原点方位。按下发动按键后,机械手从左面开端往右边移动,当机械手移动到最右边时,机械手立刻开端往下移动,最终机械手移动到最右下角的方位时,延时1秒,然后原路回来,一向回来到左上角的原点方位。留意:发动按键有必要等机械手处于左上角原点方位时,发动按键的触发才有用。
(3)源代码解说如下:
#include “REG52.H”
#define const_voice_short40 //蜂鸣器短叫的持续时刻
#define const_key_time120 //按键去颤动延时的时刻
#define const_sensor20 //开关感应器去颤动延时的时刻
#define const_1s500//1秒钟大约的守时中止次数
void initial_myself();
void initial_peripheral();
void delay_short(unsigned int uiDelayShort);
void delay_long(unsigned int uiDelaylong);
void left_to_right();//从左面移动到右边
void right_to_left(); //从右边回来到左面
void up_to_dowm(); //从上边移动到下边
void down_to_up(); //从下边回来到上边
void run(); //设备主动操控程序
void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01);
void led_update();//LED更新函数
void T0_time();//守时中止函数
void key_service(); //按键服务的应用程序
void key_scan(); //按键扫描函数 放在守时中止里
void sensor_scan(); //开关感应器软件抗干扰处理函数,放在守时中止里。
sbit hc595_sh_dr=P2^3;
sbit hc595_st_dr=P2^4;
sbit hc595_ds_dr=P2^5;
sbit beep_dr=P2^7; //蜂鸣器的驱动IO口
sbit key_sr1=P0^0; //对应朱兆祺学习板的S1键
sbit left_sr=P0^1; //左面的开关感应器 对应朱兆祺学习板的S5键
sbit right_sr=P0^2; //右边的开关感应器 有对应朱兆祺学习板的S9键
sbit down_sr=P0^3; //下边的开关感应器 对应朱兆祺学习板的S13键
sbit key_gnd_dr=P0^4; //模仿独立按键的地GND,因而有必要一向输出低电平
unsigned char ucKeySec=0; //被触发的按键编号
unsigned intuiKeyTimeCnt1=0; //按键去颤动延时计数器
unsigned char ucKeyLock1=0; //按键触发后自锁的变量标志
unsigned char ucLeftSr=0;//左面感应器通过软件抗干扰处理后的状况标志
unsigned char ucRightSr=0;//右边感应器通过软件抗干扰处理后的状况标志
unsigned char ucDownSr=0;//下边感应器通过软件抗干扰处理后的状况标志
unsigned intuiLeftCnt1=0;//左面感应器软件抗干扰所需的计数器变量
unsigned intuiLeftCnt2=0;
unsigned intuiRightCnt1=0;//右边感应器软件抗干扰所需的计数器变量
unsigned intuiRightCnt2=0;
unsigned intuiDownCnt1=0; //下边软件抗干扰所需的计数器变量
unsigned intuiDownCnt2=0;
unsigned intuiVoiceCnt=0;//蜂鸣器鸣叫的持续时刻计数器
unsigned char ucLed_dr1=0; //代表16个灯的亮灭状况,0代表灭,1代表亮
unsigned char ucLed_dr2=0;
unsigned char ucLed_dr3=0;
unsigned char ucLed_dr4=0;
unsigned char ucLed_dr5=0;
unsigned char ucLed_dr6=0;
unsigned char ucLed_dr7=0;
unsigned char ucLed_dr8=0;
unsigned char ucLed_dr9=0;
unsigned char ucLed_dr10=0;
unsigned char ucLed_dr11=0;
unsigned char ucLed_dr12=0;
unsigned char ucLed_dr13=0;
unsigned char ucLed_dr14=0;
unsigned char ucLed_dr15=0;
unsigned char ucLed_dr16=0;
unsigned char ucLed_update=1;//改写变量。每次更改LED灯的状况都要更新一次。
unsigned char ucLedStatus16_09=0; //代表底层74HC595输出状况的中心变量
unsigned char ucLedStatus08_01=0; //代表底层74HC595输出状况的中心变量
unsigned intuiRunTimeCnt=0;//运动中的时刻延时计数器变量
unsigned char ucRunStep=0;//运动操控的进程变量
void main()
{
initial_myself();
delay_long(100);
initial_peripheral();
while(1)
{
run(); //设备主动操控程序
led_update();//LED更新函数
key_service(); //按键服务的应用程序
}
}
/* 注释一:
* 开关感应器的抗干扰处理,本质上相似按键的去颤动处理。仅有的区别是:
* 按键去颤动重视的是IO口的一种状况,而开关感应器重视的是IO口的两种状况。
* 当开关感应器从本来的1状况切换到0状况之前,要进行软件滤波处理进程,一旦成功地
* 切换到0状况了,再想从0状况切换到1状况的时分,又要通过软件滤波处理进程,契合
* 条件后才干切换到1的状况。浅显的话来说,按键的去颤动从1变成0难,从0变成1简单。
* 开关感应器从1变成0难,从0变成1也难。这儿所说的”难”是指要通曩昔抖处理。
*/
void sensor_scan() //开关感应器软件抗干扰处理函数,放在守时中止里。
{
if(left_sr==1)//左面感应器是高电平,阐明有或许没有被触摸 对应朱兆祺学习板的S5键
{
uiLeftCnt1=0; //在软件滤波中,十分要害的句子!!!相似按键去颤动程序的及时清零
uiLeftCnt2++; //相似独立按键去颤动的软件抗干扰处理
if(uiLeftCnt2>const_sensor)
{
uiLeftCnt2=0;
ucLeftSr=1; //阐明感应器的确没有被触摸
}
}
else //左面感应器是低电平,阐明有或许被触摸到了
{
uiLeftCnt2=0; //在软件滤波中,十分要害的句子!!!相似按键去颤动程序的及时清零
uiLeftCnt1++;
if(uiLeftCnt1>const_sensor)
{
uiLeftCnt1=0;
ucLeftSr=0; //阐明感应器的确被触摸到了
}
}
if(right_sr==1)//右边感应器是高电平,阐明有或许没有被触摸 对应朱兆祺学习板的S9键
{
uiRightCnt1=0; //在软件滤波中,十分要害的句子!!!相似按键去颤动程序的及时清零
uiRightCnt2++; //相似独立按键去颤动的软件抗干扰处理
if(uiRightCnt2>const_sensor)
{
uiRightCnt2=0;
ucRightSr=1; //阐明感应器的确没有被触摸
}
}
else //右边感应器是低电平,阐明有或许被触摸到了
{
uiRightCnt2=0; //在软件滤波中,十分要害的句子!!!相似按键去颤动程序的及时清零
uiRightCnt1++;
if(uiRightCnt1>const_sensor)
{
uiRightCnt1=0;
ucRightSr=0; //阐明感应器的确被触摸到了
}
}
if(down_sr==1)//下边感应器是高电平,阐明有或许没有被触摸 对应朱兆祺学习板的S13键
{
uiDownCnt1=0; //在软件滤波中,十分要害的句子!!!相似按键去颤动程序的及时清零
uiDownCnt2++; //相似独立按键去颤动的软件抗干扰处理
if(uiDownCnt2>const_sensor)
{
uiDownCnt2=0;
ucDownSr=1; //阐明感应器的确没有被触摸
}
}
else //下边感应器是低电平,阐明有或许被触摸到了
{
uiDownCnt2=0; //在软件滤波中,十分要害的句子!!!相似按键去颤动程序的及时清零
uiDownCnt1++;
if(uiDownCnt1>const_sensor)
{
uiDownCnt1=0;
ucDownSr=0; //阐明感应器的确被触摸到了
}
}
}
void key_scan()//按键扫描函数 放在守时中止里
{
if(key_sr1==1)//IO是高电平,阐明按键没有被按下,这时要及时清零一些标志位
{
ucKeyLock1=0; //按键自锁标志清零
uiKeyTimeCnt1=0;//按键去颤动延时计数器清零,此行十分奇妙,是我实战中探索出来的。
}
else if(ucKeyLock1==0)//有按键按下,且是榜首次被按下
{
uiKeyTimeCnt1++; //累加守时中止次数
if(uiKeyTimeCnt1>const_key_time1)
{
uiKeyTimeCnt1=0;
ucKeyLock1=1;//自锁按键置位,防止一向触发
ucKeySec=1; //触发1号键
}
}
}
void key_service() //按键服务的应用程序
{
switch(ucKeySec) //按键服务状况切换
{
case 1:// 发动按键 对应朱兆祺学习板的S1键
if(ucLeftSr==0)//处于左上角原点方位
{
ucRunStep=1; //发动
uiVoiceCnt=const_voice_short; //按键声响触发,滴一声就停。
}
ucKeySec=0;//呼应按键服务处理程序后,按键编号清零,防止共同触发
break;
}
}
void led_update()//LED更新函数
{
if(ucLed_update==1)
{
ucLed_update=0; //及时清零,让它发生只更新一次的作用,防止一向更新。
if(ucLed_dr1==1)
{
ucLedStatus08_01=ucLedStatus08_01|0x01;
}
else
{
ucLedStatus08_01=ucLedStatus08_01&0xfe;
}
if(ucLed_dr2==1)
{
ucLedStatus08_01=ucLedStatus08_01|0x02;
}
else
{
ucLedStatus08_01=ucLedStatus08_01&0xfd;
}
if(ucLed_dr3==1)
{
ucLedStatus08_01=ucLedStatus08_01|0x04;
}
else
{
ucLedStatus08_01=ucLedStatus08_01&0xfb;
}
if(ucLed_dr4==1)
{
ucLedStatus08_01=ucLedStatus08_01|0x08;
}
else
{
ucLedStatus08_01=ucLedStatus08_01&0xf7;
}
if(ucLed_dr5==1)
{
ucLedStatus08_01=ucLedStatus08_01|0x10;
}
else
{
ucLedStatus08_01=ucLedStatus08_01&0xef;
}
if(ucLed_dr6==1)
{
ucLedStatus08_01=ucLedStatus08_01|0x20;
}
else
{
ucLedStatus08_01=ucLedStatus08_01&0xdf;
}
if(ucLed_dr7==1)
{
ucLedStatus08_01=ucLedStatus08_01|0x40;
}
else
{
ucLedStatus08_01=ucLedStatus08_01&0xbf;
}
if(ucLed_dr8==1)
{
ucLedStatus08_01=ucLedStatus08_01|0x80;
}
else
{
ucLedStatus08_01=ucLedStatus08_01&0x7f;
}
if(ucLed_dr9==1)
{
ucLedStatus16_09=ucLedStatus16_09|0x01;
}
else
{
ucLedStatus16_09=ucLedStatus16_09&0xfe;
}
if(ucLed_dr10==1)
{
ucLedStatus16_09=ucLedStatus16_09|0x02;
}
else
{
ucLedStatus16_09=ucLedStatus16_09&0xfd;
}
if(ucLed_dr11==1)
{
ucLedStatus16_09=ucLedStatus16_09|0x04;
}
else
{
ucLedStatus16_09=ucLedStatus16_09&0xfb;
}
if(ucLed_dr12==1)
{
ucLedStatus16_09=ucLedStatus16_09|0x08;
}
else
{
ucLedStatus16_09=ucLedStatus16_09&0xf7;
}
if(ucLed_dr13==1)
{
ucLedStatus16_09=ucLedStatus16_09|0x10;
}
else
{
ucLedStatus16_09=ucLedStatus16_09&0xef;
}
if(ucLed_dr14==1)
{
ucLedStatus16_09=ucLedStatus16_09|0x20;
}
else
{
ucLedStatus16_09=ucLedStatus16_09&0xdf;
}
if(ucLed_dr15==1)
{
ucLedStatus16_09=ucLedStatus16_09|0x40;
}
else
{
ucLedStatus16_09=ucLedStatus16_09&0xbf;
}
if(ucLed_dr16==1)
{
ucLedStatus16_09=ucLedStatus16_09|0x80;
}
else
{
ucLedStatus16_09=ucLedStatus16_09&0x7f;
}
hc595_drive(ucLedStatus16_09,ucLedStatus08_01);//74HC595底层驱动函数
}
}
void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01)
{
unsigned char i;
unsigned char ucTempData;
hc595_sh_dr=0;
hc595_st_dr=0;
ucTempData=ucLedStatusTemp16_09;//先送高8位
for(i=0;i<8;i++)
{
if(ucTempData>=0x80)hc595_ds_dr=1;
else hc595_ds_dr=0;
hc595_sh_dr=0; //SH引脚的上升沿把数据送入寄存器
delay_short(15);
hc595_sh_dr=1;
delay_short(15);
ucTempData=ucTempData<<1;
}
ucTempData=ucLedStatusTemp08_01;//再先送低8位
for(i=0;i<8;i++)
{
if(ucTempData>=0x80)hc595_ds_dr=1;
else hc595_ds_dr=0;
hc595_sh_dr=0; //SH引脚的上升沿把数据送入寄存器
delay_short(15);
hc595_sh_dr=1;
delay_short(15);
ucTempData=ucTempData<<1;
}
hc595_st_dr=0;//ST引脚把两个寄存器的数据更新输出到74HC595的输出引脚上而且锁存起来
delay_short(15);
hc595_st_dr=1;
delay_short(15);
hc595_sh_dr=0; //拉低,抗干扰就增强
hc595_st_dr=0;
hc595_ds_dr=0;
}
void left_to_right()//从左面移动到右边
{
ucLed_dr1=1; // 1代表左右气缸从左面移动到右边
ucLed_update=1;//改写变量。每次更改LED灯的状况都要更新一次。
}
void right_to_left() //从右边回来到左面
{
ucLed_dr1=0; // 0代表左右气缸从右边回来到左面
ucLed_update=1;//改写变量。每次更改LED灯的状况都要更新一次。
}
void up_to_down() //从上边移动到下边
{
ucLed_dr2=1; // 1代表上下气缸从上边移动到下边
ucLed_update=1;//改写变量。每次更改LED灯的状况都要更新一次。
}
void down_to_up() //从下边回来到上边
{
ucLed_dr2=0; // 0代表上下气缸从下边回来到上边
ucLed_update=1;//改写变量。每次更改LED灯的状况都要更新一次。
}
void run() //设备主动操控程序
{
switch(ucRunStep)
{
case 0: //机械手处于左上角原点的方位,待命状况。此刻触发发动按键ucRunStep=1,就触发后续一些列的接连动作。
break;
case 1: //机械手从左面往右边移动
left_to_right();
ucRunStep=2;//这便是鸿哥传说中的怎样灵敏操控进程变量
break;
case 2: //等候机械手移动到最右边,直到触发了最右边的开关感应器。
if(ucRightSr==0)//右边感应器被触发
{
ucRunStep=3;//这便是鸿哥传说中的怎样灵敏操控进程变量
}
break;
case 3: //机械手从右上边往右下边移动,从上往下。
up_to_down();
ucRunStep=4;//这便是鸿哥传说中的怎样灵敏操控进程变量
break;
case 4: //等候机械手从右上边移动到右下边,直到触发了右下边的开关感应器。
if(ucDownSr==0)//右下边感应器被触发
{
uiRunTimeCnt=0;//时刻计数器清零,为接下来延时1秒钟做准备
ucRunStep=5;//这便是鸿哥传说中的怎样灵敏操控进程变量
}
break;
case 5: //机械手在右下边延时1秒
if(uiRunTimeCnt>const_1s)//延时1秒
{
ucRunStep=6;//这便是鸿哥传说中的怎样灵敏操控进程变量
}
break;
case 6: //原路回来,机械手从右下边往右上边移动。
down_to_up();
ucRunStep=7;//这便是鸿哥传说中的怎样灵敏操控进程变量
break;
case 7: //原路回来,等候机械手移动到最右边的感应开关
if(ucRightSr==0)
{
ucRunStep=8;//这便是鸿哥传说中的怎样灵敏操控进程变量
}
break;
case 8: //原路回来,等候机械手从右边往左面移动
right_to_left();
ucRunStep=9;//这便是鸿哥传说中的怎样灵敏操控进程变量
break;
case 9: //原路回来,等候机械手移动到最左面的感应开关,表明回来到了原点
if(ucLeftSr==0) //回来到左上角的原点方位
{
ucRunStep=0;//这便是鸿哥传说中的怎样灵敏操控进程变量
}
break;
}
}
void T0_time() interrupt 1
{
TF0=0;//铲除中止标志
TR0=0; //关中止
sensor_scan(); //开关感应器软件抗干扰处理函数
key_scan(); //按键扫描函数
if(uiRunTimeCnt<0xffff) //不要超越最大int类型规模
{
uiRunTimeCnt++; //延时计数器
}
if(uiVoiceCnt!=0)
{
uiVoiceCnt–; //每次进入守时中止都自减1,直到等于零中止。才中止鸣叫
beep_dr=0;//蜂鸣器是PNP三极管操控,低电平就开端鸣叫。
}
else
{
; //此处多加一个空指令,想保持跟if括号句子的数量对称,都是两条指令。不加也能够。
beep_dr=1;//蜂鸣器是PNP三极管操控,高电平就中止鸣叫。
}
TH0=0xf8; //重装初始值(65535-2000)=63535=0xf82f
TL0=0x2f;
TR0=1;//开中止
}
void delay_short(unsigned int uiDelayShort)
{
unsigned int i;
for(i=0;i
; //一个分号相当于履行一条空句子
}
}
void delay_long(unsigned int uiDelayLong)
{
unsigned int i;
unsigned int j;
for(i=0;i
for(j=0;j<500;j++)//内嵌循环的空指令数量
{
; //一个分号相当于履行一条空句子
}
}
}
void initial_myself()//榜首区 初始化单片机
{
/* 注释二:
* 矩阵键盘也能够做独立按键,条件是把某一根公共输出线输出低电平,
* 模仿独立按键的触发地,本程序中,把key_gnd_dr输出低电平。
* 朱兆祺51学习板的S1便是本程序中用到的一个独立按键。
*/
key_gnd_dr=0; //模仿独立按键的地GND,因而有必要一向输出低电平
beep_dr=1; //用PNP三极管操控蜂鸣器,输出高电平时不叫。
TMOD=0x01;//设置守时器0为工作方式1
TH0=0xf8; //重装初始值(65535-2000)=63535=0xf82f
TL0=0x2f;
}
void initial_peripheral() //第二区 初始化外围
{
EA=1; //开总中止
ET0=1; //答应守时中止
TR0=1; //发动守时中止
}
总结陈词:
前面花了很多节内容在讲按键和跑马灯的联系,可是一向没涉及到人机界面,在大多数的实践项目中,人机界面是必不可少的。人机界面的程序结构该怎么样写?欲知概况,请听下回分解—–在主函数while循环中驱动数码管的动态扫描程序。