|||
排队呼叫系统
/***********************************系统说明********************************************/
系统组成:
(银行门口)一台取号机,取号机上有取号按键,SEG显示模块(1位),队满指示灯。
(银行里面)一台服务机,呼叫按键(表示服务窗口,2位,可以更多),队非空指示灯。
系统工作过程:
客户在门口取号,如果队未满则可得到一个号码,号码显示5s。 号码取值1~6。
大厅内有一台服务机,服务机连接每个窗口的呼叫按键和队非空指示灯(共用)。当指示灯亮时表示有客户需要服务,win窗口的服务员看到指示灯后按呼叫按键,广播喊“请 num 号客户到 win 窗口接受服务”。如果队列空,队非空指示灯灭,服务员按键无响应。
为避免连续取号,取号机5s内最多只响应一次,即得到号码后显示5s,然后才能再次取号。
为避免连续呼叫,同一个呼叫键20s内最多只呼叫一位客户,重复按键重复呼叫,自第一次呼叫20s后 可呼叫下一位客户。
程序实现:
按键处理,呼叫键和取号键被检测到按键操作即得到响应,采用延时再响应而不是等待释放方式 准备下次响应。
服务机和取号机的通信,UART通信,有线,无通信错误校验。
系统框图(//此博客不能直接上传图片)
取号机程序 (取号机.c)
服务机程序 (服务机.c)
/***********************************取号机.c********************************************/
/*
取号机
服务机复位后 向取号机发复位信号,取号机清队满标志、灭队满指示灯。
对于用户按键,如果队列未满,则响应用户按键,向服务机请求号码,接收并显示(打印)号码。
服务机接收到取号机请求后,向取号机发送一个号码,并把号码加入队列,如果队列满 则向取号机发送队满信号。
取号机接收到服务机 队满信号后,置位队满标志,亮队满指示灯。
PZ
2009年4月2日
*/
#include <reg51.h>
#define uchar unsigned char
#define uint unsigned int
// 通信用命令字
#define WINKLEDFULL 0xff /*灭 队满指示灯*/
#define LIGHTENLEDFULL 0xfe /*亮 队满指示灯*/
#define MASTERRESET 0xfd /*服务机复位了,取号机也要复位*/
#define SLAVERRESET 0xfc /*取号机复位了,询问队列状态*/
#define REQUESTCODE 0x00 /*服号机号码请求*/
// 硬件接口定义
sbit LEDFull=P2^0; // 队满指示 高电平驱动
sfr PortSEGd=0x80; // SEG段码接口P0
sbit PinSEGw=P2^3; // SEG位选
sbit KEY=P1^0; // 号码请求
// 变量定义
//uchar dataTx[8], dataRx[8]; // UART通信用 发送数组
bit qFull=0;
uchar cntT0=0; // 中断计数, T0中断周期50ms, 中断20次即1s
uchar wait=0; // 每得到一个号码,显示5s。之后才能响应下一次取号。
uchar gCode; // 接收到的号码
bit getFlag=0;
// 函数定义
void delay10ms();
void InitCPU(); // T0中断周期50ms; 设置T1方式2用于串行通信, 波特率9600bps
void InitSys(); // 初始化系统:询问服务机队列状态
void dispSEG(uchar num); // 显示号码5s函数
void IntT0(); // T0中断计时
void IntUART(); // 串口中断
/************************************************************************************/
void main()
{
InitCPU();
InitSys();
/*// test dispSEG();
// uchar i;
for(i=0; i<10; i++)
{
dispSEG(i);
} */
while(1)
{
if(!qFull)
{
if(KEY==0)
{
delay10ms();
if(KEY==0)
{
SBUF=REQUESTCODE;
while(TI==0)
;
TI=0;
while(getFlag==0)
;
getFlag=0;
dispSEG(gCode); // 显示接收的号码
}
}
}
}
}
/************************************************************************************/
void delay10ms()
{
uchar i=20;
while(i--)
;
}
void InitCPU() // T0中断周期10ms; 设置T1方式2用于串行通信, 波特率9600bps
{
TMOD=0x21; // T1方式2, T0方式1
TH1=0xfe;;// 波特率9600
TL1=0xfe;
TH0=0x3c;;
TL0=0xb0;
//IE=0x92;
//TCON=0x50;
//SCON=0x50;
}
void InitSys() // 初始化系统:通知服务机 取号机复位了
{
IE=0x92;
TCON=0x50;
SCON=0x50;
SBUF=SLAVERRESET;
while(TI==0)
;
TI=0;
}
void dispSEG(uchar num) // 显示号码5s函数
{
uchar code DISPTAB[]={0x77, 0x41,0xB3,0xE3, 0xC5,0xE6,0xF6,0x61,0xF7,0xE7};//共阴数码管显示段码 gcdehfab
wait=0;
PortSEGd=DISPTAB[num];
PinSEGw=0;
while(wait<3) // 最多显示5s, 如果要显示更长时间。 先改计时函数里的最大计时值。
;
PinSEGw=1;
}
void IntT0() interrupt 1 using 1 // T0中断计时
{
TH0=0x3c; // 50ms
TL0=0xb0;
if(++cntT0==20)
{
cntT0=0;
if(wait<5)
wait++;
}
}
void IntUART() interrupt 4 using 2 // 串口中断
{
uchar dat;
if(RI)
{
RI=0;
dat=SBUF;
switch(dat)
{
case MASTERRESET:
// break;
case WINKLEDFULL:
qFull=0;
LEDFull=0;
break;
case LIGHTENLEDFULL:
qFull=1;
LEDFull=1;
break;
default:
if(dat>0 && dat<7)
{
gCode=dat;
getFlag=1;
}
break;
}
}
}
/***********************************取号机.c********************************************/
/*
//服务机程序
// ~~具体说明 参系统说明~ 还没写成论文 都已经讲了好多遍了:)——系统说明,程序说明,程序注释 里
PZ
2009年4月2日
*/
#include <reg51.h>
#include "ISD2560.c"
#define uchar unsigned char
#define uint unsigned int
// 通信用命令字
#define WINKLEDFULL 0xff /*灭 队满指示灯*/
#define LIGHTENLEDFULL 0xfe /*亮 队满指示灯*/
#define MASTERRESET 0xfd /*服务机复位了,取号机也要复位*/
#define SLAVERRESET 0xfc /*取号机复位了,询问队列状态*/
#define REQUESTCODE 0x00 /*服号机号码请求*/
// 硬件接口定义
sbit Win1=P1^0; // 两个窗口的服务请求键
sbit Win2=P1^1;
sbit LEDNotEmpty=P2^4; // 队空指示
// 语音模块硬件接口定义 在ISD2560.c
// 变量定义
//dataTx[8]; // UART通信用 发送数组(简单通信,无错误检验)
uchar queueCode[4], gCode=1; // 队列长度, 号码1~6, 号码数量超过队列长度
uchar qFront, qRear; // 队列指针
bit qFull=0, qEmpty=1; // 每次有入队操作则检查是否qFront==qRear,是则表示队满。 类似地,出队操作时 这表示队空。
uchar winObj[2]; // 窗口1、2当前服务号
uchar winCnt[2]; // 窗口1、2服务计时。未走过20s时,按服务请求键会重复呼叫。 超过20s停止计时,此时按服务请求键会呼叫下一位。
uchar cntT0=0; // 中断计数, T0中断周期10ms, 中断100次即1s
// 函数定义
void delay10ms();
void InitCPU(); // T0中断周期10ms; 设置T1方式2用于串行通信, 波特率4800bps
void InitSys(); // 初始化系统:复位各变量,队列清空,灭 队非空指示灯, 亮 队空指示灯(这需要一个通信命令,实现服务机和取号机一起复位)
uchar GetWin(); // 查看哪个窗口有请求,失败时返回0,成功时返回请求窗口的编号
uchar GetCode(uchar win); // 响应某窗口服务请求, 失败时返回0,成功时返回一个服务编号
void CallGuest(uchar win, uchar num); // 呼叫 手持code服务号的客户 到 win窗口 接受服务
void IntT0(); // T0中断计时
void IntUART(); // 串口中断
/***************************************************************************************/
void main()
{
uchar win;
uchar num;
InitCPU();
InitSys();
//CallGuest(1, 2); // test ISD2560 module
while(1)
{
win=GetWin();
if(win!=0)
{
num=GetCode(win);
if(num!=0)
{
CallGuest(win, num);
}
}
}
}
/***************************************************************************************/
void delay10ms()
{
uchar i=20;
while(i--)
;
}
void InitCPU() // T0中断周期10ms; 设置T1方式2用于串行通信, 波特率9600bps
{
TMOD=0x21; // T1方式2, T0方式1
TH1=0xfe;; // 波特率9600~~~~~~~~~~~~~~~~~~~~~~
TL1=0xfe;
TH0=0x3c;;
TL0=0xb0;
IE=0x92;
TCON=0x50;
SCON=0x50;
}
void InitSys() // 初始化系统:复位各变量,队列清空,灭 队非空指示灯, 亮 队空指示灯(这需要一个通信命令,实现服务机和取号机一起复位)
{
//qFront=qRear=0;
//qFull=0; qEmpty=1;
//gCode=1; //// 全局变量 如果没有初始化,则默认0. 定义时已经初始化了,所以这里不用了。
winCnt[0]=winCnt[1]=20;
SBUF=MASTERRESET;
while(TI==0)
;
TI=0;
LEDNotEmpty=0; // 队列非空指示灯 灭
}
uchar GetWin() // 查看哪个窗口有请求,失败时返回0,成功时返回请求窗口的编号
{
if(Win1==0)
{
delay10ms();
if(Win1==0)
return 1;
}
if(Win2==0)
{
delay10ms();
if(Win2==0)
return 2;
}
return 0;
}
uchar GetCode(uchar win) // 响应某窗口服务请求, 失败时返回0,成功时返回一个服务编号
{
if(winCnt[win-1]<20) // 20s未到,重复呼叫
return winObj[win-1];
else // 20s到,呼叫下一位
{
if(qEmpty==1)
return 0;
winObj[win-1]=queueCode[qFront];
if(qFront==qRear)
{
// 通知取号机 灭队满指示灯
SBUF=WINKLEDFULL;
while(TI==0)
;
TI=0;
}
qFront=++qFront&0x03;
if(qFront==qRear)
{
qEmpty=1;
LEDNotEmpty=0; // 灭服务机客户指示灯
}
qFull=0; //// 一个关于语句顺序的问题:测试时让取号机连续发送号码请求。如果把这句前移6号,则刚把qFull清零, 马上进入串行中断 于是(qFront==qRear)成立,把qEmpty=1, 串口中断造成的 程序逻辑错误。不过实际中不会这样,因为有按键消抖还有延时。
}
winCnt[win-1]=0; // 启动20s服务计时
return winObj[win-1];
}
void CallGuest(uchar win, uchar num) // 呼叫 手持code服务号的客户 到 win窗口 接受服务
{
// 请 num号 客户 到 win号 窗口接受服务 // 由于语音模块中只录了6段语音“1号”“2号”“3号”“4号”“5号”“6号”,并且“5号”忘记录了~~~~~~~~~~ 所以叫两个号码表意~~~~
sound_play(num);
//
sound_play(win);
//
}
void IntT0() interrupt 1 using 1 // T0中断计时
{
TH0=0x3c; // 50ms
TL0=0xb0;
if(++cntT0==20)
{
cntT0=0;
if(winCnt[0]<20)
{winCnt[0]++; }
if(winCnt[1]<20)
winCnt[1]++;
}
}
void IntUART() interrupt 4 using 2 // 串口中断
{
uchar dat;
if(RI)
{
dat=SBUF;
RI=0;
switch(dat)
{
case SLAVERRESET:
// 查询队列状态,据此亮灭队满指示灯
qFull?(SBUF=LIGHTENLEDFULL):(SBUF=WINKLEDFULL);
while(TI==0)
;
TI=0;
break;
case REQUESTCODE:
if(qFull)return; //取号机请求, 当队列未满时取号机才会响应按键并发出请求。 不过,还是判断一下。
// 发送号码过去
SBUF=gCode;
while(TI==0)
;
TI=0;
// 把号码加入队列
queueCode[qRear]=gCode;
qRear=++qRear&0x03; // 队长4
if(qRear==qFront)
{
qFull=1;
// 通知取号机 亮起队满指示灯
SBUF=LIGHTENLEDFULL;
while(TI==0)
;
TI=0;
}
// 下一个要取得的号码
if(++gCode==7)
gCode=1;
// 亮 队列非空指示灯, 提示有窗户需要服务
LEDNotEmpty=1;
qEmpty=0;
break;
default: // 收到其它,通信错误
break;
}// end switch
}
}