紅外遙控在生產和生活中應用越來越廣泛,不同的紅外遙控芯片有不同的發碼協議,但一般都是由引導碼,系統碼,鍵碼三部分組成.
引導碼是告訴接收機準備接收紅外遙控碼.系統碼是識別碼,不同的遙控芯片有不同的誤別碼,以免搞錯.
遙控器上不同的按鍵有不同的鍵碼,系統碼和鍵碼都是16位碼,8位正碼,8位反碼.如SC6122的系統碼是FF00,FF和00互為反碼,鍵碼1為
EF10也是互為反碼.關于SC6122的資料在我們的網頁資料下載上有.
SC6122的引導碼為低電平為9000微秒,高電平為4500微秒.當然高電平不可能精確為9000微秒,在8000微秒到10000微秒都看作是正常范圍,
低電平在4000-5000之間都看作是正常范圍.
引導碼后的32位編碼(16位系統碼和16位鍵碼)不管高低電平,載波時間都是560微秒,但低電平持續時間是1125微秒,高電平持續時間是2250
微秒,所以低電平除去載波時間大約是560微秒,高電平除去載波時間大約是1680微秒.低電平也有一個波動范圍,在400-700之間都看作是正常
的,具體多少可以通過示波器測量出來.高電平也有一個波動范圍,在400-2000之間都看作是正常的,具體多少也是根據經驗.當然范圍越寬,捕
捉紅外線的范圍也越寬,越精確.在捕捉到有高低電平之間,在560-1680之間取一個中間值1120微秒,認為小于1120微秒是低電平,大于1120微
秒是高電平.
下面有兩個經過實踐能在實驗板上顯示鍵碼的程序,一個是匯編寫的,一個是用C寫的,與大家一起探討遙控器.
以下程序能在LCD上顯示系統碼和鍵碼,按不同的按鍵,系統碼不變,變的是鍵碼.有不懂的地方可以在留言本上留言.
RS EQU P2.5???????????????? ;這幾個是LCD引腳.
RW EQU P2.6
E EQU P2.7
IRR EQU P3.3??????????????? ;紅外接收的輸出接P3.3.
BUF EQU 30H ;30H-33H保存解碼結果
;=============================================
ORG 0000H
AJMP MAIN
;=============================================
ORG 0030H
MAIN:
MOV SP,#70H ;堆棧指針設到70H的地方
ACALL INIT_LCD??????????????????????????????????? ;初始化LCD
MOV R7,#10
ACALL DELAY_MS
MOV DPTR,#MSG1
CALL DISPLAY_LINE1??????????????????????? ;在第一行顯示 Test8: IR Reader
MOV DPTR,#MSG2
CALL DISPLAY_LINE2??????????????????????? ;在第二行顯示www.mcuedu.com
MAIN_LOOP:
JB IRR,$ ;等待接收頭信號為低
ACALL GET_LOW ;測量引導脈沖低電平
CLR C
MOV A,R7
SUBB A,#(8000/50) ;SC6122的引導脈沖低電平為9000US,我們只要測到低電平的值在8000-10000US范圍內就認為合格的.
JC MAIN_LOOP ;如果小于8000US,不對,重新等待接收
CLR C
MOV A,R7
SUBB A,#(10000/50)
JNC MAIN_LOOP
ACALL GET_HIGH ;測量引導脈沖高電平
CLR C
MOV A,R7
SUBB A,#(4000/50)
JC MAIN_LOOP??????????????????? ;如果小于4000US,不對,重新等待接收
CLR C
MOV A,R7
SUBB A,#(5000/50)
JNC MAIN_LOOP??????????????????????? ;如果大于5000US,不對,重新等待接收
MOV R0,#BUF ;
MOV R5,#8 ;SC6122發的碼有32位,我們用4個字節來存放,每個字節有8位
IR_NEXT:
CALL GET_LOW
CLR C
MOV A,R7
SUBB A,#(300/50) ;300US
JC MAIN_LOOP??????????????????????????????? ;低電平小于300微秒認為不對,重新接收
CLR C
MOV A,R7
SUBB A,#(800/50) ;800US
JNC MAIN_LOOP??????????????????????????????? ;低電平大于800微秒認為不對,重新接收
ACALL GET_HIGH
CLR C
MOV A,R7
SUBB A,#(300/50) ;300US
JC MAIN_LOOP??????????????????????????????? ;高電平小于300微秒認為不對,重新接收
CLR C
MOV A,R7
SUBB A,#(2000/50) ;2000US
JNC MAIN_LOOP???????????????????????????????????? ;高電平大于2000微秒認為不對,重新接收
CLR C
MOV A,R7
SUBB A,#(1120/50) ;??????????????????????????? ;跟中間值1120進行比較
RRC A
MOV @R0,A??????????????????????????????????? ;通過CY移到間接地址R0中去
DJNZ R5,IR_NEXT??????????????????????????????????? ;8位移完了嗎
MOV R5,#8
INC R0
MOV A,R0
XRL A,#(BUF+4)
JNZ IR_NEXT ;如果不到4個字節,接收下一個
MOV DPTR,#MSG_6122
ACALL DISPLAY_LINE1 ;顯示格式名稱
ACALL DISPLAY_IR_CODE ;顯示碼
AJMP MAIN_LOOP
;============================================
MSG1: DB " Test8: IR Reader "
MSG2: DB " www.mcuedu.com "
MSG_6122: DB " Format: SC6122 "
;============================================
;轉為ASCII碼在LCD在顯示
TO_ASCII:
CJNE A,#0AH,TO_ASCII_1
TO_ASCII_1:
JC TO_ASCII_2 ;小于10
ADD A,#('A'-10)
RET
TO_ASCII_2:
ADD A,#'0'
RET
;============================================
DISPLAY_IR_CODE:
MOV A,#0C0H ;顯示在第二行
ACALL SEND_COMMAND_BYTE ;設置DDRAM地址
MOV R0,#BUF
DISPLAY_IR_CODE_NEXT:
MOV A,@R0
SWAP A
ANL A,#0FH??????????????????????????? ;分離出高字節
ACALL TO_ASCII??????????????????????????????? ;轉為ASCII碼
ACALL SEND_DATA_BYTE???????????????????????????? ;顯示
MOV A,@R0
ANL A,#0FH??????????????????????????? ;分離出低字節
ACALL TO_ASCII???????????????????????????????? ;轉為ASCII碼
ACALL SEND_DATA_BYTE??????????????????????????? ;顯示
MOV A,#' '
ACALL SEND_DATA_BYTE??????????????????????????????? ;顯示空格
INC R0
MOV A,R0
XRL A,#(BUF+4)
JNZ DISPLAY_IR_CODE_NEXT
MOV R0,#8 ;第2行共有20個字符,前面顯示用了12個,再用8個空格填滿
DISPLAY_IR_CODE_B:
MOV A,#' '
ACALL SEND_DATA_BYTE
DJNZ R0,DISPLAY_IR_CODE_B
RET
;============================================
;測量低電平時間,50US采樣一次,R7加1一次,比如低電平時間為9000US,測得R7的結果為180(0B4H)
;OUTPUT: R7
GET_LOW:
MOV R7,#00H
GET_LOW_NEXT:
MOV R6,#20 ;在晶振為11.0592M時,延50US需要46個機器周期,
DJNZ R6,$ ;這條指令執行需要2個機器周期
JB IRR,GET_LOW_RTN ;接收頭為高電平,結束測量
INC R7
MOV A,R7
JNZ GET_LOW_NEXT ;看R7是否有溢出
GET_LOW_RTN:
RET
;============================================
;測量高電平時間,50US采樣一次,R7加1一次,比如高電平時間為4500US,測得R7的結果為90
;OUTPUT: R7
GET_HIGH:
MOV R7,#00H
GET_HIGH_NEXT:
MOV R6,#20 ;在晶振為11.0592M時,延50US需要46個機器周期,
DJNZ R6,$ ;這條指令執行需要2個機器周期
JNB IRR,GET_HIGH_RTN ;接收頭為低電平,結束測量
INC R7
MOV A,R7
JNZ GET_HIGH_NEXT ;看R7是否有溢出
GET_HIGH_RTN:
RET
;============================================
;============================================
DELAY_MS:
MOV R6,#250
DELAY_MS_NEXT:
NOP
NOP
DJNZ R6,DELAY_MS_NEXT
DJNZ R7,DELAY_MS
RET
;============================================
;INPUT: R7
DELAY:
DJNZ R7,$
RET
;============================================
;向LCD寫一個命令字節
;INPUT: ACC
SEND_COMMAND_BYTE:
CLR RS
CLR RW
MOV P0,A
SETB E
NOP
NOP
NOP
NOP
NOP
NOP
CLR E
MOV R7,#100
ACALL DELAY
RET
;===============================================
;向LCD寫一個數據字節
;INPUT: ACC
SEND_DATA_BYTE:
SETB RS
CLR RW
MOV P0,A
SETB E
NOP
NOP
NOP
NOP
NOP
NOP
CLR E
MOV R7,#100
ACALL DELAY
RET
;======================================================
;初始化LCD
INIT_LCD:
MOV A,#30H
ACALL SEND_COMMAND_BYTE
ACALL SEND_COMMAND_BYTE
ACALL SEND_COMMAND_BYTE
MOV A,#38H ;設置工作方式
ACALL SEND_COMMAND_BYTE
MOV A,#0CH ;顯示狀態設置
ACALL SEND_COMMAND_BYTE
MOV A,#01H ;清屏
ACALL SEND_COMMAND_BYTE
MOV A,#06H ;輸入方式設置
ACALL SEND_COMMAND_BYTE
RET
;=======================================================
;在第一行顯示
;INPUT: DPTR指向要顯示的內容
DISPLAY_LINE1:
MOV A,#080H
DISPLAY_LINE1_A:
ACALL SEND_COMMAND_BYTE ;設置DDRAM地址
MOV R6,#20
DISPLAY_LINE1_NEXT:
CLR A
MOVC A,@A+DPTR
ACALL SEND_DATA_BYTE
INC DPTR
DJNZ R6,DISPLAY_LINE1_NEXT
MOV R7,#100
ACALL DELAY
RET
;=======================================================
;在第二行顯示
;INPUT: DPTR指向要顯示的內容
DISPLAY_LINE2:
MOV A,#0C0H
AJMP DISPLAY_LINE1_A
;=======================================================
END
下面是一個用C寫的遙控器程序.能在數碼管上顯示鍵碼.
#include
#define c(x) (x*110592/120000)
sbit Ir_Pin=P3^3;
unsigned char code Led_Tab[]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,
0xf8,0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E};??????????????????????? //共陽極數碼顯示碼0-F.
unsigned char code Led_Sel[]={0xe,0xd,0xb,0x7};
unsigned char Led_Buf[4]; //顯示緩沖區
char Led_Index;?????????????????? //位選
unsigned char Ir_Buf[4]; //用于保存解碼結果
//==============================================================
//數碼管掃描
timer0() interrupt 1 using 1
{
TL0=65536-1000;
TH0=(65536-1000)/256; //定時器0設定約1000us中斷一次,用于數碼管掃描
P0=0xff;
P2=Led_Sel[Led_Index];??????????????????????????? //位選
P0=Led_Tab[Led_Buf[Led_Index]];??????????????????? //段選
if(++Led_Index>3) Led_Index=0;??????????????????? //四個掃描完了,到第一個數碼管
}
//==============================================================
unsigned int Ir_Get_Low()
{
TL1=0;
TH1=0;
TR1=1;
while(!Ir_Pin && (TH1&0x80)==0);
TR1=0;
return TH1*256+TL1;
}
//=============================================================
unsigned int Ir_Get_High()
{
TL1=0;
TH1=0;
TR1=1;
while(Ir_Pin && (TH1&0x80)==0);
TR1=0;
return TH1*256+TL1;
}
//==============================================================
main()
{
unsigned int temp;
char i,j;
Led_Index=1;
TMOD=0x11;
TL0=65536-1000;
TH0=(65536-1000)/256; //定時器0設定約1000us中斷一次,用于數碼管掃描
EA=1;
ET0=1;
TR0=1;
Led_Buf[0]=0;
Led_Buf[1]=0;
Led_Buf[2]=0;
Led_Buf[3]=0; //顯示區設成0
do{
restart:
while(Ir_Pin);
temp=Ir_Get_Low();
if(tempc(9500)) continue;//引導脈沖低電平9000
temp=Ir_Get_High();
if(tempc(5000)) continue;//引導脈沖高電平4500
for(i=0;i<4;i++) //4個字節
for(j=0;j<8;j++) //每個字節8位
{
temp=Ir_Get_Low();
if(tempc(800)) goto restart;
temp=Ir_Get_High();
if(tempc(2000)) goto restart;
Ir_Buf[i]>>=1;
if(temp>c(1120)) Ir_Buf[i]|=0x80;
}
Led_Buf[0]=Ir_Buf[2]&0xf;
Led_Buf[1]=(Ir_Buf[2]/16)&0xf;
Led_Buf[2]=Ir_Buf[3]&0xf;
Led_Buf[3]=(Ir_Buf[3]/16)&0xf; //顯示結果
}while(1);
}