Working Due Sine Wave By Frequency (Please help me understand)

Hey all,

I am trying to simulate the output of a variable reluctance sensor that is typically mounted to read a tone wheel on the differential or axle of a vehicle. This particular signal is typically a sin wave of 4000 pulses per mile. If a vehicle is traveling at 1 mph that is 4000 pulses per hour or 1.11 pulses per sec (1 Hz).

Using my limited coding knowledge, I stripped down the simple waveform generator (https://docs.arduino.cc/tutorials/due/simple-waveform-generator/) as far as I could to hopefully output a sine wave signal from the DAC0 ADC port of my arduino DUE at a commanded frequency (freq).

The details on the generator mention it is sufficient for a frequency range of 1Hz to 170Hz for reasons beyond my understanding. That 170Hz is equivalent to 153 mph if my math is correct and should be fast enough to get us in the range.

The issue is that the waveform looks really funky below 3 hz (freq = 3) in a non interpolated serial plot oscilloscope I made with an arduino uno and it seems to be moving a LOT faster than 1 Hz on the plot when I set freq to 1Hz. That said, here are the codes...

Oscilloscope on Uno:

const int analogPin = A0;
 
void setup() {
  //Setup serial connection
  Serial.begin(9600); 
}
 
void loop() {
  //Read analog pin
  int val = analogRead(analogPin);

  //Write analog value to serial port:
  Serial.write( 0xff );                                                         
  Serial.write( (val >> 8) & 0xff );                                            
  Serial.write( val & 0xff );
}

Waveforms2 Library:

//
//  Waveforms2.h
//  
//
//  "Simple Waveform Genetator with Arduino Due." Accessed: September 20, 2015. <https://www.arduino.cc/en/Tutorial/DueSimpleWaveformGenerator>
//
//


#ifndef _Waveforms2_h_
#define _Waveforms2_h_

#define maxWaveform 1
#define maxSamplesNum 120

static int waveformsTable[maxWaveform][maxSamplesNum] = {
    /*
     
     {  2047, 2154, 2261, 2367
     */
    
    
    // Sin wave
    {
        0x7ff, 0x86a, 0x8d5, 0x93f, 0x9a9, 0xa11, 0xa78, 0xadd, 0xb40, 0xba1,
        0xbff, 0xc5a, 0xcb2, 0xd08, 0xd59, 0xda7, 0xdf1, 0xe36, 0xe77, 0xeb4,
        0xeec, 0xf1f, 0xf4d, 0xf77, 0xf9a, 0xfb9, 0xfd2, 0xfe5, 0xff3, 0xffc,
        0xfff, 0xffc, 0xff3, 0xfe5, 0xfd2, 0xfb9, 0xf9a, 0xf77, 0xf4d, 0xf1f,
        0xeec, 0xeb4, 0xe77, 0xe36, 0xdf1, 0xda7, 0xd59, 0xd08, 0xcb2, 0xc5a,
        0xbff, 0xba1, 0xb40, 0xadd, 0xa78, 0xa11, 0x9a9, 0x93f, 0x8d5, 0x86a,
        0x7ff, 0x794, 0x729, 0x6bf, 0x655, 0x5ed, 0x586, 0x521, 0x4be, 0x45d,
        0x3ff, 0x3a4, 0x34c, 0x2f6, 0x2a5, 0x257, 0x20d, 0x1c8, 0x187, 0x14a,
        0x112, 0xdf, 0xb1, 0x87, 0x64, 0x45, 0x2c, 0x19, 0xb, 0x2,
        0x0, 0x2, 0xb, 0x19, 0x2c, 0x45, 0x64, 0x87, 0xb1, 0xdf,
        0x112, 0x14a, 0x187, 0x1c8, 0x20d, 0x257, 0x2a5, 0x2f6, 0x34c, 0x3a4,
        0x3ff, 0x45d, 0x4be, 0x521, 0x586, 0x5ed, 0x655, 0x6bf, 0x729, 0x794
    }
   
};

#endif

Sine Wave Generator: (I have removed potentiometer but left the comments to see if there is a more elegant way to map or use the freq variable.)

/*

  Simple Waveform generator with Arduino Due


  
 */


#include "Waveforms2.h"


#define oneHzSample 1000000/maxSamplesNum  // sample for the 1Hz signal expressed in microseconds

int wave0 = 0;


int i = 0;

int sample;
int freq = 3;


void setup() {

  analogWriteResolution(12);  // set the analog output resolution to 12 bit (4096 levels)


}


void loop() {

  // Read the the potentiometer and map the value  between the maximum and the minimum sample available

  // 1 Hz is the minimum freq for the complete wave

  // 170 Hz is the maximum freq for the complete wave. Measured considering the loop and the analogRead() time

  sample = map(freq, 0, 4095, 0, oneHzSample); 

  sample = constrain(sample, 0, oneHzSample);


  analogWrite(DAC0, waveformsTable[wave0][i]);  // write the selected waveform on DAC0

i++;

 if(i == maxSamplesNum)  // Reset the counter to repeat the wave

   i = 0;


  delayMicroseconds(sample);  // Hold the sample value for the sample time

}



I created a library for controlling the ADC and DAC on Arduino Due a while ago. It would probably provide better results than using analogWrite() , but its mostly been tested with high frequency waveforms, not so much slow ones.
In any case you can find it here:
AutoAnalogAudio

There is a sine-wave example as well: Auto Analog Audio: SimpleSine12Bit.ino

do you mean a sinusoidal "cycle" when you say pulse or do you mean just a pulse, on/off?

assuming you require low frequency sine wave have a look at

// using timer3 interrupts output sine waves to DAC

#include <DueTimer.h>

// Arduino Due - use DAC0 and DAC1 to generate two sine waves

// from https://forum.arduino.cc/t/how-can-i-get-2-sin-waves-on-the-due-at-same-time-one-out-of-phase-with-other/217778/4

const uint16_t sinTable[] = {
0x7ff, 0x823, 0x846, 0x86a, 0x88e, 0x8b1, 0x8d5, 0x8f9, 0x91c, 0x93f,
0x963, 0x986, 0x9a9, 0x9cc, 0x9ee, 0xa11, 0xa33, 0xa56, 0xa78, 0xa9a,
0xabb, 0xadd, 0xafe, 0xb1f, 0xb40, 0xb60, 0xb81, 0xba1, 0xbc0, 0xbe0,
0xbff, 0xc1e, 0xc3c, 0xc5a, 0xc78, 0xc95, 0xcb2, 0xccf, 0xcec, 0xd08,
0xd23, 0xd3e, 0xd59, 0xd73, 0xd8d, 0xda7, 0xdc0, 0xdd8, 0xdf1, 0xe08,
0xe1f, 0xe36, 0xe4c, 0xe62, 0xe77, 0xe8c, 0xea0, 0xeb4, 0xec7, 0xeda,
0xeec, 0xefe, 0xf0f, 0xf1f, 0xf2f, 0xf3f, 0xf4d, 0xf5c, 0xf69, 0xf77,
0xf83, 0xf8f, 0xf9a, 0xfa5, 0xfaf, 0xfb9, 0xfc2, 0xfca, 0xfd2, 0xfd9,
0xfdf, 0xfe5, 0xfeb, 0xfef, 0xff3, 0xff7, 0xffa, 0xffc, 0xffd, 0xffe,
0xfff, 0xffe, 0xffd, 0xffc, 0xffa, 0xff7, 0xff3, 0xfef, 0xfeb, 0xfe5,
0xfdf, 0xfd9, 0xfd2, 0xfca, 0xfc2, 0xfb9, 0xfaf, 0xfa5, 0xf9a, 0xf8f,
0xf83, 0xf77, 0xf69, 0xf5c, 0xf4d, 0xf3f, 0xf2f, 0xf1f, 0xf0f, 0xefe,
0xeec, 0xeda, 0xec7, 0xeb4, 0xea0, 0xe8c, 0xe77, 0xe62, 0xe4c, 0xe36,
0xe1f, 0xe08, 0xdf1, 0xdd8, 0xdc0, 0xda7, 0xd8d, 0xd73, 0xd59, 0xd3e,
0xd23, 0xd08, 0xcec, 0xccf, 0xcb2, 0xc95, 0xc78, 0xc5a, 0xc3c, 0xc1e,
0xbff, 0xbe0, 0xbc0, 0xba1, 0xb81, 0xb60, 0xb40, 0xb1f, 0xafe, 0xadd,
0xabb, 0xa9a, 0xa78, 0xa56, 0xa33, 0xa11, 0x9ee, 0x9cc, 0x9a9, 0x986,
0x963, 0x93f, 0x91c, 0x8f9, 0x8d5, 0x8b1, 0x88e, 0x86a, 0x846, 0x823,
0x7ff, 0x7db, 0x7b8, 0x794, 0x770, 0x74d, 0x729, 0x705, 0x6e2, 0x6bf,
0x69b, 0x678, 0x655, 0x632, 0x610, 0x5ed, 0x5cb, 0x5a8, 0x586, 0x564,
0x543, 0x521, 0x500, 0x4df, 0x4be, 0x49e, 0x47d, 0x45d, 0x43e, 0x41e,
0x3ff, 0x3e0, 0x3c2, 0x3a4, 0x386, 0x369, 0x34c, 0x32f, 0x312, 0x2f6,
0x2db, 0x2c0, 0x2a5, 0x28b, 0x271, 0x257, 0x23e, 0x226, 0x20d, 0x1f6,
0x1df, 0x1c8, 0x1b2, 0x19c, 0x187, 0x172, 0x15e, 0x14a, 0x137, 0x124,
0x112, 0x100, 0xef, 0xdf, 0xcf, 0xbf, 0xb1, 0xa2, 0x95, 0x87,
0x7b, 0x6f, 0x64, 0x59, 0x4f, 0x45, 0x3c, 0x34, 0x2c, 0x25,
0x1f, 0x19, 0x13, 0xf, 0xb, 0x7, 0x4, 0x2, 0x1, 0x0,
0x0, 0x0, 0x1, 0x2, 0x4, 0x7, 0xb, 0xf, 0x13, 0x19,
0x1f, 0x25, 0x2c, 0x34, 0x3c, 0x45, 0x4f, 0x59, 0x64, 0x6f,
0x7b, 0x87, 0x95, 0xa2, 0xb1, 0xbf, 0xcf, 0xdf, 0xef, 0x100,
0x112, 0x124, 0x137, 0x14a, 0x15e, 0x172, 0x187, 0x19c, 0x1b2, 0x1c8,
0x1df, 0x1f6, 0x20d, 0x226, 0x23e, 0x257, 0x271, 0x28b, 0x2a5, 0x2c0,
0x2db, 0x2f6, 0x312, 0x32f, 0x34c, 0x369, 0x386, 0x3a4, 0x3c2, 0x3e0,
0x3ff, 0x41e, 0x43e, 0x45d, 0x47d, 0x49e, 0x4be, 0x4df, 0x500, 0x521,
0x543, 0x564, 0x586, 0x5a8, 0x5cb, 0x5ed, 0x610, 0x632, 0x655, 0x678,
0x69b, 0x6bf, 0x6e2, 0x705, 0x729, 0x74d, 0x770, 0x794, 0x7b8, 0x7db,
};

int myLed = 13;

bool ledOn = false;
volatile long counter = 0;

void myHandler() {
 static int i = 0, k = 180;
  analogWrite(DAC0, sinTable[i]);
  analogWrite(DAC1, sinTable[k]);
  if (++k >= 360) k = 0;
  if (++i >= 360) i = 0;
  counter++;
}

void setup() {
  pinMode(myLed, OUTPUT);
  Serial.begin(115200);
  Serial.println("Arduino timer3 DAC sime wave generator ");
  Timer3.attachInterrupt(myHandler);
  Timer3.start(2000);  
  pinMode(DAC0, OUTPUT);
  pinMode(DAC1, OUTPUT);
  analogWriteResolution(12);
}

void loop() {
  static long timer1 = millis();
  if (millis() - timer1 >= 5000) {
    Serial.print("Counter ");
    Serial.println(counter);
    timer1 = millis();
    counter = 0;
  }
}

oscilloscope shows a 1.4Hz sine wave
image

look this over to determine the phase of the signal as mph would vary


char buf [90];

char s [10];
char m [10];
char d [10];
char f [10];
char p [10];
char v [10];

unsigned long msec0;
unsigned long msec;

float dSec;
float sec;

float mph;
float freq;
float ph;
float val;

const int N = 200;
float bufSec [N];
float bufVal [N];

void
gen () {
    for (int n = 0; n < N; n++)  {
        msec0   = msec;
        msec    = millis ();
        dSec    = (msec - msec0) / 1000.0;
        bufSec [n] = msec / 1000.0;

        mph     = map(analogRead (A0), 0,1023, 0, 200);
        freq    = (4000.0 / 3600) * mph;
        ph     += dSec * freq;
        bufVal [n] = sin(2 * M_PI * ph);
    }

    for (int n = 0; n < N; n++)  {
        dtostrf (bufSec [n],  9, 6, s);
        dtostrf (bufVal [n],  6, 3, v);

        sprintf (buf, " %s %s", s, v);
        Serial.println (buf);
    }
}

void
setup (void)
{
    Serial.begin (9600);
    gen ();
}

void
loop (void)
{
}

I love what you have done and it looks like a very elegant solution. I can see by sending 1 or 2 to serial you can switch between two mapped frequencies and even better you can change the amplitude with the plus and minus keys but I cant see how to enter a frequency variable like mine does I believe but again I am not an expert at coding!

It is a pulse on and off but rather than being digital square wave like a hall sensor output it is a sin wave as it rises and falls as the magnet contact comes closer to the tooth and leaves.

This looks like a better sine wave than mine for sure! I need to vary the frequency though as the vehicle speed changes.

Did you come up with this code yourself??! If so, you are definitely skilled and I think you are getting at something! I am pretty sure it is the frequency that needs to change not the phase but again could be wrong... I took a bit of physics in highschool and college but am admittedly a bit rusty.

so you mean is a sequence of pulse where the frequency varies with speed

and each pulse is sinusoidal

try the following - enter the required frequency on serial monitor (integer range 1 to 250) - default frequency 100Hz

// using timer3 interrupts output sine waves to DAC

#include <DueTimer.h>

// Arduino Due - use DAC0 and DAC1 to generate two sine waves

// from https://forum.arduino.cc/t/how-can-i-get-2-sin-waves-on-the-due-at-same-time-one-out-of-phase-with-other/217778/4

const uint16_t sinTable[] = {
0x7ff, 0x823, 0x846, 0x86a, 0x88e, 0x8b1, 0x8d5, 0x8f9, 0x91c, 0x93f,
0x963, 0x986, 0x9a9, 0x9cc, 0x9ee, 0xa11, 0xa33, 0xa56, 0xa78, 0xa9a,
0xabb, 0xadd, 0xafe, 0xb1f, 0xb40, 0xb60, 0xb81, 0xba1, 0xbc0, 0xbe0,
0xbff, 0xc1e, 0xc3c, 0xc5a, 0xc78, 0xc95, 0xcb2, 0xccf, 0xcec, 0xd08,
0xd23, 0xd3e, 0xd59, 0xd73, 0xd8d, 0xda7, 0xdc0, 0xdd8, 0xdf1, 0xe08,
0xe1f, 0xe36, 0xe4c, 0xe62, 0xe77, 0xe8c, 0xea0, 0xeb4, 0xec7, 0xeda,
0xeec, 0xefe, 0xf0f, 0xf1f, 0xf2f, 0xf3f, 0xf4d, 0xf5c, 0xf69, 0xf77,
0xf83, 0xf8f, 0xf9a, 0xfa5, 0xfaf, 0xfb9, 0xfc2, 0xfca, 0xfd2, 0xfd9,
0xfdf, 0xfe5, 0xfeb, 0xfef, 0xff3, 0xff7, 0xffa, 0xffc, 0xffd, 0xffe,
0xfff, 0xffe, 0xffd, 0xffc, 0xffa, 0xff7, 0xff3, 0xfef, 0xfeb, 0xfe5,
0xfdf, 0xfd9, 0xfd2, 0xfca, 0xfc2, 0xfb9, 0xfaf, 0xfa5, 0xf9a, 0xf8f,
0xf83, 0xf77, 0xf69, 0xf5c, 0xf4d, 0xf3f, 0xf2f, 0xf1f, 0xf0f, 0xefe,
0xeec, 0xeda, 0xec7, 0xeb4, 0xea0, 0xe8c, 0xe77, 0xe62, 0xe4c, 0xe36,
0xe1f, 0xe08, 0xdf1, 0xdd8, 0xdc0, 0xda7, 0xd8d, 0xd73, 0xd59, 0xd3e,
0xd23, 0xd08, 0xcec, 0xccf, 0xcb2, 0xc95, 0xc78, 0xc5a, 0xc3c, 0xc1e,
0xbff, 0xbe0, 0xbc0, 0xba1, 0xb81, 0xb60, 0xb40, 0xb1f, 0xafe, 0xadd,
0xabb, 0xa9a, 0xa78, 0xa56, 0xa33, 0xa11, 0x9ee, 0x9cc, 0x9a9, 0x986,
0x963, 0x93f, 0x91c, 0x8f9, 0x8d5, 0x8b1, 0x88e, 0x86a, 0x846, 0x823,
0x7ff, 0x7db, 0x7b8, 0x794, 0x770, 0x74d, 0x729, 0x705, 0x6e2, 0x6bf,
0x69b, 0x678, 0x655, 0x632, 0x610, 0x5ed, 0x5cb, 0x5a8, 0x586, 0x564,
0x543, 0x521, 0x500, 0x4df, 0x4be, 0x49e, 0x47d, 0x45d, 0x43e, 0x41e,
0x3ff, 0x3e0, 0x3c2, 0x3a4, 0x386, 0x369, 0x34c, 0x32f, 0x312, 0x2f6,
0x2db, 0x2c0, 0x2a5, 0x28b, 0x271, 0x257, 0x23e, 0x226, 0x20d, 0x1f6,
0x1df, 0x1c8, 0x1b2, 0x19c, 0x187, 0x172, 0x15e, 0x14a, 0x137, 0x124,
0x112, 0x100, 0xef, 0xdf, 0xcf, 0xbf, 0xb1, 0xa2, 0x95, 0x87,
0x7b, 0x6f, 0x64, 0x59, 0x4f, 0x45, 0x3c, 0x34, 0x2c, 0x25,
0x1f, 0x19, 0x13, 0xf, 0xb, 0x7, 0x4, 0x2, 0x1, 0x0,
0x0, 0x0, 0x1, 0x2, 0x4, 0x7, 0xb, 0xf, 0x13, 0x19,
0x1f, 0x25, 0x2c, 0x34, 0x3c, 0x45, 0x4f, 0x59, 0x64, 0x6f,
0x7b, 0x87, 0x95, 0xa2, 0xb1, 0xbf, 0xcf, 0xdf, 0xef, 0x100,
0x112, 0x124, 0x137, 0x14a, 0x15e, 0x172, 0x187, 0x19c, 0x1b2, 0x1c8,
0x1df, 0x1f6, 0x20d, 0x226, 0x23e, 0x257, 0x271, 0x28b, 0x2a5, 0x2c0,
0x2db, 0x2f6, 0x312, 0x32f, 0x34c, 0x369, 0x386, 0x3a4, 0x3c2, 0x3e0,
0x3ff, 0x41e, 0x43e, 0x45d, 0x47d, 0x49e, 0x4be, 0x4df, 0x500, 0x521,
0x543, 0x564, 0x586, 0x5a8, 0x5cb, 0x5ed, 0x610, 0x632, 0x655, 0x678,
0x69b, 0x6bf, 0x6e2, 0x705, 0x729, 0x74d, 0x770, 0x794, 0x7b8, 0x7db,
};

int myLed = 13;

bool ledOn = false;
volatile long counter = 0;

void myHandler() {
 static int i = 0, k = 180;
  //analogWrite(DAC0, sinTable[i]);
  analogWrite(DAC1, sinTable[k]);
  if (++k >= 360) k = 0;
  //if (++i >= 360) i = 0;
  counter++;
}

int frequency = 100;
void setup() {
  pinMode(myLed, OUTPUT);
  Serial.begin(115200);
  Serial.println("Arduino timer3 DAC sime wave generator ");
  Serial.print("sine wave table length ");
  Serial.println(sizeof(sinTable)/sizeof(int16_t));
  Timer3.attachInterrupt(myHandler);
  Timer3.start((int)(1000000/(360.0*frequency)));//10);  
  pinMode(DAC0, OUTPUT);
  pinMode(DAC1, OUTPUT);
  analogWriteResolution(12);
  Serial.println("enter frequency Hz (integer range 1 to 250)");
}

void loop() {
  if(Serial.available()){
    frequency=Serial.parseInt();
    Timer3.start((int)(1000000/(360.0*frequency)));
    while(Serial.available()) Serial.read();
  }
  static long timer1 = millis();
  if (millis() - timer1 >= 1000) {
    Serial.print("Counter ");
    Serial.println(counter);
    timer1 += 1000;
    counter = 0;
  }
}

on startup 100Hz sine wave
image

enter 1 on serial monitor
image

yes!

thanks! I will try....

The frequency is set in the setup() function. In your case I believe you would want to do something like call aaAudio.setSampleRate(32); since the sine wave in the example is 32-steps. Then it would play the entire sine wave over a 1-second period.

Horace thank you! After sourcing the correct library, I compiled this and I think its very close to what I need. It does respond to serial input but it doesn't produce a sine wave on the plotter (see screen shot below). I managed to hook it up to my uno which plots to the serial plotter at 2000000 baud as an oscilloscope with what looks like very nice sine wave output on DAC1 but not on DAC0 which is ok with me. If I change the frequency variable to 10 instead of 100 in the code it messes it up and when I type 1 or 1000 or 10000 in the serial plotter text input it does seem to change something when the code base frequency is set to 100 but doesn't tighten up or relax like yours is.

the output to DAC0 is commented out in my code (DAC0 is burnt out on the Due board I am using)

1 Like

Hey there! I tried modifying this to 32 and it doesn't look anything like a sine wave. I tried 320 as well and nothing looked correct. If I go to 1000 it is sort of sinusoidal as it scrolls across the screen see pic below. If I input 10000 or above it looks good...

horace, does it makes sense if I try to change the base frequency in the code that it messes with the sinusoidal output? I think so far your suggestion is the closest to what I need but it doesn't respond appropriately on my end like it did in your example when I change the input in the serial plotter...

don't know what you mean by base frequency
I set the output sine wave frequency by setting the Timer3 interrupt

void loop() {
  if(Serial.available()){
    frequency=Serial.parseInt();
    Timer3.start((int)(1000000/(360.0*frequency)));

the Timer3.start() sets the number of microseconds the timer interrupt handler is called, e.g.

Timer3.start(50000);         // Calls interrupt handler every 50ms

in the code a sine wave value is output from sinTable[] every Timer3 interrupt, e.g.

void myHandler() {
 static int i = 0, k = 180;
  analogWrite(DAC1, sinTable[k]);
  if (++k >= 360) k = 0;