Timer1 Interrupt in PWM mode does not = 50%

I'm trying to create interrupt ~235 Hz to dim some LED's. ( I use both edges of timer1 output to get the 235Hz)

I'm using Timer1 as a 50% PWM signal to create the above.

I'm actually getting a 53.6% dutycycle as measured on the LED pin. For my application this is not critical but I would like to understand where I went wrong (I assume its NOT the timer1 bug).

Thanks
John


#include <avr/power.h>

//#define period 0x0F0A //259.7  for 16Mhz 
#define period 0x0850   // 234.9 Hz for 8Mhz

#define interPulseDelay 5

uint8_t OnTime;           // OnTime 20 meas 24? and 15 measured as 14.5µs with scope (at LED strip)
// Max 7 x (OnTime + interPulseDelay) ~ 275 µs  (as measured)


// ---------------------------------------------------------------------------------
void setup(){

	cli();      //stop interrupts
    DDRD = 0b11111110; // set PORTD (digital 7~0) to outputs
    PORTD = 0;
	TIMSK0 &= ~_BV(TOIE0); // disable timer0 overflow interrupt
    
  //set timer1 interrupt period (freq)
  	TCCR1A = 0;  // set entire TCCR1A register to 0
  	TCCR1B = 0;  // same for TCCR1B
  	TCNT1  = 0;  //initialize counter value to 0
  // set compare match register
  	OCR1A = period;

    // turn on CTC mode
  	TCCR1B |= (1 << WGM12);

  // Set CS11 for ÷8 prescaler
  	TCCR1B |= (1 << CS11);	// | (1 << CS10);
  
  // enable timer compare interrupt
  	TIMSK1 |= (1 << OCIE1A);

// to save power disable unused functions in µP
    ADCSRA = 0;                 // turn off the ADC converter
    power_adc_disable();        // ADC converter (need the above and this line to fully disable the ADC)
    power_spi_disable();        // SPI
    power_usart0_disable();     // Serial (USART)
    power_timer0_disable();     // Timer 0
    //power_timer1_disable();   // Timer 1
    power_timer2_disable();     // Timer 2
    power_twi_disable();        // TWI (I2C)


// Read pot - define OnTime
    OnTime = 20;
  /*
    uint8_t PotValue = analogRead(A0);
    if (PotValue > 99) OnTime = 20;
    if (PotValue > 901) OnTime = 200;  // test and change this to the Max possible with ckt.

    OnTime = map(PotValue, 100, 900, 10, 200);
*/
    sei();      //allow interrupts
}   //end setup
    

// ---------------------------------------------------------------------------------
ISR(TIMER1_COMPA_vect){		//timer1 interrupt
    asm ("sbi %0, %1 \n": : "I" (_SFR_IO_ADDR(PINB)), "I" (PINB5)); // Toggle LED
	
    for (int i =1; i < 8; i++){
        PORTD |= (1 << i);
 	    delayMicroseconds(OnTime);
 	    PORTD = 0x00;
        delayMicroseconds(interPulseDelay);
    }
}

// ---------------------------------------------------------------------------------
void loop(){}

// -_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_

What you are doing is not a timer PWM mode. In PWM mode, the timer itself switches the state of the output pin without using interrupts.
Interrupt entry and exit takes time, so your method cannot give accurate intervals.

In your case, it is generally not clear why do you need timers and interrupts. Your code is completely equivalent to the usual blink code in a loop()

digitalWrite(pin, LOW);
delay(interval);
digitalWrite(pin, HIGH);
delay(interval);

Your code also blocks the whole program like any code with delays. More precisely, your code is even worse - unlike the code in the main program, your code also blocks system interrupts

No, it's the user1 bug :frowning:

Please understand timer modes and interrupts before you search bugs somewhere else.

That picture doesn't seem to match your code. It looks like the falling transition of the bottom trace is happening at the END of the seven pulses generated in the ISR. According to the code the LED pin transitions first, then the pulses are generated.

The second falling transition is showing up in the middle of the pulses?!?

Please understand timer modes and interrupts before you search bugs somewhere else.

Not a useful reply :frowning: I would expected better from you.

Did you somehow think I was looking for a bug after I explicitly stated I assumed it was NOT a bug?

Thanks, I'll look closer at how the measurements are made :slight_smile:

Update, I see what you mean :slight_smile: Thank you for your observation and time to look at my code.

I'm somewhat embarrassed that I missed this :thinking:

I upped the sample rate of the Logic analyzer and not ALL transitions look wrong. Before some looked right.
Its time to get out the oscilloscope and see what is really going on.

Again thanks for your help.

John

Really, What whole program am I blocking?

I thought this approach was somewhat creative, you can disagree.

Interrupt entry and exit takes time, so your method cannot give accurate intervals.

Everything takes time, however in this case the time should be the same for each interrupt.

it is not the case because your interrupt is not the only interrupt in the controller

You code like playing Lego. You can continue playing or you can start over more seriously.

And what other interrupt would that / those be?

at least Timer0 interrupt advancing millis() , if we talking about arduino boards

Perhaps you didn't understand the line

    power_timer0_disable();     // Timer 0

What it means is:
if you include:

#include <avr/power.h>

And include the line:

power_timer0_disable(); // Timer 0

Then Timer0 is disabled and will not generate an interrupt.

Again thank you for your observation. I found I had not set the LED pin to an output. I've done this before so I'll have to put a note on the wall to always check this first.

Everything is now as it should be:

Thanks again
John

image

I should have caught that! I was thinking that maybe the LED hardware was somehow delaying the pulse edge. I didn't think to check the pin mode.

perhaps
but it doesn't change the fact that this code has nothing to do with "timer pwm mode"

What also threw me was the LED was working. It seems if you don't declare it as an output the Pin will go high then go open or "kinda" low which delayed the analyzer trigger.