I am communicating with I2C/TWI between a MCU (Atmega328p) as Slave receiver and the raspberry pi 5 as master transmitter. I2C is at 100kHz configuration (the slower the Rpi5 can go).
I have an issue when, if my MCU after it receives the transmitted byte and replies with ACK , it looks like it shifts the whole received byte by 1 bit to the left. Example: 0xFF (11111111) is sent, the MCU receives 0xFE (11111110).
If, after the MCU receives a byte, responds with NACK, the MCU reads the byte as normal, I do not face any issue using NACK. And I cannot understand why is that.
I looked at it on the oscilloscope. 0xFF is sent from the PI, and the two plots are:
When NACK returned from the MCU:

When ACK returned from the MCU:

Plots look fine and since this is the only issue with the I2C bus, makes me believe that the MCU's firmware is not correct.
Raspberry pi's software: (writes the data, shows error only when MCU replies with NACK, but data are send correctly as shown in the oscilloscope):
#include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> #include <linux/i2c-dev.h> #include <stdint.h> int main() { printf("make file\n\r"); int file; printf("Make filename\n\r"); char filename[] = "/dev/i2c-1"; printf("Pick address\n\r"); int addr = 0x08; // Replace with your I2C device address uint8_t buffer[1]; buffer[0]=0xFF; printf("Open the I2C device file\n\r"); // Open the I2C device file if ((file = open(filename, O_RDWR)) < 0) { perror("Failed to open the I2C bus"); return 1; } printf("Set the I2C device address\n\r"); // Set the I2C device address if (ioctl(file, I2C_SLAVE, addr) < 0) { perror("Failed to acquire bus access and/or talk to slave"); return 1; } printf("Write data\n\r"); // Write data // Example: Writing a single byte if (write(file, buffer, 1) != 1) { perror("Failed to write to the I2C bus"); return 1; } return 0; } MCU's I2C setup:
BIT_SET(SREG, BIT(7));//Interrupt bit in SREG set //slave TWAR = 0B00010001;//Device’s Own Slave Address is 0x08 (0001000X), responds to general call(XXXXXX1) BIT_CLEAR(TWCR, BIT(TWIE));//I2C Interrupt disable BIT_CLEAR(TWCR, BIT(TWSTA));//Clear TWSTA to become slave BIT_CLEAR(TWCR, BIT(TWSTO));//Stop condition disabled BIT_SET(TWCR, BIT(TWEA));//acknowledge own address BIT_SET(TWCR, BIT(TWEN));//TWI Enable MCU's Main Loop (the "sw" function is serialWrite to the COM port of the MCU):
/*After its own slave address and the write bit have been received, the TWINT Flag is set and a valid status code can be read from TWSR*/ if (BIT_GET(TWCR, BIT(TWINT)))//if TWI interrupt flag is set { static uint8_t twStatus,receivedData; twStatus = TWSR;//read the status BIT_CLEAR(twStatus, BIT(0));//clear prescaler values BIT_CLEAR(twStatus, BIT(1)); sw("TWSR status code is ",1); swn(twStatus,16,0,1); //serialWrite the current status sw(":->",1); //serialWrite //Slave receiver if (twStatus == 0x60) { sw("Own SLA+W received, ACK returned\n\r",1); BIT_CLEAR(TWCR, BIT(TWSTA)); BIT_CLEAR(TWCR, BIT(TWSTO)); //Data byte will be received and ACK will be returned. BIT_SET(TWCR, BIT(TWEA)); } else if (twStatus == 0x80) { sw("Prev addressed with SLA+W, data received, ACK returned\n\r",1); BIT_CLEAR(TWCR, BIT(TWSTA)); BIT_CLEAR(TWCR, BIT(TWSTO)); //Data byte will be received and ACK will be returned. BIT_SET(TWCR, BIT(TWEA)); sw("Data: ",1); receivedData = TWDR; swn(receivedData,16,1,1); sw("\n\r",1); } else if (twStatus == 0x88) { sw("Prev addressed with own SLA+W, data received, ACK returned\n\r",1); //Switch to non addressed slave, Own SLA recognized. BIT_CLEAR(TWCR, BIT(TWSTA)); BIT_CLEAR(TWCR, BIT(TWSTO)); BIT_SET(TWCR, BIT(TWEA)); //sw("Data: ",1); receivedData = TWDR; swn(receivedData,16,1,1); sw("\n\r",1); } else if (twStatus == 0xA0) { sw("Stop or repeated start received while being slave.\n\r",1); //Switch to non addressed slave, Own SLA recognized. BIT_CLEAR(TWCR, BIT(TWSTA)); BIT_CLEAR(TWCR, BIT(TWSTO)); BIT_SET(TWCR, BIT(TWEA)); sw("Data: ",1); receivedData = TWDR; swn(receivedData,16,1,1); sw("\n\r",1); } /*clearing this flag starts the operation of the TWI, so all accesses to the TWI Address Register (TWAR), TWI Status Register (TWSR), and TWI Data Register (TWDR) must be complete before clearing this flag.*/ BIT_SET(TWCR, BIT(TWINT));//WRITE 1 to TWINT to clear it And the serial output of the MCU is:
TWSR status code is 60:->Own SLA+W received, ACK returned
TWSR status code is 80:->Prev addressed with SLA+W, data received, ACK returned
Data: fe
Side note: Because my MCU uses 5V logic while the Pi uses 3.3V logic, I use LSF0102DCUR to convert the 5V to 3.3V and vise versa. I was probing the side where the RPI is looking at on the above plots. The MCU's side is the same (no jittering or any funny business on the bus so I thought its not worth mentioning, I probe and leave the MCU's side (using ACK) below:
Schematic/3D:
Layout (I tried to put all the components as close as possible):





