I'm trying to configure Timer1 of a pic24h to 1ms per timer tick but don't seem to be able to get the math to match my results.

I have a 20MHz external oscillator (with a 1M resistor in parallel for stability) and think I have it enabled with the following configuration bits set in code:

#pragma config FNOSC = PRIPLL           // Oscillator Mode (Primary Oscillator (XT, HS, EC) w/ PLL)
#pragma config IESO = ON                // Internal External Switch Over Mode (Start-up device with FRC, then automatically switch to user-selected oscillator source when ready)

I'm toggling a pin and watching it on the scope to check my math. I thought the calculation is as follows:

//  ms_per_tick = ((F_OSC / 2)/prescaler)^-1 * PR1
//                    = ((20MHz / 2) / 8)^-1 * 4200
//          NO= 1ms

bit a value of PR1 = 4200 is about the closest I can get to a tick of 1ms.

What am I missing? Does the PLL change the value of what I think is F_OSC. Is there a way to validate F_OSC?

The ISR just increments a variable and clears the interrupt flag. Here is the initialization code:

T1CONbits.TCKPS = 0b01;   // configure the timer prescaler to divide-by-8
T1CONbits.TCS = 0;        // do NOT use the external clock
PR1 = 4200;               // configure the timer period

// configure the Timer1 interrupt
_T1IF = 0;             // begin with the interrupt flag cleared
_T1IE = 1;             // enable the interrupt
T1CONbits.TON = 1;         // turn on timer1

I'm starting to learn more about the PLL via some SFR's:

CLKDIV is: 0x3040 (PLLPOST = 1, PLLPRE = 0)
PLLFBD is: 0x0030 (PLLDIV = 48)

I also just found this on p.16 of the datasheet:

"If the PLL of the target device is enabled and configured for the device start-up oscillator, the maximum oscillator source frequency must be limited to ≤8 MHz for start-up with the PLL enabled to comply with device PLL start-up conditions. This means that if the external oscillator frequency is outside this range, the application must start-up in the FRC mode first. The default PLL settings after a POR with an oscillator frequency outside this range will violate the device operating speed. Once the device powers up, the application firmware can initialize the PLL SFRs, CLKDIV and PLLDBF to a suitable value, and then perform a clock switch to the Oscillator + PLL clock source. Note that clock switching must be enabled in the device Configuration word."

Now, the first thing I run after main is this example code:

void ConfigureOscillator(void) {
  // Disable the Watch Dog Timer
  RCONbits.SWDTEN = 0;

  // When clock switch occurs switch to Prim Osc (HS, XT, EC)with PLL
  __builtin_write_OSCCONH(0x03);  // Set OSCCONH for clock switch
  __builtin_write_OSCCONL(0x01);  // Start clock switching

  // Wait for Clock switch to occur
  while(OSCCONbits.COSC != 0b011);

  // Wait for PLL to lock, if PLL is used
  while (OSCCONbits.LOCK != 1);
  • \$\begingroup\$ Where is your timer initialization code and ISR? Also, pretty sure you want FNOSC set to HSPLL, but double check that for the 16 bit PICs. \$\endgroup\$
    – Matt Young
    Nov 4, 2013 at 4:08
  • \$\begingroup\$ @MattYoung HSPLL is not an option in the drop-down menu under Window >> PIC Memory Views >> Configuration bits for this PIC. \$\endgroup\$
    – tarabyte
    Nov 4, 2013 at 4:25

2 Answers 2


Two things to recognize:

  1. the timer is an upcounter that interrupts on overflows at which point you should reload the start of the upcounter.
  2. the frequency is the output of the PLL if you have one configured
= (f_osc / 4)^-1 * prescaler * (65535 - TMR1)
= (16MHz / 4)^-1 * 1 * (1000)
= 250us

Where timer is being used as a 16-bit timer and TMR1 is the value written to TMR1. I like to specify the preload as a negative number because it's more intuitive than seeing something as less than UINT16_MAX:

#define TIMER1_TIME  (-1000)

void InitTimer1(void) {
  T1CONbits.T1CKPS = 0b00;              // Prescaler is divide-by-1
  T1CONbits.TMR1CS = 0b00;              // Clock source is (f_osc / 4)
  TMR1H = TIMER1_TIME >> 8;             // Load the start of the upcounter
  IPR1bits.TMR1IP = 1;                  // Set to high priority
  PIR1bits.TMR1IF = 0;                  // Clear interrupt flag
  PIE1bits.TMR1IE = 1;                  // Enable interrupt
  T1CONbits.TMR1ON = 1;                 // Turn on the the timer

void MyIsr(void) {
  // Reload the start of the upcounter
  TMR1H = TIMER1_TIME >> 8;

The timer0 its a 16bits timer, so tu set to interrupt every 1 second you need set:

- Prescaler @ 128

- TMR0 Preload @ 26474

The equation:

\$ T= \left(\left({1 \over Fosc}\right)\times4\right)\times Presc\times\left(Resolution - Preload\right) \$


  • T = Period = 1s
  • Fosc = Oscilator Frequency = 20Mhz
  • Presc = Prescaler = 128
  • Resolution = 2^16 = 65535
  • Preload = 26474

The result will be: 0.9999872s, it will be the nearest from 1s will can reach.

Sample code:

// Timer1 Registers: Prescaler=1:1; TMR1 Preset=60536; Freq=1.000,00Hz; Period=1,00 ms
T1CON.T1CKPS1 = 0;  // bits 5-4  Prescaler Rate Select bits
T1CON.T1CKPS0 = 0;  // bit 4
T1CON.T1OSCEN = 1;  // bit 3 Timer1 Oscillator Enable Control: bit 1=on
T1CON.T1SYNC  = 1;  // bit 2 Timer1 External Clock Input Synchronization Control bit:1=Do not synchronize external clock input
T1CON.TMR1CS  = 0;  // bit 1 Timer1 Clock Source Select bit:0=Internal clock (FOSC/4) / 1 = External clock from pin T1CKI (on the rising edge)
T1CON.TMR1ON  = 1;  // bit 0 enables timer
TMR1H = $EC;         // preset for timer1 MSB register
TMR1L = $78;         // preset for timer1 LSB register

Using CCS compiler on MPALB 8:

// Timer 1 declaration

// Timer 1 Method
void timer1Method()
    //your timer1 code here
  • \$\begingroup\$ That equation does not match what I see on the o-scope. When I increase PR1, I increase the overall time until the interrupt fires again. It seems more like (2 / F_osc) * Prescaler * PR1 but I'm still trying to confirm the exact transfer function \$\endgroup\$
    – tarabyte
    Nov 27, 2013 at 19:29
  • \$\begingroup\$ It may be a difference in what micrcontrollers we're assuming. I'm using: dsPIC33EPXXXMC20X and it does not even have a prescaler option for 128. I'm also in MPLABX so some of the assumed macros you're using are not relevant to me. I appreciate you help though. \$\endgroup\$
    – tarabyte
    Dec 6, 2013 at 17:45
  • \$\begingroup\$ 2^16 = 2^16 = 65536 not 65535. 65536 is the resolution, 65535 is the maximum reload value. \$\endgroup\$
    – Clifford
    Oct 1, 2022 at 8:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.