Verified Stripboard Layouts!

“Bipolar” and “nonpolarized” mean essentially the same thing, the opposite of “polarized”. Usually “bipolar” refers to an electrolytic that is basically two polarized electrolytics back to back in the same package (and you can use two polarized electrolytics back to back as a substitute for a bipolar), but the point is to use a capacitor that isn’t polarized; whether that’s ceramic (always nonpolarized) or film (always nonpolarized) or electrolytic (either, but you want nonpolarized) isn’t that big a deal. Though film might perform better than ceramic in this case.

5 Likes

Ronnberg himself uses film or bipolar polarized electrolytic in his build. He doesn’t say anything about the non-polarized on his page.

Note the white stripe that indicates the negative side of the capacitor in this image:

And Here, how to properly connect the bipolar polarized electrolytic:
24

Following @Dud layout you will also be able to notice the correct polarization:

And as said by @analogoutput, using non-polarized electrolytics will not make any difference.

Link to the original article:
https://web.archive.org/web/20191220104258/http://familjenronnberg.se/~niklas/diy/eurorack/lfo/

4 Likes

Thanks @analogoutput!
as always your answer is at first quite a puzzle for a beginner like me, but after some puzzling and research that makes totally sense. Thanks for stimulating my learning curve!
I’ll experiment with some polyester Film caps I have.

1 Like

I did not notice the blue film cap…
thnx for pointing out!

1 Like

The conventional terminology is that “bipolar” electrolytics are synonymous with “nonpolarized” electrolytics, as differentiated from “polarized” electrolytics. Bipolar or nonpolarized electrolytics have no white stripe. So it does appear a polarized electrolytic was used. For use with an AC voltage I guess you can get away with it, but it would not be my preference.

2 Likes

You were very clear on this issue.
I wonder why the hell I still made the same mistake…
Thanks for correcting me.

Taking this opportunity I would like to ask:
Which ideal option? Since film caps with high capacitance values have a very large package because they are usually manufactured for higher voltages.
I wish I had a good option that didn’t take up so much space on the board layout.

What capacitance? Above 1uF bipolar electrolytics are probably best.

1 Like

Yes, that answers my question. Thank you!

I would like to have your opinion on this:

It’s safe? Or does it have any disadvantage (or recommended adptations in circuit) compared to the ready made types?
I had already researched this subject before, there are many disagreements but I didn’t find anything conclusive.

As I understand (I’m no expert) it’s safe and it works, the main disadvantage being footprint size.

2 Likes

Glad to know that. Thank’s for your time.

1 Like

I am in the process of laying out a pcb for the Triple Sloth version.

Am I correct to think that, since it is mostly op-amp, these circuits should also work fine with +/-15V?
Or should is foresee an LM7812/7912 optionally to be on the safe side?

3 Likes

I’d think ±15 V would be fine. You might want to adjust output levels. (I’m puzzled as to why some outputs (y and z for Inertia and Apathy) have voltage dividers while the rest don’t.)

Simple AS3364 Quad VCA board.

Dual ADSR with separate controls for each envelope generator.

Dual ADSR with controls shared between each envelope generator.

Stripboard layout for core building blocks of a basic polyphonic build based on Polykit’s Pi Pico DCO. Could probably be squeezed into a 4-voice module without too much issue, lots of options for modding. Hopefully useful for someone out there.

11 Likes

For the Engen8 chip, with some few components, you have possibility to easy add the “Time duration”, “Loop mod”, “Gate Loop”, “Hold” … fonctions of the chip.

I built one and these simple additions are really very useful :slight_smile:
if you want to take a look

4 Likes

That’s definitely a good point! I didn’t want to include them for myself but could easily add them to the layout above. Will add them in and post something in the unverified lounge.

Hi, I made a dual VCA as per this layout, so can confirm verified

6 Likes

Just finished making this little white noise daughterboard for a bigger S&H kosmo module build.

It’s 99% based on a @jkb design (itself based on a classic circuit), but with a cheaper op amp because I’m not dropping a whole euro on a chip. I like to split builds across multiple boards for easier debugging. No warranties, try it at home if you like to live dangerously.

Original project: GitHub - polykit/kosmo-sample-and-hold: Schematics and PCB layout for a sample&hold / noise module in Kosmo format

10 Likes

HAGIWO Generative Sequencer, built and verified. Very cool module…

8 Likes

The Atmega String Synth with MIDI input, built and therefore verified.


Also here is the code that is very hard to find/get:

/* Arduino Synth from
  https://janostman.wordpress.com/2016/01/15/how-to-build-your-very-own-string-synth/
*/

/* Original code by Jan Ostman,  Modified by Dave Field of Take The Moon
 *  
        https://moroccodave.com/
        https://takethemoon.com/
    
    Modifications are licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 
    To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/.
        
    MODS:
    *****

    1. Keyboard port scanning removed - this version is MIDI-only
    2. Potentiometer inputs assigned to lower analog pins for ATMega328 compatibility
    3. DIP switch on digital pins 6, 7, 8, 9 added to allow MIDI channel selection
    4. MIDI handler re-written to use stock MIDI library and to be channel-aware
    5. Any resulting redundant code and variables removed

*/

#include <MIDI.h>

#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

const unsigned char PS_2 = (1 << ADPS0);;
const unsigned char PS_4 = (1 << ADPS1);
const unsigned char PS_8 = (1 << ADPS1) | (1 << ADPS0);
const unsigned char PS_16 = (1 << ADPS2);
const unsigned char PS_32 = (1 << ADPS2) | (1 << ADPS0);
const unsigned char PS_64 = (1 << ADPS2) | (1 << ADPS1);
const unsigned char PS_128 = (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);

uint32_t NOTES[12] = {208065 >> 2, 220472 >> 2, 233516 >> 2, 247514 >> 2, 262149 >> 2, 277738 >> 2,
                      294281 >> 2, 311779 >> 2, 330390 >> 2, 349956 >> 2, 370794 >> 2, 392746 >> 2
                     };

const uint8_t ATTrates[32] = {
  1, 2, 3, 4, 5, 8, 12, 20, 32, 37, 43, 51, 64, 85, 128, 255, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};

const uint8_t RELrates[32] = {
  1, 2, 3, 4, 5, 8, 12, 20, 32, 37, 43, 51, 64, 85, 128, 255, 255, 128, 85, 64, 51, 43, 37, 32,
  20, 12, 8, 5, 4, 3, 2, 1
};

const uint8_t sinetable[256] PROGMEM = {
  127, 130, 133, 136, 139, 143, 146, 149, 152, 155, 158, 161, 164, 167, 170, 173, 176, 178,
  181, 184, 187, 190, 192, 195, 198, 200, 203, 205, 208, 210, 212, 215, 217, 219, 221,
  223, 225, 227, 229, 231, 233, 234, 236, 238, 239, 240,
  242, 243, 244, 245, 247, 248, 249, 249, 250, 251, 252, 252, 253, 253, 253, 254, 254, 254,
  254, 254, 254, 254, 253, 253, 253, 252, 252, 251, 250, 249, 249, 248, 247, 245, 244,
  243, 242, 240, 239, 238, 236, 234, 233, 231, 229, 227, 225, 223,
  221, 219, 217, 215, 212, 210, 208, 205, 203, 200, 198, 195, 192, 190, 187, 184, 181, 178,
  176, 173, 170, 167, 164, 161, 158, 155, 152, 149, 146, 143, 139, 136, 133, 130, 127,
  124, 121, 118, 115, 111, 108, 105, 102, 99, 96, 93, 90, 87, 84, 81, 78,
  76, 73, 70, 67, 64, 62, 59, 56, 54, 51, 49, 46, 44, 42, 39, 37, 35, 33, 31, 29, 27, 25, 23, 21,
  20, 18, 16, 15, 14, 12, 11, 10, 9, 7, 6, 5, 5, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
  2, 2, 3, 4, 5, 5, 6, 7, 9, 10, 11, 12, 14, 15, 16, 18, 20, 21, 23, 25, 27, 29, 31,
  33, 35, 37, 39, 42, 44, 46, 49, 51, 54, 56, 59, 62, 64, 67, 70, 73, 76, 78, 81, 84, 87, 90, 93,
  96, 99, 102, 105, 108, 111, 115, 118, 121, 124
};

volatile uint8_t lfocounter;
volatile uint8_t lfocounter2;
volatile uint16_t lfoval;
volatile uint16_t lfoval2;
volatile uint8_t GATED = 1;
uint8_t OSCNOTES[4];
int16_t volume = 0;
uint8_t envcnt = 10;

//-------- Synth parameters --------------
uint32_t FREQ[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; //DCO pitch
volatile uint32_t DETUNE = 0; //Osc spread or detune
volatile uint16_t LFO = 32; //Lfo rate 0-255
volatile uint8_t VCA = 255; //VCA level 0-255
volatile uint8_t ATTACK = 1; // ENV Attack rate 0-255
volatile uint8_t RELEASE = 1; // ENV Release rate 0-255
volatile uint8_t ENVELOPE = 0; // ENV Shape
volatile uint8_t TRIG = 0; //MIDItrig 1=note ON
volatile int16_t MOD;  //MODwheel
volatile int16_t BENDoffset;  //Pitchbend center
volatile uint32_t olddetune;
uint32_t DCOPH[16];
uint8_t integrators[16];
uint8_t delayline[256];
volatile uint8_t writepointer;
volatile uint8_t PHASERMIX;
uint8_t DCO;
int16_t ENV;

/* --------- MIDI ----------- */
/* DIP Switch Setting = Channel (1=ON, 0=OFF)
    0000 = 1   0001 = 2   0010 = 3   0011 = 4
    0100 = 5   0101 = 6   0110 = 7   0111 = 8
    1000 = 9   1001 = 10  1010 = 11  1011 = 12
    1100 = 13  1101 = 14  1110 = 15  1111 = 16

DIP Switch Pins */
#define DIP_SW1 5
#define DIP_SW2 6
#define DIP_SW3 7
#define DIP_SW4 8
MIDI_CREATE_DEFAULT_INSTANCE();
byte MIDI_CHANNEL = 1;




/*

   INITIALISE

*/
void setup() {
  
  //PWM and GATE outputs
  pinMode(11, OUTPUT);
  pinMode(10, OUTPUT);
   
  // Set up Timer 1 to send a sample every interrupt.
  cli();
  // Set CTC mode
  // Have to set OCR1A *after*, otherwise it gets reset to 0!
  TCCR1B = (TCCR1B & ~_BV(WGM13)) | _BV(WGM12);
  TCCR1A = TCCR1A & ~(_BV(WGM11) | _BV(WGM10));
  // No prescaler
  TCCR1B = (TCCR1B & ~(_BV(CS12) | _BV(CS11))) | _BV(CS10);
  // Set the compare register (OCR1A).
  // OCR1A is a 16-bit register, so we have to do this with interrupts disabled to be safe.
  OCR1A = 758; //F_CPU / SAMPLE_RATE;
  // Enable interrupt when TCNT1 == OCR1A
  TIMSK1 |= _BV(OCIE1A);
  //set timer0 interrupt at 61Hz
  TCCR0A = 0;// set entire TCCR0A register to 0
  TCCR0B = 0;// same for TCCR0B
  TCNT0 = 0;//initialize counter value to 0
  // set compare match register for 62hz increments
  OCR0A = 255;// = 61Hz
  // turn on CTC mode
  TCCR0A |= (1 << WGM01);
  // Set CS01 and CS00 bits for prescaler 1024
  TCCR0B |= (1 << CS02) | (0 << CS01) | (1 << CS00); //1024 prescaler
  // enable timer compare interrupt
  TIMSK0 |= (1 << OCIE0A);
  sei();
  // Set baud rate to 31,250. Requires modification if clock speed is not 16MHz.
  UBRR0H = ((F_CPU / 16 + 31250 / 2) / 31250 - 1) >> 8;
  UBRR0L = ((F_CPU / 16 + 31250 / 2) / 31250 - 1);
  // Set frame format to 8 data bits, no parity, 1 stop bit
  UCSR0C |= (1 << UCSZ01) | (1 << UCSZ00);
  // enable rx
  UCSR0B |= _BV(RXEN0);
  // USART RX interrupt enable bit on
  UCSR0B |= _BV(RXCIE0);
  // Set up Timer 2 to do pulse width modulation on the speaker pin.
  // Use internal clock (datasheet p.160)
  ASSR &= ~(_BV(EXCLK) | _BV(AS2));
  // Set fast PWM mode (p.157)
  TCCR2A |= _BV(WGM21) | _BV(WGM20);
  TCCR2B &= ~_BV(WGM22);
  // Do non-inverting PWM on pin OC2A (p.155)
  // On the Arduino this is pin 11.
  TCCR2A = (TCCR2A | _BV(COM2A1)) & ~_BV(COM2A0);
  TCCR2A &= ~(_BV(COM2B1) | _BV(COM2B0));
  // No prescaler (p.158)
  TCCR2B = (TCCR2B & ~(_BV(CS12) | _BV(CS11))) | _BV(CS10);
  // Set initial pulse width to the first sample.
  OCR2A = 128;
  // set up the ADC
  BENDoffset = analogRead(5);
  ADCSRA &= ~PS_128; // remove bits set by Arduino library
  // you can choose a prescaler from above. PS_16, PS_32, PS_64 or PS_128
  ADCSRA |= PS_128;
  ADMUX = 69;
  sbi(ADCSRA, ADSC);

  // MIDI channel DIP switches
  pinMode(DIP_SW1, INPUT_PULLUP);
  pinMode(DIP_SW2, INPUT_PULLUP);
  pinMode(DIP_SW3, INPUT_PULLUP);
  pinMode(DIP_SW4, INPUT_PULLUP);
  
  MIDI_CHANNEL = ReadDipSwitch();

  // Initialise MIDI
  MIDI.begin(MIDI_CHANNEL);
  MIDI.setThruFilterMode(midi::Thru::Off);

  // Initialise MIDI message handlers
  MIDI.setHandleNoteOff(midiNoteOffHandler);
  MIDI.setHandleNoteOn(midiNoteOnHandler);
}




/*

   MAIN LOOP

*/
void loop() {
 
  int8_t MUX = 3;
  while (1) {

    digitalWrite(10, TRIG);
    //--------------- ADC block -------------------------------------
    while (bit_is_set(ADCSRA, ADSC)) ; //Wait for ADC EOC
    if (MUX == 5) DETUNE = ((ADCL + (ADCH << 8)) >> 3);
    if (MUX == 5) MOD = ((ADCL + (ADCH << 8)) >> 2);
    if (MUX == 4) PHASERMIX = ((ADCL + (ADCH << 8)) >> 2);
    if (MUX == 3) ENVELOPE = ((ADCL + (ADCH << 8)) >> 5);
    if (MUX == 3) ATTACK = ATTrates[ENVELOPE];
    if (MUX == 3) RELEASE = RELrates[ENVELOPE];
    if (RELEASE == 255) GATED = 0;
    if (RELEASE != 255) GATED = 1;
    if (DETUNE != olddetune) {
      olddetune = DETUNE;
      for (uint8_t i = 0; i < 4; i++) {
        if (FREQ[i << 1]) {
          FREQ[(i << 1) | 1] = FREQ[i << 1] + (((FREQ[i << 1] / 50) >> 0) * DETUNE / 127);
        }
      }
    }
    MUX++;
    if (MUX > 5) MUX = 3;
    ADMUX = 64 | MUX; //Select MUX
    sbi(ADCSRA, ADSC); //start next conversation
    //--------------------------------------------------------------------
  }
}



/*

   MAIN INTERRUPT

*/
ISR(TIMER1_COMPA_vect) {

   // poll midi
   MIDI.read();

  //-------------------- 8 DCO block ------------------------------------------
  DCO = 0;
  for (uint8_t i = 0; i < 8; i++) {
    if (integrators[i]) integrators[i]--; //Decrement integrators
    DCOPH[i] += FREQ[i]; //Add freq to phaseacc's
    if (DCOPH[i] & 0x800000) { //Check for integrator reset
      DCOPH[i] &= 0x7FFFFF; //Trim NCO
      integrators[i] = 28; //Reset integrator
    }
    DCO += integrators[i];
  }
  writepointer++;
  delayline[writepointer] = DCO;
  DCO += (delayline[(writepointer - lfoval2) & 255] * PHASERMIX) >> 8;

  //------------------ VCA block ------------------------------------
#define M(MX, MX1, MX2) \
  asm volatile ( \
                 "clr r26 \n\t" \
                 "mulsu %B1, %A2 \n\t" \
                 "movw %A0, r0 \n\t" \
                 "mul %A1, %A2 \n\t" \
                 "add %A0, r1 \n\t" \
                 "adc %B0, r26 \n\t" \
                 "clr r1 \n\t" \
                 : \
                 "=&r" (MX) \
                 : \
                 "a" (MX1), \
                 "a" (MX2) \
                 : \
                 "r26" \
               )
  if ((ATTACK == 255) && (TRIG == 1)) VCA = 255;
  if (!(envcnt--)) {
    envcnt = 20;
    if (VCA < volume) VCA++;
    if (VCA > volume) VCA--;
  }
  M(ENV, (int16_t)DCO, VCA);
  OCR2A = ENV;

  //-------------- Calc Sample freq ---------------------------------
  OCR1A = 758 - lfoval;

}



/*

   LFO INTERRUPT

*/
ISR(TIMER0_COMPA_vect) {
   
  //------------------------------ LFO Block -----------------------
  lfocounter += LFO;
  lfoval = (pgm_read_byte_near( sinetable + lfocounter ) * MOD) >> 10; //LFO for pitch
  lfoval2 = pgm_read_byte_near( sinetable + (lfocounter2++) ); //LFO for the Phaser

  //--------------------- ENV block ---------------------------------
  if ((TRIG == 1) && (volume < 255)) {
    volume += ATTACK;
    if (volume > 255) volume = 255;
  }
  if ((TRIG == 0) && (volume > 0)) {
    volume -= RELEASE;
    if (volume < 0) volume = 0;
  }
}








/****************
  MIDI HANDLERS
*****************/
void midiNoteOnHandler(byte channel, byte note, byte velocity) {
  handleMIDINOTE(0x90, note, 127);
}

void midiNoteOffHandler(byte channel, byte note, byte velocity) {
  handleMIDINOTE(0x80, note, 0);
}


/*

   MISC METHODS

*/
//---------------- Get the base frequency for the MIDI note ---------------
uint32_t MIDI2FREQ(byte note) {
  uint8_t key = note % 12;
  if (note < 36) return (NOTES[key] >> (1 + (35 - note) / 12));
  if (note > 47) return (NOTES[key] << ((note - 36) / 12));
  return NOTES[key];
}



//---------------- Handle Notes---------------------------------------
void handleMIDINOTE(uint8_t message, uint8_t note, uint8_t vel) {
  uint8_t i;
  uint32_t freq;
  if ((!vel) && (message == 0x90)) message = 0x80;
  if (message == 0x80) {
    for (i = 0; i < 4; i++) {
      if (OSCNOTES[i] == note) {
        if (!GATED) {
          FREQ[i << 1] = 0;
          FREQ[(i << 1) | 1] = 0;
        }
        OSCNOTES[i] = 0;
      }
    }
    if (!(OSCNOTES[0] | OSCNOTES[1] | OSCNOTES[2] | OSCNOTES[3])) TRIG = 0;
    return;
  }
  if (message == 0x90) {
    if ((!TRIG) && (GATED)) {
      for (i = 0; i < 8; i++) {
        FREQ[i] = 0;
      }
    }
    i = 0;
    while (i < 4) {
      if (!OSCNOTES[i]) {
        freq = MIDI2FREQ(note);
        FREQ[i << 1] = freq;
        FREQ[(i << 1) | 1] = FREQ[i << 1] + (((FREQ[i << 1] / 50) >> 0) * DETUNE / 127);
        OSCNOTES[i] = note;
        if (!TRIG) {
          TRIG = 1;
        }
        return;
      }
      i++;
    }
  }
}

/*
 * Get MIDI channel from dipswitches
 */
byte ReadDipSwitch() {
  byte value = 0;
  if (digitalRead(DIP_SW4) == LOW) value += 1;
  if (digitalRead(DIP_SW3) == LOW) value += 2;
  if (digitalRead(DIP_SW2) == LOW) value += 4;
  if (digitalRead(DIP_SW1) == LOW) value += 8;
  return (value + 1);
}
10 Likes

It’s super-simple, but I just built myself a handful of lowpass gates based on the VTL5C3 vactrol:

The top strip acts as ground for everything. You can also use different pots for slightly different responses - I built a quad LPG using two 20Ks, one 10K and one 50K.

8 Likes