0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ArduinoからMIL-STD-1553っぽい信号を出して遊ぶ

Posted at

まえがき

 素人なので温かい目でお願いします。

MIL-STD-1553とは?

  • おおむねF-16以降の時代の西側の軍用機で多く使用されているデータリンク
    • フライ・バイ・ワイヤいうやつ(飛ばすだけじゃなくて、もっといろいろ使える)
    • 初期のF-15は別のデータバスを使っていた(H009)
    • 最近(F-22やF-35)は別のデータバスを使っている(FireWire)
    • 中国やロシアは、自国の軍用機で対応して「西側のミサイルを撃てるよ」とか、自国のミサイルで対応して「西側の飛行機から撃てるよ」とか言って売ろうとしているらしい[要出典]
  • 人工衛星でもよく使われている
    • 転送速度がボトルネックになるので、データが大容量化してきた最近は別のバスを採用する傾向(SpaceWire)
  • 公称の通信速度は1Mbps
    • マンチェスタ符号なので2Mbaud
    • ヘッダ等を除いたデータレートは0.8Mbps
    • パケットの区切りに適当な待ち時間が必要なので実際の通信レートは更に低い
    • 1本のバスに1つのコントローラと、最大31個のターミナルが接続される
      • コントローラは必要に応じて冗長系が組まれる
      • バス自体も冗長化して使われる
    • コントローラ対ターミナルで通信を行う
      • コントローラがコマンドを送り、ターミナルがレスポンスを返す
      • ターミナル対ターミナルも可能だがオーバーヘッドが増える
      • コントローラをラウンドロビン方式に回すことも可能だが、信頼性が悪いのであまり使われない?
      • 通信はすべてコントローラが制御する(衝突が発生しない)
        • コントローラが故障すると一切の通信が途切れる
        • 冗長化したバスで片側が故障しても通信を続けるとか、コントローラを監視して異常が検出されたら予備を立ち上げるとか
    • ターミナル毎に最大30個(or15個)のサブアドレスを持つ
      • パケットは基本的にサブアドレスに対する書き込みor読み込みという形式
      • サブアドレスの読み書き単位は2-64オクテット(2オクテット単位)
    • ターミナルの数が足りない場合はツリー状に階層が組まれる
    • ターミナルに対して補助的なコマンドを送ることもできる
  • 今回はタイミングだけ扱う
    • 送信はArduinoでギリギリどうにかなる、受信は……たぶん無理
    • 電圧とか電線みたいな下位層や、データフォーマットみたいな上位層は扱わない

ライブラリ

 今回は秋月のPro Mini互換ボード(K-10347)を使用した。ATmega328@16MHz専用のライブラリになる。ビルドオプションとかコンパイラバージョンによっては正常なタイミングが得られない可能性もある。
 このボードはセラミック発振子が載っているので、個体差や環境によっては誤差が大きすぎる可能性もある。
 あくまで、遊びの範疇において動いている気がする、というレベル。

 あと、ステータスワードは実装が面倒なので手抜き。

 8bitのポートに直接出力するので、他の端子の都合とかは一切気にしない。割り込みも止める。

template <uint16_t buffer_size>
class MILSTD1553
{
public:
  static constexpr bool RX = false;
  static constexpr bool TX = true;
  
  MILSTD1553(volatile uint8_t*const port,
             const uint8_t hot,
             const uint8_t cold,
             const uint8_t reset)
   : port(port), hot(hot), cold(cold), reset(reset), counter() 
   {}

  void send(unsigned int us = 5)
  {
    buff[counter++] = reset;

    const uint8_t*p = buff;
    uint16_t i = counter;
    counter = 0;

    volatile uint8_t*const q = port;

    noInterrupts();
    do {
      *q = *p++;
    } while (--i);
    interrupts();

    delayMicroseconds(us);
  }

  bool command_word(const uint8_t address,
                    const bool transmit,
                    const uint8_t subaddress,
                    const uint8_t datacount)
  {
    return data_word(
      (address & 0x1F) << 11 |
      (transmit ? 0x400 : 0) |
      (subaddress & 0x1F) << 5 |
      (datacount & 0x1F),
      true);
  }

  bool status_word(const uint8_t address, const uint16_t bitfield)
  {
    return data_word(
      (address & 0x1F) << 11 |
      (bitfield & 0x3FFF),
      true);
  }

  bool data_word(const uint16_t data, const bool sync_inverse = false)
  {
    if (buffer_size <= counter + 40 + 1)
    {
      return false;
    }

    uint16_t*const p = reinterpret_cast<uint16_t*>(&buff[counter]);
    counter += 40;

    const uint16_t a = cold | hot  << 8;
    const uint16_t b = hot  | cold << 8;
    const uint16_t c = cold | cold << 8;
    const uint16_t d = hot  | hot  << 8;

    if (!sync_inverse)
    {
      p[0] = c; p[1] = a; p[2] = d;
    } else {
      p[0] = d; p[1] = b; p[2] = c;
    }

    p[ 3] = data & 0x8000 ? b : a;
    p[ 4] = data & 0x4000 ? b : a;
    p[ 5] = data & 0x2000 ? b : a;
    p[ 6] = data & 0x1000 ? b : a;
    p[ 7] = data & 0x0800 ? b : a;
    p[ 8] = data & 0x0400 ? b : a;
    p[ 9] = data & 0x0200 ? b : a;
    p[10] = data & 0x0100 ? b : a;
    p[11] = data & 0x0080 ? b : a;
    p[12] = data & 0x0040 ? b : a;
    p[13] = data & 0x0020 ? b : a;
    p[14] = data & 0x0010 ? b : a;
    p[15] = data & 0x0008 ? b : a;
    p[16] = data & 0x0004 ? b : a;
    p[17] = data & 0x0002 ? b : a;
    p[18] = data & 0x0001 ? b : a;

    p[19] = __builtin_popcount(data) & 1 ? a : b;

    return true;
  }

private:
  volatile uint8_t*const port;
  const uint8_t hot;
  const uint8_t cold;
  const uint8_t reset;
  uint8_t buff[buffer_size];
  uint16_t counter;
};

使用例

using My_MILSTD1553 = MILSTD1553<40 * 10 + 1>;

My_MILSTD1553 ms1553_A(&PORTD, 0x04, 0x08, 0);
My_MILSTD1553 ms1553_B(&PORTD, 0x04, 0x08, 0);
My_MILSTD1553 ms1553_C(&PORTD, 0x04, 0x08, 0);

void setup() {
  pinMode(2, OUTPUT);
  pinMode(3, OUTPUT);
}

void loop() {
  delay(47);

  { // BC to RT
    ms1553_A.command_word(3, My_MILSTD1553::RX, 1, 1);
    ms1553_A.data_word(0x1234);

    ms1553_B.status_word(3, 0);

    ms1553_A.send();
    ms1553_B.send();
  }
  { // RT to BC
    ms1553_A.command_word(3, My_MILSTD1553::TX, 1, 1);

    ms1553_B.status_word(3, 0);
    ms1553_B.data_word(0x1234);

    ms1553_A.send();
    ms1553_B.send();
  }
  { // RT to RT
    ms1553_A.command_word(3, My_MILSTD1553::RX, 1, 1);
    ms1553_A.command_word(4, My_MILSTD1553::TX, 1, 1);

    ms1553_B.status_word(4, 0);
    ms1553_B.data_word(0x1234);

    ms1553_C.status_word(3, 0);
    
    ms1553_A.send();
    ms1553_B.send();
    ms1553_C.send();
  }
  { // BC to RT(s)
    ms1553_A.command_word(31, My_MILSTD1553::RX, 1, 1);
    ms1553_A.data_word(0x1234);

    ms1553_A.send();
  }
  { // RT to RT(s)
    ms1553_A.command_word(31, My_MILSTD1553::RX, 1, 1);
    ms1553_A.command_word(4, My_MILSTD1553::TX, 1, 1);

    ms1553_B.status_word(4, 0);
    ms1553_B.data_word(0x1234);

    ms1553_A.send();
    ms1553_B.send();
  }
  { // BC to RT(s) mode without data
    ms1553_A.command_word(31, My_MILSTD1553::RX, 0, 1);

    ms1553_A.send();
  }
  { // BC to RT(s) mode with data
    ms1553_A.command_word(31, My_MILSTD1553::RX, 0, 17);
    ms1553_A.data_word(0x1234);

    ms1553_A.send();
  }
  {
    ms1553_A.command_word(3, My_MILSTD1553::RX, 1, 2);
    ms1553_A.data_word(0);
    ms1553_A.data_word(-1);

    ms1553_B.status_word(3, 0);

    ms1553_A.send();
    ms1553_B.send();
  }
}

 ブロードキャストやモードコマンドも含めて一通り。


 オシロで見るとこんな感じ。

20221223T083001 MIL-STD-1553 ATmega328-16MHz - コピー.png

 パラレルバスから出力しているので、平衡で出力してみた。


 ZEROPLUSのLAP-CはMIL-STD-1553バスのプロトコル解析にも対応している。

image.png

 ただしmode command without data wordがフレームエラーになり、MSBが1のデータワードも正常に処理できない(バグ?)。


 感覚としては、1Mbpsはちょっと早いけど、出力側から見た通信プロトコルはかなりシンプル。例えばCANバスみたいにビットスタッフィングやCRCは必要ない。UARTよりちょっと難しい程度。ただし通信速度故に受信側を作る難易度が高く、プロトコルに則った通信を行うのは困難だと思う。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?