Arduino でパリティエラーチェックを実装する

私が使ったのは Arduino MEGA 2560 R3 の互換機。

Aruduino IDE には Aruduino 本体のソースコードも含まれており、本体のソースコードを編集すると、スケッチをコンパイルする時に本体が修正されていることを検知し、同時にコンパイルしてくれる。私の環境では Aruduino をインストールしたフォルダの下にhardware というフォルダがあり、その中に本体のコードがあった。Github上のリポジトリでは arduino/ArduinoCore-avr に ハードウェアシリアルのソースがある。

そのソースのシリアルデータ受信部分は以下のリンクの通り

ArduinoCore-avr/HardwareSerial_private.h at master · arduino/ArduinoCore-avr · GitHub

ここでは、パリティエラー/フレーミングエラー/オーバーランエラーが発生していても、受信データを破棄するだけでエラー処理は行っていない。

それを以下のように修正する。

void HardwareSerial::_rx_complete_irq(void)
{
    // (1) 条件を変更
    if (bit_is_clear(*_ucsra, UPE0) && bit_is_clear(*_ucsra, FE0) && bit_is_clear(*_ucsra, DOR0)) {
        icount.rx++; // (2) 受信回数を記録
        unsigned char c = *_udr;
        rx_buffer_index_t i = (unsigned int)(_rx_buffer_head + 1) % SERIAL_RX_BUFFER_SIZE;

        if (i != _rx_buffer_tail) {
            _rx_buffer[_rx_buffer_head] = c;
            _rx_buffer_head = i;
        }
    } else {
        // --- (3) ここから
        if (bit_is_set(*_ucsra, UPE0)) {
            icount.parity++;
        }

        if (bit_is_set(*_ucsra, FE0)) {
            icount.frame++;
        }

        if (bit_is_set(*_ucsra, DOR0)) {
            icount.overrun++;
        }
        // --- (3) ここまで

        *_udr;
    };
}

(1) 通信状態を表すレジスタ(UCSRnA) からパリティエラー・フレーミングエラー・オーバーランエラーのビットをチェックし、全て 0なら正常受信、いずれか一つでも1ならエラー発生としている。

(2) ついでに、正常受信なら受信データ数をカウント

(3) 各エラーをカウント

通信まわりのレジスタの情報は以下のサイトが参考になる。

シリアル通信関連のレジスタ

icountHardwareSerial.h に定義を追加し HardwareSerial に メンバーを足す。 また、スケッチから利用できるように icount_t を返すメソッドを追加する。

struct icount_t {
  unsigned long parity;
  unsigned long frame;
  unsigned long overrun;
  unsigned long rx;
};

class HardwareSerial : public Stream
{
  protected:
    // ...

    struct icount_t icount;

    // ...

  public:
    // ...

    virtual struct icount_t counter(void);

    // ...

メソッドの実態は HardwareSerial.cpp に追加する

struct icount_t HardwareSerial::counter(void)
{
    return icount;
}

スケッチでは、以下のように利用する

void setup() {
  Serial.begin(9600);
}

void loop() {
  Serial.print(Serial.counter().rx);
  Serial.print(Serial.counter().parity);
  Serial.print(Serial.counter().frame);
  Serial.print(Serial.counter().overrun);
}

スケッチ内でパリティエラーの前回値を取得しておき、現在値と比較することでエラーが新たに発生しているかを検知できる。ちょっとまどろっこしいが、ないよりはマシかなと思う。