chernic 发表于 2011-8-7 15:04:04

AVR单片机PID控制电机程序2011-3-30

转一个 AVR单片机PID控制电机程序
AVR单片机PID控制电机程序2011-3-30
2011-04-10 2053

ProjectPID_motor
Version第一版
Date   2011-3-30
Author                              
Company                           
Comments
Chip type            ATmega16L
Program type         Application
Clock frequency      4.000000 MHz
Memory model         Small
External SRAM size   0
Data Stack size      256

#include mega16.h
#include delay.h
#include fyd12864.h
#include string.h

sfrw ICR1=0x26;                                          补充定义16位寄存器ICR1地址为0x26(mega16.h中未定义)                                 
unsigned int icp_v1;
unsigned int icp_v2;
unsigned char icp_n;
unsigned char max_icp;                              量程定义字
bit icp_ok;
bit time_4ms_ok;
bit freq_ok;
bit begin_m;                                             定时器1溢出,重新测量标志字
bit full_ok;                                                定时器1溢出
long fv;

struct PID
{
    unsigned int SetPoint;                        设定目标 Desired Value
    unsigned int Proportion;                     比例常数 Proportional Const
    unsigned int Integral;                           积分常数 Integral Const
    unsigned int Derivative;                     微分常数 Derivative Const
    unsigned int LastError;                        Error[-1]
    unsigned int PrevError;                     Error[-2]
    unsigned int SumError;                     Sums of Errors
};
struct PID spid;                                       PID Control Structure

long rout;                                             PID Response (Output)
long rin;                                                 PID Feedback (Input)

void PID_Init(struct PID pp)
{
    memset ( pp,0,sizeof(struct PID));   全部初始化为0
}
unsigned int PID_Calc( struct PID pp, unsigned int NextPoint )
{
    unsigned int dError,Error;
    Error = pp-SetPoint - NextPoint;                   偏差   
    pp-SumError += Error;                                 积分   
    dError = pp-LastError - pp-PrevError;       当前微分   
    pp-PrevError = pp-LastError;   
    pp-LastError = Error;
    return (pp-ProportionError                      比例项   
    + pp-Integralpp-SumError                     积分项
    + pp-DerivativedError);                           微分项
}   

Timer 2 比较匹配中断服务,4ms定时
interrupt void timer2_comp_isr(void)   
{
#asm(sei)                           开放全局中断,允许中断嵌套   
    time_4ms_ok = 1;
}

Timer 1 溢出中断服务
interrupt void timer1_ovf_isr(void)
{
    full_ok = 1;
}

Timer 1 输入捕捉中断服务
interrupt void timer1_capt_isr(void)
{
    if (icp_n = max_icp)    第N个上升沿到
    {
      icp_v2 = ICR1;       记录第N个上升沿时间
      TIMSK = 0x80;      禁止TCI输入捕捉和溢出中断
      icp_ok = 1;
    }
    else if (icp_n == 0)
    {
      icp_v1 = ICR1;       记录第1个上升沿时间
    }   
    icp_n++;   
}

void main(void)
{
    unsigned int icp_1,icp_2;   
    PORTB=0x00;
    DDRB=0x08;
    PORTD=0x40;            PD6(icp)输入方式,上拉有效
   
   TC0 定时初始化
    TCCR0=0x79;            Mode Fast PWM top=FFh   
    TCNT0=0x00;            OC0 output Non-Inverted PWM
    OCR0=0xff;
   TC1 计数初始化
    TCCR1B = 0x41;         TC1正常计数方式,上升沿触发输入捕捉,4M1计数时钟
    TIMSK = 0xA4;            允许TC2比较匹配中断,允许TC1输入捕捉、溢出中断

   TC2 定时初始化
    TCCR2=0x0C;            内部时钟,64分频(4M64=62.5KHz),CTC模式
    OCR2=0xff;                  OCR2 = 0xf9(249),(249+1)62.5=4ms

    icp_n = 0;
    max_icp =1;                                 定义上升沿的位序
    #asm(sei)                                  开放全局中断
    INIT_FYD();
    Show_Text(3,0,chinese);         (列,行,显示字)
    PID_Init(&spid);                        Initialize Structure
    spid.SetPoint = 168;                  设定目标 Desired Value
    spid.Proportion = 6;                  比例常数 Proportional Const
    spid.Integral =5;                        积分常数 Integral Const
    spid.Derivative =2;                   微分常数 Derivative Const
    FM_Num(0,1,spid.Proportion);      显示比例
    FM_Num(3,1,spid.Integral);         显示积分
    FM_Num(6,1,spid.Derivative);       显示微分
    FM_Num(0,2,spid.SetPoint);          显示目标
    while (1)
{
   if (icp_ok == 1)                                                完成一次测量
   {
         if (icp_v2 = icp_v1)                                    计算N个上升沿的时钟脉冲个数,
             icp_2 = icp_v2 - icp_v1;                         两次连续的ICR1的差值
         else                                          
             icp_2 = 65536 - icp_v1 + icp_v2;          定时器1溢出,加上65535         
         if (!(icp_v2 = icp_v1 && full_ok))            有溢出,数据无效   
            {         
                if (icp_2 == icp_1)                              两次个数相等,测量有效
             {
                  fv = 1000000(long)max_icpicp_2;      换算成频率值
                  freq_ok = 1;                                    频率换算完成
               
                  if (fv4000)
                  {
                        max_icp = 20;                              如果频率大于4Khz,N=64
                  }
                  else
                  {
                        max_icp = 1;                                  N=1
                  }
                }
         }
         else
         max_icp = 1;                                             有溢出,N=1         
         icp_1 = icp_2;         
         icp_ok = 0;
            begin_m = 1;
       }      
   if (time_4ms_ok)                                          定时器2,定时到         
   {                           
         if(freq_ok)                                                判断频率是否换算完成
         {
            rin=fv;
            FM_Num(0,3,rin);
            rout = PID_Calc ( &spid,rin );
            rout=rout100;
            FM_Num(3,3,rout);            
            if(rout100&&rout256)
            OCR0=rout;                     
            freq_ok = 0;
         }
         else if(begin_m)         
         {
                icp_n = 0;       开始新的一次测量,
                full_ok = 0;   清除溢出标志
             TIFR = 0x24;   清除可能存在的输入捕捉、溢出中断标志位
             TIMSK = 0xa4;开启TC1输入捕捉、溢出中断允许      
             begin_m = 0;
         }
            time_4ms_ok = 0;   
   }
    }
}

chernic 发表于 2011-8-7 16:02:15

上面的版本有格式上得错误
/*****************************************************
Project : PID_motor
Version : 第一版
Date    : 2011-3-30
Author:                           
Company :                           
Comments:
Chip type         : ATmega16L
Program type      : Application
Clock frequency   : 4.000000 MHz
Memory model      : Small
External SRAM size: 0
Data Stack size   : 256
*****************************************************/
#include <mega16.h>
#include <delay.h>
#include <fyd12864.h>
#include <string.h>

sfrw ICR1=0x26;                                           // 补充定义16位寄存器ICR1地址为0x26(mega16.h中未定义)                                 
unsigned int icp_v1;
unsigned int icp_v2;
unsigned char icp_n;
unsigned char max_icp;                              //量程定义字
bit icp_ok;
bit time_4ms_ok;
bit freq_ok;
bit begin_m;                                             //定时器1溢出,重新测量标志字
bit full_ok;                                                //定时器1溢出
long fv;

struct PID
{
    unsigned int SetPoint;                         // 设定目标 Desired Value
    unsigned int Proportion;                      // 比例常数 Proportional Const
    unsigned int Integral;                        // 积分常数 Integral Const
    unsigned int Derivative;                      // 微分常数 Derivative Const
    unsigned int LastError;                     // Error[-1]
    unsigned int PrevError;                      // Error[-2]
    unsigned int SumError;                      // Sums of Errors
};
struct PID spid;                                    // PID Control Structure

long rout;                                              // PID Response (Output)
long rin;                                                // PID Feedback (Input)

void PID_Init(struct PID *pp)
{
    memset ( pp,0,sizeof(struct PID));   //全部初始化为0
}
unsigned int PID_Calc( struct PID *pp, unsigned int NextPoint )
{
    unsigned int dError,Error;
    Error = pp->SetPoint - NextPoint;                  // 偏差   
    pp->SumError += Error;                              // 积分   
    dError = pp->LastError - pp->PrevError;      // 当前微分   
    pp->PrevError = pp->LastError;   
    pp->LastError = Error;
    return (pp->Proportion * Error                     // 比例项   
    + pp->Integral * pp->SumError                  // 积分项
    + pp->Derivative * dError);                        // 微分项
}   

// Timer 2 比较匹配中断服务,4ms定时
interrupt void timer2_comp_isr(void)   
{
#asm("sei")                        // 开放全局中断,允许中断嵌套   
    time_4ms_ok = 1;
}

// Timer 1 溢出中断服务
interrupt void timer1_ovf_isr(void)
{
    full_ok = 1;
}

// Timer 1 输入捕捉中断服务
interrupt void timer1_capt_isr(void)
{
    if (icp_n >= max_icp)   // 第N个上升沿到
    {
      icp_v2 = ICR1;      // 记录第N个上升沿时间
      TIMSK = 0x80;       // 禁止T/CI输入捕捉和溢出中断
      icp_ok = 1;
    }
    else if (icp_n == 0)
    {
      icp_v1 = ICR1;      // 记录第1个上升沿时间
    }   
    icp_n++;   
}

void main(void)
{
    unsigned int icp_1,icp_2;   
    PORTB=0x00;
    DDRB=0x08;
    PORTD=0x40;             // PD6(icp)输入方式,上拉有效
   
    // T/C0 定时初始化
    TCCR0=0x79;             // Mode: Fast PWM top=FFh   
    TCNT0=0x00;             // OC0 output: Non-Inverted PWM
    OCR0=0xff;
    // T/C1 计数初始化
    TCCR1B = 0x41;          // T/C1正常计数方式,上升沿触发输入捕捉,4M/1计数时钟
    TIMSK = 0xA4;             // 允许T/C2比较匹配中断,允许T/C1输入捕捉、溢出中断

    // T/C2 定时初始化
    TCCR2=0x0C;             // 内部时钟,64分频(4M/64=62.5KHz),CTC模式
    OCR2=0xff;               // OCR2 = 0xf9(249),(249+1)/62.5=4ms

    icp_n = 0;
    max_icp =1;                                 //定义上升沿的位序
    #asm("sei")                                 // 开放全局中断
    INIT_FYD();
    Show_Text(3,0,chinese);         //(列,行,显示字)
    PID_Init(&spid);                         // Initialize Structure
    spid.SetPoint = 168;                  //设定目标 Desired Value
    spid.Proportion = 6;                  //比例常数 Proportional Const
    spid.Integral =5;                        //积分常数 Integral Const
    spid.Derivative =2;                   //微分常数 Derivative Const
    FM_Num(0,1,spid.Proportion);      //显示比例
    FM_Num(3,1,spid.Integral);         //显示积分
    FM_Num(6,1,spid.Derivative);       //显示微分
    FM_Num(0,2,spid.SetPoint);          //显示目标
    while (1)
{
   if (icp_ok == 1)                                             // 完成一次测量
   {
         if (icp_v2 >= icp_v1)                                 // 计算N个上升沿的时钟脉冲个数,
             icp_2 = icp_v2 - icp_v1;                         //两次连续的ICR1的差值
         else                                          
             icp_2 = 65536 - icp_v1 + icp_v2;          //定时器1溢出,加上65535         
         if (!(icp_v2 >= icp_v1 && full_ok))             // 有溢出,数据无效   
            {         
                //if (icp_2 == icp_1)                           // 两次个数相等,测量有效
             {
                  fv = 1000000 * (long)max_icp / icp_2;       // 换算成频率值
                  freq_ok = 1;                                    //频率换算完成
               
                  if (fv > 4000)
                  {
                        max_icp = 20;                           // 如果频率大于4Khz,N=64
                  }
                  else
                  {
                        max_icp = 1;                                 // N=1
                  }
                }
         }
         else
         max_icp = 1;                                              // 有溢出,N=1         
         icp_1 = icp_2;         
         icp_ok = 0;
            begin_m = 1;
       }      
   if (time_4ms_ok)                                          //定时器2,定时到         
   {                           
         if(freq_ok)                                                //判断频率是否换算完成
         {
            rin=fv;
            FM_Num(0,3,rin);
            rout = PID_Calc ( &spid,rin );
            rout=rout/100;
            FM_Num(3,3,rout);            
            if(rout>100&&rout<256)
            OCR0=rout;                     
            freq_ok = 0;
         }
         else if(begin_m)         
         {
                icp_n = 0;      // 开始新的一次测量,
                full_ok = 0;    // 清除溢出标志
             TIFR = 0x24;    // 清除可能存在的输入捕捉、溢出中断标志位
             TIMSK = 0xa4; // 开启T/C1输入捕捉、溢出中断允许      
             begin_m = 0;
         }
            time_4ms_ok = 0;   
   }
    }
}

cdwess 发表于 2011-10-27 18:50:11

谢谢,正需要这个

luhuaneda 发表于 2011-10-27 19:50:39

mark

zhanghanlai 发表于 2011-10-27 20:54:56

发程序不如发思路。但还是谢谢

1181zjf 发表于 2012-1-9 15:25:37

好像缺一个定时器0的中断函数!

lxdsmall 发表于 2012-1-9 20:20:03

又是PID,学学。

309794325 发表于 2012-1-9 22:19:53

mark

tangwubing1988 发表于 2012-1-16 11:49:31

mark

dingliming 发表于 2012-1-16 17:03:22

mark

zzh241 发表于 2012-2-9 16:35:22

mark

sleet1986 发表于 2012-2-9 16:44:13

以备不时之需

liyang0727 发表于 2012-2-9 19:48:26

PID电机实例

cuikai12345 发表于 2012-2-9 21:18:55

mark

senbaby2 发表于 2012-2-13 18:44:04

好的,Mark

taiwentaotwt 发表于 2012-2-15 17:21:36

正在学习

xslff 发表于 2012-2-15 17:38:41

顶起来!

gyzzg2030 发表于 2012-2-15 18:56:38

学习,不过说一下思路,最好了

tangwubing1988 发表于 2012-2-15 19:00:42

Mark

sleet1986 发表于 2012-2-16 08:42:57

留个脚印。回头来看

yanxuerui12345 发表于 2012-2-29 00:25:04

mark

dingliming 发表于 2012-2-29 10:30:36

MARK

shin555 发表于 2012-3-2 18:27:03

MARK

luhuaneda 发表于 2012-3-3 01:45:45

MARK

dayday 发表于 2012-3-6 19:43:18

想学习一下PID,不清楚是交流还是直流电机

371278638 发表于 2012-4-11 23:36:24

谢谢楼主正在学这个.....

dafeng123 发表于 2012-4-12 07:20:23

谢谢楼主,总有用到的时候!

dory_m 发表于 2012-4-12 08:23:56

标记:学习!!!

zdhxzy1981 发表于 2012-4-12 08:39:40

呵呵,整理成附件的格式好一些吧。MARK!
页: [1]
查看完整版本: AVR单片机PID控制电机程序2011-3-30