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.
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, ptrCurrent
y 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 ( ptrCurrent
y 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 blue
caracteres, ya que solo puede tener OCR0A = rand() / (RAND_MAX / 0xff + 1);
, y la memoria en estos pequeños dispositivos es preciosa.
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.
evgeniuz
garrett fogerlie