; EL wire MIDI controller ; 16f877 PIC ; Copyright (c) 2002 Jesse Lackey, all rights reserved. ; jesse@celestialaudio.com ; ; ; PIC Microcontoller Input / Ouput Methods Title "EL wire MIDI controller." list p=p16f877, f=inhx16, mm=ON, st=ON, r=dec, w=0, n=0 include "p16f877.inc" ; use definition file for 16F877 ; LVP= low voltage program ; pwrte= 72msec startup delay ON/OFF ; boden= brown-out reset detect ; wrt= program mem can be written to from app if OFF ; cpd= EEPROM write protected of ON __CONFIG _CP_OFF & _DEBUG_OFF & _LVP_OFF & _WDT_OFF & _HS_OSC & _PWRTE_ON & _BODEN_OFF & _WRT_ENABLE_ON & _CPD_OFF ; ; -------------------- ; USER RAM DEFINITIONS ; -------------------- ; CBLOCK 0x20 ; RAM starts at address 20h ;for Delay10msec_NumTimes Delay10msecNumTimes Delay10msecVar Delay10msecVarPlus1 ;for random number gen routine randomNum ;general ; serialstuff: CharIn InState LEDSelected LEDBrightness ; pushbutton debounce PBDebounceState ; mode of operation: 0= serial/midi control, 1= testmode, 2= autocycle (TODO) OpMode OpMode_TempByte ; MIDI related MIDI_MessageByte ; last message byte received MIDI_NoteNum ; last note # we received MIDI_Velocity ; last velocity we received MIDI_ReceivePhase ; input stream state phase ; These get set based on config switch settings. MIDI_OctavePairMin ; Note# that corresponds to output 0 MIDI_OctavePairMax ; Note# that corresponds to output 23 MIDI_NoteOnMessageByte ; Hex code for note on (0x90= chanl1, 0x91= chan2) MIDI_NoteOffMessageByte ; Hex code for note off (0x80= chanl1, 0x81= chan2) #define LED_MAXLEVEL d'15' ; 16 levels, 0 thru 15. If this changes need ; to change where we handle midi velocity. ; 0 to LED_MAXLEVEL - 0=off, LED_MAXLEVEL=full on (not done with PWM), ; 1 thru (LED_MAXLEVEL-1) is PWM LED00_Level LED01_Level LED02_Level LED03_Level LED04_Level LED05_Level LED06_Level LED07_Level LED08_Level LED09_Level LED10_Level LED11_Level LED12_Level LED13_Level LED14_Level LED15_Level LED16_Level LED17_Level LED18_Level LED19_Level LED20_Level LED21_Level LED22_Level LED23_Level ; the current count for the PWM code for LEDs. Decremented each ISR call, ; we switch state and reload when it hits zero. LED00_PWM LED01_PWM LED02_PWM LED03_PWM LED04_PWM LED05_PWM LED06_PWM LED07_PWM LED08_PWM LED09_PWM LED10_PWM LED11_PWM LED12_PWM LED13_PWM LED14_PWM LED15_PWM LED16_PWM LED17_PWM LED18_PWM LED19_PWM LED20_PWM LED21_PWM LED22_PWM LED23_PWM ; the current on/off state for the LEDs. Bitwise, 0= off LED_0_7_State LED_8_15_State LED_16_23_State ; enable state for the LEDs. Bitwise, 0= disabled (the LED is turned on or ; off explicitly, don't do PWM for it) LED_0_7_Enable LED_8_15_Enable LED_16_23_Enable ;interrupt handler state save and misc. used by parts of the ISR routine _status _fsr _pclath ENDC CBLOCK 0x70 ; "shadowed" across all banks, I believe. Damn picbook ; doesn't say how to do this essential part of an all-purpose ; ISR. _w _OpMode_Timer _OpMode_CurrSpot _OpMode2_CurrBrightness _OpMode_Direction _HeartbeatLedFlash_Low _HeartbeatLedFlash_High _PBDebounce ENDC ; ; ; ; org 0x0000 ; start address = 0000h clrf PCLATH goto Initialize_Everything ; ---------------------------------------- ; interrupt skeleton routine from picbook p500 ; also from p848 org 4 Int movwf _w ; Save the Context Registers movf STATUS, w bcf STATUS, RP0 ; Change to Bank 0 bcf STATUS, RP1 movwf _status movf FSR, w movwf _fsr movf PCLATH, w movwf _pclath clrf PCLATH bcf INTCON, T0IF ; reset timer0 interrupt flag ; the macro for the ISR for a particular LED. ; pass in (args shown for LED 0): ; LED bit # (0) ; LED port (PORTA) ; bit in the port to set (0) ; LEDXX_Level, the RAM address for the LED brightness level ; LEDXX_PWM, the RAM address for the current PWM for the LED ; LED_X_Y_Enable, the RAM address for the enable bit for the LED ; LED_X_Y_State, the RAM address for the status bit for the LED ; two unique labels for use for the particular instance of the macro PWM_ISR macro MACRO_Bit, MACRO_PORT, MACRO_PORTBit, MACRO_LED_Level, MACRO_LED_PWM, MACRO_LED_Enable, MACRO_LED_State, MACRO_ledIsOff, MACRO_doneLabel ; if not enabled, leave now btfss MACRO_LED_Enable, MACRO_Bit goto MACRO_doneLabel ; do PWM stuff decfsz MACRO_LED_PWM, f ; if not hit zero keep counting goto MACRO_doneLabel ; hit zero. switch state, and reload btfss MACRO_LED_State, MACRO_Bit goto MACRO_ledIsOff ; led is on, so now turn off. bcf MACRO_PORT, MACRO_PORTBit ; off bcf MACRO_LED_State, MACRO_Bit ; save off status state ; table lookup, something more? ; could just multiply by 2,4,8,16 to stretch 'em out ; Logic for now: for MACRO_LED_Level of LED_MAXLEVEL-1, we want a single ; off cycle, i.e. MACRO_LED_PWM is *two*. movlw LED_MAXLEVEL +1 movwf MACRO_LED_PWM movf MACRO_LED_Level, w subwf MACRO_LED_PWM, f goto MACRO_doneLabel MACRO_ledIsOff ; led is off, so now turn on. it may be on already, if this one was ; just enabled for PWM. bsf MACRO_PORT, MACRO_PORTBit ; on bsf MACRO_LED_State, MACRO_Bit ; save on status state movf MACRO_LED_Level, w ; table lookup, something more? ; could just multiply by 2,4,8,16 to stretch 'em out movwf MACRO_LED_PWM MACRO_doneLabel endm ;PWM_ISR macro MACRO_Bit, MACRO_PORT, MACRO_PORTBit, MACRO_LED_Level, MACRO_LED_PWM, MACRO_LED_Enable, MACRO_LED_State, MACRO_ledIsOff, MACRO_doneLabel PWM_ISR 0, PORTD, 2, LED00_Level, LED00_PWM, LED_0_7_Enable, LED_0_7_State, x0, y0 PWM_ISR 1, PORTD, 3, LED01_Level, LED01_PWM, LED_0_7_Enable, LED_0_7_State, x1, y1 PWM_ISR 2, PORTD, 4, LED02_Level, LED02_PWM, LED_0_7_Enable, LED_0_7_State, x2, y2 PWM_ISR 3, PORTD, 5, LED03_Level, LED03_PWM, LED_0_7_Enable, LED_0_7_State, x3, y3 PWM_ISR 4, PORTD, 6, LED04_Level, LED04_PWM, LED_0_7_Enable, LED_0_7_State, x4, y4 PWM_ISR 5, PORTD, 7, LED05_Level, LED05_PWM, LED_0_7_Enable, LED_0_7_State, x5, y5 PWM_ISR 6, PORTB, 0, LED06_Level, LED06_PWM, LED_0_7_Enable, LED_0_7_State, x6, y6 PWM_ISR 7, PORTB, 1, LED07_Level, LED07_PWM, LED_0_7_Enable, LED_0_7_State, x7, y7 PWM_ISR 0, PORTB, 2, LED08_Level, LED08_PWM, LED_8_15_Enable, LED_8_15_State, x8, y8 PWM_ISR 1, PORTB, 3, LED09_Level, LED09_PWM, LED_8_15_Enable, LED_8_15_State, x9, y9 PWM_ISR 2, PORTB, 4, LED10_Level, LED10_PWM, LED_8_15_Enable, LED_8_15_State, x10, y10 PWM_ISR 3, PORTB, 5, LED11_Level, LED11_PWM, LED_8_15_Enable, LED_8_15_State, x11, y11 PWM_ISR 4, PORTB, 6, LED12_Level, LED12_PWM, LED_8_15_Enable, LED_8_15_State, x12, y12 PWM_ISR 5, PORTB, 7, LED13_Level, LED13_PWM, LED_8_15_Enable, LED_8_15_State, x13, y13 PWM_ISR 6, PORTD, 1, LED14_Level, LED14_PWM, LED_8_15_Enable, LED_8_15_State, x14, y14 PWM_ISR 7, PORTD, 0, LED15_Level, LED15_PWM, LED_8_15_Enable, LED_8_15_State, x15, y15 PWM_ISR 0, PORTE, 2, LED16_Level, LED16_PWM, LED_16_23_Enable, LED_16_23_State, x16, y16 PWM_ISR 1, PORTE, 1, LED17_Level, LED17_PWM, LED_16_23_Enable, LED_16_23_State, x17, y17 PWM_ISR 2, PORTE, 0, LED18_Level, LED18_PWM, LED_16_23_Enable, LED_16_23_State, x18, y18 PWM_ISR 3, PORTA, 5, LED19_Level, LED19_PWM, LED_16_23_Enable, LED_16_23_State, x19, y19 PWM_ISR 4, PORTA, 3, LED20_Level, LED20_PWM, LED_16_23_Enable, LED_16_23_State, x20, y20 PWM_ISR 5, PORTA, 2, LED21_Level, LED21_PWM, LED_16_23_Enable, LED_16_23_State, x21, y21 PWM_ISR 6, PORTA, 1, LED22_Level, LED22_PWM, LED_16_23_Enable, LED_16_23_State, x22, y22 PWM_ISR 7, PORTA, 0, LED23_Level, LED23_PWM, LED_16_23_Enable, LED_16_23_State, x23, y23 ; --- ; Heartbeat LED counter. Also used for timing in testmode seq code. incfsz _HeartbeatLedFlash_Low, f goto _hopOverInc ; didn't "rollover" - don't inc high part incf _HeartbeatLedFlash_High, f _hopOverInc ; --- ; Flash heartbeat led - alternate on/off each time thru here. a bit of ; Kleverness: ; by testing for both clear and set we get an "if then else with minimal code. ; Also, testing bit 0 means fastest flash rate, test bit 1 for 1/2 that, ; bit 2 for 1/4 that, etc. btfsc _HeartbeatLedFlash_Low, 7 ; we test bit 7 bcf PORTA,4 ; "on" - its open drain, so is wired that PIC sinks ; to ground, so we set LOW to turn on LED. btfss _HeartbeatLedFlash_Low, 7 bsf PORTA,4 ; "off" - set HIGH to turn off ; --- ; Stuff for various testing modes ; Mode 0: do nothing ; Mode 1: all on in order quickly, then all off in order quickly, repeat ; Mode 2: all outputs dim->bright->dim cycling ; Mode 3: chase ; Mode 4: random ; --- movf OpMode, w sublw d'1' ; mode 1? btfsc STATUS, Z goto _isr_opmode1 ; yep, mode 1 movf OpMode, w sublw d'2' ; mode 2? btfsc STATUS, Z goto _isr_opmode2 ; yep, mode 2 movf OpMode, w sublw d'3' ; mode 3? btfsc STATUS, Z goto _isr_opmode3 ; yep, mode 3 movf OpMode, w sublw d'4' btfss STATUS, Z goto _isr_Continue ; nope, not in mode 4, continue onward ; --- ; In mode 4: ; fastspeedblink orange LED btfsc _HeartbeatLedFlash_Low, 6 bsf PORTC, 5 btfss _HeartbeatLedFlash_Low, 6 bcf PORTC, 5 ; pick a new random one? decfsz _OpMode_Timer, f goto _isr_Continue ; yes. movlw d'18' ; reset timer movwf _OpMode_Timer ; now turn off current one movf _OpMode_CurrSpot, w movwf LEDSelected movlw 0 movwf LEDBrightness call UpdateLED ; don't assume w, LEDSelected, nor LEDBrightness preserved ; get another one _isropmode4Loop call GetRandom ; is it between 0 and 23 inclusive? ; (p471 in book gives code for all types of comparison checks) movlw d'24' subwf randomNum, w ; subtract 24. if not negative result, is out of bounds, ; so carry should be *clear* (indicating negative) if legit. btfsc STATUS, C goto _isropmode4Loop movf randomNum, w movwf _OpMode_CurrSpot ; now lightup one pointed to by OpMode_3and4_CurrSpot movf _OpMode_CurrSpot, w movwf LEDSelected movlw LED_MAXLEVEL movwf LEDBrightness call UpdateLED ; don't assume w, LEDSelected, nor LEDBrightness preserved ; done goto _isr_Continue ; --- ; In mode 3: _isr_opmode3 ; midspeedbink orange LED btfsc _HeartbeatLedFlash_Low, 7 bsf PORTC, 5 btfss _HeartbeatLedFlash_Low, 7 bcf PORTC, 5 ; update to next step? decfsz _OpMode_Timer, f goto _isr_Continue ; yes. movlw d'17' ; reset timer movwf _OpMode_Timer ; now turn off current one movf _OpMode_CurrSpot, w movwf LEDSelected movlw 0 movwf LEDBrightness call UpdateLED ; don't assume w, LEDSelected, nor LEDBrightness preserved incf _OpMode_CurrSpot, f movf _OpMode_CurrSpot, w sublw d'24' ; wraparound back to 0? btfss STATUS, Z goto _mode3ISRskip ; yes - wrap back to 0 movlw 0 movwf _OpMode_CurrSpot _mode3ISRskip ; now lightup one pointed to by OpMode3_CurrSpot movf _OpMode_CurrSpot, w movwf LEDSelected movlw LED_MAXLEVEL movwf LEDBrightness call UpdateLED ; don't assume w, LEDSelected, nor LEDBrightness preserved ; done goto _isr_Continue ; --- ; In mode 2: _isr_opmode2 ; slowbink orange LED btfsc _HeartbeatLedFlash_High, 0 bsf PORTC, 5 btfss _HeartbeatLedFlash_High, 0 bcf PORTC, 5 ; update ELwires to next brightness level? decfsz _OpMode_Timer, f goto _isr_Continue ; yes. movlw d'25' ; reset timer movwf _OpMode_Timer movlw d'0' movwf OpMode_TempByte _loopISRmode2 movf OpMode_TempByte, w movwf LEDSelected movf _OpMode2_CurrBrightness, w movwf LEDBrightness call UpdateLED ; don't assume w, LEDSelected, nor LEDBrightness preserved incf OpMode_TempByte, f movf OpMode_TempByte, w sublw d'24' btfss STATUS, Z goto _loopISRmode2 btfsc _OpMode_Direction, 0 goto _mode2_decDir ; incrementing up incf _OpMode2_CurrBrightness, f movf _OpMode2_CurrBrightness, w sublw LED_MAXLEVEL btfss STATUS, Z goto _isr_Continue ; switch dir next time -> head down dimmer bsf _OpMode_Direction, 0 goto _isr_Continue _mode2_decDir decf _OpMode2_CurrBrightness, f movf _OpMode2_CurrBrightness, w btfss STATUS, Z goto _isr_Continue ; switch dir -> head up brighter bcf _OpMode_Direction, 0 ; done goto _isr_Continue ; --- ; In mode 1: _isr_opmode1 ; orange LED on bsf PORTC, 5 ; update to next step? decfsz _OpMode_Timer, f goto _isr_Continue ; yes. movlw d'15' ; reset timer movwf _OpMode_Timer ; now turn on movf _OpMode_CurrSpot, w movwf LEDSelected btfss _OpMode_Direction, 0 movlw LED_MAXLEVEL btfsc _OpMode_Direction, 0 movlw d'0' movwf LEDBrightness call UpdateLED ; don't assume w, LEDSelected, nor LEDBrightness preserved incf _OpMode_CurrSpot, f movf _OpMode_CurrSpot, w sublw d'24' ; wraparound back to 0? btfss STATUS, Z goto _mode1ISRskip ; yes - wrap back to 0 movlw 0 movwf _OpMode_CurrSpot ; toggle on/off movlw h'FF' xorwf _OpMode_Direction, f _mode1ISRskip ; done _isr_Continue ; --- ; Pushbutton debounce "timer" - decrement if not already zero movf _PBDebounce, w btfss STATUS, Z decf _PBDebounce, f ; --- ; restore & leave movf _pclath, w ; Restore the Context Registers movwf PCLATH movf _fsr, w movwf FSR movf _status, w movwf STATUS swapf _w, f ;trickery - load w w/o changing STATUS reg Z flag swapf _w, w retfie ; ; ; ---------------------------------------- ; Initialize_Everything movlw b'00000000' ; all port pins = low movwf PORTA movlw b'00000000' movwf PORTB movlw b'00000000' movwf PORTC movlw b'00000000' movwf PORTD movlw b'00000000' movwf PORTE bsf STATUS,RP0 ; set RAM Page 1 for TRIS registers ; Hardware usage: ; ; Switches are pulled up, so switch "ON" = input Low. ; ; PORTA, #4: Heartbeat LED (open drain, driven low, see p192) ; PORTC, #0: Switch #1: Baud rate switch ("ON" = 38400, "OFF" = midi), ; PORTC, #1: Switch #2: Midi Channel select. Switch reads "OFF" = chan 1, ; "ON" = chan 2) ; PORTC, #2: ; PORTC, #3: 2&3 are octave-pair select: ; Off, Off= lowest ; Off, On= next up from lowest ; On, Off= down from highest ; On, On= highest ; PORTC, #4: Pushbutton to cycle thru test / autopattern modes (pressed = ; input low) ; PORTC, #5: Orange "operating mode" LED (Lit when PORTC #5 is high) ; PORTC, #6: Serial Transmit ; PORTC, #7: Serial Receive ; All other port bits are for driving triacs: ; 00: PORTD, #2 (PIC pin 21) ; 01: PORTD, #3 (PIC pin 22) ; 02: PORTD, #4 (PIC pin 27) ; 03: PORTD, #5 (PIC pin 28) ; 04: PORTD, #6 (PIC pin 29) ; 05: PORTD, #7 (PIC pin 30) ; 06: PORTB, #0 (PIC pin 33) ; 07: PORTB, #1 (PIC pin 34) ; 08: PORTB, #2 (PIC pin 35) ; 09: PORTB, #3 (PIC pin 36) ; 10: PORTB, #4 (PIC pin 37) ; 11: PORTB, #5 (PIC pin 38) ; 12: PORTB, #6 (PIC pin 39) ; 13: PORTB, #7 (PIC pin 40) ; 14: PORTD, #1 (PIC pin 20) ; 15: PORTD, #0 (PIC pin 19) ; 16: PORTE, #2 (PIC pin 10) ; 17: PORTE, #1 (PIC pin 09) ; 18: PORTE, #0 (PIC pin 08) ; 19: PORTA, #5 (PIC pin 07) ; 20: PORTA, #3 (PIC pin 05) ; 21: PORTA, #2 (PIC pin 04) ; 22: PORTA, #1 (PIC pin 03) ; 23: PORTA, #0 (PIC pin 02) movlw b'00000000' ; all IO pins = outputs movwf TRISA ^ 0x80 movlw b'00000000' movwf TRISB ^ 0x80 movlw b'10011111' ; PORTC bits 0,1,2,3,4 & 7 are inputs. movwf TRISC ^ 0x80 movlw b'00000000' movwf TRISD ^ 0x80 movlw b'00000000' movwf TRISE ^ 0x80 movlw b'00000110' ; all analog pins = digital movwf ADCON1 ^ 0x80 bcf STATUS,RP0 ; back to RAM page 0 ; init stuff for interrupt routine code movlw 0 movwf _HeartbeatLedFlash_Low movwf _HeartbeatLedFlash_High movwf _PBDebounce ; ** ; setup timer - see p201 ; 2mhz, 256-count, prescale of 8, is ~977 interrupts/sec. I think. ; may need to change bsf STATUS, RP0 ; goto bank 1 ; movlw b'11010000' ; Timer0 instruction clock & prescaler (div 1) ; movlw b'11010001' ; Timer0 instruction clock & prescaler (div 2) ; movlw b'11010010' ; Timer0 instruction clock & prescaler (div 4) ; movlw b'11010011' ; Timer0 instruction clock & prescaler (div 8) ;16: at 1Khz driverfreq works for everything but level 1 ; at 6Khz driverfreqworks for level 1 fine too movlw b'11010100' ; Timer0 instruction clock & prescaler (div 16) ;32 is just a little blinky but works for level 1 (@1Khz). (64 is useless) ; movlw b'11010101' ; Timer0 instruction clock & prescaler (div 32) movwf OPTION_REG ^ 0x80 ; set timer bcf STATUS, RP0 clrf TMR0 ; start timer from scratch ;p197 for INTCON ;done below after everything initialized! ; movlw (1 << GIE) + (1 << T0IE) ; movlw b'10100000' ; movwf INTCON ; enable interrupts ; ** ; setup serial transmitter ; p231,235/236 with mods ; 2mhz, highspeedmode, 9600bps, Clockcount= 2Mhz/(DataRate * 16 * (4 ^ 0)) -1 ; clockcount= [2,000,000 / (9600 * 16)] -1 ; clockcount= 12.02 (goes into SPBRG) ; ; 2mhz, highspeedmode, 2400bps, Clockcount= 2Mhz/(DataRate * 16 * (4 ^ 0)) -1 ; clockcount= [2,000,000 / (2400 * 16)] -1 ; clockcount= 51.08 (goes into SPBRG) ; ; 2mhz, lowspeedmode, 300bps, Clockcount= 2Mhz/(DataRate * 16 * (4 ^ 1)) -1 ; clockcount= [2,000,000 / (300 * 16 *4)] -1 ; clockcount= 103.16 (goes into SPBRG) ; ; 20mhz, lowspeedmode, 2400bps, Clockcount= 20Mhz/(DataRate * 16 * (4 ^ 1)) -1 ; clockcount= [20,000,000 / (2400 * 16 *4)] -1 ; clockcount= (20,000,000 / 153600) -1 ; clockcount= 129.21 (goes into SPBRG) ; ; 12mhz, lowspeedmode, 38400bps, Clockcount= 12Mhz/(DataRate * 16 * (4 ^ 1)) -1 ; clockcount= [12,000,000 / (38400 * 16 *4)] -1 ; clockcount= (12,000,000 / 2457600) -1 ; clockcount= 3.88 (d'4' goes into SPBRG) ; 12mhz, highspeedmode, 38400bps, Clockcount= 12Mhz/(DataRate * 16 * (4 ^ 0)) -1 ; clockcount= [12,000,000 / (38400 * 16)] -1 ; clockcount= (12,000,000 / 614400) -1 ; clockcount= 19.53 (d'19' goes into SPBRG) ; 20mhz, lowspeedmode, 38400bps, Clockcount= 20Mhz/(DataRate * 16 * (4 ^ 1)) -1 ; clockcount= [20,000,000 / (38400 * 16 *4)] -1 ; clockcount= (20,000,000 / 2457600) -1 ; clockcount= 7.14 (d'7' goes into SPBRG) ; (actual rate= 39062.5, 662bps fast, 1.72% off) ; ; 20mhz, highspeedmode, 38400bps, Clockcount= 20Mhz/(DataRate * 16 * (4 ^ 0)) -1 ; clockcount= [20,000,000 / (38400 * 16)] -1 ; clockcount= (20,000,000 / (614400) -1 ; clockcount= 31.55 (d'32' goes into SPBRG) ; (actual rate= 37878.8, 522bps slow, 1.35% off) ; ; 20mhz, lowspeedmode, 31250bps, Clockcount= 20Mhz/(DataRate * 16 * (4 ^ 1)) -1 ; clockcount= [20,000,000 / (31250 * 16 *4)] -1 ; clockcount= (20,000,000 / 2000000) -1 ; clockcount= 9.00 (d'9' goes into SPBRG) ; (exact rate match) ; ; 20mhz, highspeedmode, 31250bps, Clockcount= 2Mhz/(DataRate * 16 * (4 ^ 0)) -1 ; clockcount= [20,000,000 / (31250 * 16)] -1 ; clockcount= (20,000,000 / (500000) -1 ; clockcount= 39.00 (d'39' goes into SPBRG) ; (exact rate match) ; bsf STATUS, RP0 ; goto bank 1 bcf TXSTA ^ 0x80, SYNC ; not in sync mode ; bcf TXSTA ^ 0x80, BRGH ; BRGH=0 (lowspeed mode) bsf TXSTA ^ 0x80, BRGH ; BRGH=1 (highspeed mode) ; PORTC, #0: Switch #1: Baud rate switch ("ON" = 38400, "OFF" = midi), ; we read "ON" as LOW and "OFF" as HIGH. bcf STATUS, RP0 ; goto bank 0 btfsc PORTC, 0 goto _midiBaud ;38400 movlw d'32' goto _setBaud _midiBaud ;31250 - midi movlw d'39' _setBaud bsf STATUS, RP0 ; goto bank 1 movwf SPBRG ^ 0x80 ; set USART data rate bcf STATUS, RP0 ; goto bank 0 bsf RCSTA, SPEN ; enable serial port bcf RCSTA, RX9 ; 8 bits to receive bsf RCSTA, CREN ; enable serial port receive bsf STATUS, RP0 ; goto bank 1 bcf TXSTA ^ 0x80, TX9 ; 8 bits to send bsf TXSTA ^ 0x80, TXEN ; enable data xmit bcf STATUS, RP0 ; goto bank 0 ; --------------------------------- movlw 0 movwf InState movwf PBDebounceState movwf OpMode movlw b'10010110' ; whatever... movwf randomNum call Init_MidiStuff movlw 0 movwf LED00_Level movwf LED01_Level movwf LED02_Level movwf LED03_Level movwf LED04_Level movwf LED05_Level movwf LED06_Level movwf LED07_Level movwf LED08_Level movwf LED09_Level movwf LED10_Level movwf LED11_Level movwf LED12_Level movwf LED13_Level movwf LED14_Level movwf LED15_Level movwf LED16_Level movwf LED17_Level movwf LED18_Level movwf LED19_Level movwf LED20_Level movwf LED21_Level movwf LED22_Level movwf LED23_Level movwf LED_0_7_State movwf LED_0_7_Enable movwf LED_8_15_State movwf LED_8_15_Enable movwf LED_16_23_State movwf LED_16_23_Enable ; now enable interrupts ; see p197 for INTCON movlw (1 << GIE) + (1 << T0IE) movwf INTCON ; enable interrupts ;Pertinent midi info: ;Ignore anything that we specifically don't test for. ;"Voice messages": Status byte (the base # plus channel number), followed ; by data bytes, data is 0-127. ; 0x90: note on, then pitch (note #), then velocity. ; 0x80: note off, then pitch (note #), then velocity. ; 0xB0: "Parameter" - special messages some of which are applicable - like ; "everything off". See usenet midi primer. TODO ; ; 0xF8 - 0xFF: "real-time" messages (one byte each only) that can occur at ; any time, including between note# and velocity. These should all be ; ignored. (except reset?) TODO ;Note on w/velocity zero is the same as note off. ;"Running status byte" - if status byte not sent again, the previous is ; implied. ; 0x90, 0x00, 0x7F : NoteOn note 0 max velocity ; 0x01, 0x3F : NoteOn note 1 1/2 velocity ; 0x80, 0x00, 0xXX : NoteOff note 0 velocity whatever ; 0x01, 0xXX : NoteOff note 1 velocity whatever ; Algorithm: ; Wait for 0x80/81, 0x90/91 ; Anything else: ignore, its an error or something. ; MIDI_MessageByte is the last message byte we received ; MIDI_NoteNum is the last note we received ; MIDI_Velocity is the last velocity of the note we last received ; MIDI_ReceivePhase is the phase we're in: ; 0 = Phase 0: Expect status byte OR note# w/implied (last) stat byte: ; (a) if status byte, save it to MIDI_MessageByte and goto phase 0. ; (b) if note byte, save it to MIDI_NoteNum, goto phase 1. ; (c) if > 0x7F and not 0x80/90 or 0x81/91, error, goto phase 0 ; 1 = Phase 1: Expect velocity: ; (a) if > 0x7F, error, goto phase 0. ; (b) else save it to MIDI_Velocity, call Process_MIDI, goto phase 0. ; ==== main loop ==== ; Loop ; -- ; --- check for pushbutton pressed ; -- call Pushbutton_Pressed sublw d'1' btfss STATUS, Z goto _notPressed ; button pressed. Adjust opmode. bcf INTCON, GIE ; interrupts off while we fiddle with OpMode incf OpMode, f movf OpMode, w sublw d'5' ; 5 opmodes, #0, 1,2,3,4. If OpMode is 5, we incremented ; from mode 4 back to 0. btfsc STATUS, Z goto _opmode0 movf OpMode, w sublw d'1' btfsc STATUS, Z goto _opmode1 movf OpMode, w sublw d'2' btfsc STATUS, Z goto _opmode2 movf OpMode, w sublw d'3' btfsc STATUS, Z goto _opmode3 ; we must be entering mode 4 goto _opmode4 ; ---- ; Mode 0: the main MIDI mode ; ---- _opmode0 bsf INTCON, GIE ; interrupts back on (entering opmode 0 is no problem, ; since we do nothing in this mode in the ISR) ; cycle back to mode 0 movlw 0 movwf OpMode bcf PORTC, 5 ; Mode 0: normal MIDI operating mode ; turn all outputs off call _AllOutputsOff goto _notPressed ; ---- ; Mode 1: testmode 1: all outputs on ; ---- _opmode1 call _AllOutputsOff movlw d'0' movwf _OpMode_Direction movwf _OpMode_CurrSpot movlw d'1' ; run opmode 1 ISR code immediately movwf _OpMode_Timer ; everything happens in ISR bsf INTCON, GIE ; interrupts back on (opmode 2 code in ISR is ok to ; run now) goto _notPressed ; ---- ; Mode 2: testmode 2: all outputs dim->bright->dim cycling ; timing of LEDs and orange mode LED blink happen in ISR ; ---- _opmode2 call _AllOutputsOff movlw d'0' movwf _OpMode2_CurrBrightness movwf _OpMode_Direction movlw d'1' ; run opmode 2 ISR code immediately movwf _OpMode_Timer ; everything happens in ISR bsf INTCON, GIE ; interrupts back on (opmode 2 code in ISR is ok to ; run now) goto _notPressed ; ---- ; Mode 3: testmode 3: simple chase ; timing of LEDs and orange mode LED blink happen in ISR ; ---- _opmode3 call _AllOutputsOff movlw d'23' movwf _OpMode_CurrSpot movlw d'1' ; run opmode 3 ISR code immediately, and we will start with LED0 movwf _OpMode_Timer ; everything is in ISR bsf INTCON, GIE ; interrupts back on (opmode 3 code in ISR is ok to ; run now) goto _notPressed ; ---- ; Mode 4: testmode 4: random ; timing of LEDs and orange mode LED blink happen in ISR ; ---- _opmode4 call _AllOutputsOff movlw d'0' movwf _OpMode_CurrSpot movlw d'1' ; run opmode 4 ISR code immediately movwf _OpMode_Timer ; everything is in ISR bsf INTCON, GIE ; interrupts back on (opmode 4 code in ISR is ok to ; run now) goto _notPressed ; Util routine used in modes above _AllOutputsOff movlw d'0' movwf OpMode_TempByte _loopAllOff movf OpMode_TempByte, w movwf LEDSelected movlw d'0' movwf LEDBrightness call UpdateLED ; don't assume w, LEDSelected, nor LEDBrightness preserved incf OpMode_TempByte, f movf OpMode_TempByte, w sublw d'24' btfss STATUS, Z goto _loopAllOff return ; ; Main code now ; _notPressed ; -------------------------- ; --- begin char receive --- ; overrun error ? btfss RCSTA, OERR goto _checkFrameErr ; yes, clear and start over bcf RCSTA, CREN bsf RCSTA, CREN ; call ShowMIDIError goto Loop ; framing error ? _checkFrameErr btfss RCSTA, FERR goto _checkForChar ; yes, clear and start over movf RCREG, w ; call ShowMIDIError goto Loop ; now check for char received... _checkForChar btfsc PIR1, RCIF goto _gotAChar ;no char, start over goto Loop ; -- we have a char -- _gotAChar movf RCREG, w movwf CharIn ; ==debug hack checks ; movlw 0xA0 ; subwf CharIn, w ; btfss STATUS, Z ; goto _dbghack_check2 ; ; ; do debug op #1 ; ; turn all EL outputs on ; bsf PORTD, 2 ; bsf PORTD, 3 ; bsf PORTD, 4 ; bsf PORTD, 5 ; bsf PORTD, 6 ; bsf PORTD, 7 ; bsf PORTB, 0 ; bsf PORTB, 1 ; bsf PORTB, 2 ; bsf PORTB, 3 ; bsf PORTB, 4 ; bsf PORTB, 5 ; ; bsf PORTB, 6 ; bsf PORTB, 7 ; bsf PORTD, 0 ; bsf PORTD, 1 ; bsf PORTE, 2 ; bsf PORTE, 1 ; bsf PORTE, 0 ; bsf PORTA, 5 ; bsf PORTA, 3 ; bsf PORTA, 2 ; bsf PORTA, 1 ; bsf PORTA, 0 ; ; goto _Midi_Mainline_Done ; ; ;_dbghack_check2 ; movlw 0xA1 ; subwf CharIn, w ; btfss STATUS, Z ; goto _normalMIDIin ; ; ; do debug op #2 ; ; turn all EL outputs off ; bcf PORTD, 2 ; bcf PORTD, 3 ; bcf PORTD, 4 ; bcf PORTD, 5 ; bcf PORTD, 6 ; bcf PORTD, 7 ; bcf PORTB, 0 ; bcf PORTB, 1 ; bcf PORTB, 2 ; bcf PORTB, 3 ; bcf PORTB, 4 ; bcf PORTB, 5 ; ; bcf PORTB, 6 ; bcf PORTB, 7 ; bcf PORTD, 0 ; bcf PORTD, 1 ; bcf PORTE, 2 ; bcf PORTE, 1 ; bcf PORTE, 0 ; bcf PORTA, 5 ; bcf PORTA, 3 ; bcf PORTA, 2 ; bcf PORTA, 1 ; bcf PORTA, 0 ; goto _Midi_Mainline_Done _normalMIDIin ; --- MIDI state machine --- ; ; First: if it is a "real-time" message, just ignore it and wait for next ; char in. This is 0xF8-0xFF. ; 0xFF is "reset". We'll honor this one. movlw 0xFF subwf CharIn, w btfss STATUS, Z goto _realtime_midi_msg_check ; we got a reset ; TODO goto _Midi_Mainline_Done _realtime_midi_msg_check ; if CharIn >= 0xF8 ignore it. (see p471 for compare templates) movlw 0xF8 subwf CharIn, w btfsc STATUS, C goto _Midi_Mainline_Done ; do nothing with any of them _processMidi ; branch on what phase we are in movf MIDI_ReceivePhase, w btfss STATUS, Z goto _doPhase1 ; phase 0 movf MIDI_NoteOffMessageByte,w subwf CharIn, w btfss STATUS, Z goto _ph0_check_noteon ; received noteoff message. Save and stay in phase 0. _saveStat movf CharIn, w movwf MIDI_MessageByte goto _Midi_Mainline_Done _ph0_check_noteon movf MIDI_NoteOnMessageByte, w subwf CharIn, w btfss STATUS, Z goto _ph0_checkNoteByte ; received noteon message. Save and stay in phase 0. goto _saveStat _ph0_checkNoteByte ; TODO: ; if > 0x7F, its a message byte we don't interpret. Need to ignore it ; *and all note/velocity/whatever after it* until we get a 0x80/81/90/91. ; See the usenet midi document... ; ; check that it is within the octave-pair range selected by DIP switches. ; ; (p471 in book gives code for all types of comparison checks) ; ; ; If CharIn > MIDI_OctavePairMax, goto error ; movf CharIn, w ; subwf MIDI_OctavePairMax, w ; btfss STATUS, C ; goto _midiError_gotophase0 ; TODO - eat velocity byte ? ; ; ; If CharIn < MIDI_OctavePairMin, goto error ; movf MIDI_OctavePairMin, w ; subwf CharIn, w ; btfss STATUS, C ; goto _midiError_gotophase0 ; TODO - eat velocity byte ? ; ; ; okay. its legit. ;NEW CODE ;don't do the octave pair check now. Do it right before setting ;the triac. this way we handle the velocity whether or not the note ;is within range. if the note isn't in range for this board, we just ;ignore it then. movf CharIn, w movwf MIDI_NoteNum movlw 1 ; goto phase 1 -> expecting velocity movwf MIDI_ReceivePhase goto _Midi_Mainline_Done _doPhase1 ; CharIn is char we just received, and we are in phase 1. ; Check that it is <= 0x7F btfss CharIn, 7 goto _phase1_Ok ; Its not. Error. Go to phase 0. TODO - also attempt to interpret as ; a message? _midiError_gotophase0 call ShowMIDIError _gotoPhase0 movlw 0 movwf MIDI_ReceivePhase goto _Midi_Mainline_Done _phase1_Ok movf CharIn, w movwf MIDI_Velocity ; ; OK! We have a MIDI "message". Do it, and go back to phase 0 for the next. ; call DoMIDICommand goto _gotoPhase0 _Midi_Mainline_Done ; nothing special needs to be done ... goto Loop ; ; Make the MIDI message happen. ; DoMIDICommand ; check that it is within the octave-pair range selected by DIP switches. ; (p471 in book gives code for all types of comparison checks) ; "If CharIn > MIDI_OctavePairMax, goto error" movf MIDI_NoteNum, w subwf MIDI_OctavePairMax, w btfss STATUS, C return ; not in our range, do nothing ; "If CharIn < MIDI_OctavePairMin, goto error" movf MIDI_OctavePairMin, w subwf MIDI_NoteNum, w btfss STATUS, C return ; not in our range, do nothing ; ok. note # is in our range. Do it. ; subtract out the octave-pair we are using movf MIDI_OctavePairMin, w subwf MIDI_NoteNum, w movwf LEDSelected ; if MIDI_MessageByte is noteoff, or its noteon and MIDI_Velocity is 0, ; then turn LED off. movf MIDI_NoteOffMessageByte,w subwf MIDI_MessageByte, w btfsc STATUS, Z goto _doMIDI_turnoff ; its noteon. Check if velocity is zero. movf MIDI_Velocity, w btfsc STATUS, Z goto _doMIDI_turnoff ; we have note on with velocity > 0 ; level is MIDI_Velocity (1->0x7F). Convert to 1-15. movf MIDI_Velocity, w movwf LEDBrightness bcf STATUS, C rrf LEDBrightness, f bcf STATUS, C rrf LEDBrightness, f bcf STATUS, C rrf LEDBrightness, f ; ensure it isn't zero. a non-zero velocity should be our lowest level. movf LEDBrightness, w btfss STATUS, Z goto _doMIDI_doItAndLeave movlw 1 movwf LEDBrightness _doMIDI_doItAndLeave ; final test. If not in opmode 0 (==MIDI), don't actually update anything. ; we put the check here so that the midi state machine is always running, ; so it will be in the correct state when we re-enter opmode 0. movf OpMode, w btfsc STATUS, Z call UpdateLED return _doMIDI_turnoff movlw 0 movwf LEDBrightness goto _doMIDI_doItAndLeave ShowMIDIError ; todo - flash LED or something? for now, permanently turn on. bsf PORTC, 5 return ; Called on power-up Init_MidiStuff ; Set channel & octavepair to work with. ; PORTC, #1: channel 1 (0x80/0x90) if "OFF", channel 2 (0x81/0x91) if "ON". ; (turning switch on connects PIC input to ground, so its active-low logic) movlw 0x80 movwf MIDI_NoteOffMessageByte movlw 0x90 movwf MIDI_NoteOnMessageByte btfsc PORTC, 1 ; "active low" logic: if switch reads ON, we get low input goto _doOctPair ; do channel 2 - increment both incf MIDI_NoteOffMessageByte, f incf MIDI_NoteOnMessageByte, f _doOctPair ; MIDI_OctavePairMin ; Note# that corresponds to output 0 ; MIDI_OctavePairMax ; Note# that corresponds to output 23 ; PORTC, #2: ; PORTC, #3: 2&3 are octave-pair select: ; (portc #2), (portc #3) ; (sw labeled 3), (4) ; Off, Off= lowest ; Off, On= next up from lowest ; On, Off= down from highest ; On, On= highest btfss PORTC, 2 goto _Sw3_On_Sw4_Unknown btfss PORTC, 3 goto _Sw3_Off_Sw4_On ; Sw3_Off_Sw4_Off ; OctavePair 0 movlw 0 movwf MIDI_OctavePairMin movlw d'23' movwf MIDI_OctavePairMax goto _continueMidiInit _Sw3_Off_Sw4_On ; OctavePair 1 movlw d'24' movwf MIDI_OctavePairMin movlw d'47' movwf MIDI_OctavePairMax goto _continueMidiInit _Sw3_On_Sw4_Unknown btfss PORTC, 3 goto _Sw3_On_Sw4_On ; Sw3_On_Sw4_Off ; OctavePair 2 movlw d'48' movwf MIDI_OctavePairMin movlw d'71' movwf MIDI_OctavePairMax goto _continueMidiInit _Sw3_On_Sw4_On ; OctavePair 3 movlw d'72' movwf MIDI_OctavePairMin movlw d'95' movwf MIDI_OctavePairMax _continueMidiInit ; movf MIDI_NoteOffMessageByte,w ; Start with noteoff so if garbage ; comes down the line (for the very first characters we receive) ; that looks like legit notes/velocities they will all be ; interpreted as note-off. ; new. start with note-on. the casio only sends one note-on message with ; the first keypress; after that, its all implied. if the casio is turned ; on, some keys hit, then the EL board is turned on, we interpret everything ; as note-off with the above. so. change stuff to work with the casio. movf MIDI_NoteOnMessageByte,w movwf MIDI_MessageByte movlw 0 movwf MIDI_NoteNum movwf MIDI_Velocity movwf MIDI_ReceivePhase return ;-------- ;macro for handling LED level change ;-------- UPDATE_LED macro MACRO_UniqueNum, MACRO_Bit, MACRO_PORT, MACRO_PORTBit, MACRO_LED_PWM, MACRO_LED_Level, MACRO_LED_Enable, MACRO_LED_State, MACRO_continueLabel movf LEDBrightness, w btfss STATUS, Z goto _#v(MACRO_UniqueNum)_notZeroLabel ;LED is set to zero bcf MACRO_LED_Enable, MACRO_Bit ; disable from PWM bcf MACRO_PORT, MACRO_PORTBit ; turn it off movf LEDBrightness, w movwf MACRO_LED_Level ; save level (0) goto MACRO_continueLabel _#v(MACRO_UniqueNum)_notZeroLabel movlw LED_MAXLEVEL subwf LEDBrightness, w btfss STATUS, Z goto _#v(MACRO_UniqueNum)_notMaxLevelLabel ;is set to max level - i.e. on 100% bcf MACRO_LED_Enable, MACRO_Bit ; disable from PWM bsf MACRO_PORT, MACRO_PORTBit ; on movf LEDBrightness, w movwf MACRO_LED_Level ; save level (max) goto MACRO_continueLabel _#v(MACRO_UniqueNum)_notMaxLevelLabel ;not zero nor max level. movf LEDBrightness, w movwf MACRO_LED_Level ; save level. Don't matter if is doing PWM or not. ;are we currently doing PWM for this LED? btfsc MACRO_LED_Enable, MACRO_Bit goto MACRO_continueLabel ;Setup for PWM. Do trick to have the "on" code ;run in next interrupt. Keep all logic in there, not here. ;bcf PORTA, 0 ; off ...don't turn off, it may be already, and if it is ; on, this avoids an (admittedly very brief) "blink" bcf MACRO_LED_State, MACRO_Bit ; mark that its off. its okay if it ; is actually on... movlw 1 movwf MACRO_LED_PWM ; will flip into "on" state next ISR bsf MACRO_LED_Enable, MACRO_Bit ; enable PWM for this LED now MACRO_continueLabel endm ; -------------------------------------- ; LEDSelected is the LED to change (0-23) ; LEDBrightness is the new level (0-9 or whatever) UpdateLED movlw HIGH _UpdateLED_JumpTable ; get current 256 instruction block movwf PCLATH ; store it so next jump is correct movf LEDSelected, w ; compute offset within the addlw LOW _UpdateLED_JumpTable ; 256 instruction block btfsc STATUS, C incf PCLATH, f ; if in next, increment PCLATH movwf PCL ; write correct address to the program counter _UpdateLED_JumpTable goto _UpdateLED_Led00 goto _UpdateLED_Led01 goto _UpdateLED_Led02 goto _UpdateLED_Led03 goto _UpdateLED_Led04 goto _UpdateLED_Led05 goto _UpdateLED_Led06 goto _UpdateLED_Led07 goto _UpdateLED_Led08 goto _UpdateLED_Led09 goto _UpdateLED_Led10 goto _UpdateLED_Led11 goto _UpdateLED_Led12 goto _UpdateLED_Led13 goto _UpdateLED_Led14 goto _UpdateLED_Led15 goto _UpdateLED_Led16 goto _UpdateLED_Led17 goto _UpdateLED_Led18 goto _UpdateLED_Led19 goto _UpdateLED_Led20 goto _UpdateLED_Led21 goto _UpdateLED_Led22 goto _UpdateLED_Led23 ;UPDATE_LED macro MACRO_UniqueNum, MACRO_LEDBit, MACRO_PORT, MACRO_PORTBit, MACRO_PWM, MACRO_LED_Level, MACRO_LED_Enable, MACRO_LED_State, MACRO_continueLabel _UpdateLED_Led00 UPDATE_LED 0, 0, PORTD, 2, LED00_PWM, LED00_Level, LED_0_7_Enable, LED_0_7_State, _lab0 return _UpdateLED_Led01 UPDATE_LED 1, 1, PORTD, 3, LED01_PWM, LED01_Level, LED_0_7_Enable, LED_0_7_State, _lab1 return _UpdateLED_Led02 UPDATE_LED 2, 2, PORTD, 4, LED02_PWM, LED02_Level, LED_0_7_Enable, LED_0_7_State, _lab2 return _UpdateLED_Led03 UPDATE_LED 3, 3, PORTD, 5, LED03_PWM, LED03_Level, LED_0_7_Enable, LED_0_7_State, _lab3 return _UpdateLED_Led04 UPDATE_LED 4, 4, PORTD, 6, LED04_PWM, LED04_Level, LED_0_7_Enable, LED_0_7_State, _lab4 return _UpdateLED_Led05 UPDATE_LED 5, 5, PORTD, 7, LED05_PWM, LED05_Level, LED_0_7_Enable, LED_0_7_State, _lab5 return _UpdateLED_Led06 UPDATE_LED 6, 6, PORTB, 0, LED06_PWM, LED06_Level, LED_0_7_Enable, LED_0_7_State, _lab6 return _UpdateLED_Led07 UPDATE_LED 7, 7, PORTB, 1, LED07_PWM, LED07_Level, LED_0_7_Enable, LED_0_7_State, _lab7 return _UpdateLED_Led08 UPDATE_LED 8, 0, PORTB, 2, LED08_PWM, LED08_Level, LED_8_15_Enable, LED_8_15_State, _lab8 return _UpdateLED_Led09 UPDATE_LED 9, 1, PORTB, 3, LED09_PWM, LED09_Level, LED_8_15_Enable, LED_8_15_State, _lab9 return _UpdateLED_Led10 UPDATE_LED 10, 2, PORTB, 4, LED10_PWM, LED10_Level, LED_8_15_Enable, LED_8_15_State, _lab10 return _UpdateLED_Led11 UPDATE_LED 11, 3, PORTB, 5, LED11_PWM, LED11_Level, LED_8_15_Enable, LED_8_15_State, _lab11 return _UpdateLED_Led12 UPDATE_LED 12, 4, PORTB, 6, LED12_PWM, LED12_Level, LED_8_15_Enable, LED_8_15_State, _lab12 return _UpdateLED_Led13 UPDATE_LED 13, 5, PORTB, 7, LED13_PWM, LED13_Level, LED_8_15_Enable, LED_8_15_State, _lab13 return _UpdateLED_Led14 UPDATE_LED 14, 6, PORTD, 1, LED14_PWM, LED14_Level, LED_8_15_Enable, LED_8_15_State, _lab14 return _UpdateLED_Led15 UPDATE_LED 15, 7, PORTD, 0, LED15_PWM, LED15_Level, LED_8_15_Enable, LED_8_15_State, _lab15 return _UpdateLED_Led16 UPDATE_LED 16, 0, PORTE, 2, LED16_PWM, LED16_Level, LED_16_23_Enable, LED_16_23_State, _lab16 return _UpdateLED_Led17 UPDATE_LED 17, 1, PORTE, 1, LED17_PWM, LED17_Level, LED_16_23_Enable, LED_16_23_State, _lab17 return _UpdateLED_Led18 UPDATE_LED 18, 2, PORTE, 0, LED18_PWM, LED18_Level, LED_16_23_Enable, LED_16_23_State, _lab18 return _UpdateLED_Led19 UPDATE_LED 19, 3, PORTA, 5, LED19_PWM, LED19_Level, LED_16_23_Enable, LED_16_23_State, _lab19 return _UpdateLED_Led20 UPDATE_LED 20, 4, PORTA, 3, LED20_PWM, LED20_Level, LED_16_23_Enable, LED_16_23_State, _lab20 return _UpdateLED_Led21 UPDATE_LED 21, 5, PORTA, 2, LED21_PWM, LED21_Level, LED_16_23_Enable, LED_16_23_State, _lab21 return _UpdateLED_Led22 UPDATE_LED 22, 6, PORTA, 1, LED22_PWM, LED22_Level, LED_16_23_Enable, LED_16_23_State, _lab22 return _UpdateLED_Led23 UPDATE_LED 23, 7, PORTA, 0, LED23_PWM, LED23_Level, LED_16_23_Enable, LED_16_23_State, _lab23 return ; return 1 in 'w' if button has been pressed. We debounce and return ; "1" on the press-down. We return "1" once per press. ; ; PORTC #4 is LOW for pressed, HIGH otherwise. ; ; This seems insanely complicated for a seemingly simple thing, however ; it handles every conceivable oddity and seems to work perfectly... Pushbutton_Pressed movf _PBDebounce, w btfss STATUS, Z retlw 0 ; in debounce timeout (state transition time) ; ; what state are we in: open, open to close, closed, close to open movlw d'0' ; "open" subwf PBDebounceState, w btfss STATUS, Z goto _notOpen ; open. is it now closed? btfsc PORTC, 4 retlw 0 ; nope, still open ; closed. debounce open to close transition. movlw d'1' ; "open to closed" goto _SetStateAndDoDebounceAndLeave _notOpen movlw d'1' ; "open to closed" subwf PBDebounceState, w btfss STATUS, Z goto _notOpenToClosed ; open to closed. ; check: if not closed, then it was some sort of brief errant pulse, ignore. btfss PORTC, 4 goto _okClosed ; brief errant pulse. go back to open state, debounce again to be sure. movlw d'0' goto _SetStateAndDoDebounceAndLeave _okClosed movlw d'2' ; "closed" movwf PBDebounceState retlw 1 ; PRESSED ! _notOpenToClosed movlw d'2' ; "closed" subwf PBDebounceState, w btfss STATUS, Z goto _notClosed ; closed. Is it now open? btfss PORTC, 4 retlw 0 ; nope, still pressed ; its been opened. Debounce. movlw d'3' ; "open to close" goto _SetStateAndDoDebounceAndLeave _notClosed ; well, it must be in the close to open state. if not open, it was some ; sort of weird errant "open" transition, debounce an go back to closed. btfsc PORTC, 4 goto _okOpen ; errant pulse of some sort, go back to closed movlw d'2' goto _SetStateAndDoDebounceAndLeave _okOpen movlw d'0' ; back to "open" movwf PBDebounceState retlw 0 _SetStateAndDoDebounceAndLeave movwf PBDebounceState movlw 0x10 movwf _PBDebounce retlw 0 ; Random number routine ; Probably really bad, but usable for our purposes... ; from: http://www.piclist.com/techref/microchip/rand8bit.htm ; Andrew Warren [fastfwd at ix.netcom.com] of Fast Forward Engineering ; - San Diego, California says ;Load a register called "RANDOM" with any non-zero value, then call this ; routine each time you'd like a new pseudo-random value: GetRandom: RLF randomNum,W RLF randomNum,W BTFSC randomNum,4 XORLW 1 BTFSC randomNum,5 XORLW 1 BTFSC randomNum,3 XORLW 1 MOVWF randomNum RETLW 0 ; --------------------------------- ; from p486 in book... ; 2mhz: ; val= (10ms * 2mhz/4)/5 + 256 ; val= 1256 or 0x04EB ; 20mhz: ; val= (10ms * 20mhz/4)/5 + 256 ; val= 10256 or 0x2810 ; we do the delay Delay10msecNumTimes times Delay10msec_NumTimes movwf Delay10msecNumTimes loopdelay1 movlw LOW 0x2810 movwf Delay10msecVar movlw HIGH 0x2810 movwf Delay10msecVar + 1 loopdelay2 decf Delay10msecVar, f btfsc STATUS, Z decfsz Delay10msecVar+1, f goto loopdelay2 decfsz Delay10msecNumTimes, f goto loopdelay1 return end