Tone duration innaccurate

Is it just me or is the duration of the tone() command 1/5 of the length it is supposed to be? I am using it to send signals through an IR LED and I need the duration to be very precise. As a test, I told it to pulse for 10 seconds but it only pulsed for 2. I tried 50 seconds and it pulsed for 10. Is anyone else having this problem? The tone command documentation (http://arduino.cc/en/Reference/Tone) says that the duration should be in milliseconds, but that doesn’t seem to be working for me.

Here is my code:

#define TRUE 1
#define FALSE 0

int irPin = 9;
int sensorPin = 2;
int hitLed = 13;

unsigned int irFreq = 40000;  // Transmission frequency of IR LED in Hz. This is dependant on the IR sensors used

boolean b;

void setup() {
  Serial.begin(9600);

  pinMode(irPin, OUTPUT);
  pinMode(hitLed, OUTPUT);
  pinMode(sensorPin, INPUT);

  tone(irPin, irFreq, 10000);

  b = FALSE;

  delay(5);   // Ensures transmission has started before reading sensor
}

void loop() {
  int value = digitalRead(sensorPin);

  if (value == LOW) {             // if the sensor sees the IR LED, blink status light
    digitalWrite(hitLed, HIGH);
    delay(500);
    digitalWrite(hitLed, LOW);
    delay(500);
  } else {
    if (b == FALSE) {
      Serial.println(millis());
      b = TRUE;
    }
  }
}

I’m pretty sure that the frequency is off too. When I just set an LED to 1Hz (which should be on for half a second, off for half a second) it blinks faster than I can see it, but when I wave the Arduino I see the blink trail. Strangely the IR sensor still works when I set the IR pin to 40 KHz, but in a noisy environment, I would like to have the transmission frequency as accurate as possible.

Does anyone have any suggestions?

Maybe the F_CPU given to the compiler is wrong? Are timings using millis() also off?

millis() seems accurate. I set my code to print the time to serial after tone completes (using my IR sensor) and a stop watch and IR camera to manually time it and they come out to the same time (plus some error in my reaction time).

What IR sensor are you using? What are its connections to the ATmega?.

Regards,

Dave

I'm using a Vishay's TSOP4840 and it is connected to pin 2 using the suggested resister and capacitor from the sensor's data sheet.

tsop 4840

I haven't used that particular device, so I have no way of testing with your setup, but I have used devices (from another manufacturer) with similar characteristics. See Footnote.

Here's what I observed.

1. The device is designed to work with sequences of bursts with carrier-on and carrier-off times on the order of a millisecond or so during communications, not tens (or hundreds or thousands) of milliseconds.

2. After carrier-off times of more than a few milliseconds, the response to the very first carrier pulse is erratic. So I needed to "prime the pump" with some carrier-on time for a couple of milliseconds before starting data transfer. (The internal circuitry needs a little time to lock to the carrier. The amount of time depends on how long the carrier has been off.)

3. The internal filtering circuitry keeps the device from working with long-duration carrier-on times. (In other words the output goes high again if you keep the carrier on for longer times.) The data sheet says:,

"Some examples of disturbance signals which are suppressed are...Continuous signals at any frequency"

Bottom line: The data sheet does not say what the maximum carrier-on signal time is, but that could be a show-stopper for using this device with any method that doesn't have fine enough granularity in the carrier-on time characteristics.

If you want the convenience of using tone() to create the carrier but need carrier durations of less than a millisecond, maybe you can try something like the following. I haven't tested this, and I don't have a lot of confidence that it will work without some timing adjustments, but it's something to try):

  1. Call tone*(pin,freq)* to start the carrier.
  2. Call delayMicroseconds(whatever) to wait until it's time to stop.
  3. Call noTone(pin) to turn off the carrier.

Or...

Maybe you can look for other infrared remote projects that people use to transmit TV remote signals and try whatever methods that seem to be successful..

Maybe you already have done this, and are just trying the long times for testing. An appropriate test signal would be something like a burst of a dozen or so 600 usec on-time, 600 usec off-time sequences followed by a rest time of a few tens of milliseconds and then repeat the bursts again.

Here may be the reason that the timing of the tone duration can be off: The tone() stuff has been tested pretty thoroughly at audio frequencies (a few hundred Hz). I don't have perfect pitch and I haven't put a melody on the scope, but it sounds "about right." Long duration notes don't seem to be a problem.

However...

The tone() function is capable of generating 40 kHz square waves alright, but that requires a timer2 interrupt every 12.5 microseconds. The context switch and the time to execute the instructions is (probably) on the order of a two or three microseconds. This may be too much overhead to keep timing accuracy, even with delayMicroseconds()

The real bottom line: Maybe using the tone() function is not appropriate for 40 kHz carrier generation for sub-millisecond carrier on-times (without some heuristically obtained data to tweak the actual duration).

Regards,

Dave

Footnote: My project did not involve TV remote signals, but used a proprietary data format sequence with timing characteristics compatible with the various standards for which these modules were designed.

A bit time is 1800 us.

"Zero" is 1200 usec carrier-on and 600 usec carrier-off.

"One" is 600 usec carrier-on and 1200 usec carrier-off.

The module output is low when it detects the presence of a carrier and high when there is no carrier, so I sample the module output 900 usec after I see the output go low. Then I wait for 400-500 more usec before starting to look for the output going low again (for the start of the next carrier-on time).

The data bytes are sent the same way a UART does the deed: One start bit (zero), eight data bits, one stop bit (one)

Thanks for the tips, dave. I will do some more testing shortly. I am working on making a laser tag system and intend to use the Milestag protocol (http://www.lasertagparts.com/mtformat.htm) which does use very short bursts similar to your project. These long bursts were just to test the accuracy of tone.

The reason I want to use tone is for its non-blocking properties. In a lasertag game, being able to receive a hit while also firing a shot is ideal. Using tone would give me that flexibility. I am also currently stuck with only one microprocessor, so I need non-blocking for testing until I can get another.

If the PWM pins were set to the carrier frequency, that would probably work better, but I'm not sure it is possible to change that frequency to 40KHz. I think I read that it can be changed to a value related to the clock frequency (1/2, 1/4, 1/8, etc) but nothing in between. If it can be changed to 40KHz, I would appreciate a link to instructions.

Once I have done some testing at other frequencies, I will post my findings. Thanks again!