CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

Timer 2: determine best prescaler, postscaler, period

 
Post new topic   Reply to topic    CCS Forum Index -> Code Library
View previous topic :: View next topic  
Author Message
lindsay.wilson.88



Joined: 11 Sep 2024
Posts: 40

View user's profile Send private message

Timer 2: determine best prescaler, postscaler, period
PostPosted: Wed Sep 25, 2024 4:35 pm     Reply with quote

Don't know if this is useful, but I needed to figure it out so maybe someone else does as well ;-)

Suppose you want to have timer 2 interrupts running at, or as close as possible to, a frequency specified by the user (e.g. over the UART). There are three parameters which need to be chosen:

prescaler (1,4,16)
postscaler (1-16)
period (0-255)

I wrote a program which goes through each combination of prescaler and postscaler, works out the period, and then figures out which combination results in the closest frequency match. I used floats for most of the calculations because I'm lazy, so it's not particularly fast (30-40ms on a 39MHz 18F4520), but for my application it's fine as the user only configures the timer frequency occasionally.

The resulting frequency is pretty close - the error is no worse than maybe 0.2% over the entire frequency range (in my case, 150Hz to 65kHz). For greater precision, timer 0 is better since it's 16-bit, but there were various reasons I prefer timer 2, mainly the interrupt-on-match behaviour rather than interrupt-on-overflow. Long story ;-)

Header:

Code:
#include <18F4520.h>
#device ADC=10

#FUSES NOWDT                    //No Watch Dog Timer

#use delay(clock=39.3216MHz,crystal=9.8304MHz)
#use rs232(baud=9600,parity=N,UART1,bits=8)
#use FAST_IO(ALL)


Main code:

Code:
#include <main.h>
#include <stdlib.h>
#include <math.h>

// Text received over UART
char uart_text[10];

// Main oscillator frequency
int32 fosc;

// Desired frequency we want timer to run at
int16 tmr2_desired_freq;

// Test values for prescaler, postscaler and period. The program runs
// through lots of combinations of each to find the best.
int8 tmr2_test_prescaler;
int8 tmr2_test_postscaler;
float tmr2_test_period;

// Actual frequency resulting from using the above test values
float tmr2_test_freq;

// Frequency error - difference between desired and actual
float tmr2_freq_error;

// Minimum frequency error so far - used to check whether the current
// test values result in a better frequency match or not
float tmr2_min_freq_error;

// The best values of prescaler, postscaler and period. These result
// in the smallest possible error
int8 tmr2_best_prescaler;
int8 tmr2_best_postscaler;
int8 tmr2_best_period;

// Timer interrupt routine - give an output pulse just to see what's happening
#int_timer2
void timerinterrupt()
{
   output_high(PIN_C0);
   output_low(PIN_C0);
}

void main()
{
   // General setup
   setup_adc_ports(NO_ANALOGS,VSS_VDD);
   set_tris_c(0b10000000); // Remember to leave the UART TX as output!
   setup_timer_2(T2_DIV_BY_1,100,16); // Set timer to something to start with
   enable_interrupts(int_TIMER2);
   enable_interrupts(GLOBAL);
   fosc=39321600;
   
   while(TRUE)
   {
      // Get desired frequency from the user over UART
      printf("Enter desired frequency\r\n");
      gets(uart_text);
      tmr2_desired_freq=atol(uart_text);
      printf("Desired frequency: %lu\r\n",tmr2_desired_freq);
     
      // Set the minimum error to something very large and initialise
      // the other test parameters to those that give the lowest
      // frequency(i.e. "safest")in case best values aren't
      // found for some reason
      tmr2_min_freq_error=100000;
      tmr2_best_prescaler=16;
      tmr2_best_postscaler=16;
      tmr2_best_period=255;
     
      // Now go through every possible combination of prescaler and postscaler
      // values. Work out the period which gives the desired frequency. Find
      // the closest integer value to this. Then work out the frequency resulting
      // from this integer value. Work out the error between this frequency and the
      // desired frequency. if this error is less than the previous minimum error,
      // update the best values.
      for(tmr2_test_prescaler=1; tmr2_test_prescaler<17; tmr2_test_prescaler*=4) // 1,4,16     
      {
         for(tmr2_test_postscaler=1; tmr2_test_postscaler<17; tmr2_test_postscaler++) // 1-16
         {
            // Work out the test period. Do as float to handle negative and fractional values.
            tmr2_test_period=(fosc/((float)4*tmr2_test_prescaler*tmr2_test_postscaler*tmr2_desired_freq))-1;
            if(tmr2_test_period<0){tmr2_test_period=0;} // If less than zero, set to zero
            if(tmr2_test_period>255){tmr2_test_period=255;} // If greater than 255, set to 255
            tmr2_test_period=floor(tmr2_test_period+0.5); // Round to nearest integer
           
            // Work out the frequency obtained by using this period. Again, do as float to handle fractional values.
            tmr2_test_freq=fosc/((float)4*tmr2_test_prescaler*(tmr2_test_period+1)*tmr2_test_postscaler);
           
            // Work out the absolute error between the test and desired frequencies
            tmr2_freq_error=abs(tmr2_test_freq-(float)tmr2_desired_freq);
           
            // If the current error is less than the minimum error, we've found a possible candidate
            // for the best choice of parameters. Update the minimum error and the best prescaler,
            // postscaler and period values
            if(tmr2_freq_error<tmr2_min_freq_error)
            {
               tmr2_min_freq_error=tmr2_freq_error;
               tmr2_best_prescaler=tmr2_test_prescaler;
               tmr2_best_postscaler=tmr2_test_postscaler;
               tmr2_best_period=tmr2_test_period;
            }
         }
      }
     
      // By this point, we should have best choices for the prescaler, postscaler and period
      // Update the actual timer with these values. Need to use switch because you can't just
      // pass the prescaler value directly to the setup_timer_2 function.
      switch(tmr2_best_prescaler)
      {
         case 1: setup_timer_2(T2_DIV_BY_1,tmr2_best_period,tmr2_best_postscaler); break;
         case 4: setup_timer_2(T2_DIV_BY_4,tmr2_best_period,tmr2_best_postscaler); break;
         case 16: setup_timer_2(T2_DIV_BY_16,tmr2_best_period,tmr2_best_postscaler); break;
      }
     
      // Print results
      printf("Best results:\r\n");
      printf("Prescaler %u\r\n",tmr2_best_prescaler);
      printf("Postscaler %u\r\n",tmr2_best_postscaler);
      printf("Period %u\r\n",tmr2_best_period);
   }
}
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> Code Library All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group