DelayMicroseconds w/ digitalWrite timing accuracy

I've got a Arduino Pro Mini 328 - 5V/16MHz and a very simple IR led that I need to send a time based code through. This is very similar to a simple TV remote.

The code standard can be found here: https://www.lt2portal.mil/tabid/82/ABPageId/ABIcdTree/Default.aspx or by doing a search for PMT 90-S002M.

Basically, the pulses of IR come in 20.83 microsecond bursts. There are "bins" that these pulses occur in. There are 16 bins per "time slot" and 11 time slots that make up a "word". The Word time base clock rate is 48 KHz ± 0.015%. Each bin is 20.83us and each time slot is 333.3us and each word is 3.66ms. Allowing for that +0.015% error results in a 21 microsecond bin.

I'm very confident in the pattern I need to generate. I believe the board isn't doing a good job of timing it all out. I'm using a brute force approach of doing a digitalWrite(IR, ON); delayMicroseconds(21); digitalWrite(IR, OFF); delayMicroseconds(x); ect. There ends up being 10 on pulses in any given word with the bin positioning determining the message.

Any ideas of improving the timing or verifying the timing my method is giving me?

Please post your code here so we don't have to go to another website. Also my browser tells me that link is insecure.

...R

You don't say what Arduino you're using for this endeavor, or if you're using an Arduino at all. If you're using an Uno, then the timing standard of 0.015% will be difficult to meet. The Uno's clock is a ceramic oscillator, with an accuracy on the order of +/- 0.5%.

The digitalWrite() function takes a substantial portion of your 20.83 microsecond window to execute. I mark it in the neighborhood of about 6 microseconds. That will certainly affect your timing.

Finally, any interrupts might easily confound your efforts at careful timing. Unless you've disabled the Timer0 interrupt, it will interrupt the executing code about every millisecond to manage the internal millisecond count - at least three times during any 3.33 millisecond frame. delayMicroseconds() appears to operate by executing a series of instructions whose execution time can be calculated, rather than by watching a hardware clock, so an interrupt from any source will extend the length of time that delayMicroseconds() waits. If you're using Serial for printing while all this is going on, the UART interrupt will exacerbate timing issues.

Here are some suggestions that will help you get more, and more thorough, responses:

  • Tell us what hardware you're using to implement this. An Arduino? Which one?
  • Show your code. If the code is long, you'll be happier with your responses if you write a minimal sketch that illustrates the problem you're working on.
  • Read the information in the sticky post, "How to use this forum - please read," found at or near the top of the subject listings for each section of the forum, and follow the posting guidelines you find there.
  • Post a link to the standard that you're using. I couldn't find it at the link you gave, and had to do a google search for it. If the information is difficult to find, many readers won't go to that effort, and will move on to other posts without responding to yours.

Instead of digitalWrite, use direct port manipulation.
For example on a Uno, and D8:
PORTB = PORTB & 0b11111110; // clear D8 (low)
PORTB = PORTB | 0b00000001; // set D8 (high)

I think delayMicroSeconds works at 4,8,12,16,20,24, 28 kind of numbers - numbers in between I think result in one of those.

Robin2:
Please post your code here so we don't have to go to another website. Also my browser tells me that link is insecure.

The like is to a code standard, not the actual code I created. It's a .mil address which often shows as insecure. I understand the hesitation, so here is a direct link to the file from a Russian website. http://flprog.ru/_fr/11/MCC_Standard_PM.pdf

tmd3:
You don't say what Arduino you're using for this endeavor, or if you're using an Arduino at all.

This may be my ignorance to the nomenclature, but was my first sentence not specific enough? Arduino Pro Mini 328 - 5V/16MHz?

tmd3:

  • Show your code. If the code is long, you'll be happier with your responses if you write a minimal sketch that illustrates the problem you're working on.

I attempted to do this in pseudo-code in my post. My apologies for its inadequacy. I see how that would be useful, especially in a programming forum.

/*
  Sends MILES Resurrect Code sequence to IR LED.
  
  15 October 2016
  by Matt Geders
*/


int IRBlaster = 2;
// the setup function runs once when you press reset or power the board
void setup() {
  // initialize digital pin LED_BUILTIN as an output.
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(IRBlaster, OUTPUT);
}

// the loop function runs over and over again forever
void loop() {
  digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)

  digitalWrite(IRBlaster, HIGH);     
  delayMicroseconds(21);
  digitalWrite(IRBlaster, LOW);      
  delayMicroseconds(189);
  digitalWrite(IRBlaster, HIGH);     
  delayMicroseconds(21);
  digitalWrite(IRBlaster, LOW);      
  delayMicroseconds(105);
  digitalWrite(IRBlaster, HIGH);     
  delayMicroseconds(21);
  digitalWrite(IRBlaster, LOW);      
  delayMicroseconds(147);
  digitalWrite(IRBlaster, HIGH);     
  delayMicroseconds(21);
  digitalWrite(IRBlaster, LOW);      
  delayMicroseconds(357);
  digitalWrite(IRBlaster, HIGH);     
  delayMicroseconds(21);
  digitalWrite(IRBlaster, LOW);      
  delayMicroseconds(105);
  digitalWrite(IRBlaster, HIGH);     
  delayMicroseconds(21);
  digitalWrite(IRBlaster, LOW);      
  delayMicroseconds(1659);
  digitalWrite(IRBlaster, HIGH);     
  delayMicroseconds(21);
  digitalWrite(IRBlaster, LOW);      
  delayMicroseconds(315);
  digitalWrite(IRBlaster, HIGH);     
  delayMicroseconds(21);
  digitalWrite(IRBlaster, LOW);      
  delayMicroseconds(315);
  digitalWrite(IRBlaster, HIGH);     
  delayMicroseconds(21);
  digitalWrite(IRBlaster, LOW);      
  delayMicroseconds(105);    
  digitalWrite(IRBlaster, HIGH);     
  delayMicroseconds(21);
  digitalWrite(IRBlaster, LOW);      
  delayMicroseconds(189);

  //to save space in this forum post, I've removed the above block repeated 7 more times
  
  digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
  //delay(5000);                       // wait for 5 second
}

tmd3:

  • Read the information in the sticky post, "How to use this forum - please read," found at or near the top of the subject listings for each section of the forum, and follow the posting guidelines you find there.

I believe I did, but I can't help but feel I've stepped on some toes somehow?

CrossRoads:
Instead of digitalWrite, use direct port manipulation.
For example on a Uno, and D8:
PORTB = PORTB & 0b11111110; // clear D8 (low)
PORTB = PORTB | 0b00000001; // set D8 (high)

I think delayMicroSeconds works at 4,8,12,16,20,24, 28 kind of numbers - numbers in between I think result in one of those.

I had to read up on this method, but I converted my code and it works! Thank you for teaching me something new.

/*
  Sends MILES Resurrect Code sequence to IR LED.
  
  19 October 2016
  by Matt Geders
*/


int IRBlaster = 2;
// the setup function runs once when you press reset or power the board
void setup() {
  // initialize digital pin LED_BUILTIN as an output.
  //pinMode(LED_BUILTIN, OUTPUT);
  DDRD = DDRD | B11111100;
  //pinMode(IRBlaster, OUTPUT);
}

// the loop function runs over and over again forever
void loop() {
  PORTD = B00000100;    
  delayMicroseconds(21);
  PORTD = B00000000;      
  delayMicroseconds(189);
  PORTD = B00000100;     
  delayMicroseconds(21);
  PORTD = B00000000;     
  delayMicroseconds(105);
  PORTD = B00000100;      
  delayMicroseconds(21);
  PORTD = B00000000;      
  delayMicroseconds(147);
  PORTD = B00000100;      
  delayMicroseconds(21);
  PORTD = B00000000;      
  delayMicroseconds(357);
  PORTD = B00000100;      
  delayMicroseconds(21);
  PORTD = B00000000;      
  delayMicroseconds(105);
  PORTD = B00000100;      
  delayMicroseconds(21);
  PORTD = B00000000;      
  delayMicroseconds(1659);
  PORTD = B00000100;      
  delayMicroseconds(21);
  PORTD = B00000000;      
  delayMicroseconds(315);
  PORTD = B00000100;      
  delayMicroseconds(21);
  PORTD = B00000000;      
  delayMicroseconds(315);
  PORTD = B00000100;      
  delayMicroseconds(21);
  PORTD = B00000000;      
  delayMicroseconds(105);    
  PORTD = B00000100;      
  delayMicroseconds(21);
  PORTD = B00000000;      
  delayMicroseconds(189);

//to save space in the forum, the above block is repeated 7 times 

  delay(5000);                       // wait for 5 second
}

Matthew10_28:
was my first sentence not specific enough? Arduino Pro Mini 328 - 5V/16MHz?

It was plenty specific, and very easy to find. I missed it, and I looked more than once. I apologize for incorrectly claiming that information was missing, and for generating any confusion.

... I can't help but feel I've stepped on some toes somehow?

I can't tell, but you might be talking about my post. If so, then I can answer that you didn't step on any toes. believe it or not, that was me being polite, and even a bit friendly. Did I miss that mark?

Matthew10_28:
... I converted my code and it works!

Good news, I think. Could you tell us how you determined that it works? Did you verify that the timing looks right from the overall message? Or - maybe a stronger test - does it communicate properly with the device you intended it for?

Here's one of my concerns: the code you show doesn't do anything to manage the TIMER0 interrupt. That interrupt is enabled by default, it happens roughly once every millisecond, and it takes roughly 5 to 6 milliseconds to execute. delayMicroseconds() uses a wait loop for timing, and, when it's interrupted, it doesn't advance. So, for a delay of, say, 21 microseconds, if the TIMER0 interrupt were to fire, the actual delay would be something on the order of 26.5 microseonds.

The Timer0 interrupt is certain to fire at least three times in a canonical 3.667 millisecond transmission window, extending the message by about 16 microseconds - more than half of a bit time.
If the receiver samples at mid-bit, like we'd expect, it'll get errors on at least some messages.

Do your tests include sending a variety of messages to a receiver?