找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 6837|回复: 10
收起左侧

电动车控制

[复制链接]
ID:3271 发表于 2009-7-11 11:47 | 显示全部楼层 |阅读模式
 

#include    <pic.h>
//改编PIC控制

//
电动车双闭环程序,采用双闭环方式控制电机,以得到最好的zh转速性能,并且可以

//限制电机的最大电流。本应用程序用到两个CCP部件,其中CCP1用于PWM输出,以控

//制电机电压;CCP2用于触发AD,定时器TMR2TMR1INT中断,RB口电平变化中断,

//看门狗以及6个通用I/O

#define AND  0xe0       //状态采集567   

#define CURA 0X0a       //电流环比例和积分系数之和

#define CURB 0X09       //电流环比例系数

#define THL  0X6400     //电流环最大输出

#define FULLDUTY 0X0FF  //占空比为1时的高电平时间

#define SPEA 0X1d       //转速环比例和积分系数之和

#define SPEB 0X1c       //转速环比例系数

#define GCURHILO 0X0330 //转速环最大输出

#define GCURH 0X33      //最大给定电流

#define GSPEH 0X67      //最大转速给定

#define TSON 0X38       //手柄开启电压1.1 VTSON*2为刹车后手柄开启电压,即

                         //2.2 V

#define VOLON 0X4c      //低电压保护重开电压3.0 V33 V

#define VOLOFF 0X49     //低电压保护关断电压2.86 V31.5 V

volatile unsigned char DELAYH,DELAYL,oldstate,speed,

    speedcount,tsh,count_ts,count_vol,gcur,currenth,

    voltage;                    //寄存器定义

static bit sp1,spe,ts,volflag,spepid,lowpower,

    off,shutdown,curpid;        //标志位定义

static volatile unsigned char new[10]={0xaf,0xbe,0xff,0x7e,0xcf,

    0xff,0xd7,0x77,0xff,0xff};  //状态寄存器表

//------------PIC16F877初始化子程序------------

void INIT877()

{  

    PORTC=0X0FF;            //关断所有MOSFET

    TRISC=0X02;         //设置C口输出

    PIE1=0X00;          //中断寄存器初始化,关断所有中断

    TRISA=0XCF;         //设置RA4,RA5 输出

    TRISB=0XEF;         //RB 口高三位输入,采集电机三相的霍尔信号

    PORTC=new[(PORTB&AND)>>5];  //采集第一次霍尔信号,并输出相应的信号,导通

//两个MOS

    T2CON=0X01;         //TMR2 4分频

    CCPR1L=0X0FF;       //初始时PWM输出全高

    CCP1CON=0X0FF;      //CCP1设置为PWM方式

    CCP2CON=0X0B;       //CCP2设置为特殊方式,以触发AD

    ADCON0=0X81;            //AD时钟为32分频,AD使能,选择AN0通道采集手

//柄电压   

    TMR2=0X00;          //TMR2寄存器初始化

    TMR1H=0X00;         //TMR1寄存器初始化

    TMR1L=0X00;

    T1CON=0X00;         //TMR11分频

    CCPR2H=0X08;

    CCPR2L=0X00;            //电流采样周期设置为TAD=512 μs

    PR2=0XC7;           //PWM频率设置为5 kHz

    ADCON1=0X02;            //AD结果左移

    OPTION=0XFB;            //INT上升沿触发

    TMR2ON=1;           //PWM开始工作

    INTCON=0XD8;            //中断设置GIE=1,PEIE=1,RBIE=1

    ADIE=1;             //AD中断使能

    speedcount=0x00;        //转速计数寄存器

    speed=0x7f;         //转速保持寄存器

    spe=1;              //低速标志位

    sp1=1;              //低速标志位

    oldstate=0x0ff;     //初始状态设置,区别于其他状态

    count_ts=0x08;      //电流采样8,采集1次手柄

    count_vol=0x00;     //采样256次手柄,采集1次电池电压

    ts=1;               //可以采集手柄值的标志位

    ADGO=1;             //AD采样使能

    TMR1ON=1;           //CCP2部件开始工作

}

//------------延时子程序---------------

#pragma interrupt_level 1

void DELAY1(x)

char x;

{

    DELAYH=x;           //延时参数设置

#asm

DELAY2  MOVLW   0X06

    MOVWF   _DELAYL

DELAY1  DECFSZ  _DELAYL

    GOTO    DELAY1

    DECFSZ  _DELAYH

    GOTO    DELAY2

#endasm

}

//-----------状态采集子程序----------------------

void sample()

{

    char state1,state2,state3,x;

    do  {

        x=1;

        state1=(PORTB&AND);     //霍尔信号采集

        DELAY1(x);

        state2=(PORTB&AND);

    }while(state1-state2);      //当三次采样结果不相同时继续采集状态

    if(state1-oldstate!=0)      //看本次采样结果是否与上次相同,不同

//则执行

     {oldstate=state1;          //将本次状态设置为旧状态

      state1=(oldstate>>5);

      PORTC=new[state1];            //C口输出相应的信号触发两个MOS

      if(sp1==1){spe=1;sp1=0;}

      else  {                   //如果转速很低,则spe1

spe=0;sp1=0;

        speedcount<<=1;

        state3=(TMR1H>>2);      //否则,spe=0,计转速

        speed=speedcount+state3;    //speed寄存器为每256 μs1

      }

      speedcount=0;

    }

}

回复

使用道具 举报

ID:3271 发表于 2009-7-11 11:48 | 显示全部楼层

//-----------------AD采样子程序----------------------

void AD()

{

    char x;

    ADIF=0;                 //AD中断标志位

    if(ts==1){              //如果为手柄采样,则采样手柄值

     CHS0=1;                    //选择电流采样通道

     count_vol=count_vol+1; //电池采样计数寄存器

     spepid=1;              //置转速闭环运算标志

     ts=0;tsh=ADRESH;       //存手柄值

     if(count_vol==0) { //如果电池采样时间到,则选择AN2通道,采集电池电压

        CHS0=0;CHS1=1;volflag=1;x=1;DELAY1(x);ADGO=1;

      }

    }

    else if(volflag==1) {           //电池采样完毕,进行相应的处理

       CHS1=0;CHS0=1;volflag=0;voltage=ADRESH;lowpower=1;

    }

    else    {                       //否则,中断为采样电流中断

      speedcount=speedcount+1;  //speedcount寄存器加1,作为测量转速用

      if(speedcount>0x3d)  sp1=1;   //如果转速低于1 000 000 μs/(512 μs*3eh*3)   

                                  // 则认为为低速状态

      currenth=ADRESH;

      curpid=1;

      count_ts=count_ts-1;

      if(count_ts==0) {         //如果手柄时间到,则转入手柄采样通道

          CHS0=0;count_ts=0x08;ts=1;x=1;DELAY1(x);ADGO=1;

        }

     }

}

//-------------刹车处理子程序------------------

void BREAKON()

{

    char x;

    off=0;                  //off清零,如果是干扰则不复位

    shutdown=0;

    if(RB0==1)  {           //如果刹车信号为真,则停止输出电压

    ADIE=0;             //AD中断

        INTE=0;             //关刹车中断

        CCPR1L=FULLDUTY;        //输出电压0

        TMR1ON=0;           //CCP2,不再触发AD

        for(;ADGO==1;)  continue;//如正在采样,则等待采样结束

        ADIF=0;             //ADIF位清零

        CHS0=0;             //选择通道0采样手柄

        CHS1=0;

        x=1;

DELAY1(x);

        do  {

            ADGO=1;

            for(;ADIF==0;)continue;

            ADIF=0;

            CCPR1L=FULLDUTY;

        asm("CLRWDT");

            tsh=(ADRESH>>1);

        }while(tsh>TSON||RB0==1);   //当手柄值大于2.2 V或刹车仍旧继续时,执行以

                                 //上语句

        off=1;                  //置复位标志

  }

}

回复

使用道具 举报

ID:3271 发表于 2009-7-11 11:49 | 显示全部楼层

//---------欠保护子程序-------------------

void POWER()

{

    char x;

    lowpower=0;

    voltage>>=1;                    //电压值换为7位,以利于单字节运算

    if(voltage<VOLOFF) {            //电池电压小于3*k(V)时保护

    ADIE=0;

        INTE=0;

        TMR1ON=0;

        CCPR1L=FULLDUTY;

        for(;ADGO==1;)continue;

        ADIF=0;

        CHS0=0;CHS1=1;

        x=1;

DELAY1(x);

        do{ADGO=1;

            for(;ADIF==0;)continue;

            ADIF=0;

            voltage=(ADRESH>>1);

            CCPR1L=FULLDUTY;

        asm("CLRWDT");

        }while(voltage<VOLON);  //电池电压小于35 V时继续保护

        off=1;                  //置复位标志

    }

}

//------------电流环运算子程序-----------------

void CURPI()

{   static int curep=0x00,curek=0x00,curuk=0x00;

    union data{int pwm;

        char a[2];}b;           //定义电流环运算寄存器

    curpid=0;                   //清电流运算标志

    curep=curek*CURB;           //计算上一次偏差与比例系数的积

    if(currenth<2)currenth=2;       //如果采样电流为零,则认为有一个小电流以利于

//使转速下降

    currenth>>=1;

    curek=gcur-currenth;            //计算本次偏差

    curuk=curuk+curek*CURA-curep;       //按闭环PI运算方式得到本次输出结果,下

//面对结果进行处理

    if(curuk<0x00) {                //如果输出小于零,则认为输出为零

      curuk=0;CCPR1L=FULLDUTY;CCP1X=0;CCP1Y=0; 

      }

else if(curuk-THL>=0)   {   //如果输出大于限幅值,则输出最大电压

            curuk=THL;CCPR1L=0;CCP1X=0;CCP1Y=0;

        }

else    {               //否则,按比例输出相应的高电平时间到CCPR1寄存器

            b.pwm=THL-curuk;

            b.pwm<<=1;

            CCPR1L=b.a[1]; //CCPR1L=(b.pwm>>8)&0x0ff;PWM寄存器的高半字节

            if(b.pwm&0x80!=0) CCP1X=1;

            else CCP1X=0;

            if(b.pwm&0x40!=0) CCP1Y=1;

            else CCP1Y=0;

    }

}

回复

使用道具 举报

ID:3271 发表于 2009-7-11 11:49 | 显示全部楼层

//---------------转速环运算子程序-----------------------

void SPEPI()

{   static int speep=0x00,speek=0x00,speuk=0x00;

    int tsh1,speed1;                //转速寄存器定义

    spepid=0;                   //清转速运算标志       

    if(spe==1)  speed1=0x00;        //若转速太低,则认为转速为零

    else speed1=0x7f-speed;     //否则计算实际转速

    if(speed1<0) speed1=0;

    speep=speek*SPEB;

    tsh1=tsh-0x38;              //得到计算用的手柄值

    speek=tsh1-speed1;

    if(tsh1<0) {speuk=0;gcur=0;}    //当手柄值低于1.1 V时,则认为手柄给定为零

    else {                      //否则,计算相应的转速环输出

        if(tsh1>=GSPEH)         //限制最大转速

        tsh1=GSPEH;

    speuk=speuk+speek*SPEA-speep;   //计算得转速环输出

        if(speuk<=0X00) {speuk=0x00;gcur=0x00;}//转速环输出处理

        else if(speuk>GCURHILO) {   //转速环输出限制,即限制最大电流约12 A

            speuk=GCURHILO;gcur=GCURH;}

        else        {                   //调速状态时的输出

            gcur=(speuk>>4)&0x0ff;

        }

    }

}

//-----------主程序-------------------------

void main()

{

for(;;){                    //比while(1) 优良的语句

        INIT877();              //单片机复位后,先对其进行初始化

        off=0;                  //清复位标志

        for(;off==0;)   {       //复位标志为零,则执行下面程序,否则复位

        if(curpid==1) CURPI();  //电流PI运算

        else if(spepid==1)  SPEPI();    //转速PI运算

        else if(lowpower==1)    POWER();

        else if(shutdown==1)    BREAKON();

        asm("CLRWDT");

    }

  }

}

//---------中断服务子程序---------------------

#pragma interrupt_level 1

void interrupt INTS(void)

{  

    if(RBIF==1) {RBIF=0;sample();}

    else if(ADIF==1)    AD();

    else if(INTF==1)    {shutdown=1;INTF=0;}    //刹车中断来,置刹车标志

}

初学者看看还可以,把他搞成51的就可以了

回复

使用道具 举报

ID:13088 发表于 2009-7-11 11:51 | 显示全部楼层
学习下   保存下来慢慢研究   
回复

使用道具 举报

ID:14095 发表于 2009-7-11 15:25 | 显示全部楼层
C语言看不 懂
回复

使用道具 举报

ID:14957 发表于 2009-7-11 16:11 | 显示全部楼层

啊啊啊...论坛里好多C语言....看不懂

回复

使用道具 举报

ID:15281 发表于 2009-7-14 10:49 | 显示全部楼层
hao
回复

使用道具 举报

ID:3271 发表于 2009-7-15 10:32 | 显示全部楼层

如果要做大一点的程序关靠汇编是不行的,我一般做大型的都会C嵌入汇编,让汇编做底层驱动,因为C达不到汇编那么精准

回复

使用道具 举报

ID:15225 发表于 2009-7-19 20:39 | 显示全部楼层

开始学C
回复

使用道具 举报

ID:84745 发表于 2015-10-9 23:05 来自手机 | 显示全部楼层
看看好用不
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表