加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

AT89C51单片机PWM直流电机转速PID控制源程序

(2017-01-30 08:36:28)
标签:

51单片机

c语言源程序

pid控制转速

    AT89C51单片机PWM直流电机转速PID控制源程序,重点介绍利用MCS-51系列单片机控制PWM信号从而实现对直流电机转速进行控制的方法。设计中采用了专门的芯片组成了PWM信号的发生系统并且对PWM信号的原理、产生方法以及如何通过软件编程对PWM信号占空比进行调节从而控制其输入信号波形等均作了详细的阐述。另外本系统中使用了红外对管对直流电机的转速进行测量,经过整形电路后将测量值送到单片机并且最终作为反馈值输入到单片机进行PID运算从而实现了对直流电机速度的控制。在软件方面,文章中详细介绍了PID运算程序初始化程序等的编写思路和具体的程序实现。
    1 单片机最小系统:单片机最小系统由51单片机,晶振电路,复位电路,电源组成。大家都比较熟悉,这里不再赘述。
    2 四位数码管显示:在应用系统中,设计要求不同,使用的LED显示器的位数也不同,因此就生产了位数,尺寸,型号不同的LED显示器供选择,在本设计中,选择4位一体的数码型LED显示器,简称“4-LED”。本系统中前三位显示电压的整数位,最后一位显示转速的小数位。4-LED显示器引脚如图2所示,是一个共阴极接法的4位LED数码显示管,其中a,b,c,e,f,g为4位LED各段的公共输出端,1、2、3、4分别是每一位的位数选端,dp是小数点引出端,4位一体LED数码显示管的内部结构是由4个单独的LED组成,每个LED的段输出引脚在内部都并联后,引出到器件的外部。
    3 电机驱动电路:电机驱动电中是采用ULN2003来驱动。ULN2003是高耐压、大电流达林顿陈列,由七个硅NPN达林顿管组成。该电路的特点:ULN2003的每一对达林顿都串联一个2.7K的基极电阻,在5V的工作电压下它能与TTL和CMOS电路直接相连,可以直接处理原先需要标准逻辑缓冲器来处理的数据,输入5VTTL电平,输出可达500mA/50V。ULN2003的引脚图,其中IN1~IN7为输入控制端;OUT1~OUT7为输出端;8脚为芯片的接地端;9脚为公共端,该脚是内部7个续流二极管负极的公共端,各二极管的正极分别接各达林顿管的集电极。用于感性负载时,该脚接负载电源正极,实现续流作用。如果该脚接地,实际上就是达林顿管的集电极对地接通。 
当P1.0中为高电平时,其内部三极管导通,使电机转动。当P1.0为低电平时,内部三极管截止,电路断开,电机停止转动。所以在程序中可以利用P1.0口输出PWM波来控制电机的转速。
    4 红外测速电路:发射管工作时发出红外线,当接收管收到红外信号时,其电阻变小(本设计相当于从无穷大变到1k左右)。利用其电阻变化,改变接收管分压情况。挡片是利用圆盘上剪四个孔,当挡片随电机转动时,接收管两端电平发生变化,产生脉冲。
    5 整形电路:本设计的整形电路是用555定时器接成的施密特触发器。
    6 源程序:
#include  "reg52.h"
#define uchar unsigned char
#define uint  unsigned int
uchar code table[10]={0x3f,0x06,0x5b,
0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};            //共阴数码管显示码(0-9)
sbit xiaoshudian=P0^7;
sbit wei1=P2^4;                                 //数码管位选定义
sbit wei2=P2^5;
sbit wei3=P2^6;
sbit wei4=P2^7;
sbit beep=P2^3;                                 //蜂鸣器控制端
sbit motor  = P1^0;                             //电机控制
sbit s1_jiasu = P1^4;                           //加速按键
sbit s2_jiansu= P1^5;                           //减速按键
sbit s3_jiting=P1^6;                            //停止/开始按键
uint pulse_count;                               //INT0接收到的脉冲数
uint num=0;                                     //num相当于占空比调节的精度
uchar speed[3];                                 //四位速度值存储
float bianhuasudu;                              //当前速度(理论计算值)
float reallyspeed;                              //实际测得的速度
float vv_min=0.0;vv_max=250.0;
float vi_Ref=60.0;                              //给定值
float vi_PreError,vi_PreDerror;
uint pwm=100;                                   //相当于占空比标志变量
int sample_time=0;                              //采样标志
float v_kp=1.2,v_ki=0.6,v_kd=0.2;               //比例,积分,微分常数
void delay (uint z)  
{
      uint x,y;
       for(x=z;x>0;x--)
              for (y=20;y>0;y--);
}
void time_init()
       ET1=1;                                                   //允许定时器T1中断
       ET0=1;                                                   //允许定时器T0中断
       TMOD = 0x15;                                        //定时器0计数,模式1;定时器1定时,模式1                                     
       TH1 = (65536-100)/256;                      //定时器1值,负责PID中断 ,0.1ms定时
    TL1 = (65536-100)%6;
       TR0 = 1;                                                 //开定时器
       TR1 = 1;
       IP=0X08;                                                //定时器1为高优级
       EA=1;                                       //开总中断   
}
void keyscan()
{
     float j;
     if(s1_jiasu==0)                            //加速
     {
         delay(20);
         if(s1_jiasu==0)
             vi_Ref+=10;
             j=vi_Ref;
     }
     while(s1_jiasu==0);
     if(s2_jiansu==0)                           //减速
     {
         delay(20);
         if(s2_jiansu==0)
              vi_Ref-=10;
              j=vi_Ref;
     }
     while(s2_jiansu==0);
     if(s3_jiting==0)
     {
         delay(20);
         motor=0;
         P1=0X00;
         P3=0X00;
         P0=0x00;
     }
     while(s3_jiting==0);
}
float v_PIDCalc(float vi_Ref,float vi_SpeedBack)
{   
    register float error1,d_error,dd_error;
    error1=vi_Ref-vi_SpeedBack;                 //偏差的计算
    d_error=error1-vi_PreError;                 //误差的偏差
    dd_error=d_error-vi_PreDerror;              //误差变化率
    vi_PreError=error1;                         //存储当前偏差
    vi_PreDerror=d_error;
    bianhuasudu=(v_kp*d_error+v_ki*vi_PreError+v_kd*dd_error);
    return (bianhuasudu);
}
void v_Display()
{
    uint sudu;
    sudu=(int)(reallyspeed*10);                 //乘以10之后强制转化成整型
    speed[3]=sudu/1000;                         //百位
    speed[2]=(sudu00)/100;                   //十位
    speed[1]=(sudu0)/10;                     //个位
    speed[0]=sudu;                           //小数点后一位
    wei1=0;                                     //第一位打开
    P0=table[speed[3]];
    delay(5);
    wei1=1;                                     //第一位关闭
    wei2=0;
    P0=table[speed[2]];
    delay(5);
    wei2=1;
    wei3=0;
    P0=table[speed[1]];
    xiaoshudian=1;
    delay(5);
    wei3=1;
    wei4=0;
    P0=table[speed[0]];
    delay(5);
    wei4=1;
}
void BEEP()
{
    if((reallyspeed)>=vi_Ref+5||(reallyspeed<=vi_Ref-5))
    {
        beep=~beep;
        delay(4);
    }
}
void main()
{
    time_init();
    motor=0;
    while(1)
   {
       v_Display();
        BEEP();
    }
    if(s3_jiting==0)                            //对按键3进行扫描,增强急停效果
     {
         delay(20);
         motor=0;
         P1=0X00;
         P3=0X00;
         P0=0x00;
     }
     while(s3_jiting==0);
}
void timer0() interrupt 1
{

}
void timer1() interrupt 3
{
    TH1 = (65536-100)/256;                           //1ms定时
       TL1 = (65536-100)%6;
       sample_time++;
       if(sample_time==5000)                      //采样时间0.1ms*5000=0.5s
       {
              TR0=0;                                            //关闭定时器0
              sample_time=0;
              pulse_count=TH0*255+TL0;             //保存当前脉冲数
        keyscan();                              //扫描按键
              reallyspeed=pulse_count/(4*0.6);         //计算速度
        pwm=pwm+v_PIDCalc(vi_Ref,reallyspeed);
              if(pwm<0)pwm=0;
              if(pwm>100)pwm=100;  
              TH0=TL0=0;        
              TR0=1;                                                  //开启定时器0
       }
       num++;
       if(num==pwm)                                              //此处的num值,就是占空比          
       {
              motor=0;
       }
       if(num==100)                                         //100相当于占空比调节的精度  
       {
              num=0;
              motor=1;
       }
}

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有