From fdaa6024013719f4ca636bb04e301acc7277f662 Mon Sep 17 00:00:00 2001 From: Jake-B Date: Sun, 16 Aug 2015 20:17:36 -0400 Subject: [PATCH 1/3] Very rough draft of DDS supporting the Arduio Zero and it's built-in DAC. --- DDS.cpp | 174 +++++++++++++++++++++++++++++++++++++++++--------------- DDS.h | 33 +++++++++-- 2 files changed, 154 insertions(+), 53 deletions(-) diff --git a/DDS.cpp b/DDS.cpp index 8772fc7..e025993 100644 --- a/DDS.cpp +++ b/DDS.cpp @@ -1,41 +1,95 @@ #include #include "DDS.h" +#ifdef __SAMD21G18A__ + +// The SimpleAudioPlayerZero sample project found at: +// https://www.arduino.cc/en/Tutorial/SimpleAudioPlayerZero +// is an execellent reference for setting up the Timer/Counter +#define TC_ISBUSY() (TC5->COUNT16.STATUS.reg & TC_STATUS_SYNCBUSY) +#define TC_WAIT() while (TC_ISBUSY()); +#define TC_ENABLE() TC5->COUNT16.CTRLA.reg |= TC_CTRLA_ENABLE; TC_WAIT(); +#define TC_RESET() TC5->COUNT16.CTRLA.reg = TC_CTRLA_SWRST; TC_WAIT(); \ + while (TC5->COUNT16.CTRLA.bit.SWRST); +#define TC_DISABLE() TC5->COUNT16.CTRLA.reg &= ~TC_CTRLA_ENABLE; TC_WAIT(); + +#endif + // 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() { + +#ifdef DDS_DEBUG_SERIAL + // Print these debug statements (commont to both the AVR and the SAMD21) + Serial.print(F("DDS SysClk: ")); + Serial.println(F_CPU/8); + Serial.print(F("DDS RefClk: ")); + Serial.println(refclk, DEC); +#endif + +#ifdef __SAMD21G18A__ + // Enable the Generic Clock Generator 0 and configure for TC4 and TC5. + // We only need TC5, but they are configured together + GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID(GCM_TC4_TC5)) ; + while (GCLK->STATUS.bit.SYNCBUSY); + + TC_RESET(); + + // Set TC5 16 bit + TC5->COUNT16.CTRLA.reg |= TC_CTRLA_MODE_COUNT16; + + // Set TC5 mode as match frequency + TC5->COUNT16.CTRLA.reg |= TC_CTRLA_WAVEGEN_MFRQ; + TC5->COUNT16.CTRLA.reg |= TC_CTRLA_PRESCALER_DIV1 | TC_CTRLA_ENABLE; + TC5->COUNT16.CC[0].reg = (uint16_t) (SystemCoreClock / DDS_REFCLK_DEFAULT - 1); + TC_WAIT() + + // Configure interrupt + NVIC_DisableIRQ(TC5_IRQn); + NVIC_ClearPendingIRQ(TC5_IRQn); + NVIC_SetPriority(TC5_IRQn, 0); + NVIC_EnableIRQ(TC5_IRQn); + + // Enable TC5 Interrupt + TC5->COUNT16.INTENSET.bit.MC0 = 1; + TC_WAIT(); + + //Configure the DAC + analogWriteResolution(8); + analogWrite(A0, 0); +#else // 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 -#ifdef DDS_PWM_PIN_3 - TCCR2A = (TCCR2A | _BV(COM2B1)) & ~(_BV(COM2B0) | _BV(COM2A1) | _BV(COM2A0)) | + #ifdef DDS_PWM_PIN_3 + TCCR2A = (TCCR2A | _BV(COM2B1)) & ~(_BV(COM2B0) | _BV(COM2A1) | _BV(COM2A0)) | _BV(WGM21) | _BV(WGM20); - TCCR2B = (TCCR2B & ~(_BV(CS22) | _BV(CS21))) | _BV(CS20) | _BV(WGM22); -#else + TCCR2B = (TCCR2B & ~(_BV(CS22) | _BV(CS21))) | _BV(CS20) | _BV(WGM22); + #else // Alternatively, use pin 11 // Enable output compare on OC2A, toggle mode - TCCR2A = _BV(COM2A1) | _BV(WGM21) | _BV(WGM20); - //TCCR2A = (TCCR2A | _BV(COM2A1)) & ~(_BV(COM2A0) | _BV(COM2B1) | _BV(COM2B0)) | - // _BV(WGM21) | _BV(WGM20); - TCCR2B = _BV(CS20); -#endif + TCCR2A = _BV(COM2A1) | _BV(WGM21) | _BV(WGM20); + //TCCR2A = (TCCR2A | _BV(COM2A1)) & ~(_BV(COM2A0) | _BV(COM2B1) | _BV(COM2B0)) | + // _BV(WGM21) | _BV(WGM20); + TCCR2B = _BV(CS20); + #endif // 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. -#ifdef DDS_PWM_PIN_3 - OCR2A = pow(2,COMPARATOR_BITS)-1; - OCR2B = 0; -#else - OCR2A = 0; -#endif + #ifdef DDS_PWM_PIN_3 + OCR2A = pow(2,COMPARATOR_BITS)-1; + OCR2B = 0; + #else + OCR2A = 0; + #endif -#ifdef DDS_USE_ONLY_TIMER2 - TIMSK2 |= _BV(TOIE2); -#endif + #ifdef DDS_USE_ONLY_TIMER2 + TIMSK2 |= _BV(TOIE2); + #endif // Second, setup Timer1 to trigger the ADC interrupt // This lets us use decoding functions that run at the same reference @@ -43,15 +97,11 @@ void DDS::start() { // We use ICR1 as TOP and prescale by 8 TCCR1B = _BV(CS10) | _BV(WGM13) | _BV(WGM12); TCCR1A = 0; - ICR1 = ((F_CPU / 1) / refclk) - 1; -#ifdef DDS_DEBUG_SERIAL - Serial.print(F("DDS SysClk: ")); - Serial.println(F_CPU/8); - Serial.print(F("DDS RefClk: ")); - Serial.println(refclk, DEC); - Serial.print(F("DDS ICR1: ")); - Serial.println(ICR1, DEC); -#endif + ICR1 = ((F_CPU / 1) / refclk) - 1; + #ifdef DDS_DEBUG_SERIAL + Serial.print(F("DDS ICR1: ")); + Serial.println(ICR1, DEC); + #endif // 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) @@ -60,14 +110,22 @@ void DDS::start() { DIDR0 |= _BV(0); ADCSRB = _BV(ADTS2) | _BV(ADTS1) | _BV(ADTS0); ADCSRA = _BV(ADEN) | _BV(ADSC) | _BV(ADATE) | _BV(ADIE) | _BV(ADPS2); // | _BV(ADPS0); + +#endif } void DDS::stop() { +#ifdef __SAMD21G18A__ + TC_DISABLE(); + TC_RESET(); + analogWrite(A0, 0); +#else // TODO: Stop the timers. -#ifndef DDS_USE_ONLY_TIMER2 - TCCR1B = 0; + #ifndef DDS_USE_ONLY_TIMER2 + TCCR1B = 0; + #endif + TCCR2B = 0; #endif - TCCR2B = 0; } // Set our current sine wave frequency in Hz @@ -89,6 +147,7 @@ ddsAccumulator_t DDS::calcFrequency(unsigned short freq) { newStep = (600.0 / (DDS_REFCLK_DEFAULT+DDS_REFCLK_OFFSET)) * pow(2,ACCUMULATOR_BITS); } } else { + //TODO: This doesn't work with the SAM21D... yet newStep = pow(2,ACCUMULATOR_BITS)*freq / (refclk+refclkOffset); } return newStep; @@ -112,45 +171,65 @@ void DDS::clockTick() { if(running) { accumulator += stepRate; if(timeLimited && tickDuration == 0) { -#ifndef DDS_PWM_PIN_3 - OCR2A = 0; +#ifdef __SAMD21G18A__ + #ifdef DDS_IDLE_HIGH + analogWrite(A0, pow(2,COMPARATOR_BITS)/2); + #else + analogWrite(A0, 0); + #endif #else -#ifdef DDS_IDLE_HIGH + #ifndef DDS_PWM_PIN_3 + OCR2A = 0; + #else + #ifdef DDS_IDLE_HIGH // Set the duty cycle to 50% OCR2B = pow(2,COMPARATOR_BITS)/2; -#else + #else // Set duty cycle to 0, effectively off - OCR2B = 0; -#endif + OCR2B = 0; + #endif + #endif #endif running = false; accumulator = 0; } else { -#ifdef DDS_PWM_PIN_3 - OCR2B = getDutyCycle(); +#ifdef __SAMD21G18A__ + analogWrite(A0, getDutyCycle()); #else + #ifdef DDS_PWM_PIN_3 + OCR2B = getDutyCycle(); + #else OCR2A = getDutyCycle(); + #endif #endif } // Reduce our playback duration by one tick tickDuration--; } else { +#ifdef __SAMD21G18A__ + #ifdef DDS_IDLE_HIGH + analogWrite(A0, pow(2,COMPARATOR_BITS)/2); + #else + analogWrite(A0, 0); + #endif +#else // Hold it low -#ifndef DDS_PWM_PIN_3 + #ifndef DDS_PWM_PIN_3 OCR2A = 0; -#else -#ifdef DDS_IDLE_HIGH + #else + #ifdef DDS_IDLE_HIGH // Set the duty cycle to 50% - OCR2B = pow(2,COMPARATOR_BITS)/2; -#else - // Set duty cycle to 0, effectively off - OCR2B = 0; -#endif + OCR2B = pow(2,COMPARATOR_BITS)/2; + #else + // Set duty cycle to 0, effectively off + OCR2B = 0; + #endif + #endif #endif } } -uint8_t DDS::getDutyCycle() { +ddsComparitor_t DDS::getDutyCycle() { #if ACCUMULATOR_BIT_SHIFT >= 24 uint16_t phAng; #else @@ -173,3 +252,4 @@ uint8_t DDS::getDutyCycle() { scaled += 128>>(8-COMPARATOR_BITS); return scaled; } + diff --git a/DDS.h b/DDS.h index 57cf40b..1498b9d 100644 --- a/DDS.h +++ b/DDS.h @@ -1,12 +1,19 @@ #ifndef _DDS_H_ #define _DDS_H_ - +#include #include +// Just a little reminder +#ifndef __SAMD21G18A__ +#warning Experimental support for ArduinoZero. Not yet complete +#endif + // Use pin 3 for PWM? If not defined, use pin 11 // Quality on pin 3 is higher than on 11, as it can be clocked faster // when the COMPARATOR_BITS value is less than 8 +#ifndef __SAMD21G18A__ #define DDS_PWM_PIN_3 +#endif // Normally, we turn on timer2 and timer1, and have ADC sampling as our clock // Define this to only use Timer2, and not start the ADC clock @@ -14,7 +21,9 @@ // Use a short (16 bit) accumulator. Phase accuracy is reduced, but speed // is increased, along with a reduction in memory use. +#ifndef __SAMD21G18A__ #define SHORT_ACCUMULATOR +#endif #ifdef SHORT_ACCUMULATOR #define ACCUMULATOR_BITS 16 @@ -35,10 +44,17 @@ typedef uint32_t ddsAccumulator_t; // 8 = 62.5kHz PWM // 7 = 125kHz PWM // 6 = 250kHz PWM -#ifdef DDS_PWM_PIN_3 +#ifdef __SAMD21G18A__ +//TODO: 10 bit resolution for the Zero's DAC. +//Doesn't work just yet, so keep 8-bit for now. +#define COMPARATOR_BITS 8 +typedef uint8_t ddsComparitor_t; +#elif defined(DDS_PWM_PIN_3) #define COMPARATOR_BITS 6 +typedef uint8_t ddsComparitor_t; #else // When using pin 11, we always want 8 bits #define COMPARATOR_BITS 8 +typedef uint8_t ddsComparitor_t; #endif // This is how often we'll perform a phase advance, as well as ADC sampling @@ -46,8 +62,13 @@ typedef uint32_t ddsAccumulator_t; // expense of CPU time. It maxes out around 62000 (TBD) // May be overridden in the sketch to improve performance #ifndef DDS_REFCLK_DEFAULT -#define DDS_REFCLK_DEFAULT 9600 + #ifdef __SAMD21G18A__ + #define DDS_REFCLK_DEFAULT 44100 + #else + #define DDS_REFCLK_DEFAULT 9600 + #endif #endif + // As each Arduino crystal is a little different, this can be fine tuned to // provide more accurate frequencies. Adjustments in the range of hundreds // is a good start. @@ -61,7 +82,7 @@ typedef uint32_t ddsAccumulator_t; #endif // Output some of the calculations and information about the DDS over serial -//#define DDS_DEBUG_SERIAL +#define DDS_DEBUG_SERIAL // When defined, use the 1024 element sine lookup table. This improves phase // accuracy, at the cost of more flash and CPU requirements. @@ -202,7 +223,7 @@ class DDS { return refclkOffset; } - uint8_t getDutyCycle(); + ddsComparitor_t getDutyCycle(); // Set a scaling factor. To keep things quick, this is a power of 2 value. // Set it with 0 for lowest (which will be off), 8 is highest. @@ -222,7 +243,7 @@ class DDS { volatile ddsAccumulator_t stepRate; ddsAccumulator_t refclk; int16_t refclkOffset; - static DDS *sDDS; + //static _DDS *sDDS; }; #endif /* _DDS_H_ */ From a581404e0467cb0ba2facbb09cf5e97eb17c7a7c Mon Sep 17 00:00:00 2001 From: Jake-B Date: Mon, 17 Aug 2015 17:26:33 -0400 Subject: [PATCH 2/3] - Fixed a few problems with Arduino Zero support - Added a 10-bit sine table, probably wasteful considering its stored in 16-bit integers. - Added "DDS_MAX_COMPARATOR" and "DDS_MAX_ACCUMULATOR" macros to hide some math and imrpove readability. - Simplified calcFrequency and made it work for arbirary frequencies (with/without default refclk) - Simplified getDutyCycle(). I think its more understandable now, and works for various combinations of accumulator size and lookup table size. Tested on both an Arduino Zero and an Arduino UNO. Seems to work, but YMMV. --- DDS.cpp | 94 +++++++++++++++++++++++------------ DDS.h | 150 ++++++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 172 insertions(+), 72 deletions(-) diff --git a/DDS.cpp b/DDS.cpp index e025993..32a6d8d 100644 --- a/DDS.cpp +++ b/DDS.cpp @@ -24,10 +24,12 @@ void DDS::start() { Serial.print(F("DDS SysClk: ")); Serial.println(F_CPU/8); Serial.print(F("DDS RefClk: ")); - Serial.println(refclk, DEC); + Serial.println(refclk, DEC); #endif #ifdef __SAMD21G18A__ + // SAMD21, like Ardino Zero + // Enable the Generic Clock Generator 0 and configure for TC4 and TC5. // We only need TC5, but they are configured together GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID(GCM_TC4_TC5)) ; @@ -55,9 +57,20 @@ void DDS::start() { TC_WAIT(); //Configure the DAC +#if COMPARATOR_BITS==6 + // Not sure why you'd use 6-bit, other than debugging. + // Reduced amplitude: clockTick() and getDutyCycle() do not scale the 6 up to 8-bits analogWriteResolution(8); +#elif COMPARATOR_BITS == 8 || COMPARATOR_BITS == 10 + analogWriteResolution(COMPARATOR_BITS); +#else +#error Unsupported resoltuion for DAC (expected 8 or 10) +#endif analogWrite(A0, 0); + #else + // For AVRs + // Use the clkIO clock rate ASSR &= ~(_BV(EXCLK) | _BV(AS2)); @@ -81,7 +94,7 @@ void DDS::start() { // Setting Comparator Bits smaller will allow for higher frequency PWM, // with the loss of resolution. #ifdef DDS_PWM_PIN_3 - OCR2A = pow(2,COMPARATOR_BITS)-1; + OCR2A = DDS_MAX_COMPARATOR-1; OCR2B = 0; #else OCR2A = 0; @@ -132,33 +145,32 @@ void DDS::stop() { ddsAccumulator_t DDS::calcFrequency(unsigned short freq) { // Fo = (M*Fc)/2^N // M = (Fo/Fc)*2^N - ddsAccumulator_t newStep; + if(refclk == DDS_REFCLK_DEFAULT) { // Try to use precalculated values if possible if(freq == 2200) { - newStep = (2200.0 / (DDS_REFCLK_DEFAULT+DDS_REFCLK_OFFSET)) * pow(2,ACCUMULATOR_BITS); + return (2200.0 / (DDS_REFCLK_DEFAULT+DDS_REFCLK_OFFSET)) * DDS_MAX_ACCUMULATOR; } else if (freq == 1200) { - newStep = (1200.0 / (DDS_REFCLK_DEFAULT+DDS_REFCLK_OFFSET)) * pow(2,ACCUMULATOR_BITS); + return (1200.0 / (DDS_REFCLK_DEFAULT+DDS_REFCLK_OFFSET)) * DDS_MAX_ACCUMULATOR; } else if(freq == 2400) { - newStep = (2400.0 / (DDS_REFCLK_DEFAULT+DDS_REFCLK_OFFSET)) * pow(2,ACCUMULATOR_BITS); + return (2400.0 / (DDS_REFCLK_DEFAULT+DDS_REFCLK_OFFSET)) * DDS_MAX_ACCUMULATOR; } else if (freq == 1500) { - newStep = (1500.0 / (DDS_REFCLK_DEFAULT+DDS_REFCLK_OFFSET)) * pow(2,ACCUMULATOR_BITS); + return (1500.0 / (DDS_REFCLK_DEFAULT+DDS_REFCLK_OFFSET)) * DDS_MAX_ACCUMULATOR; } else if (freq == 600) { - newStep = (600.0 / (DDS_REFCLK_DEFAULT+DDS_REFCLK_OFFSET)) * pow(2,ACCUMULATOR_BITS); + return (600.0 / (DDS_REFCLK_DEFAULT+DDS_REFCLK_OFFSET)) * DDS_MAX_ACCUMULATOR; } - } else { - //TODO: This doesn't work with the SAM21D... yet - newStep = pow(2,ACCUMULATOR_BITS)*freq / (refclk+refclkOffset); - } - return newStep; + } + + // Not a pre-calc frequency OR not using default REFCLK + return DDS_MAX_ACCUMULATOR * freq / (refclk+refclkOffset); } // Degrees should be between -360 and +360 (others don't make much sense) void DDS::setPhaseDeg(int16_t degrees) { - accumulator = degrees * (pow(2,ACCUMULATOR_BITS)/360.0); + accumulator = degrees * (DDS_MAX_ACCUMULATOR/360.0); } void DDS::changePhaseDeg(int16_t degrees) { - accumulator += degrees * (pow(2,ACCUMULATOR_BITS)/360.0); + accumulator += degrees * (DDS_MAX_ACCUMULATOR/360.0); } // TODO: Clean this up a bit.. @@ -173,7 +185,7 @@ void DDS::clockTick() { if(timeLimited && tickDuration == 0) { #ifdef __SAMD21G18A__ #ifdef DDS_IDLE_HIGH - analogWrite(A0, pow(2,COMPARATOR_BITS)/2); + analogWrite(A0, DDS_MAX_COMPARATOR/2); #else analogWrite(A0, 0); #endif @@ -183,7 +195,7 @@ void DDS::clockTick() { #else #ifdef DDS_IDLE_HIGH // Set the duty cycle to 50% - OCR2B = pow(2,COMPARATOR_BITS)/2; + OCR2B = DDS_MAX_COMPARATOR/2; #else // Set duty cycle to 0, effectively off OCR2B = 0; @@ -208,7 +220,7 @@ void DDS::clockTick() { } else { #ifdef __SAMD21G18A__ #ifdef DDS_IDLE_HIGH - analogWrite(A0, pow(2,COMPARATOR_BITS)/2); + analogWrite(A0, DDS_MAX_COMPARATOR/2); #else analogWrite(A0, 0); #endif @@ -219,7 +231,7 @@ void DDS::clockTick() { #else #ifdef DDS_IDLE_HIGH // Set the duty cycle to 50% - OCR2B = pow(2,COMPARATOR_BITS)/2; + OCR2B = DDS_MAX_COMPARATOR/2; #else // Set duty cycle to 0, effectively off OCR2B = 0; @@ -229,27 +241,49 @@ void DDS::clockTick() { } } +//TODO: rename this function as it is more than just dutyCycle? +//now it powers both a PWM dutyCycle as well as a DAC value on the Zero ddsComparitor_t DDS::getDutyCycle() { - #if ACCUMULATOR_BIT_SHIFT >= 24 + #if ACCUMULATOR_BITS - ACCUMULATOR_BIT_SHIFT > 8 + // For larger sineTables which need more thn 8 bits. uint16_t phAng; #else + // For standard sineTables which need 8 bits. uint8_t phAng; #endif + if(amplitude == 0) // Shortcut out on no amplitude - return 128>>(8-COMPARATOR_BITS); + return DDS_MAX_COMPARATOR/2; + + // Lookup the value from the sin table. phAng = (accumulator >> ACCUMULATOR_BIT_SHIFT); - int8_t position = pgm_read_byte_near(ddsSineTable + phAng); //>>(8-COMPARATOR_BITS); - // Apply scaling and return +#if DDS_LOOKUPVALUE_BITS > 8 + // 16-bit lookup + int16_t position = pgm_read_word_near(ddsSineTable + phAng); //>>(8-COMPARATOR_BITS); + int32_t scaled = position; +#else + // 8-bit lookup + int8_t position = pgm_read_byte_near(ddsSineTable + phAng); //>>(8-COMPARATOR_BITS); int16_t scaled = position; - // output = ((duty * amplitude) / 256) + 128 - // This keeps amplitudes centered around 50% duty +#endif + + // If there's an amplitude, scale it if(amplitude != 255) { // Amplitude is reduced, so do the full math - scaled *= amplitude; - scaled >>= 8+(8-COMPARATOR_BITS); - } else { // Otherwise, only shift for the comparator bits - scaled >>= (8-COMPARATOR_BITS); + scaled *= amplitude; // multiply by the amplitude, max 255 or an 8-bit shift + scaled >>= 8; // bring it back 8 bits devide } - scaled += 128>>(8-COMPARATOR_BITS); + + // Scale to the number of bits available +#if COMPARATOR_BITS > DDS_LOOKUPVALUE_BITS + scaled <<= (COMPARATOR_BITS - DDS_LOOKUPVALUE_BITS); +#elif COMPARATOR_BITS < DDS_LOOKUPVALUE_BITS + scaled >>= (DDS_LOOKUPVALUE_BITS-COMPARATOR_BITS); +#else + // COMARATOR_BITS == DDS_LOOKUPVALUE_BITS -- no math needed. +#endif + + //Move the sinewave up 1/2 scale into the positive range. + scaled += DDS_MAX_COMPARATOR/2; return scaled; } diff --git a/DDS.h b/DDS.h index 1498b9d..7eb55b6 100644 --- a/DDS.h +++ b/DDS.h @@ -3,27 +3,25 @@ #include #include -// Just a little reminder +// Just a little (useless?) reminder; #warnings are not displayed in Arduino IDE. #ifndef __SAMD21G18A__ -#warning Experimental support for ArduinoZero. Not yet complete +#warning Experimental support for ArduinoZero. Not yet complete. #endif +// For AVRs: // Use pin 3 for PWM? If not defined, use pin 11 // Quality on pin 3 is higher than on 11, as it can be clocked faster // when the COMPARATOR_BITS value is less than 8 -#ifndef __SAMD21G18A__ -#define DDS_PWM_PIN_3 -#endif +// #define DDS_PWM_PIN_3 +// For AVRs: // Normally, we turn on timer2 and timer1, and have ADC sampling as our clock // Define this to only use Timer2, and not start the ADC clock // #define DDS_USE_ONLY_TIMER2 // Use a short (16 bit) accumulator. Phase accuracy is reduced, but speed -// is increased, along with a reduction in memory use. -#ifndef __SAMD21G18A__ +// is increased, along with a reduction in memory use._ #define SHORT_ACCUMULATOR -#endif #ifdef SHORT_ACCUMULATOR #define ACCUMULATOR_BITS 16 @@ -33,10 +31,15 @@ typedef uint16_t ddsAccumulator_t; typedef uint32_t ddsAccumulator_t; #endif +// The maximum value of the accumulator based on the number of bits +// Macro for code readability. +#define DDS_MAX_ACCUMULATOR (pow(2,ACCUMULATOR_BITS)) + // If defined, the timer will idle at 50% duty cycle // This leaves it floating in the centre of the PWM/DAC voltage range #define DDS_IDLE_HIGH +// For AVRs: // Select how fast the PWM is, at the expense of level accuracy. // A faster PWM rate will make for easier filtering of the output wave, // while a slower one will allow for more accurate voltage level outputs, @@ -44,23 +47,28 @@ typedef uint32_t ddsAccumulator_t; // 8 = 62.5kHz PWM // 7 = 125kHz PWM // 6 = 250kHz PWM +// For SAMD21's -- the resolution of the DAC. + #ifdef __SAMD21G18A__ -//TODO: 10 bit resolution for the Zero's DAC. -//Doesn't work just yet, so keep 8-bit for now. -#define COMPARATOR_BITS 8 -typedef uint8_t ddsComparitor_t; + #define COMPARATOR_BITS 10 + typedef uint16_t ddsComparitor_t; #elif defined(DDS_PWM_PIN_3) -#define COMPARATOR_BITS 6 -typedef uint8_t ddsComparitor_t; + #define COMPARATOR_BITS 6 + typedef uint8_t ddsComparitor_t; #else // When using pin 11, we always want 8 bits -#define COMPARATOR_BITS 8 -typedef uint8_t ddsComparitor_t; + #define COMPARATOR_BITS 8 + typedef uint8_t ddsComparitor_t; #endif +// The maximum value of the comparitor based on the number of bits +// Macro for code readability. +#define DDS_MAX_COMPARATOR pow(2,COMPARATOR_BITS) + // This is how often we'll perform a phase advance, as well as ADC sampling // rate. The higher this value, the smoother the output wave will be, at the -// expense of CPU time. It maxes out around 62000 (TBD) -// May be overridden in the sketch to improve performance +// expense of CPU time. +// For AVRs: It maxes out around 62000 (TBD) May be overridden in the sketch +// to improve performance #ifndef DDS_REFCLK_DEFAULT #ifdef __SAMD21G18A__ #define DDS_REFCLK_DEFAULT 44100 @@ -88,9 +96,71 @@ typedef uint8_t ddsComparitor_t; // accuracy, at the cost of more flash and CPU requirements. // #define DDS_TABLE_LARGE -#ifdef DDS_TABLE_LARGE -// How many bits to keep from the accumulator to look up in this table +// When defined, use a 10-bit table of short integers. This is wasteful of +// flash space as there are 6 bits of each value that are unused. +// May improve resolution. When set, "DDS_TABLE_LARGE" is ignored. +// #define DDS_TABLE_10BIT + +#ifdef DDS_TABLE_10BIT + +// 10-bit Large SineTable, 1024 values +/-511 +// Generated using http://www.meraman.com/htmls/en/sinTable.html #define ACCUMULATOR_BIT_SHIFT (ACCUMULATOR_BITS-10) +#define DDS_LOOKUPVALUE_BITS 10 +static const int16_t ddsSineTable[1024] PROGMEM = {0, 3, 6, 9, 13, 16, 19, 22, 25, 28, 31, 34, 38, 41, 44, 47, 50, 53, + 56, 59, 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, 94, 97, 100, 103, 106, 109, 112, 115, 118, 121, 124, 127, 130, 133, 136, + 139, 142, 145, 148, 151, 154, 157, 160, 163, 166, 169, 172, 175, 178, 181, 184, 187, 190, 193, 196, 198, 201, 204, 207, + 210, 213, 216, 218, 221, 224, 227, 230, 233, 235, 238, 241, 244, 246, 249, 252, 255, 257, 260, 263, 265, 268, 271, 273, + 276, 279, 281, 284, 286, 289, 292, 294, 297, 299, 302, 304, 307, 309, 312, 314, 317, 319, 322, 324, 327, 329, 331, 334, + 336, 338, 341, 343, 345, 348, 350, 352, 355, 357, 359, 361, 364, 366, 368, 370, 372, 374, 377, 379, 381, 383, 385, 387, + 389, 391, 393, 395, 397, 399, 401, 403, 405, 407, 409, 410, 412, 414, 416, 418, 420, 421, 423, 425, 427, 428, 430, 432, + 433, 435, 437, 438, 440, 441, 443, 445, 446, 448, 449, 451, 452, 454, 455, 456, 458, 459, 461, 462, 463, 465, 466, 467, + 468, 470, 471, 472, 473, 474, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 492, + 493, 494, 495, 496, 496, 497, 498, 499, 499, 500, 501, 501, 502, 502, 503, 503, 504, 505, 505, 505, 506, 506, 507, 507, + 508, 508, 508, 509, 509, 509, 509, 510, 510, 510, 510, 510, 511, 511, 511, 511, 511, 511, 511, 511, 511, 511, 511, 511, + 511, 511, 511, 510, 510, 510, 510, 510, 509, 509, 509, 509, 508, 508, 508, 507, 507, 506, 506, 505, 505, 505, 504, 503, + 503, 502, 502, 501, 501, 500, 499, 499, 498, 497, 496, 496, 495, 494, 493, 492, 492, 491, 490, 489, 488, 487, 486, 485, + 484, 483, 482, 481, 480, 479, 478, 477, 476, 474, 473, 472, 471, 470, 468, 467, 466, 465, 463, 462, 461, 459, 458, 456, + 455, 454, 452, 451, 449, 448, 446, 445, 443, 441, 440, 438, 437, 435, 433, 432, 430, 428, 427, 425, 423, 421, 420, 418, + 416, 414, 412, 410, 409, 407, 405, 403, 401, 399, 397, 395, 393, 391, 389, 387, 385, 383, 381, 379, 377, 374, 372, 370, + 368, 366, 364, 361, 359, 357, 355, 352, 350, 348, 345, 343, 341, 338, 336, 334, 331, 329, 327, 324, 322, 319, 317, 314, + 312, 309, 307, 304, 302, 299, 297, 294, 292, 289, 286, 284, 281, 279, 276, 273, 271, 268, 265, 263, 260, 257, 255, 252, + 249, 246, 244, 241, 238, 235, 233, 230, 227, 224, 221, 218, 216, 213, 210, 207, 204, 201, 198, 196, 193, 190, 187, 184, + 181, 178, 175, 172, 169, 166, 163, 160, 157, 154, 151, 148, 145, 142, 139, 136, 133, 130, 127, 124, 121, 118, 115, 112, + 109, 106, 103, 100, 97, 94, 90, 87, 84, 81, 78, 75, 72, 69, 66, 63, 59, 56, 53, 50, 47, 44, 41, 38, 34, 31, 28, 25, 22, + 19, 16, 13, 9, 6, 3, 0, -3, -6, -9, -13, -16, -19, -22, -25, -28, -31, -34, -38, -41, -44, -47, -50, -53, -56, -59, -63, + -66, -69, -72, -75, -78, -81, -84, -87, -90, -94, -97, -100, -103, -106, -109, -112, -115, -118, -121, -124, -127, -130, + -133, -136, -139, -142, -145, -148, -151, -154, -157, -160, -163, -166, -169, -172, -175, -178, -181, -184, -187, -190, + -193, -196, -198, -201, -204, -207, -210, -213, -216, -218, -221, -224, -227, -230, -233, -235, -238, -241, -244, -246, + -249, -252, -255, -257, -260, -263, -265, -268, -271, -273, -276, -279, -281, -284, -286, -289, -292, -294, -297, -299, + -302, -304, -307, -309, -312, -314, -317, -319, -322, -324, -327, -329, -331, -334, -336, -338, -341, -343, -345, -348, + -350, -352, -355, -357, -359, -361, -364, -366, -368, -370, -372, -374, -377, -379, -381, -383, -385, -387, -389, -391, + -393, -395, -397, -399, -401, -403, -405, -407, -409, -410, -412, -414, -416, -418, -420, -421, -423, -425, -427, -428, + -430, -432, -433, -435, -437, -438, -440, -441, -443, -445, -446, -448, -449, -451, -452, -454, -455, -456, -458, -459, + -461, -462, -463, -465, -466, -467, -468, -470, -471, -472, -473, -474, -476, -477, -478, -479, -480, -481, -482, -483, + -484, -485, -486, -487, -488, -489, -490, -491, -492, -492, -493, -494, -495, -496, -496, -497, -498, -499, -499, -500, + -501, -501, -502, -502, -503, -503, -504, -505, -505, -505, -506, -506, -507, -507, -508, -508, -508, -509, -509, -509, + -509, -510, -510, -510, -510, -510, -511, -511, -511, -511, -511, -511, -511, -511, -511, -511, -511, -511, -511, -511, + -511, -510, -510, -510, -510, -510, -509, -509, -509, -509, -508, -508, -508, -507, -507, -506, -506, -505, -505, -505, + -504, -503, -503, -502, -502, -501, -501, -500, -499, -499, -498, -497, -496, -496, -495, -494, -493, -492, -492, -491, + -490, -489, -488, -487, -486, -485, -484, -483, -482, -481, -480, -479, -478, -477, -476, -474, -473, -472, -471, -470, + -468, -467, -466, -465, -463, -462, -461, -459, -458, -456, -455, -454, -452, -451, -449, -448, -446, -445, -443, -441, + -440, -438, -437, -435, -433, -432, -430, -428, -427, -425, -423, -421, -420, -418, -416, -414, -412, -410, -409, -407, + -405, -403, -401, -399, -397, -395, -393, -391, -389, -387, -385, -383, -381, -379, -377, -374, -372, -370, -368, -366, + -364, -361, -359, -357, -355, -352, -350, -348, -345, -343, -341, -338, -336, -334, -331, -329, -327, -324, -322, -319, + -317, -314, -312, -309, -307, -304, -302, -299, -297, -294, -292, -289, -286, -284, -281, -279, -276, -273, -271, -268, + -265, -263, -260, -257, -255, -252, -249, -246, -244, -241, -238, -235, -233, -230, -227, -224, -221, -218, -216, -213, + -210, -207, -204, -201, -198, -196, -193, -190, -187, -184, -181, -178, -175, -172, -169, -166, -163, -160, -157, -154, + -151, -148, -145, -142, -139, -136, -133, -130, -127, -124, -121, -118, -115, -112, -109, -106, -103, -100, -97, -94, + -90, -87, -84, -81, -78, -75, -72, -69, -66, -63, -59, -56, -53, -50, -47, -44, -41, -38, -34, -31, -28, -25, -22, -19, + -16, -13, -9, -6, -3 +}; + +#elif defined(DDS_TABLE_LARGE) + +// Large SineTable, 1024 values +/-127 +#define ACCUMULATOR_BIT_SHIFT (ACCUMULATOR_BITS-10) // How many bits to keep from the accumulator to look up in this table +#define DDS_LOOKUPVALUE_BITS 8 static const int8_t ddsSineTable[1024] PROGMEM = { 0, 0, 1, 2, 3, 3, 4, 5, 6, 7, 7, 8, 9, 10, 10, 11, 12, 13, 13, 14, 15, 16, 17, 17, 18, 19, 20, 20, 21, 22, 23, 24, 24, 25, 26, 27, 27, 28, 29, 30, 30, 31, 32, 33, 33, 34, 35, 36, 36, 37, 38, 39, 39, 40, 41, 42, 42, 43, 44, 44, 45, 46, 47, 47, @@ -125,30 +195,26 @@ static const int8_t ddsSineTable[1024] PROGMEM = { -48, -47, -47, -46, -45, -44, -44, -43, -42, -42, -41, -40, -39, -39, -38, -37, -36, -36, -35, -34, -33, -33, -32, -31, -30, -30, -29, -28, -27, -27, -26, -25, -24, -24, -23, -22, -21, -20, -20, -19, -18, -17, -17, -16, -15, -14, -13, -13, -12, -11, -10, -10, -9, -8, -7, -7, -6, -5, -4, -3, -3, -2, -1, 0 }; + #else + +// Standard SineTable, 256 values +/-127 #define ACCUMULATOR_BIT_SHIFT (ACCUMULATOR_BITS-8) -static const int8_t ddsSineTable[256] PROGMEM = { - 0, 3, 6, 9, 12, 16, 19, 22, 25, 28, 31, 34, 37, 40, 43, 46, 49, - 51, 54, 57, 60, 63, 65, 68, 71, 73, 76, 78, 81, 83, 85, 88, 90, - 92, 94, 96, 98, 100, 102, 104, 106, 107, 109, 111, 112, 113, 115, - 116, 117, 118, 120, 121, 122, 122, 123, 124, 125, 125, 126, 126, - 126, 127, 127, 127, 127, 127, 127, 127, 126, 126, 126, 125, 125, - 124, 123, 122, 122, 121, 120, 118, 117, 116, 115, 113, 112, 111, - 109, 107, 106, 104, 102, 100, 98, 96, 94, 92, 90, 88, 85, 83, 81, - 78, 76, 73, 71, 68, 65, 63, 60, 57, 54, 51, 49, 46, 43, 40, 37, - 34, 31, 28, 25, 22, 19, 16, 12, 9, 6, 3, 0, -3, -6, -9, -12, -16, - -19, -22, -25, -28, -31, -34, -37, -40, -43, -46, -49, -51, -54, - -57, -60, -63, -65, -68, -71, -73, -76, -78, -81, -83, -85, -88, - -90, -92, -94, -96, -98, -100, -102, -104, -106, -107, -109, -111, - -112, -113, -115, -116, -117, -118, -120, -121, -122, -122, -123, - -124, -125, -125, -126, -126, -126, -127, -127, -127, -127, -127, - -127, -127, -126, -126, -126, -125, -125, -124, -123, -122, -122, - -121, -120, -118, -117, -116, -115, -113, -112, -111, -109, -107, - -106, -104, -102, -100, -98, -96, -94, -92, -90, -88, -85, -83, - -81, -78, -76, -73, -71, -68, -65, -63, -60, -57, -54, -51, -49, - -46, -43, -40, -37, -34, -31, -28, -25, -22, -19, -16, -12, -9, -6, -3 - }; -#endif /* DDS_TABLE_LARGE */ +#define DDS_LOOKUPVALUE_BITS 8 +static const int8_t ddsSineTable[256] PROGMEM = {0, 3, 6, 9, 12, 16, 19, 22, 25, 28, 31, 34, 37, 40, 43, 46, 49, 51, 54, + 57, 60, 63, 65, 68, 71, 73, 76, 78, 81, 83, 85, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 107, 109, 111, 112, 113, + 115, 116, 117, 118, 120, 121, 122, 122, 123, 124, 125, 125, 126, 126, 126, 127, 127, 127, 127, 127, 127, 127, 126, 126, + 126, 125, 125, 124, 123, 122, 122, 121, 120, 118, 117, 116, 115, 113, 112, 111, 109, 107, 106, 104, 102, 100, 98, 96, + 94, 92, 90, 88, 85, 83, 81, 78, 76, 73, 71, 68, 65, 63, 60, 57, 54, 51, 49, 46, 43, 40, 37, 34, 31, 28, 25, 22, 19, 16, + 12, 9, 6, 3, 0, -3, -6, -9, -12, -16, -19, -22, -25, -28, -31, -34, -37, -40, -43, -46, -49, -51, -54, -57, -60, -63, + -65, -68, -71, -73, -76, -78, -81, -83, -85, -88, -90, -92, -94, -96, -98, -100, -102, -104, -106, -107, -109, -111, + -112, -113, -115, -116, -117, -118, -120, -121, -122, -122, -123, -124, -125, -125, -126, -126, -126, -127, -127, -127, + -127, -127, -127, -127, -126, -126, -126, -125, -125, -124, -123, -122, -122, -121, -120, -118, -117, -116, -115, -113, + -112, -111, -109, -107, -106, -104, -102, -100, -98, -96, -94, -92, -90, -88, -85, -83, -81, -78, -76, -73, -71, -68, + -65, -63, -60, -57, -54, -51, -49, -46, -43, -40, -37, -34, -31, -28, -25, -22, -19, -16, -12, -9, -6, -3 +}; + +#endif /* DDS_TABLE_LARGE, DDS_TABLE_10BIT */ class DDS { public: From 11f5e8b7336d09c89dec6b604908f0329f6a8f91 Mon Sep 17 00:00:00 2001 From: Jake-B Date: Tue, 18 Aug 2015 08:44:11 -0400 Subject: [PATCH 3/3] Updated examples/DDS/DDS.ino to include the ISRs for the Arduino Zero. NOTE: this sample won't really work for the HamShield ont he Ardiuno Zero as it will Besides the logic voltage issue, it will be playing its tones out of AO, rather than D3 or D11. Hook up your oscilloscope to A0 to see the waveform. --- examples/DDS/DDS.ino | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/examples/DDS/DDS.ino b/examples/DDS/DDS.ino index 78ca76c..beae103 100644 --- a/examples/DDS/DDS.ino +++ b/examples/DDS/DDS.ino @@ -2,6 +2,12 @@ #include #include +#ifdef __SAMD21G18A__ +// NOTE: this won't really work for the Ardiuno Zero, as it will be playing its tones +// out of AO, rather than D3 or D11. Hook up your oscilloscope to A0 to see the waveform. +#warning Hamshield may not be compatible with the Arduino Zero. +#endif + HamShield radio; DDS dds; @@ -28,6 +34,31 @@ void loop() { delay(1000); } +#ifdef __SAMD21G18A__ + +#ifdef __cplusplus +extern "C" { +#endif + +// ISR for the Arduino Zero Timer/Counter 5 +// DDS configures this to run at 44100 (default) +void DDS_Handler (void) { + // Do the thing + dds.clockTick(); + + // Clear the interrupt + TC5->COUNT16.INTFLAG.bit.MC0 = 1; +} + +void TC5_Handler (void) __attribute__ ((weak, alias("DDS_Handler"))); + +#ifdef __cplusplus +} +#endif + +#else +// Standard AVR ISRs + #ifdef DDS_USE_ONLY_TIMER2 ISR(TIMER2_OVF_vect) { dds.clockTick(); @@ -43,4 +74,7 @@ ISR(ADC_vect) { dds.clockTick(); digitalWrite(2, LOW); } -#endif +#endif /* DDS_USE_ONLY_TIMER2 */ + +#endif /* __SAMD21G18A__ */ +