;****************************************************************************** ;****************************************************************************** ; ; Colorganic Spectralizer ; ; Copyright (C) 2011 Fearless Night ; www.FearlessNight.com ; ; 11/2010 R.S.Pierce - initial version ; 5/2011 R.S.Pierce - optimized & cleaned up documentation ; 4/2012 R.S.Pierce - added more comments ; ; Version 1.201105 is for use with all production PCBs (#1212 or #110222) ; ; Edit/Compile/link with MPLAB 8.41, MPASM 5.34, MPLINK 4.34 (initial version) ; Edit/Compile/link with MPLAB 8.63, MPASM 5.39, MPLINK 4.38 (produces same binary) ; ;****************************************************************************** ;****************************************************************************** errorlevel -302 ; magic to suppress register bank warnings ; (this program operates in one bank) ;------------------------------------------------------------------------------ ; PROCESSOR DECLARATION ;------------------------------------------------------------------------------ #include ; processor specific variable definitions ; The PIC16F505 INTRC oscillator only goes one speed and there's no prescale; ; so the instruction (and timer clock) rate is 1 usec (= fOsc/4). ; chip fuse configuration: ; - watchdog is not used ; - MCLR is not used (it's an input pulled up to Vdd, which allows for ICSP) ; - internal (4 MHz) RC oscillator ; - no code protection __CONFIG _CP_OFF & _WDT_OFF & _MCLRE_OFF & _IntRC_OSC_RB4EN ;---------------------------------------------------------------------- ; D E F I N E S ;---------------------------------------------------------------------- ;-- generic definitions #DEFINE B0 0 ; bit numbers #DEFINE B1 1 #DEFINE B2 2 #DEFINE B3 3 #DEFINE B4 4 #DEFINE B5 5 #DEFINE B6 6 #DEFINE B7 7 ; create a bit mask #DEFINE MASK(b) (1 << b) ; e.g. MASK(B1) = b00000010 ; move an 8-bit constant to a register (or RAM) MOVLF macro zLITERAL, zREG ; move literal to register movlw zLITERAL movwf zREG endm ; inline delay (a few usecs) NOP_REPEAT macro zCount ; short delay (count = number of usecs) variable i i=0 while i < zCount nop i=i+1 endw endm ;-- application specific definitions ; MSGEQ7 chip Reset input, causes the MSGEQ7 to resync it's output #define RESET_PORT PORTC ; output port #define _RESET B4 ; output bit (positive logic) ; MSGEQ7 chip Strobe input, causes the MSGEQ7 to output the next frequency band #define STROBE_PORT PORTC ; output port #define _STROBE B5 ; output bit (positive logic) ; This defines the port bits associated with each LED (but not which ports the are on) #define _LED1 B2 ; LED drive pins (negative logic) #define _LED2 B0 #define _LED3 B1 #define _LED4 B2 #define _LED5 B3 #define _LED6 B5 #define _LED7 B4 ; Bit masks for the LED drive pins on each port #define PORTB_LEDMASK (MASK(_LED1)|MASK(_LED6)|MASK(_LED7)) #define PORTC_LEDMASK (MASK(_LED2)|MASK(_LED3)|MASK(_LED4)|MASK(_LED5)) ; Byte to output (to PORTC) to turn on the MSGEQ7 Reset signal ; (also turns off all the LEDs on PORTC) #define RESET_ON (MASK(_RESET)|PORTC_LEDMASK) ; Byte to output (to PORTC) to turn off the MSGEQ7 Reset signal ; (also turns off all the LEDs on PORTC) #define RESET_OFF PORTC_LEDMASK ; Byte to output (to PORTC) to turn on the MSGEQ7 Strobe signal ; (also turns off all the LEDs on PORTC) #define STROBE_ON (MASK(_STROBE)|PORTC_LEDMASK) ; Byte to output (to PORTC) to turn off the MSGEQ7 Strobe signal ; (also turns off all the LEDs on PORTC) #define STROBE_OFF PORTC_LEDMASK ; These defines create sets of parameters for use with the DO_LED macro ; ; For example: DO_LED LED1, 0 ; expands to: DO_LED _LED1, _PORTB_LEDMASK, PORTB, 0 ; #define LED_PORTB PORTB_LEDMASK, PORTB #define LED_PORTC PORTC_LEDMASK, PORTC #define LED_1 _LED1, LED_PORTB #define LED_2 _LED2, LED_PORTC #define LED_3 _LED3, LED_PORTC #define LED_4 _LED4, LED_PORTC #define LED_5 _LED5, LED_PORTC #define LED_6 _LED6, LED_PORTB #define LED_7 _LED7, LED_PORTB ; Macro to display one frequency band on one LED ; ; Parameters: ; zLED = port bit number for this LED ; zPORTMASK = all LED port bits on the this LED port ; zPORT = this LED port ; zRESET = 1 if a Reset signal is to be sent to the MSGEQ7 ; DO_LED macro zLED,zPORTMASK,zPORT,zRESET ; send strobe pulse to MSGEQ7 ; this causes the MSGEQ7 output to shift to the next frequency band MOVLF STROBE_ON, STROBE_PORT NOP_REPEAT 18 ; MSGEQ7 timing requirement (18 usec min strobe pulse width) MOVLF STROBE_OFF,STROBE_PORT ; The following conditional assembly structure allows the code to be used ; with any arrangement of the LED_1 to LED_7 constants in BIGLOOP. #if zRESET == 0 ; no Reset to be sent to MSGEQ7 ; turn on this LED ; zPORTMASK = byte to send to turn all LEDs off ; xor with zLED bit to turn on this LED only ; while keeping STROBE and RESET bits (if they're on this port) set to zero MOVLF MASK(zLED) ^ zPORTMASK, zPORT ; set output pin to '0' to turn on LED #else ; Reset is to be sent to MSGEQ7 ; resync the MSGEQ7 with a RESET pulse at the last mux frame ; this will cause the 1'st frequency band to be selected ; when the NEXT strobe pulse is sent. #if zPORT != RESET_PORT ; the LED and RESET are on different ports, so must write to both ports ; zPORTMASK = byte to send to turn all LEDs off ; xor with zLED bit to turn on this LED only MOVLF MASK(zLED) ^ zPORTMASK, zPORT ; RESET is on the other port, where all the LEDs are off MOVLF RESET_ON, RESET_PORT ; send reset pulse (requires 100 nsec min.) MOVLF RESET_OFF,RESET_PORT ; reset pulse width is ~2 usec #else ; this LED and the RESET signal are on the same port, so do both at once ; turn on this LED and activate RESET pulse at the same time ; RESET_ON = byte to send to turn on the MSGEQ7 reset signal (with all LEDs off) ; xor with zLED to turn on this LED too MOVLF MASK(zLED) ^ RESET_ON, zPORT ; keep this LED on, but turn off the RESET signal ; this generates a 2 usec RESET pulse, which meets the 100 nsec minimum required MOVLF MASK(zLED) ^ zPORTMASK, zPORT #endif #endif ; LED on dwell time movlw 2 ; amount of time one LED is on (in msecs) call msec_delay ; turn this LED off ; (actually turns off all LEDs on this port) MOVLF zPORTMASK, zPORT ; set output pin to '1' to turn off LED endm ;---------------------------------------------------------------------- ; RAM data storage ; ; PIC16F505 has 72 bytes of SRAM in several banks ;---------------------------------------------------------------------- ; "Access RAM" is the easiest memory to use because it requires no bank selection. ; But there's only 8 bytes of it. ; Fortunately this program only needs 2 bytes of RAM. ACCESS_RAM UDATA_SHR ; 8 bytes @ 0x08..0x0F, bankless delay RES 2 ; 2 bytes for msec_delay function to use ;---------------------------------------------------------------------- ; Program Memory Start ; ; PIC16F505 has 1K of Program Memory in two banks ; the Stack is 2 levels deep ; ; The actual start address is 0x3FF, which is a MOVLW K instruction ; that retrieves the factory oscillator calibration value. ;---------------------------------------------------------------------- RESET_VECTOR CODE 0x000 ; processor reset vector ; This program is small enough to fit in one of the program memory banks. BANKSEL 0 ; always in bank 0 ; See the data sheet for oscillator calibration setup information; ; for this application, the oscillator calibration is not critical. movwf OSCCAL ; update register with factory cal value (optional) ;-- initialize I/O ports ; see data sheet for OPTION register movlw b'11011111' ; make T0CKI pin digital I/O instead of timer0 clock input OPTION ; move WREG to OPTION register ; some LEDs are on PORTB; set the output pins to '1' to turn off the LEDs MOVLF PORTB_LEDMASK, PORTB ; all LEDS off ; set the PORTB pin directions (all outputs except B3) movlw b'00001000' ; B7,B6=N/A, B3(MCLR) is unused input TRIS PORTB ; some LEDs are on PORTC; set the output pins to '1' to turn off the LEDs ; the other PORTC pins go to the MSGEQ7 chip; set them to '0'. MOVLF PORTC_LEDMASK, PORTC ; all LEDS off ; set the PORTC pin directions (all outputs) movlw b'00000000' ; B7,B6=N/A, B5=STROBE, B4=RESET TRIS PORTC ;-- Foregnd process; this runs forever ; All this application does is provide timing signals and LED drive outputs. ; The BIGLOOP loop rate is the LED refresh rate. During each loop, the LEDs ; are turned on in synchronization with the output of the MSGEQ7 frequency ; analyzer chip so that each LED shows the level of a particular frequency band. BIGLOOP ; drive multiplexed LEDs in sync with the MSGEQ7 spectrum analyzer chip ; The MSGEQ7 spectrum analyzer chip outputs the frequency bands ; in a specific order. The LED_n constants can be rearranged ; to change the LEDs that correspond with each frequency band. DO_LED LED_1,0 ; 63 Hz DO_LED LED_2,0 ; 160 Hz DO_LED LED_3,0 ; 400 Hz DO_LED LED_4,0 ; 1.0 KHz DO_LED LED_5,0 ; 2.5 KHz DO_LED LED_6,0 ; 6.25 KHz ; At the last frequency band, a RESET signal is sent to the MSGEQ7 ; chip to ensure that the chip is synchronized with the LEDs. ; The RESET signal forces the MSGEQ7 to start sending the first ; frequency band on the next STROBE pulse. DO_LED LED_7,1 ; 16 KHz, with resync goto BIGLOOP ;---------------------------------------------------------------------- ; Time delay (non-precision, but typically within +/-1%) ; ; Entry: WREG = number of milliseconds to delay ;---------------------------------------------------------------------- msec_delay ; 1 usec / instruction cycle, so 1 msec = 1000 cycles movwf delay+0 ; store number of usecs MSEC_N ; outer loop: 4 usec x 250 = 1000 cycles (1 msec) MOVLF 250, delay+1 ; inner loop count MSEC_1 ; inner loop: 4 cycles per loop = 4 usec nop ; 1 cycle decfsz delay+1, f ; 1 cycle if goto is taken, 2 cycles if skip goto MSEC_1 ; 2 cycles ; The actual timing is slightly greater than 1 msec ; because the following cycles are not accounted for... decfsz delay+0, f ; 1 cycle if goto is taken, 2 cycles if skip goto MSEC_N ; 2 cycles retlw 0 ; return to caller ;---------------------------------------------------------------------- END