PID control of the buck converter

I write the PID controller of the buck converter to make the output voltage to follow the setpoint value I set in the code. In my code, the PID calculation is written in Timer interrupt while the setpoint is a square wave written in the loop part. Besides, the timer interrupt is used to calculate the PID every 1ms interval. However, after I download the code to the arduino uno and use the oscilloscope to observe the output voltage of the buck converter, the value of it is almost 0 at the beginning and after several seconds, the output voltage is much higher than the value of the setpoint I set. So, there may be something wrong with the code that I haven't noticed. Can anyone help me?

/working variables/
#include <PWM.h>
#define PIN_INPUT 5
#define PIN_OUTPUT 10
unsigned long lastTime;
//Unsigned long variables are extended size variables for number storage, and store 32 bits (4 bytes), while lastTime is stored variable
double Input, Output, Setpoint;
double ITerm, lastErr;
double error;
double Kp=1, Ki=0.2912,Kd=0;
int SampleTime = 1; //1 sec

volatile double i = 0;
int32_t frequency=100000;
void Compute();

void setup()

cli();//stop interrupts

//set timer0 interrupt at 2kHz
TCCR0A = 0;// set entire TCCR0A register to 0
TCCR0B = 0;// same for TCCR0B
TCNT0 = 0;//initialize counter value to 0
// set compare match register for 2khz increments
OCR0A = 249;// = (1610^6) / (100064) - 1 (must be <256)
// turn on CTC mode
TCCR0A |= (1 << WGM01);
// Set CS01 and CS00 bits for 64 prescaler
TCCR0B |= (1 << CS01) | (1 << CS00);
// enable timer compare interrupt
TIMSK0 |= (1 << OCIE0A);

sei();//allow interrupts
Input = analogRead(PIN_INPUT);
Input = map(analogRead(PIN_INPUT), 0, 1023, 0, 255);
unsigned long now = micros();
//Returns the number of milliseconds passed since the Arduino board began running the current program
//int timeChange = (now - lastTime);
//the algorithm now decides for itself if it’s time to calculate. Also, because we now KNOW that it’s going to be the same time between samples, we don’t need to constantly multiply by time change
/Compute all the working error variables/
double error = 255*0.5 - Input;
ITerm += (Ki * error) ;
//x += y; // equivalent to the expression x = x + y;

double dErr = (error - lastErr);

/Compute PID Output/
Output = Kp * error + ITerm + Kd * dErr;

/Remember some variables for next time/
lastErr = error;
//lastTime = now;
analogWrite(PIN_OUTPUT, Output);
void loop()
{ for (int i=0;i<=500;i++){
Setpoint = 2550;
bool success = SetPinFrequencySafe(PIN_OUTPUT, frequency);
for (int i=0;i<=500;i++){
Setpoint = 255
bool success = SetPinFrequencySafe(PIN_OUTPUT, frequency);


withtimer.ino (2.28 KB)

Most of the variable in your ISR are not declared volatile for a start, that needs sorting out first.
You are reading the pin twice for no reason apparently.

Your PID code doesn't have any protection from integral windup, this could be part of your issue.

Normally buck converters require over-current shutdown protection circuitry, and live updating
of the switch state by tracking the output voltage in real time - otherwise a sudden decrease in load
current would cause voltage overshoot before the end of that cycle.

yes. I know I haven't added the part to avoid the windup, that is because next step, I will compare the step response of the buck converter with and without code of ant-windup. And I have tested the code of the PID part while I didn't use the timer interrupt, in that code,with micros() and sample time, through comparing the interval and sample time to judge if go to the PID part and do the calculation. But I found it is limited while I want to high sample frequency to do calculation. So, I use the Timer. and I don't change other part of code, so I tend the code is invaild is more likely due to the Timer part.