为什么要从第三届开始刷题呢?因为前面两届的题与最新的考试有些脱轨,故而不再练习。
前天已经刷完了赛题,实现大多数的功能。本以为很成功,但是呢,由于疏忽,没有从整体上去理解赛题,,以为只是点点灯,显示数码管之类的简单操作。但是,它柔和了PCF8591和AD转换器的知识,还有两个重要的知识IIC通信和中断。最后,就是怎么改驱动。
第三届 自动售水机
<1>: 数码管,LED,独立按键,继电器
<2>:IIC通信( AD转换器即PCF8591)
<3>:中断与定时器
第一部分:大多为必考内容,平时可以将其写成模块化。
#include<stc15f2k60s2.h>
#define uchar unsigned char
#define uint unsigned int
uchar code tab[]={0xc0,0xf9,0xa0,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xbf,0xff};
uchar yi,er,san,si,wu,liu,qi,ba;
bit S7=0,S6=0,S5=0,S4=0;
uchar num=0;
uchar key=0;
void display1(uchar yi,uchar er);
void display2(uchar san,uchar si);
void display3(uchar wu,uchar liu);
void display4(uchar qi,uchar ba);
void allinit();
void delayms(int ms);
void keyscan();
void main(void)
{
allinit();
yi=0;er=8;san=10;si=3;wu=0;liu=10;qi=0;ba=0;
while(1)
{
display1(yi,er);
display2(san,si);
display3(wu,liu);
display4(qi,ba);
}
}
void keyscan()
{
if(P30==0)
{
delayms(5);
if(P30==0)
{
S7=1;
}
while(!P30);//上升沿检测
}
else if(P31==0)
{
delayms(5);
if(P31==0)
{
S6=1;
}
while(!P31);
}
else if(P32==0)
{
delayms(5);
if(P32==0)
{
S5=1;
}
while(!P32);
}
else if(P33==0)
{
delayms(5);
if(P33==0)
{
S4=1;
}
while(!P33);
}
}
void allinit()
{
P2=0XA0;
P0=0x00;
P2=0X80;
P0=0XFF;
P2=0XC0;
P0=0XFF;
P2=0XFF;
P0=0XFF;
}
void display1(uchar yi,uchar er)
{
P2=0XC0;
P0=0X01;
P2=0XFF;
P0=tab[yi];
delayms(5);
P2=0XC0;
P0=0X01;
P2=0XFF;
P0=tab[er];
}
void display2(uchar san,uchar si)
{
P2=0XC0;
P0=0X04;
P2=0XFF;
P0=tab[san];
delayms(5);
P2=0XC0;
P0=0X08;
P2=0XFF;
P0=tab[si];
delayms(5);
}
void display3(uchar wu,uchar liu)
{
P2=0XC0;
P0=0X10;
P2=0XFF;
P0=tab[wu];
delayms(5);
P2=0XC0;
P0=0X20;
P2=0XFF;
P0=tab[liu];
delayms(5);
}
void display4(uchar qi,uchar ba)
{
P2=0XC0;
P0=0X40;
P2=0XFF;
P0=tab[qi];
delayms(5);
P2=0XC0;
P0=0X80;
P2=0XFF;
P0=tab[ba];
delayms(5);
}
void delayms(int ms)
{
int i,j;
for(i=ms;i>0;i--)
for(j=845;j>0;j--);
}
第二部分:IIC通信只联系到PCF8591的部分,将驱动进行稍微的更改。其实只是涉及到读的要求,只要加上一个读的函数即可。
uchar IIC_read(uchar add)
{
uchar temp;
IIC_Start();//开始信号
IIC_SendByte(0x90);//PCF8591
IIC_WaitAck();//等待响应
IIC_SendByte(add);
IIC_WaitAck();
IIC_Stop();
IIC_Start();//开始信号
IIC_SendByte(0x91);//PCF8591
IIC_WaitAck();//等待响应
temp=IIC_RecByte();//读出的数据
IIC_Stop();
return temp;
}
第三部分:中断与定时器。其实并不难,只需要在以前的基础上,根据赛题的要求,进行更改。
void Timer0Init(void) //5毫秒@11.0592MHz
{
AUXR |= 0x80; //定时器时钟1T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0x00; //设置定时初值
TH0 = 0x28; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
}
void timer0() interrupt 1
{
tt++;
if(tt==20)//判断是否满100毫秒
{
tt=0;
ML_10=ML_10+1;//满10毫升加1 每一百毫秒加10毫升
wu=ML_10/1000;liu=ML_10%1000/100;qi=ML_10%100/10;ba=ML_10%10;//在数码管显示值
if(ML_10>9999)
{
ET0=0;
EA=0;
P2=0xa0;
P0=0X00;
money=ML_10*0.5;
wu=money/1000;liu=money%1000/100;qi=money%100/10;ba=money%10;
ML_10=0;
}
}
}
到这里赛题的功能已经实现了。
经验总结:
1.要有一个整体的概念,每个模块之间都有一定的联系。
2.学会根据赛题改驱动。
3.注意些细节的问题,举个栗子:赛题要求出水速度为:100毫升/秒,而数码管显示要求保留两位小数,此时,就不能再一100毫升/秒的想法写程序,需要转化一下。这一步,在定时器中实现的。具体的做法:精确定时为5毫秒,在计数器中记20下,就是100毫秒,其中是10毫升,(即10毫升/100毫秒)。
4.遇到了一个问题,就是怎么判定光敏电阻是否到了1.25v。当时,查了手册,想了很久。后来,我知道了。单片机只认二进制,就需要转化一下。有一个算式:1.25/5*255=63.74,看做是64。这样问题就解决了。