Playing with Arduino
A page to record my playing with Arduino
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 1.8.13

Last Update

June 19, 2020

inserted by FC2 system