ATmegaでのNEC IRプロトコルの実装

このプロトコルはすでに多くの場所で説明されています。 特定のマイクロコントローラーでの実装を詳細に表示および説明したいと思います。 写真のように、RGBリモコンから信号を受信する必要がありました。 彼のコマンドシステムは記事の最後にあります。



短期遠足



各NECプロト​​コルパケットは、9ミリ秒のパルスと4.5ミリ秒の一時停止という開始シーケンスで構成されています。 理論的な図面で過負荷にならないように、ロジックアナライザーからの実際のスクリーンショットを示します。







プロトコル自体は、ゼロとポーズ長のあるもののエンコードに基づいています。 各ビットの開始は、560μsの長さのパルスによって決定されます(同時に、このパルスは前のビットの終了を通知します)。 パルスに続く一時停止の長さは、ビットの論理値を決定します。 したがって、論理ゼロの合計の長さは1.12msであり、論理ユニットは2.25msです。 したがって、実際の状況では、値はわずかに異なります。









パッケージは、開始シーケンス、アドレス、およびコマンドで構成されます。 プロトコルの標準バージョンでは、アドレスとコマンドは直接形式と逆形式の両方で送信されるため、パケットの期間は常に固定です。







ボタンを押したままにすると、パッケージは再送信されません。 代わりに、11.25 msの特別な再試行コードが110 msごとに送信されます。







プログラム



必要ないので、アドレスを受け入れませんでした。 必要に応じて、プログラムを簡単に変更できます。 16 MHzクオーツを備えたATmega32がマイクロコントローラとして選択されました。 したがって、すべての時間間隔は16 MHzで計算されます。 プロトコルを実装するには、時間をカウントするタイマーと立ち下がりエッジでの外部割り込みが必要です。 タイマーは、1024の分周器、1024/16 MHzの1クロックサイクル= 64μs、64μs* 256 = 16 msのオーバーフロー割り込みで構成されます(これは明らかにパケット内のどのビットよりも大きいため、これは便利です)。

初期化および開始/停止タイマーマクロは次のようになります。



#define StopT0 TIMSK &= ~(1 << TOIE0); //    #define StartT0 TIMSK |= (1 << TOIE0); //   SREG|= (1<<7); //Global Interrupt Enable GICR|= (1<<INT0); //   INT0 MCUCR|=(1<<ISC01)|(0<<ISC00); //   TCCR0|=(1<<CS02)|(1<<CS00); // 1024,   64,  16,38  asm("sei"); //  
      
      







コードはIAR環境で記述されていますが、割り込みヘッダーを置き換えることで別の環境に簡単に移植できます。

タイマーオーバーフロー割り込みは、受信を「完了する」ためにのみ必要です。 16msのオーバーフローは、プリアンブルでもビットでも、パケットのどのコンポーネントよりも大きいため、このような割り込みはパケットの終わりと見なされ、次の割り込みを受信する準備ができます。



ここで私は一つのニュアンスを持っていましたが、その説明はわかりません。 タイマーがすぐに(INT0割り込みの処理後に自動的に)開始されると、タイマーオーバーフロー割り込みがトリガーされました。 どうやって? なんで? たぶん、当時の一般的なプログラムは既に小さくなかったので、これはある種の個々のカントだったのかもしれませんが、最初の割り込みを処理せず、2番目、つまり32ms後に処理することにしました。



 #pragma vector=TIMER0_OVF_vect __interrupt void TIMER0_interrupt (void) // 0 16,       { if (firstT ==0) { firstT = 1; } /*      ,    .    ,      32. */ else { startC = 0; //   firstT = 0; //  ""  StopT0; //  } }
      
      





INT0立ち下がりエッジ割り込み。 ここでは、ビットをカウントし、最後の中断からの経過時間を分析します。 TCNT0タイマーの値により、どのビットであったかが簡単にわかります。 ハンドラーの最後で、タイマーはゼロにリセットされ、最初からカウントダウンが開始されます。



 #pragma vector=INT0_vect __interrupt void INT0_interrupt (void) { if (startC == 0) { //  newC = 0; //   startC = 1; //  StartT0; // 0 } else { //    64,        if(TCNT0>0xD2 & TCNT0<0xFF){ //13,5    ( 13,4 ... 16,3 ) i = 32; //    } if(TCNT0>0x07 & TCNT0<0x16){ //1,12  ( 0,45 ... 1,41 ) if ((i>0) & (i<9)) Command1 &= ~(1<<(i-1)); //    if ((i>8) & (i<17)) Command |= (1<<(i-9)); //   i--; } if(TCNT0>0x19 & TCNT0<0x28){ //2,25  ( 1,60 ... 2,56 ) if ((i>0) & (i<9)) Command1 |= (1<<(i-1)); //    if ((i>8) & (i<17)) Command &= ~(1<<(i-9)); //   i--; } if(TCNT0>0xA9 & TCNT0<0xB8){ //11,25   ( 10,8 ... 11,8 ) newC = 1; //  } if (i==0) { //   StopT0; //  newC = 1; //   startC = 0; //   firstT = 0; //  ""   } } TCNT0 = 0; //  }
      
      





メインプログラムでは、リセットを忘れずにnewCフラグを分析します。 さて、チームのさらなる処理。



 while(1) { if (newC) { // ? newC = 0; //  if (Command == Command1) { //    ? switch (Command) { //  case 0x5F: { } //Up case 0xDF: { } //Down //........... } } } }
      
      





コマンドシステム



そして、これがED618リモートコントロールのコマンドシステムです(dx.comで購入します)。







他の同じRGBリモートの場合、コマンドシステムは異なる場合があります。 私はまったく同じリモコンを持っていますが、購入したRGBコントローラーから入手したので、コマンドシステムは非常に異なります。 自分で読んで確認してください。 たとえば、私はチームを承認し、コンピューターでUARTによってドロップしました。



アルゴリズム自体は複雑ではありません。誰かが役に立つといいのですが。 私はすでにそのようなプロトコルで複数のリモートコントロールデバイスを作成しました、すべてがうまく機能します。 次の記事では、ws2812b LEDプロトコルの実装について説明します



All Articles