home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 15 / 15.iso / s / s300 / 1.ddi / CHAP4 / CASCADE1.C next >
Encoding:
C/C++ Source or Header  |  1988-05-02  |  21.9 KB  |  747 lines

  1. /*********************************************************************
  2.  
  3. FILE
  4.     cascade1.c  -  position and velocity control using cascaded PID
  5.  
  6. ENTRY ROUTINES
  7.     control  -  the cascade controller
  8.  
  9.     pgain    -  get position gains
  10.     vgain    -  get velocity gains
  11.     smpset   -  set sampling interval
  12.  
  13.     getv     -  get velocity
  14.     getp     -  get position
  15.  
  16. PRIVATE ROUTINES
  17.     initpid  -  initialize cloop fields
  18.     pid      -  perform PID control
  19.  
  20. INITIALIZATION ROUTINE
  21.     init_cascade  -  allocate and initialize data structures
  22.  
  23. REMARKS
  24.     This module implements the PID cascade control configuration of 
  25.     figure 3.7 in the book.  This implementation differs from the
  26.     previous cascade0.c in that the velocity and position control 
  27.     loops are described by a special data structure of type PIDLOOP.
  28.  
  29.     Representing each control loop by a structure variable of type
  30.     PIDLOOP leads to a more elegant and flexible control system.  It
  31.     allows one to easily implement complex and deeply nested cascade 
  32.     control systems by merely connecting the various control loops
  33.     through the main control program.
  34.  
  35.     In this demonstration program, the two loops are connected by 
  36.     control() such that the input to the innner velocity loop is the 
  37.     output of the outer position loop.
  38.  
  39.     This file contains only the control implementation.  You have
  40.     to implement the user interface and perhaps the simulation
  41.     module.  Please refer to cntrl1.c and rtsim1.c for examples of 
  42.     user interface and simulation modules.
  43.  
  44. LAST UPDATE
  45.     20 March 1988
  46.         check number of fields converted by sscanf()
  47.  
  48. ***********************************************************************/
  49.     
  50. /***********************************************************************
  51.                             I M P O R T S
  52. ***********************************************************************/
  53.  
  54. #include <stdio.h>
  55. #include <stdlib.h>     /* standard library function declarations */
  56. #include <string.h>     /* standard string function declarations */
  57.  
  58. #include "envir.h"      /* environment definitions */
  59. #include "dash8.h"      /* declarations for dash8.c */
  60. #include "dac2.h"       /* declarations for dac2.c */
  61. #include "time0.h"      /* declarations for time0.c */
  62. #include "pidloop.h"    /* PIDLOOP data strcuture declarations */
  63. #include "cascade1.h"   /* export declarations for this module */
  64.  
  65. /***********************************************************************
  66.                 F O R W A R D    D E C L A R A T I O N S
  67. ***********************************************************************/
  68.  
  69. #ifdef ANSI
  70.  
  71. static double getv(void);           /* get velocity */
  72. static double getp(void);           /* get position */
  73. static void   vmact(void);          /* send velocity control output to actuator */
  74. static void   pid(PIDLOOP *);       /* basic PID control */
  75. static void   init_cascade(void);   /* initialize this module */
  76. static void   init_pid(PIDLOOP *, char *, int, int, double, double, double,
  77.                 double, double, double, double, double, double);
  78.  
  79. #else   /* no function prototypes */
  80.  
  81. double getv();
  82. double getp();
  83. void   vmact();
  84.  
  85. void   pid();
  86.  
  87. void   init_cascade();
  88. void   init_pid();
  89.  
  90. #endif
  91.  
  92. /**********************************************************************
  93.                     P R I V A T E     D A T A
  94. **********************************************************************/
  95.  
  96. static int initflag = 0;        /* has system been initialized? */
  97.  
  98. static PIDLOOP *vloop;          /* pointer to velocity control loop */
  99. static PIDLOOP *ploop;          /* pointer to position control loop */
  100.  
  101. static double tsamp = 1.0;      /* sampling interval in seconds */
  102.  
  103. #define VINCHAN     0           /* velocity input ADC channel */
  104. #define VOUTCHAN    0           /* velocity output DAC channel */
  105. #define PINCHAN     1           /* posiiton input ADC channel */
  106. #define POUTCHAN    -1          /* dummy position DAC channel */
  107.  
  108. #define ADCTOP      1.0         /* ADC units to position units */
  109. #define ADCTOV      1.0         /* ADC units to velocity units */
  110. #define MTODAC      1.0         /* output units to DAC units */
  111.  
  112. #define MPMIN   -1.0e+6         /* min position actuator output */
  113. #define MPMAX    1.0e+6         /* max position actuator output */
  114.  
  115. /*********  default PID gains and other control parameters  **********/
  116.  
  117. #define VKP     5.0             /* velocity PID gains */
  118. #define VKI     0.1
  119. #define VKD     0.0
  120.  
  121. #define VREF    0.0             /* default velocity setpoint */
  122.  
  123. #define VERR0   0.0             /* velocity errors at k, k-1 and k-2 */
  124. #define VERR1   0.0
  125. #define VERR2   0.0
  126.  
  127. #define PREF    0.0             /* default position setpoint */
  128.  
  129. #define PKP     2.0             /* position PID gains */
  130. #define PKI     0.1
  131. #define PKD     0.0
  132.  
  133. #define PERR0   0.0             /* position errors at k, k-1 and k-2 */
  134. #define PERR1   0.0
  135. #define PERR2   0.0
  136.  
  137. /**********************************************************************
  138.                     E N T R Y     R O U T I N E S
  139. **********************************************************************/
  140.  
  141. /*----------------------------------------------------------------------
  142. PROCEDURE
  143.     CONTROL  -  implements cascade position-velocity control
  144.  
  145. SYNOPSIS
  146.     void control(cline)
  147.     char *cline;
  148.  
  149. PARAMETER
  150.     cline  -  pointer to input line.
  151.  
  152. REMARKS
  153.     The error terms are initialized to zero.  When this routine starts,
  154.     it is equivalent to applying a step position reference input.
  155.  
  156. LAST UPDATE
  157.     10 March 1988
  158.         fixup for ANSI features
  159.     20 March 1988
  160.         check number of fields converted by sscanf()
  161. ----------------------------------------------------------------------*/
  162.  
  163. void control(cline)
  164. char *cline;
  165. {
  166.     int nitr;           /* # of iterations */
  167.     int prnt;           /* 1 - print control output, 0 - do not print */
  168.     int i;              /* iteration counter */
  169.  
  170.  
  171.     if (!initflag)                  /* if system not initialized, do so */
  172.         init_cascade();
  173.  
  174.     /*  get user specified iteration limit and print switch  */
  175.  
  176.     if (sscanf(cline, "%d %d", &nitr, &prnt) != 2)
  177.     {
  178.         printf("invalid or insufficient parameters\n");
  179.         return;
  180.     }
  181.  
  182.     for (i = 0; i < nitr; i++)
  183.     {
  184.         /*------------------------------------------------------------
  185.             Set countdown timer to sampling period in milliseconds.
  186.         -------------------------------------------------------------*/
  187.  
  188.         SetTimer((long)(tsamp * 1000L));
  189.  
  190.         /*--------------------------------------------------------------
  191.             Obtain the current position and call pid() to calculate
  192.             the position loop's control output value.
  193.         ---------------------------------------------------------------*/
  194.  
  195.         ploop->cl_feedback = getp();    /* get current position */
  196.         vloop->cl_feedback = getv();    /* get current velocity */
  197.  
  198.         pid(ploop);                     /* calc. position control o/p */
  199.  
  200.         /*---------------------------------------------------------------
  201.             The output of the position loop is the setpoint for the
  202.             velocity loop.  Here we copy the position output to the
  203.             velocity setpoint, get the current velocity and pass the
  204.             parameters to pid() to calculate the control output for
  205.             the velocity loop.
  206.         ----------------------------------------------------------------*/
  207.  
  208.         vloop->cl_setpoint = ploop->cl_output;
  209.  
  210.         pid(vloop);     /* calculate velocity loop output */
  211.  
  212.         vmact();        /* send control output to velocity actuator */
  213.  
  214.         if (prnt)
  215.             printf("%d: p = %.3lf, v = %.3lf, mp = %.2lf, mv = %.2lf\n",
  216.                 i, ploop->cl_feedback, vloop->cl_feedback, 
  217.                 ploop->cl_output, vloop->cl_output);
  218.  
  219.         if (TimeUp())                   /* premature time-out */
  220.         {
  221.             printf("sample time too short\n");
  222.             exit(1);
  223.         }
  224.  
  225.         while (!TimeUp())
  226.         {
  227. #if SIMRT
  228.             tstep();    /* advance simulation time step */
  229. #endif
  230.         }
  231.     }
  232.  
  233. }
  234.  
  235.  
  236.  
  237. /*----------------------------------------------------------------------
  238. PROCEDURES
  239.     PGAIN  -  interpret the input line specifying position gains
  240.     VGAIN  -  interpret the input line specifying velocity gains
  241.  
  242. SYNOPSIS
  243.     void pgain(cline)
  244.     char *cline;
  245.  
  246.     void vgain(cline)
  247.     char *cline;
  248.  
  249. PARAMETERS
  250.     cline  -  input line
  251.  
  252. REMARKS
  253.     Checks the initflag to ensure that the vloop and ploop data
  254.     structures are allocated and if not, to allocate and initialize
  255.     them through init_cascade().
  256.  
  257. LAST UPDATE
  258.     1 December 1987
  259.         declare return type as void
  260.     20 March 1988
  261.         check number of fields converted by sscanf()
  262. ----------------------------------------------------------------------*/
  263.  
  264. void pgain(cline)
  265. char *cline;
  266. {
  267.     double kp, ki, kd;      /* P, I and D gains */
  268.  
  269.  
  270.     if (sscanf(cline, "%lf %lf %lf", &kp, &ki, &kd) == 3)
  271.     {
  272. #ifdef DEBUG
  273.         printf("<pgain> kp = %.3lf, ki = %.3lf, kd = %.3lf\n", kp, ki, kd);
  274. #endif
  275.  
  276.         if (!initflag)
  277.             init_cascade();
  278.  
  279.         ploop->cl_kp = kp;
  280.         ploop->cl_ki = ki;
  281.         ploop->cl_kd = kd;
  282.     }
  283.     else
  284.         printf("invalid or insufficient position gains\n");
  285.  
  286. }
  287.  
  288.  
  289. void vgain(cline)
  290. char *cline;
  291. {
  292.     double kp, ki, kd;      /* P, I and D gains */
  293.  
  294.  
  295.     if (sscanf(cline, "%lf %lf %lf", &kp, &ki, &kd) == 3)
  296.     {
  297. #ifdef DEBUG
  298.         printf("<vgain> kp = %.3lf, ki = %.3lf, kd = %.3lf\n", kp, ki, kd);
  299. #endif
  300.         if (!initflag)
  301.             init_cascade();
  302.  
  303.         vloop->cl_kp = kp;
  304.         vloop->cl_ki = ki;
  305.         vloop->cl_kd = kd;
  306.     }
  307.     else
  308.         printf("invalid or insufficient velocity gains\n");
  309.  
  310. }
  311.  
  312.  
  313.  
  314. /*----------------------------------------------------------------------
  315. PROCEDURE
  316.     SETPOS  -  get position setpoint from command line
  317.  
  318. SYNOPSIS
  319.     void setpos(cline)
  320.     char *cline;
  321.  
  322. PARAMETERS
  323.     cline  -  user's command line
  324.  
  325. REMARKS
  326.     Note how this differs from pgain() and vgain() in that the value
  327.     is sscanf'ed directly into the cl_setpoint field instead of going
  328.     through temporary variables.
  329.  
  330. LAST UPDATE
  331.     1 December 1987
  332.         declare return type as void
  333.     20 March 1988
  334.         check number of fields converted by sscanf()
  335. ----------------------------------------------------------------------*/
  336.  
  337. void setpos(cline)
  338. char *cline;
  339. {
  340.     
  341.     if (!initflag)
  342.         init_cascade();         /* initialize controller */
  343.  
  344.     if (sscanf(cline,"%lf", &(ploop->cl_setpoint)) == 1)
  345.     {
  346. #ifdef DEBUG
  347.         printf("<setpos> position setpoint = %.3lf\n", ploop->cl_setpoint);
  348. #endif
  349.     }
  350.     else
  351.         printf("invalid position setpoint\n");
  352.  
  353. }
  354.  
  355.  
  356.  
  357. /*----------------------------------------------------------------------
  358. PROCEDURE
  359.     SETSMP  -  set sample time
  360.  
  361. SYNOPSIS
  362.     void setsmp(cline)
  363.     char *cline;
  364.  
  365. PARAMETER
  366.     cline  -  user's command line
  367.  
  368. DESCRIPTION
  369.     Extract sample time from user's command line and use it to set the
  370.     sample time on the timer.
  371.  
  372. LAST UPDATE
  373.     20 March 1988
  374.         check number of fields converted by sscanf()
  375. ----------------------------------------------------------------------*/
  376.  
  377. void setsmp(cline)
  378. char *cline;
  379. {
  380.     
  381.     if (sscanf(cline, "%lf", &tsamp) == 1)
  382.     {
  383.         if (!initflag)
  384.         {
  385.             init_cascade();         /* initialize controller */
  386.         }
  387.         else    /* set both position and velocity tsamps to be the same */
  388.         {
  389.             ploop->cl_tsamp = tsamp;
  390.             vloop->cl_tsamp = tsamp;
  391.         }
  392.  
  393. #ifdef DEBUG
  394.         printf("<setsmp> tsamp = %lf\n", tsamp);
  395. #endif
  396.     }
  397.     else
  398.         printf("invalid sampling period\n");
  399.  
  400. }
  401.  
  402.  
  403.  
  404. /*----------------------------------------------------------------------
  405. PROCEDURE
  406.     INIT  -  process system initialization command
  407.  
  408. SYNOPSIS
  409.     void init()
  410.  
  411. REMARKS
  412.     This routine must be called before control() is called.
  413.  
  414. LAST UPDATE
  415.     10 March 1988
  416.         add call to init_cascade() to reset parameters
  417. ----------------------------------------------------------------------*/
  418.  
  419. void init(cline)
  420. char *cline;
  421. {
  422.  
  423.     ad_init();              /* initialize A/D */
  424.     da_init();              /* initialize D/A */
  425.  
  426.     init_cascade();         /* initialize or reset parameters */
  427.  
  428. #ifdef SIMRT
  429.     sim_init(cline);        /* initialize simulation module */
  430. #endif
  431.  
  432. }
  433.  
  434.  
  435. /***********************************************************************
  436.  
  437.     The following section contains routines which interface between
  438.     high level control code and functions which perform the hardware
  439.     dependent functions of operating the A/D and D/A.  As such, they
  440.     are necessarily dependent on the device driver interface.
  441.  
  442. ***********************************************************************/
  443.  
  444. /*---------------------------------------------------------------------
  445. FUNCTIONS
  446.     GETV  -  returns motor velocity
  447.     GETP  -  returns motor position
  448.  
  449. SYNOPSIS
  450.     static double getv()
  451.     static double getp()
  452.  
  453. RETURNS
  454.     current motor velocity (units unspecified)
  455.     current motor position (units unspecified)
  456.  
  457. REMARKS
  458.     Reads the ADC channel specified by the cl_inchan field.
  459.  
  460.     Note that the source for the channel number is assumed here, but
  461.     that's O.K. because this is a static function that services this
  462.     module only.
  463.     
  464.     The alternative is to pass the channel number as an argument, but
  465.     that means that the control() function must know how velocity and
  466.     position is obtained, which is less desirable as it spreads the
  467.     application specific parameters into three places: the PIDLOOP data
  468.     structure, the control() routine and these two routines, instead
  469.     of just the data structure and these two routines.
  470.  
  471.     Note that the conversion of raw ADC units to velocity and position
  472.     units uses parameterized ADCTOV and ADCTOP conversion factors.
  473.  
  474. LAST UPDATE
  475.     1 March 1988
  476. ---------------------------------------------------------------------*/
  477.  
  478. static double getv()
  479. {
  480.  
  481.     return((double)ad_wread(vloop->cl_inchan) * ADCTOV);
  482. }
  483.  
  484.  
  485. static double getp()
  486. {
  487.  
  488.     return((double)ad_wread(ploop->cl_inchan) * ADCTOP);
  489. }
  490.  
  491.  
  492.  
  493. /*----------------------------------------------------------------------
  494. PROCEDURE
  495.     VMACT  -  send velocity control output to actuator
  496.  
  497. SYNOPSIS
  498.     static void vmact()
  499.  
  500. REMARKS
  501.     Output is converted from logical to physical units required by the
  502.     DAC (using MTODAC) and rounded to the nearest integer.
  503.  
  504.     Note that the DAC channel is specified in the cl_outchan field.
  505.  
  506. LAST UPDATE
  507.     1 March 1988
  508. ----------------------------------------------------------------------*/
  509.  
  510. static void vmact()
  511. {
  512.     int channel;
  513.     int value;
  514.  
  515.  
  516.     channel = vloop->cl_outchan;
  517.     value = (int)((vloop->cl_output * MTODAC) + 0.5);
  518.  
  519.     da_write(channel, value);
  520.  
  521. }
  522.  
  523.  
  524. /***********************************************************************
  525.                 P R I V A T E    R O U T I N E S
  526. /***********************************************************************
  527.  
  528. /*----------------------------------------------------------------------
  529. PROCEDURE
  530.     PID  -  apply PID control for 1 iteration
  531.  
  532. SYNOPSIS
  533.     static void pid(param)
  534.     PIDLOOP *param;
  535.  
  536. PARAMETERS
  537.     param  -  pointer to control loop data structure
  538.  
  539. REMARKS
  540.     Using data in control loop, calculate controller output and store
  541.     it in the cl_output field.
  542.  
  543. LAST UPDATE
  544.     11 May 1985
  545. ----------------------------------------------------------------------*/
  546.  
  547. static void pid(param)
  548. PIDLOOP *param;
  549. {
  550.     double delta_m;         /* change in controller output */
  551.  
  552.  
  553.     param->cl_err2 = param->cl_err1;    /* update error terms */
  554.     param->cl_err1 = param->cl_err0;
  555.     param->cl_err0 = param->cl_setpoint - param->cl_feedback;
  556.  
  557.     delta_m = param->cl_kp * (param->cl_err0 - param->cl_err1)
  558.                 +  param->cl_ki * param->cl_err0
  559.                 +  param->cl_kd * (param->cl_err0 - (2.0 *  param->cl_err1)
  560.                                     + param->cl_err2);
  561.  
  562.     param->cl_output += delta_m;                /* update output value */
  563.  
  564.     if (param->cl_output > param->cl_outmax)    /* apply output limits */
  565.     {
  566.         param->cl_output = param->cl_outmax;
  567.     }
  568.     else if (param->cl_output < param->cl_outmin)
  569.     {
  570.         param->cl_output = param->cl_outmin;
  571.     }
  572.  
  573. }
  574.  
  575.  
  576. /*********************************************************************
  577.             I N I T I A L I Z A T I O N    R O U T I N E S
  578. *********************************************************************/
  579.  
  580. /*--------------------------------------------------------------------
  581. PROCEDURE
  582.     INIT_CASCADE  -  allocate and initialize data structures
  583.  
  584. SYNOPSIS
  585.     void init_cascade()
  586.  
  587. REMARKS
  588.     Sets init flag to true to indicate system initialized.
  589.  
  590.     The vloop and ploop structures are only allocated the first
  591.     time this routine is called.  The PID gains and setpoints are
  592.     preserved on subsequent calls.  This is to allow init() to call
  593.     this routine when the user wants to reset/restart the program.
  594.  
  595. LAST UPDATE
  596.     10 March 1988
  597.         modify to preserve gains on subsequent calls.
  598. --------------------------------------------------------------------*/
  599.  
  600. static void init_cascade()
  601. {
  602.     int damin, damax;       /* min and max DAC output values */
  603.     double mvmin, mvmax;    /* min and max of velocity control values */
  604.     double kp, ki, kd;      /* copies of P, I and D gains */
  605.  
  606.  
  607.     if (!initflag)      /* allocate velocity loop control structure */
  608.     {
  609.         if ((vloop = (PIDLOOP *)calloc(1, sizeof(PIDLOOP))) == (PIDLOOP *)NULL)
  610.         {
  611.             printf("cannot allocate velocity parameter structure\n");
  612.             exit(1);
  613.         }
  614.         else    /* initialize PID gains to defaults */
  615.         {
  616.             kp = VKP;
  617.             ki = VKI;
  618.             kd = VKD;
  619.         }
  620.     }
  621.     else        /* preserve current PID gains */
  622.     {
  623.         kp = vloop->cl_kp;
  624.         ki = vloop->cl_ki;
  625.         kd = vloop->cl_kd;
  626.     }
  627.  
  628.     /*--------------------------------------------------------
  629.         Initialize velocity PID loop control parameters.
  630.     ----------------------------------------------------------*/
  631.  
  632.     da_limits(&damin, &damax);      /* find D/A output limits */
  633.  
  634.     mvmin = (double)damin / MTODAC;
  635.     mvmax = (double)damax / MTODAC;
  636.  
  637.     init_pid(vloop, "vloop", VINCHAN, VOUTCHAN, kp, ki, kd,
  638.                 VERR0, VERR1, VERR2, mvmin, mvmax, tsamp);
  639.  
  640.     if (!initflag)      /* allocate position PID control structure */
  641.     {
  642.         if ((ploop = (PIDLOOP *)calloc(1, sizeof(PIDLOOP))) == (PIDLOOP *)NULL)
  643.         {
  644.             printf("cannot allocate position parameter structure\n");
  645.             exit(1);
  646.         }
  647.         else
  648.         {
  649.             kp = PKP;
  650.             ki = PKI;
  651.             kd = PKD;
  652.         }
  653.     }
  654.     else
  655.     {
  656.         kp = ploop->cl_kp;
  657.         ki = ploop->cl_ki;
  658.         kd = ploop->cl_kd;
  659.     }
  660.  
  661.     /*------------------------------------------------------------
  662.         Since the output of the position loop does not directly
  663.         drive an actuator, we used pre-defined output limits
  664.         MPMIN and MPMAX which are set for a very wide range.
  665.     -------------------------------------------------------------*/
  666.  
  667.     init_pid(ploop, "ploop", PINCHAN, POUTCHAN, kp, ki, kd,
  668.                 PERR0, PERR1, PERR2, MPMIN, MPMAX, tsamp);
  669.  
  670.     initflag = 1;       /* system initialized */
  671.  
  672. }
  673.  
  674.  
  675.  
  676. /*----------------------------------------------------------------------
  677. PROCEDURE
  678.     INIT_PID  -  initialize data structure parameters for PID control
  679.  
  680. SYNOPSIS
  681.     static void init_pid(param, name, ichan, ochan, kp, ki, kd,
  682.                             e0, e1, e2, omin, omax, tsamp)
  683.     PIDLOOP *param;
  684.     char *name;
  685.     int ichan, ochan;
  686.     double kp, ki, kd, e0, e1, e2, omin, omax, tsamp;
  687.  
  688. PARAMETERS
  689.     param         -  pointer to control loop parameter structure
  690.     name          -  pointer to name or id string
  691.     ichan, ochan  -  input and output i/o channel numbers
  692.     kp, ki, kd    -  P, I and D gains
  693.     e0, e1, e2    -  error terms at k, k-1 and k-2
  694.     omin, omax    -  lower and upper output limits
  695.     tsamp         -  sampling interval
  696.  
  697. REMARKS
  698.     The basic function of this routine is to copy the initialization
  699.     argument parameters into the appropriate fields of the control
  700.     loop data structure.
  701.  
  702.     The name field is not used at present, but is maintained for
  703.     possible future use as a means to find or reference particular
  704.     control loops in an application with a large number of control
  705.     loops.
  706.  
  707.     The error terms can be initialized to different values for
  708.     different starting or initial conditions.
  709.  
  710. LAST UPDATE
  711.     10 March 1988
  712.         explicitly NUL terminate name string
  713. ----------------------------------------------------------------------*/
  714.  
  715. static void init_pid(param,name,ichan,ochan,kp,ki,kd,e0,e1,e2,omin,omax,tsamp)
  716. PIDLOOP *param;
  717. char *name;
  718. int ichan, ochan;
  719. double kp, ki, kd, e0, e1, e2, omin, omax, tsamp;
  720. {
  721.  
  722.     /*-------------------------------------------------------------
  723.         strncpy() may not add a NUL terminator if name length
  724.         exceeds NAMESIZE, so to make sure, we NUL terminate it.
  725.     --------------------------------------------------------------*/
  726.  
  727.     strncpy(param->cl_name, name, NAMESIZE);
  728.     param->cl_name[NAMESIZE] = '\0';    /* NUL terminate name */
  729.  
  730.     param->cl_inchan = ichan;
  731.     param->cl_outchan = ochan;
  732.     param->cl_kp = kp;
  733.     param->cl_ki = ki;
  734.     param->cl_kd = kd;
  735.     param->cl_err0 = e0;
  736.     param->cl_err1 = e1;
  737.     param->cl_err2 = e2;
  738.     param->cl_outmin = omin;
  739.     param->cl_outmax = omax;
  740.     param->cl_tsamp = tsamp;
  741.  
  742.     param->cl_feedback = 0.0;
  743.     param->cl_output = 0.0;
  744.  
  745. }
  746.  
  747.