Go Down

Topic: Arduino Due and tone() (Read 11218 times) previous topic - next topic

JacekPintera

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?

alvesjc

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. ;)
www.aqualedsource.com

mantoui

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

Code: [Select]

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

cdan

#3
May 26, 2013, 12:39 pm Last Edit: May 26, 2013, 12:42 pm by cdan Reason: 1
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.

cdan

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:
Code: [Select]

  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.

ninja2

#5
Aug 28, 2016, 03:05 am Last Edit: Aug 28, 2016, 03:09 am by ninja2
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

GregorioW

#6
Aug 28, 2016, 12:39 pm Last Edit: Aug 28, 2016, 12:50 pm by GregorioW

ninja2

#7
Aug 29, 2016, 12:32 am Last Edit: Aug 29, 2016, 12:35 am by ninja2
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?

ard_newbie


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 :

//http://ko7m.blogspot.fr/2015/01/arduino-due-timers-part-1.html

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

ninja2

#9
Aug 29, 2016, 09:33 am Last Edit: Aug 29, 2016, 09:55 am by ninja2
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).


MartinL

#10
Aug 29, 2016, 10:53 am Last Edit: Aug 29, 2016, 10:54 am by MartinL
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:

Code: [Select]
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;


ninja2

#11
Aug 29, 2016, 01:32 pm Last Edit: Aug 29, 2016, 01:35 pm by ninja2
This best I could find so far for some detail on the functions, in libsam: tc.h

ninja2

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.

Code: [Select]
/* 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
  }*/

 


GregorioW

Hi 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:
Code: [Select]
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 :)

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

ninja2

#14
Sep 04, 2016, 11:53 pm Last Edit: Sep 04, 2016, 11:55 pm by ninja2
Thanks Gregorio, again  :) (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.

Go Up
 


Please enter a valid email to subscribe

Confirm your email address

We need to confirm your email address.
To complete the subscription, please click the link in the email we just sent you.

Thank you for subscribing!

Arduino
via Egeo 16
Torino, 10131
Italy