[SOLVED] Writing to specific register of I2C device

How does one specify a particular register of an I2C device to write to?

I have an HMC6343 tilt-compensated magnetometer & a number of its features require me to write values to specific registers (eg to alter the variation angle you write to registers 0x0D & 0x0C). But how do I do this with the Wire library? The documentation for Wire doesn't mention anything about this.

I have an HMC6343 tilt-compensated magnetometer

23 posts and still no link.

a number of its features require me to write values to specific registers (eg to alter the variation angle you write to registers 0x0D & 0x0C).

You write what to these registers? What command causes it to know that the data is for a specific register?

The documentation for Wire doesn't mention anything about this.

Of course not, because WHAT to send depends entirely on what you are sending data to.

PaulS:
What command causes it to know that the data is for a specific register?

This was precisely my question. I've worked it out now, after re-reading the datasheet for the HMC6343 again. Turns out you send 0xF1 (which the HMC6343 interprets as the 'Write to EEPROM' command), followed by the address of the register you want to write, followed by the data you want to write to it.

I presume this is pretty standard fair for I2C devices & it would be nice if the Arduino.cc page on the Wire library had a bit more explanation - atm it doesn't really say anything that isn't obvious from the method names & I would be surprised if writing to specific registers of I2C devices wasn't a very common requirement for people using the Wire library.

1 Like

I would be surprised if writing to specific registers of I2C devices wasn't a very common requirement for people using the Wire library.

It might be, but so is the need to understand what Wire is doing, and what Wire is communicating with.

The Wire documentation could be expanded a thousand-fold, and still wouldn't cover half the devices that I2C can be used to communicate with.

PaulS:

I would be surprised if writing to specific registers of I2C devices wasn't a very common requirement for people using the Wire library.

The Wire documentation could be expanded a thousand-fold, and still wouldn't cover half the devices that I2C can be used to communicate with.

I wouldn't expect the Wire doc to cover specific devices, but I would expect it to at least cover some of the most common functionality of the I2C paradigm.

Anyway, in case Googlers come across this thread, here's some example code that sets some registers to do useful things;

#include <Wire.h>

#define HMC6343_ADDRESS 0x19

//====================================================================================  
void setup() {
  Wire.begin();
  Serial.begin(115200);
  
  /*
   * Set the 'variation angle correction' (magnetic declination), see p8 in datasheet.
   * At the cathedral in St Andrews on 07/08/2012 this was 3 degrees 16 seconds West
   * which is -3.2667 decimal degrees, or -33 tenths of a degree, so MSB/LSB in two's
   * complement is 11111111/11011111. This is written to EEPROM so technically doesn't
   * need to be done every time if the device isn't moving to a drastically new
   * location inbetween use.
   */
  byte deviationMSB = B11111111;
  Wire.beginTransmission(HMC6343_ADDRESS);
  Wire.write(0xF1);                          // 'Write to EEPROM' command
  Wire.write(0x0D);                          // EEPROM address of deviation angle MSB
  Wire.write(deviationMSB);
  Wire.endTransmission();
  
  byte deviationLSB = B11011111;
  Wire.beginTransmission(HMC6343_ADDRESS);
  Wire.write(0xF1);
  Wire.write(0x0C);                          // EEPROM address of deviation angle LSB
  Wire.write(deviationLSB);
  Wire.endTransmission();
  
  /*
   * Set the measurement rate to 10Hz (0x02) from default of 5Hz (0x01).
   * Again this is EEPROM so shouldn't need re-doing unless it is explicitly reset
   * at some point.
   */
  Wire.beginTransmission(HMC6343_ADDRESS);
  Wire.write(0xF1);
  Wire.write(0x05);                        // EEPROM address of Operational Mode Register 2
  Wire.write(0x02);                        // (OM2_1 = 1 && OM2_0 = 0) == 10Hz operation
  Wire.endTransmission();
   
  /*
   * Set the Heading Infinite Impulse Response (IIR) filter from its default of 0
   * to something a bit more than 0. Again, this is EEPROM so shouldn't need re-doing
   * unless it is explicitly reset at some point.
   */
  Wire.beginTransmission(HMC6343_ADDRESS);
  Wire.write(0xF1);
  Wire.write(0x14);                        // EEPROM address of the Heading IIR filter LSB
  Wire.write(0x00);                        // 0 is no filtering, 15 is filtered with 15 previous readings
  Wire.endTransmission();
    
  /*
   * Set the HMC6343 to 'upright front' orientation. This is temporary, but can be
   * written to an EEPROM register if required.
   */
  Wire.beginTransmission(HMC6343_ADDRESS);
  Wire.write(0x74);
  Wire.endTransmission();
  
}
//====================================================================================  
void loop() {
  
  /*
   * Set the HMC6343 to return the information that we want (HeadMSB, HeadLSB,
   * PitchMSB, PitchLSB, RollMSB, RollLSB).
   */
  Wire.beginTransmission(HMC6343_ADDRESS);
  Wire.write(0x50);                        // Command to return the data we want
  Wire.endTransmission();
  
  byte MSByte, LSByte;
  
  Wire.requestFrom(HMC6343_ADDRESS, 6);  // request 6 bytes (see command 0x50)
  while(Wire.available() < 1);           // busy wait while no bytes to receive
  
  MSByte = Wire.read();
  LSByte = Wire.read();
  float heading = ((MSByte << 8) + LSByte) / 10.0; // the heading in degrees
  
  MSByte = Wire.read();
  LSByte = Wire.read();
  float pitch = ((MSByte << 8) + LSByte) / 10.0;   // the pitch in degrees
  
  MSByte = Wire.read();
  LSByte = Wire.read();
  float roll = ((MSByte << 8) + LSByte) / 10.0;     // the roll in degrees
  
  Serial.print(heading);
  Serial.print(" ");
  Serial.print(pitch);
  Serial.print(" ");
  Serial.println(roll);
  
  delay(100);
}
//====================================================================================

It's really nice to see useful comments in your code. You might want to check the return values to see if the writes actually happened, especially if you count on the results later on.'

Wire.endTransmission() returns four different NAK type failures.
Wire.write() returns the number of bytes written.

Here's a snippet:
int8_t flag=-1; // signed flag, init to invalid result so we know if it has changed
byte written; // number of bytes written
Wire.beginTransmission(TMP102_ADDR); // base address
written = Wire.write(pointer); // pointer in 2 lsb
flag = Wire.endTransmission(); // flag is zero if no error
Serial.print ("wrote:");
Serial.print (written);
Serial.print (" ");
return (flag); // zero if no error, 1..4 are NAK errors