diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index b70ba2f0..f7c5e963 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -30,6 +30,7 @@ target_sources(app PRIVATE src/hid.c) target_sources(app PRIVATE src/mouse/key_listener.c) target_sources(app PRIVATE src/mouse/main.c) target_sources(app PRIVATE src/mouse/tick_listener.c) +target_sources_ifdef(CONFIG_ZMK_TRACKBALL_PIM447 app PRIVATE src/mouse/trackball_pim447.c) target_sources(app PRIVATE src/sensors.c) target_sources_ifdef(CONFIG_ZMK_WPM app PRIVATE src/wpm.c) target_sources(app PRIVATE src/event_manager.c) diff --git a/app/src/mouse/trackball_pim447.c b/app/src/mouse/trackball_pim447.c new file mode 100644 index 00000000..ece68892 --- /dev/null +++ b/app/src/mouse/trackball_pim447.c @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2021 Cedric VINCENT + * + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include +#include + +LOG_MODULE_REGISTER(PIM447, CONFIG_SENSOR_LOG_LEVEL); + +/* + * It feels more natural and more confortable to convert the speed + * reported by the PIM447 trackball. + */ +static int16_t convert_speed(int32_t value) +{ + bool negative = (value < 0); + + if (negative) { + value = -value; + } + + switch (value) { + case 0: value = 0; break; + case 1: value = 1; break; + case 2: value = 4; break; + case 3: value = 8; break; + case 4: value = 18; break; + case 5: value = 32; break; + case 6: value = 50; break; + case 7: value = 72; break; + case 8: value = 98; break; + default: value = 127; break; + } + + if (negative) { + value = -value; + } + + return value; +} + +static void thread_code(void *p1, void *p2, void *p3) +{ + const struct device *dev; + int result; + + /* PIM447 trackball initialization. */ + + const char *label = DT_LABEL(DT_INST(0, pimoroni_trackball_pim447)); + dev = device_get_binding(label); + if (dev == NULL) { + LOG_ERR("Cannot get TRACKBALL_PIM447 device"); + return; + } + + /* Event loop. */ + + bool button_press_sent = false; + bool button_release_sent = false; + + while (true) { + struct sensor_value pos_dx, pos_dy, pos_dz; + bool send_report = false; + + result = sensor_sample_fetch(dev); + if (result < 0) { + LOG_ERR("Failed to fetch TRACKBALL_PIM447 sample"); + return; + } + + result = sensor_channel_get(dev, SENSOR_CHAN_POS_DX, &pos_dx); + if (result < 0) { + LOG_ERR("Failed to get TRACKBALL_PIM447 pos_dx channel value"); + return; + } + + result = sensor_channel_get(dev, SENSOR_CHAN_POS_DY, &pos_dy); + if (result < 0) { + LOG_ERR("Failed to get TRACKBALL_PIM447 pos_dy channel value"); + return; + } + + result = sensor_channel_get(dev, SENSOR_CHAN_POS_DZ, &pos_dz); + if (result < 0) { + LOG_ERR("Failed to get TRACKBALL_PIM447 pos_dz channel value"); + return; + } + + if (pos_dx.val1 != 0 || pos_dy.val1 != 0) { + zmk_hid_mouse_movement_update(convert_speed(pos_dx.val1), + convert_speed(pos_dy.val1)); + send_report = true; + } + + if (pos_dz.val1 == 0x80 && button_press_sent == false) { + zmk_hid_mouse_button_press(0); + button_press_sent = true; + button_release_sent = false; + send_report = true; + } else if (pos_dz.val1 == 0x01 && button_release_sent == false) { + zmk_hid_mouse_button_release(0); + button_press_sent = false; + button_release_sent = true; + send_report = true; + } + + if (send_report) { + zmk_endpoints_send_mouse_report(); + zmk_hid_mouse_clear(); + } + + k_sleep(K_MSEC(10)); + } +} + +#define STACK_SIZE 1024 + +static K_THREAD_STACK_DEFINE(thread_stack, STACK_SIZE); +static struct k_thread thread; + +int zmk_trackball_pim447_init() +{ + k_thread_create(&thread, thread_stack, STACK_SIZE, thread_code, + NULL, NULL, NULL, K_PRIO_PREEMPT(8), 0, K_NO_WAIT); + return 0; +} + +SYS_INIT(zmk_trackball_pim447_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);