🎯 Objective

By the end of this lab, students will be able to:

  • Assess whether a buck converter behaves as a linear system by observing how the output responds to small sinusoidal perturbations in duty ratio.
  • Identify the conditions under which the buck converter can be approximated as a linear time-invariant (LTI) system, including assumptions related to small perturbations, steady operating point, and separation between switching and perturbation time scales.
  • Interpret system behavior in the frequency domain, by verifying whether the output preserves the input frequency and exhibits a well-defined amplitude and phase relationship.
  • Examine the effect of perturbation amplitude, and determine the range over which the system response remains proportional and free of distortion.
  • Analyze the effect of perturbation frequency, and observe how phase shift and waveform quality change as the excitation approaches the switching frequency.
  • Identify the limitations of the linear (average) model, particularly at large perturbations and high frequencies where switching dynamics and nonlinear effects become significant.

📚 Prerequisite

  • Experiment A1, A2, A3, and C1
  • Basic understanding of the frequency-domain response of a second-order system
  • Bode plot

🧠 Theory

In the previous experiment, we observed that a buck converter behaves like a second-order system when viewed through its averaged dynamics. This allows us to describe the relationship between duty ratio and output voltage using a transfer function. However, this statement carries an important assumption: A transfer function exists only if the system is linear and time-invariant. Since a buck converter is inherently a switching system, it is not immediately obvious that such a linear description is valid. Therefore, before using tools from Linear Control Systems to design controllers, we must first verify whether the duty–ratio–to–output–voltage relationship is linear.

Linearity in the Frequency Domain:

One practical way to test linearity is through frequency-domain behavior. Suppose we perturb the duty ratio around an operating point as:d(t)=D+d^sin(ωt)d(t) = D + \hat{d}\sin(\omega t)If the system is linear around the operating point, the output voltage must respond as: vo(t)=V+v^sin(ωt+ϕ)v_o(t) = V + \hat{v}\sin(\omega t + \phi)For a linear time-invariant system, a sinusoidal input results in a sinusoidal output at the same frequency, with only a change in amplitude and phase. This property enables frequency response analysis. This behavior arises because sinusoids are eigenfunctions of linear time-invariant systems, meaning the system scales and phase-shifts them without changing their frequency.

Implications of Linearity

At a fixed frequency, linearity leads to several testable properties: Scaling with input amplitude: If the perturbation amplitude increases, the output amplitude should scale proportionally. No new frequencies: The output should not contain harmonics or additional frequency components. Predictable gain and phase: The relationship between input and output can be fully described by a gain and a phase shift. If these conditions are satisfied, the system behaves linearly at that operating point and frequency.

Small-Signal Linearization

Although the converter is nonlinear due to switching actions, it can behave linearly under certain conditions: (1) The perturbation is small. (2) The system operates around a steady operating point. (3) The perturbation frequency is much lower than the switching frequency, and (4) We observe averaged quantities rather than switching waveforms. Under these conditions, the system can be approximated as linear time-invariant, and a transfer function becomes meaningful.

Duty-to-Output Transfer Function

As we derived in the last experiment using the averaged model of the buck converter, the small-signal relationship between duty ratio and output voltage is given by a second-order transfer function: Gvd(s)=VinLCs2+LRs+1G_{vd}(s)=\frac{V_{in}}{LC s^2 + \frac{L}{R}s + 1}

where:

  • VinV_{in} is the input voltage
  • LL is the inductance
  • CC is the capacitance
  • RR is the load resistance

This transfer function predicts:

  • A low-frequency gain proportional to VinV_{in}
  • A resonant behavior determined by LL, CC, and RR
  • A phase lag that increases with frequency
Why This Matters

Linear controller design tools—such as Bode plots, Gain and phase margins, and compensator design are based on linear system theory. If the converter follows this transfer function, these tools can be used to design controllers with confidence. In this experiment, we will test the validity of the linear assumption by perturbing the duty ratio at different frequencies and observing whether the output preserves frequency, scales with input amplitude, and exhibits consistent gain and phase. If these conditions hold, we conclude that: The buck converter behaves as a linear system in the small-signal sense, and the use of frequency-domain tools for control design is justified. In this case, the system can be fully characterized by its frequency response, which forms the foundation for controller design. If additional frequencies appear in the output, it is a clear indication of nonlinear behavior. We will not assume linearity—we will verify when it holds and use linear models only when it does to design the controller in the subsequent exercise.

Fig. 1: In this experiment, we will perturb the duty ratio using a signal generator and observe the output voltage. If the system (from Vcontrol as input to Vo as output) is linear, we should observe identical frequency components.

For a fixed switching frequency of 50 kHz, the perturbation frequency of the duty ratio is swept from 1 kHz to 10 kHz, 25 kHz, and 45 kHz. Figures 2–5 show the resulting waveforms. The moving average of q(t)q(t) is used to reconstruct the duty ratio from the average-model perspective. At low perturbation frequencies, the reconstructed duty ratio closely matches the injected duty ratio. However, as the perturbation frequency approaches the switching frequency, the deviation between the injected d(t)d(t)d(t) and the moving average of q(t)q(t)q(t) becomes increasingly pronounced. At 45 kHz, aliasing is clearly observed: even though the injected perturbation is at 45 kHz, the moving-average reconstruction shows a dominant component at 5 kHz, demonstrating classic aliasing.

Figure 2: The top subplot shows a sinusoidal perturbation in the injected duty ratio at 1 kHz along with the 50 kHz carrier waveform. The comparator produces the switching signal q(t)q(t), whose pulse-width modulation encodes the reference information. The moving average of q(t)q(t) reconstructs the duty ratio, closely matching the injected signal in frequency. Minor step-like variations are observed due to the discrete nature of the switching process.
Figure 3: The top subplot shows a sinusoidal perturbation in the injected duty ratio at 10 kHz along with the 50 kHz carrier waveform. The comparator produces the switching signal q(t)q(t), whose pulse-width modulation encodes the reference information. The moving average of q(t)q(t)q(t) reconstructs the duty ratio; however, compared to the 1 kHz case, the reconstructed signal exhibits increased distortion and deviation from the injected duty ratio. This degradation occurs as the perturbation frequency approaches the switching frequency, limiting the validity of the average-model approximation.
Figure 4: A sinusoidal perturbation in the injected duty ratio (25 kHz) and the carrier waveform (50 kHz) are shown in the top subplot. The comparator generates the switching signal q(t)q(t)q(t), whose pulse width is modulated to encode the reference information. Applying a moving average to q(t)q(t)q(t) attempts to reconstruct the duty ratio; however, significant distortion and deviation from the injected duty ratio are observed. At this perturbation frequency—half the switching frequency—the assumptions underlying the average model begin to break down. The reconstructed signal no longer accurately follows the injected duty ratio, highlighting the limitations of the moving-average approximation at higher frequencies.
Figure 5: A sinusoidal perturbation in the injected duty ratio (45 kHz) and the carrier waveform (50 kHz) are shown in the top subplot. The comparator generates the switching signal q(t)q(t), whose pulse width is modulated to encode the reference information. Applying a moving average to q(t)q(t) no longer reconstructs the injected duty ratio. Instead, a lower-frequency oscillation is observed in the reconstructed signal. Although the injected perturbation is at 45 kHz, the moving average of q(t)q(t) exhibits a dominant component at 5 kHz, demonstrating a clear aliasing effect. This occurs because the perturbation frequency is very close to the switching frequency, violating the assumptions of the average model and effectively folding the high-frequency content to a lower frequency.
Frequency Response Measurement Using FRA

While the frequency response can be constructed by manually injecting sinusoidal perturbations and analyzing time-domain data, as we will do in this experiment, in practice, this process can be significantly streamlined using a Frequency Response Analyzer (FRA) such as the AP310 Frequency Response Analyzer or Bode 100. An FRA automates the process of frequency response measurement by:

  • Injecting a small sinusoidal perturbation over a range of frequencies
  • Measuring the corresponding input and output signals
  • Directly computing the gain and phase at each frequency

This allows the frequency response (Bode plot) to be obtained quickly and accurately without requiring manual FFT processing.

How FRA Connects to This Experiment:

In the context of this experiment:

  • The FRA injects a small perturbation into the duty ratio (or reference signal)
  • It measures the resulting output voltage response over a very narrow band at the perturbation frequency
  • It computes: Gvd(jω)=vo(jω)d(jω)G_{vd}(j\omega) = \frac{v_o(j\omega)}{d(j\omega)}

Thus, the FRA directly measures the duty-to-output transfer function that we aim to validate. It is important to recognize that an FRA inherently assumes that the system behaves like a linear time-invariant system at each frequency. If the system is nonlinear, the output may contain harmonics or distortions, the measured gain and phase may become inaccurate or misleading. An FRA does not make the system linear—it assumes linearity. This experiment ensures that the assumption is valid and that we are aware of the limitations. Below is the data for the buck converter at five different loads (as we did in the last lab), collected using AP310. The converter's switching frequency is 50 kHz. Input voltage is 10 V, nominal duty ratio is 0.5, with a perturbation of 0.0125.

🧰 Required Components

The components needed in this lab are:

  • Red Board
  • Blue Board
  • Oscilloscope
  • Signal Generator
  • Multimeter
  • Current probe (if interested in measuring inductor current)

🎥 Overview Video

This video gives you a quick glimpse of what you can expect from this lab.

🛡️ Safety

Watch out for potential safety issues.

  1. Confirm probe grounds are properly connected for each measurement.
  2. Don't power the blue board using both the USB-C cable and the power adapter.
  3. Always disconnect the power once you are done with the experiment.

⚠ Common Mistakes

  1. The gate of a MOSFET is directly connected to a microcontroller digital pin to perform switching actions without a gate driver.
  2. Incorrect grounding between comparator output, vref source, and carrier source. All of the signals are referenced relative to gnd of the blue board.
  3. Scope probe not set to dc-coupling.
  4. The reference of the scope channels are not properly set to zero at the start of the experiment. This will lead to incorrect reading of the signals.
  5. Forgetting to record Capacitances (C).

Arduino Code

Not applicable.

Matlab Code

%% Buck Converter C2 Data Viewer + Moving Average + FFT
% Updated oscilloscope channel mapping:
%   Column 1 = time
%   Column 2 = vref(t)
%   Column 3 = sawtooth
%   Column 4 = q(t)
%   Column 5 = vmid(t) ripple, AC-coupled
%
% Scope channels:
%   CH1 = vref(t)
%   CH2 = sawtooth
%   CH3 = q(t)
%   CH4 = vmid(t), AC-coupled
%
% What this script does:
% 1) Load and preview CSV data
% 2) Extract time, vref, sawtooth, q(t), and vmid ripple
% 3) Normalize vref to injected duty ratio assuming 0–4 V carrier
% 4) Threshold q(t) and compute moving-average duty ratio
% 5) Plot time-domain signals
% 6) Plot FFTs from DC to 100 kHz

clear; clc; close all;

%% ------------------------------------------------------------------------
% Step 1: Select CSV file
%% ------------------------------------------------------------------------
[fileName, filePath] = uigetfile({'*.csv','CSV Files (*.csv)'}, 'Select CSV file');

if isequal(fileName, 0)
    error('No file selected. Script terminated.');
end

fullFileName = fullfile(filePath, fileName);
fprintf('Selected file:\n%s\n\n', fullFileName);

%% ------------------------------------------------------------------------
% Step 2: Preview raw text
%% ------------------------------------------------------------------------
rawText = fileread(fullFileName);
lines = splitlines(string(rawText));

nPreview = min(20, numel(lines));
fprintf('========== Preview of first %d lines ==========\n', nPreview);
for k = 1:nPreview
    fprintf('%2d: %s\n', k, lines(k));
end
fprintf('===============================================\n\n');

%% ------------------------------------------------------------------------
% Step 3: Detect numeric data start + delimiter
%% ------------------------------------------------------------------------
dataStartLine = [];
delimiterGuess = ',';

for k = 1:length(lines)
    thisLine = strtrim(lines(k));
    if thisLine == ""
        continue;
    end

    partsComma = split(thisLine, ',');
    scoreComma = sum(~isnan(str2double(strtrim(partsComma))));

    partsSemi = split(thisLine, ';');
    scoreSemi = sum(~isnan(str2double(strtrim(partsSemi))));

    partsTab = split(thisLine, sprintf('\t'));
    scoreTab = sum(~isnan(str2double(strtrim(partsTab))));

    if scoreComma >= max(2, ceil(0.6*numel(partsComma)))
        dataStartLine = k;
        delimiterGuess = ',';
        break;
    elseif scoreSemi >= max(2, ceil(0.6*numel(partsSemi)))
        dataStartLine = k;
        delimiterGuess = ';';
        break;
    elseif scoreTab >= max(2, ceil(0.6*numel(partsTab)))
        dataStartLine = k;
        delimiterGuess = sprintf('\t');
        break;
    end
end

if isempty(dataStartLine)
    error('Could not detect numeric data.');
end

fprintf('Detected numeric data starting at line %d\n', dataStartLine);
if strcmp(delimiterGuess, sprintf('\t'))
    fprintf('Detected delimiter: TAB\n\n');
else
    fprintf('Detected delimiter: %s\n\n', delimiterGuess);
end

%% ------------------------------------------------------------------------
% Step 4: Detect header row
%% ------------------------------------------------------------------------
headerLine = dataStartLine - 1;
hasHeader = false;

if headerLine >= 1
    headerText = strtrim(lines(headerLine));
    if headerText ~= ""
        candidateNames = split(headerText, delimiterGuess);
        nonNumericCount = sum(isnan(str2double(strtrim(candidateNames))));
        if nonNumericCount >= 1
            hasHeader = true;
        end
    end
end

if hasHeader
    fprintf('Probable header row detected at line %d\n\n', headerLine);
else
    fprintf('No reliable header row detected. Generic variable names will be used.\n\n');
end

%% ------------------------------------------------------------------------
% Step 5: Import data
%% ------------------------------------------------------------------------
opts = detectImportOptions(fullFileName, 'Delimiter', delimiterGuess);
opts.DataLines = [dataStartLine, Inf];

if hasHeader
    opts.VariableNamesLine = headerLine;
else
    opts.VariableNamesLine = 0;
end

T = readtable(fullFileName, opts);

% Remove completely empty columns
removeCols = false(1, width(T));
for i = 1:width(T)
    col = T{:,i};
    if isnumeric(col)
        removeCols(i) = all(isnan(col));
    elseif iscell(col)
        removeCols(i) = all(cellfun(@isempty, col));
    else
        removeCols(i) = false;
    end
end
T(:, removeCols) = [];

if width(T) < 5
    error('Need at least 5 columns: time, vref, sawtooth, q(t), vmid ripple.');
end

%% ------------------------------------------------------------------------
% Step 6: Extract updated oscilloscope channels
%% ------------------------------------------------------------------------
t           = T{:,1};   % time
vref        = T{:,2};   % CH1: vref(t)
saw         = T{:,3};   % CH2: sawtooth
q_raw       = T{:,4};   % CH3: q(t)
vmid_ripple = T{:,5};   % CH4: vmid(t), AC-coupled ripple

% Normalize vref to duty ratio assuming 0–4 V sawtooth/carrier
Vcar = 4;
d_inj = vref / Vcar;

t           = t(:);
vref        = vref(:);
saw         = saw(:);
q_raw       = q_raw(:);
vmid_ripple = vmid_ripple(:);
d_inj       = d_inj(:);

validRows = ~(isnan(t) | isnan(vref) | isnan(saw) | isnan(q_raw) | isnan(vmid_ripple));
t           = t(validRows);
vref        = vref(validRows);
saw         = saw(validRows);
q_raw       = q_raw(validRows);
vmid_ripple = vmid_ripple(validRows);
d_inj       = d_inj(validRows);

if numel(t) < 20
    error('Too few valid data points after cleanup.');
end

%% ------------------------------------------------------------------------
% Step 7: Time checks
%% ------------------------------------------------------------------------
dtVec = diff(t);
if any(dtVec <= 0)
    error('Time vector is not strictly increasing.');
end

dt = mean(dtVec);
fs = 1/dt;

fprintf('Estimated average sample interval = %.6e s\n', dt);
fprintf('Estimated sampling frequency      = %.3f MHz\n\n', fs/1e6);

%% ------------------------------------------------------------------------
% Step 8: Moving-average window
%% ------------------------------------------------------------------------
defaultTavg = 20e-6;   % one switching period for 50 kHz
Tavg = input(sprintf('Enter moving-average window in seconds [default = %.3e]: ', defaultTavg));

if isempty(Tavg)
    Tavg = defaultTavg;
end

if ~isscalar(Tavg) || Tavg <= 0
    error('Invalid moving-average window.');
end

N = round(Tavg/dt);
N = max(N,1);

fprintf('Moving-average window = %.6e s\n', Tavg);
fprintf('Equivalent samples    = %d\n\n', N);

%% ------------------------------------------------------------------------
% Step 9: Convert q(t) to binary and compute moving average
%% ------------------------------------------------------------------------
qThreshold = (max(q_raw) + min(q_raw))/2;
q_bin = double(q_raw >= qThreshold);

kernel = ones(N,1)/N;

d_comp = filter(kernel, 1, q_bin);              % computed duty ratio
vmid_avg = filter(kernel, 1, vmid_ripple);      % moving average of AC-coupled vmid ripple

trim = N;

if trim >= length(t)
    error('Moving-average window too large relative to data length.');
end

t_trim           = t(trim+1:end);
vref_trim        = vref(trim+1:end);
d_inj_trim       = d_inj(trim+1:end);
saw_trim         = saw(trim+1:end);
q_raw_trim       = q_raw(trim+1:end);
q_bin_trim       = q_bin(trim+1:end);
d_comp_trim      = d_comp(trim+1:end);
vmid_trim        = vmid_ripple(trim+1:end);
vmid_avg_trim    = vmid_avg(trim+1:end);

%% ------------------------------------------------------------------------
% Step 10: Visualization for PWM reconstruction
%% ------------------------------------------------------------------------
figure('Name','PWM Reconstruction View','NumberTitle','off');

subplot(3,1,1);

saw_min = min(saw_trim);
saw_max = max(saw_trim);
saw_norm = (saw_trim - saw_min) / (saw_max - saw_min);

plot(t_trim, d_inj_trim, 'LineWidth', 2.5, 'DisplayName', 'Injected duty ratio from vref'); hold on;
plot(t_trim, saw_norm, 'LineWidth', 2.0, 'DisplayName', 'Normalized sawtooth');

grid on;
xlabel('Time (s)');
ylabel('Normalized scale');
title('Injected Duty Ratio and Sawtooth');
legend('Location','best');
ylim([0 1]);

subplot(3,1,2);
plot(t_trim, q_bin_trim, 'LineWidth', 2.2);
grid on;
xlabel('Time (s)');
ylabel('q(t)');
ylim([-0.2 1.2]);
title('q(t), Thresholded PWM Signal');

subplot(3,1,3);
plot(t_trim, d_comp_trim, 'LineWidth', 2.5);
grid on;
xlabel('Time (s)');
ylabel('Moving average');
title('Moving Average of q(t) = Computed Duty Ratio');

linkaxes(findall(gcf,'Type','axes'),'x');

%% ------------------------------------------------------------------------
% Step 11: Time-domain plots
%% ------------------------------------------------------------------------
figure('Name','Time-Domain Signals','NumberTitle','off');

subplot(4,1,1);
plot(t_trim, vref_trim, 'LineWidth', 2.0);
grid on;
xlabel('Time (s)');
ylabel('vref (V)');
title('CH1: vref(t)');

subplot(4,1,2);
plot(t_trim, saw_trim, 'LineWidth', 2.0);
grid on;
xlabel('Time (s)');
ylabel('Sawtooth (V)');
title('CH2: Sawtooth');

subplot(4,1,3);
plot(t_trim, q_raw_trim, 'LineWidth', 2.0); hold on;
plot(t_trim, q_bin_trim, 'LineWidth', 1.5);
grid on;
xlabel('Time (s)');
ylabel('q(t)');
title('CH3: q(t)');
legend('Raw q(t)', 'Thresholded q(t)', 'Location','best');

subplot(4,1,4);
plot(t_trim, vmid_trim, 'LineWidth', 2.0, 'DisplayName', 'vmid ripple, AC-coupled'); hold on;
plot(t_trim, vmid_avg_trim, 'LineWidth', 2.5, 'DisplayName', 'Moving average');
grid on;
xlabel('Time (s)');
ylabel('vmid ripple (V)');
title('CH4: vmid(t), AC-Coupled');
legend('Location','best');

linkaxes(findall(gcf,'Type','axes'),'x');

%% ------------------------------------------------------------------------
% Step 12: FFT preparation
%% ------------------------------------------------------------------------
idxFFT = true(size(t_trim));

t_fft      = t_trim(idxFFT);
vref_fft   = vref_trim(idxFFT);
d_inj_fft  = d_inj_trim(idxFFT);
d_comp_fft = d_comp_trim(idxFFT);
vmid_fft   = vmid_trim(idxFFT);

validFFT = ~(isnan(t_fft) | isnan(vref_fft) | isnan(d_inj_fft) | isnan(d_comp_fft) | isnan(vmid_fft));

t_fft      = t_fft(validFFT);
vref_fft   = vref_fft(validFFT);
d_inj_fft  = d_inj_fft(validFFT);
d_comp_fft = d_comp_fft(validFFT);
vmid_fft   = vmid_fft(validFFT);

if numel(t_fft) < 8
    error('Too few points for FFT.');
end

Nfft = numel(t_fft);

%% ------------------------------------------------------------------------
% Step 13: FFT helper
%% ------------------------------------------------------------------------
w = hann(Nfft);
ampCorr = mean(w);

vref_proc   = vref_fft   .* w;
d_inj_proc  = d_inj_fft  .* w;
d_comp_proc = d_comp_fft .* w;
vmid_proc   = vmid_fft   .* w;

Vref  = fft(vref_proc);
Dinj  = fft(d_inj_proc);
Dcomp = fft(d_comp_proc);
Vmid  = fft(vmid_proc);

P2_vref  = abs(Vref  / Nfft) / ampCorr;
P2_dinj  = abs(Dinj  / Nfft) / ampCorr;
P2_dcomp = abs(Dcomp / Nfft) / ampCorr;
P2_vmid  = abs(Vmid  / Nfft) / ampCorr;

P1_vref  = P2_vref(1:floor(Nfft/2)+1);
P1_dinj  = P2_dinj(1:floor(Nfft/2)+1);
P1_dcomp = P2_dcomp(1:floor(Nfft/2)+1);
P1_vmid  = P2_vmid(1:floor(Nfft/2)+1);

if numel(P1_vref) > 2
    P1_vref(2:end-1)  = 2*P1_vref(2:end-1);
    P1_dinj(2:end-1)  = 2*P1_dinj(2:end-1);
    P1_dcomp(2:end-1) = 2*P1_dcomp(2:end-1);
    P1_vmid(2:end-1)  = 2*P1_vmid(2:end-1);
end

f = fs*(0:floor(Nfft/2))/Nfft;

%% ------------------------------------------------------------------------
% Step 14: FFT plot
%% ------------------------------------------------------------------------
fMaxPlot = 100e3;
idxPlot = f <= fMaxPlot;

figure('Name','FFT Comparison','NumberTitle','off');

semilogy(f(idxPlot), P1_vref(idxPlot) + 1e-15, 'LineWidth', 2.2, ...
    'DisplayName', 'vref(t)'); hold on;

semilogy(f(idxPlot), P1_dcomp(idxPlot) + 1e-15, 'LineWidth', 2.2, ...
    'DisplayName', 'Computed duty ratio from q(t)');

semilogy(f(idxPlot), P1_vmid(idxPlot) + 1e-15, 'LineWidth', 2.2, ...
    'DisplayName', 'vmid ripple');

grid on;
xlabel('Frequency (Hz)');
ylabel('Magnitude');
title('FFT: vref(t), Computed Duty Ratio, and vmid Ripple');
legend('Location','best');
xlim([0 fMaxPlot]);

%% ------------------------------------------------------------------------
% Step 15: Optional dominant frequency estimate
%% ------------------------------------------------------------------------
% Ignore DC for dominant perturbation estimate
idxNoDC = f > 10 & f <= fMaxPlot;

[~, idxVrefMax] = max(P1_vref(idxNoDC));
[~, idxVmidMax] = max(P1_vmid(idxNoDC));

f_noDC = f(idxNoDC);

f_vref_dom = f_noDC(idxVrefMax);
f_vmid_dom = f_noDC(idxVmidMax);

fprintf('\nDominant frequency estimate:\n');
fprintf('  vref dominant frequency  = %.2f Hz\n', f_vref_dom);
fprintf('  vmid dominant frequency  = %.2f Hz\n', f_vmid_dom);

%% ------------------------------------------------------------------------
% Step 16: Export useful variables
%% ------------------------------------------------------------------------
assignin('base','t',t);
assignin('base','vref',vref);
assignin('base','saw',saw);
assignin('base','q_raw',q_raw);
assignin('base','q_bin',q_bin);
assignin('base','d_inj',d_inj);
assignin('base','d_comp',d_comp);
assignin('base','vmid_ripple',vmid_ripple);
assignin('base','vmid_avg',vmid_avg);

assignin('base','t_trim',t_trim);
assignin('base','vref_trim',vref_trim);
assignin('base','saw_trim',saw_trim);
assignin('base','q_raw_trim',q_raw_trim);
assignin('base','q_bin_trim',q_bin_trim);
assignin('base','d_inj_trim',d_inj_trim);
assignin('base','d_comp_trim',d_comp_trim);
assignin('base','vmid_trim',vmid_trim);
assignin('base','vmid_avg_trim',vmid_avg_trim);

assignin('base','f_fft',f);
assignin('base','P1_vref',P1_vref);
assignin('base','P1_dinj',P1_dinj);
assignin('base','P1_dcomp',P1_dcomp);
assignin('base','P1_vmid',P1_vmid);

fprintf('\nDone. Key variables exported to workspace.\n');

🧷 Jumper Settings

For this experiment, we will be using only the black board.

Blue Board:

JumperFunctionSettingNote
JP4Carrier waveform selection (Option 1: Constant, Option 2: External Carrier, Option 3: Sawtooth waveform internally generated)Position 3 (we will use internal sawtooth)-
JP5Reference signal selection (Option 1: a dc, whose magnitude can be varied using the potentiometer Rduty1, Option 2: any external signal that ranges between 0 and 5 V, Option 3: Voltage mode (we will use this later on for closed-loop control)Position 2 (we will provide the reference signal from the external reference)-
JP3The PWM input signal to the deadtime generation circuit can be provided in three ways. (Option 1: Using an external PWM source, for example, an Arduino generating PWM pulses, Option 2: Internal PWM that is generated by the PWM generation circuit, and Option 3: Using current-mode control.)Position 2. We will generate PWM using the internal comparator-
JP1The gate of the high-side MOSFET [qH (in Blue Board) or PWM_H(in Red Board)] can be fed three signals. (Option 1: q1 signal from the dead time compensation circuit, Option 2: gnd, Option 3: q2 signal from the dead time compensation circuit). Note that q1 follows q(t) and q2 is complementary.Position 1. We will use the half-bridge in synchronous mode.
JP2The gate of the low-side MOSFET [qL (in Blue Board) or PWM_L(in Red Board)] can be fed three signals. (Option 1: q2 signal from the dead time compensation circuit, Option 2: gnd, Option 3: q1 signal from the dead time compensation circuit). Note that q1 follows q(t) and q2 is complementary.Position 1. We will use the half-bridge in synchronous mode.
JP6Filter selection for the onboard measured inductor current. (Option 1: RC filter with a cut-off frequency at 159 Hz, Option 2: RC filter with a cut-off frequency at 1.59 MHz, Option 3: No filter).Position 3. We are interested in measuring the unfiltered inductor current in this experiment.

Red Board:

JumperFunctionSettingNote
J7Populating this jumper provides the 12 V supply to the gate driver. (Option 1: 12 V is internally generated, Option 2: An External supply is needed)Position 1. We will provide the internally generated 12V supply to the gate driver.-
J10 and J11These jumpers allow changing the direction of current measurement through the Rsense resistor. (Option 1: Current can be measured flowing from L2 to Vmid terminals, Option 2: Current can be measured flowing from Vmid to L2 terminals)Position 1. (We will set it up to measure the buck converter current.)

Keep all the other jumpers unpopulated.

⚙️ Circuit Configuration & Setting up the experiment

We will configure the red and blue boards to operate as a synchronous buck converter by connecting an external inductor (recommended value: 30 µH) and a load resistor using Bank A and Bank B in parallel. This configuration allows the load resistance to be varied by adding parallel resistors, enabling operation from no-load to the following approximate values: 51 Ω, 8.36 Ω, 4.55 Ω, 3.13 Ω, and 2.38 Ω. If a current probe is available, the inductor current should also be measured. The system is powered via USB. To perturb the duty ratio, we will connect a signal generator to Ext. Vref and Gnd. Please be extra cautious: If the signal at Ext. Vref. exceeds 5 V, we will destroy the comparator on the blue board.

Use the checklist below to mark each step as you complete it. You can download it later on to verify that you have performed all the steps.

Startup & Setup Checklist

Word of caution: the signal generator output must remain between 0 V and 5 V under all conditions. Exceeding this limit can destroy the PWM comparator IC on the Blue Board.
Red Board + External Components
Blue Board
Blue Board
Red Board
All signals are measured with respect to GND (black test points).
vref(t) is now provided by the external signal generator.
Blue Board
Red Board
Initial Power-Up (Before Signal Injection)
Signal Generator Setup (Before Connecting to Blue Board)
Connect and Verify

🧪 Experiment

Download the checklist above and ensure you have completed all steps before we power on. We will go through the following steps:

  1. Probe the following signals on the oscilloscope: Channel 1: vref(t)v_{\text{ref}}(t), Channel 2: sawtooth, Channel 3: q(t)q(t), and Channel 4 (in ac coupled mode--we are interested in perturbation only, not the dc operating point): vmid(t)v_{mid}(t) .
  2. Before proceeding, verify that the switching frequency is 50 kHz (by observing the sawtooth waveform) and that the duty ratio is approximately 50% (by observing q(t)) with the signal generator offset at ~2 V and negligible sinusoidal perturbation at 1000 Hz (by observing vref(t)v_{\text{ref}}(t)).
  3. Set the load resistance to its minimum value by turning all load switches ON.
  4. We are now ready to perform the experiment and capture data under different conditions. First, vary the amplitude of the duty-ratio perturbation while keeping the offset and perturbation frequency fixed. For each case, adjust the time axis so that at least 10 cycles of the output voltage (Channel 4) variation are visible, unless otherwise noted. Save the raw oscilloscope data in CSV format for offline analysis, and record each filename in the table for reference. Do not forget to download the CSV data before clicking Clear. Next, we will vary the perturbation frequency while keeping the amplitude constant.
  5. Measurement Guidelines (Read Carefully): When measuring vmid(t)v_{\text{mid}}(t), focus only on the component at the excitation (perturbation) frequency. Note the presence of other frequency components (indicating nonlinearity). Use the oscilloscope to identify the dominant "excitation-frequency" sinusoidal component, and measure its amplitude and phase relative to vref(t)v_{\text{ref}}(t). At higher frequencies or larger perturbation amplitudes, it may be difficult to clearly identify the perturbation component or to measure its phase relative to vref(t)v_{\text{ref}}(t). In such cases, do your best to estimate the amplitude and phase. Clearly note any uncertainty or distortion in the Observations column. This is where a Frequency Response Analyzer (FRA) becomes extremely useful, as it extracts gain and phase at the excitation frequency with very high accuracy even in the presence of switching ripple and noise.

C2 Data Collection Tool

Record the measured response of the buck converter under different perturbation amplitudes and frequencies to test whether the duty-ratio–to–output relationship behaves linearly.

Measurement guidelines: When measuring vmid(t), focus only on the component at the excitation frequency. Ignore switching ripple and high-frequency noise. Measure the amplitude and phase of the dominant low-frequency component relative to vref(t). If this becomes difficult, note the uncertainty in Observations. This is where an FRA is especially useful, since it can automatically extract gain and phase at the excitation frequency even in the presence of ripple and noise.

Fixed conditions: Vin = 10 V, R = 4 Ω, vref offset = 2 V, perturbation frequency = 1 kHz.

File Name Vin (V) R (Ω) Freq (Hz) vref Offset (V) vref Amp (V) mid (at excitation freq) Phase (deg) Dominant Output Frequency Observations

Fixed conditions: Vin = 10 V, R = 4 Ω, vref offset = 2 V, vref amplitude = 0.5 V.

File Name Vin (V) R (Ω) Freq (Hz) vref Offset (V) vref Amp (V) mid (at excitation freq) Phase (deg) Dominant Output Frequency Observations

Turn off Checklist

Before we close the experiment, please ensure:

Turn-Off & Shutdown Checklist

🧠 Observations & Analysis

Now, let us analyze the data you collected to write a brief report that solidifies our understanding. Structure the report to include the following:

  1. Restate the objectives of this experiment in your own words. Your response should address the following: (1) Is the buck converter inherently a linear and time-invariant system? (2) State the main assumptions under which a buck converter can be treated as a linear time-invariant system. (3) From a frequency domain analysis, what is a fundamental marker of whether a system is linear or non-linear? Use the buck converter to substantiate your argument.
  2. Theory: What are the implications if the perturbation frequency approaches the switching frequency? Discuss how the separation between switching frequency and perturbation frequency affects the validity of the average model. What physical effects (sampling, aliasing, switching ripple interaction) begin to appear? Why does the assumption of a smooth averaged response begin to break down?
  3. Using the data collected in Subgroup 1, analyze the effect of increasing the perturbation amplitude while keeping the operating point and frequency fixed. For each amplitude (200 mV, 500 mV, 1 V), does the output vmid(t)v_{\text{mid}}(t) contain the same dominant frequency as the input? At what amplitude (if any) does distortion or harmonic content begin to appear? What are the possible causes of this non-linearity?
  4. As the perturbation amplitude increases, does the amplitude of vmid(t)v_{\text{mid}}(t)vmid​(t) increase proportionally? Is the relationship approximately linear between: 200 mV → 500 mV? 500 mV → 1 V?
  5. Using the data collected in Subgroup 2, analyze the system behavior as a function of frequency. You may use the FFT code in MATLAB provided above to run frequency plots. At each perturbation frequency, does the output maintain the same dominant frequency as the input? At what frequency does this assumption begin to break down?
  6. How does the phase between vref(t)v_{\text{ref}}(t) and vmid(t)v_{\text{mid}}(t) change as frequency increases?
  7. Limitation of Linear Approximation: Identify the frequency range over which the output is clean, and the phase can be reliably measured, with the response appearing linear. Identify the frequency beyond which: Measurements become unreliable, and the response deviates from a simple sinusoid.
  8. Using the data provided from AP310, compare the Bode plots you obtained in the previous experiment (using time domain features) for each loading condition. Explain the possible sources of error.
  9. Is it important to restrict perturbations to small amplitudes when measuring frequency response? Explain. How would this consideration change between a buck converter and a boost converter? Why do measurements become more difficult near the switching frequency?
  10. Write a short conclusion summarizing the main findings of the experiment. Your conclusion should address: why the average (small-signal) model is useful for analyzing and designing control systems for the buck converter. Identify and explain the key limitations of the average model, particularly in relation to switching effects, large-signal behavior, and high-frequency operation.

✔ Conclusion

This lab demonstrated that a buck converter behaves as a linear system under certain assumptions, including small perturbations and within a limited frequency range. Outside this region, nonlinear effects and switching dynamics dominate, making linear models and frequency response analysis less accurate.