Merge pull request #238 from kbjunky/master

added ADNS9800 and mouse keys/mouse buttons/mouse wheel support
This commit is contained in:
Josh Klar 2021-09-25 22:21:16 +00:00 committed by GitHub
commit e5d7333e2e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 447 additions and 0 deletions

18
docs/adns9800.md Normal file
View File

@ -0,0 +1,18 @@
# ADNS9800
Add this module for controlling ADNS9800 optical sensor.
```python
from kmk.modules.adns9800 import ADNS9800
keyboard.modules.append(ADNS9800(cs=board.GP0, sclk=board.GP2, miso=board.GP4, mosi=board.GP3, invert_y=True))
```
Firmware for this sensor has to be obtained separately and placed in kmk\modules\adns9800_firmware.py
```python
firmware = (
b'\x03'
b'\xa6'
...
)
```
## Constructor parameters
ADNS9800(cs=*cs_pin*, sclk=*clock_pin*, miso=*miso_pin*, mosi=*mosi_pin*, invert_x=*False*, invert_y=*False*)

20
docs/mouse_keys.md Normal file
View File

@ -0,0 +1,20 @@
# Mouse keys
To enable mouse cursor and/or mouse buttons control from the keyboard add this module to list:
```python
from kmk.modules.mouse_keys import MouseKeys
keyboard.modules.append(MouseKeys())
```
# Keycodes
|Keycode | Description |
|---------------|---------------------------|
|MB_LMB |Left mouse button |
|MB_RMB |Right mouse button |
|MB_MMB |Middle mouse button |
|MW_UP |Mouse wheel up |
|MW_DOWN, MW_DN |Mouse wheel down |
|MS_UP |Move mouse cursor up |
|MS_DOWN, MS_DN |Move mouse cursor down |
|MS_LEFT, MS_LT |Move mouse cursor left |
|MS_RIGHT, MS_RT|Move mouse cursor right |

241
kmk/modules/adns9800.py Normal file
View File

@ -0,0 +1,241 @@
import busio
import digitalio
import microcontroller
import time
from kmk.modules import Module
from kmk.modules.adns9800_firmware import firmware
from kmk.modules.mouse_keys import PointingDevice
class REG:
Product_ID = 0x0
Revision_ID = 0x1
MOTION = 0x2
DELTA_X_L = 0x3
DELTA_X_H = 0x4
DELTA_Y_L = 0x5
DELTA_Y_H = 0x6
SQUAL = 0x7
PIXEL_SUM = 0x8
Maximum_Pixel = 0x9
Minimum_Pixel = 0xA
Shutter_Lower = 0xB
Shutter_Upper = 0xC
Frame_Period_Lower = 0xD
Frame_Period_Upper = 0xE
Configuration_I = 0xF
Configuration_II = 0x10
Frame_Capture = 0x12
SROM_Enable = 0x13
Run_Downshift = 0x14
Rest1_Rate = 0x15
Rest1_Downshift = 0x16
Rest2_Rate = 0x17
Rest2_Downshift = 0x18
Rest3_Rate = 0x19
Frame_Period_Max_Bound_Lower = 0x1A
Frame_Period_Max_Bound_Upper = 0x1B
Frame_Period_Min_Bound_Lower = 0x1C
Frame_Period_Min_Bound_Upper = 0x1D
Shutter_Max_Bound_Lower = 0x1E
Shutter_Max_Bound_Upper = 0x1F
LASER_CTRL0 = 0x20
Observation = 0x24
Data_Out_Lower = 0x25
Data_Out_Upper = 0x26
SROM_ID = 0x2A
Lift_Detection_Thr = 0x2E
Configuration_V = 0x2F
Configuration_IV = 0x39
Power_Up_Reset = 0x3A
Shutdown = 0x3B
Inverse_Product_ID = 0x3F
Snap_Angle = 0x42
Motion_Burst = 0x50
SROM_Load_Burst = 0x62
Pixel_Burst = 0x64
class ADNS9800(Module):
tswr = tsww = 120
tsrw = tsrr = 20
tsrad = 100
tbexit = 1
baud = 2000000
cpol = 1
cpha = 1
DIR_WRITE = 0x80
DIR_READ = 0x7F
def __init__(self, cs, sclk, miso, mosi, invert_x=False, invert_y=False):
self.pointing_device = PointingDevice()
self.cs = digitalio.DigitalInOut(cs)
self.cs.direction = digitalio.Direction.OUTPUT
self.spi = busio.SPI(clock=sclk, MOSI=mosi, MISO=miso)
self.invert_x = invert_x
self.invert_y = invert_y
def adns_start(self):
self.cs.value = False
def adns_stop(self):
self.cs.value = True
def adns_write(self, reg, data):
while not self.spi.try_lock():
pass
try:
self.spi.configure(baudrate=self.baud, polarity=self.cpol, phase=self.cpha)
self.adns_start()
self.spi.write(bytes([reg | self.DIR_WRITE, data]))
finally:
self.spi.unlock()
self.adns_stop()
def adns_read(self, reg):
result = bytearray(1)
while not self.spi.try_lock():
pass
try:
self.spi.configure(baudrate=self.baud, polarity=self.cpol, phase=self.cpha)
self.adns_start()
self.spi.write(bytes([reg & self.DIR_READ]))
microcontroller.delay_us(self.tsrad)
self.spi.readinto(result)
finally:
self.spi.unlock()
self.adns_stop()
return result[0]
def adns_upload_srom(self):
while not self.spi.try_lock():
pass
try:
self.spi.configure(baudrate=self.baud, polarity=self.cpol, phase=self.cpha)
self.adns_start()
self.spi.write(bytes([REG.SROM_Load_Burst | self.DIR_WRITE]))
for b in firmware:
self.spi.write(bytes([b]))
finally:
self.spi.unlock()
self.adns_stop()
def delta_to_int(self, high, low):
comp = (high << 8) | low
if comp & 0x8000:
return (-1) * (0xFFFF + 1 - comp)
return comp
def adns_read_motion(self):
result = bytearray(14)
while not self.spi.try_lock():
pass
try:
self.spi.configure(baudrate=self.baud, polarity=self.cpol, phase=self.cpha)
self.adns_start()
self.spi.write(bytes([REG.Motion_Burst & self.DIR_READ]))
microcontroller.delay_us(self.tsrad)
self.spi.readinto(result)
finally:
self.spi.unlock()
self.adns_stop()
microcontroller.delay_us(self.tbexit)
self.adns_write(REG.MOTION, 0x0)
return result
def during_bootup(self, keyboard):
self.adns_write(REG.Power_Up_Reset, 0x5A)
time.sleep(0.1)
self.adns_read(REG.MOTION)
microcontroller.delay_us(self.tsrr)
self.adns_read(REG.DELTA_X_L)
microcontroller.delay_us(self.tsrr)
self.adns_read(REG.DELTA_X_H)
microcontroller.delay_us(self.tsrr)
self.adns_read(REG.DELTA_Y_L)
microcontroller.delay_us(self.tsrr)
self.adns_read(REG.DELTA_Y_H)
microcontroller.delay_us(self.tsrw)
self.adns_write(REG.Configuration_IV, 0x2)
microcontroller.delay_us(self.tsww)
self.adns_write(REG.SROM_Enable, 0x1D)
microcontroller.delay_us(1000)
self.adns_write(REG.SROM_Enable, 0x18)
microcontroller.delay_us(self.tsww)
self.adns_upload_srom()
microcontroller.delay_us(2000)
laser_ctrl0 = self.adns_read(REG.LASER_CTRL0)
microcontroller.delay_us(self.tsrw)
self.adns_write(REG.LASER_CTRL0, laser_ctrl0 & 0xF0)
microcontroller.delay_us(self.tsww)
self.adns_write(REG.Configuration_I, 0x10)
microcontroller.delay_us(self.tsww)
if keyboard.debug_enabled:
print('ADNS: Product ID ', hex(self.adns_read(REG.Product_ID)))
microcontroller.delay_us(self.tsrr)
print('ADNS: Revision ID ', hex(self.adns_read(REG.Revision_ID)))
microcontroller.delay_us(self.tsrr)
print('ADNS: SROM ID ', hex(self.adns_read(REG.SROM_ID)))
microcontroller.delay_us(self.tsrr)
if self.adns_read(REG.Observation) & 0x20:
print('ADNS: Sensor is running SROM')
else:
print('ADNS: Error! Sensor is not runnin SROM!')
return
def before_matrix_scan(self, keyboard):
motion = self.adns_read_motion()
if motion[0] & 0x80:
delta_x = self.delta_to_int(motion[3], motion[2])
delta_y = self.delta_to_int(motion[5], motion[4])
if self.invert_x:
delta_x *= -1
if self.invert_y:
delta_y *= -1
if delta_x < 0:
self.pointing_device.report_x[0] = (delta_x & 0xFF) | 0x80
else:
self.pointing_device.report_x[0] = delta_x & 0xFF
if delta_y < 0:
self.pointing_device.report_y[0] = (delta_y & 0xFF) | 0x80
else:
self.pointing_device.report_y[0] = delta_y & 0xFF
if keyboard.debug_enabled:
print('Delta: ', delta_x, ' ', delta_y)
self.pointing_device.hid_pending = True
if self.pointing_device.hid_pending:
keyboard._hid_helper.hid_send(self.pointing_device._evt)
self.pointing_device.hid_pending = False
self.pointing_device.report_x[0] = 0
self.pointing_device.report_y[0] = 0
return
def after_matrix_scan(self, keyboard):
return
def before_hid_send(self, keyboard):
return
def after_hid_send(self, keyboard):
return
def on_powersave_enable(self, keyboard):
return
def on_powersave_disable(self, keyboard):
return

168
kmk/modules/mouse_keys.py Normal file
View File

@ -0,0 +1,168 @@
from kmk.hid import HID_REPORT_SIZES, HIDReportTypes
from kmk.keys import make_key
from kmk.modules import Module
class PointingDevice:
MB_LMB = 1
MB_RMB = 2
MB_MMB = 4
_evt = bytearray(HID_REPORT_SIZES[HIDReportTypes.MOUSE] + 1)
def __init__(self):
self.hid_pending = False
self.report_device = memoryview(self._evt)[0:1]
self.report_device[0] = HIDReportTypes.MOUSE
self.button_status = memoryview(self._evt)[1:2]
self.report_x = memoryview(self._evt)[2:3]
self.report_y = memoryview(self._evt)[3:4]
self.report_w = memoryview(self._evt)[4:]
class MouseKeys(Module):
def __init__(self):
self.move_step = 1
self.pointing_device = PointingDevice()
make_key(
names=('MB_LMB',),
on_press=self._mb_lmb_press,
on_release=self._mb_lmb_release,
)
make_key(
names=('MB_MMB',),
on_press=self._mb_mmb_press,
on_release=self._mb_mmb_release,
)
make_key(
names=('MB_RMB',),
on_press=self._mb_rmb_press,
on_release=self._mb_rmb_release,
)
make_key(
names=('MW_UP',), on_press=self._mw_up_press, on_release=self._mw_up_release
)
make_key(
names=(
'MW_DOWN',
'MW_DN',
),
on_press=self._mw_down_press,
on_release=self._mw_down_release,
)
make_key(
names=('MS_UP',), on_press=self._ms_up_press, on_release=self._ms_y_release
)
make_key(
names=(
'MS_DOWN',
'MS_DN',
),
on_press=self._ms_down_press,
on_release=self._ms_y_release,
)
make_key(
names=(
'MS_LEFT',
'MS_LT',
),
on_press=self._ms_left_press,
on_release=self._ms_x_release,
)
make_key(
names=(
'MS_RIGHT',
'MS_RT',
),
on_press=self._ms_right_press,
on_release=self._ms_x_release,
)
def during_bootup(self, keyboard):
return
def before_matrix_scan(self, keyboard):
return
def after_matrix_scan(self, keyboard):
return
def before_hid_send(self, keyboard):
if self.pointing_device.hid_pending:
keyboard._hid_helper.hid_send(self.pointing_device._evt)
return
def after_hid_send(self, keyboard):
return
def on_powersave_enable(self, keyboard):
return
def on_powersave_disable(self, keyboard):
return
def _mb_lmb_press(self, key, keyboard, *args, **kwargs):
self.pointing_device.button_status[0] |= self.pointing_device.MB_LMB
self.pointing_device.hid_pending = True
def _mb_lmb_release(self, key, keyboard, *args, **kwargs):
self.pointing_device.button_status[0] &= ~self.pointing_device.MB_LMB
self.pointing_device.hid_pending = True
def _mb_mmb_press(self, key, keyboard, *args, **kwargs):
self.pointing_device.button_status[0] |= self.pointing_device.MB_MMB
self.pointing_device.hid_pending = True
def _mb_mmb_release(self, key, keyboard, *args, **kwargs):
self.pointing_device.button_status[0] &= ~self.pointing_device.MB_MMB
self.pointing_device.hid_pending = True
def _mb_rmb_press(self, key, keyboard, *args, **kwargs):
self.pointing_device.button_status[0] |= self.pointing_device.MB_RMB
self.pointing_device.hid_pending = True
def _mb_rmb_release(self, key, keyboard, *args, **kwargs):
self.pointing_device.button_status[0] &= ~self.pointing_device.MB_RMB
self.pointing_device.hid_pending = True
def _mw_up_press(self, key, keyboard, *args, **kwargs):
self.pointing_device.report_w[0] = self.move_step
self.pointing_device.hid_pending = True
def _mw_up_release(self, key, keyboard, *args, **kwargs):
self.pointing_device.report_w[0] = 0
self.pointing_device.hid_pending = True
def _mw_down_press(self, key, keyboard, *args, **kwargs):
self.pointing_device.report_w[0] = 0xFF
self.pointing_device.hid_pending = True
def _mw_down_release(self, key, keyboard, *args, **kwargs):
self.pointing_device.report_w[0] = 0
self.pointing_device.hid_pending = True
# Mouse movement
def _ms_up_press(self, key, keyboard, *args, **kwargs):
self.pointing_device.report_y[0] = 0xFF & (0 - self.move_step)
self.pointing_device.hid_pending = True
def _ms_down_press(self, key, keyboard, *args, **kwargs):
self.pointing_device.report_y[0] = self.move_step
self.pointing_device.hid_pending = True
def _ms_y_release(self, key, keyboard, *args, **kwargs):
self.pointing_device.report_y[0] = 0
self.pointing_device.hid_pending = False
def _ms_left_press(self, key, keyboard, *args, **kwargs):
self.pointing_device.report_x[0] = 0xFF & (0 - self.move_step)
self.pointing_device.hid_pending = True
def _ms_right_press(self, key, keyboard, *args, **kwargs):
self.pointing_device.report_x[0] = self.move_step
self.pointing_device.hid_pending = True
def _ms_x_release(self, key, keyboard, *args, **kwargs):
self.pointing_device.report_x[0] = 0
self.pointing_device.hid_pending = False