SAM2695 based synthesizer board with BSerial interface
A Compact, Feature-Rich General MIDI Synthesizer
BSerial – SYNTH is a powerful General MIDI-compliant synthesizer designed to deliver high-quality audio in an ultra-compact package. Measuring just 26mm x 23mm, it integrates seamlessly with your projects, offering exceptional sound quality, versatile connectivity, and advanced features powered by the SAM2695 chipset.
Here the BSerial SYNTH is shown hanging out with the Challenger RP2040 SD/RTC card (not included) to create a complete MIDI file player.
Key Features:
- General MIDI Compliance: Supports 128 instruments and percussion sounds, ideal for any musical application.
- High-Quality Sound: Driven by the SAM2695, featuring 64-voice polyphony and built-in effects.
- Compact Design: At just 26mm x 23mm, it’s easy to embed in any project.
- Flexible Power and Connectivity:
- Powered via a 3.3V supply from the BSerial interface (250mA).
- Single 4-pin flex cable for power and data communication.
- Audio Outputs:
- Line-level or headphone-ready 3.5mm stereo output with integrated 100µF capacitors for direct headphone drive.
- Integrated Effects: Includes reverb, chorus, and a 4-band equalizer for enhanced sound shaping.
- Reset Capability: Reset the SAM2695 via the RX line of the BSerial interface for added flexibility.
- On-chip CleanWave™: wavetable data, firmware, RAM delay lines
Applications:
- Embedded music synthesizers
- Compact MIDI instruments
- Audio-enhanced IoT devices
- DIY musical projects and kits
Technical Specifications:
Parameter | Specification |
---|---|
Dimensions | 26mm x 23mm |
Power Supply | 3.3V (via the BSerial interface) |
MIDI Compliance | General MIDI, 16 channels |
Audio Output | Stereo, 3.5mm jack (line-out or headphones) |
Polyphony | 64 voices (38 with effects) |
Onboard Effects | Reverb, chorus, 4-band EQ |
Reset Control | The BSerial RX line supports SAM2695 reset |
Getting Started:
- Connect Power & Data: Attach the 4-pin BSerial flex cable to your host device.
- Audio Output: Plug in headphones or connect to an amplifier via the 3.5mm stereo jack.
- MIDI Input: Start sending MIDI data from your host application to bring the synthesizer to life.
What is BSerial ?
BSerial is a part of iLabs concept (BConnect that spans over Bi2C, BSerial and BSpi) of connecting devices together using a small Flexible Flat Cable (FFC) and an FFC connector. It is similar to the Bi2C concept but we have replaced the electrical I2C interface with a asynchronous serial channel instead. You can read more about it here.
Connecting the module to your system takes only seconds and gives you instant General MIDI functionality.
Example
Here’s a short Arduino example showing how to build a simple MIDI player that plays MIDI files from an SD Card. This demo was built on the Challenger RP2040 SD/RTC board and the example is taken from the MD_MIDIFile library and modified slightly to run on the Challenger.
// Test playing a succession of MIDI files from the SD card.
// Example program to demonstrate the use of the MIDFile library
// Just for fun we blink the on board LED with the beat of the song
// This example was built on the Challenger RP2040 SD/RTC board
#include <SdFat.h>
#include <MD_MIDIFile.h>
#define USE_MIDI 1 // set to 1 to enable MIDI output, otherwise debug output
#if USE_MIDI // set up for direct MIDI serial output
#define DEBUG(s, x)
#define DEBUGX(s, x)
#define DEBUGS(s)
#define SERIAL_RATE 31250
#else // don't use MIDI to allow printing debug statements
#define DEBUG(s, x) do { Serial.print(F(s)); Serial.print(x); } while(false)
#define DEBUGX(s, x) do { Serial.print(F(s)); Serial.print(F("0x")); Serial.print(x, HEX); } while(false)
#define DEBUGS(s) do { Serial.print(F(s)); } while (false)
#define SERIAL_RATE 57600
#endif // USE_MIDI
// SDCARD_SS_PIN is defined for the built-in SD on some boards.
#ifndef SDCARD_SS_PIN
const uint8_t SD_CS_PIN = SS;
#else // SDCARD_SS_PIN
const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
#endif // SDCARD_SS_PIN
// SD chip select pin for SPI comms.
// Default SD chip select is the SPI SS pin (10 on Uno, 53 on Mega).
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SD_SCK_MHZ(16), &SPI1)
// LED definitions for status and user indicators
const uint8_t READY_LED = 31; // when finished
const uint8_t SMF_ERROR_LED = 31; // SMF error
const uint8_t SD_ERROR_LED = 31; // SD error
const uint8_t BEAT_LED = LED_BUILTIN; // toggles to the 'beat'
const uint16_t WAIT_DELAY = 2000; // ms
#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
// The files in the tune list should be located on the SD card
// or an error will occur opening the file and the next in the
// list will be opened (skips errors).
const char *tuneList[] =
{
"LOOPDEMO.MID", // simplest and shortest file
"NEVER.MID",
"BANDIT.MID",
"ELISE.MID",
"TWINKLE.MID",
"GANGNAM.MID",
"FUGUEGM.MID",
"POPCORN.MID",
"AIR.MID",
"PRDANCER.MID",
"MINUET.MID",
"FIRERAIN.MID",
"MOZART.MID",
"FERNANDO.MID",
"SONATAC.MID",
"SKYFALL.MID",
"XMAS.MID",
"GBROWN.MID",
"PROWLER.MID",
"IPANEMA.MID",
"JZBUMBLE.MID",
};
// These don't play as they need more than 16 tracks but will run if MIDIFile.h is changed
//#define MIDI_FILE "SYMPH9.MID" // 29 tracks
//#define MIDI_FILE "CHATCHOO.MID" // 17 tracks
//#define MIDI_FILE "STRIPPER.MID" // 25 tracks
SDFAT SD;
MD_MIDIFile SMF;
void midiCallback(midi_event *pev)
// Called by the MIDIFile library when a file event needs to be processed
// thru the midi communications interface.
// This callback is set up in the setup() function.
{
#if USE_MIDI
if ((pev->data[0] >= 0x80) && (pev->data[0] <= 0xe0))
{
Serial1.write(pev->data[0] | pev->channel);
Serial1.write(&pev->data[1], pev->size-1);
}
else
Serial1.write(pev->data, pev->size);
#endif
DEBUG("\n", millis());
DEBUG("\tM T", pev->track);
DEBUG(": Ch ", pev->channel+1);
DEBUGS(" Data");
for (uint8_t i=0; i<pev->size; i++)
DEBUGX(" ", pev->data[i]);
}
void sysexCallback(sysex_event *pev)
// Called by the MIDIFile library when a system Exclusive (sysex) file event needs
// to be processed through the midi communications interface. Most sysex events cannot
// really be processed, so we just ignore it here.
// This callback is set up in the setup() function.
{
DEBUG("\nS T", pev->track);
DEBUGS(": Data");
for (uint8_t i=0; i<pev->size; i++)
DEBUGX(" ", pev->data[i]);
}
void midiSilence(void)
// Turn everything off on every channel.
// Some midi files are badly behaved and leave notes hanging, so between songs turn
// off all the notes and sound
{
midi_event ev;
// All sound off
// When All Sound Off is received all oscillators will turn off, and their volume
// envelopes are set to zero as soon as possible.
ev.size = 0;
ev.data[ev.size++] = 0xb0;
ev.data[ev.size++] = 120;
ev.data[ev.size++] = 0;
for (ev.channel = 0; ev.channel < 16; ev.channel++)
midiCallback(&ev);
}
void setup(void)
{
while (!Serial)
delay(100);
// Set up LED pins
pinMode(READY_LED, OUTPUT);
pinMode(SD_ERROR_LED, OUTPUT);
pinMode(SMF_ERROR_LED, OUTPUT);
pinMode(BEAT_LED, OUTPUT);
// reset LEDs
digitalWrite(READY_LED, LOW);
digitalWrite(SD_ERROR_LED, LOW);
digitalWrite(SMF_ERROR_LED, LOW);
digitalWrite(BEAT_LED, LOW);
Serial1.setTX(0);
Serial1.begin(SERIAL_RATE);
DEBUGS("\n[MidiFile Play List]");
// Initialize SD
Serial.print("Configuring SPI port for SD Card support....");
SPI1.setRX(12);
SPI1.setTX(11);
SPI1.setSCK(10);
if (!SD.begin(SD_CONFIG))
{
Serial.println("SD init fail!");
digitalWrite(SD_ERROR_LED, HIGH);
while (true) ;
}
Serial.println(" Done!");
// Initialize MIDIFile
SMF.begin(&SD);
SMF.setMidiHandler(midiCallback);
SMF.setSysexHandler(sysexCallback);
digitalWrite(READY_LED, HIGH);
}
void tickMetronome(void)
// flash a LED to the beat
{
static uint32_t lastBeatTime = 0;
static boolean inBeat = false;
uint16_t beatTime;
beatTime = 60000/SMF.getTempo(); // msec/beat = ((60sec/min)*(1000 ms/sec))/(beats/min)
if (!inBeat)
{
if ((millis() - lastBeatTime) >= beatTime)
{
lastBeatTime = millis();
digitalWrite(BEAT_LED, HIGH);
inBeat = true;
}
}
else
{
if ((millis() - lastBeatTime) >= 100) // keep the flash on for 100ms only
{
digitalWrite(BEAT_LED, LOW);
inBeat = false;
}
}
}
void loop(void)
{
static enum { S_IDLE, S_PLAYING, S_END, S_WAIT_BETWEEN } state = S_IDLE;
static uint16_t currTune = ARRAY_SIZE(tuneList);
static uint32_t timeStart;
switch (state)
{
case S_IDLE: // now idle, set up the next tune
{
int err;
DEBUGS("\nS_IDLE");
digitalWrite(READY_LED, LOW);
digitalWrite(SMF_ERROR_LED, LOW);
currTune++;
if (currTune >= ARRAY_SIZE(tuneList))
currTune = 0;
// use the next file name and play it
DEBUG("\nFile: ", tuneList[currTune]);
err = SMF.load(tuneList[currTune]);
if (err != MD_MIDIFile::E_OK)
{
DEBUG(" - SMF load Error ", err);
digitalWrite(SMF_ERROR_LED, HIGH);
timeStart = millis();
state = S_WAIT_BETWEEN;
DEBUGS("\nWAIT_BETWEEN");
}
else
{
DEBUGS("\nS_PLAYING");
state = S_PLAYING;
}
}
break;
case S_PLAYING: // play the file
DEBUGS("\nS_PLAYING");
if (!SMF.isEOF())
{
if (SMF.getNextEvent())
tickMetronome();
}
else
state = S_END;
break;
case S_END: // done with this one
DEBUGS("\nS_END");
SMF.close();
midiSilence();
timeStart = millis();
state = S_WAIT_BETWEEN;
DEBUGS("\nWAIT_BETWEEN");
break;
case S_WAIT_BETWEEN: // signal finished with a dignified pause
digitalWrite(READY_LED, HIGH);
if (millis() - timeStart >= WAIT_DELAY)
state = S_IDLE;
break;
default:
state = S_IDLE;
break;
}
}
Reviews
There are no reviews yet.