GPIO interrupt lose count from quadrature decoder

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

GPIO interrupt lose count from quadrature decoder

1,186 Views
petershih
Contributor III

Hi :

 

I am working on a custom board equipped with K66 MCU, three quadrature decoders and other peripherals. Since three quadrature decoders, I am going to utilized FTM1, and 2 for two devices, and general gpio interrupt for the other. The software development is based on KDS3.0 and KSDK1.2. The first thing I would like to do is verifying the integrity of GPIO interrupt from encoder. I basically jump the FTM1_QD_PHA and FTM1_QD_PHB signals to PTE8, and PTE9, and a motor encoder signal connects to FTM1. Therefore, the same encoder signal should feed to both FTM1 and GPIO port E. When I test it with bare board and SDK platform only, the had pretty match count from both decoder. This gives me confirmation that FTM and GPIO interrupt and decoder algorithm are running OK. However, if I have similar code and utilize SDK with MQX, I am facing lose count on GPIO interrupt. For example I ran the motor about 3 seconds (the encoder signals tick in at 6.64kHz, 150us period), the FTM reads 61438 count, but GPIO interrupt counts 60967.  I am wondering if I miss something, or this is drawback of utilizing MQX. Does MQX take lots of CPU resource that cause losing interrupt? If you have any idea or recommendation please let me know. Thank you very much.

 

main.c without MQX

// Standard C Included Files #include <stdio.h> // SDK Included Files #include "board.h" #include "gpio_pins.h" #include "fsl_debug_console.h" #include "fsl_device_registers.h" #include "fsl_ftm_driver.h" #include "fsl_ftm_hal.h"  //----------------------------------------------------------------------- // Function Prototypes //----------------------------------------------------------------------- void gpio_Init(void); void encoderX_Init(void); void encoderZ_Init(void); void FlexTimer1_IRQHandler(void); uint8_t ScanEcn(uint8_t InputAB, uint8_t PreviousStateAB, int64_t *EncCnt); void BOARD_ENCZ_IRQ_HANDLER(void); int64_t GetEncXCnt(void);  //----------------------------------------------------------------------- // Constants //----------------------------------------------------------------------- #define    ENCX_OVERFLOW        4000    //1000 CPR(cycle/rev) #define    ENCY_OVERFLOW        4000    //1000 CPR(cycle/rev)  //----------------------------------------------------------------------- // Typedefs //-----------------------------------------------------------------------  //----------------------------------------------------------------------- // Global Variables //----------------------------------------------------------------------- int64_t                EncXOF = 0; int64_t                PosX = 0; int64_t                PosZ = 0; int64_t                PosXPV = 0; int64_t                PosZPV = 0; volatile  uint8_t   stateOld = 0;  //----------------------------------------------------------------------- // Main Function //----------------------------------------------------------------------- int main(void) {     /* Write your code here */     // Init hardware     hardware_init();     dbg_uart_init();      PRINTF("\r\n\nRunning QEncIntTest example.");      gpio_Init();     encoderX_Init();     encoderZ_Init();     PosXPV = GetEncXCnt();     PosXPV = PosZPV = PosZ;      /* This for loop should be replaced. By default this loop allows a single stepping. */     for (;;) {         PosX = GetEncXCnt();          if (PosX != PosXPV || PosZ != PosZPV) {             PRINTF("\r\nPosX = %ld, PosZ = %ld", (int32_t)PosX, (int32_t)PosZ);             PosXPV = PosX;             PosZPV = PosZ;         }     }     /* Never leave main */     return 0; }  void gpio_Init(void) {     gpio_output_pin_user_config_t     outputPinConfig;      outputPinConfig.pinName = kGpioE0;     outputPinConfig.config.outputLogic = 0;     outputPinConfig.config.slewRate = kPortSlowSlewRate;     outputPinConfig.config.isOpenDrainEnabled = false;     outputPinConfig.config.driveStrength = kPortLowDriveStrength;     GPIO_DRV_OutputPinInit(&outputPinConfig); }  void encoderX_Init(void) {     FTM_Type *ftmBase;      // Configure FTM1,2 encoder pins     ftmBase = g_ftmBase[BOARD_ENCX_FTM_INSTANCE];     configure_ftm_pins(BOARD_ENCX_FTM_INSTANCE);      ftm_user_config_t flexTimer1_InitConfig0 = {       .tofFrequency      = 0U,       .isWriteProtection = false,                // Disable write protection       .BDMMode           = kFtmBdmMode_11,       .syncMethod        = kFtmUseSoftwareTrig     };     FTM_DRV_Init(BOARD_ENCX_FTM_INSTANCE,&flexTimer1_InitConfig0);      FTM_HAL_SetMod(ftmBase, ENCX_OVERFLOW-1);     FTM_HAL_SetCounterInitVal(ftmBase, 0);      FTM_DRV_SetClock(BOARD_ENCX_FTM_INSTANCE, kClock_source_FTM_SystemClk, kFtmDividedBy1);    //start the timer clock, source is the the phase A and B input signals     ftm_phase_params_t flexTimer1_QdConfig0 = {       .kFtmPhaseInputFilter = false,       .kFtmPhaseFilterVal = 0U,       .kFtmPhasePolarity = kFtmQuadPhaseNormal,     };     FTM_DRV_QuadDecodeStart(BOARD_ENCX_FTM_INSTANCE, &flexTimer1_QdConfig0, &flexTimer1_QdConfig0, kFtmQuadPhaseEncode);     FTM_DRV_SetTimeOverflowIntCmd(BOARD_ENCX_FTM_INSTANCE,true);     FTM_DRV_SetFaultIntCmd(BOARD_ENCX_FTM_INSTANCE,false); }  void encoderZ_Init(void) {     GPIO_DRV_Init(encoderZPins, NULL);    //configure QD_PHB and QD_PHB signal }  /* FTM1 IRQ handler that would cover the same name's APIs in startup code. */ extern void FTM_DRV_IRQHandler(uint32_t instance); void FTM1_IRQHandler(void) {     if((FTM1_QDCTRL & FTM_QDCTRL_TOFDIR_MASK) == 0)         EncXOF--;     else         EncXOF++;      FTM_DRV_IRQHandler(1); }  uint8_t ScanEcn(uint8_t InputAB, uint8_t PreviousStateAB, int64_t *EncCnt) {          switch (PreviousStateAB){            case 0x0:                    if(InputAB == 0x1){                          (*EncCnt)++;                    }else if(InputAB == 0x2){                           (*EncCnt)--;                    }                    break;            case 0x1:                        if(InputAB == 0x0){                           (*EncCnt)--;                    }else if(InputAB == 0x3){                       (*EncCnt)++;                    }                    break;            case 0x2:                        if(InputAB == 0x0){                       (*EncCnt)++;                    }else if(InputAB == 0x3){                           (*EncCnt)--;                    }                    break;            case 0x3:                         if(InputAB == 0x1){                           (*EncCnt)--;                    }else if(InputAB == 0x2){                       (*EncCnt)++;                    }                    break;            default: break;            }          return InputAB; } /* PORTE IRQ handler that would cover the same name's APIs in startup code. */ void PORTE_IRQHandler(void) {     uint8_t        state;      if (PORTE_PCR8 & PORT_PCR_ISF_MASK) {    //interrupt on A changing state         PORTE_PCR8 |= PORT_PCR_ISF_MASK;    // clear the flag         state = (uint8_t)((GPIOE_PDIR & 0x00000300) >> 8);         stateOld = ScanEcn(state, stateOld, &PosZ);     }      if (PORTE_PCR9 & PORT_PCR_ISF_MASK) {    //interrupt on B changing state         PORTE_PCR9 |= PORT_PCR_ISF_MASK;    // clear the flag         state = (uint8_t)((GPIOE_PDIR & 0x00000300) >> 8);         stateOld = ScanEcn(state, stateOld, &PosZ);     } }  int64_t GetEncXCnt(void) {     return ((ENCX_OVERFLOW * EncXOF) + FTM1_CNT); }

 

 

main.c without MQX

//----------------------------------------------------------------------- // Standard C/C++ Includes //----------------------------------------------------------------------- #include <stdio.h> #include "main.h"  void main_task(uint32_t param); void task_example(task_param_t param); void gpio_Init(void); void encoderX_Init(void); void encoderZ_Init(void); void FlexTimer1_IRQHandler(void); uint8_t ScanEcn(uint8_t InputAB, uint8_t PreviousStateAB, int64_t *EncCnt); void BOARD_ENCZ_IRQ_HANDLER(void); int64_t GetEncXCnt(void);  //----------------------------------------------------------------------- // Constants //----------------------------------------------------------------------- #define MAIN_TASK              8U  const TASK_TEMPLATE_STRUCT  MQX_template_list[] = {    { MAIN_TASK, main_task, 0xC00, 20, "main_task", MQX_AUTO_START_TASK},    { 0L,        0L,        0L,    0L,  0L,         0L } }; #define TASK_EXAMPLE_PRIO            6U #define TASK_EXAMPLE_STACK_SIZE   1024U  #define    ENCX_OVERFLOW        4000    //1000 CPR(cycle/rev) #define    ENCY_OVERFLOW        4000    //1000 CPR(cycle/rev)  //----------------------------------------------------------------------- // Global Variables //----------------------------------------------------------------------- int64_t                EncXOF = 0; int64_t                PosX = 0; int64_t                PosZ = 0; int64_t                PosXPV = 0; int64_t                PosZPV = 0; volatile  uint8_t   stateOld = 0;  //----------------------------------------------------------------------- // Macros //----------------------------------------------------------------------- OSA_TASK_DEFINE(task_example, TASK_EXAMPLE_STACK_SIZE);  //----------------------------------------------------------------------- // Main Function //-----------------------------------------------------------------------  void main_task(uint32_t param) {     osa_status_t result = kStatus_OSA_Error;     printf("\n\nRunning the KSDK_1_2_0_PCB58852 project.");      gpio_Init();     encoderX_Init();     encoderZ_Init();     PosXPV = GetEncXCnt();     PosZ = PosXPV;     PosZPV = PosXPV;  //    OSA_Init(); //    result = OSA_TaskCreate(task_example, //                    (uint8_t *)"example", //                    TASK_EXAMPLE_STACK_SIZE, //                    task_example_stack, //                    TASK_EXAMPLE_PRIO, //                    (task_param_t)0, //                    false, //                    &task_example_task_handler); //    if (result != kStatus_OSA_Success) //    { //        printf("Failed to create example task\r\n"); //        return; //    }  //    OSA_Start();      for (;;)                                         // Forever loop     {         PosX = GetEncXCnt();          if (PosX != PosXPV || PosZ != PosZPV) {             printf("\nPosX = %ld, PosZ = %ld", (int32_t)PosX, (int32_t)PosZ);             PosXPV = PosX;             PosZPV = PosZ;         }          OSA_TimeDelay(100);        //500ms delay     } } //----------------------------------------------------------------------- // Task Functions //----------------------------------------------------------------------- // //void task_example(task_param_t param) //{ //    printf("\r\nHellow World.\r\n"); // //    while(1) //    { //        __asm("NOP"); //    } //}
Labels (1)
0 Kudos
7 Replies

793 Views
DavidS
NXP Employee
NXP Employee

Hi Peter,

Is your GPIO input interrupt configured for edge or level (i.e. low level or high level) trigger?

An edge interrupt will remain valid until the ISR is cleared.  A level interrupt goes away if it reverts back to non-asserted level.

Are the FTM and GPIO interrupts the same or different priority level?

In summary:

Edge gpio is persistent.

Level gpio is non-persistent.

Regards,

David

0 Kudos

793 Views
petershih
Contributor III

Hi David:

The GPIO interrupt is edge interrupted. As you can see in the code, I clear the interrupt flag before reading the port for process. I didn't specified the interrupt priority for both. I will assume it stay the same as MQX specified in the default.

Since you mentioned about priority. I have tried declear GPIO interrupt priority NVIC_SetPriority(PORTE_IRQn, 2U); in the encoderZ_Init(). I see match reading in a very short time, after a while the software stopped. If I set priority to 4. it doesn't help! But the software continue run without problem. As my understanding, priority 2 is the highest priority in the MQX default. Let me know if any other though. Thanks!

pastedImage_0.png

Peter Shih

0 Kudos

793 Views
DavidS
NXP Employee
NXP Employee

Hi Peter,

Did you resolve this?  If yes, how?

If no, you can always insert your ISR directly into the vector table (called a gorilla interrupt) that will preempt the RTOS.

This is OK as long as you do not make RTOS calls.  So use a global variable to keep track of count.

Regards,

David

0 Kudos

793 Views
petershih
Contributor III

Hi David:

Appreciate your feedback. I am sorry replying so late. The problem is still not resolved yet. Actually, I was working with Freescale support group regarding this issue. However, I haven't receive any positive feedback yet.

You mentioned a good point. It's related with interrupt. As the source code enclosed, I though I did replace the port E interrupt with a custom interrupt service routine to process the count (PosZ), and the count is declared as a global variable. I am not sure if I understand your recommendation clearly. Could you give me more detail though or any sample code?  Thanks your feedback.

0 Kudos

793 Views
DavidS
NXP Employee
NXP Employee

Hi Peter,

Sorry for my delay.

I have had another customer with similar issue not using MQX.  When he dug into the cause it was disabling global interrupts for too long which caused the missing edges.

Regards,

David

0 Kudos

793 Views
petershih
Contributor III

Hi David:

Thanks your information. I don't have any global interrupt disabling in my code. Of cause, I don't have any idea if MQX does this.

Best Regards,

Peter Shih

0 Kudos

793 Views
Alice_Yang
NXP TechSupport
NXP TechSupport

Hello Peter,

" Due to the MQX RTOS interrupt handling mechanism, the names of the ISR entry functions must be different than those in the CMSIS startup code file (startup_<device>.S), otherwise the kernel ISR is not called when the corresponding interrupt triggers. "

These  words is from the DOC of my colleague Jorge Gonzalez  :

Interrupt handling with KSDK and Kinetis Design Studio

I think you can check this , maybe it can help you .

Hope it helps

-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------

Have a great day,
Alice

0 Kudos