;************************************************************************** ; FILE: insect.asm * ; CONTENTS: 'Insectoid' insect * ; COPYRIGHT: MadLab Ltd. 1998-2006 * ; AUTHOR: James Hutchby * ; UPDATED: 21/07/06 * ;************************************************************************** list p=12F629 #include "p12f629.inc" __config _INTRC_OSC_NOCLKOUT & _WDT_ON & _MCLRE_OFF & _PWRTE_ON & _BODEN_OFF & _CP_ON & _CPD_OFF __idlocs h'BD21' errorlevel -207,-302,-305,-306 ;************************************************************************** ; * ; Specification * ; * ;************************************************************************** ; Stimulus Response ; -------- -------- ; dark - ; dark eyes ; dark chirrup ; dark eyes + chirrup ; light - ; light eyes ; light chirrup ; light eyes + chirrup ; test chirrup on power-up ; LEDs echo LDR light sensor on power-up ; training whistle within 1 second of response ; LEDs flash when training whistle acknowledged ; 8 whistles inhibits response ; LEDs stop flashing when fully trained ; sleep mode when fully trained if null response ; faint click as wakes from sleep = 'heartbeat' ; threatens if recognises another ; training saved to EEPROM ; subsequently loads training from EEPROM on power-up ; factory reset if very bright light on power-up (signalled by long beep) ;************************************************************************** ; * ; Port assignments * ; * ;************************************************************************** GPIO_IO equ b'111000' ; GPIO I/O status GATE equ 2 ; microphone enable LDR equ 5 ; LDR MIC equ 3 ; microphone LEDS equ 4 ; LEDs PIEZO equ 1 ; speaker ANTENNAE equ 0 ; antennae LEDS_on macro ; LEDs on bcf GPIO_,LEDS movff GPIO_,GPIO bsf STATUS,RP0 bcf TRISIO,LEDS bcf STATUS,RP0 endm LEDS_off macro ; LEDs off bsf STATUS,RP0 bsf TRISIO,LEDS bcf STATUS,RP0 endm charge macro ; charge capacitor bsf STATUS,RP0 bsf TRISIO,LDR bcf STATUS,RP0 endm discharge macro ; discharge capacitor bcf GPIO_,LDR movff GPIO_,GPIO bsf STATUS,RP0 bcf TRISIO,LDR bcf STATUS,RP0 endm ;************************************************************************** ; * ; Constants and timings * ; * ;************************************************************************** CLOCK equ d'4000000' ; processor clock frequency in Hz MIC_SAMPLE equ d'20' ; microphone sample period in ms MIC_REPEAT equ d'3' ; number of successive sample periods ; total sample period in ms SAMPLE_TIME equ MIC_SAMPLE*MIC_REPEAT DISCHARGE equ d'1' ; capacitor discharge period in 1/100 s RESET equ d'2' ; factory reset threshold BRIGHT equ d'100' ; bright threshold THRESHOLD equ d'5' ; silence threshold WHISTLE_FRQ equ d'3000' ; whistle frequency in Hz CNTL_FRQ equ d'100' ; control frequency (100 Hz) FUND_FRQ equ d'4000' ; chirrup fundamental frequency MOD1_FRQ equ d'15' ; modulation frequency #1 MOD2_FRQ equ d'3' ; modulation frequency #2 ANTENNAE_ON equ d'3' ; antennae pulse on time in 1/100 s ANTENNAE_OFF equ d'3' ; antennae pulse off time in 1/100 s CHIRRUP_PITCH equ CLOCK/(2*FUND_FRQ*d'16') CHIRRUP_TIME equ d'35' ; chirrup duration in 1/100 s RECOG_DELAY equ d'5' ; delay when recognising in 1/100 s HEADER_CYCLES equ d'250' ; click header duration in cycles CLICK_PERIOD equ d'10' ; click period in ms ON_FRQ equ d'4800' ; click 'on' frequency in Hz OFF_FRQ equ d'3200' ; click 'off' frequency in Hz ; click 'on' and 'off' durations in cycles ON_CYCLES equ (ON_FRQ*CLICK_PERIOD)/d'1000' OFF_CYCLES equ (OFF_FRQ*CLICK_PERIOD)/d'1000' BITS equ d'2' ; number of data bits ID equ b'01' ; ID code FLASH_RATE equ d'6' ; LEDs flash rate FLASH_REPEAT equ d'4' ; LEDs flash repeat ; differentiated sound events SILENCE equ b'00' ; no pulses in sample period NOISE equ b'01' ; varying or overflow pulses WHISTLE equ b'10' ; sustained pulses at whistle frequency ; training mask and value TRAINING equ (b'11'<<6)|(b'11'<<4) TRAINING_ equ (WHISTLE<<6)|(WHISTLE<<4) RESPONSE equ d'8' ; response count MAGIC equ d'42' ; valid EEPROM data BEEP_PITCH equ d'40' ; long beep pitch ;************************************************************************** ; * ; File register usage * ; * ;************************************************************************** RAM equ h'20' MAX set h'60' cblock RAM flags ; various flags GPIO_ ; shadow copy ndx ; EEPROM index training:d'8' ; training counters (4 x dark + 4 x light) response ; response (00 to 11) history ; sound event history buffer pulses ; previous pulse count timer ; response timer rand_l, rand_h ; random number pitch ; chirrup pitch service ; service flags mod1_cnt, mod2_cnt ; modulation counters duty_cnt ; antennae duty cycle counter tx_data ; transmit data prev ; previous microphone state count_l, count_h ; pulse count sound ; sound event count, loop ; scratch counters work1, work2 ; work registers RAM_ endc if RAM_ > MAX error "File register usage overflow" endif ; flag bits LIGHT equ 0 ; set if light sensed TRAINED_DARK equ 1 ; set if fully trained in dark TRAINED_LIGHT equ 2 ; set if fully trained in light ; response bits EYES equ 0 ; set if eyes on CHIRRUP equ 1 ; set if chirruping ; service flags FUND equ 0 ; fundamental flag MOD1 equ 1 ; modulation #1 flag MOD2 equ 2 ; modulation #2 flag DUTY equ 3 ; antennae duty cycle flag ;************************************************************************** ; * ; Macros * ; * ;************************************************************************** routine macro label ; routine label endm table macro label ; define lookup table label addwf PCL endm entry macro value ; define table entry retlw value endm index macro label ; index lookup table call label endm jump macro label ; jump through table goto label endm tstw macro ; test w register iorlw 0 endm movff macro f1,f2 ; move file to file movfw f1 movwf f2 endm movlf macro n,f ; move literal to file movlw n movwf f endm ;-------------------------------------------------------------------------- ; reset vector ;-------------------------------------------------------------------------- org 0 bsf STATUS,RP0 call h'3ff' movwf OSCCAL bcf STATUS,RP0 goto main_entry ;************************************************************************** ; * ; Procedures * ; * ;************************************************************************** ;-------------------------------------------------------------------------- ; returns a pseudo random number in w reg ;-------------------------------------------------------------------------- routine get_random movfw rand_l iorwf rand_h,w bnz rand1 movff pulses,rand_l ; seed generator movff TMR0,rand_h rand1 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) movfw rand_l ; random number return ;-------------------------------------------------------------------------- ; timing delay, fed with the delay in 1/100 s in the w reg ;-------------------------------------------------------------------------- routine delay movwf count delay1 movlw CLOCK/(d'100'*d'16'*d'256') movwf work1 delay2 clrf work2 delay3 clrwdt ; [4] decfsz work2 ; [4] goto delay3 ; [8] decfsz work1 goto delay2 decfsz count goto delay1 return ;-------------------------------------------------------------------------- ; writes the EEPROM with w reg ;-------------------------------------------------------------------------- routine write_EEPROM bsf STATUS,RP0 movwf EEDATA movff ndx,EEADR movfw EEDATA bsf EECON1,RD xorwf EEDATA bz write2 movwf EEDATA bsf EECON1,WREN movlf h'55',EECON2 movlf h'aa',EECON2 bsf EECON1,WR write1 clrwdt btfsc EECON1,WR goto write1 bcf EECON1,WREN write2 bcf STATUS,RP0 return ;-------------------------------------------------------------------------- ; reads the EEPROM into w reg ;-------------------------------------------------------------------------- routine read_EEPROM bsf STATUS,RP0 movff ndx,EEADR bsf EECON1,RD movfw EEDATA bcf STATUS,RP0 return ;-------------------------------------------------------------------------- ; transmits a click, fed with the number of cycles in the w reg ;-------------------------------------------------------------------------- routine tx_on movwf count movlw CLOCK/(2*ON_FRQ*d'12') goto tx0 routine tx_off movwf count movlw CLOCK/(2*OFF_FRQ*d'12') tx0 movwf pitch tx1 clrwdt bsf GPIO_,PIEZO ; toggle speaker output movff GPIO_,GPIO movff pitch,work1 ; half-cycle delay tx2 decfsz work1 ; [4] goto tx2 ; [8] bcf GPIO_,PIEZO ; toggle speaker output movff GPIO_,GPIO movff pitch,work1 ; half-cycle delay tx3 decfsz work1 ; [4] goto tx3 ; [8] decfsz count goto tx1 return ;-------------------------------------------------------------------------- ; transmits an ID code ;-------------------------------------------------------------------------- routine tx_ID movlw HEADER_CYCLES ; header call tx_on movlf 1+BITS+1,count_h ; start bit + data bits + stop bit movlf ID<<(7-BITS),tx_data tx4 movlw OFF_CYCLES btfsc tx_data,7 movlw ON_CYCLES btfss tx_data,7 call tx_off btfsc tx_data,7 call tx_on rlf tx_data decfsz count_h goto tx4 return ;-------------------------------------------------------------------------- ; sounds a long beep ;-------------------------------------------------------------------------- routine long_beep movlf d'10',count long1 clrf work1 long2 clrwdt bsf GPIO_,PIEZO ; toggle speaker output movff GPIO_,GPIO movlf BEEP_PITCH,work2 ; half-cycle delay long3 decfsz work2 goto long3 bcf GPIO_,PIEZO ; toggle speaker output movff GPIO_,GPIO movlf BEEP_PITCH,work2 ; half-cycle delay long4 decfsz work2 goto long4 decfsz work1 goto long2 decfsz count goto long1 return ;-------------------------------------------------------------------------- ; sounds a 'chirrup' and moves the antennae, fed with the duration in ; 1/100 s in the w reg ;-------------------------------------------------------------------------- routine chirrup movwf count movlf CHIRRUP_PITCH,pitch ; pitch bsf STATUS,RP0 ; prescale TMR0 1:256, movlw b'10000111' ; weak pull-ups disabled movwf OPTION_REG bcf STATUS,RP0 clrf service clrf TMR0 movlw 1 movwf mod1_cnt movwf mod2_cnt movwf duty_cnt beep1 btfss service,MOD1 goto beep2 btfss service,MOD2 goto beep2 btfsc service,FUND ; toggle speaker output bsf GPIO_,PIEZO btfss service,FUND beep2 bcf GPIO_,PIEZO ; clear speaker output movff GPIO_,GPIO movff pitch,work1 ; half-cycle delay beep3 clrwdt ; [4] decfsz work1 ; [4] goto beep3 ; [8] movlw 1< 0 incf count ; [4] btfsc GPIO,LDR ; charged ? [8] goto getl2 ; branch if yes n -= 1 endw getl1 incf count ; [4] bz getl3 ; branch if timeout [8] btfss GPIO,LDR ; charged ? [4] goto getl1 ; loop if not [8] getl2 incf count getl3 btfss response,EYES goto getl4 LEDS_on getl4 decf count,w return ;-------------------------------------------------------------------------- ; polls the microphone and light sensor ;-------------------------------------------------------------------------- routine poll bcf flags,LIGHT ; poll the LDR call get_ldr sublw BRIGHT skpnc bsf flags,LIGHT clrf sound ; signal silence clrf prev ; previous microphone state clrf count poll1 clrf count_l ; clear pulse count clrf count_h ; number of loop iterations LOOP set (CLOCK*MIC_SAMPLE)/(d'1000'*d'60'*d'16') movlf LOOP,work1 poll2 movlf d'16',work2 poll3 movlw 1 ; [4] xorwf prev ; [4] nop ; sample the microphone [4] movlw 0 ; [4] btfsc GPIO,MIC ; [8] movlw 1 xorwf prev ; [4] movwf prev ; [4] skpnz ; branch if no change [4] incf count_l ; increment pulse count [4] skpnz ; [8] incf count_h decfsz work2 ; [4] goto poll3 ; [8] clrwdt decfsz work1 goto poll2 incf count tstf count_h ; high frequency ? bnz poll6 ; branch if yes movlw THRESHOLD ; silence ? subwf count_l,w bnc poll5 ; branch if yes ; number of click pulses PULSES set (2*ON_FRQ*MIC_SAMPLE)/d'1000' min set (PULSES*(d'100'-d'10'))/d'100' max set (PULSES*(d'100'+d'10'))/d'100' movlw min ; within tolerance of number subwf count_l,w ; of click pulses ? bnc poll4 movlw max subwf count_l,w bnc rx_ID ; branch if yes ; number of whistle pulses PULSES set (2*WHISTLE_FRQ*MIC_SAMPLE)/d'1000' min set (PULSES*(d'100'-d'40'))/d'100' max set (PULSES*(d'100'+d'40'))/d'100' poll4 movlw min ; within tolerance of number subwf count_l,w ; of whistle pulses ? bnc poll6 movlw max subwf count_l,w bnc poll7 ; branch if yes goto poll6 poll5 tstf sound bz poll8 poll6 movlf NOISE,sound ; signal noise goto poll8 poll7 movlf WHISTLE,sound ; signal whistle poll8 movlw MIC_REPEAT ; loop for all sample periods subwf count,w bnz poll1 return ;-------------------------------------------------------------------------- ; tests for a click, returns the click state in the carry flag ;-------------------------------------------------------------------------- routine test_click clrf prev ; previous microphone state clrf count_l ; clear pulse count ; number of loop iterations LOOP set (CLOCK*CLICK_PERIOD)/(d'1000'*d'6'*d'56') movlf LOOP,work1 testc1 movlw 1 ; [4] xorwf prev ; [4] nop ; sample the microphone [4] movlw 0 ; [4] btfsc GPIO,MIC ; [8] movlw 1 xorwf prev ; [4] movwf prev ; [4] skpnz ; branch if no change [4] incf count_l ; increment pulse count [4] clrwdt ; [4] decfsz work1 ; [4] goto testc1 ; [8] MID_CYCLES equ (ON_CYCLES+OFF_CYCLES)/2 movlw (2*MID_CYCLES)/d'6' subwf count_l ; carry flag set if 'on' click return ;-------------------------------------------------------------------------- ; receives an ID code ;-------------------------------------------------------------------------- routine rx_ID rx1 call test_click ; wait for start bit bc rx1 movlf 1+BITS+1,count_h ; start bit + data bits + stop bit clrf tx_data goto rx3 rx2 call test_click call test_click call test_click call test_click rx3 call test_click call test_click rlf tx_data decfsz count_h goto rx2 rrf tx_data ; test stop bit bc rx4 btfsc tx_data,BITS ; test start bit goto rx4 movfw tx_data ; test ID code xorlw ID bz recognise rx4 clrf history ; clear the history buffer goto main_loop ;-------------------------------------------------------------------------- ; another recognised ;-------------------------------------------------------------------------- routine recognise LEDS_on ; LEDs on call tx_ID ; transmit ID code LEDS_off ; LEDs off movlw RECOG_DELAY ; delay call delay goto recognise ; loop ;-------------------------------------------------------------------------- ; initialises training ;-------------------------------------------------------------------------- routine init_training movlf training,FSR movlf d'8',count init1 movlf RESPONSE,0 incf FSR decfsz count goto init1 bcf flags,TRAINED_DARK bcf flags,TRAINED_LIGHT return ;-------------------------------------------------------------------------- ; saves training to EEPROM ;-------------------------------------------------------------------------- routine save_training movlf training,FSR movlf 1,ndx movlf d'8',count save1 movfw 0 call write_EEPROM incf FSR incf ndx decfsz count goto save1 clrf ndx movlw MAGIC call write_EEPROM return ;-------------------------------------------------------------------------- ; loads training from EEPROM ;-------------------------------------------------------------------------- routine load_training movlf 1,ndx movlf training,FSR movlf d'8',count load1 call read_EEPROM movwf 0 movlw RESPONSE+1 subwf 0,w movlw RESPONSE skpnc movwf 0 incf ndx incf FSR decfsz count goto load1 return ;-------------------------------------------------------------------------- ; erases training from EEPROM ;-------------------------------------------------------------------------- routine erase_training clrf ndx clrw call write_EEPROM return ;-------------------------------------------------------------------------- ; modifies training ;-------------------------------------------------------------------------- modify_training macro movlw training+0 ; decrement training counter if btfsc flags,LIGHT ; not zero movlw training+4 addwf response,w movwf FSR tstf 0 skpz decf 0 endm ;-------------------------------------------------------------------------- ; determines if fully trained ;-------------------------------------------------------------------------- test_trained macro clrf work1 ; trained if all counters except one tstf training+0 ; in a set are zero skpz incf work1 tstf training+1 skpz incf work1 tstf training+2 skpz incf work1 tstf training+3 skpz incf work1 tstf work1 ; check integrity skpnz call init_training bcf flags,TRAINED_DARK decf work1 skpnz bsf flags,TRAINED_DARK clrf work1 tstf training+4 skpz incf work1 tstf training+5 skpz incf work1 tstf training+6 skpz incf work1 tstf training+7 skpz incf work1 tstf work1 ; check integrity skpnz call init_training bcf flags,TRAINED_LIGHT decf work1 skpnz bsf flags,TRAINED_LIGHT endm ;-------------------------------------------------------------------------- ; gets a random response ;-------------------------------------------------------------------------- get_response macro call get_random getr1 andlw b'11' movwf response movlw training+0 ; response permitted ? btfsc flags,LIGHT movlw training+4 addwf response,w movwf FSR incf response,w tstf 0 bz getr1 ; branch if not endm ;-------------------------------------------------------------------------- ; flashes LEDs ;-------------------------------------------------------------------------- routine flash_leds movlf FLASH_REPEAT,pulses flash1 LEDS_on movlw FLASH_RATE call delay LEDS_off movlw FLASH_RATE call delay decfsz pulses goto flash1 return ;-------------------------------------------------------------------------- ; main entry point ;-------------------------------------------------------------------------- routine main_entry clrf GPIO ; initialise port clrf GPIO_ bsf STATUS,RP0 movlf GPIO_IO,TRISIO bcf STATUS,RP0 bsf STATUS,RP0 ; prescale TMR0 1:2, movlw b'10000000' ; weak pull-ups disabled movwf OPTION_REG clrf WPU bcf STATUS,RP0 bcf INTCON,GIE ; interrupts off movlf b'111',CMCON clrf PCLATH bsf GPIO_,GATE ; enable the microphone movff GPIO_,GPIO movfw STATUS ; wake-up from sleep ? andlw (1<