Code:
/*
Copyright 2007 Richard Cappels used with his permission
www.projects.cappels.org
This assumes an R/2R DAC connected to pins 2-9:
GND--2R--+--R--+--R--+--R--+--R--+--R--+--R--+--R--+--OUT
| | | | | | | |
2R 2R 2R 2R 2R 2R 2R 2R
| | | | | | | |
pin2 pin3 pin4 pin5 pin6 pin7 pin8 pin9
(That is:
2R from ground to point A
2R from pin 2 to point A
R from point A to point B
2R from pin 3 to point B
R from point B to point C
2R from pin 4 to point C
R from point C to point D
2R from pin 5 to point D
R from point D to point E
2R from pin 6 to point E
R from point E to point F
2R from pin 7 to point F
R from point F to point G
2R from pin 8 to point G
R from point G to point H
2R from pin 9 to point H
Direct wire from point H to headphone output (with optional buffer)
)
Also assumes headphone ground is connected to pin MIDRANGE via a low-pass filter.
We use PWM on pin 10 to get roughly half of the regulated voltage as the headphone ground,
so the 8-bit signal can show up as properly A/C.
Also assumes a pot attached to pin POTPIN (varying between VIN and GND) for pitch,
and a button attached between BUTTONPIN and GND, with pullup resistor, to
temporarily switch to triangle wave.
Also toggles the LED on LEDPIN (13) when the wavetable loops.
Note PIT counter frequency on Kinetis 20 (Teensy 3.0) is 50MHz
Original sketch:
- Hotcarrier 2008/04/14
Modified for potentiometer controlled pitch on analogpin0
- Collin Cunningham 2008/05/29
Modified for Teensy 3.0
- Emertonom 2012/10/27
*/
// Teensy 3.0 isn't AVR, so we don't need the AVR interrupt stuff
// #include <avr/io.h>
// #include <avr/interrupt.h>
#include <math.h> // for sin, round, and M_PI
// Pin for midrange reference voltage
#define MIDRANGE 10
// Pin for button input (sine/triangle selector)
#define BUTTONPIN 11
// Pin for LED
#define LEDPIN 13
// Number of steps in wave table
// Originally 32, but that's a little harsh--
// 256 bits sounds WAY better.
#define WAVESTEPS 256
// Constants for bitvalues within the TCTRL1 register
#define TIE 2
#define TEN 1
unsigned char sinetable[WAVESTEPS];
unsigned char tritable[WAVESTEPS];
volatile int i;
bool buttonval;
int potval;
volatile bool ledVal;
void ioinit (void)
{
pinMode(2,OUTPUT);
pinMode(3,OUTPUT);
pinMode(4,OUTPUT);
pinMode(5,OUTPUT);
pinMode(6,OUTPUT);
pinMode(7,OUTPUT);
pinMode(8,OUTPUT);
pinMode(9,OUTPUT);
pinMode(MIDRANGE,OUTPUT);
pinMode(BUTTONPIN, INPUT);
pinMode(A0, INPUT);
pinMode(LEDPIN, OUTPUT);
analogReference(INTERNAL);
analogReadRes(16);
}
void timer_setup() {
/* Old Arduino version of timer setup
TCCR2A = 0;
TCNT2=455; //455 outputs 1.007khz
TCCR2B = B00000010;
//Timer2 Overflow Interrupt Enable
TIMSK2 = 1<<TOIE2;
*/
// Teensy 3.0 version
SIM_SCGC6 |= SIM_SCGC6_PIT; // Activates the clock for PIT
// Turn on PIT
PIT_MCR = 0x00;
// Set the period of the timer. Unit is (1 / 50MHz) = 20ns
// So 1 kHz frequency -> 1 ms period -> 50000 * 20ns
PIT_LDVAL1 = 50000;
// Enable interrupts on timer1
PIT_TCTRL1 = TIE;
// Start the timer
PIT_TCTRL1 |= TEN;
NVIC_ENABLE_IRQ(IRQ_PIT_CH1); // Another step to enable PIT channel 1 interrupts
}
void setup(){
ioinit();
arraysetup();
cli();
timer_setup();
i = 0;
buttonval = false;
sei();
analogWrite(MIDRANGE, 127); // Output midrange signal as offset ground for headphone
digitalWrite(LEDPIN, LOW); // Start the LED off
ledVal = false;
}
void pit1_isr(void) {
PIT_TFLG1 = 1;
if (buttonval) {
digitalWriteFast(2, tritable[i] & 1);
digitalWriteFast(3, tritable[i] & 2);
digitalWriteFast(4, tritable[i] & 4);
digitalWriteFast(5, tritable[i] & 8);
digitalWriteFast(6, tritable[i] & 16);
digitalWriteFast(7, tritable[i] & 32);
digitalWriteFast(8, tritable[i] & 64);
digitalWriteFast(9, tritable[i] & 128);
} else {
digitalWriteFast(2, sinetable[i] & 1);
digitalWriteFast(3, sinetable[i] & 2);
digitalWriteFast(4, sinetable[i] & 4);
digitalWriteFast(5, sinetable[i] & 8);
digitalWriteFast(6, sinetable[i] & 16);
digitalWriteFast(7, sinetable[i] & 32);
digitalWriteFast(8, sinetable[i] & 64);
digitalWriteFast(9, sinetable[i] & 128);
}
++i;
// Arduino version of updating pitch
// TCNT2=potval;
// Teensy 3.0 Update Pitch
// If PIT_LDVAL1 gets low enough that the counter trips
// faster than this interrupt routine finishes, the
// sketch hangs...so we add 1000 just to be safe
PIT_LDVAL1 = round(potval / (WAVESTEPS / 8.0) + 1000);
if (i==WAVESTEPS) {
i=0; // Go back to start of wavetable
ledVal = !ledVal; // Toggle LED
if (ledVal) digitalWriteFast(LEDPIN, LOW);
else digitalWriteFast(LEDPIN, HIGH);
}
}
void arraysetup(void) {
int k;
// Set up sinetable with an 8-bit quantized sine wave of WAVESTEPS steps
for (k = 0; k < WAVESTEPS; k++) {
sinetable[k] = round(127.5 + 127.5 * sin( 2 * k * M_PI / WAVESTEPS ));
}
// Set up tritable with an 8-bit quantized triangle wave of WAVESTEPS steps
for (k = 0; k < WAVESTEPS; k++) {
if (k < WAVESTEPS / 4) {
tritable[k] = round(127.5 + 510.0 / WAVESTEPS * k);
} else if (k < 3 * WAVESTEPS / 4) {
tritable[k] = round(382.5 - 510.0 / WAVESTEPS * k);
} else {
tritable[k] = round(510.0 / WAVESTEPS * k - 382.5);
}
}
}
void loop() {
while (1) {
potval = analogRead(A0);
buttonval = (digitalRead(BUTTONPIN) == LOW);
}
}
Hope this helps someone!