Direct Digital Synthesizer (DDS) (Part 6)

Part 12345, 6

Patrick, thanks for your feed back. Thanks to you, there will be some updates of the PlainDDS library and its related MicroDDS applications. Here is a quite simple but already pretty effective human-machine interface for driving a signal generator.

This version is controlled by two analog potentiometers and a push button. Pressing the push button will scroll up to the next wave type  while adjusting potentiometers will change the raw frequency settings and the fine frequency setting. The fine frequency setting will range from -5% to +5% of the selected raw frequency thus allowing fine adjustment of the frequency which could not be achieved with only one potentiometer.

Note: using multiturn potentiometer might be a good idea!

multiturn_pot

 

or

trim_pot

Here is the schematic, quite simple,

 

MicroDDS_rev01

R17- C2, R18- C3 and R19-C4 are damping the output of the potentiometers in order to prevent frequent changes and unstable signal frequency. C1 value is a few tens of pF if DDS is to be used at high frequencies (up to 16kHz) and can be increased if low frequencies are commonly generated with DDS. C5 is srongly recommanded to avoid frequency changes due to VCC noise.

Here is the full related code

/*

	MicroDDS: Functions generator using R2R DAC 
	This version is controlled by analog potentiometers and a push button
	Pressing the push button will scroll up to the next wave type
	Adjusting potentiometers will change the raw _frequency settings, 
	the fine _frequency setting and the stand by value (Flat wave type).

	Copyright (c) 2012-2013 Didier Longueville

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/

/* Libraires */
#include <PlainDDS.h> /* DDS library */

PlainDDS DDS; /* Create an instance of the DDS Object */
/* Custom variables */
uint8_t _coarseTuningPin = PINC0;
uint8_t _fineTuningPin = PINC1;
uint8_t _standbyValuePin = PINC2;
uint8_t _setPushButPinMask = (1 << PIND2);
/* Global variables */
uint16_t _coarseTuning ;
uint16_t _fineTuning;
uint16_t _frequency;
uint16_t _lastFrequency = 0;
uint16_t _stdByValue;
uint16_t _lastStdByValue;
uint8_t _waveType = DDS_WAV_TYP_SINE;
uint16_t _samples = 128;
uint8_t _bits = 6;

void setup() 
{
	Serial.begin(115200);
	DDRD &= ~_setPushButPinMask; /* Input pin */
	PORTD |=  _setPushButPinMask; /* Bias pin */
	DDS.InitializeDDS(_bits, &PORTB); 
	DDS.WaveType(_waveType);
	/* Reset timer/counter 1 in order to reduce noise on timer/counter 2 */ 
	TIMSK1 = 0;
	DDS.Start();
	Serial.println("MicroDDS is ready");
};

void loop() 
{
	/* Read pot posiitons and convert adc in frequency value */
	_coarseTuning = map(getAdc(_coarseTuningPin, _samples), 0, 1023, 1, 16000);
	_fineTuning = map(getAdc(_fineTuningPin, _samples), 0, 1023, 0, (_coarseTuning / 10));
	_frequency = (_coarseTuning + _fineTuning);
	if (_frequency != _lastFrequency){
		if (_frequency < 1) {
			_frequency = 1;
		} else if (_frequency > 16000) {
			_frequency = 16000;
		}
		/* Set DDS frequency */
		DDS.Frequency(_frequency);
		Serial.print("Frequency: ");
		Serial.print(_frequency);
		Serial.println();
		/* Record actual settings */
		_lastFrequency = _frequency;
	}
	_stdByValue = map(getAdc(_standbyValuePin, _samples), 0, 1023, 0, ((1 << _bits) - 1));
	if (_stdByValue != _lastStdByValue) {	
		/* Set DDS stand by value */
		DDS.StandByValue(_stdByValue); 
		Serial.print("Stand By Value: ");
		Serial.print(_stdByValue);
		Serial.println();
		_lastStdByValue = _stdByValue;		
	}
	if (~PIND & _setPushButPinMask) {
		while(~PIND & _setPushButPinMask); /* Wait until button is released */
		_waveType += 1; /* scroll wave types */
		if (_waveType >= DDS_WAV_TYP_RANDOM) {
			_waveType = DDS_WAV_TYP_FLAT;
		}		
		DDS.Stop();
		DDS.WaveType(_waveType);
		DDS.Start();
		Serial.print("Wave type: ");
		Serial.print(_waveType);
		Serial.println();
	}
	delay(100);
};

uint16_t getAdc(uint8_t pin, uint16_t samples) 
{
	uint32_t adcValue = 0;
	for (uint16_t i = 0; i < samples; i++) {
		adcValue += analogRead(pin);
	}
	adcValue /= samples;
	return(adcValue);	
};

Just a few words about the code. The setup function contains initialization functions. Then, every 100ms, the loop function iterates and checks frequency settings, standby value setting and push button setting. If one of them was changed since the last iteration, related code will execute in order to update DDS parameters. Triming potentiometers are read by a specific function which oversamples the signal in order to (sort of) smooth it and to prevent adc reading jitter.

This code requires the PlainDDS library which is available on simple request

HTH

Leave a Reply

You must be logged in to post a comment.