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.