Multiplexación AVR (ATMega328) PWM dentro de las interrupciones

1

Estoy tratando de tomar 3 LED RGB de ánodo comunes y conectar los cátodos de cada uno a tres de las salidas PWM en un ATMega328p y luego en un ISR, multiplexar entre todos. Sin embargo, el problema con el que me estoy topando es que incluso si, por ejemplo, solo quiero el rojo en el LED0 (de los LED 0 - 2) y solo enciendo el PWM para ese canal mientras el ánodo para ese LED está encendido (Lógica alta desde el ánodo común) Todavía me pongo rojo en TODOS los LED. Vea el esquema a continuación para ver cómo está configurado. Para simplificar las cosas, solo muestro el esquema y el código de uno de los canales LED.

void setup()
{
  //Setup outputs for anodes
  DDRB |= (_BV(PINB0) | _BV(PINB1) | _BV(PINB2));


  //Setup Timer2 Interrupts
  TCCR2A = _BV(COM2A1) | _BV(WGM21) | _BV(WGM20); //Fast PWM mode on OCR2A (PINB3 - Digital 11)
  TCCR2B = _BV(CS22); //64 Prescaler
  OCR2A = 255;

  //Setup Multiplex ISR on Timer1
  // set compare match register for ~50Khz
  OCR1A = 300;
  // turn on CTC mode
  TCCR1B |= _BV(WGM12);

  TCCR1B |= PRESCALE1_1;  
  // enable timer compare interrupt
  TIMSK1 |= _BV(OCIE1A);
}

volatile uint8_t col = 0;
ISR(TIMER1_COMPA_vect)
{
  //Set all anodes to LOW
  PORTB &= ~(_BV(PINB0) | _BV(PINB1) | _BV(PINB2));

  //Faking it here: Basically simulating that LED 0 is on but LEDs 1 and 2 are not.
  //Note that because the PWM is on the cathode, 255 is OFF and 0 is full brightness.
  if(col == 0)
    OCR2A = 0;
  else
    OCR2A = 255;

  //Turn on JUST the LED that we want.
  PORTB |= _BV(col);

  //Step through the LEDs
  col++;
  if(col > 2)
  {
    col = 0;
  } 
}

simular este circuito : esquema creado usando CircuitLab

Entonces, lo que esperaría es que el LED0 esté encendido a 1/3 de brillo (ya que solo está encendido en 1 de cada tres veces a través del ISR) y los LED 1 y 2 están COMPLETAMENTE apagados. Sin embargo, en cambio, TODOS los LED están encendidos en todo momento y algunas veces parecen parpadear un poco.

Esto me ha dejado completamente perplejo. Debido a que la única vez que el PWM del LED debe estar encendido es en el LED 0 y para los tiempos en que los ánodos de los otros LED están encendidos, el PWM debe configurarse a un ciclo de trabajo del 0% (para un PWM de cátodo).

Obviamente, normalmente tengo una variable global que contiene datos de canal RGB para cada uno de los LED y los datos se extraen durante cada paso de ISR para establecer el ciclo de trabajo de PWM respectivo para ese LED. Esto se simplifica simplemente para facilitar la lectura, ya que ocurre independientemente del canal de color que esté usando.

¿Pensamientos?

    
pregunta Adam Haile

2 respuestas

1

Ok, lo descubrí. El problema era que estaba dejando la frecuencia de PWM por defecto y usando una tasa de multiplexación muy alta.

De forma predeterminada, los precalificadores están configurados en 64, lo que hace que la frecuencia de los temporizadores que estaba usando (0 y 2) sea de 976.5625Hz. Pero estaba usando una tasa de multiplexación de 6400 Hz ... solo porque era lo que usaba en un proyecto diferente que no incluía PWM. El principal problema aquí fue que no pudo obtener ningún ciclo de PWM durante un solo ciclo múltiplex.

Por lo tanto, cambié a usar una preescala de 8 en mis PWM, llevando la frecuencia a ~ 8Khz y configuré la tasa de multiplexación a 1000Hz, de modo que recibe aproximadamente 8 ciclos de PWM por ciclo de multiplexación. Probablemente podría reducir la tasa de múltiplex aún más, pero es una línea fina antes de que empiece a ver el parpadeo.

También, descubrí que cuando actualizas el ciclo de trabajo de PWM (como configurando OCR2A) toma algo de tiempo antes de que surta efecto. Así que si me muevo con el multiplex de inmediato, los valores de PWM se borrarán en el próximo LED. Así que agregué algo de lógica donde, por primera vez, a través del ISR múltiplex, desactiva todos los ánodos y establece los valores de PWM. Luego, la próxima vez, solo habilita el ánodo necesario. Para esto es la variable flip_flop.

void setup()
{
  //Setup outputs for anodes
  DDRB |= (_BV(PINB0) | _BV(PINB1) | _BV(PINB2));


  //Setup Timer2 Interrupts
  TCCR2A = _BV(COM2A1) | _BV(WGM21) | _BV(WGM20); //Fast PWM mode on OCR2A (PINB3 - Digital 11)
  TCCR2B = _BV(CS21); //8 Prescaler
  OCR2A = 255;

  //Setup Multiplex ISR on Timer1
  // set compare match register for 1000hz 
  OCR1A = 16000;
  // turn on CTC mode
  TCCR1B |= _BV(WGM12);

  TCCR1B |= PRESCALE1_1;  
  // enable timer compare interrupt
  TIMSK1 |= _BV(OCIE1A);
}

volatile uint8_t col = 0;
bool flip_flop = false;
ISR(TIMER1_COMPA_vect)
{
  //Set all anodes to LOW
  if(!flip_flop)
  {
    PORTB &= ~(_BV(PINB0) | _BV(PINB1) | _BV(PINB2));

    //Faking it here: Basically simulating that LED 0 is on but LEDs 1 and 2 are not.
    //Note that because the PWM is on the cathode, 255 is OFF and 0 is full brightness.
    if(col == 0)
      OCR2A = 0;
    else
      OCR2A = 255;
  }



  if(flip_flop)
  {
    //Turn on JUST the LED that we want.
    PORTB |= _BV(col);

    //Step through the LEDs
    col++;
    if(col > 2)
    {
      col = 0;
    } 
  }

  flip_flop = flip_flop ? false : true;
}
    
respondido por el Adam Haile
0

Es posible que deba agregar algunas resistencias desplegables a los pines de salida en el lado del ánodo.

Tuve un problema similar al multiplexar los LED. Los diodos que se suponía que estaban apagados brillarían ligeramente debido a la corriente de fuga. Intente resistencias de 4.7k o 10k para atar los pines a tierra, manteniendo el ánodo bajo a menos que el micro lo esté elevando específicamente.

    
respondido por el JYelton

Lea otras preguntas en las etiquetas