蓝桥杯电子类单片机组赛场代码思路分享

目录

前言

一、开考前30分钟

二、拿到考卷后

三、显示部分实现

1.第十四届题目

2.显示部分框架实现

四、按键功能的实现

1.功能要求

2.基本功能实现

到这里,你已经保底省三了

五、赛题要求功能的实现

1.湿度转化

2.采集触发

1)亮暗状态判断

2)触发采集

3)界面切换

写到现在,省二基本就有了

3.回显数据处理

1)温度回显

2)湿度回显

3)时间回显

4)完整的触发采集代码

4.对采集次数为0时的处理

5.采集到无效数据的处理

1)判断湿度无效

2)处理湿度无效

6.长按功能的实现

7.LED灯功能的实现

写到这里,几乎已经是省一了

六、最后一小时

1.程序源代码分享

mian.c

onewire.c

onewire.h

 iic.c

 iic.h

 ds1302.c

 ds1302.h

七、最后三十分钟


 

 

 

前言

要说单片机能考的知识点,那真是太多了,随着你对蓝桥杯了解的深入,你越来越会感到,各个模块我都能完成,但是把他们串一起时,思路就很混乱;或者感到这个题目我会,但是代码却是写了又删,删了又写,不断创造问题又解决问题,到头来也能完成比赛要求,但是会花费很多时间,这还是在不紧张的情况下,一到考场,用的不是自己的电脑,那更是大脑一片空白。

如果你省赛题目基本能完成,那你看看国赛题目,没准就会出现我上边说的情况,什么都会,但是写起来需要花费很多时间,那今天,我就分享一下我写代码的思路,以及如何应对蓝桥杯比赛。

长文制作不易,如果感觉或者以后想报单片机组,就点赞加关注吧!

本篇文章以第十四届省赛题目为例,文章顺序就是我写代码的顺序。

一、开考前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.000MHz

unsigned 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分钟写的代码是否有误。比赛时一定要步步为营

然后看赛题,先看程序框图,本篇都以十四届题目为例

c6d2b58fbf0041c5a844f6e50bf67607.png

程序框图上会告诉我们本次题目都涉及到哪些部分的内容,尤其是底层相关的部分,我们要先把底层代码拷到工程里,然后完成相关部分的实现。比如从上图十四届比赛的程序框图可以看出,我们需要使用NE555和DS18B20和PCF8591,并且PCF8591是双向箭头,所以说需要AD读取和DA输出(第一眼看确实是认为有AD输入和DA输出,毕竟是双向箭头嘛,可结果是只用到了AD输出,DA输出写了没用到,不过问题不大)。这个时候你应该先把NE555和PCF8591以及DS18B20相关的代码完成,并在main函数进行测试,完成之后再往下看显示部分。

三、显示部分实现

1.第十四届题目

这里以第十四届省赛题目为例

b34204727f434647bf962efca1555133.pngc1068995748c49f19010b6a9ff5c5f4a.pngc20663b619194064af1cbd50e8de479c.png3d3968fab7e34c79b32b65052eec73c3.pngc8db757ff26f40598d4fa3213c885e0e.png

2.显示部分框架实现

不需要看整个题目,不需要理清题目要求,咱们看到哪里写到哪里,题目要求显示六个菜单,那咱们就把六个菜单写出来,至于菜单中需要显示的数据,我们有的就写。具体到这个赛题,我们只有第一个显示菜单,时间部分已经在你拿到考卷之后就已经写好了。

68161c4f6aae468b813f1325baa42bc8.png9e906963dd0e48baa2fa47d8baf88d1d.png

菜单中其他没有的数据,我们也不着急定义变量,题目上给的示例写的是什么数据,我们就暂时让他显示什么数据,显示一个死的数据(因为我说实话,比赛的时候,有几个数据可能根本不知道是什么意思,从哪里来的,索性就先不处理)。比如温度回显显示的是温度和最大温度,我们可以不着急定义变量,题目上示例写的最大温度是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.功能要求

显示完菜单之后,题目下边就是按键了,第十四届比赛显示部分是采集触发,后边才是按键,我们先不管采集触发,先完成按键功能:

3de297f0d84d4ee28fbe717dfdad595e.png

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.湿度转化

735541760b744b98946e37bb8baa9bb3.png

湿度是通过频率转化而来的。这种代码几行就能搞定,随便找个地方,比如读取频率的函数内将频率转化成湿度就好了,注意限幅。(透露一点,其实下文提到了对湿度读取到无效值的处理,这里默认大家还不知道对这方面的要求,就先认为读取到无效数据时,湿度值为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.采集触发

c9636d682f1d49e99405302e2f73a896.png

题目要求,当检测到从暗到亮触发一次采集,然后切换到温湿度显示菜单,温湿度显示菜单刚才定义过了,3S后返回功能大家到现在应该也都有自己的处理方法了,不清楚的也可查看我之前发的一些文章。

由于采集的是温度和湿度,湿度是由NE555转化而来的,而我们都知道,我们每次运行读取温度函数,实际上读取到的温度是上次运行温度读取函数时(甚至是上上次,上上上次)温度读取函数读取出来的温度,而NE555的读取也是要1S时间才能读取,在不到1S的时间内,频率值等于上秒NE555读取到的频率。也就是说,这两个东西都不能瞬时读取,那怎么办呢?

其实在第十四届代码讲解中也已经说过了,因为这个赛题需要的单片机资源也不多,所以我们可以一直读取温度和频率,当触发读取之后,就记录一下当前的温度和频率就好了。温度读取和频率读取我们放在main函数中,频率读取还跟之前一样,由定时器计时,每隔1s读取一次。

至于采集触发,我们就放在AD读取函数内,当检测到从暗到亮,则将采集标志位置为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)触发采集

10fca26ba4d541e5ba501bb2c3ff2657.png

上边已经读取到的亮暗状态,接下来只要检测到由亮到暗,就触发一次采集。
前边也提到,我们是实时读取数据的,比如实时读取的温度值记为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)界面切换

54d7a61f4b614f02ad0e6351ee9de97f.png

我们已经可以检测到亮暗变化并触发一次采集了,现在只需要切换一次菜单即可。切换到温湿度界面之后,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)温度回显

fbba76cb3a0a4e269ba98e38be48baf2.png

很简单,温度回显数据只需要两个数据,一个是最大温度,一个是平均温度。


最大值

最大温度好处理,定义一个最大温度max_temp,如果now_temp>max_temp,则max_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)湿度回显

f1a2ee38e7ff48da8b6c94187d24af12.png

与温度回显一毛一样,这里不再过多介绍。直接多定义几个变量,然后CV一下代码改一下变量就好了。

if(max_shidu<now_shidu)//当前湿度大于记录的最大湿度
    max_shidu=now_shidu;//则更新最大湿度数据
av_shidu=(av_shidu*times+now_shidu*10)/(times+1);//计算平均湿度数据,注意这里的平均湿度扩大了十倍

注意在处理完温度和湿度数据后,要把times++

3)时间回显

14901f6f20234cada19c796796dad786.png

我们可以如法炮制地完成这个部分,同样是定义两个变量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时的处理

63161a10835d4c03adcd440b29112dff.png

虽然只有两行,但是题目要求了可千万不能丢。

要说这个处理,我想大家应该都能很好的完成,之前已经定义了times,只需要在菜单显示代码中加上如果times等于0则显示啥啥啥,否则则显示正常的数据即可。这里就不再赘述了。

5.采集到无效数据的处理

ca8324e8831647ec9afacb99589023cd.png

对这个的处理可以在一开始完成触发采集的界面切换时就完成,不过现在完成也不晚。这个主要是针对湿度数据,在湿度转化那一节,我们就可以检测到读取到的湿度是否有效。我们定义一个标志位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;

当然,题目上最开始说的是频率的无效区间,你也可以不判断湿度,直接判断频率,思路和写法都一样,这里就不再解释了

4695e8e18e3a419fb1f3f0fda712c837.png

2)处理湿度无效

我们在触发采集那里,读取now_is_shidu_youxiao=is_shidu_youxiao,并判断now_is_shidu_youxiao的值,如果为0,也就是说采集到了无效数据,则不记录采集次数,不对最高温度,平均温度,最高频率,平均频率,采集时,采集分进行更新,并且数码管对应地方显示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.长按功能的实现

833ec52ffc3742d8936078fd220cae62.png

时间回显子界面长按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灯功能的实现

e0f7791d20c941a18d86d478a8ee6dc0.png

41ef6d24e6544e7896def579ee58a25f.png

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;
    }
}

写到这里,几乎已经是省一了

六、最后一小时

当然稳定拿省一的前提是前边数据处理的都没问题哈,其实有一点问题也不大,因为功能基本实现了。但是现在比赛还没结束,活儿还没干完。比如:

d4e7cb7ea36d4003870a211f4c70909d.png

比如

6e74ee65daec4165b49eaf7a6e6e8601.png

比如:

3990998bf95d4b889245aa4d81f971ff.png

还有:

260f6f5b9c0e4195bd168f412cd40985.png

这些东西可都不能忘了。题目写完记得打包提交。

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个小时呀。

如果你把题目都写完了,现在不妨拿出自己带的面包和水或是饮料,伸个懒腰,想一想等会儿哪儿还有卖饭的,一会要吃啥吧。

如果大家有问题欢迎留言或者私信。

这里先提前预祝各位榜上有名了!

 

  • 24
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值