Use AVR Timer1 para Comparar Interrupción Y PWM al mismo tiempo

¿Es posible usar Timer1 en un ATMega para generar una interrupción de comparación en OCR1A y ​​salida PWM en OCR1B? Lo he intentado con algo similar a lo siguiente, pero tampoco funciona con todo lo que he probado.

TCCR1A = _BV(COM1B1) | _BV(WGM11) | _BV(WGM10);
TCCR1B = PRESCALE1_8;
OCR1B = 0;


// set compare match register for 1000Hz 
OCR1A = 2000;
// turn on CTC mode
TCCR1B |= _BV(WGM12);

Nota al margen: sé que esto es extremadamente similar a otra pregunta mía , pero eso se refería a PWM en un temporizador e interrupciones en otro. Esta vez tengo problemas para sacar ambos del MISMO temporizador.

Respuestas (1)

Su código establece el contador para la operación PWM de fase correcta con el valor superior 0x3FF, por lo que el primer problema que enfrentará es que las interrupciones de comparación de coincidencias no se activarán en intervalos iguales debido a la forma en que funciona este modo, que cuenta hasta la parte superior y luego cambie de dirección y cuente hacia atrás.

El otro problema es que el contador cuenta hasta 1023 (0x3ff) y vuelve a 0, por lo que no hay forma de obtener una interrupción de coincidencia por un valor de 2000 (OCR1A = 2000)

Operación PWM de fase correcta:

ingrese la descripción de la imagen aquí


Este es un código mínimo para mega328 que se ejecuta a 16 MHz que configura el temporizador 1 para generar un PWM rápido con una frecuencia de aproximadamente 1 KHz (frecuencia establecida por ICR1 y función establecida por OCR1B) y también proporciona una interrupción de desbordamiento del temporizador 1 con una tasa de 1 KHz.

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

// Timer1 overflow interrupt service routine
ISR(TIMER1_OVF_vect)
{
    PORTB ^= 1; // invert PORTB.0
}

int main(void)
{

    // Port B initialization
    // Function: Bit7=In Bit6=In Bit5=In Bit4=In Bit3=In Bit2=Out Bit1=In Bit0=Out
    DDRB = 0x05;

    // Timer/Counter 1 initialization
    // Clock source: System Clock
    // Clock value: 2000,000 kHz
    // Mode: Fast PWM top=ICR1
    // OC1A output: Disconnected
    // OC1B output: Non-Inverted PWM
    // Noise Canceler: Off
    // Input Capture on Falling Edge
    // Timer Period: 1,0245 ms
    // Output Pulse(s):
    // OC1B Period: 1,0245 ms Width: 0,12806 ms
    // Timer1 Overflow Interrupt: On
    // Input Capture Interrupt: Off
    // Compare A Match Interrupt: Off
    // Compare B Match Interrupt: Off
    TCCR1A = (0 << COM1A1) | (0 << COM1A0) | (1 << COM1B1) | (0 << COM1B0) | (1 << WGM11) | (0 << WGM10);
    TCCR1B = (0 << ICNC1) | (0 << ICES1) | (1 << WGM13) | (1 << WGM12) | (0 << CS12) | (1 << CS11) | (0 << CS10);
    TCNT1 = 0;
    ICR1 = 2048;
    OCR1A = 0;
    OCR1B = 256;

    // Timer/Counter 1 Interrupt(s) initialization
    TIMSK1 = (0 << ICIE1) | (0 << OCIE1B) | (0 << OCIE1A) | (1 << TOIE1);

    // Global enable interrupts
    sei();

    while(1)
    {
        // Place your code here

    }
}
¿Puedo usar Fast PWM en su lugar? No es que esté completamente seguro de cómo hacerlo :P
@AdamHaile Depende de lo que quiera hacer, si desea obtener intervalos iguales para la coincidencia de comparación, entonces sí, si es solo para establecer un valor superior más alto (lo que dará como resultado una frecuencia PWM más baja), puede usar ambos modos con un valor superior personalizado establecido en el registro ICR1 seleccionando el modo PWM apropiado (usando los valores de registro WGM)
Sí... definitivamente quiero intervalos iguales. La coincidencia de comparación es para realizar la multiplexación entre diferentes LED utilizando las señales PWM. ¿Alguna posibilidad de que puedas mostrar algún código de ejemplo?
@AdamHaile, ¿cuál es su MCU específico? ¿Y qué hay de la frecuencia PWM? PWM puede dar una interrupción de desbordamiento del temporizador una vez por período (indicador TOV1), tal vez pueda usar eso para su actualización
ATMega328P-PU. No tengo idea de cuál es la frecuencia PWM. ¿Estaba usando 8 prescaler, entonces 2MHz? Estoy tratando de obtener una interrupción a aproximadamente 1000 Hz.
@AdamHaile En el modo de corrección de fase o corrección de fase y frecuencia con top = 0x3ff, la frecuencia PWM de salida = reloj temporizador / (1023 * 2) entonces 2MHz / 2046 = 917Hz, si usa la interrupción de la bandera TOV1 (bandera de desbordamiento) puede obtener interrumpe a ese ritmo
Estoy confundido por lo que quieres decir con top = 0x3ff, ¿qué registro es ese? ¿Alguna posibilidad de que puedas mostrar un ejemplo?
@AdamHaile Eso no es un registro, es el valor superior de PWM, el modo que ha seleccionado en su código original es TCCR1A = _BV(WGM11) | _BV(WGM10);PWM, Phase Correct, 10 bits , ese 10 bit significa que el valor SUPERIOR es 0x03FF (está en la hoja de datos) y es lo que realmente establece la frecuencia PWM.
Ahh... pero ¿cómo se usaría exactamente el TOV1 ISR?
Nota al margen. También estoy haciendo PWM en el temporizador/contador 2... ¿tendré algún problema con el PWM en la misma frecuencia/ciclo de trabajo?
@AdamHaile Simplemente habilite la interrupción de desbordamiento del temporizador 1 TIMSK1=(1<<TOIE1);y agregue el controlador de interrupción, se llamará a una velocidad de 917 Hz, cada vez que el contador llegue al fondo (0). Timer2 es independiente de timer1, ninguno de los anteriores lo afecta.
Tenga lo siguiente ahora, pero definitivamente no está haciendo ningún PWM: TCCR1A = _BV (WGM11) | _BV(WGM10); TCCR1B = PREESCALA1_8; OCR1B = 0; TIMSK1=_BV(TOIE1);
@AdamHaile, he agregado un código de trabajo mínimo en la respuesta
@AdamHaile ¿Has probado el código?
@alexan_e, para este código, ¿suponemos que algún circuito (por ejemplo, un transistor) está conectado al pin OCR1B?
@DavidNorman El código no necesita ningún componente externo para generar los pulsos en PORTB.0 y OC1B. Los pulsos generados pueden controlar cualquier circuito que desee el diseñador (con las especificaciones de E/S de AVR).
@alexan_e, estoy usando un transceptor para modular la señal. ¿Tengo que conectar la línea TX o RX en el transceptor al pin OC1B? Ahora mismo lo tengo conectado a mi RX0 y TX0 en el microcontrolador (atmega328p). ¿Hay alguna manera de poder seguir usando señales de OC1B (internamente, no externamente) para controlar los pines RX0 y TX0? tenga en cuenta que ya no estoy usando serial, por lo que RX0 y TX0 son solo GPIO
@DavidNorman Verifique la figura 16.8 página 124 en la hoja de datos m88 . Puede obtener un evento en la parte superior e inferior del PWM utilizando las interrupciones de comparación de salida y desbordamiento del temporizador. No estoy seguro de lo que está haciendo, así que no puedo proporcionar mucha ayuda. Tal vez deberías publicar esto como una nueva pregunta.