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
已解决! 转到解答。
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
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
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
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.
Please guide me for next steps . Thanks
Kind Regards
Amit Kumar
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)
and on resetting the board through the reset switch I got the following o/p in terminal window
But the o/p came only once. Now what you suggest for me to do next.?
Kind Regards
Amit Kumar
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;
}
...
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.
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.
Thanks
Amit Kumar
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
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
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.
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
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
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
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
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
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
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
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.
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
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