News:

Let's find out together what makes a PIC Tick!

Main Menu

Using Timer 0 and Timer 1 at the same time

Started by Peter Truman, Jun 05, 2023, 05:34 AM

Previous topic - Next topic

Peter Truman

Hi All

I'm trying to run both timer 0 and Timer 1 at the same time and I'm not having a lot of luck

Pic18F25k22 @ 16MHz
Compiler 3.7.3.6

I have timer 1 running fine with a frequency of 10Hz - quite busy in that process

What I'm trying to achieve is 1ms timebase independent of timer 1

As soon as I setup Timer 0 I get all sorts of weird timing issues with the whole project.

Here is my setup
IPEN=0                                                                          'no need for priority interrupts

'timer 0 setup
Symbol TMR0ON = T0CON.7
Symbol T08BIT = T0CON.6
Symbol T0CS = T0CON.5
Symbol T0SE = T0CON.4
Symbol PSA = T0CON.3
Symbol T0PS2 = T0CON.2
Symbol T0PS1 = T0CON.1
Symbol T0PS0 = T0CON.0


' Timer0 Registers:' 8-Bit Mode; Prescaler=1:64; TMRH Preset=0; TMRL Preset=6; Freq=1,000.00Hz; Period=1,000,000 ns
TMR0ON = 1 ' Timer0 On/Off Control bit:1=Enables Timer0 / 0=Stops Timer0
T08BIT = 1 ' Timer0 8-bit/16-bit Control bit: 1=8-bit timer/counter / 0=16-bit timer/counter
T0CS   = 0 ' TMR0 Clock Source Select bit: 0=Internal Clock (CLKO) / 1=Transition on T0CKI pin
T0SE   = 0 ' TMR0 Source Edge Select bit: 0=low/high / 1=high/low
PSA    = 0 ' Prescaler Assignment bit: 0=Prescaler is assigned; 1=NOT assigned/bypassed
T0PS2  = 1 ' bits 2-0  PS2:PS0: Prescaler Select bits
T0PS1  = 0
T0PS0  = 1

TMR0H = $0     ' preset for Timer0 MSB register
TMR0L = $6     ' preset for Timer0 LSB register

Symbol T0IE        INTCON.5                             ' TMR0 Overflow Interrupt Enable
Symbol TMR0IF      INTCON.2                             'Timer 0 Interrupt flag


Clear TMR0IF 'clear the flag
T0IE=1 'start the clock





T1CON.4=1                                                                       'prescaled
T1CON.5=1
W_toffset=15535                                                                 'empirically tested at bang on 10Hz on the Logic analyser
'SETUP TO INTERRUPT AT ABOUT 10Hz (every 0.100seconds) - pretty accurate

TMR1IF=0                                                                        'clear the timer 1 interrupt flag
TMR1IE=1                                                                        'enable Timer1 as peripheral interrupt source
TMR1ON=1                                                                        'start the clock running

PEIE=1                                                                          'enable peripheral ints
RC1IF = 0                                                                       'clear the flag
RC2IF = 0
RCIE =1                                                                         'enable the USART Rx interrupt
RC2IE =1                                                                        'enable USART 2
PIE1.0=1                                                                        'enable the timer

T1CON.0=1                                                                       'start the timer running

WUE=0                                                                           'diable the auto wake up
GIE=1                                                                           'Global interrupt enable

This is the relevant section of the ISR

'--------------------
'Timer 0
If TMR0IF=1 Then 'Timer 0 interrupt
Clear TMR0IF 'clear the flag for next time
Inc W_Ms_Count
If W_Ms_Count=1000 Then
HRSOut "~",13
Clear W_Ms_Count
EndIf
TMR0H = $0     ' preset for Timer0 MSB register
TMR0L = $6     ' preset for Timer0 LSB register
EndIf
'--------------------
'timer 1
If TMR1IF=1 Then                                                                'this is a timer 1 overflow flag


Set b_100ms_Flg                                                                'set the 10hz flag

'things to do every 100ms
BusIn IO,[B_Dig_In]                                                      'read the inputs
BusOut Display,[B_EX_Port]
If B_Rep_Flag >0 Or B_Sig_Flag>0 Then                                                            'repeater flash
Clear b_Sig_LED
Else
Set b_Sig_LED
EndIf

    If B_Col_Flg_timer>0 Then Dec B_Col_Flg_timer                                'decriment the timer every 100ms
    If b_Col_flg=1 And B_Col_Flg_timer=0 Then Clear b_Col_flg                    'timeout collection after x ms

    If B_Rep_Flag>0 Then Dec B_Rep_Flag                                            'count down the repeater sig flash
    If B_Sig_Flag>0 Then Dec B_Sig_Flag                                            'count down the direct sig flash

'beeps

    Inc B_second                                                                'increment at 100ms

'things to do every second (in isr)
    If B_second>9 Then
        Clear B_second                                                          'clear the counter
Set b_1sec_flag 'flag 1 second (cleared in my_p)
'1 second timebase timers
        If W_Menu_Time>0 Then Dec W_Menu_Time                                      'countdown by seconds
If W_Menu_Time=0 Then Set b_Menu_Timeout_Flag 'flag the timeout
        If B_Timer_Start>0 Then Dec B_Timer_Start                                'dec the timer
        If B_Timer_Stop>0 Then Dec B_Timer_Stop                                  'Stop Mode and running
If W_IS_Time>0 Then Dec W_IS_Time                                          'decriment the irristop timeout
        b_dots=~b_dots

        If b_P_Status=1 Or b_His_Digital=1 Then                                  'if he or me are active - then pass it on
            b_Serial_Outpt=1
        Else
            b_Serial_Outpt=0
        EndIf

        If b_P_Status=0 Then                                                    'i'm not running
            W_Serial_Outpt=W_His_analog                                          'so pass on his output
        Else                                                                    'I am running
            If W_Valid_Rx_An >= W_His_analog And b_His_Digital=1 Then            'I'm > than him (and he's running) - so use his
                W_Serial_Outpt=W_His_analog
  Else                                                                'He's > me so use mine
                W_Serial_Outpt=W_Valid_Rx_An
            EndIf
        EndIf
 
        If b_E_Flag=1 Then                                                        'there is an error
            b_Error_LED=~b_Error_LED                                            'toggle the led
        Else
            Set b_Error_LED                                                      'make the led off
        EndIf

Inc B_Split_Dsply
If B_Split_Dsply>2 Then
Clear B_Split_Dsply
b_split_Display_flag=~b_split_Display_flag 'toggle display every 2 seconds
EndIf
    EndIf

'reset the menu timer every time a button is pressed
If _Menu=0 Or _Up=0 Or _Dn=0 Or _Enter=0 Then
W_Menu_Time=W_Menu_Time_Out 'reset the menu timer
Clear b_Menu_Timeout_Flag 'and clear the flag
EndIf
EndIf
'EndIf
TMR1IF=0                                                                    'clear the flag
TMR1H=W_toffset.HighByte                    '}                                'close to 0.1ms
TMR1L=W_toffset.LowByte

end_int:
T1CON.0=1                                                                       'restart the clock
Context Restore

I did consider running timer 1 at a faster rate (i.e.10 x) to give me a 1000hz timebase - then just have an incrementing variable in there to trigger the 100HZ and 1hz timebases I'm using - but all attempts to do that end in non functioning code (I've spend much of the day trying to figure this out)

Can anyone advise if I have missed something really silly - or provide an example of using two timers at the same time

(I did try Les's example code - but I couldn't make it run, or understand it - there were lots of errors generated that I didn't really understand - not at that level yet)

Thanks for any advice.


Stephen Moss

#1
I think your set up for timer 0 is wrong.
The input is Fosc/4 = 4MHz with a 16MHz primary clock, which, if my math is correct = 250nS per tick, you appear to be dividing that 64 which = 16uS per tick. 8 bits preloaded with 6 = 249 x 16uS = 3.984mS per tick.
So, if I understand your code correctly you appear to be counting 1000 interrupts in order to send data once a second, but with your current settings I calculate to be very 3.984 seconds.

So, for a 1ms tick I think you need to set the prescaler to 4 (1uS per tick), set the size to 16 bit, preloded the timer with 64535, then you will get an interrupt once per millisecond, in reality a fraction longer as 64535 does not take into account the take time to execute your ISR and reset the timer count.

So try that for a start, if that does not solve the timing problems then a better description of the issue may help.

Peter Truman

Hi Stephen

Many thanks - I tried this and it works perfectly. Seems I misunderstood the osc selection. This has kinda stuffed up my timer 1 setup, but no matter - now i have a 1ms timebase I can just build on that.

Thanks again

Stephen Moss

#3
Quote from: Peter Truman on Jun 07, 2023, 02:28 AMThis has kinda stuffed up my timer 1 setup
Not sure why that would be the case but try this for timer 1...
TXCON = $33 (or if that does not work $31)   'Clock source = Fosc/ 4, Prescaller = 8:1 = 1 tick every 2uS. $33 = read timer value as word, $31 = read as two bytes
TXGCON = $00   'No Gating
Pre-load TIMER1 with 15535   'Rolls over after 50000 tick * 2uS = 100mS


See_Mos

QuoteI did consider running timer 1 at a faster rate (i.e.10 x) to give me a 1000hz timebase - then just have an incrementing variable in there to trigger the 100HZ and 1hz timebases I'm using - but all attempts to do that end in non functioning code (I've spend much of the day trying to figure this out)

Is how I would do it but using two variables. One for counting the 1000Hz ticks into 100Hz units and the other counting 100Hz into 1Hz units

Peter Truman

This is my final version of the code - I was (am) a bit uncomfortable using busin and busout in an interrupt - thought it might cause problems. No problems identified so far though.

I'd appreciate any comments re mistakes or improvement if there are any - always willing to learn.

Thanks again for the assistance.

'Timer 0
If TMR0IF=1 Then 'Timer 0 interrupt
    @Clrwdt                                                                                 'Clear the WDT
Clear TMR0IF 'clear the flag for next time
Inc W_MS_4_100                                                                          'Countimng milliseconds
    Inc W_MS_4_1000

    'Things to do every ms
(*  beeps timing =
    long beep = 1000ms
    beep = 100ms
    sbeep = 25ms
    vsbeep = 5ms
    vvsbeep =1 ms
*)

    If W_Beep>0 Then                                                                        'For the beeps
        High _Piezzo                                                                        'piezzo is on while >0
        Dec W_Beep                                                                          'Decriment the count
    Else               
        Low _Piezzo                                                                         'else piezzo is off               
    EndIf 

    TMR0H = $FC     ' preset for Timer0 MSB register
    TMR0L = $18     ' preset for Timer0 LSB register

EndIf

If W_MS_4_100 >99 Then                                                                      '100 ms
    Clear W_MS_4_100                                                                        'clear the count
    Set b_100ms_flag
EndIf

If W_MS_4_1000>999 Then                                                                     '1000 ms
    Clear W_MS_4_1000                                                                       'clear the count
    Set b_1sec_flag                                                                         'set the 1 second flag
EndIf

'things to do every 100ms
If b_100ms_flag=1 Then                                                                      'ticked over 100ms                                                                   
    Clear b_100ms_flag                                                                      'clear the flag
    Set b_INPuts                                                                            'flag the 100ms update to inputs
                   
  BusIn IO,[B_Dig_In]                                                      'read the inputs
BusOut Display,[B_EX_Port]
If B_Rep_Flag >0 Or B_Sig_Flag>0 Then                                                   'repeater flash
Clear b_Sig_LED
Else
Set b_Sig_LED
EndIf

    If B_Col_Flg_timer>0 Then Dec B_Col_Flg_timer                                'decriment the timer every 100ms
    If b_Col_flg=1 And B_Col_Flg_timer=0 Then Clear b_Col_flg                    'timeout collection after x ms

    If B_Rep_Flag>0 Then Dec B_Rep_Flag                                            'count down the repeater sig flash
    If B_Sig_Flag>0 Then Dec B_Sig_Flag                                            'count down the direct sig flash
    If B_Display_Timer>0 Then Dec B_Display_Timer                                           'Display timer for Split systems

EndIf


'things to do every second (in isr)
If b_1sec_flag=1 Then
    Clear b_1sec_flag
    If W_Menu_Time>0 Then Dec W_Menu_Time                                      'countdown by seconds
    If W_Menu_Time=0 Then Set b_Menu_Timeout_Flag 'flag the timeout
    If B_Timer_Start>0 Then Dec B_Timer_Start                                'dec the timer
    If B_Timer_Stop>0 Then Dec B_Timer_Stop                                  'Stop Mode and running
    If W_IS_Time>0 Then Dec W_IS_Time                                          'decriment the irristop timeout
    If B_Display_Timer>0 Then Dec B_Display_Timer                                           'Display timer for Split systems
    b_dots=~b_dots
   
    If b_P_Status=1 Or b_His_Digital=1 Then                                  'if he or me are active - then pass it on
        b_Serial_Outpt=1
    Else
        b_Serial_Outpt=0
    EndIf
   
    If b_P_Status=0 Then                                                    'i'm not running
        W_Serial_Outpt=W_His_analog                                          'so pass on his output
    Else                                                                    'I am running
        If W_Valid_Rx_An >= W_His_analog And b_His_Digital=1 Then            'I'm > than him (and he's running) - so use his
        W_Serial_Outpt=W_His_analog
    Else                                                                'He's > me so use mine
        W_Serial_Outpt=W_Valid_Rx_An
    EndIf
    EndIf
   
    If b_E_Flag=1 Then                                                        'there is an error
    b_Error_LED=~b_Error_LED                                            'toggle the led
    Else
    Set b_Error_LED                                                      'make the led off
    EndIf
   
    Inc B_Split_Dsply
    If B_Split_Dsply>2 Then
    Clear B_Split_Dsply
    b_split_Display_flag=~b_split_Display_flag 'toggle display every 2 seconds
    EndIf

 
EndIf                                                                                           

'reset the menu timer every time a button is pressed
If _Menu=0 Or _Up=0 Or _Dn=0 Or _Enter=0 Then
    W_Menu_Time=W_Menu_Time_Out 'reset the menu timer
    Clear b_Menu_Timeout_Flag 'and clear the flag
EndIf

end_int:
Context Restore

tumbleweed

QuoteI was (am) a bit uncomfortable using busin and busout in an interrupt
I would be too.

If you're going to do that then you need to ensure that the isr doesn't run while using any busin/busout statements in your main code. I2C operations can't be used in both contexts if they can be interrupted.


Peter Truman

Thanks Tumbleweed - now I think about it a bit more, of course I shouldn't use the I2C bus inside an interrupt - I'll just set a flag in the interrupt and run the actual command in the maincode.

Sometimes it's easy to miss the obvious.

Cheers