I implement bit-banging I2C on Atmega328P and met the problem when the Master receives data from the slave. I'm in depression in debugging this code since I found no problem. Master Write work perfectly in many cases (Receive ACK, NAK if wrong slave address, sen multiple data,... ) But When Master want to read data from slave it only send 7/8 bit :( The receive algorithm seems correct just missing 1 bit! Please help me!
Master Arduino Using bit-banging i2c on AVR, Slave Arduino using Wire.h (Master writing work perfect)
Master AVR code
#include <util/delay.h>
#include <stdint.h>
// CODE from STM32
#define SDA PC4
#define SCL PC5
//OUTPUT REGISTER: PORTC
// PC4: Pin A4 - SDA | PC5: Pin A5 - SCL | Arduino (Atmega328P)
#define SDA_ON (PORTC |= (1 << PC4))
#define SDA_OFF (PORTC &= ~(1 << PC4))
#define SCL_ON (PORTC |= (1 << PC5)) // PORTC = 0b0000 0010
#define SCL_OFF (PORTC &= ~(1 << PC5))
#define READ_SDA() (PINC & (1 << SDA))
#define READ_SCL() (PINC & (1 << SCL))
#define SET_SDA_OUT() DDRC |= (1 << SDA)
#define SET_SDA_IN() DDRC &= ~(1 << SDA)
#define I2C_READ 0x01
#define I2C_WRITE 0x00
#define ADDR 0x08 //Slave Address
#define ADDR_W (ADDR << 1)
#define ADDR_R (ADDR_W | 1)
void init()
{
DDRC = (1 << DDC5) | (1 << DDC4);
SCL_ON;
SDA_ON;
}
void start()
{
SDA_ON;
_delay_us(4.5);
SCL_ON;
_delay_us(4.5);
SDA_OFF;
_delay_us(4.5);
SCL_OFF;
_delay_us(4.5);
}
/* i2c stop sequence */
void stop()
{
SDA_OFF;
_delay_us(4.5);
SCL_ON;
_delay_us(4.5);
SDA_ON;
_delay_us(4.5);
}
/* Transmit 8 bit data to slave */
uint8_t Tx(uint8_t dat)
{
for (uint8_t i = 0; i < 8; i++)
{
if (dat & 0x80)
{
SDA_ON;
}
else
{
SDA_OFF;
}
_delay_us(3);
SCL_ON;
_delay_us(5);
SCL_OFF;
_delay_us(2);
dat <<= 1;
}
SET_SDA_IN();
_delay_us(3);
SCL_ON; // the 9th pulse is high
_delay_us(3);
if (READ_SDA())
{
_delay_us(2);
SCL_OFF; // the 9th pulse is low
PORTC = 0x00;
SET_SDA_OUT();
return 0;
}
else
{
_delay_us(2);
SCL_OFF; // the 9th pulse is low
PORTC = 0x00;
SET_SDA_OUT();
return 1;
}
}
uint8_t Rx(void)
{
uint8_t byte = 0;
uint8_t bit = 0;
_delay_us(10);
SET_SDA_IN();
for (bit = 0; bit < 8; bit++)
{
byte <<= 1;
do
{
SCL_ON;
} while (READ_SCL() == 0);
_delay_us(3);
if (READ_SDA())
{
byte |= 0x01;
}
_delay_us(2);
SCL_OFF;
_delay_us(5);
}
PORTC = 0b00010000;
SET_SDA_OUT();
SDA_ON;
SCL_ON;
_delay_us(3);
SCL_OFF;
_delay_us(3);
return 1;
}
int main(void)
{
init();
_delay_ms(3000);
unsigned char ack;
while (1)
{
start();
ack = Tx(ADDR_R);
if (!ack)
{
stop();
break;
}
Rx();
stop();
_delay_ms(1000);
}
}```
**Slave code using Wire.h**
```#include <Wire.h>
void setup() {
Wire.begin(); // join i2c bus (address optional for master)
Serial.begin(9600); // start serial for output
pinMode(10,OUTPUT);
}
void loop() {
Wire.requestFrom(0x08, 1); // (a,b) request b bytes from slave device #a
while (Wire.available()) { // slave may send less than requested
char c = Wire.read(); // receive a byte as character
Serial.print("Master received data: ");Serial.println(c);
delay(100);
}
}```