• entries
  • comments
  • views

About this blog

Midibox design and construction notes

Entries in this blog


When interfacing a Joystick, Modwheel or Pitchbender it is sometimes found that the voltage range on the potentiometer output is well inside the range of the Core ADC input.


This can often be overcome with digital calibration in the firmware (e.g MIDIbox KB) in some cases there is extreme loss of resolution.


Presented here is a circuit to overcome this to give the ADC the full range (in this case 0..3.3V) even though the range of the potentiometer is well inside this.

The design process is very easily done following this app note:sloa097.pdf 230.01K 12 downloads


I've done a spreadsheet to make it even easier!Scale Offset.zip 2.45K 9 downloads If you don't have MS Excel, you can use it with free tools Google Docs, or Open Office.

To use the speadsheet you enter numbers into the blue fields. The input range at the top as measured on your pot. In preparing the examples, I played with the value of Rg2 so that Rg2+R1 came close to 10k. This allowed to replace them with a 10k trimpot as in the example circuits. It was just as well, as the trimpot did require some tweaking away from the calculated values, I found.


I chose LM324 op amp as it's output goes down very close to 0V. It's maximum possible output with a Vcc=5V is about 3.5V which should be quite safe with a 3.3V Core ADC such as LPC17 MBHP Core.

There are 4 op amps in this device, so 4 scaling/offset circuits can be implemented with 1 chip.


Here's the circuit with 2 worked examples:



Here's the test (input on left, output on right) showing 3 points (Modwheel example):



Here's another test (input on left, output on right) showing 3 points (Pitchbender example):



Appnote Cct with Examples.jpg




This article documents the hardware and software mods to provide 2 extra (4 total) Midi I/O ports on the MB SEQ4L.

The added ports use the onboard UARTs (2,3) of the LPC MIOS32 core. The underlying sequencer code supports these ports (MIDI 3 and 4), but in the case of MIDI 3, the pins used, need to be replaced as they are( were) used to drive LED matrix columns. I chose J28 to provide these LED column driver pins, unfortunately they are part of the CV output (which I don't want) which has to be disabled in the software.


The Midi Router functionality of the MB SEQV4 (Lite and "Full" versions) allows complex and well integrated MIDI interconnection of Synths and devices. The motivation in my case was to have Sysex Librarian and PC host synth patch design avaiable using the 4 available USB MIDI ports. Suffice it to say that having an extra 2 midi ports allows me to have RC50 Looper, MBSEQ, keyboards, and 2 Virus synths all talking and editable simultaneously from my PC. Perhaps the details should go in a separate blog article.

Hardware Mods

MIDI I/O Port 4

This one is straight forward as port J4B is free.

Simply connect a 2pin header receptacle:

J4B.SC->Midi In

J4B.SD->Midi Out

Ive used a GM5 PCB for MIDI sockets, opto, etc. but can use a standard circuit built on vero board. Just copy the circuit from a core.

MID I/O Port 3

This one is a little more involved as the pins that have the UART functionality are on J15 which is used to drive the LED matrix columns on the SEQV4L.

I've chosen J28.0 to replace J5B.6 (column driver)

and J28.1 to replace J5B.7 (column driver)

Core pin J5B.6->Midi IN (black wire in picture)

Core pin J5B.7->Midi OUT (red wire in picture)

J28.SDA->J5B.6 (connector) (white wire in picture)

J28.SC->J5B.7 (connecor) (blue wire in picture)


(above) MIDI port 3 wiring NOTE TRACKS ARE CUT between black and white and between red and blue!!!!

Software Mods


This enables the extra midi ports in the firmware

include the following lines (comment out conflicting #defines)

#define SEQ4VL_FOUR_MIDI_PORTS            /*switch used elsewhere to compile this mod*/

#define MIOS32_USB_MIDI_NUM_PORTS 4  /*use with bootloader 1.010 or later for proper multiport usb */

#define MIOS32_UART_NUM 4

\trunk\modules\blm_cheapo\blm_cheapo.c This is where the column drivers port pins are initialised, including the replacement pins of J28 replace
  for(pin=0; pin<8; ++pin)



  // initialize all pins of J5A and J5B as outputs in Push-Pull Mode

  //***dug*** 6 and 7 are wired to MIDI IN/OUT3 

  //these signals are generated by J28.SDA and J28.SC, 0, and 1 respectively


  for(pin=0; pin<6; ++pin)





  for(pin=0; pin<8; ++pin)





  //J5 bits 6 and 7 are not initialised for GPIO so have no effect

  MIOS32_BOARD_J28_Set(led_row[selected_row]>>6);//put J5 6,7 into J28 0,1


after MIOS32_BOARD_J5_Set(led_row[selected_row]); in s32 BLM_CHEAPO_PrepareCol(void) This drives the replacement pins of J28 of the LED matrix column. \trunk\apps\sequencers\midibox_seq_v4_lite\core\seq_file_hw.c (about line 860) comment out thus:

#elif defined(MIOS32_FAMILY_LPC17xx)

  	for(i=0; i<4; ++i) {

//    	MIOS32_BOARD_J28_PinInit(i, pin_mode);

//    	MIOS32_BOARD_J28_PinSet(i, 0);



The remaining changes are to stop the J28 now LED matrix pins being driven by the old CV code: \trunk\apps\sequencers\midibox_seq_v4\core\seq_cv.c line 70 in SEQ_CV_Init() change
#elif defined(MIOS32_FAMILY_LPC17xx)

  for(i=0; i<4; ++i)



#elif defined(MIOS32_FAMILY_LPC17xx)

  for(i=2; i<4; ++i)



comment out first 3 calls to MIOS32_BOARD_J28_PinSet in s32 SEQ_CV_Update(void) thus:

#if defined(MIOS32_FAMILY_STM32F10x)

    MIOS32_BOARD_J5_PinSet(9, start_stop);

#elif defined(MIOS32_FAMILY_LPC17xx)

    //MIOS32_BOARD_J28_PinSet(1, start_stop);		***HERE***


# warning "please adapt for this MIOS32_FAMILY"



  // DIN Sync Pulse at J5C.A8

  if( seq_core_din_sync_pulse_ctr > 1 ) {

#if defined(MIOS32_FAMILY_STM32F10x)

    MIOS32_BOARD_J5_PinSet(8, 1);

#elif defined(MIOS32_FAMILY_LPC17xx)

//    MIOS32_BOARD_J28_PinSet(0, 1);			***HERE***


# warning "please adapt for this MIOS32_FAMILY"



  } else if( seq_core_din_sync_pulse_ctr == 1 ) {

#if defined(MIOS32_FAMILY_STM32F10x)

    MIOS32_BOARD_J5_PinSet(8, 0);

#elif defined(MIOS32_FAMILY_LPC17xx)

//    MIOS32_BOARD_J28_PinSet(0, 0);			***AND HERE***


# warning "please adapt for this MIOS32_FAMILY"



That's it!!



Discussion of the problems limiting the length of SRIO chains.

Some causes of signal integrity problems in transmission lines are mentioned.

A Working solution is presented.


The problems limiting the length (physical length and the number of chips connected) of DOUT shift registers has been canvassed during the development of my Midi Keyboard LED Display (MKLD) project. MKLD Itself is the subject of an upcoming blog article. Suffice it to say for now that the design uses 24x 74HC595 chips to drive a display that covers a 4 octave keyboard. I intend to drive 2 keyboards this way adding up to 48 chips.

A limit of 16 shift register chips in SRIO chains has been arrived at empirically. This has been based on the number of chips (contained in DOUT modules for instance) that can be reliably successfully controlled. MIOS32 can address an arbitrary number however, but problems can and will arise after a certain number of shift registers are attached.

Rudimentary signal integrity for transmission lines

When the clock rate of data is high and the physical length of signal traces in long relative to it, the wave shape of signals can change to the point where the circuit no no longer functions reliably unless special steps are taken in the design to prevent this. In an absolute nutshell: voltage wave reflections due to characteristic impedance changes (particularly at the end of the line) interact with the distributed LRC impedances to produce ringing.

"The crude rule of thumb are that a conductor is electrically long when it exceeds one-seventh of the shortest wavelength of concern, or when the time that the leading edge of a signal takes to travel from the source to the furthest receiver exceeds half of its rise or fall times. " Reference

The article quoted above provides the estimate for a circuit board of 6.7ns/m for speed of signal. So in the case I have encountered with MKLD length of say 4m, signal travel time is around 30ns. The "crude rule of thumb" states that the rise time of my clock signal should not be less than 60ns.

In practise with the MKLD circuit


Simply connecting the DOUT port of J8 of the MBHP Core STM32 to the MKLD string of 30 shift registers has worked. But the seeds of a problem are there to see:


SCLK signal measured at the end of the chain

The falling edge (driven low by the open drain output of the STM32) is rather fast (<~20ns). There is distinct ringing occurring.

Interestingly, the rising edge (driven high by the 220 ohm pullup) is slower (~100ns). No sign of ringing.

The input capacitance of the inputs of 74HC595 is 3.5pF per input. With 30 shift registers, the SCLK line has a total capacitance of (3.5*30)=105pF

The RC time constant (time for a rise/fall of 66% of max/min) is (220*105)ps= 23.1ns. This suggests that the parasitic capacitance (that caused by the ribbon wire and pcb tracks, totalling close to 2m in length) may be more significant.


SCLK signal measured at the end of the chain

This is with a series 220 ohm resistor at the source. The falling edge no longer has any trace of ringing.


SCLK signal measured at the end of the chain

Using 100 Ohm resistor. Better wave shape. The cursors measure ~20ns fall time. The actual test setup is probably only around 1.5m in signal path length.


It is useful to note that the "solution" here is not impedance matching but rather making use of parasitic capacitance in conjunction with an applied series resistor to slow down the edges of the SCLK signal to the point where transmission line effects are not observed. This is only possible due to the relatively slow clock used by MIOS32. There is no performance hit as the transfer is using DMA. With an update period of 1ms, there is a theoretical limit of nearly 1000 bits of SRIO that may be transferred.

Other points to mention are that the RCLK signal has timing such that it has plenty of time to settle where there are no changes in either DIN In or SCLK. DIN is also physically shorter and is resynchronised with each chip therefore less likely to need a series resistor.

The "Working solution" may or may not work for MIOS8 systems where the bit-banged SRIO is considerably faster clocked. I would suggest smaller series R.

And finally, I'd love to hear from any signal integrity experts on any false statements or assumptions or analysis that is unreasonably flawed. Comments welcome!

Pictures and text © Duggle 2010


Simple keyboard zones

Rather than have the whole keyboard output the same midi channel (hence same sound) we may want to have "left hand ->Bass" and "right hand -> Treble" parts. This is accomplished by having a "zones" in this case upper and lower parts of the keyboard separated at a certain note on the keyboard ("split point").

A simple keyboard zone split is simple to define: for example output note numbers above a the split point on a different channel.


Velocity crossfaded zones

Rather than a sudden boundary dividing keys into one channel or another, crossfaded zones have an overlap. The incoming notes have their velocities scaled according to a curve best shown in a diagram:



A simple split my be accomplished with a call to the following function with received midi package before sinding it on:

//*********************Simple split*************************************************************

mios32_midi_package_t split(mios32_midi_package_t m_p, u8 splitpoint)


 if ((m_p.type == NoteOn || m_p.type == NoteOff)&& m_p.note>splitpoint) 

    m_p.chn++;                                //output on next channel

 return m_p;	  



The lower zone is output on the same channel as the input. The upper zone is output on the next channel. Velocity crossfade zones are done with calls to two functions: upper and lower.

//********************Upper keyboard zone*******************************************************

mios32_midi_package_t VelocityCrossfadeUpper(mios32_midi_package_t m_p,u8 sp,u8 size,u8 chan){

	m_p.chn=chan;					      //route to new channel regardless	

	if (m_p.type==NoteOn){                //other event types are passed through 

		 if (m_p.velocity==0) return m_p; //note offs (vel==0) get passed through

		 if (m_p.note>(sp+size/2))return m_p;	//above split region so return unmodified

		 if (m_p.note<(sp-size/2)){		  //below split region so return velocity 0


			return m_p;

		 }else{							  //within sp + or - size/2 so scale

			m_p.velocity=(m_p.velocity*(m_p.note-(sp-size/2)))/size;	//scale the velocity

			return m_p;


	} else return m_p;					//NoteOffs (or other events types) get passed through



//*******************Lower Keyboard zone********************************************************

mios32_midi_package_t VelocityCrossfadeLower(mios32_midi_package_t m_p,u8 sp,u8 size,u8 chan){

	m_p.chn=chan;					  			//route to new channel	

	if (m_p.type==NoteOn){

		 if (m_p.velocity==0) return m_p; 		//note offs (vel==0) get passed through

		 if (m_p.note<(sp-size/2)) return m_p;	//below split region so return unmodified

		 if (m_p.note>(sp+size/2)){

			m_p.velocity=0; 					//above split region so return velocity 0

			return m_p;

		 }else{							 		//within sp + or - size/2 so scale

			m_p.velocity=m_p.velocity-(m_p.velocity*(m_p.note-(sp-size/2)))/size;//scale the velocity

			return m_p;


	} else return m_p;					//NoteOffs (or other events types) get passed through



Application The following application applies the crossfade split to note events on any channel of any (or all) ports. Midi packages on USB0 are not processed to keep the port available to MIOS Studio as a development convenience. Usage Inputs notes on any channel CC#110: Splitpoint location (note number) CC#111: Split size (distance in midi notes) Outputs ch1: Lower zone output ch2: Upper zone output

//***********Velocity crossfade zones app example******************************

void APP_MIDI_NotifyPackage(mios32_midi_port_t port, mios32_midi_package_t m_p)


static u8 splitpoint=60;    //defaults 

static u8 splitsize=12;     //defaults

  if (port!=USB0){ //reserve USB0 for MIOS Studio comms

    //send lower split


	//send upper split


	if (m_p.type==CC){			//update split params


			case 110: splitpoint=m_p.value;break;

			case 111: splitsize=m_p.value;break;





Pictures and code © Duggle 2010


This is what worked for me:

1) Download and install openocd 0.2.0 (earliest version after 0.1.0 available as prebuilt *.msi.

Btw, 0.1.0 is preferred for compatibility with the mios provided scripts)

2) In Program Files\OpenOCD\0.2.0\drivers is a folder contained in ft223.zip, unpack this folder to a temporary location.

3) Attach the JTAG key to USB. After "Found New Hardware" wizard, install from the location of the folder in step (2)

4) Invoke OpenOcd with the following command line: openocd -f %MIOS32_PATH%\etc\openocd\interface\amontec.cfg -f %MIOS32_PATH%\etc\openocd\target\STM32F10x.cfg

5) Continue to follow Thorsten's guide in uCApps->Bootloader->MIOS32->Experts guide->"Accessing the core via JTAG"->"Quickstart DOS"

Hope this smooths the way for someone!



Jaycar is an electronic component retailer in Australia.

They have stores in many locations and for this reason are a convenient source of parts.

The QP5518 (Jaycar's cat. number) is 2x16 yel/grn LED backlit display.

It is pin compatible with the Core32 EXCEPT VDD AND VSS NEED TO BE SWAPPED.

Why this is the case I have no idea, but it's sure to bring some to grief.

FIX: Simply peal back wires 1,2 of the ribbon used and twist it before crimping the IDC connector at one end. Thats Brown and Red in the picture below:


Additionally it is not necessary to connect pins 15,16 from J15 (backlight LED drive pins) as this display has the backlight LED wired with series resistors to Vss and Vdd. The default brightness is quite good, I found. It is possible to remove the LED connections to Vss,Vdd on the LCD PCB and use the variable brightness LED driver of the Core32 J15 pins 15,16 instead.