Society of Robots - Robot Forum

Software => Software => Topic started by: madsci1016 on November 14, 2010, 02:32:53 PM

Title: Interesting phenominon with AVR timers, what's causing this?
Post by: madsci1016 on November 14, 2010, 02:32:53 PM
Hi guys

I'm trying to do some charlieplexing using an ATtiny85 running a hacked Arduino core. I'm using the Arduino PWM functions, which just load timers to generate PWM.

When you call a Arduino analogWrite, this code executes:

Code: [Select]
void analogWrite(uint8_t pin, int val)
{
// connect pwm to pin on timer 0, channel A
  sbi(TCCR0A, COM0A1);
  // set pwm duty
  OCR0A = val;   

Timer 0 is setup like this:
Code: [Select]
sbi(TCCR0A, WGM01);
sbi(TCCR0A, WGM00);
sbi(TCCR0B, CS01);
sbi(TCCR0B, CS00);
sbi(TIMSK, TOIE0);

Timer 0 is also used for time keeping functions:

Code: [Select]
SIGNAL(TIM0_OVF_vect)
{
// copy these to local variables so they can be stored in registers
// (volatile variables must be read from memory on every access)
unsigned long m = timer0_millis;
unsigned char f = timer0_fract;

m += MILLIS_INC;
f += FRACT_INC;
if (f >= FRACT_MAX) {
f -= FRACT_MAX;
m += 1;
}

timer0_fract = f;
timer0_millis = m;
timer0_overflow_count++;
}

Now here's my problem. Whenever I first call the analogWrite function with an extreme value like 1 (which should result in a wave with 1/255 duty cycle), I observe a pulse of exactly 2.040ms duration before the correct pulse train begins. This is causing a rapid 'blink' in the LEDS i'm trying to 'fade-up'.


Any Idea what's causing this? Is it because the overflow vector is doing time keeping functions? I'm about to rewrite the core files so time keeping gets shifted to the other timer, but I'd figured I'd ask the experts first.

EDIT:
Moved the time keeping to the other timer, still seeing blip.
Title: Re: Interesting phenominon with AVR timers, what's causing this?
Post by: madsci1016 on November 14, 2010, 04:43:13 PM
Well, I got a work around now to at least fix the issue with the blink at the begining:

This
Code: [Select]
void analogWrite(uint8_t pin, int val)
{
// connect pwm to pin on timer 0, channel A
  sbi(TCCR0A, COM0A1);
  // set pwm duty
  OCR0A = val;   

is now:

Code: [Select]
if(!bitRead(TCCR0A, COM0A1)){
cbi(DDRB, 0);
sbi(TCCR0A, COM0A1);
TCNT0 = 0;
delay(10);
sbi(DDRB, 0);
}
// set pwm duty
OCR0A = val;

IE, if the timer isn't already running in PWM mode, set the IO pin to input, enable PWM mode, clear the current count, delay, set the IO pin as output. So now i'm 'skipping' the initial timer blip, but i still don't get why it's there.
Title: Re: Interesting phenominon with AVR timers, what's causing this?
Post by: madsci1016 on November 15, 2010, 01:20:16 PM
I'm thinking of codding from scratch and dropping the Arduino core to see if that fixes the blip at the beginning of activating the timer.

Anyone know why this blip is happening?
Title: Re: Interesting phenominon with AVR timers, what's causing this?
Post by: Webbot on November 15, 2010, 01:25:26 PM
It may well be that the timer overflow interrupt request (in TIFR) is already set so as soon as you enable the interrupt then the interrupt happens.
Try clearing the TIFR bit (by writing a 1 to it) before enabling the interrupt.
Title: Re: Interesting phenominon with AVR timers, what's causing this?
Post by: madsci1016 on November 15, 2010, 01:43:38 PM
I'll have to check if I disabled the overflow interrupt, but I know for sure i renamed the SIGNAL(TIM0_OVF_vect) when I moved the time keeping to timer 1, so timer 0 overflow would point to no function (right?).
Title: Re: Interesting phenominon with AVR timers, what's causing this?
Post by: madsci1016 on November 15, 2010, 07:24:13 PM
It may well be that the timer overflow interrupt request (in TIFR) is already set so as soon as you enable the interrupt then the interrupt happens.
Try clearing the TIFR bit (by writing a 1 to it) before enabling the interrupt.

Tried writing a one to the overflow interrupt request bit, even wrote FF to the whole TIFR register before setting the COM0A1 bit, nothing changed.

What's odd is the very first time I call the analogWrite function, no blip. Every time after, I get a blip.

Is there something else you should do to a timer after you disable PWM (by clearing COM0A1 bit) and before you re-enable it to use it again?
Title: Re: Interesting phenominon with AVR timers, what's causing this?
Post by: madsci1016 on November 15, 2010, 07:49:49 PM
I got excited because this sounded like the issue:

Quote
The prescaler is free running, i.e. it operates independently of the Clock Select logic of
Timer/Counter0. Since the prescaler is not affected by the timer/counter’s clock select, the state
 of the prescaler will have implications for situations where a prescaled clock is used. One example
of a prescaling artifact is when the timer/counter is enabled and clocked by the prescaler (6 >
CS0[2:0] > 1). The number of system clock cycles from when the timer is enabled to the first
count occurs can be from 1 to N+1 system clock cycles, where N equals the prescaler divisor (8,
64, 256, or 1024).
It is possible to use the Prescaler Reset for synchronizing the Timer/Counter to program
execution.

so I tried

Code: [Select]
if(!bitRead(TCCR0A, COM0A1)){
sbi(GTCCR, TSM);
sbi(GTCCR, PSR0);
sbi(TCCR0A, COM0A1);
OCR0A = val;
cbi(GTCCR, TSM);
}
  // set pwm duty
  else OCR0A = val;

But still see the blip, though it does seem to be random when I see it. It really feels like some timing issue depending on when the PWM is enabled.
Title: Re: Interesting phenominon with AVR timers, what's causing this?
Post by: madsci1016 on November 15, 2010, 09:04:23 PM
Ok, i've completely recreated my code in AVR Studio without the Arduino library.

Still seeing blips, though randomly, and with random durations as well.

Attached is my code. Does anyone see the problem, or am I just crazy?

Title: Re: Interesting phenominon with AVR timers, what's causing this?
Post by: bens on November 18, 2010, 11:54:14 PM
Hello.

I've experienced the same problem doing what you're doing.  The AVR does not handle it well if you try to disable the PWM by making the pin an input, and it is a timing-dependent problem.  The cleanest way to avoid the pulse is to leave the pin an output and just set the duty cycle to zero.  Unfortunately, the AVR will generate very short glitches at 0% in fast PWM mode (the duty cycle is actually (OCRxx+1)/256), but there are a few possible workarounds:

1) If you want to be able to drive the line low, use an inverted PWM.  This will give you a clean low output when the duty cycle is 0% (OCRxx=255), though you will not be able to achieve a 100% duty cycle (the best you can do is 255/256=99.6% when OCRxx=0), but you won't be able to tell that the LED is not 100% on.

2) Use the phase correct PWM mode, where the counter alternates between counting up and counting down.  In phase correct mode, the PWM frequency is approximately half what it is in fast PWM mode, but you can get clean 0% and 100% outputs.

- Ben
Title: Re: Interesting phenominon with AVR timers, what's causing this?
Post by: madsci1016 on November 19, 2010, 07:46:33 AM
Thanks bens, at least I know I'm not crazy now.

Unfortunately, since I am Charlie-plexing LEDS, I need to be able to switch the pin into tri-state (high impedance) mode in-between PWM cycles.

I think I'm just going to do PWM in software, since the ATtiny will be doing nothing but blinking LEDs, there's no reason why I can't do that in the foreground.
Title: Re: Interesting phenominon with AVR timers, what's causing this?
Post by: bens on November 19, 2010, 01:45:17 PM
Ah, I read the Charlie-plexing comment in your first post but then promptly forgot about it.  I spent a while trying to understand the nature of this glitch and remember being frustrated that none of the things I logically expected would make it go away worked.  Then again, this was three years ago now, so I don't remember everything I tried, and I don't recall what other constraints might have been present in the system that might have prevented me from trying other things.

Generating the PWM in software sounds like a good way to go, and it doesn't even need to be very prominently in the foreground.  You can set up the timer in PWM mode with the outputs disabled and compare-match and overflow interrupts enabled.  Using the ISRs to perform the appropriate I/O manipulation would be a very minimal burden on the rest of your program.

- Ben