1

I'm writing a MIDI - to square wave converter designed to interface between a MIDI instrument and a musical Tesla coil. Essentially, this should act like a monophonic MIDI synthesiser, but the requirements are slightly different to most projects:

  • I only want to generate (audible frequency) square waves.
  • It is essential that the pulse on time not exceed a configurable constant value, e.g. 160 microseconds.
  • I'd like to map the 'velocity' of a given note to the duty cycle, scaled appropriately.
  • Ideally, I'd like to generate envelopes for the pulse width with configurable attack, decay, sustain, release parameters.
  • I'd like to write the audio generation code "from scratch" for the sake of my own understanding of how timing registers work.

The gist of what I want is this (untested) sketch, but it obviously doesn't allow for control of the on time.

#include <MIDI.h>

MIDI_CREATE_DEFAULT_INSTANCE();
const static int out_pin = 9;

void handleNoteOn(byte channel, byte pitch, byte velocity)
{
    tone(out_pin, pitch);
    digitalWrite(13, HIGH);   // turn the LED on (HIGH is the voltage level)    
}


void handleNoteOff(byte channel, byte pitch, byte velocity)
{
    noTone(out_pin);
    digitalWrite(13, LOW);   // turn the LED off  
}

void setup() {
  MIDI.setHandleNoteOn(handleNoteOn);  
  MIDI.setHandleNoteOff(handleNoteOff);

  
  MIDI.begin(MIDI_CHANNEL_OMNI);
}

void loop() {
  // put your main code here, to run repeatedly:
  MIDI.read();
}

My question is essentially, what the best way to achieve this kind of musical pulse-width control is. The ideas I had were either a) A large number of manual register-sets to bit-bang the required signal out at the highest frequency I can get, probably in the kHz b) Create each pulse using a custom pulseOut function, using timer0 for microsecond delays

I'd also be open to using external analog circuitry, e.g. a voltage controlled pulse shortener.

The load here is an LED, so no special allowances need to be made for its behavior.

3
  • 1
    What Arduino board are you using?
    – chrisl
    Dec 17, 2021 at 10:20
  • 1
    did you see the arduino-toneac library?
    – Juraj
    Dec 17, 2021 at 10:20
  • An Arduino nano. I didn't see the toneaclibrary - though not exactly capable of what I need out of the box, it's very helpful to read! Dec 18, 2021 at 3:03

1 Answer 1

3

I would recommend using a hardware PWM signal generated by Timer 1, or another 16-bit timer if you have more than one (like on the Mega). The 16 bits provide both a decent frequency range and a good time resolution. This should provide jitter-free timings, unlike any software-based method. This will also avoid unduly loading the CPU.

The first thing to do would be to choose the prescaler and the operating mode. These choices will impact both the lowest available frequency and the time resolution. The latter affects the accuracy of the highest-pitched notes. Assuming your Arduino is clocked at 16 MHz, some sensible choices are:

  • prescaler = 64, normal PWM
  • prescaler = 8, phase correct PWM
  • prescaler = 8, normal PWM
prescaler PWM mode time resolution lowest freq. lowest note
64 normal 4 µs 3.81 Hz C−1 (MIDI 0)
8 phase correct 1 µs 15.26 Hz B0 (MIDI 11)
8 normal 0.5 µs 30.52 Hz B1 (MIDI 23)

For the sake of having accurate notes, I would go for the lowest time resolution that is compatible with the music you want to play.

Once this is configured, you can control the music by writing to three registers:

  • TCCR1A turns the PWM on and off
  • the register holding TOP (OCR1A or ICR1, depending on the mode) controls the frequency (the pitch)
  • a compare match register (OCR1A or OCR1B, depending on the channel) controls the duration of the pulses

Your program would then need to set the frequency as a function of the requested note, then continuously adjust the pulse duration in order to create the desired envelope while obeying the requested velocity and ensuring the value of 160 µs is never exceeded.

The main code does not even need to be synchronized with the timer: the PWM modes guarantee glitch-free PWM even when you change the duty cycle. Be careful though when changing the frequency: you may get huge glitches when increasing the frequency. It is safer to stop, clear and restart the timer when changing notes.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.