I know it's a bit late, but I've just got this sabe doubt. After looking around, I've come across this Microchip Doc that shows some examples.
First, we calculate \$\text{PR2}\$. From this formula,
$$ F_\text{PWM} = \dfrac{1}{(\text{PR2} + 1) \times 4 \times T_\text{OSC} \times \text{T2CKPS}} $$
we get
$$ \text{PR2} = \dfrac{1}{F_\text{PWM} \times 4 \times T_\text{OSC} \times \text{T2CKPS}} - 1 $$
where \$T_\text{OSC} = 1/F_\text{OSC}\$, and \$\text{T2CKPS}\$ is the Timer2 prescaler value (1, 4 or 16).
Therefore, if we want \$F_\text{PWM} = 20\text{kHz}\$, and choosing \$\text{T2CKPS} = 1\$, we get \$\text{PR2} = 249\$. We should choose higher values for \$\text{T2CKPS}\$ only if \$\text{PR2}\$ exceeds 8 bits (\$\text{PR2} \gt 255\$) for the given prescale.
Now we calculate the max PWM resolution for the given frequency:
$$ \text{max PWM resolution} = \log_2(\;\dfrac{F_\text{OSC}}{F_\text{PWM}}\;) $$
That gives us \$9.9658\$ bits (I know, it sounds weird, but we'll use it like that later).
Now, let's calculate the PWM duty cycle. It is specified by the 10-bit value \$\text{CCPRxL:DCxB1:DCxB0}\$, that is, \$\text{CCPRxL}\$ bits as the most significant part, and \$\text{DCxB1}\$ and \$\text{DCxB0}\$ (bits 5 and 4 of \$\text{CCPxCON}\$) the least significant bits. Let's call this value \$\text{DCxB9:DCxB0}\$, or simply \$\text{DCx}\$. (x is the CCP number)
In our case, since we have a max PWM resolution of \$9.9658\$ bits, the PWM duty cycle (that is, the value of \$\text{DCx}\$) must be a value between \$0\$ and \$2^{9.9658} - 1 = 999\$. So, if we want a duty cycle of 50%, \$\text{DCx} = 0.5 \times 999 = 499.5 \approx 500\$.
The formula given on the datasheet (also on the linked doc),
$$\text{duty cycle} = \text{DCx} \times T_\text{OSC} \times \text{T2CKPS}$$
gives us the pulse duration, in seconds. In our case, it's equal to \$25\text{ns}\$. Since \$T_\text{PWM} = 50\text{ns}\$, it's obvious that we have a 50% duty cycle.
That said, to calculate DCx in terms of duty cycle as \$r \in [0,1]\$, we do:
$$ \text{DCx} = \dfrac{r \times T_\text{PWM}}{T_\text{OSC} \times \text{T2CKPS}} = \dfrac{r \times F_\text{OSC}}{F_\text{PWM} \times \text{T2CKPS}} $$
Answering your other questions:
2) The resolution of your PWM pulse with period \$T_\text{PWM}\$ is
$$ \dfrac{T_\text{PWM}}{2^\text{max PWM res}} $$
3) Because CCPRxL, along with DCxB1 and DCxB0, determine the pulse duration. Setting CCPRxL with a higher value than \$2^\text{max PWM res} - 1\$ means a pulse duration higher than the PWM period, and therefore you'll get a flat \$V_{DD}\$ signal.
There are various ways to measure the width of incoming pulses. Which is most appropriate depends on the width of the pulses, what accuracy you need, and which peripherals your PIC has. Some possibilities are:
- Use two CCP modules in capture mode. This is not possible for your particular PIC since it has only one CCP module, I'll mention it anyway for others or in case you change the PIC. A CCP module in capture mode grabs a snapshot of the free running 16 bit timer 1 on the edge of a I/O pin. With two of these captures, one for each edge, you subtract them to get the pulse duration.
This method allows for the shortest duration pulses and has the most accuracy, but takes two CCP modules.
- Use a single CCP module and flip the edge it captures on in a interrupt triggered by the first capture. This has the same accuracy and resolution as #1 and uses only a single CCP module, but requires the pulse to be some minimum width so that the interrupt routine has time to grab the captured value switch the capture edge polarity.
- Capture the free running timer 1 in firmware every edge, then do the subtraction as in #1 and #2. This requires no CCP module at all, but there must be some minimum time between edges for the interrupt routine to do its job. It also has a bit more error due to some uncertainty in the interrupt response, and possibly in variable number of instructions from the interrupt routine if not coded carefully.
- Gate timer 1 from the pulse. Have the trailing edge cause a interrupt, which then reads the timer 1 value and possibly resets it ready for the next pulse. This has good accuracy and resolution, but requires some minimum time between pulses for the interrupt to grab the timer 1 value and set up for the next pulse.
There are other methods too, but without knowing more about the characteristics of your pulses it's not worth spending time on them. Given that your PIC has a single CCP module and timer 1 with gating option, methods 2 and 4 are worth considering. Again, it would help to know more about the pulse characteristics.
One thing I didn't metion is overflow handling for long pulses, but if your pulses are known to be short enough this is not a issue.
Best Answer
Thank you PeterJ. Clearing the WDT inside the loop fixed the issue. Following is the modified part of the code.