[Arduino] - Software Phase Locked Loop (SPLL)

Started by earthtonesaudio, May 23, 2013, 03:44:36 PM

Previous topic - Next topic

earthtonesaudio

*UNVERIFIED and UNTESTED* but it does compile on Arduino 1.5.2.

Lots of comments in the code.

/*
Software PLL for Arduino
========================

Author:
EarthTonesAudio

License:
CC - BY
see http://creativecommons.org/licenses/ for more info.

Readme:
State machine code copied from Wikipedia's PLL entry.
Intended for use with Arduino Nano V3.

Be sure to include the header file.  Its contents are as follows:

// header file start
typedef struct PhaseFreqDet {
unsigned char qsig: 1;
unsigned char qref: 1;
unsigned char lsig: 1;
unsigned char lref: 1;
unsigned char direction: 1;
unsigned char sig: 1;
unsigned char ref: 1;
unsigned char rst: 1;
};
// header file end

Save the above as
PLL.h
store it in Arduino/libraries/PLL/
(you have to make this folder yourself and then reboot the Arduino IDE)

Questionable stuff:
1. Rather than right-shifting the step size variable each time, it might
make more sense to implement a real proportional-derivative type of
controller.
2. Test in real life.
3. What sort of timer interrupt interval would be good? 
4. What happens with no input? 
5. What is the bandwidth?
6. What are capture and lock ranges?
*/

#include <PLL.h>
#define LOOPDIV A0
#define LOOPTRAK A1
#define SLOWER 0
#define FASTER 1

/* initialize phase-frequency detector struct */
PhaseFreqDet pf = {0,0,0,0,0,0,0,0};

/* pin numbering is for Arduino Nano V3 */
const int outPin = 11; //output signal pin
const int inPin = 12; //input frequency pin
const int ledPin = 13; //lock indicator LED

/* other variables */
unsigned int timeCounter = 0; //audible period
unsigned int rawOsc = 0x00FF; //internal "VCO" period
unsigned int stepsize = 0;
unsigned char prevDirection = 2;

void setup() {
  pinMode(inPin, INPUT); //apply input square waves here
  pinMode(outPin, OUTPUT); //output here
  pinMode(ledPin, OUTPUT); //lock indicator LED
  digitalWrite(ledPin, LOW); //start with LED off
}
void loop() {
  /*
TODO: use timer ISR instead of loop()
TODO: determine timer frequencies
TODO: setup interrupt for ATMEGA328
*/

  /* increment output oscillator counter */
  timeCounter++; //oscillator period

/* divide oscillator by potentiometer value, but not by zero */
  if (timeCounter > rawOsc/(analogRead(A0) + 1)){

    /* update output */
    pf.ref ^= 1; //toggle reference bit
    digitalWrite(pf.ref, outPin); //toggle physical pin
    timeCounter=0; //reset timeCounter

/* Questionable section 1
I think this part is questionable because it seems like
it would either overshoot each time, or else the step
size would be too small and it would take forever to get
to its destination. Maybe that is just part of PID tuning...? */
    if (prevDirection != pf.direction) {
/* get stepsize value from pot
when frequency overshoots target value */
      stepsize = analogRead(LOOPTRAK);
      prevDirection = pf.direction; //keep track of direction
    }
    else {
      stepsize >>= 1; //divide by 2 each time
    }
/* end Questionable section 1 */
  }

  /* phase-frequency detector state machine */
  pf.sig = digitalRead(inPin); //get input signal
  pf.rst = ~(pf.qsig & pf.qref); //reset flops
//rising edge detect - 'q' output determines UP or DN
  pf.qsig = (pf.qsig | (pf.sig & ~pf.lsig)) & pf.rst;
  pf.qref = (pf.qref | (pf.ref & ~pf.lref)) & pf.rst;
//store current state
  pf.lsig = pf.sig;
  pf.lref = pf.ref;
/* end phase-frequency detector section */

  /*
if Q outputs are not equal:
a) increase VCO speed if input leads feedback
b) decrease VCO speed if input lags feedback
*/
  if (pf.qref != pf.qsig) {
    digitalWrite(ledPin, LOW); //no lock? dark LED.
    if (pf.qref > pf.qsig) {
/* ref leads sig: slow oscillator down */
      rawOsc += stepsize;
      pf.direction = SLOWER;
    }
    else {
/* ref lags sig: speed oscillator up */
      rawOsc -= stepsize;
      pf.direction = FASTER;
    }
  }
  else {
    digitalWrite(ledPin, HIGH); //LED to show phase is locked
  }
}


g_u_e_s_t

please let us know how it goes.  i tried making a pll on the arduino a while ago, and didnt get anything i liked.