# 3.5. Configuration Parameters

There are many configurable parameters in the MCAF:

- control gains
- time delays
- ramp rates
- filter time constants
- full-scale values (current, voltage, duty cycle, etc.)
- limits (current, voltage, duty cycle, etc.)

and so on. The velocity control loop is a good example. Algebraically it looks like this:

\[I_q = K_p \omega_{err} + \int K_i \omega_{err}\ dt\]

The question becomes: how can we manage parameters such as `K_p` and `K_i` for each control loop and take advantage of fixed-point mathematics?

## 3.5.1. Classic Application Notes

Microchip’s classic application notes, such as AN1292 and AN1078 used calculations expressed via the C preprocessor. For example:

Listing 3.7 from AN1078 `UserParms.h`

    #define DFCY        70000000         // Instruction cycle frequency (Hz)
    #define DTCY        (1.0/DFCY)               // Instruction cycle period (sec)
    #define DDEADTIME   (unsigned int)(DEADTIMESEC*DFCY) // Dead time in dTcys
    #define LOOPTIMEINSEC (1.0/PWMFREQUENCY) // PWM Period = 1.0 / PWMFREQUENCY
    #define IRP_PERCALC (unsigned int)(SPEEDLOOPTIME/LOOPTIMEINSEC)      // PWM loops per velocity calculation
    // PWM loops necessary for transitioning from open loop to closed loop
    #define TRANSITION_STEPS   IRP_PERCALC/4

    #define SPEEDLOOPTIME (float)(1.0/SPEEDLOOPFREQ) // Speed Control Period
    #define LOOPINTCY    DFCY/PWMFREQUENCY   // Basic loop period in units of Tcy
    #define LOCKTIME     (unsigned int)(LOCKTIMEINSEC*(1.0/LOOPTIMEINSEC))
    // Time it takes to ramp from zero to MINSPEEDINRPM. Time represented in seconds
    #define DELTA_STARTUP_RAMP   (unsigned int)(MINSPEEDINRPM*POLEPAIRS*LOOPTIMEINSEC* \
                                                         LOOPTIMEINSEC*65536*65536/(60*OPENLOOPTIMEINSEC))
    // Number of control loops that must execute before the button routine is executed.
    #define      BUTPOLLOOPCNT   (unsigned int)(BUTPOLLOOPTIME/LOOPTIMEINSEC)

Advantages of this approach are that the formula used to derive each value is visible.

Disadvantages of this approach revolve around the way the C preprocessor works. The `#define` approach is purely a text substitution, and the preprocessor does not actually do any calculations; the compiler optimizes by computing any calculations it can do at compile-time (also known as constant folding) but does not see the original code prior to preprocessing. Specific disadvantages are

- Potential for cryptic or invisible errors due to overflow, type mismatch, or unsafe calculations (lack of parentheses to control precedence, no checking of values out of range). For example:

      #define FOSC 70e6
      #define SLOW_BAUDRATE 38400
      #define FAST_BAUDRATE 3*SLOW_BAUDRATE
      #define BRG_DIVIDER FOSC/16/FAST_BAUDRATE
      U1BRG = BRG_DIVIDER - 1;

  This code has an error; the substituted code looks like

      U1BRG = 70e6/16/3*SLOW_BAUDRATE - 1;

  which is missing parentheses around `3*SLOW_BAUDRATE`

- No visibility of the intermediate or final computed values

- No debugging of preprocessor calculations is possible

- Can cover “built-in” function calls (standard math) but not more complicated calculations (e.g. requiring loops, advanced math functions like `erf` or Bessel functions, or other numerical analysis)

## 3.5.2. Motor Control Application Framework

The MCAF approach takes advantage of the code generation features in motorBench™ Development Suite. These features use a templating engine; in the MCAF firmware package are files containing parameterized code with placeholder expressions:

Listing 3.8 `pi_gains.h.template`

    // Current loop gains
    #define KII  ${INTEGRAL_GAIN_HERE    } // integral gain
    #define KIP  ${PROPORTIONAL_GAIN_HERE} // proportional gain

which will get values substituted into the `${SOME_EXPRESSION}` blocks and end up looking like

Listing 3.9 `pi_gains.h`

    // Current loop gains
    #define KII  3.021 // integral gain
    #define KIP  7.413 // proportional gain

This provides a couple of advantages:

- Calculations can be made at code generation time using mechanisms that can be debugged
- Intermediate and final results can be made visible
- Reporting of floating-point / engineering equivalents can be added
- Almost any computable equation can be used

The sample code above is an example of floating-point parameters; since we tend to use fixed-point calculations, the firmware package templates could look more like

Listing 3.10 `pi_gains.h.template`

    // Current loop gains
    #define KII_Q12  <@describe_constant Q12(INTEGRAL_GAIN) />
    #define KIP_Q12  <@describe_constant Q12(PROPORTIONAL_GAIN) />

using templating macros that produce both integer counts and an equivalent floating-point value in a comment:

Listing 3.11 `pi_gains.h`

    // Current loop gains
    #define KII_Q12  12370   // 3.021 Q12
    #define KIP_Q12  30364   // 7.413 Q12

Then we can apply the defined constants in code:

Listing 3.12 `pi_controller.c`

    void control_motor(motor *pmotor)
    {
       ...
       int16_t current_error = pmotor->iCmd - pmotor->iMeas;
       pmotor->applied_voltage =
         ((int32_t)CURRENT_LOOP_GAIN_Q12 * current_error) >> 12;
       ...
    }

This approach of compile-time separation (splitting configurable parameters into a definition in a template-generated C header file, and usage in one or more C source files) is essentially the one used in the MCAF. In general it has some important characteristics:

- Good for optimization — the compiler sees the constant value, might be able to speed up the calculation.
- Bad for modularity — What if we have software driving two motors with different gains? Then we need `KII_Q12_MOTOR1` and `KII_Q12_MOTOR2` and somehow in our C code we need to determine when to apply each.
- Bad for development — We can’t use run-time diagnostic tools to try out different gains.
- Definition and point of use are separated — this is an inevitable casualty of modular design.

We are addressing some of these concerns by taking a slightly different approach, using run-time separation, storing configuration parameters in state variables:

Listing 3.13 `pi_controller.c`

    void control_motor(motor *pmotor)
    {
       ...
       int16_t current_error = pmotor->iCmd - pmotor->iMeas;
       pmotor->applied_voltage =
         ((int32_t) pmotor->kp * current_error) >> pmotor->kp_shift;
       ...
    }

Listing 3.14 `init_motor.c` (dynamically generated)

     void init_motor_1(motor *pmotor)
     {
        pmotor->kp = 12370;    // 3.02 Q12
        pmotor->kp_shift = 12;
     }
     void init_motor_2(motor *pmotor)
     {
        pmotor->kp = 15147;    // 1.849 Q13
        pmotor->kp_shift = 13;
     }

This allows a code architecture so that

- more than one motor can be used with different gains for each
- variable shift counts can be used, to support a dynamic range that is optimized for each motor.
- parameters can be changed at run-time during development and testing

The run-time cost is slightly higher than in the case where each line of C code uses a constant fixed at compile-time, rather than a run-time parameter, but is a better approach for the MCAF.

As of MCAF version 1.0, there is a mix of compile-time and run-time separation; we are migrating to use purely run-time separation in future versions. All generated code relating to configurable parameters is located in the `parameters/xyz_params.h` files.

One drawback of the code generation approach is that it is not very easy to be “transparent”; that is, to show equations of where the calculated values came from. We are working to address this issue in the future.

Sample generated parameters of the MCAF:

Listing 3.15 `parameters/operating_params.h`

     /* --- commutation slewrate parameters --- */

     /*
      * slewrate_accel:                 7.426 krad/s^2
      *                                70.909 kRPM/s
      * slewrate_decel:                 2.335 krad/s^2
      *                                22.296 kRPM/s
      * t_coastdown:                  248.983 ms
      */

     #define VELOCITY_SLEWRATE_LIMIT1 32000
     #define VELOCITY_SLEWRATE_LIMIT_ACCEL         387      // Q15(  0.01181) =   +7.42063 rad/s       =   +7.42562 rad/s       - 0.0671%
     #define VELOCITY_SLEWRATE_LIMIT_DECEL         122      // Q15(  0.00372) =   +2.33932 rad/s       =   +2.33479 rad/s       + 0.1938%
     #define VELOCITY_COASTDOWN_TIME              4980      // Q0(4980.00000) = +249.00000 ms          = +248.98273 ms          + 0.0069%

Listing 3.16 `parameters/foc_params.h`

    //************** PI Coefficients **************
    //// Current loop
    // phase margin = 80 deg
    // PI phase at crossover = 45.000 deg
    // crossover frequency = 2.442 k rad/s (388.596 Hz)
    #define KIP                                  9151      // Q15(  0.27927) = +436.37336 mV/A        = +436.37526 mV/A        - 0.0004%
    #define KIP_Q                        15
    #define KII                                  1117      // Q15(  0.03409) =   +1.06530 kV/A/s      =   +1.06546 kV/A/s      - 0.0152%
    #define KII_Q                        15
    //// Velocity loop
    // phase margin = 50 deg
    // PI phase at crossover = 10.000 deg
    // crossover frequency = 142.852 rad/s (22.736 Hz)
    #define KWP                                 14933      // Q13(  1.82288) =  +25.73362 mA/(rad/s)  =  +25.73375 mA/(rad/s)  - 0.0005%
    #define KWP_Q                        13
    #define KWI                                  1505      // Q15(  0.04593) = +648.38105 mA/rad      = +648.19737 mA/rad      + 0.0283%
    #define KWI_Q                        15