目录
前言
要说单片机能考的知识点,那真是太多了,随着你对蓝桥杯了解的深入,你越来越会感到,各个模块我都能完成,但是把他们串一起时,思路就很混乱;或者感到这个题目我会,但是代码却是写了又删,删了有些,不断创造问题又解决问题,到头来也能完成比赛要求,但是会花费很多时间,这还是在不紧张的情况下,一到考场,用的不是自己的电脑,那更是大脑一片空白。
如果你省赛题目基本能完成,那你看看国赛题目,没准就会出现我上边说的情况,什么都会,但是写起来需要花费很多时间,那今天,我就分享一下我写代码的思路,以及如何应对蓝桥杯比赛。
长文制作不易,如果感觉或者以后想报单片机组,就点赞加关注吧!
本篇文章以第十四届省赛题目为例,文章顺序就是我写代码的顺序。
一、开考前30分钟
比赛正式开始前30分钟就可以进场,当然也可能更早。进入考场之后是还没有发赛题的,甚至连板子都没发,这时候千万不要傻愣着等开始了,这个时候你就可以开始写代码,完成基本的显示部分和按键扫描以及LED灯了。
如果你进赛场之后比较紧张的话,毕竟也是一个不熟悉的环境嘛,那估计写完这些就差不多要开始开始考试发赛题了;如果你写的比较熟练,那你可以根据你自己的习惯,多写一些模板。
下面是我期望的考前30分钟完成的代码,注释就不写了,这里主要完成了数码管扫描以及按键读取,并且把按键的键值显示到了数码管上。不懂的可以看后续的代码
#include <stc15.h>
#include <intrins.h>
#include "iic.h"
#include "ds1302.h"code unsigned char Seg_Table[] =
{
0xc0, //0
0xf9, //1
0xa4, //2
0xb0, //3
0x99, //4
0x92, //5
0x82, //6
0xf8, //7
0x80, //8
0x90, //9
};volatile unsigned char Led_Num=0xFF;
#define LED_ON(x) Led_Num&=~(0x01<<x);P0=Led_Num;P2=0x80;P2&=0x9F;P2&=0x1F;
#define LED_OFF(x) Led_Num|=0x01<<x; P0=Led_Num;P2=0x80;P2&=0x9F;P2&=0x1F;#define NIXIE_CHECK() P2=0xC0;P2&=0xDF;P2&=0x1F;
#define NIXIE_ON() P2=0xE0;P2&=0xFF;P2&=0x1F;void get_key(void);
void Timer0_Init(void); //1毫秒@12.000MHz
void Delay100ms(void); //@12.000MHzunsigned char location=0;
unsigned char key_value=0;
unsigned char Nixie_num[]={1,2,3,4,5,6,7,8};void main()
{
ds_init();
Timer0_Init();
Delay100ms();
EA=1;
while(1)
{
get_key();
Nixie_num[0]=key_value/10%10;
Nixie_num[1]=key_value/1%10;
Delay100ms();
}
}
void Timer0_Isr(void) interrupt 1
{
P0=0x01<<location;NIXIE_CHECK();
P0=Seg_Table[Nixie_num[location]];NIXIE_ON();
if(++location==8)
location=0;
}
void Timer0_Init(void) //1毫秒@12.000MHz
{
AUXR |= 0x80; //定时器时钟1T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0x20; //设置定时初始值
TH0 = 0xD1; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0=1;
}void Delay100ms(void) //@12.000MHz
{
unsigned char data i, j, k;_nop_();
_nop_();
i = 5;
j = 144;
k = 71;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void Delay5ms(void) //@12.000MHz
{
unsigned char data i, j;i = 59;
j = 90;
do
{
while (--j);
} while (--i);
}void get_key(void)
{
unsigned char key_P3=P3;
unsigned char key_P4=P4;
P3=0xFF;
P4=0xFF;
P44=0;
if(P30==0){Delay5ms();while(P30==0);Delay5ms();key_value=7;}
else if(P31==0){Delay5ms();while(P31==0);Delay5ms();key_value=6;}
else if(P32==0){Delay5ms();while(P32==0);Delay5ms();key_value=5;}
else if(P33==0){Delay5ms();while(P33==0);Delay5ms();key_value=4;}
P44=1;
P42=0;
if(P30==0){Delay5ms();while(P30==0);Delay5ms();key_value=11;}
else if(P31==0){Delay5ms();while(P31==0);Delay5ms();key_value=10;}
else if(P32==0){Delay5ms();while(P32==0);Delay5ms();key_value=9;}
else if(P33==0){Delay5ms();while(P33==0);Delay5ms();key_value=8;}
P42=1;
P35=0;
if(P30==0){Delay5ms();while(P30==0);Delay5ms();key_value=15;}
else if(P31==0){Delay5ms();while(P31==0);Delay5ms();key_value=14;}
else if(P32==0){Delay5ms();while(P32==0);Delay5ms();key_value=13;}
else if(P33==0){Delay5ms();while(P33==0);Delay5ms();key_value=12;}
P35=1;
P34=0;
if(P30==0){Delay5ms();while(P30==0);Delay5ms();key_value=19;}
else if(P31==0){Delay5ms();while(P31==0);Delay5ms();key_value=18;}
else if(P32==0){Delay5ms();while(P32==0);Delay5ms();key_value=17;}
else if(P33==0){Delay5ms();while(P33==0);Delay5ms();key_value=16;}
P34=1;
P3=key_P3;
P4=key_P4;
}
二、拿到考卷后
首先,拿到题目的同时,你绝对也已经拿到板子了,第一步一定要测试你考前30分钟写的代码是否有误。比赛时一定要步步为营
然后看赛题,先看程序框图,本篇都以十四届题目为例
程序框图上会告诉我们本次题目都涉及到哪些部分的内容,尤其是底层相关的部分,我们要先把底层代码拷到工程里,然后完成相关部分的实现。比如从上图十四届比赛的程序框图可以看出,我们需要使用NE555和DS18B20和PCF8591,并且PCF8591是双向箭头,所以说需要AD读取和DA输出,这个时候你应该先把NE555和PCF8591以及DS18B20相关的代码完成,并在main函数进行测试,完成之后再往下看显示部分。
三、显示部分实现
1.第十四届题目
这里以第十四届省赛题目为例
2.显示部分框架实现
不需要看整个题目,不需要理清题目要求,咱们看到哪里写到哪里,题目要求显示六个菜单,那咱们就把六个菜单写出来,至于菜单中需要显示的数据,我们有的就写。具体到这个赛题,我们只有第一个显示菜单,时间部分已经在你拿到考卷之后就已经写好了。
菜单中其他没有的数据,我们也不着急定义变量,题目上给的示例写的是什么数据,我们就暂时让他显示什么数据,显示一个死的数据(因为我说实话,你比赛的时候,有几个数据可能根本不知道是什么意思,从哪里来的,索性就先不处理)。比如温度回显显示的是温度和最大温度,我们可以不着急定义变量,题目上示例写的最大温度是28度,那我们就先显示28度,题目上平均温度是23.2度,那我们就显示23.2度,然后就这样编写完菜单。
大家编写菜单的习惯可能跟我不太一样,我这里姑且把我显示菜单的函数也放在下边
void show_menu(void)
{
if(mod==0)//时间显示
{
Nixie_num[0]=Time[2]/10%10;
Nixie_num[1]=Time[2]/1%10;
Nixie_num[2]=21;
Nixie_num[3]=Time[1]/10%10;
Nixie_num[4]=Time[1]/1%10;
Nixie_num[5]=21;
Nixie_num[6]=Time[0]/10%10;
Nixie_num[7]=Time[0]/1%10;
}
else if(mod==1)//回显1
{
Nixie_num[0]=22;//C
Nixie_num[1]=20;
Nixie_num[2]=2;
Nixie_num[3]=8;
Nixie_num[4]=21;
Nixie_num[5]=2;
Nixie_num[6]=3+10;
Nixie_num[7]=2;
}
else if(mod==2)//回显2
{
Nixie_num[0]=23;//H
Nixie_num[1]=20;
Nixie_num[2]=6;
Nixie_num[3]=8;
Nixie_num[4]=21;
Nixie_num[5]=5;
Nixie_num[6]=0+10;
Nixie_num[7]=4;
}
else if(mod==3)//回显3
{
Nixie_num[0]=24;//H
Nixie_num[1]=0;
Nixie_num[2]=2;
Nixie_num[3]=2;
Nixie_num[4]=1;
Nixie_num[5]=21;
Nixie_num[6]=1;
Nixie_num[7]=3;
}
else if(mod==4)//参数界面
{
Nixie_num[0]=25;//P
Nixie_num[1]=20;
Nixie_num[2]=20;
Nixie_num[3]=20;
Nixie_num[4]=20;
Nixie_num[5]=20;
Nixie_num[6]=3;
Nixie_num[7]=0;
}
else if(mod==5)
{
Nixie_num[0]=26;//E
Nixie_num[1]=20;
Nixie_num[2]=20;
Nixie_num[3]=2;
Nixie_num[4]=3;
Nixie_num[5]=21;
Nixie_num[6]=4;
Nixie_num[7]=8;
}
}
四、按键功能的实现
1.功能要求
显示完菜单之后,题目下边就是按键了,第十四届比赛显示部分是采集触发,后边才是按键,我们先不管采集触发,先完成按键功能:
2.基本功能实现
菜单这里又提到了参数的加和减,这参数都是比较好理解的变量,我们顺带着定义一下,把数码管显示部分也显示对应的变量,这样就可以完成数码管除了长按以外所有的操作了。读取按键键值的代码就不在写了,这里只展示一下对按键的处理,其中mod表示当前显示的菜单值,key_value表示当前读取到的键值。
至于长按短按,两个按键同时按下之类乱七八糟的操作,我们先暂时不写,记得有这个功能没实现就好。这些功能实现不实现,不影响我们拿省三。
//s4
if(key_value==4)//菜单切换
{
if(mod==0)//时间
mod=1;
else if(mod==1||mod==2||mod==3)//回显
mod=4;
else if(mod==4)//参数
mod=0;
}
//s5
else if(key_value==5)//回显菜单切换
{
if(mod==1)
mod=2;
else if(mod==2)
mod=3;
else if(mod==3)
mod=1;
}
//s8
else if(key_value==8)//加
{
if(mod==4)//在参数界面
{
temp_canshu=temp_canshu<99 ? temp_canshu+1 : 99;//限幅
}
}
else if(key_value==9)//减
{
if(mod==4)
{
temp_canshu=temp_canshu>0 ? temp_canshu-1 : 0;//限幅
}
}
到这里,你已经保底省三了
五、赛题要求功能的实现
下面的内容就因题而异了,能写多少写多少,写不出来的部分显示时就显示一个死的数据就好了。
到这里大家可能还是不太了解题目中的各种数据,各个菜单之间的关系。
这里在帮大家捋一下第十四届省赛的思路。
文章顺序就是我写代码的顺序。
1.湿度转化
湿度是通过频率转化而来的。这种代码几行就能搞定,随便找个地方,比如读取频率的函数内将频率转化成湿度就好了。注意限幅。(透露一点,其实下文提到了对湿度读取到无效值的处理,这里默认大家还不知道对这方面的要求,就先认为读取到无效数据时,湿度值为0了,后续我们看到了再处理)其中,shidu表示湿度,fre表示当前读取到的频率。
显示部分有一个地方需要显示湿度值,我们定义好湿度变量,并且写好湿度转化的代码之后,记得修改显示部分,让它显示变量的值。
//shidu=((90-10)/(2000-200))*(fre-200)+10;//公式
shidu=(unsigned char)(fre*0.044-200*0.044+10);//转化湿度
shidu=shidu > 90 ? 0: shidu;//湿度判断,读取到的湿度无限,则显示记录湿度为0
shidu=shidu < 10 ? 0: shidu;
写完这串代码之后,切记一定要先把关闭显示菜单,把湿度值显示到数码管上,观察湿度值是否正常。
2.采集触发
题目要求,当检测到从暗到亮触发一次采集,然后切换到温湿度显示菜单,温湿度显示菜单刚才定义过来,3S后返回功能大家到现在应该也都有自己的处理方法了,不清楚的也可查看我之前发的一些文章。
由于采集的是温度和湿度,湿度是由NE555转化而来的,而我们都知道,我们每次运行读取温度函数,实际上读取到的温度是上次运行温度读取函数时(甚至是上上次,上上上次)温度读取函数读取出来的温度,而NE555的读取也是要1S时间才能读取,在不到1S的时间内,频率值等于上秒NE555读取到的频率。也就是说,这两个东西都不能瞬时读取,那怎么办呢?
其实在第十四届代码讲解中也已经说过了,因为这个赛题需要的单片机资源也不多,所以我们可以一直读取温度和频率,当触发读取之后,就记录一下当前的温度和频率就好了。温度读取和频率读取我们放在main函数中,频率读取还跟之前一样,由定时器计时,每隔1s读取一次。
至于采集触发,我们就需要放在定时器内,当检测到从暗到亮,则将采集标志位置为1,触发一次采集即可。
我们新定义两个变量,分别为now_temp和now_shidu,每一次触发采集,也就是将当前读取到的温度值和湿度值复给now_temp和now_shidu。注意题目上的性能要求。
1)亮暗状态判断
这个地方处理方法较多,分享一下我的处理方法。如果当前为暗,并且连续3次(300ms,因为main函数的while循环内有一个100ms 的延时)AD均大于某个值,则记为亮;反之,如果当前为亮,并且连续3次AD均小于某个值,则记为暗。
其中这个AD大于的某个值和小于的某个值可以不相同,这样避免在临界点左右横跳。
我建议写好标志位之后,先把菜单显示关掉,先试一下AD值和标志位,看看标志位有没有乱跳。一定要步步为营。
bit is_liang=0;
void AD_run(void)
{
static unsigned char count_3_times=0;
AD=read_pcf(1);
if(is_liang==0)//如果当前记录的是“暗”状态
{
if(AD>150)//并且光敏电阻读取到的AD大于150
{
if(++count_3_times==3)//连续3次都这样
{
is_liang=1;//则记进入了“亮”
count_3_times=0;
}
}
else//如果不满足条件,则重新计数
count_3_times=0;
}
else if(is_liang==1)
{
if(AD<130)
{
if(++count_3_times==3)//连续3次都这样
{
is_liang=0;//则记进入了“暗”
count_3_times=0;
}
}
else
count_3_times=0;
}
}
2)触发采集
上边已经读取到的亮暗状态,接下来只要检测到由亮到暗,就触发一次采集。
前边也提到,我们是实时读取数据的,比如实时读取的温度值记为temp,实时读取的频率转化为的湿度记为shidu,每当触发一次采集,我们只需要将temp和fre的值赋值给now_temp和now_shidu,随后我们对now_temp和now_shidu进行处理即可。这样虽然浪费了许多单片机算了,不过好在题目并不复杂,应对起来应该没问题。
bit last_liang=1;
if(is_liang==0&&last_liang==1)//如果当前为暗,并且上一次为亮(即由亮变暗)
{
now_temp=temp;//则触发一次读取
now_shidu=shidu;
last_liang=0;//并更新亮暗数据
}
else if(is_liang==1&&last_liang==0)//如果当前为亮,并且上一次为暗
{
last_liang=1;//只更新亮暗数据
}
3)界面切换
我们已经可以检测到亮暗变化并触发一次采集了,现在只需要球切换一次菜单即可。切换到温湿度界面之后,3s在返回原始界面即可。这个就涉及到大家写的代码了,我写的代码是使用mod记录菜单,改变mod的值即可控制显示哪一个菜单,所以我只需要把mod=5加到上边提到的代码的第一个if判断的last_liang=0之前即可。
至于3s返回,我是定义一个标志位is_3s,定时器检查,当标志位为0时,3s后将标志位置为1;进入菜单之前将标志位置为0,在菜单内如果检查到标志位被置为1了,则返回上一次显示的菜单。
先展示一下修改后的触发采集,触发采集可以直接放在main函数中。
bit is_liang=1;
unsigned char now_temp=0;
unsigned char now_shidu=0;
bit is_3s=1;
unsigned char last_mod=0;//记录上一次的mod,用于3s后返回if(is_liang==0&&last_liang==1)//如果当前为暗,并且上一次为亮(即由亮变暗)
{
now_temp=temp;//则触发一次读取
now_shidu=shidu;
is_3s=0;
last_mod=mod;//记录上一次的mod,用于3s后返回
mod=5;
last_liang=0;//并更新亮暗数据}
else if(is_liang==1&&last_liang==0)//如果当前为亮,并且上一次为暗
{
last_liang=1;//只更新亮暗数据}
下面是计时3s的实现
bit is_3s=1;
unsigned int count_3s=0;
void Timer1_Isr(void) interrupt 3
{
if(is_3s==0)//is_3s为0时,
{
if(++count_3s==3000)//3s后将其置为1
{
is_3s=1;
count_3s=0;
}
}
else
count_3s=0;}
在mod=5的显示菜单内,只需加两行代码,3s后返回上一个菜单即可,下边代码是再显示函数中的一部分
else if(mod==5)
{
if(is_3s==1)//过了3s,则返回上一次的mod
mod=last_mod;
Nixie_num[0]=26;//E
Nixie_num[1]=20;
Nixie_num[2]=20;
Nixie_num[3]=now_temp/10%10;
Nixie_num[4]=now_temp/1%10;
Nixie_num[5]=21;
Nixie_num[6]=now_shidu/10%10;
Nixie_num[7]=now_shidu/1%10;
}
现在,你已经完成70%+的内容了。我感觉,仅仅是我个人感觉哈
写到现在,省二基本就有了
有了前边数据读取以及显示部分的基础,省下来的就很简单,就像万丈高楼拔地起,就差一个喜封金顶了。剩下的就是数据处理还有LED灯了,如果你还有时间的话,也都非常简单。
什么?你说触发次数,最大温度平均温度,最大湿度平均湿度都还没实现呢?拜托,这些东西完全可以放在最后在写,在你一开始连温度湿度怎么获取都不知道的时候,就算你拼死拼活凑出来了最高值 和平均值,那你也可能在写完后边读取数据之后,还得重头再重新计算数据。
所以,不管是省三模板也好,还是本篇介绍也好,对于显示部分的数据我都建议先把数据写死,不着急定义变量,当写到按键加减,需要控制某某参数时,由于这些参数都比较简单,也都不用算,这时候可以把参数定义出来,完成按键加减操作。
复杂数据的处理,以及长按短按连续按等功能,完全可以把简单的部分写完再写。千万不能捡了芝麻丢了西瓜。5个小时呢,在这5个小时,除了在到饭点儿时你会感觉到饿以外,你都有时间去不短晚上自己的代码,当前前提还是地基要打牢。在完成前70%时,一定要步步为营,处理好一个数据,就把那个数据显示到数码管上,千万不要着急。
另外,比赛时可以带一些食物和水进考场,如果你们考场比较好的话,一般也会提供一些面包和饮料。就算你中午饭点时还在敲代码,至少可以让你中午比完赛之后,有东西先点点肚子。
3.回显数据处理
先说明一点,地基打牢之后,剩下的数据处理还有LED显示都很简单。
这个时候,如果你按照题目顺序继续完成LED也好,回头完成数据处理也行。但是如果你时间比较紧张,或者你人比较紧张的话,建议先完成LED部分;如果你感觉地基打的不牢,可能会塌方,那你也可以先完成LED部分。我这里就先回过头完成数据处理了。
1)温度回显
很简单,温度回显数据只需要两个数据,一个是最大温度,一个是平均温度。
最大值
最大温度好处理,定义一个最大温度max_temp,如果now_temp>max_temp,则max_temp=now_temp即可。这些处理刚好需要再更新now_temp之后,而且每次读取到now_temp判断一次即可,所以我们还把上述处理放在采集触发那里(代码后续在演示)
平均值
对于平均值,我们需要一个变量,记录读取到的数据的个数times,定义平均值av_temp;那么新的av_temp就等于(av_temp*times+now_temp)/(++times)如果你更新time的时机与我的不一样,那你简单修改一下上述公式即可。
平均值要求精度精确到小数点后一位,我的处理是让av_temp扩大十倍,显示的时候最后一位就是小数部分了,当然对应的上述计算平均值的计算公式也要相应的修改。
上述两部分写出来就是下面几行代码,需要放在触发采集那里,最后会给修改后的触发采集的完整代码。
if(max_temp<now_temp)//当前温度大于记录的最大温度
max_temp=now_temp;//则更新最大温度数据
av_temp=(unsigned int)((av_temp*times+now_temp*10)/(times+1));//计算平均温度数据,注意这里的平均温度扩大了十倍
定义完新的变量,需要在显示部分让对应的菜单显示相应的变量。
2)湿度回显
与温度回显一毛一样,这里不再过多介绍。直接多定义几个变量,然后CV一下代码改一下变量就好了。
if(max_shidu<now_shidu)//当前湿度大于记录的最大湿度
max_shidu=now_shidu;//则更新最大湿度数据
av_shidu=(av_shidu*times+now_shidu*10)/(times+1);//计算平均湿度数据,注意这里的平均湿度扩大了十倍
注意在处理完温度和湿度数据后,要把times++
3)时间回显
我们可以如法炮制的完成这个部分,同样是定义两个变量now_time_hour和now_time_min,当然你也可以定义一个数组,当触发采集之后,同样的获取一次now_time_hour和now_time_min。所以这部分代码也要放在触发采集那里。
now_time_hour=Time[2];now_time_min=Time[1];
至于这个触发次数,就是我们刚才定义过的times,就算它不要求显示,为了算平均值,咱们也得记录总共触发了几次。
4)完整的触发采集代码
变量大部分都是uchar,几个标志位时bit,就不在展示了,不清楚的可以看最后边的完整代码
if(is_liang==0&&last_liang==1)//如果当前为暗,并且上一次为亮(即由亮变暗)
{
last_liang=0;//并更新亮暗数据,并触发一次读取
now_temp=temp;now_shidu=shidu;
now_time_hour=Time[2];now_time_min=Time[1];
is_3s=0;
if(max_temp<now_temp)//当前温度大于记录的最大温度
max_temp=now_temp;//则更新最大温度数据
av_temp=(unsigned int)((av_temp*times+now_temp*10)/(times+1));//计算平均温度数据,注意这里的平均温度扩大了十倍
if(max_shidu<now_shidu)//当前湿度大于记录的最大湿度
max_shidu=now_shidu;//则更新最大湿度数据
av_shidu=(av_shidu*times+now_shidu*10)/(times+1);//计算平均湿度数据,注意这里的平均湿度扩大了十倍
times++;
last_mod=mod;//记录上一次的mod,用于3s后返回
mod=5;
}
else if(is_liang==1&&last_liang==0)//如果当前为亮,并且上一次为暗
{
last_liang=1;//只更新亮暗数据
}
4.对采集次数为0时的处理
虽然只有两行,但是题目要求了可千万不能丢。
要说这个处理,我想大家应该都能很好的完成,之前已经定义了times,只需要在菜单显示代码中加上如果times等于0则显示啥啥啥,否则则显示正常的数据即可。这里就不再赘述了。
5.采集到无效数据的处理
对这个的处理可以在一开始完成触发采集的界面切换时就完成,不过现在完成也不晚。这个主要是针对湿度数据,在湿度转化那一节,我们就可以检测到读取到的湿度是否有效。我们定义一个标志位is_shidu_youxiao.如果湿度转化来的湿度无效,则is_shidu_youxiao为0,反之为1.我们使用采集温度湿度等同样的方式,定义一个now_is_shidu_youxiao标志位,触发采集之后,now_is_shidu_youxiao=is_shidu_youxiao。如果now_is_shidu_youxiao为0,则不再记录其他数据,并且回显时湿度显示AA,回显时显示AA可以通过now_is_shidu_youxiao,为0时湿度显示AA,否则正常显示即可。
1)判断湿度无效
最开始我们读取的湿度只进行了限幅,或者说可以检测到无效值,但是并未进行处理:
shidu=(unsigned char)(fre*0.044-200*0.044+10);//转化湿度
shidu=shidu > 90 ? 0 : shidu;//湿度判断,读取到的湿度无限,则显示记录湿度为0
shidu=shidu < 10 ? 0 : shidu;
现在,我们修改上述代码,使得读取到无效数据后让is_shidu_youxiao置为0,即可。
shidu=(unsigned char)(fre*0.044-200*0.044+10);//转化湿度
if(shidu<10||shidu>90)//湿度无效
is_shidu_youxiao=0;
else
is_shidu_youxiao=1;
//
// shidu=shidu > 90 ? 0 : shidu;//湿度判断,读取到的湿度无限,则显示记录湿度为0
// shidu=shidu < 10 ? 0 : shidu;
当然,题目上最开始说的是频率的无效区间,你也可以不判断湿度,直接判断频率,这里就不再解释了
2)处理湿度无效
我们在触发采集那里,读取now_is_shidu_youxiao=is_shidu_youxiao,并判断now_is_shidu_youxiao的值,如果无效,则不记录采集次数,不对最高温度,平均温度,最高频率,平均频率,采集时,采集分进行更新,并且数码管对应地方显示AA.
修改后的触发采集代码
if(is_liang==0&&last_liang==1)//如果当前为暗,并且上一次为亮(即由亮变暗)
{
last_liang=0;//并更新亮暗数据,并触发一次读取
now_temp=temp;now_shidu=shidu;
now_time_hour=Time[2];now_time_min=Time[1];
is_3s=0;
now_is_shidu_youxiao=is_shidu_youxiao;
if(now_is_shidu_youxiao==1)
{
if(max_temp<now_temp)//当前温度大于记录的最大温度
max_temp=now_temp;//则更新最大温度数据
av_temp=(unsigned int)((av_temp*times+now_temp*10)/(times+1));//计算平均温度数据,注意这里的平均温度扩大了十倍
if(max_shidu<now_shidu)//当前湿度大于记录的最大湿度
max_shidu=now_shidu;//则更新最大湿度数据
av_shidu=(av_shidu*times+now_shidu*10)/(times+1);//计算平均湿度数据,注意这里的平均湿度扩大了十倍
times++;
}last_mod=mod;//记录上一次的mod,用于3s后返回
mod=5;
}
else if(is_liang==1&&last_liang==0)//如果当前为亮,并且上一次为暗
{
last_liang=1;//只更新亮暗数据
}
数码管显示就比较简单了,就一个if判断,而且大家写法可能也不太一样就不在展示了。
6.长按功能的实现
时间回显子界面长按S9两秒后松开,清楚所有记录、
跟之前的处理方法一样,我的按键读取都是if {Delay while Delay key_value=}类型的。我们定义一个标志位is_2s,当标志位为0时,2s后定时器会把标志位置为1.我们再第一个Delay之后将is_2s置为0,在while之后,第二个Delay之前,判断is_2S是否为1,如果为1,并且在对应的菜单里,则情况数据,否则无效。随后将is_2s置为1即可。
关于is_2s的处理跟上边数3s的处理一样
void Timer1_Isr(void) interrupt 3
{
if(is_2s==1&&mod==3)//检测是否按够2s了,
{
if(++count_2000==2000)//2s后将其置为1
{
is_2s=1;
count_2000=0;
}
}
else
count_2000=0;
}
按键也很简单
P42=0;
if(P32==0)
{
Delay5ms();
is_2s=0;//开始计时
while(P32==0);
if(is_2s==1)//检测是否按够2s了
{
//清除数据
now_temp=0;max_temp=0;av_temp=0;
now_shidu=0;max_shidu=0;av_shidu=0;
times=0;now_time_hour=0;now_time_min=0;
now_is_shidu_youxiao=0;
}
is_2s=1;
Delay5ms();
key_value=9;
}
7.LED灯功能的实现
led灯的功能更为简单,唯一需要处理的就是温度湿度均升高。
对于温度湿度均升高的处理,我们可以先定义一个last_temp和last_shidu。先定义一个变量is_up当在触发采集时,并且湿度数据有效的情况下,如果last_temp>now_temp并且last_shidu>now_shidu,将is_up置为1,否则置为0;最后在更新last_temp和last_shidu,也就是last_temp=now_temp和last_shidu=now_shidu。
在led运行函数中,如果检测到了is_up为1,就对相应的LED灯处理。
void led_run(void)
{
static bit led1_is_on=0;//这几个变量都是某个led灯的状态标志位,用于记录led灯的亮灭状态
static bit led2_is_on=0;
static bit led3_is_on=0;
static bit led4_is_on=0;
static bit led5_is_on=0;
static bit led6_is_on=0;
if(mod==0&&led1_is_on==0)//在时间菜单,led1点亮
{
LED_ON(0);
led1_is_on=1;
}
else if(mod!=0&&led1_is_on==1)//否则,led1熄灭
{
LED_OFF(0);
led1_is_on=0;
}
if((mod==1||mod==2||mod==3)&&led2_is_on==0)//在数据回显菜单,led2点亮
{
LED_ON(1);
led2_is_on=1;
}
else if(!(mod==1||mod==2||mod==3)&&led2_is_on==1)//否则,led2熄灭
{
LED_OFF(1);
led2_is_on=0;
}
if(mod==5&&led3_is_on==0)//在温湿度菜单,led3点亮
{
LED_ON(2);
led3_is_on=1;
}
else if(mod!=5&&led3_is_on==1)//否则,led3熄灭
{
LED_OFF(2);
led3_is_on=0;
}
if(now_temp>temp_canshu)//如果读取到的温度值高于温度阈值,则led4闪烁
{
/*主函数的while(1)循环里有100ms延时,这里只需要保证进入一次led_run函数翻转一次led4的状态,即可闪烁*/
if(led4_is_on==0)//当前led4熄灭,则点亮
{
LED_ON(3);
led4_is_on=1;
}
else if(led4_is_on==1)//当前led4已电亮,则熄灭
{
LED_OFF(3);
led4_is_on=0;
}
}
else if(led4_is_on==1)//如果读取到的温度值不高于阈值,则led4熄灭
{
LED_OFF(3);
led4_is_on=0;
}
if(now_is_shidu_youxiao==0&&led5_is_on==0)//如果当前读取到的湿度无无效,则led5点亮
{
LED_ON(4);
led5_is_on=1;
}
else if(now_is_shidu_youxiao==1&&led5_is_on==1)//如果当前读取到的湿度有效,则led5熄灭
{
LED_OFF(4);
led5_is_on=0;
}
if(is_up==1&&led6_is_on==0)//如果温度和湿度均比上一次读取到的值高,则led6点亮
{
LED_ON(5);
led6_is_on=1;
}
else if(is_up==0&&led6_is_on==1)//否则熄灭
{
LED_OFF(5);
led6_is_on=0;
}
}
写到这里,几乎已经是省一了
六、最后一小时
当然稳定拿省一的前提是前边数据处理的都没问题哈,其实有一点问题也不大,因为功能基本实现了。但是现在比赛还没结束,活儿还没干完。比如:
比如
比如:
还有:
这些东西可都不能忘了。题目写完记得打包提交。
1.程序源代码分享
这次代码是边写文章边写的,个人感觉思路可能会比上一次更清晰一些,但是质量可能不如上一次。主要容易分心。这里把完整代码分享给大家,如果前边提到的代码有些部分不太完全的话,可以看这里的完整代码。
mian.c
#include <stc15.h>
#include <intrins.h>
#include "onewire.h"
#include "iic.h"
#include "ds1302.h"
code unsigned char Seg_Table[] =
{
0xc0, //0
0xf9, //1
0xa4, //2
0xb0, //3
0x99, //4
0x92, //5
0x82, //6
0xf8, //7
0x80, //8
0x90, //9
0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10,
0xFF,
0xBF,//21 -
0xC6,//22 C
0x89,//23 H
0x8e, //24 F
0x8C,//25 P
0x86, //26,E
0x88, //27 A
};
volatile unsigned char Led_Num=0xFF;
#define LED_ON(x) Led_Num&=~(0x01<<x);P0=Led_Num;P2=0x80;P2&=0x9F;P2&=0x1F;
#define LED_OFF(x) Led_Num|=0x01<<x; P0=Led_Num;P2=0x80;P2&=0x9F;P2&=0x1F;
#define LED__OFF_ALL() Led_Num=0xFF; P0=Led_Num;P2=0x80;P2&=0x9F;P2&=0x1F;
#define NIXIE_CHECK() P2=0xC0;P2&=0xDF;P2&=0x1F;
#define NIXIE_ON() P2=0xE0;P2&=0xFF;P2&=0x1F;
void get_key(void);
void Timer0_Init(void); //1毫秒@12.000MHz
void Timer1_Init(void); //1毫秒@12.000MHz
void Delay100ms(void); //@12.000MHz
void read_ne555(void);
void show_menu(void);
void run(void);
void AD_run(void);
void led_run(void);
unsigned char location=0;
unsigned char key_value=0;
unsigned char Nixie_num[]={20,20,20,20,20,20,20,20};
unsigned char mod=0;
unsigned int temp=0;
unsigned int fre=0;
unsigned char temp_canshu=30;
unsigned char AD=0;
unsigned char shidu=0;
bit is_liang=1;
unsigned char now_temp=0;
unsigned char now_shidu=0;
bit is_3s=1;
unsigned char last_mod=0;//记录上一次的mod,用于3s后返回
unsigned char max_temp;
unsigned int av_temp;
unsigned char max_shidu;
unsigned int av_shidu=0;
unsigned char times=0;
unsigned char now_time_hour;//采集到时
unsigned char now_time_min;//采集到的分
bit is_shidu_youxiao;//实时读取的湿度是否有效
bit now_is_shidu_youxiao;//采集的湿度是否有效
bit is_up=0;//这一次读取到的温度和湿度都比上一次高
unsigned char last_temp=0;
unsigned char last_shidu=0;
void main()
{
ds_init();
read_temp();
LED__OFF_ALL();
AD_run();
Delay100ms();
Timer0_Init();
Timer1_Init();
EA=1;
while(1)
{
get_key();
// Nixie_num[0]=shidu/10%10;
// Nixie_num[1]=shidu/1%10;
// Nixie_num[2]=fre/1000%10;
// Nixie_num[3]=fre/100%10;
// Nixie_num[4]=fre/10%10;
// Nixie_num[5]=fre/1%10;
run();
Delay100ms();
}
}
void run(void)
{
static bit last_liang=1;
show_menu();//显示菜单
read_ds();//读取时间
temp=read_temp();temp=temp > 99 ? 99 : temp;//读取并限幅,读取的温度不可能是负值,也不太可能超过99度
read_ne555();//读取NE555
AD_run();//读取并处理AD,使其转化为湿度
led_run();
if(is_liang==0&&last_liang==1)//如果当前为暗,并且上一次为亮(即由亮变暗)
{
last_liang=0;//并更新亮暗数据,并触发一次读取
now_temp=temp;now_shidu=shidu;
now_time_hour=Time[2];now_time_min=Time[1];
is_3s=0;
now_is_shidu_youxiao=is_shidu_youxiao;
if(now_is_shidu_youxiao==1)
{
if(max_temp<now_temp)//当前温度大于记录的最大温度
max_temp=now_temp;//则更新最大温度数据
av_temp=(unsigned int)((av_temp*times+now_temp*10)/(times+1));//计算平均温度数据,注意这里的平均温度扩大了十倍
if(max_shidu<now_shidu)//当前湿度大于记录的最大湿度
max_shidu=now_shidu;//则更新最大湿度数据
av_shidu=(av_shidu*times+now_shidu*10)/(times+1);//计算平均湿度数据,注意这里的平均湿度扩大了十倍
if(last_temp>now_temp&&last_shidu>now_shidu)
is_up=1;
else
is_up=0;
last_temp=now_temp;
last_shidu=now_shidu;
times++;
}
last_mod=mod;//记录上一次的mod,用于3s后返回
mod=5;
}
else if(is_liang==1&&last_liang==0)//如果当前为亮,并且上一次为暗
{
last_liang=1;//只更新亮暗数据
}
}
bit is_read_ne555=0;
unsigned int is_1s=0;
bit is_2s=1;//用于判断长按2s
unsigned int count_2000;
unsigned int count_3s=0;//用于记录3S后返回菜单
void Timer1_Isr(void) interrupt 3
{
P0=0x01<<location;NIXIE_CHECK();
P0=Seg_Table[Nixie_num[location]];NIXIE_ON();
if(++location==8)
location=0;
if(is_read_ne555==0)//如果is_read_ne555为0
{
if(++is_1s==1000)//1s后,将它置为1
{//用于为NE555提供计时
is_1s=0;
is_read_ne555=1;
}
}
if(is_3s==0)//is_3s为0时,
{
if(++count_3s==3000)//3s后将其置为1
{
is_3s=1;
count_3s=0;
}
}
else
count_3s=0;
if(is_2s==0)//is_2s为0时,
{
if(++count_2000==2000)//2s后将其置为1
{
is_2s=1;
count_2000=0;
}
}
else
count_2000=0;
}
void Timer0_Init(void) //1毫秒@12.000MHz
{
AUXR = 0x80; //定时器0为1T模式
TMOD = 0x04; //设置定时器0为16位自动重装载外部记数模式
TH0 = TL0 = 0x00; //设置定时器0初始值
TR0 = 1; //定时器0开始工作
//ET0 = 1; //开定时器0中断
}
void Timer1_Init(void) //1毫秒@12.000MHz
{
AUXR |= 0x40; //定时器时钟1T模式
TMOD &= 0x0F; //设置定时器模式
TL1 = 0x20; //设置定时初始值
TH1 = 0xD1; //设置定时初始值
TF1 = 0; //清除TF1标志
TR1 = 1; //定时器1开始计时
ET1 = 1; //使能定时器1中断
}
void Delay100ms(void) //@12.000MHz
{
unsigned char data i, j, k;
_nop_();
_nop_();
i = 5;
j = 144;
k = 71;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void Delay5ms(void) //@12.000MHz
{
unsigned char data i, j;
i = 59;
j = 90;
do
{
while (--j);
} while (--i);
}
void get_key(void)
{
unsigned char key_P3=P3;
unsigned char key_P4=P4;
P3=0xFF;
P4=0xFF;
P44=0;
if(P32==0){Delay5ms();while(P32==0){run();}Delay5ms();key_value=5;}
else if(P33==0){Delay5ms();while(P33==0){run();}Delay5ms();key_value=4;}
P44=1;
P42=0;
if(P32==0)
{
Delay5ms();
is_2s=0;//开始计时
while(P32==0);
if(is_2s==1&&mod==3)//检测是否按够2s了
{
//清除数据
now_temp=0;max_temp=0;av_temp=0;
now_shidu=0;max_shidu=0;av_shidu=0;
times=0;now_time_hour=0;now_time_min=0;
now_is_shidu_youxiao=0;
}
is_2s=1;
Delay5ms();
key_value=9;
}
else if(P33==0){Delay5ms();while(P33==0){run();}Delay5ms();key_value=8;}
P42=1;
if(mod==5)
key_value=0;
//s4
if(key_value==4)//菜单切换
{
if(mod==0)//时间
mod=1;
else if(mod==1||mod==2||mod==3)//回显
mod=4;
else if(mod==4)//参数
mod=0;
}
//s5
else if(key_value==5)//回显菜单切换
{
if(mod==1)
mod=2;
else if(mod==2)
mod=3;
else if(mod==3)
mod=1;
}
//s8
else if(key_value==8)//加
{
if(mod==4)//在参数界面
{
temp_canshu=temp_canshu<99 ? temp_canshu+1 : 99;
}
}
else if(key_value==9)//减
{
if(mod==4)
{
temp_canshu=temp_canshu>0 ? temp_canshu-1 : 0;
}
}
key_value=0;
P3=key_P3;
P4=key_P4;
}
void read_ne555(void)
{
if(is_read_ne555==1)
{
is_read_ne555=0;
TR0=0;//停止计数,便于读取
fre=TH0;//读取数据
fre<<=8;
fre|=TL0;
TL0=0;//清空数据
TH0=0;
TF0=0;
TR0=1;//在开始计数
shidu=(unsigned char)(fre*0.044-200*0.044+10);//转化湿度
if(shidu<10||shidu>90)//湿度无效
is_shidu_youxiao=0;
else
is_shidu_youxiao=1;
//
// shidu=shidu > 90 ? 0 : shidu;//湿度判断,读取到的湿度无限,则显示记录湿度为0
// shidu=shidu < 10 ? 0 : shidu;
}
}
void show_menu(void)
{
if(mod==0)//时间显示
{
Nixie_num[0]=Time[2]/10%10;
Nixie_num[1]=Time[2]/1%10;
Nixie_num[2]=21;
Nixie_num[3]=Time[1]/10%10;
Nixie_num[4]=Time[1]/1%10;
Nixie_num[5]=21;
Nixie_num[6]=Time[0]/10%10;
Nixie_num[7]=Time[0]/1%10;
}
else if(mod==1)//回显1
{
Nixie_num[0]=22;//C
Nixie_num[1]=20;
if(times==0)//如果time为0,则不显示部分内容
{
Nixie_num[2]=20;
Nixie_num[3]=20;
Nixie_num[4]=20;
Nixie_num[5]=20;
Nixie_num[6]=20;
Nixie_num[7]=20;
}
else//否则正常显示
{
Nixie_num[2]=max_temp/10%10;
Nixie_num[3]=max_temp/1%10;
Nixie_num[4]=21;
Nixie_num[5]=av_temp/100%10;
Nixie_num[6]=av_temp/10%10+10;
Nixie_num[7]=av_temp/1%10;
}
}
else if(mod==2)//回显2
{
Nixie_num[0]=23;//H
Nixie_num[1]=20;
if(times==0)//如果time为0,则不显示部分内容
{
Nixie_num[2]=20;
Nixie_num[3]=20;
Nixie_num[4]=20;
Nixie_num[5]=20;
Nixie_num[6]=20;
Nixie_num[7]=20;
}
else//否则正常显示
{
Nixie_num[2]=6;
Nixie_num[3]=8;
Nixie_num[4]=21;
Nixie_num[5]=5;
Nixie_num[6]=0+10;
Nixie_num[7]=4;
}
}
else if(mod==3)//回显3
{
Nixie_num[0]=24;//F
Nixie_num[1]=times/10%10;
Nixie_num[2]=times/1%10;
if(times==0)//如果time为0,则不显示部分内容
{
Nixie_num[3]=20;
Nixie_num[4]=20;
Nixie_num[5]=20;
Nixie_num[6]=20;
Nixie_num[7]=20;
}
else//否则正常显示
{
Nixie_num[3]=now_time_hour/10%10;
Nixie_num[4]=now_time_hour/1%10;
Nixie_num[5]=21;
Nixie_num[6]=now_time_min/10%10;
Nixie_num[7]=now_time_min/1%10;
}
}
else if(mod==4)//参数界面
{
Nixie_num[0]=25;//P
Nixie_num[1]=20;
Nixie_num[2]=20;
Nixie_num[3]=20;
Nixie_num[4]=20;
Nixie_num[5]=20;
Nixie_num[6]=temp_canshu/10%10;
Nixie_num[7]=temp_canshu/1%10;
}
else if(mod==5)
{
if(is_3s==1)//过了3s,则返回上一次的mod
mod=last_mod;
Nixie_num[0]=26;//E
Nixie_num[1]=20;
Nixie_num[2]=20;
Nixie_num[3]=now_temp/10%10;
Nixie_num[4]=now_temp/1%10;
Nixie_num[5]=21;
if(now_is_shidu_youxiao==1)//湿度有效,正常显示
{
Nixie_num[6]=now_shidu/10%10;
Nixie_num[7]=now_shidu/1%10;
}
else//否则显示AA
{
Nixie_num[6]=27;
Nixie_num[7]=27;
}
}
}
void AD_run(void)
{
static unsigned char count_times=0;//读取多次AD数据均满足
AD=read_pcf(1);
if(is_liang==0)//如果当前记录的是“暗”状态
{
if(AD>150)//并且光敏电阻读取到的AD大于阈值
{
if(++count_times==4)//连续3次都这样
{
is_liang=1;//则记进入了“亮”
count_times=0;
}
}
else//如果不满足条件,则重新计数
count_times=0;
}
else if(is_liang==1)
{
if(AD<130)//并且光敏电阻读取到的AD小于阈值
{
if(++count_times==4)//连续3次都这样
{
is_liang=0;//则记进入了“暗”
count_times=0;
}
}
else
count_times=0;
}
}
void led_run(void)
{
static bit led1_is_on=0;//这几个变量都是某个led灯的状态标志位,用于记录led灯的亮灭状态
static bit led2_is_on=0;
static bit led3_is_on=0;
static bit led4_is_on=0;
static bit led5_is_on=0;
static bit led6_is_on=0;
if(mod==0&&led1_is_on==0)//在时间菜单,led1点亮
{
LED_ON(0);
led1_is_on=1;
}
else if(mod!=0&&led1_is_on==1)//否则,led1熄灭
{
LED_OFF(0);
led1_is_on=0;
}
if((mod==1||mod==2||mod==3)&&led2_is_on==0)//在数据回显菜单,led2点亮
{
LED_ON(1);
led2_is_on=1;
}
else if(!(mod==1||mod==2||mod==3)&&led2_is_on==1)//否则,led2熄灭
{
LED_OFF(1);
led2_is_on=0;
}
if(mod==5&&led3_is_on==0)//在温湿度菜单,led3点亮
{
LED_ON(2);
led3_is_on=1;
}
else if(mod!=5&&led3_is_on==1)//否则,led3熄灭
{
LED_OFF(2);
led3_is_on=0;
}
if(now_temp>temp_canshu)//如果读取到的温度值高于温度阈值,则led4闪烁
{
/*主函数的while(1)循环里有100ms延时,这里只需要保证进入一次led_run函数翻转一次led4的状态,即可闪烁*/
if(led4_is_on==0)//当前led4熄灭,则点亮
{
LED_ON(3);
led4_is_on=1;
}
else if(led4_is_on==1)//当前led4已电亮,则熄灭
{
LED_OFF(3);
led4_is_on=0;
}
}
else if(led4_is_on==1)//如果读取到的温度值不高于阈值,则led4熄灭
{
LED_OFF(3);
led4_is_on=0;
}
if(now_is_shidu_youxiao==0&&led5_is_on==0)//如果当前读取到的湿度无无效,则led5点亮
{
LED_ON(4);
led5_is_on=1;
}
else if(now_is_shidu_youxiao==1&&led5_is_on==1)//如果当前读取到的湿度有效,则led5熄灭
{
LED_OFF(4);
led5_is_on=0;
}
if(is_up==1&&led6_is_on==0)//如果温度和湿度均比上一次读取到的值高,则led6点亮
{
LED_ON(5);
led6_is_on=1;
}
else if(is_up==0&&led6_is_on==1)//否则熄灭
{
LED_OFF(5);
led6_is_on=0;
}
}
onewire.c
/* # 单总线代码片段说明
1. 本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
2. 参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
中对单片机时钟频率的要求,进行代码调试和修改。
*/
#include <stc15.h>
#include <intrins.h>
#include "onewire.h"
sbit DQ=P1^4;
//
void Delay_OneWire(unsigned int t)
{
unsigned char i;
while(t--){
for(i=0;i<12;i++);
}
}
//
void Write_DS18B20(unsigned char dat)
{
unsigned char i;
for(i=0;i<8;i++)
{
DQ = 0;
DQ = dat&0x01;
Delay_OneWire(5);
DQ = 1;
dat >>= 1;
}
Delay_OneWire(5);
}
//
unsigned char Read_DS18B20(void)
{
unsigned char i;
unsigned char dat;
for(i=0;i<8;i++)
{
DQ = 0;
dat >>= 1;
DQ = 1;
if(DQ)
{
dat |= 0x80;
}
Delay_OneWire(5);
}
return dat;
}
//
bit init_ds18b20(void)
{
bit initflag = 0;
DQ = 1;
Delay_OneWire(12);
DQ = 0;
Delay_OneWire(80);
DQ = 1;
Delay_OneWire(10);
initflag = DQ;
Delay_OneWire(5);
return initflag;
}
unsigned int read_temp(void)
{
unsigned char low=0;
unsigned char high=0;
unsigned int temp=0;
init_ds18b20();
Write_DS18B20(0xCC);
Write_DS18B20(0x44);
Delay_OneWire(200);
init_ds18b20();
Write_DS18B20(0xCC);
Write_DS18B20(0xBE);
low=Read_DS18B20();
high=Read_DS18B20();
temp=high;
temp&=0x0F;
temp<<=8;
temp|=low;
temp>>=4;
return temp;
}
onewire.h
#ifndef _ONEWIRE_H_
#define _ONEWIRE_H_
unsigned int read_temp(void);
#endif
iic.c
/* # I2C代码片段说明
1. 本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
2. 参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
中对单片机时钟频率的要求,进行代码调试和修改。
*/
#define DELAY_TIME 5
#include <stc15.h>
#include <intrins.h>
#include "iic.h"
sbit scl=P2^0;
sbit sda=P2^1;
//
static void I2C_Delay(unsigned char n)
{
do
{
_nop_();_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();_nop_();
}
while(n--);
}
//
void I2CStart(void)
{
sda = 1;
scl = 1;
I2C_Delay(DELAY_TIME);
sda = 0;
I2C_Delay(DELAY_TIME);
scl = 0;
}
//
void I2CStop(void)
{
sda = 0;
scl = 1;
I2C_Delay(DELAY_TIME);
sda = 1;
I2C_Delay(DELAY_TIME);
}
//
void I2CSendByte(unsigned char byt)
{
unsigned char i;
for(i=0; i<8; i++){
scl = 0;
I2C_Delay(DELAY_TIME);
if(byt & 0x80){
sda = 1;
}
else{
sda = 0;
}
I2C_Delay(DELAY_TIME);
scl = 1;
byt <<= 1;
I2C_Delay(DELAY_TIME);
}
scl = 0;
}
//
unsigned char I2CReceiveByte(void)
{
unsigned char da;
unsigned char i;
for(i=0;i<8;i++){
scl = 1;
I2C_Delay(DELAY_TIME);
da <<= 1;
if(sda)
da |= 0x01;
scl = 0;
I2C_Delay(DELAY_TIME);
}
return da;
}
//
unsigned char I2CWaitAck(void)
{
unsigned char ackbit;
scl = 1;
I2C_Delay(DELAY_TIME);
ackbit = sda;
scl = 0;
I2C_Delay(DELAY_TIME);
return ackbit;
}
//
void I2CSendAck(unsigned char ackbit)
{
scl = 0;
sda = ackbit;
I2C_Delay(DELAY_TIME);
scl = 1;
I2C_Delay(DELAY_TIME);
scl = 0;
sda = 1;
I2C_Delay(DELAY_TIME);
}
unsigned char read_pcf(unsigned char add)
{
unsigned char ad=0;
I2CStart();
I2CSendByte(0x90);
I2CWaitAck();
I2CSendByte(add);
I2CWaitAck();
I2CStop();
I2CStart();
I2CSendByte(0x91);
I2CWaitAck();
ad=I2CReceiveByte();
I2CSendAck(1);
I2CStop();
return ad;
}
iic.h
#ifndef _ONEWIRE_H_
#define _ONEWIRE_H_
unsigned int read_temp(void);
#endif
ds1302.c
/* # DS1302代码片段说明
1. 本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
2. 参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
中对单片机时钟频率的要求,进行代码调试和修改。
*/
#include <stc15.h>
#include <intrins.h>
#include "ds1302.h"
sbit RST=P1^3;
sbit SCK=P1^7;
sbit SDA=P2^3;
unsigned char Time[3]={55,59,23};
//
void Write_Ds1302(unsigned char temp)
{
unsigned char i;
for (i=0;i<8;i++)
{
SCK = 0;
SDA = temp&0x01;
temp>>=1;
SCK=1;
}
}
//
void Write_Ds1302_Byte( unsigned char address,unsigned char dat )
{
RST=0; _nop_();
SCK=0; _nop_();
RST=1; _nop_();
Write_Ds1302(address);
Write_Ds1302(dat);
RST=0;
}
//
unsigned char Read_Ds1302_Byte ( unsigned char address )
{
unsigned char i,temp=0x00;
RST=0; _nop_();
SCK=0; _nop_();
RST=1; _nop_();
Write_Ds1302(address);
for (i=0;i<8;i++)
{
SCK=0;
temp>>=1;
if(SDA)
temp|=0x80;
SCK=1;
}
RST=0; _nop_();
SCK=0; _nop_();
SCK=1; _nop_();
SDA=0; _nop_();
SDA=1; _nop_();
return (temp);
}
void ds_init(void)
{
unsigned char i=0,add=0x80;
Write_Ds1302_Byte(0x8E,0x00);
for(;i<3;i++)
{
Write_Ds1302_Byte(add,(Time[i]/10<<4)|(Time[i]%10));
add+=2;
}
Write_Ds1302_Byte(0x8E,0x80);
}
void read_ds(void)
{
unsigned char i=0,add=0x81,dat=0;
for(;i<3;i++)
{
dat=Read_Ds1302_Byte(add);
Time[i]=(unsigned char )(dat/16*10+dat%16);
add+=2;
}
}
ds1302.h
#ifndef _DS_1302_H_
#define _DS_1302_H_
void ds_init(void);
void read_ds(void);
extern unsigned char Time[3];
#endif
七、最后三十分钟
对的,你是不是忘了什么,选择题还没写呢。记得把选择题写了
什么?为什么最后才写选择题,别问,当你体验过考场最后那一段时间的时候你就知道了。
另外,你完成前边的题目也不一定要用4.5个小时。
如果你把题目都写完了,现在不妨拿出自己带的面包和水或是饮料,伸个懒腰,想一想等会儿哪儿还有卖饭的,一会要吃啥吧。
如果大家有问题欢迎留言或者私信。
这里先提前预祝各位榜上有名了!