TWI issue with ATtiny416

Go To Last Post
5 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

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.

 

 

This topic has a solution.
Last Edited: Tue. Nov 19, 2019 - 08:36 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

maybe what you want to do is:

 

void InitIO()
{
	PORTMUX_CTRLB |= PORTMUX_TWI0_bm;  // |= instead of =
}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

balisong42 wrote:
on the tiny416
I don't know the device but are you sure it does not do an auto-shift of a 7 bit addr from 6:0 in the MADDR register to 7:1 on the wire and handle the R/W bit separately?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Tiny416 Datasheet wrote:
The master control logic uses bit 0 of the TWI.MADDR register as the bus protocol’s read/write flag (R/W)
OK, so I'm wrong. It's not that clever as to take R/W from a separate source but does expect it to be bit 0 of MADDR. Sorry for the noise.

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

I got things sorted out. The stuck high bit isn't R/W but the NACK bit (tunnel vision). It's actual the first bit missing. This is due to errata 2.10.3. A point of clarification, when the errata/datasheet say to wait 2 clock cycles, those are TWI clock not CPU clock. Removing the FLUSH command from the beginning of TWITask() allows the above code to work.

 

 

.