PIC16F18875 I2C - Repeated-START condition reception problem

PIC16F18875 I2C - Repeated-START condition reception problem

Postby picn00b » Tue Feb 11, 2020 11:59 am

Hey,

1st post here, not sure whether this is the correct subforum to post, as I am using peripheral handling code generated by MPLAB X / MCC plugin. (except for the "alternate" solution further below)

I at first wanted to post at the official Microchip forum, which seems to be quite a battlefield. Registration took short of a week, and now post attempts result in "access denied", apparently due to overzealous spambot defence.
It was said that this forum here "just works", so let's see about that. Registration mail after 5 mins is already a lot better than 6 days ;)

----

I have a board with an I2C bus, several different slave ICs connected, the PIC acting as master.
I can read most of the I2C slaves, but one, UCD9090, only ever replied with 0xFF on any register number.
So I tried with a RaspberryPi, attached to the bus, to read it, et voila, that just worked.
(the signal shape looks all good on a scope for all of the scenarios in here, btw).
The logic analyzer revealed that the RasPi sends a repeated-start condition, whereas the MCC library, by default, produces a STOP, START after the address + register bytes.
So I am trying to replicate that.

I saw that you can call a library function of i2c_master.c called i2c_setDataCompleteCallback( callback, param ), where one couuld set a predefined function of i2c_types.c, e.g. i2c_returnStop (default), or i2c_restartRead - which seemed to be what I am looking for.
So I made this function to read a register from a device with repeated-start:

Code: Select all
i2c_error_t i2c_readRegRepeatStart(uint8_t reg, void* data, uint8_t size)
{
    i2c_error_t ret;   
    m_reg = reg;
    i2c_setBuffer(&m_reg, 1);
    i2c_setDataCompleteCallback( i2c_restartRead, 0 ); // changing default behavior to use repeated-start, not a stop
    ret = i2c_masterOperation(false); // write address and register
    i2c_setDataCompleteCallback( i2c_returnStop, 0 ); // change back to default, to create a STOP at the very end - or so I thought
    if (ret == I2C_NOERR)
    {
        i2c_setBuffer(data, size);
        ret = i2c_masterOperation(true); // read the data bytes
    }
    return (ret);
}


On the logic analyzer, I saw the result of that starting well, but then going haywire. Here are the I2C events that the analyzer decodes:

Start
W, Addr, Ack
W, Data, Ack
Repeat Start -- good, that's what I wanted to see, instead of stop/start
R, Addr, Ack
R, Data, Ack
R, Data, Ack

And it doesn't stop here, but rather continues with...

R, Data, Ack (19 times)
R, Data, Nack (once)
Stop
Start
R, Addr, Ack
R, Data, Ack
R, Data, Ack
Stop



So I am wondering:
Am I using the library wrong?
Or is it actually a bug?
When I stepped through this with the debugger, I could not quite see why it's 20 extra bytes (maybe that was incidentally just what the logic anylyzer caught in one go), but I saw an underflow of a counter that would be decremented from 0 to 0xFFFF, here:
In i2c_master.c (line 360) in function static i2c_fsm_states_t do_I2C_RX(void), when it does if(--i2c_status.data_length), it decrements data_length from 0 fo 0xFFFF at one point in the run of the state machine, which I, as you saw above, modified to do the repeated-start - which it does.

Alternative route:
Since I didn't want to fiddle with the auto-generated library code, I looked for solutions elsewhere and found a post where someone claimed to provide the functionality on a lower level.
His code also only kinda-worked, and I, as well as some people in his comment thread, observed that after producing the exact output needed (an I2C event sequence on the logic analyzer that is identical to what RaspberryPi does and succeeds with), it hangs forever waiting for some flags to get cleared, which apparently never do.
I wonder if that has anything to do that I'm basically shooting past the MCC library (I only introduced that stuff to solve that one problem, for that one I2C slave on my bus, keeping the init stuff that the MCC lib does and the rest of it for the remaining ICs, not rewriting the whole thing).
I took the base code from here: https://electrosome.com/i2c-pic-microcontroller-mplab-xc8/#Repeated_Start
This is what I'm doing with his code snippets:

Code: Select all
void I2C_Master_Wait()
{   while ((SSP1STAT & 0x04) || (SSP1CON2 & 0x1F)); // At the very end, it hangs here forever, but works fine on calls before the last one from I2C_Master_Stop().
}
void I2C_Master_Start()
{   I2C_Master_Wait();
    SSP1CON2bits.SEN = 1;
}
void I2C_Master_RepeatedStart()
{   I2C_Master_Wait();
    SSP1CON2bits.RSEN = 1;
}
void I2C_Master_Stop()
{   I2C_Master_Wait();
    SSP1CON2bits.PEN = 1;
}
void I2C_Master_Write(unsigned d)
{   I2C_Master_Wait();
    SSP1BUF = d;
}
unsigned short I2C_Master_Read(unsigned short a)
{ unsigned short temp;
  I2C_Master_Wait();
  SSP1CON2bits.RCEN = 1;
  I2C_Master_Wait();
  temp = SSP1BUF;
  I2C_Master_Wait();
  SSP1CON2bits.ACKDT = (a)?0:1;
  SSP1CON2bits.ACKEN = 1;
  return temp;
}

int I2C_ReadWithRepeatStart(uint8_t addr, uint8_t reg, uint8_t* outVal, uint8_t numBytes)
{
    if (numBytes==0 || numBytes>2)
        return -1;
   
    uint8_t addrSh = addr << 1;
    I2C_Master_Start(); //Start condition
    I2C_Master_Write( addrSh | 0x0 ); //7 bit address + Write
    I2C_Master_Write(reg); //Write data
   
    I2C_Master_RepeatedStart();
   
    I2C_Master_Write( addrSh | 0x1 ); //7 bit address + Read
    for (uint8_t i=0; i<numBytes; ++i)
        *(outVal++) = I2C_Master_Read(1);//0); //Read + Acknowledge
   
    I2C_Master_Stop();
}


At the end, after producing the correct I2C sequence (almost there, dammit!), it will hang in I2C_Master_Wait.
I would prefer consistency and use the library correctly. But if someone knows why the second way also fails (freezes)..., well I'd take anything that works for now.
picn00b
 
Posts: 3
Joined: Tue Feb 11, 2020 11:17 am

Re: PIC16F18875 I2C - Repeated-START condition reception pro

Postby ric » Wed Feb 12, 2020 2:42 am

There's a few problems with that code.
The write function does not read and return the ACK status (and the calling code should be checking the returned status, and reporting if no ACK).
The read function is not handling the ACK bit correctly. You must ACK all but the last byte received. You MUST NAK the last byte, or you probably will not be able to send the STOP condition correctly.
(The function can do it, but expects YOU to pass a flag indicating if this is the last byte. I see you tried always zero or always 1. Neither is correct)

I actually dislike this way of accessing I2C. It's much easier to debug if you wait for each transaction to complete, checking the appropriate status bit for that operation.
You can see how I do it at viewtopic.php?f=76&t=755
Latest test project, an LED matrix display made from one reel of addressable LEDs. here
User avatar
ric
Verified identity
 
Posts: 553
Joined: Sat May 24, 2014 2:35 pm
Location: Melbourne, Australia
PIC experience: Professional 5+ years with MCHP products

Re: PIC16F18875 I2C - Repeated-START condition reception pro

Postby picn00b » Wed Feb 12, 2020 10:40 am

"I see you tried always zero or always 1. Neither is correct"

Ah well I did figure it should be NAK at the end while examining the RasPi generated sequence, it's just not visible in this code snippet that I tried that - it didn't work either, but maybe different problem.

I'll have a look at your way of doing it as suggested, thanks!
picn00b
 
Posts: 3
Joined: Tue Feb 11, 2020 11:17 am

Re: PIC16F18875 I2C - Repeated-START condition reception pro

Postby picn00b » Wed Feb 12, 2020 3:01 pm

Thanks a ton man!
After adjusting the code a bit to use the register struct names I have here and added a flag param for "repeated start or stop/start?", that seems to work! At least I'm getting *something* back from the device, a different value depending on register num, whereas I was only getting 0xFF no matter what before.
picn00b
 
Posts: 3
Joined: Tue Feb 11, 2020 11:17 am


Return to SSP (IIC, SPI)

Who is online

Users browsing this forum: No registered users and 1 guest

cron