I'm a bit confused by i2c_write_blocking()
and how to specify the device_address
, register_address
and data
to prime a device for reading and then to read data from another 2nd_register_address
in the device? I have looked at i2c_write_blocking_internal()
in the source i2c.c and I get that it is looping over the bytes in src
, to process the register address as a first byte and the transmission will complete or an abort will be triggered.
What then do we do to read data from a different 2nd_register_address
that was primed for reading by writing to the first register address? Do we do another single-byte write with the 2nd_register_address
as src
and then do an i2c_read_blocking()
to read the data from the device register address that was setup by the first i2c_write_blocking()
call?
Here is the scenario. Initializing the MPU9250 magnetometer (AK8963) with full setup of all clocks and registers, you must put the chip in FUSE mode by writing data to the AK8963_CNTL1
control register. The makes a read from the FUSE ROM possible. Then you need to read the axis sensitivity adjustments from the FUSE ROM starting at register address AK8963_ASAX
. I just did a write to put it in FUSE mode, now do I do another write sending the AK8963_ASAX
register address before I read the axis sensitivity adjustments?
I think this is right, but I'm sure not certain of it. With other SDK I've used, for the read/write functionality you would just load the device_address as a slave and then load the register address and then kick-off the read or write. You had ample control over the device and register address combination both writing and reading.
Here I get the SDK is shortening that process (which is good), by sending the register address as the first byte in src
parameter with the write and for normal reads that's fine, but here where you have to send a write to put an I2C device in a particular mode -- then follow-up to read from a different address is where the confusions is.
I have no way with the read to say, just read from AK8963_ASAX
in the call to i2c_read_blocking()
(unless I can send the register address as the first byte in the dst parameter) -- but I haven't found any documentation that says I can. hardware_i2c just says dst
is limited to "Pointer to buffer to receive data". And its internal source doesn't separate an address from the dst
argument.
Below are a couple of wrappers I added to help with i2c_read_blocking()
and i2c_write_blocking()
given that only the default i2c_default
is used along with my best cut at what I need to do to put the chip in FUSE mode and then read from the AK8963_ASAX
register:
static const uint8_t mag_addr = AK8963_DEFAULT_ADDRESS;
...
/** i2c helper function for read and write with default values */
int i2c_read (uint8_t addr, const uint8_t *dst, size_t len)
{
/* i2c_default, addr, and nostop = false (master releases bus control) */
return i2c_read_blocking (i2c_default, addr, dst, len, false);
}
int i2c_write (uint8_t addr, const uint8_t *src, size_t len)
{
/* i2c_default, addr, and nostop = true (master retains bus control) */
return i2c_write_blocking (i2c_default, addr, src, len, true);
}
...
/**
* Initialize the AK8963 magnetometer CNTL1 by placing the magnetometer in FUSE mode
* to read the Axis Sensitivity Adjustment (ASA) values from FUSE_ROM, power-down
* the chip between mode changes and then place the magnetometer in 16-bit adc and
* continual measurement 2 for normal operations. Save the computed adjustments
* factors in asax, asay, asaz. (Page 51 & 52 of Register_Map)
*/
#ifdef MPU9250
void ak8963_initialize (void)
{
uint8_t cntl1_mode = AK8963_CNTL1_OUT_BITS | AK8963_CNTL1_CONT_MSRMT2,
fuse_mode = AK8963_CNTL1_OUT_BITS | AK8963_CNTL1_FUSE_ROM,
reset = 0x00,
regdata[2] = {0};
/* set AK8963 in FUSE mode */
regdata[0] = AK8963_CNTL1;
regdata[1] = fuse_mode;
i2c_write (mag_addr, regdata, sizeof regdata);
sleep_ms (1);
/* Do I do this 2nd write of the 1-byte address to prepare for the
* axis sensitivity read?
*/
regdata[0] = AK8963_ASAX;
i2c_write (mag_addr, regdata, sizeof *regdata);
sleep_ms (1);
/* read axis sensitivity adjustments */
i2c_read (mag_addr, AK8963_ASAX, buffer, 3, false);
sleep_ms (1);
...
Is this how you handle a prepare mode and read from different register_address scenario? Or, did I miss something basic with the dst
parameter along the way that is now screwing me up? All thoughts appreciated.