📄 control.zip.c
字号:
//---------------------------------------------------------------------
//
// 单头绣花机主轴驱动软件
//
//---------------------------------------------------------------------
//
//---------------------------------------------------------------------
// 03 /17/07 -- 第一版
//----------------------------------------------------------------------
//--------------------------器件配置------------------------
_FOSC(CSW_FSCM_OFF & XT_PLL16);
_FWDT(WDT_OFF);
_FBORPOR(PBOR_ON & BORV_20 & PWRT_64 & MCLR_EN);///器件配置
//----------------------------------------------------------------------
typedef signed int SFRAC16;
#define FCY 20000000 // 配置外部晶体5M,16倍频。 xtal = 5Mhz; PLLx16 -> 20 MIPS
#define FPWM 19531 // 设定PWM频率为19。5KHZ
#define _10MILLISEC 10
#define _50MILLISEC 50
#define _100MILLISEC 100
#define _500MILLISEC 500
#define HALLA 1 // RB3
#define HALLB 2 // RB4
#define HALLC 4 // RB5
#define CW 0 // 定义电机正转
#define CCW 1 // 定义电机反转
#define Switch_2 (!PORTCbits.RC14)
///周期计算变量定义//
#define MINPERIOD 313 // 以6000转计算最小周期
#define MAXPERIOD 31250 // 以6转计算最大周期
#define MINABSSPEED 327 //最小速度
#define MAXABSSPEED 32663 //最大速度
//当用沸点数初始化有符号16位小数变量时用这个宏
#define SFloat_To_SFrac16(Float_Value) (((Float_Value) < 0.0) ? (SFRAC16)(32768 * (Float_Value) - 0.5) : (SFRAC16)(32767 * (Float_Value) + 0.5))
void SixStepComm(int _Current_Sector, int _Required_Voltage); // 这个函数是用来转化转子位置的换向信号/
void InitADC10(void); // ADC初始化程序用与速度给定
void InitMCPWM(void); // 初始化PWM在20K,互补模式中心对成,500NS的死区时间
void InitTMR1(void); // 初始化T1,速度控制和停止保护
void InitTMR3(void); // 初始化T3,两个输入扑捉通道的时基
void InitUserInt(void); //初始化所有用到的口的方向
void Init_IC_and_CN(void); //初始化扑捉输入和电平变化断口用与或而信号输入
void RunMotor(void); // 初始化所有电机开始启动时用到的变量和中断
void StopMotor(void); // 清除所有标志位,并关断任何关于电机控制和PWM势能
void ForceCommutation(void); // 当电机很慢产生霍儿中断时这个功能是强制换向
void ChargeBootstraps(void);// 在电机开始工作时,给电容充电以保证15V控制电压能够驱动上桥IGBT。
extern void SpeedControl(void); //速度PID运算函数包括汇编和C。
extern void SpeedCalculation(void);
//////// 结构定义用到的标志位
struct
{
unsigned MotorRunning :1;
unsigned MotorCommInAdv :1;
unsigned MotorAdvEnabled:1;
unsigned unused :13;
} Flags;
unsigned int HallValue; // 定义读到的霍儿值数据类型.
unsigned int Sector; // 定义当前变量转子位置
unsigned int LastSector; // 此变量定义上次转子位置,用来滤出回转率的问题
unsigned int MotorStalledCounter = 0; // 每增加1毫秒当发现新的霍尔值后并清除上次的值,用于强制换向和保持停机保护/
char SectorTable[] = {-1,4,2,3,0,5,1,-1};
/*************************************************************
驱动低端表,高端是PWM,低端开关
*************************************************************/
unsigned int StateLoTable[] = { 0x0204, /* PWM2L -> 1, PWM1H -> PWM */
0x0210, /* PWM3L -> 1, PWM1H -> PWM */
0x0810, /* PWM3L -> 1, PWM2H -> PWM */
0x0801, /* PWM1L -> 1, PWM2H -> PWM */
0x2001, /* PWM1L -> 1, PWM3H -> PWM */
0x2004}; /* PWM2L -> 1, PWM3H -> PWM */
/////////////////////////////////////////////////////////
//////////* 定义速度控制变量数据类型
/////////////////////////////////////////////////////////
unsigned char Current_Direction; // 当前电机旋转方向,通过霍尔中断获得。
unsigned char Required_Direction; // 需要的方向,
unsigned int PastCapture, ActualCapture, Period; // 半个电周期变量,通过一个HALL边缘中断获
//////////////////////////////////////////////////////////////////////
// 通过调节使用器绝对PID增益,位置来自数字PID的执行
SFRAC16 Kp = SFloat_To_SFrac16(0.10000);
SFRAC16 Ki = SFloat_To_SFrac16(0.01000);
SFRAC16 Kd = SFloat_To_SFrac16(0.00000);
SFRAC16 Speed, RefSpeed; // 实际和给定速度的误差给PID调节器//
SFRAC16 SpeedAbsValue; // 速度变量的绝对数值,通过相位超前函数//
SFRAC16 ControlOutput = 0; // 调节器输出数值电压数值,用它的需要的方向
////////////////////////////////////////////////////////////////////////////////////////
//常数通过PID调节器,有一个MAC操作在FOO,注意和库函数有变化的,见SpeedControl() Comments。
////////////////////////////////////////////////////////////////////////////////////////
SFRAC16 ControlDifference[3] __attribute__((__space__(xmemory), __aligned__(4)));
SFRAC16 PIDCoefficients[3] __attribute__((__space__(ymemory), __aligned__(4)));
// Used as a temporal variable to perform a fractional divide operation in assembly
SFRAC16 _MINPERIOD = MINPERIOD - 1; // Used by fractional divide operation
/**********************************************************************
T1中断服务程序,通过PID速度计算产生新的PDC数值
**********************************************************************/
void __attribute__((__interrupt__)) _T1Interrupt (void)
{
IFS0bits.T1IF = 0;
Period = ActualCapture - PastCapture; // This is an UNsigned sub straction
if (Period < (unsigned int)MINPERIOD) // MINPERIOD corresponds to 6000 rpm
Period = MINPERIOD;
else if (Period > (unsigned int)MAXPERIOD) // MAXPERIOD corresponds to 60 rpm
Period = MAXPERIOD;
//调用这个汇编函数用来计算速度小数数值 。
// MINPERIOD
// Speed = (Fractional divide) ---------------
// Period
SpeedCalculation();
SpeedAbsValue = Speed;
// 依照转子的当前方向调整速度
if (Current_Direction == CCW)
Speed = -Speed;
// Condition RPM SFRAC16 SINT HEX
// Max Speed CW -> 6000 RPM -> 0.996805 -> 32663 -> 0x7F97 //32663
// Min Speed CW --> 60 RPM - -> 0.009984 -> 327 -> 0x0147 //327
// Min Speed CCW -> -60 RPM -> -0.009984 -> -327 -> 0xFEB9 //-32441
// Max Speed CCW -> -6000 RPM -> -0.996805 -> -32663 -> 0x8069 //105
// 速度PID调节器调用函数,它用实际速度,参考速度加上一定的常数来为SVM计算一个新的速度
SpeedControl();
// 设置最小调节器速度输出/10%/保持电机运转Set minimum ControlOutput to 10% to keep the motor running
if (ControlOutput < 3276) //为什么要在小于3276的时候赋值3276呢??????????????
ControlOutput = 3276;
MotorStalledCounter++; // 这个变量是在每个霍尔中断里面清除的。
if ((MotorStalledCounter % _10MILLISEC) == 0)
{
ForceCommutation(); // 根据指令方向决定PID的输出数值是正还是负,从而决定旋转方向。
//Force Commutation if no hall sensor changes have occured in specified timeout.
}
else if (MotorStalledCounter >= _100MILLISEC)
{
StopMotor(); // 锁定停止电机。
}
return;
}
/**********************************************************************
霍尔中断程序,做电平变化CN5/RB3/HALLA的中断服务程序,目的是计算电机旋转的实际机械方向
并依靠得到的霍尔值和查表来调整相位变量。
注意:为了引起误中断,要进行回转率霍尔输入滤波。
**********************************************************************/
void __attribute__((__interrupt__)) _CNInterrupt (void)
{
IFS0bits.CNIF = 0; // 清除所有电平变化中断标志。
HallValue = (unsigned int)((PORTB >> 3) & 0x0007); // 从PORTB积存器读取霍尔状态换向信息。
Sector = SectorTable[HallValue]; // 查表获得相应的数据(1-5)。
if (Sector != LastSector) // This is a MUST for getting around the HW slow rate
{
// 因为检测到一个新数据,如果杜撰发生就清除变量并停止惦记
MotorStalledCounter = 0;
// 电机当前的方向是通过SECTOR值决定的。
if ((Sector == 5) || (Sector == 2)) // 如条件发生方向为CCW。
Current_Direction = CCW;
else
Current_Direction = CW;
SixStepComm(Sector, ControlOutput); // 如果ControlOutput大于0将正转,否则反转。
LastSector = Sector; // 更新上次SECTOR的数值在有变化的时候。
}
return;
}
/**********************************************************************
Hall B 中断服务程序
**********************************************************************/
void __attribute__((__interrupt__)) _IC7Interrupt (void)
{
IFS1bits.IC7IF = 0;
HallValue = (unsigned int)((PORTB >> 3) & 0x0007); // Read halls
Sector = SectorTable[HallValue]; // Get Sector from table
if (Sector != LastSector) // This is a MUST for getting around the HW slow rate
{
// 依照半个电周期计算霍尔周期
PastCapture = ActualCapture;
ActualCapture = IC7BUF;
// 清除所有缓冲器?????
IC7BUF;
IC7BUF;
IC7BUF;
MotorStalledCounter = 0;
// 电机当前的方向是基于SECTOR的数值计算的。
if ((Sector == 3) || (Sector == 0))//???????
Current_Direction = CCW;
else
Current_Direction = CW;
SixStepComm(Sector, ControlOutput); // 如果调节器输出大于0将运行CW,否则运行CCW。
LastSector = Sector; // 更新上次SECTOR的数值在有变化的时候。
}
return;
}
//**********************************************************************
// Hall C 中断服务程序
//**********************************************************************/
void __attribute__((__interrupt__)) _IC8Interrupt (void)
{
IFS1bits.IC8IF = 0; // 清除IC8中断标志。
HallValue = (unsigned int)((PORTB >> 3) & 0x0007); // 读霍尔状态数值。
Sector = SectorTable[HallValue]; // 查表获得数据(1-5)。
if (Sector != LastSector) // 如果等于上次就什么也不做
{
// 就认为检测到一个新的霍尔数值。
MotorStalledCounter = 0;
// 电机当前的方向是依据SECTOR计算的。
if ((Sector == 1) || (Sector == 4)) // 如果状态是1或4就认为是CCW
Current_Direction = CCW;
else
Current_Direction = CW;
SixStepComm(Sector, ControlOutput); // 如果调节器输出大于0将运行CW,否则运行CCW。
LastSector = Sector; // 把本次的数值作为上一次的数值。
}
return;
}
//*********************************************************************
// The ADC 中断服务程序
//*********************************************************************/
void __attribute__((__interrupt__)) _ADCInterrupt (void)
{
IFS0bits.ADIF = 0;
RefSpeed = (int)(((unsigned int)ADCBUF0) / 2);
if (RefSpeed < 2000)
RefSpeed = 2000;
return;
}
//*********************************************************************
// 主程序
//*********************************************************************/
int main(void)
{
InitUserInt();
InitADC10();
InitTMR1(); // 1 ms周期定时中断初始化
InitTMR3();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -