Problems porting Kinetis I2C driver from KL05 to K32

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

Problems porting Kinetis I2C driver from KL05 to K32

Jump to solution
1,848 Views
marco_palestro
Contributor II

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
			}
		}
	}
}

 

0 Kudos
Reply
1 Solution
1,837 Views
mjbcswitzerland
Specialist V

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

 

View solution in original post

11 Replies
1,838 Views
mjbcswitzerland
Specialist V

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

 

1,749 Views
marco_palestro
Contributor II

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

0 Kudos
Reply
1,744 Views
mjbcswitzerland
Specialist V

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

 

0 Kudos
Reply
1,742 Views
marco_palestro
Contributor II

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...

0 Kudos
Reply
1,733 Views
mjbcswitzerland
Specialist V

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

 

0 Kudos
Reply
1,795 Views
marco_palestro
Contributor II

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

0 Kudos
Reply
1,826 Views
bobpaddock
Senior Contributor III

" K32L2A, which has very different LPI2C controllers)"

Mark, I'll be firing one of those up soon.  Do you know if it works correctly, unlike most of the other Kinetis I2C's?


0 Kudos
Reply
1,820 Views
mjbcswitzerland
Specialist V

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

 

0 Kudos
Reply
1,812 Views
bobpaddock
Senior Contributor III

"K32L2A is more or less a KL28"

I remember you and NXP having a discussion about an issue with the KL28 I2C.
Did they ever get back to you?


0 Kudos
Reply
1,808 Views
mjbcswitzerland
Specialist V

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

 

0 Kudos
Reply
1,802 Views
bobpaddock
Senior Contributor III

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.

 

0 Kudos
Reply