Skip to content

Commit e07c0ba

Browse files
authored
Develop (#7)
- add **setDutyCycle()**, **getDutyCycle()** - implement duty cycle for square(), triangle() and random_DC() - add **seedRandom(a, b)** - add some optimizations - move code from .h to .cpp - update readme.md - update GitHub actions - update license 2023 - minor edits
1 parent 6c8493d commit e07c0ba

16 files changed

Lines changed: 1350 additions & 165 deletions

File tree

CHANGELOG.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,23 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
66
and this project adheres to [Semantic Versioning](http://semver.org/).
77

88

9+
## [0.2.5] - 2023-03-25
10+
- add **setDutyCycle()**, **getDutyCycle()**
11+
- implement duty cycle for square(), triangle() and random()
12+
- add **seedRandom(a, b)**
13+
- add some optimizations
14+
- move code from .h to .cpp
15+
- update readme.md
16+
- update GitHub actions
17+
- update license 2023
18+
- minor edits
19+
20+
921
## [0.2.4] - 2022-11-07
1022
- add changelog.md
1123
- add rp2040 to build-CI
1224
- update readme.md
1325

14-
1526
## [0.2.3] - 2021-12-18
1627
- update library.json, license, minor edits
1728

README.md

Lines changed: 150 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Arduino library to generate (numeric) wave forms for a DAC.
1313

1414
## Description
1515

16-
This library presents a class for a function generator in software.
16+
This library presents a class for a function generator in **software**.
1717
It is typical used to control one or more DAC's.
1818
To maximize signal quality one has to apply all (or most) processor power
1919
to calculate new values over and over again to get enough resolution.
@@ -25,119 +25,203 @@ Note: this class generates float values, performance wise this can be optimized,
2525
to achieve higher speeds at cost of accuracy / precision.
2626

2727

28-
## Performance
28+
#### Performance
2929

3030
Indication of what performance can be expected (based upon 0.2.1 version).
3131
Note that the values need to be transported to a DAC or serial port too.
3232
Numbers based on performance example, for one single signal.
3333

3434

35-
| Processor | Clock | Waveform | usec/calls | max freq |
36-
|:-------------|---------:|:---------|-----------:|---------:|
37-
| Arduino UNO | 16 MHz | sawtooth | 62 | 60 Hz |
38-
| Arduino UNO | 16 MHz | triangle | 74 | 50 Hz |
39-
| Arduino UNO | 16 MHz | square | 53 | 1000 Hz |
40-
| Arduino UNO | 16 MHz | sinus | 164 | 25 Hz |
41-
| Arduino UNO | 16 MHz | stair | 81 | 50 Hz |
42-
| Arduino UNO | 16 MHz | random | 37 | 1000 Hz |
43-
| | | | | |
44-
| ESP32 | 240 MHz | sawtooth | 3.8 | 1000 Hz |
45-
| ESP32 | 240 MHz | triangle | 3.9 | 1000 Hz |
46-
| ESP32 | 240 MHz | square | 2.8 | 1000 Hz |
47-
| ESP32 | 240 MHz | sinus | 13.6 | 250 Hz |
48-
| ESP32 | 240 MHz | stair | 4.8 | 800 Hz |
49-
| ESP32 | 240 MHz | random | 1.3 | 1000 Hz |
50-
51-
52-
- assumption minimal 250 samples per period to get a smooth signal.
53-
if a rougher signal is OK, higher frequencies are possible.
35+
| Processor | Clock | Waveform | usec/call | max freq | max values/period |
36+
|:--------------|----------:|:-----------|------------:|-----------:|--------:|
37+
| Arduino UNO | 16 MHz | sawtooth | 62 | 60 Hz | 268 |
38+
| Arduino UNO | 16 MHz | triangle | 74 | 50 Hz | 270 |
39+
| Arduino UNO | 16 MHz | square | 53 | 1000 Hz | 19 |
40+
| Arduino UNO | 16 MHz | sinus | 164 | 25 Hz | 152 |
41+
| Arduino UNO | 16 MHz | stair | 81 | 50 Hz | 246 |
42+
| Arduino UNO | 16 MHz | random | 37 | 1000 Hz | 27 |
43+
| ESP32 | 240 MHz | sawtooth | 3.8 | 1000 Hz | 263 |
44+
| ESP32 | 240 MHz | triangle | 3.9 | 1000 Hz | 256 |
45+
| ESP32 | 240 MHz | square | 2.8 | 1000 Hz | 357 |
46+
| ESP32 | 240 MHz | sinus | 13.6 | 250 Hz | 294 |
47+
| ESP32 | 240 MHz | stair | 4.8 | 800 Hz | 260 |
48+
| ESP32 | 240 MHz | random | 1.3 | 1000 Hz | 769 |
49+
50+
51+
- Assumption minimal around 250 samples per period to get a smooth signal.
52+
If a rougher signal is OK, higher frequencies are possible.
53+
For **square()** and **random()** less samples per period are often acceptable.
5454
- ESP32 can do more calculations however 1000 Hz seems to be a nice
5555
upper limit for a software based function generator.
56-
- if one wants to control multiple DAC's one need to divide the numbers
57-
and round down.
56+
- If one needs to control multiple DAC's one should divide the numbers
57+
and round down to get an estimate.
5858

5959

6060
Note: hardware function generator https://github.com/RobTillaart/AD985X
6161

6262

63+
Note: 0.2.5 due to duty cycle the triangle and square
64+
have become slightly slower.
65+
66+
| Processor | Clock | Waveform | usec/call | max freq |
67+
|:--------------|----------:|:-----------|------------:|-----------:|
68+
| Arduino UNO | 16 MHz | triangle | 84 | 50 Hz |
69+
| Arduino UNO | 16 MHz | square | 57 | 1000 Hz |
70+
| Arduino UNO | 16 MHz | random_DC | 68 | 500 Hz |
71+
72+
73+
#### Accuracy
74+
75+
If the time parameter **t** grows large, the internal math may have rounding
76+
problems after some time. This can and will affect the quality of the output.
77+
78+
Needs further investigations.
79+
80+
6381
## Interface
6482

65-
### Constructor
83+
```cpp
84+
#include "functionGenerator.h"
85+
```
86+
87+
#### Constructor
6688

6789
- **funcgen(float period = 1.0, float amplitude = 1.0, float phase = 0.0, float yShift = 0.0)**
68-
All parameters can be set in the constructor but also later in configuration.
90+
All parameters (except duty cycle) can be set in the constructor but also later in configuration.
91+
Default dutyCycle is 50%.
6992

7093

71-
### Configuration
94+
#### Configuration
7295

7396
- **void setPeriod(float period = 1.0)** set the period of the wave in seconds.
7497
- **float getPeriod()** returns the set period.
7598
- **void setFrequency(float frequency = 1.0)** set the frequency of the wave in Hertz (1/s).
7699
- **float getFrequency()** returns the set frequency in Hertz.
77-
- **void setAmplitude(float amplitude = 1.0)** sets the amplitude of the wave. TODO point to point?
78-
Setting the amplitude to 0 gives ?what?
100+
- **void setAmplitude(float amplitude = 1.0)** sets the amplitude of the wave.
101+
The range is from **-amplitude** to **+amplitude**.
102+
Setting the amplitude to 0 gives effectively a zero signal.
103+
Setting the amplitude to a negative value effectively inverts the signal.
79104
- **float getAmplitude()** returns the set amplitude.
80-
- **void setPhase(float phase = 0.0)** shifts the phase of the wave. Will only be noticeable when
81-
compared with other waves.
105+
- **void setPhase(float phase = 0.0)** shifts the phase of the wave.
106+
Will only be noticeable when compared with other waves.
107+
Phase is also known as the X- or horizontal shift.
82108
- **float getPhase()** returns the set phase.
83-
- **void setYShift(float yShift = 0.0)** sets an Y-shift in amplitude, allows to set some zero point.
109+
- **void setYShift(float yShift = 0.0)** sets an Y-shift or vertical offset in amplitude.
110+
This allows to set e.g. the zero level.
84111
- **float getYShift()** returns the set Y-shift.
112+
- **void setDutyCycle(float percentage = 100)** sets the duty cycle of the signal.
113+
Experimental, not all waveforms have a duty cycle or interpret it differently, see below.
114+
Duty cycle must be between 0 and 100% and will be clipped otherwise.
115+
- **float getDutyCycle()** returns the set duty cycle.
116+
- **void setRandomSeed(uint32_t a, uint32_t b = 314159265)** sets the initial seeds for the
117+
(Marsaglia) random number generator. The first is mandatory, the second is optional.
85118

86119

87-
### Wave forms
120+
#### Wave forms
88121

89-
t is time in seconds
122+
The variable t == time in seconds.
90123

91-
- **float sawtooth(float t, uint8_t mode = 0)** mode == 0 (default) ==> sawtooth /|. mode == 1 ==> sawtooth |\.
92-
- **float triangle(float t)** triangle form, duty cycle 50%.
93-
- **float square(float t)** square wave with duty cycle 50%.
124+
- **float sawtooth(float t, uint8_t mode = 0)** mode 0 is default.
125+
- mode == 0 ==> sawtooth /|.
126+
- mode == 1 ==> sawtooth |\\. Effectively equals inverting the amplitude.
127+
- **float triangle(float t)** triangle form, duty cycle default 50%.
128+
- **float square(float t)** square wave with duty cycle default 50%.
94129
- **float sinus(float t)** sinus wave, has no duty cycle.
95-
- **float stair(float t, uint16_t steps = 8, uint8_t mode = 0)** mode = 0 ==> steps up, mode = 1 steps down.
96-
- **float random()** noise generation.
97-
- **float line()** constant voltage line. Depends on the set YShift and amplitude.
130+
- **float stair(float t, uint16_t steps = 8, uint8_t mode = 0)** defaults to 8 steps up.
131+
- mode = 0 ==> steps up
132+
- mode = 1 ==> steps down. Effectively equals inverting the amplitude.
133+
- **float random()** random noise generation.
134+
- **float line()** constant voltage line.
135+
Height depends on the YShift and amplitude.
98136
- **float zero()** constant zero.
99137

100-
Line() and zero() are functions that can be used to drive a constant voltage from a DAC
101-
and can be used to calibrate the generator / DAC combination.
138+
The functions **line()** and **zero()** can be used to drive a constant voltage
139+
from a DAC and can be used to calibrate the generator / DAC combination.
102140

103141

104-
## Operational
142+
#### Duty Cycle
105143

106-
See examples.
144+
Since 0.2.5 the library has **experimental** support for duty cycle.
145+
The meaning of duty cycle differs per wave form.
146+
Implementation may change in the future.
107147

148+
In first iteration the following behaviour is implemented:
108149

109-
## Future
150+
- **square()** implements duty cycle in a well known way.
151+
At the start of the period the signal goes "HIGH".
152+
After duty cycle % of the period the signal goes LOW until the end of the period.
153+
- **triangle()** function uses the duty cycle to shift the peak from the
154+
begin (0%) to middle (50%) to end (100%) of the period.
155+
- **random_DC()** A duty cycle of 0% is no noise 100% is full amplitude noise
156+
with respect to previous value.
157+
Implemented as a weighed average between new and previous value.
158+
Made a separate function as handling the duty cycle slows performance substantial.
159+
Initial starts at zero and can be adjusted with **YShift()**.
110160

111161

112-
#### waveforms
162+
The other functions need to be investigated what duty cycle means.
163+
Current ideas that are **NOT** implemented:
113164

114-
- square duty-cycle (will be slower!)
115-
- triangle duty-cycle (makes sawtooth a special triangle)
116-
- trapezium wave (would merge square and triangle and sawtooth)
117-
- Bezier curve?
165+
- **sawtooth()** - move from /|. to /|__ so 0% is a spike, 100% = normal.
166+
Think of it as the halve of the triangle wave.
167+
- **sinus()** move the two peaks like the triangle (adjusting steepness / freq)??
168+
- **stair()** like sawtooth??
169+
- **line()** has no period so does not make sense (yet).
170+
- **zero()** has no period so does not make sense (yet).
118171

119-
#### investigate
172+
Feedback and ideas are welcome.
120173

121-
- duty-cycle for sinus what does it mean. move peak.
122-
- white noise, pink noise etc.
123-
- investigate algorithms for performance gains (DAC specific values 10-12-16 bit)
124-
- Amplitude modulation ?
125-
- external clock to synchronize two or more sw function generators.
126-
- RC function curve.
127-
- heartbeat curve?
128-
- record a signal and play back
129174

130-
#### misc
175+
## Future
176+
131177

132-
- stand-alone functions in separate .h?
133-
- mapping to voltage function.
134-
- check for synergy with https://github.com/RobTillaart/AD985X
178+
#### Must
135179

136-
#### examples
180+
- documentation
181+
- quality of signals - after substantial time t
182+
- max freq per wave form etc.
183+
Should this be in the library?
137184

138-
- example ESP32 version as separate task...
139-
- example with DAC. 8 12 16 bit.
140-
- example with potentiometers for 4 parameters
141185

186+
#### Should
142187

188+
- smart reseed needed for random().
189+
- initialize random generator with compile time.
143190

191+
192+
#### Could
193+
194+
- waves
195+
- trapezium wave (could merge square and triangle and sawtooth)
196+
- white noise, pink noise etc.
197+
- RC function curve.
198+
- external clock to synchronize two or more software function generators.
199+
- stand-alone functions in separate .h
200+
- check for synergy with https://github.com/RobTillaart/AD985X
201+
- investigate performance.
202+
- algorithms for DAC specific gains e.g. 10-12-16 bit.
203+
- improve performance sin() lookup table.
204+
- add float variable for ```_perDC = _period * _dutyCycle```
205+
- do we need **freq4** ? not since DC.
206+
207+
208+
#### Examples
209+
210+
- Amplitude modulation ?
211+
- heartbeat curve?
212+
- example ESP32 version as separate task.
213+
- example with DAC. 8 12 16 bit.
214+
- example with potentiometers for 4 parameters
215+
216+
#### Wont
217+
218+
- investigate duty cycle for waveforms
219+
- Derived class for the duty cycle variants? or functions!
220+
- **float squareDC()** performance (loss)
221+
- **float triangleDC()**
222+
- **float sawtoothDC()**
223+
- **float sinusDC()** duty-cycle for sinus what does it mean.
224+
- ==> move peaks, two half sinus with diff frequency
225+
- **float stairDC()**
226+
- Bezier curve? (too complex)
227+
- record a signal and play back ==> separate class

examples/functionGenerator/functionGenerator.ino

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
// PURPOSE: demo function generators
55
// DATE: 2015-01-03
66
// URL: https://github.com/RobTillaart/FunctionGenerator
7-
//
87

98

109
#include "functionGenerator.h"
@@ -33,11 +32,11 @@ void loop()
3332
{
3433
choice = Serial.read();
3534
}
36-
// wave selection by potMeter
35+
// wave selection by potMeter
3736
// int choice = analogRead(A0) / 200;
3837

3938
float value;
40-
// wait for next millisecond;
39+
// wait for next millisecond;
4140
if (millis() - lastTime > 0)
4241
{
4342
lastTime = millis();
@@ -58,5 +57,5 @@ void loop()
5857
}
5958

6059

61-
// -- END OF FILE --
60+
// -- END OF FILE --
6261

examples/functionGeneratorDuoPlot/functionGeneratorDuoPlot.ino

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
// DATE: 2020-06-10
66
// URL: https://github.com/RobTillaart/FunctionGenerator
77
//
8-
// use a Serial plotter to show the data
8+
// use a Serial plotter to show the data
99

1010

1111
#include "functionGenerator.h"
@@ -17,7 +17,9 @@ funcgen gen2;
1717
void setup()
1818
{
1919
Serial.begin(115200);
20-
// Serial.print("Start functionGeneratorPerformance - LIB VERSION: ");
20+
// Serial.println();
21+
// Serial.println(__FILE__);
22+
// Serial.print("FUNCTIONGENERATOR_LIB_VERSION: ");
2123
// Serial.println(FUNCTIONGENERATOR_LIB_VERSION);
2224

2325
gen1.setFrequency(13);
@@ -46,5 +48,5 @@ void loop()
4648
}
4749

4850

49-
// -- END OF FILE --
51+
// -- END OF FILE --
5052

0 commit comments

Comments
 (0)