A software-based small signal analysis and measurement tool for digital controlled power supplies and more.
- Platform-independent, this library can run on any hardware, F28379 and STM32F334 have been tested. Theoretically, it will work on the STM32F0 series, even on 8-bit microcontrollers.
- High performance and minimal negative impact, functions that ISR routines may call contains minimal calculations.
- FPU is not compulsory, functions that ISR routines may call can be fixed-point only.
- MODBUS front-end and MATLAB scripts are available, data transfer is much faster than TI's closed-source implementation.
- Written in C99, works on most compilers.
- Multiple SFRA instances can co-exist.
- Utilization of local hardware accelerations is possible, e.g., trigonometric functions can be faster and more accurate on some C2000 platforms (e.g., F28379).
Add all .c and .h files to the Makefile or your managed project, steps may vary, depend on your IDE, build tools, and toolchains.
The following example shows how to add this library to the Makefile generated by STM32CubeIDE:
C_SOURCES = \
$(wildcard libsfra/*.c) \
...
C_INCLUDES = \
-Ilibsfra \
...
Create a file named "libsfra_config.h":
#ifndef INC_LIBSFRA_CONFIG_H_
#define INC_LIBSFRA_CONFIG_H_
// Use fixed-point
#define SFRA_INT 1
// No tests
#define SFRA_HAS_TEST 0
#endif /* INC_LIBSFRA_CONFIG_H_ */
// Size of each SFRA result buffer field
#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 sfra_results;
// Declare the SFRA instance
sfra_t sfra = {
.config.isrFreq = 100000, // 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 = sfra_results.freq, // Points to the frequency vector
.results.magnitudeVect = sfra_results.mag, // Points to the magnitude vector
.results.phaseVect = sfra_results.phase // Points to the phase vector
};
freqStep
is always 10 powers the reciprocal of the sweep number per decade.
The stop frequency can be calculated as freqEnd = freqStart * power(freqStep, vecLength - 1)
.
Given freqStep
and freqStart
, one can find the closest vecLength
from a given stop frequency (freqEndExpected
):
floor(log(freqEndExpected/freqStart) / log(freqStep) + 1)
.
Note that vecLength
should never exceed the buffer size (SFRA_BUF_SIZE
, in this case).
void main(void) {
...
// Initialize common SFRA infrastructures
sfra_init_all();
...
while (1) { // Main Loop
...
// Run background tasks for each SFRA instance
sfra_background_task(&sfra);
...
}
}
The following code demonstrates how to measure the duty cycle to output voltage transfer function in a DC/DC converter.
// Measured output voltage, populated by ADC + DMA
float adc_result_output_voltage;
// Normally this is an ISR routine
void control_law_run(void) {
// Duty cycle
float duty = 1.0F;
// Generate perturbation
float preturb_magnitude = 1.0F;
float perturb = sfra_inject_int32(&sfra) / (float) FAST_SIN_TABLE_SCALE;
// Add perturbation
duty += preturb_magnitude * perturb;
// Scale both for a better accuracy
float mon_in = duty * (float) 256.0F;
float mon_out = adc_result_output_voltage * (float) 256.0F;
// Update SFRA state
sfra_monitor_int32(&sfra, mon_in, mon_out);
}
Alternatively, the digital filter in filter_rc.h
can be used to test the SFRA without an actual power stage:
const float dc_gain = 100.0F;
// Normally this is an ISR routine
void control_law_run(void) {
float filter_input = 1.0F;
// Generate perturbation
float preturb_magnitude = 1.0F;
float perturb = sfra_inject_int32(&sfra) / (float) FAST_SIN_TABLE_SCALE;
// Add perturbation
filter_input += preturb_magnitude * perturb;
// Run the filter
float filter_output = dc_gain * vFilterRCRun(&test_filter, filter_input);
// Scale both for a better accuracy
float mon_in = filter_input * (float) 256.0F;
float mon_out = filter_output * (float) 256.0F;
// Update SFRA state
sfra_monitor_int32(&sfra, mon_in, mon_out);
}
Call sfra_start()
in a non-ISR context, and poll sfra_is_done()
. sfra.internal_state.freqIndex
indicates the progress. Once finished, call sfra_clear_done()
to clear the done flag.
Struct sfra_results
now holds the bode plot.
Add a light-weight MODBUS serial (Supports RS485) implementation to expose the results (also settings, of course). Sometimes, the application has already implemented MODBUS.
T.B.D
MATLAB provides functions to access MODBUS (supports serial and TCP) and plot the figure. With the help of ESP8266 as a MODBUS RS485-TCP bridge, it is now possible to wirelessly acquire the bode plot from a MCU on the hot-side.
This approach requires minimal modification to the existing application code. However, it is subjected to certain limitations on certain platforms and IDEs.
This approach works great on TI's C2000 platforms, where Code Composer Studio (CCS) allows you to inspect and edit the memory content without pausing the code execution. One can start the sweeping by setting sfra.internal_state.start
to 1 and copy the results directly from the Memory
view.
However, this is not true on most other platforms. For instance, GDB cannot read the memory of an STM32 MCU unless the code execution has paused. __ Before pausing the code, make sure to disable the power stage and/or switch off its power. __
- Verify the accuracy on different topologies
- MODBUS demo project