I'm trying to make mood lamp with only hardware PWM and interrupts (i. e. main loop should be empty). I'm doing it as a challenge to learn how timers work, not for any practical reason. So far I've done this:
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdlib.h>
volatile unsigned int elapsed_cycles = 0;
volatile unsigned char red, green, blue;
int main(void)
{
// init timers as fast PWM
TCCR0A = (1 << WGM00) | (1 << WGM01);
TCCR1A = (1 << WGM10) | (1 << WGM12);
// set prescaler to 1
TCCR0B |= (1 << CS00);
TCCR1B |= (1 << CS00);
// set ports to output
DDRB |= (1 << PB2);
DDRB |= (1 << PB3);
DDRB |= (1 << PB4);
// set outputs to PWM
TCCR0A |= (1 << COM0A1);
TCCR1A |= (1 << COM1A1);
TCCR1A |= (1 << COM1B1);
// initial PWM duty cycle
OCR0A = 0;
OCR1A = 0;
OCR1B = 0;
// overflow interrupt setup
TIMSK |= (1 << TOIE0);
sei();
for (;;)
{
}
}
ISR(TIMER0_OVF_vect)
{
elapsed_cycles++;
if (elapsed_cycles == 10000)
{
red = rand() / (RAND_MAX / 0xff + 1);
green = rand() / (RAND_MAX / 0xff + 1);
blue = rand() / (RAND_MAX / 0xff + 1);
elapsed_cycles = 0;
}
OCR0A = red;
OCR1A = green;
OCR1B = blue;
}
This code change colors randomly, but not fading between them. So, how can I achieve fading to next color? Thanks in advance.
Best Answer
Give this a shot, sorry I didn't comment it much. I don't have any way to test it, but it should work. If you have any issues or questions let me know.
The interrupt changes and sets the output, and the main loop takes care of switching from increment to decrement and vice versa; and changes the leds it is working with. You can move this all to the ISR with a few changes however you normally want your ISR's to contain as little code as possible.
How it works:
Initially it sets a random value to each led,
rgbVals[0]
is red,rgbVals[1]
is green,rgbVals[2]
is blue. Then every 1000 ticks (timer0 interupt) it increments or decrements 2 leds, depending on the value stored inrgbInc[]
for each led, 1 is Red, 2 is Green, and 3 is blue. The main loops checks if a led is at it's max or min, then switches that led's increment valuergbInc[]
to 1 or -1, so it will start going the other direction. And it switches the pair of led's that are changing,ptrCurrent
andptrNext
. One problem with this is that it will have the same pattern, setting a random value of 0 1 or 2 to 'ptrCurrent' and 'ptrNext' (as long as they don't equal the same value) would make it better.With just a few minor modifications you can make it more random. Like randomly altering the increment/decrement values (
rgbInc
, currently only changes from 1 to -1,) and randomly changing the 2 led colors it is working with (ptrCurrent
andptrNext
(but make sure they don't equal the same value.)Here is a post that gives some other ideas: Running through RGB spectrum
Here is an code example from and Arduino site:
Also I want to point out that in your original ISR code you should set the OCRxx values in the if statement so that every interrupt it is not just re-setting the same values. Like this:
And if you code was for production, I would get rid of the
red green blue
chars since you can just haveOCR0A = rand() / (RAND_MAX / 0xff + 1);
, and memory on these small devices is precious.