KL05 as I2C slave

キャンセル
次の結果を表示 
表示  限定  | 次の代わりに検索 
もしかして: 
1,472件の閲覧回数
mwp_nxp
Contributor II

Greetings all,

I've been battling this for a while and not getting too far.

The problem is the KL05 receives the correct address from the master, ACKs it, then something very odd happens to the following byte. I have not been able to work out why. 

A KL03 used to work perfectly in the KL05's place (using the KL05 now due to parts shortages).

The KL05 is running off the internal clock at 20Mhz, the bus clock div is 1 (also 20Mhz).

Attached is a image of the I2C signals. The address byte, and then the odd first data byte.

The pins and I2C peripheral are setup as following:

 

 

	//i2c pin setup
	PORTA->PCR[4] = PORT_PCR_MUX(0x02);
	PORTA->PCR[3]  = PORT_PCR_MUX(0x02);
	//setup i2c hardware slave
	CLOCK_EnableClock(kCLOCK_I2c0);
	//set slave addr, no ranage
	I2C0->A1 = 0x38 << 1U;
	I2C0->RA = 0;
	I2C0->C2 = 0;
	//speed (I2C speed of master is ~315kHz)
	I2C0->F = I2C_F_MULT(0) | I2C_F_ICR(6);
	//glitch filter value, stop hold enable, and stop int
	I2C0->FLT = I2C_FLT_FLT(0) | I2C_FLT_STOPF_MASK | I2C_FLT_STOPIE_MASK;
    //reset all flags
	I2C0->S = 0xFFU;
    //enable slave, and slave int
	I2C0->C1 = I2C_C1_IICEN_MASK | I2C_C1_IICIE_MASK;
    //enable
    NVIC_SetPriority(I2C0_IRQn, 1);
    EnableIRQ(I2C0_IRQn);

 

 

The interrupt handler looks like:

 

void I2C0_IRQHandler(void)
{
	//I2C IRQ handler
	volatile uint8_t data = 0;
	volatile uint8_t status;
	bool doTransmit = false;
	static bool isBusy = false;

	//read flags
	status = I2C_SlaveGetStatusFlags(I2C0);

	//clear interrupt service flag
	I2C0->S = I2C_S_IICIF_MASK;

	//arbitration lost?
	if (status & I2C_S_ARBL_MASK)
	{
		//clear ARBL
		I2C0->S = I2C_S_ARBL_MASK;
		//set receive mode
		I2C0->C1 &= ~(I2C_C1_TX_MASK | I2C_C1_TXAK_MASK);
		//nothing more to do
		return;
	}

	//check NAK
	if (status & I2C_S_RXAK_MASK)
	{
		//set receive mode
		I2C0->C1 &= ~(I2C_C1_TX_MASK | I2C_C1_TXAK_MASK);
		//read dummy
		data = I2C0->D;
		//do something?
	}
	else
		//Check address match
		if (status & I2C_S_IAAS_MASK)
		{
			//slave transmit, master reading from slave
			isBusy = true;
			if (status & I2C_S_SRW_MASK)
			{
				//change direction to send data
				I2C0->C1 |= I2C_C1_TX_MASK;
				doTransmit = true;
			}
			else
			{
				//slave receive, master writing to us
				//send ack
				I2C0->C1 &= ~(I2C_C1_TXAK_MASK);
				//read dummy to release the bus
				data = I2C0->D;
				//reset inbuf
				indatac = 0;
			}
			//do something here?
		}
		else
			//check transfer complete flag
			if (status & I2C_S_TCF_MASK)
			{
				if (status & I2C_S_SRW_MASK)
				{
					//slave transmit, master reading from slave
					doTransmit = true;
				}
				else
				{
					//slave receive, master writing to slave
					//ack
					I2C0->C1 &= ~I2C_C1_TXAK_MASK;
					//get data
					data = I2C0->D;
					indata[indatac++] = data;
				}
			}
			else
			{
				//read dummy to release bus
				data = I2C0->D;
			}

	//send data if there is the need
	if (doTransmit)
	{
		//send data
		if (!sendComplete)
		{
			I2C0->D = outdata[outdatac++];
		}
		else
		{
			//switch to receive mode
			I2C0->C1 &= ~(I2C_C1_TX_MASK | I2C_C1_TXAK_MASK);
			//read dummy to release bus
			data = I2C0->D;
		}
	}
	//done
}

 

 

Capture.JPG

 

Anyone have any ideas what's going on here?

Thanks in advance.

ラベル(1)
0 件の賞賛
1 解決策
1,421件の閲覧回数
mwp_nxp
Contributor II

Finally solved!

The KL05 clocks were running in FEI mode at 21Mhz (Sys & Bus).
Running in FEI mode at 48Mhz (BUS at 24Mhz), now has it working.

元の投稿で解決策を見る

0 件の賞賛
10 返答(返信)
1,422件の閲覧回数
mwp_nxp
Contributor II

Finally solved!

The KL05 clocks were running in FEI mode at 21Mhz (Sys & Bus).
Running in FEI mode at 48Mhz (BUS at 24Mhz), now has it working.

0 件の賞賛
1,415件の閲覧回数
mjbcswitzerland
Specialist V

Hi

I am surprised that core speed has an effect - are you sure there are not race states that are avoided when the processor is running slightly faster?

When I get some time I may try testing the slowest core and bus clocks for reliable operation at a certain I2C master rate to see whether there is something specifically limiting it. I do have a KL02 slave (same I2C controller as in KL05) in operation at the moment that is clocked at 20.97MHz core and 10.485MHz bus/flash which hasn't shown any problems. The same application running on a KL03 (similar issue as you in that I need to use both chips due to shortages, but both are footprint compatible) at 8MHz core and 4MHz bus/flash behaves equivalently.

In both cases the I2C master is using a 50kHz I2C clock.

Regards

Mark

 

0 件の賞賛
1,408件の閲覧回数
mwp_nxp
Contributor II

I'm surprised too. 
I'm quite certain there is nothing else going on that an increase in clock rate would change... the firmware just isn't that complicated or large.
In this case the master I2C clock is approx 350khz.

Thanks again for your help.

0 件の賞賛
1,466件の閲覧回数
mjbcswitzerland
Specialist V

Hi

See appendix A of https://www.utasker.com/docs/uTasker/uTasker_I2C.pdf for details of behavioral differences of the I2C controllers in those two parts.

Regards

Mark

 

 

0 件の賞賛
1,440件の閲覧回数
mwp_nxp
Contributor II

Thanks for your reply, but I'm not sure how it helps me?

The KL05 that I'm having trouble with does not have double-buffered I2C.
It also does not have a start condition interrupt event/flag.

 

0 件の賞賛
1,435件の閲覧回数
mjbcswitzerland
Specialist V

Hi

But your correctly working KL03 does have double-buffered I2C so you need to see how the two types differ in order to possibly detected what your problem is in the KL05 case.

Regards

Mark

 

0 件の賞賛
1,433件の閲覧回数
mwp_nxp
Contributor II

Mark, yes the KL03 does.

I gave up attempting to use the KL03 code (old FSL libs) some time ago.

I'm now handling the KL05's interrupt handling myself (as in the above code).
I cant work out why the KL05 isnt ACK'ing or triggering an interrupt after the first byte after the address is sent.  After the address is handled correctly (as far as i can tell), C1/S are correctly set.

0 件の賞賛
1,430件の閲覧回数
mjbcswitzerland
Specialist V

Hi

This is the basic slave driver from the uTasker project (when running on a non-double buffered KL part) - maybe you can find something that explains your difficulty (I just show handling a master write)

Regards

Mark

Initialisation code:

POWER_UP_ATOMIC(4, I2C0);                                    // enable clock to module
fnEnterInterrupt(irq_I2C0_ID, PRIORITY_I2C0, _I2C_Interrupt_0); // enter I2C0 interrupt handler
ptrI2C->I2C_A1 = pars->ucSlaveAddress;                       // program the slave's address
_CONFIG_PERIPHERAL(A, 4,  (PA_4_I2C0_SDA | PORT_ODE | PORT_PS_UP_ENABLE)); // I2C0_SDA on PA4 (alt. function 2)
_CONFIG_PERIPHERAL(A, 3,  (PA_3_I2C0_SCL | PORT_ODE | PORT_PS_UP_ENABLE)); // I2C0_SCL on PA3 (alt. function 2)
ptrI2C->I2C_F = 0;                                           // slave doesn't have a specific speed programmed
ptrI2C->I2C_C1 = (I2C_IEN);                                  // enable I2C controller
ptrI2C->I2C_C1 |= I2C_IIEN;                                  // immediately enable interrupts if operating as a slave
ptrI2C->I2C_FLT = I2C_FLT_FLT_STOPIE;                        // enable stop condition interrupt

Interrupt handling code:

if ((ptrI2C->I2C_FLT & I2C_FLT_FLT_INT) != 0) {              // if the slave has enabled start/stop condition interrupt(s)
    if ((ptrI2C->I2C_FLT & I2C_FLT_FLT_STOPF) != 0) {        // if a stop condition has been detected
        WRITE_ONE_TO_CLEAR(ptrI2C->I2C_FLT, I2C_FLT_FLT_STOPF); // clear the stop flag (write '1' to clear) [note that if the start flag is also set, it is also cleared by this action]
        WRITE_ONE_TO_CLEAR(ptrI2C->I2C_S, I2C_IIF);          // clear the interrupt flag (write '1' to clear)
        if ((ptrI2C->I2C_S & (I2C_IAAS | I2C_TCF)) != (I2C_IAAS | I2C_TCF)) { // if the slave is already addressed we allow the reception handling to continue since we will have cleared the reception interrupt
            return;
        }
    }
}
WRITE_ONE_TO_CLEAR_INTERRUPT(ptrI2C->I2C_S, I2C_IIF, irq_I2C0_ID); // clear the interrupt flag (write '1' to clear)
if (ptrI2C->I2C_F == 0) {                                    // if we are slave
    if ((ptrI2C->I2C_S & I2C_IAAS) != 0) {                   // if being addressed as a slave
        if ((ptrI2C->I2C_S & I2C_SRW) != 0) {                // if the master is addressing for a read
            ....
        }
        else {                                               // addressed for write
            unsigned char ucAddress;
            ptrI2C->I2C_C1 = (I2C_IEN | I2C_IIEN);           // write to C1 in order to clear the IAAS flag - remain in receive mode (a write to I2C_C1 clears IAAS)
            ucAddress = ptrI2C->I2C_D;                       // dummy read (this reads out address as addressed)
        }
    }
    else {                                                   // data being received from master
        unsigned char ucRxData = ptrI2C->I2C_D;              // read data byte
        return;
    }
}

 

0 件の賞賛
1,427件の閲覧回数
mwp_nxp
Contributor II

Mark,

Thanks a lot for providing the snippet Mark.
Unfortunately implementing a very similar i2c handling int changes nothing. I still end up with the same lack of int/ack after the sent data byte.

 

	//i2c pin setup
	BOARD_INITPINS_EVE_CTP_SDA_PORT->PCR[BOARD_INITPINS_EVE_CTP_SDA_PIN] = PORT_PCR_MUX(0x02);
	BOARD_INITPINS_EVE_CTP_SCL_PORT->PCR[BOARD_INITPINS_EVE_CTP_SCL_PIN] = PORT_PCR_MUX(0x02);
    //i2c NVIC setup
    NVIC_SetPriority(I2C0_IRQn, 1);		//set i2c0 int priority
    EnableIRQ(I2C0_IRQn);				//enable int
	//setup i2c hardware slave
	CLOCK_EnableClock(kCLOCK_I2c0);		//enable i2c clock
	I2C0->A1 = 0x38 << 1U;				//set our slave address
	I2C0->RA = 0;						//no addr range
	I2C0->C2 = 0;
	I2C0->SMB = 0;						//no smbus use
	//speed (I2C speed of master is ~315kHz)
	I2C0->F = 0;						//set speed
    I2C0->S = 0xFFU;					//reset all flags
	I2C0->C1 = I2C_C1_IICEN_MASK;		//enable i2c peripheral
	I2C0->C1 |= I2C_C1_IICIE_MASK;		//enable ints
	I2C0->FLT = I2C_FLT_STOPIE_MASK;	//enable stop cond int

 

 

int:

 

void I2C0_IRQHandler(void)
{
	//I2C IRQ handler
	volatile uint8_t data = 0;
	volatile uint8_t status;
	bool doTransmit = false;

	//arbitration lost?
	if (I2C0->S & I2C_S_ARBL_MASK)
	{
		//clear ARBL
		I2C0->S = I2C_S_ARBL_MASK;
		ARBLostCount++;
	}

	//check for stop flag
	if (I2C0->FLT & I2C_FLT_STOPF_MASK)
	{
		//stop condition has been detected
		I2C0->FLT = I2C_FLT_STOPF_MASK;		//clear flag
		I2C0->S = I2C_S_IICIF_MASK;			//clear interrupt service flag
		if ((I2C0->S & (I2C_S_IAAS_MASK | I2C_S_TCF_MASK)) != (I2C_S_IAAS_MASK | I2C_S_TCF_MASK))
		{
			//if the slave is already addressed we allow the reception handling to continue since we will have cleared the reception interrupt
			return;
		}
	}

	//clear interrupt service flag
	I2C0->S = I2C_S_IICIF_MASK;

	//check address match
	if (I2C0->S & I2C_S_IAAS_MASK)
	{
		//address matched
		isBusy = true;
		if (I2C0->S & I2C_S_SRW_MASK)
		{
			//slave transmit, master reading from us
			//change direction to send data
			I2C0->C1 |= I2C_C1_TX_MASK;
			//clear interrupt service flag
			I2C0->S = I2C_S_IICIF_MASK;
			//flag transmit
			doTransmit = true;
		}
		else
		{
			//slave receive, master writing to us
			//send ack, recv mode
			//I2C0->C1 &= ~(I2C_C1_TX_MASK | I2C_C1_TXAK_MASK);
			I2C0->C1 = I2C_C1_IICEN_MASK | I2C_C1_IICIE_MASK;
			//read dummy to release the bus
			data = I2C0->D;
			//reset inbuf count
			indatac = 0;
		}
	}
	else
	{
		//check transfer complete flag
		if (I2C0->S & I2C_S_TCF_MASK)
		{
			if (I2C0->S & I2C_S_SRW_MASK)
			{
				//slave transmit, master reading from slave
				doTransmit = true;
			}
			else
			{
				//slave receive, master writing to slave
				//clear interrupt service flag
				I2C0->S = I2C_S_IICIF_MASK;
				//ack
				I2C0->C1 &= ~I2C_C1_TXAK_MASK;
				//get data
				data = I2C0->D;
				indata[indatac++] = data;
			}
		}
		else
		{
			//clear interrupt service flag
			I2C0->S = I2C_S_IICIF_MASK;
			//read dummy to release bus
			data = I2C0->D;
		}
	}

	//send data if there is the need
	if (doTransmit)
	{
		//send data
	}
	
	//done
	(void)data;
}

 

Capture.JPG

At the location of the B cursor, the INT is called with IAAS and !SRW (as it should).

The INT is never triggered again after that. Nothing ever happens after the B cursor.

 

0 件の賞賛
1,425件の閲覧回数
mwp_nxp
Contributor II

I've found something else interesting...

I have a variation of the fw that using blocking (non INT driven) handling of I2C0.

With I2C0->F=0, it does not work. It gives the same immediate result as the INT handling code.
With I2C0->F=69 (MULT=2 ICR=5), it does work for 4-5 I2C transactions before failing.

This is a capture of the blocking i2c handling code, with F=69.
Note the very short SDA pulses which i have circled in red (i have no idea what they are or are caused by).

Capture.JPG

 

 

 

0 件の賞賛