; This code may not be used for commercial gain without prior agreement ; Copyright March 2000, Colin Fraser ; ; Modifications for Boss DR-55 by Benjamin Riggs 2005... cheers Colin :) ; ; revision history: ; ; 16/12/2008 - code modified by Dennis Matana to run on 12F629. ; ; 28/03/2004 - modified the code to use interrupt on GP2 instead of polling. v4.1 modified to run a DR-55. ; ; 27/03/2004 - transfered the code from the 12c508 to 12f675 to get a larger queue. ; queue is now 60 bytes long in the 12f675 compared to 21 in the 12c508. ; 12f675 is pin for pin compatible with the 12c508 so no changes to the ; circuit or PCB is required. ; ; now also repsonds to midi reset... just for the hell of it. ; ; 26/03/2004 - optimised register usage to increase the size of the queue from ; 16 to 21 bytes. my pc sequencer is giving me a headache. ; ; 19/03/2004 - modified the midi to sync code to ensure no midibytes are lost ; and output pulses are longer than 2.2 ms. previously sync was lost if ; consecutive midi clocks were recieved in a period shorter than 2.2ms. ; to do this, they are now queued on arrival before execution. ; ; 08/03/2004 - modified code to sync a BOSS-DR110 to midi. ; now outputs 12PPM sync on pin 3(GP4), ; pin 2(GP5) goes high / pin6(GP1) goes high Z on start for 4ms, ; pin 6(GP1) goes high / pin2(GP5) goes high Z on stop for 4ms, ; ; modified code to send midi sync from Boss DR-110 internal clock ; now outputs 24 midi clocks per metronome from 12ppm internal DR-110 clock ; send midi start on rising edge of pulse on pin 2 (GP5) ; send midi stop on rising edge of pulse on pin 6 (GP1) ; ; 15/05/2003 - fixed config option for MCLRE - oops! ; ; 26/11/2001 - added sync to midi conversion ; ; 6/3/2000 - modified midi bit timing to fix rare sync problem when consecutive ; bytes were received with no gap ; ; ; ; PIC12f675 Midi to Boss DR-55 Sync ; ; This program uses Microchip assembler mnemonics and the Microchip ; MPASM assembler (http://www.microchip.com/). Default config options ; are set in the __CONFIG line below: MCLRE on, CP off, WDT off, OSC=INTRC. ; ; _______ _______ ; | \/ | ; (+5V) Vdd --+ 1 8 +-- Vss (GND) ; | | ; X1/CLKI/GP5 --+ 2 7 +-- GP0 ; (Sync Start) | | (Midi output) ; X2/CLKO/GP4 --+ 3 6 +-- GP1 ; (Sync Clock) | | (Sync Stop) ; GP3/!MCLR --+ 4 5 +-- GP2 ; (mode sel) | (PIC12F675) | (Midi input) ; +----------------+ ; ; This program converts incoming midi clock bytes ; to Boss DR-55 compatible sync pulses ; ; list p=12f629 radix dec include "p12f629.inc" __CONFIG _CP_OFF & _CPD_OFF & _BODEN_OFF & _MCLRE_OFF & _WDT_OFF & _PWRTE_ON & _INTRC_OSC_NOCLKOUT ; Variables ; bytes 0x20 to 0x28 make up special function register block cblock 0x20 delaytemp ; 0x20 used for delay in midirx and miditx bitcounter ; 0x21 used to count bits in midirx and miditx temppointer ; 0x22 used to re-organise queue midiqueuepointer ; 0x23 pointer to byte at the end of the queue clockcount ; 0x24 midi clock counter w_temp ; 0x25 w temp for isr status_temp ; 0x26 status tmep for isr midi ; 0x27 next midi byte to process dinstat ; 0x28 din input status endc ; bytes 0x27 to 0x5F make up midibyte queue block, midinext being at the first in queue ; Macros ; macros to change memory banks bank0 macro ;SET UP MACRO TO SWITCH TO BANK 0 bcf STATUS,RP0 endm bank1 macro ;SET UP MACRO TO SWITCH TO BANK 1 bsf STATUS,RP0 endm ;program code org 0X00 ;PROCESSOR RESET VECTOR goto main ;GO TO BEGINNING OF PROGRAM org 0X004 goto isr ;GO TO INTERUPT SERVICE ROUTINE main clrwdt bank1 movlw b'10000111' movwf OPTION_REG ;set Timer0 and prescale, no pullups call 0x3FF movwf OSCCAL ;calibrate oscillator ; clrf ANSEL ;turn off ADC bank0 movfw CMCON iorlw b'00000111' movwf CMCON ;turn comparitors off ; wait a second while powering up movlw 0x010 ;15 * 65536us= 992ms movwf delaytemp poweronloop ;1sec clrf TMR0 clrwdt bank1 movlw b'10000111' movwf OPTION_REG bank0 bcf INTCON,T0IF poweronloop1 ;32ms btfss INTCON,T0IF goto poweronloop1 decfsz delaytemp,F goto poweronloop ; ==================== ; midi to sync section ; ==================== m2s bank1 movlw b'00101110' ;Set the I/O direction, start and stop pins high Z movwf TRISIO bank0 movlw b'00000001' ;Preset outputs movwf GPIO ;/ movlw 0x27 movwf midiqueuepointer ;points to last byte in queue clrf midi ;initialise first midi register. ; set up interrupt for GP2 movlw b'10010000' movwf INTCON clrf clockcount ;\ midibyte execution code m2sloop btfss GPIO,GP3 ; check mode select goto s2m ; go to sync to midi if required btfsc INTCON,T0IF call clearpins ; Check for Start movfw midi xorlw 0xFA btfsc STATUS,Z goto clockstart ; Check for Stop movfw midi xorlw 0xFC btfsc STATUS,Z goto clockstop ; Check for Clock pulse movfw midi xorlw 0xF8 btfsc STATUS,Z goto clockpulse ; Check for Reset movfw midi xorlw 0xFF btfsc STATUS,Z goto 0x00 goto reorganisequeue ; Set start pin high / stop pin high Z clockstart bank1 bcf TRISIO,GP5 bank0 bcf GPIO,GP5 movlw 0x05 ;set up clock counter movwf clockcount ;set up timer0 movlw .160 movwf TMR0 bcf INTCON,T0IF clrwdt bank1 movlw b'10000110' movwf OPTION_REG bank0 goto reorganisequeue ; Set stop pin high / start pin high Z clockstop bank1 bcf TRISIO,GP1 bank0 bcf GPIO,GP1 ;set up timer0 movlw .160 movwf TMR0 bcf INTCON,T0IF clrwdt bank1 movlw b'10000110' movwf OPTION_REG bank0 goto reorganisequeue ; pulse clock pin clockpulse movlw 0x05 xorwf clockcount,W btfsc STATUS,Z goto pulsepin incf clockcount goto reorganisequeue pulsepin bsf GPIO,GP4 clrf clockcount ;set up timer0 movlw .160 movwf TMR0 bcf INTCON,T0IF clrwdt bank1 movlw b'10000110' movwf OPTION_REG bank0 reorganisequeue clrf midi ;moves all valid data up one address in queue movlw 0x27 movwf temppointer ;initialise pointer to first byte in queue movlw 0x27 ;test for empty queue xorwf midiqueuepointer,W btfsc STATUS,Z goto m2sloop reorganiseloop incf temppointer movfw temppointer movwf FSR movfw INDF decf FSR movwf INDF movfw temppointer ; test for end of queue xorwf midiqueuepointer,W btfss STATUS,Z goto reorganiseloop decf midiqueuepointer goto m2sloop ; ensure that each pin is on longer than 12mS clearpins bank1 bsf TRISIO,GP1 bsf TRISIO,GP5 bank0 bcf GPIO,GP4 return ;/end of midi execution code ;/Interupt Service Routine isr movwf w_temp ; copy W to tmep register, could be in either bank swapf STATUS,W ; swap status to be saved into W bcf STATUS,RP0 ; change to bank0 regardless of current bank movwf status_temp btfsc INTCON,INTF call midirx swapf status_temp,W ; swap status_temp register into W, sets bank to original state movwf STATUS ; move W into staus register swapf w_temp,F ; swap w_temp swapf w_temp,W ; swap w_temp into W retfie ;/end ISR ; ; rx - receive midi byte at 31250 bps. ; midirx movlw 0x5F ; (1) xorwf midiqueuepointer,W ; (1) test to see if queue is full btfsc STATUS,Z ; (1/2) goto rxexit ; (1) discard byte if queue is full incf midiqueuepointer ; (1) increment pointer movfw midiqueuepointer ; (1) initialise pointer movwf FSR ; (1) to next byte in queue ; ; Delay one bit-width (32 cycles) to get to center of LSB. ; Gather the bits into midi. ; movlw 0x08 ; Eight bits of data to get movwf bitcounter clrf INDF mbin movlw 0x06 ;(1) movwf delaytemp ;(1) del1 nop ;\ decfsz delaytemp,F ;((6 x 4) + 3) = 26 goto del1 ;/ bcf STATUS,C ;(1) Default 0 rrf INDF,F ;(1) Put 0 in MSB of recv, then btfsc GPIO,2 ;(1/2) sample the serial data and bsf INDF,7 ;(1) set the bit if sample was=1 decfsz bitcounter,F ;((7 x 3) + 2) = 23, Do all bits goto mbin ;/ [total cycles = 32, MSB = 31] ; ; Time to center of stop bit. (32 cycles) ; movlw 0x09 ;(1) movwf delaytemp ;(1) del3 decfsz delaytemp,F ;((9 x 3) + 2) = 29 goto del3 ;/ btfss GPIO,2 ;(2)Stop bit showed up? (stop bit consists of a '1') decf midiqueuepointer bcf INTCON,INTF ;clear GP2 interrupt flag return ;(2)keep processing midibytes in queue if not full ; ; extra cycles to retain timing in event ; of the queue being full ; rxexit movlw .101 ;(1) movwf delaytemp ;(1) del2 decfsz delaytemp,F ;((103 x 3) + 4) = 313 goto del2 bcf INTCON,INTF ;clear GP2 interrupt flag return ; ==================== ; sync to midi section ; ; dinstat ; b0 previous status of clock pin ; b1 previous status of start pin ; b2 previous status of start pin ; b3 current run status. set on start, reset on stop ; b4 unused ; b5 unused ; b6 unused ; b7 unused ; ==================== s2m clrf INTCON bank1 movlw b'00111110' ;Set the I/O direction movwf TRISIO bank0 movlw b'00000001' ;Preset outputs movwf GPIO ;/ movlw b'00000110' movwf dinstat starttest btfsc GPIO,3 ; check mode select goto m2s ; go back to midi to sync if required btfss GPIO,5 ; Check for DIN start = 1 goto lowstart ; branch if start = 0 goto highstart ; branch if start = 1 clktest btfss GPIO,4 ; Check DIN Clock goto lowclock ; branch if clock = 0 goto highclock ; branch if clock = 1 stoptest btfss GPIO,1 ; Check for DIN stop = 1 goto lowstop ; branch if stop = 0 goto highstop ; branch if stop = 1 lowstart btfss dinstat,1 ; was it already 0 ? goto clktest ; branch if start pin not changed bcf dinstat,1 ; start status = 0 btfsc dinstat,3 ; are we already running? goto clktest ; skip midi tx if yes bsf dinstat,3 movlw 0xFA ; tx midi start byte call miditx movlw 0xF8 ; tx midi clock byte call miditx movlw 0xF8 ; tx midi clock byte call miditx movlw 0xF8 ; tx midi clock byte call miditx movlw 0xF8 ; tx midi clock byte call miditx movlw 0xF8 ; tx midi clock byte call miditx movlw 0xF8 ; tx midi clock byte call miditx goto clktest highstart btfss dinstat,1 ; was it already 1 ? bsf dinstat,1 ; start status = 1 goto clktest ; branch if startpin not changed lowstop btfss dinstat,2 ; was it already 0 ? goto starttest ; branch if stop pin not changed bcf dinstat,2 ; stop status = 0 btfss dinstat,3 ; are we already stopped? goto starttest ; skip midi tx if yes bcf dinstat,3 ; run status = 0 movlw 0xFC ; tx midi stop byte call miditx goto starttest highstop btfss dinstat,2 ; was it already 0 ? bsf dinstat,2 ; stop status = 1 goto starttest lowclock btfsc dinstat,0 ; was previous clock 0 ? bcf dinstat,0 ; clock status = 0 goto stoptest ; don't send midi if no highclock btfsc dinstat,0 ; was previous clock 0 ? goto stoptest ; don't send midi if no bsf dinstat,0 ; clock status = 1 btfss dinstat,3 ; check run status goto stoptest ; don't send midi if not running movlw 0xF8 ; tx midi clock byte call miditx movlw 0xF8 ; tx midi clock byte call miditx movlw 0xF8 ; tx midi clock byte call miditx movlw 0xF8 ; tx midi clock byte call miditx movlw 0xF8 ; tx midi clock byte call miditx movlw 0xF8 ; tx midi clock byte call miditx goto stoptest miditx movwf midi ; store tx byte clrf GPIO ; start bit ;pause for one bit movlw 0x08 ;(1) movwf delaytemp ;(1) pause1 decfsz delaytemp ;(1/2) \ goto pause1 ;(2) / * z = 8 * 3 + 1 = 25 movlw 0x8 ;(1) movwf bitcounter ;(1) nop ;(1) mtxloop movfw midi ;(1) movwf GPIO ;(1) lsb of tx byte to midi out ;pause for one bit movlw 7 ;(1) movwf delaytemp ;(1) pause2 decfsz delaytemp ;(1/2) \ goto pause2 ;(2) / * z = 7 * 3 + 1 = 22 bsf STATUS,C ;(1) rrf midi,1 ;(1) nop ;(1) nop ;(1) decfsz bitcounter ;(1) goto mtxloop ;(2) movlw 0xFF ;(1) movwf delaytemp ;(1) pause3 decfsz delaytemp ;(1/2) \ goto pause3 ;(2) / * z = 8 * 3 + 1 = 25 return end