I think I've got an approach that addresses the 0%, 100%, near 0%, and near 100% cases, and uses the TPM input capture (one channel only) to obtain good resolution.
The trick is to let the input capture ISR just capture and store data, and let the task which needs the duty cycle compute it from this data.
Set up TPM2C0 for input capture, both edges, interrupt enabled, serviced by ISR_TPM
Set up an interrupt-driven timed task ISR_RTT executing at 2000Hz. This is the main control task that needs the input PWM value for its computations.
ISR_RTT increments a counter.
ISR_TPM reads the latched TPM counter value and the logic level of the input pin, and stores them in a ring buffer. Call these values C1 and L1, respectively. Similarly, C2 and L2 are the previous values, and C3 and L3 are the values before that. The ISR_RTT counter is read and stored in R1.
ISR_RTT contains the following logic to compute the duty cycle (DC):
Let R be the present value of the ISR_RTT counter.
if R-R1 is greater than the period of the PWM being measured, then set DC to 0% or 100% depending on the value of L1. Otherwise, do this:
L1=0 and L2=1? Compute DC using C1-C2 (but not to exceed 100%)
L1=0 and L2=0? this is the near-zero condition. Set DC=0%. Sanity check by testing C1-C2.
L1=1 and L2=1? this is the near-100 condition. Set DC=100%. Sanity check by testing C1-C2.
L1=1 and L2=0? you're in the middle of a pulse. do the following:
L3=1? Compute DC using C2-C3 (but not to exceed 100%)
L3=0? this is the near-zero condition. Set DC=0%. Sanity check by testing C2-C3.