; mic.asm ; Microphone input test. This program tests the audio input of the Raven board by capturing ; input from a microphone connected to the audio input jack. ; ; The Raven board connectes the audio input to pin RE0 (when connector J4 jumpers pins 1 and 2). ; This program uses the analog input capabilities of the PIC to capture the audio signal and ; save the samples as 8 bit quantities. ; ; This program uses an ISR to handle the actual capture. Timer0 is used to set an ISR rate of ; 4000 Hz. Each ISR will start a conversion and then the main program will check the GO bit of ; ADCON and when complete, store the results of the sample. Each sample will be reduced to ; 8 bits and stored in the LIST P=16F877, R=DEC INCLUDE "P16F877.inc" ; __CONFIG _CP_OFF & _DEBUG_OFF & _WRT_ENABLE_OFF & _CPD_OFF & _LVP_OFF & _BODEN_OFF & _PWRTE_ON & _WDT_OFF & _HS_OSC20 ; Defines ; Far call macro farcall macro flabel LOCAL mask, cpage ; Create the mask for the page bits movlw HIGH $ & 0x18 movwf mask movlw HIGH flabel & 0x18 iorwf mask,F movf mask,W xorwf HIGH flabel,W movwf PCLATH call flabel & 0x07FF ; return comes here endm ; ---------------------------------------------------- ; 16 bit handling macros Add16 macro value,toval movf toval+1,W addwf value+1,W movwf toval+1 btfsc STATUS,C incf toval,F movf toval,W addwf value,W movwf toval endm Sub16 macro value,sub movf sub+1,W subwf value+1,F btfss STATUS,C decf value,F movf sub,W subwf value,F endm Mov16 macro value,toval movf value,W movwf toval movf value+1,W movwf toval+1 endm Clr16 macro value clrf value clrf value+1 endm Incf16 macro value movlw 1 addwf value+1,F btfsc STATUS,C incf value,F endm Cmp16 macro value1, value2 Mov16 value1,Temp16 Sub16 Temp16, value2 movf Temp16,W iorwf Temp16+1,W ; W is 0 or nonzero endm RAM EQU 0x20 ; I2C defines SDA EQU 4 ; PORT C bit for SDA SCL EQU 3 ; PORT C bit for SCL I2CRW EQU 2 ; R/_W bit in SSPSTAT RX EQU 7 TX EQU 6 READCTRL EQU b'10100001' WRITECTRL EQU b'10100000' ; Variables Temp EQU RAM+0 Counter EQU RAM+1 ADValHi EQU RAM+2 ADValLo EQU RAM+3 W_Temp EQU RAM+4 Stat_Temp EQU RAM+5 SampleCt EQU RAM+6 AddressHi EQU RAM+8 AddressLo EQU RAM+9 Value EQU RAM+10 NSample EQU RAM+11 Temp16 EQU RAM+13 ; Start of the Program ORG 3 goto Start ; ISR routine. Timer0 has expired. Start a conversion ORG 4 ; ---------------------------------------------------- ISR ; Save the current state movwf W_Temp swapf STATUS,W CLRF STATUS movwf Stat_Temp ; Reset everything for next cycle. bcf INTCON, T0IF movlw 178 movwf TMR0 ; Check to see if previous the conversion is done. If not, we have a problem because ; we are falling behind and the previous sample hasn't been properly converted. The ; problem is obvious - we need to convert faster or sample less often. In ; any case, nothing happens here - just start the next one which aborts the ; current one. btfsc ADCON0, GO nop ; do something to handle problem here ; Start another conversion bsf ADCON0, GO ; Restore the prior system state swapf Stat_Temp,W movwf STATUS swapf W_Temp,F swapf W_Temp,W retfie ; ---------------------------------------------------- ; Main program. Configure things and then collect the samples. ORG 0x20 Start bsf STATUS, RP0 clrf TRISA ; for output of value to led bar clrf TRISD bsf TRISE,0 ; input port bcf STATUS, RP0 call I2CConfig call ADConfig call Tmr0Config ; This loop starts the AD conversion process initially. bsf ADCON0,GO ; start the conversion ; Set up the initial sample count and the limiting value 0x3FFF = 16,384 Clr16 SampleCt movlw 0x3F movwf NSample movlw 0xFF movwf NSample+1 main_loop call ADConvert Cmp16 SampleCt,NSample andlw 0xFF btfsc STATUS,Z goto sample_done goto main_loop sample_done movlw 0xAA call DispLedBar nop goto $-1 ; ------------------------------------------------------------------- ; AD routines ADConfig ; Set the conversion clock rate to Fosc/32, enable RE0 and turn on A/D conversion movlw b'10101001' movwf ADCON0 ; Configure the pin behavior, left justified, Vref+=Vdd, Vref-=Vss bsf STATUS,RP0 movlw b'00001001' movwf ADCON1 bcf STATUS,RP0 ; Enable interrupts bsf INTCON,GIE bsf STATUS,RP0 bsf PIE1,ADIE bcf STATUS,RP0 return ADConvert ; Check to see if the conversion is done. btfss ADCON0, GO goto convert_done ; No, so do nothing. return convert_done ; Yes, get the value and store it. bsf STATUS, RP0 movf ADRESL, W bcf STATUS, RP0 movwf ADValLo movf ADRESH, W movwf ADValHi movf ADValHi,W call StoreSample Incf16 SampleCt movf SampleCt,W call DispLedBar return ; ----------------------------------------------- ; Timer routines ; Use 16 for the prescaler and 256-78 for the timer0 count. That ; gives an interrupt rate of something close to 4000 Hz. That is, ; a prescaler of 16 gives about 3.2 usec per tick, we want about ; 0.25 ms per sample, so 0.25ms/3.2usec = 78.125, or ; 5e06/16/4000 ~ 78 Tmr0Config ; Set up prescaler - everything else is default bsf STATUS, RP0 bcf OPTION_REG,T0CS ; internal clock bcf OPTION_REG,PSA ; prescaler to Timer 0 movlw 0xF8 ; set the PSA bits for prescaler = 16 andwf OPTION_REG,F movlw 0x03 iorwf OPTION_REG,F bcf STATUS, RP0 ; Set the count for the desired delay movlw 178 movwf TMR0 ; Now we're counting ; Enable the interrupts bsf INTCON,GIE bsf INTCON,T0IE return ; ----------------------------------------------- ; Sample access routines - using the EEPROM for storage StoreSample Mov16 SampleCt,AddressHi movf ADValHi,W movwf Value call WriteEEPROM return FetchSample Mov16 SampleCt,AddressHi call ReadEEPROM movf Value,W return ; ----------------------------------------------- ; Output a value to the LED bar DispLedBar ; Enable the led bar and put the value in W there. movwf Temp ; save W movlw 0x12 movwf PORTA comf Temp,W movwf PORTD return ; -------------------------------------------------------------- ; I2C EEPROM routines WriteEEPROM call I2CStart ; Send the control byte movlw WRITECTRL call I2CSend ; Send the address high order movf AddressHi,W call I2CSend ; Send the address low order movf AddressLo,W call I2CSend ; Send the data movf Value,W call I2CSend ; Halt the command call I2CStop return ReadEEPROM call I2CStart ; Send the control byte movlw WRITECTRL call I2CSend ; Send the address high order movf AddressHi,W call I2CSend ; Send the address low order movf AddressLo,W call I2CSend ; Restart for read of data call I2CRestart ; Send the read control byte movlw READCTRL call I2CSend ; and read the data call I2CReceive movwf Value ; Stop the command call I2CStop return ;---------------------------------------------------------------------------------- ; I2C control routines I2CConfig ; movlw b'00111011' ; enable SSP and set mode to Master/Slave Idle movlw b'00111000' ; to be used when SPADD is used to set the speed movwf SSPCON bsf STATUS, RP0 ; the following three or do none of these and accept the defaults. ; clrf SSPCON2 ; bcf SSPSTAT, SMP ; bcf SSPSTAT, CKE ; If using SPADD, calculate SPADD = (Fosc/(i2c_clock_speed * 4)) - 1 ; e.g. the 24LC128 has a maximum clock rate of 400 kHz, so ; SPADD = 20x10^6/(4x10^5 * 4) - 1 = 11.5 movlw 12 movwf SSPADD bcf STATUS, RP0 return I2CStart bsf STATUS, RP0 bsf SSPCON2, SEN ; initiate a start condition bcf STATUS, RP0 call I2CWait return I2CRestart bsf STATUS, RP0 bsf SSPCON2, RSEN ; initiate a restart condition bcf STATUS, RP0 call I2CWait return I2CStop bsf STATUS, RP0 bsf SSPCON2, PEN ; initiate a stop condition bcf STATUS, RP0 call I2CWait return I2CSend movwf SSPBUF ; Start a write op call I2CWait return I2CReceive bsf STATUS, RP0 bsf SSPCON2, RCEN ; Start receive bcf STATUS, RP0 call I2CWait movf SSPBUF, W ; return in W return I2CAck bsf STATUS, RP0 bcf SSPCON2, ACKDT ; setup ACK bsf SSPCON2, ACKEN ; send ACK bcf STATUS, RP0 ; ; call I2CWait return I2CNack bsf STATUS, RP0 bsf SSPCON2, ACKDT ; setup NACK bsf SSPCON2, ACKEN ; send NACK bcf STATUS, RP0 ; call I2CWait return ; Wait for an operation end by waiting for the BF bit in SSPSTAT I2CBFWait bsf STATUS, RP0 btfsc SSPSTAT, BF goto $-1 bcf STATUS, RP0 return ; Wait for an operation end by waiting for the R/W bit I2CRWWait bsf STATUS, RP0 btfsc SSPSTAT, I2CRW goto I2CRWWait bcf STATUS, RP0 return ; Wait for end of I2C operation by watching the SSPIF flag I2CWait btfss PIR1, SSPIF goto I2CWait bcf PIR1, SSPIF ; clear for next operation return END