You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
220 lines
7.2 KiB
220 lines
7.2 KiB
#include "Arduino.h"
|
|
|
|
#include "ihandlers.h"
|
|
#include "mcpDac.h"
|
|
#include "timer.h"
|
|
|
|
#include "build.h"
|
|
|
|
#include "theremin_sintable.c"
|
|
#include "theremin_sintable2.c"
|
|
#include "theremin_sintable3.c"
|
|
#include "theremin_sintable4.c"
|
|
#include "theremin_sintable5.c"
|
|
#include "theremin_sintable6.c"
|
|
#include "theremin_sintable7.c"
|
|
#include "theremin_sintable8.c"
|
|
|
|
const int16_t* const wavetables[] PROGMEM = {
|
|
sine_table,
|
|
sine_table2,
|
|
sine_table3,
|
|
sine_table4,
|
|
sine_table5,
|
|
sine_table6,
|
|
sine_table7,
|
|
sine_table8
|
|
};
|
|
|
|
static const uint32_t MCP_DAC_BASE = 2047;
|
|
|
|
#define INT0_STATE (PIND & (1<<PORTD2))
|
|
#define PC_STATE (PINB & (1<<PORTB0))
|
|
|
|
volatile uint8_t vScaledVolume = 0;
|
|
volatile uint16_t vPointerIncrement = 0;
|
|
|
|
volatile uint16_t pitch = 0; // Pitch value
|
|
volatile uint16_t pitch_counter = 0; // Pitch counter
|
|
volatile uint16_t pitch_counter_l = 0; // Last value of pitch counter
|
|
|
|
volatile bool volumeValueAvailable = 0; // Volume read flag
|
|
volatile bool pitchValueAvailable = 0; // Pitch read flag
|
|
volatile bool reenableInt1 = 0; // reeanble Int1
|
|
|
|
volatile uint16_t vol; // Volume value
|
|
volatile uint16_t vol_counter = 0;
|
|
volatile uint16_t vol_counter_i = 0; // Volume counter
|
|
volatile uint16_t vol_counter_l; // Last value of volume counter
|
|
|
|
volatile uint16_t timer_overflow_counter; // counter for frequency measurement
|
|
|
|
volatile uint8_t vWavetableSelector = 0; // wavetable selector
|
|
|
|
static volatile uint16_t pointer = 0; // Table pointer
|
|
static volatile uint8_t debounce_p, debounce_v = 0; // Counters for debouncing
|
|
|
|
void ihInitialiseTimer() {
|
|
/* Setup Timer 1, 16 bit timer used to measure pitch and volume frequency */
|
|
TCCR1A = 0; // Set Timer 1 to Normal port operation (Arduino does activate something here ?)
|
|
TCCR1B = (1<<ICES1)|(1<<CS10); // Input Capture Positive edge select, Run without prescaling (16 Mhz)
|
|
TIMSK1 = (1<<ICIE1); // Enable Input Capture Interrupt
|
|
|
|
TCCR0A = 3; //Arduino Default: Fast PWM
|
|
TCCR0B = 3; //Arduino Default: clk I/O /64 (From prescaler)
|
|
TIMSK0 = 1; //Arduino Default: TOIE0: Timer/Counter0 Overflow Interrupt Enable
|
|
|
|
}
|
|
|
|
void ihInitialiseInterrupts() {
|
|
/* Setup interrupts for Wave Generator and Volume read */
|
|
EICRA = (1<<ISC00)|(1<<ISC01)|(1<<ISC11)|(1<<ISC10) ; // The rising edges of INT0 and INT1 generate an interrupt request.
|
|
reenableInt1 = true;
|
|
EIMSK = (1<<INT0)|(1<<INT1); // Enable External Interrupt INT0 and INT1
|
|
}
|
|
|
|
void ihInitialisePitchMeasurement() //Measurement of variable frequency oscillator on Timer 1
|
|
{ reenableInt1 = false;
|
|
EIMSK = 0; // Disable External Interrupts
|
|
TCCR1A = 0; //Normal port operation Timer 1
|
|
TIMSK1 = (1<<TOIE1); //Timer/Counter1, Overflow Interrupt Enable
|
|
|
|
}
|
|
|
|
void ihInitialiseVolumeMeasurement() //Measurement of variable frequency oscillator on Timer 0
|
|
{ reenableInt1 = false;
|
|
EIMSK = 0; // Disable External Interrupts
|
|
TIMSK1 = 0; //Timer/Counter1, Overflow Interrupt Disable
|
|
|
|
TCCR0A = 0; // Normal port operation, OC0A disconnected. Timer 0
|
|
TIMSK0 = (1<<OCIE0A); //TOIE0: Timer/Counter0 Overflow Interrupt Enable
|
|
OCR0A = 0xff; // set Output Compare Register0.
|
|
|
|
TCCR1A = 0; //Normal port operation Timer 1
|
|
TCCR1B = (1<<CS10)|(1<<CS12); // clk I/O /1024 (From prescaler)
|
|
TCCR1C=0;
|
|
|
|
|
|
}
|
|
|
|
/* 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
|
|
|
|
disableInt1(); // Disable External Interrupt INT1 to avoid recursive interrupts
|
|
// Enable Interrupts to allow counter 1 interrupts
|
|
interrupts();
|
|
|
|
int16_t waveSample;
|
|
uint32_t scaledSample;
|
|
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)
|
|
|
|
#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;
|
|
};
|
|
|
|
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)
|
|
|
|
pointer = pointer + vPointerIncrement; // increment table pointer (ca. 2us)
|
|
|
|
#endif //CV play sound
|
|
incrementTimer(); // update 32us timer
|
|
|
|
if (PC_STATE) debounce_p++;
|
|
if (debounce_p == 3) {
|
|
noInterrupts();
|
|
pitch_counter = ICR1; // Get Timer-Counter 1 value
|
|
pitch = (pitch_counter - pitch_counter_l); // Counter change since last interrupt -> pitch value
|
|
pitch_counter_l = pitch_counter; // Set actual value as new last value
|
|
};
|
|
|
|
if (debounce_p == 5) {
|
|
pitchValueAvailable = true;
|
|
};
|
|
|
|
if (INT0_STATE) debounce_v++;
|
|
if (debounce_v == 3) {
|
|
noInterrupts();
|
|
vol_counter = vol_counter_i; // Get Timer-Counter 1 value
|
|
vol = (vol_counter - vol_counter_l); // Counter change since last interrupt
|
|
vol_counter_l = vol_counter; // Set actual value as new last value
|
|
};
|
|
|
|
if (debounce_v == 5) {
|
|
volumeValueAvailable = true;
|
|
};
|
|
|
|
noInterrupts();
|
|
enableInt1();
|
|
}
|
|
|
|
/* VOLUME read - interrupt service routine for capturing volume counter value */
|
|
ISR (INT0_vect) {
|
|
vol_counter_i = TCNT1;
|
|
debounce_v = 0;
|
|
};
|
|
|
|
|
|
/* PITCH read - interrupt service routine for capturing pitch counter value */
|
|
ISR (TIMER1_CAPT_vect) {
|
|
debounce_p = 0;
|
|
};
|
|
|
|
|
|
/* PITCH read absolute frequency - interrupt service routine for calibration measurement */
|
|
ISR(TIMER0_COMPA_vect)
|
|
{
|
|
timer_overflow_counter++;
|
|
}
|
|
|
|
/* VOLUME read absolute frequency - interrupt service routine for calibration measurement */
|
|
ISR(TIMER1_OVF_vect)
|
|
{
|
|
timer_overflow_counter++;
|
|
}
|
|
|
|
|
|
|