Navigating through the Insta SPINFOC Code Dave Wilson

  • Slides: 132
Download presentation
Navigating through the Insta. SPIN-FOC Code Dave Wilson Rev. 0. 2 continue

Navigating through the Insta. SPIN-FOC Code Dave Wilson Rev. 0. 2 continue

pid. Handle[1] Idq_ref_pu. value[0] = _IQ(0. 0) Idq_pu. value[0 ] Kp_Id Ki_Id Kd_Id =

pid. Handle[1] Idq_ref_pu. value[0] = _IQ(0. 0) Idq_pu. value[0 ] Kp_Id Ki_Id Kd_Id = IQ(0. 0) max. Voltage_pu -max. Voltage_pu Ui_Id = _IQ(0. 0) Id ref Id P I D Id Current PI Controller USER_MAX_VS_MAG_PU = (1. 0) Out Vdq_out_pu. value[0 ] speed_pu Kp_spd = _IQ(1. 0) Ki_spd = _IQ(0. 1) Kd_spd = _IQ(0. 0) max. Current_pu -max. Current_pu Ui_spd = _IQ(0. 0) Vq Voltage Vd Limit Vd speed I D SVM Vab_pu. value[0] Valpha Speed PI Controller out. Max_pu Out Min I Init spd ref P Vq Out Max pid. Handle[0] g. Motor. Vars. Speed. Ref_pu proj_lab 11 Vmag angle_pu Idq_ref_pu. value[1] Out q Vq Inverse Park Transform Valpha PWMb Vab_pu. value[1] Vbeta PWMc g. Pwm. Data[0] g. Pwm. Data[1] g. Pwm. Data[2] Out Max Out Min I Init pid. Handle[2] Iq ref Idq_pu. value[1] Kp_Iq Ki_Iq Kd_Iq = IQ(0. 0) out. Max_pu -out. Max_pu Ui_Iq = _IQ(0. 0) Iq P I D Iq Current PI Controller Out Vdq_out_pu. value[1] Out Max Out Min I Init clarke. Handle_I clarke. Handle_V Ialpha g. Adc. Data. I. value[0] Ia Ibeta g. Adc. Data. I. value[1] g. Adc. Data. I. value[2] g. Adc. Data. V. value[0] g. Adc. Data. V. value[1] g. Adc. Data. V. value[2] Ib Forward Valpha Clarke Vbeta Transform Ic Va Vb Vc Left click on a block or variable to see its implementation in code. one. Over. Dc. Bus est. Handle Iab_pu. value[0] Iab_pu. value[1] Vab_pu. value[0] Vab_pu. value[1] g. Adc. Data. dc. Bus g. Motor. Vars. Speed. Ref_pu Ialpha Ibeta 1/Vbus q w l FAST Valpha Estimator Vbeta Tem Vbus spd ref Id Iq angle_pu speed_pu flux (not used) torque (not used) Idq_pu. value[0] Idq_pu. value[1] q w Angle Compensator q angle_pu Exit:

Return to Diagram: Exit:

Return to Diagram: Exit:

pid. h PI Series Form PID_run() out. Max Kp Up ref. Value + Error

pid. h PI Series Form PID_run() out. Max Kp Up ref. Value + Error + + - fback. Value Ui + Init + Ki (0. 0) out. Min Z-1 *p. Out. Value

pid. h PI Parallel Form PID_run_spd() out. Max Kp Up ref. Value + Error

pid. h PI Parallel Form PID_run_spd() out. Max Kp Up ref. Value + Error + + - fback. Value Ui + Init + Ki (0. 0) out. Min Z-1 *p. Out. Value

CLARKE_run( ) A + 2 1/3 alpha - B + + + beta -

CLARKE_run( ) A + 2 1/3 alpha - B + + + beta - C Amplitude Invariant Forward Clarke Transform num. Sensors = 3

proj_lab 11. c // ************************************* // the functions void main(void) { // IMPORTANT NOTE:

proj_lab 11. c // ************************************* // the functions void main(void) { // IMPORTANT NOTE: If you are not familiar with Motor. Ware coding guidelines // please refer to the following document: // C: /ti/motorware_1_01_00_1 x/docs/motorware_coding_standards. pdf // Only used if running from FLASH // Note that the variable FLASH is defined by the project #ifdef FLASH // Copy time critical code and Flash setup code to RAM // The Ramfuncs. Load. Start, Ramfuncs. Load. End, and Ramfuncs. Run. Start // symbols are created by the linker. Refer to the linker files. mem. Copy((uint 16_t *)&Ramfuncs. Load. Start, (uint 16_t *)&Ramfuncs. Load. End, (uint 16_t *)&Ramfuncs. Run. Start); #ifdef F 2802 x. F //copy. econst to unsecure RAM if(*econst_end - *econst_start) { mem. Copy((uint 16_t *)&econst_start, (uint 16_t *)&econst_end, (uint 16_t *)&econst_ram_load); } //copy. switch ot unsecure RAM if(*switch_end - *switch_start) { mem. Copy((uint 16_t *)&switch_start, (uint 16_t *)&switch_end, (uint 16_t *)&switch_ram_load); } #endif 1 // initialize the Hardware Abstraction Layer (HAL) // hal. Handle will be used throughout the code to interface with the HAL // (set parameters, get and set functions, etc) hal. Handle is required since // this is how all objects are interfaced, and it allows interface with // multiple objects by simply passing a different handle. The use of // handles is explained in this document: // C: /ti/motorware_1_01_00_1 x/docs/motorware_coding_standards. pdf hal. Handle = HAL_init(&hal, sizeof(hal)); // check for errors in user parameters USER_check. For. Errors(&g. User. Params); // store user parameter error in global variable g. Motor. Vars. User. Error. Code = USER_get. Error. Code(&g. User. Params); // do not allow code execution if there is a user parameter error. If there // is an error, the code will be stuck in this forever loop if(g. Motor. Vars. User. Error. Code != USER_Error. Code_No. Error) { for(; ; ) { g. Motor. Vars. Flag_enable. Sys = false; } } // initialize the Clarke modules // Clarke handle initialization for current signals clarke. Handle_I = CLARKE_init(&clarke_I, sizeof(clarke_I)); // Clarke handle initialization for voltage signals clarke. Handle_V = CLARKE_init(&clarke_V, sizeof(clarke_V)); // initialize the estimator est. Handle = EST_init((void *)USER_EST_HANDLE_ADDRESS, 0 x 200); // initialize the user parameters // This function initializes all values of structure g. User. Params with // values defined in user. h. The values in g. User. Params will be then used by // the hardware abstraction layer (HAL) to configure peripherals such as // PWM, ADC, interrupts, etc. USER_set. Params(&g. User. Params); 2

proj_lab 11. c // set the hardware abstraction layer parameters // This function initializes

proj_lab 11. c // set the hardware abstraction layer parameters // This function initializes all peripherals through a Hardware Abstraction // Layer (HAL). It uses all values stored in g. User. Params. HAL_set. Params(hal. Handle, &g. User. Params); #ifdef FAST_ROM_V 1 p 6 { // These function calls are used to initialize the estimator with ROM // function calls. It needs the specific address where the controller // object is declared by the ROM code. CTRL_Handle ctrl. Handle = CTRL_init((void *)USER_CTRL_HANDLE_ADDRESS , 0 x 200); CTRL_Obj *obj = (CTRL_Obj *)ctrl. Handle; // this sets the estimator handle (part of the controller object) to // the same value initialized above by the EST_init() function call. // This is done so the next function implemented in ROM, can // successfully initialize the estimator as part of the controller // object. obj->est. Handle = est. Handle; // initialize the estimator through the controller. These three // function calls are needed for the F 2806 x. F/M implementation of // Insta. SPIN. CTRL_set. Params(ctrl. Handle, &g. User. Params); CTRL_set. User. Motor. Params(ctrl. Handle); CTRL_setup. Est. Idle. State(ctrl. Handle); } #else { // initialize the estimator. These two function calls are needed for // the F 2802 x. F implementation of Insta. SPIN using the estimator handle // initialized by EST_init(), these two function calls configure the // estimator, and they set the estimator in a proper state prior to // spinning a motor. EST_set. Est. Params(est. Handle, &g. User. Params); EST_setup. Est. Idle. State(est. Handle); } #endif 3 // disable Rs recalculation EST_set. Flag_enable. Rs. Recalc(est. Handle, false); // set the number of current sensors setup. Clarke_I(clarke. Handle_I, USER_NUM_CURRENT_SENSORS); // set the number of voltage sensors setup. Clarke_V(clarke. Handle_V, USER_NUM_VOLTAGE_SENSORS); // set the pre-determined current and voltage feedback offset values g. Offsets_I_pu. value[0] = _IQ(I_A_offset); g. Offsets_I_pu. value[1] = _IQ(I_B_offset); g. Offsets_I_pu. value[2] = _IQ(I_C_offset); g. Offsets_V_pu. value[0] = _IQ(V_A_offset); g. Offsets_V_pu. value[1] = _IQ(V_B_offset); g. Offsets_V_pu. value[2] = _IQ(V_C_offset); // initialize the PID controllers { // This equation defines the relationship between per unit current and // real-world current. The resulting value in per units (pu) is then // used to configure the controllers _iq max. Current_pu = _IQ(USER_MOTOR_MAX_CURRENT / USER_IQ_FULL_SCALE_CURRENT_A); // This equation uses the scaled maximum voltage vector, which is // already in per units, hence there is no need to include the #define // for USER_IQ_FULL_SCALE_VOLTAGE_V _iq max. Voltage_pu = _IQ(USER_MAX_VS_MAG_PU * USER_VD_SF); float_t full. Scale. Current = USER_IQ_FULL_SCALE_CURRENT_A; float_t full. Scale. Voltage = USER_IQ_FULL_SCALE_VOLTAGE_V; float_t Isr. Period_sec = 1. 0 / USER_ISR_FREQ_Hz; float_t Ls_d = USER_MOTOR_Ls_d; float_t Ls_q = USER_MOTOR_Ls_q; float_t Rs = USER_MOTOR_Rs; // This lab assumes that motor parameters are known, and it does not // perform motor ID, so the R/L parameters are known and defined in // user. h float_t Rover. Ls_d = Rs / Ls_d; float_t Rover. Ls_q = Rs / Ls_q; 4

proj_lab 11. c // For the current controller, Kp = Ls*bandwidth(rad/sec) But in order

proj_lab 11. c // For the current controller, Kp = Ls*bandwidth(rad/sec) But in order // to be used, it must be converted to per unit values by multiplying // by full. Scale. Current and then dividing by full. Scale. Voltage. From the // statement below, we see that the bandwidth in rad/sec is equal to // 0. 25/Isr. Period_sec, which is equal to USER_ISR_FREQ_HZ/4. This means // that by setting Kp as described below, the bandwidth in Hz is // USER_ISR_FREQ_HZ/(8*pi). _iq Kp_Id = _IQ((0. 25 * Ls_d * full. Scale. Current) / (Isr. Period_sec * full. Scale. Voltage)); // In order to achieve pole/zero cancellation (which reduces the // closed-loop transfer function from a second-order system to a // first-order system), Ki must equal Rs/Ls. Since the output of the // Ki gain stage is integrated by a DIGITAL integrator, the integrator // input must be scaled by 1/Isr. Period_sec. That's just the way // digital integrators work. But, since Isr. Period_sec is a constant, // we can save an additional multiplication operation by lumping this // term with the Ki value. _iq Ki_Id = _IQ(Rover. Ls_d * Isr. Period_sec); // Now do the same thing for Kp for the q-axis current controller. // If the motor is not an IPM motor, Ld and Lq are the same, which // means that Kp_Iq = Kp_Id _iq Kp_Iq = _IQ((0. 25 * Ls_q * full. Scale. Current) / (Isr. Period_sec * full. Scale. Voltage)); // Do the same thing for Ki for the q-axis current controller. If the // motor is not an IPM motor, Ld and Lq are the same, which means that // Ki_Iq = Ki_Id. _iq Ki_Iq = _IQ(Rover. Ls_q * Isr. Period_sec); // There are three PI controllers; one speed controller and two current // controllers. Each PI controller has two coefficients; Kp and Ki. // So you have a total of six coefficients that must be defined. // This is for the speed controller pid. Handle[0] = PID_init(&pid[0], sizeof(pid[0])); // This is for the Id current controller pid. Handle[1] = PID_init(&pid[1], sizeof(pid[1])); // This is for the Iq current controller pid. Handle[2] = PID_init(&pid[2], sizeof(pid[2])); 5 // The following instructions load the parameters for the speed PI // controller. PID_set. Gains(pid. Handle[0], _IQ(1. 0), _IQ(0. 01), _IQ(0. 0)); // The current limit is performed by the limits placed on the speed PI // controller output. In the following statement, the speed // controller's largest negative current is set to -max. Current_pu, and // the largest positive current is set to max. Current_pu. PID_set. Min. Max(pid. Handle[0], -max. Current_pu, max. Current_pu); PID_set. Ui(pid. Handle[0], _IQ(0. 0)); // Set the initial condition value // for the integrator output to 0 pid. Cnt. Speed = 0; // Set the counter for decimating the speed // controller to 0 // The following instructions load the parameters for the d-axis // current controller. // P term = Kp_Id, I term = Ki_Id, D term = 0 PID_set. Gains(pid. Handle[1], Kp_Id, Ki_Id, _IQ(0. 0)); // Largest negative voltage = -max. Voltage_pu, largest positive // voltage = max. Voltage_pu PID_set. Min. Max(pid. Handle[1], -max. Voltage_pu, max. Voltage_pu); // Set the initial condition value for the integrator output to 0 PID_set. Ui(pid. Handle[1], _IQ(0. 0)); // The following instructions load the parameters for the q-axis // current controller. // P term = Kp_Iq, I term = Ki_Iq, D term = 0 PID_set. Gains(pid. Handle[2], Kp_Iq, Ki_Iq, _IQ(0. 0)); // The largest negative voltage = 0 and the largest positive // voltage = 0. But these limits are updated every single ISR before // actually executing the Iq controller. The limits depend on how much // voltage is left over after the Id controller executes. So having an // initial value of 0 does not affect Iq current controller execution. PID_set. Min. Max(pid. Handle[2], _IQ(0. 0)); // Set the initial condition value for the integrator output to 0 PID_set. Ui(pid. Handle[2], _IQ(0. 0)); } 6

proj_lab 11. c // initialize the speed reference in kilo RPM where base speed

proj_lab 11. c // initialize the speed reference in kilo RPM where base speed is // USER_IQ_FULL_SCALE_FREQ_Hz. // Set 10 Hz electrical frequency as initial value, so the k. RPM value would // be: 10 * 60 / motor pole pairs / 1000. g. Motor. Vars. Speed. Ref_krpm = _IQmpy(_IQ(10. 0), g. Speed_hz_to_krpm_sf); // initialize the inverse Park module ipark. Handle = IPARK_init(&ipark, sizeof(ipark)); // initialize the space vector generator module svgen. Handle = SVGEN_init(&svgen, sizeof(svgen)); // setup faults HAL_setup. Faults(hal. Handle); // initialize the interrupt vector table HAL_init. Int. Vector. Table(hal. Handle); // enable the ADC interrupts HAL_enable. Adc. Ints(hal. Handle); // enable global interrupts HAL_enable. Global. Ints(hal. Handle); // enable debug interrupts HAL_enable. Debug. Int(hal. Handle); // disable the PWM HAL_disable. Pwm(hal. Handle); // compute scaling factors for flux and torque calculations g. Flux_pu_to_Wb_sf = USER_compute. Flux_pu_to_Wb_sf(); g. Flux_pu_to_Vp. Hz_sf = USER_compute. Flux_pu_to_Vp. Hz_sf(); g. Torque_Ls_Id_Iq_pu_to_Nm_sf = USER_compute. Torque_Ls_Id_Iq_pu_to_Nm_sf(); g. Torque_Flux_Iq_pu_to_Nm_sf = USER_compute. Torque_Flux_Iq_pu_to_Nm_sf(); // enable the system by default g. Motor. Vars. Flag_enable. Sys = true; 7 #ifdef DRV 8301_SPI // turn on the DRV 8301 if present HAL_enable. Drv(hal. Handle); // initialize the DRV 8301 interface HAL_setup. Drv. Spi(hal. Handle, &g. Drv. Spi 8301 Vars); #endif #ifdef DRV 8305_SPI // turn on the DRV 8305 if present HAL_enable. Drv(hal. Handle); // initialize the DRV 8305 interface HAL_setup. Drv. Spi(hal. Handle, &g. Drv. Spi 8305 Vars); #endif // Begin the background loop for(; ; ) { // Waiting for enable system flag to be set while(!(g. Motor. Vars. Flag_enable. Sys)); // loop while the enable system flag is true while(g. Motor. Vars. Flag_enable. Sys) { // If Flag_enable. Sys is set AND Flag_Run_Identify is set THEN // enable PWMs and set the speed reference if(g. Motor. Vars. Flag_Run_Identify) { // update estimator state EST_update. State(est. Handle, 0); #ifdef FAST_ROM_V 1 p 6 // call this function to fix 1 p 6. This is only used for // F 2806 x. F/M implementation of Insta. SPIN (version 1. 6 of // ROM), since the inductance calculation is not done // correctly in ROM, so this function fixes that ROM bug. software. Update 1 p 6(est. Handle); #endif // enable the PWM HAL_enable. Pwm(hal. Handle); } 8

proj_lab 11. c else // Flag_enable. Sys is set AND Flag_Run_Identify is not set

proj_lab 11. c else // Flag_enable. Sys is set AND Flag_Run_Identify is not set { // set estimator to Idle EST_set. Idle(est. Handle); // disable the PWM HAL_disable. Pwm(hal. Handle); // clear integrator outputs PID_set. Ui(pid. Handle[0], _IQ(0. 0)); PID_set. Ui(pid. Handle[1], _IQ(0. 0)); PID_set. Ui(pid. Handle[2], _IQ(0. 0)); // clear Id and Iq references g. Idq_ref_pu. value[0] = _IQ(0. 0); g. Idq_ref_pu. value[1] = _IQ(0. 0); } // update the global variables update. Global. Variables(est. Handle); // enable/disable the forced angle EST_set. Flag_enable. Force. Angle(est. Handle, g. Motor. Vars. Flag_enable. Force. Angle); // set target speed g. Motor. Vars. Speed. Ref_pu = _IQmpy(g. Motor. Vars. Speed. Ref_krpm, g. Speed_krpm_to_pu_sf); #ifdef DRV 8301_SPI HAL_write. Drv. Data(hal. Handle, &g. Drv. Spi 8301 Vars); HAL_read. Drv. Data(hal. Handle, &g. Drv. Spi 8301 Vars); #endif #ifdef DRV 8305_SPI HAL_write. Drv. Data(hal. Handle, &g. Drv. Spi 8305 Vars); HAL_read. Drv. Data(hal. Handle, &g. Drv. Spi 8305 Vars); #endif 9 } // end of while(g. Flag_enable. Sys) loop // disable the PWM HAL_disable. Pwm(hal. Handle); g. Motor. Vars. Flag_Run_Identify = false; } // end of for(; ; ) loop } // end of main() function //! brief The main ISR that implements the motor control. interrupt void main. ISR(void) { // Declaration of local variables _iq angle_pu = _IQ(0. 0); _iq speed_pu = _IQ(0. 0); _iq one. Over. Dc. Bus; MATH_vec 2 Iab_pu; MATH_vec 2 Vab_pu; MATH_vec 2 phasor; // acknowledge the ADC interrupt HAL_acq. Adc. Int(hal. Handle, ADC_Int. Number_1); // convert the ADC data HAL_read. Adc. Data. With. Offsets(hal. Handle, &g. Adc. Data); // remove offsets g. Adc. Data. I. value[0] = g. Adc. Data. I. value[0] - g. Offsets_I_pu. value[0]; g. Adc. Data. I. value[1] = g. Adc. Data. I. value[1] - g. Offsets_I_pu. value[1]; g. Adc. Data. I. value[2] = g. Adc. Data. I. value[2] - g. Offsets_I_pu. value[2]; g. Adc. Data. V. value[0] = g. Adc. Data. V. value[0] - g. Offsets_V_pu. value[0]; g. Adc. Data. V. value[1] = g. Adc. Data. V. value[1] - g. Offsets_V_pu. value[1]; g. Adc. Data. V. value[2] = g. Adc. Data. V. value[2] - g. Offsets_V_pu. value[2]; // run Clarke transform on current. Three values are passed, two values // are returned. CLARKE_run(clarke. Handle_I, &g. Adc. Data. I, &Iab_pu); // run Clarke transform on voltage. Three values are passed, two values // are returned. CLARKE_run(clarke. Handle_V, &g. Adc. Data. V, &Vab_pu); 10

proj_lab 11. c // run the estimator // The speed reference is needed so

proj_lab 11. c // run the estimator // The speed reference is needed so that the proper sign of the forced // angle is calculated. When the estimator does not do motor ID as in this // lab, only the sign of the speed reference is used EST_run(est. Handle, &Iab_pu, &Vab_pu, g. Adc. Data. dc. Bus, g. Motor. Vars. Speed. Ref_pu); // generate the motor electrical angle_pu = EST_get. Angle_pu(est. Handle); speed_pu = EST_get. Fm_pu(est. Handle); // get Idq from estimator to avoid sin and cos, and a Park transform, // which saves CPU cycles EST_get. Idq_pu(est. Handle, &g. Idq_pu); // run the appropriate controller if(g. Motor. Vars. Flag_Run_Identify) { // Declaration of local variables. _iq ref. Value; _iq fback. Value; _iq out. Max_pu; // when appropriate, run the PID speed controller // This mechanism provides the decimation for the speed loop. if(pid. Cnt. Speed >= USER_NUM_CTRL_TICKS_PER_SPEED_TICK) { // Reset the Speed PID execution counter. pid. Cnt. Speed = 0; // The next instruction executes the PI speed controller and places // its output in Idq_ref_pu. value[1], which is the input reference // value for the q-axis current controller. PID_run_spd(pid. Handle[0], g. Motor. Vars. Speed. Ref_pu, speed_pu, &(g. Idq_ref_pu. value[1])); } else { // increment counter pid. Cnt. Speed++; } 11 // Get the reference value for the d-axis current controller. ref. Value = g. Idq_ref_pu. value[0]; // Get the actual value of Id fback. Value = g. Idq_pu. value[0]; // The next instruction executes the PI current controller for the // d axis and places its output in Vdq_pu. value[0], which is the // control voltage along the d-axis (Vd) PID_run(pid. Handle[1], ref. Value, fback. Value, &(g. Vdq_out_pu. value[0])); // get the Iq reference value ref. Value = g. Idq_ref_pu. value[1]; // get the actual value of Iq fback. Value = g. Idq_pu. value[1]; // The voltage limits on the output of the q-axis current controller // are dynamic, and are dependent on the output voltage from the d-axis // current controller. In other words, the d-axis current controller // gets first dibs on the available voltage, and the q-axis current // controller gets what's left over. That is why the d-axis current // controller executes first. The next instruction calculates the // maximum limits for this voltage as: // Vq_min_max = +/- sqrt(Vbus^2 - Vd^2) out. Max_pu = _IQsqrt(_IQ(USER_MAX_VS_MAG_PU * USER_MAX_VS_MAG_PU) - _IQmpy(g. Vdq_out_pu. value[0], g. Vdq_out_pu. value[0])); // Set the limits to +/- out. Max_pu PID_set. Min. Max(pid. Handle[2], -out. Max_pu, out. Max_pu); // The next instruction executes the PI current controller for the // q axis and places its output in Vdq_pu. value[1], which is the // control voltage vector along the q-axis (Vq) PID_run(pid. Handle[2], ref. Value, fback. Value, &(g. Vdq_out_pu. value[1 ])); 12

proj_lab 11. c // The voltage vector is now calculated and ready to be

proj_lab 11. c // The voltage vector is now calculated and ready to be applied to the // motor in the form of three PWM signals. However, even though the // voltages may be supplied to the PWM module now, they won't be // applied to the motor until the next PWM cycle. By this point, the // motor will have moved away from the angle that the voltage vector // was calculated for, by an amount which is proportional to the // sampling frequency and the speed of the motor. For steady-state // speeds, we can calculate this angle delay and compensate for it. angle_pu = angle. Delay. Comp(speed_pu, angle_pu); // compute the sin/cos phasor since the phase is used by the inverse // Park transform to transform from DQ to Alpha/Beta. The phasor // calculation is not part of the IPARK module. The way it works is, // the phasor is calculated by doing a sine and cosine, then the phasor // is copied into the IPARK module, and then finally the IPARK module // uses the phasor to calculate from DQ to Alpha/Beta phasor. value[0] = _IQcos. PU(angle_pu); phasor. value[1] = _IQsin. PU(angle_pu); // set the phasor in the inverse Park transform IPARK_set. Phasor(ipark. Handle, &phasor); // Run the inverse Park module. This converts the voltage vector from // synchronous frame values to stationary frame values. IPARK_run(ipark. Handle, &g. Vdq_out_pu, &Vab_pu); // This code compensates for variations in the DC bus by adjusting the // PWM duty cycle to compensate for the variations. the goal is to // produce the desired volt-second product regardless of the DC bus // value. To do this, we must divide the desired voltage values by // the DC bus value. Or. . . it is easier to multiply by 1/(DC bus value) one. Over. Dc. Bus = EST_get. One. Over. Dc. Bus_pu(est. Handle); Vab_pu. value[0] = _IQmpy(Vab_pu. value[0], one. Over. Dc. Bus); Vab_pu. value[1] = _IQmpy(Vab_pu. value[1], one. Over. Dc. Bus); // Now run the space vector generator (SVGEN) module. // There is no need to do an inverse CLARKE transform, as this is // handled in the SVGEN_run function. SVGEN_run(svgen. Handle, &Vab_pu, &(g. Pwm. Data. Tabc)); } 13 else // g. Motor. Vars. Flag_Run_Identify = 0 { // disable the PWM HAL_disable. Pwm(hal. Handle); // Set the PWMs to 50% duty cycle g. Pwm. Data. Tabc. value[0] = _IQ(0. 0); g. Pwm. Data. Tabc. value[1] = _IQ(0. 0); g. Pwm. Data. Tabc. value[2] = _IQ(0. 0); } // write to the PWM compare registers, and then we are done! HAL_write. Pwm. Data(hal. Handle, &g. Pwm. Data); return; } // end of main. ISR() function 14

pid. Handle[1] Idq_ref_pu. value[0] = _IQ(0. 0) Idq_pu. value[0 ] Kp_Id Ki_Id Kd_Id =

pid. Handle[1] Idq_ref_pu. value[0] = _IQ(0. 0) Idq_pu. value[0 ] Kp_Id Ki_Id Kd_Id = IQ(0. 0) max. Voltage_pu -max. Voltage_pu Ui_Id = _IQ(0. 0) Id ref Id P I D Id Current PI Controller USER_MAX_VS_MAG_PU = (1. 0) Out Vdq_out_pu. value[0 ] speed_pu Kp_spd = _IQ(1. 0) Ki_spd = _IQ(0. 1) Kd_spd = _IQ(0. 0) max. Current_pu -max. Current_pu Ui_spd = _IQ(0. 0) Vq Voltage Vd Limit Vd speed I D SVM Vab_pu. value[0] Valpha Speed PI Controller out. Max_pu Out Min I Init spd ref P Vq Out Max pid. Handle[0] g. Motor. Vars. Speed. Ref_pu proj_lab 11 Vmag angle_pu Idq_ref_pu. value[1] Out q Vq Inverse Park Transform Valpha PWMb Vab_pu. value[1] Vbeta PWMc g. Pwm. Data[0] g. Pwm. Data[1] g. Pwm. Data[2] Out Max Out Min I Init pid. Handle[2] Iq ref Idq_pu. value[1] Kp_Iq Ki_Iq Kd_Iq = IQ(0. 0) out. Max_pu -out. Max_pu Ui_Iq = _IQ(0. 0) Iq P I D Iq Current PI Controller Out Vdq_out_pu. value[1] Out Max Out Min I Init clarke. Handle_I clarke. Handle_V Ialpha g. Adc. Data. I. value[0] Ia Ibeta g. Adc. Data. I. value[1] g. Adc. Data. I. value[2] g. Adc. Data. V. value[0] g. Adc. Data. V. value[1] g. Adc. Data. V. value[2] Ib Forward Valpha Clarke Vbeta Transform Ic Va Vb Vc Left click on a block or variable to see its implementation in code. one. Over. Dc. Bus est. Handle Iab_pu. value[0] Iab_pu. value[1] Vab_pu. value[0] Vab_pu. value[1] g. Adc. Data. dc. Bus g. Motor. Vars. Speed. Ref_pu Ialpha Ibeta 1/Vbus q w l FAST Valpha Estimator Vbeta Tem Vbus spd ref Id Iq angle_pu speed_pu flux (not used) torque (not used) Idq_pu. value[0] Idq_pu. value[1] q w Angle Compensator q angle_pu Exit:

proj_lab 11. c // run the estimator // The speed reference is needed so

proj_lab 11. c // run the estimator // The speed reference is needed so that the proper sign of the forced // angle is calculated. When the estimator does not do motor ID as in this // lab, only the sign of the speed reference is used EST_run(est. Handle, &Iab_pu, &Vab_pu, g. Adc. Data. dc. Bus, g. Motor. Vars. Speed. Ref_pu); // generate the motor electrical angle_pu = EST_get. Angle_pu(est. Handle); speed_pu = EST_get. Fm_pu(est. Handle); // get Idq from estimator to avoid sin and cos, and a Park transform, // which saves CPU cycles EST_get. Idq_pu(est. Handle, &g. Idq_pu); // run the appropriate controller if(g. Motor. Vars. Flag_Run_Identify) { // Declaration of local variables. _iq ref. Value; _iq fback. Value; _iq out. Max_pu; // when appropriate, run the PID speed controller // This mechanism provides the decimation for the speed loop. if(pid. Cnt. Speed >= USER_NUM_CTRL_TICKS_PER_SPEED_TICK) { // Reset the Speed PID execution counter. pid. Cnt. Speed = 0; // The next instruction executes the PI speed controller and places // its output in Idq_ref_pu. value[1], which is the input reference // value for the q-axis current controller. PID_run_spd(pid. Handle[0], g. Motor. Vars. Speed. Ref_pu, speed_pu, &(g. Idq_ref_pu. value[1])); } else { // increment counter pid. Cnt. Speed++; } 11 // Get the reference value for the d-axis current controller. ref. Value = g. Idq_ref_pu. value[0]; // Get the actual value of Id fback. Value = g. Idq_pu. value[0]; // The next instruction executes the PI current controller for the // d axis and places its output in Vdq_pu. value[0], which is the // control voltage along the d-axis (Vd) PID_run(pid. Handle[1], ref. Value, fback. Value, &(g. Vdq_out_pu. value[0])); // get the Iq reference value ref. Value = g. Idq_ref_pu. value[1]; // get the actual value of Iq fback. Value = g. Idq_pu. value[1]; // The voltage limits on the output of the q-axis current controller // are dynamic, and are dependent on the output voltage from the d-axis // current controller. In other words, the d-axis current controller // gets first dibs on the available voltage, and the q-axis current // controller gets what's left over. That is why the d-axis current // controller executes first. The next instruction calculates the // maximum limits for this voltage as: // Vq_min_max = +/- sqrt(Vbus^2 - Vd^2) out. Max_pu = _IQsqrt(_IQ(USER_MAX_VS_MAG_PU * USER_MAX_VS_MAG_PU) - _IQmpy(g. Vdq_out_pu. value[0], g. Vdq_out_pu. value[0])); // Set the limits to +/- out. Max_pu PID_set. Min. Max(pid. Handle[2], -out. Max_pu, out. Max_pu); // The next instruction executes the PI current controller for the // q axis and places its output in Vdq_pu. value[1], which is the // control voltage vector along the q-axis (Vq) PID_run(pid. Handle[2], ref. Value, fback. Value, &(g. Vdq_out_pu. value[1 ])); 12

proj_lab 11. c // run the estimator // The speed reference is needed so

proj_lab 11. c // run the estimator // The speed reference is needed so that the proper sign of the forced // angle is calculated. When the estimator does not do motor ID as in this // lab, only the sign of the speed reference is used EST_run(est. Handle, &Iab_pu, &Vab_pu, g. Adc. Data. dc. Bus, g. Motor. Vars. Speed. Ref_pu); // generate the motor electrical angle_pu = EST_get. Angle_pu(est. Handle); speed_pu = EST_get. Fm_pu(est. Handle); // get Idq from estimator to avoid sin and cos, and a Park transform, // which saves CPU cycles EST_get. Idq_pu(est. Handle, &g. Idq_pu); // run the appropriate controller if(g. Motor. Vars. Flag_Run_Identify) { // Declaration of local variables. _iq ref. Value; _iq fback. Value; _iq out. Max_pu; // when appropriate, run the PID speed controller // This mechanism provides the decimation for the speed loop. if(pid. Cnt. Speed >= USER_NUM_CTRL_TICKS_PER_SPEED_TICK) { // Reset the Speed PID execution counter. pid. Cnt. Speed = 0; // The next instruction executes the PI speed controller and places // its output in Idq_ref_pu. value[1], which is the input reference // value for the q-axis current controller. PID_run_spd(pid. Handle[0], g. Motor. Vars. Speed. Ref_pu, speed_pu, &(g. Idq_ref_pu. value[1])); } else { // increment counter pid. Cnt. Speed++; } 11 // Get the reference value for the d-axis current controller. ref. Value = g. Idq_ref_pu. value[0]; // Get the actual value of Id fback. Value = g. Idq_pu. value[0]; // The next instruction executes the PI current controller for the // d axis and places its output in Vdq_pu. value[0], which is the // control voltage along the d-axis (Vd) PID_run(pid. Handle[1], ref. Value, fback. Value, &(g. Vdq_out_pu. value[0])); // get the Iq reference value ref. Value = g. Idq_ref_pu. value[1]; // get the actual value of Iq fback. Value = g. Idq_pu. value[1]; // The voltage limits on the output of the q-axis current controller // are dynamic, and are dependent on the output voltage from the d-axis // current controller. In other words, the d-axis current controller // gets first dibs on the available voltage, and the q-axis current // controller gets what's left over. That is why the d-axis current // controller executes first. The next instruction calculates the // maximum limits for this voltage as: // Vq_min_max = +/- sqrt(Vbus^2 - Vd^2) out. Max_pu = _IQsqrt(_IQ(USER_MAX_VS_MAG_PU * USER_MAX_VS_MAG_PU) - _IQmpy(g. Vdq_out_pu. value[0], g. Vdq_out_pu. value[0])); // Set the limits to +/- out. Max_pu PID_set. Min. Max(pid. Handle[2], -out. Max_pu, out. Max_pu); // The next instruction executes the PI current controller for the // q axis and places its output in Vdq_pu. value[1], which is the // control voltage vector along the q-axis (Vq) PID_run(pid. Handle[2], ref. Value, fback. Value, &(g. Vdq_out_pu. value[1 ])); 12

proj_lab 11. c else // Flag_enable. Sys is set AND Flag_Run_Identify is not set

proj_lab 11. c else // Flag_enable. Sys is set AND Flag_Run_Identify is not set { // set estimator to Idle EST_set. Idle(est. Handle); // disable the PWM HAL_disable. Pwm(hal. Handle); // clear integrator outputs PID_set. Ui(pid. Handle[0], _IQ(0. 0)); PID_set. Ui(pid. Handle[1], _IQ(0. 0)); PID_set. Ui(pid. Handle[2], _IQ(0. 0)); // clear Id and Iq references g. Idq_ref_pu. value[0] = _IQ(0. 0); g. Idq_ref_pu. value[1] = _IQ(0. 0); } // update the global variables update. Global. Variables(est. Handle); // enable/disable the forced angle EST_set. Flag_enable. Force. Angle(est. Handle, g. Motor. Vars. Flag_enable. Force. Angle); // set target speed g. Motor. Vars. Speed. Ref_pu = _IQmpy(g. Motor. Vars. Speed. Ref_krpm, g. Speed_krpm_to_pu_sf); #ifdef DRV 8301_SPI HAL_write. Drv. Data(hal. Handle, &g. Drv. Spi 8301 Vars); HAL_read. Drv. Data(hal. Handle, &g. Drv. Spi 8301 Vars); #endif #ifdef DRV 8305_SPI HAL_write. Drv. Data(hal. Handle, &g. Drv. Spi 8305 Vars); HAL_read. Drv. Data(hal. Handle, &g. Drv. Spi 8305 Vars); #endif 9 } // end of while(g. Flag_enable. Sys) loop // disable the PWM HAL_disable. Pwm(hal. Handle); g. Motor. Vars. Flag_Run_Identify = false; } // end of for(; ; ) loop } // end of main() function //! brief The main ISR that implements the motor control. interrupt void main. ISR(void) { // Declaration of local variables _iq angle_pu = _IQ(0. 0); _iq speed_pu = _IQ(0. 0); _iq one. Over. Dc. Bus; MATH_vec 2 Iab_pu; MATH_vec 2 Vab_pu; MATH_vec 2 phasor; // acknowledge the ADC interrupt HAL_acq. Adc. Int(hal. Handle, ADC_Int. Number_1); // convert the ADC data HAL_read. Adc. Data. With. Offsets(hal. Handle, &g. Adc. Data); // remove offsets g. Adc. Data. I. value[0] = g. Adc. Data. I. value[0] - g. Offsets_I_pu. value[0]; g. Adc. Data. I. value[1] = g. Adc. Data. I. value[1] - g. Offsets_I_pu. value[1]; g. Adc. Data. I. value[2] = g. Adc. Data. I. value[2] - g. Offsets_I_pu. value[2]; g. Adc. Data. V. value[0] = g. Adc. Data. V. value[0] - g. Offsets_V_pu. value[0]; g. Adc. Data. V. value[1] = g. Adc. Data. V. value[1] - g. Offsets_V_pu. value[1]; g. Adc. Data. V. value[2] = g. Adc. Data. V. value[2] - g. Offsets_V_pu. value[2]; // run Clarke transform on current. Three values are passed, two values // are returned. CLARKE_run(clarke. Handle_I, &g. Adc. Data. I, &Iab_pu); // run Clarke transform on voltage. Three values are passed, two values // are returned. CLARKE_run(clarke. Handle_V, &g. Adc. Data. V, &Vab_pu); 10

proj_lab 11. c // run the estimator // The speed reference is needed so

proj_lab 11. c // run the estimator // The speed reference is needed so that the proper sign of the forced // angle is calculated. When the estimator does not do motor ID as in this // lab, only the sign of the speed reference is used EST_run(est. Handle, &Iab_pu, &Vab_pu, g. Adc. Data. dc. Bus, g. Motor. Vars. Speed. Ref_pu); // generate the motor electrical angle_pu = EST_get. Angle_pu(est. Handle); speed_pu = EST_get. Fm_pu(est. Handle); // get Idq from estimator to avoid sin and cos, and a Park transform, // which saves CPU cycles EST_get. Idq_pu(est. Handle, &g. Idq_pu); // run the appropriate controller if(g. Motor. Vars. Flag_Run_Identify) { // Declaration of local variables. _iq ref. Value; _iq fback. Value; _iq out. Max_pu; // when appropriate, run the PID speed controller // This mechanism provides the decimation for the speed loop. if(pid. Cnt. Speed >= USER_NUM_CTRL_TICKS_PER_SPEED_TICK) { // Reset the Speed PID execution counter. pid. Cnt. Speed = 0; // The next instruction executes the PI speed controller and places // its output in Idq_ref_pu. value[1], which is the input reference // value for the q-axis current controller. PID_run_spd(pid. Handle[0], g. Motor. Vars. Speed. Ref_pu, speed_pu, &(g. Idq_ref_pu. value[1])); } else { // increment counter pid. Cnt. Speed++; } 11 // Get the reference value for the d-axis current controller. ref. Value = g. Idq_ref_pu. value[0]; // Get the actual value of Id fback. Value = g. Idq_pu. value[0]; // The next instruction executes the PI current controller for the // d axis and places its output in Vdq_pu. value[0], which is the // control voltage along the d-axis (Vd) PID_run(pid. Handle[1], ref. Value, fback. Value, &(g. Vdq_out_pu. value[0])); // get the Iq reference value ref. Value = g. Idq_ref_pu. value[1]; // get the actual value of Iq fback. Value = g. Idq_pu. value[1]; // The voltage limits on the output of the q-axis current controller // are dynamic, and are dependent on the output voltage from the d-axis // current controller. In other words, the d-axis current controller // gets first dibs on the available voltage, and the q-axis current // controller gets what's left over. That is why the d-axis current // controller executes first. The next instruction calculates the // maximum limits for this voltage as: // Vq_min_max = +/- sqrt(Vbus^2 - Vd^2) out. Max_pu = _IQsqrt(_IQ(USER_MAX_VS_MAG_PU * USER_MAX_VS_MAG_PU) - _IQmpy(g. Vdq_out_pu. value[0], g. Vdq_out_pu. value[0])); // Set the limits to +/- out. Max_pu PID_set. Min. Max(pid. Handle[2], -out. Max_pu, out. Max_pu); // The next instruction executes the PI current controller for the // q axis and places its output in Vdq_pu. value[1], which is the // control voltage vector along the q-axis (Vq) PID_run(pid. Handle[2], ref. Value, fback. Value, &(g. Vdq_out_pu. value[1 ])); 12

proj_lab 11. c // For the current controller, Kp = Ls*bandwidth(rad/sec) But in order

proj_lab 11. c // For the current controller, Kp = Ls*bandwidth(rad/sec) But in order // to be used, it must be converted to per unit values by multiplying // by full. Scale. Current and then dividing by full. Scale. Voltage. From the // statement below, we see that the bandwidth in rad/sec is equal to // 0. 25/Isr. Period_sec, which is equal to USER_ISR_FREQ_HZ/4. This means // that by setting Kp as described below, the bandwidth in Hz is // USER_ISR_FREQ_HZ/(8*pi). _iq Kp_Id = _IQ((0. 25 * Ls_d * full. Scale. Current) / (Isr. Period_sec * full. Scale. Voltage)); // In order to achieve pole/zero cancellation (which reduces the // closed-loop transfer function from a second-order system to a // first-order system), Ki must equal Rs/Ls. Since the output of the // Ki gain stage is integrated by a DIGITAL integrator, the integrator // input must be scaled by 1/Isr. Period_sec. That's just the way // digital integrators work. But, since Isr. Period_sec is a constant, // we can save an additional multiplication operation by lumping this // term with the Ki value. _iq Ki_Id = _IQ(Rover. Ls_d * Isr. Period_sec); // Now do the same thing for Kp for the q-axis current controller. // If the motor is not an IPM motor, Ld and Lq are the same, which // means that Kp_Iq = Kp_Id _iq Kp_Iq = _IQ((0. 25 * Ls_q * full. Scale. Current) / (Isr. Period_sec * full. Scale. Voltage)); // Do the same thing for Ki for the q-axis current controller. If the // motor is not an IPM motor, Ld and Lq are the same, which means that // Ki_Iq = Ki_Id. _iq Ki_Iq = _IQ(Rover. Ls_q * Isr. Period_sec); // There are three PI controllers; one speed controller and two current // controllers. Each PI controller has two coefficients; Kp and Ki. // So you have a total of six coefficients that must be defined. // This is for the speed controller pid. Handle[0] = PID_init(&pid[0], sizeof(pid[0])); // This is for the Id current controller pid. Handle[1] = PID_init(&pid[1], sizeof(pid[1])); // This is for the Iq current controller pid. Handle[2] = PID_init(&pid[2], sizeof(pid[2])); 5 // The following instructions load the parameters for the speed PI // controller. PID_set. Gains(pid. Handle[0], _IQ(1. 0), _IQ(0. 01), _IQ(0. 0)); // The current limit is performed by the limits placed on the speed PI // controller output. In the following statement, the speed // controller's largest negative current is set to -max. Current_pu, and // the largest positive current is set to max. Current_pu. PID_set. Min. Max(pid. Handle[0], -max. Current_pu, max. Current_pu); PID_set. Ui(pid. Handle[0], _IQ(0. 0)); // Set the initial condition value // for the integrator output to 0 pid. Cnt. Speed = 0; // Set the counter for decimating the speed // controller to 0 // The following instructions load the parameters for the d-axis // current controller. // P term = Kp_Id, I term = Ki_Id, D term = 0 PID_set. Gains(pid. Handle[1], Kp_Id, Ki_Id, _IQ(0. 0)); // Largest negative voltage = -max. Voltage_pu, largest positive // voltage = max. Voltage_pu PID_set. Min. Max(pid. Handle[1], -max. Voltage_pu, max. Voltage_pu); // Set the initial condition value for the integrator output to 0 PID_set. Ui(pid. Handle[1], _IQ(0. 0)); // The following instructions load the parameters for the q-axis // current controller. // P term = Kp_Iq, I term = Ki_Iq, D term = 0 PID_set. Gains(pid. Handle[2], Kp_Iq, Ki_Iq, _IQ(0. 0)); // The largest negative voltage = 0 and the largest positive // voltage = 0. But these limits are updated every single ISR before // actually executing the Iq controller. The limits depend on how much // voltage is left over after the Id controller executes. So having an // initial value of 0 does not affect Iq current controller execution. PID_set. Min. Max(pid. Handle[2], _IQ(0. 0)); // Set the initial condition value for the integrator output to 0 PID_set. Ui(pid. Handle[2], _IQ(0. 0)); } 6

proj_lab 11. c // For the current controller, Kp = Ls*bandwidth(rad/sec) But in order

proj_lab 11. c // For the current controller, Kp = Ls*bandwidth(rad/sec) But in order // to be used, it must be converted to per unit values by multiplying // by full. Scale. Current and then dividing by full. Scale. Voltage. From the // statement below, we see that the bandwidth in rad/sec is equal to // 0. 25/Isr. Period_sec, which is equal to USER_ISR_FREQ_HZ/4. This means // that by setting Kp as described below, the bandwidth in Hz is // USER_ISR_FREQ_HZ/(8*pi). _iq Kp_Id = _IQ((0. 25 * Ls_d * full. Scale. Current) / (Isr. Period_sec * full. Scale. Voltage)); // In order to achieve pole/zero cancellation (which reduces the // closed-loop transfer function from a second-order system to a // first-order system), Ki must equal Rs/Ls. Since the output of the // Ki gain stage is integrated by a DIGITAL integrator, the integrator // input must be scaled by 1/Isr. Period_sec. That's just the way // digital integrators work. But, since Isr. Period_sec is a constant, // we can save an additional multiplication operation by lumping this // term with the Ki value. _iq Ki_Id = _IQ(Rover. Ls_d * Isr. Period_sec); // Now do the same thing for Kp for the q-axis current controller. // If the motor is not an IPM motor, Ld and Lq are the same, which // means that Kp_Iq = Kp_Id _iq Kp_Iq = _IQ((0. 25 * Ls_q * full. Scale. Current) / (Isr. Period_sec * full. Scale. Voltage)); // Do the same thing for Ki for the q-axis current controller. If the // motor is not an IPM motor, Ld and Lq are the same, which means that // Ki_Iq = Ki_Id. _iq Ki_Iq = _IQ(Rover. Ls_q * Isr. Period_sec); // There are three PI controllers; one speed controller and two current // controllers. Each PI controller has two coefficients; Kp and Ki. // So you have a total of six coefficients that must be defined. // This is for the speed controller pid. Handle[0] = PID_init(&pid[0], sizeof(pid[0])); // This is for the Id current controller pid. Handle[1] = PID_init(&pid[1], sizeof(pid[1])); // This is for the Iq current controller pid. Handle[2] = PID_init(&pid[2], sizeof(pid[2])); 5 // The following instructions load the parameters for the speed PI // controller. PID_set. Gains(pid. Handle[0], _IQ(1. 0), _IQ(0. 01), _IQ(0. 0)); // The current limit is performed by the limits placed on the speed PI // controller output. In the following statement, the speed // controller's largest negative current is set to -max. Current_pu, and // the largest positive current is set to max. Current_pu. PID_set. Min. Max(pid. Handle[0], -max. Current_pu, max. Current_pu); PID_set. Ui(pid. Handle[0], _IQ(0. 0)); // Set the initial condition value // for the integrator output to 0 pid. Cnt. Speed = 0; // Set the counter for decimating the speed // controller to 0 // The following instructions load the parameters for the d-axis // current controller. // P term = Kp_Id, I term = Ki_Id, D term = 0 PID_set. Gains(pid. Handle[1], Kp_Id, Ki_Id, _IQ(0. 0)); // Largest negative voltage = -max. Voltage_pu, largest positive // voltage = max. Voltage_pu PID_set. Min. Max(pid. Handle[1], -max. Voltage_pu, max. Voltage_pu); // Set the initial condition value for the integrator output to 0 PID_set. Ui(pid. Handle[1], _IQ(0. 0)); // The following instructions load the parameters for the q-axis // current controller. // P term = Kp_Iq, I term = Ki_Iq, D term = 0 PID_set. Gains(pid. Handle[2], Kp_Iq, Ki_Iq, _IQ(0. 0)); // The largest negative voltage = 0 and the largest positive // voltage = 0. But these limits are updated every single ISR before // actually executing the Iq controller. The limits depend on how much // voltage is left over after the Id controller executes. So having an // initial value of 0 does not affect Iq current controller execution. PID_set. Min. Max(pid. Handle[2], _IQ(0. 0)); // Set the initial condition value for the integrator output to 0 PID_set. Ui(pid. Handle[2], _IQ(0. 0)); } 6

proj_lab 11. c // For the current controller, Kp = Ls*bandwidth(rad/sec) But in order

proj_lab 11. c // For the current controller, Kp = Ls*bandwidth(rad/sec) But in order // to be used, it must be converted to per unit values by multiplying // by full. Scale. Current and then dividing by full. Scale. Voltage. From the // statement below, we see that the bandwidth in rad/sec is equal to // 0. 25/Isr. Period_sec, which is equal to USER_ISR_FREQ_HZ/4. This means // that by setting Kp as described below, the bandwidth in Hz is // USER_ISR_FREQ_HZ/(8*pi). _iq Kp_Id = _IQ((0. 25 * Ls_d * full. Scale. Current) / (Isr. Period_sec * full. Scale. Voltage)); // In order to achieve pole/zero cancellation (which reduces the // closed-loop transfer function from a second-order system to a // first-order system), Ki must equal Rs/Ls. Since the output of the // Ki gain stage is integrated by a DIGITAL integrator, the integrator // input must be scaled by 1/Isr. Period_sec. That's just the way // digital integrators work. But, since Isr. Period_sec is a constant, // we can save an additional multiplication operation by lumping this // term with the Ki value. _iq Ki_Id = _IQ(Rover. Ls_d * Isr. Period_sec); // Now do the same thing for Kp for the q-axis current controller. // If the motor is not an IPM motor, Ld and Lq are the same, which // means that Kp_Iq = Kp_Id _iq Kp_Iq = _IQ((0. 25 * Ls_q * full. Scale. Current) / (Isr. Period_sec * full. Scale. Voltage)); // Do the same thing for Ki for the q-axis current controller. If the // motor is not an IPM motor, Ld and Lq are the same, which means that // Ki_Iq = Ki_Id. _iq Ki_Iq = _IQ(Rover. Ls_q * Isr. Period_sec); // There are three PI controllers; one speed controller and two current // controllers. Each PI controller has two coefficients; Kp and Ki. // So you have a total of six coefficients that must be defined. // This is for the speed controller pid. Handle[0] = PID_init(&pid[0], sizeof(pid[0])); // This is for the Id current controller pid. Handle[1] = PID_init(&pid[1], sizeof(pid[1])); // This is for the Iq current controller pid. Handle[2] = PID_init(&pid[2], sizeof(pid[2])); 5 // The following instructions load the parameters for the speed PI // controller. PID_set. Gains(pid. Handle[0], _IQ(1. 0), _IQ(0. 01), _IQ(0. 0)); // The current limit is performed by the limits placed on the speed PI // controller output. In the following statement, the speed // controller's largest negative current is set to -max. Current_pu, and // the largest positive current is set to max. Current_pu. PID_set. Min. Max(pid. Handle[0], -max. Current_pu, max. Current_pu); PID_set. Ui(pid. Handle[0], _IQ(0. 0)); // Set the initial condition value // for the integrator output to 0 pid. Cnt. Speed = 0; // Set the counter for decimating the speed // controller to 0 // The following instructions load the parameters for the d-axis // current controller. // P term = Kp_Id, I term = Ki_Id, D term = 0 PID_set. Gains(pid. Handle[1], Kp_Id, Ki_Id, _IQ(0. 0)); // Largest negative voltage = -max. Voltage_pu, largest positive // voltage = max. Voltage_pu PID_set. Min. Max(pid. Handle[1], -max. Voltage_pu, max. Voltage_pu); // Set the initial condition value for the integrator output to 0 PID_set. Ui(pid. Handle[1], _IQ(0. 0)); // The following instructions load the parameters for the q-axis // current controller. // P term = Kp_Iq, I term = Ki_Iq, D term = 0 PID_set. Gains(pid. Handle[2], Kp_Iq, Ki_Iq, _IQ(0. 0)); // The largest negative voltage = 0 and the largest positive // voltage = 0. But these limits are updated every single ISR before // actually executing the Iq controller. The limits depend on how much // voltage is left over after the Id controller executes. So having an // initial value of 0 does not affect Iq current controller execution. PID_set. Min. Max(pid. Handle[2], _IQ(0. 0)); // Set the initial condition value for the integrator output to 0 PID_set. Ui(pid. Handle[2], _IQ(0. 0)); } 6

proj_lab 11. c // set the hardware abstraction layer parameters // This function initializes

proj_lab 11. c // set the hardware abstraction layer parameters // This function initializes all peripherals through a Hardware Abstraction // Layer (HAL). It uses all values stored in g. User. Params. HAL_set. Params(hal. Handle, &g. User. Params); #ifdef FAST_ROM_V 1 p 6 { // These function calls are used to initialize the estimator with ROM // function calls. It needs the specific address where the controller // object is declared by the ROM code. CTRL_Handle ctrl. Handle = CTRL_init((void *)USER_CTRL_HANDLE_ADDRESS , 0 x 200); CTRL_Obj *obj = (CTRL_Obj *)ctrl. Handle; // this sets the estimator handle (part of the controller object) to // the same value initialized above by the EST_init() function call. // This is done so the next function implemented in ROM, can // successfully initialize the estimator as part of the controller // object. obj->est. Handle = est. Handle; // initialize the estimator through the controller. These three // function calls are needed for the F 2806 x. F/M implementation of // Insta. SPIN. CTRL_set. Params(ctrl. Handle, &g. User. Params); CTRL_set. User. Motor. Params(ctrl. Handle); CTRL_setup. Est. Idle. State(ctrl. Handle); } #else { // initialize the estimator. These two function calls are needed for // the F 2802 x. F implementation of Insta. SPIN using the estimator handle // initialized by EST_init(), these two function calls configure the // estimator, and they set the estimator in a proper state prior to // spinning a motor. EST_set. Est. Params(est. Handle, &g. User. Params); EST_setup. Est. Idle. State(est. Handle); } #endif 3 // disable Rs recalculation EST_set. Flag_enable. Rs. Recalc(est. Handle, false); // set the number of current sensors setup. Clarke_I(clarke. Handle_I, USER_NUM_CURRENT_SENSORS); // set the number of voltage sensors setup. Clarke_V(clarke. Handle_V, USER_NUM_VOLTAGE_SENSORS); // set the pre-determined current and voltage feedback offset values g. Offsets_I_pu. value[0] = _IQ(I_A_offset); g. Offsets_I_pu. value[1] = _IQ(I_B_offset); g. Offsets_I_pu. value[2] = _IQ(I_C_offset); g. Offsets_V_pu. value[0] = _IQ(V_A_offset); g. Offsets_V_pu. value[1] = _IQ(V_B_offset); g. Offsets_V_pu. value[2] = _IQ(V_C_offset); // initialize the PID controllers { // This equation defines the relationship between per unit current and // real-world current. The resulting value in per units (pu) is then // used to configure the controllers _iq max. Current_pu = _IQ(USER_MOTOR_MAX_CURRENT / USER_IQ_FULL_SCALE_CURRENT_A); // This equation uses the scaled maximum voltage vector, which is // already in per units, hence there is no need to include the #define // for USER_IQ_FULL_SCALE_VOLTAGE_V _iq max. Voltage_pu = _IQ(USER_MAX_VS_MAG_PU * USER_VD_SF); float_t full. Scale. Current = USER_IQ_FULL_SCALE_CURRENT_A; float_t full. Scale. Voltage = USER_IQ_FULL_SCALE_VOLTAGE_V; float_t Isr. Period_sec = 1. 0 / USER_ISR_FREQ_Hz; float_t Ls_d = USER_MOTOR_Ls_d; float_t Ls_q = USER_MOTOR_Ls_q; float_t Rs = USER_MOTOR_Rs; // This lab assumes that motor parameters are known, and it does not // perform motor ID, so the R/L parameters are known and defined in // user. h float_t Rover. Ls_d = Rs / Ls_d; float_t Rover. Ls_q = Rs / Ls_q; 4

proj_lab 11. c // For the current controller, Kp = Ls*bandwidth(rad/sec) But in order

proj_lab 11. c // For the current controller, Kp = Ls*bandwidth(rad/sec) But in order // to be used, it must be converted to per unit values by multiplying // by full. Scale. Current and then dividing by full. Scale. Voltage. From the // statement below, we see that the bandwidth in rad/sec is equal to // 0. 25/Isr. Period_sec, which is equal to USER_ISR_FREQ_HZ/4. This means // that by setting Kp as described below, the bandwidth in Hz is // USER_ISR_FREQ_HZ/(8*pi). _iq Kp_Id = _IQ((0. 25 * Ls_d * full. Scale. Current) / (Isr. Period_sec * full. Scale. Voltage)); // In order to achieve pole/zero cancellation (which reduces the // closed-loop transfer function from a second-order system to a // first-order system), Ki must equal Rs/Ls. Since the output of the // Ki gain stage is integrated by a DIGITAL integrator, the integrator // input must be scaled by 1/Isr. Period_sec. That's just the way // digital integrators work. But, since Isr. Period_sec is a constant, // we can save an additional multiplication operation by lumping this // term with the Ki value. _iq Ki_Id = _IQ(Rover. Ls_d * Isr. Period_sec); // Now do the same thing for Kp for the q-axis current controller. // If the motor is not an IPM motor, Ld and Lq are the same, which // means that Kp_Iq = Kp_Id _iq Kp_Iq = _IQ((0. 25 * Ls_q * full. Scale. Current) / (Isr. Period_sec * full. Scale. Voltage)); // Do the same thing for Ki for the q-axis current controller. If the // motor is not an IPM motor, Ld and Lq are the same, which means that // Ki_Iq = Ki_Id. _iq Ki_Iq = _IQ(Rover. Ls_q * Isr. Period_sec); // There are three PI controllers; one speed controller and two current // controllers. Each PI controller has two coefficients; Kp and Ki. // So you have a total of six coefficients that must be defined. // This is for the speed controller pid. Handle[0] = PID_init(&pid[0], sizeof(pid[0])); // This is for the Id current controller pid. Handle[1] = PID_init(&pid[1], sizeof(pid[1])); // This is for the Iq current controller pid. Handle[2] = PID_init(&pid[2], sizeof(pid[2])); 5 // The following instructions load the parameters for the speed PI // controller. PID_set. Gains(pid. Handle[0], _IQ(1. 0), _IQ(0. 01), _IQ(0. 0)); // The current limit is performed by the limits placed on the speed PI // controller output. In the following statement, the speed // controller's largest negative current is set to -max. Current_pu, and // the largest positive current is set to max. Current_pu. PID_set. Min. Max(pid. Handle[0], -max. Current_pu, max. Current_pu); PID_set. Ui(pid. Handle[0], _IQ(0. 0)); // Set the initial condition value // for the integrator output to 0 pid. Cnt. Speed = 0; // Set the counter for decimating the speed // controller to 0 // The following instructions load the parameters for the d-axis // current controller. // P term = Kp_Id, I term = Ki_Id, D term = 0 PID_set. Gains(pid. Handle[1], Kp_Id, Ki_Id, _IQ(0. 0)); // Largest negative voltage = -max. Voltage_pu, largest positive // voltage = max. Voltage_pu PID_set. Min. Max(pid. Handle[1], -max. Voltage_pu, max. Voltage_pu); // Set the initial condition value for the integrator output to 0 PID_set. Ui(pid. Handle[1], _IQ(0. 0)); // The following instructions load the parameters for the q-axis // current controller. // P term = Kp_Iq, I term = Ki_Iq, D term = 0 PID_set. Gains(pid. Handle[2], Kp_Iq, Ki_Iq, _IQ(0. 0)); // The largest negative voltage = 0 and the largest positive // voltage = 0. But these limits are updated every single ISR before // actually executing the Iq controller. The limits depend on how much // voltage is left over after the Id controller executes. So having an // initial value of 0 does not affect Iq current controller execution. PID_set. Min. Max(pid. Handle[2], _IQ(0. 0)); // Set the initial condition value for the integrator output to 0 PID_set. Ui(pid. Handle[2], _IQ(0. 0)); } 6

proj_lab 11. c // For the current controller, Kp = Ls*bandwidth(rad/sec) But in order

proj_lab 11. c // For the current controller, Kp = Ls*bandwidth(rad/sec) But in order // to be used, it must be converted to per unit values by multiplying // by full. Scale. Current and then dividing by full. Scale. Voltage. From the // statement below, we see that the bandwidth in rad/sec is equal to // 0. 25/Isr. Period_sec, which is equal to USER_ISR_FREQ_HZ/4. This means // that by setting Kp as described below, the bandwidth in Hz is // USER_ISR_FREQ_HZ/(8*pi). _iq Kp_Id = _IQ((0. 25 * Ls_d * full. Scale. Current) / (Isr. Period_sec * full. Scale. Voltage)); // In order to achieve pole/zero cancellation (which reduces the // closed-loop transfer function from a second-order system to a // first-order system), Ki must equal Rs/Ls. Since the output of the // Ki gain stage is integrated by a DIGITAL integrator, the integrator // input must be scaled by 1/Isr. Period_sec. That's just the way // digital integrators work. But, since Isr. Period_sec is a constant, // we can save an additional multiplication operation by lumping this // term with the Ki value. _iq Ki_Id = _IQ(Rover. Ls_d * Isr. Period_sec); // Now do the same thing for Kp for the q-axis current controller. // If the motor is not an IPM motor, Ld and Lq are the same, which // means that Kp_Iq = Kp_Id _iq Kp_Iq = _IQ((0. 25 * Ls_q * full. Scale. Current) / (Isr. Period_sec * full. Scale. Voltage)); // Do the same thing for Ki for the q-axis current controller. If the // motor is not an IPM motor, Ld and Lq are the same, which means that // Ki_Iq = Ki_Id. _iq Ki_Iq = _IQ(Rover. Ls_q * Isr. Period_sec); // There are three PI controllers; one speed controller and two current // controllers. Each PI controller has two coefficients; Kp and Ki. // So you have a total of six coefficients that must be defined. // This is for the speed controller pid. Handle[0] = PID_init(&pid[0], sizeof(pid[0])); // This is for the Id current controller pid. Handle[1] = PID_init(&pid[1], sizeof(pid[1])); // This is for the Iq current controller pid. Handle[2] = PID_init(&pid[2], sizeof(pid[2])); 5 // The following instructions load the parameters for the speed PI // controller. PID_set. Gains(pid. Handle[0], _IQ(1. 0), _IQ(0. 01), _IQ(0. 0)); // The current limit is performed by the limits placed on the speed PI // controller output. In the following statement, the speed // controller's largest negative current is set to -max. Current_pu, and // the largest positive current is set to max. Current_pu. PID_set. Min. Max(pid. Handle[0], -max. Current_pu, max. Current_pu); PID_set. Ui(pid. Handle[0], _IQ(0. 0)); // Set the initial condition value // for the integrator output to 0 pid. Cnt. Speed = 0; // Set the counter for decimating the speed // controller to 0 // The following instructions load the parameters for the d-axis // current controller. // P term = Kp_Id, I term = Ki_Id, D term = 0 PID_set. Gains(pid. Handle[1], Kp_Id, Ki_Id, _IQ(0. 0)); // Largest negative voltage = -max. Voltage_pu, largest positive // voltage = max. Voltage_pu PID_set. Min. Max(pid. Handle[1], -max. Voltage_pu, max. Voltage_pu); // Set the initial condition value for the integrator output to 0 PID_set. Ui(pid. Handle[1], _IQ(0. 0)); // The following instructions load the parameters for the q-axis // current controller. // P term = Kp_Iq, I term = Ki_Iq, D term = 0 PID_set. Gains(pid. Handle[2], Kp_Iq, Ki_Iq, _IQ(0. 0)); // The largest negative voltage = 0 and the largest positive // voltage = 0. But these limits are updated every single ISR before // actually executing the Iq controller. The limits depend on how much // voltage is left over after the Id controller executes. So having an // initial value of 0 does not affect Iq current controller execution. PID_set. Min. Max(pid. Handle[2], _IQ(0. 0)); // Set the initial condition value for the integrator output to 0 PID_set. Ui(pid. Handle[2], _IQ(0. 0)); } 6

proj_lab 11. c // For the current controller, Kp = Ls*bandwidth(rad/sec) But in order

proj_lab 11. c // For the current controller, Kp = Ls*bandwidth(rad/sec) But in order // to be used, it must be converted to per unit values by multiplying // by full. Scale. Current and then dividing by full. Scale. Voltage. From the // statement below, we see that the bandwidth in rad/sec is equal to // 0. 25/Isr. Period_sec, which is equal to USER_ISR_FREQ_HZ/4. This means // that by setting Kp as described below, the bandwidth in Hz is // USER_ISR_FREQ_HZ/(8*pi). _iq Kp_Id = _IQ((0. 25 * Ls_d * full. Scale. Current) / (Isr. Period_sec * full. Scale. Voltage)); // In order to achieve pole/zero cancellation (which reduces the // closed-loop transfer function from a second-order system to a // first-order system), Ki must equal Rs/Ls. Since the output of the // Ki gain stage is integrated by a DIGITAL integrator, the integrator // input must be scaled by 1/Isr. Period_sec. That's just the way // digital integrators work. But, since Isr. Period_sec is a constant, // we can save an additional multiplication operation by lumping this // term with the Ki value. _iq Ki_Id = _IQ(Rover. Ls_d * Isr. Period_sec); // Now do the same thing for Kp for the q-axis current controller. // If the motor is not an IPM motor, Ld and Lq are the same, which // means that Kp_Iq = Kp_Id _iq Kp_Iq = _IQ((0. 25 * Ls_q * full. Scale. Current) / (Isr. Period_sec * full. Scale. Voltage)); // Do the same thing for Ki for the q-axis current controller. If the // motor is not an IPM motor, Ld and Lq are the same, which means that // Ki_Iq = Ki_Id. _iq Ki_Iq = _IQ(Rover. Ls_q * Isr. Period_sec); // There are three PI controllers; one speed controller and two current // controllers. Each PI controller has two coefficients; Kp and Ki. // So you have a total of six coefficients that must be defined. // This is for the speed controller pid. Handle[0] = PID_init(&pid[0], sizeof(pid[0])); // This is for the Id current controller pid. Handle[1] = PID_init(&pid[1], sizeof(pid[1])); // This is for the Iq current controller pid. Handle[2] = PID_init(&pid[2], sizeof(pid[2])); 5 // The following instructions load the parameters for the speed PI // controller. PID_set. Gains(pid. Handle[0], _IQ(1. 0), _IQ(0. 01), _IQ(0. 0)); // The current limit is performed by the limits placed on the speed PI // controller output. In the following statement, the speed // controller's largest negative current is set to -max. Current_pu, and // the largest positive current is set to max. Current_pu. PID_set. Min. Max(pid. Handle[0], -max. Current_pu, max. Current_pu); PID_set. Ui(pid. Handle[0], _IQ(0. 0)); // Set the initial condition value // for the integrator output to 0 pid. Cnt. Speed = 0; // Set the counter for decimating the speed // controller to 0 // The following instructions load the parameters for the d-axis // current controller. // P term = Kp_Id, I term = Ki_Id, D term = 0 PID_set. Gains(pid. Handle[1], Kp_Id, Ki_Id, _IQ(0. 0)); // Largest negative voltage = -max. Voltage_pu, largest positive // voltage = max. Voltage_pu PID_set. Min. Max(pid. Handle[1], -max. Voltage_pu, max. Voltage_pu); // Set the initial condition value for the integrator output to 0 PID_set. Ui(pid. Handle[1], _IQ(0. 0)); // The following instructions load the parameters for the q-axis // current controller. // P term = Kp_Iq, I term = Ki_Iq, D term = 0 PID_set. Gains(pid. Handle[2], Kp_Iq, Ki_Iq, _IQ(0. 0)); // The largest negative voltage = 0 and the largest positive // voltage = 0. But these limits are updated every single ISR before // actually executing the Iq controller. The limits depend on how much // voltage is left over after the Id controller executes. So having an // initial value of 0 does not affect Iq current controller execution. PID_set. Min. Max(pid. Handle[2], _IQ(0. 0)); // Set the initial condition value for the integrator output to 0 PID_set. Ui(pid. Handle[2], _IQ(0. 0)); } 6

proj_lab 11. c else // Flag_enable. Sys is set AND Flag_Run_Identify is not set

proj_lab 11. c else // Flag_enable. Sys is set AND Flag_Run_Identify is not set { // set estimator to Idle EST_set. Idle(est. Handle); // disable the PWM HAL_disable. Pwm(hal. Handle); // clear integrator outputs PID_set. Ui(pid. Handle[0], _IQ(0. 0)); PID_set. Ui(pid. Handle[1], _IQ(0. 0)); PID_set. Ui(pid. Handle[2], _IQ(0. 0)); // clear Id and Iq references g. Idq_ref_pu. value[0] = _IQ(0. 0); g. Idq_ref_pu. value[1] = _IQ(0. 0); } // update the global variables update. Global. Variables(est. Handle); // enable/disable the forced angle EST_set. Flag_enable. Force. Angle(est. Handle, g. Motor. Vars. Flag_enable. Force. Angle); // set target speed g. Motor. Vars. Speed. Ref_pu = _IQmpy(g. Motor. Vars. Speed. Ref_krpm, g. Speed_krpm_to_pu_sf); #ifdef DRV 8301_SPI HAL_write. Drv. Data(hal. Handle, &g. Drv. Spi 8301 Vars); HAL_read. Drv. Data(hal. Handle, &g. Drv. Spi 8301 Vars); #endif #ifdef DRV 8305_SPI HAL_write. Drv. Data(hal. Handle, &g. Drv. Spi 8305 Vars); HAL_read. Drv. Data(hal. Handle, &g. Drv. Spi 8305 Vars); #endif 9 } // end of while(g. Flag_enable. Sys) loop // disable the PWM HAL_disable. Pwm(hal. Handle); g. Motor. Vars. Flag_Run_Identify = false; } // end of for(; ; ) loop } // end of main() function //! brief The main ISR that implements the motor control. interrupt void main. ISR(void) { // Declaration of local variables _iq angle_pu = _IQ(0. 0); _iq speed_pu = _IQ(0. 0); _iq one. Over. Dc. Bus; MATH_vec 2 Iab_pu; MATH_vec 2 Vab_pu; MATH_vec 2 phasor; // acknowledge the ADC interrupt HAL_acq. Adc. Int(hal. Handle, ADC_Int. Number_1); // convert the ADC data HAL_read. Adc. Data. With. Offsets(hal. Handle, &g. Adc. Data); // remove offsets g. Adc. Data. I. value[0] = g. Adc. Data. I. value[0] - g. Offsets_I_pu. value[0]; g. Adc. Data. I. value[1] = g. Adc. Data. I. value[1] - g. Offsets_I_pu. value[1]; g. Adc. Data. I. value[2] = g. Adc. Data. I. value[2] - g. Offsets_I_pu. value[2]; g. Adc. Data. V. value[0] = g. Adc. Data. V. value[0] - g. Offsets_V_pu. value[0]; g. Adc. Data. V. value[1] = g. Adc. Data. V. value[1] - g. Offsets_V_pu. value[1]; g. Adc. Data. V. value[2] = g. Adc. Data. V. value[2] - g. Offsets_V_pu. value[2]; // run Clarke transform on current. Three values are passed, two values // are returned. CLARKE_run(clarke. Handle_I, &g. Adc. Data. I, &Iab_pu); // run Clarke transform on voltage. Three values are passed, two values // are returned. CLARKE_run(clarke. Handle_V, &g. Adc. Data. V, &Vab_pu); 10

proj_lab 11. c else // Flag_enable. Sys is set AND Flag_Run_Identify is not set

proj_lab 11. c else // Flag_enable. Sys is set AND Flag_Run_Identify is not set { // set estimator to Idle EST_set. Idle(est. Handle); // disable the PWM HAL_disable. Pwm(hal. Handle); // clear integrator outputs PID_set. Ui(pid. Handle[0], _IQ(0. 0)); PID_set. Ui(pid. Handle[1], _IQ(0. 0)); PID_set. Ui(pid. Handle[2], _IQ(0. 0)); // clear Id and Iq references g. Idq_ref_pu. value[0] = _IQ(0. 0); g. Idq_ref_pu. value[1] = _IQ(0. 0); } // update the global variables update. Global. Variables(est. Handle); // enable/disable the forced angle EST_set. Flag_enable. Force. Angle(est. Handle, g. Motor. Vars. Flag_enable. Force. Angle); // set target speed g. Motor. Vars. Speed. Ref_pu = _IQmpy(g. Motor. Vars. Speed. Ref_krpm, g. Speed_krpm_to_pu_sf); #ifdef DRV 8301_SPI HAL_write. Drv. Data(hal. Handle, &g. Drv. Spi 8301 Vars); HAL_read. Drv. Data(hal. Handle, &g. Drv. Spi 8301 Vars); #endif #ifdef DRV 8305_SPI HAL_write. Drv. Data(hal. Handle, &g. Drv. Spi 8305 Vars); HAL_read. Drv. Data(hal. Handle, &g. Drv. Spi 8305 Vars); #endif 9 } // end of while(g. Flag_enable. Sys) loop // disable the PWM HAL_disable. Pwm(hal. Handle); g. Motor. Vars. Flag_Run_Identify = false; } // end of for(; ; ) loop } // end of main() function //! brief The main ISR that implements the motor control. interrupt void main. ISR(void) { // Declaration of local variables _iq angle_pu = _IQ(0. 0); _iq speed_pu = _IQ(0. 0); _iq one. Over. Dc. Bus; MATH_vec 2 Iab_pu; MATH_vec 2 Vab_pu; MATH_vec 2 phasor; // acknowledge the ADC interrupt HAL_acq. Adc. Int(hal. Handle, ADC_Int. Number_1); // convert the ADC data HAL_read. Adc. Data. With. Offsets(hal. Handle, &g. Adc. Data); // remove offsets g. Adc. Data. I. value[0] = g. Adc. Data. I. value[0] - g. Offsets_I_pu. value[0]; g. Adc. Data. I. value[1] = g. Adc. Data. I. value[1] - g. Offsets_I_pu. value[1]; g. Adc. Data. I. value[2] = g. Adc. Data. I. value[2] - g. Offsets_I_pu. value[2]; g. Adc. Data. V. value[0] = g. Adc. Data. V. value[0] - g. Offsets_V_pu. value[0]; g. Adc. Data. V. value[1] = g. Adc. Data. V. value[1] - g. Offsets_V_pu. value[1]; g. Adc. Data. V. value[2] = g. Adc. Data. V. value[2] - g. Offsets_V_pu. value[2]; // run Clarke transform on current. Three values are passed, two values // are returned. CLARKE_run(clarke. Handle_I, &g. Adc. Data. I, &Iab_pu); // run Clarke transform on voltage. Three values are passed, two values // are returned. CLARKE_run(clarke. Handle_V, &g. Adc. Data. V, &Vab_pu); 10

proj_lab 11. c else // Flag_enable. Sys is set AND Flag_Run_Identify is not set

proj_lab 11. c else // Flag_enable. Sys is set AND Flag_Run_Identify is not set { // set estimator to Idle EST_set. Idle(est. Handle); // disable the PWM HAL_disable. Pwm(hal. Handle); // clear integrator outputs PID_set. Ui(pid. Handle[0], _IQ(0. 0)); PID_set. Ui(pid. Handle[1], _IQ(0. 0)); PID_set. Ui(pid. Handle[2], _IQ(0. 0)); // clear Id and Iq references g. Idq_ref_pu. value[0] = _IQ(0. 0); g. Idq_ref_pu. value[1] = _IQ(0. 0); } // update the global variables update. Global. Variables(est. Handle); // enable/disable the forced angle EST_set. Flag_enable. Force. Angle(est. Handle, g. Motor. Vars. Flag_enable. Force. Angle); // set target speed g. Motor. Vars. Speed. Ref_pu = _IQmpy(g. Motor. Vars. Speed. Ref_krpm, g. Speed_krpm_to_pu_sf); #ifdef DRV 8301_SPI HAL_write. Drv. Data(hal. Handle, &g. Drv. Spi 8301 Vars); HAL_read. Drv. Data(hal. Handle, &g. Drv. Spi 8301 Vars); #endif #ifdef DRV 8305_SPI HAL_write. Drv. Data(hal. Handle, &g. Drv. Spi 8305 Vars); HAL_read. Drv. Data(hal. Handle, &g. Drv. Spi 8305 Vars); #endif 9 } // end of while(g. Flag_enable. Sys) loop // disable the PWM HAL_disable. Pwm(hal. Handle); g. Motor. Vars. Flag_Run_Identify = false; } // end of for(; ; ) loop } // end of main() function //! brief The main ISR that implements the motor control. interrupt void main. ISR(void) { // Declaration of local variables _iq angle_pu = _IQ(0. 0); _iq speed_pu = _IQ(0. 0); _iq one. Over. Dc. Bus; MATH_vec 2 Iab_pu; MATH_vec 2 Vab_pu; MATH_vec 2 phasor; // acknowledge the ADC interrupt HAL_acq. Adc. Int(hal. Handle, ADC_Int. Number_1); // convert the ADC data HAL_read. Adc. Data. With. Offsets(hal. Handle, &g. Adc. Data); // remove offsets g. Adc. Data. I. value[0] = g. Adc. Data. I. value[0] - g. Offsets_I_pu. value[0]; g. Adc. Data. I. value[1] = g. Adc. Data. I. value[1] - g. Offsets_I_pu. value[1]; g. Adc. Data. I. value[2] = g. Adc. Data. I. value[2] - g. Offsets_I_pu. value[2]; g. Adc. Data. V. value[0] = g. Adc. Data. V. value[0] - g. Offsets_V_pu. value[0]; g. Adc. Data. V. value[1] = g. Adc. Data. V. value[1] - g. Offsets_V_pu. value[1]; g. Adc. Data. V. value[2] = g. Adc. Data. V. value[2] - g. Offsets_V_pu. value[2]; // run Clarke transform on current. Three values are passed, two values // are returned. CLARKE_run(clarke. Handle_I, &g. Adc. Data. I, &Iab_pu); // run Clarke transform on voltage. Three values are passed, two values // are returned. CLARKE_run(clarke. Handle_V, &g. Adc. Data. V, &Vab_pu); 10

proj_lab 11. c else // Flag_enable. Sys is set AND Flag_Run_Identify is not set

proj_lab 11. c else // Flag_enable. Sys is set AND Flag_Run_Identify is not set { // set estimator to Idle EST_set. Idle(est. Handle); // disable the PWM HAL_disable. Pwm(hal. Handle); // clear integrator outputs PID_set. Ui(pid. Handle[0], _IQ(0. 0)); PID_set. Ui(pid. Handle[1], _IQ(0. 0)); PID_set. Ui(pid. Handle[2], _IQ(0. 0)); // clear Id and Iq references g. Idq_ref_pu. value[0] = _IQ(0. 0); g. Idq_ref_pu. value[1] = _IQ(0. 0); } // update the global variables update. Global. Variables(est. Handle); // enable/disable the forced angle EST_set. Flag_enable. Force. Angle(est. Handle, g. Motor. Vars. Flag_enable. Force. Angle); // set target speed g. Motor. Vars. Speed. Ref_pu = _IQmpy(g. Motor. Vars. Speed. Ref_krpm, g. Speed_krpm_to_pu_sf); #ifdef DRV 8301_SPI HAL_write. Drv. Data(hal. Handle, &g. Drv. Spi 8301 Vars); HAL_read. Drv. Data(hal. Handle, &g. Drv. Spi 8301 Vars); #endif #ifdef DRV 8305_SPI HAL_write. Drv. Data(hal. Handle, &g. Drv. Spi 8305 Vars); HAL_read. Drv. Data(hal. Handle, &g. Drv. Spi 8305 Vars); #endif 9 } // end of while(g. Flag_enable. Sys) loop // disable the PWM HAL_disable. Pwm(hal. Handle); g. Motor. Vars. Flag_Run_Identify = false; } // end of for(; ; ) loop } // end of main() function //! brief The main ISR that implements the motor control. interrupt void main. ISR(void) { // Declaration of local variables _iq angle_pu = _IQ(0. 0); _iq speed_pu = _IQ(0. 0); _iq one. Over. Dc. Bus; MATH_vec 2 Iab_pu; MATH_vec 2 Vab_pu; MATH_vec 2 phasor; // acknowledge the ADC interrupt HAL_acq. Adc. Int(hal. Handle, ADC_Int. Number_1); // convert the ADC data HAL_read. Adc. Data. With. Offsets(hal. Handle, &g. Adc. Data); // remove offsets g. Adc. Data. I. value[0] = g. Adc. Data. I. value[0] - g. Offsets_I_pu. value[0]; g. Adc. Data. I. value[1] = g. Adc. Data. I. value[1] - g. Offsets_I_pu. value[1]; g. Adc. Data. I. value[2] = g. Adc. Data. I. value[2] - g. Offsets_I_pu. value[2]; g. Adc. Data. V. value[0] = g. Adc. Data. V. value[0] - g. Offsets_V_pu. value[0]; g. Adc. Data. V. value[1] = g. Adc. Data. V. value[1] - g. Offsets_V_pu. value[1]; g. Adc. Data. V. value[2] = g. Adc. Data. V. value[2] - g. Offsets_V_pu. value[2]; // run Clarke transform on current. Three values are passed, two values // are returned. CLARKE_run(clarke. Handle_I, &g. Adc. Data. I, &Iab_pu); // run Clarke transform on voltage. Three values are passed, two values // are returned. CLARKE_run(clarke. Handle_V, &g. Adc. Data. V, &Vab_pu); 10

proj_lab 11. c else // Flag_enable. Sys is set AND Flag_Run_Identify is not set

proj_lab 11. c else // Flag_enable. Sys is set AND Flag_Run_Identify is not set { // set estimator to Idle EST_set. Idle(est. Handle); // disable the PWM HAL_disable. Pwm(hal. Handle); // clear integrator outputs PID_set. Ui(pid. Handle[0], _IQ(0. 0)); PID_set. Ui(pid. Handle[1], _IQ(0. 0)); PID_set. Ui(pid. Handle[2], _IQ(0. 0)); // clear Id and Iq references g. Idq_ref_pu. value[0] = _IQ(0. 0); g. Idq_ref_pu. value[1] = _IQ(0. 0); } // update the global variables update. Global. Variables(est. Handle); // enable/disable the forced angle EST_set. Flag_enable. Force. Angle(est. Handle, g. Motor. Vars. Flag_enable. Force. Angle); // set target speed g. Motor. Vars. Speed. Ref_pu = _IQmpy(g. Motor. Vars. Speed. Ref_krpm, g. Speed_krpm_to_pu_sf); #ifdef DRV 8301_SPI HAL_write. Drv. Data(hal. Handle, &g. Drv. Spi 8301 Vars); HAL_read. Drv. Data(hal. Handle, &g. Drv. Spi 8301 Vars); #endif #ifdef DRV 8305_SPI HAL_write. Drv. Data(hal. Handle, &g. Drv. Spi 8305 Vars); HAL_read. Drv. Data(hal. Handle, &g. Drv. Spi 8305 Vars); #endif 9 } // end of while(g. Flag_enable. Sys) loop // disable the PWM HAL_disable. Pwm(hal. Handle); g. Motor. Vars. Flag_Run_Identify = false; } // end of for(; ; ) loop } // end of main() function //! brief The main ISR that implements the motor control. interrupt void main. ISR(void) { // Declaration of local variables _iq angle_pu = _IQ(0. 0); _iq speed_pu = _IQ(0. 0); _iq one. Over. Dc. Bus; MATH_vec 2 Iab_pu; MATH_vec 2 Vab_pu; MATH_vec 2 phasor; // acknowledge the ADC interrupt HAL_acq. Adc. Int(hal. Handle, ADC_Int. Number_1); // convert the ADC data HAL_read. Adc. Data. With. Offsets(hal. Handle, &g. Adc. Data); // remove offsets g. Adc. Data. I. value[0] = g. Adc. Data. I. value[0] - g. Offsets_I_pu. value[0]; g. Adc. Data. I. value[1] = g. Adc. Data. I. value[1] - g. Offsets_I_pu. value[1]; g. Adc. Data. I. value[2] = g. Adc. Data. I. value[2] - g. Offsets_I_pu. value[2]; g. Adc. Data. V. value[0] = g. Adc. Data. V. value[0] - g. Offsets_V_pu. value[0]; g. Adc. Data. V. value[1] = g. Adc. Data. V. value[1] - g. Offsets_V_pu. value[1]; g. Adc. Data. V. value[2] = g. Adc. Data. V. value[2] - g. Offsets_V_pu. value[2]; // run Clarke transform on current. Three values are passed, two values // are returned. CLARKE_run(clarke. Handle_I, &g. Adc. Data. I, &Iab_pu); // run Clarke transform on voltage. Three values are passed, two values // are returned. CLARKE_run(clarke. Handle_V, &g. Adc. Data. V, &Vab_pu); 10

proj_lab 11. c else // Flag_enable. Sys is set AND Flag_Run_Identify is not set

proj_lab 11. c else // Flag_enable. Sys is set AND Flag_Run_Identify is not set { // set estimator to Idle EST_set. Idle(est. Handle); // disable the PWM HAL_disable. Pwm(hal. Handle); // clear integrator outputs PID_set. Ui(pid. Handle[0], _IQ(0. 0)); PID_set. Ui(pid. Handle[1], _IQ(0. 0)); PID_set. Ui(pid. Handle[2], _IQ(0. 0)); // clear Id and Iq references g. Idq_ref_pu. value[0] = _IQ(0. 0); g. Idq_ref_pu. value[1] = _IQ(0. 0); } // update the global variables update. Global. Variables(est. Handle); // enable/disable the forced angle EST_set. Flag_enable. Force. Angle(est. Handle, g. Motor. Vars. Flag_enable. Force. Angle); // set target speed g. Motor. Vars. Speed. Ref_pu = _IQmpy(g. Motor. Vars. Speed. Ref_krpm, g. Speed_krpm_to_pu_sf); #ifdef DRV 8301_SPI HAL_write. Drv. Data(hal. Handle, &g. Drv. Spi 8301 Vars); HAL_read. Drv. Data(hal. Handle, &g. Drv. Spi 8301 Vars); #endif #ifdef DRV 8305_SPI HAL_write. Drv. Data(hal. Handle, &g. Drv. Spi 8305 Vars); HAL_read. Drv. Data(hal. Handle, &g. Drv. Spi 8305 Vars); #endif 9 } // end of while(g. Flag_enable. Sys) loop // disable the PWM HAL_disable. Pwm(hal. Handle); g. Motor. Vars. Flag_Run_Identify = false; } // end of for(; ; ) loop } // end of main() function //! brief The main ISR that implements the motor control. interrupt void main. ISR(void) { // Declaration of local variables _iq angle_pu = _IQ(0. 0); _iq speed_pu = _IQ(0. 0); _iq one. Over. Dc. Bus; MATH_vec 2 Iab_pu; MATH_vec 2 Vab_pu; MATH_vec 2 phasor; // acknowledge the ADC interrupt HAL_acq. Adc. Int(hal. Handle, ADC_Int. Number_1); // convert the ADC data HAL_read. Adc. Data. With. Offsets(hal. Handle, &g. Adc. Data); // remove offsets g. Adc. Data. I. value[0] = g. Adc. Data. I. value[0] - g. Offsets_I_pu. value[0]; g. Adc. Data. I. value[1] = g. Adc. Data. I. value[1] - g. Offsets_I_pu. value[1]; g. Adc. Data. I. value[2] = g. Adc. Data. I. value[2] - g. Offsets_I_pu. value[2]; g. Adc. Data. V. value[0] = g. Adc. Data. V. value[0] - g. Offsets_V_pu. value[0]; g. Adc. Data. V. value[1] = g. Adc. Data. V. value[1] - g. Offsets_V_pu. value[1]; g. Adc. Data. V. value[2] = g. Adc. Data. V. value[2] - g. Offsets_V_pu. value[2]; // run Clarke transform on current. Three values are passed, two values // are returned. CLARKE_run(clarke. Handle_I, &g. Adc. Data. I, &Iab_pu); // run Clarke transform on voltage. Three values are passed, two values // are returned. CLARKE_run(clarke. Handle_V, &g. Adc. Data. V, &Vab_pu); 10

proj_lab 11. c // run the estimator // The speed reference is needed so

proj_lab 11. c // run the estimator // The speed reference is needed so that the proper sign of the forced // angle is calculated. When the estimator does not do motor ID as in this // lab, only the sign of the speed reference is used EST_run(est. Handle, &Iab_pu, &Vab_pu, g. Adc. Data. dc. Bus, g. Motor. Vars. Speed. Ref_pu); // generate the motor electrical angle_pu = EST_get. Angle_pu(est. Handle); speed_pu = EST_get. Fm_pu(est. Handle); // get Idq from estimator to avoid sin and cos, and a Park transform, // which saves CPU cycles EST_get. Idq_pu(est. Handle, &g. Idq_pu); // run the appropriate controller if(g. Motor. Vars. Flag_Run_Identify) { // Declaration of local variables. _iq ref. Value; _iq fback. Value; _iq out. Max_pu; // when appropriate, run the PID speed controller // This mechanism provides the decimation for the speed loop. if(pid. Cnt. Speed >= USER_NUM_CTRL_TICKS_PER_SPEED_TICK) { // Reset the Speed PID execution counter. pid. Cnt. Speed = 0; // The next instruction executes the PI speed controller and places // its output in Idq_ref_pu. value[1], which is the input reference // value for the q-axis current controller. PID_run_spd(pid. Handle[0], g. Motor. Vars. Speed. Ref_pu, speed_pu, &(g. Idq_ref_pu. value[1])); } else { // increment counter pid. Cnt. Speed++; } 11 // Get the reference value for the d-axis current controller. ref. Value = g. Idq_ref_pu. value[0]; // Get the actual value of Id fback. Value = g. Idq_pu. value[0]; // The next instruction executes the PI current controller for the // d axis and places its output in Vdq_pu. value[0], which is the // control voltage along the d-axis (Vd) PID_run(pid. Handle[1], ref. Value, fback. Value, &(g. Vdq_out_pu. value[0])); // get the Iq reference value ref. Value = g. Idq_ref_pu. value[1]; // get the actual value of Iq fback. Value = g. Idq_pu. value[1]; // The voltage limits on the output of the q-axis current controller // are dynamic, and are dependent on the output voltage from the d-axis // current controller. In other words, the d-axis current controller // gets first dibs on the available voltage, and the q-axis current // controller gets what's left over. That is why the d-axis current // controller executes first. The next instruction calculates the // maximum limits for this voltage as: // Vq_min_max = +/- sqrt(Vbus^2 - Vd^2) out. Max_pu = _IQsqrt(_IQ(USER_MAX_VS_MAG_PU * USER_MAX_VS_MAG_PU) - _IQmpy(g. Vdq_out_pu. value[0], g. Vdq_out_pu. value[0])); // Set the limits to +/- out. Max_pu PID_set. Min. Max(pid. Handle[2], -out. Max_pu, out. Max_pu); // The next instruction executes the PI current controller for the // q axis and places its output in Vdq_pu. value[1], which is the // control voltage vector along the q-axis (Vq) PID_run(pid. Handle[2], ref. Value, fback. Value, &(g. Vdq_out_pu. value[1 ])); 12

proj_lab 11. c else // Flag_enable. Sys is set AND Flag_Run_Identify is not set

proj_lab 11. c else // Flag_enable. Sys is set AND Flag_Run_Identify is not set { // set estimator to Idle EST_set. Idle(est. Handle); // disable the PWM HAL_disable. Pwm(hal. Handle); // clear integrator outputs PID_set. Ui(pid. Handle[0], _IQ(0. 0)); PID_set. Ui(pid. Handle[1], _IQ(0. 0)); PID_set. Ui(pid. Handle[2], _IQ(0. 0)); // clear Id and Iq references g. Idq_ref_pu. value[0] = _IQ(0. 0); g. Idq_ref_pu. value[1] = _IQ(0. 0); } // update the global variables update. Global. Variables(est. Handle); // enable/disable the forced angle EST_set. Flag_enable. Force. Angle(est. Handle, g. Motor. Vars. Flag_enable. Force. Angle); // set target speed g. Motor. Vars. Speed. Ref_pu = _IQmpy(g. Motor. Vars. Speed. Ref_krpm, g. Speed_krpm_to_pu_sf); #ifdef DRV 8301_SPI HAL_write. Drv. Data(hal. Handle, &g. Drv. Spi 8301 Vars); HAL_read. Drv. Data(hal. Handle, &g. Drv. Spi 8301 Vars); #endif #ifdef DRV 8305_SPI HAL_write. Drv. Data(hal. Handle, &g. Drv. Spi 8305 Vars); HAL_read. Drv. Data(hal. Handle, &g. Drv. Spi 8305 Vars); #endif 9 } // end of while(g. Flag_enable. Sys) loop // disable the PWM HAL_disable. Pwm(hal. Handle); g. Motor. Vars. Flag_Run_Identify = false; } // end of for(; ; ) loop } // end of main() function //! brief The main ISR that implements the motor control. interrupt void main. ISR(void) { // Declaration of local variables _iq angle_pu = _IQ(0. 0); _iq speed_pu = _IQ(0. 0); _iq one. Over. Dc. Bus; MATH_vec 2 Iab_pu; MATH_vec 2 Vab_pu; MATH_vec 2 phasor; // acknowledge the ADC interrupt HAL_acq. Adc. Int(hal. Handle, ADC_Int. Number_1); // convert the ADC data HAL_read. Adc. Data. With. Offsets(hal. Handle, &g. Adc. Data); // remove offsets g. Adc. Data. I. value[0] = g. Adc. Data. I. value[0] - g. Offsets_I_pu. value[0]; g. Adc. Data. I. value[1] = g. Adc. Data. I. value[1] - g. Offsets_I_pu. value[1]; g. Adc. Data. I. value[2] = g. Adc. Data. I. value[2] - g. Offsets_I_pu. value[2]; g. Adc. Data. V. value[0] = g. Adc. Data. V. value[0] - g. Offsets_V_pu. value[0]; g. Adc. Data. V. value[1] = g. Adc. Data. V. value[1] - g. Offsets_V_pu. value[1]; g. Adc. Data. V. value[2] = g. Adc. Data. V. value[2] - g. Offsets_V_pu. value[2]; // run Clarke transform on current. Three values are passed, two values // are returned. CLARKE_run(clarke. Handle_I, &g. Adc. Data. I, &Iab_pu); // run Clarke transform on voltage. Three values are passed, two values // are returned. CLARKE_run(clarke. Handle_V, &g. Adc. Data. V, &Vab_pu); 10

proj_lab 11. c // run the estimator // The speed reference is needed so

proj_lab 11. c // run the estimator // The speed reference is needed so that the proper sign of the forced // angle is calculated. When the estimator does not do motor ID as in this // lab, only the sign of the speed reference is used EST_run(est. Handle, &Iab_pu, &Vab_pu, g. Adc. Data. dc. Bus, g. Motor. Vars. Speed. Ref_pu); // generate the motor electrical angle_pu = EST_get. Angle_pu(est. Handle); speed_pu = EST_get. Fm_pu(est. Handle); // get Idq from estimator to avoid sin and cos, and a Park transform, // which saves CPU cycles EST_get. Idq_pu(est. Handle, &g. Idq_pu); // run the appropriate controller if(g. Motor. Vars. Flag_Run_Identify) { // Declaration of local variables. _iq ref. Value; _iq fback. Value; _iq out. Max_pu; // when appropriate, run the PID speed controller // This mechanism provides the decimation for the speed loop. if(pid. Cnt. Speed >= USER_NUM_CTRL_TICKS_PER_SPEED_TICK) { // Reset the Speed PID execution counter. pid. Cnt. Speed = 0; // The next instruction executes the PI speed controller and places // its output in Idq_ref_pu. value[1], which is the input reference // value for the q-axis current controller. PID_run_spd(pid. Handle[0], g. Motor. Vars. Speed. Ref_pu, speed_pu, &(g. Idq_ref_pu. value[1])); } else { // increment counter pid. Cnt. Speed++; } 11 // Get the reference value for the d-axis current controller. ref. Value = g. Idq_ref_pu. value[0]; // Get the actual value of Id fback. Value = g. Idq_pu. value[0]; // The next instruction executes the PI current controller for the // d axis and places its output in Vdq_pu. value[0], which is the // control voltage along the d-axis (Vd) PID_run(pid. Handle[1], ref. Value, fback. Value, &(g. Vdq_out_pu. value[0])); // get the Iq reference value ref. Value = g. Idq_ref_pu. value[1]; // get the actual value of Iq fback. Value = g. Idq_pu. value[1]; // The voltage limits on the output of the q-axis current controller // are dynamic, and are dependent on the output voltage from the d-axis // current controller. In other words, the d-axis current controller // gets first dibs on the available voltage, and the q-axis current // controller gets what's left over. That is why the d-axis current // controller executes first. The next instruction calculates the // maximum limits for this voltage as: // Vq_min_max = +/- sqrt(Vbus^2 - Vd^2) out. Max_pu = _IQsqrt(_IQ(USER_MAX_VS_MAG_PU * USER_MAX_VS_MAG_PU) - _IQmpy(g. Vdq_out_pu. value[0], g. Vdq_out_pu. value[0])); // Set the limits to +/- out. Max_pu PID_set. Min. Max(pid. Handle[2], -out. Max_pu, out. Max_pu); // The next instruction executes the PI current controller for the // q axis and places its output in Vdq_pu. value[1], which is the // control voltage vector along the q-axis (Vq) PID_run(pid. Handle[2], ref. Value, fback. Value, &(g. Vdq_out_pu. value[1 ])); 12

proj_lab 11. c else // Flag_enable. Sys is set AND Flag_Run_Identify is not set

proj_lab 11. c else // Flag_enable. Sys is set AND Flag_Run_Identify is not set { // set estimator to Idle EST_set. Idle(est. Handle); // disable the PWM HAL_disable. Pwm(hal. Handle); // clear integrator outputs PID_set. Ui(pid. Handle[0], _IQ(0. 0)); PID_set. Ui(pid. Handle[1], _IQ(0. 0)); PID_set. Ui(pid. Handle[2], _IQ(0. 0)); // clear Id and Iq references g. Idq_ref_pu. value[0] = _IQ(0. 0); g. Idq_ref_pu. value[1] = _IQ(0. 0); } // update the global variables update. Global. Variables(est. Handle); // enable/disable the forced angle EST_set. Flag_enable. Force. Angle(est. Handle, g. Motor. Vars. Flag_enable. Force. Angle); // set target speed g. Motor. Vars. Speed. Ref_pu = _IQmpy(g. Motor. Vars. Speed. Ref_krpm, g. Speed_krpm_to_pu_sf); #ifdef DRV 8301_SPI HAL_write. Drv. Data(hal. Handle, &g. Drv. Spi 8301 Vars); HAL_read. Drv. Data(hal. Handle, &g. Drv. Spi 8301 Vars); #endif #ifdef DRV 8305_SPI HAL_write. Drv. Data(hal. Handle, &g. Drv. Spi 8305 Vars); HAL_read. Drv. Data(hal. Handle, &g. Drv. Spi 8305 Vars); #endif 9 } // end of while(g. Flag_enable. Sys) loop // disable the PWM HAL_disable. Pwm(hal. Handle); g. Motor. Vars. Flag_Run_Identify = false; } // end of for(; ; ) loop } // end of main() function //! brief The main ISR that implements the motor control. interrupt void main. ISR(void) { // Declaration of local variables _iq angle_pu = _IQ(0. 0); _iq speed_pu = _IQ(0. 0); _iq one. Over. Dc. Bus; MATH_vec 2 Iab_pu; MATH_vec 2 Vab_pu; MATH_vec 2 phasor; // acknowledge the ADC interrupt HAL_acq. Adc. Int(hal. Handle, ADC_Int. Number_1); // convert the ADC data HAL_read. Adc. Data. With. Offsets(hal. Handle, &g. Adc. Data); // remove offsets g. Adc. Data. I. value[0] = g. Adc. Data. I. value[0] - g. Offsets_I_pu. value[0]; g. Adc. Data. I. value[1] = g. Adc. Data. I. value[1] - g. Offsets_I_pu. value[1]; g. Adc. Data. I. value[2] = g. Adc. Data. I. value[2] - g. Offsets_I_pu. value[2]; g. Adc. Data. V. value[0] = g. Adc. Data. V. value[0] - g. Offsets_V_pu. value[0]; g. Adc. Data. V. value[1] = g. Adc. Data. V. value[1] - g. Offsets_V_pu. value[1]; g. Adc. Data. V. value[2] = g. Adc. Data. V. value[2] - g. Offsets_V_pu. value[2]; // run Clarke transform on current. Three values are passed, two values // are returned. CLARKE_run(clarke. Handle_I, &g. Adc. Data. I, &Iab_pu); // run Clarke transform on voltage. Three values are passed, two values // are returned. CLARKE_run(clarke. Handle_V, &g. Adc. Data. V, &Vab_pu); 10

proj_lab 11. c // run the estimator // The speed reference is needed so

proj_lab 11. c // run the estimator // The speed reference is needed so that the proper sign of the forced // angle is calculated. When the estimator does not do motor ID as in this // lab, only the sign of the speed reference is used EST_run(est. Handle, &Iab_pu, &Vab_pu, g. Adc. Data. dc. Bus, g. Motor. Vars. Speed. Ref_pu); // generate the motor electrical angle_pu = EST_get. Angle_pu(est. Handle); speed_pu = EST_get. Fm_pu(est. Handle); // get Idq from estimator to avoid sin and cos, and a Park transform, // which saves CPU cycles EST_get. Idq_pu(est. Handle, &g. Idq_pu); // run the appropriate controller if(g. Motor. Vars. Flag_Run_Identify) { // Declaration of local variables. _iq ref. Value; _iq fback. Value; _iq out. Max_pu; // when appropriate, run the PID speed controller // This mechanism provides the decimation for the speed loop. if(pid. Cnt. Speed >= USER_NUM_CTRL_TICKS_PER_SPEED_TICK) { // Reset the Speed PID execution counter. pid. Cnt. Speed = 0; // The next instruction executes the PI speed controller and places // its output in Idq_ref_pu. value[1], which is the input reference // value for the q-axis current controller. PID_run_spd(pid. Handle[0], g. Motor. Vars. Speed. Ref_pu, speed_pu, &(g. Idq_ref_pu. value[1])); } else { // increment counter pid. Cnt. Speed++; } 11 // Get the reference value for the d-axis current controller. ref. Value = g. Idq_ref_pu. value[0]; // Get the actual value of Id fback. Value = g. Idq_pu. value[0]; // The next instruction executes the PI current controller for the // d axis and places its output in Vdq_pu. value[0], which is the // control voltage along the d-axis (Vd) PID_run(pid. Handle[1], ref. Value, fback. Value, &(g. Vdq_out_pu. value[0])); // get the Iq reference value ref. Value = g. Idq_ref_pu. value[1]; // get the actual value of Iq fback. Value = g. Idq_pu. value[1]; // The voltage limits on the output of the q-axis current controller // are dynamic, and are dependent on the output voltage from the d-axis // current controller. In other words, the d-axis current controller // gets first dibs on the available voltage, and the q-axis current // controller gets what's left over. That is why the d-axis current // controller executes first. The next instruction calculates the // maximum limits for this voltage as: // Vq_min_max = +/- sqrt(Vbus^2 - Vd^2) out. Max_pu = _IQsqrt(_IQ(USER_MAX_VS_MAG_PU * USER_MAX_VS_MAG_PU) - _IQmpy(g. Vdq_out_pu. value[0], g. Vdq_out_pu. value[0])); // Set the limits to +/- out. Max_pu PID_set. Min. Max(pid. Handle[2], -out. Max_pu, out. Max_pu); // The next instruction executes the PI current controller for the // q axis and places its output in Vdq_pu. value[1], which is the // control voltage vector along the q-axis (Vq) PID_run(pid. Handle[2], ref. Value, fback. Value, &(g. Vdq_out_pu. value[1 ])); 12

proj_lab 11. c // run the estimator // The speed reference is needed so

proj_lab 11. c // run the estimator // The speed reference is needed so that the proper sign of the forced // angle is calculated. When the estimator does not do motor ID as in this // lab, only the sign of the speed reference is used EST_run(est. Handle, &Iab_pu, &Vab_pu, g. Adc. Data. dc. Bus, g. Motor. Vars. Speed. Ref_pu); // generate the motor electrical angle_pu = EST_get. Angle_pu(est. Handle); speed_pu = EST_get. Fm_pu(est. Handle); // get Idq from estimator to avoid sin and cos, and a Park transform, // which saves CPU cycles EST_get. Idq_pu(est. Handle, &g. Idq_pu); // run the appropriate controller if(g. Motor. Vars. Flag_Run_Identify) { // Declaration of local variables. _iq ref. Value; _iq fback. Value; _iq out. Max_pu; // when appropriate, run the PID speed controller // This mechanism provides the decimation for the speed loop. if(pid. Cnt. Speed >= USER_NUM_CTRL_TICKS_PER_SPEED_TICK) { // Reset the Speed PID execution counter. pid. Cnt. Speed = 0; // The next instruction executes the PI speed controller and places // its output in Idq_ref_pu. value[1], which is the input reference // value for the q-axis current controller. PID_run_spd(pid. Handle[0], g. Motor. Vars. Speed. Ref_pu, speed_pu, &(g. Idq_ref_pu. value[1])); } else { // increment counter pid. Cnt. Speed++; } 11 // Get the reference value for the d-axis current controller. ref. Value = g. Idq_ref_pu. value[0]; // Get the actual value of Id fback. Value = g. Idq_pu. value[0]; // The next instruction executes the PI current controller for the // d axis and places its output in Vdq_pu. value[0], which is the // control voltage along the d-axis (Vd) PID_run(pid. Handle[1], ref. Value, fback. Value, &(g. Vdq_out_pu. value[0])); // get the Iq reference value ref. Value = g. Idq_ref_pu. value[1]; // get the actual value of Iq fback. Value = g. Idq_pu. value[1]; // The voltage limits on the output of the q-axis current controller // are dynamic, and are dependent on the output voltage from the d-axis // current controller. In other words, the d-axis current controller // gets first dibs on the available voltage, and the q-axis current // controller gets what's left over. That is why the d-axis current // controller executes first. The next instruction calculates the // maximum limits for this voltage as: // Vq_min_max = +/- sqrt(Vbus^2 - Vd^2) out. Max_pu = _IQsqrt(_IQ(USER_MAX_VS_MAG_PU * USER_MAX_VS_MAG_PU) - _IQmpy(g. Vdq_out_pu. value[0], g. Vdq_out_pu. value[0])); // Set the limits to +/- out. Max_pu PID_set. Min. Max(pid. Handle[2], -out. Max_pu, out. Max_pu); // The next instruction executes the PI current controller for the // q axis and places its output in Vdq_pu. value[1], which is the // control voltage vector along the q-axis (Vq) PID_run(pid. Handle[2], ref. Value, fback. Value, &(g. Vdq_out_pu. value[1 ])); 12

proj_lab 11. c else // Flag_enable. Sys is set AND Flag_Run_Identify is not set

proj_lab 11. c else // Flag_enable. Sys is set AND Flag_Run_Identify is not set { // set estimator to Idle EST_set. Idle(est. Handle); // disable the PWM HAL_disable. Pwm(hal. Handle); // clear integrator outputs PID_set. Ui(pid. Handle[0], _IQ(0. 0)); PID_set. Ui(pid. Handle[1], _IQ(0. 0)); PID_set. Ui(pid. Handle[2], _IQ(0. 0)); // clear Id and Iq references g. Idq_ref_pu. value[0] = _IQ(0. 0); g. Idq_ref_pu. value[1] = _IQ(0. 0); } // update the global variables update. Global. Variables(est. Handle); // enable/disable the forced angle EST_set. Flag_enable. Force. Angle(est. Handle, g. Motor. Vars. Flag_enable. Force. Angle); // set target speed g. Motor. Vars. Speed. Ref_pu = _IQmpy(g. Motor. Vars. Speed. Ref_krpm, g. Speed_krpm_to_pu_sf); #ifdef DRV 8301_SPI HAL_write. Drv. Data(hal. Handle, &g. Drv. Spi 8301 Vars); HAL_read. Drv. Data(hal. Handle, &g. Drv. Spi 8301 Vars); #endif #ifdef DRV 8305_SPI HAL_write. Drv. Data(hal. Handle, &g. Drv. Spi 8305 Vars); HAL_read. Drv. Data(hal. Handle, &g. Drv. Spi 8305 Vars); #endif 9 } // end of while(g. Flag_enable. Sys) loop // disable the PWM HAL_disable. Pwm(hal. Handle); g. Motor. Vars. Flag_Run_Identify = false; } // end of for(; ; ) loop } // end of main() function //! brief The main ISR that implements the motor control. interrupt void main. ISR(void) { // Declaration of local variables _iq angle_pu = _IQ(0. 0); _iq speed_pu = _IQ(0. 0); _iq one. Over. Dc. Bus; MATH_vec 2 Iab_pu; MATH_vec 2 Vab_pu; MATH_vec 2 phasor; // acknowledge the ADC interrupt HAL_acq. Adc. Int(hal. Handle, ADC_Int. Number_1); // convert the ADC data HAL_read. Adc. Data. With. Offsets(hal. Handle, &g. Adc. Data); // remove offsets g. Adc. Data. I. value[0] = g. Adc. Data. I. value[0] - g. Offsets_I_pu. value[0]; g. Adc. Data. I. value[1] = g. Adc. Data. I. value[1] - g. Offsets_I_pu. value[1]; g. Adc. Data. I. value[2] = g. Adc. Data. I. value[2] - g. Offsets_I_pu. value[2]; g. Adc. Data. V. value[0] = g. Adc. Data. V. value[0] - g. Offsets_V_pu. value[0]; g. Adc. Data. V. value[1] = g. Adc. Data. V. value[1] - g. Offsets_V_pu. value[1]; g. Adc. Data. V. value[2] = g. Adc. Data. V. value[2] - g. Offsets_V_pu. value[2]; // run Clarke transform on current. Three values are passed, two values // are returned. CLARKE_run(clarke. Handle_I, &g. Adc. Data. I, &Iab_pu); // run Clarke transform on voltage. Three values are passed, two values // are returned. CLARKE_run(clarke. Handle_V, &g. Adc. Data. V, &Vab_pu); 10

proj_lab 11. c // run the estimator // The speed reference is needed so

proj_lab 11. c // run the estimator // The speed reference is needed so that the proper sign of the forced // angle is calculated. When the estimator does not do motor ID as in this // lab, only the sign of the speed reference is used EST_run(est. Handle, &Iab_pu, &Vab_pu, g. Adc. Data. dc. Bus, g. Motor. Vars. Speed. Ref_pu); // generate the motor electrical angle_pu = EST_get. Angle_pu(est. Handle); speed_pu = EST_get. Fm_pu(est. Handle); // get Idq from estimator to avoid sin and cos, and a Park transform, // which saves CPU cycles EST_get. Idq_pu(est. Handle, &g. Idq_pu); // run the appropriate controller if(g. Motor. Vars. Flag_Run_Identify) { // Declaration of local variables. _iq ref. Value; _iq fback. Value; _iq out. Max_pu; // when appropriate, run the PID speed controller // This mechanism provides the decimation for the speed loop. if(pid. Cnt. Speed >= USER_NUM_CTRL_TICKS_PER_SPEED_TICK) { // Reset the Speed PID execution counter. pid. Cnt. Speed = 0; // The next instruction executes the PI speed controller and places // its output in Idq_ref_pu. value[1], which is the input reference // value for the q-axis current controller. PID_run_spd(pid. Handle[0], g. Motor. Vars. Speed. Ref_pu, speed_pu, &(g. Idq_ref_pu. value[1])); } else { // increment counter pid. Cnt. Speed++; } 11 // Get the reference value for the d-axis current controller. ref. Value = g. Idq_ref_pu. value[0]; // Get the actual value of Id fback. Value = g. Idq_pu. value[0]; // The next instruction executes the PI current controller for the // d axis and places its output in Vdq_pu. value[0], which is the // control voltage along the d-axis (Vd) PID_run(pid. Handle[1], ref. Value, fback. Value, &(g. Vdq_out_pu. value[0])); // get the Iq reference value ref. Value = g. Idq_ref_pu. value[1]; // get the actual value of Iq fback. Value = g. Idq_pu. value[1]; // The voltage limits on the output of the q-axis current controller // are dynamic, and are dependent on the output voltage from the d-axis // current controller. In other words, the d-axis current controller // gets first dibs on the available voltage, and the q-axis current // controller gets what's left over. That is why the d-axis current // controller executes first. The next instruction calculates the // maximum limits for this voltage as: // Vq_min_max = +/- sqrt(Vbus^2 - Vd^2) out. Max_pu = _IQsqrt(_IQ(USER_MAX_VS_MAG_PU * USER_MAX_VS_MAG_PU) - _IQmpy(g. Vdq_out_pu. value[0], g. Vdq_out_pu. value[0])); // Set the limits to +/- out. Max_pu PID_set. Min. Max(pid. Handle[2], -out. Max_pu, out. Max_pu); // The next instruction executes the PI current controller for the // q axis and places its output in Vdq_pu. value[1], which is the // control voltage vector along the q-axis (Vq) PID_run(pid. Handle[2], ref. Value, fback. Value, &(g. Vdq_out_pu. value[1 ])); 12

proj_lab 11. c // ************************************* // the functions void main(void) { // IMPORTANT NOTE:

proj_lab 11. c // ************************************* // the functions void main(void) { // IMPORTANT NOTE: If you are not familiar with Motor. Ware coding guidelines // please refer to the following document: // C: /ti/motorware_1_01_00_1 x/docs/motorware_coding_standards. pdf // Only used if running from FLASH // Note that the variable FLASH is defined by the project #ifdef FLASH // Copy time critical code and Flash setup code to RAM // The Ramfuncs. Load. Start, Ramfuncs. Load. End, and Ramfuncs. Run. Start // symbols are created by the linker. Refer to the linker files. mem. Copy((uint 16_t *)&Ramfuncs. Load. Start, (uint 16_t *)&Ramfuncs. Load. End, (uint 16_t *)&Ramfuncs. Run. Start); #ifdef F 2802 x. F //copy. econst to unsecure RAM if(*econst_end - *econst_start) { mem. Copy((uint 16_t *)&econst_start, (uint 16_t *)&econst_end, (uint 16_t *)&econst_ram_load); } //copy. switch ot unsecure RAM if(*switch_end - *switch_start) { mem. Copy((uint 16_t *)&switch_start, (uint 16_t *)&switch_end, (uint 16_t *)&switch_ram_load); } #endif 1 // initialize the Hardware Abstraction Layer (HAL) // hal. Handle will be used throughout the code to interface with the HAL // (set parameters, get and set functions, etc) hal. Handle is required since // this is how all objects are interfaced, and it allows interface with // multiple objects by simply passing a different handle. The use of // handles is explained in this document: // C: /ti/motorware_1_01_00_1 x/docs/motorware_coding_standards. pdf hal. Handle = HAL_init(&hal, sizeof(hal)); // check for errors in user parameters USER_check. For. Errors(&g. User. Params); // store user parameter error in global variable g. Motor. Vars. User. Error. Code = USER_get. Error. Code(&g. User. Params); // do not allow code execution if there is a user parameter error. If there // is an error, the code will be stuck in this forever loop if(g. Motor. Vars. User. Error. Code != USER_Error. Code_No. Error) { for(; ; ) { g. Motor. Vars. Flag_enable. Sys = false; } } // initialize the Clarke modules // Clarke handle initialization for current signals clarke. Handle_I = CLARKE_init(&clarke_I, sizeof(clarke_I)); // Clarke handle initialization for voltage signals clarke. Handle_V = CLARKE_init(&clarke_V, sizeof(clarke_V)); // initialize the estimator est. Handle = EST_init((void *)USER_EST_HANDLE_ADDRESS, 0 x 200); // initialize the user parameters // This function initializes all values of structure g. User. Params with // values defined in user. h. The values in g. User. Params will be then used by // the hardware abstraction layer (HAL) to configure peripherals such as // PWM, ADC, interrupts, etc. USER_set. Params(&g. User. Params); 2

proj_lab 11. c else // Flag_enable. Sys is set AND Flag_Run_Identify is not set

proj_lab 11. c else // Flag_enable. Sys is set AND Flag_Run_Identify is not set { // set estimator to Idle EST_set. Idle(est. Handle); // disable the PWM HAL_disable. Pwm(hal. Handle); // clear integrator outputs PID_set. Ui(pid. Handle[0], _IQ(0. 0)); PID_set. Ui(pid. Handle[1], _IQ(0. 0)); PID_set. Ui(pid. Handle[2], _IQ(0. 0)); // clear Id and Iq references g. Idq_ref_pu. value[0] = _IQ(0. 0); g. Idq_ref_pu. value[1] = _IQ(0. 0); } // update the global variables update. Global. Variables(est. Handle); // enable/disable the forced angle EST_set. Flag_enable. Force. Angle(est. Handle, g. Motor. Vars. Flag_enable. Force. Angle); // set target speed g. Motor. Vars. Speed. Ref_pu = _IQmpy(g. Motor. Vars. Speed. Ref_krpm, g. Speed_krpm_to_pu_sf); #ifdef DRV 8301_SPI HAL_write. Drv. Data(hal. Handle, &g. Drv. Spi 8301 Vars); HAL_read. Drv. Data(hal. Handle, &g. Drv. Spi 8301 Vars); #endif #ifdef DRV 8305_SPI HAL_write. Drv. Data(hal. Handle, &g. Drv. Spi 8305 Vars); HAL_read. Drv. Data(hal. Handle, &g. Drv. Spi 8305 Vars); #endif 9 } // end of while(g. Flag_enable. Sys) loop // disable the PWM HAL_disable. Pwm(hal. Handle); g. Motor. Vars. Flag_Run_Identify = false; } // end of for(; ; ) loop } // end of main() function //! brief The main ISR that implements the motor control. interrupt void main. ISR(void) { // Declaration of local variables _iq angle_pu = _IQ(0. 0); _iq speed_pu = _IQ(0. 0); _iq one. Over. Dc. Bus; MATH_vec 2 Iab_pu; MATH_vec 2 Vab_pu; MATH_vec 2 phasor; // acknowledge the ADC interrupt HAL_acq. Adc. Int(hal. Handle, ADC_Int. Number_1); // convert the ADC data HAL_read. Adc. Data. With. Offsets(hal. Handle, &g. Adc. Data); // remove offsets g. Adc. Data. I. value[0] = g. Adc. Data. I. value[0] - g. Offsets_I_pu. value[0]; g. Adc. Data. I. value[1] = g. Adc. Data. I. value[1] - g. Offsets_I_pu. value[1]; g. Adc. Data. I. value[2] = g. Adc. Data. I. value[2] - g. Offsets_I_pu. value[2]; g. Adc. Data. V. value[0] = g. Adc. Data. V. value[0] - g. Offsets_V_pu. value[0]; g. Adc. Data. V. value[1] = g. Adc. Data. V. value[1] - g. Offsets_V_pu. value[1]; g. Adc. Data. V. value[2] = g. Adc. Data. V. value[2] - g. Offsets_V_pu. value[2]; // run Clarke transform on current. Three values are passed, two values // are returned. CLARKE_run(clarke. Handle_I, &g. Adc. Data. I, &Iab_pu); // run Clarke transform on voltage. Three values are passed, two values // are returned. CLARKE_run(clarke. Handle_V, &g. Adc. Data. V, &Vab_pu); 10

proj_lab 11. c // ************************************* // the functions void main(void) { // IMPORTANT NOTE:

proj_lab 11. c // ************************************* // the functions void main(void) { // IMPORTANT NOTE: If you are not familiar with Motor. Ware coding guidelines // please refer to the following document: // C: /ti/motorware_1_01_00_1 x/docs/motorware_coding_standards. pdf // Only used if running from FLASH // Note that the variable FLASH is defined by the project #ifdef FLASH // Copy time critical code and Flash setup code to RAM // The Ramfuncs. Load. Start, Ramfuncs. Load. End, and Ramfuncs. Run. Start // symbols are created by the linker. Refer to the linker files. mem. Copy((uint 16_t *)&Ramfuncs. Load. Start, (uint 16_t *)&Ramfuncs. Load. End, (uint 16_t *)&Ramfuncs. Run. Start); #ifdef F 2802 x. F //copy. econst to unsecure RAM if(*econst_end - *econst_start) { mem. Copy((uint 16_t *)&econst_start, (uint 16_t *)&econst_end, (uint 16_t *)&econst_ram_load); } //copy. switch ot unsecure RAM if(*switch_end - *switch_start) { mem. Copy((uint 16_t *)&switch_start, (uint 16_t *)&switch_end, (uint 16_t *)&switch_ram_load); } #endif 1 // initialize the Hardware Abstraction Layer (HAL) // hal. Handle will be used throughout the code to interface with the HAL // (set parameters, get and set functions, etc) hal. Handle is required since // this is how all objects are interfaced, and it allows interface with // multiple objects by simply passing a different handle. The use of // handles is explained in this document: // C: /ti/motorware_1_01_00_1 x/docs/motorware_coding_standards. pdf hal. Handle = HAL_init(&hal, sizeof(hal)); // check for errors in user parameters USER_check. For. Errors(&g. User. Params); // store user parameter error in global variable g. Motor. Vars. User. Error. Code = USER_get. Error. Code(&g. User. Params); // do not allow code execution if there is a user parameter error. If there // is an error, the code will be stuck in this forever loop if(g. Motor. Vars. User. Error. Code != USER_Error. Code_No. Error) { for(; ; ) { g. Motor. Vars. Flag_enable. Sys = false; } } // initialize the Clarke modules // Clarke handle initialization for current signals clarke. Handle_I = CLARKE_init(&clarke_I, sizeof(clarke_I)); // Clarke handle initialization for voltage signals clarke. Handle_V = CLARKE_init(&clarke_V, sizeof(clarke_V)); // initialize the estimator est. Handle = EST_init((void *)USER_EST_HANDLE_ADDRESS, 0 x 200); // initialize the user parameters // This function initializes all values of structure g. User. Params with // values defined in user. h. The values in g. User. Params will be then used by // the hardware abstraction layer (HAL) to configure peripherals such as // PWM, ADC, interrupts, etc. USER_set. Params(&g. User. Params); 2

proj_lab 11. c else // Flag_enable. Sys is set AND Flag_Run_Identify is not set

proj_lab 11. c else // Flag_enable. Sys is set AND Flag_Run_Identify is not set { // set estimator to Idle EST_set. Idle(est. Handle); // disable the PWM HAL_disable. Pwm(hal. Handle); // clear integrator outputs PID_set. Ui(pid. Handle[0], _IQ(0. 0)); PID_set. Ui(pid. Handle[1], _IQ(0. 0)); PID_set. Ui(pid. Handle[2], _IQ(0. 0)); // clear Id and Iq references g. Idq_ref_pu. value[0] = _IQ(0. 0); g. Idq_ref_pu. value[1] = _IQ(0. 0); } // update the global variables update. Global. Variables(est. Handle); // enable/disable the forced angle EST_set. Flag_enable. Force. Angle(est. Handle, g. Motor. Vars. Flag_enable. Force. Angle); // set target speed g. Motor. Vars. Speed. Ref_pu = _IQmpy(g. Motor. Vars. Speed. Ref_krpm, g. Speed_krpm_to_pu_sf); #ifdef DRV 8301_SPI HAL_write. Drv. Data(hal. Handle, &g. Drv. Spi 8301 Vars); HAL_read. Drv. Data(hal. Handle, &g. Drv. Spi 8301 Vars); #endif #ifdef DRV 8305_SPI HAL_write. Drv. Data(hal. Handle, &g. Drv. Spi 8305 Vars); HAL_read. Drv. Data(hal. Handle, &g. Drv. Spi 8305 Vars); #endif 9 } // end of while(g. Flag_enable. Sys) loop // disable the PWM HAL_disable. Pwm(hal. Handle); g. Motor. Vars. Flag_Run_Identify = false; } // end of for(; ; ) loop } // end of main() function //! brief The main ISR that implements the motor control. interrupt void main. ISR(void) { // Declaration of local variables _iq angle_pu = _IQ(0. 0); _iq speed_pu = _IQ(0. 0); _iq one. Over. Dc. Bus; MATH_vec 2 Iab_pu; MATH_vec 2 Vab_pu; MATH_vec 2 phasor; // acknowledge the ADC interrupt HAL_acq. Adc. Int(hal. Handle, ADC_Int. Number_1); // convert the ADC data HAL_read. Adc. Data. With. Offsets(hal. Handle, &g. Adc. Data); // remove offsets g. Adc. Data. I. value[0] = g. Adc. Data. I. value[0] - g. Offsets_I_pu. value[0]; g. Adc. Data. I. value[1] = g. Adc. Data. I. value[1] - g. Offsets_I_pu. value[1]; g. Adc. Data. I. value[2] = g. Adc. Data. I. value[2] - g. Offsets_I_pu. value[2]; g. Adc. Data. V. value[0] = g. Adc. Data. V. value[0] - g. Offsets_V_pu. value[0]; g. Adc. Data. V. value[1] = g. Adc. Data. V. value[1] - g. Offsets_V_pu. value[1]; g. Adc. Data. V. value[2] = g. Adc. Data. V. value[2] - g. Offsets_V_pu. value[2]; // run Clarke transform on current. Three values are passed, two values // are returned. CLARKE_run(clarke. Handle_I, &g. Adc. Data. I, &Iab_pu); // run Clarke transform on voltage. Three values are passed, two values // are returned. CLARKE_run(clarke. Handle_V, &g. Adc. Data. V, &Vab_pu); 10

proj_lab 11. c // ************************************* // the functions void main(void) { // IMPORTANT NOTE:

proj_lab 11. c // ************************************* // the functions void main(void) { // IMPORTANT NOTE: If you are not familiar with Motor. Ware coding guidelines // please refer to the following document: // C: /ti/motorware_1_01_00_1 x/docs/motorware_coding_standards. pdf // Only used if running from FLASH // Note that the variable FLASH is defined by the project #ifdef FLASH // Copy time critical code and Flash setup code to RAM // The Ramfuncs. Load. Start, Ramfuncs. Load. End, and Ramfuncs. Run. Start // symbols are created by the linker. Refer to the linker files. mem. Copy((uint 16_t *)&Ramfuncs. Load. Start, (uint 16_t *)&Ramfuncs. Load. End, (uint 16_t *)&Ramfuncs. Run. Start); #ifdef F 2802 x. F //copy. econst to unsecure RAM if(*econst_end - *econst_start) { mem. Copy((uint 16_t *)&econst_start, (uint 16_t *)&econst_end, (uint 16_t *)&econst_ram_load); } //copy. switch ot unsecure RAM if(*switch_end - *switch_start) { mem. Copy((uint 16_t *)&switch_start, (uint 16_t *)&switch_end, (uint 16_t *)&switch_ram_load); } #endif 1 // initialize the Hardware Abstraction Layer (HAL) // hal. Handle will be used throughout the code to interface with the HAL // (set parameters, get and set functions, etc) hal. Handle is required since // this is how all objects are interfaced, and it allows interface with // multiple objects by simply passing a different handle. The use of // handles is explained in this document: // C: /ti/motorware_1_01_00_1 x/docs/motorware_coding_standards. pdf hal. Handle = HAL_init(&hal, sizeof(hal)); // check for errors in user parameters USER_check. For. Errors(&g. User. Params); // store user parameter error in global variable g. Motor. Vars. User. Error. Code = USER_get. Error. Code(&g. User. Params); // do not allow code execution if there is a user parameter error. If there // is an error, the code will be stuck in this forever loop if(g. Motor. Vars. User. Error. Code != USER_Error. Code_No. Error) { for(; ; ) { g. Motor. Vars. Flag_enable. Sys = false; } } // initialize the Clarke modules // Clarke handle initialization for current signals clarke. Handle_I = CLARKE_init(&clarke_I, sizeof(clarke_I)); // Clarke handle initialization for voltage signals clarke. Handle_V = CLARKE_init(&clarke_V, sizeof(clarke_V)); // initialize the estimator est. Handle = EST_init((void *)USER_EST_HANDLE_ADDRESS, 0 x 200); // initialize the user parameters // This function initializes all values of structure g. User. Params with // values defined in user. h. The values in g. User. Params will be then used by // the hardware abstraction layer (HAL) to configure peripherals such as // PWM, ADC, interrupts, etc. USER_set. Params(&g. User. Params); 2

proj_lab 11. c // run the estimator // The speed reference is needed so

proj_lab 11. c // run the estimator // The speed reference is needed so that the proper sign of the forced // angle is calculated. When the estimator does not do motor ID as in this // lab, only the sign of the speed reference is used EST_run(est. Handle, &Iab_pu, &Vab_pu, g. Adc. Data. dc. Bus, g. Motor. Vars. Speed. Ref_pu); // generate the motor electrical angle_pu = EST_get. Angle_pu(est. Handle); speed_pu = EST_get. Fm_pu(est. Handle); // get Idq from estimator to avoid sin and cos, and a Park transform, // which saves CPU cycles EST_get. Idq_pu(est. Handle, &g. Idq_pu); // run the appropriate controller if(g. Motor. Vars. Flag_Run_Identify) { // Declaration of local variables. _iq ref. Value; _iq fback. Value; _iq out. Max_pu; // when appropriate, run the PID speed controller // This mechanism provides the decimation for the speed loop. if(pid. Cnt. Speed >= USER_NUM_CTRL_TICKS_PER_SPEED_TICK) { // Reset the Speed PID execution counter. pid. Cnt. Speed = 0; // The next instruction executes the PI speed controller and places // its output in Idq_ref_pu. value[1], which is the input reference // value for the q-axis current controller. PID_run_spd(pid. Handle[0], g. Motor. Vars. Speed. Ref_pu, speed_pu, &(g. Idq_ref_pu. value[1])); } else { // increment counter pid. Cnt. Speed++; } 11 // Get the reference value for the d-axis current controller. ref. Value = g. Idq_ref_pu. value[0]; // Get the actual value of Id fback. Value = g. Idq_pu. value[0]; // The next instruction executes the PI current controller for the // d axis and places its output in Vdq_pu. value[0], which is the // control voltage along the d-axis (Vd) PID_run(pid. Handle[1], ref. Value, fback. Value, &(g. Vdq_out_pu. value[0])); // get the Iq reference value ref. Value = g. Idq_ref_pu. value[1]; // get the actual value of Iq fback. Value = g. Idq_pu. value[1]; // The voltage limits on the output of the q-axis current controller // are dynamic, and are dependent on the output voltage from the d-axis // current controller. In other words, the d-axis current controller // gets first dibs on the available voltage, and the q-axis current // controller gets what's left over. That is why the d-axis current // controller executes first. The next instruction calculates the // maximum limits for this voltage as: // Vq_min_max = +/- sqrt(Vbus^2 - Vd^2) out. Max_pu = _IQsqrt(_IQ(USER_MAX_VS_MAG_PU * USER_MAX_VS_MAG_PU) - _IQmpy(g. Vdq_out_pu. value[0], g. Vdq_out_pu. value[0])); // Set the limits to +/- out. Max_pu PID_set. Min. Max(pid. Handle[2], -out. Max_pu, out. Max_pu); // The next instruction executes the PI current controller for the // q axis and places its output in Vdq_pu. value[1], which is the // control voltage vector along the q-axis (Vq) PID_run(pid. Handle[2], ref. Value, fback. Value, &(g. Vdq_out_pu. value[1 ])); 12

proj_lab 11. c // run the estimator // The speed reference is needed so

proj_lab 11. c // run the estimator // The speed reference is needed so that the proper sign of the forced // angle is calculated. When the estimator does not do motor ID as in this // lab, only the sign of the speed reference is used EST_run(est. Handle, &Iab_pu, &Vab_pu, g. Adc. Data. dc. Bus, g. Motor. Vars. Speed. Ref_pu); // generate the motor electrical angle_pu = EST_get. Angle_pu(est. Handle); speed_pu = EST_get. Fm_pu(est. Handle); // get Idq from estimator to avoid sin and cos, and a Park transform, // which saves CPU cycles EST_get. Idq_pu(est. Handle, &g. Idq_pu); // run the appropriate controller if(g. Motor. Vars. Flag_Run_Identify) { // Declaration of local variables. _iq ref. Value; _iq fback. Value; _iq out. Max_pu; // when appropriate, run the PID speed controller // This mechanism provides the decimation for the speed loop. if(pid. Cnt. Speed >= USER_NUM_CTRL_TICKS_PER_SPEED_TICK) { // Reset the Speed PID execution counter. pid. Cnt. Speed = 0; // The next instruction executes the PI speed controller and places // its output in Idq_ref_pu. value[1], which is the input reference // value for the q-axis current controller. PID_run_spd(pid. Handle[0], g. Motor. Vars. Speed. Ref_pu, speed_pu, &(g. Idq_ref_pu. value[1])); } else { // increment counter pid. Cnt. Speed++; } 11 // Get the reference value for the d-axis current controller. ref. Value = g. Idq_ref_pu. value[0]; // Get the actual value of Id fback. Value = g. Idq_pu. value[0]; // The next instruction executes the PI current controller for the // d axis and places its output in Vdq_pu. value[0], which is the // control voltage along the d-axis (Vd) PID_run(pid. Handle[1], ref. Value, fback. Value, &(g. Vdq_out_pu. value[0])); // get the Iq reference value ref. Value = g. Idq_ref_pu. value[1]; // get the actual value of Iq fback. Value = g. Idq_pu. value[1]; // The voltage limits on the output of the q-axis current controller // are dynamic, and are dependent on the output voltage from the d-axis // current controller. In other words, the d-axis current controller // gets first dibs on the available voltage, and the q-axis current // controller gets what's left over. That is why the d-axis current // controller executes first. The next instruction calculates the // maximum limits for this voltage as: // Vq_min_max = +/- sqrt(Vbus^2 - Vd^2) out. Max_pu = _IQsqrt(_IQ(USER_MAX_VS_MAG_PU * USER_MAX_VS_MAG_PU) - _IQmpy(g. Vdq_out_pu. value[0], g. Vdq_out_pu. value[0])); // Set the limits to +/- out. Max_pu PID_set. Min. Max(pid. Handle[2], -out. Max_pu, out. Max_pu); // The next instruction executes the PI current controller for the // q axis and places its output in Vdq_pu. value[1], which is the // control voltage vector along the q-axis (Vq) PID_run(pid. Handle[2], ref. Value, fback. Value, &(g. Vdq_out_pu. value[1 ])); 12

proj_lab 11. c // The voltage vector is now calculated and ready to be

proj_lab 11. c // The voltage vector is now calculated and ready to be applied to the // motor in the form of three PWM signals. However, even though the // voltages may be supplied to the PWM module now, they won't be // applied to the motor until the next PWM cycle. By this point, the // motor will have moved away from the angle that the voltage vector // was calculated for, by an amount which is proportional to the // sampling frequency and the speed of the motor. For steady-state // speeds, we can calculate this angle delay and compensate for it. angle_pu = angle. Delay. Comp(speed_pu, angle_pu); // compute the sin/cos phasor since the phase is used by the inverse // Park transform to transform from DQ to Alpha/Beta. The phasor // calculation is not part of the IPARK module. The way it works is, // the phasor is calculated by doing a sine and cosine, then the phasor // is copied into the IPARK module, and then finally the IPARK module // uses the phasor to calculate from DQ to Alpha/Beta phasor. value[0] = _IQcos. PU(angle_pu); phasor. value[1] = _IQsin. PU(angle_pu); // set the phasor in the inverse Park transform IPARK_set. Phasor(ipark. Handle, &phasor); // Run the inverse Park module. This converts the voltage vector from // synchronous frame values to stationary frame values. IPARK_run(ipark. Handle, &g. Vdq_out_pu, &Vab_pu); // This code compensates for variations in the DC bus by adjusting the // PWM duty cycle to compensate for the variations. the goal is to // produce the desired volt-second product regardless of the DC bus // value. To do this, we must divide the desired voltage values by // the DC bus value. Or. . . it is easier to multiply by 1/(DC bus value) one. Over. Dc. Bus = EST_get. One. Over. Dc. Bus_pu(est. Handle); Vab_pu. value[0] = _IQmpy(Vab_pu. value[0], one. Over. Dc. Bus); Vab_pu. value[1] = _IQmpy(Vab_pu. value[1], one. Over. Dc. Bus); // Now run the space vector generator (SVGEN) module. // There is no need to do an inverse CLARKE transform, as this is // handled in the SVGEN_run function. SVGEN_run(svgen. Handle, &Vab_pu, &(g. Pwm. Data. Tabc)); } 13 else // g. Motor. Vars. Flag_Run_Identify = 0 { // disable the PWM HAL_disable. Pwm(hal. Handle); // Set the PWMs to 50% duty cycle g. Pwm. Data. Tabc. value[0] = _IQ(0. 0); g. Pwm. Data. Tabc. value[1] = _IQ(0. 0); g. Pwm. Data. Tabc. value[2] = _IQ(0. 0); } // write to the PWM compare registers, and then we are done! HAL_write. Pwm. Data(hal. Handle, &g. Pwm. Data); return; } // end of main. ISR() function 14

proj_lab 11. c // run the estimator // The speed reference is needed so

proj_lab 11. c // run the estimator // The speed reference is needed so that the proper sign of the forced // angle is calculated. When the estimator does not do motor ID as in this // lab, only the sign of the speed reference is used EST_run(est. Handle, &Iab_pu, &Vab_pu, g. Adc. Data. dc. Bus, g. Motor. Vars. Speed. Ref_pu); // generate the motor electrical angle_pu = EST_get. Angle_pu(est. Handle); speed_pu = EST_get. Fm_pu(est. Handle); // get Idq from estimator to avoid sin and cos, and a Park transform, // which saves CPU cycles EST_get. Idq_pu(est. Handle, &g. Idq_pu); // run the appropriate controller if(g. Motor. Vars. Flag_Run_Identify) { // Declaration of local variables. _iq ref. Value; _iq fback. Value; _iq out. Max_pu; // when appropriate, run the PID speed controller // This mechanism provides the decimation for the speed loop. if(pid. Cnt. Speed >= USER_NUM_CTRL_TICKS_PER_SPEED_TICK) { // Reset the Speed PID execution counter. pid. Cnt. Speed = 0; // The next instruction executes the PI speed controller and places // its output in Idq_ref_pu. value[1], which is the input reference // value for the q-axis current controller. PID_run_spd(pid. Handle[0], g. Motor. Vars. Speed. Ref_pu, speed_pu, &(g. Idq_ref_pu. value[1])); } else { // increment counter pid. Cnt. Speed++; } 11 // Get the reference value for the d-axis current controller. ref. Value = g. Idq_ref_pu. value[0]; // Get the actual value of Id fback. Value = g. Idq_pu. value[0]; // The next instruction executes the PI current controller for the // d axis and places its output in Vdq_pu. value[0], which is the // control voltage along the d-axis (Vd) PID_run(pid. Handle[1], ref. Value, fback. Value, &(g. Vdq_out_pu. value[0])); // get the Iq reference value ref. Value = g. Idq_ref_pu. value[1]; // get the actual value of Iq fback. Value = g. Idq_pu. value[1]; // The voltage limits on the output of the q-axis current controller // are dynamic, and are dependent on the output voltage from the d-axis // current controller. In other words, the d-axis current controller // gets first dibs on the available voltage, and the q-axis current // controller gets what's left over. That is why the d-axis current // controller executes first. The next instruction calculates the // maximum limits for this voltage as: // Vq_min_max = +/- sqrt(Vbus^2 - Vd^2) out. Max_pu = _IQsqrt(_IQ(USER_MAX_VS_MAG_PU * USER_MAX_VS_MAG_PU) - _IQmpy(g. Vdq_out_pu. value[0], g. Vdq_out_pu. value[0])); // Set the limits to +/- out. Max_pu PID_set. Min. Max(pid. Handle[2], -out. Max_pu, out. Max_pu); // The next instruction executes the PI current controller for the // q axis and places its output in Vdq_pu. value[1], which is the // control voltage vector along the q-axis (Vq) PID_run(pid. Handle[2], ref. Value, fback. Value, &(g. Vdq_out_pu. value[1 ])); 12

proj_lab 11. c // The voltage vector is now calculated and ready to be

proj_lab 11. c // The voltage vector is now calculated and ready to be applied to the // motor in the form of three PWM signals. However, even though the // voltages may be supplied to the PWM module now, they won't be // applied to the motor until the next PWM cycle. By this point, the // motor will have moved away from the angle that the voltage vector // was calculated for, by an amount which is proportional to the // sampling frequency and the speed of the motor. For steady-state // speeds, we can calculate this angle delay and compensate for it. angle_pu = angle. Delay. Comp(speed_pu, angle_pu); // compute the sin/cos phasor since the phase is used by the inverse // Park transform to transform from DQ to Alpha/Beta. The phasor // calculation is not part of the IPARK module. The way it works is, // the phasor is calculated by doing a sine and cosine, then the phasor // is copied into the IPARK module, and then finally the IPARK module // uses the phasor to calculate from DQ to Alpha/Beta phasor. value[0] = _IQcos. PU(angle_pu); phasor. value[1] = _IQsin. PU(angle_pu); // set the phasor in the inverse Park transform IPARK_set. Phasor(ipark. Handle, &phasor); // Run the inverse Park module. This converts the voltage vector from // synchronous frame values to stationary frame values. IPARK_run(ipark. Handle, &g. Vdq_out_pu, &Vab_pu); // This code compensates for variations in the DC bus by adjusting the // PWM duty cycle to compensate for the variations. the goal is to // produce the desired volt-second product regardless of the DC bus // value. To do this, we must divide the desired voltage values by // the DC bus value. Or. . . it is easier to multiply by 1/(DC bus value) one. Over. Dc. Bus = EST_get. One. Over. Dc. Bus_pu(est. Handle); Vab_pu. value[0] = _IQmpy(Vab_pu. value[0], one. Over. Dc. Bus); Vab_pu. value[1] = _IQmpy(Vab_pu. value[1], one. Over. Dc. Bus); // Now run the space vector generator (SVGEN) module. // There is no need to do an inverse CLARKE transform, as this is // handled in the SVGEN_run function. SVGEN_run(svgen. Handle, &Vab_pu, &(g. Pwm. Data. Tabc)); } 13 else // g. Motor. Vars. Flag_Run_Identify = 0 { // disable the PWM HAL_disable. Pwm(hal. Handle); // Set the PWMs to 50% duty cycle g. Pwm. Data. Tabc. value[0] = _IQ(0. 0); g. Pwm. Data. Tabc. value[1] = _IQ(0. 0); g. Pwm. Data. Tabc. value[2] = _IQ(0. 0); } // write to the PWM compare registers, and then we are done! HAL_write. Pwm. Data(hal. Handle, &g. Pwm. Data); return; } // end of main. ISR() function 14

proj_lab 11. c // run the estimator // The speed reference is needed so

proj_lab 11. c // run the estimator // The speed reference is needed so that the proper sign of the forced // angle is calculated. When the estimator does not do motor ID as in this // lab, only the sign of the speed reference is used EST_run(est. Handle, &Iab_pu, &Vab_pu, g. Adc. Data. dc. Bus, g. Motor. Vars. Speed. Ref_pu); // generate the motor electrical angle_pu = EST_get. Angle_pu(est. Handle); speed_pu = EST_get. Fm_pu(est. Handle); // get Idq from estimator to avoid sin and cos, and a Park transform, // which saves CPU cycles EST_get. Idq_pu(est. Handle, &g. Idq_pu); // run the appropriate controller if(g. Motor. Vars. Flag_Run_Identify) { // Declaration of local variables. _iq ref. Value; _iq fback. Value; _iq out. Max_pu; // when appropriate, run the PID speed controller // This mechanism provides the decimation for the speed loop. if(pid. Cnt. Speed >= USER_NUM_CTRL_TICKS_PER_SPEED_TICK) { // Reset the Speed PID execution counter. pid. Cnt. Speed = 0; // The next instruction executes the PI speed controller and places // its output in Idq_ref_pu. value[1], which is the input reference // value for the q-axis current controller. PID_run_spd(pid. Handle[0], g. Motor. Vars. Speed. Ref_pu, speed_pu, &(g. Idq_ref_pu. value[1])); } else { // increment counter pid. Cnt. Speed++; } 11 // Get the reference value for the d-axis current controller. ref. Value = g. Idq_ref_pu. value[0]; // Get the actual value of Id fback. Value = g. Idq_pu. value[0]; // The next instruction executes the PI current controller for the // d axis and places its output in Vdq_pu. value[0], which is the // control voltage along the d-axis (Vd) PID_run(pid. Handle[1], ref. Value, fback. Value, &(g. Vdq_out_pu. value[0])); // get the Iq reference value ref. Value = g. Idq_ref_pu. value[1]; // get the actual value of Iq fback. Value = g. Idq_pu. value[1]; // The voltage limits on the output of the q-axis current controller // are dynamic, and are dependent on the output voltage from the d-axis // current controller. In other words, the d-axis current controller // gets first dibs on the available voltage, and the q-axis current // controller gets what's left over. That is why the d-axis current // controller executes first. The next instruction calculates the // maximum limits for this voltage as: // Vq_min_max = +/- sqrt(Vbus^2 - Vd^2) out. Max_pu = _IQsqrt(_IQ(USER_MAX_VS_MAG_PU * USER_MAX_VS_MAG_PU) - _IQmpy(g. Vdq_out_pu. value[0], g. Vdq_out_pu. value[0])); // Set the limits to +/- out. Max_pu PID_set. Min. Max(pid. Handle[2], -out. Max_pu, out. Max_pu); // The next instruction executes the PI current controller for the // q axis and places its output in Vdq_pu. value[1], which is the // control voltage vector along the q-axis (Vq) PID_run(pid. Handle[2], ref. Value, fback. Value, &(g. Vdq_out_pu. value[1 ])); 12

proj_lab 11. c // run the estimator // The speed reference is needed so

proj_lab 11. c // run the estimator // The speed reference is needed so that the proper sign of the forced // angle is calculated. When the estimator does not do motor ID as in this // lab, only the sign of the speed reference is used EST_run(est. Handle, &Iab_pu, &Vab_pu, g. Adc. Data. dc. Bus, g. Motor. Vars. Speed. Ref_pu); // generate the motor electrical angle_pu = EST_get. Angle_pu(est. Handle); speed_pu = EST_get. Fm_pu(est. Handle); // get Idq from estimator to avoid sin and cos, and a Park transform, // which saves CPU cycles EST_get. Idq_pu(est. Handle, &g. Idq_pu); // run the appropriate controller if(g. Motor. Vars. Flag_Run_Identify) { // Declaration of local variables. _iq ref. Value; _iq fback. Value; _iq out. Max_pu; // when appropriate, run the PID speed controller // This mechanism provides the decimation for the speed loop. if(pid. Cnt. Speed >= USER_NUM_CTRL_TICKS_PER_SPEED_TICK) { // Reset the Speed PID execution counter. pid. Cnt. Speed = 0; // The next instruction executes the PI speed controller and places // its output in Idq_ref_pu. value[1], which is the input reference // value for the q-axis current controller. PID_run_spd(pid. Handle[0], g. Motor. Vars. Speed. Ref_pu, speed_pu, &(g. Idq_ref_pu. value[1])); } else { // increment counter pid. Cnt. Speed++; } 11 // Get the reference value for the d-axis current controller. ref. Value = g. Idq_ref_pu. value[0]; // Get the actual value of Id fback. Value = g. Idq_pu. value[0]; // The next instruction executes the PI current controller for the // d axis and places its output in Vdq_pu. value[0], which is the // control voltage along the d-axis (Vd) PID_run(pid. Handle[1], ref. Value, fback. Value, &(g. Vdq_out_pu. value[0])); // get the Iq reference value ref. Value = g. Idq_ref_pu. value[1]; // get the actual value of Iq fback. Value = g. Idq_pu. value[1]; // The voltage limits on the output of the q-axis current controller // are dynamic, and are dependent on the output voltage from the d-axis // current controller. In other words, the d-axis current controller // gets first dibs on the available voltage, and the q-axis current // controller gets what's left over. That is why the d-axis current // controller executes first. The next instruction calculates the // maximum limits for this voltage as: // Vq_min_max = +/- sqrt(Vbus^2 - Vd^2) out. Max_pu = _IQsqrt(_IQ(USER_MAX_VS_MAG_PU * USER_MAX_VS_MAG_PU) - _IQmpy(g. Vdq_out_pu. value[0], g. Vdq_out_pu. value[0])); // Set the limits to +/- out. Max_pu PID_set. Min. Max(pid. Handle[2], -out. Max_pu, out. Max_pu); // The next instruction executes the PI current controller for the // q axis and places its output in Vdq_pu. value[1], which is the // control voltage vector along the q-axis (Vq) PID_run(pid. Handle[2], ref. Value, fback. Value, &(g. Vdq_out_pu. value[1 ])); 12

proj_lab 11. c else // Flag_enable. Sys is set AND Flag_Run_Identify is not set

proj_lab 11. c else // Flag_enable. Sys is set AND Flag_Run_Identify is not set { // set estimator to Idle EST_set. Idle(est. Handle); // disable the PWM HAL_disable. Pwm(hal. Handle); // clear integrator outputs PID_set. Ui(pid. Handle[0], _IQ(0. 0)); PID_set. Ui(pid. Handle[1], _IQ(0. 0)); PID_set. Ui(pid. Handle[2], _IQ(0. 0)); // clear Id and Iq references g. Idq_ref_pu. value[0] = _IQ(0. 0); g. Idq_ref_pu. value[1] = _IQ(0. 0); } // update the global variables update. Global. Variables(est. Handle); // enable/disable the forced angle EST_set. Flag_enable. Force. Angle(est. Handle, g. Motor. Vars. Flag_enable. Force. Angle); // set target speed g. Motor. Vars. Speed. Ref_pu = _IQmpy(g. Motor. Vars. Speed. Ref_krpm, g. Speed_krpm_to_pu_sf); #ifdef DRV 8301_SPI HAL_write. Drv. Data(hal. Handle, &g. Drv. Spi 8301 Vars); HAL_read. Drv. Data(hal. Handle, &g. Drv. Spi 8301 Vars); #endif #ifdef DRV 8305_SPI HAL_write. Drv. Data(hal. Handle, &g. Drv. Spi 8305 Vars); HAL_read. Drv. Data(hal. Handle, &g. Drv. Spi 8305 Vars); #endif 9 } // end of while(g. Flag_enable. Sys) loop // disable the PWM HAL_disable. Pwm(hal. Handle); g. Motor. Vars. Flag_Run_Identify = false; } // end of for(; ; ) loop } // end of main() function //! brief The main ISR that implements the motor control. interrupt void main. ISR(void) { // Declaration of local variables _iq angle_pu = _IQ(0. 0); _iq speed_pu = _IQ(0. 0); _iq one. Over. Dc. Bus; MATH_vec 2 Iab_pu; MATH_vec 2 Vab_pu; MATH_vec 2 phasor; // acknowledge the ADC interrupt HAL_acq. Adc. Int(hal. Handle, ADC_Int. Number_1); // convert the ADC data HAL_read. Adc. Data. With. Offsets(hal. Handle, &g. Adc. Data); // remove offsets g. Adc. Data. I. value[0] = g. Adc. Data. I. value[0] - g. Offsets_I_pu. value[0]; g. Adc. Data. I. value[1] = g. Adc. Data. I. value[1] - g. Offsets_I_pu. value[1]; g. Adc. Data. I. value[2] = g. Adc. Data. I. value[2] - g. Offsets_I_pu. value[2]; g. Adc. Data. V. value[0] = g. Adc. Data. V. value[0] - g. Offsets_V_pu. value[0]; g. Adc. Data. V. value[1] = g. Adc. Data. V. value[1] - g. Offsets_V_pu. value[1]; g. Adc. Data. V. value[2] = g. Adc. Data. V. value[2] - g. Offsets_V_pu. value[2]; // run Clarke transform on current. Three values are passed, two values // are returned. CLARKE_run(clarke. Handle_I, &g. Adc. Data. I, &Iab_pu); // run Clarke transform on voltage. Three values are passed, two values // are returned. CLARKE_run(clarke. Handle_V, &g. Adc. Data. V, &Vab_pu); 10

proj_lab 11. c // run the estimator // The speed reference is needed so

proj_lab 11. c // run the estimator // The speed reference is needed so that the proper sign of the forced // angle is calculated. When the estimator does not do motor ID as in this // lab, only the sign of the speed reference is used EST_run(est. Handle, &Iab_pu, &Vab_pu, g. Adc. Data. dc. Bus, g. Motor. Vars. Speed. Ref_pu); // generate the motor electrical angle_pu = EST_get. Angle_pu(est. Handle); speed_pu = EST_get. Fm_pu(est. Handle); // get Idq from estimator to avoid sin and cos, and a Park transform, // which saves CPU cycles EST_get. Idq_pu(est. Handle, &g. Idq_pu); // run the appropriate controller if(g. Motor. Vars. Flag_Run_Identify) { // Declaration of local variables. _iq ref. Value; _iq fback. Value; _iq out. Max_pu; // when appropriate, run the PID speed controller // This mechanism provides the decimation for the speed loop. if(pid. Cnt. Speed >= USER_NUM_CTRL_TICKS_PER_SPEED_TICK) { // Reset the Speed PID execution counter. pid. Cnt. Speed = 0; // The next instruction executes the PI speed controller and places // its output in Idq_ref_pu. value[1], which is the input reference // value for the q-axis current controller. PID_run_spd(pid. Handle[0], g. Motor. Vars. Speed. Ref_pu, speed_pu, &(g. Idq_ref_pu. value[1])); } else { // increment counter pid. Cnt. Speed++; } 11 // Get the reference value for the d-axis current controller. ref. Value = g. Idq_ref_pu. value[0]; // Get the actual value of Id fback. Value = g. Idq_pu. value[0]; // The next instruction executes the PI current controller for the // d axis and places its output in Vdq_pu. value[0], which is the // control voltage along the d-axis (Vd) PID_run(pid. Handle[1], ref. Value, fback. Value, &(g. Vdq_out_pu. value[0])); // get the Iq reference value ref. Value = g. Idq_ref_pu. value[1]; // get the actual value of Iq fback. Value = g. Idq_pu. value[1]; // The voltage limits on the output of the q-axis current controller // are dynamic, and are dependent on the output voltage from the d-axis // current controller. In other words, the d-axis current controller // gets first dibs on the available voltage, and the q-axis current // controller gets what's left over. That is why the d-axis current // controller executes first. The next instruction calculates the // maximum limits for this voltage as: // Vq_min_max = +/- sqrt(Vbus^2 - Vd^2) out. Max_pu = _IQsqrt(_IQ(USER_MAX_VS_MAG_PU * USER_MAX_VS_MAG_PU) - _IQmpy(g. Vdq_out_pu. value[0], g. Vdq_out_pu. value[0])); // Set the limits to +/- out. Max_pu PID_set. Min. Max(pid. Handle[2], -out. Max_pu, out. Max_pu); // The next instruction executes the PI current controller for the // q axis and places its output in Vdq_pu. value[1], which is the // control voltage vector along the q-axis (Vq) PID_run(pid. Handle[2], ref. Value, fback. Value, &(g. Vdq_out_pu. value[1 ])); 12

proj_lab 11. c // run the estimator // The speed reference is needed so

proj_lab 11. c // run the estimator // The speed reference is needed so that the proper sign of the forced // angle is calculated. When the estimator does not do motor ID as in this // lab, only the sign of the speed reference is used EST_run(est. Handle, &Iab_pu, &Vab_pu, g. Adc. Data. dc. Bus, g. Motor. Vars. Speed. Ref_pu); // generate the motor electrical angle_pu = EST_get. Angle_pu(est. Handle); speed_pu = EST_get. Fm_pu(est. Handle); // get Idq from estimator to avoid sin and cos, and a Park transform, // which saves CPU cycles EST_get. Idq_pu(est. Handle, &g. Idq_pu); // run the appropriate controller if(g. Motor. Vars. Flag_Run_Identify) { // Declaration of local variables. _iq ref. Value; _iq fback. Value; _iq out. Max_pu; // when appropriate, run the PID speed controller // This mechanism provides the decimation for the speed loop. if(pid. Cnt. Speed >= USER_NUM_CTRL_TICKS_PER_SPEED_TICK) { // Reset the Speed PID execution counter. pid. Cnt. Speed = 0; // The next instruction executes the PI speed controller and places // its output in Idq_ref_pu. value[1], which is the input reference // value for the q-axis current controller. PID_run_spd(pid. Handle[0], g. Motor. Vars. Speed. Ref_pu, speed_pu, &(g. Idq_ref_pu. value[1])); } else { // increment counter pid. Cnt. Speed++; } 11 // Get the reference value for the d-axis current controller. ref. Value = g. Idq_ref_pu. value[0]; // Get the actual value of Id fback. Value = g. Idq_pu. value[0]; // The next instruction executes the PI current controller for the // d axis and places its output in Vdq_pu. value[0], which is the // control voltage along the d-axis (Vd) PID_run(pid. Handle[1], ref. Value, fback. Value, &(g. Vdq_out_pu. value[0])); // get the Iq reference value ref. Value = g. Idq_ref_pu. value[1]; // get the actual value of Iq fback. Value = g. Idq_pu. value[1]; // The voltage limits on the output of the q-axis current controller // are dynamic, and are dependent on the output voltage from the d-axis // current controller. In other words, the d-axis current controller // gets first dibs on the available voltage, and the q-axis current // controller gets what's left over. That is why the d-axis current // controller executes first. The next instruction calculates the // maximum limits for this voltage as: // Vq_min_max = +/- sqrt(Vbus^2 - Vd^2) out. Max_pu = _IQsqrt(_IQ(USER_MAX_VS_MAG_PU * USER_MAX_VS_MAG_PU) - _IQmpy(g. Vdq_out_pu. value[0], g. Vdq_out_pu. value[0])); // Set the limits to +/- out. Max_pu PID_set. Min. Max(pid. Handle[2], -out. Max_pu, out. Max_pu); // The next instruction executes the PI current controller for the // q axis and places its output in Vdq_pu. value[1], which is the // control voltage vector along the q-axis (Vq) PID_run(pid. Handle[2], ref. Value, fback. Value, &(g. Vdq_out_pu. value[1 ])); 12

proj_lab 11. c // For the current controller, Kp = Ls*bandwidth(rad/sec) But in order

proj_lab 11. c // For the current controller, Kp = Ls*bandwidth(rad/sec) But in order // to be used, it must be converted to per unit values by multiplying // by full. Scale. Current and then dividing by full. Scale. Voltage. From the // statement below, we see that the bandwidth in rad/sec is equal to // 0. 25/Isr. Period_sec, which is equal to USER_ISR_FREQ_HZ/4. This means // that by setting Kp as described below, the bandwidth in Hz is // USER_ISR_FREQ_HZ/(8*pi). _iq Kp_Id = _IQ((0. 25 * Ls_d * full. Scale. Current) / (Isr. Period_sec * full. Scale. Voltage)); // In order to achieve pole/zero cancellation (which reduces the // closed-loop transfer function from a second-order system to a // first-order system), Ki must equal Rs/Ls. Since the output of the // Ki gain stage is integrated by a DIGITAL integrator, the integrator // input must be scaled by 1/Isr. Period_sec. That's just the way // digital integrators work. But, since Isr. Period_sec is a constant, // we can save an additional multiplication operation by lumping this // term with the Ki value. _iq Ki_Id = _IQ(Rover. Ls_d * Isr. Period_sec); // Now do the same thing for Kp for the q-axis current controller. // If the motor is not an IPM motor, Ld and Lq are the same, which // means that Kp_Iq = Kp_Id _iq Kp_Iq = _IQ((0. 25 * Ls_q * full. Scale. Current) / (Isr. Period_sec * full. Scale. Voltage)); // Do the same thing for Ki for the q-axis current controller. If the // motor is not an IPM motor, Ld and Lq are the same, which means that // Ki_Iq = Ki_Id. _iq Ki_Iq = _IQ(Rover. Ls_q * Isr. Period_sec); // There are three PI controllers; one speed controller and two current // controllers. Each PI controller has two coefficients; Kp and Ki. // So you have a total of six coefficients that must be defined. // This is for the speed controller pid. Handle[0] = PID_init(&pid[0], sizeof(pid[0])); // This is for the Id current controller pid. Handle[1] = PID_init(&pid[1], sizeof(pid[1])); // This is for the Iq current controller pid. Handle[2] = PID_init(&pid[2], sizeof(pid[2])); 5 // The following instructions load the parameters for the speed PI // controller. PID_set. Gains(pid. Handle[0], _IQ(1. 0), _IQ(0. 01), _IQ(0. 0)); // The current limit is performed by the limits placed on the speed PI // controller output. In the following statement, the speed // controller's largest negative current is set to -max. Current_pu, and // the largest positive current is set to max. Current_pu. PID_set. Min. Max(pid. Handle[0], -max. Current_pu, max. Current_pu); PID_set. Ui(pid. Handle[0], _IQ(0. 0)); // Set the initial condition value // for the integrator output to 0 pid. Cnt. Speed = 0; // Set the counter for decimating the speed // controller to 0 // The following instructions load the parameters for the d-axis // current controller. // P term = Kp_Id, I term = Ki_Id, D term = 0 PID_set. Gains(pid. Handle[1], Kp_Id, Ki_Id, _IQ(0. 0)); // Largest negative voltage = -max. Voltage_pu, largest positive // voltage = max. Voltage_pu PID_set. Min. Max(pid. Handle[1], -max. Voltage_pu, max. Voltage_pu); // Set the initial condition value for the integrator output to 0 PID_set. Ui(pid. Handle[1], _IQ(0. 0)); // The following instructions load the parameters for the q-axis // current controller. // P term = Kp_Iq, I term = Ki_Iq, D term = 0 PID_set. Gains(pid. Handle[2], Kp_Iq, Ki_Iq, _IQ(0. 0)); // The largest negative voltage = 0 and the largest positive // voltage = 0. But these limits are updated every single ISR before // actually executing the Iq controller. The limits depend on how much // voltage is left over after the Id controller executes. So having an // initial value of 0 does not affect Iq current controller execution. PID_set. Min. Max(pid. Handle[2], _IQ(0. 0)); // Set the initial condition value for the integrator output to 0 PID_set. Ui(pid. Handle[2], _IQ(0. 0)); } 6

proj_lab 11. c // For the current controller, Kp = Ls*bandwidth(rad/sec) But in order

proj_lab 11. c // For the current controller, Kp = Ls*bandwidth(rad/sec) But in order // to be used, it must be converted to per unit values by multiplying // by full. Scale. Current and then dividing by full. Scale. Voltage. From the // statement below, we see that the bandwidth in rad/sec is equal to // 0. 25/Isr. Period_sec, which is equal to USER_ISR_FREQ_HZ/4. This means // that by setting Kp as described below, the bandwidth in Hz is // USER_ISR_FREQ_HZ/(8*pi). _iq Kp_Id = _IQ((0. 25 * Ls_d * full. Scale. Current) / (Isr. Period_sec * full. Scale. Voltage)); // In order to achieve pole/zero cancellation (which reduces the // closed-loop transfer function from a second-order system to a // first-order system), Ki must equal Rs/Ls. Since the output of the // Ki gain stage is integrated by a DIGITAL integrator, the integrator // input must be scaled by 1/Isr. Period_sec. That's just the way // digital integrators work. But, since Isr. Period_sec is a constant, // we can save an additional multiplication operation by lumping this // term with the Ki value. _iq Ki_Id = _IQ(Rover. Ls_d * Isr. Period_sec); // Now do the same thing for Kp for the q-axis current controller. // If the motor is not an IPM motor, Ld and Lq are the same, which // means that Kp_Iq = Kp_Id _iq Kp_Iq = _IQ((0. 25 * Ls_q * full. Scale. Current) / (Isr. Period_sec * full. Scale. Voltage)); // Do the same thing for Ki for the q-axis current controller. If the // motor is not an IPM motor, Ld and Lq are the same, which means that // Ki_Iq = Ki_Id. _iq Ki_Iq = _IQ(Rover. Ls_q * Isr. Period_sec); // There are three PI controllers; one speed controller and two current // controllers. Each PI controller has two coefficients; Kp and Ki. // So you have a total of six coefficients that must be defined. // This is for the speed controller pid. Handle[0] = PID_init(&pid[0], sizeof(pid[0])); // This is for the Id current controller pid. Handle[1] = PID_init(&pid[1], sizeof(pid[1])); // This is for the Iq current controller pid. Handle[2] = PID_init(&pid[2], sizeof(pid[2])); 5 // The following instructions load the parameters for the speed PI // controller. PID_set. Gains(pid. Handle[0], _IQ(1. 0), _IQ(0. 01), _IQ(0. 0)); // The current limit is performed by the limits placed on the speed PI // controller output. In the following statement, the speed // controller's largest negative current is set to -max. Current_pu, and // the largest positive current is set to max. Current_pu. PID_set. Min. Max(pid. Handle[0], -max. Current_pu, max. Current_pu); PID_set. Ui(pid. Handle[0], _IQ(0. 0)); // Set the initial condition value // for the integrator output to 0 pid. Cnt. Speed = 0; // Set the counter for decimating the speed // controller to 0 // The following instructions load the parameters for the d-axis // current controller. // P term = Kp_Id, I term = Ki_Id, D term = 0 PID_set. Gains(pid. Handle[1], Kp_Id, Ki_Id, _IQ(0. 0)); // Largest negative voltage = -max. Voltage_pu, largest positive // voltage = max. Voltage_pu PID_set. Min. Max(pid. Handle[1], -max. Voltage_pu, max. Voltage_pu); // Set the initial condition value for the integrator output to 0 PID_set. Ui(pid. Handle[1], _IQ(0. 0)); // The following instructions load the parameters for the q-axis // current controller. // P term = Kp_Iq, I term = Ki_Iq, D term = 0 PID_set. Gains(pid. Handle[2], Kp_Iq, Ki_Iq, _IQ(0. 0)); // The largest negative voltage = 0 and the largest positive // voltage = 0. But these limits are updated every single ISR before // actually executing the Iq controller. The limits depend on how much // voltage is left over after the Id controller executes. So having an // initial value of 0 does not affect Iq current controller execution. PID_set. Min. Max(pid. Handle[2], _IQ(0. 0)); // Set the initial condition value for the integrator output to 0 PID_set. Ui(pid. Handle[2], _IQ(0. 0)); } 6

proj_lab 11. c // For the current controller, Kp = Ls*bandwidth(rad/sec) But in order

proj_lab 11. c // For the current controller, Kp = Ls*bandwidth(rad/sec) But in order // to be used, it must be converted to per unit values by multiplying // by full. Scale. Current and then dividing by full. Scale. Voltage. From the // statement below, we see that the bandwidth in rad/sec is equal to // 0. 25/Isr. Period_sec, which is equal to USER_ISR_FREQ_HZ/4. This means // that by setting Kp as described below, the bandwidth in Hz is // USER_ISR_FREQ_HZ/(8*pi). _iq Kp_Id = _IQ((0. 25 * Ls_d * full. Scale. Current) / (Isr. Period_sec * full. Scale. Voltage)); // In order to achieve pole/zero cancellation (which reduces the // closed-loop transfer function from a second-order system to a // first-order system), Ki must equal Rs/Ls. Since the output of the // Ki gain stage is integrated by a DIGITAL integrator, the integrator // input must be scaled by 1/Isr. Period_sec. That's just the way // digital integrators work. But, since Isr. Period_sec is a constant, // we can save an additional multiplication operation by lumping this // term with the Ki value. _iq Ki_Id = _IQ(Rover. Ls_d * Isr. Period_sec); // Now do the same thing for Kp for the q-axis current controller. // If the motor is not an IPM motor, Ld and Lq are the same, which // means that Kp_Iq = Kp_Id _iq Kp_Iq = _IQ((0. 25 * Ls_q * full. Scale. Current) / (Isr. Period_sec * full. Scale. Voltage)); // Do the same thing for Ki for the q-axis current controller. If the // motor is not an IPM motor, Ld and Lq are the same, which means that // Ki_Iq = Ki_Id. _iq Ki_Iq = _IQ(Rover. Ls_q * Isr. Period_sec); // There are three PI controllers; one speed controller and two current // controllers. Each PI controller has two coefficients; Kp and Ki. // So you have a total of six coefficients that must be defined. // This is for the speed controller pid. Handle[0] = PID_init(&pid[0], sizeof(pid[0])); // This is for the Id current controller pid. Handle[1] = PID_init(&pid[1], sizeof(pid[1])); // This is for the Iq current controller pid. Handle[2] = PID_init(&pid[2], sizeof(pid[2])); 5 // The following instructions load the parameters for the speed PI // controller. PID_set. Gains(pid. Handle[0], _IQ(1. 0), _IQ(0. 01), _IQ(0. 0)); // The current limit is performed by the limits placed on the speed PI // controller output. In the following statement, the speed // controller's largest negative current is set to -max. Current_pu, and // the largest positive current is set to max. Current_pu. PID_set. Min. Max(pid. Handle[0], -max. Current_pu, max. Current_pu); PID_set. Ui(pid. Handle[0], _IQ(0. 0)); // Set the initial condition value // for the integrator output to 0 pid. Cnt. Speed = 0; // Set the counter for decimating the speed // controller to 0 // The following instructions load the parameters for the d-axis // current controller. // P term = Kp_Id, I term = Ki_Id, D term = 0 PID_set. Gains(pid. Handle[1], Kp_Id, Ki_Id, _IQ(0. 0)); // Largest negative voltage = -max. Voltage_pu, largest positive // voltage = max. Voltage_pu PID_set. Min. Max(pid. Handle[1], -max. Voltage_pu, max. Voltage_pu); // Set the initial condition value for the integrator output to 0 PID_set. Ui(pid. Handle[1], _IQ(0. 0)); // The following instructions load the parameters for the q-axis // current controller. // P term = Kp_Iq, I term = Ki_Iq, D term = 0 PID_set. Gains(pid. Handle[2], Kp_Iq, Ki_Iq, _IQ(0. 0)); // The largest negative voltage = 0 and the largest positive // voltage = 0. But these limits are updated every single ISR before // actually executing the Iq controller. The limits depend on how much // voltage is left over after the Id controller executes. So having an // initial value of 0 does not affect Iq current controller execution. PID_set. Min. Max(pid. Handle[2], _IQ(0. 0)); // Set the initial condition value for the integrator output to 0 PID_set. Ui(pid. Handle[2], _IQ(0. 0)); } 6

proj_lab 11. c // For the current controller, Kp = Ls*bandwidth(rad/sec) But in order

proj_lab 11. c // For the current controller, Kp = Ls*bandwidth(rad/sec) But in order // to be used, it must be converted to per unit values by multiplying // by full. Scale. Current and then dividing by full. Scale. Voltage. From the // statement below, we see that the bandwidth in rad/sec is equal to // 0. 25/Isr. Period_sec, which is equal to USER_ISR_FREQ_HZ/4. This means // that by setting Kp as described below, the bandwidth in Hz is // USER_ISR_FREQ_HZ/(8*pi). _iq Kp_Id = _IQ((0. 25 * Ls_d * full. Scale. Current) / (Isr. Period_sec * full. Scale. Voltage)); // In order to achieve pole/zero cancellation (which reduces the // closed-loop transfer function from a second-order system to a // first-order system), Ki must equal Rs/Ls. Since the output of the // Ki gain stage is integrated by a DIGITAL integrator, the integrator // input must be scaled by 1/Isr. Period_sec. That's just the way // digital integrators work. But, since Isr. Period_sec is a constant, // we can save an additional multiplication operation by lumping this // term with the Ki value. _iq Ki_Id = _IQ(Rover. Ls_d * Isr. Period_sec); // Now do the same thing for Kp for the q-axis current controller. // If the motor is not an IPM motor, Ld and Lq are the same, which // means that Kp_Iq = Kp_Id _iq Kp_Iq = _IQ((0. 25 * Ls_q * full. Scale. Current) / (Isr. Period_sec * full. Scale. Voltage)); // Do the same thing for Ki for the q-axis current controller. If the // motor is not an IPM motor, Ld and Lq are the same, which means that // Ki_Iq = Ki_Id. _iq Ki_Iq = _IQ(Rover. Ls_q * Isr. Period_sec); // There are three PI controllers; one speed controller and two current // controllers. Each PI controller has two coefficients; Kp and Ki. // So you have a total of six coefficients that must be defined. // This is for the speed controller pid. Handle[0] = PID_init(&pid[0], sizeof(pid[0])); // This is for the Id current controller pid. Handle[1] = PID_init(&pid[1], sizeof(pid[1])); // This is for the Iq current controller pid. Handle[2] = PID_init(&pid[2], sizeof(pid[2])); 5 // The following instructions load the parameters for the speed PI // controller. PID_set. Gains(pid. Handle[0], _IQ(1. 0), _IQ(0. 01), _IQ(0. 0)); // The current limit is performed by the limits placed on the speed PI // controller output. In the following statement, the speed // controller's largest negative current is set to -max. Current_pu, and // the largest positive current is set to max. Current_pu. PID_set. Min. Max(pid. Handle[0], -max. Current_pu, max. Current_pu); PID_set. Ui(pid. Handle[0], _IQ(0. 0)); // Set the initial condition value // for the integrator output to 0 pid. Cnt. Speed = 0; // Set the counter for decimating the speed // controller to 0 // The following instructions load the parameters for the d-axis // current controller. // P term = Kp_Id, I term = Ki_Id, D term = 0 PID_set. Gains(pid. Handle[1], Kp_Id, Ki_Id, _IQ(0. 0)); // Largest negative voltage = -max. Voltage_pu, largest positive // voltage = max. Voltage_pu PID_set. Min. Max(pid. Handle[1], -max. Voltage_pu, max. Voltage_pu); // Set the initial condition value for the integrator output to 0 PID_set. Ui(pid. Handle[1], _IQ(0. 0)); // The following instructions load the parameters for the q-axis // current controller. // P term = Kp_Iq, I term = Ki_Iq, D term = 0 PID_set. Gains(pid. Handle[2], Kp_Iq, Ki_Iq, _IQ(0. 0)); // The largest negative voltage = 0 and the largest positive // voltage = 0. But these limits are updated every single ISR before // actually executing the Iq controller. The limits depend on how much // voltage is left over after the Id controller executes. So having an // initial value of 0 does not affect Iq current controller execution. PID_set. Min. Max(pid. Handle[2], _IQ(0. 0)); // Set the initial condition value for the integrator output to 0 PID_set. Ui(pid. Handle[2], _IQ(0. 0)); } 6

proj_lab 11. c // For the current controller, Kp = Ls*bandwidth(rad/sec) But in order

proj_lab 11. c // For the current controller, Kp = Ls*bandwidth(rad/sec) But in order // to be used, it must be converted to per unit values by multiplying // by full. Scale. Current and then dividing by full. Scale. Voltage. From the // statement below, we see that the bandwidth in rad/sec is equal to // 0. 25/Isr. Period_sec, which is equal to USER_ISR_FREQ_HZ/4. This means // that by setting Kp as described below, the bandwidth in Hz is // USER_ISR_FREQ_HZ/(8*pi). _iq Kp_Id = _IQ((0. 25 * Ls_d * full. Scale. Current) / (Isr. Period_sec * full. Scale. Voltage)); // In order to achieve pole/zero cancellation (which reduces the // closed-loop transfer function from a second-order system to a // first-order system), Ki must equal Rs/Ls. Since the output of the // Ki gain stage is integrated by a DIGITAL integrator, the integrator // input must be scaled by 1/Isr. Period_sec. That's just the way // digital integrators work. But, since Isr. Period_sec is a constant, // we can save an additional multiplication operation by lumping this // term with the Ki value. _iq Ki_Id = _IQ(Rover. Ls_d * Isr. Period_sec); // Now do the same thing for Kp for the q-axis current controller. // If the motor is not an IPM motor, Ld and Lq are the same, which // means that Kp_Iq = Kp_Id _iq Kp_Iq = _IQ((0. 25 * Ls_q * full. Scale. Current) / (Isr. Period_sec * full. Scale. Voltage)); // Do the same thing for Ki for the q-axis current controller. If the // motor is not an IPM motor, Ld and Lq are the same, which means that // Ki_Iq = Ki_Id. _iq Ki_Iq = _IQ(Rover. Ls_q * Isr. Period_sec); // There are three PI controllers; one speed controller and two current // controllers. Each PI controller has two coefficients; Kp and Ki. // So you have a total of six coefficients that must be defined. // This is for the speed controller pid. Handle[0] = PID_init(&pid[0], sizeof(pid[0])); // This is for the Id current controller pid. Handle[1] = PID_init(&pid[1], sizeof(pid[1])); // This is for the Iq current controller pid. Handle[2] = PID_init(&pid[2], sizeof(pid[2])); 5 // The following instructions load the parameters for the speed PI // controller. PID_set. Gains(pid. Handle[0], _IQ(1. 0), _IQ(0. 01), _IQ(0. 0)); // The current limit is performed by the limits placed on the speed PI // controller output. In the following statement, the speed // controller's largest negative current is set to -max. Current_pu, and // the largest positive current is set to max. Current_pu. PID_set. Min. Max(pid. Handle[0], -max. Current_pu, max. Current_pu); PID_set. Ui(pid. Handle[0], _IQ(0. 0)); // Set the initial condition value // for the integrator output to 0 pid. Cnt. Speed = 0; // Set the counter for decimating the speed // controller to 0 // The following instructions load the parameters for the d-axis // current controller. // P term = Kp_Id, I term = Ki_Id, D term = 0 PID_set. Gains(pid. Handle[1], Kp_Id, Ki_Id, _IQ(0. 0)); // Largest negative voltage = -max. Voltage_pu, largest positive // voltage = max. Voltage_pu PID_set. Min. Max(pid. Handle[1], -max. Voltage_pu, max. Voltage_pu); // Set the initial condition value for the integrator output to 0 PID_set. Ui(pid. Handle[1], _IQ(0. 0)); // The following instructions load the parameters for the q-axis // current controller. // P term = Kp_Iq, I term = Ki_Iq, D term = 0 PID_set. Gains(pid. Handle[2], Kp_Iq, Ki_Iq, _IQ(0. 0)); // The largest negative voltage = 0 and the largest positive // voltage = 0. But these limits are updated every single ISR before // actually executing the Iq controller. The limits depend on how much // voltage is left over after the Id controller executes. So having an // initial value of 0 does not affect Iq current controller execution. PID_set. Min. Max(pid. Handle[2], _IQ(0. 0)); // Set the initial condition value for the integrator output to 0 PID_set. Ui(pid. Handle[2], _IQ(0. 0)); } 6

proj_lab 11. c // For the current controller, Kp = Ls*bandwidth(rad/sec) But in order

proj_lab 11. c // For the current controller, Kp = Ls*bandwidth(rad/sec) But in order // to be used, it must be converted to per unit values by multiplying // by full. Scale. Current and then dividing by full. Scale. Voltage. From the // statement below, we see that the bandwidth in rad/sec is equal to // 0. 25/Isr. Period_sec, which is equal to USER_ISR_FREQ_HZ/4. This means // that by setting Kp as described below, the bandwidth in Hz is // USER_ISR_FREQ_HZ/(8*pi). _iq Kp_Id = _IQ((0. 25 * Ls_d * full. Scale. Current) / (Isr. Period_sec * full. Scale. Voltage)); // In order to achieve pole/zero cancellation (which reduces the // closed-loop transfer function from a second-order system to a // first-order system), Ki must equal Rs/Ls. Since the output of the // Ki gain stage is integrated by a DIGITAL integrator, the integrator // input must be scaled by 1/Isr. Period_sec. That's just the way // digital integrators work. But, since Isr. Period_sec is a constant, // we can save an additional multiplication operation by lumping this // term with the Ki value. _iq Ki_Id = _IQ(Rover. Ls_d * Isr. Period_sec); // Now do the same thing for Kp for the q-axis current controller. // If the motor is not an IPM motor, Ld and Lq are the same, which // means that Kp_Iq = Kp_Id _iq Kp_Iq = _IQ((0. 25 * Ls_q * full. Scale. Current) / (Isr. Period_sec * full. Scale. Voltage)); // Do the same thing for Ki for the q-axis current controller. If the // motor is not an IPM motor, Ld and Lq are the same, which means that // Ki_Iq = Ki_Id. _iq Ki_Iq = _IQ(Rover. Ls_q * Isr. Period_sec); // There are three PI controllers; one speed controller and two current // controllers. Each PI controller has two coefficients; Kp and Ki. // So you have a total of six coefficients that must be defined. // This is for the speed controller pid. Handle[0] = PID_init(&pid[0], sizeof(pid[0])); // This is for the Id current controller pid. Handle[1] = PID_init(&pid[1], sizeof(pid[1])); // This is for the Iq current controller pid. Handle[2] = PID_init(&pid[2], sizeof(pid[2])); 5 // The following instructions load the parameters for the speed PI // controller. PID_set. Gains(pid. Handle[0], _IQ(1. 0), _IQ(0. 01), _IQ(0. 0)); // The current limit is performed by the limits placed on the speed PI // controller output. In the following statement, the speed // controller's largest negative current is set to -max. Current_pu, and // the largest positive current is set to max. Current_pu. PID_set. Min. Max(pid. Handle[0], -max. Current_pu, max. Current_pu); PID_set. Ui(pid. Handle[0], _IQ(0. 0)); // Set the initial condition value // for the integrator output to 0 pid. Cnt. Speed = 0; // Set the counter for decimating the speed // controller to 0 // The following instructions load the parameters for the d-axis // current controller. // P term = Kp_Id, I term = Ki_Id, D term = 0 PID_set. Gains(pid. Handle[1], Kp_Id, Ki_Id, _IQ(0. 0)); // Largest negative voltage = -max. Voltage_pu, largest positive // voltage = max. Voltage_pu PID_set. Min. Max(pid. Handle[1], -max. Voltage_pu, max. Voltage_pu); // Set the initial condition value for the integrator output to 0 PID_set. Ui(pid. Handle[1], _IQ(0. 0)); // The following instructions load the parameters for the q-axis // current controller. // P term = Kp_Iq, I term = Ki_Iq, D term = 0 PID_set. Gains(pid. Handle[2], Kp_Iq, Ki_Iq, _IQ(0. 0)); // The largest negative voltage = 0 and the largest positive // voltage = 0. But these limits are updated every single ISR before // actually executing the Iq controller. The limits depend on how much // voltage is left over after the Id controller executes. So having an // initial value of 0 does not affect Iq current controller execution. PID_set. Min. Max(pid. Handle[2], _IQ(0. 0)); // Set the initial condition value for the integrator output to 0 PID_set. Ui(pid. Handle[2], _IQ(0. 0)); } 6

proj_lab 11. c // For the current controller, Kp = Ls*bandwidth(rad/sec) But in order

proj_lab 11. c // For the current controller, Kp = Ls*bandwidth(rad/sec) But in order // to be used, it must be converted to per unit values by multiplying // by full. Scale. Current and then dividing by full. Scale. Voltage. From the // statement below, we see that the bandwidth in rad/sec is equal to // 0. 25/Isr. Period_sec, which is equal to USER_ISR_FREQ_HZ/4. This means // that by setting Kp as described below, the bandwidth in Hz is // USER_ISR_FREQ_HZ/(8*pi). _iq Kp_Id = _IQ((0. 25 * Ls_d * full. Scale. Current) / (Isr. Period_sec * full. Scale. Voltage)); // In order to achieve pole/zero cancellation (which reduces the // closed-loop transfer function from a second-order system to a // first-order system), Ki must equal Rs/Ls. Since the output of the // Ki gain stage is integrated by a DIGITAL integrator, the integrator // input must be scaled by 1/Isr. Period_sec. That's just the way // digital integrators work. But, since Isr. Period_sec is a constant, // we can save an additional multiplication operation by lumping this // term with the Ki value. _iq Ki_Id = _IQ(Rover. Ls_d * Isr. Period_sec); // Now do the same thing for Kp for the q-axis current controller. // If the motor is not an IPM motor, Ld and Lq are the same, which // means that Kp_Iq = Kp_Id _iq Kp_Iq = _IQ((0. 25 * Ls_q * full. Scale. Current) / (Isr. Period_sec * full. Scale. Voltage)); // Do the same thing for Ki for the q-axis current controller. If the // motor is not an IPM motor, Ld and Lq are the same, which means that // Ki_Iq = Ki_Id. _iq Ki_Iq = _IQ(Rover. Ls_q * Isr. Period_sec); // There are three PI controllers; one speed controller and two current // controllers. Each PI controller has two coefficients; Kp and Ki. // So you have a total of six coefficients that must be defined. // This is for the speed controller pid. Handle[0] = PID_init(&pid[0], sizeof(pid[0])); // This is for the Id current controller pid. Handle[1] = PID_init(&pid[1], sizeof(pid[1])); // This is for the Iq current controller pid. Handle[2] = PID_init(&pid[2], sizeof(pid[2])); 5 // The following instructions load the parameters for the speed PI // controller. PID_set. Gains(pid. Handle[0], _IQ(1. 0), _IQ(0. 01), _IQ(0. 0)); // The current limit is performed by the limits placed on the speed PI // controller output. In the following statement, the speed // controller's largest negative current is set to -max. Current_pu, and // the largest positive current is set to max. Current_pu. PID_set. Min. Max(pid. Handle[0], -max. Current_pu, max. Current_pu); PID_set. Ui(pid. Handle[0], _IQ(0. 0)); // Set the initial condition value // for the integrator output to 0 pid. Cnt. Speed = 0; // Set the counter for decimating the speed // controller to 0 // The following instructions load the parameters for the d-axis // current controller. // P term = Kp_Id, I term = Ki_Id, D term = 0 PID_set. Gains(pid. Handle[1], Kp_Id, Ki_Id, _IQ(0. 0)); // Largest negative voltage = -max. Voltage_pu, largest positive // voltage = max. Voltage_pu PID_set. Min. Max(pid. Handle[1], -max. Voltage_pu, max. Voltage_pu); // Set the initial condition value for the integrator output to 0 PID_set. Ui(pid. Handle[1], _IQ(0. 0)); // The following instructions load the parameters for the q-axis // current controller. // P term = Kp_Iq, I term = Ki_Iq, D term = 0 PID_set. Gains(pid. Handle[2], Kp_Iq, Ki_Iq, _IQ(0. 0)); // The largest negative voltage = 0 and the largest positive // voltage = 0. But these limits are updated every single ISR before // actually executing the Iq controller. The limits depend on how much // voltage is left over after the Id controller executes. So having an // initial value of 0 does not affect Iq current controller execution. PID_set. Min. Max(pid. Handle[2], _IQ(0. 0)); // Set the initial condition value for the integrator output to 0 PID_set. Ui(pid. Handle[2], _IQ(0. 0)); } 6

proj_lab 11. c // For the current controller, Kp = Ls*bandwidth(rad/sec) But in order

proj_lab 11. c // For the current controller, Kp = Ls*bandwidth(rad/sec) But in order // to be used, it must be converted to per unit values by multiplying // by full. Scale. Current and then dividing by full. Scale. Voltage. From the // statement below, we see that the bandwidth in rad/sec is equal to // 0. 25/Isr. Period_sec, which is equal to USER_ISR_FREQ_HZ/4. This means // that by setting Kp as described below, the bandwidth in Hz is // USER_ISR_FREQ_HZ/(8*pi). _iq Kp_Id = _IQ((0. 25 * Ls_d * full. Scale. Current) / (Isr. Period_sec * full. Scale. Voltage)); // In order to achieve pole/zero cancellation (which reduces the // closed-loop transfer function from a second-order system to a // first-order system), Ki must equal Rs/Ls. Since the output of the // Ki gain stage is integrated by a DIGITAL integrator, the integrator // input must be scaled by 1/Isr. Period_sec. That's just the way // digital integrators work. But, since Isr. Period_sec is a constant, // we can save an additional multiplication operation by lumping this // term with the Ki value. _iq Ki_Id = _IQ(Rover. Ls_d * Isr. Period_sec); // Now do the same thing for Kp for the q-axis current controller. // If the motor is not an IPM motor, Ld and Lq are the same, which // means that Kp_Iq = Kp_Id _iq Kp_Iq = _IQ((0. 25 * Ls_q * full. Scale. Current) / (Isr. Period_sec * full. Scale. Voltage)); // Do the same thing for Ki for the q-axis current controller. If the // motor is not an IPM motor, Ld and Lq are the same, which means that // Ki_Iq = Ki_Id. _iq Ki_Iq = _IQ(Rover. Ls_q * Isr. Period_sec); // There are three PI controllers; one speed controller and two current // controllers. Each PI controller has two coefficients; Kp and Ki. // So you have a total of six coefficients that must be defined. // This is for the speed controller pid. Handle[0] = PID_init(&pid[0], sizeof(pid[0])); // This is for the Id current controller pid. Handle[1] = PID_init(&pid[1], sizeof(pid[1])); // This is for the Iq current controller pid. Handle[2] = PID_init(&pid[2], sizeof(pid[2])); 5 // The following instructions load the parameters for the speed PI // controller. PID_set. Gains(pid. Handle[0], _IQ(1. 0), _IQ(0. 01), _IQ(0. 0)); // The current limit is performed by the limits placed on the speed PI // controller output. In the following statement, the speed // controller's largest negative current is set to -max. Current_pu, and // the largest positive current is set to max. Current_pu. PID_set. Min. Max(pid. Handle[0], -max. Current_pu, max. Current_pu); PID_set. Ui(pid. Handle[0], _IQ(0. 0)); // Set the initial condition value // for the integrator output to 0 pid. Cnt. Speed = 0; // Set the counter for decimating the speed // controller to 0 // The following instructions load the parameters for the d-axis // current controller. // P term = Kp_Id, I term = Ki_Id, D term = 0 PID_set. Gains(pid. Handle[1], Kp_Id, Ki_Id, _IQ(0. 0)); // Largest negative voltage = -max. Voltage_pu, largest positive // voltage = max. Voltage_pu PID_set. Min. Max(pid. Handle[1], -max. Voltage_pu, max. Voltage_pu); // Set the initial condition value for the integrator output to 0 PID_set. Ui(pid. Handle[1], _IQ(0. 0)); // The following instructions load the parameters for the q-axis // current controller. // P term = Kp_Iq, I term = Ki_Iq, D term = 0 PID_set. Gains(pid. Handle[2], Kp_Iq, Ki_Iq, _IQ(0. 0)); // The largest negative voltage = 0 and the largest positive // voltage = 0. But these limits are updated every single ISR before // actually executing the Iq controller. The limits depend on how much // voltage is left over after the Id controller executes. So having an // initial value of 0 does not affect Iq current controller execution. PID_set. Min. Max(pid. Handle[2], _IQ(0. 0)); // Set the initial condition value for the integrator output to 0 PID_set. Ui(pid. Handle[2], _IQ(0. 0)); } 6

proj_lab 11. c // set the hardware abstraction layer parameters // This function initializes

proj_lab 11. c // set the hardware abstraction layer parameters // This function initializes all peripherals through a Hardware Abstraction // Layer (HAL). It uses all values stored in g. User. Params. HAL_set. Params(hal. Handle, &g. User. Params); #ifdef FAST_ROM_V 1 p 6 { // These function calls are used to initialize the estimator with ROM // function calls. It needs the specific address where the controller // object is declared by the ROM code. CTRL_Handle ctrl. Handle = CTRL_init((void *)USER_CTRL_HANDLE_ADDRESS , 0 x 200); CTRL_Obj *obj = (CTRL_Obj *)ctrl. Handle; // this sets the estimator handle (part of the controller object) to // the same value initialized above by the EST_init() function call. // This is done so the next function implemented in ROM, can // successfully initialize the estimator as part of the controller // object. obj->est. Handle = est. Handle; // initialize the estimator through the controller. These three // function calls are needed for the F 2806 x. F/M implementation of // Insta. SPIN. CTRL_set. Params(ctrl. Handle, &g. User. Params); CTRL_set. User. Motor. Params(ctrl. Handle); CTRL_setup. Est. Idle. State(ctrl. Handle); } #else { // initialize the estimator. These two function calls are needed for // the F 2802 x. F implementation of Insta. SPIN using the estimator handle // initialized by EST_init(), these two function calls configure the // estimator, and they set the estimator in a proper state prior to // spinning a motor. EST_set. Est. Params(est. Handle, &g. User. Params); EST_setup. Est. Idle. State(est. Handle); } #endif 3 // disable Rs recalculation EST_set. Flag_enable. Rs. Recalc(est. Handle, false); // set the number of current sensors setup. Clarke_I(clarke. Handle_I, USER_NUM_CURRENT_SENSORS); // set the number of voltage sensors setup. Clarke_V(clarke. Handle_V, USER_NUM_VOLTAGE_SENSORS); // set the pre-determined current and voltage feedback offset values g. Offsets_I_pu. value[0] = _IQ(I_A_offset); g. Offsets_I_pu. value[1] = _IQ(I_B_offset); g. Offsets_I_pu. value[2] = _IQ(I_C_offset); g. Offsets_V_pu. value[0] = _IQ(V_A_offset); g. Offsets_V_pu. value[1] = _IQ(V_B_offset); g. Offsets_V_pu. value[2] = _IQ(V_C_offset); // initialize the PID controllers { // This equation defines the relationship between per unit current and // real-world current. The resulting value in per units (pu) is then // used to configure the controllers _iq max. Current_pu = _IQ(USER_MOTOR_MAX_CURRENT / USER_IQ_FULL_SCALE_CURRENT_A); // This equation uses the scaled maximum voltage vector, which is // already in per units, hence there is no need to include the #define // for USER_IQ_FULL_SCALE_VOLTAGE_V _iq max. Voltage_pu = _IQ(USER_MAX_VS_MAG_PU * USER_VD_SF); float_t full. Scale. Current = USER_IQ_FULL_SCALE_CURRENT_A; float_t full. Scale. Voltage = USER_IQ_FULL_SCALE_VOLTAGE_V; float_t Isr. Period_sec = 1. 0 / USER_ISR_FREQ_Hz; float_t Ls_d = USER_MOTOR_Ls_d; float_t Ls_q = USER_MOTOR_Ls_q; float_t Rs = USER_MOTOR_Rs; // This lab assumes that motor parameters are known, and it does not // perform motor ID, so the R/L parameters are known and defined in // user. h float_t Rover. Ls_d = Rs / Ls_d; float_t Rover. Ls_q = Rs / Ls_q; 4

proj_lab 11. c // For the current controller, Kp = Ls*bandwidth(rad/sec) But in order

proj_lab 11. c // For the current controller, Kp = Ls*bandwidth(rad/sec) But in order // to be used, it must be converted to per unit values by multiplying // by full. Scale. Current and then dividing by full. Scale. Voltage. From the // statement below, we see that the bandwidth in rad/sec is equal to // 0. 25/Isr. Period_sec, which is equal to USER_ISR_FREQ_HZ/4. This means // that by setting Kp as described below, the bandwidth in Hz is // USER_ISR_FREQ_HZ/(8*pi). _iq Kp_Id = _IQ((0. 25 * Ls_d * full. Scale. Current) / (Isr. Period_sec * full. Scale. Voltage)); // In order to achieve pole/zero cancellation (which reduces the // closed-loop transfer function from a second-order system to a // first-order system), Ki must equal Rs/Ls. Since the output of the // Ki gain stage is integrated by a DIGITAL integrator, the integrator // input must be scaled by 1/Isr. Period_sec. That's just the way // digital integrators work. But, since Isr. Period_sec is a constant, // we can save an additional multiplication operation by lumping this // term with the Ki value. _iq Ki_Id = _IQ(Rover. Ls_d * Isr. Period_sec); // Now do the same thing for Kp for the q-axis current controller. // If the motor is not an IPM motor, Ld and Lq are the same, which // means that Kp_Iq = Kp_Id _iq Kp_Iq = _IQ((0. 25 * Ls_q * full. Scale. Current) / (Isr. Period_sec * full. Scale. Voltage)); // Do the same thing for Ki for the q-axis current controller. If the // motor is not an IPM motor, Ld and Lq are the same, which means that // Ki_Iq = Ki_Id. _iq Ki_Iq = _IQ(Rover. Ls_q * Isr. Period_sec); // There are three PI controllers; one speed controller and two current // controllers. Each PI controller has two coefficients; Kp and Ki. // So you have a total of six coefficients that must be defined. // This is for the speed controller pid. Handle[0] = PID_init(&pid[0], sizeof(pid[0])); // This is for the Id current controller pid. Handle[1] = PID_init(&pid[1], sizeof(pid[1])); // This is for the Iq current controller pid. Handle[2] = PID_init(&pid[2], sizeof(pid[2])); 5 // The following instructions load the parameters for the speed PI // controller. PID_set. Gains(pid. Handle[0], _IQ(1. 0), _IQ(0. 01), _IQ(0. 0)); // The current limit is performed by the limits placed on the speed PI // controller output. In the following statement, the speed // controller's largest negative current is set to -max. Current_pu, and // the largest positive current is set to max. Current_pu. PID_set. Min. Max(pid. Handle[0], -max. Current_pu, max. Current_pu); PID_set. Ui(pid. Handle[0], _IQ(0. 0)); // Set the initial condition value // for the integrator output to 0 pid. Cnt. Speed = 0; // Set the counter for decimating the speed // controller to 0 // The following instructions load the parameters for the d-axis // current controller. // P term = Kp_Id, I term = Ki_Id, D term = 0 PID_set. Gains(pid. Handle[1], Kp_Id, Ki_Id, _IQ(0. 0)); // Largest negative voltage = -max. Voltage_pu, largest positive // voltage = max. Voltage_pu PID_set. Min. Max(pid. Handle[1], -max. Voltage_pu, max. Voltage_pu); // Set the initial condition value for the integrator output to 0 PID_set. Ui(pid. Handle[1], _IQ(0. 0)); // The following instructions load the parameters for the q-axis // current controller. // P term = Kp_Iq, I term = Ki_Iq, D term = 0 PID_set. Gains(pid. Handle[2], Kp_Iq, Ki_Iq, _IQ(0. 0)); // The largest negative voltage = 0 and the largest positive // voltage = 0. But these limits are updated every single ISR before // actually executing the Iq controller. The limits depend on how much // voltage is left over after the Id controller executes. So having an // initial value of 0 does not affect Iq current controller execution. PID_set. Min. Max(pid. Handle[2], _IQ(0. 0)); // Set the initial condition value for the integrator output to 0 PID_set. Ui(pid. Handle[2], _IQ(0. 0)); } 6

proj_lab 11. c // set the hardware abstraction layer parameters // This function initializes

proj_lab 11. c // set the hardware abstraction layer parameters // This function initializes all peripherals through a Hardware Abstraction // Layer (HAL). It uses all values stored in g. User. Params. HAL_set. Params(hal. Handle, &g. User. Params); #ifdef FAST_ROM_V 1 p 6 { // These function calls are used to initialize the estimator with ROM // function calls. It needs the specific address where the controller // object is declared by the ROM code. CTRL_Handle ctrl. Handle = CTRL_init((void *)USER_CTRL_HANDLE_ADDRESS , 0 x 200); CTRL_Obj *obj = (CTRL_Obj *)ctrl. Handle; // this sets the estimator handle (part of the controller object) to // the same value initialized above by the EST_init() function call. // This is done so the next function implemented in ROM, can // successfully initialize the estimator as part of the controller // object. obj->est. Handle = est. Handle; // initialize the estimator through the controller. These three // function calls are needed for the F 2806 x. F/M implementation of // Insta. SPIN. CTRL_set. Params(ctrl. Handle, &g. User. Params); CTRL_set. User. Motor. Params(ctrl. Handle); CTRL_setup. Est. Idle. State(ctrl. Handle); } #else { // initialize the estimator. These two function calls are needed for // the F 2802 x. F implementation of Insta. SPIN using the estimator handle // initialized by EST_init(), these two function calls configure the // estimator, and they set the estimator in a proper state prior to // spinning a motor. EST_set. Est. Params(est. Handle, &g. User. Params); EST_setup. Est. Idle. State(est. Handle); } #endif 3 // disable Rs recalculation EST_set. Flag_enable. Rs. Recalc(est. Handle, false); // set the number of current sensors setup. Clarke_I(clarke. Handle_I, USER_NUM_CURRENT_SENSORS); // set the number of voltage sensors setup. Clarke_V(clarke. Handle_V, USER_NUM_VOLTAGE_SENSORS); // set the pre-determined current and voltage feedback offset values g. Offsets_I_pu. value[0] = _IQ(I_A_offset); g. Offsets_I_pu. value[1] = _IQ(I_B_offset); g. Offsets_I_pu. value[2] = _IQ(I_C_offset); g. Offsets_V_pu. value[0] = _IQ(V_A_offset); g. Offsets_V_pu. value[1] = _IQ(V_B_offset); g. Offsets_V_pu. value[2] = _IQ(V_C_offset); // initialize the PID controllers { // This equation defines the relationship between per unit current and // real-world current. The resulting value in per units (pu) is then // used to configure the controllers _iq max. Current_pu = _IQ(USER_MOTOR_MAX_CURRENT / USER_IQ_FULL_SCALE_CURRENT_A); // This equation uses the scaled maximum voltage vector, which is // already in per units, hence there is no need to include the #define // for USER_IQ_FULL_SCALE_VOLTAGE_V _iq max. Voltage_pu = _IQ(USER_MAX_VS_MAG_PU * USER_VD_SF); float_t full. Scale. Current = USER_IQ_FULL_SCALE_CURRENT_A; float_t full. Scale. Voltage = USER_IQ_FULL_SCALE_VOLTAGE_V; float_t Isr. Period_sec = 1. 0 / USER_ISR_FREQ_Hz; float_t Ls_d = USER_MOTOR_Ls_d; float_t Ls_q = USER_MOTOR_Ls_q; float_t Rs = USER_MOTOR_Rs; // This lab assumes that motor parameters are known, and it does not // perform motor ID, so the R/L parameters are known and defined in // user. h float_t Rover. Ls_d = Rs / Ls_d; float_t Rover. Ls_q = Rs / Ls_q; 4

proj_lab 11. c // For the current controller, Kp = Ls*bandwidth(rad/sec) But in order

proj_lab 11. c // For the current controller, Kp = Ls*bandwidth(rad/sec) But in order // to be used, it must be converted to per unit values by multiplying // by full. Scale. Current and then dividing by full. Scale. Voltage. From the // statement below, we see that the bandwidth in rad/sec is equal to // 0. 25/Isr. Period_sec, which is equal to USER_ISR_FREQ_HZ/4. This means // that by setting Kp as described below, the bandwidth in Hz is // USER_ISR_FREQ_HZ/(8*pi). _iq Kp_Id = _IQ((0. 25 * Ls_d * full. Scale. Current) / (Isr. Period_sec * full. Scale. Voltage)); // In order to achieve pole/zero cancellation (which reduces the // closed-loop transfer function from a second-order system to a // first-order system), Ki must equal Rs/Ls. Since the output of the // Ki gain stage is integrated by a DIGITAL integrator, the integrator // input must be scaled by 1/Isr. Period_sec. That's just the way // digital integrators work. But, since Isr. Period_sec is a constant, // we can save an additional multiplication operation by lumping this // term with the Ki value. _iq Ki_Id = _IQ(Rover. Ls_d * Isr. Period_sec); // Now do the same thing for Kp for the q-axis current controller. // If the motor is not an IPM motor, Ld and Lq are the same, which // means that Kp_Iq = Kp_Id _iq Kp_Iq = _IQ((0. 25 * Ls_q * full. Scale. Current) / (Isr. Period_sec * full. Scale. Voltage)); // Do the same thing for Ki for the q-axis current controller. If the // motor is not an IPM motor, Ld and Lq are the same, which means that // Ki_Iq = Ki_Id. _iq Ki_Iq = _IQ(Rover. Ls_q * Isr. Period_sec); // There are three PI controllers; one speed controller and two current // controllers. Each PI controller has two coefficients; Kp and Ki. // So you have a total of six coefficients that must be defined. // This is for the speed controller pid. Handle[0] = PID_init(&pid[0], sizeof(pid[0])); // This is for the Id current controller pid. Handle[1] = PID_init(&pid[1], sizeof(pid[1])); // This is for the Iq current controller pid. Handle[2] = PID_init(&pid[2], sizeof(pid[2])); 5 // The following instructions load the parameters for the speed PI // controller. PID_set. Gains(pid. Handle[0], _IQ(1. 0), _IQ(0. 01), _IQ(0. 0)); // The current limit is performed by the limits placed on the speed PI // controller output. In the following statement, the speed // controller's largest negative current is set to -max. Current_pu, and // the largest positive current is set to max. Current_pu. PID_set. Min. Max(pid. Handle[0], -max. Current_pu, max. Current_pu); PID_set. Ui(pid. Handle[0], _IQ(0. 0)); // Set the initial condition value // for the integrator output to 0 pid. Cnt. Speed = 0; // Set the counter for decimating the speed // controller to 0 // The following instructions load the parameters for the d-axis // current controller. // P term = Kp_Id, I term = Ki_Id, D term = 0 PID_set. Gains(pid. Handle[1], Kp_Id, Ki_Id, _IQ(0. 0)); // Largest negative voltage = -max. Voltage_pu, largest positive // voltage = max. Voltage_pu PID_set. Min. Max(pid. Handle[1], -max. Voltage_pu, max. Voltage_pu); // Set the initial condition value for the integrator output to 0 PID_set. Ui(pid. Handle[1], _IQ(0. 0)); // The following instructions load the parameters for the q-axis // current controller. // P term = Kp_Iq, I term = Ki_Iq, D term = 0 PID_set. Gains(pid. Handle[2], Kp_Iq, Ki_Iq, _IQ(0. 0)); // The largest negative voltage = 0 and the largest positive // voltage = 0. But these limits are updated every single ISR before // actually executing the Iq controller. The limits depend on how much // voltage is left over after the Id controller executes. So having an // initial value of 0 does not affect Iq current controller execution. PID_set. Min. Max(pid. Handle[2], _IQ(0. 0)); // Set the initial condition value for the integrator output to 0 PID_set. Ui(pid. Handle[2], _IQ(0. 0)); } 6

proj_lab 11. c // For the current controller, Kp = Ls*bandwidth(rad/sec) But in order

proj_lab 11. c // For the current controller, Kp = Ls*bandwidth(rad/sec) But in order // to be used, it must be converted to per unit values by multiplying // by full. Scale. Current and then dividing by full. Scale. Voltage. From the // statement below, we see that the bandwidth in rad/sec is equal to // 0. 25/Isr. Period_sec, which is equal to USER_ISR_FREQ_HZ/4. This means // that by setting Kp as described below, the bandwidth in Hz is // USER_ISR_FREQ_HZ/(8*pi). _iq Kp_Id = _IQ((0. 25 * Ls_d * full. Scale. Current) / (Isr. Period_sec * full. Scale. Voltage)); // In order to achieve pole/zero cancellation (which reduces the // closed-loop transfer function from a second-order system to a // first-order system), Ki must equal Rs/Ls. Since the output of the // Ki gain stage is integrated by a DIGITAL integrator, the integrator // input must be scaled by 1/Isr. Period_sec. That's just the way // digital integrators work. But, since Isr. Period_sec is a constant, // we can save an additional multiplication operation by lumping this // term with the Ki value. _iq Ki_Id = _IQ(Rover. Ls_d * Isr. Period_sec); // Now do the same thing for Kp for the q-axis current controller. // If the motor is not an IPM motor, Ld and Lq are the same, which // means that Kp_Iq = Kp_Id _iq Kp_Iq = _IQ((0. 25 * Ls_q * full. Scale. Current) / (Isr. Period_sec * full. Scale. Voltage)); // Do the same thing for Ki for the q-axis current controller. If the // motor is not an IPM motor, Ld and Lq are the same, which means that // Ki_Iq = Ki_Id. _iq Ki_Iq = _IQ(Rover. Ls_q * Isr. Period_sec); // There are three PI controllers; one speed controller and two current // controllers. Each PI controller has two coefficients; Kp and Ki. // So you have a total of six coefficients that must be defined. // This is for the speed controller pid. Handle[0] = PID_init(&pid[0], sizeof(pid[0])); // This is for the Id current controller pid. Handle[1] = PID_init(&pid[1], sizeof(pid[1])); // This is for the Iq current controller pid. Handle[2] = PID_init(&pid[2], sizeof(pid[2])); 5 // The following instructions load the parameters for the speed PI // controller. PID_set. Gains(pid. Handle[0], _IQ(1. 0), _IQ(0. 01), _IQ(0. 0)); // The current limit is performed by the limits placed on the speed PI // controller output. In the following statement, the speed // controller's largest negative current is set to -max. Current_pu, and // the largest positive current is set to max. Current_pu. PID_set. Min. Max(pid. Handle[0], -max. Current_pu, max. Current_pu); PID_set. Ui(pid. Handle[0], _IQ(0. 0)); // Set the initial condition value // for the integrator output to 0 pid. Cnt. Speed = 0; // Set the counter for decimating the speed // controller to 0 // The following instructions load the parameters for the d-axis // current controller. // P term = Kp_Id, I term = Ki_Id, D term = 0 PID_set. Gains(pid. Handle[1], Kp_Id, Ki_Id, _IQ(0. 0)); // Largest negative voltage = -max. Voltage_pu, largest positive // voltage = max. Voltage_pu PID_set. Min. Max(pid. Handle[1], -max. Voltage_pu, max. Voltage_pu); // Set the initial condition value for the integrator output to 0 PID_set. Ui(pid. Handle[1], _IQ(0. 0)); // The following instructions load the parameters for the q-axis // current controller. // P term = Kp_Iq, I term = Ki_Iq, D term = 0 PID_set. Gains(pid. Handle[2], Kp_Iq, Ki_Iq, _IQ(0. 0)); // The largest negative voltage = 0 and the largest positive // voltage = 0. But these limits are updated every single ISR before // actually executing the Iq controller. The limits depend on how much // voltage is left over after the Id controller executes. So having an // initial value of 0 does not affect Iq current controller execution. PID_set. Min. Max(pid. Handle[2], _IQ(0. 0)); // Set the initial condition value for the integrator output to 0 PID_set. Ui(pid. Handle[2], _IQ(0. 0)); } 6

proj_lab 11. c // For the current controller, Kp = Ls*bandwidth(rad/sec) But in order

proj_lab 11. c // For the current controller, Kp = Ls*bandwidth(rad/sec) But in order // to be used, it must be converted to per unit values by multiplying // by full. Scale. Current and then dividing by full. Scale. Voltage. From the // statement below, we see that the bandwidth in rad/sec is equal to // 0. 25/Isr. Period_sec, which is equal to USER_ISR_FREQ_HZ/4. This means // that by setting Kp as described below, the bandwidth in Hz is // USER_ISR_FREQ_HZ/(8*pi). _iq Kp_Id = _IQ((0. 25 * Ls_d * full. Scale. Current) / (Isr. Period_sec * full. Scale. Voltage)); // In order to achieve pole/zero cancellation (which reduces the // closed-loop transfer function from a second-order system to a // first-order system), Ki must equal Rs/Ls. Since the output of the // Ki gain stage is integrated by a DIGITAL integrator, the integrator // input must be scaled by 1/Isr. Period_sec. That's just the way // digital integrators work. But, since Isr. Period_sec is a constant, // we can save an additional multiplication operation by lumping this // term with the Ki value. _iq Ki_Id = _IQ(Rover. Ls_d * Isr. Period_sec); // Now do the same thing for Kp for the q-axis current controller. // If the motor is not an IPM motor, Ld and Lq are the same, which // means that Kp_Iq = Kp_Id _iq Kp_Iq = _IQ((0. 25 * Ls_q * full. Scale. Current) / (Isr. Period_sec * full. Scale. Voltage)); // Do the same thing for Ki for the q-axis current controller. If the // motor is not an IPM motor, Ld and Lq are the same, which means that // Ki_Iq = Ki_Id. _iq Ki_Iq = _IQ(Rover. Ls_q * Isr. Period_sec); // There are three PI controllers; one speed controller and two current // controllers. Each PI controller has two coefficients; Kp and Ki. // So you have a total of six coefficients that must be defined. // This is for the speed controller pid. Handle[0] = PID_init(&pid[0], sizeof(pid[0])); // This is for the Id current controller pid. Handle[1] = PID_init(&pid[1], sizeof(pid[1])); // This is for the Iq current controller pid. Handle[2] = PID_init(&pid[2], sizeof(pid[2])); 5 // The following instructions load the parameters for the speed PI // controller. PID_set. Gains(pid. Handle[0], _IQ(1. 0), _IQ(0. 01), _IQ(0. 0)); // The current limit is performed by the limits placed on the speed PI // controller output. In the following statement, the speed // controller's largest negative current is set to -max. Current_pu, and // the largest positive current is set to max. Current_pu. PID_set. Min. Max(pid. Handle[0], -max. Current_pu, max. Current_pu); PID_set. Ui(pid. Handle[0], _IQ(0. 0)); // Set the initial condition value // for the integrator output to 0 pid. Cnt. Speed = 0; // Set the counter for decimating the speed // controller to 0 // The following instructions load the parameters for the d-axis // current controller. // P term = Kp_Id, I term = Ki_Id, D term = 0 PID_set. Gains(pid. Handle[1], Kp_Id, Ki_Id, _IQ(0. 0)); // Largest negative voltage = -max. Voltage_pu, largest positive // voltage = max. Voltage_pu PID_set. Min. Max(pid. Handle[1], -max. Voltage_pu, max. Voltage_pu); // Set the initial condition value for the integrator output to 0 PID_set. Ui(pid. Handle[1], _IQ(0. 0)); // The following instructions load the parameters for the q-axis // current controller. // P term = Kp_Iq, I term = Ki_Iq, D term = 0 PID_set. Gains(pid. Handle[2], Kp_Iq, Ki_Iq, _IQ(0. 0)); // The largest negative voltage = 0 and the largest positive // voltage = 0. But these limits are updated every single ISR before // actually executing the Iq controller. The limits depend on how much // voltage is left over after the Id controller executes. So having an // initial value of 0 does not affect Iq current controller execution. PID_set. Min. Max(pid. Handle[2], _IQ(0. 0)); // Set the initial condition value for the integrator output to 0 PID_set. Ui(pid. Handle[2], _IQ(0. 0)); } 6

proj_lab 11. c // The voltage vector is now calculated and ready to be

proj_lab 11. c // The voltage vector is now calculated and ready to be applied to the // motor in the form of three PWM signals. However, even though the // voltages may be supplied to the PWM module now, they won't be // applied to the motor until the next PWM cycle. By this point, the // motor will have moved away from the angle that the voltage vector // was calculated for, by an amount which is proportional to the // sampling frequency and the speed of the motor. For steady-state // speeds, we can calculate this angle delay and compensate for it. angle_pu = angle. Delay. Comp(speed_pu, angle_pu); // compute the sin/cos phasor since the phase is used by the inverse // Park transform to transform from DQ to Alpha/Beta. The phasor // calculation is not part of the IPARK module. The way it works is, // the phasor is calculated by doing a sine and cosine, then the phasor // is copied into the IPARK module, and then finally the IPARK module // uses the phasor to calculate from DQ to Alpha/Beta phasor. value[0] = _IQcos. PU(angle_pu); phasor. value[1] = _IQsin. PU(angle_pu); // set the phasor in the inverse Park transform IPARK_set. Phasor(ipark. Handle, &phasor); // Run the inverse Park module. This converts the voltage vector from // synchronous frame values to stationary frame values. IPARK_run(ipark. Handle, &g. Vdq_out_pu, &Vab_pu); // This code compensates for variations in the DC bus by adjusting the // PWM duty cycle to compensate for the variations. the goal is to // produce the desired volt-second product regardless of the DC bus // value. To do this, we must divide the desired voltage values by // the DC bus value. Or. . . it is easier to multiply by 1/(DC bus value) one. Over. Dc. Bus = EST_get. One. Over. Dc. Bus_pu(est. Handle); Vab_pu. value[0] = _IQmpy(Vab_pu. value[0], one. Over. Dc. Bus); Vab_pu. value[1] = _IQmpy(Vab_pu. value[1], one. Over. Dc. Bus); // Now run the space vector generator (SVGEN) module. // There is no need to do an inverse CLARKE transform, as this is // handled in the SVGEN_run function. SVGEN_run(svgen. Handle, &Vab_pu, &(g. Pwm. Data. Tabc)); } 13 else // g. Motor. Vars. Flag_Run_Identify = 0 { // disable the PWM HAL_disable. Pwm(hal. Handle); // Set the PWMs to 50% duty cycle g. Pwm. Data. Tabc. value[0] = _IQ(0. 0); g. Pwm. Data. Tabc. value[1] = _IQ(0. 0); g. Pwm. Data. Tabc. value[2] = _IQ(0. 0); } // write to the PWM compare registers, and then we are done! HAL_write. Pwm. Data(hal. Handle, &g. Pwm. Data); return; } // end of main. ISR() function 14

proj_lab 11. c // run the estimator // The speed reference is needed so

proj_lab 11. c // run the estimator // The speed reference is needed so that the proper sign of the forced // angle is calculated. When the estimator does not do motor ID as in this // lab, only the sign of the speed reference is used EST_run(est. Handle, &Iab_pu, &Vab_pu, g. Adc. Data. dc. Bus, g. Motor. Vars. Speed. Ref_pu); // generate the motor electrical angle_pu = EST_get. Angle_pu(est. Handle); speed_pu = EST_get. Fm_pu(est. Handle); // get Idq from estimator to avoid sin and cos, and a Park transform, // which saves CPU cycles EST_get. Idq_pu(est. Handle, &g. Idq_pu); // run the appropriate controller if(g. Motor. Vars. Flag_Run_Identify) { // Declaration of local variables. _iq ref. Value; _iq fback. Value; _iq out. Max_pu; // when appropriate, run the PID speed controller // This mechanism provides the decimation for the speed loop. if(pid. Cnt. Speed >= USER_NUM_CTRL_TICKS_PER_SPEED_TICK) { // Reset the Speed PID execution counter. pid. Cnt. Speed = 0; // The next instruction executes the PI speed controller and places // its output in Idq_ref_pu. value[1], which is the input reference // value for the q-axis current controller. PID_run_spd(pid. Handle[0], g. Motor. Vars. Speed. Ref_pu, speed_pu, &(g. Idq_ref_pu. value[1])); } else { // increment counter pid. Cnt. Speed++; } 11 // Get the reference value for the d-axis current controller. ref. Value = g. Idq_ref_pu. value[0]; // Get the actual value of Id fback. Value = g. Idq_pu. value[0]; // The next instruction executes the PI current controller for the // d axis and places its output in Vdq_pu. value[0], which is the // control voltage along the d-axis (Vd) PID_run(pid. Handle[1], ref. Value, fback. Value, &(g. Vdq_out_pu. value[0])); // get the Iq reference value ref. Value = g. Idq_ref_pu. value[1]; // get the actual value of Iq fback. Value = g. Idq_pu. value[1]; // The voltage limits on the output of the q-axis current controller // are dynamic, and are dependent on the output voltage from the d-axis // current controller. In other words, the d-axis current controller // gets first dibs on the available voltage, and the q-axis current // controller gets what's left over. That is why the d-axis current // controller executes first. The next instruction calculates the // maximum limits for this voltage as: // Vq_min_max = +/- sqrt(Vbus^2 - Vd^2) out. Max_pu = _IQsqrt(_IQ(USER_MAX_VS_MAG_PU * USER_MAX_VS_MAG_PU) - _IQmpy(g. Vdq_out_pu. value[0], g. Vdq_out_pu. value[0])); // Set the limits to +/- out. Max_pu PID_set. Min. Max(pid. Handle[2], -out. Max_pu, out. Max_pu); // The next instruction executes the PI current controller for the // q axis and places its output in Vdq_pu. value[1], which is the // control voltage vector along the q-axis (Vq) PID_run(pid. Handle[2], ref. Value, fback. Value, &(g. Vdq_out_pu. value[1 ])); 12

proj_lab 11. c // run the estimator // The speed reference is needed so

proj_lab 11. c // run the estimator // The speed reference is needed so that the proper sign of the forced // angle is calculated. When the estimator does not do motor ID as in this // lab, only the sign of the speed reference is used EST_run(est. Handle, &Iab_pu, &Vab_pu, g. Adc. Data. dc. Bus, g. Motor. Vars. Speed. Ref_pu); // generate the motor electrical angle_pu = EST_get. Angle_pu(est. Handle); speed_pu = EST_get. Fm_pu(est. Handle); // get Idq from estimator to avoid sin and cos, and a Park transform, // which saves CPU cycles EST_get. Idq_pu(est. Handle, &g. Idq_pu); // run the appropriate controller if(g. Motor. Vars. Flag_Run_Identify) { // Declaration of local variables. _iq ref. Value; _iq fback. Value; _iq out. Max_pu; // when appropriate, run the PID speed controller // This mechanism provides the decimation for the speed loop. if(pid. Cnt. Speed >= USER_NUM_CTRL_TICKS_PER_SPEED_TICK) { // Reset the Speed PID execution counter. pid. Cnt. Speed = 0; // The next instruction executes the PI speed controller and places // its output in Idq_ref_pu. value[1], which is the input reference // value for the q-axis current controller. PID_run_spd(pid. Handle[0], g. Motor. Vars. Speed. Ref_pu, speed_pu, &(g. Idq_ref_pu. value[1])); } else { // increment counter pid. Cnt. Speed++; } 11 // Get the reference value for the d-axis current controller. ref. Value = g. Idq_ref_pu. value[0]; // Get the actual value of Id fback. Value = g. Idq_pu. value[0]; // The next instruction executes the PI current controller for the // d axis and places its output in Vdq_pu. value[0], which is the // control voltage along the d-axis (Vd) PID_run(pid. Handle[1], ref. Value, fback. Value, &(g. Vdq_out_pu. value[0])); // get the Iq reference value ref. Value = g. Idq_ref_pu. value[1]; // get the actual value of Iq fback. Value = g. Idq_pu. value[1]; // The voltage limits on the output of the q-axis current controller // are dynamic, and are dependent on the output voltage from the d-axis // current controller. In other words, the d-axis current controller // gets first dibs on the available voltage, and the q-axis current // controller gets what's left over. That is why the d-axis current // controller executes first. The next instruction calculates the // maximum limits for this voltage as: // Vq_min_max = +/- sqrt(Vbus^2 - Vd^2) out. Max_pu = _IQsqrt(_IQ(USER_MAX_VS_MAG_PU * USER_MAX_VS_MAG_PU) - _IQmpy(g. Vdq_out_pu. value[0], g. Vdq_out_pu. value[0])); // Set the limits to +/- out. Max_pu PID_set. Min. Max(pid. Handle[2], -out. Max_pu, out. Max_pu); // The next instruction executes the PI current controller for the // q axis and places its output in Vdq_pu. value[1], which is the // control voltage vector along the q-axis (Vq) PID_run(pid. Handle[2], ref. Value, fback. Value, &(g. Vdq_out_pu. value[1 ])); 12

proj_lab 11. c // run the estimator // The speed reference is needed so

proj_lab 11. c // run the estimator // The speed reference is needed so that the proper sign of the forced // angle is calculated. When the estimator does not do motor ID as in this // lab, only the sign of the speed reference is used EST_run(est. Handle, &Iab_pu, &Vab_pu, g. Adc. Data. dc. Bus, g. Motor. Vars. Speed. Ref_pu); // generate the motor electrical angle_pu = EST_get. Angle_pu(est. Handle); speed_pu = EST_get. Fm_pu(est. Handle); // get Idq from estimator to avoid sin and cos, and a Park transform, // which saves CPU cycles EST_get. Idq_pu(est. Handle, &g. Idq_pu); // run the appropriate controller if(g. Motor. Vars. Flag_Run_Identify) { // Declaration of local variables. _iq ref. Value; _iq fback. Value; _iq out. Max_pu; // when appropriate, run the PID speed controller // This mechanism provides the decimation for the speed loop. if(pid. Cnt. Speed >= USER_NUM_CTRL_TICKS_PER_SPEED_TICK) { // Reset the Speed PID execution counter. pid. Cnt. Speed = 0; // The next instruction executes the PI speed controller and places // its output in Idq_ref_pu. value[1], which is the input reference // value for the q-axis current controller. PID_run_spd(pid. Handle[0], g. Motor. Vars. Speed. Ref_pu, speed_pu, &(g. Idq_ref_pu. value[1])); } else { // increment counter pid. Cnt. Speed++; } 11 // Get the reference value for the d-axis current controller. ref. Value = g. Idq_ref_pu. value[0]; // Get the actual value of Id fback. Value = g. Idq_pu. value[0]; // The next instruction executes the PI current controller for the // d axis and places its output in Vdq_pu. value[0], which is the // control voltage along the d-axis (Vd) PID_run(pid. Handle[1], ref. Value, fback. Value, &(g. Vdq_out_pu. value[0])); // get the Iq reference value ref. Value = g. Idq_ref_pu. value[1]; // get the actual value of Iq fback. Value = g. Idq_pu. value[1]; // The voltage limits on the output of the q-axis current controller // are dynamic, and are dependent on the output voltage from the d-axis // current controller. In other words, the d-axis current controller // gets first dibs on the available voltage, and the q-axis current // controller gets what's left over. That is why the d-axis current // controller executes first. The next instruction calculates the // maximum limits for this voltage as: // Vq_min_max = +/- sqrt(Vbus^2 - Vd^2) out. Max_pu = _IQsqrt(_IQ(USER_MAX_VS_MAG_PU * USER_MAX_VS_MAG_PU) - _IQmpy(g. Vdq_out_pu. value[0], g. Vdq_out_pu. value[0])); // Set the limits to +/- out. Max_pu PID_set. Min. Max(pid. Handle[2], -out. Max_pu, out. Max_pu); // The next instruction executes the PI current controller for the // q axis and places its output in Vdq_pu. value[1], which is the // control voltage vector along the q-axis (Vq) PID_run(pid. Handle[2], ref. Value, fback. Value, &(g. Vdq_out_pu. value[1 ])); 12

proj_lab 11. c // run the estimator // The speed reference is needed so

proj_lab 11. c // run the estimator // The speed reference is needed so that the proper sign of the forced // angle is calculated. When the estimator does not do motor ID as in this // lab, only the sign of the speed reference is used EST_run(est. Handle, &Iab_pu, &Vab_pu, g. Adc. Data. dc. Bus, g. Motor. Vars. Speed. Ref_pu); // generate the motor electrical angle_pu = EST_get. Angle_pu(est. Handle); speed_pu = EST_get. Fm_pu(est. Handle); // get Idq from estimator to avoid sin and cos, and a Park transform, // which saves CPU cycles EST_get. Idq_pu(est. Handle, &g. Idq_pu); // run the appropriate controller if(g. Motor. Vars. Flag_Run_Identify) { // Declaration of local variables. _iq ref. Value; _iq fback. Value; _iq out. Max_pu; // when appropriate, run the PID speed controller // This mechanism provides the decimation for the speed loop. if(pid. Cnt. Speed >= USER_NUM_CTRL_TICKS_PER_SPEED_TICK) { // Reset the Speed PID execution counter. pid. Cnt. Speed = 0; // The next instruction executes the PI speed controller and places // its output in Idq_ref_pu. value[1], which is the input reference // value for the q-axis current controller. PID_run_spd(pid. Handle[0], g. Motor. Vars. Speed. Ref_pu, speed_pu, &(g. Idq_ref_pu. value[1])); } else { // increment counter pid. Cnt. Speed++; } 11 // Get the reference value for the d-axis current controller. ref. Value = g. Idq_ref_pu. value[0]; // Get the actual value of Id fback. Value = g. Idq_pu. value[0]; // The next instruction executes the PI current controller for the // d axis and places its output in Vdq_pu. value[0], which is the // control voltage along the d-axis (Vd) PID_run(pid. Handle[1], ref. Value, fback. Value, &(g. Vdq_out_pu. value[0])); // get the Iq reference value ref. Value = g. Idq_ref_pu. value[1]; // get the actual value of Iq fback. Value = g. Idq_pu. value[1]; // The voltage limits on the output of the q-axis current controller // are dynamic, and are dependent on the output voltage from the d-axis // current controller. In other words, the d-axis current controller // gets first dibs on the available voltage, and the q-axis current // controller gets what's left over. That is why the d-axis current // controller executes first. The next instruction calculates the // maximum limits for this voltage as: // Vq_min_max = +/- sqrt(Vbus^2 - Vd^2) out. Max_pu = _IQsqrt(_IQ(USER_MAX_VS_MAG_PU * USER_MAX_VS_MAG_PU) - _IQmpy(g. Vdq_out_pu. value[0], g. Vdq_out_pu. value[0])); // Set the limits to +/- out. Max_pu PID_set. Min. Max(pid. Handle[2], -out. Max_pu, out. Max_pu); // The next instruction executes the PI current controller for the // q axis and places its output in Vdq_pu. value[1], which is the // control voltage vector along the q-axis (Vq) PID_run(pid. Handle[2], ref. Value, fback. Value, &(g. Vdq_out_pu. value[1 ])); 12

proj_lab 11. c // The voltage vector is now calculated and ready to be

proj_lab 11. c // The voltage vector is now calculated and ready to be applied to the // motor in the form of three PWM signals. However, even though the // voltages may be supplied to the PWM module now, they won't be // applied to the motor until the next PWM cycle. By this point, the // motor will have moved away from the angle that the voltage vector // was calculated for, by an amount which is proportional to the // sampling frequency and the speed of the motor. For steady-state // speeds, we can calculate this angle delay and compensate for it. angle_pu = angle. Delay. Comp(speed_pu, angle_pu); // compute the sin/cos phasor since the phase is used by the inverse // Park transform to transform from DQ to Alpha/Beta. The phasor // calculation is not part of the IPARK module. The way it works is, // the phasor is calculated by doing a sine and cosine, then the phasor // is copied into the IPARK module, and then finally the IPARK module // uses the phasor to calculate from DQ to Alpha/Beta phasor. value[0] = _IQcos. PU(angle_pu); phasor. value[1] = _IQsin. PU(angle_pu); // set the phasor in the inverse Park transform IPARK_set. Phasor(ipark. Handle, &phasor); // Run the inverse Park module. This converts the voltage vector from // synchronous frame values to stationary frame values. IPARK_run(ipark. Handle, &g. Vdq_out_pu, &Vab_pu); // This code compensates for variations in the DC bus by adjusting the // PWM duty cycle to compensate for the variations. the goal is to // produce the desired volt-second product regardless of the DC bus // value. To do this, we must divide the desired voltage values by // the DC bus value. Or. . . it is easier to multiply by 1/(DC bus value) one. Over. Dc. Bus = EST_get. One. Over. Dc. Bus_pu(est. Handle); Vab_pu. value[0] = _IQmpy(Vab_pu. value[0], one. Over. Dc. Bus); Vab_pu. value[1] = _IQmpy(Vab_pu. value[1], one. Over. Dc. Bus); // Now run the space vector generator (SVGEN) module. // There is no need to do an inverse CLARKE transform, as this is // handled in the SVGEN_run function. SVGEN_run(svgen. Handle, &Vab_pu, &(g. Pwm. Data. Tabc)); } 13 else // g. Motor. Vars. Flag_Run_Identify = 0 { // disable the PWM HAL_disable. Pwm(hal. Handle); // Set the PWMs to 50% duty cycle g. Pwm. Data. Tabc. value[0] = _IQ(0. 0); g. Pwm. Data. Tabc. value[1] = _IQ(0. 0); g. Pwm. Data. Tabc. value[2] = _IQ(0. 0); } // write to the PWM compare registers, and then we are done! HAL_write. Pwm. Data(hal. Handle, &g. Pwm. Data); return; } // end of main. ISR() function 14

proj_lab 11. c // run the estimator // The speed reference is needed so

proj_lab 11. c // run the estimator // The speed reference is needed so that the proper sign of the forced // angle is calculated. When the estimator does not do motor ID as in this // lab, only the sign of the speed reference is used EST_run(est. Handle, &Iab_pu, &Vab_pu, g. Adc. Data. dc. Bus, g. Motor. Vars. Speed. Ref_pu); // generate the motor electrical angle_pu = EST_get. Angle_pu(est. Handle); speed_pu = EST_get. Fm_pu(est. Handle); // get Idq from estimator to avoid sin and cos, and a Park transform, // which saves CPU cycles EST_get. Idq_pu(est. Handle, &g. Idq_pu); // run the appropriate controller if(g. Motor. Vars. Flag_Run_Identify) { // Declaration of local variables. _iq ref. Value; _iq fback. Value; _iq out. Max_pu; // when appropriate, run the PID speed controller // This mechanism provides the decimation for the speed loop. if(pid. Cnt. Speed >= USER_NUM_CTRL_TICKS_PER_SPEED_TICK) { // Reset the Speed PID execution counter. pid. Cnt. Speed = 0; // The next instruction executes the PI speed controller and places // its output in Idq_ref_pu. value[1], which is the input reference // value for the q-axis current controller. PID_run_spd(pid. Handle[0], g. Motor. Vars. Speed. Ref_pu, speed_pu, &(g. Idq_ref_pu. value[1])); } else { // increment counter pid. Cnt. Speed++; } 11 // Get the reference value for the d-axis current controller. ref. Value = g. Idq_ref_pu. value[0]; // Get the actual value of Id fback. Value = g. Idq_pu. value[0]; // The next instruction executes the PI current controller for the // d axis and places its output in Vdq_pu. value[0], which is the // control voltage along the d-axis (Vd) PID_run(pid. Handle[1], ref. Value, fback. Value, &(g. Vdq_out_pu. value[0])); // get the Iq reference value ref. Value = g. Idq_ref_pu. value[1]; // get the actual value of Iq fback. Value = g. Idq_pu. value[1]; // The voltage limits on the output of the q-axis current controller // are dynamic, and are dependent on the output voltage from the d-axis // current controller. In other words, the d-axis current controller // gets first dibs on the available voltage, and the q-axis current // controller gets what's left over. That is why the d-axis current // controller executes first. The next instruction calculates the // maximum limits for this voltage as: // Vq_min_max = +/- sqrt(Vbus^2 - Vd^2) out. Max_pu = _IQsqrt(_IQ(USER_MAX_VS_MAG_PU * USER_MAX_VS_MAG_PU) - _IQmpy(g. Vdq_out_pu. value[0], g. Vdq_out_pu. value[0])); // Set the limits to +/- out. Max_pu PID_set. Min. Max(pid. Handle[2], -out. Max_pu, out. Max_pu); // The next instruction executes the PI current controller for the // q axis and places its output in Vdq_pu. value[1], which is the // control voltage vector along the q-axis (Vq) PID_run(pid. Handle[2], ref. Value, fback. Value, &(g. Vdq_out_pu. value[1 ])); 12

proj_lab 11. c // run the estimator // The speed reference is needed so

proj_lab 11. c // run the estimator // The speed reference is needed so that the proper sign of the forced // angle is calculated. When the estimator does not do motor ID as in this // lab, only the sign of the speed reference is used EST_run(est. Handle, &Iab_pu, &Vab_pu, g. Adc. Data. dc. Bus, g. Motor. Vars. Speed. Ref_pu); // generate the motor electrical angle_pu = EST_get. Angle_pu(est. Handle); speed_pu = EST_get. Fm_pu(est. Handle); // get Idq from estimator to avoid sin and cos, and a Park transform, // which saves CPU cycles EST_get. Idq_pu(est. Handle, &g. Idq_pu); // run the appropriate controller if(g. Motor. Vars. Flag_Run_Identify) { // Declaration of local variables. _iq ref. Value; _iq fback. Value; _iq out. Max_pu; // when appropriate, run the PID speed controller // This mechanism provides the decimation for the speed loop. if(pid. Cnt. Speed >= USER_NUM_CTRL_TICKS_PER_SPEED_TICK) { // Reset the Speed PID execution counter. pid. Cnt. Speed = 0; // The next instruction executes the PI speed controller and places // its output in Idq_ref_pu. value[1], which is the input reference // value for the q-axis current controller. PID_run_spd(pid. Handle[0], g. Motor. Vars. Speed. Ref_pu, speed_pu, &(g. Idq_ref_pu. value[1])); } else { // increment counter pid. Cnt. Speed++; } 11 // Get the reference value for the d-axis current controller. ref. Value = g. Idq_ref_pu. value[0]; // Get the actual value of Id fback. Value = g. Idq_pu. value[0]; // The next instruction executes the PI current controller for the // d axis and places its output in Vdq_pu. value[0], which is the // control voltage along the d-axis (Vd) PID_run(pid. Handle[1], ref. Value, fback. Value, &(g. Vdq_out_pu. value[0])); // get the Iq reference value ref. Value = g. Idq_ref_pu. value[1]; // get the actual value of Iq fback. Value = g. Idq_pu. value[1]; // The voltage limits on the output of the q-axis current controller // are dynamic, and are dependent on the output voltage from the d-axis // current controller. In other words, the d-axis current controller // gets first dibs on the available voltage, and the q-axis current // controller gets what's left over. That is why the d-axis current // controller executes first. The next instruction calculates the // maximum limits for this voltage as: // Vq_min_max = +/- sqrt(Vbus^2 - Vd^2) out. Max_pu = _IQsqrt(_IQ(USER_MAX_VS_MAG_PU * USER_MAX_VS_MAG_PU) - _IQmpy(g. Vdq_out_pu. value[0], g. Vdq_out_pu. value[0])); // Set the limits to +/- out. Max_pu PID_set. Min. Max(pid. Handle[2], -out. Max_pu, out. Max_pu); // The next instruction executes the PI current controller for the // q axis and places its output in Vdq_pu. value[1], which is the // control voltage vector along the q-axis (Vq) PID_run(pid. Handle[2], ref. Value, fback. Value, &(g. Vdq_out_pu. value[1 ])); 12

proj_lab 11. c // The voltage vector is now calculated and ready to be

proj_lab 11. c // The voltage vector is now calculated and ready to be applied to the // motor in the form of three PWM signals. However, even though the // voltages may be supplied to the PWM module now, they won't be // applied to the motor until the next PWM cycle. By this point, the // motor will have moved away from the angle that the voltage vector // was calculated for, by an amount which is proportional to the // sampling frequency and the speed of the motor. For steady-state // speeds, we can calculate this angle delay and compensate for it. angle_pu = angle. Delay. Comp(speed_pu, angle_pu); // compute the sin/cos phasor since the phase is used by the inverse // Park transform to transform from DQ to Alpha/Beta. The phasor // calculation is not part of the IPARK module. The way it works is, // the phasor is calculated by doing a sine and cosine, then the phasor // is copied into the IPARK module, and then finally the IPARK module // uses the phasor to calculate from DQ to Alpha/Beta phasor. value[0] = _IQcos. PU(angle_pu); phasor. value[1] = _IQsin. PU(angle_pu); // set the phasor in the inverse Park transform IPARK_set. Phasor(ipark. Handle, &phasor); // Run the inverse Park module. This converts the voltage vector from // synchronous frame values to stationary frame values. IPARK_run(ipark. Handle, &g. Vdq_out_pu, &Vab_pu); // This code compensates for variations in the DC bus by adjusting the // PWM duty cycle to compensate for the variations. the goal is to // produce the desired volt-second product regardless of the DC bus // value. To do this, we must divide the desired voltage values by // the DC bus value. Or. . . it is easier to multiply by 1/(DC bus value) one. Over. Dc. Bus = EST_get. One. Over. Dc. Bus_pu(est. Handle); Vab_pu. value[0] = _IQmpy(Vab_pu. value[0], one. Over. Dc. Bus); Vab_pu. value[1] = _IQmpy(Vab_pu. value[1], one. Over. Dc. Bus); // Now run the space vector generator (SVGEN) module. // There is no need to do an inverse CLARKE transform, as this is // handled in the SVGEN_run function. SVGEN_run(svgen. Handle, &Vab_pu, &(g. Pwm. Data. Tabc)); } 13 else // g. Motor. Vars. Flag_Run_Identify = 0 { // disable the PWM HAL_disable. Pwm(hal. Handle); // Set the PWMs to 50% duty cycle g. Pwm. Data. Tabc. value[0] = _IQ(0. 0); g. Pwm. Data. Tabc. value[1] = _IQ(0. 0); g. Pwm. Data. Tabc. value[2] = _IQ(0. 0); } // write to the PWM compare registers, and then we are done! HAL_write. Pwm. Data(hal. Handle, &g. Pwm. Data); return; } // end of main. ISR() function 14

proj_lab 11. c // The voltage vector is now calculated and ready to be

proj_lab 11. c // The voltage vector is now calculated and ready to be applied to the // motor in the form of three PWM signals. However, even though the // voltages may be supplied to the PWM module now, they won't be // applied to the motor until the next PWM cycle. By this point, the // motor will have moved away from the angle that the voltage vector // was calculated for, by an amount which is proportional to the // sampling frequency and the speed of the motor. For steady-state // speeds, we can calculate this angle delay and compensate for it. angle_pu = angle. Delay. Comp(speed_pu, angle_pu); // compute the sin/cos phasor since the phase is used by the inverse // Park transform to transform from DQ to Alpha/Beta. The phasor // calculation is not part of the IPARK module. The way it works is, // the phasor is calculated by doing a sine and cosine, then the phasor // is copied into the IPARK module, and then finally the IPARK module // uses the phasor to calculate from DQ to Alpha/Beta phasor. value[0] = _IQcos. PU(angle_pu); phasor. value[1] = _IQsin. PU(angle_pu); // set the phasor in the inverse Park transform IPARK_set. Phasor(ipark. Handle, &phasor); // Run the inverse Park module. This converts the voltage vector from // synchronous frame values to stationary frame values. IPARK_run(ipark. Handle, &g. Vdq_out_pu, &Vab_pu); // This code compensates for variations in the DC bus by adjusting the // PWM duty cycle to compensate for the variations. the goal is to // produce the desired volt-second product regardless of the DC bus // value. To do this, we must divide the desired voltage values by // the DC bus value. Or. . . it is easier to multiply by 1/(DC bus value) one. Over. Dc. Bus = EST_get. One. Over. Dc. Bus_pu(est. Handle); Vab_pu. value[0] = _IQmpy(Vab_pu. value[0], one. Over. Dc. Bus); Vab_pu. value[1] = _IQmpy(Vab_pu. value[1], one. Over. Dc. Bus); // Now run the space vector generator (SVGEN) module. // There is no need to do an inverse CLARKE transform, as this is // handled in the SVGEN_run function. SVGEN_run(svgen. Handle, &Vab_pu, &(g. Pwm. Data. Tabc)); } 13 else // g. Motor. Vars. Flag_Run_Identify = 0 { // disable the PWM HAL_disable. Pwm(hal. Handle); // Set the PWMs to 50% duty cycle g. Pwm. Data. Tabc. value[0] = _IQ(0. 0); g. Pwm. Data. Tabc. value[1] = _IQ(0. 0); g. Pwm. Data. Tabc. value[2] = _IQ(0. 0); } // write to the PWM compare registers, and then we are done! HAL_write. Pwm. Data(hal. Handle, &g. Pwm. Data); return; } // end of main. ISR() function 14

proj_lab 11. c // The voltage vector is now calculated and ready to be

proj_lab 11. c // The voltage vector is now calculated and ready to be applied to the // motor in the form of three PWM signals. However, even though the // voltages may be supplied to the PWM module now, they won't be // applied to the motor until the next PWM cycle. By this point, the // motor will have moved away from the angle that the voltage vector // was calculated for, by an amount which is proportional to the // sampling frequency and the speed of the motor. For steady-state // speeds, we can calculate this angle delay and compensate for it. angle_pu = angle. Delay. Comp(speed_pu, angle_pu); // compute the sin/cos phasor since the phase is used by the inverse // Park transform to transform from DQ to Alpha/Beta. The phasor // calculation is not part of the IPARK module. The way it works is, // the phasor is calculated by doing a sine and cosine, then the phasor // is copied into the IPARK module, and then finally the IPARK module // uses the phasor to calculate from DQ to Alpha/Beta phasor. value[0] = _IQcos. PU(angle_pu); phasor. value[1] = _IQsin. PU(angle_pu); // set the phasor in the inverse Park transform IPARK_set. Phasor(ipark. Handle, &phasor); // Run the inverse Park module. This converts the voltage vector from // synchronous frame values to stationary frame values. IPARK_run(ipark. Handle, &g. Vdq_out_pu, &Vab_pu); // This code compensates for variations in the DC bus by adjusting the // PWM duty cycle to compensate for the variations. the goal is to // produce the desired volt-second product regardless of the DC bus // value. To do this, we must divide the desired voltage values by // the DC bus value. Or. . . it is easier to multiply by 1/(DC bus value) one. Over. Dc. Bus = EST_get. One. Over. Dc. Bus_pu(est. Handle); Vab_pu. value[0] = _IQmpy(Vab_pu. value[0], one. Over. Dc. Bus); Vab_pu. value[1] = _IQmpy(Vab_pu. value[1], one. Over. Dc. Bus); // Now run the space vector generator (SVGEN) module. // There is no need to do an inverse CLARKE transform, as this is // handled in the SVGEN_run function. SVGEN_run(svgen. Handle, &Vab_pu, &(g. Pwm. Data. Tabc)); } 13 else // g. Motor. Vars. Flag_Run_Identify = 0 { // disable the PWM HAL_disable. Pwm(hal. Handle); // Set the PWMs to 50% duty cycle g. Pwm. Data. Tabc. value[0] = _IQ(0. 0); g. Pwm. Data. Tabc. value[1] = _IQ(0. 0); g. Pwm. Data. Tabc. value[2] = _IQ(0. 0); } // write to the PWM compare registers, and then we are done! HAL_write. Pwm. Data(hal. Handle, &g. Pwm. Data); return; } // end of main. ISR() function 14

proj_lab 11. c // The voltage vector is now calculated and ready to be

proj_lab 11. c // The voltage vector is now calculated and ready to be applied to the // motor in the form of three PWM signals. However, even though the // voltages may be supplied to the PWM module now, they won't be // applied to the motor until the next PWM cycle. By this point, the // motor will have moved away from the angle that the voltage vector // was calculated for, by an amount which is proportional to the // sampling frequency and the speed of the motor. For steady-state // speeds, we can calculate this angle delay and compensate for it. angle_pu = angle. Delay. Comp(speed_pu, angle_pu); // compute the sin/cos phasor since the phase is used by the inverse // Park transform to transform from DQ to Alpha/Beta. The phasor // calculation is not part of the IPARK module. The way it works is, // the phasor is calculated by doing a sine and cosine, then the phasor // is copied into the IPARK module, and then finally the IPARK module // uses the phasor to calculate from DQ to Alpha/Beta phasor. value[0] = _IQcos. PU(angle_pu); phasor. value[1] = _IQsin. PU(angle_pu); // set the phasor in the inverse Park transform IPARK_set. Phasor(ipark. Handle, &phasor); // Run the inverse Park module. This converts the voltage vector from // synchronous frame values to stationary frame values. IPARK_run(ipark. Handle, &g. Vdq_out_pu, &Vab_pu); // This code compensates for variations in the DC bus by adjusting the // PWM duty cycle to compensate for the variations. the goal is to // produce the desired volt-second product regardless of the DC bus // value. To do this, we must divide the desired voltage values by // the DC bus value. Or. . . it is easier to multiply by 1/(DC bus value) one. Over. Dc. Bus = EST_get. One. Over. Dc. Bus_pu(est. Handle); Vab_pu. value[0] = _IQmpy(Vab_pu. value[0], one. Over. Dc. Bus); Vab_pu. value[1] = _IQmpy(Vab_pu. value[1], one. Over. Dc. Bus); // Now run the space vector generator (SVGEN) module. // There is no need to do an inverse CLARKE transform, as this is // handled in the SVGEN_run function. SVGEN_run(svgen. Handle, &Vab_pu, &(g. Pwm. Data. Tabc)); } 13 else // g. Motor. Vars. Flag_Run_Identify = 0 { // disable the PWM HAL_disable. Pwm(hal. Handle); // Set the PWMs to 50% duty cycle g. Pwm. Data. Tabc. value[0] = _IQ(0. 0); g. Pwm. Data. Tabc. value[1] = _IQ(0. 0); g. Pwm. Data. Tabc. value[2] = _IQ(0. 0); } // write to the PWM compare registers, and then we are done! HAL_write. Pwm. Data(hal. Handle, &g. Pwm. Data); return; } // end of main. ISR() function 14

proj_lab 11. c // The voltage vector is now calculated and ready to be

proj_lab 11. c // The voltage vector is now calculated and ready to be applied to the // motor in the form of three PWM signals. However, even though the // voltages may be supplied to the PWM module now, they won't be // applied to the motor until the next PWM cycle. By this point, the // motor will have moved away from the angle that the voltage vector // was calculated for, by an amount which is proportional to the // sampling frequency and the speed of the motor. For steady-state // speeds, we can calculate this angle delay and compensate for it. angle_pu = angle. Delay. Comp(speed_pu, angle_pu); // compute the sin/cos phasor since the phase is used by the inverse // Park transform to transform from DQ to Alpha/Beta. The phasor // calculation is not part of the IPARK module. The way it works is, // the phasor is calculated by doing a sine and cosine, then the phasor // is copied into the IPARK module, and then finally the IPARK module // uses the phasor to calculate from DQ to Alpha/Beta phasor. value[0] = _IQcos. PU(angle_pu); phasor. value[1] = _IQsin. PU(angle_pu); // set the phasor in the inverse Park transform IPARK_set. Phasor(ipark. Handle, &phasor); // Run the inverse Park module. This converts the voltage vector from // synchronous frame values to stationary frame values. IPARK_run(ipark. Handle, &g. Vdq_out_pu, &Vab_pu); // This code compensates for variations in the DC bus by adjusting the // PWM duty cycle to compensate for the variations. the goal is to // produce the desired volt-second product regardless of the DC bus // value. To do this, we must divide the desired voltage values by // the DC bus value. Or. . . it is easier to multiply by 1/(DC bus value) one. Over. Dc. Bus = EST_get. One. Over. Dc. Bus_pu(est. Handle); Vab_pu. value[0] = _IQmpy(Vab_pu. value[0], one. Over. Dc. Bus); Vab_pu. value[1] = _IQmpy(Vab_pu. value[1], one. Over. Dc. Bus); // Now run the space vector generator (SVGEN) module. // There is no need to do an inverse CLARKE transform, as this is // handled in the SVGEN_run function. SVGEN_run(svgen. Handle, &Vab_pu, &(g. Pwm. Data. Tabc)); } 13 else // g. Motor. Vars. Flag_Run_Identify = 0 { // disable the PWM HAL_disable. Pwm(hal. Handle); // Set the PWMs to 50% duty cycle g. Pwm. Data. Tabc. value[0] = _IQ(0. 0); g. Pwm. Data. Tabc. value[1] = _IQ(0. 0); g. Pwm. Data. Tabc. value[2] = _IQ(0. 0); } // write to the PWM compare registers, and then we are done! HAL_write. Pwm. Data(hal. Handle, &g. Pwm. Data); return; } // end of main. ISR() function 14

proj_lab 11. c // The voltage vector is now calculated and ready to be

proj_lab 11. c // The voltage vector is now calculated and ready to be applied to the // motor in the form of three PWM signals. However, even though the // voltages may be supplied to the PWM module now, they won't be // applied to the motor until the next PWM cycle. By this point, the // motor will have moved away from the angle that the voltage vector // was calculated for, by an amount which is proportional to the // sampling frequency and the speed of the motor. For steady-state // speeds, we can calculate this angle delay and compensate for it. angle_pu = angle. Delay. Comp(speed_pu, angle_pu); // compute the sin/cos phasor since the phase is used by the inverse // Park transform to transform from DQ to Alpha/Beta. The phasor // calculation is not part of the IPARK module. The way it works is, // the phasor is calculated by doing a sine and cosine, then the phasor // is copied into the IPARK module, and then finally the IPARK module // uses the phasor to calculate from DQ to Alpha/Beta phasor. value[0] = _IQcos. PU(angle_pu); phasor. value[1] = _IQsin. PU(angle_pu); // set the phasor in the inverse Park transform IPARK_set. Phasor(ipark. Handle, &phasor); // Run the inverse Park module. This converts the voltage vector from // synchronous frame values to stationary frame values. IPARK_run(ipark. Handle, &g. Vdq_out_pu, &Vab_pu); // This code compensates for variations in the DC bus by adjusting the // PWM duty cycle to compensate for the variations. the goal is to // produce the desired volt-second product regardless of the DC bus // value. To do this, we must divide the desired voltage values by // the DC bus value. Or. . . it is easier to multiply by 1/(DC bus value) one. Over. Dc. Bus = EST_get. One. Over. Dc. Bus_pu(est. Handle); Vab_pu. value[0] = _IQmpy(Vab_pu. value[0], one. Over. Dc. Bus); Vab_pu. value[1] = _IQmpy(Vab_pu. value[1], one. Over. Dc. Bus); // Now run the space vector generator (SVGEN) module. // There is no need to do an inverse CLARKE transform, as this is // handled in the SVGEN_run function. SVGEN_run(svgen. Handle, &Vab_pu, &(g. Pwm. Data. Tabc)); } 13 else // g. Motor. Vars. Flag_Run_Identify = 0 { // disable the PWM HAL_disable. Pwm(hal. Handle); // Set the PWMs to 50% duty cycle g. Pwm. Data. Tabc. value[0] = _IQ(0. 0); g. Pwm. Data. Tabc. value[1] = _IQ(0. 0); g. Pwm. Data. Tabc. value[2] = _IQ(0. 0); } // write to the PWM compare registers, and then we are done! HAL_write. Pwm. Data(hal. Handle, &g. Pwm. Data); return; } // end of main. ISR() function 14

proj_lab 11. c // The voltage vector is now calculated and ready to be

proj_lab 11. c // The voltage vector is now calculated and ready to be applied to the // motor in the form of three PWM signals. However, even though the // voltages may be supplied to the PWM module now, they won't be // applied to the motor until the next PWM cycle. By this point, the // motor will have moved away from the angle that the voltage vector // was calculated for, by an amount which is proportional to the // sampling frequency and the speed of the motor. For steady-state // speeds, we can calculate this angle delay and compensate for it. angle_pu = angle. Delay. Comp(speed_pu, angle_pu); // compute the sin/cos phasor since the phase is used by the inverse // Park transform to transform from DQ to Alpha/Beta. The phasor // calculation is not part of the IPARK module. The way it works is, // the phasor is calculated by doing a sine and cosine, then the phasor // is copied into the IPARK module, and then finally the IPARK module // uses the phasor to calculate from DQ to Alpha/Beta phasor. value[0] = _IQcos. PU(angle_pu); phasor. value[1] = _IQsin. PU(angle_pu); // set the phasor in the inverse Park transform IPARK_set. Phasor(ipark. Handle, &phasor); // Run the inverse Park module. This converts the voltage vector from // synchronous frame values to stationary frame values. IPARK_run(ipark. Handle, &g. Vdq_out_pu, &Vab_pu); // This code compensates for variations in the DC bus by adjusting the // PWM duty cycle to compensate for the variations. the goal is to // produce the desired volt-second product regardless of the DC bus // value. To do this, we must divide the desired voltage values by // the DC bus value. Or. . . it is easier to multiply by 1/(DC bus value) one. Over. Dc. Bus = EST_get. One. Over. Dc. Bus_pu(est. Handle); Vab_pu. value[0] = _IQmpy(Vab_pu. value[0], one. Over. Dc. Bus); Vab_pu. value[1] = _IQmpy(Vab_pu. value[1], one. Over. Dc. Bus); // Now run the space vector generator (SVGEN) module. // There is no need to do an inverse CLARKE transform, as this is // handled in the SVGEN_run function. SVGEN_run(svgen. Handle, &Vab_pu, &(g. Pwm. Data. Tabc)); } 13 else // g. Motor. Vars. Flag_Run_Identify = 0 { // disable the PWM HAL_disable. Pwm(hal. Handle); // Set the PWMs to 50% duty cycle g. Pwm. Data. Tabc. value[0] = _IQ(0. 0); g. Pwm. Data. Tabc. value[1] = _IQ(0. 0); g. Pwm. Data. Tabc. value[2] = _IQ(0. 0); } // write to the PWM compare registers, and then we are done! HAL_write. Pwm. Data(hal. Handle, &g. Pwm. Data); return; } // end of main. ISR() function 14

proj_lab 11. c // run the estimator // The speed reference is needed so

proj_lab 11. c // run the estimator // The speed reference is needed so that the proper sign of the forced // angle is calculated. When the estimator does not do motor ID as in this // lab, only the sign of the speed reference is used EST_run(est. Handle, &Iab_pu, &Vab_pu, g. Adc. Data. dc. Bus, g. Motor. Vars. Speed. Ref_pu); // generate the motor electrical angle_pu = EST_get. Angle_pu(est. Handle); speed_pu = EST_get. Fm_pu(est. Handle); // get Idq from estimator to avoid sin and cos, and a Park transform, // which saves CPU cycles EST_get. Idq_pu(est. Handle, &g. Idq_pu); // run the appropriate controller if(g. Motor. Vars. Flag_Run_Identify) { // Declaration of local variables. _iq ref. Value; _iq fback. Value; _iq out. Max_pu; // when appropriate, run the PID speed controller // This mechanism provides the decimation for the speed loop. if(pid. Cnt. Speed >= USER_NUM_CTRL_TICKS_PER_SPEED_TICK) { // Reset the Speed PID execution counter. pid. Cnt. Speed = 0; // The next instruction executes the PI speed controller and places // its output in Idq_ref_pu. value[1], which is the input reference // value for the q-axis current controller. PID_run_spd(pid. Handle[0], g. Motor. Vars. Speed. Ref_pu, speed_pu, &(g. Idq_ref_pu. value[1])); } else { // increment counter pid. Cnt. Speed++; } 11 // Get the reference value for the d-axis current controller. ref. Value = g. Idq_ref_pu. value[0]; // Get the actual value of Id fback. Value = g. Idq_pu. value[0]; // The next instruction executes the PI current controller for the // d axis and places its output in Vdq_pu. value[0], which is the // control voltage along the d-axis (Vd) PID_run(pid. Handle[1], ref. Value, fback. Value, &(g. Vdq_out_pu. value[0])); // get the Iq reference value ref. Value = g. Idq_ref_pu. value[1]; // get the actual value of Iq fback. Value = g. Idq_pu. value[1]; // The voltage limits on the output of the q-axis current controller // are dynamic, and are dependent on the output voltage from the d-axis // current controller. In other words, the d-axis current controller // gets first dibs on the available voltage, and the q-axis current // controller gets what's left over. That is why the d-axis current // controller executes first. The next instruction calculates the // maximum limits for this voltage as: // Vq_min_max = +/- sqrt(Vbus^2 - Vd^2) out. Max_pu = _IQsqrt(_IQ(USER_MAX_VS_MAG_PU * USER_MAX_VS_MAG_PU) - _IQmpy(g. Vdq_out_pu. value[0], g. Vdq_out_pu. value[0])); // Set the limits to +/- out. Max_pu PID_set. Min. Max(pid. Handle[2], -out. Max_pu, out. Max_pu); // The next instruction executes the PI current controller for the // q axis and places its output in Vdq_pu. value[1], which is the // control voltage vector along the q-axis (Vq) PID_run(pid. Handle[2], ref. Value, fback. Value, &(g. Vdq_out_pu. value[1 ])); 12

proj_lab 11. c // run the estimator // The speed reference is needed so

proj_lab 11. c // run the estimator // The speed reference is needed so that the proper sign of the forced // angle is calculated. When the estimator does not do motor ID as in this // lab, only the sign of the speed reference is used EST_run(est. Handle, &Iab_pu, &Vab_pu, g. Adc. Data. dc. Bus, g. Motor. Vars. Speed. Ref_pu); // generate the motor electrical angle_pu = EST_get. Angle_pu(est. Handle); speed_pu = EST_get. Fm_pu(est. Handle); // get Idq from estimator to avoid sin and cos, and a Park transform, // which saves CPU cycles EST_get. Idq_pu(est. Handle, &g. Idq_pu); // run the appropriate controller if(g. Motor. Vars. Flag_Run_Identify) { // Declaration of local variables. _iq ref. Value; _iq fback. Value; _iq out. Max_pu; // when appropriate, run the PID speed controller // This mechanism provides the decimation for the speed loop. if(pid. Cnt. Speed >= USER_NUM_CTRL_TICKS_PER_SPEED_TICK) { // Reset the Speed PID execution counter. pid. Cnt. Speed = 0; // The next instruction executes the PI speed controller and places // its output in Idq_ref_pu. value[1], which is the input reference // value for the q-axis current controller. PID_run_spd(pid. Handle[0], g. Motor. Vars. Speed. Ref_pu, speed_pu, &(g. Idq_ref_pu. value[1])); } else { // increment counter pid. Cnt. Speed++; } 11 // Get the reference value for the d-axis current controller. ref. Value = g. Idq_ref_pu. value[0]; // Get the actual value of Id fback. Value = g. Idq_pu. value[0]; // The next instruction executes the PI current controller for the // d axis and places its output in Vdq_pu. value[0], which is the // control voltage along the d-axis (Vd) PID_run(pid. Handle[1], ref. Value, fback. Value, &(g. Vdq_out_pu. value[0])); // get the Iq reference value ref. Value = g. Idq_ref_pu. value[1]; // get the actual value of Iq fback. Value = g. Idq_pu. value[1]; // The voltage limits on the output of the q-axis current controller // are dynamic, and are dependent on the output voltage from the d-axis // current controller. In other words, the d-axis current controller // gets first dibs on the available voltage, and the q-axis current // controller gets what's left over. That is why the d-axis current // controller executes first. The next instruction calculates the // maximum limits for this voltage as: // Vq_min_max = +/- sqrt(Vbus^2 - Vd^2) out. Max_pu = _IQsqrt(_IQ(USER_MAX_VS_MAG_PU * USER_MAX_VS_MAG_PU) - _IQmpy(g. Vdq_out_pu. value[0], g. Vdq_out_pu. value[0])); // Set the limits to +/- out. Max_pu PID_set. Min. Max(pid. Handle[2], -out. Max_pu, out. Max_pu); // The next instruction executes the PI current controller for the // q axis and places its output in Vdq_pu. value[1], which is the // control voltage vector along the q-axis (Vq) PID_run(pid. Handle[2], ref. Value, fback. Value, &(g. Vdq_out_pu. value[1 ])); 12

 //! brief Runs the PID controller //! param[in] handle The PID controller handle

//! brief Runs the PID controller //! param[in] handle The PID controller handle //! param[in] ref. Value The reference value to the controller //! param[in] fback. Value The feedback value to the controller //! param[in] p. Out. Value The pointer to the controller output value static inline void PID_run(PID_Handle handle, const _iq ref. Value, const _iq fback. Value, _iq *p. Out. Value) { PID_Obj *obj = (PID_Obj *)handle; _iq Error; _iq Up, Ui; Error = ref. Value - fback. Value; Ui = obj->Ui; // load the previous integral output Up = _IQmpy(obj-> Kp, Error); // Compute the proportional output Ui = _IQsat(Ui + _IQmpy(obj-> Ki, Up), obj->out. Max, obj->out. Min); // Compute the integral output obj->Ui = Ui; // store the intetral output obj->ref. Value = ref. Value; obj->fback. Value = fback. Value; *p. Out. Value = _IQsat(Up + Ui, obj-> out. Max, obj->out. Min); // Saturate the output return; } // end of PID_run() function pid. h PID_run() Page 1

 //! brief Runs the PID controller //! param[in] handle The PID controller handle

//! brief Runs the PID controller //! param[in] handle The PID controller handle //! param[in] ref. Value The reference value to the controller //! param[in] fback. Value The feedback value to the controller //! param[in] p. Out. Value The pointer to the controller output value static inline void PID_run(PID_Handle handle, const _iq ref. Value, const _iq fback. Value, _iq *p. Out. Value) { PID_Obj *obj = (PID_Obj *)handle; _iq Error; _iq Up, Ui; Error = ref. Value - fback. Value; Ui = obj->Ui; // load the previous integral output Up = _IQmpy(obj-> Kp, Error); // Compute the proportional output Ui = _IQsat(Ui + _IQmpy(obj-> Ki, Up), obj->out. Max, obj->out. Min); // Compute the integral output obj->Ui = Ui; // store the intetral output obj->ref. Value = ref. Value; obj->fback. Value = fback. Value; *p. Out. Value = _IQsat(Up + Ui, obj-> out. Max, obj->out. Min); // Saturate the output return; } // end of PID_run() function pid. h PID_run() Page 1

 //! brief Runs the PID controller //! param[in] handle The PID controller handle

//! brief Runs the PID controller //! param[in] handle The PID controller handle //! param[in] ref. Value The reference value to the controller //! param[in] fback. Value The feedback value to the controller //! param[in] p. Out. Value The pointer to the controller output value static inline void PID_run(PID_Handle handle, const _iq ref. Value, const _iq fback. Value, _iq *p. Out. Value) { PID_Obj *obj = (PID_Obj *)handle; _iq Error; _iq Up, Ui; Error = ref. Value - fback. Value; Ui = obj->Ui; // load the previous integral output Up = _IQmpy(obj-> Kp, Error); // Compute the proportional output Ui = _IQsat(Ui + _IQmpy(obj-> Ki, Up), obj->out. Max, obj->out. Min); // Compute the integral output obj->Ui = Ui; // store the intetral output obj->ref. Value = ref. Value; obj->fback. Value = fback. Value; *p. Out. Value = _IQsat(Up + Ui, obj-> out. Max, obj->out. Min); // Saturate the output return; } // end of PID_run() function pid. h PID_run() Page 1

 //! brief Runs the PID controller //! param[in] handle The PID controller handle

//! brief Runs the PID controller //! param[in] handle The PID controller handle //! param[in] ref. Value The reference value to the controller //! param[in] fback. Value The feedback value to the controller //! param[in] p. Out. Value The pointer to the controller output value static inline void PID_run(PID_Handle handle, const _iq ref. Value, const _iq fback. Value, _iq *p. Out. Value) { PID_Obj *obj = (PID_Obj *)handle; _iq Error; _iq Up, Ui; Error = ref. Value - fback. Value; Ui = obj->Ui; // load the previous integral output Up = _IQmpy(obj-> Kp, Error); // Compute the proportional output Ui = _IQsat(Ui + _IQmpy(obj-> Ki, Up), obj->out. Max, obj->out. Min); // Compute the integral output obj->Ui = Ui; // store the intetral output obj->ref. Value = ref. Value; obj->fback. Value = fback. Value; *p. Out. Value = _IQsat(Up + Ui, obj-> out. Max, obj->out. Min); // Saturate the output return; } // end of PID_run() function pid. h PID_run() Page 1

 //! brief Runs the PID controller //! param[in] handle The PID controller handle

//! brief Runs the PID controller //! param[in] handle The PID controller handle //! param[in] ref. Value The reference value to the controller //! param[in] fback. Value The feedback value to the controller //! param[in] p. Out. Value The pointer to the controller output value static inline void PID_run(PID_Handle handle, const _iq ref. Value, const _iq fback. Value, _iq *p. Out. Value) { PID_Obj *obj = (PID_Obj *)handle; _iq Error; _iq Up, Ui; Error = ref. Value - fback. Value; Ui = obj->Ui; // load the previous integral output Up = _IQmpy(obj-> Kp, Error); // Compute the proportional output Ui = _IQsat(Ui + _IQmpy(obj-> Ki, Up), obj->out. Max, obj->out. Min); // Compute the integral output obj->Ui = Ui; // store the intetral output obj->ref. Value = ref. Value; obj->fback. Value = fback. Value; *p. Out. Value = _IQsat(Up + Ui, obj-> out. Max, obj->out. Min); // Saturate the output return; } // end of PID_run() function pid. h PID_run() Page 1

 //! brief Runs the PID controller //! param[in] handle The PID controller handle

//! brief Runs the PID controller //! param[in] handle The PID controller handle //! param[in] ref. Value The reference value to the controller //! param[in] fback. Value The feedback value to the controller //! param[in] p. Out. Value The pointer to the controller output value static inline void PID_run(PID_Handle handle, const _iq ref. Value, const _iq fback. Value, _iq *p. Out. Value) { PID_Obj *obj = (PID_Obj *)handle; _iq Error; _iq Up, Ui; Error = ref. Value - fback. Value; Ui = obj->Ui; // load the previous integral output Up = _IQmpy(obj-> Kp, Error); // Compute the proportional output Ui = _IQsat(Ui + _IQmpy(obj-> Ki, Up), obj->out. Max, obj->out. Min); // Compute the integral output obj->Ui = Ui; // store the intetral output obj->ref. Value = ref. Value; obj->fback. Value = fback. Value; *p. Out. Value = _IQsat(Up + Ui, obj-> out. Max, obj->out. Min); // Saturate the output return; } // end of PID_run() function pid. h PID_run() Page 1

 //! brief Runs the PID controller //! param[in] handle The PID controller handle

//! brief Runs the PID controller //! param[in] handle The PID controller handle //! param[in] ref. Value The reference value to the controller //! param[in] fback. Value The feedback value to the controller //! param[in] p. Out. Value The pointer to the controller output value static inline void PID_run(PID_Handle handle, const _iq ref. Value, const _iq fback. Value, _iq *p. Out. Value) { PID_Obj *obj = (PID_Obj *)handle; _iq Error; _iq Up, Ui; Error = ref. Value - fback. Value; Ui = obj->Ui; // load the previous integral output Up = _IQmpy(obj-> Kp, Error); // Compute the proportional output Ui = _IQsat(Ui + _IQmpy(obj-> Ki, Up), obj->out. Max, obj->out. Min); // Compute the integral output obj->Ui = Ui; // store the intetral output obj->ref. Value = ref. Value; obj->fback. Value = fback. Value; *p. Out. Value = _IQsat(Up + Ui, obj-> out. Max, obj->out. Min); // Saturate the output return; } // end of PID_run() function pid. h PID_run() Page 1

 //! brief Runs the PID controller //! param[in] handle The PID controller handle

//! brief Runs the PID controller //! param[in] handle The PID controller handle //! param[in] ref. Value The reference value to the controller //! param[in] fback. Value The feedback value to the controller //! param[in] p. Out. Value The pointer to the controller output value static inline void PID_run(PID_Handle handle, const _iq ref. Value, const _iq fback. Value, _iq *p. Out. Value) { PID_Obj *obj = (PID_Obj *)handle; _iq Error; _iq Up, Ui; Error = ref. Value - fback. Value; Ui = obj->Ui; // load the previous integral output Up = _IQmpy(obj-> Kp, Error); // Compute the proportional output Ui = _IQsat(Ui + _IQmpy(obj-> Ki, Up), obj->out. Max, obj->out. Min); // Compute the integral output obj->Ui = Ui; // store the intetral output obj->ref. Value = ref. Value; obj->fback. Value = fback. Value; *p. Out. Value = _IQsat(Up + Ui, obj-> out. Max, obj->out. Min); // Saturate the output return; } // end of PID_run() function pid. h PID_run() Page 1

 //! brief Runs the PID controller //! param[in] handle The PID controller handle

//! brief Runs the PID controller //! param[in] handle The PID controller handle //! param[in] ref. Value The reference value to the controller //! param[in] fback. Value The feedback value to the controller //! param[in] p. Out. Value The pointer to the controller output value static inline void PID_run(PID_Handle handle, const _iq ref. Value, const _iq fback. Value, _iq *p. Out. Value) { PID_Obj *obj = (PID_Obj *)handle; _iq Error; _iq Up, Ui; Error = ref. Value - fback. Value; Ui = obj->Ui; // load the previous integral output Up = _IQmpy(obj-> Kp, Error); // Compute the proportional output Ui = _IQsat(Ui + _IQmpy(obj-> Ki, Up), obj->out. Max, obj->out. Min); // Compute the integral output obj->Ui = Ui; // store the intetral output obj->ref. Value = ref. Value; obj->fback. Value = fback. Value; *p. Out. Value = _IQsat(Up + Ui, obj-> out. Max, obj->out. Min); // Saturate the output return; } // end of PID_run() function pid. h PID_run() Page 1

 //! brief Runs the PID controller //! param[in] handle The PID controller handle

//! brief Runs the PID controller //! param[in] handle The PID controller handle //! param[in] ref. Value The reference value to the controller //! param[in] fback. Value The feedback value to the controller //! param[in] p. Out. Value The pointer to the controller output value static inline void PID_run(PID_Handle handle, const _iq ref. Value, const _iq fback. Value, _iq *p. Out. Value) { PID_Obj *obj = (PID_Obj *)handle; _iq Error; _iq Up, Ui; Error = ref. Value - fback. Value; Ui = obj->Ui; // load the previous integral output Up = _IQmpy(obj-> Kp, Error); // Compute the proportional output Ui = _IQsat(Ui + _IQmpy(obj-> Ki, Up), obj->out. Max, obj->out. Min); // Compute the integral output obj->Ui = Ui; // store the intetral output obj->ref. Value = ref. Value; obj->fback. Value = fback. Value; *p. Out. Value = _IQsat(Up + Ui, obj-> out. Max, obj->out. Min); // Saturate the output return; } // end of PID_run() function pid. h PID_run() Page 1

 //! brief Runs the PID controller for speed //! param[in] handle The PID

//! brief Runs the PID controller for speed //! param[in] handle The PID controller handle //! param[in] ref. Value The reference value to the controller //! param[in] fback. Value The feedback value to the controller //! param[in] p. Out. Value The pointer to the controller output value static inline void PID_run_spd(PID_Handle handle, const _iq ref. Value, const _iq fback. Value, _iq *p. Out. Value) { PID_Obj *obj = (PID_Obj *)handle; _iq Error; _iq Up, Ui; Error = ref. Value - fback. Value; Ui = obj->Ui; // load the previous integral output Up = _IQmpy(obj-> Kp, Error); // Compute the proportional output Ui = _IQsat(Ui + _IQmpy(obj-> Ki, Error), obj->out. Max, obj->out. Min); // Compute the integral output obj->Ui = Ui; // store the intetral output obj->ref. Value = ref. Value; obj->fback. Value = fback. Value; *p. Out. Value = _IQsat(Up + Ui, obj-> out. Max, obj->out. Min); // Saturate the output return; } // end of PID_run_spd() function pid. h PID_run_spd() Page 2

 //! brief Runs the PID controller for speed //! param[in] handle The PID

//! brief Runs the PID controller for speed //! param[in] handle The PID controller handle //! param[in] ref. Value The reference value to the controller //! param[in] fback. Value The feedback value to the controller //! param[in] p. Out. Value The pointer to the controller output value static inline void PID_run_spd(PID_Handle handle, const _iq ref. Value, const _iq fback. Value, _iq *p. Out. Value) { PID_Obj *obj = (PID_Obj *)handle; _iq Error; _iq Up, Ui; Error = ref. Value - fback. Value; Ui = obj->Ui; // load the previous integral output Up = _IQmpy(obj-> Kp, Error); // Compute the proportional output Ui = _IQsat(Ui + _IQmpy(obj-> Ki, Error), obj->out. Max, obj->out. Min); // Compute the integral output obj->Ui = Ui; // store the intetral output obj->ref. Value = ref. Value; obj->fback. Value = fback. Value; *p. Out. Value = _IQsat(Up + Ui, obj-> out. Max, obj->out. Min); // Saturate the output return; } // end of PID_run_spd() function pid. h PID_run_spd() Page 2

 //! brief Runs the PID controller for speed //! param[in] handle The PID

//! brief Runs the PID controller for speed //! param[in] handle The PID controller handle //! param[in] ref. Value The reference value to the controller //! param[in] fback. Value The feedback value to the controller //! param[in] p. Out. Value The pointer to the controller output value static inline void PID_run_spd(PID_Handle handle, const _iq ref. Value, const _iq fback. Value, _iq *p. Out. Value) { PID_Obj *obj = (PID_Obj *)handle; _iq Error; _iq Up, Ui; Error = ref. Value - fback. Value; Ui = obj->Ui; // load the previous integral output Up = _IQmpy(obj-> Kp, Error); // Compute the proportional output Ui = _IQsat(Ui + _IQmpy(obj-> Ki, Error), obj->out. Max, obj->out. Min); // Compute the integral output obj->Ui = Ui; // store the intetral output obj->ref. Value = ref. Value; obj->fback. Value = fback. Value; *p. Out. Value = _IQsat(Up + Ui, obj-> out. Max, obj->out. Min); // Saturate the output return; } // end of PID_run_spd() function pid. h PID_run_spd() Page 2

 //! brief Runs the PID controller for speed //! param[in] handle The PID

//! brief Runs the PID controller for speed //! param[in] handle The PID controller handle //! param[in] ref. Value The reference value to the controller //! param[in] fback. Value The feedback value to the controller //! param[in] p. Out. Value The pointer to the controller output value static inline void PID_run_spd(PID_Handle handle, const _iq ref. Value, const _iq fback. Value, _iq *p. Out. Value) { PID_Obj *obj = (PID_Obj *)handle; _iq Error; _iq Up, Ui; Error = ref. Value - fback. Value; Ui = obj->Ui; // load the previous integral output Up = _IQmpy(obj-> Kp, Error); // Compute the proportional output Ui = _IQsat(Ui + _IQmpy(obj-> Ki, Error), obj->out. Max, obj->out. Min); // Compute the integral output obj->Ui = Ui; // store the intetral output obj->ref. Value = ref. Value; obj->fback. Value = fback. Value; *p. Out. Value = _IQsat(Up + Ui, obj-> out. Max, obj->out. Min); // Saturate the output return; } // end of PID_run_spd() function pid. h PID_run_spd() Page 2

 //! brief Runs the PID controller for speed //! param[in] handle The PID

//! brief Runs the PID controller for speed //! param[in] handle The PID controller handle //! param[in] ref. Value The reference value to the controller //! param[in] fback. Value The feedback value to the controller //! param[in] p. Out. Value The pointer to the controller output value static inline void PID_run_spd(PID_Handle handle, const _iq ref. Value, const _iq fback. Value, _iq *p. Out. Value) { PID_Obj *obj = (PID_Obj *)handle; _iq Error; _iq Up, Ui; Error = ref. Value - fback. Value; Ui = obj->Ui; // load the previous integral output Up = _IQmpy(obj-> Kp, Error); // Compute the proportional output Ui = _IQsat(Ui + _IQmpy(obj-> Ki, Error), obj->out. Max, obj->out. Min); // Compute the integral output obj->Ui = Ui; // store the intetral output obj->ref. Value = ref. Value; obj->fback. Value = fback. Value; *p. Out. Value = _IQsat(Up + Ui, obj-> out. Max, obj->out. Min); // Saturate the output return; } // end of PID_run_spd() function pid. h PID_run_spd() Page 2

 //! brief Runs the PID controller for speed //! param[in] handle The PID

//! brief Runs the PID controller for speed //! param[in] handle The PID controller handle //! param[in] ref. Value The reference value to the controller //! param[in] fback. Value The feedback value to the controller //! param[in] p. Out. Value The pointer to the controller output value static inline void PID_run_spd(PID_Handle handle, const _iq ref. Value, const _iq fback. Value, _iq *p. Out. Value) { PID_Obj *obj = (PID_Obj *)handle; _iq Error; _iq Up, Ui; Error = ref. Value - fback. Value; Ui = obj->Ui; // load the previous integral output Up = _IQmpy(obj-> Kp, Error); // Compute the proportional output Ui = _IQsat(Ui + _IQmpy(obj-> Ki, Error), obj->out. Max, obj->out. Min); // Compute the integral output obj->Ui = Ui; // store the intetral output obj->ref. Value = ref. Value; obj->fback. Value = fback. Value; *p. Out. Value = _IQsat(Up + Ui, obj-> out. Max, obj->out. Min); // Saturate the output return; } // end of PID_run_spd() function pid. h PID_run_spd() Page 2

 //! brief Runs the PID controller for speed //! param[in] handle The PID

//! brief Runs the PID controller for speed //! param[in] handle The PID controller handle //! param[in] ref. Value The reference value to the controller //! param[in] fback. Value The feedback value to the controller //! param[in] p. Out. Value The pointer to the controller output value static inline void PID_run_spd(PID_Handle handle, const _iq ref. Value, const _iq fback. Value, _iq *p. Out. Value) { PID_Obj *obj = (PID_Obj *)handle; _iq Error; _iq Up, Ui; Error = ref. Value - fback. Value; Ui = obj->Ui; // load the previous integral output Up = _IQmpy(obj-> Kp, Error); // Compute the proportional output Ui = _IQsat(Ui + _IQmpy(obj-> Ki, Error), obj->out. Max, obj->out. Min); // Compute the integral output obj->Ui = Ui; // store the intetral output obj->ref. Value = ref. Value; obj->fback. Value = fback. Value; *p. Out. Value = _IQsat(Up + Ui, obj-> out. Max, obj->out. Min); // Saturate the output return; } // end of PID_run_spd() function pid. h PID_run_spd() Page 2

 //! brief Runs the PID controller for speed //! param[in] handle The PID

//! brief Runs the PID controller for speed //! param[in] handle The PID controller handle //! param[in] ref. Value The reference value to the controller //! param[in] fback. Value The feedback value to the controller //! param[in] p. Out. Value The pointer to the controller output value static inline void PID_run_spd(PID_Handle handle, const _iq ref. Value, const _iq fback. Value, _iq *p. Out. Value) { PID_Obj *obj = (PID_Obj *)handle; _iq Error; _iq Up, Ui; Error = ref. Value - fback. Value; Ui = obj->Ui; // load the previous integral output Up = _IQmpy(obj-> Kp, Error); // Compute the proportional output Ui = _IQsat(Ui + _IQmpy(obj-> Ki, Error), obj->out. Max, obj->out. Min); // Compute the integral output obj->Ui = Ui; // store the intetral output obj->ref. Value = ref. Value; obj->fback. Value = fback. Value; *p. Out. Value = _IQsat(Up + Ui, obj-> out. Max, obj->out. Min); // Saturate the output return; } // end of PID_run_spd() function pid. h PID_run_spd() Page 2

 //! brief Runs the PID controller for speed //! param[in] handle The PID

//! brief Runs the PID controller for speed //! param[in] handle The PID controller handle //! param[in] ref. Value The reference value to the controller //! param[in] fback. Value The feedback value to the controller //! param[in] p. Out. Value The pointer to the controller output value static inline void PID_run_spd(PID_Handle handle, const _iq ref. Value, const _iq fback. Value, _iq *p. Out. Value) { PID_Obj *obj = (PID_Obj *)handle; _iq Error; _iq Up, Ui; Error = ref. Value - fback. Value; Ui = obj->Ui; // load the previous integral output Up = _IQmpy(obj-> Kp, Error); // Compute the proportional output Ui = _IQsat(Ui + _IQmpy(obj-> Ki, Error), obj->out. Max, obj->out. Min); // Compute the integral output obj->Ui = Ui; // store the intetral output obj->ref. Value = ref. Value; obj->fback. Value = fback. Value; *p. Out. Value = _IQsat(Up + Ui, obj-> out. Max, obj->out. Min); // Saturate the output return; } // end of PID_run_spd() function pid. h PID_run_spd() Page 2

 //! brief Runs the PID controller for speed //! param[in] handle The PID

//! brief Runs the PID controller for speed //! param[in] handle The PID controller handle //! param[in] ref. Value The reference value to the controller //! param[in] fback. Value The feedback value to the controller //! param[in] p. Out. Value The pointer to the controller output value static inline void PID_run_spd(PID_Handle handle, const _iq ref. Value, const _iq fback. Value, _iq *p. Out. Value) { PID_Obj *obj = (PID_Obj *)handle; _iq Error; _iq Up, Ui; Error = ref. Value - fback. Value; Ui = obj->Ui; // load the previous integral output Up = _IQmpy(obj-> Kp, Error); // Compute the proportional output Ui = _IQsat(Ui + _IQmpy(obj-> Ki, Error), obj->out. Max, obj->out. Min); // Compute the integral output obj->Ui = Ui; // store the intetral output obj->ref. Value = ref. Value; obj->fback. Value = fback. Value; *p. Out. Value = _IQsat(Up + Ui, obj-> out. Max, obj->out. Min); // Saturate the output return; } // end of PID_run_spd() function pid. h PID_run_spd() Page 2

CLARKE_run() A + 2 1/3 alpha - B + + + beta - C

CLARKE_run() A + 2 1/3 alpha - B + + + beta - C Amplitude Invariant Forward Clarke Transform num. Sensors = 3

CLARKE_run() //! brief Runs the Clarke transform module for three inputs //! param[in] handle

CLARKE_run() //! brief Runs the Clarke transform module for three inputs //! param[in] handle The Clarke transform handle //! param[in] p. In. Vec The pointer to the input vector //! param[in] p. Out. Vec The pointer to the output vector static inline void CLARKE_run(CLARKE_Handle handle, const MATH_vec 3 *p. In. Vec, MATH_vec 2 *p. Out. Vec) { CLARKE_Obj *obj = (CLARKE_Obj *)handle; uint_least 8_t num. Sensors = obj-> num. Sensors; _iq alpha_sf = obj-> alpha_sf; _iq beta_sf = obj-> beta_sf; if(num. Sensors == 3) { p. Out. Vec->value[0] = _IQmpy(lshft_1(p. In. Vec-> value[0]) - (p. In. Vec-> value[1] + p. In. Vec->value[2]), alpha_sf); p. Out. Vec->value[1] = _IQmpy(p. In. Vec-> value[1] - p. In. Vec->value[2], beta_sf); } else if(num. Sensors == 2) { p. Out. Vec->value[0] = _IQmpy(p. In. Vec-> value[0], alpha_sf); p. Out. Vec->value[1] = _IQmpy(p. In. Vec-> value[0] + lshft_1(p. In. Vec-> value[1]), beta_sf); } return; } // end of CLARKE_run() function clarke. h CLARKE_run()

IPARK_run( ) + Vd - Valpha Cos + + Vq angle Sin Inverse Park

IPARK_run( ) + Vd - Valpha Cos + + Vq angle Sin Inverse Park Transform Vbeta

ipark. h //! brief Runs the inverse Park transform module //! param[in] handle The

ipark. h //! brief Runs the inverse Park transform module //! param[in] handle The inverse Park transform handle //! param[in] p. In. Vec The pointer to the input vector //! param[in] p. Out. Vec The pointer to the output vector static inline void IPARK_run(IPARK_Handle handle, const MATH_vec 2 *p. In. Vec, MATH_vec 2 *p. Out. Vec) { IPARK_Obj *obj = (IPARK_Obj *)handle; _iq sin. Th = obj->sin. Th; _iq cos. Th = obj->cos. Th; d _iq value_0 = p. In. Vec->value[0]; _iq value_1 = p. In. Vec->value[1]; q alpha p. Out. Vec->value[0] = _IQmpy(value_0, cos. Th) - _IQmpy(value_1, sin. Th); p. Out. Vec->value[1] = _IQmpy(value_1, cos. Th) + _IQmpy(value_0, sin. Th); return; beta } // end of IPARK_run() function V V IPARK_run()

proj_lab 11. c // run the estimator // The speed reference is needed so

proj_lab 11. c // run the estimator // The speed reference is needed so that the proper sign of the forced // angle is calculated. When the estimator does not do motor ID as in this // lab, only the sign of the speed reference is used EST_run(est. Handle, &Iab_pu, &Vab_pu, g. Adc. Data. dc. Bus, g. Motor. Vars. Speed. Ref_pu); // generate the motor electrical angle_pu = EST_get. Angle_pu(est. Handle); speed_pu = EST_get. Fm_pu(est. Handle); // get Idq from estimator to avoid sin and cos, and a Park transform, // which saves CPU cycles EST_get. Idq_pu(est. Handle, &g. Idq_pu); // run the appropriate controller if(g. Motor. Vars. Flag_Run_Identify) { // Declaration of local variables. _iq ref. Value; _iq fback. Value; _iq out. Max_pu; // when appropriate, run the PID speed controller // This mechanism provides the decimation for the speed loop. if(pid. Cnt. Speed >= USER_NUM_CTRL_TICKS_PER_SPEED_TICK) { // Reset the Speed PID execution counter. pid. Cnt. Speed = 0; // The next instruction executes the PI speed controller and places // its output in Idq_ref_pu. value[1], which is the input reference // value for the q-axis current controller. PID_run_spd(pid. Handle[0], g. Motor. Vars. Speed. Ref_pu, speed_pu, &(g. Idq_ref_pu. value[1])); } else { // increment counter pid. Cnt. Speed++; } 11 // Get the reference value for the d-axis current controller. ref. Value = g. Idq_ref_pu. value[0]; // Get the actual value of Id fback. Value = g. Idq_pu. value[0]; // The next instruction executes the PI current controller for the // d axis and places its output in Vdq_pu. value[0], which is the // control voltage along the d-axis (Vd) PID_run(pid. Handle[1], ref. Value, fback. Value, &(g. Vdq_out_pu. value[0])); // get the Iq reference value ref. Value = g. Idq_ref_pu. value[1]; // get the actual value of Iq fback. Value = g. Idq_pu. value[1]; // The voltage limits on the output of the q-axis current controller // are dynamic, and are dependent on the output voltage from the d-axis // current controller. In other words, the d-axis current controller // gets first dibs on the available voltage, and the q-axis current // controller gets what's left over. That is why the d-axis current // controller executes first. The next instruction calculates the // maximum limits for this voltage as: // Vq_min_max = +/- sqrt(Vbus^2 - Vd^2) out. Max_pu = _IQsqrt(_IQ(USER_MAX_VS_MAG_PU * USER_MAX_VS_MAG_PU) - _IQmpy(g. Vdq_out_pu. value[0], g. Vdq_out_pu. value[0])); // Set the limits to +/- out. Max_pu PID_set. Min. Max(pid. Handle[2], -out. Max_pu, out. Max_pu); // The next instruction executes the PI current controller for the // q axis and places its output in Vdq_pu. value[1], which is the // control voltage vector along the q-axis (Vq) PID_run(pid. Handle[2], ref. Value, fback. Value, &(g. Vdq_out_pu. value[1 ])); Vq_max = 2 2 Vbus - Vd 12

//! brief Implements a SVM that saturates at the level of Max. Modulation. //!

//! brief Implements a SVM that saturates at the level of Max. Modulation. //! param[in] handle The space vector generator (SVGEN) handle //! param[in] p. Vab The pointer to the a/b voltages //! param[in] p. T The pointer to the pwm duty cycle time durations static inline void SVGEN_run(SVGEN_Handle handle, const MATH_vec 2 *p. Vab, MATH_vec 3 *p. T) { _iq Vmax, Vmin, Vcom; _iq Va, Vb, Vc; _iq Va_tmp = -(p. Vab->value[0]>>1); _iq Vb_tmp = _IQmpy(SVGEN_SQRT 3_OVER_2, p. Vab->value[1]); Va = p. Vab->value[0]; //alpha Vb = Va_tmp + Vb_tmp; //-0. 5*alpha + sqrt(3)/2 * beta; Vc = Va_tmp - Vb_tmp; //-0. 5*alpha - sqrt(3)/2 * beta; Vmax=0; Vmin=0; // find order Vmin, Vmid, Vmax if (Va > Vb) { Vmax = Va; Vmin = Vb; } svgen. h SVGEN_run() Page 1

//! brief Implements a SVM that saturates at the level of Max. Modulation. //!

//! brief Implements a SVM that saturates at the level of Max. Modulation. //! param[in] handle The space vector generator (SVGEN) handle //! param[in] p. Vab The pointer to the a/b voltages //! param[in] p. T The pointer to the pwm duty cycle time durations static inline void SVGEN_run(SVGEN_Handle handle, const MATH_vec 2 *p. Vab, MATH_vec 3 *p. T) { _iq Vmax, Vmin, Vcom; _iq Va, Vb, Vc; _iq Va_tmp = -(p. Vab->value[0]>>1); _iq Vb_tmp = _IQmpy(SVGEN_SQRT 3_OVER_2, p. Vab->value[1]); Va = p. Vab->value[0]; //alpha Vb = Va_tmp + Vb_tmp; //-0. 5*alpha + sqrt(3)/2 * beta; Vc = Va_tmp - Vb_tmp; //-0. 5*alpha - sqrt(3)/2 * beta; Vmax=0; Vmin=0; // find order Vmin, Vmid, Vmax if (Va > Vb) { Vmax = Va; Vmin = Vb; } svgen. h SVGEN_run() Inverse Clarke Transform Page 1

 else // Va not greater than Vb { Vmax = Vb; Vmin =

else // Va not greater than Vb { Vmax = Vb; Vmin = Va; } if (Vc > Vmax) { Vmax = Vc; } else if (Vc < Vmin) { Vmin = Vc; } Vmax svgen. h SVGEN_run() Vcom Vmin p. T -> value[0] Vcom = _IQmpy(Vmax+Vmin, _IQ(0. 5)); // Subtract common-mode term to achieve SV modulation p. T->value[0] = (Va - Vcom); p. T->value[1] = (Vb - Vcom); p. T -> value[1] p. T->value[2] = (Vc - Vcom); return; p. T -> value[2] } // end of SVGEN_run() function This SVM technique is nice for overmodulation, as the clipping occurs naturally when the gain is increased. Page 2

Angle Compensation PROBLEM: The output voltages from the FOC algorithm are calculated assuming a

Angle Compensation PROBLEM: The output voltages from the FOC algorithm are calculated assuming a specific angle of the rotor flux. When the PWM values are placed into the PWM registers, they don’t affect the PWM pins until the next PWM cycle due to the double-buffered nature of the PWM module. By this time, the rotor flux angle has changed by an amount that is directly proportional to the speed of the flux vector rotation and inversely proportional to the PWM frequency. As a result, the applied voltages are incorrect in relation to the new rotor flux angle. SOLUTION: Based on the PWM frequency and speed at the time that the voltages are calculated, predict what the angle will be when the calculated voltages will appear on the motor windings. Then use this angle prediction for the inverse Park transform. I_Park_angle = calculated_angle + electrical speed * PWM period * compensation_factor If the sampling frequency is equal to the PWM frequency, then the compensation_factor is equal to 1. 5. However, the compensation_factor changes as the relationship between PWM frequency and sampling frequency changes (next slide…)

Compensation Factor changes as a function of USER_NUM_PWM_TICKS_PER_ISR_TICK The Compensation factor is the number

Compensation Factor changes as a function of USER_NUM_PWM_TICKS_PER_ISR_TICK The Compensation factor is the number of PWM periods (T) from when the ADC samples used to calculate a given angle are acquired until the average time that the PWM values resulting from that angle calculation appear on the motor windings. Example 1 PWM Ticks / ISR Ticks = 1 q 2 q 3 q 4 q 5 q 6 q 7 q 8 q 9 q 10 q 11 q 12 Compensation Factor = 1. 5 V 1 V 2 V 3 V 4 V 5 V 6 V 7 V 8 V 9 V 10 V 11 V 12 PWM Values Example 2 PWM Ticks / ISR Ticks = 3 q 1 q 2 Compensation Factor = 2. 5 V 1 PWM Values q 3 V 2 q 4 V 3 q 5 V 4 q 6 V 5 T T = PWM period Compensated angle = Uncompensated angle + speed * T * Compensation Factor V 6

//! brief The angle. Delay. Comp function compensates for the delay introduced //! brief

//! brief The angle. Delay. Comp function compensates for the delay introduced //! brief from the time when the system inputs are sampled to when the PWM //! brief voltages are applied to the motor windings. _iq angle. Delay. Comp(const _iq fm_pu, const _iq angle. Uncomp_pu) { _iq angle. Delta_pu = _IQmpy(fm_pu, _IQ(USER_IQ_FULL_SCALE_FREQ_Hz / (USER_PWM_FREQ_k. Hz*1000. 0))); _iq angle. Comp. Factor = _IQ(1. 0 + (float_t)USER_NUM_PWM_TICKS_PER_ISR_TICK * 0. 5); _iq angle. Delta. Comp_pu = _IQmpy(angle. Delta_pu, angle. Comp. Factor); uint 32_t angle. Mask = ((uint 32_t)0 x. FFFF >> (32 - GLOBAL_Q)); _iq angle. Comp_pu; _iq angle. Tmp_pu; // increment the angle. Tmp_pu = angle. Uncomp_pu + angle. Delta. Comp_pu; // mask the angle for wrap around // note: must account for the sign of the angle. Comp_pu = _IQabs(angle. Tmp_pu) & angle. Mask; // account for sign if(angle. Tmp_pu < _IQ(0. 0)) { angle. Comp_pu = -angle. Comp_pu; } return(angle. Comp_pu); } // end of angle. Delay. Comp() function proj_lab 11. c angle. Delay. Comp()

proj_lab 11. c // ************************************* // the functions void main(void) { // IMPORTANT NOTE:

proj_lab 11. c // ************************************* // the functions void main(void) { // IMPORTANT NOTE: If you are not familiar with Motor. Ware coding guidelines // please refer to the following document: // C: /ti/motorware_1_01_00_1 x/docs/motorware_coding_standards. pdf // Only used if running from FLASH // Note that the variable FLASH is defined by the project #ifdef FLASH // Copy time critical code and Flash setup code to RAM // The Ramfuncs. Load. Start, Ramfuncs. Load. End, and Ramfuncs. Run. Start // symbols are created by the linker. Refer to the linker files. mem. Copy((uint 16_t *)&Ramfuncs. Load. Start, (uint 16_t *)&Ramfuncs. Load. End, (uint 16_t *)&Ramfuncs. Run. Start); #ifdef F 2802 x. F //copy. econst to unsecure RAM if(*econst_end - *econst_start) { mem. Copy((uint 16_t *)&econst_start, (uint 16_t *)&econst_end, (uint 16_t *)&econst_ram_load); } //copy. switch ot unsecure RAM if(*switch_end - *switch_start) { mem. Copy((uint 16_t *)&switch_start, (uint 16_t *)&switch_end, (uint 16_t *)&switch_ram_load); } #endif 1 // initialize the Hardware Abstraction Layer (HAL) // hal. Handle will be used throughout the code to interface with the HAL // (set parameters, get and set functions, etc) hal. Handle is required since // this is how all objects are interfaced, and it allows interface with // multiple objects by simply passing a different handle. The use of // handles is explained in this document: // C: /ti/motorware_1_01_00_1 x/docs/motorware_coding_standards. pdf hal. Handle = HAL_init(&hal, sizeof(hal)); // check for errors in user parameters USER_check. For. Errors(&g. User. Params); // store user parameter error in global variable g. Motor. Vars. User. Error. Code = USER_get. Error. Code(&g. User. Params); // do not allow code execution if there is a user parameter error. If there // is an error, the code will be stuck in this forever loop if(g. Motor. Vars. User. Error. Code != USER_Error. Code_No. Error) { for(; ; ) { g. Motor. Vars. Flag_enable. Sys = false; } } // initialize the Clarke modules // Clarke handle initialization for current signals clarke. Handle_I = CLARKE_init(&clarke_I, sizeof(clarke_I)); // Clarke handle initialization for voltage signals clarke. Handle_V = CLARKE_init(&clarke_V, sizeof(clarke_V)); // initialize the estimator est. Handle = EST_init((void *)USER_EST_HANDLE_ADDRESS, 0 x 200); // initialize the user parameters // This function initializes all values of structure g. User. Params with // values defined in user. h. The values in g. User. Params will be then used by // the hardware abstraction layer (HAL) to configure peripherals such as // PWM, ADC, interrupts, etc. USER_set. Params(&g. User. Params); 2

proj_lab 11. c // set the hardware abstraction layer parameters // This function initializes

proj_lab 11. c // set the hardware abstraction layer parameters // This function initializes all peripherals through a Hardware Abstraction // Layer (HAL). It uses all values stored in g. User. Params. HAL_set. Params(hal. Handle, &g. User. Params); #ifdef FAST_ROM_V 1 p 6 { // These function calls are used to initialize the estimator with ROM // function calls. It needs the specific address where the controller // object is declared by the ROM code. CTRL_Handle ctrl. Handle = CTRL_init((void *)USER_CTRL_HANDLE_ADDRESS , 0 x 200); CTRL_Obj *obj = (CTRL_Obj *)ctrl. Handle; // this sets the estimator handle (part of the controller object) to // the same value initialized above by the EST_init() function call. // This is done so the next function implemented in ROM, can // successfully initialize the estimator as part of the controller // object. obj->est. Handle = est. Handle; // initialize the estimator through the controller. These three // function calls are needed for the F 2806 x. F/M implementation of // Insta. SPIN. CTRL_set. Params(ctrl. Handle, &g. User. Params); CTRL_set. User. Motor. Params(ctrl. Handle); CTRL_setup. Est. Idle. State(ctrl. Handle); } #else { // initialize the estimator. These two function calls are needed for // the F 2802 x. F implementation of Insta. SPIN using the estimator handle // initialized by EST_init(), these two function calls configure the // estimator, and they set the estimator in a proper state prior to // spinning a motor. EST_set. Est. Params(est. Handle, &g. User. Params); EST_setup. Est. Idle. State(est. Handle); } #endif 3 // disable Rs recalculation EST_set. Flag_enable. Rs. Recalc(est. Handle, false); // set the number of current sensors setup. Clarke_I(clarke. Handle_I, USER_NUM_CURRENT_SENSORS); // set the number of voltage sensors setup. Clarke_V(clarke. Handle_V, USER_NUM_VOLTAGE_SENSORS); // set the pre-determined current and voltage feedback offset values g. Offsets_I_pu. value[0] = _IQ(I_A_offset); g. Offsets_I_pu. value[1] = _IQ(I_B_offset); g. Offsets_I_pu. value[2] = _IQ(I_C_offset); g. Offsets_V_pu. value[0] = _IQ(V_A_offset); g. Offsets_V_pu. value[1] = _IQ(V_B_offset); g. Offsets_V_pu. value[2] = _IQ(V_C_offset); // initialize the PID controllers { // This equation defines the relationship between per unit current and // real-world current. The resulting value in per units (pu) is then // used to configure the controllers _iq max. Current_pu = _IQ(USER_MOTOR_MAX_CURRENT / USER_IQ_FULL_SCALE_CURRENT_A); // This equation uses the scaled maximum voltage vector, which is // already in per units, hence there is no need to include the #define // for USER_IQ_FULL_SCALE_VOLTAGE_V _iq max. Voltage_pu = _IQ(USER_MAX_VS_MAG_PU * USER_VD_SF); float_t full. Scale. Current = USER_IQ_FULL_SCALE_CURRENT_A; float_t full. Scale. Voltage = USER_IQ_FULL_SCALE_VOLTAGE_V; float_t Isr. Period_sec = 1. 0 / USER_ISR_FREQ_Hz; float_t Ls_d = USER_MOTOR_Ls_d; float_t Ls_q = USER_MOTOR_Ls_q; float_t Rs = USER_MOTOR_Rs; // This lab assumes that motor parameters are known, and it does not // perform motor ID, so the R/L parameters are known and defined in // user. h float_t Rover. Ls_d = Rs / Ls_d; float_t Rover. Ls_q = Rs / Ls_q; 4

proj_lab 11. c // For the current controller, Kp = Ls*bandwidth(rad/sec) But in order

proj_lab 11. c // For the current controller, Kp = Ls*bandwidth(rad/sec) But in order // to be used, it must be converted to per unit values by multiplying // by full. Scale. Current and then dividing by full. Scale. Voltage. From the // statement below, we see that the bandwidth in rad/sec is equal to // 0. 25/Isr. Period_sec, which is equal to USER_ISR_FREQ_HZ/4. This means // that by setting Kp as described below, the bandwidth in Hz is // USER_ISR_FREQ_HZ/(8*pi). _iq Kp_Id = _IQ((0. 25 * Ls_d * full. Scale. Current) / (Isr. Period_sec * full. Scale. Voltage)); // In order to achieve pole/zero cancellation (which reduces the // closed-loop transfer function from a second-order system to a // first-order system), Ki must equal Rs/Ls. Since the output of the // Ki gain stage is integrated by a DIGITAL integrator, the integrator // input must be scaled by 1/Isr. Period_sec. That's just the way // digital integrators work. But, since Isr. Period_sec is a constant, // we can save an additional multiplication operation by lumping this // term with the Ki value. _iq Ki_Id = _IQ(Rover. Ls_d * Isr. Period_sec); // Now do the same thing for Kp for the q-axis current controller. // If the motor is not an IPM motor, Ld and Lq are the same, which // means that Kp_Iq = Kp_Id _iq Kp_Iq = _IQ((0. 25 * Ls_q * full. Scale. Current) / (Isr. Period_sec * full. Scale. Voltage)); // Do the same thing for Ki for the q-axis current controller. If the // motor is not an IPM motor, Ld and Lq are the same, which means that // Ki_Iq = Ki_Id. _iq Ki_Iq = _IQ(Rover. Ls_q * Isr. Period_sec); // There are three PI controllers; one speed controller and two current // controllers. Each PI controller has two coefficients; Kp and Ki. // So you have a total of six coefficients that must be defined. // This is for the speed controller pid. Handle[0] = PID_init(&pid[0], sizeof(pid[0])); // This is for the Id current controller pid. Handle[1] = PID_init(&pid[1], sizeof(pid[1])); // This is for the Iq current controller pid. Handle[2] = PID_init(&pid[2], sizeof(pid[2])); 5 // The following instructions load the parameters for the speed PI // controller. PID_set. Gains(pid. Handle[0], _IQ(1. 0), _IQ(0. 01), _IQ(0. 0)); // The current limit is performed by the limits placed on the speed PI // controller output. In the following statement, the speed // controller's largest negative current is set to -max. Current_pu, and // the largest positive current is set to max. Current_pu. PID_set. Min. Max(pid. Handle[0], -max. Current_pu, max. Current_pu); PID_set. Ui(pid. Handle[0], _IQ(0. 0)); // Set the initial condition value // for the integrator output to 0 pid. Cnt. Speed = 0; // Set the counter for decimating the speed // controller to 0 // The following instructions load the parameters for the d-axis // current controller. // P term = Kp_Id, I term = Ki_Id, D term = 0 PID_set. Gains(pid. Handle[1], Kp_Id, Ki_Id, _IQ(0. 0)); // Largest negative voltage = -max. Voltage_pu, largest positive // voltage = max. Voltage_pu PID_set. Min. Max(pid. Handle[1], -max. Voltage_pu, max. Voltage_pu); // Set the initial condition value for the integrator output to 0 PID_set. Ui(pid. Handle[1], _IQ(0. 0)); // The following instructions load the parameters for the q-axis // current controller. // P term = Kp_Iq, I term = Ki_Iq, D term = 0 PID_set. Gains(pid. Handle[2], Kp_Iq, Ki_Iq, _IQ(0. 0)); // The largest negative voltage = 0 and the largest positive // voltage = 0. But these limits are updated every single ISR before // actually executing the Iq controller. The limits depend on how much // voltage is left over after the Id controller executes. So having an // initial value of 0 does not affect Iq current controller execution. PID_set. Min. Max(pid. Handle[2], _IQ(0. 0)); // Set the initial condition value for the integrator output to 0 PID_set. Ui(pid. Handle[2], _IQ(0. 0)); } 6

proj_lab 11. c // initialize the speed reference in kilo RPM where base speed

proj_lab 11. c // initialize the speed reference in kilo RPM where base speed is // USER_IQ_FULL_SCALE_FREQ_Hz. // Set 10 Hz electrical frequency as initial value, so the k. RPM value would // be: 10 * 60 / motor pole pairs / 1000. g. Motor. Vars. Speed. Ref_krpm = _IQmpy(_IQ(10. 0), g. Speed_hz_to_krpm_sf); // initialize the inverse Park module ipark. Handle = IPARK_init(&ipark, sizeof(ipark)); // initialize the space vector generator module svgen. Handle = SVGEN_init(&svgen, sizeof(svgen)); // setup faults HAL_setup. Faults(hal. Handle); // initialize the interrupt vector table HAL_init. Int. Vector. Table(hal. Handle); // enable the ADC interrupts HAL_enable. Adc. Ints(hal. Handle); // enable global interrupts HAL_enable. Global. Ints(hal. Handle); // enable debug interrupts HAL_enable. Debug. Int(hal. Handle); // disable the PWM HAL_disable. Pwm(hal. Handle); // compute scaling factors for flux and torque calculations g. Flux_pu_to_Wb_sf = USER_compute. Flux_pu_to_Wb_sf(); g. Flux_pu_to_Vp. Hz_sf = USER_compute. Flux_pu_to_Vp. Hz_sf(); g. Torque_Ls_Id_Iq_pu_to_Nm_sf = USER_compute. Torque_Ls_Id_Iq_pu_to_Nm_sf(); g. Torque_Flux_Iq_pu_to_Nm_sf = USER_compute. Torque_Flux_Iq_pu_to_Nm_sf(); // enable the system by default g. Motor. Vars. Flag_enable. Sys = true; 7 #ifdef DRV 8301_SPI // turn on the DRV 8301 if present HAL_enable. Drv(hal. Handle); // initialize the DRV 8301 interface HAL_setup. Drv. Spi(hal. Handle, &g. Drv. Spi 8301 Vars); #endif #ifdef DRV 8305_SPI // turn on the DRV 8305 if present HAL_enable. Drv(hal. Handle); // initialize the DRV 8305 interface HAL_setup. Drv. Spi(hal. Handle, &g. Drv. Spi 8305 Vars); #endif // Begin the background loop for(; ; ) { // Waiting for enable system flag to be set while(!(g. Motor. Vars. Flag_enable. Sys)); // loop while the enable system flag is true while(g. Motor. Vars. Flag_enable. Sys) { // If Flag_enable. Sys is set AND Flag_Run_Identify is set THEN // enable PWMs and set the speed reference if(g. Motor. Vars. Flag_Run_Identify) { // update estimator state EST_update. State(est. Handle, 0); #ifdef FAST_ROM_V 1 p 6 // call this function to fix 1 p 6. This is only used for // F 2806 x. F/M implementation of Insta. SPIN (version 1. 6 of // ROM), since the inductance calculation is not done // correctly in ROM, so this function fixes that ROM bug. software. Update 1 p 6(est. Handle); #endif // enable the PWM HAL_enable. Pwm(hal. Handle); } 8

proj_lab 11. c else // Flag_enable. Sys is set AND Flag_Run_Identify is not set

proj_lab 11. c else // Flag_enable. Sys is set AND Flag_Run_Identify is not set { // set estimator to Idle EST_set. Idle(est. Handle); // disable the PWM HAL_disable. Pwm(hal. Handle); // clear integrator outputs PID_set. Ui(pid. Handle[0], _IQ(0. 0)); PID_set. Ui(pid. Handle[1], _IQ(0. 0)); PID_set. Ui(pid. Handle[2], _IQ(0. 0)); // clear Id and Iq references g. Idq_ref_pu. value[0] = _IQ(0. 0); g. Idq_ref_pu. value[1] = _IQ(0. 0); } // update the global variables update. Global. Variables(est. Handle); // enable/disable the forced angle EST_set. Flag_enable. Force. Angle(est. Handle, g. Motor. Vars. Flag_enable. Force. Angle); // set target speed g. Motor. Vars. Speed. Ref_pu = _IQmpy(g. Motor. Vars. Speed. Ref_krpm, g. Speed_krpm_to_pu_sf); #ifdef DRV 8301_SPI HAL_write. Drv. Data(hal. Handle, &g. Drv. Spi 8301 Vars); HAL_read. Drv. Data(hal. Handle, &g. Drv. Spi 8301 Vars); #endif #ifdef DRV 8305_SPI HAL_write. Drv. Data(hal. Handle, &g. Drv. Spi 8305 Vars); HAL_read. Drv. Data(hal. Handle, &g. Drv. Spi 8305 Vars); #endif 9 } // end of while(g. Flag_enable. Sys) loop // disable the PWM HAL_disable. Pwm(hal. Handle); g. Motor. Vars. Flag_Run_Identify = false; } // end of for(; ; ) loop } // end of main() function //! brief The main ISR that implements the motor control. interrupt void main. ISR(void) { // Declaration of local variables _iq angle_pu = _IQ(0. 0); _iq speed_pu = _IQ(0. 0); _iq one. Over. Dc. Bus; MATH_vec 2 Iab_pu; MATH_vec 2 Vab_pu; MATH_vec 2 phasor; // acknowledge the ADC interrupt HAL_acq. Adc. Int(hal. Handle, ADC_Int. Number_1); // convert the ADC data HAL_read. Adc. Data. With. Offsets(hal. Handle, &g. Adc. Data); // remove offsets g. Adc. Data. I. value[0] = g. Adc. Data. I. value[0] - g. Offsets_I_pu. value[0]; g. Adc. Data. I. value[1] = g. Adc. Data. I. value[1] - g. Offsets_I_pu. value[1]; g. Adc. Data. I. value[2] = g. Adc. Data. I. value[2] - g. Offsets_I_pu. value[2]; g. Adc. Data. V. value[0] = g. Adc. Data. V. value[0] - g. Offsets_V_pu. value[0]; g. Adc. Data. V. value[1] = g. Adc. Data. V. value[1] - g. Offsets_V_pu. value[1]; g. Adc. Data. V. value[2] = g. Adc. Data. V. value[2] - g. Offsets_V_pu. value[2]; // run Clarke transform on current. Three values are passed, two values // are returned. CLARKE_run(clarke. Handle_I, &g. Adc. Data. I, &Iab_pu); // run Clarke transform on voltage. Three values are passed, two values // are returned. CLARKE_run(clarke. Handle_V, &g. Adc. Data. V, &Vab_pu); 10

proj_lab 11. c // run the estimator // The speed reference is needed so

proj_lab 11. c // run the estimator // The speed reference is needed so that the proper sign of the forced // angle is calculated. When the estimator does not do motor ID as in this // lab, only the sign of the speed reference is used EST_run(est. Handle, &Iab_pu, &Vab_pu, g. Adc. Data. dc. Bus, g. Motor. Vars. Speed. Ref_pu); // generate the motor electrical angle_pu = EST_get. Angle_pu(est. Handle); speed_pu = EST_get. Fm_pu(est. Handle); // get Idq from estimator to avoid sin and cos, and a Park transform, // which saves CPU cycles EST_get. Idq_pu(est. Handle, &g. Idq_pu); // run the appropriate controller if(g. Motor. Vars. Flag_Run_Identify) { // Declaration of local variables. _iq ref. Value; _iq fback. Value; _iq out. Max_pu; // when appropriate, run the PID speed controller // This mechanism provides the decimation for the speed loop. if(pid. Cnt. Speed >= USER_NUM_CTRL_TICKS_PER_SPEED_TICK) { // Reset the Speed PID execution counter. pid. Cnt. Speed = 0; // The next instruction executes the PI speed controller and places // its output in Idq_ref_pu. value[1], which is the input reference // value for the q-axis current controller. PID_run_spd(pid. Handle[0], g. Motor. Vars. Speed. Ref_pu, speed_pu, &(g. Idq_ref_pu. value[1])); } else { // increment counter pid. Cnt. Speed++; } 11 // Get the reference value for the d-axis current controller. ref. Value = g. Idq_ref_pu. value[0]; // Get the actual value of Id fback. Value = g. Idq_pu. value[0]; // The next instruction executes the PI current controller for the // d axis and places its output in Vdq_pu. value[0], which is the // control voltage along the d-axis (Vd) PID_run(pid. Handle[1], ref. Value, fback. Value, &(g. Vdq_out_pu. value[0])); // get the Iq reference value ref. Value = g. Idq_ref_pu. value[1]; // get the actual value of Iq fback. Value = g. Idq_pu. value[1]; // The voltage limits on the output of the q-axis current controller // are dynamic, and are dependent on the output voltage from the d-axis // current controller. In other words, the d-axis current controller // gets first dibs on the available voltage, and the q-axis current // controller gets what's left over. That is why the d-axis current // controller executes first. The next instruction calculates the // maximum limits for this voltage as: // Vq_min_max = +/- sqrt(Vbus^2 - Vd^2) out. Max_pu = _IQsqrt(_IQ(USER_MAX_VS_MAG_PU * USER_MAX_VS_MAG_PU) - _IQmpy(g. Vdq_out_pu. value[0], g. Vdq_out_pu. value[0])); // Set the limits to +/- out. Max_pu PID_set. Min. Max(pid. Handle[2], -out. Max_pu, out. Max_pu); // The next instruction executes the PI current controller for the // q axis and places its output in Vdq_pu. value[1], which is the // control voltage vector along the q-axis (Vq) PID_run(pid. Handle[2], ref. Value, fback. Value, &(g. Vdq_out_pu. value[1 ])); 12

proj_lab 11. c // The voltage vector is now calculated and ready to be

proj_lab 11. c // The voltage vector is now calculated and ready to be applied to the // motor in the form of three PWM signals. However, even though the // voltages may be supplied to the PWM module now, they won't be // applied to the motor until the next PWM cycle. By this point, the // motor will have moved away from the angle that the voltage vector // was calculated for, by an amount which is proportional to the // sampling frequency and the speed of the motor. For steady-state // speeds, we can calculate this angle delay and compensate for it. angle_pu = angle. Delay. Comp(speed_pu, angle_pu); // compute the sin/cos phasor since the phase is used by the inverse // Park transform to transform from DQ to Alpha/Beta. The phasor // calculation is not part of the IPARK module. The way it works is, // the phasor is calculated by doing a sine and cosine, then the phasor // is copied into the IPARK module, and then finally the IPARK module // uses the phasor to calculate from DQ to Alpha/Beta phasor. value[0] = _IQcos. PU(angle_pu); phasor. value[1] = _IQsin. PU(angle_pu); // set the phasor in the inverse Park transform IPARK_set. Phasor(ipark. Handle, &phasor); // Run the inverse Park module. This converts the voltage vector from // synchronous frame values to stationary frame values. IPARK_run(ipark. Handle, &g. Vdq_out_pu, &Vab_pu); // This code compensates for variations in the DC bus by adjusting the // PWM duty cycle to compensate for the variations. the goal is to // produce the desired volt-second product regardless of the DC bus // value. To do this, we must divide the desired voltage values by // the DC bus value. Or. . . it is easier to multiply by 1/(DC bus value) one. Over. Dc. Bus = EST_get. One. Over. Dc. Bus_pu(est. Handle); Vab_pu. value[0] = _IQmpy(Vab_pu. value[0], one. Over. Dc. Bus); Vab_pu. value[1] = _IQmpy(Vab_pu. value[1], one. Over. Dc. Bus); // Now run the space vector generator (SVGEN) module. // There is no need to do an inverse CLARKE transform, as this is // handled in the SVGEN_run function. SVGEN_run(svgen. Handle, &Vab_pu, &(g. Pwm. Data. Tabc)); } 13 else // g. Motor. Vars. Flag_Run_Identify = 0 { // disable the PWM HAL_disable. Pwm(hal. Handle); // Set the PWMs to 50% duty cycle g. Pwm. Data. Tabc. value[0] = _IQ(0. 0); g. Pwm. Data. Tabc. value[1] = _IQ(0. 0); g. Pwm. Data. Tabc. value[2] = _IQ(0. 0); } // write to the PWM compare registers, and then we are done! HAL_write. Pwm. Data(hal. Handle, &g. Pwm. Data); return; } // end of main. ISR() function 14

 //! brief Runs the PID controller //! param[in] handle The PID controller handle

//! brief Runs the PID controller //! param[in] handle The PID controller handle //! param[in] ref. Value The reference value to the controller //! param[in] fback. Value The feedback value to the controller //! param[in] p. Out. Value The pointer to the controller output value static inline void PID_run(PID_Handle handle, const _iq ref. Value, const _iq fback. Value, _iq *p. Out. Value) { PID_Obj *obj = (PID_Obj *)handle; _iq Error; _iq Up, Ui; Error = ref. Value - fback. Value; Ui = obj->Ui; // load the previous integral output Up = _IQmpy(obj-> Kp, Error); // Compute the proportional output Ui = _IQsat(Ui + _IQmpy(obj-> Ki, Up), obj->out. Max, obj->out. Min); // Compute the integral output obj->Ui = Ui; // store the intetral output obj->ref. Value = ref. Value; obj->fback. Value = fback. Value; *p. Out. Value = _IQsat(Up + Ui, obj-> out. Max, obj->out. Min); // Saturate the output return; } // end of PID_run() function pid. h PID_run() Page 1

 //! brief Runs the PID controller for speed //! param[in] handle The PID

//! brief Runs the PID controller for speed //! param[in] handle The PID controller handle //! param[in] ref. Value The reference value to the controller //! param[in] fback. Value The feedback value to the controller //! param[in] p. Out. Value The pointer to the controller output value static inline void PID_run_spd(PID_Handle handle, const _iq ref. Value, const _iq fback. Value, _iq *p. Out. Value) { PID_Obj *obj = (PID_Obj *)handle; _iq Error; _iq Up, Ui; Error = ref. Value - fback. Value; Ui = obj->Ui; // load the previous integral output Up = _IQmpy(obj-> Kp, Error); // Compute the proportional output Ui = _IQsat(Ui + _IQmpy(obj-> Ki, Error), obj->out. Max, obj->out. Min); // Compute the integral output obj->Ui = Ui; // store the intetral output obj->ref. Value = ref. Value; obj->fback. Value = fback. Value; *p. Out. Value = _IQsat(Up + Ui, obj-> out. Max, obj->out. Min); // Saturate the output return; } // end of PID_run_spd() function pid. h PID_run_spd() Page 2

//! brief Implements a SVM that saturates at the level of Max. Modulation. //!

//! brief Implements a SVM that saturates at the level of Max. Modulation. //! param[in] handle The space vector generator (SVGEN) handle //! param[in] p. Vab The pointer to the a/b voltages //! param[in] p. T The pointer to the pwm duty cycle time durations static inline void SVGEN_run(SVGEN_Handle handle, const MATH_vec 2 *p. Vab, MATH_vec 3 *p. T) { _iq Vmax, Vmin, Vcom; _iq Va, Vb, Vc; _iq Va_tmp = -(p. Vab->value[0]>>1); _iq Vb_tmp = _IQmpy(SVGEN_SQRT 3_OVER_2, p. Vab->value[1]); Va = p. Vab->value[0]; //alpha Vb = Va_tmp + Vb_tmp; //-0. 5*alpha + sqrt(3)/2 * beta; Vc = Va_tmp - Vb_tmp; //-0. 5*alpha - sqrt(3)/2 * beta; Vmax=0; Vmin=0; // find order Vmin, Vmid, Vmax if (Va > Vb) { Vmax = Va; Vmin = Vb; } svgen. h SVGEN_run() Page 1

 else // Va not greater than Vb { Vmax = Vb; Vmin =

else // Va not greater than Vb { Vmax = Vb; Vmin = Va; } if (Vc > Vmax) { Vmax = Vc; } else if (Vc < Vmin) { Vmin = Vc; } svgen. h SVGEN_run() Vcom = _IQmpy(Vmax+Vmin, _IQ(0. 5)); // Subtract common-mode term to achieve SV modulation p. T->value[0] = (Va - Vcom); p. T->value[1] = (Vb - Vcom); p. T->value[2] = (Vc - Vcom); return; } // end of SVGEN_run() function Page 2

CLARKE_run() //! brief Runs the Clarke transform module for three inputs //! param[in] handle

CLARKE_run() //! brief Runs the Clarke transform module for three inputs //! param[in] handle The Clarke transform handle //! param[in] p. In. Vec The pointer to the input vector //! param[in] p. Out. Vec The pointer to the output vector static inline void CLARKE_run(CLARKE_Handle handle, const MATH_vec 3 *p. In. Vec, MATH_vec 2 *p. Out. Vec) { CLARKE_Obj *obj = (CLARKE_Obj *)handle; uint_least 8_t num. Sensors = obj-> num. Sensors; _iq alpha_sf = obj-> alpha_sf; _iq beta_sf = obj-> beta_sf; if(num. Sensors == 3) { p. Out. Vec->value[0] = _IQmpy(lshft_1(p. In. Vec-> value[0]) - (p. In. Vec-> value[1] + p. In. Vec->value[2]), alpha_sf); p. Out. Vec->value[1] = _IQmpy(p. In. Vec-> value[1] - p. In. Vec->value[2], beta_sf); } else if(num. Sensors == 2) { p. Out. Vec->value[0] = _IQmpy(p. In. Vec-> value[0], alpha_sf); p. Out. Vec->value[1] = _IQmpy(p. In. Vec-> value[0] + lshft_1(p. In. Vec-> value[1]), beta_sf); } return; } // end of CLARKE_run() function clarke. h CLARKE_run()

//! brief The angle. Delay. Comp function compensates for the delay introduced //! brief

//! brief The angle. Delay. Comp function compensates for the delay introduced //! brief from the time when the system inputs are sampled to when the PWM //! brief voltages are applied to the motor windings. _iq angle. Delay. Comp(const _iq fm_pu, const _iq angle. Uncomp_pu) { _iq angle. Delta_pu = _IQmpy(fm_pu, _IQ(USER_IQ_FULL_SCALE_FREQ_Hz / (USER_PWM_FREQ_k. Hz*1000. 0))); _iq angle. Comp. Factor = _IQ(1. 0 + (float_t)USER_NUM_PWM_TICKS_PER_ISR_TICK * 0. 5); _iq angle. Delta. Comp_pu = _IQmpy(angle. Delta_pu, angle. Comp. Factor); uint 32_t angle. Mask = ((uint 32_t)0 x. FFFF >> (32 - GLOBAL_Q)); _iq angle. Comp_pu; _iq angle. Tmp_pu; // increment the angle. Tmp_pu = angle. Uncomp_pu + angle. Delta. Comp_pu; // mask the angle for wrap around // note: must account for the sign of the angle. Comp_pu = _IQabs(angle. Tmp_pu) & angle. Mask; // account for sign if(angle. Tmp_pu < _IQ(0. 0)) { angle. Comp_pu = -angle. Comp_pu; } return(angle. Comp_pu); } // end of angle. Delay. Comp() function proj_lab 11. c angle. Delay. Comp()

…for using

…for using