back to all projects

Embedded Systems

Grid Sequencer

A simple grid sequencer for composing and visualizing musical patterns, programmed in C

A demo of my finished grid sequencer.

I built a sequencer as my final project for 6.115, a class on microcontrollers and embedded systems. The goal was to bring an embedded software project from concept to working prototype, and to create something cool and creative along the way! You can find demo videos of my sequencer below.

Watch Percussion Demo

Watch Melody Demo

The Idea

Grid instruments, like the Novation Launchpad, are often used in electronic music production and live performance to create looping sound patterns. A simple one works like this:

The Idea: diagram

In the grid, rows represent different sounds, and columns represent different times. The user toggles buttons to indicate which sounds should be played and when. The sequencer cycles through the columns in order, playing the correct combination of sounds as it arrives at each column.

Setting Up the Button Pad

To assemble a grid with 4 rows and 8 columns, I bought 2 SparkFun 4x4 button pads, the corresponding breakout PCBs and bezels, and 3mm common anode RGB LEDs.

Setting up 32 buttons and LEDs, even in a matrix arrangement, involved a lot of soldering. At the end, my 4x8 button pad had 32 wires coming out of it! Managing them all was a learning experience.

Breakout PCB: front Breakout PCB: back

The front and back of the button pad breakout PCB. (SparkFun)

So many wires

It was hard to cut wires to the perfect length between the two boards.

Grid Software Interface

After assembling my grid controller, I set up a software interface for it. 6.115 provided each student with a PSoC board, which is like an instant chip inventory! In PSoC Creator, I could configure pins on the board to act like a variety of chips and reassign pins easily.

The software interface involved (1) matrix scanning to detect button presses, and (2) updating the LED matrix to reflect grid state.

Detecting Button Presses

Button press

A scope screenshot of a button press.

Driving LEDs

Now, I have a grid controller that turns on an LED when I press the corresponding button!

Playing Audio

The next step was to get my PSoC to play audio. Originally, I wanted to play 16-bit 44.1 kHz WAV drum samples using an external RAM. After wiring up my RAM, however, I realized, how am I supposed to transfer such a large amount of audio data onto the RAM every time the program starts up?

Converting WAV files to C arrays and putting them into code memory didn't seem feasible, because the PSoC internal memory was limited. I tried burning them onto a ROM chip, but I ran into unknown errors with the lab chip burners. I looked into using serial, but the setup seemed too complex for the timeframe I had.

Soon, I found a much simpler (though hackier) solution. I discovered that PSoC Creator's WaveDAC component allows for custom user-defined tables. If I converted my WAV files to CSV files of 8-bit raw audio data, then WaveDAC could play them!

WaveDAC

WaveDAC configuration with a custom table loaded into Waveform 1.

WaveDAC takes a max of 4000 table values, so I downsampled my WAV files to 16 kHz, trimmed and padded them to exactly 0.25 seconds, and removed the WAV metadata and headers (all in Audacity). Soon after... my PSoC played drums! (And the 8-bit 16 kHz samples didn't sound that bad, too!)

Timing and Sequencer Logic

The driving force of a sequencer is a timer that determines which column is active. I implemented this with a PSoC Timer component and had it generate a software interrupt on overflow (with the max load value, an interrupt was triggered every 2.731 ms).

Every NUM_INTERRUPTS interrupts, the interrupt handler increments the active column, starts the appropriate WaveDAC components, and turns on all LEDs in that column. The result is a "shifting" column of LEDs that shows the sequencer timing.

The tempo is set by the NUM_INTERRUPTS variable:

tempo = 60 / (NUM_INTERRUPTS * 0.002731)

The user can control the sequencer tempo by turning the PSoC's onboard potentiometer. I sampled the potentiometer value using a 12-bit PSoC ADC, and then mapped that to the NUM_INTERRUPTS variable. In the following formula, adcResult ranges from 0x0000 to 0x0FFF:

NUM_INTERRUPTS = 400 - (adcResult * 300) / 0x0FFF

This gives a NUM_INTERRUPTS range of 100-400, which corresponds to tempos of approximately 220 to 55 columns per minute.

Mixing Audio

One downside of using WaveDAC components is that it's difficult to manipulate their outputs in code — I couldn't sum the outputs of multiple WaveDAC components to play multiple sounds at once.

Analog mixer

An analog mixer example. (Jacob Smith)

I decided to build an analog mixer instead, following this tutorial by Jacob Smith. This added the bonus feature of being able to adjust relative volumes of sounds using potentiometers.

Additional Challenges

PSoC Creator Schematic

PSoC schematic

My final PSoC Creator schematic. View Code

Reflections

This project taught me the value of being flexible. Coming in, I had minimal experience with C programming or PSoC Creator, and I wasn't sure how to implement most of my ideas. Many times, the implementations I'd guessed didn't work out, and I learned a lot through rapidly adapting and exploring alternative strategies.

Overall, this final project, and 6.115 in general, have made me more confident in my ability to envision and implement embedded software projects. In the many long hours I've invested into this class, I've gained fluency with design patterns, working with lab equipment, reading data sheets, and integrating various types of chips into a system. I also had fun playing around with my finished grid sequencer :)

I especially found the audio aspects of the project — picking apart WAV files and manipulating them as data — quite interesting, and I'd like to explore that in more depth in the future.

Final demo

My finished sequencer: grid controller, breadboard, and PSoC. Speaker not pictured.
back to all projects