最近用到了一种常见的低成本红外遥控器:

  

  这种遥控器的编码方式为NEC,它的特征如下:

  1、8 位地址和 8 位指令长度;

  2、地址和命令 2 次传输(确保可靠性)

  3、PWM 脉冲位置调制,以发射红外载波的占空比代表“0”和“1”;

  4、载波频率为 38Khz;

  5、位时间为 1.125ms 或 2.25ms;

  NEC 码的位定义:一个脉冲对应 560us 的连续载波,一个逻辑 1 传输需要 2.25ms(560us 脉冲+1680us 低电平),一个逻辑 0 的传输需要 1.125ms(560us 脉冲+560us 低电平)。而遥控 接收头在收到脉冲的时候为低电平,在没有脉冲的时候为高电平,这样,我们在接收头端收到 的信号为:逻辑 1 应该是 560us 低+1680us 高,逻辑 0 应该是 560us 低+560us 高。

  NEC 遥控指令的数据格式为:同步码头、地址码、地址反码、控制码、控制反码。同步码 由一个 9ms 的低电平和一个 4.5ms 的高电平组成,地址码、地址反码、控制码、控制反码均是8 位数据格式。按照低位在前,高位在后的顺序发送。采用反码是为了增加传输的可靠性(可 用于校验)。

  一个实际的通信例子如下:

  

  根据通信协议,逻辑0和逻辑1的高电平时间时不同的,由此我们想到用单片机定时器的输入捕获功能来进行解码。思路如下:初始化定时器后配置为上升沿捕获,发送捕获中断后记录捕获值,再捕获下降沿,下降沿捕获到的值与上升沿的值之差即为高电平时间,根据高电平时间得到是逻辑0、逻辑1还是连续发送。

  基于MSP432P401单片机的红外解码程序如下:  

//红外遥控器
#define IR_RXD GPIO_PORT_P5, GPIO_PIN7
#define IR_Romate GPIO_PORT_P4, GPIO_PIN4 
#define REMOTE_ID 0
uint8_t IRcount=0;
uint16_t IRCapValue1=0;
uint16_t IRCapValue2=0;
uint16_t IRCapValue=0;

void Romate_Init(void)
{
  /* 定时器配置参数*/
  Timer_A_ContinuousModeConfig continuousModeConfig =
  {
    TIMER_A_CLOCKSOURCE_SMCLK,           // SMCLK Clock Source
    TIMER_A_CLOCKSOURCE_DIVIDER_6,       // SMCLK/6 = 1MHz
    TIMER_A_TAIE_INTERRUPT_ENABLE,       // Enable Timer ISR
    TIMER_A_SKIP_CLEAR                   // Skup Clear Counter
  };

  /* Timer_A 捕获模式参数配置*/
  Timer_A_CaptureModeConfig captureModeConfig1 =
  {
    TIMER_A_CAPTURECOMPARE_REGISTER_2,        // CC Register 2
    TIMER_A_CAPTUREMODE_RISING_AND_FALLING_EDGE,//上升沿和下降沿
    TIMER_A_CAPTURE_INPUTSELECT_CCIxA,        // CCIxB Input Select
    TIMER_A_CAPTURE_SYNCHRONOUS,              // Synchronized Capture
    TIMER_A_CAPTURECOMPARE_INTERRUPT_ENABLE,  // Enable interrupt
    TIMER_A_OUTPUTMODE_OUTBITVALUE            // Output bit value
  };
  GPIO_setAsPeripheralModuleFunctionInputPin(IR_RXD,GPIO_PRIMARY_MODULE_FUNCTION);
  GPIO_setAsOutputPin(IR_Romate);
  GPIO_setOutputLowOnPin(IR_Romate);
  
  Timer_A_initCapture(TIMER_A2_BASE, &captureModeConfig1);
  Timer_A_configureContinuousMode(TIMER_A2_BASE, &continuousModeConfig);
  Interrupt_enableInterrupt(INT_TA2_N);
  Timer_A_startCounter(TIMER_A2_BASE, TIMER_A_CONTINUOUS_MODE);
}
//遥控器接收状态
//[7]:收到了引导码标志
//[6]:得到了一个按键的所有信息
//[5]:保留    
//[4]:0表示捕获上升沿,1表示捕获下降沿                                   
//[3:0]:溢出计时器
uint8_t Romsta;
uint32_t RmtRec=0;    //红外接收到的数据                   
uint8_t  RmtCnt=0;    //按键按下的次数
void TA2_N_IRQHandler(void)
{
  /*溢出中断*/
  if(Timer_A_getEnabledInterruptStatus(TIMER_A2_BASE))
  {
    Timer_A_clearInterruptFlag(TIMER_A2_BASE);
    if(Romsta&0x80)//上次有数据被接收到了
    {
      IRcount++;
      Romsta&=~0X10;//取消上升沿已经被捕获标记
      if((Romsta&0X0F)==0X00)
        Romsta|=1<<6;//标记已经完成一次按键的键值信息采集
      if((Romsta&0X0F)<14)
        Romsta++;
      else
      {
        Romsta&=~(1<<7);//清空引导标识
        Romsta&=0XF0;    //清空计数器    
      }                                    
    }
  }
  /*捕获中断*/
  if(Timer_A_getCaptureCompareEnabledInterruptStatus(TIMER_A2_BASE,TIMER_A_CAPTURECOMPARE_REGISTER_2))
  {
    Timer_A_clearCaptureCompareInterrupt(TIMER_A2_BASE,TIMER_A_CAPTURECOMPARE_REGISTER_2);
    /*捕获到下降沿*/
    if(Timer_A_getSynchronizedCaptureCompareInput(TIMER_A2_BASE,
       TIMER_A_CAPTURECOMPARE_REGISTER_2,TIMER_A_READ_CAPTURE_COMPARE_INPUT)
       ==TIMER_A_CAPTURECOMPARE_INPUT_LOW)
    {
      IRCapValue2=Timer_A_getCaptureCompareCount(TIMER_A2_BASE,TIMER_A_CAPTURECOMPARE_REGISTER_2);
      IRCapValue=IRCapValue2-IRCapValue1+IRcount*65536;
      IRcount=0;
      /*接收到引导码*/
      if(Romsta&0x80)
      {
        if(IRCapValue>300&&IRCapValue<800)//560为标准值,560us
        {
          RmtRec<<=1;    //左移一位.
          RmtRec|=0;    //接收到0       
        }else if(IRCapValue>1400&&IRCapValue<1800)    //1680为标准值,1680us
        {
          RmtRec<<=1;    //左移一位.
          RmtRec|=1;    //接收到1
        }else if(IRCapValue>2200&&IRCapValue<2600)    //得到按键键值增加的信息 2500为标准值2.5ms
        {
          RmtCnt++;         //按键次数增加1次
          Romsta&=0XF0;    //清空计时器    
        }
      }
      /*接收引导码*/
      else if(IRCapValue>4200&&IRCapValue<4700)        //4500为标准值4.5ms
      {
        Romsta|=1<<7;    //标记成功接收到了引导码
        RmtCnt=0;        //清除按键次数计数器
        RmtRec=0;
      }    
      Romsta&=~(1<<4);
    }
    /*捕获到上升沿*/
    else
    {
      IRCapValue1=Timer_A_getCaptureCompareCount(TIMER_A2_BASE,TIMER_A_CAPTURECOMPARE_REGISTER_2);
      Romsta|=0x10;
      IRcount=0;
    }
  }
}
//扫描遥控器
uint8_t RomateScan(void)
{
  uint8_t sta=0;
  uint8_t t1,t2;
  if(Romsta&0x40)
  {
    t1=RmtRec>>24;//得到地址码
    t2=(RmtRec>>16)&0xff;//得到地址反码 
    if((t1==(uint8_t)~t2)&&t1==REMOTE_ID)//检验遥控识别码(ID)及地址 
    { 
      t1=RmtRec>>8;
      t2=RmtRec;     
      if(t1==(uint8_t)~t2) sta=t1;//键值正确     
    }   
    if((sta==0)||((Romsta&0X80)==0))//按键数据错误/遥控已经没有按下了
    {
      Romsta&=~(1<<6);//清除接收到有效按键标识
      RmtCnt=0;        //清除按键次数计数器
    }
  } 
  return sta;
}

  一个用这种遥控器遥控小车的例子:

  https://v.youku.com/v_show/id_XNDA4Nzk4OTEwNA==.html?spm=a2h3j.8428770.3416059.1

posted on 2019-03-06 20:21  花子山下  阅读(2279)  评论(0编辑  收藏  举报