单片机多电梯控制系统
写在前面:
前段时间单片机的课设题目,难度为5,算是选题1~5最难的一类。之前也没怎么学过,这次算是从头开始学习51单片机。此篇所写代码是基于我自己理解能让小白容易弄懂的代码,其中难免出现一些纰漏,所写不足之处,望指正(下载地址在我的公众号 For My Future对应文章文末)。
这篇文章是是从我公众号复制的,没有排版,想看美观排版及下载源代码,请关注我的公众号,找到对应的历史文章
一.应用场景
二.主要模块介绍
三.详细编写步骤
一.应用场景
先放图,心里有个大概认识(步进电机没拍到,后面有详细说明图)
模拟电梯系统,4层,两部电梯,用步进电机顺逆时针模拟电梯上下行,矩阵键盘模拟电梯内外层呼叫,数码管显示楼层数,蜂鸣器模拟故障警报,并且有多电梯中最近电梯响应的逻辑,板子是福讯的stc89c52。
最近电梯响应解释:我们正常坐电梯,一般一栋楼有两座电梯,我住在三楼,我按下呼叫按钮,这时A电梯在1层,B电梯在18层,假设都是空闲状态,那么A电梯离我最近,A电梯会下来接我。
二.主要模块介绍
1.电梯步进电机模块:
(1)步进电机正反转代表电梯上下行
(2)电梯每变化一层,步进电机转半圈
2.数码管显示模块
(1)选用两个数码管分别显示一号电梯,二号电梯的楼层数
(2)动态实时显示楼层数,楼层变化数码管数字变化
3.警报器模块
(1)当电梯发生故障时,按下按钮,蜂鸣器发出警报
4.电梯响应呼叫模块(难点)
(1)矩阵键盘模拟一,二号电梯内外呼叫
(2)一号电梯内部四个键盘代表四层,二号电梯同理,外呼叫四个按钮代表电梯外每层的呼叫按钮
(3)即时呼叫,按钮按下电梯工作
(4)判断最近电梯逻辑离呼叫层数最近的电梯响应
矩阵键盘按下所实现的任务:
4×4的键盘,编号为1~16。
14即第一行按键代表电梯14外层呼叫,通俗来讲就是我按下按键电梯要来接我。
5~8即第二行按键代表A电梯内部层数按钮,我进入A电梯内部,会有4个按钮,我想上4楼,就按下对应按钮即单片机键盘的8号按键,想去2楼,就按下单片机键盘的6号按键。
9~12即第三行按键代表B电梯内部层数按钮同上。
16号是报警按键,按下蜂鸣器就会响。
三.详细编写步骤
1.编写电梯步进电机模块:
步进电机无非是控制正转倒转以及控制圈数,
步进电机的方向是用脉冲来控制的
图示为三种方法,这里采用第一种写法,正转代码:
void cw(){
uchar o;
for(o=0;o<250;o++){
a=0;b=0;c=0;d=1;
delaydj(500); //改变这个参数可以调整电机转速 ,数字越小,转速越大
a=0;b=0;c=1;d=0;
delaydj(500);
a=0;b=1;c=0;d=0;
delaydj(500);
a=1;b=0;c=0;d=0;
delaydj(500);
}
}
倒转反着写就可以了。
编写此模块出现的问题:
在我测试的时候还发现了一个问题,就是要多加一个电压源,否则步进电机无法转动 ,这是个小问题,但当时我对电机原理不了解,耗了很长时间才发现原因所在。
2.数码管显示模块
首先要明确单片机是单线程的,要实现两位数字的显示,则切换的时间要短,达到视觉暂留的效应,这样看起来才是同时显示的。
首先我们来写一号电梯的显示函数
void display1(){
uchar i;
for(i=0;i<=100;i++){
P0=0X00;//关掉数码管的位选信号。阻止数码管受到P0口信号的影响。
delay(1);
wela=1;
delay(1);
wela=0;
dula=0;
P0 = table[floor2-1]; /显示数字 i/
dula=1;
dula=0;
wela=0;
P0=0x10; //数码管位选
wela=1;
wela=0;
delay(10);
P0=0X00;//关掉数码管的位选信号。阻止数码管受到P0口信号的影响。
delay(1);
wela=1;
delay(1);
wela=0;
dula=0;
P0 = table[floor1-1]; /显示数字 i/
dula=1;
dula=0;
wela=0;
P0=0x01; //数码管位选
wela=1;
wela=0;
delay(10);}}
上面的代码意思就是选数码管的一位,然后把数组table[]的值显示出来,table里面放的就是1~4这四个数字,代码写的是table[floor1-1],这个floor1就是我所设的全局变量,用来实时记录一号电梯当前的楼层数,比如我在三楼呼叫,此时一号电梯在一层,floor1=1,接着它会上来,从1变成2再到3,同理floor2表示的就是二号电梯的实时楼层数。接着数码管再选第二位,显示另一部电梯的楼层数,这是一次显示,要想长时间的显示,外面再套个for循环100次即可。
这里我写了两个display函数display1(),display2(),就是显示的顺序不一样,display1()是先显示第二位后显示第一位,那么循环完最后锁存的是第一位,也就是说display1()执行完后,再执行其它任务时数码管第一位是亮的,第二位是熄灭的,而
display2()执行完第一位是熄灭的,第二位是亮的。(不重要,不理解也没事,实操就明白了)
3.警报器模块
警报器编写较为简单
void fengming(){
feng=0;
delayms(1000);
feng=1;
delayms(1000);
}
4.电梯响应呼叫模块
在这之前,还要编写键盘扫描模块,去识别到
底是哪个按键按下
uchar KeyScan()
{ uchar temp; //用于存储 P3 口的状态
uchar key_value = 0; //键值的定义
P3 = 0x0f; //设置P3高四位为0,第四位为1
temp = P3; //用一个变量存储P3口的状态,以免外界影响P3口,从而造成扫描有误
if(temp != 0x0f)
{
delayms(10);
if(temp!= 0x0f) //确定有键按下
{
switch(temp) //查看P3(temp)的状态
{
case 0x0e :key_value = 1; break; //按下的为第一行
case 0x0d :key_value = 5; break;
case 0x0b :key_value = 9; break;
case 0x07 :key_value = 13;break; //按下的为第四行
}
}
P3 = 0xf0; //再将 P3 口重新赋值,高四位为1,第四位为0
temp = P3;
if(temp != 0xf0)
{
switch(temp)
{
case 0xe0 : key_value +=3 ; break; //按下的是第四列,键值key_value加3
case 0xd0 : key_value +=2 ; break;
case 0xb0 : key_value +=1 ; break;
case 0x70 : key_value +=0 ; break; //按下的是第一列,键值key_value加0
}
}
while(P3 != 0xf0); //松手检测,因为最近的一次赋值是P3 = 0xf0
}
return(key_value);
}
KeyScan()函数返回的就是按键按下的编号,5号按键按下就会返回5。
我把电梯的响应呼叫的编写分为两部分
(1)电梯外部按钮响应事件
(2)电梯内部电梯响应事件
(1)电梯外部按钮响应
即编写对应按键1~4按下所触发的事件,这里编写dotask()函数
void dotask(){
if((floor1-floor0)(floor1-floor0)<=(floor2-floor0)(floor2-floor0)){ flag=0;
if(floor0>=floor1) up1(floor0-floor1); else if(floor0
else if((floor1-floor0)(floor1-floor0)>(floor2-floor0)(floor2-floor0)){ flag=1;
if(floor0>=floor2) up2(floor0-floor2) ; else if(floor0
这里面有一个全局变量floor0,记录的是电梯将要执行的任务,也就是说我在四楼呼叫,floor0就被赋值为4。dotask()函数先判断哪个电梯最近,然后再去选择是上升还是下降,一号电梯对应up1(),down1()
void up1(uchar m){
state1=0;
flag=0;
display1();
MotorCW(m);
display1();
state1=1;
}
参数m是上升还是下降m层的意思,up1(2)就表示一号电梯上升两层,state1代表一号电梯的状态,0是忙碌,1是空闲。flag=0表示现在是一号电梯执行任务,flag=1就表示二号电梯执行任务,MotorCW(m)表示正转m个半圈。
二号电梯up2()及down2()同理。
编写当中出现的问题:
一开始用的是 if else语句,会出现错误,换成 if elseif变得正常,现在想想可能是有一些情况没有考虑到,毕竟这个逻辑还是挺多的。
(2)电梯内部按钮响应
即编写一号电梯对应58,二号电梯对应912按键所触发事件。
这里用switch case来编写,举两个例子:
case 8: floor0=4; if(floor0>=floor1) up1(floor0-floor1) ;else down1(floor1-floor0);break;
case 12: floor0=4; if(floor0>=floor2) up2(floor0-floor2) ;else down2(floor2-floor0);break;
应该很容易理解,按下8号按键,即一号电梯内部4楼按钮,此时要执行的任务是一号电梯到4楼,floor0=4,然后根据当前电梯所在位置判断是上升还是下降。其它按键事件同理。
最后外部套个while(true)循环检测扫描按键是否按下就可以了,这样一个简易的多电梯控制系统就完成了。
(我的单片机还回去了,忘录视频了,不好意思)