ME Labs, Inc.
719-520-5323
 
Home:
  Developer Resources:

Programming Clues
    Sample Programs
   
    PICBASIC PRO™
Compiler Manual
    PICBASIC™ Compiler
Manual
    Serin2/Serout2 Modes
    ASCII Character Set
    Number Conversion
    Floating Point
Routines
    PBP Debug Monitor
    Articles and Tutorials

Hardware Clues
    Parts / Vendor List
    PICPROTO™ Boards
    LAB-X1 Docs
    LAB-X2 Docs
    LAB-X20 Docs
    LAB-X3 Docs
    LAB-X4 Docs
    LAB-XUSB Docs
    LAB-XT Docs
     
 

ME Labs, Inc. | 1-719-520-5323 | Example Program - CNTDWN_1934X.pbp

PICBASIC PRO program to demonstrate using Timer1 assembly interrupts for dual countdown timers in the background. Interrupt periods are approximately 100mS intervals with a 4MHz oscillator. Controlled with SW13-SW16.
' Name        : CNTDWN_1934X.pbp
' Compiler    : PICBASIC PRO Compiler 2.6
' Assembler   : MPASM
' Target PIC  : 40-pin 16F1934 or similar
' Hardware    : LAB-X1 Experimenter Board
' Oscillator  : 4MHz external crystal
' Keywords    : IF THEN ELSE, FOR NEXT, LCDOUT, WHILE WEND
' Description : PICBASIC PRO program to demonstrate using Timer1 assembly
' interrupts for dual countdown timers in the background. Interrupt periods
' are approximately 100mS intervals with a 4MHz oscillator. Controlled with
' SW13-SW16.
'

' Define LCD pins
Define LCD_DREG  PORTD
Define LCD_DBIT  4
Define LCD_RSREG PORTE
Define LCD_RSBIT 0
Define LCD_EREG  PORTE
Define LCD_EBIT  1

Asm
#Define Alarm PORTD ; define port for outputs in interrupt handler
#Define Out1 0      ; output1 pin on RD0
#Define Out2 1      ; output2 pin on RD1
Endasm

Define INTHAND TmrInt ' point to assembler interrupt handler
        
' setup vars
Tick1 Var Byte Bank0 System   ' Tick counter for alarm1. 255 * 100mS MAX
Time1 Var Byte Bank0 System   ' # of 100mS ticks to compare with Tick1
Tick2 Var Word Bank0 System   ' Tick counter for alarm2. 65,535 * 100mS MAX
Time2 Var Word Bank0 System   ' # of 100mS ticks to compare with Tick2
 
TmrFlag Var Byte Bank0 System ' last 2-bits indicate timer enabled or expired
State   Var Bit  Bank0 System ' state var to indicate LCD needs updating.

IocFlag Var INTCON.0    ' portb int-on-change flag bit

TmrFlag = %00000011     ' 1 = enabled. 0 = expired. start enabled.
State = 1

  ' set output "on" times below by adjusting values in Time1 & Time2
   Time1 = 100   ' 100 * 100mS = 10 seconds (255 MAX)
   Time2 = 600   ' 600 * 100mS = 60 seconds (65535 MAX)
   
   ' clear interrupt tick counters
   Tick1 = 0     ' clear Tick1 count
   Tick2 = 0     ' clear Tick2 count

   Goto Init     ' jump to hardware initialization routine

' ===========\ Assembly Interrupt Handler /=========== '   
Asm
TmrInt                 ; registers in bank0 so no switching necessary 
   bcf    PIR1,TMR1IF  ; clear Timer1 interrupt flag bit
   btfss  TmrFlag,0    ; has this timer expired?
   bra    DoTick2      ; yes, jump to Tick2
   incf   Tick1,F      ; no, increment Tick1 count             
   movf   Time1,W      ; get Time1                  
   subwf  Tick1,W           
   btfsc  STATUS,2     ; is Tick1 = Time1?      
   bra    Alarm1       ; yes, do alarm1
   bra    DoTick2      ; nope, not time, do Tick2 count
   
Alarm1
   bcf    Alarm,Out1   ; clear PORTD,0. turn off alarm1
   clrf   Tick1        ; clear Tick1 count, we're done
   bsf    State        ; indicate State change
   bcf    TmrFlag,0    ; expire Tick1. user has to re-arm by setting TmrFlag.0
   
DoTick2
   btfss  TmrFlag,1    ; has this timer expired?
   bra    ExitISR      ; yes, so exit
   incf   Tick2,F      ; no, increment Tick2 low byte count
   btfsc  STATUS,2     ; low byte over-flow?
   incf   Tick2+1,F    ; yes, increment Tick2 high byte
   movf   Time2+1,W    ; get Time2 high byte
   subwf  Tick2+1,W    
   btfsc  STATUS,2     ; are Time2 & Tick2 high bytes the same?
   bra    DoLow        ; yes, check low byte
   bra    ExitISR      ; no, exit
   
DoLow
   movf   Time2,W      ; get Time2 low byte
   subwf  Tick2,W
   btfsc  STATUS,2     ; are Time2 & Tick2 low bytes the same?
   bra    Alarm2       ; yes, do alarm2
   bra    ExitISR      ; nope, exit

KillTmr1
   bcf    T1CON,TMR1ON ; stop Timer1
   clrf   TMR1H        ; clear high/low counts
   clrf   TMR1L
   retfie              ; return with auto context restore
      
Alarm2
   bcf    Alarm,Out2   ; clear PORTD,1. turn off alarm2
   clrf   Tick2        ; clear Tick2 low
   clrf   Tick2+1      ; clear Tick2 high, we're done
   bsf    State        ; indicate State change
   bcf    TmrFlag,1    ; expire Tick2. user has to re-arm by setting TmrFlag.1
      
ExitISR
   movf   TmrFlag,W    ; load enable/disable flags
   sublw  0x00
   btfsc  STATUS,2     ; are both tick timers expired?
   bra    KillTmr1     ; yes, stop Timer1, we're done
   bcf    T1CON,TMR1ON ; no, still have one or more enabled, so stop Timer1
   movlw  0xb3         ; get low byte of reload value
   addwf  TMR1L,F      ; add to existing count
   btfsc  STATUS,2     ; low byte over-flow?
   incf   TMR1H,F      ; yes, bump-up TMR1 high byte by 1
   movlw  0x3c         ; no, get high byte to add
   addwf  TMR1H,F      ; add to existing TMR1 count for ~100mS interrupt intervals
   bsf    T1CON,TMR1ON ; re-start Timer1
   retfie              ; return with auto context restore
Endasm

' =============\ Main Program Start /============= '   
Start:
   ' using IOCBF you can tap 1 or more keys very fast, and never miss
   ' a press. Much faster than reading the entire port. And key presses
   ' do not generate an interrupt. Only set flag bits in IOCBF for each
   ' individual input that was edge-triggered.
   
   If IocFlag Then    ' if keys SW13, 14, 15 or 16 were pressed,
      State = 1       ' indicate LCD update needed
      Gosub GetKeys   ' get the button/buttons pressed
   Endif
   
   If State Then      ' update status on LCD only after a change
      Gosub GetStatus ' in State
      State = 0
   Endif
   
   Goto Start         ' loop forever

' =============\ Update Output Status /============= '
GetStatus:   
   ' Print status of timed outputs
   If (PORTD.0 | TmrFlag.0) = 0 Then
      Lcdout $fe,2,"Out1 off  "
   Else
      Lcdout $fe,2,"Out1 on   "
   Endif
   
   If (PORTD.1 | TmrFlag.1) = 0 Then
      Lcdout $fe,$c0,"Out2 off  "
   Else
      Lcdout $fe,$c0,"Out2 on   "
   Endif
   Return

' ===========\ Get Keypad Switch Presses /=========== '   
GetKeys:
   Pause 10              ' crude debounce for key press
   
   ' SW13 resets Out1
   If IOCBF.4 Then       ' IOCBF.4 = 1 when low edge detected on RB4
      While PORTB.4 = 0  ' wait for key release        
      Wend               ' 
      PORTD.0 = 1        ' activate output
      Tick1 = 0          ' reset Tick0 count
      TmrFlag.0 = 1      ' reset enable flag
      If !T1CON.0 Then T1CON.0 = 1 ' if Timer1 disabled, re-enable
   Endif
   
   ' SW14 resets Out2
   If IOCBF.5 Then       ' IOCBF.5 = 1 when low edge detected on RB5
      While PORTB.5 = 0  ' wait for key release
      Wend               '         
      PORTD.1 = 1        ' activate output
      Tick2 = 0          ' reset Tick2 count
      TmrFlag.1 = 1      ' reset enablg flag
      If !T1CON.0 Then T1CON.0 = 1 ' if Timer1 disabled, re-enable
   Endif
   
   ' SW15 disables Out1
   If IOCBF.6 Then       ' IOCBF.6 = 1 when low edge detected on RB6
      While PORTB.6 = 0  ' wait for key release
      Wend               '      
      TmrFlag.0 = 0      ' expire Out1 time immediately
      Tick1 = 0          ' reset Tick1 count
      PORTD.0 = 0        ' de-activate output
   Endif
   
   ' SW16 disables Out2
   If IOCBF.7 Then       ' IOCBF.7 = 1 when low edge detected on RB7
      While PORTB.7 = 0  ' wait for key release
      Wend               '         
      TmrFlag.1 = 0      ' expire Out2 time immediately
      Tick2 = 0          ' reset Tick2 count
      PORTD.1 = 0        ' de-activate output
   Endif
   IOCBF = 0             ' reset all int-on-change bits on exit
   IocFlag = 0
   Return

' ===========\ Hardware Initialization Routine /=========== '   
Init:
   ANSELA = 0    ' porta all digital
   ANSELB = 0    ' portb all digital
   ANSELD = 0    ' portd all digital
   ANSELE = 0    ' porte all digital
   
   PORTD = %00000011   ' turn on alarm outputs
   TRISD = 0           ' PORTD all outputs
       
   OPTION_REG.7 = 0    ' enable PORTB pullups   
   PORTB = 0           ' RB3 low
   TRISB = %11110111   ' RB3 = 0 for output to keys 13, 14, 15, 16
   WPUB = %11110000    ' weak pull-ups on for ----> RB4, 5,  6,  7
   IOCBP = 0           ' positive edge disabled
   IOCBN = %11110000   ' negative edge int-on-change enabled for keys

   Pause 100           ' wait for LCD to start
   Lcdout $fe,1        ' clear LCD
       
   PIR1 = 0            ' clear interrupt flags before enabling interrupts below
   PIE1 = %00000001    ' Timer1 interrupt enabled
   INTCON = %11000000  ' global & peripheral interrupts enabled
   
   ' and finally -- setup & start Timer1
   TMR1L = $b3         ' load Timer1 for ~100mS over-flows
   TMR1H = $3c
   T1CON = %00010001   ' set 1:2 prescale, Timer1 on
   T1GCON = 0          ' Timer1 gate control not used
   
   Goto Start          ' start main program
   
   End

           

Download the program file.