How to use Exact Timings on Arduino?

If I want to sync something so that it happens exactly 1 time every 20 ms in sync with a 50Hz device, what do I need to consider incorporating into an Arduino project?

I have many R3 Uno's, one R4 Uno, Leonardo's and Mega 2560's. I have many electronic components, including some crystals in the MHz, but I wouldn't have thought they would be useful.

Any ideas?

What is the 50Hz device, and how does the Arduino know it's state?

How close in time is the requirement on "sync"?

What else the code has to do. Timing to 20ms is easy with millis.

Read about zero-crossing mains sync.
You need to clock your event from the same mains source as your device.

I am attempting to check if the beam of a 50Hz CRT TV is in a certain place on screen (the place is constantly having light drawn to it at 50Hz (always on in regards to CRT)). I have a phototransistor pointing at a certain point (connected to digitalRead(3)), and just want to know that I can rely on the program to test the same point each loop.

I have some naive code that does it like this (just to see what will happen):

void loop()
{
OT = NT;
do{NT = micros();
} while((NT-OT)<20000);
 Serial.println( digitalRead(3));
}

What I get a s a result is around 6 seconds worth of Serial results saying 1 and after that around 6 seconds worth of Serial results saying 0.

This is using micros() which suggests that the micros() usage is 'rolling' as a badly synced screen would on a display, rolling up the picture.

I understand that I should have a start point set, for example, once the digitalRead(1)=1 then sync from there, and I will do that afterwards. I would just like to get consistent results from this setup before I change it to a sync start method.

What the results currently show is that my method of waiting 20ms is not accurate.

Please suggest a method to amend my program with that will at least result in the Serial data being the same result (wether 0 or 1) for a few minutes.

I'm sure we all know what CRT's are and what I am describing, but this makes it easier, the light beam that is slowed down in this video is passing the phototransistor 50 times every second: https://bit.ly/47oNocX

If the timing went off a little bit, then the phototransistor would report "0". If the beam is around the front of it, it will report "1".

If the timing of the program was EXACTLY 50 times per second (20ms (or 20000us) delay) then the phototransistor would report the SAME number each loop.

Although the phototransistor does report the same number for 6 full seconds (6x50 times) - after that it reports the other number, and that suggests that half the time it is not near the beam at that time.

Thanks for any suggestions.

I would think that you would want to use a photodiode which is much faster than a phototransister, and use it on D3 with an interrupt.

This phototransistor works at 7 micro seconds fall and rise. It is adequate.

OK. I understand that you get an adequately fast signal every time the beam passes a certain spot.

There are ways using pin change interrupts, or perhaps input capture on a timer to capture a time for the event.

I'm unclear about what you want to do with the time value that you capture.

I am experimenting at the minute, and what I want is consistent results with this simple setup. Gaining an understanding along the way.

If you think my timing method is bad, then please suggest another way.

I want to use this phototransistor (BPY-62 3/4) as described, to read a consistent result of:

Is the beam lit around the front of the Phototransistor during a 20000 microsecond interval?

So far my results are as I described, and not consistent, although, I thought that the timing of the Arduino Uno R3 I am using would have allowed for reading not changing on a 6-10 second basis - I thought it might report different results every few minutes or hours, not 6-10 seconds.

So, "exact" means +/- 7us, every 20ms?
That seems like it could be done in SW using a Pin Change interrupt and micros() (micros() is "close", since it's resolution is 4us), or more accurately (and less portable) setting up a timer in capture mode (or set up a timer to count every microsecond instead of every 4us, and use that with an ISR.)

Keeping the Arduino's clock well synchronized with an external timing source (50hz mains) is a second problem, maybe. Presumably it would help to implement a Zero-crossing detector on the same mains, and use a crystal-based clone.

For a simple experiment to see what the frequency of the photo transistor trigger, I would use the FreqMeasure library. It will be available through the library manager.

That library will use the input capture method(as @westfw mentions) on one of the hardware timers of the Arduino for an accurate measure of frequency.

If you think your system is stable at 50Hz, and you want to do some thing when you capture a rising edge from the photo transistor we can continue with the discussion.

I think expecting to sync those two signals using timing alone is a bad idea. That's very hard to do. Usually you would want some sort of sync signal that is telling both things when to happen. But you can't always have that.

Either way, there will always be drift in the timing on one side or the other.

One thing that might make a difference would be to change your code like this. This way if it does have one long interval and misses then at least it can catch up on the next loop instead of propagating that error.

void loop()
{
 OT += 20000;
 NT = millis();
 while((NT-OT)<20000){
     NT = millis();
 }
 Serial.println( digitalRead(3));
}

But even with that, you won't have the kind of long term stability that I think you want.

What is the purpose of the measurement? Are you just experimenting with timing? I think you are learning a lesson that many an engineer has learned before you. It is really really hard to sync two clocks exactly.

1 Like

Let me talk about why real quick, because I know it comes up and confuses people quite often.

Let's talk first about the code that sets the old time to millis. I'm going to use the previousMillis and currentMillis names from BlinkWithoutDelay.ino even though I hate those names just so I don't have to rewrite the whole thing. But I am going to abbreviate as PM and CM.

In the normal BWoD we have a line like this:

previousMillis = currentMillis;
and you'll sometimes see it like this:
previousMillis = millis();
with the difference being that if it takes some time to complete whatever task you are doing in the if statement then the second method will get the actual time it completed and the first will get the time that it started.

So let's imagine that we are timing something at 50Hz, 20ms. Let's say we start with millis at 0 and PM at 0. 20ms later CM is 20 and 20 - 0 is 20 so the thing happens and PM gets set to 20. 20ms later CM is 40 and 40-20 is 20 so it happens again and PM gets set to 40.

Now let's imagine that on the next loop something else takes a long time and the timing misses 60 exactly and gets checked instead at 61. So CM is 61 and 61-40 is greater than 20 so the thing happens. But now PM gets set to 61, not 61.

So next it happens at 81, then 101, then let's say something takes a long time again and it doesn't get checked until 103. So now PM gets set to 103 and the next round happens at 113 then 123...

You see the patter developing. Every time the check is a little late, the PM variable gets a few extra millis added. So the events get progressively later and later and out of sync.

NOW THE OTHER WAY:
So let's talk about what I did in the last post. Instead of setting the PM to the actual time, I just added the interval to it. This means that when the timing gets checked late and CM is 61, PM still just gets set to 60 meaning the next event will happen only 19ms later and get back in sync.

The problem there is when you need to enforce a minimum interval. Let's imagine for a second that something took a really really long time and while PM was at 60 it doesn't get checked again until millis is at 120.

So 120 - 60 is greater than 20 so an event happens. And 20 gets added to PM to make 80. On the very next loop the timing gets checked again and this time CM is probably still 120 and PM is 80 and 120 - 80 is still greater than 20 so another event is fired and PM gets set to 100. On the very next loop another event will fire because it is still more than 20ms between CM and PM. So PM gets set to 120 now.

So you can see that in the example where we set PM to millis or currentMillis we are able to enforce a minimum interval between events and no two events will ever happen less than 20ms apart. But we run the risk of the events getting out of sync.

With the add-the-interval method we are able to always stay in sync, but if events are missed then they will happen rapid-fire style to catch up. You can stop that with some additional clever code to check if more than one interval has passed, but you still will have cases where the 20ms was actually 18 or 19 between some events.

3 Likes

Well, I have had some incredible results from that suggestion!

There are times that the frequency of the FreqMeasure will be 50.27, and the can be for a long time, and that's good enough for me, if I know that.

But, I think the main problem has been suggested, if not exposed.

The closer I have the phototransistor to the TV the worse the signal varies, I mean wildly varies, from 50.27 to 136.78 in 2 seconds, and 1027.44 in 8 seconds, and loops around.

I remember the Static electricity I could feel from the front of a CRT as a child, and assume that has something to do with it.

There was a similar problem when I was holding the wire the Phototransistor is connected to with my hand and the 0's and 1's were very unpredictable.

If is it not static, then it is something else caused by the static.

If I hold the hand of the little helping hands magnifying glass soldering helper that is holding the Phototransistor, it manages to drain the static, and keeps the frequency at 50.27 again, so I can do that, but if there is a better way to manage an over-sensitive input to an Arduino Uno, please let me know.

I still think my setup is stable at around 50Hz, and shouldn't have gone out of sync so quick (actually how long would 50.27Hz last assuming it was 50Hz?) but I can change it to 50.27 anyway :-).

I'll have to re-assess my setup!

Thanks for the suggestions.

The code I am using to create the results I mentioned is:

/* FreqMeasure - Example with serial output
 * http://www.pjrc.com/teensy/td_libs_FreqMeasure.html
 *
 * This example code is in the public domain.
 */
#include <FreqMeasure.h>

void setup() {
  Serial.begin(115200);
  FreqMeasure.begin();
}

double sum=0;
int count=0;

void loop() {
  if (FreqMeasure.available()) {
    // average several reading together
    sum = sum + FreqMeasure.read();
    count = count + 1;
    if (count > 30) {
      float frequency = FreqMeasure.countToFrequency(sum / count);
      Serial.println(frequency);
      sum = 0;
      count = 0;
    }
  }
}


Just the example Serial Out code suggested by FreqMeasure.

Something is causing interference with the Arduino, and I cannot actuallt pinpoint it down to distance from the TV either with the Arduino, or the Sensor, or distance from me.

What might be at play? It will behave very well at 50.27ms consistently for a long time according to FreqMeasure.

OK, now that we know that you know the rising edge from the photo transistor can be a consistent signal with emi/static control, how are you presently capturing that in your code, and what are you trying to sync with that signal.

I apologize, but I do not understand the bigger picture of what you are trying to achieve.

Honestly, I would like to see some consistent results from each test at the minute.

I assume that the results of FreqMeasure being 50.27 could have something to do with it.

I have just tested and using:

unsigned long Told = 0;  //some global variables available anywhere in the program
unsigned long Tnew = 0;

void setup()
  pinMode(8, INPUT);
}

void loop()
{
Told = Tnew;
do{Tnew = micros();
} while((Tnew-Told)<19892);     1000000/50.27=19892.5801
 Serial.println( digitalRead(8));
}

Results in much a much longer time settled on one result from digitalRead(3). More than 29 seconds settled on each result.

That will give me enough time to re-sync.

If I wanted to get this perfect, as it is not, but when no other outside forces are acting upon it, this stream settles around 50.27 times per second. Is there any way to make that simple "Fully Sync With My Sync Or Lose Sync" method better? Just to experiment with various periods somewhere around 19892 to see?

Is there any way to help towards the CRT TV itself not messing with the times?

#13. Explanation in #14

Honestly, I would like to see some consistent results from each test at the minute.

You might have better result is you start the "am I in sync" timing from 10 ms after the trigger.

I'm not certain if this code will do that, but you can give it a try and see if it extends the timing you are in sync.

unsigned long Told = 0;  //some global variables available anywhere in the program
unsigned long Tnew = 0;

void setup()
  pinMode(8, INPUT);
}

void loop()
{
 while(digitalRead(8)==LOW); //hang out until pin goes HIGH
 delayMicroseconds(10000);
 Told = Tnew;
do{Tnew = micros();
} while((Tnew-Told)<19892);     1000000/50.27=19892.5801
 Serial.println( digitalRead(8));
}