なんでも作っちゃう、かも。

Arduino/Make/フィジカルコンピューティング/電子工作あたりで活動しています。スタバの空きカップを使ったスタバカップアンプなど製作。最近はもっぱらArduinoと3Dプリンタの自作に興味があります。

Arduinoで遊ぼう - digitalWrite関数をハックする

Posted by arms22 on 2009年01月25日 0  0

digitalWrite関数は指定したピンにHIGHまたLOWを出力する関数だ。Arduinoボード上に書かれているピン番号をdigitalWrite関数に指定すればそのピンの出力が変わる。

AVRマイコンのどのポートのどのビットかなんて気にしなくても、Arduinoボード上のピン番号を指定するだけでいい。AVRマイコンの違いをうまく吸収し、デジタルポートというシンプル機能を提供してくれる。

その反面、ピン1つ出力を変化させるのに1回のdigitalWrite関数の呼び出しが必要で、高速なピン制御には向かない。今回、そんなdigitalWrite関数をハックし、高速なピン制御の方法を紹介する。

まずはdigitalWrite関数の実装を見てみよう(arduino-0012/hardware/cores/arduino/wiring_digital.c)。

void digitalWrite(uint8_t pin, uint8_t val)
{
  uint8_t timer = digitalPinToTimer(pin);
  uint8_t bit = digitalPinToBitMask(pin);
  uint8_t port = digitalPinToPort(pin);
  volatile uint8_t *out;

  if (port == NOT_A_PIN) return;

  // If the pin that support PWM output, we need to turn it off
  // before doing a digital write.
  if (timer != NOT_ON_TIMER) turnOffPWM(timer);

  out = portOutputRegister(port);

  if (val == LOW) *out &= ~bit;
  else *out |= bit;
}
注目すべきポイントは三つ。digitalPinToBitMask関数、digitalPinToPort関数、portOutputRegister関数だ。
digitalPinToBitMask関数はピン番号をレジスタのビットマスクに、digitalPinToPort関数はピン番号をポート番号に、portOutputRegister関数はポート番号をポートレジスタのアドレスに変換する。これらの処理は一度行ってしまえばよくて、2度目以降はピン番号から得られたポートレジスタに直接書込むことで高速にピン制御を行うことができる。

実際にshiftOut関数を高速化してみた。これで3倍は速くなっているはず。
void shiftOut(uint8_t dataPin,
              uint8_t clockPin,
              uint8_t bitOrder,
              byte val)
{
  int i;
  uint8_t bit_data = digitalPinToBitMask(dataPin);
  uint8_t bit_clock = digitalPinToBitMask(clockPin);
  volatile uint8_t *out_data = portOutputRegister(digitalPinToPort(dataPin));
  volatile uint8_t *out_clock = portOutputRegister(digitalPinToPort(clockPin));
  
  for (i = 0; i < 8; i++) {
    if (bitOrder == LSBFIRST){
      if(val & (1 << i))
        *out_data |= bit_data;
      else
        *out_data &= ~bit_data;
    }else{
      if(val & (1 << (7 - i)))
        *out_data |= bit_data;
      else
        *out_data &= ~bit_data;
  }
        
  *out_clock |= bit_clock;
  *out_clock &= ~bit_clock;
  }
}
オリジナルのコード(arduino-0012/hardware/cores/arduino/wiring_shift.c)。
void shiftOut(uint8_t dataPin,
              uint8_t clockPin,
              uint8_t bitOrder,
              byte val)
{
  int i;

  for (i = 0; i < 8; i++) {
    if (bitOrder == LSBFIRST)
      digitalWrite(dataPin, !!(val & (1 << i)));
    else
      digitalWrite(dataPin, !!(val & (1 << (7 - i))));
      
    digitalWrite(clockPin, HIGH);
    digitalWrite(clockPin, LOW);
  }
}
今回、紹介した高速化はArduino 012でのみ動作確認しています。012以降のバージョン、また012以前のバージョンにおいて動作するかどうかは未検証です。またdigitalWrite関数をハックし内部関数の直接呼び出しを行っています。012以降のバージョンでこれらの関数がなくなってしまう可能性もあり、著しく互換性を落としてしまう諸刃の剣であることもご理解ください。
digitalPinToBitMask関数、digitalPinToPort関数、portOutputRegister関数を使うにはpins_arduino.hをインクルードする必要があります。


Arduino - DigitalWrite
http://www.arduino.cc/en/Reference/DigitalWrite


Making Things Talk -Arduinoで作る「会話」するモノたち
Tom Igoe
オライリージャパン
売り上げランキング: 55559


Ads by Google

Leave a reply






管理者にだけ表示を許可する

該当の記事は見つかりませんでした。