Go Down

Topic: MEGA 2560 Tone conflict width PWM (Read 7772 times) previous topic - next topic

sldiesel

Hi
I have a problem as i want to have a Tone out of the Arduino MEGA 2560.
It has to go from around 10 HZ to 350 HZ
But it conflits width PWM on PIN 10 and tone on pin 46. (The tone jumps out of control when PWM is turned on and off).

Well then i tryed to change the code in TONE.CPP to use timer "5"

#define USE_TIMER[font=Verdana]5[/font] //Here i wrote 5 instedt of 2
const uint8_t PROGMEM tone_pin_to_timer_PGM = { [font=Verdana]5[/font] /, 3, 4, 5, 1, 0 / }; //Here i wrote 5 instedt of 2

That made it work but it now and then falls out, no tone for around 100 mS (this only happens when i smooth change the tone up and down from 10 to 350 HZ)

Any idea would be greatly appreciated.

Ole

Coding Badly


~32Hz is the minimum frequency available from tone.

sldiesel

Hi
Works fine below 32 HZ, as it is a 16 bit timer (but i am not sure i know what i am talking about.)

But the Code below gives the same problem fallout in 100 mS

int X = 0;

void setup() {
}

void loop() {
  X = analogRead(14)-300;
  if( X < 33 ){ X = 33;}
  if( X > 300 ){ X = 300;}
  tone(46 , X);
}

nickgammon


But the Code below gives the same problem fallout in 100 mS


Can you explain what you mean by "fallout in 100 mS"?
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

Coding Badly


Try this...

Code: [Select]
int X = 0;
Int Xp;

void setup() {
}

void loop() {
  X = analogRead(14)-300;
  if( X < 33 ){ X = 33;}
  if( X > 300 ){ X = 300;}
  if ( X != Xp )
  {
    tone(46 , X);
    Xp = X;
  }
}

sldiesel



But the Code below gives the same problem fallout in 100 mS


Can you explain what you mean by "fallout in 100 mS"?

Yes there is no activity on the tone pin at all for a short time.
tryed to record it on the shitty small scope :-)
http://www.youtube.com/watch?v=PhpKg9Szzzo
But it is more easy to see on the speedo i am trying to drive.

sldiesel



Try this...

Code: [Select]
int X = 0;
Int Xp;

void setup() {
}

void loop() {
  X = analogRead(14)-300;
  if( X < 33 ){ X = 33;}
  if( X > 300 ){ X = 300;}
  if ( X != Xp )
  {
    tone(46 , X);
    Xp = X;
  }
}


Thanks for the help, just tryed you code, No change still no activity for short time :-(
But remember i am now using timer5
http://www.youtube.com/watch?v=PhpKg9Szzzo

nickgammon

Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

sldiesel


nickgammon

I can reproduce your problem, just trying to work out why. :)
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

sldiesel


I can reproduce your problem, just trying to work out why. :)

You don't know how glad i am to hear that ;-)

nickgammon

I think I know roughly what is going wrong, I can't put my finger on the fix. The timer is a 16-bit timer, so if it happens to overshoot the count (which would tend to happen when the frequency decreases) then it counts all the way up to 65535 before matching. This is confirmed by:

Code: [Select]

62.5e-9 * 65536 * 64 = 0.262144


So you might expect a gap of around 262 mS if this happens. And indeed I am measuring pauses along those lines.

For example, if it was previously counting up to 500, and it had reached 450, but you change the count to 300, it has already overshot 300, so it counts all the way up to 65535, giving a much longer gap.

So far my attempts to fix it have been spectacularly unsuccessful. Some of the gaps are gone, but the ones that remain blow out to 1.5 seconds! There must be more to this than meets the eye. :)

The theory is to do this, while the timer is stopped:

Code: [Select]

      TCNT5H = 0;
      TCNT5L = 0;


That should reset the counter so it doesn't overshoot.
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

sldiesel


I think I know roughly what is going wrong, I can't put my finger on the fix. The timer is a 16-bit timer, so if it happens to overshoot the count (which would tend to happen when the frequency decreases) then it counts all the way up to 65535 before matching. This is confirmed by:

Code: [Select]

62.5e-9 * 65536 * 64 = 0.262144


So you might expect a gap of around 262 mS if this happens. And indeed I am measuring pauses along those lines.

For example, if it was previously counting up to 500, and it had reached 450, but you change the count to 300, it has already overshot 300, so it counts all the way up to 65535, giving a much longer gap.

So far my attempts to fix it have been spectacularly unsuccessful. Some of the gaps are gone, but the ones that remain blow out to 1.5 seconds! There must be more to this than meets the eye. :)

The theory is to do this, while the timer is stopped:

Code: [Select]

      TCNT5H = 0;
      TCNT5L = 0;


That should reset the counter so it doesn't overshoot.


Vow You are a HERO ;-)
I put them in the Code and run those for every 200 mS,
And it works Great
Thanks ALOT for your help :-) :-)

nickgammon

Here's my more general solution. :)

Use the hardware timer to output the tone. You'll get a better result anyway, because it isn't affected by jitter from interrupts.

Code: [Select]

int Hz = 0;
int prevHz;

void setup()
  {
  pinMode (46, OUTPUT);  // OC5A
  }

void myTone (unsigned int wantedHz)
  {
  // allow for prescaler of 64, and it takes two toggles for one "cycle"
  unsigned int ocr = F_CPU / wantedHz / 64 / 2;
 
  // stop timer
  TCCR5A = 0;
  TCCR5B = 0;
 
  // reset counter
  TCNT5H = 0;
  TCNT5L = 0;
 
  // what to count up to
  OCR5AH = highByte (ocr);
  OCR5AL = lowByte (ocr);
 
  TCCR5A = _BV (COM5A0);             // toggle output pin: OC5A
  TCCR5B = _BV (WGM52) |             // CTC
           _BV (CS50) | _BV (CS51);  // prescaler of 64
   
  }  // end of myTone
 
void loop()
  {
  Hz = analogRead (14) - 300;
 
  if( Hz < 33 )
    Hz = 33;
  if( Hz > 300 )
    Hz = 300;

  if ( abs (Hz - prevHz) > 2 )
    {
    myTone (Hz);
    prevHz = Hz;
    }  // end of frequency change
   
  }  // end ofloop


This doesn't use the Tone library at all. It just manipulates the timer (5) directly, to output the wanted frequency.
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

sldiesel


Here's my more general solution. :)

Use the hardware timer to output the tone. You'll get a better result anyway, because it isn't affected by jitter from interrupts.

Code: [Select]

int Hz = 0;
int prevHz;

void setup()
  {
  pinMode (46, OUTPUT);  // OC5A
  }

void myTone (unsigned int wantedHz)
  {
  // allow for prescaler of 64, and it takes two toggles for one "cycle"
  unsigned int ocr = F_CPU / wantedHz / 64 / 2;
 
  // stop timer
  TCCR5A = 0;
  TCCR5B = 0;
 
  // reset counter
  TCNT5H = 0;
  TCNT5L = 0;
 
  // what to count up to
  OCR5AH = highByte (ocr);
  OCR5AL = lowByte (ocr);
 
  TCCR5A = _BV (COM5A0);             // toggle output pin: OC5A
  TCCR5B = _BV (WGM52) |             // CTC
           _BV (CS50) | _BV (CS51);  // prescaler of 64
   
  }  // end of myTone
 
void loop()
  {
  Hz = analogRead (14) - 300;
 
  if( Hz < 33 )
    Hz = 33;
  if( Hz > 300 )
    Hz = 300;

  if ( abs (Hz - prevHz) > 2 )
    {
    myTone (Hz);
    prevHz = Hz;
    }  // end of frequency change
   
  }  // end ofloop


This doesn't use the Tone library at all. It just manipulates the timer (5) directly, to output the wanted frequency.


Cool i was thinking of using the timer directly, but my programming skills, are not Close to yours :-)
I will try to test this tomorrow.
Its getting late here on the other side of the Earth.
I will get back width result

Go Up