首先写上要做的模块,界面,按键细节
一页纸就能让你不用看pdf即可写出整套题
简单示范:
突然就觉得很简单了对不对,笼统看就写几个模块,外加几个界面~
下面,我就参照我是如何思考以及我的写题策略来讲解第九届的国赛题啦
void smg()
{
P2 = (P2&0x1f)|0xc0; P0 = T_COM[smg_i]; P2 = (P2&0x1f);
P2 = (P2&0x1f)|0xe0; P0 = ~table[smg_i]; P2 = (P2&0x1f);
smg_i++;
smg_i &= 0x07;
}
参照了第十届国赛题中的矩阵长短按键:第十届国赛题的做法
首先写这儿时候,我先写了三行独立按键,但是发现不好长按S6不好操作(对于我来说不太熟悉),就放弃了。
改写为以前写过的状态机的思路:
状态1:检测按键是否按下;
状态2:赋值;
状态3:检测按键是否松开;
状态机消抖的原理我就不讲啦,这里主要假设按键正常按下的思路。
在状态三中,我们就可以记录从状态2中到状态三中按键弹起的时间。
(将keyreturn2 = key)key
为检测按键的值。keyscan()
函数中,keyreturn2又被赋初值99;void keyscan()
{
u8 key = 99,keyreturn2 = 99;
h1 = 1; h2 = 1; h3 = 1; h4 = 1;
if(h1 != 1) key = 7;
if(h2 != 1) key = 6;
if(h3 != 1) key = 5;
if(h4 != 1) key = 4;
switch(state)
{
case 0: if(key!=99) state = 1;keyreturn = 99; time=0; break;
case 1: if(key==99) state = 0; else{state = 2; keyreturn = key;} break;
case 2: if(key==99) {state = 0;
time_flag = 0;
if(time>780) keyreturn2 = keyreturn*10;
else keyreturn2 = keyreturn;
}
}
只要把以上的思路理解啦,以后长短按键都不怕咯。
按键我同时写了led,检测按键模块是否写对。模块如下:
void led(u8 dat)
{
P2 = (P2&0x1f)|0x80; P0 = ~dat; P2 = (P2&0x1f);
}
switch(keyreturn2)
{
case 4: led(0x10); break;
case 5: led(0x08); break;
case 6: led(0x04); break;
case 7: led(0x02); break;
case 60: led(0x01); break;
}
测量光敏的通道是0x01,测量电压的通道是0x03,输出电压的通道是0x40。
关于这部分代码可以看我之前的博客。
链接:依据PCF芯片手册写代码
关于这部分代码可以参考我前面的文章,看芯片手册就行写代码,比赛完全不用背模块代码,只需要注意一些读写数据延时的细节即可:
链接:教你依据AT24C02英文手册写代码
w_at24(0x55,40);
Delay5ms(); //刚刚没写这个,读出的数据re_v是255
re_v = r_at24(0x55);
// Delay5ms(); //写不写这个无所谓不影响
w_at24(0x55,v);Delay5ms(); w_at24(0x56,pinlv/100);Delay5ms(); w_at24(0x57,pinlv%100);Delay5ms(); w_at24(0x58,temp/100);Delay5ms(); w_at24(0x59,temp%100);Delay5ms(); //写的后面要delay5没事,才能写进去
这个就是简单的测温模块,同样我是记得中文步骤,有好几个写法,我都放上来啦
扩大100倍的温度,保留了两位小数。
u16 r_t()
{
u8 l,h;
u16 temp;
init_ds18b20();
Write_DS18B20(0xcc);
Write_DS18B20(0x44);
Delay_OneWire(20);
init_ds18b20();
Write_DS18B20(0xcc);
Write_DS18B20(0xbe);
l = Read_DS18B20();
h = Read_DS18B20();
temp = ((u16)(h<<8)|l)*6.25;
return temp;
}
下面是测得整数温度
u8 r_t()
{
u8 l,h,temp;
init_ds18b20();
Write_DS18B20(0xcc);
Write_DS18B20(0x44);
Delay_OneWire(20);
init_ds18b20();
Write_DS18B20(0xcc);
Write_DS18B20(0xbe);
l = Read_DS18B20();
h = Read_DS18B20();
temp = h<<4|l>>4;//高位往高处移动,低位往低处移动
return temp;
}
怎么样,我的模版是不是很简单~
题目要求是P34管脚进行外部脉冲计数
而在比赛时你可以查看STC手册
P34进行计数时,我们用的事定时器0,所以一开始看了题目,我就用的定时器1进行单片机整体程序的定时,中断,控制程序的运行。
测频原理和目的就是:测得单位时间内的外部脉冲数
那这时候T0作为计数器,进行计数;T1作为定时器,定时。
两者合作即可测得频率啦
代码如下:
void pinlv_count()
{
if(flag1s)
{
TR0 = 0; //如果这里不停止计数,pinlv的值就会很奇怪,写了这个,pinlv的值就会比较稳定
flag1s = 0;
pinlv = TH0*256+TL0;
TF0 = 0;
TH0 = 0;
TL0 = 0;
TR0 = 1;
}
}
要注意的是TR0一定要在pinlv = TH0*256+TL0;
计算频率值 前 赋值为0,使T0停止计数。否则会测出错误的值(值会几k到几十k变动)。另外要退出计时时记得打开TR0,使得T0计数器工作。
哦对,还有一点,T0的工作模式bit3置1,让T0变为计数模式 直接利用位与TMOD |= 0x04;
虽然题目只说了按下 非电压阈值界面3) 按下S6,进入界面2)数据回显。但我为了控制方便,就设置为再次按下S6返回数据测试界面1)
检测按键:
switch(keyreturn2)
{
case 4: mode++;if(mode == 3) mode = 0; break;
case 5: flag_cunchu = 1;break;
case 6: if(flag_interface==1){index_v++; if(index_v == 51) index_v = 1; } else {re_view = ~re_view; mode = 0; flag_onece = 1;} break;
case 7: flag_interface = ~flag_interface; mode = 0; break;
case 60: index_v++; if(index_v == 51) index_v = 1; break;//长按
}
数码管界面:
void smg_dis()
{
if(flag_interface == 0 && re_view == 0) //dat_dis
{
if(mode == 0)
{led1 = 0; led2 = 0; led3 = 1;
//数据显示-电压
table[0] = 0x3e;
table[1] = 0x00;
table[2] = 0x00;
table[3] = 0x00;
table[4] = 0x00;
table[5] = 0x00;
table[6] = T_duan[v/10%10]|0x80;
table[7] = T_duan[v%10];
}
else if(mode == 1)
{led1 = 0; led2 = 1; led3 = 0;
//频率显示
table[0] = 0x71;
table[1] = 0x00;
table[2] = 0x00;
if(pinlv/10000>0)
table[3] = T_duan[pinlv/10000%10];
else table[3] = 0x00;
if(pinlv/1000>0) table[4] = T_duan[pinlv/1000%10]; else table[4] = 0x00;
if(pinlv/100>0) table[5] = T_duan[pinlv/100%10]; else table[5] = 0x00;
if(pinlv/10>0) table[6] = T_duan[pinlv/10%10]; else table[6] = 0x00;
table[7] = T_duan[pinlv%10];
}
else
{led1 = 1; led2 = 0; led3 = 0;
//温度显示
table[0] = 0x39;
table[1] = 0x00;
table[2] = 0x00;
table[3] = 0x00;
table[4] = T_duan[temp/1000%10];
table[5] = T_duan[temp/100%10]|0x80;
table[6] = T_duan[temp/10%10];
table[7] = T_duan[temp%10];
}
}
else if(flag_interface == 1)//index_dis
{
led1 = 0; led2 = 0; led3 = 0;
table[0] = 0x3e;
table[1] = 0x00;
table[2] = 0x00;
table[3] = 0x00;
table[4] = 0x00;
table[5] = 0x00;
table[6] = T_duan[index_v/10%10]|0x80;
table[7] = T_duan[index_v%10];
}
else if(re_view == 1)
{
led1 = 0; led2 = 0; led3 = 0;
if(flag_onece)
{
flag_onece = 0;
Delay5ms(); //刚刚没写这个,读出的数据re_v是255
re_v = r_at24(0x55);
Delay5ms();
re_pinlv = r_at24(0x56);
re_pinlv = re_pinlv*100;
Delay5ms(); //刚刚没写这个,读出的数据re_v是255
re_temp = r_at24(0x58)*100+r_at24(0x59);
}
if(mode == 0)
{
//数据显示-电压
table[0] = 0x3e;
table[1] = 0x00;
table[2] = 0x00;
table[3] = 0x00;
table[4] = 0x00;
table[5] = 0x00;
table[6] = T_duan[re_v/10%10]|0x80;
table[7] = T_duan[re_v%10];
}
else if(mode == 1)
{
//频率显示
table[0] = 0x71;
table[1] = 0x00;
table[2] = 0x00;
if(re_pinlv/10000>0)
table[3] = T_duan[re_pinlv/10000%10];
else table[3] = 0x00;
if(re_pinlv/1000>0) table[4] = T_duan[re_pinlv/1000%10]; else table[4] = 0x00;
if(re_pinlv/100>0) table[5] = T_duan[re_pinlv/100%10]; else table[5] = 0x00;
if(re_pinlv/10>0) table[6] = T_duan[re_pinlv/10%10]; else table[6] = 0x00;
table[7] = T_duan[re_pinlv%10];
}
else
{
//温度显示
table[0] = 0x39;
table[1] = 0x00;
table[2] = 0x00;
table[3] = 0x00;
if(re_temp/1000>0) table[4] = T_duan[re_temp/1000%10]; else table[4] = 0x00;
table[5] = T_duan[re_temp/100%10]|0x80;
table[6] = T_duan[re_temp/10%10];
table[7] = T_duan[re_temp%10];
}
}
我每写一部分就会测试自己的逻辑是否正确。
就像是把一张乱网里的线慢慢摊开
学电子的同学,一定要学会分解,一步一步来,就会有水到渠成的顺利。
如果想要整个工程的代码,可以点赞收藏,评论区留下你的邮箱~