High speed timing (1uS) with 500 mS delay.

High speed timer.

Im toying with this code :

//
// Use of timer2 to generate a signal for a particular frequency on pin 11
//
// davekw7x
//

const int freqOutputPin = 11;   // OC2A output pin for ATmega328 boards
//const int freqOutputPin = 10; // OC2A output for Mega boards

// Constants are computed at compile time

// If you change the prescale value, it affects CS22, CS21, and CS20
// For a given prescale value, the eight-bit number that you
// load into OCR2A determines the frequency according to the
// following formulas:
//
// With no prescaling, an ocr2val of 3 causes the output pin to
// toggle the value every four CPU clock cycles. That is, the
// period is equal to eight slock cycles.
//
// With F_CPU = 16 MHz, the result is 2 MHz.
//
// Note that the prescale value is just for printing; changing it here
// does not change the clock division ratio for the timer!  To change
// the timer prescale division, use different bits for CS22:0 below
const int prescale  = 1;
const int ocr2aval  = 3;
// The following are scaled for convenient printing
//

// Period in microseconds
const float period    = 2.0 * prescale * (ocr2aval+1) / (F_CPU/1.0e6);

// Frequency in Hz
const float freq      = 1.0e6 / period;

void setup()
{
   pinMode(freqOutputPin, OUTPUT);
   Serial.begin(9600);

   // Set Timer 2 CTC mode with no prescaling.  OC2A toggles on compare match
   //
   // WGM22:0 = 010: CTC Mode, toggle OC
   // WGM2 bits 1 and 0 are in TCCR2A,
   // WGM2 bit 2 is in TCCR2B
   // COM2A0 sets OC2A (arduino pin 11 on Uno or Duemilanove) to toggle on compare match
   //
   TCCR2A = ((1 << WGM21) | (1 << COM2A0));

   // Set Timer 2  No prescaling  (i.e. prescale division = 1)
   //
   // CS22:0 = 001: Use CPU clock with no prescaling
   // CS2 bits 2:0 are all in TCCR2B
   TCCR2B = (1 << CS20);

   // Make sure Compare-match register A interrupt for timer2 is disabled
   TIMSK2 = 0;
   // This value determines the output frequency
   OCR2A = ocr2aval;

   Serial.print("Period    = ");
   Serial.print(period);
   Serial.println(" microseconds");
   Serial.print("Frequency = ");
   Serial.print(freq);
   Serial.println(" Hz");
}


void loop()
{
   // Do (almost) anything you want here.  Just don't do analogWrite to
   // pins controlled by Timer 2.  In fact, don't do anything that messes
   // with the registers in Timer 2.
}

It produces 2MHz 50% square wave, but thats easy to change. But i dont know how to generate a uS pulse with a 500 mS delay. i know i cant use digitalwrite as it takes about 10uS to execute.

Ive included two pics to further explain what id like.

waveform.png

The 500 ms interval is easily done using the standard millis() timing.

The 1 µs pulse is harder - at 16 MHz that's a mere 16 clock cycles. Timer interrupts will have to play a major role in pulling that off, and you'll have to ditch the digitalRead() function and go for direct port writes. I see in your code you're playing with timer interrupts already, that's a good start.

Basically every 500 ms you call a function that resets the timer, sets the interrupt value, then sets the pin high (use a PORTx register write).

In the timer interrupt ISR you set the pin LOW (again a PORT write), and then clear the interrupt.

At 16 MHz that's 16 counts (no prescaler) per µs, with an offset to correct for the delay between setting the timer and setting the port. That'll be a few counts extra.

The 16 clock ticks you have should be more than enough for calling the interrupt and setting the port. Order of those manipulations matters here! Set the timer interrupt first, only then the pin to HIGH. Otherwise you would need some of those 16 clock ticks for setting the interrupt, you don't want to lose anything of that 1 µs of processor time.

The calling of an ISR normally takes 3 or 4 clock ticks (see datasheet for details), the setting of a PORT register should be 1 tick. If you set the timer first, you just add the ticks between that and the PORTx being set to the 16.

It's not so hard to use the timer hardware for the 1µs pulse. Set up a timer for a convenient clock rate and mode, that can generate 1µs pulses. Then, after the 500ms delay, start the timer and enable the signal output and disable it again when the pulse was generated. Even with an 8 bit counter and 1µs timer clock you have up to 256*1µs time to disable the output after the pulse in code. That's sufficient to poll the timer registers for the pulse done condition.

This'll get you down to one-clock-cycle pulse widths - The Perfect Pulse- generating precise one-shots on AVR8 | josh.com

with this code :

// More info about this program is here...
// http://wp.josh.com/2015/03/05/the-perfect-pulse-some-tricks-for-generating-precise-one-shots-on-avr8/

// Demo of a technique to generate narrow and precise one shot pulses using a 
// timer module on an AVR. This demo code is writen for an Arduino and uses
// the Timer2 moudle, but this techniquie should would on other AVRs and timers. 

// The demo generates one pulse per second. 
// The 1st pulse is 0 cycles long (no pulse), 
// The 2nd pulse is 1 cycle long (~63ns),
// The 3rd pulse is 2 cycles long (~126ns), 
// ...up to a total of 20 pulses, and then starts over.

// The one shot pulses are output on Digial pin 3



#define OSP_SET_WIDTH(cycles) (OCR2B = 0xff-(cycles-1))

// Setup the one-shot pulse generator and initialize with a pulse width that is (cycles) clock counts long

void osp_setup(uint8_t cycles) {


  TCCR2B =  0;      // Halt counter by setting clock select bits to 0 (No clock source).
              // This keeps anyhting from happeneing while we get set up

  TCNT2 = 0x00;     // Start counting at bottom. 
  OCR2A = 0;      // Set TOP to 0. This effectively keeps us from counting becuase the counter just keeps reseting back to 0.
          // We break out of this by manually setting the TCNT higher than 0, in which case it will count all the way up to MAX and then overflow back to 0 and get locked up again.
  OSP_SET_WIDTH(cycles);    // This also makes new OCR values get loaded frm the buffer on every clock cycle. 

  TCCR2A = _BV(COM2B0) | _BV(COM2B1) | _BV(WGM20) | _BV(WGM21); // OC2B=Set on Match, clear on BOTTOM. Mode 7 Fast PWM.
  TCCR2B = _BV(WGM22)| _BV(CS20);         // Start counting now. WGM22=1 to select Fast PWM mode 7

  DDRD |= _BV(3);     // Set pin to output (Note that OC2B = GPIO port PD3 = Arduino Digital Pin 3)
}

// Setup the one-shot pulse generator

void osp_setup() {

  osp_setup(1);

}

// Fire a one-shot pulse. Use the most recently set width. 

#define OSP_FIRE() (TCNT2 = OCR2B - 1)

// Test there is currently a pulse still in progress

#define OSP_INPROGRESS() (TCNT2>0)

// Fire a one-shot pusle with the specififed width. 
// Order of operations in calculating m must avoid overflow of the unint8_t.
// TCNT2 starts one count lower than the match value becuase the chip will block any compare on the cycle after setting a TCNT. 

#define OSP_SET_AND_FIRE(cycles) {uint8_t m=0xff-(cycles-1); OCR2B=m;TCNT2 =m-1;}


void setup()
{
  osp_setup();

}

void loop()
{
  // Step though 0-19 cycle long pulses for demo purposes 

  for (uint8_t o = 0; o < 20; o++) {

    OSP_SET_AND_FIRE(o);

    while (OSP_INPROGRESS());         // This just shows how you would wait if nessisary - not nessisary in this application. 

    _delay_ms(1000);      // Wait a sec to let the audience clap

  }


}

ive been able to duplicate "bigjosh" result in a expanding fast wave as seen in this gif :

now i need to remove the repeating expanding part and be able to modify the pulse width from 1 to 200 or so uS, though the pulse width in uS will be a fixed feature (eventually) and can then be a permanent part of the sketch.

Have you read the detailed explanation of the code. It shows how to set the one shot.

cattledog:
Have you read the detailed explanation of the code. It shows how to set the one shot.
https://wp.josh.com/2015/03/12/avr-timer-based-one-shot-explained/

im trying too. but keep in mind im returning to the coding after 15 years. i only took 3 semesters of c++ and wasnt that good at it.

embedded systems are becoming more essential to my work.

Theres a lot of unnecessary stuff im weeding through too, that's a lot of code to prune and not kill it totally.

Perhaps you could use this as a guide. I made these modifications for testing input capture on another UNO.

// nanosecond pulser

// one-shot code https://wp.josh.com/2015/03/05/the-perfect-pulse-some-tricks-for-generating-precise-one-shots-on-avr8/
// Pulses provided by the one-shot code from Josh
// This sketch's only purpose is to generate variable width pulses for testing.

//  Pin 3 (output) of pulse generator, this UNO, wired to pin 8 (input) capture UNO
// Connect UNO grounds together!
//--------------------------

#define OSP_SET_WIDTH(cycles) (OCR2B = 0xff-(cycles-1))
#define flasherLED 13
unsigned long timeToPulse;

// pushbutton variables

const int  incrbuttonPin = 10;    // the pin that the pushbutton is attached to
int incrbuttonPushCounter = 0;   // counter for the number of button presses
int incrbuttonState = 0;         // current state of the button
int incrlastbuttonState = 0;     // previous state of the button
bool incrChanged;

const int  decrbuttonPin = 9;    // the pin that the pushbutton is attached to
int decrbuttonPushCounter = 0;   // counter for the number of button presses
int decrbuttonState = 0;         // current state of the button
int decrlastbuttonState = 0;     // previous state of the button
bool decrChanged;

unsigned char widthBottomLimit = 0;
unsigned char widthTopLimit = 15;
int8_t widthIndex;
uint8_t count;
uint8_t widthTable[] {60, 70, 80, 90, 100, 160, 180, 190, 220, 225, 230, 235, 240, 245, 250};

//--------------------------------------------------------------------
// One shot generator vars / setup

unsigned char o = widthTable[widthIndex]; // generate a pulse of 'o' clock cycles
// 35 seems to be the absolute minimum resolution

unsigned int t; // measured pulse time.

// Setup the one-shot pulse generator and initialize with a pulse width that is (cycles) clock counts long

void osp_setup(uint8_t cycles) {

  TCCR2B =  0;      // Halt counter by setting clock select bits to 0 (No clock source).
  // This keeps anyhting from happening while we get set up

  TCNT2 = 0x00;     // Start counting at bottom.
  OCR2A = 0;      // Set TOP to 0. This effectively keeps us from counting becuase the counter just keeps reseting back to 0.
  // We break out of this by manually setting the TCNT higher than 0, in which case it will count all the way up to
  // MAX and then overflow back to 0 and get locked up again.
  OSP_SET_WIDTH(cycles);    // This also makes new OCR values get loaded from the buffer on every clock cycle.

  TCCR2A = _BV(COM2B0) | _BV(COM2B1) | _BV(WGM20) | _BV(WGM21); // OC2B=Set on Match, clear on BOTTOM. Mode 7 Fast PWM.
  TCCR2B = _BV(WGM22) | _BV(CS20);        // Start counting now. WGM22=1 to select Fast PWM mode 7

  DDRD |= _BV(3);     // Set pin to output (Note that OC2B = GPIO port PD3 = Arduino Digital Pin 3)
}

// Setup the one-shot pulse generator

void osp_setup() {
  osp_setup(1);
}

// Fire a one-shot pulse. Use the most recently set width.

#define OSP_FIRE() (TCNT2 = OCR2B - 1)

// Test there is currently a pulse still in progress

#define OSP_INPROGRESS() (TCNT2>0)

// Fire a one-shot pulse with the specified width.
// Order of operations in calculating m must avoid overflow of the uint8_t.
// TCNT2 starts one count lower than the match value becuase the chip will block any compare on the cycle after setting a TCNT.

#define OSP_SET_AND_FIRE(cycles) {uint8_t m=0xff-(cycles-1); OCR2B=m;TCNT2 =m-1;}

//-------------------------------------  S E T U P  --------------------------------
void setup() {
  Serial.begin (115200);
  pinMode(flasherLED, OUTPUT);       // telltele LED to indicate activity
  pinMode(10, INPUT_PULLUP); // increment pushbutton
  pinMode(9, INPUT_PULLUP); // decrement pushbutton
  //  pinMode(3,OUTPUT);
  osp_setup(); // got to have this!
  Serial.print("Finished setup\r\n");
  
}

//---------------------------------------- L O O P -------------------------------
void loop() {
  // one second timer
  if (millis() - timeToPulse >= 1000UL) { // send a pulse on timer done
    o = widthTable[widthIndex];
    OSP_SET_AND_FIRE(o); // Generate a one-shot with the width, in cycles, specified by o.
    timeToPulse = millis(); // timer reset
    digitalWrite(flasherLED, !(digitalRead(flasherLED)));
    Serial.print("width ");
    Serial.print(o);

    Serial.print(" \tcount ");
    Serial.println (count++);
    //    Serial.println(digitalRead(flasherLED));
  }
  // debounce two switches to increment/decrement the width index
  incrementWidth();
  decrementWidth();
}

void incrementWidth() {
  // read the pushbutton input pin:
  incrbuttonState = digitalRead(incrbuttonPin);
  // compare the buttonState to its previous state
  if (incrbuttonState != incrlastbuttonState) {
    // if the state has changed, increment the counter
    if (incrbuttonState == HIGH) {
      // if the current state is HIGH then the button
      // wend from off to on:
      widthIndex++;
      if (widthIndex > 14) widthIndex = 14;

    } else {
      // if the current state is LOW then the button
      // wend from on to off:
    }
    // Delay a little bit to avoid bouncing
    delay(50);
  }
  // save the current state as the last state,
  //for next time through the loop

  // decrement button
  incrlastbuttonState = incrbuttonState;
}

void decrementWidth() {
  // read the pushbutton input pin:
  decrbuttonState = digitalRead(decrbuttonPin);

  // compare the buttonState to its previous state
  if (decrbuttonState != decrlastbuttonState) {
    // if the state has changed, decrement the counter
    if (decrbuttonState == HIGH) {
      // if the current state is HIGH then the button
      // went from off to on:
      widthIndex--;
      if (widthIndex < 0) widthIndex = 0;
    } else {
      delay(50);
    }
    // save the current state as the last state,
    //for next time through the loop
    decrlastbuttonState = decrbuttonState;
  }}

Yes that helps produce down to 2.5uS, which I can live with, I can increment up from there by nS and uS.

Now i just need to repeat every 500mS or so. so it needs to be monostable. There's alot of code im trying to wade through.

PatrickZ:
Yes that helps produce down to 2.5uS, which I can live with, I can increment up from there by nS and uS.

Wouldn't a preset of 16 yield exactly 1µs?

dougp:
Wouldn't a preset of 16 yield exactly 1µs?

yes it did, so now on to the repeating part.

here is a pic of the overall setup so far. Ive been able to get 200 and 300 nS pulses pretty easy now. but i need to have an adjustable repetition frequency on the order of mS.

Well, the 500 ms repeat is of course trivial; basic millis() based timing.

if (millis() - lastPulse > 500) {
  lastPulse = millis();
  doPulse();
}

How do i disable that "growing" pulse loop part. i keep getting errors.

How do i disable that "growing" pulse loop part.

Wouldn't a preset of 16 yield exactly 1µs?

yes it did, so now on to the repeating part.

What growing pulse loop? Please post your code.

cattledog:
What growing pulse loop? Please post your code.

 // More info about this program is here...
// http://wp.josh.com/2015/03/05/the-perfect-pulse-some-tricks-for-generating-precise-one-shots-on-avr8/

// Demo of a technique to generate narrow and precise one shot pulses using a
// timer module on an AVR. This demo code is writen for an Arduino and uses
// the Timer2 moudle, but this techniquie should would on other AVRs and timers.

// The demo generates one pulse per second.
// The 1st pulse is 0 cycles long (no pulse),
// The 2nd pulse is 1 cycle long (~63ns),
// The 3rd pulse is 2 cycles long (~126ns),
// ...up to a total of 20 pulses, and then starts over.

// The one shot pulses are output on Digial pin 3



#define OSP_SET_WIDTH(cycles) (OCR2B = 0xff-(cycles-1))

// Setup the one-shot pulse generator and initialize with a pulse width that is (cycles) clock counts long

void osp_setup(uint8_t cycles) {


  TCCR2B =  0;      // Halt counter by setting clock select bits to 0 (No clock source).
              // This keeps anyhting from happeneing while we get set up

  TCNT2 = 0x00;     // Start counting at bottom.
  OCR2A = 0;      // Set TOP to 0. This effectively keeps us from counting becuase the counter just keeps reseting back to 0.
          // We break out of this by manually setting the TCNT higher than 0, in which case it will count all the way up to MAX and then overflow back to 0 and get locked up again.
  OSP_SET_WIDTH(cycles);    // This also makes new OCR values get loaded frm the buffer on every clock cycle.

  TCCR2A = _BV(COM2B0) | _BV(COM2B1) | _BV(WGM20) | _BV(WGM21); // OC2B=Set on Match, clear on BOTTOM. Mode 7 Fast PWM.
  TCCR2B = _BV(WGM22)| _BV(CS20);         // Start counting now. WGM22=1 to select Fast PWM mode 7

  DDRD |= _BV(3);     // Set pin to output (Note that OC2B = GPIO port PD3 = Arduino Digital Pin 3)
}

// Setup the one-shot pulse generator

void osp_setup() {

  osp_setup(1);

}

// Fire a one-shot pulse. Use the most recently set width.

#define OSP_FIRE() (TCNT2 = OCR2B - 1)

// Test there is currently a pulse still in progress

#define OSP_INPROGRESS() (TCNT2>0)

// Fire a one-shot pusle with the specififed width.
// Order of operations in calculating m must avoid overflow of the unint8_t.
// TCNT2 starts one count lower than the match value becuase the chip will block any compare on the cycle after setting a TCNT.

#define OSP_SET_AND_FIRE(cycles) {uint8_t m=0xff-(cycles-1); OCR2B=m;TCNT2 =m-1;}


void setup()
{
  osp_setup();

}

void loop()
{
  // Step though 0-19 cycle long pulses for demo purposes

  for (uint8_t o = 0; o < 20; o++) {

    OSP_SET_AND_FIRE(o);

    while (OSP_INPROGRESS());         // This just shows how you would wait if nessisary - not nessisary in this application.

    _delay_ms(1000);      // Wait a sec to let the audience clap

  }


}

So how does that code not work? What errors? The only odd thing I see is that you don't use the normal delay() command but something else.

How do i disable that "growing" pulse loop part.

The for() statement in loop is stepping through pulse widths

void loop()
{
  // Step though 0-19 cycle long pulses for demo purposes

  for (uint8_t o = 0; o < 20; o++) {

    OSP_SET_AND_FIRE(o);

    while (OSP_INPROGRESS());         // This just shows how you would wait if nessisary - not nessisary in this application.

    _delay_ms(1000);      // Wait a sec to let the audience clap

  }

For a 500 ms repeating 1 microsecond pulse all the code that you have in loop() with

 static unsigned long lastPulse = 0;
if (millis() - lastPulse >= 500) { // send a pulse on timer done
    lastPulse = millis(); // timer reset
    uint8_t o = 16;
    OSP_SET_AND_FIRE(o); // Generate a one-shot with the width, in cycles, specified by o.
  }

If this gives you what you want, we can get you serial input for o, or for testing, just change the value of o and restart the program

cattledog:

For a 500 ms repeating 1 microsecond pulse all the code that you have in loop() with

static unsigned long lastPulse = 0;
if (millis() - lastPulse >= 500) { // send a pulse on timer done
    lastPulse = millis(); // timer reset
    uint8_t o = 16;
    OSP_SET_AND_FIRE(o); // Generate a one-shot with the width, in cycles, specified by o.
  }




If this gives you what you want, we can get you serial input for o, or for testing, just change the value of o and restart the program

Yes excellent this works well.

i know how to set timer 1 for uS from 1 and longer pulses from the prescaler , but i dont see that in the followig code:

#define OSP_SET_WIDTH(cycles) (OCR2B = 0xff-(cycles-1))

// Setup the one-shot pulse generator and initialize with a pulse width that is (cycles) clock counts long

void osp_setup(uint8_t cycles) {


  TCCR2B =  0;      // Halt counter by setting clock select bits to 0 (No clock source).
              // This keeps anyhting from happeneing while we get set up

  TCNT2 = 0x00;     // Start counting at bottom.
  OCR2A = 0;      // Set TOP to 0. This effectively keeps us from counting becuase the counter just keeps reseting back to 0.
          // We break out of this by manually setting the TCNT higher than 0, in which case it will count all the way up to MAX and then overflow back to 0 and get locked up again.
  OSP_SET_WIDTH(cycles);    // This also makes new OCR values get loaded frm the buffer on every clock cycle.

  TCCR2A = _BV(COM2B0) | _BV(COM2B1) | _BV(WGM20) | _BV(WGM21); // OC2B=Set on Match, clear on BOTTOM. Mode 7 Fast PWM.
  TCCR2B = _BV(WGM22)| _BV(CS20);         // Start counting now. WGM22=1 to select Fast PWM mode 7

  DDRD |= _BV(3);     // Set pin to output (Note that OC2B = GPIO port PD3 = Arduino Digital Pin 3)
}

i know how to set timer 1 for uS from 1 and longer pulses from the prescaler ,but i don't see that in the following code:

I'm not certain what you mean by this.

The one shot timer code is based on Timer2. I believe it could be rewritten to use Timer1 if Timer2 is required for some other purpose.

This code uses no timer prescaler, so every timer count is .0625 microseconds. That's why you are using the number 16 for cycles. If you wanted to use the prescaler 16 to make each tick 1 microsecond, you could refigure the code to do that, but I don't see any advantage to that unless you want significantly longer pulses.