I have an ATMEGA324P controlling a dual motor driver circuit. I'm using the 16-bit Timer Counter 1 in Phase and Frequency Correct mode to create (2) PWM outputs using the ICR1 as the TOP value and the OCR1A and OCR1B to generate the PWM signals on the output pins. The problem persists the same on OCR1A and OCR1B so I have disabled OCR1A to simplify troubleshooting.
The problem I am experiencing is when any other interrupts on the system fires , such as UART or PCINT, the PWM duty cycle will invert. For example if the duty cycle is set to 75% it will invert to 25% and flop back and forth as other interrupts fire making the output extremely erratic. The scope shots below show the desired output on top and the inverted on bottom. I've been able to trigger this inversion by UART RX ISR, Pin change interrupt ISR, and Timer Counter 0 and 2 Compare A & B ISR's. For example sending a character to the UART will cause it to invert.
Everything seems to work in my code (below) except for this issue. I have tried a new circuit board and it exhibited the exact same behavior which suggests it's not a hardware problem. What I think might be happening is the OCR1B interrupt is somehow triggered when the other interrupt fires causing an extra OCR1B toggle of the output pin. Once this happens it keeps working as it should, toggling the pin but in an inverted state. This is illustrated in the timing diagram below.
It's possible I have some error with my stack and the ISR address is being corrupted. The addresses I've tried are shown below with the X and the target address with the arrow. I appreciate any help.
#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