I've written a function in which you should able to input a number and the function then calculates the value to write to the register. However it looks like it's writing the "wrong" value to the register. I've used a calculator and wolframalpha to confirm that I'm not screwing up order of operations. The function is listed below:
void SetFrequency_Hz(int Freq) {
PR4 = ((41667000 / (4 * 16 * Freq)) - 1);
}
When I try to set the Frequency to 20kHz (20,000Hz), I would need to set the PR4 register to 32. Which in theory if I put in the 20000 calue into the function should be spit out, BUT it's spitting out 179 for some reason. Any guesses why?
MCU: PIC18F67J60 IDE: MP LAB X
Since in PIC18 a int
type would be only 16 bits, the somewhat arcane implicit conversion rules in C will cause intermediate results in your expression to be truncated. I am surprised that your compiler did not issue a warning for the literal 41667000 since that clearly will not fit in a PIC18 int
type in any case.
The problem can be solved easily by using explicit literal type suffixes to change the overall type of the expression:
PR4 = 41667000ul / (64ul * Freq) - 1u ;
or if the required frequency resolution is in KHz, you can scale the expression:
PR4 = 41667u / (64u * (Freq/1000)) - 1u ;
It should be noted however that in both these cases the real value of 41667000 / (64 x 20x103)) - 1 is ~31.55 so the value in PR4 will be 31 not 32, and will result in an actual frequency of 20345Hz.
To round to the nearest integer value of the real expression:
PR4 = (41667000ul / (32ul * Freq) - 1u) / 2 ;
That will result in PR4=32 and a frequency of 19729Hz.
It may be useful to have the function return the actual achieved frequency:
unsigned SetFrequency_Hz( unsigned ideal_freq )
{
PR4 = (41667000ul / (32ul * ideal_freq ) - 1u) / 2u ;
// Return actual achievable frequency
return 41667000ul / ((PR4 + 1ul) * 64ul)
}