当前位置:   article > 正文

蓝桥杯第十四届电子类单片机组程序设计_csdn蓝桥杯十四

csdn蓝桥杯十四

目录

前言

蓝桥杯大赛历届真题(点击查看)

一、第十四届比赛题目

1.比赛原题

2.题目解读

1)任务要求

2)注意事项

二、任务实现

1.NE555读取时机的问题

1)缩短计数时间

2)实时读取

2.温度传感器读取时机的问题

3.由亮变暗的检查

4.按键长按/短按

5.平均数和最大值的处理

6.对于小数的处理

三、代码实现

main.c

onewire.c

onewire.h

iic.c

iic.h

ds1302.c


前言

之前也不止一次提到,随着蓝桥杯蓝桥杯知名度越来越高,它省赛题目也一年比一年难,第十四届蓝桥杯省赛题目,可以说是这些省赛题目当中最难得了,个人感觉已经赶上国赛水平了。这其中也用到了许多对问题的处理方法,本篇文章会择其进行介绍,当然,条条大路通罗马,我通向罗马的道路也仅供参考,也并非绝对。

蓝桥杯第十四届省赛题目可以在蓝桥杯官网找到

蓝桥杯大赛历届真题(点击查看)

一、第十四届比赛题目

1.比赛原题

2.题目解读

说实话,5页的题目,5个数码管菜单,真的让人看到眼花缭乱,下面是我列出来的比赛的一些要求完成的任务以及注意事项,仅供帮助大家理解,具体要求还得看赛题。

1)任务要求

  • 定义湿度,使用频率表示湿度(将频率转化为湿度),注意范围
  • PCF8591读取光敏电阻电压,当检测到由“亮”到“暗”时,读取一次温度和湿度并记录,读取一次当前的时间并记录。
  • 计算最近读取的若干次温度和湿度的平均值(保留一位小数)和最大值
  • 显示菜单0:显示当前的时间
  • 显示菜单1 2 3:回显记录的数据,共三个子菜单,分别显示温度,湿度和上一次读取数据时的时间以及总共读取数据的次数
  • 显示菜单4,显示温度参数(具体作用下文会提)
  • 每当触发一次读取(参看第二条),界面跳转到温湿度显示界面,显示读取到的温湿度值,3s后再跳回原菜单,期间所有按键失效,并且不再触发读取
  • 如果读取到的湿度数据为无效数据(参看第一条),则湿度显示“AA”,该次读取的数据不进行计算,即把该次读取到的温湿度值,算到平均温湿度值以及最高温湿度值里,读取到有效次数也不+1
  • 按键s4,s4在除了温湿度界面均有效,可以在时间界面,数据回显以及参数设置界面之间跳转
  • 按键s5,s5仅在数据回显的子菜单内有效,用于在三个子菜单之间切换
  • 按键s8,s8仅在参数界面有效,按下s8,温度参数+1
  • 按键s9,s9仅在参数界面和时间数据回显界面(数据回显的子菜单)下有效。在参数界面,按下s9,温度参数-1;在数据回显界面长按2s以上s9,松开s9后所有记录的数据清空。
  • LED灯:在时间界面,led1点亮,否则熄灭;在数据回显界面,led2点亮,否则熄灭;在温湿度显示界面,led3点亮,否则熄灭
  • 如果当前采集到的温度值高于温度参数,则led4以100ms为间隔闪烁,否则熄灭;如果当前采集到的湿度数据为无效数据,则led5点亮,直到采集到有效数据;如果当前采集到的温度数据和湿度数据比上一次的数据都高(至少采集两组数据才算),则led6点亮,否则熄灭
  • led7熄灭

2)注意事项

  • 上电时处在时间界面,温度参数设置为30(初始时间呢?好像没说设置多少哎)
  • 注意湿度的有效值,如果读取到的湿度值为无效值,则该次读取到的温度湿度和时间不参与计算(或存储),但是还是得显示
  • 如果一次没有读取数据或者一次有效数据都没有读取到,则时间回显子界面的时、间隔、分显示位置熄灭;温度、湿度回显子界面除界面标识符外的其它位熄灭。
  • 其它相关的注意事项

二、任务实现

对于此项赛题,有许多需要处理的问题:

1.NE555读取时机的问题

之前的NE555都是记录1s内,电平的变化次数,这个次数就刚好是频率。但是如果我们每次都是接收到由亮到暗的信号之后,再读取NE555的话,会花费超过1s的时间,不符合题目要求的<0.5s的灵敏度。因此要读取NE555,可以进行以下两种处理方法,这里只说思路:

1)缩短计数时间

频率是1s内电平变化的次数,我们之前记录1s内电平变化的次数也正因如此。但是现在我们没有1s时间去读取NE555,我们可以缩短读取电平的时间,比如每隔200ms读取一次这一段时间内电平变化的次数,然后再乘5就是频率。这种方法可以提高读取的响应速度,但是会失去更多精度,具体的实现方法相信已经阅读过关于NE555的小伙伴们都可以轻松实现。

2)实时读取

虽然题目要求的是,接收到由亮变暗的信号之后读取一次NE555,但我们可以不那么“老实”,我们可以一直都在读取NE555,当收到由亮变暗的信号之后,在用其他变量记录此时NE555读取出来的值即可,虽然这种方法会浪费更多的单片机算力,需要添加更多的变量,但是读取的NE555更加及时。后面的代码中也都用的这种方法。

2.温度传感器读取时机的问题

与NE555类似,温度传感器也要考虑读取时机的问题。咱们之前提到温度传感器的温度转化需要时间,而且需要的时间不短,如果我们在温度转化完成之前就读取温度,那我们只能读取到上一次温度转化出来的结果。而我们使用的读取温度传感器的函数,恰恰是这样的,每调用一次函数,发送一次温度转化的指令并读取一次温度,也就是说,我们需要连续调用温度传感器,或者调用一次读取温度的函数之后,等500ms甚至更久之后,在重新读取才能读取到有效的数据。这就跟NE555一样了,解决方法也类似,可以一直读取温度传感器,当接收到由亮变暗的信号之后,我们再用新的变量接受当前温度传感器的值,同样需要mcu由更多的开销,同样需要定义更多的变量,不过这些都无足轻重。具体方法跟NE555处理方法类似,就不在赘述了。

当然,这个问题是跟着我之前写的代码完成赛题的话才会遇到的问题,也并非所有人写的代码都需要考虑这个问题。

3.由亮变暗的检查

由亮变暗触发读取(通过AD转化器读取光敏电阻),或者前几年有个赛题出的,当前电压从高过阈值到低于阈值则记录一次之类的题目都类似,这里先介绍一下思路:

由亮到暗这个过程的第一步,就是当前环境是“亮”,在某一时刻环境又变到了“暗”,我们定义一个变量is_Liang来记录这个状态,is_Liang=1表示当前环境是“亮”,is_Liang=0表示当前环境为“暗”。当is_Liang==1时,我们只需检查当前实时读取的AD值是否小于某个阈值,如果小于,则判断“由暗到亮”,并更新is_Liang状态位。同理,如果当前环境为“暗”,AD又高于某个值,说明进行了从暗到亮的跳变。当然,为了使亮暗分明,我们可以把由亮到暗的阈值设置的低于由暗到亮的阈值:

if(is_Liang==1)//如果当前正处在“亮”状态
    {
        if(ad<70)//如果检测到光敏电阻的AD小于70,就尝试判断为进入“暗”状态,从亮到暗触发一次采集。(当然这个70的阈值可以自己设置,尽量设置低一些)
        {
            if(++change_200>200)//连续200次进入定时器,都处在暗状态,说明真的暗了(题目要求响应时间小于500ms,这个精度没毛病)
            {
                change_200=0;
                is_Liang=0;
                is_cai_ji=1;//触发一次采集
            }
        }
        else if(change_200>0)//不满足条件,就当什么都没发生
        {
            change_200=0;
        }
    }
    else if(is_Liang==0&&mod!=5)//如果正处在“暗”状态
    {
        if(ad>100)//与从亮到暗同理,注意两个阈值尽量不要设置的一样,设置一样了不好处理
        {
            if(++change_200>200)
            {
                change_200=0;
                is_Liang=1;
            }
        }
        else if(change_200>0)
        {
            change_200=0;
        }
    }

我是使用定时器1来完成上述代码的,为了防止出现误判,也为了提高判断的时间,这里是连续200ms内,都满足条件才判断状态改变。

至于“每从高于阈值到低于阈值记录一次”,其实与亮暗跳变一致。

4.按键长按/短按

这个也是一个最近几年经常考的问题,这个的处理方法很多,我只介绍我自己会用到一种。

第七届比赛时已经介绍了一种led灯闪烁的方法,我们稍加改进就可以实现记录按下去多长时间的功能,当按下去的时间超过某个值,我们就视为长按。题目要求的是按下按键2s以上就视为长按,所以我们定义一个标志位is_2s,跟之前一样,当is_2s等于0时,2s之后会被定时器置为1.我们再while(P32==0)之前将is_2s置为0,当跳出while循环时,判断is_2s是否为1,为1,说明按下按键这段时间超过2s了,我们就可以对其进行长按按键的处理,可以是修改其他的标志位,也可以直接把长按的功能在这里实现,为0说明没有长按,就直接跳过了。

if(P32==0)
  {
      Delay5ms();
      is_2s=0;//长按功能,按下按键之后,is_2s被置为0,2s之后is_2s被置为1,如果2s之后还没松手(此时is_2s为1,则视为长按)
      while(P32==0){run();}
      if(mod==3&&is_2s==1)//is_2s标志位为1,说明从is_2s清零到现在已经过去2s了,也就是已经长按按键2s了
      {
               //长按
      }
      Delay5ms();
      key_value=9;
}

is_2s为0时,2s后会在1ms的定时器内被置为1

if(is_3s==0)//数3s
{
    if(++count_3s>3000)
    {
        is_3s=1;//数够3s的标志位
        count_3s=0;
    }

 }

5.平均数和最大值的处理

平均数和最大值的处理其实也很简单,不过如果第一次写或者没看懂题目要求,以为需要记录每次读取的数据时,确实也会手足无措,会想着定义一个指针,怎么怎么存储这些数据,其实题目并没有要求可以读取到每次记录的数据,只要求记录每次读取数据的平均值和最大值即可。我们只需要知道已经读取了几次数据、之前记录的平均值、这次读取到的数据就可以计算出新的平均值:

新的平均值=(旧的平均值*已经读取到的有效数据数+这次读取到的数据)/(已经读取到的有效数据数+1)

最大值只需要比较当前最大值与当前读取到的数据谁大,当前读取到的数据大,新的最大值等于当前读取到的数据,当前最大值大,新的最大值就是当前最大值

这个已经读取到的有效数据数可以根据自己的代码修改,可能需要加一减一之类的
Now_temp=temp;//更新温度
arr_temp=(arr_temp*ci_shu+Now_temp*10)/(ci_shu+1);//平均温度。扩大了10倍,便于读取小数点后1位
max_temp=max_temp>Now_temp? max_temp:Now_temp;//更新最高温度

last_shi_du=Now_shi_du;//记录上一次的湿度值
Now_shi_du=shi_du;//更新湿度
arr_shi_du=(arr_shi_du*ci_shu+Now_shi_du*10)/(ci_shu+1);//平均湿度。扩大了10倍,便于读取小数点后1位
max_shi_du=max_shi_du>Now_shi_du? max_shi_du:Now_shi_du;//更新最大湿度

这里算均值的时候,由于题目要求精确到小数点后一位,于是就算了10倍的均值,后续处理时,在缩小十倍,就可以符合题目要求了。

6.对于小数的处理

一听到小数,我们就会想到float,但是,对于定义一个变量都不用int,连char都不用,还得用unsigned char的keil来说,我们更倾向于它是一个整数,而且还得是正整数。所以我们就想到把这个数字扩大10倍,显示的时候,只需要在最后一位之前加一个小数点即可,而且使用整数时也便于我们后续的处理。一般资源数据包会给我们0到9的断码表,我们再写出0.到9.的断码表,使得Seg_Table[5]是显示5,Seg_Table[5+10]显示5.(注意是5加上“.”)。对此次省赛的处理中,所有平均值都是要求精确到小数点后一位,所以在计算时都是把平均值扩大十倍,在显示时多显示一个小数点。

三、代码实现

这次用到了onewire iic和ds1302,其实onewire.c iic.c和ds1302.c里的内容,每次都是一样的,顶多函数名字叫的不一样,只会比之前写的模板少,不会多(所以底层驱动里的注释就不写了,不清楚的可以看对应的之前写过的文章)。

main.c

  1. #include <stc15.h>
  2. #include <intrins.h>
  3. #include "onewire.h"
  4. #include "iic.h"
  5. #include "ds1302.h"
  6. code unsigned char Seg_Table[] =
  7. {
  8. 0xc0, //0
  9. 0xf9, //1
  10. 0xa4, //2
  11. 0xb0, //3
  12. 0x99, //4
  13. 0x92, //5
  14. 0x82, //6
  15. 0xf8, //7
  16. 0x80, //8
  17. 0x90, //9
  18. 0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10,//0-9加上小数点
  19. //0. 1. 2. 3. 4. 5. 6. 7. 8. 9.
  20. 0xFF,//20 熄灭
  21. 0xBF,//- 21
  22. 0xC6,//C 22
  23. 0x89,//H 23
  24. 0x8e, //F 24
  25. 0x8C, //P 25
  26. 0x86, //E 26
  27. 0x88 //A 27
  28. };
  29. unsigned char Led_Num=0xFF;
  30. #define LED_ON(x) Led_Num&=~(0x01<<x);P0=Led_Num;P2|=0x80;P2&=0x9F;P2&=0x1F;
  31. #define LED_OFF(x) Led_Num|=0x01<<x; P0=Led_Num;P2|=0x80;P2&=0x9F;P2&=0x1F;
  32. #define LED_OFF_ALL() Led_Num=0xFF; P0=0xFF;P2|=0x80;P2&=0x9F;P2&=0x1F;
  33. #define NIXIE_CHECK() P2|=0xC0;P2&=0xEF;P2&=0x1F;
  34. #define NIXIE_ON() P2|=0xE0;P2&=0xFF;P2&=0x1F;
  35. void Timer0_Init(void);//定时器0用于NE555
  36. void Timer1_Init(void); //1毫秒@12.000MHz
  37. void Delay100ms(void); //@12.000MHz
  38. void get_key(void);//读取按键函数
  39. void show_menu(void);//显示菜单函数
  40. void run(void);//(主)运行程序函数
  41. void led_run(void);//LED灯控制函数
  42. unsigned char Nixie_num[8]={20,20,20,20,20,20,20,20};//数码管待显示的数据
  43. unsigned char location=0;//当前数码管扫描到的位置,中间变量
  44. unsigned char key_value=0;//读取到的键值,中间变量
  45. unsigned int temp=0;//实时读取到的温度值
  46. unsigned char ad=0;//实时读取到的AD转化值
  47. unsigned int f=0;//频率的中间变量
  48. unsigned int fre=0;//定义频率
  49. unsigned char mod=0;//定义菜单模式,取值范围0到5,0:时钟,1 2 3回显数据 4:参数设置, 5:温湿度显示
  50. unsigned int shi_du=0;//定义湿度
  51. unsigned char ci_shu=0;//读取到有效数据的次数
  52. unsigned int Now_temp=0;//当前记录的温度值(温度一直在读取,但不是实时记录,因此会产生许多中间的变量,下同)
  53. unsigned int Now_shi_du=0;//当前记录的湿度值
  54. unsigned char Now_time[3];//记录读取到有效数据的时间(秒分时)
  55. unsigned int arr_temp=0;//平均温度,为方便保留一位小数,这里在计算时扩大了十倍
  56. unsigned int arr_shi_du=0;//平均湿度,处理同上
  57. unsigned int max_temp=0;//记录的最高温度
  58. unsigned int max_shi_du=0;//记录的最高湿度
  59. unsigned int temp_canshu=30;//温度参数,默认为30
  60. unsigned int last_temp=0;//上一次记录的温度(主要用于当前数据与上一次数据比较,以控制led6
  61. unsigned int last_shi_du=0;//上一次记录的湿度
  62. unsigned int wrong_temp;//记录当读取到的湿度值无效时的温度值,只读取,不处理或储存
  63. bit shi_du_is_you_xiao=0;//实时读取的湿度值是否有效,1:有效,0:无效
  64. bit is_cai_ji=0;//采集标志位,为1时采集一次数据
  65. bit is_3s=1;//记录进入菜单5之后,3s后返回原菜单的标志为
  66. bit is_2s=1;//记录按下s9长按2s的标志位
  67. bit shidu_wuxiao=0;//当前读取的湿度是否有效,1:无效,0:有效
  68. bit is_up=0;//当前读取的温度和湿度均比上一次高,1:均比上一次高,0:不是均比上一次高,用于控制Led6
  69. void main()
  70. {
  71. LED_OFF_ALL();
  72. read_ds();
  73. ad=read_pcf(1);//光敏电阻在通道1
  74. ds1302_init();
  75. Timer0_Init();
  76. Timer1_Init();
  77. EA=1;
  78. Delay100ms();
  79. while(1)
  80. {
  81. get_key();
  82. run();
  83. Delay100ms();
  84. }
  85. }
  86. unsigned char last_mod=0;
  87. void run()
  88. {
  89. show_menu();//显示菜单
  90. led_run();//控制LED灯
  91. ad=read_pcf(1);//实时读取AD
  92. read_time();//实时更新时间
  93. temp=read_ds();
  94. temp=temp<99?temp:99;//如果读取到的温度值高于99,则令读取到的温度值为99(提上要求的,实际上应该达不到,除非仿真)
  95. if(is_cai_ji==1)//采集标志位为1,采集一次数据
  96. {
  97. is_cai_ji=0;
  98. if(shi_du_is_you_xiao==1)//只有当实时读取的湿度值为有效值时,才进行记录和除了
  99. {
  100. shidu_wuxiao=0;
  101. last_temp=Now_temp;//记录上一次的温度值
  102. Now_temp=temp;//更新温度
  103. arr_temp=(arr_temp*ci_shu+Now_temp*10)/(ci_shu+1);//平均温度。扩大了10倍,便于读取小数点后1位
  104. max_temp=max_temp>Now_temp? max_temp:Now_temp;//更新最高温度
  105. last_shi_du=Now_shi_du;//记录上一次的湿度值
  106. Now_shi_du=shi_du;//更新湿度
  107. arr_shi_du=(arr_shi_du*ci_shu+Now_shi_du*10)/(ci_shu+1);//平均湿度。扩大了10倍,便于读取小数点后1位
  108. max_shi_du=max_shi_du>Now_shi_du? max_shi_du:Now_shi_du;//更新最大湿度
  109. Now_time[0]=time[0];Now_time[1]=time[1];Now_time[2]=time[2];//记录读取数据时的时间
  110. ci_shu++;//读取到有效数据的次数++
  111. /*如果读取到了至少两次有效数据,并且当前读取的温度和湿度都比上一次高,则给点亮Led6的标志位is_up置1,否则置0*/
  112. if(Now_temp>last_temp&&Now_shi_du>last_shi_du&&ci_shu>=2)
  113. {
  114. is_up=1;
  115. }
  116. else if(Now_temp<=last_temp||Now_shi_du<=last_shi_du||ci_shu>=2)
  117. {
  118. is_up=0;
  119. }
  120. }
  121. else//没有读取到有效数据,只读取数据,不记录和处理
  122. {
  123. wrong_temp=temp;//无效的温度数据(只用于数码管显示)
  124. shidu_wuxiao=1;
  125. }
  126. last_mod=mod;//受到读取数据的指令之后,要跳转到温湿度界面,跳转完3s还要再跳回来,所以要记录是从哪个菜单跳出去的
  127. mod=5;//跳转到温湿度显示界面
  128. is_3s=0;//清零3s计时
  129. }
  130. else if(mod==5&&is_3s==1)//3s过后
  131. {
  132. mod=last_mod;//重新跳回刚才的菜单
  133. }
  134. }
  135. unsigned int count_1s=0;//数1s,中间变量
  136. bit is_Liang=1;//明暗标志位,is_Liang==1,当前环境为亮。is_Liang==0,当前环境为暗,中间变量
  137. unsigned int change_200=0;//连续200次进入定时器,其亮暗情况均与亮暗标志位不符合,则改变亮暗标志位(其实也就对应main读取两次AD,非必要)
  138. unsigned int count_3s=0;//数3s,用于3s后跳转菜单,中间变量
  139. unsigned int count_2s=0;//数2s,用于数长按2s按键,中间变量
  140. void Timer1_Isr(void) interrupt 3
  141. {
  142. /*数码管显示*/
  143. P0=0x01<<location;NIXIE_CHECK();
  144. P0=Seg_Table[Nixie_num[location]];NIXIE_ON();
  145. if(++location==8)
  146. location=0;
  147. /*NE555读取,每隔1s读取一次,读取到的值就是频率*/
  148. if(++count_1s==1000)
  149. {
  150. count_1s=0;
  151. fre=f;//读取频率
  152. f=0;
  153. if(fre>=200&&fre<=2000)//读取的频率有效
  154. {
  155. shi_du_is_you_xiao=1;//实时读取的频率是否有效标志位,注意与当前读取的频率是否有效标志位shidu_wuxiao进行区分
  156. shi_du=(unsigned int)(4*fre/90+10/9);//将频率转化为湿度(方程需要自己拟合)
  157. }
  158. else//读取的频率无效
  159. {
  160. shi_du_is_you_xiao=0;
  161. }
  162. }
  163. if(is_Liang==1&&mod!=5)//如果当前正处在“亮”状态,且没有在温湿度显示界面(题目要求处在温湿度显示界面的3s内,不重复读取)
  164. {
  165. if(ad<70)//如果检测到光敏电阻的AD小于70,就尝试判断为进入“暗”状态,从亮到暗触发一次采集。(当然这个70的阈值可以自己设置,尽量设置低一些)
  166. {
  167. if(++change_200>200)//连续200次进入定时器,都处在暗状态,说明真的暗了(题目要求响应时间小于500ms,这个精度没毛病)
  168. {
  169. change_200=0;
  170. is_Liang=0;
  171. is_cai_ji=1;//触发一次采集
  172. }
  173. }
  174. else if(change_200>0)//不满足条件,就当什么都没发生
  175. {
  176. change_200=0;
  177. }
  178. }
  179. else if(is_Liang==0&&mod!=5)//如果正处在“暗”状态
  180. {
  181. if(ad>100)//与从亮到暗同理,注意两个阈值尽量不要设置的一样,设置一样了不好处理
  182. {
  183. if(++change_200>200)
  184. {
  185. change_200=0;
  186. is_Liang=1;
  187. }
  188. }
  189. else if(change_200>0)
  190. {
  191. change_200=0;
  192. }
  193. }
  194. if(is_3s==0)//数3s
  195. {
  196. if(++count_3s>3000)
  197. {
  198. is_3s=1;//数够3s的标志位
  199. count_3s=0;
  200. }
  201. }
  202. if(is_2s==0)
  203. {
  204. if(++count_2s>2000)
  205. {
  206. is_2s=1;//数够2s的标志位
  207. count_2s=0;
  208. }
  209. }
  210. }
  211. void Timer0_Isr(void) interrupt 1
  212. {
  213. f++;
  214. }
  215. void Timer0_Init(void)
  216. {//可以从stc-isp的范例程序里抄
  217. AUXR = 0x80; //定时器0为1T模式
  218. TMOD = 0x04; //设置定时器0为16位自动重装载外部记数模式
  219. TH0 = TL0 = 0xff; //设置定时器0初始值
  220. TR0 = 1; //定时器0开始工作
  221. ET0 = 1; //开定时器0中断
  222. }
  223. void Timer1_Init(void) //1毫秒@12.000MHz
  224. {
  225. AUXR |= 0x40; //定时器时钟1T模式
  226. TMOD &= 0x0F; //设置定时器模式
  227. TL1 = 0x20; //设置定时初始值
  228. TH1 = 0xD1; //设置定时初始值
  229. TF1 = 0; //清除TF1标志
  230. TR1 = 1; //定时器1开始计时
  231. ET1 = 1; //使能定时器1中断
  232. }
  233. void Delay100ms(void) //@12.000MHz
  234. {
  235. unsigned char data i, j, k;
  236. _nop_();
  237. _nop_();
  238. i = 5;
  239. j = 144;
  240. k = 71;
  241. do
  242. {
  243. do
  244. {
  245. while (--k);
  246. } while (--j);
  247. } while (--i);
  248. }
  249. void Delay5ms(void) //@12.000MHz
  250. {
  251. unsigned char data i, j;
  252. i = 59;
  253. j = 90;
  254. do
  255. {
  256. while (--j);
  257. } while (--i);
  258. }
  259. void get_key()
  260. {
  261. unsigned char key_P3=P3;
  262. unsigned char key_P4=P4;
  263. P44=0;
  264. if(P32==0){Delay5ms();while(P32==0){run();}Delay5ms();key_value=5;}
  265. else if(P33==0){Delay5ms();while(P33==0){run();}Delay5ms();key_value=4;}
  266. P42=0;
  267. if(P32==0)
  268. {
  269. Delay5ms();
  270. is_2s=0;//长按功能,按下按键之后,is_2s被置为0,2s之后is_2s被置为1,如果2s之后还没松手(此时is_2s为1,则视为长按)
  271. while(P32==0){run();}
  272. if(mod==3&&is_2s==1)//is_2s标志位为1,说明从is_2s清零到现在已经过去2s了,也就是已经长按按键2s了
  273. {
  274. /*在时间回显菜单里,长按2s按键9,则情况所有储存的数据。
  275. 这里需要清除的数据有:当前读取的温度值和湿度值,记录的最高的温度值和湿度值,记录的平均温度值和湿度值
  276. 上一次读取的温度值和湿度值(仅仅用于比较,为了安全也清0)
  277. 上一次读取数据的时间
  278. 读取的湿度是否有效标志位等
  279. */
  280. Now_temp=0;Now_shi_du=0;
  281. arr_temp=0;arr_shi_du=0;
  282. max_temp=0;max_shi_du=0;
  283. Now_time[0]=0;Now_time[1]=0;Now_time[2]=0;
  284. last_temp=0;last_shi_du=0;
  285. ci_shu=0;shidu_wuxiao=0;
  286. }
  287. Delay5ms();
  288. key_value=9;
  289. }
  290. else if(P33==0){Delay5ms();while(P33==0){run();}Delay5ms();key_value=8;}
  291. //s4切换菜单
  292. if(key_value==4&&mod!=5)//仅在不是在温湿度显示菜单时有效
  293. {
  294. if(mod==0)//如果在时间显示菜单,则切换到数据回显菜单
  295. mod=1;
  296. else if(mod==1||mod==2||mod==3)//如果在数据回显菜单,则切换到参数菜单
  297. mod=4;//注意数据回显菜单有三个子菜单
  298. else if(mod==4)//如果在参数菜单,则切换到时间显示菜单
  299. mod=0;
  300. }
  301. //s5切换数据回显的子菜单
  302. else if(key_value==5&&(mod==1||mod==2||mod==3))//仅在数据回显菜单内有效
  303. {
  304. if(mod<3)
  305. mod++;
  306. else
  307. mod=1;
  308. }
  309. //s8加
  310. else if(key_value==8&&mod==4)//在参数设置菜单中,按下s8参数+1
  311. {
  312. if(++temp_canshu>100)//注意参数范围是0到99
  313. temp_canshu=0;
  314. }
  315. //s9减(长按功能在if(P32==0)那呢)
  316. else if(key_value==9&&mod==4)//在参数显示菜单,按下s9参数-1
  317. {
  318. if(temp_canshu>0)//注意参数范围是0到99
  319. temp_canshu--;
  320. else
  321. temp_canshu=99;
  322. }
  323. key_value=0;
  324. P3=key_P3;
  325. P4=key_P4;
  326. }
  327. void show_menu(void)
  328. {
  329. if(mod==0)//菜单0显示时间
  330. {
  331. Nixie_num[0]=time[2]/10%10;
  332. Nixie_num[1]=time[2]/1%10;
  333. Nixie_num[2]=21;
  334. Nixie_num[3]=time[1]/10%10;
  335. Nixie_num[4]=time[1]/1%10;
  336. Nixie_num[5]=21;
  337. Nixie_num[6]=time[0]/10%10;
  338. Nixie_num[7]=time[0]/1%10;
  339. }
  340. /*题目要求:当触发次数为0时,时间回显子界面的时、间隔、分显示位置熄灭;温度、湿度回显子界面除界面标识符外的其它位熄灭。
  341. 下边mod=1 2 3时对ci_shu=0的判断就是为了完成题目的上述要求*/
  342. else if(mod==1)//回显子菜单1,温度回显
  343. {
  344. Nixie_num[0]=22;//C
  345. Nixie_num[1]=20;//熄灭
  346. if(ci_shu==0)//如果一次有效数据都没有读到,则时间回显子界面的时、间隔、分显示位置熄灭;
  347. {
  348. Nixie_num[2]=20;//熄灭,下同
  349. Nixie_num[3]=20;
  350. Nixie_num[4]=20;
  351. Nixie_num[5]=20;
  352. Nixie_num[6]=20;
  353. Nixie_num[7]=20;
  354. }
  355. else//计算平均温度时数据扩大了10倍,在显示时把小数点加上
  356. {
  357. Nixie_num[2]=max_temp/10%10;//最大温度
  358. Nixie_num[3]=max_temp/1%10;
  359. Nixie_num[4]=21;
  360. Nixie_num[5]=arr_temp/100%10;//平均温度
  361. Nixie_num[6]=arr_temp/10%10+10;//加小数点
  362. Nixie_num[7]=arr_temp/1%10;
  363. }
  364. }
  365. else if(mod==2)//回显子菜单2,湿度回显
  366. {
  367. Nixie_num[0]=23;//H
  368. Nixie_num[1]=20;
  369. if(ci_shu==0)//“温度、湿度回显子界面除界面标识符外的其它位熄灭”
  370. {
  371. Nixie_num[2]=20;//熄灭
  372. Nixie_num[3]=20;
  373. Nixie_num[4]=20;
  374. Nixie_num[5]=20;
  375. Nixie_num[6]=20;
  376. Nixie_num[7]=20;
  377. }
  378. else//同上,计算平均湿度时数据扩大了10倍,在显示时把小数点加上
  379. {
  380. Nixie_num[2]=max_shi_du/10%10;//最大湿度
  381. Nixie_num[3]=max_shi_du/1%10;
  382. Nixie_num[4]=21;
  383. Nixie_num[5]=arr_shi_du/100%10;//平均湿度
  384. Nixie_num[6]=arr_shi_du/10%10+10;//加上小数点
  385. Nixie_num[7]=arr_shi_du/1%10;
  386. }
  387. }
  388. else if(mod==3)//回显子菜单3,时间回显
  389. {
  390. Nixie_num[0]=24;//F
  391. Nixie_num[1]=ci_shu/10%10;//触发次数
  392. Nixie_num[2]=ci_shu/1%10;
  393. if(ci_shu==0)//“温度、湿度回显子界面除界面标识符外的其它位熄灭”
  394. {
  395. Nixie_num[3]=20;//熄灭
  396. Nixie_num[4]=20;
  397. Nixie_num[5]=20;
  398. Nixie_num[6]=20;
  399. Nixie_num[7]=20;
  400. }
  401. else
  402. {
  403. /*显示的是读取数据时的时间,只显示时分*/
  404. Nixie_num[3]=Now_time[2]/10%10;//时间
  405. Nixie_num[4]=Now_time[2]/1%10;
  406. Nixie_num[5]=21;
  407. Nixie_num[6]=Now_time[1]/10%10;//分钟
  408. Nixie_num[7]=Now_time[1]/1%10;
  409. }
  410. }
  411. else if(mod==4)//菜单4参数界面
  412. {
  413. Nixie_num[0]=25;//P
  414. Nixie_num[1]=20;//熄灭
  415. Nixie_num[2]=20;
  416. Nixie_num[3]=20;
  417. Nixie_num[4]=20;
  418. Nixie_num[5]=20;
  419. Nixie_num[6]=temp_canshu/10%10;//温度参数
  420. Nixie_num[7]=temp_canshu/1%10;
  421. }
  422. else if(mod==5)//菜单5温湿度界面
  423. {
  424. Nixie_num[0]=26;//E
  425. Nixie_num[1]=20;//熄灭
  426. Nixie_num[2]=20;
  427. /*如果当前的湿度无效,则温度显示读取到的温度(只读取不储存和处理那个),湿度显示AA*/
  428. if(shidu_wuxiao==1)
  429. {
  430. Nixie_num[3]=wrong_temp/10%10;//温度
  431. Nixie_num[4]=wrong_temp/1%10;
  432. Nixie_num[5]=21;//-
  433. Nixie_num[6]=27;//湿度,显示A
  434. Nixie_num[7]=27;//A
  435. }
  436. else//数据有效,正常显示
  437. {
  438. Nixie_num[3]=Now_temp/10%10;//温度
  439. Nixie_num[4]=Now_temp/1%10;
  440. Nixie_num[5]=21;//-
  441. Nixie_num[6]=Now_shi_du/10%10;//湿度
  442. Nixie_num[7]=Now_shi_du/1%10;
  443. }
  444. }
  445. }
  446. bit led1_is_on=0;//这几个变量都是某个led灯的状态标志位,用于记录led灯的亮灭状态
  447. bit led2_is_on=0;
  448. bit led3_is_on=0;
  449. bit led4_is_on=0;
  450. bit led5_is_on=0;
  451. bit led6_is_on=0;
  452. void led_run(void)
  453. {
  454. if(mod==0&&led1_is_on==0)//在时间菜单,led1点亮
  455. {
  456. LED_ON(0);
  457. led1_is_on=1;
  458. }
  459. else if(mod!=0&&led1_is_on==1)//否则,led1熄灭
  460. {
  461. LED_OFF(0);
  462. led1_is_on=0;
  463. }
  464. if((mod==1||mod==2||mod==3)&&led2_is_on==0)//在数据回显菜单,led2点亮
  465. {
  466. LED_ON(1);
  467. led2_is_on=1;
  468. }
  469. else if(!(mod==1||mod==2||mod==3)&&led2_is_on==1)//否则,led2熄灭
  470. {
  471. LED_OFF(1);
  472. led2_is_on=0;
  473. }
  474. if(mod==5&&led3_is_on==0)//在温湿度菜单,led3点亮
  475. {
  476. LED_ON(2);
  477. led3_is_on=1;
  478. }
  479. else if(mod!=5&&led3_is_on==1)//否则,led3熄灭
  480. {
  481. LED_OFF(2);
  482. led3_is_on=0;
  483. }
  484. if(Now_temp>temp_canshu)//如果读取到的温度值高于温度阈值,则led4闪烁
  485. {
  486. /*主函数的while(1)循环里有100ms延时,这里只需要保证进入一次led_run函数翻转一次led4的状态,即可闪烁*/
  487. if(led4_is_on==0)//当前led4熄灭,则点亮
  488. {
  489. LED_ON(3);
  490. led4_is_on=1;
  491. }
  492. else if(led4_is_on==1)//当前led4已电亮,则熄灭
  493. {
  494. LED_OFF(3);
  495. led4_is_on=0;
  496. }
  497. }
  498. else if(led4_is_on==1)//如果读取到的温度值不高于阈值,则led4熄灭
  499. {
  500. LED_OFF(3);
  501. led4_is_on=0;
  502. }
  503. if(shidu_wuxiao==1&&led5_is_on==0)//如果当前读取到的湿度无无效,则led5点亮
  504. {
  505. LED_ON(4);
  506. led5_is_on=1;
  507. }
  508. else if(shidu_wuxiao==0&&led5_is_on==1)//如果当前读取到的湿度有效,则led5熄灭
  509. {
  510. LED_OFF(4);
  511. led5_is_on=0;
  512. }
  513. if(is_up==1&&led6_is_on==0)//如果温度和湿度均比上一次读取到的值高(在run函数进行判断),则led6点亮
  514. {
  515. LED_ON(5);
  516. led6_is_on=1;
  517. }
  518. else if(is_up==0&&led6_is_on==1)//否则熄灭
  519. {
  520. LED_OFF(5);
  521. led6_is_on=0;
  522. }
  523. }

onewire.c

  1. /* # 单总线代码片段说明
  2. 1. 本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
  3. 2. 参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
  4. 中对单片机时钟频率的要求,进行代码调试和修改。
  5. */
  6. #include <stc15.h>
  7. #include <intrins.h>
  8. #include "onewire.h"
  9. sbit DQ=P1^4;
  10. //
  11. void Delay_OneWire(unsigned int t)
  12. {
  13. unsigned char i;
  14. while(t--){
  15. for(i=0;i<12;i++);
  16. }
  17. }
  18. //
  19. void Write_DS18B20(unsigned char dat)
  20. {
  21. unsigned char i;
  22. for(i=0;i<8;i++)
  23. {
  24. DQ = 0;
  25. DQ = dat&0x01;
  26. Delay_OneWire(5);
  27. DQ = 1;
  28. dat >>= 1;
  29. }
  30. Delay_OneWire(5);
  31. }
  32. //
  33. unsigned char Read_DS18B20(void)
  34. {
  35. unsigned char i;
  36. unsigned char dat;
  37. for(i=0;i<8;i++)
  38. {
  39. DQ = 0;
  40. dat >>= 1;
  41. DQ = 1;
  42. if(DQ)
  43. {
  44. dat |= 0x80;
  45. }
  46. Delay_OneWire(5);
  47. }
  48. return dat;
  49. }
  50. //
  51. bit init_ds18b20(void)
  52. {
  53. bit initflag = 0;
  54. DQ = 1;
  55. Delay_OneWire(12);
  56. DQ = 0;
  57. Delay_OneWire(80);
  58. DQ = 1;
  59. Delay_OneWire(10);
  60. initflag = DQ;
  61. Delay_OneWire(5);
  62. return initflag;
  63. }
  64. unsigned int read_ds(void)
  65. {
  66. unsigned char low=0;
  67. unsigned char high=0;
  68. unsigned int temp=0;
  69. init_ds18b20();
  70. Write_DS18B20(0xCC);
  71. Write_DS18B20(0x44);
  72. Delay_OneWire(200);
  73. init_ds18b20();
  74. Write_DS18B20(0xCC);
  75. Write_DS18B20(0xBE);
  76. low=Read_DS18B20();
  77. high=Read_DS18B20();
  78. temp=high;
  79. temp&=0x0F;
  80. temp<<=8;
  81. temp|=low;
  82. temp>>=4;
  83. return temp;
  84. }

onewire.h

  1. #ifndef _ONEWIRE_H_
  2. #define _ONEWIRE_H_
  3. unsigned int read_ds(void);
  4. #endif

iic.c

  1. /* # I2C代码片段说明
  2. 1. 本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
  3. 2. 参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
  4. 中对单片机时钟频率的要求,进行代码调试和修改。
  5. */
  6. #define DELAY_TIME 5
  7. #include <stc15.h>
  8. #include <intrins.h>
  9. #include "iic.h"
  10. sbit scl=P2^0;
  11. sbit sda=P2^1;
  12. //
  13. static void I2C_Delay(unsigned char n)
  14. {
  15. do
  16. {
  17. _nop_();_nop_();_nop_();_nop_();_nop_();
  18. _nop_();_nop_();_nop_();_nop_();_nop_();
  19. _nop_();_nop_();_nop_();_nop_();_nop_();
  20. }
  21. while(n--);
  22. }
  23. //
  24. void I2CStart(void)
  25. {
  26. sda = 1;
  27. scl = 1;
  28. I2C_Delay(DELAY_TIME);
  29. sda = 0;
  30. I2C_Delay(DELAY_TIME);
  31. scl = 0;
  32. }
  33. //
  34. void I2CStop(void)
  35. {
  36. sda = 0;
  37. scl = 1;
  38. I2C_Delay(DELAY_TIME);
  39. sda = 1;
  40. I2C_Delay(DELAY_TIME);
  41. }
  42. //
  43. void I2CSendByte(unsigned char byt)
  44. {
  45. unsigned char i;
  46. for(i=0; i<8; i++){
  47. scl = 0;
  48. I2C_Delay(DELAY_TIME);
  49. if(byt & 0x80){
  50. sda = 1;
  51. }
  52. else{
  53. sda = 0;
  54. }
  55. I2C_Delay(DELAY_TIME);
  56. scl = 1;
  57. byt <<= 1;
  58. I2C_Delay(DELAY_TIME);
  59. }
  60. scl = 0;
  61. }
  62. //
  63. unsigned char I2CReceiveByte(void)
  64. {
  65. unsigned char da;
  66. unsigned char i;
  67. for(i=0;i<8;i++){
  68. scl = 1;
  69. I2C_Delay(DELAY_TIME);
  70. da <<= 1;
  71. if(sda)
  72. da |= 0x01;
  73. scl = 0;
  74. I2C_Delay(DELAY_TIME);
  75. }
  76. return da;
  77. }
  78. //
  79. unsigned char I2CWaitAck(void)
  80. {
  81. unsigned char ackbit;
  82. scl = 1;
  83. I2C_Delay(DELAY_TIME);
  84. ackbit = sda;
  85. scl = 0;
  86. I2C_Delay(DELAY_TIME);
  87. return ackbit;
  88. }
  89. //
  90. void I2CSendAck(unsigned char ackbit)
  91. {
  92. scl = 0;
  93. sda = ackbit;
  94. I2C_Delay(DELAY_TIME);
  95. scl = 1;
  96. I2C_Delay(DELAY_TIME);
  97. scl = 0;
  98. sda = 1;
  99. I2C_Delay(DELAY_TIME);
  100. }
  101. //void wirite_pcf(unsigned dat)
  102. //{
  103. // I2CStart();
  104. // I2CSendByte(0x90);
  105. // I2CWaitAck();
  106. // I2CSendByte(0x40);
  107. // I2CWaitAck();
  108. // I2CSendByte(dat);
  109. // I2CWaitAck();
  110. // I2CStop();
  111. //}
  112. unsigned char read_pcf(unsigned add)
  113. {
  114. unsigned char ad=0;
  115. I2CStart();
  116. I2CSendByte(0x90);
  117. I2CWaitAck();
  118. I2CSendByte(add);
  119. I2CWaitAck();
  120. I2CStop();
  121. I2CStart();
  122. I2CSendByte(0x91);
  123. I2CWaitAck();
  124. ad=I2CReceiveByte();
  125. I2CSendAck(1);
  126. I2CStop();
  127. return ad;
  128. }

iic.h

  1. #ifndef _IIC_H_
  2. #define _IIC_H_
  3. //void wirite_pcf(unsigned dat);
  4. unsigned char read_pcf(unsigned add);
  5. #endif

ds1302.c

  1. /* # DS1302代码片段说明
  2. 1. 本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
  3. 2. 参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
  4. 中对单片机时钟频率的要求,进行代码调试和修改。
  5. */
  6. #include <stc15.h>
  7. #include <intrins.h>
  8. #include "ds1302.h"
  9. sbit SCK=P1^7;
  10. sbit SDA=P2^3;
  11. sbit RST=P1^3;
  12. unsigned char time[3]={5,3,13};
  13. //
  14. void Write_Ds1302(unsigned char temp)
  15. {
  16. unsigned char i;
  17. for (i=0;i<8;i++)
  18. {
  19. SCK = 0;
  20. SDA = temp&0x01;
  21. temp>>=1;
  22. SCK=1;
  23. }
  24. }
  25. //
  26. void Write_Ds1302_Byte( unsigned char address,unsigned char dat )
  27. {
  28. RST=0; _nop_();
  29. SCK=0; _nop_();
  30. RST=1; _nop_();
  31. Write_Ds1302(address);
  32. Write_Ds1302(dat);
  33. RST=0;
  34. }
  35. //
  36. unsigned char Read_Ds1302_Byte ( unsigned char address )
  37. {
  38. unsigned char i,temp=0x00;
  39. RST=0; _nop_();
  40. SCK=0; _nop_();
  41. RST=1; _nop_();
  42. Write_Ds1302(address);
  43. for (i=0;i<8;i++)
  44. {
  45. SCK=0;
  46. temp>>=1;
  47. if(SDA)
  48. temp|=0x80;
  49. SCK=1;
  50. }
  51. RST=0; _nop_();
  52. SCK=0; _nop_();
  53. SCK=1; _nop_();
  54. SDA=0; _nop_();
  55. SDA=1; _nop_();
  56. return (temp);
  57. }
  58. void ds1302_init(void)
  59. {
  60. unsigned char add=0;
  61. unsigned char i=0;
  62. add=0x80;
  63. Write_Ds1302_Byte(0x8E,0x00);
  64. for(i=0;i<3;i++)
  65. {
  66. Write_Ds1302_Byte(add,(time[i]/10)<<4|(time[i]%10));
  67. add+=2;
  68. }
  69. Write_Ds1302_Byte(0x8E,0x80);
  70. }
  71. void read_time(void)
  72. {
  73. unsigned char add=0;
  74. unsigned char dat=0;
  75. unsigned char i=0;
  76. add=0x81;
  77. for(i=0;i<3;i++)
  78. {
  79. dat=Read_Ds1302_Byte(add);
  80. time[i]=dat/16*10+dat%16;
  81. add+=2;
  82. }
  83. }

ds1302,h

  1. #ifndef _DS1302_H_
  2. #define _DS1302_H_
  3. extern unsigned char time[3];
  4. void ds1302_init(void);
  5. void read_time(void);
  6. #endif
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/456065
推荐阅读
相关标签
  

闽ICP备14008679号