Hi guys!
I'm using MKE04Z8VWJ4 in MCUXpresso and the SDK version is v2.8.0.
I want to add I2C to my project, I used the example of the SDK "i2c_interrupt_b2b_transfer_slave" and everything worked out nice.
However I'd like to add to the response the NACK bit when the master tries to read invalid information. I already read the Reference Manual and found out that with the "TXAK" i can control the ACK/NACK bit, but I´m not sure where to put the instruction to response ACK or NACK.
If I’m wrong about what I’m trying to do, I hope you could help me with this one.
This is the callback that I have.
static void i2c_slave_callback(I2C_Type *base, i2c_slave_transfer_t *xfer, void *userData)
{
switch (xfer->event)
{
/* Address match event */
case kI2C_SlaveAddressMatchEvent:
xfer->data = NULL;
xfer->dataSize = 0;
break;
/* Transmit request */
case kI2C_SlaveTransmitEvent:
/* Update information for transmit process */
xfer->data = &g_slave_buff[2];
xfer->dataSize = g_slave_buff[1];
break;
/* Receive request */
case kI2C_SlaveReceiveEvent:
/* Update information for received process */
xfer->data = g_slave_buff;
xfer->dataSize = I2C_DATA_LENGTH;
break;
/* Transfer done */
case kI2C_SlaveCompletionEvent:
g_SlaveCompletionFlag = true;
xfer->data = NULL;
xfer->dataSize = 0;
break;
default:
g_SlaveCompletionFlag = false;
break;
}
}
I hope you could help me.
Best Regards.
Hi
To control the ACK sent back due to the reception of the present byte you need to set SMB[FACK] to '1' and set C1[TXAK] to '1'.
Alternatively, if you want to acknowledge the present reception but not the FOLLOWING one SMB[FACK] is set to '0' ad C1[TXAK] to '1'.
However I don't think that you can control this in the callback code and you will need to modify the driver to do it. The reason is that the driver will be writing C1[TXAK] in order to release the bus after a slave reception and so you will need to add a return value to the callback which the driver can then interpret to control its behavior.
Below I have shown an I2C EEPROM slave emulation as included in the uTasker KE04 project which has a more flexible interface, but does use an interrupt callback as in your case. Here the code can return, for example, a NACK to its slave address (to receive the address but pretend not to be there) by returning
I2C_SLAVE_ADDRESS_REFUSED
or it could reject data reception by returning
I2C_SLAVE_RX_REFUSED
Beware that you may need to change the location of the callback in the driver in case it is presently calling it after it has already written to the C1 register.
Although it doesn't show NACKing reception the following video shows I2C slave operation and clock stretching in operation as an I2C serial loader: https://youtu.be/awREsqeCEzQ
The uTasker project for KE04 is available as free open source, including Kinetis and I2C simulation on Github: https://github.com/uTasker/uTasker-Kinetis/tree/master/Applications/uTaskerV1.4
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 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-KE04Z.html
EEPROM emulation ( see also https://www.utasker.com/docs/uTasker/uTasker_I2C.pdf )
static int fnI2C_SlaveCallback(int iChannel, unsigned char *ptrDataByte, int iType)
{
#define I2C_RAM_IDLE 0 // RAM pointer states
#define SET_ADDRESS_POINTER 1
static unsigned char ucRAM[256] = {0}; // RAM buffer, initially zeroed
static unsigned char ucState = I2C_RAM_IDLE; // initially idle
static unsigned char ucAddress = 0; // RAM address pointer is reset to zero
switch (iType) { // the interrupt callback type
case I2C_SLAVE_ADDRESSED_FOR_READ: // the slave is being addressed for reading from
case I2C_SLAVE_READ: // further reads
*ptrDataByte = ucRAM[ucAddress++]; // return the data and increment the address pointer
ucState = I2C_RAM_IDLE; // return to the idle state
return I2C_SLAVE_TX_PREPARED; // the prepared byte is to to be sent
case I2C_SLAVE_READ_COMPLETE: // complete read is complete
break;
case I2C_SLAVE_ADDRESSED_FOR_WRITE: // the slave is being addressed to write to
// *ptrDataByte is our address
//
ucState = SET_ADDRESS_POINTER; // a write is being received and we expect the address followed by a number of data bytes
return I2C_SLAVE_RX_CONSUMED; // the byte has been consumed and nothing is to be put in the queue buffer
case I2C_SLAVE_WRITE: // data byte received
// *ptrDataByte is the data received
//
if (ucState == SET_ADDRESS_POINTER) { // we are expecting the address to be received
ucAddress = *ptrDataByte; // set the single-byte address
ucState = I2C_RAM_IDLE; // return to data reception
}
else {
ucRAM[ucAddress++] = *ptrDataByte; // save the data and increment the address pointer
}
return I2C_SLAVE_RX_CONSUMED; // the byte has been consumed and nothing is to be put in the queue buffer
case I2C_SLAVE_WRITE_COMPLETE: // the write has terminated
// ptrDataByte is 0 and so should not be used
//
return I2C_SLAVE_RX_CONSUMED;
}
return I2C_SLAVE_BUFFER; // use the buffer for this transaction
}