From 5c91683ec87b057b93f6832f51385dcc902812cd Mon Sep 17 00:00:00 2001 From: Thierry Frenkel Date: Sun, 19 Apr 2020 16:53:12 +0200 Subject: [PATCH 01/10] Update ihandlers.cpp Fix wrong PROGMEM declaration, add optional debug code for timing checks --- Open_Theremin_V3/ihandlers.cpp | 64 +++++++++++++++++----------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/Open_Theremin_V3/ihandlers.cpp b/Open_Theremin_V3/ihandlers.cpp index 1cc16bc..8025fa8 100644 --- a/Open_Theremin_V3/ihandlers.cpp +++ b/Open_Theremin_V3/ihandlers.cpp @@ -3,7 +3,6 @@ #include "ihandlers.h" #include "mcpDac.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, @@ -31,6 +30,14 @@ static const uint32_t MCP_DAC_BASE = 2047; #define INT0_STATE (PIND & (1<>6) & 0x3ff; + int16_t waveSample; + uint32_t scaledSample; + uint16_t offset = (uint16_t)(pointer >> 6) & 0x3ff; #if CV_ENABLED // Generator for CV output @@ -136,26 +148,11 @@ ISR (INT1_vect) { #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); - } + // multiply 16 bit wave number by 8 bit volume value (11.2us / 5.4us) + scaledSample = MCP_DAC_BASE + (mulsu_16_8(waveSample, vScaledVolume) >> 8); mcpDacSend(scaledSample); //Send result to Digital to Analogue Converter (audio out) (9.6 us) @@ -190,6 +187,11 @@ 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 +218,3 @@ ISR(TIMER1_OVF_vect) { timer_overflow_counter++; } - - From d17ded142fc0c6143bc033ac82c1477579eaad65 Mon Sep 17 00:00:00 2001 From: Thierry Frenkel Date: Sun, 19 Apr 2020 22:15:56 +0200 Subject: [PATCH 02/10] Hardware SPI Use hardware SPI instead of bit-banging to save a few us and some flash ROM --- Open_Theremin_V3/OTPinDefs.h | 56 ------------ Open_Theremin_V3/SPImcpDAC.h | 102 +++++++++++++++++++++ Open_Theremin_V3/application.cpp | 36 ++++---- Open_Theremin_V3/ihandlers.cpp | 31 ++++--- Open_Theremin_V3/mcpDac.h | 151 ------------------------------- 5 files changed, 135 insertions(+), 241 deletions(-) delete mode 100644 Open_Theremin_V3/OTPinDefs.h create mode 100644 Open_Theremin_V3/SPImcpDAC.h delete mode 100644 Open_Theremin_V3/mcpDac.h diff --git a/Open_Theremin_V3/OTPinDefs.h b/Open_Theremin_V3/OTPinDefs.h deleted file mode 100644 index cdd9996..0000000 --- a/Open_Theremin_V3/OTPinDefs.h +++ /dev/null @@ -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 diff --git a/Open_Theremin_V3/SPImcpDAC.h b/Open_Theremin_V3/SPImcpDAC.h new file mode 100644 index 0000000..57ab867 --- /dev/null +++ b/Open_Theremin_V3/SPImcpDAC.h @@ -0,0 +1,102 @@ +/* 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 + +// 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 |= SPI2X; // double the SPI clock, ideally we get 8 MHz, so that a 16bit word goes out in 2us plus only a small overhead +} + +static inline void SPImcpDACtransmit(uint16_t data) +{ + // Send highbyte and wait for complete + SPDR = highByte(data); + while (!(SPSR && _BV(SPIF))) + ; + // Send lowbyte and wait for complete + SPDR = lowByte(data); + 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 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 diff --git a/Open_Theremin_V3/application.cpp b/Open_Theremin_V3/application.cpp index 80dc893..c0c3c3f 100644 --- a/Open_Theremin_V3/application.cpp +++ b/Open_Theremin_V3/application.cpp @@ -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(); @@ -327,7 +327,7 @@ static long pitchfn = 0; InitialisePitchMeasurement(); interrupts(); - mcpDacInit(); + SPImcpDACinit(); qMeasurement = GetQMeasurement(); // Measure Arudino clock frequency Serial.print("Arudino Freq: "); @@ -344,13 +344,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 +362,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 +411,7 @@ static long volumefn = 0; InitialiseVolumeMeasurement(); interrupts(); - mcpDacInit(); + SPImcpDACinit(); volumeXn0 = 0; @@ -424,12 +424,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 +443,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 +474,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) { @@ -512,7 +512,3 @@ void Application::delay_NOP(unsigned long time) { __asm__ __volatile__ ("nop"); } } - - - - diff --git a/Open_Theremin_V3/ihandlers.cpp b/Open_Theremin_V3/ihandlers.cpp index 8025fa8..4da4830 100644 --- a/Open_Theremin_V3/ihandlers.cpp +++ b/Open_Theremin_V3/ihandlers.cpp @@ -1,8 +1,9 @@ #include "Arduino.h" #include "ihandlers.h" -#include "mcpDac.h" +#include "SPImcpDAC.h" #include "timer.h" +#include "hw.h" #include "build.h" #include "theremin_sintable.c" @@ -31,7 +32,7 @@ static const uint32_t MCP_DAC_BASE = 2047; #define PC_STATE (PINB & (1<> 8); - mcpDacSend(scaledSample); //Send result to Digital to Analogue Converter (audio out) (9.6 us) - pointer = pointer + vPointerIncrement; // increment table pointer (ca. 2us) + SPImcpDACsend(scaledSample); //Send result to Digital to Analogue Converter (audio out) (6 us) + + + pointer = pointer + vPointerIncrement; // increment table pointer (ca. 2us) #endif //CV play sound incrementTimer(); // update 32us timer @@ -187,10 +192,8 @@ ISR (INT1_vect) { noInterrupts(); enableInt1(); - - // Added by ThF 20200419 - #ifdef TH_DEBUG - HW_LED2_OFF; +#ifdef TH_DEBUG + HW_LED2_OFF; #endif } diff --git a/Open_Theremin_V3/mcpDac.h b/Open_Theremin_V3/mcpDac.h deleted file mode 100644 index ddbb1ce..0000000 --- a/Open_Theremin_V3/mcpDac.h +++ /dev/null @@ -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 - * . - */ -/** - * Macros and inline functions for MCP4921 DAC - */ -#ifndef mcpDac_h -#define mcpDac_h - -#include -#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 From b59e92da9d7bb39f0e24ca402313d13ed68f93ab Mon Sep 17 00:00:00 2001 From: Thierry Frenkel Date: Sun, 19 Apr 2020 22:18:21 +0200 Subject: [PATCH 03/10] Revert "Hardware SPI" This reverts commit d17ded142fc0c6143bc033ac82c1477579eaad65. --- Open_Theremin_V3/OTPinDefs.h | 56 ++++++++++++ Open_Theremin_V3/SPImcpDAC.h | 102 --------------------- Open_Theremin_V3/application.cpp | 36 ++++---- Open_Theremin_V3/ihandlers.cpp | 31 +++---- Open_Theremin_V3/mcpDac.h | 151 +++++++++++++++++++++++++++++++ 5 files changed, 241 insertions(+), 135 deletions(-) create mode 100644 Open_Theremin_V3/OTPinDefs.h delete mode 100644 Open_Theremin_V3/SPImcpDAC.h create mode 100644 Open_Theremin_V3/mcpDac.h diff --git a/Open_Theremin_V3/OTPinDefs.h b/Open_Theremin_V3/OTPinDefs.h new file mode 100644 index 0000000..cdd9996 --- /dev/null +++ b/Open_Theremin_V3/OTPinDefs.h @@ -0,0 +1,56 @@ +/** + * \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 diff --git a/Open_Theremin_V3/SPImcpDAC.h b/Open_Theremin_V3/SPImcpDAC.h deleted file mode 100644 index 57ab867..0000000 --- a/Open_Theremin_V3/SPImcpDAC.h +++ /dev/null @@ -1,102 +0,0 @@ -/* 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 - -// 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 |= SPI2X; // double the SPI clock, ideally we get 8 MHz, so that a 16bit word goes out in 2us plus only a small overhead -} - -static inline void SPImcpDACtransmit(uint16_t data) -{ - // Send highbyte and wait for complete - SPDR = highByte(data); - while (!(SPSR && _BV(SPIF))) - ; - // Send lowbyte and wait for complete - SPDR = lowByte(data); - 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 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 diff --git a/Open_Theremin_V3/application.cpp b/Open_Theremin_V3/application.cpp index c0c3c3f..80dc893 100644 --- a/Open_Theremin_V3/application.cpp +++ b/Open_Theremin_V3/application.cpp @@ -3,7 +3,7 @@ #include "application.h" #include "hw.h" -#include "SPImcpDAC.h" +#include "mcpDac.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 - SPImcpDACinit(); + mcpDacInit(); EEPROM.get(0,pitchDAC); EEPROM.get(2,volumeDAC); -SPImcpDAC2Asend(pitchDAC); -SPImcpDAC2Bsend(volumeDAC); +mcpDac2ASend(pitchDAC); +mcpDac2BSend(volumeDAC); initialiseTimer(); @@ -327,7 +327,7 @@ static long pitchfn = 0; InitialisePitchMeasurement(); interrupts(); - SPImcpDACinit(); + mcpDacInit(); qMeasurement = GetQMeasurement(); // Measure Arudino clock frequency Serial.print("Arudino Freq: "); @@ -344,13 +344,13 @@ Serial.print("\nPitch Set Frequency: "); Serial.println(pitchfn); -SPImcpDAC2Bsend(1600); +mcpDac2BSend(1600); -SPImcpDAC2Asend(pitchXn0); +mcpDac2ASend(pitchXn0); delay(100); pitchfn0 = GetPitchMeasurement(); -SPImcpDAC2Asend(pitchXn1); +mcpDac2ASend(pitchXn1); delay(100); pitchfn1 = GetPitchMeasurement(); @@ -362,11 +362,11 @@ Serial.println(pitchfn1); while(abs(pitchfn0-pitchfn1)>CalibrationTolerance){ // max allowed pitch frequency offset -SPImcpDAC2Asend(pitchXn0); +mcpDac2ASend(pitchXn0); delay(100); pitchfn0 = GetPitchMeasurement()-pitchfn; -SPImcpDAC2Asend(pitchXn1); +mcpDac2ASend(pitchXn1); delay(100); pitchfn1 = GetPitchMeasurement()-pitchfn; @@ -411,7 +411,7 @@ static long volumefn = 0; InitialiseVolumeMeasurement(); interrupts(); - SPImcpDACinit(); + mcpDacInit(); volumeXn0 = 0; @@ -424,12 +424,12 @@ Serial.print("\nVolume Set Frequency: "); Serial.println(volumefn); -SPImcpDAC2Bsend(volumeXn0); +mcpDac2BSend(volumeXn0); delay_NOP(44316);//44316=100ms volumefn0 = GetVolumeMeasurement(); -SPImcpDAC2Bsend(volumeXn1); +mcpDac2BSend(volumeXn1); delay_NOP(44316);//44316=100ms volumefn1 = GetVolumeMeasurement(); @@ -443,11 +443,11 @@ Serial.println(volumefn1); while(abs(volumefn0-volumefn1)>CalibrationTolerance){ -SPImcpDAC2Bsend(volumeXn0); +mcpDac2BSend(volumeXn0); delay_NOP(44316);//44316=100ms volumefn0 = GetVolumeMeasurement()-volumefn; -SPImcpDAC2Bsend(volumeXn1); +mcpDac2BSend(volumeXn1); delay_NOP(44316);//44316=100ms volumefn1 = GetVolumeMeasurement()-volumefn; @@ -474,7 +474,7 @@ EEPROM.put(2,volumeXn0); HW_LED2_OFF; HW_LED1_ON; - Serial.println("\nCALIBRATION COMPLETED\n"); + Serial.println("\nCALIBRATION COMPTLETED\n"); } void Application::hzToAddVal(float hz) { @@ -512,3 +512,7 @@ void Application::delay_NOP(unsigned long time) { __asm__ __volatile__ ("nop"); } } + + + + diff --git a/Open_Theremin_V3/ihandlers.cpp b/Open_Theremin_V3/ihandlers.cpp index 4da4830..8025fa8 100644 --- a/Open_Theremin_V3/ihandlers.cpp +++ b/Open_Theremin_V3/ihandlers.cpp @@ -1,9 +1,8 @@ #include "Arduino.h" #include "ihandlers.h" -#include "SPImcpDAC.h" +#include "mcpDac.h" #include "timer.h" -#include "hw.h" #include "build.h" #include "theremin_sintable.c" @@ -32,7 +31,7 @@ static const uint32_t MCP_DAC_BASE = 2047; #define PC_STATE (PINB & (1<> 8); + mcpDacSend(scaledSample); //Send result to Digital to Analogue Converter (audio out) (9.6 us) - SPImcpDACsend(scaledSample); //Send result to Digital to Analogue Converter (audio out) (6 us) - - - pointer = pointer + vPointerIncrement; // increment table pointer (ca. 2us) + pointer = pointer + vPointerIncrement; // increment table pointer (ca. 2us) #endif //CV play sound incrementTimer(); // update 32us timer @@ -192,8 +187,10 @@ ISR (INT1_vect) { noInterrupts(); enableInt1(); -#ifdef TH_DEBUG - HW_LED2_OFF; + + // Added by ThF 20200419 + #ifdef TH_DEBUG + HW_LED2_OFF; #endif } diff --git a/Open_Theremin_V3/mcpDac.h b/Open_Theremin_V3/mcpDac.h new file mode 100644 index 0000000..ddbb1ce --- /dev/null +++ b/Open_Theremin_V3/mcpDac.h @@ -0,0 +1,151 @@ +/* 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 + * . + */ +/** + * Macros and inline functions for MCP4921 DAC + */ +#ifndef mcpDac_h +#define mcpDac_h + +#include +#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 From 341ebadeb97d7f0af86fd4a779c9039d920398eb Mon Sep 17 00:00:00 2001 From: Thierry Frenkel Date: Sun, 19 Apr 2020 22:24:27 +0200 Subject: [PATCH 04/10] Use hardware SPI and save a few us and flash ROM bytes --- Open_Theremin_V3/OTPinDefs.h | 56 ------------ Open_Theremin_V3/SPImcpDAC.h | 102 +++++++++++++++++++++ Open_Theremin_V3/application.cpp | 34 +++---- Open_Theremin_V3/ihandlers.cpp | 33 ++++--- Open_Theremin_V3/mcpDac.h | 151 ------------------------------- 5 files changed, 138 insertions(+), 238 deletions(-) delete mode 100644 Open_Theremin_V3/OTPinDefs.h create mode 100644 Open_Theremin_V3/SPImcpDAC.h delete mode 100644 Open_Theremin_V3/mcpDac.h diff --git a/Open_Theremin_V3/OTPinDefs.h b/Open_Theremin_V3/OTPinDefs.h deleted file mode 100644 index cdd9996..0000000 --- a/Open_Theremin_V3/OTPinDefs.h +++ /dev/null @@ -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 diff --git a/Open_Theremin_V3/SPImcpDAC.h b/Open_Theremin_V3/SPImcpDAC.h new file mode 100644 index 0000000..9ead1f5 --- /dev/null +++ b/Open_Theremin_V3/SPImcpDAC.h @@ -0,0 +1,102 @@ +/* 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 + +// 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 |= SPI2X; // double the SPI clock, ideally we get 8 MHz, so that a 16bit word goes out in 2us plus only a small overhead +} + +static inline void SPImcpDACtransmit(uint16_t data) +{ + // Send highbyte and wait for complete + SPDR = highByte(data); + while (!(SPSR && _BV(SPIF))) + ; + // Send lowbyte and wait for complete + SPDR = lowByte(data); + 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 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 \ No newline at end of file diff --git a/Open_Theremin_V3/application.cpp b/Open_Theremin_V3/application.cpp index 80dc893..bc7e609 100644 --- a/Open_Theremin_V3/application.cpp +++ b/Open_Theremin_V3/application.cpp @@ -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(); @@ -327,7 +327,7 @@ static long pitchfn = 0; InitialisePitchMeasurement(); interrupts(); - mcpDacInit(); + SPImcpDACinit(); qMeasurement = GetQMeasurement(); // Measure Arudino clock frequency Serial.print("Arudino Freq: "); @@ -344,13 +344,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 +362,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 +411,7 @@ static long volumefn = 0; InitialiseVolumeMeasurement(); interrupts(); - mcpDacInit(); + SPImcpDACinit(); volumeXn0 = 0; @@ -424,12 +424,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 +443,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 +474,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) { @@ -515,4 +515,4 @@ void Application::delay_NOP(unsigned long time) { - + diff --git a/Open_Theremin_V3/ihandlers.cpp b/Open_Theremin_V3/ihandlers.cpp index 8025fa8..2b9c51f 100644 --- a/Open_Theremin_V3/ihandlers.cpp +++ b/Open_Theremin_V3/ihandlers.cpp @@ -1,7 +1,7 @@ #include "Arduino.h" #include "ihandlers.h" -#include "mcpDac.h" +#include "SPImcpDAC.h" #include "timer.h" #include "build.h" @@ -126,12 +126,10 @@ static inline uint32_t mulsu_16_8(uint16_t a, uint8_t b) /* Externaly generated 31250 Hz Interrupt for WAVE generator (32us) */ ISR (INT1_vect) { - // Interrupt takes up a total of max 25 us - - // Added by ThF 20200419 - #ifdef TH_DEBUG - HW_LED2_ON; - #endif + // Interrupt takes up a total of 14us plus overhead when interrupted itself. + + // Latch previously written DAC value: + SPImcpDAClatch(); disableInt1(); // Disable External Interrupt INT1 to avoid recursive interrupts // Enable Interrupts to allow counter 1 interrupts @@ -154,9 +152,19 @@ ISR (INT1_vect) { // multiply 16 bit wave number by 8 bit volume value (11.2us / 5.4us) scaledSample = MCP_DAC_BASE + (mulsu_16_8(waveSample, vScaledVolume) >> 8); - mcpDacSend(scaledSample); //Send result to Digital to Analogue Converter (audio out) (9.6 us) +// Added by ThF 20200419 +#ifdef TH_DEBUG + HW_LED2_ON; +#endif + + SPImcpDACsend(scaledSample); //Send result to Digital to Analogue Converter (audio out) (6 us) + +// Added by ThF 20200419 +#ifdef TH_DEBUG + HW_LED2_OFF; +#endif - pointer = pointer + vPointerIncrement; // increment table pointer (ca. 2us) + pointer = pointer + vPointerIncrement; // increment table pointer (ca. 2us) #endif //CV play sound incrementTimer(); // update 32us timer @@ -187,11 +195,6 @@ 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 */ @@ -218,3 +221,5 @@ ISR(TIMER1_OVF_vect) { timer_overflow_counter++; } + + diff --git a/Open_Theremin_V3/mcpDac.h b/Open_Theremin_V3/mcpDac.h deleted file mode 100644 index ddbb1ce..0000000 --- a/Open_Theremin_V3/mcpDac.h +++ /dev/null @@ -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 - * . - */ -/** - * Macros and inline functions for MCP4921 DAC - */ -#ifndef mcpDac_h -#define mcpDac_h - -#include -#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 From b7a56bd1f7e0ab74a80618e93fd4a4f030b9d9e4 Mon Sep 17 00:00:00 2001 From: Thierry Frenkel Date: Sun, 19 Apr 2020 23:27:25 +0200 Subject: [PATCH 05/10] Improve the register switch Allow it to transpose either -1, +/-0, or +1 octave without affecting the tone spacing and clean, independent of the pitch pot setting --- Open_Theremin_V3/application.cpp | 34 ++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/Open_Theremin_V3/application.cpp b/Open_Theremin_V3/application.cpp index bc7e609..a547b16 100644 --- a/Open_Theremin_V3/application.cpp +++ b/Open_Theremin_V3/application.cpp @@ -82,7 +82,7 @@ unsigned long Application::GetQMeasurement() { int qn=0; - TCCR1B = (1<= 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 +263,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; @@ -512,7 +524,3 @@ void Application::delay_NOP(unsigned long time) { __asm__ __volatile__ ("nop"); } } - - - - From b559848598dc58a56929140a8318af42a6b678de Mon Sep 17 00:00:00 2001 From: Thierry Frenkel Date: Mon, 20 Apr 2020 01:20:34 +0200 Subject: [PATCH 06/10] Improve the volume response Give it a pseudo- exponential characteristic and extend the volume control values to 16bit which gives more dynamic --- Open_Theremin_V3/application.cpp | 32 +++++++++-------- Open_Theremin_V3/ihandlers.cpp | 59 +++++++++----------------------- Open_Theremin_V3/ihandlers.h | 4 +-- 3 files changed, 36 insertions(+), 59 deletions(-) diff --git a/Open_Theremin_V3/application.cpp b/Open_Theremin_V3/application.cpp index a547b16..9689d19 100644 --- a/Open_Theremin_V3/application.cpp +++ b/Open_Theremin_V3/application.cpp @@ -82,7 +82,7 @@ unsigned long Application::GetQMeasurement() { int qn=0; - TCCR1B = (1< 681) + if (registerPotValue > 681) { - registerValue = 1; + registerValue = 1; } else if(registerPotValue < 342) { - registerValue = 3; + registerValue = 3; } else { - registerValue = 2; + registerValue = 2; } if (_state == PLAYING && HW_BUTTON_PRESSED) { @@ -288,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 @@ -494,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; diff --git a/Open_Theremin_V3/ihandlers.cpp b/Open_Theremin_V3/ihandlers.cpp index 2b9c51f..9b29087 100644 --- a/Open_Theremin_V3/ihandlers.cpp +++ b/Open_Theremin_V3/ihandlers.cpp @@ -25,7 +25,7 @@ const int16_t* const wavetables[] = { //Fixed following a suggestion by Michael sine_table8 }; -static const uint32_t MCP_DAC_BASE = 2047; +static const uint32_t MCP_DAC_BASE = 2048; #define INT0_STATE (PIND & (1<> 6) & 0x3ff; #if CV_ENABLED // Generator for CV output @@ -146,25 +130,14 @@ ISR (INT1_vect) { #else //Play sound - // Read next wave table value - waveSample = (int16_t)pgm_read_word_near(wavetables[vWavetableSelector] + offset); + // Read next wave table value + waveSample = (int16_t)pgm_read_word_near(wavetables[vWavetableSelector] + offset); - // multiply 16 bit wave number by 8 bit volume value (11.2us / 5.4us) - scaledSample = MCP_DAC_BASE + (mulsu_16_8(waveSample, vScaledVolume) >> 8); + scaledSample = ((int32_t)waveSample * (uint32_t)vScaledVolume) >> 16; -// Added by ThF 20200419 -#ifdef TH_DEBUG - HW_LED2_ON; -#endif + SPImcpDACsend(scaledSample + MCP_DAC_BASE); //Send result to Digital to Analogue Converter (audio out) (6 us) - SPImcpDACsend(scaledSample); //Send result to Digital to Analogue Converter (audio out) (6 us) - -// Added by ThF 20200419 -#ifdef TH_DEBUG - HW_LED2_OFF; -#endif - - pointer = pointer + vPointerIncrement; // increment table pointer (ca. 2us) + pointer += vPointerIncrement; // increment table pointer (ca. 2us) #endif //CV play sound incrementTimer(); // update 32us timer @@ -195,6 +168,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 */ @@ -221,5 +198,3 @@ ISR(TIMER1_OVF_vect) { timer_overflow_counter++; } - - diff --git a/Open_Theremin_V3/ihandlers.h b/Open_Theremin_V3/ihandlers.h index d6de5d0..9e65988 100644 --- a/Open_Theremin_V3/ihandlers.h +++ b/Open_Theremin_V3/ihandlers.h @@ -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; } From 2eca6fd84881fc3cb940ebfcf420301f8dd19863 Mon Sep 17 00:00:00 2001 From: Thierry <25463159+Theremingenieur@users.noreply.github.com> Date: Mon, 20 Apr 2020 01:36:29 +0200 Subject: [PATCH 07/10] Update README.md --- README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7914b33..6fd4cca 100644 --- a/README.md +++ b/README.md @@ -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 @michaelfreitas) +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. From 07e09fcfaf98ec702528261a9d5b5feb92b88b18 Mon Sep 17 00:00:00 2001 From: Thierry Frenkel Date: Mon, 20 Apr 2020 14:38:26 +0200 Subject: [PATCH 08/10] Make h/w SPI still better and some cosmetic fixes --- Open_Theremin_V3/Open_Theremin_V3.ino | 6 +++--- Open_Theremin_V3/SPImcpDAC.h | 18 ++++++++++-------- Open_Theremin_V3/ihandlers.cpp | 23 ++++++++++++----------- 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/Open_Theremin_V3/Open_Theremin_V3.ino b/Open_Theremin_V3/Open_Theremin_V3.ino index 94bedb5..49255a4 100644 --- a/Open_Theremin_V3/Open_Theremin_V3.ino +++ b/Open_Theremin_V3/Open_Theremin_V3.ino @@ -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(); } - diff --git a/Open_Theremin_V3/SPImcpDAC.h b/Open_Theremin_V3/SPImcpDAC.h index 9ead1f5..e87f9e8 100644 --- a/Open_Theremin_V3/SPImcpDAC.h +++ b/Open_Theremin_V3/SPImcpDAC.h @@ -45,18 +45,20 @@ static inline void SPImcpDACinit() 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 |= SPI2X; // double the SPI clock, ideally we get 8 MHz, so that a 16bit word goes out in 2us plus only a small overhead + 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); - while (!(SPSR && _BV(SPIF))) + asm("nop"); + while (!(SPSR & _BV(SPIF))) ; // Send lowbyte and wait for complete SPDR = lowByte(data); - while (!(SPSR && _BV(SPIF))) + asm("nop"); + while (!(SPSR & _BV(SPIF))) ; } @@ -69,12 +71,12 @@ static inline void SPImcpDAClatch() 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; + // 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 beginning of the next interrupt to get consistent timing + // 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) @@ -99,4 +101,4 @@ static inline void SPImcpDAC2Bsend(uint16_t data) SPImcpDAClatch(); } -#endif \ No newline at end of file +#endif diff --git a/Open_Theremin_V3/ihandlers.cpp b/Open_Theremin_V3/ihandlers.cpp index 9b29087..894e037 100644 --- a/Open_Theremin_V3/ihandlers.cpp +++ b/Open_Theremin_V3/ihandlers.cpp @@ -106,14 +106,15 @@ void ihInitialiseVolumeMeasurement() //Measurement of variable frequency oscilla /* Externaly generated 31250 Hz Interrupt for WAVE generator (32us) */ ISR (INT1_vect) { - // Interrupt takes up a total of 16us plus overhead when interrupted itself. -// Added by ThF 20200419 -#ifdef TH_DEBUG - HW_LED2_ON; -#endif + // Interrupt takes up normally 14us but can take up to 22us when interrupted by another interrupt. + + // Added by ThF 20200419 + #ifdef TH_DEBUG + HW_LED2_ON; + #endif - // Latch previously written DAC value: - SPImcpDAClatch(); + // Latch previously written DAC value: + SPImcpDAClatch(); disableInt1(); // Disable External Interrupt INT1 to avoid recursive interrupts // Enable Interrupts to allow counter 1 interrupts @@ -126,18 +127,18 @@ ISR (INT1_vect) { #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 waveSample = (int16_t)pgm_read_word_near(wavetables[vWavetableSelector] + offset); - scaledSample = ((int32_t)waveSample * (uint32_t)vScaledVolume) >> 16; + scaledSample = ((int32_t)waveSample * (uint32_t)vScaledVolume) >> 16; // The compiler optimizes this better than any assembly written by hand !!! - SPImcpDACsend(scaledSample + MCP_DAC_BASE); //Send result to Digital to Analogue Converter (audio out) (6 us) + SPImcpDACsend(scaledSample + MCP_DAC_BASE); //Send result to Digital to Analogue Converter (audio out) (5.5 us) - pointer += vPointerIncrement; // increment table pointer (ca. 2us) + pointer += vPointerIncrement; // increment table pointer #endif //CV play sound incrementTimer(); // update 32us timer From 7c6735332d6f07c4d5ede73855cec2137b14d61d Mon Sep 17 00:00:00 2001 From: Thierry <25463159+Theremingenieur@users.noreply.github.com> Date: Tue, 21 Apr 2020 09:49:43 +0200 Subject: [PATCH 09/10] Remove old backup file --- README.md~ | 22 ---------------------- 1 file changed, 22 deletions(-) delete mode 100644 README.md~ diff --git a/README.md~ b/README.md~ deleted file mode 100644 index 128f3a7..0000000 --- a/README.md~ +++ /dev/null @@ -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. From 6a69fc7ee2d8f723c728b01b58e204cbbf9cf91e Mon Sep 17 00:00:00 2001 From: Thierry <25463159+Theremingenieur@users.noreply.github.com> Date: Tue, 21 Apr 2020 09:53:27 +0200 Subject: [PATCH 10/10] Update README.md Fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6fd4cca..05efafa 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Arduino UNO Software for the Open.Theremin ### Changes since V3.0 (all by @Theremingenieur): -1. Fix a wavetable addressing issue (found by @michaelfreitas) +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 (*)