I really hate this kind of stuff: I know it should work, but it does not. I’m loosing a lot of time (hours, days, even weeks) to track it down to the root cause. Yes, I create my own bugs. Yes, there are bugs in tools, sources, libraries and components. But what many might not believe: there are bugs in silicon too :-(. If you do not believe, here is one: there is a hardware I2C problem on the KL25Z used on the Freedom board. It worked in one project, but not in another.
❗ The silicon bug described here is present on many Kinetis devices, not only the KL25Z!
So if you are facing a problem where you read 0xFF or wrong values from the I2C bus with the KL25Z, here is probably why (and how to workaround it). The problem showed up with a modified version of the Freedom Accelerometer tutorial….
Start or not Start, that’s the question
In my application I need to read through I2C from a device. It is using the I2C1_MasterSendBlock()
followed by a I2C1_MasterReceiveBlock()
(see as well this post).
byte I2C2_ReadAddress(byte i2cAddr, byte *memAddr, byte memAddrSize, byte *data, word dataSize) { byte res = ERR_OK; TMOUT1_CounterHandle timeout; bool isTimeout; if (I2C2_SelectSlave(i2cAddr)!=ERR_OK) { return ERR_FAILED; } for(;;) { /* breaks */ /* send device address and memory address */ I2C2_deviceData.dataTransmittedFlg = FALSE; res = I2C1_MasterSendBlock(I2C2_deviceData.handle, memAddr, memAddrSize, LDD_I2C_NO_SEND_STOP); if (res!=ERR_OK) { break; /* break for(;;) */ } timeout = TMOUT1_GetCounter(I2C2_TIMEOUT_MS/TMOUT1_TICK_PERIOD_MS); /* set up timeout counter */ if (timeout==TMOUT1_OUT_OF_HANDLE) { res = ERR_FAILED; break; /* break for(;;) */ } do { /* Wait until data is sent */ isTimeout = TMOUT1_CounterExpired(timeout); if (isTimeout) { break; /* break while() */ } } while (!I2C2_deviceData.dataTransmittedFlg); TMOUT1_LeaveCounter(timeout); if (isTimeout) { res = ERR_FAILED; break; /* break for(;;) */ } /* receive data */ I2C2_deviceData.dataReceivedFlg = FALSE; res = I2C1_MasterReceiveBlock(I2C2_deviceData.handle, data, dataSize, LDD_I2C_SEND_STOP); if (res!=ERR_OK) { break; /* break for(;;) */ } timeout = TMOUT1_GetCounter(I2C2_TIMEOUT_MS/TMOUT1_TICK_PERIOD_MS); /* set up timeout counter */ if (timeout==TMOUT1_OUT_OF_HANDLE) { res = ERR_FAILED; break; /* break for(;;) */ } do { /* Wait until data is received */ isTimeout = TMOUT1_CounterExpired(timeout); if (isTimeout) { break; /* break while() */ } } while (!I2C2_deviceData.dataReceivedFlg); TMOUT1_LeaveCounter(timeout); if (isTimeout) { res = ERR_FAILED; break; /* break for(;;) */ } break; /* break for(;;) */ } /* for(;;) */ if (I2C2_UnselectSlave()!=ERR_OK) { return ERR_FAILED; } return res; }
There is nothing wrong with that code, as it worked for other devices very well.
Below is a logic analyzer output from that code sequence, which reads from the device a value:
It sends a start condition (first green dot on the SDA line), followed by the I2C device address (0x68), then the memory address (0x00), followed by a ‘repeated start’ condition (the second green dot), then again the I2C device address (0x68), and then reads the data from the device (0x26). So this one is working.
Now here is the same thing on the KL25Z, same code, but this time things are screwed up:
The problem is: somehow the start condition is not sent, screwing up the communication, and the software just reads back 0xff (wrong!) :-(.
It took me a while to find out what is the difference between the ‘working’ and ‘non-working’ version: it is the pre-scaler setting for the I2C peripheral on the KL25Z. It works if the prescaler to the I2C bus is set to 1!
Workaround
To verify the settings, I inspect the ‘Internal Frequency’ Settings of the I2C_LDD component:
Here I have a dialog with which has a ‘Clock path’ tab which shows the clock used for the peripheral:
💡 The ‘Clock path’ is a really, really nice feature of Processor Expert!
If that bottom prescaler setting is set to one, I’m fine.
But if that setting is set to anything other than one, the bus communication does not work reliably, as shown above:
Looks like the silicon has a real issue with the internal clock distribution :-(.
So the workaround is to make sure that a prescaler of 1 is used. This means to select the maximum clock frequency as offered in the above dialog. And to achieve the desired I2C bus clock frequency, to use the frequency divider bits e.g. to reach a desired I2C clock frequency:
Summary
- Always have a working example (that’s why tutorials or example projects are great!)
- It is possible that my code has bugs (yep! I admit.)
- Libraries or sources from somebody else has bugs too (yep! they are not better than I am ;-).)
- It is not unlikely that silicon has bugs too, especially if the silicon is brand new (absolutely! that’s why companies wait until Rev 2.0 of the silicon comes out)
- The debugger combined with a logic analyzer is my best friend (yes, indeed! and makes a prefect Christmas present.)
Thinking that only software can be flawed is simply wrong: silicon has bugs too, and this is obvious with reading the silicon erratas. The current KL25Z erratas are available here (but does not list above issue (yet?)).
Happy Starting 🙂
PS: My Saleae Logic16 was my ‘self-wished-and-purchased’ Christmas gift for 2011. Still thinking what I should wish (and buy) for 2012 :-). Suggestions and donations are welcome. 🙂
Thank you for making all this very clear !
I can’t wait to retry the ‘Accelerating …’ tutorial with various different frequencies as shown (have you already tried it ?).
Also, you might want to add a link to this topic in your ‘Accelerating …’ tutorial.
LikeLike
Good suggestion about the link (added now). And yes, I have used the I2C bus with different clock speeds, from below 100 kHz up to 1 MHz.
LikeLike
Pingback: Tutorial: Accelerating the KL25Z Freedom Board | MCU on Eclipse
I2C is a very hard system to troubleshoot when it goes wrong. That is great that you found the error. One suggestion for Christmas… the Google Nexus 7 pad. I use it to keep my schematics, doc sets, for projects, source code, email …everything. The disadvantage is you can’t escape having your work with you at all times. I have the Saleae analzyer as well; I wish they would add the conditional software triggers… it has been over a year they have been promising… that would be a good Christmas gift.
LikeLike
Hi Robert, yes, I miss the conditional triggers on the Saleae analyzer as well. And good suggestion with the Google Nexus 7 pad: I’m already using an iPad for this :-). The challenge with it is that the whole family wants to use it too 😉
LikeLike
And what type of cable adapter do you guys use to connect to the iPad or Nexus 7? Also, the Saleae website doesn’t mention that they have software for iOS or Android.
LikeLike
There is no adapter. The Salea software runs on a host, not on a tablet or phone. But if you really want to have it on a tablet, then use a remote UI connection software, but I have not done that.
LikeLike
I had misunderstood the comments. I thought you guys were running the Saleae on the iPad and Nexus. Got it now! Thanks.
LikeLike
Pingback: Processor Expert Maxim I2C RTC Driver the Arduino Data Logger Shield | MCU on Eclipse
Again (I hope this doesn’t get old) thanks! One update here. Mask Set Errata for Mask 2N97F lists it as Errata ID 6070, but it actually suggests that a value of zero is the safe bet: “I2C: Repeat start cannot be generated if the I2Cx_F[MULT] field is set to a non-zero value” . Ref: http://cache.freescale.com/files/microcontrollers/doc/errata/KINETIS_L_2N97F.pdf?fpsp=1
LikeLike
ah, finally that errata has been published. Thanks for the link, as sometimes it is really hard to find such a important information.
LikeLike
I’ve been trying to get a Sensirion SHT21 humidity sensor to work on the KL15, wasn’t working and this is why.
I don’t know whether to drink, or go on a monkey sh*t throwing rampage, or both.
LikeLike
Hi Gibbon1,
intersting, I thought that issue only exists for the KL25Z. Thanks for the heads up, as I want to switch one of my projects to the KL15.
LikeLike
I had the same problem with a KL05. Thanks. That would have been a nightmare.
LikeLike
Yes, Freescale is using the same IP blocks/silicon for multiple devices. So silicon bugs get easily propagated :-(. The good thing is that the most recent Processor Expert has a built in warning (at least for some known cases).
LikeLike
Pingback: Bit-Banging I2C with ResetBus() Functionality | MCU on Eclipse
Hi Erich.
I’m running “very low power” mode in KL25Z-FRDM.
Core Clock: 2 MHz, bus Clock 0.666667 MHz. And Maximum I2C bus clock I can set is 33.33 KHz.
I’m using I2C to write config and read data on Accelerometer MMA8451.
Sometimes MMA8451 work correctly, but sometimes when rebooting, MMA8451 can not write config (initialize) –> Accelerometer can not work (hang if I put unlimited while loop at I2C return).
This case, though I do many reset (simple software reset), it still can not work again. I must cut down power (re-solder battery) then it can work again.
How can solve this problem? 33.33 KHz is not fine for MMA8451? (recommended in spec is 100 KHz –> 400 KHz)
LikeLike
Hi Thach,
this is a problem of the MMA8451 accerometer: it can hang, and you need reset the accelerometer (e.g. with a power-on reset). This is a known problem, and for this sometimes I put in a FET in my designs so I can unpower/repower I2C devices. Unfortunately the Freescale I2C hardware has no way to reset through the bus a hanging device. But you could do this with bit banging (see https://mcuoneclipse.com/2013/12/08/bit-banging-i2c-with-resetbus-functionality/).
I hope this helps.
LikeLike
One thing I’ve had issues with over the years is, devices with I2C and SPI buses, don’t have reset lines, and after a watchdog timeout or jtag reset get left in the middle of a bus operation. For that reason when initializing the bus I bit bang the I2C or SPI Clock 8-10 times.
LikeLike
Yes, this is indeed a problem. Unfortunately most I2C implementations do not support this in the hardware.
What I do is bitbanging instead: https://mcuoneclipse.com/2013/12/08/bit-banging-i2c-with-resetbus-functionality/
LikeLike
Hi Erich,
I saw that you own a Saleae Logic16. I’m thinking in buy one Logic Analizer, and I saw this and a gwinstek GLA-1016, did you have the oportunity to test this one ? Do you know if one is much better than the other ?
Best Regards,
Christian
LikeLike
Hi Christian,
I don’t know this GLA-1016. To me the software made the difference, and the Saleae one is really nice. To me it is exactly what I needed.
LikeLike
Thaks a lot Erich.. i’m begining to make me crazy…
Yes, with the kl15 succes the same.
Sometimes sending the restart and sometimes not.
It has been to put the preescaler value to 1 and work it correctly
Thanks !!
LikeLike
Hi Carlos,
yes, I know how this feels to find out that the silicon is buggy 😦
LikeLike
Hey yet again, Erich!
Do you have experience working with SHT21?
LikeLike
Hi Piotr,
no, I have not used the SHT21.
Erich
LikeLike
Outstanding! I just came across the same issue on the K22 and was pounding my head against the wall trying to figure it out. Showing the clock path screen is the key. I tried manually setting mult to 0 but that did not work. When I did exactly as you said and made sure I2C0 prescale value was set to 1 (it was 2 in my design) everything started working!
LikeLike
good to hear that I was able to help out :-)!
LikeLike
Hey Erich,
First of all, thank you for all the helpful notes on Kinetis. It really helped!
I have been getting what appears to be the exact same problem as the I2C LDD component works sometimes but not always (sometimes the SDA line is always pulled low for no reason).
I am working with a KL46Z board and I tried following your tutorial to solve the bug, but I’ve encountered the following condition:
My OUTDIV2PRESC = 1 but my OUTDIV4PRESC = 2. Do you know where we could change this value?
Thank you!
Thomas
LikeLike
Hi Thomas, what tools/toolchain are you using? Processor Expert with Kinetis Design Studio? With or without Kinetis SDK?
You can manually change the clock prescaler values in Processor Expert.
LikeLike
Hey Erich,
I am using Processor Expert with KDS but not SDK.
I still can’t change the OUTDIV4PRESC even with Advanced option on.
Interestingly, I enabled auto-initialization for all components of the project and it seems to have solved the problem. But I’m pretty sure it’ll fail again, given how it has failed before with Auto-initialization.
Thanks.
Thomas
LikeLike
Hi Thomas,
if you use auto-initialization, then all what it does is to call the Init() function (and driver handle generation) during PE_low_level_init(). Could it be that you are somehow missing to call the Init() from your application, or using a duplicate device handle?
It should not affect the prescaler settings in my view anyway, so I’m really wondering what the problem could be.
LikeLike
Hi Erich,
Thank you for the prompt replies!
I made sure that the Init function was called and I didn’t have a duplicate DeviceData handle (I had this error once but the compiler pointed it out).
What is really interesting is that the Sensor Fusion project that uses the exact same configurations always works, but my project only works some of the time.
LikeLike
‘Some of the time’ means same source/code/binary works sometimes, but not always. Or do you have a state/configuration/source file set where it does not always work? Would be interesting to know the difference between ‘working’ and ‘non-working’. Or are you saying that one time the clock prescalers are set correctly, one time not?
LikeLike
Hey Erich,
working = I2C is getting reading from Accelerometer, SDA line is not always low;
not-working = SDA line always low, I2C not reading anything.
The clock prescalar OUTDIV4PRESC is still at 2 though, however. I wish I could find some documentation on OUTDIV4PRESC but Google turned up nothing.
Anyway, after doing the following, the code is miraculously working:
1. following the work-around in this tutorial and use the highest value for I2C source clock.
2. Use 010 for both divider to get 375kHz (even though it gives a warning of exceeding the 100kHz limit.
3. set all flag variables to volatile, which solved another problem of the ADC component of the same project only working with breakpoint present.
LikeLike
Hi Thomas,
about point 2): you can ignore that warning. The original specs of I2C is for 100 kHz, but this warning does not make any sense to me if the device (like the one you are using) allows a higher speed).
About 3): if you have to set things to volatile, either the compiler is doing things in a wrong way, or indeed there is a reentrancy problem. Marking the variable volatile prevents compiler optimizations, and will affect timing. So it might be because of optimizations, or because of timing that things work or do not work. Have you optimizations turned off?
LikeLike
Hi Erich,
Pardon me if this question is too simple, but how do you see optimization levels?
I followed a link you replied to on Freescale forum and check the Debug settings and the Other compiler flags blank is blank. Should I manually type in -0 to turn that off? Or maybe I don’t need volatile variable at all, even if they correspond to flags that are being set in events.c?
Also, in the Tool Settings section under properties of the project, I am seeing ARM GNU Assembler, ARM C compiler, ARM C++ Compiler and ARM C++ Linker. Is this what I am supposed to be seeing?
Apologies in advance for not know what appears to be basics of C.
Thanks again for all the help.
Thomas
LikeLike
Hi Thomas,
you find the compiler optimization settings under Project > Properties > C/C++ Build > Settings > Optimization. The Optimization level should be ‘None -O0’ to disable optimizations. And yes, you should not need to use ‘volatile’, as this would not be a proper solution for a problem.
Erich
LikeLike
Is there any example of use kl25 or kl26 as I2C Slave? I try use I2C_LLD with MASTER-SLAVE mode, but when I try detect address with i2cdetect on my Linux I don’t have any results 😦
LikeLike
I have used the KL25Z as I2C slave, but have not published that project on GitHub because it was done for a research partner, and he did not want that project published. Have you checked the I2C signals with a logic analyzer if they make sense?
LikeLike
Now I have result of i2cdetect with correct address, OnRX and OnTX event in CI2C component when I put i2cset and i2cget command, but I don’t known how to get bytes from i2cset command, and send response for i2cget command. What is the order of events and methods of CI2C komponent to get and send bytes? I can’t find any example of internet 😦
Thanks for any help.
Michal
LikeLike
I don’t know i2cdetec. I have an example with the KL25Z as master here: https://mcuoneclipse.com/2012/09/21/tutorial-accelerating-the-kl25z-freedom-board/
I would need to dig out another example (was with CodeWarrior and using the S08), or to create a new example with the KL25Z as I2C slave first.
As for not finding anything on the internet: have you checked the I2C_LDD help (see https://mcuoneclipse.com/2012/11/12/getting-help-on-processor-expert-components/) as it describes as well the slave use case?
LikeLike
I’m getting this exact lack of repeat start condition on an MKL16, but setting the I2Cx_F[MULT] to 0 or 1 does nothing to resolve it 😦 I suppose will continue to modify the clock path until I find a solution
LikeLike
Are you using Processor Expert? You should be able to verify that clock setting in there.
LikeLike
Hi Erich!
I had a trouble very similar with the KL03Z, my problem is when i send the repeated start, the bus sends the last byte that i sent instead of the byte that i want to send. But the trouble it’s only when i run the application, but when i debug step by step it works! I’m not using Processor Expert.
LikeLike
Hi Saul,
there is yet another hardware bug with Kinetis around the double buffer implementation, see https://community.nxp.com/thread/377611
Erich
LikeLike
Hi Erich,
I have seen that post, but it’s possible to solve that problem?
LikeLike
Hi Saul,
ah, I see you have posted that question there too. I believe there is really no solution. I saw that several users switched to bit banging (I have the GenericSWI2C component doing this). That double buffering thing seems to be only in the newer Kinetis devices which have double buffering.
LikeLike
Hi Erich,
Thanks for the post.
I want to read two bytes from I2C slave device (using KL03 as master). If I try for reading only one byte it is working fine, but reading of two bytes is showing “1 FF” data. Is it because of repeated start conditions?
and how to change I2C prescalar to 1 in KDS 3.0.0 ?
I dont find “Internal clock frequency” in I2C component inspector.
LikeLike
I am using PE and fsl_I2C component
LikeLike
Hi Syed,
I have not used that fsl_i2c component in my projects. Is it not possible to use the one I have used because there I can set the clock divider?
Otherwise, you need to step through th fsl_i2c component code to see where the clock is configured.
I recommend that you check with a logic analyzer the signals on the i2c bus so you see that is going on: without such a tool, your are more or less blind and don’t know what is going on 😦
Erich
LikeLike
Hello Erich,
Can this problem happen in the mke02z family?
In my case it does not go into the OnMasterBlockSent event and does not return errors.
LikeLike
I don’t have any KE02Z at hand. I have seen that silicon bug present on all Kinetis I have used so far, but the KE series uses very different peripherals. The only way to find out is to hook up a logic analyzer/scope and see if the repeated start condition is sent or not. If you don’t have a scope, try an I2C clock settings with no divider and check if the problem goes away.
I hope this helps,
Erich
LikeLike