Dear community,
I'm trying now for a while to get my ATmega328PB to communicate on TWI (I2C) with my Raspberry Pi.
I started with a program skeleton that I created with AtmelStart but in the meantime I've reduced it to the bare minimum to get information I can work on.
This is the program
void I2C_0_isr() { printf("%03d: TWSR0: 0x%02X, TWDR0: 0x%02X\r\n", counter++, TWSR0, TWDR0); switch (TWSR0 & 0xF8) { case 0xA8: case 0xB0: case 0xB8: TWDR0 = 0x99; TWCR0 = (1 << TWEN) | (1 << TWINT) | (1 << TWEA) | (0 << TWIE); break; case 0xC0: case 0xC8: TWCR0 = (1 << TWEN) | (1 << TWINT) | (1 << TWEA) | (0 << TWIE); break; case 0x60: case 0x68: TWCR0 = (1 << TWEN) | (1 << TWINT) | (1 << TWEA) | (0 << TWIE); break; case 0x70: case 0x78: case 0x90: case 0x98: TWCR0 = (1 << TWEN) | (1 << TWINT) | (0 << TWIE); break; case 0x80: case 0x88: TWCR0 = (1 << TWEN) | (1 << TWINT) | (1 << TWEA) | (0 << TWIE); break; case 0xA0: TWCR0 = (1 << TWEN) | (1 << TWINT) | (1 << TWEA) | (0 << TWIE); break; } } void I2C_loop(void) { TWCR0 = (1 << TWEN) | (1 << TWINT) | (1 << TWEA); for (;;) { while (!(TWCR0 & (1 << TWINT))); I2C_0_isr(); } }
Please don't get confused with the name I2C_isr(), I've disabled the TWI interrupt in the meantime and I'm calling the I2C_isr() function from a loop that polls the TWINT flag in the TWCR0. Whenever the flag is set, the I2C_isr() is called. The implementation about the TWSR0 codes is taken from the data sheet. I just wanted to see the codes I get when I issue I2C commands from the Raspberry Pi.
This is what I get:
pi@rasberrypi:~ $ i2cdetect -y 1 096: TWSR0: 0xA8, TWDR0: 0xA1 097: TWSR0: 0xC0, TWDR0: 0x99
Here I see the 0xA8 (Slave_Addr_Read) and a 0xC0 (Slave_Not_Ack) which is perfectly fine.
pi@raspberrypi:~ $ i2cset -y 1 0x50 0x12 0x34 098: TWSR0: 0x60, TWDR0: 0xA0 099: TWSR0: 0x80, TWDR0: 0x12 100: TWSR0: 0x80, TWDR0: 0x34 101: TWSR0: 0xA0, TWDR0: 0x68
When I try to write a byte to, let's say the register 0x12, I also see a sequence, I think, I'm satisfied with.
Slave_Addr_Write, followed by the write operations Slave_Write_Ack and followed by a Slave_Stop
Okay.
But then:
pi@raspberrypi:~ $ i2cget -y 1 0x50 0x56 b 102: TWSR0: 0x60, TWDR0: 0xA0 103: TWSR0: 0x80, TWDR0: 0x56 104: TWSR0: 0x80, TWDR0: 0xA1 <-- its a Slave_Write_Ack 0x80, not a Slave_Read 0xB8 105: TWSR0: 0x80, TWDR0: 0xFF 106: TWSR0: 0xA0, TWDR0: 0xFE
Here I want to read a byte from, let's say the register 0x56 of my Atmel Slave. But it seems again to be a bare write sequence.
I would have assumed that the i2cset operation is a combination of some kind: first use a Slave_Addr_Write to tell the I2C Slave which register to read from, but the let a Slave_Addr_Read follow (probably after a REPEATED START). But I seem to be wrong.
What I get is a sequence of 3 Slave_Write_Ack. The first is the address of the register I want to read: 0x56. Then I get another Slave_Write_Ack carrying the "magic number"(=) 0xA1 followed by a Slave_Write_Ack where the Master does nothing on the SDA line, as if the Master expects the Slave to write its data onto the SDA here.
But I can't do that. The TWI module in the AVR is not in a state where it would write data from the TWDR0 to the I2C bus.
Shouldn't I see a 0xB8 (Slave_Read) in this line 104 instead of a 0x80 (Slave_Write_Ack)? In this case I would assume that the TWI module is in the right mood to transmit data from its TWDR0 to the bus.
BTW, the same applies to anything I could do from the Raspberry Pi using the smbus library in Python, so it's not only the behavior of the i2c-tools.
Is there anything wrong with the I2C bus implementation on the Raspberry Pi (or in the i2c-tools of Debian and smbus)? Or is anything fishy with the AVR implementation of the TWI module (is it probably not 100% compatible to the I2C protocol)?
Any help is highly appreciated!
Best regards,
Bernhard