Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

PIC32 + C32 can't read I2C BUS

Status
Not open for further replies.

SUPER COPTER

New Member
Hi I'm new to the forum.
I'm using a Pinguino board which has a PIC32MX440F256H microcontroller. I'm not using the Pinguino IDE, instead I'm using MPLAB with C32 compiler. I'm also using a PicKit3 programmer for loading the code to the chip.
I've been trying to make I2C bus to work with no luck.

Currently I can send bytes over the BUS, I´ve seen them whith an oscilloscope, and I get Ack's from the slave device.
I have a DS2482-100 1-Wire IC hooked up to the I2C BUS, I've made a function for writing the configuration following the steps mentioned. It (kind of) works (I recive Ack's after every command), I'm also checking the BUS's status after every step. The program gets stuck at the stop command, with a status=408. This just happens when executing the write configuration (config_status()) function, the stop command works fine in other conditions. I know status=8 means that the bus is in a START condition. Other testing before gave me error code 400 which means "Arbitration Loss" (BUS Collision). So I dont know what this 408 code would mean.
Besides that, I've been trying to read the supposed data I sould recive for checking the write configuration.
I'm using the following function to read the data:

Code:
UINT8 i2c_read_buffer(BYTE address)
{
    UINT8 i2cbyte=0;
    StartTransfer(0);
while ( !I2CAcknowledgeHasCompleted(I2C_BUS) );
    if (I2CReceivedDataIsAvailable(I2C_BUS))
    {
        I2CAcknowledgeByte(I2C_BUS, TRUE);
        i2cbyte = I2CGetByte(I2C_BUS);
        return TRUE;
    }
    else
    {
        return FALSE;
    }
    StopTransfer();
    return i2cbyte;
}

which always returns FALSE, so it's not even executing the code inside the "if" statement. In other words, the I2CReceivedDataIsAvailable(I2C_BUS) function is returning FALSE, so I'm assuming that means the I2C read buffer is empty.

I searched the web several times to find some info about this and it seems I should be sending the slave adress before reading but, anyway, the buffer is empty so it's not even getting to that part of the code.

Here's the library I wrote for handling the Ds2482:

Code:
#define dly 1000
int Ack_count=1;
char msg[16];
UINT8 mico;

void OW_S(void)
{
    StartTransfer(0);
    print_error("S");
    DelayMs(dly);
}

void OW_Sr(void)
{
    //StopTransfer();
    StartTransfer(1);
    print_error("Sr");
    DelayMs(dly);
}

void OW_AD0(UINT8 address)
{
    I2C_7_BIT_ADDRESS   SlaveAddress;
    I2C_FORMAT_7_BIT_ADDRESS(SlaveAddress, address, I2C_WRITE);
    TransmitOneByte(SlaveAddress.byte);
    print_error("AD,0");
    DelayMs(dly);
}

void OW_AD1(UINT8 address)
{
    I2C_7_BIT_ADDRESS   SlaveAddress;
    I2C_FORMAT_7_BIT_ADDRESS(SlaveAddress, address, I2C_READ);
    TransmitOneByte(SlaveAddress.byte);
    print_error("AD,1");
    DelayMs(dly);
}

void OW_A()
{
    if(!I2CByteWasAcknowledged(I2C_BUS))    //OJO CON ESO
    {                                //EN VOLA WHILE
        print_error("NAck");
        DelayMs(1*dly);   
    }
    else
    {
        sprintf(msg,"Ack %d",Ack_count);
        print_error(msg);
        DelayMs(dly);
        Ack_count++;   
    }

}

void OW_DRST()
{
    I2CSendByte(I2C_BUS, 0xF0);
    print_error("DRST");
    DelayMs(dly);
}

void OW_WCFG(void)
{
    I2CSendByte(I2C_BUS, 0xD2);
    print_error("WCFG");
    DelayMs(dly);
}

void OW_P()
{
    StopTransfer();
    print_error("P");
    DelayMs(dly);
}

void config_status(void)
{
    OW_S();
    print_status();
    OW_AD0(OW_ADDRESS);
    print_status();
    OW_A();                    //Ack1
    print_status();
    OW_WCFG();
    print_status();
    OW_A();                    //Ack2
    print_status();
    TransmitOneByte(0xE1);       
    print_status();   
    OW_A();                    //Ack3
    print_status();
    OW_Sr();
    print_status();
    OW_AD1(OW_ADDRESS);
    print_status();
    OW_A();                    //Ack4
    print_status();
    //DATA
    print_status();
    OW_A();                    //Ack5
    print_status();
    OW_P();
    print_status();
    mico=i2c_read_buffer();
    print_status();
}

I I made another library with the primitive functions, but I didn't find it useful to post, unless someone asks for it. I'm using another 2 custom libraries for the DelayMs() function and for a 16x2 serial LCD (print_error() function).

Any help would be very appreciated.
 
Let me get this right!!! The DS2xxx is a one wire to I2c chip..

What is the one wire device on the other end?? It may not be the I2C it might be the "other " device..
I found directly talking to OW devices using bit banging was a tad easier!!

BTW.. I use the same chip on a pinguino micro!
 
Hi, thanks for your reply. I haven´t hooked up any 1 wire device yet... I thought I would be able to use the "write configuration" command sequence and then read back the data. Would it be necessary for the DS2482 to have a OW device connected to work?
I know bit-banging can be done, but that wouldn't be suitable for my project, I need to use the HW controller, besides I'm kind of trying to test the I2C bus itself so I could use it with other I2C devices, such as an I2C 16x2 display which I also tried to make work with no luck (just able to ses random cursors), I still reicived Acks after sending the display address.
 
Where did you get the I2C software from??? I always write my own... I never trust Microchip's versions, the interrupts aren't correct...

The last time I used I2C on the pic32 I used a software implementation due to limitations on the Pinguino micro...

I really need to port my I2C stuff, but as I use MikroC, I don't really need to as theirs works very well.

Have you got the low level I2C code so I can see it!!
 
I'm using some microchip example code I found on the internet for I2C EEPROM, I adapted the code and made custom functions (some of them not even used).

Here's the code of the primitive functions:
Code:
void initI2C()
{                   
    I2CConfigure(I2C1, I2C_ENABLE_HIGH_SPEED | I2C_STOP_IN_IDLE | I2C_ENABLE_SMB_SUPPORT);
    I2CSetFrequency(I2C1, GetPeripheralClock(), 100000);
    I2CEnable(I2C1, TRUE);
}

// Set the I2C baudrate
void Set_I2C_Clock(UINT32 FREQ)
{
    UINT32 actualClock;
    char    text[16];
        actualClock = I2CSetFrequency(I2C_BUS, GetPeripheralClock(), FREQ);
        if ( abs(actualClock-FREQ) > FREQ/10)
        {
            sprintf(text, "Freq (%u) error", (unsigned)actualClock);
            print_error(text);
            DelayMs(1000);
        }
        else
    {
            sprintf(text, "Clock: %uKHz", ((unsigned)actualClock)/1000);
            print_error(text);
            DelayMs(300);
    }
}

//START TRANSFER
BOOL StartTransfer( BOOL restart )
{
    I2C_STATUS  status;  
    if(restart)    // Start = 0;  Restart = 1
    {
        I2CRepeatStart(I2C_BUS);
    }
    else
    {
        // Wait for the bus to be idle, then start the transfer
        while( !I2CBusIsIdle(I2C_BUS) );
        if(I2CStart(I2C_BUS) != I2C_SUCCESS)
        {
            print_error("Start Error");
            DelayMs(1000);
            return FALSE;
        }
    //print_error("Start Transfer");
    //DelayMs(1);
    }
    // Wait for the signal to complete
    do
    {
        status = I2CGetStatus(I2C_BUS);
    }
    while (!(status & I2C_START));
        return TRUE;
}

//TRANSMIT ONE BYTE
BOOL TransmitOneByte( UINT8 data )
{
    // Wait for the transmitter to be ready
    while(!I2CTransmitterIsReady(I2C_BUS));
  
    //Transmit the byte
    if(I2CSendByte(I2C_BUS, data) == I2C_MASTER_BUS_COLLISION)
    {
        print_error("Master Bus Collision");
        return FALSE;
    }
    // Wait for the transmission to finish
    while(!I2CTransmissionHasCompleted(I2C_BUS));
        return TRUE;
}

//STOP TRANSFER
void StopTransfer( void )
{
    I2C_STATUS  status;
    // Send the Stop signal
    I2CStop(I2C_BUS);
    // Wait for the signal to complete
    do
    {
        status = I2CGetStatus(I2C_BUS);

        char chip[16];
        sprintf(chip,"Stop: %x",status);
        DelayMs(100);
        print_error(chip);

    }
    while ( !(status & I2C_STOP) );
}

//Busca direcciones I2C en bus
UINT8 Address_Search(void)
{
    TRISDbits.TRISD1 = 0;
    TRISGbits.TRISG6 = 0;
    LATDbits.LATD1 = 0;
    LATGbits.LATG6 = 0;

    static UINT8 vector[20];
    UINT8 Test_Addr;
    int valid;
    char maxpower[20];
    I2C_7_BIT_ADDRESS   SlaveAddress;
    UINT8 Addr;
    print_error("Buscando...");
    StartTransfer(0);
    for(Test_Addr=0x08; Test_Addr < 0x7F; Test_Addr++)
    {
    StartTransfer(1);
        I2C_FORMAT_7_BIT_ADDRESS(SlaveAddress, Test_Addr, I2C_WRITE);
        Addr = SlaveAddress.byte; //Direccion Formateada
        TransmitOneByte(Addr);
        if(!I2CByteWasAcknowledged(I2C_BUS))
        {
            LATDbits.LATD1 = 1;
            delay(1000);
            LATDbits.LATD1 = 0;
            delay(100000);

        }
            else
            {
            LATGbits.LATG6 = 1;
            delay(1000);
            LATGbits.LATG6 = 0;
            sprintf(maxpower, "Direccion: 0x%X",Test_Addr);
            print_error(maxpower);
            delay(100000);
            }
        StopTransfer();
    }
    print_error("Fin Busqueda");
}

//ACK = LED VERDE, NACK = LED AMARILLO
void Test_Address(UINT8 test)
{
    I2C_7_BIT_ADDRESS   SlaveAddress;
    UINT8 Addr;
    char maxpower[20];
    StartTransfer(0);
    //Formato Direccion
    I2C_FORMAT_7_BIT_ADDRESS(SlaveAddress, test, I2C_WRITE);
    Addr = SlaveAddress.byte;
    TransmitOneByte(Addr);
    if(!I2CByteWasAcknowledged(I2C_BUS))
    {
        LATDbits.LATD1 = 1;
        delay(1000);
        LATDbits.LATD1 = 0;
        delay(10000);
        sprintf(maxpower, "0x%X NAck",test);
        print_error(maxpower);
    }
    else
    {
        LATGbits.LATG6 = 1;
        delay(1000);
        LATGbits.LATG6 = 0;
        delay(10000);
        sprintf(maxpower, "0x%X Ack",test);
        print_error(maxpower);
    }
        StopTransfer();
}

//Escribe en el Bus i2c
BOOL i2c_write(UINT8 address, UINT8 command[20], int size)
{
    int i=0;
    UINT8 Addr;
    char fritz[16];
    I2C_7_BIT_ADDRESS   SlaveAddress;

    I2C_FORMAT_7_BIT_ADDRESS(SlaveAddress, address, I2C_WRITE);
    Addr = SlaveAddress.byte; //Direccion Formateada
  
    StartTransfer(0);
    I2CSendByte(I2C_BUS, Addr); //Envía direccion
    sprintf(fritz,"Write to: 0x%02X", address);
    print_error(fritz);  
    DelayMs(1000);
  
    sprintf(fritz,"Size: %d Byte",strlen(command));
    print_error(fritz);
    DelayMs(500);

    if(!I2CByteWasAcknowledged(I2C_BUS))
    {
        print_error("NAck");
        DelayMs(5000);  
        return FALSE; //Direccion no válida
    }
    else
    {
    print_error("Ack");
    DelayMs(300);  

        for(i=0;i<=(size-1);i++)
        {
            sprintf(fritz,"Dato %d", (i+1));
            print_error(fritz);
            while(!I2CTransmitterIsReady(I2C_BUS));
            if(I2CSendByte(I2C_BUS, command[i]) == I2C_MASTER_BUS_COLLISION)
            {
                print_error("Bus Collision");  
                DelayMs(5000);
                return FALSE;  //master bus collision
            }
            while(!I2CTransmissionHasCompleted(I2C_BUS));

            sprintf(fritz,"Data: 0x%02X", command[i]);
            print_error(fritz);  
            DelayMs(500);
            //return TRUE;
        }
    }
    StopTransfer();
}


//Lee Datos de buffer si estan presentes
UINT8 i2c_read_buffer(BYTE address)
{
    UINT8 i2cbyte=0;
    char    fritz[16];

    StartTransfer(0);
    print_error("Read");
    DelayMs(100);  
while ( !I2CAcknowledgeHasCompleted(I2C_BUS) );
    if (I2CReceivedDataIsAvailable(I2C_BUS))
    {
        print_error("Data");
        DelayMs(10000);      
        I2CAcknowledgeByte(I2C_BUS, TRUE);
        i2cbyte = I2CGetByte(I2C_BUS);
        sprintf(fritz," %02X", i2cbyte);
        print_error(fritz);    
    }
    else
    {
        print_error("No Data");
        DelayMs(100);  
    }
    StopTransfer();
    return i2cbyte;
}



void print_status()
{
    I2C_STATUS  status;
    status = I2CGetStatus(I2C_BUS);
    char chip[16];
    sprintf(chip,"STATUS: %x",status);
    print_error(chip);
    DelayMs(500);
    //I2CClearStatus   (I2C_BUS, I2C_ARBITRATION_LOSS);
}

And here's the "main.c":
Code:
#include <plib.h>
//#include "comandos.h"
#include "delay3.c"
#include "uart1.c"
#include "lcd_16x2.c"

#define SYS_CLOCK         (80000000L)
#define PBCLK              (SYSCLK)
#pragma config FPBDIV        = DIV_1        // Peripheral Clock divisor
#define Fsck                50000
#define BRG_VAL             ((PBCLK/2/Fsck)-2)
#define GetSystemClock()           (SYS_CLOCK)
#define GetPeripheralClock()       (SYS_CLOCK)    // FPBDIV = DIV_1
#define GetInstructionClock()       (SYS_CLOCK)
#define I2C_CLOCK_FREQ        100000

#pragma config FNOSC = PRIPLL
#pragma config FSOSCEN = ON
#pragma config IESO = ON
#pragma config POSCMOD = HS
#pragma config OSCIOFNC = ON
#pragma config FCKSM = CSDCMD
#pragma config WDTPS = PS1048576
#pragma config FWDTEN = OFF     //Watchdog

// I2C Constants
#define I2C_BUS                      I2C1
#define LCD_ADDRESS              0x27
#define OW_ADDRESS              0x18


#ifndef OVERRIDE_CONFIG_BITS
#pragma config FPLLMUL        = MUL_24        // PLL Multiplier
#pragma config FPLLIDIV        = DIV_2        // PLL Input Divider
#pragma config FPLLODIV        = DIV_1        // PLL Output Divider 
#endif // OVERRIDE_CONFIG_BITS

#include "funciones_i2c.c"
#include "OW_I2C.C"



void main(void)
{
    UINT8 quark=0;
    UINT32        actualClock;
    UINT8        send[8];

    char            fritz[16];
    char            text[16];

    I2C_7_BIT_ADDRESS   SlaveAddress;

    //LEDS
    TRISDbits.TRISD1 = 0;
    TRISGbits.TRISG6 = 0;
    LATDbits.LATD1 = 0;
    LATGbits.LATG6 = 0;

    //BUS I2C Como entrada
    TRISDbits.TRISD9 = 1;    //SDA
    TRISDbits.TRISD10 = 1;    //SCL

    uart1_init();
    initI2C();
    I2CEnable(I2C_BUS, TRUE);
         

    config_status();
}

Looking some examples on the internet i found this function: "I2CReceiverEnable(I2C_BUS, TRUE)", which I was not using... still no luck on reading the bus (I2C Status: 408).

EDIT: forgot to mention that on the read functon I have an "address" input variable, but it is not used in the function.
 
I don't like the look of the read function... You haven't set the address .

Write:-
open
send device address
send internal address
write data until done
send nack
close

Read:-
open
send device address
send internal address
re open
send device address + 1
read bytes -- ack till done
send nack
close

I prefer simple routines.. These are clustered and become hard to understand..
here are my EEprom routines

C:
//Write a byte to the EEPROM
char WriteByte(int Address, char Byte)
   {
   SendID(0b10100000);
   if(SendByte(Address>>8)==0)
     return(0);
   if(SendByte(Address&0xff)==0)
     return(0);
   if(SendByte(Byte)==0)
     return(0);
   SendStop();
   return(1);
   }

//Read a byte from the EEPROM
char ReadByte(int Address)
   {
   char Byte;
   SendID(0b10100000);
   if(SendByte(Address>>8)==0)
     return(0);
   if(SendByte(Address&0xff)==0)
     return(0);
   SendRestart();
   if(SendByte(0b10100001)==0)
     return(0);
   Byte=ReceiveByte();
   SendNack();
   SendStop();
   return(Byte);
   }

Hard coded addresses....
 
I've only done this on mid range devices, but I couldnt get microchips code to work either.
I pinched the code from a project on the net and used it ever since as an include.
 
Hi Ian, thanks for your reply. I know my code is not the prettiest, too much libraries and stuff.
I didn't put the address in the read function cause it was done before in the sequence I was using:
3684Fig04.gif

I was just testing if i could get some response from the OW IC. I know it wasnt' exactly a tidy way.
Now I did a library with the basic functions and I'm sending commands step by step.

Anyway, I found this function: "I2CReceiverEnable(I2C_BUS, TRUE)".
I used it before reading and now I'm able (with some wired status errors after) to read "data" from the bus (using the read sequence you posted, but adding the I2CRecieverEnable() function.
The problem is I don't know if what I'm reading is actual recived data from the DSxxxx or just garbage. I initialized the data with some random number, and it changes after the read function, still I dont know if this is valid data.

Do you know something about this "I2CReceiverEnable()" function? I noticed you don't use it in your code.
And do you know anything about this wired status errors? I get stuff like "a"... I checked an i2c.h file that came with plib where it shows the status errors:
Code:
typedef enum
{
    // Transmit buffer full.  Set if the transmit buffer is full (unable to
    // accept more data to transmit.
    I2C_TRANSMITTER_FULL
        /*DOM-IGNORE-BEGIN*/ = 0x00000001 /*DOM-IGNORE-END*/,

    // Received data available.  Set if data is available in the receiver
    // buffer.  Cleared if not.  (Valid for both master and slave transfers.)
    I2C_DATA_AVAILABLE
        /*DOM-IGNORE-BEGIN*/= 0x00000002 /*DOM-IGNORE-END*/,

    // Slave read.  Set if the current (or most recent) slave operation
    // was a read.  Cleared if it was a write.  (Not valid during master
    // operations).
    I2C_SLAVE_READ
        /*DOM-IGNORE-BEGIN*/ = 0x00000004 /*DOM-IGNORE-END*/,

    // Start condition detected.
    I2C_START
        /*DOM-IGNORE-BEGIN*/ = 0x00000008 /*DOM-IGNORE-END*/,

    // Stop  condition detected.
    I2C_STOP
        /*DOM-IGNORE-BEGIN*/ = 0x00000010 /*DOM-IGNORE-END*/,

    // Slave data byte (sent or received).  If cleared, the most recently
    // sent or received data byte was an address byte.
    I2C_SLAVE_DATA
        /*DOM-IGNORE-BEGIN*/ = 0x00000020 /*DOM-IGNORE-END*/,

    // Receiver overflow error.  Data was received while the receiver buffer
    // was full.  The incoming data was lost.
    I2C_RECEIVER_OVERFLOW
        /*DOM-IGNORE-BEGIN*/ = 0x00000040 /*DOM-IGNORE-END*/,

    // Transmitter overflow error.  The software attempted to write new data
    // to the transmitter buffer and the write was ignored.
    I2C_TRANSMITTER_OVERFLOW
        /*DOM-IGNORE-BEGIN*/ = 0x00000080 /*DOM-IGNORE-END*/,

    // A 10-bit slave address matching the current slave address and mask
    // settings has been received.
    I2C_10BIT_ADDRESS
        /*DOM-IGNORE-BEGIN*/ = 0x00000100 /*DOM-IGNORE-END*/,

    // The General Call address has been received.
    I2C_GENERAL_CALL
        /*DOM-IGNORE-BEGIN*/ = 0x00000200 /*DOM-IGNORE-END*/,

    // A master transmitter has lost arbitration and transmission has been
    // aborted.
    I2C_ARBITRATION_LOSS
        /*DOM-IGNORE-BEGIN*/ = 0x00000400 /*DOM-IGNORE-END*/,

    // The module is currently transmitting data.
    I2C_TRANSMITTER_BUSY
        /*DOM-IGNORE-BEGIN*/ = 0x00004000 /*DOM-IGNORE-END*/,

    // The most recently transmitted byte was acknowledged by the receiver.
    I2C_BYTE_ACKNOWLEDGED
        /*DOM-IGNORE-BEGIN*/ = 0x00008000 /*DOM-IGNORE-END*/

} I2C_STATUS;

As you can see, there isn't any "a" code... wired.

And i've also noticed (by using a function for printing the status) I get in certain conditions (which is pretty often) an I2C_ARBITRATION_LOSS condition. When this happens there's nothing I can do (not even reprogramming) to get rid of the error but disconnecting and reconnecting the power.
 
Can you pinpoint specifically when your bus collision is happening? Microchip details the causes of bus collisions pretty thoroughly in the family reference manual for the I2C module.

Many common collisions are a result of checking the wrong status bits after initiating start, restart, and stop conditions since they all have their own bits. Another common source of collision is starting to write a 2nd byte of data before the first sequence is done. If you check for an ACK and then immediately start transmitting a second byte, you will collide with the 9th-bit's time period since it hasn't concluded yet. I don't know how your libraries handle everything but the lower level way to ensure your bus is clear is to first check I2C1STATbits.ACKSTAT for the reception of the slave's ACK and then follow it up with checking I2C1STATbits.TRSTAT to see when the master has completed its transmission and the bus is free.
 
Status
Not open for further replies.

New Articles From Microcontroller Tips

Back
Top