DS18B20课后题:
负数以补码形式存在的。
如果是负数,那么S全为1,前面那些不看,我们只看最后一个S,当它为1时,那么就是0x08,所以只要高8位大于等于0x08,就是负数。
其中s=0x40这里
上面打错了,如果不是负数,则s=0,则不显示负号,如果是负数,就执行else的语句。
温度达不到零下怎么显示呢?模拟
这里是错的,所以中文手册会出错,要参照英文手册。
错的是16进制,应该是FE6F
开始红外通讯
红外遥控广泛用于家庭中,体积小,抗干扰强,功能强,功耗低,成本低
开发板带有红外接收和红外发射功能
自学习型万能遥控器源代码,下载到开发板运行后,拿电视机遥控器对着开发板按下开关机键,开发板就能把开关机键代码解码,同时按下矩阵键盘某个按键,这个按键就有了电视机遥控器开关机键的功能,并且断电后学习到的数据不会丢失,因为程序把解码的程序储存到EEPROM中,只要是NEC协议的红外遥控器,开发板都可以解码,复制到开发板按键上。这就是万能遥控器。
红外发射电路用的红外发射二极管,发红外光,特殊颜色的普通光,可见光谱之下,即眼看不到它发光。
解码芯片用单片机解码。应用电路就随机了,例如遥控器按下后,流水灯亮,流水灯就是应用电路。
上图右侧为一体化红外接收头。就是U6这个器件。
1脚将接收到的红外信号放大解调后输出,我们把数据送给了P32,便于我们编写程序解码。
可参考那个电路,也可参考芯片手册的电路:
红外通讯没有射频模块做的好用,红外光会被东西挡到。
红外通讯流程:
红外发射装置:键盘编码调制,送给红外发射管,然后发射出去。例如我按下了CH-,右边是对应的键值码,即45,16进制,然后编码芯片就会对它进行编码、调制,然后送给发射管,在发射出去。
红外接收装置:接收到信号后,放大、解调,然后输出给解码芯片解码。
我们输出给P32,由单片机解码。
信号的调制和解调是红外通讯的基本原理。
将我们要发送的数据,也就是我们说的基带二进制信号,调制为38.41kHz的载波信号,然后发射出去。
红外接收设备接收到信号后要还原为我们发射的数据,也就是解调。目前大部分红外接收设备都是用的一体化红外接收头进行解调。
调制主要是像我们看的那个自学习型万能遥控器程序,编码和调制都需要通过单片机进行。
接下来看看编码和解码:
以我们目前这红外遥控系统为例,红外发射就不用考虑了,遥控器就是一个完整的红外发射器件,任意按下某个按键,遥控器就会把这按键的值进行编码调制发射。
接收端我们用了一体化接收头,接收到信号后,把这个信号放大解调,输出给单片机,我们写程序解码。要解码我们首先要知道是如何编码的,我们遥控器使用的NEC编码协议,要解码这个发出的数据,我们要先了解这个NEC协议。如果不知道遥控器(红外发射器件)是NEC协议的该怎么知道呢?我们可以用示波器或逻辑分析仪,把红外信号通过一体化接收头解调后的信号,把编码抓出来,之后分析是哪种协议。这是用逻辑分析仪分析的。
之前说了调制是为了信号更好的传输,那么为什么要编码呢?如果不编码,会出现这种情况,抽屉里有空调和电视机,我用空调的遥控器按下开机键,想打开空调,结果都打开了,这就是为了区分不同机器类型,才将信号按照一定规律进行编码传输。现在不论是业余还是专业制作,都是用的编码芯片。不同的编码芯片编码协议不同。NEC是最广泛的。日本人定的。
空调遥控器的红外通讯,红外编码通常是用遥控器内单片机特定的编码,编码协议是自己定制的,不同厂家编码协议不一样。不过编码调制后基本都在38kHz频率上发射的。
所以我们也可以通过一体化接收头,把它解调出来,要想知道如何解码,就要用逻辑分析仪或示波器抓出来,分析编码规律。
NEC的编码完成后,所要发送的基带二进制编码通常有30多位。空调编码完后有100多位。
NEC
开始会有一段引导码,是有规定的,高电平9000微秒左右,低电平4500微秒。
只有两个8位的用户码。也是为了区分不同器件,例如两台电视机,不同品牌,都使用的NEC协议,为了不串用,厂家用户码做的就不一样。
在之后就是8位数据码和数据反码,数据码就是键值码,反码是为了校验前面8位是否正确的。
数据是一位一位发送的。
为了更形象化,我们把遥控器所发送的键值,通过开发板一体化接收头解调出来后,把基带二进制码通过示波器抓出来。开发板和逻辑分析仪接好。
发送和接收那端,起始码高电平在8~10ms就可以。
所有的起始码,用户码,数据码和数据反码都能分析出来。
程序根据波形建立起一个思路,红外接收头的输出端,解调后输出给P32,有第二功能外部中断0,可以配置在跳边沿触发,来一个低电平触发一次,触发时我们就启动定时器,在它下次再次触发时,定时器在这期间走的数,我们取出来,就能知道脉冲有多宽。
比如从这到这,看到有多长就能知道这是起始码。之后是判断0还是1,0约是1.125ms宽,1约是2.25ms宽。通过这种方法让单片机充当解码芯片,把红外遥控器键值解码出来。
这里起始码后只有32位数据,空调遥控器就长了,有100多位。
如果有告诉是某品牌的编码规律是怎么样的,还简单一点,否则只能一点一点分析找出规律。
解码后也可以用单片机把键值码学习下来,按照这个规律重新编码,编码完成后用定时器调制到38k这频率通过红外发射头发射,就可以遥控空调。
编程,接收到红外信号后,把数据通过NEC协议规定解码,之后让串口以9600波特率发出,用计算机串口助手显示。
写之前在梳理下思路:
一体化红外接收头输出端接到了P32,第二功能是外部中断0,可以设置外部中断为跳边沿模式,来一个低电平触发一次,触发外部中断时让定时器开始计数,当下次再触发时,就可以读取计时器所计的数值,就可以计算出上一次脉宽的持续时间,根据时间确认是引导码(13.5ms左右)、数据0(1.125ms左右)或者数据1(2.25ms左右)。
为了更形象,我们通过逻辑分析仪先把红外数据的波形抓出来,通过波形一边写程序一边讲解。
先是起始码,从1这里触发外部中断,开启定时器,2这里再次触发,就把定时器走的数读取出来,计算出持续时间,并且把定时器走数的值清0,再开始加,定时器一直不关,到下一次触发外部中断,再一次计算持续时间。
1 #include2 3 #define uchar unsigned char 4 #define uint unsigned int 5 sbit P10=P1^0; 6 uchar IRtime; //储存检测红外高低电平持续时间 7 uchar IRcord[4]; //储存解码后的4个字节数据 8 uchar IRdata[33];//包含起始码在内的33位数据 9 bit IRpro_ok; //解码后4个字节数据接收完成标志位 10 bit IRok; //33位数据接收完成标志位 11 12 void init() 13 { 14 TMOD|=0x02;//设置定时器0工作模式2,8位自动重装 15 TL0=TH0=0;//初始化定时器0寄存器,定时器0溢出一次时间为256个机器周期 16 EA=1; 17 ET0=1; 18 TR0=1; 19 20 IT0=1;//设置外部中断0跳边沿触发方式 21 EX0=1; 22 23 TMOD|=0x20;//设置定时器1工作模式2,8位自动重装 24 TL1=TH1=0xfd;//比特率9600 25 SM1=1;SM0=0;//设置串口工作模式1,10位异步收发 26 TR1=1; 27 } 28 29 //定时器中断,每中断一次需要256*1.085us=277.76us(256个机器周期,晶振频率位11.0592Mhz,机器周期=12*晶振周期) 30 void time0() interrupt 1 31 { 32 IRtime++;//277.76us 33 } 34 35 //外部中断0存入33次脉宽 36 void int0() interrupt 0 37 { 38 static uchar i;//静态变量用于存入33次数据计数 39 static bit startflag;//开始储存脉宽标志位 40 if(startflag)//标志被置1则开始存储33次脉冲宽度,一位一位存 41 { 42 if((IRtime<53)&&(IRtime>=32))i=0;//判断引导码,如果是引导码则从起始码开始存 43 IRdata[i]=IRtime;//以T0溢出的次数来计算脉宽把这个时间存放在数组 44 IRtime=0;//计数清零 45 i++;//计数脉宽存入次数自加 46 if(i==33)//i=33就表示已经存入了33次脉宽 47 { 48 IRok=1;//脉宽检查完成 49 i=0;//把脉宽计数清零准备下次存入 50 } 51 } 52 else 53 { 54 IRtime=0;//定时器0计数清零,因为初始化时就启动了,即使没有收到红外数据也再加 55 startflag=1;//开始处理标志位置1 56 } 57 } 58 59 //把提取的33次脉宽解码 NEC协议 60 void IRcordpro() 61 { 62 uchar i;//计数处理4个字节 63 uchar j;//用于处理1个字节的8位数据 64 uchar k;//用于计数处理33次脉宽 65 k=1;//从第一位脉宽开始处理,丢掉起始码 66 for(i=0;i<4;i++) 67 { 68 for(j=0;j<8;j++) 69 { 70 if(IRdata[k]>5) IRcord[i]|=0x80;//如果脉宽大于数据0标准的1125us那么就判断为数据1 71 72 if(j<7) IRcord[i]>>=1;//只能移动7次,防止最后一位移出 73 k++;//处理下一次脉宽 74 } 75 } 76 IRpro_ok=1;//解码完成 77 } 78 79 void main() 80 { 81 uchar i;//计数串口发送字节数 82 init(); 83 while(1) 84 { 85 if(IRok)//判断33次脉宽是否提取完成 86 { 87 IRcordpro();//根据脉宽解码出4个字节数据 88 IRok=0;//清零脉宽检查完成标志位等待下一次脉宽检查 89 if(IRpro_ok)//判断是否解码完成 90 { 91 if(IRcord[2]==0x45)P10=!P10;//若是CH-按键则亮第一个灯 92 for(i=0;i<4;i++)//串口发送4个字节数据 93 { 94 SBUF=IRcord[i];//发送数据 95 while(!TI);//等待发送完成标志 96 TI=0;//清零发送完成标志位 97 } 98 IRpro_ok=0;//清零解码完成标志位 99 } 100 } 101 } 102 103 }