Maximum Output Frequencies on "Due"

Hi,

I just got my "One" and tried to generate a square wave on an output pin with "configurable" frequency. On my Scope I soon realized that something like this

loop() {
digitalWrite(pulserOPin, LOW);
digitalWrite(pulserOPin, HIGH);
}

gives 2 pulses each 4 us long plus some 2.6 us "loop()-overhead", so the duty cycle is not 50:50 und the frequ. is limited below 100 kHz.

  1. Can anybody tell if there is benefit to switch over to "due" since it runs at much higher clock freq. ? Can I produce higher frequencies of have at least more "programming overhead" due to higher processor clock ?
  2. What would be the output frequency and duty cycle of the above program on the "due"?
  3. Is there a "timebase" such as micros() with higher resolution than 1us on the due?
  4. How many instructions can the "due" process during a 1 us ?

Any comments are highly appreciated. Thanks
Chris

p.s.: FinalIy I want to be able to instantiate several of these "Generator" objects in order to generate several different frequencies on different outputs. Some code to give the idea below…and please stop saying "don't program OO in order to be fast" :slight_smile:

////////////////////////////////////////////////////
// Generator
////////////////////////////////////////////////////

Generator::Generator(int _outPin, int _freq, float _duty) :
  outPin(_outPin), freq(_freq), duty(_duty), state(LOW), previousMicros(0) {

  pinMode(outPin, OUTPUT);

  onTime_us =  1.e6/float(freq) * duty;
  offTime_us = 1.e6/float(freq) * (1.f-duty);

  ser_printf("Generator::onTime_us %d\n",onTime_us);   
  ser_printf("Generator::offTime_us %d\n",offTime_us);   
  //ser_printf("Generator::frequ %f\n",1.f/(onTime_us+offTime_us));   
}

bool Generator::process(bool enable) {
  if(!enable) {
    state = LOW;
    digitalWrite(outPin, LOW);
    return(LOW);
  }
  
  unsigned long currentMicros = micros();

  if(state == LOW) {
    if(currentMicros - previousMicros >= offTime_us) {
      previousMicros = currentMicros;
      state = HIGH;
      digitalWrite(outPin, HIGH);
    }
  } else {
    if(currentMicros - previousMicros >= onTime_us) {
      previousMicros = currentMicros;
      state = LOW;
      digitalWrite(outPin, LOW);
    }
  }
  return(state);    
}

If you want fast, you shouldn't be using digitalWrite.

As fast as it gets:

void setup(){
 pinMode(13,OUTPUT);
}//setup()

void loop(){
  while(1) bitSet(PINB, 5);
}//loop()

thanks for the replies so far, but…

my question was not on how to toggle as fast as possible but rather whether the time resolution of the "due" is better than that of the "one" and if so how much.

I just would be happy if anybody simply could answer to my 4 clear questions at thread opening.

Thanks
Chris

  1. Yes, yes
  2. Don't know
  3. No
  4. It depends

chnbr:
I just would be happy if anybody simply could answer to my 4 clear questions at thread opening.

Doing a square wave in a loop is just not the right way of doing it. Use a timer, that will give you an output of up to 8 MHz (a toggle every 62.5 nS).

There is no point in switching to a Due for that.

Your other questions are irrelevant if you use a timer.

Hi,

thanks for the link. I did some study of the ATmega manual and when I finally understood what's going on, I came up with the code below which is exactly what I needed... a pulse width modulated "high" freq. pulsing with variable phase delay after an trigger pulse :wink:

I am really impressed by the possibilities of such a cheap hardware. Really great !

Regards
Chris

const long prescaler = 64L;
long freq_trigger = 7500L;  // Hz
long freq_pulse = 45000L;   // Hz
int  duty_pulsetrain = 10;  // 0...100 [%]
int  phase_delta=10;        // 0...100 [%]

ISR (PCINT2_vect) {
  // if pin 3 now high, turn on toggling of OC1A on compare
  if (PIND & _BV (3)) {
    TCCR1A |= _BV (COM1A0) ;  // Toggle OC1A on Compare Match
  } else  {
    TCCR1A &= ~_BV (COM1A0) ;  // DO NOT Toggle OC1A on Compare Match
  }
}  


void setup() {
  pinMode (5, OUTPUT);  // OC0B
  pinMode (3, OUTPUT);  // OC2B
  pinMode (9, OUTPUT);  // OC1B

  // set up the LCD's number of columns and rows: 
  lcd.begin(20, 4);  
  lcd.clear();

  GTCCR = (1<<TSM)|(1<<PSRASY)|(1<<PSRSYNC); // halt timers

  // Timer 0
  // Pin 5 (OC0B) -> trigger Pulse (1us every 1/2000kHz)
  // intended for inverter
  TCCR0A = _BV (WGM00) | _BV (WGM01) | _BV (COM0B1); // fast PWM Mode
  TCCR0B = _BV (WGM02) | _BV (CS00)  | _BV (CS01);  // prescaler 64
  OCR0A = 16000000L / freq_trigger / prescaler - 1;
  OCR0B = 1;

  // set up Timer 1 (OC1B)
  // PIN 9 (OC1B) - gives us 50 KHz pulses 
  TCCR1A = _BV(COM1A0); 
  TCCR1B = _BV(WGM12) | _BV (CS10);   // CTC, No prescaler
  OCR1A =  (16000000L / freq_pulse / 2) - 1;  // zero relative, f=1/(62.5ns * OCR1A)
  
  // Set up Timer 2
  // Pin 3 (OC2B) -> pulse train envelope
  // helper signal (envelope for pulse train bursts)
  TCCR2A = _BV (WGM20) | _BV (WGM21) | _BV (COM2B1);   // Fast PWM mode
  TCCR2B = _BV (WGM22) | _BV (CS22) ;  // prescaler of 64
  OCR2A = 16000000L / freq_trigger / prescaler - 1; // frequency
  OCR2B = OCR2A * duty_pulsetrain / 100; // duty cycle from 0...OCR2A

  TCNT0 =  0; // sync timers
  TCNT2 =  0; // offset of 1 for OCRA as TOP timer 
  GTCCR =  0; // restart timers
  
  // pin change interrupt
  PCMSK2 |= _BV (PCINT19);  // want pin 3
  PCIFR  |= _BV (PCIF2);    // clear any outstanding interrupts
  PCICR  |= _BV (PCIE2);    // enable pin change interrupts for D0 to D7
  
}

// in loop() I change the frequ., duty and phase parameters with POTs, which is not interesting the part here…

please note that in the code above there are some wrong numbers in the comments. That is because during development the frequency settings changed but the comments stayed.. (as always :wink: ).