Comportamiento de interrupción inexplicable en AVR ATMEGA324P Timer Counter ISR

Tengo un ATMEGA324P que controla un circuito de controlador de motor dual. Estoy usando el contador de tiempo de 16 bits 1 en el modo de corrección de fase y frecuencia para crear (2) salidas PWM usando el ICR1 como el valor SUPERIOR y el OCR1A y ​​OCR1B para generar las señales PWM en los pines de salida. El problema persiste igual en OCR1A y ​​OCR1B, por lo que he desactivado OCR1A para simplificar la solución de problemas.

El problema que estoy experimentando es que cuando se dispara cualquier otra interrupción en el sistema, como UART o PCINT, el ciclo de trabajo de PWM se invertirá. Por ejemplo, si el ciclo de trabajo se establece en 75 %, se invertirá al 25 % y cambiará de un lado a otro mientras otras interrupciones se activan, lo que hace que la salida sea extremadamente errática. Las tomas de alcance a continuación muestran la salida deseada en la parte superior y la invertida en la parte inferior. Pude activar esta inversión mediante UART RX ISR, Pin change interrupt interrupt ISR y Timer Counter 0 and 2 Compare A & B ISR's. Por ejemplo, enviar un carácter al UART hará que se invierta.

Tiro de alcance

Todo parece funcionar en mi código (a continuación) excepto por este problema. Probé una nueva placa de circuito y exhibió exactamente el mismo comportamiento, lo que sugiere que no es un problema de hardware. Lo que creo que podría estar sucediendo es que la interrupción OCR1B se activa de alguna manera cuando se activa la otra interrupción, lo que provoca una alternancia adicional de OCR1B en el pin de salida. Una vez que esto sucede, sigue funcionando como debería, alternando el pin pero en un estado invertido. Esto se ilustra en el siguiente diagrama de tiempo.

ingrese la descripción de la imagen aquí

Es posible que tenga algún error con mi pila y que la dirección ISR esté dañada. Las direcciones que probé se muestran a continuación con la X y la dirección de destino con la flecha. Agradezco cualquier ayuda.

ingrese la descripción de la imagen aquí

#define SS_Hi()         PORTB |= _BV(4)
#define SS_Lo()         PORTB &= ~ _BV(4)
#define SS_IN()     (PINB&_BV(PINB4))


void init_TCNT1(void) {

    // setup timer counter 1

    // PWM, Phase Frequency Correct 8-bit. TOP = ICR1

    TCCR1A |= _BV(WGM11);
    TCCR1B |= _BV(WGM13);
    TCCR1B |= _BV(CS11); 

    ICR1 = 40;  

    TIMSK1 |= _BV(OCIE1A); 
//  TIMSK1 |= _BV(OCIE1B);

    OCR1A = 10; 
    OCR1B = 10;
    TCNT1 = 0; 

}

ISR(TIMER1_COMPA_vect) {

    if ((EnA_1() != 0) && (EnB_1() != 0)) { // only if both enable pins are high

    // if (PWM_PIN_1() == 0) PWMIN_1_Hi(); else PWMIN_1_Lo();

    }
    else PWMIN_1_Lo();
} // End Timer 1 Compare A ISR

ISR(TIMER1_COMPB_vect) {
// ISR For Motor 2
    if (SS_IN() == 0) SS_Hi(); else SS_Lo();
} // End Timer 1 Compare B ISR
Pregunta: ¿Está alternando manualmente los pines de salida o está usando las salidas OCRn para hacer PWM automáticamente en esos pines? Casi parece que lo estás haciendo manualmente.
A mano. El pin PWM real está conectado a un controlador IC, así que lo moví en código a un pin no utilizado (pin de selección de esclavo en PB4) para solucionar problemas.
¿Ves este problema incluso cuando lo haces automáticamente? Utilicé PWM en ATMega para docenas de aplicaciones y no tuve problemas cuando se configuró correctamente (y el pwm automático no se preocupará por la pila o cualquier otra actividad (como las interrupciones de UART) ya que los registros de control del temporizador están en un diferente espacio de memoria y todo sucede sin control de la CPU).
Entonces, si se pierde una interrupción de coincidencia de comparación, ¿la señal se invertirá?
Lo probé usando el pin OCR1B en PD4 y no se invierte.

Respuestas (2)

Con la ayuda de los comentarios y la publicación a continuación, pude determinar la causa del problema.

https://stackoverflow.com/questions/24128926/what-happens-when-an-isr-is-running-and-other-interrupt-happens

La causa del problema no es una interrupción adicional, ya que originalmente pensé que se está perdiendo una porque las interrupciones globales se deshabilitaron para manejar otra ISR. Pude verificar esto agregando un sei(); instrucción al comienzo de todos los demás controladores ISR y el problema de inversión desapareció. Volver a habilitar las interrupciones globales en todos los demás ISR no es una solución ideal y causa más problemas, especialmente con el UART. La mejor solución sería usar los pines OCR1A/B en PD4 y PD5; sin embargo, estos pines se usan para otra cosa, por lo que no es realmente una opción. La solución que se me ocurrió es usar la interrupción de desbordamiento del temporizador 1 para borrar el pin de salida que se dispara en la PARTE INFERIOR en el modo PWM de fase y frecuencia.

#define SS_Hi() PORTB |= _BV(4)
# define SS_Lo() PORTB &= ~_BV(4)
# define SS_IN()(PINB & _BV(PINB4))

void init_TCNT1(void) {
  // setup timer counter 1
  // PWM, Phase and Frequency Correct 8-bit. TOP = ICR1

  TCCR1A |= _BV(WGM11);
  TCCR1B |= _BV(WGM13);
  TCCR1B |= _BV(CS11);

  ICR1 = 40;

  TIMSK1 |= _BV(OCIE1A);
  TIMSK1 |= _BV(OCIE1B);
  TIMSK1 |= _BV(TOIE1);

  OCR1A = 10; //Set the top value
  OCR1B = 10; // Set the compare value 
  TCNT1 = 0; // intitilize the start value 

}

ISR(TIMER1_COMPA_vect) {

    if ((EnA_1() != 0) && (EnB_1() != 0))

  } // End Timer 1 Compare A ISR

ISR(TIMER1_COMPB_vect) {
    // ISR For Motor 2

    if (SS_IN() == 0) SS_Hi();
    else SS_Lo();

  } // End Timer 1 Compare B ISR

ISR(TIMER1_OVF_vect) {

  SS_Lo();
}

Dado que está alternando manualmente los pines en el isr, está expuesto a rmw. Así que lo que has experimentado es posible.

Configure los bits COMx en su lugar.

Desafortunadamente, esto está en una placa de circuito impreso donde los pines COMx van a otra parte.
Estaba hablando de los bits COMx, no de los pines. Como com1a1..0 o com1b1..0. Especialmente para sacar los pines ocr1x al modo propio.
Si esos pines se usan en otro lugar, intente pin flip en su lugar o registros de sombra.
Siempre es una mala práctica producir una placa de circuito impreso cuando no se ha definido el software.
Tengo curiosidad por seguir el registro, ¿puede proporcionar más detalles sobre cómo lograr esto o una guía a la que pueda hacer referencia?