Not simple BLDC controller It RUNS! :)

SO In my searching I found this link to some good code examples for various fetures and functions. http://roboted.wordpress.com/dspic/
 
Ok so from the same family refernce manual how to I write this properly

Code:
PWMCON1bits.PMOD1 0x0;    // Enables complemetry mode
PWMCON1bits.PMOD2 0x0;
PWMCON1bits.PMOD3 0x0;
PWMCON1bits.PEN3H 0x1;   //Enables PWM H for out put
PWMCON1bits.PEN2H 0x1;
PWMCON1bits.PEN1H 0x1;
 
if you write:

PWMCON1bits.PMOD1 = 1;

the PWM1 I/O pair operates in complementary mode (essential for PMSM or BLDC controller). Conversely, if you write:

PWMCON1bits.PMOD1 = 0;

The IO pins operate operate independently of each other.

Similarly, if you write:

PWMCON1bits.PEN4H = 1;

the pin associated with PWM4H is controlled by the PWM module. If you write:

PWMCON1bits.PEN4H = 0;

the pin is not tied to the PWM module, and can be used for another peripheral or a GPIO pin.

Also, pay attention to Note1. The config bits determine if the output pins are active high or low. Off the top of my head, the low side pins are active high, and the high side pins are active low, but I could be wrong. :)
 
Also, according to p30F4011.h

/* PWMCON1 */
#define _PEN1L PWMCON1bits.PEN1L
#define _PEN2L PWMCON1bits.PEN2L
#define _PEN3L PWMCON1bits.PEN3L
#define _PEN1H PWMCON1bits.PEN1H
#define _PEN2H PWMCON1bits.PEN2H
#define _PEN3H PWMCON1bits.PEN3H
#define _PMOD1 PWMCON1bits.PMOD1
#define _PMOD2 PWMCON1bits.PMOD2
#define _PMOD3 PWMCON1bits.PMOD3

This means you don't have to type PWMCON1bits.PMOD1, instead you can type _PMOD1 (much faster and cleaner)

If you're still using MPLAB 8 or earlier, I strongly suggest you switch to MPLAB X. It's terribly easy to find definitions of stuff like this by ctrl+clicking on any variable or #define

If you want to set up the PWM module to control a BLDC motor with a 30F4011 through the PWM1H/L PWM2H/L and PWM3H/L pins, your init code should look something like this:

PDC1 = 0;
PDC2 = 0;
PDC3 = 0;
_PTOPS = 7; // Postscale of 1:16
_PMOD1 = 1; // Complimentary mode
_PMOD2 = 1; // Complimentary mode
_PMOD3 = 1; // Complimentary mode
_PEN1H = 1; // PWM Output Enabled
_PEN2H = 1; // PWM Output Enabled
_PEN3H = 1; // PWM Output Enabled
_PEN1L = 1; // PWM Output Enabled
_PEN2L = 1; // PWM Output Enabled
_PEN3L = 1; // PWM Output Enabled
PTPER = 511; // ~20 kHz at 40MIPS
_PTEN = 1; // Enable PWM Module

Depending on if you want active high/low outputs, you may need to modify the config bits.

Lastly, to cycle (commutate) the outputs, you need to use the OVDCON register to blank or override the outputs that need to be inactive. (Section 15.8 of the family reference manual)

The PDCx registers store the output duty cycle, and will go from 0-2048.
 
chbaird said:
if you write:

PWMCON1bits.PMOD1 = 1;

the PWM1 I/O pair operates in complementary mode (essential for PMSM or BLDC controller). Conversely, if you write:

PWMCON1bits.PMOD1 = 0;

The IO pins operate operate independently of each other.
HU?? But the chart I posted says 0=complementary and 1= independently.....
 
chbaird said:
Also, according to p30F4011.h

/* PWMCON1 */
#define _PEN1L PWMCON1bits.PEN1L
#define _PEN2L PWMCON1bits.PEN2L
#define _PEN3L PWMCON1bits.PEN3L
#define _PEN1H PWMCON1bits.PEN1H
#define _PEN2H PWMCON1bits.PEN2H
#define _PEN3H PWMCON1bits.PEN3H
#define _PMOD1 PWMCON1bits.PMOD1
#define _PMOD2 PWMCON1bits.PMOD2
#define _PMOD3 PWMCON1bits.PMOD3

This means you don't have to type PWMCON1bits.PMOD1, instead you can type _PMOD1 (much faster and cleaner)

If you're still using MPLAB 8 or earlier, I strongly suggest you switch to MPLAB X. It's terribly easy to find definitions of stuff like this by ctrl+clicking on any variable or #define

If you want to set up the PWM module to control a BLDC motor with a 30F4011 through the PWM1H/L PWM2H/L and PWM3H/L pins, your init code should look something like this:

PDC1 = 0;
PDC2 = 0;
PDC3 = 0;
_PTOPS = 7; // Postscale of 1:16
_PMOD1 = 1; // Complimentary mode
_PMOD2 = 1; // Complimentary mode
_PMOD3 = 1; // Complimentary mode
_PEN1H = 1; // PWM Output Enabled
_PEN2H = 1; // PWM Output Enabled
_PEN3H = 1; // PWM Output Enabled
_PEN1L = 1; // PWM Output Enabled
_PEN2L = 1; // PWM Output Enabled
_PEN3L = 1; // PWM Output Enabled
PTPER = 511; // ~20 kHz at 40MIPS
_PTEN = 1; // Enable PWM Module

Depending on if you want active high/low outputs, you may need to modify the config bits.

Lastly, to cycle (commutate) the outputs, you need to use the OVDCON register to blank or override the outputs that need to be inactive. (Section 15.8 of the family reference manual)

The PDCx registers store the output duty cycle, and will go from 0-2048.
Thanks man Im looking for the tips on how to be more organized.
 
Ok so from the ap not 957 this is the whole code sample from the pwm function
Code:
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
}


And I worked on mine for a couple hours tonight and I already have this. I still need to work on it and define them etc.
Code:
    __FOSC, FRC_PLL16 & PRI & CSW_FSCM_OFF
    __FWDT, WDT_OFF

   FCY = 30MHz               //    Havent figured this part out yet.
   #define FPWM = 50000      // PWM at 50khz

   PTCONbits.PTOPS = 0x0;   // Postscale (1:1)
   PTCONbits.PTCKPS = 0x0;  // Prescale Tcy (1:1)
   PTCONbits.PTMOD = 0b11;   // PWM continious up/down with dual up dates
   PWMCON1bits.PMOD3 = 0x0;  // PWM pair is complementary
   PWMCON1bits.PMOD2 = 0x0;
   PWMCON1bits.PMOD1 = 0x0;
   PWMCON1bits.PEN1H = 0x1;   // enable for output
   PWMCON1bits.PEN2H = 0x1;
   PWMCON1bits.PEN3H = 0x1;
   PWMCON1bits.PEN1L = 0x1;
   PWMCON1bits.PEN2L = 0x1;
   PWMCON1bits.PEN3L = 0x1;
   DTCON1bits.DTBPS = 0x0;   // Dead time unit B  1 Tcy
   DTCON1bits.DTAPS = 0x0;   // Dead time unit A 1 Tcy
   FLTACONbits.FAEN3 = 0x0;   // PWM pins not controlled by fault A input
   FLTACONbits.FAEN2 = 0x0;
   FLTACONbits.FAEN1 = 0x0;
   FLTBCONbits.FAEN3 = 0x0;   // PWM pins not controlled by fault B input
   FLTBCONbits.FAEN2 = 0x0;
   FLTBCONbits.FAEN1 = 0x0;
   OVDCONbits.POVD1L = 0x1;   // PWM pins controlled by PWM generator
   OVDCONbits.POVD2L = 0x1;
   OVDCONbits.POVD3L = 0x1;
   FBORPORbits.HPOL = 0x1;    //  sets pins for active Hi polarity
   FBORPORbits.LPOL = 0x1;
 
Ok so Im working on it tonight but I cant figure out how to set the HPOL and LPOL bits to 1 for active Hi output.... Is this something that you don't bother with on the dspic30f4011??? Once I remove them I can get this to compile once I remove them...
 
OK so at 10:15 pm I made great success... I think i have a hall sensor or two off in the motor I have to scope it tomorrow. I cant remember if these motors can just have all the sensors in between the teeth or not.
[youtube]fDzOdgLpc60[/youtube]

I used this code Its a hybrid of the sample code and I wrote some of the PWM function. I still have a long long way to go but now I have seen the light! :mrgreen:
Code:
#define   __dsPIC30F4011__
#include "p30F4011.h"
//Configuration bits
_FOSC(CSW_FSCM_OFF & FRC_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 16000       // 50 kHz, so that no audible noise is present. (from sensorless code)
#define Ksp 1200
#define Ksi 10
#define RPMConstant 60* (FCY/256)
/* PWMCON1 */
#define _PEN1L PWMCON1bits.PEN1L
#define _PEN2L PWMCON1bits.PEN2L
#define _PEN3L PWMCON1bits.PEN3L
#define _PEN1H PWMCON1bits.PEN1H
#define _PEN2H PWMCON1bits.PEN2H
#define _PEN3H PWMCON1bits.PEN3H
#define _PMOD1 PWMCON1bits.PMOD1
#define _PMOD2 PWMCON1bits.PMOD2
#define _PMOD3 PWMCON1bits.PMOD3
#define _PTMOD PTCONbits.PTMOD
#define _PTCKPS PTCONbits.PTCKPS
#define _PTOPS PTCONbits.PTOPS
#define _DTBPS DTCON1bits.DTBPS
#define _DTAPS DTCON1bits.DTAPS
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)
{
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 (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
}
}
Flags.RunMotor = 0;// reset run flag
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 = 0xFFFB; // 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;
   _PTOPS = 0x0;   // Postscale (1:1)
   _PTCKPS = 0x0;  // Prescale Tcy (1:1)
   _PTMOD = 0b11;   // PWM continious up/down with dual up dates
   _PMOD3 = 0x0;  // PWM pair is complementary
   _PMOD2 = 0x0;
   _PMOD1 = 0x0;
   _PEN1H = 0x1;   // enable for output
   _PEN2H = 0x1;
   _PEN3H = 0x1;
   _PEN1L = 0x1;
   _PEN2L = 0x1;
   _PEN3L = 0x1; 
   _DTBPS = 0x0;     // Dead time unit B  1 Tcy
   _DTAPS = 0x0;     // Dead time unit A 1 Tcy
 #define  FLTACONbits.FAEN3 = 0x0;   // PWM pins not controlled by fault A input.
 #define  FLTACONbits.FAEN2 = 0x0;
 #define  FLTACONbits.FAEN1 = 0x0;
 #define  FLTBCONbits.FAEN3 = 0x0;   // PWM pins not controlled by fault B input
 #define FLTBCONbits.FAEN1 = 0x0;
 #define FLTBCONbits.FAEN2 = 0x0;
 #define  OVDCONbits.POVD1L = 0x1;   // PWM pins controlled by PWM generator
 #define  OVDCONbits.POVD2L = 0x1;
 #define  OVDCONbits.POVD3L = 0x1;
   PDC1 = 50;                 // init PWM to 50
   PDC2 = 50;
   PDC3 = 50;
   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++);
}
Thanks Everyone for all you help espicialy you Dave!
 
Arlo1 said:
Ok so Im working on it tonight but I cant figure out how to set the HPOL and LPOL bits to 1 for active Hi output.... Is this something that you don't bother with on the dspic30f4011??? Once I remove them I can get this to compile once I remove them...

This is set in a configuration word:
Code:
    config __FBORPOR, MCLR_EN & RST_PWMPIN & PWMxL_ACT_HI & PWMxH_ACT_HI

you can overwrite these bits with your program, in my case active HIGH or active LOW (independent
for high and low side) is an option in one of the setup menus.

Code:
1) PWM frequency: 21kHz
2) deadtime: 499ns
3) dutycycle testsignal: 50%
4) toggle high side polarity, now active HIGH
5) toggle low side polarity, now active HIGH
8) test PWM signals
9) return to main menu

It's really cool right the first time you get the motor to turn :D
 
Congrats Arlo!
this is encoraging to a electard like me...I have learnd a ton just sitting back & whatching this thread develop.
Thanks for letting us follow along & thanks to all the contributers.
truely insperational.
 
Thanks guys. And yup I will make sure the code is open source. Then anybody can build it. And in all honesty it was not that bad. Dont get me wrong I proly have ~40 hours of work into this but I think with the right instructions and a proper PCB printed up for the brain and another one for the powerstage a newb who is good at following instructions and has a bit of soldering skill can make one in 10-20 hours and if the powerstage design is good he can make all the power he wants.

For me this is the main bord 3 (because I ordered SMD chips :roll: ) and powerstage 2 to get it to run. All the mistakes were because I didn't folow instructions.
But Let me note Im far from done. I have a very long way to go. Most of that is in the code though and for those of you who want something turn KEY lebowski might sell you some preprogramed dspic30f4011 chips and then you just tweak them to suit your motor.
 
Whooppee Arlo!! Congratulations! Fantastic progress. You have the core running now, the rest is just duck soup. Sure has been fun watching the progress!
 
bigmoose said:
Whooppee Arlo!! Congratulations! Fantastic progress. You have the core running now, the rest is just duck soup. Sure has been fun watching the progress!
Thanks dave. I would be no were close to here if it wasnt for you! I owe at least 10 of the ES members HUGE thanks for everything!!! But my payback will be my work documented in open source and the 5 grade four students I am mentoring to help me get the REAL word out about EVs!
 
Great to see it running Arlo :D.

I was a bit concerned looking at your video with all conductive bits near your boards.
One little slip and you will be starting again, been there done that on earlier projects :lol:
 
Conratulations on a running controller :D Regarding schematics, look at the multisim + ultiboard package :)
 
Fantastic Arlo! It always surprises the hell out of me when one of my projects finally works. That's a great feeling! Massive progress in such short time. At this rate, you'll be tutoring Lebowski by Summer.
 
Ricky_nz said:
I was a bit concerned looking at your video with all conductive bits near your boards.
One little slip and you will be starting again, been there done that on earlier projects :lol:

yes, and murphy guarantees it will happen--see here:
http://www.endless-sphere.com/forums/viewtopic.php?p=514879#p514879
:roll: :oops:
 
Congratulation, Arlo!
It took shorter than I expected! :shock:
but now the challenge is to make it to perform
far better than what is available on the market,
so .... HOP HOP HOP :mrgreen: :mrgreen: :mrgreen:

have fun!
 
Njay said:
Way to go Arlo :D! Now to conquest the world :)
Its funny you say that. Thats what i said to my best friend last night! Just who are you mr njay if that is your real name.
 
Back
Top