News:

;) This forum is the property of Proton software developers

Main Menu

PIC18F25K22 Timer3 Issue

Started by Bernard, May 07, 2026, 09:23 AM

Previous topic - Next topic

Bernard

I am trying to use Timer3 on a PIC18F25K22 to measure the execution time of various sections of code.
I tested if this was possible with the code below. The include file was written by Les Johnson in reply to my
Proton8 thread: PIC18F25K22 10-bit PWM? on July 10 2024 and can be inspected there.

Include "18F25K22pwm.inc"    ' Configure 18F25K22 and set PLL to achieve 64MHz
Dim T3Val As Word            ' Variable for 16-bit Timer3 count TMR3
' Configure Timer3 Interrupt off: 16 bit mode: Internal Clock Tosc: 1:8 prescale: Sync off
T3CON = $B0                  ' Configure Timer for a 0.5 us tick and make sure it is off
' Make sure Timer3 interrupt is disabled
PIE2.1 = 0          ' TMR3IE = 0
PIR2.1 = 0          ' TMR3IF = 0  (clear flag)
' Test if Timer3 is functioning correctly
' Expect a T3Val reading of around 2000 for a 1 ms delay
TMR3H = 0           ' Clear Timer3 high byte first
TMR3L = 0           ' Clear Timer3 low byte
PIR2.1 = 0          ' Clear overflow flag
T3CON.0 = 1         ' Start Timer3
DelayMS 1           ' 1 ms delay to advance Timer3 count         
T3CON.0 = 0         ' Stop Timer3
T3Val.LowByte  = TMR3L      ' Read Timer3 value low byte first
T3Val.HighByte = TMR3H      ' Read Timer3 value high byte

When I output T3Val to a Terminal via SerOut I get 0. If I set TMR3 to any 16 bit number T3Val will return that number.
Timer3 does not seem to be starting, is there an error in my code or is this a known issue?

CPR

For Timer3 on a 18F26K22 @ 64Mhz I set it up like so -

        T3CON.7 = 0
        T3CON.6 = 0
        T3CON.5 = 1                                 '// 11=1:8, 10=1:4, 01=1:2, 00=1:1 prescale (Timer0=1:8)
        T3CON.4 = 1                                 '// WAS 1
        T3CON.3 = 0                                 '// Secondary oscillator disabled
        T3CON.2 = 1                                 '// Do not synchronise external clock
        T3CON.1 = 1                                 '// ENABLE 16 bit read write
        T3CON.0 = 0                                 '// 0 stops timer
        T3GCON.7 = 0                                '// Don't use gate function on timer
        IPR2.1 = 0                                  '// TIMER3 LOW priority int
        TMR3GIE = 0
        CCPR2H = 0
        CCPR2L = 0
        Symbol TMR3IF = PIR2.1
        Symbol TMR3IE = PIE2.1

Followed with
TMR3H  = 0
TMR3L  = 0
TMR3IE = 1
TMR3IF = 0
T3CON.0 = 1

For an interrupt 'tick' of around 32Hz.

Maybe of use to you?

trastikata

Quote from: Bernard on May 07, 2026, 09:23 AMPIC18F25K22


Hello Bernard,

you need to enable the Global Interrupt and Peripheral Interrupt bits. But the real problem is the clock source bits in T3CON.

Bernard

CPR. Your code worked perfectly for me. Incidentally Positron could not parse the line TMR3GIE = 0 so I replaced it with PIE3.1 = 0.
Thank you for your assistance.

Bernard

trastikata. Thank you for your reply.

CPR

Quote from: Bernard on May 07, 2026, 01:45 PMCPR. Your code worked perfectly for me. Incidentally Positron could not parse the line TMR3GIE = 0 so I replaced it with PIE3.1 = 0.
Thank you for your assistance.

Oops. My apologies. I omitted that in my copy & paste from my code. I declared

Symbol TMR3GIE = PIE3.1                    '// "0" DISABLE Gate interrupt

Glad you got it all working. As Trastikata said - don't forget to enable the interrupts. I'm using both high and low priority so I had this section.

        Symbol GIEL      = INTCON.6      '// Peripheral int enable, global int LOW priority. "1" enables
        Symbol GIEH      = INTCON.7      '// Global int enable "1" enables
        Symbol IPEN      = RCON.7        '// enable high AND low priority interrupts, "1" = BOTH

But - you have it all working, so all is good!  8)
 

Stephen Moss

Quote from: trastikata on May 07, 2026, 12:41 PMyou need to enable the Global Interrupt and Peripheral Interrupt bits.
A rough timing could be obtained by finding the first and last lines of code being timed in the assembler file, subtracting the line numbers and then multiplying by the instruction clock period.
If that is projected approximate time is well below 65535 then I using interrupts would not be necessary as Bernard is trying to time how long a section of code takes to execute using...
Start timer
code being timed
Stop timer

Thus if the count is not expected to exceed 65535 the interrupt will never fire and so setting the timer interrupt and writing an ISR to count the number of overflows would have no effect on the result. Equally, if the projected time is expected to exceed 65535 and a the slight decrease in accuracy resulting in an increase of the timer clock period to keep the projected timer count below 65535 is deemed acceptable using the timer interrupt could again be avoided.

Depending on the instruction cycle time, Timer tick time, the expected code execution and how much accuracy you want there could be situations where it may be better ensure that no timer overflow occurs as otherwise the execution time recorded for the code could be off as it will also include ISR execution time.
Even if you stop the timer at the beginning of the ISR and restart it at the end the context restore (if you wisely executed a context save) could still add a few extra timer ticks to the count during each execution of the interrupt.    

John Lawton

I have found it easy to measure code execution times using an oscilloscope. Set a latch output at the beginning of the code section and clear it at the end. This is also useful to measure a loop repetition rate.

John
-----------------------------------------------------------------------------------------
Amicus 8 and 16A/16B dev boards
Especially created for Positron development
https://www.easy-driver.co.uk/Amicus

RGV250

Hi Bernard,
I use a routine (post 3) https://protoncompiler.com/index.php/topic,1160.msg8835.html#msg8835
That shows how many cycles a routine takes, it uses timer 1 but I am sure it could work the same with timer 3.

regards,
Bob

Bernard

I do have an oscilloscope - I did not think about using it to check the timings so thanks for suggesting it.

xldaedalus

#10
A way to make your code more readable and useable use an alias for the Timer bytes,

  Symbol Timer3 = TMR3L.Word  'Word var from TMR3H and TMR3L

You can read the Timer as a 16bit value instead of two bytes at a time.  You can print it, use it.

It's also a good idea to name the bits not defined by the compiler so they match the bit names in the data sheet

   Symbol TMR3IF = PIR2.1      'Timer3 Interrupt Flag
   Symbol TMR3IE = PIE2.1      'Timer3 Interrupt Enable
   Symbol TMR3IP = IPR2.1      'Timer3 Interrupt Priority 1 = HIGH

Then you use 

Set TMR31E  'enable timer three interrupt.

Writing to the timer word while timer is ON can cause issues, turn OFF before writing.

A better way to manage time is to create a System Tick

Set the timer to interrupt once per millisecond, then increment a SysTick

This is a Timer0 High Priority interrupt to create a 1ms Tick, including the time to Context Save/Restore
SysTick rolls over at 65.535 seconds.  Or you can add a few extra lines to rollover at every 60.000 seconds.

SysTick is a WORD variable and Qtr is a byte.  Qtr is used to create a rough 5mS timer in the MAIN: / GOTO MAIN State Machine loop. Good for timing button presses, or other events with ease.

'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
  ISRhigh:    'high priority interrupt 18FxxK22
'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
 Context Save

'I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-
  If TMR0IF = 1 Then   '1ms High briority System Tick
'I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-
   clear TMR0ON                    'turn off the Timer to write to it
   Timer0 = Timer0Start           '1ms Interrupts
   Inc SysTick                    'inc system tick timer

'     If SysTick > 59999 Then      'uncomment for optional minute Timer
'       Clear SysTick               
'       Inc Minutes                '18.2 hours if Minutes is WORD variable
'     Endif     

   Inc Qtr                        'inc misc rough timer
  SET TMR0ON                      'Turn the timer back on

''I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-
 EndIf
''I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-

'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
Context Restore  'end ISRhigh
'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII

'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
InitTimer0:     'Set up Timer0
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Symbol Timer0 = TMR0L.Word  'Word var from TMR0H And TMR0L
Symbol Timer0Start = 49536  'Change this value to adjust, use Crystal Clock if super accurate time needed
Dim SysTick as Word         'counts 1ms interrupts
Dim Qtr as Byte             'Use in Main for rough timer, or other timed events
'Dim Minutes as Word        'Uncomment if optional Minutes Timer0 is used

T0CON  = %00001000         ' 16-bit, 1:1, Prescaler not assigned, Timer OFF
   Clear TMR0IF '= False   'interrupt flag must be cleared before timer is enable
   Timer0 = Timer0Start    '49536 1ms interrupts @ 64Mhz  16Mhz + PLL
   Clear TMR0IF              'clear interrupt flag
   Set TMR0IE              'enable Interrupt - Timer 0 is always HIGH priority
   Set TMR0ON              'Timer 0 = ON

Return

Never, never, never give up

CPR

Quote from: xldaedalus on May 10, 2026, 12:32 AMA way to make your code more readable and useable use an alias for the Timer bytes,

  Symbol Timer3 = TMR3L.Word  'Word var from TMR3H and TMR3L

You can read the Timer as a 16bit value instead of two bytes at a time.  You can print it, use it.

It's also a good idea to name the bits not defined by the compiler so they match the bit names in the data sheet

  Symbol TMR3IF = PIR2.1      'Timer3 Interrupt Flag
  Symbol TMR3IE = PIE2.1      'Timer3 Interrupt Enable
  Symbol TMR3IP = IPR2.1      'Timer3 Interrupt Priority 1 = HIGH

Then you use 

Set TMR31E  'enable timer three interrupt.

Writing to the timer word while timer is ON can cause issues, turn OFF before writing.

A better way to manage time is to create a System Tick

Set the timer to interrupt once per millisecond, then increment a SysTick

This is a Timer0 High Priority interrupt to create a 1ms Tick, including the time to Context Save/Restore
SysTick rolls over at 65.535 seconds.  Or you can add a few extra lines to rollover at every 60.000 seconds.

SysTick is a WORD variable and Qtr is a byte.  Qtr is used to create a rough 5mS timer in the MAIN: / GOTO MAIN State Machine loop. Good for timing button presses, or other events with ease.

'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
  ISRhigh:    'high priority interrupt 18FxxK22
'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
 Context Save

'I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-
  If TMR0IF = 1 Then  '1ms High briority System Tick
'I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-
  clear TMR0ON                    'turn off the Timer to write to it
  Timer0 = Timer0Start          '1ms Interrupts
  Inc SysTick                    'inc system tick timer

'    If SysTick > 59999 Then      'uncomment for optional minute Timer
'      Clear SysTick               
'      Inc Minutes                '18.2 hours if Minutes is WORD variable
'    Endif   

  Inc Qtr                        'inc misc rough timer
  SET TMR0ON                      'Turn the timer back on

''I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-
 EndIf
''I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-I-

'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
Context Restore  'end ISRhigh
'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII

'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
InitTimer0:    'Set up Timer0
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Symbol Timer0 = TMR0L.Word  'Word var from TMR0H And TMR0L
Symbol Timer0Start = 49536  'Change this value to adjust, use Crystal Clock if super accurate time needed
Dim SysTick as Word        'counts 1ms interrupts
Dim Qtr as Byte            'Use in Main for rough timer, or other timed events
'Dim Minutes as Word        'Uncomment if optional Minutes Timer0 is used

T0CON  = %00001000        ' 16-bit, 1:1, Prescaler not assigned, Timer OFF
  Clear TMR0IF '= False  'interrupt flag must be cleared before timer is enable
  Timer0 = Timer0Start    '49536 1ms interrupts @ 64Mhz  16Mhz + PLL
  Clear TMR0IF              'clear interrupt flag
  Set TMR0IE              'enable Interrupt - Timer 0 is always HIGH priority
  Set TMR0ON              'Timer 0 = ON

Return




?

tumbleweed

QuoteA way to make your code more readable and useable use an alias for the Timer bytes,

  Symbol Timer3 = TMR3L.Word  'Word var from TMR3H and TMR3L

You can read the Timer as a 16bit value instead of two bytes at a time.  You can print it, use it.

Be careful with that.

When you use a timer in 16-bit mode you must access the H and L bytes in a particular order and the compiler doesn't know that. When you write it must be H then L bytes, and the opposite order for read... L byte then H byte. Currently the compiler produces code that works for writes but it's the wrong order for reads.

This applies to any of the 16-bit timers (T0, T1, T3, etc)


Also, if you have a Systick variable that's larger than a single byte (ie word or dword) then you should protect it from being accessed outside the ISR while interrupts are enabled else you can get an invalid systick time when the variable rolls over.