Showing posts with label MIDI. Show all posts
Showing posts with label MIDI. Show all posts

Saturday, November 7, 2015

DRSSTC Δt5: MIDI Streaming and Shutter Syncing

Until last week, I hadn't really touched my Tesla coil setup since I moved out to Seattle. Maybe because the next step was a whole bunch of software writing. As of Δt3, I had written a little test program that could send some basic frequency (resonant and pulse generation) and pulse shaping commands to the driver. But it was just for a fixed frequency of pulse generation and of course I really wanted to make a multi-track MIDI driver for it.

The number I had in mind was three tracks, to match the output capabilities of MIDI Scooter. While the concept was cool, the parsing/streaming implementation was flawed and the range of notes that you can play with a motor is kinda limited by the power electronics and motor RPM. So I reworked the parser and completely scrapped and rebuilt the streaming component of it. (More on that later.) Plus I did a lot of preliminary thinking on how best to play three notes using discrete pulses. As it turns out, the way that works best in most scenarios is also the easiest to code, since it uses the inherent interrupt prioritization and preemption capabilities that most microcontrollers have.


So despite my hesitation to start on the new software, it actually turned out to be a pretty simple coding task. It did require a lot of communication protocol code on both the coil driver and the GUI, to support sending commands and streaming MIDI events at the same time. But it went pretty smoothly. I don't think I've written as many lines of code before and had them mostly work on the first try. And the result is a MIDI parser/streamer that I can finally be proud of. Here it is undergoing my MIDI torture test, a song known only as "Track 1" from the SNES game Top Gear.




The note density is so high that it makes a really good test song for MIDI streaming. I only wish I had more even more tracks...


The workflow from .mid file to coil driver is actually pretty similar to MIDI scooter. First, I load and parse the MIDI, grouping events by track/channel. Then, I pick the three track/channel combinations I want to make up the notes for the coil. These get restructured into an array with absolute timestamps (still in MIDI ticks). The array is streamed wirelessly, 64 bytes at a time, to a circular buffer on the coil driver. The coil driver reports back what event index is currently playing, so the streamer can wait if the buffer is full.


On the coil driver itself, there are five timers. In order of interrupt priority:

  • The pulse timer, which controls the actual gate drive, runs in the 120-170kHz range and just cycles through a pre-calculated array of duty cycles for pulse shaping. It's always running, but it only sets duty cycles when a pulse is active. 
  • Then, there are three note timers that run at lower priority. Their rollover frequencies are the three MIDI note frequencies. When they roll over, they configure and start a new pulse and then wait for the pulse to end (including ringdown time). They're all equal priority interrupts, so they can't preempt each other. This ensures no overlap of pulses.
  • Lastly, there's the MIDI timer, which runs at the MIDI tick frequency and has the lowest interrupt priority. It checks for new MIDI events and updates the note timers accordingly. I'm actually using SysTick for this (sorry, SysTick) since I ran out of normal timers.
There are three levels of volume control involved as well. Relative channel volume is set by configuring the pulse length (how many resonant periods each pulse lasts). But since the driver was designed to be hard-switched, I'm also using duty cycle control for individual note volume. And there is a master volume that scales all the duty cycles equally. All of this is controlled through the GUI, which can send commands simultaneously while streaming notes, as shown in the video.


It's really nice to have such a high level of control over the pulse generation. For example, I also added a test mode that produces a single long pulse with gradually ramped duty cycle. This allows for making longer, quieter sparks with low power...good for testing.

I also got to set up an experiment that I've wanted to do ever since I got my Grasshopper 3 camera. The idea is to use the global shutter and external trigger capabilities of the industrial camera to image a Tesla coil arc at a precise time. Taking it one step further, I also have my favorite Tektronix 2445 analog oscilloscope and a current transformer. I thought that it would be really cool to have the scope trace of primary current and the arc in the same image at the same time, and then to sweep the trigger point along the duration of the pulse to see how they both evolve.




The setup for this was a lot of fun.

Camera is in the foreground, taped to a tripod because I lost my damn tripod adapter.
Using a picture frame glass as a reflective surface with a black background (and a neon bunny!).
I knew I wanted to keep the scope relatively far from the arc itself, but still wanted the image of the scope trace to appear near the spark and be in focus. So, I set up a reflective surface at a 45º angle and placed the scope about the same distance to the left as the arc is behind the mirror, so they could both be in focus. When imaged straight on, they appear side by side, but the scope trace is horizontally flipped, which is why the pulse progresses from right to left.


This picture is a longer exposure, so you can see the entire pulse on the scope. To make it sweep out the pulse and show the arc condition, I set the exposure to 20-50μs and had the trigger point sweep from the very start of the pulse to the end on successful pulses. So, each frame is actually a different pulse (should be clear from the arcs being differently-shaped) but the progression still shows the life cycle of the spark, including the ring-up period before the arc even forms.The pulse timer fires the trigger at the right point in the pulse through a GPIO on the microcontroller. Luckily, the trigger input on the camera is already optocoupled, so it didn't seem to have any issues with EMI.

Seeing the pulse shape and how it relates to arc length is really interesting. It might be useful for tuning to be able to see primary current waveform and arc images as different things are adjusted. No matter what, the effect is cool and I like that it can only really be done with old-school analog methods and mirrors (no smoke, yet).

Sunday, December 19, 2010

MIDI Scooter...

It was only a matter of time...


I merged my MIDI-playing code with my scooter-driving code and put them both on the new, rad-hardened 3ph v3.1 controller. A brief summary of what exactly is going on here:
  1. Laptop program parses a MIDI file and I choose three tracks that look interesting.
  2. XBee radio streams MIDI data wirelessly to the motor controller.
  3. Motor controller uses the frequency of the MIDI note as the PWM frequency.
  4. Each phase corresponds to one MIDI track. Three phases can play three tracks simultaneously.
For kicks, here's the code for both ends of the system.

Because the PWM frequency is independent of the motor speed, this all happens more-or-less in the background while the regular motor control algorithm runs in the foreground. Meaning, I can ride the scooter around at low speeds without noticing any difference in performance. At high speeds, when the note frequencies get close to the commutation frequencies, bad things would happen.

The real motivation for this was to demonstrate just how much extra processing power there is to be squandered in this motor control setup. The MIDI routine now dominates both the processor usage and the data bandwidth. The motor controller itself, which is full field-oriented sinusoidal control, runs in the leftover gaps, and is still fast enough to execute a 1kHz current/torque controller. Yay 32-bit processors.

Thursday, August 26, 2010

3ph HD: I'm serious about this MIDI thing,

While I've been waiting for the circuit boards for the 3ph HD, I could have been porting every piece of code I've ever written for microcontrollers over to the STM32F103. At least, all the things I would need to make a functioning motor controller: clocks and timers, PWM output, digital I/O, ADC, UART communication, interrupt handlers. All of that. It's a known unknown. In other words, I know what I have to do and I just haven't spent the time scouring the 1,072-page reference manual for all the right register and bit names to make that stuff work.

The "clock tree". Page 84/1072.

After I wade through the fundamentals, I'll also have to port over the actual motor control algorithm. It will be indistinguishable from the controller currently implemented on the 3ph Duo, a modified version of something called a synchronous current regulator. If you're really really interested, I updated my documentation of the control scheme to include some fun new graphs.

Oh, I get it now.

Enough of that.

Instead, I've been working on the unknown unknown: how to encode the three tracks of music to be played on the three phases of the motor? I settled quickly on MIDI, for two reasons. One: It efficiently encodes the type of audio I want to play, namely three tracks of music. No vocals. No intermediate frequencies. Just what you can find on the keys of a piano. I'm not saying you can't send pure audio to the motor (just use it like a speaker), but my goal is to replicate 8-bit video game music, which is a simpler format. Which brings me to the second reason for using MIDI: Tons and tons of 8-bit music libraries.

And yes, I have picked out the first test song. I've pretty much had the first song picked out since before I started the project. It's known only as "Track 1" and is from Top Gear for the SNES. (Trivia: The first video game I ever owned. It came way before the car show.) It's also got one of the best 8-bit soundtracks ever. If you don't believe me, ask Muse.

Time for another VB GUI, to piss off my programmer friends.


I must admit, I enjoyed writing a MIDI file parser. It's nice when you write code that won't cause things to blow up or fall over if it doesn't work. If it doesn't parse correctly you just...try again. Is this what I passed up by not pursuing my early career ambitions as a programmer? Risk/danger free debugging? Anyway, I know it's right now because the length of time of the parsed data is the length of the song, and I can see from the note visualization that it's at least got the general melodic form of "Track 1".

MIDI is actually pretty easy to parse, if you don't care about anything other than notes. Every event has a delta-time stamp. By incrementing an absolute time counter while stepping through the file, every note can be assigned an absolute time stamp. This is what I will send to the controller: [Timestamp, Note]. Since the controller can't store a full song in RAM, there will be three streams of this and some yet-to-be-determined buffering routine, but that's essentially the plan. There are "note on" and "note off" commands, but I'll lump everything into one packet format and send an inaudible frequency like 20kHz as the "off" note.

Now I should get back to the motor controller part...