谁有直流电机闭环控制的仿真

2019-07-18 12:16发布

本帖最后由 许明康2013 于 2015-5-1 20:33 编辑

4个按键实现小直流电机的加减速和正反转,L298N驱动,LCD1602显示设定速度和反馈速度的     仿真图和程序
完整的程序!!!
下面的图和程序,不知道哪里有问题,减速为0时,电机还在转,而且没有固定的波形??有哪位大神直到啊??

#include <reg52.h>
#include <intrins.h>

                        //IO口设置          a=IN1,b=IN2
sbit a=P2^4;            //L298信号口,{ a=1 ;b=0;}      //启动输出  正转
sbit b=P2^5;            //            { a=0 ;b=1;}      //启动输出   反转
sbit K1 =P1^0 ;                    //减少键
sbit K2 =P1^1 ;                    //增加键
sbit K3 =P1^2 ;                    //正转键
sbit K4 =P1^3 ;                    //反转键

unsigned int  speed_s=10;      //设置的转速,单位r/s
unsigned int  speed_m=0;       //测量的转速
unsigned char  zhuanxiang=1;   //转向标志,0不转,1正转,2反转.默认正转                 ....

#define LCD_DATA P0
sbit LCD_RS = P2^0;
sbit LCD_RW = P2^1;
sbit LCD_EN = P2^2;
unsigned char dispBuff0[16]={'S','D',':',' ',' ',' ',' ',' ',' ',' ',' ','r','/','m','i','n'};//LCD第一行显示  不显示用空格
unsigned char dispBuff1[16]={'D','Q',':',' ',' ',' ',' ',' ',' ',' ',' ','r','/','m','i','n'};//LCD第二行显示

void    Delay20ms() ;
void         LCD_WriteDat(unsigned char lcd_dat);
void         LCD_WriteCmd(unsigned char lcd_cmd);
unsigned char         LCD_ReadStatus(void);
void         LCD_Goto(unsigned char x,unsigned char y);
void    LCD_Display(unsigned char row,unsigned char *str);


  void         LCD_Init(void)
{
        Delay20ms();
    LCD_WriteCmd(0x38);        //8位机接口、双行显示、5×7字符点阵;
        LCD_WriteCmd(0x0c);        //显示开启、光标不显示也不闪烁;
        LCD_WriteCmd(0x01);        //清屏;
        LCD_WriteCmd(0x06);        //光标右移一位、整屏不移动;

    LCD_Goto(0,0);
        }
void         LCD_WriteDat(unsigned char lcd_dat)        //写数据 数字 字母
{
        unsigned char tmp;
        tmp = LCD_ReadStatus();                //读状态;
        while((tmp & 0x80))                        //是否忙 ?
                {
                        tmp = LCD_ReadStatus();
                        }
       
        LCD_RS = 1;        //数据寄存器
        LCD_RW = 0;         //写
        LCD_DATA = lcd_dat;
        _nop_();
        LCD_EN = 0;
        _nop_();
        _nop_();
        LCD_EN = 1;
       
        }
void         LCD_WriteCmd(unsigned char lcd_cmd)         //写指令数据到LCD
{
        unsigned char tmp;
        tmp = LCD_ReadStatus();
        while((tmp & 0x80))
        {
                tmp = LCD_ReadStatus();
                }
       
        LCD_RS = 0;         //指令寄存器
        LCD_RW = 0;           //写
        LCD_DATA = lcd_cmd;
        _nop_();
        LCD_EN = 0;
        _nop_();
        _nop_();
        LCD_EN = 1;
        }
unsigned char         LCD_ReadStatus(void)
{
        unsigned char tmp;
        #if 0
        LCD_RS = 0;
        LCD_RW = 1;
        LCD_EN = 1;
        tmp = LCD_DATA;
        LCD_EN = 0;
        #endif
        LCD_DATA = 0xff;
        LCD_RS = 0;
        LCD_RW = 1;
        LCD_EN = 0;
        _nop_();
        _nop_();
        LCD_EN = 1;
        tmp = LCD_DATA;
        return tmp;
        }
void         LCD_Goto(unsigned char x,unsigned char y)
{
        unsigned char tmp;
        if(y)                                //若是第二行;
                {
                        tmp = 0xc0 + x;
                        LCD_WriteCmd(tmp);
                        }
        else
                {
                        tmp = 0x80 + x;
                        LCD_WriteCmd(tmp);
                        }               
        }


       
void LCD_Display(unsigned char row,unsigned char *str)
{
        if(row)
                {
                        LCD_Goto(0,1);
                        }
  else
          {
                  LCD_Goto(0,0);
                  }
  while(*str != '')
  {
          LCD_WriteDat(*str++);
          }
        }


void Delay20ms()   //粗略延时;
{
  unsigned int tmp = 50000;
  while(tmp--);
  }       

//        .............................................................................
void key()        //正转反转调节  zhuanxiang; 转向标志,0不转,1正转,2反转
{
    if(!K1)          //减速
  {
            speed_s--;
     if (speed_s<0)
       { speed_s=0;
           }
  }
    if(!K2)           //加速
  {  if (speed_s<20)
       { speed_s++;}
  }
  dispBuff0[4]=(speed_s*60/1000)%10+'0';
  dispBuff0[5]=(speed_s*60/100)%10+'0';
  dispBuff0[6]=(speed_s*60/10)%10+'0';
  dispBuff0[7]=(speed_s*60)%10+'0';
  LCD_Display(0,dispBuff0);

  if(!K3 && K4)           //正转
    {
     zhuanxiang=1;
    }
  if(!K4 && K3)             //反转
    {
        zhuanxiang=2;
    }
  if(!K4 && !K3)        //两键按下,停止
    {
        zhuanxiang=0;
    }
}


/*------------------测转速程序------------------*/
unsigned int pusle=0;//脉冲个数
unsigned char count=0;//个数

void timer2 (void) interrupt 5//定时器2中断  计时1s
{         

             if(TF2==1)
           {
                TF2=0;   // 溢出标志必须软件清0
                   count++;
            if(count>=20)//这就大约1s
      {
             speed_m=pusle/4;//测速转速,单位r/M.转一圈产生2个脉冲
             dispBuff1[4]=(speed_m*60/1000)%10+'0';
                dispBuff1[5]=(speed_m*60/100)%10+'0';
             dispBuff1[6]=(speed_m*60/10)%10+'0';
                dispBuff1[7]=(speed_m*60)%10+'0';
             LCD_Display(1,dispBuff1);
             count=0;//计数变量清零
             pusle=0;
      }
        }
       
}

void int0() interrupt 0 //外部中断0中断处理程序,用于脉冲计数
{       
        pusle++;
}

/*-------------电机调速程序,增量式PID算法------------*/
int Now_speed[3]={0,0,0};  //用于存储当前转速、前一次转速、再前一次转速

int KP=100;
int KI=20;
int KD=15;
int last_out=0;
#define out_max 10000
#define out_min 0
unsigned int PWMH=0;//PWM波在10000us内高电平时间

/********电机PID控制**********/
void Motor_control(void)      
{
    int PID=0;
    int P=0;
    int I=0;
    int D=0;
    int out=0;

    Now_speed[2] = Now_speed[1];
    Now_speed[1] = Now_speed[0];
    Now_speed[0] = speed_s-speed_m;
    P = KP*(Now_speed[0]-Now_speed[1]);
    I = KI* Now_speed[0];
    D = KD*(Now_speed[0]-2*Now_speed[1]+Now_speed[2]);
        PID = P+I+D;
        out=last_out+PID;
        if(out>out_max) out=out_max;        //大于10000,取值为10000
        if(out<out_min) out=out_min;        //小于0,取值为0
    PWMH=out;
        last_out=out;
}

unsigned char jishi=0;//计时
void timer0 (void) interrupt 1 //定时器0中断,用于定时产生PWM周期,一个控制周期定为10000us
{   
        jishi++;
        if (jishi>=100)//1s计算一次PID
    {/*设置并打开定时器1*/
    Motor_control()  ;
    jishi=0;
    }
  TH1=((65536-PWMH) & 0xff00)>>8;           // 高8位
  TL1=(65536-PWMH) & 0x00ff;                   // 低8位
  TR1=1; //运行开关  打开计时器1
TH0=0xb8; //10000us
TL0=0x00;

switch (zhuanxiang)
       {case 0:
              { a=0 ;b=0;break;}      //停止
        case 1:
              {  a=1 ;b=0;break;}      //启动输出  正转
        case 2:
              { a=0 ;b=1; break;}      //启动输出   反转
        case 3:
                      {
                          if(speed_s=0)
                               {
                                 a=0;b=0;break;
                                }
                          
                          }
            default:break;
       }
}

void timer1 (void) interrupt 3//定时器1中断,用于定时产生PWM波的高电平时间
{
a=0 ;b=0;    //PWM输出低电平
TR1=0;
}




/*----------------主程序----------------------*/
void main()
{
/*计时器2设置,用于定时1s测速*/
TCLK=0;     //可令T2CON=0;或TCLK=0,RCLK=0;
RCLK=0;     //【T2CON中其他位可默认为0,而TCLK和RCLK必须手动置0】
            //因RCAP2L和RCAP2H是由软件预设的
RCAP2H=0x3C;
RCAP2L=0xB0;

TL2=0x00;// TL2 = (65536-50000) % 256;  //或TL2=0xb0
TH2=0x4C;// TH2 = (65536-50000) / 256;  //或TH2=0x3c

ET2=1;
TR2=1;

/*计时器0设置,用于设置PWM的周期*/
TMOD=0x11; //定时器1、2都工作于方式1
TH0=0xb8;   //10000us   高8位
TL0=0x00;   //低8位
ET0=1;     //定时器0的中断允许
TR0=1;     //运行开关 开始计时

/*计时器1设置,用于设置PWM的高电平时间*/
TH1=0;
TL1=0;
ET1=1;     //定时器1的中断允许
TR1=0;     //停止计时

/*外部中断0设置*/
IT0=1;    //外部中断0,0低电平触发,1边沿触发
EX0=1;    //打开外部中断0
EA=1;//开全局中断

LCD_Init();   //LCD初始化

LCD_Display(0,dispBuff0);
LCD_Display(1,dispBuff1);
Delay20ms();
while(1)
     {key();//按键识别
      Delay20ms()           ;
     }

}


W`4T3ZLPE6$36Q4A]9VSDD1.png
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。