I think I've uncovered a serious error in the TWI hardware on the tiny416. Hopefully, someone can prove me wrong.
All the TWI code I've examined writes the address byte like this:
MADDR = (SlaveAddress<<1) | RWbit;
What I'm seeing on the oscilloscope is the tiny416 is applying an additional shift and always sends a high RW bit.
Here's my minimum code which cycles thru addresses 0->15 trying both reads and writes (no slaves on the bus yet).
#define F_CPU 20000000UL #include <avr/io.h> #include <avr/interrupt.h> #include <stdbool.h> #define LED_GREEN_SOLID 2 void InitIO() { PORTMUX_CTRLB = PORTMUX_TWI0_bm; // TWI on PA1-PA2 } static volatile uint16_t TickCounter; ISR(TCA0_OVF_vect) { TickCounter++; TCA0.SINGLE.INTFLAGS = TCA_SINGLE_OVF_bm; } void InitTimer(void) { // 20MHz clock / 16 = 1.25MHz // 1kHz tick from 1.25MHz TCA0.SINGLE.PER = 1250; TCA0.SINGLE.CTRLA = TCA_SINGLE_CLKSEL2_bm | TCA_SINGLE_ENABLE_bm; TCA0.SINGLE.INTCTRL = TCA_SINGLE_OVF_bm; } static volatile bool TWI_A_d; static volatile uint8_t TWIByte; ISR(TWI0_TWIM_vect) { if((TWI0_MSTATUS & TWI_RIF_bm) == TWI_RIF_bm) { TWIByte = TWI0_MDATA; TWI0_MCTRLB = 0x03; // send STOP TWI0_MSTATUS = TWI_BUSSTATE0_bm; // IDLE } else if((TWI0_MSTATUS & TWI_WIF_bm) == TWI_WIF_bm) { if(TWI_A_d)// just wrote address { if((TWI0_MSTATUS & TWI_RXACK_bm) == 0) { TWI0_MDATA = TWIByte; TWI_A_d = false; } else // NACK, ARBLOST, BUSERR or BUSY { TWI0_MCTRLB = 0x03; // send STOP TWI0_MSTATUS = TWI_BUSSTATE0_bm; } } else // just wrote data { TWI0_MCTRLB = 0x03; // send STOP TWI0_MSTATUS = TWI_BUSSTATE0_bm; // IDLE } } } void InitTWI() { TWI0_MBAUD = 200; // 100kHz TWI0_MCTRLA = TWI_RIEN_bm | TWI_WIEN_bm | TWI_ENABLE_bm; TWI0_MCTRLB = TWI_FLUSH_bm | TWI_ACKACT_bm; TWI0_MSTATUS = TWI_BUSSTATE0_bm; // IDLE } void TWISend(uint8_t Addr, uint8_t DataByte) { TWIByte = DataByte; TWI0_MADDR = (Addr<<1); TWI_A_d = true; } void TWIRead(uint8_t Addr) { TWI0_MADDR = (Addr<<1)|0x01; TWI_A_d = true; } static bool ReadCycle; static uint16_t NextTWIPoll; static uint8_t NextPollAddr; void TWITask() { // poll sensors periodically if(TickCounter < NextTWIPoll) return; // timer roll-over can glitch this, but caught by IDLE check if((TWI0_MSTATUS & TWI_BUSSTATE_gm) != TWI_BUSSTATE0_bm) return; // bus not IDLE TWI0_MCTRLB = TWI_FLUSH_bm | TWI_ACKACT_bm; if(ReadCycle) TWIRead(NextPollAddr); else TWISend(NextPollAddr,LED_GREEN_SOLID); NextPollAddr++; if(NextPollAddr==16) { NextPollAddr=0; ReadCycle = !ReadCycle; } NextTWIPoll = TickCounter+4; // sample at ~250Hz } int main(void) { CPU_CCP = 0xD8; CLKCTRL_MCLKCTRLB = 0; // full 20MHz clock InitIO(); InitTimer(); InitTWI(); sei(); NextTWIPoll = TickCounter+10; // start polling 10ms after startup while (1) { TWITask(); } }
And here's a shot of the scope with 1 second persistance.