First Version of Software V3

Still needs some work.
master
Gaudi 8 years ago
parent d4c161eeeb
commit b08b78404e
  1. 56
      Open_Theremin_V3/OTPinDefs.h
  2. 79
      Open_Theremin_V3/Open_Theremin_V3.ino
  3. 480
      Open_Theremin_V3/application.cpp
  4. 69
      Open_Theremin_V3/application.h
  5. 14
      Open_Theremin_V3/build.h
  6. 19
      Open_Theremin_V3/hw.h
  7. 209
      Open_Theremin_V3/ihandlers.cpp
  8. 44
      Open_Theremin_V3/ihandlers.h
  9. 151
      Open_Theremin_V3/mcpDac.h
  10. 1032
      Open_Theremin_V3/theremin_sintable.c
  11. 1032
      Open_Theremin_V3/theremin_sintable2.c
  12. 1032
      Open_Theremin_V3/theremin_sintable3.c
  13. 1033
      Open_Theremin_V3/theremin_sintable4.c
  14. 1032
      Open_Theremin_V3/theremin_sintable5.c
  15. 1032
      Open_Theremin_V3/theremin_sintable6.c
  16. 1033
      Open_Theremin_V3/theremin_sintable7.c
  17. 1032
      Open_Theremin_V3/theremin_sintable8.c
  18. 17
      Open_Theremin_V3/timer.cpp
  19. 39
      Open_Theremin_V3/timer.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

@ -0,0 +1,79 @@
/*
* Open.Theremin control software for Arduino.UNO
* Version 3.0
* Copyright (C) 2010-2016 by Urs Gaudenz
*
* Open.Theremin.UNO control software 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.
*
* Open.Theremin.UNO control software 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 Open.Theremin.UNO control software. If not, see <http://www.gnu.org/licenses/>.
*
* With important contributions by
* David Harvey
* Michael Margolis
*/
/**
Building the code
=================
build.h contains #defines that control the compilation of the code
ENABLE_SERIAL - if non-0, the build will include code to write the detected
pitch to the serial connection every 100 milliseconds. Set serial
receive baud to 115200
ENABLE_CV - if non-0, emit cv output on pin 6 (EXPERIMENTAL!)
Structure of the code
=====================
** Open_Theremin_UNO.ino **
This file. Creates and hooks up the application object to the arduino setup()
and loop() callbacks.
** application.h/application.cpp **
Main application object. Holds the state of the app (playing, calibrating), deals
with initialisation and the app main loop, reads pitch and volume changed flags
from the interrupt handlers and sets pitch and volume values which the timer
interrupt sends to the DAC.
** OTPinDefs.h **
Pin definitions for the DAC.
** build.h **
Preprocessor definitions for build (see above).
** hw.h **
Definitions for hardware button and LED.
** ihandlers.h/ihandlers.cpp
Interrupt handler code and volatile variables implementing the communication between
the app and its input/output.
** theremin_sinetable<N>.c **
Wavetable data for a variety of sounds. Switchable via the potentiometer.
** timer.h/timer.cpp **
Definitions and functions for setting delays in tics and in milliseconds
*/
#include "application.h"
Application app;
void setup() {
app.setup();
}
void loop() {
app.loop();
}

@ -0,0 +1,480 @@
#include "Arduino.h"
#include "application.h"
#include "hw.h"
#include "mcpDac.h"
#include "ihandlers.h"
#include "timer.h"
const AppMode AppModeValues[] = {MUTE,NORMAL};
static int32_t pitchCalibrationBase = 0;
static int32_t pitchCalibrationBaseFreq = 0;
static int32_t pitchCalibrationConstant = 0;
static int32_t pitchSensitivityConstant = 70000;
static float qMeasurement = 0;
static int32_t volCalibrationBase = 0;
Application::Application()
: _state(PLAYING),
_mode(NORMAL) {
};
void Application::setup() {
#if SERIAL_ENABLED
Serial.begin(Application::BAUD);
#endif
HW_LED1_ON;HW_LED2_OFF;
pinMode(Application::BUTTON_PIN, INPUT_PULLUP);
pinMode(Application::LED_PIN_1, OUTPUT);
pinMode(Application::LED_PIN_2, OUTPUT);
digitalWrite(Application::LED_PIN_1, HIGH); // turn the LED off by making the voltage LOW
initialiseTimer();
initialiseInterrupts();
mcpDacInit();
/// TEST
calibrate_pitch();
calibrate_volume();
initialiseTimer();
initialiseInterrupts();
#if CV_ENABLED
initialiseCVOut();
#endif
playStartupSound();
calibrate();
}
void Application::initialiseTimer() {
ihInitialiseTimer();
}
void Application::initialiseInterrupts() {
ihInitialiseInterrupts();
}
void Application::InitialisePitchMeasurement() {
ihInitialisePitchMeasurement();
}
void Application::InitialiseVolumeMeasurement() {
ihInitialiseVolumeMeasurement();
}
unsigned long Application::GetQMeasurement()
{
int qn=0;
TCCR1B = (1<<CS10);
while(!(PIND & (1<<PORTD3)));
while((PIND & (1<<PORTD3)));
TCNT1 = 0;
timer_overflow_counter = 0;
while(qn<31250){
while(!(PIND & (1<<PORTD3)));
qn++;
while((PIND & (1<<PORTD3)));
};
TCCR1B = 0;
unsigned long frequency = TCNT1;
unsigned long temp = 65536*(unsigned long)timer_overflow_counter;
frequency += temp;
return frequency;
}
unsigned long Application::GetPitchMeasurement()
{
TCNT1 = 0;
timer_overflow_counter = 0;
TCCR1B = (1<<CS12) | (1<<CS11) | (1<<CS10);
delay(1000);
TCCR1B = 0;
unsigned long frequency = TCNT1;
unsigned long temp = 65536*(unsigned long)timer_overflow_counter;
frequency += temp;
return frequency;
}
unsigned long Application::GetVolumeMeasurement()
{timer_overflow_counter = 0;
TCNT0=0;
TCNT1=49911;
TCCR0B = (1<<CS02) | (1<<CS01) | (1<<CS00); // //External clock source on T0 pin. Clock on rising edge.
TIFR1 = (1<<TOV1); //Timer1 INT Flag Reg: Clear Timer Overflow Flag
while(!(TIFR1&((1<<TOV1)))); // on Timer 1 overflow (1s)
TCCR0B = 0; // Stop TimerCounter 0
unsigned long frequency = TCNT0; // get counter 0 value
unsigned long temp = (unsigned long)timer_overflow_counter; // and overflow counter
frequency += temp*256;
return frequency;
}
#if CV_ENABLED // Initialise PWM Generator for CV output
void initialiseCVOut() {
}
#endif
AppMode Application::nextMode() {
return _mode == NORMAL ? MUTE : AppModeValues[_mode + 1];
}
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)
int32_t pitch_f = 0; // pitch in frequency (for filtering)
uint16_t volumePotValue = 0;
uint16_t pitchPotValue = 0;
mloop: // Main loop avoiding the GCC "optimization"
pitchPotValue = analogRead(PITCH_POT);
volumePotValue = analogRead(VOLUME_POT);
vWavetableSelector = analogRead(WAVE_SELECT_POT) >> 7;
if (_state == PLAYING && HW_BUTTON_PRESSED) {
_state = CALIBRATING;
resetTimer();
}
if (_state == CALIBRATING && HW_BUTTON_RELEASED) {
if (timerExpired(1500)) {
_mode = nextMode();
if (_mode==NORMAL) {HW_LED1_ON;HW_LED2_OFF;} else {HW_LED1_OFF;HW_LED2_ON;};
// playModeSettingSound();
}
_state = PLAYING;
};
if (_state == CALIBRATING && timerExpired(10000)) {
HW_LED2_ON;
playCalibratingCountdownSound();
calibrate();
_mode=NORMAL;
HW_LED2_OFF;
while (HW_BUTTON_PRESSED)
; // NOP
_state = PLAYING;
};
#if CV_ENABLED
OCR0A = pitch & 0xff;
#endif
#if SERIAL_ENABLED
if (timerExpired(TICKS_100_MILLIS)) {
resetTimer();
Serial.write(pitch & 0xff); // Send char on serial (if used)
Serial.write((pitch >> 8) & 0xff);
}
#endif
if (pitchValueAvailable) { // If capture event
pitch_v=pitch; // Averaging pitch values
pitch_v=pitch_l+((pitch_v-pitch_l)>>2);
pitch_l=pitch_v;
pitch_f=FREQ_FACTOR/pitch_v;
//HW_LED2_ON;
// set wave frequency for each mode
switch (_mode) {
case MUTE : /* NOTHING! */; break;
case NORMAL : setWavetableSampleAdvance((pitchCalibrationBase-pitch_v)/4+2048-(pitchPotValue<<2)); break;
};
// HW_LED2_OFF;
pitchValueAvailable = false;
}
if (volumeValueAvailable) {
vol = max(vol, 5000);
vol_v=vol; // Averaging volume values
vol_v=vol_l+((vol_v-vol_l)>>2);
vol_l=vol_v;
switch (_mode) {
case MUTE: vol_v = 0; break;
case NORMAL: vol_v = MAX_VOLUME-(volCalibrationBase-vol_v)/2+(volumePotValue<<2)-1024; break;
};
// Limit and set volume value
vol_v = min(vol_v, 4095);
// vol_v = vol_v - (1 + MAX_VOLUME - (volumePotValue << 2));
vol_v = vol_v ;
vol_v = max(vol_v, 0);
vScaledVolume = vol_v >> 4;
volumeValueAvailable = false;
}
goto mloop; // End of main loop
}
void Application::calibrate()
{
resetPitchFlag();
resetTimer();
savePitchCounter();
while (!pitchValueAvailable && timerUnexpiredMillis(10))
; // NOP
pitchCalibrationBase = pitch;
pitchCalibrationBaseFreq = FREQ_FACTOR/pitchCalibrationBase;
pitchCalibrationConstant = FREQ_FACTOR/pitchSensitivityConstant/2+200;
resetVolFlag();
resetTimer();
saveVolCounter();
while (!volumeValueAvailable && timerUnexpiredMillis(10))
; // NOP
volCalibrationBase = vol;
}
void Application::calibrate_pitch()
{
static int16_t pitchXn0 = 0;
static int16_t pitchXn1 = 0;
static int16_t pitchXn2 = 0;
static float q0 = 0;
static long pitchfn0 = 0;
static long pitchfn1 = 0;
static long pitchfn = 0;
Serial.begin(9600);
Serial.println("Pitch calibration");
InitialisePitchMeasurement();
interrupts();
mcpDacInit();
qMeasurement = GetQMeasurement();
Serial.print("Q ");
Serial.println(qMeasurement);
q0 = (16000000/qMeasurement*500000);
Serial.println(q0);
pitchXn0 = 0;
pitchXn1 = 4095;
pitchfn = q0-600;
mcpDac2BSend(1600);
mcpDac2ASend(pitchXn0);
delay(100);
pitchfn0 = GetPitchMeasurement();
mcpDac2ASend(pitchXn1);
delay(100);
pitchfn1 = GetPitchMeasurement();
Serial.print(pitchfn0);
Serial.print(" ");
Serial.println(pitchfn1);
while(abs(pitchfn0-pitchfn1)>10){
mcpDac2ASend(pitchXn0);
delay(100);
pitchfn0 = GetPitchMeasurement()-pitchfn;
mcpDac2ASend(pitchXn1);
delay(100);
pitchfn1 = GetPitchMeasurement()-pitchfn;
pitchXn2=pitchXn1-((pitchXn1-pitchXn0)*pitchfn1)/(pitchfn1-pitchfn0);
Serial.print(pitchXn0);
Serial.print(" ");
Serial.println(pitchfn0);
Serial.print(pitchXn1);
Serial.print(" ");
Serial.println(pitchfn1);
Serial.println(pitchXn2);
Serial.println();
pitchXn0 = pitchXn1;
pitchXn1 = pitchXn2;
HW_LED2_TOGGLE;
}
digitalWrite(Application::LED_PIN_2, LOW); // turn the LED off by making the voltage LOW
}
void Application::calibrate_volume()
{
static int16_t volumeXn0 = 0;
static int16_t volumeXn1 = 0;
static int16_t volumeXn2 = 0;
static float q0 = 0;
static long volumefn0 = 0;
static long volumefn1 = 0;
static long volumefn = 0;
Serial.begin(9600);
Serial.println("Volume calibration");
InitialiseVolumeMeasurement();
interrupts();
mcpDacInit();
volumeXn0 = 0;
volumeXn1 = 4095;
q0 = (16000000/qMeasurement*460765);
volumefn = q0-600;
mcpDac2BSend(volumeXn0);
delay_NOP(44316);//44316=100ms
volumefn0 = GetVolumeMeasurement();
mcpDac2BSend(volumeXn1);
delay_NOP(44316);//44316=100ms
volumefn1 = GetVolumeMeasurement();
Serial.print(volumefn0);
Serial.print(" ");
Serial.println(volumefn1);
while(abs(volumefn0-volumefn1)>10){
mcpDac2BSend(volumeXn0);
delay_NOP(44316);//44316=100ms
volumefn0 = GetVolumeMeasurement()-volumefn;
mcpDac2BSend(volumeXn1);
delay_NOP(44316);//44316=100ms
volumefn1 = GetVolumeMeasurement()-volumefn;
volumeXn2=volumeXn1-((volumeXn1-volumeXn0)*volumefn1)/(volumefn1-volumefn0);
Serial.print(volumeXn0);
Serial.print(" ");
Serial.println(volumefn0);
Serial.print(volumeXn1);
Serial.print(" ");
Serial.println(volumefn1);
Serial.println(volumeXn2);
Serial.println();
volumeXn0 = volumeXn1;
volumeXn1 = volumeXn2;
HW_LED2_TOGGLE;
}
digitalWrite(Application::LED_PIN_2, HIGH); // turn the LED off by making the voltage LOW
}
void Application::hzToAddVal(float hz) {
setWavetableSampleAdvance((uint16_t)(hz * HZ_ADDVAL_FACTOR));
}
void Application::playNote(float hz, uint16_t milliseconds = 500, uint8_t volume = 255) {
vScaledVolume = volume;
hzToAddVal(hz);
millitimer(milliseconds);
vScaledVolume = 0;
}
void Application::playStartupSound() {
playNote(MIDDLE_C, 150, 25);
playNote(MIDDLE_C * 2, 150, 25);
playNote(MIDDLE_C * 4, 150, 25);
}
void Application::playCalibratingCountdownSound() {
for (int i = 0; i < 5; i++) {
playNote(MIDDLE_C, 500, 25);
millitimer(150);
}
playNote(MIDDLE_C * 2, 1000, 25);
}
void Application::playModeSettingSound() {
for (int i = 0; i <= _mode; i++) {
playNote(MIDDLE_C * 2, 200, 25);
millitimer(100);
}
}
void Application::delay_NOP(unsigned long time) {
volatile unsigned long i = 0;
for (i = 0; i < time; i++) {
__asm__ __volatile__ ("nop");
}
}

@ -0,0 +1,69 @@
#ifndef _APPLICATION_H
#define _APPLICATION_H
#include <avr/io.h>
#include "build.h"
enum AppState {CALIBRATING = 0, PLAYING};
enum AppMode {MUTE = 0, NORMAL};
class Application {
public:
Application();
void setup();
void loop();
private:
static const uint16_t MAX_VOLUME = 4095;
static const uint32_t TRIM_PITCH_FACTOR = 33554432;
static const uint32_t FREQ_FACTOR = 1600000000;
static const int16_t BUTTON_PIN = 6;
static const int16_t LED_PIN_1 = 18;
static const int16_t LED_PIN_2 = 19;
static const int16_t PITCH_POT = 0;
static const int16_t VOLUME_POT = 1;
static const int16_t WAVE_SELECT_POT = 2;
static const int16_t REGISTER_SELECT_POT = 3;
#if SERIAL_ENABLED
static const int BAUD = 115200;
#endif
AppState _state;
AppMode _mode;
void calibrate();
void calibrate_pitch();
void calibrate_volume();
AppMode nextMode();
void initialiseTimer();
void initialiseInterrupts();
void InitialisePitchMeasurement();
void InitialiseVolumeMeasurement();
unsigned long GetPitchMeasurement();
unsigned long GetVolumeMeasurement();
unsigned long GetQMeasurement();
static const float HZ_ADDVAL_FACTOR = 2.09785;
static const float MIDDLE_C = 261.6;
void playNote(float hz, uint16_t milliseconds, uint8_t volume);
void hzToAddVal(float hz);
void playStartupSound();
void playCalibratingCountdownSound();
void playModeSettingSound();
void delay_NOP(unsigned long time);
};
#endif // _APPLICATION_H

@ -0,0 +1,14 @@
// Build definitions
#ifndef _BUILD_H
#define _BUILD_H
// Set to build with serial support
#define SERIAL_ENABLED 0
// Set to build with control voltage output (experimental)
#define CV_ENABLED 0
#endif // _BUILD_H

@ -0,0 +1,19 @@
#ifndef _HW_H
#define _HW_H
#define HW_BUTTON_STATE (PIND & (1<<PORTD6))
#define HW_BUTTON_PRESSED (HW_BUTTON_STATE == LOW)
#define HW_BUTTON_RELEASED (HW_BUTTON_STATE != LOW)
#define HW_LED1_ON (PORTC |= (1<<PORTC4))
#define HW_LED1_OFF (PORTC &= ~(1<<PORTC4))
#define HW_LED2_ON (PORTC |= (1<<PORTC5))
#define HW_LED2_OFF (PORTC &= ~(1<<PORTC5))
#define HW_LED1_TOGGLE (PORTC = PORTC ^ (1<<PORTC4))
#define HW_LED2_TOGGLE (PORTC = PORTC ^ (1<<PORTC5))
#endif // _HW_H

@ -0,0 +1,209 @@
#include "Arduino.h"
#include "ihandlers.h"
#include "mcpDac.h"
#include "timer.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 = 1748;
#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;
// 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) >> 9);
} else {
scaledSample = MCP_DAC_BASE - (mul_16_8(-waveSample, vScaledVolume) >> 9);
}
mcpDacSend(scaledSample); //Send result to Digital to Analogue Converter (audio out) (9.6 us)
pointer = pointer + vPointerIncrement; // increment table pointer (ca. 2us)
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++;
}

@ -0,0 +1,44 @@
#ifndef _IHANDLERS_H
#define _IHANDLERS_H
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 pitch_counter; // Pitch counter
extern volatile uint16_t pitch_counter_l; // Last value of pitch counter
extern volatile uint16_t vol_counter; // Pitch counter
extern volatile uint16_t vol_counter_l; // Last value of pitch counter
extern volatile uint16_t timer_overflow_counter; // counter for frequency measurement
extern volatile bool volumeValueAvailable; // Volume read flag
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
inline void resetPitchFlag() { pitchValueAvailable = false; }
inline void resetVolFlag() { volumeValueAvailable = false; }
inline void savePitchCounter() { pitch_counter_l=pitch_counter; }
inline void saveVolCounter() { vol_counter_l=vol_counter; };
inline void setWavetableSampleAdvance(uint16_t val) { vPointerIncrement = val;}
inline void disableInt1() { EIMSK &= ~ (1 << INT1); }
inline void enableInt1() { if (reenableInt1) EIMSK |= (1 << INT1); }
void ihInitialiseTimer();
void ihInitialiseInterrupts();
void ihInitialisePitchMeasurement();
void ihInitialiseVolumeMeasurement();
void resetPitchFlag();
void resetVolFlag();
void savePitchCounter();
void saveVolCounter();
#endif // _IHANDLERS_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
* <http://www.gnu.org/licenses/>.
*/
/**
* Macros and inline functions for MCP4921 DAC
*/
#ifndef mcpDac_h
#define mcpDac_h
#include <avr/io.h>
#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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,17 @@
#include "Arduino.h"
#include "timer.h"
volatile uint16_t timer = 0;
void ticktimer (uint16_t ticks) {
resetTimer();
while (timerUnexpired(ticks))
; // NOP
};
void millitimer (uint16_t milliseconds) {
ticktimer(millisToTicks(milliseconds));
}

@ -0,0 +1,39 @@
#ifndef _TIMER_H
#define _TIMER_H
extern volatile uint16_t timer;
inline uint16_t millisToTicks(uint16_t milliseconds) {
return milliseconds * (1000.0f/32);
}
inline void resetTimer() {
timer = 0;
}
inline void incrementTimer() {
timer++;
}
inline bool timerExpired(uint16_t ticks) {
return timer >= ticks;
}
inline bool timerUnexpired(uint16_t ticks) {
return timer < ticks;
}
inline bool timerExpiredMillis(uint16_t milliseconds) {
return timerExpired(millisToTicks(milliseconds));
}
inline bool timerUnexpiredMillis(uint16_t milliseconds) {
return timerUnexpired(millisToTicks(milliseconds));
}
void ticktimer (uint16_t ticks);
void millitimer (uint16_t milliseconds);
const uint16_t TICKS_100_MILLIS = millisToTicks(100);
#endif // _TIMER_H
Loading…
Cancel
Save