I2C: no se puede leer varios bytes mediante el método de Bit-Banging

1

Estoy utilizando Bit-Banging para la comunicación I2C a través de PIC24FJ128GA010.

El código funciona bien para escribir 16 bytes en EEPROM (recibí ACK = 0 por cada escritura de bytes).

Mientras leo EEPROM, solo puedo leer el primer byte. A partir de entonces, todos los bytes recibidos son 0x00.

Mi dispositivo EEPROM tiene 3 pines (Data_in, Data_out, CLK).

Aquí está mi código.

**** I2CInterface.h ****

#ifndef I2CINTERFACE_H
#define I2CINTERFACE_H

#include <xc.h>
#include <p24FJ128GA010.h>
#include <stdbool.h>

#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<<i)&0x80)  // Place data bit value on SDA pin
            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<limit;i++);

}

**** main.c ****

#include <xc.h>
#include <p24FJ128GA010.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <stdbool.h>
#include <i2c.h>

#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++);
    }

}

No puedo encontrar la causa. Podría estar perdiendo algo. Por favor, ayúdame a encontrar el problema.

EDITAR: verifiqué los datos escritos en el dispositivo cambiando manualmente las direcciones de bytes. Y puedo ver todos los datos almacenados en el dispositivo. Supongo que la dirección interna no es auto-creciente. ¿Hay alguna forma de controlar el dispositivo para un incremento automático o hay algún problema lógico en mi código?

    
pregunta skg

3 respuestas

1

Breve descripción sobre el tema.

Tengo un dispositivo EEPROM con 3 pines. El detalle del dispositivo se encuentra Aquí

Utilicé el concepto I2C Bit-Banging como se sugiere para iniciar la comunicación con el dispositivo.

Finalmente me quedé atascado con el problema. Pude escribir el dispositivo (con la recepción de ACK = 0) para cada byte escrito.

Mientras leía el dispositivo, pude leer el primer byte del dispositivo, mientras que para el descanso, el byte recibía 0x00.

Después de seguir todas las sugerencias anteriores, encontré que necesito tirar del pin SDA_OUT ALTO mientras leo cada bit.

Finalmente pude leer todos los bytes del dispositivo.

Estoy publicando I2CInterface.c de nuevo con todos los cambios que lo hicieron funcionar.

**** 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<<i)&0x80)  // Place data bit value on SDA pin
            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<limit;i++);

}
    
respondido por el skg
2

Después de leer un byte, se llama I2C_Send_ACK , que establece SDA en bajo. Después, siempre lee 0 en SDA ya que nunca lo suelta de nuevo (hasta que llama a I2C_Send_NACK ). Debería establecer SDA en alto (sin unidad) cuando llame a su función I2C_Read_Byte .

Además, debe cambiar su Set_SDA_OUT_Low y Set_SDA_OUT_High de tal manera, que al configurarlo en bajo establece el pin como salida y lo baja, y cuando lo configura en alto establece el pin como entrada.

    
respondido por el Tobias Müller
1

Para mí, parece que tus microcontroladores controlan las líneas SDA y SCK todo el tiempo. El bus I2C es un bus de drenaje abierto, lo que significa que solo maneja 0 en el bus mientras que 1 es generado por los pull-ups (por lo tanto, es mucho más lento que SPI). La forma de hacer bitbang es que, si no puede realizar un triple estado de sus IO, debe configurar el pin PORT en '0' y usar el registro TRIS para conducir el bus. Cuando TRIS = '0', la línea se maneja y cuando TRIS = '1' se lanza la línea.
Además, no es necesario usar 2 pines IO por línea.

Modifique su código en consecuencia y vea qué sucede.

    
respondido por el Alexxx

Lea otras preguntas en las etiquetas