精华内容
下载资源
问答
  • 使用无线技术,通过单片机串口通信,达到控制小车运动的目的。
  • 悬挂运动控制系统,包含原理图,源程序,包含各大学对悬挂运动控制系统研究
  • 单片机控制双步进电机运动控制系统 ,朱雷平,,采用单片机AT89C55作为控制系统的核心,控制双步进电机带小负载做运动。 单片机接收相应的信号执行显示程序和轨迹控制程序,向LCD显�
  • 51单片机串口控制io带动气缸运动,附带protues电路仿真,单片机C语言程序源代码
  • 这是基于MC9S12XS128单片机开发的程序,通过串口接收到的指令控制小车的左转、右转、前进、后退和停止;小车的电机是直流电机,驱动芯片是L289N.程序简洁易懂,适合入门。
  • 实验室的运动平台控制器坏了,使用51单片机对平台进行控制,包含步进电机,零位开关,限位开关和串口通信
  •  系统由以下几部分构成:①单片机:设计的核心,在软件的配合下实现对键盘所输入信息的识别,根据输入信息向云台中的步进电机发出指令,使其实现正/ 反转、速度控制程序控制等功能,并将步进电机的转速通过数码管...
  • 2.副翼舵面(安装在飞机机翼后缘),用来控制飞机的横滚运动; 3.水平尾舵面,用来控制飞机的俯仰角; 4.垂直尾舵面,用来控制飞机的偏航角; 遥控器有四个通道,分别对应四个舵机,而舵机又通过连杆等传动元件带动...
  • 电子-运动控制程序20160219.rar,单片机/嵌入式STM32-F0/F1/F2
  • 摘要:本文提出了一种基于TMS320F2812 的PCI 总线通用运动控制卡的设计方案。详细介绍了运动控制卡的硬件结构,并对控制卡的配置进行了深入分析,给出了具体的配置参数。最后,介绍了板卡驱动程序的设计方法。采用...
  • 控制器内置完整的控制程序,用户只需设定相应的参数,控制器能自行检测到脉冲信号并同时控制四台电机工作。该控制器也支持手动操作,用户可根据情况自行运作。该控制器的特点就是结构简单,专用性强,操作简捷,...
  • 单片机(STC89C52RC)控制两个直流电机,从而实现各种运动形式。 本例中实现功能为:前进 后退 右转 左转。 C语言程序如下: /*------------------------------------ FileName: main.c Function: MCU控制两个直流...

    项目描述:
    用单片机(STC89C52RC)控制两个直流电机,从而实现各种运动形式。
    本例中实现功能为:前进 后退 右转 左转。
    C语言程序如下:

    /*------------------------------------
    FileName: main.c
    Function: MCU控制两个直流电机组合运动
    Description: 向前 向后 右转 左转
    Author: Zhang Kaizhou
    Date: 2019-6-9 17:28:42
    ------------------------------------*/
    #include "ZKZ.h"
    
    bit dirFlag = 0; // 前进方向标志 0-向前 1-向后
    bit turnFlag = 0; // 转向方向标志 0-向右 1-向左
    
    void main(){
    	while(1){
    		keyScan();
    		motorRun();
    	}
    }
    
    /*软件延时xms函数*/
    void delay(uchar xms){
    	uchar i, j;
    	for(i = xms; i > 0; i--)
    		for(j = 110; j > 0; j--);
    }
    
    /*键盘扫描函数*/
    void keyScan(){
    	if(!goAhead){
    		delay(5);
    		if(!goAhead){
    			while(!goAhead);
    			dirFlag = 0;
    		}
    	}
    	if(!goBack){
    		delay(5);
    		if(!goBack){
    			while(!goBack);
    			dirFlag = 1;
    		}
    	}
    	if(!turnRight){
    		delay(5);
    		if(!turnRight){
    			while(!turnRight);
    			turnFlag = 0;
    		}
    	}
    	if(!turnLeft){
    		delay(5);
    		if(!turnLeft){
    			while(!turnLeft);
    			turnFlag = 1;
    		}
    	}
    }
    
    /*直流电机运转函数*/
    void motorRun(){
    	if(!dirFlag){
    		MLEn = 1;
    		ML1 = 1;
    		ML2 = 0;
    		MREn = 1;
    		MR1 = 1;
    		MR2 = 0;
    	}else{
    		MLEn = 1;
    		ML1 = 0;
    		ML2 = 1;
    		MREn = 1;
    		MR1 = 0;
    		MR2 = 1;
    	}
    	if(!turnFlag){
    		MLEn = 1;
    		MREn = 0;
    	}else{
    		MLEn = 0;
    		MREn = 1;
    	}
    }
    
    /*------------------------------------
    FileName: ZKZ.h
    Function: MCU控制两个直流电机组合运动
    Description: 向前 向后 右转 左转
    Author: Zhang Kaizhou
    Date: 2019-6-9 17:28:42
    ------------------------------------*/
    #include <reg52.h>
    #define uchar unsigned char
    #define uint unsigned int
    
    /*直流电机驱动模块端口定义*/
    sbit MLEn = P1^4; // 左边电机驱动端口
    sbit ML1 = P1^2;
    sbit ML2 = P1^3;
    sbit MREn = P1^5; // 右边电机驱动端口
    sbit MR1 = P1^6;
    sbit MR2 = P1^7;
    
    /*独立按键端口定义*/
    sbit goAhead = P3^4; // 向前
    sbit goBack = P3^5; // 向后
    sbit turnRight = P3^6; // 向右转
    sbit turnLeft = P3^7; // 向左转
    
    /*主模块函数声明*/
    void delay(uchar xms);
    void keyScan();
    void motorRun();
    
    展开全文
  • 基于单片机的步进电机控制系统设计 前言 步进电机是一种进行精确步进运动的机电执行元件它广泛使用于工业机械的数字控 制为使系统的可靠性通用性可维护性以及性价比最优根据控制系统功能要求及步进 电机使用环境确定...
  • 二、编写程序 /******************************************************************************************************************** ---- @Project: LED-74HC595 ---- @File: main.c ---- @Edit: ZHQ ---- ...

    一、使用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单片机控制的,二氧化碳激光平面雕刻系统!可用于平面绣花,片面雕刻,激光切割等!
  • 28点工业级高可靠单片机控制板JMDM-28DIOMRrar,28点工业级高可靠单片机控制板 RS232串口控制器 步进电机控制器 继电器气缸电磁阀控制器 精密转速控制 精密运动控制 精密机械加工控制器包括晶体管输出(JMDM-28DIOMT)...
  • 第二章 单片机输出接口与伺服电机控制;摘要;C51单片机的输入/输出接口;任务一 单灯闪烁控制;任务一 单灯闪烁控制;任务一 单灯闪烁控制;任务一 单灯闪烁控制;任务二 机器人伺服电机控制信号;任务二 机器人伺服电机...
  • 工业级8~20点单片机控制器JMDM-20DIOV2rar,防雷击 抗强干扰全光电隔离数字量 工业级8~20点单片机控制器 RS232串口控制器 步进电机控制器 气缸电磁阀控制器 精密转速控制、精密运动控制、精密机械加工控制器 JMDM-...
  • 此帖子现在有一个后续版本,实现了手机端的蓝牙程序控制小车运动以及接收小车状态并展示,链接地址:http://bbs.elecfans.com/forum.ph ... d&tid=544299&page=1 最初的想法是做一个红外遥控的装置,链接地址: ...

    原文地址:http://bbs.elecfans.com/forum.php?mod=viewthread&tid=544143

    此帖子现在有一个后续版本,实现了手机端的蓝牙程序控制小车运动以及接收小车状态并展示,链接地址:http://bbs.elecfans.com/forum.ph ... d&tid=544299&page=1
    最初的想法是做一个红外遥控的装置,链接地址: 
    http://bbs.elecfans.com/forum.ph ... d&tid=540165&extra=
    我只需要前进和停止,二路遥控,想使用模拟电路搭建,但最后也没能完成,这个想法依然在,现在只能留带以后是否有想法再做了。

    第二个想法是想做一个小车,小车的功能如下: 前进,后退,转弯,遥控控制,自主智能运转。 上某宝买了一个小车的底座(4驱动的,带电机), 买了两个L298N驱动模块用来驱动小车,买了蓝牙模块(HC05)用来充当遥控。

    实践篇:
    1,首先根据51单片机的最小系统的电路图,焊接了一个最小系统板,使用的STC89c52的单片机(晶振6Mhz,带复位电路,复位指示灯显示),为了便于测试,又焊接了一个发光二极管连接一个I/O口,用于测试最小系统。
    2,然后开始测试最小系统,不过我很多年没有用过keil和下载器了,就上网寻找了一下关于这方面的帖子,过程总结如下:

    最小系统测试篇:
    要测试最小系统,首先需要编译代码的工具(我用的keil),下载代码到单片机的工具(stc-isp),usb转ttl硬件设备(某宝上2块多钱买的),串口调试助手(使用的是单片机多功能调试助手PortHelper.exe),于是从网上下载了keil4破解版本,stc-isp下载软件,单片机多功能调试助手三个软件。
    第一步:测试usb转ttl是否可用(就4个引脚,5v,GND, TX, RX)
    打开串口调试助手,设置波特率,打开,发现打开失败,比较郁闷,为何呢? 极有可能是串口号不对,于是打开我的电脑-》管理-》设备管理器,找到串口的条目,查看它的串口号,我的竟然是串口号12.于是设置串口号,重新打开,OK。 如何测试usb转ttl呢? 我的想法是如果发送数据的话,对应的tx引脚应该有信号,如果将TX和RX连接到一起,那么发送出去的应该自己可以接收到。我没有示波器,就简单的使用万用表量它的TX引脚的电压,点击发送按钮,发现TX引脚的电压有波动即可。
    第二步: 将usb转ttl的四个引脚接入到单片机的对应引脚即可(其实就是VCC接VCC,GND接GND,TX接RX,RX接TX)。没有采用外部供电,直接利用usb转ttl进行的5v供电
    第三步:在keil中写代码,对单片机的某一个I/O进行翻转电平的操作,代码如下
    sbit LED=P0^0;
    while(1){
        LED = !LED
        Delay10ms(100);
    }
    第四步:编译程序,生成HEX文件
    第五步:打开stc-isp下载工具,选择单片机型号,hex程序位置,点击下载即可,如果识别了单片机的话,会出现给MCU重新上电的字样,这个时候只需要关闭再打开MCU的电源开关即可,就会出现烧写程序的过程。
    最小系统测试篇遇到的问题,回忆篇:
    51单片机的电源供电问题,忘了接单片机的VCC引脚了(如果是这方面的问题,就检查几个关键地方,vcc和地接好了吗,tx和rx接好了吗,晶振接好了吗,复位电路先不用管,我是使用的万用表一个个的量的)
    晶振的电路没焊好(万用表搞定)
    usb转ttl找不到串口(软件问题的话,容易解决,也有可能是usb转ttl硬件有问题,驱动没有安装成功,导致识别不了硬件)
    第六步: 用万用表量P0^0口,查看电压是否1秒一次变化即可。

    3,蓝牙模块工作篇
    从某宝上花了17大洋买了一个HC05蓝牙主从模块,有6个引脚(VCC,GND,TX,RX,AT,STATUS),前4个引脚与usb转ttl的接法相同(注意RX,TX交叉接线接入到单片机),AT和STatus引脚是我自己命的名字。 AT引脚高电平有效,用于蓝牙模块进入AT状态(所谓AT状态,即是其他程序可以通过它的引脚向蓝牙模块发送AT控制命令,例如设置波特率,查看版本号,设置主从模式),AT引脚悬空默认为低电平。Status引脚用于显示配对的状态(配对成功输出高电平,未配对输出低电平)

    蓝牙模块测试篇:
    第一步:蓝牙模块既然包含串口,那么它应该可以跟usb转ttl直接连接,使用电脑向蓝牙模块发送命令。于是连接蓝牙模块与usb转ttl的对应引脚。
    第二步:将AT引脚悬空,使用手机搜索周围的蓝牙设备,发现有HC05的蓝牙(因为这个蓝牙模块默认是从模块,可以被收到)
    第三步:将AT引脚拉高电平,使其进入AT状态(按照文档描述,如果上电之后再置位AT,指示灯无变化,依旧是,如果上电之前拉高AT,指示灯转为1秒一次),然后通过电脑串口发送AT+VERSION?(注意需要换行)查看版本号,使用AT+UART?查看波特率(默认9600,不带校验)
    此文档有用: ATK-HC05-V11用户手册_V1.0.pdf
    第四步:实验了一下,我的模块有问题,就是上电之前拉高AT,模块不接受AT命令,只有上电后拉高AT才有用,不过不影响使用。
    第五步:将蓝牙模块和usb转ttl连接后,AT保持悬空,即蓝牙模块为从模块,手机安装蓝牙串口助手,打开手机蓝牙,然后搜索HC05,配对,然后打开手机端的蓝牙串口助手软件,向HC05发送消息,如果蓝牙模块工作正常,那么电脑端的串口调试助手应该收到手机发送的消息。同样的也可以通过电脑端发送消息,到手机端。
    消息传递流程如下: 手机端蓝牙串口助手 -> 手机蓝牙 ->空气  -> HC05蓝牙模块 -> USB转ttl模块 -> 电脑的串口调试助手,反过来也是可行的(注意线路连接方式都是RX与TX交叉连接)

    蓝牙模块与单片机集成调试篇:
    上面的测试已经证明了蓝牙模块是可以发送接收手机端消息的,现在开始将蓝牙模块与单片机的TX,RX接口连接起来,通过程序控制蓝牙模块与手机蓝牙进行沟通,从而达到利用手机蓝牙进行遥控的目的。

    main函数如下,主要设置串口波特率,以及开启串口中断
    //PCON:SMOD位默认为0,串行口波特率加倍位
    PCON = 0x80;                //SMOD=1;

    TMOD=0x20;          //8位自动加载计数器
    //TH1=0xfd;  TL1=0xfd;   for 11.0592MHZ and SMOD=0, 
    TH1 = 0xf3;//2400bps
    TL1 = 0xf3;
    TR1=1;    // T1
    //SCON: 0x50=SM0=0, SM1=1,REN=1
    REN=1;   
    SM0=0;
    SM1=1; //串口
    EA=1; //中断
    ES=1; 

    串口中断函数
    void serial() interrupt 4
    {
             char sbuf;
             sbuf=SBUF;
             switch (sbuf)
             {
                     case 'f': direc=4; break;
                     case 'b': direc=5; break;
                     case 'l': direc=6; break;
                     case 'r': direc=7; break;
                     case 's': direc=-1; break;
                     default : LED = !LED; //LED为一个I/O引脚,控制发光二极管
             }
             RI=0;


    遇到的问题:
    利用手机给蓝牙HC05发送消息,如果不是switch中的几个case的话,那么LED灯会明暗变化,但是刚开始的测试却始终不如意。而后仔细查看了代码,并没有发现什么错误,后来怀疑是波特率的问题,因为我的晶振是6Mhz的,蓝牙模块的波特率是9600. 我手动计算了一下:
    采用波特率倍频,那么计算如下 6*1000000*2 / 12 / 32 / 9600 = 3.255, 取整为3. 然后使用3反代入到此式子中
    6*1000000*2 / 12 / 32 /3 = 10416.7 与9600相差1000多,这个似乎差距太大了。我又尝试了其他几个参数,像4800,19200,38400都相差挺大的,估计就是这个原因。于是继续尝试,发现2400的话,相差不多。6*1000000*2/12/32/2400 = 13.02 ,然后使用13代入进去结果为2403.8,与2400的波特率相差很小,估计是可以用的。但是看蓝牙模块的文档,发现它说不支持2400的,我不甘心,就使用usb转ttl模块设置了一下2400,没想到AT命令还真的返回OK了,单片机程序也可以正常工作了。


    4,小车驱动篇:
    将两个L298N模块与单片机的P2口直接相连(小车4轮驱动,每个电机需要两个输入引脚,以及一个使能引脚,那就是12个引脚,我刚开始并不想支持调速的功能,因此使能引脚直接高电平了,就连接了8个I/O口)。注意L298N的GND引脚一定要和单片机的GND共地。

    关于L298N: 它的EN引脚用于使能,EN为高电平,才使能。另外两个输入引脚IN1,IN2根据电平的组合变化会有4种情况(00,10,01,11),电机相应的在00和11停止(这个停止是带电的,类似于锁死的感觉),在10正转,01反转。可以直接使用单片机的VCC和GND连接L298N的IN1和IN2,同时将EN端接VCC,看电机转不转就可以测试L298N模块了。

    编写程序,控制轮子的正转,翻转,停止等。基本上就是以下的这种代码
    void wheelForward(uchar which)
    {
            switch(which)
            {
                    case 1:
                    {        
                            wheel_1_1 = 0;
                            wheel_1_2 = 1;
                            break;
                    }
                    case 2:
                    {
                            wheel_2_1 = 0;
                            wheel_2_2 = 1;
                            break;
                    }
                    case 3:
                    {
                            wheel_3_1 = 0;
                            wheel_3_2 = 1;
                            break;                
                    }
                    case 4:
                    {
                            wheel_4_1 = 0;
                            wheel_4_2 = 1;
                            break;
                    }
            }
    }

    遇到的问题:
    问题1: 刚开始L298N直接连接P0口,死活不转,而直接引出高低电平到某一个电机的IN1,IN2口,电机正常运转。于是猜测是I/O有问题,使用万用表测量,发现P0的I/O在输出高电平的时候,根本不是高电平,而后发了帖子,询问了一下才知道P0口在高电平是呈现高阻态的,需要外部焊接电路加上拉电阻才可工作。我不想焊接过多的电路,就将其I/O换到P2口,可以正常工作
    帖子链接地址: http://bbs.elecfans.com/forum.ph ... 8&page=1#pid4108114
    问题2: 我使用的是路由器的9v直流电源,使用其带动两个L298N,同时将L298N的输出的一个5V高电平接到单片机上给单片机供电,启动4轮驱动,电机只会翁的一声,然后啥也没有,二轮驱动也不转。 上网查看了不少资料,基本上都是电源功率过低,需要将单片机与L298N分别供电才可以。 于是使用笔记本的usb口给单片机供电,使用9v直流电源给电机供电,比刚才好了一些,两轮可以转,但是4个轮子还是转不了。没办法,想到自己有一个小的卡片相机,有镍氢电池8节,然后上网买了一个电池盒,装上去,电机转的吼吼叫,同时L298N给单片机供电也没问题。
    问题3:
    关于L298N同时给单片机供电的问题,大家可以在启动轮子转动的时候量一量单片机的电源电压,会发现在电机启动的一刹那,单片机的电压有一个瞬时的拉低,这样单片机就会复位了。

    5,蓝牙遥控小车汇总篇
    第一步:部署小车,L298N,镍氢电池盒,蓝牙模块组装到一起
    第二步:编写代码,本来可以使用EN口进行调速控制的,但是考虑到还需要使用额外的I/O口,就先不打算做了。
    代码完成的功能:
    小车前进,后退,停止,左转,右转
    小车单个轮子的转动(用于测试)
    小车当前状态的获取(用于后期给小车增加其他模块的时候,例如温度模块,就可以读取温度了)
    小车命令帮助

    代码思路:
    首先完成单个轮子的控制
    再完成小车的控制
    再加入串口接收中断,收到不同命令,设置方向变量
    main程序读取方向变量控制不同的方向

    6,思路篇
    一些比较有特点的思路:
    思路1:一个棘手的问题想实现这样的功能: 用户按键,小车才走,用户松开按键,小车停止。
    我的想法如下: 小车使用一个定时器T0,控制定时器的延时时间为100ms,用户每发送一个前进信号,小车会前进100ms,但是这100ms内小车还需要能够响应外部的命令。例如如果用户在t=0ms发送前进命令,然后在t=50ms发送左转命令,小车能够立刻左转,而不是继续前进100ms,然后才执行左转的命令。基本的代码思路是用户的每一个命令,都会重置timer0,timer0的超时中断函数会将小车停止。这样用户只要以连续的小于100ms间隔发送前进命令,小车就会一直前进,也就是说timer0的中断没有执行,如果用户间隔100ms还没有发送命令,那么小车停止。 当前我使用的蓝牙串口助手它的按键不支持连续发送,只能按下松开手,才会发送消息,这样我现在就把时间间隔设置为1000ms,用户只要以小于1000ms的间隔连续按键,小车就会一直前进,如果不按键,小车会继续前进1000ms才会停止。 后期打算找到蓝牙串口调试助手的源代码,修改它的源代码,设置成如果用户按下不丢,小车前进命令能够一直发送。
    思路2: 既然有了手机蓝牙,那么小车再想展示一些状态信息,就没必要使用像1602,12864之类的东东了,直接定时发给手机蓝牙模块就OK了。我现在并没有实现定时发送,不过支持了命令获取的功能。 当用户发送命令h到单片机,单片机会返回一个帮助界面,告知如何控制小车,例如"f"控制小车前进,"b"控制小车后退,当用户发送命令i到单片机,单片机会返回小车的一些状态信息,我当前只返回了一些简单的变量状态(后面想加入距离,温度,光敏都是可行的)。
    思路3:有了蓝牙,这个小车就可以被我们随心所欲的控制了。你既可以推命令到单片机,控制它,你又可以把单片机内部的当前状态拉出来。单片机本身又可以定期将它的状态向你的手机进行推送。这个我感觉还是很好玩的,有了这个代码的基本框架,后面有可能的话像实现一个小车的扩展功能,加上红外对管让它不撞墙,加上超声波让它测距,加上麦克风让它向着声音跑,加上人体感应让它做一个跟屁虫,甚至于加上一个智能化点的程序,让它能够在一个屋子里随便的转悠,然后记录屋里的情况。还想做的是加入一个wifi模块,能将它的状态信息上传到路由器里面(手里有一个坏的路由器华为hg255d,正在修理中,还没有摸清楚如何修),这样远在千里之外都可以访问小车上面传感器的情况了。还想加的是一个摄像头模块,不过单片机的处理能力有限,摄像头的解码对它是个大问题,估计很难办到(手头有优龙fs2401开发板,刚修好它的电源模块,正在研究中,不过前天它突然bios引导不起来了,也不知道是什么问题,关于这种arm的东西,还没有接触过,完全不懂刷bios,uboot等等的东西,看情况是要重新刷bios了)。

    展开全文
  • 用光敏电阻做传感器,C8051F310单片机控制器,采集到的信息经A/D转换后,由单片机处理;采用PWM脉冲控制电机转速,采用H桥驱动直流电机,实现小车向光强的一侧运动
  • 阐述了该控制系统中DSP控制器的工作原理,给出了自适应LMS算法程序。实验结果表明,该控制系统具有较好的动态特性。  关键词:DSP 变几何桁架机器人 滤波器 LMS算法 八面体变几何桁架机器人是通过八面体机构中可...
  • 控制器的工作方式有2种:根据单片机程序独立控制、上位机串口监控。采用智能S形加减速曲线控制,使电机控制更加平滑稳定。精敏上位机在线控制界面提供了一般应用场合常用的运转参数设置项(如步进电机运转脉冲数、...
  • 系统的CAM工作主要是通过单片机控制完成的,单片机按照输入的加工程序进行插补等各种运算产生进给量,由软硬件相结合的技术实现脉冲分配,输出一系列脉冲信号,经过功放驱动步进电机工作,实现了刀具按照规定曲线...
  • 开场白:前面三节讲了独立按键控制跑马灯的各种状态,这一节我们要做一个机械手控制程序,这个机械手可以左右移动,最左边有一个开关感应器,最右边也有一个开关感应器。它也可以上下移动,最下面有一个开关感应器。...
    开场白:
    前面三节讲了独立按键控制跑马灯的各种状态,这一节我们要做一个机械手控制程序,这个机械手可以左右移动,最左边有一个开关感应器,最右边也有一个开关感应器。它也可以上下移动,最下面有一个开关感应器。左右移动是通过一个气缸控制,上下移动也是通过一个气缸控制。而单片机控制气缸,本质上是通过三极管把信号放大,然后控制气缸上的电磁阀。这个系统机械手驱动部分的输出和输入信号如下:
        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_short  40   //蜂鸣器短叫的持续时间
    
    #define const_key_time1  20    //按键去抖动延时的时间
    
    
    #define const_sensor  20   //开关感应器去抖动延时的时间
    
    #define const_1s  500  //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 int  uiKeyTimeCnt1=0; //按键去抖动延时计数器
    unsigned char ucKeyLock1=0; //按键触发后自锁的变量标志
    
    
    unsigned char ucLeftSr=0;  //左边感应器经过软件抗干扰处理后的状态标志
    unsigned char ucRightSr=0;  //右边感应器经过软件抗干扰处理后的状态标志
    unsigned char ucDownSr=0;  //下边感应器经过软件抗干扰处理后的状态标志
    
    unsigned int  uiLeftCnt1=0;  //左边感应器软件抗干扰所需的计数器变量
    unsigned int  uiLeftCnt2=0;
    
    unsigned int  uiRightCnt1=0;  //右边感应器软件抗干扰所需的计数器变量
    unsigned int  uiRightCnt2=0;
    
    unsigned int  uiDownCnt1=0;   //下边软件抗干扰所需的计数器变量
    unsigned int  uiDownCnt2=0;
    
    unsigned int  uiVoiceCnt=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 int  uiRunTimeCnt=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<uiDelayShort;i++)
       {
         ;   //一个分号相当于执行一条空语句
       }
    }
    
    void delay_long(unsigned int uiDelayLong)
    {
       unsigned int i;
       unsigned int j;
       for(i=0;i<uiDelayLong;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循环中驱动数码管的动态扫描程序。

    展开全文
  • 第二十五节:用LED灯和按键来模拟工业自动化设备的运动控制。 开场白: 前面三节讲了独立按键控制跑马灯的各种状态,这一节我们要做一个机械手控制程序,这个机械手可以左右移动,最左边有一个开关感应器,最...
  • 单片机译码后, 根据预先编写好的程序, 输出相应的指令, 通过电动机的控制电路, 从而控制电动机的运动, 实现对电动车的运动控制的目的。如把设计中的直流电动机换成伺服电机, 再配合单片机可以精确的控制伺服...
  • 文章目录使用Arduino mega 2560读取PS2无线手柄信号并控制小车运动1 前言2 硬件连接2.1 硬件构成简介2.2 硬件连线3 程序编制4 实际测试5 TIPS 1 前言 最近开始学习单片机和树莓派小车的编程相关内容,本例较为简单,...
  • 对于经常接触单片机控制步进电机 伺服电机的工程师来说, 步进电机加减速可以有各种实现方法, 本来有可以用的驱动, 可是我总感觉有什么不完善的地方, 抽时间写了个感觉功能足够完善的, 共享一下, 也希望有大神...
  • 第一个引脚:PWM,作用,控制电机速度,单片机产生(定时器、硬件PWM,PCA产生PWM等); 第二个引脚和第三个引脚分别接AIN2和AIN1,如果是0,0那么停止,10正转,01反转,11也是停止。单片机的普通IO引
  • 这个accelstepper库功能太强大了,以前用51单片机程序真的是很复杂,现在几行代码就搞定了复杂的功能。 以下代码实现的功能是,在串口指令是s就是慢速连续旋转,h就是高速旋转。相对高了,毕竟是减速步进电机。 ...

空空如也

空空如也

1 2 3 4 5
收藏数 87
精华内容 34
关键字:

单片机程序运动控制