#include <PID_v1.h>
#include <PWM.h> // For high-frequency PWM
#include <Wire.h>
//#include <LiquidCrystal_PCF8574.h>
// Pin Definitions
#define POT_PIN A0 // Potentiometer input (0–25V scaled down to 0–5V)
#define FEEDBACK_PIN A1 // Output voltage feedback (via voltage divider)
#define PWM_PIN 9 // PWM output pin
// LCD
//LiquidCrystal_PCF8574 lcd(0x27);
// Constants
const int32_t PWM_FREQ = 20000; // 20kHz PWM frequency
const float VOUT_MAX = 25.0; // Max output voltage
const float ADC_SCALE = 5.0 / 1024.0; // Convert ADC to voltage
// PID Variables
double Setpoint, Input, Output;
double lastInput = 0.0; // For low-pass filter
// PID Tuning Parameters
double aggKp = 2.0, aggKi = 5.0, aggKd = 0.05;
double consKp = 1.0, consKi = 1.0, consKd = 0.01;
PID myPID(&Input, &Output, &Setpoint, aggKp, aggKi, aggKd, DIRECT);
// Timing
unsigned long lastLCDupdate = 0;
const unsigned long LCD_UPDATE_INTERVAL = 200;
double lastVref = 0.0;
void setup() {
pinMode(PWM_PIN, OUTPUT);
InitTimersSafe();
SetPinFrequencySafe(PWM_PIN, PWM_FREQ);
Serial.begin(9600);
Wire.begin();
// lcd.begin(16, 2);
// lcd.setBacklight(255);
// lcd.print("Buck Converter");
// delay(1000);
// lcd.clear();
myPID.SetMode(AUTOMATIC);
myPID.SetSampleTime(200); // Sample every 100ms
myPID.SetOutputLimits(0, 255); // PWM range
}
void loop() {
// --- Read and filter Vref (A0) ---
double pot_voltage_raw = analogRead(POT_PIN) * ADC_SCALE; // 0–5V
double pot_voltage_filtered = 0.9 * lastVref + 0.1 * pot_voltage_raw;
lastVref = pot_voltage_filtered;
Setpoint = pot_voltage_filtered * (VOUT_MAX / 5.0); // Scale 0–5V to 0–25V
// --- Read and filter Vout (A1) ---
double raw_voltage = analogRead(FEEDBACK_PIN) * ADC_SCALE;
double rawInput = raw_voltage * (VOUT_MAX / 5.0); // Scale to 0–25V
Input = 0.9 * lastInput + 0.1 * rawInput;
lastInput = Input;
// --- Adaptive PID Tuning ---
double error = abs(Setpoint - Input);
if (error > 1.5) {
myPID.SetTunings(aggKp, aggKi, aggKd);
} else {
myPID.SetTunings(consKp, consKi, consKd);
}
// --- PID Computation ---
myPID.Compute();
// --- Apply PWM Output ---
int duty_cycle = (int)constrain(Output, 0, 255);
pwmWrite(PWM_PIN, duty_cycle);
// --- LCD Update (optional) ---
// if (millis() - lastLCDupdate >= LCD_UPDATE_INTERVAL) {
// lastLCDupdate = millis();
// lcd.setCursor(0, 0);
// lcd.print("Vref: ");
// lcd.print(Setpoint, 1);
// lcd.print("V ");
// lcd.setCursor(0, 1);
// lcd.print("Vout: ");
// lcd.print(Input, 1);
// lcd.print("V ");
// }
// --- Serial Debug ---
Serial.print("Vref: ");
Serial.print(Setpoint, 2);
Serial.print(" V | Vout: ");
Serial.print(Input, 2);
Serial.print(" V | PWM: ");
Serial.println(duty_cycle);
delay(10); // Small delay
}
I'm working on a closed-loop buck converter using an Arduino Uno for voltage regulation. Despite tuning the PID parameters, my output voltage doesn't settle to a constant value and keeps oscillating around the setpoint (e.g., if setpoint = 20V, output oscillates between 19V–21V).
System Specifications
- Hardware:
- Input Voltage: 25V DC
- Output Voltage Range: 0–25V (scaled to 0–5V via voltage divider for feedback)
- Switching Frequency: 20kHz (using
PWM.hlibrary) - Components:
- Inductor: 9mH
- Capacitor: 470μF (output filter)
- Load: 1Ω resistor (max 2A current)
- MOSFET: IRFZ44N (with gate driver)
- Control Logic:
- Setpoint: Potentiometer (A0) → Scaled to 0–25V
- Feedback: Voltage divider (A1) → Scaled to 0–25V
- PID Library:
PID_v1.h - Non-blocking code with adaptive tuning:
- Aggressive PID (
error > 1.5V):Kp=2.0, Ki=5.0, Kd=0.05 - Conservative PID (
error ≤ 1.5V):Kp=1.0, Ki=1.0, Kd=0.01
- Aggressive PID (
- Observed Behavior:
- Output voltage oscillates ±1V around setpoint (e.g., 20V → 19V–21V).
- Oscillations persist even after trying multiple PID tunings (see below).