#define __p30F3010__
#include "p30F3010.h" // put compiler dsPIC.h file in root
#include "adc10.h"
#include "delay.h"
//-----------------------------------------------------------------------------
//Configuration bits
_FOSC(CSW_FSCM_OFF & XT_PLL4);
_FWDT(WDT_OFF);
_FBORPOR (PBOR_OFF & PWRT_16 & MCLR_EN);
_FGS(CODE_PROT_OFF);
//-----------------------------------------------------------------------------
//Program Specific Constants
//Fcy = cycle clock = Fosc/4 = (XTAL * PLLmultiplier)/(4*PostScaler)
#define FCY 5000000 // 5 MHz Xtal*PLLof4/(4*Postscalerof1)
#define MILLISEC FCY/10000 // 1 mSec delay constant
#define FPWM 20000 // 20 kHz, so that no audible noise is present. (from sensorless code)
// Put global variables here
unsigned int ADCValue;
unsigned int RefSpeed;
// subroutines or functions must be defined before they are used ===============
// subroutines or functions must be defined before they are used ===============
// subroutines or functions must be defined before they are used ===============
// void DelayNmSec(unsigned int N) =============================================
void DelayNmSec(unsigned int N){
unsigned int j;
while(N--)
for(j=0;j < MILLISEC;j++);
}
/*********************************************************************
Function: void __attribute__((__interrupt__)) _ADCInterrupt (void)
PreCondition: None.
Input: None.
Output: None.
Side Effects: None.
Overview: The ADC interrupt loads the reference speed (RefSpeed) with
the respective value of the POT. The value will be a signed
fractional value, so it doesn't need any scaling.
Note: None.
********************************************************************/
void __attribute__((interrupt, no_auto_psv)) _ADCInterrupt (void)
{
IFS0bits.ADIF = 0; // Clear interrupt flag
RefSpeed = ADCBUF0; // Read POT value to set Reference Speed
return;
}
// void ADC_Init() ==================================================================
void ADC_Init(void) { // functions must be defined before you use them
ADPCFG = 0xFFFB; // all PORTB = Digital; RB2 = analog
ADCON1 = 0x00E4; // Autoconvert starts conversion, auto sampling
ADCON2 = 0; // sample CH0 channel only
ADCHS = 0x0002; // Connect RB2/AN2 as CH0 input in this example RB2/AN2 is the input
ADCON3 = 0x0080; // Tad = internal RC clock (4uS)
ADCSSL = 0; // not sure on this register, I will need to research it's effects
IFS0bits.ADIF = 0; // clear ADC interrupt flag
ADCON1bits.ADON = 1; // turn ADC ON
}
// ReadADC function =====================================================
unsigned int ReadADC(void){
ADCON1bits.SAMP = 1; // sampling begins immediately after last conversion
DelayNmSec(100); // for 100 mS
while (!ADCON1bits.DONE); // conversion done?
ADCValue = ADCBUF0; // yes Now ADCValue holds the value for you to use
return ADCValue; // Value of ADC
}
// PWM Function // ===============================================
void InitMCPWM(void)
{
TRISE = 0x0100; // PWM pins as outputs, and FLTA as input
PTPER = (FCY/FPWM - 1) >> 1; // Compute Period based on CPU speed and
// required PWM frequency (see defines)
OVDCON = 0x0000; // Disable all PWM outputs.
DTCON1 = 0x0010; // ~1 us of dead time
PWMCON1 = 0x0077; // Enable PWM output pins and configure them as
// complementary mode
PDC1 = PTPER; // Initialize as 0 voltage
PDC2 = PTPER; // Initialize as 0 voltage
PDC3 = PTPER; // Initialize as 0 voltage
SEVTCMP = 1; // Enable triggering for ADC
PWMCON2 = 0x0F02; // 16 postscale values, for achieving 20 kHz
PTCON = 0x8002; // start PWM as center aligned mode
return;
}
// main routine ================================================================
int main(void) {
// Arlo, I assume your LED is on Pin D0
LATD = 0xfffe; // Writes to the latch, sets DO to off state for LED
TRISD = 0xfffe; // O sets pin as output for driving LED on DO
// This should flash your LED 5 times at startup
int ijk; // From bigmoose
for (ijk = 0; ijk < 5; ijk++) {
PORTDbits.RD0 = 1; // turn on D0 for LED
DelayNmSec(500); // delay a half second
PORTDbits.RD0 = 0; // turn off DO
DelayNmSec(500); // delay a half second
} // end of for
// now lets play with the adc
ADC_Init(); // call Initialize ADC to initialize it
InitMCPWM(); // Initialize PWM @ 20 kHz, center aligned, 1 us of dead time
while (1) {
} // end of the while(1) loop
} // end of main
Read Throttle Position. ADC function
Read hall position. Read Hall function
Read Temp of motor. Temp ADC function
Read temp of mosfets. Temp2 ADC function
Read speed of motor. Speed function
Read amps in phase. Current Function
Watch interupt (brake) (or over amp shutdown) SD pin watch function
Calculate PWM based on above. Main function
Calculate What phase to energize based on above. Main function
Calculate Advance based on above. Advance function
Energize PWM with set min off time. Main function
Wait for x time Start over. Main function
Loop forever Main function(while (1)?)
#define __p30F3010__
#include "p30F3010.h" // put compiler dsPIC.h file in root
#include "adc10.h"
#include "delay.h"
//-----------------------------------------------------------------------------
//Configuration bits
_FOSC(CSW_FSCM_OFF & XT_PLL4);
_FWDT(WDT_OFF);
_FBORPOR (PBOR_OFF & PWRT_16 & MCLR_EN);
_FGS(CODE_PROT_OFF);
//-----------------------------------------------------------------------------
//Program Specific Constants
//Fcy = cycle clock = Fosc/4 = (XTAL * PLLmultiplier)/(4*PostScaler)
#define FCY 5000000 // 5 MHz Xtal*PLLof4/(4*Postscalerof1)
#define MILLISEC FCY/10000 // 1 mSec delay constant
// Put global variables here
unsigned int ADCValue;
#define FPWM 20000 // 20 kHz, so that no audible noise is present.
// subroutines or functions must be defined before they are used ===============
// subroutines or functions must be defined before they are used ===============
// subroutines or functions must be defined before they are used ===============
// subroutines or functions must be defined before they are used ===============
// void DelayNmSec(unsigned int N) =============================================
void DelayNmSec(unsigned int N){
unsigned int j;
while(N--)
for(j=0;j < MILLISEC;j++);
}
/*********************************************************************
/**Read Throttle Position.************************
/**ADC
// void ADC_Init() ==================================================================
void ADC_Init(void) { // functions must be defined before you use them
ADPCFG = 0xFFFB; // all PORTB = Digital; RB2 = analog
ADCON1 = 0x00E4; // Autoconvert starts conversion, auto sampling
ADCON2 = 0; // sample CH0 channel only
ADCHS = 0x0002; // Connect RB2/AN2 as CH0 input in this example RB2/AN2 is the input
ADCON3 = 0x0080; // Tad = internal RC clock (4uS)
ADCSSL = 0; // not sure on this register, I will need to research it's effects
IFS0bits.ADIF = 0; // clear ADC interrupt flag
ADCON1bits.ADON = 1; // turn ADC ON
}
// ReadADC function =====================================================
unsigned int ReadADC(void){
ADCON1bits.SAMP = 1; // sampling begins immediately after last conversion
DelayNmSec(100); // for 100 mS
while (!ADCON1bits.DONE); // conversion done?
ADCValue = ADCBUF0; // yes Now ADCValue holds the value for you to use
return ADCValue; // Value of ADC
}
/**Read hall position.*******************************
/**Read Temp of motor.*******************************
/**Read temp of mosfets.*****************************
/**Read speed of motor.*******************************
/**Read amps in phase.********************************
/**Watch interupt (brake) or over amp shutdown*********************
/**Calculate PWM based on above.**************************
/**Calculate What phase to energize based on above.*********************
/**Calculate Advance based on above.****************************
/**Energize PWM with set min off time.****************************
/**Wait for x time Start over.*******************************
Thanks for the link Fechter.fechter said:Disclaimer: *I'm a complete idiot at microcontroller code, so perhaps I should not even comment here.*
I have been researching various Field Oriented Control schemes, some which use sensors and some sensorless. There are existing code examples out there that look like they could be used as a starting point for a very advanced controller. While I would be clueless about writing the code from scratch, taking an example that's close and hacking on it is something that's more in line with my skill set.
Here's an interesting one, but no doubt very complex:
http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=1824&appnote=en544825
The more I read about FOC, the more convinced I am that implementing it would not add that much to the cost of building a controller yet give much improved operation.
Running an induction motor would also be possible using the same hardware. The real hang up is getting the code right with all the parameters dialed in.
The example above is sensorless, and may not work well (or at all) at low speeds. For good low speed performance, it seems some kind of high resolution rotor speed sensor is the most economical route.
fechter said:Disclaimer: *I'm a complete idiot at microcontroller code, so perhaps I should not even comment here.*
I have been researching various Field Oriented Control schemes, some which use sensors and some sensorless. There are existing code examples out there that look like they could be used as a starting point for a very advanced controller. While I would be clueless about writing the code from scratch, taking an example that's close and hacking on it is something that's more in line with my skill set.
Here's an interesting one, but no doubt very complex:
http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=1824&appnote=en544825
The more I read about FOC, the more convinced I am that implementing it would not add that much to the cost of building a controller yet give much improved operation.
Running an induction motor would also be possible using the same hardware. The real hang up is getting the code right with all the parameters dialed in.
The example above is sensorless, and may not work well (or at all) at low speeds. For good low speed performance, it seems some kind of high resolution rotor speed sensor is the most economical route.
#define __p30F3010__
#include "p30F3010.h"
#define FCY 10000000// xtal = 5.0Mhz; PLLx8
#define MILLISEC FCY/10000// 1 mSec delay constant
#define FPWM 20000 // 20 khz
#define Ksp 1200
#define Ksi 10
#define RPMConstant 60 *(FCY/256)
#define S2 !PORTCbits.RC14
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 unused : 14;
} Flags;
unsigned int HallValue;
int Speed;
unsigned int Timer3;
unsigned char Count;
unsigned char SpeedCount;
int DesiredSpeed;
int ActualSpeed;
int SpeedError;
int DutyCycle;
int SpeedIntegral;
/*************************************************************
low side driver table is as below. In this StateLoTable,
the Low side driver is PWM while the high side driver is
either on or off. This table is used in this exercise
*************************************************************/
unsigned int StateLoTable[] = {0x0000, 0x1002, 0x0420, 0x0402,
0x0108, 0x1008, 0x0120, 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 user has to then read the 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 StateLoTable
to determine the value loaded in the OCDCON register
*****************************************************************/
void _ISR _CNInterrupt(void)
{
IFS0bits.CNIF = 0; // clear flag
HallValue = PORTB & 0x0038; // mask RB3,4 & 5
HallValue = HallValue >> 3; // shift right 3 times
OVDCON = StateLoTable[HallValue];// Load the overide control register
}
/*********************************************************************
The ADC interrupt loads the DesiredSpeed variable with the demand pot
value. This is then used to determing the Speed error. When the motor
is not running, the PDC values use the direct Demand value from the pot.
*********************************************************************/
void _ISR _ADCInterrupt(void)
{
IFS0bits.ADIF = 0;
DesiredSpeed = ADCBUF0;
if (!Flags.RunMotor)
{
PDC1 = ADCBUF0; // get value ...
PDC2 = PDC1; // and load all three PWMs ...
PDC3 = PDC1; // duty cycles
}
}
/************************************************************************
The main routine controls the initialization, and the keypress to start
and stop the motor.
************************************************************************/
int main(void)
{
LATE = 0x0000;
TRISE = 0xFFC0; // PWMs are outputs
CNEN1 = 0x00E0; // CN5,6 and 7 enabled
CNPU1 = 0x00E0; // enable internal pullups
IFS0bits.CNIF = 0; // clear CNIF
IEC0bits.CNIE = 1; // enable CN interrupt
SpeedError = 0;
SpeedIntegral = 0;
InitTMR3();
InitMCPWM();
InitADC10();
while(1)
{
while (!S2); // wait for start key hit
while (S2) // wait till key is released
DelayNmSec(10);
// 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 = StateLoTable[HallValue];// Load the overide control register
PWMCON1 = 0x0777; // enable PWM outputs
Flags.RunMotor = 1; // set flag
T3CON = 0x8030; // start TMR3
while (Flags.RunMotor) // while motor is running
if (!S2) // if S2 is not pressed
{
if (HallValue == 1) //IF in sector 1
{
HallValue = 0xFF; // force a new value as a sector
if (++Count == 5) // do this for 5 electrical revolutions or 1
// mechanical revolution for a 10 pole motor
{
Timer3 = TMR3;// read latest tmr3 value
TMR3 = 0;
Count = 0;
GetSpeed();// determine spped
}
}
}
else // else S2 is pressed to stop motor
{
PWMCON1 = 0x0700;// disable PWM outputs
OVDCON = 0x0000; // overide PWM low.
Flags.RunMotor = 0;// reset run flag
while (S2)// wait for key release
DelayNmSec(10);
}
} // end of while (1)
}
/*******************************************************************
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. Manual Stop Sampling and start converting
5. Manual check of Conversion complete
*********************************************************************/
void InitADC10(void)
{
ADPCFG = 0xFFF8; // all PORTB = Digital;RB0 to RB2 = analog
ADCON1 = 0x0064; // PWM starts conversion
ADCON2 = 0x0000; // sample CH0 channel
ADCHS = 0x0002; // Connect RB2/AN2 as CH0 = pot.
ADCON3 = 0x0080; // Tad = internal RC (4uS)
IFS0bits.ADIF = 0; // clear flag
IEC0bits.ADIE = 1; // enable interrupt
ADCON1bits.ADON = 1; // turn ADC ON
}
/********************************************************************
InitMCPWM, intializes the PWM as follows:
1. FPWM = 16000 hz
2. Independant PWMs
3. Control outputs using OVDCON
4. Set Duty Cycle using PI algorithm and Speed Error
5. Set ADC to be triggered by PWM special trigger
*********************************************************************/
void InitMCPWM(void)
{
PTPER = FCY/FPWM - 1;
PWMCON1 = 0x0700; // disable PWMs
OVDCON = 0x0000; // allow control using OVD
PDC1 = 100; // init PWM 1, 2 and 3 to 100
PDC2 = 100;
PDC3 = 100;
SEVTCMP = PTPER; // special trigger is 16 period values
PWMCON2 = 0x0F00; // 16 postscale values
PTCON = 0x8000; // start PWM
}
/************************************************************************
Tmr3 is used to determine the speed so it is set to count using Tcy/256
*************************************************************************/
void InitTMR3(void)
{
T3CON = 0x0030; // internal Tcy/256 clock
TMR3 = 0;
PR3 = 0x8000;
}
/************************************************************************
GetSpeed, determins the exact speed of the motor by using the value in
TMR3 for every mechanical cycle.
*************************************************************************/
void GetSpeed(void)
{
if (Timer3 > 23000) // if TMR3 is large ignore reading
return;
if (Timer3 > 0)
Speed = RPMConstant/(long)Timer3;// get speed in RPM
ActualSpeed += Speed;
ActualSpeed = ActualSpeed >> 1;
if (++SpeedCount == 1)
{SpeedCount = 0;CalculateDC();}
}
/*****************************************************************************
CalculateDC, uses the PI algorithm to calculate the new DutyCycle value which
will get loaded into the PDCx registers.
****************************************************************************/
void CalculateDC(void)
{
DesiredSpeed = DesiredSpeed*3;
Flags.Minus = 0;
if (ActualSpeed > DesiredSpeed)
SpeedError = ActualSpeed - DesiredSpeed;
else
{
SpeedError = DesiredSpeed - ActualSpeed;
Flags.Minus = 1;
}
SpeedIntegral += SpeedError;
if (SpeedIntegral > 9000)
SpeedIntegral = 0;
DutyCycle = (((long)Ksp*(long)SpeedError + (long)Ksi*(long)SpeedIntegral) >> 12);
DesiredSpeed = DesiredSpeed/3;
if (Flags.Minus)
DutyCycle = DesiredSpeed + DutyCycle;
else DutyCycle = DesiredSpeed - DutyCycle;
if (DutyCycle < 100)
DutyCycle = 100;
if (DutyCycle > 1250)
{DutyCycle = 1250;SpeedIntegral = 0;}
PDC1 = DutyCycle;
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++);
}
I know you are great at this code stuff and you are building some great things. But when you up the anti with a Lo-inductance motor and hi HP then you can talk. Untill then its just a miracle that anyone has a working Hi hp brushless motor of that kind of power level! You need to get a motor from marko and test with it. Don't worry about putting a load on it thats the easy part.Lebowski said:man that's some nasty noises that motor is making maybe the guy could use some pointers from me
SD was just there to show if there was current sensing on the phase it could be used to trigger the SD pin. Its not going to be used just yet. And the zener is to protect the gate of the mosfet if the Driver ever fails. I did not install the Zeners it was just a thought I picked up from Shane Coltonzombiess said:Why do you have the 15V zeners in your diagram and an SD connection to the phase output (are you planning some sort of driver shut down for over current output protection?)? You don't want to connect the SD directly to the phase output or bye bye driver!
Arlo1 said:SD was just there to show if there was current sensing on the phase it could be used to trigger the SD pin. Its not going to be used just yet. And the zener is to protect the gate of the mosfet if the Driver ever fails. I did not install the Zeners it was just a thought I picked up from Shane Coltonzombiess said:Why do you have the 15V zeners in your diagram and an SD connection to the phase output (are you planning some sort of driver shut down for over current output protection?)? You don't want to connect the SD directly to the phase output or bye bye driver!
Yes that sounds right.Arlo1 said:Ok so I think I got it. I need to run the isolated 12 v to the hi side driver and the isolated negative to the phase wire to keep the hi side cap charged and the hi side driver 12v above the phase wire. And all other pins can be powered off the non isolated power. Each H bridge gets its own isolated 12v SD pin held to non isolated ground.
#define __p30F3010__
#include "p30F3010.h"
#define FCY 10000000// xtal = 5.0Mhz; PLLx8
#define MILLISEC FCY/10000// 1 mSec delay constant
#define FPWM 20000 // 20 khz
#define Ksp 1200
#define Ksi 10
#define RPMConstant 60 *(FCY/256)
#define S2 !PORTCbits.RC14
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 unused : 14;
} Flags;
unsigned int HallValue;
int Speed;
unsigned int Timer3;
unsigned char Count;
unsigned char SpeedCount;
int DesiredSpeed;
int ActualSpeed;
int SpeedError;
int DutyCycle;
int SpeedIntegral;
/*************************************************************
low side driver table is as below. In this StateLoTable,
the Low side driver is PWM while the high side driver is
either on or off. This table is used in this exercise
*************************************************************/
unsigned int StateLoTable[] = {0x0000, 0x1002, 0x0420, 0x0402,
0x0108, 0x1008, 0x0120, 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 user has to then read the 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 StateLoTable
to determine the value loaded in the OCDCON register
*****************************************************************/
void _ISR _CNInterrupt(void)
{
IFS0bits.CNIF = 0; // clear flag
HallValue = PORTB & 0x0038; // mask RB3,4 & 5
HallValue = HallValue >> 3; // shift right 3 times
OVDCON = StateLoTable[HallValue];// Load the overide control register
}
/*********************************************************************
The ADC interrupt loads the DesiredSpeed variable with the demand pot
value. This is then used to determing the Speed error. When the motor
is not running, the PDC values use the direct Demand value from the pot.
*********************************************************************/
void _ISR _ADCInterrupt(void)
{
IFS0bits.ADIF = 0;
DesiredSpeed = ADCBUF0;
if (!Flags.RunMotor)
{
PDC1 = ADCBUF0; // get value ...
PDC2 = PDC1; // and load all three PWMs ...
PDC3 = PDC1; // duty cycles
}
}
/************************************************************************
The main routine controls the initialization, and the keypress to start
and stop the motor.
************************************************************************/
int main(void)
{
LATE = 0x0000;
TRISE = 0xFFC0; // PWMs are outputs
CNEN1 = 0x00E0; // CN5,6 and 7 enabled
CNPU1 = 0x00E0; // enable internal pullups
IFS0bits.CNIF = 0; // clear CNIF
IEC0bits.CNIE = 1; // enable CN interrupt
SpeedError = 0;
SpeedIntegral = 0;
InitTMR3();
InitMCPWM();
InitADC10();
while(1)
{
while (!S2); // wait for start key hit
while (S2) // wait till key is released
DelayNmSec(10);
// 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 = StateLoTable[HallValue];// Load the overide control register
PWMCON1 = 0x0777; // enable PWM outputs
Flags.RunMotor = 1; // set flag
T3CON = 0x8030; // start TMR3
while (Flags.RunMotor) // while motor is running
if (!S2) // if S2 is not pressed
{
if (HallValue == 1) //IF in sector 1
{
HallValue = 0xFF; // force a new value as a sector
if (++Count == 5) // do this for 5 electrical revolutions or 1
// mechanical revolution for a 10 pole motor
{
Timer3 = TMR3;// read latest tmr3 value
TMR3 = 0;
Count = 0;
GetSpeed();// determine spped
}
}
}
else // else S2 is pressed to stop motor
{
PWMCON1 = 0x0700;// disable PWM outputs
OVDCON = 0x0000; // overide PWM low.
Flags.RunMotor = 0;// reset run flag
while (S2)// wait for key release
DelayNmSec(10);
}
} // end of while (1)
}
/*******************************************************************
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. Manual Stop Sampling and start converting
5. Manual check of Conversion complete
*********************************************************************/
void InitADC10(void)
{
ADPCFG = 0xFFF8; // all PORTB = Digital;RB0 to RB2 = analog
ADCON1 = 0x0064; // PWM starts conversion
ADCON2 = 0x0000; // sample CH0 channel
ADCHS = 0x0002; // Connect RB2/AN2 as CH0 = pot.
ADCON3 = 0x0080; // Tad = internal RC (4uS)
IFS0bits.ADIF = 0; // clear flag
IEC0bits.ADIE = 1; // enable interrupt
ADCON1bits.ADON = 1; // turn ADC ON
}
/********************************************************************
InitMCPWM, intializes the PWM as follows:
1. FPWM = 16000 hz
2. Independant PWMs
3. Control outputs using OVDCON
4. Set Duty Cycle using PI algorithm and Speed Error
5. Set ADC to be triggered by PWM special trigger
*********************************************************************/
void InitMCPWM(void)
{
PTPER = FCY/FPWM - 1;
PWMCON1 = 0x0700; // disable PWMs
OVDCON = 0x0000; // allow control using OVD
PDC1 = 100; // init PWM 1, 2 and 3 to 100
PDC2 = 100;
PDC3 = 100;
SEVTCMP = PTPER; // special trigger is 16 period values
PWMCON2 = 0x0F00; // 16 postscale values
PTCON = 0x8000; // start PWM
}
/************************************************************************
Tmr3 is used to determine the speed so it is set to count using Tcy/256
*************************************************************************/
void InitTMR3(void)
{
T3CON = 0x0030; // internal Tcy/256 clock
TMR3 = 0;
PR3 = 0x8000;
}
/************************************************************************
GetSpeed, determins the exact speed of the motor by using the value in
TMR3 for every mechanical cycle.
*************************************************************************/
void GetSpeed(void)
{
if (Timer3 > 23000) // if TMR3 is large ignore reading
return;
if (Timer3 > 0)
Speed = RPMConstant/(long)Timer3;// get speed in RPM
ActualSpeed += Speed;
ActualSpeed = ActualSpeed >> 1;
if (++SpeedCount == 1)
{SpeedCount = 0;CalculateDC();}
}
/*****************************************************************************
CalculateDC, uses the PI algorithm to calculate the new DutyCycle value which
will get loaded into the PDCx registers.
****************************************************************************/
void CalculateDC(void)
{
DesiredSpeed = DesiredSpeed*3;
Flags.Minus = 0;
if (ActualSpeed > DesiredSpeed)
SpeedError = ActualSpeed - DesiredSpeed;
else
{
SpeedError = DesiredSpeed - ActualSpeed;
Flags.Minus = 1;
}
SpeedIntegral += SpeedError;
if (SpeedIntegral > 9000)
SpeedIntegral = 0;
DutyCycle = (((long)Ksp*(long)SpeedError + (long)Ksi*(long)SpeedIntegral) >> 12);
DesiredSpeed = DesiredSpeed/3;
if (Flags.Minus)
DutyCycle = DesiredSpeed + DutyCycle;
else DutyCycle = DesiredSpeed - DutyCycle;
if (DutyCycle < 100)
DutyCycle = 100;
if (DutyCycle > 1250)
{DutyCycle = 1250;SpeedIntegral = 0;}
PDC1 = DutyCycle;
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++);
}
Arlo1 said:OK so I built a controller and a 6 fet power stage with 4115 fets I just have to go switch around the Hi side fets because i forgot that positive needs to be on the proper leg due to the internal diode.
What I need is to rip apart this code. I need to know what pins are for the on off switch and what else it will take to make it turn a motor.
Then as an open source project we are going to modify it and develop a code with all the features we can fit!
Thanks ricky. I almost didnt even need to ask lol I just did not see that when I fisrt looked while I was tired last night.Ricky_nz said:Arlo1 said:OK so I built a controller and a 6 fet power stage with 4115 fets I just have to go switch around the Hi side fets because i forgot that positive needs to be on the proper leg due to the internal diode.
What I need is to rip apart this code. I need to know what pins are for the on off switch and what else it will take to make it turn a motor.
Then as an open source project we are going to modify it and develop a code with all the features we can fit!
Since S2 seems to be used to control what its doing (presume Switch 2 on some board) the following line looks like its read from Port C bit 14
#define S2 !PORTCbits.RC14
check the datasheet for the pin number.
Seems to be +ve logic, ie +V for switch pressed, 0v for switch released.
it does mention this is the start switch
Click on any of the underlined column headers to sort by that column
Microchip currently only accepts online sample orders from registered business or university email addresses. We currently do not accept orders from generic ISP accounts. Please do one of the following:
1. Register with a valid business or university email address and place your sample order
2. Visit www.microchipdirect.com to purchase parts
Thank you for your interest in Microchip.