Fading RGB led with ATtiny2313 timer

4

Estoy tratando de hacer una lámpara de estado de ánimo con solo hardware PWM e interrupciones (es decir, el bucle principal debe estar vacío). Lo hago como un desafío para aprender cómo funcionan los temporizadores, no por una 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 los colores al azar, pero no se desvanece entre ellos. Entonces, ¿cómo puedo lograr el desvanecimiento al siguiente color? Gracias de antemano.

    
pregunta evgeniuz

2 respuestas

1

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

  

Es fácil hacerlo con bucle principal y 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 trabaja. Puede mover todo esto al ISR con algunos cambios, sin embargo, normalmente desea que su ISR contenga el menor código posible.

Cómo funciona:

Inicialmente, establece un valor aleatorio para cada led, rgbVals[0] es rojo, rgbVals[1] es verde, rgbVals[2] es azul. Luego, cada 1000 ticks (timer0 interupt) incrementa o disminuye 2 leds, dependiendo del valor almacenado en rgbInc[] para cada led, 1 es rojo, 2 es verde y 3 es azul. Los bucles principales comprueban 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 que están cambiando, ptrCurrent y ptrNext . Un problema con esto es que tendrá el mismo patrón, al establecer un valor aleatorio de 0 1 o 2 en 'ptrCurrent' y 'ptrNext' (siempre que no sean iguales al mismo valor) lo haría mejor.

#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 entero de la lámpara del estado de ánimo es su aleatoriedad

Con solo unas pocas modificaciones menores, puedes hacerlo más aleatorio. Al igual que alterar aleatoriamente los valores de incremento / decremento ( rgbInc , actualmente solo cambia de 1 a -1) y cambiar aleatoriamente los 2 colores de led con los que trabaja ( ptrCurrent y ptrNext (pero asegúrese de que no funcionen) igual el mismo valor.)

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

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

También quiero señalar que en su código ISR original, debe configurar los valores de OCRxx en la instrucción if para que cada interrupción no solo esté restableciendo los mismos valores. Así:

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 el código fuera para producción, me desharía de los red green blue chars ya que solo puedes tener OCR0A = rand() / (RAND_MAX / 0xff + 1); , y la memoria en estos dispositivos pequeños es preciosa.

    
respondido por el Garrett Fogerlie
0

En tu rutina de interrupción,

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

ídem para verde, azul con valores iniciales adecuados para establecer colores de inicio 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.

    
respondido por el Brian Drummond

Lea otras preguntas en las etiquetas