Skip to content

Instantly share code, notes, and snippets.

@amb
Created February 4, 2025 17:52
Show Gist options
  • Save amb/e4f8b388908a991f30a686a82a9c87be to your computer and use it in GitHub Desktop.
Save amb/e4f8b388908a991f30a686a82a9c87be to your computer and use it in GitHub Desktop.
Reading data from QMC6309 3D hall effect I2C sensor
# QMC6309 3D Hall Sensor Micropython Library sketch
from machine import I2C, Pin, SoftI2C
from micropython import const
import ustruct
import utime
class QMC6310:
# I2C Address and Register Constants
QMC6310_ADDRESS = const(0x7C)
# Register addresses
QMC6310_X_LSB = const(0x01)
QMC6310_X_MSB = const(0x02)
QMC6310_Y_LSB = const(0x03)
QMC6310_Y_MSB = const(0x04)
QMC6310_Z_LSB = const(0x05)
QMC6310_Z_MSB = const(0x06)
QMC6310_STATUS = const(0x09)
QMC6310_CONTROL_1 = const(0x0A)
QMC6310_CONTROL_2 = const(0x0B)
# Status register bits
QMC6310_STATUS_DRDY = const(0x01)
QMC6310_STATUS_OVFL = const(0x02)
def __init__(self, i2c):
"""Initialize the QMC6310 sensor with the given I2C bus"""
self._i2c = i2c
def self_test(self):
"""
TODO: function not yet finished
Perform a self-test by:
* Write Register 0AH by 0x00 (Set Suspend Mode)
* Write Register 0AH by 0x03 (Set Continuous Mode)
* Waiting 20 millisecond until measurement ends
* Write Register 0EH by 0x80 (Set Selftest enable)
* Waiting 150 millisecond until measurement ends
* Check status register 09H[2] ,”1” means ready
* Read data Register 13H ~ 15H
* Self-test Judgment: If the delta value of each axis is in the range of following table, the chip is working properly.
"""
# Set suspend mode
self._i2c.writeto_mem(self.QMC6310_ADDRESS, self.QMC6310_CONTROL_1, bytes([0x00]))
# Set continuous mode
self._i2c.writeto_mem(self.QMC6310_ADDRESS, self.QMC6310_CONTROL_1, bytes([0x03]))
# Wait 20ms
utime.sleep_ms(20)
# Set self-test enable
self._i2c.writeto_mem(self.QMC6310_ADDRESS, 0xE, bytes([0x80]))
# Wait 150ms
utime.sleep_ms(150)
# Check status register
status = self._i2c.readfrom_mem(self.QMC6310_ADDRESS, self.QMC6310_STATUS, 1)[0]
if not status & self.QMC6310_STATUS_DRDY:
return False
# Read data
data = self._i2c.readfrom_mem(self.QMC6310_ADDRESS, self.QMC6310_X_LSB, 6)
x, y, z = ustruct.unpack("<hhh", data)
# Check delta values
return all(-100 <= val <= 100 for val in (x, y, z))
def normal_mode_setup(self):
"""
* Write Register 0BH by 0x40 (Define Set/Reset mode, with Set/Reset On, Field Range 32Guass, ODR=200HZ)
* Write Register 0AH by 0x61 (Set Normal Mode, OSR1=8, OSR2=8)
"""
# Define Set/Reset mode
self._i2c.writeto_mem(self.QMC6310_ADDRESS, self.QMC6310_CONTROL_2, bytes([0x40]))
# Set Normal Mode
self._i2c.writeto_mem(self.QMC6310_ADDRESS, self.QMC6310_CONTROL_1, bytes([0x61]))
def continuous_mode_setup(self):
"""
* Write Register 0BH by 0x00 (Define Set/Reset mode, with Set/Reset On, Field Range 32Guass)
* Write Register 0AH by 0x63 (Set Continuous Mode, OSR1=8,OSR2=8)
"""
# example: write to control register 1 0x00: define set/reset mode, with set/reset on, field range 32Gauss
# bits 4-6 is output data rate, 000 = 1, 100-111 = 200
self._i2c.writeto_mem(self.QMC6310_ADDRESS, self.QMC6310_CONTROL_2, bytes([0b01110000]))
# example: write to control register 2 0x63 (0b1100011): set continuous mode, OSR1=8, OSR2=8
# Set Continuous Mode
# bits 0-1 is mode, 11 = continuous mode
# bits 3-4 is over sample ratio OSR1, both bits set to 1 means no over sampling
# bits 5-7 is low pass filter, 100-111 = 16, 000 = 1
self._i2c.writeto_mem(self.QMC6310_ADDRESS, self.QMC6310_CONTROL_1, bytes([0b1100011]))
def read_data(self):
"""
Read magnetometer data
:return: Tuple of (x, y, z) magnetic field readings, or None if read fails
"""
try:
# Check data ready status
status = self._i2c.readfrom_mem(self.QMC6310_ADDRESS, self.QMC6310_STATUS, 1)[0]
if not status & self.QMC6310_STATUS_DRDY:
return None
# Read 6 bytes of magnetic data (2 bytes each for x, y, z)
data = self._i2c.readfrom_mem(self.QMC6310_ADDRESS, self.QMC6310_X_LSB, 6)
# Unpack little-endian 16-bit signed integers
x, y, z = ustruct.unpack("<hhh", data)
# Optional: Check data range
if not all(-32768 <= val <= 32767 for val in (x, y, z)):
print("Bad data range")
return None
return (x, y, z)
except OSError:
print("Error reading data")
return None
i2c = I2C(0, sda=Pin(8), scl=Pin(9))
utime.sleep_ms(100)
# Measurement example
# * Check status register 09H[0] ,”1” means ready
# * Read data register 01H ~ 06H
status = i2c.readfrom_mem(0x7C, 0x09, 1)
print(bin(int.from_bytes(status)))
utime.sleep_ms(100)
# Try reading data
hall = QMC6310(i2c)
hall.continuous_mode_setup()
skips = 0
while True:
val = hall.read_data()
if val:
print(val, " ", skips)
skips = 0
else:
skips += 1
utime.sleep_ms(5)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment