系统控制代码
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
/*
不知道你是要写转速闭环控制程序还是功能测试时计算转速。
如测试时计算转速:
1.可用转速计直接测试
2.可用示波器看反电动势波形的频率进行计算。频率/极对数*60即为每分钟转速。
如是写转速闭环控制程序,这个比较复杂。分为加速/减速/匀速三种情况来写。
一般为连续换12次相/2即为一圈时间(以三相六极匀速为例)。
转速=60*F/磁极,F为电源频率,一般为50,磁极SN为1
3.?同步电动机的转速=60*频率/?极对数(我国工频为50Hz)
异步电动机转速=(60*频率/?极对数)×转差率
电机转速与频率的公式?n=60f/p?上式中?
n——电机的转速(转/分);?60——每分钟(秒);?f——电源频率(赫芝);?p——电机旋转磁场的极对数。?
*/
/*
由于一个特定的优先级可被分配给多个中断请求源,因此提供了一种解决给定用户分配优先级内优先级冲突的方法。
每个中断源都有一个自然顺序优先级,这由其在IVT中的位置决定。表28-2给出了每个中断源在IVT中的位置。中断向量的编号越低
,其自然优先级越高,而向量的编号越高,其自然优先级越低。任何待处理的中断源的总优先级都
首先由该中断源在中断优先级控制(IPCx)寄存器中的用户分配优先级决定,然后由IVT中的自然顺序优先级决定。
自然顺序优先级仅用于解决具有相同用户分配优先级而同时等待处理的中断之间的冲突。一旦解决了优先级冲突,
异常处理过程就开始了,CPU只能被具有更高用户分配优先级的中断源中断。在异常处理过程开始后才等待处理的中断,
如果它与当前正在处理的中断具有相同的用户分配优先级,即便具有较高的自然顺序优先级,
也将保持待处理状态直到当前的异常处理过程结束
*/
#define __dsPIC30F2010__
#include
#define FCY 10000000
/*FCY为指令周期时钟频率 xtal = 5.0Mhz; PLLx8
板子上接的晶振为5MHz,5MHz/4*8=10^7;
*/
#define MILLISEC FCY/10000 // 1 mSec delay constant
#define FPWM 39000
#define POLEPAIRS 2 // 转子的极对数;number of pole pairs of the motor
#define INDEX 1 // 转子的起始位置;Hall sensor position index
#define S2 !PORTCbits.RC14
#define S3 !PORTCbits.RC13
#define CR 0x0D
#define LF 0x0A
#define BAUD 19200
#define SPEEDMULT 2343750 // 10^7*60/256=2343750;factor used to calculate speed
#define OFFSET 8 // offset in InData to load speed values
#define POTMULT 4 //电位器的AD转换系数: pot to speed ratio
#define Kps 750 // Kp and Ks terms need to be adjusted as per
#define Kis 20 // the motor and load
void InitADC10(void);
void AverageADC(void);
void DelayNmSec(unsigned int N);
void InitMCPWM(void);//电机控制模块初始化
void InitUART(voi
d);
void SendSpeed(void);
void InitTMR3(void);
void SendMsg(void);
void CalculateDC(void);//PID调节函数
struct {
unsigned RunMotor : 1;//电机运行标志
unsigned CalSpeed : 1; //速度调节标志
unsigned CheckRX : 1;
unsigned SendTX : 1;
unsigned unused : 12;
} Flags;
unsigned int HallValue;
unsigned int timer3value;
unsigned int timer3avg;
unsigned char polecount;
unsigned char *TXPtr;
unsigned char *RXPtr;
int DesiredSpeed;
int ActualSpeed;
int SpeedError;
int DutyCycle;
int SpeedIntegral;
int timeout;//TMR2定时器中断溢出的次数,从而达到计时的目的
unsigned char InData[] = {"000000"};
unsigned char OutData[] = {"Speed = 00000 rpm\r\n"};
/*通过看这个OutData这个数组可以看到当在这个数组的第九个字符的时候是要设定的
speed的值。这也是OFFSET=8的原因,
*/
unsigned int StateTable[] = {0x0000, 0x0210, 0x2004, 0x0204,
0x0801, 0x0810, 0x2001, 0x0000};
/*************************************************************
Low side driver table is as below. In the StateLoTableClk,
the Low side driver is a PWM while the high side driver is
either on or off.
*************************************************************/
unsigned int StateLoTableClk[] = {0x0000, 0x0210, 0x2004, 0x0204,
0x0801, 0x0810, 0x2001, 0x0000};
unsigned int StateLoTableAntiClk[] = {0x0000, 0x2001, 0x0810, 0x0801,
0x0204, 0x2004, 0x0210, 0x0000};
/****************************************************************
Interrupt vector for Change Notification CN5, 6 and 7 is as below.
When a Hall sensor changes states, an interrupt will be
caused which will vector to the routine below
The program then reads PORTB, mask bits 3, 4 and 5,
shift and adjust the value to read as 1, 2 ... 6. This
value is then used as an offset in the lookup table StateLoTableClk
or StateLoTableAntiClk to determine the value loaded in the OCDCON
register. This routine also reads the Hall sensors and counts
up to the POLEPAIRS to determine the time for one mechanical
revolution using the fact that 1 mech rev = POLEPAIR*(1 elect. rev)
*****************************************************************/
void __attribute__((__interrupt__)) _CNInterrupt (void)
{
IF = 0; // clear flag
HallValue = PORTB & 0x0038; // mask RB3,4 & 5
HallValue = HallValue >> 3; // shift right 3 times
OVDCON = StateLoTableAntiClk[HallValue];
// The code below is uses TMR3 to calculate the speed of the rotor
if (HallValue == INDEX) // has the same position been sensed?
if (polecount++ == POLEPAIRS) //has one mech rev elasped?
{
/*
这个if条件成立的话,那么电机就转了一圈了。
用TMR3的计数值来计算电机的转速,要满足的条件是:TMR3计数到满值的时间要长于电机转一圈的
时间,即:1/(10^7/256)*65536>电机转一圈的时间===》10^7/256/65536=0.59<转速,故这样
做是可行的。
*/
// yes then read timer 3
timer3value = TMR3;
TMR3 = 0;
timer3avg = ((timer3avg + timer3value) >> 1);//除以二的原因是把老的值和新的值去一个平均值作为新的值
polecount = 1;
}
}
//---------------------------------------------------------------------
// Below are the interrupt vectors for the serial receive and transmit
//---------------------------------------------------------------------
void __attribute__((__interrupt__)) _U1TXInterrupt(void)
{
IFS0bits.U1TXIF = 0; // clear interrupt flag
}
/*
void __attribute__((__interrupt__)) _U1RXInterrupt(void)
{
IFS0bits.U1RXIF = 0; // clear interrupt flag
*RXPtr = U1RXREG;
if(*RXPtr == 'R')//这样的话OutData[] = {"Speed = R00000 rpm\r\n"};应该改成这样
{Flags.CheckRX = 1;RXPtr = &InData[0];}
if(InData[0]=='R') *RXPtr++;
}
*/
void __attribute__((__interrupt__)) _U1RXInterrupt(void)
{
IFS0bits.U1RXIF = 0; // clear interrupt flag
*RXPtr = U1RXREG;
if (*RXPtr == CR)//为什么不是*RXPtr=='0'??????????????????????????????????????????
{Flags.CheckRX = 1;RXPtr = &InData[0];}
else *RXPtr++;
}
/*********************************************************************
The ADC interrupt reads the demand pot value.
*********************************************************************/
void __attribute__((__interrupt__)) _ADCInterrupt (void)
{
IFS0bits.ADIF = 0;
DesiredSpeed = ADCBUF0;
Flags.CalSpeed = 1; // 每次发生AD中断的时候也即需要更新电机的速度了;after every adc read, do a PI calculation
}
/*
void InitTMR2()
{
TMR2=0;
timeout=0;
IEC0bits.T2IE=1;//TMR2定时器中断使能
T2CON=0x0030;//1:256的分频
PR2=39062;
//初始化好了之后便可以对TMR2定时器开启:T2CONbits.TON=1;
}
//TMR2定时器中断
void __attribute__((__interrupt__)) _TMR2Interrupt (void)
{
IFS0bits.T2IF=0;
timeout++;
}
*/
void InitTMR1()
{
TMR1=0;
T1CON=0x0030; //关闭定时器,使用内部时钟,预分频比为1:256
PR1=39062; //1秒
IFS0bits.T1IF=0; //清除中断标志
//IPC0bits.T1IP=7; //中断优先级为7
IEC0bits.T1IE=1; //使能中断
}
void __attribute__((__interrupt)) _T1interrupt(void)
{
TMR1=0;
IFS0bits.T1IF=0; //清除中断标志
timeout=timeout+1;
}
void wait_S2_start()
{
while(1)
{
if(S2)//按键按下去,RC14变为低电平,S2变为1
{
DelayNmSec(10);//去抖
if(S2)
{
while(S2);//松手等待
break;//跳出while(1)
}
}
}
}
int main(void)
{
LATE = 0x0000;//设置E口的各位为低电平
TRISE = 0xFFC0; // PWMs(RE0~RE5) are outputs
CNEN1 = 0x00E0; // CN5,6 and 7 enabled
CNPU1 = 0x00E0; // enable internal pullups,因为J19接上的话就是有上拉啦
IF = 0; // clear CNIF
IE = 1; // enable CN interr
upt
InitMCPWM();
InitADC10();
InitUART();
InitTMR3();
InitTMR1();//初始化TMR1定时器
timer3avg = 0;
//wait_S2_start();
while(1)
{
T1CONbits.TON=1;//开启TMR1定时器
Flags.RunMotor = 1; // set running flag
while (Flags.RunMotor&&timeout<30)// while motor is running 而且定了30S的定时
{
// read hall position sensors on PORTB
HallValue = PORTB & 0x0038; // mask RB3,4 & 5
HallValue = HallValue >> 3; // shift right to get value = 1, 2 ... 6
OVDCON = StateLoTableAntiClk[HallValue]; //电机逆时钟转动 Load the overide control register
PWMCON1 = 0x0777; // enable PWM outputs
T3CONbits.TON = 1; // start tmr3
polecount = 1;
DelayNmSec(100);
if (S2) // if S2 is pressed, stop
{
DelayNmSec(10);//按键去抖
if(S2)//如果S2真的被按下去了,那么就要停止电机的转动
{
PWMCON1 = 0x0700; // disable PWM outputs
OVDCON = 0x0000; // overide PWM low.
Flags.RunMotor = 0; // reset run flag
while (S2) // wait for key release
DelayNmSec(10);
}
}
if (Flags.CalSpeed) // if calculate flag set then// Flags_CalSpeed=1只会出现在AD中断中
{
SendSpeed(); // send present speed serially
CalculateDC(); // calculate new dutycycle using PI
Flags.CalSpeed = 0; // 计算本次的速度之后也即更新了电机速度设定值了,及时即使清除这个标志位clear flag
}
}
if(Flags.RunMotor==0)
{
wait_S2_start();
continue;//也即当等待开启电机按钮的时候,下面的语句不执行了,直接从头开始while(1)大语句开始执行
}
timeout=0;
TMR1=0;//TMR1初值
T3CONbits.TON = 0; //TMR3计数器关掉
T1CONbits.TON=1;//TMR1定时器开启
//电机暂停30s
PWMCON1 = 0x0700; // disable PWM outputs
OVDCON = 0x0000; // overide PWM low.
Flags.RunMotor = 0; // reset run flag
while(timeout<30);//等待30s
//开始正转30s
timeout=0;
TMR1=0;//
T1CONbits.TON=1;
Flags.RunMotor = 1; // set running flag
while (Flags.RunMotor&&timeout<30) // while motor is running
{
HallValue = PORTB & 0x0038; // mask RB3,4 & 5
HallValue = HallValue >> 3; // shift right to get value = 1, 2 ... 6
OVDCON = StateLoTableClk[HallValue]; // 电机顺时钟转动Load the overide control register
PWMCON1 = 0x0777; // enable PWM outputs
T3CONbits.TON = 1;
if (S2) // if S2 is pressed, stop
{
DelayNmSec(10);//按键去抖
if(S2)
{
PWMCON1 = 0x0700; // disable PWM outputs
OVDCON = 0x0000; // overide PWM low.
Flags.RunMotor = 0; // reset run flag
while (S2) // wait for key release
DelayNmSec(10);
}
}
if (Flags.CalSpeed) // if calculate flag set then
{
SendSpeed(); // send present speed serially
CalculateDC(); // calculate new dutycycle using PI
Flags.CalSpeed = 0; // clear flag
}
}
if(Flags.RunMotor==0)
{
wait_S2_start();
continue;//也即当等待开启电机按钮的时候,下面的语句不执行了,直接从头开始while(1)大语句开始执行
}
} // end of while (1)
} // end of main
/*******************************************************************
Below is the code required to setup the ADC registers for :
1. 1 channel conversion (in this case RB2/AN2)
2. PWM trigger starts conversion
3. Pot is connected to CH0 and RB2
4. AD interrupt is set and buffer is read in the interrupt
*********************************************************************/
void InitADC10(void)
{
ADPCFG = 0xFFF8; // all PORTB = Digital;RB0 to RB2 = analog
ADCON1 = 0x0064; // PWM starts conversion
ADCON2 = 0x0200; // simulataneous sample 4 channels
ADCHS = 0x0002; // Connect RB2/AN2 as CH0 = pot
ADCON3 = 0x0080; // Tad = internal RC (4uS)
IFS0bits.ADIF = 0;
IEC0bits.ADIE = 1;
ADCON1bits.ADON = 1; // turn ADC ON
}
/********************************************************************
InitMCPWM, intializes the Motor PWM as follows:
1. FPWM = 39000 hz at 10Mips
2. Independant PWMs
3. Control outputs using OVDCON
4. Init Duty Cycle with a value = 100
5. Set ADC to be triggered by PWM special trigger
*********************************************************************/
void InitMCPWM(void)
{
PTPER = FCY/FPWM - 1; // set the pwm period register:PTPER=10^7/39000-1=255,那么计算PDCx的时候大于这个数字占空比为100%
PWMCON1 = 0x0700; // disable PWMs
/*这里的7表示:PMOD4:PMOD1=111,即PWM配对IO脚处于独立输出模式。RE5:RE0=0表示disable PWMs
为什么要禁止掉PWMx的引脚???????
*/
OVDCON = 0x0000; // allow control using OVD register
PDC1 = 200; // init PWM DC 1, 2 and 3 to 100
PDC2 = 200;
PDC3 = 200;
SEVTCMP = PTPER; // ?????????????set ADC to trigeer at ...
PWMCON2 = 0x0F00; // 16 PWM values
PTCON = 0x8000; // start PWM however output ...
// is enabled by OVDCON which is OFF
}
/************************************************************************
Tmr3 is used to determine the rotor speed so it is set to count using Tcy/256
*************************************************************************/
void InitTMR3(void)
{
T3CON = 0x0030; // internal Tcy/256 clock]
TMR3 = 0;
PR3 = 0xFFFF;//Timer3 周期寄存器
}
void InitUART(void)
{
//---------------------------------------------------------------------
// Initialize the UART1 for BAUD = 19,200
U1MODE = 0x8000;
U1STA = 0x0000;
U1BRG = ((FCY/16)/BAUD) - 1; // set baud to 19200
IEC0bits.U1RXIE = 1; // enable RX interrupt
RXPtr = &InData[0]; // point to first char in receive buffer
Flags.CheckRX = 0; // clear rx and tx flags
Flags.SendTX = 0;
U1STAbits.UTXEN
= 1; // Initiate transmission
}
//------------------------------------------------------------------------
// SendSpeed sends the speed information on the uart at 19200 baud
void SendSpeed()
{
unsigned int k;
unsigned char c;
k = SPEEDMULT/timer3avg;
c = k/10000;
if (c > 0)
k = k - c*10000;//OutData[]这个字符数组设定的speed有五位数,故第一次除以10000
OutData[OFFSET] = (c + 0x30);//加上0x30的原因是把数字转换成字符
c = k/1000;
if (c > 0)
k = k - c*1000;
OutData[OFFSET+1] = (c + 0x30);
c = k/100;
if (c > 0)
k = k - c*100;
OutData[OFFSET+2] = (c + 0x30);
c = k/10;
if (c > 0)
k = k - c*10;
OutData[OFFSET+3] = (c + 0x30);
OutData[OFFSET+4] = (char)(k + 0x30);
TXPtr = &OutData[0];//发送字符串指针的起始地址。
//SendMsg();//发送字符串消息(字符串中带有speed信息),以指针TxPtr指向的内容为指定字符来发送
Flags.CalSpeed = 0;//计算speed这个过程已经结束,所以清零。
}
void SendMsg(void)
{
while (*TXPtr)
{
while (U1STAbits.UTXBF);//等待发送缓冲器发送完,否则的话下一个要发送的数据可能会掩盖掉上一个要发送的数据
U1TXREG = *TXPtr++;
}
}
/*****************************************************************************
CalculateDC, uses the PI algorithm to calculate the new DutyCycle value which
will get loaded into the PDCx registers.
****************************************************************************/
void CalculateDC(void)
{
ActualSpeed = SPEEDMULT/timer3avg;
DesiredSpeed = DesiredSpeed*POTMULT;
//这个POTMULT其实没有什么特别的意思,也就是用AD转换得到的值在乘以这个POTMULT的值来代表人想要设定的电机的转速。
SpeedError = DesiredSpeed - ActualSpeed;
SpeedIntegral += SpeedError;
DutyCycle = (((long)Kps*(long)SpeedError + (long)Kis*(long)SpeedIntegral) >> 16);
PDC1 = PDC1 + DutyCycle;
if (PDC1 < 50)
{PDC1 = 50;SpeedIntegral = 0;}
if (PDC1 > 512)
{PDC1 = 512;SpeedIntegral = 0;}
PDC2 = PDC1;
PDC3 = PDC1;
}
//---------------------------------------------------------------------
// This is a generic 1ms delay routine to give a 1mS to 65.5 Seconds delay
// For N = 1 the delay is 1 mS, for N = 65535 the delay is 65,535 mS.
// Note that FCY is used in the computation. Please make the necessary
// Changes(PLLx4 or PLLx8 etc) to compute the right FCY as in the define
// statement above.
void DelayNmSec(unsigned int N)
{
unsigned int j;
while(N--)
for(j=0;j < MILLISEC;j++);
}