论坛» 高校专区» 坤创E-Geek/天科大新电社

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

高工
2022-07-22 19:00 1楼

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

2856e7b5a85f489b945efbbee58dd77f.png

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

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

#include  #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<
        

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

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 2楼

谢谢分享

专家
2022-07-29 22:56 3楼

感谢分享

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

谢谢分享,学习了。

菜鸟
2022-08-11 09:19 5楼

谢谢分享

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

十分感谢你的分享

高工
2022-08-17 22:54 7楼

解析的挺到位的

专家
2022-08-17 23:46 8楼

谢谢分享

高工
2022-08-20 19:21 9楼

都是大佬,厉害

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

解析的非常到位

共20条 1/2 1 2 跳转至

回复

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