赞
踩
目录
关于决赛的题,这也是我头一次自己去做,真心感觉好难啊,而且有许多“套路”都不能用了,这里来剖析一下我写的第十四届决赛代码,也是对前边提到的许多代码,关于“套路”不能用时,该如何去处理。
此外,决赛的题目官网上没有,也没链接可放了,我直接截图把题目放出来,决赛和省赛的资源数据包好像是一样的,第十四届比赛也就是在2023年,今年是第十五届比赛。
这个的意思就是,如果使用四位数码管显示一个数码,但是待显示的数据不足四位,比如只有三位,这个数是340,则只用三个数码管显示数据,四个数码管显示的结果应该是“熄灭”“3”“4”“0”,而非“0”“3”“4”“0”。之前写数码管时,都是直接让第一个数码管显示数据的千位,第二个显示百位,第三个显示十位,第四个显示个位。比如像下边这样
Nixie_num[0]=value/1000%10;
Nixie_num[1]=value/100%10;
Nixie_num[2]=value/10%10;
Nixie_num[3]=value/1%10;
然后再在定时器里在数码管对应的位置显示Nixie_num数组内的数据(如果是按照我之前写的代码的话)。
code unsigned char Seg_Table[] =
{
0xc0, //0
0xf9, //1
0xa4, //2
0xb0, //3
0x99, //4
0x92, //5
0x82, //6
0xf8, //7
0x80, //8
0x90, //9
};unsigned char location=0;
void Timer0_Isr(void) interrupt 1
{
P0=0x01<<location;NIXIE_CHECK();
P0=Seg_Table[Nixie_num[location]];NIXIE_ON();
if(++location>=8)
location=0;}
但是如果改为是0的话,直接这样处理,高位就不是熄灭,而是显示0了。显然不符合要求。其实到这里,大家至少应该能想到最最笨的处理方法了——判断数据的位数,在依次显示需要显示的位数,或者熄灭不需要显示的位。也就是这样:
unsigned char Wei_shu=0;
if(value/1000>0)Wei_shu=4;
else if(value/100>0)Wei_shu=3;
else if(value/10>0)Wei_shu=2;
else if(value/1>0)Wei_shu=1;if(Wei_shu==4)//四位数据,四个数码管都显示数据
{
Nixie_num[0]=value/1000%10;
Nixie_num[1]=value/100%10;
Nixie_num[2]=value/10%10;
Nixie_num[3]=value/1%10;
}
else if(Wei_shu==3)//三位数据,第一个数码管熄灭,后三个显示数据
{
Nixie_num[0]=10;//假设Nixie_num=10时对应该位熄灭,下同
Nixie_num[1]=value/100%10;
Nixie_num[2]=value/10%10;
Nixie_num[3]=value/1%10;
}
elseif(Wei_shu==2)//两位数据,前两个数码管熄灭,后两个显示数据
{
Nixie_num[0]=10;
Nixie_num[1]=10;
Nixie_num[2]=value/10%10;
Nixie_num[3]=value/1%10;
}
else if(Wei_shu==1)//一位数据,前三个数码管熄灭,最后一个显示数据
{
Nixie_num[0]=10;
Nixie_num[1]=10;
Nixie_num[2]=10;
Nixie_num[3]=value/1%10;
}
这种方法当然可行,但是太麻烦了(反正我刚接触单片机编程时,遇到这个问题就是这样想的,也不知道和大家想到一样不一样)。现在在反过来看问题,我们完全可以边判断数据的位数,边显示数据。如果value/1000>0,说明这个数据是一个四位(或者以上)数据,则该位显示Value/1000%10(千位),否则熄灭,其他数码管同理,个位的数码管如果也都没有数据的话,则直接显示0即可,不然整个数据位就全部熄灭了。这里用到了三目运算符,是编程的基础,就不过多介绍了
Nixie_num[4]=value/1000>0 ? value/1000%10:20;
Nixie_num[5]=value/100>0 ? value/100%10:20;
Nixie_num[6]=value/10>0 ? value/10%10:20;
//Nixie_num[7]=value/1>0 ? value/1%10:0;//数据连一位数都没有,则显示0而非全部熄灭
Nixie_num[7]=value/1%10;//数据连一位数都没有,则显示0而非全部熄灭
至此,我们就实现了“数据不足四位,高位熄灭”的功能,对应题目的话,大概在这些地方提到过
之前我们显示数码管,都是通过断码表Seg_Table来完成Nixie_num数组内的数据到数码管显示的数据之间的映射的。比如基本的Nixie_num[0]=0就代表第0位显示0(如果不修改Seg_Table数组内的值的话)。我们也知道,0和0.的段码绝对是不一样的,虽然只相差一点,我们不妨把Seg_Table数组内,0到9为段码对应数组0到9,10到19为段码对应0.到9.(注意有小数点欧)。具体0.到9.的段码如何计算,这里就不在介绍了,完善后的Seg_Table为
code unsigned char Seg_Table[] =
{
0xc0, //0
0xf9, //1
0xa4, //2
0xb0, //3
0x99, //4
0x92, //5
0x82, //6
0xf8, //7
0x80, //8
0x90, //9
//0. 1. 2. 3. 4. 5. 6. 7. 8. 9.
0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10,}
这样,我们写Nixie_num[0]=0,表示数码管第0位显示0,Nixie_num[0]=0+10就表示数码管第0位显示0.了。这也算一个小窍门吧,以后真的考这个赚钱时,肯定还是优先考虑把函数封装好。
需要显示小数点的地方题目上还是比较多的,比如
没错,这个正负数据仅针对题目要求显示的校准值
这个校准值不但要显示正负,还要完成第二章第一节提到的高位为0时熄灭的功能,正数的处理跟前边提到的一样,这里主要介绍负数的处理。第二章第一节实现了在判断数据长度的同时显示数据,而这里还需要根据数据的长度,判断正负号显示的位置,我是没更好的办法了,只能使用第二章第一节提到的那个“笨方法”了。不过还好,因为校准值取值是-90到90,每次增减也是5,所以校准值只有可能是两位数或者一位数,判断起来也好判断
if(remote_jiaozhun>=0)//正
{
Nixie_num[5]=20;//熄灭
Nixie_num[6]=jiaozhun/10>0 ? jiaozhun/10%10:20;
Nixie_num[7]=jiaozhun/1>0 ? jiaozhun/1%10:0;
}
else//负
{
if(jiaozhun/10>0)
{
Nixie_num[5]=21;//显示-
Nixie_num[6]=jiaozhun/10%10;//距离参数
Nixie_num[7]=jiaozhun/1%10;
}
else
{
Nixie_num[5]=20;
Nixie_num[6]=21;//显示-
Nixie_num[7]=jiaozhun/1%10;//距离参数
}
}
之前咱们写的温度传感器读取是这样的
unsigned int read_18b20()
{
unsigned int T=0;//定义温度
unsigned char low=0;//用于接受温度的低八位
unsigned char high=0;//用于接受温度的高八位
init_ds18b20();//初始化DS18B20
Write_DS18B20(0xCC);//跳过ROM检测
Write_DS18B20(0x44);//发送开始温度转换的命令
Delay_OneWire(200);//温度转化需要时间,这里直接延时一下。。注意应避免连续读取DS18B20
init_ds18b20();//重新初始化DS18B20
Write_DS18B20(0xCC);//跳过ROM检查
Write_DS18B20(0xBE);//发送读取温度数据的指令
low=Read_DS18B20();//接收低八位
high=Read_DS18B20();//接收高八位
T=high;
T&=0x0F;//第八位的高四位置0,也就是不考虑符号位
T<<=8;
T|=low;
T>>=4;//舍去低八位的低四位,也就是不考虑小数位
return T;}
这里是实打实的直接舍去了符号位和小数位,因为符号位和小数位一般用不上,但是偏偏在国赛出现了温度传感器需要读取到小数点后一位,其实也简单。
我们知道从温度传感器读取到的温度数据是16位的温度数据,其中高八位的高四位是符号位,低八位的低四位是小数位,我们之前都是只取中间八位,也就是高八位的低四位和低八位的高四位,也就是只有温度的整数部分,现在我们只需要加上小数部分即可。
但是直接加小数的话,温度值可就得变成float型的数据了,这显然不是我们想要看到的,我们不妨把温度数据扩大十倍,也就是整数部分*10加小数部分,这样我们就还可以使用unsigned int来记录温度数据了。修改之后的代码
unsigned int read_temp(void)
{
unsigned int temp=0;
unsigned char low=0;
unsigned char high=0;
unsigned char xiaoshu=0;
init_ds18b20();
Write_DS18B20(0xCC);
Write_DS18B20(0x44);
Delay_OneWire(200);
init_ds18b20();
Write_DS18B20(0xCC);
Write_DS18B20(0xBE);
low=Read_DS18B20();
high=Read_DS18B20();
temp=high;
temp&=0x0F;
temp<<=8;
temp|=low;
temp>>=4;
/*获取小数部分*/
xiaoshu=low;
xiaoshu&=0x0F;
temp=temp*10+xiaoshu;//温度扩大了十倍,把小数点后一位也加上了
return temp;
}
在第十四届省赛已经实现了按键的长按,这里就不再赘述,我们这里要解决的是这个:
也就是同时按下两个按键,并长按两秒。
其实,用理性的角度来解释的话,是不存在“同时按下两个按键”的过程的,只可能是“按下一个按键后,按下第二个按键”,因此,我们只需要在按下S8或S9时,判断S9或S8是否被按下,两种情况分别对应按下S8后按下S9和按下S9后按下S8,当检查到两个按键都被按下之后,我们再开始数数,把它当按下一个按键的长按处理。
需要注意的,应避免按下一个S8之后按下S9,此时松开S8,保持S9按下,这种情况不能算作S8和S9同时按下。我们的短按都是在松开按键之后才生效的,而题目要求按下S8和S9达到2s就触发复位,也就是说不需要再松开S9或S9(好事,不然又得多一堆判断了),因此,如果S8和S9瞎按的话,就比如这一段话最开始提到的情况,那确实会出现一些不太好的情况,这涉及到底层逻辑的问题,而且题目也没要求,所以就暂时不管了。
至于按下按键2s,我们还是使用定时器数数,定义一个标志位is_2s_changan,如果is_2s_changan为0时,2s后会被置为1,通过判断将is_2s_changan置0到松开按键之前is_2s_changan是否被置为1,就可以判断是否长按够2s了。
对于两个按键的处理类似,这里只介绍其中一个:
if(P32==0)
{
Delay5ms();
while(P32==0)//按下s9
{
run();
/*以下为同时长按s8和s9*/
is_2s_changan=0;//在按下s9,但没按下s8之前,已经将2s数数置为0,确保按下s8时is_2s_changan=0;
while(P33==0)//按下s8(此时处于同时按下S9和S8的状态太)
{
run();
if(is_2s_changan==1)//如果这个状态持续了2s,一直等到is_2s_changan=1了,说明长按了2s
{
restart=1;//启动重置功能(见下方的if(restart==1))
break;//并跳出等待(题目的意思貌似是,按够2s直接重置,不管松没松按键,所以要break。
//如果要等松开按键才重置的话,可以把这个和下边那个break及其if判断注释掉
}
if(!(P32==0))
break;
}
if(restart==1)
break;
}
Delay5ms();
key_value=9;
}
之前写的代码都是LED_ON(X),通过一个宏函数,快速点亮一个LED灯,但是现在,至少对于这个国赛题是一点也不行了,我们只能单独写。要说也简单,之前的宏函数都是根据传入的参数x来改变Led_Num的值,进而改变Led灯的状态,如下(代码有点长,其实是一行,可能显示不下就被换行了):
#define LED_ON(x) Led_Num&=~(0x01<<x);P0=Led_Num;P2|=0x80;P2&=0x9F;P2&=0x1F;
现在,只穿一个参数点亮一个LED灯已经不能实现题目要求了:
我们干脆直接手动修改Led_Num的值,然后给P0赋值,最后开关一次锁存器。
LED指示灯的第一个功能,就是在测距界面(mod==10)下,显示距离值,这里我们加一个数数,定时器数100ms,每100ms处理一次LED灯(因为超声波更新的也不会那么快,而且后边也有100ms闪烁的功能),切记不要一直重复地给P0赋值,开关锁存器,LED灯容易误闪烁。
下面是代码演示
if(mod==10&&is_100ms==1)//距离显示界面下,led灯显示距离(注意取反)
{
is_100ms=0;//这个是100ms是额外的处理,减慢led处理的速度,
Led_Num=~remote;P0=Led_Num;P2|=0x80;P2&=0x9F;P2&=0x1F;
}
其他LED灯功能就中规中矩了,都是之前提到了,不过既然这里已经舍弃使用LED_ON(x)的宏函数了,就干脆都直接修改Led_Num的值,来控制LED灯
if(mod==10&&is_100ms==1)//距离显示界面下,led灯显示距离(注意取反)
{
is_100ms=0;//这个是100ms是额外的处理,减慢led处理的速度,
Led_Num=~remote;P0=Led_Num;P2|=0x80;P2&=0x9F;P2&=0x1F;
}
else if(mod==20||mod==21)//在参数界面下,L8点亮,同时其它灯熄灭
{
if(Led_Num!=~(0x80))//如果在参数界面下,未处在“L8点亮,同时其它灯熄灭”的状态,则使“L8点亮,同时其它灯熄灭”
{
Led_Num=~0x80;P0=Led_Num;P2|=0x80;P2&=0x9F;P2&=0x1F;
}
}
else if((mod==30||mod==31||mod==32)&&is_100ms==1)//在工厂模式下L1以100ms闪烁
{
is_100ms=0;//is_100ms为0时,100ms后会被置为1,每次进入这个else if,则翻转一次L1
if(Led_Num==~(0x01))//如果点亮了,则熄灭
{
Led_Num=~0x00;P0=Led_Num;P2|=0x80;P2&=0x9F;P2&=0x1F;
}
else if(Led_Num!=~(0x01))//如果熄灭了,则点亮
{
Led_Num=~0x01;P0=Led_Num;P2|=0x80;P2&=0x9F;P2&=0x1F;
}
}
先说一句,过年回家的着急,万用表没带,关于DAC输出的我都没办法测试,包括前几篇提到的,不过应该也没什么大问题。本次写的代码中涉及到有许多简单重复的if判断,写的时候都改成三目运算符了。
- #include <stc15.h>
- #include <intrins.h>
- #include "onewire.h"
- #include "iic.h"
-
- code unsigned char Seg_Table[] =
- {
- 0xc0, //0
- 0xf9, //1
- 0xa4, //2
- 0xb0, //3
- 0x99, //4
- 0x92, //5
- 0x82, //6
- 0xf8, //7
- 0x80, //8
- 0x90, //9
- //0. 1. 2. 3. 4. 5. 6. 7. 8. 9.
- 0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10,
- 0xFF,//熄灭
- 0xBF,//- 21
- 0x8C,//P 22
- 0x8E,//F 23
- };
- unsigned char Led_Num=0xFF;
-
- /*在这次国赛题目中,关于LED的宏用着不太方便*/
- #define LED_ON(x) Led_Num&=~(0x01<<x);P0=Led_Num;P2|=0x80;P2&=0x9F;P2&=0x1F;
- #define LED_OFF(x) Led_Num|=0x01<<x; P0=Led_Num;P2|=0x80;P2&=0x9F;P2&=0x1F;
- #define LED_OFF_ALL() Led_Num=0xFF; P0=0xFF;P2|=0x80;P2&=0x9F;P2&=0x1F;
-
- #define NIXIE_CHECK() P2|=0xC0;P2&=0xDF;P2&=0x1F;
- #define NIXIE_ON() P2|=0xE0;P2&=0xFF;P2&=0x1F;
-
- unsigned char ULN=0x00;
- #define RELAY_ON() ULN|=0x10; P0=ULN;P2|=0xA0;P2&=0xBF;P2&=0x1F;
- #define RELAY_OFF() ULN&=0xEF; P0=ULN;P2|=0xA0;P2&=0xBF;P2&=0x1F;
-
- #define BUZZER_ON() ULN|=0x40; P0=ULN;P2|=0xA0;P2&=0xBF;P2&=0x1F;
- #define BUZZER_OFF() ULN&=0xBF; P0=ULN;P2|=0xA0;P2&=0xBF;P2&=0x1F;
-
- sbit TX=P1^0;//定义超声波的TX
- sbit RX=P1^1;//定义超声波的RX
-
- void Timer0_Init(void); //1毫秒@12.000MHz
- void Timer1_Init(void); //@12.000MHz
- void Delay100ms(void); //@12.000MHz
- void get_key(void);//按键读取与处理
- void read_ul(void);//读取超声波测距(其他子函数均在read_ul前定义了,就不在这里声明了)
- void show_menu(void);//显示菜单
- void run(void);//主运行函数
- void led_run(void);//led运行函数
- void relay_run(void);//继电器运行函数
-
- unsigned char location=0;//当前扫描到的数码管的位置,中间变量
- unsigned char Nixie_num[]={20,20,20,20,20,20,20,20};//数码管待显示的数据
- unsigned char key_value=0;//读取到的键值,中间变量
- unsigned int temp=0;//温度,这里为了方便处理温度的小数部分,已经将温度扩大了10倍
- unsigned int remote=0;//距离
- unsigned char mod=10;//菜单模式,取值10,20,21,30,31,32,分分别对应三个菜单的各个子菜单
- unsigned int speed=340;//超声波传播速度
- unsigned char remote_canshu=40;//距离参数
- unsigned char wendu_canshu=30;//温度参数
- signed int remote_jiaozhun=0;//距离校准(有符号)
- unsigned char dac_xiaxian=10;//为便于除了。扩大了10倍,取值1到20,对应0.1v到20v
- unsigned char jilu_remote=0;//记录距离(用于DAC输出)
-
- bit is_read_ul=1;//读取超声波的标志位
- bit remote_danwei=0;//0:cm,1:0
- bit is_jilu=0;//是否处在记录距离的标志位(就是6s那个状态)
- bit is_2s_changan=0;//记录是否完成长按2s按键的标志位
- bit restart=0;//重置标志位
- bit is_100ms=0;
- void main()
- {
- BUZZER_OFF();//关闭蜂鸣器
- RELAY_OFF();//关闭继电器
- read_temp();//初始化温度传感器
- Timer0_Init();//定时器0初始化
- Timer1_Init();//定时器1初始化(超声波)
- EA=1;
- Delay100ms();//如果想上电第一次就能读取到正确的温度,可以多加六七个Delay100ms()
- //mod=0;
- while(1)
- {
- get_key();
- run();
- //Delay100ms();
- }
- }
- void run(void)
- {
- temp=read_temp();//读取温度
- led_run();//控制led灯
- relay_run();//控制继电器
- if(is_read_ul==1)//每500ms读取一次超声波(见定时器0)
- {
- read_ul();
- is_read_ul=0;
- }
- if(remote_danwei!=0&&mod!=10)//确保每一次从其它菜单进入菜单10时,距离单位都是10:cm,也即remote_danwei=0
- remote_danwei=0;
- show_menu();
- }
-
- unsigned int count_500ms=0;
- unsigned int count_6s=0;
- unsigned int count_2s=0;
- unsigned int count_100ms=0;
- void Timer0_Isr(void) interrupt 1
- {
- P0=0x01<<location;NIXIE_CHECK();
- P0=Seg_Table[Nixie_num[location]];NIXIE_ON();
-
- if(++location>=8)
- location=0;
-
- //is_read_ul为0时,500ms后被置为1,用于每500ms读取一次超声波
- if(is_read_ul==0)
- {
- if(++count_500ms>500)
- {
- is_read_ul=1;
- count_500ms=0;
- }
- }
-
- //is_jilu为1时,6s后被置为0(用于s8的第6个功能)
- if(is_jilu==1)
- {
- if(++count_6s==6000)
- {
- is_jilu=0;
- count_6s=0;
- }
- }
- //is_2s_changan为0时,2s后被置为1,根据将is_2s_changan置为0之后
- //检查is_2s_changan是否被置为1可以检测,从将is_2s_changan置为0到现在
- //是否过了2s(用于检查长按2s)
- if(is_2s_changan==0)
- {
- if(++count_2s>2000)
- {
- is_2s_changan=1;
- count_2s=0;
- }
- }
-
- if(is_100ms==0)//100ms,用于led闪烁
- {
- if(++count_100ms>100)
- {
- count_100ms=0;
- is_100ms=1;
- }
- }
- }
-
- void Timer0_Init(void) //1毫秒@12.000MHz
- {
- AUXR |= 0x80; //定时器时钟1T模式
- TMOD &= 0xF0; //设置定时器模式
- TL0 = 0x20; //设置定时初始值
- TH0 = 0xD1; //设置定时初始值
- TF0 = 0; //清除TF0标志
- TR0 = 1; //定时器0开始计时
- ET0 = 1; //使能定时器0中断
- }
-
- void Timer1_Init(void) //@12.000MHz
- {
- AUXR |= 0x40; //定时器时钟1T模式
- TMOD &= 0x0F; //设置定时器模式
- TL1 = 0x00; //设置定时初始值
- TH1 = 0x00; //设置定时初始值
- TF1 = 0; //清除TF1标志
- //TR1 = 1; //定时器1开始计时
- //ET1 = 1; //使能定时器1中断
- }
-
- void Delay100ms(void) //@12.000MHz
- {
- unsigned char data i, j, k;
-
- _nop_();
- _nop_();
- i = 5;
- j = 144;
- k = 71;
- do
- {
- do
- {
- while (--k);
- } while (--j);
- } while (--i);
- }
-
- void Delay5ms(void) //@12.000MHz
- {
- unsigned char data i, j;
-
- i = 59;
- j = 90;
- do
- {
- while (--j);
- } while (--i);
- }
- void get_key()
- {
- unsigned char key_P3=P3;
- unsigned char key_P4=P4;
- float V=0;//中间变量,记录需要输出的电压值
-
- //当处在6s距离记录的状态下
- if(is_jilu==1)//6秒的距离记录具有最高的优先级,在记录过程中,所有按键功能失效
- {
- jilu_remote=remote;//实时记录距离信息
- //restart=0;
- key_value=0;
- return;//直接返回,不再往下进行
- }
-
- P44=0;
- if(P32==0){Delay5ms();while(P32==0){run();}Delay5ms();key_value=5;}
- else if(P33==0){Delay5ms();while(P33==0){run();}Delay5ms();key_value=4;}
-
- P42=0;
- if(P32==0)
- {
- Delay5ms();
- while(P32==0)//按下s9
- {
- run();
- /*以下为同时长按s8和s9*/
- is_2s_changan=0;//在按下s9,但没按下s8之前,已经将2s数数置为0,确保按下s8时is_2s_changan=0;
- while(P33==0)//按下s8(此时处于同时按下S9和S8的状态太)
- {
- run();
- if(is_2s_changan==1)//如果这个状态持续了2s,一直等到is_2s_changan=1了,说明长按了2s
- {
- restart=1;//启动重置功能(见下方的if(restart==1))
- break;//并跳出等待(题目的意思貌似是,按够2s直接重置,不管松没松按键,所以要break。
- //如果要等松开按键才重置的话,可以把这个和下边那个break及其if判断注释掉
- }
- if(!(P32==0))
- break;
- }
- if(restart==1)
- break;
- }
- Delay5ms();
- key_value=9;
- }
- else if(P33==0)
- {
- Delay5ms();
- while(P33==0)
- {
- run();
- /*以下为同时长按s8和s9*/
- /*同上*/
- is_2s_changan=0;
- while(P32==0)
- {
- run();
- if(is_2s_changan==1)
- {
- restart=1;
- break;
- }
- if(!(P33==0))
- break;
- }
- if(restart==1)
- break;
- }
- Delay5ms();
- key_value=8;
- }
- //重置数据
- if(restart==1)
- {
- restart=0;
- mod=10;//重置菜单
- remote_canshu=40;//重置距离参数
- wendu_canshu=30;//重置温度参数
- remote_jiaozhun=0;//重置距离校准
- speed=340;//重置速度
- dac_xiaxian=10;//重置DAC下限
- remote_danwei=0;//重置距离单位为:cm
- key_value=0;//key_value=0,使得刚才长按2s的效果不会被当成短按处理
- }
- //s4模式切换
- if(key_value==4)
- {
- if(mod==10)//在测距界面
- {
- mod=20;//跳转到参数界面
- }
- else if(mod==20||mod==21)//在参数界面
- {
- mod=30;//跳转到工厂界面
- }
- else if(mod==30||mod==31||mod==32)//在工厂界面
- {
- mod=10;//跳转到测距界面
- }
- }
-
- //s5在同一个界面下不同子菜单之间跳转
- else if(key_value==5)
- {
- if(mod==10)//在测距界面,调整距离单位
- remote_danwei=~remote_danwei;
- else if(mod==20)//在参数界面的距离参数界面,跳转到温度参数界面,下类似
- mod=21;
- else if(mod==21)
- mod=20;
- else if(mod==30)
- mod=31;
- else if(mod==31)
- mod=32;
- else if(mod==32)
- mod=30;
- }
-
- //s8加
- else if(key_value==8)
- {
- if(mod==20)
- remote_canshu=remote_canshu<90?remote_canshu+10:90;//距离参数+10=(取值范围10到90)
- else if(mod==21)
- wendu_canshu=wendu_canshu<80?wendu_canshu+1:80;//温度参数+10(0到80)
- else if(mod==30)
- remote_jiaozhun=remote_jiaozhun<90?remote_jiaozhun+5:90;//距离校准+5(取值-90到90)
- else if(mod==31)
- speed=speed<9990?speed+10:9990;//超声波速度+10(取值10到9990)
- else if(mod==32)
- dac_xiaxian=dac_xiaxian<200?dac_xiaxian+1:20;//DAC下限加0.1(取值0.1到2v)
- //注意前边已经提到为便于处理DAC扩大了10倍,,所以这里加的是1
- else if(mod==10)
- is_jilu=1;//在测距模式下,按下s8触发一次6s的记录
- }
-
- //s9减,与上边的加类似,三目运算符的作用均为控制取值范围
- else if(key_value==9)
- {
- if(mod==20)
- remote_canshu=remote_canshu>10?remote_canshu-10:10;
- else if(mod==21)
- wendu_canshu=wendu_canshu>0?wendu_canshu-1:0;
- else if(mod==30)
- remote_jiaozhun=remote_jiaozhun>-90?remote_jiaozhun-5:-90;
- else if(mod==31)
- speed=speed>10?speed-10:10;
- else if(mod==32)
- dac_xiaxian=dac_xiaxian>10?dac_xiaxian-1:10;
- else if(mod==20&&!(jilu_remote==0))
- {
- V=(5-dac_xiaxian)/80*jilu_remote+dac_xiaxian-10*(5-dac_xiaxian)/80;//计算需要输出的电压
- V=V>dac_xiaxian?V:dac_xiaxian;//输出电压限幅
- V=V<5?V:5;
- wirte_pcf((unsigned char)(V/5*255));
- }
- }
-
-
- }
-
- void Delay14us(void) //@12.000MHz
- {
- unsigned char data i;
-
- _nop_();
- _nop_();
- i = 45;
- while (--i);
- }
- void send_wave(void)
- {
- unsigned char i=0;
- for(i=0;i<8;i++)//8个40kHz的超声波
- {
- TX=1;Delay14us();
- TX=0;Delay14us();
- }
- }
- void read_ul(void)
- {
- unsigned int ul_time;//记录超声波来回的时间(注意没有单位),中间变量
- send_wave();//发送超声波
- TR1=1;//开始计时
- while((RX==1)&&(TF1==0));//等待接受返回的数据
- TR1=0;//接收到返回的数据,停止计时
- if(TF1==1)//如果是因为定时器溢出,说明没检测到底有效数据
- {
- ul_time=0;
- TF1=0;
- }
- else//检查到有效数据
- {
- /*读取超声波来回的时间*/
- ul_time=TH1;
- ul_time<<=8;
- ul_time|=TL1;
- }
- /*距离=时间*速度/2=定时器时间/单片机主频*速度(m/s)*100/2+距离校准 单位:cm*/
- remote=ul_time*0.00000452115*speed+remote_jiaozhun>0?ul_time*0.0000041667*speed+remote_jiaozhun:0;
- ul_time=0;
- TH1=0;
- TL1=0;
- }
- void show_menu(void)
- {
-
- unsigned char jiaozhun=0;
- if(mod==10)//测距界面
- {
- Nixie_num[0]=temp/100>0 ? temp/100%10:20;//显示温度十位或者熄灭(十位为0)
- Nixie_num[1]=temp/10>0 ? temp/10%10+10:20+10;//显示个位加小数点,十位各位为0时显示0.
- Nixie_num[2]=temp/1%10;
- Nixie_num[3]=21;
-
- if(remote_danwei==0)//显示单位为cm时,直接显示。最高位之前的各位熄灭
- {
- Nixie_num[4]=remote/1000>0 ? remote/1000%10:20;
- Nixie_num[5]=remote/100>0 ? remote/100%10:20;
- Nixie_num[6]=remote/10>0 ? remote/10%10:20;
- //Nixie_num[7]=remote/1>0 ? remote/1%10:0;//数据连一位数都没有,则显示0而非全部熄灭
- Nixie_num[7]=remote/1%10;//数据连一位数都没有,则显示0而非全部熄灭
- }
- else if(remote_danwei==1)//如果单位是m
- {
- Nixie_num[4]=remote/1000>0 ? remote/1000%10:20;
- Nixie_num[5]=remote/100>0 ? remote/100%10+10:10;//精确到小数点后两位,只需要在倒数第二位前加个小数点即可
- Nixie_num[6]=remote/10>0 ? remote/10%10:0;
- Nixie_num[7]=remote/1>0 ? remote/1%10:0;
- }
- }
- else if(mod==20)//参数界面1距离参数
- {
- Nixie_num[0]=22;//P
- Nixie_num[1]=1;
- Nixie_num[2]=20;
- Nixie_num[3]=20;
- Nixie_num[4]=20;
- Nixie_num[5]=20;
- Nixie_num[6]=remote_canshu/10>0 ? remote_canshu/10%10:0;//显示距离参数
- Nixie_num[7]=remote_canshu/1>0 ? remote_canshu/1%10:0;
- }
- else if(mod==21)//参数界面2温度参数
- {
- Nixie_num[0]=22;//P
- Nixie_num[1]=2;
- Nixie_num[2]=20;
- Nixie_num[3]=20;
- Nixie_num[4]=20;
- Nixie_num[5]=20;
- Nixie_num[6]=wendu_canshu/10>0 ? wendu_canshu/10%10:0;//显示温度参数
- Nixie_num[7]=wendu_canshu/1>0 ? wendu_canshu/1%10:0;
- }
- else if(mod==30)//工厂1校准值
- {
- Nixie_num[0]=23;//F
- Nixie_num[1]=1;
- Nixie_num[2]=20;
- Nixie_num[3]=20;
- Nixie_num[4]=20;
-
- //正负号转化,remote_jiaozhun是实际值,带正负。jiaozhun是中间变量,不带符号,值与remote_jiaozhun的绝对值相同
- //用abs()也行
- jiaozhun=remote_jiaozhun>0?remote_jiaozhun:-remote_jiaozhun;
-
- //PS,距离参数取值10到90,故数码管5熄灭(正数)或显示-(负数)后两位显示数据
- if(remote_jiaozhun>=0)//正
- {
- Nixie_num[5]=20;//熄灭
- Nixie_num[6]=jiaozhun/10>0 ? jiaozhun/10%10:20;
- Nixie_num[7]=jiaozhun/1>0 ? jiaozhun/1%10:0;
- }
- else//负
- {
- if(jiaozhun/10>0)
- {
- Nixie_num[5]=21;//显示-
- Nixie_num[6]=jiaozhun/10%10;//距离参数
- Nixie_num[7]=jiaozhun/1%10;
- }
- else
- {
- Nixie_num[5]=20;
- Nixie_num[6]=21;//显示-
- Nixie_num[7]=jiaozhun/1%10;//距离参数
- }
- }
-
- }
- else if(mod==31)//工厂2速度
- {
- Nixie_num[0]=23;//F
- Nixie_num[1]=2;
- Nixie_num[2]=20;
- Nixie_num[3]=20;
- Nixie_num[4]=speed/1000>0 ? speed/1000%10:20;//显示速度
- Nixie_num[5]=speed/100>0 ? speed/100%10:20;
- Nixie_num[6]=speed/10>0 ? speed/10%10:20;
- Nixie_num[7]=speed/1>0 ? speed/1%10:20;
- }
- else if(mod==32)//工厂3DAC输出下限
- {
- Nixie_num[0]=23;//F
- Nixie_num[1]=3;
- Nixie_num[2]=20;
- Nixie_num[3]=20;
- Nixie_num[4]=20;
- Nixie_num[5]=20;
- Nixie_num[6]=dac_xiaxian/10%10+10;//DAC下限
- Nixie_num[7]=dac_xiaxian/1%10;
- }
- }
- void led_run(void)
- {
- if(mod==10&&is_100ms==1)//距离显示界面下,led灯显示距离(注意取反)
- {
- is_100ms=0;//这个是100ms是额外的处理,减慢led处理的速度,
- Led_Num=~remote;P0=Led_Num;P2|=0x80;P2&=0x9F;P2&=0x1F;
- }
- else if(mod==20||mod==21)//在参数界面下,L8点亮,同时其它灯熄灭
- {
- if(Led_Num!=~(0x80))//如果在参数界面下,未处在“L8点亮,同时其它灯熄灭”的状态,则使“L8点亮,同时其它灯熄灭”
- {
- Led_Num=~0x80;P0=Led_Num;P2|=0x80;P2&=0x9F;P2&=0x1F;
- }
- }
- else if((mod==30||mod==31||mod==32)&&is_100ms==1)//在工厂模式下L1以100ms闪烁
- {
- is_100ms=0;//is_100ms为0时,100ms后会被置为1,每次进入这个else if,则翻转一次L1
- if(Led_Num==~(0x01))//如果点亮了,则熄灭
- {
- Led_Num=~0x00;P0=Led_Num;P2|=0x80;P2&=0x9F;P2&=0x1F;
- }
- else if(Led_Num!=~(0x01))//如果熄灭了,则点亮
- {
- Led_Num=~0x01;P0=Led_Num;P2|=0x80;P2&=0x9F;P2&=0x1F;
- }
- }
- }
-
- bit relay_is_on=0;//继电器状态,1打开,0关闭
- void relay_run()
- {
- //如果距离参数-5<当前距离<距离参数+5,并且当前温度小于温度参数(前两个条件都成立的话,就需要打开继电器)
- //并且继电器没有打开,则打开继电器
- if(remote_canshu-5<=remote&&remote<=remote_canshu+5&&temp/10<=wendu_canshu&&relay_is_on==0)
- {
- RELAY_ON();
- relay_is_on=1;
- }
-
- //如果不满足上述前两个条件(此时需要关闭继电器)
- //并且继电器没有关闭,则关闭继电器
- else if(!(remote_canshu-5<=remote&&remote<=remote_canshu+5&&temp/10<=wendu_canshu)&&relay_is_on==1)
- {
- RELAY_OFF();
- relay_is_on=0;
- }
- }
onewire.c
- /* # 单总线代码片段说明
- 1. 本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
- 2. 参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
- 中对单片机时钟频率的要求,进行代码调试和修改。
- */
- #include <stc15.h>
- #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;
- }
- unsigned int read_temp(void)
- {
- unsigned int temp=0;
- unsigned char low=0;
- unsigned char high=0;
- unsigned char xiaoshu=0;
-
- init_ds18b20();
- Write_DS18B20(0xCC);
- Write_DS18B20(0x44);
- Delay_OneWire(200);
-
- init_ds18b20();
- Write_DS18B20(0xCC);
- Write_DS18B20(0xBE);
- low=Read_DS18B20();
- high=Read_DS18B20();
-
- temp=high;
- temp&=0x0F;
- temp<<=8;
- temp|=low;
- temp>>=4;
-
- /*获取小数部分*/
- xiaoshu=low;
- xiaoshu&=0x0F;
-
- temp=temp*10+xiaoshu;//温度扩大了十倍,把小数点后一位也加上了
- return temp;
- }
- #ifndef _ONEWIRE_H_
- #define _ONEWIRE_H_
-
- unsigned int read_temp(void);
-
- #endif
- /* # I2C代码片段说明
- 1. 本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
- 2. 参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
- 中对单片机时钟频率的要求,进行代码调试和修改。
- */
-
- #define DELAY_TIME 5
- #include <stc15.h>
- #include <intrins.h>
- #include "iic.h"
- sbit sda=P2^1;
- sbit scl=P2^0;
- //
- static void I2C_Delay(unsigned char n)
- {
- do
- {
- _nop_();_nop_();_nop_();_nop_();_nop_();
- _nop_();_nop_();_nop_();_nop_();_nop_();
- _nop_();_nop_();_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);
- }
-
- void wirte_pcf(unsigned char dat)
- {
- I2CStart();
- I2CSendByte(0x90);
- I2CWaitAck();
- I2CSendByte(0x40);
- I2CWaitAck();
- I2CSendByte(dat);
- I2CWaitAck();
- I2CStop();
- }
- #ifndef _IIC_H_
- #define _IIC_H_
-
- void wirte_pcf(unsigned char dat);
-
- #endif
临近比赛了,这里也提前祝参加比赛的同学能够拿个好奖,也对得起这么久的努力了
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。