NEC红外遥控协议理解与实现

来源:互联网 发布:js 计算时间间隔 毫秒 编辑:程序博客网 时间:2024/05/01 14:01

NEC红外遥控协议理解与实现

在上个链接里转载了NEC标准的远程控制协议,家电的遥控器基本遵守这个标准。

红外发射管有2个管脚,发送的是经过38KHz时钟调制过的信号。例如下图使用PWM产生一个等占空时钟信号用于调制。


接收管收下来的信号已经经过了解调,可以直接连接系统的外部中断脚。


 

下面通过逻辑分析仪来实际测量一下。

随便找了个红外遥控器,测量power键按下后的波形。首先是信号发送侧。


可以看到,0秒开始是一个按键动作,0.11秒后的那个波形是一个repeat,展开:


把波形重叠的部分展开,就可以看到这个38KHz的调制时钟


如果持续按下遥控器上的按键,那么就会发送连续的repeat信号,发送的间隔也基本满足协议上指出的110ms

下面是接收侧


可以看到信号被解调了,也就是说重叠的部分变成了低电平。

 

最后通过编写协议分析插件的方式,来描述如何通过程序来理解上面的波形。

我使用的逻辑分析软件是 Saleae Logic 1.1.15 编译环境是Microsoft Visual Studio 2008,编译时需要SaleaeAnalyzerSdk-1.1.14。完整的代码从这里下载。

 

分析的方法是测量两个信号下降沿之间的时间长度,这里先定义一些时间参数。

/* Timing define , unit : ms*/

#defineSTART_LOW_TIMING         9000

#defineSTART_HIGH_TIMING        4500

#defineREPEAT_HIGH_TIMING       2250

#defineLOGIC_ONE_TIMING         (562*3)

#defineLOGIC_ZERO_TIMING        (562*1)

#defineDATA_LOW_TIMING     (562*1)

从逻辑分析仪测量的结果,可以发现发射器给出的信号并不是非常的精确,所以我们需要定义误差范围。

/* Timing Margin , unit : ms*/

#definedelta                        20

在下面代码里,通过API函数AdvanceToNextEdge来获取下一个信号发生变化的采样点,如果对应的采样点是低电平,则表示下跳沿,这时和前一个下跳沿采样点的时间做差,按照采样频率换算成时间间隔。再根据上面定义的时间常量来判断这是一个START标记、REPEAT标记、逻辑1、逻辑0还是无效的信号。对于逻辑1和0的情况,需要通过移位来整理成32bits的有效数据,这里要特别注意协议里规定,先发送的是LSB,后发送的MSB。

voidIRNECAnalyzer::WorkerThread()

{

     U64per_sample = 0;

     U64cur_sample = 0;

     U64starting_sample = 0;

     U64differ = 0;

     char action = state_down;

     U8fail = 0;

     U64data = 0;

     U32code = 0;

     U8count = 0;

     U8data_f = 0;

    

     mResults.reset(new IRNECAnalyzerResults( this, mSettings.get() ) );

     SetAnalyzerResults(mResults.get() );

     mResults->AddChannelBubblesWillAppearOn(mSettings->mInputChannel );

     mSampleRateHz= GetSampleRate();

     mSerial= GetAnalyzerChannelData( mSettings->mInputChannel );

 

     if( mSerial->GetBitState() == BIT_LOW )

         mSerial->AdvanceToNextEdge();

 

     for(;;){

         mSerial->AdvanceToNextEdge();

         cur_sample= mSerial->GetSampleNumber();

//只处理时钟的下跳沿

         if(mSerial->GetBitState() == BIT_LOW){

              differ= (cur_sample - per_sample)*1000000/mSampleRateHz;

//判断是否是REPEAT信号           if(((START_LOW_TIMING+REPEAT_HIGH_TIMING-delta*6)<differ)&&((differ)<(START_LOW_TIMING+REPEAT_HIGH_TIMING+delta*6))){

                   action=state_repeat;

                   fail=0;

              }else //判断是否是START信号

if(((START_LOW_TIMING+START_HIGH_TIMING-delta*6)<differ)&&((differ)<(START_LOW_TIMING+START_HIGH_TIMING+delta*6))){

                  action=state_start;

                  fail=0;

             }else//判断是否是逻辑1if(((LOGIC_ONE_TIMING+DATA_LOW_TIMING-delta*2)<differ)&&((differ)<(LOGIC_ONE_TIMING+DATA_LOW_TIMING+delta*2))){

                   action=state_data;

                   data_f=1;

                   fail=0;

              }else  //判断是否是逻辑0if(((LOGIC_ZERO_TIMING+DATA_LOW_TIMING-delta*2)<differ)&&((differ)<(LOGIC_ZERO_TIMING+DATA_LOW_TIMING+delta*2))){

                   action=state_data;

                   data_f=0;

                   fail=0;

              }else{//否则为错误信号

                  fail=1;

             }

              if(fail==0){

                   switch (action){

                   case state_start:

                       code= 0;

                       count= 0;

                       starting_sample= cur_sample;

                       AddFrame(per_sample,cur_sample, 0, FStart);

                            break;

 

                   case state_data:

                       data_f= data_f << count;

                       code= code | data_f;

                       count++;

                       if(count == 8){

                            count= 0;        

                            AddFrame(starting_sample,cur_sample, code, FData);

                            code= 0;

                            starting_sample= cur_sample;

                       }

                       break;

 

                   case state_repeat:

                       AddFrame(per_sample,cur_sample, data, FRepeat);

                       break;

                      

                   default:

                       break;

                   }

              }

              per_sample= cur_sample;

         }

     }

}

加载上面的插件后,可以看到分析的结果


所以如果将这份代码放在板卡上运行,首先应该将接收器的信号接到处理器的外部中断管脚,然后注册一个下跳沿触发的快速中断。然后最通常的情况你需要再注册一个标准的输入设备,映射一下遥控器码字和按键事件的对应关系就可以了。


原创粉丝点击