前言

电源方向资料免费开源到QQ群280730348,欢迎进群交流沟通。博客地址edadong.com,博文同步发布在知乎、bilibili,其中bilibili主要以视频为主。

下面是BUCK电路的PID控制方案和具体电路,更详细的讲解请移步Bilibili:EDAdong。

软件思路

PID其实属于比较常见且通用的闭环调节算法,经典古老但实用。在一般的单环或者双环闭环控制环节,拥有较为不错的效果。本次分享的PID属于增量式PID,大家可以去了解一下增量式PID和位置式PID的区别,一般来说在电源设计里面增量式PID会用的比较多一点。传统PID中,P环是根据当前误差量输出反馈量,I环是根据当前误差和上一次误差之差输出反馈量,D环是根据上次I环误差量和此次I环误差量进行刹车反馈,通过上一次误差量抑制当前误差。P环比较通俗易懂一点,所以传统PID里面P环会用的比较多一点。放在增量式PID中,传统PID的P环其实跟增量式的I环一样,所以这一点有所区分,大家需要注意。我通常是P环和I环一起调,以I环为主导。

image-20250323142231953

本次PID代码在基于BUCK的实战演练项目中修改,直接可用,话不多说直接上代码

在PID.c文件中,可以看到buck_pid的调节函数,第一个参量NOW代表的是你当前采集到的数据(比如在本设计中就是电压所转换成的ADC值,范围0-4096)。第二个参量Target代表你要追踪的目标值(比如在本设计里面把目标电压转换成对应ADC值)。第三个参量SIGNLE_ADD_NUM_LIMIT代表单次增加量,每次PID调节的增量范围,这个值不能太大,否则系统就容易震荡起来。程序中设定的PID周期为1ms,每秒能执行1000次,每次如果拉满的话,那么1s理论上就能调节5000,但我们重装载值也就3600,而且越接近目标值,增量就会越小,所以这个参数你可以根据自己的系统去调节,目前我用的5基本跑单环速度和精度都没问题。第四个参量SUM_OUTPUT_NUM_LIMIT代表输出限制,比如我们重装载值为3600,那么这个值就可以设置成3599,确保占空比不会继续递增。后面的两个参量kp和ki分别代表增量式PID里面的积分调节和比例调节。

pid.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include "pid.h"
#include "main.h"
PID_Struct pid1;

float OUT=50;
float buck_pid(float NOW,float Target,float SIGNLE_ADD_NUM_LIMIT,float SUM_OUTPUT_NUM_LIMIT,float kp,float ki)//SIGNLE_ADD_NUM_LIMIT是单次增加最大值 如100即为单次转换输出不能超过+-100
{
//SUM_OUTPUT_NUM_LIMIT 是总输出限位 如100 即总输出不能超出+-100的范围 防止长时间低于或高于 对于突变反应不及时

pid1.Pv=NOW;
pid1.Ek=Target-pid1.Pv;
pid1.Pout=kp*(pid1.Ek-pid1.Ek_1);
pid1.Iout=ki*pid1.Ek*pid1.T/pid1.Ti;
pid1.Dout=pid1.Kp_D*pid1.Td*(pid1.Ek-pid1.Ek_1-pid1.Ek_1+pid1.Ek_2)/pid1.T;
pid1.OUT_Single=pid1.Pout+pid1.Iout+pid1.Dout;
if(pid1.OUT_Single>SIGNLE_ADD_NUM_LIMIT)pid1.OUT_Single=SIGNLE_ADD_NUM_LIMIT;
else if(pid1.OUT_Single<-SIGNLE_ADD_NUM_LIMIT)pid1.OUT_Single=-SIGNLE_ADD_NUM_LIMIT;
OUT+=pid1.OUT_Single;
if(OUT>SUM_OUTPUT_NUM_LIMIT)OUT=SUM_OUTPUT_NUM_LIMIT;
else if(OUT<0)OUT=0;
pid1.Ek_2=pid1.Ek_1;
pid1.Ek_1=pid1.Ek;
return OUT;
}

void init_buck_PID()
{
pid1.Kp_P=0;
pid1.Kp_I=1;
pid1.Kp_D=0;
pid1.T=1;//Ms
pid1.Td=2.5;
pid1.Ti=150;
pid1.Pv=1000;
pid1.Ek_2=0;
pid1.Ek_1=0;
pid1.Ek=0;
}

pid.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#ifndef _PID_H
#define _PID_H

typedef struct
{
float Sv;//期望值
float Pv;//实际值
float Ek_1;
float Ek_2;
float Ek;
float T;//采样周期
float Ti;//积分常数
float Td;//微分常数
float Kp_P;//系数
float Kp_I;//系数
float Kp_D;//系数
float OUT_Single;
float Pout;
float Iout;
float Dout;
}PID_Struct;
float buck_pid(float NOW,float Target,float SIGNLE_ADD_NUM_LIMIT,float SUM_OUTPUT_NUM_LIMIT,float kp,float ki);
void init_buck_PID(void);
extern float OUT;
#endif


在定时器中断中(20KHz)每进入20次进行一次PID调节,也就是单次调节时间为1ms。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//中断服务函数
void TIM1_UP_IRQHandler(void)
{
static u16 pid_count;
if (TIM_GetITStatus(TIM1, TIM_IT_Update) != RESET) //检查更新中断发生与否
{
pid_count++;
//PID调节
if(pid_count==20)
{
pid_count=0;
if(pid_mode==1&&protect_status==0)
{
buck_pwm = buck_pid(ADC_ConvertedValue[0],(Target_V/3.3*4096/V_xishu),5,3599,kp,ki);
set_pwm(TIM1,1,buck_pwm,3599);
}
else if(pid_mode==0&&protect_status==0)
{
buck_pwm=0;
set_pwm(TIM1,1,buck_pwm,1000);
}
else if(protect_status==1)
{
buck_pwm=0;
set_pwm(TIM1,1,buck_pwm,1000);
}
}
TIM_ClearITPendingBit(TIM1, TIM_IT_Update); //清除TIMx更新中断标志
}
}

主函数中只需要定义float kp和float ki两个参量,extern到main.h里面,再在main函数中初始化init_buck_PID();即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#include "stm32f10x.h" 
#include "main.h"
#include "delay.h"
#include "timer.h"
#include "timer3.h"
#include "oled.h"
#include "show.h"
#include "adc.h"
#include "sw.h"
#include "key.h"


u8 pid_mode=0;//=0不工作,=1工作
u8 pid_status=0;//=0为电压环,=1为电流环
u16 buck_pwm=500;//BOOST电路的PWM

float DC_V=0.0f;//电压数据
float Target_V=10.00f;//追踪的电压数据
float DC_I=0.0f;//电流数据

float V_yuzhi=13.00f;//保护的电压阈值
float I_yuzhi=3.00f;//保护的电流阈值

u8 protect_status=0;//保护状态
u8 sw_status=0;//开关状态

float kp=0.01;
float ki=1;
/************************************************************
电压电流检测:电压PA0,电流PA1
PWM波驱动引脚:PA8
芯片使能引脚:PA9
OLED屏幕:SCL:PB1,SDA:PB0
继电器控制位:PA10
按键:ADD:PB12 REDUCE:PB13 SET:PB14 MODE:PB15
************************************************************/
int main(void)
{
u8 key;
Delay_Init(); //延时功能初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级

BUCK_PWM_Init(); //BUCK电路初始化,20KHz工作频率,带中断。
Init_adc(); //ADC初始化
SW_Init( ); //继电器初始化
KEY_Init(); //按键初始化

TIM3_ENABLE_30S(); //开启定时器3工作,实现电压电流数据采集
OLED_Init(); //初始化OLED

loop:
SW_OFF;
SD_OFF;
sw_status=0;
pid_mode=0;//失能PID
OLED_Clear(); //OLED清零
canshu_view(); //启动界面
while(1)
{
key=KEY_Scan(0); //扫描按键
if(key==SW_PRES)
{
SD_ON; //打开芯片引脚使能
SW_ON; //打开继电器电路准备工作
Delay_Ms(500);
break;
}
OLED_ShowNum(72,0,DC_V,2,16);
OLED_ShowNum(96,0,(u16)(DC_V*100)%100,2,16);
OLED_ShowNum(72,2,DC_I,2,16);
OLED_ShowNum(96,2,(u16)(DC_I*100)%100,2,16);
OLED_ShowNum(72,4,Target_V,2,16);
OLED_ShowNum(96,4,(u16)(Target_V*100)%100,2,16);
if(sw_status==0)
{
OLED_ShowCHinese(84,6,13);OLED_ShowCHinese(100,6,14);
}
else
{
OLED_ShowCHinese(84,6,11);OLED_ShowCHinese(100,6,12);
}
Delay_Ms(50);
}
while(1)
{
key=KEY_Scan(0);
OLED_ShowNum(72,0,DC_V,2,16);
OLED_ShowNum(96,0,(u16)(DC_V*100)%100,2,16);
OLED_ShowNum(72,2,DC_I,2,16);
OLED_ShowNum(96,2,(u16)(DC_I*100)%100,2,16);
OLED_ShowNum(72,4,Target_V,2,16);
OLED_ShowNum(96,4,(u16)(Target_V*100)%100,2,16);
if(sw_status==0)
{
OLED_ShowCHinese(84,6,13);OLED_ShowCHinese(100,6,14);
}
else
{
OLED_ShowCHinese(84,6,11);OLED_ShowCHinese(100,6,12);
}

if(key==SW_PRES)
{
pid_mode=1;
sw_status=1;
}
else if(key==ADD_PRES)
{
Target_V=Target_V+0.100001f;
}
else if(key==REDUCE_PRES)
{
Target_V=Target_V-0.099999f;
}
else if(key==CHOICE_PRES)
{
SW_OFF;
SD_OFF;
sw_status=0;
pid_mode=0;//失能PID
goto loop;
}

if((DC_V>V_yuzhi)||(DC_I>I_yuzhi))
{
SW_OFF;
SD_OFF;
protect_status=1;
sw_status=0;
pid_mode=0;//失能PID
goto loop;
}
}
}


资料放百度网盘链接了:

通过网盘分享的文件:post12资料合集
链接: https://pan.baidu.com/s/1O4975ZoDPHOWwQN88SApsA?pwd=fvay 提取码: fvay
–来自百度网盘超级会员v6的分享