Arbitrary Waveform Generator in NodeEEBench
Jörg Vollrath, University of Applied Science Kempten, Germany, Joerg.vollrath@hs-kempten.de
May, 2024
Overview
To Do List
|
|
Task
Each system has a sample rate (fs, ts) for the AWG a minimum voltage Vmin, a maximum voltage Vmax
and a resolution NABit.
At the moment the system-server-client interface uses 16-Bit values.
At the moment sine, rectangle, and triangle waveforms are supported.
The interface was optimized for the FPGA to minimize FPGA sine calculations.
Sine Waveform
A "S" command is used.
S<step><amplitude><offset>
Example f = 1 kHz, amp = 1 V, offset = 1 V as shown in AWG1: S0000A7C526C9B26C26C9B26C
step = 0000A7C5, amplitude = 26C9B26C, offset = 26C9B26C
Based on FPGA R2R output range from 0 V to 3.3 V with 16-Bit and a sample frequency of 100MHz (T=10ns).
VHDL code:
entity sineX is
Port (
CLK : in STD_LOGIC;
RST: in STD_LOGIC;
step: in STD_LOGIC_Vector(31 downto 0); -- increment
amplitude: in STD_LOGIC_Vector(31 downto 0); -- signal amplitude
offset: in STD_LOGIC_Vector(31 downto 0); -- signal offset
mySine: out STD_LOGIC_Vector(31 downto 0)
);
end sineX;
From 'step' (in degree) a real and imaginary part (StepRe, StepIm) are calculated for
complex calculation of next complex sine value (X):
Xi+1 =
Xi ·
Step
Maximum 32-Bit: 0x40000000 = 1024 * 1024 * 1024
Sine Arduino
The Arduino loop looks first for a command.
With no command Analog values are generated for sine and triangle
using a stepIndex in the range from 0 up to 4095 for sine signal.
The active value is then written to the internal ADC, PMOD AD2 and R2R DAC.
Then ADC values are read from internal ADC 0,1,2 and PMOD AD2 and stored in bufVal[bufIndex].
cntV is incremented and data is sent if cntV = bufSize.
ADC and DAC sampling is always done with maximum sampling rate.
timeBase could be used to reduce ADC sample rate.
stepS = hexToDec(myString.substring(2,6)); // 16 bit, 4 hex; from 32 bit value, 8 hex
ampS = hexToDec(myString.substring(9,13))/8; // 12 bit, 3 hex; from 32 bit value, 8 hex
offS = hexToDec(myString.substring(17,21))/8; // 12 bit, 3 hex; from 32 bit value, 8 hex
awgX = (int)(offS) + (int)(ampS) * sin( TWO_PI * stepIndex * 5 * stepS / 256 / 256); // 256 steps per cycle
At the moment the sample time is 780 us.
Maximum samples in the buffer can be 2048.
Tmax = 1024 * 780 us = 780 ms; fmin = 1.2 Hz
Tmin8 = 8 * 780 us = 6.24 ms; f8max = 160 Hz
Tmin4 = 4 * 780 us = 3.12 ms; f4max = 320 Hz
Tmin2 = 2 * 780 us = 1560 us; f2max = 641 Hz
Sine AWG NodeEEBench.html
The sine generator is synchronized to the acquisition to allow a FFT without bleeding.
| Time Base | 100 us/div | 200 us/div | 500 us/div | 1 ms/div | 2 ms/div |
| timeBase | 1 | 2 | 4 | 6 | 10 |
The oscilloscope has a 'baseVal' (example: 200 us/div). This gives a display of 10 * baseVal.
timeBase = 'baseVal'/200E-6
var timeX = (timeBase + 1)/4;
There are dataMax/2 sampled points displayed and should contain an odd/prime number of sine periods.
There are nSample DAC samples (tSD) per ADC sample (tSA).
nSample = tSA/tSD
The frequency for each sine sample calculation is
fCalc = 10 ns = 1E-8
The signal frequency comes from id="frequencyVal" and is stored in frequency.
frequency = "frequencyVal"
Maximum sine frequency should have 8 points (2 Shannon) per period to make a nice picture.
fmax = 1 / tSD / 8
Minimum sine frequency is limited by the step size.
fmin = 1 / tsd / stepS
function genCmdAWG() {
var boardX = parseInt(document.getElementById("board").value);
if (boardX == 0) {
// document.getElementById("timeSampling").value = 8.32E-6;
nSample = 13 * 128; // 8.32us / 10 ns * 2 FGA = Math.trunc(8.32E-6/10E-9*2)
maxC = 32767;
} else if (boardX == 2) {
// document.getElementById("timeSampling").value = 360E-6;
nSample = 72000 * 3; // 360us / 10 ns * 2 Arduino Maker WiFi = Math.trunc(360E-6/10E-9 * 2 * 3)
maxC = 4095;
}
cmd = "S"; // Sine
ampC = Math.trunc( (2*1024*1024*1024-1) / maxV * amp); // Range 0.. 2^30
offC = Math.trunc( (2*1024*1024*1024-1) / maxV * off);
if (offC >= 2*1024*1024*1024) { offC = 2*1024*1024*1024-1; }
if ((offC + ampC) >= 2*1024*1024*1024) { ampC = 2*1024*1024*1024-1-offC; }
if ((offC - ampC) < 0) { ampC = offC; }
// correct step with better frequency for good fft
// timeBase = 4 fft good 256 samples, 1 ms/div time base oscilloscope
var timeX = (timeBase + 1)/4; // (timeBase + 1)/3; //
// fix nCycle to odd (prime)
var nCycle = Math.round(dataMax / fCalc * timeX * nSample * frequency); // number of cycles
if ((nCycle % 2) == 0) { nCycle = nCycle + 1; }
var frequency1 = nCycle * fCalc / dataMax / nSample / timeX; // odd corrected frequency
frequency = nearestPrime(nCycle) * fCalc / dataMax / timeX / nSample; // prime corrected frequency
infoX = infoX + " is mapped to " + valueToUnit(frequency1)
+ " prime " + valueToUnit(frequency) + " <br>:\n"
+ " Cycles " + nCycle ;
// end correct step size
step = Math.trunc(1024 * 1024 * 8 / fCalc * nSample * frequency);
Pulse Waveform
Arduino
NodeEEBench.html
Triangle Waveform
Arduino
NodeEEBench.html
Staircase Waveform
Arduino
NodeEEBench.html
Sawtooth with Lookup Table Waveform
Arduino
NodeEEBench.html
Test
Maximum frequency:
Minimum frequency:
| Platform | Waveform | Frequencies |
| Arduino | Sine | 20 Hz |
| Arduino | Sine | 50 Hz |
| Arduino | Sine | 100 Hz |
| Arduino | Sine | 200 Hz |
| Arduino | Sine | 400 Hz |
| Arduino | Sine | 500 Hz |