1、项目需求分析
项目目标意义:
 随着社会的发展、 科技的进步以及人们生活水平的逐步提高, 各种方便于生活的遥控系统开始进入了人们的生活。 电梯的发展是由于需要从山坡上运输包括煤和材在内的原材料而引发的,而到了今日,电梯的已经遍布于各种写字楼,住宅,工厂,医院等。其操作简单。使用者坐好后按下按钮即可运作,到达自己想到的楼层后松开按钮即可停止; 舒适安全。楼道电梯可以让使用者在坐着的状态下到达高楼层。楼道电梯上有安全带,使用者可以系上安全带确保安全。如果楼道电梯在运行时遇上障碍物的话,便会自动停止运行。除此之外,楼道电梯在停电时仍可运行10个往返,使用者无需担心突然停电的风险。占地面积小。楼道电梯有可折叠的设计,根据款式楼道电梯折叠后离墙距离为28-44cm不等,展开后离墙距离53-66cm不等。
功能需求分析:
 本项目主要应用了STM32F103R6单片机作为核心,综合应用了STM32中断系统、定时器、计数器等知识,用按键控制楼梯的上下走向,用四位的共阴极的数码管来显示楼梯层层数和变化。
 系统开发环境需求分析:
 本项目主要应用kell5来编写代码,proteus设计仿真图,来模拟功能的实现。
非功能性需求分析(成本、社会意义、使用便利性能):
 用软件仿真实现简单的楼梯层控制系统,.使用4位显示屏来显示要进行的操作(上,下,停止),电梯楼层使用人在某个楼层按下电梯号,电梯进行上下移动每一层可以选择上或者下。实现简单的电梯控制。
2、项目硬件系统设计
2.1、 系统方案原理图
Proteus仿真图:
 
如图所示,使用的芯片为STM32F103R6,PA0-PA7引脚接7SEG-MPX4-CA七段数码管的ABCDEFG和DP,2SC1162通用型高频中功率三极管连接了PB0到PB3引脚、直流发电机和七段数码管的1234,PA8到PA14连接图中左边的电梯运行状态显示LED。当开始运行时,电梯停在1层,状态为停止。当点击右侧2楼上按钮时,电梯先把停止状态改为上升状态,同时左侧的D2(2上)的LED状态灯点亮,之后再改变楼层数,到达二层后先点亮开门的LED灯,开门LED指示灯熄灭后再把电梯状态由上升改为停止。

当开始运行时,电梯停在1层,状态为停止。当点击右侧3楼下按钮时,电梯先把停止状态改为上升状态,同时左侧的D4(下)的LED状态灯点亮,之后再改变楼层数,到达三层后先点亮开门的LED灯,开门LED指示灯熄灭后再把电梯状态由上升改为停止。
2.2、STM32F10X原理介绍
 1)内核架构–CM3内核架构:
 
STM32F103是一个ARM单片机,内嵌有一个Cortex-M3(简称CM3)处理器作为核心。
 CM3的三条总线通过总线矩阵与STM32F103主要部件及外设相连。
 STM32F103的架构包括CM3的内核架构和除内核以外的其他架构。
 2)DMA(Direct Memory Access,直接存储器访问):早期用来提供高速外设和存储器之间或者存储器和存储器之间的高速数据传输的一种技术。(不需执行指令,硬件控制传输)
 因此DMA可以在高速数据采集,数据移动和周期性数据处理等方面有效地降低CPU负荷,使系统运行更加高效。STM32 系列MCP的外设(不管高速、中低速都提供了DMA数据传输方式)
 3)GPIO:(General Purpose Input Output,通用输入输出接口)是MCU与外部电路和设备连接的基本外设。也就是常说的端口或管脚。
 GPIO8种工作方式,可由软件配置:
 1、浮空输入:上下拉电阻断开,用于不确定高低电平的输入。(复位后处于此状态
 2、上拉输入:上拉电阻接通,默认高电平输入。
 3、下拉输入:下拉电阻接通,默认低电平输入。
 4、模拟输入:施密特触发器截止,模拟量输入常用于AD转换
 5、开漏输出:上部MOS管不工作,只能输出低电平,不能输出高电平。如果要输出高电平则需要外接上拉。用于实现电平转换和线与功能的输出。
 6、推挽式输出:上下MOS管交替导通,用于较大功率驱动的输出。
 7、推挽式复用输出功能:复用功能情况下的推挽输出。
 8、开漏复用输出功能:复用功能情况下的开漏输出。
 4)重映射:STM32F103有80个GPIO端口,其中的一些还可以把复用功能重新映射到其他引脚,以实现优化管脚数目和配置目的。(重映射是系统做好的,不能随便映射)
 5)NVIC中断控制器:(Nested Vectored Interrupt Controller,NVIC)位于CM3内核,是中断系统硬件设施,用来管理和配置中断。
 NVIV的功能:(硬件+控制寄存器组成)
 1、管理中断源2、保存中断申请信号3、优先级判断,中断嵌套4、屏蔽中断5、中断优先级设置
 6)EXTI控制器:主要用来管理片外引脚中断 和事件控制。
 EXTI控制器是片上外设,可以管理 16路外部引脚中断 和 4路事件 的控制, 共20 路输入。
 EXTI --外部中断/事件线路映像:
 80个通用I/O端口:均可做外部中断或事件输入线,连接到16个外部中断/事件线上。使用时,须设置AFIO功能,并使能其时钟。(可以是中断也可以是事件)
 四个专用的线: 1、EXTI线16连接到PVD输出(中断)。 2、EXTI线17连接到RTC闹钟事件。 3、EXTI线18连接到USB唤醒事件。 4、EXTI线19连接到以太网唤醒事件
 注释:PVD(可编程电压监控器),可管理上电复位和掉电中断
2.3、 LED灯模块原理
 由于该设计时一个模拟电梯控制系统的开门关门状态难以直接描述,故用LED灯的亮和灭两张状态来表示门的开于关。同时每一个按键分别用一个小灯来指示按键是否按下,使得电梯运行过程中乘客更清楚了解电梯运行状态。本系统共有7个LED灯,正极接电源,负极接stm32的GPIO引脚。其中一个LED用用来表示开门关门的状态,灯亮时为开门。其余六个灯用来表示电梯要进行的操作,如2楼上等。当系统查询到由按键被按下时
 系统立刻使stm32相应GPIO口为低电平,点亮相应小灯。系统保持LED灯的状态直到电梯完成相应的操作。
 
2.4、按键模块原理
 按键由一组独立按键构成,由于弹性按键结构简单,价格低廉,故选用弹性按键。同时,每个按键单独连接芯片的对应引脚,简化了电路的设计。本设计共有12个独立的弹性小键来控制电梯升降,其均与相对应的GPIO口直接相连。所有GPIO口设置为上拉输入电平触发方式。一旦小键按下,相应的引脚与地接通成低电平,当程序进入按键查询时为低电平,表示键被按下,按键弹起后为高电平。系统根据小灯的状态立即或等待执行相应的操作。
 
2.5、数码管模块原理
 数码管功耗虽然比液晶显示器大而且显示09及AF,但其驱动电路简单,使用方法简单且价格便宜。本设计电梯只有四层,因此只需要显示电梯层数再1到4。综合考虑数码管比较合适。无论几位的数码管,其原理均为点亮内部的二极管来发亮。故当需要显示数字0到9时,首先对其进行编码,当要显示的某个数字式像数码管送入相应的编码即可显示该数字。

3、系统软件体系设计
3.1 项目软件系统总架构图
***见文档 ***
 电梯在运行中,先判断是否需要电梯,需要时判断电梯运行方向,接着判断当前是否为目标楼层,目标楼层时停下开门,不是时看上方时候需要电梯,需要时上移,不需要时反向、
 程序先进行按键的定义和初始化,按键也就是某层楼梯控制信号发出的定义,信号接收定义在PC0到PC11引脚,信号输出定义在PC0到PC14引脚,设置中断函数,判断程序是否需要改变楼梯层状态和开门关门状态。
 然后进行进行GPIO的初始化,即7SEG-MPX4-CA接芯片的PA0PA7和PB0PB3,PA8~PA14接指示灯,初始化状态:指示灯不亮,继电器不闭合。
3.2 重要子模块流程图
如果开始时电梯在第二层,那么电梯由两种运行状态,即上行和下行,系统会通过if语句先行判断是否由下行要求,即if是否查询到LED_2(2上)==0||LED_3(2下)0;如是系统会下行。否则,系统会通过if语句再次判断是否由下行要求,即if查询到的LED_4(3上).0||LED_5(3下)==0,若是系统则上行。
3.3 重要的数据结构、参数和函数分析
 3.3.1 KEY_Init
 设置STM32的GPIO口为输入模式时,通过读取IO口状态,判断是否有按键按下。为避免干扰产生一些误操作,在检测到电平变化后延时一段时间,再重新读取IO口状态。
 代码如下:
void KEY_Init(void)
	// 在使用I/O前,首先 开时钟->设置I/O工作模式
	RCC->APB2ENR |= (1 << 4); //开时钟
	// 上拉输入:区别在于没有输入信号的时候默认输入高电平(因为有弱上拉)
	// 下拉输入:区别在于没有输入信号的时候默认输入低电平(因为有弱下拉)
	//  对于上下拉输入,在设置I/O模式位 1000 
	//                  还应设置上拉下拉类型 GPIOx->ODR相应位
	// 浮空输入:输入什么信号才是什么信号,对于浮空输入要保证有明确的输入信号
	
	//开GPIOC时钟
	RCC->APB2ENR |= (1 << 4);
	GPIOC->CRL &=0x00000000; // 
	GPIOC->CRL |=0x11111111; // 设置C组I/O 0-9 为0011 最大速度通用推挽输出
	GPIOC->CRH &=0x00000000; 
	GPIOC->CRH |=0x00001111; //上拉
	GPIOC->ODR |=0xFFFFFFFF ;
	/*
	// PC9
	GPIOC->CRH&=0XFFFFFF0F;      
  GPIOC->CRH|=0X00000080;  
	GPIOC->ODR|=1<<9;           // 上
	// PC8
	GPIOC->CRH&=0XFFFFFFF0;      
  GPIOC->CRH|=0X00000008;  
	GPIOC->ODR|=1<<8;           // 上
	// PC7
	GPIOC->CRL &=0x0FFFFFFF;//清空
	GPIOC->CRL |= 0X80000000;//上/下拉输入
	GPIOC->ODR|=1<<7; 
	// PC6
	GPIOC->CRL &=0xF0FFFFFF;//清空
	GPIOC->CRL |= 0X08000000;//上/下拉输入
	GPIOC->ODR|=1<<6;           // 上
	// PC5
	GPIOC->CRL &=0xFF0FFFFF;//清空
	GPIOC->CRL |= 0X00800000;;//上/下拉输入
	GPIOC->ODR|=1<<5;            // 上
	// PC4
	GPIOC->CRL &=0xFFF0FFFF;//清空
	GPIOC->CRL |= (0x8 << 4 * 4);//上/下拉输入
	GPIOC->ODR|=1<<4; 
	// PC3
	GPIOC->CRL &=0xFFFF0FFF;//清空
	GPIOC->CRL |= (0x8 << 4 * 3);//上/下拉输入
  GPIOC->ODR|=1<<3;  
	// PC2
	GPIOC->CRL &=0xFFFFF0FF;//清空
	GPIOC->CRL |= (0x8 << 4 * 2);//上/下拉输入
  GPIOC->ODR|=1<<2;  
	// PC1
	GPIOC->CRL &=0xFFFFFF0F;//清空
	GPIOC->CRL |= (0x8 << 4 * 1);//上/下拉输入
  GPIOC->ODR|=1<<1;  
	// PC0
	GPIOC->CRL &=0xFFFFFFF0;//清空
	GPIOC->CRL |= (0x8 << 4 * 0);//上/下拉输入
  GPIOC->ODR|=1<<0;  
	*/
} 
3.3.2 NVIC
 向量中断控制器(NVIC)与Cortex-M3内核的逻辑紧密耦合、相互交融,共同完成对中断的控制。STM32有两个优先级概念—抢占式优先级和响应优先级,响应优先级又被称为亚优先级或副优先级,STM32的每个中断源都需要指定这两种优先级。
//Ex_NVIC_Config专用定义
#define GPIO_A 0
#define GPIO_B 1
#define GPIO_C 2
#define GPIO_D 3
#define GPIO_E 4
#define GPIO_F 5
#define GPIO_G 6 
#define FTIR   1  //下降沿触发
#define RTIR   2  //上升沿触发
								   
//JTAG模式设置定义
#define JTAG_SWD_DISABLE   0X02
#define SWD_ENABLE         0X01
#define JTAG_SWD_ENABLE    0X00	
/  
void Stm32_Clock_Init(u8 PLL);  //时钟初始化  
void Sys_Soft_Reset(void);      //系统软复位
void Sys_Standby(void);         //待机模式 	
void MY_NVIC_SetVectorTable(u32 NVIC_VectTab, u32 Offset);//设置偏移地址
void MY_NVIC_PriorityGroupConfig(u8 NVIC_Group);//设置NVIC分组
void MY_NVIC_Init(u8 NVIC_PreemptionPriority,u8 NVIC_SubPriority,u8 NVIC_Channel,u8 NVIC_Group);//设置中断
void Ex_NVIC_Config(u8 GPIOx,u8 BITx,u8 TRIM);//外部中断配置函数(只对GPIOA~G)
void JTAG_Set(u8 mode);
//
//以下为汇编函数
void WFI_SET(void);		//执行WFI指令
void INTX_DISABLE(void);//关闭所有中断
void INTX_ENABLE(void);	//开启所有中断
void MSR_MSP(u32 addr);	//设置堆栈地址
#endif
4、项目运行效果展示
 程序运行初态(没有按键按下):显示屏显示2 1,指示灯不亮。
 按下2上按键,
 显示屏:1 1(表示电梯当前再1层,状态为上升)
 指示灯: 2上指示灯点亮
显示屏:1 2(表示电梯当前再2层,状态为上升)
 指示灯:开门指示灯点亮,开门指示灯灭
显示屏:2 2(表示电梯当前再2层,状态为停止)
 指示灯:2上指示灯灭
其余按键同理
5、总结与心得体会
 总结:通过基于STM32芯片,七段数码管,三极管等原件,用proteus模拟仿真了电梯的升降,实现了用按键实现电梯的楼梯层控制的操作。
 心得体会:在知识方面,学习到了楼梯层控制和7SEG-MPX4-CA七段数码管的一点浅显知识,加深了接口这门课程所学习到的芯片内核结构、GPIO、中断等知识,充分的练习了单片机c语言的编程规则,也对一些基本的数理逻辑问题和事理逻辑问题有了更深入的体会。但这次课程设计对于我来说最重要的是,让我明白了理论与实际动手的差距。我以为之前做了很多的练习,现在做起来应该很容易,在整个过程中还是遇到了许多问题,虽然整个过程很累,感受到了在遇到问题时的无助和绝望,但当自己通过各种方法把问题一件件的解决后,那种快乐是无以言表的。让我明白也学会了,知识要动手实践才能真正的转换为自己的。
 局限性:当短时间内先后按下按键时,本系统以最后一次按下的楼梯层为准,无法实现多楼梯层同时需要电梯时的情景。
6、附录
#include "main.h"
#include "delay.h"
#include "led.h"
#include "key.h"
#include "timer.h"
 char flag1;		//定时时间到标志
 u8 counter;		//计数器
u8 state;		//当前电梯状态
u8 statepre;	//之前电梯状态
u8 flag;		//电梯上下标志,0下,1上
u8  i;
char in1=0, in2=0, in3=0, in4=0, up1=0, up2=0, up3=0, down2=0, down3=0, down4=0;	//KEY
u8 table[]={0xf9, 0xa4,0xb0,0x99,  0xa1, 0xc1, 0xff};        //楼层显示码表,一共是四层
void keyscan(void);
void elvator(void);
void display()
	{
	GPIOB->ODR &=0xFFFFFFF0 ;
	set_DigitalNum(state);  //楼乘标号	
	GPIOB->ODR |=0x00000002 ;
  Delay_ms(2);
		
	GPIOB->ODR &=0xFFFFFFF0 ;	
  set_DigitalNum(flag);
  GPIOB->ODR |=0x00000008 ;
  Delay_ms(2);
}
void iniclock()      //start clock
{
TIM3->CR1|=0x01; 
}	
void run()
{
	for(i=0;i<50;i++)//100ms
	{
			keyscan();
			display();
	}
}
int main()
{
//NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 设置中断优先级分组2	
	Delay_Init(); // 延时程序初始化
	Led_Init();   // LED灯初始化
	MultDigitalNum_Init(); // 数码管初始化
	KEY_Init();            // 按键初始化
//TIM3_Int_Init(499,7199);//50ms进入一次中断
	
   	state=1;
		statepre=1;
		flag=1;//上
		flag1=0;
	while(1)
	 {
		 elvator();
	}
}
void keyscan(void)
{
//内一按下动作	
	if(!(GPIOC->IDR & (1 << 0)))
	{
		Delay_ms(1); //消抖
		if(!(GPIOC->IDR & (1 << 0)))
		{
			in1=1;			//被按下后标记,亮灯,下同
			while(!(GPIOC->IDR & (1 << 0)));
		}
	}
//内二按下动作		
	if(!(GPIOC->IDR & (1 << 1)))
	{
		Delay_ms(1); //消抖
		if(!(GPIOC->IDR & (1 << 1)))
		{
			in2=1;			//被按下后标记,亮灯,下同
			while(!(GPIOC->IDR & (1 << 1)));
		}
	}	
//内三按下动作		
	if(!(GPIOC->IDR & (1 << 2)))
	{
		Delay_ms(1); //消抖
		if(!(GPIOC->IDR & (1 << 2)))
		{
			in3=1;			//被按下后标记,亮灯,下同
			while(!(GPIOC->IDR & (1 << 2)));
		}
	}	
//内四按下动作	
	if(!(GPIOC->IDR & (1 << 3)))
	{
		Delay_ms(1); //消抖
		if(!(GPIOC->IDR & (1 << 3)))
		{
			in4=1;			//被按下后标记,亮灯,下同
			while(!(GPIOC->IDR & (1 << 3)));
		}
	}	
//1上按下动作	
	if(!(GPIOC->IDR & (1 << 4)))
	{
		Delay_ms(1); //消抖
		if(!(GPIOC->IDR & (1 << 4)))
		{	
			   up1=1;
				lup1(1);
			while(!(GPIOC->IDR & (1 << 4)));
		}
	}
//2上按下动作	
	if(!(GPIOC->IDR & (1 << 5)))
	{
		Delay_ms(1); //消抖
		if(!(GPIOC->IDR & (1 << 5)))
		{
			up2=1;
			lup2(1);
			while(!(GPIOC->IDR & (1 << 5)));
		}
	}		
//3上按下动作	
	if(!(GPIOC->IDR & (1 << 6)))
	{
		Delay_ms(1); //消抖
		if(!(GPIOC->IDR & (1 << 6)))
		{
			up3=1;
			lup3(1);
			
			while(!(GPIOC->IDR & (1 << 6)));
		}
	}	
//2下按下动作	
	if(!(GPIOC->IDR & (1 << 7)))
	{
		Delay_ms(1); //消抖
		if(!(GPIOC->IDR & (1 << 7)))
		{
		     down2=1;
				ldown2(1);
			while(!(GPIOC->IDR & (1 << 7)));
		}
	}		
//3下按下动作	
	if(!(GPIOC->IDR & (1 << 8)))
	{
		Delay_ms(1); //消抖
		if(!(GPIOC->IDR & (1 << 8)))
		{
				
		     down3=1;
				ldown3(1);
			
			while(!(GPIOC->IDR & (1 << 8)));
		}
	}			
	
//4下按下动作	
	if(!(GPIOC->IDR & (1 << 9)))
	{
		Delay_ms(1); //消抖
		if(!(GPIOC->IDR & (1 << 9)))
		{
		     down4=1;
				ldown4(1);
			while(!(GPIOC->IDR & (1 << 9)));
		}
	}		
//4下按下动作	
	if(!(GPIOC->IDR & (1 << 10)))
	{
		Delay_ms(1); //消抖
		if(!(GPIOC->IDR & (1 << 10)))
		{	   lopen(1);
			while(!(GPIOC->IDR & (1 << 10)));
		}
	}		
//4下按下动作	
	if(!(GPIOC->IDR & (1 << 11)))
	{	Delay_ms(1); //消抖
		if(!(GPIOC->IDR & (1 << 11)))
		{
	   lopen(0);
			while(!(GPIOC->IDR & (1 << 11)));}}		}
//电梯执行程序	
void elvator(void)
{
switch(state){				//扫描电梯所在位置
				case(1):{
					if(state!=statepre){	//上次的状态和本次不一样说明电梯 需要开门
						lopen(1);
						run();			//按键扫描三秒钟
						lopen(0);
						in1=0;		//如果in1和up1被按下则无效
						up1=0;
						lup1(0);
					}
                    else{
                        if(up1){
                                up1=0;
                                lup1(0);
                                lopen(1);
                                run();
                                lopen(0);
                                }
                         }
                      in1=0;     
					if(up2|down2|up3|down3|down4|in2|in3|in4){
						flag=1;			//说明上行
						run();				//按键扫描三秒钟
						state=2;			//更新状态
						statepre=1;
					}
					else{
						statepre=state;
           flag=2;
						keyscan();
						display();
					}
					break;
				}
				case(2):{					  //电梯到2楼
					if(state!=statepre)
                    {                          //如果前后两次状态不一样则可能需要开门
                     	if(!(
							((flag==1)&&(in3|up3|down3)&&(~up2)&&(~in2))		   //去三楼
							||((flag==1)&&(in4|down4)&&(~up2)&&(~in2))		  
//去四楼
                            ||((flag==0)&&(in1||up1)&&(~down2)&&(~in2))		   
//去一楼
                            )
                          )
                           {			
								lopen(1);
								run();				//按键扫描三秒钟
								lopen(0);
								in2=0;				//如果in2被按下则无效
								if(flag==1){			//如果上行,电梯外上行指示灯按下无效
								up2=0;
								lup2(0);
								}
								else{				//如果下行,电梯外下行指示灯按下无效
								down2=0;
								ldown2(0);
								}
                         	}
					}
                                        else{									//在二楼停
                                        	if(down2|up2){
                                                	down2=0;
                                                        up2=0;
                                                        lopen(0);
                                                        run();
                                                        lopen(0);
                                                        ldown2(0);
                                                        lup2(0);
                                                	}
                                        	}
                                        in2=0;
                                       
                                        if(flag)
                                        	up2=0;
                                        else
                                        	down2=0;
					if(flag==1){
						if(down3|in3|up3|down4|in4){
							flag=1;
							run();
							state=3;
							statepre=2;
						}
						else if(in1|up1){
							flag=0;
							run();
							state=1;
							statepre=2;
						 }
						 else{
						 	statepre=state;
                                                        flag=2;
						 	keyscan();
						 	display();
						 }
					}
					else {
						if(up1|in1){
							flag=0;
							run();
							state=1;
							statepre=2;
						}
						else if(in3|down3|up3|in4|down4){
							flag=1;
							run();
							state=3;
							statepre=2;
						}
						else{
							statepre=state;
            flag=2;
							keyscan();
							display();
						}
					}
					break;
				}
				case(3):{				//电梯到3楼
	
					if(state!=statepre)
                    {                          //如果前后两次状态不一样则可能需要开门
                     	if(!(
							((flag==0)&&(in2|up2|down2)&&(~up3)&&(~in3))		   //去二楼
							||((flag==1)&&(in4|down4)&&(~up3)&&(~in3))		   //去四楼
                            ||((flag==0)&&(in1||up1)&&(~down3)&&(~in2))		   //去一楼
                            )
                          )
                           {			
								lopen(0);
								run();				//按键扫描三秒钟
								lopen(0);
								in3=0;				//如果in3被按下则无效
						
								if(flag==1){			//如果上行,电梯外上行指示灯按下无效
								up3=0;
								lup3(0);
								}
								else{				//如果下行,电梯外下行指示灯按下无效
								down3=0;
								ldown2(0);
								}
                         	}
					}
                                        else{									//在三楼停
                                        	if(down3|up3){
                                                	down3=0;
                                                        up3=0;
                                                        lopen(1);
                                                        run();
                                                        lopen(0);
                                                        ldown3(0);
                                                        lup3(0);
                                                	}
                                        	}
                                        in3=0;
                                       
                                        if(flag)
                                        	up3=0;
                                        else
                                        	down3=0;
					if(flag==1){
						if(down4|in4){
							flag=1;
							run();
							state=4;
							statepre=3;
						}
						else if(in1|up1|in2|up2|down2){
							flag=0;
							run();
							state=2;
							statepre=3;
						 }
						 else{
						 	statepre=state;
                                                        flag=2;
						 	keyscan();
						 	display();
						 }
					}
					else {
						if(up1|in1|up2|in2|down2){
							flag=0;
							run();
							state=2;
							statepre=3;
						}
						else if(in4|down4){
							flag=1;
							run();
							state=4;
							statepre=3;
						}
						else{
							statepre=state;
                                                        flag=2;
							keyscan();
							display();
						}
					}
					break;
				}
				case(4):{					//case4和case1类似
					keyscan();
					display();
					if(state!=statepre){
						lopen(1);
						run();
						lopen(0);
						in4=0;				//如果in4和down4被按下则无效
					
						down4=0;
						ldown4(0);
					}
           else{
       	if(down4){
										 down4=0;
										 ldown4(0);
										 lopen(1);
										 run();
										 lopen(0);
                }
              }
           in4=0;
                                      
					if(in1|up1|up2|down2|in2|in3|up3|down3){
						flag=0;				//说明下行
						run();
						state=3;			//更新状态
						statepre=4;
					}
					else{
						statepre=state;
              flag=2;
						keyscan();
						display();
					}
					break;
				}
				default: break;
			}
}
需要仿真图和keil源代码以及课设报告私聊我获取



















