Maker Pro
Maker Pro

I2C Understanding of ACK and NAK?

I do not understand how to implement i2c coding for white box (ACK/NACK Bit) shown in image.


upload_2019-10-14_21-21-6.png

When writing to the slave, the master writes 8 bits of data (slave address) and select write mode

master must check ACK or NAK from the slave as it is writing bytes. If it receives ACK, it continues to send the next byte . If it receives NAK, then it must stop transmission and issue a stoP condition.

(if someone can help with rough code for explanation it would be appreciated )
 
Which processor do you use?.
I have 8051 and PIC16F877A microcontroller

I am trying to implement this in coding

My program supposed to do following things

Generate
  • Start
  • sto
  • repeat Start
  • get ACK/NAK from a slave being written
  • generate ACK/ANK to a slave being read
  • write the first byte of a a slave address with R/W- bit to specify read or write transactions
  • write a data byte to a slave - maybe combine with get ACK/ANK
  • read a data byte from a slave - maybe combine with generate ACK/NAK
 

Harald Kapp

Moderator
Moderator
Use your favorite search engine with these terms:
"8051 i²c library"
"PIC16F877A i²c library"
Read the documentation of the libraries, study the code. This should answer many of your questions.
 
Read the documentation of the libraries, study the code. This should answer many of your questions.
I am trying to understand this code https://aticleworld.com/interfacing-eeprom-using-i2c/
Code:
#include <reg51.h>
//Delay for I2c
#define I2C_DELAY    50
//Define Led Toggle Time
#define TOGGLE_LED  20000
//control address of 24lc64
#define device_addr 0xA0
#define ACK_BIT    0
//Define the Pin for the I2c and lec

sbit SDA_BUS = P2^0;
sbit SCL_BUS = P2^1;
sbit Led = P3^0;

/*=========================================
   Prototypes for I2c functions
 ==========================================*/
void InitI2c(void);
void StartI2c(void);
void RepeatedStartI2c(void);
void StopI2c(void);
void SendAckBit(void);
void SendNackBit(void);
void delay(unsigned int);
bit write_i2c(unsigned char);
unsigned char read_i2c(void);
void write_byte_to_eeprom(unsigned int,unsigned char);
unsigned char  read_byte_from_eeprom(unsigned int);

/*=========================================
   Definition of I2c functions
 ==========================================*/
/**
\brief of  delay function.
This function provide the delay which is used in clock generation.
*/
void delay(unsigned int d)
{
    unsigned int i;
    for(i=0; i<d; i++);
}

/**\brief of InitI2c function.
This function  use to make the data line and clock line idle to put the both line high
*/
void InitI2c(void)
{
    SDA_BUS =1;
    SCL_BUS =1;
}
/**
\brief of StartI2c function.

This function performs the start operation to initiate the communication.
*/
void StartI2c(void)
{
    SDA_BUS  = 1;
    SCL_BUS  = 1;
    delay(I2C_DELAY);
    SDA_BUS  = 0;
    delay(I2C_DELAY);
}
/**
\brief of void RepeatedStartI2c function.
When master does not want to relaese the control from the bus then it assert the repeated
start condition on the i2c bus.
*/
void RepeatedStartI2c()
{
    SCL_BUS  = 0;
    delay(I2C_DELAY/2);
    SDA_BUS  = 1;
    delay(I2C_DELAY/2);
    SCL_BUS  = 1;
    delay(I2C_DELAY/2);
    SDA_BUS  = 0;
    delay(I2C_DELAY);
}
/**
\brief of void StopI2c function.
When master want to stop the communication then it will assert the stop condition to the i2c bus.
*/
void StopI2c(void)
{
    SCL_BUS  = 0;
    delay(I2C_DELAY/2);
    SDA_BUS  = 0;
    delay(I2C_DELAY/2);
    SCL_BUS  = 1;
    delay(I2C_DELAY/2);
    SDA_BUS  = 1;
    delay(I2C_DELAY);
}
/**
\brief of  SendAckBit function.
This function use to send the acknoledgement(ACK) bit the i2c bus.
*/
void SendAckBit()
{
    SCL_BUS  = 0;
    delay(I2C_DELAY/2);
    SDA_BUS  = 0;
    delay(I2C_DELAY/2);
    SCL_BUS  = 1;
    delay(I2C_DELAY);
}
/**
\brief of  SendNackBit function.
This function use to send the Non-acknoledgement(NACK) bit the i2c bus.
*/
void SendNackBit(void)
{
    SCL_BUS  = 0;
    delay(I2C_DELAY/2);
    SDA_BUS  = 1;
    delay(I2C_DELAY/2);
    SCL_BUS  = 1;
    delay(I2C_DELAY);
}
/**
\brief of write_i2c function.
This function use to send signle byte to the I2C Data Bus
*/
bit write_i2c(unsigned char byte)
{
    unsigned char i;
    for(i=0; i<8; i++)
    {
        SCL_BUS  = 0;
        delay(I2C_DELAY);
        if((byte<<i)&0x80)
            SDA_BUS  = 1;
        else
            SDA_BUS  = 0;
        delay(I2C_DELAY/2);
        SCL_BUS  = 1;
        delay(I2C_DELAY);
    }
//ack from slave //
    SCL_BUS  = 0;
    SDA_BUS  = 0;
    delay(I2C_DELAY/2);
    SCL_BUS  = 1;
    delay(I2C_DELAY);
    return SDA_BUS;
}
/**
\brief of write_i2c function.
This function use to read the data from the I2C data bus
*/
unsigned char read_i2c(void)
{
    unsigned char i,d, rxdata=0;
    for(i=0; i<8; i++)
    {
        SCL_BUS  = 0;
        SDA_BUS  = 1;
        delay(I2C_DELAY);
        SCL_BUS  = 1;
        delay(I2C_DELAY/2);
        d=SDA_BUS;
        rxdata=rxdata|(d<<7-i);
        delay(I2C_DELAY);
    }
    return rxdata;
}

I don't understand this part of the code
A function write_i2c return a value and pass the value
Code:
\brief of write_i2c function.
This function use to send signle byte to the I2C Data Bus
*/
bit write_i2c(unsigned char byte)
{
    unsigned char i;
    for(i=0; i<8; i++)
    {
        SCL_BUS  = 0;
        delay(I2C_DELAY);
        if((byte<<i)&0x80)
            SDA_BUS  = 1;
        else
            SDA_BUS  = 0;
        delay(I2C_DELAY/2);
        SCL_BUS  = 1;
        delay(I2C_DELAY);
    }
//ack from slave //
    SCL_BUS  = 0;
    SDA_BUS  = 0;
    delay(I2C_DELAY/2);
    SCL_BUS  = 1;
    delay(I2C_DELAY);
    return SDA_BUS;
}
 

Harald Kapp

Moderator
Moderator
A function write_i2c return a value and pass the value
The for loop sends a byte as a series of bits.
The last part returns the status of the ACK/NACK bit from the I²C slave.
The signals are controlled by setting the port bits (SCL_BUS, SDA_BUS) to eiterh 0 (low) or 1 (high).
Timing is controlled by the delay() function.
Draw a timing diagram following the instruction sequence to understand what's going on.
 
The for loop sends a byte as a series of bits.
The last part returns the status of the ACK/NACK bit from the I²C slave.
The signals are controlled by setting the port bits (SCL_BUS, SDA_BUS) to eiterh 0 (low) or 1 (high).
Timing is controlled by the delay() function.
Draw a timing diagram following the instruction sequence to understand what's going on.

as i have read following function's, both function generate start condition
Code:
void I2CStart()
{
    SDA = 0;  /* clear SDA */
    SCL = 0;   /* clear SCL */
}

Code:
void I2C_start(void)
{
    if(SCL)
    SCL = 0;        /* Clear SCL */

    SDA = 1;        /* Set SDA */
    SCL = 1;        /* Set SCL */

    I2C_delay();

    SDA = 0;        /* Clear SDA */

    I2C_delay();

    SCL = 0;        /* Clear SCL */
}

The default state of SDA and SCL line is high.
My understanding A high to low transition of SDA line while the SCL line is high called the START condition. according this situation first code is not valid for start condition

which is valid function to generate start condition
 

Harald Kapp

Moderator
Moderator
Both functions may be valid. It depends on the context where and how they are used. Although the second function looks more elaborate, the first function may work equally well if it is called from another routine that ensures the correct environment, namely SDA=high and SCL=0.

You can't evaluate the correctness of a subroutine without inspecting the context.
 
Both functions may be valid. It depends on the context where and how they are used..
I am trying to write my own routine for start and stop conditions
  • Start is generated when SDA goes 1-0 when SCL=1
  • Stop is generated when SDA goes 0-1 when SCL=1
What kind of code we can write for the following:

Code:
void START (void)
{
  SDA = 0 ;  Clear DATA
  SCL = 1;   Set clock to 1
}
void STOP (void)
{
  SDA = 0 ;  Clear DATA
  SCL = 1;   Set clock to 1
}

does above statement performs the start and stop task
 

Harald Kapp

Moderator
Moderator
The 8051 may be slow enough for this to work without delay - I frankly do not know.
However, I²C requires a defined timing, specifically setup and hold times between changes in clock and data. On a fast processor simply toggling the port bits one after another may violate these conditions. Therefore delays are routinely used to ensure proper timing sequences.
 
The 8051 may be slow enough for this to work without delay - I frankly do not know.
However, I²C requires a defined timing, specifically setup and hold times between changes in clock and data. On a fast processor simply toggling the port bits one after another may violate these conditions. Therefore delays are routinely used to ensure proper timing sequences.

@Harald Kapp Thank you . I am sure you have worked on I2C communication

I understand the simple code to set or clear port bits

Code:
Port1..0 = 1 /* set port bit P1.0 to 1
Port1..0 = 0 /* clear  port bit P1.0 to 0

same for SDA and SCL

SDA = 1 /* set SDA to 1
SCL = 0 /* clear  SCL to 0

A change in the state of the data line from high to low, while the clock line is high, defines a START condition

I don't understand the transition of SDA and SCL for start condition in I2C communication
 
Last edited:
Refer to page 80 of your PIC16F87XA uC. Ut has a hardware serial communication module, all you need to do is correctly configure registers and follow the setup/operation steps thoroughly documented in the datasheet. Good luck
"Do not try to write in software what you can do in hardware, you won't be able to do it better if at all"
 
Top