I am trying to implement a Phase Locked Loop in a microcontroller STM32. I have a reference signal and feedback signal which has $\phi$ difference wrt reference signal. For the moment, I am not using a real time feedback signal, rather simulating it inside the microcontroller itself, but later extend this to Real time.

  1. I have a timer interrupt to generate a reference signal where sampling rate = 100KHz, and the target frequency = 1KHz.
  2. I used a DDS inside this timer which has a phase accumulator and a LUT for both reference and shifted sine.
  3. In the beginning when no control loop is running, the ref and feedback would be generated.
  4. now I implement a PLL, for that I have a multiplier phase detector and i use averaging integrator to remove the higher frequency components to get error.
  5. Here, I also implement a PI control loop which runs at a slower clock rate compared to the timer interrupt. so I take for example, 50*1 sine period steps. I send the error to the PI control when the triggers starts(as it reaches 5000 steps).
  6. In the PI loop, i calculated error and also the sign of the phase difference if it is laging or leading. Calculating the sign of theta with cos was tricky so I used sign.
  7. now I calculate the PI output and send to the interrupt handler again. The problem that I am facing is, the feedback signal keeps on shifting and when aligns with the reference signal, it is not locked. It starts shifting continuously.

what am I doing wrong here? I assume the thing that i am doing wrong is that I am taking output_val2 from a LUT and it will be changing continuously even if the phases are locked. But I could be wrong.

Also, if want to extend this to a real time, suppose my output_val2 is not from a LUT but directly from a ADC, how do I modify it?

void timer_interrupt(){
static int64_t phase1 = 0;
    static int64_t phase2 = 0;
    static uint64_t temp = 0;
    static int error = 0;

    static int idx1 = 0;
    static int idx2 = 0;
    static int output_val1 = 0;
    static int output_val2 = 0;
    static int p = 0;
  // Check which version of the timer triggered this callback and toggle LED
    if (htim == &htim16 )
        //initializing variables

    //implementing DDS for VCO of PLL

      phase1 += tuning; //the phase for reference signal
      phase2 += tuning+ tuning_word_int;//the phase for feedback signal
      //the tuning_word_int is the correction from PI above. It is zero before the start of control loop
      idx1 = (phase1>> 20) & 0xFFF;   //the index for reference
      idx2 = ((phase2>> 20) & 0xFFF) ; //the index for feedback

      output_val1 = LUT_r[idx1];  //the output value of the indices on LUT
      output_val2 = LUT_s[idx2];
      //start of the phase detector
      //use of multiplier phase detector
      error = output_val1 * output_val2;
      //incrementing error to remove the higher frequency component. It is the mean averaging is done in while loop.
      temp += error;

      //triggers a counter to send signal to the PI controller

      if(p >= 5000-1){  // 5000 is the number of steps for averaging or say to start thr trigger
              counter = temp;
              p = 0;  //resetting the inner counter
              temp = 0; //resetting the temp value


      //waveform display
      //to send the value to the oscilloscope
      HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_1, DAC_ALIGN_12B_R, output_val1);
      HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_2, DAC_ALIGN_12B_R, output_val2);

int main(){
while (1)
      //phase detector part
      //if counter is on, perform the calculation to find the error
      if(counter != 0 ){
          //this is the integrated product from Phase detector
          pd_out = 2 * (counter / (5000 * AMP * AMP)-1);
          if(asin(pd_out) < 0){
              err = -1 * acos(pd_out);
          //phase wrapping, sign test
          err = acos(pd_out);}
          //PI loop
      phase_inc += err/Fs ;

      //output of PI loop in terms of phase error
      phase_error = Kp * err +  Ki * phase_inc;

      //to update the phase accumulator for feedback signal
      tuning_word_int = (int64_t)((phase_error)*(double)(1LL << 31));

    /* USER CODE BEGIN 3 */
  /* USER CODE END 3 */

LUT_s is a shifted sine table. Output https://i.sstatic.net/0ynmQ.jpg

PS: The frequency is also oscillating around the original frequency and not locking.

Update: The algorithm for my current code is algorithm

  • $\begingroup$ you should not have an additional integrator after your multiplier to remove higher frequency component and get error. The multiplier output is the error and the PI loop filter will remove the higher frequency component. If you add an an additional integrator, you will end up with a 3rd order Type 3 loop (3 integrators including the NCO) which will be difficult to stabilize. $\endgroup$ Commented May 5, 2023 at 17:58
  • $\begingroup$ Based on what we had discussions before, i tried to implement the Phase detector just like you said, but for the microcontroller, the DDS was running at a higher speed and we don't want the PI loop to be that fast. in that case, how do we avoid the first integrator in Phase detector? $\endgroup$
    – Rima
    Commented May 7, 2023 at 9:11
  • $\begingroup$ I see, if you are using it to down sample — you can do an “integrate and dump” or moving average and decimate. As long as you don’t integrate or average over multiple samples of the loop update rate you should be ok $\endgroup$ Commented May 7, 2023 at 9:50
  • $\begingroup$ I am not sure if I get what you are saying, but for example, my DDS is running at sampling rate of 100 KHz, and the output signal is at 1 KHz, but the PI loop would get the trigger at suppose 20 KHz. We don't need the PI loop to control at every sample points, rather at certain period of times. This obviously, involves integrating and averaging like the last time we discussed to get error (phi) at every trigger point and calculate PI output. $\endgroup$
    – Rima
    Commented May 7, 2023 at 11:12
  • $\begingroup$ I slightly got modified code which locked the signals quite quickly. I am unsure if this is the correct method, but I could post the correct code tomorrow with the output. $\endgroup$
    – Rima
    Commented May 7, 2023 at 11:13

1 Answer 1


1.- In this link: https://opus.lib.uts.edu.au/bitstream/10453/19598/1/9_sithamparanathan.pdf

Sithamparanathan explains how to do it but no code supplied. Perhaps if you ask by email they will send you their code, to save you time.

2.- This site seems to have all you may need :



Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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