编码器输出的A向脉冲接到单片机的外部中断INT0,B向脉冲接到I/O端口P1.0。
当系统工作时,首先要把INT0设置成下降沿触发,并开相应中断。
当有有效脉冲触发中断时,进行中断处理程序,判别B脉冲是高电平还是低电平,若是高电平则编码器正转,加1计数;若是低电平则编码器反转,减1计数。
基于51单片机的直流电机PID闭环调速系统原理详解与程序(2013-08-04 01:18:15)转载▼标签:分类:单片机51单片机直流电机pidpcf8591基于51单片机的直流电机PID闭环调速系统1.电机转速反馈:原理:利用光电编码器作为转速的反馈元件,设电机转一周光电编码器发送N个PWM波形,利用测周法测量电机转速。
具体实现:将定时器0设置在计数模式,用来统计一定的时间T内接受到的脉冲个数M个,而定时器0置在计时模式,用来计时T时间。
则如果T时间接受到M个PWM波形,而电机转一圈发出N个PWM波形,则根据测周法原理,电机的实际的转速为:real_speed=M/(N*T),单位转/秒。
若将定时器1置在计数模式,则PWM波形应该由P3^3脚输入。
代码实现://定时器0初始化,用来定时10msvoid Init_Timer0(void){TMOD |= 0x01; //使用模式1,16位定时器,且工作在计时模式TH0=(65536-10000)/256; //定时10msTL0=(65536-10000)%6;EA=1; //总中断打开ET0=1; //定时器中断打开TR0=1; //定时器开关打开}// 计数器1初始化,用来统计定时器1计时250ms内PWM波形个数void Init_Timer1(void){TMOD |= 0x50; //使用计数模式1,16位计数器模式TH1=0x00; //给定初值,由0往上计数TL1=0x00;EA=1; //总中断打开ET1=1; //定时器中断打开TR1=1; //定时器开关打开}//定时器0的中断服务子函数,主要完成脉冲个数的读取,实际转速的计算和PID 控制以及控制结//果输出等工作void Timer0_isr(void) interrupt 1{unsigned char count;TH0=(65536-10000)/256; //重新赋值 10msTL0=(65536-10000)%6;count++;if (count==25) //如果达到250ms,则计算一次转速并进行一次控制运算{count=0;//清零以便于定时下一个250msTR1=0;//关闭定时器1,统计脉冲个数real_speed=(256*TH1+TL1)*4/N;//250ms内脉冲个数并由此计算转速TH1=0x00; //计数器1清零,重新开始计数TL1=0x00;TR1=1;OUT=contr_PID();//进入PID控制,PID控制子函数代码在后面给出write_add(0x40,OUT);//进行DA转换,将数字量转换为模拟量,后面会介绍到 }}2.PID控制:PID的基本原理在这里不作具体讲解,这里主要给出PID算法的实现,通过调节结构体中比例常数(Proportion)、积分常数(Integral)、微分常数( Derivative)使得转速控制达到想要的精度。
试凑法:注意这里参数调节采用实验凑试法,试凑法也有其规律,下面做出讲解:实验凑试法是通过闭环运行或模拟,观察系统的输出结果,然后根据各参数对系统的影响,反复凑试参数,直至出现满意的响应,从而确定PID控制参数。
实验凑试法的整定步骤为"先比例,再积分,最后微分"。
(1)整定比例控制将比例控制作用由小变到大,观察各次响应,直至得到反应快、超调小的响应曲线。
(2)整定积分环节若在比例控制下稳态误差不能满足要求,需加入积分控制。
先将步骤(1)中选择的比例系数减小为原来的50~80%,再将积分时间置一个较大值,观测响应曲线。
然后减小积分时间,加大积分作用,并相应调整比例系数,反复试凑至得到较满意的响应,确定比例和积分的参数。
(3)整定微分环节若经过步骤(2),PI控制只能消除稳态误差,而动态过程不能令人满意,则应加入微分控制,构成PID控制。
先置微分时间TD=0,逐渐加大TD,同时相应地改变比例系数和积分时间,反复试凑至获得满意的控制效果和PID控制参数。
代码实现unsigned int Proportion=8;//比例常数 Proportional Constunsigned int Integral=3;//积分常数 Integral Constunsigned int Derivative=1;//微分常数 Derivative Constunsigned int LastError=0;//Error[-1]unsigned int PrevError=0;//Error[-2]unsigned int Error=0;//Error[0]int Out=0;//outcomeint contr_PID(){Error=expc_speed-real_speed;//计算偏差//进行增量式PID计算Out=Out+Proportion*(Error-LastError)+Integral*Error+Derivative*(Error+PrevError-2*LastError);//进行误差更新PrevError=LastError;LastError=Error;//上下限幅处理if(Out<0)Out=0;//驱动电机的电压不可能小于零,故当输出小于零时,输出应该值为零if(Out>255)Out=255;//DA转换为8位,最大数值为255,故当大于255时,限制为255 return Out;}3.电机驱动:原理:直流有刷电机既可以用模拟电压驱动,又可以用PWM驱动.如果用PWM驱动时,刚刚PID后的输出可以直接对应到PWM高电平持续的时间,PWM周期为T,则低电平对应为T-Out即可,这个实现比较简单。
这里介绍利用DA将数字量Out 转化为模拟电压驱动电机的方法。
模拟电压驱动实现:将数字量转化为模拟量的最常用方法就是使用DA转换。
在一般的使用中我比较喜欢的一款芯片是PCF8591。
它内部集成了4路8位AD转换和1路8位DA转换,与单片机之间使用IIC通信方式。
由于IIC通信只需要SDA 和SCL两根信号线,因此很省IO口,并且一个芯片AD,DA都有了,很方便。
具体的PCF8591资料请参考其datasheet,这里直接给出其封装好的IIC通信代码,使用直接就可以了。
这里面其他的函数不用管,因为它们最终在两个函数中被调用,即write_add()和read_add()中。
前者实现DA转换,有两个参数,第一个参数是控制DA转换对应寄存器地址,取0x40,第二个是要转换的数据,即返回的OUT,使用时直接一句代码:write_add(0x40,OUT)就能将数字信号转换为模拟电压信号。
后者实现AD转换,只有一个参数,就是选择AD转换的4个通道,通过宏定义可以定义出其四个通道:#define AD_IN0 0x40#define AD_IN1 0x41#define AD_IN2 0x42#define AD_IN3 0x43使用时只需要一句代码:dat=read_add(AD_IN2)就能实现将第二个通道的模拟信号转换为数字信号。
代码实现://PCF8591.h#define uchar unsigned char//延时4usvoid delay(void);//iic初始化void init(void);// iic开始信号void start(void) ;// iic停止信号void stop(void) ;// iic应答相当于一个智能的延时函数void respons(void) ;// iic读一个字节数据uchar read_byte(void) ;//iic写一字节数据void write_byte(uchar date) ;// 控制DA转换void write_add(uchar control,uchar date);// 控制AD转换uchar read_add(uchar control) ;//PCF8591.c#include<AD.h>#include<reg52.h> #define PCF8591 0x90 //默认为写,如果为读,则为0x91 sbit SDA=P2^0;sbit SCL=P2^1;//延时4usvoid delay(void){unsigned char i;for(i=1;i>0;i--);} // iic初始化void init(){SDA=1;delay();SCL=1;delay();} // iic开始信号void start(){SDA=1;delay();SCL=1;delay();SDA=0;delay();}// iic停止信号void stop(){SDA=0;delay();SCL=1;delay();SDA=1;delay();} // iic应答相当于一个智能的延时函数void respons(){uchar i;SCL=1;delay();while((SDA==1)&&(i<250))i++;SCL=0;delay();}// iic读一个字节数据uchar read_byte(){uchar i,k;SCL=0;delay();SDA=1;delay();for(i=0;i<8;i++){SCL=1;delay();k=(k<<1)|SDA;//先左移一位,再在最低位接受当前位SCL=0;delay();}return k;} //iic写一字节数据void write_byte(uchar date){uchar i,temp;temp=date;for(i=0;i<8;i++){temp=temp<<1; //左移一位移出的一位在CY中SCL=0; //只有在scl=0时sda能变化值delay();SDA=CY;delay();SCL=1;delay();}SCL=0;delay();SDA=1;delay();}// 控制DA转换void write_add(uchar control,uchar date){start();write_byte(PCF8591);respons();write_byte(control);respons();write_byte(date);respons();stop();}// 控制AD转换uchar read_add(uchar control){uchar date;start();write_byte(PCF8591);respons();write_byte(control);respons();start();write_byte(PCF8591+1);//把最后一位变成1,读数据 respons();date=read_byte();stop();return date;}主函数:主函数中主要是一些初始化工作,还有按键检测和实际转速和期望转速显示。