bhdsd 发表于 2010-11-28 01:08:28

mega16 电机闭环控制程序

本人新手,用的AVR mega16 想写一个简单的函数。让电机能够在速度为0的时候抱死,即用手拧不动。就用了比例一项。
我用的是输入捕获来计算脉冲的数目,同时在输入捕获的中断里面判断AB相的方向,如果正向就脉冲数加1 ,反之减1。现在有点不理解的就是,是否我可以用定时器每隔5个微秒读一下脉冲数和输入捕获里面的定时值。但是这样的话,定时值感觉就没有太大的意义了。不知道应该怎么处理呢?我程序里面写的是脉冲值一个变量,另外一个变量是无论脉冲正反,他都记一次数。记了10次,他就算一下脉冲值(有方向那个)和输入捕获的时间值。但是这样,如果速度为0的话,很有可能那个脉冲值达不到10.想问下有什么好办法吗?


下面是程序://定时器2PWM输出端口在OC2引脚,PC2为码盘B相输入

//电机减速后事150转码盘线数是400
#include<iom16v.h>
#include<macros.h>
#define uint unsigned int
#define uchar unsigned char

/**********************************************
电机速度和转向宏定义
***********************************************/
//定义PC0口为电机正反控制端1
#define CRT1_POWERON PORTC|=BIT(0)   
#define CRT1_POWEROFF PORTC&=~BIT(0)

//定义PC1口为电机正反控制端2
#define CRT2_POWERONPORTC|=BIT(1)   
#define CRT2_POWEROFF PORTC&=~BIT(1)

//限制最高转速若要求限定最高转速,改变这个值即可
#define speed_MAX       255

//限制最高转速若要求限定最高转速,改变这个值即可
#define speed_MIN       0


/**********************************************
码盘宏定义
码盘M/T法测速:
测速时间内码盘输出的脉冲数m1,又检测同一时间间隔内高频时钟脉冲数m2。
设高频时钟脉冲频率为f0 则测速时间 T=M2/f0;
习惯上转速常以每分钟转数来表示,则电机的转速可表示为:
n=60M1 *f0/(z*M2)r/min
式中,z为电机每转一圈所产生的脉冲数(z=倍频系数×码盘光栅数)
***********************************************/
#defineCRYSTAL80000000//晶振
#defineN   7000       //定义码盘线数
#defineBEI 1          //倍频数
#defineM1100         //定义每次需测量的码盘输出的脉冲个数
#definef0CRYSTAL/256//定义时钟
#definefeedback    60*time*f0/(N*clknum)   //定义码盘反馈的转速
#definesetPWMOCR2      //PWM   
//#definedirection   PC2    //定义码盘B相,码盘A相接ICP1如果B相为1,则为正,为0 ,则为负

/**********************************************
PID参数宏定义
***********************************************/
#define P100
#define I10
#define D20



float setspeed;   //设定转速
uchar direction; //定义方向,1为正,0为反      
uint olddata =0;//输入捕获ICP1的旧读数
uint newdata=0;//输入捕获ICP1的读数
uint clknum=0;
int adjPWM;   //PID调整后的PWM
int tt;   //码盘上升沿次数
float time;//码盘上升沿次数 ,分了方向的
float adjspeed;



//1ms延时,准确性较高
void DelayMs(unsigned int i)               
   {
    while(i--)
    {                           
    unsigned int j;               
      for(j=1;j<=613;j++) ;
               
    }                        
    }
   
   /*-----------------------------------------------------------------
函数名称: void time2_int(void)
函数功能: 定时器2初始化函数
参    数: 无
返 回 值: 无
-----------------------------------------------------------------*/
void time2_int(void)
{
    TCNT2=0X01;
    OCR2=100;
    TCCR2=(1<<WGM20)|(1<<WGM21)|(1<<COM21)|0x06;//快速PWM,256分频,升序清0,降序置1
    TIMSK=0XC0;    //定时器2溢出中断使能,比较匹配中断使能
}

void PORT_INIT()
{
   DDRA|=0XFF;
PORTA|=0;
DDRB|=0XFF;
PORTB|=0;
DDRC|=0XFF&(~(1<<2));
// PORTC|=BIT(0)|BIT(1);
DDRD|=0XFF;
// PORTD|=BIT(7);
}


/*正方向*/
void zheng()
{
   CRT1_POWERON;
CRT2_POWEROFF;
}

/*反方向*/
void fan()
{
   CRT1_POWEROFF;
CRT2_POWERON;
}

/*停止*/
void stop()
{
   CRT1_POWEROFF;
   CRT2_POWEROFF;
}

/*正转*/
void forward (float setspeed)
{
   if(speed!=0)
{
setPWM=(setspeed*255)/150;//setspeed是想要的转速,这里把它转化成PWM的数字(0~255)

}

zheng();

}

/*反转*/

void reverse (uchar setspeed)
{
   if(speed!=0)
{
   setPWM=(setspeed*255)/150;   //setspeed是想要的转速,这里把它转化成PWM的数字(0~255)
}
fan();

}





/*PID反馈调速*/
void speedadjust()
{
   adjspeed=P*(setspeed-feedback);
if(feedback>setspeed)
{
setspeed=adjPWM;
zheng();
// PORTC ^= (1 << PC0);
//PORTC ^= (1 << PC1);
}
else
{
setspeed=adjPWM;
fan();
   //PORTC ^= (1 << PC0);
   //PORTC ^= (1 << PC1);
}
forward(setspeed);
}


/*
输入捕获初始化
*/
void Timer1_Init(void)
{
    TCCR1A = 0x00;
                                                                     // Input Capture on Falling Edge
    TCCR1B = (1<<ICNC1)|(0<<ICES1)|(0<<CS12)|(1<<CS11)|(1<<CS10);   //噪声抑制 ,低电平进行捕捉 , 64预分频
                                                                   // Timer(s)/Counter(s) Interrupt(s) initialization
    TIMSK = (1<<TICIE1);                                          //定时器1 输入捕捉使能
}

//定时器1 输入捕获中断
#pragma interrupt_handler timer1_capt_isr:6
void timer1_capt_isr(void)

{   
if( PINC & (1 << 3) )   //读取PC2位,即B相
{time++;}
else
{
time--;
}
tt++;


if(tt==M1)// 100次读一下
{
tt=0;
time=0;
newdata=ICR1;
clknum=newdata-olddata;
olddata=newdata;
}
}


/*读取内部时钟的计数值
void readclk()
{

tt=0;
time=0;
newdata=ICR1;
clknum=newdata-olddata;
olddata=newdata;

}*/



void main()
{

Timer1_Init();
          _SEI();//全局中断使能

PORT_INIT();
time2_int();

while(1)
{
forward(0);
speedadjust();

DelayMs(50);
}


}

eworker 发表于 2010-11-28 11:17:22

http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=4419728&bbs_page_no=1&search_mode=4&search_text=eworker&bbs_id=9999

eefans 发表于 2010-11-28 11:55:27

avr做电机控制不方便,没有QEP单元,也不好处理码盘jitter的干扰。另外单纯位置闭环是不能做到你的功能的,必须要电流环进行力矩调节才行的。

bhdsd 发表于 2010-11-28 12:37:50

回复【1楼】eworker
-----------------------------------------------------------------------

哦,我只是想先试试看P的能不能成功,至少有一点效果的吧。然后再加I 进去。因为现在在参加学校比赛,而且马上要考试了。不然就考虑用STM32来做。
页: [1]
查看完整版本: mega16 电机闭环控制程序