“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.
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:
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/
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.
I did not notice the blue film cap…
thnx for pointing out!
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.
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.
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.
Glad to know that. Thank’s for your time.
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?
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.
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
if you want to take a look
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.
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
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);
}