目录
概述
1 系统框架和算法
1.1 框架结构介绍
1.2 PID算法实现
1.2.1 理论介绍
1.2.2 离散化位置式PID
1.2.3 位置式PID算法
2 STM32Cube 配置项目
2.1 配置参数
2.2 GENERATE项目
3 功能实现
3.1 ADC采样数据功能
3.2 DAC数据转换
3.3 PID相关的调制函数
4 测试
4.1 编写测试代码
4.2 运行结果
源代码下载地址:
使用PID算法实现DAC模拟量输出的快速调节资源-CSDN文库
概述
本文主要介绍一个PID在实际项目应用的案例,通过ADC采样DAC的输出结果,调整使其快速到达期望的结果,系统基于STM32F103RC,使用DAC输出模拟量,ADC采集该模拟量作为反馈信号,以实现系统的闭环控制。
1 系统框架和算法
1.1 框架结构介绍
MCU内核处理数据: 设置DAC的初始值,读取ADC模块反馈回来的数值,内部进行PID运算,并将运算结果发送给DAC模块,重新输出数据。
DAC输出模拟量: 经过MCU计算后,将运行结果通道DAC模块输出
ADC采集输出数据:ADC模块采集DAC输出的模拟量,将其转换为数据值,MCU应用该值和输出量进行比较,以确定是否还需要继续调节输出数值
1.2 PID算法实现
1.2.1 理论介绍
PID 算法是闭环控制系统中常用的算法, PID 分别是 Proportion(比例)、 Integral(积分)、
Differential(微分)的首字母缩写。它是一种结合比例、积分和微分三个环节于一体的闭环控制算法。具体的控制流程如下图所示:
通过将输入目标值和实际输出值进行偏差的计算,然后把计算结果输入到 PID控制算法中,经过比例、积分和微分三个环节的运算,运算后的输出作用于执行器,从而让系统的实际值逐渐靠近目标值。连续控制的理想 PID 控制规律:
• Kp —— 比例增益, Kp 与比例度成倒数关系
• Tt —— 积分时间常数
• TD —— 微分时间常数
• u(t)—— PID 控制器的输出信号
• e(t)—— 给定值 r(t)与测量值误差
1.2.2 离散化位置式PID
对离散式的PID公式进行运算,则 u(k) 可表示为:
进一步进行简化,则得出PID的标准公式:
参数介绍
• k 采样的序号
• err(k) 第 k 次的误差
• u(k) 输出量
• Kp 比例参数
• Ki=Kp*T/Ti 积分参数
• Kd=Kp*Td/T 微分参数
1.2.3 位置式PID算法
typedef struct
{
float target_val; //目标值
float actual_val; //实际值
float err; //定义偏差值
float err_last; //定义上一个偏差值
float Kp,Ki,Kd; //定义比例、积分、微分系数
float integral; //定义积分值
}_pid;
_pid pid_location;
void pid_param_init(void)
{
/* 位置相关初始化参数 */
pid_location.target_val = 20;
pid_location.actual_val=0.0;
pid_location.err=0.0;
pid_location.err_last=0.0;
pid_location.integral=0.0;
pid_location.Kp = 0.045;
pid_location.Ki = 0.0;
pid_location.Kd = 0.0;
}
float pid_location_realize(_pid *pid, float actual_val)
{
/*计算目标值与实际值的误差*/
pid->err=pid->target_val-actual_val;
/* 设定闭环死区 */
if((pid->err >= -20) && (pid->err <= 20))
{
pid->err = 0;
pid->integral = 0;
}
pid->integral += pid->err; // 误差累积
/*PID算法实现*/
pid->actual_val = pid->Kp*pid->err+pid->Ki*pid->integral+pid->Kd*(pid->err-pid->err_last);
/*误差传递*/
pid->err_last=pid->err;
/*返回当前实际值*/
return pid->actual_val;
}
2 STM32Cube 配置项目
2.1 配置参数
1)配置DAC参数,使用DAC2模块,选择PA5作为输出IO
DAC相关参数配置
2)配置ADC参数,选择ADC1,通道号为IN2
ADC1参数配置
ADC1对应的DMA配置
使能DMA1中断
2.2 GENERATE项目
配置完成参数后,点击GENERATE生成项目,具体目录结构如下:
3 功能实现
端口介绍:
PA2: 模拟量ADC的输入端口
PA5: 模拟量DAC的输出端口
3.1 ADC采样数据功能
实现功能:
1)使能ADC模块
2)数据处理
3)DMA调用ADC相关的回调函数
typedef struct
{
uint8_t sta : 2; //00: IELD, 01: Ready 02: Fail 11: unuse
uint8_t res : 6;
} Status_bit;
typedef struct
{
uint16_t adcRaw;
union{
uint8_t status;
Status_bit status_bit;
};
}Stru_GetAdc;
static uint32_t adcDMAValue[2];
Stru_GetAdc struc_GetAdc;
void ADC_ConvInit( ADC_HandleTypeDef* hadc )
{
HAL_ADC_Start_DMA(hadc, adcDMAValue, 4);
}
void ADC_HandleDataPackets( void )
{
uint16_t adcvalueList[4];
uint32_t value=0;
adcvalueList[0] = adcDMAValue[0]&0x0000ffff;
adcvalueList[1] = (adcDMAValue[0]>>16)&0x0000ffff;
adcvalueList[2] = adcDMAValue[1]&0x0000ffff;
adcvalueList[3] = (adcDMAValue[1]>>16)&0x0000ffff;
value = 0;
for( int i =0; i< 4; i++ ){
value += adcvalueList[i];
}
struc_GetAdc.adcRaw = value>>2;
struc_GetAdc.status_bit.sta = 1;
}
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
if(hadc->Instance == ADC1)
{
HAL_ADC_Stop_DMA(hadc); // stop to convert adc value
ADC_HandleDataPackets();
HAL_ADC_Start_DMA(hadc, adcDMAValue, 4); // start to convert adc value
}
}
3.2 DAC数据转换
功能介绍:
设置对应通道需要转换的数字量
void DAC_SetConvert(DAC_HandleTypeDef *hdac, uint32_t Channel, uint16_t value)
{
HAL_DAC_SetValue( hdac, Channel, DAC_ALIGN_12B_R,value );
}
3.3 PID相关的调制函数
代码73行: 获取当前ADC的值
代码74行:获取PID的期望值
代码75行: 比较期望值和实际值的误差
代码78行:使用当前的ADC值进行PID运算
代码80行:设置DAC的输出值
详细源代码:
void ADC_DAC_MatchByPID( void )
{
bool is_match;
uint32_t current_val,cal_value;
{
current_val = struc_GetAdc.adcRaw;
cal_value = pid_get_target(&pid_speed);
is_match = abs((int)((current_val- cal_value))) > 5 ? false : true;
if( !is_match )
{
cal_value = pid_speed_realize(&pid_speed, current_val);
cal_value = cal_value > MAX_VALE ? MAX_VALE: cal_value;
DAC_SetConvert( &hdac, DAC_CHANNEL_2, cal_value );
}
printf("ADC Raw Count: %04d , voltage: %0.2f V \r\n",current_val, (current_val*3.3)/4096 );
}
}
4 测试
4.1 编写测试代码
代码96行:初始化PID数据结构
代码97行:设置期望值
代码97行:设置P,I,D三个参数的值
详细源码:
void voltage_InitPara( void )
{
pid_param_init();
pid_set_target( &pid_speed, 3400);
pid_set_para(&pid_speed, 0.5, 0.1, 0.1);
}
4.2 运行结果
编译代码,下载到板卡中运行.
测试1: 期望值RAW = 3400,误差delta < 5对应实际电压值V = 2.74V
稳定前的值:
稳定后的值:
测试2: 期望值RAW = 1400,误差delta < 5,对应实际电压值V = 1.12V
void voltage_InitPara( void )
{
pid_param_init();
pid_set_target( &pid_speed, 1400);
pid_set_para(&pid_speed, 0.5, 0.1, 0.1);
}
稳定前的值:
稳定后的值: