ATmega I2C Slave + Raspberry Pi

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

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);
    case 0xC0:
    case 0xC8:
        TWCR0 = (1 << TWEN) | (1 << TWINT) | (1 << TWEA) | (0 << TWIE);

    case 0x60:
    case 0x68:
        TWCR0 = (1 << TWEN) | (1 << TWINT) | (1 << TWEA) | (0 << TWIE);
    case 0x70:
    case 0x78:
    case 0x90:
    case 0x98:
        TWCR0 = (1 << TWEN) | (1 << TWINT) | (0 << TWIE);
    case 0x80:
    case 0x88:
        TWCR0 = (1 << TWEN) | (1 << TWINT) | (1 << TWEA) | (0 << TWIE);
    case 0xA0:
        TWCR0 = (1 << TWEN) | (1 << TWINT) | (1 << TWEA) | (0 << TWIE);


void I2C_loop(void) {

    TWCR0 = (1 << TWEN) | (1 << TWINT) | (1 << TWEA);
    for (;;) {
        while (!(TWCR0 & (1 << TWINT)));

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



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,


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

bgoedel wrote:
Is there anything wrong with the I2C bus implementation on the Raspberry Pi


Yes, R-Pi Zero has a clock-stretching bug.


This link is to a little blink program so that I can toggle an LED with an SMBus command. I am only doing the SMBus block commands, and the TWI software is not well tested.


SMBus uses I2C oddly if you are expecting the R-Pi to work like normal I2C then stop expecting that.


update: my python example on Github has some errors (I will update it at some point).


#!/usr/bin/env python3
#Use Raspberry Pi /dev/i2c-1

# notes:

import smbus

# on a Raspberry Pi with 256k memory use 0 for /dev/i2c-0
bus = smbus.SMBus(1)

data = [3]

#write the command

# up to 32 bytes can be buffered
num_of_bytes = len( [I2C_COMMAND_TO_TOGGLE_LED_BUILTIN_MODE] ) + len (data)
# the command is included in the echo

echo = bus.read_i2c_block_data(RPUBUS_MGR_I2C_ADDR, I2C_COMMAND_TO_TOGGLE_LED_BUILTIN_MODE, num_of_bytes)


Last Edited: Thu. Mar 26, 2020 - 12:49 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you very much for taking the time to explain that to me. I really didn't have a clue about that.


This might also be an explanation for the problems that I have with the PCA9539 (TCA9539) IO Port Expander.


I abandoned the idea of using I²C for my solution and switched to USB, taking the one of the USB capable AVR8 devices AT90USB162 or ATmega32U2.



Thank you for clarifying that for me.


Kind regards,