[go: up one dir, main page]

0% found this document useful (0 votes)
191 views9 pages

Mistralxg - A Usb Connected, Pic-Based Midi Synthesizer: Let'S Get Technical!

This document provides an overview of the hardware and software design of the mistralXG MIDI synthesizer. It describes the main hardware components including the PIC microcontroller, logic chips, amplifiers and daughterboard. It also summarizes the software design, which is based on the Microchip USB Firmware Framework and implements MIDI streaming functionality. Key aspects like the power requirements and USB descriptor configuration are also covered at a high level.

Uploaded by

Cristobalzq
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
191 views9 pages

Mistralxg - A Usb Connected, Pic-Based Midi Synthesizer: Let'S Get Technical!

This document provides an overview of the hardware and software design of the mistralXG MIDI synthesizer. It describes the main hardware components including the PIC microcontroller, logic chips, amplifiers and daughterboard. It also summarizes the software design, which is based on the Microchip USB Firmware Framework and implements MIDI streaming functionality. Key aspects like the power requirements and USB descriptor configuration are also covered at a high level.

Uploaded by

Cristobalzq
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 9

mistralXG – a USB connected, PIC-based MIDI synthesizer

Part II
Let’s get technical!
Welcome back! Last time, I described how the user interacts with mistralXG to switch MIDI
streams to the synthesizer and to control the various user options. This month, I’ll lift the
hood on the design to show you how the hardware and software work together to make
mistralXG do its thing.

Guided tour of the hardware


First, let’s take another look at the mistralXG block diagram (Figure 1).

mistralXG
USB interface
to PC

External memory
(Future)

MIDI OUT
Microchip
PIC18F2550
DB50XG
Switch 2 Synthesizer
Daughter card Headphone
& Line Out

MIDI IN
Switch 1

MIDI THRU

WX IN

Figure 1. mistralXG block diagram


Comparing this with the circuit schematic (Figure 2), we can identify the major functional
areas of the design. The detail may be too fine to read in the magazine, so I recommend
getting the schematic from the Nuts & Volts website and opening it with the free version of
Eagle (http://www.cadsoft.de/freeware.htm) so you can explore it more fully. In the
meantime, I’ll give you an overview.

The hardware is relatively simple. As you can see in the schematic, there are only a handful
of chips – the PIC18F2550, three 74ALS00s, a couple of dual op-amps, and an opto-isolator
for the MIDI input. The WX IN input is for a wind controller – as well as receiving the MIDI
data, it provides power to the controller. Make sure anything you attach to this port is suitable
and won’t be damaged by the voltage present at the connector. If you don’t need this input,
you can replace it with a second standard MIDI IN port by duplicating the other MIDI IN
circuit.
Figure 2. mistralXG schematic
Areas 1 and 2 implement Switches 1 and 2 in the block diagram respectively. Area 3 provides
the control to enable and disable MIDI THRU and MIDI OUT. These controls use three
74ALS00 logic chips, selected to give a fast response with low power consumption. Outputs
from the MCU control these features.

Area 4 contains the MCU and its oscillator. The PIC18F2550 supports many oscillator
configurations. I chose a 4MHz crystal, running the internal clock at 48MHz as needed by the
USB port. Each MCU instruction takes 4 CPU clocks, giving a cycle time of about 83ns. To
use a different crystal, you’ll have to modify the MCU’s configuration settings
(mxg_config.c). Q1 is a transistor that uses pulse width modulation to control the brightness
of the LCD backlight.

The DB50XG daughter card is attached via connector WB1 (area 5). The pin numbering used
on WB1 matches the numbering printed on the DB50XG.

The outputs of the DB50XG are protected from an inadvertent short-circuit by a pair of op-
amp buffers (IC4A&B). A second pair of op-amps (IC5A&B) provides stereo outputs for
headphones; the volume controlled by a 50K dual-gang pot. These amplifiers are in area 6 in
Figure 2. The DB50XG outputs about 3V peak-to-peak, so I’ve included a way to attenuate
the line outputs. If no attenuation is required, R12 and R14 should be replaced with plain wire
links. As shown, they are 10K resistors and halve the output voltage (-6dB). You can select
an appropriate value to give the attenuation you need, but I wouldn’t go above about 50K for
each of these two resistors – and use 1% components to give good stereo matching.

The headphone amps can also be tweaked. As shown, they give a gain of 2 (+6dB – about 6V
peak-to-peak), but I found a gain of 1 (leaving resistors R16 and R18 out of the circuit
altogether) sufficient. I don’t recommend increasing the gain beyond 2, to minimise the risk
of distortion.

Area 7 shows the LCD, the two push buttons and the volume control. Header pins for these
are included in the circuit so that they can be connected via flying leads. Also here is the USB
connector. This, and the MIDI inputs and outputs could be either mounted on the circuit
board or panel-mounted, as you prefer.

Building the circuit


I used a breadboard for the prototype but techniques such as stripboard, custom PCB, etc.
could also be used. Unfortunately, the free version of Eagle only creates circuit boards up to
80x100mm, which is too small, so there is no PCB design at present.

Care should be taken when laying out the oscillator circuitry – keep track lengths short and
position the crystal and its capacitors close to the MCU. Also keep the analogue (op-amps)
area separate from the logic to avoid digital noise on the analogue signals.

The capacitors shown to the bottom right of the schematic are for general decoupling.
Positio123245678293
39 2329 232
 2 23929
. These provide good high
frequency decoupling. The larger value polarized capacitors are for bulk decoupling and are
not so critical in their placement.

Construction is straightforward. Anyone with basic electronic skills (soldering, reading a


schematic, and so on) will be able to put the unit together.

Power
The completed circuit needs Ground, +5V, +12V and -12V power supplies. +/-12V are only
needed if the daughterboard is used. If not, the op-amps can also be omitted and only +5V
and Ground are required.

Table 1 shows the current drawn under various conditions to allow you to tailor your power
supplies depending on what you choose to include in the design.

Condition +5V +12V -12V Notes


supply supply supply
No daughterboard, 50mA Not Not needed
backlight off needed
No daughterboard, 250mA Not Not needed
backlight on max. needed
Daughterboard playing 600mA 25mA 25mA My wind
music, backlight on max. (see note) controller draws
an additional
50mA from the
+12V rail
Table 1. Power supply measurements

Software: the brains behind mistralXG


mistralXG’s software is written in C, using the Microchip C18 compiler (student edition).
The compiler and MPLAB integrated development environment (IDE) are both available as
free Windows downloads from the Microchip website.

The individual modules are listed in Table 2. Each C module is shown with its directory path
relative to the main.c module and a brief description of its function. A matching *.h file for
each *.c module contains definitions used in the other modules. There are some additional *.h
files included with the framework to control things such as how the MCU senses when the
USB port is connected to the PC.

Module name Function Module name Function


mxg_config.c MCU configuration user\user.c Main user function
io_cfg.h MCU I/O pin user\mxg_xlcd.c LCD functions
configuration
main.c main() function user\mxg_usart.c EUSART functions
autofiles\usbdsc.c USB descriptors user\mxg_umsm.c USB to MIDI
usb\usbdefs\usbdefs_ conversion
std_dsc.h
system\usb\ Microchip USB user\mxg_musm.c MIDI to USB
usbmmap.c Firmware Framework conversion
system\usb\usbdrv\ files. user\mxg_msgs.c Messages
usbdrv.c
system\usb\usbctrltrf\ usbms.c is the user\mxg_iface.c User interface
usbctrltrf.c modified usbgen.c functions
system\usb\usb9\ from the framework. user\mxg_error.c Error functions
usb9.c
system\usb\class\ms\ user\mxg_eeprom.c EEPROM functions
usbms.c
system\interrupt\
interrupt.c
Table 2. Program structure and module function

Framework file changes


Several sample applications are included with the USB Firmware Framework. mistralXG is
based on the Generic sample with a few simple text changes to reflect that a MIDI Streaming
interface is being used. The framework comprises all modules in Table 2, with the exception
of those in the user\ directory.

Many framework files are used without change, but a few have to be changed. io_cfg.h
defines the pinout for Microchip’s standard evaluation boards, so this was modified to match
mistralXG’s I/O. Perhaps the trickiest file holds the USB descriptor (autofiles\usbdsc.c). This
file and its associated header file (usb\usbdefs\usbdefs_std_dsc.h) define the information that
is presented to the PC during enumeration. Getting them right is quite difficult – I managed to
blue screen my Windows XP system several times while experimenting with mistralXG’s
descriptors. It’s likely you’ll need to make descriptor changes if you plan to extend the
features of mistralXG – I recommend reading the relevant USB specifications carefully
before starting!

A couple of important pieces of information are held in the descriptors. These are the vendor
ID and product ID (VID and PID). The PC uses these to identify a device during
enumeration. mistralXG has its own unique VID/PID combination.

mistralXG application firmware


The main() routine (Example 1) first initializes the system by calling the framework
initialization code, which in turn calls the initialization routines for the application modules.
It then loops around servicing the user routines in ProcessIO() and USBTasks(), which
handles USB housekeeping routines such as disconnecting and reconnecting to the PC. The
USB code is not interrupt driven – ProcessIO() must execute promptly otherwise the USB
interface will fail. I’ll come back to this point later.

void main(void)
{
InitializeSystem();
while(1)
{
USBTasks(); // USB Tasks
ProcessIO(); // See user\user.c & .h
}
}

Example 1. main() routine

Interrupts provide the heartbeat


The heart of the mistralXG code is the interrupt routine (interrupt.c), which handles three
types of event: a timer running every 1ms, and receive and transmit interrupts from the
EUSART, generated by the incoming and outgoing MIDI streams.

The timer interrupt manages a number of things:


• A display refresh timer that updates the display every 100ms.
• A display timeout timer that returns the display to the Home Screen after a few seconds.
• The Running Status injection timer that ensures a MIDI command/status byte is sent at
least every 250ms.
• Switch debouncing to prevent spurious input from the push buttons.

MIDI data to and from mistralXG are handled by the interrupt routine and placed in buffers
for transmission to the PC and synth.

Keeping everything going – user.c


There are three main routines in user.c. UserInit() handles initialization of the various user
routines and is responsible for configuring the various peripherals. Next in line is
ProcessIO(), discussed later, and finally there is ServiceRequests(), responsible for the user
data flowing over the USB.
ProcessIO() – the scheduler
If interrupts form mistralXG’s heart, ProcessIO() is the brain. This routine keeps everything
in sync and on track. A pseudo-code skeleton of its function is shown in Example 2.

void ProcessIO(void)
{
if(MIDI data received) processNextByte();
if(buttons changed) handle the button that has been pressed;
if(data to write to EEPROM and it’s not busy) write data to EEPROM;
if(display timeout occurred) switch to Home Screen;
if(display refresh timer complete) refreshDisplay();
if (WritesToLCDPending and LCD not busy) nextChar();

// User Application USB tasks


if(USB not connected) disable MIDI to USB;
else { // USB is active
ServiceRequests();
}
}//end ProcessIO

void ServiceRequests(void)
{
if(incoming MIDI data ready) send it to the PC via USB;
if(USB-MIDI data packet ready) send it to the synth/MIDIOUT;
}//end ServiceRequests

Example 2. The ProcessIO() and ServiceRequests() user routines


If you look at the source code, you’ll see that there is a lot of additional fine detail to make
sure that everything works as it needs to, but the pseudo-code captures the essence of the
routine’s function, and I’ll take you through this step-by-step:

• It first checks whether a MIDI byte has been received by the EUSART. If so,
processNextByte() (mxg_musm.c) handles it by building it into a USB-MIDI packet. A
complete MIDI command message of one to three bytes is combined into a single packet,
with up to 16 packets being sent in a single USB transfer.
• Changes to the two push-buttons are checked for and handled in mxg_iface.c.
• If the user has changed an option, its setting is saved in the MCU’s EEPROM
(mxg_eeprom.c). Writes to the EEPROM take several milliseconds so they are scheduled
and processed when the hardware is available.
• If the display timeout timer has expired, the display is switched back to the Home Screen
(mxg_iface.c).
• If the display refresh timer has expired, a display refresh is scheduled (mxg_iface.c).
• Some display updates can take well over 1ms, so these are scheduled to occur when the
hardware is available (mxg_iface.c).

If the USB port is connected, ServiceRequests() is then called to handle the application’s
USB transfers, doing so in two stages:

• It first checks whether any incoming MIDI data has been assembled into packets for the
PC and, if so, sends them.
• Then it checks for received USB-MIDI packets and, if so, transfers extracted MIDI data
to the transmit buffer where the interrupt routine picks them up and sends them out to the
synth.

Timing is critical
I’ve used the word scheduled several times in the code description. This is important and I’ll
use the LCD as to illustrate why. Microchip provides a library of routines for driving an LCD
display with its C18 compiler, but it quickly became obvious that I couldn’t use it. This is
because many of the routines wait for the display to finish executing a command before
returning control to the calling routine. As mentioned earlier, if ProcessIO() holds things up
for too long, the USB code times out and fails.

To avoid this problem, the application code is written using non-blocking routines – that is,
routines that do not wait for an event to complete. I achieved this by setting flags to indicate
when actions are pending and waiting until the hardware is idle before executing them. It
takes 34 writes to completely update the screen. They are scheduled to start every 100ms and
ProcessIO() performs one write each time it executes and the display isn’t busy. Using this
technique, the 34 writes take just a few milliseconds.

Monitoring MIDI channels


The error flags were originally on the Home Screen. I decided that it would be a better use of
the display to monitor MIDI data, but wasn’t sure how to do it with the limited display
resources. Figure 3 shows how I solved the problem:

No data MIDI IN MIDI OUT Both IN & OUT

0x0C 0x0D 0x0E 0x0F


00001100 00001101 00001110 00001111
Set this bit for MIDI IN
Set this bit for MIDI OUT

Figure 3. MIDI monitoring user-defined characters


The LCD controller chip supports eight user-defined characters. By defining the four
characters shown, a single bit change indicates that a channel has received data. 16 characters
representing channels 1-16 are held in a buffer. After the character for a particular position is
written to the display, it is set to the “no data” state. Then, as each MIDI IN or OUT packet is
processed, the appropriate bit is switched on, changing the character code to indicate data
traffic IN and/or OUT – this is reflected on the display at the next refresh. The overhead to do
this is low as the channel number is easily obtained from the MIDI streams, and the MCU has
instructions that flip a single bit. You can see the monitor in action in Figure 4.

Figure 4. mistralXG Home Screen

Programming mistralXG’s MCU


Microchip provides a bootloader for the PIC18F2550 MCU. This sits in the first 2KB of
program memory and lets you reprogram the chip over the USB interface. I modified this to
suit the hardware configuration used by mistralXG. The bootloader hex file
(MistralBoot.hex) needs to be burned into the MCU. The push button connectors have been
arranged to double up as the In-Circuit Serial Programming (ICSP) interface, so this could be
used if you have a suitable programmer.

With the bootloader installed, holding the Select button down as you switch on puts
mistralXG into programming mode. You can then program the device with hex files from the
PC, using Microchip’s PDFSUSB.exe utility (included with the firmware framework). Use
the utility to program in mistralXG.hex and then cycle the power (leaving the push buttons
alone) to see the Splash Screen and start playing with your own mistralXG!

Conclusion
I had great fun developing mistralXG and learned a lot about USB and PIC peripherals. I
hope you feel as inspired to experiment as I did and build a mistralXG of your own or put the
code to use in some other personal project.

I plan to continue to enhance the project over time. One plan is to add external storage to the
design using the SCL and SDA pins on the MCU – these are not used at the moment. I may
also update the code to use a later version of the USB Firmware Framework. I’ll post any
new developments at my website (www.grapevyne.com/pic.projects).

Sidebars
Acknowledgements
Robert Lang’s MIDI-nator was the initial inspiration for me to start work on the design.
Thorsten Klose (www.ucapps.de) was generous in giving his expertise to answer some
technical MIDI-USB questions I had at the outset of the project, for running my MIDI-USB
implementation through his test suite, and for the suggestion to add what I’ve called an
injection timer to the running status code.

Useful websites
PIC MCU information: http://www.microchip.com
PIC discussion group: http://www.piclist.com
Eagle PCB layout software: http://www.cadsoft.de

Parts list
The parts are all very easily sourced from many vendors that advertise in Nuts & Volts. The
bill of materials and schematic are included with the code on the N&V website. I used the 28-
pin version of the MCU – any version will do, but it should be revision B6 (or later) of the
silicon. Earlier revisions have a design flaw that may corrupt MIDI data under certain
circumstances.

I used dual-in-line packages for ease of assembly. Resistors are all 1/4W, 5% unless noted
otherwise, and capacitors are 16V or higher – ceramic caps for values below 1uF, tantalum or
electrolytic for the larger, polarized caps.

Tools for construction


Construction is straightforward; just make sure you get the chips and polarized capacitors the
right way round. Programming the device requires a suitable device programmer to install the
bootloader. Once that is installed, the chip can be programmed with the mistralXG code via
the USB port.

A quick Google search found many sites from which you can purchase relatively cheap
programmers and programmer kits (<$100, some much less). Probably the best solution is
one of the programmers/debuggers from Microchip – the PICKit2 is good value at less than
$50 for a starter kit that includes an evaluation board.

Alternatively, I may be able to provide a pre-programmed PIC18F2550 for a nominal charge.


Anyone interested should first contact me at pic.projects@grapevyne.com to discuss
availability

Tools for Debug


A multimeter is handy for checking voltages and it is useful to have access to an oscilloscope
to check what various signals are doing. As long as you’ve not made any assembly errors,
mistralXG should just burst into life when you apply power. If that doesn’t happen, you’ll
need to be able to check that the oscillator is running and the MCU has reset correctly. The
Microchip programmer/debugger tools are invaluable in helping you to do this.

Skill rating: hardware 2, software 3-4


The software is a quite complex. It is well commented, however, so if you are familiar with
PIC peripherals and C, understanding the code will not be too difficult.

You might also like