Brushless Motor Control

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Brushless Motor Control

3,878 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by tgalluzzo on Fri Jul 29 15:45:31 MST 2011
I'm trying to control a brushless motor with hall sensors. I'm using an lpc1768 with the embedded artists motor control demo board. I have it working but I notice small "twitches" randomly between 0.5-5 seconds apart. Does anyone have advice or have experience with something like this? I'd like to getting running near perfect if possible.

Thanks!
Tom
0 Kudos
Reply
18 Replies

3,658 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by cyc on Wed Apr 04 11:20:58 MST 2012
Rob,

You are correct, I over looked the errata sheet, good to know that it is fixed.

thank you much,

Chu-Yin
0 Kudos
Reply

3,658 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Rob65 on Tue Apr 03 22:41:17 MST 2012

Quote: cyc

If my understanding is correct, the proposed work around does not seem to be ideal; i.e. instead of having the hardware keeping counts of the encoder inputs; it will have to be handled by interrupts.


This only applies on the "Rev -" revision of the lpc167x (see section 2 "Errata Overview" in the PDF you pointed out).
If you buy new lpc1769 boards (I think Rev A was introduced in the beginning of 2011), these will contain the Rev A version of the chip.

So when you need the encoder feedback, just make sure you get "Rev A" chips for you application.

Regards,
[INDENT]Rob
[/INDENT]
0 Kudos
Reply

3,658 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by cyc on Tue Apr 03 14:23:41 MST 2012
Hi,

I recently started to look into using a lpc1769 for motor control with encoder feedback.  It seems the MCPWM.1 errata will affect the quadrature encoder inputs.
If my understanding is correct, the proposed work around does not seem to be ideal; i.e. instead of having the hardware keeping counts of the encoder inputs; it will have to be handled by interrupts.

Please correct me if I miss understood:
1. The MCPWM.1 errata affects quadrature encoder inputs and this has not been fixed yet.
2. The work around is inefficient compared with the intended hardware solution.

Do we know if this problem can be fix by future software updates?  When can we expect this problem to be fixed?

thanks a lot,
Chu-Yin



Quote: NXP_Europe
Hi Tom,
Browsing through your code, I directly spotted the problem.
First I'd like to refer to the LPC1700 Errata sheet:
http://ics.nxp.com/support/documents/microcontrollers/pdf/errata.lpc176x.pdf
Chapter 3.6 MCPWM.1: Input pins (MCI0-2) on the Motor Control PWM peripheral are not functional.

Actually it states that the interrupts using the CAP pins on the MCPWM doesn't always fire an interrupt. Exactly your problem
Since you were using the example available sampe code, I figured you saw that in the code. I used GPIOs instead of the CAPs of the MCPWM to generate the HALL interrupts

Initialization:
            /*-- HALL input settings using GPIO interrupts --*/
            
            /* Disable interrupt for MCPWM */
            /* preemption = 1, sub-priority = 1 */
            NVIC_DisableIRQ(EINT3_IRQn);
            NVIC_SetPriority(EINT3_IRQn, ((0x01<<3)|0x01));
            
            /* General nSD settings, FUNC0, No OpenDrain, Tristate */
            PinCfg.Funcnum         = PINSEL_FUNC_0;
            PinCfg.OpenDrain     = PINSEL_PINMODE_NORMAL;
            PinCfg.Pinmode         = PINSEL_PINMODE_TRISTATE;
            PinCfg.Portnum         = PINSEL_PORT_0;
            
            /* Initialize P0_4 as HALL1, FUNC1, No opendrain, Tristate */
            PinCfg.Pinnum         = PINSEL_PIN_4;
            PINSEL_ConfigPin(&PinCfg);
            
            /* Initialize P0_5 as HALL2, FUNC1, No opendrain, Tristate */
            PinCfg.Pinnum         = PINSEL_PIN_5;
            PINSEL_ConfigPin(&PinCfg);
            
            /* Initialize P0_6 as HALL3, FUNC1, No opendrain, Tristate */
            PinCfg.Pinnum         = PINSEL_PIN_6;
            PINSEL_ConfigPin(&PinCfg);
            
            /* Set HALL1 .. HALL3 to input */
            FIO_SetDir(0, 1<<4, 0);
            FIO_SetDir(0, 1<<5, 0);    
            FIO_SetDir(0, 1<<6, 0);
            
            /* Clear all pending interrupt */
            LPC_GPIOINT->IO0IntClr = (0xFFFFFFFF);
            /* Enable the rising edge interrupts on GPIO */
            LPC_GPIOINT->IO0IntEnR |= (0x7<<4);
            /* Enable the falling edge interrupts on GPIO */
            LPC_GPIOINT->IO0IntEnF |= (0x7<<4);
Interrupt:
/**
 *  @brief
 *          This function handles the GPIO P0.4..6 interrupts.
 *          The HALL sensor is connected to the P0.4..6 inputs
 *          and will cause a pattern which determines the next commutation pattern.
 *     @param[in]    None
 *     @return        None
 */
void EINT3_IRQHandler (void)
{
    unsigned long regVal;

    regVal = LPC_GPIOINT->IntStatus;

    /* check whether GPIO0 was the interrupt source */
    if (regVal & 1<<0) 
    {
        /* Get the GPIO Falling edge interrupt source */
        regVal = LPC_GPIOINT->IO0IntStatF; 
         
        /* Do only once per Mechanical revolution a RPM calculation */
        if (regVal & 1<<4)
        {
            /* Calculate the RPM */
            vBLDC_CalcRPM (&Motor);

        }

        /* Get the GPIO Falling edge interrupt source */
        regVal = LPC_GPIOINT->IO0IntStatR;
        
        /* Fetch & Calculate the Current of Phase W @ Hall 1 Rising edge */
        if (regVal & 1<<4)
        {
            Motor.CurrU = u32ANALOG_GetIU_Volt();
            Motor.CurrU = i32ANALOG_CalcI(Motor.CurrU_REF, Motor.CurrU);
        }

        /* Fetch & Calculate the Current of Phase W @ Hall 2 Rising edge */
        if (regVal & 1<<5)
        {
            Motor.CurrW = u32ANALOG_GetIW_Volt();
            Motor.CurrW = i32ANALOG_CalcI(Motor.CurrW_REF, Motor.CurrW);    
        }

        /* Clear all pending interrupt */
        LPC_GPIOINT->IO0IntClr = (0x7<<4);

        /* Read the current hall value 1..6 */
        Motor.HALstate = (GPIO_ReadValue(0) & 0x7<<4) >> 4;
    
        /* Fetch the commutation step from Commutation table */
        Motor.CMT_step = CMT_tbl[Motor.Direction][Motor.HALstate];
    
        /* Commutate the motor */
        vBLDC_Commutate(Motor.CMT_step);


    }
}
Please find attached a Keil project I'm working on right now for BLDC control with the LPC1700. In here I also do sinusiodal (or SVPWM) control. And there are some nice tricks with fixed point calculations.
Owww.. you should also use the PID in this code, much better than I had earlier..

Hope this helps you out!
Kind regards,

0 Kudos
Reply

3,658 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by tgalluzzo on Tue Aug 30 18:33:25 MST 2011
Thanks for all the help. I have finally had a chance to try the interrupt on port 0 and it works much better!
0 Kudos
Reply

3,658 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by NXP_Europe on Tue Aug 09 01:38:56 MST 2011
Hi Tom,
Have had a look for you.
Here are some signals you can use to get the Hall sensors @ P0:
Connect the following lines on JP6:
C-PWMA-FB1 (P0.4) <-> E-SENSE1-A
C-PWMA-FB2 (P0.5) <-> E-SENSE1-B
C-PWMD-L (P0.6) <-> E-SENSE1-C

This also have the nice advantage that @ GPIO interrupt, with reading the status of the GPIO you directly have the correct hall value.
HallState = ((LPC_GPIO0->FIOPIN >> 4) & 0x7);
Don't forget to de-solder the C to E side connections on JP6 for these signals!

Kind regards,
0 Kudos
Reply

3,658 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by tgalluzzo on Mon Aug 08 03:30:42 MST 2011
Hello,

I really appreciate the follow-up. I haven't had a chance to try this yet. I didn't see an easy way to connect the hall sensors to port 0 or 2 on our embedded artist motor control development board. I've had to work on other things in the mean time. Any suggestions might help.

Thanks,
Tom
0 Kudos
Reply

3,658 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by NXP_Europe on Sun Aug 07 23:44:12 MST 2011
Hi Tom,
And any luck with the suggestions I gave you?

Kind regards,
0 Kudos
Reply

3,658 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by tgalluzzo on Thu Aug 04 08:30:50 MST 2011
This is great news! I can't wait to try it!

Thanks
Tom
0 Kudos
Reply

3,658 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by NXP_Europe on Thu Aug 04 08:02:05 MST 2011
Hi Tom,
Browsing through your code, I directly spotted the problem.
First I'd like to refer to the LPC1700 Errata sheet:
http://ics.nxp.com/support/documents/microcontrollers/pdf/errata.lpc176x.pdf
Chapter 3.6 MCPWM.1: Input pins (MCI0-2) on the Motor Control PWM peripheral are not functional.

Actually it states that the interrupts using the CAP pins on the MCPWM doesn't always fire an interrupt. Exactly your problem
Since you were using the example available sampe code, I figured you saw that in the code. I used GPIOs instead of the CAPs of the MCPWM to generate the HALL interrupts

Initialization:
            /*-- HALL input settings using GPIO interrupts --*/
            
            /* Disable interrupt for MCPWM */
            /* preemption = 1, sub-priority = 1 */
            NVIC_DisableIRQ(EINT3_IRQn);
            NVIC_SetPriority(EINT3_IRQn, ((0x01<<3)|0x01));
            
            /* General nSD settings, FUNC0, No OpenDrain, Tristate */
            PinCfg.Funcnum         = PINSEL_FUNC_0;
            PinCfg.OpenDrain     = PINSEL_PINMODE_NORMAL;
            PinCfg.Pinmode         = PINSEL_PINMODE_TRISTATE;
            PinCfg.Portnum         = PINSEL_PORT_0;
            
            /* Initialize P0_4 as HALL1, FUNC1, No opendrain, Tristate */
            PinCfg.Pinnum         = PINSEL_PIN_4;
            PINSEL_ConfigPin(&PinCfg);
            
            /* Initialize P0_5 as HALL2, FUNC1, No opendrain, Tristate */
            PinCfg.Pinnum         = PINSEL_PIN_5;
            PINSEL_ConfigPin(&PinCfg);
            
            /* Initialize P0_6 as HALL3, FUNC1, No opendrain, Tristate */
            PinCfg.Pinnum         = PINSEL_PIN_6;
            PINSEL_ConfigPin(&PinCfg);
            
            /* Set HALL1 .. HALL3 to input */
            FIO_SetDir(0, 1<<4, 0);
            FIO_SetDir(0, 1<<5, 0);    
            FIO_SetDir(0, 1<<6, 0);
            
            /* Clear all pending interrupt */
            LPC_GPIOINT->IO0IntClr = (0xFFFFFFFF);
            /* Enable the rising edge interrupts on GPIO */
            LPC_GPIOINT->IO0IntEnR |= (0x7<<4);
            /* Enable the falling edge interrupts on GPIO */
            LPC_GPIOINT->IO0IntEnF |= (0x7<<4);
Interrupt:
/**
 *  @brief
 *          This function handles the GPIO P0.4..6 interrupts.
 *          The HALL sensor is connected to the P0.4..6 inputs
 *          and will cause a pattern which determines the next commutation pattern.
 *     @param[in]    None
 *     @return        None
 */
void EINT3_IRQHandler (void)
{
    unsigned long regVal;

    regVal = LPC_GPIOINT->IntStatus;

    /* check whether GPIO0 was the interrupt source */
    if (regVal & 1<<0) 
    {
        /* Get the GPIO Falling edge interrupt source */
        regVal = LPC_GPIOINT->IO0IntStatF; 
         
        /* Do only once per Mechanical revolution a RPM calculation */
        if (regVal & 1<<4)
        {
            /* Calculate the RPM */
            vBLDC_CalcRPM (&Motor);

        }

        /* Get the GPIO Falling edge interrupt source */
        regVal = LPC_GPIOINT->IO0IntStatR;
        
        /* Fetch & Calculate the Current of Phase W @ Hall 1 Rising edge */
        if (regVal & 1<<4)
        {
            Motor.CurrU = u32ANALOG_GetIU_Volt();
            Motor.CurrU = i32ANALOG_CalcI(Motor.CurrU_REF, Motor.CurrU);
        }

        /* Fetch & Calculate the Current of Phase W @ Hall 2 Rising edge */
        if (regVal & 1<<5)
        {
            Motor.CurrW = u32ANALOG_GetIW_Volt();
            Motor.CurrW = i32ANALOG_CalcI(Motor.CurrW_REF, Motor.CurrW);    
        }

        /* Clear all pending interrupt */
        LPC_GPIOINT->IO0IntClr = (0x7<<4);

        /* Read the current hall value 1..6 */
        Motor.HALstate = (GPIO_ReadValue(0) & 0x7<<4) >> 4;
    
        /* Fetch the commutation step from Commutation table */
        Motor.CMT_step = CMT_tbl[Motor.Direction][Motor.HALstate];
    
        /* Commutate the motor */
        vBLDC_Commutate(Motor.CMT_step);


    }
}
Please find attached a Keil project I'm working on right now for BLDC control with the LPC1700. In here I also do sinusiodal (or SVPWM) control. And there are some nice tricks with fixed point calculations.
Owww.. you should also use the PID in this code, much better than I had earlier..

Hope this helps you out!
Kind regards,
0 Kudos
Reply

3,658 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by tgalluzzo on Thu Aug 04 07:38:25 MST 2011
Thanks for this excellent support! Here is my project for lpcxpresso 4.
0 Kudos
Reply

3,658 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by NXP_Europe on Thu Aug 04 05:45:17 MST 2011
Hi Tom,
My apologies for the delayed response.
Well actually I unfortunately don't have a up and running port of a LPC1700 BLDC control under LPCXpresso just yet.
I actually would like to ask you whether you could perhaps post your code (the full project). This will help a lot in getting your problem fixed.
If the code is too large for the forum, please let me know and I'll send you an e-mail to where you can send the code to..

Kind regards..
0 Kudos
Reply

3,658 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by tgalluzzo on Tue Aug 02 16:59:30 MST 2011
Just wondering if you can suggest anymore ideas for me to try? or if you are able to reproduce this?

Is there a chance there could be something wrong with my 1769? Should I buy a new one to try?

Thanks!
0 Kudos
Reply

3,658 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by tgalluzzo on Mon Aug 01 17:47:44 MST 2011
Just as a reference piece of information. I've run the "BLDC_Sensored" demo code that comes with the motor control development kit on the lpc1114 that also comes with the kit, and this issue does not appear.

I'm wondering if perhaps there are compilation settings that I should change for my lpc1769 test code? I'm just trying to brainstorm ideas for what could be wrong. If you have an lpc1769 and can reproduce my issue please let me know.

Tom
0 Kudos
Reply

3,658 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by tgalluzzo on Mon Aug 01 17:13:15 MST 2011
Hi, thank you for your support! I have tried your suggestions but unfortunately they have not fixed the issue. I've tried to simplify the code down as much as possible in an effort to fix the issue, but still I notice this small twitching. It is very subtle with the motor that comes with the Motor Control Development Board (you may have to hold the motor in your hand while it is running to feel it), but still it is clear that there is a glitch in the commutation. Also, the glitches get worse if the motor is driven faster. This is the code I am using now, whether I'm using this code or the full demonstration code the resultant behavior is the same:

#include "LPC17xx.h"
#include "lpc/lpc17xx_nvic.h"
#include "lpc/lpc17xx_gpio.h"
#include "lpc/lpc17xx_pinsel.h"
#include "lpc/lpc17xx_libcfg.h"
#include "lpc/lpc17xx_timer.h"
#include "lpc/lpc17xx_clkpwr.h"
#include "lpc/lpc17xx_pinsel.h"
#include "lpc/lpc17xx_mcpwm.h"
#include "BLDC.h"
#include <cr_section_macros.h>
#include <NXP/crp.h>

__CRP const unsigned int CRP_WORD = CRP_NO_CRP ;

/* Pin configuration data */
/** Motor Control Channel 0 Output A */
const PINSEL_CFG_Type mcpwm_mco0a_pin[1] = {{1, 19, 1, 0, 0}};
/** Motor Control Channel 0 Output B */
const PINSEL_CFG_Type mcpwm_mco0b_pin[1] = {{1, 22, 1, 0, 0}};

/** Motor Control Channel 1 Output A */
const PINSEL_CFG_Type mcpwm_mco1a_pin[1] = {{1, 25, 1, 0, 0}};
/** Motor Control Channel 1 Output B */
const PINSEL_CFG_Type mcpwm_mco1b_pin[1] = {{1, 26, 1, 0, 0}};

/** Motor Control Channel 2 Output A */
const PINSEL_CFG_Type mcpwm_mco2a_pin[1] = {{1, 28, 1, 0, 0}};
/** Motor Control Channel 2 Output B */
const PINSEL_CFG_Type mcpwm_mco2b_pin[1] = {{1, 29, 1, 0, 0}};

/** Motor Control Feed Back Channel 0 - MCI0 */
const PINSEL_CFG_Type mcpwm_mcfb0_pin[1] = {{1, 20, 1, 0, 0}};
/** Motor Control Feed Back Channel 1 - MCI1 */
const PINSEL_CFG_Type mcpwm_mcfb1_pin[1] = {{1, 23, 1, 0, 0}};
/** Motor Control Feed Back Channel 2 - MCI2 */
const PINSEL_CFG_Type mcpwm_mcfb2_pin[1] = {{1, 24, 1, 0, 0}};

/** Motor Control Low-active abort feed back */
const PINSEL_CFG_Type mcpwm_mcabort_pin[1] = {{ 1, 21, 0, 0, 0}};

/** @brief MCPWM Channel configuration data */
MCPWM_CHANNEL_CFG_Type channelsetup[3];

/** @brief Capture configuration data */
MCPWM_CAPTURE_CFG_Type captureCfg;


/** Motor controlling structure */
volatile PIDstr PID_Motor
= {
/* PID_Motor.p           */ 4,
/* PID_Motor.i           */ 10,
/* PID_Motor.d           */ 0,
/* PID_Motor.sp          */ 1500,
/* PID_Motor.pv          */ 0,
/* PID_Motor.err[3]      */ {0,0,0},
/* PID_Motor.mv          */ 0,
/* PID_Motor.HALstate    */ 0,
/* PID_Motor.CMT_CNT     */ 0,
/* PID_Motor.CMT_step    */ 0,
/* PID_Motor.RPM         */ 0,
/* PID_Motor.Enable      */ DISABLE,
/* PID_Motor.Direction   */ CW,
/* PID_Motor.Brake       */ ENABLE,
/* PID_Motor.Period      */ 900,
/* PID_Motor.Poles       */ 1,
/* PID_Motor.Tick_cur    */ 0,
/* PID_Motor.Tick_old    */ 0,
/* PID_Motor.Tick_new    */ 0
};

/** Commutation table, connected to hall sensor */
volatile uint8_t CMT_tbl[2][8] = {{0xF, 2, 0, 1, 4, 3, 5, 0xF},
{0xF, 5, 3, 4, 1, 0, 2, 0xF} };

volatile unsigned char activationPattern[8] = { MCPWM_PATENT_A0 | MCPWM_PATENT_B1,
MCPWM_PATENT_A2 | MCPWM_PATENT_B1,
MCPWM_PATENT_A2 | MCPWM_PATENT_B0,
MCPWM_PATENT_A1 | MCPWM_PATENT_B0,
MCPWM_PATENT_A1 | MCPWM_PATENT_B2,
MCPWM_PATENT_A0 | MCPWM_PATENT_B2,MCPWM_PATENT_B0 | MCPWM_PATENT_B1 | MCPWM_PATENT_B2,
0
 };
/**
 * @briefInitialize the system to control the Brushless DC motor
 */
void BLDC_Init (void)
{
int i = 0;
    /* MCPWM INITIALIZATION *******************************************/
    /* Initializes pin corresponding to MCPWM function */
    PINSEL_ConfigPin((PINSEL_CFG_Type *)&mcpwm_mco0a_pin[0]);
    PINSEL_ConfigPin((PINSEL_CFG_Type *)&mcpwm_mco0b_pin[0]);
    PINSEL_ConfigPin((PINSEL_CFG_Type *)&mcpwm_mco1a_pin[0]);
    PINSEL_ConfigPin((PINSEL_CFG_Type *)&mcpwm_mco1b_pin[0]);
    PINSEL_ConfigPin((PINSEL_CFG_Type *)&mcpwm_mco2a_pin[0]);
    PINSEL_ConfigPin((PINSEL_CFG_Type *)&mcpwm_mco2b_pin[0]);
    PINSEL_ConfigPin((PINSEL_CFG_Type *)&mcpwm_mcabort_pin[0]);

/* Configure the MCPWM Feedback pins to be CAP inputs */
    PINSEL_ConfigPin((PINSEL_CFG_Type *)&mcpwm_mcfb0_pin[0]);
    PINSEL_ConfigPin((PINSEL_CFG_Type *)&mcpwm_mcfb1_pin[0]);
    PINSEL_ConfigPin((PINSEL_CFG_Type *)&mcpwm_mcfb2_pin[0]);
    /* Disable interrupt for MCPWM */
    NVIC_SetPriority(MCPWM_IRQn, ((0x01<<3)|0x01));
    NVIC_DisableIRQ(MCPWM_IRQn);

    /* Init MCPWM peripheral */
    MCPWM_Init(LPC_MCPWM);

    for(i = 0; i < 3; i++)
    {
        /* MCPWM Channel i setup */
        channelsetup.channelType = MCPWM_CHANNEL_EDGE_MODE;
        channelsetup.channelPolarity = MCPWM_CHANNEL_PASSIVE_LO;
        channelsetup.channelDeadtimeEnable = DISABLE;
        channelsetup.channelDeadtimeValue = 0;
        channelsetup.channelUpdateEnable = ENABLE;
        channelsetup.channelTimercounterValue = 0;
        channelsetup.channelPeriodValue = PID_Motor.Period;
        channelsetup.channelPulsewidthValue = 200;

        /* Apply the setup to the channels */
        MCPWM_ConfigChannel(LPC_MCPWM, i, &channelsetup);

        captureCfg.captureChannel = i;
captureCfg.captureFalling = ENABLE;
captureCfg.captureRising = ENABLE;
captureCfg.hnfEnable = DISABLE;
captureCfg.timerReset = DISABLE;
MCPWM_ConfigCapture(LPC_MCPWM, i, &captureCfg);
    }
}

/**
 *@brief
 * This function enables the BLDC motor control by setting the MCPWM in the
 *right mode, enable the capturing and starting the MCPWM
 */
void BLDC_Enable (void)
{
    /* Enable the MCPWM DC mode and all output pins */
    MCPWM_DCMode(LPC_MCPWM, ENABLE, DISABLE, ( 0 ));

    /* Set the PWM output value */
    channelsetup[0].channelPulsewidthValue = 100;

    /* Write to the shadow register */
    MCPWM_WriteToShadow(LPC_MCPWM, 0, &channelsetup[0]);

    /* Disable LIM0 and MAT0 interrupt flag */
    MCPWM_IntConfig(LPC_MCPWM, MCPWM_INTFLAG_LIM0, DISABLE);
    MCPWM_IntConfig(LPC_MCPWM, MCPWM_INTFLAG_MAT0, DISABLE);

    /* Disable LIM1 and MAT1 interrupt flag */
    MCPWM_IntConfig(LPC_MCPWM, MCPWM_INTFLAG_LIM1, DISABLE);
    MCPWM_IntConfig(LPC_MCPWM, MCPWM_INTFLAG_MAT1, DISABLE);

    /* Disable LIM2 and MAT2 interrupt flag */
    MCPWM_IntConfig(LPC_MCPWM, MCPWM_INTFLAG_LIM2, DISABLE);
    MCPWM_IntConfig(LPC_MCPWM, MCPWM_INTFLAG_MAT2, DISABLE);

    /* Enable interrupt for capture event on MCI0 (MCFB0)  */
    MCPWM_IntConfig(LPC_MCPWM, MCPWM_INTFLAG_CAP0, ENABLE);

    /* Enable interrupt for capture event on MCI0 (MCFB1) */
    MCPWM_IntConfig(LPC_MCPWM, MCPWM_INTFLAG_CAP1, ENABLE);

    /* Enable interrupt for capture event on MCI0 (MCFB2) */
    MCPWM_IntConfig(LPC_MCPWM, MCPWM_INTFLAG_CAP2, ENABLE);

/* Enable interrupt for MCPWM  */
    NVIC_EnableIRQ(MCPWM_IRQn);

    /* Start up the MCPWM */
    MCPWM_Start(LPC_MCPWM, ENABLE, ENABLE, ENABLE);

    /* Disable the brake */
    PID_Motor.Brake = DISABLE;

    /* Enable the motor */
    PID_Motor.Enable = ENABLE;

    // Enable Motor Control Board Output
GPIO_SetDir(1, 0x200000, GPIO_DIR_OUT);
GPIO_ClearValue(1, 0x200000);
}

void BLDC_setDuty (unsigned int duty)
{
/* Set the PWM output value */
channelsetup[0].channelPulsewidthValue = duty;
    /* Write to the shadow register */
MCPWM_WriteToShadow(LPC_MCPWM, 0, &channelsetup[0]);
}

/**
 *  @brief  This function handles the MCPWM feedback interrupts.
 *          The HALL sensor is connected to the MCPWM feedback inputs
 *          and will cause a pattern which determines the next commutation pattern.
 */
void MCPWM_IRQHandler (void)
{
/* Clear pending interrupt */
MCPWM_IntClear(LPC_MCPWM, MCPWM_INTFLAG_CAP0);
MCPWM_IntClear(LPC_MCPWM, MCPWM_INTFLAG_CAP1);
MCPWM_IntClear(LPC_MCPWM, MCPWM_INTFLAG_CAP2);

/* Read the current HALL sensor input */
unsigned char pinstat = 0;
pinstat = (GPIO_ReadValue(1) & 1<<20) >> 20;
pinstat |= (GPIO_ReadValue(1) & 3<<23) >> 22;
PID_Motor.CMT_step = pinstat;

// Commutate the motor
    LPC_MCPWM->MCCCP = activationPattern[CMT_tbl[0][pinstat]];
return;
}

int main(void)
{
SystemInit();
SystemCoreClockUpdate();

BLDC_Init();
BLDC_Enable();
BLDC_setDuty(PID_Motor.Period/4); // Set the motor power to 75%
MCPWM_IRQHandler(); // Startup the motor

while (1);
return 1;
}


I am fairly certain that this is some kind of issue with the interrupt for the external hall sensors, because when I call the interrupt routine from within my main loop the issue completely goes away and the motor also feels to run very smoothly. For example:

while (1)
{
MCPWM_IRQHandler();
}


Based on what I've read about the external interrupts, the ISR should be called in less than a microsecond, so it should be able to keep up with the hall triggering. I have o-scoped the hall sensor triggering frequency to be about 1khz, when the motor is running at test speed. It seems like the code inside the ISR is simple enough to finish in under 1ms.

Please let me know your thoughts and suggestions. We really want to use the NXP lpcxpresso in our products, and we want to make sure we're using everything correctly. I appreciate all of your help!
0 Kudos
Reply

3,658 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by NXP_Europe on Mon Aug 01 01:11:58 MST 2011
Hi Tom,
While developing the application note, I came to similar  problems. At the end it seemed to be the PI-controller in combination  with the RPM calculation.

To make sure that the PI controller  isn't the problem, you can fix the mv-output of the PI controller by  putting the following line in the [B][FONT=Courier New][SIZE=2]BLDC_commutate[/SIZE][/FONT][/B] function.
PID_Motor.mv = PID_Motor.Period/2;
This line is probably already in there but commented out.

The  second thing that created glitches in my commutation while developing  the code for the LPCXpresso Motor Control Kit LPC1100 example was the  RPM calculation. It used the Systick as reference, but after using a  regular timer in free-running mode, it glitches were not present  anymore.

Setting up the Free running counter:
    /* FREE RUNNING COUNTER CONFIGURATION *****************************/
    CLKPWR_ConfigPPWR (CLKPWR_PCONP_PCTIM0, ENABLE);
    CLKPWR_SetPCLKDiv (CLKPWR_PCLKSEL_TIMER0, CLKPWR_PCLKSEL_CCLK_DIV_4);

    /* Set the prescaler to get never get an overflow */
    LPC_TIM0->PR = 1;
    /* RESET timer16 1  */
    LPC_TIM0->TCR = 1<<1;
    /* ENABLE timer16 1 */
    LPC_TIM0->TCR = 1;
Do the RPM calculation
void vBLDC_CalcRPM (MOTOR_TypeDef *ptr)
{
    volatile uint32_t currVal = LPC_TIM0->TC, deltaTicks = 0, tmp = SystemCoreClock;

    /* Get the number of timer ticks passed since last RPM
     * calculation */
    if (currVal > ptr->Tick_old)
        deltaTicks = currVal - ptr->Tick_old;
    else
        deltaTicks = 0xFFFFFFFF - (ptr->Tick_old - currVal);

    /* Convert the timer value to a RPM value */
    ptr->RPM = (((uint32_t)(tmp/4)/LPC_TIM0->PR)* 60 )/(deltaTicks * ptr->Poles);            // convert to RPM

    /* Store the currVal for next RPM Calc */
    ptr->Tick_old = currVal;
}
:

I hope this fix your problem, if not please let me know and we can take a look at the interrupts.

Kind regards,
0 Kudos
Reply

3,658 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by tgalluzzo on Sat Jul 30 19:46:13 MST 2011
Indeed it appears as though the interrupt routine is not able to respond 100% reliably to the hall sensor change. I tested this by changing my main() program loop to dedicate itself to only commutate the motor. The code looks like this:


// Main loop
while (1)
{
/* Read the current HALL sensor input */
unsigned char pinstat = 0;
pinstat = (GPIO_ReadValue(1) & 1<<20) >> 20;
pinstat |= (GPIO_ReadValue(1) & 3<<23) >> 22;
PID_Motor.CMT_step = pinstat;

// Commutate the motor
BLDC_commutate(CMT_tbl[0][pinstat]);
}

When I do this I see no twitching of the motor and I have confirmed this with an O-scope that the PWM pattern is stable and consistent relative to the oscillation of the hall sensor signal. Whereas with the interrupt based technique it is not at all.

Is there a way I can give the interrupt routine a higher priority than the main program loop? I like the interrupt based technique, so if it's possible to make it run better I would prefer it.

Thanks
Tom
0 Kudos
Reply

3,658 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by tgalluzzo on Sat Jul 30 17:08:56 MST 2011
Hello,

Thank you for the suggestion. Yes I have visited the website and in fact the code that I'm running comes from that application note.

I've added some code to the IRQ handler for the hall sensor based commutation. This code determines if the hall sensor state is consistent with the pattern sequence that we expect to see as the motor progresses through its revolution. The code is telling me that the sometimes the hall sensors appear to be "skipping" steps. I don't think there is anything wrong with the hardware, because I've tested two different motors from different manufacturers and I get the same result. I'm thinking that perhaps the interrupt latency is too high or inconsistent? Could this issue be causing the motor to twitch? Or is this a common thing?

Thanks,
Tom
0 Kudos
Reply

3,658 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by NXP_Europe on Sat Jul 30 15:38:50 MST 2011
Hello tgalluzzo,

did you check the NXP-website?

... see:

see AN10898
http://ics.nxp.com/support/documents/microcontrollers/?search=bldc&locale=en%2C%2CLatn&type=appnote
0 Kudos
Reply