大家好,今天给大家分享2022年第十三届的蓝桥杯省赛程序题。先上题目:
今年的省赛程序题难度不算大,使用到的模块有矩阵按键,蜂鸣器、继电器、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(); }
做好这些准备工作之后,我们就可以开始审题了:
显示功能这一块要求三个界面,我们可以写三个显示函数并将它们单独封装,方便后面调用:
/***************温度数据显示函数******************/ 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(); }
然后看按键功能要求:
对于按键扫描函数也是尽量只做数据处理,不调用其他函数,设置了几个变量,在按键按下时,只对变量进行处理,之后再写一个函数,根据变量的值进行显示函数的调用,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的控制了:
打开定时器,对温度以及时间进行判断,并选择要打开的模块,对函数进行封装;注意,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(* ̄▽ ̄*)ブ。