Can I use microseconds in the Tone function with Teensy 3.1?

Status
Not open for further replies.

Blitter

Member
I'd like to run a 38kHz tone on an output pin.
I know I can do it this way, but it isn't optimal:

tone(1, 38000); // Begin the tone
delayMicroseconds(300);
noTone(1); // manually stop it


How can I use microseconds using the tone function?

tone(1, 38000, ???); // Begin a tone which automatically stops after 300us
 
Does that function for you? Thread title suggests you are not sure - though text says it can.
How critical is the duration? The following may overrun if the code later in the loop() starts doing much. In which case you could stop looping in advance and then delay the remaining time.

You could record the time when you start the tone and then on each loop see if the desired time has elapsed and either noTone(1) then or continue until is has elapsed.

File / Examples / Digital / BlinkWithoutDelay : shows an example of this, you'd have to adjust that code - taking out the LED toggle and if you don't want it to come on as soon as it goes off add code to not trigger until whatever condition is met, or just recode it to swap the tone for the output and the tone would cycle on>off and repeat.
 
Last edited:
Duration is critical, and the 300us pulse is followed by 4 others.

I am looking to pass microseconds into the tone function, but it accepts only millis:
Code:
tone(1, 38000, 300)
will give a 300 millisecond, 38kHz modulated pulse.

Is there a way to have it accept micros, or should I just live with a delayMicroseconds(300) between tone and noTone?
 
That 3rd Param is millis it seems.

If timing is critical and you have nothing else to do (in loop at the same time) then you should be able to (almost) trust that to work while you wait.

Any effort at a solution like BlinkWithoutDelay could have you doing something else when you should be stopping the tone, unless you build in a safe buffer period and then wait out the difference.

I have seen the delayMicroseconds() run over 1 or more ticks when I tried to record the micros() before and after calling it so you might look to test that for your interval. Test against 300 and see what you see - I just tested and typically 33 report differences of 302-304 out of 100 trials, with half again that many at 301 which could be rounding error and function call time. I added the second loop below that manually watches the time and it hits 301 (rarely 302) 1000 times in 592,210. I compiled for T_3.1 at 96 MHz optimized.

You should do your own while() loop watching the micros() tick away might be better, as long as you don't get a math error when micros() rolls over to zero - I'm confirming the code below now.

Code:
  ulong mStart, mStop;
  int jj = 0;
  for ( int ii = 0; ii < 100; ii++ ) {
    mStart = micros();
    delayMicroseconds( 300 );
    mStop = micros();
    if ( 301 < (mStop - mStart) ) {
      Serial.println(mStop - mStart);
      jj++;
    }
  }
  Serial.println(jj);

  jj = 0;
  for ( int ii = 0; ii < 100; ii++ ) {
    mStart = micros();
    while ( 300 + mStart > micros() ) ;
    mStop = micros();
    if ( 300 != (mStop - mStart) ) {
      Serial.println(mStop - mStart);
      jj++;
    }
  }
  Serial.println(jj);
 
Thanks! I'll give it a shot.

The only thing that might happen is that I receive a pulse from on a different pin during my 38kHz modulated pulse.

That incoming pulse would take precedence, so I'll just check it between my outgoing pulse quintet. I'll use your timing routine to measure how long it take to measure the incoming pin, and deduct that time from my delay during (or between) emitted pulses.
 
That second loop is what you want then and in the EMPTY WHILE : "while ( 300 + mStart > micros() ) ;"

When needed you can monitor the INPUT from the other pin by expanding that. Checking that will get precedence and may over run the loop depending on what your test consists of? You'll have some coding to handle that pin and yet finish the current tone that would have been started as commented below. I can confirm that this timing loop BREAKS when the value of micros() rolls over as written.

Code:
    mStart = micros();
    // start 38kHz tone here
    while ( 300 + mStart > micros() ) {
        // Monitor for incoming tone here
        // wait for time to expire on the 38kHz tone
    }
    // stop 38kHz tone here
    mStop = micros();
    if ( 300 != (mStop - mStart) ) {
      Serial.println(mStop - mStart);
      jj++;
    }
 
Last edited:
Awesome - glad it works!

It'll screw up when micros rolls over as above - the "ulong mStart, mStop;" needs to be "uint mStart, mStop;"

Actually it screws up 300 us before the rollover with ulong until after it rolls over.

Rollover will hit every : 1 hour, 11 minutes, 34.967295 seconds

Even then going to uint's I saw one fail right at the boundary and the 300 interval timed out after 1us.

Paul posted once there is a right and simple way to have it work . . . not sure what the deal is . . .
 
You might look at the IRremote library. On Teensy 3.1, it uses the CMT (carrier modulator timer) to generate 38 kHz with modulation. Even if the library itself isn't what you need, perhaps you could pull out the CMT stuff from its source code and use it?

You could also help yourself to the tone() code, in hardware/teensy/avr/cores/teensy3/Tone.cpp. It uses the IntervalTimer object to generate interrupts for every time the waveform needs to change. You could edit tone() to calculate "tone_toggle_count" based on some different formula. The rest of the code should work as-is, to create exactly the number of 38 kHz pulses you want.
 
Cloning the tone code could allow working with microseconds - and doing the interrupts directly too.

Also maybe picking up the incoming tone with a pin change/high interrupt?

Paul - regarding the above process to the answer:
> In Msg #4: is the 'noise' of 1-4(6) microseconds overrun on the "delayMicroseconds( 300 );" just from call/return overhead? [TD_1.23b1 still in use here]
> where did I read about your proper way to 'compare times' where rollover happens? When I ran (a version like) the above code shown below it bailed out once just before rollover after 1us.

Code:
  uint mStart, mStop;
  while (micros() > 800)
  {
    mStart = micros();
    while ( 300 + mStart > micros() ) ;
    if ( 300 >= (mStop - mStart) ) Serial.println(mStop - mStart);
    mStop = micros();
  }
 
> In Msg #4: is the 'noise' of 1-4(6) microseconds overrun on the "delayMicroseconds( 300 );" just from call/return overhead?

No. Teensyduino's implementation of delayMicroseconds() uses inline functions, so there's no call/return overhead.

But you can easily get extra delay if the SysTick (for millis) interrupt happens, or USB interrupts, or serial or other libraries you may be using.


> where did I read about your proper way to 'compare times' where rollover happens?

Handling unsigned integer overflow for comparisons has been discussed many, many times, both here and on Arduino's forum, and on numerous other websites. I'm going to refrain from rehashing that all over again here.

Generally, I recommend using elaspedMillis and elapsedMicros. Those special variables are simpler to understand and use. Their use always results in efficient, inline code. Best of all, elaspedMillis and elapsedMicros always properly handle the internal 32 bit rollover properly. They always give correct results, up to 2^31 - 1, even when the internal millis() and micros() overflow, even when you write to them or do math with them.
 
recommend using elaspedMillis and elapsedMicros

Indeed I have these both working in a test case, I still see overruns up to 306 for requested 300 elapsedMicros, but they are few about 5 in 3 million tries? I set up a case to count to an array overnight.

I haven't found a source for the rollover in timing yet, maybe it'll be a good example on wiki.pjrc.com.
 
Bingo. Works like a champ. Thanks!

This code should work better without timer wrap issues:

Code:
  elapsedMicros EUwait;
  uint mStart, mStop = 0;
  mStop = micros();
  while (micros() < 1000000600L)
  {
    EUwait = 0;
    mStart = micros();
    while ( 300 > EUwait ) {
      // Do what you must while waiting
    }
    mStop = micros();
    if ( 304 < (mStop - mStart) ) {
      Serial.print( "@_@" );
      Serial.print(mStop - mStart);
    }
  }
 
systick interrupts (1KHz) might/will affect things?

Yeah - Paul suggested that above - minimal USB and no other libraries in use, just the code in #14 gave results like these:

iterations=3322035
Hitting 301 usecs between stop&start:: 192956
Hitting 302 usecs between stop&start:: 4307
Hitting 303 usecs between stop&start:: 119
Hitting 304 usecs between stop&start:: 7
Hitting 305 usecs between stop&start:: 4
None hit over 305 this time, but any more code in the while() and it does hit 306 11 times per 3.3 million.

I put this line in the loop with a bad++; : " if ( !( bad % 26000 )) delay (1);" to force periodic errors to know it was running because it would delay I millisecond and get detected.
 
Looked at the library code behind: elapsedMicros -it is cool how it works! I assumed it would add overhead - but it doesn't seem to on average, though elapsedMicros does miss slightly more often by a few more ticks.

I took one of my loops comparing waiting for 300 elapsedMicros to end and waiting for comparing to 300 micros() - and they both completed the same avg number of loops - with just a Cnt++; in the while loop.

I just pulled the zip from here to see the code: http://playground.arduino.cc/Code/ElapsedMillis
 
Status
Not open for further replies.
Back
Top