Implement easypoint
This commit is contained in:
parent
1863543428
commit
4f7f3dcc3c
25
docs/easypoint.md
Normal file
25
docs/easypoint.md
Normal file
@ -0,0 +1,25 @@
|
||||
# AS5013 (aka. 'EasyPoint')
|
||||
|
||||
Module handles the AM5013 Two-dimensional magnetic position sensor with digital coordinates output
|
||||
|
||||
Product page: https://ams.com/en/as5013
|
||||
|
||||
### Usage
|
||||
|
||||
Declare I2C bus and add this module in your main class.
|
||||
|
||||
```python
|
||||
from kmk.modules.easypoint import Easypoint
|
||||
import busio as io
|
||||
|
||||
i2c = busio.I2C(scl=board.GP1, sda=board.GP0)
|
||||
|
||||
easypoint = Easypoint(i2c, address=0x40)
|
||||
keyboard.modules.append(easypoint)
|
||||
```
|
||||
|
||||
Further configuring the AS5013 involved x/y-offset, and deadzone.
|
||||
|
||||
```python
|
||||
easypoint = Easypoint(i2c, address=0x40, y_offset=Y_OFFSET, x_offset=X_OFFSET, dead_x=DEAD_X, dead_y=DEAD_Y)
|
||||
```
|
@ -29,3 +29,4 @@ These modules are for specific hardware and may require additional libraries to
|
||||
- [ADNS9800](adns9800.md): Controlling ADNS9800 optical sensor.
|
||||
- [Encoder](encoder.md): Handling rotary encoders.
|
||||
- [Pimoroni trackball](pimoroni_trackball.md): Handling a small I2C trackball made by Pimoroni.
|
||||
- [AS5013 aka. easypoint](easypoint.md): Handling a small I2C magnetic position sensor made by AMS.
|
||||
|
145
kmk/modules/easypoint.py
Normal file
145
kmk/modules/easypoint.py
Normal file
@ -0,0 +1,145 @@
|
||||
'''
|
||||
Extension handles usage of AS5013 by AMS
|
||||
'''
|
||||
|
||||
from supervisor import ticks_ms
|
||||
|
||||
from kmk.modules import Module
|
||||
from kmk.modules.mouse_keys import PointingDevice
|
||||
|
||||
I2C_ADDRESS = 0x40
|
||||
I2X_ALT_ADDRESS = 0x41
|
||||
|
||||
X = 0x10
|
||||
Y_RES_INT = 0x11
|
||||
|
||||
XP = 0x12
|
||||
XN = 0x13
|
||||
YP = 0x14
|
||||
YN = 0x15
|
||||
|
||||
M_CTRL = 0x2B
|
||||
T_CTRL = 0x2D
|
||||
|
||||
Y_OFFSET = 17
|
||||
X_OFFSET = 7
|
||||
|
||||
DEAD_X = 5
|
||||
DEAD_Y = 5
|
||||
|
||||
|
||||
class Easypoint(Module):
|
||||
'''Module handles usage of AS5013 by AMS'''
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
i2c,
|
||||
address=I2C_ADDRESS,
|
||||
y_offset=Y_OFFSET,
|
||||
x_offset=X_OFFSET,
|
||||
dead_x=DEAD_X,
|
||||
dead_y=DEAD_Y,
|
||||
):
|
||||
self._i2c_address = address
|
||||
self._i2c_bus = i2c
|
||||
|
||||
# HID parameters
|
||||
self.pointing_device = PointingDevice()
|
||||
self.polling_interval = 20
|
||||
self.last_tick = ticks_ms()
|
||||
|
||||
# Offsets for poor soldering
|
||||
self.y_offset = y_offset
|
||||
self.x_offset = x_offset
|
||||
|
||||
# Deadzone
|
||||
self.dead_x = DEAD_X
|
||||
self.dead_y = DEAD_Y
|
||||
|
||||
def during_bootup(self, keyboard):
|
||||
return
|
||||
|
||||
def before_matrix_scan(self, keyboard):
|
||||
'''
|
||||
Return value will be injected as an extra matrix update
|
||||
'''
|
||||
now = ticks_ms()
|
||||
if now - self.last_tick < self.polling_interval:
|
||||
return
|
||||
self.last_tick = now
|
||||
|
||||
x, y = self._read_raw_state()
|
||||
|
||||
# I'm a shit coder, so offset is handled in software side
|
||||
s_x = self.getSignedNumber(x, 8) - self.x_offset
|
||||
s_y = self.getSignedNumber(y, 8) - self.y_offset
|
||||
|
||||
# Evaluate Deadzone
|
||||
if s_x in range(-self.dead_x, self.dead_x) and s_y in range(
|
||||
-self.dead_y, self.dead_y
|
||||
):
|
||||
# Within bounds, just die
|
||||
return
|
||||
else:
|
||||
# Set the X/Y from easypoint
|
||||
self.pointing_device.report_x[0] = x
|
||||
self.pointing_device.report_y[0] = y
|
||||
|
||||
self.pointing_device.hid_pending = x != 0 or y != 0
|
||||
|
||||
return
|
||||
|
||||
def after_matrix_scan(self, keyboard):
|
||||
return
|
||||
|
||||
def before_hid_send(self, keyboard):
|
||||
return
|
||||
|
||||
def after_hid_send(self, keyboard):
|
||||
if self.pointing_device.hid_pending:
|
||||
keyboard._hid_helper.hid_send(self.pointing_device._evt)
|
||||
self._clear_pending_hid()
|
||||
return
|
||||
|
||||
def on_powersave_enable(self, keyboard):
|
||||
return
|
||||
|
||||
def on_powersave_disable(self, keyboard):
|
||||
return
|
||||
|
||||
def _clear_pending_hid(self):
|
||||
self.pointing_device.hid_pending = False
|
||||
self.pointing_device.report_x[0] = 0
|
||||
self.pointing_device.report_y[0] = 0
|
||||
self.pointing_device.report_w[0] = 0
|
||||
self.pointing_device.button_status[0] = 0
|
||||
|
||||
def _read_raw_state(self):
|
||||
'''Read data from AS5013'''
|
||||
x, y = self._i2c_rdwr([X], length=2)
|
||||
return x, y
|
||||
|
||||
def getSignedNumber(self, number, bitLength=8):
|
||||
mask = (2 ** bitLength) - 1
|
||||
if number & (1 << (bitLength - 1)):
|
||||
return number | ~mask
|
||||
else:
|
||||
return number & mask
|
||||
|
||||
def _i2c_rdwr(self, data, length=1):
|
||||
'''Write and optionally read I2C data.'''
|
||||
while not self._i2c_bus.try_lock():
|
||||
pass
|
||||
|
||||
try:
|
||||
if length > 0:
|
||||
result = bytearray(length)
|
||||
self._i2c_bus.writeto_then_readfrom(
|
||||
self._i2c_address, bytes(data), result
|
||||
)
|
||||
return result
|
||||
else:
|
||||
self._i2c_bus.writeto(self._i2c_address, bytes(data))
|
||||
return []
|
||||
finally:
|
||||
self._i2c_bus.unlock()
|
Loading…
x
Reference in New Issue
Block a user