Author Topic: What bit to poll while waiting for I2C Transmit/ What am I doing wrong?  (Read 3874 times)

0 Members and 1 Guest are viewing this topic.

Offline BurnedResistorTopic starter

  • Regular Contributor
  • *
  • Posts: 192
  • Country: at
Dear All,

After getting a spi bus working with your help, I just could not leave it alone and managed to get stuck on trying to write my own peripheral handler for an I2C master.

Here is my code:

The function which transmits 4 12bit values, via 6 8bit transmitions:
Code: [Select]
BOOL motor_update(int mfl, int mfr, int mbl, int mbr) {
     char msg1 = ((0b111111110000 & mfl) >> 4);
     char msg2 = ((0b1111 & mfl) << 4) | ((0b1111000000000 & mfr) >> 8);
     char msg3 = ((0b11111111 & mfr));
     char msg4 = ((0b111111110000 & mbl) >> 4);
     char msg5 = ((0b1111 & mbl) << 4) | ((0b111100000000 & mbr) >> 8);
     char msg6 = (IIC1, ((0b11111111 & mbr)));
     
     
    IIC_start(IIC1);
    while (IIC_status(IIC1, IIC_STAT_START)) {
        ;
    }

    //Send Address, wait
    IIC_address(IIC1, IIC_ADR_INT, IIC_WRITE);
    while(IIC_status(IIC1, IIC_STAT_TRANSMIT_FULL) & IIC_status(IIC1, IIC_STAT_TRANSMIT)){;}
    if(IIC_status(IIC1,IIC_STAT_ACK)){
        terminate();
        return TRUE;
    }

    //Send ms1, wait
    IIC_put(IIC1, msg1);
   
    while(IIC_status(IIC1, IIC_STAT_TRANSMIT_FULL) & IIC_status(IIC1, IIC_STAT_TRANSMIT)){;}
    if(IIC_status(IIC1,IIC_STAT_ACK)){
        terminate();
        return TRUE;
    }

    //Send ms2,  wait
    IIC_put(IIC1, msg2);
    while(IIC_status(IIC1, IIC_STAT_TRANSMIT_FULL) & IIC_status(IIC1, IIC_STAT_TRANSMIT)){;}
    if(IIC_status(IIC1,IIC_STAT_ACK)){
        terminate();
        return TRUE;
    }

    //Send ms3, wait
    IIC_put(IIC1, msg3);
   
    while(IIC_status(IIC1, IIC_STAT_TRANSMIT_FULL) & IIC_status(IIC1, IIC_STAT_TRANSMIT)){;}
    if(IIC_status(IIC1,IIC_STAT_ACK)){
        terminate();
        return TRUE;
    }

    //Send ms4, wait
    IIC_put(IIC1, msg4);
    while(IIC_status(IIC1, IIC_STAT_TRANSMIT_FULL) & IIC_status(IIC1, IIC_STAT_TRANSMIT)){;}
    if(IIC_status(IIC1,IIC_STAT_ACK)){
        terminate();
        return TRUE;
    }

    //Send ms5, wait
    IIC_put(IIC1, msg5);
    while(IIC_status(IIC1, IIC_STAT_TRANSMIT_FULL) & IIC_status(IIC1, IIC_STAT_TRANSMIT)){;}
    if(IIC_status(IIC1,IIC_STAT_ACK)){
        terminate();
        return TRUE;
    }

    //Send ms6, wait
    IIC_put(IIC1, msg6);
    while(IIC_status(IIC1, IIC_STAT_TRANSMIT_FULL) & IIC_status(IIC1, IIC_STAT_TRANSMIT)){;}
    if(IIC_status(IIC1,IIC_STAT_ACK)){
        terminate();
        return TRUE;
    }
 
    //Stop
    IIC_stop(IIC1);
    while (IIC_status(IIC1, IIC_STAT_STOP)) {
        ;
    }
}

void terminate(){
        IIC_stop(IIC1);
        while(IIC_status(IIC1, IIC_STAT_STOP)){;}
}


Here is the code for my IIC methods, which I call in the above function:
Code: [Select]

  typedef enum {
        IIC_READ = 1,
        IIC_WRITE = 0
    } IIC_OPERATION;

    typedef enum {
        //A0000000 00000000 XXXXXXXX XXXXXXXX
        //A: 1 = CON register, 0 = Status register
        //X: Bit mask
        IIC_STAT_CLKSTRETCH =          0b10000000000000000000000001000000,
        IIC_STAT_RECEIVE =             0b10000000000000000000000000001000,
        IIC_STAT_START =               0b10000000000000000000000000000001,
        IIC_STAT_STOP =                0b10000000000000000000000000000100,
        IIC_STAT_RESTART =             0b10000000000000000000000000000010,
        IIC_STAT_ACK_SENT =            0b10000000000000000000000000010000,
        IIC_STAT_ACK =                 0b00000000000000001000000000000000,
        IIC_STAT_TRANSMIT =            0b00000000000000000100000000000000,
        IIC_STAT_MASTER_COLLISION =    0b00000000000000000000010000000000,
        IIC_STAT_GENERAL_CALL =        0b00000000000000000000001000000000,
        IIC_STAT_10BIT_MATCH =         0b00000000000000000000000100000000,
        IIC_STAT_WRITE_COLLOSION =     0b00000000000000000000000010000000,
        IIC_STAT_RECEIVE_OVERFLOW =    0b00000000000000000000000001000000,
        IIC_STAT_DATA_NADDRESS =       0b00000000000000000000000000100000,
        IIC_STAT_STOP_DET =            0b00000000000000000000000000010000,
        IIC_STAT_START_DET =           0b00000000000000000000000000001000,
        IIC_STAT_READ_NWRITE_DET =     0b00000000000000000000000000000100,
        IIC_STAT_RECEIVE_FULL =        0b00000000000000000000000000000010,
        IIC_STAT_TRANSMIT_FULL =       0b00000000000000000000000000000001,
    } IIC_STATUS;

void IIC_stop(IIC_CHANNEL channel) {
    if (channel == IIC2) {
       I2C2CONbits.PEN = 1;
    } else {
        //Default to Channel 1
        I2C1CONbits.PEN = 1;   
    }
}
void IIC_start(IIC_CHANNEL channel) {
    if (channel == IIC2) {
        I2C2CONbits.SEN = 1;
    } else {
        //Default to Channel 1
        I2C1CONbits.SEN = 1;
    }
}

BOOL IIC_status(IIC_CHANNEL channel, IIC_STATUS stat){
   if(channel == IIC2){
       if((stat & 0x80000000)){
           return ((I2C2CON) & (0x7FFFFFFF & stat));
       } else {
           return ((I2C2STAT) & (0x7FFFFFFF & stat));
       }
   } else{
       if((stat & 0x80000000)){
           return ((I2C1CON) & (0x7FFFFFFF & stat));
       } else {
           return ((I2C1STAT) & (0x7FFFFFFF & stat));
       }
   }
}
void IIC_address(IIC_CHANNEL channel, char address, IIC_OPERATION op){
    char value = ((address & 0b1111111) << 1) | op;
    IIC_put(channel, value);
}
void IIC_put(IIC_CHANNEL channel,char data) {
    if(channel == IIC2){
        I2C2TRN = data;
    } else {
        //default to channel 1
        I2C1TRN = data;                                     
    }
}


My issue is that, according to my scope, only the address + r/w bit is being sent, immediately followed by a Stop state:


 I am however quite certain that the function does not exit right after the IIC_address call, as the terminate method is never executed (checked by placing a stop point there) and the the method does not return TRUE, which would indicate an error.

My best guess is that I am polling the wrong bits to check for completion, which results in all the future writes to the I2C1BUF being ignore, and then the STOP bit at the end of the function executing.

In other words, I suspect an issue with:
Code: [Select]
while (IIC_status(IIC1, IIC_STAT_TRANSMIT_FULL)){ ...

which is simply a complicated way of writing:
Code: [Select]
while(I2C1STATbits.TBF){...

I am slightly confused what I should be polling, as the data sheet mentions booth the I2C1STATbits.TBF and I2C1CONbits.TRSTAT...
Any help would be greatly appreciated.

Side Note:
XC32 Complier
PIC32MX120F064H
« Last Edit: February 15, 2016, 05:26:35 am by BurnedResistor »
 

Offline BurnedResistorTopic starter

  • Regular Contributor
  • *
  • Posts: 192
  • Country: at
Re: What bit to poll while waiting for I2C Transmit/ What am I doing wrong?
« Reply #1 on: February 15, 2016, 05:36:22 am »
I just tried to change the line in question to:
Code: [Select]
while(I2C1STATbits.TBF && I2C1STATbits.TRSTAT){;}

and it transmits SOME of the following bytes, but skips others... Still makes me think I am polling the wrong bit..!
 

Offline BurnedResistorTopic starter

  • Regular Contributor
  • *
  • Posts: 192
  • Country: at
Re: What bit to poll while waiting for I2C Transmit/ What am I doing wrong?
« Reply #2 on: February 15, 2016, 06:16:12 am »
furthermore it seems that that change caused it to only send every other byte... Again confirming the wrong polling bit theory?
 

Offline dannyf

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: What bit to poll while waiting for I2C Transmit/ What am I doing wrong?
« Reply #3 on: February 15, 2016, 12:33:04 pm »
Never used your chip so cannot be of much help.

A few suggestions:

1) I would see if you could use a hardware debugger to step through the code; If not, see if you can send debugging messages (uart, or pin flip patterns, etc.) so you have some sense where the issues are.

2) i would check the datasheet / library you use to see which is the right status to check. Check the errata as well.

3) from the flow, looks like it exited right after the address is sent, through terminate().

4) the various channel selection things can be done via switch / case so it is more expandable.

5) some of the formation steps for msg1..6 are not necessary.

6) I typically organize my i2c routines into single byte send/ receive and multi-byte send / receive, like
Code: [Select]
  i2c_write(IIC2, addr, data_ptr); //write single byte
  i2c_writes(IIC1, addr, data_ptr, data_length); //write multiple bytes

Such a set-up allows a consistent approach should you decide in the future to standard transmission via an interrupt.
================================
https://dannyelectronics.wordpress.com/
 

Offline BurnedResistorTopic starter

  • Regular Contributor
  • *
  • Posts: 192
  • Country: at
Re: What bit to poll while waiting for I2C Transmit/ What am I doing wrong?
« Reply #4 on: February 15, 2016, 01:05:56 pm »


3) from the flow, looks like it exited right after the address is sent, through terminate().


Thats the frustrating part... If I put a breakpoint on terminate(), it never triggers, but there is still a stop bit being sent. That is also confirmed because if I place a breakpoint onto the stopbit part it stops there...!
 

Offline dannyf

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: What bit to poll while waiting for I2C Transmit/ What am I doing wrong?
« Reply #5 on: February 15, 2016, 01:21:21 pm »
In the while() loop right after sending the address byte, keep flipping a pin. and see what happens.

In the following if, before terminate(), keep flipping a (different) pin too.
================================
https://dannyelectronics.wordpress.com/
 

Offline mikerj

  • Super Contributor
  • ***
  • Posts: 3240
  • Country: gb
Re: What bit to poll while waiting for I2C Transmit/ What am I doing wrong?
« Reply #6 on: February 16, 2016, 12:03:45 am »
Here is my code:

The function which transmits 4 12bit values, via 6 8bit transmitions:
Code: [Select]
    //Send Address, wait
    IIC_address(IIC1, IIC_ADR_INT, IIC_WRITE);
    while(IIC_status(IIC1, IIC_STAT_TRANSMIT_FULL) & IIC_status(IIC1, IIC_STAT_TRANSMIT)){;}

The last line above, and probably several other similar lines may not do what you probably want them to do.  You have used a bitwise AND operator so you will only wait in this loop while both the TBF (buffer full) and the TRSTAT (transmit status) are set.  However TBF will clear after the last data bit is transmitted, but TRSTAT will only be cleared after the ACK bit has been received.  This means you jump out of the loop before the ACK is received, so your subsequent test of the ACK bit may not be (probably isn't) valid; you have a race condition.

It looks like you should only need to look at TRSTAT for this particular case, but it might be easier to simply poll the I2CxMIF interrupt flag instead since all the typical master operations set this flag.  Remember to clear I2CxMIF before starting a new operation.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf