这是2012年蓝桥杯的省赛的题目,也是单片机第一次加入蓝桥杯比赛系列,由于是初次加入蓝桥杯,所以这次比赛的自动售水机的题目(自动售水机)也比较简单,主要考察了IIC,独立按键,数码管显示,继电器和AD转化。
刚拿到比赛题目时我们首先要看清楚每次比赛对我们的要求,一般比赛的主要要求都会在设计任务及要求这一部分,所以看懂这一部分尤为重要,在这次的比赛设计比赛要求中,比赛方要求我们实现三大部分
1.按键控制单元
2.数码管显示
3.AD转化
那么我们现在就对这三部分进行讲解吧!
有题可知,但我们按下S7这按钮时,数码管的DS2部分开始出水(也就是说S7按下时,DS2部分开始数据变化,开始显示出水毫升),当然在系统说明部分也告诉我们最大只可显示9999,并且S7按下时继电器是打开的——L10打开,所以当我们写代码的时候我们些S7按键控制的变量也要与继电器相联系。
同样S6也是,按下S6时,DS2部分显示我们的水价,而且继电器断开——L10熄灭。
数码管显示部分我认为是这道题里最简单的,当按键**S7,S6,**不同时,其数码管显示的也不同,当要记住,DS1部分是不变的,而且数码管在这里的数据变化我认为只要在定时器里加个变量连来改变就行了(可能有点模糊下面的代码我会详细讲解的,其实很简单的)
这里主要涉及的是IIC的知识,通过IIC控制PCF89C51来控制光密电阻的电压从而控制led1的亮闪,其实也不难,只要把底层驱动代码修改一下就行了,然后再把相应的函数加到main函数就行。
time.c
#include "time.h"
void Timer0Init(void) //1毫秒@12.000MHz
{
AUXR &= 0x7F; //定时器时钟12T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
EA=ET0=1; //打开定时器中断
}
(在这里我喜欢用12Mhz,因为比较精确)
time.h
#ifndef _TIME_H_ //格式——.h
#define _TIME_H_ //格式——.h
#include
void Timer0Init(void);
#endif //格式——.h
smg.c
# include "smg.h"
unsigned char code tab []={
0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff,
0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10};//这里主要是小数部分
unsigned char dspbuf[8]={
10,10,10,10,10,10,10,10};
void smg_display(void)
{
static unsigned char i; //静态变量
P0=0XFF;
P2=(P2&0X1F)|0XE0; //关闭段选
P2&=0X1F;
P0=(1<<i); //自左移i,== (0X01<
P2=(P2&0X1F)|0XC0; //打开位选
P2&=0X1F;
P0=tab[dspbuf[i]]; //打开段选
P2=(P2&0X1F)|0XE0;
P2&=0X1F;
if(++i==8)
i=0;
}
smg.h
#ifndef _SMG_H_
#define _SMG_H_
#include
#include
#define uchar unsigned char //这里是定义,uchar=unsigned char
#define uint unsigned int //uint = unsigned int
void smg_display(void);
extern uchar dspbuf[8]; //如果主函数要用到的变量可以用extern来调用
#endif
key.c
# include "key.h"
unsigned char key_value;
unsigned char flag_realy;
void key_mode(void)
{
static unsigned char key_times;
if(P30!=1||P31!=1)
{
key_times++;
if(key_times==1)
{
if(P30==0)
key_value=1;
flag_realy=1;
if(P31==0)
key_value=2;
flag_realy=2;
}
else if(key_times>=33)
{
key_times=1;
key_value=0;
}
else
key_times=0;
}
}
key.h
# ifndef _KEY_H
# define _KEY_H
# include
void key_mode(void);
extern unsigned char key_value;
extern unsigned char flag_realy;
# endif
iic.c
# include "iic.h"
#include "intrins.h"
//总线引脚定义
sbit SDA = P2^1; /* 数据线 */
sbit SCL = P2^0; /* 时钟线 */
# define somenop{_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();}
//总线启动条件
void IIC_Start(void)
{
SDA = 1;//确保SDA线为高电平
SCL = 1;
somenop;
SDA = 0;//在SCL为高时拉低SDA线,即为起始信号
somenop;
SCL = 0;//将SCL线拉低,为后面SDA的电平变化做准备
}
//总线停止条件
void IIC_Stop(void)
{
SDA = 0; //确保SDA线为低电平
SCL = 1; //确保SCL高电平
somenop;
SDA = 1; //在SCL为高电平时拉高SDA线,即为停止信号,此时SDA和SCL均为高,同时完成IIC初始化过程
}
//等待应答
bit IIC_WaitAck(void)
{
SCL=1;//确保SDA线为低电平,应答相应时,SDA为高
somenop;
SDA=1;//确保SCL高电平,应答相应时,SCL为高
somenop;
if(SDA)
{
SCL=0;
IIC_Stop();//停止传输
return 0;//应答
}
else
{
SCL=0;//继续传输
return 1;//非应答
}
}
//通过I2C总线发送数据
void IIC_SendByte(unsigned char byt)
{
unsigned char i;
for(i=0;i<8;i++)
{
if(byt&0x80)
{
SDA = 1;
}
else
{
SDA = 0;
}
somenop;
SCL = 1;
byt <<= 1;
somenop;
SCL = 0;
}
}
//从I2C总线上接收数据
unsigned char IIC_RecByte(void)
{
unsigned char da;
unsigned char i;
for(i=0;i<8;i++)
{
SCL = 1;
somenop;
da <<= 1;
if(SDA)
da |= 0x01;
SCL = 0;
somenop;
}
return da;
}
unsigned char adc_read(unsigned char add) //这里是在底层驱动那量加上去的,主要是对于PCF8591选取端口,蓝桥杯多次选取的是1,3端口
{
unsigned char dat=0;
IIC_Start();
IIC_SendByte(0X90); //写
IIC_WaitAck(); //应答
IIC_SendByte(add);
IIC_WaitAck();
IIC_Stop();
IIC_Start();
IIC_SendByte(0X91); //读
IIC_WaitAck();
dat=IIC_RecByte();
IIC_Stop();
return dat;
}
iic.h
# ifndef _IIC_H
# define _IIC_H
# include
unsigned char adc_read(unsigned char add);
# endif
main.c
# include
# include "time.h"
# include "smg.h"
# include "key.h"
# include "iic.h"
unsigned char key_allow;
unsigned char water_sum=0;
unsigned char smg_key;
unsigned char flag_adc;
unsigned char light;
unsigned char water_sum;
unsigned char water_price;
unsigned char water_read;
void realy_on(void)
{
P2=(P2&0X1F)|0XA0;
P0=0X10;
P2&=0X1F;
// P0=0x10;
// P2=0xa0; P0=0x10; P2=0;
}
void realy_off(void)
{
P2=(P2&0X1F)|0XA0;
P0=0X00;
P2&=0X1F;
// P0=0;
// P2=0xa0; P0=0; P2=0;
}
void allinit(void)
{
P0=0XFF;
P2=(P2&0X1F)|0X80;
P2&=0X1F;
P0=0X00;
P2=(P2&0X1F)|0XA0;
P2&=0X1F;
}
void main (void)
{
allinit();
Timer0Init();
while(1)
{
/******key******/
if(key_allow)
{
key_allow=0;
key_mode();
}
switch(key_value)
{
case 1:
smg_key=1;
flag_realy=1;
break;
case 2:
smg_key=2;
flag_realy=2;
break;
}
/******adc******/
//
light=adc_read(0X01);
// }
//
if(light<64)
{
// P2=0X80;P0=0XFE;
P2=(P2&0X1F)|0X80;
P0=0XFE;
P2&=0X1F;
}
else
{
// P2=0X80;P0=0XFF;
P2=(P2&0X1F)|0X80;
P0=0xFF;
P2&=0X1F;
}
//
/*********realy*********/
if(flag_realy==1)//继电器
{
realy_on();
}
else
{
realy_off();
}
/***********water************/
if(flag_realy==1)
{
if(water_read==1)
{
water_read=0;
water_sum=water_sum+1;
water_price=water_sum/2;
}
if(water_sum==9999)
{
flag_realy=0;
smg_key=2;
}
}
/**************smg***************/
if(smg_key==1)
{
dspbuf[0]=10;
dspbuf[1]=11;
dspbuf[2]=5;
dspbuf[3]=0;
dspbuf[4]=water_sum/1000;
dspbuf[5]=(water_sum%1000/100+11);
dspbuf[6]=water_sum%100/10;
dspbuf[7]=water_sum%10;
}
if(smg_key==2)
{
dspbuf[0]=10;
dspbuf[1]=11;
dspbuf[2]=5;
dspbuf[3]=0;
dspbuf[4]=water_price/1000;
dspbuf[5]=(water_price%1000/100+11);
dspbuf[6]=water_price%100/10;
dspbuf[7]=water_price%10;
water_sum=0;//将之前数据清理,不然再次按下S7时会跟上之前的数值
}
}
}
void timer0_isr() interrupt 1
{
static unsigned char key_30_times;
static unsigned char water_allow;
static unsigned char adc_allow;
if(++key_30_times==30) //定时器的消抖
{
key_30_times=0;
key_allow=1;
}
if(++water_allow==100) //售水机时间的间隔
{
water_allow=0;
water_read=1;
}
smg_display();
}
这是我第一次写博客,可能会有写错的的地方,如有错误请指正,而且我的代码烧进CT107D可能会有一点点小bug,(光敏电阻的不灵敏,蜂鸣器的可能会发出一点点的微声音,但不大),这些bug我会尽快处理,但总体来说基本可以实现题目的要求。
我是hwh,一个刚入单片机的小萌新,希望在CSDN博客能与各位大佬交流,感谢观看。