Thursday, 6 August 2015

microcontroller - I2C: Unable to Read Multiple Byte using Bit-Banging method


I am using Bit-Banging for I2C communication via PIC24FJ128GA010.


The code works fine for writing 16bytes to EEPROM ( I recieved ACK = 0 for every Byte write ).


While reading EEPROM I am able to read only first Byte. Thereafter all bytes received are 0x00.


My EEPROM Device has 3 Pins ( Data_in, Data_out, CLK).


Here is my code.


**** I2CInterface.h ****


#ifndef I2CINTERFACE_H

#define I2CINTERFACE_H

#include
#include
#include

#define HIGH 1
#define LOW 0

#define SCK PORTFbits.RF6 // Clock Pin for i2c

#define SDA_OUT PORTFbits.RF7 // Data Input Pin
#define SDA_IN PORTFbits.RF8 // Data Output Pin

#define SCK_DIR TRISFbits.TRISF6 // Clock Pin for i2c
#define SDA_OUT_DIR TRISFbits.TRISF7 // Data Input Pin
#define SDA_IN_DIR TRISFbits.TRISF8 // Data Output Pin

#define Set_SDA_OUT_Low ( SDA_OUT = 0 )
#define Set_SDA_OUT_High ( SDA_OUT = 1 )
#define Set_SCK_Low ( SCK = 0 )

#define Set_SCK_High ( SCK = 1 )

#define I2C_SPEED_FACTOR 1 // Low Value means low i2c frequency
#define Crystal_Value 8 // MHz
#define HalfBitDelay (500*Crystal_Value)/(12*I2C_SPEED_FACTOR)

void InitI2C(void);
void I2C_Start(void);
void I2C_ReStart(void);
bool I2C_Write_Byte(unsigned char Byte);

unsigned char I2C_Read_Byte(void);
void I2C_Stop(void);
bool I2C_Get_ACK(void);
void I2C_Send_ACK(void);
void I2C_Send_NACK(void);
unsigned char I2C_Data_Inverter(unsigned char Byte); // this function is just for current circuit.
void __delay_us(unsigned int d);


#endif /* I2CINTERFACE_H */


***** I2CInterface.c *******


#include "I2CInterface.h"


// Function : Set Initial values of SCK & SDA pins
void InitI2C(void)
{

SDA_IN_DIR = 1; // Configure RF8 pin as Input;


SCK_DIR = 0; // Configure RF6 pin as Output;
SDA_OUT_DIR = 0; // Configure RF7 pin as Output;

SCK = 1; // write 1
SDA_OUT = 1; // write 1

}

//Function : I2C_Start sends bit sequence

void I2C_Start(void)
{

Set_SCK_High; // Make SCK pin High
__delay_us( HalfBitDelay/2 ); // Half bit delay

Set_SDA_OUT_High; // Make SDA_OUT pin High
__delay_us( HalfBitDelay/2 ); // Half bit delay

Set_SDA_OUT_Low; // Make SDA_OUT pin Low

__delay_us( HalfBitDelay/2 ); // Half bit delay

}

// Function Purpose: I2C_ReStart sends start bit sequence
void I2C_ReStart(void)
{

Set_SCK_Low; // Make SCK pin low
__delay_us(HalfBitDelay/2); // Data pin should change it's value,

// when it is confirm that SCK is low
Set_SDA_OUT_High; // Make SDA pin High
__delay_us(HalfBitDelay/2); // 1/4 bit delay

Set_SCK_High; // Make SCK pin high
__delay_us(HalfBitDelay/2); // 1/4 bit delay

Set_SDA_OUT_Low; // Make SDA Low
__delay_us(HalfBitDelay/2); // 1/4 bit delay


}

void I2C_Stop(void)
{

Set_SCK_Low;
__delay_us( HalfBitDelay/2 );

Set_SDA_OUT_Low; // Make SDA pin low
__delay_us( HalfBitDelay/2 ); // 1/4 bit delay


Set_SCK_High; // Make SCK pin low
__delay_us( HalfBitDelay/2 ); // Data pin should change it's value,when it is confirm that SCK is low

Set_SDA_OUT_High; // Make SDA high
__delay_us( HalfBitDelay/2 ); // 1/4 bit delay

}

bool I2C_Write_Byte(unsigned char Byte)

{
unsigned char i; // Variable to be used in for loop

bool ack = false;

for(i=0;i<8;i++) // Repeat for every bit
{
Set_SCK_Low; // Make SCK pin low

__delay_us(HalfBitDelay/2); // Data pin should change it's value,

// when it is confirm that SCK is low

if((Byte< Set_SDA_OUT_High; // If bit is high, make SDA high
else // Data is transferred MSB first
Set_SDA_OUT_Low; // If bit is low, make SDA low

__delay_us(HalfBitDelay/2); // Toggle SCK pin
Set_SCK_High; // So that slave can
__delay_us(HalfBitDelay); // latch data bit


}
Set_SCK_Low;
__delay_us( HalfBitDelay );

Set_SCK_High;
__delay_us( HalfBitDelay );

ack = SDA_IN;


return ack;

}

unsigned char I2C_Read_Byte(void)
{
unsigned char i, RxData = 0;

for(i=0;i<8;i++)
{

Set_SCK_Low; // Make SCK pin low
__delay_us(HalfBitDelay); // Half bit delay
Set_SCK_High; // Make SCK pin high
__delay_us( HalfBitDelay ); // 1/4 bit delay
RxData = RxData |( SDA_IN << (7-i) ); // Captured received bit
}

return RxData; // Return received byte
}



//Function : I2C_Send_ACK sends ACK bit sequence
void I2C_Send_ACK(void)
{

Set_SCK_Low; // Make SCK pin low
__delay_us(HalfBitDelay/2); // Data pin should change it's value,
// when it is confirm that SCK is low
Set_SDA_OUT_Low; // Make SDA High
__delay_us(HalfBitDelay/2); // 1/4 bit delay


Set_SCK_High; // Make SCK pin high
__delay_us(HalfBitDelay); // Half bit delay

}


//Function : I2C_Send_NACK sends NACK bit sequence
void I2C_Send_NACK(void)
{


Set_SCK_Low; // Make SCK pin low
__delay_us(HalfBitDelay/2); // Data pin should change it's value,
// when it is confirm that SCK is low
Set_SDA_OUT_High; // Make SDA high
__delay_us(HalfBitDelay/2); // 1/4 bit delay

Set_SCK_High; // Make SCK pin high
__delay_us(HalfBitDelay); // Half bit delay


}

// Function Purpose: Produce approximate delay in given uSecs.
void __delay_us(unsigned int d)
{
unsigned int i, limit;
limit = d/15;

for(i=0;i

}

**** main.c ****


#include 
#include
#include
#include
#include
#include
#include


#include "src/idmodule.h"
#include "src/lcd.h"
#include "src/I2CInterface.h"

_CONFIG1( JTAGEN_OFF & FWDTEN_OFF )
_CONFIG2( FNOSC_FRCPLL & OSCIOFNC_OFF )


void writeData( void );

void readData( void );
void wait();

bool ackWriteOp[16] = {false};
bool ackReadOp[16] = {false};

bool ackWrite_1 = false, ackWrite_2 = false;
bool ackRead_1 = false, ackRead_2 = false, ackRead_3 = false;

unsigned char dataRead[16] = {0};


unsigned char addr_byte = 0x70;
unsigned char data_byte [] = { 'r', 'a', 't', 'n', 'e', 's', 'h', '#', 's', 'u', 'd', 'h', 'e', 'e', 'r', '#' };


int main()
{
TRISA = 0;
TRISD = 0;


LCD_Initialize();

idmInitI2C();

writeData();

wait();
wait();

readData();


return 0;

}

void writeData( void )
{
unsigned char i;

I2C_Start();


ackWrite_1 = I2C_Write_Byte( 0xA0 );

ackWrite_2 = I2C_Write_Byte( 0x70 );

for (i = 0; i < 16; i++)
{
ackWriteOp[i] = I2C_Write_Byte( data_byte[i]);

}


I2C_Stop();
}

void readData( void )
{
unsigned char i;

I2C_Start();


ackRead_1 = I2C_Write_Byte(0xA0);

ackRead_2 = I2C_Write_Byte(0x70);

I2C_Restart();
ackRead_3 = I2C_Write_Byte(0xA1);

for ( i = 0; i < 16; i++)
{
dataRead[i] = I2C_Read_Byte();


if (i < 15){
I2C_Send_ACK();
}

}

I2C_Send_NACK();
I2C_Stop();


for (i = 0; i < 16; i++)
{
LCD_PutChar ( dataRead[i] ) ;
}
}

void wait()
{
unsigned int i, j;


for (i = 0 ; i < 2000; i++)
{
for (j = 0; j < 1000; j++);
}

}

I am unable to find the cause. Might be missing something. Please help me finding the issue.


EDIT: I checked the data writted to the device by manually changing the Byte-addresses. And I can see all the data stored in the device. I guess internally address is not auto-increasing. Is there a way out to command the device for auto increment or is there any logical issue in my code??



Answer




Brief description about the issue.


I have an EEPROM device with 3 pins. Detail of the device can be found Here


I used I2C Bit-Banging concept as suggested to start communication with the device.


Finally I got stuck with the issue. I was able to write the device ( with recieving ACK = 0) for each byte written.


While reading the device I was able to Read the First Byte from the device while for rest Byte I was getting 0x00.


After following all the suggestions above I found that I need to pull SDA_OUT pin HIGH while reading each bit.


Finally I was able to Read all the Bytes from the device.


I am posting I2CInterface.c again with all the changes that made it working.


**** I2CInterface ****


#include "I2CInterface.h"


unsigned tempData[16] = {0};

// Function : Set Initial values of SCK & SDA pins
void InitI2C(void)
{
SDA_IN_DIR = 1; // Configure RF8 pin as Input;

SCK_DIR = 0; // Configure RF6 pin as Output;
SDA_OUT_DIR = 0; // Configure RF7 pin as Output;


SCK = 1; // write 1
SDA_OUT = 1; // write 1

}

//Function : I2C_Start sends bit sequence
void I2C_Start(void)
{
Set_SCK_High; // Make SCK pin High

__delay_us( HalfBitDelay/2 ); // Half bit delay

Set_SDA_OUT_High; // Make SDA_OUT pin High
__delay_us( HalfBitDelay/2 ); // Half bit delay

Set_SDA_OUT_Low; // Make SDA_OUT pin Low
__delay_us( HalfBitDelay/2 ); // Half bit delay

}


// Function Purpose: I2C_ReStart sends start bit sequence
void I2C_ReStart(void)
{
Set_SCK_Low; // Make SCK pin low
__delay_us(HalfBitDelay/2); // Data pin should change it's value,
// when it is confirm that SCK is low
Set_SDA_OUT_High; // Make SDA pin High
__delay_us(HalfBitDelay/2); // 1/4 bit delay

Set_SCK_High; // Make SCK pin high

__delay_us(HalfBitDelay/2); // 1/4 bit delay

Set_SDA_OUT_Low; // Make SDA Low
__delay_us(HalfBitDelay/2); // 1/4 bit delay

}

// Function : I2C_Stop to generate Stop Sequence
void I2C_Stop(void)
{

Set_SDA_OUT_Low; // Make SDA pin low
__delay_us( HalfBitDelay/2 ); // 1/4 bit delay

Set_SCK_Low;
__delay_us( HalfBitDelay/2 );

Set_SCK_High; // Make SCK pin low
__delay_us( HalfBitDelay ); // Data pin should change it's value,when it is confirm that SCK is low

Set_SDA_OUT_High; // Make SDA high

__delay_us( HalfBitDelay ); // 1/4 bit delay

}

// Function : I2C_Write_Byte to write Bytes
bool I2C_Write_Byte(unsigned char Byte)
{
unsigned char i; // Variable to be used in for loop

bool ack = false;


for(i=0;i<8;i++) // Repeat for every bit
{
Set_SCK_Low; // Make SCK pin low

__delay_us(HalfBitDelay/2); // Data pin should change it's value,
// when it is confirm that SCK is low

if((Byte< Set_SDA_OUT_High; // If bit is high, make SDA high

else // Data is transferred MSB first
Set_SDA_OUT_Low; // If bit is low, make SDA low

__delay_us(HalfBitDelay/2); // Toggle SCK pin
Set_SCK_High; // So that slave can
__delay_us(HalfBitDelay); // latch data bit

}
Set_SCK_Low;
__delay_us( HalfBitDelay );


Set_SCK_High;
__delay_us( HalfBitDelay );

ack = SDA_IN;

return ack;
}

// Function : I2C_Read_Byte reads Byte

unsigned char I2C_Read_Byte(void)
{
unsigned char i, RxData = 0;

for(i=0;i<8;i++)
{
Set_SCK_Low; // Make SCK pin low
__delay_us(HalfBitDelay); // Half bit delay

Set_SCK_High; // Make SCK pin high

__delay_us( HalfBitDelay ); // 1/4 bit delay

RxData = RxData |( SDA_IN << (7-i) ); // Captured received bit
__delay_us( HalfBitDelay/2 ); // 1/4 bit delay

Set_SDA_OUT_High;
__delay_us(HalfBitDelay/2); // 1/4 bit delay

}


return RxData; // Return received byte
}

//Function : I2C_Send_ACK sends ACK bit sequence
void I2C_Send_ACK(void)
{

Set_SCK_Low; // Make SCK pin low
__delay_us(HalfBitDelay/2); // Data pin should change it's value,
// when it is confirm that SCK is low

Set_SDA_OUT_Low; // Make SDA High
__delay_us(HalfBitDelay/2); // 1/4 bit delay

Set_SCK_High; // Make SCK pin high
__delay_us( HalfBitDelay ); // Half bit delay

}

//Function : I2C_Send_NACK sends NACK bit sequence
void I2C_Send_NACK(void)

{
SDA_OUT_DIR = 0;

Set_SDA_OUT_Low;
__delay_us(HalfBitDelay/2);

Set_SCK_Low; // Make SCK pin low
__delay_us(HalfBitDelay/2); // Data pin should change it's value,
// when it is confirm that SCK is low
Set_SDA_OUT_High; // Make SDA high

__delay_us(HalfBitDelay/2); // 1/4 bit delay

Set_SCK_High; // Make SCK pin high
__delay_us(HalfBitDelay); // Half bit delay

}

// Function Purpose: Produce approximate delay in given uSecs.
void __delay_us(unsigned int d)
{

unsigned int i, limit;
limit = d/15;

for(i=0;i
}

No comments:

Post a Comment

arduino - Can I use TI&#39;s cc2541 BLE as micro controller to perform operations/ processing instead of ATmega328P AU to save cost?

I am using arduino pro mini (which contains Atmega328p AU ) along with cc2541(HM-10) to process and transfer data over BLE to smartphone. I...