2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Arduinoでラジコン受信機のパルスを計測するのに苦労した話

Last updated at Posted at 2020-11-08

問題の概要

Arduinoで受信機の信号を受信して処理したい場合、pulseIn()関数を用いることが多いが、以下の理由で困ったことが起きる。

  • 受信機メーカによっては同時にパルスが送られるため、同時に計測できない(例:タミヤ)。
  • 各チャネルが五月雨式に送られるため、パルス計測開始の遅れで計測値がばらつく(参考)。
  • 受信チャンネル数を増やすと、メイン処理を回す時間がなくなる。
    本来ではマイコンに内蔵されたタイマのパルス幅計測機能を使いたいところだが、delayやtoneなんかの脳死で使えるタイマ機能と被りが気になるところ。そこで次に述べる解決手段を使用した。

解決方法

  1. attachInterrupt()により、両エッジの外部端子入力割り込みを設定する
  2. 割り込み関数の中で、立ち上がり、立ち下がりエッジを判定。micros()で取得した時間を保存する。
  3. 立ち下がりエッジで立ち下がりエッジ~立ち下がりエッジの時間間隔を計測する。

コード例

#define         bit_read(portdat, portconf)          ((portdat & portconf) ? 1 : 0)  
#define         bit_write(portdat, portconf, hilo)   (portdat = (hilo ? (portdat | portconf) : (portdat & ~portconf)))  

// STR_RCV = PA0
#define         PINDIR_STR      (VPORTA.DIR) 
#define         PINRAM_STR      (VPORTA.IN) 
#define         PIN_STRRSV      (0x01)
#define         INTR_STRRSV     (2)

// ESC_RCV = PF5
#define         PINDIR_ESC      (VPORTF.DIR) 
#define         PINRAM_ESC      (VPORTF.IN) 
#define         PIN_ESCRSV      (0x20)
#define         INTR_ESCRSV     (3)

volatile unsigned long     time;
volatile unsigned long     strFallingUs;
volatile unsigned long     strRisingUs;
volatile unsigned long     strPlsWdhUs;
volatile unsigned long     escFallingUs;
volatile unsigned long     escRisingUs;
volatile unsigned long     escPlsWdhUs;

// セットアップ
void setup()
{
    //  入出力設定
    bit_write(PINDIR_STR,   PIN_STRRSV, 0);
    bit_write(PINDIR_ESC,   PIN_ESCRSV, 0);

    // 外部割り込み設定。INTRNo(0~45)を直接指示してもうまく行かない。digitalPinToInterruptを使用するのが推奨されるらしい。
    attachInterrupt(digitalPinToInterrupt(INTR_STRRSV), fnc_intr_irqStr, CHANGE);
    attachInterrupt(digitalPinToInterrupt(INTR_ESCRSV), fnc_intr_irqEsc, CHANGE);

    // デバッグ用コンソール
    Serial.begin(115200);
    Serial.print("Start");
    Serial.println();
    Serial.print("time,Sterring, ESC");
}

// メインループ
void loop()
{
    Serial.print(time);
    Serial.print(",");
    Serial.print(strPlsWdhUs);
    Serial.print(",");
    Serial.print(escPlsWdhUs);
    Serial.println();
    time++;
    delay(100);
}

void fnc_intr_irqStr()
{
    noInterrupts();
    if (bit_read(PINRAM_STR,PIN_STRRSV) )
    {
        // RISING
        strRisingUs = micros();
    }
    else
    {
        // Falling
        strFallingUs = micros();
        strPlsWdhUs = strFallingUs - strRisingUs;
    }
    interrupts();
}

void fnc_intr_irqEsc()
{
    noInterrupts();
    if (bit_read(PINRAM_ESC,PIN_ESCRSV) )
    {
        // RISING
        escRisingUs = micros();
    }
    else
    {
        escFallingUs = micros();
        // オーバーフローしても正しく計算可能
        escPlsWdhUs = escFallingUs - escRisingUs;
    }
    interrupts();
}

結果

青がハンドル。オレンジがスロットル。少々ばらつきがあるのが気になるところだが、フィルタ処理を行えば良さそう。なお、受信機のスイッチを切るとパルスが来なくなるため、値が更新されなくなる。

スクリーンショット 2020-11-08 16.29.08.png

ポイント

  • 時間のかかるdigitalRead()を使わず、レジスタを直接参照する。
  • ビット操作を楽にするためのマクロを使う(作る)。
  • micros()のオーバーフロー対策として、unsignedでそのまま引き算する。
2
2
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
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?