藍橋杯第3屆省賽(單片機)_自動售水機
題目
這是2012年藍橋杯的省賽的題目,也是單片機第一次加入藍橋杯比賽系列,由於是初次加入藍橋杯,所以這次比賽的自動售水機的題目(自動售水機)也比較簡單,主要考察了IIC,獨立按鍵,數碼管顯示,繼電器和AD轉化。
題目講解
剛拿到比賽題目時我們首先要看清楚每次比賽對我們的要求,一般比賽的主要要求都會在設計任務及要求這一部分,所以看懂這一部分尤爲重要,在這次的比賽設計比賽要求中,比賽方要求我們實現三大部分
1.按鍵控制單元
2.數碼管顯示
3.AD轉化
那麼我們現在就對這三部分進行講解吧!
按鍵控制單元
有題可知,但我們按下S7這按鈕時,數碼管的DS2部分開始出水(也就是說S7按下時,DS2部分開始數據變化,開始顯示出水毫升),當然在系統說明部分也告訴我們最大隻可顯示9999,並且S7按下時繼電器是打開的——L10打開,所以當我們寫代碼的時候我們些S7按鍵控制的變量也要與繼電器相聯繫。
同樣S6也是,按下S6時,DS2部分顯示我們的水價,而且繼電器斷開——L10熄滅。
數碼管顯示
數碼管顯示部分我認爲是這道題裏最簡單的,當按鍵**S7,S6,**不同時,其數碼管顯示的也不同,當要記住,DS1部分是不變的,而且數碼管在這裏的數據變化我認爲只要在定時器里加個變量連來改變就行了(可能有點模糊下面的代碼我會詳細講解的,其實很簡單的)
AD轉化
這裏主要涉及的是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 <STC15F2K60S2.H>
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<<i)
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 <STC15F2K60S2.H>
#include <absacc.h>
#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 <STC15F2K60S2.H>
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 <STC15F2K60S2.H>
unsigned char adc_read(unsigned char add);
# endif
main.c
# include <STC15F2K60S2.H>
# 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博客能與各位大佬交流,感謝觀看。