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

共20条 1/2 1 2 跳转至

2021年第十二届蓝桥杯省赛真题解析

高工
2022-07-22 19:00:14     打赏

大家好,今天给大家分享第十二届蓝桥杯单片机省赛的程序题;我们来先看题目:

2856e7b5a85f489b945efbbee58dd77f.png

题目的第一页内容只有一些注意事项,在考试时注意一下就行,这一页我们可以知道这道题所用到的模块有DS18B20、DA输出、按键输入、LED指示灯,那么我们就先把官方给的DS18B20和DA输出模块的程序给他补充完整。

这个是I2C.C的参考代码:

#include <reg52.h>
#include "intrins.h"
#include "iic.h"
 
#define somenop {_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();}   
 
 
#define SlaveAddrW 0xA0
#define SlaveAddrR 0xA1
 
//总线引脚定义
sbit SDA = P2^1;  /* 数据线 */
sbit SCL = P2^0;  /* 时钟线 */
 
void DA_write(uchar dat)
{
   IIC_Start();
   IIC_SendByte(0x90);
   IIC_WaitAck();
   IIC_SendByte(0x40);
   IIC_WaitAck();
   IIC_SendByte(dat);
   IIC_WaitAck();
    IIC_Stop();
}
//总线启动条件
void IIC_Start(void)
{
       SDA = 1;
       SCL = 1;
       somenop;
       SDA = 0;
       somenop;
       SCL = 0;
}
 
//总线停止条件
void IIC_Stop(void)
{
       SDA = 0;
       SCL = 1;
       somenop;
       SDA = 1;
}
 
 
 
//等待应答
bit IIC_WaitAck(void)
{
       SDA = 1;
       somenop;
       SCL = 1;
       somenop;
       if(SDA)   
       {  
              SCL = 0;
              IIC_Stop();
              return 0;
       }
       else 
       {
              SCL = 0;
              return 1;
       }
}
 
//通过I2C总线发送数据
void IIC_SendByte(unsigned char byt)
{
       unsigned char i;
       for(i=0;i<8;i++)
       {  
              if(byt&0x80)
              {     
                     SDA = 1;
              }
              else
              {
                     SDA = 0;
              }
              somenop;
              SCL = 1;
              byt <<= 1;
              somenop;
              SCL = 0;
       }
}
 
//从I2C总线上接收数据
unsigned char IIC_RecByte(void)
{
       unsigned char da;
       unsigned char i;
      
       for(i=0;i<8;i++)
       {  
              SCL = 1;
              somenop;
              da <<= 1;
              if(SDA)
              da |= 0x01;
              SCL = 0;
              somenop;
       }
       return da;
}

I2C.H的参考代码:

#ifndef _IIC_H
#define _IIC_H

#define uchar unsigned char 
#define uint unsigned int	



//函数声明
void IIC_Start(void); 
void IIC_Stop(void);  
void IIC_SendByte(unsigned char byt); 
bit IIC_WaitAck(void);  
unsigned char IIC_RecByte(void); 
void DA_write(uchar dat);
#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<<8;
       temp=temp | low;
      
       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和蜂鸣器继电器、数码管显示的通道选择函数写出来,数码管段选数组、矩阵键盘的位定义、延迟函数等准备工作做好:

#include "reg52.h"
#include "iic.h"
#include "onewire.h"
 
typedef unsigned int u16;
typedef unsigned char u8;
 
sfr P4 = 0XC0; //REG52的库内没有定义P4端口,所以我们需要自己定义上
 
 
sbit C1 = P4^2;
sbit C2 = P4^4;
 
sbit R1 = P3^2;
sbit R2 = P3^3;
 
sbit L1 = P0^0; //LED的位定义
sbit L2 = P0^1;
sbit L3 = P0^2;
sbit L4 = P0^3;
 
 
 
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};//共阳极数码管段选带小数点
 
/****************延迟函数,单位为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;  //LED、
              case(5):
                     P2=(P2&0X1F)|0Xa0;break;  //蜂鸣器继电器、
              case(6):
                     P2=(P2&0X1F)|0Xc0;break;  //数码管段选
              case(7):
                     P2=(P2&0X1F)|0Xe0;break;  //数码管位选
       }
}
 
/***************LED、蜂鸣器继电器初始化函数*********************/
void InitHC138(u8 add)
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;
}

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

2.png

4.3要求我们显示三个页面,温度显示界面、参数设置界面,DAC输出界面,那么我们就先写三个显示函数,把他们分别封装起来,方便后面调用,首先,我们需要设置几个全局变量:

u16 temp_dat; //温度获取
u8 temp_setup = 25; //温度参数
u16 output_v; //输出电压
 
/**************环境温度显示函数*************************/
void DisplayTempDat()
{
       DigDisplay(0,0xc6);
       delayms(1);
       DigDisplay(4,smgduan[temp_dat*10/1000]);
       delayms(1);
       DigDisplay(5,smgduan_dp[temp_dat*10/100%10]);
       delayms(1);
       DigDisplay(6,smgduan[temp_dat*10/10%10]);
       delayms(1);
       DigDisplay(7,smgduan[temp_dat*10%10]);
       delayms(1);
       DisplayOFF();
}
 
/**************温度参数显示函数*************************/
void DisplayTempSetup()
{
       DigDisplay(0,0X8C);
       delayms(1);
       DigDisplay(6,smgduan[temp_setup/10]);
       delayms(1);
       DigDisplay(7,smgduan[temp_setup%10]);
       delayms(1);
       DisplayOFF();
}
 
/**************电压输出显示函数*************************/
void DisplayDAC()
{
       DigDisplay(0,0x88);
       delayms(1);
       DigDisplay(5,smgduan_dp[output_v/100]);
       delayms(1);
       DigDisplay(6,smgduan[output_v/10%10]);
       delayms(1);
       DigDisplay(7,smgduan[output_v%10]);
       delayms(1);
       DisplayOFF();
}

写好之后,我们就来看按键功能的要求:

80deda164cc9458080dbafcca5603c0b.png

3.png

S4是控制三个界面来回切换,为了不影响数码管显示和数据采集,我们只设置一个变量,这个变量只有三个值,0,1,2,每当S4按下,这个变量依次累加,大于2之后又会变回0,之后再写一个函数根据变量的值进行显示;S5是控制模式切换,我们可以设置一个标志变量,每当S5按下,这个标志变量就取反;S8、S9是温度参数加减按键,只在参数设置界面有效,所以S8、S9按键扫描的时候还需要判断S4的变量的条件,“设定的温度参数在退出参数设置界面时生效”,这句话的意思就是,只有在进入DAC输出界面时生效,所以S5在扫面的时候也需要额外判断S4的变量条件:

bit MODE = 1;  //标志变量
u8 dis_add;//界面切换变量
 
/**************按键扫描处理函数*************************/
void KeyScan()
{
       R1 = 0;
       R2 = 1;C1 = 1;C2 = 1;
       if((C2 == 0)&&(dis_add == 2)) //S5
       {
              delayms(10);
              if(C2 == 0)
              {
                     MODE = ~MODE;
                     while(!C2);
              }
       }
       else if((C1 == 0) && (dis_add == 1)) //S9
       {
              delayms(10);
              if((C1 == 0) && (dis_add == 1))
              {
                     temp_setup++;
                     while(!C1);
              }
       }
      
       R2 = 0;
       R1 = 1;C1 = 1;C2 = 1;
       if(C2 == 0)    //S4
       {
              delayms(10);
              if(C2 == 0)
              {
                     dis_add++;
                     while(!C2);
              }
       }
       else if((C1 == 0) && (dis_add == 1))  //S8
       {
              delayms(10);
              if((C1 == 0) && (dis_add == 1))
              {
                     temp_setup--;
                     while(!C1);
              }
       }
}
 
/**************界面显示函数*************************/
void DisplayDat()
{
       switch(dis_add)
       {
              case(0):DisplayTempDat();break;
              case(1):DisplayTempSetup();break;
              case(2):DisplayDAC();break;
       }
}

按键说明要求中还涉及到DAC的输出逻辑,模式1下判断环境温度和温度参数的大小并输出0V或5V,而且这个输出只能在DAC输出界面时才输出,在模式2下当达到40℃时输出4V,小于20℃时输出1V,在20℃-40℃时输出电压与温度呈线性关系,我们可以得到这个方程的斜率:0.15,由于我们的温度获取函数得到的温度需要除以10才是我们的实际温度,所以需要对得到的环境温度进行处理,每变化1℃,DAC变化0.15V,且输出电压在1V和4V之间:

/**************DAC处理函数*************************/
void DACModePro()
{
       if((MODE==0)&&(dis_add == 2))
       {
              if(temp_dat<(temp_setup*10))
              {
                     DA_write(0);
                     output_v=0;
              }
              else
              {
                     DA_write(255);
                     output_v=500;
              }
       }
       if(MODE==1)
       {
              if(temp_dat<=200)
              {
                     DA_write(51);
                     output_v=100;
              }
              else if(temp_dat>=400)
              {
                     DA_write(204);
                     output_v=400;
              }
              else if(temp_dat>200&&temp_dat<400)
              {
                     int temp1_dat;
                     temp1_dat=temp_dat/10;
                     DA_write((1+(0.15*(temp_dat-20)))*51);
                     output_v=100*(1+(0.15*(temp1_dat-20)));
              }     
       }
}

写到这里,我们还要写一个函数对之前设置的那些参数变量进行范围约束:

/*************数据处理函数*************************/
void DatPro()
{
       if(temp_setup > 50)
       {
              temp_setup = 1;
       }
       if(temp_setup < 1)
       {
              temp_setup = 50;
       }
       if(dis_add>2)
       {
              dis_add = 0;
       }
}

接下来就是LED模块要求了:

4.png

本次的LED模块设置比较简单,根据前面设置的标志位以及变量进行判断就好:

/**************LED处理函数*************************/
void LedPro()
{
       InitHC138(4);
       P0 = 0XFF;
       if(MODE == 1) L1 = 0;else L1 = 1;
       if(dis_add == 0) L2 = 0;else L2 = 1;
       if(dis_add == 1) L3 = 0;else L3 = 1;
       if(dis_add == 2) L4 = 0;else L4 = 1;
}

初始状态我们在设置全局变量的时候进行设置就好,最后,我们需要在主函数里进行调用就可以,需要说明的是,我们可以把DAC输出和温度获取函数放在定时器里获取,可以设置每隔一秒获取一次,不需要让数据获取的那么频繁:

/**************定时器初始化函数*************************/
void TimeInit()
{
       TMOD = 0X01;
       TL0 = 0x18;          //设置定时初值1MS
       TH0 = 0xFC;
       TR0 = 1;
       ET0 = 1;
       EA = 1;
}
 
 
/**************主函数*************************/
void main()
{
       InitHC573();
       TimeInit();
       while(1)
       {
             
              KeyScan();
              DisplayDat();
              DatPro();
              LedPro();
       }
}
 
/**************定时器处理*************************/
void timer0() interrupt 1
{
       u16 count1;
       TL0 = 0x18;          //设置定时初值1MS
       TH0 = 0xFC;
      
       count1++;
       if(count1 == 1000)
       {
              temp_dat = Tempget();
              DACModePro();
              count1 = 0;
       }
}

好了,第十二届蓝桥杯单片机程序题的分享就到这里,感谢大家o(* ̄ ̄*)ブ。




菜鸟
2022-07-29 14:27:06     打赏
2楼

谢谢分享


专家
2022-07-29 22:56:38     打赏
3楼

感谢分享


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

谢谢分享,学习了。


菜鸟
2022-08-11 09:19:46     打赏
5楼

谢谢分享


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

十分感谢你的分享


高工
2022-08-17 22:54:14     打赏
7楼

解析的挺到位的


专家
2022-08-17 23:46:32     打赏
8楼

谢谢分享


高工
2022-08-20 19:21:50     打赏
9楼

都是大佬,厉害


工程师
2022-09-19 22:53:56     打赏
10楼

解析的非常到位


共20条 1/2 1 2 跳转至

回复

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