Hochschule Kempten      
Fakultät Elektrotechnik      
Publications       Fachgebiet Elektronik, Prof. Vollrath      

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 Base100 us/div 200 us/div 500 us/div 1 ms/div 2 ms/div
timeBase124610
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:
PlatformWaveformFrequencies
ArduinoSine20 Hz
ArduinoSine50 Hz
ArduinoSine100 Hz
ArduinoSine200 Hz
ArduinoSine400 Hz
ArduinoSine500 Hz