题目要求
功能简述
通过竞赛硬件平台模拟小区自动售水机的工作流程:通过按键控制售水机水流出和停止;通过数码管显示费率、出水量及总费用;通过光敏电阻检测环境亮度,在亮度过低的情况下,自动开灯。系统硬件电路主要由单片机控制电路、数码管显示电路、 A/D 转换电路及功能按键组成。系统框图如图 1 所示:
I2C 总线驱动程序、 CT107D 考试平台电路原理图以及本题所涉及到的芯片数据手册,可参考计算机上的电子文档。程序流程图及相关工程文件请以考生号命名,并保存在计算机上的考生文件夹中(文件夹名为考生准考证号,文件夹位于 Windows 桌面上)
设计任务及要求
1. 按键控制单元
设定按键 S7 为出水控制按键,当 S7 按下后,售水机持续出水(继电器接通,指示灯 L10 点亮)。设定按键 S6 为停水控制按键,当 S6 按下后,停止出水(继电器断开,
指示灯 L10 熄灭)。
2. 数码管显示单元
通过 4 位数码管 DS1 显示费率,单位为元/升,保留 2 位有效数字;
通过 4 位数码管 DS2 显示当前出水总量(出水时,单位为升)和总价(停止时,单位为元):按下出水按键 S7 后,清除数码管 DS2 显示数据,数码管DS2 实时显示出水量(保留两位有效数字),在出水状态下,再次按下 S7,不会影响出水状态,直到按下停止按键 S6 为止;按下停止出水按键 S6 后,数码管 DS2 显示总价(保留两位有效数字)。
例:当 S7 按下后,数码管示意图如图 2 所示:
当按键 S6 按下后,数码管示意图如图 3 所示:
3. AD 转换单元
通过光敏电阻 RD1 和 AD 转换芯片 PCF8591 组成的亮度检测电路(亮度值转换为PCF8591 光敏电阻通道的电压)检测环境亮度;当 PCF8591 光敏电阻通道输入电压小于 1.25 V 时, L1 点亮,大于 1.25V 时, L1 熄灭。
4. 系统说明
- 假定水价为 0.5 元/升,出水速度为 100 毫升/秒;
- 一次出水总量达到 99.99L 时,继电器自动断开,数码管显示 DS2 显示价格。
5. 设计部分
假定自动售水机中存在一出水量检测传感器,输出信号为 4mA 到 20mA 直流信号,使用运算放大器设计接口电路,使得输入 4mA,输出 0V;输入 20mA,输出 5V。输入与输出满足线性关系。
程序代码
主函数
#include"stc15f2k60s2.h"
#include"iic.h"
sbit beep=P0^6; //蜂鸣器
sbit Relay=P0^4; //继电器
uchar code SMG_tab[]={
0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x00}; //共阴 0~9
uchar code SMG_duan[]={
0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
uchar smg1,smg2,smg3,smg4,smg5,smg6,smg7,smg8;
uchar key; //键值
uchar water_on; //是否出水
uchar SMG_delete;
uchar on_display[10];
uchar off_display[10];
uint price; //水的价格
uint water_v; //水的体积
uint count; //定时器计数,大于256一定不能再是uchar型!!!!
void delayms(uchar ms);
void display();
uchar Key_init();
void Timer0Init();
char read_key();
void main()
{
uchar key_val;
Timer0Init(); //1ms定时器
Write(0x01,0);
P2=0XA0;P0=0X00;
P2=0X80;P0=0XFF;
on_display[0]=SMG_tab[10];on_display[1]=SMG_tab[0]|0x80;
on_display[2]=SMG_tab[5];on_display[3]=SMG_tab[0];
off_display[0]=SMG_tab[10];off_display[1]=SMG_tab[0]|0x80;
off_display[2]=SMG_tab[5];off_display[3]=SMG_tab[0];
off_display[4]=SMG_tab[0];off_display[5]=SMG_tab[0]|0x80;
off_display[6]=SMG_tab[0];off_display[7]=SMG_tab[0];
while(1)
{
on_display[4]=SMG_tab[water_v/1000];
on_display[5]=SMG_tab[water_v/100%10]|0x80;
on_display[6]=SMG_tab[water_v/10%10];
on_display[7]=SMG_tab[water_v%10];
key_val=read_key();
ET0=0; //定时器会影响iic的时序,所以先关闭
if(Read(0x01)<64)
{
P2=0X80;P0=0XFF;
P0=0XFE;
}
else
{
P2=0X80;P0=0XFF;
P0=0XFF;
}
ET0=1;
if(water_v==10000)
{
water_on=0;
P2=0XA0;
Relay=0;beep=0;
price=water_v/2;
off_display[4]=SMG_tab[price/1000];
off_display[5]=SMG_tab[price/100%10]|0x80;
off_display[6]=SMG_tab[price/10%10];
off_display[7]=SMG_tab[price%10];
}
switch(key_val)
{
case 4:break;
case 5:break;
case 6:
water_on=0;
price=water_v/2;
off_display[4]=SMG_tab[price/1000];
off_display[5]=SMG_tab[price/100%10]|0x80;
off_display[6]=SMG_tab[price/10%10];
off_display[7]=SMG_tab[price%10];
P2=0XA0;
Relay=0;beep=0;
break;
case 7:
if(water_on==0)
water_v=0; //清除上次数据
water_on=1;
P2=0XA0;
Relay=1;beep=0;
break;
}
}
}
void Timer0Init() //1毫秒@11.0592MHz
{
AUXR |= 0x80; //定时器时钟1T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0xCD; //设置定时初值
TH0 = 0xD4; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
EA=1;
ET0=1;
}
void Timer0() interrupt 1
{
uchar i;
count++;
SMG_delete++;
if(SMG_delete==1)
{
SMG_delete=0;
P2=0XC0;P0=0X00;P2=0X00;
if(water_on)
{
P2=0XE0;P0=~on_display[i];P2=0;
}
if(water_on==0)
{
P2=0XE0;P0=~off_display[i];P2=0;
}
P2=0XC0;P0=SMG_duan[i];P2=0;
i++;
if(i==8)
i=0;
}
if(count==10)
{
count=0;
if(water_on)
water_v=water_v+10;
}
}
void delayms(uchar ms)
{
int i,j;
for(i=0;i<ms;i++)
for(j=845;j>0;j--);
}
/* 按键状态机 */
#define key_input P3
#define key_state_0 0 //判断是否按下
#define key_state_1 1 //判断是否为抖动
#define key_state_2 2 //判断是否弹起
char read_key()
{
static char key_state = 0;
char key_press, key_return = 0;
uchar key_mask=0x0f;
key_press = key_input&key_mask;
switch (key_state)
{
case key_state_0:
if (key_press!=key_mask)
key_state = key_state_1;
break;
case key_state_1:
if (key_press!=key_mask)
{
if(key_press==0x0e) key_return = 7; //S7
if(key_press==0x0d) key_return = 6; //S6
if(key_press==0x0b) key_return = 5; //S5
if(key_press==0x07) key_return = 4; //S4
key_state = key_state_2;
}
else
key_state = key_state_0;
break;
case key_state_2:
if (key_press==0x0f)
key_state = key_state_0;
break;
}
return key_return;
}
EEPROM模块
#include"IIC.h"
void IIC_delay(uchar m);
void IIC_start() //起始
{
SDA=1;
_nop_();
SCL=1;
_nop_();
SDA=0;
_nop_();
SCL=0;
_nop_();
}
void IIC_stop() //终止
{
SDA=0;
_nop_();
SCL=1;
_nop_();
SDA=1;
_nop_();
}
void Writebyte(uchar dat) //写一个字节
{
uchar i;
for(i=0;i<8;i++)
{
SCL=0;
IIC_delay(5);
SDA=dat&0x80; //从高位开始传输
SCL=1;
IIC_delay(5);
dat<<=1;
}
SCL=0;
IIC_delay(5);
}
uchar Readbyte() //读一个字节
{
uchar dat;
uchar i;
for(i=0;i<8;i++)
{
SCL=1;
IIC_delay(5);
dat<<=1;
if(SDA)
{
dat|=0x01;
}
SCL=0;
IIC_delay(5);
}
return dat;
}
uchar answer()
{
SCL=1;
IIC_delay(5);
if(SDA==1) //SDA=1表示非应答
{
SCL=0;
IIC_stop();
return 0;
}
else
{
SCL=0;
return 1;
}
}
void Write(uchar addr,uchar dat)
{
IIC_start();
Writebyte(0x90); //器件地址 A/D
answer(); //每收到一个8位数据,EEPROM都会在第9个时钟周期返回应答信号
Writebyte(addr); //数据首地址
answer();
Writebyte(dat);
answer();
IIC_stop();
}
uchar Read(uchar addr)
{
uchar num;
IIC_start();
Writebyte(0x90); //器件地址 最后一位为0,写入
answer();
Writebyte(addr); //读出首地址
answer();
IIC_start(); //在传送过程中,当需要改变传送方向时,起始信号和从机地址都被重复产生一次,但两次读/写方向位正好反相。
Writebyte(0x91); //器件地址+1 最后一位为1,读取
answer();
num=Readbyte();
IIC_stop();
return num;
}
void IIC_delay(uchar m)
{
do
{
_nop_();
}
while(m--);
}
以上就是代码的全部内容,欢迎交流,共同学习~