Hello, I'm trying to port an I2C driver from KL05 to K32; while peripheral registers definition seem the same, the driver on K32 fails to switch from write to read: after writing slave and register address, instead of sending the read command it repeats the last write.
Does anybody know which are the differences between the two peripherals?
Thanx in advance.
Here is the code related:
// end of tx: check if read must be performed
if (ch->rx_length > 0)
{
// Generate restart
i2c->C1 |= (I2C_C1_RSTA_MASK | I2C_C1_TX_MASK);
// send slave address and read command
ch->state = I2C_RX_START;
i2c->D = ch->slave | 0x01;
}
And here is the full code of the ISR:
typedef struct
{
uint8_t slave; // slave address used for current communication
uint8_t tx_length; // number of bytes to write
uint8_t rx_length; // number of bytes to read
uint8_t index; // index of current array (write/read)
uint8_t state; // current operation
uint8_t* rx_data; // pointer to read data (at least rx_length allocated bytes)
uint8_t tx_data[I2C_MAX_MSG_SIZE]; // buffer of data to write
} I2C_Channel;
volatile I2C_Channel i2c_channels[I2C_TOT_DEVICES] = { 0, } ;
static I2C_Type* i2c_base_ptrs[] = I2C_BASE_ADDRS;
void isr_i2c(uint8_t channel)
{
I2C_Type* i2c = i2c_base_ptrs[channel];
I2C_Channel* ch = &i2c_channels[channel];
// check status register
uint8_t status = i2c->S;
// clear interrupt service flag
i2c->S = I2C_S_IICIF_MASK;
if (status & I2C_S_ARBL_MASK)
{
// lost arbitration (should not happen): stop i2c
i2c->S = I2C_S_ARBL_MASK; // clear arbl (by setting it)
i2c->C1 = 0x00;
ch->state = I2C_ERROR;
}
else
if (ch->state == I2C_TX)
{
if (status & I2C_S_RXAK_MASK)
{
// ACK not received
// Generate STOP (set MST=0), stay in tx mode, and disable further interrupts
i2c->C1 = (I2C_C1_IICEN_MASK | I2C_C1_TX_MASK);
ch->state = I2C_ERROR;
}
else
if (ch->index < ch->tx_length)
{
// write byte
i2c->D = ch->tx_data[ch->index];
ch->index++;
}
else
{
// end of tx: check if read must be performed
if (ch->rx_length > 0)
{
// Generate restart
i2c->C1 |= (I2C_C1_RSTA_MASK | I2C_C1_TX_MASK);
// send slave address and read command
ch->state = I2C_RX_START;
i2c->D = ch->slave | 0x01;
}
else
{
// Generate STOP (set MST=0), stay in tx mode, and disable further interrupts
i2c->C1 = (I2C_C1_IICEN_MASK | I2C_C1_TX_MASK);
ch->state = I2C_AVAILABLE;
}
}
}
else
if (ch->state == I2C_RX_START)
{
// switch to reading
ch->state = I2C_RX;
ch->index = 0;
i2c->C1 &= ~I2C_C1_TX_MASK; // switch to RX mode
if (ch->rx_length > 1)
{
i2c->C1 &= ~I2C_C1_TXAK_MASK; // ack next byte read
}
else
{
i2c->C1 |= I2C_C1_TXAK_MASK; // do not ACK the final read
}
// dummy read to start communication
*(ch->rx_data) = i2c->D;
}
else
if (ch->state == I2C_RX)
{
if (ch->index == ch->rx_length - 1)
{
// last read
// All the reads in the sequence have been processed (but note that the final data register
// read still needs to be done below!) Now the next thing is the end of a sequence; we need
// to switch to TX mode to avoid triggering another I2C read when reading the contents of
// the data register
i2c->C1 |= I2C_C1_TX_MASK;
// Perform the final data register read now that it's safe to do so
*(ch->rx_data + ch->index) = i2c->D;
// Generate STOP (set MST=0), stay in tx mode, and disable further interrupts
i2c->C1 = (I2C_C1_IICEN_MASK | I2C_C1_TX_MASK);
ch->state = I2C_AVAILABLE;
}
else
{
// read byte
*(ch->rx_data + ch->index) = i2c->D;
ch->index++;
if (ch->index == ch->rx_length - 1)
{
i2c->C1 |= I2C_C1_TXAK_MASK; // do not ACK the final read
}
else
{
i2c->C1 &= ~I2C_C1_TXAK_MASK; // ack next byte read
}
}
}
}
已解决! 转到解答。
Hi
I assume you are referring to a K32L2B part which has 3 I2C controllers (rather than K32L2A, which has very different LPI2C controllers).
The K32L2B is more or less a KL43 and has the same I2C version as in KL03, KL43, KL27 etc. This version is a bit different to the one in KL05 (and KL25 etc. and in the majority of K parts too) since it is double buffered and has a few register content changes (and some different interrupts).
In addition there are a few problems with the double-buffered parts where some errata have been added - but only partly. Complications include when trying to read after sending a write address, whereby instead of sending the read address it tends to repeat the previous byte - this may be the issue that you have detected. I also don't know that this has been recorded as an errata in the meantime or not.
This document shows how to work around the problems in order to create compatible I2C drivers across all Kinetis parts: https://www.utasker.com/docs/uTasker/uTasker_I2C.pdf (see pages 15, 16 and 17). The uTasker project's I2C driver allows compatible operation on single-buffered, double buffered and LPI2C on all Kinetis and i.MX RT parts. The double-buffered issues were solved in around 2015 and Freescale put a team on updating erratas after the reports to them (you will find a number of threads about strange behavior if you search this forum) but I never found them to be complete (the SDK tends to work around these with random delay loops but it is better to do it by correctly handling interrupts as in the diagrams).
Regards
Mark
[uTasker project developer for Kinetis and i.MX RT]
Contact me by personal message or on the uTasker web site to discuss professional training, solutions to problems or rapid product development requirements
For professionals searching for faster, problem-free Kinetis and i.MX RT 10xx developments the uTasker project holds the key: https://www.utasker.com/kinetis/FRDM-K32L2B3.html / https://www.utasker.com/kinetis/FRDM-KL05Z.html
Hi
I assume you are referring to a K32L2B part which has 3 I2C controllers (rather than K32L2A, which has very different LPI2C controllers).
The K32L2B is more or less a KL43 and has the same I2C version as in KL03, KL43, KL27 etc. This version is a bit different to the one in KL05 (and KL25 etc. and in the majority of K parts too) since it is double buffered and has a few register content changes (and some different interrupts).
In addition there are a few problems with the double-buffered parts where some errata have been added - but only partly. Complications include when trying to read after sending a write address, whereby instead of sending the read address it tends to repeat the previous byte - this may be the issue that you have detected. I also don't know that this has been recorded as an errata in the meantime or not.
This document shows how to work around the problems in order to create compatible I2C drivers across all Kinetis parts: https://www.utasker.com/docs/uTasker/uTasker_I2C.pdf (see pages 15, 16 and 17). The uTasker project's I2C driver allows compatible operation on single-buffered, double buffered and LPI2C on all Kinetis and i.MX RT parts. The double-buffered issues were solved in around 2015 and Freescale put a team on updating erratas after the reports to them (you will find a number of threads about strange behavior if you search this forum) but I never found them to be complete (the SDK tends to work around these with random delay loops but it is better to do it by correctly handling interrupts as in the diagrams).
Regards
Mark
[uTasker project developer for Kinetis and i.MX RT]
Contact me by personal message or on the uTasker web site to discuss professional training, solutions to problems or rapid product development requirements
For professionals searching for faster, problem-free Kinetis and i.MX RT 10xx developments the uTasker project holds the key: https://www.utasker.com/kinetis/FRDM-K32L2B3.html / https://www.utasker.com/kinetis/FRDM-KL05Z.html
Hello Mark,
after examining uTasker driver and documentation I've fixed the problem related to repeated start, but I'm still doubtful about another I2C issue (ERR009308: I2C does not hold bus between byte transfers in receive and may result in lost data): what is happening here is that when reading starts, the clock line is continuosly driven until a NACK is issued; unfortunately I cannot raise I2C interrupt priority, and at required clock frequency it cannot always service the ISR in time; as a result I cannot guarantee even a 2 bytes read!
If I am correct, the only way to pragmatically handle this issue is by means of DMA, and as described in this post: Adding I2C reads to your processing bandwidth constrained application , I need to use 2 DMA channels to handle one I2C channel! (... and since 2 DMA channels are already used and I need to service two I2C channels, I guess I'm in a pinch)
Could you please confirm that this is the only option, or maybe is there another workaround I'm unaware?
Thanx again for all your help!
Best regards
Marco
Marco
ERR009308 seems to only make sense if using the double-buffered feature of the I2C controller, and if only one tx and rx is performed at a time I don't see that this situation can arise.
Personally I never had a case where data was lost in interrupt driven mode because I have avoided trying to "slightly" increase efficiency with double-buffered operation and opening a potential can of worms.
Regards
Mark
[uTasker project developer for Kinetis and i.MX RT]
Contact me by personal message or on the uTasker web site to discuss professional training, solutions to problems or rapid product development requirements
For professionals searching for faster, problem-free Kinetis and i.MX RT 10xx developments the uTasker project holds the key
Hi Mark,
sorry but I don't understand: is there a way to NOT use the double buffered feature? Actually as soon as reading starts with the first dummy read from D, the I2C clock start running and doesn't stop after the first byte, so if I cannot service the next interrupt in time, it will go on to the next byte...
Marco
I think I may have mixed up the double-buffered I2C with the LPI2C.
Here is my original report on the behavior I found in the KL43 back in 2014 which triggered an errata:
https://community.nxp.com/t5/Kinetis-Microcontrollers/KL43-I2C-problem/m-p/329936
If this is the case in the part that you are using and you can't guarantee the interrupt latency being adequate for the minimum I2C speed that you need it is true that you will only be able to solve it using DMA.
As noted before though I have never had an actual problem with this (apart from the fact that it fails if trying to debug code and stopping at each byte) when working with standard speeds up to 100kb/s.
Regards
Mark
Thanx a lot Mark, indeed my device is K32L2B and my problems are related to this lovely double buffered I2C controllers; I'm trying to fix my driver as you suggested.
Sure Kinetis is a tough biscuit...
Best regards
Marco
Hello Bob
The K32L2A is more or less a KL28 with a different name and I use the same LPI2S driver for it - I have a binary here that exercises the accelerometer on the board:
https://www.utasker.com/kinetis/FRDM-K32L2A4S.html
which runs equivalently and reliably.
Therefore I expect the LPI2C controller to be identical to the one in the KL28 and inherit its behavior - since however I found solutions to avoid the typical controller problems neither cause any difficulties but I can't say whether in some situations and with other firmware there will be new or identical issues.
I also use the same LPI2C driver on i.MX RT parts where I did identify a situation where I had to use different workarounds on different channels (specifically on an i.MX RT 1064 controlling a TFT touch screen one one channel and reading a temperature sense on another) I found a byte shift on one channel which I compensated for on that one but not on the other. That means I could kludge the correct operation for that particular product requirement but still need to investigate the root cause of this....
Regards
Mark
Bob
At the time investigations and new erratas generation were assigned to a (Freescale? or NXP?) department in East Europe where I delivered a bit of additional information to.
However I don't think that the erratas were ever fully completed, whereby one of the recommendations was to use DMA to avoid some of the issues and when I checked SDK modifications I only found some (new) delay loops that potentially managed to avoid critical points in time (?) but without any explanations.
When I say the K32L2A is more or less a KL28 it is general and not specific to the LPI2C. It is almost possible to run KL28 project code on it without any changes. The K32L2A is (although I haven't checked every single peripheral) is basically a KL28 with SAI and one of its EVMSIMs removed. Maybe there is a new process used but it is strange that it is part of a different Kinetis range - but they do have a somewhat cheaper price-tag!
Regards
Mark
I'm moving my KL27 project to the K32L2A due to the increased flash space, and being pin-compatible with the KL27.
I had hoped such a move would put the nightmare of KL27-I2C issues behind me.
Sad to say doesn't look like that is happening.
Thanks.