Sound

produces a note of specified frequency and duration.

The Sound routine generates squarewave tones with frequencies of 280 Hz to 71 kHz and durations of 3.6 ms to 0.92 seconds at 4 MHz. It differs from its PBASIC counterpart in that all frequency values from 1 to 255 produce squarewave tones. PBASIC's Sound instruction generates tones when the frequency is in the range of 1 to 127, and hissing white-noise effects from 128 to 255. Both PBASIC and assembly versions of Sound produce a silent pause with a frequency value of 0.

To use the routine, store a frequency value from 0 to 255 in freq, and a duration from 0 to 255 in duratn. Put the output pin number (0 to 7) into pin and the port number (0 to 2 for RA through RC) into the w register, and call Sound.

The following formula determines the frequency output by Sound (assuming a PIC clock speed of 4 MHz):

The formula shows that freq sets the period of the tone in 14-µs units. As a result, the frequency isn't a linear function of freq. For example, a freq of 1 produces a 280.1-Hz tone, while 2 produces 281.2 Hz. At the higher end of the scale, a freq of 250 produces an 11.9-kHz tone, while 251 produces 14.29 kHz.

PBASIC's Sound command has the same characteristic, but starts slightly below 100 Hz. It has a period resolution of approximately 83µs. If you want to more closely emulate PBASIC, just pad the :loop of this Sound routine to take a total of 83µs.

The PBASIC command permits you to specify a list of frequencies and durations to be played. The demonstration program shows how to do this in assembly by using a pair of lookup tables. If you need white-noise effects in your program, use the Random routine. Call Random from within a tight program loop, and copy one bit of its shift register to the speaker output. The random bit stream will produce a hissing sound.

One hint on using Sound: always assign the port number to w immediately before calling the routine. If you don't, some other instruction that uses w may change its contents, causing an error.

Demonstrating Sound.

To see Sound in operation, connect the circuit below to an erasable PIC or PIC emulator, such as the Parallax downloader. Assemble and run SOUND.SRC. When you apply power to the PIC or reset the emulator, the speaker will play a short tune.


; SOUND port, pin, frequency, duration
; Generates squarewave tones (notes) of the specified frequency and  
; duration. This demonstration program shows how to use lookup tables to 
; play tunes or effects from data stored in program memory. 

        org     8
pin     ds      1       ; Pin number to use as output (0-7). 
freq    ds      1       ; Frequency of note. 
duratn  ds      1       ; Duration of note. 
f_copy  ds      1       ; Copy of freq variable. 
temp    ds      1       ; Variable to stretch duratn to 16 bits.
index   ds      1       ; Index to tables of notes, lengths. 

; Device data and reset vector
        device  pic16c55,xt_osc,wdt_off,protect_off
        reset   start
        org     0

; Table to convert pin number (0-7) into bit mask (00000001b to 10000000b). 
Pinz    jmp     pc+w
        retw    1,2,4,8,16,32,64,128

; Table of notes for the demonstration tune "Charge."
Notes   jmp     pc+w
        retw    166,189,204,212,204,212

; Table of note durations for the demonstration tune "Charge."
Lengths jmp     pc+w
        retw    52,52,52,82,45,190

start   mov     !ra, #0 ; Output for speaker. 
        clr     temp    ; Clear LSB of duratn counter. 
        clr     index   ; Clear the note counter. 
:loop   mov     w,index ; Look up note in table. 
        call    Notes
        mov     freq,w  ; Store note in freq. 
        mov     w,index ; Look up length in table. 
        call    Lengths
        mov     duratn,w        ; Store in duratn. 
        mov     pin,#3  ; Select pin 3 of port
        mov     w,#0    ; RA for speaker output
        call    Sound   ; Play the sound. 
        inc     index   ; Advance to next sound in tables. 
        cjbe    index,#5,:loop  ; IF index <= 5, play note
        jmp     $       ; ELSE end. 

; Upon entry, the desired speaker pin must already be set up as an output. 
; The w register contains a number representing the output port (0-2) for 
; RA through RC. Variable "pin" contains the pin number (0-7). The variables
; freq and duratn are the frequency and duration of the note. 

; Upon exit, duratn will be cleared to 0, but freq will be intact. If you require the 
; value of duratn for some other purpose, copy it before calling Sound. The 
; variable pin and the file-select register (fsr) will also have be altered. 

Sound   NEG     duratn  ; Convert to two's complement. 
        mov     fsr,w   ; Point to the port number. 
        add     fsr,#RA ; Add offset for port RA. 
        mov     w,pin   ; Now look up pin number in 
        call    Pinz    ; table and get bit mask. 
        mov     pin,w   ; Put the mask into pin.
        mov     f_copy,freq     ; Use copy so freq value is saved. 
        snz             ; If freq is 0, clear pin to 
        clr     pin     ; produce a silent delay. 
:loop   mov     w,pin   ; f_copy=f_copy+1: IF f_copy<=255 
        incsz   f_copy  ; THEN w=pin ELSE w=0
        clr     w       ; (makes a silent delay if freq = 0)
        XOR     indirect,w      ; indirect=indirect XOR w
        mov     w,f_copy        ; If f_copy has rolled over to 0, 
        snz             ; reload it with the value of freq. 
        mov     w,freq  
        mov     f_copy,w
        inc     temp    ; Increment LSB of duratn counter. 
        snz             ; Overflow? Yes: Increment duratn and
        incsz   duratn  ; if it overflows, return. Else loop. 
        jmp     :loop
        ret