Menu

Using more than 1 Timer Interrupt

Help
viscomjim
2016-07-14
2016-07-15
  • viscomjim

    viscomjim - 2016-07-14

    I want to use 2 timer interrupts. One of them will be always firing at 50 ms (Timer1) and the other at 1ms (Timer0). The 50ms one should always run and never stop. The 1ms one will be used for other things and will be stopped and started occasionally. How can I use the IntOn and IntOff on just the 1ms timer without effecting the 50ms timer? In other words is there a method or someting like Timer0IntOff and Timer0IntOn to control an individual timer?

     
  • Chris Roper

    Chris Roper - 2016-07-14

    IntOn and IntOff will start and stop all interrupts, they are the Global Interrupt Enable / Disable commands.

    You can enable and disable the interrupts on individual peripherals by setting and clearing their unique interrupt Control Bit.

    You need to refer to the Datasheet for the Register/Bit names but as you chose Timer0 which I happen to know off hand you need the following 2 commands:

    TMR0IE = 1      ' Enable  TMR0 interrupt.
    TMR0IE = 0      ' Disable TMR0 interrupt.
    

    As you mentioned Starting and Stopping Timer0 as well as disabling the interrupt you may also need:

    T0EN = 1      ' Start Timer0.
    T0EN = 0      ' Stop  Timer0.
    

    Cheers
    Chris

     

    Last edit: Chris Roper 2016-07-14
    • Anobium

      Anobium - 2016-07-14

      Great Cow BASIC commands are

      StartTimer x
      StopTimer x

      so, you would use StartTImer 0 and StopTimer 0

      :-)

       
      • Chris Roper

        Chris Roper - 2016-07-14

        You can tell I know PIC architecture better than I do Great Cow Basic at the moment :)

        But that is one of the things that make Great Cow Basic stand out from all other compilers and languages, you can freely mix assembler, direct register access and the Basic language on the fly.

        But don't do it if you want your code to be portable, in that case stick with GCBasic commands alone.

         
  • kent_twt4

    kent_twt4 - 2016-07-14

    Or use a state machine? Set up a single timer for both (or multiple) operations by incrementing a counter in the 1ms interrupt handler. Use Select Case in main to decide what to do at 1ms and the 50 x 1ms interval where the counter is reset and start over again.

     
  • viscomjim

    viscomjim - 2016-07-14

    Excellent, so I probably will never have to stop the timers, I just need to disable the interrupt. Thanks for all you help Chris. Your examples are really helpful!!!

     
  • viscomjim

    viscomjim - 2016-07-14

    Now seeing Evans post, maybe I should stop the timer instead? Just saw your statemachine post. That sounds like the best idea yet. I will start looking into that. I could probably use that method for many different events also, using just one timer instead of multiple timers. Can you give me a quick example of that using the case?

     
  • Anobium

    Anobium - 2016-07-14

    I have a question. What are we, as a collective, building?

    This may help us understand the goal.

     
    • Anobium

      Anobium - 2016-07-15

      Are you building a timemachine? That would be so cool.

       
  • kent_twt4

    kent_twt4 - 2016-07-14

    viscomjim:

    So a 100ms timer1 interrupt example I believe? Well in this case I used if statements...dealers choice :) In one instance a binary pushwheel switch is checked on. And in the other, a DS18S20 temp conversion is started, and then a second later the scratchpad is read (No tying up the controller with a wait!).

    '*******Timer 1 Interrupt**************
    On Interrupt Timer1Overflow Call Event_Schedule
    InitTimer1 Osc, PS1_1/8
    TMR1H = 133     '10MHz clock, PS1_1/8, TMR1H= 133, TMR1L = 237
    TMR1L = 237
    StartTimer 1
    ...
    ...
    ...
    If TenthSec = 5 Then
       'remember to install 10k pull down R's to input pins & zero ohm R's to   the binary switch!!!
       Group = PortD AND b'01110000'
       If  Group = 0 Then
          FourColor = True
          AnalogColor = False
          EncoderColor = False
          DisplayPots = False
          Latch = 0
          pfetsH = 255
          pfetsL = 255
          OutSeg pfetsH
          OutSeg pfetsL
          Latch = 1
       end if
       If Group = 64 Then
          Group = 1
          FourColor = False
          AnalogColor = True
          EncoderColor = False
          TempRead = False
          VoltRead = False
          AmpRead = False
          DisplayPots = True
          Set led1 Off
       end if
       If Group = 32 Then
          Group = 2
          FourColor = False
          AnalogColor = False
          EncoderColor = True
          DisplayPots = False
          Latch = 0
          pfetsH = 255
          pfetsL = 255
          OutSeg pfetsH
          OutSeg pfetsL
          Latch = 1
          'Set led1 On
       end if
    end if
    
    If TenthSec = 10 Then         'tenthsec multiplier or 1 sec intervals
      Call DisplayNum LedTemp
      LED_ON = NOT LED_ON  'toggle led flag
      'Set TMR1ON OFF
      IF LED_ON = True Then
        Set LED1 ON
      'start DS18S20 temp conversion
        MasterRST
        OWout SkipRom
        OWout ConvertT
      Else
        Set LED1 OFF
      'read DS18S20 temp conversion
        MasterRST
        OWout SkipRom
        OWout ReadScratch
        Owin
        LedTemp = HighLow/2
      End If
      TenthSec = 0
    
      fanspeed += 1
    
      TMR1H = 60
      TMR1L = 175
      Set TMR1ON ON
    End if
    ...
    ...
    '*****Timer 1 Overflow Event Scheduler*******
    sub Event_Schedule
      TMR1IF = 0
      TMR1H = 133     '10MHz clock, PS1_1/8, TMR1H= 133, TMR1L = 237
      TMR1L = 237
      TenthSec += 1
    end sub
    
     
  • Chris Roper

    Chris Roper - 2016-07-14

    This is the way I normally handle a state machine, it is very close to the way you are doing it above but makes it easier to read and debug:

    Do
        Select Case State
            Case 1
                DoState1
            Case 2
                DoState2
            Case 3
                DoState3
            Case Else
                DoErrorState
         End Select
     Loop
    
      Sub DoState1
      ' ...
      End Sub
    
      Sub DoState2
      ' ...
      End Sub
    
      Sub DoState3
      ' ...
      End Sub
    
      Sub DoErrorState
      ' ...
      End Sub
    
    END
    

    Actual subroutine names are up to you, they don't have to be DoState1 etc, they could be FlashLED, updateDisplay, etc..

    In the interrupts or whatever triggers you have for changing state you set the State variable to the appropriate State.

    Cheers
    Chris

    p.s. was sure to only use GCBasic commands this time :-)

     

    Last edit: Chris Roper 2016-07-14
  • Chris Roper

    Chris Roper - 2016-07-14

    Task Scheduler

    Whilst we are on the subject of State Machines and Timers you may notice that the above structure could also form the basis of a Multitasking or Time Slicing system. If your timer tick is 1 mS and your Microcontroller is running at 32Mhz it can execute about 8 thousand instructions in that millisecond.

    Consider this structure:

    ''
    '   16f18855 - Task Scheduler.gcb '
    ''
    ' --- Configuration '
      #chip 16f18855,32
      #Config FEXTOSC_OFF, RSTOSC_HFINT32
      #Config WRT_OFF, CPD_ON, MCLRE_OFF
    
     ' TMR0_Initialize() '
        InitTimer0(Osc, (TMR0_FOSC4 + PRE0_32) , POST0_1)
        SetTimer(0, 0xF900) ' Preload Count for 1ms '
        StartTimer 0
      ' Add a handler for the interrupt '
      On Interrupt Timer0Overflow Call TMR0_ISR
    
        #Define TASKS   3
    
    Do
        Select Case  Task
            Case 0 : Task0
            Case 1 : Task1
            Case 2 : Task2
            Case 3 : Task3
            Case Else
                TaskError
         End Select
     Loop
    
      Sub Task0
      ' ... '
      End Sub
    
      Sub Task1
      ' ... '
      End Sub
    
      Sub Task2
      ' ... '
      End Sub
    
      Sub Task3
      ' ... '
      End Sub
    
      Sub TaskError
        END
      End Sub
    
      Sub TMR0_ISR()
        SetTimer(0, 0xFA00) ' reset count for 1ms adj for isr time'
        t0_millis += 1          ' Keep for the Millis() function. '
        if Task++ > TASKS then Task = 0
      End Sub
    
      function millis() as long
        ' disable interrupts while we read t0_millis or we might get an  '
        ' inconsistent value (e.g. in the middle of a write to t0_millis)'
        IntOff
          millis = t0_millis
        IntOn
      end Function
    
    End
    

    Every millisecond a new task will run, to a human it will appear as if they all execute simultaneously because a 1 mS period is far to small for us to notice. Even the best reaction time from the best athlete will be in the region of 100mS. But to the Microcontroller that 1 mS is a lot of time and it can perform a lot of instructions in that time.

    The entire outline above is only 204 instructions, so even if every instruction were to run on every pass through the structure we would still have time to execute 7,750 instructions at least before the task switches again.. Just remember that the task must complete within the mS, this code is not reentrant and it is not preemptive, if the task does not yield the next task will not be able to run.

    To handle delays we can use the Millis() function, described elsewhere, and abort if it is not time to change, so you will be surprised how much can be done in a 1 mS time slice.

    If anything is time critical like the USART it can be interrupt driven, as can input buttons etc. just have the Interrupt Routine set a flag to say the event happened and have the appropriate task handle it when called.

    Cheers
    Chris

     

    Last edit: Chris Roper 2016-07-14

Log in to post a comment.

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.