跳到正文
SFRA移植与LIBSFRA对接TI上位机
2026-06-12
电力电子软件
00

目录

SFRA
SFRA的原理
功率级传递函数
系统开环传递函数
SFRA的移植
拷贝文件
定义变量
测试SFRA
连接TI官方上位机
LIBSFRA
移植
测试
对接TI上位机
上位机连接测试
更进一步?
移植到别的平台
参考

毕设答辩完了,终于有时间水文章了
几年前了解到TI的SFRA,是一个用于扫描功率级传递函数或者系统开环传递函数的库。根据TI的介绍,SFRA能够快速测量数字功率变换器的频率响应,便于数字环路的开发。这篇文章记录一下SFRA如何移植和使用,以及六花大佬开源的LIBSFRA如何使用,并且ntr到TI的上位机上。

SFRA

SFRA (Software Frequency Response Analyzer)是TI开发用于系统频率响应分析的软件库。可识别高频功率系统,分析系统闭合环路的开环特性,从而用户可根据得到的幅、相频率特性曲线来评估系统性能。
Compensation Designer与SFRA配合使用,用户可设计并优化所选补偿器的参数,界面会显示Plant, Open Loop 和Compensator的频率响应曲线。
TI还是有挺多参考设计用了SFRA去分析系统闭合环路的开环特性的:
PMP41006:

TIDM-02002:

TIDA-010054:

SFRA的原理

通常来说,数字闭环控制变换器可以分为两部分,一个是电路功率拓扑,即系统控制对象H(s),另一个是补偿器G(z)。

SFRA的原理简单来说,就是在变换器的某点的稳态直流工作点上注入一个小幅度正弦信号,再测量系统输出(上图中的y)与注入信号的相位差和幅度之比。然后改变注入信号的频率,将不同注入频率的结果收集起来。
通过选择合适的注入点,最后就能得到功率级和系统的开环传递函数。

功率级传递函数

当断开G(z)开环运行,注入点为PWM调制信号时,SFRA扫描出来的即为功率级的传递函数H(s)=yuH(s)=\frac{y}{u}

系统开环传递函数

当注入点为参考信号时,扫描出来的传函为yr=H(s)G(s)1+H(s)G(s)\frac{y}{r}=\frac{H(s)G(s)}{1+H(s)G(s)}。经过运算后即可得到开环传函:

H(s)G(s)=yryH(s)G(s)=\frac{y}{r-y}

具体推导可以去看SFAR开发者写的论文:Online Frequency Response Analysis: A Powerful Plug-in Tool for Compensation Design and Health Assessment of Digitally Controlled Power Converters

SFRA的移植

拷贝文件

移植步骤参考TI的User's Guide:SPRUIK4A
首先将C2000Ware DigitalPower SDK中的相关文件拷到工程中:

本次移植所用的DSP为28379D,所以选择的库文件为sfra_f32_tmu_eabi.lib,可以使用TMU进行加速。
所有移植文件如下:

将对应的.lib文件路径和.c文件路径加入编译器的include路径中。

定义变量

新建一个sfra_test.c文件,定义SFRA所需变量:

c
// sfra变量定义 SFRA_F32 ti_sfra; #define CONTROL_ISR_FREQUENCY ((float32_t)100 * 1000) // 100KHz #define SFRA_ISR_FREQ CONTROL_ISR_FREQUENCY #define SFRA_FREQ_START 10 // // SFRA step Multiply = 10^(1/No of steps per decade(40)) // #define SFRA_FREQ_STEP_MULTIPLY (float32_t)1.584893192461113 #define SFRA_AMPLITUDE (float32_t)0.1 #define SFRA_FREQ_LENGTH 100 float32_t plantMagVect[SFRA_FREQ_LENGTH]; float32_t plantPhaseVect[SFRA_FREQ_LENGTH]; float32_t olMagVect[SFRA_FREQ_LENGTH]; float32_t olPhaseVect[SFRA_FREQ_LENGTH]; float32_t clMagVect[SFRA_FREQ_LENGTH]; float32_t clPhaseVect[SFRA_FREQ_LENGTH]; float32_t freqVect[SFRA_FREQ_LENGTH]; // //extern to access tables in ROM // extern long FPUsinTable[]; // 通信串口,LED #define SFRA_GUI_SCI_BASE SCIA_BASE #define SFRA_GUI_VBUS_CLK DEVICE_LSPCLK_FREQ #define SFRA_GUI_SCI_BAUDRATE 115200 #define SFRA_GUI_SCIRX_GPIO 43 #define SFRA_GUI_SCITX_GPIO 42 #define SFRA_GUI_SCIRX_GPIO_PIN_CONFIG GPIO_43_SCIRXDA #define SFRA_GUI_SCITX_GPIO_PIN_CONFIG GPIO_42_SCITXDA #define SFRA_GUI_LED_INDICATOR 1 #define SFRA_GUI_LED_GPIO 31 #define SFRA_GUI_LED_GPIO_PIN_CONFIG GPIO_31_GPIO31

然后初始化SFRA

c
void sfra_init() { SFRA_F32_reset(&ti_sfra); SFRA_F32_config(&ti_sfra, SFRA_ISR_FREQ, SFRA_AMPLITUDE, SFRA_FREQ_LENGTH, SFRA_FREQ_START, SFRA_FREQ_STEP_MULTIPLY, plantMagVect, plantPhaseVect, olMagVect, olPhaseVect, clMagVect, clPhaseVect, freqVect, 1); SFRA_F32_resetFreqRespArray(&ti_sfra); SFRA_F32_initFreqArrayWithLogSteps(&ti_sfra, SFRA_FREQ_START, SFRA_FREQ_STEP_MULTIPLY); SFRA_GUI_config(SFRA_GUI_SCI_BASE, SFRA_GUI_VBUS_CLK, SFRA_GUI_SCI_BAUDRATE, SFRA_GUI_SCIRX_GPIO, SFRA_GUI_SCIRX_GPIO_PIN_CONFIG, SFRA_GUI_SCITX_GPIO, SFRA_GUI_SCITX_GPIO_PIN_CONFIG, SFRA_GUI_LED_INDICATOR, SFRA_GUI_LED_GPIO, SFRA_GUI_LED_GPIO_PIN_CONFIG, &ti_sfra, SFRA_GUI_PLOT_GH_H); }

添加后台任务,放在while(1)中运行,模拟1ms执行一次

c
void sfra_task_run() { DEVICE_DELAY_US(1.0f *1000); SFRA_F32_runBackgroundTask(&ti_sfra); SFRA_GUI_runSerialHostComms(&ti_sfra); }

在cmd文件中添加SFRA所需的段

c
// 添加 SFRA 库需要的段 SFRA_F32_Data : > RAMGS0, PAGE = 1

浮点模式设置为relaxed,启用TMU加速

测试SFRA

先随便配置一个CPUTIMER触发一个100kHz的中断,在sfra_init()中设置中断频率:

c
CPUTimer_setPeriod(CPUTIMER0_BASE, (DEVICE_SYSCLK_FREQ / CONTROL_ISR_FREQUENCY) - 1); CPUTimer_startTimer(CPUTIMER0_BASE);

因为目前手里只有一块LaunchPad,所以先暂时使用一个一阶低通滤波器测试SFRA:
lowpass.h

c
#ifndef LOWPASS_H #define LOWPASS_H #include <math.h> // 一阶低通滤波器结构体 typedef struct { float b0, b1; // 分子系数 float a1; // 分母系数 (a0 归一化为 1) float x1; // 上一个输入样本 float y1; // 上一个输出样本 } LowPassFilter_t; /** * 初始化低通滤波器 * @param f 滤波器结构体指针 * @param fs 采样频率 (Hz) * @param fc 截止频率 (-3dB 频率) (Hz) */ static inline void LowPassFilter_Init(LowPassFilter_t *f, float fs, float fc) { // 预畸变角频率: w = 2 * pi * fc / fs, 然后 tan(w/2) float wc = 2.0f * M_PI * fc / fs; float tan_wc2 = tanf(wc * 0.5f); float den = 1.0f + tan_wc2; f->b0 = tan_wc2 / den; f->b1 = f->b0; f->a1 = (tan_wc2 - 1.0f) / den; f->x1 = 0.0f; f->y1 = 0.0f; } /** * 运行低通滤波器 * @param f 滤波器结构体指针 * @param x 当前输入样本 * @return 滤波后的输出 */ static inline float LowPassFilter_Run(LowPassFilter_t *f, float x) { // y[n] = b0*x[n] + b1*x[n-1] - a1*y[n-1] float y = f->b0 * x + f->b1 * f->x1 - f->a1 * f->y1; f->x1 = x; f->y1 = y; return y; } #endif // LOWPASS_H

截至频率设置为10kHz:

c
// lowpass filter LowPassFilter_t lowPass_test; #define FS CONTROL_ISR_FREQUENCY #define FC 10.0f * 1000

同样在sfra_init()中设置中初始化:

c
LowPassFilter_Init(&lowPass_test, FS, FC);

然后在中断中运行滤波器和SFRA相关代码:

c
__interrupt void TIMER0_ISR() { static float32_t input_dc = 0.8f; float32_t plant_input; float32_t plant_output; plant_input = SFRA_F32_inject(input_dc); // 直通,用于测试SFRA,plant扫描结果应为0°,0db // plant_output = plant_input; plant_output = LowPassFilter_Run(&lowPass_test, plant_input); SFRA_F32_collect(&plant_input, &plant_output); Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP1); }

Note

TI在声明SFRA_F32_collect()时给形参起的名字真的很令人费解,为什么要输入的量是control_output和feedback,直觉上应该是扰动输入in和系统输出out才对。在给出的示例代码中,在扫描功率级和开环函数的时候,分别使用的这两句代码:

c
// H(s) SFRA_F32_collect(&Duty_pu,&Vout1_Read); // H(S)G(S) SFRA_F32_collect(&Out,&Fdbk); //Out就是上面的占空比Duty_pu,Fdbk就是上面的Vout1_Read

对于功率级来说,Duty_pu是系统输入,Vout1_Read是系统输出,在SPRUIK4A第21页也说到,此时测量的是Vout/Duty的传递函数。因此可以推断SFRA_F32_collect收集参数后,计算的是系统输出/扰动输入的传递函数。
那为什么闭环测量时收集的仍然是Duty和Vout呢?翻了下上面的论文,是这样解释的:

注入点在闭环系统的参考端,测量控制器输出 u(即占空比)和反馈 y(即Vout),通过DFT分解,再利用公式:

y=GH1+GHry= \frac{GH}{1+GH}r
u=G1+GHru=\frac{G}{1+GH}r

可以解出 GH=yryGH=\frac{y}{r-y}H=yuH=\frac{y}{u}

其中r为注入的扰动,对于sfra内部是已知信号,因此没有收集这个数据。所以ti在测量闭环系统时仍然选择收集Duty和Vout,是为了运行一次扫频就得到GH和H。

连接TI官方上位机

TI官方的上位机位于sfra库目录下的gui子文件夹中

选择浮点模式

点击右下角Setup,填入对应的波特率和端口,取消勾选Boot on connect

将程序下载到dsp中,点击右下角的connect。连接成功后,左侧会显示当前的扫频配置以及是否将接收到的数据保存到csv文件中,左下角会显示当前的连接状态。如果SFRA_GUI_config()中LED的管脚没有配置错的话,此时LED会以大约1Hz的频率闪烁。如果连接不上请检查端口和波特率。

点击Start Sweep开始扫频,同时下面的进度条会显示当前进度

扫描完成后,上位机就会接收数据,然后显示在右边。如果勾选了Save SFRA Data as CSV,就会在gui文件夹中保存一份CSV格式的数据,便于导入其他工具中使用。

箭头所指光标处的频率为9.817kHz,增益-2.93dB,相移-44.44°,和前面设置的10kHz LP基本完全一致。

只扫描Plant时,Open Loop的bode图并没有实际意义。

一些小BUG

虽然上位机左侧的Start Frequency、Steps Per Decade和Injection Amplitude能够修改,并且修改后能同步更新到SFRA_TYPE结构体 中,但是存储频率向量的数组freqVect[]中的数据因为某些BUG好像并不会随之更新。因此就算在上位机中更改了扫频设置,最后接收到的数据任然是按照原先代码中的配置运行得到的结果。

LIBSFRA

TI的SFRA虽然好用,但是由于TI只提供了闭源的.lib文件,导致SFRA基本只能在C28x内核的DSP上使用(TI甚至没给新一代的C29x内核做适配),也不能移植到其他平台上面。
某次上网冲浪,偶然间找到了六花大佬开源的LIBSFRA,用这个库就能随便移植了。

移植

首先创建一个libsfra_config.h配置LIBSFRA

c
#ifndef INC_LIBSFRA_CONFIG_H_ #define INC_LIBSFRA_CONFIG_H_ // 使用浮点模式 #define SFRA_INT 0 // 不需要内置的测试代码 #define SFRA_HAS_TEST 0 #ifndef SFRA_RAMFUNC #define SFRA_RAMFUNC(functionName) #endif #endif /* INC_LIBSFRA_CONFIG_H_ */

sfra_test.c中定义LIBSFRA所需变量

c
#define SFRA_BUF_SIZE 100 typedef struct { float freq[SFRA_BUF_SIZE]; // In Hz, log-spaced float mag[SFRA_BUF_SIZE]; // In dB float phase[SFRA_BUF_SIZE]; // In degrees } sfra_results_t; // Holds measured bode plot sfra_results_t libsfra_results; // Declare the SFRA instance sfra_t libsfra = { .config.isrFreq = CONTROL_ISR_FREQUENCY, // Sampling frequency .config.freqStart = 10, // Start frequency .config.freqStep = 1.584893192461113, // Frequency step (log-spaced) .config.vecLength = SFRA_BUF_SIZE, // Number of sweeps .results.freqVect = libsfra_results.freq, // Points to the frequency vector .results.magnitudeVect = libsfra_results.mag, // Points to the magnitude vector .results.phaseVect = libsfra_results.phase // Points to the phase vector };

然后初始化,初始化完成就直接启动扫频。

c
void sfra_init() { sfra_init_all(); sfra_start(&libsfra); }

执行后台任务并轮询扫频是否完成,扫频完成就通过串口将数据打印出来

c
void sfra_task_run() { DEVICE_DELAY_US(1.0f *1000); sfra_background_task(&libsfra); if(sfra_is_done(&libsfra)) { sfra_print_results_csv(&libsfra_results, SFRA_BUF_SIZE); sfra_clear_done(&libsfra); } }

中断中执行

c
__interrupt void TIMER0_ISR() { static float32_t input_dc = 0.8f; float32_t plant_input; float32_t plant_output; plant_input = input_dc + 0.1f * sfra_inject(&libsfra); plant_output = LowPassFilter_Run(&lowPass_test, plant_input); sfra_monitor(&libsfra, plant_input, plant_output); Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP1); }

测试

随便找个上位机接收串口数据,然后用MatLab画一下bode图:

可以看出来,扫出来的结果和TI的SFRA基本一致,也符合10kHz LP的特性。

对接TI上位机

LIBSFRA并没有提供类似TI的SFRA GUI那样的上位机,在使用的时候得另外想法将扫频数据存储到本地的csv文件中。因为不想重复造轮子搞上位机,所以我选择将LIBSFAR强兼到TI的上位机上。
TI的SFRA与上位机交互依靠的是sfra_gui_scicomms_driverlib.c,为了避免搞出来奇怪的BUG,这里不修改这个文件,而是在LIBSFRA外再“包一层”代码模拟SFRA的行为,然后与sfra_gui_scicomms_driverlib.c交互。
让ai简单写了一段代码:
libsfra_ti_hal.h

c
#ifndef LIBSFRA_TI_HAL_H #define LIBSFRA_TI_HAL_H #include "sfra_f32.h" #include "libsfra.h" #ifdef __cplusplus extern "C" { #endif void libsfra_ti_hal_init(sfra_t *libsfra, float32_t isrFrequency, float32_t injectionAmplitude, int16_t noFreqPoints, float32_t fraSweepStartFreq, float32_t freqStep, float32_t *h_magVect, float32_t *h_phaseVect, float32_t *gh_magVect, float32_t *gh_phaseVect, float32_t *cl_magVect, float32_t *cl_phaseVect, float32_t *freqVect ); /** * 获取 TI 适配器结构体指针,用于传给 SFRA_GUI_config */ SFRA_F32* libsfra_ti_hal_get_adapter(void); #ifdef __cplusplus } #endif #endif

libsfra_ti_hal.c

c
#include "libsfra_ti_hal.h" #include <string.h> // 静态适配器实例 static SFRA_F32 g_sfra_adapter; static sfra_t *g_libsfra = NULL; // 标志,用于检测 start 边沿 static int16_t g_last_start = 0; // 需要同步的参数(上位机通过命令修改) static float32_t g_last_amplitude; // 内部函数:同步 libsfra 参数 static void sync_parameters(void) { if (g_sfra_adapter.amplitude != g_last_amplitude) { g_last_amplitude = g_sfra_adapter.amplitude; } if(g_sfra_adapter.freqStart != g_libsfra->config.freqStart){ g_libsfra->config.freqStart = g_sfra_adapter.freqStart; } if(g_sfra_adapter.freqStep != g_libsfra->config.freqStep){ g_libsfra->config.freqStep = g_sfra_adapter.freqStep; } } // 检查 start 标志并启动扫频 static void check_start_flag(void) { if (g_sfra_adapter.start == 1 && g_last_start == 0) { sfra_start(g_libsfra); g_sfra_adapter.start = 0; // 清除,避免重复触发 } g_last_start = g_sfra_adapter.start; } // 更新状态和频率索引 static void update_status(void) { g_sfra_adapter.freqIndex = g_libsfra->internal_state.freqIndex; if (sfra_is_running(g_libsfra)) { g_sfra_adapter.status = 1; // 运行中 } else if (sfra_is_done(g_libsfra)) { g_sfra_adapter.status = 2; // 完成 sfra_clear_done(g_libsfra); } else { g_sfra_adapter.status = 0; // 空闲 } } // 注入函数,返回 ref + 扰动 float SFRA_F32_inject(float ref) { float perturb = sfra_inject(g_libsfra); // 返回 -1..1 return ref + g_sfra_adapter.amplitude * perturb; } // 收集函数 void SFRA_F32_collect(float *control_output, float *feedback) { sfra_monitor(g_libsfra, *control_output, *feedback); } // 后台任务 void SFRA_F32_runBackgroundTask(SFRA_F32 *obj) { (void)obj; // 不使用参数,直接用全局 sync_parameters(); check_start_flag(); sfra_background_task(g_libsfra); update_status(); } // ------------------------------------------------------------ // 初始化适配器 // ------------------------------------------------------------ void libsfra_ti_hal_init(sfra_t *libsfra, float32_t isrFrequency, float32_t injectionAmplitude, int16_t noFreqPoints, float32_t fraSweepStartFreq, float32_t freqStep, float32_t *h_magVect, float32_t *h_phaseVect, float32_t *gh_magVect, float32_t *gh_phaseVect, float32_t *cl_magVect, float32_t *cl_phaseVect, float32_t *freqVect ) { g_libsfra = libsfra; g_libsfra->config.isrFreq = isrFrequency; g_libsfra->config.freqStart = fraSweepStartFreq; g_libsfra->config.freqStep = freqStep; g_libsfra->config.vecLength = noFreqPoints; g_libsfra->results.freqVect = freqVect; g_libsfra->results.magnitudeVect = h_magVect; g_libsfra->results.phaseVect = h_phaseVect; // 清空适配器结构体 memset(&g_sfra_adapter, 0, sizeof(SFRA_F32)); // 配置指针 g_sfra_adapter.freqVect = freqVect; g_sfra_adapter.h_magVect = h_magVect; g_sfra_adapter.h_phaseVect = h_phaseVect; // H(s) 和 G(s)H(s) 的数据存储在同一个数组中 g_sfra_adapter.gh_magVect = h_magVect; g_sfra_adapter.gh_phaseVect= h_phaseVect; g_sfra_adapter.cl_magVect = cl_magVect; g_sfra_adapter.cl_phaseVect= cl_phaseVect; g_sfra_adapter.vecLength = noFreqPoints; g_sfra_adapter.isrFreq = isrFrequency; g_sfra_adapter.amplitude = injectionAmplitude; g_sfra_adapter.freqStart = fraSweepStartFreq; g_sfra_adapter.freqStep = freqStep; g_sfra_adapter.speed = 1; // 默认速度 g_sfra_adapter.storeH = 1; // 存储 plant 数据 g_sfra_adapter.storeGH = 0; g_sfra_adapter.storeCL = 0; g_sfra_adapter.start = 0; g_sfra_adapter.state = 0; g_sfra_adapter.status = 0; g_sfra_adapter.freqIndex = 0; // 同步内部影子变量 g_last_amplitude = injectionAmplitude; g_last_start = 0; } SFRA_F32* libsfra_ti_hal_get_adapter(void) { return &g_sfra_adapter; }

由于LIBSFRA只能计算out/in的传函,所以只能用于测量功率级H(s),无法直接测量开环传函G(s)H(s)。这里将TI的SFRA中的G(s)H(s)也指向g_libsfra->results。

有了这个兼容层,LIBSFRA用法就和TI的差不多了。
初始化:

c
// sfra变量定义 SFRA_F32 ti_sfra; #define CONTROL_ISR_FREQUENCY ((float32_t)100 * 1000) // 100KHz #define SFRA_ISR_FREQ CONTROL_ISR_FREQUENCY #define SFRA_FREQ_START 10 // // SFRA step Multiply = 10^(1/No of steps per decade(40)) // #define SFRA_FREQ_STEP_MULTIPLY (float32_t)1.584893192461113 #define SFRA_AMPLITUDE (float32_t)0.1 #define SFRA_FREQ_LENGTH 100 float32_t plantMagVect[SFRA_FREQ_LENGTH]; float32_t plantPhaseVect[SFRA_FREQ_LENGTH]; float32_t olMagVect[SFRA_FREQ_LENGTH]; float32_t olPhaseVect[SFRA_FREQ_LENGTH]; float32_t clMagVect[SFRA_FREQ_LENGTH]; float32_t clPhaseVect[SFRA_FREQ_LENGTH]; float32_t freqVect[SFRA_FREQ_LENGTH]; // //extern to access tables in ROM // extern long FPUsinTable[]; // 通信串口,LED #define SFRA_GUI_SCI_BASE SCIA_BASE #define SFRA_GUI_VBUS_CLK DEVICE_LSPCLK_FREQ #define SFRA_GUI_SCI_BAUDRATE 115200 #define SFRA_GUI_SCIRX_GPIO 43 #define SFRA_GUI_SCITX_GPIO 42 #define SFRA_GUI_SCIRX_GPIO_PIN_CONFIG GPIO_43_SCIRXDA #define SFRA_GUI_SCITX_GPIO_PIN_CONFIG GPIO_42_SCITXDA #define SFRA_GUI_LED_INDICATOR 1 #define SFRA_GUI_LED_GPIO 31 #define SFRA_GUI_LED_GPIO_PIN_CONFIG GPIO_31_GPIO31 // hal层变量 sfra_t hal_sfra; void sfra_init() { // 初始化 HAL 适配层 libsfra_ti_hal_init(&hal_sfra, SFRA_ISR_FREQ, SFRA_AMPLITUDE, SFRA_FREQ_LENGTH, SFRA_FREQ_START, SFRA_FREQ_STEP_MULTIPLY, plantMagVect, plantPhaseVect, olMagVect, olPhaseVect, clMagVect, clPhaseVect, freqVect); SFRA_GUI_config(SFRA_GUI_SCI_BASE, SFRA_GUI_VBUS_CLK, SFRA_GUI_SCI_BAUDRATE, SFRA_GUI_SCIRX_GPIO, SFRA_GUI_SCIRX_GPIO_PIN_CONFIG, SFRA_GUI_SCITX_GPIO, SFRA_GUI_SCITX_GPIO_PIN_CONFIG, SFRA_GUI_LED_INDICATOR, SFRA_GUI_LED_GPIO, SFRA_GUI_LED_GPIO_PIN_CONFIG, libsfra_ti_hal_get_adapter(), SFRA_GUI_PLOT_GH_H); }

后台任务:

c
void sfra_task_run() { DEVICE_DELAY_US(1.0f *1000); SFRA_F32_runBackgroundTask(libsfra_ti_hal_get_adapter()); SFRA_GUI_runSerialHostComms(libsfra_ti_hal_get_adapter()); }

中断:

c
__interrupt void TIMER0_ISR() { static float32_t input_dc = 0.8f; float32_t plant_input; float32_t plant_output; plant_input = SFRA_F32_inject(input_dc); // 直通,用于测试SFRA,plant扫描结果应为0°,0db // plant_output = plant_input; plant_output = LowPassFilter_Run(&lowPass_test, plant_input); SFRA_F32_collect(&plant_input, &plant_output); Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP1); }

此时用到的文件为

上位机连接测试

设置好端口和波特率,连接上SFRA GUI,Plant扫频结果如下:

此时的Open Loop结果和Plant一样:

所以这样强兼之后就只能用来扫描功率级H(s)了。不过还有一点好处就是,LIBSFRA生成频率向量采用的是扫完一个频点再计算下个频点,这样上位机左侧更改的参数就能正确同步到实际扫频配置中了。

更进一步?

假如你一不小心打开CMD,脸滚键盘不小心打出来一串命令

再一不小心就能得到汇编文件

最不小心的是搞出来了符号表和重定位信息

然后把这些莫名其妙冒出来的文件给AL大人,就能获得一个sfra_f32.c

聪明的你把sfra_f32_tmu_eabi.lib删了,换成sfra_f32.c
咦,竟然能编译过去。
GUI软件自己突然跑起来了,结果如下:

这次仍然扫描的是10kHz的LP。上面是Plant的结果,下面是Open Lopp(此时的Open Loop结果没有实际意义,这里只用作比较);左侧是sfra_f32_tmu_eabi.lib跑出来的结果,右边是sfra_f32.c的结果。看得出来,在奈奎斯特频率之前,二者基本一致。
这里还让ai修复了上位机数据同步失败的特性,上位机更改数据后会立即更新相应参数。

移植到别的平台

如果要移植到别的平台,需要关注的只有sfra_gui_scicomms_driverlib.c中与串口和GPIO有关的函数,然后根据平台特性自行实现。

c
/* SCI 模块接口 */ void SCI_setConfig(uint32_t sci_base, uint32_t vbus_clk, uint32_t baudrate, uint16_t config); void SCI_enableModule(uint32_t sci_base); void SCI_performSoftwareReset(uint32_t sci_base); void SCI_clearInterruptStatus(uint32_t sci_base, uint16_t flags); void SCI_enableInterrupt(uint32_t sci_base, uint16_t flags); void SCI_disableLoopback(uint32_t sci_base); void SCI_writeCharBlockingNonFIFO(uint32_t sci_base, uint8_t data); uint16_t SCI_getRxStatus(uint32_t sci_base); uint8_t SCI_readCharBlockingNonFIFO(uint32_t sci_base); uint16_t SCI_isTransmitterBusy(uint32_t sci_base); /* GPIO 接口 */ void GPIO_setPinConfig(uint32_t pinConfig); void GPIO_setDirectionMode(uint32_t gpio_pin, uint16_t mode); void GPIO_setQualificationMode(uint32_t gpio_pin, uint16_t qual); void GPIO_togglePin(uint32_t gpio_pin);

最后附上神秘链接:SFRA_F32

参考

SPRUIK4A
LIBSFRA
TMS320F28035软件频率响应分析仪(SFRA)的使用
4.1 SFRA软件分析 - 工作原理
你还在盲调PI参数吗?SFRA加速你的调试进度!

本文作者:zxcli

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

正在加载文章碎片...