If you use a lot of oldschool drum machines, you will probably get to the point where you will want to synchronize your drum machine to some other kind of sequencer, be it software or hardware.
That’s when you find out that you need a Midi to Sync converter.

Midi to Sync/Sync to Midi converter front view
Another picture to show the connector in/outputs:

Rear View MidiDinSync/DinSyncMidi
Here, I used a PIC16F628 microcontroller with integrated serial port and I wrote some code which can convert MidiSync messages to DinSync (24 ppqn as per Roland standard) and also DinSync to Midi Sync.
In Midi-Sync mode I also added a 12ppqn clock output and optionally a 48ppqn output to link up with old Korg drum machines as KPR77 for example.
In order to use the 48ppqn output you will need to add the commented out part for the 48ppqn clock generation and recompile the code.
Note that in this case the maximum clock speed will be limited to about 200 bpm, beyond that rate, the clock signal will show a serious amount of jitter.
Before, I spent a lot of time trying out various methods of bit-banging an PIC16F84 running at 4MHz, in order to simulate a serial input. My code worked well, but I always had a feeling that the amount of jitter produced was quite too high, therefore I decided to go with an F628 which has already a built in USART port, which of course makes things a hell lot simpler.
Also it is worth noting that the quality of the generated sync signal is depending a lot on the precision of the Midi sync signal fed into the system. Furthermore I would recommend using a dedicated midi output port for the midi sync signal, additional traffic on the midi port like midi note messages and Sysex will contribute to additional jitter.
There are also some drum machines which will send midi note messages along with the midi sync signal and it is generally a good idea to turn this transmission off if not needed and possible of course.
Personally, I like the Sync-Midi mode best, it enables me to use my TR808 as a master clock for my software sequencer. I always have a feeling that the computer generated MidiSync signal is a bit untight, therefore this mode comes handy. In Ableton live, the one thing to keep in mind is to turn the ‘warp’ feature off after recording some tracks if you like to keep the original groove for further mixing.

A peek inside the box, prototype, not exactly beautiful!
The schematic is pretty straightforward, just one microcontroller running at 20MHz, an optocoupler as per midi specification and some more parts for power supply and connectors.

Midi-Sync/Sync-Midi Schematic
The device can easily be built on breadboard and if somebody decides to run some PCB’s off from this schematic please let me know, I might be interested to take one as well.
I created a board just to get an impression about the minimum size achievable, but it could yet be improved. Also some design rules errors are still present which have to be corrected. If you plan to reproduce this board, you should definitely consider checking the design rules!

Midi-Sync/Sync-Midi PCB
In terms of operation, there are 3 connectors on board, 1 Midi-In, 1 Midi-Out and 1 Din Sync In/Out (bidirectional Port).
Operation mode is selected with a switch and the selected mode is shown via led to the user. The led will flash 3 times long if Sync-Midi mode has been selected and 5 times short upon switching to Midi-Sync mode.
During operation, the led will blink upon each transmitted clock message/ clock signal and give feedback of correct processing.
In Sync to Midi mode, if the Sync Port is left open with nothing connected to it, the LED will start blinking and the Midi Out will send clock messages at a random rate, this because there is no Pullup/Pulldown resistor foreseen on the clock input.
You are allowed to use this code for your own NON-COMMERCIAL applications. If you re-use the code or modify it, please be so respectful to mention the original source when you publish it!
Schematics, Board, Assembly:
Download the complete project from here.
A couple more pictures from the developement:
Prototyping board setup:

MidiSync SyncMidi Converter on Prototyping board
Midi Clock message on scope (F8)

Midi clock signal on scope (upper), lower: sync input
Addendum:
I noticed that the PIC16F628 has got the low power programming fuse set by default, which means that if you do not explicitly turn it off, it will be set and PortB4 will not be adressable as In/Output.
That’s kind of a pitfall which could be time consuming at debugging.
So do not forget: LVP_off if you like to address PB4!
Assembler Code:
;
; -----------------------------------------------------------------------------------------------------------------------------------
;
; 2009 by Obsoletetechnology, S.Bidinger
;
; ! This code might be used for NON COMMERCIAL and DIY applications only !
; I do share it to encourage others to do alike, so please be respectful!
;
; If you do like this code and have used it on your apps, I would be pleased to hear so.
; Drop me a line under : obsoletetechnology(at)gmx.com
;
; -----------------------------------------------------------------------------------------------------------------------------------
; Midi to Sync and Sync to Midi converter for PIC16F628 using USART
; PIC at 20MHz 0.2us/instruction
; DinSync Out: 24ppqn, 12ppqn, 48ppqn optional
;
; OUTPUTS:
; RA0 DIN/OUT Start/Stop
; RA1 DIN/OUT Clock 24 ppqn
; RA2 DIN/OUT Clock 12ppqn
; RA3 Out Clock 48ppqn optional (commented out in code) will reduce max bpm speed of Midi-Sync conversion
; 48ppqn sync out: at Midi Sync Message: Pulse 2ms high, 2ms low, 2ms high, then low until next midi sync message, tested on Korg KPR77
;
; RB3 Operation Indicator Out
;
; INPUTS:
; RA0 Start/Stop Input
; RA1 Clock Input 24ppqn
;
; RB0 DIN Mode select (Mode Midi-Sync or Sync-Midi)
;
;
; 16/07/2009
; Tested, works.
;
; 22/07/2009
; Tested ok in both directions, on TR808-TR909-X0XB0X-TR707
; Jitter minimal, but depending very much on Midi-Clock quality
;
; 23/07/2009
; Added Operation Indicator Output on RB3
; At mode switch 5 short Pulses indicate Midi-Sync operation, 3 long Pulses
; indicate Sync Midi. OK.
; In MS mode, Indicator is strobed on each din sync clock out.
; In SM mode, Indicator is strobed on every midi sync message out.
;
; Added USART overrun error clear procedure in MidiSync receive in order to avoid
; lock up of the USART receive buffer.
;
;
; Enjoy!
; ----------------------------------------------------------------------------------------------------------------------------------------
LIST p=16F628
radix dec
include
__FUSES _HS_OSC & _LVP_OFF & _CP_OFF & _WDT_OFF & _PWRTE_ON
; Variables
pwCount equ 0x20 ; Temporary Buffer counter1
pwTime equ 0x21 ; Temporary Buffer counter2
MIDI equ 0x22 ; Buffer Midi Message
dinstat equ 0x23 ; Buffer Syncronization status
porttemp equ 0x24 ; Buffer temporary Port Status
dcompare equ 0x25 ; Buffer for comparison operations
temp equ 0x26 ; temp buffer
blink equ 0x27 ; register for blink counter
; 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 0x0000 ; Program reset Vector
goto MAIN
MAIN
; ------------------------------------------------------------------------------------------------
; Main Setup
bank0
clrf PORTA ; Clear Port
clrf PORTB ; Clear Port
movlw 0x07
movwf CMCON ; turn comparators off
bcf STATUS,DC ; set Digit Carry low
bank1
clrf TRISA ; Make all PortA Output
movlw b'00000011'
movwf TRISB ; PB0 In (Mode Select),PB1 In (Rx), PB2 Out(Tx)
movlw 0x09
movwf SPBRG ; Setup Baud rate generator for 31250 Baud
bcf TXSTA,SYNC ; Setup Asyncronous Serial Port
bcf TXSTA,BRGH ; BRGH = 0
bsf TXSTA,TXEN ; enable async transmission
bank0
bsf RCSTA,SPEN
bsf RCSTA,CREN ; Enable reception
call Pulselenght ; wait for 2ms to settle
; Check Mode Select, PB0=0 -> Midi-Sync, PB0=1 -> Sync-Midi
btfsc PORTB,0 ; Check ModeSelect State
goto SyncMidi
; ------------------------------------------------------------------------------------------------
MidiSync
; Port Setup Midi to Sync Mode
bank0
clrf PORTA ; Clear Port
clrf PORTB ; Clear Port
movlw 0x07
movwf CMCON ; turn comparators off
bcf STATUS,DC ; set Digit Carry low
bank1
movlw b'00000000'
movwf TRISA ; All Port A Outputs
movlw b'00000011'
movwf TRISB ; PB0 In (Mode Select),PB1 In (Rx), PB2 Out(Tx)
movlw 0x09
movwf SPBRG ; Setup Baud rate generator for 31250 Baud
bcf TXSTA,SYNC ; Setup Asyncronous Serial Port
bcf TXSTA,BRGH ; BRGH = 0
bsf TXSTA,TXEN ; enable async transmission
bank0
bsf RCSTA,SPEN
bsf RCSTA,CREN ; Enable reception
; ---------------------------------------------------------------------------------------------------
; Time waste for Startup
call Pulselenght
call Pulselenght
call MSBlink ; Mode Indicator Output Blink
; Clear registers
movf RCREG,W ; Flush receive register
movf RCREG,W
movf RCREG,W
clrf MIDI
btfsc RCSTA,OERR ; Is overrun bit set?
call ClearOverrun ; Clear OERR routine
; ---------------------------------------------------------------------------------------------------
MSLoop
Receive
btfsc PORTB,0 ; Mode Select test
goto SyncMidi ; Change operation mode
btfsc RCSTA,OERR ; Is overrun bit set?
call ClearOverrun ; Call clear OERR routine
btfss PIR1,RCIF ; USART reception complete bit test
goto Receive ; Repeat until midi byte received
movf RCREG,W ; Get midi data
movwf MIDI ; Store into midi buffer
; call Send ; DEBUG Mirror received Bytes Out (Midi Through) (might create some jitter on sync output
; bcf RCSTA,CREN ; Disable USART reception, so buffer can not fill up during data handling
; also avoids incoming Midi Note messages messing up the sync timing, reenabling later will also clear OERR if occured
movf MIDI,W
xorlw 0xF8 ; Test for Midi Clock Byte
btfsc STATUS,Z
goto ClockPulse ; Clock Byte received
movf MIDI,W
xorlw 0xFA ; Test for Midi Start Byte
btfsc STATUS,Z
goto StartHigh ; Start Byte received
movf MIDI,W
xorlw 0xFB ; Test for Midi Continue Byte
btfsc STATUS,Z
goto StartHigh ; Continue Byte received
movf MIDI,W
xorlw 0xFC ; Test for Midi Stop Byte
btfsc STATUS,Z
goto StopHigh ; Stop Byte received
; bsf RCSTA,CREN ; Enable USART reception again
goto MSLoop ; Nothing happened, begin all over
; -----------------------------------------------------------------------------------------------------
ClockPulse
bsf PORTA,1 ; set PA1 high (24ppqn output)
movlw b'00000100'
xorwf PORTA ; Toggle PA2 for 12ppqn and PA4 for operation indication
bsf PORTB,3 ; Operation Indicator Output on
; bsf PORTA,3 ; set PA3 high (48ppqn output)
call Pulselenght ; wait for specified time
bcf PORTA,1 ; set PA1 low
; bcf PORTA,3 ; set PA3 low (48ppqn output)
; call Pulselenght ; wait another 2ms (48ppqn output)
; bsf PORTA,3 ; (48ppqn output)
; call Pulselenght ; (48ppqn output)
; bcf PORTA,3 ; set PA3 low (48ppqn output)
bcf PORTB,3 ; Operation Indicator off
; bsf RCSTA,CREN ; Enable USART reception again
goto MSLoop ; Wait for next midi byte
StartHigh
bsf PORTA,0 ; Set PA0 high
; bsf RCSTA,CREN ; Enable USART reception again
goto MSLoop ; Wait for next midi byte
StopHigh
bcf PORTA,0 ; Set PA0 low
call ClearMem ; Clear registers
; bsf RCSTA,CREN ; Enable USART reception again
goto MSLoop ; Wait for next midi byte
; ------------------------------------------------------------------------------------------------------
SyncMidi
; Port Setup for Sync to Midi Operation
bank0
clrf PORTA ; Clear Port
clrf PORTB ; Clear Port
movlw 0x07
movwf CMCON ; turn comparators off
bcf STATUS,DC ; set Digit Carry low
bank1
movlw b'00000011' ; Clock Input PA1, Start/Stop input PA0
movwf TRISA
movlw b'00000011' ; PB0 In (Mode Select),PB1 In (Rx), PB2 Out(Tx)
movwf TRISB ;
movlw 0x09
movwf SPBRG ; Setup Baud rate generator for 31250 Baud
bcf TXSTA,SYNC ; Setup Asyncronous Serial Port
bcf TXSTA,BRGH ; BRGH = 0
bsf TXSTA,TXEN ; enable async transmission
bank0
bsf RCSTA,SPEN
bsf RCSTA,CREN ; Enable reception
clrf MIDI ; Clear Midi Buffer
clrf dinstat ; Clear Buffer Syncronization status
clrf porttemp ; Clear Buffer temporary Port Status
clrf dcompare ; Clear Buffer for comparison operation
; Wait to settle
call Pulselenght ; Wait for 4ms to settle
call Pulselenght
call SMBlink ; Mode indicator Output blink
; ------------------------------------------------------------------------------------------------------
; Sync to Midi Loop
SMLoop
movfw PORTA
movwf porttemp ; save port status temporary, in case status changes during operation
movfw dinstat ; put previous digital input port status into w
movwf dcompare ; move digital in status to coparison register
; test if change on PA0 (Start/Stop)occured
movlw b'00000001'
andwf dcompare ; mask all bits except bit 0 (start/stop)
movlw b'00000001'
andwf porttemp,W ; mask all bits except bit 0, into W register
subwf dcompare ; compare bit4 of both registers
btfss STATUS,Z ; if nothing changed, skip next instruction
goto STARTSET ; either Start or stop occured, send midi message
; test if change on PA1 (clock) occured (only positive change)
btfss porttemp,1
goto ENDPOLL ; do only if Clock high
movfw dinstat ; put previous digital input port status into w
movwf dcompare ; move digital in status to coparison register
movlw b'00000010'
andwf dcompare ; mask all bits except bit 1 (clock)
movlw b'00000010'
andwf porttemp,W ; mask all bits except bit 1, into W register
subwf dcompare ; compare bit 1 of both registers
btfss STATUS,Z ; if nothing changed, skip next instruction
goto CLOCKSET ; Clock high occured, send midi message
goto ENDPOLL ; nothing happened
; Send Midi messages
CLOCKSET
movlw 0xF8 ; Midi Clock Byte
movwf MIDI ; Load midi byte into register
call Send ; Call Midi send routine
movlw b'00001000'
xorwf PORTB ; toggle operation indicator output
goto ENDPOLL ; 26us until clock command goes out (0.026ms)
; at 24ppqn and 120 bpm we have 1 pulse each 20833us
STARTSET
btfsc porttemp,0 ; Start or Stop?
goto Startbyte
Stopbyte
movlw 0xFC ; Midi Stop Byte
movwf MIDI ;
call Send ; Goto Midi send routine
goto ENDPOLL
Startbyte
movlw 0xFA ; Midi Start Byte
movwf MIDI ;
call Send ; Goto Midi send routine
goto ENDPOLL
ENDPOLL
movfw porttemp
movwf dinstat ; store previous port status
btfss PORTB,0 ; ModeSelect test
goto MidiSync ; MidiSync Mode selected
goto SMLoop ; Repeat
; ------------------------------------------------------------------------------------------------------
; -----------------------------------------------------------------------------------------------------------
; SUBROUTINES
; Serial Data receive routine
;Receive
; btfsc PORTB,0 ; Mode Select test
; goto SyncMidi
;
; btfss PIR1,RCIF ; Reception Complete bit test
; goto Receive ; Repeat until midi byte received
;
; movf RCREG,W ; Get midi data
; movwf MIDI ; Store into midi buffer
;
; return
; Serial data send routine
Send
movfw MIDI
movwf TXREG ; Load data to be transmitted
bank1
Transmit
btfss TXSTA,TRMT ; 1 if transmission finished
goto Transmit
bank0
return
; Clear registers
ClearMem
movf RCREG,W ; Flush receive register
movf RCREG,W
movf RCREG,W
clrf MIDI
return
; Clear USART Overrun Error
ClearOverrun
bcf RCSTA,CREN ; Disable USART receive
bsf RCSTA,CREN ; Enable USART receive
; This clears OERR!
movf RCREG,W ; Flush receive register
movf RCREG,W
movf RCREG,W
clrf MIDI
return
; Wait determined Pulselenght
Pulselenght
; Clock Sync Pulsewidth, 2.007ms
movlw 0x0D ; (1)
movwf pwTime ; (1)
DLoop1
movlw 0xFF ; (1)
movwf pwCount ; (1)
DLoop2
decfsz pwCount ; (1\2) (256*3)+ 3 = 771*0.2 = 154.2us
goto DLoop2 ; (2)
decfsz pwTime ; (1\2) (13*154.2)+3 = 2007.6us
goto DLoop1 ; (2)
return
; Blink PB3 5 times short
MSBlink
movlw 0x05
movwf blink ; do 5 times
blinksh
bsf PORTB,3 ; turn on PA4
call Waitshort
bcf PORTB,3 ; turn off PA4
call Waitshort
decfsz blink ; done 5 times?
goto blinksh
return
; Blink PB3 3 times long
SMBlink
movlw 0x03
movwf blink ; do 3 times
blinklng
bsf PORTB,3 ; turn on PA4
call Waitlong
bcf PORTB,3 ; turn off PA4
call Waitlong
decfsz blink ; done 3 times?
goto blinklng
return
; Wait approx 500ms
Waitlong
movlw 0x08
movwf temp
WLoop1
movlw 0xFF ; (1)
movwf pwTime ; (1)
WLoop2
movlw 0xFF ; (1)
movwf pwCount ; (1)
WLoop3
decfsz pwCount ; (1\2) (256*3)+ 3 = 771*0.2 = 154.2us
goto WLoop3 ; (2)
decfsz pwTime ; (1\2) (256*154.2)+3 = 39424us
goto WLoop2 ; (2)
decfsz temp ; 14* 39424us = 551936us
goto WLoop1
return
; Wait for approx. 250ms
Waitshort
movlw 0x03
movwf temp
WsLoop1
movlw 0xFF ; (1)
movwf pwTime ; (1)
WsLoop2
movlw 0xFF ; (1)
movwf pwCount ; (1)
WsLoop3
decfsz pwCount ; (1\2) (256*3)+ 3 = 771*0.2 = 154.2us
goto WsLoop3 ; (2)
decfsz pwTime ; (1\2) (256*154.2)+3 = 39424us
goto WsLoop2 ; (2)
decfsz temp ; 7* 39424us = 275968us
goto WsLoop1
return
end
