Desvanecimiento LED RGB con temporizador ATtiny2313

Estoy tratando de hacer una lámpara de estado de ánimo con solo PWM de hardware e interrupciones (es decir, el bucle principal debe estar vacío). Lo estoy haciendo como un desafío para aprender cómo funcionan los temporizadores, no por ninguna razón práctica. Hasta ahora he hecho esto:

#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;
}

Este código cambia de color al azar, pero sin desvanecerse entre ellos. Entonces, ¿cómo puedo lograr el desvanecimiento al siguiente color? Gracias de antemano.

Respuestas (2)

Dale una oportunidad a esto, lo siento, no lo comenté mucho. No tengo forma de probarlo, pero debería funcionar. Si tiene algún problema o pregunta, hágamelo saber.

Es fácil hacerlo con el bucle principal y los retrasos. Pero, como dije, estoy tratando de hacer esto solo con interrupciones.

La interrupción cambia y establece la salida, y el bucle principal se encarga de cambiar de incremento a decremento y viceversa; y cambia los leds con los que está trabajando. Puede mover todo esto al ISR con algunos cambios; sin embargo, normalmente desea que su ISR contenga la menor cantidad de código posible.

Cómo funciona:

Inicialmente pone un valor aleatorio a cada led, rgbVals[0]es rojo, rgbVals[1]es verde, rgbVals[2]es azul. Luego, cada 1000 tics (interrupción del temporizador 0) aumenta o disminuye 2 LED, según el valor almacenado rgbInc[]para cada LED, 1 es rojo, 2 es verde y 3 es azul. Los bucles principales verifican si un LED está en su máximo o mínimo, luego cambia el valor de incremento de ese LED rgbInc[]a 1 o -1, por lo que comenzará a ir en la otra dirección. Y cambia el par de led's que están cambiando, ptrCurrenty ptrNext. Un problema con esto es que tendrá el mismo patrón, establecer un valor aleatorio de 0 1 o 2 en 'ptrCurrent' y 'ptrNext' (siempre que no tengan el mismo valor) lo mejoraría.

#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdlib.h>

volatile uint16_t tick = 0;
volatile uint8_t rgbVals[3]; // rgbVals[0] is red; rgbVals[1] is green; rgbVals[2] is blue
volatile uint8_t ptrCurrent = 0; // current led
volatile uint8_t ptrNext = 1; // next led
int8_t rgbInc[3]; // will hold the increment/decrement value for each led

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);

    // overflow interrupt setup
    TIMSK |= (1 << TOIE0);
    sei();

    rgbVals[0] = rand() / (RAND_MAX / 0xff + 1);
    rgbVals[1] = rand() / (RAND_MAX / 0xff + 1);
    rgbVals[2] = rand() / (RAND_MAX / 0xff + 1);

    // Power led to rand values
    OCR0A = rgbVals[0];
    OCR1A = rgbVals[1];
    OCR1B = rgbVals[2];

    rgbInc[0] = 1; // You can play with these values to make it change faster
    rgbInc[1] = 1;
    rgbInc[2] = 1;

    while(1)
    {
        for(uint8_t i =0; i<3; i++)
        {
            if(rgbVals[i] == 0 || rgbVals[i] == 255)
            {
                rgbInc[i] *= -1;
                ptrCurrent = ptrNext;
                ptrNext++;
                if(ptrNext == 3) // roll over, since we only have 3 led colors, 0 1 and 2
                    ptrNext =0;
            }
        }
    }

    return 1; // never executed
}

ISR(TIMER0_OVF_vect)
{
    tick++;
    if(tick == 1000)
    {
        rgbVals[ptrCurrent] += rgbInc[ptrCurrent];
        rgbVals[ptrNext] += rgbInc[ptrNext];

        OCR0A = rgbVals[0]; // set PB2 to Red's value
        OCR1A = rgbVals[1]; // set PB3 to Green's value
        OCR1B = rgbVals[2]; // set PB4 to Blue's value

        tick = 0; // reset tick
    }   
}

el punto central de la lámpara de humor es su aleatoriedad

Con solo unas pocas modificaciones menores, puede hacerlo más aleatorio. Como alterar aleatoriamente los valores de incremento/decremento ( rgbInc, actualmente solo cambia de 1 a -1) y cambiar aleatoriamente los 2 colores de los LED con los que está trabajando ( ptrCurrenty ptrNext(pero asegúrese de que no tengan el mismo valor).

Aquí hay una publicación que brinda algunas otras ideas: Ejecución a través del espectro RGB

Aquí hay un ejemplo de código de un sitio de Arduino :

También quiero señalar que en su código ISR original debe establecer los valores OCRxx en la instrucción if para que cada interrupción no solo restablezca los mismos valores. Como esto:

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);

        OCR0A = red;
        OCR1A = green;
        OCR1B = blue;

        elapsed_cycles = 0;
    }
}

Y si su código fuera para producción, me desharía de los red green bluecaracteres, ya que solo puede tener OCR0A = rand() / (RAND_MAX / 0xff + 1);, y la memoria en estos pequeños dispositivos es preciosa.

Es fácil hacerlo con el bucle principal y los retrasos. Pero, como dije, estoy tratando de hacer esto solo con interrupciones. Además, el punto central de la lámpara de humor es su aleatoriedad :)
@Shark Hice un nuevo ejemplo de código que usa la interrupción pero lo mantiene pequeño (no quieres mantener la menor cantidad de código posible en un ISR) y el resto está en el bucle principal, aunque puedes moverlo al ISR si deseado.

En su rutina de interrupción,

    red := red + delta_r;
    if red = 0 then 
       delta_r := 1;
    elsif red = 255 then 
       delta_r := -1;
    end if;

lo mismo para verde, azul con valores iniciales adecuados para establecer colores iniciales y deltas.
Lo siento, no está en C, pero uso Ada para mis Arduinos. Sin embargo, la traducción debería ser muy fácil.