Created
February 4, 2025 17:52
-
-
Save amb/e4f8b388908a991f30a686a82a9c87be to your computer and use it in GitHub Desktop.
Reading data from QMC6309 3D hall effect I2C sensor
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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