Jump to content
OMRON Forums

Recommended Posts

Posted

Hello, I'm trying to use PowerPMAC as a high frequency pulse generator through PFM.

I'm using a custom servo loop, where I monitor the positioning error of my motor and calculate what the PFM output frequency should be so that enough pulses are generated for it to reach the desired position in the next servo cycle, saturating at 5 MHz.

Here is the code:

double custom_pfm(struct MotorData *Mptr) {

    // Auxiliar variables
    double servo_out, servo_period, pfm_clk_freq, pos_error;
    unsigned int pfm_clock_div;
    volatile GateArray3 *Gate3;

    // If closed loop is enabled
    if (Mptr->ClosedLoop == 1) {

        // Calculates position error relative to JogPos
        pos_error = ((Mptr->JogPos) - (Mptr->HomePos)) - ((Mptr->ActPos) - (Mptr->HomePos));

        // Calculates PFM clock frequency
        Gate3 = GetGate3MemPtr(0);
        pfm_clock_div = ((Gate3->HardwareClockCtrl) >> 16) & 0b1111;
        pfm_clk_freq = 100e6 / pow(2, pfm_clock_div);

        // Calculates Servo Period
        servo_period = (pshm->ServoPeriod) / 1000;

        // Calculates Servo Out
        servo_out = ((pos_error / servo_period) * 65536) / pfm_clk_freq;

        // Saturates at 5 MHz
        if (servo_out > 3276.8) {
            return 3276.8;
        } else if (servo_out < -3276.8) {
            return -3276.8;
        }

        // Return Servo Out
        return servo_out;
    }

    // Return 0 if closed loop is not enabled
    return 0.0;
}

 

The implementation is working relatively well, but this type of overshoot happens, which I would like to solve:

image.thumb.png.73141d5bd8440c385f19b528a786a9a5.png

 

The EncTable used by the motor is configured like this:

Gate3[0].Chan[0].TimerMode = 3

EncTable[11].Type          = 1
EncTable[11].pEnc          = PowerBrick[0].Chan[0].TimerA.a
EncTable[11].index1        = 0
EncTable[11].index2        = 0
EncTable[11].index3        = 0
EncTable[11].MaxDelta      = 0
EncTable[11].ScaleFactor   = 1/256

 

Why does overshoot happen? I imagined that by calculating exactly the frequency needed to generate the pulses, the behavior of the position curve would be a ramp that stops exactly at the setpoint...

  • Replies 7
  • Created
  • Last Reply

Top Posters In This Topic

Posted

Hello Rafael,

Are you getting the expected value in pfm_clock_div?
It's not clear to me why you are masking with 0b1111, instead of getting the first 4 bits (0xF) after the right-shift getting the bits 16-19 of HardwareClockCtrl.

Posted

Hello Leandro,

Yes, the pfm_clock_div value is correct. Making the mask with 0b1111 or 0xF don't give the same result?

Anyway, my PfmClockDiv is zero (I'm using 100MHz clock on the PFM), so I did the test below and the overshoot keeps happening.

 

// Calculates PFM clock frequency
Gate3 = GetGate3MemPtr(0);
pfm_clock_div = 0;
pfm_clk_freq = 100e6 / pow(2, pfm_clock_div);

 

Posted

Continuing the tests here, I'm coming to the conclusion that it won't be possible to do what I need, because of the fact that the return of the servo loop has an effect on the PFM immediately at the end of the calculation, and not at the instant of the next Servo interruption.

Because of this detail, the calculation that takes ServoPeriod into account is not true, as ServoOut can be updated at any time within the ServoPeriod interval.

And I also believe that it is not possible to measure what that instant was for each Motor, only for the entire Servo cycle (Sys.ServoTime), as this would be possible to deduct such time from the ServoPeriod and adjust the ServoOut.

Is there any register that measures this time between the start of the Servo Interrupt and the end of the loop calculation of motor n?

Or even, a register that configures the controller to only use the new return value of the servo loop in the next Servo Interrupt?

Posted (edited)
22 hours ago, RafaelFalcaro said:

Yes, the pfm_clock_div value is correct. Making the mask with 0b1111 or 0xF don't give the same result?

You're right, I've misread as 0x0b1111.
 

15 hours ago, RafaelFalcaro said:

Or even, a register that configures the controller to only use the new return value of the servo loop in the next Servo Interrupt?

Maybe you can do this using a static variable.
I've never tried to use in a Real-time routine on powerpmac, so, I'm  not sure if work.


If you want to try, it would be something like this:
 

double custom_pfm(struct MotorData *Mptr) {

    // Auxiliar variables
    double servo_out, servo_period, pfm_clk_freq, pos_error;
    static double servo_out_nxt = 0;
    unsigned int pfm_clock_div;
    volatile GateArray3 *Gate3;

    // If closed loop is enabled
    if (Mptr->ClosedLoop == 1) {

        servo_out = servo_out_nxt;

        // Calculates position error relative to JogPos
        pos_error = ((Mptr->JogPos) - (Mptr->HomePos)) - ((Mptr->ActPos) - (Mptr->HomePos));

        // Calculates PFM clock frequency
        Gate3 = GetGate3MemPtr(0);
        pfm_clock_div = ((Gate3->HardwareClockCtrl) >> 16) & 0b1111;
        pfm_clk_freq = 100e6 / pow(2, pfm_clock_div);

        // Calculates Servo Period
        servo_period = (pshm->ServoPeriod) / 1000;

        // Calculates Servo Out
        servo_out_nxt = ((pos_error / servo_period) * 65536) / pfm_clk_freq;

        // Saturates at 5 MHz
        if (servo_out_nxt > 3276.8) {
            return 3276.8;
        } else if (servo_out_nxt < -3276.8) {
            return -3276.8;
        }

        // Return Servo Out
        return servo_out;
    } else {
      	servo_out_nxt = 0;
    }

    // Return 0 if closed loop is not enabled
    return 0.0;
}



 

Edited by leandro.martins
Posted

Thanks for the suggestion, Leandro!

But it didn't work... the oscillation of the graph below happens when I execute a move.

image.thumb.png.2719cd031a6d441d8b5910d39a71e121.png

Also, the 1-cycle delay that the snippet of code you suggested doesn't change the fact that the return from the Servo loop has an effect on the PFM at any instant of the Servo Period, which in my opinion is the big problem.

Looking at the user manual, in the "Phase and Servo Hardware and Software Synchronization" section, I understand that the solution is to parameterize the motor that uses the PFM so that the PowerPMAC commutes it (PhaseCtrl > 0), but I could not define the required registers for this to work fine.

Posted (edited)

Since you want a high-frequency pulse generator, it might help to use PhaseCtrl = 8.

In that way, I suppose that with your servo routine being executed at the phase interruption, obtaining the same effect as a motor being commuted (without closing the current loop).

If I'm right in supposing that, you will need to :

  • Consider the Sys.PhaseOverServoTime in your servo routine.
  • Set your feedback properly to this mode by configuring the Motor[x].pPhaseEnc/PhaseEncLeftShift/PhaseEncRightShift

I hope that helps.

Edited by leandro.martins
  • 4 weeks later...
Posted

If you want a very stable pulse frequency output, please use the open loop command such as #1out10

Since PMAC controls the pulse frequency through the PID algorithm to control the number of pulses, there will be overshoot and frequency jitter.

If you want to reduce overshoot, you can adjust Motor[x].Servo.Kvfb=0;Motor[x].Servo.Ki=0

Adjust Motor[x].Servo.Kp with Motor[x].Servo.Kvff=0, then adjust Motor[x].Servo.Kvff precisely

 

The precise velocity and number of pulses are impossible to obtain at the same time unless you use FPGA hardware circuitry

 

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • Create New...