Arduino Due and tone()

Hi,

I wanted to ask if standard tone function works on Arduino Due board? I bought Arduiono week ago, and built all the simple projects with LEDs etc. and now I wanted to use buzzer in project. I connected the circuit like in http://arduino.cc/en/Tutorial/Tone but when I choose upload I get the message "error: 'tone' was not declared in this scope".

When I change the board to AVR (like Arduino Due) it compiles, so how can I use buzzer with my Arduino Due?

Hi!.

Has far has I can see, tone library is still disabled. I think you have to wait in case you don't know how to mess with timers. :wink:

here's a monolithic proof-of-concept of tone()

/*
  Melody on pin 8 of DUE
 
 http://arduino.cc/en/Tutorial/Tone
 
 */


// notes in the melody:
int melody[] = { 262,196, 196, 220, 196, 0,  247, 262
  /*NOTE_C4, NOTE_G3,NOTE_G3, NOTE_A3, NOTE_G3,0, NOTE_B3, NOTE_C4*/ };

// note durations: 4 = quarter note, 8 = eighth note, etc.:
int noteDurations[] = {
  4, 8, 8, 4,4,4,4,4 };

void setup() {
}

void loop() {
  // iterate over the notes of the melody:

  for (int thisNote = 0; thisNote < 8; thisNote++) {

    // to calculate the note duration, take one second 
    // divided by the note type.
    //e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc.
    int noteDuration = 1000/noteDurations[thisNote];
    tone(8, melody[thisNote],noteDuration);

    // to distinguish the notes, set a minimum time between them.
    // the note's duration + 30% seems to work well:
    int pauseBetweenNotes = noteDuration * 1.30;
    delay(pauseBetweenNotes);
    // stop the tone playing:
    noTone(8);
  }
  delay(3000);

}


/*
 Tone generator
 v1  use timer, and toggle any digital pin in ISR
   funky duration from arduino version
   TODO use FindMckDivisor?
   timer selected will preclude using associated pins for PWM etc.
    could also do timer/pwm hardware toggle where caller controls duration
*/


// timers TC0 TC1 TC2   channels 0-2 ids 0-2  3-5  6-8     AB 0 1
// use TC1 channel 0 
#define TONE_TIMER TC1
#define TONE_CHNL 0
#define TONE_IRQ TC3_IRQn

// TIMER_CLOCK4   84MHz/128 with 16 bit counter give 10 Hz to 656KHz
//  piano 27Hz to 4KHz

static uint8_t pinEnabled[PINS_COUNT];
static uint8_t TCChanEnabled = 0;
static boolean pin_state = false ;
static Tc *chTC = TONE_TIMER;
static uint32_t chNo = TONE_CHNL;

volatile static int32_t toggle_count;
static uint32_t tone_pin;

// frequency (in hertz) and duration (in milliseconds).

void tone(uint32_t ulPin, uint32_t frequency, int32_t duration)
{
		const uint32_t rc = VARIANT_MCK / 256 / frequency; 
		tone_pin = ulPin;
		toggle_count = 0;  // strange  wipe out previous duration
		if (duration > 0 ) toggle_count = 2 * frequency * duration / 1000;
		 else toggle_count = -1;

		if (!TCChanEnabled) {
 			pmc_set_writeprotect(false);
			pmc_enable_periph_clk((uint32_t)TONE_IRQ);
			TC_Configure(chTC, chNo,
				TC_CMR_TCCLKS_TIMER_CLOCK4 |
				TC_CMR_WAVE |         // Waveform mode
				TC_CMR_WAVSEL_UP_RC ); // Counter running up and reset when equals to RC
	
			chTC->TC_CHANNEL[chNo].TC_IER=TC_IER_CPCS;  // RC compare interrupt
			chTC->TC_CHANNEL[chNo].TC_IDR=~TC_IER_CPCS;
			 NVIC_EnableIRQ(TONE_IRQ);
                         TCChanEnabled = 1;
		}
		if (!pinEnabled[ulPin]) {
			pinMode(ulPin, OUTPUT);
			pinEnabled[ulPin] = 1;
		}
		TC_Stop(chTC, chNo);
                TC_SetRC(chTC, chNo, rc);    // set frequency
		TC_Start(chTC, chNo);
}

void noTone(uint32_t ulPin)
{
	TC_Stop(chTC, chNo);  // stop timer
	digitalWrite(ulPin,LOW);  // no signal on pin
}

// timer ISR  TC1 ch 0
void TC3_Handler ( void ) {
	TC_GetStatus(TC1, 0);
	if (toggle_count != 0){
		// toggle pin  TODO  better
		digitalWrite(tone_pin,pin_state= !pin_state);
		if (toggle_count > 0) toggle_count--;
	} else {
		noTone(tone_pin);
	}
}

I have tried to change your code and configure TC0/Ch0 to use the TIOA0 pin as output for the square wave.
I have a piezo connected to Arduino PIN 2 (which, according to the mapping in http://arduino.cc/en/Hacking/PinMappingSAM3X should be PB25).

It seems that I am doing somethig wrong, since I get no output from pin 2.
If I enable the TC0_IRQn interrupt then I can see my handler being called, which means that at least the timer was configured correctly.

Can you please have a look over my code? I must have done something wrong.
http://paste.ubuntu.com/5703102/

I want to eliminate the need for interrupts and manual bit banging on pin 2.

Best regards,
Dan.

I'll answer my own question:
In order to get your square wave out on TIOA you have to set the appropriate bits on the TC Channel Mode Register:

  TC_Configure(chTC, chNo,
	       TC_CMR_TCCLKS_TIMER_CLOCK4 |
	       TC_CMR_WAVE |         // Waveform mode
	       TC_CMR_WAVSEL_UP_RC | // Counter running up and reset when equals to RC
	       TC_CMR_ACPA_SET |     // RA compare sets TIOA
	       TC_CMR_ACPC_CLEAR );  // RC compare clears TIOA

This solution is much accurate than using interrupts but it has the drawback that you have to use the pin associated with the TIOA0 signal (on arduino DUE this is pin 2).

Best regards,
Dan.

mantoiu, cdan, or anyone...

where can I find the documentation that details all the Timer/Counter programming names (like TC_CMR_WAVSEL_UP_RC) and functions (like TC_SetRC)

I have looked for these in the Atmel Software Framework, and the datahseet for ARM Cortex-M3 but no luck so far.

TIA

Hi ninja2,

Check out the Atmel components:
https://github.com/arduino/Arduino/tree/master/hardware/arduino/sam/system/CMSIS/Device/ATMEL/sam3xa/include/component
and the libsam:
https://github.com/arduino/Arduino/tree/master/hardware/arduino/sam/system/libsam

Or are you looking for something more detailed?

Have fun!

GregorioW

Thanks GregorioW. Those links answer some of my queries, but there's too much detail !

For example in components_tc.h I can now see that TC_CMR_WAVSEL_UP_RC = 0x2u << 13 (woohoo!) but what I really need is a document that provides an explanation, or a bridge, between the datasheet and all these components. Also I couldn't find anything for functions, like TC_SetRC?

These may be somewhere in those GitHub repositories, but I'm not that familiar with finding my way around there yet.

Maybe I'm after a SAM programmers guide?

If you look at the TC channel Mode register (datasheet 36.7.3) you will see why TC_CMR_WAVSEL_UP_RC equals 2<< 13. But you don't need to use magic numbers, it is much easier to use TC_CMR_WAVSEL_UP_RC for UP mode with automatic trigger on RC Compare.

And maybe this tutorial could help :

//KO7M - Ham Radio Blog: Arduino Due Timers (Part 1)

Apart from this, you can read the "definitive guide to ARM CORTEX M3 processors" from Joseph YIU.

They look interesting and I'll certainly have a look at those ...

I'm across the datasheet already, but it doesn't detail or explain the principles and how all names like TC_CMR_WAVSEL_UP_RC are built up, nor does it list all available functions, like TC_Configure(), TC_Start(), TC_GetStatus() and TC_SetRC(). From the datasheet and the code comments I have I know the code I posted uses waveform mode and compares to the value loaded into Register C ... but this is just one case, not all.

So my question is not how the M3 Timer/Counters work, but how names like TC_CMR_WAVSEL_UP_RC are built up and what all the available TC functions are. If the answer is I have to keep digging, so be it. But I'm still hoping for a programmers guide (although it may be hidden deep in Yiu's book).

The register and bitfield names for the SAM3X8E on the Due are Atmel files located (on my Windows machine) at:

C:\Users\Computer\AppData\Local\Arduino15\packages\arduino\tools\CMSIS\4.0.0-Atmel\Device\ATMEL\sam3xa\include...

In this sub-directory you have two further directories "component" and "instance".

"Component" contains the files that specify the microcontroller registers' structure and bitfield names. For example the file "tc.h" is for the Due's timers.

"Instance" contains the files that define the register locations of each peripheral instance. For example the files "tc0.h", "tc1.h" and "tc2.h" specify the register locations for each timer peripheral: 0, 1 and 2.

You don't need any includes in your Arduino sketch, you can just call on the register and bitfield names directly, for example to set the bits and bitfields in timer 0 waveform mode register:

REG_TC0_CMR0 = TC_CMR_BCPC_SET | TC_CMR_BCPB_CLEAR | TC_CMR_ACPA_CLEAR | TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | TC_CMR_EEVT_XC0 | TC_CMR_TCCLKS_TIMER_CLOCK1;

This best I could find so far for some detail on the functions, in libsam: tc.h

If,like me, you just need your trusty piezo buzzer to beep using the Due like it used to on Uno or Mega ... then you may find this sketch useful. It's a simple stripped down version of the melody example posted earlier, with a few clues and clarifications I picked up along the way.

/* Tone generation for DUE
 see http://arduino.cc/forum/index.php/topic,136500.msg1029238.html#msg1029238 and 
 http://asf.atmel.com/docs/latest/sam3a/html/group__sam__drivers__pmc__group.html and
 http://asf.atmel.com/docs/latest/sam.drivers.usart.usart_synchronous_example.sam3u_ek/html/sam_pio_quickstart.html
 and http://ko7m.blogspot.com.au/2015/01/arduino-due-timers-part-1.html
 
 The Atmel Power Management Controller (pmc) optimizes power consumption by controlling
 all system and user peripheral clocks, including enabling/disabling clock inputs to 
 many peripherals and the Cortex-M Processor.
 VARIANT_MCK = 84000000 (Master ClocK freq) 

 Table showing relationship between the Timer/Counter, it's channels, the IRQ to use, what 
 IRQ function must be called and the power management ID for that peripheral. 

  TC Ch NVIC_IRQ IRQ_handler PMC_id
  ---------------------------------
  TC0 0 TC0_IRQn TC0_Handler ID_TC0
  TC0 1 TC1_IRQn TC1_Handler ID_TC1
  TC0 2 TC2_IRQn TC2_Handler ID_TC2
  TC1 0 TC3_IRQn TC3_Handler ID_TC3 << using this one below
  TC1 1 TC4_IRQn TC4_Handler ID_TC4
  TC1 2 TC5_IRQn TC5_Handler ID_TC5
  TC2 0 TC6_IRQn TC6_Handler ID_TC6
  TC2 1 TC7_IRQn TC7_Handler ID_TC7
  TC2 2 TC8_IRQn TC8_Handler ID_TC8 
  */

#define TONEpin    3                                 // piezo buzzer pin
#include <Streaming.h>

const int toneFreq = 4000;                           // tone starting value, Hz

void setup() {
  Serial.begin(115200);
  pinMode(TONEpin, OUTPUT);
  
  Serial << F("\n==== Simple Due tone example ====\n");
  beep();  delay(1000);                             // 1 beep
  beep2(); delay(1000);                             // 2
  beep3(); delay(1000);                             // 3
  beep4();                                          // 4
  Serial << F("done...\n");
  }

void loop() {}

void beep()  {tone(50);}
void beep2() {beep(); delay(150); beep();}
void beep3() {beep(); delay(150); beep(); delay(150); beep();}
void beep4() {beep(); delay(150); beep(); delay(150); beep(); delay(150); beep();}

volatile static int32_t toggles;                    // number of ups/downs in a tone burst 

void tone(int32_t duration){                        // duration in ms
  const uint32_t rcVal = VARIANT_MCK/256/toneFreq;  // target value for counter, before it resets (= 82 for 4kHz)
  toggles = 2*toneFreq*duration/1000;               // calculate no of waveform edges (rises/falls) for the tone burst
  setupTC(TC1,0,TC3_IRQn,toneFreq);                 // Start Timer/Counter 1, channel 0, interrupt, frequency
  }
  
void setupTC(Tc *tc, uint32_t channel, IRQn_Type irq, uint32_t freq){
  pmc_set_writeprotect(false);                       // disable write protection of timer registers
  pmc_enable_periph_clk((uint32_t) irq);             // enable clock / interrupt 
//pmc_enable_periph_clk((uint32_t) ID_TC3);          // alternate syntax, using PMC_id instead
  TC_Configure(tc, channel,            
               TC_CMR_TCCLKS_TIMER_CLOCK4 |          // TIMER_CLOCK4: MCK/128=656,250Hz. 16 bits so 656,250/65,536=~10Hz/bit
               TC_CMR_WAVE |                         // Waveform mode
               TC_CMR_WAVSEL_UP_RC );                // Counter running up and reset when = Register C value (rcVal)
  const uint32_t rcVal = VARIANT_MCK/256/freq;       // target value for counter, before it resets
//Serial << "rcVal: " << rcVal << " toggles: " << toggles << '\n'; 
//TC_SetRA(tc, channel, rcVal/2);                    // could also use Register A for 50% duty cycle square wave
  TC_SetRC(tc, channel, rcVal);
  TC_Start(tc, channel);
  (*tc).TC_CHANNEL[channel].TC_IER =  TC_IER_CPCS;   // IER: CPCS bit enables RC compare interrupt when set 
  (*tc).TC_CHANNEL[channel].TC_IDR = ~TC_IER_CPCS;   // IDR: clear CPCS bit = don't disable RC compare interrupt
  NVIC_EnableIRQ(irq);                               // Enable TC3_IRQn in the Nested Vector Interrupt Controller)
  }

void TC3_Handler(void){                              // timer ISR  TC1 ch 0
  TC_GetStatus(TC1,0);
  if (toggles != 0){
    digitalWrite(TONEpin,!digitalRead(TONEpin));     // invert the pin state (i.e toggle)
    if (toggles > 0) toggles--;
    } 
  //else noTone();                                   // seems superfluous ?
  }
/*
void noTone(){
  TC_Stop(tc,channel);                               // stop timer
  digitalWrite(TONEpin,LOW);                         // no signal on pin
  }*/

Hi ninja2,

ninja2:
I'm across the datasheet already, but it doesn't detail or explain the principles and how all names like TC_CMR_WAVSEL_UP_RC are built up, nor does it list all available functions, like TC_Configure(), TC_Start(), TC_GetStatus() and TC_SetRC(). From the datasheet and the code comments I have I know the code I posted uses waveform mode and compares to the value loaded into Register C ... but this is just one case, not all.

So my question is not how the M3 Timer/Counters work, but how names like TC_CMR_WAVSEL_UP_RC are built up and what all the available TC functions are. If the answer is I have to keep digging, so be it. But I'm still hoping for a programmers guide (although it may be hidden deep in Yiu's book).

As ard_newbie and MartinL wrote, in component_tc.h (and other *.h’s over there) you can find some definitions that help you use all the registers. In lines 58–69 you can see a Tc structure, which consists of few TcChannel members and few registers. In the comments at the end of the lines you can see which register is which, while generally the names match to those in the datasheet. (For example, TC_BCR member in Tc structure refers to the TC Block Control Register decribed in subsection 36.7.13 of the datasheet.) And as ard_newbie wrote, basically everything about the *.h file is to ‘translate’ easy-to-understand names to some numbers: memory adresses or bit values and positions.

So, if you wanted to set the RC in timer/counter 1, you would need:

tc->TC_CHANNEL[1].TC_RC = new_rc_value;

where tc is a pointer to a Tc structure instance.

And now have a look on line 226 of tc.c source file. That’s why you may prefer to use the SetRC() function :slight_smile:

You have all available functions listed in include/tc.h, while these are coded and documented in source/tc.c. I guess that once you carefully browse through the tc.c file (and confront everything with the datasheet or some examples), you won’t need any more guides.

Is this what you needed?

Have fun!

GregorioW

Thanks Gregorio, again :slight_smile: (and ard_newbie) knowing about Tc.c was the last bit of the puzzle. I'm still learning my way around GitHub, and the Arduino code base there and this thread has provided a good intro.

Hello Guys,

I have to get a tone out of my headphones. Just like a metronome. In the beginning I thought that I can use tone(), turns out like you all know, this function is not available for arduino due.

So I wanted to try the code above from ninja2, but it won't compile because something does not work with #include <Streaming.h>. Could someone give me a shore explanation for that? Or do you have a better idea to create the metronome with an arduino due and headphones?

Thanks!

yes my code is suitable for your project

to get it to compile you just need to either:

  1. download the streaming library (from Streaming | Arduiniana) and put it in the .../Sketches/libraries folder

or

  1. update all the lines containing << with conventional Serial.print commands

I just had a quick look at my example at post #12 and the commands using streaming.h are only in 2 places in setup(), so for option 2 above is easy: just comment the lib out and edit setup() to be like this:

//#include <Streaming.h>

void setup() {
  Serial.begin(115200);
  pinMode(TONEpin, OUTPUT);
  Serial.println("\n==== Simple Due tone example ====");
  //Serial << F("\n==== Simple Due tone example ====\n");
  beep();  delay(1000);                             // 1 beep
  beep2(); delay(1000);                             // 2
  beep3(); delay(1000);                             // 3
  beep4();                                          // 4
  Serial.println("done...");
  //Serial << F("done...\n");
  }

just out of interest, why don't the Arduino folks provide a tone lib for the Due, too?

1 Like

Ok I will try that on Monday at work! Thank you for that.

Do you know whether I have to be careful about frying my earphones? Or will this code definitely work for headphones?