ハイパワー赤外線リモコンの製作

TVのリモコンはLED1ヶで赤外線通信しています.あれのLEDをハイパワーにしてみようというのがこの製作の目的です.
送受信でSTM8S-DISCOVERYを2ヶ使います.下図の上段が送信側、下段が受信側です.
ここでは送受信実験をするのが目的ですので、送信側は自走的にデータを送信し続けます.
受信側は、受信データをLCDに表示します.
とりあえずこれだけの機能ですが、赤外線の到達距離を試験するにはOKということにします.



赤外線通信の仕組みづくり
赤外線通信は、一般的には38kHzで変調したバースト波でデジタルデータを伝送します.(そうじゃないのもあります)

送信デバイス
赤外線の送信には、秋月電子で売られている「
赤外線LEDユニットFRS5JS」を使いました.
これを38kHzで高速点滅させるってことになります.


受信デバイス
赤外線受信センサーは秋月電子で売られている「
赤外線リモコン受信モジュールPL-IRM1261-C438」を使いました.
このセンサーには、38kHzのBPFが内蔵されていて、38kHzのバーストの有無を出力してくれます.



bit送受信の仕組み
38kHzという仕様は受信センサーで決められていますので踏襲するしかありませんが、それ以外の送受信仕様(プロトコル)は自分で勝手に決めてよいわけですので、以下のように決めました.

送信側波形と、受信側波形(受信センサー出力)をまとめると下表になります.
●送信側は、
    ”1”を送信するときには、38kHzバーストを500uS+休み500uS を出力します.
    ”0”を送信するときには、38kHzバーストを1000uS+休み500uS を出力します.
●受信センサー出力は、
普段はHighになっています.38kHzのバーストを受信すると、Lowになります.
したがって、
    ”1”を受信したときには、500uSのLow+500uSのHigh が出力されます.
    ”0”を受信したときには、1000uSのLow+500uSのHigh が出力されます.
●こういう受信信号を得るのですから、立ち下がりエッジから750uS後に受信信号をsampleすれば1か0を復号できます.



データ構造
上記で、1と0を送受信できる仕組みを定義できましたので、つぎはデータ構造を決めます.
下図のように、ID+アドレス+データからなるデータ構造にします.
IDを設けた理由は、受信側の機器が1つとは限らず、100個ぐらいたくさんある場合に、各々の受信側機器に向けて個別の制御をできるようにするためです.

ゆえに、受信信号には、32bit分のデータがひとかたまりになって現れます.下図は38kHzのバーストを意味する図ではありませんので注意してください.


エラー検出
データ構造を決めておしまいかというと、そうではないんですね~.
エラーが生じたときにどうするの?っていうことを考えないと、受信側機器が発狂してしまって大変なことになるかもしれませんから.
たとえば、この製作物が赤外線を発信しているときに、TVのリモコンを誰かが操作したら、確実にエラーが生じます.

わたしは最初、ID+アドレス+データ+CRC というデータ構造にしてCRCでエラーチェックしようかと考えたんですが、やめました.
CRCを生成するよりも、同じデータ構造を3回送信して、受信側で3回一致でエラーチェックすりゃ簡単でいいじゃんと思ったからです.
したがって下図のように、N番目のデータ構造(32bit)を3回送信します.送信頻度は、1秒間に6回送信します.
したがってデータレートは、2個/秒っていうことになります.



製作物の外観
下図は送信側回路の写真です.
ステッピングモータドライバの基板を流用したので、余計なものがゴチャゴチャついています.
本当に必要な回路はわずかです.放熱器も不要だしね.


下図は受信側回路の写真です.黄色いボタンはreset SWです.



製作物の回路図

送信回路
LEDモジュールの回路はつぎです.
売られている状態では、CdSセンサでオンオフさせる回路がついているので、それを切断し、12V直結にします.
12V直結にしたとき、300mAぐらい流れます.すなわち3.6Wの赤外線パワー.


LEDモジュールをドライブする回路はつぎです.
STM8S-PD4が出す38kHzのパルスを12Vに変換して右端のLEDモジュールを駆動します.
12Vは外部電源から導入します.78L05で5Vをつくり、STM8S-DISCOVERYにも与えます.


STM8S-DISCOVERYの回路はつぎです.
Reset SWはお好みでつければいいでしょう.


受信回路
下図はLCD部の回路図です.


赤外線センサーおよびLCDの実体配線図です.
Reset SWはお好みでつけてください.
電源について重要なことは、JP1を下図の設定にするということです.こうすることで、STM8S+LCD+赤外線センサがすべて5Vで動作します.
LCDは5V電源なので、全てを5Vで統一するためです.



製作物の動作試験

受信機の表示
赤外線通信が成功すると、LCD画面に、つぎの表示が出ます.
    myID32
    ID32 A:f0 D:xxxx
myIDというのは受信側装置のIDナンバーです.
2行目の表示は、受信データです.A:foはアドレスを表します.D:xxxxはデータを表します.
送信側装置がD:xxxxを逐次変更しているので、1234のところはクルクルと変化します.

近くでTVのリモコンを動かすと、LCDの表示が乱れますので、エラーが生じたことを察知できます.
ちなみにこのLCD表示は、あえて受信した生データをそのまま表示しており、3回一致とかID比較はやってませんので、エラーしたらエラーしたなりに表示が乱れます.

もうひとつ、STM8S-DISCOVERYのLEDが、ゆっくりと明るくなり→消灯するを繰り返します.
D:xxxxでLEDのdutyを制御しているので、リモートで明るさを制御していることになっています.

どのくらいの距離を通信できたか?
1)室内
自宅内において、見通しで最も遠距離にできる場所で通信試験をしました.
間に廊下を挟んだ部屋と部屋なので、壁の反射波はほとんどない状態で、距離は約15m.
この条件で、問題なく受信できました.
少し影になるポジションでも受信は可能でした.

2)屋外
これも壁の反射波がない条件です.
見通しできる場所で徒歩で遠ざかってどこまで受信できたかというと、30mぐらいが限界でした.

3)大きな会議室
我が家には会議室がないので、これはやっておりません.どうなるのかな?
会議室のような箱の中では壁が赤外線を反射するので、20x20mぐらいの箱の中で通信できるんじゃないかと期待します.


ソースコードの解説

サンプルプログラムをこちらに置きます.

送信回路

main.c

#include "stm8s.h"
STマイクロ社提供のライブラリを使うためにインクルードします.

#define CK_Hz            16000000  // cpu clk
clock周波数です.16MHz

#define WIDTH1_us        470       // mark "1" 500uS
#define WIDTH0_us        970      // mark "0" 1000uS
38kHzをバースト送信するときのバースト時間幅を規定しています.
”1”なら500uSec、”0”なら1000uSecです.
500と1000じゃない理由は、割り込みルーチンのオーバーヘッドが30uSecぐらいあるので、現物合わせで470uSecと970uSecに決めました.


#define CARRIER_Hz  38000     // carrier freq 38kHz
38kHzを規定しています.

#define TIM2_ARR         ((float)CK_Hz/(float)CARRIER_Hz-1)
38kHzはTIM2で生成します.カウンタのmax値を規定しています.16MHz/38kHz=4209になると思います.

// prototypes
void transmit(void);

void main(void)
{
    CLK_ClockSwitchConfig ( CLK_SWITCHMODE_AUTO, CLK_SOURCE_HSE, DISABLE, CLK_CURRENTCLOCKSTATE_DISABLE );
clockを外部水晶の16MHzに設定しています.

    GPIO_Init(GPIOD, GPIO_PIN_0, GPIO_MODE_OUT_PP_HIGH_FAST); // LED indicator
LEDインジケータが接続されたポートPD0を出力に設定しています.

    TIM1_DeInit();
    TIM1_TimeBaseInit( 16000, TIM1_COUNTERMODE_UP, 166, 0 ); // 16MHz/16000/166=6Hz
    TIM1_ITConfig(TIM1_IT_UPDATE, ENABLE);
    TIM1_Cmd(ENABLE);
TIM1に、データ送信インターバルを生成させます.この設定では6Hzの割り込みを発生させます.
6Hzというのは、下図の1秒間に6回ということです.


    TIM2_DeInit();
    TIM2_TimeBaseInit( TIM2_PRESCALER_1, (u16)TIM2_ARR-1);  // 16MHz/TIM2_ARR=38kHz
    TIM2_OC1Init(TIM2_OCMODE_PWM1, TIM2_OUTPUTSTATE_ENABLE, (u16)(TIM2_ARR/2), TIM2_OCPOLARITY_HIGH); // output D4
    TIM2_Cmd(ENABLE);
TIM2は、38kHzを発生させます.ここでは割り込みは発生させません.
TIM2の出力ポートは、TIM2_CH1がPD4に割り当てられています.(下記を参照)
http://stm8sdatasheet.web.fc2.com/STVD-project21-TIMER1234-PWM/STM8S-TIMER1234-PWM.html
したがって、PD4に、38kHzがダラダラと出ることになります.
一方で、赤外線通信をするためには、38kHzをオン/オフさせなくちゃいけないわけですので、それは、次の関数で根こそぎオンオフさせます.
    TIM2_Cmd(ENABLE);     オン
    TIM2_Cmd(DISABLE);    オフ

   enableInterrupts();
割り込みを許可します.

   while(1) { transmit(); } // Main loop
送信ルーチンを無限ループでコールしつづけます.
}


// data send interval timer
u8 start=0;
void Tim1Update(void) interrupt 11
166mSec毎のTIM1割り込みでここに飛んできます.11番はTIM1の割り込み番号です.
{
    start=1;  // transfer start      まずは、送信開始フラグを立てます.
    GPIO_WriteReverse ( GPIOD, GPIO_PIN_0 );    LEDインジケータをチカチカさせます.他意はありません.
    TIM1_ClearFlag(TIM1_FLAG_UPDATE);   次の割り込みの準備のためフラグをクリアするおまじないです.
    return;
}


u8 end_of_tim3_delay;     遅延終了フラグ
#define SHORT 1       470uSec遅延の意味
#define LONG  2       970uSec遅延の意味
void delay(u8 x){       割り込みで470Usecまたは970uSecの遅延時間を作るルーチンです
    if(x==SHORT) { // mark "1"    470uSecだったら、
        // Delay timer interrupt
        TIM3_DeInit();     TIM3で遅延時間を作ります
        TIM3_TimeBaseInit( TIM3_PRESCALER_1, (u16)((float)CK_Hz/(float)1000000*(float)WIDTH1_us-1) );
この設定ですと、470uSec後にカウンタがオーバーフローします.
        TIM3_ITConfig(TIM3_IT_UPDATE, ENABLE);    カウンタがオーバーフローしたら割り込みをかけてもらいます.
        TIM3_ClearFlag(TIM3_FLAG_UPDATE);    これはたぶん不要な関数ですがなんとなく残しました
        TIM3_Cmd(ENABLE);     カウンタ動作開始
    }
    else if(x==LONG){ // mark "0"    970uSecだったら、以下同様
        // Delay timer interrupt
        TIM3_DeInit();
        TIM3_TimeBaseInit( TIM3_PRESCALER_1, (u16)((float)CK_Hz/(float)1000000*(float)WIDTH0_us-1) );
この設定ですと、970uSec後にカウンタがオーバーフローします.
        TIM3_ITConfig(TIM3_IT_UPDATE, ENABLE);
        TIM3_ClearFlag(TIM3_FLAG_UPDATE);
        TIM3_Cmd(ENABLE);
    }
    // wait untill tim3 end
    while(end_of_tim3_delay!=1){;}
ここで、whileループで待ち続けます.
そして裏で、470uSecまたは970uSec後にTIM3の割り込みルーチンが
end_of_tim3_delayフラグを立ててくれるので、それまで待ち続けます.
    end_of_tim3_delay=0;      フラグクリア
    return;    遅延のための暇つぶしを終えてreturnします
}


void Tim3Update(void) interrupt 15
delay()で470uSecまたは970uSecの遅延時間をセットされたTIM3がオーバーフローしたら割り込みが発生してここへ飛んできます.
{

    TIM3_Cmd(DISABLE);   これいじょうTIM3がクルクルまわる必要はないので、TIM3を止めます.
    end_of_tim3_delay=1;    フラグを立てます
    TIM3_ClearFlag(TIM3_FLAG_UPDATE);    割り込み解消
    return;
}


//*************************************************
//    transmit routine
//*************************************************

// mark "1" ___||||||_____||||
// mark "0" ___|||||||||||_____||||
void mark(u8 x){      bit”1”か”0”を送信するルーチン
    TIM2_Cmd(ENABLE);    TIM2をオン=38kHzオン
    if(x==1) delay(SHORT);     1を送信したいなら470uSec遅延
    else if(x==0) delay(LONG);     0を送信したいなら970uSec遅延
    TIM2_Cmd(DISABLE);       TIM2をオフ=38kHzオフ
    delay(SHORT);
      470uSecの無信号期間をつくるための遅延
}


// recieve device parameter
u8 id = 0x32;     受信側装置のIDを32に設定しています
u8 adrs = 0xf0;    受信装置のアドレスをf0に設定しています
u16 dat = 0x0000;     送信データ
u8 seq=0;   シーケンサ
u8 times=0;    送信回数
void transmit(void)      送信するルーチン
{
    switch(seq){    32bitの送信データをシーケンサで1bitづつ送信してゆきます
        case(0): if(start){start=0;seq=1;}  break;    TIM1が発生する送信開始フラグで送信開始
        case(1): dat=dat+0x1234; seq=2; break;     次の16bitデータを用意するために1234hを加算する
        case(2): times=1; seq=11; break;     同じデータを3回送信する1回目を開始する
       
        case(5): if(start){start=0;times=2;seq=11;}  break; // transmit start 2nd   2回目の開始待ち

        case(6): if(start){start=0;times=3;seq=11;}  break; // transmit start 3rd   3回目の開始待ち

        case(11): mark((id&0x80)>>7); seq++; break; // transmit ID   IDのMSBから送信開始
        case(12): mark((id&0x40)>>6); seq++; break;  送信するルーチンはmark()
        case(13): mark((id&0x20)>>5); seq++; break;
        case(14): mark((id&0x10)>>4); seq++; break;
        case(15): mark((id&0x08)>>3); seq++; break;
        case(16): mark((id&0x04)>>2); seq++; break;
        case(17): mark((id&0x02)>>1); seq++; break;
        case(18): mark((id&0x01));    seq=20; break;
       
        case(20): mark((adrs&0x80)>>7); seq++; break; // transmit ADRS  アドレスのMSBから送信
        case(21): mark((adrs&0x40)>>6); seq++; break;
        case(22): mark((adrs&0x20)>>5); seq++; break;
        case(23): mark((adrs&0x10)>>4); seq++; break;
        case(24): mark((adrs&0x08)>>3); seq++; break;
        case(25): mark((adrs&0x04)>>2); seq++; break;
        case(26): mark((adrs&0x02)>>1); seq++; break;
        case(27): mark((adrs&0x01)); seq=30; break;
       
        case(30): mark((dat&0x8000)>>15); seq++; break; // transmit DATA   データのMSBから送信
        case(31): mark((dat&0x4000)>>14); seq++; break;
        case(32): mark((dat&0x2000)>>13); seq++; break;
        case(33): mark((dat&0x1000)>>12); seq++; break;
        case(34): mark((dat&0x0800)>>11); seq++; break;
        case(35): mark((dat&0x0400)>>10); seq++; break;
        case(36): mark((dat&0x0200)>>9); seq++; break;
        case(37): mark((dat&0x0100)>>8); seq++; break;
        case(38): mark((dat&0x0080)>>7); seq++; break;
        case(39): mark((dat&0x0040)>>6); seq++; break;
        case(40): mark((dat&0x0020)>>5); seq++; break;
        case(41): mark((dat&0x0010)>>4); seq++; break;
        case(42): mark((dat&0x0008)>>3); seq++; break;
        case(43): mark((dat&0x0004)>>2); seq++; break;
        case(44): mark((dat&0x0002)>>1); seq++; break;
        case(45): mark((dat&0x0001)); seq=50; break;
       
        case(50): if(times==1) seq=5; // complete 1st transmit    1回目終了したら2回目待ちへ飛ぶ
                       else if(times==2) seq=6; // complete 2nd transmit   2回目終了したら3回目待ちへ飛ぶ
                       else if(times>=3) seq=0; // complete 3rd transmit   3回目終了したら次の1回目待ちへ飛ぶ
                       break;
    }
    return;
}

#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t* file, uint32_t line)
{
  while (1)  {  }
}
#endif



受信回路

main.c

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
LCD表示のためにprintf()などを使いますのでインクルードします.

#include "stm8s.h"
STマイクロ社のライブラリを使うためにインクルードします.

#include "usrlib-lcd.h"
LCDを使うために自作のライブラリをインクルードします.
このライブラリコードを見るとわかりますが、TIM3を占有してしまいますので、ご注意ください.

#define CK_Hz            16000000  // cpu clk   clock 16MHz
#define DELAY_us        750       // check "1" or "0" after 750uS
受信信号の立ち下がりエッジから750uSec後に、受信信号の1または0を判定すればよいので750uSecを定義してあります.

// prototypes
void recieve(void);

// groval variables
char str[20];    LCD表示文字列

void main(void)
{
    CLK_ClockSwitchConfig ( CLK_SWITCHMODE_AUTO, CLK_SOURCE_HSE, DISABLE, CLK_CURRENTCLOCKSTATE_DISABLE );
外部水晶の16MHzを使います

    LCD_Init();   LCD初期化

    GPIO_Init(GPIOA, GPIO_PIN_5, GPIO_MODE_IN_PU_IT); // IRDA interrupt
このポートPA5には、赤外線センサーを取りつけてあります.
GPIO_MODE_IN_PU_ITの末尾のITは、この端子を割り込み入力として使う意味があります.

    GPIO_Init(GPIOD, GPIO_PIN_0, GPIO_MODE_OUT_PP_HIGH_FAST);   LEDインジケータがPD0についてます

    EXTI_DeInit();   GPIO割り込み初期化
    EXTI_SetExtIntSensitivity( EXTI_PORT_GPIOA, EXTI_SENSITIVITY_FALL_ONLY );
GPIOAの立ち下がりエッジで割り込みをかけるという意味です.

    TIM4_DeInit();
    TIM4_TimeBaseInit( TIM4_PRESCALER_16, 1000 ); // 16MHz/16/1000=1kHz
    TIM4_ITConfig(TIM4_IT_UPDATE, ENABLE);
    TIM4_Cmd(ENABLE);
TIM4では、1kHzで割り込みをかけてもらいます.LEDインジケータの明るさを制御するためです.
   
    enableInterrupts();  割り込み許可
    while(1) { recieve(); } // Main loop    受信ルーチンの永久ループ
}


// IRDA negative edge interrupt
void gpioa(void) interrupt 3 {       GPIOAの立ち下がりエッジで割り込みが発生してここに飛んできます.
    if(GPIO_ReadInputPin( GPIOA, GPIO_PIN_5 )==0) {   PA5=0であることの確認

        // Delay timer start
        TIM2_DeInit();  
        TIM2_TimeBaseInit( TIM2_PRESCALER_1, (u16)((float)CK_Hz/(float)1000000*(float)DELAY_us-1) );
        TIM2_ITConfig(TIM2_IT_UPDATE, ENABLE);
        TIM2_ClearFlag(TIM2_FLAG_UPDATE);
        TIM2_Cmd(ENABLE);
受信信号のネガティブエッジで、TIM2をスタートさせます.
そして750uSec後に割り込みをかけさせるようにします.
750uSec後に0か1かの判定をするためです.

        // timeout timer start
        TIM1_DeInit();
        TIM1_TimeBaseInit( 0, TIM1_COUNTERMODE_UP, 0xFFFE, 0 );
        TIM1_ITConfig(TIM1_IT_UPDATE, ENABLE);
        TIM1_ClearFlag(TIM1_FLAG_UPDATE);
        TIM1_Cmd(ENABLE);
タイムアウトという大事なタイマをスタートさせます.
ところでタイムアウトって何でしょうか?
赤外線の経路を人が歩いて遮断したときなんかで、赤外線通信路は容易にダウンします.
そんなふうに赤外線通信路がダウンしているにもかかわらず、次のbitが来るのを延々と待っていたのでは、受信側装置がハングアップしてしまって困ります.
あるいは、赤外線通信路が再開したときに、bitの順番がめちゃくちゃになって受信側装置が発狂してしまって困ります.
なので、待てども次のbitが来ないときには、ひとまず受信をあきらめて、次の32bitデータの先頭bitが来るのを待つのが吉です.
そこで、受信信号のネガティブエッジで、4mSecのタイマをスタートさせます.
このタイマは、リトリガラブルに運用しますので、データを正常に受信できているうちはオーバーフローしません.
信号がとぎれたり、32bitを受信できた後で、4ms以上データが来なかったら割り込みが発生します.
そしてその割り込みは、次の32bitを受信するべく構えよという意味を持ちます.

        return;
    }
}


// timeout timer
u8 timeout=0;
void Tim1Update(void) interrupt 11    4mSec後にタイムアウトしたらここに飛んできます
{
    TIM1_Cmd(DISABLE);    TIM1を止めます
    timeout=1;  // recieve timeout     タイムアウトフラグを立てます
    TIM1_ClearFlag(TIM1_FLAG_UPDATE);    割り込みフラグを消します
    return;
}


// sample recieved data
u8 recieved_bit_exist=0;
u32 recieved_bit=0;
void Tim2Update(void) interrupt 13     750uSec後にデータの1か0を判定する割り込みルーチン
{
    if(GPIO_ReadInputPin(GPIOA,GPIO_PIN_5)) {    PA5=1なら、
        recieved_bit=1;     受信bit=1
    }
    else {
        recieved_bit=0;     さもなくば受信bit=0
    }
    recieved_bit_exist=1;    受信bitありのフラグを立てる
    TIM2_Cmd(DISABLE);    TIM2を止める
    TIM2_ClearFlag(TIM2_FLAG_UPDATE);   割り込みフラグを消します
    return;
}

// 1kHz chopper LED indicator
u16 cnt_tim4=0;
u16 comp_tim4;
void TIM4_interrupt(void) interrupt 23  {   LEDインジケータの明るさを調整するルーチンです
    cnt_tim4+=2000;     カウンタを2000づつカウントアップ
    if(cnt_tim4>comp_tim4) GPIO_WriteHigh( GPIOD, GPIO_PIN_0 );    受信データよりもカウンタが大ならLEDオフ
    else GPIO_WriteLow( GPIOD, GPIO_PIN_0 );   さもなくばLEDオン
    TIM4_ClearFlag(TIM4_FLAG_UPDATE);     割り込みフラグを消す
}

//*************************************************
//    recieve routine
//*************************************************
// recieve device parameter
u8 id_self = 0x32;  // device ID of this device    自分のIDを32Hに設定

u32 rdat,rdat1,rdat2,rdat3; // recieved data consists of ID+ADRS+DATA

rdatは最新の32bit受信データ.
rdat1は1つ前の32bit受信データ.
rdat2は2つ前の32bit受信データ.
rdat3は3つ前の32bit受信データ.

u8 seq=0;
u16 id=0,adrs=0,dat=0;
void recieve(void)     データ受信ルーチンです
{
    if(timeout) { timeout=0; seq=0; rdat=0; } // wait next data sequence
タイムアウトフラグが立ったら、受信を中断して次の32bitの到来を待つ
    else {
        switch(seq) {
            case(0):  if(recieved_bit_exist){rdat+=recieved_bit<<31; recieved_bit_exist=0; seq++;} break;
recieved_bit_existフラグは、1bit受信したという意味があります.
受信データrdatにMSBから順に埋めて行きます.
以下同様にLSBまで同じ動作をします.
            case(1):  if(recieved_bit_exist){rdat+=recieved_bit<<30; recieved_bit_exist=0; seq++;} break;
            case(2):  if(recieved_bit_exist){rdat+=recieved_bit<<29; recieved_bit_exist=0; seq++;} break;
            case(3):  if(recieved_bit_exist){rdat+=recieved_bit<<28; recieved_bit_exist=0; seq++;} break;
            case(4):  if(recieved_bit_exist){rdat+=recieved_bit<<27; recieved_bit_exist=0; seq++;} break;
            case(5):  if(recieved_bit_exist){rdat+=recieved_bit<<26; recieved_bit_exist=0; seq++;} break;
            case(6):  if(recieved_bit_exist){rdat+=recieved_bit<<25; recieved_bit_exist=0; seq++;} break;
            case(7):  if(recieved_bit_exist){rdat+=recieved_bit<<24; recieved_bit_exist=0; seq++;} break;
            case(8):  if(recieved_bit_exist){rdat+=recieved_bit<<23; recieved_bit_exist=0; seq++;} break;
            case(9):  if(recieved_bit_exist){rdat+=recieved_bit<<22; recieved_bit_exist=0; seq++;} break;
            case(10): if(recieved_bit_exist){rdat+=recieved_bit<<21; recieved_bit_exist=0; seq++;} break;
            case(11): if(recieved_bit_exist){rdat+=recieved_bit<<20; recieved_bit_exist=0; seq++;} break;
            case(12): if(recieved_bit_exist){rdat+=recieved_bit<<19; recieved_bit_exist=0; seq++;} break;
            case(13): if(recieved_bit_exist){rdat+=recieved_bit<<18; recieved_bit_exist=0; seq++;} break;
            case(14): if(recieved_bit_exist){rdat+=recieved_bit<<17; recieved_bit_exist=0; seq++;} break;
            case(15): if(recieved_bit_exist){rdat+=recieved_bit<<16; recieved_bit_exist=0; seq++;} break;
            case(16): if(recieved_bit_exist){rdat+=recieved_bit<<15; recieved_bit_exist=0; seq++;} break;
            case(17): if(recieved_bit_exist){rdat+=recieved_bit<<14; recieved_bit_exist=0; seq++;} break;
            case(18): if(recieved_bit_exist){rdat+=recieved_bit<<13; recieved_bit_exist=0; seq++;} break;
            case(19): if(recieved_bit_exist){rdat+=recieved_bit<<12; recieved_bit_exist=0; seq++;} break;
            case(20): if(recieved_bit_exist){rdat+=recieved_bit<<11; recieved_bit_exist=0; seq++;} break;
            case(21): if(recieved_bit_exist){rdat+=recieved_bit<<10; recieved_bit_exist=0; seq++;} break;
            case(22): if(recieved_bit_exist){rdat+=recieved_bit<<9;  recieved_bit_exist=0; seq++;} break;
            case(23): if(recieved_bit_exist){rdat+=recieved_bit<<8;  recieved_bit_exist=0; seq++;} break;
            case(24): if(recieved_bit_exist){rdat+=recieved_bit<<7;  recieved_bit_exist=0; seq++;} break;
            case(25): if(recieved_bit_exist){rdat+=recieved_bit<<6;  recieved_bit_exist=0; seq++;} break;
            case(26): if(recieved_bit_exist){rdat+=recieved_bit<<5;  recieved_bit_exist=0; seq++;} break;
            case(27): if(recieved_bit_exist){rdat+=recieved_bit<<4;  recieved_bit_exist=0; seq++;} break;
            case(28): if(recieved_bit_exist){rdat+=recieved_bit<<3;  recieved_bit_exist=0; seq++;} break;
            case(29): if(recieved_bit_exist){rdat+=recieved_bit<<2;  recieved_bit_exist=0; seq++;} break;
            case(30): if(recieved_bit_exist){rdat+=recieved_bit<<1;  recieved_bit_exist=0; seq++;} break;
            case(31): if(recieved_bit_exist){rdat+=recieved_bit;     recieved_bit_exist=0; seq=40;} break;
rdatのLSBまで受信完了.

            case(40): // display recieved data
                                id        =(rdat&0xFF000000)>>24;   受信IDを分離
                                adrs    =(rdat&0x00FF0000)>>16;   受信アドレスを分離
                                dat        =    rdat&0x0000FFFF;      受信データを分離
                                sprintf(str,"myID%02x",id_self);
                                LCD_Disp_Str(str,LCD_DISP_UPPERLINE);   自機IDをLCDに表示
                                sprintf(str,"ID%02x A:%02x D:%04x",id,adrs,dat);
                                LCD_Disp_Str(str,LCD_DISP_LOWERLINE);   受信データをLCDに表示
                                seq=41;
                                break;
            case(41): rdat3=rdat2; rdat2=rdat1; rdat1=rdat; seq=42; break;
最新の受信データから1,2,3個目までを残してrdatをシフト.

            case(42): // check error or good (3 times the same?)
                                if(rdat3==rdat2 && rdat2==rdat1) seq=50; else seq=0; break;
rdat3=rdat2=rdat1なら受信データは正常とみなします.
NGならシーケンス0へ戻る.

            case(50): // ID check
                                id        =(rdat3&0xFF000000)>>24;
                                adrs    =(rdat3&0x00FF0000)>>16;
                                dat        =    rdat3&0x0000FFFF;
                                if(id_self!=id) seq=0; // not myself
                                else seq=51;
                                break;
rdat3からID,アドレス,データを分離.
自機IDと受信IDの一致を判断する.一致しないならシーケンス0へ戻る.

            case(51): comp_tim4=dat; // GOOD data process
                                seq=0;
                                break;
データが正常だとわかったところで、受信データをcomp_tim4に代入して、処理を完了します.
アドレスはここではシカトします.

            default:     seq=0;
                                break;
        }
    }
    return;
}

#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t* file, uint32_t line)
{
  while (1)  {  }
}
#endif

inserted by FC2 system