' 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