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 #include <reg52.h> 2 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 }