Question about ISRTimer

General discussion relating to the library modules supplied with the compiler

Moderators: David Barker, Jerry Messina

Post Reply
garryp4
Posts: 126
Joined: Mon May 21, 2007 7:18 am
Location: Loveland, CO USA

Question about ISRTimer

Post by garryp4 » Sat Apr 08, 2023 1:13 am

I am trying to get timer3 to run for 2 seconds. It works the first time after any MCLR but not again. I can not figure out how to reset the time. Here is my code. The LED's are just so I can see whats happening. The comments where I have questions have '****' :

Code: Select all

Device = 18F27J13
Clock  = 8

#option TIMER_TMR_SELECT = 3
#option TIMER_AUTO_RELOAD = true

Config IOL1WAY = OFF

// import modules...
Include "USART.bas"
Include "convert.bas"
Include "system.bas"
Include "setdigitalio.bas"
Include "ISRTimer.bas"
Include "utils.bas"


Private Dim
  red          As PORTA.7,
  green        As PORTA.6,
  e32_aux      As PORTC.0,
  e32_snd      As PORTC.1,
  e32_rcv      As PORTC.2,
  e32_m0       As PORTB.0,
  e32_m1       As PORTB.1,
  pwr          As PORTB.4

'****************************************************************************

OSCCON  = $7F                          ' Interanl clock to 8mhz
OSCCON2 = $54
REFOCON = $00
OSCTUNE = $00                           
ADCON0  = $00                          ' A/D disabled
ADCON1  = $00                          ' A/D disabled
ANCON0  = $FF                          '
ANCON1  = $0F
REFOCON = $00

DSCONH   = $00
DSCONL   = $05
ODCON1   = $00
ODCON2   = $00
CCP4CON  = $00                         ' 
CCP5CON  = $00
CCP6CON  = $00
CCP7CON  = $00
CCP8CON  = $00
CCP9CON  = $00
CCP10CON = $00

PMDIS3  = $FF                          ' Shut down peripherals
PMDIS2  = $FC                          ' Enable CCP4
PMDIS1  = $E0                          ' Enable Timer 4, 3, 2, 1
PMDIS0  = $C4                          ' Enable A/D, SPI1, UARTS  

SetAllDigital()
SetAnalogPort(AN0, ANA)                ' Set AN0 (RA0) to analog mode
SetAnalogPort(AN1, ANA)                ' Set AN1 (RA1) to analog mode

Low(red)
Low(green)
Input(e32_aux)
High(e32_m1)
High(e32_m0)
Low(pwr)

// activate the timer module...
Timer.Initialize(3)                   ' If this is not (3), it doesnot work the first time *****
Timer.Items(0).Interval = 1000        ' If this is (3), it doesnot work the first time  *****
Timer.Items(0).Enabled = true         ' If this is (3), it doesnot work the first time  *****
                                      ' I don't understand what the .items(x) means  ****

'****************************************************************************
MAIN:

  High(red)                            ' Make LED's blink so I can see program progression  ****
  DelayMS(100)
  Low(red)

  High(green)
  DelayMS(20)
  Timer.Start
  While Timer.Items(0).Enabled         ' This only delays once  ****
  Wend
  Low(green)
  DelayMS(500)
  GoTo main

Jerry Messina
Swordfish Developer
Posts: 1473
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Re: Question about ISRTimer

Post by Jerry Messina » Sat Apr 08, 2023 12:31 pm

Ok, let's look at the whole 'Timer.Items(x)' part first...

Each 'timer' (ie 'timer item') gets a data structure allocated to it that contains all the details for that timer

Code: Select all

// a timer item structure...
structure TTimerItem
   Enabled as boolean
   Interval as TInterval
   OnTimer as TEvent
#if TIMER_AUTO_RELOAD = true
   ReloadInterval as TInterval
#endif
end structure
These structures are kept in an array named 'FTimers', which, since it's not declared public is private to the Timer module...

Code: Select all

dim 
   FTimers(MaxNumberOfTimers) as TTimerItem,
Arrays are 0-based, so when you create 3 timers using 'Timer.Initialize(3)' you get three data structures, FTimers(0) through FTimers(2)
Per above, 'FTimers' is local to the Timer module, so no one outside the module can see it.

So, how do you access the timer data structures?

There's a public alias to the FTimers array called 'Items' created by

Code: Select all

// public variables...
public dim 
   Items as FTimers,   // access timer event list
So, when you say 'Timer.Items(1)' what you're really saying is 'Timer module.FTimers(1)'... they're the same thing.

This style of declaration is done in a number of modules... you declare the data for the module as private and then expose what you want others to have access to using a public alias. It can be confusing at first, but you get used to it. The array of structures could have been declared 'public' to start with, but that sort of violates the spirit of structured programming.

Now, on to how the timer mechanism works...
The Timer module has two modes: one-shot or auto-reload mode.
When a timer is started/running, the TimerItem.Enabled flag is set true, which is then set false when the timer period expires.

In either mode, you can assign an event() routine that will automatically be called when the timer times out, or you can manually poll the timer using the Timer.Items().Enabled flag in your code

In one-shot mode you'd then have to set the interval again and re-enable the timer to get it running.
Auto-reload mode sets the interval for you, but what happens when it times out depends on if you're using events or not. With no event assigned to the timer, when it times out it assumes that you must be polling the timer in your code and leaves the 'Enabled' flag false. If you do have an event assigned then the event will get called and the timer remains enabled, running continuously.

Looking at your code, you have this (I removed the extra stuff for now):

Code: Select all

#option TIMER_TMR_SELECT = 3		// use hardware TMR3 (the module default is to use TMR1)
#option TIMER_AUTO_RELOAD = true	// enable auto-reload mode (this is the default option in ISRTimer.bas)

Timer.Initialize(3)                   // create 3 timers... Timer.Items(0) through Timer.Items(2)
Timer.Items(0).Interval = 1000        // set the interval for the first timer
Timer.Items(0).Enabled = true         // and enable it
// the other two timers, Timer.Items(1) and Timer.Items(2), remain unused at this point

MAIN:
  // enable the OnTimer ISR and enable interrupts
  // this starts the whole thing rolling and is normally done once outside the main loop
  Timer.Start
  
  // wait for the first timer to timeout (polled mode)
  While Timer.Items(0).Enabled
  Wend
  
  // at this point (after 1000ms) the first timer has expired
  // even though '#option TIMER_AUTO_RELOAD = true', since there's no event assigned to the first timer the Enabled flag remains false
  // to get it started again, 'Enabled' must be set true
  GoTo main
Here's what I think you're looking for in the main loop:

Code: Select all

  // enable the OnTimer ISR and enable interrupts
Timer.Start

MAIN:
  // wait for the first timer to timeout (polled mode)
  While Timer.Items(0).Enabled
  Wend

  // at this point (after 1000ms) the first timer has expired
  // do stuff
  
  // start the first timer again for another 1000ms timeout
  Timer.Items(0).Enabled = true
  
  GoTo main
Instead of polling the timer, if you assign an event() routine to Timer.Items(0).OnTimer then the event will automatically be called every Timer.Items(0).Interval for you without doing anything else, and it would do this without regard for all the 'delayms()' calls in the main loop.

garryp4
Posts: 126
Joined: Mon May 21, 2007 7:18 am
Location: Loveland, CO USA

Re: Question about ISRTimer

Post by garryp4 » Sat Apr 08, 2023 9:32 pm

Thanks for the quick reply. I was missing 'Timer.Items(0).Enabled = true'. That did fix it. Now I am wondering if what my actual goal is possible with the timers. I built a circuit that uses IR to look for rain/snow. It puts out a pulse of varying length depending on the size of the droplet/flake. I'm using Timer3 gate to measure look for and measure a pulse. If there is a pulse, then I want to see how may there are in 2 seconds. I picked a count of up to 30 in the 2 seconds but not sure what it needs to be as I can not get Timer1 to count the 2 seconds and Timer3 Gate to work at the same time. I can get either to work just fine.

Here is my code.

Code: Select all

{
*****************************************************************************
*  Name    : Snow Sensor.BAS                                                *
*  Author  : [Garry Preston]                                                *
*  Notice  : Copyright (c) 2011 [ ]                                         *
*          : All Rights Reserved                                            *
*  Date    :                                                                *
*  Version : 1.0                                                            *
*  Notes   :                                                                *
*****************************************************************************

}
Device = 18F27J13
Clock  = 8

#option USART2_TX = PORTC.1            ' 
#option USART2_RX = PORTC.2            '
#option TIMER_TMR_SELECT = 1
#option TIMER_AUTO_RELOAD = true

Config IOL1WAY = OFF

// import modules...
Include "USART.bas"
Include "USART2.bas"
Include "SPI.bas"
Include "convert.bas"
Include "system.bas"
Include "PPS.bas"
Include "setdigitalio.bas"
Include "ISRTimer.bas"
Include "utils.bas"
Include "DS1394.bas"
Include "SETUP.bas"
Include "MEM_64K.BAS"
Include "ANALOG.bas"
Include "THERM_LUT.bas"
Include "SX1278.bas"
'Include "RX_2.bas"

Private Dim
  red          As PORTA.7,
  green        As PORTA.6,
  e32_aux      As PORTC.0,
  e32_snd      As PORTC.1,
  e32_rcv      As PORTC.2,
  e32_m0       As PORTB.0,
  e32_m1       As PORTB.1,
  pwr          As PORTB.4,
  comp         As PORTB.5
  

Private Dim
  w1           As Word,
  w2           As Word,
  lp1          As Byte,
  pulse_w(30)  As Word,
  got_pulse    As Boolean,
  t3_gir       As PIE3.booleans(1),     ' T1 Gate Interupt enable = 1  
  t3_gmode     As T3GCON.Booleans(4),   ' T1 Single Pulse Mode Enable = 1, toggle to clear int
  t3_go        As T3GCON.Booleans(3)    ' T1 Go Enable = 1  
'****************************************************************************
Private Sub ERASE_PULSES()
  For lp1 = 0 To 29
    pulse_w(lp1) = 0
  Next
End Sub
'****************************************************************************  
Private Function CAPTURE() As Word

  TMR3H = 0
  TMR3L = 0
  t3_gmode = true
  t3_go = true
  While t3_go = true
  Wend
  result.byte0 = TMR3L
  result.byte1 = TMR3H
  t3_gmode = false
'  USART.write("COUNTS = ",DecToStr(result),10,13)
'  DelayMS(3)

End Function
'****************************************************************************
'****************************************************************************
'****************************************************************************

OSCCON  = $7F                          ' Interanl clock to 8mhz
OSCCON2 = $54
REFOCON = $00
OSCTUNE = $00                           
ADCON0  = $00                          ' A/D disabled
ADCON1  = $00                          ' A/D disabled
ANCON0  = $FF                          '
ANCON1  = $0F
REFOCON = $00

DSCONH   = $00
DSCONL   = $05
ODCON1   = $00
ODCON2   = $00
CCP4CON  = $00                         ' 
CCP5CON  = $00
CCP6CON  = $00
CCP7CON  = $00
CCP8CON  = $00
CCP9CON  = $00
CCP10CON = $00

PMDIS3  = $FF                          ' Shut down peripherals
PMDIS2  = $FF                          ' 
PMDIS1  = $E0                          ' Enable Timer 4, 3, 2, 1
PMDIS0  = $C4                          ' Enable A/D, SPI1, UARTS

' Timer3 gate stuff
Input(comp)                            ' Timer1 Gate input B.5
INTCON  = $C0                          ' Enable interupts *** Don't think I need this for the T3Gate if I am polling it ****
t3_gir  = true                         ' Enable Timer1 Gate Mode Interupt
T3GCON  = $C0                          ' Timer1 gate, active high, no toggle, signle pulse, Timer1 gate pin
T3CON   = $37                          ' Timer 1 Fosc4, 1:8, 16 bit read, on
pps.unlock()
pps.assign_input(PPS_T3G, PPS_IN_RP8)   ' RB.5
pps.unlock()

SetAllDigital()
SetAnalogPort(AN0, ANA)                ' Set AN0 (RA0) to analog mode
SetAnalogPort(AN1, ANA)                ' Set AN1 (RA1) to analog mode

Low(red)
Low(green)
Input(e32_aux)                         ' Sets the Radio status
High(e32_m1)
High(e32_m0)
Low(pwr)                               ' Holds the circuit on
got_pulse = false

USART.SetBaudrate(br9600)              ' Baud rate
USART.ClearOverrun
USART.Write("I'M ALIVE!          ",13,10)
DelayMS(5)

// activate the timer module...
Timer.Initialize(0)
Timer.Items(0).Interval = 2000        ' 2000ms
Timer.Items(0).Enabled = true

'****************************************************************************
MAIN:

  ' This section looks for a single pulse. Is it raining/snowing?
  got_pulse = false                    ' Reset the flag
  ERASE_PULSES                         ' Clear the data incase the timer1 expires before 30 pulses are counted
  Timer.Start                          ' Start Timer1
  High(red)                            ' Let me see that the code executes to this point
  While Timer.Items(0).Enabled         ' Watch for Timer1 to be done
    If CAPTURE > 0 Then                ' Did T3 Gate see a pulse?
      got_pulse = true                 ' Set the flag for later
      Timer.Stop
      Break
    EndIf  
  Wend
  Low(red)

  if got_pulse = true then
    Timer.Items(0).Enabled = true      ' Let Timer1 run again
    Timer.Start                        ' And start it
    While Timer.Items(0).Enabled       ' Look for pulses while Timer1 runs
      For lp1 = 0 To 29                ' And see if there are up to 30 pulses before Timer1 expires
        pulse_w(lp1) = CAPTURE         ' T3 Gate data
      Next
    Wend
    
    For lp1 = 0 To 29                  ' Print the data
      If pulse_w(lp1) > 0 Then
        USART.write("COUNTS = ",DecToStr(w1),10,13)
        DelayMS(3)
      EndIf
    Next
  endif  
  
  goto MAIN        

Jerry Messina
Swordfish Developer
Posts: 1473
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Re: Question about ISRTimer

Post by Jerry Messina » Sun Apr 09, 2023 1:33 pm

This part here is going to cause problems:

Code: Select all

INTCON  = $C0                          ' Enable interupts *** Don't think I need this for the T3Gate if I am polling it ****
T3_GIR  = true                         ' Enable Timer1 Gate Mode Interupt
I wouldn't recommend directly writing to the INTCON register (that's what the enable()/disable() statements are for), but if you do then
a) don't write the whole register, just INTCON.7 (GIE) and INTCON.6 (GIEL)
b) don't do it here, do it after everything is setup
c) don't enable a peripheral IE enable flag without an ISR to handle it (T3_GIR = true)
d) highly recommend using 'enable()' and 'disable()' instead

Most modules that use interrupts already have code to enable/disable the interrupts. For example, in ISRTimer.bas that's done by Timer.Start()

The peripheral IE flag (PIEx register) doesn't need to be set in order to poll the peripheral's IF flag, the IF flag operates independantly. Matter of fact, that's how you poll a peripheral (keep PIE=0, check PIR for 1).

From what I can see, the CAPTURE routine sets up the TMR3 gate to look for a single pulse, triggered on the rising edge, and returns the TMR3 count value (the pulse width). It'll wait forever until it gets a pulse, so code like this isn't going to work...

Code: Select all

  While Timer.Items(0).Enabled         ' Watch for Timer1 to be done
    If CAPTURE > 0 Then                ' Did T3 Gate see a pulse?
      got_pulse = true                 ' Set the flag for later
      Timer.Stop
      Break
    EndIf  
  Wend
As long as the gate pulse width is greater than the TMR3 count rate, CAPTURE should always return > 0, but it won't return until it sees a pulse.

Likewise, something like this will miss any pulses that occur while CAPTURE() is not actively executing

Code: Select all

    While Timer.Items(0).Enabled       ' Look for pulses while Timer1 runs
      For lp1 = 0 To 29                ' And see if there are up to 30 pulses before Timer1 expires
        pulse_w(lp1) = CAPTURE         ' T3 Gate data
      Next
    Wend
That'll hang until you get all thirty pulses and may miss some (if that's at all important).

Keep in mind that while all this is happening, the Timer module TMR1 interrupt will occur every 1ms to check for timeouts, so that may cause you to miss pulses too.

Jerry Messina
Swordfish Developer
Posts: 1473
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Re: Question about ISRTimer

Post by Jerry Messina » Mon Apr 10, 2023 1:21 pm

You might give this a try... I took what you had and added a timeout to the capture() routine.
There are some other misc changes and corrections, just look for the 'jm' comments
I changed a few variable names just so I could keep track of what was going on (mostly register bit names).

Code: Select all

{
*****************************************************************************
*  Name    : Snow Sensor.BAS                                                *
*  Author  : [Garry Preston]                                                *
*  Notice  : Copyright (c) 2011 [ ]                                         *
*          : All Rights Reserved                                            *
*  Date    :                                                                *
*  Version : 1.0                                                            *
*  Notes   :                                                                *
*****************************************************************************
Now I am wondering if what my actual goal is possible with the timers. 
I built a circuit that uses IR to look for rain/snow. 
It puts out a pulse of varying length depending on the size of the droplet/flake. 
I'm using Timer3 gate to look for and measure a pulse. 
If there is a pulse, then I want to see how may there are in 2 seconds. 
I picked a count of up to 30 in the 2 seconds but not sure what it needs to be 
as I can not get Timer1 to count the 2 seconds and Timer3 Gate to work at the same time. 
I can get either to work just fine. 

V1.1 - modified to add timeouts and misc changes. look for 'jm' comments
}
Device = 18F27J13
Clock  = 8

// USART2 options
#option USART2_TX = PORTC.1
#option USART2_RX = PORTC.2
// ISRTimer options
#option TIMER_TMR_SELECT = 1
#option TIMER_AUTO_RELOAD = true
//setdigitalio options
#option DIGITALIO_INIT = true   // jm - automatically call SetAllDigital at startup

// config settings
Config IOL1WAY = OFF

// import modules...
include "intosc.bas"        // jm - added to manage int osc 
Include "setdigitalio.bas"  // jm - moved so init occurs close to startup

Include "USART.bas"
Include "USART2.bas"
Include "SPI.bas"
Include "convert.bas"
Include "system.bas"
Include "PPS.bas"
Include "ISRTimer.bas"
Include "utils.bas"
// jm - commented out to compile
{
Include "DS1394.bas"
Include "SETUP.bas"
Include "MEM_64K.BAS"
Include "ANALOG.bas"
Include "THERM_LUT.bas"
Include "SX1278.bas"
'Include "RX_2.bas"
}

Private Dim
  red          As PORTA.7,
  green        As PORTA.6,
  e32_aux      As PORTC.0,
  e32_snd      As PORTC.1,
  e32_rcv      As PORTC.2,
  e32_m0       As PORTB.0,
  e32_m1       As PORTB.1,
  pwr          As PORTB.4,
  comp         As PORTB.5
  

Private Dim
  lp1          As Byte,
  pulse_w(30)  As Word,
  got_pulse    As Boolean,
  T3GCON_TxGSPM     As T3GCON.Booleans(4),   ' T1 Single Pulse Mode Enable = 1, toggle to clear int
  T3GCON_TxGGO      As T3GCON.Booleans(3)    ' T1 Go Enable = 1  

'****************************************************************************
Private Sub ERASE_PULSES()
// jm - replace loop with 'clear' (faster + less code)
{
  For lp1 = 0 To 29
    pulse_w(lp1) = 0
  Next
}
    clear(pulse_w)
End Sub

'****************************************************************************  
// jm - added these to control the Timer.Items(0) 2sec timer 
// assumes #option TIMER_AUTO_RELOAD = true
sub timer_2s_start()
    Timer.Items(0).Enabled = false      ' disable while init
    
    Timer.Items(0).Interval = 2000      ' 2000ms
    Timer.Items(0).ReloadInterval = 0   ' force reload at initial run

    Timer.Items(0).Enabled = true
end sub

inline sub timer_2s_stop()
    Timer.Items(0).Enabled = false      ' disable 2 sec timer
end sub

// return 2 sec timer enabled flag... true=is running, false=stopped/timeout
inline function timer_2s_enabled() as boolean
    result = Timer.Items(0).Enabled
end function

'****************************************************************************  
Private Function CAPTURE() As Word
  TMR3H = 0
  TMR3L = 0
  T3GCON_TxGSPM = true      // T3GCON.4 - set single pulse mode
  T3GCON_TxGGO = true       // T3GCON.3
  While T3GCON_TxGGO = true // T3GCON.3 - TxGGO go_done cleared on the next trailing edge of the pulse
    // jm - added timeout
    if (timer_2s_enabled() = false) then  // two second timer has elapsed... quit
        break    
    endif
  Wend
  result.byte0 = TMR3L
  result.byte1 = TMR3H
  T3GCON_TxGSPM = false     // T3GCON.4 - this also clears the TxGGO go_done bit
'  USART.write("COUNTS = ",DecToStr(result),10,13)
'  DelayMS(3)
End Function

'****************************************************************************
'****************************************************************************
'****************************************************************************

// jm - clock managed by intosc.bas
{
OSCCON  = $7F                          ' Interanl clock to 8mhz
OSCCON2 = $54
OSCTUNE = $00                           
}

REFOCON = $00
ADCON0  = $00                          ' A/D disabled
ADCON1  = $00                          ' A/D disabled
'ANCON0  = $FF      // jm - handled by 'setalldigital'                    '
'ANCON1  = $0F
'REFOCON = $00      // jm - redundant

DSCONH   = $00
DSCONL   = $05
ODCON1   = $00
ODCON2   = $00
CCP4CON  = $00                         ' 
CCP5CON  = $00
CCP6CON  = $00
CCP7CON  = $00
CCP8CON  = $00
CCP9CON  = $00
CCP10CON = $00

PMDIS3  = $FF                          ' Shut down peripherals
PMDIS2  = $FF                          ' 
PMDIS1  = $E0                          ' Enable Timer 4, 3, 2, 1
PMDIS0  = $C4                          ' Enable A/D, SPI1, UARTS

' Timer3 gate stuff
Input(comp)                            ' Timer1 Gate input B.5

// jm - remove accessing intr regs
{
INTCON  = $C0                          ' Enable interupts *** Don't think I need this for the T3Gate if I am polling it ****
T3_GIR  = true                         ' Enable Timer1 Gate Mode Interupt
}

T3GCON  = $C0                          ' Timer1 gate, active high, no toggle, signle pulse, Timer1 gate pin
' 1100 0000
'   TMR3GE = 1  count controlled by TMR3 GATE
'   T3GPOL = 1  active-high
'   T3GTM = 0   gate toggle mode - gate FF toggles on every rising edge
'   T3GSPM = 0  single pulse mode disabled
'   T3GGO = 0   go/done_n
'   T3GVAL      read-only
'   T3GSS1 = 0  gate source = T3G input pin
'   T3GSS0 = 0
T3CON   = $37                          ' Timer 1 Fosc4, 1:8, 16 bit read, on
' 0011 0111
'   TMR3CS = 00 instruction clock FOSC/4
'   TMR3PS = 11 prescaler 1:8
'   T3OSCEN = 0
'   T3SYNCn = 1
'   RD16 = 1
'   TMR3ON = 1

pps.unlock()
pps.assign_input(PPS_T3G, PPS_IN_RP8)   ' RP8 = RB.5
// jm - lock??
'pps.unlock()

// jm - setalldigital now called automatically at startup
'SetAllDigital()
SetAnalogPort(AN0, ANA)                ' Set AN0 (RA0) to analog mode
SetAnalogPort(AN1, ANA)                ' Set AN1 (RA1) to analog mode

Low(red)
Low(green)
Input(e32_aux)                         ' Sets the Radio status
High(e32_m1)
High(e32_m0)
Low(pwr)                               ' Holds the circuit on
got_pulse = false

USART.SetBaudrate(br9600)              ' Baud rate
USART.ClearOverrun
USART.Write("I'M ALIVE!          ",13,10)
DelayMS(5)

// activate the timer module...
// jm - correct this to add one timer, Items(0)
// timer.items(0) is used in polled mode (no event assigned), auto-reload
Timer.Initialize(1)
timer_2s_start()
// start timers running (enables intr)
Timer.Start

'****************************************************************************
MAIN:

  ' This section looks for a single pulse. Is it raining/snowing?
  ERASE_PULSES()                       ' Clear the data incase the timer1 expires before 30 pulses are counted
  got_pulse = false                    ' Reset the flag

  timer_2s_start()                  // start 2 sec timer                     
  High(red)                            ' Let me see that the code executes to this point
  While timer_2s_enabled()             ' Watch for Timer1 to be done
    If CAPTURE() > 0 Then                ' Did T3 Gate see a pulse?
      got_pulse = true                 ' Set the flag for later
      Break
    EndIf  
  Wend
  timer_2s_stop()                   // stop 2 sec timer (if it's still running)
  Low(red)

  if got_pulse = true then
    timer_2s_start()                // start 2 sec timer                     
    While timer_2s_enabled()           ' Look for pulses while Timer1 runs
      For lp1 = 0 To bound(pulse_w)    ' And see if there are up to 30 pulses before Timer1 expires
        pulse_w(lp1) = CAPTURE()       ' T3 Gate data
        // jm - added timeout
        if (timer_2s_enabled() = false) then  // two second timer has elapsed... quit
            break    
        endif
      Next
    Wend
    
    For lp1 = 0 To bound(pulse_w)       ' Print the data
      If pulse_w(lp1) > 0 Then
        USART.write("COUNTS = ",DecToStr(pulse_w(lp1)),10,13)
        DelayMS(3)
      EndIf
    Next
  endif  
  
  goto MAIN 
  
  end

garryp4
Posts: 126
Joined: Mon May 21, 2007 7:18 am
Location: Loveland, CO USA

Re: Question about ISRTimer

Post by garryp4 » Mon Apr 10, 2023 8:45 pm

Jerry: Thanks so much for you help. I did not know about the 'clear' instruction. After a bit of contemplating, I realized I needed a 'While (t3_go) and (Timer.Items(0).Enabled)' statement for reading the gate. This also eliminated the for/to/next loop in the CAPTURE routine. I didn't see your reply till after I had figured this out. Here is what now works just fine:

Code: Select all

Device = 18F27J13
Clock  = 8

#option TIMER_TMR_SELECT = 1
#option TIMER_AUTO_RELOAD = true

Config IOL1WAY = OFF

// import modules...
Include "USART.bas"
Include "convert.bas"
Include "system.bas"
Include "PPS.bas"
Include "setdigitalio.bas"
Include "ISRTimer.bas"
Include "utils.bas"

Private Dim
  red          As PORTA.7,
  green        As PORTA.6,
  e32_aux      As PORTC.0,
  e32_snd      As PORTC.1,
  e32_rcv      As PORTC.2,
  e32_m0       As PORTB.0,
  e32_m1       As PORTB.1,
  pwr          As PORTB.4,
  comp         As PORTB.5
  

Private Dim
  lp1          As Byte,
  pulse_count  As Byte,
  pulse_w(60)  As Word,
  t3_gmode     As T3GCON.Booleans(4),   ' T1 Single Pulse Mode Enable = 1, toggle to clear int
  t3_go        As T3GCON.Booleans(3)    ' T1 Go Enable = 1  

'****************************************************************************
Private Sub ERASE_PULSES()
  For lp1 = 0 To 59
    clear(pulse_w)
  Next
End Sub
'****************************************************************************  
Private Sub CAPTURE()
  ERASE_PULSES
  pulse_count = 0 
  Timer.Start
  High(red)
  While Timer.Items(0).Enabled
    t3_gmode = true                    ' Clear done bit
    TMR3H = 0                          ' Zero the count register
    TMR3L = 0
    t3_go = true                       ' Start the gate counter
    While (t3_go) and (Timer.Items(0).Enabled) ' Wait
    Wend
    pulse_w(pulse_count).byte0 = TMR3L ' Get the data
    pulse_w(pulse_count).byte1 = TMR3H
    t3_gmode = false                   ' Get ready for another
    inc(pulse_count)                   ' Increment the loop counter
'    USART.write("COUNTS = ",DecToStr(w1),10,13)
'    DelayMS(3)
  Wend
  Low(red)  
  Timer.Items(0).Enabled = true
  High(green)                          ' Just to show Timer1 has expired
  DelayMS(500)
  Low(green)
End Sub
'****************************************************************************
'****************************************************************************
'****************************************************************************

OSCCON  = $7F                          ' Interanl clock to 8mhz
OSCCON2 = $54
REFOCON = $00
OSCTUNE = $00                           
ADCON0  = $00                          ' A/D disabled
ADCON1  = $00                          ' A/D disabled
ANCON0  = $FF                          '
ANCON1  = $0F
REFOCON = $00

DSCONH   = $00
DSCONL   = $05
ODCON1   = $00
ODCON2   = $00
CCP4CON  = $00                         ' 
CCP5CON  = $00
CCP6CON  = $00
CCP7CON  = $00
CCP8CON  = $00
CCP9CON  = $00
CCP10CON = $00

PMDIS3  = $FF                          ' Shut down peripherals
PMDIS2  = $FC                          ' Enable CCP4
PMDIS1  = $E0                          ' Enable Timer 4, 3, 2, 1
PMDIS0  = $C4                          ' Enable A/D, SPI1, UARTS

' Timer3 gate stuff
Input(comp)                            ' Timer3 Gate input
T3GCON  = $C0                          ' Timer1 gate, active high, no toggle, signle pulse, Timer1 gate pin
T3CON   = $37                          ' Timer 1 Fosc4, 1:8, 16 bit read, on
pps.unlock()
pps.assign_input(PPS_T3G, PPS_IN_RP8)   ' RB.5
pps.lock()

SetAllDigital()
SetAnalogPort(AN0, ANA)                ' Set AN0 (RA0) to analog mode
SetAnalogPort(AN1, ANA)                ' Set AN1 (RA1) to analog mode

Low(red)
Low(green)
Input(e32_aux)
High(e32_m1)
High(e32_m0)
Low(pwr)

USART.SetBaudrate(br9600)              ' Baud rate
USART.ClearOverrun
USART.Write("I'M ALIVE!          ",13,10)
DelayMS(5)

// activate the timer module...
Timer.Initialize(1)
Timer.Items(0).Interval = 2000         ' 2000ms
Timer.Items(0).Enabled = true

'****************************************************************************
MAIN:

  CAPTURE

  If pulse_count > 1 Then
    For lp1 = 0 To 59
      if pulse_w(lp1) <> 0 then         ' Stop printing when data ends
        USART.write(DecToStr(lp1)," - COUNTS = ",DecToStr(pulse_w(lp1)),10,13)
        DelayMS(3)
       else
        break
      endif   
    Next
   Else
    USART.write("NO DATA",10,13)
    DelayMS(3)
  EndIf  
  usart.write(10,13)
  DelayMS(500)

  goto Main
Now I need to figure out how long the pulse is from the Gate data. That will probably require breaking out the scope and look at pulse width vs counts. I don't know how may instruction cycles each Gate loop takes.

Post Reply