Why does my wave form generator using interrupts max out at 3.8KHz.

I’m trying to generate four wave forms (sine, square, saw, triangle) on a standard 328. I’ve already posted the code on the thread titled “Fast PWM - Compare to OCR1A”, #10 and #11, so I don’t want to double post it here. I’m using a rotary encoder to increment the frequency and its switch to select the frequency and wave form. That seems to be working fine. The data for the wave is coming from an R2R 6-bit ladder on A0-A5 which is then combined with calculated wave data as store in four 128 byte arrays, one for each wave form.

The output waves look great on a scope up to about 3.8KHz, but will not go higher. The setup for the interrupts and the Timer1 vector is repeated here. I left off the PCINT2 ISR because it is working correctly. These two functions appear between cli() and sei(). Any ideas why it turns into a mule at 3.8KHz?

void InitTimer()
  // Clear Timer1/Counter Control Register for Interrupt, bytes A and B (TCCR1?)
  TCCR1A = 0;       // Clear TCCR1A/B registers
  TCCR1B = 0;
  TCNT1 = 0;        // Initialize counter to 0

  // Compare register for TIMER1: (16mHz / frequency - 1) / 2^6, no prescaling
  period = (CLOCKFREQUENCY / frequency - 1) / 64;
  OCR1A = period;
  // Timer/Counter Control Register for Interrupt 1 on register B
  TCCR1B |= (1 << WGM12);    // Mode 4, CTC--Clear Timer on Compare
  TCCR1B |= (1 << CS10);     // Clock Select Bit, no prescaling
  TIMSK1 |= (1 << OCIE1A);   // The value in OCR1A is used for compare
  DDRB = 0b11111111;         // PORTB all outputs
  DDRC = 0b00111111;         // PORTC all inputs except PC6, which is RESET, and PC7
  DDRD = 0b11111111;         // PORTD all outputs

void InitEncoder()
  PCICR |= (1 << PCIE2);  // Turn on Port Control Interrupt Enable for Timer2 for 
                          // Pin Change Interrupt Control Register
  PCMSK2 |= (1 << PCINT18) | (1 << PCINT19); // Look for Pin Control Interrupts on pins 2 and 3

ISR(TIMER1_COMPA_vect)//timer 1 interrupt

  if (index >= tableVals) { // TableVals = 128, index is volatile
    index = 0;
  PORTC = (byte)( pgm_read_byte_near(waveTypePtr + index));  // waveTypePtr points to selected wave form array

You are stepping thru the 128 values up & down?
1/(1/16000000 * 2 * (128 * 2)) = 31250 Hz
Then you lose some more code cycles jump to & from the function calls, could end up slowing things down to 3800 Hz.

I've already posted the code on the thread titled "Fast PWM - Compare to OCR1A", #10 and #11, so I don't want to double post it here.

Double posting is when you deal with the same subject in two different Threads - which is exactly what you are doing. It causes immense confusion for everyone.

Ask the moderator to merge this Thread with your other one.


@CrossRoads: Yes, I do step through the entire array in either direction. Not sure what the solution is, though. I tried cutting the array size in half, but it still dies at about the same frequency. I've gotta be doing something stupid at some low level.

@Robin2: I wasn't the OP where I posted the code, but was coattailing the thread topic since my code pertained to that post. Given that only 33 people have even read this post with only one helpful reply, I doubt that it's caused immense confusion for everyone.

I think the 256 steps up & down are what's killing the speed. Each step needs some finite number of clock cycles, no getting around that. Having lots of function calls also has an impact. Takes a few microseconds to jump back & forth, that also adds up.
Rewrite the code to make it more inline, run it tighter in a while loop inside of the overall loop.

I did cut the array size in half, but it still dies at almost exactly the same frequency. I'll try to move the code inline and see what happens. Thanks!

How much memory does one complete waveform require? I don't know how long pgm_read_byte_near() takes but it might be significant at these frequencies. Can you get a general idea of how much time the interrupt handler is using by setting an I/O pin high at the start of the handler and low at the end? The duty cycle on the pin would tell you whether your handler was anywhere near overrunning.

PS if you have performance critical functions that you want to execute inline, you can mark them as inline functions - you don't need to inline them manually.

Mornin' Peter: I'm away from my system and I don't know how long a read takes from program memory. I'll investigate that as soon as I can. It may well be that I simply don't have the horsepower to do what I'm trying to do. Thanks...

@Robin2: I wasn't the OP where I posted the code,

I'm sure you will understand why I assumed you were. So post your code here to save people having to jump elsewhere to read it.


@Peter: Each waveform takes 128 bytes, processing it's a fairly long process and therein lies the rub. I think I'll have to live with the cutoff.

Thanks, all, for your help.