; audio.asm ; Program to demonstrate the use of PWM using the CCP modules on the PIC ; by driving the audio output on the Raven board. ; ; PWM output is a square wave with the following characteristics ; ; Period ; |<----------------->| ; ; |-------| |-------| ; | | | | ; -------| |-----------| |------- ; ; :<----->: : ; : Duty : : ; : Cycle : TMR2 = PR2 ; : : ; TMR2=PR2 TMR2 = Duty Cycle ; ; You begin with PR2 set to some value. Timer2 is started and when the ; Timer2 count register is equal to PR2, the PWM output goes high. It ; then resets Timer2 and counts until the Timer2 count equals the duty ; cycle counter (CCPRxL:CCP1CON) and then the signal goes low again. ; Then the process starts again. So the period consists of a high part ; and a low part, defined by the period and the duty cycle. Note that ; the duty cycle need not be one-half of the period. Normally the duty ; cycle is reported as a percentage, such as 65%, meaning that 65% of the ; period the signal is high. ; ; In this instance, we want to generate signals that can be used to drive ; a speaker, so we will have a duty cycle of 50% and we will want to vary ; the frequency (by varying the period) to get different tones. LIST P=16F877, R=DEC INCLUDE "P16F877.inc" ; Definitions ; __CONFIG _CP_OFF & _DEBUG_OFF & _WRT_ENABLE_OFF & _CPD_OFF & _LVP_OFF & _BODEN_OFF & _PWRTE_ON & _WDT_OFF & _HS_OSC20 ; Define start of vars RAM EQU 0x20 ; Variables Temp EQU RAM+0 DTemp1 EQU RAM+1 DTemp2 EQU RAM+2 Counter EQU RAM+3 ; Start of the Program ORG 3 goto Start ORG 4 Timer1ISR ; Since PWM won't give us the low end frequencies we want, we will generate our own. ; Timer1 will be set up to interrupt every 1/4000 seconds to give us the desired ; resolution. Then, we will set our own version of T2CON to determine the period. ; In this case, the duty cycle is always 50%, so its not a problem. ; ; ORG 0x20 ; The frequencies are numbered 0 through 15 and represent an uniformly spaced ; group from 1225 Hz to 9470 Hz in steps of about 550Hz. GetFreq addwf PCL,F retlw 33 retlw 48 retlw 63 retlw 77 retlw 92 retlw 107 retlw 122 retlw 137 retlw 151 retlw 166 retlw 181 retlw 196 retlw 211 retlw 225 retlw 240 retlw 255 SongNote addwf PCL,F retlw 0x0F retlw 0x0E retlw 0x1B retlw 0x0C retlw 0x0B retlw 0x09 retlw 0x0A retlw 0x0B retlw 0x0C retlw 0x0D retlw 0x1A retlw 0x1C Start bsf STATUS, RP0 bcf TRISC,2 ; PWM output port bcf STATUS, RP0 call PWMConfig ; Go through a cycle of creating some special sounds. We aren't far from music ; here. Everybody sing along!! movlw 11 movwf Counter main_loop movf Counter,W call SongNote call PlayNote decfsz Counter,F goto main_loop movlw 11 movwf Counter goto main_loop ; Sound support PlayNote movwf Temp andlw 0x0F call GetFreq call PWMSetPeriod movlw 250 btfss Temp,7 movlw 100 call Delay_ms return ; ------------------------------------------------------------------- ; PWM routines ; We want to have frequencies in the range of 100 Hz to about 10,000 Hz, so ; we need periods of 0.01 to 0.0001. Given the formula: ; PWM period = [PR2 + 1] * 4 / Fosc * Timer2 Prescale Value ; ; and an oscillator frequency of 20Mhz (Fosc), ; For a 20 MHz clock, the frequency table is: ; ; Prescaler Pr2=1 Pr2=255 ; 1 5 MHz 1.9608 kHz ; 4 1.25 MHz 4.902 kHz ; 16 0.3125 MHz 1.225 kHz ; ; So we will have to use two different prescalers. One for use a prescaler of 4 for Timer2 and let Pr2 vary from 208 down ; to about 0. PWMConfig ; Initialize CCP1 movlw 255 call PWMSetPeriod movlw b'00100000' ; duty cycle is 50% = 128 movwf CCPR1L movlw b'00000110' ; Timer 2 is on and prescaler is 16 movwf T2CON movlw b'00001100' ; PWM mode with two LSB's = 00 movwf CCP1CON return PWMSetPeriod bsf STATUS,RP0 movwf PR2 ; set period bcf STATUS,C rrl PR2,W ; and duty cycle = period/2 movwf CCPR1L bcf STATUS,RP0 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 ; ------------------------- ; Delay milliseconds Delay_ms bsf STATUS,RP0 bsf STATUS,RP1 movwf DTemp1 dloop_0 ; number of ms to delay movlw 250 movwf DTemp2 dloop_1 ; 1 ms delay (approximately) fill (nop), 17 decfsz DTemp2,F goto dloop_1 decfsz DTemp1,F goto dloop_0 bcf STATUS,RP1 bcf STATUS,RP0 return END