ATTiny814 I2C code

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

Hi all,

 

I'm struggling to get the I2C code working for a custom PCB with an ATTiny814. I'm trying to talk to an Allegro A1454 over I2C and I can't get any data back from it.

 

There is a lack of info on the web about this chip, I suspect since they are so new. The TWI module appears to be new to this series and doesn't match up with what I've used before on Tiny, Mega, and XMEGA.

 

At it stands I can transmit the address of the chip and the register I want to access, and both get Acknowledged successfully, but when I do a restart on the bus and try to read from it I get nothing on the SDA line.

 

I've tested the A1454 with a MEGA328 and it works fine.

 

What I can't work out it is how the slave (A1454) gets clocked, the ATTiny814 datasheet is pretty vague on this. My best guess is that when you try to access the MDATA register it sends out the clock. The datasheet states that when you send a slave address with the R/W bit set that it receives one byte from the slave before setting the Read Interrupt Flag. But where does that byte go?? If it goes to MDATA, then won't reading MDATA causes another clock train? What if I only want to read one byte.

 

Here's my code:

void twi_init()
{
	TWI0.MBAUD = 0x7F;

	CLKCTRL.MCLKCTRLB |= CLKCTRL_PDIV3_bm | CLKCTRL_PEN_bm;

	TWI0_MCTRLA = TWI_ENABLE_bm;// | 1<<3 Little bit of timeout.
	
	TWI0_MSTATUS = 1;//Clear Bus State and make idle
}

char twi_start(char address)
{
	char regData[4];
	if (TWI0_MSTATUS & TWI_BUSERR_bm)
		return -1;

	TWI0_MADDR = address;		//Send Device Address
	while (!(TWI0_MSTATUS & TWI_WIF_bm));
	
	if (TWI0_MSTATUS & TWI_RXACK_bm)	//No ACK.
		return -1;
		
	TWI0_MDATA = A1454_REG_SENSE;	//Send Register Address
	while (!(TWI0_MSTATUS & TWI_WIF_bm));
	
	if (TWI0_MSTATUS & TWI_RXACK_bm)	//No ACK.
		return -1;
		
	TWI0_MADDR = address+1;
	while (!(TWI0_MSTATUS & TWI_RIF_bm));
	
	regData[1] = TWI0_MDATA;
	while (!(TWI0_MSTATUS & TWI_RIF_bm));
	regData[1] = TWI0_MDATA;
	while (!(TWI0_MSTATUS & TWI_RIF_bm));
	regData[2] = TWI0_MDATA;
	while (!(TWI0_MSTATUS & TWI_RIF_bm));
	
	TWI0_MCTRLB = 1<<2; //Send NACK next time.
	
	regData[3] = TWI0_MDATA;
	while (!(TWI0_MSTATUS & TWI_RIF_bm));
	
	TWI0_MCTRLB = 0<<2;	//Change back to ACK
	
	//Do some stuff with regData...
}

My code is latching up at the line

while (!(TWI0_MSTATUS & TWI_RIF_bm));

Any help is appreciated! I'm very close to scratching the I2C and bit-banging it...

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

I got it working, but it's not pretty. I worked out I need to disable the TWI between each write-read sequence and bit-bang my own stop sequence, otherwise it leave the SCL line low. I also need to clear the Clock-State field in the status register before each use, setting the clock to idle. I had to turn on smart-mode but the datasheet doesn't explain what it does properly. For example it says that NACK won't be sent automatically, but they clear are when I scope the signal. I'll upload my code if anyone would like a copy.

 

The Errata in the datasheet is concerning. Here are the notes regarding the TWI module:

Notice how there is no workaround for a few things, and the Quick Command feature is simply unusable.

 

This is the first AVR device I've been disappointed with. They've changed the way things work and it seems very half-assed. I have to implement the USART now, hopefully that isn't as painful for me.

Last Edited: Tue. Apr 24, 2018 - 03:31 AM