I2C Question
Greg LaPolla
Posts: 323
in Propeller 2
I am working on a 30105 driver(Spec Sheet). Looking at the Arduino driver as a starting point. I have used Johnny Macs jm_i2c driver. However, the Arduino has hardware i2c and I am stuck on the following code:
uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity, uint32_t iaddress, uint8_t isize, uint8_t sendStop) { if (isize > 0) { // send internal address; this mode allows sending a repeated start to access // some devices' internal registers. This function is executed by the hardware // TWI module on other processors (for example Due's TWI_IADR and TWI_MMR registers) beginTransmission(address); // the maximum size of internal address is 3 bytes if (isize > 3){ isize = 3; } // write internal register address - most significant byte first while (isize-- > 0) write((uint8_t)(iaddress >> (isize*8))); endTransmission(false); } // clamp to buffer length if(quantity > BUFFER_LENGTH){ quantity = BUFFER_LENGTH; } // perform blocking read into buffer uint8_t read = twi_readFrom(address, rxBuffer, quantity, sendStop); // set rx buffer iterator vars rxBufferIndex = 0; rxBufferLength = read; return read; }
I may be overthinking this, but I don't see an easy way to implement this.
Here is the calling code that uses that method. It is mostly ported to spin2 but unproven and untested at this point
PUB check(): numberOfSamples | byte readPointer, byte writePointer, bytesLeftToRead, toGet, byte temp[32], tempLong ' Read register FIDO_DATA in (3-byte * number of active LED) chunks ' Until FIFO_RD_PTR = FIFO_WR_PTR readPointer := getReadPointer() writePointer := getWritePointer() numberOfSamples := 0 debug(udec(readPointer)) debug(udec(writePointer)) ' Do we have new data? if (readPointer <> writePointer) ' Calculate the number of readings we need to get from sensor numberOfSamples := writePointer - readPointer if (numberOfSamples < 0) numberOfSamples += 32 ' Wrap condition ' We now have the number of readings, now calc bytes to read ' For this example we are just doing Red and IR (3 bytes each) bytesLeftToRead := numberOfSamples * activeLEDs * 3 ' Get ready to read a burst of data from the FIFO register i2c.start() i2c.write(MAX30105_ADDRESS) i2c.write(FIFODATA) i2c.stop() ' We may need to read as many as 288 bytes so we read in blocks no larger than I2C_BUFFER_LENGTH ' I2C_BUFFER_LENGTH changes based on the platform. 64 bytes for SAMD21, 32 bytes for Uno. ' Wire.requestFrom() is limited to BUFFER_LENGTH which is 32 on the Uno debug(udec(bytesLeftToRead)) repeat while (bytesLeftToRead > 0) toGet := bytesLeftToRead if (toGet > I2C_BUFFER_LENGTH) ' If toGet is 32 this is bad because we read 6 bytes (Red+IR * 3 = 6) at a time ' 32 % 6 = 2 left over. We don't want to request 32 bytes, we want to request 30. ' 32 % 9 (Red+IR+GREEN) = 5 left over. We want to request 27. toGet := I2C_BUFFER_LENGTH - (I2C_BUFFER_LENGTH +// (activeLEDs * 3)) ' Trim toGet to be a multiple of the samples we need to read bytesLeftToRead -= toGet ' Request toGet number of bytes from sensor **_i2cPort->requestFrom(MAX30105_ADDRESS, toGet)** repeat while (toGet > 0) head++ ' Advance the head of the storage struct head +//= STORAGE_SIZE ' Wrap condition ' Burst read three bytes - RED temp[3] := 0 temp[2] := i2c.read(i2c.ack) temp[1] := i2c.read(i2c.ack) temp[0] := i2c.read(i2c.nak) waitms(1000) ' Convert array to long longmove(@tempLong, @temp, 32) tempLong &= $3FFFF ' Zero out all but 18 bits red[head] := tempLong ' Store this reading into the sense array if (activeLEDs > 1) '/Burst read three more bytes - IR temp[3] := 0 temp[2] := i2c.read(i2c.ack) temp[1] := i2c.read(i2c.ack) temp[0] := i2c.read(i2c.nak) ' Convert array to long longmove(@tempLong, @temp, 32) tempLong &= $3FFFF ' Zero out all but 18 bits IR[head] := tempLong if (activeLEDs > 2) ' Burst read three more bytes - Green temp[3] := 0 temp[2] := i2c.read(i2c.ack) temp[1] := i2c.read(i2c.ack) temp[0] := i2c.read(i2c.nak) ' Convert array to long longmove (@tempLong, @temp, 32) tempLong &= $3FFFF ' Zero out all but 18 bits green[head] := tempLong toGet -= activeLEDs * 3 return numberOfSamples ' Let the world know how much new data we found
Comments
The Two Wire Arduino code uses begin Transmission and end Transmission to signal the Start and End I2C codes.
The function looks like a normal read bytes from the unit and place it in a buffer and then return the number of bytes actually read.
Mike
Ignore Arduino examples (I have found bugs in more than one) and go right to the docs. The best guidance comes from the transaction diagrams that nearly every I2C spec doc includes. Figures 10 and 11 show the sequence of reading one byte or reading a bunch of bytes. Here's how those translate to Spin using my library.
Finally... there is no H in my name!
A few minutes later...
I found a simple library for the MAX30102 pulse/ox sensor that seems to have similar characteristics to the 30105; maybe some of this code will help. I wrote it for a friend and he got things working, but I never tested it.
But the H comes so naturally! Thanks for the input! I know I2C is pretty simple, but I always tend to way overthink things!
Had to put this aside for a bit, but back on it now. I have used some of the code that Jon provided.
The I2C communications fails pretty consistently around the same spot. I can't seem to figure out why. I have attached the code and snippet of the debug output.
I had an problem with the I2C code above when using a Nunchuck controller...
The fix for me was to add in a delay in i2c_Read function.
The code has since been converted to C, but I think you can see it's very similar to the Spin2 version except for the extra delays.
Maybe adding these delays would help you too?
@Rayman Just tried it. No go. I think something else is happening here
I have made huge progress on this hopefully very close to functional. I am seeing some weirdness in the debug output. What does the exclamation point signify ? And second why is cog 1 showing up when it should be cog 0 ?
For things like this, I highly recommend a cheap logic analyzer. If you search Amazon or eBay for a 24MHz usb logic analyzer, expecting to pay ~10usd, you'll find lots of saleae clones that work perfectly with PulseView from the sigrok project. Captures in PulseView are exceptionally helpful to debugging i2c issues.
I can second that -- I use this little dude all the time with PulseView.
-- https://www.amazon.com/gp/product/B077LSG5P2
It's the best $13 I've ever spent, and helped me solve a problem with my I2C object a while back. After a forums discussion with Peter Jakacki and others, we all agreed that supporting clock stretching in our I2C code was no longer necessary. Then, a friend asked me to create an object for this device:
-- https://www.sparkfun.com/products/15083
I couldn't get it to work with the P2. I connected it to a Schmarschmino board and it did work (though I did find a bug in one of their driver functions). Since it worked, I connected the $13 logic analyzer, and there it was, big as day: clock stretching. You can see it in the attached image capture from PulseView. Note that at about the 460us mark, the clock line stays low for a long time -- this is the device telling the master it's busy. Once the clock line is released, the master is free to read the response. I added clock stretch support back into my driver and get the code working for the Qwiic Twist module.
I've also added my P2 I2C driver that has clock stretching in case it's helpful to anyone.
I do have one. I use it all the time The problem now is trying to determine where the logic is failing. i guess I should have started a new thread for this one. I still do have some issues with the I2C but at this point I think it is more logic. I am stumped with the output from debug as it is not correct.
This is definitely a case where I'd be using the logic analyzer. Add stacked decoders, and it should help visualize what registers are being written and read.
Again, I2C is mostly working now. Dumping some variables because the logic is wrong. However debug output is faulty as seen in the attached image. The ! and output coming from cog1. Not sure what would cause that.
Hi, JonnyMac and anybody:
Do you have a C code example for I2C slave? The master side is OK but the receiving side need to study more. The slave in my project is just simply received the data and display it.
Thanks.
I don't use C to program the Propeller, and I've never needed an I2C slave object.
I did find the attached file (Spin1/PASM) on my computer, but I haven't used it.
That can be used verbatim in FlexC. eg:
It still of course needs ported from prop1 to prop2.