51微控制器學習筆記:紅外接收(對原子哥的ALIENTEK遙控器進行解碼)

xouou_53320發表於2013-02-23

正點原子的ALIENTEK遙控器

使用者碼00ff

16進位制鍵值碼錶
45    46    47
44    40    43
07    15    09
16    19    0d
0c    18    5e
08    1c     5a
42    ☻     4a

 

 

紅外接收管  通用型即可

 


 ---------------------------------------------------------------------------

下面是類似的波形圖,這個是網上找的,圖片較大,縮小後看上去有些模糊,不過沒關係,資料手冊上一般都有



 

遙控器在發射紅外訊號之前,我們的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;	
	    }
	}
}

 

相關文章