Record & Playback DigitalPin status

Hi!
I'm working on a project where I'm sending keyboard presses to my Arduino from a windows program (RS232). A pre-specified key on my keyboard sets a digital pin "high" or "low". The sending process works great and almost no delay. I need to record the time (and value) when my digital pin sets to "high" or "low" to be able to playback the exact information later - Macro. I have done the "recording"/"playback" in my Arduino, but I have also tried to do it in my windows application. My problem is that can't get the timing 100% correct...

During the playback I have tried to use this code:

//MyTimeStamp is my recorded time using micros()-micros_when_i_started_the_recording...
//It's reading the micros() value directly before I set the digital pin high or low.

PlackBackStart = micros();
while (micros()<(PlackBackStart + MyTimeStamp)) ;
//set pin high or low...

Timing seems to be a problem... it playbacks everything nice and don't miss a thing, but the timing seems incorrect.

Should I use an external timer? Anyone got a clue?

PS. I'm setting the DigitalPin High or Low directly: PORTD ^= 0b00000100; to minimize delay.

Best Regards
Anders Jansson

Timing seems to be a problem.

What is the problem? All you have said is that you have a problem, and need help. You haven't said what the problem is.

Be aware that timestamp values wrap-round (a timestamp is the result of a call to millis() or micros()).
The micros() value wraps every 71.5 minutes and the millis() value wraps every 50 days or so.

PlackBackStart = micros();
while (micros()<(PlackBackStart + MyTimeStamp)) ;

Won't do what you want when the micros()+MyTimeStamp sum wraps round to a small value. You always need to do tests that compare a difference of two timestamps against a delay value (and that delay should be less than the wrap-around time). Like this:

PlackBackStart = micros();
while (micros() - PlackBackStart < MyDelay) ;

I presume your 'MyTimeStamp' variable is a delay value, not a timestamp - otherwise the code makes no sense at all.

You can use delayMicroseconds() if you don't need to do anything else whilst waiting...

PaulS:

My problem is that I can't get the timing 100% correct...

MarkT: MyTimeStamp is actually an array "MyTimeStamp[300]". Everytime a DigitalPin is set high or low the program saves the current time in microseconds since I started the recording: MyTimeStamp[X] = micros()-micros_when_i_started_the_recording;

I can't use delayMicrosecond() because it's not accurate for microseconds over 16383.

To sum things up. My code is working, but my Problem is that it's not accurate enought. I need my playback to set my digitalpins high or low at the exact recorded time (milliseconds should be enought if the timing is correct). Maybee it's a better way to record my event or maybee there is a better way to playback my events... I can't figure it out.

Thanks for replying!

MyTimeStamp is actually an array "MyTimeStamp[300]"

That array is then using 1200 bytes. That's a significant portion of the memory you have available to you. Perhaps we need to see your code, and perhaps (no, make that definitely) you should describe the purpose of resetting this pin with millisecond resolution, and what "it's not accurate enough" means (with a lot less hand waving).

More information:
What my program should be able to do:

  1. Record - Save DigitalPin status (high / low) and time when it changes. (changed thru rs232)
  2. Playback - Change the DigitalPin status based on the saved status and time.

What is the best approach?

1)
RecordingTimeStart=micros();
//...
//...time goes by...
//...on rs232 event...(byte byteReceived;)
//...
TimeStampWhenButtonPushed=micros()-RecordingTimeStart;
//set DigitalPin based on rs232 event
PORTB ^= 0b00010000;
//Save information
MacroTimeStamp[counter] = TimeStampWhenButtonPushed;
MacroByte[counter]=byteReceived ;
//…
2) 
PlaybackStart = micros();
//...
for (int k = 1; counter; k++) 
{
//...

while (micros()<(PlaybackStart + MacroTimeStamp[k])) ;

//set DigitalPin based on saved byte...

PORTB |= 0b00010000;
//...
}
//…

The first thing you need to clarify is how much precision you really need. Have a look at my article on crystal deviations. Crystal Deviations | Blinkenlight
Crystal Deviations 2 | Blinkenlight. So crystal based Arduinos will typically be somewhere between 10 and 100ppm of the mark. If a resonator is used even more so.

Now if you are recording and playing back with the same Arduino, then the relative deviations will be typically below 10ppm. Thus I would expect that the precision of the clock is no issue.

The real issue is probably the way you are measuring and playing back. That is: you code will take some time to react to a pin change. It will also take some time to read the microseconds back. In addition you get jitter from the timer interrupts.

This can be resolved and I did this for some project once. My solution was to disable all timer interrupts and use a very tight polling loop. The inner loop was hand coded in assembler and I kept track of all cycles. Thus I got a resolution of 1us (with a 8 MHz clock). You can get even finer if you use the ICMP interrupt. However both of these approaches are considerably advanced. I will not post the code because it not even closely related to the way Arduino is usually programmed. The whole thing included a rudimentary forth interpreter / compiler such that I could compile the desired code at runtime.

If you really want to record and playback signals at such speeds I suggest to take a different approach: get some SRAM chips and record directly into the memory chips and playback from them.

Another option would be to analyze the keyboard signal and synthesize it later on instead of playing it back. That is: figure out the keyboard clock frequency (e.g. with FFT) and "lock" to the clock. Later you would synthesize the desired playback with the same clock. It is always the same issue: you have to avoid jitter.

Maybe this is to complicated but that's the only thing that got into my mind right now.

For the other forum members: he asked by PM if I could comment on this.