diff --git a/docs/adns9800.md b/docs/adns9800.md new file mode 100644 index 0000000..0f07184 --- /dev/null +++ b/docs/adns9800.md @@ -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*) \ No newline at end of file diff --git a/docs/mouse_keys.md b/docs/mouse_keys.md new file mode 100644 index 0000000..184b299 --- /dev/null +++ b/docs/mouse_keys.md @@ -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 | \ No newline at end of file diff --git a/kmk/modules/adns9800.py b/kmk/modules/adns9800.py new file mode 100644 index 0000000..00981c2 --- /dev/null +++ b/kmk/modules/adns9800.py @@ -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 diff --git a/kmk/modules/mouse_keys.py b/kmk/modules/mouse_keys.py new file mode 100644 index 0000000..8cd6cef --- /dev/null +++ b/kmk/modules/mouse_keys.py @@ -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