Arduinoで遊ぶページ

Arduinoで遊んだ結果を残すページです。
garretlab
toneBegin()

toneBegin()

概要

toneBegin()は、tone()関数で利用するタイマとピンの関連づけを管理します。

ソースコード

toneBetin()は、hardware/arduino/cores/arduino/Tone.cpp に定義されています。以下に全ソースコードを示します。Arduino Unoの場合に適用される部分だけを抜粋しています。実際には#if によりさまざまなチップに対応しています。

volatile uint8_t *timer0_pin_port;
volatile uint8_t timer0_pin_mask;
volatile uint8_t *timer1_pin_port;
volatile uint8_t timer1_pin_mask;
volatile uint8_t *timer2_pin_port;
volatile uint8_t timer2_pin_mask;


#define AVAILABLE_TONE_PINS 1

// Leave timer 0 to last.
const uint8_t PROGMEM tone_pin_to_timer_PGM[] = { 2 /*, 1, 0 */ };
static uint8_t tone_pins[AVAILABLE_TONE_PINS] = { 255 /*, 255, 255 */ };


static int8_t toneBegin(uint8_t _pin)
{
  int8_t _timer = -1;

  // if we're already using the pin, the timer should be configured.  
  for (int i = 0; i < AVAILABLE_TONE_PINS; i++) {
    if (tone_pins[i] == _pin) {
      return pgm_read_byte(tone_pin_to_timer_PGM + i);
    }
  }
  
  // search for an unused timer.
  for (int i = 0; i < AVAILABLE_TONE_PINS; i++) {
    if (tone_pins[i] == 255) {
      tone_pins[i] = _pin;
      _timer = pgm_read_byte(tone_pin_to_timer_PGM + i);
      break;
    }
  }
  
  if (_timer != -1)
  {
    // Set timer specific stuff
    // All timers in CTC mode
    // 8 bit timers will require changing prescalar values,
    // whereas 16 bit timers are set to either ck/1 or ck/64 prescalar
    switch (_timer)
    {
      case 0:
        // 8 bit timer
        TCCR0A = 0;
        TCCR0B = 0;
        bitWrite(TCCR0A, WGM01, 1);
        bitWrite(TCCR0B, CS00, 1);
        timer0_pin_port = portOutputRegister(digitalPinToPort(_pin));
        timer0_pin_mask = digitalPinToBitMask(_pin);
        break;

      case 1:
        // 16 bit timer
        TCCR1A = 0;
        TCCR1B = 0;
        bitWrite(TCCR1B, WGM12, 1);
        bitWrite(TCCR1B, CS10, 1);
        timer1_pin_port = portOutputRegister(digitalPinToPort(_pin));
        timer1_pin_mask = digitalPinToBitMask(_pin);
        break;

      case 2:
        // 8 bit timer
        TCCR2A = 0;
        TCCR2B = 0;
        bitWrite(TCCR2A, WGM21, 1);
        bitWrite(TCCR2B, CS20, 1);
        timer2_pin_port = portOutputRegister(digitalPinToPort(_pin));
        timer2_pin_mask = digitalPinToBitMask(_pin);
        break;

    }
  }

  return _timer;
}

timerX_pin_portは、tone()で出力するピンに対応するポート(digitalWrite()の説明)を保持します。タイマ/カウンタ毎に変数が用意されています。

timerX_pin_maskは、ポート内のピンに対応するビット位置を示します。

volatile uint8_t *timer0_pin_port;
volatile uint8_t timer0_pin_mask;
volatile uint8_t *timer1_pin_port;
volatile uint8_t timer1_pin_mask;
volatile uint8_t *timer2_pin_port;
volatile uint8_t timer2_pin_mask;

AVAILABLE_TONE_PINSは1に定義されています。同時にtone()を利用できるピンの数です。

tone_pin_to_timer_PGM[]は、利用するタイマ/カウンタを定義する配列です。現状、"2"だけが定義されています。

tone_pins[]は、tone_pin_to_timer_PGM[]に対応するタイマ/カウンタで利用しているピンを表します。255は未使用ということを示します。初期状態では、タイマ2が使われていないという状態です。

#define AVAILABLE_TONE_PINS 1

// Leave timer 0 to last.
const uint8_t PROGMEM tone_pin_to_timer_PGM[] = { 2 /*, 1, 0 */ };
static uint8_t tone_pins[AVAILABLE_TONE_PINS] = { 255 /*, 255, 255 */ };

toneBegin()の入力は_pinでuint8_t型の変数です。int8_t型の値を返します。

指定されたピンですでにtone()を実行中であれば、そのピンで利用しているタイマ/カウンタの番号を返します。つまり、現状のピンの設定を変えることになります。

static int8_t toneBegin(uint8_t _pin)
{
  int8_t _timer = -1;

  // if we're already using the pin, the timer should be configured.  
  for (int i = 0; i < AVAILABLE_TONE_PINS; i++) {
    if (tone_pins[i] == _pin) {
      return pgm_read_byte(tone_pin_to_timer_PGM + i);
    }
  }

pgm_read_byte()は、指定したアドレス(PROGMEM領域)に格納されているデータを1バイト読み取るためのマクロです。tone_pin_to_timer_PGMは配列なので、tone_pin_to_timer_PGM+iは、配列の第i番目の要素を示します。

指定されたピンでtone()が実行されていない場合は、未使用のタイマ/カウンタを探します。

  // search for an unused timer.
  for (int i = 0; i < AVAILABLE_TONE_PINS; i++) {
    if (tone_pins[i] == 255) {
      tone_pins[i] = _pin;
      _timer = pgm_read_byte(tone_pin_to_timer_PGM + i);
      break;
    }
  }

tone_pins[]の値が255のものがあれば、tone_pins[]にピン番号を設定し、_timerにtone_pin_to_timer_PGM+iの値を代入します。

_timerの値が-1でなければ、すなわち、利用可能なタイマ/カウンタが見つかったときは、タイマ関連のレジスタの設定とtimerX_pin_port、timerX_pin_maskの設定を行います。

  if (_timer != -1)
  {
    // Set timer specific stuff
    // All timers in CTC mode
    // 8 bit timers will require changing prescalar values,
    // whereas 16 bit timers are set to either ck/1 or ck/64 prescalar
    switch (_timer)
    {
      case 0:
        // 8 bit timer
        TCCR0A = 0;
        TCCR0B = 0;
        bitWrite(TCCR0A, WGM01, 1);
        bitWrite(TCCR0B, CS00, 1);
        timer0_pin_port = portOutputRegister(digitalPinToPort(_pin));
        timer0_pin_mask = digitalPinToBitMask(_pin);
        break;

      case 1:
        // 16 bit timer
        TCCR1A = 0;
        TCCR1B = 0;
        bitWrite(TCCR1B, WGM12, 1);
        bitWrite(TCCR1B, CS10, 1);
        timer1_pin_port = portOutputRegister(digitalPinToPort(_pin));
        timer1_pin_mask = digitalPinToBitMask(_pin);
        break;

      case 2:
        // 8 bit timer
        TCCR2A = 0;
        TCCR2B = 0;
        bitWrite(TCCR2A, WGM21, 1);
        bitWrite(TCCR2B, CS20, 1);
        timer2_pin_port = portOutputRegister(digitalPinToPort(_pin));
        timer2_pin_mask = digitalPinToBitMask(_pin);
        break;

    }
  }

TCCRnXは、タイマ/カウンタを制御するレジスタで、今回はタイマをCTCモードに設定しています。

digitalPinToPort()portOutputRegister()digaitalPinToBitMask()の説明は、digitalWrite()の説明を参照してください。

各タイマで利用するポートとポート内のビットを、それぞれ、timerX_pin_portとtimerX_pin_maskに設定します。

最後に利用するタイマの番号を返却します。利用可能なタイマがない場合(すでに他のピンでtone()を実行している場合)は、_timerには-1が入っています。

  return _timer;
}

バージョン

Arduino 1.0.5



メニューを表示するためにJavaScriptを有効にしてください。

Arduinoで遊ぶページ
Copyright © 2012 garretlab all rights reserved.
inserted by FC2 system