K20 LDD I2C always busy

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

K20 LDD I2C always busy

Jump to solution
4,656 Views
thatonesaxkid
Contributor II

Hi,

I'm pretty new to Kinetis chips and Processor Expert and I was having some trouble with using the LDD I2C components. When I'm writing to I2C, it seems like it will never finish writing successfully. I believe I set the I2C component to run at about 95 kHz. Not sure if I set up the component incorrectly in Processor Expert (I don't think so because I was following a tutorial I saw) or I if my user code is erroneous. If anybody could take a look and give some comments that would be greatly appreciated. Thanks!

/*lint -save  -e970 Disable MISRA rule (6.3) checking. */

int main(void)

/*lint -restore Enable MISRA rule (6.3) checking. */

{

  /* Write your local variable definition here */

  LDD_TError myError;

  uint32_t myUserData;

  LDD_TDeviceData* myDeviceData;

  LDD_TData* myDataBufferPtr;

  uint8_t number = 0xFF;

  /*** Processor Expert internal initialization. DON'T REMOVE THIS CODE!!! ***/

  PE_low_level_init();

  /*** End of Processor Expert internal initialization.                    ***/

  /* Write your code here */

  myDataBufferPtr = &number;

  myDeviceData = CI2C1_Init(&myUserData);

  if(CI2C1_Enable(myDeviceData) !=  ERR_OK) // Enable I2C, trap potential errors

  while(1);

  CI2C1_MasterSendBlock(myDeviceData, myDataBufferPtr, 2U, LDD_I2C_SEND_STOP);

  while(1){

  myError = CI2C1_MasterSendBlock(myDeviceData, myDataBufferPtr, 2U, LDD_I2C_SEND_STOP);

  switch(myError){

  case ERR_OK: while(1); break;

  case ERR_DISABLED: while(1); break;

  case ERR_SPEED: while(1); break;

  case ERR_BUSY: break;

  default: while(1);

  }

  }

I can attach my whole project if that would be useful to anybody.

1 Solution
2,416 Views
mjbcswitzerland
Specialist V

Christopher

Do you have pull-ups on the I2C bus lines?

If not, the bus will always be busy.

Regards

Mark

View solution in original post

15 Replies
2,416 Views
thatonesaxkid
Contributor II

Hi Mark,

I have found and fixed the issue after many trials and tribulations. The key to figuring it out was your mention that the I2C bus would be busy without pull-up registers. I was very confused because if I plugged in the K20D50M  USB port, I could watch the SCA and SDL on and oscilloscope and would see the whole entire message sent out, including the address, ack bits, data bits, even the stop bit by the master. However, when I would run the code in debug (with the OpenSDA USB port also connected to the computer), no interrupt ever fired, and only the address was sent (because it is not sent in an interrupt). It turns out that on the Arduino shield I was using (Adafruit Learning System), PB2 and PB3 are connected to PB0 and PB1, respectively. I was using PB2 and PB3 for I2C. Searching through Freescale's K20D50M board schematics led me to find that there is a signal (the significance of I have yet to find out) that uses PB0. This signal, which in turn was connected also to PB2, led the I2C peripheral to say the the bus was busy. When I clipped the header at PB0, suddenly everything worked, and I could suddenly debug and see the whole message succesfully. Thank you so much for your help in this! This may or may not be applicable to others, but they should note that if they choose to use PB0 and PB1 for I2C, the OpenSDA port may interfere with whatever they are trying to do.

Regards,

Chris

0 Kudos
2,416 Views
joephaneuf
Contributor I

I was having the same trouble (using k20D50), but found a different underlying problem, thought I'd share my solution in case anybody else is having this issue.

I found that the serial flag (DeviceDataPrv->SerFlag) wasn't getting cleared promptly after calling MasterSendBlock(), so MasterReceiveBlock() kept throwing an ERR_BUSY, despite using the help files' recommended DataTransmittedFlg check, and the I2C modules CheckBus() before calling MasterReceiveBlock().

Ended up adding a check in the I2C CheckBus() function to verify that the MASTER_IN_PROGRES bit in SerFlag was cleared.

Still seems weird, as the ISR appears to try to clear that flag before calling OnMasterBlockSent(), which is how the DataTransmittedFlg is updated.  But it works.

0 Kudos
2,416 Views
ise1
Contributor III

Hi Joe !
I have the same problem: the serial flag (DeviceDataPrv->SerFlag) is not clear after calling MasterSendBlock().
I don't understand how you fix the problem because I2C CheckBus() is never called.
So I've decided to call this function in the MasterSendBlock but the second parameter is BusStatePtr and this is not recognized because not in the MasterSendBlock() parameters.
So did you redefine the MasterSendBlock function ?

So can you explain step by step what you've changed in these functions ?
Thanks in advance :smileywink:

0 Kudos
2,416 Views
adriancano
NXP Employee
NXP Employee

Hi,

Where exactly your code stops if you debug it?

It would be better to see the configuration of the component, if you can please paste a picture. Also you can have a little help with the common usage information that Processor Expert has: right click on the name of the component then select Help on component after that a new window will pop up and you will see there the option Component I2C_LDD Typical Usage there you can find some common usage of this component and the code.

Hope this information can help you.

Kind Regards,

Adrian

0 Kudos
2,416 Views
thatonesaxkid
Contributor II

Hi Adrian,

I couldn't find a screen that included all of the component settings, so I included the I2C init method which PE generated for me. When I ran my code on the hardware and stopped it, it never would get caught in the while(1); loops in the switch statement, meaning that CI2C1_MasterSendBlock(myDeviceData, myDataBufferPtr, 2U, LDD_I2C_SEND_STOP) always returned ERR_BUSY.

LDD_TDeviceData* CI2C1_Init(LDD_TUserData *UserDataPtr)

{

  /* Allocate HAL device structure */

  CI2C1_TDeviceData *DeviceDataPrv;

  /* {Default RTOS Adapter} Driver memory allocation: Dynamic allocation is simulated by a pointer to the static object */

  DeviceDataPrv = &DeviceDataPrv__DEFAULT_RTOS_ALLOC;

  DeviceDataPrv->UserData = UserDataPtr; /* Store the RTOS device structure */

  DeviceDataPrv->SerFlag = ADDR_7;     /* Reset all flags start with 7-bit address mode */

  DeviceDataPrv->EnUser = TRUE;        /* Enable device */

  DeviceDataPrv->SlaveAddr = 0x00U;    /* Set variable for slave address */

  DeviceDataPrv->SendStop = LDD_I2C_SEND_STOP; /* Set variable for sending stop condition (for master mode) */

  DeviceDataPrv->OutLenM = 0x00U;      /* Set zero counter of data of transmission */

  /* SIM_SCGC4: I2C0=1 */

  SIM_SCGC4 |= SIM_SCGC4_I2C0_MASK;

  /* I2C0_C1: IICEN=0,IICIE=0,MST=0,TX=0,TXAK=0,RSTA=0,WUEN=0,DMAEN=0 */

  I2C0_C1 = 0x00U;                     /* Clear control register */

  /* I2C0_S: TCF=0,IAAS=0,BUSY=0,ARBL=0,RAM=0,SRW=0,IICIF=1,RXAK=0 */

  I2C0_S = I2C_S_IICIF_MASK;           /* Clear interrupt flag */

  /* PORTB_PCR3: ISF=0,MUX=2 */

  PORTB_PCR3 = (uint32_t)((PORTB_PCR3 & (uint32_t)~(uint32_t)(

                PORT_PCR_ISF_MASK |

                PORT_PCR_MUX(0x05)

               )) | (uint32_t)(

                PORT_PCR_MUX(0x02)

               ));

  PORT_PDD_SetPinOpenDrain(PORTB_BASE_PTR, 0x03u, PORT_PDD_OPEN_DRAIN_ENABLE); /* Set SDA pin as open drain */

  /* PORTB_PCR2: ISF=0,MUX=2 */

  PORTB_PCR2 = (uint32_t)((PORTB_PCR2 & (uint32_t)~(uint32_t)(

                PORT_PCR_ISF_MASK |

                PORT_PCR_MUX(0x05)

               )) | (uint32_t)(

                PORT_PCR_MUX(0x02)

               ));

  PORT_PDD_SetPinOpenDrain(PORTB_BASE_PTR, 0x02u, PORT_PDD_OPEN_DRAIN_ENABLE); /* Set SCL pin as open drain */

  /* I2C0_C2: GCAEN=0,ADEXT=0,HDRS=0,SBRC=0,RMEN=0,AD=0 */

  I2C0_C2 = I2C_C2_AD(0x00);

  /* I2C0_FLT: ??=0,??=0,??=0,FLT=0 */

  I2C0_FLT = I2C_FLT_FLT(0x00);        /* Set glitch filter register */

  /* I2C0_SMB: FACK=0,ALERTEN=0,SIICAEN=0,TCKSEL=0,SLTF=1,SHTF1=0,SHTF2=0,SHTF2IE=0 */

  I2C0_SMB = I2C_SMB_SLTF_MASK;

  /* I2C0_F: MULT=0,ICR=0x22 */

  I2C0_F = (I2C_F_MULT(0x00) | I2C_F_ICR(0x22)); /* Set prescaler bits */

  HWEnDi(DeviceDataPrv);               /* Enable/disable device according to status flags */

  /* Registration of the device structure */

  PE_LDD_RegisterDeviceStructure(PE_LDD_COMPONENT_CI2C1_ID,DeviceDataPrv);

  return ((LDD_TDeviceData *)DeviceDataPrv); /* Return pointer to the data data structure */

}

/*

** ===================================================================

**     Method      :  CI2C1_Enable (component I2C_LDD)

*/

/*

0 Kudos
2,416 Views
mjbcswitzerland
Specialist V

Christopher

Can the code be run on a standard Freescale board? If it can it is easiest and faster to post the project as a zip attachment so that it can be loaded to a board and usually the reason can be seen and explained.

Regards

Mark

0 Kudos
2,416 Views
thatonesaxkid
Contributor II

Absolutely. I've been messing with it for a bit, so the project I'm attaching is a bit different than it was, but I'm still having the same problem (That the I2C bus remains busy after the first attempt at transmission). I got this code from the typical usage example found in PE's Help window for the I2C_LDD component.


Note: I'm running it on a Freedom K20D50M board. I had been using Keil to debug and write my code after generating the code with Processor Expert, however, I'm fairly sure it's not an issue with how I'm using Keil, so use whatever IDE you want, you just may have to change the compiler option for the Cpu in PE.

Many thanks for your help!

Chris

0 Kudos
2,416 Views
mjbcswitzerland
Specialist V

Christopher

I couldn't import the CW project so I simply loaded the Keil binary to the board and checked what was happening in disassembly mode. This is rather more difficult than debugging in the source code so I may not be fully correct but I think that I see two problems, but not directly with the I2C peripheral/driver. I didn't see that it was blocked in a busy state or similar.

1. The first thing that I see is that there are two variabels called DataTransmittedFlg and DataReceivedFlg.

According to the code these are initialised with

volatile bool DataReceivedFlg = FALSE;

volatile bool DataTransmittedFlg = FALSE;

FALSE is 0 so I would expect to see these with this valeu when the code starts main() but they have the value 0xff - in fact there are 6 long words of 0xffffffff in the area where I expect these to be set with zero.

It looks like there is a Keil variable initialisation problem (like it is setting 0xff instead of 0x00 for zero initialised variables) or else there is something overwriting the area with 0xff after the initial initialisation. If you step the steatup code you should be able to work out what is happening and fix this part.

This then causes the code not to wait for the transmission/reception to be ready before continuing.The I2C read is then started before the I2C tx has completed which presumably causes the driver to make futher mistakes.

  Error = CI2C1_MasterSendBlock(MyI2CPtr, OutData, 4U, LDD_I2C_SEND_STOP); /* Send OutData (4 bytes) on the I2C bus and generates a stop condition to end transmission */

  while (!DataTransmittedFlg) {                                   /* Wait until OutData are transmitted */

  }

  DataTransmittedFlg = FALSE;

  /* Read configuration of I2C BUS device(e.g. RTC) - Read Operation */

  OutData[0] = 0x00U;                                             /* Initialization of OutData buffer */

  Error = CI2C1_MasterSendBlock(MyI2CPtr, OutData, 1U, LDD_I2C_NO_SEND_STOP); /* Send OutData (1 byte) on the I2C bus stop condition and will not generate the end of the transmission */

  while (!DataTransmittedFlg) {                                   /* Wait until OutData are transmitted */

  }

  DataTransmittedFlg = FALSE;

Since the two flags are already !FALSE the complete code just wizzes through.

2. The second problem is that I don't see any code controlling the flags either.

I've seen similar issues with PE code which works in a pre-prepared demo project because there are interrupt callbacks that have been modified to control such flags. When generating just driver code the flags don't exist in it so it will not be able to work with code examples without this type of additional manipulation.

The trick seems to be to use the callbacks in Events.c to work around this:

void CI2C1_OnMasterBlockSent(LDD_TUserData *UserDataPtr)

{

  /* Write your code here ... */      <------------------------------------------------------------------------------

}

void Cpu_OnNMIINT(void)

{

  /* Write your code here ... */     <------------------------------------------------------------------------------

}

If you set the flags to TRUE in these it will probably solve it.

The I2C driver that you are using is a non-blocking one (presumably interrupt driven) and returns immediately but you application code is blocking code, waiting for a flag. This un-does any possible advantage of the driver type and makes the operation more difficult to understand (and things to go wrong) but will of course operate if you can work out what needs to control the flag.

Probabyl the interrrupts are operating (I didn't check) but if they are not then thsi will also need to be solved to (put a break in the callbacks to be sure).

With these tips I expect that you will be able to work out a solution.

Regards

Mark

2,416 Views
mjbcswitzerland
Specialist V

Christopher

I think that issue 1 has something to do with your project setup. In the SREC produced from the ELF file I see this:

S3151FFFE00000000000000000000000000000000001EB

S30D1FFFE0100203000000000000DF

This means that when you load the code with the debugger it is probably zeroeing the variables when it loads the code. Maybe if you move to release mode it will already sort itself out(?).

This will leave point 2, which will block without adding the flag change in the I2C interrupt call-backs as pointed out. So it may be as simple as adding these two missing lines of code.

Regards

Mark

0 Kudos
2,416 Views
thatonesaxkid
Contributor II

Mark,

What is the name of the ELF file you are talking about, and how are you opening it? (notepad, wordpad, Keil, etc?)

Thanks,

Chris

0 Kudos
2,417 Views
mjbcswitzerland
Specialist V

Chris

Your elf file is "frdm_test_2.axf"

I generated an SREC from it using objcopy "arm-none-eabi-objcopy  frdm_test_2.axf --output-target=srec frdm_test_2.srec", which I have attached.

If one generates a binary form it it results in a 500MByte file due to the space between the code and the variables (code aroung 0x00000000 and variables around 0x1fffe000) which is not usual.

In Keil you can use the "fromelf" utility to do the same.

Regards

Mark

0 Kudos
2,417 Views
thatonesaxkid
Contributor II

I fixed the things you said and am still having my original issue. In the I2C callback I am setting the flags and even setting a breakpoint see if they're even being called, however, the I2C callback never gets called. Could this be caused if the slave never sends an ACK bit?

0 Kudos
2,417 Views
mjbcswitzerland
Specialist V

Christopher

Does the slave send back an ACK? If it doesn't I would correct this first, although I don't expect that it will stop the interrupt from firing.

Regards

Mark

0 Kudos
2,417 Views
mjbcswitzerland
Specialist V

Christopher

Do you have pull-ups on the I2C bus lines?

If not, the bus will always be busy.

Regards

Mark

2,417 Views
thatonesaxkid
Contributor II

Mark,

I do have a couple of 10k pull-ups in the bus lines. I'm using this Arduino Shield from Adafruit: Overview | Adafruit Motor Shield V2 for Arduino | Adafruit Learning System which has the resistors on board. I'm using the pins for I2C which match up with Arduino I2C pins (PB2 and PB3).

Chris

0 Kudos