;============================================================================= ; Filename: midipulsehold.asm ;============================================================================= ; Author: Johannes Taelman ; Company: Johannes Taelman ; Revision: 2.00 ; Date: July 2005 ;============================================================================= ; Specs: ; ; - midi input ; - 8 pulse outputs, pulse duration controlled by note-on velocity ; - 16 bit pulse counter resolution, full velocity->duration lookup table ; per output ; - pulse counter clock: 25us (?) ; - 100% deterministic pulse duration ; - 8 hold outputs ;============================================================================= list p=18f252 ;list directive to define processor #include ;processor specific definitions #define Timer10Out OutsA,1; #define Timer9Out OutsA,3; #define Timer8Out OutsA,5; #define Timer7Out OutsB,6; #define Timer6Out OutsB,4; #define Timer5Out OutsB,2; #define Timer4Out OutsB,0; #define Timer3Out OutsC,4; #define Timer2Out OutsC,1; #define Timer1Out OutsC,3; #define Note10Hold OutsA,2; #define Note9Hold OutsA,0; #define Note8Hold OutsA,4; #define Note7Hold OutsB,7; #define Note6Hold OutsB,5; #define Note5Hold OutsB,3; #define Note4Hold OutsB,1; #define Note3Hold OutsC,5; #define Note2Hold OutsC,0; #define Note1Hold OutsC,2; #define ErrorLed LATC, 6; #define SetErrorLed BCF ErrorLed #define ClearErrorLed BSF ErrorLed #include "..\midi.asm" ; #define simulate 1 ; enable simulation ; #define heartbeat 1 ; enable heatbeats ;****************************************************************************** ;Configuration bits CONFIG OSC = HSPLL CONFIG OSCS = OFF CONFIG PWRT = ON CONFIG BOR = ON CONFIG BORV = 27 CONFIG WDT = OFF CONFIG WDTPS = 1 CONFIG CCP2MUX = ON CONFIG STVR = ON CONFIG LVP = OFF ;---------------------------------------------------------------------------- ;Constants SPBRG_VAL EQU 04Fh ;set baud rate 31250 for 40Mhz clock TX_BUF_LEN EQU 010h ;length of transmit circular buffer RX_BUF_LEN EQU TX_BUF_LEN ;length of receive circular buffer ;---------------------------------------------------------------------------- ;Bit Definitions TxBufFull EQU 0 ;bit indicates Tx buffer is full TxBufEmpty EQU 1 ;bit indicates Tx buffer is empty RxBufFull EQU 2 ;bit indicates Rx buffer is full RxBufEmpty EQU 3 ;bit indicates Rx buffer is empty ;---------------------------------------------------------------------------- ; Variables ; memory map ;---------------------------------------------------------------------------- CBLOCK 0x000 WREG_TEMP ;to save WREG during interrupt STATUS_TEMP ;to save STATUS during interrupt BSR_TEMP ;to save BSR during interrupt FSR0H_TEMP ;to save FSR0H during interrupt FSR0L_TEMP ;to save FSR0L during interrupt FSR0H_SHADOW ;to save FSR0H during high interrupt FSR0L_SHADOW ;to save FSR0L during high interrupt TmpM ; non-ISR scratch TmpT ; non-ISR scratch OutsA ; output port A internal states OutsB ; output port B internal states OutsC ; output port C internal states COUNTER COUNTER_HI CODE_ADDR_UPPER CODE_ADDR_HIGH CODE_ADDR_LOW BUFFER_ADDR_HIGH BUFFER_ADDR_LOW SysExPtrH SysExPtrL ProgramNumber ENDC CBLOCK 0x020 ; DO NOT MOVE THIS BLOCK Timer1Msb Timer1Lsb Timer2Msb Timer2Lsb Timer3Msb Timer3Lsb Timer4Msb Timer4Lsb Timer5Msb Timer5Lsb Timer6Msb Timer6Lsb Timer7Msb Timer7Lsb Timer8Msb Timer8Lsb Timer9Msb Timer9Lsb Timer10Msb Timer10Lsb ENDC ; DO NOT MOVE THIS BLOCK CmdBuff0RdIndx equ 0EEh CmdBuff0WrIndx equ 0EFh CmdBuff0Timer equ 0F4h CmdBuff0TimerValMSB equ 0F5h CmdBuff0TimerValLSB equ 0F6h CmdBuff1Timer equ 0F7h CmdBuff1TimerValMSB equ 0F8h CmdBuff1TimerValLSB equ 0F9h CmdBuff2Timer equ 0FAh CmdBuff2TimerValMSB equ 0FBh CmdBuff2TimerValLSB equ 0FCh CmdBuff3Timer equ 0FDh CmdBuff3TimerValMSB equ 0FEh CmdBuff3TimerValLSB equ 0FFh ; ... and incrementing this last adress wraps it to zero. ; don't change this property! ;---------------------------------------------------------------------------- ; end of memory map ;---------------------------------------------------------------------------- ;---------------------------------------------------------------------------- ;This code executes when a reset occurs. ORG 0x0000 ;place code at reset vector ResetVector: bra Main ;go to beginning of program ;---------------------------------------------------------------------------- ;This code executes when a high priority interrupt occurs. ORG 0x0008 HighInt: bra HighIntCode ;go to high priority interrupt routine ;---------------------------------------------------------------------------- ;This code executes when a low priority interrupt occurs. ORG 0x0018 LowInt: movff STATUS,STATUS_TEMP ;save STATUS register movff WREG,WREG_TEMP ;save working register movff BSR,BSR_TEMP ;save BSR register movff FSR0H,FSR0H_TEMP ;save FSR0H register movff FSR0L,FSR0L_TEMP ;save FSR0L register ;test other interrupt flags here btfss PIR1,RCIF ;test for RCIF receive interrupt flag bra LowInt1 ;if RCIF is not set, done with test btfsc PIE1,RCIE ;else test if Rx interrupt enabled bra GetData ;if so, go get received data LowInt1: btfss PIR1,TXIF ;test for TXIF transmit interrupt flag bra LowInt2 ;if TXIF is not set, done with test btfsc PIE1,TXIE ;else test if Tx interrupt is enabled bra PutData ;if so, go transmit data ;can do special error handling here - an unexpected interrupt occurred LowInt2: reset ;error if no valid interrupt so reset ;------------------------------------ ;End of low priority interrupt routine restores context. EndLowInt: movff FSR0L_TEMP,FSR0L ;restore FSR0L register movff FSR0H_TEMP,FSR0H ;restore FSR0H register movff BSR_TEMP,BSR ;restore BSR register movff WREG_TEMP,WREG ;restore working register movff STATUS_TEMP,STATUS ;restore STATUS register retfie ;---------------------------------------------------------------------------- ;High priority interrupt routine. HighIntCode: movff FSR0H,FSR0H_SHADOW ;save FSR0H register movff FSR0L,FSR0L_SHADOW ;save FSR0L register ;test other interrupt flags here btfss PIR2,TMR3IF ;test for Timer3 receive interrupt flag bra HighInt1 ;if RCIF is not set, done with test btfsc PIE2,TMR3IE ;else test if Timer3 interrupt enabled bra Timer3ISR ;if so, go get received data ;can do special error handling here - an unexpected interrupt occurred HighInt1: reset ;error if no valid interrupt so reset Main: clrf LATA ; All outputs low clrf LATB ; All outputs low clrf LATC ; All outputs low clrf TRISA ; Config PORTA as all outputs #ifdef ICD movlw 0xE0 movwf TRISB #else clrf TRISB ; Config PORTB as all outputs #endif movlw 0x80 ; movwf TRISC ; Config PORTC all outputs + tristate for serial rx movlw 0x07 ; init adc movwf ADCON1 ; init adc clrf ADCON0 ; poweroff adc clrf OutsA; clrf OutsB; clrf OutsC; MOVLW 0x10; MOVWF ProgramNumber; MOVLW 0x08 CALL LedGlowOut MOVLW 0x08 CALL LedGlowIn movlw CmdBuff0Timer; movwf CmdBuff0RdIndx; movwf CmdBuff0WrIndx; lfsr FSR0 ,0000h lfsr FSR1 ,0000h movlw 0x10 movwf ProgramNumber; rcall SetupMidi rcall SetupTimer3 MainLoop: #ifdef simulate nop; your chance to schedule midi cmd movff MidiTestByte,WREG; rcall MidiInParser; rcall Timer3ISR #endif call PollMidiIn; bra MainLoop ;go wait for more data ;---------------------------------------------------------------------------- ; Timer3 setup SetupTimer3: movlw 0x01 movwf T3CON ; timer3 on, other bits cleared bsf IPR2,1 ; timer3 = high priority interrupt bsf PIE2,1 ; timer3 interrupt enable return Timer3ISR: bcf PIR2,TMR3IF ; clear interrupt flag movlw 090h ;0x7F; 0xA0 movwf TMR3L ; preset timer setf TMR3H ; preset timer movf OutsA,0 movwf LATA; movf OutsB,0 movwf LATB; movf OutsC,0 movwf LATC; ReadBuffer movf CmdBuff0RdIndx, W; subwf CmdBuff0WrIndx, W ; result in w bz Timers; no new task on cmd queue movff CmdBuff0RdIndx, FSR0L; clrf FSR0H movlw 0x03; addwf CmdBuff0RdIndx bnz NoWrap; movlw CmdBuff0Timer; Wrap to start movwf CmdBuff0RdIndx; NoWrap movf INDF0,W; call SetPulseAndHold movf POSTINC0, W; W = timer# addlw 010h; 10h= half of startaddress of timers addwf WREG; W = (0x10+timer# )* 2 movwf FSR1L; clrf FSR1H; FSR1 points to timer values movf POSTINC0, W; W = timer val msb movwf POSTINC1; movf POSTINC0, W; W = timer val lsb movwf POSTINC1; ;************************************************************ ; Timers ; !!! change to timer interrupt instead of idle loop !!! Timers Timer1 decf Timer1Lsb; bc Timer2 ; decf Timer1Msb; bc Timer2 ; setf Timer1Lsb; bcf Timer1Out Timer2 decf Timer2Lsb; bc Timer3 ; decf Timer2Msb; bc Timer3 ; setf Timer2Lsb; bcf Timer2Out Timer3 decf Timer3Lsb; bc Timer4 ; decf Timer3Msb; bc Timer4 ; setf Timer3Lsb; bcf Timer3Out Timer4 decf Timer4Lsb; bc Timer5 ; decf Timer4Msb; bc Timer5 ; setf Timer4Lsb; bcf Timer4Out Timer5 decf Timer5Lsb; bc Timer6 ; decf Timer5Msb; bc Timer6 ; setf Timer5Lsb; bcf Timer5Out Timer6 decf Timer6Lsb; bc Timer7 ; decf Timer6Msb; bc Timer7 ; setf Timer6Lsb; bcf Timer6Out Timer7 decf Timer7Lsb; bc Timer8 ; decf Timer7Msb; bc Timer8 ; setf Timer7Lsb; bcf Timer7Out Timer8 decf Timer8Lsb; bc Timer9 ; decf Timer8Msb; bc Timer9 ; setf Timer8Lsb; bcf Timer8Out Timer9 decf Timer9Lsb; bc Timer10 ; decf Timer9Msb; bc Timer10 ; setf Timer9Lsb; bcf Timer9Out Timer10 decf Timer10Lsb; bc Timer11 ; decf Timer10Msb; bc Timer11 ; setf Timer10Lsb; bcf Timer10Out Timer11 ;EndHighInt: MOVFF FSR0L_SHADOW,FSR0L ;restore FSR0L register MOVFF FSR0H_SHADOW,FSR0H ;restore FSR0H register retfie FAST ;return and restore context ; bra EndHighInt ; resume MidiInNoteOn: ; lookup timer #, read from table in PM:0Fvv movf MidiByte2,W; bz MidiInNoteOffNoVelo; ; velocity=0 -> NOTE OFF ; check if note is in range movf MidiByte2,W; bz MidiInNoteOff; ; velocity=0 -> NOTE OFF movlw 0Fh; movwf TBLPTRH MOVFF MidiByte1,TBLPTRL TBLRD* ; read into TABLAT movf TABLAT,W bz MidiInNoteOn2 ; decf TABLAT,W movwf TmpT ; TmpT = timer# ; add to cmd queue clrf FSR0H movff CmdBuff0WrIndx, FSR0L; movf TmpT,W movwf POSTINC0; write timer # ; lookup velo -> timer val movf MidiByte2,W; addwf MidiByte2,W; movwf TBLPTRL; movf TmpT,W; ADDWF ProgramNumber,W movwf TBLPTRH TBLRD*+ ; movff TABLAT,TmpM TBLRD*+ ; movf TABLAT,W movwf POSTINC0 ; write timer lsb movf TmpM,W; movwf POSTINC0 ; write timer msb movf FSR0L,W; bz NoWrap2; corrected: was: bnz movwf CmdBuff0WrIndx; return NoWrap2 movlw CmdBuff0Timer; Wrap to start movwf CmdBuff0WrIndx; return MidiInNoteOn2: return MidiInNoteOffNoVelo: MidiInNoteOff: ; for non-timered note outputs movlw FirstNote subwf MidiByte1,W bn MidiInNoteOff2; movwf TmpT movlw LastNote subwf MidiByte1,W bnn MidiInNoteOff2; movf TmpT,W bra ClearHold return MidiInNoteOff2: return MidiInCtrlChange: movlw 0x7B; AllNotesOff subwf MidiByte1,0; bz MidiInAllNotesOff; return MidiInAllNotesOff: clrf OutsA; clrf OutsB; clrf OutsC; clrf LATA clrf LATB clrf LATC return MidiInProgramChange: MOVF MidiByte1,W CALL ProgramLookup MOVWF ProgramNumber NOP RETURN ProgramLookup: ADDLW 0x80; INFSNZ WREG RETLW 0x70 INFSNZ WREG RETLW 0x60 INFSNZ WREG RETLW 0x50 INFSNZ WREG RETLW 0x40 INFSNZ WREG RETLW 0x30 INFSNZ WREG RETLW 0x20 RETLW 0x10 NoteLookup: MOVWF TBLPTRL movlw 0Fh; movwf TBLPTRH TBLRD* ; read into TABLAT movf TABLAT,W RETURN MidiInRTReset: MidiInRTActiveSense: MidiInRTStop: MidiInRTContinue: MidiInRTStart: MidiInRTTick: MidiInRTClock: MidiInTuneRequest: MidiInAfterTouch: MidiInChannelPressure: MidiInPitchWheel: RETURN #include "..\midiSysexFlash.asm" ORG 0x0600 SetPulseAndHold: ; lookup using a computed goto ; cfr. PIC18fxx2 datasheet section 4.4 mullw 0x06; movf PCL,W movf PRODL,W addwf PCL _sph1: bsf Note1Hold bsf Timer1Out return _sph2: bsf Note2Hold bsf Timer2Out return _sph3: bsf Note3Hold bsf Timer3Out return _sph4: bsf Note4Hold bsf Timer4Out return _sph5: bsf Note5Hold bsf Timer5Out return _sph6: bsf Note6Hold bsf Timer6Out return _sph7: bsf Note7Hold bsf Timer7Out return _sph8: bsf Note8Hold bsf Timer8Out return _sph9: bsf Note9Hold bsf Timer9Out return _sph10: bsf Note10Hold bsf Timer10Out return ClearHold: mullw 0x04; movf PCL,W movf PRODL,W addwf PCL _ch1: bcf Note1Hold return _ch2: bcf Note2Hold return _ch3: bcf Note3Hold return _ch4: bcf Note4Hold return _ch5: bcf Note5Hold return _ch6: bcf Note6Hold return _ch7: bcf Note7Hold return _ch8: bcf Note8Hold return _ch9: bcf Note9Hold return _ch10: bcf Note10Hold return SetHold: mullw 0x04; movf PCL,W movf PRODL,W addwf PCL _sh1: bsf Note1Hold return _sh2: bsf Note2Hold return _sh3: bsf Note3Hold return _sh4: bsf Note4Hold return _sh5: bsf Note5Hold return _sh6: bsf Note6Hold return _sh7: bsf Note7Hold return _sh8: bsf Note8Hold return _sh9: bsf Note9Hold return _sh10: bsf Note10Hold return SetPulse: mullw 0x04; movf PCL,W movf PRODL,W addwf PCL _sp1 bsf Timer1Out return _sp2 bcf Timer2Out return _sp3 bcf Timer3Out return _sp4 bcf Timer4Out return _sp5 bcf Timer5Out return _sp6 bcf Timer6Out return _sp7 bcf Timer7Out return _sp8 bcf Timer8Out return _sp9 bcf Timer9Out return _sp10 bcf Timer10Out return