新會員
Joined
: 2009/6/23 12:52 最後登入時間
: 2009/6/23 18:26
Group:
註冊會員
Level : 1 HP : 0 / 0 MP : 0 / 0 EXP : 0
|
最近在做无刷电机控制器,用了PIC30f2010,也参考了官方的例子。开机运行时要慢慢启转动的,我是控制PDC的值,先为0再一直加到512。这个功能是行的,但是在过流时要把占空比调小,就是转速要慢,让它电流值在那个范围内,我是这样做的,一当有电流超过设定值就减一个PDC的值,电流如果没有到设定值就加一个PDC值,可到那运行时却变成调时基了,PDC的值为512时是没有时基的,511就有一点点时基了,时基频率是设定值,但脉冲很窄。这个值越小时基就越多,PDC明明是调占空比的怎么会变成调时基(PWM)去了呢?程序如下:写的有点乱,也比较笨,别见笑!
//---------------------------------------------------------------------- //**************** 振荡器 ************************************ //#define dFoscExt 5 000 000 // 外部晶振或时钟频率(Hz) //#define dPLL 8 // PLL 比率 //#define dLoopTimeInSec 0.00005 // PWM 周期 - 100 uS, 10Khz PWM //#define dDeadTimeSec 0.000 002 // 以秒为单位的死区时间 // Derived //#define dFosc (5 000 000*8) // 时钟频率(Hz) //#define dFcy (40 000 000/4) // 指令频率(Hz) //#define dTcy (1.0/10 000 000) // 指令周期(s)=0.000 000 1s=100ns //#define dDeadTime (int)(0.000 002*10 000 000) // 以dTcys 为单位的死区时间=0.000 000 000 0002 //#define dLoopInTcy (dLoopTimeInSec/dTcy) // 以Tcy 为单位的基本循环周期 //#define dDispLoopTime 0.100 // 显示和按钮状态查询循环
#include "p30F2010.h" _FOSC(0x0c306); //XT振荡,4倍频晶振. _FWDT(WDT_OFF); //关闭看门狗定时器 _FBORPOR(PBOR_OFF & MCLR_EN); //掉电复位禁止,MCLR复位使能。 _FGS(CODE_PROT_OFF); //代码保护禁止 #define FCY 10000000// xtal = 5.0Mhz ; PLLx8 #define MILLISEC FCY/10000// 1 mS 延迟常数 #define FPWM 39000 #define S2 LATCbits.LATC14 #define EN2 LATCbits.LATC13 #define RS2 LATDbits.LATD0 //15 #define HC595_SCLK LATDbits.LATD1 //14 #define HC595_SID LATFbits.LATF2 //18 #define HC595_enable LATFbits.LATF3 //17
void InitTMR3(void); void InitADC10(void); void AverageADC(void); void DelayNmSec(unsigned int N); void InitMCPWM(void); void CalculateDC(void); void GetSpeed(void); struct { unsigned RunMotor : 1; unsigned Minus : 1; unsigned xsbit : 1; //0.5S显示一次 unsigned Ysbit : 1; //电机刚起的时候的延时 unsigned tdybit : 1; //太低压了保护 unsigned dzbit : 1; //堵转的位 unsigned dlbit : 1; //短路的位 unsigned zfbit : 1; //正反转的位 unsigned addybit : 1; //ad低压 unsigned adglbit : 1; //ad过流 unsigned unused : 6; } Flags; unsigned int HallValue; unsigned int Ys; //刚开电机时电压和电流不检测延时一下 unsigned int ysmoto,glms; //慢起动 unsigned int glys; unsigned int gl[10]; unsigned int dy[10]; unsigned int dybit; unsigned int glbit; unsigned int i,j,k,l,m; unsigned long glnum,dynum; unsigned long vmoto,zsmoto,zsbit,zsjs; unsigned char ZF[ ]={'U',':','.','V',' ','B','A','T','%','R','P','M'}; unsigned int test; unsigned int dzbf,dzbfjs; //堵转保护 unsigned int ADAD,state; /************************************************************/ void delay(unsigned int i) { while(i--); } /************************************************************* 以下是低端驱动器表。在此StateLoTable 中, 在低端驱动器施加PWM 信号,而高端驱动器为“导通”或“截止”状态。 在本练习中使用此表。 *************************************************************/ unsigned int StateLoTable[] = {0x0000, 0x02001, 0x0810, 0x0801,0x0204, 0x2004, 0x0210, 0x0000}; /**************************************************************** 以下是变化通知引脚CN5、CN6 和CN7 的中断向量。 当霍尔传感器改变状态时,将引起中断,指令执行将转到下面的子程序。 然后用户必须读端口B 的第3 位、第4 位和第5 位, 对读到的值进行移位和调节以使之读作1、2……6。 然后将调整后的值用作查找表StateLoTable 中的偏移量 以确定装入OVDCON 寄存器的值。 *****************************************************************/ void _ISR _CNInterrupt(void) { IFS0bits.CNIF = 0; // 清零标志 HallValue = PORTB & 0x0038; // 屏蔽其它位,保留RB3、RB4 和RB5 HallValue = HallValue >> 3; // 执行3 次右移 OVDCON = StateLoTable[HallValue]; zsbit++; dzbf=0; //if(HallValue==1) //S2=!S2; } /********************************************************************* ADC 中断用给定的电位计值装载PDCx 寄存器。 仅在电机运行时执行此操作。 *********************************************************************/ void _ISR _ADCInterrupt(void) { IFS0bits.ADIF = 0; if (Flags.RunMotor) { //ADAD=ADCBUF0>>1; //ADAD=ADAD-ysmoto-glms; //PDC1 = ADAD; // 赋值……>>等于/2笨蛋 //PDC2 = PDC1; // 并装载所有的三个PWM…… //PDC3 = PDC1; // 占空比寄存器
if(Flags.addybit==0){ dy[dybit]=ADCBUF1; if(dybit++>=8) { dybit=0;Flags.addybit=1; } }
if(Flags.adglbit==0){ gl[glbit]=ADCBUF2; if(glbit++>=8) { glbit=0;Flags.adglbit=1; } } } } /********************************************************************* 外中断,下降沿触发,短路保护 *********************************************************************/ void _ISR _INT0Interrupt(void) { IFS0bits.INT0IF = 0; //PWMCON1 = 0x0700; // 禁止PWM 输出 //OVDCON = 0x0000; // 将PWM 改写为低电平 Flags.RunMotor = 0; // 复位运行标志 Flags.dlbit=1; } /*******************************************************************************************************************/ //发送一个字节(底层函数) void lcm_w_byte(unsigned char bbyte) { unsigned char i; for(i=0;i<8;i++){ HC595_SID=bbyte&0x01; //取出最高位 DelayNmSec(1); HC595_SCLK=0; DelayNmSec(1); HC595_SCLK=1; bbyte>>=1; //左移 HC595_enable=0; DelayNmSec(1); HC595_enable=1; DelayNmSec(1); } } /*************************************/ void enable2(void) { EN2=1; //E=1; DelayNmSec(1); //当系统时钟为1MHz时,延时ns EN2=0; //E=0; } /****************************指令*****************************/ void writecmd2(unsigned char command) { unsigned char command_temp; command_temp = command; RS2=0; //命令; DelayNmSec(1); lcm_w_byte(command_temp); enable2(); DelayNmSec(1); RS2=1; } /*******************************数据*******************************/ void writedata2(unsigned char Adata) { unsigned char data_temp; data_temp = Adata; EN2=0; DelayNmSec(1); RS2=1; DelayNmSec(12); lcm_w_byte(data_temp); DelayNmSec(1); enable2(); }
/*****************************位址*****************************/ void setcoordinate2(unsigned char x,unsigned char y) { unsigned char address; if (y == 0) address = 0x80 + x; else address = 0xc0 + x; writecmd2(address); } /**************************送字符**************************/ void writestring2(unsigned char X,unsigned char Y,unsigned char s) { setcoordinate2(X, Y); writedata2(s); } /******************************************************/ void initLCD2(void) { writecmd2(0x38); //4bit 只要改成38就为8位 DelayNmSec(2); enable2(); DelayNmSec(2); writecmd2(0x38); //4bit DelayNmSec(2); writecmd2(0x0c); //显示开 DelayNmSec(2); writecmd2(0x01); //显示清屏 DelayNmSec(2); writecmd2(0x06); //显示光标移动设置 DelayNmSec(2); } /************************************************************************ **Tmr1初始化函数 *************************************************************************/ void InitTMR1(void) { T1CON=0x0010; // internal Tcy/256 clock TMR1=0xcf2c; PR1=0xffff; //16 16*62=1ms IFS0bits.T1IF=0; //清除TMR1的中断标志 IEC0bits.T1IE=1; //使能中断 } /*******************计算电压和电流***********************/ void js(void) { unsigned char i; if (Flags.RunMotor) { if(Flags.addybit){ for(i=0;i<8;i++) dynum+=dy[i]; dynum=dynum>>3; vmoto=dynum; if(dynum<372) //低压 { Flags.RunMotor = 0; // 复位运行标志 Flags.tdybit=1; } //如果电压小于20V的话继电器关 dynum=0; Flags.addybit=0; }
if(Flags.adglbit){ for(i=0;i<8;i++) glnum+=gl[i]; glnum=glnum>>3; if(glnum>=512) //抗干拢一下明天再试--10=4ms { glms--; if(glms<20) glms=20; //电流到这时转速就慢下来, } else { glms++; if(glms>510) glms=510; } glnum=0; test = glms; PDC1 = glms; // 赋值------------------------这就是处理电流后去给PDC寄存器的值,可是却变成调时基了(39K的载波) PDC2 = glms; // 并装载所有的三个PWM…… PDC3 = glms; Flags.adglbit=0; } } } /******************************************************** **定时器1中断服务 *********************************************************/ //void __attribute__((__interrupt__)) _T1Interrupt(void) void _ISR _T1Interrupt(void) { TMR1=0xcf2c; IFS0bits.T1IF = 0; //清定时器中断标志 if (Flags.RunMotor) //10ms { zsjs++; if(zsjs>=50) { S2=!S2;js(); Flags.xsbit=1; zsjs=0; zsmoto=zsbit*60/24*2; zsbit=0; }
dzbf++; if(dzbf>=200) //如果超过两秒就认为堵转 { dzbf=0; Flags.dzbit=1; Flags.RunMotor=0; } } } /*******************************************************************************************************************/ int main(void) { TRISC = 0x9FFF; TRISD = 0x0000; TRISF = 0x0000; LATD = 0xFFFF; LATF = 0xFFFF; LATE = 0x0000; TRISE = 0xFFC0; // 设置为输出PWM 信号 CNEN1 = 0x00E0; // 使能CN5、CN6 和CN7 CNPU1 = 0x00E0; // 使能内部上拉 InitMCPWM(); InitADC10(); initLCD2(); InitTMR1(); INTCON2bits.INT0EP = 1; IFS0bits.INT0IF = 0; IEC0bits.INT0IE = 1; IFS0bits.CNIF = 0; // 清零CNIF IEC0bits.CNIE = 1; // 允许CN 中断 glms=511; //Flags.Ysbit = 0; //Flags.gjbit = 1; //DelayNmSec(1000); S2=1; //打开继电器 DelayNmSec(1000); //3s //DelayNmSec(100); //100ms // 在PORTB 上读霍尔位置传感器 HallValue = PORTB & 0x0038; // 屏蔽其它位,保留RB3、RB4 和RB5 HallValue = HallValue >> 3; // 右移以获得值1、2……6 OVDCON = StateLoTable[HallValue]; // 装载改写控制寄存器 PWMCON1 = 0x0777; // 使能PWM 输出 T1CONbits.TON = 1; for(ysmoto=0;ysmoto<510;ysmoto++) { DelayNmSec(2); PDC1 = ysmoto; // 赋值------------------开机慢慢启动,这是行的,可以改变速度。 PDC2 = PDC1; // 并装载所有的三个PWM…… PDC3 = PDC1; } ysmoto=0x0000; Flags.RunMotor = 1; // 将标志置1
/*****************/ while(1) { DelayNmSec(100); while (Flags.RunMotor) // 当电机运行时 { //if(Flags.xsbit){ //Flags.xsbit=0; test=vmoto; //vmoto=vmoto*550/1024; writestring2(0,0,ZF[0]); writestring2(1,0,ZF[1]); writedata2((vmoto*550/1024/100)%10+48); writedata2((vmoto*550/1024/10)%10+48); writestring2(4,0,ZF[2]); writedata2((vmoto*550/1024%10)+48); writestring2(6,0,ZF[3]); writestring2(7,0,ZF[4]); writestring2(8,0,ZF[5]); writestring2(9,0,ZF[6]); writestring2(10,0,ZF[7]); writestring2(11,0,ZF[7]); writestring2(12,0,ZF[1]); writedata2(((vmoto*550/1024*10)/360)%10+48); writedata2(((vmoto*550/1024*10)/36)%10+48); writestring2(15,0,ZF[8]); writestring2(0,1,ZF[9]); writestring2(1,1,ZF[10]); writestring2(2,1,ZF[11]); writestring2(3,1,ZF[1]); writedata2((zsmoto/1000)%10+48); writedata2((zsmoto/100)%10+48); writedata2((zsmoto/10)%10+48); writedata2(zsmoto%10+48); //DelayNmSec(200); //writestring2(8,1,ZF[4]);//AD //writedata2((test/1000)%10+48); writedata2((test/100)%10+48); writedata2((test/10)%10+48); writedata2(test%10+48); //writedata2((zsbit/1000)%10+48); //writedata2((zsbit/100)%10+48); //writedata2((zsbit/10)%10+48); //writedata2(zsbit%10+48);*/ //} } if((Flags.dlbit==1)||(Flags.dzbit==1))//如果短路堵转 { PWMCON1 = 0x0700; // 禁止PWM 输出 OVDCON = 0x0000; // 将PWM 改写为低电平 DelayNmSec(1); //while(l<30) //{ while(k<1200)//k为展开速度 { for(j=0;j<25;j++) { if(j<=i) LATE=0xffea; else LATE=0xffc0; }k++; }i++;k=0; //if(i>=20) {i=20;l++;} //} //这里要显示什么故障 state=0; while(1){ //死循环 if(Flags.dlbit) state=1; if(Flags.dzbit) state=2; //if(Flags.dlbit) state=3; //if(Flags.dlbit) state=4; writestring2(13,1,state%10+48); } } } } /******************************************************************* 以下代码用于设置ADC 寄存器,该代码可实现下列功能: 1. 1 个通道转换( 本例中,该通道为RB2/AN2) 2. PWM 触发信号启动转换 3. 电位计连接到CH0 和RB2 4. 手动停止采样和启动转换 5. 手动检查转换完成 *********************************************************************/ void InitADC10(void) { ADPCFG = 0xFFF8; // 将端口B 的RB0 到RB2 配置为模拟引脚;将其它引脚配置为数字引脚 ADCON1 = 0x0064; // PWM 启动转换 ADCON2 = 0x0208; // 同时采样4 个通道 ADCHS = 0x0002; // 将RB2/AN2 作为CH0 连接到电位计…… // ch1 连接母线电压、Ch2 连接电机, Ch3 连接电位计 ADCON3 = 0x0080; // Tad 来源于内部RC (4uS) IFS0bits.ADIF = 0; IEC0bits.ADIE = 1; ADCON1bits.ADON = 1; // 启动ADC } /******************************************************************** InitMCPWM,对PWM 做以下初始化: 1. FPWM = 39000 hz 2. 独立的PWM 3. 使用OVDCON 控制输出 4. 用从电位计读取的ADC 值设置占空比 5. 将ADC 设置为由PWM 特殊触发信号触发 *********************************************************************/ void InitMCPWM(void) { PTPER = FCY/FPWM - 1; PWMCON1 = 0x0700; // 禁止PWM OVDCON = 0x0000; // 允许使用OVD 控制 PDC1 = 100; // 将PWM1、PWM2 和PWM3 初始化为100 PDC2 = 100; PDC3 = 100; //SEVTCMP = PTPER; PWMCON2 = 0x0F00; // 后分频比设为1:16 PTCON = 0x8000; // 启动PWM } //--------------------------------------------------------------------- // 这是普通的1 ms 延迟程序,用于提供1 mS 到65.5 秒的延迟。 // 如果N = 1,则延迟为1 mS ;如果N = 65535,则延迟为65,535 mS。 // 注意FCY 用于计算。 // 请根据上述定义语句做出必要的更改(PLLx4 或PLLx8 等) // 以计算出正确的FCY。 void DelayNmSec(unsigned int N) { unsigned int j; while(N--) for(j=0;j < MILLISEC;j++); }
發表於: 2009/6/23 18:29
|