Install Honeywell 3-axis digital compass IC HMC5883L on SP7021 BPI-F2S board

The goal of this document is to illustrate how to install Honeywell 3-axis digital compass IC HMC5883L on SP7021 BPI-F2S board.

Refer to HMC5883L compass IC module (GY-273) below:

This is a compact module board. It consists of only a compass IC HMC5883L, a 3.3V LDO regulator XC6206, two 4.7k resistors and five capacitors. HMC5883L is the main IC. Size of HMC5883L is very small. It's only 3.0x3.0x0.9mm.

HMC5883L has magnetoresistive sensor circuit which converts incident magnetic field in the sensitive axis direction to a voltage output. It supports I2C interface operating at 400 kHz. I2C address is 0x1E (7-bit).

Please follow the following steps:

1. Install hardware

Refer to schematics of HMC5883L compass IC module:

XC6206 is a low drop-out regulator (LDO) which converters VCC (+5V) input power to +3.3V power. HMC5883L is 3-axis digital compass IC which senses magnetic flux density. R1 and R2 are 4.7k pull-up resistors for I2C signals SCL and SDA. C1, C2 and C3 are bypass capacitors which stabilize the +5V and 3.3V power, respectively. C4 and C5 are filter capacitors which are necessary for HMC5883L. Ceramic capacitors with ESR values less than 200 milli-ohms are recommended. I2C signals SDA, SCL, ground and +5 Volts power are connected to CON1 directly. One can use the connector to supply power and communicate with HMC5883L.

Refer to pin-assignment of Raspberry Pi compatible pin-header of SP7021 BPI-F2S board:

Connect HMC5883L compass IC module to SP7021 BPI-F2S board as shown in following table:

Pin # of CON1 of HMC5883L module

Pin name of CON1 of HMC5883L module

Pin # of 40-pin pin-header

Pin name of 40-pin pin-header

1

VCC

2

+5V_VDD

2

GND

9

GND

3

SCL

5

GPIO_P1_5/G_MX13 (SCL1)

4

SDA

3

GPIO_P1_4/G_MX12 (SDA1)

5

DRDY

-

-

Refer to photograph, HMC5883L compass module is connected to 40-pin pin-header of SP7021 BPI-F2S board.

2. Modify device-tree source file

Modify device-tree node i2cm0 in device-tree source file linux/kernel/arch/arm/boot/dts/sp7021-bpi-f2s.dts to setup channel 0 of I2C master for HMC5883L as shown below:

&i2cm0 { clock-frequency = <100000>; pinctrl-names = "default"; pinctrl-0 = <&i2cm0_pins>; #address-cells = <1>; #size-cells = <0>; hmc5883l@1e { compatible = "honeywell,hmc5883l"; reg = <0x1e>; mount-matrix = "1", "0", "0", "0", "1", "0", "0", "0", "1"; status = "okay"; }; };

Note that i2cm0 is a label to node i2c@9c004600. I2C address of HMC5883L is at 0x1E. Frequency of clock of I2C is set to 100,000 Hz. Property mount-matrix is optional. By default, identity matrix is used.

Also, modify dts node pinmux_i2cm0-pins to setup pins for channel 0 of I2C master as shown below:

i2cm0_pins: pinmux_i2cm0-pins { sunplus,pins = < SPPCTL_IOPAD(13, SPPCTL_PCTL_G_PMUX, MUXF_I2CM0_CLK, 0) SPPCTL_IOPAD(12, SPPCTL_PCTL_G_PMUX, MUXF_I2CM0_DAT, 0) >; };

Note that node pinmux_i2cm0-pins is sub-node of node pctl. G_MX13 and G_MX12 are set as CLK and DAT signals of channel 0 of I2C master, respectively.

Please note that to comply with Linux rules, after version 5.10.59, 4 property-names of pin node of SP7021 are changed as shown in table below:

5.4.35

5.10.59

5.4.35

5.10.59

sppctl,function

function

sppctl,groups

groups

sppctl,pins

sunplus,pins

sppctl,zero_func

sunplus,zerofunc

3. Enable Linux device drivers

Run make kconfig in project top directory. When “Linux/arm Kernel Configuration” menu pops up, move cursor to go to “Device Drivers” → “I2C support” → “I2C Hardware Bus support” sub-menu. Move cursor to “SP I2C support” and enable it. Refer to screenshot below:

Move cursor to go to “Device Drivers” → “Industrial I/O support” and press <Y> to enable it. Refer to screenshot below:

 

Press <Enter> key to enter “Industrial I/O support” sub-menu. Move cursor to go to “Magnetometer sensors” → “Honeywell HMC5843/HMC5883/HMC5883L 3-Axis Magnetometer (I2C)” and enable it. Refer to screenshot below:

Finally, save configuration.

4. Build Linux image

Go to top folder. Run make all to build Linux image.

5. Boot Linux

Boot Linux with the built image.

6. Read magnetic flux density

After Linux boots up successfully, run ls /sys/bus/iio/devices/iio:device0 to list sysfs of device iio:device0 (hmc5883l). Refer to screenshot below:

Please run cat command to show information of device iio:device0. Refer to screenshot below:

Device name is hmc5883l. Device number is 252:0. Available measurement configuration are “normal”, “positivebias” and “negativebias”. Current measurement configuration is “normal”. Available scaling factor are 0.000007299, 0.000009174, 0.000012195, 0.000015152, 0.000022727, 0.000025641, 0.000030303 and 0.000043478. Current scaling factor is 0.000009174. Available sampling frequencies are 0.750000 (Hz), 1.500000 (Hz), 3.0 (Hz), 7.500000 (Hz), 15.0 (Hz), 30.0 (Hz) and 75.0 (Hz). Current sampling frequency is 15.000000 (Hz). Mounting-matrix is identity matrix (no transformation):

1, 0, 0

0, 1, 0

0, 0, 1

Run cat /sys/bus/iio/devices/iio:device0/in_magn_x_raw to read magnetic flux density (raw data) in X-direction, run cat /sys/bus/iio/devices/iio:device0/in_magn_y_raw to read magnetic flux density (raw data) in Y-direction, and run cat /sys/bus/iio/devices/iio:device0/in_magn_z_raw to read magnetic flux density (raw data) in Z-direction. Refer to screenshot below:

The current magnetic flux density in x direction is

in_magn_x_raw * in_magn_scale = 195 * 0.000009174 = 0.001789 (100G) = 178.8 (mG)

The current magnetic flux density in y direction is

in_magn_y_raw * in_magn_scale = 124 * 0.000009174 = 0.001138 (100G) = 113.8 (mG)

The current magnetic flux density in z direction is

in_magn_z_raw * in_magn_scale = -82 * 0.000009174 = -0.0007523 (100G) = -75.23 (mG)

G is CGS unit Gauss. mG represents milli-gauss.

7. User-space device

If you want to use user-space applications to access I2C device file, instead of IIO interface via sysfs, please run make menuconfig and move cursor to go to “Device Drivers” → “I2C support” → “I2C device interface” and enable it. Refer to screenshot below:

When I2C interface driver probes successfully, it creates character device, /dev/i2c-X, where X is the channel number of I2C bus. Refer to screenshot, I2C device, /dev/i2c-0, is shown:

8. Example C code of using user-space device

Refer to the following C code for reading magnetic flux density from HMC5883L via Linux device file, /dev/i2c-0.

#include <stdio.h> #include <linux/types.h> #include <ctype.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include <sys/types.h> #include <sys/ioctl.h> #include <errno.h> #include <string.h> #define I2C_RETRIES 0x0701 #define I2C_TIMEOUT 0x0702 #define I2C_RDWR 0x0707 #define I2C_DEV "/dev/i2c-0" /* i2c device */ #define I2C_ADDR 0x1e /* slave device address */ struct i2c_msg { unsigned short addr; unsigned short flags; unsigned short len; unsigned char *buf; }; struct i2c_rdwr_ioctl_data { struct i2c_msg *msgs; int nmsgs; }; struct i2c_rdwr_ioctl_data storage_data; struct i2c_msg storage_msg[2]; int i2c_write(int fd, unsigned char slvAddr, unsigned short index, unsigned char * const data, unsigned char len) { int ret; unsigned char tmp[20]; tmp[0] = ((unsigned char *)&index)[0]; memcpy(tmp+1, data, len); len += 1; /***write data to storage**/ storage_data.nmsgs = 1; storage_data.msgs[0].len = len; // Data length storage_data.msgs[0].addr = slvAddr; // Device Addr storage_data.msgs[0].flags = 0; // write storage_data.msgs[0].buf = tmp; if (ioctl(fd, I2C_RDWR, &storage_data) < 0) { perror("ioctl write error"); return -1; } return 0; } int i2c_read(int fd, unsigned char slvAddr, unsigned short index, unsigned char *data, int len) { char tmp[4]; tmp[0] = ((unsigned char *)&index)[0]; storage_data.nmsgs = 2; storage_data.msgs[0].len = 1; storage_data.msgs[0].addr = slvAddr; storage_data.msgs[0].flags = 0; //Dummy write storage_data.msgs[0].buf = (unsigned char *)&tmp; storage_data.msgs[1].len = len; storage_data.msgs[1].addr = slvAddr; storage_data.msgs[1].flags = 1; storage_data.msgs[1].buf = data; if (ioctl(fd,I2C_RDWR, &storage_data) < 0) { perror("ioctl read error"); return -1; } return 0; } int main(int argc, char **argv) { int fd = 0, i; unsigned char read_buf[16]; unsigned char write_buf[16]; short adc_X, adc_Y, adc_Z; storage_data.nmsgs = 2; storage_data.msgs = storage_msg; fd = open(I2C_DEV,O_RDWR); if (fd < 0) { printf("Cannot open \'%s\'!\n", I2C_DEV); exit(1); } printf("Opened \'%s\' successfully!\n", I2C_DEV); if (i2c_read(fd, I2C_ADDR, 0X0A, read_buf, 3) != 0) goto error; printf("I2C addr = %02X, Chip id = %c%c%c (%02X %02X %02X)\n", I2C_ADDR, read_buf[0], read_buf[1], read_buf[2], read_buf[0], read_buf[1], read_buf[2]); ioctl(fd, I2C_TIMEOUT, 1); /*set timeout value*/ ioctl(fd, I2C_RETRIES, 2); /*set retry times*/ // Set configuration register A. // MA = 11 (average=8) // DO = 100 (rate=15Hz) // MS = 00 (normal) write_buf[0] = 0x70; if (i2c_write(fd, I2C_ADDR, 0x00, write_buf, 1) != 0) goto error; // Set configuration register B. // GN = 001 (0.92 mG/LSB) write_buf[0] = 0x20; if (i2c_write(fd, I2C_ADDR, 0x01, write_buf, 1) != 0) goto error; // Set mode register. // HS = 0 (400kHz) // MR = 00 (continue) write_buf[0] = 0x20; if (i2c_write(fd, I2C_ADDR, 0x02, write_buf, 1) != 0) goto error; i = 0; do { // Read status. if (i2c_read(fd, I2C_ADDR, 0X09, read_buf, 1) != 0) goto error; if (read_buf[0] & 0x01) break; usleep(1000); // 1 mS i++; } while (i < 100); printf("Status = %02X\n", read_buf[0]); // Read magnetic data: X, Y, Z (MSB, LSB) if (i2c_read(fd, I2C_ADDR, 0X03, read_buf, 6) != 0) goto error; adc_X = (read_buf[0]<<8) | read_buf[1]; adc_Z = (read_buf[2]<<8) | read_buf[3]; adc_Y = (read_buf[4]<<8) | read_buf[5]; printf("adc_X = %5d (%04x)\n", adc_X, (adc_X & 0xffff)); printf("adc_Y = %5d (%04x)\n", adc_Y, (adc_Y & 0xffff)); printf("adc_Z = %5d (%04x)\n", adc_Z, (adc_Z & 0xffff)); printf("B_X = %+5.0f (mG)\n", adc_X*0.92f); printf("B_Y = %+5.0f (mG)\n", adc_Y*0.92f); printf("B_Z = %+5.0f (mG)\n", adc_Z*0.92f); if (fd) { close(fd); } return 0; error: fprintf(stderr, "Failed to access i2c device \'%s\'!\n", I2C_DEV); exit(1); }

Refer to screenshot captured when executable hmc5883l was running:

The current magnetic flux densities are 185 mG in X-direction, 107 mG in Y-direction and -69 mG in Z-direction.