zigbee 万能遥控器 裸机发送和协议栈发送
HX1010
http://www.sbprojects.com/knowledge/ir/index.php
遥控编码
http://www.sbprojects.com/knowledge/ir/rc5.php
http://www.sbprojects.com/knowledge/ir/nec.php
发送方:波形图上的高电平对应发相同时间长度的38KHZ方波;波形图上的低电平对应停止发送对应时间长度的方波(即保持低或高电平即可)
比如在发送方发送引导码时,先发送9ms的38KHZ防波,然后停止发送4.5ms。接着发送数据。
发送数据0时,先发送0.56ms的38KHZ方波,然后停止发送0.56ms.接着发送下一位。发送数据1时,先发送0.56ms的38KHZ方波,然后停止发送1.68ms.接着发送下一位。全部数据发送完成之后,就停止发送。
接收方:IRM3638,平时没接收到38KHZ方波时,输出高电平;接收到38KHZ方波时,持续输出低电平,直到方波消失。
比如在接收到波形图上的高电平时(即接收发送者发送的方波时),持续输出低电平,直到法波消失。
http://www.100y.com.tw/pdf_file/FM-6038LM-5A.pdf
裸机发送:
#include <ioCC2530.h> #define IROUT P1_1 //红外发射脚 #define RLED P1_0 //红外接收 #define uchar unsigned char #define uint unsigned int /***************************************** //定义全局变量 *****************************************/ //接收模式 unsigned int irtime;//红外用全局变量 unsigned char irok;//=1,表示一组连续的红外代码已经成功接收完毕 unsigned char irpro_ok;//=1,表示红外代码已经解析完毕 unsigned char IRCode[4]; //处理后的红外码,分别是 客户码,客户码,数据码,数据码反码 unsigned short __xdata IRSourceData[160]; //33个高低电平的时间数据 unsigned char __xdata IRSourceData2[14]= { 0x65,0x4b,0x00,0x00, 0x02,0x05,0x00,0x00, 0x00,0x00,0x00,0x00, 0xa0,0xba };//haier kogntiao kaiji 29 static char ifg=0;//每次按下遥控器时,会触发多次红外中断,只需在第一次红外中断时开启定时器,ifg=0表示初次红外中断 //发射模式 uint counter = 0; uchar LEDFlag = 0; uchar ucIs38KHZ=0; uint uiCurrentCount=0; uint uiTotalCount=0; void Delay(uint n) { uint i; for(i = 0;i<n;i++); for(i = 0;i<n;i++); for(i = 0;i<n;i++); for(i = 0;i<n;i++); for(i = 0;i<n;i++); } extern void SendIRdata2(char cData); //////////////////////////////////////////////////////////////////////////////////////////////////////////接收模式的函数 void Ircordpro(void)//红外码值处理函数 { unsigned char i, j, k; unsigned int cord,value; k=1; for(i=0;i<4;i++) //处理4个字节 { for(j=1;j<=8;j++) //处理1个字节8位 { cord=IRSourceData[k]; if(cord>67*2)//大于某值为1,这个和晶振有绝对关系,这里使用12M计算,此值可以有一定误差 value=value|0x80; else value=value; if(j<8) value=value>>1; k++; } IRCode[i]=value; value=0; } irpro_ok=1; } //红外接收中断处理 #pragma vector = P1INT_VECTOR __interrupt void P1_ISR(void) { if(P1IFG & 0x01) //红外中断 { //需要在前面清理中断标志 P1IFG &= 0; P1IF=0; static unsigned int iIndex=0; //每次按下遥控器时,会触发多次红外中断,只需在第一次红外中断时开启定时器,ifg=0表示初次红外中断 if(ifg==0) { T4CTL |= 0x10; //启动定时器T4 ifg=1; irtime=0; iIndex=0;//重新开始 for(int i=0;i<160;i++)//初始化数组为0 IRSourceData[i]=0; return ;//第一个下降沿测得的irtime不要,因为=0 } //if(irtime<605*2&&irtime>=317*2)//引导码 TC9012的头码,9ms+4.5ms // i=0; IRSourceData[iIndex]=irtime; irtime=0; iIndex++; } if(P1IFG & 0x04) //按键中断 { Delay(5); if(P1IFG & 0x04) { //需要在前面清理中断标志 P1IFG &= 0; //清中断标志 P1IF=0; Delay(5); SendIRdata2(68); P1_4=0;//表示发送完毕,进行下一个接收 } } } //接收定时器,产生38KHZ方波 #pragma vector = T4_VECTOR __interrupt void T4_ISR(void) { if(TIMIF &= 0x18) //Timer4溢出? { irtime++; //判断对方的一组连续的代码是否发送完毕 //如果长时间(>6000)未接收到相邻的一个下降沿,判定此组红外代码完毕 //此时就停掉定时器,并且 //置ifg标志为0,为下一组代码做初始化的准备 //置irok标志为1,表示此组红外代码已经接收完毕,后续处理使用此标志 if(irtime>6000) { T4CTL &= ~0x10; // 停止定时器T4 ifg=0; irok=1; } //需要在最后清理中断标志 TIMIF &= ~0x18; //清除Timer4溢出中断标记 IRCON &= ~0x10; //清除Timer4中断标记 } } //初始化红外接收 //p1.0是红外接收脚,需配置为中断,下降沿触发。核心板的第14脚。也是LED1. void InitReceiveIR(void) { EA=0; P1INP |= 0x01; //上拉 P1IEN |= 0X01; //P10设置为中断方式 PICTL |= 0X02; //下降沿触发 IEN2 |= 0x10; // P1 使能中断; P1IFG |= 0x00; //初始化中断标志位 //EA = 1; } //初始化定时器4 void InitT4(void) { EA=0; T4CTL = 0x07; //不分頻,Up-Down Mode(0x00->T1CC0->0x00) T4CC0 = 0x30; //设定初值 TIMIF &= ~0x18; //清除Timer4溢出中断标记 T4CTL |= 0x08; //设定Timer4溢出中断使能 IRCON &= ~0x10; //清除Timer4中断标记 IEN1 |= 0x10; //设定Timer中断使能 //EA = 1; } //////////////////////////////////////////////////////////////////////////////////////////////////////////接收模式的函数 //////////////////////////////////////////////////////////////////////////////////////////////////////////发送模式的函数 void StartAndSendIRData(uint TotalCoun,uchar Is38KHZ) { #if 1 ucIs38KHZ=Is38KHZ; uiCurrentCount=0; do{}while(uiCurrentCount<TotalCoun); ucIs38KHZ=0;//发送完成之后停止发射载波 #endif } void SendIRdata(char cData) { if(IRSourceData[0]==0) return; //开启定时器T4 T4CTL |= 0x10; int i,j; unsigned short irdata=0; //发送引导码 irdata=IRSourceData[0]; //先发送0.56ms的38KHZ红外波(即编码中0.56ms的高电平) StartAndSendIRData(irdata/2,1); //停止发送红外信号(即编码中的低电平) StartAndSendIRData(irdata/2,0); //1为宽的低电平 //StartAndSendIRData(irdata,0);//0为窄的低电平 irdata=IRSourceData[1]; //先发送0.56ms的38KHZ红外波(即编码中0.56ms的高电平) StartAndSendIRData(irdata/2,1); //停止发送红外信号(即编码中的低电平) StartAndSendIRData(irdata/2,0); //1为宽的低电平 //StartAndSendIRData(irdata,0);//0为窄的低电平 //发送数据码 j=2; while(IRSourceData[j]!=0) { irdata=IRSourceData[j]; if(irdata>120){ StartAndSendIRData(irdata/2,1); StartAndSendIRData(irdata/2,0); } else { StartAndSendIRData(irdata/4,1); StartAndSendIRData(irdata*3/4,0); } j++; } // 再发送一位,以便使得接收端产生一个中断,确定上一位的持续时间 irdata=0x1; for(i=0;i<1;i++) { //先发送0.56ms的38KHZ红外波(即编码中0.56ms的高电平) StartAndSendIRData(22,1); //停止发送红外信号(即编码中的高电平) if(irdata & 1) StartAndSendIRData(65,0); //1为宽的低电平 else StartAndSendIRData(22,0);//0为窄的低电平 irdata=irdata>>1; } //发送完毕时关闭定时器T4 T4CTL &= ~0x10; } void SendIRdata2(char cData) { int i; unsigned char irdata; T3CTL |= 0x10; #if 0 //发送9ms的38KHZ起始码 StartAndSendIRData(346,1); //停止发送4.5ms,属于起始码 StartAndSendIRData(173,0); #endif //发送9ms的38KHZ起始码 StartAndSendIRData(346*48/100,1); //停止发送4.5ms,属于起始码 StartAndSendIRData(173*48/100,0); //发送9ms的38KHZ起始码 StartAndSendIRData(346*60/98,1); //停止发送4.5ms,属于起始码 StartAndSendIRData(173*60/98,0); for(int j=0;j<14;j++) { irdata=IRSourceData2[j]; for(i=0;i<8;i++) { //先发送0.56ms的38KHZ红外波(即编码中0.56ms的高电平) StartAndSendIRData(22,1); //停止发送红外信号(即编码中的低电平) if(irdata & 1) StartAndSendIRData(65,0); //1为宽的低电平 else StartAndSendIRData(22,0);//0为窄的低电平 irdata=irdata>>1; } } // 再发送一位,以便使得接收端产生一个中断,确定上一位的持续时间 irdata=0x1; for(i=0;i<1;i++) { //先发送0.56ms的38KHZ红外波(即编码中0.56ms的高电平) StartAndSendIRData(22,1); //停止发送红外信号(即编码中的高电平) if(irdata & 1) StartAndSendIRData(65,0); //1为宽的低电平 else StartAndSendIRData(22,0);//0为窄的低电平 irdata=irdata>>1; } T3CTL &= ~0x10; } //发送定时器,产生38KHZ方波 #pragma vector = T3_VECTOR __interrupt void T3_ISR(void) { if(TIMIF &= 0x01) //Timer4溢出? { uiCurrentCount++; if(ucIs38KHZ==1) IROUT = !IROUT;//载波发射 else IROUT =0;//不发射,保持0或者1均可 //需要在最后清理中断标志 TIMIF &= ~0x01; //清除Timer4溢出中断标记,cpu自动也会清除 } IRCON &= ~0x08; //清除Timer4中断标记,,cpu自动也会清除 } //初始化红外发送 //p1.1是红外发送脚,需配置为输出。核心板的第13脚。也是LED2. void InitSendIR(void) { P1SEL &= ~0x02; //设定P1_1为通用I/O P1DIR |= 0x02; IROUT = 1; } //初始化定时器3,用于产生发送方波,38KhZ void InitT3(void) { EA=0; T3CTL = 0x07; //不分頻,Up-Down Mode(0x00->T1CC0->0x00) T3CC0 = 0x30; //设定初值 TIMIF &= ~0x01; //清除Timer4溢出中断标记 T3CTL |= 0x08; //设定Timer4溢出中断使能 IRCON &= ~0x08; //清除Timer4中断标记 IEN1 |= 0x08; //设定Timer中断使能 //EA = 1; } //////////////////////////////////////////////////////////////////////////////////////////////////////////发送模式的函数 //设置发送键P1_2即中断 void InitKey(void) { //P1SEL &= ~0x04; //设定P1_2为通用I/O //P1DIR &= ~0x04; //P1INP &= ~0x04; EA=0; P1INP |= 0x04; //上拉 P1IEN |= 0X04; //P12设置为中断方式 PICTL |= 0X02; //下降沿触发 IEN2 |= 0x10; // P1 使能中断; P1IFG |= 0x04; //初始化中断标志位 } //设置学习成功指示灯P1_4即S2为通用,输出 void InitLED(void) { P1SEL &= ~0x10; //设定P1_4为通用I/O P1DIR |= 0x10; P1INP &= ~0x10; P1_4=0; } main() { InitLED(); InitT4(); InitReceiveIR(); InitKey(); InitT3(); InitSendIR(); EA = 1; while(1)//主循环 { #if 1 if(irok) //如果接收好了进行红外处理 { //Ircordpro(); irok=0; P1_4=1;//表示接受完毕。可以发送 } if(irpro_ok) //如果处理好后进行工作处理,如按对应的按键后显示对应的数字等 { // Ir_work(); } #endif } }
协议栈 接收
2013-5-29 18:01:45
使用两个外部中断,上升沿和下降沿触发,可以识别所有红外协议
/** bysong 2013-3-11 8:19:28 process receiving the ir signal */ #include "OSAL.h" #include "ZGlobals.h" #include "AF.h" #include "aps_groups.h" #include "ZDApp.h" #include "OnBoard.h" /* HAL */ #include "hal_lcd.h" #include "hal_led.h" #include "hal_key.h" #include "sapi.h" #include "smt_ir_rec.h" //#include "zcl_general.h" //中断数目 #define MAX_IRDATA_NUM 230 #define MAX_IRHEAD_NUM 10 #define MAX_IRTAIL_NUM 10 #define MAX_IR_BYTE_NUM ((MAX_IRHEAD_NUM+MAX_IRTAIL_NUM+MAX_IRDATA_NUM)*2) static uint16 *m_pu16IRSourceData=NULL; //MAX_DATALEN个高低电平的时间数据 //static uint16 __xdata m_pu16IRSourceData3[MAX_IRDATA_NUM]; //MAX_DATALEN个高低电平的时间数据 //static uint16 __xdata m_szu16IRSourceDataR[MAX_DATALEN]; //MAX_DATALEN个高低电平的时间数据 static uint16 m_u16SourceDataTotalNum;//实际接收到的有效位个数,包括data,head和tail static uint32 m_u32IRtime;//红外用全局变量 static bool m_bIRok=0;//红外位数据已经接收到 static uint8 m_u8Ifg=0;//每次按下遥控器时,会触发多次红外中断,只需在第一次红外中断时开启定时器,m_u8Ifg=0表示本次中断是初次红外中断 //初始化红外接收 //由于p1.0-led1-irin已经作为led显示使用,协议栈中很多地方都使用led1作为指示使用,所以选用了p1.3-botton2, //此时需要隔断红外芯片的1脚,直接连到p1.3,即p16的37脚, //由于红外1脚在有载波信号时输出0,所以p1.3最好要上拉, void smt_ir_rec_InitReceiveIR(void) { if ( m_pu16IRSourceData== NULL) m_pu16IRSourceData=(uint16 *)osal_mem_alloc(MAX_IR_BYTE_NUM); if ( m_pu16IRSourceData== NULL) SystemReset(); osal_memset(m_pu16IRSourceData,0,MAX_IR_BYTE_NUM); //return; EA=0; #if 1 // 使用p1.3,button2 P1INP |= 1<<3; //上拉 P1IEN |= 1<<3; //P13中断使能 PICTL |= (1<<1); //下降沿触发 #endif #if 1 // 使用p1.7 P1INP |= 1<<7; //上拉 P1IEN |= 1<<7; //P17中断使能 PICTL &= ~(1<<2); //上升沿触发 #endif P1IFG |= 0x00; //初始化中断标志位 IEN2 |= 1<<4; // P1 使能中断; EA = 1; } void smt_ir_rec_StopReceiveIR(void) { // 使用p1.3,button2 //P1INP |= 0x01; //上拉 if(m_pu16IRSourceData!=NULL) { osal_mem_free(m_pu16IRSourceData); m_pu16IRSourceData=NULL; } //return; //P1IEN &= ~0x08; P1IEN &= ~(1<<3); P1IEN &= ~(1<<7); IEN2 &= ~(1<<4); // EA = 0; } //红外接收中断处理 /* #pragma vector = P1INT_VECTOR __interrupt void smt_ir_rec_P1_ISR(void) #pragma vector = P1INT_VECTOR __interrupt void smt_ir_rec_P1_ISR(void) */ #if 1 HAL_ISR_FUNCTION( smt_ir_rec_P1_ISR, P1INT_VECTOR ) { static uint16 u16_Index=0; if(P1IFG & 0x08) //红外中断,p1.3,下降沿中断中记录的是上次高电平电平持续的时间 { P1IFG = 0; P1IF=0; //if(P1IFG==0) //红外中断 { //每次按下遥控器时,会触发多次红外中断,只需在第一次红外中断时开启定时器,m_u8Ifg=0表示初次红外中断 if(m_u8Ifg==0) { smt_ir_rec_InitT4();//启动定时器T4。如果直接使用T4CTL |= 0x10去启动,有时候会启动失败 m_u8Ifg=1; m_u32IRtime=0; u16_Index=0;//重新开始 m_u16SourceDataTotalNum=0; osal_memset( m_pu16IRSourceData, 0, MAX_IR_BYTE_NUM ); // osal_memset( m_szu16IRSourceDataR, 0, sizeof(m_szu16IRSourceDataR) ); return ;//第一个下降沿测得的irtime不要,因为=0 } if( m_u16SourceDataTotalNum < MAX_IR_BYTE_NUM/2) { m_pu16IRSourceData[u16_Index++]=m_u32IRtime;//t4中断次数存储起来 m_u16SourceDataTotalNum=u16_Index; m_u32IRtime=0;//清零,为下一次中断准备 } } } #if 1 if(P1IFG & 0x80) //红外中断,p1.7,上升沿中断中记录的是上次低电平持续的时间 { P1IF = 0; P1IFG = 0; //if(P1IFG==0) //红外中断 { if( m_u16SourceDataTotalNum < MAX_IR_BYTE_NUM/2) { m_pu16IRSourceData[u16_Index++]=m_u32IRtime;//t4中断次数存储起来 m_u16SourceDataTotalNum=u16_Index; m_u32IRtime=0;//清零,为下一次中断准备 } } } P1IF = 0; P1IFG = 0; /* if(P1IFG!=0) { P1IF = 0; P1IFG = 0; } */ #endif } #endif /* 定时器t4在hal_timer.c中已经定义了中断处理函数,所以先要在hal_timer.c中注释掉,如下 HAL_ISR_FUNCTION( halTimer4Isr, T4_VECTOR ) { halProcessTimer4 (); } 否则中断发生时会进入hal_timer.c中的中断处理函数中执行 */ /* #pragma vector = T4_VECTOR __interrupt void smt_ir_rec_T4_ISR(void) #pragma vector = T4_VECTOR __interrupt void smt_ir_rec_T4_ISR(void) */ #if 1 HAL_ISR_FUNCTION( smt_ir_rec_T4_ISR, T4_VECTOR ) { // if(TIMIF &= 0x18) //Timer4溢出? { m_u32IRtime++; //判断对方的一组连续的代码是否发送完毕 //如果长时间(>16000)未接收到相邻的一个下降沿,判定此组红外代码完毕 //此时就停掉定时器,并且 //置ifg标志为0,为下一组代码做初始化的准备 //置irok标志为1,表示此组红外代码已经接收完毕,后续处理使用此标志 if(m_u32IRtime>16000) { T4CTL &= ~0x10; // 停止定时器T4 m_u8Ifg=0; if( m_pu16IRSourceData[0]<6 || m_pu16IRSourceData[1]<6 || m_pu16IRSourceData[2]<6 || m_pu16IRSourceData[3]<6 || m_pu16IRSourceData[4]<6 || m_pu16IRSourceData[5]<6 || m_pu16IRSourceData[6]<6 || m_pu16IRSourceData[7]<6 )//在自己发射时,虽然禁止了接受,但也会检测到全是0的ir码,需排除此种情况 m_bIRok=0; else m_bIRok=1; } //TIMIF &= ~0x18; //清除Timer4溢出中断标记 IRCON &= ~0x10; //清除Timer4中断标记 } } #endif //初始化定时器4 void smt_ir_rec_InitT4(void) { T4CTL = 0x07; //不分頻,Up-Down Mode(0x00->T1CC0->0x00) T4CC0 = 210; //设定初值 TIMIF &= ~0x18; //清除Timer4溢出中断标记 T4CTL |= 0x08; //设定Timer4溢出中断使能 IRCON &= ~0x10; //清除Timer4中断标记 IEN1 |= 0x10; //设定Timer中断使能 EA = 1; T4CTL |= 0x10; //启动定时器T4 } //m_u16SourceDataTotalNum是中断个数,每个占用2个字节 //为了节约资源,传输时,前面10个中断数每个占2个字节,后面10个每个中断数每个占2个字节,中间每个占1个字节 uint16 smt_ir_rec_GetIRSourceData(uint8 * pu8IRSourceData) { //return 20; if(m_bIRok==1) { m_bIRok=0; uint16 byteNum =0; byteNum= Uint162Ir(m_pu16IRSourceData,m_u16SourceDataTotalNum<<1,pu8IRSourceData); osal_memset( m_pu16IRSourceData, 0, MAX_IR_BYTE_NUM ); // byteNum=200; return byteNum; } else return 0; } /** * 返回转化为1字节的数目的字节总数目 */ uint16 Uint162Ir(uint16 *pSrc,uint16 srcByteCnt,uint8 *pDst) { uint16 ret=0; if(srcByteCnt<=0) return 0; if(srcByteCnt>(MAX_IRHEAD_NUM+MAX_IRHEAD_NUM+MAX_IRDATA_NUM)*2 ) return 0; if(srcByteCnt <= (MAX_IRHEAD_NUM+MAX_IRHEAD_NUM)*2) { osal_memcpy(pDst,pSrc,srcByteCnt); ret=srcByteCnt; } else if(srcByteCnt > (MAX_IRHEAD_NUM+MAX_IRHEAD_NUM)*2) { uint16 dataByteCnt=( srcByteCnt- ((MAX_IRHEAD_NUM+MAX_IRHEAD_NUM) *2 ) ); osal_memcpy(pDst,pSrc,MAX_IRHEAD_NUM*2); osal_memcpy(&pDst[MAX_IRHEAD_NUM*2+dataByteCnt/2],&pSrc[MAX_IRHEAD_NUM+dataByteCnt/2],MAX_IRTAIL_NUM*2); //process data //2字节数据--》1字节数据 for(uint16 i=0;i<dataByteCnt/2;i++) { //pDst是单字节类型 //pSrc是双字节类型 pDst[MAX_IRHEAD_NUM*2+i]=pSrc[MAX_IRHEAD_NUM+i]; } ret=(dataByteCnt>>1) + ((MAX_IRHEAD_NUM+MAX_IRHEAD_NUM)<<1); } return ret; } /** * 返回转化为2字节的数目的字节总数目 */ uint16 Ir2Uint16(uint8 *pSrc,uint16 srcByteCnt,uint16 *pDst) { uint16 ret=0; if(srcByteCnt <= (MAX_IRHEAD_NUM+MAX_IRHEAD_NUM)*2) { osal_memcpy(pDst,pSrc,srcByteCnt); ret=srcByteCnt; } else if(srcByteCnt > (MAX_IRHEAD_NUM+MAX_IRHEAD_NUM)*2) { uint16 dataNum=srcByteCnt-(MAX_IRHEAD_NUM+MAX_IRHEAD_NUM)*2; //cout<<"srcByteCnt "<<srcByteCnt<<endl; //cout<<"dataNum "<<dataNum<<endl; osal_memcpy(pDst,pSrc,MAX_IRHEAD_NUM*2); osal_memcpy(&pDst[MAX_IRHEAD_NUM+dataNum],&pSrc[MAX_IRHEAD_NUM*2+dataNum],MAX_IRTAIL_NUM*2); //process data //1字节数据--》2字节数据 for(uint16 i=0;i<dataNum;i++) {//pSrc 是单字节类型 //pDst是双字节类型 pDst[MAX_IRHEAD_NUM+i]=pSrc[MAX_IRHEAD_NUM*2+i]; } ret=(dataNum+MAX_IRHEAD_NUM+MAX_IRHEAD_NUM)*2; } return ret; } /* 本文件内函数调用顺序 smt_ir_rec_InitReceiveIR(); 即直接在外部函数中调用此函数就可以接收红外信号了 定时器在接收到红外中断信号的时候启动 smt_ir_rec_GetIRCmd(); */