微波EDA网,见证研发工程师的成长!
首页 > 研发问答 > 嵌入式设计讨论 > MCU和单片机设计讨论 > 有谁可以帮我写一个直流电机闭环控制程序?

有谁可以帮我写一个直流电机闭环控制程序?

时间:10-02 整理:3721RD 点击:

四个按键,加减速和正反转,通过占空比调节速度;L298驱动,LCD1602显示设定速度和反馈速度
程序附录:  程序有问题,减速为零,电机还在转?波形也没有?
#include <reg52.h>
#include <intrins.h>
                        //IO口设置
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=2;      //设置的转速,单位r/s
unsigned int  speed_m=0;       //测量的转速
unsigned char  zhuanxiang=1;   //转向标志,0不转,1正转,2反转.默认正转

                        //LCD模块IO口以及子程序定义
#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);
/*1、LCD模块子函数*/
  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 != '\0')
  {
          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/2;//测速转速,单位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;}      //启动输出   反转
        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是由软件预设的
//TMOD=0x00;//....
//RCAP2H=0x3C;
//RCAP2L=0xB0;
TL2=0x00;// TL2 = (65536-50000) % 256;  //或TL2=0xb0
TH2=0x4C;// TH2 = (65536-50000) / 256;  //或TH2=0x3c
//EA=1;//......
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显示*/
LCD_Init();//LCD初始化
LCD_Display(0,dispBuff0);
LCD_Display(1,dispBuff1);
Delay20ms();
while(1)
     {key();//按键识别
      Delay20ms()           ;
     }
}


注明: L298驱动     LCD1602显示设定速度和反馈速度,用的是光电码盘测速

太好了                                                                                             

请问下大神,光电码盘接在51哪个引脚上了吖?

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top