pre-commit, docs, full test

This commit is contained in:
Liz 2025-07-07 14:35:29 -04:00
parent 80a4ab7cff
commit ec2c626ffd
7 changed files with 348 additions and 101 deletions

View file

@ -94,8 +94,19 @@ Usage Example
.. code-block:: python
import time
import board
import adafruit_as5600
i2c = board.I2C()
sensor = adafruit_as5600.AS5600(i2c)
while True:
print(f"Raw angle: {sensor.raw_angle}")
print(f"Scaled angle: {sensor.angle}")
print(f"Magnitude: {sensor.magnitude}")
time.sleep(2)
Documentation
=============
API documentation for this library can be found on `Read the Docs <https://docs.circuitpython.org/projects/as5600/en/latest/>`_.

View file

@ -26,14 +26,15 @@ Implementation Notes
* Adafruit's Register library: https://github.com/adafruit/Adafruit_CircuitPython_Register
"""
from micropython import const
from adafruit_bus_device.i2c_device import I2CDevice
from adafruit_register.i2c_bit import ROBit, RWBit
from adafruit_register.i2c_bits import RWBits
from adafruit_register.i2c_struct import ROUnaryStruct, UnaryStruct
from adafruit_register.i2c_bits import RWBits, ROBits
from adafruit_register.i2c_bit import RWBit, ROBit
from micropython import const
try:
import typing # pylint: disable=unused-import
from typing import Optional
from busio import I2C
except ImportError:
pass
@ -42,21 +43,21 @@ __version__ = "0.0.0+auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_AS5600.git"
# I2C Address
AS5600_DEFAULT_ADDRESS = const(0x36)
_ADDR = const(0x36)
# Register addresses
_AS5600_REG_ZMCO = const(0x00) # ZMCO register (burn count)
_AS5600_REG_ZPOS_H = const(0x01) # Zero position high byte
_AS5600_REG_MPOS_H = const(0x03) # Maximum position high byte
_AS5600_REG_MANG_H = const(0x05) # Maximum angle high byte
_AS5600_REG_CONF_L = const(0x08) # Configuration register low byte
_AS5600_REG_CONF_H = const(0x07) # Configuration register high byte
_AS5600_REG_STATUS = const(0x0B) # Status register
_AS5600_REG_RAWANGLE_H = const(0x0C) # Raw angle high byte
_AS5600_REG_ANGLE_H = const(0x0E) # Scaled angle high byte
_AS5600_REG_AGC = const(0x1A) # Automatic Gain Control register
_AS5600_REG_MAGNITUDE_H = const(0x1B) # Magnitude high byte
_AS5600_REG_BURN = const(0xFF) # Burn command register
_ZMCO = const(0x00) # ZMCO register (burn count)
_ZPOS_H = const(0x01) # Zero position high byte
_MPOS_H = const(0x03) # Maximum position high byte
_MANG_H = const(0x05) # Maximum angle high byte
_CONF_L = const(0x08) # Configuration register low byte
_CONF_H = const(0x07) # Configuration register high byte
_STATUS = const(0x0B) # Status register
_RAWANGLE_H = const(0x0C) # Raw angle high byte
_ANGLE_H = const(0x0E) # Scaled angle high byte
_AGC = const(0x1A) # Automatic Gain Control register
_MAGNITUDE_H = const(0x1B) # Magnitude high byte
_BURN = const(0xFF) # Burn command register
# Power mode constants
POWER_MODE_NOM = const(0x00) # Normal mode (default)
@ -89,56 +90,70 @@ SLOW_FILTER_4X = const(0x02) # 4x
SLOW_FILTER_2X = const(0x03) # 2x
# Fast filter threshold constants
FAST_FILTER_THRESH_SLOW_ONLY = const(0x00) # Slow filter only (default)
FAST_FILTER_THRESH_6LSB = const(0x01) # 6 LSB
FAST_FILTER_THRESH_7LSB = const(0x02) # 7 LSB
FAST_FILTER_THRESH_9LSB = const(0x03) # 9 LSB
FAST_FILTER_THRESH_18LSB = const(0x04) # 18 LSB
FAST_FILTER_THRESH_21LSB = const(0x05) # 21 LSB
FAST_FILTER_THRESH_24LSB = const(0x06) # 24 LSB
FAST_FILTER_THRESH_10LSB = const(0x07) # 10 LSB
FAST_FILTER_SLOW_ONLY = const(0x00) # Slow filter only (default)
FAST_FILTER_6LSB = const(0x01) # 6 LSB
FAST_FILTER_7LSB = const(0x02) # 7 LSB
FAST_FILTER_9LSB = const(0x03) # 9 LSB
FAST_FILTER_18LSB = const(0x04) # 18 LSB
FAST_FILTER_21LSB = const(0x05) # 21 LSB
FAST_FILTER_24LSB = const(0x06) # 24 LSB
FAST_FILTER_10LSB = const(0x07) # 10 LSB
class AS5600:
"""Driver for the AS5600 12-bit contactless position sensor.
:param ~busio.I2C i2c_bus: The I2C bus the AS5600 is connected to.
:param int address: The I2C device address. Defaults to :const:`AS5600_DEFAULT_ADDRESS`
:param int address: The I2C device address. Defaults to :const:`_ADDR`
"""
# Register definitions using adafruit_register
_zmco = ROUnaryStruct(_AS5600_REG_ZMCO, "B") # Read-only burn count
_zmco = ROUnaryStruct(_ZMCO, "B") # Read-only burn count
# 12-bit position registers (stored as 16-bit big-endian)
_zpos = UnaryStruct(_AS5600_REG_ZPOS_H, ">H")
_mpos = UnaryStruct(_AS5600_REG_MPOS_H, ">H")
_mang = UnaryStruct(_AS5600_REG_MANG_H, ">H")
_rawangle = ROUnaryStruct(_AS5600_REG_RAWANGLE_H, ">H")
_angle = ROUnaryStruct(_AS5600_REG_ANGLE_H, ">H")
_zpos = UnaryStruct(_ZPOS_H, ">H")
_mpos = UnaryStruct(_MPOS_H, ">H")
_mang = UnaryStruct(_MANG_H, ">H")
_rawangle = ROUnaryStruct(_RAWANGLE_H, ">H")
_angle = ROUnaryStruct(_ANGLE_H, ">H")
# 8-bit registers
_agc = ROUnaryStruct(_AS5600_REG_AGC, "B")
_magnitude = ROUnaryStruct(_AS5600_REG_MAGNITUDE_H, ">H")
agc: int = ROUnaryStruct(_AGC, "B")
"""The current AGC (Automatic Gain Control) value.
Range is 0-255 in 5V mode, 0-128 in 3.3V mode."""
_magnitude = ROUnaryStruct(_MAGNITUDE_H, ">H")
# Status register bits
_mh = ROBit(_AS5600_REG_STATUS, 3) # MH (magnet too strong)
_ml = ROBit(_AS5600_REG_STATUS, 4) # ML (magnet too weak)
_md = ROBit(_AS5600_REG_STATUS, 5) # MD (magnet detected)
min_gain_overflow: bool = ROBit(_STATUS, 3) # MH (magnet too strong)
"""True if AGC minimum gain overflow occurred (magnet too strong)."""
max_gain_overflow: bool = ROBit(_STATUS, 4) # ML (magnet too weak)
"""True if AGC maximum gain overflow occurred (magnet too weak)."""
magnet_detected: bool = ROBit(_STATUS, 5) # MD (magnet detected)
"""True if a magnet is detected, otherwise False"""
# Configuration bits
_power_mode = RWBits(2, _AS5600_REG_CONF_L, 0)
_hysteresis = RWBits(2, _AS5600_REG_CONF_L, 2)
_output_stage = RWBits(2, _AS5600_REG_CONF_L, 4)
_pwm_freq = RWBits(2, _AS5600_REG_CONF_L, 6)
_slow_filter = RWBits(2, _AS5600_REG_CONF_H, 0)
_fast_filter_thresh = RWBits(3, _AS5600_REG_CONF_H, 2)
_watchdog = RWBit(_AS5600_REG_CONF_H, 5) # Bit 13 of the 16-bit config register
_power_mode = RWBits(2, _CONF_L, 0)
_hysteresis = RWBits(2, _CONF_L, 2)
_output_stage = RWBits(2, _CONF_L, 4)
_pwm_freq = RWBits(2, _CONF_L, 6)
_slow_filter = RWBits(2, _CONF_H, 0)
_fast_filter = RWBits(3, _CONF_H, 2)
watchdog: bool = RWBit(_CONF_H, 5) # Bit 13 of the 16-bit config register
"""Enable or disable the watchdog timer."""
def __init__(self, i2c: I2C, address: int = AS5600_DEFAULT_ADDRESS) -> None:
self.i2c_device = I2CDevice(i2c, address)
# Check if we can communicate with the device
# The AS5600 doesn't have a WHO_AM_I register, so we'll just try reading status
_ = self.magnet_detected
def __init__(self, i2c: I2C, address: int = _ADDR) -> None:
try:
self.i2c_device = I2CDevice(i2c, address)
# Check if we can communicate with the device
self.watchdog = False
self.power_mode = POWER_MODE_NOM
self.hysteresis = HYSTERESIS_OFF
self.slow_filter = SLOW_FILTER_16X
self.fast_filter_threshold = FAST_FILTER_SLOW_ONLY
self.z_position = 0
self.m_position = 4095
self.max_angle = 4095
except ValueError:
raise ValueError(f"No I2C device found at address 0x{address:02X}")
@property
def zm_count(self) -> int:
@ -196,42 +211,11 @@ class AS5600:
This is scaled according to ZPOS/MPOS/MANG settings."""
return self._angle & 0x0FFF
@property
def agc_min_gain_overflow(self) -> bool:
"""True if AGC minimum gain overflow occurred (magnet too strong)."""
return self._mh
@property
def agc_max_gain_overflow(self) -> bool:
"""True if AGC maximum gain overflow occurred (magnet too weak)."""
return self._ml
@property
def magnet_detected(self) -> bool:
"""True if a magnet is detected."""
return self._md
@property
def agc(self) -> int:
"""The current AGC (Automatic Gain Control) value.
Range is 0-255 in 5V mode, 0-128 in 3.3V mode."""
return self._agc
@property
def magnitude(self) -> int:
"""The magnitude value from the CORDIC processor (0-4095)."""
return self._magnitude & 0x0FFF
@property
def watchdog(self) -> bool:
"""Enable or disable the watchdog timer."""
return self._watchdog
@watchdog.setter
def watchdog(self, value: bool) -> None:
"""Enable or disable the watchdog timer."""
self._watchdog = value
@property
def power_mode(self) -> int:
"""The power mode setting. Use POWER_MODE_* constants."""
@ -294,12 +278,12 @@ class AS5600:
@property
def fast_filter_threshold(self) -> int:
"""The fast filter threshold setting. Use FAST_FILTER_THRESH_* constants."""
return self._fast_filter_thresh
"""The fast filter threshold setting. Use FAST_FILTER_* constants."""
return self._fast_filter
@fast_filter_threshold.setter
def fast_filter_threshold(self, value: int) -> None:
"""Set the fast filter threshold. Use FAST_FILTER_THRESH_* constants."""
"""Set the fast filter threshold. Use FAST_FILTER_* constants."""
if not 0 <= value <= 7:
raise ValueError("Invalid fast filter threshold setting")
self._fast_filter_thresh = value
self._fast_filter = value

View file

@ -25,7 +25,7 @@ extensions = [
# Uncomment the below if you use native CircuitPython modules such as
# digitalio, micropython and busio. List the modules you use. Without it, the
# autodoc module docs will fail to generate with a warning.
# autodoc_mock_imports = ["digitalio", "busio"]
autodoc_mock_imports = ["digitalio", "busio", "adafruit_register"]
autodoc_preserve_defaults = True

View file

@ -6,3 +6,12 @@ Ensure your device works with this simple test.
.. literalinclude:: ../examples/as5600_simpletest.py
:caption: examples/as5600_simpletest.py
:linenos:
Full test
----------
Full test of the library
.. literalinclude:: ../examples/as5600_fulltest.py
:caption: examples/as5600_fulltest.py
:linenos:

View file

@ -24,14 +24,12 @@ Table of Contents
.. toctree::
:caption: Tutorials
.. todo:: Add any Learn guide links here. If there are none, then simply delete this todo and leave
the toctree above for use later.
Learn Guide: <https://learn.adafruit.com/adafruit-as5600-magnetic-angle-sensor>
.. toctree::
:caption: Related Products
.. todo:: Add any product links here. If there are none, then simply delete this todo and leave
the toctree above for use later.
Adafruit AS5600 Magnetic Angle Sensor <https://www.adafruit.com/product/6357>
.. toctree::
:caption: Other Links

241
examples/as5600_fulltest.py Normal file
View file

@ -0,0 +1,241 @@
# SPDX-FileCopyrightText: Copyright (c) 2025 Liz Clark for Adafruit Industries
#
# SPDX-License-Identifier: MIT
"""
Full library testing example for the Adafruit AS5600 CircuitPython library
This example tests all functionality of the AS5600 magnetic angle sensor
"""
import time
import board
import adafruit_as5600
# Initialize I2C and AS5600
i2c = board.I2C() # uses board.SCL and board.SDA
as5600 = adafruit_as5600.AS5600(i2c)
print("Adafruit AS5600 Full Test")
print("AS5600 found!")
print()
# Test zm_count property
zm_count = as5600.zm_count
print(f"ZM Count (burn count): {zm_count}")
# Test z_position property
z_pos = as5600.z_position
print(f"Z Position: {z_pos}")
# Test setting z_position (XOR current value with 0xADA to change it)
test_pos = (z_pos ^ 0xADA) & 0x0FFF # XOR with 0xADA and keep within 12-bit range
print(f"Setting Z Position to {test_pos} (0x{test_pos:03X})... ")
try:
as5600.z_position = test_pos
print("Success")
new_z_pos = as5600.z_position
print(f"New Z Position: {new_z_pos} (0x{new_z_pos:03X})")
except Exception as e:
print(f"Failed: {e}")
# Test m_position property
m_pos = as5600.m_position
print(f"M Position: {m_pos}")
# Test setting m_position (XOR current value with 0xBEE)
test_m_pos = (m_pos ^ 0xBEE) & 0x0FFF
print(f"Setting M Position to {test_m_pos} (0x{test_m_pos:03X})... ")
try:
as5600.m_position = test_m_pos
print("Success")
new_m_pos = as5600.m_position
print(f"New M Position: {new_m_pos} (0x{new_m_pos:03X})")
except Exception as e:
print(f"Failed: {e}")
# Test max_angle property
max_angle = as5600.max_angle
print(f"Max Angle: {max_angle}")
# Test setting max_angle (XOR current value with 0xCAB)
test_max_angle = (max_angle ^ 0xCAB) & 0x0FFF
print(f"Setting Max Angle to {test_max_angle} (0x{test_max_angle:03X})... ")
try:
as5600.max_angle = test_max_angle
print("Success")
new_max_angle = as5600.max_angle
print(f"New Max Angle: {new_max_angle} (0x{new_max_angle:03X})")
except Exception as e:
print(f"Failed: {e}")
# Test watchdog property
print("Turning on watchdog... ")
try:
as5600.watchdog = True
print("Success")
print(f"Watchdog status: {'ENABLED' if as5600.watchdog else 'DISABLED'}")
except Exception as e:
print(f"Failed: {e}")
print("Turning off watchdog...")
try:
as5600.watchdog = False
print("Success")
print(f"Watchdog status: {'ENABLED' if as5600.watchdog else 'DISABLED'}")
except Exception as e:
print(f"Failed: {e}")
# Test power_mode property
print("Setting power mode...")
try:
as5600.power_mode = adafruit_as5600.POWER_MODE_NOM
print("Success")
mode = as5600.power_mode
print("Power mode: ")
if mode == adafruit_as5600.POWER_MODE_NOM:
print("Normal")
elif mode == adafruit_as5600.POWER_MODE_LPM1:
print("Low Power Mode 1")
elif mode == adafruit_as5600.POWER_MODE_LPM2:
print("Low Power Mode 2")
elif mode == adafruit_as5600.POWER_MODE_LPM3:
print("Low Power Mode 3")
except Exception as e:
print(f"Failed: {e}")
# Test hysteresis property
print("Setting hysteresis...")
try:
as5600.hysteresis = adafruit_as5600.HYSTERESIS_OFF
print("Success")
hysteresis = as5600.hysteresis
print("Hysteresis: ")
if hysteresis == adafruit_as5600.HYSTERESIS_OFF:
print("OFF")
elif hysteresis == adafruit_as5600.HYSTERESIS_1LSB:
print("1 LSB")
elif hysteresis == adafruit_as5600.HYSTERESIS_2LSB:
print("2 LSB")
elif hysteresis == adafruit_as5600.HYSTERESIS_3LSB:
print("3 LSB")
except Exception as e:
print(f"Failed: {e}")
# Test output_stage property
print("Setting output stage...")
try:
as5600.output_stage = adafruit_as5600.OUTPUT_STAGE_ANALOG_FULL
print("Success")
output_stage = as5600.output_stage
print("Output stage: ")
if output_stage == adafruit_as5600.OUTPUT_STAGE_ANALOG_FULL:
print("Analog Full (0% to 100%)")
elif output_stage == adafruit_as5600.OUTPUT_STAGE_ANALOG_REDUCED:
print("Analog Reduced (10% to 90%)")
elif output_stage == adafruit_as5600.OUTPUT_STAGE_DIGITAL_PWM:
print("Digital PWM")
elif output_stage == adafruit_as5600.OUTPUT_STAGE_RESERVED:
print("Reserved")
except Exception as e:
print(f"Failed: {e}")
# Test pwm_frequency property
print("Setting PWM frequency...")
try:
as5600.pwm_frequency = adafruit_as5600.PWM_FREQ_115HZ
print("Success")
pwm_freq = as5600.pwm_frequency
print("PWM frequency: ")
if pwm_freq == adafruit_as5600.PWM_FREQ_115HZ:
print("115 Hz")
elif pwm_freq == adafruit_as5600.PWM_FREQ_230HZ:
print("230 Hz")
elif pwm_freq == adafruit_as5600.PWM_FREQ_460HZ:
print("460 Hz")
elif pwm_freq == adafruit_as5600.PWM_FREQ_920HZ:
print("920 Hz")
except Exception as e:
print(f"Failed: {e}")
# Test slow_filter property
print("Setting slow filter to 16x (options: 16X=0, 8X=1, 4X=2, 2X=3)... ")
try:
as5600.slow_filter = adafruit_as5600.SLOW_FILTER_16X
print("Success")
slow_filter = as5600.slow_filter
print("Slow filter: ")
if slow_filter == adafruit_as5600.SLOW_FILTER_16X:
print("16x")
elif slow_filter == adafruit_as5600.SLOW_FILTER_8X:
print("8x")
elif slow_filter == adafruit_as5600.SLOW_FILTER_4X:
print("4x")
elif slow_filter == adafruit_as5600.SLOW_FILTER_2X:
print("2x")
except Exception as e:
print(f"Failed: {e}")
# Test fast_filter_threshold property
print("Setting fast filter threshold... ")
try:
as5600.fast_filter_threshold = adafruit_as5600.FAST_FILTER_SLOW_ONLY
print("Success")
fast_thresh = as5600.fast_filter_threshold
print("Fast filter threshold: ", end="")
if fast_thresh == adafruit_as5600.FAST_FILTER_SLOW_ONLY:
print("Slow filter only")
elif fast_thresh == adafruit_as5600.FAST_FILTER_6LSB:
print("6 LSB")
elif fast_thresh == adafruit_as5600.FAST_FILTER_7LSB:
print("7 LSB")
elif fast_thresh == adafruit_as5600.FAST_FILTER_9LSB:
print("9 LSB")
elif fast_thresh == adafruit_as5600.FAST_FILTER_18LSB:
print("18 LSB")
elif fast_thresh == adafruit_as5600.FAST_FILTER_21LSB:
print("21 LSB")
elif fast_thresh == adafruit_as5600.FAST_FILTER_24LSB:
print("24 LSB")
elif fast_thresh == adafruit_as5600.FAST_FILTER_10LSB:
print("10 LSB")
except Exception as e:
print(f"Failed: {e}")
# Reset position settings to defaults
print("\nResetting position settings to defaults...")
as5600.z_position = 0
as5600.m_position = 4095
as5600.max_angle = 4095
print("\nStarting continuous angle reading...")
print("=" * 80)
# Continuously read and display angle values
while True:
# Get angle readings
raw_angle = as5600.raw_angle
angle = as5600.angle
# Build output string
output = f"Raw: {raw_angle:4d} (0x{raw_angle:03X}) | Scaled: {angle:4d} (0x{angle:03X})"
# Check status conditions
if as5600.magnet_detected:
output += " | Magnet: YES"
else:
output += " | Magnet: NO "
if as5600.min_gain_overflow:
output += " | MH: magnet too strong"
if as5600.max_gain_overflow:
output += " | ML: magnet too weak"
# Get AGC and Magnitude values
agc = as5600.agc
magnitude = as5600.magnitude
output += f" | AGC: {agc:3d} | Mag: {magnitude:4d}"
print(output)
time.sleep(2)

View file

@ -1,24 +1,28 @@
# SPDX-FileCopyrightText: Copyright (c) 2025 Liz Clark for Adafruit Industries
#
# SPDX-License-Identifier: MIT
"""AS5600 Simple Test"""
import time
import board
import adafruit_as5600
# Create I2C bus and sensor instance
i2c = board.I2C()
sensor = adafruit_as5600.AS5600(i2c)
# Configure sensor
sensor.power_mode = adafruit_as5600.POWER_MODE_NOM
sensor.hysteresis = adafruit_as5600.HYSTERESIS_OFF
sensor.slow_filter = adafruit_as5600.SLOW_FILTER_16X
while True:
# Read angle values
print(f"Raw angle: {sensor.raw_angle}")
print(f"Scaled angle: {sensor.angle}")
print(f"Magnitude: {sensor.magnitude}")
print(f"Magnet detected: {sensor.magnet_detected}")
if sensor.magnet_detected:
if sensor.max_gain_overflow is True:
print("Magnet is too weak")
if sensor.min_gain_overflow is True:
print("Magnet is too strong")
print(f"Raw angle: {sensor.raw_angle}")
print(f"Scaled angle: {sensor.angle}")
print(f"Magnitude: {sensor.magnitude}")
else:
print("Waiting for magnet..")
print()
time.sleep(2)