First steps with PWM - Won't work within the interrupt

Go To Last Post
7 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello.

 

I am doing my first steps learning about AVRs. I wrote this code. I want the LED to fade in within a sec to MAX.

If the line

OCR0A = (dutyCycle * 255) / 100;

is within the loop, everything works as expected.

Whereas, if it's within the ISR(), it shines low for a few seconds, than suddenly it shines bright for a few seconds and resets again.

The full code is:

#define F_CPU 16000000

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

volatile int dutyCycle = 0;

int
main(void)
{
  DDRD = (1 << DDD6);

  TCCR0A = (1 << COM0A1) | (1 << WGM00) | (1 << WGM01);

  TIMSK0 = (1 << TOIE0);

  OCR0A = dutyCycle;

  sei();

  TCCR0B = (1 << CS00);

  while (1)
    {
      _delay_ms(100);

      dutyCycle += 10;

      //      OCR0A = (dutyCycle * 255) / 100;

      if (dutyCycle > 100)
      	dutyCycle = 0;
    }
}

ISR(TIMER0_OVF_vect)
{
  OCR0A = (dutyCycle * 255) / 100;
}

Many thanks!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Why are you mixing _delay_ms() and Timer0?  Sounds like a recipe for a "mess" to me.

 

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

While you do have a "volatile", the 16-bit operations in the main loop are not atomic.  At some point, your interrupt is going to hit in the middle of the operation and you will end up with fruit salad.

 

Also,  as soon as I saw

kodcode wrote:

(dutyCycle * 255) / 100;

I'm thinking "overflow".  As it turns out, it won't happen here.  But did you consider it on that multiply? 

 

And, force of habit, I say to myself "255 and 100 are both divisible by 5" so *51 and /20 would have less chance of overflow for a wider range of operands.

 

Now, if you stat having apparent glitches in the output, also consider what the datasheet for that [unnamed!] AVR model and that timer within that model and that timer mode says about when the OCR value is updated.  Can you catch a set of values such that it will appear to "run away" for a timer cycle until it "catches up"?

 

Lessee -- an 8-bit timer running at 16MHz and /1 will race around in about 64us, right?  Lots of chances to "hit".  And updating the OCR value every 64us means every 2ms or so you do a complete run through your values if my mental arithmetic works.  You ain't gonna see much on an LED at those speeds.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You can not set OCR0A to 0, and in the same time have OVF interrupt, and btw. setting is double-buffered (will not be lost).

You can set to 255, to have very short, or very long pulse (depending on COM0An).

 

Try 5...250 first, to be sure it works, then approach to real min/max.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
      _delay_ms(100);

Why this???

Set your isr to hit say every 100ms, or 10ms, or whatever.

each time it hits, the ISR will step the OCR value by some amount to give the desire slew rate

also, no slow division is needed

 

ISR:

setting = setting +stepsize (step & ISR rate controls the dimming speed)

If setting  blah blah setting =0

ocr = setting

 

 

This assumes you have one timer dedicated doing the LED pwm, and then also a system IRQ timer tick going every 10 ms, 100ms, etc.

 

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

Last Edited: Wed. Sep 21, 2022 - 11:42 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:

 

Lessee -- an 8-bit timer running at 16MHz and /1 will race around in about 64us, right?  ...

Wrong.  But even "worse" w.r.t. fast times as it would be every 16us.  So I'd guess you are very likely to hit the non-atomic hole sooner than later.

 

And LED work has even smaller windows.  I'd think that with LED persistence you would have a hard time seeing if things are "working" or not.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

grohote wrote:
You can not set OCR0A to 0

And this is indeed the bug:

Your old code was not susceptible but your ISR code IS.

 

while (1) {
  _delay_ms(100);

  dutyCycle += 10;

  // OCR0A = (dutyCycle * 255) / 100;

  if (dutyCycle > 100)
    dutyCycle = 0;  // The ISR may fire after this line when dutyCycle == 0
}