Here’s my code - I want to send out a burst of 336 pulses at 20KHz rate, with ability to change the duty cycle later to simulate the output of a high speed camera for testing another card.
Am seeing on a scope that the 20KHz is not even - instead I get periods from 44.77uS up to 50.75uS.
How can I improve that?
I need an even 50uS to allow another processer to trigger off the edges and set up 42 bytes into shift registers; the next rising edge of the 20KHz then clocks the data into the output stage of the shift register.
/* sketch to output burst of 336 pulses @ 20KHz rate,
simulating high speed camera trigger output
Need to support variable width as well.
50/50 to start.
Run on regular Arduino
*/
byte triggerIn = 2; // button push to start pulse train
byte triggerOut = 3; // waveform out
int pulses = 0; // number of pulses - actually counts # of transitions
byte pinState = 0; // high or low level of waveform
byte pulsing = 0; // pulse train active flag
unsigned long currentMicros;
unsigned long nextMicros;
unsigned long duration = 25UL; // flip every 50uS = 20KHz pulse
unsigned long elapsedMicros;
void setup(){
pinMode (triggerIn, INPUT);
digitalWrite (triggerIn, HIGH);
pinMode (triggerOut, OUTPUT);
digitalWrite(triggerOut, LOW);
Serial.begin(115200);
Serial.println("hello"); // sanity test
}
void loop(){
// Serial.println(pulsing);
if (pulsing == 0){ // not pulsing yet
if ( digitalRead(triggerIn) == LOW){ // read button to start
// Serial.println("starting");
pulsing = 1;
currentMicros = micros(); // get times caught up & current
nextMicros = currentMicros;
}
}
while (pulsing == 1){ // stay in loop until 336 pulses go out
currentMicros = micros(); // now blink without delay style code
elapsedMicros = currentMicros - nextMicros;
if (elapsedMicros >= duration){
nextMicros = nextMicros + duration;
pulses = pulses +1; // count pulses
/*
pinState = 1 - pinState; // keep track of output state
if (pinState == 1){
PORTD = PORTD | 0b00001000; // set the output
}
else {
PORTD = PORTD & 0b00000111; // clear the output, leave Serial & input pin alone
}
*/
// alternate method - neither seems to affect period tho
PIND = 0b00001000; // toggle IO pin
if (pulses == 672){ // all created?
pulses = 0; // reset count for next time
pulsing = 0; // turn off active flag
delay (500); // debounce button push that starts pulse train
}
}
}
}
Got this from the “Arduino Cookbook” by Margolis, seems to be more stable, now to incorporate it to make a burst of pulses.
#include <TimerOne.h>
#include <TimerOne.h>
#define pwmRegister OCR1A // the logical pin, can be set to OCR1B
const int outPin = 9; // the physical pin
long period = 50; // the period in microseconds - changed for 20KHz
long pulseWidth = 25; // width of a pulse in microseconds - changed to 50/50 duty cycle at 50 KHz
int prescale[] = {0,1,8,64,256,1024}; // the range of prescale values
void setup()
{
Serial.begin(9600);
pinMode(outPin, OUTPUT);
Timer1.initialize(period); // initialize timer1, 1000 microseconds
setPulseWidth(pulseWidth);
}
void loop()
{
}
bool setPulseWidth(long microseconds)
{
bool ret = false;
int prescaleValue = prescale[Timer1.clockSelectBits];
// calculate time per counter tick in nanoseconds
long precision = (F_CPU / 128000) * prescaleValue ;
period = precision * ICR1 / 1000; // period in microseconds
if( microseconds < period)
{
int duty = map(microseconds, 0,period, 0,1024);
if( duty < 1)
duty = 1;
if(microseconds > 0 && duty < RESOLUTION)
{
Timer1.pwm(outPin, duty);
ret = true;
}
}
return ret;
}
Can you help me work that in? Not quite sure how to count the pulses, and I think I have turning the PWM on/off here, maybe:
/* sketch to output burst of 336 pulses @ 20KHz rate,
simulating high speed camera trigger output
Need to support variable width as well.
50/50 to start.
Run on regular Arduino
Updated to use TimerOne library for better period control
*/
#include <TimerOne.h>
#define pwmRegister OCR1A // the logical pin, can be set to OCR1B
const int outPin = 9; // the physical pin
long period = 50; // the period in microseconds
long pulseWidth = 25; // width of a pulse in microseconds
int prescale[] = {
0,1,8,64,256,1024}; // the range of prescale values
byte triggerIn = 2; // button push to start pulse train
//byte triggerOut = 3; // waveform out
int pulses = 0; // number of pulses - actually counts # of transitions
byte pinState = 0; // high or low level of waveform
byte pulsing = 0; // pulse train active flag
int burstCount = 0;
int burstLength = 336;
//unsigned long currentMicros;
//unsigned long nextMicros;
//unsigned long duration = 25UL; // flip every 50uS = 20KHz pulse
//unsigned long elapsedMicros;
void setup(){
pinMode (triggerIn, INPUT);
digitalWrite (triggerIn, HIGH);
//pinMode (triggerOut, OUTPUT);
//digitalWrite(triggerOut, LOW);
pinMode(outPin, OUTPUT);
Timer1.initialize(period); // initialize timer1, 1000 microseconds
Serial.begin(115200);
Serial.println("hello"); // sanity test
}
void loop(){
if (pulsing == 0){ // not pulsing yet
if ( digitalRead(triggerIn) == LOW){ // read button to start
// Serial.println("starting");
pulsing = 1;
//currentMicros = micros(); // get times caught up & current
//nextMicros = currentMicros;
}
}
while (pulsing == 1){ // turn on PWM for 50uS * 336 = 16,800uS
setPulseWidth(pulseWidth);
while (..... >= burstLength){
setPulseWidth(0); // turn off PWM ??
pulsing = 0;
}
}
}
bool setPulseWidth(long microseconds){
bool ret = false;
int prescaleValue = prescale[Timer1.clockSelectBits];
// calculate time per counter tick in nanoseconds
long precision = (F_CPU / 128000) * prescaleValue ;
period = precision * ICR1 / 1000; // period in microseconds
if( microseconds < period)
{
int duty = map(microseconds, 0,period, 0,1024);
if( duty < 1)
duty = 1;
if(microseconds > 0 && duty < RESOLUTION)
{
Timer1.pwm(outPin, duty);
ret = true;
}
}
return ret;
}
/*
if (pulses == 672){ // all created?
pulses = 0; // reset count for next time
pulsing = 0; // turn off active flag
delay (500); // debounce button push that starts pulse train
}
*/
Sure, give me a bit to look at the TimerOne library, I haven't used it before. Let's see if it's easier to work with that or just program down to the bare metal.
Give this a try. I didn’t bother wiring up a button, am just generating the burst every 3 seconds.
//Arduino Uno, Arduino 1.0.5
//Output a given number of PWM cycles
#include <TimerOne.h>
const int OUTPUT_PIN = 9;
const unsigned long EVERY = 3000; //start the burst every this many milliseconds
const int NCYCLE = 336; //number of cycles to output
const unsigned long PERIOD = 25; //half a cycle in microseconds
const int DUTY = 512; //duty cycle as a number from 0 to 1023
void setup()
{
Timer1.attachInterrupt( timerIsr ); // attach the service routine here
}
void loop()
{
unsigned long ms = millis();
static unsigned long lastStart;
if (ms - lastStart >= EVERY) {
lastStart += EVERY;
Timer1.pwm(OUTPUT_PIN, DUTY, PERIOD);
}
}
void timerIsr()
{
static unsigned int toggleCount;
if (++toggleCount > NCYCLE * 2) {
Timer1.disablePwm(OUTPUT_PIN);
toggleCount = 0;
}
}
I think I’m getting close - I can get burst at 20 KHz, the burst duration is never the same tho.
336 * .00005 = 16.8mS, I’m getting seemingly random amounts, 1mS up to 16+, but not quite 16.8
/* sketch to output burst of 336 pulses @ 20KHz rate,
simulating high speed camera trigger output
Need to support variable width as well.
50/50 to start.
Run on regular Arduino
Updated to use TimerOne library for better period control
*/
#include <TimerOne.h>
#define pwmRegister OCR1A // the logical pin, can be set to OCR1B
const int outPin = 9; // the physical pin of the PWM output
long period = 50; // the period in microseconds
long pulseWidth = 25; // width of a pulse in microseconds
int prescale[] = {
0,1,8,64,256,1024}; // the range of prescale values
byte triggerIn = 2; // button push to start pulse train
volatile int toggleCount = 0; // number of pulses - actually counts # of transitions
volatile byte pulsing = 0; // pulse train active flag
void setup(){
pinMode (triggerIn, INPUT);
digitalWrite (triggerIn, HIGH);
pinMode(outPin, OUTPUT);
Timer1.initialize(period); // initialize timer1, 1000 microseconds
Timer1.attachInterrupt (countPulses); // attaches countPulses() as a timer overflow interrupt
Serial.begin(115200);
Serial.println("hello"); // sanity test
}
void countPulses(){ // ISR to count pulses
toggleCount = toggleCount +1;
if (toggleCount == 336) {
Timer1.disablePwm(outPin);
toggleCount = 0;
pulsing = 0;
}
}
void loop(){
if (pulsing == 0){ // not pulsing yet
Timer1.disablePwm(outPin); // just in case
if ( digitalRead(triggerIn) == LOW){ // read button to start
Serial.println("starting + debounce delay");
delay(250);
pulsing = 1;
setPulseWidth(pulseWidth);
}
}
}
bool setPulseWidth(long microseconds){
bool ret = false;
int prescaleValue = prescale[Timer1.clockSelectBits];
// calculate time per counter tick in nanoseconds
long precision = (F_CPU / 128000) * prescaleValue ;
period = precision * ICR1 / 1000; // period in microseconds
if( microseconds < period)
{
int duty = map(microseconds, 0,period, 0,1024);
if( duty < 1)
duty = 1;
if(microseconds > 0 && duty < RESOLUTION)
{
Timer1.pwm(outPin, duty);
ret = true;
}
}
return ret;
}