;************************************************************************************************************************************ ; ;PURPOSE: Adjustable Audio Delay ;PROCESSOR: PIC 16F88 ;CLOCK SOURCE: 20 MHz external oscillator ;************************************************************************************************************************************ ;************************************************************************************************************************************ ;PROCESSOR DECLARATIONS, INCLUDE FILES, CONFIG WORD SETUP ;************************************************************************************************************************************ LIST P=PIC16F88 ; list directive to define processor #INCLUDE ; processor specific variable definitions errorlevel -302 ; suppress message 302 from list file __CONFIG _CONFIG1, _CP_OFF & _CCP1_RB0 & _DEBUG_OFF & _WRT_PROTECT_OFF & _CPD_OFF & _LVP_OFF & _BODEN_OFF & _MCLR_OFF & _PWRTE_ON & _WDT_OFF & _EXTCLK __CONFIG _CONFIG2, _IESO_OFF & _FCMEN_OFF ;************************************************************************************************************************************ ;END OF PROCESSOR DECLARATIONS ;************************************************************************************************************************************ ;************************************************************************************************************************************ ;RAM VARIABLES SETUP ;************************************************************************************************************************************ cblock H'020' VAR_COUNT1 ;Counter variables VAR_COUNT2 VAR_COUNT3 VAR_FLAGS ;register used to store different settings during program operation VAR_DELAY_RANGE ;Variable used to store a value based on the state of the delay range;input bits (RB7, RB6, RB5) VAR_RAMP_WAVE_VAL VAR_CURRENT_ADDR_H ;Address where the current sample is to be stored VAR_CURRENT_ADDR_L VAR_CURRENT_SAMPLE ;8 bit current sample read from the audio input AN1 VAR_DELAY_VAL_H ;Delay value read from analog input. This is the amout of dealy between the input and VAR_DELAY_VAL_L ;output waveforms. This value will be used along with the settings of the ;delay range input bits to create a vaule that will be subtracted from ;Current address VAR_CURRENT_ADDR to determine the address of the stored sample to output VAR_DELAY_ADDR_H ;This is the address of the stored sample that is to be played back VAR_DELAY_ADDR_L VAR_DAC_LOWER_BYTE ;These are the processed bytes that will be transferred the DAC to output the sample VAR_DAC_UPPER_BYTE ;The 4 most significant bits of the upper byte configure the DAC, and the remaining bits ;represent the sample value VAR_TEXT_INDEX VAR_TABLE_OFFSET WAVE_CYCLE_COUNTER endc ;************************************************************************************************************************************ ;END OF RAM VARIBLES SETUP ;************************************************************************************************************************************ ;************************************************************************************************************************************ ;EQUATES ;************************************************************************************************************************************ ;This is the value to be loaded into timer 1 to create a 11025 sample rate PERIOD_1_H_11025_SA_P_S EQU H'FE' PERIOD_1_L_11025_SA_P_S EQU H'4E' ;This is the value to be loaded into timer 1 to create a 8000 sample rate PERIOD_1_H_8000_SA_P_S EQU H'FD' PERIOD_1_L_8000_SA_P_S EQU H'A3' ;This is the value to be loaded into timer 1 to create a 8000 sample rate, used only in the test mode for outputting the 1000Hz sine bursts PERIOD_1_H_20000_SA_P_S EQU H'FF' PERIOD_1_L_20000_SA_P_S EQU H'12' ;Number of cycles of sine wave per burst, used in test mode NUMBER_OF_WAVES EQU D'250' SAMPLES_PER_CYCLE EQU D'20' ;Number of samples used to represent the sine wave RAM_READ_COMMAND EQU b'00000011' ;Command to read from SPI RAM RAM_WRITE_COMMAND EQU b'00000010' ;Command to write SPI RAM RAM_WRITE_STATUS_REG_COMMAND EQU b'00000001' ;Command to write to the STATUS register of the SPI RAM DAC_WRITE_COMMAND_MASK EQU b'00110000' ;The mask OR'd with the value to output AUDIO_IN_SELECT EQU b'00010000' ;Used to set ADC to audio input (AN2) DELAY_IN_SELECT EQU b'00001000' ;Used to set ADC to audio input (AN1) ;**********IO Assignments ;*****PORTA ;RA0/AN0 DIGITAL INPUT UNUSED ;RA1/AN1 ANALOG INPUT Delay setting analog input. 0Volts = no delay, Vmax=Max delay ;RA2/AN2 ANALOG INPUT Audio analog input ;RA3/AN3 DIGITAL INPUT CS_RAM_1, Chip select for second RAM, if used. This line must be pulled high with a resistor to use the second RAM ;It should be pulled low with a resistor is the second RAM is not used. ;RA4/AN4 DIGITAL INPUT Test Mode*/Normal Mode Select Input ;RA5 DIGITAL INPUT Sample rate selection ;RA6 DIGITAL OUTPUT LED Output ;RA7 DIGITAL INPUT OSC1 External Clock Input ;*****PORTB ;RB0 DIGITAL OUTPUT CS_DAC Chip select for SPI DAC ;RB1 DIGITAL INPUT SDI SPI Serial Data Input ;RB2 DIGITAL OUTPUT SDO SPI Serial Data Output ;RB3 DIGITAL OUTPUT CS_RAM_0 Chip select for first SPI RAM ;RB4 DIGITAL OUTPUT SCK SPI Clock Output ;RB5 DIGITAL INPUT Delay Range Setting Input A ;RB6/AN5 DIGITAL INPUT Delay Range Setting Input B ;RB7/AN6 DIGITAL INPUT Delay Range Setting Input C MODE_SELECT EQU D'4' ;Digital input used to select test mode or delay mode LED_OUT EQU D'6' ;General purpose LED output RAM_CONFIG EQU D'0' ;RAM configuration bit in the VAR_FLAGS register used to indicate whether 1 or 2 RAM ;chips are in use ;1 = 2 x 32k RAM chips, 0 = 1 x 32k RAM chip SAMPLE_RATE_SEL EQU D'5' ;Sample rate select input CS_DAC EQU D'0' ;Chip select line for SPI DAC CS_RAM_0 EQU D'3' ;Chip select line for SPI RAM 0 CS_RAM_1 EQU D'3' ;Chip select for second SPI RAM 1 ;************************************************************************************************************************************ ;END OF EQUATES ;************************************************************************************************************************************ ORG H'000' GOTO PROGRAM_START ORG H'005' PROGRAM_START ;************************************************************************************************************************************ ;CONFIGURATION OF THE PROCESSOR ;************************************************************************************************************************************ BCF STATUS,RP1 BCF STATUS,RP0 ;Change to Bank0 ;Clear the ports CLRF PORTA CLRF PORTB BSF STATUS,RP0 ;Change to Bank1 ;Configure I/O ports as inputs or outputs MOVLW B'10111111' ;Configure PORTA I/O MOVWF TRISA MOVLW B'11100010' ;Configure PORTB I/O MOVWF TRISB ;Configure AN1 and AN2 as analog inputs, all others are digital I/O MOVLW b'00000110' MOVWF ANSEL ;Configure the A/D converter for leftt justified results, reference voltage of Vdd, MOVLW B'00000000' MOVWF ADCON1 BCF STATUS,RP0 ;Change to Bank0 ;Set A/D conversion clock to Fosc/32, and configure an analog input. ;Note: The conversion process takes 9 Tad, and Tad will be 1.6usec for an 20MHz oscillator and these settings, so the conversion ;time will be 15.4 usec or 77 instruction cycles. ;select ananlog channel 1, and turn the converter ON. MOVLW B'10001001' MOVWF ADCON0 ;Configure the SSP interface for SPI communication ;The SPI mode is 0,0, ;CKP = 0, CKE = 1, SMP =1 ;SPI Master mode, clock = OSC/4 BSF STATUS,RP0 ;Change to Bank1 MOVLW b'11000000' MOVWF SSPSTAT BCF STATUS,RP0 ;Change to Bank0 MOVLW b'00100000' MOVWF SSPCON MOVFW SSPBUF ;Read the SPI Buffer to begin ;************************************************************************************************************************************ ;END CONFIGURATION OF THE PROCESSOR ;************************************************************************************************************************************ BCF VAR_FLAGS,RAM_CONFIG ;Clear the RAM_CONFIG flag to begin with ;Read input RA3 to determine if a second RAM is to be used BTFSS PORTA,CS_RAM_1 GOTO CONFIGURE_RAM_STATUS_REGS BSF STATUS,RP0 ;Change to Bank1 BCF TRISA,CS_RAM_1 ;Set CS_RAM_1 as an output, for use as the chip select for the second RAM BCF STATUS,RP0 ;Change to Bank0 BSF VAR_FLAGS,RAM_CONFIG ;Set the RAM_CONFIG flag to indicate that two RAM chips are in use CONFIGURE_RAM_STATUS_REGS ;Set all chip selects high BSF PORTB,CS_DAC BSF PORTB,CS_RAM_0 BSF PORTB,CS_RAM_1 CALL SUB_DELAY_1S ;Configure the STATUS register of the first SPI RAM BCF PORTB,CS_RAM_0 ;Drop the chip select line for the RAM low MOVLW RAM_WRITE_STATUS_REG_COMMAND CALL SUB_SPI_TRANSFER MOVLW b'00000001' ;Set up for byte mode, disable HOLD function CALL SUB_SPI_TRANSFER BSF PORTB,CS_RAM_0 ;Set the chip select line for the RAM high at the end of the read ;Configure the STATUS register of the second SPI RAM BCF PORTA,CS_RAM_1 ;Drop the chip select line for the RAM low MOVLW RAM_WRITE_STATUS_REG_COMMAND CALL SUB_SPI_TRANSFER MOVLW b'00000001' ;Set up for byte mode, disable HOLD function CALL SUB_SPI_TRANSFER BSF PORTA,CS_RAM_1 ;Set the chip select line for the RAM high at the end of the read ;Check the mode pin to determine if the program should run in normal mode or test mode BTFSS PORTA,MODE_SELECT GOTO TEST_MODE GOTO MAIN_PROGRAM_LOOP TEST_MODE ;***Test Mode ;RAM0 Test ;Write a value to RAM 0 BCF PORTB,CS_RAM_0 ;Drop the chip select line for the RAM low MOVLW RAM_WRITE_COMMAND ;Transfer the read command CALL SUB_SPI_TRANSFER MOVLW H'00' ;Transfer the high byte of the address CALL SUB_SPI_TRANSFER MOVLW H'00' ;Transfer the high byte of the address CALL SUB_SPI_TRANSFER MOVLW H'55' ;55 is the value that will be stored to RAM CALL SUB_SPI_TRANSFER BSF PORTB,CS_RAM_0 ;Set the chip select line for the RAM high ; Delay CALL SUB_DELAY_500_MICROSEC ;Read value from RAM ;***Read RAM via SPI interface BCF PORTB,CS_RAM_0 ;Drop the chip select line for the RAM low MOVLW RAM_READ_COMMAND ;Transfer the read command CALL SUB_SPI_TRANSFER MOVLW H'00' ;Transfer the high byte of the address CALL SUB_SPI_TRANSFER MOVLW H'00' ;Transfer the high byte of the address CALL SUB_SPI_TRANSFER MOVLW H'FF' ;Transfer a byte so that the sample value is returned from the RAM CALL SUB_SPI_TRANSFER BSF PORTB,CS_RAM_0 ;Set the chip select line for the RAM high at the end of the read ;Check to see if the value read matches the value written SUBLW H'55' BTFSS STATUS,Z GOTO RAM0_TEST_FAIL ;If the value matches, turn the LED on for 2 seconds. BSF PORTA, LED_OUT CALL SUB_DELAY_1S CALL SUB_DELAY_1S BCF PORTA, LED_OUT GOTO RAM1_TEST RAM0_TEST_FAIL ;If the value does not match, blink the LED 3 times CALL SUB_BLINK_LED CALL SUB_BLINK_LED CALL SUB_BLINK_LED RAM1_TEST ;If the second RAM is used, then run the test on that RAM as well. Otherwise, skip over this test. ;Wait 2 seconds from the first RAM test. CALL SUB_DELAY_1S CALL SUB_DELAY_1S ;Read the RAM_CONFIG bit to see if the second RAM is used BTFSS VAR_FLAGS,RAM_CONFIG GOTO DAC_TEST ;Write a value to RAM 1 BCF PORTA,CS_RAM_1 ;Drop the chip select line for the RAM low MOVLW RAM_WRITE_COMMAND ;Transfer the read command CALL SUB_SPI_TRANSFER MOVLW H'00' ;Transfer the high byte of the address CALL SUB_SPI_TRANSFER MOVLW H'00' ;Transfer the high byte of the address CALL SUB_SPI_TRANSFER MOVLW H'55' ;55 is the value that will be stored to RAM CALL SUB_SPI_TRANSFER BSF PORTA,CS_RAM_1 ;Set the chip select line for the RAM high ; Delay CALL SUB_DELAY_500_MICROSEC ;Read value from RAM ;***Read RAM via SPI interface BCF PORTA,CS_RAM_1 ;Drop the chip select line for the RAM low MOVLW RAM_READ_COMMAND ;Transfer the read command CALL SUB_SPI_TRANSFER MOVLW H'00' ;Transfer the high byte of the address CALL SUB_SPI_TRANSFER MOVLW H'00' ;Transfer the high byte of the address CALL SUB_SPI_TRANSFER MOVLW H'FF' ;Transfer a byte so that the sample value is returned from the RAM CALL SUB_SPI_TRANSFER BSF PORTA,CS_RAM_1 ;Set the chip select line for the RAM high at the end of the read ;Check to see if the value read matches the value written SUBLW H'55' BTFSS STATUS,Z GOTO RAM1_TEST_FAIL ;If the value matches, turn the LED on for 2 seconds. BSF PORTA, LED_OUT CALL SUB_DELAY_1S CALL SUB_DELAY_1S BCF PORTA, LED_OUT GOTO DAC_TEST RAM1_TEST_FAIL ;If the value does not match, blink the LED 3 times CALL SUB_BLINK_LED CALL SUB_BLINK_LED CALL SUB_BLINK_LED DAC_TEST ;Set the DAC output to ~ 1/3 full scale ;***Output sample to DAC via SPI BCF PORTB,CS_DAC ;Drop the chip select line for the DAC low MOVLW H'35' CALL SUB_SPI_TRANSFER MOVLW H'50' CALL SUB_SPI_TRANSFER BSF PORTB,CS_DAC ;Set the chip select line for the DAC high CALL SUB_DELAY_1S CALL SUB_DELAY_1S CALL SUB_DELAY_1S ;Set the DAC to output to ~ 2/3 full scale ;***Output sample to DAC via SPI BCF PORTB,CS_DAC ;Drop the chip select line for the DAC low MOVLW H'3A' CALL SUB_SPI_TRANSFER MOVLW H'A0' CALL SUB_SPI_TRANSFER BSF PORTB,CS_DAC ;Set the chip select line for the DAC high CALL SUB_DELAY_1S CALL SUB_DELAY_1S CALL SUB_DELAY_1S ;Set the DAC to output to full scale ;***Output sample to DAC via SPI BCF PORTB,CS_DAC ;Drop the chip select line for the DAC low MOVLW H'3F' CALL SUB_SPI_TRANSFER MOVLW H'F0' CALL SUB_SPI_TRANSFER BSF PORTB,CS_DAC ;Set the chip select line for the DAC high ;Wait 3 seconds before next part of the test CALL SUB_DELAY_1S CALL SUB_DELAY_1S CALL SUB_DELAY_1S ;**********Output 0.25sec bursts of 1000Hz Sine wave SINE_WAVE_BURST CLRF VAR_TEXT_INDEX MOVLW NUMBER_OF_WAVES MOVWF WAVE_CYCLE_COUNTER SINE_WAVE ;Configure and start Timer 1. This will be used to set the sample rate. MOVLW B'00000000' ;Turn timer1 OFF, prescaler=1:1, use system clock. Bits 5 and 4 set the prescaler MOVWF T1CON ;Make sure the Timer 1 overflow flag is cleared to begin BCF PIR1,TMR1IF ;Load the interval time values into TMR1 high and low bytes MOVLW PERIOD_1_H_20000_SA_P_S MOVWF TMR1H MOVLW PERIOD_1_L_20000_SA_P_S MOVWF TMR1L ;Turn on timer 1 by setting the TMR1ON bit in T1CON BSF T1CON,TMR1ON ;Look up the value from the table CALL SUB_SINE_SAMPLE_LOOKUP MOVWF VAR_DAC_LOWER_BYTE ;Process the sample to output. The upper four bits of the first byte transferred to the DAC are the command parameters. ;The remaining bits are the sample. CLRF VAR_DAC_UPPER_BYTE ;First, shift the 8 bit sample four bits to the left BCF STATUS,C RLF VAR_DAC_LOWER_BYTE,f RLF VAR_DAC_UPPER_BYTE,f BCF STATUS,C RLF VAR_DAC_LOWER_BYTE,f RLF VAR_DAC_UPPER_BYTE,f BCF STATUS,C RLF VAR_DAC_LOWER_BYTE,f RLF VAR_DAC_UPPER_BYTE,f BCF STATUS,C RLF VAR_DAC_LOWER_BYTE,f RLF VAR_DAC_UPPER_BYTE,f ;Now OR the first byte with the command mask MOVFW VAR_DAC_UPPER_BYTE IORLW DAC_WRITE_COMMAND_MASK MOVWF VAR_DAC_UPPER_BYTE ;***Output sample to DAC via SPI BCF PORTB,CS_DAC ;Drop the chip select line for the DAC low MOVFW VAR_DAC_UPPER_BYTE CALL SUB_SPI_TRANSFER MOVFW VAR_DAC_LOWER_BYTE CALL SUB_SPI_TRANSFER BSF PORTB,CS_DAC ;Set the chip select line for the DAC high ;Increment lookup index INCF VAR_TEXT_INDEX,f MOVFW VAR_TEXT_INDEX SUBLW SAMPLES_PER_CYCLE BTFSS STATUS,Z GOTO WAIT_FOR_PERIOD_COMPLETE_0 ;Check to see if the number of waves has counted down to zero CLRF VAR_TEXT_INDEX DECFSZ WAVE_CYCLE_COUNTER,f GOTO WAIT_FOR_PERIOD_COMPLETE_0 GOTO SINE_BURST_COMPLETE ;Poll the Timer 1 interrupt flag to determine when the counter is overflowed. WAIT_FOR_PERIOD_COMPLETE_0 BTFSS PIR1,TMR1IF GOTO WAIT_FOR_PERIOD_COMPLETE_0 GOTO SINE_WAVE SINE_BURST_COMPLETE ;Delay 0.25 seconds before next burst CALL SUB_DELAY_125MS CALL SUB_DELAY_125MS ;Repeat forever GOTO SINE_WAVE_BURST ;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ;Main Program ;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ;***Set the current address and the delay value to zero to begin with. CLRF VAR_CURRENT_ADDR_H CLRF VAR_CURRENT_ADDR_L CLRF VAR_DELAY_VAL_H CLRF VAR_DELAY_VAL_L MAIN_PROGRAM_LOOP ;***Set the A/D to read AN1 (Audio signal input). ;Once the input is set to AN1, the acquisition time must pass before initiating and A/D convervsion. The acquisition time is the length ;of time for the sample capacitor in the A/D to fully charge to the voltage on the analog input. ;Other tasks will be performed during the acquisition time. MOVFW ADCON0 ANDLW b'11000111' IORLW AUDIO_IN_SELECT MOVWF ADCON0 ;***Configure and start Timer 1. This will be used to set the sample rate. MOVLW B'00000000' ;Turn timer1 OFF, prescaler=1:1, use system clock. Bits 5 and 4 set the prescaler MOVWF T1CON ;Make sure the Timer 1 overflow flag is cleared to begin BCF PIR1,TMR1IF ;Read the sample rate select input to determine whether the 11025 Sa/s or 8000 Sa/s rate should be used BTFSS PORTA,SAMPLE_RATE_SEL GOTO SAMPLE_RATE_8000 ;Load the interval time values into TMR1 high and low bytes for the 11025 Sa/s case MOVLW PERIOD_1_H_11025_SA_P_S MOVWF TMR1H MOVLW PERIOD_1_L_11025_SA_P_S MOVWF TMR1L GOTO START_TIMER1 SAMPLE_RATE_8000 ;Load the interval time values into TMR1 high and low bytes for the 8000 Sa/s case MOVLW PERIOD_1_H_8000_SA_P_S MOVWF TMR1H MOVLW PERIOD_1_L_8000_SA_P_S MOVWF TMR1L NOP START_TIMER1 ;Turn on timer 1 by setting the TMR1ON bit in T1CON BSF T1CON,TMR1ON ;***Calculate address of the sample to read from RAM. This will be the address of the delayed sample that will be output to the DAC ;Subtract the delay value from the current sample address to get the address of the sample to output. ;The delay value is the value that is determined from the delay analog input and the delay range setting inputs. This value is essentially ;how far back to go to retrieve the sample to output ;First, copy the current address into the delay address variables. MOVFW VAR_CURRENT_ADDR_L MOVWF VAR_DELAY_ADDR_L MOVFW VAR_CURRENT_ADDR_H MOVWF VAR_DELAY_ADDR_H ;Subtract the low bytes MOVFW VAR_DELAY_VAL_L SUBWF VAR_DELAY_ADDR_L,f BTFSS STATUS,C ;If a borrow occurs, decrement the high byte DECF VAR_DELAY_ADDR_H,f ;Subtract the high bytes MOVFW VAR_DELAY_VAL_H SUBWF VAR_DELAY_ADDR_H,f ;At this point, VAR_DELAY_ADDR will contain the adress of the delayed sample to play back ;***Read the sample to play from the RAM via SPI interface ;Check the RAM configuration bit in the flags register. This is a bit that indicates whether one or two RAM chips are used ;If the RAM configuration bit is clear (indicating only one 32k RAM chip is in use) then: ; Drive the chip select of RAM0 low ;If it is set(indicating two 32k RAM chips are in use) then: ; Check to see if bit 7 of VAR_DELAY_ADDR_H is a 1 ; If it is a 1, drive the chip select of RAM1 low. ; If it is a 0, drive the chip select of RAM0 low. ;This process is necessary to determine which of the two chips to read from to retrieve the necessary sample BTFSS VAR_FLAGS,RAM_CONFIG GOTO READ_RAM_SINGLE GOTO READ_RAM_DUAL ;If only a single RAM chip is in use or if reading from the lower 32k bytes: READ_RAM_SINGLE BCF PORTB,CS_RAM_0 ;Drop the chip select line for the RAM low GOTO READ_RAM ;If two RAM chips are in use, determine which to read from based on whether the address of the sample is in the upper or lower ;32k of address space comprised by the two SPI RAM chips. READ_RAM_DUAL BTFSS VAR_DELAY_ADDR_H,7 GOTO READ_RAM_SINGLE BCF PORTA,CS_RAM_1 ;Drop the chip select line for the RAM low READ_RAM MOVLW RAM_READ_COMMAND ;Transfer the read command CALL SUB_SPI_TRANSFER MOVFW VAR_DELAY_ADDR_H ;Transfer the high byte of the address of the sample CALL SUB_SPI_TRANSFER MOVFW VAR_DELAY_ADDR_L ;Transfer the low byte of the address of the sample CALL SUB_SPI_TRANSFER MOVLW H'FF' ;Transfer a byte so that the sample value is returned from the RAM CALL SUB_SPI_TRANSFER ;Set the chip select lines for the RAM chips high at the end of the read BSF PORTB,CS_RAM_0 BSF PORTA,CS_RAM_1 MOVWF VAR_DAC_LOWER_BYTE ;Store the byte that is received from the RAM. This value will be sent to the DAC ;***Increment RAM address pointer to point to the next memory location. This is where the next sample will be stored INCF VAR_CURRENT_ADDR_L,f ;Increment the low byte of the current address MOVF VAR_CURRENT_ADDR_L,f ;Check to see if the value rolled over to zero BTFSC STATUS,Z INCF VAR_CURRENT_ADDR_H,f ;If the low byte rolled over, increment the high byte ;By this time, the acquistion time for reading the Audio sample will have passed. ;Initiate Conversion of AN1 (Audio Sample) ;Start conversion by setting the GO/DONE bit in ADCON0 register BSF ADCON0,GO_DONE ;***Process the sample to output. The upper four bits of the first byte transferred to the DAC are the command parameters. ;The remaining bits are the sample value itself. Refer to the DAC datasheet for details. CLRF VAR_DAC_UPPER_BYTE ;First, shift the 8 bit sample four bits to the left BCF STATUS,C RLF VAR_DAC_LOWER_BYTE,f RLF VAR_DAC_UPPER_BYTE,f BCF STATUS,C RLF VAR_DAC_LOWER_BYTE,f RLF VAR_DAC_UPPER_BYTE,f BCF STATUS,C RLF VAR_DAC_LOWER_BYTE,f RLF VAR_DAC_UPPER_BYTE,f BCF STATUS,C RLF VAR_DAC_LOWER_BYTE,f RLF VAR_DAC_UPPER_BYTE,f ;Now OR the upper byte with the command mask. MOVFW VAR_DAC_UPPER_BYTE IORLW DAC_WRITE_COMMAND_MASK MOVWF VAR_DAC_UPPER_BYTE ;***Output write command/sample to DAC via SPI BCF PORTB,CS_DAC ;Drop the chip select line for the DAC low MOVFW VAR_DAC_UPPER_BYTE CALL SUB_SPI_TRANSFER MOVFW VAR_DAC_LOWER_BYTE CALL SUB_SPI_TRANSFER BSF PORTB,CS_DAC ;Set the chip select line for the DAC high ;Poll the go done bit. The conversion is complete when the bit is cleared POLL_GO_DONE_1 BTFSC ADCON0,GO_DONE GOTO POLL_GO_DONE_1 ;Read out the A/D value from ADRESH. MOVFW ADRESH MOVWF VAR_CURRENT_SAMPLE ;Store the current sample value, it will be transferred to the SPI RAM later ;***Set A/D to read AN2 (Delay Setting Input) ;***Other tasks will be performed during the acquisition time. ;Set the A/D converter to read the on time setting (AN2) MOVFW ADCON0 ANDLW b'11000111' IORLW DELAY_IN_SELECT MOVWF ADCON0 ;***Write the audio sample obtained to RAM via SPI interface ;Check the RAM configuration bit in the flags register. This is a bit that indicates whether one or two RAM chips are used ;If the RAM configuration bit is clear (indicating only one 32k RAM chip is in use) then: ; Drive the chip select of RAM0 low ;If it is set(indicating two 32k RAM chips are in use) then: ; Check to see if bit 7 of VAR_DELAY_ADDR_H is a 1 ; If it is a 1, drive the chip select of RAM1 low. ; If it is a 0, drive the chip select of RAM0 low. ;This process is necessary to determine which of the two chips to write to store the sample BTFSS VAR_FLAGS,RAM_CONFIG GOTO WRITE_RAM_SINGLE GOTO WRITE_RAM_DUAL WRITE_RAM_SINGLE BCF PORTB,CS_RAM_0 ;Drop the chip select line for the RAM low GOTO WRITE_RAM WRITE_RAM_DUAL BTFSS VAR_CURRENT_ADDR_H,7 GOTO WRITE_RAM_SINGLE BCF PORTA,CS_RAM_1 ;Drop the chip select line for the RAM low WRITE_RAM MOVLW RAM_WRITE_COMMAND ;Transfer the read command CALL SUB_SPI_TRANSFER MOVFW VAR_CURRENT_ADDR_H ;Transfer the high byte of the address of the sample CALL SUB_SPI_TRANSFER MOVFW VAR_CURRENT_ADDR_L ;Transfer the low byte of the address of the sample CALL SUB_SPI_TRANSFER MOVFW VAR_CURRENT_SAMPLE ;Transfer a byte so that the sample value is returned from the RAM CALL SUB_SPI_TRANSFER ;Set the chip select lines for the RAM chips high at the end of the write BSF PORTB,CS_RAM_0 BSF PORTA,CS_RAM_1 ;***Initiate Conversion of AN2 (delay setting) BSF ADCON0,GO_DONE ;Poll the go done bit. The conversion is complete when the bit is cleared POLL_GO_DONE_2 BTFSC ADCON0,GO_DONE GOTO POLL_GO_DONE_2 ;Read out the A/D value from ADRESH. MOVFW ADRESH ;Set up the VAR_DELAY value from the reading MOVWF VAR_DELAY_VAL_H ;The upper byte is the value read from the analog input CLRF VAR_DELAY_VAL_L ;Set the lower byte of the delay to zero ;***The delay value is now processed, based on the RAM configuration and the delay range inputs. ;The delay range inputs allow the maximum delay to be selected. ;If only one RAM chips is used, add a shift so that the delay value does not exceed the 32k address range BTFSC VAR_FLAGS,RAM_CONFIG GOTO READ_RANGE_SETTING BCF STATUS,C RRF VAR_DELAY_VAL_H,f RRF VAR_DELAY_VAL_L,f ;The delay range select inputs are three digital inputs that allow a total of 8 different ranges of delay to be selected. ;The max delay is determined by right shifting the delay value read from the delay analog input. Every right shift divides ;the max range by 2 ;The table below lists the full scale delay produced based on the delay range select inputs and the overall RAM size. ;Note that the max delay for a given setting of the delay range select inputs depends on whether one or two RAM chips are used. ;Full Scale Delay ;For Sample Rate = 11025 Hz ;Delay Range Input Settings 2 RAM chips 1 RAM chip ;RB7 RB6 RB5 64k bytes total 32k bytes total ;**************************************************************************** ;1 1 1 46.4 milliseconds 23.2 milliseconds ;1 1 0 92.9 milliseconds 46.4 milliseconds ;1 0 1 185.8 milliseconds 92.9 milliseconds ;1 0 0 371.5 milliseconds 185.8 milliseconds ;0 1 1 743.0 milliseconds 371.5 milliseconds ;0 1 0 1.486 seconds 0.743 seconds ;0 0 1 2.972 seconds 1.486 seconds ;0 0 0 5.944 seconds 2.972 seconds READ_RANGE_SETTING ;Read the delay range input bits (RB7, RB6, and RB5). This controls how much delay is introduced MOVFW PORTB MOVWF VAR_DELAY_RANGE BCF STATUS,C RRF VAR_DELAY_RANGE,f SWAPF VAR_DELAY_RANGE,f MOVLW H'0F' ANDWF VAR_DELAY_RANGE,f ;Test the value in VAR_DELAY_RANGE, and branch to the appropriate code based on the delay range input. MOVLW D'7' SUBWF VAR_DELAY_RANGE,w BTFSC STATUS,Z GOTO RANGE_7 MOVLW D'6' SUBWF VAR_DELAY_RANGE,w BTFSC STATUS,Z GOTO RANGE_6 MOVLW D'5' SUBWF VAR_DELAY_RANGE,w BTFSC STATUS,Z GOTO RANGE_5 MOVLW D'4' SUBWF VAR_DELAY_RANGE,w BTFSC STATUS,Z GOTO RANGE_4 MOVLW D'3' SUBWF VAR_DELAY_RANGE,w BTFSC STATUS,Z GOTO RANGE_3 MOVLW D'2' SUBWF VAR_DELAY_RANGE,w BTFSC STATUS,Z GOTO RANGE_2 MOVLW D'1' SUBWF VAR_DELAY_RANGE,w BTFSC STATUS,Z GOTO RANGE_1 GOTO RANGE_0 RANGE_7 BCF STATUS,C RRF VAR_DELAY_VAL_H,f RRF VAR_DELAY_VAL_L,f RANGE_6 BCF STATUS,C RRF VAR_DELAY_VAL_H,f RRF VAR_DELAY_VAL_L,f RANGE_5 BCF STATUS,C RRF VAR_DELAY_VAL_H,f RRF VAR_DELAY_VAL_L,f RANGE_4 BCF STATUS,C RRF VAR_DELAY_VAL_H,f RRF VAR_DELAY_VAL_L,f RANGE_3 BCF STATUS,C RRF VAR_DELAY_VAL_H,f RRF VAR_DELAY_VAL_L,f RANGE_2 BCF STATUS,C RRF VAR_DELAY_VAL_H,f RRF VAR_DELAY_VAL_L,f RANGE_1 BCF STATUS,C RRF VAR_DELAY_VAL_H,f RRF VAR_DELAY_VAL_L,f RANGE_0 ;Poll the Timer 1 interrupt flag to determine when the counter is overflowed. WAIT_FOR_PERIOD_COMPLETE_1 BTFSS PIR1,TMR1IF GOTO WAIT_FOR_PERIOD_COMPLETE_1 GOTO MAIN_PROGRAM_LOOP ;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ;END of Main Program ;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ;BEGINNING OF SUBROUTINE CODE ;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ;************************************************************************************************************************************ ; Subroutine SUB_SPI_TRANSFER ;Total execution time is approximately 18 instruction cycles, including call and return ;************************************************************************************************************************************ SUB_SPI_TRANSFER MOVWF SSPBUF ;Write the byte to SSPBUF. The byte to transfer is passed to this subroutine via the w register. BSF STATUS,RP0 ;Change to Bank1 TRANFER_BYTE BTFSS SSPSTAT,BF ;Poll the buffer full flag to determine when transfer is complete GOTO TRANFER_BYTE BCF STATUS,RP0 ;Change to Bank0 MOVFW SSPBUF ;Read the byte into the w register. The SSPBUF register has to be read, even if the byte is not ;used RETURN ;Return from SUB_SPI_TRANSFER ;************************************************************************************************************************************ ; END of Subroutine SUB_SPI_TRANSFER ;************************************************************************************************************************************ ;************************************************************************************************************************************ ; Subroutine SUB_DELAY_500_MICROSEC ;Calling this routine produces a 500 microsecond delay (20MHz Oscillator/ 5MHz Intruction Cycle Rate) ;************************************************************************************************************************************ SUB_DELAY_500_MICROSEC movlw D'249' movwf VAR_COUNT1 LOOP1 nop nop nop nop nop nop nop decfsz VAR_COUNT1,f goto LOOP1 nop nop nop nop nop return ;Return from SUB_DELAY_500_MICROSEC ;************************************************************************************************************************************ ; END of Subroutine SUB_DELAY_500_MICROSEC ;************************************************************************************************************************************ ;************************************************************************************************************************************ ;Subroutine SUB_DELAY_125MS ;Calling this routine produces an approximatley 125 millisecond delay (20MHz Oscillator/ 5MHz Intruction Cycle Rate) ;************************************************************************************************************************************ SUB_DELAY_125MS movlw D'250' movwf VAR_COUNT2 LOOP2 call SUB_DELAY_500_MICROSEC decfsz VAR_COUNT2,f goto LOOP2 return ;Return from SUB_DELAY_125MS ;************************************************************************************************************************************ ; END of Subroutine SUB_DELAY_125MS ;************************************************************************************************************************************* ;************************************************************************************************************************************ ;Subroutine SUB_DELAY_1S ;Calling this routine produces an approximatley 1 second delay (20MHz Oscillator/ 5MHz Intruction Cycle Rate) ;************************************************************************************************************************************ SUB_DELAY_1S movlw D'8' movwf VAR_COUNT3 LOOP3 call SUB_DELAY_125MS decfsz VAR_COUNT3,f goto LOOP3 return ;Return from SUB_DELAY_1S ;************************************************************************************************************************************ ; END of Subroutine SUB_DELAY_1S ;************************************************************************************************************************************* ;************************************************************************************************************************************ ;Subroutine SUB_BLINK_LED ;************************************************************************************************************************************ SUB_BLINK_LED BSF PORTA,LED_OUT ;Turn LED ON CALL SUB_DELAY_125MS ;Delay 250ms CALL SUB_DELAY_125MS ;Delay 250ms BCF PORTA,LED_OUT ;Turn LED ON CALL SUB_DELAY_125MS ;Delay 250ms CALL SUB_DELAY_125MS ;Delay 250ms return ;Return from SUB_BLINK_LED ;************************************************************************************************************************************ ; END of Subroutine SUB_BLINK_LED ;************************************************************************************************************************************* ;********************************************************************************************************************************** ;Subroutine SUB_SINE_SAMPLE_LOOKUP ;This subroutine is used to lookup values for representing a sine wave. Each value represents a duty cycle for the PWM generator. ;********************************************************************************************************************************** SUB_SINE_SAMPLE_LOOKUP MOVFW VAR_TEXT_INDEX MOVWF VAR_TABLE_OFFSET MOVLW LOW SUB_SINE_SAMPLE_LOOKUP_TABLE + 1 ADDWF VAR_TABLE_OFFSET,F MOVLW HIGH SUB_SINE_SAMPLE_LOOKUP_TABLE BTFSC STATUS,C ADDLW 1 MOVWF PCLATH MOVF VAR_TABLE_OFFSET,W CALL SUB_SINE_SAMPLE_LOOKUP_TABLE RETURN ;Return from SUB_SINE_SAMPLE_LOOKUP ;********************************************************************************************************************************** ; END of Subroutine SUB_SINE_SAMPLE_LOOKUP ;********************************************************************************************************************************** ;********************************************************************************************************************************** ;Subroutine SUB_SINE_SAMPLE_LOOKUP_TABLE ;This subroutine is called by subroutine SUB_SAMPLE_LOOKUP. The value will be in the ;W register when this subroutine returns. ;********************************************************************************************************************************** SUB_SINE_SAMPLE_LOOKUP_TABLE MOVWF PCL RETLW D'127' RETLW D'166' RETLW D'202' RETLW D'230' RETLW D'248' RETLW D'254' RETLW D'248' RETLW D'230' RETLW D'202' RETLW D'166' RETLW D'127' RETLW D'88' RETLW D'52' RETLW D'24' RETLW D'6' RETLW D'0' RETLW D'6' RETLW D'24' RETLW D'52' RETLW D'88' ;********************************************************************************************************************************** ; END of Subroutine SUB_SINE_SAMPLE_LOOKUP_TABLE ;********************************************************************************************************************************** ;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ;END OF SUBROUTINE CODE ;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX END