I'm implementing an I2C slave device on an ATTiny416 (the ATTiny416-XNANO demo board, to be precise), Studio 7, using the START utility. It follows the slave example given, I haven't changed that flow. I have an I2CTools interface tool and a bus monitor, so I can watch the bus pretty closely.
The Master is trying to read the slave's "status register" at I2C address C2. For testing, the slave is simply reporting the value '0xA5'. Everything runs properly the first time through: the Master presents the address, the slave triggers an interrupt and acknowledges, the ISR writes 0xA5 to the bus, the master reads it, and acknowledges, and the master sets the STOP condition.
The second time through, and thereafter, the slave responds properly to the address interrupt, it writes 0xA5 to the status register, but nothing comes out over the bus. All of the proper ACK and STOP signals are there, but the data line stays high.
I think that the ATTiny416 uses a new TWI module, different from the older ones, and the START example certainly doesn't match the older application notes, but I can't find any condition that would cause the TWI module to not put the data out over the bus, especially while properly responding to everything else.
START TWI module configuration
The TWI ISR, for reference (changed nothing here from the START generated code)
ISR(TWI0_TWIS_vect) { //I2C collision if (TWI0.SSTATUS & TWI_COLL_bm) { I2C_0_collision_callback(); return; } //bus error if (TWI0.SSTATUS & TWI_BUSERR_bm) { I2C_0_bus_error_callback(); return; } //address interrupt if ((TWI0.SSTATUS & TWI_APIF_bm) && (TWI0.SSTATUS & TWI_AP_bm)) { //Address or Stop Interrupt Flag && Address I2C_0_address_callback(); if (TWI0.SSTATUS & TWI_DIR_bm) { //Master Read // Master wishes to read from slave I2C_0_read_callback(); //supply byte TWI0.SCTRLB = TWI_ACKACT_ACK_gc | TWI_SCMD_RESPONSE_gc; //ensure interrupt flag cleared and proper responses sent. } return; } //Data interrupt if (TWI0.SSTATUS & TWI_DIF_bm) { //Data Interrupt Flag if (TWI0.SSTATUS & TWI_DIR_bm) { //Master Read // Master wishes to read from slave if (!(TWI0.SSTATUS & TWI_RXACK_bm)) { //Received Acknowledge // Received ACK from master I2C_0_read_callback(); TWI0.SCTRLB = TWI_ACKACT_ACK_gc | TWI_SCMD_RESPONSE_gc; //ensure interrupt flag cleared and proper responses sent. } else { // Received NACK from master I2C_0_goto_unaddressed(); //reset interrupt flags and respond with an ACK } } else // Master wishes to write to slave { I2C_0_write_callback(); } return; } // STOP was received (separate interrupt from preceding data byte) if ((TWI0.SSTATUS & TWI_APIF_bm) && (!(TWI0.SSTATUS & TWI_AP_bm))) { //Address or Stop Interrupt Flag && Stop I2C_0_stop_callback(); TWI0.SCTRLB = TWI_SCMD_COMPTRANS_gc; //complete transaction return; } }
The address handler and read handler:
void I2C_address_handler() { //a valid address was received. //should be either the current address or all-call (0x00) uint8_t received_address = I2C_0_read(); received_address = received_address & 0xFE; //mask off R/W bit if (received_address == matchbox_I2C_address) { I2C_0_send_ack(); matchbox_I2C_state = I2C_STATE_GOOD_ADDRESS; XNANO_USER_LED_toggle_level(); } else if (received_address == I2C_GENERAL_CALL_ADDRESS) { I2C_0_send_ack(); matchbox_I2C_state = I2C_STATE_ALL_CALL; } else { I2C_0_send_nack(); matchbox_I2C_state = I2C_STATE_IDLE; } } void I2C_read_handler() { switch (matchbox_I2C_state) { case I2C_STATE_GOOD_ADDRESS : //a read was received directly after the address, send the status byte // I2C_0_write(matchbox_status_byte); I2C_0_write(0xA5); //<== gets to this part every time. break; case I2C_STATE_GOOD_COMMAND : //a good command was received, the master wants to read data //do stuff here break; case I2C_STATE_ALL_CALL : //TBD default : //anything else matchbox_I2C_state = I2C_STATE_IDLE; break; } }
As far as I can tell, both from using the on-board debugger and putting in debugging lines that pulse output ports, it gets to and executes the "I2C_0_write(0xA5);" in the read handler every time, and otherwise properly traces through the logic - but after the first time nothing comes out. If I use the programming utility to reset the ATTiny416, by reading the chip ID (which will reset any AVR, and won't reset the I2C tools in any way), it works again - once.
Any help would be greatly appreciated.