I'm trying to get a basic handshake going. Below is the ISR for the C8051F120's SMBus (System Management Bus). I'm trying to implement an I2C device on it (ads1115 7addr 0x48 for those who are curious). Note this is mainly the example given by silicon labs for the F120.
void SMBUS_ISR (void) interrupt 7
{
bit FAIL = 0; // Used by the ISR to flag failed
// transfers
static unsigned char sent_byte_counter;
static unsigned char rec_byte_counter;
// Status code for the SMBus (SMB0STA register)
switch (SMB0STA)
{
// Master Transmitter/Receiver: START condition transmitted.
// Load SMB0DAT with slave device address.
case SMB_START: //0x08
// Master Transmitter/Receiver: repeated START condition transmitted.
// Load SMB0DAT with slave device address
case SMB_RP_START: //0x10
SMB0DAT = TARGET; // Load address of the slave.
SMB0DAT &= 0xFE; // Clear the LSB of the address for the
// R/W bit
SMB0DAT |= SMB_RW; // Load R/W bit
STA = 0; // Manually clear STA bit
rec_byte_counter = 1; // Reset the counter
sent_byte_counter = 1; // Reset the counter
break;
// Master Transmitter: Slave address + WRITE transmitted. ACK received.
// For a READ: N/A
//
// For a WRITE: Send the first data byte to the slave.
case SMB_MTADDACK: //0x18
SMB0DAT = SMB_DATA_OUT[sent_byte_counter-1];
sent_byte_counter++;
break;
// Master Transmitter: Slave address + WRITE transmitted. NACK received.
// Restart the transfer.
case SMB_MTADDNACK: //0x20
STA = 1; // Restart transfer
break;
// Master Transmitter: Data byte transmitted. ACK received.
// For a READ: N/A
//
// For a WRITE: Send all data. After the last data byte, send the stop
// bit.
case SMB_MTDBACK: //0x28
if (sent_byte_counter <= NUM_BYTES_WR)
{
// send data byte
SMB0DAT = SMB_DATA_OUT[sent_byte_counter-1];
sent_byte_counter++;
}
else
{
STO = 1; // Set STO to terminate transfer
SMB_BUSY = 0; // And free SMBus interface
}
break;
// Master Transmitter: Data byte transmitted. NACK received.
// Restart the transfer.
case SMB_MTDBNACK: //0x30
STA = 1; // Restart transfer
break;
// Master Receiver: Slave address + READ transmitted. ACK received.
// For a READ: check if this is a one-byte transfer. if so, set the
// NACK after the data byte is received to end the transfer. if not,
// set the ACK and receive the other data bytes.
//
// For a WRITE: N/A
case SMB_MRADDACK: //0x40
if (rec_byte_counter == NUM_BYTES_RD)
{
AA = 0; // Only one byte in this transfer,
// send NACK after byte is received
}
else
{
AA = 1; // More than one byte in this transfer,
// send ACK after byte is received
}
break;
// Master Receiver: Slave address + READ transmitted. NACK received.
// Restart the transfer.
case SMB_MRADDNACK: //0x48
STA = 1; // Restart transfer
break;
// Master Receiver: Data byte received. ACK transmitted.
// For a READ: receive each byte from the slave. if this is the last
// byte, send a NACK and set the STOP bit.
//
// For a WRITE: N/A
case SMB_MRDBACK: //0x50
if (rec_byte_counter < NUM_BYTES_RD)
{
SMB_DATA_IN[rec_byte_counter-1] = SMB0DAT; // Store received byte
AA = 1; // Send ACK to indicate byte received
rec_byte_counter++; // Increment the byte counter
}
else
{
AA = 0; // Send NACK to indicate last byte
// of this transfer
}
break;
// Master Receiver: Data byte received. NACK transmitted.
// For a READ: Read operation has completed. Read data register and
// send STOP.
//
// For a WRITE: N/A
case SMB_MRDBNACK: //0x58
SMB_DATA_IN[rec_byte_counter-1] = SMB0DAT; // Store received byte
STO = 1;
SMB_BUSY = 0;
AA = 1; // Set AA for next transfer
break;
// Master Transmitter: Arbitration lost.
case SMB_MTARBLOST: //0x38
FAIL = 1; // Indicate failed transfer
// and handle at end of ISR
break;
// All other status codes invalid. Reset communication.
default:
FAIL = 1;
break;
}
if (FAIL) // If the transfer failed,
{
SMB0CN &= ~0x40; // Reset communication
SMB0CN |= 0x40;
STA = 0;
STO = 0;
AA = 0;
SMB_BUSY = 0; // Free SMBus
FAIL = 0;
}
SI = 0; // Clear interrupt flag
}
//-----------------------------------------------------------------------------
// Support Functions
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// SMB_Write
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters : None
//
// Writes a single byte to the slave with address specified by the <TARGET>
// variable.
// Calling sequence:
// 1) Write target slave address to the <TARGET> variable
// 2) Write outgoing data to the <SMB_DATA_OUT> array
// 3) Call SMB_Write()
//
void SMB_Write (void)
{
char SFRPAGE_SAVE = SFRPAGE; // Save Current SFR page
SFRPAGE = SMB0_PAGE;
while (SMB_BUSY); // Wait for SMBus to be free.
SMB_BUSY = 1; // Claim SMBus (set to busy)
SMB_RW = 0; // Mark this transfer as a WRITE
STA = 1; // Start transfer
SFRPAGE = SFRPAGE_SAVE; // Restore SFR page detector
}
//-----------------------------------------------------------------------------
// SMB_Read
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters : None
//
// Reads a single byte from the slave with address specified by the <TARGET>
// variable.
// Calling sequence:
// 1) Write target slave address to the <TARGET> variable
// 2) Call SMB_Write()
// 3) Read input data from <SMB_DATA_IN> array
//
void SMB_Read (void)
{
char SFRPAGE_SAVE = SFRPAGE; // Save Current SFR page
SFRPAGE = SMB0_PAGE;
while (SMB_BUSY); // Wait for bus to be free.
SMB_BUSY = 1; // Claim SMBus (set to busy)
SMB_RW = 1; // Mark this transfer as a READ
STA = 1; // Start transfer
while (SMB_BUSY); // Wait for transfer to complete
SFRPAGE = SFRPAGE_SAVE; // Restore SFR page detector
}
The main continuously does the following: Sends 3 bytes. The first byte is the device register pointer. Then reads the same register (since the pointer is already set). It does do this.
while (1)
{
TARGET = SLAVE_ADDR; // Target the Slave for next SMBus
// transfer
SMB_DATA_OUT[0] = 0x01; // Device register
SMB_DATA_OUT[1] = 0x0A; // Register MSByte
SMB_DATA_OUT[2] = 0x03; // Register LSbyte
SMB_Write(); // Initiate SMBus write
// SMBus Read Sequence
TARGET = SLAVE_ADDR; // Target the Slave for next SMBus
// transfer
SMB_Read();
}
And here is a trace capture of transfer:
Looks to me like the master receive is sending an extra ACK. So my main focus has been on cases:
SMB_MRADDACK: //0x40
SMB_MRADDNACK: //0x48
SMB_MRDBACK: //0x50
My main focus is more so SMB_MRADDNACK: //0x48 and the number of times it goes through that if statement during the ISR calls. I'm having a little trouble wrapping my head around the exact failure point. So where is this extra ACK coming from? I'll look back here Monday afternoon if I don't figure it out myself by then.
Bonus question: Is there a embedded stack exchange of some sort? Didn't see anything that stood out for me in the communities..
case SMB_MRDBACK: //0x50
SMB_DATA_IN[rec_byte_counter-1] = SMB0DAT; // Store received byte
rec_byte_counter++; // Increment the byte counter
if (rec_byte_counter < NUM_BYTES_RD)
{
AA = 1; // Send ACK to indicate byte received
}
else
{
AA = 0; // Send NACK to indicate last byte
// of this transfer
}
break;