toneBegin()

概要

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

ソースコード

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
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は、ポート内のピンに対応するビット位置を示します。

1
2
3
4
5
6
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が使われていないという状態です。

 8
 9
10
11
12
#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()を実行中であれば、そのピンで利用しているタイマ/カウンタの番号を返します。つまり、現状のピンの設定を変えることになります。

14
15
16
17
18
19
20
21
22
23
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()が実行されていない場合は、未使用のタイマ/カウンタを探します。

25
26
27
28
29
30
31
32
// 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の値を代入します。現状、この値は、2しかとりません。

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

34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
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;
  }
}

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

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

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

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

74
75
  return _timer;
}

バージョン

Arduino AVR Boards 1.8.6

最終更新日

March 21, 2023

inserted by FC2 system