Question regarding timer1/counter

I was wondering the plausibility of writing a program to do this:

Hook a 1 Hz square wave to an input When the arduino detects a rising edge from this input, the counter begins counting as dictated by an external 1 MHz clock When the counter reaches a predetermined number (say, 56 for a delay of 56 microseconds), the arduino will output 5V to a specified pin When the arduino detects the falling edge from the input, the counter begins counting (by 1MHz external clock again) When counter reaches a different predetermined number, arduino outputs 0 volts

I will probably use port manipulation to set the output low/high since port commands have a very short processing time. My question is how to use the timer/counter using an external clock.

The goal is to delay an input signal by a predetermined abount of microseconds as determined by an external 1MHz clock. I already have a program that makes use of the arduino's 16MHz clock, but want to try to do this with a more accurate external clock.

Similar question was here several weeks ago, cannot find it so quickly.

The solution I proposed was connecting the 1Hz signal to an IRQ, and set a flag with a timestamp, in the main loop one switches the output. Something like the code below.

(did not check if it compiles)

#define PULSESHIFT 200  // micros;

int pin = 13;
int state = LOW;
volatile unsigned long timestamp = 0;

void setup()
{
  pinMode(pin, OUTPUT);
  pinMode(2, INPUT);   // IRQ pin
  attachInterrupt(0, IRQ, CHANGE);
}

void loop()
{
  if (timestamp > 0  &&  micros() - timestamp >= PULSESHIFT )  // if the timestamp has been set and longer than PULSESHIFT ago swap the output line.
  {
    timestamp = 0;
    digitalWrite(pin, state);
    state = !state;
  }
}

void IRQ()
{
  timestamp = micros();
}

robtillaart: Similar question was here several weeks ago, cannot find it so quickly.

The solution I proposed was connecting the 1Hz signal to an IRQ, and set a flag with a timestamp, in the main loop one switches the output. Something like the code below.

(did not check if it compiles)

#define PULSESHIFT 200  // micros;

int pin = 13; int state = LOW; volatile unsigned long timestamp = 0;

void setup() {   pinMode(pin, OUTPUT);   pinMode(2, INPUT);   // IRQ pin   attachInterrupt(0, IRQ, CHANGE); }

void loop() {   if (timestamp > 0  &&  micros() - timestamp >= PULSESHIFT )  // if the timestamp has been set and longer than PULSESHIFT ago swap the output line.   {     timestamp = 0;     digitalWrite(pin, state);     state = !state;   } }

void IRQ() {   timestamp = micros(); }

Thanks for taking the time to write up this code, I appreciate the help :)

I have code similar to this and it does make use of the arduino's micros() command, but is there any way to use an external 1 MHz clock (that is more accurate than the arduino's) to govern the delay? My initial instinct was to use counter1 clocked by the external 1 MHz crystal but I am not sure how to do that.

The solution I proposed was connecting the 1Hz signal to an IRQ, and set a flag with a timestamp, in the main loop one switches the output.

This means that your external signal is connected to an interrupt pin, and generates an interrupt every time it ticks, which will be 1/16th as often as the Arduino's ticker.

Why do you think your external crystal will be more accurate than the Arduino's external crystal, especially since it is so much slower?

I might be missing something, but why not just delay in the interrupt? For instance, in Rob's code add delayMicroseconds(56):

robtillaart: void IRQ() {   delayMicroseconds(56); }

The advantage here is you start timing exactly on the event, whereas using a 1mhz signal, the timing and the event may be out of sync some of the time (unless you have some way for them to be perfectly in lockstep).

From what I've been told the clock we have is much more accurate than the arduino clock (as in error in PPM), and the rest of our experiment is running on the 1 MHz clock and we want to keep this program in sync with the other machines. Is there any way to just flat out use the 1 MHz clock instead of the arduino clock then? Disconnect the 16 MHz and use the other one?

Or is there a way to do it using counter1/timer1?

csnsc14320: From what I've been told the clock we have is much more accurate than the arduino clock (as in error in PPM), and the rest of our experiment is running on the 1 MHz clock and we want to keep this program in sync with the other machines. Is there any way to just flat out use the 1 MHz clock instead of the arduino clock then? Disconnect the 16 MHz and use the other one? Or is there a way to do it using counter1/timer1?

I couldn't find anything on 'arduino share clock', but I have heard of two arduinos sharing the same crystal - whether that applies, I don't know.

One way to get good timing is to put everything in the interrupt, so you have exact sync with your outside clock. If you did that, then you'd set up a time value/count on a rise of 56, and output your values when they hit zero.

However, from your specification, you said you want to delay your pulses. However, if you take a 1mhz signal and wait 56 microsec to output, and then wait another 56 to output a 0v, aren't you actually stretching part of the signal by a factor of 56? On the other hand, if you really want to delay, then you'd need a 56 number buffer, where at every 1mhz signal you store the current pin value for later, and output the value stored from 56 counts ago. Since you only have high and low, that means you could get away with a bit buffer, or BYTE for each value.

[quote author=David Pankhurst link=topic=66527.msg491070#msg491070 date=1311010070] One way to get good timing is to put everything in the interrupt, so you have exact sync with your outside clock. If you did that, then you'd set up a time value/count on a rise of 56, and output your values when they hit zero.

[/quote]

Know of any documentation that describes how to use the timer/counters effectively? The datasheet seems cryptic and nothing on google seems to be particularly useful to me yet :(

However, from your specification, you said you want to delay your pulses. However, if you take a 1mhz signal and wait 56 microsec to output, and then wait another 56 to output a 0v, aren't you actually stretching part of the signal by a factor of 56?

The signal I am trying to delay is the 1 Hz input. I want a separate, 1 MHz input that will govern the clock for the counter that will delay the 1 Hz wave.

Sorry - I missed the 1hz and 1mhz as spelling errors, and didn't realize they were two different signals. Because of that, I thought you could use the input signal as the timing. Unfortunately, it's 1hz, which isn't what I was expecting.

If however you could get both the 1 hz and 1mhz signals into two pins on your Arduino, you could set up two interrupts - one for the timing (1mhz), and one for noting the change of the 1hz signal.

robtillaart already posted interrupt code - you'd simply add another interrupt:

volatile unsigned long tick=0;
void IRQ1mhz()
{
  +tick;
}

You would use this value much like the timestamp in his code - but you'd get the benefit of your clock timing.

Is this what you were thinking?

typo: ++tick

[quote author=David Pankhurst link=topic=66527.msg491315#msg491315 date=1311027129] If however you could get both the 1 hz and 1mhz signals into two pins on your Arduino, you could set up two interrupts - one for the timing (1mhz), and one for noting the change of the 1hz signal.

volatile unsigned long tick=0;
void IRQ1mhz()
{
  +tick;
}

You would use this value much like the timestamp in his code - but you'd get the benefit of your clock timing.

Is this what you were thinking? [/quote]

I am trying think of how to use the second interrupt. I think this may be what I am thinking.

This would be my guess for how the program would work:

Program does nothing until it gets a rising edge from the 1 Hz input (say pin 2) Go to an interrupt loop, in which a counter (clocked by my 1 MHz signal on, say, pin 3) counts to X (where I specify X in the setup) before setting an output pin (say pin 6) to +5. Doing this there should now be a delay of X microseconds between receiving the rising edge of pin 2 and outputting +5V to pin 6. Keep outputting +5V on pin 6 until the falling edge of pin 2 is detected. Again, to to an interrupt loop where it counts to X (clocked by 1 MHz) before switching output pin 6 back to zero. Repeat so that the input 1 Hz wave is delayed X microseconds, with its shaped preserved.

If this would work, I am unsure how to control the counter and how to make it wait until it hits X before moving on to output +5V. I am also unsure how to run the counter on the 1 MHz clock instead of the arduino's 16 MHz clock as well.

Or is there something I may have overlooked in this process that wouldn't work the way I plan? Or perhaps an easier way? The ultimate goal is to delay a 1 Hz wave by X microseconds as clocked by our 1 MHz clock rather than the arduino's 16 MHz clock.

csnsc14320:
The ultimate goal is to delay a 1 Hz wave by X microseconds as clocked by our 1 MHz clock rather than the arduino’s 16 MHz clock.

OK, I think I understand now. How about this:

  • Your clock interrupt (1mhz) samples the 1hz stream each time it ticks, and stores it.

  • It then outputs another value it stored from 56 ticks ago.

  • repeat

The advantage of this is the code is extremely simple, using what is called a ring buffer:

#define TOTAL 56
#define TOTALRING TOTAL+4
volatile unsigned char ringBuff[TOTALRING];
//  a little extra...
volatile unsigned int start=0, end=0;
volatile unsigned long tick2=0;
void interrupt1mhz(void)
{
  
// get 1hz pulse - 0 or 1 (may need fast polling of some sort)
unsigned char flag=tick2+100; // eg - you'd need your own code here to get byte

  // add to buffer
  if (++end>=TOTALRING)
    end=0;
  ringBuff[end]=flag;
  if (tick2+1<=TOTAL)
  {
    ++tick2;
    return; // nothing to output until 56 ticks have passed!
  }
  // get entry 56 ago
  if (++start>=TOTALRING)
    start=0;
  flag=ringBuff[start];

// send byte 'flag' out as high/low pulse
// note: you may want to check against last write, and see if it's worth doing or not...
    
}

What you’re doing here is grabbing a snapshot of the 1hz signal every 1mhz, and storing it in a buffer, then outputting the previous snapshot from 56 x 1mhz cycles ago. The code leaves out that you’d need to do direct port reading (apparently digitalWrite and digitalRead are too slow for 1mhz). Another aspect is that you are taking the value of the waveform every microsecond, so you don’t need the 1hz interrupt anymore. However, you are sampling at 1mhz, which means you may be off by (on average) +/- 0.5 microsecond on the waveform - if that’s not a problem, this code should work.

…and of course, assuming this interrupt can finish soon enough for the next 1mhz call…

David's "delay line" ring buffer will work and that is the way I would do it, you can't get much simpler, much better that starting timers etc.

But for future reference you can provide an external clock to the timers on the T0 and T1 pins.


Rob

Thanks David for the awesome responses so far! I have a couple questions regarding your code:

  • Your clock interrupt (1mhz) samples the 1hz stream each time it ticks, and stores it.

  • It then outputs another value it stored from 56 ticks ago.

  • repeat

I understand the gist of what the code should do and I see how it would be much simpler than what I was initially trying to do.

I am trying to figure out exactly what your code is doing, but part of the problem may be that I can't figure out where you skipped code that I need to fill in (new to programming the arduino sort of and am having a little trouble figuring out what is missing).

also, what are ringBuff(TOTALRING), tick2, and flag doing exactly?

Here’s my version of a delay line.

// will produce invalid results for the first DELAY_IN_US micro seconds
// as defined produces an image of PORTB:2 on PORTC:6, delayed by 56uS
// Note that if you organise the hardware so the in and out bits are on different
// ports but the same bit location you don't have to do any bit shifting

#define DELAY_IN_US    56
#define INPORT         PINB
#define OUTPORT        PORTC
#define MYPORT_IN_BIT  2  
#define MYPORT_OUT_BIT 6  

byte buffer [DELAY_IN_US];
byte *ptr = buffer;
// define something to compare the pointers against assuming it's faster to compare two 
// pointers than a pointer against a literal
byte *end_ptr = buffer + sizeof(buffer) + 1;  

void  one_mhz_interrupt_func () {

    *ptr++ = (INPORT >> MYPORT_IN_BIT) & 1;         // isolate the input bit   
    
    // by definition the next byte in the buffer is DELAY_IN_US uS old so the pointer now points 
    // either to that byte or it's off the end of the buffer
                                
    if (ptr == end_ptr) ptr = buffer; // reset pointer to start of the buffer if necessary
    
    // note that if you have nothing else connected to the port you don't have to fool around with 
    // bits here either, but this assumes there are other things on OUTPORT
    OUTPORT |= (*ptr) << MYPORT_OUT_BIT;    
    
}

In a nutshell every uS you read the IO port and put that value in the buffer, then take the oldest value out and write it to the IO port. The same ±.5uS jitter will apply as David’s routine, however I think this is about as fast as you can get so you could possibly make the interrupt frequency faster to reduce the jitter.

Caveat: Off the top of my head, should work but I may have missed something.


Rob

very nice solution, Rob!

one typo? PINB => PORTB ?

// Note that if you organise the hardware so the in and out bits are on different // ports but the same bit location you don't have to do any bit shifting

You don't need shifting, not even with different bitlocations when you use MASKS; might even be slightly faster (smaller footprint?)

#define DELAY_IN_US    56
#define INPORT           PORTB
#define OUTPORT        PORTC
#define MYPORT_IN_BIT_MASK 0x04        // bit 2  
#define MYPORT_OUT_BIT_MASK 0x40     // bit 6  

byte buffer [DELAY_IN_US];
byte *ptr = buffer;
byte *end_ptr = buffer + sizeof(buffer) + 1;  

void  one_mhz_interrupt_func () 
{
    *ptr++ = INPORT & MYPORT_IN_BIT_MASK;
    if (ptr == end_ptr) ptr = buffer; 
    if (*ptr) OUTPORT |= MYPORT_OUT_BIT_MASK;   //make them 1
    else OUTPORT &= ~MYPORT_OUT_BIT_MASK;    // make them 0
}

-- update -- fixed a bug in the code above. (make them 0 line).

thank you guys for the awesome, in depth responses so far! I will tinker with some of this code and try to get it to work soon.

One question about the method not using bit shifting:

where you defined one output pin on PORTC:

#define MYPORT_OUT_BIT_MASK 0x64     // bit 6

can I also do

#define MYPORT_OUT_BIT_MASK 0x224

to have the delayed output signal on pins 6, 7, and 8 on PORTC?

edit: also, I’m assuming the

#define MYPORT_IN_BIT_MASK 0x04        // bit 2

is for the 1 Hz signal.

Do I need to add an attachInterrupt for the 1 MHz signal to run void  one_mhz_interrupt_func () upon detecing a rising edge of the 1 MHz?

define MYPORT_OUT_BIT_MASK 0x224

to have the delayed output signal on pins 6, 7, and 8 on PORTC?

you mean 224 iso 0x224 but yes definitely

BUG ALERT

there is a bug in my code; it must be

if (*ptr) OUTPORT |= MYPORT_OUT_BIT_MASK; //make them 1 else OUTPORT &= ~MYPORT_OUT_BIT_MASK; // make them 0

I'll fix that in the other post,

-- update -- done

robtillaart: you mean 224 iso 0x224 but yes definitely

wait so not #define MYPORT_OUT_BIT_MASK 0x224     // bit 6 just #define MYPORT_OUT_BIT_MASK 224    // bit 6?

also, what about the attachInterrupt()? Or am I missing how the 1 MHz triggers anything?

0x is used for hexadecimal notation,

think I made an error there too in my sample code I'll check it. Did I use 0x64 or 64?

-- update -- yeah my bug, should be 64 or 0x40 , patched the code above. but you got the idea!