电机控制DSP编程
DSP无刷直流电动机驱动控制程序

DSP无刷直流电动机驱动控制程序————————————————————————————————作者:————————————————————————————————日期:2.4 无刷直流电动机驱动控制程序//########################################################################## ###/////无刷电机控制源程序//TMS320F2812////########################################################################## ###//=====================================================================//头文件调用//=====================================================================#include "DSP28_Device.h"#include "math.h"#include "float.h"//=====================================================================//常量附值//=====================================================================#define Idc_max 3000 //电流给定最大值#define Idc_min 0 //电流给定最小值//=====================================================================//标志位//=====================================================================char Iab_Data=0;struct Flag_Bits { // bits descriptionUint16 Send:1; // 0 串口发数Uint16 Test:1; // 1 串口测试Uint16 Vflag:1; // 2 转速更新Uint16 SendEnd:1; // 3 串口发送结束Uint16 Sign1:1; // 4 上一次给定转向标志Uint16 Sign2:1; // 5 本次给定转向标志Uint16 Openint:1; // 6 启动标志Uint16 Adfrist:1; // 7 保留Uint16 Spdoff:1; // 8 保留Uint16 Zero:1; // 9 转速过零标志Uint16 Mode:3; // 10-12 保留Uint16 Dshow:1; // 13 保留Uint16 Sign:1; // 14 当前转向标志};union Flag_Reg {Uint16 all;struct Flag_Bits bit;}FlagRegs;//===================================================================== //全局变量//===================================================================== //串口通信变量unsigned int Sci_Rx[8]={0,0,0,0,0,0,0,0}; //接收数据数组unsigned int Sci_Tx[8]={0,0,0,0,0,0,0,0}; //发送数据数组char T_pointer=0; //发送数据数组指针char R_pointer=0; //接收数据数组指针char T_length=1; //发送数据长度char R_length=0; //接收数据长度char a2=0;//控制参数unsigned int spd_kp=0; //转速环P参数unsigned int spd_ki=0; //转速环I参数unsigned int id_kp=0,id_ki=0; //电流PI参数signed int spd_given=0; //转速给定signed int spd_given1=0; //转速给定signed int spd_given2=0; //转速给定//控制变量unsigned int cap1=0; //前次换向标志位unsigned int cap2=0; //本次换向标志位unsigned int intruptcount=0; //定时器1下溢中断次数unsigned int speed_given = 1000; //转速给定signed int Idc_given2=0; //算得本次电流给定值signed int speed_e1=0; //前次转速误差signed int speed_e2=0; //本次转速误差signed int Ia_e1=0; //前次电流误差signed int Ia_e2=0; //本次电流误差char t2first=0;signed int COMP2=0; //装比较寄存器值//转速反馈char Spd_Data=0; //滤波计数int speed_counter=0; //速度环计数器unsigned int cap_a; //本次捕获单元数据unsigned int cap_b; //上次捕获单元数据long cap_c; //捕获单元数据差signed int BLDC_SPD[10]={0,0,0,0,0,0,0,0,0,0};//转速滤波signed int spd_fd_q1=0; //转速反馈临时变量unsigned int spd_fd_q0=0; //转速检测值char cc=0;//电流反馈unsigned int ia[6]={0,0,0,0,0,0}; //A相电流反馈值unsigned int ib[6]={0,0,0,0,0,0}; //B相电流反馈值signed int ia_fd=0,ib_fd=0; //电流反馈signed int Temp_filter=0; //临时变量unsigned int t1per=0; //周期寄存器数值//临时变量signed long long1_tmp=0;signed long long2_tmp=0;signed int u16_tmp1=0;signed int u16_tmp2=0;//===================================================================== //子程序声明//===================================================================== interrupt void t1uf_int(void);interrupt void cap_int(void);void Ad(void);void speed(void); //计算速度void Ia_PI(void); //电流环调节Iavoid speed_PI(void); //速度环调节void bldc(void);void Check_Rxdata(void);void Sci_Send(signed int sci_delay);char *Fen_Jie(signed int Send_Temp);unsigned int Sci_Rx_check(unsigned int i_Rx,unsigned int *p_Rx);signed int DIV_CAL(long signed int dividend,signed int divisor);unsigned int U_DIV_CAL(long unsigned int udividend,unsigned int udivisor);//=====================================================================//主程序开始//===================================================================== void main(void){//控制寄存器初始设置InitSysCtrl(); //初始化系统DINT; //关全局中断IER = 0x0000;IFR = 0x0000;InitPieCtrl(); //初始化PIE中断InitPieVectTable(); //初始化PIE中断矢量表InitGpio(); //初始化Gpio输入输出口InitEv(); //初始化Eva的T和T2InitAdc_Eva(); //初始化ADInitData();EALLOW; // This is needed to write to EALLOW protected registersPieVectTable.T1UFINT=&t1uf_int;//T1下溢中断地址PieVectTable.CAPINT4=&cap_int;PieVectTable.CAPINT5=&cap_int;PieVectTable.CAPINT6=&cap_int;EDIS; // This is needed to disable write to EALLOW protected registersPieCtrl.PIEIER2.bit.INTx6 = 1; //T1下溢中断使能PieCtrl.PIEIER5.bit.INTx5 = 1;PieCtrl.PIEIER5.bit.INTx6 = 1;PieCtrl.PIEIER5.bit.INTx7 = 1;IER |= M_INT2; // Enable CPU Interrupt 2IER |= M_INT4; // Enable CPU Interrupt 4IER |= M_INT5; // Enable CPU Interrupt 5EvbRegs.EVBIMRC.bit.CAP4INT=1;EvbRegs.EVBIMRC.bit.CAP5INT=1;EvbRegs.EVBIMRC.bit.CAP6INT=1;EvbRegs.CAPCONB.all=0x36FF;NOP;NOP;NOP;NOP;EvbRegs.CAPCONB.all=0xB6FF;EINT; //使能全局中断INTMERTM; // Enable Global realtime interrupt DBG//等待中断(中断之外的时间内进行LCD的发送和接收)for(;;){SCI_CTL();}}//===================================================================== //串口控制//===================================================================== //*************//接收数据检测//************void Check_Rxdata(){switch(Sci_Rx[0]){case 'm': //转速给定和转速在线更新 case 'v':{FlagRegs.bit.Vflag=1;if(Sci_Rx[1]=='-'){FlagRegs.bit.Sign1=0; //转速为负值spd_given1 = Sci_Rx_check(4,Sci_Rx);}else{FlagRegs.bit.Sign1=1; //转速为正值spd_given1 = Sci_Rx_check(3,Sci_Rx);}if(Sci_Rx[0]=='m'){FlagRegs.bit.Sign2=FlagRegs.bit.Sign1;FlagRegs.bit.Sign=FlagRegs.bit.Sign1;}break;}case 's': //启动{Sci_Tx[0]='a';Sci_Tx[1]='+';Sci_Tx[2]='0';Sci_Tx[3]='0';Sci_Tx[4]='0';Sci_Tx[5]='0';Sci_Tx[6]='0';Sci_Tx[7]='z';spd_given=spd_given1;Protect_Data();Motor_Start();FlagRegs.bit.Send=1;T_length=8;T_pointer=0;break;}case 't': //停止 {Motor_Stop();break;}default: break;}}//*******************// 串口接收/发送判断//******************SCI_CTL(){if((SciaTx_Ready() == 1) && (FlagRegs.bit.Send == 1))//发送数据准备好并且软件使能发送{if(FlagRegs.bit.Test==0) //SCI测通状态{SciaRegs.SCITXBUF = Sci_Rx[T_pointer];T_pointer++; //发送缓冲器数组指针+1if(T_pointer==R_length){FlagRegs.bit.Test=1;FlagRegs.bit.Send=0;T_pointer=0;}}else //SCI非测通状态{SciaRegs.SCITXBUF = Sci_Tx[T_pointer];T_pointer++; //发送缓冲器数组指针+1if(T_pointer==T_length){FlagRegs.bit.Send = 0;T_pointer=0;}if(T_pointer>=10){T_pointer=0;FlagRegs.bit.Send= 0;}}R_pointer=0;}#if !SCIA_INTif(SciaRx_Ready() == 1) //接收数据准备好{FlagRegs.bit.SendEnd=0;FlagRegs.bit.Send= 0;Sci_Rx[R_pointer] = SciaRegs.SCIRXBUF.all;R_pointer++;if(Sci_Rx[R_pointer-1]=='z'){R_length=R_pointer-1;FlagRegs.bit.Send= 1;FlagRegs.bit.SendEnd=1;if(FlagRegs.bit.Test==1){Check_Rxdata();R_pointer = 0;}}if(R_pointer== 10){R_pointer = 0;}}#endif}//****************//接收数据格式调整//****************unsigned int Sci_Rx_check(unsigned int i_Rx,unsigned int *p_Rx){unsigned long data_Rx;unsigned int *p_tmp_Rx=p_Rx+i_Rx;data_Rx=1000*(*p_tmp_Rx)+100*(*(p_tmp_Rx+1))+10*(*(p_tmp_Rx+2))+(*(p_tmp_Rx+ 3))-53328;return(data_Rx);}//****************//发送数据格式调整//****************char *Fen_Jie(signed int Send_Temp) {unsigned int Temp;char s1,s2,s3,s4;char String_Tmp[6]={0,0,0,0,0,'\0'}; if(Send_Temp< 0){String_Tmp[0]='-';Send_Temp=-Send_Temp;}else{String_Tmp[0]='+';}s1=((long)Send_Temp*2097)>>21;if(s1>=10) {s1=9;}Temp=Send_Temp-s1*1000;s2=((long)Temp*5253)>>19;if(s2>=10) {s2=9;}Temp=Temp-s2*100;s3=((long)Temp*3277)>>15;if(s3>=10) {s3=9;}s4=Temp-s3*10;if(s4>=10) {s4=9;}String_Tmp[1]=s1+48; //千位String_Tmp[2]=s2+48; //百位String_Tmp[3]=s3+48; //十位String_Tmp[4]=s4+48; //个位return(String_Tmp);}//**************// 数据发送//**************void Sci_Send(signed int sci_delay){//串口数据发送char *p_send=0;a2++;if((a2>=sci_delay)&&(FlagRegs.bit.Send==0)&&(FlagRegs.bit.SendEnd==1)) {p_send=Fen_Jie(spd_fd_q0);Sci_Tx[0]='a';Sci_Tx[1]=*p_send;Sci_Tx[2]='0';Sci_Tx[3]=*(p_send+1);Sci_Tx[4]=*(p_send+2);Sci_Tx[5]=*(p_send+3);Sci_Tx[6]=*(p_send+4);Sci_Tx[7]='z';FlagRegs.bit.Send=1;a2=0;}}//===================================================================== //电机状态控制//===================================================================== //*******************// 数据处理//******************Protect_Data(){if(spd_kp<=0)spd_kp=1; //转速环P参数else if(spd_kp>30000) spd_kp=30000;if(spd_ki<=0)spd_ki=1; //转速环P参数else if(spd_ki>30000) spd_ki=30000;if(id_kp<=0)id_kp=1; //转速环P参数else if(id_kp>30000) id_kp=30000;if(id_ki<=0)id_ki=1; //转速环P参数else if(id_ki>30000) id_ki=30000;EvaRegs.T1PR =7500; //矢量控制开关频率5K固定t1per=7500;EvaRegs.T1CON.all = 0X080C; //连续增减计数InitAdc_Eva();} //************//电机停止//************ Motor_Stop(){EvaRegs.CMPR1=0;EvaRegs.CMPR2=0;EvaRegs.CMPR3=0;EvaRegs.ACTR.all=0X0FFF;InitData();FlagRegs.bit.Openint=0;EvaRegs.T1CON.bit.TENABLE=0;EvbRegs.T3CON.bit.TENABLE=0;}//************//电机启动//************Motor_Start(){EvaRegs.ACTR.all=0X0999;FlagRegs.bit.Openint=1;t1per=7500;EvaRegs.T1PR =7500; //周期寄存器 5khzEvaRegs.T1CON.all = 0X080C; //连续增减计数EvbRegs.T3PR =0xffff; // 周期寄存器EvbRegs.T3CON.all = 0X170C; // 连续增减计数EvbRegs.EVBIMRA.all=0X0000; // T3下溢使能EvbRegs.EVBIFRA.all=0X0FFFF; // 清中断标志位GpioMuxRegs.GPBMUX.bit.CAP4Q1_GPIOB8=0; //将cap456设置为io口 GpioMuxRegs.GPBMUX.bit.CAP5Q2_GPIOB9=0;GpioMuxRegs.GPBMUX.bit.CAP6QI2_GPIOB10=0;cap2 = GpioDataRegs.GPBDAT.all & 0x0700; //记录cap4-6初始状态cap2 = cap2>>8;cap2 = cap2&0x0007;if(FlagRegs.bit.Sign==1) cap2=7-cap2;else cap2=cap2;GpioMuxRegs.GPBMUX.bit.CAP4Q1_GPIOB8=1; //将cap456设置为CAP口 GpioMuxRegs.GPBMUX.bit.CAP5Q2_GPIOB9=1;GpioMuxRegs.GPBMUX.bit.CAP6QI2_GPIOB10=1;EvaRegs.T1CON.bit.TENABLE=1;FlagRegs.bit.Vflag=1;}//===================================================================== //直流无刷电动机驱动双闭环主程序//===================================================================== //*****************//驱动主程序//*****************void bldc(){intruptcount++;speed();switch((int)cap2){case 4:{Ad(); //检测电流if(ia_fd<0) ia_fd=0; //保护speed_PI(); //转速PI调节Ia_PI(); //电流PI调节EvaRegs.ACTR.all = 0x0F3E; //ia+,ib-EvaRegs.CMPR1=COMP2;break;}case 5:{Ad(); //检测电流if(ia_fd<0) ia_fd=0; //保护speed_PI(); //转速PI调节Ia_PI(); //电流PI调节EvaRegs.ACTR.all = 0x03FE; //ia+,ic- EvaRegs.CMPR1=COMP2;break;}case 1:{Ad();if(ib_fd<0) ib_fd=0;ia_fd=ib_fd;speed_PI();Ia_PI();EvaRegs.ACTR.all = 0x03EF; //ib+,ic- EvaRegs.CMPR2=COMP2;break;}case 3:{Ad();if(ib_fd<0) ib_fd=0;speed_PI();Ia_PI();EvaRegs.ACTR.all = 0x0FE3; //ib+,ia- EvaRegs.CMPR2=COMP2;break;}case 2:{Ad();ia_fd=-ia_fd;if(ia_fd<0) ia_fd=0;speed_PI();Ia_PI();EvaRegs.ACTR.all = 0x0EF3; //ic+,ia- EvaRegs.CMPR3=COMP2;break;}case 6:{Ad();ib_fd=-ib_fd;if(ib_fd<0) ib_fd=0;speed_PI();Ia_PI();EvaRegs.ACTR.all = 0x0E3F; //ic+,ib-EvaRegs.CMPR3=COMP2;break;}default: break;}}//********************************//电流反馈//********************************void Ad(){char delay_zy=0;ia_fd=0,ib_fd=0; //AB相电流检测值清零 for(Iab_Data=0;Iab_Data<=5;Iab_Data++){ia[Iab_Data]=AdcRegs.RESULT0>>4;ib[Iab_Data]=AdcRegs.RESULT1>>4;if(Iab_Data==0){for(delay_zy=0;delay_zy<=10;delay_zy++) {;}}else if(Iab_Data==1){if(ia[0]>ia[1]){Temp_filter= ia[1];ia[1] = ia[0];ia[0] = Temp_filter;}if(ib[0]>ib[1]){Temp_filter= ib[1];ib[1] = ib[0];ib[0] = Temp_filter;}}else{if(ia[Iab_Data]>ia[1]){Temp_filter= ia[1];ia[1] = ia[Iab_Data];ia[Iab_Data] = Temp_filter;}else if(ia[Iab_Data]<ia[0]){Temp_filter= ia[0];ia[0] = ia[Iab_Data];ia[Iab_Data] = Temp_filter;}if(ib[Iab_Data]<ib[0]){Temp_filter= ib[0];ib[0] = ib[Iab_Data];ib[Iab_Data] = Temp_filter;}else if(ib[Iab_Data]>ib[1]){Temp_filter= ib[1];ib[1] = ib[Iab_Data];ib[Iab_Data] = Temp_filter;}}}ia_fd=(ia[2]+ia[3]+ia[4]+ia[5])>>2; ib_fd=(ib[2]+ib[3]+ib[4]+ib[5])>>2; ia_fd=ia_fd-2133;ib_fd=ib_fd-2150;}//********************************//转速反馈计算//********************************void speed(void){if((cap2 != cap1)&&(t2first==1)){if(cap_c!= 0){long2_tmp = 1953125/cap_c;spd_fd_q1 = long2_tmp;intruptcount=0;switch(cap2){case 5:{if(cap1==4) spd_fd_q1=-spd_fd_q1; break;}case 4:{if(cap1==6) spd_fd_q1=-spd_fd_q1; break;case 6:{if(cap1==2) spd_fd_q1=-spd_fd_q1; break;}case 2:{if(cap1==3) spd_fd_q1=-spd_fd_q1; break;}case 3:{if(cap1==1) spd_fd_q1=-spd_fd_q1; break;}case 1:{if(cap1==5) spd_fd_q1=-spd_fd_q1; break;}default:break;}BLDC_SPD[Spd_Data]=spd_fd_q1;if(Spd_Data==1)if(BLDC_SPD[0]>BLDC_SPD[1]){Temp_filter = BLDC_SPD[1];BLDC_SPD[1] = BLDC_SPD[0];BLDC_SPD[0] = Temp_filter;}}else{if(BLDC_SPD[Spd_Data]>BLDC_SPD[1]){Temp_filter= BLDC_SPD[1];BLDC_SPD[1] = BLDC_SPD[Spd_Data];BLDC_SPD[Spd_Data] = Temp_filter;}else if(BLDC_SPD[Spd_Data]<BLDC_SPD[0]) {Temp_filter= BLDC_SPD[0];BLDC_SPD[0] = BLDC_SPD[Spd_Data];BLDC_SPD[Spd_Data] = Temp_filter;}}Spd_Data++;if(Spd_Data>=10){Spd_Data=0;}cc++;if(cc>=3){cc=0;spd_fd_q0=(BLDC_SPD[2]+BLDC_SPD[3]+BLDC_SPD[4]+BLDC_SPD[5]+BLDC_SPD[6]+BLDC_ SPD[7]+BLDC_SPD[8]+BLDC_SPD[9])>>3;}cap1=cap2;}else{spd_fd_q0 = 0;}}else{if(intruptcount>=1500){intruptcount=0;spd_fd_q0=0;}else{spd_fd_q0=spd_fd_q0;}}if(t2first==0) t2first=1;}//********************************//转速调节器//********************************void speed_PI(void){unsigned int abs_spd=0;signed int spd_ki_bldc=1;speed_counter++;if(speed_counter>=10){speed_counter=0;if(FlagRegs.bit.Vflag==1) //转速给定更新{if(FlagRegs.bit.Sign1!=FlagRegs.bit.Sign2)//新转向和原有转向不同 {FlagRegs.bit.Zero =1; //换转向标志位置位spd_given2=spd_given; //缓存目标频率spd_given=0; //当前目标频率给定0}FlagRegs.bit.Vflag =0; //转速给定更新标志位清零FlagRegs.bit.Sign2=FlagRegs.bit.Sign1; //更新转向标志}if((spd_fd_q0==0)&&(FlagRegs.bit.Zero==1)) //当前给定频率,目标频率需要反向(电机需要从正转到反转){spd_given=spd_given2; //给定新的目标频率FlagRegs.bit.Zero=0; //换转向标志位清零FlagRegs.bit.Sign=FlagRegs.bit.Sign1; //给定新的转向}speed_e1 = speed_e2; //将本次误差用作下次使用speed_e2 = spd_given - abs(spd_fd_q0);abs_spd=abs(spd_given)>>2;if((abs(speed_e2))<abs_spd){spd_ki_bldc=spd_ki;}else{if(spd_given<100) spd_ki_bldc=12;//10*8else if(spd_given<150) spd_ki_bldc=10;else if(spd_given<200) spd_ki_bldc=8;else if(spd_given<250) spd_ki_bldc=6;else if(spd_given<300) spd_ki_bldc=5;else spd_ki_bldc=4;}long1_tmp= (long)spd_kp * (speed_e2 - speed_e1); long2_tmp= (long)spd_ki_bldc * speed_e2;u16_tmp1=(long1_tmp+long2_tmp)>>10;Idc_given2=Idc_given2+u16_tmp1;if(Idc_given2 > Idc_max) //限幅Idc_given2 = Idc_max;else if(Idc_given2 < Idc_min)Idc_given2 = Idc_min;}}//********************************//电流调节器//********************************void Ia_PI(void){Ia_e1 = Ia_e2; //将本次误差用作下次使用Ia_e2 = Idc_given2 - ia_fd;long1_tmp=(long)id_kp * (Ia_e2 - Ia_e1);long2_tmp=(long)id_ki * Ia_e2;u16_tmp1=(long1_tmp+long2_tmp)>>12;COMP2=COMP2+u16_tmp1;if(COMP2 < 0)COMP2 = 0;else if(COMP2 > (t1per-200))COMP2 = (t1per-200);}//===================================================================== //T1下溢中断子程序//===================================================================== interrupt void t1uf_int(void){IFR=0x0000; //中断标志位清零PieCtrl.PIEACK.all=0xffff;if(FlagRegs.bit.Openint==1) //控制程序准备开始运行{bldc();GpioDataRegs.GPACLEAR.bit.GPIOA11=1;Sci_Send(400);}EvaRegs.EVAIFRA.bit.T1UFINT=1; //T1下溢中断标志位清零EINT;}//=====================================================================// CAP4/5/6中断子程序//===================================================================== interrupt void cap_int(void){IFR=0x0000;PieCtrl.PIEACK.all=0xffff;GpioMuxRegs.GPBMUX.bit.CAP4Q1_GPIOB8=0; //将cap456设置为io口GpioMuxRegs.GPBMUX.bit.CAP5Q2_GPIOB9=0;GpioMuxRegs.GPBMUX.bit.CAP6QI2_GPIOB10=0;cap2 = GpioDataRegs.GPBDAT.all & 0x0700; //记录cap4-6初始状态cap2 = cap2>>8;cap2 = cap2&0x0007;if(FlagRegs.bit.Sign==1) cap2=7-cap2;else cap2=cap2;if(EvbRegs.EVBIFRC.bit.CAP4INT==1){cap_a=EvbRegs.CAP4FIFO;EvbRegs.EVBIFRC.bit.CAP4INT=1;}else if(EvbRegs.EVBIFRC.bit.CAP5INT==1){cap_a=EvbRegs.CAP5FIFO;EvbRegs.EVBIFRC.bit.CAP5INT=1;}else if(EvbRegs.EVBIFRC.bit.CAP6INT==1)cap_a=EvbRegs.CAP6FIFO;EvbRegs.EVBIFRC.bit.CAP6INT=1;}cap_c=cap_a-cap_b;if(cap_c<0) cap_c=cap_c+0xffff;cap_b=cap_a;GpioMuxRegs.GPBMUX.bit.CAP4Q1_GPIOB8=1; //将cap456设置为CAP口 GpioMuxRegs.GPBMUX.bit.CAP5Q2_GPIOB9=1;GpioMuxRegs.GPBMUX.bit.CAP6QI2_GPIOB10=1;EINT;}InitData(){char i;T_pointer=0; //发送数据数组指针R_pointer=0; //接收数据数组指针a2=0;for(i=0;i<8;i++){Sci_Rx[i]=0;Sci_Tx[i]=0;cap1=0; //前次换向标志位cap2=0; //本次换向标志位intruptcount=0; //定时器1下溢中断次数Idc_given2=0; //算得本次电流给定值speed_e1=0; //前次转速误差speed_e2=0; //本次转速误差Ia_e1=0; //前次电流误差Ia_e2=0; //本次电流误差t2over=0;t2first=0;COMP2=0; //装比较寄存器值speed_counter=0; //速度环计数器cap_a=0;cap_b=0;cap_c=0;for(i=0;i<10;i++){BLDC_SPD[i]=0;}}/////////////////////////////////////////////////////////////////////// // BLDC.c 程序结束////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 文件名: DSP28_Ev.c// 意义: DSP28 EV初始化/////////////////////////////////////////////////////////////////////// #include "DSP28_Device.h"void InitEv(void){//定时器T1初始化EvaRegs.T1PR =7500; //周期寄存器 5khzEvaRegs.T1CON.all = 0X080C; //连续增减计数EvaRegs.CMPR1=0; //比较寄存器初始化EvaRegs.CMPR2=0;EvaRegs.CMPR3=0;EvaRegs.ACTR.all=0X0FFF; //6PWM强制高CONA.all=0X8A00; //比较寄存器T1下溢重载CONA.bit.ACTRLD=2; //方式控制寄存器立即重载EvaRegs.DBTCONA.all=0X0FF4; //死区3.2usEvaRegs.T1CNT=0;EvaRegs.EVAIMRA.all=0X0200; //T1下溢使能EvaRegs.EVAIFRA.all=0X0FFFF; //清中断标志位//定时器T4初始化EvbRegs.T4PR = 60000; // 周期寄存器EvbRegs.T4CON.all = 0X0F0C; // 连续增减计数EvbRegs.EVBIMRB.all=0X0000; // T4中断不使能EvbRegs.EVBIFRB.all=0X0FFFF; // 清中断标志位}/////////////////////////////////////////////////////////////////////// // DSP28_Ev.c结束/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 文件名: DSP28_Sci.c// 意义: DSP28 SCI 初始化//////////////////////////////////////////////////////////////////////// #include "DSP28_Device.h"unsigned int * UART_MODE = (unsigned int *) 0x4010;void InitSci(void){// Initialize SCI-A:*UART_MODE = 0x44;EALLOW;GpioMuxRegs.GPFMUX.all = 0x0030;EDIS;SciaRegs.SCICCR.all = 0x07; //8位,无校验SciaRegs.SCICTL1.all = 0x03; //软件复位发送接收使能SciaRegs.SCIHBAUD = 0x01; //波特率9600B/S SciaRegs.SCILBAUD = 0x0E7;SciaRegs.SCICTL1.all = 0x23; //清除软件复位发送接收使能}//发送准备好int SciaTx_Ready(void){unsigned int i;if(SciaRegs.SCICTL2.bit.TXRDY == 1){i = 1;}else{i = 0;}return(i);}//接收准备好int SciaRx_Ready(void){unsigned int i;if(SciaRegs.SCIRXST.bit.RXRDY == 1){i = 1;}else{i = 0;}return(i);}///////////////////////////////////////////////////////////////////////////DSP28_Sci.c结束///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// 文件名: DSP28_SysCtrl.c// 意义: DSP28 系统控制寄存器初始化//////////////////////////////////////////////////////////////////////////// #include "DSP28_Device.h"void InitSysCtrl(void){Uint16 i;EALLOW;DevEmuRegs.M0RAMDFT = 0x0300;DevEmuRegs.M1RAMDFT = 0x0300;DevEmuRegs.L0RAMDFT = 0x0300;DevEmuRegs.L1RAMDFT = 0x0300;DevEmuRegs.H0RAMDFT = 0x0300;// Disable watchdog moduleSysCtrlRegs.WDCR= 0x0068;// Initalize PLLSysCtrlRegs.PLLCR = 0xA; //CLKIN=150M// Wait for PLL to lockfor(i= 0; i< 5000; i++){}// HISPCP/LOSPCP prescale register settings, normally it will be set to default valuesSysCtrlRegs.HISPCP.all = 0x0001; //高速时钟75MSysCtrlRegs.LOSPCP.all = 0x0002; //低速时钟37.5M// Peripheral clock enables set for the selected peripherals.SysCtrlRegs.PCLKCR.bit.EVAENCLK=1; //使能EVASysCtrlRegs.PCLKCR.bit.EVBENCLK=1; //使能EVBSysCtrlRegs.PCLKCR.bit.SCIENCLKA=1;//使能SCISysCtrlRegs.PCLKCR.bit.ADCENCLK=1; //使能ADC}///////////////////////////////////////////////////////////////////////////DSP28_SysCtrl.c结束//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 文件: DSP28_PieCtrl.c// 意义: DSP28 PIE 控制寄存器初始化///////////////////////////////////////////////////////////////////////// #include "DSP28_Device.h"void InitPieCtrl(void){// Disable PIE:PieCtrl.PIECRTL.bit.ENPIE = 0;// Clear all PIEIER registers:PieCtrl.PIEIER1.all = 0;PieCtrl.PIEIER2.all = 0;PieCtrl.PIEIER3.all = 0;PieCtrl.PIEIER4.all = 0;PieCtrl.PIEIER5.all = 0;PieCtrl.PIEIER6.all = 0;PieCtrl.PIEIER7.all = 0;PieCtrl.PIEIER8.all = 0;PieCtrl.PIEIER9.all = 0;PieCtrl.PIEIER10.all= 0;PieCtrl.PIEIER11.all= 0;PieCtrl.PIEIER12.all= 0;// Clear all PIEIFR registers:PieCtrl.PIEIFR1.all = 0;PieCtrl.PIEIFR2.all = 0;PieCtrl.PIEIFR3.all = 0;PieCtrl.PIEIFR4.all = 0;PieCtrl.PIEIFR5.all = 0;PieCtrl.PIEIFR6.all = 0;PieCtrl.PIEIFR7.all = 0;PieCtrl.PIEIFR8.all = 0;PieCtrl.PIEIFR9.all = 0;PieCtrl.PIEIFR10.all= 0;PieCtrl.PIEIFR11.all= 0;PieCtrl.PIEIFR12.all= 0;// Enable PIE:PieCtrl.PIECRTL.bit.ENPIE = 1;PieCtrl.PIEACK.all = 0xFFFF;}///////////////////////////////////////////////////////////////////////////DSP28_PieCtrl.c结束//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 文件名: DSP28_PieVect.c// 意义: DSP28 PIE 向量表初始化//////////////////////////////////////////////////////////////////////////// #include "DSP28_Device.h"const struct PIE_VECT_TABLE PieVectTableInit = {PIE_RESERVED, // Reserved spacePIE_RESERVED,PIE_RESERVED,PIE_RESERVED,PIE_RESERVED,PIE_RESERVED,PIE_RESERVED,PIE_RESERVED,PIE_RESERVED,PIE_RESERVED,PIE_RESERVED,PIE_RESERVED,PIE_RESERVED,// Non-Peripheral InterruptsINT13_ISR, // XINT13 or CPU-Timer 1 INT14_ISR, // CPU-Timer2DATALOG_ISR, // Datalogging interrupt RTOSINT_ISR, // RTOS interruptEMUINT_ISR, // Emulation interrupt NMI_ISR, // Non-maskable interrupt ILLEGAL_ISR, // Illegal operation TRAP USER0_ISR, // User Defined trap 0 USER1_ISR, // User Defined trap 1 USER2_ISR, // User Defined trap 2USER3_ISR, // User Defined trap 3 USER4_ISR, // User Defined trap 4 USER5_ISR, // User Defined trap 5 USER6_ISR, // User Defined trap 6 USER7_ISR, // User Defined trap 7 USER8_ISR, // User Defined trap 8 USER9_ISR, // User Defined trap 9 USER10_ISR, // User Defined trap 10 USER11_ISR, // User Defined trap 11 // Group 1 PIE VectorsPDPINTA_ISR, // EV-APDPINTB_ISR, // EV-Brsvd_ISR,XINT1_ISR,XINT2_ISR,ADCINT_ISR, // ADCTINT0_ISR, // Timer 0WAKEINT_ISR, // WD// Group 2 PIE VectorsCMP1INT_ISR, // EV-ACMP2INT_ISR, // EV-ACMP3INT_ISR, // EV-AT1PINT_ISR, // EV-AT1CINT_ISR, // EV-AT1UFINT_ISR, // EV-Arsvd_ISR,// Group 3 PIE Vectors T2PINT_ISR, // EV-A T2CINT_ISR, // EV-A T2UFINT_ISR, // EV-A T2OFINT_ISR, // EV-A CAPINT1_ISR, // EV-A CAPINT2_ISR, // EV-A CAPINT3_ISR, // EV-A rsvd_ISR,// Group 4 PIE Vectors CMP4INT_ISR, // EV-B CMP5INT_ISR, // EV-B CMP6INT_ISR, // EV-B T3PINT_ISR, // EV-B T3CINT_ISR, // EV-B T3UFINT_ISR, // EV-B T3OFINT_ISR, // EV-B rsvd_ISR,// Group 5 PIE Vectors T4PINT_ISR, // EV-B T4CINT_ISR, // EV-B T4UFINT_ISR, // EV-B T4OFINT_ISR, // EV-BCAPINT5_ISR, // EV-B CAPINT6_ISR, // EV-B rsvd_ISR,// Group 6 PIE Vectors SPIRXINTA_ISR, // SPI-A SPITXINTA_ISR, // SPI-A rsvd_ISR,rsvd_ISR,MRINTA_ISR, // McBSP-A MXINTA_ISR, // McBSP-A rsvd_ISR,rsvd_ISR,// Group 7 PIE Vectors rsvd_ISR,rsvd_ISR,rsvd_ISR,rsvd_ISR,rsvd_ISR,rsvd_ISR,rsvd_ISR,rsvd_ISR,// Group 8 PIE Vectors rsvd_ISR,rsvd_ISR,rsvd_ISR,rsvd_ISR,rsvd_ISR,rsvd_ISR,rsvd_ISR,// Group 9 PIE Vectors SCIRXINTA_ISR, // SCI-A SCITXINTA_ISR, // SCI-A SCIRXINTB_ISR, // SCI-B SCITXINTB_ISR, // SCI-B ECAN0INTA_ISR, // eCAN ECAN1INTA_ISR, // eCANrsvd_ISR,rsvd_ISR,// Group 10 PIE Vectors rsvd_ISR,rsvd_ISR,rsvd_ISR,rsvd_ISR,rsvd_ISR,rsvd_ISR,rsvd_ISR,rsvd_ISR,// Group 11 PIE Vectorsrsvd_ISR,rsvd_ISR,rsvd_ISR,rsvd_ISR,rsvd_ISR,rsvd_ISR,rsvd_ISR,// Group 12 PIE Vectorsrsvd_ISR,rsvd_ISR,rsvd_ISR,rsvd_ISR,rsvd_ISR,rsvd_ISR,rsvd_ISR,rsvd_ISR,};void InitPieVectTable(void){int16 i;Uint32 *Source = (void *) &PieVectTableInit;Uint32 *Dest = (void *) &PieVectTable;EALLOW;for(i=0; i < 128; i++)*Dest++ = *Source++;EDIS;// Enable the PIE Vector TablePieCtrl.PIECRTL.bit.ENPIE = 1;}///////////////////////////////////////////////////////////////////////// //DSP28_PieVect.c结束////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 文件名: DSP28_Gpio.c// 意义: 初始化I/O///////////////////////////////////////////////////////////////////////// #include "DSP28_Device.h"void InitGpio(void){EALLOW;// Set GPIO A port pins, enable 6PWMGpioMuxRegs.GPAMUX.all=0x033F; // EVA PWM 1-6GpioMuxRegs.GPADIR.all=0xFF3F;。
基于DSP的直流电机控制系统C程序(包括1602显示,捕获CAP、PWM波发生程序)

DSP2407控制直流电机程序附录系统主程序:/*Main.c*//*PWM电机控制程序,正反转,默认设置的工作频率1kHz*/#include "register.h"void SystemInit();void Timer1Init();void KickDog();void change();void cap4Init();unsigned int numled=20,numkey=0;unsigned int a=0,b=0;unsigned int count=0,time_count=0,f_count=0;unsigned char tab1[3]={'r','/','s'};unsigned char tab2[15]={' ','S','e','t',' ','B','y',' ','L,'i','n','F','a','n','g'};#define RS_H PADATDIR=PADA TDIR | 0x0008#define RS_L PADATDIR=PADA TDIR & 0xFFF7#define RW_H PADATDIR=PADATDIR | 0x0010#define RW_L PADATDIR=PADATDIR & 0xFFEF#define E_H PADATDIR=PADA TDIR | 0x0020#define E_L PADATDIR=PADA TDIR & 0xFFDF#define PASC PADATDIR=PADA TDIR | 0x3800#define PCSC PCDA TDIR=PCDATDIR | 0xFF00#define PCSR PCDA TDIR=PCDATDIR & 0x00FF#define uchar unsigned char#define uint unsigned intvoid LCD1602init();void writecommand(uchar command);void LCDdisplay();void readstat();void delay(unsigned int i);main(){SystemInit(); //系统初始化MCRB=MCRB & 0xFF00; //IOPC口为一般I/O口MCRA=MCRA & 0xC0C7; //IOPB0-5、IOPA3-5设为IO口模式MCRC=MCRC & 0xE0FF; //IOPF0-4设为IO口模式PBDATDIR=0xFFC2; //所有LED=0PFDATDIR=0xF8E7; //置IOPE1-3为输入口Timer1Init(); //定时器1初始化cap4Init();LCD1602init();T3CON=T3CON|0X0040; // 启动定时器3asm(" CLRC INTM ");while(1){change();LCDdisplay();}}void SystemInit(){asm(" SETC INTM "); /* 关闭总中断*/asm(" CLRC SXM "); /* 禁止符号位扩展*/asm(" CLRC CNF ");asm(" CLRC OVM "); /* 累加器结果正常溢出*/SCSR1=0x83FE; /* 系统时钟CLKOUT=20*2=40M *//* 打开ADC,EV A,EVB,CAN和SCI的时钟,系统时钟CLKOUT=40M */ WDCR=0x006F; /* 禁止看门狗,看门狗时钟64分频*/KickDog(); /* 初始化看门狗*/IFR=0xFFFF; /* 清除中断标志*/IMR=0x0000; /* 禁止所有中断*/}void Timer1Init(){EVAIMRA=0x0080; // 定时器1周期中断使能EVAIFRA=0xFFFF; // 清除中断标志GPTCONA=0x0000;T1PR=25000; // 定时器1初值,定时0.4us*2500=1msCMPR1=25000;T1CNT=0;T1CON=0x144E; //增模式, TPS系数40M/16=2.5M,T1使能IMR|=0x0002; //打开中断2}void cap4Init(){T3PR=0xFFFF; // 通用定时器3的周期寄存器为0XFFFFT3CON=0x1400; // 通用定时器3为连续增计数模式T3CNT=0x00; // 计数器清0WSGR=0x0000; // 禁止所有等待状态CAPCONB=0x0A440; // 设置捕获单元4为检测上升沿,且选择TIMER3为时钟asm(" clrc INTM"); // 开总中断IMR|=0x0008; // 允许中断优先级4的中断CAP4FIFO=0;EVBIMRC=EVBIMRC|0X0001; // 允许CAPTURE4中断EVBIFRC=EVBIFRC|0X0FFFF;}void LCD1602init() //LCD1602初始化函数{writecommand(0x38);writecommand(0x38);writecommand(0x38);delay(150);writecommand(0x01);delay(150);writecommand(0x02);delay(150);writecommand(0x06);delay(150);writecommand(0x0c);delay(150);writecommand(0x38);}void writecommand(uchar command) //LCD1602写命令函数{readstat();PCSC;PASC;RS_L;RW_L;PCDATDIR=PCDATDIR & 0x0FF00|command;//在此条指令之后只要使能信号有个由高到低的跳变即可写液晶delay(5);E_L; //可以不要asm(" NOP"); //可以不要asm(" NOP"); //可以不要asm(" NOP"); //可以不要E_H;delay(5); //可以不要E_L;}void readstat(){unsigned int stat;PCSR;PASC;while(1){RS_L;E_H;RW_H;stat=PCDATDIR & 0x0080;if(stat==0)break;}}void writedata(uchar data) //LCD1602写数据函数{readstat();PCSC;PASC;RS_H;RW_L;PCDA TDIR=PCDA TDIR & 0x0FF00|data;//在此条指令之后只要使能信号有个由高到低的跳变即可写液晶//如果在这之前就有//由高到低的变化就会送出去乱码,即不是自己想要的值delay(5);E_L; //可以不要asm(" NOP");//可以不要asm(" NOP");//可以不要asm(" NOP");//可以不要E_H;delay(5); //可以不要E_L;}void change(){MCRA=MCRA | 0x00C0; //PA6为PWM口ACTRA=0x0006; //PWM1高有效DBTCONA=0x0530; //使能死区定时器1,分频40M/16=2.5M,死区时5*0.4us=2us COMCONA=0xA600; //比较控制寄存器if((PFDATDIR & 0x0001)!=0x0001) //若有按键仍为低,IOPF0=0{delay(800);if((PFDATDIR & 0x0001)!=0x0001){b=0;numkey=1;a++;if(a==4) a=1;while((PFDATDIR & 0x0001)!=0x0001);}}if((PFDATDIR & 0x0002)!=0x0002) //若有按键仍为低,IOPF1=0{a=0;b=0;numkey=2;}if((PFDATDIR & 0x0004)!=0x0004) //若有按键仍为低,IOPF2=0 {delay(800);if((PFDATDIR & 0x0004)!=0x0004){a=0;numkey=3;b++;if(b==4) b=1;while((PFDA TDIR & 0x0004)!=0x0004);}}if(numkey==1){PFDATDIR=PFDATDIR & 0xF8E7;PFDATDIR=PFDATDIR | 0x1010;PBDATDIR=PBDA TDIR & 0xFFC7; //将其他的led灭掉PBDATDIR=PBDA TDIR | 0x0404; //IOPB2=1;LED1亮if(a==1) CMPR1=21000;if(a==2) CMPR1=17000;if(a==3) CMPR1=23500;}if(numkey==2){PFDATDIR=PFDA TDIR & 0xF8E7;CMPR1=25000;PFDATDIR=PFDATDIR | 0x1818;PBDATDIR=PBDA TDIR & 0xFFCB;PBDATDIR=PBDA TDIR | 0x0808; //IOPB3=1;LED2亮}if(numkey==3){PFDATDIR=PFDATDIR & 0xF8E7;PFDATDIR=PFDATDIR | 0x0808;PBDATDIR=PBDA TDIR & 0xFFD3;PBDATDIR=PBDA TDIR | 0x1010; //IOPB4=1;LED3亮if(b==1) CMPR1=21000;if(b==2) CMPR1=17000;if(b==3) CMPR1=23500;}}void c_int2() /*定时器1中断服务程序*/{if(PIVR!=0x27){ asm(" CLRC INTM ");return;}time_count++;if(time_count>=100){time_count=0;f_count=count;count=0;}T1CNT=0;numled--;if(numled==0){numled=20;if((PBDATDIR & 0x0001)==0x0001)PBDA TDIR=PBDATDIR & 0xFFFE; //IOPB0=0;LED灭elsePBDA TDIR=PBDATDIR |0x0101; //IOPB0=1;LED亮}EVAIFRA=0x80;asm(" CLRC INTM ");return;}void cap4int(){int flag;flag=EVBIFRC&0X01; // 判断是否是CAP4中断if(flag!=0x01){asm(" clrc INTM"); // 返回前开中断return; // 如果不是CAP4中断,则直接返回}count++;EVBIFRC=EVBIFRC|0x01; // 写"1"清除CAP4中断标志asm(" clrc INTM"); // 返回前开中断return; // 中断返回}void LCDdisplay(void){uint data;uchar data_qian,data_bai,data_shi,data_ge,address=0,add=0;writecommand(0x84);if(numkey == 3){data = '-';}else{data = '+';}writedata((uchar)data);data=f_count/4;data_qian=data/1000;data_bai=data%1000/100;data_shi=data%100/10;data_ge =data%10; writecommand(0x85);writedata(data_qian+0x30); writecommand(0x86);writedata(data_bai+0x30); writecommand(0x87);writedata(data_shi+0x30); writecommand(0x88);writedata(data_ge+0x30); address=0x89;for(add=0;add<3;add++,address++) {writecommand(address);data=tab1[add];writedata(data);}address=0xC0;for(add=0;add<15;add++,address++){writecommand(address);data=tab2[add];writedata(data);}}void delay(unsigned int i){ unsigned int j;for(;i>0;i--)for(j=1000;j>0;j--);}void interrupt PHANTOM(){asm(" clrc INTM"); // 返回前开中断return;}void KickDog() /*踢除看门狗*/{WDKEY=0x5555;WDKEY=0xAAAA;}中断向量表:;define.asm.include "lf2407a.h".include "F2407REGS.H".global _c_int0,_c_int2,_cap4int.global _IMR,_IFR.global _SCSR1,_SCSR2,_XINT1CR,_XINT2CR.global _WDCNTR,_WDKEY,_WDCR.global_MCRA,_MCRB,_MCRC,_PADA TDIR,_PBDATDIR,_PCDA TDIR,_PEDATDIR,_PFDA TDIR.global_ADCTRL1,_ADCTRL2,_MAXCONV,_CHSELSEQ1,_CHSELSEQ2,_CHSELSEQ3,_CHSELSEQ 4.global _AUTO_SEQ_SR,_RESULT0,_RESULT1,_RESULT2,_RESULT8,_RESULT9.global_CANMDER,_CANTCR,_CANRCR,_CANMCR,_CANBCR2,_CANBCR1,_CANESR,_CANGSR .global _CANCEC,_CANIFR,_CANIMR,_CANLAM0H,_CANLAM0L.global_CANID0L,_CANID0H,_CANCTRL0,_CANBX0A,_CANBX0B,_CANBX0C,_CANBX0D.global_CANID1L,_CANID1H,_CANCTRL1,_CANBX1A,_CANBX1B,_CANBX1C,_CANBX1D.global_CANID2L,_CANID2H,_CANCTRL2,_CANBX2A,_CANBX2B,_CANBX2C,_CANBX2D.global_CANID3L,_CANID3H,_CANCTRL3,_CANBX3A,_CANBX3B,_CANBX3C,_CANBX3D.global_CANID4L,_CANID4H,_CANCTRL4,_CANBX4A,_CANBX4B,_CANBX4C,_CANBX4D.global_CANID5L,_CANID5H,_CANCTRL5,_CANBX5A,_CANBX5B,_CANBX5C,_CANBX5D.global_SCICCR,_SCICTL1,_SCIHBAUD,_SCILBAUD,_SCICTL2,_SCIRXST,_SCIRXEMU,_SCIRXBU F,_SCITXBUF,_SCIPRI;.global _GPTCONA,_T1CNT,_T1PR,_T1CON,_T2CNT,_T2PR,_T2CON.global _EV AIMRA,_EV AIMRB,_EV AIFRA,_EV AIFRB,_PIVR.global_GPTCONB,_T3CNT,_T3CMPR,_T3PR,_T3CON,_COMCONA,_ACTRA,_DBTCONA,_CMPR1, _EVBIMRA,_EVBIFRA.global _WSGR,_CAPCONB,_EVBIMRC,_EVBIFRC;建立中断向量表.sect ".vectors"RSVECT B _c_int0INT1 B PHANTOMINT2 B _c_int2INT3 B PHANTOMINT4 B _cap4intINT5 B PHANTOMINT6 B PHANTOM.textPHANTOM:KICK_DOGRET.end。
DSP异步电机模糊控制编程实现

第10卷 第1期2006年1月电 机 与 控 制 学 报EL EC TR IC MACH I N ES AND CON TROLVol 110No 11Jan .2006基于D SP 的异步电机模糊控制系统编程实现刘福才1, 韩会山1,2, 陈 丽2(1.燕山大学电气工程学院,河北秦皇岛066004;2.邢台职业技术学院电子系,河北邢台054000)摘 要:针对模糊控制器的DSP 编程实现问题,依据模糊输入、输出空间的对称性,采用对空间分区建立索引表的方法,建立了一种基于T MS320LF2407A 的新型模糊控制器的编程实现框图,并应用在交流感应电动机V /F 闭环变频控制系统的速度环中。
在理论分析和系统仿真的基础上,通过实验验证该算法的可行性,并且系统具有良好的动态性能,转速超调量小。
关键词:异步电机;DSP;V /F 变频;模糊控制中图分类号:TP273;T M921.5文献标识码:A文章编号:1007-449X (2006)01-0018-05A DSP based i n ducti on motor control syste m withfuzzy controller program sche meL I U Fu 2cai 1, HAN Hui 2shan1,2, CHEN L i2(1.Depart m ent of Aut omati on,Yanshan University,Q inhuangdao 066004,China;2.Depart m ent of Electricity,Xingtai I nstitute of Pr ofessi onal Technol ogy,Xingtai 054000,China )Abstract:I n order t o realize the DSP comp ile of fuzzy contr ol,an index bl ock of fuzzy s pace was com 2p iled,utilizing the input/out put fuzzy s pace sy mmetry .Based on the T MS320LF2407A,a novel fuzzy al 2gorith m p r ogra m method f or V /F inducti on mot or contr ol syste m has been p r oposed .Si m ulati on results and experi m ent results de monstrate that the p r oposed fuzzy p r ogra m sche me is valid,and the dyna m ic perf or mance is quite well,and the overshoot is s mall .Key words:inducti on mot or;DSP;V /F converter;fuzzy contr ol收稿日期:2004-09-21;修订日期:2005-06-22基金项目:燕山大学博士基金资助项目(B111)作者简介:刘福才(1966-),男,博士、教授,研究方向为模糊辨识与预测控制、电力拖动及其计算机控制;韩会山(1975-),男,硕士研究生,研究方向为电力拖动控制系统及交流伺服调速系统智能控制;陈 丽(1977-),女,讲师、硕士,从事机器人控制、智能控制研究。
基于DSP的无刷直流电机控制方法

算法精度
改进算法,提高控制精度,减小 电机运行过程中的误差,提升电 机性能。
鲁棒性增强
增强控制算法的鲁棒性,减小外 部干扰对电机性能的影响,提高 系统的稳定性。
硬件设计优化
电路板布局优化
合理布置电路板上的元器 件,减小信号传输延时和 干扰,提高信号质量。
电源管理优化
优化电源电路设计,提高 电源稳定性和效率,降低 电源噪声对控制系统的影 响。
基于DSP的无刷直流电机控 制方法
汇报人: 2024-01-01
目录
• 引言 • 无刷直流电机原理 • DSP技术基础 • 基于DSP的无刷直流电机控制
方法 • 优化与改进 • 结论与展望
01
引言
研究背景与意义
研究背景
随着工业自动化和智能化的快速发展,无刷直流电机(BLDCM)因其高效、节能、高可靠性等优点 ,在许多领域得到了广泛应用。为了实现无刷直流电机的精确控制,需要研究先进的控制策略和方法 。
直流电机的控制中。
研究趋势
随着人工智能和机器学习技术的不断发展,基于深度学习、强化学习等机器学习算法的 无刷直流电机控制方法成为新的研究趋势。这些方法能够实现对电机的自适应、自主学 习控制,进一步提高电机的性能和智能化水平。同时,随着电力电子技术和传感器技术
的不断发展,无刷直流电机的控制精度和响应速度也将得到进一步提高。
定性。
无刷直流电机应用领域
01
02
03
工业自动化
无刷直流电机广泛应用于 各种自动化生产线、机器 人、数控机床等领域。
电动车
无刷直流电机在电动车领 域具有广泛的应用,如电 动自行车、电动摩托车、 电动汽车等。
家用电器
无刷直流电机也应用于家 用电器中,如空调、冰箱 、洗衣机等。
利用DSP控制直流无刷电机

利用DSP控制直流无刷电机直流无刷电机(Brushless DC Motor,简称BLDC)由于其高效、高转速、大扭矩和低噪音等特性而被广泛应用于各种领域。
要控制BLDC进行转速调节、位置控制等,需要使用数字信号处理器(Digital Signal Processor,简称DSP)来实现。
本文将详细介绍如何利用DSP控制直流无刷电机。
一、直流无刷电机介绍直流无刷电机由转子和定子组成,电机可通过电子调速控制技术实现闭环控制,即通过检测电流、电压、角度等参数来实现控制。
相较于传统的可调电阻电调速和功率电子器件调速,无刷电机控制方式更为精确,可控性更高,并且在减小电气噪声的同时大大提高了效率。
二、直流无刷电机的控制方式直流无刷电机的控制方式可以分为三种:感应式、霍尔传感器控制、反电动势检测控制。
其中,感应式控制方式较为简单,但其准确性和鲁棒性较差;霍尔传感器控制方式使用霍尔元件检测转子位置,可以获得更高的准确性和鲁棒性;反电动势检测控制方式通过检测转子的反电动势来确定位置,具有简化硬件和准确性高等优点。
三、DSP控制直流无刷电机利用DSP控制直流无刷电机需要进行以下几个步骤:1. 设置DSP的GPIO口并输入代码:用GPIO口连接电机,可根据需要设置GPIO管脚的中断、状态和其他属性,并输入代码到DSP中。
2. 制作电机转速控制器:通过编写参考电路和硬件控制程序来制作电机转速控制器,代码需要根据控制方式进行适当的修改。
3. 编写电机控制程序:根据转速调节、位置控制等的需求,编写相关的电机控制程序。
基本步骤包括:初始化电机控制器、设定控制参数、检测电机状态、执行电机控制指令等。
4. 测试和优化:根据测试结果优化电机控制程序,以达到最佳效果。
在测试过程中可以使用示波器、逻辑分析仪等工具进行分析。
四、DSP控制直流无刷电机的优点1. 高精度DSP能够提供高精度的控制,可在微秒级的时间内执行多种运算,实现高速、高精度的控制。
电机控制的DSP程序设计及CAN基础知识

电机控制系统程序设计
通信发送程序设计 在发送部分软件设计中,如果根 据状态寄存器的发送状态标志来 判断是否可以发送数据,那么当 CAN总线出现断路,然后又正常, 这时即便进行软件初始化,数据也无法正常发送,原因是该状态标志位无法通过软件进行初始化来进行复位,因此在软件设计时可以不考虑该状态标志位。
CAN总线基本概念
CAN总线线与
高速CAN( CAN High Speed )遵循标准ISO-IS 11898,用于位速率为125kbp
到1Mbps之间的高速总线。
低速CAN( CAN Low Speed )遵循标准ISO-IS 11519-2,用于位速率在125kbps
以下的低速总线。
CAN总线接口标准
DSP芯片简介
2.DSP的性能 DSP性能公式:CPU时间 = CPI × IC / 时钟频率 三个参数反映了与体系结构相关的三种技术。 (1).时钟频率反映了DSP实现技术、生产工艺和计算机组织。 (2).CPI是指令时钟数,反映了DSP实现技术、计算机指令集的结构和计 算机组织。 (3).IC是程序执行过程中所处理的指令数,反映了DSP指令集的结构和编 译术。 从目前情况来看,提高某一个参数指标,不会明显地影响其它两个指标。 这对于综合运用各种技术改进计算机系统的性能是非常有益的。
电机控制的DSP程序设计及CAN基础知识 姓 名:叶振锋 时 间:2009年3月26日 公 司:上海电驱动有限公司
旅游旅行攻略
CLICK TO ADD TITLE
01
概述
电机控制系统程序设计
03
电机控制系统结构图
子程序模块设计说明
05
电机控制系统动态结构图
软件设计与调试注意事项
基于DSP控制的感应电机变频调速系统上位机程序使用说明

基于DSP控制的感应电机变频调速系统上位机程序使用说明目录基于DSP控制的感应电机变频调速系统上位机程序使用说明1.概述 (1)2.安装指南 (3)3.功能使用说明3.1 系统状态控制命令 (3)3.2 系统设置命令 (5)3.3 控制方式参数设置 (6)3.4 波形显示 (9)3.5 显示辅助设置 (10)3.6 数据采集参数设置 (12)3.7 状态显示 (14)4.使用实例4.1 开环SPWM异步调制启动转速与启动电流观测 (14)4.2 磁场定向(FOC)双闭环控制实验 (15)4.3 试验结束注意 (17)4.4 显示后期处理 (17)基于DSP控制的高性能无刷直流电动机实验系统上位机程序使用说明1.安装指南 (19)2.软件功能 (19)3.功能使用说明3.1 系统状态控制命令 (20)3.2系统命令 (21)3.3实验数据数值及波形显示 (23)3.4数据采集参数设置 (26)3.5控制方式参数设置 (27)4.调速系统 (27)5.伺服系统 (28)6.状态显示 (28)7.其他支持功能 (29)8.使用实例 (29)外扩DSP使用说明 (32)基于DSP 控制的感应电机变频调速系统上位机程序使用说明1.概述MCL-13上位机控制程序,是“基于DSP 控制的感应电机变频调速系统(MCL-13)”的上位机控制程序。
本软件与MCL-13挂箱配套使用。
MCL-13挂箱上备有串口RS232连接插座。
用户使用本软件前,应通过此连接插座与上位PC 机串口妥善连接。
脱离MCL-13挂箱,本软件将无效,装入运行时会引起死机。
程序主界面(User Interface )如图1.所示。
MSCL-13上位机控制程序可以完成对MCL-13系统的上位机控制,包括面板命令控制、各类控制策略内部参数给定、各类波形捕捉和后期数据图象处理。
1.1 面板控制命令给定:串口设置。
●数据后期处理命令给定。
在下列4种控制策略中,任意选择一种进行实验:开环SPWM 控制,开环空间矢量控制,闭环磁场定向控制,闭环直接转矩控制。
DSP三相交流电动机SVPWM开环调速控制程序(硬件法)

《4-3三相交流电动机SVPWM开环调速控制程序(硬件法)》.include "240x.h".global _c_int0ST0 .set 0ST1 .set 1.bss TEMP,1.bss SET_F,1.bss F_OMEGA,1.bss OMEGA,1.bss SET_V,1.bss MAX_V,1.bss T_SAMPLE,1.bss THETA_H,1.bss THETA_L,1.bss THETA_R,1.bss THETA_M,1.bss THETA_I,1.bss SS,1.bss SC,1.bss SIN_INDX,1.bss SIN_ENTRY,1.bss SIN_END,1.bss SIN_THETA,1.bss COS_THETA,1.bss UA,1.bss UB,1.bss THETA_S,1.bss SECTOR,1.bss THETA_90,1.bss THETA_180,1.bss THETA_270,1.bss THETA_360,1.bss DEC_MS,24.bss T1_PERIODS,1.bss CMP_1,1.bss CMP_2,1.bss SVPA T,1.bss ACCH,1.bss ACCL,1.bss AR0_SA VE,1.bss P_HI,1.bss P_LO,1ST0_SA VE .usect ".context",1ST1_SA VE .usect ".context",1.sect ".vectors" RESET B _c_int0INT1 B PHANTOMINT2 B _C_INT2 5 INT3 B PHANTOMINT4 B PHANTOMINT5 B PHANTOMINT6 B PHANTOM RESERVED B PHANTOM SW_INT8 B PHANTOM SW_INT9 B PHANTOM SW_INT10 B PHANTOM SW_INT11 B PHANTOM SW_INT12 B PHANTOM SW_INT13 B PHANTOM SW_INT14 B PHANTOM SW_INT15 B PHANTOM SW_INT16 B PHANTOM TRAP B PHANTOM NMI B PHANTOM EMU_TRAP B PHANTOM SW_INT20 B PHANTOM SW_INT21 B PHANTOM SW_INT22 B PHANTOM SW_INT23 B PHANTOM SW_INT24 B PHANTOM SW_INT25 B PHANTOM SW_INT26 B PHANTOM SW_INT27 B PHANTOM SW_INT28 B PHANTOM SW_INT29 B PHANTOM SW_INT30 B PHANTOM SW_INT31 B PHANTOM.sect ".pvecs" PVECTORS B PHANTOMB PHANTOMB PHANTOMB PHANTOMB PHANTOMB PHANTOMB PHANTOMB PHANTOMB PHANTOMB PHANTOMB PHANTOM B PHANTOM B PHANTOM B PHANTOM B PHANTOM B PHANTOM B PHANTOM B PHANTOM B PHANTOM B PHANTOM B PHANTOM B PHANTOM B PHANTOM B PHANTOM B PHANTOM B PHANTOM B PHANTOM B PHANTOM B PHANTOM B PHANTOM B PHANTOM B PHANTOM B PHANTOM B PHANTOM B PHANTOM B PHANTOM B PHANTOM B PHANTOM B PHANTOM B PHANTOM B T1UF_ISR B PHANTOM B PHANTOM B PHANTOM B PHANTOM B PHANTOM B PHANTOM B PHANTOM B PHANTOM B PHANTOM B PHANTOM B PHANTOM B PHANTOMB PHANTOMB PHANTOMB PHANTOMB PHANTOMB PHANTOMB PHANTOMB PHANTOMB PHANTOMB PHANTOMB PHANTOMB PHANTOM.text_c_int0SETC INTMCLRC CNFLDP #224SPLK #0000001000000100B,SCSR1SPLK #68H,WDCRLDP #225LACC MCRAOR #0FC0HSACL MCRALDP #0SPLK #0FFH,IFRSPLK #00000010B,IMRLDP #232SPLK #0FFFH,EV AIFRASPLK #0FH,EV AIFRBSPLK #0FH,EV AIFRCSPLK #0200H,EV AIMRASPLK #0,EV AIMRBSPLK #0,EV AIMRCSPLK #500,T1PRSPLK #500,CMPR1SPLK #500,CMPR2SPLK #500,CMPR3SPLK #0000011001100110B,ACTRASPLK #01F4H,DBTCONASPLK #1001001000000000B,COMCONASPLK #1000100000000010B,T1CONLDP #6SPLK #0347H,T_SAMPLESPLK #16000,T1_PERIODSSPLK #11585,MAX_VSPLK #0,SET_FSPLK #10053,F_OMEGASPLK #0,THETA_LSPLK #0,THETA_HLAR AR0,#THETA_90LAR AR1,#(28-1)LACC #ANGLES_LARP AR0INIT_TBLTBLR *+,AR1ADD #1BANZ INIT_TBL,AR0SPLK #29335,THETA_ISPLK #31291,THETA_SSPLK #SIN_ENTRY_,SIN_ENTRYSPLK #(SIN_ENTRY_+90),SIN_ENDLDP #232SPLK #0000100001000010B,T1CONCLRC INTMMAIN_LOOPLDP #6LT SET_FMPYU F_OMEGAPACSACH OMEGALT SET_FMPYU MAX_VPACSACH SET_VB MAIN_LOOPPHANTOMCLRC INTMRET_C_INT2SST #ST0,ST0_SA VESST #ST1,ST1_SA VELDP #6SACH ACCHSACL ACCLSPH P_HISPL P_LOMPY #1SPL T_SA VESAR AR0,AR0_SA VECLRC SXMLDP #224LACC PIVRSUB #029HBCND T1UF_ISR,EQ RESTLDP #6LAR AR0, AR0_SA VELT P_LOMPY #1LPH P_HILT T_SA VELACC ACCH,16ADDS ACCLLDP #0LST #ST1,ST1_SA VELST #ST0,ST0_SA VECLRC INTMRETT1UF_ISRLDP #232SPLK #0FFFH,EV AIFRALDP #6LT OMEGAMPY T_SAMPLEPACSFRADD THETA_H,16ADDS THETA_LSACH THETA_HSACL THETA_LBCND CHK_UPLIM,GEQADD THETA_360,16SACH THETA_HB RND_THETA CHK_UPLIMSUB THETA_360,16BCND REST_THETA,LEQSACH THETA_HB RND_THETA REST_THETAADD THETA_360,16RND_THETAADD #1,15SACH THETA_RLACC #1SACL SSSACL SCLACC THETA_RSACL THETA_MSUB THETA_90 ;BCND E_Q,LEQSPLK #-1,SCLACC THETA_180SUB THETA_RSACL THETA_MBCND E_Q,GEQSPLK #-1,SSLACC THETA_RSUB THETA_180SACL THETA_MLACC THETA_270SUB THETA_RBCND E_Q,GEQSPLK #1,SCLACC THETA_360SUB THETA_RSACL THETA_ME_QLT THETA_MMPYU THETA_IPACSACH SIN_INDXLACC SIN_INDX,11SACH SIN_INDXLACC SIN_ENTRYADD SIN_INDXTBLR SIN_THETALACC SIN_ENDSUB SIN_INDX ;TBLR COS_THETA ;LT SSMPY SIN_THETAPACSACL SIN_THETALT SCMPY COS_THETAPACSACL COS_THETALT SET_VMPY COS_THETAPACSACH UAMPY SIN_THETAPACSACH UBLT THETA_RMPY THETA_SPACSACH SECTORLACC SECTOR,5SACH SECTORLACC #DEC_MSADD SECTOR,2SACL TEMPLAR AR0,TEMPLT UAMPY *+PACLT UBMPY *+APACBCND CMP1BIG0,GEQLACC #0CMP1BIG0SACH TEMPLT TEMPMPY T1_PERIODSPACADD #1,15SACH CMP_1,1LT UAMPY *+PACLT UBMPY *+APACBCND CMP2BIG0,GEQLACC #0CMP2BIG0SACH TEMPLT TEMPMPY T1_PERIODSPACADD #1,15SACH CMP_2,1LACC #CCKWISE_ADD SECTORTBLR SVPATLAR AR0,#ACTRALACC *AND #0FFFHOR SVPATSACL *LAR AR0,#CMPR1LACC CMP_1SACL *+ADD CMP_2SACL *SUB #500BCND IN_LMT,LEQSPLK #500,*IN_LMTB REST.dataANGLES_.word 01922H.word 03244H.word 04B66H.word 06488HDEC_MS .word 20066.word –11585.word 0.word 23170.word 20066.word 11585.word -20066.word 11585.word 0.word 23170.word -20066.word -11585.word -20066.word 11585.word 0.word -23170.word -20066.word -11585.word 20066.word -11585.word 0.word -23170.word 20066.word 11585CCKWISE_.word 0001000000000000B.word 0011000000000000B.word 0010000000000000B.word 0110000000000000B.word 0100000000000000B.word 0101000000000000BSIN_ENTRY_.word 0.word 286,572,857,1143,1428.word 1713,1997,2280,2563,2845.word 3126,3406,3686,3964,4240.word 4516,4790,5063,5334,5604.word 5872,6138,6402,6664,6924.word 7182,7438,7692,7943,8192.word 8438,8682,8923,9162,9397.word 9630,9860,10087,10311,10531.word 10749,10963,11174,11381,11585.word 11786,11982,12176,12365,12551.word 12733,12911,13085,13255,13421.word 13583,13741,13894,14044,14189.word 14330,14466,14598,14726,14849.word 14968,15082,15191,15296,15396.word 15491,15582,15668,15749,15826.word 15897,15964,16026,16083,16135.word 16182,16225,16262,16294,16322.word 16344,16362,16374,16382,16384.end。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
电机控制DSP编程
软件的层次化:
高级语言框架:仿真和实现可用同样的代码
(若程序不很复杂以及熟悉汇编,用宏汇编建立框架也可):
通用算法模块:模块封装,接口清晰
专用算法模块:PWM,电机模型
硬件环境封装:中断,端口,控制寄存器,存储器地址。
端口操作封装便于移植和检查。
不要直接对外设寄存器进行读写,最好建立接口程序,使算法部分与硬件独立。
中断结构:
主循环和中断的方式就是多线程的程序。
中断有定时中断、故障中断和通讯中断等。
可以多个定时中断实现多采样率的控制。
将复位看成是一种不可屏蔽中断,每次复位后指令都从同一地点开始执行,但是运行环境会有所不同,完全掉电除外。
后台计算,前台计算:有的模块可能只需具备几个部分,但是可以这样看。
后台程序可以用高级语言编写,前台程序用汇编语言编写。
注意:不要使前台程序时间超出,要以可能最长的执行时间计算,而且还要给后台程序留有一定的时间。
代码组织:
将每个程序模块都分为几个部分:常量定义,变量定义,初始化,执行部分(前台部分和后台部分)。
有条理的代码组织使得程序维护:填加模块、删除模块、修改模块变得有按部就班有法可依。
程序描述方法
流程图:另外一种语言的程序实现
结构图:框图为执行的操作,连线为传递的变量参数。
程序实现容易。
状态机:连线为状态转换的方向并标注转换条件,框图为可能的几种状态。
结构图举例:滞洄比较器,开方器,多路开关
Ctrl
存储器的规划:
数据存储器:两种片内RAM ,SARAM ,DARAM ,可以同时读写。
外部存储器一般要
加等待周期。
临时变量:scratch registers ,特点,在一个线程开始其值不固定,退出时也不固定。
全局变量:虽然不建议使用,但在电机控制程序中,可以在程序模块保持独立的情况下采用少量全局变量:输出电流,电压,给定转速等。
局部专用变量:可以保留中间计算结果或预先计算的结果,还可以用于调试。
永久全局变量:初始化一次,以后只读,方便编程。
常数和表头。
临时变量和专用变量的权衡:空间和时间存在矛盾,资源占用和调试方便的矛盾。
采用临时变量可以少占用数据存储器,用专用变量可以保留中间计算结果或预先计算的结果,可以节省时间。
数据存储器容量的计算:临时变量,全局变量和专用变量,还可能有堆栈变量。
如果数据存储器紧张,可以考虑数据区特殊的利用方法。
存储器的安置:常用的变量如临时变量,固定全局变量放在最快DARAM 中。
可重入性和可重用性:
可重入性的方法:只修改堆栈变量和专用临时变量;对全局变量和静态变量只读;不用自修改代码的程序。
子程序不必设计成带堆栈的可重入形式,可以为不同的程序端(主程序,中断)编制多个相同的子程序,针对性的子程序。
这也不占用很多空间。
而且省下了重入保护现场的时间和程序空间。
在C语言中为程序模块建立一个struct,其成员为传递的参数(输入及输出)。
可以使调用更加简洁。
用指针引用,可以重入。
TI不推荐使用“hard-coded” data memory locations,要求可重定位,实际上,我们通过辅助编辑软件可以协助重定位,防止遗漏和重复。
我们利用固定位置的变量和SCOPE进行在线调试。
宏的采用可以帮助我们解决重定位的问题。
F2xx系列的特殊考虑:如果采用直接寻址,有DP切换的问题,可以在常用DP设置专门的临时变量。
另外还有DP自适应和DP切换的方法。
程序的可重定位并可固化(ROM-able)特性:主要取决于直接寻址的DP的处理。
可以让程序模块中的变量用一个DP的变量,并且在调用前设置正确的DP.
没有用C的原因:
熟悉了汇编
汇编的常用指令也没有几条
程序结构不复杂
总是要和硬件打交道
编程效率高
位操作和定点运算为主
汇编有宏和子程序的使用也可以避免错误
编程标准
建立编程标准的好处:便于合作者代码集成;编写的模块具有通用性;便于调试;可以只对客户提供二进制文件;可以部分编译。
目前的标准:目前虽然没有统一的标准,但是我们要了解编程方面好的常规,在没有按照常规的方法要将接口描写清楚,站在前人的基础上。
命名的标准:
编程前先想好变量定义和程序实现所需的各个模块的名称和接口定义。
模块的命名:“<module>_<vendor>_ [A-Z0-9]”。
类似地,前后缀的约定和规范应该提前明确规定。
模块内部变量的名字的定义并不重要,关键是接口用到的那些外部变量和符号。
为便于名称意义的判断,在组合词中间不要加“_”,例如currentScale中间,最好用大写开头。
命名规则举例:
变量全部小写(前缀后缀除外);
常量全部大写(或加后缀“_k”);
定标值皆加后缀“_sh”
一个32位变量低字“_l”
IO空间地址的定义后面都加“_p”
编译器的利用
宏和子程序的选择和相互使用:宏可以简化子程序参数传递的形式,各子程序的相同部分可以用宏来实现。
常量的计算:DP值(ldp #uab<<7)
宏的使用:简单而实用的宏:sdp,SBIT0,SBIT1等
文件的包含要注意用条件编译防止多次包含的发生。
程序的优化
1)程序优化和可维护性的权衡。
举例说:一个程序将执行时间优化10%很难,而CPU的
时钟速度增加的很快。
程序的优化可以使程序变得难读,接口变得复杂,模块化会被打乱。
2)时间和空间的矛盾:
3)程序的优化的原则:程序太精简使可维护性变差,优化可以但是要单向进行即调试版本
和发行版本;建议模块优化。
4)执行时间优化方法:sdp的使用提高可重用性;顺序执行保证流水线连续;如何利用
复合指令优化程序(LTP,LTS和LTA,APAC和SPAC,MPYS和MPYA)。
5)运行环境:在中断中一般要保护现场,保证中断退出时对现场还原,但是在中断进入时
的需要的环境要进行设置。
有一个办法可以节省时间,按照常用的环境建立一个默认的环境,在主程序和每个中断开始设置这一环境,每个程序模块需要的环境与此不同时进行设置,模块退出时还原,这样在每个功能模块中就可以认为在这个默认的环境中,由于大多数模块使用默认环境,不需重新设置。
例如:PM,OVM,SXM在缺省状态下都是
1。
6)溢出防止:在用sach dma,shift实现右移(先左移shift位),SXM不起作用,而且左移
过程中OVM也不起作用。
因此要注意溢出发生。
7)饱和抑制:
setc ovm
lacc reference,16
sub feedback,16
sach error
具有饱和限制的左移: norm * ;
8)所有变量的初始状态为0,除非特别加以赋值,初始化变得简单,没有初始化程序会缺乏
可重复性。
9)四舌五入:F240定点运行的截尾误差对于正负数都是产生一个负向的偏移,而不是绝
对值的减小。
主要用于除法和结尾。