BLDC motor control + MPLAB code to mikoc + interrupt?

General discussion on mikroC for dsPIC30/33 and PIC24.
Post Reply
Author
Message
ko8i
Posts: 17
Joined: 01 Dec 2012 10:58

BLDC motor control + MPLAB code to mikoc + interrupt?

#1 Post by ko8i » 30 Dec 2012 11:47

Hi guys,

I'm having a problem with translating the
code from MPLAB to MIKROC code
this is the original MPLAB code

Code: Select all

//
//---------------------------------------------------------------------
//        File:                ClosedLoopSenBLDC.c
//
// This program runs a sensored BLDC motor in a closed PI loop
// The basic circuit is as depicted in AN957. The hardware is as
// depicted in the Masters2004 SNS845 class.  The board is expected
// to be productized in 2005.  Please check the web for details
//
//        Written By:                Stan D'Souza, Microchip Technology
//                                                
// 
// The following files should be included in the MPLAB project:
//
//                ClosedLoopSenBLDC.c                -- Main source code file
//                p30f2010.gld        -- Linker script file
//                                
//
//---------------------------------------------------------------------
//
// Revision History
//
// 10/01/04 -- first version 
// 2/11/05 -- updated with improved PI and serial connection
//---------------------------------------------------------------------- 


#define __dsPIC30F2010__
#include "c:\pic30_tools\support\h\p30F2010.h"

#define FCY  10000000                        // xtal = 5.0Mhz; PLLx8
#define MILLISEC FCY/10000                        // 1 mSec delay constant
#define FPWM 39000
#define POLEPAIRS        5                // 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                // factor used to calculate speed
#define OFFSET 8                                // offset in InData to load speed values
#define POTMULT 4                                // 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(void);
void SendSpeed(void);
void InitTMR3(void);
void SendMsg(void);
void CalculateDC(void);



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;
unsigned char InData[] = {"000000"};
unsigned char OutData[] = {"Speed = 00000 rpm\r\n"};

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)
{
        IFS0bits.CNIF = 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?
                {                                                                // 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 == CR)
                {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;                // after every adc read,  do a PI calculation
}

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
        InitMCPWM();
        InitADC10();
        InitUART();
        InitTMR3();
        timer3avg = 0;
        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 = StateLoTableAntiClk[HallValue];        // Load the overide control register
                PWMCON1 = 0x0777;                                        // enable PWM outputs
                Flags.RunMotor = 1;                                        // set running flag
                T3CONbits.TON = 1;                                        // start tmr3
                polecount = 1;
                DelayNmSec(100);
                while (Flags.RunMotor)                                // while motor is running
                        {
                        if (S2)                                                        // if S2 is pressed, stop
                                {
                                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 
                                }
                        }
        }        // 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

        PWMCON1 = 0x0700;                        // disable PWMs
        OVDCON = 0x0000;                        // allow control using OVD register
        PDC1 = 100;                                        // init PWM DC 1, 2 and 3 to 100
        PDC2 = 100;
        PDC3 = 100;
        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;
}

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[OFFSET] = (c + 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();
        Flags.CalSpeed = 0;
}

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;
        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++);
}

The things that i change to suit the mikroc compiler are:
#define __dsPIC30F2010__ //replace with #include <p30Fxxxx.h>
#include "c:\pic30_tools\support\h\p30F2010.h" //remove, I have no idea how to replace this
void __attribute__((__interrupt__)) _CNInterrupt (void) //becomes void CNInterrupt (void)
void __attribute__((__interrupt__)) _U1TXInterrupt(void) //void U1TXInterrupt (void)
void __attribute__((__interrupt__)) _U1RXInterrupt(void) //void U1RXInterrupt (void)
void __attribute__((__interrupt__)) _ADCInterrupt (void) //void ADCInterrupt (void)

and I also configure the PIC

watchdog timer :disabled
primary oscillator mode:XT w/PLL 8x-XT crystal osc.mode 8x PLL enabled

once all these was change the mikroc compiler compile and i compile it into the dsPIC2010
I then put it into the MCLV board, when I press the button the motor turns a tiny bit and stops (1 pole or sector or phase turn)
it repeat this whenever i press the button.
I am 100% sure the board is correctly set up
the code is 100% correct taken from the CD that comes with the motor control board MCLV (and i have tested it by uploading the HEX file)

The only thing that i think may cause the motor to turn a tiny bit and stop is that the interrupts are not going off.
Is there some special settings that I have to do or may have miss?
Or do I need to prioritize the interrupts?
Or my translation of the MPLAB code to MIKROC code is incorrect?

Thank You for reading
Happy New Year
Ko8i

Post Reply

Return to “mikroC for dsPIC30/33 and PIC24 General”