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