Skip to content

Commit 52448b0

Browse files
release lib v1.2
1 parent b65c99c commit 52448b0

File tree

9 files changed

+492
-394
lines changed

9 files changed

+492
-394
lines changed

README.md

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
1-
# Native DDS Library for Arduino
2-
This repository contains the *NativeDDS* library for Arduino. *NativeDDS* is a software implemented Direct Digital Synthesizer. *NativeDDS* generates sinusoidal waves for example for use in audio applications, motor drives, sine wave inverters or as a reference frequency source. The library is easy portable to other platforms.
1+
# Native DDS Library for Arduino v1.2
2+
This repository contains the *NativeDDS* library for Arduino. *NativeDDS* is a software implemented Direct Digital Synthesizer. *NativeDDS* generates sinusoidal waves for example for use in audio applications, motor drives, sine wave inverters or for a reference frequency source. The library is easy portable to other platforms.
33

44
## Function
5-
The DDS frequency resolution is 32-bit. This means that the frequency can be tuned with steps of `f_update/(2^32)`, with `f_update = 1/timestep` (see below). The amplitude resolution can be set to eight bit or ten bit by defining `EIGHTBIT` or `TENBIT` in the `NativeDDS.h` file. The eight bit implementation is faster and will be precise enough for most applications.
5+
The DDS frequency resolution is 32-bit. This means that the frequency can be tuned in steps of `f_update/(2^32)`, with `f_update = 1/timestep` (see below). The amplitude resolution can be eight bits or ten bits by choosing the class instances `DDS_8bit_xx` or `DDS_10bit_xx`. The eight bit implementation is faster and will be precise enough in most applications.
66

7-
The following library classes are implemented:
7+
The following library instance methods are implemented:
88

9-
* `DDS_1Ch`
10-
* `DDS_2Ch`
11-
* `DDS_IQ`
12-
* `DDS_3Ph`
9+
* `DDS_8bit_1Ch or DDS_10bit_1Ch`
10+
* `DDS_8bit_2Ch or DDS_10bit_2Ch`
11+
* `DDS_8bit_IQ or DDS_10bit_IQ`
12+
* `DDS_8bit_3Ph or DDS_10bit_3Ph`
1313

14-
`DDS_1Ch` is a single (output) channel DDS, `DDS_2Ch` is a dual channel DDS, `DDS_IQ` is a dual channel DDS with orthogonal outputs and `DDS_3Ph` is a three channel DDS.
14+
The explanation below will be continued for the 8-bit case.
15+
16+
`DDS_8bit_1Ch` is a single (output) channel DDS, `DDS_8bit_2Ch` is a dual channel DDS, `DDS_8bit_IQ` is a dual channel DDS generating orthogonal outputs and `DDS_8bit_3Ph` is a three output channel DDS.
1517

1618
The following methods exists:
1719

@@ -21,31 +23,31 @@ The following methods exists:
2123

2224
Initialize the class instance with the member function `begin()`. Also, `begin()` must be called each time when the frequency or phase has to be updated.
2325

24-
DDS_1Ch, use: `.begin(float frequency, float starting-phase, float timestep);`
26+
DDS_8bit_1Ch, use: `.begin(float frequency, float starting-phase, float timestep);`
2527

26-
DDS_2Ch, use: `.begin(float frequency1, float frequency2, starting-phase1, float starting-phase2, timestep);`
28+
DDS_8bit_2Ch, use: `.begin(float frequency1, float frequency2, starting-phase1, float starting-phase2, timestep);`
2729

28-
DDS_IQ, use: `.begin(float frequency, float starting-phase, float timestep);`
30+
DDS_8bit_IQ, use: `.begin(float frequency, float starting-phase, float timestep);`
2931

30-
DDS_3Ph, use: `.begin(float frequency, float starting-phase, float timestep);`
32+
DDS_8bit_3Ph, use: `.begin(float frequency, float starting-phase, float timestep);`
3133

32-
For the member function `.begin()`, the variable `frequency` is in Hz and `starting-phase` in radians. `timestep` is the loop iteration time period in seconds with which `.update()` is repeatedly invoked.
34+
For the member function `.begin()`, the variable `frequency` is in Hz and `starting-phase` in radians. `timestep` is the loop repeating time in seconds at which `.update()` is invoked.
3335

34-
The following public variables are defined for the outputs:
36+
The following public output variables are defined:
3537

36-
DDS_1Ch: `out1`
38+
DDS_8bit_1Ch: `out1` and `uout1` for unsigned.
3739

38-
DDS_2Ch: `out1`, `out2`
40+
DDS_8bit_2Ch: `out1`, `out2` and `uout1`, `uout2` for unsigned.
3941

40-
DDS_IQ: `outi`, `outq`
42+
DDS_8bit_IQ: `outi`, `outq` and `uouti`, `uoutq` for unsigned.
4143

42-
DDS_3Ph: `outu`, `outv`, `outw`
44+
DDS_8bit_3Ph: `outu`, `outv`, `outw` and `uoutu`, `uoutv`, `uoutw` for unsigned.
4345

4446
## Usage
4547
* First create an instance of the library object, for example here we define *mySin*:
4648

4749
```
48-
DDS_1Ch mySine;
50+
DDS_8bit_1Ch mySine;
4951
```
5052

5153
* Initialize the DDS on some place in your setup function:
@@ -60,24 +62,26 @@ void setup() {
6062

6163
* Call `mySine.update();` from the main loop or from an Interrupt Service Routine (ISR). Use `.update(void)` on the basis of a regular time interval to generate a jitter free output signal.
6264

63-
Right after the previous step, the instantaneous output value is obtained with: `int sample = mySine.out1;`
65+
Right after the previous step, the instantaneous (unsigned) output value is obtained with: `int sample = mySine.uout1;`
6466

6567
```
6668
void loop() {
6769
...
6870
mySine.update();
69-
sample=mySine.out1
70-
analogWrite(PWM_OUT, sample + 127); // add 127 to convert to unsigned.
71+
sample=mySine.uout1
72+
analogWrite(PWM_OUT, sample);
7173
...
7274
}
7375
```
7476

7577
## Examples
76-
`SineWave.ino` - Example of a single channel DDS. The time base is generated in the main loop. The maximum usable output frequency is limited to about 100Hz.
78+
`SineWave_8bit.ino` - Example of a single channel 8-bits DDS. The time base is generated in the main loop. The maximum usable output frequency is limited to about 100Hz.
79+
80+
`SineWave_10bit.ino` - Example of a single channel 10-bits DDS. The time base is generated in the main loop. The maximum usable output frequency is limited to about 100Hz.
7781

7882
`WobblingServo.ino` - A slowly sine-wave modulated servo motor application.
7983

80-
`AudioGenerator.ino` - A generator for audible frequencies. A timer interrupt has been used to create a fixed time base.
84+
`AudioGenerator.ino` - A generator for audible frequencies. A timer interrupt has been used to create the time base.
8185

8286
## Acknowledgement
8387
A lot of time was saved in developing this library by using the alternative Arduino-IDE [Sloeber](https://eclipse.baeyens.it/). Sloeber is a wonderful Arduino plugin for Eclipse. Thanks to Jantje and his contributors!

examples/AudioGenerator/AudioGenerator.ino

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
*
33
* File: AudioGenerator.ino
44
* Purpose: Native Direct Digital Synthesizer library example project
5-
* Version: 1.0.2
5+
* Version: 1.0.3
66
* Release date: 24-10-2018
7-
* Last edit date: 10-08-2020
7+
* Last edit date: 02-11-2020
88
*
99
* URL: https://github.com/MartinStokroos/NativeDDS
1010
* License: MIT License
@@ -13,6 +13,7 @@
1313
* v1.0.0, 24 Oct 2018 - initial release
1414
* v1.0.1, 18 Nov 2018 - textual changes in comments
1515
* v1.0.2, 10-08-2020 - select 8-bit/10bit DDS from main c file but it does not work (yet).
16+
* v1.0.3, 02-11-2020 - library update for seperate instance methods for 8-bit and 10-bit DDS
1617
*
1718
* This sketch demonstrates the Native DDS library. Native DDS is a Direct Digital Synthesizer
1819
* algorithm that runs in software on the Arduino. In this example, a 220Hz and a 440Hz sine wave are
@@ -31,7 +32,6 @@
3132
*
3233
*/
3334

34-
//#define DDS_8BIT //Why does defining it here won't work? Now it still should be done from NativeDDS.h
3535
#include "NativeDDS.h"
3636

3737
//#define DEBUG
@@ -50,7 +50,7 @@
5050
const float Ts = 3.1875E-5; // Timer2 period
5151
float f = 220.0; // Wanted DDS output frequency
5252

53-
DDS_2Ch mySine; // Create instance of DDS_2Ch for a dual channel DDS.
53+
DDS_8bit_2Ch mySine; // Create instance of DDS_8bit_2Ch for a dual channel DDS.
5454

5555

5656

@@ -97,13 +97,12 @@ ISR(TIMER2_OVF_vect) {
9797
#endif
9898

9999
mySine.update();
100-
OCR2B = mySine.out1 + 127; // output PWM_OUT1 directly updated with the OCR2B register value (220Hz)
101-
OCR2A = mySine.out2 + 127; // output PWM_OUT2 directly updated with the OCR2A register value (440Hz)
100+
OCR2B = mySine.uout1; // PWM_OUT1 directly updated by writing the OCR2B register value (220Hz)
101+
OCR2A = mySine.uout2; // PWM_OUT2 directly updated by writing the OCR2A register value (440Hz)
102102

103103
#if defined(DEBUG)
104104
digitalWriteFast(DEBUG_PIN, false);
105105
#endif
106106
}
107107

108108
// end of AudioGenerator.ino
109-
Lines changed: 18 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
/*
2-
* File: SineWave.ino
2+
* File: SineWave_10bit.ino
33
* Purpose: Native Direct Digital Synthesizer library example project
4-
* Version: 1.1.1
4+
* Version: 1.0.0
55
* First release date: 22-10-2018
6-
* Last edit date: 10-08-2020
6+
* Last edit date:
77
*
88
* URL: https://github.com/MartinStokroos/NativeDDS
99
* License: MIT License
1010
*
1111
* Version history:
12-
* v1.0.0, 22 Oct 2018 - initial release
13-
* v1.1.0, 18 Nov 2018 - looptime=200us (5kHz). Pin3 + pin11 PWM frequency=31250Hz and the output frequency=100Hz.
14-
* v1.1.1, 10-08-2020 - select 8-bit/10bit DDS from main c file but it does not work (yet).
12+
*
13+
* v1.0.0, 02-11-2020 - separate instance methods for 8-bit / 10-bit DDS.
1514
*
1615
* This sketch demonstrates the Native DDS library. Native DDS is a Direct Digital Synthesizer
1716
* algorithm that runs in software on the Arduino. In this example the output frequency is set to 100Hz.
@@ -32,28 +31,28 @@
3231
*
3332
*/
3433

35-
//#define DDS_8BIT //Why does defining it here won't work? Now it still should be done from NativeDDS.h
3634
#include "NativeDDS.h"
3735

3836
#define LPERIOD 200 // loop period time in us. Update freq. is 5kHz
37+
3938
#define PWM_OUT 3 // define PWM output pin
4039
#define DEBUG_PIN 13 // define debug PIN for timing observation (=LED pin)
4140

4241
double frequency = 100.0; // output frequency in Hz
4342
unsigned long nextLoop;
4443
int output;
4544

46-
DDS_1Ch mySine; // create instance of DDS_1Ch
45+
DDS_10bit_1Ch mySine; // create instance of DDS_10bit_1Ch
4746

4847

4948

5049
void setup() {
5150
// run once:
5251
pinMode(PWM_OUT, OUTPUT);
5352
pinMode(DEBUG_PIN, OUTPUT);
54-
TCCR2B = (TCCR2B & 0xF8)|0x01; // set Pin3 + Pin11 PWM frequency to 31250Hz
53+
TCCR2B = (TCCR2B & 0xF8)|0x01; // Set Pin3 + Pin11 PWM frequency to 31250Hz.
5554

56-
mySine.begin(frequency, 0, LPERIOD*1.0e-6); // initilize the DDS (frequency, startphase, update period time)
55+
mySine.begin(frequency, 0, LPERIOD*1.0e-6); // Initilize the DDS (frequency, startphase, update period time).
5756

5857
nextLoop = micros() + LPERIOD; // Set the loop timer variable for the next loop interval.
5958
}
@@ -63,27 +62,16 @@ void setup() {
6362
void loop() {
6463
// run repeatedly:
6564

66-
#ifdef DDS_8BIT
67-
//digitalWrite(DEBUG_PIN, HIGH); // for checking loop period time and loop execution time (signal high time)
68-
69-
mySine.update(); //update the DDS, measured execution time <10us
70-
71-
//digitalWrite(DEBUG_PIN, LOW);
72-
analogWrite(PWM_OUT, mySine.out1 + 127); // add 127 to convert to unsigned.
73-
74-
#elif defined (DDS_10BIT)
75-
//digitalWrite(DEBUG_PIN, HIGH); // debugging: check loop period time and loop execution time (signal high time)
76-
77-
mySine.update(); // update the DDS, measured execution time <20us
78-
79-
//digitalWrite(DEBUG_PIN, LOW);
80-
output = mySine.out1 + 511; // add 511 to convert to unsigned.
81-
analogWrite(PWM_OUT, output>>2); // convert back to to 8-bit and write to analog out.
82-
#endif
65+
//digitalWrite(DEBUG_PIN, HIGH); // debugging: for checking loop cycling time and function execution time (signal high time)
66+
mySine.update(); // update the DDS, measured execution time <20us
67+
//digitalWrite(DEBUG_PIN, LOW);
68+
69+
//output = mySine.out1 + 511; // add 511 to convert to unsigned.
70+
output = mySine.uout1; // use unsigned output.
71+
analogWrite(PWM_OUT, output>>2); // convert back to 8-bit and write to analog out.
8372

8473
while(nextLoop > micros()); // wait until the end of the time interval
85-
nextLoop += LPERIOD; // set next loop time at current time + LOOP_PERIOD
74+
nextLoop += LPERIOD; // set next loop time at current time + LOOP_PERIOD
8675
}
8776

88-
// end of SineWave.ino
89-
77+
// end of SineWave_10bit.ino
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* File: SineWave_8bit.ino
3+
* Purpose: Native Direct Digital Synthesizer library example project
4+
* Version: 1.0.0
5+
* First release date: 02-11-2020
6+
* Last edit date:
7+
*
8+
* URL: https://github.com/MartinStokroos/NativeDDS
9+
* License: MIT License
10+
* Version history:
11+
*
12+
* v1.0.0, 02-11-2020 - separate instance methods for 8-bit / 10-bit DDS.
13+
*
14+
* This sketch demonstrates the Native DDS library. Native DDS is a Direct Digital Synthesizer
15+
* algorithm that runs in software on the Arduino. In this example the output frequency is set to 100Hz.
16+
* The pulse width modulated sine wave is available on pin 3.
17+
*
18+
* In this example the DDS runs at 5kHz (the update frequency of the DDS). In theory the maximum output frequency
19+
* at 5kHz update rate is 2500Hz. To get a smooth output wave, a low pass filter (RC or multistage LC), is required.
20+
* For more information please read: A Technical Tutorial on Digital Signal Synthesis, Ken Gentile and Rick Cushing,
21+
* Analog Devices, 1999.
22+
*
23+
* NOTE: The DDS output is sent to the PWM output pin with the analogWrite function. analogWrite sets the duty-cycle
24+
* of the PWM signal corresponding to the wanted average output voltage level. The default PWM-frequency of pin 3
25+
* is approximately 490Hz. Because of this low PWM frequency, the maximum usable DDS output frequency is even lower.
26+
* For this reason the pwm frequency has been set to much a higher frequency than the DDS update frequency in this example.
27+
*
28+
* The loop timing can be observed with an oscilloscope on pin 13 (uncomment the lines with a digitalWrite).
29+
* Please, also check the other example projects of the NativeDDS library.
30+
*
31+
*/
32+
33+
#include "NativeDDS.h"
34+
35+
#define LPERIOD 200 // loop period time in us. Update freq. is 5kHz
36+
37+
#define PWM_OUT 3 // define PWM output pin
38+
#define DEBUG_PIN 13 // define debug PIN for timing observation (=LED pin)
39+
40+
double frequency = 100.0; // output frequency in Hz
41+
unsigned long nextLoop;
42+
int output;
43+
44+
DDS_8bit_1Ch mySine; // create instance of DDS_1Ch
45+
46+
47+
48+
void setup() {
49+
// run once:
50+
pinMode(PWM_OUT, OUTPUT);
51+
pinMode(DEBUG_PIN, OUTPUT);
52+
TCCR2B = (TCCR2B & 0xF8)|0x01; // Set Pin3 + Pin11 PWM frequency to 31250Hz.
53+
54+
mySine.begin(frequency, 0, LPERIOD*1.0e-6); // Initilize the DDS (frequency, startphase, update period time).
55+
56+
nextLoop = micros() + LPERIOD; // Set the loop timer variable for the next loop interval.
57+
}
58+
59+
60+
61+
void loop() {
62+
// run repeatedly:
63+
64+
//digitalWrite(DEBUG_PIN, HIGH); // for checking loop cycling time and function execution time (signal high time)
65+
mySine.update(); // update DDS. Measured execution time <10us.
66+
//digitalWrite(DEBUG_PIN, LOW);
67+
68+
//analogWrite(PWM_OUT, mySine.out1 + 127); // add 127 to convert to unsigned.
69+
analogWrite(PWM_OUT, mySine.uout1); // use unsigned output.
70+
71+
while(nextLoop > micros()); // wait until the end of the time interval
72+
nextLoop += LPERIOD; // set next loop time at current time + LOOP_PERIOD
73+
}
74+
75+
// end of SineWave_8bit.ino

0 commit comments

Comments
 (0)