diff --git a/DDS.cpp b/DDS.cpp index 8772fc7..32a6d8d 100644 --- a/DDS.cpp +++ b/DDS.cpp @@ -1,41 +1,108 @@ #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__ + // 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)) ; + 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 +#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)); // 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 = DDS_MAX_COMPARATOR-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 +110,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,46 +123,54 @@ 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 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 { - 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.. @@ -112,64 +183,107 @@ 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, DDS_MAX_COMPARATOR/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 + OCR2B = DDS_MAX_COMPARATOR/2; + #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, DDS_MAX_COMPARATOR/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 = DDS_MAX_COMPARATOR/2; + #else + // Set duty cycle to 0, effectively off + OCR2B = 0; + #endif + #endif #endif } } -uint8_t DDS::getDutyCycle() { - #if ACCUMULATOR_BIT_SHIFT >= 24 +//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_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 57cf40b..7eb55b6 100644 --- a/DDS.h +++ b/DDS.h @@ -1,19 +1,26 @@ #ifndef _DDS_H_ #define _DDS_H_ - +#include #include +// Just a little (useless?) reminder; #warnings are not displayed in Arduino IDE. +#ifndef __SAMD21G18A__ +#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 -#define DDS_PWM_PIN_3 +// #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. +// is increased, along with a reduction in memory use._ #define SHORT_ACCUMULATOR #ifdef SHORT_ACCUMULATOR @@ -24,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, @@ -35,19 +47,36 @@ typedef uint32_t ddsAccumulator_t; // 8 = 62.5kHz PWM // 7 = 125kHz PWM // 6 = 250kHz PWM -#ifdef DDS_PWM_PIN_3 -#define COMPARATOR_BITS 6 +// For SAMD21's -- the resolution of the DAC. + +#ifdef __SAMD21G18A__ + #define COMPARATOR_BITS 10 + typedef uint16_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 + #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 -#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,15 +90,77 @@ 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. // #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, @@ -104,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: @@ -202,7 +289,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 +309,7 @@ class DDS { volatile ddsAccumulator_t stepRate; ddsAccumulator_t refclk; int16_t refclkOffset; - static DDS *sDDS; + //static _DDS *sDDS; }; #endif /* _DDS_H_ */ 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__ */ +