BXAK 发表于 2011-10-28 21:06:50

发一个红外线解码程序+仿真文件(可定义任意I/O作接收脚,支持长/短按,自适应主频6MHz~40

http://cache.amobbs.com/bbs_upload782111/files_47/ourdev_689712TW2D7X.jpg
(原文件名:TP0.jpg)




//*********************【 NEC解码头文件 】*******************
//
//   简介:本程序适用于NCE解码:(9ms+4.5ms)引导码+32位编码。
//         兼容STC所有型号(包括 1T 和 12T 系列),可以定义任意I/O作红外接收脚,
//                       自适应解码主频:6MHz ~ 40MHz。
//
// 使用条件:占用系统定时器0,开启定时器0中断(如使用其它定时器请自改IR_Init();初始化函数)
//
// 使用说明:填相关宏定义:USER_H、USER_L、Check_EN、CPU_Fosc、IR,
//         上电初始化函数IR_Init(),
//         在定时器0中断中调用IR_NEC()解码函数,
//         解码有效时,IR_BT=2即短按,IR_BT=3即长按,由用户清0,
//         解码存放:用户码高8位NEC,用户码低8位NEC,操作码NEC,操作码反码NEC。
//
//【供用户调用的函数】
//    IR_Init();                       //接收初始化,开启定时器0中断400us
//    IR_NEC();                             //红外线解码(解NEC编码)
//          
//***************************************************************/
#ifndef __IR_NEC_H__
#define __IR_NEC_H__


//【用户必填项:USER_H、USER_L、Check_EN、CPU_Fosc、IR】
#defineUSER_H   0x80         //用户码高8位
#defineUSER_L   0x7F         //用户码低8位
#defineCheck_EN   0                    //是否要校验16位用户码:不校验填0,校验则填1       
#defineCPU_Fosc   12000000L    //输入主频,自适应解码(单位:Hz,范围:6MHz ~ 40MHz)
#defineCA_S       8                    //长按时间设置,单位:108mS(即 108mS整数倍,10倍以上为宜)

sbit IR = P3^6;                  //红外线接口(任意引脚)

#defineStep       400          //红外采样步长:400us
#defineTH_H      ((65536-Step*(CPU_Fosc/300)/40000)/256)//定时器高8位基准赋值
#defineTH_L      ((65536-Step*(CPU_Fosc/300)/40000)%256)//定时器低8位基准赋值

uint8   IR_BT;   //解码效果返回:0无效,1有效,2短按,3长按
uint8   NEC;    //解码存放:16位用户码、操作码正反码
uint8   cntCA;   //长按计数
uint16cntStep;   //步数计
bit   IRa,IRb;   //接收脚电位状态保存
bit   IRsync;    //同步标志
uint8   BitN;      //位码装载数


/*┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
函数:红外线解码初始化
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈*/
void IR_Init()
{
   TMOD &= 0xF0;           //清定时器0
   TMOD |= 0x01;           //定时器0:16位定时器
   TL0 = TH_L;           //每步时间
   TH0 = TH_H;
   ET0 = 1;
   EA= 1;
   TR0 = 1;
}

/*┈┈┈┈┈┈┈┈┈┈ 基准 ┈┈┈┈┈┈┈┈┈┈┈*/
#define    Boot_Limit    ((9000+4500 +1000)/Step)   //引导码周期上限   
#define    Boot_Lower    ((9000+4500 -1000)/Step)   //引导码周期下限   
#define    Bit1_Limit    ((2250 +800)/Step)         //“1”周期上限
#define    Bit0_Limit    ((1125 +400)/Step)         //“0”周期上限
/*┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
函数:红外线NEC周期采样解码法(定时中断,下降沿查询周期时间)
全局变量:IR_BT = 0无效
                  1有效,待继续判断长、短按(如不需要判断长、短按,则直接使用)
                  2短按
                  3长按
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈*/
void IR_NEC()
{        
   TL0 = TH_L;   //重赋值
   TH0 = TH_H;       

   cntStep++;    //步数累加
   if(IR_BT==1)if(cntStep>300)IR_BT=2; //解码有效后,如果无长按,120ms(400us×300)后默认短按

   IRb = IRa;    //保存上次电位状态
   IRa = IR;   //保存当前电位状态
       
   if(IRb && !IRa)    //是否下降沿(上次高,当前低)
   {
      if(cntStep > Boot_Limit)   //超过同步时间?
      {       
          if(IR_BT==1)if(++cntCA>CA_S)IR_BT=3; //解码有效后,继续按住遥控>CA_S即长按
          IRsync=0;                            //同步位清0
      }
      else if(cntStep > Boot_Lower){ IRsync=1; BitN=32; }   //同步位置1,装载位码数32                          
      else if(IRsync)          //如果已同步
      {
         if(cntStep > Bit1_Limit)IRsync=0;                   
         else
         {       
            NEC >>= 1;                               
            if(cntStep > Bit0_Limit)NEC |= 0x80;    //“0”与“1”
            if(--BitN == 0)                               
            {
               IRsync = 0;    //同步位清0

               #if (Check_EN == 1)                                       
               if((NEC==USER_H)&&(NEC==USER_L)&&(NEC==~NEC))    //校验16位用户码、操作码正反码
               {IR_BT=1; cntCA=0;}   //解码有效,接下来判断:短按?长按?
               #else
               if(NEC==~NEC){ IR_BT=1; cntCA=0; }//只校验操作码正反码
               #endif                                       
            }
            else if((BitN & 0x07)== 0)    //NEC每装满8位,移动保存一次(即 BitN%8 == 0)
            {   NEC=NEC; NEC=NEC; NEC=NEC;   }
         }
      }
      cntStep = 0;   //步数计清0
   }
}

//取消相关宏定义
#undef CPU_Fosc

#endif





主程序
#include "INC\STC89C52RC.H"
#include "INC\MY_SET.H"
#include "INC\IR_NEC.H"      //调用NEC解码头文件

sfr   SE   = 0x80;             //数码管段选 P0:0x80   P1:0x90
sbitWX1= P2^0;                 //数码管位显
sbitWX2= P2^1;
sbitWX3= P2^2;
sbitWX4= P2^3;
sbitWX5= P2^4;
sbitWX6= P2^5;
sbitWX7= P2^6;
sbitWX8= P2^7;

uint8c tab[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0X88,0X83,0XC6,0XA1,0X86,0X8E,0xFF};
uint8Xn,X1,X2,X3,X4,X5,X6;

void KZ0();      //短按处理
void KZ1();      //长按处理


/***************** 主函数 ********************/
void main(void)
{
   IR_Init();                           //红外线解码初始化
                                                  
   while(1)
   {
      //遥控检测
      if((IR_BT==2)||(IR_BT==3))                          
      {
         if(IR_BT==2)KZ0();      //短按处理                  
         else      KZ1();      //长按处理       
         IR_BT =0;               //清有效标志

         X1 = NEC/16;              //更新显示
         X2 = NEC%16;
         X3 = NEC/16;
         X4 = NEC%16;
         X5 = NEC/16;
         X6 = NEC%16;
      }
          
   }

}
/*┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
函数:遥控短按处理
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈*/
void KZ0()
{
   switch(NEC)             
   {
          case 0x12: P10 = !P10; break;
          case 0x05: break;
          case 0x1E: break;
          case 0x55: break;

          case 0x01: break;
          case 0x1B: break;
          case 0x03: break;
          case 0x6B: break;

          case 0x07: break;
          case 0x08: break;
          case 0x09: break;
          case 0x68: break;

          case 0x22: break;
          case 0xE6: break;
          case 0x33: break;
          case 0xE2: break;
          default:break;
   }
}
/*┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
函数:遥控长按处理
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈*/
void KZ1()
{
   switch(NEC)             
   {
          case 0x12: P14 = !P14; break;
          case 0x05: break;
          case 0x1E: break;
          case 0x55: break;

          case 0x01: break;
          case 0x1B: break;
          case 0x03: break;
          case 0x6B: break;

          case 0x07: break;
          case 0x08: break;
          case 0x09: break;
          case 0x68: break;

          case 0x22: break;
          case 0xE6: break;
          case 0x33: break;
          case 0xE2: break;
          default:break;
   }
}
/*********************数码管扫描*************************/
void XS(void)
{
if(++Xn > 7)Xn=0;
switch(Xn)             
{
            case 0: WX8=1;NOP;           //屏蔽上个位显
                  SE=tab;           //送段码
                  WX1=0;           //开位显
                  break;
            case 1: WX1=1; NOP; SE=tab; WX2=0; break;
            case 2: WX2=1; NOP; SE=tab; WX3=0; break;       
       case 3: WX3=1; NOP; SE=tab; WX4=0; break;
       case 4: WX4=1; NOP; SE=tab; WX5=0; break;
       case 5: WX5=1; NOP; SE=tab; WX6=0; break;
       case 6: WX6=1; NOP; SE=tab; WX7=0; break;
       case 7: WX7=1; NOP; SE=tab; WX8=0; break;               
       default:break;                      
}
}

/********************** 定时器0中断函数************************/
void time0(void) interrupt 1
{
   IR_NEC();
   XS();        
}



接收源程序+仿真
点击此处下载 ourdev_689713KCBR6N.rar(文件大小:111K) (原文件名:NEC(任意接收引脚,支持长短按).rar)



遥控器源程序
点击此处下载 ourdev_689745J4Z85P.rar(文件大小:94K) (原文件名:遥控器源程序.rar)

/***************************************************************
    作品:红外线遥控发射(NEC编码)
单片机:STC89C52RC
    晶振:12M
***************************************************************/
//
// 发射引脚(接PNP三极管b极)
// PNP三极管e极接2Ω电阻,c极接红外发射管
   
#include <REG51.h>
#include "INC\MY_SET.h"
#include "INC\LCD1602_6IO.h"

sbitIR= P3^6;                  //发射引脚(接PNP三极管基极)

#defineUSER_H   P2            //用户码高8位
#defineUSER_L   P0            //用户码低8位
uint8c tab = {                  //操作码
0x12,0x05,0x1e,0x55,
0x01,0x1b,0x03,0x6b,
0x07,0x08,0x09,0x68,
0x22,0xE6,0x33,0xe2};

#define m9    (65536-9000)           //9mS
#define m4_5(65536-4500)           //4.5mS
#define m1_6(65536-1650)           //1.65mS
#define m_56(65536-560)           //0.56mS
#define m40   (65536-40000)       //40mS
#define m56   (65536-56000)       //56mS
#define m2_25 (65536-2250)      //2.25mS

voidSanZhuan();
uint8 KEY(void);
voidZZ(uint8 x);                  //NEC编码发送程序
voidZ0(uint8 temp);                  //单帧(8位数据)发送程序
voidTT0(bit BT,uint16 x);          //38KHz载波发射 + 延时程序



/*┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
函数:主程序
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈*/
void main(void)
{
TMOD = 0x01;         //T0 16位工作方式
IR=1;                  //发射端口常态为高电平
L1602_Init();
L1602_clr();
L1602_xy(0,0);
L1602_ZIFUC("UserCode :0x");
L1602_xy(0,1);
L1602_ZIFUC("Opcode   :0x");

while(1)
{          
   L1602_xy(12,0);
   L1602_JZ(USER_H,16,1);
   L1602_JZ(USER_L,16,1);
   SanZhuan();
}
}
/*┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
函数:4×4矩阵键盘
                      【 线翻转法键值表 】                  
P1.0P1.1P1.2P1.3P1.4P1.5P1.6P1.7
│   │    │    │    │    │    │    │   
│   │    │    └──7e    be    de   ee      
│   │    └─────7d    bd    dd   ed      
│   └────────7b    bb    db   eb      
└─────────── 77    b7    d7   e7   
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈*/
uint8 KEY(void)
{
uint8 Key = 0;

P1 = 0xf0;         //键盘初始:行值=0,列值=1
NOP;               //缓冲,待IO端口电位稳定
Key = P1&0xf0;       //得到行标志

P1= 0x0f;          //翻转键盘接口输出
NOP;
Key |= (P1&0x0f);    //列标志 + 行标志

return Key;          //返回键值
}
/*┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
函数:散转程序
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈*/
void SanZhuan()
{
uint8 v;
v = KEY();                           //键盘检测
switch(v)
{
case 0x7e:ZZ(tab);break;               
case 0xbe:ZZ(tab);break;               
case 0xde:ZZ(tab);break;               
case 0xee:ZZ(tab);break;               
case 0x7d:ZZ(tab);break;               
case 0xbd:ZZ(tab);break;               
case 0xdd:ZZ(tab);break;               
case 0xed:ZZ(tab);break;
case 0x7b:ZZ(tab);break;               
case 0xbb:ZZ(tab);break;               
case 0xdb:ZZ(tab);break;               
case 0xeb:ZZ(tab);break;
case 0x77:ZZ(tab);break;               
case 0xb7:ZZ(tab);break;               
case 0xd7:ZZ(tab);break;               
case 0xe7:ZZ(tab);break;                               
default:break;
}
v=0;
}

/*┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
函数:NEC编码发送程序
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈*/
void ZZ(uint8 Value)
{
L1602_xy(12,1);
L1602_JZ(Value,16,1);    //更新显示

TT0(1,m9);             //高电平9mS
TT0(0,m4_5);             //低电平4.5mS

/*┈ 发送4帧数据┈*/
Z0(USER_H);             //用户码高8位
Z0(USER_L);             //用户码低8位
Z0(Value);             //操作码
Z0(~Value);             //操作码反码

/*┈┈ 结束码 ┈┈*/
TT0(1,m_56);
TT0(0,m40);

/*┈┈ 重复码 ┈┈*/
while(KEY() != 0xFF)
   {
      TT0(1,m9);
      TT0(0,m2_25);

      TT0(1,m_56);
      TT0(0,m40);
      TT0(0,m56);                                  
    }                   
}

/*┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
函数:单帧(8位数据)发送程序
入口:temp
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈*/
void Z0(uint8 temp)
{
uint8 v;
for(v=0;v<8;v++)
{   
      TT0(1,m_56);                  //高电平0.65mS         
      if(temp&0x01) TT0(0,m1_6);//发送最低位
      else          TT0(0,m_56);   
      temp >>= 1;               //右移一位
}   
}

/*┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
函数:38KHz载波发射 + 延时程序
入口:(是否发射载波,延时约 x (uS))
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈*/
void TT0(bit BT,uint16 x)
{
TH0 = x>>8;                //输入定时值
TL0 = x;
TF0=0;                        //溢出标志位清0
TR0=1;                        //启动定时器0
if(BT == 0) while(!TF0);        //BT=0时,不发射38KHz载波只延时;
else while(1)                //BT=1时,发射38KHz脉冲+延时;38KHz载波(低电平)占空比5:26
       {
              IR = 0;
              if(TF0)break;if(TF0)break;
              IR = 1;
              if(TF0)break;if(TF0)break;
                 if(TF0)break;if(TF0)break;
                 if(TF0)break;if(TF0)break;
                 if(TF0)break;if(TF0)break;
                 if(TF0)break;if(TF0)break;
       }
TR0=0;          //关闭定时器0
IR =1;          //载波停止后,发射端口常态为高
}

jlhgold 发表于 2011-10-28 21:25:03

mark 有空试试

zuu0 发表于 2011-10-28 22:53:25

发送部分代码看一下呀,只有解码

Bicycle 发表于 2011-10-28 23:14:26

很不错

linquan315 发表于 2011-10-29 00:40:37

不错顶一下!

daicp 发表于 2011-10-29 00:44:40

多任务的思想,不错

tonyone 发表于 2011-10-29 15:18:06

不错,mark一下

afei8856 发表于 2011-11-2 16:32:05

mark

boy364100 发表于 2011-11-2 16:48:32

mark!!!

chenfzg 发表于 2011-11-25 21:53:08

谢谢!

yyt1372 发表于 2011-12-9 11:34:43

不错

wenfaxiang 发表于 2011-12-9 23:15:07

好啊

tmaa3 发表于 2011-12-12 21:14:59

还可以

pangjineng 发表于 2011-12-20 10:45:13

实物怎么运行不了啊,1602无显示。

BXAK 发表于 2011-12-20 12:00:07

回复【13楼】pangjineng
-----------------------------------------------------------------------

1602的程序只是临时加进去,没经过实物验证

lovelywwei 发表于 2011-12-21 21:45:20

mark.有空再玩。学无止境啊

daniel68 发表于 2012-1-8 11:33:43

THX

hzy3774 发表于 2012-1-13 12:06:12

不错,谢谢分享

tzsteel 发表于 2012-2-2 19:04:22

回复【楼主位】BXAK
-----------------------------------------------------------------------

试了一下发射的,蛮不错的,测了发射的频率为38.6473K(低电平4.9375uS,脉宽25.875uS),请问一下要微调发射的频率,软件要咋改啊?谢谢

BXAK 发表于 2012-2-2 19:32:59

回复【18楼】tzsteel
-----------------------------------------------------------------------
else while(1) //BT=1时,发射38KHz脉冲+延时;38KHz载波(低电平)占空比约5:26
{
          IR = 0;
          if(TF0)break;if(TF0)break;
          IR = 1;
          if(TF0)break;if(TF0)break;
          if(TF0)break;if(TF0)break;
          if(TF0)break;if(TF0)break;
          if(TF0)break;if(TF0)break;
          if(TF0)break;if(TF0)break;
}

修改大括号里面的就可以,
频率有些高,降低添加适量的空操作“_nop_();”(注:不要放在大括号两侧)
http://cache.amobbs.com/bbs_upload782111/files_51/ourdev_715650M2F4ZX.jpg
(原文件名:TP0.jpg)

比如改成:

else while(1) //BT=1时,发射38KHz脉冲+延时;38KHz载波(低电平)占空比约5:26
{
          IR = 0;
          if(TF0)break;if(TF0)break;
          _nop_();
          IR = 1;
          if(TF0)break;if(TF0)break;
          if(TF0)break;if(TF0)break;
          if(TF0)break;if(TF0)break;
          if(TF0)break;if(TF0)break;
          if(TF0)break;if(TF0)break;
}

cxm007 发表于 2012-2-2 22:43:29

新人,表示有压力!

williamrain 发表于 2012-2-3 00:12:57

mark

tzsteel 发表于 2012-2-3 13:58:57

回复【19楼】BXAK
-----------------------------------------------------------------------

谢谢楼主解答,试了一下加NOP;是可以减少频率,加if(TF0)break;也是一样的,但加或减这些语句低电平4.9375uS是一直不变的,变的只是后面高电平的时间,请问一下要增加低电平的时间要咋改软件?谢谢

zhuchina 发表于 2012-2-3 15:13:27

红外遥控 MARK

BXAK 发表于 2012-2-3 19:15:56

回复【22楼】tzsteel
-----------------------------------------------------------------------

调整低/高时间比例,比如

else while(1) //BT=1时,发射38KHz脉冲+延时;38KHz载波(低电平)占空比约5:26
{
          IR = 0;
          if(TF0)break;if(TF0)break;
          if(TF0)break;if(TF0)break;
          _nop_();
          IR = 1;   
          if(TF0)break;if(TF0)break;
          if(TF0)break;if(TF0)break;
          if(TF0)break;if(TF0)break;
          if(TF0)break;if(TF0)break;
}

hero245 发表于 2012-2-3 19:24:34

mark谢谢

xslff 发表于 2012-2-3 23:23:59

好贴,好人,好思路!

babygs 发表于 2012-2-4 08:33:57

mark

to_xyr 发表于 2012-2-4 17:14:55

谢了,下载试试

tzsteel 发表于 2012-2-4 19:29:20

回复【24楼】BXAK
-----------------------------------------------------------------------

非常感谢

tzsteel 发表于 2012-2-4 19:30:13

回复【24楼】BXAK
-----------------------------------------------------------------------

非常感谢

fjh120 发表于 2012-2-4 20:04:11

多谢分享,先留名再看

sszzeettee 发表于 2012-2-4 21:21:05

mark

ljt80158015 发表于 2012-2-4 21:39:30

mark!

tzsteel 发表于 2012-2-5 20:43:28

回复【24楼】BXAK
-----------------------------------------------------------------------

请问一下楼主,else while(1) //BT=1时,发射38KHz脉冲+延时;38KHz载波(低电平)占空比约5:26   
{   
          IR = 0;   
          if(TF0)break;if(TF0)break;
          if(TF0)break;if(TF0)break;
          _nop_();
          IR = 1;   
          if(TF0)break;if(TF0)break;   
          if(TF0)break;if(TF0)break;   
          if(TF0)break;if(TF0)break;   
          if(TF0)break;if(TF0)break;   
}这段程序中,是怎么产生38KHz脉冲的?比如9mS时,定时器装的初值是65536-9000,计数满9000次TF0才为1,要产生5:26的38KHz载波不能理解,但用数字示波器看波型,9mS确实是由一串等分的38KHz载波组成.谢谢

BXAK 发表于 2012-2-5 22:42:39

回复【34楼】tzsteel
-----------------------------------------------------------------------

通过软件产生啊,你通过反汇编查看这段C的汇编代码看看,对照“51指令执行所用时钟表”就知道了

那9mS区间一直循环着while(1)
{                                 //12MHz时指令执行时间
          IR = 0;                   //1us
          if(TF0)break;             //2us
          if(TF0)break;   
          if(TF0)break;if(TF0)break;   
          IR = 1;   
          if(TF0)break;if(TF0)break;   
          if(TF0)break;if(TF0)break;   
          if(TF0)break;if(TF0)break;   
          if(TF0)break;if(TF0)break;   
}
上面一个循环正好26us(传统51@12MHz晶振,比如STC89系列),周期26us则频率38.461KHz

tzsteel 发表于 2012-2-6 20:45:56

回复【35楼】BXAK
-----------------------------------------------------------------------

谢谢,非常好的思路.

stely 发表于 2012-2-6 20:53:03

学习收藏,谢谢楼主

zyw19987 发表于 2012-2-6 21:22:57

红外解码多任务思路

egowang 发表于 2012-2-7 09:47:27

要学习

kk2008 发表于 2012-2-16 10:09:27

mark留用!

liangwb 发表于 2012-2-16 19:28:42

收藏,学习。

vp110 发表于 2012-2-16 23:35:19

想搞红外很久了,以前学习的时候也试过,但是没有成功。楼主程序写的很不错,虽然看不太懂,但是楼主的解释很详细。
先收藏了,等有时间了,好好研究。非常感谢楼主分享。


还有个问题想请教楼主

发射程序中  

else while(1) //BT=1时,发射38KHz脉冲+延时;38KHz载波(低电平)占空比5:26
      {
      IR = 0;
      if(TF0)break;if(TF0)break;
      IR = 1;
       if(TF0)break;if(TF0)break;
       if(TF0)break;if(TF0)break;
       if(TF0)break;if(TF0)break;
       if(TF0)break;if(TF0)break;
       if(TF0)break;if(TF0)break;
      }
  TR0=0;   //关

占空比非得5:26左右吗?
只要占空比小于1,能行吗?


这个软件实现载波实在是不懂。

BXAK 发表于 2012-2-17 00:31:29

回复【42楼】vp110
-----------------------------------------------------------------------

晕,没规定一定是5:26,低占空比是为了节省能量的同时,通过接小限流电阻(2.2Ω ~ 4.7Ω)获得高峰值电流,

比如:像电视遥控器之类一般都是(高电平)占空比约1:3,用NPN三极管驱动。

这也是为什么不用可编程时钟输出 或者 定时器中断 产生38K载波的原因之一,而普通的51没有PWM,想获得低占空比的38K载波只好用软件实现了。


你看看下面的信息,会有收获的:

二、红外发射(接收)距离与哪些因素有关?怎样提高红外发射管的发射距离?

      常用的红外发射管,它的外形与普通的发光二极管(LED)相似,发出人眼不可见的近红外线,约0.93μm 。工作电流小于20ma,为了使其在不同的电路中正常使用,回路中常串有限流电阻。 使用红外线控制时,其控制的距离与发射功率成正比。
      红外发射管工作于脉冲状态,因为脉动光(调制光)的有效传送距离与脉冲的峰值电流成正比,只需尽量提高峰值Ip,就能增加红外光的发射距离。
      提高Ip 的方法:尽量减小脉冲占空比,即压缩脉冲的宽度т,比如家电红外遥控中,红外发射管的工作脉冲占空比约为1/4~1/3;一些电气产品红外遥控器,其占空比是1/10。减小脉冲占空比可使红外发射管的发射距离增大。
       常见的红外发射管,其功率分为小功率(1mW~10mW)、中功率(20mW~50mW)和大功率(50mW~100mW以上)三大类。要使红外发光二极管产生调制光,只需在驱动管上加上一定频率的脉冲电压。
       用红外发光二极管发射红外线去控制受控装置时,受控装置中均有相应的红外接收元件,现在常用的是一体化的红外接收头,一体化的红外接收头相关材料看这里:图解红外遥控的发射和接收原理。
       红外线发射与接收的方式有两种,其一是直射式,其二是反射式。直射式指红外发射管和红外接收头相对安放在发射与受控物的两端,中间相距一定距离,比如常见的红外遥控;反射式是指红外发射管和红外接收头在同一平面上,红外发射管发出的红外光经过物体反射回来,被红外接收头接收。

三、红外发射、接收遥控距离,轻松达到10米以上:

(1)发射载波一定要接近接收头的中心频率(最关键);
(2)提高发射电流峰值Ip:使用三极管驱动发射管,占空比1:3(或者1:4……等等),小限流电阻(2欧姆):
    ①使用NPN型三极管驱动时,用“高电平”占空比(如果是没有推挽输出的51,应加3K左右的上拉电阻
);
    ②使用PNP型三极管驱动时,用“低电平”占空比(没有推挽输出的51推荐用PNP)。                              
(3)选用 脉冲型 红外接收头。

vp110 发表于 2012-2-17 18:06:44

基本上是了解了


谢谢楼主./emotion/em020.gif

wangyz1997 发表于 2012-2-23 00:04:20

感谢楼主的分享!我非常喜欢你的思路

在这里提一个问题:按说STC的1T单片机的定时器默认是12T分频的,所以这个程序应该可以直接移植到STC的1T单片机上,但我把程序移植到了STC11F04上,用红外一体头接受没有信号,吧红外LED换成普通LED,能看到闪烁,和正常遥控器遥控一体头的信号看不出什么区别,因为手头上没有示波器,所以无法抓红外的波形。

求解,楼主能不能放个STC1T的红外发射程序,就这个在网上还比较稀缺,谢谢!

21006091 发表于 2012-2-23 00:15:01

mark,留名

BXAK 发表于 2012-2-23 00:45:39

回复【45楼】wangyz1997
-----------------------------------------------------------------------

因为那个发射程序的38K载波使用软件指令生成,所以上面的发射程序不兼容1T系列,

STC 1T系列换成下面的( 另:如果不喜欢用“goto”,就用“if(TF0)break;” )

/*┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
函数:38KHz载波发射 + 延时
入口:(是否发射载波,约 X (uS))
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈*/
void TT0(bit BT,uint16 X)
{
   uint8 t;

   TH0 = ((uint8 *)&X);        //X高8位
   TL0 = ((uint8 *)&X);        //X低8位
   TF0=0;                                //清0
   TR0=1;                                //启动定时器0
                                                               
   if(BT)                                //38KHz载波,(低电平)占空比约 9:(9+28)【12M: 1t <=> 1/12(uS),38KHz周期26.316uS <=> 316t 】
   {                                               
          while(1)
          {
          IR_Out = 0;                                               // 4t
      for(t=9;t;t--)if(TF0)goto TN;    // 2t + (4t+4t)*x
      if(TF0)goto TN;                                       // 4t
                IR_Out = 1;                                               // 4t
      for(t=28;t;t--)if(TF0)goto TN;       // 2t + (4t+4t)*x
                if(TF0)goto TN;                                     // 4t
          }                                                                       
   }                                                                       
   else while(!TF0);

TN:TR0 =0;                                //关闭定时器0
   IR_Out =1;                          //38KHz载波停止后,发射端口常态=1
}

yifeng009 发表于 2012-2-23 15:10:26

mark   研读。。。

wangyz1997 发表于 2012-2-23 18:33:41

回复【47楼】BXAK
-----------------------------------------------------------------------

BXAK大侠,你太厉害了!让我这个新手佩服不已啊!

就是注释里的// 2t + (4t+4t)*x   是什么意思?软延时计算吗?求教!

BXAK 发表于 2012-2-23 20:32:07

回复【49楼】wangyz1997
-----------------------------------------------------------------------

业余爱好罢了,闲时就折腾折腾

是延时计算,
你可以用keil反汇编看代码 对照 指令执行时间表 计算,
也可以用keil断点调试直接查看指令执行时间(适合STC 12T系列),
也可以用TKStudio断点调试直接查看指令执行时间(适合STC 1T、12T系列)

STC 12T系列(传统51) STC 1T系列 指令执行时间查询:点击此处下载 ourdev_721603SOZVXC.pdf(文件大小:1.38M) (原文件名:STC 1T 12系列 指令执行时间查询.pdf)

wangyz1997 发表于 2012-2-23 21:14:04

谢谢!长见识了!

beck_ck 发表于 2012-2-24 03:04:52

回复【楼主位】BXAK
-----------------------------------------------------------------------

mark

jjj2012 发表于 2012-2-24 13:13:47

mark

chinasy519 发表于 2012-2-27 12:18:50

学习,谢谢分享。

chinasy519 发表于 2012-2-28 20:41:41

能遥控电视吗?遥控电视需要改哪里?谢谢。

BXAK 发表于 2012-2-28 21:33:29

回复【55楼】chinasy519
-----------------------------------------------------------------------

是NEC编码格式(9ms高 + 4.5ms低 + 16位用户码 + 8位操作码正/反码)的就可以,换上你遥控器的用户码、操作码

chinasy519 发表于 2012-2-28 22:18:41

回复【56楼】BXAK
回复【55楼】chinasy519
-----------------------------------------------------------------------
是nec编码格式(9ms高 + 4.5ms低 + 16位用户码 + 8位操作码正/反码)的就可以,换上你遥控器的用户码、操作码
-----------------------------------------------------------------------

谢谢!遥控电视实验成功。

b60885262 发表于 2012-2-29 01:09:30

强大,实物简单测试了一下,发射成功,1602有显示,用户码控制码可以控制电视。

还有个问题,就是如果我用STC11F104E   1T单片机,没有P0 P2口,如何设置用户码?
比如用户码固定为 0xcced ,如何改呢?

BXAK 发表于 2012-2-29 01:39:53

回复【58楼】b60885262
-----------------------------------------------------------------------



#defineUSER_H   P2            //用户码高8位
#defineUSER_L   P0            //用户码低8位

改成

#defineUSER_H   0xcc            //用户码高8位
#defineUSER_L   0xed            //用户码低8位

实物直接去掉1602显示得了(把"INC\LCD1602_6IO.h"去掉,有关于1602的函数也去掉),
加入掉电模式,按键中断唤醒,一副电池用一年都可以,再加个指示灯,

http://cache.amobbs.com/bbs_upload782111/files_45/ourdev_672303W3V3QD.jpg
(原文件名:20110819099.jpg)

b60885262 发表于 2012-2-29 01:50:13

回复【59楼】BXAK
-----------------------------------------------------------------------

谢谢指导,明天我试试!
楼主注意休息啊,呵呵!

b60885262 发表于 2012-2-29 01:59:57

回复【59楼】BXAK
-----------------------------------------------------------------------

刚看到你的图片,能不能把你的实物程序发给我 60885262@163.com

那么多按键,估计不好拿吧?我打算只用5个按键就可以了。常用的换台加减,音量加减,和静音键就好了。这样体积可以更小。

xue_pic 发表于 2012-2-29 11:14:07

好思路学习一下。

lisen090 发表于 2012-3-28 15:18:42

顶一个!!

Forever 发表于 2012-3-28 15:50:37

mark一下。

hanzhicheng 发表于 2012-3-30 22:32:59

过了一遍 感觉很好,下次仔细研究下 谢谢

IamPolaris 发表于 2012-3-30 23:39:00

这么多人都说好!那好吧,我也马克吧。

BXAK 发表于 2012-3-31 12:15:18

以前写的有缺陷啊,
【1】
像:
uint16cntStep;   //步数计
uint16 即 unsigned int ,其实用 uint8 (即 unsigned char)就够了,这样省了些运算提高了速度

【2】
像:
{
    TL0 = TH_L;   //重赋值
    TH0 = TH_H;      

   cntStep++;    //步数累加
    if(IR_BT==1)if(cntStep>300)IR_BT=2; //解码有效后,如果无长按,120ms(400us×300)后默认短按

   IRb = IRa;    //保存上次电位状态
    IRa = IR;   //保存当前电位状态
……

接收端的电平查询应该放在前面合理些:
{
    TL0 = TH_L;   //重赋值
    TH0 = TH_H;

   IRb = IRa;    //保存上次电位状态
    IRa = IR;   //保存当前电位状态      

   cntStep++;    //步数累加
    if(IR_BT==1)if(cntStep>300)IR_BT=2; //解码有效后,如果无长按,120ms(400us×300)后默认短按
……



ok168 发表于 2012-4-7 09:23:02

楼主给个联系方式

xyz2008 发表于 2012-4-7 10:30:53

{:biggrin:}{:biggrin:}

wgm_123 发表于 2012-4-7 11:40:35

谢谢哦。是个好贴,学习了

longyin108 发表于 2012-4-10 22:29:07

{:smile:}强悍!

hpdell 发表于 2012-4-11 11:32:49

强悍啦,不错,顶顶

hpdell 发表于 2012-4-11 12:38:20

请教,为何要这个样
#defineTH_H      ((65536-Step*(CPU_Fosc/300)/40000)/256)//定时器高8位基准赋值
#defineTH_L      ((65536-Step*(CPU_Fosc/300)/40000)%256)//定时器低8位基准赋值
进行/300 再/40000??????????????

小黑鱼1148 发表于 2012-4-11 12:50:07

有空试试!!!

电子蔡鸟 发表于 2012-4-11 13:19:30

值得学习啊。。。

zhenglu891028 发表于 2012-4-11 13:33:47

这么好的资料,对新手来说太好了。谢谢!

BXAK 发表于 2012-4-11 15:09:13

本帖最后由 BXAK 于 2012-4-11 15:10 编辑

hpdell 发表于 2012-4-11 12:38 static/image/common/back.gif
请教,为何要这个样
#defineTH_H      ((65536-Step*(CPU_Fosc/300)/40000)/256)//定时器高8位基准赋值 ...

编译软件是Keil,其它的不知有没有这种情况

其实原是:Step*CPU_Fosc/120000000       ①
为什么用:Step*(CPU_Fosc/300)/40000   ②

unsigned long范围: 0 ~ 4,294,967,295

如果Step = 400,4294967295/400 = 10737418.2375,
当 CPU_Fosc < 10,737,418 时,用①没问题,因为 Step*CPU_Fosc < 4,294,967,295;
当 CPU_Fosc > 10,737,418 时,用①就会有溢出问题,因为 Step*CPU_Fosc > 4,294,967,295;

②缩小300倍后计算, Step*(CPU_Fosc/300) / (120000000/300) →Step*(CPU_Fosc/300)/40000
   保证CPU_Fosc < 40,000,000 范围内,Step*(CPU_Fosc/300)< 4,294,967,295,这样就不会有溢出问题

不过用 Step*(CPU_Fosc/100)/120000 应该够了,具体的,你可以编译后反汇编看看代码就知道其中的差异了

784956936 发表于 2012-4-11 15:55:13

好人!赞!

hpdell 发表于 2012-4-11 16:40:50

本帖最后由 hpdell 于 2012-4-11 16:45 编辑

BXAK 发表于 2012-4-11 15:09 static/image/common/back.gif
编译软件是Keil,其它的不知有没有这种情况

其实原是:Step*CPU_Fosc/120000000       ①


谢谢你的回复,现在有些明白了。兄台考虑的很全面啦。

dongwei123 发表于 2012-4-11 16:49:51

学习了

starli 发表于 2012-4-11 16:58:39

楼主的态度很值得学习.

软件也很棒.

zhenglu891028 发表于 2012-4-11 19:37:28

学习收藏,谢谢楼主

hpdell 发表于 2012-4-12 22:22:31

效果不错哦

我不乐意啊 发表于 2012-4-12 23:09:10

不错啊!有空试一下

xyr 发表于 2012-5-4 18:25:17

你好:BXAK

         红外接收程序在51单片机试过效果很好,可是我把这思路移植到AVR上怎么就不行了,帮忙参考。

#include<avr\io.h>
#include<avr\interrupt.h>
#include<util\delay.h>




/*==================================
         遥控代码处理
==================================*/
unsigned char i,TEST_flag;
unsigned int TEST=0;


#defineUSER_H   0x02             //用户码高8位
#defineUSER_L   0xfd             //用户码低8位
#defineCheck_EN   0             //是否要校验16位用户码:不校验填0,校验则填1   
#defineCA_S       8                //长按时间设置,单位:108mS(即 108mS整数倍,10倍以上为宜)
#defineIR      (PINB&_BV(PINB7))                  //PB4//红外线接口(任意引脚)
#defineStep      400         //红外采样步长:400us


//#defineTH_H       (65536-(8000000*0.0004)/256)//定时器高8位基准赋值
//#defineTH_L       (65536-(8000000*0.0004)%256)//定时器低8位基准赋值

unsigned char   IR_BT;               //解码效果返回:0无效,1有效,2短按,3长按
unsigned char   NEC;            //解码存放:16位用户码、操作码正反码
unsigned char   cntCA;               //长按计数
unsigned int    cntStep;             //步数计      
unsigned charIRa , IRb;   //电位状态保存

unsigned charIRsync   ;   //同步标志

unsigned char   BitN;                //位码装载数
/*┈┈┈┈┈┈┈┈┈┈ 基准 ┈┈┈┈┈┈┈┈┈┈┈*/
#define    Boot_Limit       (((9000+4500) +2000)/Step)    //引导码周期上限   
#define    Boot_Lower       (((9000+4500) -2000)/Step)    //引导码周期下限   
#define    Bit1_Limit       ((2250 +800)/Step)            //“1”周期上限800
#define    Bit0_Limit       ((1125 +400)/Step)            //“0”周期上限400
/*┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
函数:红外线NEC周期采样解码法(定时中断,下降沿查询周期时间)
全局变量:IR_BT = 0无效
                  1有效,待继续判断长、短按(如不需要判断长、短按,则直接使用)
                  2短按
                  3长按
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈*/
/*┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
函数:遥控短按处理
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈*/
void KZ0(void)
{
    switch(NEC)
    {
    case 0x0D:      //音量加
      break;
    case 0x0E:      //音量减
      break;
    case 0x0A:      //上曲
      break;
    case 0x15:         //下曲
      break;
    case 0x0C:    PORTC=0XFF;      //播放
      break;
    case 0x66:         //单曲
      break;
    case 0x12:         //循环
      break;
    case 0x09:         //电源
      break;
    default:
      break;
    }
}
/*┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
函数:遥控长按处理
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈*/
void KZ1(void)
{
    switch(NEC)
    {
    case 0x0D:      //音量加
      break;
    case 0x0E:      //音量减
      break;
    case 0x0A:      //上曲
      break;
    case 0x15:         //下曲
      break;
    case 0x0C:   PORTC=0XFF;    //播放
      break;
    case 0x66:         //单曲
      break;
    case 0x12:         //循环
      break;
    case 0x09:         //电源
      break;
    default:
      break;
    }
}
/*=================================
      红外遥控译码
=================================*/
void IR_NEC(void)
{
    cntStep++;         //步数采样
    if(IR_BT==1)if(cntStep>300)
      {
            IR_BT=2;    //解码有效后,如果无长按,120ms(400us×300)后默认短按
      }
    IRb = IRa;         //上次电位状态
    IRa = IR;          //当前电位状态
    if((IRb==1) &&(IRa==0))                   //是否下降沿(上次高,当前低)
    {
       if(cntStep > Boot_Limit)      //超过同步时间?38.75
      {
            if(IR_BT==1)if(++cntCA>CA_S)
                {
                  IR_BT=3;    //解码有效后,继续按住遥控>CA_S即长按
                }
            IRsync=0;               //同步位清0
      }
      else if(cntStep > Boot_Lower)//28.75
      {
            IRsync=1;    //同步位置1,装载位码数
            BitN=32;
      }
      else if(IRsync)                                    //如果已同步
      {
            if(cntStep >Bit1_Limit)//7.6
            {if(TEST_flag==0){TEST=2;TEST_flag=1;}
                IRsync=0;       
            }
            else
            {
                NEC >>= 1;
                if(cntStep >Bit0_Limit)//3.8
                {
                  NEC |= 0x80;    //“0”与“1”
                }
                if(--BitN == 0)
                {
                  IRsync = 0;                  //同步位清0
#if (Check_EN == 1)
                  if((NEC==USER_H)&&(NEC==USER_L)&&(NEC==~NEC)) //校验16位用户码、操作码正反码
                  {
                        IR_BT=1;    //解码有效,接下来判断:短按?长按?
                        cntCA=0;
                  }
#else
                  if(NEC==~NEC)
                  {
                        IR_BT=1;    //校验操作码正反码
                        cntCA=0;
                  }
#endif
                }
                else if((BitN & 0x07)== 0)       //NEC每装满8位,移动保存一次(即 BitN%8 == 0)
                {
                  NEC=NEC;
                  NEC=NEC;
                  NEC=NEC;
                               
                }
            }
      }
       
      cntStep = 0;   //步数计清0
    }
}
/*==================================
          主程序
===================================*/
int main()
{   
    DDRC=0XFF;
    cli();         //关总中断
    TCCR1B= 0x00;//停止定时器
        OSCCAL=0XBF;//内部时钟校准补尝
   TCNT1H =0XF3; //225;//初始值
   TCNT1L=0X80; //225;//初始值
       
    TIMSK1 |= 0x01;//中断允许
    TCCR1B= 0x01;//03启动定时器
    DDRB&=~_BV(DDB7);//PIN7输入
        PORTB&=~_BV(PORTB7);//PORTB7配置上拉电阻
        sei();   //开总中断
        for(;;)
        {
         
        if((IR_BT==2)||(IR_BT==3))
      {
            if(IR_BT==2)
            {
                KZ0();    //短按处理
            }
            else
            {
                KZ1();    //长按处理
            }
                       IR_BT=0;
      }

/***********************************         
               for(;TEST>0;)
                  {
                PORTC=0XFF;
            _delay_ms(1000);    调试用计算步
                       PORTC=0X00;
                   _delay_ms(1000);               
                   TEST--;
if(TEST==0){TEST_flag=0;}
                   }
   
        }
        return 0;

}
************************/
/*==================================
    定时中断
===================================*/
ISR(TIMER1_OVF_vect)
{
TCNT1H=0XF3;//定时400US
TCNT1L=0X80;
    IR_NEC();
        PORTC^=_BV(PORTC1);
}

BXAK 发表于 2012-5-4 20:35:47

本帖最后由 BXAK 于 2012-5-4 22:08 编辑

xyr 发表于 2012-5-4 18:25 static/image/common/back.gif
你好:BXAK

         红外接收程序在51单片机试过效果很好,可是我把这思路移植到AVR上怎么就不行了,帮 ...

才开始学AVR,暂时帮不了,
有条件的话,你看看AVR定时器是否每400us定时中断,是否正确捕捉接收脚的电平……

补充:
我觉得问题可能是
51有可位寻址区,所以用 位 来保存接收脚状态很方便,
bit IRa,IRb;
if((IRb==1) &&(IRa==0))下降沿判断没问题

但在AVR中用unsigned char IRa,IRb保存电位状态,
if((IRb==1) &&(IRa==0))应该不适合了

xyr 发表于 2012-5-5 09:08:28

用位域定义过位 IRb IRa if(IRb&&!IRa)不行,
在这测试(PORTC^=_BV(PORTC1);)频率为1.24K符正常的。

BXAK 发表于 2012-5-5 09:15:32

xyr 发表于 2012-5-5 09:08 static/image/common/back.gif
用位域定义过位 IRb IRa if(IRb&&!IRa)不行,
在这测试(PORTC^=_BV(PORTC1);)频率为1.24K符正常的。 ...

这样改应该可以了,试试:

if( (IRb!=0) && (IRa==0) )                   //是否下降沿(上次高,当前低)

dcqq88 发表于 2012-5-5 09:20:16

不错啊,这么强!

8795950F 发表于 2012-5-6 11:28:36

不错不错!!!!!!!正在学习中

2006lc 发表于 2012-5-6 14:50:05

谢谢分享

TIANKUANG52 发表于 2012-5-6 20:29:26

本帖最后由 TIANKUANG52 于 2012-5-6 20:34 编辑

其中cnt的时间是怎么算的,9ms的cnt是多久,怎么算的,X1到X4的值怎么算的
我不懂,教下,谢谢
void exint0() interrupt 0
{        
   uint cnt;
   uchar i;

   EX0 = 0;
   cnt = 0;       
       
   while(!IR) cnt++;                               //记录引导码时间
   if(cnt < 1000){EX0=1;return;}                   //9ms的计数值(12MHz:1000< cnt <1500)
          
   cnt = 0;                       
   while(IR) if(cnt++ > 400){EX0=1;return;}      //防卡死,超时保护(12MHz: > 300)   
   if(cnt < 200){EX0=1;return;}                  //(12MHz不分频: <260)          

   for(i=0; i<32; i++)                           //读取32位位码
       {
         cnt = 0;                                                                                                                                               
         while(!IR);                     
         while(IR) if(cnt++ > 200){EX0=1;return;}//超时保护(12MHz:>=200)
         N >>= 1;
         if(cnt>60) N |= 0x80;                //0和1的计数界线(12MHz:< 109)   
       }

       if(N == ~N && N == ~N)            //校验识别码,操作码       
           {
                   X1 = N/16;
                X2 = N%16;
                X3 = N/16;
                X4 = N%16;
           }

        EX0 = 1;               
}

xyr 发表于 2012-5-7 09:18:26

本帖最后由 xyr 于 2012-5-7 14:04 编辑

BXAK 发表于 2012-5-5 09:15 static/image/common/back.gif
这样改应该可以了,试试:

if( (IRb!=0) && (IRa==0) )                   //是否下降沿(上次高,当前 ...

不是软件的问题,我由原来AVR Studio 4改为ICC编译就好了,具体AVR Studio 4为什么不行我还未查清,

谢谢!

BXAK 发表于 2012-5-7 11:38:37

本帖最后由 BXAK 于 2012-5-7 21:31 编辑

TIANKUANG52 发表于 2012-5-6 20:29 static/image/common/back.gif
其中cnt的时间是怎么算的,9ms的cnt是多久,怎么算的,X1到X4的值怎么算的
我不懂,教下,谢谢
void exint0 ...

好像是以前写的,但此法解码区间占用掉CPU,所以实用性有限。
原版的已经没了,修改版倒的还在,也贴出来算了,至于9ms的cnt是多久,怎么算,这涉及汇编,如果你不懂汇编……

while(!IR)cnt++;//查询低电平时间 【1T系列指令周期】【12T系列指令周期】
C:0x000F    JB         IR(0xB0.2),C:0019         4 t               2T
C:0x0012    INC      R7                                  3 t               1T
C:0x0013    CJNE      R7,#0x00,C:0017             4 t               2T
C:0x0016    INC      R6                                  3 t               1T
C:0x0017    SJMP   C:000F                           3 t               2T

【STC 1T系列】
time*12t = 14t*cnt + 3t*(cnt/256)
time*12 = 14*cnt + 3*cnt/256 = (14 + 3/256)*cnt
cnt= (time*12)/(14 + 3/256)≈7708 (12MHz @ 9000us,time=9000)

【STC 12T系列】
time*1T = 7T*cnt + 1T*(cnt/256)
time = 7*cnt + cnt/256 = (7+ 1/256)*cnt
cnt= time/(7 + 1/256)≈1285 (12MHz @ 9000us,time=9000)

修改版的已经将计数值公式化,直接使用就行了
                                                            以下计算公式只适合STC 1T系列                                      以下计算公式适合STC 12T系列 以及 其它传统51指令速度的MCU/*┈┈┈┈┈┈ 红外线NEC解码函数 ┈┈┈┈┈┈┈
检测接收端高电平时,采用超时保护,防信号终断卡死。
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈*/
#definems10        8571   //ms10 = 10000*CPU工频/14000000       //ms10 = 10000*CPU工频/12000000/7
#definems8        6857   //ms8=8000*CPU工频/14000000       //ms8=8000*CPU工频/12000000/7
#definems5        3000   //ms5=5000*CPU工频/20000000       //ms5=5000*CPU工频/12000000/12
#definems4        2400   //ms4=4000*CPU工频/20000000       //ms4=4000*CPU工频/12000000/12
#definems2        1200   //ms2=2000*CPU工频/20000000       //ms2=2000*CPU工频/12000000/12
#definems1        600    //ms1=1000*CPU工频/20000000       //ms1=1000*CPU工频/12000000/12
                        //以上计算公式只适合STC 1T系列       //以上计算公式适合STC 12T系列 以及 其它传统51指令速度的MCU
void IR_NEC(void)
{
   uintcnt=0;
   uchar i;                           

   while(!IR)cnt++;                                 
   if((cnt < ms8)||(cnt > ms10))return; //<8ms || >10ms,则返回

   cnt = 0;                       
   while(IR)if(++cnt > ms5)return;      //超时保护,>5ms,则返回
   if(cnt < ms4)return;               //<4ms,则返回

   for(i=32;i;)                         //读取32编码
   {
      cnt = 0;                                                                                                                                               
      while(!IR);                     
      while(IR)if(++cnt > ms2)return;   //超时保护,>2ms,则返回
      NEC >>= 1;
      if(cnt > ms1)NEC |= 0x80;      //1ms,“0”与“1”
          
          if((--i)&&(i%8==0)){ NEC=NEC; NEC=NEC; NEC=NEC; }
   }

   if((NEC==YHM_H)&&(NEC==YHM_L)&&(NEC==~NEC))//校验16位用户码,操作码正反码       
   {IR_BT = 1; }                                                //解码有效标志
}

xiaoyigechaos 发表于 2012-5-7 15:11:27

好东西啊这下省事了

TIANKUANG52 发表于 2012-5-7 20:46:48

本帖最后由 TIANKUANG52 于 2012-5-7 21:47 编辑

BXAK 发表于 2012-5-7 11:38 static/image/common/back.gif
好像是以前写的,但此法解码区间占用掉CPU,所以实用性有限。
原版的已经没了,修改版倒的还在,也贴出来 ...

我用的是stc89c52的单片机,能不能麻烦你帮我算下cnt的时间,晶振是12m的,不是一个指令是1us的吗?
那个9ms的cnt和4ms等一些时间的cnt怎么算,汇编我懂一些,谢了

我用的是stc89c52的用的是下面的公式吗?我上面的9ms的cnt就是1285左右也就是在1000到1500之间
那 if(cnt < 200){EX0=1;return;}                  //(12MHz不分频: <260) 是4.5ms的判断吗?
按公式4ms的cnt不是571左右吗怎么小于200是什么意思
【STC 12T系列】
time*1T = 7T*cnt + 1T*(cnt/256)
time = 7*cnt + cnt/256 = (7+ 1/256)*cnt
cnt= time/(7 + 1/256)≈1285 (12MHz @ 9000us,time=9000)

BXAK 发表于 2012-5-7 21:26:14

TIANKUANG52 发表于 2012-5-7 20:46 static/image/common/back.gif
我用的是stc89c52的单片机,能不能麻烦你帮我算下cnt的时间,晶振是12m的,不是一个指令是1us的吗?
那个 ...

晕,回复够详细,看的确不认真,
不是已经列成公式帖出来了,直接套用公式计算就可以了

看看上面给你的回复:
……
//以上计算公式只适合STC 1T系列         //以上计算公式适合STC 12T系列 以及 其它传统51指令速度的MCU
……

TIANKUANG52 发表于 2012-5-8 14:12:12

BXAK 发表于 2012-5-7 21:26 static/image/common/back.gif
晕,回复够详细,看的确不认真,
不是已经列成公式帖出来了,直接套用公式计算就可以了



我用的是stc89c52的用的是下面的公式。我上面的9ms的cnt就是1285左右也就是在1000到1500之间
那 if(cnt < 200){EX0=1;return;}                  //(12MHz不分频: <260) 是4.5ms的判断吗?
按公式4ms的cnt不是571左右吗怎么小于200是什么意思
while(IR) if(cnt++ > 400){EX0=1;return;}      //防卡死,超时保护(12MHz: > 300)   
这是怎么防卡死的
【STC 12T系列】
time*1T = 7T*cnt + 1T*(cnt/256)
time = 7*cnt + cnt/256 = (7+ 1/256)*cnt
cnt= time/(7 + 1/256)≈1285 (12MHz @ 9000us,time=9000)
谢了

BXAK 发表于 2012-5-8 16:20:21

本帖最后由 BXAK 于 2012-5-8 16:25 编辑

TIANKUANG52 发表于 2012-5-8 14:12 static/image/common/back.gif
我用的是stc89c52的用的是下面的公式。我上面的9ms的cnt就是1285左右也就是在1000到1500之间
那 if(cnt < ...

原版是 while(IR) if(cnt++ > ……
修改版是 while(IR)if(++cnt > ……
“cnt++” “++cnt ”指令执行时间的计算方式是不同的

用修改版的吧,修改版的都有公式,
原版的没了,也懒得重新反汇编计算

另:
也不一定要反汇编计算那么麻烦,
你可以先不加限制,按一下遥控将每个时间段cnt计数值保存下来后发送串口显示,就知道当前晶振下9ms、4ms……各时间段的cnt计数值
页: [1] 2 3
查看完整版本: 发一个红外线解码程序+仿真文件(可定义任意I/O作接收脚,支持长/短按,自适应主频6MHz~40