赞
踩
我采取的做题原则是先易后难,先做熟悉的点再做看着比较陌生的考点,基本模块的处理要先考虑周全,不然后面写到后面越多,改动越麻烦。
动态数码管扫描---定时器1(配置为1ms定时模式)
矩阵按键---P42 P44 P33 P32 定时器1(10ms刷新一次按键)
NE555频率测量---P34 定时器0
实时时钟---死循环刷新
温度测量---定时器1(500ms进入一次刷新,但是不采集)
光敏电阻---定时器1(20ms进入刷新一次)
实际上考虑的主要就是考虑一些可能会共用的IO口,和定时器资源的分配问题
系统初始化—>动态数码管扫描—>小矩阵按键—>实时时钟的初始化和读取—>温度数据的读取(整数)—>模拟输入光敏电阻电压的检测—>频率测量—>采集数据的处理
—>LED灯光指示
频率测量随RB4旋钮变化不正常(很容易过大或过小成为无效数据)
采样温度和湿度数据的处理,求当前数组的最大值和平均值。
#include "ds1302.h" #include "onewire.h" #include "iic.h" sbit R3 = P3^2; sbit R4 = P3^3; sbit C2 = P4^2; sbit C1 = P4^4; //===================函数声明======================== void systemInit(); void select573(unsigned char channel); void seg_dispbit(unsigned char pos,unsigned char *buf); void seg_proc();//数码管显示 void Timer1_Init(void); //1毫秒@12.000MHz unsigned char key_read(); void key_proc();//按键扫描 void ds1302_config(); void ds1302_proc();//实时时钟读取 unsigned int read_temp(); void temp_proc();//温度采集 unsigned char ADC_PCF8591(unsigned char channel);//光敏电阻 void ADC_proc();//检测光敏电阻的亮度变化 void Timer0_Init(void); //NE555频率采集 void led_disp(unsigned char ucled); void led_proc();//指示灯和报警灯 void fre_to_wet(); void led_proc(); //======================全局变量===================== unsigned long ulms = 0;//数据很长可以计时很久,这种定义前两个字母为数据结构,后两个字母为含义 unsigned char date_nodot[10] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90}; unsigned char date_dot[10] = {0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10}; unsigned char time_now[3] = {0x13,0x20,0x40}; unsigned char sample_time[3];//表示最近一次采样时间 unsigned char ucpos = 0,ucbuf[8],seg_model=0,recall_model = 0;//数码管相关变量 bit fre_work = 1;//用于表示采集到的频率是否有效,1为有效,0为无效 bit wet_temp_model = 0;//表示是否在温度和湿度显示界面 unsigned int wet_temp_time = 0;//用于表示停留在温度湿度界面的时间 bit S9_flag = 0; unsigned int S9_time = 0; unsigned char sample_count = 0;//表示采集次数 unsigned int uitemp_now = 23;//扩大了10倍的温度值 unsigned int uitemp_max = 28,uitemp_ave =232; unsigned int uitemp_refer = 30;//没有扩大的温度参数 unsigned int uitemp_sample[100]; unsigned int uiwet_now = 48; unsigned int uiwet_max = 68,uiwet_ave =504; //扩大了10倍的温度平均值 unsigned int uiwet_sample[100]; unsigned int frequency = 0; unsigned char ADC_old = 0;//表示上一次的光敏值 unsigned char count_key=0; unsigned int count_temp=0; unsigned int count_f = 0,count_t = 0;//频率 unsigned char count_adc= 0; unsigned char led_stat = 0xff; bit L4_flag = 0; bit L4 = 0; unsigned long ul100ms; void main() { systemInit(); Timer0_Init(); Timer1_Init(); ds1302_config(); while(1) { fre_to_wet(); seg_proc(); key_proc(); ADC_proc(); led_proc(); } } //====================初始化========================= void systemInit() { P0 = 0xff; //关灯 select573(4); select573(0); P0 = 0x00; //关蜂鸣器,关继电器 select573(5); select573(0); P0 = 0xff; //关灯 select573(7); select573(0); } void select573(unsigned char channel) { switch(channel) { case 0:P2 = P2&0x1f;break; 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; } } //====================数码管显示================= void seg_dispbit(unsigned char pos,unsigned char *buf) { P0 = 0xff; //消影 select573(7); select573(0); P0 = 0x01<<pos; //位选 select573(6); select573(0); P0 = buf[pos]; //段选 select573(7); select573(0); } void seg_proc() { if(count_temp)return; count_temp = 1; //这次的数码管显示要比前几届都要复杂,前期搭建框架的时候要特别注意一些细节的地方 //比如说题目要求在采集次数为0时,回显界面除了标志符号和触发次数以外其他全部熄灭, //在频率采集到无效数字的时候要显示AA字母。 temp_proc(); if(!wet_temp_model) { if(seg_model==0)//时间界面 { ds1302_proc(); ucbuf[0] = date_nodot[time_now[0]/16]; ucbuf[1] = date_nodot[time_now[0]%16]; ucbuf[2] = 0xbf; ucbuf[3] = date_nodot[time_now[1]/16]; ucbuf[4] = date_nodot[time_now[1]%16]; ucbuf[5] = 0xbf; ucbuf[6] = date_nodot[time_now[2]/16]; ucbuf[7] = date_nodot[time_now[2]%16]; } else if(seg_model==1)//回显界面 { if(sample_count)//采集次数大于0时 { if(recall_model==0)//温度回显界面 { ucbuf[0] = 0xc6;//标志位C ucbuf[1] = 0xff; ucbuf[2] = date_nodot[uitemp_max/10]; ucbuf[3] = date_nodot[uitemp_max/10]; ucbuf[4] = 0xff; ucbuf[5] = date_nodot[uitemp_ave/100]; ucbuf[6] = date_dot[uitemp_ave%100/10]; ucbuf[7] = date_nodot[uitemp_ave%10]; } else if(recall_model==1)//湿度回显界面 { ucbuf[0] = 0x89;//标志位H ucbuf[1] = 0xff; ucbuf[2] = date_nodot[uiwet_max/10]; ucbuf[3] = date_nodot[uiwet_max/10]; ucbuf[4] = 0xff; ucbuf[5] = date_nodot[uiwet_ave/100]; ucbuf[6] = date_dot[uiwet_ave%100/10]; ucbuf[7] = date_nodot[uiwet_ave%10]; } else if(recall_model==2)//时间回显界面 { ucbuf[0] = 0x8e;//标志位F ucbuf[1] = date_nodot[sample_count/10]; ucbuf[2] = date_nodot[sample_count%10]; ucbuf[3] = date_nodot[sample_time[0]/16]; ucbuf[4] = date_nodot[sample_time[0]%16]; ucbuf[5] = 0xbf; ucbuf[6] = date_nodot[sample_time[1]/16]; ucbuf[7] = date_nodot[sample_time[1]%16]; } } else//采集次数等于0时 { if(recall_model==0)//温度回显界面 { ucbuf[0] = 0xc6;//标志位C ucbuf[1] = 0xff; ucbuf[2] = 0xff;ucbuf[3] = 0xff;ucbuf[4] = 0xff; ucbuf[5] = 0xff;ucbuf[6] = 0xff;ucbuf[7] = 0xff; } else if(recall_model==1)//湿度回显界面 { ucbuf[0] = 0x89;//标志位H ucbuf[1] = 0xff; ucbuf[2] = 0xff;ucbuf[3] = 0xff;ucbuf[4] = 0xff; ucbuf[5] = 0xff;ucbuf[6] = 0xff;ucbuf[7] = 0xff; } else if(recall_model==2)//时间回显界面 { ucbuf[0] = 0x8e;//标志位F ucbuf[1] = date_nodot[sample_count/10]; ucbuf[2] = date_nodot[sample_count%10]; ucbuf[3] = 0xff;ucbuf[4] = 0xff;ucbuf[5] = 0xff; ucbuf[6] = 0xff;ucbuf[7] = 0xff; } } } else if(seg_model==2)//参数显示界面 { ucbuf[0] = 0x8c; ucbuf[1] = 0xff; ucbuf[2] = 0xff; ucbuf[3] = 0xff; ucbuf[4] = 0xff; ucbuf[5] = 0xff; ucbuf[6] = date_nodot[uitemp_refer/10]; ucbuf[7] = date_nodot[uitemp_refer%10]; } } else//当前温度和湿度界面 { ucbuf[0] = 0x86; ucbuf[1] = 0xff; ucbuf[2] = 0xff; ucbuf[3] = date_nodot[uitemp_sample[sample_count]/10]; ucbuf[4] = date_nodot[uitemp_sample[sample_count]%10]; ucbuf[5] = 0xff; if(fre_work)//采集到有效湿度 { ucbuf[6] = date_nodot[uiwet_sample[sample_count]/10]; ucbuf[7] = date_nodot[uiwet_sample[sample_count]%10]; //将最新保存的温湿度数据显示3s } else//采集到无效湿度 { ucbuf[6] = 0x88; ucbuf[7] = 0x88; } } } //===================开定时器================ void Timer0_Isr(void) interrupt 1 { count_f++; } void Timer0_Init(void) { TMOD &= 0xF0; //设置定时器模式 TMOD |= 0x04; TL0 = 0xff; //设置定时初始值 TH0 = 0xff; //设置定时初始值 TF0 = 0; //清除TF0标志 TR0 = 1; //定时器0开始计时 ET0 = 1; //使能定时器0中断 } void Timer1_Isr(void) interrupt 3 { ulms++;//滴答计时 if(ulms%100) ul100ms++; if(++count_key==10){count_key=0;} if(++count_temp==500){count_temp=0;} if(++count_adc==20){count_adc=0;} if(S9_flag){S9_time++;} if(++count_t==1000){frequency = count_f;count_f = 0;count_t =0;}//算频率 if(wet_temp_model)//处于温度湿度界面时开始计时 { if(++wet_temp_time>=3000)//显示时间超过3秒钟 { wet_temp_time = 0; wet_temp_model = 0; if(fre_work==1) { if(++sample_count==100)sample_count=0;//触发次数加1,最大加到99 sample_time[0] = time_now[0]; sample_time[1] = time_now[1]; sample_time[2] = time_now[2]; } else { sample_count = sample_count;//无效数据时,触发次数不变,数据不进入采集数组 } } } if(L4_flag) { if(ul100ms&1) { L4 = ~L4; } } if(L4)led_stat &= 0xf7; else led_stat |= 0x08; seg_dispbit(ucpos,ucbuf);//扫描数码管 if(++ucpos==8){ucpos=0;} led_disp(led_stat); } void Timer1_Init(void) //1毫秒@12.000MHz { AUXR |= 0x40; //定时器时钟1T模式 TMOD &= 0x0F; //设置定时器模式 TL1 = 0x20; //设置定时初始值 TH1 = 0xD1; //设置定时初始值 TF1 = 0; //清除TF1标志 TR1 = 1; //定时器1开始计时 ET1 = 1; //使能定时器1中断 EA = 1; } //==========================按键模块===================== void key_proc() { unsigned char key_down=0,key_temp=0,key_up; static unsigned char key_old = 0; if(count_key)return; count_key = 1; key_temp = key_read();//三行代码,实现消抖 key_down = key_temp&(key_temp^key_old);//表示按键按下后的下降沿 key_up = ~key_temp&(key_temp^key_old);//表示按键按下后的松开时的上升沿 key_old = key_temp; switch(key_down) { case 4: if(!wet_temp_model){if(++seg_model==3){seg_model=0;}}//显示模式切换 break; case 5: if(!wet_temp_model)// { if(seg_model==1) { if(++recall_model==3){recall_model= 0;} } } break; case 8: if(!wet_temp_model) { if(seg_model==2) { if(++uitemp_refer==100){uitemp_refer=0;} } } break; case 9: if(!wet_temp_model) { if(seg_model==2) { if(uitemp_refer>0){uitemp_refer--;} } else if((seg_model==1)&&(recall_model==2))//处于下降沿的时候将数码管显示为分秒 { S9_flag = 1;//开始判断按键是否长于2s S9_time = 0; } } break; default:key_down = 0; } if(key_up==9) { if(S9_flag==1) { S9_flag = 0;//松开S9后判断一下是否在当前界面,然后才暂停 } } if(S9_time>=2000)//判断是否大于两秒 { S9_time = 0; sample_count = 0;//清除记录次数 } } unsigned char key_read() { unsigned char key_new = 0; unsigned char key_val=0; C1 = 0;C2 = 1; key_new = P3&0x0C;//00001100 S4S5 C1 = 1;C2 = 0; key_new = (key_new>>2)|(P3&0x0C);//00001100 S8S9 switch(~key_new)//取反前高四位一直都是0000,低四位有四种可能,0111,1011,1101,1110 { case 0xf8:key_val = 8;break; case 0xf4:key_val = 9;break; case 0xf2:key_val = 4;break; case 0xf1:key_val = 5;break; default:key_val = 0; } return key_val; } //===================实时时钟读取======================== void ds1302_config() { Write_Ds1302_Byte(0x8e,0x00); Write_Ds1302_Byte(0x84,time_now[0]); Write_Ds1302_Byte(0x82,time_now[1]); Write_Ds1302_Byte(0x80,time_now[2]); Write_Ds1302_Byte(0x8e,0x80); } void ds1302_proc() { time_now[0] = Read_Ds1302_Byte(0x85); time_now[1] = Read_Ds1302_Byte(0x83); time_now[2] = Read_Ds1302_Byte(0x81); } //======================温度读取======================== unsigned int read_temp() { unsigned char low = 0x00,high = 0x00; unsigned int temp = 0x0000; init_ds18b20(); Write_DS18B20(0xcc); Write_DS18B20(0x44); init_ds18b20(); Write_DS18B20(0xcc); Write_DS18B20(0xbe); low = Read_DS18B20(); high = Read_DS18B20(); init_ds18b20(); temp = high; temp = (temp<<8)|low; temp = temp>>4;//左移四位的操作时,已经将整数部分乘以了1/16,所以后面可以直接得整数或者将整数放大十倍 return temp; } void temp_proc() { uitemp_now = read_temp(); if(uitemp_now==85){uitemp_now = 25;} } //==================ADC输出模块=============== unsigned char ADC_PCF8591(unsigned char channel) { unsigned char temp; I2CStart(); I2CSendByte(0x90); I2CWaitAck(); I2CSendByte(channel); I2CWaitAck(); I2CStart(); I2CSendByte(0x91); I2CWaitAck(); temp = I2CReceiveByte(); I2CSendAck(1); I2CStop(); return temp; } void ADC_proc()//检测光敏电阻的亮度变化 { unsigned char ADC_temp = 0; if(count_adc)return; count_adc = 1; ADC_temp = ADC_PCF8591(0x01);//光敏电阻通道 if((ADC_temp<100)&&(ADC_old>100))//采集触发的条件,只有从亮到暗状态 { wet_temp_model = 1; if(fre_work == 1) { uiwet_sample[sample_count] = uiwet_now; uitemp_sample[sample_count] = uitemp_now; if(sample_count>=2)//采集次数大于两次 { if((uitemp_sample[sample_count]>uitemp_sample[sample_count-1])&&(uiwet_sample[sample_count]>uiwet_sample[sample_count-1])) {//这次采集的温度大于上次采集的温度,采集的湿度大于上次采集的湿度 led_stat &= 0xdf; } else { led_stat |= 0x20; } } if(uitemp_sample[sample_count]>uitemp_refer)//采集温度大于温度参数 { L4_flag = 1;//开始以0.1s为间隔闪烁 } else { L4_flag = 0; } led_stat |= 0x10;//采集到无效数据时L5熄灭 }//保存此刻的温度和湿度 else { led_stat &= 0xef;//采集到有效数据时L5点亮 } //采取到无效数据的时候,此次温度和湿度的数据都丢弃,采集次数也不加1,采集的时间也不保存 } ADC_old = ADC_temp; } //=======================频率转换为湿度模块================== void fre_to_wet() { if(frequency>2000)//无效数据 { fre_work = 0; } else if(frequency<200)//无效数据 { fre_work = 0; } else //有效数据 { fre_work = 1; uiwet_now = (frequency-200)*0.044+10; } } void count_max_ave() { unsigned char i; if(sample_count)//采集次数大于0就计算最大值和平均值 { for(i=0,i<sample_count,i++) { if(uitemp_sample[i]>uitemp_sample[i+1]) } } } //================指示灯模块=================== void led_disp(unsigned char ucled) { P0 = ucled; select573(4); select573(0); } void led_proc() { if(!wet_temp_model) { if(seg_model==0) { led_stat = (led_stat|0x07)&0xfe;//点亮L1 } else if(seg_model==1) { led_stat = (led_stat|0x07)&0xfd;//点亮L2 } } else { led_stat = (led_stat|0x07)&0xfb;//点亮L3 } }
我在比赛前几天才突然发现以前用的软件延时,在复杂的程序设计中(赛前练过几套国赛题),存在很多问题。有时候尽管逻辑正确,但是当外设驱动太多,软件延时时间积累上去之后,代码仍然会出现对前期的代码有影响,比如说数码管扫描出现闪烁,亮度不够,按键反应能力过慢。但是在比赛前一周偶然看到蓝桥杯单片机的官方指导书,才开始使用定时器扫描数码管,三行代码实现消抖这样的驱动方式。我在换写法后拿来试练了四套题目,分别是第13届,第12届,第8届和第9届的程序,发现其效果与我之前使用的方式相比确实要好的多(可能是我对软件延时那些写法领悟不够深入)
由于对指导书的模板使用次数和时间都比较少,对于一些地方的理解也比较模糊,比如对按键的三行代码的理解,数码管将字符转换为对应数字显示函数(改成了自己能理解的写法),函数扫描时间怎么设置比较合理,什么函数放在数码管显示里面运行比较合理,这些问题我都没有弄的特别明白和清楚,只是简单背住了用法但是不解其意所以在比赛实际应用过程中,对于一些自己没做过的考点心里还是有点慌的。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。