Add files via upload

pull/19/head
MrDham 5 years ago committed by GitHub
parent ad0c37b16e
commit 250bd4dc21
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      Open_Theremin_V3/Open_Theremin_V3.ino
  2. 104
      Open_Theremin_V3/SPImcpDAC.h
  3. 1
      Open_Theremin_V3/application.cpp
  4. 63
      Open_Theremin_V3/ihandlers.cpp
  5. 2
      Open_Theremin_V3/ihandlers.h

@ -2,6 +2,8 @@
* Open Theremin V3 with MIDI interface control software for Arduino UNO * Open Theremin V3 with MIDI interface control software for Arduino UNO
* Based on Open Theremin V3 version 3.0 Copyright (C) 2010-2016 by Urs Gaudenz * Based on Open Theremin V3 version 3.0 Copyright (C) 2010-2016 by Urs Gaudenz
* *
* Also integrate changes from Open Theremin V3 Version 3.1 Copyright (C) 2010-2020 by Urs Gaudenz
*
* *
* Open Theremin V3 with MIDI interface control software is free software: * Open Theremin V3 with MIDI interface control software is free software:
* you can redistribute it and/or modify it under the terms of * you can redistribute it and/or modify it under the terms of
@ -19,9 +21,10 @@
* the Open Theremin V3 with MIDI interface control software. * the Open Theremin V3 with MIDI interface control software.
* If not, see <http://www.gnu.org/licenses/>. * If not, see <http://www.gnu.org/licenses/>.
* *
* Urs Gaudenz also credits for their important contributions to Open Theremin V3: * Also credited for their important contributions to Open Theremin V3:
* David Harvey * David Harvey
* Michael Margolis * Michael Margolis
* "Theremingenieur" Thierry Frenkel
*/ */
/* Midi added by Vincent Dhamelincourt - September 2017. /* Midi added by Vincent Dhamelincourt - September 2017.

@ -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

@ -196,7 +196,6 @@ AppMode Application::nextMode() {
void Application::loop() { void Application::loop() {
int32_t pitch_v = 0, pitch_l = 0; // Last value of pitch (for filtering) int32_t pitch_v = 0, pitch_l = 0; // Last value of pitch (for filtering)
int32_t vol_v = 0, vol_l = 0; // Last value of volume (for filtering) int32_t vol_v = 0, vol_l = 0; // Last value of volume (for filtering)
uint16_t tmpVolume;
uint16_t volumePotValue = 0; uint16_t volumePotValue = 0;
uint16_t pitchPotValue = 0; uint16_t pitchPotValue = 0;

@ -1,7 +1,7 @@
#include "Arduino.h" #include "Arduino.h"
#include "ihandlers.h" #include "ihandlers.h"
#include "mcpDac.h" #include "SPImcpDAC.h"
#include "timer.h" #include "timer.h"
#include "build.h" #include "build.h"
@ -15,7 +15,7 @@
#include "theremin_sintable7.c" #include "theremin_sintable7.c"
#include "theremin_sintable8.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_table,
sine_table2, sine_table2,
sine_table3, sine_table3,
@ -26,12 +26,12 @@ const int16_t* const wavetables[] PROGMEM = {
sine_table8 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 INT0_STATE (PIND & (1<<PORTD2))
#define PC_STATE (PINB & (1<<PORTB0)) #define PC_STATE (PINB & (1<<PORTB0))
volatile uint8_t vScaledVolume = 0; volatile uint16_t vScaledVolume = 0;
volatile uint16_t vPointerIncrement = 0; volatile uint16_t vPointerIncrement = 0;
volatile uint16_t pitch = 0; // Pitch value volatile uint16_t pitch = 0; // Pitch value
@ -97,69 +97,38 @@ 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) */ /* Externaly generated 31250 Hz Interrupt for WAVE generator (32us) */
ISR (INT1_vect) { 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.
// Latch previously written DAC value:
SPImcpDAClatch();
disableInt1(); // Disable External Interrupt INT1 to avoid recursive interrupts disableInt1(); // Disable External Interrupt INT1 to avoid recursive interrupts
// Enable Interrupts to allow counter 1 interrupts // Enable Interrupts to allow counter 1 interrupts
interrupts(); interrupts();
int16_t waveSample; int16_t waveSample;
uint32_t scaledSample; uint32_t scaledSample=0;
uint16_t offset = (uint16_t)(pointer>>6) & 0x3ff; uint16_t offset = (uint16_t)(pointer>>6) & 0x3ff;
#if CV_ENABLED // Generator for CV output #if CV_ENABLED // Generator for CV output
vPointerIncrement = min(vPointerIncrement, 4095); vPointerIncrement = min(vPointerIncrement, 4095);
mcpDacSend(vPointerIncrement); //Send result to Digital to Analogue Converter (audio out) (9.6 us) SPImcpDACsend(vPointerIncrement); //Send result to Digital to Analogue Converter (audio out) (5.5 us)
#else //Play sound #else //Play sound
// Read next wave table value (3.0us) // Read next wave table value
// The slightly odd tactic here is to provide compile-time expressions for the wavetable waveSample = (int16_t)pgm_read_word_near(wavetables[vWavetableSelector] + offset);
// positions. Making addr1 the index into the wavtables array breaks the time limit for
// the interrupt handler scaledSample = ((int32_t)waveSample * (uint32_t)vScaledVolume) >> 16; // The compiler optimizes this better than any assembly written by hand !!!
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;
};
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);
}
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 #endif //CV play sound
incrementTimer(); // update 32us timer incrementTimer(); // update 32us timer

@ -3,7 +3,7 @@
extern volatile uint16_t pitch; // Pitch value extern volatile uint16_t pitch; // Pitch value
extern volatile uint16_t vol; // Volume 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; // Pitch counter
extern volatile uint16_t pitch_counter_l; // Last value of pitch counter extern volatile uint16_t pitch_counter_l; // Last value of pitch counter

Loading…
Cancel
Save