300 likes | 505 Views
User C Programs in Power PMAC December 2013. C vs. Script Programs. Why write Power PMAC programs and routines in C? Compiled C code runs much faster than interpreted Script code (10-20x) C is a powerful and flexible language Many programmers are already familiar with C
E N D
C vs. Script Programs • Why write Power PMAC programs and routines in C? • Compiled C code runs much faster than interpreted Script code (10-20x) • C is a powerful and flexible language • Many programmers are already familiar with C • Why not write Power PMAC programs and routines in C? • Many engineers are not experienced in C • C is a difficult language for non-programmers • C requires explicit type matching of different variables • C access to I/O registers is less straightforward than in Script • C lacks automatic sequencing and pipelining of Script motion programs
Priorities for C Programs in Power PMAC • Capture/compare interrupt: Fast updating to/from PMAC3 IC • Phase interrupt: “User-written phase” function • Typically for specialized commutation and current-loop algorithms • For non-commutation tasks, use extra motor • Servo interrupt: “User-written servo” function • Typically for specialized feedback and feedforward algorithms • For non-servo tasks, use extra motor • Real-time interrupt: “Real-time C PLC” function • Equivalent of Turbo PMAC PLCC0 • Each background scan: “Background C PLC” functions • Called between each scan of one background Script PLC • 32 separate programs (0 – 31), separately enabled • General-purpose operating system: C programs (applications) • Run as programs (not called functions) on standard computer • Linux GPOS allocates CPU time (when free from interrupt tasks)
Creating C Routines and Programs • Power PMAC IDE provides tool set for implementing C routines • Project manager to organize all C and Script software • Smart text editor for writing software (or pasting in from other source) • GNU C cross-compiler automatically invoked on project “build” • In Project Manager’s “Solution Explorer”, expand “C language” branch • For user-written servo and/or phase, expand “Realtime Routines”, then select “usrcode.c” for editing (multiple routines can be in this file) • For RTI CPLC, expand “CPLCs” and “rticplc”, then select “rticplc.c” for editing • For background CPLCs, click on “CPLCs” and select program number (nn = 0 to 31) in resulting window, creating “bgcplcnn” subfolder and “bgcplcnn.c” file for editing • For background applications, expand “Background Programs”, and add your own files for editing • To compile and download code, right-click on project name, then select “Build and Download All Programs”
Accessing Shared Memory and Structures • All files with C code must start with “#include <gplib.h>” • Header file provides access to shared-memory structures • Access for both “real time” (interrupt-driven) and “general-purpose” (background) code • Functions called by Power PMAC control code (user-written phase and servo, RTI and background CPLCs) automatically have access to several pre-defined pointers • pshm: Pointer to SHM shared-memory data structure • piom: Pointer to I/O memory space • pushm: Pointer to user-defined buffer memory space • Independent background programs must declare variables for these pointers • Structure elements are basically the same as in the Script environment • Case-sensitive names in C (not in Script) • No write-protection in C (but no access to many “internal” elements) • Only full-word elements in I/O structures
C User-Written Servo Routines • Each servo update period, every active motor calls its specified servo routine (built-in or user-written) as function • Each motor can have its own routine • Can provide own name(s) for user-written routine(s) • Routine must return a “double” value for servo command output • Must be in range of +/-32,768.0 • Power PMAC will copy to output or use for commutation • Routine must accept a “MotorData” pointer as argument • Power PMAC will automatically pass pointer to present motor’s structure • Permits same algorithm to be used unchanged for different motors • Provides access to several useful automatically computed floating-point values • DesPos: Net desired position (trajectory + master) • ActPos: Net actual position (including corrections) • PosError: Following error • DesVel: Net desired velocity • ActVel: Net actual velocity
C User-Written Servo Routines (cont.) • Declare in form: “double MyServoAlg(struct MotorData *Mptr)” • IDE creates declaration automatically from user-entered routine name • Use IDE Project Manager to tell which motor(s) use this routine • In Solution Explorer, right-click on “Realtime Routines” under “C Language” • Select “User Servo Setup” to get window to assign routines to motors • Sets Motor[x].Ctrl to UserAlgo.ServoCtrlAddr[i] • Multiple-motor servo algorithms can be implemented • Permits sophisticated handling of dynamic cross-coupling effects • Algorithm to be executed for lowest-numbered of consecutively-numbered motors • Set Motor[x].ExtraMotors to number of additional motors handled here • Can access elements for additional motors in two ways: • Relative addressing: Mptr2 = Mptr + 1; Mptr2->Integrator += Mptr2->PosError; • Absolute addressing: pshm->Motor[7].Integrator += pshm->Motor[7].PosError; • Write to Mptrn->ServoOut for additional motor command outputs
Example User-Written Servo Routine • Very simple PID control algorithm • Uses Delta Tau-defined saved gain elements Kp, Kvfb, Ki double user_pid_ctrl(struct MotorData *Mptr) { double ctrl_effort; if (Mptr->ClosedLoop) { // Servo active in closed loop? ctrl_effort = Mptr->Servo.Kp * Mptr->PosError - Mptr->Servo.Kvfb * Mptr->ActVel; // PD terms Mptr->Servo.Integrator += Mptr->PosError * Mptr->Servo.Ki; // I term ctrl_effort += Mptr->Servo.Integrator; // Combine return ctrl_effort; // Return value for automatic handling } else { // Open loop mode Mptr->Servo.Integrator = 0.0; // Clear integrator return 0.0; // Zero output } }
C User-Written Phase Routines • Each phase update period, every motor with PhaseCtrl > 0 calls its specified phase routine (built-in or user-written) as function • Each motor can have its own routine • Can provide own name(s) for user-written routine(s) • Routine must accept a “MotorData” pointer as argument • Power PMAC will automatically pass pointer to present motor’s structure • Permits same algorithm to be used unchanged for different motors • Can use saved setup elements as for standard algorithm • Must directly access I/O registers • No automatic pre/post-processing as for servo • Must explicitly convert between fixed-point I/O and (recommended) floating-point computations • Have access to Power PMAC’s floating-point math library, even though executing in the real-time kernel
C User-Written Phase Routines (cont.) • Must be declared in form: “void MyPhaseAlg(struct MotorData *Mptr)” • IDE creates declaration automatically from user-entered routine name • Routine must write command values directly to output registers • Can use address set by Motor[x].pDac, as built-in routine does, e.g.: Mptr->pDac[0] = PhaseACmd; Mptr->pDac[1] = PhaseBCmd; • Use IDE Project Manager to tell which motor(s) use this routine • In Solution Explorer, right-click on “Realtime Routines” under “C Language” • Select “User Servo Setup” to get window to assign routines to motors
Example User-Written Phase Routine • Simple sine output commutation routine: void user_phase(struct MotorData *Mptr) { int PresentEnc, PhaseTableOffset; float *SineTable; double DeltaEnc, PhasePos, IqVolts, IaVolts, IbVolts; PresentEnc = *Mptr->pPhaseEnc; // Read new rotor position DeltaEnc = (double) (PresentEnc - Mptr->PrevPhaseEnc); // Compute change and convert to floating-point Mptr->PrevPhaseEnc = PresentEnc; // Store new rotor position for next cycle PhasePos = Mptr->PhasePos + Mptr->PhasePosSf * DeltaEnc; // Scale change to sine table increments and accumulate if (PhasePos < 0.0) PhasePos += 2048.0; // Negative rollover? else if (PhasePos >= 2048.0) PhasePos -= 2048.0; // Positive rollover? PhaseTableOffset = (int) PhasePos; // Table entry index Mptr->PhasePos = PhasePos; // Store in structure for next cycle IqVolts = Mptr->IqCmd; // Get torque (quadrature) command from servo output SineTable = Mptr->pVoltSineTable; // Start address of lookup table IaVolts = IqVolts * SineTable[(PhaseTableOffset + 512) & 2047]; // Compute Phase A command IbVolts = IqVolts * SineTable[(PhaseTableOffset + Mptr->PhaseOffset + 512) & 2047]; // Compute Phase B command Mptr->pDac[0] = ((int) (IaVolts*65536)); // Scale, fix, and output Phase A Mptr->pDac[1] = ((int) (IbVolts*65536)); // Scale, fix, and output Phase B }
Using Matlab/SimulinkTM for Servo Design • Design algorithm graphically in Simulink • Simulate performance with plant model and excitation block • Use “scopes” to analyze performance • Debug and refine until ready to test
Using Matlab/SimulinkTM for Servo Design (cont.) • Replace excitation block(s) and plant model with provided Power PMAC I/O blocks • (One-time) installation of “DELTATAU PPMAC Library” in Simulink Library Browser • (One-time) load of preset “Configuration Parameters” • Invoke Embedded CoderTM “Build Model” action • Bring resulting “.c” and “.h” files into IDE, compile, and download
C Capture/Compare Interrupt Routine • Interrupt comes from PMAC3-style IC • Can be generated on capture or compare event on any channel of IC • User unmasks any or all interrupt sources from IC(s) • This interrupt is highest priority in Power PMAC • Can suspend phase and servo calculations • Can execute at 60 kHz+ frequency • Essential to keep interrupt service routine short! • Mainly intended to update to/from list of positions in memory • Usually list is in user shared memory buffer • Must be declared as void CaptCompISR (void); • Must be placed in usrcode.c file • No floating-point math permitted (cannot use P & Q variables!) • Enabled if UserAlgo.CaptCompIntr = 1
Sample Capture/Compare ISR • Interrupt Service Routine C code to log captured positions void CaptCompISR (void) { volatile GateArray3 *MyFirstGate3IC; // ASIC structure pointer int *CaptCounter; // Logs number of triggers int *CaptPosStore; // Storage pointer MyFirstGate3IC = GetGate3MemPtr(0); // Pointer to IC base CaptCounter = (int *)pushm + 65535; // Sys.Idata[65535] CaptPosStore = (int *)pushm + *CaptCounter + 65536; // Sys.Idata[65536+Counter] *CaptPosStore = MyFirstGate3IC->Chan[0].HomeCapt;// Store in array (*CaptCounter)++; // Increment counter MyFirstGate3IC->IntCtrl = 1; // Clear interrupt source } • Script commands to set up ISR Gate3[0].IntCtrl = $10000 // Unmask PosCapt[0] Sys.Idata[65535] = 0 // Initialize counter UserAlgo.CaptCompIntr = 1 // Enable capture/compare ISR
Real-Time Interrupt C PLC Routine • Each real-time interrupt (Sys.RtIntPeriod+1 servo interrupts), Power PMAC calls “realtimeinterrupt_plcc” routine if UserAlgo.RtiCplc = 1 • Note that if tasks from previous RTI are not finished, can “skip a beat” • Routine must be in file rticplc.c in folder rticplc under CPLCs and C Language in project • Routine must be declared as “void realtimeinterrupt_plcc()” • Do not put within indefinite loop – Power PMAC causes repeated execution by repeated calls • Use a “sleep” function instead to suspend
Background C PLC Routines • After each scan of one background Script PLC, Power PMAC calls each background C PLC routine n as function if its UserAlgo.BgCplc[n] = 1 • Up to 32 background C PLC routines (n = 0 to 31) • Routine for C PLC n must be in file bgcplcnn.c in folder bgcplcnn under CPLCs and C Language in project • Each routine must be declared as “void user_plcc()” (routines are distinguished by file name and folder) • These background C PLC routines are equivalent to background compiled PLC programs in Turbo PMAC • Next background Script PLC will not run until C PLCs are finished, or 100 μsec later, whichever is less • Do not put within indefinite loop – Power PMAC causes repeated execution by repeated calls • Use a “sleep” function instead to suspend
Example C PLC Routine • Sample program to alternate outputs on UMAC I/O Card #include <RtGpShm.h> #include <stdio.h> #include <dlfcn.h> #define IoCard0Out0_7 *(piom + 0xA0000C/4) #define IoCard0Out8_15 *(piom + 0xA00010/4) #define IoCard0Out16_23 *(piom + 0xA00014/4) #define OutputData(x) (x << 8) void user_plcc() // Background C PLC function { static int i = 0; if (i++ > 1000) { // > 1 sec from cycle start IoCard0Out0_7 = OutputData(0xAA); // Odd-numbered outputs on IoCard0Out8_15 = OutputData(0xAA); IoCard0Out16_23 = OutputData(0xAA); if (i > 2000) i = 0; // Reset to start of cycle } else { // < 1 sec from cycle start IoCard0Out0_7 = OutputData(0x55); // Even-numbered outputs on IoCard0Out8_15 = OutputData(0x55); IoCard0Out16_23 = OutputData(0x55); } }
“CfromScript” Subroutine • Permits any Script program to call a C function as a subroutine • Useful when need flexibility of C • Useful when need high-speed computation of C • Mainly used for intensive kinematic calculations • C function must be declared as:double CfromScript (double arg1, double arg2, … , double arg7, LocalData *Ldata) • Script program command must invoke function with 7 double arguments and accept returned double value – e.g.:Test = CfromScript (CSNum, Mode, 0, 0, 0, 0, 0); • Pointer to local variable structure passed automatically • Only single function of this type permitted in Power PMAC • Logic inside can allow calling this function for multiple purposes
Background C Application Programs • Generic applications that run under Linux GPOS (not real-time) • Applications are separate programs, not functions/subroutines (but can call their own functions/subroutines) • Can be completely independent of Power PMAC dedicated tasks • Execution and scheduling are functions of Linux OS settings • Can have access to Power PMAC shared memory structures • Must use “#include <RtGpShm.h>” to access header file with definitions • Must explicitly declare variables to access structures, e.g.: • volatile struct SHM *pshm; (“volatile” specifies re-read for every access) • (This is unlike C routines called as Power PMAC functions)