How to output a pulse of specific width without using Delay

Hi everyone

I'm somewhat new to coding software but am experienced in logic-style electronics.

I would like to make a system which will output two pairs of independent pulse trains:

Pulse train 1 has 2 channels, each channel must put out 1 pulse with a width of 200uS, with a variable time until the next pulse on that channel (as specified by an analogue input). The 2nd channel on this pulse train will output a pulse of 200uS half way (given a constant cycle frequency at that point) between the pulses of the 1st channel, so I have two pulse channels, one with a 180 degree phase shift to the other. The distance between the pulses will always be significantly greater than 200uS, even up to 1 second, but possibly down to 2ms.

The 2nd pulse train is identical, only its variable speed is independent.

I have made a quadrature version (90 degree phase shift, 50% duty cycle at a constant frequency) which works fine.

My question is how can I make a pulse of 200uS wide, without delaying the whole code.

The reason is that one pulse train pair may be running fast, the other may be running slow. I can't have the slow one affecting the fast one by any significant means.

My initial thoughts are to set a timer (one timer per pulse train pair) so when a counter reaches value x, set the output high, set a timer to return an interrupt after 200uS, which sets that output low again.

Has anyone any ideas on this? Am I going down the correct path?

Personally I would set up one timer that ran at a granularity you were happy to work at - say 50uS (1/4 of a pulse width), and use that to keep track of a counter per pulse train. Those counters can be compared to different values to decide what state the pulses should be in at any given time.

For example, at counter 0 you turn the pulse on. At counter 2 you turn on pulse 2. At counter 4 you turn off pulse 1. At counter 6 you turn off pulse 2. At counter 427 you set counter to 0 so it starts the next pulse again, ad infinitum. Do the same for the other pulse train, etc.

"Blink without delay" ( see examples) does just this!

Mark

Blink without delay relies on millis(). Millis() can't do a 200uS pulse width.

For that kind of precision you need a timer and interrupt.

Blink without delay relies on millis().

No. It uses millis(), but the process relies on measuring intervals and taking action at the appropriate time. One could just as easily substitute micros() for millis() to achieve greater resolution of the "when" something happens.

That still might not be enough for OPs purpose.

majenko:
Blink without delay relies on millis(). Millis() can't do a 200uS pulse width.

For that kind of precision you need a timer and interrupt.

Or if no other task takes many cycles you could use micros() instead of millis() in a BlinkWithoutDelay pattern.

Some of the timer1 modes might be amenable to do the work - you can setup pins 9 and 10 to switch at
distinct threshold values and you can dynamically change the target count in certain modes cleanly. Datasheet
for all the grubby details.

He mentioned the use of the ADC... That won't be fast :wink:

analogRead() at best gives you 9k samples/sec, however if you look at the datasheet you find that there are ways of getting 80k samples/sec.

Mark

Like Mark said, Timer1 can do things like this, but it will mess up any PWM that you have on pins 9 and 10. It could also cause problems with some other libraries that might rely on Timer1. The "output compare" feature can toggle a pin at a precise future time. I believe you can make use of both output compare registers to create the entire pulse. Other features it has will allow you to precisely time the interval between the pulses on the incoming train asynchronously while you are doing other things. Timer1 rox. :wink:

holmes4:
analogRead() at best gives you 9k samples/sec, however if you look at the datasheet you find that there are ways of getting 80k samples/sec.

Mark

It's not the sampling speed that I am worrying about here - it's the fact that analogRead() is a blocking function. Completely messes with any synchronous timing.

it's the fact that analogRead() is a blocking function.

The analogRead() function is NOT the only way to read analog pins. Nick has a writeup on doing asynchronous reads, in a non-blocking fashion. That may, or may not, be appropriate in any given situation.

PaulS:

it's the fact that analogRead() is a blocking function.

The analogRead() function is NOT the only way to read analog pins. Nick has a writeup on doing asynchronous reads, in a non-blocking fashion. That may, or may not, be appropriate in any given situation.

Yes, I know that and you know that, but you and I aren't average Arduino users, are we? (We have grown hair on our chests.) But if you have that sort of knowledge already, why would you be asking about not using delay() as you'd already have those kind of answers already. Ergo, I would expect the OP to be using analogRead(). "I'm somewhat new to coding software" kind of sums it up.

Personally I'd use something a little better than an Atmel too, something that can do proper transformations and calculations, with decent OC and ADC modules - something like a dsPIC33FJxxMCxxx series, which is designed for getting data through it's 1.1Msps ADC and controlling motors and such through its high resoluion, fault tolerant OC modules. The internal DSP in there kind of helps do the calculations you may need to do, including single cycle 16x16->32 multiplications, etc.

Hi Guys

The millis() and micros() worked brilliantly. With a bit of manipulation I was able to get a 0.2ms pulse output with a variable frequency from 100Hz to 0.1Hz.

 rate_int = analogRead(rate);         // Read the Rate  Input and set the value to "rate_int"
  
 if (millis() >= (timer + rate_int))  // Checks the millis() time against the previous time + rate
 {
  timer = millis();                     // Set Timer to Millisecond Clock
   
  digitalWrite(ch_a1, LOW);              // Sets the Output Low for the pulse

  pulse = micros();                   // Sets the Start Time for Pulse
 }

 if (micros() >= (pulse + pulseWidth))   // If the micros function is larger than the pulse width (200uS),
 {
  digitalWrite(output, HIGH);         // Turn the output back to High
 }

PaulS:

it's the fact that analogRead() is a blocking function.

The analogRead() function is NOT the only way to read analog pins. Nick has a writeup on doing asynchronous reads, in a non-blocking fashion. That may, or may not, be appropriate in any given situation.

I got the impression from the ATMEL doc that running the CPU during ADC introduces noise.

I can tell you now that all CPU's make noise while running.

In my situation the speed at which it reads makes little odds.

As for the noise, I personally would clamp the input down harder, even drive it with an external Opamp to really hold it tight if noise was that much of an issue.

With a 1024 scale, that is almost 5mV per division. With the input hard-clamped you aren't going to get a 5mV noise on there unless your power supply is gash - which most are I am sorry to say.

The only time CPU noise should really cause an issue is when you are trying to receive RF signals. Often you will find a system (e.g. wireless router) has 2 aerials, one Tx/Rx near the processor and one extra Rx with its own RF to TTL processor far away from the main CPU.

PCB Earth plane is the stuff to use.

The ATMEL doc goes into details you could use. Lots of details if you're interested.

Go to this page:

Find and get this datasheet:
ATmega48A/PA/88A/PA/168A/PA/328/P Complete

Head's up forum! The 328P datasheet is updated this month (02/2013)! It's now 660 pages!