; ir_range.asm ; By Joel Jordan ; 4 August, 2002 ; ; Simple IR object detector ; detects objects and gives a relative distance ; ; Circuit uses PNA4602 IR receiver ; schematics available at http://www.acm.uiuc.edu/sigarch/ ; Include the header file for our target chip #include "p16f876.inc" ; First line, declare that we want a 16F876 PIC ; and that our numbers will be in Hexadecimal ; unless otherwise noted LIST P=16f876, R=HEX ; Tell the programmer what options we want to use ; In this case: ; _WDT_OFF -- Watchdog Timer Off ; _XT_OSC -- Crystal Oscillator ; _CP_OFF -- Code Protect Off ; _LVP_OFF -- Low-voltage programming off ; -> important so we can use PORTB<3> __CONFIG _WDT_OFF & _XT_OSC & _CP_OFF & _LVP_OFF ; Variable Declarations cblock 0x20 ; local variables, only in bank 0 int_postscalar ; temporary counter ir_freq_cnt ; keeps track of what frequency being sent range_out ; displayed on LEDs to show distance int_flag_reg ; holds flag set after every interrupt endc cblock 0x70 ; global variables -- visible across all banks StatusSave ; save space for flags during interrupt WRegSave ; save space for accumulator during interrupt endc #define int_flag int_flag_reg, 0 org 0x0 ; reset vector -- executed on reset goto Start org 0x04 ; interrupt vector -- called on timer Interrupt: btfss INTCON, T0IF ; if the interrupt was not from TMR0 retfie ; then return movwf WRegSave ; save accumulator swapf STATUS, w ; and status flag movwf StatusSave bcf STATUS, RP0 ; set bank 0 decfsz int_postscalar,f ; TMR0 prescalar is only 8 bits, so goto int_done ; int_postscalar acts as an 8-bit extension to it bsf int_flag movlw 0x2 movwf int_postscalar decfsz ir_freq_cnt, f goto int_get_freq movlw 0x8 movwf ir_freq_cnt int_get_freq: movf ir_freq_cnt, w call get_pwm_freq bsf STATUS, RP0 movwf PR2 bcf STATUS, RP0 movf ir_freq_cnt, w call get_pwm_duty movwf CCPR1L int_done: swapf StatusSave, w movwf STATUS swapf WRegSave, f swapf WRegSave, w bcf INTCON, T0IF retfie Start CLRF PORTB ; Initialize Port B CLRW BSF STATUS,RP0 ; Select Register Bank 1 MOVWF TRISB ; Set B to all outputs movlw b'11111111' movwf TRISA ; set A to all inputs movlw 0x06 ; 0x6 - code found in data sheet to movwf ADCON1 ; set port a to digital inputs bcf OPTION_REG, T0CS ; TMR0 prescalar = 256 movlw b'00000111' iorwf OPTION_REG, f bcf OPTION_REG, 3 ; assign prescalar to TMR0 bsf INTCON, T0IE ; enable TMR0 interrupt bsf INTCON, GIE ; global interrupt enable bcf STATUS, RP0 ; select register bank 0 clrf TMR0 ; clear timer0 count movlw 0x8 movwf int_postscalar ; used to further reduce int frequency movwf ir_freq_cnt ; select intial PWM frequency call SetupPWM ; start IR led blinking on RC2 call PWM_Enable loop: btfss int_flag ; wait for interrupt to happen goto loop bcf int_flag ; this should check to see if a code is received. now it just looks to ; see if a '1' is received. Effectively, this lights up an LED ; if a signal is _not_ seen, since the receiver holds the output ; high when no signal is seen. This is correct behavior, but ; it isn't reliable. btfss PORTA, 0 bcf range_out, 0 btfsc PORTA, 0 bsf range_out, 0 ; the distance displayed on LEDs is rotated into the correct position movf ir_freq_cnt, w sublw 0x8 btfss STATUS, Z goto rotate_temp movf range_out,w movwf PORTB goto loop rotate_range: rlf range_out, f goto loop SetupPWM: ; initializes PWM to 38KHz for IR LED bsf STATUS, RP0 ; select register bank 1 movlw 0x82 ; used to calculate PWM period movwf PR2 bcf STATUS, RP0 ; register bank 0 bsf CCP1CON, 4 ; CCP1CON<4:5> used to determine bcf CCP1CON, 5 ; PWM duty cycle movlw b'01000000' ; set initial duty cycle movwf CCPR1L bsf STATUS, RP0 ; register bank 1 bcf TRISC, 2 ; port c is an output bcf STATUS, RP0 ; register bank 0 movlw b'00000100' ; movwf T2CON ; enable PWM on TIMER2 return PWM_Enable: bsf CCP1CON, 3 bsf CCP1CON, 2 return PWM_Disable: bcf CCP1CON, 3 bcf CCP1CON, 2 return get_pwm_freq: addwf PCL, f nop retlw 0x82 retlw 0x88 retlw 0x8D retlw 0x91 retlw 0x94 retlw 0x96 retlw 0x97 retlw 0x98 get_pwm_duty: ; all PWM duty cycles are the same because I didn't bother ; to calculate the correct values. These should be redone addwf PCL, f nop retlw b'01000000' retlw b'01000000' retlw b'01000000' retlw b'01000000' retlw b'01000000' retlw b'01000000' retlw b'01000000' retlw b'01000000' END