项目实战:单片机16层可电梯源码带串口通讯+Proteus仿真设计(实物已通过) C语言源代码及PROTEUS源代码

楼层数可选,硬件可删除。16层内任意设置最大楼层数,用不到的楼层硬件可以删除,不影响程序。提供串口通讯,波特率可在main初使化里设置。更新了详细注释版本,需要学习的朋友请关注。

电梯仿真原理图如下

在这里插入图片描述
C语言源代码截图

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
C语言参考源代码

/*Uart串口发送函数示例:
1.USART_SendByte(u8 dat) //发送单字节数据
例:USART_SendByte(0x04);

2.USART_Send(u8* arr,u16 len) //发送多字节数据
例:
Usart.TX_BUFF[0]=0xA1;
Usart.TX_BUFF[1]=0x08;
Usart.TX_BUFF[2]=0x04;
Usart.TX_BUFF[3]=0xF9;
USART_SendByte(Usart.TX_BUFF,4);

u8 Arr[16]={0xA1,0x08,0x04,0xF9};
USART_SendByte(Arr,4);

3.USART_SendStr(u8* str) //发送字符串数据
USART_SendStr(“大吉在利,晚上吃鸡!\r\n”);

4.有关电梯通信代码参考:
<1>发送电梯当前楼层给其他设备: USART_SendByte(ElevState.CurrentFloor);
<2>发送电梯当前运行方向给其他设备: USART_SendByte(ElevState.Direction);
<3>发送电梯当前所有数据给其他设备: USART_Send((u8*)(&ElevState),sizeof(ElevState));

*/

单片机源程序如下:
/电梯主程序****/

#include “reg51.h”
#include “intrins.h”

/程序选择*******/
#define USE_MODE 0 //0-定时模拟 1-传感器
/*********************************************************/
/平台移植部分代码
/
//定时器模拟版接口:
#if (USE_MODE==0)
sbit DOOR=P3^5; //电梯门开关控制信号
sbit ElevMotor_P=P3^6; //电梯电机正极控制信号
sbit ElevMotor_N=P3^7; //电梯电机负极控制信号
sbit SHCP=P0^5; //74HC595 LED、数码管显示驱动
sbit DS=P0^6; //74HC595 LED、数码管显示驱动
sbit STCP=P0^7; //74HC595 LED、数码管显示驱动
#define KeyPort_X P1 //矩阵按键接口
#define KeyPort_Y P2 //矩阵按键接口
#endif
//除以上定义的IO外,还需用到P33作为矩阵键盘的中断触发引脚
//8路与门可用芯片:CD4068或74LS30(芯片是与非门)+出端接一个三极管反向电平。

//传感器版接口:
#if (USE_MODE==1)
sbit DOOR_CLOSE_SENSOR=P3^4;//电梯门关闭检测传感器输入
sbit DOOR=P3^5; //电梯门开关控制信号
sbit ElevMotor_P=P3^6; //电梯电机正极控制信号
sbit ElevMotor_N=P3^7; //电梯电机负极控制信号
sbit SHCP=P0^5; //74HC595 LED、数码管显示驱动
sbit DS=P0^6; //74HC595 LED、数码管显示驱动
sbit STCP=P0^7; //74HC595 LED、数码管显示驱动
#define KeyPort_X P1 //矩阵按键接口
#define KeyPort_Y P2 //矩阵按键接口
#endif
//除以上定义的IO外,还需用到P33作为矩阵键盘的中断触发引脚
//需用到P32作为楼层限位开关触发中断引脚
//每个楼层一个限位开关,接在N路输入的与门芯片上
//如果大于8层,可用两个8路输入的与门芯片+一个2路的与门芯片组合
//8路与门:用8路与非门CD4068或74LS30+输出端接一个三极管反向电平。
/****************************************************************/
/楼梯参数设置******/
#define MaxFloor 16 //电梯总楼层(最大16)

#define FloorStayTime 40 //单位:50ms, 楼层停留时间:4050ms=2S
#define DoorStayTime 2 //单位:S , 电梯门停留时间
#define SEG_Num 2 //数码管数量(为1时请删除第一个数码管的595)
/
***************************************************************/
#define u8 unsigned char
#define u16 unsigned int

#define TRUE 1
#define FALSE 0

#define Null 0
#define STOP 1
#define UP 2
#define DOWN 3

#define UP_IN 0x01
#define UP_OUT 0x02
#define DOWN_IN 0x04
#define DOWN_OUT 0x08

#define Elev_UP ElevMotor_P=0;ElevMotor_N=1 //低电平有效
#define Elev_DOWN ElevMotor_P=1;ElevMotor_N=0 //低电平有效
#define Elev_STOP ElevMotor_P=1;ElevMotor_N=1 //低电平有效

#define OPEN 1
#define CLOSE 0
#define DOOR_Open DOOR=OPEN
#define DOOR_Close DOOR=CLOSE

#define SHCP_H SHCP=1
#define SHCP_L SHCP=0
#define DS_H DS=1
#define DS_L DS=0
#define STCP_H STCP=1
#define STCP_L STCP=0

#if MaxFloor>8
#define MHC595_NUM 8
#else
#define MHC595_NUM 5
#endif

#define SEGPort 0x01
#define LED_IN_Port 0x02
#define LED_OUT_UP_Port 0x03
#define LED_OUT_DOWN_Port 0x04

typedef struct
{
u8 ElevRunning; //电梯运行状态标志
u8 CurrentFloor; //当前电梯所处楼层
u8 Direction; //电梯运行方向
u8 FloorCallTable[MaxFloor+1];//数组[0]不用,从[1]开始使用 ,呼叫列表(所有楼层呼叫的信息均保存于此)
u8 FloorTimerCount; //计时
u8 ArriveFlag; //到达楼层信号(用于停止或在前一楼层提前加减速)(定时器模拟的用不到)
u8 FreeFlag; //电梯空闲状态标志
}TYPEDEF_ELEVSTATE;
TYPEDEF_ELEVSTATE ElevState; //电梯状态结构体

//u8 code SEG_NUM[]={0x3f,0x06,0x5b,0x4f,0x66,0x6D,0x7D,0x07,0x7f,0x6f}; //共阴SEG数码管段码 0~9
u8 code SEG_NUM[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e}; //共阳SEG数码管段码 0~F
u16 code LED_NUM[]={0x00,0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80}; //SEG数码管段码 0~9

u8 HC595_BUFF[MHC595_NUM]={0};
u8 Timer_Second;
u16 TimerCount;

u8 RIOver_Flag=0;
u8 ExternKey[2]={0}; //外部按键按下信息
u8 FloorCallTableBUFF[MaxFloor+10]={0};
u8 KeyPressFlag=0;

#define USART_RX_LEN 20 //接收缓存长度(请勿超过芯片rom剩余大小)
typedef struct
{
u8 RX_BUFF[USART_RX_LEN]; //接收缓存
u8 TX_BUFF[USART_RX_LEN]; //发送缓存
u16 RX_COUNT;
}USART;
USART Usart;

void delay_ms(u16 xms);
void delay_s(u8 xs);
void EXTI0_Init(void);
void Timer0_Init(void);
void USART_Configure(u16 BaudRate);
void HC595_Send(u8 p);
u8 KeyScan(void);
void KeyDatHandle(u8 key);
void WaitDoorOpen(u8 ts);
void WaitDoorClose(u8 ts);
void SEG_Display(u8 segnum);
void LED_ON(u8 addr,u8 lednum);
void LED_OFF(u8 addr,u8 lednum);
void DlevWorkState(u8 state);
void Elevator(void);
u8 FloorCall_UP(u8 floorside);
u8 FloorCall_DOWN(u8 floorside);
u8 DelNullCheck(void);
void FloorCallCheck(void);
void USART_SendByte(u8 dat);
void USART_Send(u8
arr,u16 len);
void USART_SendStr(u8* str);
void SYSYEM_INIT(void);

void delay_ms(u16 xms)
{
u8 i;
u16 t;
for(t=xms;t>0;t–)
for(i=112;i>0;i–);
}

void delay_s(u8 xs)
{
TimerCount=0;
Timer_Second=0;
while(Timer_Second<xs);
}

void EXTI0_Init(void)
{
EA = 1;
IT0= 1; //下降沿触发
EX0= 1;
}

void EXTI1_Init(void)
{
EA = 1;
IT1= 1; //下降沿触发
EX1= 1;
}

else
if(((key>0)&&(key<=8))||((key>32)&&(key<=40))) //楼梯内的楼层选择按键(使用第1行和第5行作为电梯内部16个按键,每行8个键)
{
if(key>32) tempkey=key-32+8;
else tempkey=key;
if(tempkey<=MaxFloor)
{
LED_ON(LED_IN_Port,tempkey);
if(((tempkey>ElevState.CurrentFloor)&&(tempkey<MaxFloor))||(tempkey1)) ElevState.FloorCallTable[tempkey]|=UP_IN;//内部按1只能往上
else
if((tempkey<ElevState.CurrentFloor)||(tempkey
MaxFloor)) ElevState.FloorCallTable[tempkey]|=DOWN_IN;
}
}
else
if(((key>9)&&(key<=16))||((key>40)&&(key<=48))) //每个楼层门口外的向下按键(底楼按向下无效),第2行和第6行
{
if(key>40) tempkey=key-40+8;
else tempkey=key-8;
if((tempkey>1)&&(tempkey<=MaxFloor))
{
LED_ON(LED_OUT_DOWN_Port,tempkey);
ElevState.FloorCallTable[tempkey]|=DOWN_OUT;
}
}
else
if(((key>16)&&(key<=24))||((key>48)&&(key<56))) //每个楼层门口外的向上按键(顶楼按向上无效),第2行和第7行
{
if(key>48) tempkey=key-48+8;
else tempkey=key-16;
if(tempkey<MaxFloor)
{
LED_ON(LED_OUT_UP_Port,tempkey);
ElevState.FloorCallTable[tempkey]|=UP_OUT;
}
}
else
if(key25) //开门
{
if(ElevState.ElevRunning
FALSE)
{
if(DOORCLOSE) DOOR_Open;
}
}
else
if(key
26) //关门
{
if(DOOR==OPEN)
{
DOOR_Close;
ElevState.FloorTimerCount=0; //计时器清0
}
}
}

void WaitDoorOpen(u8 ts)
{
DOOR_Open;
delay_s(ts);
}

void WaitDoorClose(u8 ts)
{
DOOR_Close;
#if (USE_MODE0)
delay_s(ts);
#endif
#if (USE_MODE
1)
ts=ts; //防止警告
while(DOOR_CLOSE_SENSOR==OPEN);
#endif

}

void SEG_Display(u8 segnum)
{
HC595_BUFF[0]=SEG_NUM[segnum/10];
HC595_BUFF[1]=SEG_NUM[segnum%10];
HC595_Send(HC595_BUFF);
}
void LED_ON(u8 addr,u8 lednum)
{
switch(addr)
{
case LED_IN_Port: if(lednum<9) HC595_BUFF[2]|=LED_NUM[lednum];
else HC595_BUFF[5]|=LED_NUM[lednum-8]; break;
case LED_OUT_DOWN_Port: if(lednum<9) HC595_BUFF[3]|=LED_NUM[lednum];
else HC595_BUFF[6]|=LED_NUM[lednum-8]; break;
case LED_OUT_UP_Port: if(lednum<9) HC595_BUFF[4]|=LED_NUM[lednum];
else HC595_BUFF[7]|=LED_NUM[lednum-8]; break;
}
HC595_Send(HC595_BUFF);
}
void LED_OFF(u8 addr,u8 lednum)
{
switch(addr)
{
case LED_IN_Port: if(lednum<9) HC595_BUFF[2]&=~LED_NUM[lednum];
else HC595_BUFF[5]&=~LED_NUM[lednum-8]; break;
case LED_OUT_DOWN_Port: if(lednum<9) HC595_BUFF[3]&=~LED_NUM[lednum];
else HC595_BUFF[6]&=~LED_NUM[lednum-8]; break;
case LED_OUT_UP_Port: if(lednum<9) HC595_BUFF[4]&=~LED_NUM[lednum];
else HC595_BUFF[7]&=~LED_NUM[lednum-8]; break;
}
HC595_Send(HC595_BUFF);
}

void ElevWorkState(u8 state)
{
if(stateUP)
{
Elev_UP;
ElevState.ElevRunning=TRUE;
}
else
if(state
DOWN)
{
Elev_DOWN;
ElevState.ElevRunning=TRUE;
}
else
if(state==STOP)
{
Elev_STOP;
ElevState.ElevRunning=FALSE;
}
SEG_Display(ElevState.CurrentFloor); //数码管显示
}

void TurnHead_Check(void)
{
if(ElevState.CurrentFloor1)
{
ElevState.Direction=UP; //1楼调头
}
else if(ElevState.CurrentFloor
MaxFloor)
{
ElevState.Direction=DOWN; //顶楼调头
}
}
void Elevator(void)
{
if((DOOROPEN)||(ElevState.FreeFlagTRUE)) ElevWorkState(STOP);
else ElevWorkState(ElevState.Direction);
TurnHead_Check();
if(ElevState.Direction==UP) //电梯正在向上运行的情况
{
if(ElevState.CurrentFloor<=MaxFloor) //扫描呼叫列表中的向上方向的呼叫楼层
{
if(((ElevState.FloorCallTable[ElevState.CurrentFloor]&UP_IN)==UP_IN)||((ElevState.FloorCallTable[ElevState.CurrentFloor]&UP_OUT)UP_OUT)) //到达目标楼层,停下开门5秒
{
LED_OFF(LED_IN_Port,ElevState.CurrentFloor);
LED_OFF(LED_OUT_UP_Port,ElevState.CurrentFloor);
ElevState.FloorCallTable[ElevState.CurrentFloor]&=~UP_IN; //到达相应的楼层从向上列表中清除
ElevState.FloorCallTable[ElevState.CurrentFloor]&=~UP_OUT; //到达相应的楼层从向上列表中清除
ElevState.FreeFlag=TRUE; //到达楼层,暂时进入空闲状态
ElevWorkState(STOP); //修改电梯状态为停止态
WaitDoorOpen(DoorStayTime); //延时等待电梯门打开
delay_s(2*DoorStayTime); //适当延时
WaitDoorClose(DoorStayTime); //等待门关好
ElevWorkState(ElevState.Direction);
ElevState.FreeFlag=FALSE; //离开楼层,退出空闲状态
ElevState.FloorTimerCount=0; //计时器清0
}
}
}
else
if(ElevState.Direction
DOWN) //电梯正在向下运行的情况
{
if(ElevState.CurrentFloor>=1) //扫描呼叫列表中的向下方向的呼叫楼层
{
if(((ElevState.FloorCallTable[ElevState.CurrentFloor]&DOWN_IN)==DOWN_IN)||((ElevState.FloorCallTable[ElevState.CurrentFloor]&DOWN_OUT)==DOWN_OUT)) //到达楼层,停下开门5秒
{
LED_OFF(LED_IN_Port,ElevState.CurrentFloor);
LED_OFF(LED_OUT_DOWN_Port,ElevState.CurrentFloor);
ElevState.FloorCallTable[ElevState.CurrentFloor]&=~DOWN_IN; //到达相应的楼层从向下列表中清除
ElevState.FloorCallTable[ElevState.CurrentFloor]&=~DOWN_OUT; //到达相应的楼层从向下列表中清除
ElevState.FreeFlag=TRUE; //到达楼层,暂时进入空闲状态
ElevWorkState(STOP); //修改电梯状态为停止态
WaitDoorOpen(DoorStayTime); //延时等待电梯门打开
delay_s(2*DoorStayTime); //保持门开2秒让人进出
WaitDoorClose(DoorStayTime); //适当延时
ElevWorkState(ElevState.Direction); //等待门关好
ElevState.FreeFlag=FALSE; //离开楼层,退出空闲状态
ElevState.FloorTimerCount=0; //计时器清0
}
}
}

}

u8 FloorCall_UP(u8 floorside) // floorside = DOWN在楼下 UP在楼上
{
u8 i;

if(floorside==UP)
{
for(i=ElevState.CurrentFloor+1;i<=MaxFloor;i++) //扫描呼叫列表楼上是否有要向上的呼叫
{
if(((ElevState.FloorCallTable[i]&UP_IN)==UP_IN)||((ElevState.FloorCallTable[i]&UP_OUT)UP_OUT)) //如果扫描到上方楼层有向上的呼叫
{
return TRUE;
}
}
return FALSE;
}
else
if(floorside
DOWN)
{
for(i=ElevState.CurrentFloor-1;i>=1;i–) //扫描呼叫列表楼下是否有要向上的呼叫
{
if(((ElevState.FloorCallTable[i]&UP_IN)==UP_IN)||((ElevState.FloorCallTable[i]&UP_OUT)==UP_OUT)) //如果扫描到上方楼层有向上的呼叫
{
return TRUE;
}
}
return FALSE;
}
else return FALSE;
}

u8 FloorCall_DOWN(u8 floorside) // floorside = DOWN在楼下 UP在楼上
{
u8 i;

if(floorside==UP)
{
for(i=ElevState.CurrentFloor+1;i<=MaxFloor;i++) //扫描呼叫列表楼上是否有要向下的呼叫
{
if(((ElevState.FloorCallTable[i]&DOWN_IN)==DOWN_IN)||((ElevState.FloorCallTable[i]&DOWN_OUT)DOWN_OUT)) //如果扫描到上方楼层有向下的呼叫
{
return TRUE;
}
}
return FALSE;
}
else
if(floorside
DOWN)
{
for(i=ElevState.CurrentFloor-1;i>=1;i–) //扫描呼叫列表楼下是否有要向下的呼叫
{
if(((ElevState.FloorCallTable[i]&DOWN_IN)==DOWN_IN)||((ElevState.FloorCallTable[i]&DOWN_OUT)==DOWN_OUT)) //如果扫描到上方楼层有向下的呼叫
{
return TRUE;
}
}
return FALSE;
}
else return FALSE;
}

u8 DelNullCheck(void) //扫描呼叫列表是否空闲
{
u8 i;
u8 result;

for(i=1;i<=MaxFloor;i++)
{
if(ElevState.FloorCallTable[i]!=Null)
{
result=FALSE; //非空闲
break;
}
else if(i==MaxFloor) result=TRUE; //空闲
}
return result;
}

void FloorCallCheck(void)
{
if(DelNullCheck()FALSE) //非空闲
{
ElevState.FreeFlag=FALSE; //退出空闲状态
ElevState.ElevRunning=TRUE;//正在运行标志打开
}
else ElevState.FreeFlag=TRUE; //进入空闲状态
TurnHead_Check();
if((ElevState.FreeFlag
FALSE)&&(DOORCLOSE)) //非空闲下
{
if(ElevState.Direction
UP)//方向向上
{
if((FloorCall_UP(UP)TRUE)||(FloorCall_DOWN(UP)TRUE)) //如果当前楼层上方有呼叫向上的 或 如果当前楼层上方有呼叫向下的
{
#if (USE_MODE
0)
if(ElevState.FloorTimerCount>FloorStayTime) //检测是否到达楼层 如果接收到 到达楼层的限位开关信号,则当前楼层值+1(这里用定时器来模拟)
{
ElevState.FloorTimerCount=0; //计时器清0
ElevState.CurrentFloor++;//电梯继续往上走
}
#endif
#if (USE_MODE
1)
if(ElevState.ArriveFlagTRUE) //检测是否到达楼层 如果接收到 到达楼层的限位开关信号,则当前楼层值+1
{
ElevState.ArriveFlag=FALSE; //清除标志
ElevState.FloorTimerCount=0; //计时器清0
ElevState.CurrentFloor++;//电梯继续往上走
}
#endif
}
else ElevState.Direction=DOWN; //调头
}
else
if(ElevState.Direction
DOWN)//方向向下
{
if((FloorCall_DOWN(DOWN)TRUE)||(FloorCall_UP(DOWN)TRUE)) //如果当前楼层下方有呼叫向下的 或 如果当前楼层下方有呼叫向上的
{
#if (USE_MODE
0)
if(ElevState.FloorTimerCount>FloorStayTime) //检测是否到达楼层 如果接收到 到达楼层的限位开关信号,则当前楼层值+1(这里用定时器来模拟)
{
ElevState.FloorTimerCount=0;
ElevState.CurrentFloor–;//电梯继续往下走
}
#endif
#if (USE_MODE
1)
if(ElevState.ArriveFlag==TRUE) //检测是否到达楼层 如果接收到 到达楼层的限位开关信号,则当前楼层值+1
{
ElevState.ArriveFlag=FALSE; //清除标志
ElevState.FloorTimerCount=0;
ElevState.CurrentFloor–;//电梯继续往下走
}
#endif
}
else ElevState.Direction=UP; //调头
}
}
else
{
ElevWorkState(STOP); //停止运行
ElevState.ElevRunning=FALSE;//正在运行标志关闭
}
}

void USART_SendByte(u8 dat)
{
SBUF=dat;
while(!TI); //等待发送数据完成
TI=0; //清除发送完成标志位
}
void USART_Send(u8* arr,u16 len)
{
u16 i;
for(i=0;i<len;i++)
{
SBUF=arr[i];
while(!TI); //等待发送数据完成
TI=0; //清除发送完成标志位
}
}
void USART_SendStr(u8* str)
{
while(1)
{
if(*str==’\0’) break;
SBUF=*str++;
while(!TI); //等待发送数据完成
TI=0; //清除发送完成标志位
}
}

void SYSYEM_INIT(void)
{
u8 i;
ElevState.CurrentFloor=1;
ElevState.Direction=UP; //初使化方向为向上
ElevState.FloorTimerCount=0;
ElevState.FreeFlag=TRUE; //初使化为空闲状态
ElevState.ElevRunning=FALSE;
RIOver_Flag=FALSE;
Usart.RX_COUNT=0;
for(i=0;i<=MaxFloor;i++)
{
ElevState.FloorCallTable[i]=Null; //初使化呼叫列表为Null

     ……………………

void EXTI0_IRQHandler(void) interrupt 0
{
#if (USE_MODE==1)
ElevState.ArriveFlag=TRUE;
#endif
}
void EXTI1_IRQHandler(void) interrupt 2 //矩阵按键中断
{
KeyDatHandle(KeyScan());
}
void TIM0_IRQHandler(void) interrupt 1 //定时模拟电梯上升和下降
{
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;

TimerCount++;
if(TimerCount==20) //1秒
{
TimerCount=0;
Timer_Second++;
}
if(DelNullCheck()==FALSE) ElevState.FloorTimerCount++; //非空闲状态计时累加
else ElevState.FloorTimerCount=0; //空闲状态计时清0
}

void USART_IRQHandler(void) interrupt 4 //用于电梯之间的通信
{
if(RI==1) //判断是否为接收中断(串口中断分为发送中断和接收中断,均用同一个中断服务函数入口)
{
……………………
鉴于篇幅原因,只能写部分参考代码。

最后,如果有什么意见或者建议欢迎您留言给我,让我们共同学习一起进步,
如果需要 完整代码或设计文件,请在下方留言或者私信我,看到后会第一时间回复。

谢谢!

发布了74 篇原创文章 · 获赞 159 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/weixin_44212493/article/details/104334379