I'm having trouble reading the I2C data from the CPS8200, that has a 32 bit Register Address. Since I'm not too familiar with I2C register addresses more than 8 bits, and right now I'm not sure if I'm doing it correctly. My main goal is to be able to read and write from Register Address 0x2000 003C, and I don't believe I am doing this properly since when I read the data in the Serial Monitor, what I'm getting is 0xC4, when I should be getting 0x0000 0AFF. I wanted to post here to understand what I'm doing wrong, and how to properly read from the 32 bit register address.
#include <Wire.h>
uint32_t FUNC_EN_REG = 0x2000003C;
uint32_t FUNC_EN_REG_DATA;
void setup() {
Wire.begin(4, 5);
Serial.begin(115200);
Wire.beginTransmission(0x30);
Wire.write(0x20);
Wire.write(0x00);
Wire.write(0x00);
Wire.write(0x3C);
Wire.endTransmission();
}
void loop() {
Wire.requestFrom(0x30, 4);
FUNC_EN_REG_DATA = Wire.read();
Serial.println(FUNC_EN_REG_DATA, HEX);
delay(500);
}
-
2This is not a part of I2C spec. Read the datasheet for your IC (the-one-you-don-name-for-some-obscure-reason). It could be 20-00-00-3c or 3c-00-00-20, who knows?Matt– Matt2025年05月28日 13:52:16 +00:00Commented May 28 at 13:52
-
To hammer home Matt's point: please link documentation or tell us what device this is! There is no universal answer for the question you are askingInBedded16– InBedded162025年05月28日 17:42:38 +00:00Commented May 28 at 17:42
-
@InBedded16 Noted, the post has been updatedJ. Street– J. Street2025年05月28日 18:23:11 +00:00Commented May 28 at 18:23
2 Answers 2
You are almost there. The line where you have Wire.endTransmission();
should actually be
Wire.endTransmission(false);
As described in the documentation :
The
endTransmission()
method accepts a boolean argument changing its behavior for compatibility with certain I2C devices. If true,endTransmission()
sends a stop message after transmission, releasing the I2C bus. If false,endTransmission()
sends a restart message after transmission. The bus will not be released, which prevents another controller device from transmitting between messages. This allows one controller device to send multiple transmissions while in control. The default value is true.
The endTransmission(false)
is crucial because it allows you to switch from a write operation (sending the register address) to a read operation (getting the data) without releasing the bus.
When you are reading your response from the external device, you do not allow it to return all of the 4 bytes in the response before you ask for the response again. Wire.read()
should be run 4 times to read the 4 bytes of the response.
Your loop function should look something like this:
void loop() {
Wire.requestFrom(0x30, 4); // request 4 bytes from peripheral device #0x30
char a = Wire.read(); // receive a byte as character
Serial.print(a, HEX); // print the character in hex
char b = Wire.read(); // receive a byte as character
Serial.print(b, HEX); // print the character in hex
char c = Wire.read(); // receive a byte as character
Serial.print(c, HEX); // print the character in hex
char d = Wire.read(); // receive a byte as character
Serial.print(d, HEX); // print the character in hex
Serial.println("");
delay(500);
}
Or like this using a while loop:
void loop() {
Wire.requestFrom(0x30, 4); // request 4 bytes from peripheral device #0x30
while (Wire.available()) { // peripheral may send less than requested
char c = Wire.read(); // receive a byte as character
Serial.print(c, HEX); // print the character in hex
}
Serial.println("");
delay(500);
}
Side note:
If you want to make youre code a little more fancy and a little more robust, test the result of the endTransmission()
like so:
byte endTransmissionStatus = Wire.endTransmission(false);
if (endTransmissionStatus != 0) {
Serial.print("Error sending register address (Status: ");
Serial.print(endTransmissionStatus);
Serial.println(")");
return 0; // Return 0 or handle the error appropriately
}
The meaning of the status that is returned can be found on the documentation page link mentioned above.
-
1What's the difference in having endTransmission(false) and just not calling endTransmission?J. Street– J. Street2025年05月28日 16:16:03 +00:00Commented May 28 at 16:16
-
1@sa_leinad this isn't actually always true. Some I2C slave devices have separate read and write commands, requiring you to release the bus using endTransmission(), and then a read command will fetch the previously set register. The correct way for this device will be in the documentation, so OP will either have to find it themselves or tell us what device this is.InBedded16– InBedded162025年05月28日 17:35:27 +00:00Commented May 28 at 17:35
-
1@J.Street endTransmission(false) sends a restart message, not calling it at all will not. It is necessary to send a stop or a restart message to the slave so that it knows the current read/write command is over. Read the documentation to better understand that nuance.InBedded16– InBedded162025年05月28日 17:39:28 +00:00Commented May 28 at 17:39
-
The part about the loop for Wire.read() is important, if you edit the answer to reflect my first comment then I will +1 :)InBedded16– InBedded162025年05月28日 17:43:02 +00:00Commented May 28 at 17:43
-
@InBedded16
Some I2C slave devices have separate read and write commands, requiring you to release the bus using endTransmission(), and then a read command will fetch the previously set register.
Actually, all i2c slaves require that and all i2c slaves must keep current register number (with maybe autoincrement) until powered off. I2c start-without-stop is good and recommended practice but it is not critical, unless one runs multi-master i2c bus config.Matt– Matt2025年05月29日 03:46:19 +00:00Commented May 29 at 3:46
The answer to your question lies in Fig8 of the document you linked.
Fig of the CPS820 datasheet This is the order of I2C commands to run. First, AW:30 which is a beginTransmission(0x30), starting a write transmission. Then, DW:FF DW:FF DW:FF DW:00, the 4 bytes of the register address using MSBFirst. This part you already were doing correctly. You need to have an endTransmission() because:
This function ends a transmission to a peripheral device that was begun by beginTransmission() and transmits the bytes that were queued by write().
Then without releasing the bus lines it launches right into AR:30 which is the beginning of a read transmission. Therefore your endTransmission() function should be passed a false argument, so that you don't release the bus in between.
Next is DR:0E DR:00 DR:00 DR:00, which you receive using wire.requestFrom(0x30, 4) and then wire.Read() 4 times, one for each byte. Make sure to store each byte or use a different variable for each read, don't overwrite them with the next read. Notice that the correct value for this register should be 0x0000000E. The data is transmitted using LSBFirst notation. Incidentally, if you want to write data to a register (refer to Fig 7 or 9), you should also transmit the data in LSBFirst, but the register addresses are always done using MSBFirst.
Then just end with an endTransmission()!
Also, it's a little unclear whether you have to follow Steps1-3 (Fig 7-9) in order to read registers or just to write to them, but if you still aren't having success then that might be your issue.