//
// 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.
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.
// 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.
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;
}}
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.
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
}
}
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
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.