Estoy tratando de hacer una luz LED RGB de control remoto utilizando un ATtiny13A.
Sé que el ATtiny85 es más adecuado para este propósito, y sé que eventualmente no podría encajar todo el código, pero por ahora mi principal preocupación es generar un PWM por software usando interrupciones en modo CTC.
No puedo operar en ningún otro modo (excepto en PWM rápido con OCR0A
como TOP
que es básicamente lo mismo) porque el código del receptor de infrarrojos que estoy utilizando necesita una frecuencia de 38 kHz que genera utilizando CTC y OCR0A=122
.
Así que estoy intentando (y he visto que la gente lo menciona en Internet) utilizar el Output Compare A
y Output Compare B
interrupciones para generar un PWM por software.
OCR0A
que también utiliza el código IR, determina la frecuencia, que no me interesa. Y OCR0B
determina el ciclo de trabajo del PWM que utilizaré para cambiar los colores del LED.
Espero poder conseguir un PWM con un ciclo de trabajo de 0-100% cambiando el OCR0B
valor de 0
a OCR0A
. Esto es lo que yo entiendo que debería ocurrir:
Pero lo que realmente ocurre es esto (esto es de la simulación de Proteus ISIS):
Como se puede ver a continuación, soy capaz de obtener alrededor de 25%-75% de ciclo de trabajo, pero para ~0-25% y ~75-100% la forma de la onda es simplemente atascado y no cambia.
Línea AMARILLA: Hardware PWM
Línea roja: Software PWM con ciclo de trabajo fijo
Línea VERDE: Software PWM con ciclo de trabajo variable
Y aquí está mi código:
#ifndef F_CPU
#define F_CPU (9600000UL) // 9.6 MHz
#endif
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
int main(void)
{
cli();
TCCR0A = 0x00; // Init to zero
TCCR0B = 0x00;
TCCR0A |= (1<<WGM01); // CTC mode
TCCR0A |= (1<<COM0A0); // Toggle OC0A on compare match (50% PWM on PINB0)
// => YELLOW line on oscilloscope
TIMSK0 |= (1<<OCIE0A) | (1<<OCIE0B); // Compare match A and compare match B interrupt enabled
TCCR0B |= (1<<CS00); // Prescalar 1
sei();
DDRB = 0xFF; // All ports output
while (1)
{
OCR0A = 122; // This is the value I'll be using in my main program
for(int i=0; i<OCR0A; i++)
{
OCR0B = i; // Should change the duty cycle
_delay_ms(2);
}
}
}
ISR(TIM0_COMPA_vect){
PORTB ^= (1<<PINB3); // Toggle PINB3 on compare match (50% <SOFTWARE> PWM on PINB3)
// =>RED line on oscilloscope
PORTB &= ~(1<<PINB4); // PINB4 LOW
// =>GREEN line on oscilloscope
}
ISR(TIM0_COMPB_vect){
PORTB |= (1<<PINB4); // PINB4 HIGH
}
0 votos
¿Puedo preguntar por qué no se puede utilizar el PWM por hardware? La razón que das no tiene ningún sentido. La única razón para no usar hardware es si necesitas una interfaz SPI o una interrupción externa.
0 votos
@Maple Estoy tratando de controlar un LED RGB por lo que necesito 3 señales PWM, una para cada color.
OCR0A
es utilizado por el código IR así que sólo tengoOCR0B
. Estoy tratando de usarlo para generar PWM por software en 3 pines no PWM.0 votos
El software PWM de 38kHz no funcionará. Es demasiado rápido para la MCU.
0 votos
@JimmyB ¿Puedes explicarte mejor? Está funcionando a 9,6MHz de reloj y la rutina de interrupción es muy corta. ¿Por qué 38kHz es demasiado rápido?
1 votos
Puedes (y lo has hecho) ejecutar un ISR a 38kHz. Pero para cualquier ciclo de trabajo que no sea el 50% necesitarás una frecuencia más alta. Ejemplo: Para un 25% @ 38kHz necesitas ser capaz de manejar dos interrupciones sucesivas dentro de un marco de tiempo de 38kHz/25%=152kHz. Eso deja sólo unos 63 ciclos de reloj de la CPU (9600kHz/152kHz) para el ISR. Con un ciclo de trabajo del 10% te quedan unos 25 relojes de CPU para el ISR.
3 votos
No ha especificado la frecuencia PWM deseada. Para el control del brillo no necesitarás estar cerca de los 38kHz. 100Hz puede ser suficiente. Te sugiero que utilices la frecuencia de 38kHz (IR) como el ciclo de trabajo más bajo para tu software PWM e implementes el PWM como algún múltiplo de eso, por ejemplo 256, para que el ciclo de trabajo más bajo sea 1/256 (un período de reloj de 38kHz) y el más alto (por debajo del 100%) sea (255/256), igual a 255 períodos de reloj de 38kHz. Esto te da un PWM de 8 bits a (38000/256)~148Hz.
0 votos
@JimmyB ¡Gracias! Eso explica por qué la señal está bien cuando tiene ~50% de ciclo de trabajo pero al acercarse a ambos extremos empieza a actuar de forma extraña. Probaré tu solución a ver si me da un PWM lo suficientemente bueno. Y sí como has dicho la frecuencia no me importa mientras no haga que las luces se pongan nerviosas.
0 votos
Por cierto, buen trabajo al publicar esa pregunta, especialmente la imagen animada del 'scope muestra perfectamente el efecto de que el ISR se vuelva demasiado lento en algún momento.
0 votos
Re: "Puede que al final no pueda meter todo el código", AFAIK el delay.h enlaza la librería de punto flotante con la aplicación. No utilice _delay_ms en ATTiny.
0 votos
@Maple no lo haré. Es sólo para el propósito de la prueba.