;********************************************************************** ; This file is a basic code template for assembly code generation * ; on the PICmicro PIC16F873. This file contains the basic code * ; building blocks to build upon. * ; * ; If interrupts are not used all code presented between the ORG * ; 0x004 directive and the label main can be removed. In addition * ; the variable assignments for 'w_temp' and 'status_temp' can * ; be removed. * ; * ; Refer to the MPASM User's Guide for additional information on * ; features of the assembler (Document DS33014). * ; * ; Refer to the respective PICmicro data sheet for additional * ; information on the instruction set. * ; * ;********************************************************************** ; * ; Filename: xxx.asm * ; Date: * ; File Version: * ; * ; Author: * ; Company: * ; * ; * ;********************************************************************** ; * ; Files required: * ; * ; * ; * ;********************************************************************** ; * ; Notes: * ; * ; * ; * ; * ;********************************************************************** list p=16f873 ; list directive to define processor #include ; processor specific variable definitions ;; NOTE: For 16F873A, change both 16f873 references to 16f873a above __CONFIG _CP_OFF & _WDT_OFF & _BODEN_OFF & _PWRTE_ON & _HS_OSC & _WRT_ENABLE_ON & _LVP_OFF & _CPD_OFF ;; NOTE: For 16F873A, change the _WRT_ENABLE_ON to _WRT_OFF here ^^^^^^^^^^^^^^ ; '__CONFIG' directive is used to embed configuration data within .asm file. ; The lables following the directive are located in the respective .inc file. ; See respective data sheet for additional information on configuration word. ;***** VARIABLE DEFINITIONS ; ring buffers for audio band processing band0_buf0 EQU 0x20 band0_buf1 EQU 0x21 band0_buf2 EQU 0x22 band0_buf3 EQU 0x23 band0_buf4 EQU 0x24 band0_buf5 EQU 0x25 band0_buf6 EQU 0x26 band0_buf7 EQU 0x27 band1_buf0 EQU 0x28 band1_buf1 EQU 0x29 band1_buf2 EQU 0x2a band1_buf3 EQU 0x2b band1_buf4 EQU 0x2c band1_buf5 EQU 0x2d band1_buf6 EQU 0x2e band1_buf7 EQU 0x2f band2_buf0 EQU 0x30 band2_buf1 EQU 0x31 band2_buf2 EQU 0x32 band2_buf3 EQU 0x33 band2_buf4 EQU 0x34 band2_buf5 EQU 0x35 band2_buf6 EQU 0x36 band2_buf7 EQU 0x37 filt_buf0 EQU 0x38 filt_buf1 EQU 0x39 filt_buf2 EQU 0x3a filt_buf3 EQU 0x3b filt_buf4 EQU 0x3c filt_buf5 EQU 0x3d filt_buf6 EQU 0x3e filt_buf7 EQU 0x3f ; temporary buffers for summing sum_temp0 EQU 0x40 sum_temp1 EQU 0x41 sum_temp2 EQU 0x42 sum_temp3 EQU 0x43 ; band buffer pointers band0_bufptr EQU 0x48 band1_bufptr EQU 0x49 band2_bufptr EQU 0x4a filt_bufptr EQU 0x4b ; band 8shot counters band0_ctr EQU 0x4c band1_ctr EQU 0x4d band2_ctr EQU 0x4e filt_ctr EQU 0x4f ;; the following 16 values need to be in order so that ;; we can select their output waveforms with 3 switches ; band waveforms band0_wave EQU 0x50 band1_wave EQU 0x51 band2_wave EQU 0x52 band3_wave EQU 0x53 ; band averages band0_avg EQU 0x54 band1_avg EQU 0x55 band2_avg EQU 0x56 a2d_input EQU 0x57 ; band thresholds for firing output band0_thresh EQU 0x58 band1_thresh EQU 0x59 band2_thresh EQU 0x5a band3_thresh EQU 0x5b ; timer variable delay_timer EQU 0x61 ; flags to see what bands to process band_flags EQU 0x62 ;; bits to capture for sending audio_port EQU 0x63 ;********************************************************************** ; "vectors" section of code ORG 0x000 ; processor reset vector nop ; nop required for icd GOTO init ; "page0" section of code ORG 0x005 init ; initialize IOs, A2D, PWM and other variables ; initialize buffer ring pointers CLRF band0_bufptr CLRF band1_bufptr CLRF band2_bufptr CLRF filt_bufptr ; initialize buffer ring pointers CLRF band0_thresh CLRF band1_thresh CLRF band2_thresh ;; set 8 shot counters to 8 MOVLW 0x8 MOVWF band0_ctr MOVWF band1_ctr MOVWF band2_ctr MOVWF filt_ctr ;; clear processing flags and audio strobes CLRF band_flags CLRF audio_port ; configure PWM for 7 bit output BSF STATUS,5 ;; set to bank 1 MOVLW 0x0f MOVWF TRISB ;; drive out top 4 bits of B port for lights ;;; DEBUG PWM START ;;MOVLW 0x7f ;;MOVWF PR2 ;; period is 512 20Mhz cycles ;;MOVLW 0xfb ;;MOVWF TRISC ;; drive bit 2(PWM) of port C ;;BCF STATUS,5 ;; following registers in bank 0 ;;MOVLW 0x4 ;;MOVWF T2CON ;; start timer 2 for PWM with prescaler of 1 ;;MOVLW 0x40 ;; ;;MOVWF CCPR1L ;; set upper five bits of duty cycle to binary 1000000 (50%) ;;MOVLW 0x0c ;;MOVWF CCP1CON ;; turn on PWM and set lowest two bits to 00 ;;; DEBUG PWM END ; configure A2D for left-justified, foscdiv32 ; input on AN0 only BSF STATUS,5 ;; ADCON1 register is in bank1 MOVLW 0x0e MOVWF ADCON1 BCF STATUS,5 ;; ADCON0 register in bank 0 MOVLW 0x81 MOVWF ADCON0 ; wait for acquisition of voltage into holding cap MOVLW 0x80 ;; 128 5Mhz cycles enough (>20us) MOVWF delay_timer delay1 DECFSZ delay_timer, F GOTO delay1 ; start A2D BSF ADCON0,2 ; wait until done wait_a2d_1 BTFSC ADCON0,2 GOTO wait_a2d_1 ; main processing loop after we get proper A2D ; we must do enough processing to acqure a new voltage ; and then start a new A2D at the right time mainloop ; first put A2D into initial circular buffer INCF band0_bufptr,W ;; bufptr+1 -> W ANDLW 0x7 ;; truncate to 0-7 MOVWF band0_bufptr ;; put back in pointer reg ADDLW band0_buf0 ;; add offset to band0 circ buffer MOVWF FSR ;; put in indirect address register RRF ADRESH, W ;; get A2D result and right shift by 1 MOVWF INDF ;; put into address at FSR ptr MOVWF a2d_input ;; put into output waveform debug register ; add up first circular buffer MOVF band0_buf0,W ;; get value 0 ADDWF band0_buf1,W ;; add in value1 MOVWF sum_temp0 ;; store partial sum in temp0 MOVF band0_buf2,W ADDWF band0_buf3,W MOVWF sum_temp1 MOVF band0_buf4,W ADDWF band0_buf5,W MOVWF sum_temp2 MOVF band0_buf6,W ADDWF band0_buf7,W ;; adding two <0x80 numbers, carry is clear MOVWF sum_temp3 RRF sum_temp0, F ;; right shift temp0, carry is guaranteed clear BCF STATUS, 0 ;; clear C bit to prevent rotation errors RRF sum_temp1, W ;; right shift temp1 into W ADDWF sum_temp0, F ;; add W to temp0 and put back in temp0 RRF sum_temp2, F ;; right shift temp2 BCF STATUS, 0 ;; clear C bit to prevent rotation errors RRF sum_temp3, W ;; right shift temp3 into W ADDWF sum_temp2, F ;; add W to temp2 and put back in temp2 RRF sum_temp0, F ;; right shift temp0 BCF STATUS, 0 ;; clear C bit to prevent rotation errors RRF sum_temp2, W ;; right shift temp2 into W ADDWF sum_temp0, F ;; add W to temp0 and put back in temp0 ;; temp0 now has sum of 8 A2D values right shifted by 3 RRF sum_temp0, F ; halve the average, carry possibly set MOVF sum_temp0, W ;; get the halved average MOVWF band0_avg ; save in register for next stage BCF STATUS, 0 ;; clear carry bit from RRF above RRF ADRESH, W ; halve the input signal into W SUBWF sum_temp0, F ; subtract input from average MOVFW sum_temp0 ;; get wave into W BTFSC sum_temp0, 7 ; test top bit for negative value SUBLW 0 ;; do 0-W if negative MOVWF sum_temp0 ;; move W back, possibly subtracted from zero ;; sum_temp0 now contains a value between 0-127 with ;; absolute waveform that we want ;; we've done about 30 instructions (6us of acquisition time) ;; so start a new A2D BSF ADCON0,2 MOVF band0_thresh, W ;; get current threshold SUBWF sum_temp0, W ;; waveform-thresh -> W, set carry bit MOVF sum_temp0, W ;; get wave in W, don't touch carry bit MOVWF band0_wave ;; save waveform for debugging BTFSC STATUS, 0 ; see if wave > thresh, borrow bit high MOVWF band0_thresh ; if wave is > thresh, set thresh to wave ;; threshold checking RRF band0_thresh, W ;; take half of threshold MOVWF sum_temp1 ;; save in register ;;RRF sum_temp1 ;; shift again if want 7/8th RRF sum_temp1, W ;; get quarter of threshold ANDLW 0x3f ;; clear out bogus rotate bits (use 0x1f for 7/8ths) SUBWF band0_thresh, W ;; threshold - 1/4 thresh = 3/4 threshold ADDLW 1 ;; add 10% to half of threshold just to make nonzero SUBWF sum_temp0, W ;; take difference ;;BCF audio_port, 4 ;; clear strobe 0 BTFSC STATUS, 0 ;; see if wave > 0.5*thresh BSF audio_port, 4 ;; set bit 0 if > half threshold ;; Now that we've averaged the first audio band, we need to see ;; if we need to process more audio bands DECFSZ band0_ctr, F ; decrement band counter by one GOTO proc_extra ;; if not zero, go and see if we need to process bands MOVLW 0x8; MOVWF band0_ctr ;; reset counter to 8 for next time ;; we've now processed 8 samples and we need to process bands 1,2,3 ;; after every 8, always process band 1... process_band1 ;; put average from band0 into ring buffer INCF band1_bufptr,W ;; bufptr+1 -> W ANDLW 0x7 ;; truncate to 0-7 MOVWF band1_bufptr ;; put back in pointer reg ADDLW band1_buf0 ;; add offset to band0 circ buffer MOVWF FSR ;; put in indirect address register MOVF band0_avg, W ;; get band0 avg MOVWF INDF ;; put into address at FSR ptr ; add up first circular buffer MOVF band1_buf0,W ;; get value 0 ADDWF band1_buf1,W ;; add in value1 MOVWF sum_temp0 ;; store partial sum in temp0 MOVF band1_buf2,W ADDWF band1_buf3,W MOVWF sum_temp1 MOVF band1_buf4,W ADDWF band1_buf5,W MOVWF sum_temp2 MOVF band1_buf6,W ADDWF band1_buf7,W MOVWF sum_temp3 RRF sum_temp0, F ;; right shift temp0 BCF STATUS, 0 ;; clear C bit to prevent rotation errors RRF sum_temp1, W ;; right shift temp1 into W ADDWF sum_temp0, F ;; add W to temp0 and put back in temp0 RRF sum_temp2, F ;; right shift temp2 BCF STATUS, 0 ;; clear C bit to prevent rotation errors RRF sum_temp3, W ;; right shift temp3 into W ADDWF sum_temp2, F ;; add W to temp2 and put back in temp2 RRF sum_temp0, F ;; right shift temp0 BCF STATUS, 0 ;; clear C bit to prevent rotation errors RRF sum_temp2, W ;; right shift temp2 into W ADDWF sum_temp0, F ;; add W to temp0 and put back in temp0 ;; temp0 now has sum of 8 A2D values right shifted by 3 RRF sum_temp0, F ; halve the average MOVF sum_temp0, W ;; get the halved average MOVWF band1_avg ; save in register for next stage MOVF band0_avg, W ; halve the input signal into W SUBWF sum_temp0, F ; subtract input from average MOVFW sum_temp0 ;; get wave into W BTFSC sum_temp0, 7 ; test top bit for negative value SUBLW 0 ;; do 0-W if negative MOVWF sum_temp0 ;; move W back, possibly subtracted from zero ;; sum_temp0 now contains a value between 0-127 with ;; absolute waveform that we want MOVF band1_thresh, W ;; get current threshold SUBWF sum_temp0, W ;; waveform-thresh -> W, set carry bit MOVF sum_temp0, W ;; get wave in W, don't touch carry bit MOVWF band1_wave ;; save waveform for debugging BTFSC STATUS, 0 ; see if wave > thresh, borrow bit high MOVWF band1_thresh ; if wave is > thresh, set thresh to wave ;; threshold checking RRF band1_thresh, W ;; take half of threshold MOVWF sum_temp1 ;; save in register ;;RRF sum_temp1 ;; shift again if want 7/8th RRF sum_temp1, W ;; get quarter of threshold ANDLW 0x3f ;; clear out bogus rotate bits (use 0x1f for 7/8ths) SUBWF band1_thresh, W ;; threshold - 1/4 thresh = 3/4 threshold ADDLW 1 ;; add 10% to half of threshold just to make nonzero SUBWF sum_temp0, W ;; take difference ;;BCF audio_port, 5 ;; clear strobe 1 BTFSC STATUS, 0 ;; see if wave > 0.5*thresh BSF audio_port, 5 ;; set bit 1 if > half threshold ;; done processing, check 8shot counter DECFSZ band1_ctr, F ; decrement band1 counter GOTO loop_done ;; if not zero we're done MOVLW 0x8 MOVWF band1_ctr; reset to 8 BSF band_flags,0 ;; tell band 2 that we're ready to process GOTO loop_done ;; here we see if any band_flags are set to process for bands 2 and 3 proc_extra BTFSC band_flags,0 ;; test bit 0 for band 2 GOTO process_band2 ;; process band2 if bit 0 is set BTFSC band_flags,1 ;; test bit 1 for band 3 GOTO process_filt ;; process filters if bit 1 is set BTFSC band_flags,2 ;; test bit 1 for band 3 GOTO process_thresholds ;; process thresholds if bit 2 is set GOTO loop_done ;; no bands to process, done process_band2 ;; put average from band1 into ring buffer INCF band2_bufptr,W ;; bufptr+1 -> W ANDLW 0x7 ;; truncate to 0-7 MOVWF band2_bufptr ;; put back in pointer reg ADDLW band2_buf0 ;; add offset to band0 circ buffer MOVWF FSR ;; put in indirect address register MOVF band1_avg, W ;; get band1 avg MOVWF INDF ;; put into address at FSR ptr ; add up first circular buffer MOVF band2_buf0,W ;; get value 0 ADDWF band2_buf1,W ;; add in value1 MOVWF sum_temp0 ;; store partial sum in temp0 MOVF band2_buf2,W ADDWF band2_buf3,W MOVWF sum_temp1 MOVF band2_buf4,W ADDWF band2_buf5,W MOVWF sum_temp2 MOVF band2_buf6,W ADDWF band2_buf7,W MOVWF sum_temp3 RRF sum_temp0, F ;; right shift temp0 BCF STATUS, 0 ;; clear C bit to prevent rotation errors RRF sum_temp1, W ;; right shift temp1 into W ADDWF sum_temp0, F ;; add W to temp0 and put back in temp0 RRF sum_temp2, F ;; right shift temp2 BCF STATUS, 0 ;; clear C bit to prevent rotation errors RRF sum_temp3, W ;; right shift temp3 into W ADDWF sum_temp2, F ;; add W to temp2 and put back in temp2 RRF sum_temp0, F ;; right shift temp0 BCF STATUS, 0 ;; clear C bit to prevent rotation errors RRF sum_temp2, W ;; right shift temp2 into W ADDWF sum_temp0, F ;; add W to temp0 and put back in temp0 ;; temp0 now has sum of 8 A2D values right shifted by 3 RRF sum_temp0, F ; halve the average MOVF sum_temp0, W ;; get the halved average MOVWF band2_avg ; save in register for next stage MOVF band1_avg, W ; halve the input signal into W SUBWF sum_temp0, F ; subtract input from average MOVFW sum_temp0 ;; get wave into W BTFSC sum_temp0, 7 ; test top bit for negative value SUBLW 0 ;; do 0-W if negative MOVWF sum_temp0 ;; move W back, possibly subtracted from zero ;; sum_temp0 now contains a value between 0-127 with ;; absolute waveform that we want MOVF band2_thresh, W ;; get current threshold SUBWF sum_temp0, W ;; waveform-thresh -> W, set carry bit MOVF sum_temp0, W ;; get wave in W, don't touch carry bit MOVWF band2_wave ;; save waveform for debugging BTFSC STATUS, 0 ; see if wave > thresh, borrow bit high MOVWF band2_thresh ; if wave is > thresh, set thresh to wave ;; threshold checking RRF band2_thresh, W ;; take half of threshold MOVWF sum_temp1 ;; save in register ;;RRF sum_temp1 ;; shift again if want 7/8th RRF sum_temp1, W ;; get quarter of threshold ANDLW 0x3f ;; clear out bogus rotate bits (use 0x1f for 7/8ths) SUBWF band2_thresh, W ;; threshold - 1/4 thresh = 3/4 threshold ADDLW 1 ;; add 10% to half of threshold just to make nonzero SUBWF sum_temp0, W ;; take difference ;;BCF audio_port, 6 ;; clear strobe 2 BTFSC STATUS, 0 ;; see if wave > 0.5*thresh BSF audio_port, 6 ;; set bit 2 if > half threshold BCF band_flags,0 ;; done processing so clear bit DECFSZ band2_ctr, F ; decrement band2 counter GOTO loop_done ;; if not zero we're done MOVLW 0x8 MOVWF band2_ctr; reset to 8 BSF band_flags,1 ;; tell band 3 that we're ready to process GOTO loop_done process_filt ;; this process gets called 60 times a second ;; we'll take the audio_port bits set by the bands and error checks ;; and filter them so that they more easily trigger the zero-crossing ;; output optocouplers ;; We do this by storing the last four values in a circular buffer ;; and ORing all them together. If any of the last four values is 1 ;; we turn the opto-LED on, guaranteeing an "on" time of 1/15 second ;; which should be easy to see for the user. ;; This might be too long and we might have to trim this down to 1/30 second ;; put the last trigger values into 4-deep ring buffer INCF filt_bufptr,W ;; bufptr+1 -> W ANDLW 0x1 ;; truncate to 0-1 MOVWF filt_bufptr ;; put back in pointer reg ADDLW filt_buf0 ;; add offset to band0 circ buffer MOVWF FSR ;; put in indirect address register MOVF audio_port, W ;; get switch values MOVWF INDF ;; put into address at FSR ptr ; OR together circular buffer MOVF filt_buf0, W ;; get first value IORWF filt_buf1, W ;; OR with second value ;;IORWF filt_buf2, W ;; OR with third value ;;IORWF filt_buf3, W ;; OR with fourth value MOVWF PORTB ;; send out port to opto LEDs CLRF audio_port ;; clear bits for next time around BCF band_flags,1 ;; done processing so clear bit DECFSZ filt_ctr, F ; decrement filter counter GOTO loop_done ;; if not zero we're done MOVLW 0x2 MOVWF filt_ctr; reset to 2 so process_thresholds gets run 30x second BSF band_flags,2 ;; tell thresholds that we're ready to process GOTO loop_done process_thresholds ;; this gets processed every 1024 samples (~ 1/30 second) ;; we need to decrement thresholds slowly to allow them ;; to decay over time (2.5 seconds for full decay) MOVF band0_thresh, W ;; get band0 threshold into W BTFSS STATUS, 2 ;; check zero bit DECF band0_thresh, F ;; decrement if not zero MOVF band1_thresh, W ;; get band0 threshold into W BTFSS STATUS, 2 ;; check zero bit DECF band1_thresh, F ;; decrement if not zero MOVF band2_thresh, W ;; get band0 threshold into W BTFSS STATUS, 2 ;; check zero bit DECF band2_thresh, F ;; decrement if not zero BCF band_flags,2 ;; done processing, so clear bit ;; GOTO loop_done ;; redundant command ;; we're now all done with processing and we want to grab ;; the next A2D conversion and go back again loop_done ;;; DEBUG PWM START ;;MOVF PORTB, W ; get switches from port B, bits 0123 ;;ANDLW 0xF ; clear all bits but bottom 3, W now has switches value ;;ADDLW band0_wave ;; get offset into table ;;MOVWF FSR ;;MOVF INDF, W ;; get waveform value (0-127 range) ;;MOVWF CCPR1L ;; store 0-127 in PWM output ;;; DEBUG PWM END ;; error checking to see if we ran too many instructions before A2D done ;; BTFSS ADCON0,2 ;; test DONE bit in A2D and skip next instr if no error ;; BSF audio_port, 7 ;; set error flag if too many instructions check_a2d BTFSC ADCON0,2 ;; test DONE bit in A2D converter GOTO check_a2d ;; A2D still converting ;; check A2D value for 1 or 0 to see if close to saturation (overload) MOVF ADRESH, W ;; get a2d value into W ANDLW 0xfe ;; AND a2d value with 0xfe, set Z status bit BTFSC STATUS, Z ;; check zero status, skip if not zero BSF audio_port,7 ;; set error bit 7 if saturated GOTO mainloop ;; A2D done, go back and go again END ; directive 'end of program'