一、使用proteus绘制简单的电路图,用于后续仿真

二、编写程序

/********************************************************************************************************************
----    @Project:   LED-74HC595
----    @File:  main.c
----    @Edit:  ZHQ
----    @Version:   V1.0
----    @CreationTime:  20200602
----    @ModifiedTime:  20200603
----    @Description:   用矩阵键盘中的S1键作为启动独立按键,用S5按键模拟左边
----    的开关感应器,用S9按键模拟右边的开关感应器,用S13按键模拟下边的开关感应器。
----    记得把输出线P0.4一直输出低电平,模拟独立按键的触发地GND。
----    开机默认机械手在左上方的原点位置。按下启动按键后,机械手从左边开始往右边移动,当机械手移动
----    到最右边时,机械手马上开始往下移动,最后机械手移动到最右下角的位置时,延时1秒,然后原路返
----    回,一直返回到左上角的原点位置。注意:启动按键必须等机械手处于左上角原点位置时,启动按键的
----    触发才有效。
----    单片机:AT89C52
********************************************************************************************************************/
#include "reg52.h"
/*——————宏定义——————*/
#define FOSC 11059200L
#define T1MS (65536-FOSC/12/1000)   /*1ms timer calculation method in 12Tmode*/#define  const_voice_short   40  /*蜂鸣器短叫的持续时间*/#define   const_key_time1 20  /*按键去抖动延时的时间*/#define   const_sensor    20  /*开关感应器去抖动延时的时间*/#define    const_1s    500 /*开关感应器去抖动延时的时间*//*——————变量函数定义及声明——————*/
/*定义74HC595*/
sbit Hc595_Sh = P2^3;
sbit Hc595_St = P2^4;
sbit Hc595_Ds = P2^5;/*定义蜂鸣器*/
sbit Beep = P2^7;/*定义按键*/
sbit Key_S1 = P0^0; /*定义按键S1,对应S1*/
sbit Key_Left = P0^1;   /*定义开关感应器Left,对应S5*/
sbit Key_Right = P0^2;  /*定义开关感应器Right,对应S9*/
sbit Key_Down = P0^3;   /*定义开关感应器Down,对应S13*/sbit Key_Gnd = P0^4;    /*定义按键模拟地*/unsigned char ucLED1 = 0;   /*代表16个灯的亮灭状态,0代表灭,1代表亮*/
unsigned char ucLED2 = 0;
unsigned char ucLED3 = 0;
unsigned char ucLED4 = 0;
unsigned char ucLED5 = 0;
unsigned char ucLED6 = 0;
unsigned char ucLED7 = 0;
unsigned char ucLED8 = 0;
unsigned char ucLED9 = 0;
unsigned char ucLED10 = 0;
unsigned char ucLED11 = 0;
unsigned char ucLED12 = 0;
unsigned char ucLED13 = 0;
unsigned char ucLED14 = 0;
unsigned char ucLED15 = 0;
unsigned char ucLED16 = 0;unsigned char ucLed_update = 1;  /*刷新变量。每次更改LED灯的状态都要更新一次。*/unsigned char ucLedStatus16_09 = 0;   /*代表底层74HC595输出状态的中间变量*/
unsigned char ucLedStatus08_01 = 0;   /*代表底层74HC595输出状态的中间变量*/unsigned int uiRunTimeCnt = 0;    /*运动中的时间延时计数器变量*/
unsigned char ucRunStep = 0;    /*运动控制的步骤变量*/unsigned char ucKeySec = 0;    /*被触发的按键编号*/unsigned int    uiKeyTimeCnt1 = 0;
unsigned char   ucKeyLock1 = 0; unsigned int    uiLeftCnt1 = 0; /*左边感应器软件抗干扰所需的计数器变量*/
unsigned int    uiLeftCnt2 = 0;
unsigned char   ucLeft = 0; /*左边感应器经过软件抗干扰处理后的状态标志*/unsigned int    uiRightCnt1 = 0;    /*右边感应器软件抗干扰所需的计数器变量*/
unsigned int    uiRightCnt2 = 0;
unsigned char   ucRight = 0;    /*右边感应器经过软件抗干扰处理后的状态标志*/unsigned int    uiDownCnt1 = 0; /*下边感应器软件抗干扰所需的计数器变量*/
unsigned int    uiDownCnt2 = 0;
unsigned char   ucDown = 0; /*下边感应器经过软件抗干扰处理后的状态标志*/unsigned int uiVoiceCnt = 0;    /*蜂鸣器鸣叫的持续时间计数器*//**
* @brief  定时器0初始化函数
* @param  无
* @retval 初始化T0
**/
void Init_T0(void)
{TMOD = 0x01;                    /*set timer0 as mode1 (16-bit)*/TL0 = T1MS;                     /*initial timer0 low byte*/TH0 = T1MS >> 8;                /*initial timer0 high byte*/
}
/**
* @brief  外围初始化函数
* @param  无
* @retval 初始化外围
**/
void Init_Peripheral(void)
{ET0 = 1;/*允许定时中断*/TR0 = 1;/*启动定时中断*/EA = 1;/*开总中断*/}/**
* @brief  初始化函数
* @param  无
* @retval 初始化单片机
**/
void    Init(void)
{Key_Gnd = 0;Beep = 1;Init_T0();
}
/**
* @brief  延时函数
* @param  无
* @retval 无
**/
void Delay_Long(unsigned int uiDelayLong)
{unsigned int i;unsigned int j;for(i=0;i<uiDelayLong;i++){for(j=0;j<500;j++)  /*内嵌循环的空指令数量*/{; /*一个分号相当于执行一条空语句*/}}
}
/**
* @brief  延时函数
* @param  无
* @retval 无
**/
void Delay_Short(unsigned int uiDelayShort)
{unsigned int i;for(i=0;i<uiDelayShort;i++){; /*一个分号相当于执行一条空语句*/}
}/**
* @brief  开关感应器软件抗干扰处理函数
* @param  无
* @retval 放在定时中断里
* 开关感应器的抗干扰处理,本质上类似按键的去抖动处理。唯一的区别是:
* 按键去抖动关注的是IO口的一种状态,而开关感应器关注的是IO口的两种状态。
* 当开关感应器从原来的1状态切换到0状态之前,要进行软件滤波处理过程,一旦成功地
* 切换到0状态了,再想从0状态切换到1状态的时候,又要经过软件滤波处理过程,符合
* 条件后才能切换到1的状态。通俗的话来说,按键的去抖动从1变成0难,从0变成1容易。
* 开关感应器从1变成0难,从0变成1也难。这里所说的"难"是指要经过去抖处理。
**/
void Sensor_Scan(void)
{if(Key_Left == 1)  /*左边感应器是高电平,说明有可能没有被接触*/{uiLeftCnt1 = 0; /*在软件滤波中,非常关键的语句!!!类似按键去抖动程序的及时清零*/uiLeftCnt2 ++;if(uiLeftCnt2 > const_sensor){uiLeftCnt2 = 0;ucLeft = 1;    /*说明感应器确实没有被接触*/}}else{uiLeftCnt2 = 0;  uiLeftCnt1 ++;if(uiLeftCnt1 > const_sensor){uiLeftCnt1 = 0;ucLeft = 0;   /*说明感应器确实被接触到了*/}}if(Key_Right == 1)    /*右边感应器是高电平,说明有可能没有被接触*/{uiRightCnt1 = 0;    /*在软件滤波中,非常关键的语句!!!类似按键去抖动程序的及时清零*/uiRightCnt2 ++;if(uiRightCnt2 > const_sensor){uiRightCnt2 = 0;ucRight = 1;    /*说明感应器确实没有被接触*/}}else{uiRightCnt2 = 0; uiRightCnt1 ++;if(uiRightCnt1 > const_sensor){uiRightCnt1 = 0;ucRight = 0;   /*说明感应器确实被接触到了*/}}if(Key_Down == 1) /*下边感应器是高电平,说明有可能没有被接触*/{uiDownCnt1 = 0; /*在软件滤波中,非常关键的语句!!!类似按键去抖动程序的及时清零*/uiDownCnt2 ++;if(uiDownCnt2 > const_sensor){uiDownCnt2 = 0;ucDown = 1;    /*说明感应器确实没有被接触*/}}else{uiDownCnt2 = 0;  uiDownCnt1 ++;if(uiDownCnt1 > const_sensor){uiDownCnt1 = 0;ucDown = 0;   /*说明感应器确实被接触到了*/}}
}/**
* @brief  扫描按键
* @param  无
* @retval 放在定时中断里
**/
void Key_Scan(void)
{if(Key_S1 == 1)    /*IO是高电平,说明按键没有被按下,这时要及时清零一些标志位*/{ucKeyLock1 = 0;uiKeyTimeCnt1 = 0;}else if(ucKeyLock1 == 0)  /*有按键按下,且是第一次被按下*/{uiKeyTimeCnt1 ++; /*累加定时中断次数*/if(uiKeyTimeCnt1 > const_key_time1){uiKeyTimeCnt1 = 0;ucKeyLock1 = 1;    /*自锁按键置位,避免一直触发*/ucKeySec = 1;}}
}
/**
* @brief  按键服务的应用程序
* @param  无
* @retval 无
**/
void Key_Service(void)
{switch(ucKeySec)   /*按键服务状态切换*/{case 1:    /*启动按键,对应S1*/if(ucLeft == 0) /*处于左上角原点位置*/{ucRunStep = 1;    /*启动*/uiVoiceCnt = const_voice_short;   /*按键声音触发,滴一声就停。*/                }ucKeySec = 0;  /*响应按键服务处理程序后,按键编号清零,避免一致触发*/break;}
}
/**
* @brief  595驱动函数
* @param  无
* @retval * 两个联级74HC595的工作过程:
* 每个74HC595内部都有一个8位的寄存器,两个联级起来就有两个寄存器。ST引脚就相当于一个刷新
* 信号引脚,当ST引脚产生一个上升沿信号时,就会把寄存器的数值输出到74HC595的输出引脚并且锁存起来,
* DS是数据引脚,SH是把新数据送入寄存器的时钟信号。也就是说,SH引脚负责把数据送入到寄存器里,ST引脚
* 负责把寄存器的数据更新输出到74HC595的输出引脚上并且锁存起来。
**/
void HC595_Drive(unsigned char ucLedStatusTemp16_09, unsigned char ucLedStatusTemp08_01)
{unsigned char i;unsigned char ucTempData;Hc595_Sh = 0;Hc595_St = 0;    ucTempData = ucLedStatusTemp16_09;  /*先送高8位*/for(i = 0; i < 8; i ++){if(ucTempData >= 0x80){Hc595_Ds = 1;}else{Hc595_Ds = 0;}Hc595_Sh = 0;    /*SH引脚的上升沿把数据送入寄存器*/Delay_Short(15); Hc595_Sh = 1;Delay_Short(15);  ucTempData = ucTempData <<1;}ucTempData = ucLedStatusTemp08_01;   /*再先送低8位*/for(i = 0; i < 8; i ++){if(ucTempData >= 0x80){Hc595_Ds = 1;}else{Hc595_Ds = 0;}Hc595_Sh = 0;   /*SH引脚的上升沿把数据送入寄存器*/Delay_Short(15); Hc595_Sh = 1;Delay_Short(15);  ucTempData = ucTempData <<1;}Hc595_St = 0;    /*ST引脚把两个寄存器的数据更新输出到74HC595的输出引脚上并且锁存起来*/Delay_Short(15);Hc595_St = 1;Delay_Short(15);Hc595_Sh = 0; /*拉低,抗干扰就增强*/Hc595_St = 0;Hc595_Ds = 0;
}
/**
* @brief  LED更新函数
* @param  无
* @retval
* 把74HC595驱动程序翻译成类似单片机IO口直接驱动方式的过程。
* 每次更新LED输出,记得都要把ucLed_update置1表示更新。
**/
void LED_Update()
{if(ucLed_update == 1){ucLed_update = 0;    /*及时清零,让它产生只更新一次的效果,避免一直更新。*/if(ucLED1 == 1){ucLedStatus08_01 = ucLedStatus08_01 | 0x01;}else{ucLedStatus08_01 = ucLedStatus08_01 & 0xfe;}if(ucLED2 == 1){ucLedStatus08_01 = ucLedStatus08_01 | 0x02;}else{ucLedStatus08_01 = ucLedStatus08_01 & 0xfd;}if(ucLED3 == 1){ucLedStatus08_01 = ucLedStatus08_01 | 0x04;}else{ucLedStatus08_01 = ucLedStatus08_01 & 0xfb;}if(ucLED4 == 1){ucLedStatus08_01 = ucLedStatus08_01 | 0x08;}else{ucLedStatus08_01 = ucLedStatus08_01 & 0xf7;}     if(ucLED5 == 1){ucLedStatus08_01 = ucLedStatus08_01 | 0x10;}else{ucLedStatus08_01 = ucLedStatus08_01 & 0xef;}if(ucLED6 == 1){ucLedStatus08_01 = ucLedStatus08_01 | 0x20;}else{ucLedStatus08_01 = ucLedStatus08_01 & 0xdf;}if(ucLED7 == 1){ucLedStatus08_01 = ucLedStatus08_01 | 0x40;}else{ucLedStatus08_01 = ucLedStatus08_01 & 0xbf;}if(ucLED8 == 1){ucLedStatus08_01 = ucLedStatus08_01 | 0x80;}else{ucLedStatus08_01 = ucLedStatus08_01 & 0x7f;}if(ucLED9 == 1){ucLedStatus16_09 = ucLedStatus16_09 | 0x01;}else{ucLedStatus16_09 = ucLedStatus16_09 & 0xfe;}if(ucLED10 == 1){ucLedStatus16_09 = ucLedStatus16_09 | 0x02;}else{ucLedStatus16_09 = ucLedStatus16_09 & 0xfd;}if(ucLED11 == 1){ucLedStatus16_09 = ucLedStatus16_09 | 0x04;}else{ucLedStatus16_09 = ucLedStatus16_09 & 0xfb;}if(ucLED12 == 1){ucLedStatus16_09 = ucLedStatus16_09 | 0x08;}else{ucLedStatus16_09 = ucLedStatus16_09 & 0xf7;}     if(ucLED13 == 1){ucLedStatus16_09 = ucLedStatus16_09 | 0x10;}else{ucLedStatus16_09 = ucLedStatus16_09 & 0xef;}if(ucLED14 == 1){ucLedStatus16_09 = ucLedStatus16_09 | 0x20;}else{ucLedStatus16_09 = ucLedStatus16_09 & 0xdf;}if(ucLED15 == 1){ucLedStatus16_09 = ucLedStatus16_09 | 0x40;}else{ucLedStatus16_09 = ucLedStatus16_09 & 0xbf;}if(ucLED16 == 1){ucLedStatus16_09 = ucLedStatus16_09 | 0x80;}else{ucLedStatus16_09 = ucLedStatus16_09 & 0x7f;}HC595_Drive(ucLedStatus16_09, ucLedStatus08_01);}
}
/**
* @brief  机械手变化函数
* @param  无
* @retval 无
**/
void Left2Right(void)
{ucLED1 = 1;    /*1代表左右气缸从左边移动到右边*/ucLed_update = 1;
}
void Right2Left(void)
{ucLED1 = 0;    /*0代表左右气缸从右边移动到左边*/ucLed_update = 1;
}
void Up2Down(void)
{ucLED2 = 1;    /*1代表左右气缸从上边移动到下边*/ucLed_update = 1;
}
void Down2Up(void)
{ucLED2 = 0;    /*0代表左右气缸从下边移动到上边*/ucLed_update = 1;
}
/**
* @brief  设备自动控制程序
* @param  无
* @retval 无
**/
void Run(void)
{switch(ucRunStep){case 0:  /*机械手处于左上角原点的位置,待命状态。此时触发启动按键ucRunStep=1,就触发后续一些列的连续动作。*/break;case 1:    /*机械手从左边往右边移动*/Left2Right();ucRunStep = 2;break;case 2: /*等待机械手移动到最右边,直到触发了最右边的开关感应器。*/if(ucRight == 0)  /*右边感应器被触发*/{ucRunStep = 3;}break;case 3:   /*机械手从上往下移动*/Up2Down();ucRunStep = 4;break;case 4:  /*等待机械手从右上边移动到右下边,直到触发了右下边的开关感应器。*/if(ucDown == 0){uiRunTimeCnt = 0; /*时间计数器清零,为接下来延时1秒钟做准备*/ucRunStep = 5;}break;case 5:if(uiRunTimeCnt > const_1s){/*该步骤计数器不用清零*/ucRunStep = 6;}break;case 6:Down2Up();ucRunStep = 7;break;case 7:   /*原路返回,等待机械手移动到最右边的感应开关*/if(ucRight == 0){ucRunStep = 8;}break;case 8:   /*原路返回,等待机械手从右边往左边移动*/Right2Left();ucRunStep = 9;break;case 9:   /*原路返回,等待机械手移动到最左边的感应开关,表示返回到了原点*/if(ucLeft == 0) /*返回到左上角的原点位置*/{ucRunStep = 0;}break;}
}/**
* @brief  定时器0中断函数
* @param  无
* @retval 无
**/
void ISR_T0(void)   interrupt 1
{TF0 = 0;  /*清除中断标志*/TR0 = 0; /*关中断*/if(uiRunTimeCnt < 0xffff)   /*设定这个条件,防止uiTimeCnt超范围。*/{uiRunTimeCnt ++;      }Key_Scan();    Sensor_Scan();if(uiVoiceCnt != 0){uiVoiceCnt--; /*每次进入定时中断都自减1,直到等于零为止。才停止鸣叫*/Beep=0;  /*蜂鸣器是PNP三极管控制,低电平就开始鸣叫。*/}else{; /*此处多加一个空指令,想维持跟if括号语句的数量对称,都是两条指令。不加也可以。*/Beep=1;  /*蜂鸣器是PNP三极管控制,高电平就停止鸣叫。*/} TL0 = T1MS;                     /*initial timer0 low byte*/TH0 = T1MS >> 8;                /*initial timer0 high byte*/TR0 = 1; /*开中断*/
}/*——————主函数——————*/
/**
* @brief  主函数
* @param  无
* @retval 实现LED灯闪烁
**/
void main()
{/*单片机初始化*/Init();/*延时,延时时间一般是0.3秒到2秒之间,等待外围芯片和模块上电稳定*/Delay_Long(100);/*单片机外围初始化*/   Init_Peripheral();while(1){/*设备自动控制程序*/Run();/*LED更新函数*/LED_Update(); /*按键服务的应用程序*/Key_Service();}
}

三、仿真实现

51单片机实现用LED灯和按键来模拟工业自动化设备的运动控制

单片机学习笔记————51单片机实现用LED灯和按键来模拟工业自动化设备的运动控制相关推荐

  1. 单片机学习笔记————51单片机实现带数码管显示的象棋比赛专用计时器

    一.使用proteus绘制简单的电路图,用于后续仿真 二.编写程序 /***************************************************************** ...

  2. 单片机学习笔记————51单片机实现数码管中的倒计时程序

    一.使用proteus绘制简单的电路图,用于后续仿真 二.编写程序 /***************************************************************** ...

  3. 单片机学习笔记————51单片机实现带数码管显示的加法简易计算器

    一.使用proteus绘制简单的电路图,用于后续仿真 二.编写程序 /***************************************************************** ...

  4. 单片机学习笔记————51单片机实现在数码管中实现iphone4S开机密码锁的程序

    一.使用proteus绘制简单的电路图,用于后续仿真 二.编写程序 /***************************************************************** ...

  5. 单片机学习笔记————51单片机实现主机的串口收发

    proteus虚拟串口的实现:https://mp.csdn.net/console/editor/html/107251649 一.使用proteus绘制简单的电路图,用于后续仿真 二.编写程序 / ...

  6. 单片机学习笔记————51单片机实现矩阵键盘的组合按键触发

    一.使用proteus绘制简单的电路图,用于后续仿真 二.编写程序 /***************************************************************** ...

  7. 单片机学习笔记————51单片机实现按住一个独立按键不松手的加速匀速触发

    一.使用proteus绘制简单的电路图,用于后续仿真 二.编写程序 /***************************************************************** ...

  8. C51汇编语言寻址方式,单片机学习:51单片机寻址方式详解

    原标题:单片机学习:51单片机寻址方式详解 51单片机是对所有兼容Intel 8031指令系统的单片机的统称.该系列单片机的始祖是Intel 8031单片机,后来随着Flash rom 技术的发展,8 ...

  9. DSP(TMSF280049C)学习笔记2:点亮LED灯

    DSP(TMSF280049C)学习笔记2:点亮LED灯 实验目的:点亮Texas InstrumentsTMSF280049C开发板,LED0与GPIO23相连,下载到FLASH,具体代码如下: # ...

最新文章

  1. 五大洲30国在华留学生千年古城欢度中国年
  2. R语言plotly可视化:plotly可视化基本散点图(指定图像类型、模式)、plotly可视化散点图(为不同分组数据配置不同的色彩)、ggplotly使用plotly包呈现ggplot2的可视化结果
  3. 如何通过预加载器提升网页加载速度
  4. Java GC系列(4):垃圾回收监视和分析
  5. mysql memory=off_MySQL内存调优
  6. Excel表格模板:记帐汇总凭证表下载
  7. Layui layer详细参数解释说明
  8. win8+sdk8+vs2012+freeglut+glew开发opengl
  9. 微信回应 WeTool 被封事件;支付宝小程序开放直播功能;Raspberry Pi 4 发布 8GB 版本| 极客头条...
  10. 1117 Eddington Number
  11. spring3,上传文件ApplicationHttpRequest cannot be ca...
  12. 搭建SSM全流程框架过程
  13. JSP自定义标签开发步骤
  14. java什么叫元素_java-什么是HTTP标头元素?
  15. 手把手教学电信天翼校园接入无线路由器(Windows Server版)
  16. 微信小程序开发教程、小程序资讯、小程序demo合揖(10月16日更新)
  17. 通达信标记符号_通达信添加标记符号
  18. CPRI之TDD开关控制字
  19. ros建图过程中给上位机发布地图信息
  20. 使用Rancher的RKE快速部署Kubernetes集群

热门文章

  1. 为测试赋能,腾讯WeTest探索手游AI自动化测试之路
  2. EXE文件也能直接制作成屏保吗?
  3. xshell退出当前文件夹_xshell基本语法
  4. python中稀疏矩阵的常用表示COO LIL CSR CSC【上篇】
  5. a标签下载文件的两种姿势
  6. AT155 高压绝缘电阻测试仪 都有哪些功能?
  7. KEIL5兼容KEIL4方法
  8. quartus软件调用代码模板
  9. 从编译错误引发的: Excel与Wps各自VBE中引用内容的差别
  10. java 二叉树的实现以及前序、中序、后续遍历实现