本例使用的是LPC21XX系列芯片提供的PWM功能实现稳定的温度控制。首先我们获得当前环境温度之后,再用设定的温度与当前温度相减,通过PID算法计算出当前输出脉宽,并将其输出到L298N模块中,使加热丝发热,形成闭环,经过一段时间温度稳定在预设值。
概念说明
- PWM:PWM(Pulse Width Modulation)简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。PWM输出的是周期信号,其中PWM频率指的是1秒内PWM的周期次数,占空比指的是一个脉冲周期内,高电平的时间与整个周期时间的比例。PWM可通过其不同的占空比配置来实现LED呼吸灯,电机转速等应用:
- PID算法:就是“比例(proportional)、积分(integral)、微分(derivative)”,是一种常见的“保持稳定”控制算法,在闭环系统的控制中,可自动对控制系统进行准确且迅速的校正,是工业应用中最广泛算法之一。PID算法主要涉及三个最基本的参数,最终PID输出值是三部分调节增益之和Pout+Iout+Dout:
- Kp比例增益:已知当前环境温度与用户设定值之间的差值Ek,Kp与Ek在Pout的计算中为乘法关系,其大小将直接影响系统的响应速度
- Kd微分增益:两次差值之差可表示Dk,Kd与Dk也是乘法关系,其作用有让其"变化速度"趋于0,即类似"阻尼"的作用。
- Ki积分增益:Ki积分控制考虑过去误差,将误差值过去一段时间之和(误差和)乘以一个正值的常数Ki,其作用是减小静态情况下的误差,让受控物理量尽可能接近目标值。
- L298N:L298N是意法半导体集团旗下量产的一种电机驱动芯片,拥有工作电压高、输出电流大、驱动能力强、发热量低、抗干扰能力强等特点,通常用来驱动继电器、螺线管、电磁阀、直流电机以及步进电机。本例我们用来驱动一个加热丝来进行发热。
实现原理
嵌入式代码运行在LPC21XX系列芯片平台上,使用平台提供的PWM通道进行控制信号输出,实现原理如下图所示:
嵌入式程序
LPC21XX平台使用PWM功能涉及的步骤如下:
- 首先是配置 PLL以生成时钟,因为它根据程序员的需要设置 LPC2148 的系统时钟和外设时钟。LPC2148 的最大时钟频率为 60Mhz。
- 接下来是使用 PINSEL 寄存器选择 LPC2148 的 PWM 引脚和 PWM 功能。我们使用 PINSEL0,因为我们使用 P0.0 作为 LPC2148 的 PWM 输出。
- 接下来我们需要使用 PWMTCR(定时器控制寄存器)重置定时器,然后,设置决定 PWM 分辨率的预分频值。我将它设置为零。
- 接下来我们需要设置 PWMMCR(PWM 匹配控制寄存器),因为它设置了复位等操作,PWMMR0 的中断。
- 使用 PWMMR 设置 PWM 通道的最大周期。
- 接下来我们需要使用 PWMLER 将 Latch Enable 设置为相应的匹配寄存器。
- 要使 PWM 输出到引脚,我们需要使用 PWMTCR 来启用 PWM 定时器计数器和 PWM 模式。
代码如下,提供UpdatePWMDutyRatio接口可以实时调整占空比:
#include <lpc214x.h>
#include <stdint.h>
#include <string.h>
unsigned int PWMvalue=0;
void initilizePLL(void);
void initilizePWM(unsigned int periodPWM);
void delaytime(uint16_t j);
void UpdatePWMDutyRatio();
void initilizePLL (void) //Function to use PLL for clock generation
{
PLL0CON = 0x01;
PLL0CFG = 0x24;
PLL0FEED = 0xAA;
PLL0FEED = 0x55;
while(!(PLL0STAT & 0x00000400));
PLL0CON = 0x03;
PLL0FEED = 0xAA;
PLL0FEED = 0x55;
VPBDIV = 0x01;
}
void delaytime(uint16_t j) // fucntion to generate 1 milisecond delay
{
uint16_t x,i;
for(i=0;i<j;i++)
{
for(x=0; x<6000; x++);
}
}
void initilizePWM(unsigned int PWMvalue)
{
PINSEL0 = 0x00000002; //Setting pin P0.0 for PWM output
PWMTCR = (1<<1); //Setting PWM Timer Control Register as counter reset
PWMPR = 0X00; //Setting PWM prescale value
PWMMCR = (1<<0)|(1<<1); //Setting PWM Match Control Register
PWMMR0 = PWMvalue; //Giving PWM value Maximum value
PWMLER = (1<<0); //Enalbe PWM latch
PWMTCR = (1<<0) | (1<<3); //Enabling PWM and PWM counter
}
void UpdatePWMDutyRatio()
{
PWMTCR = ~((1<<0) | (1<<3)); //Disable PWM and PWM counter
PWMMR0 = PWMvalue; //Giving PWM value
PWMTCR = (1<<0) | (1<<3); //Enabling PWM and PWM counter
}
PID.h文件内是PID算法因子结构体定义,代码就不再往这里贴了。下面是PID算法实现的代码,代码中用中文帮助您理解:
#include "PID.h"
PID pid;
void PID_Init()
{
pid.Sv=38;//用户设定温度
pid.Kp=30;
pid.T=400;//PID计算周期
pid.Ti=4000000;//积分时间
pid.Td=1000;//微分时间
pid.pwmcycle=200;//pwm周期200
pid.OUT0=1;
pid.C1ms=0;
}
void PID_Calc() //pid计算
{
float DelEk;
float ti,ki;
float td;
float kd;
float out;
if(pid.C1ms<(pid.T)) //计算周期未到
{
return ;
}
pid.Ek=pid.Sv-pid.Pv; //得到当前的偏差值
pid.Pout=pid.Kp*pid.Ek; //比例输出
pid.SEk+=pid.Ek; //历史偏差总和
DelEk=pid.Ek-pid.Ek_1; //最近两次偏差之差
ti=pid.T/pid.Ti;
ki=ti*pid.Kp;
pid.Iout=ki*pid.SEk; //积分输出
td=pid.Td/pid.T;
kd=pid.Kp*td;
pid.Dout=kd*DelEk; //微分输出
out= pid.Pout+ pid.Iout+ pid.Dout;
if(out>pid.pwmcycle)
{
pid.OUT=pid.pwmcycle;
}
else if(out<=0)
{
pid.OUT=pid.OUT0;
}
else
{
pid.OUT=out;
}
pid.Ek_1=pid.Ek; //更新偏差
pid.C1ms=0;
}
下面是此例的main函数,通过上面封装的接口对温度进行控制,稳定的达到目标温度:
#include "PID.h"
#include "PWMOUT.h"
exyern unsigned int PWMvalue;
#define PERIOD 400
int main()
{
unsigned int num=0;
PID_Init();
initilizePLL();
while(1)
{
while(获取当前温度,赋值到pid.pv中);
PID_Calc();//计算PID的值
num=(((pid.OUT*PERIOD)/pid.pwmcycle)-1);//获取当前的PWM脉冲占空比
PWMvalue=num;
UpdatePWMDutyRatio();
}
}
十六宿舍 原创作品,转载必须标注原文链接。
©2023 Yang Li. All rights reserved.
欢迎关注 『十六宿舍』,大家喜欢的话,给个👍,更多关于嵌入式相关技术的内容持续更新中。