FRDM-K20D50M-Accelerometer Interrupt issue

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

FRDM-K20D50M-Accelerometer Interrupt issue

Jump to solution
3,005 Views
Amit_Kumar1
Senior Contributor II

Hi

I am trying to interface MMA8451Q accelerometer present in the FRDM-K20D50M board, I am having issues with the Interrupt. The accelerometer is working fine but somehow my interrupt is not being triggered. I have tried according to the specification given in the datasheet. but it is not working. I am able to poll data without interrupt but  I want to extract it only when interrupt is triggered when data ready generates the interrupts, I have changed the polarity of interrupt to active high and also to push-pull configuration , and have set the data ready flag to interrupt 1 pin. I have attached the code along with accelerometer datasheet and FRDM user manual , please go through it and let me know where I am doing something wrong, I have tried with all interrupt configuration i.e rising edge, falling edge, both edge, high level, low level but it is not going in ISR function. If simultaneously I poll the data then I can see the interrupt pin changing the polarity on oscilloscope but still not entering the ISR. My program hangs when I configure the interrupt to low level.  Thanks in advance.

 

KInd Regards

Amit Kumar

Original Attachment has been moved to: Accelerometer.zip

1 Solution
1,763 Views
mjbcswitzerland
Specialist V

Amit

There are still a couple of problems with the code. As I mentioned, I removed the I2C reads from the port interrupt routine so I didn't have such difficulties.

The code that you have uses an interrupt driven I2C driver that is blocking so if you use it in an interrupt routine it will hang if the interrupt routine that you are calling it from has a higher priority than the interrupt that the I2C driver has.

Since your code is setting the interrupt level of each used interrupts to 8 it can't work - the I2C driver will be waiting for its own interrupt to occur which can't be executed because it is operating from an interrupt with the same priority level - it will therefore hang forever. To avoid this you need to set the port interrupt's priority lower than the I2C interrupt's (that is, a higher value): NVICIP42 = NVIC_IP_PRI42(0x90); will do it.

The second problem is that when the port interrupt is set to high level edge trigger it causes an immediate interrupt to occur (even though there was no physical edge). This causes your interrupt routine to read from the I2C bus before the I2C interface has been initialised. There are various methods to improve this but a simple one is to modify the routine INTR1_Init() to call

  PORTC_ISFR = PORT_ISFR_ISF(0x40);

after it has set the value of PORTC_PCR6 (to make the pin an input with rising edge interrupt).

The PE generated port initialisation clears pending interrupts before enabling the interrupt but also immediately generates its own interrupt when it configures the port setting (if the input is already at a high level).

I wouldn't necessarily say that the PE generated code is wrong but basically the complete design is more the issue since it is all a bit of a mash-up which needs some extra control to ensure the overall behaviour.

In any case the code will then be able to run even when you use a "blocking" interrupt driven I2C driver waiting for its own interrupt from within another interrupt...(I don't see the actual point in having an interrupt driven driver that blocks but maybe that is what you selected (?)).

Regards

Mark

View solution in original post

18 Replies
1,763 Views
mjbcswitzerland
Specialist V

Hi

Attached is a binary for the FRDM-K20M50 (from the uTasker project) which configures the accelerometer for a falling edge interrupt on IRQ1. This generates a 50Hz interrupt. The accelerometer values are written to the debug interface (virtual COM on the debug interface at 115kBaud) twice a second. With this code loaded you can monitor the interrupt signal operation to verify that your HW is OK and perform a register dump with the debugger to check the configuration of registers of interest.

There are a two points to note:

1. The jumper J17 must be mounted on the board (not normally present) so that the interrupt is connected to the processor. If not, the interrupt input will be floating - without configuring a pull-up on the port his will result in it being at logic level '0'. In this case a low level sensitive interrupt would be present indefinitely.

2. When the accelerometer is operating in this mode it will assert the interrupt output when it has completed a data conversion and the output will only be reset when the data is read via I2C.

This means that if the accelerometer is operating and the board is reset, the interrupt output will assert and remain asserted until the device is read again. For this reason it is important to read the data from the device during initialisation to ensure that the interrupt is reset (for example, check whether the logic level on the IRQ is in the asserted state and perform a dummy read if this is the case - or simply perform a dummy read at each start).

I loaded your code to my board and see that input PC6 (IRQ1) looks to be correctly configured as rising edge interrupt (matching the acceleromenter IRQ setting). I think that you are in fact getting one interrupt edge (after a power up) but this may not be handled correctly (or missed) because it remains asserted forever. Further resets of the board mean that the interrupt remains asserted and so no longer triggers (see point 2).

I would also enable the interrupts "before" you configure the accelerometer because it looks like your I2C driver is blocking and so only returns after the command has been sent to the accelerometer. It may be that the first rising edge takes place before you have configured the port interrupt and if it is configured too late you will never get interrupted.

Furthermore I notice that your port interrupt handler contain I2C commands and printf() ouputs which I would personally defer to outside of the IRQs in a final system design.

Regards

Mark

P.S. You may find the following thread of interest since it explains how I2C bus lockup can be avoided when using the accelerometer (valid generally for I2C devices that have no reset input): I2C device dead-lock recovery

http://www.utasker.com/kinetis.html

1,763 Views
Amit_Kumar1
Senior Contributor II

Hi Mark

Thanks a lot for your response. I checked the points you brought forward. And tried to make it work. But the problem is still not resolved. What I did was

1. Checked J17 I already soldered it from the bottom

2. I Initiated interrupts just after the PE_low_level_init(); as u already mentioned I can see interrupt for 1 time and then it is completely being asserted.

3. I am reading the registers once just before the for(;;) loop.  Status and interrupt source registers are 0x00.

Regarding the binary code which is attached in your reply: I dumped that code on probing on PORTC11 (int1) and PORTC6(int2), I got the following trace

TEK0000.JPG.jpg

channel 1(yellow) is connected to PTC11(INT1), channel 2 (blue) is connected to PTC6(INT2).

I can see the green LED blinking.

The output from the terminal window is coming once i.e.

Untitled.jpg

Please guide me for next steps . Thanks

Kind Regards

Amit Kumar

0 Kudos
1,763 Views
Amit_Kumar1
Senior Contributor II

Hi Mark

After loading the code initially I didn't disconnect the board, so after disconnecting and reconnecting the board I got the following waveform Channel 1(yellow) is INT1(PTC11) and channel 2 (blue) is INT2(PTC6)

TEK0000.JPG.jpg

and on resetting the board through the reset switch I got the following o/p in terminal window

Untitled.jpg

But the o/p came only once. Now what you suggest for me to do next.?

Kind Regards

Amit Kumar

0 Kudos
1,763 Views
mjbcswitzerland
Specialist V

Hi Amit

In your diagram channel 2 (blue) is INT1 (PTC6).

You should find that the pulse repeast every 20ms (50Hz) whereby the pulse width is the time until the data is read via I2C, resetting the interrupt source

If you hit the enter key on the debug port you will see a menu - there is an I2C sub-menu (eneter the menu number 7 plus return to move to it) where you can command "acc_on" so that the values are displayed every 2 seconds - "acc_off" to disable.

In the case of your code I don't know wheher the interrupt is being handled or whether the data is not being read correcly so that the interrupt is not being cleared - this will require you verifiying these two points to idenify what is not happening or what is not correct. As reference I can show the code that is running in the uTasker project. This is using an interrupt driven I2C driver and a simple OS so the code is based on a simple state-event machines. You should get the idea and can certainly verify the I2C messages being used. Perhaps you notice something.

Regards

Mark

// Definition of I2C messages (reads and wries)

//

static const unsigned char ucSetAccelerometerAddress[] = {MMA8451Q_WRITE_ADDRESS, ACC_START_ADDRESS}; // command to set address to read to the start address

static const unsigned char ucReadAccelerometerRegisters[] = {ACC_READ_LENGTH, MMA8451Q_READ_ADDRESS, OWN_TASK}; // command to start a read the defined amount of bytes with the task scheduled when the read has completed

static const unsigned char ucSetAccelerometerRead[] = {MMA8451Q_WRITE_ADDRESS, 0}; // command to set address to read to the first register address (status)

static const unsigned char ucReadAccelerometerState[] = {4, MMA8451Q_READ_ADDRESS, OWN_TASK}; // command to start a read the defined amount of bytes with the task scheduled when the read has completed

static const unsigned char ucRouteIrq[] = {MMA8451Q_WRITE_ADDRESS, ACC_CONTROL_REGISTER_5, (ACC_CONTROL_REGISTER_5_INT_1_DATA_RDY)}; // route interrupt to IRQ1

static const unsigned char ucConfigureIrq[] = {MMA8451Q_WRITE_ADDRESS, ACC_CONTROL_REGISTER_4, (ACC_CONTROL_REGISTER_4_INT_EN_DATA_RDY)}; // command to enable interrupt

static const unsigned char ucSetAccelerometerMode[] = {MMA8451Q_WRITE_ADDRESS, ACC_CONTROL_REGISTER, (ACC_CONTROL_REGISTER_ACTIVE | ACC_CONTROL_REGISTER_DATA_RATE_50Hz | ACC_CONTROL_REGISTER_SLEEP_RATE_6_25Hz | ACC_CONTROL_REGISTER_LNOISE | ACC_CONTROL_REGISTER_F_READ)}; // command to set the mode

// State machine states

//

#define ACC_INITIALISING              0

#define ACC_X_Y_Z                     1                                  // reading X,Y,Z data

#define ACC_WAITING                   2                                  // waiting for accelerometer to interrupt

#define ACC_TRIGGERED                 3                                  // accelerometer has interrupted so we should read data

#define ACC_MAGNETOMETER              4                                  // reading magnetic data

static int iAccelerometerState = ACC_INITIALISING;                       // state machine variable

// Initialisation (executeted once to start)

//

static void fnConfigIIC_Interface(void)

{

    IICTABLE tIICParameters;

    tIICParameters.Channel = OUR_IIC_CHANNEL;

    tIICParameters.usSpeed = 50;                                         // 50k

    tIICParameters.Rx_tx_sizes.TxQueueSize = 64;                         // transmit queue size

    tIICParameters.Rx_tx_sizes.RxQueueSize = 64;                         // receive queue size

    tIICParameters.Task_to_wake = 0;                                     // no wake on transmission

    if ((IICPortID = fnOpen(TYPE_IIC, FOR_I_O, &tIICParameters)) !=0) {  // open the channel with defined configurations

        fnWrite(IICPortID, (unsigned char *)ucSetAccelerometerAddress, sizeof(ucSetAccelerometerAddress)); // write the register address to read from

        fnRead(IICPortID, (unsigned char *)ucReadAccelerometerRegisters, 0); // start the read process of the required amount of bytes

    }

}

// Accelerometer data ready (interrupt routine)

//

static void acc_data_ready(void)

{

    if (ACC_WAITING == iAccelerometerState) {                            // if expecting an interrupt

        iAccelerometerState = ACC_TRIGGERED;                             // signal that data is ready and should be read

        uTaskerStateChange(OWN_TASK, UTASKER_GO);                        // schedule the task to handle the new state and request the daa

    }

}

// State-event machine for the accelerometer in interrupt driver mode

// - executed on events (I2C message ready or interrupt scheduled)

//

...

    switch (iAccelerometerState) {

    case ACC_INITIALISING:                                               // initial register dump ready

        if (fnRead(IICPortID, ucInputMessage, ACC_READ_LENGTH) != 0) {   // if the read has completed

            int i = 0;

            int iLine;

            fnDebugMsg("3-axis accelerometer:\r\n");

            while (i < ACC_READ_LENGTH) {

                for (iLine = 0; iLine < 15; iLine++) {

                    fnDebugHex(ucInputMessage[i], (sizeof(ucInputMessage[i]) | WITH_LEADIN | WITH_SPACE)); // display the received register contents

                    if (++i >= ACC_READ_LENGTH) {

                        break;

                    }

                }

                fnDebugMsg("\r\n");

            }

            // We now set the operating mode

            //

            {                                                            // configure a falling edge sensitive interrupt in IRQ1 output

                INTERRUPT_SETUP interrupt_setup;                         // interrupt configuration parameters

                interrupt_setup.int_type       = PORT_INTERRUPT;         // identifier to configure port interrupt

                interrupt_setup.int_handler    = acc_data_ready;         // handling function

                interrupt_setup.int_priority   = PRIORITY_PORT_C_INT;    // interrupt priority level

                interrupt_setup.int_port       = PORTC;                  // the port that the interrupt input is on

                interrupt_setup.int_port_bits  = PORTC_BIT6;             // the IRQ input connected (SWITCH_1 on TWR_K60N512)

                interrupt_setup.int_port_sense = (IRQ_FALLING_EDGE | PULLUP_ON); // interrupt is to be falling edge sensitive

                fnConfigureInterrupt((void *)&interrupt_setup);          // configure interrupt

            }

            iAccelerometerState = ACC_WAITING;                           // waiting for interrupts from the accelerometer

            fnWrite(IICPortID, (unsigned char *)ucRouteIrq, sizeof(ucRouteIrq)); // route interrupt output to IRQ1

            fnWrite(IICPortID, (unsigned char *)ucConfigureIrq, sizeof(ucConfigureIrq)); // configure interrupt

            fnWrite(IICPortID, (unsigned char *)ucSetAccelerometerMode, sizeof(ucSetAccelerometerMode)); // write the operating mode

            if (_READ_PORT_MASK(C, PORTC_BIT6) == 0) {                   // if the accelerometer is already signalling that it has data (it is possible that it was previouly operating and has data ready, signaled by the interrupt line laread being low)

                acc_data_ready();                                        // start an initial read

            }

        }

        break;

    case ACC_WAITING:                                                    // ignore in this state

        break;

    case ACC_TRIGGERED:                                                  // accelerometer has indicated that there is data to be read

        iAccelerometerState = ACC_X_Y_Z;

        fnWrite(IICPortID, (unsigned char *)ucSetAccelerometerRead, sizeof(ucSetAccelerometerRead)); // write the register address to read

        fnRead(IICPortID, (unsigned char *)ucReadAccelerometerState, 0); // start the read process of the status

        break;

    case ACC_X_Y_Z:                                                      // we are expecting status data from the accelerometer to arrive

        if (fnRead(IICPortID, ucInputMessage, 4) != 0) {                 // if the status read has completed

            static int iDisplayRate = 0;

            #define ACC_DISPLAY_FILTER  25

            iAccelerometerState = ACC_WAITING;

            if (++iDisplayRate > ACC_DISPLAY_FILTER) {

                if (iAccelOutput != 0) {

                    int i = 0;

                    fnDebugMsg("3-axis state:");                         // display the status on a regular basis

                    while (i < 4) {                                      // display 4 values

                        fnDebugHex(ucInputMessage[i], (sizeof(ucInputMessage[i]) | WITH_LEADIN | WITH_SPACE)); // display the received register contents

                        i++;

                    }

                    fnDebugMsg("\r\n");

                }

                iDisplayRate = 0;

            }

        }

        break;

    }

...

1,763 Views
Amit_Kumar1
Senior Contributor II

Hi Mark

Thanks for the code. Many things are cleared now . I had some queries regarding my code.

1. Plz check the configuration of the I2C if it is matching with yours. As once I had issue with a gyro that I was trying to operate at higher frequency and at that frequency(500KHz) I was getting the value WHO_AM_I register correctly but was unable to configure other registers and get the readings out. when I changed to 180KHz then it worked fine. I don't know with this accelerometer.

Untitled.jpg

2. In the datasheet, for each register 2 register address is given (Register Address and Auto-Increment Address). Here I am using Register Address. am I reading the correct register address? I checked with the WHO_AM_I register It i giving the correct value on 0x0D register.

1.jpg

Thanks

Amit Kumar

0 Kudos
1,763 Views
mjbcswitzerland
Specialist V

Amit

It is possible to read with up to 400kHz I2C clock.

In the uTasker code 8 bit mode is used and so F_READ is set to 1 so that the read of 4 bytes from the address 0x00 causes the values from 0x00, 0x01, 0x03 and 0x05 to be returned, and the internal pointer automatically be set back to 0x00. This avoids having to set the internal pointer again each time a read is performed.

Regards

Mark

1,763 Views
Amit_Kumar1
Senior Contributor II

Hi Mark

I thing there is a huge bug in Processor Expert software. While debugging, my code is hanging in the interrupt initialization. I think I have done all the things as per the specification given in the datasheet and it shouldn't be so difficult to configure an interrupt driven accelerometer using I2C. I think processor expert team should go through the components as I am making this code run on a standard h/w FDM-K20D50M. and more importantly your code is working on it. So I guess there are some issues with the PE itself. Attached is the code where during the initialization my program is hanging on interrupts.

Kind Regards

Amit Kumar

0 Kudos
1,763 Views
mjbcswitzerland
Specialist V

Amit

The attachment looks to be corrupted so can't be opened.

Regards

Mark

0 Kudos
1,762 Views
Amit_Kumar1
Senior Contributor II

Hi Mark

I am extremely sorry for the inconvenience. Please have a look at the attachment attached in this reply. Also I tried extracting the data using GI2C1_ReadAddress(add, 0x01, 1, &data[0], 6); where

add = 0x1D (Acc address)

0x01 --> MSB_X address

1--> size of MSB_X_Address in bytes

data--> uint8_t data[6];

6--> total no. of elements in  the array(to be extracted)

Using this function I am not getting the exact data which I am getting when extracting each address individually.

For the interrupt pins I tried disconnecting the board and reconnecting it (for power on reset) but still my code hanged in interrupt pins initialization function. 

0 Kudos
1,762 Views
mjbcswitzerland
Specialist V

Amit

I checked your code.

The first problem is that INTR1 and INTR2 are incorrectly defined. INTR2 is in fact INT1 on the board (or from the accelerometer) so the wrong interrupt routine is being used.

If you use INTR2_OnInterrupt() instead of INTR1_OnInterrupt() the interrupt arrives.

The second problem is that the port interrupts are configured for falling edge whereas they should be rising edge.

With these corrections it runs, although I removed the read from the IRQ to the main loop (reacting instead on a flag set in the IRQ).

Regards

Mark

1,762 Views
Amit_Kumar1
Senior Contributor II

Hi mark

I tried with the changes u mentioned i.e

1. I changed the interrupt1 pin to -->PTC6.

2. Changed the falling edge to rising edge and checked.

in case of rising edge my program is still stucking in the  intr1 = INTR1_Init(NULL);

Can there be a hardware issue? I checked J16 and J17 as u mentioned in earlier post. I am attaching the updated program which was supposed to work according to you. Check if it is working in your hardware.

Regards

Amit Kumar


0 Kudos
1,764 Views
mjbcswitzerland
Specialist V

Amit

There are still a couple of problems with the code. As I mentioned, I removed the I2C reads from the port interrupt routine so I didn't have such difficulties.

The code that you have uses an interrupt driven I2C driver that is blocking so if you use it in an interrupt routine it will hang if the interrupt routine that you are calling it from has a higher priority than the interrupt that the I2C driver has.

Since your code is setting the interrupt level of each used interrupts to 8 it can't work - the I2C driver will be waiting for its own interrupt to occur which can't be executed because it is operating from an interrupt with the same priority level - it will therefore hang forever. To avoid this you need to set the port interrupt's priority lower than the I2C interrupt's (that is, a higher value): NVICIP42 = NVIC_IP_PRI42(0x90); will do it.

The second problem is that when the port interrupt is set to high level edge trigger it causes an immediate interrupt to occur (even though there was no physical edge). This causes your interrupt routine to read from the I2C bus before the I2C interface has been initialised. There are various methods to improve this but a simple one is to modify the routine INTR1_Init() to call

  PORTC_ISFR = PORT_ISFR_ISF(0x40);

after it has set the value of PORTC_PCR6 (to make the pin an input with rising edge interrupt).

The PE generated port initialisation clears pending interrupts before enabling the interrupt but also immediately generates its own interrupt when it configures the port setting (if the input is already at a high level).

I wouldn't necessarily say that the PE generated code is wrong but basically the complete design is more the issue since it is all a bit of a mash-up which needs some extra control to ensure the overall behaviour.

In any case the code will then be able to run even when you use a "blocking" interrupt driven I2C driver waiting for its own interrupt from within another interrupt...(I don't see the actual point in having an interrupt driven driver that blocks but maybe that is what you selected (?)).

Regards

Mark

1,762 Views
Amit_Kumar1
Senior Contributor II

Hi Mark

one more Issue I had which I mention above . i.e GI2C1_ReadAddress(add, 0x01, 1, &data[0], 6); is not working

I wanted to extract the x,y,z values using auto increment function. currently I am extracting all the values one by one. In auto increment the value being received does not match with the values extracted individually. I tried with little Endian configuration which our uc is.

Please Check the issue. Thanks in advance

Kind Regards

Amit Kumar

0 Kudos
1,762 Views
mjbcswitzerland
Specialist V

Amit

GI2C1_ReadAddress(add, 0x01, 1, &data[0], 6);


This use in incorrect and also results in a warning when compiled. The read address parameter must be a pointer to the address (this is not logical but that is the way that the routine is defined). I would also suggest reading 7 bytes from address 0 so that also the status register is read - but don't know whether that makes any difference in regards to resetting the interrupt or not.

Please note that I don't work for Freescale and so if you believe it is worth requesting a correction in the user's manual to the board you can create a service request so that it doesn't get overlooked. Also I don't use PE for my own work but support cases to get to know problems that people have with such work (typical usage, typical board problems, typical peripheral difficulties, typical mistakes, etc.) so that I can better support uTasker users where I am involved with many industrial project developments based on various Kinetis parts, where development efficiency and operational reliability is high priority. As seen in this case PE can be used to generate some building blocks, but whether they are suitable for a particular system depends on various factors and one still needs a good system level understanding and knowledge of the components' operation. I needed about 1 hour to develop and prove an interrupt driven accelerometer in the uTasker project which was many times quicker than the time needed to work out what was wrong with a generated solution, which may have been generated a bit faster but proved to be only the start of the struggle.

Regards

Mark



1,762 Views
Amit_Kumar1
Senior Contributor II

Hi Mark

Thanks for your unconditional support. Your support really helped me a lot and I hope this post will benefit many other developers.

Kind Regards

Amit Kumar 

0 Kudos
1,762 Views
Amit_Kumar1
Senior Contributor II

Hi Mark

thanks a lot, It worked !!!! I just change the interrupt priority (I increased the priority of external INT1(PTC6) to 15 from 8(default). On changing the priority level it worked.

Thanks a Lot for your support. I request you to raise a request to update the User manual as any new user who wants to work on FRDM board will first access the User manual as over there the RGB LED, Accelerometer connection, temperature sensor, ambient light sensor, touch interface etc.. all are easily available for starting.  Atleast they will get benefited.

Regards

Amit Kumar

0 Kudos
1,762 Views
Amit_Kumar1
Senior Contributor II

Hi mark

According to the user manual of FRDM-K20D50M i.e  http://cache.freescale.com/files/microcontrollers/doc/user_guide/FRDM-K20D50MUM.pdf?fasp=1&WT_TYPE=U...  on page 14

1.jpg

Do you mean that it is documentation issue or in program I am defining it in a wrong way ? Since there is no test pads near accelerometer for INT pins so I cannot probe it and confirm.

I just saw it is mentioned in the schematic but I think this issue should be corrected in the user manual also.

4.jpg

2.jpg

3.jpg

For rising and falling edge actually I was trying with various combination of interrupt polarity.

I will correct according to you and will update it.

Thanks & Regards

Amit Kumar

0 Kudos
1,762 Views
mjbcswitzerland
Specialist V

Amit

The code is enabling the interrupt on the accelerometer's IRQ1 output. The schematic shows this to be connected to PC6 and this is the input that works for me. This suggests to me that the user's manual is at fault. Generally you need to question everything that you read (in user manuals, schematics and wherever else since there is always a possibility of a typo, mis-matching revision etc.) - a quick measurement to confirm can save lots of time in some cases.

Regards

Mark

0 Kudos