/*************************************************************************************************/ //38k NEC 編碼接收和模擬發射 //完整的信號構成:引導碼+8位的客戶碼+8位客戶碼的補碼+8位的按鍵值+8位按鍵值的補碼+結束碼 //接收使用外部中斷0,發射管低電平觸發 //STC15F104W@24MHz // //為了盡可能的簡化代碼量,沒有引入按鍵,程序運行起來為每250ms一個周期,每250ms通過紅外發射管發射一次數據,同 //時若檢測到接收管接收到了完整的數據則把這個數據通過串口發出 // //由於發送和接收數據肯定無法同時在一片單片機上完成,所以若需測試代碼的完整功能應准備兩個單片機或者其他設備。 // //網上能找到的模擬紅外發射的代碼多為通過定時器實現,本代碼則統統使用軟件延時實現。一來使用定時器太浪費資源,二 //來對於1T單片機來說,用軟件延時馬馬虎虎輸出38K的波形還是挺簡單的(這都0202年了,估計以后很難由機會再用到12T //的51單片機了吧) /*************************************************************************************************/ #include "config.h" #include "delay.h" #include "suart.h" sbit IRIN =P3^2; //紅外接收端口 sbit IROUT=P3^3; //紅外發射端口 unsigned char IrValue[4]; //紅外鍵值.四個數據分別為用戶碼、用戶補碼、鍵值、鍵值補碼 bit flag_Ir; //紅外接收完成標志位 void Ir_Init(void); //紅外初始化(外部中斷0和發送接收端口初始化) void Ir_Out(unsigned char u, unsigned char x); //發送數據,兩個參數分別為用戶碼和鍵值 void Ir_Out_Frame(unsigned char y); //發送一幀數據,由 Ir_Out() 函數調用 void Delay7us(); //7us延時 @24.000MHz_STC-Y5 void Delay19us(); //19us延時@24.000MHz_STC-Y5 void Delay100us(); //100us延時@24.000MHz_STC-Y5 /************************************************************************************************** * 主函數 */ void main(void) { Ir_Init(); //紅外初始化 SUART_INIT(); //串口初始化 TxString("38KHz NEC 紅外接收測試程序\r\n"); while (1) { delay_ms(250); //每250ms通過紅外發送一個數據 Ir_Out(0x42,0x5A); //參數為用戶碼、鍵值 if(flag_Ir) //如果接收到完整數據,則把數據通過串口打印出來 { flag_Ir=0; //必須清空接收完成標志 TxByte(IrValue[0]); TxByte(IrValue[1]); TxByte(IrValue[2]); TxByte(IrValue[3]); } } } /************************************************************************************************** * 紅外接收初始化 */ void Ir_Init(void) { IT0 = 1; //下降沿觸發 EX0 = 1; //打開中斷0允許 EA = 1; //打開總中斷 IRIN = 1; //初始化端口 IROUT = 1; flag_Ir = 0; //初始化接收完成標志位 } /************************************************************************************************** * 延時7us @ 24MHz STC-Y5 * 生成38k波使用,占空比在1/3時效果最好。i的取值供參考,使用時視波形表現調整。 */ void Delay7us() //@24.000MHz { unsigned char i; _nop_(); _nop_(); i = 38; while (--i); } /************************************************************************************************** * 延時19us @ 24MHz STC-Y5 * 生成38k波使用,占空比在1/3時效果最好。i的取值供參考,使用時視波形表現調整。 */ void Delay19us() //@24.000MHz { unsigned char i; _nop_(); _nop_(); i = 105; while (--i); } /************************************************************************************************** * 延時100us @ 24MHz STC-Y5 */ void Delay100us() //@24.000MHz { unsigned char i, j; i = 3; j = 82; do { while (--j); } while (--i); } /************************************************************************************************** * 紅外發射程序 */ void Ir_Out(unsigned char u, unsigned char x) { int tt; EA=0; //關閉所有中斷 /*發送引導碼*/ tt=350; //輸出9ms 1(有38k信號輸出表示 1. 38k每個周期約26us,9ms大概tt個周期,視實際情況調整) do {IROUT=0; Delay19us(); IROUT=1; Delay7us();} while(--tt); tt=45; //輸出4.5ms 0 (無38k信號輸出表示 0. 同樣,tt的取值看實際情況調整) do {IROUT=1; Delay100us();} while(--tt); /*發送用戶碼和用戶補碼*/ Ir_Out_Frame(u); Ir_Out_Frame(~u); /*發送鍵值和鍵值補碼*/ Ir_Out_Frame(x); Ir_Out_Frame(~x); /*發送結束碼*/ tt=25; //輸出0.65ms 1 do {IROUT=0; Delay19us(); IROUT=1; Delay7us();} while(--tt); tt=5; //輸出40ms 0 do {IROUT=1; Delay100us();} while(--tt); /*重復碼*/ //9ms 1 //2.25ms 0 //0.56ms 1 //40ms 0 //56ms 0 EA=1; // 打開中斷 } /************************************************************************************************** * 紅外單幀發射程序 */ void Ir_Out_Frame(unsigned char y) { char num; int tt; for (num=0; num<8; num++) //循環8次移位 { tt=25; //輸出0.65ms 1 do {IROUT=0; Delay19us(); IROUT=1; Delay7us();} while(--tt); if(y&0x01) //if為1 { tt=16; //輸出1.65ms 0 do {IROUT=1; Delay100us();} while(--tt); } else //否則 { tt=5; //輸出0.56ms 0 do {IROUT=1; Delay100us();} while(--tt); } y >>= 1; //右移一位 } } /************************************************************************************************** * 讀取紅發外值 * 外部中斷的服務程序 */ void ReadIr() interrupt 0 { unsigned int err; unsigned int tt; unsigned char Time; unsigned char j, k; EA=0; //關閉總中斷 Time=0; tt=80; //延時8ms去干擾,引導碼為9ms的低電平和4.5ms的高電平 do { Delay100us(); } while(--tt); if(IRIN==0) //確認是否真的接收到正確的信號 { err=500; //超時判斷 while((IRIN==0)&&(err>0)) //等待高電平,最多等待50ms { Delay100us(); err--; } if(IRIN==1) //收到高電平 { err=500; //超市判斷 while((IRIN==1)&&(err>0)) //等待4.5ms的高電平過去 { Delay100us(); err--; } for(k=0; k<4; k++) //開始接收數據,共有4個數據 { for(j=0; j<8; j++) //每個8位 { err=60; while((IRIN==0)&&(err>0)) //等待信號前面的560us低電平過去 { Delay100us(); err--; } err=500; while((IRIN==1)&&(err>0)) //計算高電平的時間長度。 { Delay100us(); Time++; err--; if(Time>50) //超過5ms說明接收到重復碼。重復碼:9ms高電平+2.25ms低電平+560us高電平 { EA=1; //打開總中斷 return; //退出函數 } } IrValue[k]>>=1; //k表示第幾組數據 if(Time>=8) //若高電平出現大於0.8ms,為1(1.65ms為1,0.56ms為0,這里取中間值) IrValue[k]|=0x80; Time=0; //用完時間要重新賦值 } } } if(IrValue[2]=~IrValue[3]) //這里一般只判斷鍵值而不判斷用戶碼,因為對於一些用戶碼是0的遙控無論怎樣都是0 { flag_Ir = 1; //如果按鍵值與按鍵值的補碼的取反相同,則接收完成標志置位 } } EA=1; //打開總中斷 }
