toneBegin()

Abstract

The toneBegin() manages the relation of pin and timer which is used by tone(), and sets ther timer. In the current implementation, timer2_pin port is used to set timer output port and timer2_pin_mask is used to set timer output bit. These variables are used in an intterrupt handler.

Source Code

The toneBegin() is defined in hardware/arduino/avr/cores/arduino/Tone.cpp as below. Only the souce code for Arduino UNO is quoted. The original source code supports many tips using #if’s.

 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;
}

The timerX_pin_port holds the port(see digitalWrite()) used by tone().

The timerX_pin_mask holds the bit in the port corresponds to the digital pin.

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;

The AVAILABLE_TONE_PINS is set to 1. This is the number of pins that can be used at the same time.

The tone_pin_to_timer_PGM[] is an array that holds timer/counter. Currently only “2” is defined.

The tone_pins[] shows pins which is used by timer/counter, which is related to tone_pin_to_timer_PGM[]. 255 means unused.

 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 */ };

The input is _pin and its type is int8_t. The return type is int8_t.

If the tone() is already executed, it returns the current timer/counter. This means it changes the current pin settings.

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);
    }
  }

The pgm_read_byte() is a macro to read one byte from PROGMEM. As the tone_pin_to_timer_PGM is an array, tone_pin_to_timer_PGM+i means the i-th element of the array.

If the tone() is not executed on the specified pin, it searchs unused timer/counter.

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;
  }
}

If one of the element of the tone_pins[] is 255, sets the pin number to tone_pins[] and sets the tone_pin_to_timer_PGM+i to _timer. Currently the value is fixed to 2.

If the _timer is not -1, timerX_pin_port and timerX_pin_mask are set.

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;
  }
}

The TCCR is a register to control timer/counter. In this case, CTC mode is set.

See digitalWrite() for digitalPinToPort(), portOutputRegister(), digitalPinToBitMask().

The port and the bit inside the port are set to timerX_pin_port and timerX_pin_mask.

Finally the timer number is returned. If there is no available timer, the _timer is -1.

74
75
  return _timer;
}

Version

Arduino AVR Boards 1.8.6

License

 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
/*
  Copyright (c) 2019 garretlab.
  Added source code explanation by garretlab.
*/

/* Tone.cpp

  A Tone Generator Library

  Written by Brett Hagman

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

Version Modified By Date     Comments
------- ----------- -------- --------
0001    B Hagman    09/08/02 Initial coding
0002    B Hagman    09/08/18 Multiple pins
0003    B Hagman    09/08/18 Moved initialization from constructor to begin()
0004    B Hagman    09/09/26 Fixed problems with ATmega8
0005    B Hagman    09/11/23 Scanned prescalars for best fit on 8 bit timers
                    09/11/25 Changed pin toggle method to XOR
                    09/11/25 Fixed timer0 from being excluded
0006    D Mellis    09/12/29 Replaced objects with functions
0007    M Sproul    10/08/29 Changed #ifdefs from cpu to register
0008    S Kanemoto  12/06/22 Fixed for Leonardo by @maris_HY
0009    J Reucker   15/04/10 Issue #292 Fixed problems with ATmega8 (thanks to Pete62)
0010    jipp        15/04/13 added additional define check #2923
*************************************************/

Last Update

July 31, 2025

inserted by FC2 system