;************************************************************************** ; FILE: beastie.asm * ; CONTENTS: The Wee Beastie * ; COPYRIGHT: MadLab Ltd. 2003 * ; AUTHOR: James Hutchby * ; UPDATED: 13/04/03 * ;************************************************************************** list p=12F629 #include "p12f629.inc" ; __config _INTRC_OSC_NOCLKOUT & _WDT_ON & _MCLRE_OFF & _PWRTE_ON & _BODEN_ON & _CP_OFF & _CPD_OFF __config _INTRC_OSC_NOCLKOUT & _WDT_ON & _MCLRE_OFF & _PWRTE_ON & _BODEN_ON & _CP_ON & _CPD_ON __idlocs h'DE10' errorlevel -207,-302,-305,-306 ;************************************************************************** ; * ; Specification * ; * ;************************************************************************** ; power-up self-test - LEDs flash twice and motors run briefly ; LEDs flash continuously if LDR failure ; jitters if any change in light level ; 'feeds' when held under light source ; LEDs flash faster until fully fed ; motors run occasionally at random while feeding ; takes 45s to fully feed ; after feeding moves at random for typically 120s (if fully fed) ; LDRs charge in ~40us at 10k (light), ~40ms at 10M (dark) ;************************************************************************** ; * ; Port assignments * ; * ;************************************************************************** GPIO_IO equ b'101010' ; port I/O status LEDS equ 0 ; LEDs MOTOR1 equ 4 ; motors MOTOR2 equ 2 CAP equ 0 ; capacitor LDR1 equ 5 ; LDRs LDR2 equ 1 charge1 macro ; charge capacitor through LDR #1 clrf GPIO tris_ GPIO_IO&~(1< d'40' variable n = (i-d'20')/d'12' i -= (n*d'12')+d'20' variable m = i/d'4' i = 0 movlw n ; [4] call ldelay-m ; [8] else if i >= d'16' variable n = (i-d'16')/d'4' i -= (n*d'4')+d'16' call delay16-n ; [8] endif endif while i > 0 nop ; [4] i -= d'4' endw endm ;-------------------------------------------------------------------------- ; waits, fed with the wait in ms in the wreg ;-------------------------------------------------------------------------- routine wait_ms movwf work1 movlf 1,count goto wait2 ;-------------------------------------------------------------------------- ; waits, fed with the wait in 10ms in the w reg ;-------------------------------------------------------------------------- routine wait movwf count wait1 movlf d'10',work1 wait2 movlf CLOCK/(d'1000'*d'20'),work2 wait3 clrwdt ; [4] nop ; [4] decfsz work2 ; [4] goto wait3 ; [8] decfsz work1 goto wait2 decfsz count goto wait1 retlw 0 ;-------------------------------------------------------------------------- ; generates a pseudo random number in the range 0 to 15 ;-------------------------------------------------------------------------- routine get_random movfw rand_l iorwf rand_h,w bnz getr1 movfw TMR0 ; seed generator movwf rand_l xorlw h'ff' movwf rand_h getr1 rlf rand_h,w ; calculate next in sequence xorwf rand_h,w movwf work1 ; msb <= Q15 ^ Q14 swapf rand_l,w btfsc rand_h,4 xorlw h'80' ; msb <= Q12 ^ Q3 xorwf work1 rlf work1 rlf rand_l rlf rand_h ; << 1 + (Q15 ^ Q14 ^ Q12 ^ Q3) retlw 0 ;-------------------------------------------------------------------------- ; motor pwm control, fed with the period in 10ms in the w reg ;-------------------------------------------------------------------------- routine pwm movwf count+0 movlf d'10',count+1 pwm1 bcf GPIO,MOTOR1 ; drive motors in antiphase tstf duty2 skpz bsf GPIO,MOTOR2 DELAY set (MOTOR_PERIOD*CLOCK)/(d'1000'*MAX_SPEED) movlf MAX_SPEED,work2 pwm2 clrwdt ; [4] movfw duty1 ; [4] subwf work2,w ; [4] skpnz ; [8/4] bsf GPIO,MOTOR1 ; motor on [0/4] movfw duty2 ; [4] addwf work2,w ; [4] xorlw MAX_SPEED ; [4] skpnz ; [8/4] bcf GPIO,MOTOR2 ; motor off [0/4] delay DELAY-d'52' decfsz work2 ; [4] goto pwm2 ; [8] movfw speed1 ; adjust duty cycle subwf duty1,w skpc incf duty1 ; ramp up movfw speed1 skpnc movwf duty1 ; cap movfw speed2 ; adjust duty cycle subwf duty2,w skpc incf duty2 ; ramp up movfw speed2 skpnc movwf duty2 ; cap movlw MOTOR_PERIOD subwf count+1 bz pwm3 bc pwm1 pwm3 movlw d'10' addwf count+1 decfsz count+0 goto pwm1 bcf GPIO,MOTOR1 ; motors off bcf GPIO,MOTOR2 retlw 0 ;-------------------------------------------------------------------------- ; tests the LDRs, returns NZ flag set if both present ;-------------------------------------------------------------------------- routine test_ldrs discharge ; discharge the capacitor movlw DISCHARGE call wait_ms clrf timer+0 ; charge the capacitor through clrf timer+1 ; LDR #1 charge1 test1 clrwdt bsf GPIO,CAP incf timer+1 skpnz incf timer+0 movfw timer+0 iorwf timer+1,w bz test3 ; branch if timeout btfss GPIO,CAP goto test1 discharge ; discharge the capacitor movlw DISCHARGE call wait_ms clrf timer+0 ; charge the capacitor through clrf timer+1 ; LDR #2 charge2 test2 clrwdt bsf GPIO,CAP incf timer+1 skpnz incf timer+0 movfw timer+0 iorwf timer+1,w bz test3 ; branch if timeout btfss GPIO,CAP goto test2 clrz ; signal ok goto test4 test3 setz ; signal error test4 movlf 0,GPIO ; re-initialise port tris_ GPIO_IO retlw 0 ;-------------------------------------------------------------------------- ; samples the current light levels ;-------------------------------------------------------------------------- routine sample_ldrs discharge ; discharge the capacitor movlw DISCHARGE call wait_ms clrf timer+0 ; charge the capacitor through clrwdt ; LDR #1 charge1 variable i = d'10' while i > 0 bsf GPIO,CAP ; [4] incf timer+0 ; [4] btfsc GPIO,CAP ; [8] goto samp3 i -= 1 endw samp1 bsf GPIO,CAP ; [4] incf timer+0 ; [4] skpnz ; [8] goto samp2 btfss GPIO,CAP ; [4] goto samp1 ; [8] goto samp3 samp2 decf timer+0 ; overflow samp3 discharge ; discharge the capacitor movlw DISCHARGE call wait_ms clrf timer+1 ; charge the capacitor through clrwdt ; LDR #2 charge2 variable i = d'10' while i > 0 bsf GPIO,CAP ; [4] incf timer+1 ; [4] btfsc GPIO,CAP ; [8] goto samp6 i -= 1 endw samp4 bsf GPIO,CAP ; [4] incf timer+1 ; [4] skpnz ; [8] goto samp5 btfss GPIO,CAP ; [4] goto samp4 ; [8] goto samp6 samp5 decf timer+1 ; overflow samp6 clrf GPIO ; re-initialise port tris_ GPIO_IO retlw 0 ;-------------------------------------------------------------------------- ; feed from light source ;-------------------------------------------------------------------------- routine feed bcf flags,FULL clrf repeat feed1 movlw FLASH_PERIOD/d'10' ; determine flash period movwf flash rrf power,w movwf work1 rrf work1,w andlw b'111111' subwf flash tstf repeat bnz feed2 call get_random ; run motors at random swapf power,w andlw h'0f' index feed_probs andwf rand_l,w bnz feed2 SPEED set MAX_SPEED-MAX_POWER/4 movlf SPEED,work1 ; determine motor speed rrf power,w movwf work2 rrf work2,w andlw b'111111' addwf work1,w movwf speed1 btfss rand_h,0 clrf speed1 movwf speed2 btfsc rand_h,0 clrf speed2 clrf duty1 clrf duty2 movlf d'25',repeat feed2 tstf repeat bnz feed3 clrf speed1 clrf speed2 clrf duty1 clrf duty2 feed3 bsf GPIO,LEDS ; flash LEDs movfw flash subwf repeat skpc clrf repeat call pwm bcf GPIO,LEDS movfw flash subwf repeat skpc clrf repeat call pwm incf power ; increment power level until movlw MAX_POWER ; maximum subwf power,w skpnz bsf flags,FULL bz move call sample_ldrs movfw threshold ; still under light source ? subwf timer+0,w bnc feed4 movfw threshold subwf timer+1,w bc move ; branch if not feed4 goto feed1 ;-------------------------------------------------------------------------- ; move at random ;-------------------------------------------------------------------------- routine move move1 bsf GPIO,LEDS SPEED set MAX_SPEED-MAX_POWER/8 movlf SPEED,work1 ; determine motor speed rrf power,w movwf work2 rrf work2 rrf work2,w andlw b'11111' addwf work1,w movwf speed1 movwf speed2 call get_random movfw rand_l ; left, right or both motors andlw b'11' ; at random xorlw b'10' skpnz clrf speed1 xorlw b'10'^b'01' skpnz clrf speed2 clrf duty1 clrf duty2 movfw rand_h andlw ~h'80' iorlw h'40' call pwm bcf GPIO,LEDS call sample_ldrs btfsc flags,FULL goto move2 movlw MAX_POWER/8 subwf power,w bnc move2 movfw threshold ; under light source ? subwf timer+0,w bnc feed movfw threshold subwf timer+1,w bnc feed ; branch if yes move2 decfsz power goto move1 clrf timeout+0 ; reset timeout timer clrf timeout+1 goto main_loop ;-------------------------------------------------------------------------- ; main entry point ;-------------------------------------------------------------------------- routine main_entry clrf GPIO ; initialise port tris_ GPIO_IO bsf STATUS,RP0 ; prescale WDT 1:128, movlw b'10001111' ; weak pull-ups disabled movwf OPTION_REG clrf WPU bcf STATUS,RP0 bcf INTCON,GIE ; interrupts off movlf b'111',CMCON clrf PCLATH movfw STATUS ; wake-up from sleep ? andlw (1<