MATLAB/Simulink中调用C语言实现的传递函数

发布时间 2023-11-17 17:14:29作者: 昨夜三更雨

1. 引言

在变流器控制中,通常采用C语言实现传递函数,且通常写成独立的C文件,本文简要介绍如何在MATLAB/Simulink中调用这些C文件。

在本文中,采用C语言实现了一阶低通滤波器、二阶低通滤波器、滑动平均滤波器,具体代码见附录。同时需要安装C编译工具链,参考《Using GCC with MinGW》

2. 接口代码文件准备

为调用已写好的C文件,需要做一层额外的封装供Simulink的MATLAB Function调用。本文将这一接口文件命名作porting.hporting.c

2.1 porting.c文件

在该文件中,主要提供两类函数:仿真运行开始时的初始化(init)函数、离散周期中调用的回调(callback)函数。这两类函数都是基于已有的文件进行封装的,一个原则就是不要动已有的文件。

#include "ufilter.h"
#include "porting.h"

/**
 * @brief Handler for the 1st-order LPF
 */
static ufilter_lp1st_t lp1st;
/**
 * @brief Hanlder for the 2nd-order LPF
 */
static ufilter_lp2nd_t lp2nd;
/**
 * @brief Handler for the moving average
 */
static float ma_buffer[260];
static ufilter_ma_t ma;


float porting_ufilter_lp1st_init_lp1st(void)
{
    ufilter_lp1st_init(&lp1st, 314, 0.0001);
}

float porting_ufilter_lp1st_callback_lp1st(float _x)
{
    float y;
    y = ufilter_lp1st_callback(&lp1st, _x);
    return y;
}

float porting_ufilter_lp2nd_init_lp2nd(void)
{
    ufilter_lp2nd_init(&lp2nd, 314, 0.5, 0.0001);
}

float porting_ufilter_lp2nd_callback_lp2nd(float _x)
{
    float y;
    y = ufilter_lp2nd_callback(&lp2nd, _x);
    return y;
}

float porting_ufilter_ma_init_ma(void)
{
    ufilter_ma_init(&ma, 260, ma_buffer);
}

float porting_ufilter_ma_callback_ma(float _x)
{
    float y;
    y = ufilter_ma_callback(&ma, _x, 200);
    return y;
}

2.2 porting.h文件

该文件的主要作用时将已经封装函数声明,供外部调用。

float porting_ufilter_lp1st_init_lp1st(void);
float porting_ufilter_lp1st_callback_lp1st(float _x);

float porting_ufilter_lp2nd_init_lp2nd(void);
float porting_ufilter_lp2nd_callback_lp2nd(float _x);

float porting_ufilter_ma_init_ma(void);
float porting_ufilter_ma_callback_ma(float _x);

3. Simulink调用及配置方法

3.1 初始化函数的调用

在如下位置调用porting.c文件中重新封装的初始化函数。

image

3.2 回调函数的调用

在Simulink中添加MATLAB Function模块,在MATLAB Function中调用porting.c文件中重新封装的回调函数,例如:

function y = lp1st(u)

y = 0;
y = coder.ceval("porting_ufilter_lp1st_callback_lp1st", u);

end

并将MATLAB Function模块的Update method设置为DiscreteSample Time设置为C代码中回调函数在实际系统中的采样周期。(在Simulink中MODELING选项卡下选择Model Explorer可打开以下窗口)

image

3.3 Simulink仿真文件配置

主要需要进行3个内容的配置:Include directories (C文件所在路径)、Header file(以代码方式添加)、Source file(列出C文件)。

image

image

image

4. 仿真效果

仿真模型如下:

image

运行结果如下,仿真结果正确:

image

附录

A. ufilter.h文件

/**
 *******************************************************************************
 * @file    ufilter.h
 * @author  xixizhk
 *******************************************************************************
 * @version V20210109 \n
 * Initial version.
 *******************************************************************************
 */


/* Define to prevent recursive inclusion **************************************/
#ifndef _UFILTER_H
#define _UFILTER_H

#ifdef __cplusplus
extern "C" {
#endif

/**
 *******************************************************************************
 * @addtogroup Includes
 * @{
 */

#include "stdint.h"

/**
 * @}
 */

/**
 *******************************************************************************
 * @addtogroup Definitions
 * @{
 */

/** Definition of the handler for the 1st lowpass filter. */
typedef struct
{
    /* Coefficients for differential equation */
    float b_0;
    float a_1;
    /* Intermediate variables */
    float y_k_1;
} ufilter_lp1st_t;

/** Definition of the handler for the 2nd lowpass filter. */
typedef struct
{
    /* Coefficients for differential equation */
    float b_1;
    float a_1;
    float a_2;
    /* Intermediate variables */
    float x_k_1;
    float y_k_1;
    float y_k_2;
} ufilter_lp2nd_t;

/* Definitions of the handler for the moving average. */
typedef struct
{
    /* Pointer to integral buffer. */
    float *buffer;
    /* Length of int_buffer. */
    unsigned int N;
    /* Index for callback. */
    unsigned int index;
    /* Offset for integral. */
    float offset;
} ufilter_ma_t;

/**
 * @}
 */

/**
 *******************************************************************************
 * @addtogroup Types
 * @{
 */

/**
 * @}
 */

/**
 *******************************************************************************
 * @addtogroup Constants
 * @{
 */

/**
 * @}
 */

/**
 *******************************************************************************
 * @addtogroup Variables
 * @{
 */

/**
 * @}
 */

/**
 *******************************************************************************
 * @addtogroup Macros
 * @{
 */

/**
 * @}
 */

/**
 *******************************************************************************
 * @addtogroup Functions
 * @{
 */

/* For the 1st lowpass filter. */
void ufilter_lp1st_init
(
    ufilter_lp1st_t *_ufilter,
    float _omega_c,
    float _Ts
);
float ufilter_lp1st_callback
(
    ufilter_lp1st_t *_ufilter,
    float _input
);
void ufilter_lp1st_reset
(
    ufilter_lp1st_t *_ufilter
);

/* For the 2nd lowpass filter. */
void ufilter_lp2nd_init
(
    ufilter_lp2nd_t *_ufilter,
    float _omega_n,
    float _zeta,
    float _Ts
);
float ufilter_lp2nd_callback
(
    ufilter_lp2nd_t *_ufilter,
    float _input
);
void ufilter_lp2nd_reset
(
    ufilter_lp2nd_t *_ufilter
);

/* For the moving average. */
void ufilter_ma_init
(
    ufilter_ma_t *_ufilter,
    unsigned int _N,
    float *_buffer
);
float ufilter_ma_callback
(
    ufilter_ma_t *_ufilter,
    float _input,
    unsigned int _W
);
void ufilter_ma_reset
(
    ufilter_ma_t *_ufilter
);

/**
 * @}
 */


#ifdef __cplusplus
}
#endif


#endif /* _UFILTER_H */

/**************************** ALL RIGHTS RESERVED *****************************/

B. ufilter.c文件

/**
 *******************************************************************************
 * @file    ufilter.c
 * @author  xixizhk
 *******************************************************************************
 * @version V20210109 \n
 * Initial version.
 *******************************************************************************
 */


/**
 *******************************************************************************
 * @addtogroup Includes
 * @{
 */

#include "ufilter.h"
#include "math.h"

/**
 * @}
 */

/**
 *******************************************************************************
 * @addtogroup Definitions
 * @{
 */

/**
 * @}
 */

/**
 *******************************************************************************
 * @addtogroup Types
 * @{
 */

/**
 * @}
 */

/**
 *******************************************************************************
 * @addtogroup Constants
 * @{
 */

/**
 * @}
 */

/**
 *******************************************************************************
 * @addtogroup Variables
 * @{
 */

/**
 * @}
 */

/**
 *******************************************************************************
 * @addtogroup Macros
 * @{
 */

/**
 * @}
 */

/**
 *******************************************************************************
 * @addtogroup Functions
 * @{
 */

/**
 * @brief   Initialize the handler of the 1st lowpass filter.
 * @param   _ufilter [In]: Pointer to the handler of the filter.
 * @param   _omega_c [In]: Cut frequency of the filter.
 * @param   _Ts [In]: Sample time.
 * @retval  None.
 */
void ufilter_lp1st_init
(
    ufilter_lp1st_t *_ufilter,
    float _omega_c,
    float _Ts
)
{
    /* Coefficients calculation */
    _ufilter->b_0 = _omega_c * _Ts;
    /**
     * Origin expression:
     *  _ufilter->a_1 = -exp(-(_omega_c * _Ts)),
     * which results in steady-state error.
     */
    _ufilter->a_1 = -(1.0F - _omega_c * _Ts);
    /* Reset intermediate variables */
    ufilter_lp1st_reset(_ufilter);
}

/**
 * @brief   Callback function of the 1st lowpass filter.
 * @param   _ufilter [In]: Pointer to the handler of the filter.
 * @param   _input [In]: Input of the filter.
 * @retval  Output of the filter.
 */
float ufilter_lp1st_callback
(
    ufilter_lp1st_t *_ufilter,
    float _input
)
{
    float output;
    /* Calculate output */
    output = _ufilter->b_0 * _input - _ufilter->a_1 * _ufilter->y_k_1;
    /* Update intermediate variables */
    _ufilter->y_k_1 = output;
    /* Return output */
    return output;
}

/**
 * @brief   Reset the handler of the 1st lowpass filter.
 * @param   _ufilter [In]: Pointer to the handler of the filter.
 * @retval  None.
 */
void ufilter_lp1st_reset
(
    ufilter_lp1st_t *_ufilter
)
{
    _ufilter->y_k_1 = 0;
}

/**
 * @brief   Initialize the handler of the 2nd lowpass filter.
 * @param   _ufilter [In]: Pointer to the handler of the filter.
 * @param   _omega_n [In]: Resonant frequency of the filter.
 * @param   _zeta [In]: Coefficient of damping.
 * @param   _Ts [In]: Sample time.
 * @retval  None.
 */
void ufilter_lp2nd_init
(
    ufilter_lp2nd_t *_ufilter,
    float _omega_n,
    float _zeta,
    float _Ts
)
{
    /* Coefficients calculation */
    _ufilter->b_1 = _omega_n * _Ts / sqrt(1 - _zeta * _zeta)
            * exp(-_zeta * _omega_n * _Ts)
            * sin(_omega_n * _Ts * sqrt(1 - _zeta * _zeta));
    _ufilter->a_1 = -2 * exp(-_zeta * _omega_n * _Ts)
            * cos(_omega_n * _Ts * sqrt(1 - _zeta * _zeta));
    _ufilter->a_2 = exp(-2 * _zeta * _omega_n * _Ts);
    /* Reset intermediate variables */
    ufilter_lp2nd_reset(_ufilter);
}

/**
 * @brief   Callback function of the 2nd lowpass filter.
 * @param   _ufilter [In]: Pointer to the handler of the filter.
 * @param   _input [In]: Input of the filter.
 * @retval  Output of the filter.
 */
float ufilter_lp2nd_callback
(
    ufilter_lp2nd_t *_ufilter,
    float _input
)
{
    float output;
    /* Calculate output */
    output = _ufilter->b_1 * _ufilter->x_k_1 - _ufilter->a_1 * _ufilter->y_k_1
            - _ufilter->a_2 * _ufilter->y_k_2;
    /* Update intermediate variables */
    _ufilter->x_k_1 = _input;
    _ufilter->y_k_2 = _ufilter->y_k_1;
    _ufilter->y_k_1 = output;
    /* Return output */
    return output;
}

/**
 * @brief   Reset the handler of the 2nd lowpass filter.
 * @param   _ufilter [In]: Pointer to the handler of the filter.
 * @retval  None.
 */
void ufilter_lp2nd_reset
(
    ufilter_lp2nd_t *_ufilter
)
{
    _ufilter->x_k_1 = 0;
    _ufilter->y_k_1 = 0;
    _ufilter->y_k_2 = 0;
}

/**
 * @brief   Initialize handler of specified moving average handler.
 * @param   _ufilter [In]: Pointer to the handler of moving average.
 * @param   _N [In]: Length of integral buffer.
 * @param   _buffer [In]: Pointer to integral buffer.
 * @retval  None.
**/
void ufilter_ma_init
(
    ufilter_ma_t *_ufilter,
    unsigned int _N,
    float *_buffer
)
{
    unsigned int i;
    _ufilter->N = _N;
    _ufilter->buffer = _buffer;
    _ufilter->index = 0;
    _ufilter->offset = 0;
    for (i = 0; i < _N; i++)
    {
        *(_ufilter->buffer + i) = 0;
    }
}

/**
 * @brief   Callback function of specified moving average handler to
 *          calculate the output.
 * @param   _ufilter [In]: Pointer to the handler of moving average.
 * @param   _input [In]: Input of moving average.
 * @param   _W [In]: Length of window of moving average (1 <= W <= N).
 * @retval  Value of moving average.
**/
float ufilter_ma_callback
(
    ufilter_ma_t *_ufilter,
    float _input,
    unsigned int _W
)
{
    float tmp_float;

    /* moving average.*/
    float ave;
    /* Integral value at the end of the window and its index. */
    float int_W;
    unsigned int int_W_index;

    /* Limit the window. */
    if (_W > _ufilter->N)
    {
        _W = _ufilter->N;
    }
    if (_W == 0)
    {
        _W = 1;
    }

    /* Update callback index and calculate integral. */
    tmp_float = *(_ufilter->buffer + _ufilter->index);
    _ufilter->index = (_ufilter->index + 1U) % _ufilter->N;
    int_W_index = (_ufilter->index + _ufilter->N - _W) % _ufilter->N;
    int_W = *(_ufilter->buffer + int_W_index);
    *(_ufilter->buffer + _ufilter->index) = tmp_float + _input;

    /* Determine the offset and add to the buffer. */
    if ( _ufilter->index == 0U)
    {
        _ufilter->offset = *(_ufilter->buffer + _ufilter->index);
        *(_ufilter->buffer + _ufilter->index) = 0.0F;
    }

    /* Calculate moving average. */
    if (int_W_index >= _ufilter->index)
    {
        int_W -= _ufilter->offset;
    }
    ave = (*(_ufilter->buffer + _ufilter->index) - int_W) / ((float)_W);

    /* Return the average. */
    return ave;
}

/**
 * @brief   Reset the handler of the moving average filter.
 * @param   _ufilter [In]: Pointer to the handler of the filter.
 * @retval  None.
 */
void ufilter_ma_reset
(
    ufilter_ma_t *_ufilter
)
{
    unsigned int i;
    for (i = 0; i < _ufilter->N; i++)
    {
        *(_ufilter->buffer + i) = 0.0F;
    }
}

/**
 * @}
 */


/**************************** ALL RIGHTS RESERVED *****************************/

参考文献

[1] Rlover_star. Simulink调用外部C代码的几种方法. 2023.11.17. https://blog.csdn.net/Rlover_star/article/details/127882271