Arduino VS1053 General MIDI Synth – Part 2

Another I’ve wanted to do with my Arduino MIDI Proto Shield PCB is link it with my Arduino VS1053 General MIDI Synth to provide a means of selecting the voice from the shield itself.  As I almost always pair my VS1053 shield with an off the shelf MIDI module to use it, this seems like a natural thing to do with one of my Arduino MIDI proto shields.

This is part of the way forward on that idea, although I’ve not quite got to the stage of building it into a PCB yet – this is just the first idea, initially using a solderless breadboard.

IMG_6464

Warning! I strongly recommend using an old or second hand keyboard for your MIDI experiments.  I am not responsible for any damage to expensive instruments!

These are the key Arduino tutorials for the main concepts used in this project:

If you are new to Arduino, see the Getting Started pages.

Parts list

IMG_6466

The Circuit

ArduinoMIDIVS1053SynthPC_bb

There were several design decisions to be made for this project, as the VS1053 uses quite a number of Arduino pins itself:

  • D2 – DREQ
  • D6 – X-Cs
  • D7 – X_DCS
  • D8 – X_RESET
  • D9 – CS
  • D11 – COPI (used to be called MOSI)
  • D12 – CIPO (used to be called MISO)
  • D13 – SCK

MIDI needs to connect to D0 and D1, so this leaves D3-D5, D10, and A0-A5 free for use.  The rotary encoder and switch can use A0-A2 as before, but that leaves just 7 further pins for the 7-segment display. I need 10 for a 3-digit display!

Options:

  • Use a shift register such as a 74HC595 to control the 7-segment display.
  • Use some kind of IO expander.
  • Use a I2C or SPI-connected 7-segment display module.
  • Use an alternative display set-up, e.g. an I2C OLED display.
  • Cut down on the use of pins some other way!

For the last option, I don’t actually need MIDI OUT or the switch on the rotary encoder and if I used a hex display, I’d only need two digits, so that actually could be a viable option: reclaim the use of D1 and A2 for use with a 7-segment display and drop one of the digit pins.

There is a version of the SevSeg library that supports a shift register (see: https://github.com/bridystone/SevSegShift) so that could be another option too. That means finding room on the proto board for a 16 pin DIP chip.  It will still need current limiting resistors too.

As I said before, whilst I2C 7-segment displays are really convenient, most seem to be pretty big.  I don’t really want 0.56″ digits.  There are a few that use SPI but as the VS1053 shield is also using SPI I wonder if the overhead of managing two SPI devices might get complicated or not really responsive enough for MIDI use.

An I2C OLED display is probably the easiest and might also allow for the text of instruments to be shown too.  But if I’m really honest, I really wanted the “80s aesthetic” look of a small 3-digit 7-segment display for this project!

Decision: for this project, I’m going to cut down on the IO usage, drop a digit and squeeze out those two extra pins to drive the 7-segment display directly as I did in my last project!  This does however mean the display will be showing voices numbers in hex: 0x00 to 0x7F.

I decided to build the whole thing on a mini solderless breadboard using my Adafruit Proto Shield for Arduino (see photos).

What about D10/SS?

The SPI interface defines D10 as the CS (used to be called SS) pin – this pin will allow another SPI controller to control the Arduino as an SPI peripheral if required.  For the Arduino to be acting as a controller itself (which is what we need here) D10 must be configured as an OUTPUT. It isn’t required to be used by SPI, but it does have to be an OUTPUT to keep the Arduino in controller mode.

Thankfully the SevSeg library works by setting all pins as OUTPUT pins and just driving them HIGH or LOW as required to make the LEDs function.  This means we are fine to use D10 as an OUTPUT for the 7-segment display and it doesn’t matter if it is a digit or a segment.

Also note that the Arduino’s SPI CS pin (D10) is different from the CS pin required to control another SPI peripheral, in this case the VS1053, which is using D9 for that purpose.

A note on current limiting resistors.

Almost any Arduino tutorial showing the use of shift registers for driving 7-segment displays will show resistors on all segments of the display.  As each segment is just an LED, it can be seen why this is necessary – there is the potential for all segments to be displayed at once.  This is particularly relevant to use of a shift register – the whole point is that this is a device that receives a serial list of IO states and have those appear on the outputs at the same time in parallel.

The SevSeg library, which drives IO pins directly, can support circuits with resistors on either segments or digits.  This is because it will “scan” the non-resistored elements in sequence, and never at the same time.  So, if you have resistors on digits, it will sequence through each segment and set all digits in parallel to drive just that one segment for each digit as required.  If you have resistors on segments, then it will sequence through each digit in turn but set all segments in parallel for each individual digit.  Basically, it will ensure that no two non-resistored circuits will be active at the same time.

So how about if a shift register is used?  Well, the SevSegShift library takes the same approach.  Even though the shift register could drive all segments at the same time if required, if you are running it in “resistors on digits” mode, then it won’t ever actually do this.

Both these highlight an interesting close dependency on the hardware design and the importance of having the driving code accurate drive the hardware to ensure nothing literally breaks.

It also implies that the code may run more optimally, if using a shift register for segments, with resistors on segments, as this would allow the entire register to be set up for the required pattern of segments in one go for each digit.  The alternative results in having to send 7 (or 8 if using a decimal point) whole registers worth of information, but each time just having a single bit set, in order to scan each segment one at a time.  Of course, if you are using a shift register for both segments and digits then this doesn’t apply.

To be honest, I suspect the performance hit won’t be particularly relevant in either case anyway.

IMG_6465

The Code

I’m using a mixture of code from the following previous projects:

I won’t go over details again, but the main points are detailed below.  Once again I’m using the following libraries:

And of course, FortySevenEffect’s ubiquitous Arduino MIDI library: https://github.com/FortySevenEffects/arduino_midi_library

And it needs the extra files of code for the VS1053 from here: Arduino MIDI VS1003 or VS1053 Synth.

Allowing the use of RX but not TX

As soon as you call Serial.begin() (as the MIDI library will do) both D0 and D1 are grabbed for use as RX and TX respectively.  It doesn’t matter what you do after that, or what is connected, it will assuming that D1 is TX and will be held HIGH by the UART.

Do resolve this, the transmitter side of the UART has to be disabled in hardware after the MIDI library is initialised.  This can be done by writing directly to the “USART0 Control and Status Register B” – UCSR0B – and setting the TXEN0 bit to zero (and most importantly, leaving all the other control and status bits untouched).

 UCSR0B &= ~(1<<TXEN0);

Seven Segment Library Configuration

The configuration for the SevSeg library is as follows (for details of how it works, see the above-mentioned previous project):

// Pin definitions for the display
//
//  = A =
// F     B
//  = G =
// E     C
//  = D =
//
// Pattern repeated for three digits (1,2,3) each with a common cathode.
// Note: Cathode should be connected to the "digit" pins via a resistor (1k).
//
// Digit Order: D1-D2-D3-D4
//
// Pinout of my 12-pin display.
//
// D1-A--F-D2-D3--B
// |              |
// E--D-DP--C--G-D4
//
#define NUM_DIGITS 2
#define NUM_SEGMENTS 7
byte digitPins[NUM_DIGITS] = {4,3}; // Order: D3,D4 (D1, D2 not used)
byte segmentPins[NUM_SEGMENTS] = {5,10,A3,A4,A5,A2,1}; // Order: A,B,C,D,E,F,G
#define RESISTORS_ON_SEGMENTS false // Resistors on segments (true) or digits (false)
#define HARDWARE_CONFIG COMMON_CATHODE // COMMON_CATHODE or COMMON_ANODE
#define UPDATE_WITH_DELAYS false // false recommended apparently
#define LEADING_ZEROS      true // Set true to show leading zeros
#define NO_DECIMAL_POINT   true // Set true if no DP or not connected

The configuration for the rotary encoder is as follows:

#define RE_A A1 // "CLK"
#define RE_B A0 // "DT"
RotaryEncoder encoder(RE_A, RE_B, RotaryEncoder::LatchMode::FOUR3);

Note the use of “FOUR3” mode for the encoder.  This is the one that works best for me, other encoders might need to use a different mode (see the encoder library for details) and it is possible that the A and B pins could be the other way round.

Main functional logic

The main logic is something like this:

loop:
  Call MIDI.Read() to handle any MIDI functionality to the VS1053
  Refresh the display
  Read the rotary encoder

  IF the rotary encoder has changed position THEN
    Increase or decrease the stored Program Number (range 1 to 128)
    Update the display with the new number
    Send the new MIDI Program Change message

Find it on GitHub here.

Closing Thoughts

This has proven that the idea could work on one of my Arduino MIDI Proto Shield but it would be so much more useful if I could have a 3-digit decimal display.

But given the complexity of adding a shift register or the difficulty of finding a small and neat 0.28″ (ideally) I2C 7-segment display module, I’m seriously tempted to just go with an I2C display and see if I can actually get the text for the 128 general MIDI instruments displayed too.

Kevin

One thought on “Arduino VS1053 General MIDI Synth – Part 2

Leave a comment