这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 高校专区 » 坤创E-Geek/天科大新电社 » 2022年第十三届蓝桥杯省赛真题解析

共16条 1/2 1 2 跳转至

2022年第十三届蓝桥杯省赛真题解析

高工
2022-07-23 10:48:50     打赏

大家好,今天给大家分享2022年第十三届的蓝桥杯省赛程序题。先上题目:

1.png

今年的省赛程序题难度不算大,使用到的模块有矩阵按键,蜂鸣器、继电器、LED、DS18B20、时钟模块、数码管;这些都是往年经常考到的模块,需要要注意的就是矩阵按键是简化版的,只打开了S12、S13、S15、S16,还有就是LED和继电器蜂鸣器模块的配合,因为本次的题目把他们三个全都用到了,他们三个又共用P0端口,LED是共阳极的接法,继电器和蜂鸣器是共阴极的接法,所以在写代码的时候,我建议把LED和继电器蜂鸣器分开,把其中的一个放到定时器里去控制,这样程序会稳定一些。

拿到题目后,我们先把温度传感器和时钟的相关函数进行完善。

时钟模块:

#include "ds1302.h"
 
//---DS1302写入和读取时分秒的地址命令---//
//---秒分时日月周年 最低位读写位;-------//
unsigned char code READ_RTC_ADDR[7] = {0x81, 0x83, 0x85, 0x87, 0x89, 0x8b, 0x8d}; 
unsigned char code WRITE_RTC_ADDR[7] = {0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c};
 
//---DS1302时钟初始化2016年5月7日星期六23点59分50秒。---//
//---存储顺序是秒分时日月周年,存储格式是用BCD码---//
unsigned char TIME[7] = {50, 59, 23, 0x07, 0x05, 0x06, 0x16};
 
unsigned char read(unsigned char add)
{
   unsigned char i,temp;
   unsigned char dat1,dat2;
   CE=0;
   SCLK=0;
   CE=1;
   writebyte(add);
   for(i=0;i<8;i++)
   {
       SCLK=0;
       temp>>=1;
       if(IO)
       {
          temp|=0x80;
       }
       SCLK=1;
   }
   
   IO=0;
   
   dat1=temp/16;//0XFF 1111 1111
   dat2=temp%16;
   
   temp=dat1*10+dat2;
   
   return temp;
}
 
 
void writebyte(unsigned char dat)
{
   unsigned char i;
   for(i=0;i<8;i++)
   {
       SCLK=0;
       IO=dat&0x01;
       SCLK=1;
       dat>>=1;
   }
}
 
void write(unsigned char add,unsigned char dat)
{
   unsigned char num;
   CE=0;
   SCLK=0;
   CE=1;
   writebyte(add);
   num=(dat/10<<4)|(dat%10);// 55
   writebyte(num);
   CE=0;
}
 
 
void Init_DS1302()
{
   unsigned char i;
   write(0x8e,0x00);
   for(i=0;i<7;i++)
   {
       write(WRITE_RTC_ADDR[i],TIME[i]);
   }
   write(0x8e,0x80);
   
}
void readtime()
{
   unsigned char i;
   write(0x8e,0x00);
   for(i=0;i<7;i++)
   {
       TIME[i]=read(READ_RTC_ADDR[i]);
   }
   write(0x8e,0x80); 
}

时钟模块头文件:

#ifndef __DS1302_H_
#define __DS1302_H_
 
//---包含头文件---//
#include <reg52.h>
#include <intrins.h>
 
//---定义ds1302使用的IO口---//
sbit CE=P1^3;
sbit IO=P2^3;
sbit SCLK=P1^7;
 
//---定义全局函数---//
void readtime();
unsigned char read(unsigned char add);
void writebyte(unsigned char dat);
void write(unsigned char add,unsigned char dat);
void Init_DS1302();
 
//---加入全局变量--//
extern unsigned char TIME[7];  //加入全局变量
 
#endif

温度传感器模块:

#include "onewire.h"
 
/****************************单总线延时函数****************************/
void Delay_OneWire(unsigned int t)
{
   unsigned char aa;
  while(t--)
   {
       for(aa=0;aa<10;aa++);
   }
}
 
/****************************DS18B20芯片初始化****************************/
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;
}
 
 
/****************************通过单总线向DS18B20写一个字节****************************/
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);
}
 
 
/****************************从DS18B20读取一个字节****************************/
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;
}
 
 
/****************************温度读取处理函********************/
unsigned char Tempget()
{
   unsigned int low,high,temp;
   
    Init_DS18B20();
   Write_DS18B20(0xcc);
   Write_DS18B20(0x44);
   Delay_OneWire(20);
 
    Init_DS18B20();
   Write_DS18B20(0xcc);
   Write_DS18B20(0xbe);
   
   low=Read_DS18B20();
   high=Read_DS18B20();
 
   temp=high<<4;
   temp|=(low>>4);
 
   return temp;
}

温度传感器模块头文件:

#ifndef _ONEWIRE_H
#define _ONEWIRE_H
 
#include <reg52.h>
 
#define OW_SKIP_ROM 0xcc
#define DS18B20_CONVERT 0x44
#define DS18B20_READ 0xbe
 
//IC引脚定义
sbit DQ = P1^4;
 
//函数声明
void Delay_OneWire(unsigned int t);
void Write_DS18B20(unsigned char dat);
bit Init_DS18B20(void);
unsigned char Read_DS18B20(void);
unsigned char Tempget();
 
#endif

写完这些之后,我们要新建一个工程,把准备工作做好,比如关闭蜂鸣器继电器LED、初始化数码管、延迟函数、LED、矩阵键盘的位定义等:

#include <reg52.h>
#include <ds1302.h>
#include <onewire.h>
typedef unsigned int u16;
typedef unsigned char u8;
 
sfr P4=0XC0;
 
sbit R3=P3^2;
sbit R4=P3^3;
 
sbit C3=P3^5;
sbit C4=P3^4;
 
sbit L1=P0^0;
sbit L2=P0^1;
sbit L3=P0^2;
 
u8 code smgduan[10]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90};
u8 code smgduan_dp[10]={0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10};
 
u8 temp_setup=23,temp_get;
 
 
void display_sec();
 
 
/***************延迟函数函数,单位1ms******************/
void delayms(u16 i)
{
   u16 aa;
   while(i--)
   {
       for(aa=0;aa<845;aa++);
   }
}
 
/***************通道选择函数******************/
void InitHC138(u8 add)
{
   switch(add)
   {
       case(4):
          P2=(P2&0X1F)|0X80;break;
       case(5):
          P2=(P2&0X1F)|0Xa0;break;
       case(6):
          P2=(P2&0X1F)|0Xc0;break;
       case(7):
          P2=(P2&0X1F)|0Xe0;break;
   }
}
 
/***************蜂鸣器继电器LED初始化函数******************/
void InitHC573()
{
   InitHC138(4);
   P0=0XFF;
   InitHC138(5);
   P0=0X00;
}
 
/***************数码管段选位选函数******************/
void DigDisplay(u8 add,u8 dat)
{
   
   InitHC138(6);
   P0=0X01<<add;
   InitHC138(7);
   P0=dat;
}
 
/***************消隐函数******************/
void DisplayOFF()
{
   InitHC138(7);
   P0=0XFF;
}
 
/***************温度获取函数******************/
void TempGet1()
{
   temp_get=Tempget();
   
}

做好这些准备工作之后,我们就可以开始审题了:

2.png

显示功能这一块要求三个界面,我们可以写三个显示函数并将它们单独封装,方便后面调用:

/***************温度数据显示函数******************/
void DispalyTempDat()
{
   DigDisplay(0,0xc1);
       delayms(1);
       DigDisplay(1,smgduan[1]);
       delayms(1);
       DigDisplay(5,smgduan[temp_get/10]);
       delayms(1);
       DigDisplay(6,smgduan_dp[temp_get%10]);
       delayms(1);
       DigDisplay(7,smgduan[temp_get*10%10]);
       delayms(1);
       DisplayOFF();
}
 
/***************时间显示函数******************/
void DisplayTime()
{
   DigDisplay(0,0xc1);
       delayms(1);
       DigDisplay(1,smgduan[2]);
       delayms(1);
       DigDisplay(3,smgduan[TIME[2]/10]);
       delayms(1);
       DigDisplay(4,smgduan[TIME[2]%10]);
       delayms(1);
       DigDisplay(5,0xBF);
       delayms(1);
       DigDisplay(6,smgduan[TIME[1]/10]);
       delayms(1);
       DigDisplay(7,smgduan[TIME[1]%10]);
       delayms(1);
       DisplayOFF();
}
 
/***************温度参数显示函数******************/
void DisplayTempSetup()
{
   DigDisplay(0,0xc1);
       delayms(1);
       DigDisplay(1,smgduan[3]);
       delayms(1);
       DigDisplay(6,smgduan[temp_setup/10]);
       delayms(1);
       DigDisplay(7,smgduan[temp_setup%10]);
       delayms(1);
       DisplayOFF();
}

然后看按键功能要求:

3.png

对于按键扫描函数也是尽量只做数据处理,不调用其他函数,设置了几个变量,在按键按下时,只对变量进行处理,之后再写一个函数,根据变量的值进行显示函数的调用,S16只在参数设置界面下有效,S17在时间显示界面、参数设置界面有效,所以我们需要在判断按键按下之后还要再判断一个条件,这样子函数在执行的时候更加稳定。要注意的是在S17按键按下的时候,要进行分和秒的显示,最简单的办法就是在松手检测的时候调用一下显示函数,这也是按键扫描函数里唯一调用显示函数的一个地方,注意参数变量的范围。

u8 display_add=1;
bit relay_setup=1;
 
/***************数码管显示_按下S17时显示秒******************/
void display_sec()
{
       DigDisplay(0,0xc1);
       delayms(1);
       DigDisplay(1,smgduan[2]);
       delayms(1);
       DigDisplay(3,smgduan[TIME[1]/10]);
       delayms(1);
       DigDisplay(4,smgduan[TIME[1]%10]);
       delayms(1);
       DigDisplay(5,0xBF);
       delayms(1);
       DigDisplay(6,smgduan[TIME[0]/10]);
       delayms(1);
       DigDisplay(7,smgduan[TIME[0]%10]);
       delayms(1);
   DisplayOFF();
}
/***************按键扫描******************/
void KeyScan()
{
   R3=0;
   R4=1;
   C3=1;C4=1;
   if(C3==0)          //S13
   {
       delayms(10);
       if(C3==0)
       {
          relay_setup=~relay_setup;
          while(!C3);
       }
   }
   else if(C4==0)     //S17
   {
       delayms(10);
       if(C4==0)
       {
          if(display_add==3) 
          { 
              temp_setup--;
              while(!C4);
          }
          else if(display_add==2) 
          {
              while(!C4)
              {
                 display_sec();
              }
          }
          
       }
   }
   
   R3=1;
   R4=0;
   C3=1;C4=1;
   if(C3==0)            //S12
   {
       delayms(10);
       if(C3==0)
       {
           display_add++;
          while(!C3);
       }
   }
   else if(C4==0)      //S16
   {
       delayms(10);
       if(C4==0)
       {
          if(display_add==3)
          {
              temp_setup++;
              while(!C4);
          }             
       }
   }
}
/***************按键处理******************/
void KeyPro()
{
   if(display_add>3) display_add=1;
   if(temp_setup>99) temp_setup=10;
   if(temp_setup<10) temp_setup=99;
}
 
/***************数码管显示******************/
void DisplayDat()
{
   if(display_add==1)
   {
       DispalyTempDat();
   }
   
   if(display_add==2)
   {
       DisplayTime();
   }
   
   if(display_add==3)
   {
       DisplayTempSetup();
   }
}

当按键函数和扫描函数都写完之后,整体的逻辑也就结束了,接下来就是继电器蜂鸣器和LED的控制了:

4.png

打开定时器,对温度以及时间进行判断,并选择要打开的模块,对函数进行封装;注意,L3在达到要求后需要以0.1秒为间隔进行闪烁,对于LED再定义几个标志变量,当达到要求后,标志变量置1,在定时器里对LED进行开关处理。

bit relay;
bit flag_L3;
bit flag_L1;
bit flag_L2;
 
/***************继电器处理******************/
void RelayPro()
{
   if(relay_setup==1)  //温度控制
   {
       if(temp_get>(temp_setup))
       {
          InitHC138(5);
          P0=0X10;
          relay=1;
       }
       else 
       {
          InitHC138(5);
          P0=0X00;
          relay=0;
       }
   }
   else if(relay_setup==0)  //时间控制
   {
       InitHC138(5);
       P0=0X00;
       relay=0;
       if(TIME[1]==0)
       {
          InitHC138(5);
          P0=0X10;
          relay=1;
       }
       if((TIME[1]==0)&&(TIME[0]>5))
       {
          InitHC138(5);
          P0=0X00;
          relay=0;
       }
   }
}
 
/***************led处理******************/
void LedPro()
{
   if((TIME[1]==0)&&(TIME[0]<5))
   {
       flag_L1 = 1;
   }
   else if((TIME[1]==0)&&(TIME[0]>5))
   {
       flag_L1 = 0;
   }
   
 
   
   if(relay_setup==1)
   {
       InitHC138(4);
       P0=0XFF;
       L2=0;
   }
   else if(relay_setup==0)
   {
       InitHC138(4);
       P0=0XFF;
       L2=1;
   }
   
   if(relay==1)
   {
       flag_L3=1;
   }
   else if(relay==0)
   {
       flag_L3=0;
   }
}
/***************定时器中断******************/
void Time0Init()
{
   TMOD|=0X01;
   TL0 = 0x18;       //设置定时初值1ms
   TH0 = 0xFC;
   
   ET0=1;
   TR0=1;
   EA=1;
}
void Time0() interrupt 1
{
   u16 count_ms;
   TL0 = 0x18;       //设置定时初值1ms
   TH0 = 0xFC;
   
   if(flag_L3==1)
   {
       count_ms++;
       if(count_ms==100)
       {
          InitHC138(4);
          P0=0XFF;
          L3=~L3;
          count_ms=0;
       }
   }
   if(flag_L1 == 1)
   {
       
          L1=0;
   }
}

所有的代码完成之后,我们把需要用到的函数放进主函数里就可以了,要记得上电演示一遍哦:

void main()
{
   InitHC573();
   Time0Init();
   Init_DS1302();
 
   while(1)
   {
       readtime();
       TempGet1();
       KeyScan();
       KeyPro();
       DisplayDat();
       RelayPro();
       LedPro();
   }
}

好了,今天的分享到这里就结束了,感谢大家o(* ̄ ̄*)ブ。



专家
2022-07-23 11:10:44     打赏
2楼

学习


专家
2022-08-02 15:39:25     打赏
3楼
赞一个

院士
2022-08-10 23:19:29     打赏
4楼

学习并收藏了这个资料,谢谢分享。


工程师
2022-08-11 11:20:21     打赏
5楼
都是大佬,厉害

工程师
2022-08-11 21:46:58     打赏
6楼

学到了


专家
2022-08-20 19:21:19     打赏
7楼

都是大佬,厉害


菜鸟
2022-09-15 10:04:07     打赏
8楼


工程师
2022-09-19 22:54:07     打赏
9楼

非常感谢您的分享


高工
2022-11-18 08:38:19     打赏
10楼

学习一下


共16条 1/2 1 2 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]