Very rough draft of DDS supporting the Arduio Zero and it's built-in DAC.
This commit is contained in:
		
							parent
							
								
									c679bec886
								
							
						
					
					
						commit
						fdaa602401
					
				
							
								
								
									
										90
									
								
								DDS.cpp
								
								
								
								
							
							
						
						
									
										90
									
								
								DDS.cpp
								
								
								
								
							|  | @ -1,9 +1,63 @@ | ||||||
| #include <Arduino.h> | #include <Arduino.h> | ||||||
| #include "DDS.h" | #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
 | // To start the DDS, we use Timer1, set to the reference clock
 | ||||||
| // We use Timer2 for the PWM output, running as fast as feasible
 | // We use Timer2 for the PWM output, running as fast as feasible
 | ||||||
| void DDS::start() { | 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
 |   // Use the clkIO clock rate
 | ||||||
|   ASSR &= ~(_BV(EXCLK) | _BV(AS2)); |   ASSR &= ~(_BV(EXCLK) | _BV(AS2)); | ||||||
|    |    | ||||||
|  | @ -45,10 +99,6 @@ void DDS::start() { | ||||||
|   TCCR1A = 0; |   TCCR1A = 0; | ||||||
|   ICR1 = ((F_CPU / 1) / refclk) - 1;  |   ICR1 = ((F_CPU / 1) / refclk) - 1;  | ||||||
|   #ifdef DDS_DEBUG_SERIAL |   #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.print(F("DDS ICR1: ")); | ||||||
|     Serial.println(ICR1, DEC); |     Serial.println(ICR1, DEC); | ||||||
|   #endif |   #endif | ||||||
|  | @ -60,14 +110,22 @@ void DDS::start() { | ||||||
|   DIDR0 |= _BV(0); |   DIDR0 |= _BV(0); | ||||||
|   ADCSRB = _BV(ADTS2) | _BV(ADTS1) | _BV(ADTS0); |   ADCSRB = _BV(ADTS2) | _BV(ADTS1) | _BV(ADTS0); | ||||||
|   ADCSRA = _BV(ADEN) | _BV(ADSC) | _BV(ADATE) | _BV(ADIE) | _BV(ADPS2); // | _BV(ADPS0);    
 |   ADCSRA = _BV(ADEN) | _BV(ADSC) | _BV(ADATE) | _BV(ADIE) | _BV(ADPS2); // | _BV(ADPS0);    
 | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void DDS::stop() { | void DDS::stop() { | ||||||
|  | #ifdef __SAMD21G18A__ | ||||||
|  |   TC_DISABLE(); | ||||||
|  |   TC_RESET(); | ||||||
|  |   analogWrite(A0, 0); | ||||||
|  | #else | ||||||
|   // TODO: Stop the timers.
 |   // TODO: Stop the timers.
 | ||||||
|   #ifndef DDS_USE_ONLY_TIMER2 |   #ifndef DDS_USE_ONLY_TIMER2 | ||||||
|     TCCR1B = 0; |     TCCR1B = 0; | ||||||
|   #endif |   #endif | ||||||
|     TCCR2B = 0; |     TCCR2B = 0; | ||||||
|  | #endif | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Set our current sine wave frequency in Hz
 | // 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);       |       newStep = (600.0 / (DDS_REFCLK_DEFAULT+DDS_REFCLK_OFFSET)) * pow(2,ACCUMULATOR_BITS);       | ||||||
|     } |     } | ||||||
|   } else { |   } else { | ||||||
|  |     //TODO:  This doesn't work with the SAM21D... yet
 | ||||||
|     newStep = pow(2,ACCUMULATOR_BITS)*freq / (refclk+refclkOffset); |     newStep = pow(2,ACCUMULATOR_BITS)*freq / (refclk+refclkOffset); | ||||||
|   } |   } | ||||||
|   return newStep; |   return newStep; | ||||||
|  | @ -112,6 +171,13 @@ void DDS::clockTick() { | ||||||
|   if(running) { |   if(running) { | ||||||
|     accumulator += stepRate; |     accumulator += stepRate; | ||||||
|     if(timeLimited && tickDuration == 0) { |     if(timeLimited && tickDuration == 0) { | ||||||
|  | #ifdef __SAMD21G18A__ | ||||||
|  |   #ifdef DDS_IDLE_HIGH | ||||||
|  |     analogWrite(A0, pow(2,COMPARATOR_BITS)/2); | ||||||
|  |   #else | ||||||
|  |     analogWrite(A0, 0); | ||||||
|  |   #endif | ||||||
|  | #else | ||||||
|   #ifndef DDS_PWM_PIN_3 |   #ifndef DDS_PWM_PIN_3 | ||||||
|       OCR2A = 0; |       OCR2A = 0; | ||||||
|   #else |   #else | ||||||
|  | @ -122,19 +188,31 @@ void DDS::clockTick() { | ||||||
|       // Set duty cycle to 0, effectively off
 |       // Set duty cycle to 0, effectively off
 | ||||||
|         OCR2B = 0; |         OCR2B = 0; | ||||||
|   #endif |   #endif | ||||||
|  |   #endif | ||||||
| #endif | #endif | ||||||
|       running = false; |       running = false; | ||||||
|       accumulator = 0; |       accumulator = 0; | ||||||
|     } else { |     } else { | ||||||
|  | #ifdef __SAMD21G18A__ | ||||||
|  |     analogWrite(A0, getDutyCycle()); | ||||||
|  | #else | ||||||
|   #ifdef DDS_PWM_PIN_3 |   #ifdef DDS_PWM_PIN_3 | ||||||
|       OCR2B = getDutyCycle(); |       OCR2B = getDutyCycle(); | ||||||
|   #else |   #else | ||||||
|       OCR2A = getDutyCycle(); |       OCR2A = getDutyCycle(); | ||||||
|  |   #endif | ||||||
| #endif | #endif | ||||||
|     } |     } | ||||||
|     // Reduce our playback duration by one tick
 |     // Reduce our playback duration by one tick
 | ||||||
|     tickDuration--; |     tickDuration--; | ||||||
|   } else { |   } else { | ||||||
|  | #ifdef __SAMD21G18A__ | ||||||
|  |   #ifdef DDS_IDLE_HIGH | ||||||
|  |     analogWrite(A0, pow(2,COMPARATOR_BITS)/2); | ||||||
|  |   #else | ||||||
|  |     analogWrite(A0, 0); | ||||||
|  |   #endif | ||||||
|  | #else | ||||||
|     // Hold it low
 |     // Hold it low
 | ||||||
|   #ifndef DDS_PWM_PIN_3 |   #ifndef DDS_PWM_PIN_3 | ||||||
|     OCR2A = 0; |     OCR2A = 0; | ||||||
|  | @ -147,10 +225,11 @@ void DDS::clockTick() { | ||||||
|       OCR2B = 0; |       OCR2B = 0; | ||||||
|     #endif |     #endif | ||||||
|   #endif |   #endif | ||||||
|  | #endif | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| uint8_t DDS::getDutyCycle() { | ddsComparitor_t DDS::getDutyCycle() { | ||||||
|   #if ACCUMULATOR_BIT_SHIFT >= 24 |   #if ACCUMULATOR_BIT_SHIFT >= 24 | ||||||
|     uint16_t phAng; |     uint16_t phAng; | ||||||
|   #else |   #else | ||||||
|  | @ -173,3 +252,4 @@ uint8_t DDS::getDutyCycle() { | ||||||
|   scaled += 128>>(8-COMPARATOR_BITS); |   scaled += 128>>(8-COMPARATOR_BITS); | ||||||
|   return scaled; |   return scaled; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  |  | ||||||
							
								
								
									
										31
									
								
								DDS.h
								
								
								
								
							
							
						
						
									
										31
									
								
								DDS.h
								
								
								
								
							|  | @ -1,12 +1,19 @@ | ||||||
| #ifndef _DDS_H_ | #ifndef _DDS_H_ | ||||||
| #define _DDS_H_ | #define _DDS_H_ | ||||||
| 
 | #include <Arduino.h> | ||||||
| #include <avr/pgmspace.h> | #include <avr/pgmspace.h> | ||||||
| 
 | 
 | ||||||
|  | // 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
 | // 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
 | // Quality on pin 3 is higher than on 11, as it can be clocked faster
 | ||||||
| // when the COMPARATOR_BITS value is less than 8
 | // when the COMPARATOR_BITS value is less than 8
 | ||||||
|  | #ifndef __SAMD21G18A__ | ||||||
| #define DDS_PWM_PIN_3 | #define DDS_PWM_PIN_3 | ||||||
|  | #endif | ||||||
| 
 | 
 | ||||||
| // Normally, we turn on timer2 and timer1, and have ADC sampling as our clock
 | // 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 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
 | // 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.
 | ||||||
|  | #ifndef __SAMD21G18A__ | ||||||
| #define SHORT_ACCUMULATOR | #define SHORT_ACCUMULATOR | ||||||
|  | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef SHORT_ACCUMULATOR | #ifdef SHORT_ACCUMULATOR | ||||||
| #define ACCUMULATOR_BITS      16 | #define ACCUMULATOR_BITS      16 | ||||||
|  | @ -35,10 +44,17 @@ typedef uint32_t ddsAccumulator_t; | ||||||
| // 8 = 62.5kHz PWM
 | // 8 = 62.5kHz PWM
 | ||||||
| // 7 = 125kHz PWM
 | // 7 = 125kHz PWM
 | ||||||
| // 6 = 250kHz 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 | #define COMPARATOR_BITS       6 | ||||||
|  | typedef uint8_t ddsComparitor_t; | ||||||
| #else // When using pin 11, we always want 8 bits
 | #else // When using pin 11, we always want 8 bits
 | ||||||
| #define COMPARATOR_BITS       8 | #define COMPARATOR_BITS       8 | ||||||
|  | typedef uint8_t ddsComparitor_t; | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| // This is how often we'll perform a phase advance, as well as ADC sampling
 | // 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)
 | // expense of CPU time. It maxes out around 62000 (TBD)
 | ||||||
| // May be overridden in the sketch to improve performance
 | // May be overridden in the sketch to improve performance
 | ||||||
| #ifndef DDS_REFCLK_DEFAULT | #ifndef DDS_REFCLK_DEFAULT | ||||||
|  |   #ifdef __SAMD21G18A__ | ||||||
|  |     #define DDS_REFCLK_DEFAULT     44100 | ||||||
|  |   #else | ||||||
|     #define DDS_REFCLK_DEFAULT     9600 |     #define DDS_REFCLK_DEFAULT     9600 | ||||||
|   #endif |   #endif | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| // As each Arduino crystal is a little different, this can be fine tuned to
 | // As each Arduino crystal is a little different, this can be fine tuned to
 | ||||||
| // provide more accurate frequencies. Adjustments in the range of hundreds
 | // provide more accurate frequencies. Adjustments in the range of hundreds
 | ||||||
| // is a good start.
 | // is a good start.
 | ||||||
|  | @ -61,7 +82,7 @@ typedef uint32_t ddsAccumulator_t; | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| // Output some of the calculations and information about the DDS over serial
 | // 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
 | // When defined, use the 1024 element sine lookup table. This improves phase
 | ||||||
| // accuracy, at the cost of more flash and CPU requirements.
 | // accuracy, at the cost of more flash and CPU requirements.
 | ||||||
|  | @ -202,7 +223,7 @@ public: | ||||||
|     return refclkOffset; |     return refclkOffset; | ||||||
|   } |   } | ||||||
|    |    | ||||||
|   uint8_t getDutyCycle(); |   ddsComparitor_t getDutyCycle(); | ||||||
| 
 | 
 | ||||||
|   // Set a scaling factor. To keep things quick, this is a power of 2 value.
 |   // 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.
 |   // Set it with 0 for lowest (which will be off), 8 is highest.
 | ||||||
|  | @ -222,7 +243,7 @@ private: | ||||||
|   volatile ddsAccumulator_t stepRate; |   volatile ddsAccumulator_t stepRate; | ||||||
|   ddsAccumulator_t refclk; |   ddsAccumulator_t refclk; | ||||||
|   int16_t refclkOffset; |   int16_t refclkOffset; | ||||||
|   static DDS *sDDS; |   //static _DDS *sDDS;
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| #endif /* _DDS_H_ */ | #endif /* _DDS_H_ */ | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue