Merge pull request #8 from Theremingenieur/improve-volume-response

Cumulative update V3.1
pull/18/head
Urs Gaudenz 4 years ago committed by GitHub
commit 856b9d78c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 56
      Open_Theremin_V3/OTPinDefs.h
  2. 6
      Open_Theremin_V3/Open_Theremin_V3.ino
  3. 104
      Open_Theremin_V3/SPImcpDAC.h
  4. 66
      Open_Theremin_V3/application.cpp
  5. 93
      Open_Theremin_V3/ihandlers.cpp
  6. 4
      Open_Theremin_V3/ihandlers.h
  7. 151
      Open_Theremin_V3/mcpDac.h
  8. 10
      README.md
  9. 22
      README.md~

@ -1,56 +0,0 @@
/**
* \file
* Pin definitions
*/
#ifndef WavePinDefs_h
#define WavePinDefs_h
//------------------------------------------------------------------------------
// DAC pin definitions
// LDAC may be connected to ground to save a pin
/** Set USE_MCP_DAC_LDAC to 0 if LDAC is grounded. */
#define USE_MCP_DAC_LDAC 1
// use arduino pins 2, 3, 4, 5 for DAC
// pin 2 is DAC chip select
/** Data direction register for DAC chip select. */
#define MCP_DAC_CS_DDR DDRB
#define MCP_DAC2_CS_DDR DDRB
/** Port register for DAC chip select. */
#define MCP_DAC_CS_PORT PORTB
/** Port bit number for DAC chip select. */
#define MCP_DAC_CS_BIT 2
#define MCP_DAC2_CS_BIT 1
// pin 3 is DAC serial clock
/** Data direction register for DAC clock. */
#define MCP_DAC_SCK_DDR DDRB
/** Port register for DAC clock. */
#define MCP_DAC_SCK_PORT PORTB
/** Port bit number for DAC clock. */
#define MCP_DAC_SCK_BIT 5
// pin 4 is DAC serial data in
/** Data direction register for DAC serial in. */
#define MCP_DAC_SDI_DDR DDRB
/** Port register for DAC clock. */
#define MCP_DAC_SDI_PORT PORTB
/** Port bit number for DAC clock. */
#define MCP_DAC_SDI_BIT 3
// pin 5 is LDAC if used
#if USE_MCP_DAC_LDAC
/** Data direction register for Latch DAC Input. */
#define MCP_DAC_LDAC_DDR DDRD
/** Port register for Latch DAC Input. */
#define MCP_DAC_LDAC_PORT PORTD
/** Port bit number for Latch DAC Input. */
#define MCP_DAC_LDAC_BIT 7
#endif // USE_MCP_DAC_LDAC
#endif // WavePinDefs_h

@ -1,7 +1,7 @@
/*
* Open.Theremin control software for Arduino UNO
* Version 3.0
* Copyright (C) 2010-2016 by Urs Gaudenz
* Version 3.1
* Copyright (C) 2010-2020 by Urs Gaudenz
*
* Open.Theremin control software is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published
@ -19,6 +19,7 @@
* With important contributions by
* David Harvey
* Michael Margolis
* "Theremingenieur" Thierry Frenkel
*/
/**
@ -76,4 +77,3 @@ void setup() {
void loop() {
app.loop();
}

@ -0,0 +1,104 @@
/* Control the mcp 4921/4922 DACs with hardware SPI of the Arduino UNO
* ...without all the overhead of the Arduino SPI lib...
* Just the needed functions in a runtime optimized way by "Theremingenieur" Thierry Frenkel
* This file is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*/
#ifndef SPImcpDac_h
#define SPImcpDac_h
#include <Arduino.h>
// Data direction & Port register & Bit number for DAC Latch:
#define MCP_DAC_LDAC_DDR DDRD
#define MCP_DAC_LDAC_PORT PORTD
#define MCP_DAC_LDAC_BIT 7
// Data direction & Port register & Bit number for DAC CS
#define MCP_DAC_CS_DDR DDRB
#define MCP_DAC_CS_PORT PORTB
#define MCP_DAC_CS_BIT 2
// Data direction & Port register & Bit number for DAC2 CS
#define MCP_DAC2_CS_DDR DDRB
#define MCP_DAC2_CS_PORT PORTB
#define MCP_DAC2_CS_BIT 1
// Data direction & Port registers & Bit numbers for Hardware SPI
#define HW_SPI_DDR DDRB
#define HW_SPI_SCK_BIT 5
#define HW_SPI_MISO_BIT 4 // unused in this configuration
#define HW_SPI_MOSI_BIT 3
static inline void SPImcpDACinit()
{
// initialize the latch pin:
MCP_DAC_LDAC_DDR |= _BV(MCP_DAC_LDAC_BIT);
MCP_DAC_LDAC_PORT |= _BV(MCP_DAC_LDAC_BIT);
// initialize the CS pins:
MCP_DAC_CS_DDR |= _BV(MCP_DAC_CS_BIT);
MCP_DAC_CS_PORT |= _BV(MCP_DAC_CS_BIT);
MCP_DAC2_CS_DDR |= _BV(MCP_DAC2_CS_BIT);
MCP_DAC2_CS_PORT |= _BV(MCP_DAC2_CS_BIT);
// initialize the hardware SPI pins:
HW_SPI_DDR |= _BV(HW_SPI_SCK_BIT);
HW_SPI_DDR |= _BV(HW_SPI_MOSI_BIT);
// initialize the hardware SPI registers
SPCR = _BV(SPE) | _BV(MSTR); // no interrupt, SPI enable, MSB first, SPI master, SPI mode 0, clock = f_osc/4 (maximum)
SPSR = _BV(SPI2X); // double the SPI clock, ideally we get 8 MHz, so that a 16bit word goes out in 3.5us (5.6us when called from an interrupt) including CS asserting/deasserting
}
static inline void SPImcpDACtransmit(uint16_t data)
{
// Send highbyte and wait for complete
SPDR = highByte(data);
asm("nop");
while (!(SPSR & _BV(SPIF)))
;
// Send lowbyte and wait for complete
SPDR = lowByte(data);
asm("nop");
while (!(SPSR & _BV(SPIF)))
;
}
static inline void SPImcpDAClatch()
{
MCP_DAC_LDAC_PORT &= ~_BV(MCP_DAC_LDAC_BIT);
MCP_DAC_LDAC_PORT |= _BV(MCP_DAC_LDAC_BIT);
}
static inline void SPImcpDACsend(uint16_t data)
{
MCP_DAC_CS_PORT &= ~_BV(MCP_DAC_CS_BIT);
// Sanitize input data and add DAC config MSBs
data &= 0x0FFF;
data |= 0x7000;
SPImcpDACtransmit(data);
MCP_DAC_CS_PORT |= _BV(MCP_DAC_CS_BIT);
// Do not latch immpediately, let's do it at the very beginning of the next interrupt to get consistent timing
}
static inline void SPImcpDAC2Asend(uint16_t data)
{
MCP_DAC2_CS_PORT &= ~_BV(MCP_DAC2_CS_BIT);
// Sanitize input data and add DAC config MSBs
data &= 0x0FFF;
data |= 0x7000;
SPImcpDACtransmit(data);
MCP_DAC2_CS_PORT |= _BV(MCP_DAC2_CS_BIT);
SPImcpDAClatch();
}
static inline void SPImcpDAC2Bsend(uint16_t data)
{
MCP_DAC2_CS_PORT &= ~_BV(MCP_DAC2_CS_BIT);
// Sanitize input data and add DAC config MSBs
data &= 0x0FFF;
data |= 0xF000;
SPImcpDACtransmit(data);
MCP_DAC2_CS_PORT |= _BV(MCP_DAC2_CS_BIT);
SPImcpDAClatch();
}
#endif

@ -3,7 +3,7 @@
#include "application.h"
#include "hw.h"
#include "mcpDac.h"
#include "SPImcpDAC.h"
#include "ihandlers.h"
#include "timer.h"
#include "EEPROM.h"
@ -42,13 +42,13 @@ void Application::setup() {
digitalWrite(Application::LED_PIN_1, HIGH); // turn the LED off by making the voltage LOW
mcpDacInit();
SPImcpDACinit();
EEPROM.get(0,pitchDAC);
EEPROM.get(2,volumeDAC);
mcpDac2ASend(pitchDAC);
mcpDac2BSend(volumeDAC);
SPImcpDAC2Asend(pitchDAC);
SPImcpDAC2Bsend(volumeDAC);
initialiseTimer();
@ -164,9 +164,8 @@ void Application::loop() {
uint16_t pitchPotValue = 0;
int registerPotValue,registerPotValueL = 0;
int wavePotValue,wavePotValueL = 0;
uint8_t registerValue = 0;
uint8_t registerValue = 2;
uint16_t tmpVolume;
mloop: // Main loop avoiding the GCC "optimization"
@ -179,7 +178,19 @@ void Application::loop() {
if (((wavePotValue-wavePotValueL) >= HYST_VAL) || ((wavePotValueL-wavePotValue) >= HYST_VAL)) wavePotValueL=wavePotValue;
vWavetableSelector=wavePotValueL>>7;
registerValue=4-(registerPotValueL>>8);
// New register pot configuration:
// Left = -1 octave, Center = +/- 0, Right = +1 octave
if (registerPotValue > 681)
{
registerValue = 1;
} else if(registerPotValue < 342)
{
registerValue = 3;
} else
{
registerValue = 2;
}
if (_state == PLAYING && HW_BUTTON_PRESSED) {
_state = CALIBRATING;
@ -251,7 +262,7 @@ void Application::loop() {
// set wave frequency for each mode
switch (_mode) {
case MUTE : /* NOTHING! */; break;
case NORMAL : setWavetableSampleAdvance((pitchCalibrationBase-pitch_v)/registerValue+2048-(pitchPotValue<<2)); break;
case NORMAL : setWavetableSampleAdvance(((pitchCalibrationBase-pitch_v)+2048-(pitchPotValue<<2))>>registerValue); break;
};
// HW_LED2_OFF;
@ -276,9 +287,12 @@ void Application::loop() {
// vol_v = vol_v - (1 + MAX_VOLUME - (volumePotValue << 2));
vol_v = vol_v ;
vol_v = max(vol_v, 0);
vScaledVolume = vol_v >> 4;
tmpVolume = vol_v >> 4;
// Give vScaledVolume a pseudo-exponential characteristic:
vScaledVolume = tmpVolume * (tmpVolume + 2);
volumeValueAvailable = false;
volumeValueAvailable = false;
}
goto mloop; // End of main loop
@ -327,7 +341,7 @@ static long pitchfn = 0;
InitialisePitchMeasurement();
interrupts();
mcpDacInit();
SPImcpDACinit();
qMeasurement = GetQMeasurement(); // Measure Arudino clock frequency
Serial.print("Arudino Freq: ");
@ -344,13 +358,13 @@ Serial.print("\nPitch Set Frequency: ");
Serial.println(pitchfn);
mcpDac2BSend(1600);
SPImcpDAC2Bsend(1600);
mcpDac2ASend(pitchXn0);
SPImcpDAC2Asend(pitchXn0);
delay(100);
pitchfn0 = GetPitchMeasurement();
mcpDac2ASend(pitchXn1);
SPImcpDAC2Asend(pitchXn1);
delay(100);
pitchfn1 = GetPitchMeasurement();
@ -362,11 +376,11 @@ Serial.println(pitchfn1);
while(abs(pitchfn0-pitchfn1)>CalibrationTolerance){ // max allowed pitch frequency offset
mcpDac2ASend(pitchXn0);
SPImcpDAC2Asend(pitchXn0);
delay(100);
pitchfn0 = GetPitchMeasurement()-pitchfn;
mcpDac2ASend(pitchXn1);
SPImcpDAC2Asend(pitchXn1);
delay(100);
pitchfn1 = GetPitchMeasurement()-pitchfn;
@ -411,7 +425,7 @@ static long volumefn = 0;
InitialiseVolumeMeasurement();
interrupts();
mcpDacInit();
SPImcpDACinit();
volumeXn0 = 0;
@ -424,12 +438,12 @@ Serial.print("\nVolume Set Frequency: ");
Serial.println(volumefn);
mcpDac2BSend(volumeXn0);
SPImcpDAC2Bsend(volumeXn0);
delay_NOP(44316);//44316=100ms
volumefn0 = GetVolumeMeasurement();
mcpDac2BSend(volumeXn1);
SPImcpDAC2Bsend(volumeXn1);
delay_NOP(44316);//44316=100ms
volumefn1 = GetVolumeMeasurement();
@ -443,11 +457,11 @@ Serial.println(volumefn1);
while(abs(volumefn0-volumefn1)>CalibrationTolerance){
mcpDac2BSend(volumeXn0);
SPImcpDAC2Bsend(volumeXn0);
delay_NOP(44316);//44316=100ms
volumefn0 = GetVolumeMeasurement()-volumefn;
mcpDac2BSend(volumeXn1);
SPImcpDAC2Bsend(volumeXn1);
delay_NOP(44316);//44316=100ms
volumefn1 = GetVolumeMeasurement()-volumefn;
@ -474,7 +488,7 @@ EEPROM.put(2,volumeXn0);
HW_LED2_OFF;
HW_LED1_ON;
Serial.println("\nCALIBRATION COMPTLETED\n");
Serial.println("\nCALIBRATION COMPLETED\n");
}
void Application::hzToAddVal(float hz) {
@ -482,7 +496,7 @@ void Application::hzToAddVal(float hz) {
}
void Application::playNote(float hz, uint16_t milliseconds = 500, uint8_t volume = 255) {
vScaledVolume = volume;
vScaledVolume = volume * (volume + 2);
hzToAddVal(hz);
millitimer(milliseconds);
vScaledVolume = 0;
@ -512,7 +526,3 @@ void Application::delay_NOP(unsigned long time) {
__asm__ __volatile__ ("nop");
}
}

@ -1,9 +1,8 @@
#include "Arduino.h"
#include "ihandlers.h"
#include "mcpDac.h"
#include "SPImcpDAC.h"
#include "timer.h"
#include "build.h"
#include "theremin_sintable.c"
@ -15,7 +14,7 @@
#include "theremin_sintable7.c"
#include "theremin_sintable8.c"
const int16_t* const wavetables[] PROGMEM = {
const int16_t* const wavetables[] = { //Fixed following a suggestion by Michael Freitas, does not need to be in PROGMEM
sine_table,
sine_table2,
sine_table3,
@ -26,12 +25,20 @@ const int16_t* const wavetables[] PROGMEM = {
sine_table8
};
static const uint32_t MCP_DAC_BASE = 2047;
static const uint32_t MCP_DAC_BASE = 2048;
#define INT0_STATE (PIND & (1<<PORTD2))
#define PC_STATE (PINB & (1<<PORTB0))
volatile uint8_t vScaledVolume = 0;
// Added by ThF 20200419
// #define TH_DEBUG // <-- comment this out for normal operation
// end
#ifdef TH_DEBUG
#include "hw.h"
#endif
volatile uint16_t vScaledVolume = 0;
volatile uint16_t vPointerIncrement = 0;
volatile uint16_t pitch = 0; // Pitch value
@ -97,69 +104,41 @@ void ihInitialiseVolumeMeasurement() //Measurement of variable frequency oscilla
}
/* 16 bit by 8 bit multiplication */
static inline uint32_t mul_16_8(uint16_t a, uint8_t b)
{
uint32_t product;
asm (
"mul %A1, %2\n\t"
"movw %A0, r0\n\t"
"clr %C0\n\t"
"clr %D0\n\t"
"mul %B1, %2\n\t"
"add %B0, r0\n\t"
"adc %C0, r1\n\t"
"clr r1"
:
"=&r" (product)
:
"r" (a), "r" (b));
return product;
}
/* Externaly generated 31250 Hz Interrupt for WAVE generator (32us) */
ISR (INT1_vect) {
// Interrupt takes up a total of max 25 us
// Interrupt takes up normally 14us but can take up to 22us when interrupted by another interrupt.
disableInt1(); // Disable External Interrupt INT1 to avoid recursive interrupts
// Enable Interrupts to allow counter 1 interrupts
interrupts();
// Added by ThF 20200419
#ifdef TH_DEBUG
HW_LED2_ON;
#endif
int16_t waveSample;
uint32_t scaledSample;
uint16_t offset = (uint16_t)(pointer>>6) & 0x3ff;
// Latch previously written DAC value:
SPImcpDAClatch();
disableInt1(); // Disable External Interrupt INT1 to avoid recursive interrupts
// Enable Interrupts to allow counter 1 interrupts
interrupts();
int16_t waveSample;
uint32_t scaledSample = 0;
uint16_t offset = (uint16_t)(pointer >> 6) & 0x3ff;
#if CV_ENABLED // Generator for CV output
vPointerIncrement = min(vPointerIncrement, 4095);
mcpDacSend(vPointerIncrement); //Send result to Digital to Analogue Converter (audio out) (9.6 us)
mcpDacSend(vPointerIncrement); //Send result to Digital to Analogue Converter (audio out) (5.5 us)
#else //Play sound
// Read next wave table value (3.0us)
// The slightly odd tactic here is to provide compile-time expressions for the wavetable
// positions. Making addr1 the index into the wavtables array breaks the time limit for
// the interrupt handler
switch (vWavetableSelector) {
case 1: waveSample = (int16_t) pgm_read_word_near(wavetables[1] + offset); break;
case 2: waveSample = (int16_t) pgm_read_word_near(wavetables[2] + offset); break;
case 3: waveSample = (int16_t) pgm_read_word_near(wavetables[3] + offset); break;
case 4: waveSample = (int16_t) pgm_read_word_near(wavetables[4] + offset); break;
case 5: waveSample = (int16_t) pgm_read_word_near(wavetables[5] + offset); break;
case 6: waveSample = (int16_t) pgm_read_word_near(wavetables[6] + offset); break;
case 7: waveSample = (int16_t) pgm_read_word_near(wavetables[7] + offset); break;
default: waveSample = (int16_t) pgm_read_word_near(wavetables[0] + offset); break;
};
// Read next wave table value
waveSample = (int16_t)pgm_read_word_near(wavetables[vWavetableSelector] + offset);
if (waveSample > 0) { // multiply 16 bit wave number by 8 bit volume value (11.2us / 5.4us)
scaledSample = MCP_DAC_BASE + (mul_16_8(waveSample, vScaledVolume) >> 8);
} else {
scaledSample = MCP_DAC_BASE - (mul_16_8(-waveSample, vScaledVolume) >> 8);
}
scaledSample = ((int32_t)waveSample * (uint32_t)vScaledVolume) >> 16; // The compiler optimizes this better than any assembly written by hand !!!
mcpDacSend(scaledSample); //Send result to Digital to Analogue Converter (audio out) (9.6 us)
SPImcpDACsend(scaledSample + MCP_DAC_BASE); //Send result to Digital to Analogue Converter (audio out) (5.5 us)
pointer = pointer + vPointerIncrement; // increment table pointer (ca. 2us)
pointer += vPointerIncrement; // increment table pointer
#endif //CV play sound
incrementTimer(); // update 32us timer
@ -190,6 +169,10 @@ ISR (INT1_vect) {
noInterrupts();
enableInt1();
// Added by ThF 20200419
#ifdef TH_DEBUG
HW_LED2_OFF;
#endif
}
/* VOLUME read - interrupt service routine for capturing volume counter value */
@ -216,5 +199,3 @@ ISR(TIMER1_OVF_vect)
{
timer_overflow_counter++;
}

@ -3,7 +3,7 @@
extern volatile uint16_t pitch; // Pitch value
extern volatile uint16_t vol; // Volume value
extern volatile uint8_t vScaledVolume; // Volume byte
extern volatile uint16_t vScaledVolume; // Volume byte
extern volatile uint16_t pitch_counter; // Pitch counter
extern volatile uint16_t pitch_counter_l; // Last value of pitch counter
@ -19,7 +19,7 @@ extern volatile bool pitchValueAvailable; // Pitch read flag
extern volatile bool reenableInt1; // Pitch read flag
extern volatile uint8_t vWavetableSelector;
extern volatile uint16_t vPointerIncrement; // Table pointer increment
extern volatile uint16_t vPointerIncrement; // Table pointer increment
inline void resetPitchFlag() { pitchValueAvailable = false; }
inline void resetVolFlag() { volumeValueAvailable = false; }

@ -1,151 +0,0 @@
/* Arduino WaveHC Library
* Copyright (C) 2009 by William Greiman
*
* This file is part of the Arduino WaveHC Library
*
* This Library is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This Library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the Arduino WaveHC Library. If not, see
* <http://www.gnu.org/licenses/>.
*/
/**
* Macros and inline functions for MCP4921 DAC
*/
#ifndef mcpDac_h
#define mcpDac_h
#include <avr/io.h>
#include "OTPinDefs.h"
//------------------------------------------------------------------------------
#define mcpDacCsLow() MCP_DAC_CS_PORT &= ~_BV(MCP_DAC_CS_BIT)
#define mcpDacCsHigh() MCP_DAC_CS_PORT |= _BV(MCP_DAC_CS_BIT)
#define mcpDac2CsLow() MCP_DAC_CS_PORT &= ~_BV(MCP_DAC2_CS_BIT)
#define mcpDac2CsHigh() MCP_DAC_CS_PORT |= _BV(MCP_DAC2_CS_BIT)
#define mcpDacSckLow() MCP_DAC_SCK_PORT &= ~_BV(MCP_DAC_SCK_BIT)
#define mcpDacSckHigh() MCP_DAC_SCK_PORT |= _BV(MCP_DAC_SCK_BIT)
#define mcpDacSckPulse() {mcpDacSckHigh();mcpDacSckLow();}
#define mcpDacSdiLow() MCP_DAC_SDI_PORT &= ~_BV(MCP_DAC_SDI_BIT)
#define mcpDacSdiHigh() MCP_DAC_SDI_PORT |= _BV(MCP_DAC_SDI_BIT)
#define mcpDacSdiSet(v) if(v){mcpDacSdiHigh();}else{mcpDacSdiLow();}
// send bit b of d
#define mcpDacSendBit(d, b) {mcpDacSdiSet(d&_BV(b));mcpDacSckPulse();}
//------------------------------------------------------------------------------
// init dac I/O ports
inline void mcpDacInit(void) {
// set all to output mode
MCP_DAC_CS_DDR |= _BV(MCP_DAC_CS_BIT);
MCP_DAC2_CS_DDR |= _BV(MCP_DAC2_CS_BIT);
MCP_DAC_SCK_DDR |= _BV(MCP_DAC_SCK_BIT);
MCP_DAC_SDI_DDR |= _BV(MCP_DAC_SDI_BIT);
// chip select high
mcpDacCsHigh();
mcpDac2CsHigh();
#if USE_MCP_DAC_LDAC
// LDAC low always - use unbuffered mode
MCP_DAC_LDAC_DDR |= _BV(MCP_DAC_LDAC_BIT);
MCP_DAC_LDAC_PORT &= ~_BV(MCP_DAC_LDAC_BIT);
#endif // USE_MCP_DAC_LDAC
}
//------------------------------------------------------------------------------
// send 12 bits to dac
// trusted compiler to optimize and it does
// csLow to csHigh takes 8 - 9 usec on a 16 MHz Arduino
inline void mcpDacSend(uint16_t data) {
mcpDacCsLow();
// send DAC config bits
mcpDacSdiLow();
mcpDacSckPulse(); // DAC A
mcpDacSdiHigh();
mcpDacSckPulse(); // buffered REF
mcpDacSckPulse(); // 1X gain
mcpDacSckPulse(); // no SHDN
// send 12 data bits
mcpDacSendBit(data, 11);
mcpDacSendBit(data, 10);
mcpDacSendBit(data, 9);
mcpDacSendBit(data, 8);
mcpDacSendBit(data, 7);
mcpDacSendBit(data, 6);
mcpDacSendBit(data, 5);
mcpDacSendBit(data, 4);
mcpDacSendBit(data, 3);
mcpDacSendBit(data, 2);
mcpDacSendBit(data, 1);
mcpDacSendBit(data, 0);
mcpDacCsHigh();
}
inline void mcpDac2ASend(uint16_t data) {
mcpDac2CsLow();
// send DAC config bits
mcpDacSdiLow();
mcpDacSckPulse(); // DAC A
mcpDacSdiHigh();
mcpDacSckPulse(); // buffered REF
mcpDacSckPulse(); // 1X gain
mcpDacSckPulse(); // no SHDN
// send 12 data bits
mcpDacSendBit(data, 11);
mcpDacSendBit(data, 10);
mcpDacSendBit(data, 9);
mcpDacSendBit(data, 8);
mcpDacSendBit(data, 7);
mcpDacSendBit(data, 6);
mcpDacSendBit(data, 5);
mcpDacSendBit(data, 4);
mcpDacSendBit(data, 3);
mcpDacSendBit(data, 2);
mcpDacSendBit(data, 1);
mcpDacSendBit(data, 0);
mcpDac2CsHigh();
}
inline void mcpDac2BSend(uint16_t data) {
mcpDac2CsLow();
// send DAC config bits
mcpDacSdiHigh();
mcpDacSckPulse(); // DAC A
mcpDacSdiHigh();
mcpDacSckPulse(); // buffered REF
mcpDacSckPulse(); // 1X gain
mcpDacSckPulse(); // no SHDN
// send 12 data bits
mcpDacSendBit(data, 11);
mcpDacSendBit(data, 10);
mcpDacSendBit(data, 9);
mcpDacSendBit(data, 8);
mcpDacSendBit(data, 7);
mcpDacSendBit(data, 6);
mcpDacSendBit(data, 5);
mcpDacSendBit(data, 4);
mcpDacSendBit(data, 3);
mcpDacSendBit(data, 2);
mcpDacSendBit(data, 1);
mcpDacSendBit(data, 0);
mcpDac2CsHigh();
}
#endif //mcpDac_h

@ -1,7 +1,15 @@
## Open.Theremin V3 control software
## Open.Theremin V3.1 control software
Arduino UNO Software for the Open.Theremin
### Changes since V3.0 (all by @Theremingenieur):
1. Fix a wavetable addressing issue (found by @miguelfreitas)
2. Use the Arduino's hardware SPI to control the DACS and use the Latch signal to reduce audio jitter
3. Improve the register switch to transpose by clean octaves and keep the tone spacing and pitch tuning consistent
4. Improve the volume response to give a smoother start and wider dynamics (*)
(*) This relies on a recent gcc compiler version. Make sure to compile it with the Arduino IDE >= 1.8.10
### Don't click on the files!
Click on the "Download ZIP" Button to the right or [Click here](https://github.com/GaudiLabs/OpenTheremin_V3/archive/master.zip)
Then unpack the archive.

@ -1,22 +0,0 @@
## Open.Theremin V3 control software
Arduino UNO Software for the Open.Theremin
### Don't click on the files!
Click on the "Download ZIP" Button to the right or [Click here](https://github.com/GaudiLabs/OpenTheremin_V3/archive/master.zip)
Then unpack the archive.
### Open Source Theremin based on the Arduino Platform
Open.Theremin is an arduino shield to build the legendary music instrument invented by Leon Theremin back in 1920. The theremin is played with two antennas, one to control the pitch and one for volume. The electronic shield with two ports to connect those antennas comprises two heterodyne oscillators to measure the distance of the hand to the antenna when playing the instrument. The resulting signal is fed into the arduino. After linearization and filtering the arduino generates the instruments sound that is then played through a high quality digital analog audio converter on the board. The characteristics of the sound can be determined by a wave table on the arduino.
For more info on the open source project and on availability of ready made shield see:
http://www.gaudi.ch/OpenTheremin/
### Installation
1. Open up the Arduino IDE
2. Open the File "Open_Theremin_V3.ino"
3. Selecting the correct usb port on Tools -> Serial Port
4. Select the correct arduino board from Tools -> Board
5. Upload the code by clicking on the upload button.
Loading…
Cancel
Save