136 lines
3.4 KiB
C++
136 lines
3.4 KiB
C++
/*
|
|
* Arduino MIDI Player
|
|
*
|
|
* Setup Arduino and use timer2 to synthesize and output sine wave
|
|
*
|
|
* 2016 by ilufang
|
|
*/
|
|
|
|
/*
|
|
* Part of this file contains code modified/referenced from
|
|
* http://interface.khm.de/index.php/lab/interfaces-advanced/arduino-dds-sinewave-generator/
|
|
*
|
|
* DDS Sine Generator mit ATMEGS 168
|
|
* Timer2 generates the 31250 KHz Clock Interrupt
|
|
*
|
|
* KHM 2009 / Martin Nawrath
|
|
* Kunsthochschule fuer Medien Koeln
|
|
* Academy of Media Arts Cologne
|
|
*/
|
|
|
|
#include "avr/pgmspace.h"
|
|
|
|
#include "midi2wave.h"
|
|
|
|
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
|
|
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
|
|
|
|
#define POW2_32 4294967296
|
|
#define refclk 31376.6 // Reference clock
|
|
|
|
// variables in interrupt service
|
|
volatile int timer_tick = 0; // seeking position in wave
|
|
volatile unsigned char timer_micro = 0; // timing counter in microseconds
|
|
volatile unsigned short timer_milli = 0; // timing counter in milliseconds
|
|
volatile unsigned long phaccu_1, phaccu_2, phaccu_3, phaccu_4, phaccu_5, phaccu_6, phaccu_7, phaccu_8; // phase accumulator
|
|
volatile unsigned long tword_m_1, tword_m_2, tword_m_3, tword_m_4, tword_m_5, tword_m_6, tword_m_7, tword_m_8; // DDS tuning word m
|
|
unsigned long phaccu_all;
|
|
|
|
void setup()
|
|
{
|
|
Serial.begin(9600);
|
|
Serial.println("Hello");
|
|
for (int i = 2; i <= 8; ++i)
|
|
pinMode(i,OUTPUT); // LED output
|
|
|
|
pinMode(11,OUTPUT); // PWM Wave output
|
|
setupMidi();
|
|
setupTimer2();
|
|
}
|
|
|
|
void loop()
|
|
{
|
|
while(true) {
|
|
if (timer_milli > event_length) { // wait for the next midi event
|
|
cbi (TIMSK2,TOIE2);
|
|
loadNextEvent();
|
|
// calculate new DDS tuning word
|
|
tword_m_1=POW2_32*PIANO(active_keys[0])/refclk;
|
|
tword_m_2=POW2_32*PIANO(active_keys[1])/refclk;
|
|
tword_m_3=POW2_32*PIANO(active_keys[2])/refclk;
|
|
tword_m_4=POW2_32*PIANO(active_keys[3])/refclk;
|
|
if (!tword_m_1) phaccu_1 = 0;
|
|
if (!tword_m_2) phaccu_2 = 0;
|
|
if (!tword_m_3) phaccu_3 = 0;
|
|
if (!tword_m_4) phaccu_4 = 0;
|
|
timer_milli=0;
|
|
sbi (TIMSK2,TOIE2);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* timer2 setup
|
|
*
|
|
* set pre-scaler to 1, PWM mode to phase correct PWM, 16000000/510 = 31372.55 Hz clock
|
|
*/
|
|
void setupTimer2() {
|
|
// Timer2 Clock Pre-scaler to : 1
|
|
sbi (TCCR2B, CS20);
|
|
cbi (TCCR2B, CS21);
|
|
cbi (TCCR2B, CS22);
|
|
|
|
// Timer2 PWM Mode set to Phase Correct PWM
|
|
cbi (TCCR2A, COM2A0); // clear Compare Match
|
|
sbi (TCCR2A, COM2A1);
|
|
|
|
sbi (TCCR2A, WGM20); // Mode 1 / Phase Correct PWM
|
|
cbi (TCCR2A, WGM21);
|
|
cbi (TCCR2B, WGM22);
|
|
|
|
// initialize DDS tuning word
|
|
tword_m_1=0;
|
|
tword_m_2=0;
|
|
tword_m_3=0;
|
|
tword_m_4=0;
|
|
|
|
// disable Timer0 interrupts to avoid timing distortion
|
|
cbi (TIMSK0,TOIE0);
|
|
// start Timer2!
|
|
sbi (TIMSK2,TOIE2);
|
|
|
|
}
|
|
|
|
/*
|
|
* Timer2 Interrupt Service
|
|
*
|
|
* Running at 31372,550 KHz = 32uSec
|
|
* this is the timebase REFCLOCK for the DDS generator
|
|
* FOUT = (M (REFCLK)) / (2 exp 32)
|
|
* runtime : 8 microseconds ( inclusive push and pop)
|
|
*/
|
|
ISR(TIMER2_OVF_vect) {
|
|
// soft DDS, phase accumulator with 32 bits
|
|
phaccu_1 += tword_m_1;
|
|
phaccu_2 += tword_m_2;
|
|
phaccu_3 += tword_m_3;
|
|
phaccu_4 += tword_m_4;
|
|
phaccu_5 += tword_m_5;
|
|
|
|
// use upper 8 bits for phase accumulator as frequency information
|
|
int phaccu_all = sine[phaccu_1>>24];
|
|
phaccu_all += sine[phaccu_2>>24];
|
|
phaccu_all += sine[phaccu_3>>24];
|
|
phaccu_all += sine[phaccu_4>>24];
|
|
phaccu_all += sine[phaccu_5>>24];
|
|
|
|
// Write to PWM port 11
|
|
OCR2A = phaccu_all/KEYBUF_SIZE;
|
|
|
|
// Increment timing counter
|
|
if(++timer_micro == 31) {
|
|
++timer_milli;
|
|
timer_micro=0;
|
|
}
|
|
}
|