1
#define LIN_OUT 1   // Include the resources for the linear output function
#define Adafruit_ZeroFFT 256  // Sets the ZeroFFT length to 256 points
#define MERR 10     // Number of error values to store in elist[MERR]
#define PID_ON 1    // Use PID feedback or not
#define TRI_PERIOD 100 // Half-period of triangle wave in loops
#define F_BLACKMAN 0.02463994238109641755656975202572 // Osillation freq. for Blackman filter
#define ARM_MATH_CM0PLUS    
#include <arm_common_tables.h>
#include <arm_math.h>
#include <Adafruit_ZeroFFT.h>
#include <SPI.h>

// Photodiode array variables

byte CLKpin = 4;    // Arduino pin delivering the clock pulses to pin 3 (CLK) of the TSL1402R
byte SIpin = 5;     // Arduino pin delivering the SI (serial-input) pulse to pin 2 of the TSL1402R
byte AOpin0 = A1;    // Arduino pin connected to pin 4 (analog output 1)of the TSL1402R
byte AOpin1 = A2;    // Arduino pin connected to pin 8 (analog output 2)of the TSL1402R

// Variables for determining the displacment from the ZeroFFT

int maxindex;       // Index of the maximum of the ZeroFFT
float maxvalue;     // For comparing the values of the ZeroFFT to find the global maximum
double kmax;        // Spatial wavenumber corresponding to the dominant fringe modulation Fourier component
float Am;           // Real part of Fourier coefficent corresponding to kmax
float Bm;           // Imag part of Fourier coefficent corresponding to kmax
float theta;        // Phase angle of Fourier transform at kmax
float preTheta = 0; // Storage for previous value of phase angle
int Nwrap = 0;      // Number of phase wraps
float displacement; // Displacement in nanometers
float predisplacement; // Storage for initial value of displacement
int w = 0;
int prevw = 0;
int wdiff = 0;

// PID Variables

unsigned long lastTime;
double Output=0;  // Output value for PID (voltage to the piezoelectric actuator (in millivolts))
double Setpoint;  // Desired value of displacement (in nm)
double kp = 0 / 1;    // 0 proportional gain for PID
double ki = 2/1;  // (1000 / (138.5)) integral gain for PID
//double ki = 2/ 1;  // (1000 / (138.5)) integral gain for PID
double kd = 0/ 100;    // 0 derivative gain for PID
double error;     // Error signal; difference between setpoint and displacement (in nm)
double preverror; // Stores previous value of error for derivative control
float esum;       // Integral of error signal over time
float D;          // Derivative of error signal
unsigned long time0 = 0; // Used in calculating the derivative term
float MSerror = 0;  // Mean squared error
float RMSerror = 0; // Root mean squared error
float elist[MERR];// Array of last MERR error values
int jerr = 0;        // index of integral error term
float runningRMSerror = 0; // RMS error calc'd from elist

float m = 0;      // extra variables for one time calculations
float pchange = 0;
float lastp = 0;
float k = 1;
int p = 1;
float M = 0;
//int switchtime = 1200; // Time (in ms) between Setpoint switches

void setup() {
  Serial.begin(9600); // Initialize serial interface to computer (i.e. serial monitor or plotter)
  INIT_PHOTO();       // Initialize the photodiode array
  READ_PHOTO();       // Read the photodiode array
  DO_ZeroFFT();           // Calculate the ZeroFFT of the photodiode array data
  FIND_PEAK_INDEX();  // Determine index of peak in ZeroFFT
  KMAX();             // Calculate spatial wavevector of the interferogram
  INIT_PHOTO();       // Maybe could be replaced by START_EXPOSURE()
  READ_PHOTO();       // Read photodiode again in preparation for next step
  PHASE_ANGLE();      // Calculate the complex phase angle of the ZeroFFT peak
  DISPLACEMENT();     // Calculate the displacement from the phase angle
  predisplacement = displacement; // Save the initial displacement corresponding to center of PZT range
  Setpoint = 0;       // Define the initial setpoint
  delay(100);         // Allow things to settle before beginning loop
  START_EXPOSURE();   // Turn on the photodiode array and begin integrating
}

void loop() {
  KMAX();                 // Calculate kmax from previous cycle's ZeroFFT
  READ_PHOTO();           // Read photodiode array
  PHASE_ANGLE();          // Calculate complex phase angle corresponding to kmax peak
  WRAPPING();            // Determine if a phase wrap occurred
  DISPLACEMENT();        // Calculate the displacement from the unwrapped phase
  DO_ZeroFFT();              // Calculate the ZeroFFT of the photodiode array data
  FIND_PEAK_INDEX();       // Determine index of peak in ZeroFFT
  //OSC_SET();             // Oscillate the setpoint up and down
  PID_LOOP();              // Perform feedback to follow the setpoint (if PID_ON is set to 1)
  //OSC_DAC();               // Send an oscillating voltage to the DAC
  //TRI_DAC();              // Send a triangular wave voltage to the DAC
  START_EXPOSURE();          // Turn on the photodiode array and begin integrating for next cycle
  SERIAL_PRINT();               // Print some outputs to the serial USB port
}

// Photodiode functions 

void ClockPulse() { // For sending clock pulses during reading of photodiode
  delayMicroseconds(1);
  digitalWrite(CLKpin, HIGH);
  digitalWrite(CLKpin, LOW);
}

void INIT_PHOTO() { // Intialization of photodiode
  // Initialize two Arduino pins as digital output:
    pinMode(CLKpin, OUTPUT);
    pinMode(SIpin, OUTPUT);
  // To set up the ADC, first remove bits set by Arduino library, then choose

    for ( int i = 0; i < 14; i++ )
    {
      digitalWrite(i, LOW);
    }
  // Clock out any existing SI pulse through the ccd register:
    for (int i = 0; i < 260; i++)
    {
      ClockPulse();
    }
  // Create a new SI pulse and clock out that same SI pulse through the sensor register:
    digitalWrite(SIpin, HIGH);
    ClockPulse();
    digitalWrite(SIpin, LOW);
    for (int i = 0; i < 260; i++)
    {
      ClockPulse();
    }
}

void READ_PHOTO() { // Read the photodiode

  // Stop the ongoing integration of light quanta from each photodiode by clocking in a SI pulse
  // into the sensors register:
  digitalWrite(SIpin, HIGH);
  ClockPulse();
  digitalWrite(SIpin, LOW);

  // Next, read all 256 pixels in parallel. Store the result in the array. Each clock pulse
  // causes a new pixel to expose its value on the two outputs:
  for (int i = 0; i < 128; i++)
  {
    delayMicroseconds(20);  // We add a delay to stabilize the AO output from the sensor
    ZeroFFT_input[2 * i] = analogRead(AOpin0);        // Output of first half of photodiode array
    ZeroFFT_input[2 * i + 1] = 0;                     // Store zero in the "imaginary" part of the array
    ZeroFFT_input[2 * i + 256] = analogRead(AOpin1);  // Output of second half of photodiode array
    ZeroFFT_input[2 * i + 257] = 0;                   // Store zero in the "imaginary" part of the array
    ClockPulse();
  }

  // Next, AGAIN stop the ongoing integration of light quanta from each photodiode 
  // by clocking in a SI pulse. This is necessary because during the readout operation,
  // the 18th clock pulse starts the integration of light intensity again.
  // This way we can turn on the integration when we want using the START_EXPOSURE() command.
  digitalWrite(SIpin, HIGH);
  ClockPulse();
  digitalWrite(SIpin, LOW);

  // The photodiode remains OFF after this function completes.
  // Call START_EXPOSURE() to turn on the photodiode array and begin integrating.
}

void START_EXPOSURE() { 
  // The photodiode array starts integrating once 18 clock pulses have passed. 
  // At that time, the photodiodes are once again active. We clock out the SI pulse through
  // the 256 bit register in order to be ready to halt the ongoing measurement at our will
  // (by clocking in a new SI pulse):
  // To be used after all calculations are finished, but 
  // before the next photodiode read operation.
  for (int i = 0; i < 260; i++)
  {
    if (i == 18)
    {
      // Now the photodiodes goes active..
      // An external trigger can be placed here
    }
    ClockPulse();
  }
  // Uncomment the next line to increase the integration time.
  //delay(15);// <-- Add 15 ms integration time
}

//Calculation Functions 

void DO_ZeroFFT() {  // Performs the ZeroFFT
  ZeroFFT_reorder();  // Reorders the ZeroFFT_input array to allow the ZeroFFT_run() command to be used
  ZeroFFT_run();      // This is the function that actually does the ZeroFFT
  ZeroFFT_mag_lin();  // Produces a variable ZeroFFT_lin_out that contains the absolute values of the ZeroFFT, 128 elements long.
}

void FIND_PEAK_INDEX() { // Finds the index of the major peak in the ZeroFFT
  maxvalue = 0;
  maxindex = 0;
  for (int i = 5; i < 128; i++) // Go through each element of the ZeroFFT (skip the DC peak)
  {
    if (ZeroFFT_lin_out[i] > maxvalue) 
    // If the magnitude is larger than the previously seen maximum...
    {
      maxvalue = ZeroFFT_lin_out[i]; // ...store the new max...
      maxindex = i;              // ...and the index at which it occurs.
    }
  }
}

void KMAX() { 
  // Calculates the spatial wavevector of the interferogram corresponding 
  // to the peak in the ZeroFFT.
  kmax = 2 * PI / 256 * maxindex;
}

void PHASE_ANGLE() { // Calculates the complex phase angle of the ZeroFFT peak
  // NOTE: This function must be called BEFORE the ZeroFFT is calculated.
  Am = 0;
  Bm = 0;
  for (int i = 0; i < 256; i++) // Calculate the real and imag Fourier components
  {
    Am = Am + float ((ZeroFFT_input[2 * i]) * cos(kmax * i)); //magnitude of real part
    Bm = Bm + float ((ZeroFFT_input[2 * i]) * sin(kmax * i)); //magnitude of imaginary part
  }
  theta = atan2(Bm, Am); // Complext phase angle (in radians)
}

void WRAPPING() { // Determines if a phase wrap/unwrap occured and accounts for it
  if ((preTheta ) > (0.5 * PI) && (theta) < (-0.7 * PI)) {
    // Compare the previous theat to the current theta to see if a wrap occured.
    Nwrap = Nwrap + 1; // If so, add 1 to the number of wraps.
  }
  if ((preTheta) < (-0.7 * PI) && (theta) > (0.5 * PI)) {
    // If an unwrap occured (a phase wrap in the other direction)...
    Nwrap = Nwrap - 1; // ...then subtract 1 from the number of wraps.
  }
  preTheta = theta; // Store the current theta for the next cycle
}

void DISPLACEMENT() { // Calculate the displacement after the phase angle is determined
  m =   2 * PI * Nwrap;
  //M = theta + m;
  displacement = ((-(theta + m) * 632) * (1 / (4 * PI)) * (1 / sqrt(2))) - predisplacement;
  //displacement = ((-(theta + m) * 632) * (1 / (4 * PI)) * (0.80)) - predisplacement;
}

// Servo Functions

void PID_LOOP() { // Performs feedback signal for PID control
  error = Setpoint - displacement;

  // Store the current error signal; overwrite if necessary
  if( jerr >= MERR ){
    jerr = 0;
  }
  elist[jerr] = error;
  jerr++;

  // Calculate the running RMS error
  runningRMSerror = 0;
  for(int i=0; i<MERR; i++) {
    runningRMSerror += pow(elist[i],2);
  }
  runningRMSerror = sqrt(runningRMSerror/MERR);

  // Calculate the mean-squared and root-mean-square errors over all time
  MSerror = (k - 1) / k * MSerror + pow(error, 2) / k; 
  RMSerror = sqrt(MSerror);
  k = k + 1;

  // Integral of error over all time
  esum = esum + error;

  // Derivative of error
  time0 = millis();
  D = (error - preverror) / (time0 - lastTime);

  // The feedback signal (in millivolts)
  // The minus signs are to make sure the piezo moves in the right direction
  Output = -(kp * error) -(ki * esum) -(kd * D);

  // Send the feedback to the DAC if PID_ON is 1
  /*if (PID_ON) {
    val = offset + Output;
    val = max(val, 0);    // Constrain the output voltage so that is stays in range 
    val = min(val, 4096);
    DAC_TRANSFER();
  }

  // Save the current time and error for calc'ing the derivative next cycle
  lastTime = time0;
  preverror = error;*/
}

void SERIAL_PRINT(){

  Serial.print(displacement);
  Serial.print(" , ");
  Serial.println(millis());
  Serial.print(" ") ; 
}

Hi every one. I tried to compile this code for the Arduino Zero which was originally written for the Arduino Uno. I got the following errors. What am I doing wrong?

Error:

/Users/RAJU/Documents/Documents/All Folders/Arduino 2017_v03_Maintable/Arduino-zero/DIS_SET_V04_Arduino-zero/DIS_SET_V04_Arduino-zero.ino: In function 'void READ_PHOTO()':
DIS_SET_V04_Arduino-zero:144: error: 'ZeroFFT_input' was not declared in this scope
     ZeroFFT_input[2 * i] = analogRead(AOpin0);        // Output of first half of photodiode array
     ^
/Users/RAJU/Documents/Documents/All Folders/Arduino 2017_v03_Maintable/Arduino-zero/DIS_SET_V04_Arduino-zero/DIS_SET_V04_Arduino-zero.ino: In function 'void DO_ZeroFFT()':
DIS_SET_V04_Arduino-zero:186: error: 'ZeroFFT_reorder' was not declared in this scope
   ZeroFFT_reorder();  // Reorders the ZeroFFT_input array to allow the ZeroFFT_run() command to be used
                   ^
DIS_SET_V04_Arduino-zero:187: error: 'ZeroFFT_run' was not declared in this scope
   ZeroFFT_run();      // This is the function that actually does the ZeroFFT
               ^
DIS_SET_V04_Arduino-zero:188: error: 'ZeroFFT_mag_lin' was not declared in this scope
   ZeroFFT_mag_lin();  // Produces a variable ZeroFFT_lin_out that contains the absolute values of the ZeroFFT, 128 elements long.
                   ^
/Users/RAJU/Documents/Documents/All Folders/Arduino 2017_v03_Maintable/Arduino-zero/DIS_SET_V04_Arduino-zero/DIS_SET_V04_Arduino-zero.ino: In function 'void FIND_PEAK_INDEX()':
DIS_SET_V04_Arduino-zero:196: error: 'ZeroFFT_lin_out' was not declared in this scope
     if (ZeroFFT_lin_out[i] > maxvalue) 
         ^
/Users/RAJU/Documents/Documents/All Folders/Arduino 2017_v03_Maintable/Arduino-zero/DIS_SET_V04_Arduino-zero/DIS_SET_V04_Arduino-zero.ino: In function 'void PHASE_ANGLE()':
DIS_SET_V04_Arduino-zero:217: error: 'ZeroFFT_input' was not declared in this scope
     Am = Am + float ((ZeroFFT_input[2 * i]) * cos(kmax * i)); //magnitude of real part
                       ^
exit status 1
'ZeroFFT_input' was not declared in this scope
/Users/RAJU/Documents/Arduino/libraries/Adafruit_Zero_FFT_Library/examples/mic_tft/mic_tft.ino
/Users/RAJU/Documents/Arduino/libraries/Adafruit_Zero_FFT_Library/examples/normalized/normalized.ino
  • Are you sure, that this really compiled for an Arduino Uno? The used library already is for the Arduino Zero, which is totally different from the Uno. Where did you get this code from? The array ZeroFFT_input has to be defined somewhere, that the error. – chrisl Jul 3 '18 at 15:10
  • That's true. Sorry for the confusion. This code compiles for the Arduino Uno if I replaced all Zero_FFT by FFT. Obviously I have to comment out the following lines of syntax to make it working for the Arduino Zero: #define Adafruit_ZeroFFT 256 #define ARM_MATH_CM0PLUS #include <arm_common_tables.h> #include <arm_math.h> #include <Adafruit_ZeroFFT.h> And I have to add one more library <FFT.h> to make it work for the Arduino Uno. If you want I will post the code which works for the Arduino Uno. – Raju KC Jul 3 '18 at 15:15
2

The Adafruit ZeroFFT library is not a drop in replacement for the FFT Library.

You can't easily use the same code for both.

  • Do you have any suggestions? – Raju KC Jul 3 '18 at 17:25
  • Hi Craig did you have any idea how does the FFT library work in the Arduino Zero? – Raju KC Jul 3 '18 at 22:42
  • No, I have never used it. I suggest reading the source code and the examples. – Craig Jul 9 '18 at 15:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.