基于单片机的电梯模拟控制系统 操作说明: 1.图中绿灯为电梯上行指示灯、黄灯为下行指示灯、红灯为报警指示灯。 2.电梯初始状态位于一楼,在一楼的用户可直接按下电梯内部按钮,按后动电梯开始运行。 3.在任一层楼的用户,可以通过电梯外部的按钮请求电梯,电梯到达后通过内部按钮确定目标层。 注意:启动按钮相当于关门,电梯内部的请求都要按启动后电梯才会运行。 程序有注释!!! 包含: 仿真、程序、原理图、参考报告【上万字,有好多篇,看图片】
先瞅硬件配置:三个LED灯对应运行状态,P1.0接绿灯(上行),P1.1黄灯(下行),P1.2红灯报警。4x4矩阵键盘接P2口,其中前两行是楼层外呼按钮,后两行是轿厢内选层。数码管用动态扫描方式显示当前楼层,接在P0和P3的部分引脚上。
主程序的状态机是关键。全局变量里藏着电梯的"小心思":
bit direction = 1; // 1上行,0下行 unsigned char current_floor = 1; // 当前楼层 unsigned char target_floors = 0; // 按位存储目标楼层 unsigned char ext_requests = 0; // 外部请求定时器0每20ms扫一次按钮,这个防抖处理挺实在:
void scan_buttons() interrupt 1 { static unsigned char debounce_cnt = 0; P2 = 0x0f; // 扫描前四行 if ((P2 & 0x0f) != 0x0f) { if (++debounce_cnt > 10) { handle_external_call(); // 处理外呼 debounce_cnt = 0; } } // 内选按钮扫描同理... }注意这里用debounce_cnt实现松手检测——按着不放?超过200ms才算有效触发,比普通延时防抖更靠谱。
电梯移动的核心逻辑在这段代码里:
void move_elevator() { if (target_floors == 0) return; // 判断运行方向 if (direction) { if (current_floor < 3) { run_up(); } else { direction = 0; // 到顶后自动调头 run_down(); } } else { if (current_floor > 1) { run_down(); } else { direction = 1; run_up(); } } // 到达目标楼层处理 if (target_floors & (1 << (current_floor-1))) { open_door(); target_floors &= ~(1 << (current_floor-1)); // 清除该位 } }这里的位操作target_floors变量很巧妙,用unsigned char的每一位表示对应楼层是否有请求,比用数组更节省内存。比如用户按了3楼,就把第2位(从0开始计)置1。
启动按钮的处理是重点,它相当于电梯的"执行键":
if (start_btn_pressed) { close_door(); // 模拟关门动作 start_moving = 1; // 解除电梯锁定 // 这里有个细节:关门过程中还能取消操作 }这里有个小trick:启动按钮按下后不是立即运行,而是先进入2秒的关门等待期,期间如果检测到障碍物(通过外接传感器),可以重新开门。虽然仿真里没体现,但代码里留了接口。
调试时发现个有趣的现象:电梯在响应外部请求时,会遵循"顺向截停"规则。比如电梯正从1楼升往3楼,这时2楼有人按了上行按钮,电梯就会在2楼停下。但如果是下行请求,则要等完成当前上行任务再响应。
这个项目最实用的经验是:用有限状态机(FSH)处理电梯状态切换特别合适。定义五个状态:空闲、门开、门关、上升、下降。状态转换时通过switch-case结构处理,代码比纯标志位方式清晰很多。
最后说下报警功能的实现逻辑:当电梯卡在两个楼层之间超过30秒,或者门状态传感器异常时,触发P1.2的红灯报警,同时蜂鸣器发出特定频率的响声。这部分用定时器1做基准计时,配合软件计数器实现。
整个工程看下来,电梯控制的核心还是对"请求优先级"和"运行方向"的把控。下次可以尝试加入负载检测功能,或者做并联电梯调度,那就更有挑战性了。