Implementing Fixed-Point Programming for DSP PR Controllers

Today, we continue our work on fixed-point programming, which is quite significant for using PR control, especially since floating-point microcontrollers are expensive.

Previously, in floating-point development, we discussed the discretization and code implementation of the Proportional Resonant Controller (QPR).

For fixed-point implementation: Considering quantization errors, we directly run the IIR2 function of the PR controller on IQ26, which provides a fourfold increase in precision compared to single-precision floating-point. The integer representation can handle a maximum of 32 digits, which is sufficient to meet the output dynamic range of the PR control. If necessary, we can also directly scale the PR output by 2^N to satisfy the modulation range of the controller.

IIR2 Definition:

typedef struct IIR_2ORDER_IQ_TAG{

Uint16 coeff_init_flag;

_iq filter_out;

_iq filter_W0;

_iq filter_W1;

_iq filter_W2;

_iq coeff_B0;

_iq coeff_B1;

_iq coeff_B2;

_iq coeff_A1;

_iq coeff_A2;

}IIR_2ORD_IQ_DATA_DEF;

Function:

static inline _iq iir2_IQ_func_(volatile _iq input, volatile IIR_2ORD_IQ_DATA_DEF *p)

{

//

// w0 = x(0) – A1 * W1 – A2 * W2

//

p->filter_W0 = input – _IQ26mpy(p->coeff_A1, p->filter_W1) – _IQ26mpy(p->coeff_A2, p->filter_W2);

//

// Y(0) = Gain * (B0 * W0 + B1 * W1 + B2 * W2)

//

p->filter_out = _IQ26mpy(p->coeff_B0, p->filter_W0) + _IQ26mpy(p->coeff_B1, p->filter_W1) + _IQ26mpy(p->coeff_B2, p->filter_W2);

//

// Update last data

//

p->filter_W2 = p->filter_W1;

p->filter_W1 = p->filter_W0;

return(p->filter_out);

}

PR Control Coefficient Calculation, using floating-point numbers for input, makes debugging parameters much easier. The IQ26 coefficient calculations are performed within this function:

static inline void updata_pr_ctrl_coeff_IQ( volatile IIR_2ORD_IQ_DATA_DEF *p,

float32 kr,

float32 wc,

float32 wr,

float32 kp,

float32 ts)

{

// with KP gain

float32 ts_x_ts = ts * ts;

float32 wr_x_wr = wr * wr;

float32 div_x = ts * ts * wr * wr + 4.0f * wc * ts + 4.0f;

#if 1

p->coeff_B0 = _IQ26((4.0f * kp + kp * ts_x_ts * wr_x_wr + 4.0f * kp * ts * wc + 4.0f * kr * wc * ts)/div_x);

p->coeff_B1 = _IQ26(-1.0f * (8.0f * kp – 2.0f * kp * ts_x_ts * wr_x_wr)/div_x);

p->coeff_B2 = _IQ26((4.0f * kp + kp * ts_x_ts * wr_x_wr – 4.0f * kp * ts * wc – 4.0f * kr * ts * wc)/div_x);

p->coeff_A1 = _IQ26((2.0f * ts_x_ts * wr_x_wr – 8.0f)/div_x);

p->coeff_A2 = _IQ26((ts_x_ts * wr_x_wr – 4.0f * ts * wc + 4.0f)/div_x);

#else

// without KP gain

float32 coeff_B0 = (4.0f * kr * wc * ts)/div_x;

float32 coeff_B1 = 0;

float32 coeff_B2 = -1.0f * p->coeff_B0;

float32 coeff_A1 = (2.0f * ts_x_ts * wr_x_wr – 8.0f)/div_x;

float32 coeff_A2 = (ts_x_ts * wr_x_wr – 4.0f * ts * wc + 4.0f)/div_x;

p->coeff_B0 = _IQ(coeff_B0);

p->coeff_B1 = _IQ(coeff_B1);

p->coeff_B2 = _IQ(coeff_B2);

p->coeff_A1 = _IQ(coeff_A1);

p->coeff_A2 = _IQ(coeff_A2);

#endif

}

PR Data Structure Definition

typedef struct PR_CTRL_LAW_DATA_IQ_TAG{

IIR_2ORD_IQ_DATA_DEF f_1st;

IIR_2ORD_IQ_DATA_DEF f_3st;

IIR_2ORD_IQ_DATA_DEF f_5st;

IIR_2ORD_IQ_DATA_DEF lead_lag_comp;

_iq f1stOut;

_iq f3rdOut;

_iq f5thOut;

_iq max;

_iq min;

_iq pr_ctrl_out;

}PR_CTRL_IQ_DATA_DEF;

PR + Lead-Lag Compensation:

static inline _iq pr_ctrl_IQ_func( _iq error, PR_CTRL_IQ_DATA_DEF *p)

{

const _iq error_iq26 = _IQmpy4(error);

p->f1stOut = iir2_IQ_func_(error_iq26, &p->f_1st);

p->f3rdOut = iir2_IQ_func_(error_iq26, &p->f_3st);

p->f5thOut = iir2_IQ_func_(error_iq26, &p->f_5st);

p->pr_ctrl_out = (p->f1stOut + p->f3rdOut + p->f5thOut);

p->pr_ctrl_out = iir2_IQ_func_(p->pr_ctrl_out, &p->lead_lag_comp);

_IQsat(p->pr_ctrl_out, p->max, p->min);

return(_IQdiv4(p->pr_ctrl_out));

}

Overall Parameter Calculation for PR Control Function:

static void pr_ctrl_coeff_IQ_func(

PR_CTRL_IQ_DATA_DEF *p,

float32 grid_freq,

float32 kp,

float32 kr,

float32 ts,

float32 lead_fz,

float32 lead_fp,

float32 max,

float32 min)

{

updata_pr_ctrl_coeff_IQ(&p->f_1st, kr, 2.0f * M_PI, 2.0f * M_PI * grid_freq, kp, ts);

updata_pr_ctrl_coeff_IQ(&p->f_3st, kr, 2.0f * M_PI, 2.0f * M_PI * grid_freq * 3.0f, kp, ts);

updata_pr_ctrl_coeff_IQ(&p->f_5st, kr, 2.0f * M_PI, 2.0f * M_PI * grid_freq * 5.0f, kp, ts);

float32 ts_x_wz = ts * 2.0f * M_PI * lead_fz;

float32 ts_x_wp_puls_2 = ts * 2.0f * M_PI * lead_fp + 2.0f;

p->lead_lag_comp.coeff_B0 = _IQ((ts_x_wz + 2.0f)/ts_x_wp_puls_2);

p->lead_lag_comp.coeff_B1 = _IQ((ts_x_wz – 2.0f)/ts_x_wp_puls_2);

p->lead_lag_comp.coeff_B2 = 0;

p->lead_lag_comp.coeff_A1 = _IQ((ts * 2.0f * M_PI * lead_fp – 2.0f)/ts_x_wp_puls_2);

p->lead_lag_comp.coeff_A2 = 0;

p->max = _IQ(max);

p->min = _IQ(min);

p->pr_ctrl_out = 0;

}

Running Tests:

Implementing Fixed-Point Programming for DSP PR Controllers

Comparison with MATLAB:

The discretization coefficients of the PR function are compared; the upper part is the coefficients converted using MATLAB’s built-in functions, while the lower part shows the coefficients converted as described above. The results from the DSP show that after adapting to IQ26 conversion, the coefficients maintain high precision.

Implementing Fixed-Point Programming for DSP PR Controllers

Adapting the PR control with 135 iterations to recombine the lead-lag compensator to address the instability caused by phase jumps in the PR.

Implementing Fixed-Point Programming for DSP PR Controllers

My abilities are limited, and I am currently learning fixed-point programming. If there are any mistakes, please kindly point them out. Thank you.

Leave a Comment