目录
毕设答辩完了,终于有时间水文章了
几年前了解到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扫描出来的即为功率级的传递函数。

系统开环传递函数
当注入点为参考信号时,扫描出来的传函为。经过运算后即可得到开环传函:
具体推导可以去看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
cvoid 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执行一次
cvoid 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()中设置中初始化:
cLowPassFilter_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分解,再利用公式:
可以解出 和
其中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
};
然后初始化,初始化完成就直接启动扫频。
cvoid sfra_init()
{
sfra_init_all();
sfra_start(&libsfra);
}
执行后台任务并轮询扫频是否完成,扫频完成就通过串口将数据打印出来
cvoid 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);
}
后台任务:
cvoid 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 许可协议。转载请注明出处!