CBC-USNA-T77 shares this code
;**********************************************************************
; This program for the PIC16F874 solves the following program specification:
; 1. Have the processor generate an interrupt every 1 ms.
; 2. Toggle Port A <4> every 500 ms.
;**********************************************************************
; *
; Filename: easysamp.asm *
; Date: 06 March 2001 *
; File Version: 1 *
; *
; Author: CDR Charles B. Cameron, USN *
; Company: United States Naval Academy *
; *
; *
;**********************************************************************
; *
; Files required: *
; *
; p16f874.inc *
; *
;**********************************************************************
; *
; Notes:
; This program generates an interrupt every 1 ms. Upon interrupt
; it updates a counter.
; PORTA<4> is an output pin. It is toggled every 500 ms.
;
; It's assumed the processor is driven with a 4.000 MHz crystal
; oscillator and is to operate in HS mode.
;**********************************************************************
list p=16f874 ; list directive to define processor
#include <p16f874.inc> ; processor specific variable definitions
__CONFIG _CP_OFF & _WDT_OFF & _BODEN_OFF & _PWRTE_ON & _HS_OSC & _WRT_ENABLE_ON & _LVP_OFF & _CPD_OFF
; '__CONFIG' directive is used to embed configuration data within .asm file.
; The labels following the directive are located in the respective .inc file.
; See respective data sheet for additional information on configuration word.
; The particular choice given above turns code protection off, watch dog timer off,
; brown-out reset disabled, power-up timer enabled, HS oscillator mode selected,
; flash program memory write disabled, low-voltage in-circuit serial programming
; disabled, and data EE memory code protrection off. Change these at will!
;***** VARIABLE DEFINITIONS
w_temp EQU 0x20 ; variable used for context saving
status_temp EQU 0x21 ; variable used for context saving
cblock 0x22 ; define a series of variables.
count1 ; The MSB of a 2-byte register counting
; the number of Timer 2 interrupts
; which have occurred.
count0 ; The LSB of the same 2-byte register.
flag500ms ; Contains a 0 unless 500 1-ms Timer 2
; interrupts have been processed.
endc
; Bits within PORTA
ToggleBit equ 4 ; An output bit, toggled every 500 ms.
PortAMask equ B'00000000' ; We'll make all of PORT A an output.
;**********************************************************************
ORG 0x000 ; processor reset vector
clrf PCLATH ; ensure page 0 is used
goto main ; go to beginning of program
ORG 0x004 ; interrupt vector location
movwf w_temp ; save off current W register contents
swapf STATUS,W ; move status register into W register
clrf STATUS ; select Bank 0
movwf status_temp ; save off contents of STATUS register
; There's no need to save PCLATH since
; we'll stick to Bank 0 of program memory.
; isr code can go here or be located as a call subroutine elsewhere
btfsc PIR1,TMR2IF ; If Timer 2 caused the interrupt, handle it.
call Timer2
swapf status_temp,w ; retrieve copy of STATUS register
movwf STATUS ; restore pre-isr STATUS register contents
swapf w_temp,f
swapf w_temp,w ; restore pre-isr W register contents
retfie ; return from interrupt
; *********************************************
; Timer 2 Interrupt handler.
; Timer 2 has overflowed
;
Timer2
; Increment the LSB of the 1-ms counter.
incf count0,F
; If it rolls over, increment the MSB.
btfsc STATUS,Z
incf count1,F
; See if the count has reached 500.
; If so, set the toggle flag for the main processor
; to take action.
movlw 1 ; Is the MSB of the count 1?
subwf count1,W
btfss STATUS,Z ; If count1 is not 1, we haven't reached 500.
goto EndTimer2Interrupt
; Is the LSB = 500 - 256 = 244?
movlw D'500' - D'256'
subwf count0,W
btfss STATUS,Z ; If not, we still haven't reached 500.
goto EndTimer2Interrupt
; If we get here, it's because we have processed 500 1-ms Timer 2 Interrupts.
; Set the flag500ms variable to a non-zero value.
movlw H'FF'
movwf flag500ms
; Reinitialize count1 and count0
clrf count1
clrf count0
EndTimer2Interrupt
BCF PIR1,TMR2IF ; Clear flag and continue.
return
main
; ***********************************************************************************
; START OF CODE to initialize the processor
; The initialization code goes here since we'll end up here shortly after a reset.
; ***********************************************************************************
; ***********************************************************************************
; Most Bank 0 initializations are done here and they come first.
; ***********************************************************************************
bcf STATUS,RP0 ; Select Bank 0
bcf STATUS,RP1
clrf PORTA ; Initialize Port A by clearing the output latches.
clrf count1 ; Re-initialize count1 and count0.
clrf count0
clrf flag500ms ; Turn off the flag which, when set, says 500 ms has elapsed.
; ***********************************************************************************
; Most Bank 1 initializations come next.
; ***********************************************************************************
bsf STATUS,RP0 ; Select Bank 1
movlw PortAMask ; Initialize direction pins for Port A using TRISA.
movwf TRISA
movlw B'00000110' ; Don't use any pins of Port A for A/D conversions
movwf ADCON1
bcf STATUS,RP0 ; Revert to Bank 0
; ***********************************************************************************
; START OF CODE to initialize Timer 2
; These come next only because it's convenient to group them together, not because
; it's a necessity.
; Set up Timer 2 to generate interrupts every 1 ms. Since we're assuming an instruction
; cycle consumes 1 us, we need to cause an interrupt every 1000 instruction cycles.
; We'll set the prescaler to 4, the PR2 register to 25, and the postscaler to 10. This
; will generate interrupts every 4 x 25 x 10 = 1000 instruction cycles.
; ***********************************************************************************
CLRF TMR2 ; Clear Timer2 register
BSF STATUS, RP0 ; Bank1
bsf INTCON,PEIE ; Enable peripheral interrupts
CLRF PIE1 ; Mask all peripheral interrupts except
bsf PIE1,TMR2IE ; the timer 2 interrupts.
BCF STATUS, RP0 ; Bank0
CLRF PIR1 ; Clear peripheral interrupts Flags
movlw B'01001001' ; Set Postscale = 10, Prescale = 4, Timer 2 = off.
movwf T2CON
BSF STATUS, RP0 ; Bank1
movlw D'25'-1 ; Set the PR2 register for Timer 2 to divide by 25.
movwf PR2
BCF STATUS, RP0 ; Bank0
bsf INTCON,GIE ; Global interrupt enable.
BSF T2CON,TMR2ON ; Timer2 starts to increment
; ***********************************************************************************
; END OF CODE to initialize Timer 2
; ***********************************************************************************
; ***********************************************************************************
; main()
; This is the main program. It does only one thing: check to see if it's time to
; toggle PORTA<togglebit> and do so if it is time. Otherwise it's busily engaged
; in using up all the instruction cycles not required by the interrupt handlers.
loop
movf flag500ms,W ; Has the flag500ms been set?
btfsc STATUS,Z
goto loop ; Not yet. Keep looking.
; Yes, it's time to toggle PORTA<ToggleBit> and reset flag500ms.
movlw ToggleBit
xorwf PORTA,f
clrf flag500ms
goto loop ; Now wait for the next occurence.
; ***********************************************************************************
END ; directive 'end of program''
See:
Questions: