《蓝桥杯真题》:1.自动售水机

真题内容取自:
无语凝烟

功能简述:

  1. 通过竞赛硬件平台模拟小区自动售水机的工作流程,具体的:通过按键控制售水机水流出和停止;通过数码管显示费率、出水量及总费用;通过光敏电阻检测环境亮度,在亮度过低的情况下,自动开灯。系统硬件电路主要由单片机控制电路、数码管显示电路、A/D 转换电路及功能按 键组成。系统框图如图 1 所示:

在这里插入图片描述

设计任务及要求

  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. 系统说明
    (1)假定水价为 0.5 元/升,出水速度为 100 毫升/秒;
    (2)一次出水总量达到 99.99L 时,继电器自动断开,数码管显示 DS2 显示价格。

实现代码

注意点:记得添加资源数据包中的两个iic的底层代码到考生文件夹中,同时需查看发送非应答中的实参是0还是1(即结束读pcf8591,最后要给数据总线SDA一个高电平,下图给了大概的位置,自己实现代码时,要留意一下!)

//config.h--用于声明头文件,声明函数,声明一些经常使用的变量或难书写的语句
#ifndef _CONFIG_H
#define _CONFIG_H
#include <STC15F2K60S2.H>//对应芯片函数头文件,定义了一些特殊功能寄存器
#include<intrins.h>
#include "iic.h"
//typedef unsigned char uchar;
#define uchar unsigned char
#define uint unsigned int
void Delay10us();		//@12.000MHz

#endif

#include "config.h"
uchar n1 = 0, ad_val = 0;
uint cnt = 0, cnt1 = 0, v_val = 0;
uchar key_val = 0, key_cnt = 0;
bit key_flag = 0, flag_s6 = 0, flag_20ms = 0;
uchar dspcom = 0;
uchar dspbuf[8] = { 16, 0, 5, 0,  0, 0, 0, 0};
//共阴数码管段选表
uchar tab[] = {                       //标准字库
//   0    1    2    3    4    5    6    7    8    9    A    B    C    D    E    F
    0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71,
//black  -     H    J    K    L    N    o   P    U     t    G    Q    r   M    y
    0x00,0x40,0x76,0x1E,0x70,0x38,0x37,0x5C,0x73,0x3E,0x78,0x3d,0x67,0x50,0x37,0x6e,
    0xBF,0x86,0xDB,0xCF,0xE6,0xED,0xFD,0x87,0xFF,0xEF,0x46};    //0. 1. 2. 3. 4. 5. 6. 7. 8. 9. -1

void close_peripheral()
{
	//初始化led为全灭
	P2 = (P2 & 0x1f) | 0x80;
	P0 = 0xff;
	
	//初始化继电器,蜂鸣器不吸附,不发声
	P2 = (P2 & 0x1f) | 0xa0;
	P0 = 0xaf;
	P2 &= 0x1f;

}

void Timer0Init(void)		//2毫秒@12.000MHz
{
	AUXR &= 0x7F;		//定时器时钟12T模式
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x01;		//设置定时器模式
	TL0 = 0x30;		//设置定时初值
	TH0 = 0xF8;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
	ET0 = 1;
	EA = 1;
}

void Timer1Init(void)		//50毫秒@12.000MHz
{
	AUXR &= 0xBF;		//定时器时钟12T模式
	TMOD &= 0x0F;		//设置定时器模式
	TL1 = 0xB0;		//设置定时初值
	TH1 = 0x3C;		//设置定时初值
	TF1 = 0;		//清除TF1标志
	TR1 = 0;		//定时器1根据S7是否按下来判断是否计时

	//这边注释就对了??具体为啥还不知道
//	ET1 = 1;
//	EA = 1;
}



void display()
{
	//消隐
	P2 = (P2 & 0x1f) | 0xe0;
	P0 = 0xff;
	
	//位选
	P2 = (P2 & 0x1f) | 0xc0;
	P0 = 1 << dspcom;
	
	//段选
	P2 = (P2 & 0x1f) | 0xe0;
	P0 = ~tab[dspbuf[dspcom]];
	if (dspcom == 1 || dspcom == 5)
		P0 = (~tab[dspbuf[dspcom]]) & 0x7f;//小数点
	P2 &= 0x1f;
	
	if (++dspcom == 8)
	{
		dspcom = 0;
	}
}

void key_scanf()
{
	static char key_st = 0;
	switch(key_st)
	{
		case 0:
		{
			P3 = 0x0f;
			if (P3 != 0x0f)
				key_st++;
			break;
		}
		case 1://由case0到case1需要20ms其实相当于消抖
		{
			P3 = 0x0f;
			if (P3 != 0x0f)
			{
				key_st = 2;
				if (P30 == 0) key_val = 1;//s7
				if (P31 == 0) key_val = 2;//s6
				if (P32 == 0) key_val = 3;//s5
				if (P33 == 0) key_val = 4;//s4
			}
			else //按键抖动
				key_st = 0;
				
			break;
		}
		case 2:
		{
			P3 = 0x0f;
			if (P3 == 0x0f)
			{
				key_st = 0;
			}
			
			break;
		}
	}


}


void init_pcf8591(unsigned char channel)
{
	IIC_Start();
	IIC_SendByte(0x90);//写
	IIC_WaitAck();
	IIC_SendByte(channel);  //ADC通道1
	IIC_WaitAck();
	IIC_Stop();
	Delay10us();
}

unsigned char read_pcf8591(void)
{
	unsigned char temp;

	IIC_Start();
	IIC_SendByte(0x91);
	IIC_WaitAck();
	temp = IIC_RecByte();
	IIC_SendAck(1);//发送非应答信号 !!!注意这个地方的改变,要跟资源数据包中的内容对照一下
	IIC_Stop();
	
	return temp;
}

void main()
{
	close_peripheral();
	Timer0Init();//按键扫描和数码管显示
	Timer1Init();//用于出水总量和出水费用计算

	while(1)
	{
		if (key_flag)//20ms一次按键扫描
		{
			key_scanf();
			key_flag = 0;
		}
		
		if (key_val == 1 && !flag_s6)//出水键S7按下并且停水控制按键S6未按下,此时即将持续出水
		{
			TR1 = 1;//S7按下开始计时
			if (TF1)//50ms自动溢出标志
			{
				TF1 = 0;
				n1++;
			}
			if (n1 == 2)//100ms增加0.01升总出水量
			{
				n1 = 0;
				cnt++;//100ms增加0.01升总出水量
				if (cnt > 9999)//一次出水总量达到 99.99L 时,继电器自动断开,停止出水
				{	
					TR1 = 0;
					cnt = 0;
					P2 = (P2 & 0x1f) | 0xa0;
					P0 = 0xaf;
					P2 &= 0x1f;
				}
				dspbuf[4] = cnt / 1000;
				dspbuf[5] = cnt / 100 % 10;
				dspbuf[6] = cnt / 10 % 10;
				dspbuf[7] = cnt % 10;
			}
			//按下S7键时,继电器吸附,蜂鸣器发声
			P2 = (P2 & 0x1f) | 0xa0;
			P0 |= 0x10;
			P2 &= 0x1f;
			P0=0xff;
		}
		
		
		//按下S6键
		if (key_val == 2)
		{
			TR1 = 0;
			flag_s6 = 1;
			cnt1 = cnt / 2;  	
			dspbuf[4] = cnt1 / 1000;
			dspbuf[5] = cnt1 / 100 % 10;
			dspbuf[6] = cnt1 / 10 % 10;
			dspbuf[7] = cnt1 % 10;
			
			//S6 按下后,停止出水(继电器断开,蜂鸣器不发声 指示灯 L10 熄灭)。
			P2 = (P2 & 0x1f) | 0xa0;
			P0 &= 0xaf;
			P2 &= 0x1f;
			P0=0xff;
		}
		
		//按下S6后我们就,清除数码管 DS2 显示数据,使得数码管 DS2 实时显示出水量
		if (key_val == 1 && flag_s6)
		{
			cnt = 0;
			flag_s6 = 0;
		}
		
		
		//AD 转换单元每20ms采集数据	
		if (flag_20ms)
		{
			flag_20ms = 0;
			init_pcf8591(0x01);
			ad_val = read_pcf8591();//ad_value:0--255
			v_val = ad_val * 100 / 51;// v_value:0--500	
		}
		
		//PCF8591 结合光敏电阻通道输入电压小于 1.25 V 时,L1 点亮,大于 1.25V 时,L1 熄灭。
		if (v_val < 125)
		{
			P2 = (P2 & 0x1f) | 0x80;
			P0 = 0xfe;
			P2 &= 0x1f;
		}
		else 
		{
			P2 = (P2 & 0x1f) | 0x80;
			P0 = 0xff;
			P2 &= 0x1f;
		}
	}

}

void timer0() interrupt 1
{
	TL0 = 0x30;		//设置定时初值,2ms
	TH0 = 0xF8;		//设置定时初值
	display();
	
	if (++key_cnt == 10)
	{	
		flag_20ms = 1;
		key_cnt = 0;
		key_flag = 1;//扫描按键的标志
	}
}

void Delay10us()		//@12.000MHz
{
	unsigned char i;

	_nop_();
	_nop_();
	i = 27;
	while (--i);
}

版权声明:本文为CSDN博主「是七喜呀!」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/MerryMaking7946/article/details/122800850

是七喜呀!

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐