正點原子的ALIENTEK遙控器
用戶碼00ff
16進制鍵值碼表
45 46 47
44 40 43
07 15 09
16 19 0d
0c 18 5e
08 1c 5a
42 ☻ 4a
90d6ade3-634c-321f-9829-7e3c8da43554.png (256.26 KB, 下載次數: 52)
下載附件
2017-1-14 23:27 上傳
紅外接收管 通用型即可
9bd62bdc-f583-38d8-adcb-a4fad9e071d4.png (444.74 KB, 下載次數: 66)
下載附件
2017-1-14 23:27 上傳
---------------------------------------------------------------------------
下面是類似的波形圖,這個是網上找的,圖片較大,縮小后看上去有些模糊,不過沒關系,數據手冊上一般都有
82767798-1022-35be-bb92-f6d9598dbe5a.png (130.3 KB, 下載次數: 67)
下載附件
2017-1-14 23:27 上傳
遙控器在發射紅外信號之前,我們的mcu已經開啟了定時器
在定時器中斷函數中的全局變量irTimeCounts++ 一直在自加
irTimeCounts多長時間加一次? 或者說多長時間進入一次定時器中斷函數呢?
在方式2時,t=256*12/11059200 約為277.78us
============================================================
1.對于1.125ms的時間,timer0會中斷1.125/t=4.05次 也就是要5次,算上各種誤差,(頂多5次,至少4次)
2.對于2.25ms 的時間,timer0會中斷 2.25/t=8.1次, 也就是要9次,算上各種誤差,(頂多9次,至少8次)
對于這里的次數,比N次小一點,就是N次,比N次大一點,就是N+1次,因為不會有半次,
誤差給它正負0.4次足夠了,給了誤差后再算次數,直接舍入就行
那么我們就檢測irTimeCounts的值,
如果小于6或7,那么接收到的數據為0
如果大于6或7,那么接收到的數據為1
從計算結果來看,我覺得0.56,1.125,2.25這些值的出現是比較合理的,
能有效避免由于器件誤差和環境造成的數據誤判,而且計算也方便- #include "my51.h"
- #include "ir.h"
- #include "smg.h"
- void main()
- {
- timer0Init(); //定時器0初始化
- int0Init(); //外部中斷0初始化
-
- while(1)
- {
- if(irTimeCountsArrProcess()) //如果成功接收并解析完成一幀數據
- { //就讓蜂鳴器響一下
- beep=0; //蜂鳴器開啟
- led4=~led4; //4號燈反轉一下
- }
-
- displaySMG(irCodeByteDataProcessForSmg());//顯示
- beep=1; //蜂鳴器關閉
- //由于displaySMG()函數執行時間較短,故蜂鳴器響聲時間也較短,聽到滴了一下
- }
- }
復制代碼- #ifndef _51IR_H_
- #define _51IR_H_
- #include "my51.h"
- extern u8 data smgWela[7]; //數碼管顯示的數據
- extern void int0Init(); //外部中斷0初始化
- extern void timer0Init(); //定時器0初始化
- extern bool irTimeCountsArrProcess(); //成功解析一幀中斷數據返回TRUE
- extern u8* irCodeByteDataProcessForSmg();//將遙控器碼值處理成數碼管可顯示數據
- #endif
復制代碼- #include "ir.h"
- u8 irTimeCounts=0; //定時器0在方式2下8位自動重裝時的中斷計數值
- u8 irTimeCountsArr[32]={0}; //存放紅外接收數據時的中斷次數記錄值,
- u8 bitNum=0; //標志當前接收的是第幾個比特位
- u8 irReceFlag=0; //紅外接收一幀數據未完成標志,為1時完成
- u8 irCodeByteData[4]={0}; //保存接收到的4個字節的有效數據
- u8 irTimeCountsArrProcessOk=0;//對接收到的33位數據處理未完成標志,1完成
- void int0Init(); //外部中斷0初始化
- void timer0Init(); //定時器0初始化
- bool irTimeCountsArrProcess(); //解析中斷次數,即解碼
- u8* irCodeByteDataProcessForSmg();//將遙控器碼值處理成數碼管可顯示數據
- u8* irCodeByteDataProcessForSmg() //將解碼的4字節數據處理成數碼管可顯示的數據
- {
- if(irTimeCountsArrProcessOk) //檢測一幀數據是否解析完成
- {
- //這里的用戶碼只顯示低八位,因為高八位反正都是00(手上2個遙控器都是00)
- //然后還顯示遙控鍵值及其反碼,我們的數碼管只有6位,只顯示3字節數據
- if(irCodeByteData[2]+irCodeByteData[3]!=0xff)//校驗數據的完整性
- { //最后一個字節是鍵碼的反碼
- led6=0; //調試代碼
- }
- else
- {
- smgWela[0]=irCodeByteData[1] >> 4 ; //取高4位
- smgWela[1]=irCodeByteData[1] & 0x0f; //取低4位
- smgWela[2]=irCodeByteData[2] >> 4 ;
- smgWela[3]=irCodeByteData[2] & 0x0f;
- smgWela[4]=irCodeByteData[3] >> 4 ;
- smgWela[5]=irCodeByteData[3] & 0x0f;
- smgWela[6]=0xff; //小數點全不顯
- }
- irTimeCountsArrProcessOk=0;//標志清零,下一次有未解析的數據時才會再解析
- }
- return smgWela;
- }
- bool irTimeCountsArrProcess() //對接收到的32位的中斷次數數據進行解析
- {
- u8 i,j,k,value=0;
- if(irReceFlag) //檢測是否已經接收到新的4字節的紅外通信數據
- {
- for(j=0;j<4;j++) //有4個字節
- {
- for(i=0;i<8;i++) //對每個字節的8位數據處理
- {
- value>>=1;
- if(irTimeCountsArr[k++]>6) //這里我們用6或7都是可以的
- {
- value|=0x80; //大于6的話該位數據是1
- }
- }
- irCodeByteData[j]=value;//保存該字節,也就是遙控器的鍵碼
- }
- irReceFlag=0; //接收標志清零,這樣就會等到下次收到數據后才會再解析
- irTimeCountsArrProcessOk=1; //中斷數據解析完畢標志置1
- return TRUE; //解析完成
- }
- return FALSE; //未進行解析,該返回值主要是為了方便外部文件調用時判斷的
- }
- void int0() interrupt 0//外部中斷0
- {
- if(irTimeCounts>30) //9ms的話中斷32.4次,30這個取值差不多就可以了,不用太精確
- { //這里9ms引導碼需要timer0中斷irTimeCounts=9*11059.2/(256*12)=32.4次
- bitNum =0;
- irTimeCounts=0;//為接收第0位數據做準備
- return; //丟棄引導碼,反正不是有效數據0或1的都丟棄,直接返回
- }
- irTimeCountsArr[bitNum]=irTimeCounts; //將中斷次數數據存儲起來
- irTimeCounts=0; //存好了就立即清零,這樣不會影響下一位數據的接收
- bitNum++; //繼續下一位
- if(32==bitNum) //32位數據已經接收完成(0~31已經存儲)
- {
- bitNum=0; //清零,這里不清也可以,反正引導時也會清
- irReceFlag=1; //接收完成標志
- }
-
- }
- void timer0() interrupt 1 //定時器0中斷函數
- {
- irTimeCounts++; //注:該值最大為255
- }
- void timer0Init() //定時器0初始化
- { //配置工作方式寄存器,且不影響定時器1的狀態
- TMOD &= 0xf0; //保留定時器1的配置,并清除定時器0的配置
- TMOD |= 0X02; //使用定時器0的工作方式2
- TH0=0X00;
- TL0=0X00; //工作方式2是8位自動重裝
- ET0=1; //打開定時器
- EA=1; //打開總中斷
- TR0=1; //啟動定時器0
- }
- void int0Init() //外部中斷0初始化
- {
- IT0=1; //配置外部中斷0的觸發方式為 跳變延觸發
- EX0=1; //打開外部中斷0
- EA=1; //打開總中斷
- }
復制代碼- #ifndef _51SMG_H_
- #define _51SMG_H_
- #include "my51.h"
- sbit dula =P2^6; //段選鎖存器控制 控制筆段
- sbit wela =P2^7; //位選鎖存器控制 控制位置
- extern u8 data smgWela[7]; //第一位到第六位,最后一個是小數點位置控制
- #define dark 0x11//0x11是第17號元素,0x00是低電平,數碼管不亮,即table[17]
- #define dotDark 0xff //小數點全暗
- void displaySMG(u8* pWela); //數碼管顯示函數,參數是數組指針
- #endif
復制代碼- #include "smg.h"
- #include "my51.h"
- static u8 code table[]= { //0~F外加小數點和空輸出的數碼管編碼
- 0x3f , 0x06 , 0x5b , 0x4f , // 0 1 2 3
- 0x66 , 0x6d , 0x7d , 0x07 , // 4 5 6 7
- 0x7f , 0x6f , 0x77 , 0x7c , // 8 9 A B
- 0x39 , 0x5e , 0x79 , 0x71 , // C D E F
- 0x80 , 0x00 , 0x40 // . 暗 負號 暗即不顯示是第17索引號
- }; //負號為第18索引號元素
- /* 由于此表只能一次顯示一個小數點,故已注釋掉,僅供查詢
- 例如想要第一個和第六個數碼管小數點同時點亮,
- 則執行 pWela->dot = 0xfe & 0xdf 即可
- u8 code dotTable[]={ //小數點位置,某一位置0時,小數點亮
- 0xff , //那么全暗就是0xff
- 0xfe , 0xfd , 0xfb , //1 2 3
- 0xf7 , 0xef , 0xdf //4 5 6
- };*/
- u8 data smgWela[7]={0,0,0,0,0,0,0}; //第一位到第六位,最后一個是小數點位置控制
- //P0口的數碼管位選控制鎖存器只用了低6位,我們保留高2位的數據,留作它用
- void displaySMG(u8* pWela)
- {
- u8 i=0;
- //控制6位數碼管顯示函數,不顯示的位用參數dark
- u8 preState=P0|0x3f; //保存高2位狀態,其中最高位是ADC0804的片選信號
- wela=0;dula=0;_nop_();//先鎖定數據,防止吳亮及位選鎖存器高2位數據被改變
-
- P0=0; //由于數碼管是共陰極的,陽極送低電平,燈不亮
- dula=1;_nop_();
- dula=0; //段選數據清空并鎖定
- P0=preState; //共陰極數碼管是陰極置高不亮,低6位置1,高2位保留
- wela=1;_nop_(); //注:wela和dula上電默認為1
- wela=0; //位選鎖定,初始保留高2位的數據,低6位置高不亮
- for(i=0;i<6;i++) //顯示6位數碼管
- {
- P0=table[pWela[i]]|(((1<<i) &="" pwela[6])?0x00:0x80);
- dula=1;_nop_(); //送段數據,疊加小數點的顯示,0x00點亮小數點
- dula=0;
-
- P0=preState&~(1<<i); 不影響高2位數據,低6位是數碼管位選,低電平有效
- wela=1; _nop_(); //送位選號
- wela=0;
- delayms(1); //稍作延時,讓燈管亮起來
- { //消除疊影及誤亮,陰極置1不亮,低6位置1,高2位保留并鎖定
- P0=preState;
- wela=1; _nop_();
- wela=0;
- }
- }
- }
復制代碼
|