Tool/software:
I am trying to detect rising and falling edges on GPIO via interrupt.
Since MSP430FR6043 allows the detection of only one type of edge, I am changing PxIES within ISR by reading the current value of the pin.
This method works fine most of the time, but it sometimes fails to detect edges.
So far, I only saw cases where it misses the falling edges, but I cannot say that all rising edges are detected.
The pin is connected to a PWM signal of 125Hz, so there's a 4ms gap between each ISR occurrence. When I measured the pin, the high level was 3.32V, and the low level was 0V.
How can I make sure that all edges are detected?
#pragma vector = PORT6_VECTOR __interrupt void EIC_PORT6_ISR(void) { HWREG16(P6_BASE + OFS_PAIFG) &= ~(GPIO_PIN0 << 8); if((HWREG16(P6_BASE + OFS_PAIN) & (GPIO_PIN0 << 8)) > 0) { HWREG16(P6_BASE + OFS_PAIES) |= (GPIO_PIN0 << 8); GPIO_PIN0_Value = true; } else { HWREG16(P6_BASE + OFS_PAIES) &= ~(GPIO_PIN0 << 8); GPIO_PIN0_Value = false; } HWREG16(P6_BASE + OFS_PAIFG) &= ~(GPIO_PIN0 << 8); }
Thanks!
You mentioned that your signal is PWM (125Hz/4ms). Is it close to 50% duty, or at least not close to 0% or 100%? That would be the prime hazard: If two edges appeared very close together.
How do you tell that edges are being missed? (Scope? Counter? Debugger?)
It is close to 50% duty. 8ms cycle with 4ms high, 4ms low.
I connected the pins to the Scope and MSP430FR6043 to the debugger and monitored GPIO_PIN0_Value.
I inserted the code below into the ISR handler and the main function. In the ISR handler, I added a sequence to check for HWREG16(P6_BASE + OFS_PAIES) values in case the interrupt edge switching was not applied.
From what I observed, in the ISR handler, HWREG16(P6_BASE + OFS_PAIES) has the correct value (I checked both via HWREG16(P6_BASE + OFS_PAIES) and through register values read by the debugger). Also, GPIO_PIN0_Value matches with the PAIN value inside the ISR handler.
However, in the main function, I detected a few cases where GPIO_PIN0_Value is 1, but both PAIN and the oscilloscope indicate that the signal level is low.
Which is why I suspect that it's missing the falling edges sometimes.
if((HWREG16(P6_BASE + OFS_PAIN) & (GPIO_PIN0 << 8)) > 0) { if(GPIO_PIN0_Value != true) { ISR_Missed = true; } else { ISR_Missed = false; } } else { if(GPIO_PIN0_Value != false) { ISR_Missed = true; } else { ISR_Missed = false; } }
I was considering that or polling GPIO values along with the interrupt.
But it would be nice to know the cause of the issue to make sure the same thing doesn't occur.
Is GPIO_PIN0_Value declared "volatile"? I'm wondering if main() is holding onto a stale copy of it.
Hi, Min
How do you check GPIO_PIN0_Value, in debug? But compare it with Scope in realtime, I think there maybe some time delay between debugger and Scope.
Can you try to set a GPIO output to indicate the Rasing/Falling edge detected in interrupt? Also try to catch input 125Hz PWM and output GPIO for real time debug.
Also, I recommend you to use this method, with less software steps and more stable:
If it were me, I would use two pins. One configured for the rising and the other for the falling edge. Then read P6IV in the ISR to find out which one caused the interrupt. No need to read P6IN or change P6IES.
Regards,
Helic
The fact that this is visible only in main, not in the ISR, suggests to me that main's observation is victim to a race (or other illusion).
However, on the remote chance that there is some latency thing (yes, 4ms is a long time), I'll observe:
The table in User Guide (SLAU367P) Sec 12.2.6.2 describes the cases where switching IES can trigger an IFG. It turns out that these conditions are exactly the ones you would see if you just missed an edge. E.g. When switching to falling-edge, if the PxIN is already low the IFG is set.
In effect, these conditions tell you retroactively about an edge you didn't see. The IFG will take you immediately back to the ISR to notice it. This allows you (asymptotically) up to a full cycle of latency.
The strategy would be:
1) Deduce which edge was triggered based on IES, not PxIN; if it was a falling edge, conclude that the pin transitioned to 0, else to 1.
2) Always reverse ("^=") IES.
3) Don't clear the IFG at the end of the ISR, only at the beginning, since you want to know about the IES trigger.
This might look something like (untested):
__interrupt void PORT6_ISR(void) { P6IFG &= ~BIT0; // We "know" it's P6.0 that got us here. if (P6IES & BIT0) // Falling edge? GPIO_PIN0_Value = false; // Must have become 0 else GPIO_PIN0_Value = true; // Must have become 1 P6IES ^= BIT0; // Reverse sense; set IFG if we missed an edge return; }
Hello Helic,
I initially had a separate MCU that generated PWM using a 4 ms timer and GPIO control.
To check the GPIO interrupt operation, I moved the 4ms timer and GPIO operation to the MSP430FR6043 and made a separate MCU to output the mirrored GPIO signal received from the MSP430FR6043 and send it back to P6.0 on the MSP430FR6043. I created a variable to check the PWM output level for debugging purposes. I confirmed that the PWM generation by MSP430FR6043 and the mirroring of the signal by the separate MCU works without a problem on an oscilloscope. Then I connected the debugger on MSP430FR6043 and set a breakpoint to my code above where it checks if GPIO_PIN0_Value doesn't match the P6.0 input value.
From this process, I confirmed the GPIO_PIN0_Value mismatch with the P6.0 input value occurs when the MSP430FR6043 PWM signal is low and the separate MCU is also outputting the low signal. The breakpoint stopped at was in the main function and not in the ISR.
And I concluded that the falling edge was missed by ISR based on this.
Thanks,
Min
Then I connected the debugger on MSP430FR6043 and set a breakpoint to my code above where it checks if GPIO_PIN0_Value doesn't match the P6.0 input value.
That code to compare PIN0_Value to P6.0 isn't a single instruction. So what happens if the interrupt arrives in the middle? One of them changes and you get a mismatch.
I also realized that the instruction was too long so I modified the code as follows:
if(GPIO_PIN0_Value != ((HWREG16(P6_BASE + OFS_PAIN) & (GPIO_PIN0 << 8)) > 0)) { ISR_Missed = true; } else { ISR_Missed = false; }
Also, since the PWM level change occurs every 4ms, I set the CCR interrupt at 2ms to delay the GPIO_PIN0_Value check. And the result is the same
Any gap between reading the two values is a hazard. Putting them in one C statement does not result in a single assembly language instruction.
The correct way to handle a critical section like this is to disable interrupts before entering it and enable them after.
I tried the suggestion to use PxIES instead of PxIN in the ISR for determining the GPIO level and to toggle PxIES.
I also added disable/enable interrupts before and after the GPIO level consistency check instruction.
However, the result is still the same that it's missing falling edges.
I am note sure how long time will this function takes.
You can add some GPIO set/clear function here, to check the task run duration.
By observe the GPIO positive pulse width using Scope.
// Debug GPIO set here if(GPIO_PIN0_Value != ((HWREG16(P6_BASE + OFS_PAIN) & (GPIO_PIN0 << 8)) > 0)) { ISR_Missed = true; } else { ISR_Missed = false; } // Debug GPIO clear here
While working on some code to test this on a G2553 Launchpad, I realized that there was a second hazard. Besides the hazard of the interrupt being triggered in the middle of the check, the pin can change.
Once I added code to disable checks near an edge, the problem vanished.
So I suspect that this inconsistency is a product of the code and not the result of actually missing interrupts.
**Attention** This is a public forum