Ultra short pwm

Hi!
Sorry I am somewhat new to Arduino, but I can't seem to find anywhere this question/answer.

Anyways I want to build a square signal generator, with a circa 1 Hz signal, but the tricky part is I want to have an ultra-short signal of like 100 microseconds so that makes PWM of 0.01%. As far as I am aware it is impossible using TimerOne library.

Only thing that comes to my mind is using delayMicrosecond for high state and regular delay for the low state, but this method is somewhat clunky if I want to change my PWM/signal period on the fly. And I am not even sure if Arduino can handle it since my new Arduino arrives in a few days, so I can't test it just yet. (Yeah, I am somewhat impatient to test it).

Is there any other method/library that can use pwm of 0.01%?

Sorry if a question is somewhat stupid, as I said, new to Arduino.

The thing about a 1 Hz signal is that it takes 1 full second to make.

What you seem to want is one 100 usec blink/pulse every 1 second?

Instead of using delays, how about using micros() timing to be rounded-to-the-nearest-four-microseconds close? 1/250 ms grain.
And that's only as good as the Arduino clock; crystal or resonator.

Time math is done with unsigned longs (starting out at least) and millis() or micros() values. Millis returns ms since startup +/- 1 and micros returns us (microsecs) to the nearest 4 since startup.

The math that works every time (within 1 full turn of that clock -- unsigned long millis is 49.71... days and unsigned long micros is over 70 minutes.. in 4 microsec steps!

end time - start time = elapsed time

if ( micros() - startTime <= interval ) // executes during an interval

if ( micros() - startTime >= interval ) // executes after an interval

The code those are used in goes back to void loop that runs again, checks the time again with startTime and interval changeable.

What you get is the ability to on-time down to small scale.
You also get code that can run with dozens of other tasks smoothly.

1 Like

I think Timer1 can do this

I used David Buezas' Web Timers Calculator to set timer1 to a one second frequency and turn on two interrupts.
One interrupt to turn on a pin and the second interrupt to turn it off.

This will get you a brief pulse every second. Lenght of pulse is now an arbitrary 500. Set it to less than 100 and you can't see the led turning on (at least I can't see it then anymore).

/**
    URL: https://dbuezas.github.io/arduino-web-timers/#mcu=ATMEGA328P&timer=1&clockPrescalerOrSource=256&interruptA=on&OCR1A=62493&interruptB=on&OCR1B=61248&topValue=OCR1A&CompareOutputModeA=disconnect
    Mode     : CTC
    Period   : 999.904 ms
    Frequency: 1.0001 Hz
    Outputs  : none
*/
void setup(){
  TCCR1A = 0; // just to undo some Arduino core initialization stuff
  TCCR1B = 1 << WGM12 | 1 << CS12;
  TIMSK1 = 1 << OCIE1A | 1 << OCIE1B;
  OCR1A = 62493;// change this to alter the frequency
  OCR1B = OCR1A - 500; // change this value on the fly to get a longer or shorter pulse
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop(){
}

ISR(TIMER1_COMPA_vect) {
  /* on OCR0A match */
  digitalWrite(LED_BUILTIN, LOW);
}
ISR(TIMER1_COMPB_vect) {
  /* on OCR0B match */
  digitalWrite(LED_BUILTIN, HIGH);
}

This may disable other Arduino functions that rely on timer1.
I think Servo and Analogwrite on pins 9 and 10 won't work anymore.

How exact must it be?

It's everything but stupid. :+1:

Hold the led in the dark and a very brief led flash is visible.
But 100 microseconds may be too faint even for adjusted to dark eyes.

What isn't visible is 990 ms ON the 10 ms OFF. 1% less is below sensory change threshold, test that and be sure.

But a flash in the dark gets the persistence of vision that masks brief OFF periods during long ON periods, a longer sensation than the duration of the flash. We see motion at 24 FPS. 25 FPS is 40 ms per frame, that's how slow eyes are!

I haven't tried it, but it isn't clear to me that this can't be done with the TimerOne library.

Something like

#include "TimerOne.h"

void setup()
{
  pinMode(10, OUTPUT);
  Timer1.initialize(1000000);     // initialize timer1, and set a 1 second period
  Timer1.setPeriod(100);          // Set timer1 "on" period to 100 us
}

Maybe not with that library, but you can certainly do it with the 16 bit Timer 1. How you do it depends on how exact you need the timing.

There are three options.

You can use the method in #2 and just use millis and micros. This would be the easiest, but the least accurate.

You can use millis to time out the 1 second interval and use Timer1 to create a one shot 100uS pulse. That would be pretty easy and pretty accurate.

You could use Timer 1 to creat the 100uS pulse and the 999900uS off time too but this would require setting up an interrupt to change the prescaler on Timer1 with each transition. That would be extrememly accurate but would require you to learn a lot about the Timer and the code around it.

Nope -- Timer1.setPeriod(100) overwrites the 1Hz frequency and changes the frequency to 10000Hz. Timer1's OCR1A-setting functions (like timer1.pwm() operate at 1% resolution.

You could hijack it a bit and overwrite OCR1A to get 0.1% pwm

#include "TimerOne.h"

void setup()
{
  pinMode(10, OUTPUT);   /OC1B
  Timer1.initialize(1000000);     // initialize timer1, and set a 1 second period
  Timer1.pwm(pin,100);          // Set timer1 "on" period to 100 us
  Timer1.pwm(10,1);  // 1%  (Sets up to /256 , ICR1=31250, OCR1B=30)
  OCR1B /= 10;  // 0.1% (OC1B=3)
}

The smallest you could set TimerOne library for still have a signal is to override with OC1B=1, which using Timer1.setPeriod(1000000) is 320us, or a 0.032% duty cycle.

I think this - The Perfect Pulse- generating precise one-shots on AVR8 | josh.com - plays off @Delta_G's option #2.

1 Like

Here's an important question. What type of Arduino? So far we've all been talking about AVR style boards like the Uno or Mega.

2 Likes

Another trick would be to setup for a hardware oneshot per:

You could use millis() to set the one-shots and then get a 63ns pulse for 0.0000063% PWM

A hacked version of his 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++) {

   uint8_t o = 1;

    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

 // }


}

You could wrap OSP_SET_AND_FIRE(o); in a non-blocking millis()-guarded loop to set the frequency to whatever you want.

ETA: @dougp -- Yep.

Hi @tunaman

Here's how to generate a 1Hz PWM signal with 96us pulses on digital pin D9 using fast PWM mode (Arduino Uno):

// Set-up fast PWM on the Arduino UNO at 1Hz on Digital pin D9
void setup() { 
  pinMode(9, OUTPUT);                               // Set digital pin 9 (D9) to an output  
  TCCR1A = _BV(COM1A1) | _BV(WGM11);                // Enable the PWM output OC1A on digital pins 9
  TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS12);     // Set fast PWM and prescaler of 256 on timer 1
  ICR1 = 62499;                                     // Set the PWM frequency to 1Hz: 16MHz / (256 * 1Hz) - 1 = 62499
  OCR1A = 5;                                        // Set the pulse width to 96us
}

void loop() {}

At best, millis() is +/- 1 ms. If it has to be close, use micros().

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.