From 11175424113617ed84a2bf95a85e3d8ceeb1bafb Mon Sep 17 00:00:00 2001 From: Stephen Olesen Date: Wed, 1 Jul 2015 14:04:20 -0600 Subject: [PATCH] Created generic DDS class for tone generation, update AFSK to start the right timers. --- AFSK.cpp | 28 ++++++--- DDS.cpp | 82 ++++++++++++++++++++++++++ DDS.h | 171 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 272 insertions(+), 9 deletions(-) create mode 100644 DDS.cpp create mode 100644 DDS.h diff --git a/AFSK.cpp b/AFSK.cpp index 7bd4612..336e055 100644 --- a/AFSK.cpp +++ b/AFSK.cpp @@ -14,6 +14,8 @@ #define PPOOL_SIZE 2 +#define COMPARE_BITS 6 +#define ACCUMULATOR_SIZE 32 #define ACCUMULATOR_BITS 24 // This is 2^10 bits used from accum //#undef PROGMEM //#define PROGMEM __attribute__((section(".progmem.data"))) @@ -44,15 +46,15 @@ volatile unsigned long lastTx = 0; volatile unsigned long lastTxEnd = 0; volatile unsigned long lastRx = 0; -#define REFCLK 9600 +#define REFCLK 38400 //#define REFCLK 31372.54902 //#define REFCLK (16000000.0/510.0) //#define REFCLK 31200.0 // 2200Hz = pow(2,32)*2200.0/refclk // 1200Hz = pow(2,32)*1200.0/refclk static const unsigned long toneStep[2] = { - pow(2,32)*2200.0/REFCLK, - pow(2,32)*1200.0/REFCLK + (2200.0/REFCLK)*pow(2,ACCUMULATOR_SIZE), + (1200.0/REFCLK)*pow(2,ACCUMULATOR_SIZE) }; // Set to an arbitrary frequency @@ -143,12 +145,7 @@ void AFSK::Encoder::process() { accumulator += toneStep[currentTone]; uint8_t phAng = (accumulator >> ACCUMULATOR_BITS); - /*if(toneVolume[currentTone] != 255) { - OCR2B = pwm * toneVolume[currentTone] / 255; - } else {*/ - // No volume scaling required - OCR2B = pgm_read_byte_near(sinetable + phAng); - /*}*/ + OCR2B = pgm_read_byte_near(sinetable + phAng)>>(8-COMPARE_BITS); } bool AFSK::Encoder::start() { @@ -368,6 +365,19 @@ bool AFSK::Decoder::read() { void AFSK::Decoder::start() { // Do this in start to allocate our first packet currentPacket = pBuf.makePacket(PACKET_MAX_LEN); + ASSR &= ~(_BV(EXCLK) | _BV(AS2)); + + // Do non-inverting PWM on pin OC2B (arduino pin 3) (p.159). + // OC2A (arduino pin 11) stays in normal port operation: + // COM2B1=1, COM2B0=0, COM2A1=0, COM2A0=0 + // Mode 1 - Phase correct PWM + TCCR2A = (TCCR2A | _BV(COM2B1)) & ~(_BV(COM2B0) | _BV(COM2A1) | _BV(COM2A0)) | + _BV(WGM21) | _BV(WGM20); + // No prescaler (p.162) + TCCR2B = (TCCR2B & ~(_BV(CS22) | _BV(CS21))) | _BV(CS20) | _BV(WGM22); + + OCR2A = pow(2,COMPARE_BITS)-1; + OCR2B = 0; // Configure the ADC and Timer1 to trigger automatic interrupts TCCR1A = 0; TCCR1B = _BV(CS11) | _BV(WGM13) | _BV(WGM12); diff --git a/DDS.cpp b/DDS.cpp new file mode 100644 index 0000000..b88b556 --- /dev/null +++ b/DDS.cpp @@ -0,0 +1,82 @@ +#include +#include "DDS.h" + +// To start the DDS, we use Timer1, set to the reference clock +// We use Timer2 for the PWM output, running as fast as feasible +void DDS::start() { + // Use the clkIO clock rate + ASSR &= ~(_BV(EXCLK) | _BV(AS2)); + + // First, the timer for the PWM output + // Setup the timer to use OC2B (pin 3) in fast PWM mode with a configurable top + // Run it without the prescaler + TCCR2A = (TCCR2A | _BV(COM2B1)) & ~(_BV(COM2B0) | _BV(COM2A1) | _BV(COM2A0)) | + _BV(WGM21) | _BV(WGM20); + TCCR2B = (TCCR2B & ~(_BV(CS22) | _BV(CS21))) | _BV(CS20) | _BV(WGM22); + + // Set the top limit, which will be our duty cycle accuracy. + // Setting Comparator Bits smaller will allow for higher frequency PWM, + // with the loss of resolution. + OCR2A = pow(2,COMPARATOR_BITS)-1; + OCR2B = 0; + + // Second, setup Timer1 to trigger the ADC interrupt + // This lets us use decoding functions that run at the same reference + // clock as the DDS. + TCCR1B = _BV(CS11) | _BV(WGM13) | _BV(WGM12); + TCCR1A = 0; + ICR1 = ((F_CPU / 8) / DDS_REFCLK_DEAULT) - 1; + + // Configure the ADC here to automatically run and be triggered off Timer1 + ADMUX = _BV(REFS0) | _BV(ADLAR) | 0; // Channel 0, shift result left (ADCH used) + DDRC &= ~_BV(0); + PORTC &= ~_BV(0); + DIDR0 |= _BV(0); + ADCSRB = _BV(ADTS2) | _BV(ADTS1) | _BV(ADTS0); + ADCSRA = _BV(ADEN) | _BV(ADSC) | _BV(ADATE) | _BV(ADIE) | _BV(ADPS2); // | _BV(ADPS0); +} + +void DDS::stop() { + // TODO: Stop the timers. +} + +// Set our current sine wave frequency in Hz +void DDS::setFrequency(unsigned short freq) { + // Fo = (M*Fc)/2^N + // M = (Fo/Fc)*2^N + if(refclk == DDS_REFCLK_DEAULT) { + // Try to use precalculated values if possible + if(freq == 2200) { + stepRate = (2200.0 / DDS_REFCLK_DEAULT) * pow(2,ACCUMULATOR_BITS); + } else if (freq == 1200) { + stepRate = (1200.0 / DDS_REFCLK_DEAULT) * pow(2,ACCUMULATOR_BITS); + } + } else { + // Do the actual math instead. + stepRate = (freq / refclk) * pow(2,ACCUMULATOR_BITS); + } +} + +void DDS::clockTick() { + accumulator += stepRate; + if(running) { + if(tickDuration == 0) { + OCR2B = 0; + running = false; + } else { + OCR2B = getPhaseAngle(); + } + // Reduce our playback duration by one tick + tickDuration--; + } +} + +uint8_t DDS::getPhaseAngle() { +#if ACCUMULATOR_BIT_SHIFT >= 24 + uint16_t phAng; +#else + uint8_t phAng; +#endif + phAng = (accumulator >> ACCUMULATOR_BIT_SHIFT); + return pgm_read_byte_near(ddsSineTable + phAng)>>(8-COMPARATOR_BITS); +} diff --git a/DDS.h b/DDS.h new file mode 100644 index 0000000..edb8dcf --- /dev/null +++ b/DDS.h @@ -0,0 +1,171 @@ +#ifndef _DDS_H_ +#define _DDS_H_ + +#include + +#define SHORT_ACCUMULATOR + +#ifdef SHORT_ACCUMULATOR +#define ACCUMULATOR_BITS 16 +#else +#define ACCUMULATOR_BITS 32 +#endif + +#define COMPARATOR_BITS 6 + +#define DDS_REFCLK_DEAULT 38400 + +#define DDS_TABLE_LARGE + +#ifdef DDS_TABLE_LARGE +#define ACCUMULATOR_BIT_SHIFT (ACCUMULATOR_BITS-10) +static const uint8_t ddsSineTable[1024] PROGMEM = { + 128,128,129,130,131,131,132,133,134,135,135,136,137,138,138,139, + 140,141,142,142,143,144,145,145,146,147,148,149,149,150,151,152, + 152,153,154,155,155,156,157,158,158,159,160,161,162,162,163,164, + 165,165,166,167,167,168,169,170,170,171,172,173,173,174,175,176, + 176,177,178,178,179,180,181,181,182,183,183,184,185,186,186,187, + 188,188,189,190,190,191,192,192,193,194,194,195,196,196,197,198, + 198,199,200,200,201,202,202,203,203,204,205,205,206,207,207,208, + 208,209,210,210,211,211,212,213,213,214,214,215,215,216,217,217, + 218,218,219,219,220,220,221,221,222,222,223,224,224,225,225,226, + 226,227,227,228,228,228,229,229,230,230,231,231,232,232,233,233, + 234,234,234,235,235,236,236,236,237,237,238,238,238,239,239,240, + 240,240,241,241,241,242,242,242,243,243,243,244,244,244,245,245, + 245,246,246,246,246,247,247,247,248,248,248,248,249,249,249,249, + 250,250,250,250,250,251,251,251,251,251,252,252,252,252,252,252, + 253,253,253,253,253,253,253,254,254,254,254,254,254,254,254,254, + 254,254,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,254, + 254,254,254,254,254,254,254,254,254,254,253,253,253,253,253,253, + 253,252,252,252,252,252,252,251,251,251,251,251,250,250,250,250, + 250,249,249,249,249,248,248,248,248,247,247,247,246,246,246,246, + 245,245,245,244,244,244,243,243,243,242,242,242,241,241,241,240, + 240,240,239,239,238,238,238,237,237,236,236,236,235,235,234,234, + 234,233,233,232,232,231,231,230,230,229,229,228,228,228,227,227, + 226,226,225,225,224,224,223,222,222,221,221,220,220,219,219,218, + 218,217,217,216,215,215,214,214,213,213,212,211,211,210,210,209, + 208,208,207,207,206,205,205,204,203,203,202,202,201,200,200,199, + 198,198,197,196,196,195,194,194,193,192,192,191,190,190,189,188, + 188,187,186,186,185,184,183,183,182,181,181,180,179,178,178,177, + 176,176,175,174,173,173,172,171,170,170,169,168,167,167,166,165, + 165,164,163,162,162,161,160,159,158,158,157,156,155,155,154,153, + 152,152,151,150,149,149,148,147,146,145,145,144,143,142,142,141, + 140,139,138,138,137,136,135,135,134,133,132,131,131,130,129,128, + 128,127,126,125,124,124,123,122,121,120,120,119,118,117,117,116, + 115,114,113,113,112,111,110,110,109,108,107,106,106,105,104,103, + 103,102,101,100,100,99,98,97,97,96,95,94,93,93,92,91, + 90,90,89,88,88,87,86,85,85,84,83,82,82,81,80,79, + 79,78,77,77,76,75,74,74,73,72,72,71,70,69,69,68, + 67,67,66,65,65,64,63,63,62,61,61,60,59,59,58,57, + 57,56,55,55,54,53,53,52,52,51,50,50,49,48,48,47, + 47,46,45,45,44,44,43,42,42,41,41,40,40,39,38,38, + 37,37,36,36,35,35,34,34,33,33,32,31,31,30,30,29, + 29,28,28,27,27,27,26,26,25,25,24,24,23,23,22,22, + 21,21,21,20,20,19,19,19,18,18,17,17,17,16,16,15, + 15,15,14,14,14,13,13,13,12,12,12,11,11,11,10,10, + 10,9,9,9,9,8,8,8,7,7,7,7,6,6,6,6, + 5,5,5,5,5,4,4,4,4,4,3,3,3,3,3,3, + 2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1, + 1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, + 1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2, + 2,3,3,3,3,3,3,4,4,4,4,4,5,5,5,5, + 5,6,6,6,6,7,7,7,7,8,8,8,9,9,9,9, + 10,10,10,11,11,11,12,12,12,13,13,13,14,14,14,15, + 15,15,16,16,17,17,17,18,18,19,19,19,20,20,21,21, + 21,22,22,23,23,24,24,25,25,26,26,27,27,27,28,28, + 29,29,30,30,31,31,32,33,33,34,34,35,35,36,36,37, + 37,38,38,39,40,40,41,41,42,42,43,44,44,45,45,46, + 47,47,48,48,49,50,50,51,52,52,53,53,54,55,55,56, + 57,57,58,59,59,60,61,61,62,63,63,64,65,65,66,67, + 67,68,69,69,70,71,72,72,73,74,74,75,76,77,77,78, + 79,79,80,81,82,82,83,84,85,85,86,87,88,88,89,90, + 90,91,92,93,93,94,95,96,97,97,98,99,100,100,101,102, + 103,103,104,105,106,106,107,108,109,110,110,111,112,113,113,114, + 115,116,117,117,118,119,120,120,121,122,123,124,124,125,126,127 +}; +#else +#define ACCUMULATOR_BIT_SHIFT (ACCUMULATOR_BITS-8) +static const uint8_t ddsSineTable[256] PROGMEM = { + 128,131,134,137,140,143,146,149,152,155,158,162,165,167,170,173, + 176,179,182,185,188,190,193,196,198,201,203,206,208,211,213,215, + 218,220,222,224,226,228,230,232,234,235,237,238,240,241,243,244, + 245,246,248,249,250,250,251,252,253,253,254,254,254,255,255,255, + 255,255,255,255,254,254,254,253,253,252,251,250,250,249,248,246, + 245,244,243,241,240,238,237,235,234,232,230,228,226,224,222,220, + 218,215,213,211,208,206,203,201,198,196,193,190,188,185,182,179, + 176,173,170,167,165,162,158,155,152,149,146,143,140,137,134,131, + 128,124,121,118,115,112,109,106,103,100,97,93,90,88,85,82, + 79,76,73,70,67,65,62,59,57,54,52,49,47,44,42,40, + 37,35,33,31,29,27,25,23,21,20,18,17,15,14,12,11, + 10,9,7,6,5,5,4,3,2,2,1,1,1,0,0,0, + 0,0,0,0,1,1,1,2,2,3,4,5,5,6,7,9, + 10,11,12,14,15,17,18,20,21,23,25,27,29,31,33,35, + 37,40,42,44,47,49,52,54,57,59,62,65,67,70,73,76, + 79,82,85,88,90,93,97,100,103,106,109,112,115,118,121,124 +}; +#endif /* DDS_TABLE_LARGE */ + +class DDS { +public: + DDS(): refclk(DDS_REFCLK_DEAULT), accumulator(0), running(false) {}; + + void start(); + const bool isRunning() { return running; }; + void stop(); + + // Start the PWM output, or stop it + void on() { + running = true; + } + // Provide a duration in ms for the tone + void on(unsigned short duration) { + // Duration in ticks from milliseconds is: + // t = (1/refclk) + tickDuration = duration / (1000.0/refclk); + running = true; + } + void off() { + running = false; + } + + // Generate a tone for a specific amount of time + void play(unsigned short freq, unsigned short duration) { + setFrequency(freq); + on(duration); + } + // Blocking version + void playWait(unsigned short freq, unsigned short duration) { + setFrequency(freq); + on(duration); + delay(duration * 1000); + } + + // Our maximum clock isn't very high, so our highest + // frequency supported will fit in a short. + void setFrequency(unsigned short freq); + + // Adjustable reference clock + void setReferenceClock(unsigned long ref); + + uint8_t getPhaseAngle(); + + void clockTick(); + +private: + bool running; + unsigned long tickDuration; +#ifdef SHORT_ACCUMULATOR + unsigned short accumulator; + unsigned short stepRate; + unsigned short refclk; +#else + unsigned long accumulator; + unsigned long stepRate; + unsigned long refclk; +#endif + static DDS *sDDS; +}; + +#endif /* _DDS_H_ */