目录
前言
赛题感受
难点分析
程序设计题目与代码解析
题目
代码解析
main.c
ds1302.c
ds1302.h
onewire.c
onewire.h
iic.c
iic.h
总结(附客观题目与答案)
前言
赛题感受
刚刚结束了第14届蓝桥杯大赛,本人参加的是电子类的单片机设计与开发项目,个人感受是本届的相较于往年省赛题程序设计题,难度加大了不少,堪比国赛。一些功能在往年国赛题也出现过,比如最大值和平均值,刷过国赛题的选手应该在第十二届的国赛题里见过。
本届光是数码管显示界面就有六个,除了国赛才考的超声波和串口,几乎所有模块都用上了。本届官方提供的底层驱动代码也全部都用上了,而且底层驱动代码的.h文件和引脚定义都需要自己手动添加,考查到了模块化编程和单片机原理图阅读。
难点分析
难点1:通过adc通道对光敏电阻亮暗状态的检测来开启测温度测湿度;
难点2:回显模式显示温湿度最大值和平均值;
难点3:长按2s后松手来清除所有数据;
程序设计题目与代码解析
题目
代码解析
main.c
#include#include "DS1302.H" #include "ONEWIRE.H" #include "IIC.H" //ne555测频率需将跳线帽接到P34和SGNAL口 bit key_line,range_mode,clean_flag,L1,L2,L3,L4,L5,L6,l4_flag; unsigned char time_8ms,time_20ms,tick_20ms,time_200ms,time_100ms,work_mode,back_mode,down_flag,SMG[8]; unsigned int shi,fen,miao,temp,temp_read,frequence,count,water,water_read,time_1s,light,time_3s,time_2s,clean_time; unsigned int range_num,shi_back,fen_back,water_aver,water_max,water_sum,temp_aver,temp_max,temp_sum; int temp_num=30; unsigned char code t_display[]={ //标准字库 // 0 1 2 3 4 5 6 7 8 9 A B C D E F 0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71, //black - H J K L N o P U t G Q r M y 0x00,0x40,0x76,0x1E,0x70,0x38,0x37,0x5C,0x73,0x3E,0x78,0x3d,0x67,0x50,0x37,0x6e, 0xBF,0x86,0xDB,0xCF,0xE6,0xED,0xFD,0x87,0xFF,0xEF,0x46}; //0. 1. 2. 3. 4. 5. 6. 7. 8. 9. -1 unsigned char code t_com[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80}; //位码 void smg(unsigned char wei,unsigned char duan) //数码管刷新函数,放在定时器中断刷新 { P0=0xFF; P2=0xC0;P0=t_com[wei];P2=0x00; P2=0xE0;P0=~t_display[duan];P2=0x00; } void led_set(void) //led初始化函数 { P0=0xFF;P2=0x80; P00=~L1;P01=~L2; P02=~L3;P03=~L4; P04=~L5;P05=~L6; P2=0x00; } void Ds1302_Init(unsigned char shi_set,fen_set,miao_set) //DS1302时钟初始化函数 { Write_Ds1302_Byte(0x8E,0x00); Write_Ds1302_Byte(0x84,shi_set); Write_Ds1302_Byte(0x82,fen_set); Write_Ds1302_Byte(0x80,miao_set); Write_Ds1302_Byte(0x8E,0x80); shi=Read_Ds1302_Byte(0x85); fen=Read_Ds1302_Byte(0x83); miao=Read_Ds1302_Byte(0x81); } void Time_Display(void) //时间显示界面函数 { SMG[0]=shi/16; SMG[1]=shi%16; SMG[2]=17; SMG[3]=fen/16; SMG[4]=fen%16; SMG[5]=17; SMG[6]=miao/16; SMG[7]=miao%16; } void C_Display(void) //回显界面——温度回显函数 { SMG[0]=12; SMG[1]=16; if(range_num>0) //触发采集次数大于0就显示,等于0就熄灭 { SMG[2]=temp_max/10; SMG[3]=temp_max%10; SMG[4]=17; SMG[5]=temp_aver/100; SMG[6]=temp_aver/10%10+32; SMG[7]=temp_aver%10; } else{SMG[2]=16;SMG[3]=16;SMG[4]=16;SMG[5]=16;SMG[6]=16;SMG[7]=16;} } void H_Display(void) //回显界面——湿度回显函数 { SMG[0]=18; SMG[1]=16; if(range_num>0) //触发采集次数大于0就显示,等于0就熄灭 { SMG[2]=water_max/10; SMG[3]=water_max%10; SMG[4]=17; SMG[5]=water_aver/100; SMG[6]=water_aver/10%10+32; SMG[7]=water_aver%10; } else{SMG[2]=16;SMG[3]=16;SMG[4]=16;SMG[5]=16;SMG[6]=16;SMG[7]=16;} } void F_Display(void) //回显界面——时间回显函数 { SMG[0]=15; SMG[1]=range_num/10; SMG[2]=range_num%10; if(range_num>0) //触发采集次数大于0就显示,等于0就熄灭 { SMG[3]=shi_back/16; SMG[4]=shi_back%16; SMG[5]=17; SMG[6]=fen_back/16; SMG[7]=fen_back%16; } else{SMG[3]=16;SMG[4]=16;SMG[5]=16;SMG[6]=16;SMG[7]=16;} } void P_Display(void) //温度参数显示函数 { SMG[0]=24; SMG[1]=16; SMG[2]=16; SMG[3]=16; SMG[4]=16; SMG[5]=16; if(temp_num>9)SMG[6]=temp_num/10;else{SMG[6]=16;} //长度不足两位高位熄灭 SMG[7]=temp_num%10; } void E_Display(void) //温度湿度测量界面函数 { SMG[0]=14; SMG[1]=16; SMG[2]=16; SMG[3]=temp/10; SMG[4]=temp%10; SMG[5]=17; if(water>=10&&water<=90) //湿度为有效值时正常显示,为无效值时显示AA { SMG[6]=water/10; SMG[7]=water%10; } else{SMG[6]=10;SMG[7]=10;} } void key_task(void) { } void key_work(void) //按键工作函数 { if(key_line==0) //20ms读一次按键 { P32=1;P33=0;P44=1;P42=1; if(P44==0) //S4 //按键消抖 { while(!P44) //防止在时间显示界面按下按键程序困在while里时间不能读 { key_task(); shi=Read_Ds1302_Byte(0x85); fen=Read_Ds1302_Byte(0x83); miao=Read_Ds1302_Byte(0x81); } if(++work_mode>2)work_mode=0; //S4切换界面 } if(P42==0) //S8 { while(!P42) { key_task(); shi=Read_Ds1302_Byte(0x85); fen=Read_Ds1302_Byte(0x83); miao=Read_Ds1302_Byte(0x81); } if(work_mode==2){if(++temp_num>99)temp_num=0;} //S8在参数界面下按下温度参数加1 } } if(key_line==1) { P32=0;P33=1;P44=1;P42=1; if(P44==0) //S5 { while(!P44) { key_task(); shi=Read_Ds1302_Byte(0x85); fen=Read_Ds1302_Byte(0x83); miao=Read_Ds1302_Byte(0x81); } if(work_mode==1){if(++back_mode>2)back_mode=0;} //S5回显子界面切换 } if(P42==0) //S9 { while(!P42) { key_task(); shi=Read_Ds1302_Byte(0x85); fen=Read_Ds1302_Byte(0x83); miao=Read_Ds1302_Byte(0x81); if(back_mode==2)clean_flag=1; //长按触发清除的时间标志位 } if(work_mode==2){if(--temp_num<0)temp_num=99;} //S9在参数界面下按下温度参数减1 if(back_mode==2)clean_flag=0; //松开清除的时间标志位归0 } } } void Timer1_Init(void) //1毫秒@12.000MHz { AUXR |= 0x40; //定时器时钟1T模式 TMOD &= 0x0F; //设置定时器模式 TL1 = 0x20; //设置定时初始值 TH1 = 0xD1; //设置定时初始值 TF1 = 0; //清除TF1标志 TR1 = 1; //定时器1开始计时 ET1 = 1; EA = 1; } void Timer0_Init(void) //定时器0设置为计数模式测ne555频率 { TMOD = 0x07; //设置定时器模式 TL0 = 0xFF; //设置定时初始值 TH0 = 0xFF; //设置定时初始值 TF0 = 0; //清除TF1标志 TR0 = 1; //定时器1开始计时 ET0 = 1; EA = 1; } void read_task(void) //读时间温湿度函数 { if(tick_20ms>20) //20ms读一次时间和温度(亲测温度超过30ms读无效) { tick_20ms=0; shi=Read_Ds1302_Byte(0x85); fen=Read_Ds1302_Byte(0x83); miao=Read_Ds1302_Byte(0x81); temp_read=(read_temp()*1); } if(frequence>=200&&frequence<=2000){water_read=((frequence*2/45)+1.2);} //将ne555频率转化为湿度 else{water_read=0;} } void task_work(void) //任务执行函数 { if(range_mode==0) //当光明电阻一直处于亮状态 或 暗状态超过3s 时可以显示时间 回显 参数 { //光敏电阻从亮到暗的下降沿触发温湿度测量 if(light>125)down_flag=0; else{if(down_flag<=10)down_flag++;} if(down_flag==10) { range_mode=1; //此时从亮到暗触发温湿度采集 range_num++; if(range_num>=2) //当触发次数大于2时 若测量温度比上一次测量温度大L6亮 { if(temp_read>temp&&water_read>water)L6=1; else{L6=0;} } temp=temp_read;water=water_read; //采集温度和湿度 if(temp>temp_max)temp_max=temp; //温度最大值采集 if(water>water_max)water_max=water; //湿度最大值采集 temp_sum=temp_sum+temp;temp_aver=((temp_sum*10)/range_num); //温度平均值采集 water_sum=water_sum+water;water_aver=((water_sum*10)/range_num); //湿度平均值采集 shi_back=shi;fen_back=fen; //时分采集 } //按键控制显示 调用各个显示函数 if(work_mode==0){L1=1;L2=0;L3=0;Time_Display();back_mode=0;} if(work_mode==1) { L1=0;L2=1;L3=0; if(back_mode==0){C_Display();} if(back_mode==1){H_Display();} if(back_mode==2) { F_Display(); if(clean_time>=2000) //长按大于2s松开后清除所有数据 { time_2s=0;clean_time=0; range_num=0; temp_aver=0;temp_max=0;temp_sum=0; water_aver=0;water_max=0;water_sum=0; shi_back=0;fen_back=0; } } } if(work_mode==2){L1=0;L2=0;L3=0;P_Display();} } else //此时从亮到暗触发温湿度采集 显示采集温湿度 { L1=0;L2=0;L3=1; E_Display(); if(temp>temp_num)l4_flag=1; //当采集温度大于温度参数时,L4 0.1s闪烁 else{l4_flag=0;} if(water>=10&&water<=90)L5=0; else{L5=1;} } } int main() { P2=0x80;P0=0xFF;P2=0x00; //关闭led外设 P2=0xA0;P0=0x00;P2=0x00; //关闭蜂鸣器继电器外设 light=adc(0x01)*1.961; //光敏电阻上电读数,防止上电触发采集 Ds1302_Init(0x13,0x03,0x05); //时钟初始化 Timer1_Init();Timer0_Init(); while(1) { if(range_mode==0)key_work(); //温湿度采集界面下按键无效 read_task(); //读取任务调用 } } void server1(void) interrupt 3 //定时器1中断 { if(++time_8ms==8)time_8ms=0; //8ms刷新八个数码管 smg(time_8ms,SMG[time_8ms]); if(++time_20ms>20){time_20ms=0;key_line=~key_line;} //20ms读一次按键 if(++time_200ms>200){time_200ms=0;light=adc(0x01)*1.961;} //200ms读一次光敏电阻 if(++time_1s>1000){time_1s=0;frequence=count;count=0;} //1s读一次定时器0计数值即为ne555频率 if(clean_flag==1){time_2s++;} //长按S9时计时 else{if(time_2s>=2000)clean_time=time_2s;else{time_2s=0;}} //松开S9后计算长按时间 if(range_mode==1){if(++time_3s>3000){time_3s=0;range_mode=0;}} //采集功能触发后3s内不可再次触发 if(l4_flag==1){if(++time_100ms>100){time_100ms=0;L4=~L4;}} //L4 0.1s闪烁 else{L4=0;} tick_20ms++; //20ms读取时间温湿度时间 task_work(); //任务函数调用(放在中断以防止在时间显示界面下按下按键时钟不走动) led_set(); //led初始化 } void server0(void) interrupt 1 //定时器0测ne555频率 { count++; }
ds1302.c
#include#include "intrins.h" #include "ds1302.h" sbit SCK=P1^7; sbit SDA=P2^3; sbit RST=P1^3; // void Write_Ds1302(unsigned char temp) { unsigned char i; for (i=0;i<8;i++) { SCK = 0; SDA = temp&0x01; temp>>=1; SCK=1; } } // void Write_Ds1302_Byte( unsigned char address,unsigned char dat ) { RST=0; _nop_(); SCK=0; _nop_(); RST=1; _nop_(); Write_Ds1302(address); Write_Ds1302(dat); RST=0; } // unsigned char Read_Ds1302_Byte ( unsigned char address ) { unsigned char i,temp=0x00; RST=0; _nop_(); SCK=0; _nop_(); RST=1; _nop_(); Write_Ds1302(address); for (i=0;i<8;i++) { SCK=0; temp>>=1; if(SDA) temp|=0x80; SCK=1; } RST=0; _nop_(); SCK=0; _nop_(); SCK=1; _nop_(); SDA=0; _nop_(); SDA=1; _nop_(); return (temp); }
ds1302.h
#ifndef __DS1302_H__ #define __DS1302_H__ void Write_Ds1302_Byte( unsigned char address,unsigned char dat ); unsigned char Read_Ds1302_Byte ( unsigned char address ); #endif
onewire.c
#include#include "intrins.h" #include "onewire.h" sbit DQ=P1^4; // void Delay_OneWire(unsigned int t) { unsigned char i; while(t--){ for(i=0;i<12;i++); } } // 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); } // 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; } // 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; } float read_temp(void) { unsigned char low,high; unsigned int tem; float temp; init_ds18b20(); Write_DS18B20(0xCC); Write_DS18B20(0x44); Delay_OneWire(200); init_ds18b20(); Write_DS18B20(0xCC); Write_DS18B20(0xBE); Delay_OneWire(200); low=Read_DS18B20(); high=Read_DS18B20(); high&=0x0F; tem=(high<<8)+low; temp=tem*0.0625; return temp; }
onewire.h
#ifndef __ONEWIRE_H__ #define __ONEWIRE_H__ float read_temp(void); #endif
iic.c
#include#include "intrins.h" #include "iic.h" sbit sda=P2^1; sbit scl=P2^0; #define DELAY_TIME 5 // static void I2C_Delay(unsigned char n) { do { _nop_();_nop_();_nop_(); } while(n--); } // void I2CStart(void) { sda = 1; scl = 1; I2C_Delay(DELAY_TIME); sda = 0; I2C_Delay(DELAY_TIME); scl = 0; } // void I2CStop(void) { sda = 0; scl = 1; I2C_Delay(DELAY_TIME); sda = 1; I2C_Delay(DELAY_TIME); } // void I2CSendByte(unsigned char byt) { unsigned char i; for(i=0; i<8; i++){ scl = 0; I2C_Delay(DELAY_TIME); if(byt & 0x80){ sda = 1; } else{ sda = 0; } I2C_Delay(DELAY_TIME); scl = 1; byt <<= 1; I2C_Delay(DELAY_TIME); } scl = 0; } // unsigned char I2CReceiveByte(void) { unsigned char da; unsigned char i; for(i=0;i<8;i++){ scl = 1; I2C_Delay(DELAY_TIME); da <<= 1; if(sda) da |= 0x01; scl = 0; I2C_Delay(DELAY_TIME); } return da; } // unsigned char I2CWaitAck(void) { unsigned char ackbit; scl = 1; I2C_Delay(DELAY_TIME); ackbit = sda; scl = 0; I2C_Delay(DELAY_TIME); return ackbit; } // void I2CSendAck(unsigned char ackbit) { scl = 0; sda = ackbit; I2C_Delay(DELAY_TIME); scl = 1; I2C_Delay(DELAY_TIME); scl = 0; sda = 1; I2C_Delay(DELAY_TIME); } unsigned char adc(unsigned char addr) { unsigned char temp; I2CStart(); I2CSendByte(0x90); I2CWaitAck(); I2CSendByte(addr); I2CWaitAck(); I2CStart(); I2CSendByte(0x91); I2CWaitAck(); temp=I2CReceiveByte(); I2CWaitAck(); I2CStop(); return temp; }
iic.h
#ifndef __IIC_H__ #define __IIC_H__ unsigned char adc(unsigned char addr); #endif
总结(附客观题目与答案)
以上代码仅是个人理解的逻辑思路和分析加上实验室学长教授的代码模块,不是完美答案,最终可以实现所有功能。
本届省赛已经预示了未来蓝桥杯电子类的试题难度将会上升,建议还是要熟悉所有模块,学习一些基本的c语言算法。一定要多刷题,历届省赛题和国赛题,本届省赛难度大主要是要实现的功能多,综合了前几届几乎所有省赛要实现的功能,综合性强。
最后祝大家在本届蓝桥杯单片机省赛中取得好成绩!