tag 标签: 发射

相关博文
  • 热度 20
    2014-11-21 21:25
    10938 次阅读|
    3 个评论
    STC的51单片机红外遥控器读码、发射程序,已试成功 wxleasyland@sina.com 2014.11.21   有一台DVD机没有遥控器,正好别的遥控器有的键可以用,但功能不一样。   于是开展本工程,程序原为网上摘的,经过修改均已全部成功。   采用STC的51单片机,STC12C5A60S2,可以直接串口编程,而且是1T的,非常方便。   一、红外遥控器读码 读码程序没怎么修改就成功了。 注意:这里的延时程序是STC12C5A60S2的,如果用别的单片机,需要修改。   #include STC\STC12C5A60S2.H #include INTRINS.h    //采用1T周期的STC12C5A60S2单片机,11.0592MHZ //WXL:一体化接收头默认是输出高电平,有信号时输出低电平;接P3.2脚。 //WXL:这里按“低位在先”   /******************************************************************/ /* 本程序的蓝本从网上搜集,经修改并注释,万能遥控器解码成功 */ /* 晶振:11.0592MHz */ /* 整理与测试:单片机教程网  胡琴 2012.5.15 */ /************************* 说 明 *********************************/ /* 以一个9ms的低电平和4.5ms的高电平为引导码,后跟32位二进制代码 */ /* 前16位为8位用户码及其反码,后16位为8位的操作码及其反码 */ /* 以脉宽为低电平0.565ms、间隔高电平0.56ms、周期为1.125ms的组合表示"0"; */ /* 以脉宽为低电平0.565ms、间隔高电平1.685ms、周期为2.25ms的组合表示"1"。 */ /* 注意:接收码的脉宽与间隔是对发射码取反的,即间隔是0.565ms */ /* 解码后共有四个十六进制码,本程序取第三个作为识别码 */ /*******************************************************************/   #define uchar unsigned char uchar data IRcode ; //定义一个4字节的数组用来存储代码 uchar CodeTemp; //编码字节缓存变量 uchar i,j,k; //延时用的循环变量 sbit IRsignal=P3^2; //HS0038接收头OUT端直接连P3.2(INT0) sbit P0_0=P0^0; //P0连接到 LED 上 sbit P0_1=P0^1; sbit P0_2=P0^2;       /**************************延时0.6ms子程序**********************/ void Delay0_6ms(void) //@11.0592MHz { unsigned char i, j;   _nop_(); _nop_(); i = 7; j = 112; do { while (--j); } while (--i);   }     /**************************延时0.9ms子程序**********************/ void Delay0_9ms(void) //@11.0592MHz { unsigned char i, j; _nop_(); _nop_(); _nop_(); i = 10; j = 170; do { while (--j); } while (--i); }         /***************************延时1ms子程序**********************/ void Delay1ms(void) { unsigned char i, j;   _nop_(); i = 11; j = 190; do { while (--j); } while (--i); }   /***************************延时4ms子程序**********************/ void Delay4ms(void) { unsigned char i, j;   _nop_(); _nop_(); _nop_(); i = 44; j = 3; do { while (--j); } while (--i); }   /**************************** 延时子程序 ************************/ void Delay(void) { uchar i,j,k; for(i=200;i0;i--) for(j=200;j0;j--) for(k=3;k0;k--)  ; }   /******************** 中断0解码服务子程序 ********************/ void int0(void) interrupt 0 using 2 { EA = 0; //??? 可以这样,跳入中断,但仍可对P3.2(INT0)进行电平变化的读取 for(k=0;k10;k++) { Delay0_9ms(); if (IRsignal==1)        //如果0.9ms后IRsignal=1,说明不是引导码,退出中断 { k=10; break; }   else if(k==9)         //如果 持续了10×0.9ms=9ms的低电平,说明是引导码。WXL:一定是从引导码开始 { while(IRsignal==0);      // WXL:因为红外头默认输出是高电平,故用while(IRsignal==0)很安全,而用while(IRsignal==1)则可能会进入死循环 Delay4ms();       //跳过持续4.5ms的高电平     WXL:要超过4.5ms更好 Delay0_6ms();   for(i=0;i4;i++)     //分别读取4个字节 { for(j=1;j=8;j++)    //每个字节8个bit的判断 { while(IRsignal==0); //等待上升沿,此处用得很好:因为0.56ms的低电平(接收时)是代码0与1的相同部分 Delay0_9ms(); //从上升沿那一时刻开始延时0.9ms(因为0.9介于0.56(=1.125-0.56)与1.69(=2.25-0.56)之间),再判断IRsignal if(IRsignal==1) //  如果IRsignal是"1",高位置"1",并向右移一位 { Delay1ms(); //为什么要延时1ms呢?因为要使IRsignal跳至低电平(即0.56ms的0与1相同部分上) CodeTemp=CodeTemp | 0x80; //此处的算法很好 if(j8)  CodeTemp=CodeTemp1; } else       //  如果IRsignal是"0",高位置"0",并向右移一位 if(j8) CodeTemp=CodeTemp1;   //如果IRsignal是"0",则直接向右移一位,自动补"0" } IRcode =CodeTemp; CodeTemp=0; }   //end for   for(i=0;i4;i++) //通过串口将代码发出 { SBUF=IRcode ; while(!TI); //等待一个字节发送完毕 TI=0; }   Delay(); }    //end else }   //END for EA = 1; }   /***********************串口初始化程序*********************/ void initUart(void) { TMOD |= 0x20; // SCON = 0x50; // PCON |= 0x80; // TH1 = 250; // 9600 bps @ 11.0592MHz TL1 = 250; TR1 = 1; }   /**************************主程序*************************/ void main() { //P0=0XFF; initUart(); IT0 = 1; //INT0为负边沿触发, (1:负边沿触发,0:低电平触发) EX0 = 1; //外部中断INT0开, (1:开, 0:关 ) EA = 1; //开所有中断   CodeTemp = 0; //初始化红外编码字节缓存变量 Delay(); while(1) {   } }             二、红外遥控发射   网上的程序是http://gudeng614.blog.163.com/blog/static/818017420101545648734/   做发射程序费了很大波折,因为网上的程序不好用。   后来不得不用计算机的并口采集了发射数据,发现数据有异常,终于找到了问题所在。   原因是count变量是int的,对其赋值或比较时,汇编语句一句完不成,会被中断服务程序中断,造成count变量赋值或比较出现问题。 解决方法是必须在操作时屏蔽中断。而flag变量是bit的,一句汇编即可完成赋值,故不会有问题。   其间还发现别的遥控器会在起始码前加一个前脉冲,以为是这个问题,其实不是。   注意:由于13us会中断一次,这里是采用1T的单片机。如果采用普通的51单片机,由于是12T的,不知道能不能成功。   //程序从网上修改而来 //由于中断需要13us中断一次,即中断要在几us处理完,因此需要单片机速度比较快,用24MHZ晶振才能保证正常 //但24MHZ晶振,用串口不方便 //这里采用1T周期的STC12C5A60S2单片机,11.0592MHZ,可以兼顾。 //STC12C5A60S2  引脚可灌入20mA电流,直接从正电源→红外LED→串1K电阻→P0.0脚。 //串口1默认选T1作为波特率发生器 //TO用于中断 //发送时,低比特位优先   #include STC\STC12C5A60S2.H #include INTRINS.h   sbit  P0_0 = P0^0;   static bit g_OP;            //红外发射管的亮灭 static unsigned int g_count;       //延时计数器 static unsigned int g_endcount;    //终止延时计数 static bit g_flag;       //红外发送标志 unsigned char g_iraddr1;     //十六位地址的第一个字节 unsigned char g_iraddr2;     //十六位地址的第二个字节     //定时器0中断处理  void timeint(void) interrupt 1  {    g_count++;   if (g_flag)    g_OP=~g_OP;   else    g_OP = 1;    //LED不点亮   P0_0 = g_OP; }     ///////////////////////////////////////////////////// void SendIRdata_38KHZ(unsigned int temp1, bit temp2) {   g_endcount=temp1;    g_flag=temp2;   EA=0; g_count=0; EA=1;  //避免中断影响count置数   while(1)   {   EA=0;    if( g_count g_endcount ) EA=1;  //避免中断影响count比较   else    {    EA=1;    break;    }     }   }   ///////////////////////////////////////////////////// void SendIRdata_BYTE(unsigned char irdata) {   unsigned char i;   for(i=0;i8;i++)   {      //先发送0.56ms的38KHZ红外波(即编码中0.56ms的高电平) SendIRdata_38KHZ(43, 1);   //13.02*43=0.56ms       //停止发送红外信号(即编码中的低电平)      if(irdata 1)  //判断最低位为1还是0。   低位先发送!!        SendIRdata_38KHZ(130, 0);         //1为宽电平,13.02*130=1.693ms     else      SendIRdata_38KHZ(43, 0);   //0为窄电平,13.02*43=0.560ms               irdata=irdata1;   } }   ///////////////////////////////////////////////////// void SendIRdata(unsigned char p_irdata) {   //有的遥控器会发一个前脉冲,如果不灵,可试试加上前脉冲   //发送起始码前脉冲,高电平有38KHZ载波   //SendIRdata_38KHZ(18, 1);   //发送起始码前脉冲,低电平无38KHZ载波   //SendIRdata_38KHZ(18, 0);     //发送9ms的起始码,高电平有38KHZ载波   SendIRdata_38KHZ(692, 1); //13.02*692=9.010ms     //发送4.5ms的结果码,低电平无38KHZ载波   SendIRdata_38KHZ(346, 0);    //13.02*346=4.505ms     //发送十六位地址的前八位   SendIRdata_BYTE(g_iraddr1);     //发送十六位地址的后八位   SendIRdata_BYTE(g_iraddr2);     //发送八位数据   SendIRdata_BYTE(p_irdata);     //发送八位数据的反码   SendIRdata_BYTE(~p_irdata);       //发送总的结束位1bit   SendIRdata_38KHZ(43, 1);     //13.02*43=0.56ms     /*  //后面这些可以不用发   g_endcount=1766;    g_flag=0;   EA=0; g_count=0; EA=1;   while(1){EA=0; if(g_countg_endcount) EA=1; else { EA=1; break; }  }        //发送9ms的起始码,高电平有38KHZ载波   g_endcount=692;   //13.02*692=9.010ms   g_flag=1;   EA=0; g_count=0; EA=1;   while(1){EA=0; if(g_countg_endcount) EA=1; else { EA=1; break; }  }        //发送4.5ms的结果码,低电平无38KHZ载波   g_endcount=346;    //13.02*346=4.505ms   g_flag=0;   EA=0; g_count=0; EA=1;   while(1){EA=0; if(g_countg_endcount) EA=1; else { EA=1; break; }  }      //发送总的结束位1bit   g_endcount=43;    //13.02*43=0.56ms   g_flag=1;   EA=0; g_count=0; EA=1;   while(1){EA=0; if(g_countg_endcount) EA=1; else { EA=1; break; }  }      */     g_flag=0;     }       /////////////////////////////////////////////////////////// void main(void)  { unsigned char com_data;    //数据字节      g_count = 0;   g_flag = 0;   g_OP = 1;    P0_0 = g_OP;   //LED接电源正极,不点亮   SCON=0x50;  //串口方式1    01 0 1 0 0 00  模式1,非多机,允许接收,无数据位8,清中断标识TI和RI     TMOD = 0x22; //(定时器0和1:方式2,自动重装,8位) TH1=253;  //11.0592MHZ,9600bps。没有设置SMOD,故波特率没有加倍。即:11.0592/12/3/32=9600bps TL1=253; TR1=1;    //启动定时器     TH0 = 244;    TL0 = 244; //(WXL:即计数12次中断一次,即11.0592MHZ晶振,机器周期是1.085us,12次*1.085=13.02us,这样达38KHZ。  13us一次中断,时间太短了,所以单片机要快)   ET0 = 1;   //定时器0中断允许   EA = 1;    //允许CPU中断    TR0 = 1;   //开始计数     g_iraddr1=0;       //地址码   g_iraddr2=255;     //地址反码   RI=0; while(1) { if(RI==1)  { com_data =SBUF; RI=0;       //要人工清RI SendIRdata(com_data);   //发送红外数据 TI=0; SBUF = com_data;   //输出字符 while(!TI) ;     //空语句判断字符是否发完,TI=1表示发完 TI = 0;         //要人工清TI } }   }            
相关资源