- I have a PIC16F18875, on a board where it is configured as I2C master.
- 7 bit addressing, 100 kHz clock
- Several different I2C slave devices are on the bus.
- am using MPLAB X IDE with MCC plugin
- using the MCC-generated code for interacting with the peripherals
- I can read most slave ICs on the board, like some ADCs, and am getting sensible values back
- the UCD9090 PMIC, though, is only giving me 0xFF back, no matter which register is attempted to be read
- oscilloscope shows good signal integrity
- with a RaspberryPi's I²C attached to the same bus, I can read the 9090 just fine
- the logic analyzer shows that the slave is transmitting the value to be read back, a nack and the last stop with 36ms (milli) delay after the address-read (see image below)
- it is shown that the library code used generates a START+STOP after the address and register# bytes have been sent
- using i2c_masterOperation(), i2c_setBuffer() etc
- whereas the RaspberryPi logic analyzer readout shows it does the REPEATED START
- according to this, some slave devices need a REPEATED START, instead of a STOP+START, to be readable
- hence it looks to me that that's the problem
Now, from the MCC generated I2C interface header file, I see no way of telling the code to use the method of repeated-start instead of stop-start. Haven't seen anything configurable on the sparsely populated I2C page in MCC, either.
So, before I go and muck about within autp-generated code…
Is there a way to "properly" do that?
Otherwise, I guess I'll have to dig through their library code and find how to modify it to do what's necessary.
Update:
What I was trying, having been made aware of the state machine altering nature of the setCallbackX functions by brhans, is this:
i2c_error_t i2c_readRegRepeatStart(uint8_t reg, void* data, uint8_t size)
{
i2c_error_t ret;
__i2c_buf.reg = reg;
i2c_setBuffer(&__i2c_buf.reg, 1);
i2c_setDataCompleteCallback( i2c_restartRead, 0 );
ret = i2c_masterOperation(false); // writing the start,addr,reg,restart part
i2c_setDataCompleteCallback( i2c_returnStop, 0 );
if (ret == I2C_NOERR)
{
i2c_setBuffer(data, size);
ret = i2c_masterOperation(true); // reading the data bytes
}
return (ret);
}
I call this like: i2c_readRegRepeatStart( registerNum, &someUshortVar, 2 ).
This code does produce a sequence on the logic analyzer screen that starts out good:
- 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
So far, so good, right? Well, almost. Instead of now a STOP, this is what follows:
- R, Data, Ack (19 times)
- R, Data, Nack (once)
- Stop
- Start
- R, Addr, Ack
- R, Data, Ack
- R, Data, Ack
- Stop
I haven't figured out yet what's going on. It seems in principle what's supposed to be done, and I traced down to where the buffer pointer and size are set within the MCC library, and it clearly has the value of 2 (size), then the driver is engaged to perform the I2C operation, and it does that weird stuff.
Best Answer
This helped. Gave up on using the MCC stuff for this, other than the clicked-together basic peripheral init stuff. (Note that in the microchip official forum it was said the MCC I2C library is currently changing and not tested a lot) I basically just added a further parameter to indicate whether I want to use stop,start; or restart (aka repeated start condition), making it so that for "restart" the write-block routine omits the STOP at the end, and the read-block routine calls restart() instead of start(). Also note that that code expects left-aligned slave addresses (Lshift 1).
http://picforum.ric323.com/viewtopic.php?f=76&t=755#
Here an excerpt of the most relevant stuff, incl. those of my changes I mentioned - you might have to change things to qualify the correct register structs before the register names in this code, if you also use the MCC generated init code, despite then not using MCC for the actual business. If you use the read-block routine and pass repeatStart=true, it will do repeated-start condition intead of stop,start. Otherwise, it will do stop,start. With this, I was able to read that device which would refuse to be read before, so the question is answered - just without using the MCC I2C state machine, instead rather bypassing it with direct register access.