; PIC sound - pulse, white noise and (CPU intensive) samples ; v 0.29 By Phil Ruston. 15-5-2005 ; 8 MHz source clock - 500 nS per instruction ;---------------------------------------------------------------------- list p=16f627a ; define processor list r=decimal ; default number base #include ; processor specific defs #define bank0 bcf STATUS,RP0 ;macros #define bank1 bsf STATUS,RP0 #define skipifzero btfss STATUS,Z #define skipifnotzero btfsc STATUS,Z #define skipifcarry btfss STATUS,C #define skipifnotcarry btfsc STATUS,C #define skipifborrow btfsc STATUS,C #define skipifnotborrow btfss STATUS,C ;----- PIC H/W OPTIONS -------------------------------------------------------- __CONFIG _CP_OFF & _WDT_OFF & _BODEN_OFF & _PWRTE_ON & _EXTCLK_OSC & _MCLRE_OFF & _LVP_OFF ;----- PROJECT EQUATES ---------------------------------------------- code_version equ 0x29 sound_channel equ 0 ;0 to 2 rand1 equ 0x3d ;random number seeds for white noise rand2 equ 0xa9 rand3 equ 0x52 ;--------------------------------------------------------------------- ;--- PORT A assignments -- command_or_data equ 4 new_data_on_latch equ 5 clear_busy_out equ 6 ;----- VARIABLES------------------------------------------------------------------------------ freq_lo equ 0x20 ; First PIC RAM address freq_hi equ 0x21 volume equ 0x22 control_nybble equ 0x23 incoming_freq_lo equ 0x24 incoming_freq_hi equ 0x25 incoming_volume equ 0x26 incoming_pulsewidth equ 0x27 multiply_freq_lo_a equ 0x28 multiply_freq_hi_a equ 0x29 multiply_freq_lo_b equ 0x2a multiply_freq_hi_b equ 0x2b pulse_mark_lo equ 0x2c pulse_mark_hi equ 0x2d temp_pulse_space_lo equ 0x2e temp_pulse_space_hi equ 0x2f pulse_space_lo equ 0x30 pulse_space_hi equ 0x31 sample equ 0x32 randoml equ 0x33 randomm equ 0x34 randomh equ 0x35 scale_lo equ 0x36 triangle_counter equ 0x37 new_values_ready equ 0x38 pulse_max_test_lo equ 0x39 pulse_highlow equ 0x3a error_flag equ 0x3b tempbytestore equ 0x3c port_temp equ 0x3d waitloop1 equ 0x3e waitloop2 equ 0x3f ;--------------------------------------------------------------------------------------------- w_temp equ 0x70 ; variable used for context saving status_temp equ 0x71 ; variable used for context saving ;********************************************************************************************* ORG 0x000 ; processor reset vector goto main ; go to beginning of program retlw code_version ; version number only retlw sound_channel ; sound channel for this PIC ORG 0x004 ; interrupt vector location movwf w_temp ; save off current W register contents swapf STATUS,w ; move status register into W register movwf status_temp ; save off contents of STATUS register bcf PIR1,TMR1IF ; clear timer 1 irq flag bcf T1CON,0 ; stop timer1 movf control_nybble,w ; what type of waveform? skipifnotzero goto pulse_wave ;0 sublw 1 skipifnotzero goto white_noise ;1 movf control_nybble,w sublw 2 skipifnotzero goto play_samples ;2 goto test_triangle ;3 ;-------------------------------------------------------------------------------------------- pulse_wave comf pulse_highlow,f skipifzero goto pulse_high nop pulse_low movlw 0 movwf PORTB ; set amplitude low movf pulse_space_lo,w movwf TMR1L movf pulse_space_hi,w movwf TMR1H bsf T1CON,0 ; restart timer 1 goto sound_done pulse_high movf volume,w ; set amplitude high movwf PORTB movf pulse_mark_lo,w movwf TMR1L movf pulse_mark_hi,w movwf TMR1H bsf T1CON,0 ; restart timer 1 goto sound_done ;---------------------------------------------------------------------------------------- white_noise: movf sample,w movwf PORTB movf freq_lo,w movwf TMR1L movf freq_hi,w movwf TMR1H bsf T1CON,0 ; restart timer 1 movf randoml,w ; mangle together a random number in W iorwf randomh,w btfsc STATUS,Z comf randomh,f movlw 0x80 btfsc randomh,6 xorwf randomh,f btfsc randomh,4 xorwf randomh,f btfsc randoml,3 xorwf randomh,f bcf STATUS,C btfsc randomh,7 bsf STATUS,C rlf randoml,f rlf randomm,f rlf randomh,f movf randoml,w xorwf randomm,w xorwf randomh,w clrf sample ; scale the random number based on the the volume setting clrf scale_lo bcf STATUS,C btfsc volume,0 addwf sample,f rrf sample,f rrf scale_lo,f btfsc volume,1 addwf sample,f rrf sample,f rrf scale_lo,f btfsc volume,2 addwf sample,f rrf sample,f rrf scale_lo,f btfsc volume,3 addwf sample,f rrf sample,f rrf scale_lo,f btfsc volume,4 addwf sample,f rrf sample,f rrf scale_lo,f btfsc volume,5 addwf sample,f rrf sample,f rrf scale_lo,f btfsc volume,6 addwf sample,f rrf sample,f rrf scale_lo,f btfsc volume,7 addwf sample,f rrf sample,f rrf scale_lo,f goto sound_done ;--------------------------------------------------------------------------------------- test_triangle movf sample,w movwf PORTB movf freq_lo,w movwf TMR1L movf freq_hi,w movwf TMR1H bsf T1CON,0 ; restart timer 1 incf triangle_counter,f movf triangle_counter,w btfsc triangle_counter,7 comf triangle_counter,w movwf sample bcf STATUS,C rlf sample,w goto sound_done ;-------------------------------------------------------------------------------------- play_samples clrf TMR1L clrf TMR1H bsf T1CON,0 ; restart timer 1 ;-------------------------------------------------------------------------------------- sound_done movf new_values_ready,f skipifnotzero goto end_irq movf incoming_freq_lo,w ;safe update of frequency & mark/space timings movwf freq_lo movf incoming_freq_hi,w movwf freq_hi comf multiply_freq_lo_b,w movwf pulse_mark_lo comf multiply_freq_hi_b,w movwf pulse_mark_hi movf temp_pulse_space_lo,w movwf pulse_space_lo movf temp_pulse_space_hi,w movwf pulse_space_hi clrf new_values_ready ;---------------------------------------------------------------------------------------------- end_irq swapf status_temp,w ; retrieve copy of STATUS register movwf STATUS ; restore pre-isr STATUS register contents swapf w_temp,f swapf w_temp,w ; restore pre-isr W register contents retfie ; return from interrupt ;--------------------------------------------------------------------------------------------- main movlw b'00000111' ; INITIALIZE CHIP movwf CMCON ; use digital mode for PORTB (disable comparitors) clrf PORTA clrf PORTB banksel TRISA movlw b'00111111' ; set data direction for port A (bits 0:3 in - receive update nybs) movwf TRISA ; 4:in = reg/data, 5:in = new data to be analysed, 6:out = clear ff pulse movlw b'00000000' ; set data direction for port B (all outputs) movwf TRISB bsf PIE1,TMR1IE ; timer 1 interupt enable banksel PORTA movlw b'00000000' ; timer 1 - 1:1 prescale, no osc, sync ignored, movwf T1CON ; use int.clk and stop timer movlw b'11000000' movwf INTCON ; Peripheral IRQs enable (for Timer1)+Global IRQ enable. ;-------------------------------------------------------------------------------------- bcf PORTA,clear_busy_out ; delay start-up to allow everything to settle clrf waitloop1 ; clear busy flipflop too clrf waitloop2 decwlp decfsz waitloop2,f goto decwlp decfsz waitloop1,f goto decwlp bsf PORTA,clear_busy_out ;active lo so return high movlw rand1 movwf randoml movlw rand2 movwf randomm movlw rand3 movwf randomh clrf volume clrf pulse_highlow clrf control_nybble clrf incoming_freq_lo movlw 0x80 movwf incoming_freq_hi movwf incoming_pulsewidth call calculate_pulse_period movlw 1 movwf new_values_ready bsf T1CON,0 ;start timer 1 ;-------------------------------------------------------------------------------------------- sound_start call clear_busy_flipflop mainloop call wait_new_data_on_latch ;wait for new instruction on latch signal btfss PORTA,command_or_data ;is it data (0) or command (1) (address/channel) select? goto mainloop ;if data, ignore as its for another channel receivedcommand movf port_temp,w ;is it addressing this sound channel (PIC)? andlw b'00001100' ;mask bits 2:3 to compare xorlw sound_channel * 4 skipifnotzero goto chan_match xorlw sound_channel * 4 ;if not is the non-existant channel 3 being xorlw b'00001100' ;accessed? Clear the busy flipflop if so skipifzero goto mainloop goto sound_start chan_match movlw 3 andwf port_temp,f ;what register is to be updated? skipifnotzero goto read_in_frequency ;0 movf port_temp,w sublw 1 skipifnotzero goto read_in_volume ;1 movf port_temp,w sublw 2 skipifnotzero goto read_in_pulse_width ;2 goto read_in_control_nybble ;3 read_in_frequency call read_in_byte ;two bytes required btfsc error_flag,0 goto receivedcommand movwf incoming_freq_lo call read_in_byte btfsc error_flag,0 goto receivedcommand movwf incoming_freq_hi comf incoming_freq_hi,w ;test new pitch against maximum allowed frequency (ff80) skipifzero goto pitchok movlw 0x80 btfsc incoming_freq_lo,7 movwf incoming_freq_lo pitchok call calculate_pulse_period movlw 1 movwf new_values_ready goto sound_start read_in_volume call read_in_byte ;one byte requried btfsc error_flag,0 goto receivedcommand movwf volume movf control_nybble,w ; if in "play samples: mode 2", put volume byte sublw 2 ; directly on PORT B skipifzero goto sound_start movf volume,w movwf PORTB goto sound_start read_in_pulse_width call read_in_byte ;one byte required btfsc error_flag,0 goto receivedcommand movwf incoming_pulsewidth call calculate_pulse_period movlw 1 movwf new_values_ready goto sound_start read_in_control_nybble call clear_busy_flipflop ;only 4 bits required call settle_delay call wait_new_data_on_latch btfsc port_temp,command_or_data goto receivedcommand movf port_temp,w andlw b'00001111' movwf control_nybble goto sound_start ;--------------------------------------------------------------------------------------------- read_in_byte call clear_busy_flipflop ;signal to CPU - ready for more data call settle_delay call wait_new_data_on_latch ;wait for low nybble data chunk btfsc port_temp,command_or_data goto not_data ;if this is a reg select, somethings gone spoony movf port_temp,w andlw b'00001111' movwf tempbytestore call clear_busy_flipflop ;signal to CPU - ready for more data call settle_delay call wait_new_data_on_latch ;wait for high nybble data chunk btfsc port_temp,command_or_data goto not_data swapf port_temp,w andlw b'11110000' iorwf tempbytestore,w clrf error_flag return not_data bsf error_flag,0 return ;--------------------------------------------------------------------------------------------- wait_new_data_on_latch btfss PORTA,new_data_on_latch ; wait for new latch data signal hi goto wait_new_data_on_latch btfss PORTA,new_data_on_latch ; double check goto wait_new_data_on_latch call settle_delay movf PORTA,w movwf port_temp return ;-------------------------------------------------------------------------------------------- clear_busy_flipflop bcf INTCON,PEIE ; stop interrupts happening during clear pulse nop bcf PORTA,clear_busy_out ; which would extend pulse unnacceptibly nop bsf PORTA,clear_busy_out nop bsf INTCON,PEIE return settle_delay nop nop return ;-------------------------------------------------------------------------------------------- calculate_pulse_period clrf multiply_freq_hi_a ; 8 * 16 bit multiplication. First, pulse width * freq_lo clrf multiply_freq_lo_a comf incoming_freq_lo,w ; value in register is complimented to give positive number of cycles bcf STATUS,C btfsc incoming_pulsewidth,0 addwf multiply_freq_hi_a,f rrf multiply_freq_hi_a,f rrf multiply_freq_lo_a,f btfsc incoming_pulsewidth,1 addwf multiply_freq_hi_a,f rrf multiply_freq_hi_a,f rrf multiply_freq_lo_a,f btfsc incoming_pulsewidth,2 addwf multiply_freq_hi_a,f rrf multiply_freq_hi_a,f rrf multiply_freq_lo_a,f btfsc incoming_pulsewidth,3 addwf multiply_freq_hi_a,f rrf multiply_freq_hi_a,f rrf multiply_freq_lo_a,f btfsc incoming_pulsewidth,4 addwf multiply_freq_hi_a,f rrf multiply_freq_hi_a,f rrf multiply_freq_lo_a,f btfsc incoming_pulsewidth,5 addwf multiply_freq_hi_a,f rrf multiply_freq_hi_a,f rrf multiply_freq_lo_a,f btfsc incoming_pulsewidth,6 addwf multiply_freq_hi_a,f rrf multiply_freq_hi_a,f rrf multiply_freq_lo_a,f btfsc incoming_pulsewidth,7 addwf multiply_freq_hi_a,f rrf multiply_freq_hi_a,f rrf multiply_freq_lo_a,f clrf multiply_freq_hi_b ; Second, pulse width * freq_hi clrf multiply_freq_lo_b comf incoming_freq_hi,w ; value in register is complimented to give positive count of cycles bcf STATUS,C btfsc incoming_pulsewidth,0 addwf multiply_freq_hi_b,f rrf multiply_freq_hi_b,f rrf multiply_freq_lo_b,f btfsc incoming_pulsewidth,1 addwf multiply_freq_hi_b,f rrf multiply_freq_hi_b,f rrf multiply_freq_lo_b,f btfsc incoming_pulsewidth,2 addwf multiply_freq_hi_b,f rrf multiply_freq_hi_b,f rrf multiply_freq_lo_b,f btfsc incoming_pulsewidth,3 addwf multiply_freq_hi_b,f rrf multiply_freq_hi_b,f rrf multiply_freq_lo_b,f btfsc incoming_pulsewidth,4 addwf multiply_freq_hi_b,f rrf multiply_freq_hi_b,f rrf multiply_freq_lo_b,f btfsc incoming_pulsewidth,5 addwf multiply_freq_hi_b,f rrf multiply_freq_hi_b,f rrf multiply_freq_lo_b,f btfsc incoming_pulsewidth,6 addwf multiply_freq_hi_b,f rrf multiply_freq_hi_b,f rrf multiply_freq_lo_b,f btfsc incoming_pulsewidth,7 addwf multiply_freq_hi_b,f rrf multiply_freq_hi_b,f ; pulse **mark** width hi - positive cycle count rrf multiply_freq_lo_b,f ; pulse **mark** width lo - positive cycle count movf multiply_freq_hi_a,w ; Third, add pertinent parts of results together addwf multiply_freq_lo_b,f ; result is in: MFHB/MFLB/MFLA (only interested in skipifnotcarry ; MFHB/MFLB here) incf multiply_freq_hi_b,f movf incoming_freq_lo,w ; Fourth, make sure pulse width does not come within addwf multiply_freq_lo_b,w ; 64 cycles of the end of the wave cycle. If it does movwf pulse_max_test_lo ; clip it at period-64 movf incoming_freq_hi,w skipifnotcarry addlw 1 addwf multiply_freq_hi_b,w sublw 0xff skipifzero goto pulse_size_ok movf pulse_max_test_lo,w addlw 0x40 skipifcarry goto pulse_size_ok movf incoming_freq_lo,w addlw 0x40 movwf multiply_freq_lo_b movf incoming_freq_hi,w skipifnotcarry addlw 1 movwf multiply_freq_hi_b comf multiply_freq_lo_b,f ; pulse **mark** width hi - positive cycle count comf multiply_freq_hi_b,f ; pulse **mark** width lo - positive cycle count pulse_size_ok movf incoming_freq_hi,w ; copy to relevant registers movwf temp_pulse_space_hi ; and calculate pulse ** space ** time movf incoming_freq_lo,w movwf temp_pulse_space_lo addwf multiply_freq_lo_b,w skipifnotcarry incf temp_pulse_space_hi,f movwf temp_pulse_space_lo movf multiply_freq_hi_b,w addwf temp_pulse_space_hi,f return ;******************************************************************************************* END ; directive 'end of program'