# 写在前面的 为了参加这次的模拟赛我真实下了血本呀!因为之前换了一台MacBook Pro 然后在虚拟机里运行keil用不了。我无奈下只有再去买个台式电脑,好在这次成绩也比较理想,我们那组的第四名!好了,下面我们开始做题! # 赛题 赛题的话我已经在发出来了,需要看的话进我下面这篇文章吧。 蓝桥杯----2020年模拟试题 下面是最近出的蓝桥杯2020年的模拟试题需要的可以保存 # 分析 ## 理解题意 题中的大意就是需要我们做一个人体温度的检测系统,需要输入、检测、显示、报警。所以我们需要用到的模块就有数码管、DS18B20、矩阵键盘、RTC、蜂鸣器。 并且还有个考点就是温度的小数显示。 ## 难点分析 ### DS18B20显示小数 我们在之前用的DS18B20都是用来显示整数的。而这里我们需要用他来显示小数我们该怎么做呢? 关于DS18B20显示整数请参考下面这篇文章: 蓝桥杯-单片机经验分享-18B20 前言经过几天对107D开发版上的18B20的琢磨,终于在今天顺利的让他正确的显示了温度。下面写一些在这过程中遇到的... 我们都知道,在读取DS18B20的时候都是先读low再读high,当然!我们读取小数也是这样的。但是,我们在显示整数的时候需要对low和high处理一下,就是去high的低4位和low的高4位。最后把他们组合在一起就行了。 那小数我们怎么办?从DS18B20的芯片手册我们知道,low的低4位就是小数位。 所以我们在处理数据的时候可以先把整数读取出来,然后我们在对low的低4位也就是小数位来处理。那我们的小数位需要怎么处理呢?小数位我们需要乘上的分辨率。我们知道,CT107D这块板子上的DS18B20的分辨率是0.0625.所以,我们在处理数据的时候直接把low的低4位乘以0.0625,然后再保留两位就行了。 ### 数码管输入工号 在题中,还有个输入工号的功能,当你按下数字的按键后,则自动跳转到工号输入界面。一共有8位,未输入的位用-代替,按下对应的数字以后则显示对应的数字。难点主要是移位,我们在这里可以用一个存储当前的输入到哪一位的变量存储就行了。 ### 难点总结 个人觉得这题的难点就以上这些,当然,还有个最大的难点就是整体的逻辑。这个必须要清晰! # 代码分享 接下来我就贴出我自己写的代码。 ### ds1302.c ``` #include "ds1302.h" void Write_Ds1302_Byte(unsigned char temp) { unsigned char i; for (i=0;i<8;i++) { SCK=0; SDA=temp&0x01; temp>>=1; SCK=1; } } void Write_Ds1302( unsigned char address,unsigned char dat ) { RST=0; _nop_(); SCK=0; _nop_(); RST=1; _nop_(); Write_Ds1302_Byte(address); Write_Ds1302_Byte(dat); RST=0; } unsigned char Read_Ds1302 ( unsigned char address ) { unsigned char i,temp=0x00; RST=0; _nop_(); SCK=0; _nop_(); RST=1; _nop_(); Write_Ds1302_Byte(address); for (i=0;i<8;i++) { SCK=0; temp>>=1; if(SDA) temp|=0x80; SCK=1; } RST=0; _nop_(); RST=0; SCK=0; _nop_(); SCK=1; _nop_(); SDA=0; _nop_(); SDA=1; return (temp); } void Write_time(unsigned char hour,min,sec) { Write_Ds1302(0x8e,1); Write_Ds1302(0x84,((hour / 10)<<4) | (hour % 10)); Write_Ds1302(0x82,((min / 10)<<4) | (min % 10)); Write_Ds1302(0x80,((sec / 10)<<4) | (sec % 10)); Write_Ds1302(0x8e,0); } unsigned char * Read_time() { unsigned char time_num[3]={0}; time_num[0] = Read_Ds1302(0x85); time_num[0] = (time_num[0] >> 4) * 10 + (time_num[0] & 0x0f); time_num[1] = Read_Ds1302(0x83); time_num[1] = (time_num[1] >> 4) * 10 + (time_num[1] & 0x0f); time_num[2] = Read_Ds1302(0x81); time_num[2] = (time_num[2] >> 4) * 10 + (time_num[2] & 0x0f); return time_num; } ``` ### DS1302.h ``` #ifndef __DS1302_H #define __DS1302_H #include "reg52.h" #include "intrins.h" sbit SCK=P1^7; sbit SDA=P2^3; sbit RST = P1^3; // DS1302复位 unsigned char * Read_time(); void Write_time(unsigned char hour,min,sec); #endif ``` ### onewire.c ``` #include "reg52.h" sbit DQ = P1^4; ////��������ʱ���� void Delay_OneWire(unsigned int t) { unsigned char i; while(t--){ for(i=0;i<12;i++); } } void Delay_us(void) { unsigned char i; i = 30; while (--i); } //ͨ����������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; // unsigned char index = 0; for(i=0;i<8;i++) { DQ = 0; dat >>= 1; DQ = 1; Delay_us(); if(DQ) { dat |= 0x80; } Delay_OneWire(5); } return dat; } //DS18B20��ʼ�� bit init_ds18b20(void) { bit initflag = 0; DQ = 1; Delay_OneWire(12); DQ = 0; Delay_OneWire(80); // ��ʱ����480us DQ = 1; Delay_OneWire(10); // 14 initflag = DQ; // initflag����1��ʼ��ʧ�� Delay_OneWire(5); return initflag; } //DS18B20�¶Ȳɼ��������� unsigned char check_1[2] = {0}; unsigned char * rd_temperature(void) { float tem3; unsigned char low,hight,tem,tem2,i; init_ds18b20(); Write_DS18B20(0xCC); Write_DS18B20(0x44); for(i = 0;i < 200;i ++) { Delay_us(); } init_ds18b20(); Write_DS18B20(0xCC); Write_DS18B20(0xBE); for(i = 0;i < 200;i ++) { Delay_us(); } low = Read_DS18B20(); hight = Read_DS18B20(); tem3 = (low & 0x0f) * 0.0625; tem3 *= 10; tem2 = (int)tem3; tem = hight << 4; tem |= (low>>4); check_1[0] = tem; check_1[1] = tem2; if(check_1[0] == 85) { check_1[0] = 0; check_1[1] = 0; } return check_1; } ``` ### onewire.h ``` #ifndef _ONEWIRE_H #define _ONEWIRE_H unsigned char Read_DS18B20(void); unsigned char * rd_temperature(void); #endif ``` ## main.c (主程序) ``` #include #include #include #include #include #define uint unsigned int #define uchar unsigned char #define LED_data P2 = (P2 & 0x1f) | 0x80;P0 #define buzz_data P2 = (P2 & 0x1f) | 0xa0;P0 #define smg_wei_data P2 = (P2 & 0x1f) | 0xc0;P0 #define smg_duan_data P2 = (P2 & 0x1f) | 0xe0;P0 uchar smg_duan[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff,0xbf}; //数码管段码缓存 uchar smg_wei[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80}; //数码管位码缓存 uchar smg_count[]={0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}; //刷管显示缓存 uint tem_time = 0; //0.5秒刷新温度计时 uint tem_time1 = 0; //5秒测量时间 uchar flag_S13 = 0; //按钮13 uchar display_state = 0; //显示的状态 uchar ID_num[8]={11,11,11,11,11,11,11,11}; //工号缓存 uchar ID_num_count = 0; //工号次数 uchar buzz_count = 0; //蜂鸣器当前状态 uchar tem_count = 0; //温度检测是否清零 //定时器初始化 void Timer0Init(void) //1毫秒@12.000MHz { AUXR |= 0x80; //定时器时钟1T模式 TMOD &= 0xF0; //设置定时器模式 TL0 = 0x20; //设置定时初值 TH0 = 0xD1; //设置定时初值 TF0 = 0; //清除TF0标志 TR0 = 1; //定时器0开始计时 ET0 = 1; //开定时器中断 EA = 1; //开总中断 } //数码管显示程序 void smg_display() { static uchar i = 0; //静态变量 存储显示了几位了 smg_duan_data = 0xff; //消影 smg_wei_data = smg_wei[i]; //送入相应的位码 smg_duan_data = smg_count[i]; //送入相应的段码 if(++i == 8) //加1判断是否到8如果到8则置0 i = 0; } //键盘扫描 void key_scan() { uchar PPP,PPP1,x; static uchar state = 0; //状态机的当前状态存储 PPP = P3 | 0xf0; //读取高四位 P3 = 0xf0; //拉低高四位 P42 = P44 = 1; //板子上P36和P37是用P42和P44代替的 if(P42 == 0) // { // PPP &= 0xbf; // } // if(P44 == 0) //判断P42和P44并且把数据给PPP { // PPP &= 0x7f; // } // PPP1 = P3 | 0xcf; // PPP &= PPP1; // switch (state) { case 0: //状态0 { if(PPP != 0xff) { state = 1; //判断是否有按键按下,如果有则进入状态1 没有则退出 } break; } case 1: //状态1 { if(PPP != 0xff) //消抖过后如果有按键按下则判断键值 { switch(PPP) { case 0x77: //S4 { //这题用不上S4,所以不做任何操作 break; } case 0x7b: //S5 8 { if(display_state != 3) { display_state = 2; //多加了一个判断,防止误操作 } if(display_state == 2) { ID_num[ID_num_count] = 8; //此按键键值为8 ID_num_count++; } break; } case 0x7d: //S6 4 { if(display_state != 3) { display_state = 2; } if(display_state == 2) { ID_num[ID_num_count] = 4; //此按键键值为4 ID_num_count++; } break; } case 0x7e: //S7 0 { if(display_state != 3) { display_state = 2; } if(display_state == 2) { ID_num[ID_num_count] = 0; //此按键键值为0 ID_num_count++; } break; } case 0xb7: //S8 { break; } case 0xbb: //S9 9 { if(display_state != 3) { display_state = 2; } if(display_state == 2) { ID_num[ID_num_count] = 9; //此按键键值为9 ID_num_count++; } break; } case 0xbd: //S10 5 { if(display_state != 3) { display_state = 2; } if(display_state == 2) { ID_num[ID_num_count] = 5; //此按键键值为5 ID_num_count++; } break; } case 0xbe: //S11 1 { if(display_state != 3) { display_state = 2; } if(display_state == 2) { ID_num[ID_num_count] = 1; //此按键键值为1 ID_num_count++; } break; } case 0xd7: //S12 { break; } case 0xdb: //S13 { if(display_state != 3) //只有在显示模式不是温度检测的时候才执行开始时钟 { flag_S13 = 1; display_state = 1; for(x = 0;x < 8;x ++) { ID_num[x] = 11; //如果在输入工号的过程中按下此按键 } //则把工号缓存数组请空 ID_num_count = 0; } break; } case 0xdd: //S14 6 { if(display_state != 3) { display_state = 2; } if(display_state == 2) { ID_num[ID_num_count] = 6; //此按键键值为6 ID_num_count++; } break; } case 0xde: //S15 2 { if(display_state != 3) { display_state = 2; } if(display_state == 2) { ID_num[ID_num_count] = 2; //此按键键值为2 ID_num_count++; } break; } case 0xe7: //S16 { break; } case 0xeb: //S17 { break; } case 0xed: //S18 7 { if(display_state != 3) { display_state = 2; } if(display_state == 2) { ID_num[ID_num_count] = 7; //此按键键值为7 ID_num_count++; } break; } case 0xee: //S19 3 { if(display_state != 3) { display_state = 2; } if(display_state == 2) { ID_num[ID_num_count] = 3; //此按键键值为3 ID_num_count++; } break; } } if(ID_num_count == 8) { ID_num_count = 0; //判断工号是否输入完毕,如果是则进入温度检测并清零工号次数缓存 display_state =3; } state = 2; } else //如果是抖动则状态回到0 { state = 0; } break; } case 2: { if(PPP == 0xff) //判断是否还在按下状态 { state = 0; } break; } } } //读取温度并更新温度数据 void Read_tem() { static uchar *p; //用于存储所读取到的温度数据 uint x = 0; if(tem_count == 0) //每次进入该程序先执行一次 { smg_count[0] = 0xc6; //第一位显示C smg_count[1] = smg_count[2] = smg_count[3] = smg_count[4] = 0xff; //中间几位不显示 tem_count =1; p = rd_temperature(); //读取温度 smg_count[5] = smg_duan[*p/10]; //更新显示数据 smg_count[6] = (smg_duan[*p%10])& 0x7f; smg_count[7] = smg_duan[*(p+1)]; } if(tem_time >= 500 && tem_time1 == 0) //0.5s 更新一次温度的数据 { p = rd_temperature(); smg_count[5] = smg_duan[*p/10]; smg_count[6] = (smg_duan[*p%10])& 0x7f; smg_count[7] = smg_duan[*(p+1)]; tem_time = 0; } if(tem_time1 == 1) { x = ((*p)*10) + *(p+1); //把数据转换成我们能比较的数据 if(x > 370 && buzz_count == 0) //如果温度大于37度则报警并且报警5s 停止 { buzz_data = 0x40; buzz_count = 1; } } } //更新时间的数据 void Read_timer() { unsigned char *p; p = Read_time(); //读取时间数据 smg_count[0] = smg_duan[*p/10]; //更新时间数据 smg_count[1] = smg_duan[*p%10]; smg_count[2] = 0xbf; smg_count[3] = smg_duan[*(p+1)/10]; smg_count[4] = smg_duan[*(p+1)%10]; smg_count[5] = 0xbf; smg_count[6] = smg_duan[*(p+2)/10]; smg_count[7] = smg_duan[*(p+2)%10]; } //所有程序的初始化 void allinit() { Write_time(12,00,00); //写入时间 Timer0Init(); //定时器0初始化 LED_data = 0xff; //关闭所有不需要用到的器件 buzz_data = 0x00; smg_count[0] = smg_duan[1]; //显示12:00:00 smg_count[1] = smg_duan[2]; smg_count[2] = 0xbf; smg_count[3] = smg_duan[0]; smg_count[4] = smg_duan[0]; smg_count[5] = 0xbf; smg_count[6] = smg_duan[0]; smg_count[7] = smg_duan[0]; } void main() { uchar i = 0,x = 0; allinit(); while(1) { if(display_state == 1 && flag_S13 == 1) //显示状态1 { if(i == 0) { Write_time(12,00,00); //写一次时间 i = 1; } Read_timer(); //更新时间数据 } else if(display_state == 2) //显示状态2 { for(x = 0;x < 8;x ++) { smg_count[x] = smg_duan[ID_num[x]]; //工号输入 } } else if (display_state == 3) //显示状态3 { Read_tem(); //更新温度 if(tem_time1 == 2) //当显示5S后清空数据为下一次显示做准备 { display_state = 1; tem_time1 = 0; tem_time = 0; buzz_data = 0x00; buzz_count = 0; tem_count = 0; for(x = 0;x < 8;x ++) { ID_num[x] = 11; } smg_count[0] = smg_duan[1]; smg_count[1] = smg_duan[2]; smg_count[2] = 0xbf; smg_count[3] = smg_duan[0]; smg_count[4] = smg_duan[0]; smg_count[5] = 0xbf; smg_count[6] = smg_duan[0]; smg_count[7] = smg_duan[0]; } } } } void timer0() interrupt 1 { static uchar i = 0; static uint x = 0; i++; tem_time++; if (display_state == 3) { x++; } if(x >= 5000 && x < 5010) { tem_time1 = 1; } if(x >= 10000) { tem_time1 = 2; x = 0; } if(i >= 10) //状态机消抖方式10ms { P3 = 0x0f; P42 = P44 =0; key_scan(); i = 0; } smg_display(); //数码管显示 } ``` 好了,以上就是2020年模拟赛的试题了。希望各位在比赛中取得好成绩哦! # 武汉加油!中国加油! Last modification:April 4, 2020 © Reprint prohibited Support Appreciate the author AliPayWeChat Like 0 如果觉得我的文章对你有用,请随意赞赏