Add files via upload

pull/19/head
MrDham 4 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. 4
      Open_Theremin_V3/ihandlers.h

@ -1,6 +1,8 @@
/*
* 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
*
* 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:
@ -19,9 +21,10 @@
* the Open Theremin V3 with MIDI interface control software.
* 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
* Michael Margolis
* "Theremingenieur" Thierry Frenkel
*/
/* 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() {
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)
uint16_t tmpVolume;
uint16_t volumePotValue = 0;
uint16_t pitchPotValue = 0;

@ -1,7 +1,7 @@
#include "Arduino.h"
#include "ihandlers.h"
#include "mcpDac.h"
#include "SPImcpDAC.h"
#include "timer.h"
#include "build.h"
@ -15,7 +15,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 +26,12 @@ 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;
volatile uint16_t vScaledVolume = 0;
volatile uint16_t vPointerIncrement = 0;
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) */
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
// Enable Interrupts to allow counter 1 interrupts
interrupts();
int16_t waveSample;
uint32_t scaledSample;
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)
SPImcpDACsend(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);
scaledSample = ((int32_t)waveSample * (uint32_t)vScaledVolume) >> 16; // The compiler optimizes this better than any assembly written by hand !!!
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
incrementTimer(); // update 32us timer

@ -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
@ -41,4 +41,4 @@ void resetVolFlag();
void savePitchCounter();
void saveVolCounter();
#endif // _IHANDLERS_H
#endif // _IHANDLERS_H

Loading…
Cancel
Save