From 2cac694fa2361d00957f40dec2162b8a1474ceec Mon Sep 17 00:00:00 2001 From: Kostas Karachalios Date: Thu, 3 Feb 2022 19:00:03 +0100 Subject: [PATCH 001/157] feat(behaviors): Allow mod-morph to swallow mods Revert "fix(hid): Implicit mods on non-key page events" This reverts commit 6ef1e7034ffaed14378e6194152269ed9ed5bdd1. masked mods Unrevert "fix(hid): Implicit mods on non-key page events" Fix docs Lint code with clang-format --- .../behaviors/zmk,behavior-mod-morph.yaml | 3 +++ app/include/zmk/hid.h | 2 ++ app/src/behaviors/behavior_mod_morph.c | 6 +++++ app/src/hid.c | 24 ++++++++++++++++--- docs/docs/behaviors/mod-morph.md | 5 ++++ 5 files changed, 37 insertions(+), 3 deletions(-) diff --git a/app/dts/bindings/behaviors/zmk,behavior-mod-morph.yaml b/app/dts/bindings/behaviors/zmk,behavior-mod-morph.yaml index 66b452a5f99..70982764fa6 100644 --- a/app/dts/bindings/behaviors/zmk,behavior-mod-morph.yaml +++ b/app/dts/bindings/behaviors/zmk,behavior-mod-morph.yaml @@ -14,3 +14,6 @@ properties: mods: type: int required: true + masked_mods: + type: int + required: false diff --git a/app/include/zmk/hid.h b/app/include/zmk/hid.h index e23caff99d0..42b9e88fec8 100644 --- a/app/include/zmk/hid.h +++ b/app/include/zmk/hid.h @@ -229,6 +229,8 @@ int zmk_hid_register_mods(zmk_mod_flags_t explicit_modifiers); int zmk_hid_unregister_mods(zmk_mod_flags_t explicit_modifiers); int zmk_hid_implicit_modifiers_press(zmk_mod_flags_t implicit_modifiers); int zmk_hid_implicit_modifiers_release(); +int zmk_hid_masked_modifiers_set(zmk_mod_flags_t masked_modifiers); +int zmk_hid_masked_modifiers_clear(); int zmk_hid_keyboard_press(zmk_key_t key); int zmk_hid_keyboard_release(zmk_key_t key); void zmk_hid_keyboard_clear(); diff --git a/app/src/behaviors/behavior_mod_morph.c b/app/src/behaviors/behavior_mod_morph.c index 9d6eac17cdc..162a8546c7f 100644 --- a/app/src/behaviors/behavior_mod_morph.c +++ b/app/src/behaviors/behavior_mod_morph.c @@ -27,6 +27,7 @@ struct behavior_mod_morph_config { struct zmk_behavior_binding normal_binding; struct zmk_behavior_binding morph_binding; zmk_mod_flags_t mods; + zmk_mod_flags_t masked_mods; }; struct behavior_mod_morph_data { @@ -45,6 +46,8 @@ static int on_mod_morph_binding_pressed(struct zmk_behavior_binding *binding, } if (zmk_hid_get_explicit_mods() & cfg->mods) { + zmk_mod_flags_t trigger_mods = zmk_hid_get_explicit_mods() & cfg->mods; + zmk_hid_masked_modifiers_set(cfg->masked_mods); data->pressed_binding = (struct zmk_behavior_binding *)&cfg->morph_binding; } else { data->pressed_binding = (struct zmk_behavior_binding *)&cfg->normal_binding; @@ -64,6 +67,7 @@ static int on_mod_morph_binding_released(struct zmk_behavior_binding *binding, struct zmk_behavior_binding *pressed_binding = data->pressed_binding; data->pressed_binding = NULL; + zmk_hid_masked_modifiers_clear(); return behavior_keymap_binding_released(pressed_binding, event); } @@ -88,6 +92,8 @@ static int behavior_mod_morph_init(const struct device *dev) { return 0; } .normal_binding = _TRANSFORM_ENTRY(0, n), \ .morph_binding = _TRANSFORM_ENTRY(1, n), \ .mods = DT_INST_PROP(n, mods), \ + .masked_mods = COND_CODE_0(DT_INST_NODE_HAS_PROP(n, masked_mods), (0), \ + (DT_INST_PROP(n, masked_mods))), \ }; \ static struct behavior_mod_morph_data behavior_mod_morph_data_##n = {}; \ DEVICE_DT_INST_DEFINE(n, behavior_mod_morph_init, device_pm_control_nop, \ diff --git a/app/src/hid.c b/app/src/hid.c index d6c63e1dd08..86d13e45615 100644 --- a/app/src/hid.c +++ b/app/src/hid.c @@ -19,10 +19,12 @@ static struct zmk_hid_consumer_report consumer_report = {.report_id = 2, .body = // Only release the modifier if the count is 0. static int explicit_modifier_counts[8] = {0, 0, 0, 0, 0, 0, 0, 0}; static zmk_mod_flags_t explicit_modifiers = 0; +static zmk_mod_flags_t implicit_modifiers = 0; +static zmk_mod_flags_t masked_modifiers = 0; #define SET_MODIFIERS(mods) \ { \ - keyboard_report.body.modifiers = mods; \ + keyboard_report.body.modifiers = (mods | implicit_modifiers) & ~masked_modifiers; \ LOG_DBG("Modifiers set to 0x%02X", keyboard_report.body.modifiers); \ } @@ -136,13 +138,29 @@ static inline int deselect_keyboard_usage(zmk_key_t usage) { } \ } -int zmk_hid_implicit_modifiers_press(zmk_mod_flags_t implicit_modifiers) { +int zmk_hid_implicit_modifiers_press(zmk_mod_flags_t new_implicit_modifiers) { + implicit_modifiers = new_implicit_modifiers; zmk_mod_flags_t current = GET_MODIFIERS; - SET_MODIFIERS(explicit_modifiers | implicit_modifiers); + SET_MODIFIERS(explicit_modifiers); return current == GET_MODIFIERS ? 0 : 1; } int zmk_hid_implicit_modifiers_release() { + implicit_modifiers = 0; + zmk_mod_flags_t current = GET_MODIFIERS; + SET_MODIFIERS(explicit_modifiers); + return current == GET_MODIFIERS ? 0 : 1; +} + +int zmk_hid_masked_modifiers_set(zmk_mod_flags_t new_masked_modifiers) { + masked_modifiers = new_masked_modifiers; + zmk_mod_flags_t current = GET_MODIFIERS; + SET_MODIFIERS(explicit_modifiers); + return current == GET_MODIFIERS ? 0 : 1; +} + +int zmk_hid_masked_modifiers_clear() { + masked_modifiers = 0; zmk_mod_flags_t current = GET_MODIFIERS; SET_MODIFIERS(explicit_modifiers); return current == GET_MODIFIERS ? 0 : 1; diff --git a/docs/docs/behaviors/mod-morph.md b/docs/docs/behaviors/mod-morph.md index 2606aaf8bf9..a8b7150cf16 100644 --- a/docs/docs/behaviors/mod-morph.md +++ b/docs/docs/behaviors/mod-morph.md @@ -16,6 +16,10 @@ The Mod-Morph behavior acts as one of two keycodes, depending on if the required When the modifier is being held it is sent along with the morphed keycode. This can cause problems when the morphed keycode and modifier have an existing relationship (such as `shift-delete` or `ctrl-v` on many operating systems). +As a remedy, you can add the optional attribute `masked_mods`, containing +the bitwise OR of the modifiers that should be disabled while the key is held, +so that they are not included in the report sent to the host. + ### Configuration An example of how to implement the mod-morph "Grave Escape": @@ -29,6 +33,7 @@ An example of how to implement the mod-morph "Grave Escape": #binding-cells = <0>; bindings = <&kp ESC>, <&kp GRAVE>; mods = <(MOD_LGUI|MOD_LSFT|MOD_RGUI|MOD_RSFT)>; + masked_mods = <(MOD_LGUI|MOD_LSFT)>; // don't send left modifiers }; }; From 04b6c96e1059c72aeb8938e6c9a7db40af33864b Mon Sep 17 00:00:00 2001 From: "Dylan (Luberry) Kozicki" Date: Mon, 7 Mar 2022 20:31:13 -0500 Subject: [PATCH 002/157] initial untested implementation of the pmw3389 driver --- app/drivers/sensor/CMakeLists.txt | 3 +- app/drivers/sensor/pmw3389/CMakeLists.txt | 9 + app/drivers/sensor/pmw3389/pmw3389.c | 290 +++++++++++++++++++ app/drivers/sensor/pmw3389/pmw3389.h | 92 ++++++ app/drivers/sensor/pmw3389/pmw3389_trigger.c | 132 +++++++++ 5 files changed, 525 insertions(+), 1 deletion(-) create mode 100644 app/drivers/sensor/pmw3389/CMakeLists.txt create mode 100644 app/drivers/sensor/pmw3389/pmw3389.c create mode 100644 app/drivers/sensor/pmw3389/pmw3389.h create mode 100644 app/drivers/sensor/pmw3389/pmw3389_trigger.c diff --git a/app/drivers/sensor/CMakeLists.txt b/app/drivers/sensor/CMakeLists.txt index b549320f121..5d061280bbe 100644 --- a/app/drivers/sensor/CMakeLists.txt +++ b/app/drivers/sensor/CMakeLists.txt @@ -2,4 +2,5 @@ # SPDX-License-Identifier: MIT add_subdirectory_ifdef(CONFIG_ZMK_BATTERY battery) -add_subdirectory_ifdef(CONFIG_EC11 ec11) \ No newline at end of file +add_subdirectory_ifdef(CONFIG_EC11 ec11) +add_subdirectory_ifdef(CONFIG_PMW3389 pmw3389) \ No newline at end of file diff --git a/app/drivers/sensor/pmw3389/CMakeLists.txt b/app/drivers/sensor/pmw3389/CMakeLists.txt new file mode 100644 index 00000000000..0db5bd48679 --- /dev/null +++ b/app/drivers/sensor/pmw3389/CMakeLists.txt @@ -0,0 +1,9 @@ +# Copyright (c) 2020 The ZMK Contributors +# SPDX-License-Identifier: MIT + +zephyr_include_directories(.) + +zephyr_library() + +zephyr_library_sources(pmw3389.c) +zephyr_library_sources_ifdef(CONFIG_PMW3389_TRIGGER pmw3389_trigger.c) \ No newline at end of file diff --git a/app/drivers/sensor/pmw3389/pmw3389.c b/app/drivers/sensor/pmw3389/pmw3389.c new file mode 100644 index 00000000000..32bcce636b4 --- /dev/null +++ b/app/drivers/sensor/pmw3389/pmw3389.c @@ -0,0 +1,290 @@ + +#define DT_DRV_COMPAT pixart_pmw3389 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pmw3389.h" + +LOG_MODULE_REGISTER(PMW3389, CONFIG_SENSOR_LOG_LEVEL); + +static int pmw3389_access(const struct device *dev, + uint8_t reg, uint8_t *value) +{ + struct pmw3389_data *data = dev->data; + const struct pmw3389_config *cfg = dev->config; + const struct spi_config *spi_cfg = &cfg->bus_cfg.spi_cfg->spi_conf; + uint8_t access[1] = {reg}; + struct spi_buf bufs[] = { + { + .buf = access, + .len = 1, + }, + { + .buf = value, + .len = 1, + }, + }; + struct spi_buf_set tx = { + .buffers = bufs, + .count = 2, + }; + struct spi_buf_set *rx; + if (reg & PMW3389_WR_MASK > 0) + { + rx = &(struct spi_buf_set){ + .buffers = bufs, + .count = 2, + }; + } + return spi_transceive(data->bus, spi_cfg, &tx, rx); +} +static int pmw3389_read_reg(const struct device *dev, const uint8_t reg, uint8_t *value) +{ + return pmw3389_access(dev, reg, value); +} +static int pmw3389_write_reg(const struct device *dev, const uint8_t reg, const uint8_t value) +{ + return pmw3389_access(dev, reg & PMW3389_WR_MASK, &value); +} + +// converts twos complement data to an int16 +static int16_t pmw3389_raw_to_int16(const uint8_t src[2]) +{ + int16_t res = sys_get_be16(src[2]); + if (res > BIT_MASK(15)) + res -= BIT(16); + return res; +} + +static int pmw3389_read_raw(const struct device *dev, const uint8_t reg_high, const uint8_t reg_low, int16_t *value) +{ + uint8_t raw[2] = {0x0, 0x0}; + int err; + err = pmw3389_read_reg(dev, reg_high, &raw[0]); + if (err) + { + LOG_ERR("could not read high byte at %x", reg_high); + return err; + } + err = pmw3389_read_reg(dev, reg_low, &raw[1]); + if (err) + { + LOG_ERR("could not read low byte at %x", reg_low); + return err; + } + *value = pmw3389_raw_to_int16(raw); + return 0; +} + +static bool pmw3389_spi_check_id(const struct device *dev) +{ + int err; + uint8_t val; + err = pmw3389_read_reg(dev, &val, PMW3389_REG_PID); + if (err) + { + LOG_ERR("could not read PID"); + return false; + } + if (val != PMW3389_PID) + { + LOG_ERR("invalid PID"); + return false; + } + err = pmw3389_read_reg(dev, &val, PMW3389_REG_REV); + if (err) + { + LOG_ERR("could not read REV"); + return false; + } + if (val != PMW3389_REV) + { + LOG_ERR("invalid REV"); + return false; + } + return true; +} + +int pmw3389_spi_init(const struct device *dev) +{ + struct pmw3389_data *data = dev->data; + const struct pmw3389_config *cfg = dev->config; + const struct pmw3389_spi_cfg *spi_cfg = cfg->bus_cfg.spi_cfg; + + if (spi_cfg->cs_gpios_label != NULL) + { + + /* handle SPI CS thru GPIO if it is the case */ + data->cs_ctrl.gpio_dev = device_get_binding(spi_cfg->cs_gpios_label); + if (!data->cs_ctrl.gpio_dev) + { + LOG_ERR("Unable to get GPIO SPI CS device"); + return -ENODEV; + } + } + + return 0; +} + +static int pmw3389_sample_fetch(const struct device *dev, enum sensor_channel chan) +{ + struct pmw3389_data *data = dev->data; + const struct pmw3389_config *cfg = dev->config; + uint16_t dx = 0, dy = 0; + + if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_POS_DX && + chan != SENSOR_CHAN_POS_DY) + return -ENOTSUP; + if (!pmw3389_spi_check_id(dev)) + return -EINVAL; + int err; + if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_POS_DX) + { + err = pmw3389_read_raw(dev, PMW3389_REG_DX_H, PMW3389_REG_DX_L, &dx); + if (err) + { + LOG_DBG("could not read x motion"); + return -EIO; + } + } + if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_POS_DY) + { + err = pmw3389_read_raw(dev, PMW3389_REG_DY_H, PMW3389_REG_DY_L, &dy); + if (err) + { + LOG_DBG("could not read y motion"); + return -EIO; + } + } + data->dx = dx; + data->dy = dy; + return 0; +} + +static int pmw3389_channel_get(const struct device *dev, enum sensor_channel chan, + struct sensor_value *val) +{ + struct pmw3389_data *data = dev->data; + const struct pmw3389_config *cfg = dev->config; + + switch (chan) + { + case SENSOR_CHAN_POS_DX: + return data->dx; + case SENSOR_CHAN_POS_DY: + return data->dy; + default: + return -ENOTSUP; + } + return 0; +} + +static const struct sensor_driver_api pmw3389_driver_api = { +#ifdef CONFIG_PMW3389_TRIGGER + .trigger_set = pmw3389_trigger_set, +#endif + // .attr_set = pmw3389_attr_set, + .sample_fetch = pmw3389_sample_fetch, + .channel_get = pmw3389_channel_get, +}; + +static int pmw3389_init_chip(const struct device *dev) +{ + struct pmw3389_data *data = dev->data; + return 0; +} + +static int pmw3389_init(const struct device *dev) +{ + const struct pmw3389_config *const config = dev->config; + struct pmw3389_data *data = dev->data; + + data->bus = device_get_binding(config->bus_name); + if (!data->bus) + { + LOG_DBG("master not found: %s", log_strdup(config->bus_name)); + return -EINVAL; + } + + config->bus_init(dev); + + if (pmw3389_init_chip(dev) < 0) + { + LOG_DBG("failed to initialize chip"); + return -EIO; + } + +#ifdef CONFIG_PMW3389_TRIGGER + if (pmw3389_init_interrupt(dev) < 0) + { + LOG_DBG("Failed to initialize interrupt!"); + return -EIO; + } +#endif + + return 0; +} + +#define PMW3389_HAS_CS(n) DT_INST_SPI_DEV_HAS_CS_GPIOS(n) + +#define PMW3389_DATA_SPI_CS(n) \ + { \ + .cs_ctrl = { \ + .gpio_pin = DT_INST_SPI_DEV_CS_GPIOS_PIN(n), \ + .gpio_dt_flags = DT_INST_SPI_DEV_CS_GPIOS_FLAGS(n), \ + }, \ + } + +#define PMW3389_DATA_SPI(n) COND_CODE_1(PMW3389_HAS_CS(n), (PMW3389_DATA_SPI_CS(n)), ({})) + +#define PMW3389_SPI_CS_PTR(n) COND_CODE_1(PMW3389_HAS_CS(n), (&(pmw3389_data_##n.cs_ctrl)), (NULL)) + +#define PMW3389_SPI_CS_LABEL(n) \ + COND_CODE_1(PMW3389_HAS_CS(n), (DT_INST_SPI_DEV_CS_GPIOS_LABEL(n)), (NULL)) + +#define PMW3389_SPI_CFG(n) \ + (&(struct pmw3389_spi_cfg){ \ + .spi_conf = \ + { \ + .frequency = DT_INST_PROP(n, spi_max_frequency), \ + .operation = \ + (SPI_WORD_SET(8) | SPI_OP_MODE_MASTER | SPI_MODE_CPOL | SPI_MODE_CPHA), \ + .slave = DT_INST_REG_ADDR(n), \ + .cs = PMW3389_SPI_CS_PTR(n), \ + }, \ + .cs_gpios_label = PMW3389_SPI_CS_LABEL(n), \ + }) + +#define PMW3389_GPIO_DT_SPEC_GET_BY_IDX(node_id, prop, idx) \ + { \ + .port = DEVICE_DT_GET(DT_GPIO_CTLR_BY_IDX(node_id, prop, idx)), \ + .pin = DT_GPIO_PIN_BY_IDX(node_id, prop, idx), \ + .dt_flags = DT_GPIO_FLAGS_BY_IDX(node_id, prop, idx), \ + } + +#define PMW3389_CONFIG_SPI(n) \ + { \ + .bus_name = DT_INST_BUS_LABEL(n), .bus_init = pmw3389_spi_init, \ + .bus_cfg = {.spi_cfg = PMW3389_SPI_CFG(n)}, \ + COND_CODE_0(DT_INST_NODE_HAS_PROP(n, resolution), (1), (DT_INST_PROP(n, resolution))) \ + COND_CODE_1(CONFIG_PMW3389_TRIGGER, \ + (, PMW3389_GPIO_DT_SPEC_GET_BY_IDX(DT_DRV_INST(n), motswk_gpios, 0), ), \ + ()) \ + } + +#define PMW3389_INST(n) \ + static struct pmw3389_data pmw3389_data_##n = PMW3389_DATA_SPI(n); \ + static const struct pmw3389_config pmw3389_cfg_##n = PMW3389_CONFIG_SPI(n); \ + DEVICE_DT_INST_DEFINE(n, pmw3389_init, device_pm_control_nop, &pmw3389_data_##n, &pmw3389_cfg_##n, \ + POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, &pmw3389_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(PMW3389_INST) \ No newline at end of file diff --git a/app/drivers/sensor/pmw3389/pmw3389.h b/app/drivers/sensor/pmw3389/pmw3389.h new file mode 100644 index 00000000000..ee000f3058f --- /dev/null +++ b/app/drivers/sensor/pmw3389/pmw3389.h @@ -0,0 +1,92 @@ +#ifndef ZEPHYR_DRIVERS_SENSOR_PIXART_PMW3389_H_ +#define ZEPHYR_DRIVERS_SENSOR_PIXART_PMW3389_H_ + +#include +#include +#include +#include +#include + +#define PMW3389_WR_MASK 0x80 + +#define PMW3389_PID 0x47 +#define PMW3389_REV 0x01 + +#define PMW3389_REG_PID 0x00 +#define PMW3389_REG_REV 0x01 + +#define PMW3389_REG_DX_L 0x2A +#define PMW3389_REG_DX_H 0x2B +#define PMW3389_REG_DY_L 0x2C +#define PMW3389_REG_DY_H 0x2D + +struct pmw3389_gpio_dt_spec { + const struct device *port; + gpio_pin_t pin; + gpio_dt_flags_t dt_flags; +}; + +struct pmw3389_spi_cfg { + struct spi_config spi_conf; + const char *cs_gpios_label; +}; + +union pmw3389_bus_cfg { + struct pmw3389_spi_cfg *spi_cfg; +}; + +struct pmw3389_config { + char *bus_name; + int (*bus_init)(const struct device *dev); + const union pmw3389_bus_cfg bus_cfg; + int resolution; + struct pmw3389_gpio_dt_spec reset; +#if CONFIG_PMW3389_TRIGGER + struct pmw3389_gpio_dt_spec motswk; +#endif // CONFIG_PMW3389_TRIGGER +}; + +struct pmw3389_data; + +struct pmw3389_transfer_function { + int (*read_data)(const struct device *dev, uint16_t *value); +}; + +struct pmw3389_data { + const struct device *bus; + struct spi_cs_control cs_ctrl; + + uint16_t dx; + uint16_t dy; + + const struct pmw3389_transfer_function *hw_tf; + +#ifdef CONFIG_PMW3389_TRIGGER + + struct gpio_callback motswk_gpio_cb; + const struct device *dev; + + sensor_trigger_handler_t handler; + const struct sensor_trigger *trigger; + +#if defined(CONFIG_PMW3389_TRIGGER_OWN_THREAD) + K_THREAD_STACK_MEMBER(thread_stack, CONFIG_PMW3389_THREAD_STACK_SIZE); + struct k_sem gpio_sem; + struct k_thread thread; +#elif defined(CONFIG_PMW3389_TRIGGER_GLOBAL_THREAD) + struct k_work work; +#endif + +#endif /* CONFIG_PMW3389_TRIGGER */ +}; + +int pmw3389_spi_init(const struct device *dev); +#ifdef CONFIG_PMW3389_TRIGGER + +int pmw3389_trigger_set(const struct device *dev, const struct sensor_trigger *trig, + sensor_trigger_handler_t handler); + +int pmw3389_init_interrupt(const struct device *dev); +#endif + +#endif /* ZEPHYR_DRIVERS_SENSOR_PIXART_PMW3389_H_ */ \ No newline at end of file diff --git a/app/drivers/sensor/pmw3389/pmw3389_trigger.c b/app/drivers/sensor/pmw3389/pmw3389_trigger.c new file mode 100644 index 00000000000..1ccd74f4347 --- /dev/null +++ b/app/drivers/sensor/pmw3389/pmw3389_trigger.c @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#define DT_DRV_COMPAT pixart_pmw3389 + +#include +#include +#include +#include +#include + +#include "pmw3389.h" + +// extern struct pmw3389_data pmw3389_driver; + +#include +LOG_MODULE_DECLARE(PMW3389, CONFIG_SENSOR_LOG_LEVEL); + +static inline void setup_int(const struct device *dev, bool enable) +{ + struct pmw3389_data *data = dev->data; + const struct pmw3389_config *cfg = dev->config; + + if (gpio_pin_interrupt_configure(cfg->motswk.port, cfg->motswk.pin, + enable ? GPIO_INT_EDGE_TO_ACTIVE : GPIO_INT_DISABLE)) + { + LOG_WRN("Unable to set MOTSWK GPIO interrupt"); + } +} + +static void pmw3389_motswk_gpio_callback(const struct device *dev, struct gpio_callback *cb, + uint32_t pins) +{ + struct pmw3389_data *drv_data = CONTAINER_OF(cb, struct pmw3389_data, motswk_gpio_cb); + + LOG_DBG(""); + + setup_int(drv_data->dev, false); + +#if defined(CONFIG_PMW3389_TRIGGER_OWN_THREAD) + k_sem_give(&drv_data->gpio_sem); +#elif defined(CONFIG_PMW3389_TRIGGER_GLOBAL_THREAD) + k_work_submit(&drv_data->work); +#endif +} + +static void pmw3389_thread_cb(const struct device *dev) +{ + struct pmw3389_data *drv_data = dev->data; + + LOG_DBG("%p", drv_data->handler); + drv_data->handler(dev, drv_data->trigger); + + // Enable once the wall/spam of interrupts is solved + setup_int(dev, true); +} + +#ifdef CONFIG_PMW3389_TRIGGER_OWN_THREAD +static void pmw3389_thread(int dev_ptr, int unused) +{ + const struct device *dev = INT_TO_POINTER(dev_ptr); + struct pmw3389_data *drv_data = dev->data; + + ARG_UNUSED(unused); + + while (1) + { + k_sem_take(&drv_data->gpio_sem, K_FOREVER); + pmw3389_thread_cb(dev); + } +} +#endif + +#ifdef CONFIG_PMW3389_TRIGGER_GLOBAL_THREAD +static void pmw3389_work_cb(struct k_work *work) +{ + struct pmw3389_data *drv_data = CONTAINER_OF(work, struct pmw3389_data, work); + + LOG_DBG(""); + + pmw3389_thread_cb(drv_data->dev); +} +#endif + +int pmw3389_trigger_set(const struct device *dev, const struct sensor_trigger *trig, + sensor_trigger_handler_t handler) +{ + struct pmw3389_data *drv_data = dev->data; + + setup_int(dev, false); + + k_msleep(5); + + drv_data->trigger = trig; + drv_data->handler = handler; + + setup_int(dev, true); + + return 0; +} + +int pmw3389_init_interrupt(const struct device *dev) +{ + struct pmw3389_data *drv_data = dev->data; + const struct pmw3389_config *drv_cfg = dev->config; + + drv_data->dev = dev; + /* setup gpio interrupt */ + + gpio_init_callback(&drv_data->motswk_gpio_cb, pmw3389_motswk_gpio_callback, BIT(drv_cfg->motswk_spec.pin)); + + if (gpio_add_callback(drv_cfg->motswk_spec.port, &drv_data->motswk_gpio_cb) < 0) + { + LOG_DBG("Failed to set MOTSWK callback!"); + return -EIO; + } + +#if defined(CONFIG_PMW3389_TRIGGER_OWN_THREAD) + k_sem_init(&drv_data->gpio_sem, 0, UINT_MAX); + + k_thread_create(&drv_data->thread, drv_data->thread_stack, CONFIG_PMW3389_THREAD_STACK_SIZE, + (k_thread_entry_t)pmw3389_thread, dev, 0, NULL, + K_PRIO_COOP(CONFIG_PMW3389_THREAD_PRIORITY), 0, K_NO_WAIT); +#elif defined(CONFIG_PMW3389_TRIGGER_GLOBAL_THREAD) + k_work_init(&drv_data->work, pmw3389_work_cb); +#endif + + return 0; +} \ No newline at end of file From f6af5a531f4b9995a6fd1ecd3829a89840d923c0 Mon Sep 17 00:00:00 2001 From: "Dylan (Luberry) Kozicki" Date: Sun, 13 Mar 2022 12:53:23 -0400 Subject: [PATCH 003/157] add kconfig and initial dts binding files --- app/drivers/sensor/Kconfig | 3 +- app/drivers/sensor/pmw3389/Kconfig | 49 ++++++ app/drivers/sensor/pmw3389/pmw3389.c | 164 +++++++----------- app/drivers/sensor/pmw3389/pmw3389.h | 4 +- app/drivers/sensor/pmw3389/pmw3389_trigger.c | 35 ++-- .../dts/bindings/sensor/pixart,pmw3389.yaml | 22 +++ 6 files changed, 155 insertions(+), 122 deletions(-) create mode 100644 app/drivers/sensor/pmw3389/Kconfig create mode 100644 app/drivers/zephyr/dts/bindings/sensor/pixart,pmw3389.yaml diff --git a/app/drivers/sensor/Kconfig b/app/drivers/sensor/Kconfig index a828f6c63ab..d47c7fea63f 100644 --- a/app/drivers/sensor/Kconfig +++ b/app/drivers/sensor/Kconfig @@ -2,4 +2,5 @@ # SPDX-License-Identifier: MIT rsource "battery/Kconfig" -rsource "ec11/Kconfig" \ No newline at end of file +rsource "ec11/Kconfig" +rsource "pmw3389/Kconfig" \ No newline at end of file diff --git a/app/drivers/sensor/pmw3389/Kconfig b/app/drivers/sensor/pmw3389/Kconfig new file mode 100644 index 00000000000..219d80577fb --- /dev/null +++ b/app/drivers/sensor/pmw3389/Kconfig @@ -0,0 +1,49 @@ +config PMW3389 + bool "PMW3389 Mouse Sensor" + depends on SPI + help + Enable mouse sensor + + The PMW3389 is a 16-bit optical mouse sensor + +if PMW3389 + +choice + prompt "Trigger mode" + default PMW3389_TRIGGER_NONE + help + Specify the type of triggering to be used by the driver. + +config PMW3389_TRIGGER_NONE + bool "No trigger" + +config PMW3389_TRIGGER_GLOBAL_THREAD + bool "Use global thread" + depends on GPIO + select PMW3389_TRIGGER + +config PMW3389_TRIGGER_OWN_THREAD + bool "Use own thread" + depends on GPIO + select PMW3389_TRIGGER + +endchoice + +config PMW3389_TRIGGER + bool + +config PMW3389_THREAD_PRIORITY + int "Thread priority" + depends on PMW3389_TRIGGER_OWN_THREAD + default 10 + help + Priority of thread used by the driver to handle interrupts. + +config PMW3389_THREAD_STACK_SIZE + int "Thread stack size" + depends on PMW3389_TRIGGER_OWN_THREAD + default 1024 + help + Stack size of thread used by the driver to handle interrupts. + +endif # PMW3389 \ No newline at end of file diff --git a/app/drivers/sensor/pmw3389/pmw3389.c b/app/drivers/sensor/pmw3389/pmw3389.c index 32bcce636b4..914bc5b8eec 100644 --- a/app/drivers/sensor/pmw3389/pmw3389.c +++ b/app/drivers/sensor/pmw3389/pmw3389.c @@ -16,9 +16,7 @@ LOG_MODULE_REGISTER(PMW3389, CONFIG_SENSOR_LOG_LEVEL); -static int pmw3389_access(const struct device *dev, - uint8_t reg, uint8_t *value) -{ +static int pmw3389_access(const struct device *dev, uint8_t reg, uint8_t *value) { struct pmw3389_data *data = dev->data; const struct pmw3389_config *cfg = dev->config; const struct spi_config *spi_cfg = &cfg->bus_cfg.spi_cfg->spi_conf; @@ -38,8 +36,7 @@ static int pmw3389_access(const struct device *dev, .count = 2, }; struct spi_buf_set *rx; - if (reg & PMW3389_WR_MASK > 0) - { + if (reg & PMW3389_WR_MASK > 0) { rx = &(struct spi_buf_set){ .buffers = bufs, .count = 2, @@ -47,37 +44,32 @@ static int pmw3389_access(const struct device *dev, } return spi_transceive(data->bus, spi_cfg, &tx, rx); } -static int pmw3389_read_reg(const struct device *dev, const uint8_t reg, uint8_t *value) -{ +static int pmw3389_read_reg(const struct device *dev, const uint8_t reg, uint8_t *value) { return pmw3389_access(dev, reg, value); } -static int pmw3389_write_reg(const struct device *dev, const uint8_t reg, const uint8_t value) -{ +static int pmw3389_write_reg(const struct device *dev, const uint8_t reg, const uint8_t value) { return pmw3389_access(dev, reg & PMW3389_WR_MASK, &value); } // converts twos complement data to an int16 -static int16_t pmw3389_raw_to_int16(const uint8_t src[2]) -{ +static int16_t pmw3389_raw_to_int16(const uint8_t src[2]) { int16_t res = sys_get_be16(src[2]); if (res > BIT_MASK(15)) res -= BIT(16); return res; } -static int pmw3389_read_raw(const struct device *dev, const uint8_t reg_high, const uint8_t reg_low, int16_t *value) -{ +static int pmw3389_read_raw(const struct device *dev, const uint8_t reg_high, const uint8_t reg_low, + int16_t *value) { uint8_t raw[2] = {0x0, 0x0}; int err; err = pmw3389_read_reg(dev, reg_high, &raw[0]); - if (err) - { + if (err) { LOG_ERR("could not read high byte at %x", reg_high); return err; } err = pmw3389_read_reg(dev, reg_low, &raw[1]); - if (err) - { + if (err) { LOG_ERR("could not read low byte at %x", reg_low); return err; } @@ -85,48 +77,40 @@ static int pmw3389_read_raw(const struct device *dev, const uint8_t reg_high, co return 0; } -static bool pmw3389_spi_check_id(const struct device *dev) -{ +static bool pmw3389_spi_check_id(const struct device *dev) { int err; uint8_t val; err = pmw3389_read_reg(dev, &val, PMW3389_REG_PID); - if (err) - { + if (err) { LOG_ERR("could not read PID"); return false; } - if (val != PMW3389_PID) - { + if (val != PMW3389_PID) { LOG_ERR("invalid PID"); return false; } err = pmw3389_read_reg(dev, &val, PMW3389_REG_REV); - if (err) - { + if (err) { LOG_ERR("could not read REV"); return false; } - if (val != PMW3389_REV) - { + if (val != PMW3389_REV) { LOG_ERR("invalid REV"); return false; } return true; } -int pmw3389_spi_init(const struct device *dev) -{ +int pmw3389_spi_init(const struct device *dev) { struct pmw3389_data *data = dev->data; const struct pmw3389_config *cfg = dev->config; const struct pmw3389_spi_cfg *spi_cfg = cfg->bus_cfg.spi_cfg; - if (spi_cfg->cs_gpios_label != NULL) - { + if (spi_cfg->cs_gpios_label != NULL) { /* handle SPI CS thru GPIO if it is the case */ data->cs_ctrl.gpio_dev = device_get_binding(spi_cfg->cs_gpios_label); - if (!data->cs_ctrl.gpio_dev) - { + if (!data->cs_ctrl.gpio_dev) { LOG_ERR("Unable to get GPIO SPI CS device"); return -ENODEV; } @@ -135,32 +119,26 @@ int pmw3389_spi_init(const struct device *dev) return 0; } -static int pmw3389_sample_fetch(const struct device *dev, enum sensor_channel chan) -{ +static int pmw3389_sample_fetch(const struct device *dev, enum sensor_channel chan) { struct pmw3389_data *data = dev->data; const struct pmw3389_config *cfg = dev->config; uint16_t dx = 0, dy = 0; - if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_POS_DX && - chan != SENSOR_CHAN_POS_DY) + if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_POS_DX && chan != SENSOR_CHAN_POS_DY) return -ENOTSUP; if (!pmw3389_spi_check_id(dev)) return -EINVAL; int err; - if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_POS_DX) - { + if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_POS_DX) { err = pmw3389_read_raw(dev, PMW3389_REG_DX_H, PMW3389_REG_DX_L, &dx); - if (err) - { + if (err) { LOG_DBG("could not read x motion"); return -EIO; } } - if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_POS_DY) - { + if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_POS_DY) { err = pmw3389_read_raw(dev, PMW3389_REG_DY_H, PMW3389_REG_DY_L, &dy); - if (err) - { + if (err) { LOG_DBG("could not read y motion"); return -EIO; } @@ -171,17 +149,15 @@ static int pmw3389_sample_fetch(const struct device *dev, enum sensor_channel ch } static int pmw3389_channel_get(const struct device *dev, enum sensor_channel chan, - struct sensor_value *val) -{ + struct sensor_value *val) { struct pmw3389_data *data = dev->data; const struct pmw3389_config *cfg = dev->config; - switch (chan) - { + switch (chan) { case SENSOR_CHAN_POS_DX: - return data->dx; + val->val1 = data->dx; case SENSOR_CHAN_POS_DY: - return data->dy; + val->val1 = data->dy; default: return -ENOTSUP; } @@ -197,35 +173,30 @@ static const struct sensor_driver_api pmw3389_driver_api = { .channel_get = pmw3389_channel_get, }; -static int pmw3389_init_chip(const struct device *dev) -{ +static int pmw3389_init_chip(const struct device *dev) { struct pmw3389_data *data = dev->data; return 0; } -static int pmw3389_init(const struct device *dev) -{ +static int pmw3389_init(const struct device *dev) { const struct pmw3389_config *const config = dev->config; struct pmw3389_data *data = dev->data; data->bus = device_get_binding(config->bus_name); - if (!data->bus) - { + if (!data->bus) { LOG_DBG("master not found: %s", log_strdup(config->bus_name)); return -EINVAL; } config->bus_init(dev); - if (pmw3389_init_chip(dev) < 0) - { + if (pmw3389_init_chip(dev) < 0) { LOG_DBG("failed to initialize chip"); return -EIO; } #ifdef CONFIG_PMW3389_TRIGGER - if (pmw3389_init_interrupt(dev) < 0) - { + if (pmw3389_init_interrupt(dev) < 0) { LOG_DBG("Failed to initialize interrupt!"); return -EIO; } @@ -236,55 +207,54 @@ static int pmw3389_init(const struct device *dev) #define PMW3389_HAS_CS(n) DT_INST_SPI_DEV_HAS_CS_GPIOS(n) -#define PMW3389_DATA_SPI_CS(n) \ - { \ - .cs_ctrl = { \ - .gpio_pin = DT_INST_SPI_DEV_CS_GPIOS_PIN(n), \ - .gpio_dt_flags = DT_INST_SPI_DEV_CS_GPIOS_FLAGS(n), \ - }, \ +#define PMW3389_DATA_SPI_CS(n) \ + { \ + .cs_ctrl = { \ + .gpio_pin = DT_INST_SPI_DEV_CS_GPIOS_PIN(n), \ + .gpio_dt_flags = DT_INST_SPI_DEV_CS_GPIOS_FLAGS(n), \ + }, \ } #define PMW3389_DATA_SPI(n) COND_CODE_1(PMW3389_HAS_CS(n), (PMW3389_DATA_SPI_CS(n)), ({})) #define PMW3389_SPI_CS_PTR(n) COND_CODE_1(PMW3389_HAS_CS(n), (&(pmw3389_data_##n.cs_ctrl)), (NULL)) -#define PMW3389_SPI_CS_LABEL(n) \ +#define PMW3389_SPI_CS_LABEL(n) \ COND_CODE_1(PMW3389_HAS_CS(n), (DT_INST_SPI_DEV_CS_GPIOS_LABEL(n)), (NULL)) -#define PMW3389_SPI_CFG(n) \ - (&(struct pmw3389_spi_cfg){ \ - .spi_conf = \ - { \ - .frequency = DT_INST_PROP(n, spi_max_frequency), \ - .operation = \ - (SPI_WORD_SET(8) | SPI_OP_MODE_MASTER | SPI_MODE_CPOL | SPI_MODE_CPHA), \ - .slave = DT_INST_REG_ADDR(n), \ - .cs = PMW3389_SPI_CS_PTR(n), \ - }, \ - .cs_gpios_label = PMW3389_SPI_CS_LABEL(n), \ +#define PMW3389_SPI_CFG(n) \ + (&(struct pmw3389_spi_cfg){ \ + .spi_conf = \ + { \ + .frequency = DT_INST_PROP(n, spi_max_frequency), \ + .operation = \ + (SPI_WORD_SET(8) | SPI_OP_MODE_MASTER | SPI_MODE_CPOL | SPI_MODE_CPHA), \ + .slave = DT_INST_REG_ADDR(n), \ + .cs = PMW3389_SPI_CS_PTR(n), \ + }, \ + .cs_gpios_label = PMW3389_SPI_CS_LABEL(n), \ }) -#define PMW3389_GPIO_DT_SPEC_GET_BY_IDX(node_id, prop, idx) \ - { \ - .port = DEVICE_DT_GET(DT_GPIO_CTLR_BY_IDX(node_id, prop, idx)), \ - .pin = DT_GPIO_PIN_BY_IDX(node_id, prop, idx), \ - .dt_flags = DT_GPIO_FLAGS_BY_IDX(node_id, prop, idx), \ +#define PMW3389_GPIO_DT_SPEC_GET_BY_IDX(node_id, prop, idx) \ + { \ + .port = DEVICE_DT_GET(DT_GPIO_CTLR_BY_IDX(node_id, prop, idx)), \ + .pin = DT_GPIO_PIN_BY_IDX(node_id, prop, idx), \ + .dt_flags = DT_GPIO_FLAGS_BY_IDX(node_id, prop, idx), \ } -#define PMW3389_CONFIG_SPI(n) \ - { \ - .bus_name = DT_INST_BUS_LABEL(n), .bus_init = pmw3389_spi_init, \ - .bus_cfg = {.spi_cfg = PMW3389_SPI_CFG(n)}, \ - COND_CODE_0(DT_INST_NODE_HAS_PROP(n, resolution), (1), (DT_INST_PROP(n, resolution))) \ - COND_CODE_1(CONFIG_PMW3389_TRIGGER, \ - (, PMW3389_GPIO_DT_SPEC_GET_BY_IDX(DT_DRV_INST(n), motswk_gpios, 0), ), \ - ()) \ +#define PMW3389_CONFIG_SPI(n) \ + { \ + .bus_name = DT_INST_BUS_LABEL(n), .bus_init = pmw3389_spi_init, \ + .bus_cfg = {.spi_cfg = PMW3389_SPI_CFG(n)}, \ + COND_CODE_1(CONFIG_MA730_TRIGGER, \ + (, PMW3389_GPIO_DT_SPEC_GET_BY_IDX(DT_DRV_INST(n), motswk_gpios, 0)), ()) \ } -#define PMW3389_INST(n) \ - static struct pmw3389_data pmw3389_data_##n = PMW3389_DATA_SPI(n); \ - static const struct pmw3389_config pmw3389_cfg_##n = PMW3389_CONFIG_SPI(n); \ - DEVICE_DT_INST_DEFINE(n, pmw3389_init, device_pm_control_nop, &pmw3389_data_##n, &pmw3389_cfg_##n, \ - POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, &pmw3389_driver_api); +#define PMW3389_INST(n) \ + static struct pmw3389_data pmw3389_data_##n = PMW3389_DATA_SPI(n); \ + static const struct pmw3389_config pmw3389_cfg_##n = PMW3389_CONFIG_SPI(n); \ + DEVICE_DT_INST_DEFINE(n, pmw3389_init, device_pm_control_nop, &pmw3389_data_##n, \ + &pmw3389_cfg_##n, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \ + &pmw3389_driver_api); DT_INST_FOREACH_STATUS_OKAY(PMW3389_INST) \ No newline at end of file diff --git a/app/drivers/sensor/pmw3389/pmw3389.h b/app/drivers/sensor/pmw3389/pmw3389.h index ee000f3058f..0390b64a337 100644 --- a/app/drivers/sensor/pmw3389/pmw3389.h +++ b/app/drivers/sensor/pmw3389/pmw3389.h @@ -40,9 +40,9 @@ struct pmw3389_config { int (*bus_init)(const struct device *dev); const union pmw3389_bus_cfg bus_cfg; int resolution; - struct pmw3389_gpio_dt_spec reset; + struct pmw3389_gpio_dt_spec reset_spec; #if CONFIG_PMW3389_TRIGGER - struct pmw3389_gpio_dt_spec motswk; + struct pmw3389_gpio_dt_spec motswk_spec; #endif // CONFIG_PMW3389_TRIGGER }; diff --git a/app/drivers/sensor/pmw3389/pmw3389_trigger.c b/app/drivers/sensor/pmw3389/pmw3389_trigger.c index 1ccd74f4347..d60051b96c8 100644 --- a/app/drivers/sensor/pmw3389/pmw3389_trigger.c +++ b/app/drivers/sensor/pmw3389/pmw3389_trigger.c @@ -19,21 +19,18 @@ #include LOG_MODULE_DECLARE(PMW3389, CONFIG_SENSOR_LOG_LEVEL); -static inline void setup_int(const struct device *dev, bool enable) -{ +static inline void setup_int(const struct device *dev, bool enable) { struct pmw3389_data *data = dev->data; const struct pmw3389_config *cfg = dev->config; - if (gpio_pin_interrupt_configure(cfg->motswk.port, cfg->motswk.pin, - enable ? GPIO_INT_EDGE_TO_ACTIVE : GPIO_INT_DISABLE)) - { + if (gpio_pin_interrupt_configure(cfg->motswk_spec.port, cfg->motswk_spec.pin, + enable ? GPIO_INT_EDGE_TO_ACTIVE : GPIO_INT_DISABLE)) { LOG_WRN("Unable to set MOTSWK GPIO interrupt"); } } static void pmw3389_motswk_gpio_callback(const struct device *dev, struct gpio_callback *cb, - uint32_t pins) -{ + uint32_t pins) { struct pmw3389_data *drv_data = CONTAINER_OF(cb, struct pmw3389_data, motswk_gpio_cb); LOG_DBG(""); @@ -47,8 +44,7 @@ static void pmw3389_motswk_gpio_callback(const struct device *dev, struct gpio_c #endif } -static void pmw3389_thread_cb(const struct device *dev) -{ +static void pmw3389_thread_cb(const struct device *dev) { struct pmw3389_data *drv_data = dev->data; LOG_DBG("%p", drv_data->handler); @@ -59,15 +55,13 @@ static void pmw3389_thread_cb(const struct device *dev) } #ifdef CONFIG_PMW3389_TRIGGER_OWN_THREAD -static void pmw3389_thread(int dev_ptr, int unused) -{ +static void pmw3389_thread(int dev_ptr, int unused) { const struct device *dev = INT_TO_POINTER(dev_ptr); struct pmw3389_data *drv_data = dev->data; ARG_UNUSED(unused); - while (1) - { + while (1) { k_sem_take(&drv_data->gpio_sem, K_FOREVER); pmw3389_thread_cb(dev); } @@ -75,8 +69,7 @@ static void pmw3389_thread(int dev_ptr, int unused) #endif #ifdef CONFIG_PMW3389_TRIGGER_GLOBAL_THREAD -static void pmw3389_work_cb(struct k_work *work) -{ +static void pmw3389_work_cb(struct k_work *work) { struct pmw3389_data *drv_data = CONTAINER_OF(work, struct pmw3389_data, work); LOG_DBG(""); @@ -86,8 +79,7 @@ static void pmw3389_work_cb(struct k_work *work) #endif int pmw3389_trigger_set(const struct device *dev, const struct sensor_trigger *trig, - sensor_trigger_handler_t handler) -{ + sensor_trigger_handler_t handler) { struct pmw3389_data *drv_data = dev->data; setup_int(dev, false); @@ -102,18 +94,17 @@ int pmw3389_trigger_set(const struct device *dev, const struct sensor_trigger *t return 0; } -int pmw3389_init_interrupt(const struct device *dev) -{ +int pmw3389_init_interrupt(const struct device *dev) { struct pmw3389_data *drv_data = dev->data; const struct pmw3389_config *drv_cfg = dev->config; drv_data->dev = dev; /* setup gpio interrupt */ - gpio_init_callback(&drv_data->motswk_gpio_cb, pmw3389_motswk_gpio_callback, BIT(drv_cfg->motswk_spec.pin)); + gpio_init_callback(&drv_data->motswk_gpio_cb, pmw3389_motswk_gpio_callback, + BIT(drv_cfg->motswk_spec.pin)); - if (gpio_add_callback(drv_cfg->motswk_spec.port, &drv_data->motswk_gpio_cb) < 0) - { + if (gpio_add_callback(drv_cfg->motswk_spec.port, &drv_data->motswk_gpio_cb) < 0) { LOG_DBG("Failed to set MOTSWK callback!"); return -EIO; } diff --git a/app/drivers/zephyr/dts/bindings/sensor/pixart,pmw3389.yaml b/app/drivers/zephyr/dts/bindings/sensor/pixart,pmw3389.yaml new file mode 100644 index 00000000000..1b9fc50eb6b --- /dev/null +++ b/app/drivers/zephyr/dts/bindings/sensor/pixart,pmw3389.yaml @@ -0,0 +1,22 @@ +description: | + Sensor driver for the pixart PMW3389 optical mouse sensor +compatible: "pixart,pmw3389" + +include: spi-device.yaml + +properties: + label: + type: string + required: true + motswk-gpios: + type: phandle-array + required: false + description: interrupt pin for motion + reset-gpios: + type: phandle-array + required: false + description: A pin for the encoder + resolution: + type: int + description: mouse resolution + required: false \ No newline at end of file From f398f02e5799c5a45c2fc4dec40b89cf8dda4b15 Mon Sep 17 00:00:00 2001 From: "Dylan (Luberry) Kozicki" Date: Sat, 16 Apr 2022 16:22:27 -0400 Subject: [PATCH 004/157] switch to multi device pmw33xx driver --- app/drivers/sensor/CMakeLists.txt | 2 +- app/drivers/sensor/Kconfig | 2 +- app/drivers/sensor/pmw3389/CMakeLists.txt | 9 - app/drivers/sensor/pmw3389/Kconfig | 49 --- app/drivers/sensor/pmw3389/pmw3389.c | 260 ------------ app/drivers/sensor/pmw3389/pmw3389.h | 92 ---- app/drivers/sensor/pmw3389/pmw3389_trigger.c | 123 ------ app/drivers/sensor/pmw33xx/.gitignore | 1 + app/drivers/sensor/pmw33xx/CMakeLists.txt | 10 + app/drivers/sensor/pmw33xx/Kconfig | 67 +++ app/drivers/sensor/pmw33xx/pmw3360_srom.h | 291 +++++++++++++ app/drivers/sensor/pmw33xx/pmw33xx.c | 396 ++++++++++++++++++ app/drivers/sensor/pmw33xx/pmw33xx.h | 136 ++++++ app/drivers/sensor/pmw33xx/pmw33xx_trigger.c | 130 ++++++ ...ixart,pmw3389.yaml => pixart,pmw33xx.yaml} | 18 +- 15 files changed, 1042 insertions(+), 544 deletions(-) delete mode 100644 app/drivers/sensor/pmw3389/CMakeLists.txt delete mode 100644 app/drivers/sensor/pmw3389/Kconfig delete mode 100644 app/drivers/sensor/pmw3389/pmw3389.c delete mode 100644 app/drivers/sensor/pmw3389/pmw3389.h delete mode 100644 app/drivers/sensor/pmw3389/pmw3389_trigger.c create mode 100644 app/drivers/sensor/pmw33xx/.gitignore create mode 100644 app/drivers/sensor/pmw33xx/CMakeLists.txt create mode 100644 app/drivers/sensor/pmw33xx/Kconfig create mode 100644 app/drivers/sensor/pmw33xx/pmw3360_srom.h create mode 100644 app/drivers/sensor/pmw33xx/pmw33xx.c create mode 100644 app/drivers/sensor/pmw33xx/pmw33xx.h create mode 100644 app/drivers/sensor/pmw33xx/pmw33xx_trigger.c rename app/drivers/zephyr/dts/bindings/sensor/{pixart,pmw3389.yaml => pixart,pmw33xx.yaml} (50%) diff --git a/app/drivers/sensor/CMakeLists.txt b/app/drivers/sensor/CMakeLists.txt index 5d061280bbe..02696974309 100644 --- a/app/drivers/sensor/CMakeLists.txt +++ b/app/drivers/sensor/CMakeLists.txt @@ -3,4 +3,4 @@ add_subdirectory_ifdef(CONFIG_ZMK_BATTERY battery) add_subdirectory_ifdef(CONFIG_EC11 ec11) -add_subdirectory_ifdef(CONFIG_PMW3389 pmw3389) \ No newline at end of file +add_subdirectory_ifdef(CONFIG_PMW33xx pmw33xx) diff --git a/app/drivers/sensor/Kconfig b/app/drivers/sensor/Kconfig index d47c7fea63f..52eaca0a9d4 100644 --- a/app/drivers/sensor/Kconfig +++ b/app/drivers/sensor/Kconfig @@ -3,4 +3,4 @@ rsource "battery/Kconfig" rsource "ec11/Kconfig" -rsource "pmw3389/Kconfig" \ No newline at end of file +rsource "pmw33xx/Kconfig" diff --git a/app/drivers/sensor/pmw3389/CMakeLists.txt b/app/drivers/sensor/pmw3389/CMakeLists.txt deleted file mode 100644 index 0db5bd48679..00000000000 --- a/app/drivers/sensor/pmw3389/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2020 The ZMK Contributors -# SPDX-License-Identifier: MIT - -zephyr_include_directories(.) - -zephyr_library() - -zephyr_library_sources(pmw3389.c) -zephyr_library_sources_ifdef(CONFIG_PMW3389_TRIGGER pmw3389_trigger.c) \ No newline at end of file diff --git a/app/drivers/sensor/pmw3389/Kconfig b/app/drivers/sensor/pmw3389/Kconfig deleted file mode 100644 index 219d80577fb..00000000000 --- a/app/drivers/sensor/pmw3389/Kconfig +++ /dev/null @@ -1,49 +0,0 @@ -config PMW3389 - bool "PMW3389 Mouse Sensor" - depends on SPI - help - Enable mouse sensor - - The PMW3389 is a 16-bit optical mouse sensor - -if PMW3389 - -choice - prompt "Trigger mode" - default PMW3389_TRIGGER_NONE - help - Specify the type of triggering to be used by the driver. - -config PMW3389_TRIGGER_NONE - bool "No trigger" - -config PMW3389_TRIGGER_GLOBAL_THREAD - bool "Use global thread" - depends on GPIO - select PMW3389_TRIGGER - -config PMW3389_TRIGGER_OWN_THREAD - bool "Use own thread" - depends on GPIO - select PMW3389_TRIGGER - -endchoice - -config PMW3389_TRIGGER - bool - -config PMW3389_THREAD_PRIORITY - int "Thread priority" - depends on PMW3389_TRIGGER_OWN_THREAD - default 10 - help - Priority of thread used by the driver to handle interrupts. - -config PMW3389_THREAD_STACK_SIZE - int "Thread stack size" - depends on PMW3389_TRIGGER_OWN_THREAD - default 1024 - help - Stack size of thread used by the driver to handle interrupts. - -endif # PMW3389 \ No newline at end of file diff --git a/app/drivers/sensor/pmw3389/pmw3389.c b/app/drivers/sensor/pmw3389/pmw3389.c deleted file mode 100644 index 914bc5b8eec..00000000000 --- a/app/drivers/sensor/pmw3389/pmw3389.c +++ /dev/null @@ -1,260 +0,0 @@ - -#define DT_DRV_COMPAT pixart_pmw3389 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "pmw3389.h" - -LOG_MODULE_REGISTER(PMW3389, CONFIG_SENSOR_LOG_LEVEL); - -static int pmw3389_access(const struct device *dev, uint8_t reg, uint8_t *value) { - struct pmw3389_data *data = dev->data; - const struct pmw3389_config *cfg = dev->config; - const struct spi_config *spi_cfg = &cfg->bus_cfg.spi_cfg->spi_conf; - uint8_t access[1] = {reg}; - struct spi_buf bufs[] = { - { - .buf = access, - .len = 1, - }, - { - .buf = value, - .len = 1, - }, - }; - struct spi_buf_set tx = { - .buffers = bufs, - .count = 2, - }; - struct spi_buf_set *rx; - if (reg & PMW3389_WR_MASK > 0) { - rx = &(struct spi_buf_set){ - .buffers = bufs, - .count = 2, - }; - } - return spi_transceive(data->bus, spi_cfg, &tx, rx); -} -static int pmw3389_read_reg(const struct device *dev, const uint8_t reg, uint8_t *value) { - return pmw3389_access(dev, reg, value); -} -static int pmw3389_write_reg(const struct device *dev, const uint8_t reg, const uint8_t value) { - return pmw3389_access(dev, reg & PMW3389_WR_MASK, &value); -} - -// converts twos complement data to an int16 -static int16_t pmw3389_raw_to_int16(const uint8_t src[2]) { - int16_t res = sys_get_be16(src[2]); - if (res > BIT_MASK(15)) - res -= BIT(16); - return res; -} - -static int pmw3389_read_raw(const struct device *dev, const uint8_t reg_high, const uint8_t reg_low, - int16_t *value) { - uint8_t raw[2] = {0x0, 0x0}; - int err; - err = pmw3389_read_reg(dev, reg_high, &raw[0]); - if (err) { - LOG_ERR("could not read high byte at %x", reg_high); - return err; - } - err = pmw3389_read_reg(dev, reg_low, &raw[1]); - if (err) { - LOG_ERR("could not read low byte at %x", reg_low); - return err; - } - *value = pmw3389_raw_to_int16(raw); - return 0; -} - -static bool pmw3389_spi_check_id(const struct device *dev) { - int err; - uint8_t val; - err = pmw3389_read_reg(dev, &val, PMW3389_REG_PID); - if (err) { - LOG_ERR("could not read PID"); - return false; - } - if (val != PMW3389_PID) { - LOG_ERR("invalid PID"); - return false; - } - err = pmw3389_read_reg(dev, &val, PMW3389_REG_REV); - if (err) { - LOG_ERR("could not read REV"); - return false; - } - if (val != PMW3389_REV) { - LOG_ERR("invalid REV"); - return false; - } - return true; -} - -int pmw3389_spi_init(const struct device *dev) { - struct pmw3389_data *data = dev->data; - const struct pmw3389_config *cfg = dev->config; - const struct pmw3389_spi_cfg *spi_cfg = cfg->bus_cfg.spi_cfg; - - if (spi_cfg->cs_gpios_label != NULL) { - - /* handle SPI CS thru GPIO if it is the case */ - data->cs_ctrl.gpio_dev = device_get_binding(spi_cfg->cs_gpios_label); - if (!data->cs_ctrl.gpio_dev) { - LOG_ERR("Unable to get GPIO SPI CS device"); - return -ENODEV; - } - } - - return 0; -} - -static int pmw3389_sample_fetch(const struct device *dev, enum sensor_channel chan) { - struct pmw3389_data *data = dev->data; - const struct pmw3389_config *cfg = dev->config; - uint16_t dx = 0, dy = 0; - - if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_POS_DX && chan != SENSOR_CHAN_POS_DY) - return -ENOTSUP; - if (!pmw3389_spi_check_id(dev)) - return -EINVAL; - int err; - if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_POS_DX) { - err = pmw3389_read_raw(dev, PMW3389_REG_DX_H, PMW3389_REG_DX_L, &dx); - if (err) { - LOG_DBG("could not read x motion"); - return -EIO; - } - } - if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_POS_DY) { - err = pmw3389_read_raw(dev, PMW3389_REG_DY_H, PMW3389_REG_DY_L, &dy); - if (err) { - LOG_DBG("could not read y motion"); - return -EIO; - } - } - data->dx = dx; - data->dy = dy; - return 0; -} - -static int pmw3389_channel_get(const struct device *dev, enum sensor_channel chan, - struct sensor_value *val) { - struct pmw3389_data *data = dev->data; - const struct pmw3389_config *cfg = dev->config; - - switch (chan) { - case SENSOR_CHAN_POS_DX: - val->val1 = data->dx; - case SENSOR_CHAN_POS_DY: - val->val1 = data->dy; - default: - return -ENOTSUP; - } - return 0; -} - -static const struct sensor_driver_api pmw3389_driver_api = { -#ifdef CONFIG_PMW3389_TRIGGER - .trigger_set = pmw3389_trigger_set, -#endif - // .attr_set = pmw3389_attr_set, - .sample_fetch = pmw3389_sample_fetch, - .channel_get = pmw3389_channel_get, -}; - -static int pmw3389_init_chip(const struct device *dev) { - struct pmw3389_data *data = dev->data; - return 0; -} - -static int pmw3389_init(const struct device *dev) { - const struct pmw3389_config *const config = dev->config; - struct pmw3389_data *data = dev->data; - - data->bus = device_get_binding(config->bus_name); - if (!data->bus) { - LOG_DBG("master not found: %s", log_strdup(config->bus_name)); - return -EINVAL; - } - - config->bus_init(dev); - - if (pmw3389_init_chip(dev) < 0) { - LOG_DBG("failed to initialize chip"); - return -EIO; - } - -#ifdef CONFIG_PMW3389_TRIGGER - if (pmw3389_init_interrupt(dev) < 0) { - LOG_DBG("Failed to initialize interrupt!"); - return -EIO; - } -#endif - - return 0; -} - -#define PMW3389_HAS_CS(n) DT_INST_SPI_DEV_HAS_CS_GPIOS(n) - -#define PMW3389_DATA_SPI_CS(n) \ - { \ - .cs_ctrl = { \ - .gpio_pin = DT_INST_SPI_DEV_CS_GPIOS_PIN(n), \ - .gpio_dt_flags = DT_INST_SPI_DEV_CS_GPIOS_FLAGS(n), \ - }, \ - } - -#define PMW3389_DATA_SPI(n) COND_CODE_1(PMW3389_HAS_CS(n), (PMW3389_DATA_SPI_CS(n)), ({})) - -#define PMW3389_SPI_CS_PTR(n) COND_CODE_1(PMW3389_HAS_CS(n), (&(pmw3389_data_##n.cs_ctrl)), (NULL)) - -#define PMW3389_SPI_CS_LABEL(n) \ - COND_CODE_1(PMW3389_HAS_CS(n), (DT_INST_SPI_DEV_CS_GPIOS_LABEL(n)), (NULL)) - -#define PMW3389_SPI_CFG(n) \ - (&(struct pmw3389_spi_cfg){ \ - .spi_conf = \ - { \ - .frequency = DT_INST_PROP(n, spi_max_frequency), \ - .operation = \ - (SPI_WORD_SET(8) | SPI_OP_MODE_MASTER | SPI_MODE_CPOL | SPI_MODE_CPHA), \ - .slave = DT_INST_REG_ADDR(n), \ - .cs = PMW3389_SPI_CS_PTR(n), \ - }, \ - .cs_gpios_label = PMW3389_SPI_CS_LABEL(n), \ - }) - -#define PMW3389_GPIO_DT_SPEC_GET_BY_IDX(node_id, prop, idx) \ - { \ - .port = DEVICE_DT_GET(DT_GPIO_CTLR_BY_IDX(node_id, prop, idx)), \ - .pin = DT_GPIO_PIN_BY_IDX(node_id, prop, idx), \ - .dt_flags = DT_GPIO_FLAGS_BY_IDX(node_id, prop, idx), \ - } - -#define PMW3389_CONFIG_SPI(n) \ - { \ - .bus_name = DT_INST_BUS_LABEL(n), .bus_init = pmw3389_spi_init, \ - .bus_cfg = {.spi_cfg = PMW3389_SPI_CFG(n)}, \ - COND_CODE_1(CONFIG_MA730_TRIGGER, \ - (, PMW3389_GPIO_DT_SPEC_GET_BY_IDX(DT_DRV_INST(n), motswk_gpios, 0)), ()) \ - } - -#define PMW3389_INST(n) \ - static struct pmw3389_data pmw3389_data_##n = PMW3389_DATA_SPI(n); \ - static const struct pmw3389_config pmw3389_cfg_##n = PMW3389_CONFIG_SPI(n); \ - DEVICE_DT_INST_DEFINE(n, pmw3389_init, device_pm_control_nop, &pmw3389_data_##n, \ - &pmw3389_cfg_##n, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \ - &pmw3389_driver_api); - -DT_INST_FOREACH_STATUS_OKAY(PMW3389_INST) \ No newline at end of file diff --git a/app/drivers/sensor/pmw3389/pmw3389.h b/app/drivers/sensor/pmw3389/pmw3389.h deleted file mode 100644 index 0390b64a337..00000000000 --- a/app/drivers/sensor/pmw3389/pmw3389.h +++ /dev/null @@ -1,92 +0,0 @@ -#ifndef ZEPHYR_DRIVERS_SENSOR_PIXART_PMW3389_H_ -#define ZEPHYR_DRIVERS_SENSOR_PIXART_PMW3389_H_ - -#include -#include -#include -#include -#include - -#define PMW3389_WR_MASK 0x80 - -#define PMW3389_PID 0x47 -#define PMW3389_REV 0x01 - -#define PMW3389_REG_PID 0x00 -#define PMW3389_REG_REV 0x01 - -#define PMW3389_REG_DX_L 0x2A -#define PMW3389_REG_DX_H 0x2B -#define PMW3389_REG_DY_L 0x2C -#define PMW3389_REG_DY_H 0x2D - -struct pmw3389_gpio_dt_spec { - const struct device *port; - gpio_pin_t pin; - gpio_dt_flags_t dt_flags; -}; - -struct pmw3389_spi_cfg { - struct spi_config spi_conf; - const char *cs_gpios_label; -}; - -union pmw3389_bus_cfg { - struct pmw3389_spi_cfg *spi_cfg; -}; - -struct pmw3389_config { - char *bus_name; - int (*bus_init)(const struct device *dev); - const union pmw3389_bus_cfg bus_cfg; - int resolution; - struct pmw3389_gpio_dt_spec reset_spec; -#if CONFIG_PMW3389_TRIGGER - struct pmw3389_gpio_dt_spec motswk_spec; -#endif // CONFIG_PMW3389_TRIGGER -}; - -struct pmw3389_data; - -struct pmw3389_transfer_function { - int (*read_data)(const struct device *dev, uint16_t *value); -}; - -struct pmw3389_data { - const struct device *bus; - struct spi_cs_control cs_ctrl; - - uint16_t dx; - uint16_t dy; - - const struct pmw3389_transfer_function *hw_tf; - -#ifdef CONFIG_PMW3389_TRIGGER - - struct gpio_callback motswk_gpio_cb; - const struct device *dev; - - sensor_trigger_handler_t handler; - const struct sensor_trigger *trigger; - -#if defined(CONFIG_PMW3389_TRIGGER_OWN_THREAD) - K_THREAD_STACK_MEMBER(thread_stack, CONFIG_PMW3389_THREAD_STACK_SIZE); - struct k_sem gpio_sem; - struct k_thread thread; -#elif defined(CONFIG_PMW3389_TRIGGER_GLOBAL_THREAD) - struct k_work work; -#endif - -#endif /* CONFIG_PMW3389_TRIGGER */ -}; - -int pmw3389_spi_init(const struct device *dev); -#ifdef CONFIG_PMW3389_TRIGGER - -int pmw3389_trigger_set(const struct device *dev, const struct sensor_trigger *trig, - sensor_trigger_handler_t handler); - -int pmw3389_init_interrupt(const struct device *dev); -#endif - -#endif /* ZEPHYR_DRIVERS_SENSOR_PIXART_PMW3389_H_ */ \ No newline at end of file diff --git a/app/drivers/sensor/pmw3389/pmw3389_trigger.c b/app/drivers/sensor/pmw3389/pmw3389_trigger.c deleted file mode 100644 index d60051b96c8..00000000000 --- a/app/drivers/sensor/pmw3389/pmw3389_trigger.c +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (c) 2020 The ZMK Contributors - * - * SPDX-License-Identifier: MIT - */ - -#define DT_DRV_COMPAT pixart_pmw3389 - -#include -#include -#include -#include -#include - -#include "pmw3389.h" - -// extern struct pmw3389_data pmw3389_driver; - -#include -LOG_MODULE_DECLARE(PMW3389, CONFIG_SENSOR_LOG_LEVEL); - -static inline void setup_int(const struct device *dev, bool enable) { - struct pmw3389_data *data = dev->data; - const struct pmw3389_config *cfg = dev->config; - - if (gpio_pin_interrupt_configure(cfg->motswk_spec.port, cfg->motswk_spec.pin, - enable ? GPIO_INT_EDGE_TO_ACTIVE : GPIO_INT_DISABLE)) { - LOG_WRN("Unable to set MOTSWK GPIO interrupt"); - } -} - -static void pmw3389_motswk_gpio_callback(const struct device *dev, struct gpio_callback *cb, - uint32_t pins) { - struct pmw3389_data *drv_data = CONTAINER_OF(cb, struct pmw3389_data, motswk_gpio_cb); - - LOG_DBG(""); - - setup_int(drv_data->dev, false); - -#if defined(CONFIG_PMW3389_TRIGGER_OWN_THREAD) - k_sem_give(&drv_data->gpio_sem); -#elif defined(CONFIG_PMW3389_TRIGGER_GLOBAL_THREAD) - k_work_submit(&drv_data->work); -#endif -} - -static void pmw3389_thread_cb(const struct device *dev) { - struct pmw3389_data *drv_data = dev->data; - - LOG_DBG("%p", drv_data->handler); - drv_data->handler(dev, drv_data->trigger); - - // Enable once the wall/spam of interrupts is solved - setup_int(dev, true); -} - -#ifdef CONFIG_PMW3389_TRIGGER_OWN_THREAD -static void pmw3389_thread(int dev_ptr, int unused) { - const struct device *dev = INT_TO_POINTER(dev_ptr); - struct pmw3389_data *drv_data = dev->data; - - ARG_UNUSED(unused); - - while (1) { - k_sem_take(&drv_data->gpio_sem, K_FOREVER); - pmw3389_thread_cb(dev); - } -} -#endif - -#ifdef CONFIG_PMW3389_TRIGGER_GLOBAL_THREAD -static void pmw3389_work_cb(struct k_work *work) { - struct pmw3389_data *drv_data = CONTAINER_OF(work, struct pmw3389_data, work); - - LOG_DBG(""); - - pmw3389_thread_cb(drv_data->dev); -} -#endif - -int pmw3389_trigger_set(const struct device *dev, const struct sensor_trigger *trig, - sensor_trigger_handler_t handler) { - struct pmw3389_data *drv_data = dev->data; - - setup_int(dev, false); - - k_msleep(5); - - drv_data->trigger = trig; - drv_data->handler = handler; - - setup_int(dev, true); - - return 0; -} - -int pmw3389_init_interrupt(const struct device *dev) { - struct pmw3389_data *drv_data = dev->data; - const struct pmw3389_config *drv_cfg = dev->config; - - drv_data->dev = dev; - /* setup gpio interrupt */ - - gpio_init_callback(&drv_data->motswk_gpio_cb, pmw3389_motswk_gpio_callback, - BIT(drv_cfg->motswk_spec.pin)); - - if (gpio_add_callback(drv_cfg->motswk_spec.port, &drv_data->motswk_gpio_cb) < 0) { - LOG_DBG("Failed to set MOTSWK callback!"); - return -EIO; - } - -#if defined(CONFIG_PMW3389_TRIGGER_OWN_THREAD) - k_sem_init(&drv_data->gpio_sem, 0, UINT_MAX); - - k_thread_create(&drv_data->thread, drv_data->thread_stack, CONFIG_PMW3389_THREAD_STACK_SIZE, - (k_thread_entry_t)pmw3389_thread, dev, 0, NULL, - K_PRIO_COOP(CONFIG_PMW3389_THREAD_PRIORITY), 0, K_NO_WAIT); -#elif defined(CONFIG_PMW3389_TRIGGER_GLOBAL_THREAD) - k_work_init(&drv_data->work, pmw3389_work_cb); -#endif - - return 0; -} \ No newline at end of file diff --git a/app/drivers/sensor/pmw33xx/.gitignore b/app/drivers/sensor/pmw33xx/.gitignore new file mode 100644 index 00000000000..ee2a286bb29 --- /dev/null +++ b/app/drivers/sensor/pmw33xx/.gitignore @@ -0,0 +1 @@ +pmw3389_srom.h diff --git a/app/drivers/sensor/pmw33xx/CMakeLists.txt b/app/drivers/sensor/pmw33xx/CMakeLists.txt new file mode 100644 index 00000000000..a4c89fb2edd --- /dev/null +++ b/app/drivers/sensor/pmw33xx/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright (c) 2020 The ZMK Contributors +# SPDX-License-Identifier: MIT + +zephyr_include_directories(.) +zephyr_include_directories_ifdef(CONFIG_PMW33XX_3389 CONFIG_PMW33XX_3389_SROM_INCLUDE_DIR) + +zephyr_library() + +zephyr_library_sources(pmw33xx.c) +zephyr_library_sources_ifdef(CONFIG_PMW33XX_TRIGGER pmw33xx_trigger.c) diff --git a/app/drivers/sensor/pmw33xx/Kconfig b/app/drivers/sensor/pmw33xx/Kconfig new file mode 100644 index 00000000000..845a0879732 --- /dev/null +++ b/app/drivers/sensor/pmw33xx/Kconfig @@ -0,0 +1,67 @@ +config PMW33XX + bool "PMW33XX Mouse Sensor" + depends on SPI + help + Enable mouse sensor + + The PMW33XX is a 16-bit optical mouse sensor + +if PMW33XX +choice + prompt "Sensor" + default PMW33XX_3360 + + config PMW33XX_3360 + bool "Enable PMW3360" + + config PMW33XX_3389 + bool "Enable PMW3389" + help + PMW3389 requires an external srom included in `pmw3389_srom.h` which we cannot currently include in zmk +endchoice + +config PMW33XX_3389_SROM_INCLUDE_DIR + string "PMW3389 SROM Include Dir" + default "." + help + directory where the header with the pmw3389 srom file is located + +choice + prompt "Trigger mode" + default PMW33XX_TRIGGER_NONE + help + Specify the type of triggering to be used by the driver. + +config PMW33XX_TRIGGER_NONE + bool "No trigger" + +config PMW33XX_TRIGGER_GLOBAL_THREAD + bool "Use global thread" + depends on GPIO + select PMW33XX_TRIGGER + +config PMW33XX_TRIGGER_OWN_THREAD + bool "Use own thread" + depends on GPIO + select PMW33XX_TRIGGER + +endchoice + +config PMW33XX_TRIGGER + bool + +config PMW33XX_THREAD_PRIORITY + int "Thread priority" + depends on PMW33XX_TRIGGER_OWN_THREAD + default 10 + help + Priority of thread used by the driver to handle interrupts. + +config PMW33XX_THREAD_STACK_SIZE + int "Thread stack size" + depends on PMW33XX_TRIGGER_OWN_THREAD + default 1024 + help + Stack size of thread used by the driver to handle interrupts. + +endif # PMW33XX diff --git a/app/drivers/sensor/pmw33xx/pmw3360_srom.h b/app/drivers/sensor/pmw33xx/pmw3360_srom.h new file mode 100644 index 00000000000..44a5b316994 --- /dev/null +++ b/app/drivers/sensor/pmw33xx/pmw3360_srom.h @@ -0,0 +1,291 @@ +/* +This SROM is provided "AS IS" by PixArt Imaging Inc., WITHOUT WARRANTY +OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL PIXART +IMAGING INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THIS +SROM OR THE USE OR OTHER DEALINGS IN THIS SROM. +*/ + +#ifndef ZEPHYR_DRIVERS_SENSOR_PIXART_PMW33XX_PMW3360_SROM_H_ +#define ZEPHYR_DRIVERS_SENSOR_PIXART_PMW33XX_PMW3360_SROM_H_ + +#include +static const uint8_t SROM[] = { + 0x01, 0x05, 0x8d, 0x92, 0x66, 0x67, 0x1e, 0xbe, 0xfe, 0x5f, 0x1d, 0xb8, 0xf2, 0x66, 0x4e, + 0xff, 0x5d, 0x38, 0xf2, 0x46, 0x0c, 0x98, 0x97, 0x8d, 0x79, 0x51, 0x20, 0xc2, 0x06, 0x8e, + 0x9e, 0xbe, 0xfe, 0x7e, 0x5f, 0x1d, 0xb8, 0xd3, 0x24, 0xca, 0x16, 0x8f, 0x7d, 0x78, 0x53, + 0x05, 0x88, 0x73, 0x45, 0xe9, 0x55, 0x22, 0xa7, 0xae, 0xd8, 0x32, 0xe6, 0x2f, 0xbd, 0xf8, + 0x72, 0x47, 0x0c, 0x7b, 0x74, 0x6a, 0x56, 0x2e, 0xbf, 0xdd, 0x38, 0xd5, 0x20, 0xca, 0x16, + 0xae, 0xde, 0x1f, 0x9d, 0xb8, 0xf2, 0x47, 0x0c, 0x7b, 0x55, 0x28, 0xd2, 0x26, 0xaf, 0xdc, + 0x3a, 0xd7, 0x2c, 0xbb, 0xd5, 0x09, 0x90, 0xa2, 0xa7, 0xcc, 0xfb, 0x74, 0x4b, 0xf5, 0x49, + 0x10, 0x83, 0x84, 0x8a, 0x77, 0x4d, 0x18, 0xb2, 0xe6, 0x4e, 0x1e, 0x9f, 0xbc, 0xfa, 0x6a, + 0xea, 0xab, 0x55, 0x91, 0xa9, 0xda, 0x58, 0xa9, 0xe1, 0x1a, 0xc2, 0x52, 0x1c, 0x0d, 0xef, + 0x50, 0x71, 0x04, 0x21, 0x93, 0x87, 0x0e, 0xb1, 0xa9, 0x4a, 0xc0, 0x24, 0xaf, 0xe3, 0x26, + 0xa2, 0x8d, 0xa1, 0x6d, 0xf2, 0x88, 0x9e, 0x99, 0xbb, 0xf2, 0x62, 0x38, 0xfe, 0x78, 0x1d, + 0x58, 0x19, 0x9b, 0xbe, 0x10, 0x6c, 0x15, 0x9e, 0x50, 0x8b, 0x99, 0x40, 0x92, 0x01, 0x38, + 0xb6, 0x97, 0x83, 0x51, 0x59, 0xa6, 0xb7, 0xc9, 0x48, 0xfd, 0x68, 0x21, 0x78, 0xf4, 0x2e, + 0x86, 0x61, 0xb0, 0xb2, 0x2b, 0x5b, 0xf0, 0x1b, 0x71, 0xa3, 0x0a, 0xd5, 0xd6, 0x30, 0x7a, + 0x0c, 0xf8, 0xaf, 0xc6, 0x54, 0x6b, 0x89, 0x30, 0x58, 0xe8, 0xe2, 0x0d, 0x39, 0x2f, 0x47, + 0xa0, 0x41, 0x74, 0x5d, 0xd6, 0xf3, 0x85, 0xb7, 0x66, 0x59, 0x51, 0x74, 0x3d, 0xfe, 0xee, + 0x3a, 0x1e, 0x2a, 0x5f, 0x93, 0x0c, 0x3f, 0xd4, 0x4d, 0x24, 0x13, 0x2c, 0xf2, 0x7b, 0x4d, + 0x06, 0x60, 0x2e, 0x39, 0xa9, 0xd4, 0x72, 0x36, 0xb6, 0x4d, 0xea, 0x8c, 0xd9, 0xa7, 0xbc, + 0x38, 0xe7, 0xbb, 0xb7, 0x7b, 0x04, 0x08, 0x83, 0x5a, 0x27, 0x6f, 0x42, 0xc6, 0x7e, 0xbd, + 0x97, 0x0e, 0x55, 0xdc, 0x79, 0x63, 0xd8, 0x89, 0xc1, 0x83, 0x3d, 0x34, 0x01, 0x76, 0xff, + 0xde, 0x7f, 0x65, 0x4b, 0xb3, 0x46, 0x56, 0x8d, 0x9e, 0x6f, 0x1f, 0x2b, 0xa4, 0x69, 0x54, + 0xde, 0x1c, 0x2d, 0xa8, 0x51, 0x7e, 0xdf, 0xbb, 0x18, 0x90, 0x27, 0xaf, 0x79, 0xd1, 0x86, + 0x4f, 0x44, 0x88, 0x7f, 0xb9, 0x80, 0x21, 0xa9, 0x5d, 0x7b, 0xd4, 0x81, 0x21, 0xa9, 0x13, + 0x59, 0x72, 0x58, 0x11, 0xe3, 0xf2, 0xf7, 0xae, 0xc3, 0xc2, 0x64, 0x1d, 0xfb, 0xd8, 0x91, + 0x2d, 0x13, 0xb5, 0x0e, 0x67, 0xdb, 0xbc, 0x63, 0xde, 0xa0, 0x86, 0x49, 0x6c, 0x40, 0x82, + 0x82, 0x3f, 0xeb, 0x38, 0x22, 0xa8, 0x20, 0x1e, 0xc5, 0x5f, 0x25, 0xea, 0xaf, 0xe6, 0x78, + 0x1c, 0x41, 0x0b, 0x5a, 0x94, 0xcc, 0x8d, 0x0d, 0xc5, 0xb8, 0x9c, 0x1a, 0x9f, 0x6d, 0xa8, + 0x57, 0xe9, 0x1a, 0xf0, 0xad, 0x6c, 0x26, 0x5f, 0xb8, 0x38, 0x16, 0x0e, 0x28, 0xd6, 0x76, + 0x5a, 0xc2, 0xb7, 0x6a, 0x9e, 0x26, 0x9d, 0x3e, 0x1f, 0x25, 0xb0, 0x14, 0x46, 0xcd, 0x67, + 0x49, 0x93, 0x13, 0x81, 0x01, 0x46, 0x72, 0x31, 0x42, 0x91, 0x87, 0x6c, 0x1c, 0xb8, 0x08, + 0x72, 0x6a, 0x33, 0x64, 0xed, 0xc0, 0x99, 0xb9, 0xf6, 0x34, 0xe9, 0xa5, 0x6c, 0x57, 0x9a, + 0x72, 0x18, 0x48, 0x62, 0xc1, 0x88, 0xcf, 0xa3, 0x50, 0x53, 0x50, 0xdf, 0xc8, 0x9f, 0xf0, + 0xb6, 0xa2, 0x23, 0xc4, 0xaa, 0xe3, 0xd0, 0xe2, 0xab, 0x76, 0xc8, 0x4a, 0x02, 0x21, 0xdd, + 0xf3, 0x59, 0xdc, 0x30, 0x6e, 0xb5, 0x42, 0xe2, 0x9d, 0x8a, 0xdf, 0xfd, 0xa5, 0x41, 0x75, + 0x01, 0x83, 0xbe, 0x47, 0xa5, 0x21, 0x30, 0x3b, 0x0e, 0x33, 0x2e, 0x74, 0xaf, 0x27, 0x90, + 0xf4, 0x20, 0xa7, 0x1b, 0xff, 0x77, 0x01, 0xab, 0x79, 0x0a, 0xfb, 0x86, 0x1e, 0x1f, 0x22, + 0x6d, 0x75, 0x75, 0xfc, 0xe0, 0x8c, 0xa2, 0x72, 0x0f, 0x45, 0x48, 0xf9, 0x43, 0x10, 0xf0, + 0x57, 0x15, 0x17, 0x74, 0x40, 0x8f, 0x77, 0x69, 0x8a, 0xd4, 0x0f, 0x25, 0x27, 0x0a, 0xe8, + 0x84, 0x73, 0x41, 0xb9, 0x5f, 0xb6, 0xc8, 0x2f, 0x02, 0x83, 0xdc, 0x1e, 0x1c, 0xdb, 0x3f, + 0x2d, 0x58, 0xf7, 0x5e, 0xfd, 0x6f, 0xda, 0xad, 0xdc, 0x43, 0x01, 0x22, 0xc6, 0x45, 0x9c, + 0xec, 0x56, 0x8a, 0x1a, 0x4e, 0xaa, 0xae, 0x3a, 0xa1, 0xf3, 0xf1, 0x9a, 0x97, 0x8d, 0x79, + 0x51, 0x01, 0xf5, 0x31, 0x04, 0xcd, 0x2a, 0xbc, 0xfd, 0xac, 0x59, 0x1b, 0x0a, 0xe2, 0x26, + 0xdd, 0x2f, 0x96, 0xaa, 0x62, 0xc5, 0x6d, 0xb8, 0xe1, 0x72, 0x4e, 0x4e, 0xb7, 0x4f, 0x9f, + 0xaa, 0x33, 0x75, 0x8b, 0x43, 0x44, 0x0a, 0xc3, 0x0a, 0x5e, 0x9c, 0xdb, 0x1e, 0x4e, 0xbd, + 0x0f, 0xec, 0x09, 0x96, 0x5a, 0x37, 0x6d, 0x10, 0x44, 0xd4, 0x91, 0xc0, 0xc3, 0x0d, 0x35, + 0x62, 0xcf, 0x0a, 0xb1, 0x1a, 0x8a, 0xda, 0xa8, 0xcf, 0xbc, 0x3a, 0xb2, 0x4f, 0x59, 0x29, + 0xd9, 0x91, 0x96, 0x0d, 0x6b, 0x9c, 0xcf, 0x34, 0x5b, 0x76, 0x7b, 0xb2, 0xb2, 0xce, 0xbf, + 0x72, 0xd9, 0x96, 0x4a, 0xc0, 0x2d, 0x89, 0x33, 0xd0, 0xe4, 0xbe, 0x69, 0x7f, 0x1c, 0x9e, + 0x71, 0x19, 0x12, 0xf6, 0x74, 0xfb, 0xd6, 0x41, 0x95, 0x69, 0xf0, 0xe9, 0xa7, 0x50, 0x82, + 0x04, 0x78, 0x4d, 0x99, 0xb3, 0xa7, 0xfe, 0xee, 0x7f, 0xed, 0x56, 0x57, 0x57, 0xe9, 0xd3, + 0x6a, 0x43, 0xf4, 0x0b, 0x6d, 0xba, 0x34, 0xde, 0xf8, 0x4c, 0x45, 0xae, 0x00, 0x32, 0x1a, + 0x4e, 0xff, 0x54, 0x60, 0x6a, 0xd9, 0x59, 0x18, 0xd3, 0xbe, 0x5a, 0x94, 0x16, 0x9d, 0xec, + 0xdf, 0xb4, 0x48, 0x8e, 0x6f, 0x19, 0xf8, 0xe6, 0xec, 0x7f, 0xb7, 0x49, 0x92, 0xb3, 0xb6, + 0xec, 0x7e, 0xdc, 0x08, 0x3b, 0xe5, 0x5b, 0x88, 0xf9, 0x92, 0xf3, 0x7c, 0x9d, 0x7c, 0x22, + 0xf2, 0x5d, 0x7d, 0x76, 0x07, 0x2e, 0x42, 0xd6, 0x9e, 0x5a, 0xbc, 0x7d, 0xfd, 0xb5, 0x92, + 0x4a, 0x7f, 0xbf, 0xac, 0xc0, 0xb6, 0x18, 0x8b, 0x02, 0x71, 0xae, 0xc7, 0xa8, 0x8c, 0x19, + 0xc1, 0x0a, 0x36, 0x3e, 0xa4, 0x2d, 0xa6, 0xbb, 0xe8, 0x77, 0x34, 0x45, 0xca, 0x60, 0x52, + 0xf0, 0x26, 0xf7, 0x58, 0x68, 0x34, 0x28, 0xb5, 0x29, 0xe5, 0xb8, 0x65, 0x0f, 0xc8, 0xc2, + 0x64, 0x50, 0x91, 0x12, 0xa1, 0x7b, 0xd7, 0x09, 0x74, 0x5f, 0xcc, 0xac, 0x9d, 0xac, 0x0a, + 0xd1, 0x46, 0xbe, 0xc2, 0x2d, 0xa6, 0x67, 0x69, 0x69, 0x55, 0x8a, 0xbd, 0x67, 0xd9, 0x31, + 0xc7, 0x80, 0x7c, 0xd5, 0xb2, 0xbe, 0x83, 0x17, 0x7a, 0x2e, 0xf4, 0x3e, 0x14, 0xa5, 0x62, + 0x77, 0xa5, 0xdb, 0x77, 0x58, 0x35, 0x5f, 0xc4, 0x6c, 0xd2, 0xf2, 0x38, 0x91, 0x74, 0x80, + 0x95, 0x52, 0xd2, 0xaf, 0xe2, 0x1b, 0x12, 0xfe, 0x4b, 0x5f, 0x84, 0x5f, 0x7d, 0x7e, 0x0b, + 0xd9, 0x42, 0x72, 0xf1, 0x32, 0x1d, 0x8d, 0x5c, 0xcb, 0x20, 0x05, 0x4f, 0xbe, 0xbf, 0x40, + 0xe9, 0xa6, 0x9f, 0xff, 0x05, 0xe1, 0x73, 0x52, 0x65, 0xb4, 0x59, 0x45, 0x04, 0x10, 0xe1, + 0xdc, 0x0b, 0x30, 0xaa, 0x23, 0x4d, 0xb9, 0x58, 0xe5, 0xf1, 0xa3, 0x9c, 0xf2, 0xf7, 0xaf, + 0xbc, 0xe3, 0xa7, 0x0d, 0x68, 0x97, 0x6c, 0x2b, 0x33, 0x60, 0xe3, 0xce, 0x7c, 0xda, 0xd1, + 0x2d, 0x78, 0x52, 0x10, 0xa0, 0xf7, 0x9a, 0xf4, 0x79, 0xdb, 0xa5, 0x2a, 0x71, 0xc6, 0xcf, + 0xa0, 0x68, 0xd0, 0x32, 0x2a, 0xd3, 0xb2, 0xe8, 0x03, 0x27, 0xbe, 0x55, 0xfd, 0x0e, 0x90, + 0xa2, 0x76, 0x8c, 0xdc, 0xbd, 0x69, 0x9c, 0xb6, 0x18, 0x92, 0xe3, 0xb3, 0x23, 0x1d, 0x3c, + 0x85, 0x2a, 0x2b, 0x04, 0x3a, 0xf5, 0xee, 0xf8, 0xd2, 0xdd, 0x6d, 0x52, 0xa2, 0x45, 0x75, + 0xd9, 0xa1, 0x03, 0x23, 0x83, 0x24, 0x04, 0xe7, 0xee, 0xa2, 0x14, 0x4e, 0xa9, 0xde, 0x4e, + 0x9d, 0x24, 0x1a, 0x63, 0xd3, 0x4b, 0xf7, 0x71, 0xc1, 0xe7, 0xfb, 0x48, 0xa4, 0x7c, 0x39, + 0xcb, 0x80, 0xdd, 0x5e, 0xaf, 0x0d, 0xca, 0xef, 0x58, 0x17, 0xf4, 0xca, 0x0c, 0x00, 0x30, + 0x68, 0x3e, 0x5d, 0x9b, 0xc3, 0x19, 0x27, 0x7a, 0x35, 0x32, 0x5c, 0x7b, 0x92, 0x16, 0x7f, + 0x6e, 0x86, 0x6b, 0x50, 0x7a, 0xd6, 0x15, 0x10, 0x16, 0xa8, 0x28, 0xb1, 0xa1, 0xd8, 0xda, + 0xc8, 0xfd, 0x5b, 0xe2, 0xc0, 0xd6, 0x8d, 0x75, 0xbd, 0x1c, 0x0b, 0x27, 0xfe, 0x1d, 0x09, + 0x23, 0x13, 0x4f, 0x5a, 0x5a, 0xf8, 0xbe, 0x5d, 0x31, 0x8a, 0x81, 0xaf, 0xbe, 0xed, 0x00, + 0x43, 0x14, 0x8f, 0xa5, 0x4a, 0xfe, 0x90, 0xe1, 0x67, 0x25, 0x29, 0x52, 0x2f, 0x26, 0x17, + 0xef, 0x6e, 0xe6, 0x1e, 0xaf, 0x7f, 0x4a, 0x40, 0x15, 0xc7, 0x6e, 0x8d, 0x60, 0x92, 0xd6, + 0x8d, 0x95, 0xca, 0xff, 0x8e, 0xfe, 0x4b, 0x8d, 0x1a, 0x83, 0x40, 0xe6, 0xb7, 0xbc, 0x6b, + 0xd6, 0x39, 0x53, 0xd0, 0x4a, 0xe6, 0x1f, 0x1f, 0xb1, 0x16, 0x6a, 0xae, 0x6a, 0x6d, 0xe3, + 0xe6, 0xed, 0x4e, 0xff, 0x49, 0x28, 0xc7, 0xaf, 0xb4, 0x14, 0x9f, 0x85, 0x9f, 0xfe, 0x6a, + 0x92, 0x93, 0x9c, 0x6f, 0xff, 0x4a, 0xd4, 0x3e, 0x26, 0xd9, 0x72, 0x4f, 0x01, 0xe2, 0x54, + 0x06, 0xc6, 0x7d, 0xb8, 0xef, 0x0b, 0x04, 0x56, 0x3a, 0x51, 0x41, 0xe2, 0x1d, 0xc2, 0x96, + 0x95, 0xd1, 0x66, 0x16, 0x3b, 0x50, 0x36, 0x3f, 0x2e, 0xf2, 0xb2, 0x25, 0x2d, 0x59, 0x0f, + 0x23, 0xf2, 0x9d, 0xb0, 0xee, 0x59, 0x89, 0xd0, 0x79, 0x30, 0xc7, 0x54, 0x9b, 0xec, 0xf8, + 0xd7, 0xdc, 0x5a, 0x11, 0x1d, 0xfb, 0xf0, 0x82, 0x33, 0x3c, 0xff, 0x17, 0xff, 0xfc, 0x92, + 0xc6, 0x6f, 0x3d, 0x5d, 0x99, 0x85, 0x2a, 0xa1, 0x04, 0xe8, 0x21, 0xed, 0x12, 0x2b, 0x7d, + 0xbc, 0xa3, 0x36, 0x9b, 0x9f, 0xfd, 0x5d, 0x60, 0x66, 0xe9, 0x39, 0x5e, 0x34, 0x29, 0xa7, + 0x08, 0x56, 0xf7, 0x4d, 0x19, 0x8d, 0x91, 0xff, 0xc8, 0x8a, 0xe7, 0xe8, 0x22, 0x00, 0x35, + 0x68, 0x34, 0x93, 0xe7, 0x3b, 0x41, 0xbb, 0xf6, 0x58, 0x2f, 0x10, 0x66, 0x77, 0x69, 0xcd, + 0x94, 0xb9, 0xbe, 0x59, 0x73, 0x4f, 0xc3, 0x11, 0xa1, 0xb3, 0x13, 0xe2, 0x82, 0xf1, 0x05, + 0xcb, 0xa7, 0x75, 0xf6, 0x01, 0xb2, 0x24, 0xa9, 0x59, 0x05, 0x59, 0x46, 0xc9, 0x25, 0x39, + 0x36, 0xee, 0x48, 0x90, 0x83, 0x89, 0xc4, 0xdb, 0xa3, 0x83, 0xd0, 0xd3, 0x62, 0x3f, 0x5e, + 0x0e, 0x0c, 0xb7, 0x77, 0xb3, 0xd6, 0xbb, 0x37, 0x70, 0xb2, 0xb3, 0x87, 0x60, 0xd7, 0x8a, + 0xe2, 0x99, 0xa9, 0x0f, 0x18, 0x16, 0xf6, 0xcd, 0x14, 0x4e, 0x2c, 0x7e, 0xfe, 0xf9, 0xa4, + 0x48, 0xfb, 0xb9, 0x60, 0x13, 0x01, 0xd2, 0x6e, 0xdc, 0x3f, 0xe7, 0x2d, 0x7f, 0xe1, 0x11, + 0x96, 0x12, 0x9a, 0x90, 0x48, 0x2b, 0x76, 0x78, 0xa6, 0xdd, 0x67, 0x48, 0x68, 0x42, 0xfd, + 0x74, 0xb6, 0x51, 0xd7, 0x99, 0x07, 0x7d, 0x8b, 0x02, 0xd6, 0x68, 0x36, 0x97, 0x6d, 0x49, + 0x28, 0xbf, 0xcc, 0x23, 0x8f, 0xed, 0x78, 0x4c, 0x49, 0xb0, 0xd1, 0x9a, 0x3d, 0x1c, 0xdd, + 0xf0, 0xeb, 0xd5, 0xd4, 0xc2, 0x12, 0xae, 0x5d, 0x00, 0xe0, 0xf4, 0x84, 0x10, 0x08, 0x76, + 0x28, 0x5a, 0x5e, 0xde, 0xe3, 0xad, 0x4e, 0x56, 0xcc, 0x22, 0x85, 0x5f, 0xa9, 0xca, 0xa5, + 0x4e, 0xc6, 0x82, 0xf0, 0xbb, 0x5a, 0x82, 0xbf, 0x32, 0x54, 0xd2, 0x8a, 0x12, 0x05, 0x6f, + 0xd8, 0xd1, 0xc0, 0x60, 0x00, 0x41, 0x03, 0xf4, 0xa8, 0xd6, 0xee, 0x9e, 0x5a, 0x75, 0xea, + 0x76, 0x88, 0xf1, 0x2d, 0x8d, 0x63, 0xfc, 0x14, 0xe9, 0x7b, 0xeb, 0x0b, 0x49, 0xa4, 0x8b, + 0x86, 0xf4, 0x97, 0x61, 0x22, 0x29, 0x32, 0x23, 0x80, 0x2e, 0x59, 0x44, 0xea, 0xf5, 0x56, + 0x18, 0x51, 0xc0, 0x60, 0xe1, 0x83, 0x66, 0x1f, 0x1f, 0xa1, 0x13, 0x64, 0xae, 0xbc, 0x59, + 0xd0, 0xc4, 0x38, 0x70, 0x3f, 0xb5, 0xb5, 0xa2, 0xdc, 0x89, 0x77, 0x70, 0x5a, 0x86, 0xb7, + 0xde, 0x67, 0x3c, 0x22, 0x83, 0xc8, 0x62, 0x77, 0xb6, 0x5d, 0x7e, 0x41, 0x99, 0x70, 0x7d, + 0xc3, 0x27, 0x90, 0x7f, 0x3c, 0xc6, 0x90, 0xc7, 0xb9, 0x32, 0xd8, 0x01, 0xdf, 0x02, 0x20, + 0x27, 0x8f, 0x7b, 0x76, 0xb3, 0x0d, 0xfa, 0x93, 0x46, 0x6f, 0xde, 0x23, 0x12, 0xb9, 0x4e, + 0xb8, 0x36, 0x8e, 0x9c, 0x3a, 0x2f, 0xd4, 0x2b, 0x54, 0xab, 0x63, 0xe6, 0xf1, 0x62, 0x8c, + 0xd1, 0x1e, 0x87, 0x48, 0x2d, 0x83, 0x93, 0xfe, 0xac, 0x29, 0x82, 0xc2, 0x3c, 0x4f, 0xd0, + 0x19, 0xe7, 0xa3, 0x99, 0x8d, 0x7d, 0x64, 0x15, 0xde, 0x64, 0xdb, 0xc1, 0x73, 0xe0, 0x78, + 0x24, 0x23, 0x9e, 0x8a, 0xfb, 0xe2, 0xea, 0xd5, 0x0f, 0x93, 0x51, 0x62, 0x38, 0x41, 0x3e, + 0x40, 0xa4, 0x2f, 0x7e, 0xb8, 0x11, 0x5c, 0xd3, 0x46, 0x0a, 0x74, 0xcb, 0xb7, 0xd0, 0x15, + 0x96, 0xf1, 0xe7, 0x48, 0x32, 0x05, 0x69, 0xa8, 0xda, 0x56, 0xee, 0x9f, 0x6a, 0x95, 0xe6, + 0x0d, 0xd2, 0x2c, 0xc1, 0xc2, 0xa5, 0xf4, 0xd8, 0xc6, 0xd7, 0xf6, 0x1f, 0x6f, 0x88, 0x78, + 0xab, 0xd4, 0x1f, 0x53, 0xa0, 0xa1, 0x3b, 0x03, 0xb1, 0x4a, 0xcd, 0xbe, 0x25, 0x1c, 0x6f, + 0xb4, 0x30, 0x41, 0xb5, 0x4a, 0xaa, 0xa5, 0x38, 0x91, 0x77, 0xce, 0x48, 0xc8, 0xe1, 0x34, + 0xc2, 0x85, 0xdc, 0x9b, 0x24, 0x6f, 0x63, 0x12, 0xaa, 0x97, 0xaf, 0x9e, 0x37, 0x1e, 0xaf, + 0x94, 0x2c, 0x5c, 0x43, 0xa6, 0x93, 0x17, 0x7d, 0x1c, 0x77, 0x18, 0x0f, 0xb5, 0xf2, 0x66, + 0x4a, 0xaf, 0xd9, 0x92, 0xfb, 0x47, 0xfc, 0x1f, 0x7a, 0xdb, 0x77, 0x0b, 0x11, 0x61, 0xe0, + 0x85, 0x12, 0x96, 0xf7, 0x55, 0x09, 0x90, 0xa2, 0x77, 0xdb, 0x9c, 0x9c, 0x8b, 0x04, 0x55, + 0x45, 0x39, 0x64, 0x4e, 0x46, 0x5f, 0xa2, 0x98, 0xbc, 0xd6, 0x1b, 0x5d, 0x5f, 0x67, 0x21, + 0xc7, 0x12, 0xe5, 0xe6, 0x13, 0x93, 0x7b, 0x83, 0xd8, 0x5b, 0xcc, 0x2e, 0x77, 0xce, 0x56, + 0x97, 0xdc, 0x96, 0x70, 0xcc, 0x47, 0x1a, 0x79, 0x1f, 0xb0, 0x6a, 0xe4, 0x7c, 0xd6, 0xa7, + 0x28, 0x72, 0x7a, 0x1e, 0xac, 0x54, 0xc0, 0x5d, 0x8c, 0xa1, 0x00, 0xf4, 0x2e, 0x86, 0x28, + 0x3a, 0x82, 0xce, 0xbc, 0xf0, 0xfd, 0x2d, 0xb1, 0xc4, 0x9f, 0x6d, 0x6b, 0xba, 0x9e, 0x9b, + 0xec, 0xb5, 0x38, 0xe2, 0xe9, 0xf8, 0x57, 0x74, 0xa6, 0x9b, 0x7b, 0xec, 0xe2, 0x90, 0x0d, + 0xec, 0x3e, 0xf0, 0x1b, 0x2b, 0x38, 0x9c, 0x82, 0xd9, 0xd1, 0xbe, 0xe1, 0x08, 0x7f, 0x02, + 0x54, 0xbc, 0x0b, 0x90, 0x14, 0x57, 0xa2, 0x4e, 0x56, 0xbe, 0x1b, 0xdf, 0x48, 0x9c, 0x17, + 0xa1, 0x70, 0xa2, 0x8c, 0x74, 0x74, 0x24, 0xbf, 0xe0, 0x46, 0xb7, 0xdc, 0xa4, 0xb5, 0x66, + 0x44, 0xb3, 0x53, 0x0e, 0xa4, 0xe5, 0xb4, 0x83, 0x06, 0x1f, 0x78, 0x2a, 0xe2, 0x16, 0x5c, + 0x79, 0x00, 0xab, 0x0a, 0x08, 0x57, 0xe5, 0xbc, 0x2c, 0x9b, 0x91, 0x19, 0xf6, 0x1d, 0x31, + 0x25, 0xbb, 0x9b, 0xb5, 0x89, 0x32, 0xb7, 0x53, 0xb9, 0x49, 0xa4, 0x55, 0xc5, 0x9d, 0x2b, + 0xa1, 0xeb, 0xd5, 0xec, 0x02, 0x40, 0x14, 0x44, 0x35, 0xc1, 0x65, 0xd7, 0xae, 0x4c, 0xf8, + 0x16, 0xf5, 0x70, 0x62, 0x28, 0xcd, 0x58, 0x71, 0x01, 0xcb, 0x85, 0x09, 0x24, 0x91, 0xf0, + 0x18, 0xfa, 0xc6, 0x37, 0x07, 0xde, 0xe7, 0x43, 0xed, 0x3a, 0xeb, 0x98, 0x97, 0xf4, 0xc5, + 0xf3, 0x60, 0xa5, 0x83, 0x60, 0x1a, 0xf1, 0x81, 0x03, 0x06, 0x2c, 0x1f, 0x38, 0x71, 0x61, + 0x30, 0x21, 0x8a, 0x49, 0xb4, 0x2a, 0xa6, 0x03, 0x0e, 0x1d, 0x1d, 0x79, 0xc0, 0xe5, 0x5d, + 0xd1, 0x82, 0x37, 0x1b, 0x70, 0x3a, 0xd2, 0x65, 0x48, 0x59, 0xc0, 0xa2, 0xf3, 0x8b, 0x82, + 0x24, 0xf5, 0xfe, 0x2a, 0xdd, 0x8d, 0x3a, 0xe9, 0xe5, 0x1c, 0x92, 0xc7, 0x6a, 0x43, 0xde, + 0x78, 0xb3, 0x33, 0xa0, 0x9a, 0x78, 0x4a, 0x54, 0xba, 0x01, 0x84, 0xd2, 0xb2, 0xa4, 0x6f, + 0x28, 0x21, 0xf2, 0xc4, 0x21, 0x9f, 0x18, 0x72, 0x16, 0x46, 0x6d, 0xfa, 0xf2, 0xa7, 0x7c, + 0x99, 0xa4, 0x2c, 0x93, 0x4d, 0xba, 0xdd, 0xa7, 0x59, 0x50, 0x0b, 0x34, 0x48, 0x63, 0x6f, + 0x0d, 0x4b, 0x81, 0x22, 0x3b, 0x24, 0x9b, 0x37, 0x4b, 0xd3, 0x94, 0x23, 0xc0, 0x71, 0x39, + 0x91, 0xd0, 0x06, 0x2e, 0xd2, 0xae, 0x7c, 0xfd, 0xfd, 0x4d, 0x4a, 0x95, 0x0a, 0xd0, 0x84, + 0xbf, 0x8c, 0x5c, 0xe4, 0xc1, 0x10, 0xfa, 0xe6, 0x26, 0x18, 0xea, 0x75, 0x3e, 0x7d, 0x0b, + 0xc5, 0x9d, 0xbb, 0x14, 0x3c, 0xa5, 0xe3, 0x19, 0x2b, 0xdd, 0xcd, 0xbb, 0xb4, 0x48, 0x9e, + 0x8c, 0xa5, 0xf2, 0xcb, 0x19, 0xa9, 0x8f, 0x0c, 0x0b, 0x36, 0x73, 0xd7, 0x78, 0x2b, 0x64, + 0xc9, 0x8c, 0x4a, 0xb2, 0x8f, 0xe9, 0xd9, 0x51, 0xb1, 0x84, 0x49, 0xd1, 0xec, 0x73, 0xa6, + 0xb1, 0x41, 0x70, 0xa0, 0xd3, 0xea, 0xb4, 0xf8, 0x50, 0x57, 0xb6, 0x69, 0xda, 0x4a, 0xe6, + 0xe0, 0x73, 0xba, 0xdc, 0x98, 0xc4, 0x0e, 0xab, 0xc7, 0xbd, 0x8a, 0xf6, 0xe3, 0xc1, 0x0b, + 0xc3, 0x5b, 0x48, 0xa1, 0x79, 0xab, 0xae, 0x9b, 0x17, 0xbd, 0x56, 0x3b, 0x0c, 0x38, 0xbd, + 0xed, 0x86, 0x95, 0x05, 0xf4, 0x32, 0xf9, 0xdc, 0x87, 0x4f, 0x78, 0x9a, 0xb0, 0x6f, 0x2b, + 0x76, 0xd2, 0xf4, 0x1f, 0x65, 0xf8, 0xd0, 0x9f, 0x8d, 0x3c, 0xb2, 0x53, 0x8c, 0xca, 0xa0, + 0x2b, 0x6d, 0xfb, 0x65, 0x96, 0xde, 0x7c, 0x64, 0xbb, 0xf7, 0x7e, 0x3d, 0x8d, 0xfb, 0xd1, + 0x2d, 0xf1, 0xc3, 0x1a, 0x66, 0x8a, 0x8f, 0x5e, 0x89, 0x37, 0x19, 0xe7, 0x9b, 0xee, 0xf0, + 0xc1, 0xe7, 0xac, 0xe5, 0x15, 0x2f, 0x6d, 0xfb, 0x57, 0xca, 0x78, 0xd0, 0xaa, 0x19, 0xc0, + 0x80, 0x67, 0xc9, 0x2f, 0x81, 0x26, 0x7f, 0xbe, 0xdf, 0xde, 0x50, 0x00, 0x2b, 0x1b, 0x17, + 0x2a, 0x5d, 0x54, 0xfa, 0x46, 0xc1, 0x3f, 0xb7, 0x2f, 0xc8, 0x31, 0xf4, 0x58, 0x43, 0x36, + 0xae, 0x91, 0xc4, 0x46, 0xc4, 0x93, 0xaf, 0x05, 0x9e, 0xa3, 0xba, 0x0c, 0x07, 0x2f, 0x0c, + 0xeb, 0x2f, 0x4d, 0x4a, 0x90, 0xd8, 0xc0, 0x51, 0x0f, 0xa5, 0x97, 0x09, 0x16, 0xfa, 0x24, + 0xc3, 0x99, 0xe6, 0x1c, 0x9e, 0xed, 0x03, 0x76, 0x57, 0xce, 0x29, 0x73, 0x54, 0x19, 0x5a, + 0x20, 0x65, 0x52, 0xa6, 0xfa, 0x97, 0x00, 0x52, 0x23, 0xbb, 0x74, 0x9b, 0x6e, 0xf3, 0x25, + 0x94, 0x9c, 0x66, 0x0f, 0x8b, 0x67, 0x62, 0x1a, 0x13, 0xdb, 0xd5, 0xdb, 0xdb, 0xb9, 0x11, + 0x02, 0x13, 0x63, 0xe4, 0x6e, 0x41, 0x8a, 0xa0, 0xc2, 0x52, 0x71, 0x66, 0xde, 0x3b, 0x3d, + 0x6c, 0xd3, 0xaa, 0x5f, 0x78, 0x3b, 0xf2, 0x7b, 0xad, 0x71, 0x48, 0x2e, 0x06, 0x90, 0x4c, + 0x76, 0x89, 0x99, 0xb5, 0x91, 0x61, 0x29, 0x74, 0x8d, 0x90, 0xd6, 0x1b, 0x60, 0x11, 0xf6, + 0xc0, 0xa0, 0xc7, 0x3c, 0x1e, 0xf6, 0xec, 0x80, 0x91, 0xe8, 0x6d, 0x46, 0xe0, 0x2c, 0xda, + 0x25, 0xdb, 0x18, 0xdb, 0xb1, 0xbe, 0x7c, 0x87, 0x14, 0xa9, 0xa3, 0x46, 0xdb, 0x6e, 0xeb, + 0x5d, 0x98, 0xd2, 0x4c, 0xfa, 0x09, 0x13, 0x71, 0xee, 0x51, 0xae, 0x5d, 0x57, 0x79, 0x80, + 0x02, 0x43, 0x7b, 0xf7, 0x92, 0x3c, 0xf9, 0x03, 0x07, 0x4f, 0x6a, 0x53, 0x9e, 0x35, 0x2d, + 0x40, 0x29, 0x7d, 0x44, 0x39, 0xd2, 0xb4, 0x48, 0xa7, 0x4f, 0xa5, 0x90, 0x9d, 0xb7, 0x4e, + 0xa0, 0xb5, 0xb7, 0x92, 0x0f, 0x1c, 0x18, 0xdd, 0x4c, 0xff, 0xd4, 0xf9, 0x53, 0xd1, 0x82, + 0x38, 0xc5, 0xcc, 0xe2, 0x36, 0x2d, 0xf7, 0x18, 0x07, 0xda, 0x5b, 0xf6, 0xef, 0xe5, 0xcb, + 0xaa, 0x90, 0xd2, 0x84, 0x25, 0xbc, 0x4f, 0x4a, 0x3b, 0x10, 0x0f, 0x81, 0x92, 0xe4, 0xda, + 0x84, 0xad, 0x8b, 0xbb, 0x94, 0xc5, 0xf4, 0x4c, 0x25, 0xd2, 0x08, 0x97, 0xe0, 0x98, 0x0b, + 0xbe, 0xe7, 0xaa, 0x40, 0x06, 0xfa, 0xe4, 0x79, 0xab, 0x76, 0x1f, 0xb6, 0x7f, 0x72, 0x48, + 0x5c, 0x27, 0x8b, 0xab, 0xef, 0x13, 0xe1, 0x0c, 0xb1, 0x79, 0x7b, 0x6d, 0x8f, 0x0b, 0x90, + 0x22, 0x7f, 0x2f, 0xa5, 0x71, 0xca, 0xb7, 0xc0, 0xbb, 0x31, 0x23, 0x03, 0x44, 0xd2, 0x51, + 0x74, 0x08, 0x85, 0xad, 0x1e, 0xfb, 0xc3, 0xa2, 0xa1, 0xe1, 0x3e, 0x14, 0x0e, 0xf2, 0x0a, + 0xb3, 0x9d, 0x74, 0x14, 0x50, 0xff, 0xfe, 0x95, 0xed, 0x02, 0x76, 0xe6, 0xf7, 0x0b, 0x32, + 0x4e, 0x40, 0xb0, 0xd2, 0x67, 0x74, 0x92, 0x51, 0x1e, 0x54, 0x6f, 0x30, 0x6f, 0x58, 0x6a, + 0xbb, 0x8a, 0x6c, 0xa6, 0x6d, 0x92, 0xe3, 0x3e, 0x0c, 0x33, 0x7c, 0xfc, 0x87, 0x12, 0xb8, + 0x61, 0x70, 0xa6, 0x6c, 0x28, 0xad, 0xa2, 0x55, 0x81, 0x58, 0xd5, 0xc7, 0xb6, 0xd1, 0xdf, + 0x47, 0x5d, 0x07, 0x1f, 0xed, 0xfd, 0x4e, 0x0f, 0x3c, 0xe3, 0x9d, 0xae, 0xeb, 0xf6, 0x3a, + 0xb6, 0x0e, 0x5e, 0xc9, 0x9f, 0xc9, 0x5e, 0xbc, 0x37, 0xcf, 0x81, 0x04, 0x60, 0x26, 0xe4, + 0x8b, 0x90, 0xdb, 0xf1, 0xc2, 0x66, 0x87, 0xd2, 0x29, 0x72, 0x01, 0xa6, 0xcd, 0x01, 0xdf, + 0x9a, 0xfb, 0x4d, 0x62, 0xcb, 0x4d, 0x59, 0x18, 0xd2, 0x3f, 0x79, 0xf7, 0xaf, 0xda, 0x10, + 0x66, 0xb6, 0x3e, 0x7e, 0x7b, 0xcb, 0xb6, 0xa0, 0xd7, 0xa8, 0x5b, 0xe7, 0x8e, 0xa0, 0x24, + 0xf4, 0x55, 0x43, 0x00, 0xda, 0x86, 0x50, 0xec, 0x0a, 0x04, 0x27, 0xb1, 0x98, 0x40, 0x5c, + 0xb1, 0xa5, 0x18, 0x87, 0xa9, 0x88, 0x87, 0x85, 0xe0, 0x89, 0xd5, 0xe2, 0x24, 0xef, 0x65, + 0xac, 0x2c, 0xd1, 0x3a, 0xe7, 0x67, 0xee, 0xc4, 0xd6, 0x3a, 0x76, 0x2a, 0x55, 0x03, 0x5b, + 0x0b, 0xde, 0x5e, 0x38, 0x5a, 0xb7, 0x7d, 0xb3, 0x82, 0x39, 0x5c, 0x98, 0xc3, 0x2f, 0x8d, + 0x53, 0xa0, 0x41, 0x7d, 0xa8, 0x83, 0x47, 0x4a, 0xb0, 0x42, 0xc8, 0xeb, 0xf4, 0xfb, 0x9e, + 0xe9, 0x82, 0xe3, 0xf6, 0x4d, 0x16, 0xe4, 0xd9, 0xd1, 0xca, 0x56, 0xae, 0x09, 0xe7, 0xcf, + 0xaa, 0x25, 0x15, 0x91, 0xce, 0x15, 0xa9, 0x87, 0x50, 0x80, 0x38, 0xcd, 0x6a, 0x64, 0x4d, + 0xb5, 0x55, 0x63, 0xc7, 0x9c, 0xb0, 0x52, 0xb0, 0x3d, 0x5a, 0x8b, 0x65, 0x19, 0x33, 0x43, + 0xa2, 0x97, 0x20, 0xad, 0x79, 0xa6, 0xe8, 0x82, 0x06, 0x39, 0x45, 0x8b, 0x22, 0x35, 0x28, + 0x72, 0x45, 0xc8, 0xca, 0xa4, 0xc3, 0x10, 0x20, 0x22, 0x83, 0x8f, 0x39, 0x49, 0x83, 0x72, + 0x6d, 0xf4, 0x56, 0xfd, 0x55, 0x43, 0x60, 0x3d, 0x75, 0xa3, 0x4e, 0x14, 0x5b, 0xb8, 0xff, + 0x4f, 0x17, 0xd9, 0x93, 0xd7, 0xc0, 0xaf, 0x5c, 0xb9, 0x65, 0xa5, 0x86, 0x8a, 0x9a, 0x35, + 0x5d, 0x95, 0x30, 0x21, 0xc3, 0x38, 0x82, 0x05, 0x0b, 0xb8, 0x02, 0x20, 0xf1, 0x8a, 0x37, + 0x15, 0x0a, 0xc2, 0x46, 0x8a, 0x16, 0x6f, 0x0d, 0x3a, 0x5b, 0x12, 0x6e, 0xee, 0xbf, 0xaf, + 0xfb, 0x04, 0xe5, 0x34, 0xcc, 0x44, 0x2c, 0x3c, 0x22, 0x7c, 0x4f, 0xd7, 0xad, 0xe1, 0xe2, + 0x12, 0xe6, 0xda, 0x69, 0x6f, 0x9c, 0xc8, 0x1f, 0xb9, 0x7e, 0x42, 0x20, 0x7d, 0x21, 0x0a, + 0x0f, 0xb6, 0x16, 0x59, 0x01, 0x91, 0x16, 0xfe, 0xa4, 0x7b, 0xb6, 0x36, 0x43, 0x72, 0xbf, + 0x33, 0x71, 0xb8, 0x5d, 0x8a, 0x4f, 0x91, 0xe0, 0x79, 0xb5, 0x40, 0xda, 0x9f, 0x02, 0xbe, + 0x35, 0x60, 0xc0, 0xef, 0xc9, 0x2f, 0x42, 0xd3, 0xcd, 0xef, 0xde, 0x10, 0x71, 0x14, 0x8d, + 0x86, 0x29, 0xcd, 0x31, 0x39, 0x90, 0xe0, 0x2d, 0x28, 0x97, 0xe4, 0xb3, 0xe5, 0xd7, 0x61, + 0xe3, 0x2b, 0x21, 0xe7, 0xc5, 0x4b, 0xd6, 0xe9, 0xdb, 0x51, 0x03, 0xa2, 0x0e, 0x7c, 0x7e, + 0x95, 0xa8, 0xf6, 0x8b, 0x95, 0x8f, 0x6e, 0xfd, 0x54, 0xfd, 0x28, 0x71, 0x4a, 0xfc, 0x75, + 0x54, 0x19, 0x8c, 0xd3, 0x8a, 0x9b, 0x94, 0xcd, 0x33, 0x60, 0x80, 0x7a, 0xd4, 0x68, 0x38, + 0x29, 0x0e, 0xf0, 0xc1, 0xeb, 0xac, 0xaf, 0xb1, 0xe6, 0xce, 0xbd, 0xd1, 0xfd, 0xbc, 0x23, + 0x85, 0x8d, 0x97, 0xf4, 0xc9, 0xf9, 0xbd, 0x69, 0xd2, 0x0c, 0x60, 0x12, 0x05, 0x82, 0x6e, + 0x5a, 0x6e, 0xfd, 0x55, 0xcc, 0xd8, 0xe6, 0xed, 0x54, 0xde, 0xdb, 0xcc, 0xf9, 0x85, 0x0b, + 0xf6, 0x42, 0x39, 0x76, 0xde, 0xab, 0x08, 0xd7, 0x65, 0xe8, 0x19, 0xbd, 0x72, 0xec, 0x53, + 0x7d, 0xc8, 0xa6, 0x00, 0xb3, 0x3e, 0x5b, 0x7d, 0xa0, 0x7b, 0xaa, 0x6e, 0xc5, 0x45, 0x12, + 0xe3, 0xac, 0x02, 0x1c, 0x01, 0xb8, 0x2b, 0x2b, 0xd6, 0xf8, 0x2e, 0x96, 0x3a, 0x5e, 0x6e, + 0xda, 0xf9, 0x6c, 0xac, 0x36, 0xf8, 0xcd, 0xef, 0x05, 0x8d, 0x8f, 0xe7, 0x6c, 0x39, 0x24, + 0xb3, 0x8d, 0xed, 0xd1, 0x70, 0xa6, 0x20, 0x1e, 0x2b, 0x1d, 0x2c, 0x40, 0x16, 0xd6, 0x23, + 0xb0, 0x99, 0xaa, 0x74, 0x31, 0xbe, 0xdc, 0x28, 0xd4, 0x64, 0xfa, 0xd2, 0x8d, 0xdb, 0xa1, + 0x32, 0xaf, 0xdc, 0xb1, 0x71, 0xb3, 0x4d, 0x0a, 0x3a, 0x3a, 0x53, 0x11, 0x8c, 0x11, 0x8c, + 0xe7, 0xd5, 0xdb, 0xdd, 0x67, 0xb8, 0x1a, 0x39, 0x0e, 0xf6, 0x69, 0xd8, 0x1b, 0xcb, 0xe5, + 0xc5, 0xa6, 0x3d, 0x3b, 0xfe, 0xe1, 0x05, 0x48, 0x83, 0x67, 0x7f, 0xf1, 0x9c, 0x70, 0x7e, + 0x70, 0x5c, 0xc4, 0xd3, 0x78, 0x8b, 0x80, 0x87, 0xf5, 0x94, 0x2d, 0x0a, 0xbf, 0x82, 0x13, + 0xad, 0x36, 0x10, 0x0a, 0x60, 0xaa, 0xc4, 0xa3, 0xcd, 0xf1, 0xa9, 0xa1, 0x49, 0xf5, 0x41, + 0x14, 0xd6, 0xd1, 0xa7, 0xe0, 0x50, 0xce, 0xe2, 0x76, 0xbe, 0x09, 0xc4, 0x3a, 0x68, 0xc5, + 0x8a, 0x1f, 0x8e, 0xd0, 0x4d, 0x90, 0x92, 0xc8, 0x3b, 0x63, 0xae, 0xc2, 0xfe, 0x77, 0x8c, + 0x4b, 0xa6, 0x6c, 0x04, 0xff, 0x62, 0xe1, 0x32, 0x04, 0xb8, 0xfd, 0xb3, 0x34, 0x47, 0x85, + 0x32, 0x6b, 0x50, 0xce, 0xcf, 0x4f, 0x0f, 0x72, 0x41, 0x09, 0x7f, 0xde, 0x5c, 0xe7, 0x08, + 0xdf, 0x63, 0x09, 0x89, 0xd1, 0x23, 0xb6, 0xe3, 0xbc, 0xff, 0xe4, 0x88, 0x61, 0x67, 0x8e, + 0x4a, 0xb6, 0x86, 0x08, 0x33, 0xad, 0xac, 0x9f, 0xf3, 0x37, 0xbd, 0xcc, 0xd6, 0x0e, 0x3e, + 0x4e, 0x4f, 0xc9, 0xff, 0xc8, 0xf6, 0xd8, 0x2a, 0x43, 0x3d, 0x79, 0xf2, 0x5d, 0x87, 0x33, + 0x1d, 0xf4, 0xfd, 0x80, 0x2c, 0x4f, 0xc4, 0xa5, 0x3a, 0x2f, 0x51, 0xb1, 0x19, 0xf8, 0xed, + 0xe0, 0xc9, 0x6c, 0x82, 0x2d, 0x43, 0xdd, 0x71, 0x27, 0x32, 0x8b, 0x51, 0x3d, 0xbc, 0x83, + 0x6b, 0xa8, 0xda, 0x2b, 0xbb, 0xb0, 0xba, 0x5b, 0x30, 0x4f, 0x44, 0xad, 0x99, 0xb7, 0x47, + 0x35, 0x6f, 0x1f, 0xbe, 0x5f, 0x24, 0x4e, 0xba, 0x92, 0x89, 0xc8, 0xb6, 0x45, 0x12, 0x19, + 0xee, 0xe7, 0xd8, 0x74, 0xb3, 0x33, 0x41, 0x39, 0x26, 0x4c, 0xc3, 0xb0, 0x22, 0x1e, 0xee, + 0xd0, 0xfb, 0xc6, 0xa3, 0x7d, 0xaa, 0x7d, 0xa1, 0x51, 0x98, 0xe8, 0xcc, 0x58, 0x39, 0x22, + 0x1e, 0xe7, 0xf0, 0xb4, 0xd3, 0x7c, 0xa4, 0xa6, 0x10, 0xba, 0x2f, 0x41, 0xd6, 0x17, 0x95, + 0x76, 0xe1, 0xbb, 0xec, 0x43, 0x99, 0x87, 0xd4, 0x13, 0x9b, 0x85, 0x4f, 0x44, 0x32, 0x5a, + 0xe0, 0x9a, 0xee, 0x29, 0x15, 0x45, 0x05, 0xf7, 0xe4, 0xc9, 0x4d, 0x6b, 0x45, 0xad, 0x1f, + 0x13, 0x06, 0x29, 0xb4, 0x2b, 0x74, 0x83, 0xe1, 0xdb, 0x3d, 0x99, 0x28, 0xbd, 0x4f, 0xfb, + 0x0d, 0x03, 0x4d, 0x79, 0x09, 0x7f, 0x12, 0x91, 0xe9, 0x69, 0x40, 0x84, 0xc7, 0x10, 0xd5, + 0xce, 0xae, 0xbb, 0xa5, 0xb2, 0x35, 0xdb, 0x24, 0xa4, 0x0e, 0xe4, 0x6f, 0x80, 0xb4, 0xb6, + 0x94, 0xf7, 0x51, 0x5b, 0x0c, 0x5b, 0xae, 0x3f, 0xa5, 0x17, 0xe0, 0x9c, 0xd1, 0x05, 0x31, + 0x05, 0xc4, 0x14, 0xdd, 0xde, 0x6f, 0x39, 0xa1, 0x9a, 0x46, 0x3d, 0x68, 0x3c, 0xfe, 0x04, + 0x8e, 0xc2, 0x11, 0x9d, 0xc2, 0x3a, 0xaa, 0xcf, 0x22, 0x26, 0x54, 0xcb, 0x8d, 0x47, 0x61, + 0x9e, 0xf4, 0x2e, 0xe7, 0x3c, 0xe6, 0xf0, 0xd6, 0x1a, 0xe5, 0x18, 0xbd, 0x9f, 0xa4, 0x3e, + 0x47, 0xdb, 0x85, 0xe0, 0x8c, 0xbf, 0xdd, 0x19, 0x91, 0x81, 0x80, 0x63, 0x44, 0x0a, 0x96, + 0xae, 0xde, 0x1f, 0xbc, 0xdb, 0x15, 0x89, 0x71, 0x60, 0x23, 0xc4, 0x0a, 0x96, 0xae, 0xbf, + 0xfc, 0x7a, 0x76, 0x4f, 0x1c, 0x9b, 0xb4, 0xcb, 0x14, 0x8b, 0x94, 0x8b, 0x94, 0xaa, 0xd6, + 0x0f, 0x7d, 0x78, 0x72, 0x47, 0x0c, 0x9a, 0x97, 0xac, 0xbb, 0xf4, 0x6a, 0x56, 0x0f, 0x7d, + 0x59, 0x30, 0xc3, 0x04, 0x6b, 0x54, 0x0b, 0x75, 0x49, 0x10, 0xa2, 0xa7, 0xad, 0xd8, 0x13, + 0x85, 0x88, 0x73, 0x45, 0xe9, 0x31, 0xc1, 0x00, 0x82, 0x67, 0x4c, 0xfb, 0x74, 0x6a, 0x56, + 0x2e, 0xbf, 0xdd, 0x19, 0x91, 0x81, 0x80, 0x82, 0x86, 0x6f, 0x5c, 0x3a, 0xf6, 0x4f, 0x1c, + 0xba, 0xf6, 0x4f, 0xfd, 0x59, 0x11, 0x81, 0x61, 0x21, 0xa1, 0xc0, 0x02, 0x86, 0x6f, 0x3d, + 0xf8, 0x53, 0x24, 0xca, 0xf7, 0x6c, 0x5a, 0x36, 0xcf, 0xfd, 0x59, 0x11, 0xa0, 0xa3, 0xc4, + 0x0a, 0x96, 0x8f, 0x9c, 0xba, 0xd7, 0x0d, 0x98, 0xb2, 0xc7, 0xed, 0x58, 0x32, 0xc7, 0xed, + 0x39, 0xf0, 0x43, 0x04, 0x6b, 0x54, 0x2a, 0xb7, 0xec, 0x5a, 0x36, 0xee, 0x3f, 0xfc, 0x7a, + 0x76, 0x6e, 0x5e, 0x1f, 0x9d, 0x99, 0x91, 0x81, 0x80, 0x82, 0x86, 0x6f, 0x5c, 0x1b, 0x95, + 0x89, 0x90, 0xa2, 0xa7, 0xcc, 0x1a, 0x97, 0xac, 0xbb, 0xf4, 0x6a, 0x37, 0xec, 0x3b, 0xf4, + 0x6a, 0x37, 0xec, 0x3b, 0xf4, 0x4b, 0xf5, 0x49, 0xf1, 0x60, 0x23, 0xa5, 0xa9, 0xb1, 0xc1, + 0xe1, 0x21, 0xa1, 0xa1, 0xc0, 0xe3, 0x25, 0xa9, 0xb1, 0xe0, 0x42, 0xe7, 0x4c, 0xfb, 0x74, + 0x6a, 0x37, 0xec, 0x5a, 0x17, 0xac, 0xbb, 0xd5, 0x28, 0xd2, 0x07, 0x6d, 0x58, 0x13, 0x85, + 0x69, 0x31, 0xe0, 0x42, 0xe7, 0x4c, 0xfb, 0x55, 0x28, 0xd2, 0x07, 0x8c, 0x7b, 0x74, 0x4b, + 0x14, 0xaa, 0xd6, 0x2e, 0xde, 0x3e, 0xfe, 0x7e, 0x5f, 0x1d, 0xb8, 0xf2, 0x47, 0x0c, 0x7b, + 0x55, 0x09, 0x90, 0x83, 0x65, 0x48, 0x12, 0x87, 0x6d, 0x39, 0xf0, 0x62, 0x46, 0x0e, 0x9e, + 0x9f, 0xbc, 0xfa, 0x57, 0x2c, 0xbb, 0xd5, 0x09, 0x71, 0x60, 0x5b, 0x08, 0xf7, 0x2b, +}; +#endif diff --git a/app/drivers/sensor/pmw33xx/pmw33xx.c b/app/drivers/sensor/pmw33xx/pmw33xx.c new file mode 100644 index 00000000000..1d07d2efeda --- /dev/null +++ b/app/drivers/sensor/pmw33xx/pmw33xx.c @@ -0,0 +1,396 @@ + + +#define DT_DRV_COMPAT pixart_pmw33xx + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_PMW33XX_3389 +#include +#elif CONFIG_PMW33XX_3360 +#include +#endif + +#include "pmw33xx.h" + +LOG_MODULE_REGISTER(PMW33XX, CONFIG_SENSOR_LOG_LEVEL); +#define PMW33XX_PID COND_CODE_1(CONFIG_PMW33XX_3389, (PMW33XX_3389_PID), (PMW33XX_3360_PID)) + +struct pmw33xx_motion_burst { + uint8_t motion; + uint8_t observation; + int16_t dx; + int16_t dy; +} __attribute__((packed)); + +static inline int pmw33xx_cs_select(const struct pmw33xx_gpio_dt_spec *cs_gpio_cfg, + const uint8_t value) +{ + return gpio_pin_set(cs_gpio_cfg->port, cs_gpio_cfg->pin, value); +} + +static int pmw33xx_access(const struct device *dev, const uint8_t reg, uint8_t *value) +{ + struct pmw33xx_data *data = dev->data; + const struct pmw33xx_config *cfg = dev->config; + const struct spi_config *spi_cfg = &cfg->bus_cfg.spi_cfg->spi_conf; + const struct pmw33xx_gpio_dt_spec *cs_gpio_cfg = &cfg->bus_cfg.spi_cfg->cs_spec; + + uint8_t access[1] = { reg }; + struct spi_buf_set tx = { + .buffers = { &(struct spi_buf){ + .buf = access, + .len = 1, + } }, + .count = 1, + }; + uint8_t result[1] = { *value }; + struct spi_buf_set rx = { + .buffers = { &(struct spi_buf){ + .buf = result, + .len = 1, + } }, + .count = 1, + }; + + pmw33xx_cs_select(cs_gpio_cfg, 0); + + int err = spi_write(data->bus, spi_cfg, &tx); + k_sleep(K_USEC(120)); //Tsrad + if (err) { + pmw33xx_cs_select(cs_gpio_cfg, 1); + return err; + } + + if ((reg & PMW33XX_WR_MASK)) + err = spi_write(data->bus, spi_cfg, &rx); + else + err = spi_read(data->bus, spi_cfg, &rx); + pmw33xx_cs_select(cs_gpio_cfg, 1); + k_sleep(K_USEC(160)); + if ((reg & PMW33XX_WR_MASK) == 0) + *value = result[0]; + return err; +} +static int pmw33xx_read_reg(const struct device *dev, const uint8_t reg, uint8_t *value) +{ + return pmw33xx_access(dev, reg & PMW33XX_RD_MASK, value); +} +static int pmw33xx_write_reg(const struct device *dev, const uint8_t reg, const uint8_t value) +{ + uint8_t v = value; + return pmw33xx_access(dev, reg | PMW33XX_WR_MASK, &v); +} +static int pmw33xx_write_srom(const struct device *dev) +{ + struct pmw33xx_data *data = dev->data; + const struct pmw33xx_config *cfg = dev->config; + const struct spi_config *spi_cfg = &cfg->bus_cfg.spi_cfg->spi_conf; + const struct pmw33xx_gpio_dt_spec *cs_gpio_cfg = &cfg->bus_cfg.spi_cfg->cs_spec; + uint8_t access[1] = { PMW33XX_REG_SROM_BURST | PMW33XX_WR_MASK }; + struct spi_buf_set tx = { + .buffers = { &(struct spi_buf){ + .buf = access, + .len = 1, + } }, + .count = 1, + }; + + pmw33xx_write_reg(dev, PMW33XX_REG_SROM_EN, PMW33XX_SROM_DWNLD_CMD); + k_sleep(K_USEC(15)); + pmw33xx_write_reg(dev, PMW33XX_REG_SROM_EN, PMW33XX_SROM_DWNLD_START_CMD); + + pmw33xx_cs_select(cs_gpio_cfg, 0); + + int err = spi_write(data->bus, spi_cfg, &tx); + + k_sleep(K_USEC(15)); + if (err) { + pmw33xx_cs_select(cs_gpio_cfg, 1); + return err; + } + + for (uint16_t i = 0; i < sizeof(SROM); i++) { + access[0] = SROM[i]; + err = spi_write(data->bus, spi_cfg, &tx); + k_sleep(K_USEC(15)); + if (err) { + pmw33xx_cs_select(cs_gpio_cfg, 1); + return err; + } + } + + pmw33xx_cs_select(cs_gpio_cfg, 1); + k_sleep(K_MSEC(2)); //Tbexit + return err; +} + +static int pmw33xx_read_motion_burst(const struct device *dev, struct pmw33xx_motion_burst *burst) +{ + struct pmw33xx_data *data = dev->data; + const struct pmw33xx_config *cfg = dev->config; + const struct spi_config *spi_cfg = &cfg->bus_cfg.spi_cfg->spi_conf; + const struct pmw33xx_gpio_dt_spec *cs_gpio_cfg = &cfg->bus_cfg.spi_cfg->cs_spec; + + uint8_t access[1] = { PMW33XX_REG_BURST }; + struct spi_buf_set tx = { + .buffers = { &(struct spi_buf){ + .buf = access, + .len = 1, + } }, + .count = 1, + }; + struct spi_buf_set rx = { + .buffers = { &(struct spi_buf){ + .buf = (uint8_t *)burst, + .len = sizeof(struct pmw33xx_motion_burst), + } }, + .count = 1, + }; + + pmw33xx_cs_select(cs_gpio_cfg, 0); + + int err = spi_write(data->bus, spi_cfg, &tx); + k_sleep(K_USEC(35)); // tsrad motbr + if (err) { + pmw33xx_cs_select(cs_gpio_cfg, 1); + return err; + } + err = spi_read(data->bus, spi_cfg, &rx); + pmw33xx_cs_select(cs_gpio_cfg, 1); + return err; +} + +// converts twos complement data to an int16 +static int16_t pmw33xx_raw_to_int16(const uint8_t src[2]) +{ + int16_t res = sys_get_be16(src); + if (res > BIT_MASK(15)) + res -= BIT(16); + return res; +} + +static int pmw33xx_read_raw(const struct device *dev, const uint8_t reg_high, const uint8_t reg_low, + int16_t *value) +{ + uint8_t raw[2] = { 0x0, 0x0 }; + int err; + err = pmw33xx_read_reg(dev, reg_high, &raw[0]); + if (err) { + LOG_ERR("could not read high byte at %x", reg_high); + return err; + } + k_sleep(K_USEC(100)); + err = pmw33xx_read_reg(dev, reg_low, &raw[1]); + if (err) { + LOG_ERR("could not read low byte at %x", reg_low); + return err; + } + k_sleep(K_USEC(100)); + *value = pmw33xx_raw_to_int16(raw); + return 0; +} + +int pmw33xx_spi_init(const struct device *dev) +{ + struct pmw33xx_data *data = dev->data; + const struct pmw33xx_config *cfg = dev->config; + const struct pmw33xx_spi_cfg *spi_cfg = cfg->bus_cfg.spi_cfg; + const struct pmw33xx_gpio_dt_spec *cs_gpio_cfg = &cfg->bus_cfg.spi_cfg->cs_spec; + + int err; + err = gpio_pin_configure(cs_gpio_cfg->port, cs_gpio_cfg->pin, GPIO_OUTPUT_ACTIVE); + if (err) { + LOG_ERR("could configure cs pin %d", err); + return -EIO; + } + return 0; +} + +static int pmw33xx_sample_fetch(const struct device *dev, enum sensor_channel chan) +{ + struct pmw33xx_data *data = dev->data; + const struct pmw33xx_config *cfg = dev->config; + struct pmw33xx_motion_burst burst; + + if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_POS_DX && chan != SENSOR_CHAN_POS_DY) + return -ENOTSUP; + + int err = pmw33xx_read_motion_burst(dev, &burst); + if (err) { + return err; + } + if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_POS_DX) + data->dx = burst.dx; + if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_POS_DY) + data->dy = burst.dy; + return 0; +} + +static int pmw33xx_channel_get(const struct device *dev, enum sensor_channel chan, + struct sensor_value *val) +{ + struct pmw33xx_data *data = dev->data; + const struct pmw33xx_config *cfg = dev->config; + + switch (chan) { + case SENSOR_CHAN_POS_DX: + val->val1 = data->dx; + data->dx = 0; + break; + case SENSOR_CHAN_POS_DY: + val->val1 = data->dy; + data->dy = 0; + break; + default: + return -ENOTSUP; + } + + return 0; +} + +static const struct sensor_driver_api pmw33xx_driver_api = { +#ifdef CONFIG_PMW33XX_TRIGGER + .trigger_set = pmw33xx_trigger_set, +#endif + // .attr_set = pmw33xx_attr_set, + .sample_fetch = pmw33xx_sample_fetch, + .channel_get = pmw33xx_channel_get, +}; + +static int pmw33xx_init_chip(const struct device *dev) +{ + struct pmw33xx_data *data = dev->data; + return 0; +} + +static int pmw33xx_init(const struct device *dev) +{ + const struct pmw33xx_config *const config = dev->config; + struct pmw33xx_data *data = dev->data; + + data->bus = device_get_binding(config->bus_name); + if (!data->bus) { + LOG_DBG("master not found: %s", log_strdup(config->bus_name)); + return -EINVAL; + } + + config->bus_init(dev); + + if (pmw33xx_init_chip(dev) < 0) { + LOG_DBG("failed to initialize chip"); + return -EIO; + } + + const struct pmw33xx_gpio_dt_spec *cs_gpio_cfg = &config->bus_cfg.spi_cfg->cs_spec; + +#ifdef CONFIG_PMW33XX_TRIGGER + if (pmw33xx_init_interrupt(dev) < 0) { + LOG_DBG("Failed to initialize interrupt!"); + return -EIO; + } +#endif + + pmw33xx_cs_select(cs_gpio_cfg, 1); + k_sleep(K_MSEC(1)); + + int err = pmw33xx_write_reg(dev, PMW33XX_REG_PWR_UP_RST, PMW33XX_RESET_CMD); + if (err) { + LOG_ERR("could not reset %d", err); + return -EIO; + } + uint8_t pid = 0x0; + err = pmw33xx_read_reg(dev, PMW33XX_REG_PID, &pid); + if (err) { + LOG_ERR("could not reset %d", err); + return -EIO; + } + if (pid != PMW33XX_PID) { + LOG_ERR("pid does not match expected: got (%x), expected(%x)", pid, PMW33XX_PID); + return -EIO; + } + pmw33xx_write_reg(dev, PMW33XX_REG_CONFIG2, 0x00); // clear rest enable + + err = pmw33xx_write_srom(dev); + if (err) { + LOG_ERR("could not upload srom %d", err); + return -EIO; + } + uint8_t srom_run = 0x0; + err = pmw33xx_read_reg(dev, PMW33XX_REG_OBSERVATION, &srom_run); + if (err) { + LOG_ERR("could not check srom status %d", err); + return -EIO; + } + if (!(srom_run & PMW33XX_SROM_RUN)) { + LOG_ERR("srom status invalid %d", srom_run); + return -EIO; + } + + uint8_t srom_id = 0x0; + err = pmw33xx_read_reg(dev, PMW33XX_REG_SROM_ID, &srom_id); + if (err) { + LOG_ERR("could not check srom id %d", err); + return -EIO; + } + if (!srom_id) { + LOG_ERR("srom id invalid %d", srom_id); + return -EIO; + } + + pmw33xx_write_reg(dev, PMW33XX_REG_CONFIG2, 0x00); // clear rest enable + pmw33xx_write_reg(dev, PMW33XX_REG_BURST, 0x01); // clear rest enable + struct pmw33xx_motion_burst val; + pmw33xx_read_motion_burst(dev, &val); // read and throwout initial motion data + + return 0; +} + +#define PMW33XX_DATA_SPI(n) \ + { \ + .cs_ctrl = {}, \ + } + +#define PMW33XX_SPI_CFG(n) \ + (&(struct pmw33xx_spi_cfg){ \ + .spi_conf = \ + { \ + .frequency = DT_INST_PROP(n, spi_max_frequency), \ + .operation = \ + (SPI_WORD_SET(8) | SPI_OP_MODE_MASTER | SPI_MODE_CPOL | SPI_MODE_CPHA), \ + .slave = DT_INST_REG_ADDR(n), \ + }, \ + .cs_spec = PMW33XX_GPIO_DT_SPEC_GET_BY_IDX(DT_DRV_INST(n), cs_gpios, 0), \ + }) + +#define PMW33XX_GPIO_DT_SPEC_GET_BY_IDX(node_id, prop, idx) \ + { \ + .port = DEVICE_DT_GET(DT_GPIO_CTLR_BY_IDX(node_id, prop, idx)), \ + .pin = DT_GPIO_PIN_BY_IDX(node_id, prop, idx), \ + .dt_flags = DT_GPIO_FLAGS_BY_IDX(node_id, prop, idx), \ + } + +#define PMW33XX_CONFIG_SPI(n) \ + { \ + .bus_name = DT_INST_BUS_LABEL(n), .bus_init = pmw33xx_spi_init, \ + .bus_cfg = { .spi_cfg = PMW33XX_SPI_CFG(n) }, \ + COND_CODE_1(CONFIG_MA730_TRIGGER, \ + (, PMW33XX_GPIO_DT_SPEC_GET_BY_IDX(DT_DRV_INST(n), motswk_gpios, 0)), \ + ()) \ + } + +#define PMW33XX_INST(n) \ + static struct pmw33xx_data pmw33xx_data_##n = PMW33XX_DATA_SPI(n); \ + static const struct pmw33xx_config pmw33xx_cfg_##n = PMW33XX_CONFIG_SPI(n); \ + DEVICE_DT_INST_DEFINE(n, pmw33xx_init, device_pm_control_nop, &pmw33xx_data_##n, \ + &pmw33xx_cfg_##n, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \ + &pmw33xx_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(PMW33XX_INST) diff --git a/app/drivers/sensor/pmw33xx/pmw33xx.h b/app/drivers/sensor/pmw33xx/pmw33xx.h new file mode 100644 index 00000000000..31b28944fb5 --- /dev/null +++ b/app/drivers/sensor/pmw33xx/pmw33xx.h @@ -0,0 +1,136 @@ +#ifndef ZEPHYR_DRIVERS_SENSOR_PIXART_PMW33XX_H_ +#define ZEPHYR_DRIVERS_SENSOR_PIXART_PMW33XX_H_ + +#include +#include +#include +#include +#include + +#define PMW33XX_WR_MASK 0x80 +#define PMW33XX_RD_MASK 0x7F + +#define PMW33XX_3389_PID 0x47 +#define PMW33XX_3360_PID 0x45 +#define PMW33XX_REV 0x01 + +/* General Registers */ +#define PMW33XX_REG_PID 0x00 +#define PMW33XX_REG_REV_ID 0x01 +#define PMW33XX_REG_PWR_UP_RST 0x3A + +/* Motion Registers */ +#define PMW33XX_REG_MOTION 0x02 +#define PMW33XX_REG_DX_L 0x03 +#define PMW33XX_REG_DX_H 0x04 +#define PMW33XX_REG_DY_L 0x05 +#define PMW33XX_REG_DY_H 0x06 +#define PMW33XX_REG_BURST 0x50 + +/* Motion bits */ +#define PMW33XX_MOTION (1 << 8) +#define PMW33XX_OPMODE_RUN (0) +#define PMW33XX_OPMODE_REST1 (0b01 << 1) +#define PMW33XX_OPMODE_REST2 (0b10 << 1) +#define PMW33XX_OPMODE_REST3 (0b11 << 1) + +/* SROM Registers */ +#define PMW33XX_REG_SROM_EN 0x13 +#define PMW33XX_REG_SROM_ID 0x2A +#define PMW33XX_REG_SROM_BURST 0x62 + +/* SROM CMDs */ +#define PMW33XX_SROM_CRC_CMD 0x15 +#define PMW33XX_SROM_DWNLD_CMD 0x1D +#define PMW33XX_SROM_DWNLD_START_CMD 0x18 + +/* CPI Registers */ +#define PMW33XX_3360_REG_CPI 0x0F +#define PMW33XX_3389_REG_CPI_L 0x0E +#define PMW33XX_3389_REG_CPI_H 0x0F + +/* Config Registers */ +#define PMW33XX_REG_CONFIG2 0x10 +#define PMW33XX_REG_OBSERVATION 0x24 +#define PMW33XX_REG_DOUT_L 0x25 +#define PMW33XX_REG_DOUT_H 0x26 + +/* Config2 Bits */ +#define PMW33XX_RESTEN 0x20 +#define PMW33XX_RPT_MOD 0x04 + +/* Observation Bits */ +#define PMW33XX_SROM_RUN 0x40 + +/* power up reset cmd */ +#define PMW33XX_RESET_CMD 0x5A + +struct pmw33xx_gpio_dt_spec { + const struct device *port; + gpio_pin_t pin; + gpio_dt_flags_t dt_flags; +}; + +struct pmw33xx_spi_cfg { + struct spi_config spi_conf; + struct pmw33xx_gpio_dt_spec cs_spec; +}; + +union pmw33xx_bus_cfg { + struct pmw33xx_spi_cfg *spi_cfg; +}; + +struct pmw33xx_config { + char *bus_name; + int (*bus_init)(const struct device *dev); + const union pmw33xx_bus_cfg bus_cfg; + int resolution; +#if CONFIG_PMW33XX_TRIGGER + struct pmw33xx_gpio_dt_spec motswk_spec; +#endif // CONFIG_PMW33XX_TRIGGER +}; + +struct pmw33xx_data; + +struct pmw33xx_transfer_function { + int (*read_data)(const struct device *dev, uint16_t *value); +}; + +struct pmw33xx_data { + const struct device *bus; + struct spi_cs_control cs_ctrl; + + int16_t dx; + int16_t dy; + + const struct pmw33xx_transfer_function *hw_tf; + +#ifdef CONFIG_PMW33XX_TRIGGER + + struct gpio_callback motswk_gpio_cb; + const struct device *dev; + + sensor_trigger_handler_t handler; + const struct sensor_trigger *trigger; + +#if defined(CONFIG_PMW33XX_TRIGGER_OWN_THREAD) + K_THREAD_STACK_MEMBER(thread_stack, CONFIG_PMW33XX_THREAD_STACK_SIZE); + struct k_sem gpio_sem; + struct k_thread thread; +#elif defined(CONFIG_PMW33XX_TRIGGER_GLOBAL_THREAD) + struct k_work work; +#endif + +#endif /* CONFIG_PMW33XX_TRIGGER */ +}; + +int pmw33xx_spi_init(const struct device *dev); +#ifdef CONFIG_PMW33XX_TRIGGER + +int pmw33xx_trigger_set(const struct device *dev, const struct sensor_trigger *trig, + sensor_trigger_handler_t handler); + +int pmw33xx_init_interrupt(const struct device *dev); +#endif + +#endif /* ZEPHYR_DRIVERS_SENSOR_PIXART_PMW33XX_H_ */ diff --git a/app/drivers/sensor/pmw33xx/pmw33xx_trigger.c b/app/drivers/sensor/pmw33xx/pmw33xx_trigger.c new file mode 100644 index 00000000000..9451a330d5f --- /dev/null +++ b/app/drivers/sensor/pmw33xx/pmw33xx_trigger.c @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#define DT_DRV_COMPAT pixart_pmw3389 + +#include +#include +#include +#include +#include + +#include "pmw3389.h" + +// extern struct pmw3389_data pmw3389_driver; + +#include +LOG_MODULE_DECLARE(PMW3389, CONFIG_SENSOR_LOG_LEVEL); + +static inline void setup_int(const struct device *dev, bool enable) +{ + struct pmw3389_data *data = dev->data; + const struct pmw3389_config *cfg = dev->config; + + if (gpio_pin_interrupt_configure(cfg->motswk_spec.port, cfg->motswk_spec.pin, + enable ? GPIO_INT_EDGE_TO_ACTIVE : GPIO_INT_DISABLE)) { + LOG_WRN("Unable to set MOTSWK GPIO interrupt"); + } +} + +static void pmw3389_motswk_gpio_callback(const struct device *dev, struct gpio_callback *cb, + uint32_t pins) +{ + struct pmw3389_data *drv_data = CONTAINER_OF(cb, struct pmw3389_data, motswk_gpio_cb); + + LOG_DBG(""); + + setup_int(drv_data->dev, false); + +#if defined(CONFIG_PMW3389_TRIGGER_OWN_THREAD) + k_sem_give(&drv_data->gpio_sem); +#elif defined(CONFIG_PMW3389_TRIGGER_GLOBAL_THREAD) + k_work_submit(&drv_data->work); +#endif +} + +static void pmw3389_thread_cb(const struct device *dev) +{ + struct pmw3389_data *drv_data = dev->data; + + LOG_DBG("%p", drv_data->handler); + drv_data->handler(dev, drv_data->trigger); + + // Enable once the wall/spam of interrupts is solved + setup_int(dev, true); +} + +#ifdef CONFIG_PMW3389_TRIGGER_OWN_THREAD +static void pmw3389_thread(int dev_ptr, int unused) +{ + const struct device *dev = INT_TO_POINTER(dev_ptr); + struct pmw3389_data *drv_data = dev->data; + + ARG_UNUSED(unused); + + while (1) { + k_sem_take(&drv_data->gpio_sem, K_FOREVER); + pmw3389_thread_cb(dev); + } +} +#endif + +#ifdef CONFIG_PMW3389_TRIGGER_GLOBAL_THREAD +static void pmw3389_work_cb(struct k_work *work) +{ + struct pmw3389_data *drv_data = CONTAINER_OF(work, struct pmw3389_data, work); + + LOG_DBG(""); + + pmw3389_thread_cb(drv_data->dev); +} +#endif + +int pmw3389_trigger_set(const struct device *dev, const struct sensor_trigger *trig, + sensor_trigger_handler_t handler) +{ + struct pmw3389_data *drv_data = dev->data; + + setup_int(dev, false); + + k_msleep(5); + + drv_data->trigger = trig; + drv_data->handler = handler; + + setup_int(dev, true); + + return 0; +} + +int pmw3389_init_interrupt(const struct device *dev) +{ + struct pmw3389_data *drv_data = dev->data; + const struct pmw3389_config *drv_cfg = dev->config; + + drv_data->dev = dev; + /* setup gpio interrupt */ + + gpio_init_callback(&drv_data->motswk_gpio_cb, pmw3389_motswk_gpio_callback, + BIT(drv_cfg->motswk_spec.pin)); + + if (gpio_add_callback(drv_cfg->motswk_spec.port, &drv_data->motswk_gpio_cb) < 0) { + LOG_DBG("Failed to set MOTSWK callback!"); + return -EIO; + } + +#if defined(CONFIG_PMW3389_TRIGGER_OWN_THREAD) + k_sem_init(&drv_data->gpio_sem, 0, UINT_MAX); + + k_thread_create(&drv_data->thread, drv_data->thread_stack, CONFIG_PMW3389_THREAD_STACK_SIZE, + (k_thread_entry_t)pmw3389_thread, dev, 0, NULL, + K_PRIO_COOP(CONFIG_PMW3389_THREAD_PRIORITY), 0, K_NO_WAIT); +#elif defined(CONFIG_PMW3389_TRIGGER_GLOBAL_THREAD) + k_work_init(&drv_data->work, pmw3389_work_cb); +#endif + + return 0; +} diff --git a/app/drivers/zephyr/dts/bindings/sensor/pixart,pmw3389.yaml b/app/drivers/zephyr/dts/bindings/sensor/pixart,pmw33xx.yaml similarity index 50% rename from app/drivers/zephyr/dts/bindings/sensor/pixart,pmw3389.yaml rename to app/drivers/zephyr/dts/bindings/sensor/pixart,pmw33xx.yaml index 1b9fc50eb6b..91f48a00180 100644 --- a/app/drivers/zephyr/dts/bindings/sensor/pixart,pmw3389.yaml +++ b/app/drivers/zephyr/dts/bindings/sensor/pixart,pmw33xx.yaml @@ -1,6 +1,6 @@ description: | - Sensor driver for the pixart PMW3389 optical mouse sensor -compatible: "pixart,pmw3389" + Sensor driver for the pixart PMW33XX optical mouse sensor supports 3360 built in, and 3389 with external srom +compatible: "pixart,pmw33xx" include: spi-device.yaml @@ -8,15 +8,15 @@ properties: label: type: string required: true + cs-gpios: + type: phandle-array + required: true + description: chip select pin for the sensor motswk-gpios: type: phandle-array required: false description: interrupt pin for motion - reset-gpios: - type: phandle-array - required: false - description: A pin for the encoder - resolution: + cpi: type: int - description: mouse resolution - required: false \ No newline at end of file + description: mouse cpi + required: false From ab7673f26817e7eec0978632f39cb19bc36beaba Mon Sep 17 00:00:00 2001 From: "Dylan (Luberry) Kozicki" Date: Sun, 17 Apr 2022 10:17:48 -0400 Subject: [PATCH 005/157] warning cleanup, and ability to set cpi via the device tree --- app/drivers/sensor/pmw33xx/pmw33xx.c | 616 +++++++++---------- app/drivers/sensor/pmw33xx/pmw33xx.h | 7 +- app/drivers/sensor/pmw33xx/pmw33xx_trigger.c | 144 +++-- 3 files changed, 373 insertions(+), 394 deletions(-) diff --git a/app/drivers/sensor/pmw33xx/pmw33xx.c b/app/drivers/sensor/pmw33xx/pmw33xx.c index 1d07d2efeda..af016781722 100644 --- a/app/drivers/sensor/pmw33xx/pmw33xx.c +++ b/app/drivers/sensor/pmw33xx/pmw33xx.c @@ -1,5 +1,3 @@ - - #define DT_DRV_COMPAT pixart_pmw33xx #include @@ -22,344 +20,326 @@ LOG_MODULE_REGISTER(PMW33XX, CONFIG_SENSOR_LOG_LEVEL); #define PMW33XX_PID COND_CODE_1(CONFIG_PMW33XX_3389, (PMW33XX_3389_PID), (PMW33XX_3360_PID)) +#define PMW33XX_CPI_MAX \ + COND_CODE_1(CONFIG_PMW33XX_3389, (PMW33XX_3389_CPI_MAX), (PMW33XX_3360_CPI_MAX)) +#define PMW33XX_CPI_MIN \ + COND_CODE_1(CONFIG_PMW33XX_3389, (PMW33XX_3389_CPI_MIN), (PMW33XX_3360_CPI_MIN)) struct pmw33xx_motion_burst { - uint8_t motion; - uint8_t observation; - int16_t dx; - int16_t dy; + uint8_t motion; + uint8_t observation; + int16_t dx; + int16_t dy; } __attribute__((packed)); static inline int pmw33xx_cs_select(const struct pmw33xx_gpio_dt_spec *cs_gpio_cfg, - const uint8_t value) -{ - return gpio_pin_set(cs_gpio_cfg->port, cs_gpio_cfg->pin, value); + const uint8_t value) { + return gpio_pin_set(cs_gpio_cfg->port, cs_gpio_cfg->pin, value); } -static int pmw33xx_access(const struct device *dev, const uint8_t reg, uint8_t *value) -{ - struct pmw33xx_data *data = dev->data; - const struct pmw33xx_config *cfg = dev->config; - const struct spi_config *spi_cfg = &cfg->bus_cfg.spi_cfg->spi_conf; - const struct pmw33xx_gpio_dt_spec *cs_gpio_cfg = &cfg->bus_cfg.spi_cfg->cs_spec; - - uint8_t access[1] = { reg }; - struct spi_buf_set tx = { - .buffers = { &(struct spi_buf){ - .buf = access, - .len = 1, - } }, - .count = 1, - }; - uint8_t result[1] = { *value }; - struct spi_buf_set rx = { - .buffers = { &(struct spi_buf){ - .buf = result, - .len = 1, - } }, - .count = 1, - }; - - pmw33xx_cs_select(cs_gpio_cfg, 0); - - int err = spi_write(data->bus, spi_cfg, &tx); - k_sleep(K_USEC(120)); //Tsrad - if (err) { - pmw33xx_cs_select(cs_gpio_cfg, 1); - return err; - } - - if ((reg & PMW33XX_WR_MASK)) - err = spi_write(data->bus, spi_cfg, &rx); - else - err = spi_read(data->bus, spi_cfg, &rx); - pmw33xx_cs_select(cs_gpio_cfg, 1); - k_sleep(K_USEC(160)); - if ((reg & PMW33XX_WR_MASK) == 0) - *value = result[0]; - return err; +static int pmw33xx_access(const struct device *dev, const uint8_t reg, uint8_t *value) { + struct pmw33xx_data *data = dev->data; + const struct pmw33xx_config *cfg = dev->config; + const struct spi_config *spi_cfg = &cfg->bus_cfg.spi_cfg->spi_conf; + const struct pmw33xx_gpio_dt_spec *cs_gpio_cfg = &cfg->bus_cfg.spi_cfg->cs_spec; + + uint8_t access[1] = {reg}; + struct spi_buf_set tx = { + .buffers = + &(struct spi_buf){ + .buf = access, + .len = 1, + }, + .count = 1, + }; + uint8_t result[1] = {*value}; + struct spi_buf_set rx = { + .buffers = + &(struct spi_buf){ + .buf = result, + .len = 1, + }, + .count = 1, + }; + + pmw33xx_cs_select(cs_gpio_cfg, 0); + + int err = spi_write(data->bus, spi_cfg, &tx); + k_sleep(K_USEC(120)); // Tsrad + if (err) { + pmw33xx_cs_select(cs_gpio_cfg, 1); + return err; + } + + if ((reg & PMW33XX_WR_MASK)) + err = spi_write(data->bus, spi_cfg, &rx); + else + err = spi_read(data->bus, spi_cfg, &rx); + pmw33xx_cs_select(cs_gpio_cfg, 1); + k_sleep(K_USEC(160)); + if ((reg & PMW33XX_WR_MASK) == 0) + *value = result[0]; + return err; } -static int pmw33xx_read_reg(const struct device *dev, const uint8_t reg, uint8_t *value) -{ - return pmw33xx_access(dev, reg & PMW33XX_RD_MASK, value); +static int pmw33xx_read_reg(const struct device *dev, const uint8_t reg, uint8_t *value) { + return pmw33xx_access(dev, reg & PMW33XX_RD_MASK, value); } -static int pmw33xx_write_reg(const struct device *dev, const uint8_t reg, const uint8_t value) -{ - uint8_t v = value; - return pmw33xx_access(dev, reg | PMW33XX_WR_MASK, &v); +static int pmw33xx_write_reg(const struct device *dev, const uint8_t reg, const uint8_t value) { + uint8_t v = value; + return pmw33xx_access(dev, reg | PMW33XX_WR_MASK, &v); } -static int pmw33xx_write_srom(const struct device *dev) -{ - struct pmw33xx_data *data = dev->data; - const struct pmw33xx_config *cfg = dev->config; - const struct spi_config *spi_cfg = &cfg->bus_cfg.spi_cfg->spi_conf; - const struct pmw33xx_gpio_dt_spec *cs_gpio_cfg = &cfg->bus_cfg.spi_cfg->cs_spec; - uint8_t access[1] = { PMW33XX_REG_SROM_BURST | PMW33XX_WR_MASK }; - struct spi_buf_set tx = { - .buffers = { &(struct spi_buf){ - .buf = access, - .len = 1, - } }, - .count = 1, - }; - - pmw33xx_write_reg(dev, PMW33XX_REG_SROM_EN, PMW33XX_SROM_DWNLD_CMD); - k_sleep(K_USEC(15)); - pmw33xx_write_reg(dev, PMW33XX_REG_SROM_EN, PMW33XX_SROM_DWNLD_START_CMD); - - pmw33xx_cs_select(cs_gpio_cfg, 0); - - int err = spi_write(data->bus, spi_cfg, &tx); - - k_sleep(K_USEC(15)); - if (err) { - pmw33xx_cs_select(cs_gpio_cfg, 1); - return err; - } - - for (uint16_t i = 0; i < sizeof(SROM); i++) { - access[0] = SROM[i]; - err = spi_write(data->bus, spi_cfg, &tx); - k_sleep(K_USEC(15)); - if (err) { - pmw33xx_cs_select(cs_gpio_cfg, 1); - return err; - } - } - - pmw33xx_cs_select(cs_gpio_cfg, 1); - k_sleep(K_MSEC(2)); //Tbexit - return err; +static int pmw33xx_write_srom(const struct device *dev) { + struct pmw33xx_data *data = dev->data; + const struct pmw33xx_config *cfg = dev->config; + const struct spi_config *spi_cfg = &cfg->bus_cfg.spi_cfg->spi_conf; + const struct pmw33xx_gpio_dt_spec *cs_gpio_cfg = &cfg->bus_cfg.spi_cfg->cs_spec; + uint8_t access[1] = {PMW33XX_REG_SROM_BURST | PMW33XX_WR_MASK}; + struct spi_buf_set tx = { + .buffers = + &(struct spi_buf){ + .buf = access, + .len = 1, + }, + .count = 1, + }; + + pmw33xx_write_reg(dev, PMW33XX_REG_SROM_EN, PMW33XX_SROM_DWNLD_CMD); + k_sleep(K_USEC(15)); + pmw33xx_write_reg(dev, PMW33XX_REG_SROM_EN, PMW33XX_SROM_DWNLD_START_CMD); + + pmw33xx_cs_select(cs_gpio_cfg, 0); + + int err = spi_write(data->bus, spi_cfg, &tx); + + k_sleep(K_USEC(15)); + if (err) { + pmw33xx_cs_select(cs_gpio_cfg, 1); + return err; + } + + for (uint16_t i = 0; i < sizeof(SROM); i++) { + access[0] = SROM[i]; + err = spi_write(data->bus, spi_cfg, &tx); + k_sleep(K_USEC(15)); + if (err) { + pmw33xx_cs_select(cs_gpio_cfg, 1); + return err; + } + } + + pmw33xx_cs_select(cs_gpio_cfg, 1); + k_sleep(K_MSEC(2)); // Tbexit + return err; } -static int pmw33xx_read_motion_burst(const struct device *dev, struct pmw33xx_motion_burst *burst) -{ - struct pmw33xx_data *data = dev->data; - const struct pmw33xx_config *cfg = dev->config; - const struct spi_config *spi_cfg = &cfg->bus_cfg.spi_cfg->spi_conf; - const struct pmw33xx_gpio_dt_spec *cs_gpio_cfg = &cfg->bus_cfg.spi_cfg->cs_spec; - - uint8_t access[1] = { PMW33XX_REG_BURST }; - struct spi_buf_set tx = { - .buffers = { &(struct spi_buf){ - .buf = access, - .len = 1, - } }, - .count = 1, - }; - struct spi_buf_set rx = { - .buffers = { &(struct spi_buf){ - .buf = (uint8_t *)burst, - .len = sizeof(struct pmw33xx_motion_burst), - } }, - .count = 1, - }; - - pmw33xx_cs_select(cs_gpio_cfg, 0); - - int err = spi_write(data->bus, spi_cfg, &tx); - k_sleep(K_USEC(35)); // tsrad motbr - if (err) { - pmw33xx_cs_select(cs_gpio_cfg, 1); - return err; - } - err = spi_read(data->bus, spi_cfg, &rx); - pmw33xx_cs_select(cs_gpio_cfg, 1); - return err; +static int pmw33xx_read_motion_burst(const struct device *dev, struct pmw33xx_motion_burst *burst) { + struct pmw33xx_data *data = dev->data; + const struct pmw33xx_config *cfg = dev->config; + const struct spi_config *spi_cfg = &cfg->bus_cfg.spi_cfg->spi_conf; + const struct pmw33xx_gpio_dt_spec *cs_gpio_cfg = &cfg->bus_cfg.spi_cfg->cs_spec; + + uint8_t access[1] = {PMW33XX_REG_BURST}; + struct spi_buf_set tx = { + .buffers = + &(struct spi_buf){ + .buf = access, + .len = 1, + }, + .count = 1, + }; + struct spi_buf_set rx = { + .buffers = + &(struct spi_buf){ + .buf = (uint8_t *)burst, + .len = sizeof(struct pmw33xx_motion_burst), + }, + .count = 1, + }; + + pmw33xx_cs_select(cs_gpio_cfg, 0); + + int err = spi_write(data->bus, spi_cfg, &tx); + k_sleep(K_USEC(35)); // tsrad motbr + if (err) { + pmw33xx_cs_select(cs_gpio_cfg, 1); + return err; + } + err = spi_read(data->bus, spi_cfg, &rx); + pmw33xx_cs_select(cs_gpio_cfg, 1); + return err; } -// converts twos complement data to an int16 -static int16_t pmw33xx_raw_to_int16(const uint8_t src[2]) -{ - int16_t res = sys_get_be16(src); - if (res > BIT_MASK(15)) - res -= BIT(16); - return res; +int pmw33xx_spi_init(const struct device *dev) { + const struct pmw33xx_config *cfg = dev->config; + const struct pmw33xx_gpio_dt_spec *cs_gpio_cfg = &cfg->bus_cfg.spi_cfg->cs_spec; + + int err; + err = gpio_pin_configure(cs_gpio_cfg->port, cs_gpio_cfg->pin, GPIO_OUTPUT_ACTIVE); + if (err) { + LOG_ERR("could configure cs pin %d", err); + return -EIO; + } + return 0; } -static int pmw33xx_read_raw(const struct device *dev, const uint8_t reg_high, const uint8_t reg_low, - int16_t *value) -{ - uint8_t raw[2] = { 0x0, 0x0 }; - int err; - err = pmw33xx_read_reg(dev, reg_high, &raw[0]); - if (err) { - LOG_ERR("could not read high byte at %x", reg_high); - return err; - } - k_sleep(K_USEC(100)); - err = pmw33xx_read_reg(dev, reg_low, &raw[1]); - if (err) { - LOG_ERR("could not read low byte at %x", reg_low); - return err; - } - k_sleep(K_USEC(100)); - *value = pmw33xx_raw_to_int16(raw); - return 0; -} - -int pmw33xx_spi_init(const struct device *dev) -{ - struct pmw33xx_data *data = dev->data; - const struct pmw33xx_config *cfg = dev->config; - const struct pmw33xx_spi_cfg *spi_cfg = cfg->bus_cfg.spi_cfg; - const struct pmw33xx_gpio_dt_spec *cs_gpio_cfg = &cfg->bus_cfg.spi_cfg->cs_spec; - - int err; - err = gpio_pin_configure(cs_gpio_cfg->port, cs_gpio_cfg->pin, GPIO_OUTPUT_ACTIVE); - if (err) { - LOG_ERR("could configure cs pin %d", err); - return -EIO; - } - return 0; -} - -static int pmw33xx_sample_fetch(const struct device *dev, enum sensor_channel chan) -{ - struct pmw33xx_data *data = dev->data; - const struct pmw33xx_config *cfg = dev->config; - struct pmw33xx_motion_burst burst; - - if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_POS_DX && chan != SENSOR_CHAN_POS_DY) - return -ENOTSUP; - - int err = pmw33xx_read_motion_burst(dev, &burst); - if (err) { - return err; - } - if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_POS_DX) - data->dx = burst.dx; - if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_POS_DY) - data->dy = burst.dy; - return 0; +static int pmw33xx_sample_fetch(const struct device *dev, enum sensor_channel chan) { + struct pmw33xx_data *data = dev->data; + struct pmw33xx_motion_burst burst; + + if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_POS_DX && chan != SENSOR_CHAN_POS_DY) + return -ENOTSUP; + + int err = pmw33xx_read_motion_burst(dev, &burst); + if (err) { + return err; + } + if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_POS_DX) + data->dx += burst.dx; + if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_POS_DY) + data->dy += burst.dy; + return 0; } static int pmw33xx_channel_get(const struct device *dev, enum sensor_channel chan, - struct sensor_value *val) -{ - struct pmw33xx_data *data = dev->data; - const struct pmw33xx_config *cfg = dev->config; - - switch (chan) { - case SENSOR_CHAN_POS_DX: - val->val1 = data->dx; - data->dx = 0; - break; - case SENSOR_CHAN_POS_DY: - val->val1 = data->dy; - data->dy = 0; - break; - default: - return -ENOTSUP; - } - - return 0; + struct sensor_value *val) { + struct pmw33xx_data *data = dev->data; + + switch (chan) { + case SENSOR_CHAN_POS_DX: + val->val1 = data->dx; + data->dx = 0; + break; + case SENSOR_CHAN_POS_DY: + val->val1 = data->dy; + data->dy = 0; + break; + default: + return -ENOTSUP; + } + + return 0; } static const struct sensor_driver_api pmw33xx_driver_api = { #ifdef CONFIG_PMW33XX_TRIGGER - .trigger_set = pmw33xx_trigger_set, + .trigger_set = pmw33xx_trigger_set, #endif - // .attr_set = pmw33xx_attr_set, - .sample_fetch = pmw33xx_sample_fetch, - .channel_get = pmw33xx_channel_get, + // eventually implement this to allow setting cpi from the driver api maybe + // .attr_set = pmw33xx_attr_set, + .sample_fetch = pmw33xx_sample_fetch, + .channel_get = pmw33xx_channel_get, }; -static int pmw33xx_init_chip(const struct device *dev) -{ - struct pmw33xx_data *data = dev->data; - return 0; +static int pmw33xx_set_cpi(const struct device *dev, uint16_t cpi) { +#ifdef CONFIG_PMW33XX_3360 + if (cpi > PMW33XX_CPI_MIN && cpi < PMW33XX_CPI_MAX) + return pmw33xx_write_reg(dev, PMW33XX_3360_REG_CPI, + ((cpi / 100) - 1)); /* 100-12000, 100 cpi LSb */ +#elif CONFIG_PMW33XX_3389 + if (cpi > PMW33XX_CPI_MIN && cpi < PMW33XX_CPI_MAX) { + cpi /= 50; + int err = + pmw33xx_write_reg(dev, PMW33XX_3389_REG_CPI_L, cpi & 0xFF); /* 50-16000, 50 cpi LSb */ + if (err) { + return err; + } + return pmw33xx_write_reg(dev, PMW33XX_3389_REG_CPI_H, cpi >> 8); /* 50-16000, 50 cpi LSb */ + } +#endif + return -ENOTSUP; +} +static int pmw33xx_init_chip(const struct device *dev) { + const struct pmw33xx_config *const config = dev->config; + const struct pmw33xx_gpio_dt_spec *cs_gpio_cfg = &config->bus_cfg.spi_cfg->cs_spec; + pmw33xx_cs_select(cs_gpio_cfg, 1); + k_sleep(K_MSEC(1)); + + int err = pmw33xx_write_reg(dev, PMW33XX_REG_PWR_UP_RST, PMW33XX_RESET_CMD); + if (err) { + LOG_ERR("could not reset %d", err); + return -EIO; + } + uint8_t pid = 0x0; + err = pmw33xx_read_reg(dev, PMW33XX_REG_PID, &pid); + if (err) { + LOG_ERR("could not reset %d", err); + return -EIO; + } + if (pid != PMW33XX_PID) { + LOG_ERR("pid does not match expected: got (%x), expected(%x)", pid, PMW33XX_PID); + return -EIO; + } + pmw33xx_write_reg(dev, PMW33XX_REG_CONFIG2, 0x00); // clear rest enable + + err = pmw33xx_write_srom(dev); + if (err) { + LOG_ERR("could not upload srom %d", err); + return -EIO; + } + uint8_t srom_run = 0x0; + err = pmw33xx_read_reg(dev, PMW33XX_REG_OBSERVATION, &srom_run); + if (err) { + LOG_ERR("could not check srom status %d", err); + return -EIO; + } + if (!(srom_run & PMW33XX_SROM_RUN)) { + LOG_ERR("srom status invalid %d", srom_run); + return -EIO; + } + + uint8_t srom_id = 0x0; + err = pmw33xx_read_reg(dev, PMW33XX_REG_SROM_ID, &srom_id); + if (err) { + LOG_ERR("could not check srom id %d", err); + return -EIO; + } + if (!srom_id) { + LOG_ERR("srom id invalid %d", srom_id); + return -EIO; + } + + pmw33xx_write_reg(dev, PMW33XX_REG_CONFIG2, 0x00); // clear rest enable + pmw33xx_write_reg(dev, PMW33XX_REG_BURST, 0x01); // clear rest enable + struct pmw33xx_motion_burst val; + pmw33xx_read_motion_burst(dev, &val); // read and throwout initial motion data + + if (config->cpi > PMW33XX_CPI_MIN && config->cpi < PMW33XX_CPI_MAX) + return pmw33xx_set_cpi(dev, config->cpi); + return 0; } -static int pmw33xx_init(const struct device *dev) -{ - const struct pmw33xx_config *const config = dev->config; - struct pmw33xx_data *data = dev->data; - - data->bus = device_get_binding(config->bus_name); - if (!data->bus) { - LOG_DBG("master not found: %s", log_strdup(config->bus_name)); - return -EINVAL; - } +static int pmw33xx_init(const struct device *dev) { + const struct pmw33xx_config *const config = dev->config; + struct pmw33xx_data *data = dev->data; - config->bus_init(dev); + data->bus = device_get_binding(config->bus_name); + if (!data->bus) { + LOG_DBG("master not found: %s", log_strdup(config->bus_name)); + return -EINVAL; + } - if (pmw33xx_init_chip(dev) < 0) { - LOG_DBG("failed to initialize chip"); - return -EIO; - } + config->bus_init(dev); - const struct pmw33xx_gpio_dt_spec *cs_gpio_cfg = &config->bus_cfg.spi_cfg->cs_spec; + if (pmw33xx_init_chip(dev) < 0) { + LOG_DBG("failed to initialize chip"); + return -EIO; + } #ifdef CONFIG_PMW33XX_TRIGGER - if (pmw33xx_init_interrupt(dev) < 0) { - LOG_DBG("Failed to initialize interrupt!"); - return -EIO; - } + if (pmw33xx_init_interrupt(dev) < 0) { + LOG_DBG("Failed to initialize interrupt!"); + return -EIO; + } #endif - pmw33xx_cs_select(cs_gpio_cfg, 1); - k_sleep(K_MSEC(1)); - - int err = pmw33xx_write_reg(dev, PMW33XX_REG_PWR_UP_RST, PMW33XX_RESET_CMD); - if (err) { - LOG_ERR("could not reset %d", err); - return -EIO; - } - uint8_t pid = 0x0; - err = pmw33xx_read_reg(dev, PMW33XX_REG_PID, &pid); - if (err) { - LOG_ERR("could not reset %d", err); - return -EIO; - } - if (pid != PMW33XX_PID) { - LOG_ERR("pid does not match expected: got (%x), expected(%x)", pid, PMW33XX_PID); - return -EIO; - } - pmw33xx_write_reg(dev, PMW33XX_REG_CONFIG2, 0x00); // clear rest enable - - err = pmw33xx_write_srom(dev); - if (err) { - LOG_ERR("could not upload srom %d", err); - return -EIO; - } - uint8_t srom_run = 0x0; - err = pmw33xx_read_reg(dev, PMW33XX_REG_OBSERVATION, &srom_run); - if (err) { - LOG_ERR("could not check srom status %d", err); - return -EIO; - } - if (!(srom_run & PMW33XX_SROM_RUN)) { - LOG_ERR("srom status invalid %d", srom_run); - return -EIO; - } - - uint8_t srom_id = 0x0; - err = pmw33xx_read_reg(dev, PMW33XX_REG_SROM_ID, &srom_id); - if (err) { - LOG_ERR("could not check srom id %d", err); - return -EIO; - } - if (!srom_id) { - LOG_ERR("srom id invalid %d", srom_id); - return -EIO; - } - - pmw33xx_write_reg(dev, PMW33XX_REG_CONFIG2, 0x00); // clear rest enable - pmw33xx_write_reg(dev, PMW33XX_REG_BURST, 0x01); // clear rest enable - struct pmw33xx_motion_burst val; - pmw33xx_read_motion_burst(dev, &val); // read and throwout initial motion data - - return 0; + return 0; } #define PMW33XX_DATA_SPI(n) \ - { \ - .cs_ctrl = {}, \ - } + { .cs_ctrl = {}, } #define PMW33XX_SPI_CFG(n) \ - (&(struct pmw33xx_spi_cfg){ \ + (&(struct pmw33xx_spi_cfg){ \ .spi_conf = \ { \ .frequency = DT_INST_PROP(n, spi_max_frequency), \ @@ -367,30 +347,30 @@ static int pmw33xx_init(const struct device *dev) (SPI_WORD_SET(8) | SPI_OP_MODE_MASTER | SPI_MODE_CPOL | SPI_MODE_CPHA), \ .slave = DT_INST_REG_ADDR(n), \ }, \ - .cs_spec = PMW33XX_GPIO_DT_SPEC_GET_BY_IDX(DT_DRV_INST(n), cs_gpios, 0), \ + .cs_spec = PMW33XX_GPIO_DT_SPEC_GET_BY_IDX(DT_DRV_INST(n), cs_gpios, 0), \ }) #define PMW33XX_GPIO_DT_SPEC_GET_BY_IDX(node_id, prop, idx) \ - { \ - .port = DEVICE_DT_GET(DT_GPIO_CTLR_BY_IDX(node_id, prop, idx)), \ - .pin = DT_GPIO_PIN_BY_IDX(node_id, prop, idx), \ - .dt_flags = DT_GPIO_FLAGS_BY_IDX(node_id, prop, idx), \ - } + { \ + .port = DEVICE_DT_GET(DT_GPIO_CTLR_BY_IDX(node_id, prop, idx)), \ + .pin = DT_GPIO_PIN_BY_IDX(node_id, prop, idx), \ + .dt_flags = DT_GPIO_FLAGS_BY_IDX(node_id, prop, idx), \ + } #define PMW33XX_CONFIG_SPI(n) \ - { \ - .bus_name = DT_INST_BUS_LABEL(n), .bus_init = pmw33xx_spi_init, \ - .bus_cfg = { .spi_cfg = PMW33XX_SPI_CFG(n) }, \ - COND_CODE_1(CONFIG_MA730_TRIGGER, \ - (, PMW33XX_GPIO_DT_SPEC_GET_BY_IDX(DT_DRV_INST(n), motswk_gpios, 0)), \ - ()) \ - } + { \ + .bus_name = DT_INST_BUS_LABEL(n), .bus_init = pmw33xx_spi_init, \ + .bus_cfg = {.spi_cfg = PMW33XX_SPI_CFG(n)}, \ + COND_CODE_0(DT_INST_NODE_HAS_PROP(n, cpi), (0), (DT_INST_PROP(n, cpi))) \ + COND_CODE_1(CONFIG_PMW33XX_TRIGGER, \ + (PMW33XX_GPIO_DT_SPEC_GET_BY_IDX(DT_DRV_INST(n), motswk_gpios, 0)), ()) \ + } #define PMW33XX_INST(n) \ - static struct pmw33xx_data pmw33xx_data_##n = PMW33XX_DATA_SPI(n); \ - static const struct pmw33xx_config pmw33xx_cfg_##n = PMW33XX_CONFIG_SPI(n); \ - DEVICE_DT_INST_DEFINE(n, pmw33xx_init, device_pm_control_nop, &pmw33xx_data_##n, \ - &pmw33xx_cfg_##n, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \ - &pmw33xx_driver_api); + static struct pmw33xx_data pmw33xx_data_##n = PMW33XX_DATA_SPI(n); \ + static const struct pmw33xx_config pmw33xx_cfg_##n = PMW33XX_CONFIG_SPI(n); \ + DEVICE_DT_INST_DEFINE(n, pmw33xx_init, device_pm_control_nop, &pmw33xx_data_##n, \ + &pmw33xx_cfg_##n, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \ + &pmw33xx_driver_api); DT_INST_FOREACH_STATUS_OKAY(PMW33XX_INST) diff --git a/app/drivers/sensor/pmw33xx/pmw33xx.h b/app/drivers/sensor/pmw33xx/pmw33xx.h index 31b28944fb5..6bab327d716 100644 --- a/app/drivers/sensor/pmw33xx/pmw33xx.h +++ b/app/drivers/sensor/pmw33xx/pmw33xx.h @@ -65,6 +65,11 @@ /* power up reset cmd */ #define PMW33XX_RESET_CMD 0x5A +#define PMW33XX_3389_CPI_MIN 50 +#define PMW33XX_3389_CPI_MAX 16000 +#define PMW33XX_3360_CPI_MIN 100 +#define PMW33XX_3360_CPI_MAX 12000 + struct pmw33xx_gpio_dt_spec { const struct device *port; gpio_pin_t pin; @@ -84,7 +89,7 @@ struct pmw33xx_config { char *bus_name; int (*bus_init)(const struct device *dev); const union pmw33xx_bus_cfg bus_cfg; - int resolution; + int cpi; #if CONFIG_PMW33XX_TRIGGER struct pmw33xx_gpio_dt_spec motswk_spec; #endif // CONFIG_PMW33XX_TRIGGER diff --git a/app/drivers/sensor/pmw33xx/pmw33xx_trigger.c b/app/drivers/sensor/pmw33xx/pmw33xx_trigger.c index 9451a330d5f..92c8a05e386 100644 --- a/app/drivers/sensor/pmw33xx/pmw33xx_trigger.c +++ b/app/drivers/sensor/pmw33xx/pmw33xx_trigger.c @@ -4,7 +4,7 @@ * SPDX-License-Identifier: MIT */ -#define DT_DRV_COMPAT pixart_pmw3389 +#define DT_DRV_COMPAT pixart_pmw33xx #include #include @@ -12,119 +12,113 @@ #include #include -#include "pmw3389.h" +#include "pmw33xx.h" -// extern struct pmw3389_data pmw3389_driver; +// extern struct pmw33xx_data pmw33xx_driver; #include -LOG_MODULE_DECLARE(PMW3389, CONFIG_SENSOR_LOG_LEVEL); +LOG_MODULE_DECLARE(PMW33XX, CONFIG_SENSOR_LOG_LEVEL); -static inline void setup_int(const struct device *dev, bool enable) -{ - struct pmw3389_data *data = dev->data; - const struct pmw3389_config *cfg = dev->config; +static inline void setup_int(const struct device *dev, bool enable) { + struct pmw33xx_data *data = dev->data; + const struct pmw33xx_config *cfg = dev->config; - if (gpio_pin_interrupt_configure(cfg->motswk_spec.port, cfg->motswk_spec.pin, - enable ? GPIO_INT_EDGE_TO_ACTIVE : GPIO_INT_DISABLE)) { - LOG_WRN("Unable to set MOTSWK GPIO interrupt"); - } + if (gpio_pin_interrupt_configure(cfg->motswk_spec.port, cfg->motswk_spec.pin, + enable ? GPIO_INT_EDGE_TO_ACTIVE : GPIO_INT_DISABLE)) { + LOG_WRN("Unable to set MOTSWK GPIO interrupt"); + } } -static void pmw3389_motswk_gpio_callback(const struct device *dev, struct gpio_callback *cb, - uint32_t pins) -{ - struct pmw3389_data *drv_data = CONTAINER_OF(cb, struct pmw3389_data, motswk_gpio_cb); +static void pmw33xx_motswk_gpio_callback(const struct device *dev, struct gpio_callback *cb, + uint32_t pins) { + struct pmw33xx_data *drv_data = CONTAINER_OF(cb, struct pmw33xx_data, motswk_gpio_cb); - LOG_DBG(""); + LOG_DBG(""); - setup_int(drv_data->dev, false); + setup_int(drv_data->dev, false); -#if defined(CONFIG_PMW3389_TRIGGER_OWN_THREAD) - k_sem_give(&drv_data->gpio_sem); -#elif defined(CONFIG_PMW3389_TRIGGER_GLOBAL_THREAD) - k_work_submit(&drv_data->work); +#if defined(CONFIG_PMW33XX_TRIGGER_OWN_THREAD) + k_sem_give(&drv_data->gpio_sem); +#elif defined(CONFIG_PMW33XX_TRIGGER_GLOBAL_THREAD) + k_work_submit(&drv_data->work); #endif } -static void pmw3389_thread_cb(const struct device *dev) -{ - struct pmw3389_data *drv_data = dev->data; +static void pmw33xx_thread_cb(const struct device *dev) { + struct pmw33xx_data *drv_data = dev->data; - LOG_DBG("%p", drv_data->handler); - drv_data->handler(dev, drv_data->trigger); + LOG_DBG("%p", drv_data->handler); + drv_data->handler(dev, drv_data->trigger); - // Enable once the wall/spam of interrupts is solved - setup_int(dev, true); + // Enable once the wall/spam of interrupts is solved + setup_int(dev, true); } -#ifdef CONFIG_PMW3389_TRIGGER_OWN_THREAD -static void pmw3389_thread(int dev_ptr, int unused) -{ - const struct device *dev = INT_TO_POINTER(dev_ptr); - struct pmw3389_data *drv_data = dev->data; +#ifdef CONFIG_PMW33XX_TRIGGER_OWN_THREAD +static void pmw33xx_thread(int dev_ptr, int unused) { + const struct device *dev = INT_TO_POINTER(dev_ptr); + struct pmw33xx_data *drv_data = dev->data; - ARG_UNUSED(unused); + ARG_UNUSED(unused); - while (1) { - k_sem_take(&drv_data->gpio_sem, K_FOREVER); - pmw3389_thread_cb(dev); - } + while (1) { + k_sem_take(&drv_data->gpio_sem, K_FOREVER); + pmw33xx_thread_cb(dev); + } } #endif -#ifdef CONFIG_PMW3389_TRIGGER_GLOBAL_THREAD -static void pmw3389_work_cb(struct k_work *work) -{ - struct pmw3389_data *drv_data = CONTAINER_OF(work, struct pmw3389_data, work); +#ifdef CONFIG_PMW33XX_TRIGGER_GLOBAL_THREAD +static void pmw33xx_work_cb(struct k_work *work) { + struct pmw33xx_data *drv_data = CONTAINER_OF(work, struct pmw33xx_data, work); - LOG_DBG(""); + LOG_DBG(""); - pmw3389_thread_cb(drv_data->dev); + pmw33xx_thread_cb(drv_data->dev); } #endif -int pmw3389_trigger_set(const struct device *dev, const struct sensor_trigger *trig, - sensor_trigger_handler_t handler) -{ - struct pmw3389_data *drv_data = dev->data; +int pmw33xx_trigger_set(const struct device *dev, const struct sensor_trigger *trig, + sensor_trigger_handler_t handler) { + struct pmw33xx_data *drv_data = dev->data; - setup_int(dev, false); + setup_int(dev, false); - k_msleep(5); + k_msleep(5); - drv_data->trigger = trig; - drv_data->handler = handler; + drv_data->trigger = trig; + drv_data->handler = handler; - setup_int(dev, true); + setup_int(dev, true); - return 0; + return 0; } -int pmw3389_init_interrupt(const struct device *dev) -{ - struct pmw3389_data *drv_data = dev->data; - const struct pmw3389_config *drv_cfg = dev->config; +int pmw33xx_init_interrupt(const struct device *dev) { + struct pmw33xx_data *drv_data = dev->data; + const struct pmw33xx_config *drv_cfg = dev->config; - drv_data->dev = dev; - /* setup gpio interrupt */ + drv_data->dev = dev; + /* setup gpio interrupt */ - gpio_init_callback(&drv_data->motswk_gpio_cb, pmw3389_motswk_gpio_callback, - BIT(drv_cfg->motswk_spec.pin)); + gpio_init_callback(&drv_data->motswk_gpio_cb, pmw33xx_motswk_gpio_callback, + BIT(drv_cfg->motswk_spec.pin)); - if (gpio_add_callback(drv_cfg->motswk_spec.port, &drv_data->motswk_gpio_cb) < 0) { - LOG_DBG("Failed to set MOTSWK callback!"); - return -EIO; - } + int err = gpio_add_callback(drv_cfg->motswk_spec.port, &drv_data->motswk_gpio_cb); + if (err < 0) { + LOG_DBG("Failed to set MOTSWK callback!"); + return -EIO; + } -#if defined(CONFIG_PMW3389_TRIGGER_OWN_THREAD) - k_sem_init(&drv_data->gpio_sem, 0, UINT_MAX); +#if defined(CONFIG_PMW33XX_TRIGGER_OWN_THREAD) + k_sem_init(&drv_data->gpio_sem, 0, UINT_MAX); - k_thread_create(&drv_data->thread, drv_data->thread_stack, CONFIG_PMW3389_THREAD_STACK_SIZE, - (k_thread_entry_t)pmw3389_thread, dev, 0, NULL, - K_PRIO_COOP(CONFIG_PMW3389_THREAD_PRIORITY), 0, K_NO_WAIT); -#elif defined(CONFIG_PMW3389_TRIGGER_GLOBAL_THREAD) - k_work_init(&drv_data->work, pmw3389_work_cb); + k_thread_create(&drv_data->thread, drv_data->thread_stack, CONFIG_PMW33XX_THREAD_STACK_SIZE, + (k_thread_entry_t)pmw33xx_thread, dev, 0, NULL, + K_PRIO_COOP(CONFIG_PMW33XX_THREAD_PRIORITY), 0, K_NO_WAIT); +#elif defined(CONFIG_PMW33XX_TRIGGER_GLOBAL_THREAD) + k_work_init(&drv_data->work, pmw33xx_work_cb); #endif - return 0; + return 0; } From 218c5b0572aee935eb79c91477351afc66e853bb Mon Sep 17 00:00:00 2001 From: "Dylan (Luberry) Kozicki" Date: Sun, 17 Apr 2022 12:47:44 -0400 Subject: [PATCH 006/157] fix interrupt handling --- app/drivers/sensor/pmw33xx/pmw33xx.c | 25 +++++++-- app/drivers/sensor/pmw33xx/pmw33xx.h | 56 ++++++++++--------- app/drivers/sensor/pmw33xx/pmw33xx_trigger.c | 7 ++- .../dts/bindings/sensor/pixart,pmw33xx.yaml | 4 ++ 4 files changed, 60 insertions(+), 32 deletions(-) diff --git a/app/drivers/sensor/pmw33xx/pmw33xx.c b/app/drivers/sensor/pmw33xx/pmw33xx.c index af016781722..19721744b36 100644 --- a/app/drivers/sensor/pmw33xx/pmw33xx.c +++ b/app/drivers/sensor/pmw33xx/pmw33xx.c @@ -52,7 +52,11 @@ static int pmw33xx_access(const struct device *dev, const uint8_t reg, uint8_t * }, .count = 1, }; - uint8_t result[1] = {*value}; + + uint8_t result[1]; + if (value != NULL) { + result[0] = *value; + } struct spi_buf_set rx = { .buffers = &(struct spi_buf){ @@ -77,7 +81,7 @@ static int pmw33xx_access(const struct device *dev, const uint8_t reg, uint8_t * err = spi_read(data->bus, spi_cfg, &rx); pmw33xx_cs_select(cs_gpio_cfg, 1); k_sleep(K_USEC(160)); - if ((reg & PMW33XX_WR_MASK) == 0) + if ((reg & PMW33XX_WR_MASK) == 0 && value != NULL) *value = result[0]; return err; } @@ -88,6 +92,7 @@ static int pmw33xx_write_reg(const struct device *dev, const uint8_t reg, const uint8_t v = value; return pmw33xx_access(dev, reg | PMW33XX_WR_MASK, &v); } + static int pmw33xx_write_srom(const struct device *dev) { struct pmw33xx_data *data = dev->data; const struct pmw33xx_config *cfg = dev->config; @@ -166,9 +171,19 @@ static int pmw33xx_read_motion_burst(const struct device *dev, struct pmw33xx_mo } err = spi_read(data->bus, spi_cfg, &rx); pmw33xx_cs_select(cs_gpio_cfg, 1); +#ifdef CONFIG_PMW33XX_TRIGGER + pmw33xx_reset_motion(dev); +#endif return err; } +#ifdef CONFIG_PMW33XX_TRIGGER +void pmw33xx_reset_motion(const struct device *dev) { + // reset motswk interrupt + pmw33xx_read_reg(dev, PMW33XX_REG_MOTION, NULL); +} +#endif + int pmw33xx_spi_init(const struct device *dev) { const struct pmw33xx_config *cfg = dev->config; const struct pmw33xx_gpio_dt_spec *cs_gpio_cfg = &cfg->bus_cfg.spi_cfg->cs_spec; @@ -269,7 +284,8 @@ static int pmw33xx_init_chip(const struct device *dev) { LOG_ERR("pid does not match expected: got (%x), expected(%x)", pid, PMW33XX_PID); return -EIO; } - pmw33xx_write_reg(dev, PMW33XX_REG_CONFIG2, 0x00); // clear rest enable + pmw33xx_write_reg(dev, PMW33XX_REG_CONFIG2, + config->disable_rest ? 0x00 : PMW33XX_RESTEN); // set rest enable err = pmw33xx_write_srom(dev); if (err) { @@ -361,9 +377,10 @@ static int pmw33xx_init(const struct device *dev) { { \ .bus_name = DT_INST_BUS_LABEL(n), .bus_init = pmw33xx_spi_init, \ .bus_cfg = {.spi_cfg = PMW33XX_SPI_CFG(n)}, \ + .disable_rest = DT_INST_NODE_HAS_PROP(n, disable_rest), \ COND_CODE_0(DT_INST_NODE_HAS_PROP(n, cpi), (0), (DT_INST_PROP(n, cpi))) \ COND_CODE_1(CONFIG_PMW33XX_TRIGGER, \ - (PMW33XX_GPIO_DT_SPEC_GET_BY_IDX(DT_DRV_INST(n), motswk_gpios, 0)), ()) \ + (, PMW33XX_GPIO_DT_SPEC_GET_BY_IDX(DT_DRV_INST(n), motswk_gpios, 0)), ()) \ } #define PMW33XX_INST(n) \ diff --git a/app/drivers/sensor/pmw33xx/pmw33xx.h b/app/drivers/sensor/pmw33xx/pmw33xx.h index 6bab327d716..32693957b28 100644 --- a/app/drivers/sensor/pmw33xx/pmw33xx.h +++ b/app/drivers/sensor/pmw33xx/pmw33xx.h @@ -65,65 +65,67 @@ /* power up reset cmd */ #define PMW33XX_RESET_CMD 0x5A +/* cpi max and min values */ #define PMW33XX_3389_CPI_MIN 50 #define PMW33XX_3389_CPI_MAX 16000 #define PMW33XX_3360_CPI_MIN 100 #define PMW33XX_3360_CPI_MAX 12000 struct pmw33xx_gpio_dt_spec { - const struct device *port; - gpio_pin_t pin; - gpio_dt_flags_t dt_flags; + const struct device *port; + gpio_pin_t pin; + gpio_dt_flags_t dt_flags; }; struct pmw33xx_spi_cfg { - struct spi_config spi_conf; - struct pmw33xx_gpio_dt_spec cs_spec; + struct spi_config spi_conf; + struct pmw33xx_gpio_dt_spec cs_spec; }; union pmw33xx_bus_cfg { - struct pmw33xx_spi_cfg *spi_cfg; + struct pmw33xx_spi_cfg *spi_cfg; }; struct pmw33xx_config { - char *bus_name; - int (*bus_init)(const struct device *dev); - const union pmw33xx_bus_cfg bus_cfg; - int cpi; + char *bus_name; + int (*bus_init)(const struct device *dev); + const union pmw33xx_bus_cfg bus_cfg; + bool disable_rest; + int cpi; #if CONFIG_PMW33XX_TRIGGER - struct pmw33xx_gpio_dt_spec motswk_spec; + struct pmw33xx_gpio_dt_spec motswk_spec; #endif // CONFIG_PMW33XX_TRIGGER }; struct pmw33xx_data; struct pmw33xx_transfer_function { - int (*read_data)(const struct device *dev, uint16_t *value); + int (*read_data)(const struct device *dev, int16_t *value); }; struct pmw33xx_data { - const struct device *bus; - struct spi_cs_control cs_ctrl; + const struct device *bus; + struct spi_cs_control cs_ctrl; - int16_t dx; - int16_t dy; + int16_t dx; + int16_t dy; - const struct pmw33xx_transfer_function *hw_tf; + const struct pmw33xx_transfer_function *hw_tf; #ifdef CONFIG_PMW33XX_TRIGGER - struct gpio_callback motswk_gpio_cb; - const struct device *dev; + struct gpio_callback motswk_gpio_cb; + const struct device *dev; - sensor_trigger_handler_t handler; - const struct sensor_trigger *trigger; + sensor_trigger_handler_t handler; + const struct sensor_trigger *trigger; #if defined(CONFIG_PMW33XX_TRIGGER_OWN_THREAD) - K_THREAD_STACK_MEMBER(thread_stack, CONFIG_PMW33XX_THREAD_STACK_SIZE); - struct k_sem gpio_sem; - struct k_thread thread; + K_THREAD_STACK_MEMBER(thread_stack, CONFIG_PMW33XX_THREAD_STACK_SIZE); + struct k_sem gpio_sem; + struct k_thread thread; #elif defined(CONFIG_PMW33XX_TRIGGER_GLOBAL_THREAD) - struct k_work work; + struct k_work work; #endif #endif /* CONFIG_PMW33XX_TRIGGER */ @@ -133,9 +135,11 @@ int pmw33xx_spi_init(const struct device *dev); #ifdef CONFIG_PMW33XX_TRIGGER int pmw33xx_trigger_set(const struct device *dev, const struct sensor_trigger *trig, - sensor_trigger_handler_t handler); + sensor_trigger_handler_t handler); int pmw33xx_init_interrupt(const struct device *dev); + +void pmw33xx_reset_motion(const struct device *dev); #endif #endif /* ZEPHYR_DRIVERS_SENSOR_PIXART_PMW33XX_H_ */ diff --git a/app/drivers/sensor/pmw33xx/pmw33xx_trigger.c b/app/drivers/sensor/pmw33xx/pmw33xx_trigger.c index 92c8a05e386..89c92d21a8d 100644 --- a/app/drivers/sensor/pmw33xx/pmw33xx_trigger.c +++ b/app/drivers/sensor/pmw33xx/pmw33xx_trigger.c @@ -23,6 +23,7 @@ static inline void setup_int(const struct device *dev, bool enable) { struct pmw33xx_data *data = dev->data; const struct pmw33xx_config *cfg = dev->config; + gpio_pin_configure(cfg->motswk_spec.port, cfg->motswk_spec.pin, cfg->motswk_spec.dt_flags); if (gpio_pin_interrupt_configure(cfg->motswk_spec.port, cfg->motswk_spec.pin, enable ? GPIO_INT_EDGE_TO_ACTIVE : GPIO_INT_DISABLE)) { LOG_WRN("Unable to set MOTSWK GPIO interrupt"); @@ -72,7 +73,7 @@ static void pmw33xx_thread(int dev_ptr, int unused) { static void pmw33xx_work_cb(struct k_work *work) { struct pmw33xx_data *drv_data = CONTAINER_OF(work, struct pmw33xx_data, work); - LOG_DBG(""); + LOG_DBG(" "); pmw33xx_thread_cb(drv_data->dev); } @@ -91,6 +92,8 @@ int pmw33xx_trigger_set(const struct device *dev, const struct sensor_trigger *t setup_int(dev, true); + // reset motion on int setup + pmw33xx_reset_motion(dev); return 0; } @@ -121,4 +124,4 @@ int pmw33xx_init_interrupt(const struct device *dev) { #endif return 0; -} +} \ No newline at end of file diff --git a/app/drivers/zephyr/dts/bindings/sensor/pixart,pmw33xx.yaml b/app/drivers/zephyr/dts/bindings/sensor/pixart,pmw33xx.yaml index 91f48a00180..504f9b93496 100644 --- a/app/drivers/zephyr/dts/bindings/sensor/pixart,pmw33xx.yaml +++ b/app/drivers/zephyr/dts/bindings/sensor/pixart,pmw33xx.yaml @@ -20,3 +20,7 @@ properties: type: int description: mouse cpi required: false + disable-rest: + type: boolean + description: disables mouse rest mode, will decrease battery life + required: false From fd73042e8169ccecb23234ba3e04ec4aa4680dd9 Mon Sep 17 00:00:00 2001 From: "Dylan (Luberry) Kozicki" Date: Sun, 17 Apr 2022 12:59:59 -0400 Subject: [PATCH 007/157] update license headers --- app/drivers/sensor/pmw33xx/CMakeLists.txt | 2 +- app/drivers/sensor/pmw33xx/Kconfig | 2 ++ app/drivers/sensor/pmw33xx/pmw33xx.c | 5 +++++ app/drivers/sensor/pmw33xx/pmw33xx.h | 5 +++++ app/drivers/sensor/pmw33xx/pmw33xx_trigger.c | 2 +- app/drivers/zephyr/dts/bindings/sensor/pixart,pmw33xx.yaml | 2 ++ 6 files changed, 16 insertions(+), 2 deletions(-) diff --git a/app/drivers/sensor/pmw33xx/CMakeLists.txt b/app/drivers/sensor/pmw33xx/CMakeLists.txt index a4c89fb2edd..011b4cac058 100644 --- a/app/drivers/sensor/pmw33xx/CMakeLists.txt +++ b/app/drivers/sensor/pmw33xx/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2020 The ZMK Contributors +# Copyright (c) 2022 The ZMK Contributors # SPDX-License-Identifier: MIT zephyr_include_directories(.) diff --git a/app/drivers/sensor/pmw33xx/Kconfig b/app/drivers/sensor/pmw33xx/Kconfig index 845a0879732..b6a926f0224 100644 --- a/app/drivers/sensor/pmw33xx/Kconfig +++ b/app/drivers/sensor/pmw33xx/Kconfig @@ -1,3 +1,5 @@ +# Copyright (c) 2022 The ZMK Contributors +# SPDX-License-Identifier: MIT config PMW33XX bool "PMW33XX Mouse Sensor" depends on SPI diff --git a/app/drivers/sensor/pmw33xx/pmw33xx.c b/app/drivers/sensor/pmw33xx/pmw33xx.c index 19721744b36..5fcb1ae6cd5 100644 --- a/app/drivers/sensor/pmw33xx/pmw33xx.c +++ b/app/drivers/sensor/pmw33xx/pmw33xx.c @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ #define DT_DRV_COMPAT pixart_pmw33xx #include diff --git a/app/drivers/sensor/pmw33xx/pmw33xx.h b/app/drivers/sensor/pmw33xx/pmw33xx.h index 32693957b28..fe071a3220b 100644 --- a/app/drivers/sensor/pmw33xx/pmw33xx.h +++ b/app/drivers/sensor/pmw33xx/pmw33xx.h @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ #ifndef ZEPHYR_DRIVERS_SENSOR_PIXART_PMW33XX_H_ #define ZEPHYR_DRIVERS_SENSOR_PIXART_PMW33XX_H_ diff --git a/app/drivers/sensor/pmw33xx/pmw33xx_trigger.c b/app/drivers/sensor/pmw33xx/pmw33xx_trigger.c index 89c92d21a8d..e254faf1de1 100644 --- a/app/drivers/sensor/pmw33xx/pmw33xx_trigger.c +++ b/app/drivers/sensor/pmw33xx/pmw33xx_trigger.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 The ZMK Contributors + * Copyright (c) 2022 The ZMK Contributors * * SPDX-License-Identifier: MIT */ diff --git a/app/drivers/zephyr/dts/bindings/sensor/pixart,pmw33xx.yaml b/app/drivers/zephyr/dts/bindings/sensor/pixart,pmw33xx.yaml index 504f9b93496..6a45218a78f 100644 --- a/app/drivers/zephyr/dts/bindings/sensor/pixart,pmw33xx.yaml +++ b/app/drivers/zephyr/dts/bindings/sensor/pixart,pmw33xx.yaml @@ -1,3 +1,5 @@ +# Copyright (c) 2022 The ZMK Contributors +# SPDX-License-Identifier: MIT description: | Sensor driver for the pixart PMW33XX optical mouse sensor supports 3360 built in, and 3389 with external srom compatible: "pixart,pmw33xx" From bdc8b9ba2d7e4bcee1f742de623a222ec2ead254 Mon Sep 17 00:00:00 2001 From: crides Date: Sun, 23 Jan 2022 14:47:23 -0600 Subject: [PATCH 008/157] add: Cirque Pinnacle trackpad driver --- app/drivers/sensor/CMakeLists.txt | 3 +- app/drivers/sensor/Kconfig | 3 +- .../sensor/cirque_trackpad/CMakeLists.txt | 8 + app/drivers/sensor/cirque_trackpad/Kconfig | 51 ++++ .../sensor/cirque_trackpad/cirque_trackpad.c | 260 ++++++++++++++++++ .../sensor/cirque_trackpad/cirque_trackpad.h | 80 ++++++ .../dts/bindings/sensor/cirque,pinnacle.yaml | 27 ++ app/include/zmk/sensors.h | 7 + 8 files changed, 437 insertions(+), 2 deletions(-) create mode 100644 app/drivers/sensor/cirque_trackpad/CMakeLists.txt create mode 100644 app/drivers/sensor/cirque_trackpad/Kconfig create mode 100644 app/drivers/sensor/cirque_trackpad/cirque_trackpad.c create mode 100644 app/drivers/sensor/cirque_trackpad/cirque_trackpad.h create mode 100644 app/drivers/zephyr/dts/bindings/sensor/cirque,pinnacle.yaml diff --git a/app/drivers/sensor/CMakeLists.txt b/app/drivers/sensor/CMakeLists.txt index b549320f121..c8931edacb5 100644 --- a/app/drivers/sensor/CMakeLists.txt +++ b/app/drivers/sensor/CMakeLists.txt @@ -2,4 +2,5 @@ # SPDX-License-Identifier: MIT add_subdirectory_ifdef(CONFIG_ZMK_BATTERY battery) -add_subdirectory_ifdef(CONFIG_EC11 ec11) \ No newline at end of file +add_subdirectory_ifdef(CONFIG_EC11 ec11) +add_subdirectory_ifdef(CONFIG_PINNACLE cirque_trackpad) diff --git a/app/drivers/sensor/Kconfig b/app/drivers/sensor/Kconfig index a828f6c63ab..01a8c49169d 100644 --- a/app/drivers/sensor/Kconfig +++ b/app/drivers/sensor/Kconfig @@ -2,4 +2,5 @@ # SPDX-License-Identifier: MIT rsource "battery/Kconfig" -rsource "ec11/Kconfig" \ No newline at end of file +rsource "ec11/Kconfig" +rsource "cirque_trackpad/Kconfig" diff --git a/app/drivers/sensor/cirque_trackpad/CMakeLists.txt b/app/drivers/sensor/cirque_trackpad/CMakeLists.txt new file mode 100644 index 00000000000..92b6744cc53 --- /dev/null +++ b/app/drivers/sensor/cirque_trackpad/CMakeLists.txt @@ -0,0 +1,8 @@ +# Copyright (c) 2022 The ZMK Contributors +# SPDX-License-Identifier: MIT + +zephyr_include_directories(.) + +zephyr_library() + +zephyr_library_sources(cirque_trackpad.c) diff --git a/app/drivers/sensor/cirque_trackpad/Kconfig b/app/drivers/sensor/cirque_trackpad/Kconfig new file mode 100644 index 00000000000..16a4792482c --- /dev/null +++ b/app/drivers/sensor/cirque_trackpad/Kconfig @@ -0,0 +1,51 @@ +# Copyright (c) 2022 The ZMK Contributors +# SPDX-License-Identifier: MIT + +menuconfig PINNACLE + bool "PINNACLE Incremental Encoder Sensor" + depends on GPIO + depends on SPI + help + Enable driver for Cirque Pinnacle trackpads + +if PINNACLE + +choice + prompt "Trigger mode" + default PINNACLE_TRIGGER_NONE + help + Specify the type of triggering to be used by the driver. + +config PINNACLE_TRIGGER_NONE + bool "No trigger" + +config PINNACLE_TRIGGER_GLOBAL_THREAD + bool "Use global thread" + depends on GPIO + select PINNACLE_TRIGGER + +config PINNACLE_TRIGGER_OWN_THREAD + bool "Use own thread" + depends on GPIO + select PINNACLE_TRIGGER + +endchoice + +config PINNACLE_TRIGGER + bool + +config PINNACLE_THREAD_PRIORITY + int "Thread priority" + depends on PINNACLE_TRIGGER_OWN_THREAD + default 10 + help + Priority of thread used by the driver to handle interrupts. + +config PINNACLE_THREAD_STACK_SIZE + int "Thread stack size" + depends on PINNACLE_TRIGGER_OWN_THREAD + default 1024 + help + Stack size of thread used by the driver to handle interrupts. + +endif # PINNACLE diff --git a/app/drivers/sensor/cirque_trackpad/cirque_trackpad.c b/app/drivers/sensor/cirque_trackpad/cirque_trackpad.c new file mode 100644 index 00000000000..779595ce4c5 --- /dev/null +++ b/app/drivers/sensor/cirque_trackpad/cirque_trackpad.c @@ -0,0 +1,260 @@ +#define DT_DRV_COMPAT cirque_pinnacle + +#include +#include +#include +#include +#include + +#include "cirque_trackpad.h" + +LOG_MODULE_REGISTER(pinnacle, CONFIG_SENSOR_LOG_LEVEL); + +static int pinnacle_seq_read(const struct device *dev, const uint8_t start, uint8_t *buf, const uint8_t len) { + uint8_t tx_buffer[len + 3], rx_dummy[3]; + tx_buffer[0] = PINNACLE_READ | start; + memset(&tx_buffer[1], PINNACLE_AUTOINC, len + 1); + tx_buffer[len + 2] = PINNACLE_DUMMY; + + const struct spi_buf tx_buf = { + .buf = tx_buffer, + .len = len + 3, + }; + const struct spi_buf_set tx = { + .buffers = &tx_buf, + .count = 1, + }; + struct spi_buf rx_buf[2] = { + { + .buf = rx_dummy, + .len = 3, + }, + { + .buf = buf, + .len = len, + }, + }; + const struct spi_buf_set rx = { + .buffers = rx_buf, + .count = 2, + }; + const struct pinnacle_data *data = dev->data; + const struct pinnacle_config *config = dev->config; + return spi_transceive(data->spi, &config->spi_config, &tx, &rx); +} + +static int pinnacle_write(const struct device *dev, const uint8_t addr, const uint8_t val) { + uint8_t tx_buffer[2] = { PINNACLE_WRITE | addr, val }; + uint8_t rx_buffer[2]; + + const struct spi_buf tx_buf = { + .buf = tx_buffer, + .len = 2, + }; + const struct spi_buf_set tx = { + .buffers = &tx_buf, + .count = 1, + }; + const struct spi_buf rx_buf[1] = { + { + .buf = rx_buffer, + .len = sizeof(rx_buffer), + }, + }; + const struct spi_buf_set rx = { + .buffers = rx_buf, + .count = 1, + }; + const struct pinnacle_data *data = dev->data; + const struct pinnacle_config *config = dev->config; + const int ret = spi_transceive(data->spi, &config->spi_config, &tx, &rx); + if (rx_buffer[1] != 0xFB) { + LOG_ERR("bad ret val"); + return -EIO; + } + if (ret < 0) { + LOG_ERR("spi ret: %d", ret); + } + return ret; +} + +static int pinnacle_channel_get(const struct device *dev, enum sensor_channel chan, struct sensor_value *val) { + const struct pinnacle_data *data = dev->data; + switch (chan) { + case SENSOR_CHAN_POS_DX: val->val1 = data->dx; break; + case SENSOR_CHAN_POS_DY: val->val1 = data->dy; break; + case SENSOR_CHAN_PRESS: val->val1 = data->btn; break; + default: return -ENOTSUP; + } + return 0; +} + +static int pinnacle_attr_set(const struct device *dev, enum sensor_channel chan, enum sensor_attribute attr, const struct sensor_value *val) { + const struct pinnacle_config *config = dev->config; + if (attr == SENSOR_ATTR_PINNACLE_GE) { + const uint8_t ge_set = val->val1 ? 0 : PINNACLE_FEED_CFG2_DIS_GE; + const uint8_t taps_set = config->no_taps ? PINNACLE_FEED_CFG2_DIS_TAP : 0; + pinnacle_write(dev, PINNACLE_FEED_CFG2, ge_set | taps_set); + return 0; + } + return -ENOTSUP; +} + +static int pinnacle_sample_fetch(const struct device *dev, enum sensor_channel chan) { + uint8_t packet[3]; + int res = pinnacle_seq_read(dev, PINNACLE_2_2_PACKET0, packet, 3); + if (res < 0) { + LOG_ERR("res: %d", res); + return res; + } + struct pinnacle_data *data = dev->data; + data->btn = packet[0] & PINNACLE_PACKET0_BTN_PRIM; + data->dx = (int16_t) (int8_t) packet[1]; + data->dy = (int16_t) (int8_t) packet[2]; + return 0; +} + +#ifdef CONFIG_PINNACLE_TRIGGER +static void set_int(const struct device *dev, const bool en) { + const struct pinnacle_config *config = dev->config; + int ret = gpio_pin_interrupt_configure(config->dr_port, config->dr_pin, en ? GPIO_INT_LEVEL_ACTIVE : GPIO_INT_DISABLE); + if (ret < 0) { + LOG_ERR("can't set interrupt"); + } +} + +static int pinnacle_trigger_set(const struct device *dev, const struct sensor_trigger *trig, sensor_trigger_handler_t handler) { + struct pinnacle_data *data = dev->data; + + set_int(dev, false); + if (trig->type != SENSOR_TRIG_DATA_READY) { + return -ENOTSUP; + } + data->data_ready_trigger = trig; + data->data_ready_handler = handler; + set_int(dev, true); + return 0; +} + +static void pinnacle_int_cb(const struct device *dev) { + struct pinnacle_data *data = dev->data; + data->data_ready_handler(dev, data->data_ready_trigger); + set_int(dev, true); +} + +#ifdef CONFIG_PINNACLE_TRIGGER_OWN_THREAD +static void pinnacle_thread(void *arg) { + const struct device *dev = arg; + struct pinnacle_data *data = dev->data; + + while (1) { + k_sem_take(&data->gpio_sem, K_FOREVER); + pinnacle_int_cb(dev); + pinnacle_write(dev, PINNACLE_STATUS1, 0); // Clear SW_DR + } +} +#elif defined(CONFIG_PINNACLE_TRIGGER_GLOBAL_THREAD) +static void pinnacle_work_cb(struct k_work *work) { + struct pinnacle_data *data = CONTAINER_OF(work, struct pinnacle_data, work); + pinnacle_int_cb(data->dev); + pinnacle_write(dev, PINNACLE_STATUS1, 0); // Clear SW_DR +} +#endif + +static void pinnacle_gpio_cb(const struct device *port, struct gpio_callback *cb, uint32_t pins) { + struct pinnacle_data *data = CONTAINER_OF(cb, struct pinnacle_data, gpio_cb); + const struct device *dev = data->dev; +#if defined(CONFIG_PINNACLE_TRIGGER_OWN_THREAD) + k_sem_give(&data->gpio_sem); +#elif defined(CONFIG_PINNACLE_TRIGGER_GLOBAL_THREAD) + k_work_submit(&data->work); +#endif +} +#endif + +#define SPI_BUS DT_BUS(DT_DRV_INST(0)) +#define SPI_REG DT_REG_ADDR(DT_DRV_INST(0)) + +static int pinnacle_init(const struct device *dev) { + struct pinnacle_data *data = dev->data; + const struct pinnacle_config *config = dev->config; + data->spi = DEVICE_DT_GET(SPI_BUS); + + pinnacle_write(dev, PINNACLE_STATUS1, 0); // Clear CC + pinnacle_write(dev, PINNACLE_Z_IDLE, 0); // No Z-Idle packets + if (config->sleep_en) { + pinnacle_write(dev, PINNACLE_SYS_CFG, PINNACLE_SYS_CFG_EN_SLEEP); + } + if (config->no_taps) { + pinnacle_write(dev, PINNACLE_FEED_CFG2, PINNACLE_FEED_CFG2_DIS_TAP); + } + uint8_t feed_cfg1 = PINNACLE_FEED_CFG1_EN_FEED; + if (config->invert_x) { + feed_cfg1 |= PINNACLE_FEED_CFG1_INV_X; + } + if (config->invert_y) { + feed_cfg1 |= PINNACLE_FEED_CFG1_INV_Y; + } + if (feed_cfg1) { + pinnacle_write(dev, PINNACLE_FEED_CFG1, feed_cfg1); + } + +#ifdef CONFIG_PINNACLE_TRIGGER + data->dev = dev; + gpio_pin_configure(config->dr_port, config->dr_pin, GPIO_INPUT | config->dr_flags); + gpio_init_callback(&data->gpio_cb, pinnacle_gpio_cb, BIT(config->dr_pin)); + int ret = gpio_add_callback(config->dr_port, &data->gpio_cb); + if (ret < 0) { + LOG_ERR("Failed to set DR callback: %d", ret); + return -EIO; + } + +#if defined(CONFIG_PINNACLE_TRIGGER_OWN_THREAD) + k_sem_init(&data->gpio_sem, 0, UINT_MAX); + + k_thread_create(&data->thread, data->thread_stack, CONFIG_PINNACLE_THREAD_STACK_SIZE, + (k_thread_entry_t) pinnacle_thread, (void *) dev, 0, NULL, + K_PRIO_COOP(CONFIG_PINNACLE_THREAD_PRIORITY), 0, K_NO_WAIT); +#elif defined(CONFIG_PINNACLE_TRIGGER_GLOBAL_THREAD) + k_work_init(&data->work, pinnacle_work_cb); +#endif + pinnacle_write(dev, PINNACLE_FEED_CFG1, feed_cfg1); +#endif + return 0; +} + +static const struct sensor_driver_api pinnacle_driver_api = { +#if CONFIG_PINNACLE_TRIGGER + .trigger_set = pinnacle_trigger_set, +#endif + .sample_fetch = pinnacle_sample_fetch, + .channel_get = pinnacle_channel_get, + .attr_set = pinnacle_attr_set, +}; + +static struct pinnacle_data pinnacle_data; +static const struct pinnacle_config pinnacle_config = { + .spi_cs = { + .gpio_dev = DEVICE_DT_GET(DT_GPIO_CTLR_BY_IDX(SPI_BUS, cs_gpios, SPI_REG)), + .gpio_pin = DT_GPIO_PIN_BY_IDX(SPI_BUS, cs_gpios, SPI_REG), + .delay = 0, + .gpio_dt_flags = DT_GPIO_FLAGS_BY_IDX(SPI_BUS, cs_gpios, SPI_REG), + }, + .spi_config = { + .cs = &pinnacle_config.spi_cs, + .frequency = DT_INST_PROP(0, spi_max_frequency), + .slave = DT_INST_REG_ADDR(0), + .operation = (SPI_OP_MODE_MASTER | SPI_WORD_SET(8) | SPI_LINES_SINGLE | SPI_TRANSFER_MSB), + }, + .invert_x = DT_INST_PROP(0, invert_x), + .invert_y = DT_INST_PROP(0, invert_y), + .sleep_en = DT_INST_PROP(0, sleep), + .no_taps = DT_INST_PROP(0, no_taps), +#ifdef CONFIG_PINNACLE_TRIGGER + .dr_port = DEVICE_DT_GET(DT_GPIO_CTLR(DT_DRV_INST(0), dr_gpios)), + .dr_pin = DT_INST_GPIO_PIN(0, dr_gpios), + .dr_flags = DT_INST_GPIO_FLAGS(0, dr_gpios), +#endif +}; + +DEVICE_DT_INST_DEFINE(0, pinnacle_init, device_pm_control_nop, &pinnacle_data, &pinnacle_config, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, &pinnacle_driver_api); diff --git a/app/drivers/sensor/cirque_trackpad/cirque_trackpad.h b/app/drivers/sensor/cirque_trackpad/cirque_trackpad.h new file mode 100644 index 00000000000..95c01a01ce1 --- /dev/null +++ b/app/drivers/sensor/cirque_trackpad/cirque_trackpad.h @@ -0,0 +1,80 @@ +#pragma once + +#include + +#define PINNACLE_READ 0xA0 +#define PINNACLE_WRITE 0x80 + +#define PINNACLE_AUTOINC 0xFC +#define PINNACLE_DUMMY 0xFB + +// Registers +#define PINNACLE_FW_ID 0x00 // ASIC ID. +#define PINNACLE_FW_VER 0x01 // Firmware Version Firmware revision number. +#define PINNACLE_STATUS1 0x02 // Contains status flags about the state of Pinnacle. +#define PINNACLE_SYS_CFG 0x03 // Contains system operation and configuration bits. +#define PINNACLE_SYS_CFG_EN_SLEEP BIT(2) +#define PINNACLE_SYS_CFG_SHUTDOWN BIT(1) +#define PINNACLE_SYS_CFG_RESET BIT(0) + +#define PINNACLE_FEED_CFG1 0x04 // Contains feed operation and configuration bits. +#define PINNACLE_FEED_CFG1_EN_FEED BIT(0) +#define PINNACLE_FEED_CFG1_ABS_MODE BIT(1) +#define PINNACLE_FEED_CFG1_DIS_FILT BIT(2) +#define PINNACLE_FEED_CFG1_DIS_X BIT(3) +#define PINNACLE_FEED_CFG1_DIS_Y BIT(4) +#define PINNACLE_FEED_CFG1_INV_X BIT(6) +#define PINNACLE_FEED_CFG1_INV_Y BIT(7) +#define PINNACLE_FEED_CFG2 0x05 // Contains feed operation and configuration bits. +#define PINNACLE_FEED_CFG2_EN_IM BIT(0) // Intellimouse +#define PINNACLE_FEED_CFG2_DIS_TAP BIT(1) // Disable all taps +#define PINNACLE_FEED_CFG2_DIS_SEC BIT(2) // Disable secondary tap +#define PINNACLE_FEED_CFG2_DIS_SCRL BIT(3) // Disable scroll +#define PINNACLE_FEED_CFG2_DIS_GE BIT(4) // Disable GlideExtend +#define PINNACLE_FEED_CFG2_SWAP_XY BIT(7) // Swap X & Y +#define PINNACLE_CAL_CFG 0x07 // Contains calibration configuration bits. +#define PINNACLE_PS2_AUX 0x08 // Contains Data register for PS/2 Aux Control. +#define PINNACLE_SAMPLE 0x09 // Sample Rate Number of samples generated per second. +#define PINNACLE_Z_IDLE 0x0A // Number of Z=0 packets sent when Z goes from >0 to 0. +#define PINNACLE_Z_SCALER 0x0B // Contains the pen Z_On threshold. +#define PINNACLE_SLEEP_INTERVAL 0x0C // Sleep Interval +#define PINNACLE_SLEEP_TIMER 0x0D // Sleep Timer +#define PINNACLE_AG_PACKET0 0x10 // trackpad Data (Pinnacle AG) +#define PINNACLE_2_2_PACKET0 0x12 // trackpad Data +#define PINNACLE_REG_COUNT 0x18 + +#define PINNACLE_PACKET0_BTN_PRIM BIT(0) // Primary button +#define PINNACLE_PACKET0_BTN_SEC BIT(1) // Secondary button +#define PINNACLE_PACKET0_BTN_AUX BIT(2) // Auxiliary (middle?) button +#define PINNACLE_PACKET0_X_SIGN BIT(4) // X delta sign +#define PINNACLE_PACKET0_Y_SIGN BIT(5) // Y delta sign + +struct pinnacle_data { + const struct device *spi; + int16_t dx, dy; + int8_t wheel; + uint8_t btn; +#ifdef CONFIG_PINNACLE_TRIGGER + const struct device *dev; + const struct sensor_trigger *data_ready_trigger; + struct gpio_callback gpio_cb; + sensor_trigger_handler_t data_ready_handler; +#if defined(CONFIG_PINNACLE_TRIGGER_OWN_THREAD) + K_THREAD_STACK_MEMBER(thread_stack, CONFIG_PINNACLE_THREAD_STACK_SIZE); + struct k_sem gpio_sem; + struct k_thread thread; +#elif defined(CONFIG_PINNACLE_TRIGGER_GLOBAL_THREAD) + struct k_work work; +#endif +#endif +}; + +struct pinnacle_config { + struct spi_cs_control spi_cs; + struct spi_config spi_config; + bool invert_x, invert_y, sleep_en, no_taps; +#ifdef CONFIG_PINNACLE_TRIGGER + const struct device *dr_port; + uint8_t dr_pin, dr_flags; +#endif +}; diff --git a/app/drivers/zephyr/dts/bindings/sensor/cirque,pinnacle.yaml b/app/drivers/zephyr/dts/bindings/sensor/cirque,pinnacle.yaml new file mode 100644 index 00000000000..fa7e561d07f --- /dev/null +++ b/app/drivers/zephyr/dts/bindings/sensor/cirque,pinnacle.yaml @@ -0,0 +1,27 @@ +description: | + Sensor driver for the Cirque Pinnacle trackpad ASICs + +compatible: "cirque,pinnacle" + +include: base.yaml + +on-bus: spi + +properties: + spi-max-frequency: + type: int + required: true + description: | + Maximum SPI clock speed supported by the device, in Hz. + dr-gpios: + type: phandle-array + required: true + description: Data ready pin for the trackpad + invert-x: + type: boolean + invert-y: + type: boolean + sleep: + type: boolean + no-taps: + type: boolean diff --git a/app/include/zmk/sensors.h b/app/include/zmk/sensors.h index 8c6c28b3848..09fbd5f69bc 100644 --- a/app/include/zmk/sensors.h +++ b/app/include/zmk/sensors.h @@ -10,3 +10,10 @@ #define ZMK_KEYMAP_HAS_SENSORS DT_NODE_HAS_STATUS(ZMK_KEYMAP_SENSORS_NODE, okay) #define ZMK_KEYMAP_SENSORS_LEN DT_PROP_LEN(ZMK_KEYMAP_SENSORS_NODE, sensors) #define ZMK_KEYMAP_SENSORS_BY_IDX(idx) DT_PHANDLE_BY_IDX(ZMK_KEYMAP_SENSORS_NODE, sensors, idx) + +#include + +enum { + // Cirque Pinnacle Glide Extend + SENSOR_ATTR_PINNACLE_GE = SENSOR_ATTR_PRIV_START, +}; From 14eb5fffbc6e4293a6e84c7b253abdc103ce749e Mon Sep 17 00:00:00 2001 From: "Dylan (Luberry) Kozicki" Date: Thu, 21 Apr 2022 08:50:03 -0400 Subject: [PATCH 009/157] fix pid for pmw3360 --- app/drivers/sensor/pmw33xx/pmw33xx.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/drivers/sensor/pmw33xx/pmw33xx.h b/app/drivers/sensor/pmw33xx/pmw33xx.h index fe071a3220b..31119e65899 100644 --- a/app/drivers/sensor/pmw33xx/pmw33xx.h +++ b/app/drivers/sensor/pmw33xx/pmw33xx.h @@ -16,7 +16,7 @@ #define PMW33XX_RD_MASK 0x7F #define PMW33XX_3389_PID 0x47 -#define PMW33XX_3360_PID 0x45 +#define PMW33XX_3360_PID 0x42 #define PMW33XX_REV 0x01 /* General Registers */ From 3e0780b7db81388f88db46bc5bfae9ae0c62f7aa Mon Sep 17 00:00:00 2001 From: "Dylan (Luberry) Kozicki" Date: Thu, 21 Apr 2022 09:20:37 -0400 Subject: [PATCH 010/157] remove accidental clear of rest enable --- app/drivers/sensor/pmw33xx/pmw33xx.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/drivers/sensor/pmw33xx/pmw33xx.c b/app/drivers/sensor/pmw33xx/pmw33xx.c index 5fcb1ae6cd5..486a885c098 100644 --- a/app/drivers/sensor/pmw33xx/pmw33xx.c +++ b/app/drivers/sensor/pmw33xx/pmw33xx.c @@ -319,8 +319,7 @@ static int pmw33xx_init_chip(const struct device *dev) { return -EIO; } - pmw33xx_write_reg(dev, PMW33XX_REG_CONFIG2, 0x00); // clear rest enable - pmw33xx_write_reg(dev, PMW33XX_REG_BURST, 0x01); // clear rest enable + pmw33xx_write_reg(dev, PMW33XX_REG_BURST, 0x01); struct pmw33xx_motion_burst val; pmw33xx_read_motion_burst(dev, &val); // read and throwout initial motion data From c03ef6b7d2de2ab1e31700d04a937da4b4074783 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Sun, 15 May 2022 02:29:39 +0800 Subject: [PATCH 011/157] feat(drivers): add driver for MAX17048 fuel gauge --- app/drivers/sensor/CMakeLists.txt | 3 +- app/drivers/sensor/Kconfig | 3 +- app/drivers/sensor/max17048/CMakeLists.txt | 9 + app/drivers/sensor/max17048/Kconfig | 9 + app/drivers/sensor/max17048/max17048.c | 196 +++++++++++++++++++++ app/drivers/sensor/max17048/max17048.h | 42 +++++ 6 files changed, 260 insertions(+), 2 deletions(-) create mode 100644 app/drivers/sensor/max17048/CMakeLists.txt create mode 100644 app/drivers/sensor/max17048/Kconfig create mode 100644 app/drivers/sensor/max17048/max17048.c create mode 100644 app/drivers/sensor/max17048/max17048.h diff --git a/app/drivers/sensor/CMakeLists.txt b/app/drivers/sensor/CMakeLists.txt index b549320f121..9654600a756 100644 --- a/app/drivers/sensor/CMakeLists.txt +++ b/app/drivers/sensor/CMakeLists.txt @@ -2,4 +2,5 @@ # SPDX-License-Identifier: MIT add_subdirectory_ifdef(CONFIG_ZMK_BATTERY battery) -add_subdirectory_ifdef(CONFIG_EC11 ec11) \ No newline at end of file +add_subdirectory_ifdef(CONFIG_EC11 ec11) +add_subdirectory_ifdef(CONFIG_MAX17048 max17048) diff --git a/app/drivers/sensor/Kconfig b/app/drivers/sensor/Kconfig index a828f6c63ab..877569e2a5f 100644 --- a/app/drivers/sensor/Kconfig +++ b/app/drivers/sensor/Kconfig @@ -2,4 +2,5 @@ # SPDX-License-Identifier: MIT rsource "battery/Kconfig" -rsource "ec11/Kconfig" \ No newline at end of file +rsource "ec11/Kconfig" +rsource "max17048/Kconfig" diff --git a/app/drivers/sensor/max17048/CMakeLists.txt b/app/drivers/sensor/max17048/CMakeLists.txt new file mode 100644 index 00000000000..e895fa11fb8 --- /dev/null +++ b/app/drivers/sensor/max17048/CMakeLists.txt @@ -0,0 +1,9 @@ +# Copyright (c) 2022 The ZMK Contributors +# SPDX-License-Identifier: MIT + +zephyr_include_directories(.) + +zephyr_library() + +zephyr_library_sources_ifdef(CONFIG_MAX17048 max17048.c) +zephyr_library_sources_ifndef(CONFIG_MAX17048 ${ZEPHYR_BASE}/misc/empty_file.c) diff --git a/app/drivers/sensor/max17048/Kconfig b/app/drivers/sensor/max17048/Kconfig new file mode 100644 index 00000000000..6d3de88b9bd --- /dev/null +++ b/app/drivers/sensor/max17048/Kconfig @@ -0,0 +1,9 @@ +# Copyright (c) 2022 The ZMK Contributors +# SPDX-License-Identifier: MIT + +config MAX17048 + bool "MAX17048 Fuel Gauge" + depends on I2C + help + Enable I2C-based driver for MAX17048/9 Fuel Gauge. Supports measuring + battery voltage and state-of-charge. diff --git a/app/drivers/sensor/max17048/max17048.c b/app/drivers/sensor/max17048/max17048.c new file mode 100644 index 00000000000..6540873c2b5 --- /dev/null +++ b/app/drivers/sensor/max17048/max17048.c @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#define DT_DRV_COMPAT maxim_max17048 + +#include +#include +#include +#include +#include +#include +#include + +#include "max17048.h" + +LOG_MODULE_REGISTER(MAX17048, CONFIG_SENSOR_LOG_LEVEL); + +static int read_register(const struct device *dev, uint8_t reg, uint16_t *value) { + + if (k_is_in_isr()) { + return -EWOULDBLOCK; + } + + struct max17048_drv_data *const drv_data = dev->data; + uint16_t dev_addr = ((struct max17048_config *)dev->config)->device_addr; + + uint16_t data = 0; + int ret = i2c_burst_read(drv_data->i2c, dev_addr, reg, (uint8_t *)&data, sizeof(data)); + if (ret != 0) { + LOG_DBG("i2c_write_read FAIL %d\n", ret); + return ret; + } + + *value = sys_le16_to_cpu(data); + return 0; +} + +static int write_register(const struct device *dev, uint8_t reg, uint16_t value) { + + if (k_is_in_isr()) { + return -EWOULDBLOCK; + } + + struct max17048_drv_data *const drv_data = dev->data; + + uint16_t data = sys_cpu_to_le16(value); + uint16_t dev_addr = ((struct max17048_config *)dev->config)->device_addr; + + return i2c_burst_write(drv_data->i2c, dev_addr, reg, (uint8_t *)&data, sizeof(data)); +} + +static int set_rcomp_value(const struct device *dev, uint8_t rcomp_value) { + + uint16_t tmp = 0; + int err = read_register(dev, REG_CONFIG, &tmp); + if (err != 0) { + return err; + } + + tmp = ((uint16_t)rcomp_value << 8) | (tmp & 0xFF); + err = write_register(dev, REG_CONFIG, tmp); + if (err != 0) { + return err; + } + + LOG_DBG("set RCOMP to %d", rcomp_value); + return 0; +} + +static int set_sleep_enabled(const struct device *dev, bool sleep) { + uint16_t tmp = 0; + int err = read_register(dev, REG_CONFIG, &tmp); + if (err != 0) { + return err; + } + + if (sleep) { + tmp |= 0x80; + } else { + tmp &= ~0x0080; + } + + err = write_register(dev, REG_CONFIG, tmp); + if (err != 0) { + return err; + } + + LOG_DBG("sleep mode %s", sleep ? "enabled" : "disabled"); + return 0; +} + +static int max17048_sample_fetch(const struct device *dev, enum sensor_channel chan) { + struct max17048_drv_data *const data = dev->data; + + if (chan != SENSOR_CHAN_GAUGE_VOLTAGE && chan != SENSOR_CHAN_GAUGE_STATE_OF_CHARGE) { + LOG_DBG("unsupported channel %d", chan); + return -ENOTSUP; + } + + int err = read_register(dev, REG_STATE_OF_CHARGE, &data->raw_state_of_charge); + if (err != 0) { + LOG_WRN("failed to read state-of-charge: %d", err); + return err; + } + + err = read_register(dev, REG_VCELL, &data->raw_vcell); + if (err != 0) { + LOG_WRN("failed to read vcell: %d", err); + return err; + } + + LOG_DBG("read values: soc=%d, vcell=%d", data->raw_state_of_charge, data->raw_vcell); + + return 0; +} + +static int max17048_channel_get(const struct device *dev, enum sensor_channel chan, + struct sensor_value *val) { + + struct max17048_drv_data *const data = dev->data; + unsigned int tmp = 0; + + switch (chan) { + case SENSOR_CHAN_GAUGE_VOLTAGE: + // 1250 / 16 = 78.125 + tmp = data->raw_vcell * 1250 / 16; + val->val1 = tmp / 1000000; + val->val2 = tmp % 1000000; + break; + + case SENSOR_CHAN_GAUGE_STATE_OF_CHARGE: + val->val1 = (data->raw_state_of_charge >> 8); + val->val2 = (data->raw_state_of_charge & 0xFF) * 1000000 / 256; + break; + + default: + return -ENOTSUP; + } + + return 0; +} + +static int max17048_init(const struct device *dev) { + struct max17048_drv_data *driver_data = dev->data; + const struct max17048_config *driver_config = dev->config; + + driver_data->i2c = device_get_binding((char *)driver_config->i2c_device_name); + if (!driver_data->i2c) { + LOG_DBG("Unable to get i2c device"); + return -ENODEV; + } + + if (!device_is_ready(driver_data->i2c)) { + LOG_WRN("i2c bus not ready!"); + return -EINVAL; + } + + uint16_t ic_version = 0; + int err = read_register(dev, REG_VERSION, &ic_version); + if (err != 0) { + LOG_WRN("could not get IC version!"); + return err; + } + + // bring the device out of sleep + set_sleep_enabled(dev, false); + + // set the default rcomp value -- 0x97, as stated in the datasheet + set_rcomp_value(dev, 0x97); + + LOG_INF("device initialised at 0x%x (i2c=%s) (version %d)", driver_config->device_addr, + driver_config->i2c_device_name, ic_version); + + return 0; +} + +static const struct sensor_driver_api max17048_api_table = {.sample_fetch = max17048_sample_fetch, + .channel_get = max17048_channel_get}; + +#define MAX17048_INIT(inst) \ + static struct max17048_config max17048_##inst##_config = { \ + .i2c_device_name = DT_INST_BUS_LABEL(inst), \ + .device_addr = DT_INST_REG_ADDR(inst), \ + }; \ + \ + static struct max17048_drv_data max17048_##inst##_drvdata = {}; \ + \ + /* This has to init after SPI master */ \ + DEVICE_DT_INST_DEFINE(inst, max17048_init, NULL, &max17048_##inst##_drvdata, \ + &max17048_##inst##_config, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \ + &max17048_api_table); + +DT_INST_FOREACH_STATUS_OKAY(MAX17048_INIT) diff --git a/app/drivers/sensor/max17048/max17048.h b/app/drivers/sensor/max17048/max17048.h new file mode 100644 index 00000000000..db728994016 --- /dev/null +++ b/app/drivers/sensor/max17048/max17048.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define REG_VCELL 0x02 +#define REG_STATE_OF_CHARGE 0x04 +#define REG_MODE 0x06 +#define REG_VERSION 0x08 +#define REG_HIBERNATE 0x0A +#define REG_CONFIG 0x0C +#define REG_VALERT 0x14 +#define REG_CHARGE_RATE 0x16 +#define REG_VRESET 0x18 +#define REG_STATUS 0x1A + +struct max17048_config { + const char *i2c_device_name; + uint16_t device_addr; +}; + +struct max17048_drv_data { + const struct device *i2c; + + uint16_t raw_state_of_charge; + uint16_t raw_charge_rate; + uint16_t raw_vcell; +}; + +#ifdef __cplusplus +} +#endif From 2921a17ad4b46c7d09cd8c598dba0eab05a656be Mon Sep 17 00:00:00 2001 From: Shawn Meier Date: Tue, 17 May 2022 18:01:19 -0600 Subject: [PATCH 012/157] changes from krikun98/mouse-pr rebased on zmkfirmware/main --- app/CMakeLists.txt | 10 ++ app/Kconfig | 11 ++ app/dts/behaviors.dtsi | 5 +- app/dts/behaviors/mouse_key_press.dtsi | 9 + app/dts/behaviors/mouse_move.dtsi | 12 ++ app/dts/behaviors/mouse_scroll.dtsi | 12 ++ .../zmk,behavior-mouse-key-press.yaml | 5 + .../behaviors/zmk,behavior-mouse-move.yaml | 13 ++ .../behaviors/zmk,behavior-mouse-scroll.yaml | 13 ++ app/include/dt-bindings/zmk/hid_usage_pages.h | 1 + app/include/dt-bindings/zmk/mouse.h | 55 ++++++ app/include/zmk/endpoints.h | 1 + .../zmk/events/mouse_button_state_changed.h | 27 +++ .../zmk/events/mouse_move_state_changed.h | 33 ++++ .../zmk/events/mouse_scroll_state_changed.h | 34 ++++ app/include/zmk/events/mouse_tick.h | 39 +++++ app/include/zmk/hid.h | 148 +++++++++++++++- app/include/zmk/hog.h | 1 + app/include/zmk/mouse.h | 30 ++++ app/src/behaviors/behavior_mouse_key_press.c | 48 ++++++ app/src/behaviors/behavior_mouse_move.c | 57 +++++++ app/src/behaviors/behavior_mouse_scroll.c | 58 +++++++ app/src/endpoints.c | 35 ++++ app/src/events/mouse_button_state_changed.c | 10 ++ app/src/events/mouse_move_state_changed.c | 10 ++ app/src/events/mouse_scroll_state_changed.c | 10 ++ app/src/events/mouse_tick.c | 10 ++ app/src/hid.c | 86 ++++++++++ app/src/hid_listener.c | 12 +- app/src/hog.c | 89 ++++++++++ app/src/main.c | 8 + app/src/mouse/Kconfig | 38 +++++ app/src/mouse/key_listener.c | 160 ++++++++++++++++++ app/src/mouse/main.c | 30 ++++ app/src/mouse/tick_listener.c | 102 +++++++++++ app/tests/mouse-keys/mmv/events.patterns | 1 + .../mouse-keys/mmv/keycode_events.snapshot | 2 + app/tests/mouse-keys/mmv/native_posix.keymap | 26 +++ docs/docs/behaviors/mouse-emulation.md | 110 ++++++++++++ docs/docs/intro.md | 3 +- docs/sidebars.js | 1 + 41 files changed, 1351 insertions(+), 14 deletions(-) create mode 100644 app/dts/behaviors/mouse_key_press.dtsi create mode 100644 app/dts/behaviors/mouse_move.dtsi create mode 100644 app/dts/behaviors/mouse_scroll.dtsi create mode 100644 app/dts/bindings/behaviors/zmk,behavior-mouse-key-press.yaml create mode 100644 app/dts/bindings/behaviors/zmk,behavior-mouse-move.yaml create mode 100644 app/dts/bindings/behaviors/zmk,behavior-mouse-scroll.yaml create mode 100644 app/include/dt-bindings/zmk/mouse.h create mode 100644 app/include/zmk/events/mouse_button_state_changed.h create mode 100644 app/include/zmk/events/mouse_move_state_changed.h create mode 100644 app/include/zmk/events/mouse_scroll_state_changed.h create mode 100644 app/include/zmk/events/mouse_tick.h create mode 100644 app/include/zmk/mouse.h create mode 100644 app/src/behaviors/behavior_mouse_key_press.c create mode 100644 app/src/behaviors/behavior_mouse_move.c create mode 100644 app/src/behaviors/behavior_mouse_scroll.c create mode 100644 app/src/events/mouse_button_state_changed.c create mode 100644 app/src/events/mouse_move_state_changed.c create mode 100644 app/src/events/mouse_scroll_state_changed.c create mode 100644 app/src/events/mouse_tick.c create mode 100644 app/src/mouse/Kconfig create mode 100644 app/src/mouse/key_listener.c create mode 100644 app/src/mouse/main.c create mode 100644 app/src/mouse/tick_listener.c create mode 100644 app/tests/mouse-keys/mmv/events.patterns create mode 100644 app/tests/mouse-keys/mmv/keycode_events.snapshot create mode 100644 app/tests/mouse-keys/mmv/native_posix.keymap create mode 100644 docs/docs/behaviors/mouse-emulation.md diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 4b61fc72174..351505ad777 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -24,6 +24,9 @@ target_sources(app PRIVATE src/stdlib.c) target_sources(app PRIVATE src/activity.c) target_sources(app PRIVATE src/kscan.c) target_sources(app PRIVATE src/matrix_transform.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(app PRIVATE src/sensors.c) target_sources_ifdef(CONFIG_ZMK_WPM app PRIVATE src/wpm.c) target_sources(app PRIVATE src/event_manager.c) @@ -31,6 +34,10 @@ target_sources_ifdef(CONFIG_ZMK_EXT_POWER app PRIVATE src/ext_power_generic.c) target_sources(app PRIVATE src/events/activity_state_changed.c) target_sources(app PRIVATE src/events/position_state_changed.c) target_sources(app PRIVATE src/events/sensor_event.c) +target_sources(app PRIVATE src/events/mouse_button_state_changed.c) +target_sources(app PRIVATE src/events/mouse_move_state_changed.c) +target_sources(app PRIVATE src/events/mouse_tick.c) +target_sources(app PRIVATE src/events/mouse_scroll_state_changed.c) target_sources_ifdef(CONFIG_ZMK_WPM app PRIVATE src/events/wpm_state_changed.c) target_sources_ifdef(CONFIG_USB_DEVICE_STACK app PRIVATE src/events/usb_conn_state_changed.c) target_sources(app PRIVATE src/behaviors/behavior_reset.c) @@ -53,6 +60,9 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL) target_sources(app PRIVATE src/behaviors/behavior_transparent.c) target_sources(app PRIVATE src/behaviors/behavior_none.c) target_sources(app PRIVATE src/behaviors/behavior_sensor_rotate_key_press.c) + target_sources(app PRIVATE src/behaviors/behavior_mouse_key_press.c) + target_sources(app PRIVATE src/behaviors/behavior_mouse_move.c) + target_sources(app PRIVATE src/behaviors/behavior_mouse_scroll.c) target_sources(app PRIVATE src/combo.c) target_sources(app PRIVATE src/behavior_queue.c) target_sources(app PRIVATE src/conditional_layer.c) diff --git a/app/Kconfig b/app/Kconfig index 4bcd88b09af..571c2d2dbe0 100644 --- a/app/Kconfig +++ b/app/Kconfig @@ -127,6 +127,10 @@ config ZMK_BLE_CONSUMER_REPORT_QUEUE_SIZE int "Max number of consumer HID reports to queue for sending over BLE" default 5 +config ZMK_BLE_MOUSE_REPORT_QUEUE_SIZE + int "Max number of mouse HID reports to queue for sending over BLE" + default 20 + config ZMK_BLE_CLEAR_BONDS_ON_START bool "Configuration that clears all bond information from the keyboard on startup." default n @@ -278,6 +282,13 @@ endif #Display/LED Options endmenu +menu "Mouse Options" + +rsource "src/mouse/Kconfig" + +#Mouse Options +endmenu + menu "Power Management" config ZMK_IDLE_TIMEOUT diff --git a/app/dts/behaviors.dtsi b/app/dts/behaviors.dtsi index b3502cbbc13..77eccf912fd 100644 --- a/app/dts/behaviors.dtsi +++ b/app/dts/behaviors.dtsi @@ -18,4 +18,7 @@ #include #include #include -#include \ No newline at end of file +#include +#include +#include +#include diff --git a/app/dts/behaviors/mouse_key_press.dtsi b/app/dts/behaviors/mouse_key_press.dtsi new file mode 100644 index 00000000000..8b2aacb3663 --- /dev/null +++ b/app/dts/behaviors/mouse_key_press.dtsi @@ -0,0 +1,9 @@ +/ { + behaviors { + /omit-if-no-ref/ mkp: behavior_mouse_key_press { + compatible = "zmk,behavior-mouse-key-press"; + label = "MOUSE_KEY_PRESS"; + #binding-cells = <1>; + }; + }; +}; diff --git a/app/dts/behaviors/mouse_move.dtsi b/app/dts/behaviors/mouse_move.dtsi new file mode 100644 index 00000000000..d34329c8067 --- /dev/null +++ b/app/dts/behaviors/mouse_move.dtsi @@ -0,0 +1,12 @@ +/ { + behaviors { + /omit-if-no-ref/ mmv: behavior_mouse_move { + compatible = "zmk,behavior-mouse-move"; + label = "MOUSE_MOVE"; + #binding-cells = <1>; + delay-ms = <0>; + time-to-max-speed-ms = <300>; + acceleration-exponent = <1>; + }; + }; +}; diff --git a/app/dts/behaviors/mouse_scroll.dtsi b/app/dts/behaviors/mouse_scroll.dtsi new file mode 100644 index 00000000000..fb54886dcbe --- /dev/null +++ b/app/dts/behaviors/mouse_scroll.dtsi @@ -0,0 +1,12 @@ +/ { + behaviors { + /omit-if-no-ref/ mwh: msc: behavior_mouse_scroll { + compatible = "zmk,behavior-mouse-scroll"; + label = "MOUSE_SCROLL"; + #binding-cells = <1>; + delay-ms = <0>; + time-to-max-speed-ms = <300>; + acceleration-exponent = <0>; + }; + }; +}; diff --git a/app/dts/bindings/behaviors/zmk,behavior-mouse-key-press.yaml b/app/dts/bindings/behaviors/zmk,behavior-mouse-key-press.yaml new file mode 100644 index 00000000000..8540916b72a --- /dev/null +++ b/app/dts/bindings/behaviors/zmk,behavior-mouse-key-press.yaml @@ -0,0 +1,5 @@ +description: Mouse key press/release behavior + +compatible: "zmk,behavior-mouse-key-press" + +include: one_param.yaml diff --git a/app/dts/bindings/behaviors/zmk,behavior-mouse-move.yaml b/app/dts/bindings/behaviors/zmk,behavior-mouse-move.yaml new file mode 100644 index 00000000000..73ec34ec2db --- /dev/null +++ b/app/dts/bindings/behaviors/zmk,behavior-mouse-move.yaml @@ -0,0 +1,13 @@ +description: Mouse move + +compatible: "zmk,behavior-mouse-move" + +include: one_param.yaml + +properties: + delay-ms: + type: int + time-to-max-speed-ms: + type: int + acceleration-exponent: + type: int diff --git a/app/dts/bindings/behaviors/zmk,behavior-mouse-scroll.yaml b/app/dts/bindings/behaviors/zmk,behavior-mouse-scroll.yaml new file mode 100644 index 00000000000..5a932bc5904 --- /dev/null +++ b/app/dts/bindings/behaviors/zmk,behavior-mouse-scroll.yaml @@ -0,0 +1,13 @@ +description: Mouse scroll + +compatible: "zmk,behavior-mouse-scroll" + +include: one_param.yaml + +properties: + delay-ms: + type: int + time-to-max-speed-ms: + type: int + acceleration-exponent: + type: int diff --git a/app/include/dt-bindings/zmk/hid_usage_pages.h b/app/include/dt-bindings/zmk/hid_usage_pages.h index 2ccdba5540f..7fa54fd88b9 100644 --- a/app/include/dt-bindings/zmk/hid_usage_pages.h +++ b/app/include/dt-bindings/zmk/hid_usage_pages.h @@ -26,6 +26,7 @@ #define HID_USAGE_GDV (0x06) // Generic Device Controls #define HID_USAGE_KEY (0x07) // Keyboard/Keypad #define HID_USAGE_LED (0x08) // LED +#define HID_USAGE_BUTTON (0x09) // Button #define HID_USAGE_TELEPHONY (0x0B) // Telephony Device #define HID_USAGE_CONSUMER (0x0C) // Consumer #define HID_USAGE_DIGITIZERS (0x0D) // Digitizers diff --git a/app/include/dt-bindings/zmk/mouse.h b/app/include/dt-bindings/zmk/mouse.h new file mode 100644 index 00000000000..cf0415c9b2a --- /dev/null +++ b/app/include/dt-bindings/zmk/mouse.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ +#pragma once + +/* Mouse press behavior */ +/* Left click */ +#define MB1 (0x01) +#define LCLK (MB1) + +/* Right click */ +#define MB2 (0x02) +#define RCLK (MB2) + +/* Middle click */ +#define MB3 (0x04) +#define MCLK (MB3) + +#define MB4 (0x08) + +#define MB5 (0x10) + +#define MB6 (0x20) + +#define MB7 (0x40) + +#define MB8 (0x80) + +/* Mouse move behavior */ +#define MOVE_VERT(vert) ((vert)&0xFFFF) +#define MOVE_VERT_DECODE(encoded) (int16_t)((encoded)&0x0000FFFF) +#define MOVE_HOR(hor) (((hor)&0xFFFF) << 16) +#define MOVE_HOR_DECODE(encoded) (int16_t)(((encoded)&0xFFFF0000) >> 16) + +#define MOVE(hor, vert) (MOVE_HOR(hor) + MOVE_VERT(vert)) + +#define MOVE_UP MOVE_VERT(-600) +#define MOVE_DOWN MOVE_VERT(600) +#define MOVE_LEFT MOVE_HOR(-600) +#define MOVE_RIGHT MOVE_HOR(600) + +/* Mouse scroll behavior */ +#define SCROLL_VERT(vert) ((vert)&0xFFFF) +#define SCROLL_VERT_DECODE(encoded) (int16_t)((encoded)&0x0000FFFF) +#define SCROLL_HOR(hor) (((hor)&0xFFFF) << 16) +#define SCROLL_HOR_DECODE(encoded) (int16_t)(((encoded)&0xFFFF0000) >> 16) + +#define SCROLL(hor, vert) (SCROLL_HOR(hor) + SCROLL_VERT(vert)) + +#define SCROLL_UP SCROLL_VERT(10) +#define SCROLL_DOWN SCROLL_VERT(-10) +#define SCROLL_LEFT SCROLL_HOR(-10) +#define SCROLL_RIGHT SCROLL_HOR(10) diff --git a/app/include/zmk/endpoints.h b/app/include/zmk/endpoints.h index c8860533e1d..450d7ea370d 100644 --- a/app/include/zmk/endpoints.h +++ b/app/include/zmk/endpoints.h @@ -13,3 +13,4 @@ int zmk_endpoints_toggle(); enum zmk_endpoint zmk_endpoints_selected(); int zmk_endpoints_send_report(uint16_t usage_page); +int zmk_endpoints_send_mouse_report(); diff --git a/app/include/zmk/events/mouse_button_state_changed.h b/app/include/zmk/events/mouse_button_state_changed.h new file mode 100644 index 00000000000..7ec4d2087c5 --- /dev/null +++ b/app/include/zmk/events/mouse_button_state_changed.h @@ -0,0 +1,27 @@ + +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include +#include +#include +#include + +struct zmk_mouse_button_state_changed { + zmk_mouse_button_t buttons; + bool state; + int64_t timestamp; +}; + +ZMK_EVENT_DECLARE(zmk_mouse_button_state_changed); + +static inline struct zmk_mouse_button_state_changed_event * +zmk_mouse_button_state_changed_from_encoded(uint32_t encoded, bool pressed, int64_t timestamp) { + return new_zmk_mouse_button_state_changed((struct zmk_mouse_button_state_changed){ + .buttons = HID_USAGE_ID(encoded), .state = pressed, .timestamp = timestamp}); +} diff --git a/app/include/zmk/events/mouse_move_state_changed.h b/app/include/zmk/events/mouse_move_state_changed.h new file mode 100644 index 00000000000..8866f81d4e8 --- /dev/null +++ b/app/include/zmk/events/mouse_move_state_changed.h @@ -0,0 +1,33 @@ + +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include +#include +#include + +struct zmk_mouse_move_state_changed { + struct vector2d max_speed; + struct mouse_config config; + bool state; + int64_t timestamp; +}; + +ZMK_EVENT_DECLARE(zmk_mouse_move_state_changed); + +static inline struct zmk_mouse_move_state_changed_event * +zmk_mouse_move_state_changed_from_encoded(uint32_t encoded, struct mouse_config config, + bool pressed, int64_t timestamp) { + struct vector2d max_speed = (struct vector2d){ + .x = MOVE_HOR_DECODE(encoded), + .y = MOVE_VERT_DECODE(encoded), + }; + + return new_zmk_mouse_move_state_changed((struct zmk_mouse_move_state_changed){ + .max_speed = max_speed, .config = config, .state = pressed, .timestamp = timestamp}); +} diff --git a/app/include/zmk/events/mouse_scroll_state_changed.h b/app/include/zmk/events/mouse_scroll_state_changed.h new file mode 100644 index 00000000000..fa60e8a7422 --- /dev/null +++ b/app/include/zmk/events/mouse_scroll_state_changed.h @@ -0,0 +1,34 @@ + +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include +#include +#include +#include + +struct zmk_mouse_scroll_state_changed { + struct vector2d max_speed; + struct mouse_config config; + bool state; + int64_t timestamp; +}; + +ZMK_EVENT_DECLARE(zmk_mouse_scroll_state_changed); + +static inline struct zmk_mouse_scroll_state_changed_event * +zmk_mouse_scroll_state_changed_from_encoded(uint32_t encoded, struct mouse_config config, + bool pressed, int64_t timestamp) { + struct vector2d max_speed = (struct vector2d){ + .x = SCROLL_HOR_DECODE(encoded), + .y = SCROLL_VERT_DECODE(encoded), + }; + + return new_zmk_mouse_scroll_state_changed((struct zmk_mouse_scroll_state_changed){ + .max_speed = max_speed, .config = config, .state = pressed, .timestamp = timestamp}); +} diff --git a/app/include/zmk/events/mouse_tick.h b/app/include/zmk/events/mouse_tick.h new file mode 100644 index 00000000000..c75b9b4f86e --- /dev/null +++ b/app/include/zmk/events/mouse_tick.h @@ -0,0 +1,39 @@ + +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include +#include +#include +#include + +struct zmk_mouse_tick { + struct vector2d max_move; + struct vector2d max_scroll; + struct mouse_config move_config; + struct mouse_config scroll_config; + int64_t *start_time; + int64_t timestamp; +}; + +ZMK_EVENT_DECLARE(zmk_mouse_tick); + +static inline struct zmk_mouse_tick_event *zmk_mouse_tick(struct vector2d max_move, + struct vector2d max_scroll, + struct mouse_config move_config, + struct mouse_config scroll_config, + int64_t *movement_start) { + return new_zmk_mouse_tick((struct zmk_mouse_tick){ + .max_move = max_move, + .max_scroll = max_scroll, + .move_config = move_config, + .scroll_config = scroll_config, + .start_time = movement_start, + .timestamp = k_uptime_get(), + }); +} diff --git a/app/include/zmk/hid.h b/app/include/zmk/hid.h index 902b76d15a4..b0294a10577 100644 --- a/app/include/zmk/hid.h +++ b/app/include/zmk/hid.h @@ -10,11 +10,10 @@ #include #include +#include #include #include -#define ZMK_HID_KEYBOARD_NKRO_MAX_USAGE HID_USAGE_KEY_KEYPAD_EQUAL - #define COLLECTION_REPORT 0x03 static const uint8_t zmk_hid_report_desc[] = { @@ -85,10 +84,123 @@ static const uint8_t zmk_hid_report_desc[] = { #else #error "A proper consumer HID report usage range must be selected" #endif - HID_REPORT_COUNT(CONFIG_ZMK_HID_CONSUMER_REPORT_SIZE), - /* INPUT (Data,Ary,Abs) */ - HID_INPUT(0x00), - HID_END_COLLECTION, + /* REPORT_COUNT (CONFIG_ZMK_HID_CONSUMER_REPORT_SIZE) */ + HID_GI_REPORT_COUNT, + CONFIG_ZMK_HID_CONSUMER_REPORT_SIZE, + HID_MI_INPUT, + 0x00, + /* END COLLECTION */ + HID_MI_COLLECTION_END, + + /* USAGE_PAGE (Generic Desktop) */ + HID_GI_USAGE_PAGE, + HID_USAGE_GD, + /* USAGE (Mouse) */ + HID_LI_USAGE, + HID_USAGE_GD_MOUSE, + /* COLLECTION (Application) */ + HID_MI_COLLECTION, + COLLECTION_APPLICATION, + /* REPORT ID (4) */ + HID_GI_REPORT_ID, + 0x04, + /* USAGE (Pointer) */ + HID_LI_USAGE, + HID_USAGE_GD_POINTER, + /* COLLECTION (Physical) */ + HID_MI_COLLECTION, + COLLECTION_PHYSICAL, + /* USAGE_PAGE (Button) */ + HID_GI_USAGE_PAGE, + HID_USAGE_BUTTON, + /* USAGE_MINIMUM (0x1) (button 1?) */ + HID_LI_USAGE_MIN(1), + 0x1, + /* USAGE_MAXIMUM (0x10) (button 5? Buttons up to 8 still work) */ + HID_LI_USAGE_MAX(1), + 0x10, + /* LOGICAL_MINIMUM (0) */ + HID_GI_LOGICAL_MIN(1), + 0x00, + /* LOGICAL_MAXIMUM (1) */ + HID_GI_LOGICAL_MAX(1), + 0x01, + /* REPORT_SIZE (1) */ + HID_GI_REPORT_SIZE, + 0x01, + /* REPORT_COUNT (16) */ + HID_GI_REPORT_COUNT, + 0x10, + /* INPUT (Data,Var,Abs) */ + HID_MI_INPUT, + 0x02, + /* USAGE_PAGE (Generic Desktop) */ + HID_GI_USAGE_PAGE, + HID_USAGE_GD, + /* LOGICAL_MINIMUM (-32767) */ + HID_GI_LOGICAL_MIN(2), + 0x01, + 0x80, + /* LOGICAL_MAXIMUM (32767) */ + HID_GI_LOGICAL_MAX(2), + 0xFF, + 0x7F, + /* REPORT_SIZE (16) */ + HID_GI_REPORT_SIZE, + 0x10, + /* REPORT_COUNT (2) */ + HID_GI_REPORT_COUNT, + 0x02, + /* USAGE (X) */ // Vertical scroll + HID_LI_USAGE, + HID_USAGE_GD_X, + /* USAGE (Y) */ + HID_LI_USAGE, + HID_USAGE_GD_Y, + /* Input (Data,Var,Rel) */ + HID_MI_INPUT, + 0x06, + /* LOGICAL_MINIMUM (-127) */ + HID_GI_LOGICAL_MIN(1), + 0x81, + /* LOGICAL_MAXIMUM (127) */ + HID_GI_LOGICAL_MAX(1), + 0x7F, + /* REPORT_SIZE (8) */ + HID_GI_REPORT_SIZE, + 0x08, + /* REPORT_COUNT (1) */ + HID_GI_REPORT_COUNT, + 0x01, + /* USAGE (Wheel) */ + HID_LI_USAGE, + HID_USAGE_GD_WHEEL, + /* Input (Data,Var,Rel) */ + HID_MI_INPUT, + 0x06, + /* USAGE_PAGE (Consumer) */ // Horizontal scroll + HID_GI_USAGE_PAGE, + HID_USAGE_CONSUMER, + /* USAGE (AC Pan) */ + 0x0A, + 0x38, + 0x02, + /* LOGICAL_MINIMUM (-127) */ + HID_GI_LOGICAL_MIN(1), + 0x81, + /* LOGICAL_MAXIMUM (127) */ + HID_GI_LOGICAL_MAX(1), + 0x7F, + /* REPORT_COUNT (1) */ + HID_GI_REPORT_COUNT, + 0x01, + /* Input (Data,Var,Rel) */ + HID_MI_INPUT, + 0x06, + /* END COLLECTION */ + HID_MI_COLLECTION_END, + /* END COLLECTION */ + HID_MI_COLLECTION_END, }; // struct zmk_hid_boot_report @@ -126,6 +238,19 @@ struct zmk_hid_consumer_report { struct zmk_hid_consumer_report_body body; } __packed; +struct zmk_hid_mouse_report_body { + zmk_mouse_button_flags_t buttons; + int16_t x; + int16_t y; + int8_t scroll_y; + int8_t scroll_x; +} __packed; + +struct zmk_hid_mouse_report { + uint8_t report_id; + struct zmk_hid_mouse_report_body body; +} __packed; + zmk_mod_flags_t zmk_hid_get_explicit_mods(); int zmk_hid_register_mod(zmk_mod_t modifier); int zmk_hid_unregister_mod(zmk_mod_t modifier); @@ -150,5 +275,16 @@ int zmk_hid_press(uint32_t usage); int zmk_hid_release(uint32_t usage); bool zmk_hid_is_pressed(uint32_t usage); +int zmk_hid_mouse_button_press(zmk_mouse_button_t button); +int zmk_hid_mouse_button_release(zmk_mouse_button_t button); +int zmk_hid_mouse_buttons_press(zmk_mouse_button_flags_t buttons); +int zmk_hid_mouse_buttons_release(zmk_mouse_button_flags_t buttons); +void zmk_hid_mouse_movement_set(int16_t x, int16_t y); +void zmk_hid_mouse_scroll_set(int8_t x, int8_t y); +void zmk_hid_mouse_movement_update(int16_t x, int16_t y); +void zmk_hid_mouse_scroll_update(int8_t x, int8_t y); +void zmk_hid_mouse_clear(); + struct zmk_hid_keyboard_report *zmk_hid_get_keyboard_report(); struct zmk_hid_consumer_report *zmk_hid_get_consumer_report(); +struct zmk_hid_mouse_report *zmk_hid_get_mouse_report(); diff --git a/app/include/zmk/hog.h b/app/include/zmk/hog.h index 7523fb661ad..9debc3ff319 100644 --- a/app/include/zmk/hog.h +++ b/app/include/zmk/hog.h @@ -13,3 +13,4 @@ int zmk_hog_init(); int zmk_hog_send_keyboard_report(struct zmk_hid_keyboard_report_body *body); int zmk_hog_send_consumer_report(struct zmk_hid_consumer_report_body *body); +int zmk_hog_send_mouse_report(struct zmk_hid_mouse_report_body *body); diff --git a/app/include/zmk/mouse.h b/app/include/zmk/mouse.h new file mode 100644 index 00000000000..f8f857441e2 --- /dev/null +++ b/app/include/zmk/mouse.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include +#include + +typedef uint16_t zmk_mouse_button_flags_t; +typedef uint16_t zmk_mouse_button_t; + +struct mouse_config { + int delay_ms; + int time_to_max_speed_ms; + // acceleration exponent 0: uniform speed + // acceleration exponent 1: uniform acceleration + // acceleration exponent 2: uniform jerk + int acceleration_exponent; +}; + +struct vector2d { + float x; + float y; +}; + +struct k_work_q *zmk_mouse_work_q(); +int zmk_mouse_init(); \ No newline at end of file diff --git a/app/src/behaviors/behavior_mouse_key_press.c b/app/src/behaviors/behavior_mouse_key_press.c new file mode 100644 index 00000000000..e5f2709cbf2 --- /dev/null +++ b/app/src/behaviors/behavior_mouse_key_press.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2021 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#define DT_DRV_COMPAT zmk_behavior_mouse_key_press + +#include +#include +#include + +#include +#include +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) + +static int behavior_mouse_key_press_init(const struct device *dev) { return 0; }; + +static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + LOG_DBG("position %d keycode 0x%02X", event.position, binding->param1); + + return ZMK_EVENT_RAISE( + zmk_mouse_button_state_changed_from_encoded(binding->param1, true, event.timestamp)); +} + +static int on_keymap_binding_released(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + LOG_DBG("position %d keycode 0x%02X", event.position, binding->param1); + return ZMK_EVENT_RAISE( + zmk_mouse_button_state_changed_from_encoded(binding->param1, false, event.timestamp)); +} + +static const struct behavior_driver_api behavior_mouse_key_press_driver_api = { + .binding_pressed = on_keymap_binding_pressed, .binding_released = on_keymap_binding_released}; + +#define KP_INST(n) \ + DEVICE_DT_INST_DEFINE(n, behavior_mouse_key_press_init, device_pm_control_nop, NULL, NULL, \ + APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \ + &behavior_mouse_key_press_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(KP_INST) + +#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */ \ No newline at end of file diff --git a/app/src/behaviors/behavior_mouse_move.c b/app/src/behaviors/behavior_mouse_move.c new file mode 100644 index 00000000000..5977a039d1c --- /dev/null +++ b/app/src/behaviors/behavior_mouse_move.c @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2021 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#define DT_DRV_COMPAT zmk_behavior_mouse_move + +#include +#include +#include + +#include +#include +#include +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) + +static int behavior_mouse_move_init(const struct device *dev) { return 0; }; + +static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + LOG_DBG("position %d keycode 0x%02X", event.position, binding->param1); + const struct device *dev = device_get_binding(binding->behavior_dev); + const struct mouse_config *config = dev->config; + return ZMK_EVENT_RAISE( + zmk_mouse_move_state_changed_from_encoded(binding->param1, *config, true, event.timestamp)); +} + +static int on_keymap_binding_released(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + LOG_DBG("position %d keycode 0x%02X", event.position, binding->param1); + const struct device *dev = device_get_binding(binding->behavior_dev); + const struct mouse_config *config = dev->config; + return ZMK_EVENT_RAISE(zmk_mouse_move_state_changed_from_encoded(binding->param1, *config, + false, event.timestamp)); +} + +static const struct behavior_driver_api behavior_mouse_move_driver_api = { + .binding_pressed = on_keymap_binding_pressed, .binding_released = on_keymap_binding_released}; + +#define KP_INST(n) \ + static struct mouse_config behavior_mouse_move_config_##n = { \ + .delay_ms = DT_INST_PROP(n, delay_ms), \ + .time_to_max_speed_ms = DT_INST_PROP(n, time_to_max_speed_ms), \ + .acceleration_exponent = DT_INST_PROP(n, acceleration_exponent), \ + }; \ + DEVICE_DT_INST_DEFINE(n, behavior_mouse_move_init, device_pm_control_nop, NULL, \ + &behavior_mouse_move_config_##n, APPLICATION, \ + CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_mouse_move_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(KP_INST) + +#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */ diff --git a/app/src/behaviors/behavior_mouse_scroll.c b/app/src/behaviors/behavior_mouse_scroll.c new file mode 100644 index 00000000000..6416235265d --- /dev/null +++ b/app/src/behaviors/behavior_mouse_scroll.c @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2021 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#define DT_DRV_COMPAT zmk_behavior_mouse_scroll + +#include +#include +#include + +#include +#include +#include +#include +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) + +static int behavior_mouse_scroll_init(const struct device *dev) { return 0; }; + +static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + LOG_DBG("position %d keycode 0x%02X", event.position, binding->param1); + const struct device *dev = device_get_binding(binding->behavior_dev); + const struct mouse_config *config = dev->config; + return ZMK_EVENT_RAISE(zmk_mouse_scroll_state_changed_from_encoded(binding->param1, *config, + true, event.timestamp)); +} + +static int on_keymap_binding_released(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + LOG_DBG("position %d keycode 0x%02X", event.position, binding->param1); + const struct device *dev = device_get_binding(binding->behavior_dev); + const struct mouse_config *config = dev->config; + return ZMK_EVENT_RAISE(zmk_mouse_scroll_state_changed_from_encoded(binding->param1, *config, + false, event.timestamp)); +} + +static const struct behavior_driver_api behavior_mouse_scroll_driver_api = { + .binding_pressed = on_keymap_binding_pressed, .binding_released = on_keymap_binding_released}; + +#define KP_INST(n) \ + static struct mouse_config behavior_mouse_scroll_config_##n = { \ + .delay_ms = DT_INST_PROP(n, delay_ms), \ + .time_to_max_speed_ms = DT_INST_PROP(n, time_to_max_speed_ms), \ + .acceleration_exponent = DT_INST_PROP(n, acceleration_exponent), \ + }; \ + DEVICE_DT_INST_DEFINE(n, behavior_mouse_scroll_init, device_pm_control_nop, NULL, \ + &behavior_mouse_scroll_config_##n, APPLICATION, \ + CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_mouse_scroll_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(KP_INST) + +#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */ diff --git a/app/src/endpoints.c b/app/src/endpoints.c index 33760010608..0728fff3bde 100644 --- a/app/src/endpoints.c +++ b/app/src/endpoints.c @@ -144,6 +144,40 @@ int zmk_endpoints_send_report(uint16_t usage_page) { } } +int zmk_endpoints_send_mouse_report() { + struct zmk_hid_mouse_report *mouse_report = zmk_hid_get_mouse_report(); + + switch (current_endpoint) { +#if IS_ENABLED(CONFIG_ZMK_USB) + case ZMK_ENDPOINT_USB: { + int err = zmk_usb_hid_send_report((uint8_t *)mouse_report, sizeof(*mouse_report)); + if (err) { + LOG_ERR("FAILED TO SEND OVER USB: %d", err); + } + return err; + } +#endif /* IS_ENABLED(CONFIG_ZMK_USB) */ + +#if IS_ENABLED(CONFIG_ZMK_BLE) + case ZMK_ENDPOINT_BLE: { +#if IS_ENABLED(CONFIG_ZMK_MOUSE_WORK_QUEUE_DEDICATED) + int err = zmk_hog_send_mouse_report_direct(&mouse_report->body); +#else + int err = zmk_hog_send_mouse_report(&mouse_report->body); +#endif + if (err) { + LOG_ERR("FAILED TO SEND OVER HOG: %d", err); + } + return err; + } +#endif /* IS_ENABLED(CONFIG_ZMK_BLE) */ + + default: + LOG_ERR("Unsupported endpoint %d", current_endpoint); + return -ENOTSUP; + } +} + #if IS_ENABLED(CONFIG_SETTINGS) static int endpoints_handle_set(const char *name, size_t len, settings_read_cb read_cb, @@ -228,6 +262,7 @@ static enum zmk_endpoint get_selected_endpoint() { static void disconnect_current_endpoint() { zmk_hid_keyboard_clear(); zmk_hid_consumer_clear(); + zmk_hid_mouse_clear(); zmk_endpoints_send_report(HID_USAGE_KEY); zmk_endpoints_send_report(HID_USAGE_CONSUMER); diff --git a/app/src/events/mouse_button_state_changed.c b/app/src/events/mouse_button_state_changed.c new file mode 100644 index 00000000000..e1ede414217 --- /dev/null +++ b/app/src/events/mouse_button_state_changed.c @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include + +ZMK_EVENT_IMPL(zmk_mouse_button_state_changed); diff --git a/app/src/events/mouse_move_state_changed.c b/app/src/events/mouse_move_state_changed.c new file mode 100644 index 00000000000..faf89cb8abc --- /dev/null +++ b/app/src/events/mouse_move_state_changed.c @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include + +ZMK_EVENT_IMPL(zmk_mouse_move_state_changed); diff --git a/app/src/events/mouse_scroll_state_changed.c b/app/src/events/mouse_scroll_state_changed.c new file mode 100644 index 00000000000..4b4170fe005 --- /dev/null +++ b/app/src/events/mouse_scroll_state_changed.c @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include + +ZMK_EVENT_IMPL(zmk_mouse_scroll_state_changed); diff --git a/app/src/events/mouse_tick.c b/app/src/events/mouse_tick.c new file mode 100644 index 00000000000..0930b9fb902 --- /dev/null +++ b/app/src/events/mouse_tick.c @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include + +ZMK_EVENT_IMPL(zmk_mouse_tick); diff --git a/app/src/hid.c b/app/src/hid.c index c3462ddeb72..9e7451b7f43 100644 --- a/app/src/hid.c +++ b/app/src/hid.c @@ -16,6 +16,9 @@ static struct zmk_hid_keyboard_report keyboard_report = { static struct zmk_hid_consumer_report consumer_report = {.report_id = 2, .body = {.keys = {0}}}; +static struct zmk_hid_mouse_report mouse_report = { + .report_id = 4, .body = {.buttons = 0, .x = 0, .y = 0, .scroll_x = 0, .scroll_y = 0}}; + // Keep track of how often a modifier was pressed. // Only release the modifier if the count is 0. static int explicit_modifier_counts[8] = {0, 0, 0, 0, 0, 0, 0, 0}; @@ -246,6 +249,85 @@ bool zmk_hid_is_pressed(uint32_t usage) { return false; } +// Keep track of how often a button was pressed. +// Only release the button if the count is 0. +static int explicit_button_counts[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static zmk_mod_flags_t explicit_buttons = 0; + +#define SET_MOUSE_BUTTONS(btns) \ + { \ + mouse_report.body.buttons = btns; \ + LOG_DBG("Mouse buttons set to 0x%02X", mouse_report.body.buttons); \ + } + +int zmk_hid_mouse_button_press(zmk_mouse_button_t button) { + explicit_button_counts[button]++; + LOG_DBG("Button %d count %d", button, explicit_button_counts[button]); + WRITE_BIT(explicit_buttons, button, true); + SET_MOUSE_BUTTONS(explicit_buttons); + return 0; +} + +int zmk_hid_mouse_button_release(zmk_mouse_button_t button) { + if (explicit_button_counts[button] <= 0) { + LOG_ERR("Tried to release button %d too often", button); + return -EINVAL; + } + explicit_button_counts[button]--; + LOG_DBG("Button %d count: %d", button, explicit_button_counts[button]); + if (explicit_button_counts[button] == 0) { + LOG_DBG("Button %d released", button); + WRITE_BIT(explicit_buttons, button, false); + } + SET_MOUSE_BUTTONS(explicit_buttons); + return 0; +} + +int zmk_hid_mouse_buttons_press(zmk_mouse_button_flags_t buttons) { + for (zmk_mod_t i = 0; i < 16; i++) { + if (buttons & (1 << i)) { + zmk_hid_mouse_button_press(i); + } + } + return 0; +} + +int zmk_hid_mouse_buttons_release(zmk_mouse_button_flags_t buttons) { + for (zmk_mod_t i = 0; i < 16; i++) { + if (buttons & (1 << i)) { + zmk_hid_mouse_button_release(i); + } + } + return 0; +} + +void zmk_hid_mouse_movement_set(int16_t x, int16_t y) { + mouse_report.body.x = x; + mouse_report.body.y = y; + LOG_DBG("Mouse movement set to 0x%02X 0x%02X ", mouse_report.body.x, mouse_report.body.y); +} + +void zmk_hid_mouse_movement_update(int16_t x, int16_t y) { + mouse_report.body.x += x; + mouse_report.body.y += y; + LOG_DBG("Mouse movement updated to 0x%02X 0x%02X ", mouse_report.body.x, mouse_report.body.y); +} + +void zmk_hid_mouse_scroll_set(int8_t x, int8_t y) { + mouse_report.body.scroll_x = x; + mouse_report.body.scroll_y = y; + LOG_DBG("Mouse scroll set to 0x%02X 0x%02X ", mouse_report.body.scroll_x, + mouse_report.body.scroll_y); +} + +void zmk_hid_mouse_scroll_update(int8_t x, int8_t y) { + mouse_report.body.scroll_x += x; + mouse_report.body.scroll_y += y; + LOG_DBG("Mouse scroll updated to 0x%02X 0x%02X ", mouse_report.body.scroll_x, + mouse_report.body.scroll_y); +} +void zmk_hid_mouse_clear() { memset(&mouse_report.body, 0, sizeof(mouse_report.body)); } + struct zmk_hid_keyboard_report *zmk_hid_get_keyboard_report() { return &keyboard_report; } @@ -253,3 +335,7 @@ struct zmk_hid_keyboard_report *zmk_hid_get_keyboard_report() { struct zmk_hid_consumer_report *zmk_hid_get_consumer_report() { return &consumer_report; } + +struct zmk_hid_mouse_report *zmk_hid_get_mouse_report() { + return &mouse_report; +} diff --git a/app/src/hid_listener.c b/app/src/hid_listener.c index e233b0b8ed6..8cde3a43236 100644 --- a/app/src/hid_listener.c +++ b/app/src/hid_listener.c @@ -11,7 +11,6 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #include #include -#include #include #include #include @@ -71,13 +70,14 @@ static int hid_listener_keycode_released(const struct zmk_keycode_state_changed } int hid_listener(const zmk_event_t *eh) { - const struct zmk_keycode_state_changed *ev = as_zmk_keycode_state_changed(eh); - if (ev) { - if (ev->state) { - hid_listener_keycode_pressed(ev); + const struct zmk_keycode_state_changed *kc_ev = as_zmk_keycode_state_changed(eh); + if (kc_ev) { + if (kc_ev->state) { + hid_listener_keycode_pressed(kc_ev); } else { - hid_listener_keycode_released(ev); + hid_listener_keycode_released(kc_ev); } + return 0; } return 0; } diff --git a/app/src/hog.c b/app/src/hog.c index 3dd3e874a56..f915d27a91a 100644 --- a/app/src/hog.c +++ b/app/src/hog.c @@ -56,6 +56,11 @@ static struct hids_report consumer_input = { .type = HIDS_INPUT, }; +static struct hids_report mouse_input = { + .id = 0x04, + .type = HIDS_INPUT, +}; + static bool host_requests_notification = false; static uint8_t ctrl_point; // static uint8_t proto_mode; @@ -93,6 +98,13 @@ static ssize_t read_hids_consumer_input_report(struct bt_conn *conn, sizeof(struct zmk_hid_consumer_report_body)); } +static ssize_t read_hids_mouse_input_report(struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) { + struct zmk_hid_mouse_report_body *report_body = &zmk_hid_get_mouse_report()->body; + return bt_gatt_attr_read(conn, attr, buf, len, offset, report_body, + sizeof(struct zmk_hid_mouse_report_body)); +} + // static ssize_t write_proto_mode(struct bt_conn *conn, // const struct bt_gatt_attr *attr, // const void *buf, uint16_t len, uint16_t offset, @@ -139,6 +151,13 @@ BT_GATT_SERVICE_DEFINE( BT_GATT_CCC(input_ccc_changed, BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT), BT_GATT_DESCRIPTOR(BT_UUID_HIDS_REPORT_REF, BT_GATT_PERM_READ_ENCRYPT, read_hids_report_ref, NULL, &consumer_input), + + BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_REPORT, BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY, + BT_GATT_PERM_READ_ENCRYPT, read_hids_mouse_input_report, NULL, NULL), + BT_GATT_CCC(input_ccc_changed, BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT), + BT_GATT_DESCRIPTOR(BT_UUID_HIDS_REPORT_REF, BT_GATT_PERM_READ_ENCRYPT, read_hids_report_ref, + NULL, &mouse_input), + BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_CTRL_POINT, BT_GATT_CHRC_WRITE_WITHOUT_RESP, BT_GATT_PERM_WRITE, NULL, write_ctrl_point, &ctrl_point)); @@ -261,6 +280,76 @@ int zmk_hog_send_consumer_report(struct zmk_hid_consumer_report_body *report) { return 0; }; +K_MSGQ_DEFINE(zmk_hog_mouse_msgq, sizeof(struct zmk_hid_mouse_report_body), + CONFIG_ZMK_BLE_MOUSE_REPORT_QUEUE_SIZE, 4); + +void send_mouse_report_callback(struct k_work *work) { + struct zmk_hid_mouse_report_body report; + while (k_msgq_get(&zmk_hog_mouse_msgq, &report, K_NO_WAIT) == 0) { + struct bt_conn *conn = destination_connection(); + if (conn == NULL) { + return; + } + + struct bt_gatt_notify_params notify_params = { + .attr = &hog_svc.attrs[13], + .data = &report, + .len = sizeof(report), + }; + + int err = bt_gatt_notify_cb(conn, ¬ify_params); + if (err) { + LOG_DBG("Error notifying %d", err); + } + + bt_conn_unref(conn); + } +}; + +K_WORK_DEFINE(hog_mouse_work, send_mouse_report_callback); + +int zmk_hog_send_mouse_report(struct zmk_hid_mouse_report_body *report) { + int err = k_msgq_put(&zmk_hog_mouse_msgq, report, K_NO_WAIT); + if (err) { + switch (err) { + case -EAGAIN: { + LOG_WRN("Mouse message queue full, dropping report"); + return err; + } + default: + LOG_WRN("Failed to queue mouse report to send (%d)", err); + return err; + } + } + + k_work_submit_to_queue(&hog_work_q, &hog_mouse_work); + + return 0; +}; + +int zmk_hog_send_mouse_report_direct(struct zmk_hid_mouse_report_body *report) { + struct bt_conn *conn = destination_connection(); + if (conn == NULL) { + return 1; + } + + struct bt_gatt_notify_params notify_params = { + .attr = &hog_svc.attrs[13], + .data = report, + .len = sizeof(*report), + }; + + int err = bt_gatt_notify_cb(conn, ¬ify_params); + if (err) { + LOG_DBG("Error notifying %d", err); + return err; + } + + bt_conn_unref(conn); + + return 0; +}; + int zmk_hog_init(const struct device *_arg) { static const struct k_work_queue_config queue_config = {.name = "HID Over GATT Send Work"}; k_work_queue_start(&hog_work_q, hog_q_stack, K_THREAD_STACK_SIZEOF(hog_q_stack), diff --git a/app/src/main.c b/app/src/main.c index ae604a7b9ee..d3b3e578b3e 100644 --- a/app/src/main.c +++ b/app/src/main.c @@ -17,6 +17,10 @@ LOG_MODULE_REGISTER(zmk, CONFIG_ZMK_LOG_LEVEL); #include #include +#ifdef CONFIG_ZMK_MOUSE +#include +#endif /* CONFIG_ZMK_MOUSE */ + #define ZMK_KSCAN_DEV DT_LABEL(ZMK_MATRIX_NODE_ID) void main(void) { @@ -29,4 +33,8 @@ void main(void) { #ifdef CONFIG_ZMK_DISPLAY zmk_display_init(); #endif /* CONFIG_ZMK_DISPLAY */ + +#ifdef CONFIG_ZMK_MOUSE + zmk_mouse_init(); +#endif /* CONFIG_ZMK_MOUSE */ } diff --git a/app/src/mouse/Kconfig b/app/src/mouse/Kconfig new file mode 100644 index 00000000000..1161b86b429 --- /dev/null +++ b/app/src/mouse/Kconfig @@ -0,0 +1,38 @@ +# Copyright (c) 2021 The ZMK Contributors +# SPDX-License-Identifier: MIT + +menuconfig ZMK_MOUSE + bool "Enable ZMK mouse emulation" + default n + +config ZMK_MOUSE_TICK_DURATION + int "Mouse tick duration in ms" + default 8 + +if ZMK_MOUSE + +choice ZMK_MOUSE_WORK_QUEUE + prompt "Work queue selection for mouse events" + default ZMK_MOUSE_WORK_QUEUE_DEDICATED + +config ZMK_MOUSE_WORK_QUEUE_SYSTEM + bool "Use default system work queue for mouse events" + +config ZMK_MOUSE_WORK_QUEUE_DEDICATED + bool "Use dedicated work queue for mouse events" + +endchoice + +if ZMK_MOUSE_WORK_QUEUE_DEDICATED + +config ZMK_MOUSE_DEDICATED_THREAD_STACK_SIZE + int "Stack size for dedicated mouse thread/queue" + default 2048 + +config ZMK_MOUSE_DEDICATED_THREAD_PRIORITY + int "Thread priority for dedicated mouse thread/queue" + default 3 + +endif # ZMK_MOUSE_WORK_QUEUE_DEDICATED + +endif diff --git a/app/src/mouse/key_listener.c b/app/src/mouse/key_listener.c new file mode 100644 index 00000000000..713d032352a --- /dev/null +++ b/app/src/mouse/key_listener.c @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2021 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#include +#include +#include +#include +#include +#include +#include +#include + +static struct vector2d move_speed = {0}; +static struct vector2d scroll_speed = {0}; +static struct mouse_config move_config = (struct mouse_config){0}; +static struct mouse_config scroll_config = (struct mouse_config){0}; +static int64_t start_time = 0; + +bool equals(const struct mouse_config *one, const struct mouse_config *other) { + return one->delay_ms == other->delay_ms && + one->time_to_max_speed_ms == other->time_to_max_speed_ms && + one->acceleration_exponent == other->acceleration_exponent; +} + +static void clear_mouse_state(struct k_work *work) { + move_speed = (struct vector2d){0}; + scroll_speed = (struct vector2d){0}; + start_time = 0; + zmk_hid_mouse_movement_set(0, 0); + zmk_hid_mouse_scroll_set(0, 0); + LOG_DBG("Clearing state"); +} + +K_WORK_DEFINE(mouse_clear, &clear_mouse_state); + +void mouse_clear_cb(struct k_timer *dummy) { + k_work_submit_to_queue(zmk_mouse_work_q(), &mouse_clear); +} + +static void mouse_tick_timer_handler(struct k_work *work) { + zmk_hid_mouse_movement_set(0, 0); + zmk_hid_mouse_scroll_set(0, 0); + LOG_DBG("Raising mouse tick event"); + ZMK_EVENT_RAISE( + zmk_mouse_tick(move_speed, scroll_speed, move_config, scroll_config, &start_time)); + zmk_endpoints_send_mouse_report(); +} + +K_WORK_DEFINE(mouse_tick, &mouse_tick_timer_handler); + +void mouse_timer_cb(struct k_timer *dummy) { + LOG_DBG("Submitting mouse work to queue"); + k_work_submit_to_queue(zmk_mouse_work_q(), &mouse_tick); +} + +K_TIMER_DEFINE(mouse_timer, mouse_timer_cb, mouse_clear_cb); + +static int mouse_timer_ref_count = 0; + +void mouse_timer_ref() { + if (mouse_timer_ref_count == 0) { + start_time = k_uptime_get(); + k_timer_start(&mouse_timer, K_NO_WAIT, K_MSEC(CONFIG_ZMK_MOUSE_TICK_DURATION)); + } + mouse_timer_ref_count += 1; +} + +void mouse_timer_unref() { + if (mouse_timer_ref_count > 0) { + mouse_timer_ref_count--; + } + if (mouse_timer_ref_count == 0) { + k_timer_stop(&mouse_timer); + } +} + +static void listener_mouse_move_pressed(const struct zmk_mouse_move_state_changed *ev) { + move_speed.x += ev->max_speed.x; + move_speed.y += ev->max_speed.y; + mouse_timer_ref(); +} + +static void listener_mouse_move_released(const struct zmk_mouse_move_state_changed *ev) { + move_speed.x -= ev->max_speed.x; + move_speed.y -= ev->max_speed.y; + mouse_timer_unref(); +} + +static void listener_mouse_scroll_pressed(const struct zmk_mouse_scroll_state_changed *ev) { + scroll_speed.x += ev->max_speed.x; + scroll_speed.y += ev->max_speed.y; + mouse_timer_ref(); +} + +static void listener_mouse_scroll_released(const struct zmk_mouse_scroll_state_changed *ev) { + scroll_speed.x -= ev->max_speed.x; + scroll_speed.y -= ev->max_speed.y; + mouse_timer_unref(); +} + +static void listener_mouse_button_pressed(const struct zmk_mouse_button_state_changed *ev) { + LOG_DBG("buttons: 0x%02X", ev->buttons); + zmk_hid_mouse_buttons_press(ev->buttons); + zmk_endpoints_send_mouse_report(); +} + +static void listener_mouse_button_released(const struct zmk_mouse_button_state_changed *ev) { + LOG_DBG("buttons: 0x%02X", ev->buttons); + zmk_hid_mouse_buttons_release(ev->buttons); + zmk_endpoints_send_mouse_report(); +} + +int mouse_listener(const zmk_event_t *eh) { + const struct zmk_mouse_move_state_changed *mmv_ev = as_zmk_mouse_move_state_changed(eh); + if (mmv_ev) { + if (!equals(&move_config, &(mmv_ev->config))) + move_config = mmv_ev->config; + + if (mmv_ev->state) { + listener_mouse_move_pressed(mmv_ev); + } else { + listener_mouse_move_released(mmv_ev); + } + return 0; + } + const struct zmk_mouse_scroll_state_changed *msc_ev = as_zmk_mouse_scroll_state_changed(eh); + if (msc_ev) { + if (!equals(&scroll_config, &(msc_ev->config))) + scroll_config = msc_ev->config; + if (msc_ev->state) { + listener_mouse_scroll_pressed(msc_ev); + } else { + listener_mouse_scroll_released(msc_ev); + } + return 0; + } + const struct zmk_mouse_button_state_changed *mbt_ev = as_zmk_mouse_button_state_changed(eh); + if (mbt_ev) { + if (mbt_ev->state) { + listener_mouse_button_pressed(mbt_ev); + } else { + listener_mouse_button_released(mbt_ev); + } + return 0; + } + return 0; +} + +ZMK_LISTENER(mouse_listener, mouse_listener); +ZMK_SUBSCRIPTION(mouse_listener, zmk_mouse_button_state_changed); +ZMK_SUBSCRIPTION(mouse_listener, zmk_mouse_move_state_changed); +ZMK_SUBSCRIPTION(mouse_listener, zmk_mouse_scroll_state_changed); diff --git a/app/src/mouse/main.c b/app/src/mouse/main.c new file mode 100644 index 00000000000..49208a76ef9 --- /dev/null +++ b/app/src/mouse/main.c @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include + +#if IS_ENABLED(CONFIG_ZMK_MOUSE_WORK_QUEUE_DEDICATED) +K_THREAD_STACK_DEFINE(mouse_work_stack_area, CONFIG_ZMK_MOUSE_DEDICATED_THREAD_STACK_SIZE); +static struct k_work_q mouse_work_q; +#endif + +struct k_work_q *zmk_mouse_work_q() { +#if IS_ENABLED(CONFIG_ZMK_MOUSE_WORK_QUEUE_DEDICATED) + return &mouse_work_q; +#else + return &k_sys_work_q; +#endif +} + +int zmk_mouse_init() { +#if IS_ENABLED(CONFIG_ZMK_MOUSE_WORK_QUEUE_DEDICATED) + k_work_q_start(&mouse_work_q, mouse_work_stack_area, + K_THREAD_STACK_SIZEOF(mouse_work_stack_area), + CONFIG_ZMK_MOUSE_DEDICATED_THREAD_PRIORITY); +#endif + return 0; +} \ No newline at end of file diff --git a/app/src/mouse/tick_listener.c b/app/src/mouse/tick_listener.c new file mode 100644 index 00000000000..9c76bd5d2a8 --- /dev/null +++ b/app/src/mouse/tick_listener.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#include +#include +#include +#include + +#include // CLAMP + +#if CONFIG_MINIMAL_LIBC +static float powf(float base, float exponent) { + // poor man's power implementation rounds the exponent down to the nearest integer. + float power = 1.0f; + for (; exponent >= 1.0f; exponent--) { + power = power * base; + } + return power; +} +#else +#include +#endif + +struct vector2d move_remainder = {0}; +struct vector2d scroll_remainder = {0}; + +static int64_t ms_since_start(int64_t start, int64_t now, int64_t delay) { + int64_t move_duration = now - (start + delay); + // start can be in the future if there's a delay + if (move_duration < 0) { + move_duration = 0; + } + return move_duration; +} + +static float speed(const struct mouse_config *config, float max_speed, int64_t duration_ms) { + // Calculate the speed based on MouseKeysAccel + // See https://en.wikipedia.org/wiki/Mouse_keys + if (duration_ms > config->time_to_max_speed_ms || config->time_to_max_speed_ms == 0 || + config->acceleration_exponent == 0) { + return max_speed; + } + float time_fraction = (float)duration_ms / config->time_to_max_speed_ms; + return max_speed * powf(time_fraction, config->acceleration_exponent); +} + +static void track_remainder(float *move, float *remainder) { + float new_move = *move + *remainder; + *remainder = new_move - (int)new_move; + *move = (int)new_move; +} + +static struct vector2d update_movement(struct vector2d *remainder, + const struct mouse_config *config, struct vector2d max_speed, + int64_t now, int64_t *start_time) { + struct vector2d move = {0}; + if (max_speed.x == 0 && max_speed.y == 0) { + *remainder = (struct vector2d){0}; + return move; + } + + int64_t move_duration = ms_since_start(*start_time, now, config->delay_ms); + move = (struct vector2d){ + .x = speed(config, max_speed.x, move_duration) * CONFIG_ZMK_MOUSE_TICK_DURATION / 1000, + .y = speed(config, max_speed.y, move_duration) * CONFIG_ZMK_MOUSE_TICK_DURATION / 1000, + }; + + track_remainder(&(move.x), &(remainder->x)); + track_remainder(&(move.y), &(remainder->y)); + + return move; +} + +static void mouse_tick_handler(const struct zmk_mouse_tick *tick) { + struct vector2d move = update_movement(&move_remainder, &(tick->move_config), tick->max_move, + tick->timestamp, tick->start_time); + zmk_hid_mouse_movement_update((int16_t)CLAMP(move.x, INT16_MIN, INT16_MAX), + (int16_t)CLAMP(move.y, INT16_MIN, INT16_MAX)); + struct vector2d scroll = update_movement(&scroll_remainder, &(tick->scroll_config), + tick->max_scroll, tick->timestamp, tick->start_time); + zmk_hid_mouse_scroll_update((int8_t)CLAMP(scroll.x, INT8_MIN, INT8_MAX), + (int8_t)CLAMP(scroll.y, INT8_MIN, INT8_MAX)); +} + +int zmk_mouse_tick_listener(const zmk_event_t *eh) { + const struct zmk_mouse_tick *tick = as_zmk_mouse_tick(eh); + if (tick) { + mouse_tick_handler(tick); + return 0; + } + return 0; +} + +ZMK_LISTENER(zmk_mouse_tick_listener, zmk_mouse_tick_listener); +ZMK_SUBSCRIPTION(zmk_mouse_tick_listener, zmk_mouse_tick); \ No newline at end of file diff --git a/app/tests/mouse-keys/mmv/events.patterns b/app/tests/mouse-keys/mmv/events.patterns new file mode 100644 index 00000000000..833100f6ac4 --- /dev/null +++ b/app/tests/mouse-keys/mmv/events.patterns @@ -0,0 +1 @@ +s/.*hid_listener_keycode_//p \ No newline at end of file diff --git a/app/tests/mouse-keys/mmv/keycode_events.snapshot b/app/tests/mouse-keys/mmv/keycode_events.snapshot new file mode 100644 index 00000000000..259501ba3d9 --- /dev/null +++ b/app/tests/mouse-keys/mmv/keycode_events.snapshot @@ -0,0 +1,2 @@ +pressed: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00 diff --git a/app/tests/mouse-keys/mmv/native_posix.keymap b/app/tests/mouse-keys/mmv/native_posix.keymap new file mode 100644 index 00000000000..ecf06601c05 --- /dev/null +++ b/app/tests/mouse-keys/mmv/native_posix.keymap @@ -0,0 +1,26 @@ +#include +#include +#include +#include + +/ { + keymap { + compatible = "zmk,keymap"; + label ="Default keymap"; + + default_layer { + bindings = < + &mmv MOVE_LEFT &none + &none &none + >; + }; + }; +}; + + +&kscan { + events = < + ZMK_MOCK_PRESS(0,0,100) + ZMK_MOCK_RELEASE(0,0,10) + >; +}; \ No newline at end of file diff --git a/docs/docs/behaviors/mouse-emulation.md b/docs/docs/behaviors/mouse-emulation.md new file mode 100644 index 00000000000..efe095e7a04 --- /dev/null +++ b/docs/docs/behaviors/mouse-emulation.md @@ -0,0 +1,110 @@ +--- +title: Mouse Emulation Behaviors +sidebar_label: Mouse Emulation +--- + +## Summary + +Mouse emulation behaviors send mouse movements, button presses or scroll actions. + +Please view [`dt-bindings/zmk/mouse.h`](https://github.com/zmkfirmware/zmk/blob/main/app/include/dt-bindings/zmk/mouse.h) for a comprehensive list of signals. + +## Configuration options + +This feature should be enabled via a config option: + +``` +CONFIG_ZMK_MOUSE=y +``` + +This option enables several others. + +### Dedicated thread processing + +`CONFIG_ZMK_MOUSE_WORK_QUEUE_DEDICATED` is enabled by default and separates the processing of mouse signals into a dedicated thread, significantly improving performance. + +### Tick rate configuration + +`CONFIG_ZMK_MOUSE_TICK_DURATION` sets the tick rate for mouse polling. It is set to 8 ms. by default. + +## Keycode Defines + +To make it easier to encode the HID keycode numeric values, most keymaps include +the [`dt-bindings/zmk/mouse.h`](https://github.com/zmkfirmware/zmk/blob/main/app/include/dt-bindings/zmk/mouse.h) header +provided by ZMK near the top: + +``` +#include +``` + +Doing so allows using a set of defines such as `MOVE_UP`, `MOVE_DOWN`, `LCLK` and `SCROLL_UP` with these behaviors. + +## Mouse Button Press + +This behavior can press/release up to 16 mouse buttons. + +### Behavior Binding + +- Reference: `&mkp` +- Parameter: A `uint16` with each bit referring to a button. + +Example: + +``` +&mkp LCLK +``` + +## Mouse Movement + +This behavior is used to manipulate the cursor. + +### Behavior Binding + +- Reference: `&mmv` +- Parameter: A `uint32` with the first 16 bits relating to horizontal movement + and the last 16 - to vertical movement. + +Example: + +``` +&mmv MOVE_UP +``` + +## Mouse Scrolling + +This behaviour is used to scroll, both horizontally and vertically. + +### Behavior Binding + +- Reference: `&mwh` +- Parameter: A `uint16` with the first 8 bits relating to horizontal movement + and the last 8 - to vertical movement. + +Example: + +``` +&mwh SCROLL_UP +``` + +## Acceleration + +Both mouse movement and scrolling have independently configurable acceleration profiles with three parameters: delay before movement, time to max speed and the acceleration exponent. +The exponent is usually set to 0 for constant speed, 1 for uniform acceleration or 2 for uniform jerk. + +These profiles can be configured inside your keymap: + +``` +&mmv { + time-to-max-speed-ms = <500>; +}; + +&mwh { + acceleration-exponent=<1>; +}; + +/ { + keymap { + ... + }; +}; +``` diff --git a/docs/docs/intro.md b/docs/docs/intro.md index 142dcafc97e..4b2c8592f6f 100644 --- a/docs/docs/intro.md +++ b/docs/docs/intro.md @@ -33,7 +33,7 @@ ZMK is currently missing some features found in other popular firmware. This tab | One Shot Keys | ✅ | ✅ | ✅ | | [Combo Keys](features/combos.md) | ✅ | | ✅ | | [Macros](behaviors/macros.md) | ✅ | ✅ | ✅ | -| Mouse Keys | 🚧 | ✅ | ✅ | +| Mouse Keys | ✅ | ✅ | ✅ | | Low Active Power Usage | ✅ | | | | Low Power Sleep States | ✅ | ✅ | | | [Low Power Mode (VCC Shutoff)](behaviors/power.md) | ✅ | ✅ | | @@ -43,6 +43,7 @@ ZMK is currently missing some features found in other popular firmware. This tab | AVR/8 Bit | | | ✅ | | [Wide Range of ARM Chips Supported](https://docs.zephyrproject.org/latest/boards/index.html) | ✅ | | | + [^3]: Tap-Dances are limited to single and double-tap on BlueMicro [^2]: Encoders are not currently supported on peripheral side splits. [^1]: OLEDs are currently proof of concept in ZMK. diff --git a/docs/sidebars.js b/docs/sidebars.js index e7d05850c5a..8bfba2630fd 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -34,6 +34,7 @@ module.exports = { "behaviors/tap-dance", "behaviors/caps-word", "behaviors/key-repeat", + "behaviors/mouse-emulation", "behaviors/reset", "behaviors/bluetooth", "behaviors/outputs", From 62b6b436d79529adb4db511c43739354b36bd93a Mon Sep 17 00:00:00 2001 From: Shawn Meier Date: Wed, 18 May 2022 23:07:54 -0600 Subject: [PATCH 013/157] ifdefs for link errors when compiling non-central, some includes, other minor hacks to compile, cleanup is needed later --- app/include/zmk/events/mouse_button_state_changed.h | 2 +- app/src/behaviors/behavior_mouse_key_press.c | 2 +- app/src/behaviors/behavior_mouse_move.c | 2 +- app/src/behaviors/behavior_mouse_scroll.c | 2 +- app/src/mouse/key_listener.c | 4 ++++ app/src/mouse/tick_listener.c | 5 +++++ 6 files changed, 13 insertions(+), 4 deletions(-) diff --git a/app/include/zmk/events/mouse_button_state_changed.h b/app/include/zmk/events/mouse_button_state_changed.h index 7ec4d2087c5..6c3adae30d8 100644 --- a/app/include/zmk/events/mouse_button_state_changed.h +++ b/app/include/zmk/events/mouse_button_state_changed.h @@ -23,5 +23,5 @@ ZMK_EVENT_DECLARE(zmk_mouse_button_state_changed); static inline struct zmk_mouse_button_state_changed_event * zmk_mouse_button_state_changed_from_encoded(uint32_t encoded, bool pressed, int64_t timestamp) { return new_zmk_mouse_button_state_changed((struct zmk_mouse_button_state_changed){ - .buttons = HID_USAGE_ID(encoded), .state = pressed, .timestamp = timestamp}); + .buttons = ZMK_HID_USAGE_ID(encoded), .state = pressed, .timestamp = timestamp}); } diff --git a/app/src/behaviors/behavior_mouse_key_press.c b/app/src/behaviors/behavior_mouse_key_press.c index e5f2709cbf2..b7ab81e5c94 100644 --- a/app/src/behaviors/behavior_mouse_key_press.c +++ b/app/src/behaviors/behavior_mouse_key_press.c @@ -39,7 +39,7 @@ static const struct behavior_driver_api behavior_mouse_key_press_driver_api = { .binding_pressed = on_keymap_binding_pressed, .binding_released = on_keymap_binding_released}; #define KP_INST(n) \ - DEVICE_DT_INST_DEFINE(n, behavior_mouse_key_press_init, device_pm_control_nop, NULL, NULL, \ + DEVICE_DT_INST_DEFINE(n, behavior_mouse_key_press_init, NULL, NULL, NULL, \ APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \ &behavior_mouse_key_press_driver_api); diff --git a/app/src/behaviors/behavior_mouse_move.c b/app/src/behaviors/behavior_mouse_move.c index 5977a039d1c..12552d54892 100644 --- a/app/src/behaviors/behavior_mouse_move.c +++ b/app/src/behaviors/behavior_mouse_move.c @@ -48,7 +48,7 @@ static const struct behavior_driver_api behavior_mouse_move_driver_api = { .time_to_max_speed_ms = DT_INST_PROP(n, time_to_max_speed_ms), \ .acceleration_exponent = DT_INST_PROP(n, acceleration_exponent), \ }; \ - DEVICE_DT_INST_DEFINE(n, behavior_mouse_move_init, device_pm_control_nop, NULL, \ + DEVICE_DT_INST_DEFINE(n, behavior_mouse_move_init, NULL, NULL, \ &behavior_mouse_move_config_##n, APPLICATION, \ CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_mouse_move_driver_api); diff --git a/app/src/behaviors/behavior_mouse_scroll.c b/app/src/behaviors/behavior_mouse_scroll.c index 6416235265d..b6c4b1d31fd 100644 --- a/app/src/behaviors/behavior_mouse_scroll.c +++ b/app/src/behaviors/behavior_mouse_scroll.c @@ -49,7 +49,7 @@ static const struct behavior_driver_api behavior_mouse_scroll_driver_api = { .time_to_max_speed_ms = DT_INST_PROP(n, time_to_max_speed_ms), \ .acceleration_exponent = DT_INST_PROP(n, acceleration_exponent), \ }; \ - DEVICE_DT_INST_DEFINE(n, behavior_mouse_scroll_init, device_pm_control_nop, NULL, \ + DEVICE_DT_INST_DEFINE(n, behavior_mouse_scroll_init, NULL, NULL, \ &behavior_mouse_scroll_config_##n, APPLICATION, \ CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_mouse_scroll_driver_api); diff --git a/app/src/mouse/key_listener.c b/app/src/mouse/key_listener.c index 713d032352a..cc3f1762c8e 100644 --- a/app/src/mouse/key_listener.c +++ b/app/src/mouse/key_listener.c @@ -45,6 +45,8 @@ void mouse_clear_cb(struct k_timer *dummy) { k_work_submit_to_queue(zmk_mouse_work_q(), &mouse_clear); } +//TODO: There is probably a better flag to use here +#if(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL) static void mouse_tick_timer_handler(struct k_work *work) { zmk_hid_mouse_movement_set(0, 0); zmk_hid_mouse_scroll_set(0, 0); @@ -158,3 +160,5 @@ ZMK_LISTENER(mouse_listener, mouse_listener); ZMK_SUBSCRIPTION(mouse_listener, zmk_mouse_button_state_changed); ZMK_SUBSCRIPTION(mouse_listener, zmk_mouse_move_state_changed); ZMK_SUBSCRIPTION(mouse_listener, zmk_mouse_scroll_state_changed); + +#endif /* CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL */ \ No newline at end of file diff --git a/app/src/mouse/tick_listener.c b/app/src/mouse/tick_listener.c index 9c76bd5d2a8..e9788499f75 100644 --- a/app/src/mouse/tick_listener.c +++ b/app/src/mouse/tick_listener.c @@ -11,6 +11,7 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #include #include #include +#include #include #include // CLAMP @@ -78,6 +79,7 @@ static struct vector2d update_movement(struct vector2d *remainder, return move; } +#if(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL) static void mouse_tick_handler(const struct zmk_mouse_tick *tick) { struct vector2d move = update_movement(&move_remainder, &(tick->move_config), tick->max_move, tick->timestamp, tick->start_time); @@ -88,13 +90,16 @@ static void mouse_tick_handler(const struct zmk_mouse_tick *tick) { zmk_hid_mouse_scroll_update((int8_t)CLAMP(scroll.x, INT8_MIN, INT8_MAX), (int8_t)CLAMP(scroll.y, INT8_MIN, INT8_MAX)); } +#endif int zmk_mouse_tick_listener(const zmk_event_t *eh) { const struct zmk_mouse_tick *tick = as_zmk_mouse_tick(eh); + #if(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL) if (tick) { mouse_tick_handler(tick); return 0; } + #endif return 0; } From b9951ee3c066f49f893b6a733b840fbd68a4f150 Mon Sep 17 00:00:00 2001 From: Shawn Meier Date: Thu, 19 May 2022 11:31:37 -0600 Subject: [PATCH 014/157] fix hid deprication warnings with crides 6ca7298730859ef2ebab1c970cac7f9550bef14b, add missing method declaration to hog.h --- app/include/zmk/hid.h | 115 +++++++++++++++--------------------------- app/include/zmk/hog.h | 1 + app/src/mouse/main.c | 5 +- 3 files changed, 44 insertions(+), 77 deletions(-) diff --git a/app/include/zmk/hid.h b/app/include/zmk/hid.h index b0294a10577..b6dc84a6c17 100644 --- a/app/include/zmk/hid.h +++ b/app/include/zmk/hid.h @@ -7,7 +7,8 @@ #pragma once #include -#include +//#include +#include #include #include @@ -85,122 +86,86 @@ static const uint8_t zmk_hid_report_desc[] = { #error "A proper consumer HID report usage range must be selected" #endif /* REPORT_COUNT (CONFIG_ZMK_HID_CONSUMER_REPORT_SIZE) */ - HID_GI_REPORT_COUNT, - CONFIG_ZMK_HID_CONSUMER_REPORT_SIZE, - HID_MI_INPUT, + HID_REPORT_COUNT(CONFIG_ZMK_HID_CONSUMER_REPORT_SIZE), + HID_INPUT(0x00), 0x00, /* END COLLECTION */ - HID_MI_COLLECTION_END, + HID_END_COLLECTION, /* USAGE_PAGE (Generic Desktop) */ - HID_GI_USAGE_PAGE, - HID_USAGE_GD, + HID_USAGE_PAGE(HID_USAGE_GD), /* USAGE (Mouse) */ - HID_LI_USAGE, - HID_USAGE_GD_MOUSE, + HID_USAGE(HID_USAGE_GD_MOUSE), /* COLLECTION (Application) */ - HID_MI_COLLECTION, - COLLECTION_APPLICATION, + HID_COLLECTION(HID_COLLECTION_APPLICATION), /* REPORT ID (4) */ - HID_GI_REPORT_ID, - 0x04, + HID_REPORT_ID(0x04), /* USAGE (Pointer) */ - HID_LI_USAGE, - HID_USAGE_GD_POINTER, + HID_USAGE(HID_USAGE_GD_POINTER), /* COLLECTION (Physical) */ - HID_MI_COLLECTION, - COLLECTION_PHYSICAL, + HID_COLLECTION(HID_COLLECTION_PHYSICAL), /* USAGE_PAGE (Button) */ - HID_GI_USAGE_PAGE, - HID_USAGE_BUTTON, + HID_USAGE_PAGE(HID_USAGE_BUTTON), /* USAGE_MINIMUM (0x1) (button 1?) */ - HID_LI_USAGE_MIN(1), - 0x1, + HID_USAGE_MIN8(0x01), /* USAGE_MAXIMUM (0x10) (button 5? Buttons up to 8 still work) */ - HID_LI_USAGE_MAX(1), - 0x10, + HID_USAGE_MAX8(0x10), /* LOGICAL_MINIMUM (0) */ - HID_GI_LOGICAL_MIN(1), - 0x00, + HID_LOGICAL_MIN8(0x00), /* LOGICAL_MAXIMUM (1) */ - HID_GI_LOGICAL_MAX(1), - 0x01, + HID_LOGICAL_MAX8(0x01), /* REPORT_SIZE (1) */ - HID_GI_REPORT_SIZE, - 0x01, + HID_REPORT_SIZE(0x01), /* REPORT_COUNT (16) */ - HID_GI_REPORT_COUNT, - 0x10, + HID_REPORT_COUNT(0x10), /* INPUT (Data,Var,Abs) */ - HID_MI_INPUT, - 0x02, + HID_INPUT(0x02), /* USAGE_PAGE (Generic Desktop) */ - HID_GI_USAGE_PAGE, - HID_USAGE_GD, + HID_USAGE_PAGE(HID_USAGE_GD), /* LOGICAL_MINIMUM (-32767) */ - HID_GI_LOGICAL_MIN(2), - 0x01, - 0x80, + HID_LOGICAL_MIN16(0x01, 0x80), /* LOGICAL_MAXIMUM (32767) */ - HID_GI_LOGICAL_MAX(2), - 0xFF, - 0x7F, + HID_LOGICAL_MAX16(0xFF, 0x7F), /* REPORT_SIZE (16) */ - HID_GI_REPORT_SIZE, - 0x10, + HID_REPORT_SIZE(0x10), /* REPORT_COUNT (2) */ - HID_GI_REPORT_COUNT, - 0x02, + HID_REPORT_COUNT(0x02), /* USAGE (X) */ // Vertical scroll - HID_LI_USAGE, - HID_USAGE_GD_X, + HID_USAGE(HID_USAGE_GD_X), /* USAGE (Y) */ - HID_LI_USAGE, - HID_USAGE_GD_Y, + HID_USAGE(HID_USAGE_GD_Y), /* Input (Data,Var,Rel) */ - HID_MI_INPUT, - 0x06, + HID_INPUT(0x06), /* LOGICAL_MINIMUM (-127) */ - HID_GI_LOGICAL_MIN(1), - 0x81, + HID_LOGICAL_MIN8(0x81), /* LOGICAL_MAXIMUM (127) */ - HID_GI_LOGICAL_MAX(1), - 0x7F, + HID_LOGICAL_MAX8(0x7F), /* REPORT_SIZE (8) */ - HID_GI_REPORT_SIZE, - 0x08, + HID_REPORT_SIZE(0x08), /* REPORT_COUNT (1) */ - HID_GI_REPORT_COUNT, - 0x01, + HID_REPORT_COUNT(0x01), /* USAGE (Wheel) */ - HID_LI_USAGE, - HID_USAGE_GD_WHEEL, + HID_USAGE(HID_USAGE_GD_WHEEL), /* Input (Data,Var,Rel) */ - HID_MI_INPUT, - 0x06, + HID_INPUT(0x06), /* USAGE_PAGE (Consumer) */ // Horizontal scroll - HID_GI_USAGE_PAGE, - HID_USAGE_CONSUMER, + HID_USAGE_PAGE(HID_USAGE_CONSUMER), /* USAGE (AC Pan) */ 0x0A, 0x38, 0x02, /* LOGICAL_MINIMUM (-127) */ - HID_GI_LOGICAL_MIN(1), - 0x81, + HID_LOGICAL_MIN8(0x81), /* LOGICAL_MAXIMUM (127) */ - HID_GI_LOGICAL_MAX(1), - 0x7F, + HID_LOGICAL_MAX8(0x7F), /* REPORT_COUNT (1) */ - HID_GI_REPORT_COUNT, - 0x01, + HID_REPORT_COUNT(0x01), /* Input (Data,Var,Rel) */ - HID_MI_INPUT, - 0x06, + HID_INPUT(0x06), /* END COLLECTION */ - HID_MI_COLLECTION_END, + HID_END_COLLECTION, /* END COLLECTION */ - HID_MI_COLLECTION_END, + HID_END_COLLECTION, }; // struct zmk_hid_boot_report diff --git a/app/include/zmk/hog.h b/app/include/zmk/hog.h index 9debc3ff319..1b26242751f 100644 --- a/app/include/zmk/hog.h +++ b/app/include/zmk/hog.h @@ -14,3 +14,4 @@ int zmk_hog_init(); int zmk_hog_send_keyboard_report(struct zmk_hid_keyboard_report_body *body); int zmk_hog_send_consumer_report(struct zmk_hid_consumer_report_body *body); int zmk_hog_send_mouse_report(struct zmk_hid_mouse_report_body *body); +int zmk_hog_send_mouse_report_direct(struct zmk_hid_mouse_report_body *body); diff --git a/app/src/mouse/main.c b/app/src/mouse/main.c index 49208a76ef9..ee759171736 100644 --- a/app/src/mouse/main.c +++ b/app/src/mouse/main.c @@ -22,9 +22,10 @@ struct k_work_q *zmk_mouse_work_q() { int zmk_mouse_init() { #if IS_ENABLED(CONFIG_ZMK_MOUSE_WORK_QUEUE_DEDICATED) - k_work_q_start(&mouse_work_q, mouse_work_stack_area, + k_work_queue_start(&mouse_work_q, mouse_work_stack_area, K_THREAD_STACK_SIZEOF(mouse_work_stack_area), - CONFIG_ZMK_MOUSE_DEDICATED_THREAD_PRIORITY); + CONFIG_ZMK_MOUSE_DEDICATED_THREAD_PRIORITY, + NULL); #endif return 0; } \ No newline at end of file From f18d4900583158c7978dc0a934bd15719d9b8436 Mon Sep 17 00:00:00 2001 From: Shawn Meier Date: Thu, 19 May 2022 11:56:48 -0600 Subject: [PATCH 015/157] move ifdefs to get rid of wunused warning --- app/src/mouse/key_listener.c | 4 ++-- app/src/mouse/tick_listener.c | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/src/mouse/key_listener.c b/app/src/mouse/key_listener.c index cc3f1762c8e..4f804251080 100644 --- a/app/src/mouse/key_listener.c +++ b/app/src/mouse/key_listener.c @@ -18,6 +18,8 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #include #include +//TODO: There is probably a better flag to use here +#if(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL) static struct vector2d move_speed = {0}; static struct vector2d scroll_speed = {0}; static struct mouse_config move_config = (struct mouse_config){0}; @@ -45,8 +47,6 @@ void mouse_clear_cb(struct k_timer *dummy) { k_work_submit_to_queue(zmk_mouse_work_q(), &mouse_clear); } -//TODO: There is probably a better flag to use here -#if(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL) static void mouse_tick_timer_handler(struct k_work *work) { zmk_hid_mouse_movement_set(0, 0); zmk_hid_mouse_scroll_set(0, 0); diff --git a/app/src/mouse/tick_listener.c b/app/src/mouse/tick_listener.c index e9788499f75..37c0dcd39f5 100644 --- a/app/src/mouse/tick_listener.c +++ b/app/src/mouse/tick_listener.c @@ -16,6 +16,8 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #include // CLAMP +//TODO: probably a better flag to use here +#if(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL) #if CONFIG_MINIMAL_LIBC static float powf(float base, float exponent) { // poor man's power implementation rounds the exponent down to the nearest integer. @@ -79,7 +81,6 @@ static struct vector2d update_movement(struct vector2d *remainder, return move; } -#if(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL) static void mouse_tick_handler(const struct zmk_mouse_tick *tick) { struct vector2d move = update_movement(&move_remainder, &(tick->move_config), tick->max_move, tick->timestamp, tick->start_time); @@ -90,18 +91,17 @@ static void mouse_tick_handler(const struct zmk_mouse_tick *tick) { zmk_hid_mouse_scroll_update((int8_t)CLAMP(scroll.x, INT8_MIN, INT8_MAX), (int8_t)CLAMP(scroll.y, INT8_MIN, INT8_MAX)); } -#endif int zmk_mouse_tick_listener(const zmk_event_t *eh) { const struct zmk_mouse_tick *tick = as_zmk_mouse_tick(eh); - #if(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL) if (tick) { mouse_tick_handler(tick); return 0; } - #endif return 0; } ZMK_LISTENER(zmk_mouse_tick_listener, zmk_mouse_tick_listener); -ZMK_SUBSCRIPTION(zmk_mouse_tick_listener, zmk_mouse_tick); \ No newline at end of file +ZMK_SUBSCRIPTION(zmk_mouse_tick_listener, zmk_mouse_tick); + +#endif /* CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL */ \ No newline at end of file From a42b90959732c81f58b6f2fb4c438702dd014a6d Mon Sep 17 00:00:00 2001 From: Shawn Meier Date: Thu, 19 May 2022 12:53:22 -0600 Subject: [PATCH 016/157] better ifdef to handle non split kb --- app/include/zmk/hid.h | 3 ++- app/src/mouse/key_listener.c | 3 +-- app/src/mouse/tick_listener.c | 5 ++--- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/app/include/zmk/hid.h b/app/include/zmk/hid.h index b6dc84a6c17..83762b81fe0 100644 --- a/app/include/zmk/hid.h +++ b/app/include/zmk/hid.h @@ -15,6 +15,8 @@ #include #include +#define ZMK_HID_KEYBOARD_NKRO_MAX_USAGE HID_USAGE_KEY_KEYPAD_EQUAL + #define COLLECTION_REPORT 0x03 static const uint8_t zmk_hid_report_desc[] = { @@ -88,7 +90,6 @@ static const uint8_t zmk_hid_report_desc[] = { /* REPORT_COUNT (CONFIG_ZMK_HID_CONSUMER_REPORT_SIZE) */ HID_REPORT_COUNT(CONFIG_ZMK_HID_CONSUMER_REPORT_SIZE), HID_INPUT(0x00), - 0x00, /* END COLLECTION */ HID_END_COLLECTION, diff --git a/app/src/mouse/key_listener.c b/app/src/mouse/key_listener.c index 4f804251080..76b2817e02d 100644 --- a/app/src/mouse/key_listener.c +++ b/app/src/mouse/key_listener.c @@ -18,8 +18,7 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #include #include -//TODO: There is probably a better flag to use here -#if(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL) +#if !defined(CONFIG_ZMK_SPLIT) || defined(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL) static struct vector2d move_speed = {0}; static struct vector2d scroll_speed = {0}; static struct mouse_config move_config = (struct mouse_config){0}; diff --git a/app/src/mouse/tick_listener.c b/app/src/mouse/tick_listener.c index 37c0dcd39f5..ec66f30bbfe 100644 --- a/app/src/mouse/tick_listener.c +++ b/app/src/mouse/tick_listener.c @@ -16,8 +16,7 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #include // CLAMP -//TODO: probably a better flag to use here -#if(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL) +#if !defined(CONFIG_ZMK_SPLIT) || defined(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL) #if CONFIG_MINIMAL_LIBC static float powf(float base, float exponent) { // poor man's power implementation rounds the exponent down to the nearest integer. @@ -104,4 +103,4 @@ int zmk_mouse_tick_listener(const zmk_event_t *eh) { ZMK_LISTENER(zmk_mouse_tick_listener, zmk_mouse_tick_listener); ZMK_SUBSCRIPTION(zmk_mouse_tick_listener, zmk_mouse_tick); -#endif /* CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL */ \ No newline at end of file +#endif /* !defined(CONFIG_ZMK_SPLIT) || defined(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL) */ \ No newline at end of file From 19e494878e4951e4be13398af8ae0d9c2f416609 Mon Sep 17 00:00:00 2001 From: Shawn Meier Date: Tue, 7 Jun 2022 17:57:04 -0600 Subject: [PATCH 017/157] update mouse code from old macro CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL to new CONFIG_ZMK_SPLIT_ROLE_CENTRAL --- app/src/mouse/key_listener.c | 4 ++-- app/src/mouse/tick_listener.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/mouse/key_listener.c b/app/src/mouse/key_listener.c index 76b2817e02d..fcb700c23ed 100644 --- a/app/src/mouse/key_listener.c +++ b/app/src/mouse/key_listener.c @@ -18,7 +18,7 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #include #include -#if !defined(CONFIG_ZMK_SPLIT) || defined(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL) +#if !defined(CONFIG_ZMK_SPLIT) || defined(CONFIG_ZMK_SPLIT_ROLE_CENTRAL) static struct vector2d move_speed = {0}; static struct vector2d scroll_speed = {0}; static struct mouse_config move_config = (struct mouse_config){0}; @@ -160,4 +160,4 @@ ZMK_SUBSCRIPTION(mouse_listener, zmk_mouse_button_state_changed); ZMK_SUBSCRIPTION(mouse_listener, zmk_mouse_move_state_changed); ZMK_SUBSCRIPTION(mouse_listener, zmk_mouse_scroll_state_changed); -#endif /* CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL */ \ No newline at end of file +#endif /*!defined(CONFIG_ZMK_SPLIT) || defined(CONFIG_ZMK_SPLIT_ROLE_CENTRAL) */ diff --git a/app/src/mouse/tick_listener.c b/app/src/mouse/tick_listener.c index ec66f30bbfe..996563184b8 100644 --- a/app/src/mouse/tick_listener.c +++ b/app/src/mouse/tick_listener.c @@ -16,7 +16,7 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #include // CLAMP -#if !defined(CONFIG_ZMK_SPLIT) || defined(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL) +#if !defined(CONFIG_ZMK_SPLIT) || defined(CONFIG_ZMK_SPLIT_ROLE_CENTRAL) #if CONFIG_MINIMAL_LIBC static float powf(float base, float exponent) { // poor man's power implementation rounds the exponent down to the nearest integer. @@ -103,4 +103,4 @@ int zmk_mouse_tick_listener(const zmk_event_t *eh) { ZMK_LISTENER(zmk_mouse_tick_listener, zmk_mouse_tick_listener); ZMK_SUBSCRIPTION(zmk_mouse_tick_listener, zmk_mouse_tick); -#endif /* !defined(CONFIG_ZMK_SPLIT) || defined(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL) */ \ No newline at end of file +#endif /* !defined(CONFIG_ZMK_SPLIT) || defined(CONFIG_ZMK_SPLIT_ROLE_CENTRAL) */ From 5bae0dd95ff4a7f320eb5da1b8c0d00faa8f73de Mon Sep 17 00:00:00 2001 From: Peter Johanson Date: Tue, 5 Oct 2021 16:12:10 +0000 Subject: [PATCH 018/157] feat(display): Add optional full refresh timer. * Useful for EPD that ghost after several partial updates. Only a temporary fix, tracking number of partial updates and triggering full refreshes based on that would be better. --- app/src/display/Kconfig | 7 +++++++ app/src/display/main.c | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/app/src/display/Kconfig b/app/src/display/Kconfig index 8023025a0cd..faeb91628db 100644 --- a/app/src/display/Kconfig +++ b/app/src/display/Kconfig @@ -71,6 +71,13 @@ endchoice endif # ZMK_DISPLAY_STATUS_SCREEN_BUILT_IN +config ZMK_DISPLAY_FULL_REFRESH_PERIOD + int "(Optional) Period to issue a full refresh to the display (in seconds)" + default 0 + help + Period in seconds for how often to completely refresh/redraw the whole screen. + Most useful for e-ink/EPD displays that require occasional full redraws. + rsource "widgets/Kconfig" endif diff --git a/app/src/display/main.c b/app/src/display/main.c index 5dfad910a4e..0b9269b1e4f 100644 --- a/app/src/display/main.c +++ b/app/src/display/main.c @@ -50,6 +50,17 @@ struct k_work_q *zmk_display_work_q() { #endif } +#if CONFIG_ZMK_DISPLAY_FULL_REFRESH_PERIOD > 0 +void full_refresh_work_cb(struct k_work *work) { lv_obj_invalidate(lv_scr_act()); } + +K_WORK_DEFINE(full_refresh_work, full_refresh_work_cb); + +void full_refresh_timer_cb() { k_work_submit_to_queue(zmk_display_work_q(), &full_refresh_work); } + +K_TIMER_DEFINE(full_refresh_timer, full_refresh_timer_cb, NULL); + +#endif + void display_timer_cb() { lv_tick_inc(TICK_MS); k_work_submit_to_queue(zmk_display_work_q(), &display_tick_work); @@ -71,6 +82,10 @@ static void start_display_updates() { k_work_submit_to_queue(zmk_display_work_q(), &unblank_display_work); k_timer_start(&display_timer, K_MSEC(TICK_MS), K_MSEC(TICK_MS)); +#if CONFIG_ZMK_DISPLAY_FULL_REFRESH_PERIOD > 0 + k_timer_start(&full_refresh_timer, K_SECONDS(CONFIG_ZMK_DISPLAY_FULL_REFRESH_PERIOD), + K_SECONDS(CONFIG_ZMK_DISPLAY_FULL_REFRESH_PERIOD)); +#endif } static void stop_display_updates() { @@ -81,6 +96,9 @@ static void stop_display_updates() { k_work_submit_to_queue(zmk_display_work_q(), &blank_display_work); k_timer_stop(&display_timer); +#if CONFIG_ZMK_DISPLAY_FULL_REFRESH_PERIOD > 0 + k_timer_stop(&full_refresh_timer); +#endif } int zmk_display_is_initialized() { return initialized; } From 2e0050fa8f9b7c2b2a26a8001e39135246b92678 Mon Sep 17 00:00:00 2001 From: crides Date: Sun, 12 Jun 2022 22:12:08 -0500 Subject: [PATCH 019/157] fix dts binding include --- app/drivers/zephyr/dts/bindings/sensor/cirque,pinnacle.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/drivers/zephyr/dts/bindings/sensor/cirque,pinnacle.yaml b/app/drivers/zephyr/dts/bindings/sensor/cirque,pinnacle.yaml index fa7e561d07f..62a8c5ab15f 100644 --- a/app/drivers/zephyr/dts/bindings/sensor/cirque,pinnacle.yaml +++ b/app/drivers/zephyr/dts/bindings/sensor/cirque,pinnacle.yaml @@ -3,7 +3,7 @@ description: | compatible: "cirque,pinnacle" -include: base.yaml +include: spi-device.yaml on-bus: spi From bef8f161c2d62d7a3ddc2dd8eb42636dfcb079ef Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Wed, 22 Jun 2022 14:58:38 +0800 Subject: [PATCH 020/157] backup 1. change il0323 scanning direction 2. allow output status widget on the peripheral side --- app/CMakeLists.txt | 27 +++++++++++++-------------- app/drivers/display/il0323.c | 5 +++-- app/src/display/widgets/Kconfig | 2 +- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 4b61fc72174..e9989e18100 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -24,19 +24,25 @@ target_sources(app PRIVATE src/stdlib.c) target_sources(app PRIVATE src/activity.c) target_sources(app PRIVATE src/kscan.c) target_sources(app PRIVATE src/matrix_transform.c) +target_sources(app PRIVATE src/hid.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) target_sources_ifdef(CONFIG_ZMK_EXT_POWER app PRIVATE src/ext_power_generic.c) target_sources(app PRIVATE src/events/activity_state_changed.c) target_sources(app PRIVATE src/events/position_state_changed.c) +target_sources(app PRIVATE src/events/endpoint_selection_changed.c) +target_sources(app PRIVATE src/events/layer_state_changed.c) +target_sources(app PRIVATE src/events/modifiers_state_changed.c) +target_sources(app PRIVATE src/events/keycode_state_changed.c) target_sources(app PRIVATE src/events/sensor_event.c) target_sources_ifdef(CONFIG_ZMK_WPM app PRIVATE src/events/wpm_state_changed.c) target_sources_ifdef(CONFIG_USB_DEVICE_STACK app PRIVATE src/events/usb_conn_state_changed.c) +target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/events/ble_active_profile_changed.c) + target_sources(app PRIVATE src/behaviors/behavior_reset.c) target_sources_ifdef(CONFIG_ZMK_EXT_POWER app PRIVATE src/behaviors/behavior_ext_power.c) if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL) - target_sources(app PRIVATE src/hid.c) target_sources(app PRIVATE src/behaviors/behavior_key_press.c) target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_KEY_TOGGLE app PRIVATE src/behaviors/behavior_key_toggle.c) target_sources(app PRIVATE src/behaviors/behavior_hold_tap.c) @@ -56,25 +62,15 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL) target_sources(app PRIVATE src/combo.c) target_sources(app PRIVATE src/behavior_queue.c) target_sources(app PRIVATE src/conditional_layer.c) - target_sources(app PRIVATE src/endpoints.c) - target_sources(app PRIVATE src/events/endpoint_selection_changed.c) - target_sources(app PRIVATE src/hid_listener.c) target_sources(app PRIVATE src/keymap.c) - target_sources(app PRIVATE src/events/layer_state_changed.c) - target_sources(app PRIVATE src/events/modifiers_state_changed.c) - target_sources(app PRIVATE src/events/keycode_state_changed.c) - - if (CONFIG_ZMK_BLE) - target_sources(app PRIVATE src/events/ble_active_profile_changed.c) - target_sources(app PRIVATE src/behaviors/behavior_bt.c) - target_sources(app PRIVATE src/ble.c) - target_sources(app PRIVATE src/hog.c) - endif() endif() target_sources_ifdef(CONFIG_ZMK_RGB_UNDERGLOW app PRIVATE src/behaviors/behavior_rgb_underglow.c) target_sources_ifdef(CONFIG_ZMK_BACKLIGHT app PRIVATE src/behaviors/behavior_backlight.c) +target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/ble.c) +target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/hog.c) +target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/behaviors/behavior_bt.c) target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/events/battery_state_changed.c) target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/battery.c) @@ -85,6 +81,9 @@ target_sources_ifdef(CONFIG_USB_DEVICE_STACK app PRIVATE src/usb.c) target_sources_ifdef(CONFIG_ZMK_USB app PRIVATE src/usb_hid.c) target_sources_ifdef(CONFIG_ZMK_RGB_UNDERGLOW app PRIVATE src/rgb_underglow.c) target_sources_ifdef(CONFIG_ZMK_BACKLIGHT app PRIVATE src/backlight.c) + +target_sources(app PRIVATE src/endpoints.c) +target_sources(app PRIVATE src/hid_listener.c) target_sources(app PRIVATE src/main.c) add_subdirectory(src/display/) diff --git a/app/drivers/display/il0323.c b/app/drivers/display/il0323.c index b8117584163..73dbbdd0c18 100644 --- a/app/drivers/display/il0323.c +++ b/app/drivers/display/il0323.c @@ -304,7 +304,8 @@ static int il0323_controller_init(const struct device *dev) { il0323_busy_wait(driver); /* Pannel settings, KW mode */ - tmp[0] = IL0323_PSR_UD | IL0323_PSR_SHL | IL0323_PSR_SHD | IL0323_PSR_RST; + /* tmp[0] = IL0323_PSR_UD | IL0323_PSR_SHL | IL0323_PSR_SHD | IL0323_PSR_RST; */ + tmp[0] = IL0323_PSR_SHD | IL0323_PSR_RST; #if EPD_PANEL_WIDTH == 80 #if EPD_PANEL_HEIGHT == 128 @@ -429,4 +430,4 @@ static struct display_driver_api il0323_driver_api = { }; DEVICE_DT_INST_DEFINE(0, il0323_init, NULL, &il0323_driver, NULL, POST_KERNEL, - CONFIG_APPLICATION_INIT_PRIORITY, &il0323_driver_api); \ No newline at end of file + CONFIG_APPLICATION_INIT_PRIORITY, &il0323_driver_api); diff --git a/app/src/display/widgets/Kconfig b/app/src/display/widgets/Kconfig index 96e7e16d309..991ba35ed11 100644 --- a/app/src/display/widgets/Kconfig +++ b/app/src/display/widgets/Kconfig @@ -17,7 +17,7 @@ config ZMK_WIDGET_BATTERY_STATUS config ZMK_WIDGET_OUTPUT_STATUS bool "Widget for keyboard output status icons" - depends on BT && (!ZMK_SPLIT_BLE || ZMK_SPLIT_ROLE_CENTRAL) + # depends on BT && (!ZMK_SPLIT_BLE || ZMK_SPLIT_ROLE_CENTRAL) default y if BT && (!ZMK_SPLIT_BLE || ZMK_SPLIT_ROLE_CENTRAL) select LVGL_USE_LABEL From 0f109777847be529c005d05ce7f538260d0c4ecc Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Thu, 23 Jun 2022 07:33:21 +0800 Subject: [PATCH 021/157] change il0323 scanning direction to rotate the screen 180 degree --- app/CMakeLists.txt | 27 ++++++++++++++------------- app/src/display/widgets/Kconfig | 2 +- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index e9989e18100..4b61fc72174 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -24,25 +24,19 @@ target_sources(app PRIVATE src/stdlib.c) target_sources(app PRIVATE src/activity.c) target_sources(app PRIVATE src/kscan.c) target_sources(app PRIVATE src/matrix_transform.c) -target_sources(app PRIVATE src/hid.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) target_sources_ifdef(CONFIG_ZMK_EXT_POWER app PRIVATE src/ext_power_generic.c) target_sources(app PRIVATE src/events/activity_state_changed.c) target_sources(app PRIVATE src/events/position_state_changed.c) -target_sources(app PRIVATE src/events/endpoint_selection_changed.c) -target_sources(app PRIVATE src/events/layer_state_changed.c) -target_sources(app PRIVATE src/events/modifiers_state_changed.c) -target_sources(app PRIVATE src/events/keycode_state_changed.c) target_sources(app PRIVATE src/events/sensor_event.c) target_sources_ifdef(CONFIG_ZMK_WPM app PRIVATE src/events/wpm_state_changed.c) target_sources_ifdef(CONFIG_USB_DEVICE_STACK app PRIVATE src/events/usb_conn_state_changed.c) -target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/events/ble_active_profile_changed.c) - target_sources(app PRIVATE src/behaviors/behavior_reset.c) target_sources_ifdef(CONFIG_ZMK_EXT_POWER app PRIVATE src/behaviors/behavior_ext_power.c) if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL) + target_sources(app PRIVATE src/hid.c) target_sources(app PRIVATE src/behaviors/behavior_key_press.c) target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_KEY_TOGGLE app PRIVATE src/behaviors/behavior_key_toggle.c) target_sources(app PRIVATE src/behaviors/behavior_hold_tap.c) @@ -62,15 +56,25 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL) target_sources(app PRIVATE src/combo.c) target_sources(app PRIVATE src/behavior_queue.c) target_sources(app PRIVATE src/conditional_layer.c) + target_sources(app PRIVATE src/endpoints.c) + target_sources(app PRIVATE src/events/endpoint_selection_changed.c) + target_sources(app PRIVATE src/hid_listener.c) target_sources(app PRIVATE src/keymap.c) + target_sources(app PRIVATE src/events/layer_state_changed.c) + target_sources(app PRIVATE src/events/modifiers_state_changed.c) + target_sources(app PRIVATE src/events/keycode_state_changed.c) + + if (CONFIG_ZMK_BLE) + target_sources(app PRIVATE src/events/ble_active_profile_changed.c) + target_sources(app PRIVATE src/behaviors/behavior_bt.c) + target_sources(app PRIVATE src/ble.c) + target_sources(app PRIVATE src/hog.c) + endif() endif() target_sources_ifdef(CONFIG_ZMK_RGB_UNDERGLOW app PRIVATE src/behaviors/behavior_rgb_underglow.c) target_sources_ifdef(CONFIG_ZMK_BACKLIGHT app PRIVATE src/behaviors/behavior_backlight.c) -target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/ble.c) -target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/hog.c) -target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/behaviors/behavior_bt.c) target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/events/battery_state_changed.c) target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/battery.c) @@ -81,9 +85,6 @@ target_sources_ifdef(CONFIG_USB_DEVICE_STACK app PRIVATE src/usb.c) target_sources_ifdef(CONFIG_ZMK_USB app PRIVATE src/usb_hid.c) target_sources_ifdef(CONFIG_ZMK_RGB_UNDERGLOW app PRIVATE src/rgb_underglow.c) target_sources_ifdef(CONFIG_ZMK_BACKLIGHT app PRIVATE src/backlight.c) - -target_sources(app PRIVATE src/endpoints.c) -target_sources(app PRIVATE src/hid_listener.c) target_sources(app PRIVATE src/main.c) add_subdirectory(src/display/) diff --git a/app/src/display/widgets/Kconfig b/app/src/display/widgets/Kconfig index 991ba35ed11..96e7e16d309 100644 --- a/app/src/display/widgets/Kconfig +++ b/app/src/display/widgets/Kconfig @@ -17,7 +17,7 @@ config ZMK_WIDGET_BATTERY_STATUS config ZMK_WIDGET_OUTPUT_STATUS bool "Widget for keyboard output status icons" - # depends on BT && (!ZMK_SPLIT_BLE || ZMK_SPLIT_ROLE_CENTRAL) + depends on BT && (!ZMK_SPLIT_BLE || ZMK_SPLIT_ROLE_CENTRAL) default y if BT && (!ZMK_SPLIT_BLE || ZMK_SPLIT_ROLE_CENTRAL) select LVGL_USE_LABEL From 09ac7ef4e453bfd53d2390d51183d25336aaccbe Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Thu, 23 Jun 2022 20:44:13 +0800 Subject: [PATCH 022/157] add short comment about epd reverse scanning --- app/drivers/display/il0323.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/drivers/display/il0323.c b/app/drivers/display/il0323.c index 73dbbdd0c18..c48e393a2cd 100644 --- a/app/drivers/display/il0323.c +++ b/app/drivers/display/il0323.c @@ -304,7 +304,9 @@ static int il0323_controller_init(const struct device *dev) { il0323_busy_wait(driver); /* Pannel settings, KW mode */ + // scanning in from left -> right, up -> down /* tmp[0] = IL0323_PSR_UD | IL0323_PSR_SHL | IL0323_PSR_SHD | IL0323_PSR_RST; */ + // rotate the screen upside down by scanning in from right -> left, down -> up tmp[0] = IL0323_PSR_SHD | IL0323_PSR_RST; #if EPD_PANEL_WIDTH == 80 From bedd068cb23be3b6b0ec6e7734db817c3f38ce9e Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Thu, 23 Jun 2022 21:21:43 +0800 Subject: [PATCH 023/157] do not update layer widget, for momentory layer by disabling event raising in mo behaviour --- app/include/zmk/keymap.h | 2 ++ app/src/behaviors/behavior_momentary_layer.c | 4 ++-- app/src/keymap.c | 12 ++++++++---- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/app/include/zmk/keymap.h b/app/include/zmk/keymap.h index 1195b94320e..ffe205829d5 100644 --- a/app/include/zmk/keymap.h +++ b/app/include/zmk/keymap.h @@ -16,6 +16,8 @@ bool zmk_keymap_layer_active(uint8_t layer); uint8_t zmk_keymap_highest_layer_active(); int zmk_keymap_layer_activate(uint8_t layer); int zmk_keymap_layer_deactivate(uint8_t layer); +int zmk_keymap_layer_activate_noevent(uint8_t layer); +int zmk_keymap_layer_deactivate_noevent(uint8_t layer); int zmk_keymap_layer_toggle(uint8_t layer); int zmk_keymap_layer_to(uint8_t layer); const char *zmk_keymap_layer_label(uint8_t layer); diff --git a/app/src/behaviors/behavior_momentary_layer.c b/app/src/behaviors/behavior_momentary_layer.c index 46e49fcc57a..5e855f466d7 100644 --- a/app/src/behaviors/behavior_momentary_layer.c +++ b/app/src/behaviors/behavior_momentary_layer.c @@ -23,13 +23,13 @@ static int behavior_mo_init(const struct device *dev) { return 0; }; static int mo_keymap_binding_pressed(struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event) { LOG_DBG("position %d layer %d", event.position, binding->param1); - return zmk_keymap_layer_activate(binding->param1); + return zmk_keymap_layer_activate_noevent(binding->param1); } static int mo_keymap_binding_released(struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event) { LOG_DBG("position %d layer %d", event.position, binding->param1); - return zmk_keymap_layer_deactivate(binding->param1); + return zmk_keymap_layer_deactivate_noevent(binding->param1); } static const struct behavior_driver_api behavior_mo_driver_api = { diff --git a/app/src/keymap.c b/app/src/keymap.c index e586316f4f4..25c8681b4e3 100644 --- a/app/src/keymap.c +++ b/app/src/keymap.c @@ -80,7 +80,7 @@ static struct zmk_behavior_binding zmk_sensor_keymap[ZMK_KEYMAP_LAYERS_LEN] #endif /* ZMK_KEYMAP_HAS_SENSORS */ -static inline int set_layer_state(uint8_t layer, bool state) { +static inline int set_layer_state(uint8_t layer, bool state, bool flag_event) { if (layer >= ZMK_KEYMAP_LAYERS_LEN) { return -EINVAL; } @@ -93,7 +93,7 @@ static inline int set_layer_state(uint8_t layer, bool state) { zmk_keymap_layers_state_t old_state = _zmk_keymap_layer_state; WRITE_BIT(_zmk_keymap_layer_state, layer, state); // Don't send state changes unless there was an actual change - if (old_state != _zmk_keymap_layer_state) { + if (flag_event && (old_state != _zmk_keymap_layer_state)) { LOG_DBG("layer_changed: layer %d state %d", layer, state); ZMK_EVENT_RAISE(create_layer_state_changed(layer, state)); } @@ -124,9 +124,13 @@ uint8_t zmk_keymap_highest_layer_active() { return zmk_keymap_layer_default(); } -int zmk_keymap_layer_activate(uint8_t layer) { return set_layer_state(layer, true); }; +int zmk_keymap_layer_activate(uint8_t layer) { return set_layer_state(layer, true, true); }; -int zmk_keymap_layer_deactivate(uint8_t layer) { return set_layer_state(layer, false); }; +int zmk_keymap_layer_deactivate(uint8_t layer) { return set_layer_state(layer, false, true); }; + +int zmk_keymap_layer_activate_noevent(uint8_t layer) { return set_layer_state(layer, true, false); }; + +int zmk_keymap_layer_deactivate_noevent(uint8_t layer) { return set_layer_state(layer, false, false); }; int zmk_keymap_layer_toggle(uint8_t layer) { if (zmk_keymap_layer_active(layer)) { From 521b99076fb613d5a6c4972ec625b54b7c8f8ec4 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Thu, 23 Jun 2022 22:09:50 +0800 Subject: [PATCH 024/157] add an kconfig option to control whether showing the layer change for mo behavior on layer-status widget --- app/src/behaviors/behavior_momentary_layer.c | 9 +++++++++ app/src/display/Kconfig | 7 +++++++ 2 files changed, 16 insertions(+) diff --git a/app/src/behaviors/behavior_momentary_layer.c b/app/src/behaviors/behavior_momentary_layer.c index 5e855f466d7..ddf0d20c410 100644 --- a/app/src/behaviors/behavior_momentary_layer.c +++ b/app/src/behaviors/behavior_momentary_layer.c @@ -23,13 +23,22 @@ static int behavior_mo_init(const struct device *dev) { return 0; }; static int mo_keymap_binding_pressed(struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event) { LOG_DBG("position %d layer %d", event.position, binding->param1); + +#if IS_ENABLED(CONFIG_MYCHANGE_DISPLAY_UPDATE_MO_BEHAVIOR) + return zmk_keymap_layer_activate(binding->param1); +#else return zmk_keymap_layer_activate_noevent(binding->param1); +#endif } static int mo_keymap_binding_released(struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event) { LOG_DBG("position %d layer %d", event.position, binding->param1); +#if IS_ENABLED(CONFIG_MYCHANGE_DISPLAY_UPDATE_MO_BEHAVIOR) + return zmk_keymap_layer_deactivate(binding->param1); +#else return zmk_keymap_layer_deactivate_noevent(binding->param1); +#endif } static const struct behavior_driver_api behavior_mo_driver_api = { diff --git a/app/src/display/Kconfig b/app/src/display/Kconfig index faeb91628db..3ee823d0f18 100644 --- a/app/src/display/Kconfig +++ b/app/src/display/Kconfig @@ -78,6 +78,13 @@ config ZMK_DISPLAY_FULL_REFRESH_PERIOD Period in seconds for how often to completely refresh/redraw the whole screen. Most useful for e-ink/EPD displays that require occasional full redraws. +config MYCHANGE_DISPLAY_UPDATE_MO_BEHAVIOR + bool "(Opional) flag controlling whether the temporary layer change should be updated on the layer status widget" + default y + depends on ZMK_DISPLAY + help + This is useful for EPD display due to the slow fresh rate + rsource "widgets/Kconfig" endif From 89dac4c54bf0f679d79f1da27c069d30cc102581 Mon Sep 17 00:00:00 2001 From: Kostas Karachalios Date: Tue, 28 Jun 2022 09:43:02 +0200 Subject: [PATCH 025/157] Add some whitespace for clarity --- app/include/zmk/hid.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/include/zmk/hid.h b/app/include/zmk/hid.h index 03477551771..c1f81d15eda 100644 --- a/app/include/zmk/hid.h +++ b/app/include/zmk/hid.h @@ -135,8 +135,10 @@ int zmk_hid_register_mods(zmk_mod_flags_t explicit_modifiers); int zmk_hid_unregister_mods(zmk_mod_flags_t explicit_modifiers); int zmk_hid_implicit_modifiers_press(zmk_mod_flags_t implicit_modifiers); int zmk_hid_implicit_modifiers_release(); + int zmk_hid_masked_modifiers_set(zmk_mod_flags_t masked_modifiers); int zmk_hid_masked_modifiers_clear(); + int zmk_hid_keyboard_press(zmk_key_t key); int zmk_hid_keyboard_release(zmk_key_t key); void zmk_hid_keyboard_clear(); From f9bed0899d8c0fb059c5d494b1f4230bc55b6031 Mon Sep 17 00:00:00 2001 From: Stephen Wan Date: Mon, 15 Mar 2021 11:57:33 -0700 Subject: [PATCH 026/157] refactor(core): read sensor values earlier Reading the values earlier will let us send the sensor values over GATT for split, which will happen in a subsequent commit. --- app/include/drivers/behavior.h | 12 ++++++------ app/include/zmk/events/sensor_event.h | 3 ++- app/src/behaviors/behavior_sensor_rotate_key_press.c | 11 +---------- app/src/keymap.c | 6 +++--- app/src/sensors.c | 9 ++++++++- 5 files changed, 20 insertions(+), 21 deletions(-) diff --git a/app/include/drivers/behavior.h b/app/include/drivers/behavior.h index fcb24f6fbbd..5c78ac21ea3 100644 --- a/app/include/drivers/behavior.h +++ b/app/include/drivers/behavior.h @@ -6,6 +6,7 @@ #pragma once +#include #include #include #include @@ -25,7 +26,7 @@ typedef int (*behavior_keymap_binding_callback_t)(struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event); typedef int (*behavior_sensor_keymap_binding_callback_t)(struct zmk_behavior_binding *binding, - const struct device *sensor, + const struct sensor_value value, int64_t timestamp); enum behavior_locality { @@ -159,12 +160,11 @@ static inline int z_impl_behavior_keymap_binding_released(struct zmk_behavior_bi * @retval Negative errno code if failure. */ __syscall int behavior_sensor_keymap_binding_triggered(struct zmk_behavior_binding *binding, - const struct device *sensor, + const struct sensor_value value, int64_t timestamp); -static inline int -z_impl_behavior_sensor_keymap_binding_triggered(struct zmk_behavior_binding *binding, - const struct device *sensor, int64_t timestamp) { +static inline int z_impl_behavior_sensor_keymap_binding_triggered( + struct zmk_behavior_binding *binding, const struct sensor_value value, int64_t timestamp) { const struct device *dev = device_get_binding(binding->behavior_dev); if (dev == NULL) { @@ -177,7 +177,7 @@ z_impl_behavior_sensor_keymap_binding_triggered(struct zmk_behavior_binding *bin return -ENOTSUP; } - return api->sensor_binding_triggered(binding, sensor, timestamp); + return api->sensor_binding_triggered(binding, value, timestamp); } /** diff --git a/app/include/zmk/events/sensor_event.h b/app/include/zmk/events/sensor_event.h index f579bc398c0..8a4299780f8 100644 --- a/app/include/zmk/events/sensor_event.h +++ b/app/include/zmk/events/sensor_event.h @@ -6,12 +6,13 @@ #pragma once +#include #include #include #include struct zmk_sensor_event { uint8_t sensor_number; - const struct device *sensor; + struct sensor_value value; int64_t timestamp; }; diff --git a/app/src/behaviors/behavior_sensor_rotate_key_press.c b/app/src/behaviors/behavior_sensor_rotate_key_press.c index c4a34a94088..81be3d967bc 100644 --- a/app/src/behaviors/behavior_sensor_rotate_key_press.c +++ b/app/src/behaviors/behavior_sensor_rotate_key_press.c @@ -21,19 +21,10 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); static int behavior_sensor_rotate_key_press_init(const struct device *dev) { return 0; }; static int on_sensor_binding_triggered(struct zmk_behavior_binding *binding, - const struct device *sensor, int64_t timestamp) { - struct sensor_value value; - int err; + const struct sensor_value value, int64_t timestamp) { uint32_t keycode; LOG_DBG("inc keycode 0x%02X dec keycode 0x%02X", binding->param1, binding->param2); - err = sensor_channel_get(sensor, SENSOR_CHAN_ROTATION, &value); - - if (err) { - LOG_WRN("Failed to ge sensor rotation value: %d", err); - return err; - } - switch (value.val1) { case 1: keycode = binding->param1; diff --git a/app/src/keymap.c b/app/src/keymap.c index e586316f4f4..f3f28ddb2fc 100644 --- a/app/src/keymap.c +++ b/app/src/keymap.c @@ -251,7 +251,7 @@ int zmk_keymap_position_state_changed(uint8_t source, uint32_t position, bool pr } #if ZMK_KEYMAP_HAS_SENSORS -int zmk_keymap_sensor_triggered(uint8_t sensor_number, const struct device *sensor, +int zmk_keymap_sensor_triggered(uint8_t sensor_number, const struct sensor_value value, int64_t timestamp) { for (int layer = ZMK_KEYMAP_LAYERS_LEN - 1; layer >= _zmk_keymap_layer_default; layer--) { if (zmk_keymap_layer_active(layer) && zmk_sensor_keymap[layer] != NULL) { @@ -269,7 +269,7 @@ int zmk_keymap_sensor_triggered(uint8_t sensor_number, const struct device *sens continue; } - ret = behavior_sensor_keymap_binding_triggered(binding, sensor, timestamp); + ret = behavior_sensor_keymap_binding_triggered(binding, value, timestamp); if (ret > 0) { LOG_DBG("behavior processing to continue to next layer"); @@ -298,7 +298,7 @@ int keymap_listener(const zmk_event_t *eh) { #if ZMK_KEYMAP_HAS_SENSORS const struct zmk_sensor_event *sensor_ev; if ((sensor_ev = as_zmk_sensor_event(eh)) != NULL) { - return zmk_keymap_sensor_triggered(sensor_ev->sensor_number, sensor_ev->sensor, + return zmk_keymap_sensor_triggered(sensor_ev->sensor_number, sensor_ev->value, sensor_ev->timestamp); } #endif /* ZMK_KEYMAP_HAS_SENSORS */ diff --git a/app/src/sensors.c b/app/src/sensors.c index dd5f4267b38..02ed5eeb918 100644 --- a/app/src/sensors.c +++ b/app/src/sensors.c @@ -44,8 +44,15 @@ static void zmk_sensors_trigger_handler(const struct device *dev, struct sensor_ return; } + struct sensor_value value; + err = sensor_channel_get(dev, SENSOR_CHAN_ROTATION, &value); + if (err) { + LOG_WRN("Failed to get sensor rotation value: %d", err); + return; + } + ZMK_EVENT_RAISE(new_zmk_sensor_event((struct zmk_sensor_event){ - .sensor_number = item->sensor_number, .sensor = dev, .timestamp = k_uptime_get()})); + .sensor_number = item->sensor_number, .value = value, .timestamp = k_uptime_get()})); } static void zmk_sensors_init_item(const char *node, uint8_t i, uint8_t abs_i) { From d8a8b565086673510c0950562726ba45ed923a86 Mon Sep 17 00:00:00 2001 From: Kim Streich Date: Tue, 15 Mar 2022 20:09:26 +0400 Subject: [PATCH 027/157] refactor(split): take params in subscribe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We'll reuse this function later to subscribe to multiple characteristics. — This commit was originally made by Stephen Wan. I just adjusted it so that it rebases on top of later changes on the zmk main branch. --- app/src/split/bluetooth/central.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/app/src/split/bluetooth/central.c b/app/src/split/bluetooth/central.c index e94a59aece8..76fe8955266 100644 --- a/app/src/split/bluetooth/central.c +++ b/app/src/split/bluetooth/central.c @@ -206,14 +206,8 @@ static uint8_t split_central_notify_func(struct bt_conn *conn, return BT_GATT_ITER_CONTINUE; } -static void split_central_subscribe(struct bt_conn *conn) { - struct peripheral_slot *slot = peripheral_slot_for_conn(conn); - if (slot == NULL) { - LOG_ERR("No peripheral state found for connection"); - return; - } - - int err = bt_gatt_subscribe(conn, &slot->subscribe_params); +static void split_central_subscribe(struct bt_conn *conn, struct bt_gatt_subscribe_params *params) { + int err = bt_gatt_subscribe(conn, params); switch (err) { case -EALREADY: LOG_DBG("[ALREADY SUBSCRIBED]"); @@ -260,7 +254,7 @@ static uint8_t split_central_chrc_discovery_func(struct bt_conn *conn, slot->subscribe_params.value_handle = bt_gatt_attr_value_handle(attr); slot->subscribe_params.notify = split_central_notify_func; slot->subscribe_params.value = BT_GATT_CCC_NOTIFY; - split_central_subscribe(conn); + split_central_subscribe(conn, &slot->subscribe_params); } else if (!bt_uuid_cmp(((struct bt_gatt_chrc *)attr->user_data)->uuid, BT_UUID_DECLARE_128(ZMK_SPLIT_BT_CHAR_RUN_BEHAVIOR_UUID))) { LOG_DBG("Found run behavior handle"); From f01c148ddece6561ad228d1ec99c38b76d510d26 Mon Sep 17 00:00:00 2001 From: Kim Streich Date: Sun, 3 Apr 2022 12:50:51 +0400 Subject: [PATCH 028/157] feature(split): add support for sensors from peripheral MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds a new GATT characteristics on the peripheral side and wires it up to read sensor values. The central side subscribes to this new characteristics and replays sensor values on its side. — This commit was originally made by Stephen Wan. I just adjusted it so that it rebases on top of later changes on the zmk main branch. --- app/include/zmk/split/bluetooth/service.h | 6 +- app/include/zmk/split/bluetooth/uuid.h | 1 + app/src/split/bluetooth/central.c | 105 ++++++++++++++++++++++ app/src/split/bluetooth/service.c | 72 ++++++++++++++- app/src/split/bluetooth/split_listener.c | 32 +++++-- 5 files changed, 207 insertions(+), 9 deletions(-) diff --git a/app/include/zmk/split/bluetooth/service.h b/app/include/zmk/split/bluetooth/service.h index f0c1d79ff7d..9acf120c80e 100644 --- a/app/include/zmk/split/bluetooth/service.h +++ b/app/include/zmk/split/bluetooth/service.h @@ -6,6 +6,8 @@ #pragma once +#include + #define ZMK_SPLIT_RUN_BEHAVIOR_DEV_LEN 9 struct zmk_split_run_behavior_data { @@ -20,5 +22,7 @@ struct zmk_split_run_behavior_payload { char behavior_dev[ZMK_SPLIT_RUN_BEHAVIOR_DEV_LEN]; } __packed; + int zmk_split_bt_position_pressed(uint8_t position); -int zmk_split_bt_position_released(uint8_t position); \ No newline at end of file +int zmk_split_bt_position_released(uint8_t position); +int zmk_split_bt_sensor_triggered(uint8_t sensor_number, struct sensor_value value); diff --git a/app/include/zmk/split/bluetooth/uuid.h b/app/include/zmk/split/bluetooth/uuid.h index 735f5751d0a..7dac743ffe7 100644 --- a/app/include/zmk/split/bluetooth/uuid.h +++ b/app/include/zmk/split/bluetooth/uuid.h @@ -16,3 +16,4 @@ #define ZMK_SPLIT_BT_SERVICE_UUID ZMK_BT_SPLIT_UUID(0x00000000) #define ZMK_SPLIT_BT_CHAR_POSITION_STATE_UUID ZMK_BT_SPLIT_UUID(0x00000001) #define ZMK_SPLIT_BT_CHAR_RUN_BEHAVIOR_UUID ZMK_BT_SPLIT_UUID(0x00000002) +#define ZMK_SPLIT_BT_CHAR_SENSOR_STATE_UUID ZMK_BT_SPLIT_UUID(0x00000002) diff --git a/app/src/split/bluetooth/central.c b/app/src/split/bluetooth/central.c index 76fe8955266..52aa3d6d912 100644 --- a/app/src/split/bluetooth/central.c +++ b/app/src/split/bluetooth/central.c @@ -20,10 +20,12 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #include #include #include +#include #include #include #include #include +#include #include static int start_scan(void); @@ -162,6 +164,60 @@ int confirm_peripheral_slot_conn(struct bt_conn *conn) { return 0; } +void peripheral_event_work_callback(struct k_work *work) { + struct zmk_position_state_changed ev; + while (k_msgq_get(&peripheral_event_msgq, &ev, K_NO_WAIT) == 0) { + LOG_DBG("Trigger key position state change for %d", ev.position); + ZMK_EVENT_RAISE(new_zmk_position_state_changed(ev)); + } +} + +K_WORK_DEFINE(peripheral_event_work, peripheral_event_work_callback); + +#if ZMK_KEYMAP_HAS_SENSORS +K_MSGQ_DEFINE(peripheral_sensor_event_msgq, sizeof(struct zmk_sensor_event), + CONFIG_ZMK_SPLIT_BLE_CENTRAL_POSITION_QUEUE_SIZE, 4); + +void peripheral_sensor_event_work_callback(struct k_work *work) { + struct zmk_sensor_event ev; + while (k_msgq_get(&peripheral_sensor_event_msgq, &ev, K_NO_WAIT) == 0) { + LOG_DBG("Trigger sensor change for %d", ev.sensor_number); + ZMK_EVENT_RAISE(new_zmk_sensor_event(ev)); + } +} + +K_WORK_DEFINE(peripheral_sensor_event_work, peripheral_sensor_event_work_callback); + +struct sensor_event { + uint8_t sensor_number; + struct sensor_value value; +}; + +static uint8_t split_central_sensor_notify_func(struct bt_conn *conn, + struct bt_gatt_subscribe_params *params, + const void *data, uint16_t length) { + + const struct sensor_event *sensor_event = data; + + if (!data) { + LOG_DBG("[UNSUBSCRIBED]"); + params->value_handle = 0U; + return BT_GATT_ITER_STOP; + } + LOG_DBG("[SENSOR NOTIFICATION] data %p length %u", data, length); + + struct zmk_sensor_event ev = { + .sensor_number = sensor_event->sensor_number, + .value = {.val1 = (sensor_event->value).val1, .val2 = (sensor_event->value).val2}, + .timestamp = k_uptime_get()}; + + k_msgq_put(&peripheral_sensor_event_msgq, &ev, K_NO_WAIT); + k_work_submit(&peripheral_sensor_event_work); + + return BT_GATT_ITER_CONTINUE; +} +#endif /* ZMK_KEYMAP_HAS_SENSORS */ + static uint8_t split_central_notify_func(struct bt_conn *conn, struct bt_gatt_subscribe_params *params, const void *data, uint16_t length) { @@ -221,6 +277,39 @@ static void split_central_subscribe(struct bt_conn *conn, struct bt_gatt_subscri } } +#if ZMK_KEYMAP_HAS_SENSORS +static struct bt_uuid_128 sensor_uuid = BT_UUID_INIT_128(ZMK_SPLIT_BT_SERVICE_UUID); +static struct bt_gatt_discover_params sensor_discover_params; +static struct bt_gatt_subscribe_params sensor_subscribe_params; +static uint8_t split_central_sensor_desc_discovery_func(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + struct bt_gatt_discover_params *params) { + int err; + + if (!bt_uuid_cmp(sensor_discover_params.uuid, + BT_UUID_DECLARE_128(ZMK_SPLIT_BT_CHAR_SENSOR_STATE_UUID))) { + memcpy(&sensor_uuid, BT_UUID_GATT_CCC, sizeof(sensor_uuid)); + sensor_discover_params.uuid = &sensor_uuid.uuid; + sensor_discover_params.start_handle = attr->handle; + sensor_discover_params.type = BT_GATT_DISCOVER_DESCRIPTOR; + + sensor_subscribe_params.value_handle = bt_gatt_attr_value_handle(attr); + + err = bt_gatt_discover(conn, &sensor_discover_params); + if (err) { + LOG_ERR("Discover failed (err %d)", err); + } + } else { + sensor_subscribe_params.notify = split_central_sensor_notify_func; + sensor_subscribe_params.value = BT_GATT_CCC_NOTIFY; + sensor_subscribe_params.ccc_handle = attr->handle; + split_central_subscribe(conn, &sensor_subscribe_params); + } + + return BT_GATT_ITER_STOP; +} +#endif /* ZMK_KEYMAP_HAS_SENSORS */ + static uint8_t split_central_chrc_discovery_func(struct bt_conn *conn, const struct bt_gatt_attr *attr, struct bt_gatt_discover_params *params) { @@ -298,6 +387,22 @@ static uint8_t split_central_service_discovery_func(struct bt_conn *conn, if (err) { LOG_ERR("Failed to start discovering split service characteristics (err %d)", err); } + +#if ZMK_KEYMAP_HAS_SENSORS + memcpy(&sensor_uuid, BT_UUID_DECLARE_128(ZMK_SPLIT_BT_CHAR_SENSOR_STATE_UUID), + sizeof(sensor_uuid)); + sensor_discover_params.uuid = &sensor_uuid.uuid; + sensor_discover_params.start_handle = attr->handle; + sensor_discover_params.end_handle = 0xffff; + sensor_discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC; + sensor_discover_params.func = split_central_sensor_desc_discovery_func; + + err = bt_gatt_discover(conn, &sensor_discover_params); + if (err) { + LOG_ERR("Discover failed (err %d)", err); + } +#endif /* ZMK_KEYMAP_HAS_SENSORS */ + return BT_GATT_ITER_STOP; } diff --git a/app/src/split/bluetooth/service.c b/app/src/split/bluetooth/service.c index 5da5401d682..730e260333b 100644 --- a/app/src/split/bluetooth/service.c +++ b/app/src/split/bluetooth/service.c @@ -4,6 +4,7 @@ * SPDX-License-Identifier: MIT */ +#include #include #include #include @@ -20,6 +21,23 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #include #include #include +#include + +#if ZMK_KEYMAP_HAS_SENSORS +struct sensor_event { + uint8_t sensor_number; + struct sensor_value value; +} sensor_event; + +static ssize_t split_svc_sensor_state(struct bt_conn *conn, const struct bt_gatt_attr *attrs, + void *buf, uint16_t len, uint16_t offset) { + return bt_gatt_attr_read(conn, attrs, buf, len, offset, &sensor_event, sizeof(sensor_event)); +} + +static void split_svc_sensor_state_ccc(const struct bt_gatt_attr *attr, uint16_t value) { + LOG_DBG("value %d", value); +} +#endif /* ZMK_KEYMAP_HAS_SENSORS */ #define POS_STATE_LEN 16 @@ -98,7 +116,14 @@ BT_GATT_SERVICE_DEFINE( BT_GATT_CHRC_WRITE_WITHOUT_RESP, BT_GATT_PERM_WRITE_ENCRYPT, NULL, split_svc_run_behavior, &behavior_run_payload), BT_GATT_DESCRIPTOR(BT_UUID_NUM_OF_DIGITALS, BT_GATT_PERM_READ, split_svc_num_of_positions, NULL, - &num_of_positions), ); + &num_of_positions), +#if ZMK_KEYMAP_HAS_SENSORS + BT_GATT_CHARACTERISTIC(BT_UUID_DECLARE_128(ZMK_SPLIT_BT_CHAR_SENSOR_STATE_UUID), + BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY, BT_GATT_PERM_READ_ENCRYPT, + split_svc_sensor_state, NULL, &sensor_event), + BT_GATT_CCC(split_svc_sensor_state_ccc, BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT), +#endif /* ZMK_KEYMAP_HAS_SENSORS */ +); K_THREAD_STACK_DEFINE(service_q_stack, CONFIG_ZMK_SPLIT_BLE_PERIPHERAL_STACK_SIZE); @@ -151,6 +176,51 @@ int zmk_split_bt_position_released(uint8_t position) { return send_position_state(); } +#if ZMK_KEYMAP_HAS_SENSORS +K_MSGQ_DEFINE(sensor_state_msgq, sizeof(sensor_event), + CONFIG_ZMK_SPLIT_BLE_PERIPHERAL_POSITION_QUEUE_SIZE, 4); + +void send_sensor_state_callback(struct k_work *work) { + struct sensor_event ev; + + while (k_msgq_get(&sensor_state_msgq, &ev, K_NO_WAIT) == 0) { + int err = bt_gatt_notify(NULL, &split_svc.attrs[5], &ev, sizeof(ev)); + if (err) { + LOG_DBG("Error notifying %d", err); + } + } +}; + +K_WORK_DEFINE(service_sensor_notify_work, send_sensor_state_callback); + +int send_sensor_state() { + int err = k_msgq_put(&sensor_state_msgq, &sensor_event, K_MSEC(100)); + if (err) { + // retry... + switch (err) { + case -EAGAIN: { + LOG_WRN("Sensor state message queue full, popping first message and queueing again"); + struct sensor_event discarded_state; + k_msgq_get(&sensor_state_msgq, &discarded_state, K_NO_WAIT); + return send_sensor_state(); + } + default: + LOG_WRN("Failed to queue sensor state to send (%d)", err); + return err; + } + } + + k_work_submit_to_queue(&service_work_q, &service_sensor_notify_work); + return 0; +} + +int zmk_split_bt_sensor_triggered(uint8_t sensor_number, struct sensor_value value) { + sensor_event.sensor_number = sensor_number; + sensor_event.value = value; + return send_sensor_state(); +} +#endif /* ZMK_KEYMAP_HAS_SENSORS */ + int service_init(const struct device *_arg) { static const struct k_work_queue_config queue_config = { .name = "Split Peripheral Notification Queue"}; diff --git a/app/src/split/bluetooth/split_listener.c b/app/src/split/bluetooth/split_listener.c index 3f3763ae04a..93e58743037 100644 --- a/app/src/split/bluetooth/split_listener.c +++ b/app/src/split/bluetooth/split_listener.c @@ -5,6 +5,7 @@ */ #include +#include #include #include @@ -13,21 +14,38 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #include #include +#include #include +#include #include int split_listener(const zmk_event_t *eh) { LOG_DBG(""); - const struct zmk_position_state_changed *ev = as_zmk_position_state_changed(eh); - if (ev != NULL) { - if (ev->state) { - return zmk_split_bt_position_pressed(ev->position); - } else { - return zmk_split_bt_position_released(ev->position); + const struct zmk_position_state_changed *pos_ev; + if ((pos_ev = as_zmk_position_state_changed(eh)) != NULL) { + if (pos_ev != NULL) { + if (pos_ev->state) { + return zmk_split_bt_position_pressed(pos_ev->position); + } else { + return zmk_split_bt_position_released(pos_ev->position); + } } } + +#if ZMK_KEYMAP_HAS_SENSORS + const struct zmk_sensor_event *sensor_ev; + if ((sensor_ev = as_zmk_sensor_event(eh)) != NULL) { + if (sensor_ev != NULL) { + return zmk_split_bt_sensor_triggered(sensor_ev->sensor_number, sensor_ev->value); + } + } +#endif /* ZMK_KEYMAP_HAS_SENSORS */ return ZMK_EV_EVENT_BUBBLE; } ZMK_LISTENER(split_listener, split_listener); -ZMK_SUBSCRIPTION(split_listener, zmk_position_state_changed); \ No newline at end of file +ZMK_SUBSCRIPTION(split_listener, zmk_position_state_changed); + +#if ZMK_KEYMAP_HAS_SENSORS +ZMK_SUBSCRIPTION(split_listener, zmk_sensor_event); +#endif /* ZMK_KEYMAP_HAS_SENSORS */ From 7a1fc03d2811e0b84815b64d1f2e1826f5807ba8 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Thu, 30 Jun 2022 19:15:59 +0800 Subject: [PATCH 029/157] rebase split-encoder upon main branch, and resolve conflicts --- app/src/split/bluetooth/central.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/app/src/split/bluetooth/central.c b/app/src/split/bluetooth/central.c index 52aa3d6d912..b6c8b57060b 100644 --- a/app/src/split/bluetooth/central.c +++ b/app/src/split/bluetooth/central.c @@ -164,16 +164,6 @@ int confirm_peripheral_slot_conn(struct bt_conn *conn) { return 0; } -void peripheral_event_work_callback(struct k_work *work) { - struct zmk_position_state_changed ev; - while (k_msgq_get(&peripheral_event_msgq, &ev, K_NO_WAIT) == 0) { - LOG_DBG("Trigger key position state change for %d", ev.position); - ZMK_EVENT_RAISE(new_zmk_position_state_changed(ev)); - } -} - -K_WORK_DEFINE(peripheral_event_work, peripheral_event_work_callback); - #if ZMK_KEYMAP_HAS_SENSORS K_MSGQ_DEFINE(peripheral_sensor_event_msgq, sizeof(struct zmk_sensor_event), CONFIG_ZMK_SPLIT_BLE_CENTRAL_POSITION_QUEUE_SIZE, 4); From ee10458f3924d5b3cf7d69a656a531fa5658b3fd Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Mon, 11 Jul 2022 20:40:50 +0800 Subject: [PATCH 030/157] add nrfmacro board and sweepro-ce shield --- .gitignore | 3 +- app/boards/arm/nrfmacro/CMakeLists.txt | 10 + app/boards/arm/nrfmacro/Kconfig | 5 + app/boards/arm/nrfmacro/Kconfig.board | 8 + app/boards/arm/nrfmacro/Kconfig.defconfig | 213 +++++++++++++ .../arm/nrfmacro/arduino_pro_micro_pins.dtsi | 59 ++++ app/boards/arm/nrfmacro/board.cmake | 4 + .../arm/nrfmacro/custom_status_screen.c | 72 +++++ .../arm/nrfmacro/custom_status_screen.h | 10 + .../arm/nrfmacro/epd_screen/CMakeLists.txt | 7 + .../nrfmacro/epd_screen/master/CMakeLists.txt | 55 ++++ .../epd_screen/master/battery_status.c | 165 ++++++++++ .../epd_screen/master/battery_status.h | 20 ++ .../epd_screen/master/icons/CMakeLists.txt | 4 + .../epd_screen/master/icons/USB_connected.c | 63 ++++ .../nrfmacro/epd_screen/master/icons/batt_0.c | 65 ++++ .../epd_screen/master/icons/batt_0_chg.c | 64 ++++ .../epd_screen/master/icons/batt_100.c | 64 ++++ .../epd_screen/master/icons/batt_100_chg.c | 65 ++++ .../epd_screen/master/icons/batt_25.c | 64 ++++ .../epd_screen/master/icons/batt_25_chg.c | 64 ++++ .../nrfmacro/epd_screen/master/icons/batt_5.c | 64 ++++ .../epd_screen/master/icons/batt_50.c | 64 ++++ .../epd_screen/master/icons/batt_50_chg.c | 64 ++++ .../epd_screen/master/icons/batt_5_chg.c | 64 ++++ .../epd_screen/master/icons/batt_75.c | 64 ++++ .../epd_screen/master/icons/batt_75_chg.c | 64 ++++ .../master/icons/bluetooth_advertising.c | 66 ++++ .../master/icons/bluetooth_advertising_1.c | 64 ++++ .../master/icons/bluetooth_advertising_2.c | 64 ++++ .../master/icons/bluetooth_advertising_3.c | 64 ++++ .../master/icons/bluetooth_advertising_4.c | 64 ++++ .../master/icons/bluetooth_advertising_5.c | 64 ++++ .../master/icons/bluetooth_connected_1.c | 64 ++++ .../master/icons/bluetooth_connected_2.c | 64 ++++ .../master/icons/bluetooth_connected_3.c | 64 ++++ .../master/icons/bluetooth_connected_4.c | 64 ++++ .../master/icons/bluetooth_connected_5.c | 64 ++++ .../master/icons/bluetooth_connected_right.c | 69 ++++ .../master/icons/bluetooth_disconnected.c | 68 ++++ .../nrfmacro/epd_screen/master/icons/layers.c | 45 +++ .../nrfmacro/epd_screen/master/layer_status.c | 102 ++++++ .../nrfmacro/epd_screen/master/layer_status.h | 20 ++ .../epd_screen/master/output_status.c | 183 +++++++++++ .../epd_screen/master/output_status.h | 19 ++ .../epd_screen/master/status_screen.c | 60 ++++ .../nrfmacro/epd_screen/slave/CMakeLists.txt | 28 ++ .../epd_screen/slave/battery_status.c | 89 ++++++ .../epd_screen/slave/battery_status.h | 18 ++ .../epd_screen/slave/icons/customlogo.c | 87 +++++ .../epd_screen/slave/icons/eye_of_horus1.c | 85 +++++ .../epd_screen/slave/icons/eye_of_horus2.c | 95 ++++++ .../epd_screen/slave/icons/marklogo.c | 45 +++ .../epd_screen/slave/icons/pictures/beer.png | Bin 0 -> 1092140 bytes .../slave/icons/pictures/communism1.png | Bin 0 -> 251466 bytes .../slave/icons/pictures/communism2.png | Bin 0 -> 35629 bytes .../slave/icons/pictures/communism3.png | Bin 0 -> 60877 bytes .../slave/icons/pictures/eye_of_horus2.png | Bin 0 -> 1864 bytes .../slave/icons/pictures/figure.png | Bin 0 -> 107828 bytes .../slave/icons/pictures/finger.png | Bin 0 -> 118477 bytes .../epd_screen/slave/icons/pictures/girl1.png | Bin 0 -> 140239 bytes .../epd_screen/slave/icons/pictures/horse.png | Bin 0 -> 49337 bytes .../slave/icons/pictures/karl_marx.png | Bin 0 -> 455938 bytes .../slave/icons/pictures/test_64x64.svg | 132 ++++++++ .../slave/icons/pictures/testlogo_68x62.png | Bin 0 -> 2566 bytes .../slave/icons/pictures/we_need_you.png | Bin 0 -> 30349 bytes .../slave/icons/pictures/wukong.png | Bin 0 -> 151917 bytes .../slave/icons/pictures/wukong2.png | Bin 0 -> 23836 bytes .../nrfmacro/epd_screen/slave/icons/stdlogo.c | 95 ++++++ .../epd_screen/slave/peripheral_status.c | 60 ++++ .../epd_screen/slave/peripheral_status.h | 19 ++ .../nrfmacro/epd_screen/slave/status_screen.c | 74 +++++ app/boards/arm/nrfmacro/nrfmacro.dts | 177 +++++++++++ app/boards/arm/nrfmacro/nrfmacro.yaml | 17 + app/boards/arm/nrfmacro/nrfmacro.zmk.yml | 10 + app/boards/arm/nrfmacro/nrfmacro_defconfig | 23 ++ .../arm/nrfmacro/widgets/CMakeLists.txt | 7 + .../arm/nrfmacro/widgets/battery_status.c | 165 ++++++++++ .../arm/nrfmacro/widgets/battery_status.h | 20 ++ .../arm/nrfmacro/widgets/icons/CMakeLists.txt | 4 + .../nrfmacro/widgets/icons/USB_connected.c | 63 ++++ .../arm/nrfmacro/widgets/icons/batt_0.c | 65 ++++ .../arm/nrfmacro/widgets/icons/batt_0_chg.c | 64 ++++ .../arm/nrfmacro/widgets/icons/batt_100.c | 64 ++++ .../arm/nrfmacro/widgets/icons/batt_100_chg.c | 65 ++++ .../arm/nrfmacro/widgets/icons/batt_25.c | 64 ++++ .../arm/nrfmacro/widgets/icons/batt_25_chg.c | 64 ++++ .../arm/nrfmacro/widgets/icons/batt_5.c | 64 ++++ .../arm/nrfmacro/widgets/icons/batt_50.c | 64 ++++ .../arm/nrfmacro/widgets/icons/batt_50_chg.c | 64 ++++ .../arm/nrfmacro/widgets/icons/batt_5_chg.c | 64 ++++ .../arm/nrfmacro/widgets/icons/batt_75.c | 64 ++++ .../arm/nrfmacro/widgets/icons/batt_75_chg.c | 64 ++++ .../widgets/icons/bluetooth_advertising.c | 66 ++++ .../widgets/icons/bluetooth_advertising_1.c | 64 ++++ .../widgets/icons/bluetooth_advertising_2.c | 64 ++++ .../widgets/icons/bluetooth_advertising_3.c | 64 ++++ .../widgets/icons/bluetooth_advertising_4.c | 64 ++++ .../widgets/icons/bluetooth_advertising_5.c | 64 ++++ .../widgets/icons/bluetooth_connected_1.c | 64 ++++ .../widgets/icons/bluetooth_connected_2.c | 64 ++++ .../widgets/icons/bluetooth_connected_3.c | 64 ++++ .../widgets/icons/bluetooth_connected_4.c | 64 ++++ .../widgets/icons/bluetooth_connected_5.c | 64 ++++ .../widgets/icons/bluetooth_connected_right.c | 69 ++++ .../icons/bluetooth_disconnected_right.c | 68 ++++ .../arm/nrfmacro/widgets/icons/layers.c | 68 ++++ .../arm/nrfmacro/widgets/icons/layers2.c | 45 +++ .../arm/nrfmacro/widgets/icons/zenlogo.c | 71 +++++ .../arm/nrfmacro/widgets/layer_status.c | 102 ++++++ .../arm/nrfmacro/widgets/layer_status.h | 20 ++ .../arm/nrfmacro/widgets/output_status.c | 190 +++++++++++ .../arm/nrfmacro/widgets/output_status.h | 19 ++ app/boards/shields/34key-common-colemak.dtsi | 60 ++++ app/boards/shields/34key-common.dtsi | 289 +++++++++++++++++ app/boards/shields/36key-common-colemak.dtsi | 58 ++++ app/boards/shields/36key-common.dtsi | 296 ++++++++++++++++++ app/boards/shields/42key-common-colemak.dtsi | 291 +++++++++++++++++ app/boards/shields/42key-common.dtsi | 291 +++++++++++++++++ app/boards/shields/key-common.dtsi | 250 +++++++++++++++ .../shields/sweepro-ce/Kconfig.defconfig | 29 ++ app/boards/shields/sweepro-ce/Kconfig.shield | 8 + app/boards/shields/sweepro-ce/README.md | 35 +++ .../shields/sweepro-ce/sweepro-ce.conf.bak | 29 ++ app/boards/shields/sweepro-ce/sweepro-ce.dtsi | 126 ++++++++ .../shields/sweepro-ce/sweepro-ce.keymap | 13 + .../shields/sweepro-ce/sweepro-ce.zmk.yml | 12 + .../shields/sweepro-ce/sweepro-ce_left.conf | 36 +++ .../sweepro-ce/sweepro-ce_left.overlay | 36 +++ .../shields/sweepro-ce/sweepro-ce_right.conf | 38 +++ .../sweepro-ce/sweepro-ce_right.overlay | 33 ++ 131 files changed, 8110 insertions(+), 1 deletion(-) create mode 100644 app/boards/arm/nrfmacro/CMakeLists.txt create mode 100644 app/boards/arm/nrfmacro/Kconfig create mode 100644 app/boards/arm/nrfmacro/Kconfig.board create mode 100644 app/boards/arm/nrfmacro/Kconfig.defconfig create mode 100644 app/boards/arm/nrfmacro/arduino_pro_micro_pins.dtsi create mode 100644 app/boards/arm/nrfmacro/board.cmake create mode 100644 app/boards/arm/nrfmacro/custom_status_screen.c create mode 100644 app/boards/arm/nrfmacro/custom_status_screen.h create mode 100644 app/boards/arm/nrfmacro/epd_screen/CMakeLists.txt create mode 100644 app/boards/arm/nrfmacro/epd_screen/master/CMakeLists.txt create mode 100644 app/boards/arm/nrfmacro/epd_screen/master/battery_status.c create mode 100644 app/boards/arm/nrfmacro/epd_screen/master/battery_status.h create mode 100644 app/boards/arm/nrfmacro/epd_screen/master/icons/CMakeLists.txt create mode 100644 app/boards/arm/nrfmacro/epd_screen/master/icons/USB_connected.c create mode 100644 app/boards/arm/nrfmacro/epd_screen/master/icons/batt_0.c create mode 100644 app/boards/arm/nrfmacro/epd_screen/master/icons/batt_0_chg.c create mode 100644 app/boards/arm/nrfmacro/epd_screen/master/icons/batt_100.c create mode 100644 app/boards/arm/nrfmacro/epd_screen/master/icons/batt_100_chg.c create mode 100644 app/boards/arm/nrfmacro/epd_screen/master/icons/batt_25.c create mode 100644 app/boards/arm/nrfmacro/epd_screen/master/icons/batt_25_chg.c create mode 100644 app/boards/arm/nrfmacro/epd_screen/master/icons/batt_5.c create mode 100644 app/boards/arm/nrfmacro/epd_screen/master/icons/batt_50.c create mode 100644 app/boards/arm/nrfmacro/epd_screen/master/icons/batt_50_chg.c create mode 100644 app/boards/arm/nrfmacro/epd_screen/master/icons/batt_5_chg.c create mode 100644 app/boards/arm/nrfmacro/epd_screen/master/icons/batt_75.c create mode 100644 app/boards/arm/nrfmacro/epd_screen/master/icons/batt_75_chg.c create mode 100644 app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_advertising.c create mode 100644 app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_advertising_1.c create mode 100644 app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_advertising_2.c create mode 100644 app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_advertising_3.c create mode 100644 app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_advertising_4.c create mode 100644 app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_advertising_5.c create mode 100644 app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_connected_1.c create mode 100644 app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_connected_2.c create mode 100644 app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_connected_3.c create mode 100644 app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_connected_4.c create mode 100644 app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_connected_5.c create mode 100644 app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_connected_right.c create mode 100644 app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_disconnected.c create mode 100644 app/boards/arm/nrfmacro/epd_screen/master/icons/layers.c create mode 100644 app/boards/arm/nrfmacro/epd_screen/master/layer_status.c create mode 100644 app/boards/arm/nrfmacro/epd_screen/master/layer_status.h create mode 100644 app/boards/arm/nrfmacro/epd_screen/master/output_status.c create mode 100644 app/boards/arm/nrfmacro/epd_screen/master/output_status.h create mode 100644 app/boards/arm/nrfmacro/epd_screen/master/status_screen.c create mode 100644 app/boards/arm/nrfmacro/epd_screen/slave/CMakeLists.txt create mode 100644 app/boards/arm/nrfmacro/epd_screen/slave/battery_status.c create mode 100644 app/boards/arm/nrfmacro/epd_screen/slave/battery_status.h create mode 100644 app/boards/arm/nrfmacro/epd_screen/slave/icons/customlogo.c create mode 100644 app/boards/arm/nrfmacro/epd_screen/slave/icons/eye_of_horus1.c create mode 100644 app/boards/arm/nrfmacro/epd_screen/slave/icons/eye_of_horus2.c create mode 100644 app/boards/arm/nrfmacro/epd_screen/slave/icons/marklogo.c create mode 100644 app/boards/arm/nrfmacro/epd_screen/slave/icons/pictures/beer.png create mode 100644 app/boards/arm/nrfmacro/epd_screen/slave/icons/pictures/communism1.png create mode 100644 app/boards/arm/nrfmacro/epd_screen/slave/icons/pictures/communism2.png create mode 100644 app/boards/arm/nrfmacro/epd_screen/slave/icons/pictures/communism3.png create mode 100644 app/boards/arm/nrfmacro/epd_screen/slave/icons/pictures/eye_of_horus2.png create mode 100644 app/boards/arm/nrfmacro/epd_screen/slave/icons/pictures/figure.png create mode 100644 app/boards/arm/nrfmacro/epd_screen/slave/icons/pictures/finger.png create mode 100644 app/boards/arm/nrfmacro/epd_screen/slave/icons/pictures/girl1.png create mode 100644 app/boards/arm/nrfmacro/epd_screen/slave/icons/pictures/horse.png create mode 100644 app/boards/arm/nrfmacro/epd_screen/slave/icons/pictures/karl_marx.png create mode 100644 app/boards/arm/nrfmacro/epd_screen/slave/icons/pictures/test_64x64.svg create mode 100644 app/boards/arm/nrfmacro/epd_screen/slave/icons/pictures/testlogo_68x62.png create mode 100644 app/boards/arm/nrfmacro/epd_screen/slave/icons/pictures/we_need_you.png create mode 100644 app/boards/arm/nrfmacro/epd_screen/slave/icons/pictures/wukong.png create mode 100644 app/boards/arm/nrfmacro/epd_screen/slave/icons/pictures/wukong2.png create mode 100644 app/boards/arm/nrfmacro/epd_screen/slave/icons/stdlogo.c create mode 100644 app/boards/arm/nrfmacro/epd_screen/slave/peripheral_status.c create mode 100644 app/boards/arm/nrfmacro/epd_screen/slave/peripheral_status.h create mode 100644 app/boards/arm/nrfmacro/epd_screen/slave/status_screen.c create mode 100644 app/boards/arm/nrfmacro/nrfmacro.dts create mode 100644 app/boards/arm/nrfmacro/nrfmacro.yaml create mode 100644 app/boards/arm/nrfmacro/nrfmacro.zmk.yml create mode 100644 app/boards/arm/nrfmacro/nrfmacro_defconfig create mode 100644 app/boards/arm/nrfmacro/widgets/CMakeLists.txt create mode 100644 app/boards/arm/nrfmacro/widgets/battery_status.c create mode 100644 app/boards/arm/nrfmacro/widgets/battery_status.h create mode 100644 app/boards/arm/nrfmacro/widgets/icons/CMakeLists.txt create mode 100644 app/boards/arm/nrfmacro/widgets/icons/USB_connected.c create mode 100644 app/boards/arm/nrfmacro/widgets/icons/batt_0.c create mode 100644 app/boards/arm/nrfmacro/widgets/icons/batt_0_chg.c create mode 100644 app/boards/arm/nrfmacro/widgets/icons/batt_100.c create mode 100644 app/boards/arm/nrfmacro/widgets/icons/batt_100_chg.c create mode 100644 app/boards/arm/nrfmacro/widgets/icons/batt_25.c create mode 100644 app/boards/arm/nrfmacro/widgets/icons/batt_25_chg.c create mode 100644 app/boards/arm/nrfmacro/widgets/icons/batt_5.c create mode 100644 app/boards/arm/nrfmacro/widgets/icons/batt_50.c create mode 100644 app/boards/arm/nrfmacro/widgets/icons/batt_50_chg.c create mode 100644 app/boards/arm/nrfmacro/widgets/icons/batt_5_chg.c create mode 100644 app/boards/arm/nrfmacro/widgets/icons/batt_75.c create mode 100644 app/boards/arm/nrfmacro/widgets/icons/batt_75_chg.c create mode 100644 app/boards/arm/nrfmacro/widgets/icons/bluetooth_advertising.c create mode 100644 app/boards/arm/nrfmacro/widgets/icons/bluetooth_advertising_1.c create mode 100644 app/boards/arm/nrfmacro/widgets/icons/bluetooth_advertising_2.c create mode 100644 app/boards/arm/nrfmacro/widgets/icons/bluetooth_advertising_3.c create mode 100644 app/boards/arm/nrfmacro/widgets/icons/bluetooth_advertising_4.c create mode 100644 app/boards/arm/nrfmacro/widgets/icons/bluetooth_advertising_5.c create mode 100644 app/boards/arm/nrfmacro/widgets/icons/bluetooth_connected_1.c create mode 100644 app/boards/arm/nrfmacro/widgets/icons/bluetooth_connected_2.c create mode 100644 app/boards/arm/nrfmacro/widgets/icons/bluetooth_connected_3.c create mode 100644 app/boards/arm/nrfmacro/widgets/icons/bluetooth_connected_4.c create mode 100644 app/boards/arm/nrfmacro/widgets/icons/bluetooth_connected_5.c create mode 100644 app/boards/arm/nrfmacro/widgets/icons/bluetooth_connected_right.c create mode 100644 app/boards/arm/nrfmacro/widgets/icons/bluetooth_disconnected_right.c create mode 100644 app/boards/arm/nrfmacro/widgets/icons/layers.c create mode 100644 app/boards/arm/nrfmacro/widgets/icons/layers2.c create mode 100644 app/boards/arm/nrfmacro/widgets/icons/zenlogo.c create mode 100644 app/boards/arm/nrfmacro/widgets/layer_status.c create mode 100644 app/boards/arm/nrfmacro/widgets/layer_status.h create mode 100644 app/boards/arm/nrfmacro/widgets/output_status.c create mode 100644 app/boards/arm/nrfmacro/widgets/output_status.h create mode 100644 app/boards/shields/34key-common-colemak.dtsi create mode 100644 app/boards/shields/34key-common.dtsi create mode 100644 app/boards/shields/36key-common-colemak.dtsi create mode 100644 app/boards/shields/36key-common.dtsi create mode 100644 app/boards/shields/42key-common-colemak.dtsi create mode 100644 app/boards/shields/42key-common.dtsi create mode 100644 app/boards/shields/key-common.dtsi create mode 100644 app/boards/shields/sweepro-ce/Kconfig.defconfig create mode 100644 app/boards/shields/sweepro-ce/Kconfig.shield create mode 100644 app/boards/shields/sweepro-ce/README.md create mode 100644 app/boards/shields/sweepro-ce/sweepro-ce.conf.bak create mode 100644 app/boards/shields/sweepro-ce/sweepro-ce.dtsi create mode 100644 app/boards/shields/sweepro-ce/sweepro-ce.keymap create mode 100644 app/boards/shields/sweepro-ce/sweepro-ce.zmk.yml create mode 100644 app/boards/shields/sweepro-ce/sweepro-ce_left.conf create mode 100644 app/boards/shields/sweepro-ce/sweepro-ce_left.overlay create mode 100644 app/boards/shields/sweepro-ce/sweepro-ce_right.conf create mode 100644 app/boards/shields/sweepro-ce/sweepro-ce_right.overlay diff --git a/.gitignore b/.gitignore index 93c801d9aa9..c2ded0fdd1f 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ /zmk-config /build *.DS_Store -__pycache__ \ No newline at end of file +__pycache__ +*.~undo-tree~ \ No newline at end of file diff --git a/app/boards/arm/nrfmacro/CMakeLists.txt b/app/boards/arm/nrfmacro/CMakeLists.txt new file mode 100644 index 00000000000..085a625b68c --- /dev/null +++ b/app/boards/arm/nrfmacro/CMakeLists.txt @@ -0,0 +1,10 @@ +# +# Copyright (c) 2021 Darryl deHaan +# SPDX-License-Identifier: MIT +# + +if(CONFIG_ZMK_DISPLAY AND CONFIG_ZMK_DISPLAY_STATUS_SCREEN_CUSTOM) + add_subdirectory_ifdef(CONFIG_NRFMACRO_EPD_DISPLAY epd_screen) + # add_subdirectory_ifdef(CONFIG_NRFMACRO_LCD_DISPLAY lcd_screen) + # add_subdirectory_ifdef(CONFIG_NRFMACRO_OLED_DISPLAY oled_screen) +endif() diff --git a/app/boards/arm/nrfmacro/Kconfig b/app/boards/arm/nrfmacro/Kconfig new file mode 100644 index 00000000000..7ad73654457 --- /dev/null +++ b/app/boards/arm/nrfmacro/Kconfig @@ -0,0 +1,5 @@ +config BOARD_ENABLE_DCDC + bool "Enable DCDC mode" + select SOC_DCDC_NRF52X + default y + depends on (BOARD_NRFMACRO) \ No newline at end of file diff --git a/app/boards/arm/nrfmacro/Kconfig.board b/app/boards/arm/nrfmacro/Kconfig.board new file mode 100644 index 00000000000..234af2c76f4 --- /dev/null +++ b/app/boards/arm/nrfmacro/Kconfig.board @@ -0,0 +1,8 @@ +# nrfmacro board configuration + +# Copyright (c) 2020 The ZMK Contributors +# SPDX-License-Identifier: MIT + +config BOARD_NRFMACRO + bool "nrfmacro" + depends on SOC_NRF52840_QIAA diff --git a/app/boards/arm/nrfmacro/Kconfig.defconfig b/app/boards/arm/nrfmacro/Kconfig.defconfig new file mode 100644 index 00000000000..627f3594421 --- /dev/null +++ b/app/boards/arm/nrfmacro/Kconfig.defconfig @@ -0,0 +1,213 @@ +# Electronut Labs Papyr board configuration + +# Copyright (c) 2020 The ZMK Contributors +# SPDX-License-Identifier: MIT + +if BOARD_NRFMACRO + +config BOARD + default "nrfmacro" + +# DISPLAY options: oled, epd, lcd, default: no display +choice + prompt "nrfmacro Display option" + depends on ZMK_DISPLAY + help + Specify the type of the display peripheral. + +# config NRFMACRO_NO_DISPLAY +# bool "No display screen" + +config NRFMACRO_EPD_DISPLAY + bool "Use epaper display" + select SPI + select IL0323 + select LVGL_USE_CONT + select LVGL_FONT_MONTSERRAT_26 + select LVGL_FONT_MONTSERRAT_20 + select LVGL_FONT_MONTSERRAT_16 + select LVGL_USE_LABEL + select LVGL_USE_IMG + +config NRFMACRO_OLED_DISPLAY + bool "Ues OLED display" + select I2C + select SSD1306 + select SSD1306_REVERSE_MODE + +config NRFMACRO_LCD_DISPLAY + bool "Use SHARP memory LCD" + select SPI + select LS0XX + select LVGL_USE_CONT + select LVGL_FONT_MONTSERRAT_26 + # select LVGL_FONT_MONTSERRAT_20 + # select LVGL_FONT_MONTSERRAT_16 + # select LVGL_USE_LABEL + # select LVGL_USE_IMG + +endchoice + +# Role of the keyboard in split setup, default is master (i.e., central) +## NOTE: this is a dedicated config option for nrfmacro board, so that all shields +## using this board can share the some configuration. It's mainly used for controlling +## which widget to show on different side of the keyboard +choice + prompt "Role of the keyboard" + help + Specify the role of the keyboard + +config NRFMACRO_SHIELD_MASTER + bool "master side, in charge of communication with host" + +config NRFMACRO_SHIELD_SLAVE + bool "slave side, acting as a peripheral to master side" + +endchoice + +# Rotate the display screen 180 degree (upside down) +config NRFMACRO_ROTATE_SCREEN + bool "rotate the display screen upside down" + +# Show product mark on the screen +config NRFMACRO_SCREEN_MARK_LOGO + bool "show the mark logo on the screen" + +choice + prompt "Keyboard logo on the display screen" + help + Specify which logo to show on the centre of screen + +config NRFMACRO_SCREEN_STANDARD_LOGO + bool "Use the standard logo distributed with the firmware source" + +config NRFMACRO_SCREEN_CUSTOM_LOGO + bool "Use the customized logo designed by yourself" + +endchoice + +# USB STACK setup +if USB_DEVICE_STACK + +config USB_NRFX + default y + +endif # USB_DEVICE_STACK + +config BT_CTLR + default BT + +config ZMK_BLE + default y + +config ZMK_USB + default y + +# DISPLAY setup +## common configuration +if ZMK_DISPLAY +config ZMK_DISPLAY_DEDICATED_THREAD_STACK_SIZE + default 2048 + +endif # ZMK_DISPLAY + +if ZMK_DISPLAY_STATUS_SCREEN_CUSTOM + +if NRFMACRO_SHIELD_SLAVE +config LVGL_FONT_MONTSERRAT_16 + default y + +choice LVGL_THEME_DEFAULT_FONT_NORMAL + default LVGL_THEME_DEFAULT_FONT_NORMAL_MONTSERRAT_16 + +endchoice + +config LVGL_FONT_MONTSERRAT_12 + default y + +choice LVGL_THEME_DEFAULT_FONT_SMALL + default LVGL_THEME_DEFAULT_FONT_SMALL_MONTSERRAT_12 + +endchoice +endif + +endif # ZMK_DISPLAY_STATUS_SCREEN_CUSTOM + +## dedicated configuration +if LVGL + +config LVGL_BITS_PER_PIXEL + default 1 + +choice LVGL_COLOR_DEPTH + default LVGL_COLOR_DEPTH_1 +endchoice + +config LVGL_USE_CONT + default y + +## oled display configuration +if NRFMACRO_OLED_DISPLAY +config LVGL_HOR_RES_MAX + default 128 + +config LVGL_VER_RES_MAX + default 32 + +config LVGL_VDB_SIZE + default 64 + +config LVGL_DPI + default 148 +endif # NRFMACRO_OLED_DISPLAY + +## epd display configuration +if NRFMACRO_EPD_DISPLAY +config LVGL_HOR_RES_MAX + default 80 + +config LVGL_VER_RES_MAX + default 128 + +config LVGL_VDB_SIZE + default 100 + +config LVGL_DPI + default 145 +endif # NRFMACRO_EPD_DISPLAY + +## memory lcd display configuration +if NRFMACRO_LCD_DISPLAY +choice LVGL_THEME_DEFAULT_FONT_SMALL + default LVGL_THEME_DEFAULT_FONT_SMALL_MONTSERRAT_36 +endchoice + +config LVGL_HOR_RES_MAX + default 160 + +config LVGL_VER_RES_MAX + default 68 + +config LVGL_VDB_SIZE + default 16 + +config LVGL_DPI + default 30 +endif # NRFMACRO_LCD_DISPLAY + +endif # LVGL + +# custom widgets configuration +menuconfig CUSTOM_WIDGET_BATTERY_STATUS + bool "custom battery status widget" + +menuconfig CUSTOM_WIDGET_OUTPUT_STATUS + bool "custom output status widget" + +menuconfig CUSTOM_WIDGET_LAYER_STATUS + bool "custom layer status widget" + +menuconfig CUSTOM_WIDGET_PERIPHERAL_STATUS + bool "custom layer status widget" + +endif # BOARD_NRFMACRO diff --git a/app/boards/arm/nrfmacro/arduino_pro_micro_pins.dtsi b/app/boards/arm/nrfmacro/arduino_pro_micro_pins.dtsi new file mode 100644 index 00000000000..537aaed35b0 --- /dev/null +++ b/app/boards/arm/nrfmacro/arduino_pro_micro_pins.dtsi @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + + +/ { + pro_micro: connector { + compatible = "arduino-pro-micro"; + #gpio-cells = <2>; + gpio-map-mask = <0xffffffff 0xffffffc0>; + gpio-map-pass-thru = <0 0x3f>; + gpio-map + = <0 0 &gpio0 8 0> /* D0 */ + , <1 0 &gpio0 6 0> /* D1 */ + , <2 0 &gpio0 15 0> /* D2 */ + , <3 0 &gpio0 17 0> /* D3 */ + , <4 0 &gpio0 20 0> /* D4/A6 */ + , <5 0 &gpio0 13 0> /* D5 */ + , <6 0 &gpio0 24 0> /* D6/A7 */ + , <7 0 &gpio0 9 0> /* D7 */ + , <8 0 &gpio0 10 0> /* D8/A8 */ + , <9 0 &gpio1 6 0> /* D9/A9 */ + , <10 0 &gpio1 11 0> /* D10/A10 */ + , <16 0 &gpio0 28 0> /* D16 */ + , <14 0 &gpio0 3 0> /* D14 */ + , <15 0 &gpio1 13 0> /* D15 */ + , <18 0 &gpio0 2 0> /* D18/A0 */ + , <19 0 &gpio0 29 0> /* D19/A1 */ + , <20 0 &gpio0 31 0> /* D20/A2 */ + , <21 0 &gpio0 30 0> /* D21/A3 */ + ; + }; + + pro_micro_a: connector_a { + compatible = "arduino-pro-micro"; + #gpio-cells = <2>; + gpio-map-mask = <0xffffffff 0xffffffc0>; + gpio-map-pass-thru = <0 0x3f>; + gpio-map + = <0 0 &gpio0 2 0> /* D18/A0 */ + , <1 0 &gpio0 29 0> /* D19/A1 */ + , <2 0 &gpio0 31 0> /* D20/A2 */ + , <3 0 &gpio0 30 0> /* D21/A3 */ + , <6 0 &gpio0 20 0> /* D4/A6 */ + , <7 0 &gpio0 24 0> /* D6/A7 */ + , <8 0 &gpio0 10 0> /* D8/A8 */ + , <9 0 &gpio1 6 0> /* D9/A9 */ + , <10 0 &gpio1 11 0> /* D10/A10 */ + ; + }; +}; + + +pro_micro_d: &pro_micro {}; +pro_micro_i2c: &i2c0 {}; +pro_micro_spi: &spi0 {}; +pro_micro_serial: &uart0 {}; diff --git a/app/boards/arm/nrfmacro/board.cmake b/app/boards/arm/nrfmacro/board.cmake new file mode 100644 index 00000000000..b7feee2ee9e --- /dev/null +++ b/app/boards/arm/nrfmacro/board.cmake @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: MIT + +board_runner_args(nrfjprog "--nrf-family=NRF52" "--softreset") +include(${ZEPHYR_BASE}/boards/common/nrfjprog.board.cmake) diff --git a/app/boards/arm/nrfmacro/custom_status_screen.c b/app/boards/arm/nrfmacro/custom_status_screen.c new file mode 100644 index 00000000000..7347691d414 --- /dev/null +++ b/app/boards/arm/nrfmacro/custom_status_screen.c @@ -0,0 +1,72 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include "widgets/battery_status.h" +#include "widgets/output_status.h" +#include "widgets/layer_status.h" +#include "custom_status_screen.h" + +#include +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +LV_IMG_DECLARE(zenlogo); +LV_IMG_DECLARE(layers2); + +#if IS_ENABLED(CONFIG_CUSTOM_WIDGET_BATTERY_STATUS) +static struct zmk_widget_battery_status battery_status_widget; +#endif + +#if IS_ENABLED(CONFIG_CUSTOM_WIDGET_OUTPUT_STATUS) +static struct zmk_widget_output_status output_status_widget; +#endif + +#if IS_ENABLED(CONFIG_CUSTOM_WIDGET_LAYER_STATUS) +static struct zmk_widget_layer_status layer_status_widget; +#endif + +lv_obj_t *zmk_display_status_screen() { + + lv_obj_t *screen; + screen = lv_obj_create(NULL, NULL); + +#if IS_ENABLED(CONFIG_CUSTOM_WIDGET_BATTERY_STATUS) + zmk_widget_battery_status_init(&battery_status_widget, screen); + lv_obj_align(zmk_widget_battery_status_obj(&battery_status_widget), NULL, LV_ALIGN_IN_TOP_MID, 0, 2); +#endif + +#if IS_ENABLED(CONFIG_CUSTOM_WIDGET_OUTPUT_STATUS) + zmk_widget_output_status_init(&output_status_widget, screen); + lv_obj_align(zmk_widget_output_status_obj(&output_status_widget), NULL, LV_ALIGN_IN_TOP_MID, 0, 41); +#endif + +#if IS_ENABLED(CONFIG_CUSTOM_WIDGET_LAYER_STATUS) + //lv_style_set_pad_inner(&layerstyle, LV_STATE_DEFAULT, 12); + //lv_obj_add_style(&layer_status_widget, LV_WIDGET_PART_MAIN, &layerstyle); + zmk_widget_layer_status_init(&layer_status_widget, screen); + lv_obj_align(zmk_widget_layer_status_obj(&layer_status_widget), NULL, LV_ALIGN_IN_BOTTOM_MID, 0, -5); +#endif + +#if IS_ENABLED(CONFIG_NRFMACRO_SHIELD_SLAVE) + lv_obj_t * zenlogo_icon; + zenlogo_icon = lv_img_create(screen, NULL); + lv_img_set_src(zenlogo_icon, &zenlogo); + lv_obj_align(zenlogo_icon, NULL, LV_ALIGN_IN_BOTTOM_MID, 2, -5); +#endif + +#if IS_ENABLED(CONFIG_NRFMACRO_SHIELD_MASTER) + lv_obj_t * LayersHeading; + LayersHeading = lv_img_create(screen, NULL); + lv_obj_align(LayersHeading, NULL, LV_ALIGN_IN_BOTTOM_MID, 8, 5); + lv_img_set_src(LayersHeading, &layers2); +#endif + + //lv_task_handler(); + lv_refr_now(NULL); + //display_blanking_off(display_dev); + + return screen; +} diff --git a/app/boards/arm/nrfmacro/custom_status_screen.h b/app/boards/arm/nrfmacro/custom_status_screen.h new file mode 100644 index 00000000000..dc203fd1b49 --- /dev/null +++ b/app/boards/arm/nrfmacro/custom_status_screen.h @@ -0,0 +1,10 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#pragma once + +#include \ No newline at end of file diff --git a/app/boards/arm/nrfmacro/epd_screen/CMakeLists.txt b/app/boards/arm/nrfmacro/epd_screen/CMakeLists.txt new file mode 100644 index 00000000000..d5ca27e6b5a --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/CMakeLists.txt @@ -0,0 +1,7 @@ +# +# Copyright (c) 2021 Darryl deHaan +# SPDX-License-Identifier: MIT +# + +add_subdirectory_ifdef(CONFIG_NRFMACRO_SHIELD_MASTER master) +add_subdirectory_ifdef(CONFIG_NRFMACRO_SHIELD_SLAVE slave) diff --git a/app/boards/arm/nrfmacro/epd_screen/master/CMakeLists.txt b/app/boards/arm/nrfmacro/epd_screen/master/CMakeLists.txt new file mode 100644 index 00000000000..0d9748f1f3c --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/master/CMakeLists.txt @@ -0,0 +1,55 @@ +# +# Copyright (c) 2021 Darryl deHaan +# SPDX-License-Identifier: MIT +# + +# target_sources(app PRIVATE status_screen.c) +# target_sources_ifdef(CONFIG_CUSTOM_WIDGET_BATTERY_STATUS app PRIVATE battery_status.c) +# target_sources_ifdef(CONFIG_CUSTOM_WIDGET_OUTPUT_STATUS app PRIVATE output_status.c) +# target_sources_ifdef(CONFIG_CUSTOM_WIDGET_LAYER_STATUS app PRIVATE layer_status.c) + +zephyr_library() + +zephyr_library_include_directories(${ZEPHYR_LVGL_MODULE_DIR}) +zephyr_library_include_directories(${ZEPHYR_BASE}/lib/gui/lvgl/) +zephyr_library_include_directories(${CMAKE_SOURCE_DIR}/include) +zephyr_library_include_directories(${ZEPHYR_BASE}/drivers) + +zephyr_library_sources(status_screen.c) +zephyr_library_sources_ifdef(CONFIG_CUSTOM_WIDGET_BATTERY_STATUS battery_status.c) +zephyr_library_sources_ifdef(CONFIG_CUSTOM_WIDGET_OUTPUT_STATUS output_status.c) +zephyr_library_sources_ifdef(CONFIG_CUSTOM_WIDGET_LAYER_STATUS layer_status.c) + +if (CONFIG_CUSTOM_WIDGET_BATTERY_STATUS) + zephyr_library_sources(icons/batt_100.c) + zephyr_library_sources(icons/batt_100_chg.c) + zephyr_library_sources(icons/batt_75.c) + zephyr_library_sources(icons/batt_75_chg.c) + zephyr_library_sources(icons/batt_50.c) + zephyr_library_sources(icons/batt_50_chg.c) + zephyr_library_sources(icons/batt_25.c) + zephyr_library_sources(icons/batt_25_chg.c) + zephyr_library_sources(icons/batt_5.c) + zephyr_library_sources(icons/batt_5_chg.c) + zephyr_library_sources(icons/batt_0.c) + zephyr_library_sources(icons/batt_0_chg.c) +endif() + +if (CONFIG_CUSTOM_WIDGET_OUTPUT_STATUS) + zephyr_library_sources(icons/bluetooth_disconnected.c) + zephyr_library_sources(icons/USB_connected.c) + zephyr_library_sources(icons/bluetooth_connected_1.c) + zephyr_library_sources(icons/bluetooth_connected_2.c) + zephyr_library_sources(icons/bluetooth_connected_3.c) + zephyr_library_sources(icons/bluetooth_connected_4.c) + zephyr_library_sources(icons/bluetooth_connected_5.c) + zephyr_library_sources(icons/bluetooth_advertising_1.c) + zephyr_library_sources(icons/bluetooth_advertising_2.c) + zephyr_library_sources(icons/bluetooth_advertising_3.c) + zephyr_library_sources(icons/bluetooth_advertising_4.c) + zephyr_library_sources(icons/bluetooth_advertising_5.c) +endif() + +if (CONFIG_CUSTOM_WIDGET_LAYER_STATUS) + zephyr_library_sources(icons/layers.c) +endif() diff --git a/app/boards/arm/nrfmacro/epd_screen/master/battery_status.c b/app/boards/arm/nrfmacro/epd_screen/master/battery_status.c new file mode 100644 index 00000000000..6e8a5282f07 --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/master/battery_status.c @@ -0,0 +1,165 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include +#include + +#include +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#include +#include "battery_status.h" +#include +#include +#include +#include +#include + +static sys_slist_t widgets = SYS_SLIST_STATIC_INIT(&widgets); +static lv_style_t label_style; + +LV_IMG_DECLARE(batt_100); +LV_IMG_DECLARE(batt_100_chg); +LV_IMG_DECLARE(batt_75); +LV_IMG_DECLARE(batt_75_chg); +LV_IMG_DECLARE(batt_50); +LV_IMG_DECLARE(batt_50_chg); +LV_IMG_DECLARE(batt_25); +LV_IMG_DECLARE(batt_25_chg); +LV_IMG_DECLARE(batt_5); +LV_IMG_DECLARE(batt_5_chg); +LV_IMG_DECLARE(batt_0); +LV_IMG_DECLARE(batt_0_chg); + +static bool style_initialized = false; + +void battery_status_init() { + + if (style_initialized) { + return; + } + + style_initialized = true; + lv_style_init(&label_style); + lv_style_set_text_font(&label_style, LV_STATE_DEFAULT, &lv_font_montserrat_26); + lv_style_set_text_letter_space(&label_style, LV_STATE_DEFAULT, 1); + lv_style_set_text_line_space(&label_style, LV_STATE_DEFAULT, 1); + lv_style_set_text_color(&label_style, LV_STATE_DEFAULT, LV_COLOR_BLACK); + lv_style_set_bg_color(&label_style, LV_STATE_DEFAULT, LV_COLOR_WHITE); + + //lv_obj_t * batt_full_chg_icon = lv_img_create(lv_scr_act(), NULL); + //lv_img_set_src(batt_full_chg_icon, &batt_full_chg); +} + +K_MUTEX_DEFINE(battery_status_mutex); + +struct { + uint8_t level; +#if IS_ENABLED(CONFIG_USB_DEVICE_STACK) + bool usb_present; +#endif +} battery_status_state; + +void set_battery_symbol(lv_obj_t *icon) { + + k_mutex_lock(&battery_status_mutex, K_FOREVER); + + uint8_t level = battery_status_state.level; + +#if IS_ENABLED(CONFIG_USB_DEVICE_STACK) + if (level > 95) { + if (battery_status_state.usb_present) { + lv_img_set_src(icon, &batt_100_chg); + }else{ + lv_img_set_src(icon, &batt_100); + } + } else if (level > 74) { + if (battery_status_state.usb_present) { + lv_img_set_src(icon, &batt_75_chg); + }else{ + lv_img_set_src(icon, &batt_75); + } + } else if (level > 49) { + if (battery_status_state.usb_present) { + lv_img_set_src(icon, &batt_50_chg); + }else{ + lv_img_set_src(icon, &batt_50); + } + } else if (level > 24) { + if (battery_status_state.usb_present) { + lv_img_set_src(icon, &batt_25_chg); + }else{ + lv_img_set_src(icon, &batt_25); + } + } else if (level > 5) { + if (battery_status_state.usb_present) { + lv_img_set_src(icon, &batt_5_chg); + }else{ + lv_img_set_src(icon, &batt_5); + } + } else { + if (battery_status_state.usb_present) { + lv_img_set_src(icon, &batt_0_chg); + }else{ + lv_img_set_src(icon, &batt_0); + } + } + //lv_label_set_text(label, text); + //lv_img_set_src(icon, ); + +#endif /* IS_ENABLED(CONFIG_USB_DEVICE_STACK) */ + + k_mutex_unlock(&battery_status_mutex); + +} + +int zmk_widget_battery_status_init(struct zmk_widget_battery_status *widget, lv_obj_t *parent) { + battery_status_init(); + //widget->obj = lv_label_create(parent, NULL); + widget->obj = lv_img_create(parent, NULL); + //widget->obj2 = lv_label_create(parent, NULL); + lv_obj_add_style(widget->obj, LV_LABEL_PART_MAIN, &label_style); + + //lv_obj_set_size(widget->obj, 40, 15); + set_battery_symbol(widget->obj); + + sys_slist_append(&widgets, &widget->node); + + return 0; +} + +lv_obj_t *zmk_widget_battery_status_obj(struct zmk_widget_battery_status *widget) { + return widget->obj; +} + +void battery_status_update_cb(struct k_work *work) { + struct zmk_widget_battery_status *widget; + SYS_SLIST_FOR_EACH_CONTAINER(&widgets, widget, node) { set_battery_symbol(widget->obj); } +} + +K_WORK_DEFINE(battery_status_update_work, battery_status_update_cb); + +int battery_status_listener(const zmk_event_t *eh) { + k_mutex_lock(&battery_status_mutex, K_FOREVER); + + battery_status_state.level = bt_bas_get_battery_level(); + +#if IS_ENABLED(CONFIG_USB_DEVICE_STACK) + battery_status_state.usb_present = zmk_usb_is_powered(); +#endif /* IS_ENABLED(CONFIG_USB_DEVICE_STACK) */ + + k_mutex_unlock(&battery_status_mutex); + + k_work_submit_to_queue(zmk_display_work_q(), &battery_status_update_work); + return ZMK_EV_EVENT_BUBBLE; +} + +ZMK_LISTENER(widget_battery_status, battery_status_listener) +ZMK_SUBSCRIPTION(widget_battery_status, zmk_battery_state_changed); +#if IS_ENABLED(CONFIG_USB_DEVICE_STACK) +ZMK_SUBSCRIPTION(widget_battery_status, zmk_usb_conn_state_changed); +#endif /* IS_ENABLED(CONFIG_USB_DEVICE_STACK) */ diff --git a/app/boards/arm/nrfmacro/epd_screen/master/battery_status.h b/app/boards/arm/nrfmacro/epd_screen/master/battery_status.h new file mode 100644 index 00000000000..c2ad4bf4094 --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/master/battery_status.h @@ -0,0 +1,20 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#pragma once + +#include + +#include + +struct zmk_widget_battery_status { + sys_snode_t node; + lv_obj_t *obj; +}; + +int zmk_widget_battery_status_init(struct zmk_widget_battery_status *widget, lv_obj_t *parent); +lv_obj_t *zmk_widget_battery_status_obj(struct zmk_widget_battery_status *widget); \ No newline at end of file diff --git a/app/boards/arm/nrfmacro/epd_screen/master/icons/CMakeLists.txt b/app/boards/arm/nrfmacro/epd_screen/master/icons/CMakeLists.txt new file mode 100644 index 00000000000..19af8ead415 --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/master/icons/CMakeLists.txt @@ -0,0 +1,4 @@ +# +# Copyright (c) 2021 Darryl deHaan +# SPDX-License-Identifier: MIT +# \ No newline at end of file diff --git a/app/boards/arm/nrfmacro/epd_screen/master/icons/USB_connected.c b/app/boards/arm/nrfmacro/epd_screen/master/icons/USB_connected.c new file mode 100644 index 00000000000..8cf0badc38a --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/master/icons/USB_connected.c @@ -0,0 +1,63 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_USB_CONNECTED +#define LV_ATTRIBUTE_IMG_USB_CONNECTED +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_IMG_USB_CONNECTED uint8_t USB_connected_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0f, 0xff, 0xff, 0xc0, + 0x00, 0x3f, 0xff, 0xff, 0xc0, + 0x00, 0xff, 0xff, 0xff, 0xc0, + 0x01, 0xff, 0xff, 0xff, 0x80, + 0x03, 0xff, 0xff, 0xff, 0xfe, + 0x07, 0xff, 0xff, 0xff, 0xff, + 0x07, 0x1e, 0x30, 0x38, 0x07, + 0x0f, 0x1c, 0x20, 0x38, 0x07, + 0x0f, 0x1c, 0x47, 0x10, 0xc3, + 0x3e, 0x1c, 0x43, 0xf1, 0xc7, + 0x7e, 0x3c, 0x60, 0x70, 0x0e, + 0x7e, 0x3c, 0x70, 0x30, 0x0e, + 0x7e, 0x38, 0xfc, 0x33, 0xc7, + 0xfe, 0x18, 0x8f, 0x23, 0x87, + 0x0e, 0x00, 0xc6, 0x20, 0x07, + 0x0f, 0x01, 0xe0, 0x60, 0x0e, + 0x0f, 0x87, 0xf0, 0xe0, 0x3e, + 0x07, 0xff, 0xff, 0xff, 0xfc, + 0x07, 0xff, 0xff, 0xff, 0xf0, + 0x03, 0xff, 0xff, 0xfe, 0x00, + 0x01, 0xff, 0xff, 0xfc, 0x00, + 0x00, 0x7f, 0xff, 0xfc, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t USB_connected = { + .header.always_zero = 0, + .header.w = 40, + .header.h = 31, + .data_size = 164, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = USB_connected_map, +}; diff --git a/app/boards/arm/nrfmacro/epd_screen/master/icons/batt_0.c b/app/boards/arm/nrfmacro/epd_screen/master/icons/batt_0.c new file mode 100644 index 00000000000..d061f1d87bf --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/master/icons/batt_0.c @@ -0,0 +1,65 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_BATT_0 +#define LV_ATTRIBUTE_IMG_BATT_0 +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BATT_0 uint8_t batt_0_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x00, 0x7c, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x01, 0xff, 0x00, 0x00, + 0x00, 0x01, 0xff, 0x00, 0x00, + 0x7f, 0xfd, 0xff, 0x7f, 0xf8, + 0xff, 0xfd, 0xff, 0x7f, 0xfc, + 0xff, 0xfd, 0xff, 0x7f, 0xfc, + 0xff, 0xfc, 0xfe, 0x7f, 0xfc, + 0xf0, 0x00, 0xfe, 0x00, 0x3f, + 0xf0, 0x00, 0xfe, 0x00, 0x3f, + 0xf0, 0x00, 0xfe, 0x00, 0x3f, + 0xf0, 0x00, 0xfe, 0x00, 0x3f, + 0xf0, 0x00, 0xfe, 0x00, 0x0f, + 0xf0, 0x00, 0x7c, 0x00, 0x0f, + 0xf0, 0x00, 0x7c, 0x00, 0x0f, + 0xf0, 0x00, 0x7c, 0x00, 0x0f, + 0xf0, 0x00, 0x7c, 0x00, 0x3f, + 0xf0, 0x00, 0x7c, 0x00, 0x3f, + 0xf0, 0x00, 0x7c, 0x00, 0x3f, + 0xf0, 0x00, 0x00, 0x00, 0x3f, + 0xff, 0xff, 0x01, 0xff, 0xfc, + 0xff, 0xff, 0x01, 0xff, 0xfc, + 0xff, 0xfe, 0x7c, 0xff, 0xfc, + 0x7f, 0xfc, 0xfe, 0x7f, 0xf8, + 0x00, 0x01, 0xff, 0x00, 0x00, + 0x00, 0x01, 0xff, 0x00, 0x00, + 0x00, 0x01, 0xff, 0x00, 0x00, + 0x00, 0x01, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0x00, 0x00, +}; + +const lv_img_dsc_t batt_0 = { + .header.always_zero = 0, + .header.w = 40, + .header.h = 31, + .data_size = 163, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = batt_0_map, +}; + diff --git a/app/boards/arm/nrfmacro/epd_screen/master/icons/batt_0_chg.c b/app/boards/arm/nrfmacro/epd_screen/master/icons/batt_0_chg.c new file mode 100644 index 00000000000..e05e1524f09 --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/master/icons/batt_0_chg.c @@ -0,0 +1,64 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_BATT_0_CHG +#define LV_ATTRIBUTE_IMG_BATT_0_CHG +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BATT_0_CHG uint8_t batt_0_chg_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x00, 0x00, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x00, 0x00, 0xc0, 0x00, + 0x00, 0x00, 0x01, 0xc0, 0x00, + 0x00, 0x00, 0x03, 0x80, 0x00, + 0x7f, 0xff, 0xf7, 0xbf, 0xf8, + 0xff, 0xff, 0xef, 0xbf, 0xfc, + 0xff, 0xff, 0xdf, 0x7f, 0xfc, + 0xff, 0xff, 0xbf, 0x7f, 0xfc, + 0xf0, 0x00, 0x7e, 0x00, 0x3f, + 0xf0, 0x00, 0xfe, 0x00, 0x3f, + 0xf0, 0x01, 0xfe, 0x00, 0x3f, + 0xf0, 0x03, 0xfc, 0x00, 0x3f, + 0xf0, 0x07, 0xfc, 0x00, 0x0f, + 0xf0, 0x0f, 0xff, 0xe0, 0x0f, + 0xf0, 0x1f, 0xff, 0xc0, 0x0f, + 0xf0, 0x00, 0x7f, 0x80, 0x0f, + 0xf0, 0x00, 0x7f, 0x00, 0x3f, + 0xf0, 0x00, 0xfe, 0x00, 0x3f, + 0xf0, 0x00, 0xfc, 0x00, 0x3f, + 0xf0, 0x01, 0xf8, 0x00, 0x3f, + 0xff, 0xfd, 0xf7, 0xff, 0xfc, + 0xff, 0xfb, 0xef, 0xff, 0xfc, + 0xff, 0xfb, 0xdf, 0xff, 0xfc, + 0x7f, 0xfb, 0xbf, 0xff, 0xf8, + 0x00, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t batt_0_chg = { + .header.always_zero = 0, + .header.w = 40, + .header.h = 31, + .data_size = 163, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = batt_0_chg_map, +}; + diff --git a/app/boards/arm/nrfmacro/epd_screen/master/icons/batt_100.c b/app/boards/arm/nrfmacro/epd_screen/master/icons/batt_100.c new file mode 100644 index 00000000000..915472c3e2b --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/master/icons/batt_100.c @@ -0,0 +1,64 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_BATT_100 +#define LV_ATTRIBUTE_IMG_BATT_100 +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BATT_100 uint8_t batt_100_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0xff, 0xff, 0xff, 0xf8, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xf0, 0x00, 0x00, 0x00, 0x3f, + 0xf0, 0x00, 0x00, 0x00, 0x3f, + 0xf3, 0xff, 0xff, 0xff, 0x3f, + 0xf3, 0xff, 0xff, 0xff, 0x3f, + 0xf3, 0xff, 0xff, 0xff, 0x0f, + 0xf3, 0xff, 0xff, 0xff, 0x0f, + 0xf3, 0xff, 0xff, 0xff, 0x0f, + 0xf3, 0xff, 0xff, 0xff, 0x0f, + 0xf3, 0xff, 0xff, 0xff, 0x3f, + 0xf3, 0xff, 0xff, 0xff, 0x3f, + 0xf0, 0x00, 0x00, 0x00, 0x3f, + 0xf0, 0x00, 0x00, 0x00, 0x3f, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0x7f, 0xff, 0xff, 0xff, 0xf8, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t batt_100 = { + .header.always_zero = 0, + .header.w = 40, + .header.h = 31, + .data_size = 163, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = batt_100_map, +}; + diff --git a/app/boards/arm/nrfmacro/epd_screen/master/icons/batt_100_chg.c b/app/boards/arm/nrfmacro/epd_screen/master/icons/batt_100_chg.c new file mode 100644 index 00000000000..9ad05d61c76 --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/master/icons/batt_100_chg.c @@ -0,0 +1,65 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_BATT_100_CHG +#define LV_ATTRIBUTE_IMG_BATT_100_CHG +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BATT_100_CHG uint8_t batt_100_chg_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x00, 0x00, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x00, 0x00, 0xc0, 0x00, + 0x00, 0x00, 0x01, 0xc0, 0x00, + 0x00, 0x00, 0x03, 0x80, 0x00, + 0x7f, 0xff, 0xf7, 0xbf, 0xf8, + 0xff, 0xff, 0xef, 0xbf, 0xfc, + 0xff, 0xff, 0xdf, 0x7f, 0xfc, + 0xff, 0xff, 0xbf, 0x7f, 0xfc, + 0xf0, 0x00, 0x7e, 0x00, 0x3f, + 0xf0, 0x00, 0xfe, 0x00, 0x3f, + 0xf3, 0xfd, 0xfe, 0xff, 0x3f, + 0xf3, 0xfb, 0xfd, 0xff, 0x3f, + 0xf3, 0xf7, 0xfc, 0x07, 0x0f, + 0xf3, 0xef, 0xff, 0xef, 0x0f, + 0xf3, 0xdf, 0xff, 0xdf, 0x0f, + 0xf3, 0x80, 0x7f, 0xbf, 0x0f, + 0xf3, 0xff, 0x7f, 0x7f, 0x3f, + 0xf3, 0xfe, 0xfe, 0xff, 0x3f, + 0xf0, 0x00, 0xfc, 0x00, 0x3f, + 0xf0, 0x01, 0xf8, 0x00, 0x3f, + 0xff, 0xfd, 0xf7, 0xff, 0xfc, + 0xff, 0xfb, 0xef, 0xff, 0xfc, + 0xff, 0xfb, 0xdf, 0xff, 0xfc, + 0x7f, 0xfb, 0xbf, 0xff, 0xf8, + 0x00, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t batt_100_chg = { + .header.always_zero = 0, + .header.w = 40, + .header.h = 31, + .data_size = 163, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = batt_100_chg_map, +}; + diff --git a/app/boards/arm/nrfmacro/epd_screen/master/icons/batt_25.c b/app/boards/arm/nrfmacro/epd_screen/master/icons/batt_25.c new file mode 100644 index 00000000000..1dba62c2f6c --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/master/icons/batt_25.c @@ -0,0 +1,64 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_BATT_25 +#define LV_ATTRIBUTE_IMG_BATT_25 +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BATT_25 uint8_t batt_25_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0xff, 0xff, 0xff, 0xf8, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xf0, 0x00, 0x00, 0x00, 0x3f, + 0xf0, 0x00, 0x00, 0x00, 0x3f, + 0xf3, 0xfc, 0x00, 0x00, 0x3f, + 0xf3, 0xfc, 0x00, 0x00, 0x3f, + 0xf3, 0xfc, 0x00, 0x00, 0x0f, + 0xf3, 0xfc, 0x00, 0x00, 0x0f, + 0xf3, 0xfc, 0x00, 0x00, 0x0f, + 0xf3, 0xfc, 0x00, 0x00, 0x0f, + 0xf3, 0xfc, 0x00, 0x00, 0x3f, + 0xf3, 0xfc, 0x00, 0x00, 0x3f, + 0xf0, 0x00, 0x00, 0x00, 0x3f, + 0xf0, 0x00, 0x00, 0x00, 0x3f, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0x7f, 0xff, 0xff, 0xff, 0xf8, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t batt_25 = { + .header.always_zero = 0, + .header.w = 40, + .header.h = 31, + .data_size = 163, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = batt_25_map, +}; + diff --git a/app/boards/arm/nrfmacro/epd_screen/master/icons/batt_25_chg.c b/app/boards/arm/nrfmacro/epd_screen/master/icons/batt_25_chg.c new file mode 100644 index 00000000000..544e71db7c5 --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/master/icons/batt_25_chg.c @@ -0,0 +1,64 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_BATT_25_CHG +#define LV_ATTRIBUTE_IMG_BATT_25_CHG +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BATT_25_CHG uint8_t batt_25_chg_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x00, 0x00, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x00, 0x00, 0xc0, 0x00, + 0x00, 0x00, 0x01, 0xc0, 0x00, + 0x00, 0x00, 0x03, 0x80, 0x00, + 0x7f, 0xff, 0xf7, 0xbf, 0xf8, + 0xff, 0xff, 0xef, 0xbf, 0xfc, + 0xff, 0xff, 0xdf, 0x7f, 0xfc, + 0xff, 0xff, 0xbf, 0x7f, 0xfc, + 0xf0, 0x00, 0x7e, 0x00, 0x3f, + 0xf0, 0x00, 0xfe, 0x00, 0x3f, + 0xf3, 0xf9, 0xfe, 0x00, 0x3f, + 0xf3, 0xfb, 0xfc, 0x00, 0x3f, + 0xf3, 0xf7, 0xfc, 0x00, 0x0f, + 0xf3, 0xef, 0xff, 0xe0, 0x0f, + 0xf3, 0xdf, 0xff, 0xc0, 0x0f, + 0xf3, 0x80, 0x7f, 0x80, 0x0f, + 0xf3, 0xf8, 0x7f, 0x00, 0x3f, + 0xf3, 0xf8, 0xfe, 0x00, 0x3f, + 0xf0, 0x00, 0xfc, 0x00, 0x3f, + 0xf0, 0x01, 0xf8, 0x00, 0x3f, + 0xff, 0xfd, 0xf7, 0xff, 0xfc, + 0xff, 0xfb, 0xef, 0xff, 0xfc, + 0xff, 0xfb, 0xdf, 0xff, 0xfc, + 0x7f, 0xfb, 0xbf, 0xff, 0xf8, + 0x00, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t batt_25_chg = { + .header.always_zero = 0, + .header.w = 40, + .header.h = 31, + .data_size = 163, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = batt_25_chg_map, +}; + diff --git a/app/boards/arm/nrfmacro/epd_screen/master/icons/batt_5.c b/app/boards/arm/nrfmacro/epd_screen/master/icons/batt_5.c new file mode 100644 index 00000000000..a8c5ded9289 --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/master/icons/batt_5.c @@ -0,0 +1,64 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_BATT_5 +#define LV_ATTRIBUTE_IMG_BATT_5 +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BATT_5 uint8_t batt_5_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0xff, 0xff, 0xff, 0xf8, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xf0, 0x00, 0x00, 0x00, 0x3f, + 0xf0, 0x00, 0x00, 0x00, 0x3f, + 0xf3, 0x00, 0x00, 0x00, 0x3f, + 0xf3, 0x00, 0x00, 0x00, 0x3f, + 0xf3, 0x00, 0x00, 0x00, 0x0f, + 0xf3, 0x00, 0x00, 0x00, 0x0f, + 0xf3, 0x00, 0x00, 0x00, 0x0f, + 0xf3, 0x00, 0x00, 0x00, 0x0f, + 0xf3, 0x00, 0x00, 0x00, 0x3f, + 0xf3, 0x00, 0x00, 0x00, 0x3f, + 0xf0, 0x00, 0x00, 0x00, 0x3f, + 0xf0, 0x00, 0x00, 0x00, 0x3f, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0x7f, 0xff, 0xff, 0xff, 0xf8, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t batt_5 = { + .header.always_zero = 0, + .header.w = 40, + .header.h = 31, + .data_size = 163, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = batt_5_map, +}; + diff --git a/app/boards/arm/nrfmacro/epd_screen/master/icons/batt_50.c b/app/boards/arm/nrfmacro/epd_screen/master/icons/batt_50.c new file mode 100644 index 00000000000..3c93ae5bc71 --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/master/icons/batt_50.c @@ -0,0 +1,64 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_BATT_50 +#define LV_ATTRIBUTE_IMG_BATT_50 +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BATT_50 uint8_t batt_50_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0xff, 0xff, 0xff, 0xf8, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xf0, 0x00, 0x00, 0x00, 0x3f, + 0xf0, 0x00, 0x00, 0x00, 0x3f, + 0xf3, 0xff, 0xf0, 0x00, 0x3f, + 0xf3, 0xff, 0xf0, 0x00, 0x3f, + 0xf3, 0xff, 0xf0, 0x00, 0x0f, + 0xf3, 0xff, 0xf0, 0x00, 0x0f, + 0xf3, 0xff, 0xf0, 0x00, 0x0f, + 0xf3, 0xff, 0xf0, 0x00, 0x0f, + 0xf3, 0xff, 0xf0, 0x00, 0x3f, + 0xf3, 0xff, 0xf0, 0x00, 0x3f, + 0xf0, 0x00, 0x00, 0x00, 0x3f, + 0xf0, 0x00, 0x00, 0x00, 0x3f, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0x7f, 0xff, 0xff, 0xff, 0xf8, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t batt_50 = { + .header.always_zero = 0, + .header.w = 40, + .header.h = 31, + .data_size = 163, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = batt_50_map, +}; + diff --git a/app/boards/arm/nrfmacro/epd_screen/master/icons/batt_50_chg.c b/app/boards/arm/nrfmacro/epd_screen/master/icons/batt_50_chg.c new file mode 100644 index 00000000000..b21b4e25df3 --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/master/icons/batt_50_chg.c @@ -0,0 +1,64 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_BATT_50_CHG +#define LV_ATTRIBUTE_IMG_BATT_50_CHG +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BATT_50_CHG uint8_t batt_50_chg_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x00, 0x00, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x00, 0x00, 0xc0, 0x00, + 0x00, 0x00, 0x01, 0xc0, 0x00, + 0x00, 0x00, 0x03, 0x80, 0x00, + 0x7f, 0xff, 0xf7, 0xbf, 0xf8, + 0xff, 0xff, 0xef, 0xbf, 0xfc, + 0xff, 0xff, 0xdf, 0x7f, 0xfc, + 0xff, 0xff, 0xbf, 0x7f, 0xfc, + 0xf0, 0x00, 0x7e, 0x00, 0x3f, + 0xf0, 0x00, 0xfe, 0x00, 0x3f, + 0xf3, 0xfd, 0xfe, 0x00, 0x3f, + 0xf3, 0xfb, 0xfc, 0x00, 0x3f, + 0xf3, 0xf7, 0xfc, 0x00, 0x0f, + 0xf3, 0xef, 0xff, 0xe0, 0x0f, + 0xf3, 0xdf, 0xff, 0xc0, 0x0f, + 0xf3, 0x80, 0x7f, 0x80, 0x0f, + 0xf3, 0xff, 0x7f, 0x00, 0x3f, + 0xf3, 0xfe, 0xfe, 0x00, 0x3f, + 0xf0, 0x00, 0xfc, 0x00, 0x3f, + 0xf0, 0x01, 0xf8, 0x00, 0x3f, + 0xff, 0xfd, 0xf7, 0xff, 0xfc, + 0xff, 0xfb, 0xef, 0xff, 0xfc, + 0xff, 0xfb, 0xdf, 0xff, 0xfc, + 0x7f, 0xfb, 0xbf, 0xff, 0xf8, + 0x00, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t batt_50_chg = { + .header.always_zero = 0, + .header.w = 40, + .header.h = 31, + .data_size = 163, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = batt_50_chg_map, +}; + diff --git a/app/boards/arm/nrfmacro/epd_screen/master/icons/batt_5_chg.c b/app/boards/arm/nrfmacro/epd_screen/master/icons/batt_5_chg.c new file mode 100644 index 00000000000..da96fc4da1b --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/master/icons/batt_5_chg.c @@ -0,0 +1,64 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_BATT_5_CHG +#define LV_ATTRIBUTE_IMG_BATT_5_CHG +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BATT_5_CHG uint8_t batt_5_chg_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x00, 0x00, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x00, 0x00, 0xc0, 0x00, + 0x00, 0x00, 0x01, 0xc0, 0x00, + 0x00, 0x00, 0x03, 0x80, 0x00, + 0x7f, 0xff, 0xf7, 0xbf, 0xf8, + 0xff, 0xff, 0xef, 0xbf, 0xfc, + 0xff, 0xff, 0xdf, 0x7f, 0xfc, + 0xff, 0xff, 0xbf, 0x7f, 0xfc, + 0xf0, 0x00, 0x7e, 0x00, 0x3f, + 0xf0, 0x00, 0xfe, 0x00, 0x3f, + 0xf3, 0x01, 0xfe, 0x00, 0x3f, + 0xf3, 0x03, 0xfc, 0x00, 0x3f, + 0xf3, 0x07, 0xfc, 0x00, 0x0f, + 0xf3, 0x0f, 0xff, 0xe0, 0x0f, + 0xf3, 0x1f, 0xff, 0xc0, 0x0f, + 0xf3, 0x00, 0x7f, 0x80, 0x0f, + 0xf3, 0x00, 0x7f, 0x00, 0x3f, + 0xf3, 0x00, 0xfe, 0x00, 0x3f, + 0xf0, 0x00, 0xfc, 0x00, 0x3f, + 0xf0, 0x01, 0xf8, 0x00, 0x3f, + 0xff, 0xfd, 0xf7, 0xff, 0xfc, + 0xff, 0xfb, 0xef, 0xff, 0xfc, + 0xff, 0xfb, 0xdf, 0xff, 0xfc, + 0x7f, 0xfb, 0xbf, 0xff, 0xf8, + 0x00, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t batt_5_chg = { + .header.always_zero = 0, + .header.w = 40, + .header.h = 31, + .data_size = 163, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = batt_5_chg_map, +}; + diff --git a/app/boards/arm/nrfmacro/epd_screen/master/icons/batt_75.c b/app/boards/arm/nrfmacro/epd_screen/master/icons/batt_75.c new file mode 100644 index 00000000000..cbba208ef88 --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/master/icons/batt_75.c @@ -0,0 +1,64 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_BATT_75 +#define LV_ATTRIBUTE_IMG_BATT_75 +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BATT_75 uint8_t batt_75_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0xff, 0xff, 0xff, 0xf8, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xf0, 0x00, 0x00, 0x00, 0x3f, + 0xf0, 0x00, 0x00, 0x00, 0x3f, + 0xf3, 0xff, 0xff, 0xe0, 0x3f, + 0xf3, 0xff, 0xff, 0xe0, 0x3f, + 0xf3, 0xff, 0xff, 0xe0, 0x0f, + 0xf3, 0xff, 0xff, 0xe0, 0x0f, + 0xf3, 0xff, 0xff, 0xe0, 0x0f, + 0xf3, 0xff, 0xff, 0xe0, 0x0f, + 0xf3, 0xff, 0xff, 0xe0, 0x3f, + 0xf3, 0xff, 0xff, 0xe0, 0x3f, + 0xf0, 0x00, 0x00, 0x00, 0x3f, + 0xf0, 0x00, 0x00, 0x00, 0x3f, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0x7f, 0xff, 0xff, 0xff, 0xf8, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t batt_75 = { + .header.always_zero = 0, + .header.w = 40, + .header.h = 31, + .data_size = 163, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = batt_75_map, +}; + diff --git a/app/boards/arm/nrfmacro/epd_screen/master/icons/batt_75_chg.c b/app/boards/arm/nrfmacro/epd_screen/master/icons/batt_75_chg.c new file mode 100644 index 00000000000..b2c2298d3b2 --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/master/icons/batt_75_chg.c @@ -0,0 +1,64 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_BATT_75_CHG +#define LV_ATTRIBUTE_IMG_BATT_75_CHG +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BATT_75_CHG uint8_t batt_75_chg_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x00, 0x00, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x00, 0x00, 0xc0, 0x00, + 0x00, 0x00, 0x01, 0xc0, 0x00, + 0x00, 0x00, 0x03, 0x80, 0x00, + 0x7f, 0xff, 0xf7, 0xbf, 0xf8, + 0xff, 0xff, 0xef, 0xbf, 0xfc, + 0xff, 0xff, 0xdf, 0x7f, 0xfc, + 0xff, 0xff, 0xbf, 0x7f, 0xfc, + 0xf0, 0x00, 0x7e, 0x00, 0x3f, + 0xf0, 0x00, 0xfe, 0x00, 0x3f, + 0xf3, 0xfd, 0xfe, 0xe0, 0x3f, + 0xf3, 0xfb, 0xfd, 0xe0, 0x3f, + 0xf3, 0xf7, 0xfc, 0x00, 0x0f, + 0xf3, 0xef, 0xff, 0xe0, 0x0f, + 0xf3, 0xdf, 0xff, 0xc0, 0x0f, + 0xf3, 0x80, 0x7f, 0xa0, 0x0f, + 0xf3, 0xff, 0x7f, 0x60, 0x3f, + 0xf3, 0xfe, 0xfe, 0xe0, 0x3f, + 0xf0, 0x00, 0xfc, 0x00, 0x3f, + 0xf0, 0x01, 0xf8, 0x00, 0x3f, + 0xff, 0xfd, 0xf7, 0xff, 0xfc, + 0xff, 0xfb, 0xef, 0xff, 0xfc, + 0xff, 0xfb, 0xdf, 0xff, 0xfc, + 0x7f, 0xfb, 0xbf, 0xff, 0xf8, + 0x00, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t batt_75_chg = { + .header.always_zero = 0, + .header.w = 40, + .header.h = 31, + .data_size = 163, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = batt_75_chg_map, +}; + diff --git a/app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_advertising.c b/app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_advertising.c new file mode 100644 index 00000000000..89809d1ffbf --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_advertising.c @@ -0,0 +1,66 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_BLUETOOTH_ADVERTISING +#define LV_ATTRIBUTE_IMG_BLUETOOTH_ADVERTISING +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BLUETOOTH_ADVERTISING uint8_t bluetooth_advertising_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x40, 0x00, 0x00, + 0x00, 0x60, 0x00, 0x00, + 0x00, 0x70, 0x00, 0x00, + 0x00, 0x78, 0x00, 0x00, + 0x00, 0x7c, 0x00, 0x00, + 0x00, 0x7e, 0x00, 0x00, + 0x00, 0x7f, 0x00, 0x00, + 0x20, 0x7f, 0x80, 0x00, + 0x70, 0x77, 0xc0, 0x00, + 0xf8, 0x73, 0xe0, 0xc0, + 0x7c, 0x71, 0xe0, 0xe0, + 0x3e, 0x73, 0xc0, 0x70, + 0x1f, 0x77, 0x86, 0x30, + 0x0f, 0xff, 0x07, 0x30, + 0x07, 0xfe, 0x07, 0x38, + 0x03, 0xfc, 0x23, 0x38, + 0x01, 0xf8, 0x63, 0x18, + 0x01, 0xf8, 0x63, 0x18, + 0x03, 0xfc, 0x23, 0x38, + 0x07, 0xfe, 0x07, 0x38, + 0x0f, 0xff, 0x07, 0x30, + 0x1f, 0x77, 0x86, 0x70, + 0x3e, 0x73, 0xc0, 0x70, + 0x7c, 0x71, 0xe0, 0xe0, + 0xf8, 0x73, 0xe0, 0xc0, + 0x70, 0x77, 0xc0, 0x00, + 0x20, 0x7f, 0x80, 0x00, + 0x00, 0x7f, 0x00, 0x00, + 0x00, 0x7e, 0x00, 0x00, + 0x00, 0x7c, 0x00, 0x00, + 0x00, 0x78, 0x00, 0x00, + 0x00, 0x70, 0x00, 0x00, + 0x00, 0x60, 0x00, 0x00, + 0x00, 0x40, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t bluetooth_advertising = { + .header.always_zero = 0, + .header.w = 29, + .header.h = 35, + .data_size = 148, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = bluetooth_advertising_map, +}; diff --git a/app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_advertising_1.c b/app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_advertising_1.c new file mode 100644 index 00000000000..9fdc8e55a47 --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_advertising_1.c @@ -0,0 +1,64 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif +#ifndef LV_ATTRIBUTE_IMG_BLUETOOTH_ADVERTISING_1 +#define LV_ATTRIBUTE_IMG_BLUETOOTH_ADVERTISING_1 +#endif +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_IMG_BLUETOOTH_ADVERTISING_1 uint8_t bluetooth_advertising_1_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xfc, 0x00, 0x00, 0x01, 0xfc, 0x00, + 0x00, 0xfe, 0x00, 0x00, 0x0f, 0xff, 0x00, + 0x20, 0xff, 0x00, 0x00, 0x1f, 0xff, 0xc0, + 0x70, 0xf7, 0x80, 0x00, 0x3f, 0xff, 0xe0, + 0xf8, 0xf3, 0xc1, 0xc0, 0x7f, 0xff, 0xe0, + 0x7c, 0xf3, 0xe0, 0xc0, 0xff, 0xff, 0xf0, + 0x3e, 0xf7, 0xc0, 0xe0, 0xff, 0x8f, 0xf8, + 0x1f, 0xff, 0x84, 0x61, 0xfe, 0x0f, 0xf8, + 0x0f, 0xff, 0x0e, 0x71, 0xfe, 0x0f, 0xf8, + 0x07, 0xfe, 0x06, 0x31, 0xff, 0xcf, 0xfc, + 0x03, 0xfc, 0x27, 0x33, 0xff, 0xcf, 0xfc, + 0x01, 0xf8, 0x67, 0x33, 0xff, 0xcf, 0xfc, + 0x01, 0xf8, 0x67, 0x33, 0xff, 0xcf, 0xfc, + 0x03, 0xfc, 0x27, 0x33, 0xff, 0xcf, 0xfc, + 0x07, 0xfe, 0x06, 0x31, 0xff, 0xcf, 0xfc, + 0x0f, 0xff, 0x0e, 0x71, 0xff, 0xcf, 0xf8, + 0x1f, 0xff, 0x84, 0x61, 0xff, 0xcf, 0xf8, + 0x3e, 0xf7, 0xc0, 0xe0, 0xff, 0xcf, 0xf8, + 0x7c, 0xf3, 0xe1, 0xc0, 0xff, 0xff, 0xf0, + 0xf8, 0xf3, 0xc0, 0xc0, 0x7f, 0xff, 0xe0, + 0x70, 0xf7, 0x80, 0x00, 0x3f, 0xff, 0xe0, + 0x20, 0xff, 0x00, 0x00, 0x1f, 0xff, 0x80, + 0x00, 0xfe, 0x00, 0x00, 0x0f, 0xff, 0x00, + 0x00, 0xfc, 0x00, 0x00, 0x01, 0xfc, 0x00, + 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t bluetooth_advertising_1 = { + .header.always_zero = 0, + .header.w = 54, + .header.h = 35, + .data_size = 254, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = bluetooth_advertising_1_map, +}; diff --git a/app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_advertising_2.c b/app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_advertising_2.c new file mode 100644 index 00000000000..a0c4345d99b --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_advertising_2.c @@ -0,0 +1,64 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif +#ifndef LV_ATTRIBUTE_IMG_BLUETOOTH_ADVERTISING_2 +#define LV_ATTRIBUTE_IMG_BLUETOOTH_ADVERTISING_2 +#endif +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_IMG_BLUETOOTH_ADVERTISING_2 uint8_t bluetooth_advertising_2_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xfe, 0x00, 0x00, 0x03, 0xfc, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x0f, 0xff, 0x00, + 0x60, 0xff, 0x80, 0x00, 0x1f, 0xff, 0xc0, + 0xf0, 0xf7, 0xc0, 0x80, 0x7f, 0xff, 0xe0, + 0x78, 0xf3, 0xe1, 0xc0, 0x7f, 0xff, 0xf0, + 0x3c, 0xf3, 0xc0, 0xe0, 0xff, 0x0f, 0xf0, + 0x1e, 0xf7, 0x84, 0xe1, 0xfe, 0x07, 0xf8, + 0x0f, 0xff, 0x0e, 0x71, 0xfc, 0x63, 0xf8, + 0x07, 0xfe, 0x0e, 0x71, 0xff, 0xf3, 0xfc, + 0x03, 0xfc, 0x06, 0x33, 0xff, 0xe3, 0xfc, + 0x01, 0xf8, 0x67, 0x33, 0xff, 0xe7, 0xfc, + 0x01, 0xf8, 0xe7, 0x33, 0xff, 0xc7, 0xfc, + 0x01, 0xfc, 0x67, 0x33, 0xff, 0x8f, 0xfc, + 0x03, 0xfe, 0x06, 0x33, 0xff, 0x1f, 0xfc, + 0x07, 0xff, 0x0e, 0x71, 0xfe, 0x3f, 0xfc, + 0x0f, 0xff, 0x8e, 0x61, 0xfc, 0x03, 0xf8, + 0x1e, 0xf7, 0xc0, 0xe1, 0xfc, 0x03, 0xf8, + 0x3c, 0xf3, 0xe0, 0xc0, 0xff, 0xff, 0xf0, + 0x78, 0xf3, 0xe1, 0xc0, 0x7f, 0xff, 0xf0, + 0xf0, 0xf7, 0xc0, 0x80, 0x7f, 0xff, 0xe0, + 0x60, 0xff, 0x80, 0x00, 0x1f, 0xff, 0xc0, + 0x00, 0xff, 0x00, 0x00, 0x0f, 0xff, 0x00, + 0x00, 0xfe, 0x00, 0x00, 0x03, 0xfc, 0x00, + 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t bluetooth_advertising_2 = { + .header.always_zero = 0, + .header.w = 54, + .header.h = 35, + .data_size = 254, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = bluetooth_advertising_2_map, +}; diff --git a/app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_advertising_3.c b/app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_advertising_3.c new file mode 100644 index 00000000000..3e70f082868 --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_advertising_3.c @@ -0,0 +1,64 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif +#ifndef LV_ATTRIBUTE_IMG_BLUETOOTH_ADVERTISING_3 +#define LV_ATTRIBUTE_IMG_BLUETOOTH_ADVERTISING_3 +#endif +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_IMG_BLUETOOTH_ADVERTISING_3 uint8_t bluetooth_advertising_3_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xfc, 0x00, 0x00, 0x01, 0xfc, 0x00, + 0x00, 0xfe, 0x00, 0x00, 0x0f, 0xff, 0x00, + 0x20, 0xff, 0x00, 0x00, 0x1f, 0xff, 0xc0, + 0x70, 0xf7, 0x80, 0x00, 0x3f, 0xff, 0xe0, + 0xf8, 0xf3, 0xc1, 0xc0, 0x7f, 0xff, 0xf0, + 0x7c, 0xf3, 0xe0, 0xc0, 0xff, 0x9f, 0xf0, + 0x3e, 0xf7, 0xc0, 0xe0, 0xfe, 0x07, 0xf8, + 0x1f, 0xff, 0x84, 0x61, 0xfe, 0x03, 0xf8, + 0x0f, 0xff, 0x0e, 0x71, 0xfe, 0xe3, 0xfc, + 0x07, 0xfe, 0x06, 0x31, 0xff, 0xe3, 0xfc, + 0x03, 0xfc, 0x27, 0x33, 0xff, 0x87, 0xfc, + 0x01, 0xf8, 0x67, 0x33, 0xff, 0x87, 0xfc, + 0x01, 0xf8, 0x67, 0x33, 0xff, 0x83, 0xfc, + 0x03, 0xfc, 0x27, 0x33, 0xff, 0xf3, 0xfc, + 0x07, 0xfe, 0x06, 0x31, 0xfe, 0xf3, 0xfc, + 0x0f, 0xff, 0x0e, 0x71, 0xfc, 0x63, 0xfc, + 0x1f, 0xff, 0x84, 0x61, 0xfe, 0x03, 0xf8, + 0x3e, 0xf7, 0xc0, 0xe0, 0xff, 0x0f, 0xf8, + 0x7c, 0xf3, 0xe1, 0xc0, 0xff, 0xff, 0xf0, + 0xf8, 0xf3, 0xc0, 0xc0, 0x7f, 0xff, 0xf0, + 0x70, 0xf7, 0x80, 0x00, 0x3f, 0xff, 0xe0, + 0x20, 0xff, 0x00, 0x00, 0x1f, 0xff, 0xc0, + 0x00, 0xfe, 0x00, 0x00, 0x0f, 0xff, 0x00, + 0x00, 0xfc, 0x00, 0x00, 0x01, 0xfc, 0x00, + 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t bluetooth_advertising_3 = { + .header.always_zero = 0, + .header.w = 54, + .header.h = 35, + .data_size = 254, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = bluetooth_advertising_3_map, +}; diff --git a/app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_advertising_4.c b/app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_advertising_4.c new file mode 100644 index 00000000000..3e8441f7b37 --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_advertising_4.c @@ -0,0 +1,64 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif +#ifndef LV_ATTRIBUTE_IMG_BLUETOOTH_ADVERTISING_4 +#define LV_ATTRIBUTE_IMG_BLUETOOTH_ADVERTISING_4 +#endif +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_IMG_BLUETOOTH_ADVERTISING_4 uint8_t bluetooth_advertising_4_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xfc, 0x00, 0x00, 0x01, 0xfc, 0x00, + 0x00, 0xfe, 0x00, 0x00, 0x0f, 0xff, 0x00, + 0x20, 0xff, 0x00, 0x00, 0x1f, 0xff, 0xc0, + 0x70, 0xf7, 0x80, 0x00, 0x3f, 0xff, 0xe0, + 0xf8, 0xf3, 0xc0, 0xc0, 0x7f, 0xff, 0xe0, + 0x7c, 0xf3, 0xe0, 0xc0, 0xff, 0xff, 0xf0, + 0x3e, 0xf7, 0xc0, 0xe0, 0xff, 0xcf, 0xf8, + 0x1f, 0xff, 0x84, 0x61, 0xff, 0x8f, 0xf8, + 0x0f, 0xff, 0x0e, 0x71, 0xff, 0x0f, 0xf8, + 0x07, 0xfe, 0x06, 0x31, 0xff, 0x0f, 0xfc, + 0x03, 0xfc, 0x27, 0x31, 0xfe, 0x4f, 0xfc, + 0x01, 0xf8, 0x67, 0x31, 0xfc, 0x4f, 0xfc, + 0x01, 0xf8, 0x67, 0x33, 0xfc, 0xcf, 0xfc, + 0x03, 0xfc, 0x27, 0x31, 0xf8, 0x07, 0xfc, + 0x07, 0xfe, 0x06, 0x31, 0xf8, 0x03, 0xfc, + 0x0f, 0xff, 0x0e, 0x71, 0xf8, 0x07, 0xf8, + 0x1f, 0xff, 0x84, 0x61, 0xff, 0xcf, 0xf8, + 0x3e, 0xf7, 0xc0, 0xe0, 0xff, 0xcf, 0xf8, + 0x7c, 0xf3, 0xe1, 0xc0, 0xff, 0xff, 0xf0, + 0xf8, 0xf3, 0xc0, 0xc0, 0x7f, 0xff, 0xe0, + 0x70, 0xf7, 0x80, 0x00, 0x3f, 0xff, 0xc0, + 0x20, 0xff, 0x00, 0x00, 0x1f, 0xff, 0x80, + 0x00, 0xfe, 0x00, 0x00, 0x07, 0xff, 0x00, + 0x00, 0xfc, 0x00, 0x00, 0x01, 0xf8, 0x00, + 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t bluetooth_advertising_4 = { + .header.always_zero = 0, + .header.w = 54, + .header.h = 35, + .data_size = 254, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = bluetooth_advertising_4_map, +}; diff --git a/app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_advertising_5.c b/app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_advertising_5.c new file mode 100644 index 00000000000..3b24c9d81e8 --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_advertising_5.c @@ -0,0 +1,64 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif +#ifndef LV_ATTRIBUTE_IMG_BLUETOOTH_ADVERTISING_5 +#define LV_ATTRIBUTE_IMG_BLUETOOTH_ADVERTISING_5 +#endif +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_IMG_BLUETOOTH_ADVERTISING_5 uint8_t bluetooth_advertising_5_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xfc, 0x00, 0x00, 0x01, 0xfc, 0x00, + 0x00, 0xfe, 0x00, 0x00, 0x0f, 0xff, 0x00, + 0x20, 0xff, 0x00, 0x00, 0x1f, 0xff, 0x80, + 0x70, 0xf7, 0x80, 0x00, 0x3f, 0xff, 0xe0, + 0xf8, 0xf3, 0xc0, 0xc0, 0x7f, 0xff, 0xe0, + 0x7c, 0xf3, 0xe0, 0xe0, 0xff, 0xff, 0xf0, + 0x3e, 0xf7, 0xc0, 0xe1, 0xfe, 0x07, 0xf8, + 0x1f, 0xff, 0x84, 0x61, 0xfe, 0x07, 0xf8, + 0x0f, 0xff, 0x0e, 0x71, 0xfc, 0x7f, 0xf8, + 0x07, 0xfe, 0x06, 0x33, 0xfc, 0x7f, 0xfc, + 0x03, 0xfc, 0x27, 0x33, 0xfc, 0x07, 0xfc, + 0x01, 0xf8, 0x67, 0x33, 0xfc, 0x03, 0xfc, + 0x01, 0xf8, 0x67, 0x33, 0xff, 0x63, 0xfc, + 0x03, 0xfc, 0x27, 0x33, 0xff, 0xf3, 0xfc, + 0x07, 0xfe, 0x06, 0x33, 0xfe, 0xf3, 0xfc, + 0x0f, 0xff, 0x0e, 0x71, 0xfc, 0x63, 0xf8, + 0x1f, 0xff, 0x84, 0x61, 0xfe, 0x07, 0xf8, + 0x3e, 0xf7, 0xc0, 0xe0, 0xff, 0x0f, 0xf8, + 0x7c, 0xf3, 0xe1, 0xc0, 0xff, 0xff, 0xf0, + 0xf8, 0xf3, 0xc0, 0xc0, 0x7f, 0xff, 0xe0, + 0x70, 0xf7, 0x80, 0x00, 0x3f, 0xff, 0xc0, + 0x20, 0xff, 0x00, 0x00, 0x1f, 0xff, 0x80, + 0x00, 0xfe, 0x00, 0x00, 0x0f, 0xff, 0x00, + 0x00, 0xfc, 0x00, 0x00, 0x01, 0xf8, 0x00, + 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t bluetooth_advertising_5 = { + .header.always_zero = 0, + .header.w = 54, + .header.h = 35, + .data_size = 254, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = bluetooth_advertising_5_map, +}; diff --git a/app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_connected_1.c b/app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_connected_1.c new file mode 100644 index 00000000000..1cb545aa2b2 --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_connected_1.c @@ -0,0 +1,64 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif +#ifndef LV_ATTRIBUTE_IMG_BLUETOOTH_CONNECTED_1 +#define LV_ATTRIBUTE_IMG_BLUETOOTH_CONNECTED_1 +#endif +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_IMG_BLUETOOTH_CONNECTED_1 uint8_t bluetooth_connected_1_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7e, 0x00, 0x00, 0x01, 0xfc, 0x00, + 0x00, 0x7f, 0x00, 0x00, 0x0f, 0xff, 0x00, + 0x30, 0x7f, 0x80, 0x00, 0x1f, 0xff, 0xc0, + 0x78, 0x77, 0xc0, 0x00, 0x3f, 0xff, 0xe0, + 0x7c, 0x73, 0xe0, 0x00, 0x7f, 0xff, 0xe0, + 0x3e, 0x71, 0xe0, 0x00, 0xff, 0xff, 0xf0, + 0x1f, 0x73, 0xc0, 0x00, 0xff, 0x8f, 0xf8, + 0x0f, 0xff, 0x80, 0x01, 0xfe, 0x0f, 0xf8, + 0x87, 0xff, 0x08, 0x01, 0xfe, 0x0f, 0xf8, + 0xc3, 0xfe, 0x18, 0x01, 0xff, 0xcf, 0xfc, + 0xe1, 0xfc, 0x38, 0x03, 0xff, 0xcf, 0xfc, + 0xf0, 0xf8, 0x78, 0x03, 0xff, 0xcf, 0xfc, + 0xf0, 0xfc, 0x78, 0x03, 0xff, 0xcf, 0xfc, + 0xe1, 0xfc, 0x38, 0x03, 0xff, 0xcf, 0xfc, + 0xc3, 0xfe, 0x18, 0x01, 0xff, 0xcf, 0xfc, + 0x87, 0xff, 0x08, 0x01, 0xff, 0xcf, 0xf8, + 0x0f, 0xff, 0x80, 0x01, 0xff, 0xcf, 0xf8, + 0x1e, 0x73, 0xc0, 0x00, 0xff, 0xcf, 0xf8, + 0x3c, 0x71, 0xe0, 0x00, 0xff, 0xff, 0xf0, + 0x78, 0x73, 0xe0, 0x00, 0x7f, 0xff, 0xe0, + 0x78, 0x77, 0xc0, 0x00, 0x3f, 0xff, 0xe0, + 0x30, 0x7f, 0x80, 0x00, 0x1f, 0xff, 0x80, + 0x00, 0x7f, 0x00, 0x00, 0x0f, 0xff, 0x00, + 0x00, 0x7e, 0x00, 0x00, 0x01, 0xfc, 0x00, + 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t bluetooth_connected_1 = { + .header.always_zero = 0, + .header.w = 54, + .header.h = 35, + .data_size = 254, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = bluetooth_connected_1_map, +}; diff --git a/app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_connected_2.c b/app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_connected_2.c new file mode 100644 index 00000000000..c5775a61167 --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_connected_2.c @@ -0,0 +1,64 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif +#ifndef LV_ATTRIBUTE_IMG_BLUETOOTH_CONNECTED_2 +#define LV_ATTRIBUTE_IMG_BLUETOOTH_CONNECTED_2 +#endif +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_IMG_BLUETOOTH_CONNECTED_2 uint8_t bluetooth_connected_2_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7e, 0x00, 0x00, 0x03, 0xfc, 0x00, + 0x00, 0x7f, 0x00, 0x00, 0x0f, 0xff, 0x00, + 0x30, 0x7f, 0x80, 0x00, 0x1f, 0xff, 0xc0, + 0x78, 0x73, 0xc0, 0x00, 0x7f, 0xff, 0xe0, + 0x7c, 0x71, 0xe0, 0x00, 0x7f, 0xff, 0xf0, + 0x3e, 0x73, 0xe0, 0x00, 0xff, 0x0f, 0xf0, + 0x1f, 0x77, 0xc0, 0x01, 0xfe, 0x07, 0xf8, + 0x0f, 0xff, 0x80, 0x01, 0xfc, 0x63, 0xf8, + 0x87, 0xff, 0x08, 0x01, 0xff, 0xf3, 0xfc, + 0xc3, 0xfe, 0x18, 0x03, 0xff, 0xe3, 0xfc, + 0xe1, 0xfc, 0x38, 0x03, 0xff, 0xe7, 0xfc, + 0xf0, 0xf8, 0x78, 0x03, 0xff, 0xc7, 0xfc, + 0xe1, 0xfc, 0x38, 0x03, 0xff, 0x8f, 0xfc, + 0xc3, 0xfe, 0x18, 0x03, 0xff, 0x1f, 0xfc, + 0x87, 0xff, 0x08, 0x01, 0xfe, 0x3f, 0xfc, + 0x0f, 0xff, 0x80, 0x01, 0xfc, 0x03, 0xf8, + 0x1f, 0x77, 0xc0, 0x01, 0xfc, 0x03, 0xf8, + 0x3e, 0x73, 0xe0, 0x00, 0xff, 0xff, 0xf0, + 0x7c, 0x71, 0xe0, 0x00, 0x7f, 0xff, 0xf0, + 0x78, 0x73, 0xc0, 0x00, 0x7f, 0xff, 0xe0, + 0x30, 0x7f, 0x80, 0x00, 0x1f, 0xff, 0xc0, + 0x00, 0x7f, 0x00, 0x00, 0x0f, 0xff, 0x00, + 0x00, 0x7e, 0x00, 0x00, 0x03, 0xfc, 0x00, + 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t bluetooth_connected_2 = { + .header.always_zero = 0, + .header.w = 54, + .header.h = 35, + .data_size = 254, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = bluetooth_connected_2_map, +}; diff --git a/app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_connected_3.c b/app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_connected_3.c new file mode 100644 index 00000000000..111b9d320a9 --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_connected_3.c @@ -0,0 +1,64 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif +#ifndef LV_ATTRIBUTE_IMG_BLUETOOTH_CONNECTED_3 +#define LV_ATTRIBUTE_IMG_BLUETOOTH_CONNECTED_3 +#endif +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_IMG_BLUETOOTH_CONNECTED_3 uint8_t bluetooth_connected_3_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7e, 0x00, 0x00, 0x03, 0xfc, 0x00, + 0x00, 0x7f, 0x00, 0x00, 0x0f, 0xff, 0x00, + 0x30, 0x7f, 0x80, 0x00, 0x1f, 0xff, 0xc0, + 0x78, 0x77, 0xc0, 0x00, 0x7f, 0xff, 0xe0, + 0x7c, 0x73, 0xe0, 0x00, 0x7f, 0xff, 0xf0, + 0x3e, 0x71, 0xe0, 0x00, 0xff, 0x9f, 0xf0, + 0x1f, 0x73, 0xc0, 0x01, 0xfe, 0x07, 0xf8, + 0x0f, 0xff, 0x80, 0x01, 0xfc, 0x03, 0xf8, + 0x87, 0xff, 0x08, 0x01, 0xfe, 0xe3, 0xfc, + 0xc3, 0xfe, 0x18, 0x03, 0xff, 0xe3, 0xfc, + 0xe1, 0xfc, 0x38, 0x03, 0xff, 0x87, 0xfc, + 0xf0, 0xf8, 0x78, 0x03, 0xff, 0x87, 0xfc, + 0xf0, 0xfc, 0x78, 0x03, 0xff, 0x83, 0xfc, + 0xe1, 0xfe, 0x38, 0x03, 0xff, 0xf3, 0xfc, + 0xc3, 0xff, 0x18, 0x03, 0xfe, 0xf3, 0xfc, + 0x87, 0xff, 0x88, 0x01, 0xfc, 0x63, 0xf8, + 0x0f, 0xf7, 0xc0, 0x01, 0xfe, 0x03, 0xf8, + 0x1f, 0x73, 0xe0, 0x01, 0xff, 0x0f, 0xf8, + 0x3e, 0x71, 0xe0, 0x00, 0xff, 0xff, 0xf0, + 0x7c, 0x73, 0xe0, 0x00, 0x7f, 0xff, 0xe0, + 0x78, 0x77, 0xc0, 0x00, 0x3f, 0xff, 0xe0, + 0x30, 0x7f, 0x80, 0x00, 0x1f, 0xff, 0x80, + 0x00, 0x7f, 0x00, 0x00, 0x0f, 0xff, 0x00, + 0x00, 0x7e, 0x00, 0x00, 0x01, 0xf8, 0x00, + 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t bluetooth_connected_3 = { + .header.always_zero = 0, + .header.w = 54, + .header.h = 35, + .data_size = 254, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = bluetooth_connected_3_map, +}; diff --git a/app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_connected_4.c b/app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_connected_4.c new file mode 100644 index 00000000000..3b2db361515 --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_connected_4.c @@ -0,0 +1,64 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif +#ifndef LV_ATTRIBUTE_IMG_BLUETOOTH_CONNECTED_4 +#define LV_ATTRIBUTE_IMG_BLUETOOTH_CONNECTED_4 +#endif +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_IMG_BLUETOOTH_CONNECTED_4 uint8_t bluetooth_connected_4_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7e, 0x00, 0x00, 0x01, 0xf8, 0x00, + 0x00, 0x7f, 0x00, 0x00, 0x07, 0xff, 0x00, + 0x30, 0x7f, 0x80, 0x00, 0x1f, 0xff, 0x80, + 0x78, 0x77, 0xc0, 0x00, 0x3f, 0xff, 0xe0, + 0x7c, 0x73, 0xe0, 0x00, 0x7f, 0xff, 0xe0, + 0x3e, 0x71, 0xe0, 0x00, 0xff, 0xff, 0xf0, + 0x1f, 0x73, 0xc0, 0x00, 0xff, 0xcf, 0xf8, + 0x0f, 0xff, 0x80, 0x01, 0xff, 0x8f, 0xf8, + 0x87, 0xff, 0x08, 0x01, 0xff, 0x0f, 0xf8, + 0xc3, 0xfe, 0x18, 0x01, 0xff, 0x0f, 0xfc, + 0xe1, 0xfc, 0x38, 0x03, 0xfe, 0x4f, 0xfc, + 0xf0, 0xf8, 0x78, 0x03, 0xfc, 0x4f, 0xfc, + 0xf0, 0xfc, 0x78, 0x03, 0xfc, 0xcf, 0xfc, + 0xe1, 0xfe, 0x38, 0x03, 0xf8, 0xc7, 0xfc, + 0xc3, 0xff, 0x18, 0x01, 0xf8, 0x03, 0xfc, + 0x87, 0xff, 0x88, 0x01, 0xf8, 0x03, 0xf8, + 0x0f, 0xf7, 0xc0, 0x01, 0xff, 0xcf, 0xf8, + 0x1f, 0x73, 0xe0, 0x00, 0xff, 0xcf, 0xf8, + 0x3e, 0x71, 0xe0, 0x00, 0xff, 0xff, 0xf0, + 0x7c, 0x73, 0xe0, 0x00, 0x7f, 0xff, 0xe0, + 0x78, 0x77, 0xc0, 0x00, 0x3f, 0xff, 0xe0, + 0x30, 0x7f, 0x80, 0x00, 0x1f, 0xff, 0x80, + 0x00, 0x7f, 0x00, 0x00, 0x0f, 0xff, 0x00, + 0x00, 0x7e, 0x00, 0x00, 0x01, 0xf8, 0x00, + 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t bluetooth_connected_4 = { + .header.always_zero = 0, + .header.w = 54, + .header.h = 35, + .data_size = 254, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = bluetooth_connected_4_map, +}; diff --git a/app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_connected_5.c b/app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_connected_5.c new file mode 100644 index 00000000000..bab117b89dc --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_connected_5.c @@ -0,0 +1,64 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif +#ifndef LV_ATTRIBUTE_IMG_BLUETOOTH_CONNECTED_5 +#define LV_ATTRIBUTE_IMG_BLUETOOTH_CONNECTED_5 +#endif +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_IMG_BLUETOOTH_CONNECTED_5 uint8_t bluetooth_connected_5_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7e, 0x00, 0x00, 0x01, 0xfc, 0x00, + 0x00, 0x7f, 0x00, 0x00, 0x0f, 0xff, 0x00, + 0x30, 0x7f, 0x80, 0x00, 0x1f, 0xff, 0x80, + 0x78, 0x77, 0xc0, 0x00, 0x3f, 0xff, 0xe0, + 0x7c, 0x73, 0xe0, 0x00, 0x7f, 0xff, 0xe0, + 0x3e, 0x71, 0xe0, 0x00, 0xff, 0xff, 0xf0, + 0x1f, 0x73, 0xc0, 0x01, 0xfe, 0x07, 0xf8, + 0x0f, 0xff, 0x80, 0x01, 0xfe, 0x07, 0xf8, + 0x87, 0xff, 0x08, 0x01, 0xfc, 0x7f, 0xf8, + 0xc3, 0xfe, 0x18, 0x03, 0xfc, 0x7f, 0xfc, + 0xe1, 0xfc, 0x38, 0x03, 0xfc, 0x07, 0xfc, + 0xf0, 0xf8, 0x78, 0x03, 0xfc, 0x03, 0xfc, + 0xf0, 0xfc, 0x78, 0x03, 0xff, 0x63, 0xfc, + 0xe1, 0xfe, 0x38, 0x03, 0xff, 0xf3, 0xfc, + 0xc3, 0xff, 0x18, 0x03, 0xfe, 0xf3, 0xfc, + 0x87, 0xff, 0x88, 0x01, 0xfc, 0x63, 0xf8, + 0x0f, 0xf7, 0xc0, 0x01, 0xfe, 0x07, 0xf8, + 0x1e, 0x73, 0xc0, 0x00, 0xff, 0x0f, 0xf8, + 0x3c, 0x71, 0xe0, 0x00, 0xff, 0xff, 0xf0, + 0x7c, 0x73, 0xe0, 0x00, 0x7f, 0xff, 0xe0, + 0x78, 0x77, 0xc0, 0x00, 0x3f, 0xff, 0xc0, + 0x30, 0x7f, 0x80, 0x00, 0x1f, 0xff, 0x80, + 0x00, 0x7f, 0x00, 0x00, 0x0f, 0xff, 0x00, + 0x00, 0x7e, 0x00, 0x00, 0x01, 0xf8, 0x00, + 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t bluetooth_connected_5 = { + .header.always_zero = 0, + .header.w = 54, + .header.h = 35, + .data_size = 254, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = bluetooth_connected_5_map, +}; diff --git a/app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_connected_right.c b/app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_connected_right.c new file mode 100644 index 00000000000..1e637bdf29a --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_connected_right.c @@ -0,0 +1,69 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_BLUETOOTH_CONNECTED_RIGHT +#define LV_ATTRIBUTE_IMG_BLUETOOTH_CONNECTED_RIGHT +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BLUETOOTH_CONNECTED_RIGHT uint8_t bluetooth_connected_right_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0x00, 0x00, 0x03, 0xf8, 0x00, + 0x00, 0x3f, 0x80, 0x00, 0x0f, 0xfe, 0x00, + 0x10, 0x3f, 0xc0, 0x00, 0x3f, 0xff, 0x80, + 0x38, 0x3b, 0xe0, 0x00, 0x7f, 0xff, 0xc0, + 0x7c, 0x39, 0xf0, 0x00, 0xff, 0xff, 0xe0, + 0x3e, 0x38, 0xf0, 0x01, 0xff, 0xff, 0xf0, + 0x1f, 0x39, 0xe0, 0x01, 0xff, 0xfe, 0x30, + 0x0f, 0xbb, 0xc0, 0x03, 0xff, 0xfc, 0x38, + 0x87, 0xff, 0x84, 0x03, 0xff, 0xf8, 0x38, + 0xc3, 0xff, 0x0c, 0x07, 0xff, 0xf0, 0x3c, + 0xe1, 0xfe, 0x1c, 0x07, 0xcf, 0xe0, 0x7c, + 0xf0, 0xfc, 0x3c, 0x07, 0x87, 0xc0, 0xfc, + 0xf0, 0xfc, 0x3c, 0x07, 0x83, 0x81, 0xfc, + 0xe1, 0xfe, 0x1c, 0x07, 0x81, 0x03, 0xfc, + 0xc3, 0xff, 0x0c, 0x07, 0xc0, 0x07, 0xfc, + 0x87, 0xff, 0x84, 0x07, 0xe0, 0x0f, 0xfc, + 0x0f, 0xbb, 0xc0, 0x03, 0xf0, 0x1f, 0xf8, + 0x1f, 0x39, 0xe0, 0x03, 0xf8, 0x3f, 0xf8, + 0x3e, 0x38, 0xf0, 0x01, 0xfc, 0xff, 0xf0, + 0x7c, 0x39, 0xf0, 0x01, 0xff, 0xff, 0xf0, + 0x38, 0x3b, 0xe0, 0x00, 0xff, 0xff, 0xe0, + 0x10, 0x3f, 0xc0, 0x00, 0x7f, 0xff, 0xc0, + 0x00, 0x3f, 0x80, 0x00, 0x3f, 0xff, 0x80, + 0x00, 0x3f, 0x00, 0x00, 0x0f, 0xfe, 0x00, + 0x00, 0x3e, 0x00, 0x00, 0x03, 0xf8, 0x00, + 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t bluetooth_connected_right = { + .header.always_zero = 0, + .header.w = 54, + .header.h = 35, + .data_size = 253, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = bluetooth_connected_right_map, +}; + diff --git a/app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_disconnected.c b/app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_disconnected.c new file mode 100644 index 00000000000..05f81393f52 --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/master/icons/bluetooth_disconnected.c @@ -0,0 +1,68 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_BLUETOOTH_DISCONNECTED +#define LV_ATTRIBUTE_IMG_BLUETOOTH_DISCONNECTED +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BLUETOOTH_DISCONNECTED uint8_t bluetooth_disconnected_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0x00, 0x00, 0x03, 0xf8, 0x00, + 0x00, 0x3f, 0x80, 0x00, 0x0f, 0xfe, 0x00, + 0x10, 0x3f, 0xc0, 0x00, 0x3f, 0xff, 0x80, + 0x38, 0x3b, 0xe0, 0x00, 0x7f, 0xff, 0xc0, + 0x7c, 0x39, 0xf0, 0x00, 0xff, 0xff, 0xe0, + 0x3e, 0x38, 0xf0, 0x01, 0xff, 0xff, 0xf0, + 0x1f, 0x39, 0xe0, 0x01, 0xe3, 0xf8, 0xf0, + 0x0f, 0xbb, 0xc0, 0x03, 0xe1, 0xf0, 0xf8, + 0x07, 0xff, 0x80, 0x03, 0xe0, 0xe0, 0xf8, + 0x03, 0xff, 0x00, 0x07, 0xf0, 0x01, 0xfc, + 0x01, 0xfe, 0x00, 0x07, 0xf8, 0x03, 0xfc, + 0x00, 0xfc, 0x00, 0x07, 0xfc, 0x07, 0xfc, + 0x00, 0xfc, 0x00, 0x07, 0xfc, 0x07, 0xfc, + 0x01, 0xfe, 0x00, 0x07, 0xfc, 0x07, 0xfc, + 0x03, 0xff, 0x00, 0x07, 0xf8, 0x03, 0xfc, + 0x07, 0xff, 0x80, 0x07, 0xf0, 0x01, 0xfc, + 0x0f, 0xbb, 0xc0, 0x03, 0xe0, 0xe0, 0xf8, + 0x1f, 0x39, 0xe0, 0x03, 0xe1, 0xf0, 0xf8, + 0x3e, 0x38, 0xf0, 0x01, 0xe3, 0xf8, 0xf0, + 0x7c, 0x39, 0xf0, 0x01, 0xff, 0xff, 0xf0, + 0x38, 0x3b, 0xe0, 0x00, 0xff, 0xff, 0xe0, + 0x10, 0x3f, 0xc0, 0x00, 0x7f, 0xff, 0xc0, + 0x00, 0x3f, 0x80, 0x00, 0x3f, 0xff, 0x80, + 0x00, 0x3f, 0x00, 0x00, 0x0f, 0xfe, 0x00, + 0x00, 0x3e, 0x00, 0x00, 0x03, 0xf8, 0x00, + 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t bluetooth_disconnected = { + .header.always_zero = 0, + .header.w = 54, + .header.h = 35, + .data_size = 253, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = bluetooth_disconnected_map, +}; + diff --git a/app/boards/arm/nrfmacro/epd_screen/master/icons/layers.c b/app/boards/arm/nrfmacro/epd_screen/master/icons/layers.c new file mode 100644 index 00000000000..9c0b53f7dbf --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/master/icons/layers.c @@ -0,0 +1,45 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_LAYERS +#define LV_ATTRIBUTE_IMG_LAYERS +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_LAYERS uint8_t layers_map[] = { + 0x00, 0x00, 0x00, 0xff, /*Color of index 0*/ + 0xff, 0xff, 0xff, 0xff, /*Color of index 1*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x30, 0x0f, 0x18, 0xdf, 0xdf, 0xc0, 0x04, 0x00, + 0x00, 0x80, 0x30, 0x19, 0x98, 0xd8, 0x18, 0xe0, 0x0e, 0x00, + 0x00, 0x80, 0x30, 0x30, 0xd8, 0xd8, 0x18, 0xe0, 0x1f, 0x00, + 0x00, 0x80, 0x30, 0x30, 0xcf, 0x9f, 0xd9, 0xc0, 0x04, 0x00, + 0x00, 0x80, 0x30, 0x3f, 0xc7, 0x18, 0x1f, 0x80, 0x04, 0x00, + 0x03, 0xe0, 0x30, 0x39, 0xc6, 0x18, 0x1b, 0x80, 0x04, 0x00, + 0x01, 0xc0, 0x3f, 0xb0, 0xc6, 0x1f, 0xd9, 0xc0, 0x04, 0x00, + 0x00, 0x80, 0x3f, 0xb0, 0xc6, 0x1f, 0xd8, 0xe0, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t layers = { + .header.always_zero = 0, + .header.w = 78, + .header.h = 12, + .data_size = 128, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = layers_map, +}; + diff --git a/app/boards/arm/nrfmacro/epd_screen/master/layer_status.c b/app/boards/arm/nrfmacro/epd_screen/master/layer_status.c new file mode 100644 index 00000000000..92022629a12 --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/master/layer_status.c @@ -0,0 +1,102 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include +#include +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); +#include +#include "layer_status.h" +#include +#include +#include +#include + +static sys_slist_t widgets = SYS_SLIST_STATIC_INIT(&widgets); +static lv_style_t label_style; + +static bool style_initialized = false; + +K_MUTEX_DEFINE(layer_status_mutex); + +struct { + uint8_t index; + const char *label; +} layer_status_state; + +void layer_status_init() { + if (style_initialized) { + return; + } + style_initialized = true; + lv_style_init(&label_style); + lv_style_set_text_color(&label_style, LV_STATE_DEFAULT, LV_COLOR_BLACK); + lv_style_set_text_font(&label_style, LV_STATE_DEFAULT, &lv_font_montserrat_16); + lv_style_set_text_letter_space(&label_style, LV_STATE_DEFAULT, 1); + lv_style_set_text_line_space(&label_style, LV_STATE_DEFAULT, 1); + +} + +void set_layer_symbol(lv_obj_t *label) { + + k_mutex_lock(&layer_status_mutex, K_FOREVER); + const char *layer_label = layer_status_state.label; + uint8_t active_layer_index = layer_status_state.index; + k_mutex_unlock(&layer_status_mutex); + + //LOG_DBG("Layer Label: %s", layer_label); + + if (layer_label == NULL) { + char text[6] = {}; + + sprintf(text, " %i", active_layer_index); + + lv_label_set_text(label, text); + } else { + lv_label_set_text(label, layer_label); + } +} + +static void update_state() { + k_mutex_lock(&layer_status_mutex, K_FOREVER); + layer_status_state.index = zmk_keymap_highest_layer_active(); + layer_status_state.label = zmk_keymap_layer_label(layer_status_state.index); + LOG_DBG("Layer changed to %i", layer_status_state.index); + + k_mutex_unlock(&layer_status_mutex); +} + +int zmk_widget_layer_status_init(struct zmk_widget_layer_status *widget, lv_obj_t *parent) { + layer_status_init(); + update_state(); + widget->obj = lv_label_create(parent, NULL); + lv_obj_add_style(widget->obj, LV_LABEL_PART_MAIN, &label_style); + set_layer_symbol(widget->obj); + sys_slist_append(&widgets, &widget->node); + + return 0; +} + +lv_obj_t *zmk_widget_layer_status_obj(struct zmk_widget_layer_status *widget) { + return widget->obj; +} + +void layer_status_update_cb(struct k_work *work) { + struct zmk_widget_layer_status *widget; + SYS_SLIST_FOR_EACH_CONTAINER(&widgets, widget, node) { set_layer_symbol(widget->obj); } +} + +K_WORK_DEFINE(layer_status_update_work, layer_status_update_cb); + +int layer_status_listener(const zmk_event_t *eh) { + update_state();; + + k_work_submit_to_queue(zmk_display_work_q(), &layer_status_update_work); + return 0; +} + +ZMK_LISTENER(widget_layer_status, layer_status_listener) +ZMK_SUBSCRIPTION(widget_layer_status, zmk_layer_state_changed); \ No newline at end of file diff --git a/app/boards/arm/nrfmacro/epd_screen/master/layer_status.h b/app/boards/arm/nrfmacro/epd_screen/master/layer_status.h new file mode 100644 index 00000000000..2616ad225d0 --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/master/layer_status.h @@ -0,0 +1,20 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#pragma once + +#include +#include + +struct zmk_widget_layer_status { + sys_snode_t node; + lv_obj_t *obj; + //lv_obj_t *obj2; +}; + +int zmk_widget_layer_status_init(struct zmk_widget_layer_status *widget, lv_obj_t *parent); +lv_obj_t *zmk_widget_layer_status_obj(struct zmk_widget_layer_status *widget); \ No newline at end of file diff --git a/app/boards/arm/nrfmacro/epd_screen/master/output_status.c b/app/boards/arm/nrfmacro/epd_screen/master/output_status.c new file mode 100644 index 00000000000..a67ad0b3bba --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/master/output_status.c @@ -0,0 +1,183 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include +#include + +#include +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#include +#include "output_status.h" +#include +#include +#include +#include +#include +#include +#include + +LV_IMG_DECLARE(bluetooth_disconnected); +LV_IMG_DECLARE(bluetooth_connected_1); +LV_IMG_DECLARE(bluetooth_connected_2); +LV_IMG_DECLARE(bluetooth_connected_3); +LV_IMG_DECLARE(bluetooth_connected_4); +LV_IMG_DECLARE(bluetooth_connected_5); +LV_IMG_DECLARE(bluetooth_advertising_1); +LV_IMG_DECLARE(bluetooth_advertising_2); +LV_IMG_DECLARE(bluetooth_advertising_3); +LV_IMG_DECLARE(bluetooth_advertising_4); +LV_IMG_DECLARE(bluetooth_advertising_5); +LV_IMG_DECLARE(USB_connected); + +static sys_slist_t widgets = SYS_SLIST_STATIC_INIT(&widgets); +static lv_style_t label_style; + +static bool style_initialized = false; + +K_MUTEX_DEFINE(output_status_mutex); + +struct { + enum zmk_endpoint selected_endpoint; + bool active_profile_connected; + bool active_profile_bonded; + uint8_t active_profile_index; +} output_status_state; + +void output_status_init() { + if (style_initialized) { + return; + } + + style_initialized = true; + lv_style_init(&label_style); + lv_style_set_text_color(&label_style, LV_STATE_DEFAULT, LV_COLOR_BLACK); + lv_style_set_text_font(&label_style, LV_STATE_DEFAULT, &lv_font_montserrat_26); + lv_style_set_text_letter_space(&label_style, LV_STATE_DEFAULT, 1); + lv_style_set_text_line_space(&label_style, LV_STATE_DEFAULT, 1); +} + +void set_status_symbol(lv_obj_t *icon) { + + k_mutex_lock(&output_status_mutex, K_FOREVER); + enum zmk_endpoint selected_endpoint = output_status_state.selected_endpoint; + bool active_profile_connected = output_status_state.active_profile_connected; + bool active_profie_bonded = output_status_state.active_profile_bonded; + uint8_t active_profile_index = output_status_state.active_profile_index; + k_mutex_unlock(&output_status_mutex); + + switch (selected_endpoint) { + case ZMK_ENDPOINT_USB: + lv_img_set_src(icon, &USB_connected); + break; + case ZMK_ENDPOINT_BLE: + if (active_profie_bonded) { + if (active_profile_connected) { + //sprintf(text, LV_SYMBOL_BLUETOOTH "%i " LV_SYMBOL_OK, active_profile_index); + switch (active_profile_index) { + case 0: + lv_img_set_src(icon, &bluetooth_connected_1); + break; + case 1: + lv_img_set_src(icon, &bluetooth_connected_2); + break; + case 2: + lv_img_set_src(icon, &bluetooth_connected_3); + break; + case 3: + lv_img_set_src(icon, &bluetooth_connected_4); + break; + case 4: + lv_img_set_src(icon, &bluetooth_connected_5); + break; + } + } else { + //sprintf(text, LV_SYMBOL_BLUETOOTH "%i " LV_SYMBOL_CLOSE, active_profile_index); + lv_img_set_src(icon, &bluetooth_disconnected); + } + } else { + //sprintf(text, LV_SYMBOL_BLUETOOTH "%i " LV_SYMBOL_SETTINGS, active_profile_index); + switch (active_profile_index) { + case 0: + lv_img_set_src(icon, &bluetooth_advertising_1); + break; + case 1: + lv_img_set_src(icon, &bluetooth_advertising_2); + break; + case 2: + lv_img_set_src(icon, &bluetooth_advertising_3); + break; + case 3: + lv_img_set_src(icon, &bluetooth_advertising_4); + break; + case 4: + lv_img_set_src(icon, &bluetooth_advertising_5); + break; + } + } + break; + } + + //lv_label_set_text(label, text); +} + +static void update_state() { + k_mutex_lock(&output_status_mutex, K_FOREVER); + output_status_state.selected_endpoint = zmk_endpoints_selected(); + output_status_state.active_profile_connected = zmk_ble_active_profile_is_connected(); + output_status_state.active_profile_bonded = !zmk_ble_active_profile_is_open(); + output_status_state.active_profile_index = zmk_ble_active_profile_index(); + k_mutex_unlock(&output_status_mutex); +} + +int zmk_widget_output_status_init(struct zmk_widget_output_status *widget, lv_obj_t *parent) { + output_status_init(); + update_state(); + //widget->obj = lv_label_create(parent, NULL); + widget->obj = lv_img_create(parent, NULL); + + set_status_symbol(widget->obj); + + sys_slist_append(&widgets, &widget->node); + + return 0; +} + +lv_obj_t *zmk_widget_output_status_obj(struct zmk_widget_output_status *widget) { + return widget->obj; +} + +void output_status_update_cb(struct k_work *work) { + struct zmk_widget_output_status *widget; + SYS_SLIST_FOR_EACH_CONTAINER(&widgets, widget, node) { set_status_symbol(widget->obj); } +} + +K_WORK_DEFINE(output_status_update_work, output_status_update_cb); + +int output_status_listener(const zmk_event_t *eh) { + + + // Be sure we have widgets initialized before doing any work, + // since the status event can fire before display code inits. + if (!style_initialized) { + return ZMK_EV_EVENT_BUBBLE; + } + + update_state(); + + k_work_submit_to_queue(zmk_display_work_q(), &output_status_update_work); + return ZMK_EV_EVENT_BUBBLE; +} + +ZMK_LISTENER(widget_output_status, output_status_listener) +ZMK_SUBSCRIPTION(widget_output_status, zmk_endpoint_selection_changed); +#if defined(CONFIG_USB) +ZMK_SUBSCRIPTION(widget_output_status, zmk_usb_conn_state_changed); +#endif +#if defined(CONFIG_ZMK_BLE) +ZMK_SUBSCRIPTION(widget_output_status, zmk_ble_active_profile_changed); +#endif diff --git a/app/boards/arm/nrfmacro/epd_screen/master/output_status.h b/app/boards/arm/nrfmacro/epd_screen/master/output_status.h new file mode 100644 index 00000000000..d10018b15e3 --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/master/output_status.h @@ -0,0 +1,19 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#pragma once + +#include +#include + +struct zmk_widget_output_status { + sys_snode_t node; + lv_obj_t *obj; +}; + +int zmk_widget_output_status_init(struct zmk_widget_output_status *widget, lv_obj_t *parent); +lv_obj_t *zmk_widget_output_status_obj(struct zmk_widget_output_status *widget); \ No newline at end of file diff --git a/app/boards/arm/nrfmacro/epd_screen/master/status_screen.c b/app/boards/arm/nrfmacro/epd_screen/master/status_screen.c new file mode 100644 index 00000000000..daff751d9c4 --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/master/status_screen.c @@ -0,0 +1,60 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include "battery_status.h" +#include "output_status.h" +#include "layer_status.h" +#include + +#include +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +LV_IMG_DECLARE(layers); + +#if IS_ENABLED(CONFIG_CUSTOM_WIDGET_BATTERY_STATUS) +static struct zmk_widget_battery_status battery_status_widget; +#endif + +#if IS_ENABLED(CONFIG_CUSTOM_WIDGET_OUTPUT_STATUS) +static struct zmk_widget_output_status output_status_widget; +#endif + +#if IS_ENABLED(CONFIG_CUSTOM_WIDGET_LAYER_STATUS) +static struct zmk_widget_layer_status layer_status_widget; +#endif + +lv_obj_t *zmk_display_status_screen() { + + lv_obj_t *screen; + screen = lv_obj_create(NULL, NULL); + +#if IS_ENABLED(CONFIG_CUSTOM_WIDGET_BATTERY_STATUS) + zmk_widget_battery_status_init(&battery_status_widget, screen); + lv_obj_align(zmk_widget_battery_status_obj(&battery_status_widget), NULL, LV_ALIGN_IN_TOP_MID, 0, 2); +#endif + +#if IS_ENABLED(CONFIG_CUSTOM_WIDGET_OUTPUT_STATUS) + zmk_widget_output_status_init(&output_status_widget, screen); + lv_obj_align(zmk_widget_output_status_obj(&output_status_widget), NULL, LV_ALIGN_IN_TOP_MID, 0, 41); +#endif + +#if IS_ENABLED(CONFIG_CUSTOM_WIDGET_LAYER_STATUS) + //lv_style_set_pad_inner(&layerstyle, LV_STATE_DEFAULT, 12); + //lv_obj_add_style(&layer_status_widget, LV_WIDGET_PART_MAIN, &layerstyle); + zmk_widget_layer_status_init(&layer_status_widget, screen); + lv_obj_align(zmk_widget_layer_status_obj(&layer_status_widget), NULL, LV_ALIGN_IN_BOTTOM_MID, 0, -5); + + lv_obj_t * LayersHeading; + LayersHeading = lv_img_create(screen, NULL); + lv_obj_align(LayersHeading, NULL, LV_ALIGN_IN_BOTTOM_MID, 8, 5); + lv_img_set_src(LayersHeading, &layers); +#endif + + lv_refr_now(NULL); + + return screen; +} diff --git a/app/boards/arm/nrfmacro/epd_screen/slave/CMakeLists.txt b/app/boards/arm/nrfmacro/epd_screen/slave/CMakeLists.txt new file mode 100644 index 00000000000..4db91eff76c --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/slave/CMakeLists.txt @@ -0,0 +1,28 @@ +# +# Copyright (c) 2021 Darryl deHaan +# SPDX-License-Identifier: MIT +# + +zephyr_library() + +zephyr_library_include_directories(${ZEPHYR_LVGL_MODULE_DIR}) +zephyr_library_include_directories(${ZEPHYR_BASE}/lib/gui/lvgl/) +zephyr_library_include_directories(${CMAKE_SOURCE_DIR}/include) +zephyr_library_include_directories(${ZEPHYR_BASE}/drivers) + +zephyr_library_sources(status_screen.c) +zephyr_library_sources_ifdef(CONFIG_CUSTOM_WIDGET_BATTERY_STATUS battery_status.c) +zephyr_library_sources_ifdef(CONFIG_CUSTOM_WIDGET_PERIPHERAL_STATUS peripheral_status.c) + +if (CONFIG_NRFMACRO_SCREEN_MARK_LOGO) +zephyr_library_sources(icons/marklogo.c) +endif() + +if (CONFIG_NRFMACRO_SCREEN_STANDARD_LOGO) +zephyr_library_sources(icons/stdlogo.c) +endif() + +if (CONFIG_NRFMACRO_SCREEN_CUSTOM_LOGO) +zephyr_library_sources(icons/customlogo.c) +endif() + diff --git a/app/boards/arm/nrfmacro/epd_screen/slave/battery_status.c b/app/boards/arm/nrfmacro/epd_screen/slave/battery_status.c new file mode 100644 index 00000000000..e4520965c68 --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/slave/battery_status.c @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include + +#include +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#include +#include "battery_status.h" +#include +#include +#include +#include + +static sys_slist_t widgets = SYS_SLIST_STATIC_INIT(&widgets); + +struct battery_status_state { + uint8_t level; +#if IS_ENABLED(CONFIG_USB_DEVICE_STACK) + bool usb_present; +#endif +}; + +static void set_battery_symbol(lv_obj_t *label, struct battery_status_state state) { + char text[2] = " "; + + uint8_t level = state.level; + +#if IS_ENABLED(CONFIG_USB_DEVICE_STACK) + if (state.usb_present) { + strcpy(text, LV_SYMBOL_CHARGE); + } +#endif /* IS_ENABLED(CONFIG_USB_DEVICE_STACK) */ + + if (level > 95) { + strcat(text, LV_SYMBOL_BATTERY_FULL); + } else if (level > 65) { + strcat(text, LV_SYMBOL_BATTERY_3); + } else if (level > 35) { + strcat(text, LV_SYMBOL_BATTERY_2); + } else if (level > 5) { + strcat(text, LV_SYMBOL_BATTERY_1); + } else { + strcat(text, LV_SYMBOL_BATTERY_EMPTY); + } + lv_label_set_text(label, text); +} + +void battery_status_update_cb(struct battery_status_state state) { + struct zmk_widget_battery_status *widget; + SYS_SLIST_FOR_EACH_CONTAINER(&widgets, widget, node) { set_battery_symbol(widget->obj, state); } +} + +static struct battery_status_state battery_status_get_state(const zmk_event_t *eh) { + return (struct battery_status_state) { + .level = bt_bas_get_battery_level(), +#if IS_ENABLED(CONFIG_USB_DEVICE_STACK) + .usb_present = zmk_usb_is_powered(), +#endif /* IS_ENABLED(CONFIG_USB_DEVICE_STACK) */ + }; +} + +ZMK_DISPLAY_WIDGET_LISTENER(widget_battery_status, struct battery_status_state, + battery_status_update_cb, battery_status_get_state) + +ZMK_SUBSCRIPTION(widget_battery_status, zmk_battery_state_changed); +#if IS_ENABLED(CONFIG_USB_DEVICE_STACK) +ZMK_SUBSCRIPTION(widget_battery_status, zmk_usb_conn_state_changed); +#endif /* IS_ENABLED(CONFIG_USB_DEVICE_STACK) */ + +int zmk_widget_battery_status_init(struct zmk_widget_battery_status *widget, lv_obj_t *parent) { + widget->obj = lv_label_create(parent, NULL); + + lv_obj_set_size(widget->obj, 43, 15); + + sys_slist_append(&widgets, &widget->node); + + widget_battery_status_init(); + return 0; +} + +lv_obj_t *zmk_widget_battery_status_obj(struct zmk_widget_battery_status *widget) { + return widget->obj; +} diff --git a/app/boards/arm/nrfmacro/epd_screen/slave/battery_status.h b/app/boards/arm/nrfmacro/epd_screen/slave/battery_status.h new file mode 100644 index 00000000000..b87e87ee6d4 --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/slave/battery_status.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include +#include + +struct zmk_widget_battery_status { + sys_snode_t node; + lv_obj_t *obj; +}; + +int zmk_widget_battery_status_init(struct zmk_widget_battery_status *widget, lv_obj_t *parent); +lv_obj_t *zmk_widget_battery_status_obj(struct zmk_widget_battery_status *widget); \ No newline at end of file diff --git a/app/boards/arm/nrfmacro/epd_screen/slave/icons/customlogo.c b/app/boards/arm/nrfmacro/epd_screen/slave/icons/customlogo.c new file mode 100644 index 00000000000..77640717994 --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/slave/icons/customlogo.c @@ -0,0 +1,87 @@ +#include "lvgl.h" + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_CUSTOMLOGO +#define LV_ATTRIBUTE_IMG_CUSTOMLOGO +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_CUSTOMLOGO uint8_t customlogo_map[] = { + 0x00, 0x00, 0x00, 0x06, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xdb, /*Color of index 1*/ + + 0x1f, 0xdf, 0x1e, 0x7f, 0x00, 0x00, 0xfe, 0x00, 0x00, + 0x1f, 0x9f, 0x3f, 0x7f, 0x00, 0x07, 0xff, 0xc0, 0x00, + 0x06, 0x18, 0x30, 0x1c, 0x00, 0x1f, 0x43, 0xf0, 0x00, + 0x06, 0x1f, 0x3c, 0x0c, 0x00, 0x7c, 0x00, 0x7c, 0x00, + 0x06, 0x1f, 0x1f, 0x18, 0x00, 0xf0, 0x00, 0x1e, 0x00, + 0x06, 0x18, 0x03, 0x0c, 0x01, 0xc0, 0x00, 0x07, 0x00, + 0x06, 0x1d, 0x37, 0x18, 0x03, 0x80, 0x00, 0x03, 0x00, + 0x06, 0x1f, 0x3e, 0x0c, 0x03, 0x00, 0x00, 0x03, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x01, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x3c, 0x00, + 0x07, 0xff, 0xff, 0xf0, 0x1c, 0x00, 0x00, 0xf8, 0x00, + 0x3f, 0xff, 0xff, 0xfc, 0x18, 0x00, 0x01, 0xe0, 0x00, + 0x7d, 0x55, 0x55, 0x5e, 0x18, 0x00, 0x07, 0x80, 0x00, + 0x60, 0x00, 0x00, 0x06, 0x18, 0x00, 0x0f, 0x00, 0x00, + 0xe0, 0x00, 0x00, 0x07, 0x18, 0x00, 0x3f, 0x40, 0x00, + 0xc0, 0x00, 0x00, 0x03, 0x18, 0x00, 0x7f, 0xff, 0xa0, + 0xc0, 0x00, 0x00, 0x03, 0x18, 0x00, 0x01, 0x7f, 0xf0, + 0xc0, 0x00, 0x00, 0x03, 0x18, 0x00, 0x00, 0x01, 0x70, + 0xc0, 0x00, 0x00, 0x03, 0x18, 0x00, 0x00, 0x00, 0x60, + 0xc0, 0x00, 0x00, 0x03, 0x0c, 0x00, 0x00, 0x00, 0x70, + 0xc0, 0x00, 0x00, 0x03, 0x0c, 0x00, 0x00, 0x00, 0x60, + 0xc0, 0x00, 0x00, 0x03, 0x0e, 0x00, 0x00, 0x00, 0xe0, + 0xc0, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, 0x00, 0xc0, + 0xc0, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe1, 0xc0, + 0xc0, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe3, 0x80, + 0xc0, 0x03, 0x80, 0x03, 0x01, 0xc0, 0x01, 0xc7, 0x00, + 0xc0, 0x01, 0x80, 0x03, 0x01, 0xe0, 0x01, 0x8e, 0x00, + 0xc0, 0x01, 0xc0, 0x03, 0x00, 0x70, 0x03, 0xbc, 0x00, + 0xc0, 0x00, 0xe0, 0x03, 0x00, 0x3e, 0x07, 0x78, 0x00, + 0xc0, 0x00, 0x60, 0x03, 0x00, 0x1f, 0xff, 0xe0, 0x00, + 0xc0, 0x00, 0x70, 0x03, 0x00, 0x03, 0xff, 0x80, 0x00, + 0xc0, 0x00, 0x30, 0x03, 0x00, 0x00, 0x5c, 0x00, 0x00, + 0xc0, 0x00, 0x38, 0x03, 0x00, 0x00, 0x18, 0x00, 0x00, + 0xc0, 0x00, 0x1c, 0x03, 0x00, 0x00, 0x38, 0x00, 0x00, + 0xc0, 0x00, 0x0c, 0x03, 0x00, 0x00, 0x70, 0x00, 0x00, + 0xc0, 0x00, 0x0e, 0x03, 0x00, 0x00, 0x60, 0x00, 0x00, + 0xe0, 0x00, 0x06, 0x07, 0x00, 0x00, 0xe0, 0x00, 0x00, + 0x70, 0x00, 0x07, 0x0e, 0x00, 0x01, 0xc0, 0x00, 0x00, + 0x3f, 0xff, 0xff, 0xfc, 0x00, 0x01, 0x80, 0x00, 0x00, + 0x1f, 0xff, 0xff, 0xf8, 0x00, 0x03, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x01, 0xc0, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xc0, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xe0, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x70, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x30, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x38, 0x00, 0x38, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x70, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1c, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0c, 0x00, 0xe0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x01, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x07, 0x01, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x03, 0x80, 0x00, 0x00, 0x00, + 0x1e, 0x78, 0xe6, 0x7b, 0x87, 0x00, 0xe3, 0x9f, 0x00, + 0x13, 0x4d, 0x32, 0x49, 0xc6, 0x00, 0xf3, 0x9f, 0x00, + 0x02, 0x08, 0x26, 0x1c, 0xce, 0x00, 0xf7, 0x98, 0x00, + 0x06, 0x38, 0xe2, 0x18, 0xec, 0x00, 0xf7, 0x9f, 0x00, + 0x04, 0x0c, 0x32, 0x20, 0x78, 0x00, 0xdd, 0x9f, 0x00, + 0x08, 0x0c, 0x30, 0x00, 0x78, 0x00, 0xdd, 0x98, 0x00, + 0x1b, 0x4d, 0x32, 0x20, 0x30, 0x00, 0xc9, 0x9d, 0x00, + 0x1f, 0x79, 0xe2, 0x20, 0x00, 0x00, 0xc1, 0x9f, 0x80, +}; + +const lv_img_dsc_t customlogo = { + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .header.always_zero = 0, + .header.reserved = 0, + .header.w = 68, + .header.h = 62, + .data_size = 566, + .data = customlogo_map, +}; diff --git a/app/boards/arm/nrfmacro/epd_screen/slave/icons/eye_of_horus1.c b/app/boards/arm/nrfmacro/epd_screen/slave/icons/eye_of_horus1.c new file mode 100644 index 00000000000..455fbc75b47 --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/slave/icons/eye_of_horus1.c @@ -0,0 +1,85 @@ +#include "lvgl.h" + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_STDLOGO +#define LV_ATTRIBUTE_IMG_STDLOGO +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_STDLOGO uint8_t stdlogo_map[] = { + 0x00, 0x00, 0x00, 0x04, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xed, /*Color of index 1*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xfe, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x7f, 0xff, 0xfc, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xc0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, + 0x00, 0x00, 0x00, 0x03, 0xff, 0xd0, 0x01, 0x7f, 0xfc, 0x00, + 0x00, 0x00, 0x00, 0x1f, 0xfc, 0x00, 0x00, 0x03, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x3f, 0xe0, 0x00, 0x00, 0x00, 0x3f, 0x80, + 0x00, 0x00, 0x01, 0xff, 0x00, 0x00, 0x00, 0x00, 0x07, 0x80, + 0x00, 0x00, 0x0f, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0xbf, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0xff, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x5f, 0xc0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xfc, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0x80, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xc0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7f, 0x0f, 0xff, 0xfb, 0xe0, 0x00, + 0x00, 0x00, 0x00, 0x03, 0xf8, 0x1f, 0xff, 0xf9, 0xf0, 0x00, + 0x00, 0x00, 0x00, 0x5f, 0xf0, 0x0f, 0xff, 0xf8, 0xf8, 0x00, + 0x00, 0x00, 0x2f, 0xff, 0xe0, 0x0f, 0xff, 0xf8, 0x7c, 0x00, + 0x07, 0xff, 0xff, 0xff, 0xf0, 0x0f, 0xff, 0xf8, 0x3e, 0x00, + 0x0f, 0xff, 0xff, 0xff, 0xfc, 0x0f, 0xff, 0xf0, 0x1e, 0x00, + 0x0f, 0xfa, 0x80, 0x02, 0xff, 0x8f, 0xff, 0xf0, 0x1f, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x1f, 0xf7, 0xff, 0xe0, 0x0f, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xea, 0xbf, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbf, 0xff, 0x80, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x3f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf8, 0x3f, 0x00, 0x00, + 0x01, 0xc0, 0x00, 0x00, 0x00, 0x07, 0xf0, 0x3f, 0x00, 0x00, + 0x07, 0xf0, 0x00, 0x00, 0x00, 0x0f, 0xe0, 0x3f, 0x00, 0x00, + 0x0e, 0x30, 0x00, 0x00, 0x00, 0x3f, 0x80, 0x3f, 0x80, 0x00, + 0x0c, 0x18, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x3f, 0x80, 0x00, + 0x19, 0x08, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x3f, 0x80, 0x00, + 0x19, 0x10, 0x00, 0x00, 0x03, 0xf8, 0x00, 0x3f, 0x80, 0x00, + 0x19, 0x10, 0x00, 0x00, 0x07, 0xf0, 0x00, 0x1f, 0x80, 0x00, + 0x19, 0xe0, 0x00, 0x00, 0x1f, 0xc0, 0x00, 0x1f, 0x80, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x7f, 0x80, 0x00, 0x1f, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x01, 0xfe, 0x00, 0x00, 0x1f, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x07, 0xfc, 0x00, 0x00, 0x1f, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00, 0x1e, 0x00, 0x00, + 0x03, 0x80, 0x00, 0x7f, 0xc0, 0x00, 0x00, 0x1e, 0x00, 0x00, + 0x01, 0xf0, 0x07, 0xff, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, + 0x00, 0x7f, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, + 0x00, 0x1f, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, + 0x00, 0x02, 0xfa, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t stdlogo = { + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .header.always_zero = 0, + .header.reserved = 0, + .header.w = 78, + .header.h = 60, + .data_size = 608, + .data = stdlogo_map, +}; diff --git a/app/boards/arm/nrfmacro/epd_screen/slave/icons/eye_of_horus2.c b/app/boards/arm/nrfmacro/epd_screen/slave/icons/eye_of_horus2.c new file mode 100644 index 00000000000..a04d632acdb --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/slave/icons/eye_of_horus2.c @@ -0,0 +1,95 @@ +#include "lvgl.h" + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_STDLOGO +#define LV_ATTRIBUTE_IMG_STDLOGO +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_STDLOGO uint8_t stdlogo_map[] = { + 0x00, 0x00, 0x00, 0x04, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xef, /*Color of index 1*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0xff, 0xf8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0xff, 0xea, 0xff, 0xc0, 0x00, 0x00, + 0x00, 0x00, 0x0f, 0xf8, 0x00, 0x0f, 0xf0, 0x00, 0x00, + 0x00, 0x00, 0x3f, 0xc0, 0x00, 0x01, 0xfc, 0x00, 0x00, + 0x00, 0x01, 0xfe, 0x01, 0x7d, 0x00, 0x3f, 0x00, 0x00, + 0x00, 0x07, 0xf0, 0x1f, 0xff, 0xf0, 0x0f, 0xe0, 0x00, + 0x00, 0x3f, 0xc0, 0x7f, 0xff, 0xfe, 0x03, 0xfa, 0x20, + 0x15, 0xff, 0x03, 0xff, 0xff, 0xff, 0xc0, 0xff, 0xe0, + 0x1f, 0xf8, 0x0f, 0xff, 0xff, 0xff, 0xf0, 0x1f, 0x80, + 0x07, 0xe0, 0x7f, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, + 0x00, 0x01, 0xff, 0x7f, 0xff, 0xfc, 0x7f, 0x00, 0x00, + 0x00, 0x0f, 0xf8, 0x3f, 0xff, 0xf8, 0x1f, 0xe0, 0x00, + 0x01, 0xff, 0xe0, 0x1f, 0xff, 0xf8, 0x07, 0xfe, 0x00, + 0x01, 0xff, 0xc0, 0x1f, 0xff, 0xf0, 0x01, 0xfc, 0x00, + 0x00, 0x7f, 0xe0, 0x0f, 0xff, 0xe0, 0x03, 0xf0, 0x00, + 0x00, 0x7b, 0xf8, 0x07, 0xff, 0xc0, 0x0f, 0xc0, 0x00, + 0x00, 0x00, 0xfe, 0x01, 0xff, 0x00, 0x3f, 0x80, 0x00, + 0x00, 0x00, 0x3f, 0x80, 0x00, 0x01, 0xfe, 0x00, 0x00, + 0x00, 0x00, 0x0f, 0xe0, 0x00, 0x07, 0xf8, 0x00, 0x00, + 0x00, 0x00, 0x03, 0xfc, 0x00, 0xbf, 0xf0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xe0, 0x00, 0x00, + 0x00, 0x1f, 0xc0, 0x07, 0xff, 0xff, 0xc0, 0x00, 0x00, + 0x00, 0x3f, 0xf0, 0x00, 0x00, 0xff, 0xc0, 0x00, 0x00, + 0x00, 0xff, 0xf0, 0x00, 0x01, 0xff, 0xc0, 0x00, 0x00, + 0x00, 0xff, 0xf8, 0x00, 0x03, 0xff, 0xc0, 0x00, 0x00, + 0x01, 0xff, 0xfc, 0x00, 0x07, 0xef, 0xc0, 0x00, 0x00, + 0x01, 0xff, 0xfc, 0x00, 0x0f, 0xdf, 0xc0, 0x00, 0x00, + 0x03, 0xef, 0xfc, 0x00, 0x0f, 0x9f, 0xe0, 0x00, 0x00, + 0x03, 0xef, 0xfc, 0x00, 0x1f, 0x1f, 0xe0, 0x00, 0x00, + 0x03, 0xe7, 0xfc, 0x00, 0x3e, 0x1f, 0xf0, 0x00, 0x00, + 0x03, 0xc7, 0xf8, 0x00, 0x7c, 0x1f, 0xf8, 0x00, 0x00, + 0x03, 0xe3, 0xf8, 0x00, 0xf8, 0x1f, 0xfc, 0x00, 0x00, + 0x01, 0xe1, 0xe0, 0x01, 0xf8, 0x1f, 0xfc, 0x00, 0x00, + 0x01, 0xe0, 0x00, 0x07, 0xe0, 0x1f, 0xf8, 0x00, 0x00, + 0x01, 0xf0, 0x00, 0x0f, 0xe0, 0x1f, 0xf0, 0x00, 0x00, + 0x00, 0xfc, 0x00, 0x3f, 0x80, 0x1f, 0xe0, 0x00, 0x00, + 0x00, 0x7f, 0x00, 0xff, 0x00, 0x0f, 0xc0, 0x00, 0x00, + 0x00, 0x7f, 0xff, 0xfe, 0x00, 0x0f, 0x80, 0x00, 0x00, + 0x00, 0x1f, 0xff, 0xfc, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x00, 0x0f, 0xff, 0xf0, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x03, 0xff, 0xc0, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t stdlogo = { + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .header.always_zero = 0, + .header.reserved = 0, + .header.w = 70, + .header.h = 70, + .data_size = 638, + .data = stdlogo_map, +}; diff --git a/app/boards/arm/nrfmacro/epd_screen/slave/icons/marklogo.c b/app/boards/arm/nrfmacro/epd_screen/slave/icons/marklogo.c new file mode 100644 index 00000000000..711e91938ec --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/slave/icons/marklogo.c @@ -0,0 +1,45 @@ +#include "lvgl.h" + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_MARKLOGO +#define LV_ATTRIBUTE_IMG_MARKLOGO +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_MARKLOGO uint8_t marklogo_map[] = { + 0x00, 0x00, 0x00, 0x06, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xc4, /*Color of index 1*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x10, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x87, 0xbd, 0xe0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0d, 0xef, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0d, 0x7b, 0x20, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0f, 0x7a, 0x60, + 0x3f, 0x00, 0x00, 0x00, 0x00, 0x06, 0x08, 0x5b, 0x40, + 0x70, 0x00, 0x00, 0x06, 0x03, 0x06, 0x18, 0x4b, 0xc0, + 0x60, 0x00, 0x00, 0x1e, 0x0f, 0x87, 0x00, 0x00, 0x00, + 0x3c, 0x18, 0x04, 0x3e, 0x1f, 0x0f, 0xc0, 0x00, 0x00, + 0x0f, 0x8b, 0x84, 0x78, 0x3e, 0x1c, 0x60, 0x00, 0x00, + 0x01, 0xdb, 0x0c, 0xc0, 0x30, 0x1c, 0x20, 0x00, 0x00, + 0x00, 0x4f, 0x98, 0x80, 0x60, 0x08, 0x60, 0x00, 0x00, + 0x55, 0xdf, 0x30, 0x80, 0xc0, 0x5f, 0xc0, 0x00, 0x00, + 0xff, 0x1d, 0xb0, 0xff, 0xff, 0xdf, 0x00, 0x00, 0x00, + 0x00, 0x09, 0xc0, 0x54, 0x2a, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t marklogo = { + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .header.always_zero = 0, + .header.reserved = 0, + .header.w = 67, + .header.h = 20, + .data_size = 188, + .data = marklogo_map, +}; diff --git a/app/boards/arm/nrfmacro/epd_screen/slave/icons/pictures/beer.png b/app/boards/arm/nrfmacro/epd_screen/slave/icons/pictures/beer.png new file mode 100644 index 0000000000000000000000000000000000000000..7b2376badc5034fb7661a6ee615247769f6315da GIT binary patch literal 1092140 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 literal 0 HcmV?d00001 diff --git a/app/boards/arm/nrfmacro/epd_screen/slave/icons/pictures/communism1.png b/app/boards/arm/nrfmacro/epd_screen/slave/icons/pictures/communism1.png new file mode 100644 index 0000000000000000000000000000000000000000..15d22c87eae6429cbb470d3fb4e5560281e6cd3d GIT binary patch literal 251466 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

BoLX8v}t8X*Z~#WQhaA1yLRsG34dzZ zqvj1*nzB*FkvC)nTq|)q0JDg>dZ6`69gAH=dw#a>LZRXu(mG}wT#YImzSM|{R-}Xa z@4F;~o#clXajE3z3qFx^-rfZ<&JXo6PXYM8Db}c$`3TUM2C1l2nSFb&<)43Yc_M9R zJ)Awz$+m2#ofkYgcXO-K;Rlq;-hnnfx{?zp7jpAjx&(pX4_iujm;lg0s}% zlCvDtulRh0kd8<)`%`ihLaoH;&A6MapK)8WXZj0GD*>P0~Csse{B3otKWem@0Z@~VF;KlEB z26oj+0cV#FUA|2lfvWMqVn#}XAjs|B0T*O2+yTotl2;m*NVS>I?sCz}$L+sEpC6d9 z%QdTK-IoEgj(O(6y8)x-C&oHZ3=}t~Q)Ps~x<* ztIvVbQPKT%LR0=;CEc?kaLRPiuPyopbCgWc+XlD5D8exe&TzzSCtfp%RDCu}AU*VEom-)L%#i2YBn=sB(v9kYuP96EyZxqep znf~$DAClGXBZ;9%9&K3Cl-(WnWDVLBejK;mR`;aP zpF!g;4AUFHxw}f-AFUPDssihDR|eRd(h2_9+XEv5-~hKp{I)(mC z%^x2bLMj{t{tW5!-CEQSDSo}lLC_d9do|;*`O&GB;6a=N8vPsg0TG+|0^CLOvXw$_PWA>W)+U6$*MZeue6#mMms%)xB^J}b!h zUj`bCl=BjT3eC6E-T5aKOf=ZKo-w|te?JGaOM3fm zz5*~XsJ;P!Y=3-2AS-^arPmh;Y}4FTh-~8d5^{)) z5PK^t%;Ms&tLV+MR)G>vXm6Vw-At6QvPgT1_hN#ae2b<4iw_KRW5ESQ=xX+H`!_U6 zg{gM+6X`ZiS~d6C-6?R)j>r^ZLk$fK_qBJ;&Q=9tdrKa!tI$PG8J2Ip-c0lCRxG*u zF&BT~JdO)_DW?%1anPH1^qL->+6&|3vbi)*Rz!j-4!9}xU(1DU+DG)J2fM-(GNI%g zpn)yefh#TfYJ_=k(4}?_2zwu~_mW;_c?NGpcI)sQO`CRPA@V)ygl;5g^v+fz#^=wk zCQ0s`6#i+53XD|lh+C)dd9D(wp#e494o=XAOwWymcm-LOgi#F?0!biM=uiYt%{C~9GPnD@~lYV2-H zEs0<$`*F3ta^cn1l`Xhl=;*`#-+*+n-~&R$O`A5!V=oq8XlQM}ZP_pSsn;;P0vS+7 zj`U2McidviqOVoCa=OM;a4Tpm0>jo;#t@$QmT!kclqzU)p9oh`CO+raSJ{9*kX}lk z2OOhIKKiKT=aS^}W${o`HNhDjbq)4(?1EuphX2&F!D6a2z?Pd!yFNp2NW#fBQ>e+= z2n*~p*B*C)^_;j1HHeRxtg>nQ0N>*nt`GWu(ju6kF|9}Jx2dk*@e<~*r2J;vlXJ59HK9*C_w7o)!ax7lBD!I?%i|=y zyEDNT^e}k!vF9CRR(`A`+yToX73jsElX7jW

@;S}yqKWbonHA-H#f4uVPI$dLm; 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%0A{09 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∾F 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¥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 literal 0 HcmV?d00001 diff --git a/app/boards/arm/nrfmacro/epd_screen/slave/icons/pictures/communism2.png b/app/boards/arm/nrfmacro/epd_screen/slave/icons/pictures/communism2.png new file mode 100644 index 0000000000000000000000000000000000000000..e6f80e5b74903e06b7a5e4e5ecc517953ba955e9 GIT binary patch literal 35629 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�UV|w7x;& zMQ%}qg{^z_y7TDFT(BNrjb5nd2zbyU1(F5X(}`y&nHj98sqs?{#yX|EBPSD*46P0f zKK!Jg2xP)pxg68$n^KPA6ZXXguJ!yosD+jrIyaJZiW5MS2hzVAgyuj-G6Yy8_r->% z1Qby-YEQp-Vl>v^pf%?Y8z#ds{IiT(mRVNuAstL^U0b?}M?M@Z$(UoMM}E*+U(@`r z?;ehVfuSObCc0#G;UCX!eYw$}c2`$vd!f1T`w;{oCM$SP!eA`+>)})XD#Faw zQ7( z&QaIJw!XTSmyNBhi%?E_ORxP13=n%VlQ}u_TIWTTu(p!Ll!Kz-8Q>_(z)yVZwvAo4 zTV}qrf&>bx;`;fW>mJYUFe@@|zU2K=R&DyYAp5iW2Zx)?kQvUBjMP|LYWF-34EcLqO9lar)#9z?Iy2LJpm6*r?c14~ zqw>1foA#p9=I-Kx$^YW8xT!5r7Ge?KT` zvm2{-j93Pv_St8pTRew}BH<_(R7=85B#)NFkWOg7aUTI^=?xre-Lixs2<% zbBoZ}I(rp@Qj*=oWr`n`uU4 zJo!N6kzpxku)-L@hS9YwDj=(QN|FJpp&W)VR=mGh30(o~?eSwkhn?we;4^!95qtSM z8NoWIqOUSWrjv2j?+&?BLzz;*y6|Q`<=1);s{1+W``2qUEQP}&m7>7}@Zx%ThL)=&Y%&x~tb-hz+9;WKh}~+@ zII)9;NLg$Q1r}tZFrgG*x=F| z9+wCJ4(4UtF;4S@1l9pxoO5Pm=AUd38DksKFJwtxQ#*c{aOK7h-sG+w+5{?I3z^~- zbE4tglEg7)Y9<>wBve;D;(2zNa?J?zHq(uAdJ=`~7UECgI2LyYg4dRDa*yURG^6x( z4flWf8y^~=)?T%g-(jrNFX*`Y-LI#*lB|w@H%xoj42FtJA+p+1`8AXczLso6mSgwN zm%CoI!@{7{Ao=+%)p)VSJWw|+RmR7%>}kv>an1scfniy2B5!^LeUv(LwEwGPD7#)I{nW2ejce3Lmg!zqg|zq7xU|L9c9Zp z`R?)33}n2{<%73RIB$2`J#71fDFEuFw`;oJr7I$~Fu+NneuyNB1j?*GRY}`HNBZ^p za1-ZFP(1|k<+k(ItY1&8IQss+zcOzvMXpS60BHza?#ApH_R6F?W=B@miV0b>l0?W> zu8bNa>1SD$m>4=sVmZ~C+l=Y^UJ31*uHRHil`V7wnziG^dl^2ew=Z5y7TL+ysnyKi zNrnn%MwZ4ccV1zEj6K%a!W;NKk5F|u_h(?{aH+efKPNTrsaR`KM_iskUXJ(Wq^9%#V3B%q7De{CB%j$73INXEk}QqrRJIB(*PR6(hWK@ zA+w>(jH@)}SE#?)xxES$j{&^EV}p5Fj}pfWdWu*v1uNQ&iCsNv0e+Dx$-tANJq@%% zyY9pMmfp#F7Ij=GLJye@8o`Z>9hZ|40G$C{s=uU7nh!ge2`v4Kp+Jky7%IO96PV{0 zHN?9I(umLI0UaJc<`ZI7cK<-*v~{$t9T*f$qf2qbl(`zW->qxyfieQhD$ILgWcyfo z5C6<}eRg@1t7vt89??E3<^z6MH%{yOR$H0;gObAy%HwFBuc177j>~C5_e(z72QfjU zUgO5eKZCT73RQvBy<1+UmQgh-l+eDK;f7})jhg_8_?`0%rRfV!Z=nm=J3A-2Yuo6O zzn>Exiv~5TqD*fEXSv;|{Q-Mbg!iJ|&X08eScV(r+@LzBsoPE-z^ z$t(~EN627uoY_laph^Norfz!N(AB?Q{b+w$ce^AD^Hz=?qnWj-`!vIwL^oa1ym<%V zvxyw5)S#npylg$hNE-iJ?X**Xoeql@S#ufN2Cz@%;Gi2=Tu(^mcpJ{#%u2T}{aqeR zymXU^xO7(DE@x0P?V~pEXJ_qQ)?Dc+0GMXBv@jQtnVE^rC=1*e#65WLYe(0iDT4ug~8}AxFoexhB`Wa+%jN?BSgBlnG zjQ{oVY5g$0dhr*o=5-7SSz=@fn!gk=$JO9ZT1DB!S+T62KHsRSV(a0hmpLB)@k??$ zpa4O6Q|(=yQx-2?ypEI=1oU{ch?RL9zNd%XQ8JAYMy*5oaN1ih^DXlbu1;e)+sk4R zl0UtHSh{&A8;-A&%o0>wjk0R`LYJzchsjtox566E1t)q_puQUwiD-bj&X8x1X=z}4 zWULY3U*e|NtKT$jrPj)|YrD}_x*VLUb$t0!ZJmsugod3zNHC{I?W*bKYX@41HHuK# zsg{d=C^Z#7kW0;e1r-TskE8xaY&9Gx)9(L>zkXPLl+&dN0S7nyK&yVg-Y=8(6W z4~E@WGM|!&F=Z)qAI?Y@-*R`kjz=z5bTCzl7m&AybJEx|uT54IQi#y;sE+b9v)(MK z-Ha`v&axAm`5dL_28#wZ5~Hz|v~FZNR`@@qfm9tYj~a8niux`U)k~b$SaA2^nS<`M zwRg0{%l;BCa8&s_XPQ>pawvaY)W4hDgblb64S1;I+X6_P$EnKoSK2@77ek)@@MijXuA*h!*nb+)5N5G{mo-tDXPp6z1V<{MW8 zPwrK>Zr=H70WV13Z&0`2ITO;4`@FG3Xh&p_obQ| z)qB0jSVu8AuL@K~cAZ|-Q@5;@yT9xX1{;@Aa$!q&{1jc^O9Ct6n%&f$8>e%*U+$0B z?6i-x^&>uOOrg=ay`;h;SpqDSOn%46nA|jqmzs+Kl+V6l&lTLjj!mB?#{tjGbs>}0 z%x-P)JYgS%aZEc{s{$@j)fh_#a0lls{kATl%gnhSGOkHwV3A_-e%_+?{;$o?&WiNM z=HDAbLSoZ5DW#q+Z!{qs#4CTy_G?<9x#X!HBf<1`xb$24%!GYHzS&LyLg*y`@xNra zl8A>HYtceyoPzaHfpR&KRVG8r_rKeg$uHECKK(9c49jn9=J0cb{wqjC!Oz4xij%zh zLl_kTy6#1wIeHdPZWiiyac(;yBJQ@z?`YlpA}j^+MAE>bP#Cn*MKI< z`x>$&31{|n_j(#>+ZYPvkh0y8hA1~tWJ9_mFNd9#Z8=WYeF4`Aq;-G^ZpEF&NJ&PU zEkaIF@SCi<+iRh*&->uT6CLNpRq2xbY*rvwNn*We z!7Fv_@%fFF-MM(_%kNQr=LC(_9lb<;PcyJaDXz(WYQqs8!zvzA;cBg1+ryP-(?37H zySB1htQD5`jt=O|>5F-0MkUa$Y|G@S$sBVM>_;{zoim9vUB!4Q6DW?RuE%Cc1t5*(SNc_k>aQdMPzukw#=S^v zA1bJWcKi*kYsL80 zs9l(wJTxU*)edbo7no|L8>%lRGJ;^;*gfoO|E5Gic7@+{dfcN=EwH@pH7s{zi>ZcT zo|URHqXv9!YvMx`dGOsA_4_J;ow@#PF<`h(8h=p7L-#ixBhgV|B~GMDJV%@tE6+&E zTWDCyFdDMX+5gP{MBq}XkD#z4u(NNZn+DINMDD2$=I5yXcZ z!W4?sa4Tx(9eP&pE2}w%gAj&}QM9~0skg*N{C~efR17zUMxFsExy~%e@J`7P-;f@` zrQ^h(iyyI8i;Wy1StPL9eH82E;(QfEr#>Pw60E~xfW$hWrPbTFx41pV4;XYSyGyJ% z#W|1_yiNMyzPztLn>M|Cwz;yc$_2yBC+3*Z0vTsn#%LGL9Um@t#jGE>tFu&Z;+Q6N z>cS;IbQI@oLgWS&>H^W?ml_=PyeBJw{XsA3ADN=Umh=k;Ic3iIaKM zS!*`5&so=Gw^bEIXEfUk^Z5Qtetoe08Dd;RsZ&5*0)e1Olb(S_%kS1K@?x*a2;d`x zK`yPQB)mQ}5II&IoskMTkQslCl%y48*N2&0Q%!YdZ{A>r_~+{GgQ4rhWI`Yn*++~I zp0#0^j#g%WP@e!2lND22Wv*M>yifdqx2>mlWsAPT>@i*gzAH`5Sw=GIg$n1%AT1@C zEgD#tFbp^QjNiV5u^0{_Brhe{iCM2xQ~-c=^0xns`YL-!ne*tx0}ep=MWFBP-5N z@mB?Fi3Ek5q=@5w9uK{@{~ZR8u^(^|ez`tj zyg~|GO_Su5lPv|1UfOJQ2)n?|Rw3rYFLa~ie%t0h-(F=Z!xQA0aPcm@G#ajZD;)#oxDwTpB?s4huwV@+~?Z?{t*K&QW z6N_1wn{YW@z}WjaIe~jvw~k4$$_jjPNJnRS^C$WR_1>wq=_-C<8n5P|Cv=1PE zQKq)?8?+&PHoa?r`wvvF-u@g*9o>HeMhSM7YCa?r$~_rq>?P%KT|E#^(i+t>EA78@Ngat zs;X@0%m6!^@Ybe%f44(>7}4JxhwE5~eXEGReqZ`27PV;mPY3qZxNVO< zWOBQ+^N{2ve^dUYU>hB~y|p-r16axTN)VvAzG<$`@j0(FcOVHGZT@7QiYd$msc<&W z4XLV(mxnriQO;c$@f$ibg{cfmiBQ-1g)8sNR}8Ai?`cOn_zHGn%od zGDTZR7Xmt5?yIZpw}7aX56Gl>lvS%@KHtVaXZNb0rm;a)(okFi% zK$-17Zoh}=0v*B*h)0uN17fa*X}a&hWtqrHGh)`)QV5Pp>>fBNA}Jp;!?1=jNyH56 zyxv+v9GFQzas*RcI4uUP{=FDH0V-NF%xC#du^f!(yLiF9Gcz%9T>0?2&T++3C7Uh;8NN&VwiC;Osn-lPnfWzg=Kp z8r98UfbDZlKD3w+QXXW<&<)PSSWfXMQAyM1%$lhcdT$qNvLWadU%nBjZ}$DiN|b+3 zW>$j04An?DbaxD;T2=x$giYZMeRI9zxGj6LhlZtfCT zY$J1hd@hDBjclMC>-HZxQC89L)SLJX>^>oj7^Zt=-DR_v4C@lwEFVU$A6=N(TuBu2 zP7$pO>HZcqd2Tj5<&m3YR5DIOa9A*zyXaO&$Xqat^5)#zT~!I3D60v=+%uUL13_-q zx(ElJ#(j<+Hz}Wp9DFY6JuQceU}b7zaMV;~`ZKYg=g6k>XEHc_N$)8C0O<$E*E>>IMaX)=&mZ_epx@Oei#7j2A; zLhBnSnm6?P_3QS$*C-a<%<&PB4v$Oo?iS%)D6(eIz)7uJFhk{!;95E!3rWe+OYR&p zg^?P$(|vbSwPD8~;fE0iDu+OY`TqqZSyoxF?CMdvBi?0c2;p>^~{m7p5W$RBowKkf{j~dT}5qpcTq+8 z*A&TQMKLv8$!qJHv{r@$Ncsqnzok$q6n&!``vXRmJ*F!5SQ8)%+5UP%$PTxGdnvAx zd1Ceq1z}~WsmX1Co}ZG_H{>dvrz-(VA!jzZ@Z);h{A*x~^JM}H6<$mf)ab@QFCdTG zb7pF#RU<)oo~?RcF*rHQOfF%l_zcv}JSd$bCeevEw>{U=D_1M+FxvaQOhE=Sj1+Aw zT%4M9>%NE|i0RoXm`|d$uN-L(@dMIhW=QqCCJUshq-&Vj&$W!G(6px@yBDiQ{|7cRuXtkhxUq50N91y4aIq7X3)nNu;9r z>W2T8IG9il{45MPKyZ_pq>v%s%=V*vB61E*4y)OWIY=WYxzH zIXrpHv?T`Z}FG;8D!-%ZGRE23>O5qpdc@O#V!1;6=g>JQc$ z|AFCc2Se1?g#(1A3)YlQm*CEc&juT1*4`)0c&^>)f?~1Njo^Ds0*4)8AHck$`Kaah zG!ujQ9;uCS27LE^;zRi9()yxQn*7kA6%kLfF|g{uD&pL<^}%=>jv)o13fLr?Lk{qL zkgx|!4(4~~pfK?+Omkix*ojVSVy?~ZLwO`_!pX`7RW3s<`XAXrU~cAmHR6;eQ7 z`C@ACK9Re-%Y-R;*qqr;pDt!oZHV>di4FY?sa-B#8E;a6gN&s1nt&vnTrTpeT;zz9 z)&j7|t_KhbSbXsBBV<{Fry*p+p2U|mtpU+CHctl57SACWXIx>gEtynl;HW?cOLLiH zWyvgG1i+}yiQa@{qL4j@ppcG*$#bv1>E%~4DS)c=ATzBQOB7plh7urAU-B%nmqS+* z>w#R&{>0Md|8<1cHf?_DF1M^%;HgP8{b@{x=G4GEgU0tI89yU)CD^6+K33H|C9L41 zGaiZwSu90T9`QWE>W1r723DfDbxFNBg&61$fXI23_~uu$sa$q4En|OT5b?`yDfisR z91iO37;1-mr9QeZPI6%0P4`}5-S!4a!tG|c3A}wH;)(w2Zk$`sC*78<+eTKqxU#@u z0((#;(|%NOvaJ}4&@3RxYq|H9-a!tFQ4h)x9+?ID3{hFcmMy7PF|N=TCW6YHzj}4U zVq-noVRvSl^~6OlhyYGJuCJtUC2V+O~?E?3Grkv5$qBX z>_gh1?HnJ$<{hZh+H)HHNm=q`X*UUmla9&dsv&zgi+g~EMy;#ph%+JviOC%DGJRWS zrxRRWTp!qdBZZ30zLhQ4d^*1p%fo%WeiG^2t#Fv1j+kw|``&FHXHGsKFjsFcgD*M8 z)wDQb`V|mx=Oe`2C)p^8zSQp$@WroA#?lF884^iumyf&Wz;G?23!>@ra7aZ` zedNKMd4{22%#tn9cR=`ub`K~tB58fHcyo10oiOhdr*rA#IW~WdeOEM@QwotB{46t( z#HtM_Hbj!&E5>r#wt)yGm9n&_#y&7=*Q?iP@!AwnRtA=fffR!%l2=qL#Dn$(^Ttc# zrtjtcgMXPpy3;d1`GscOI^>;+gRP+)-JhTeGHgi~5@1;T=ZH2_Q42 zT$7wC4s8lb{tibZSq4kusS?8>@#)O(uk65Cr2W!Q+R|s?NJewC1qaAz^`s^kW=H6Y zi*kBv4i8xzV7yYb$MlxPVI{AdXA(^(n|dfyPb7t$N8))y%x(TVh(-*(9Q0U}AyCX# zt+f8NP!!+q-c=M%?13vWfIxd=W4=ci()JDx5mY^ER$gRvJ|i8FVHK#3<3XvOJgPV& zGuR$B?Af0j%!e>E{Y3n^jk4!Pr%SMPWa_etlkXqR2oHTlX$lzJTU@UxEDl}1kkt4? zGhP^lXPe%=$FLM9ukzlyq7?SV)4bTLVqVDQa~n!%miVnp-47ksw_QYaPVp`{VD#ZU zvmPQqGbCo7s@E!SPP?NcaPMfvH(FcI+pMj;@h0xwVDu=ji-UajAysk8NRnF1u4cp_evK+=lMW^(Xc8P|Ef_c~Z1&yyBb{vB>^sy$lD^cjF`a?G2w z_3H*c;7vg;n{cg8{nzx%-10uiQ++{J>_LloK!cau+xEW>r-z=(B3(@NOMU7Pc}fjP9IxI z@-_&=8O9YBd+vW1{v13KIdv>8z>KH!Y?H$@xzghA&thdsc{o5M{K)99f-ze64I?l=gEm=aO zHVzv6k|mV%f-&WitizKwDe%)2Mx&f$0X6LV`Li!U|}OLGOYi)TRi?F($$q!okj9Pwcd%A z{K2dZ!FY+!=Vd2A)p)NDCqf2V9K`$}$=t6SDjsk2E}A#SyV*TdF6##4$r6RN$fgma z8zt{jM4L6jO(pfr{@>$dQE^>&SuYHCP>k07{M@kIb8?(g&1RWWlYZ3MH0n1nb{QSM>=v^D?_IoSGf?z=Vnis2<2`u?yZ zdn>!8*;a{EtMc%j^UEt>3z6kjK6E~*z9yNZ4k zgadwNA-zIh?wLF=kLO}CY+EV{Ech;=Id*xPTOVwu#?A~;s-jHyq-S_z|9C5SKt8&W zL%?~vW^ZVLFGgsNi-Wxs;2h$~eIIaun2C=Ctt3WTDoW81DlHhYqiGYF75IHF*?Zui zep8EdUYOuNtn;oTa;#!PvbyjGs3D#`rN9;*sB-Kk%Tl5j-89>TMr7*Ld_6-hlP3}RRzUDM}=!W)a-y;(8&GNb-f2K$`i{AM_UVo^^W z^%;vsI9%CtvbA{d)BpMOua#ykhisOMPQH(737UtUpllM&zv%$nNxPPspmX|MMM|nZ z(i)a&Bh9ajiyhlx3ZaHylY`P~?Fy5jf3B+EJ$i@pVVv)$!#YpbKg zv9WGNKR%%1@5oiRqr$5|KA|XyGG)@2ha@ddlY%-H5FE-hT@J0{7Eo;40oc}qT+E|6 zz_B7XkrpSG`+|1z>rT^}dJv?cMOkd(+%F%M(9lz*S3e`9;-9XeaSy(D%oNN9H0{w&XpVa0xE}BuB3n)uEw@3`dd# zA~aGu&0zcI{@J}fj4;pmhm=3T&z1Y_3xzOz{y=6*+!qRhGxES7Wzl_Qwbm`+pzMlmjByL)D$ z;aypJVB(Gxonp?d1Zqj4j?ciYePd%+9a-kYq$3#t%yNnuLFPj<{Rnxb`p5C}e7b{s z8nbPb&?YB>-))28rxKVb$*K+(KX5YUIY_-py4q)i=3QNhQensFC5Fp#Sck3={vf)5 z4jg^nUT=+@QkUKmP6FahW-=GMwpbBY39!ko40IJ57TCTZ4cSpS{Ye_kMp5wHL8l(C zd9v|bj!ke?McHv3zKcwRk|K3B~1yfLif9+DbT5~2i%+OcCtw*yf;;=_pTT-f=~ zezKWt#0pcqK_TR(>lgKhS!6wiGuH<#S{Tj$() z!*3J0*`O&2$SMc!(pcVcF&@V9g#sLjN$uVKA~#uW`97ASG!OW<>1pFImEJV5wxR*8 zSZoq|2`GncPw#qZfq_&aTq9=VcH+1Q{x-_#o3rKN6EmNLaiMWiJjf37=py1#+FKvW z{dE@LB>SIv*SP(p)dC%eNZ56~nNv!_P@!kShFe23U(&=(ADJLL|HanexZgdmF;>p^ zA8v~W)lq~5XoEK2tbDZj`^Mgmqv=N;pk)4yUbz8oKg}=PU5cg8(Dbl-ZJG5XNjwMR zmW-0+&mLMVryNbGOjs4gDI0!?*j|9{m{$uniuo(66z;7rj|MaSNxNHEfw)yuN6E+; zTBqV>&u!64l$|V1H-1U_9hQ}ZfEChV;2gf+X8D-E`K_7=)7Xo-nM<`Lxl~MM^G>A8 zC8pbc;o5jJhN+9wwn{^oOwDsi6{tgddcF@kvY z(OX*rFo`B&Wc|*y|Ba>AsQ4+_2j3SF9-irQQ6A(pjPIBsTAcjS{FX84)huCtmRw6% zhI`gn%_A|!+9ZlahNV`c?U`18)Sv1;lpd=u38aiHnwC?AXnh2>pMk(S1YFIz=On5J zAy^N0TG^BiQI-Q5MP`-jm%9~GFwxX2&Gt@xLn71+{-22-fNXT8?KE#`1=xlP>`IP0e(ayDS4Yy4p=EM`9=*RmguLl{kT%uB*kcf9%R zjnmu6B-_ft&S^_U=uhI6i1s+Ptg%bku-r%NP?)u%iPLJ#c`3U(d&`@(}!RL z6W(728=x>eT-4X$|EfCoxSsF!kALaLZOEFkI_ad8IfNuh2-zxx=&(X^sxik@ zA~lQ3bdWZ}I;`1hOLA^ZqO@4jaI3}?I(&c6>ubC3$Il=4cF*_p`F!5*>wR6X>-BnF zSvwaO;OOiBYew`tU8T)O;3BGNV7(EAb%X2M^1RwO{AOS`q2YY_PD?f{+x5iI2X+;~ zy?~c2#03we*?#?1Cw8~=MdL_Dbfozyzn#T05`?kvco&7sAA}T@%~$w~5pzcxNSORn z#s2FOg(@V04$;Cq)mj|5H#T;GR8N2BV5{6jV!UTh9MyG4c^6NkbUf{bNs3M-a7ktA z*o|>_IQEF8=6<&Gq88oXu6^ODmrg!xLO=L`?A%*zy6HNBPY|{}#U}FrBJHO!+cJNl z5w{REGo0|uKNOo`YUP4y$=d zT3TABLuSr|aQMHsI#*otyJ3v)Gn0MFtlE-W6l>NL=Ce_=nZL2pu^^|T?GAjlLr0GM zCr7QBi?|JHE#nG30JvHz%P#Wg@mm~{ebntv=X5l+z;0NrwD7a2q_vz1k{y`c;krp- zWo5BO1~JiEuGqTYXH)wfc%8fYKaB{alhwD^dJe#Ei$1oJKa~Mck;1Cp?1=Wg-lEJW z=VnT(RrT{AaG}>Tft_^h)_*M+lA!cuKh=`+PRp2`GInQFU`;X}-C~(UXF`|}--W-- zXsiXv>K7g!epz@;^b_tV12bok>1IjB8`gW}BWX-mo;m~)RUPQa_zGo?*ao2B04(?f zTd>{2a}xhmvB3>d{21C0Q+0NE6mYx@6e%x3S;nS0D(=w0AxppSwe_5~ZAjWin8N6d zyK`L9DO+9L_Y}tTdeY>3C1uRmB`59nF3z5t86(3O5%{`%s(>h*C*uT#Z7YK?8+B2+ z9mH5nD4F&zuUTZ+G~LxGt?B@07L#-lz~{ATrR!|iCZjFr17{r6u?NnS^a9V6arIqZ zbJhL`@1#*9E0aa@EQWq}q*Qk-Q{iT1*emaLDXrSVr~cE2&HF8dUt44Y69^v4(RWnA zd<|UMI9Lu44m^KXY=$(f#m5rc5?OCvDq3S#IO*VoFuyEOFs$11a=%uzw%Mc~+8V#d zBxeuT`A9nKMAUm*9T}u#6iiM45N{Yx4Q}M(I-l7^RQj=$FeL|iJP^K^0D5%FR6^%L zisHF4#~FLsdj}eHvp1lRuOKvXqx%to#FPv#=z>y*DOug!r2;=m0DQ+&@tsLnJVg3E zogyIMQ-uRuLtG`|z!9$qXkNzFyG2k;Lz0kZV{lljv}&uM+N8m94(8trGpv|6>L`Pw zgu@B^oocuD8&F*_n@gh@$g@)zP&Ko4u9DC!=Ng|@=bMz9u2)Fi8XKEmAKFX#ga)NQp$`XpV-EN48f_Gj z5piT^ZvX}!;f$2qVe4$5f9FmEh!bz z%nVRfuG`)TBwuPye9s@PZy2@UE6>G=;a`eu!1bjwiTldY z^oNmqAe+sAT3rt}z3})%H4bMH(+GtiH1VK}D1X=JxBm=Y%e5ry*#O6nTH4gob%M8X z44e>dFDqy5G_!P3%ZLC-lH;!fKq)MTl0`9TL;7#oTN`_1$y4 zKl)Cv{N29{qo^07^~*=?tQ$-Xj(a90^0h+t6Qr?4?U;v}YbX)`+2Wi`HwTw`@D~QM z9*l<`t#<+5QMJ^2_S0{^wz{}ydwcr`I!2@q2DBg(X}!-7Jy%&-LtNR(|5o~aX3=HU zCxLt)la?FHU7gNY4F%UB>#{HD2r{gQJZOvu)hot-t00iQ>&3jCJ_5##U3xssTq`>l zqpffP9jkpb5_=VWLi%A;(?V&IwiPI1!pUuG$uZ}pP1g{+kj6x{S>pN+cJTY=$6Ym7 zzeJAU@b(YACl%QrHgyznwPS-j+ruk3BjjXkG+eZW3i=krNcEr9n~7Q{1*RtO|E=uL zcvN3M67)gUhJ)rQqcf;u2Delk6jWNwR$8xqt`8PQrTRMc?o`7&ZwKrg&N-9lu;p!Y zva*M><%3Ba8gzO-R=JwR{2uam_4eue>vP06NN@&uMAXRNU~newc6VGau+?~7GV(s~ ztgBrkpmBCX@+Q|G+Zyd^Ooo0bA^Qakp%?A`AIgM}+bR#!;F`pJ6}1JadriF zQD~pyx9OA2+!ssqo2PRgyPxzhGGahaN!P)g zXqZvp?{L=CQ%KZzi2qyhh_OtU zf7{v=6xFb3)5eX?uuPy*e4%!xJ9VoYa7V&BeVg@%43v z+0jW0|5$(}SuPF`HD(W-%>Ayt&arVcyon_70|HyVZU6HSr5P#uDQECyYG0v^#lhsw z4-ZNjTv~NT(2oI@{+dlq9;tl340F}O^Ha!V^^K3Q?+D{s)O+||{~w>wdoJ9czC#zk z@*a(|%*Y>=|8bEB%vT-&SqAhokf{a8rY`xEkKa{yo}t+Ol!}E*J%1Cn9yS)|i{|ce z2X$uMdT{pC;}uhQJZ(BwBhXtCrmZ+!%(|OLzJWtZA35CR#!s(X#(dB<#6>)6J=R2UcEyMDE&iskK8x{L$h7G%Wx~Yyq7)crrr+6xHJ%_`UZSg` zBKOtV&qo?4{v`I}uZ|G;3SY&78FP~6v5KM0<#-^8hgo~~^juQRIaoQkQ)fbS8kEM1 z%p*ECv#Q3leb&wG>l9aT1(zc>ajma^b$8}4TW!a;H%u}J(2x3D33FKo`k$yu#3X5rXg*g@5rMGV7c;_Vb7a}jt&5| zubYs3F?xojH=cjF2wexz;`d^>c-SBf4I|ovFhF+Ir|QR}dsxQ^V*FFY^&y1N3ihPe z7OGQmV$Nh4=^57BK3xJAC6fW7*JYpN3`>vKF<-XZ^h{@%9I7NpFC1p;^{pXEI>J;w zdd&Oyt=IElHa6e3-aSv@9LxZUR;Q1y%tHUR##a&DqlXBi5P~?F4Ur{xmjl=IHI>Mook$pSo+t1Y&BT zUa>EI>63Qb0? zk0RdQgQK*W?x7RtvF+)5Oewrb3noaVVe%aX)_hQOLX)0PBtradk4ZL*L`N8?!Ve~M zI`a0V&{Ii~-RYUDU(lbH<>Vhv4-x$5>elBIWgLPC#O=Bd(ZeU$9159mSJDuuoNoL+5~EHO2sg<&D3DpKu`E9K2F5G8f?^wjR*+bh*W&l1{Cs@*o@ zXy7dKWtYGoIxn(e2wx^rKl2c@@w7Pd>zYQoNBqA(V!oP3PjER|lBT}_alvXX78^@R zWW`!c4nTFB1iE&WTwkb(>KFQpu`G6Y!{JzS+hoYtK5^FNzDeaJlFO^WQQpGke|dP| zo7F}I)%~B5BZ>Fxg_4r(tv~c@o^D)xky4^!MamX^*|hL{Ut~obr2-~@kH)?+O6|-J z@x?j`21`8a%4uBhsC*Mkh&UH(_Y=e*@qQ9Fv|k68251?%PoYychQ7t`6B-}KyUPh7 zx3p%CR|e(iSww&i#90<#$?8jXb?@s5S33nOXf zvGuwPDqX4ys0Ha%%9lU^#AI93fA{V-<8BdX0u67EWpc!QF^ITH=2A!6l0ai@I&>=c zziPD*@k!c?{N)24&sQFL=>^a7U&7mRt=4LEz2?wMiEv!gDmwlQLkE zC-h=r4IFd(UU9Xki?v8GFg7-30UV^0@A#rtytR=-_Nui#cZ$JzVlh3a;m;r%LZwHG zmU#32r>e;}C3hq z(R0V(9G#7w7OFWlJCH2x1UhSA=i@+Jp|fz2^pgpF~)bdIYe-hEqAyqfM&r0ndALlE~fE{95aR( zvR8dsa*TWl0Dc<~^y8b1M(qr-3zWS^#a%0+`VG3>yy}H~^6}U7_Vt90$LeqCbDc-! z&Dcx(7}mH@QNw!Y3$7zpb9~HP;?SukFx0Fu#=d0DztL#Byhc&jW z^I=({S&SFP9#v;F`jLXlAjO!{F{OpPS=0ZZ>bCRb49OQIZr7MKCvs-qSP78>GYKi^ zv7g<~sd$BwXCGpYIqKd$3!~Q2sS@m!RaEA%bdu`U56RjwO{I9+xG}`XPnS*dAxpi+ z?1(-{9DHI!gW1XPKsv`6yKr9VZQFP6I;h)TZ$p8rU5;7V%JqM7)a2Hme{FEceIufw zo!KV0mkj3&HU~?2gBPcIN;NY2Yr*%spImbzZXe+zx1khtm#HmNLLQ7JHxgYY^QIL$@Nml`!(Br3Ajz;I>w8y zgpkEKE$?NHp?RpVa>}|`tA$50*->ffmzODEv>`^^99&l`6Prm*^ z(1V_kh&H`?dg!F`6tc8`1pIA@Q^TLA(?m{Yumd|@S0uZQ|DL{sF4twK^h`avobG51 zqg${e;h13T93QoLm=s^r@M1lcRKGJ z_3lF8z*Kr7%wRcCy$Tjy1JUsiG8CAJx-z!|Q!>u3c*+K6+BuLFn>!VQrW=XY>gA<9 z>yAy@v5Y>z{f3|QJv-&jrRns|`K$U%%AQrd@$#{KYPJ|NmpFFAOd7~yG<1MI&)7e@ z7;wVgrz46i;Lql!hU-ZS=w7%S%hS-8I46<|<}UKviiMMJZhoMbO|L=_W}#KUwH!WA zc@&+xG;bKuH;n7p&yoMJ%qZOH)9j$|;wOrcj8I(wovs2KK03T2*~jr)RXFpas%Dxz zCi>Xr4`=R)^a(>WR?NZi%sqBv_dwyVMs&8v-j}W)vohKbiyJtrAo>6w{b--Bpo9s&=mN-@l0LS(^H5qGWX@f>ASoDb5F2L7_OZTlL@g z75uk3Wyoh6_#|;fF@1*YYBU zZYl07`ps;;FzX6C!C|sdx}BP$_%e4c!+gb!F~amcUOZLSB)M>d>Vu)AD1z09_7Y?o zwS$MxH=seR!xaYi!zbqE;4Wh?+zNtxxt+@m6hG37ut7^nslzl26mmJ0^sN*R{DhAr zj(fzbgW%7X=*F&_wr#r>su%1oernBvM}hmhqTmeda$&Z-!AncvSe2Za9DZ%tBpngu z3C)DpDI~E}*h|)jI?C$?;4*V9n7@lK zzNVgE+XKc^EaPtHa4u%K9s3y%N|9uOPc9VGQ4}I|B}#F_Tsv+3K-Dv<KVjFn=^BLMiWV z`fWnZnI)sxWa8H)%&Oqw+`i&GG1EsRW(3_{v;$X3@;aZL#urkb`3MO-G=Xu24S;7~ zNAtp(hzP${kn%?aEThhQI0xchXRNDB?agNUt2rf|oS9r_>J*t_+f_|%Xy-Y6cxW%Z zuc1L^g*|A*>vCGQLppQ+A&gUGV*KBI2Buh=qda7TMfU1&{8q~=<+A>^OSErCuAS<+ zcA3xG<>QxzEax8zb5k>GV^b?*Gjk79i}4oY#*Z@_ZE7>#)O5P3+KK=7hJc`DtNg;{ z4OYgcvq(%Q^=xv7t-m94d}**H@x ZGt;rWbkDIfO?UsT}2hZ@!vhAP}*uO3UX1b)<%Jvf{I#gL{ltC zj7Ad;Ar?rKSky!X#1P*^6yJ$}QIUX1uqcq$S5SO`f(VGpLmLF7EwrRUODlzY%YFG_ z?USDFGv}PW&!d+Q`=3mj-o5v%HEY(aS+nL)QBhG*QBhG*QBhG*k!NVS6%abi!wYdR z4#9kk;xTN(L)e1xy{XjRWG-HbS7Ql|z#<$_K$j8Ri(Bz4{1!V0rs-Z_0X~M`<4H{Q zsXdPGV(}o99+bz-$9r%xUXK|q-W$Px@E|s3ZF}}?wpdy(7M}HT%uMt#fnVYa_-kLZ z?=v+Vi>vUI<{6&E1NaV3#7nbpiZT}~@HMfdCKLVtOf1u91aojY?v{4>b@&`!p?@Aa zBn)tY2+)o4Ch!ZqMLEMa3Fwvucpp~b`MGCL2+z3`w~5(ouv%_!jQdybLy7`qbRZp3BTwc(2po{wO>*A3OKOJF~%W%Z=wD;S~F{G7RBLAI8o2pO}puxC39sYZ5{a6}!%c zHVW4VTK~6O@2DGS8*a*UY-OMo(!0`39440AB3;CseMOR$N~&gw8CWa5j;F-V^b@Sa znLQXI)xMGGF;^s!x3+liw(R-rvV$*5O~jH@@FRP=V}3?&z)6|Xb@mr7+Yh6Ri5S+% zkccrqmG;Q(SdG)fMlw$iInG-%K{tw%Z98Qj#%B@=H?p$8po_GNJLr9Q_D(AMZj3E! z3fLgYusEa@$vH02d94nK;Ma>${*GrA)c;`oDbdGMI9DH**GCmI&YyKYr_2$j>~4&5 zyL8Ty;wX`7Lpx@QV?t_Y zniMJ04!;t*j9X6djr+;ieqt{IL{oY^X$GT>-)ik+$r28JZRdPAx^n>YF2Uw zE)-?WZnw#rFyI*?foqisKZ&z+$YiI3nwa?;rHe`T3L~9_Bk^3##+rkLV)>pWcC!tM z{_<^;F#a=|A>qn5X+M~85gfN*ULn~=H|^SEgUwx2AZSYtc4PjZ)PT~^4qjeU|?m^Ue3KN%A# zc`po$gK26jPRZP&htqOdYNA@#O(49a2f6#;cZojl5tYMMJKKRh$SlS3gaSJ<1v?q$ zfga?Zm+0?K93<+{tz@q3L1rl~$_btjdCA%~%GKLW-MRLOnwRc6yHC~Ewvl;57xKE| z=A7VL3Mf}^o0UJwF4Y`EHj4~c*5CRLa*r=5vn%e(3B54;``I0on-cj)8J1|i;&?)w z!0Q~O^G2{i@tRH7+@2G9e)jk0iR;o<%8lar@x+iPLk!9=7LI%0f{SzE8OD)#A4> zML9+D)yt$fO{aLX;)YpQLXXl6YgW9BH?*RL&r8=+KGi{=BDaxGi+pFGT#*wxD(WcR zvOpX-^MjcbCDL{biTtRYQT`=%w}G=zQLaRud6xID+ zuQX0;=(CrNU?99s+&FI1-Wa>Y&E00VLe1ONa4KB@8T00009a7bBm000ie z000ie0hKEb8vp^M=1D|BRCwC#y$yU7<u#(yep{#!I$Ynx-DzsUNp z`X0&K=9u$F`Wr3YX!AzFj>73hBS(##de_XmW{sG2?!4sn$-9#eyA^J=8%#3X<{CHX zRwVDbdf{EOMm}&)*Ed>KyA*YN@^ObFuQa+fDXqxre*3$gFP0tk^^E%a2J>PP`|=Wd zQ;59`V$CGsf{8JVJZCZy$BF$r_S@JOg_zqeIWfgt3#oq@_DmFYmfHW9PU{}+Nii=60lWCC)%G_Y>nP$qi2aLyM$$c_nCnPf zI|y`A6!reZ?u*`J^1N-a-$`#hIl$TKGscteJqY})&oTXs`Bbkt2MI^4a7(h@%e}wL zePbt1x^`;L=JQLkOWpFMhH-0lJEQ6koekSH8^#Hwr%r5#i)S$e_9C&hi8%v#t|{^C zFyM^x|I)Vvhhcse)CxN8HTHYcKz#h?ynI<0dh0 zK_c2=7!QfPF77+;w>IDLGoaWO)+-CL8s~`ZS&T5jw;91nT<_p>kdHB$!1zw$_!GX- z)qzh_e%JR0quZ>weAoH=ld4azb4RV*D?d#kapuIo5=I>H_{9nd&&&JN2tFfXM^17Q z*$$g!A#a^F!zH*lMsrlg(HkQq=6ABKhdL;(SslD|XW?8^FAyZeU@oRhrkbdTJ4 zn^8=#OX6P&J{Dq_5m%lD^W&TpeesUe-WN8}>_EdMfp9I~LFvvbe;<7}ln9>|x71^b ze#UWQwf?Z{7zp-~q>Sc#7%jF%dCF5gN8jCf-C28@j)T{2BgPT4UgVcO-Z@yT6Bu?R zBPKA)O}hC&nE^{Tf>Y$gvO}IHXrV&ydAW(ZQOEkoeS9Sb;+q&t@p!v&9Ni3Uf^XZA zkkzz4w0)4Wr}@u^U%Ke*@}}qDb=&1zupK0fEe7T$aN>}xBinWhC)?-IlH$!5Oa6LB*t(6tNuAc5+*a+6`&S4{ASqT54Pr`)EBz+nS9~;&vX54x1}H4 zmSsR9kw6rg4-j!dArulLILb+LCbC?WCYBv20nLTx47ftW9?P&nI@BSB_hS1W+=JMs z|LeQfZ-`VL`CJz8TaEL6+SKB9voG4;m}#*F-D=i!u>*D3C5yB*rtLY%X_%{O+IH{= z69VHdfoZz;nn=hn=;shZ3`RJB7AQfPPGZ*)1bQ&-6ZF%76&jy_Gax-zyS<+C>*5>c zxrXpuw%B+4J7dG3MXf7VwzBOB3!}8H?BS1BzafeIz zNK#@8hu7wXZYK!{6If-Ehsjc*M7#?Oxgqfoj3OG>1rjw5cMB>_qEaK`m zr3l0ZJ$SrZY*w`*u)OG7GxPQ&u7jTf&Fp_KlM65rxC>E0%wuk7`+fJZ<1g@$1Gslm-V^``&_xit!M%(>B@`N_FMFw2kz^+ zuIsu^ujHf3xH>D|%4@O8_SgfaqCVe1tb=#eOAJPm&-N1d)W9H>0b?A(L?((Iq>k-& zbS_$e(y+-iWRFD*ys6Pc#eWr5&O5oo?#|eK`qtxTA8a@CJ5#vxeRIj;4j*KF6wrby z(RS?4yKt}Fna|&fQCc$MpBNaERt-Ux*anU*$o>f?h8=u8JwFVRRSWXfg6~YMMr#ts ze1a<@iOd3%{-EBR90OJ%m>ejw=UZ0PdA3`AI!Ek2i{41DJ@*;jvj(5&g_A{m`Y80* zxFL4u&MuXbRV0oUdJDen({3}$w39x6$Tba zkVSb3mm5YgV^j`GBDB=DK%^*t?ghv&-BAf8zYu9a$G5Vt{SnR8IbCd$Qor(Nut@IXMQ+x6A7QE9TV};kqz+# z#XS+nxlcbMJX!Tw-WKKV-`n0Y_sq)1bMbRao3Fli}>U<>DvbsM?APnvsf(Y?^l1Db79-XjR$5;Df#-M$NRpW9Xi{Wck&GDTib-M zanulb>drCMW3nT1?wD$Goj0u1S;GxvO6AjET$s>+qZ%O*MJnV2T}>j`Vapmvuv3yl z+yqLkr6c)*mNw-ST4+1wY|igBuUX^d$b+es-4{O6=k44`n|VjSddpEae1GP)XB@qY z9*pBkGHZW6*V86JShPOKzRbif!tkVz1ZTi45RaRm7JkmrbnHNQJR?#{*tS6R=mao& z5CSXpYetL&YJnsI5Jy}KES2qZ8PARRJs)ZuAWY_=&dcZZElu-|I=rJ_yEO8;-z;F# zYsJ!Zl7E5gws%s`Icx#P*}EfKhg?XX8OU$1RYK$Ak#cLo>UaPug$?yB_&PNLx-qEKHi`?hU zHeN@c*V`}YX5{C&%WdNBJK7jWVsMd|#ibF?sMU<5hKo=+3OO2kU{kk4s~0Dr#UgXV zY3p*_f+DNe8lo1>E|t+Z$lrG}zq+uY@os^tOE=y-d~4bZET0vawxp1%My~sY|Vy;m#mI%ApEuI+Tl-_)6YJ(g=XI4Ck@t#o^g!ibqrfU}w)8G2<_*B{RlUY) zen%TC?ixYDqKS`TYa&SG$n0hkctw`NA{NO`aAa$$cA&yoS%rP;(+=VIbWD^t?k(rF1I_j^Ahy#d`kwX$H<&-=WWrH{7Q(a$cAZt-TmTk3K2LG>x@ z{UF<|vJi5ZDU}&X;(ap2SSvX~q=&l55~6ZUBs*lP43v77x!K7vs!zx)v+5M+^hVX= zIoFLd8p_FA?)aaH+gl<61O8e$N6*+7eY@YxaXrz4d4ug3O8D=O1TI)(fwiB9xp}3E zLNuyrGdCQqk!!tW86pCJsKl^C8ug_RopBHWq!BCY(7kbVm1QHA#PKUD&dTctEap-S+gl@AwxknW#wgLCxEh0!m-n;Gs7ucP}HVUE1IesguJlJ^H4EGvj* zgh<6KYTTsuEl6#FiNukWW8Ts(V2n;KV( z3h==r&e6wCiGI2Ex@T{Q-RJSa_T)(1x4*m9hB+?i#zT~?v#DV20$t`>{!8m(o4y^k zz+S|sXve2#!(SKqy(1|yW9b=55Dy1Cj72%0&IcOV;;me_UIi;zd&ZC3*qlclNhp6g zz4o)ou@ko&*YS?dQzaRT=)}7ANfxX0<=Jv|m0bmv4WAkxE<&V>1kry8jF#<@~JP8?hqlW5E1>;Dq z3L_9>-WFu*`cHKba z!uR4AqjX!ZTuN|yi+g`!U5YmS40=Z3rE?vAV0(t z4bLzWKl5vy0fVOdb@JS8V{a|9#w3wNF%R$PvHNyg8@ta}C;DN><-iuRigkMnclcxA zwc|{~Ey!Z>Y?rnr%Sn5p*kO;4vkB!H-(|3tSa(S}4kZ;dSiVs+gwmwQIHl=d+U2V- zWzU46@1)t&`e`{Y?RVp%aN)U48(95P}xoD zj7?@-2*SSXSZqib=TQpWa9DR8kg5kRNG!F)6hF4qJatCARAi5C zKeMXGtPj)eXBGBB5rv)%xm)p0UhEONd1#uyx=fe44TySmZ$tamz(3c8N5;!*H<7oCI2xz%NjY=B}U;O_AiMNHj|gSfd2OcfZ2AL7gW4M>(qT(bsZ-HkJ4 zlpTis@owA;V;r4^k1$9+WYj>NOadu0463~D{yDF8-ZMLPZIro>{I2y1 z8%MusN{u7&U+ZPKYI@C6B75s{#?hC=?sd%;jVgdZ*fQMWgVdH=^{%coTgP;fQbr^5 zk~L-v8+wVRWrttN3teQ&_$Vbu-#JJWho7uqI0BM`<%1oVHOn}%o2ni16y8`*&Ly69?6oNQ?IR5#_aOFTJ`Q4#MeiK?xvX1KjsA*voO@JF=NTn? z&Si2V_bA*Jd)9Bv@SfwQ1ztzjuF9q?@<(IzyD)hSV(^omDeqb&%)F2@>fEMuq7Jbl zr6RQ=ts?!zmUetgtqjO_qAA*8K(V$Fnc@&*9FmIA>i4}@EV61#vTx*Ixn&6ysA*i{ zAV}orw@k1pTX~_KQ5O?BLl}wEdatAR8tQf0n>>lI>}`8psRmlAZ40+p)JMk>-1UHQ zu&SqCDr=8Tv%|K9e*3`L+~OY{9_cptp}})+pMU%Owu_RtB$v2VZt(b4<5s(s?!nBp zgN}bQc*gDhHg(+G_TAK`XopPSjOE8kLGjrwa7o30N-_G{;NE{A!l2>&+K9hF`AD`Dd#1)_Gfa7GA?< zg=MM`nL1gY6u*`Bc%QjLZk&3}h8A1hQeSkQRTu5JX2Xz4kN01n`T5&{O0tb(qp51Z zHSz4rIPWeMh{w(w#KpXwumB?7A%U-*1K3Dqe9^~vJI_6$zZyT8d^+SOypI0op5CM0 z^q9xdOJ8k4Rv2g*v3Q=!@P2+tMI#^|?5?o(ucPjsyln+P8QX1E+MzS5>(AAjT%ESF z@6;!|@5!%nPp2Jz2up1OZ1G&V&0Ldj?;z0xa&!hSnIeyr^np=21dKv|xvk9$@waw} zxBwGs@Dni-(t?FsV$Zv>*xQBOG{Xy&eYL=rKTHbYoTtxiD*o773lAAbQy4d*JOJNz zRpBGOi)VFS>mIJJbF+!V@@v=joAiA5Pg5G(4nNOBD^9*?an*I#`9ywTE#y-`g zhn1w{2{{tc1^4aU7Ay2Kcka)ywLr2B`M9XRch? z!Q21dk&E)eEA8iQtSg5dVcVBvEgJ6fb3783M%% zd^zCRrTT-P-&a?g*P~fjEMaV80lA{qJ zgrO z+I2_W`UI@dRuOo;<)}eD7A9AH&JG@{l(T#ExHnr=H$bS)|MU1fH|aVY1E;bi6t$xe z1Ink9h~DN)_|f_1542#C*;l4aWn|o&VdMQ=-+AT6=rdokpS#^RjY5u&rfR64j_qvA z0{QFIu+`yz=aGQjm$_B>z|p$Gv3Ivy?^ZS~7mu*{$hFIVS^VD=y`U6L_0b*YuY z2=}R}7+~@ilt?7uU5R59_8;80>+x(85;xA&A9I^IQe?m%s$NF!T^YmJ-eMourQI~l zdyVTl>SP-PSC8TCn~U}Pv8|0=>7^b;sc`?5d*8?lHl;LK7mu9EXC1t4;;XGH0=A5< z4N*30$DB-#O}t9uIxXhfngWGfBTZ z^!t_TqtE=MJsgg(8#4>JqnJOxt?*vm@qcJh$2Ypa5yN^$)d;Emw>xJ}zb`L!s}jJ) zqujmCs>k~8$|4wZ;%Ab^DokpWx&#cv!EYD|4<4VR_8E6%@qG~Tp7df+HxJ|yw7ruKFJ12 zE%)vx9K9e(RiC?hsYY9k^+k^co-b;6_4&_b3w~wkbKMW7V%lhF#K#3ID4COmYjPm^2RNfed_vI{gZ%Y*FY0j#uSRS zoXd>~oTJ|{T}B^}nC1(=NJrd@_o%FS<#+}qg&a%WdwX_RZ&N9lIL@B8b^MU`Gi2@~ zDJMi0bQcI$DT8825SHf(iV05GZ^-7ibKJ-)}LOx3={r>U;~@>OqYZd1*=+r)s-_^*Y9mv8<5gOi6R^7$)gbS-u3Q(#08 z=1m}Rr$L@^%GE#wsnh|$Vh>^yJiM#QQ*aWa$8s@YY$xCzXNk4Qp0^mpi z+3U#;Pj|X=RPmC}F&58dcYLwz^5)tx7ND51z$zog*|B-!*C}bS@oKE`}5vu?8 z?aBH>8+kWgD9GHQ_9J@));&fqW@;a<*XA6(?Sg9G_nWV-uL>lq6=TVV{*fnvdTT%b zTTYi1jhaTQ$vD_*d-ti+Mm^oWGOaS@Sd<;d+^;PDTu1hr3s?QMU5#smNig1y) zjF^y$#AHOOouC?dk#T6O_7=zKjFb}5KH^fBF!#0FWfqglKlI1JEn~r0k7+cxRLhmq zVjR6K7qH|UJ$daX0V7lrU>?%g2OM<1K35+>H+Dj`T%n(TEj({qqan`^cUs8A1xiDU~ccBz322=p1rLRJEQXADF?GrGE0BRtrluw~)@)J^u5763^ zFxruT%s~YBIDbeAkTCM++kx{`8K&f#Em{66e6)2u?tQPE6n*~pGPu#1b@W}Iq}Z0v zYO{>*TZl8=)kl)z+tmK-hR3}H&pLA6%JCDPXnQdGQ;U`FbOFqJ=D~MP=DrW7?P`9{ zkXvS*TM}rLtU&gjvHd?yv6Er+OH@WyR392+y&Tf!j|5Zs-Vq$H6bU*yUs=ve+VPW= z$ZuD7(3MHfJRr+{ia7PMhjKG9sD@EZ> zo$FHOb#0)k^2(N5hK^W~TjSy!vTVn11xJ6mD*|lQZZ+;-TaBKSv#Vj__Fr1Hsil8A z;w=ESLDtEP%98(paydM`#cZ$yL>?raOA=Q1-k@LaCHljeSww(rmq3URRg7u6rlFrP zj$Zz88b_A3qz~hG!RQg)C`Gff;Lo)dW7^hC@1*Xpoz80jj@FB|e{a&-b|1SEX3Hq$ z%tmzdzjiTVI`%{iC*`SbeP=f$=WO!+hdcPU1EEC-_=o|tA6o3+urV%P%27!)H4Ga# zsZ(>CQj+q(6>HQ{t9o}!18*Hxyp+@^339#EZqbgOy!Yc&ZX)(vZ!S{&Ng7Z7i;62G zj9)hkI*woUjw1RZf*FUdpRu~*ku)1jwr0oo#$mMMUpv{2dgQ#HIlFK3WvLCc z7&)WtH-m$&KRf)PEz@JERul&MNV0ZEdHEO@kyqGMqLnyK5~&2iO-cixtRX*Mj_ zItREB=`q4>V;p_W#{q7(@?0<8(fx6QOK55vf~{ZO9sNYs-}OlaFbjI< z>(h2KB3o`wJ$GH)dGVSnmORz@;}nT{#0Xdb-j3E7_H$@`uK#nqm$t0yHH+MOuiMY4 zo<5R%*uhb(VrB$_*)I8a0wF*?zHiB+WuL{9g=6H^uuOS7p|Ac(zB59{A=%-KZMSGg zAGCnq>$&cn8&1#F71(X;I6?E|rmAePm+XpI2&H{(Wqhyw*@p&BeKX&08K#XYz-6$m z-rc)CJ)XtliCj(2@bOuD>N&Otrwp~XLuMI_&kkGEDawYakwXKa`hFm?WI29H5rcas z>5pUCt-6*bUkG4Be8r)iXGJ^ud~RxW+|i4;^w+dbni#Nwr~>cwEJIywXCHj`~)vp*h<@G$YbVF>Yat%SfYbxbLa@h+xw&}n669j~| zXh*+&E*R0LYm`odNdsVU>q0fJ20fYTh&)U5Q435Lij|>}1 zq8wy>?f6S~d{tlG$z?n+;tai4)z0rbe0fp4)A+9~s0qaLUZJ!urdu_>eMV{#Lp;G# zF`zP4X??D%q#95Ni%R*)Ip$=lQpwF%_?1l5>xeX7_gPVevx7(TJ@0e59QN!tBkU^V z?8e1=Zq;AaUk*Oz%EmJ+V>(ba`y&&Ko3wQb*HSyx|BX?d=t2J>s zdvI276Zc5lP%1&u+tI+E+W&S`>)EY$w%*bGv6;QA15l|&1J4=jVhg*_9YT%E>;6R@ zb&%)e3GoXok*{b&xFdQrN}6F_SH9Jnlrxwr@866z4f60qpY3a*zr01uzCO(VD}L^w zzjd;SuGn-~_&)cUDmeNMKfG!>t)g*8_s-hq-_`SWew1#54dE+75I72zG|74dA2=_y z{r<$Wc12%d&i3E*e?QfWz!&5bjI!FsYj-Sb!;@Cv(*Cf)PxrGKuTXg*jtCITWrM>y zv5(-Z_&jUqK$gz#M;N;o%+&d-*4%%F0O34$2 zk;iqgayBMnl5Q;?={0%^gtrTQTC_nktzcDUpmB+;xb4wikqR;YXpQ#c9R0zu@vhPN z8i9KUG&FI<@G;e{bVjncD!Uf@MBL-75e;z;?yqeb&G!3Dvn|t)%FQ6E0w1UE&zM>? zwg0c%A4m_qtE5n;NB+Mnb-<{zOYE;RZO3bQG31b>tO!sKjw+Qj&TzXlUF2s6GA>Zd zF#FHvKpRNW!Z8TxExkA@{#>OIR( zZ34O~10^&gTih#R^&cII-!-{*OuxduQ+)zrD>r0ng2&NU@Tv2UmLo>C+zvyFYQT5vhB~T=2jB<8N5NSUj zE#`GhC1rCGW~7lVzWI>fJ2St(^gLqUyXOn9T9(A}_vG)sckssw=F~XHoGIN}Zi=r9 zAO3*I>u%tPXq(KiLHQtNP0mf%x6`MYFBkct!e|(|sN;?vG3ETqR(y^N7=Y@f8~n&# zAS7b=ZUvo7_BE87rIPU1oO7PNc7{fpc_YJ=Q`ci|% z-A9ce0X2z5I8aEFfDA}oi}UtNwQ@lhvE1|gs|M&4cwkPbRqaoTKHgDNBv2UX#|XJP!^SNg znAMb}aB#v%mwyhr5stnRW5IHFGmgGXLXY7zCBanaed8L&&97SZVTw+dZS&Ju5mz5| z&lPZw$=ibp0f!B_H`fWxX`LDe@rGOa&lb}2sNG)8`1)eoE+l>T+QQg(peit?_j$V- z;#vNp4@E*?vS?^`a&DWanbUR+37rcLnHaVML;HI>p-4x+W}1O} z=WbyfeJ@!F@}bViJ9C=auOVKIvkyJrg5XRHqpT%@5hI8okn>D?iQl*QF$Cx$pe)1_ zI>qikueG~3zB}}%juL-x(D`G>^LO~V;|EuD4*SAq&NJ<{p~lBAQ) zPt2w4B2Upo0)Z3zyd5xIm5zi;*y?=O@;+zyC2;H|8(7 za(S&(S*6iT@W%-Ke%#Sdt;FUICNNbs&i9%0(UHSW#z)-RhifCOAS zVmiZNIy8Vpwzy%L#7(!OK-LQ<3ogQFJ05Ry=HB|;qvRc*2KeXklgagU%`Xegwp<69qy-SPzwy7-5=D8tC5wdIWjEznwB* zn%kHnm2q#cwo`gZRE=hY6dOjy`9)RLefsl}Y9;yxQgCdB6@`{9F@oG--4=9CO|^oL zcft^0&e4;~R<*=gabk&xuvT{Hj8Av3*DF!|+gnj|gCK(3Kuj9e5{dle^=v69cEef( zT_S;lkYJSN-Q488^>>};TgTdQAf?sprs(7+6ow}OsEnjfMr^N*cqoa*{SXZ9abTLl z`Ko~6Ea`}vf(D|F_?+WR?2uUyqZMmqaRU4?%8N4unYNDt<1 zs0bKP2!bd%$O2U^+wppVG~K;%qWtWPg9JG^I}T zH%~HI%T#_4T@^~>s4+r?vK`-6ftsoqInhRy6v^Ys$waJe75XIS>I!f2{OkYVo59MrlJNBov zmMi=!hmInr{d)aQjUOL*j(r)e7JUv5T-AD8+YL4>c*%#^*NH%QN)kavQ+&jRh5B6Y ze($GVq{8`U6d9PKC!xZgpP-UblOKJKA3Sz4YFZ_=m^7XnJyvxmi%}6vWpFv}4jbZq zalW*vB8Au`j)rzH;*tlW5JKCC@sjH;ZbJf79AaJL?HDZc>Q&s?_oz$GFGpF#GS-K$ zV}HgKOHRL&e`De7_8+;Qxg&b(!X|Ew$ozt1(el*YfIPbqM1XNo#JcvXSaR@<0-o5> zPN2a*LO$}-I!A9-CLM9oSnkSK75gK>wn-u0H9p>=)ERyJpUo!4MwWMwophFdW?8MR zovp^bd$=U#kY4_9x6e-O`&wQ=jhwgGQ+4vlZ)ab=@^m}-Z43U{;+R{9haYTFQ_nKE zX%b5aBFoD)8mnL&-d^si_E$&VXd%ldA)LF;P>Pr~BgUgyCmnqgVl}V*e-ax|e4$tt zFycpU1E`Eb+uVWij^|~mC?zGK_Tl0YJL{#tpAmd+l%@6WnQ;?S8TtGk4c zl&Y769$9^2rg0#<)wt5wXTK7m_D@V7{F0m16)tNXoVn4p!&=Hyu7jG&F z7;$ZA99`$tul{P?CnHYc@b8={wU{(ec64+Ow8q8Sv#;hS$Cb&QUTl93NyXD|i(_uG zP`L4<$qjQ=+ghavP@=T5wVfhc+-Z@d{8dkKmbbWK%jtCT^$t2fv6HACh0$?WcICq_lr~h~w z333>dSTf_E-Ab_Z#+E+8J5Ncih$G&?lBbXvI&i!q;E5B_N zi8){cMF6CE$65Z@jMa~HCWU)I5+T6XC}-qPaNdjf{MYjF^TX$lKIdnFK7uQxm;I$E z_Bgxq-7Xxf-MprFS_n84YxPr|nyJ^3e9*BGStPZ{(}tC$&%n9@`g1mJ$dkz? z!|e`d>LkWLA0VZ8{07(jw%Aq}x{CWcNk=)9!#etOe>g(2k|i27)$eQcjroxp;I@L4 z@mCW>+A&L5VY-Lk%c_6H!ncQG>V}Ncb)CE|g3e8=8`4eB-uptwy?Jfs#oIn(|EzAu zT>ibfe7oMh=rLF`=1-2D)C~DE9h*gjLb5lk&$3e^Ih>=LvA8d0tVJB1HY5s0R93J1 zZO2JRf2$P|OFEGxcu4&Fc`$L&+*Vr84 zH$577+(V@H?`K^e1J^%y_0bdwIbGNGH+n>|=GpG{4DJL4V(1C|93qfqd}}r-`gr)b zp>26y8U(yHt9-c#*|DrkeGrB*aq=})Zs_;{-(aRQs&5+qNh&<&D=Q|&){9%;6YX5#K%MYQ#`TPEnNg|UyFZnA;ZQ*<7wWClNlOFAb zSp`wrGxD>RfBT!VKOw#0Uahdewkd$ZEub6mq_d0nXKHij2r!C_$gd z`LA>B6gao3y0cG}Xh&WHq1_5L)tq|Ef9PvL(0bLs@=+(Yuqe%vXsTIxE%6mV#gU*H zr3o~|{`OL9SV|w9-_Jm8^w8fIsG+#?4;fKH7wcbX!pI~d`NR9%aC}VaKM}oJ@7bx>6)tp4I3`2m-@L-OsmDvUa0u%h`7z6=#4ng1s5PhO6{nl@jBuoy=}+ymFTWO$onSD&ms*qadYcDg+w^-K&zqH?bDE@Qo%x{in z3c>GBt`6vS{5L(h0pKt<&5gdwXSyV7LB6U!Q$I}Oujw=Qu`cKMh#dr~e%t4iB$F=O zfJQxpiVuIjvTeFbbNA&%hhQ-5`xA6yCQg27m42MsUGorliaNx){a8mYJCH-L z7T*yHM}ODWz~WTIdKB7*K$)dG=2oTH z+VQ~9dIMz+f8?1vENSI}!OyMw-lWn@g!tQ+)7mE=VB3|YDW^mhrKz`l;UVAK4z~?w zRE_U-j5*SqR%xR9hLf5Jkkdn?@{y5ar{iXXvwoX%^r{1SD37k&L6Ak4oL{O^4i|5G z%at^8`19gfkH5d_j?fPiHUy8R=wKplwlSg)%7I@p|1HaN zHCL~$rKdvPHlw3o0<83HyRyT_uuPlTNG`9Ecd=7*;Gf>tq{P(k+w0YFLZoK}EEXsknfu$IR zRNVY+n8b4T9}OVdDq_p4Lfh(gnV=f3`Ldnqe(d-twdL;mIr`i#To&et(<6qR;47=2 z?s+tTlYO)L2YgC1rR-l)5NV4Veu)=p{9f8+>-}6|zTm_BZ!~LqyUVUUx6c6vh3_H4DmJqw?V5;w6nWPE)fz8KO)}g-GoV^J-vm z4+^l7^J-c%ke6?=P9JFcx)F5NACCx^esRqh{px6Awxt!pVq?Ry)#>BB0nUh9eURnC;L`VJ zkg60PdsQC>x-9?;m94vT{M+$py;kYhEU$#~kds_<0StQ%{r%sJF1|@8&pNoIW9%F0 z$Wq2q;U{wQ+LoIO{Y3{)W)GL_a>K`}aROBqy=Jf^m(;Z_dZ5zO&0h;rTlAaAJG$LD z$k+U;_WJ>I@m>4QSUZv}e^qS$TYQj>ukn#ye$PS9^dUT-?8Up@Xc>vFn$Ipkv`yRKSNkX(p)}O5k z{7--X8g3VCc`hFT#6$oqk_aL}lK8!}wwvn@Tg%5Ep?wEw0Yv)y1 z-iupe{J1xl^S*drt((`!jyKxGE1LM5{#yP71%ayr|1%+}%6I5*-TOIs$bhj?O>hLOo{)4}W#;-UCC3N_7zR2hzjDXSG+6clJ4@vGUM}KZnV(NG@HNxa(Md?J)&866 zrI@b%zbP=a!*PAlJKn8n9Eq!oaWkf-KnTy?*F;6zs~pS^o~miF3V zfkhIEg{1m@67tj+Ag=Z5rwUPsGWOc4onGU;v&f70zR{*(?3gtmp7(O_6j`>4r(XMa z&V_4zpR0QE*izR(@-H>LCQWm=JwFu-6dKLZ7a5CS4OU1c3mzIQ80|;O;(&X>ZqCtV z@e5K{c@hD`d68+(npxeALiJ=9@Y5DeoV1Eygl8+>*g(Xyw#$i z?0BJdz<+Q#`1g)D--Buow-sFIpHuG8uY1^#hA4nXg4J5yDejY<-SX4#=zZrKu}DEq zO4%U2arh1;l#m$J6--w2J5!Cof2Ii(NPP3P+YckExV|>0#+4It{?0+--5@3NU;Fz~ zNWgf`9=?V5I{u#9%p;%FeDkj64Qa>uzU$Jn_P&)RDG77mJ=iYK_jxYf`C@^NfQcH> z3vDs*_%(ln;&=d8B!Ko?xxVBTjOPusj=}n3Yh--+$H)sQ0c2?IXO>sI0Z1? zvn$UAylit0BMt?|dJ*y*g;0ad`n zZ_nKM!aa@&`E?!J&D&%>R`q8vtgDGA^sNyJQ&K$r15Naa0llFf36oclb5 z&=)WJPew!Aai8B@dhqdHet=uXxqp!FAa?cOng4OH6lm<(Eyi+feN+3_+BPXiJ|{;X zDvufndT10>fKhVS1f&{}j@t=F+jR`f%U%PP5(o?{N8V$%8&9z44jPB}X)uL@fm9O) zmcK}{kX}2w!lMw_|6%zm#!m#fK?2RgQIgJTM};%D0;j zt_mP!BJQkE=ujUm!Z+k_>Lsf`A2&bl=>4>qDk6O}0v(e9#Ru}fMkt@G{p-sNoHFyU z#I;MXJd-RZp!fAl3SPJLNU9{cg#{>i=&z)JM&Mp>c648XM#OiT6U1rO9erDNZgk9} zde}paV@GLPrmtN3&2x;1eT!nIbMN(yJUM62%bo2KoG}nsUI>5y54RUPH)7D|<>pD| z?qU-3QDtpXgdUIz5DYBZFrfS*e7k!yj60b}@bO3p74aE;Z`b@m#$6ATW)Va>lHi;M z&SNCzIuef%=>Ru@RFYe=KU<5W)VSTE%YG}rKdd^?Say8NS1$drVyrU|eN`pyS0?&> zMbAa(I?EY|FJE%WRM9+|97YYH3Jj+ffm^D7iTMvAmV=wC_3RX#Cyfzl}h zjtFebNPH!N?pewx8n#-t?&t6O*c0e|W%JPhIq#<5JgwpI@Szc%V73|SqmzIY&?552 z>Kw6CsYix+uRo(aD83FPTzx!rUs`Lt_Vdd!L`oKUS1T8Upzax_o#%h1gxB*oEIp9X z*mhL9w(N0k^@1WB&inVXzT#8GzSXV;ZEf@p`S(Fg2N?*Hsr&rxEO(EO?BYo>`zmKr z-)5x^<>;iQNJy#2BM2BVBKtc}5{V=l$$5mE09Gu_!Z02K0W(HO%8~jVPX~d>qmV`o z!IdrFBKofHYJ;BX3&pY>Pj#uAwx^c$8Tm=zbl7qIJWB}67Hz>8645U32^PO%!GSDt zbh#_k?K?&&*~Dalb$fK0?&o5s4Gh1?PjzdU@|QK;C1p=6O~5mt7=URbk*!VQD2cqb zM>uBI>V@q%44z~MDk+vBpCl3)8KHL0II{2(7MMT)&=b4`h2WW1tZFOU(dHyH>#~iH z7o09TW(>2GOc-t;XzG+&OWvja+`MY^gm(i{dKH&f)!hs0f)8%BzO+lSFBI^?{*kAD zvU{_`4ddu|(u>wCNZ|v!Fh430ocMDu=$14tJh`xk-8rx>II<9D^zl9M+d{f30}6pd z|NjWUP%hb5<4Sfs+sh3m1<%=c=dc4Qr_YYfxmFYR^7k{fYR{+Jr1&C;I;Zq!U5>d@ z6@?8qv6zq|abaOXo_-}!y#XkaSDlNCmcC2f2>%2$$?@T?tc2oyk3(d0|XM6@Z14G-> zol^JtdB1Zu|Gm(TKo~C3LQQxuUm%4LtcH@J) zj2p$t0zH6M^IQR9XsZdhj0I2iCjoI!3gi_llxxunyWg~ZjPD=yigu9m zU3Hb7T(-KkJUA}uk>-a7F4%49-!tqI-%^s8<&2YZ&5?Wlu^`1KY%i^Ruj^eNEa=y) zs$m=*Vf#?@T*l5Bs}`7xhJ!8CxSXiMQv-N13xWd0c3FKJty@2}_coDxga_dE{mx_j z6P-GyY1(nGig@oaLq7U5yb~TTO);{wHx`^_$ zTb9&+L^mL2&4(+9#W>^ekwnuYb%~KJk2zmr1cSaa6bm_`$TQB?^Qd=%2^bO))7(r) z;?P5N1Kjw0Mh!obRirglJFZ)b-^23{9?V5xs*04`#{1D_ZL{r}79^wqaZxp<@%Gij zg|^S!s}{xMl9^?=zuca7PJFZ#!O)0~Dfo>T)RFV9c(P1Nzz4w>{+H3wDw-m-;Oo)t zrleYu$*#5W-9n@NsQv#n*JU03suk64Q?+CEVEiWjbOM?>9}V0x%g<=uZbKbyGl^y5 zi1UtbAwpnzNbFDh#-|!x>*yHHxuM{^lDHhbE2%4Rg4j1>K>$T?{Fd=%+-NhLbM%Eq zjk+696R#0upd^(Y`4gYaF%C}m*xNCu1HW7wJh$oE@t3v%@aCsft}Ebc?>|oe#sXjL z?DOBx8IBTjmG_H=^F7J_9zB(k57^M8O_^^P%3*$$+< zp1YM_D!d-Jb-ADSxp2G7_nW2f%kv;Jse9FE=vXA)6S zqEUK9g0%iNqBXR>q;ylq zrfkQpAeVmMMD7{Bl6~$PKkxE2b3RCw*_UM!Fe%XrOE3AJ^yRkk>#Btu9qV=0Do(Xr z?DMEKFBRDonHcdKL@QxHD9%8r4JKL$Z9f>!IQm9oK~ZHOmbWNBT> z74@KulBVu;pYF-KXqQh%@9d`}lLTD92Am)ZYAsSABXf%wm9b6wnKj}?KfzR+vQ4tI z&7UTEq#f;9X4Q*!zMIyR?bzLtk1YH-FZLM!XmhSFj`sFz-p^>*WFf1)hSoFDuJN0C zQAV9xW*kif-Z-<0lKq7Gs$c5Vkojebt1VgLtBmtnTh7rnNQ5S z1agG8tJiVQv%}`5ZO7sC^LKF9eK;qwMbKH&IeWh^)M&}8c{&rI4LT4ZJbd&G+tag3 z>hI_Vtc=1xubb%K(WMtb!fK%e8L!MDQAXOGV>{#Mmu@QceN|ZJl}GU#vGA4zb`qmh z!jaVq+1sKWTd4WfIZfS;UW<^UhmStCyX%t^xz$R76(@EHMy&$Rk=V5&U%XG8+a9Z5 z`AMClYXO#z(hJws$I-PA7O};uh&a$VGBGfcIOeIiwikM*e(tVq8Hn)Fs8kUo$W9@J z6hzIS0YUjYCDnkT7RI@XMVZYV($wvk)`z?9C2i_jd=?lr%76CgoI^V^iG6zwh)Ea- zvf@LKB0WLm4f;bzx1wPj9hYm={r=~IvyNChdS?TRH+Ak-V#9+{%$rz@l`Sf3du!Jd z%ybWLYfcc`MQYg4Sk_|m*e@x;C`bAE(qQ>+)U|#+BYh7me9*J0+p(&EBTTt}Y-o@D z&WNdT(Vqrq_9H+eB2#)`mgeX?uB?LUzWpSnV10cn$ImUZ4;|b-&=UP#tq0>!vPGte zFarG0PmAdNwlmaQZpF6Nw&#$>GX$8BfV>z}k8|}Xw}6F>ykpPCLT=bNqw2Mkrf$b) zP75YneBQ3j8OOV?{MJk2t=hPJ<436y*$X-Lf>MA=$|ivI%f9|%;c_s}sO%UY2l7_f zrq_&+%O*9eilHV_D$uTlH$(rCQ?Sw7S#2vcFuh~qpp%fu6!gDj1~`Pw^Y_;)oj8X` zD@_DDx=v#uGaJ7-Jb7<@2EsYL{LprjgBQYF0+Dr!*k0|e)C}vSSL&|$o!1v$uuh)3 z%i3JE+p5CkGCz(kQA0@N62>u*+odsD6nS&2!EJ5zbC9n>MSHGSsRdd6q&Uv;N@wnA zUb`p}?3mWSaimBt*&In2)zC>cmA#_&-$-jNnX`6p&8xj-i`sZc^^hisjB1K>`5%w- zKcb@vni1>3j5@VP>uyP@oL^5}&tRqg)egUI1egN&Dnq_ZhajX>lXN1VI02jMul@g* zJ-Da&lPQT{$G_4Vj;uWTU4FQ)4N-=acEg3X7P-WIx#t|Rz0^frLhX;aZB}rOt`pov zpz2eTcUU-j7b7O6Fk?D)B8_{5MCNz-XP0@rqw|q}O&yFc8R6}aN<<_PXhcFn7Km(w zDjdkBP{;O31UrrfF5A#JV|wc?Wf>Zj)xf(e!IKy@Rqwde*;XC8=9b~%ga5Q2M?8VX zaXyy#WLGs&QB#L?*Cu-816U#z!jNMZQbcsWcMn63F4J0keiRdpu??KdJlkL_qJ_v&t-@`m zmH0&!wU98$<4K7$kPN;)qn?KcYb<1{wjSt)knCu(il*@fUbl>4!1oVT`w^tOLK^jv z|9@{GC#|?AH!Fieg>5I!QOgY*{0M++0@IrtVwov$$X zvK!XK$|N>bJGSNr4mH}CKDEXtirsDwAmD{-|9&eg%aVoqyJKS4Rz=G=M<=03lzYjQ zF+GZSLZ=uuEJ#LUm9bMHK}(V7&leSY!<7F!ktYwp{CC76`;ZhA>b@lH4=Kl>5my<2 zN38qw2pfGVUX(a?bX?zPWBQRr1sL;4DpmxMktjXE=)rcE*K+ILwAka!h@r<#E`K3K z!ujwk=w}N=q|qtj2^^V}36UjSHi_iUyh1sK;eNF&dMGEgMiERBC za3iU_>0}4|5-{={M>06L%#Q~pjvcp;X)H%iG98wD8nBh3CZ&#%jkCvd*s;Ap(KnBi178DD4OUBV<&1+POOAwb$prC*pj>Cov2Sv9y*X!uB zNo;}mO0nV?37{LaWZ~}s!7>`ThJYaXM6IJUF`gr@<|mRJXO%Rbqi61VH*#R+PTpoi|VI~NJxC4RrcGN=kM31?&QcqHF{D)Z$UyCsZMqf zZjt2)cH*6sMzwiTfNg+Iyd5{=vkudT8_L>7DK>uK)7yDyUHp8ZiyXyJ_b^VU)VEb@rwZSp!g`F;sWofB!x z7#)V8i&c8XBcaPH4l#K%hT4^ z{=8>+j6iT7i2w*gC0+~Q&_}HH8d^E?nk}e?nWtwY@PbJrk@t~|XP997PDHVEp5+{+ zBzahq{DSOg9e>kl0;O^*R~|9K@7T z5ol+@^OC;fZ~#+QY;G03_riWgAOJ!{K($gvm#vl@9ffQpt`aQGV-%?-lxO9E9Nt&( z9tMJRR_Q-ma)MZ-VDm5|j-=y(3t^1_CgFuF_#;SZAacIS6ngH=PBc4yKcI0P{p>y4 zbEFw1iR<;)F3LgTFaK%oT1?#U86d5>59CFU#0&e!i!T1b6fB!;yS5_7J~%^-(4uae zFp6aXUGTB1bGZ_T`{N$nUkg+37B?N(UpfkSU!A>^-3? zzX*Dwo3S8IkOhjgpuDqBZ96c}_k8 z0F4-J986efRA`J*gM}m@7vy3PJ{n=z`I3w_-yTT!#H;_Wd*az)oJ}nniGm_xo;P|3 zDF_P^2A2pmRo*H7la``V*5>UylByB+^q$N)dOX|E37VthwK<6ZQ(08fj-iBv5`);W z>D^qn3X%Tq&BJ}O#Ec_V)L|}EOb@J|LssJ^K})W;=u&q4llzxudm)$T{Ta_frozoQ%~gR(KKxNedkSHEj=eAf?IU?ackYf+subqr2))ct$0d*nlXaG+t+1V~yT@=_0_ zrI#Z|Cx>9wnijA^vXaJ;b+$<;A&|L8|Lf+h!c(;u+M}x>A%=`cFf6o8fS`dRA;IA) zl$OE;8OwXcTHS7ABHB^=-OfBKnzdl3mCv_}AZ5ut+XvVtxngymfw_Ky^&HWEgOx}} z3%S=B#Uy6$YJa{MHBGyIjE+vmc%}1L5ud3&G=vjD)({?2W7mSjmArh8+XT(*%hw%9 zA)%7q2;4IU5@ejt7AP%M_uF$GM;|;P5$y>5yk@rMCn;6^hkcqFeVp&k^fRX4FzXl$ z(aH>X;ZIrYS4E&;n|}I=bVSY<{&&dDiI}EdKLw3|XdI3FtltCNRrKwLL5&>n1u?xN9OD>){(&GjZREA}^x)!V4@yKk zLccxVS}s^UXUm^DcmY?x8{unhU%tG(Bs3yv0>(%(Mc=GfTf5~tyH$lE+8+9=)$RyK zpX0xXAZ*eE7V+M9s6V9^CT?Fe^UnEZAnskQn@NQMi|k^XR-!W z8{*VsbVv2|*b=4S`sRsf$0-%L#rRoXGiP@uw{~agP{2`DGD{fa@2bYA*t}KJwFd*tAsNG1_A>fc_F8qwKkAb!{-d4<4-a z=RK$MIRg@>rA9yi7!Y$X&<+B2%29EwLK{9t@ycjx0~LvBN8Rts3e(tOF{R|;K{YPl z)mNqXO|!k&xjsdZYnkw;)$w6k7OJyRe=ea;%Om=tt5O|aZKI9Ws4Kj zj_^^wKh8S@U9#onHaH^nS-@{b{rZ(M%t!l_b*{m4^%D=CYuA%Zi|912UmY(6zzIujg*v~#|gUC-L<>hZbey&HWQJ76n?Q^s0DrnHJefV!1^r~W%bVmR>c=z*#iI{V{wH#of(+K#%HuCj3SsdCi3 z?qU75ceqW_I=r&LwcO$V`!MB=eW%N0%>*%RB$o6eU2TFD^pfO5^d74_>I&Mk`pCzy zm!i<;PGs@w_-EJSkMo~AH>MQiPn{Ccj_5tAQv1vcu{XHoS1z90RzqNX7I1xI>aJT- z6)*ymf+VTT)}w!t>RPCi-!Z_ZuAH2|MDT+Zt&g9_rFPFn%#^q9EO~?_$ofV=DUGl9 z##F(iR*1*GK5u58b5Og#xqEj7j733?ZW9S1Eex%-86RbaRh~!nKP{u9)NbXQtrF3W z*ymBA**asLx?v%@Iovh1qqC@9A56S zo%$WlIJ~n331uF}nrgu~NFoD;AdOfu!Dpy3y=UYLKcj85l{YO;OgoJ7JV?~o`FCn$ zM7{M!+jaV1T9bhIH|!wfDQYFJ<60dhcAk}4{tdRk;w$T4$ zqw$T^&lM%69a8bI0Z97@M~|eT3T|otN`Cfkn>XUTgL`u&YAMW2#29e9O>Irp=C+z} zP~E>f$8%G69HEw~*k*XxS-!PYCo14|$L&N75rG9cSO*-f+{2&#cC=j&-fmsc;T2ys zItHvyj_e>yT)#akoV%)@MDfL)SNd@vG3}7ROd2&cIS-GFc68$`?5LwrYqy^c(dn<2$%?s~mv;X&H5QuCXQ>>DAV|1ld{7CK5Og7-W*RMO zrZdZ)Z{AYKKv^btI0r zg0-=ZZk#cFlPqMOuGsup01;$J>|9!Q6opaWX=u)XD!7O$d znh;ydUF+y5eI}KzVC=XN*}{z4f-i=Q{$@sNhROTT#35dU)p@($%fy6hvUHb#A7^u>Z zCm5k%!7op(O#zX;o{dddg!~oITu-IBNfiK zAzx#r?$HMPGxe}xblLm!I1lA@(Y9SQA@S84>Oi@0o2 z7dtdc)p}_AG}W#4IQklL{@gmr^Z6MSFE=MBoP{}P`Lv;7Q!x@a2(zvlh%X>kBBORo ztBeH89L`QuJ8Yi^H@c1;8@)W+a;gaRv0HT@O?v6Jd=iUrxctAzkyf=$s<6;--m0@$ zMG*FHhz`b~nl-)eJU;0yis9c_QxZVoGKr%F>9gzmcl5jKwjv#G=1*)Hux|z`t!)RRj{#4!d*mbB~hrv!kDVP2GrIyQNt~ z$fgdoqcV%UIyn0Qy!mbT%29~`WEU)NRD0Y1`zu>c;IT2*Wl5Wpik*SkD1 zD0YobmMy*C?@PCfCdE`4i6hspzhrWNL*gJi{ zVPTDnQ-f0Vl~iL->R3iFr6sYkV36t)QCj!UbWc<}ctI!|eIRlQH^yeLwzLX=|L+`E zT{^o}>vC;iudMc3-333>)5_7e$fI)f|MO4I5;||x69IbCu2WRp);(O3g%N6`r1MBj z$!RSJ7F6mjhI(L0_f0S-NBds$y=k_W+ZzYzu;H)~Df1=`Dk7JGB5*2(mCTC(ikx|P zQ+}e_fnm8u$k?Cg>bl29XPR#-NeS;4bh<6p{qIMkjU$VE#&(7tb8Z`B=ja5ctlZ20 zZc1uZ$k8!>hfKN{xo7{VRjgZHk}WZ;8AlTDAqC+kv%ngM#CkbQOJSTpdRTn!8(sI^ z=6tUt@3kvR!7=#+CBmB-n2qM`K=^Hmf7HNW)x3y_X~DL{wL^QxC?>x@J$qB^ll|(Z z=oSxKh&P_Mk(BN+_I>kwdD|QtN55vXzqD9-X>>3SNgNo9zvmB(p2n2qAU01sia)Hj zt&1k30tr%3SjPEh``B2>ef`4}8xy?UZS}9Z*;93pkm|!pLjG!Xfg%Deyp!q2BOHC; z%L*x_MqDs1dbe+^uyXYEd@LE!1UxXPj(Ea`*bc9M zLqE^n>2(m@5eY&n?c`|XnSoK}0N(jZr;T1m51->`O44Dka#y)p4Cx0?=D)+$^hveW zVMi~TA5367jHQEzw_|Q^Rz0wIuJ^2YD=qX}_U*{_b6-4bAxlSJgUO7!v2*VU-N|Uq zILsc4Yk)$Z4Vb|>IxmEWiX9Blyudu(GX1k8Da411u8qp1A76sxMqW?Qxz)>%SN(L6 zo?Ie@Ed{gY7+A0?v)LG!<&OO>?C8H9n8tdYgBj@s!R*t^XU%$FPt=ZOC7FhefGj_W>$_3uelZk!gshu7^vth7_r^TZ~ zqPf$;v4)ee_tq(%!o_aO>zKlc%}7YH1*BB{lSL9r)95CLL|kK?qq`N`TP3m`8qZAZ z_V26Sxg#bIT5-x!bH9eHluHAfQ6eMaLa|0l^w?8x(Tg ze!puMc`f&MBCxhXqdcxy!URI(Na7P3jU+d!&yP~9bq|(g#?Cjc^Ttc6R&|sG2ys4G zQi{VVPXh=Yj2Z#qYQg56gic+t{)m&vc4#f4&+%#CySzeM^~1&75$qh@-FnP50^B?L z>SHeIK(=e8^Vo5r-&NGOeo1EdFl{YsS8Mck^T_CBC@rP+z4G)=ql!dKacBlXpsqU7 z+ReFVM5IxYHgDCL>(4pBA!k0a@-7eU2x3hAbK}#?$^Dzxg@f2OK%R#^v9d3-{VN`sk7Nf*vt{9(VK_D@UK8rzc2p0o&iW&m$$2 zCr;A(R&wHF`ILz!NP!IL1&gDmMmYK+Zd_Vc;GMjmzxVA7QoBeTfi{X&d;th9fyAPR z+_^?hO{!ep&cKLDqS%42D!QXt-{y=`&Vw#naWI9SYVXWUFSB#>!qsweH}XniUe5jb zNmdFH-y9*q|FuE_``A0`GgsUiS@mU8ZA^}&S7uYd%5i(jpb9kUto#4&2m*q>(|A_= zj_bXX_a}Q}87ZtnVs8h&Uzos?AcI--IW{)`LFZ3JBE`h8Lt7@EEJt>GEa3d-j6?hK z(fj_#;v`EImrHhk7|_9Lr>5?a)1v*&?N0HBA@wDNsvkz~&z-{Ije95C7^Ic2Pi zgCZsdeD_7MBYZ_4ITtVIyyu3gM*vXUIRDOdOROE;Ea}SY!F%sW>>AzL(T!s-E^Hfe zv8C&)-CCSNisvlUH9Zh|k6F9q7Ih?aeZbd2=C<(6xDcJxCP>ZJS;iKPDR}@^ewgaN7ddOSeV)A#& z0*|s2kq9oq@F!UZM@y1oPW3OA9d-YI$a4CoA9=A5`^8ILlB@kV`ga!C43p%oUi0JV za_m`xDryWbHrtd_0MeX2Mhws3h^PUzNIF}nB-LvmvA_3anboTf7x!{DVyk;I#~YkE zv+|V!1mPvH=-?L>JY#eRj*%q z6f7EJx0oB?e?+(8+a^b*R??DbT&U~CoKxwh5}k0LfBf3yoh-{DjS{9I2;+FMRq_cf z`b&3L0iX~$)q%Eay-(ntL2|dLtsjj76*dA*mUfQ%wn%I{>i%)@_Wtv#V4S=xmL1jx z2D|rzbh|43n4SEt(QS+I{SojRi<18GboaFK$PuA*vp|`{_Na4_UFMYFH1~^#icH;zx&1m@wj=Cd7y)o_X=AXucHr=Ag?+?PH#)<$Umt^ zoxH)~Y^k(RA`2uj(yt!}WifHYHnv4g;A`_*Zm6v?Cn0JN@1ItKYb#cAw zPA=Fq;#6Exb^jvSaiY+)mnVE$_DTypKYcIz{Dbj1`Y(pqWxV3dqdz`Sqt7E{n(mh= z&U>a!?N5?KRz2H6<=!c(TCflTb8M{}xA5kECl?^>l$Zf}61n&xG`_l}y@YEW%!X z&tZI@BT?37%Ut`P@3n65coP#AuRm4@kBf3M(c3=DZSstE%Km`)6CRo-dVpl=@E5u z{GjCQ+&%icuY_o?f{S}g^nE<57?xTv46=$u&<4uHV!lx6W#$(Rcg*reTz96T*OrS5 z7>GDC5Nkj}{DChBB9PKFVExOjl@r?z<4hmwk=q(3kz>`-am2ITKaoV#rzgGU%h7|6 z_pw!Tw3|r#;q4B8H|8X0W^rGvy@p|q2q=T}Q4NG@8lhGh5hU1NRo!0?_VGT4*?lBA z39J-{QRd{32pr}GEQ~Uh#Uv4!cP)|axIBhnHFxX5RJ;)RG;r1Wc-=h7el|#Ym!D~uR29FYU%1?kO(EV9y{BE> zxPO?8A7`&}&O9OIu(+Si${trH8zu0fmD`XHbiFpNpcWsxmwE7UofZ&`f)M03GeVWU z0d6NjS8xHR3}3OdeyEEc<^W%=bPDb%jBw)tBqtEM33V{wz zx5?lbK8S}pj7yh<9UWJlaS1ZLR6*kcvlEmuR*WSmX%o2(^-u=3+d}H%f!G|dQD0%3 znr$6rMi7jI!?KBPN1unHpVHT61s%+aSD7l5rPkcA?t-0)qi-g4v0IN3eO{3~8j_f} z!dz|gq}dzx`0>Q(5naxv8j-slmM8L;mQy;J z!v8ym%{j=qZCQ-qb!UmM?hm~oytV9}9>?6J1+IvgyKp->8YiAdAKkjBrDk3S%=bmG zPt}O-$&rAd+&KJvo7$JZ&e2Ke9{6w=KjM;OqnAz)OhFFfw=rVJXT&MyJ1JQ)Yrobz zK5EB=vF$jN;YLwAUb>?+9ZOFbr`2-g=D%nk+UN4! zqNDFdGhe~cjlH86+SZNxr-`yz4xB<1rChj)TceWz-I2$AR0x5q(1-=Z%5`*HD-vcd zq3zg~rdlpYPHa2=ofp*= zSO2k{GzflY48Jt~`s8D7U5m?iY9qQ0J~ifrw4{5ko|kP?5GuI{2_>K;mLEpn;J+7S z9Q<1ziFM05oDr?s?Qn8NK*^IxJ|)pddtd9vfDd=A3gP zPMkQAh!6=0$%qhfArTP~5g{QF@gqYbzN8cxq9GBQ5fKp)5g{THA|VkHArTT0PQ;0b zfUt1RIp-MnbAHY-w|npTyg#3FcX#)vUe5~{yZ7AZp65B|IiK_Sy#FBdMi|z;<`=J~ z`v>d4qEieZR;+k;mlaZi_#lx;yYd(&yVzGO0vIkH4`1Ex4ezQwVJzpqT>8)cFftyA z^c?~S2*W;l5e)_B(lN=Xx%H#EymsNS=>sakw5$5l$KuFxWx?S<_}M=Lks73mS) zkBA|7G!WdXndrDI<{qxby9KExkgWOLbXwG0h7 z@BQS`ZS9N|EnoZdfsEwnBwJ|F@W@Bk(AARg1OdI|2&b01U*Wd(`W2lV)>KVh-TwUL zig=VWntd;cA+LN6 zj#u71`=P5X&9j8$*X(&CH9P_+PR`jaO!U#R)ngsNOzj}lO!U^}a=1=$4a&)jB?If? z*+J|Rn=7=S+&)SMp@5Ue*Vp{+KfyqqU$G?x!Xk7cxC8xVvYDLci9?99VdlmEdMRZ(}`nA+BWv$AXee~P1o8xaYtWcEioBE z%z5U6CxW?9X(ABL5?sQLT)l~H-G!k03lTjXpPCi=-!>F9j5S0ic| zenz2p(Yj2t`bu^?ZUr(;5N+Qlqk!3{f@|cmDY1but%57J@dL(qM=~=CYwPGTv=+f~ zVTfx00Yh!W1p4W-|9#NhD%oKT| zr;m#PjQSU(t3ccPO=9`D?(F(Ec=kZC;QagxfsZaqQaM{G#l z{QD7|><#b18Smz(?ePtZZQo_$4ACEbmNeds9WX=QlLHkKy+o5)#C~j8hX&5EsDy}jC$w{m+g0{A@%-z5oKVfhV$!(PK2B=L;Ru^+gs zXS}iVBInchhT7KTx&5Vin0jnq7FQi2zK&k~%Z+`KMCpl?6Y(IZxYW({MLZM=8i7h7 zS8F*pH{Cgi-NQ@YDzZP1SFZ|Xf9)n=0JQNEmR834dn6Q9Ni1K=Scc}@kGh|D*1Py{ z1f#s*)ihi5@inpj)%gG)M@G?9BL~q_9g{>xddd-;W+PQdO#=2!Q&ZP)*o~eV%`^%8D1Of9fBf5*m@z_d|n&ki{XJZ$A**es%B2h4eeq zZ1+0kwGw%xT~^BiWf2p-w!yyO1xLh*eux~`WOtnwy?P5?(~;5(L3BG8h#N%QU&||)55}s{@l5yna9u_D10ZyEW#Fds`RY~Nr69~qT4KXo838l(Nv1>#nGXu_H z7x1%}n>hB?`~gX!kL)D@%7J-a2A%l8_hR>z`YwR13 z`zOm2{bzmcATb*|-SRxhEfgpE9yd*Q6jZq>by17~J0ccp%4_y#I#=g5kffqj!od~- z|4F^#P`9f{()E0Yzx%0xxpe)6=Gk=*Kc|)&$y18#fiR& zV6E4HN-41wa)SFL9nH}G?B~c6c#Y>LVYKlNu_`F2A6!^TEDo#gZw~r)tvX}iBTtgq)v?zw{*FBI2&B7sK>=^s2YtKUG}Omym~8-{fiJnn@M@ zOU!WG!oCiZfV(G;m+F_W_=b@oe z9!m$v>-oidUrE7Q4#d$^YmNseN+LM)R&a6#It$oY~Fx}|BF$QHau z9gZPlt8~a4D%~pQf{bXn{jfi=ON=g?vnCkD@L6-M3RD>*T75PGCiM-{Hio1v2B6m0k@D6p7)O^Y%TGS{xrZB5XcE z1UC+I<=nBPX3r6HNWh;NmJQw#`w@Mv)9HSBmUTn$it#fXqP6pAm=@xX+|xuxgf```(KMhx3H@h%?mHBRj%x4&jT&* zD^K$zGpD~{*>d)uEd#LJZ?{^%M<+ndl#Ji7PO9;U#j0_VOd1_9EFXo6Hk_=KWD3Zw z43g*q?-YwrU%c^vVKM&bOjzyK?JD9HEu$ay8c?74b!SlSPCFpTc*7RS)7(FCEW@x^ z54*CRH2@y7Oql5NB&t77z1c_zvZ6X?ZzD0)E6T8GUObZe#wZvQ{sraL% z{u*XtN~J4OFc?}|KO#p{$h#~oAfGbPPLK|H!PD|Yzj<|~hYZMpZF2Z=`(o`x=NY)< z>BJ#q`1$MsHw8@90CNRO^}qqKzb{3d#MJ^Ip9dv82?TE zs6N(qtm|v<7r&5Wf8Sz-kXw>jFwDIgW&Mib*1Q%M=dE%g48t7VebW)$U;Zvyei6Ta zCV)KR(>(|$#Y1{XNGjqm5@n)EC_C~SCEg95MCP7sW!bX&5q;NxG_>yg>ACJ5-#()9 zACaedTG9R-!;ofpoQ8F%{=Y$zQws?z6P9r%c3ss&Kl#g#Qd%5-j+hdb#*Gazouuz! zSS@h_q6YFLg>&+-Ap){1CYGFG*z@y+KtwD4Z|Fz$Z*HkeQVGo*Vt+`zlA3)`p6Geo zB-1e4ShY@%T|M!sT2*fD$m7$rFh@-*H@J%Nu%o3W<{(8NsUh#U=-zV<>nL31B`mq2 zjkCf?pO$Zd?5pf{2`#@L(Y2vt$ys$=&NEI_L0@g-;rD$sf%`WnVuYT3TWuYvfA20~ zbAB}nnJ94xcvRy=eBRhv7$7-h*cH^ZMWW8Z@zeb7xUc zyX?xF$-V+AbIJA!U*-gmEg2WR+`sL2-h)r~(d{~>um*`FC*=qLWHrw;-ssV&P--NC zaOJ>X0?bI<0gZK&#F>+fFJj~)9}}-W%eWb$bYy_1%2OJDfV~$s)q(DGj3XJ z91sRa+;b;BUG{+^DK2U-?!u7n&{)h!<7Au-NEvE5Qn>8KuRR#tDd7X$e(mm(0O|*)hRY!1j5py<>68om#X`uB9}!ji z#Ko8?0)M2{mVnj>_S2Tgvv||NE6nnCPMFJb zcYS+KW^>m-sXWoYF$(wyK}vO1DG^DYY}jLm zUf2UtNm1W;vYC;cRW0EJB{YCkq-y#72>;w3TWUFLMX4MH@YZz-OkANB;%h0d*#n@p z9VJe>f3h?g`+rI%4R(~07Cd81B9e!toOm@i9qUOWEFQ!W?d`!TfZzk0gO7@eG@~l;YlkbtkyF1LXJcT7I%1aDJ;; zIK%17mfDZ-Pkpv)Qd!N6nKzI_*8N1qLf_FX_7Ts%IYl&2esHKpIgW(qO{^1G7NSp&#GRjkpJ=RX;QF7#cv@RWBZmKyO`&1WqyU;gWxqd%@gyt6mfeq<&wuEq4|LuN zUP$N8I%%Ff%~SRsO(l=mJP;abkVgQbOzg^MSH0&gi)Ah!x6x{u{iyp1UzuAoUq;U7 zt3RoQ@EO62{vzj5k8_yddW z{!|gCb7i3hZ;Kg7GTonGG0sj;FaYUhAafUKiQCs24(rdyCDe*H*dVWo@X9864asF?I~Zjg8A*CqPFzu zow=j}pBh#A0x5M$i8Uh>PO#!DF*;kwJrzq`VL1g}fF0i&}DGe^~HqOeQVsy#j zY+~Y&*U~Oi$W6>0fy5<@xD2tu^GhA%eZ%BCaflf!C|08iMlqn}T)&r2zEH=x(^e2< zFpq|NCu!u#p0qfaCAiVy7C*JWfFad zUH4v23mlM6f7=>K_eINI!<9|r0FC9X;N?^e;xgNJe5B%&^Q9jQy20KsXn*P=@qqkJ zj4BF=&pBF7KWcyTqsi5YxHWzG#p7Gk<@LPFx_5nT`pv!sfuhC0P15PF*!7=eX%h21 zHWHu72uh2If?Rmc`=Q~gD{roZozoD%4lBqQpfZC{TTWofs7jShE{%sr{(?t;XJ6HBV-Y z=M#Ko$-c~5D|@yp)@C%I2Jyj)ZWs*j7w(jwG{5hzm&=#s1#}p9{oWQ&UrbOX&%o3i zVxnV(8*K}Tw`MQf*^y(0#e3PK6au9|0`&^61%ZTUOYBF@k-Iv{v^`RmY}l&JL%rl# zcHQo?zGmBtn37;Bts5+Vh3PZg)?jj;w!~t+T~YEHm2Nwl4WZX1iXi8E&ewf8n?YO= zwhwFFdpX6lhC4^g???6juH2JkW=}9IG}m~Or^&Ny*epK2<2Rb&eeif?w00)inu-Pe33fW_Aq%^sa;>-;<06JMmdsr6^UgV z<4H!xq{7qj;u3cJ013D}1M0@elmP8pjmHZs^ArihxF^quc)^z3kGh}ty|EAVLiO)| zWnOSX2Bt}xh#J=e4>_D%enk2btxFFDkPVT}e1YKQ!vVCOn!=&o_<_L@VK51Ttjse$ z{l~#;kSB=|>d4Y>^={=dS^)2#U4SMnr5`AM2>;ek^_Oet*DXOZSUi8iMAKBcVtdX8 z1=M~30l_@1sBHicL8sF~03tzxSb*`2fNpkyw@7jqx}iucebPW&J_nq@D;A%G)S=8c zuqp{*am(%pw)6;p-_U#I9r_POk+9Smrc7+2HRJ5Z#2NC}`2J|!icX$JV2Ve+YuI7L z*4uF*OA-dQqtmqll*U8mReGZ$^bPo2GEdS(R#4<2kuF(vluu8!{C?<^606?*j@NU& zjEQ?OSE?)Vq#E)p1`g40oijZ%2(upKrOW&T4A&q^42)?c)m^_S(@mrCAHYA|`DMeaFDE=W)Eq(=*7-cQFAEabu_E{y(t@INw zC%Z~K+s8QUM5TB0DtV%RZ;S>BM;i;UiPs~ud7)wo z0SW9h9&eAU2FByPy(ORz#+UJrW-QBS2a%u*$$vvXw5|jXjF#utKROt8Ovtq+C?yr% z9 zF~SDKuH0w)mdGcCKZm5V!lTb@Ma-7l58bN%YA}0`G_St644F@Al;XmWacEV0%fEV` zJkftLo>&;31_$PXEaEY4yzDOtI3hl9B#)PU#`3?o_A7~3qnxn;1t1dSk+>>7S@YYZ zswnV@N)iYoEw>-C>fk>+CyAzK-yfmds!Cf7W5>FOf5dg0v~Bvn87yfpVj(b`ulvf+hlum4 z{F!d@Mo%NxVyUA?CzxhTktg~!dw4Ij*a~N^wQ^o&nsY-VmO)1#a3fX@5?KUtYA9so z)Nx8TeA0+lJ0q4#W81QjC%c^(2>s+7?*3c*Vc0U^*B%iKQVyIBa8MquB+1cQ!bmOl zb>|m~+~CE9uLSE4j3FhwRRa;w1oB*xL!;};aml$HJ_%~4#;G=nO$rG}Xvpj;PNU5} z2_#cP^jK`k{otAL)&ExdZMR6Xs>gDacR-IZ+j~Wp&Eq*T3^u7^b1wtRP`p$Ur~JZW zmF*lN0xLGL=f)6A$zX|NLXi`4iaf1Gp5y|{qx=c5z=#o4l&}KChQ~6Usb%*=vml)b z_@a8#!m(Jm#BDpP_*&=525{!#f%QYQX*AvSJU2U4r27BI{iU6j4*AvSUyXV182jEa z=2xSZ4qw{mr68VMxV%R!q1K*EBk=4sU=t$COJdxC*6xU7hhu{zWIp-d(+{LpSabZo zn>?rf(jF0IG+Cesg&DH)q-x)K{%-k8eePjCk81x^VkV4^lILQvcr5u)ho29)YtF|W zNjccMELfgYX^Bxn!SdGSDJMR(>aGXo4LQ{D)iir@C|caluvjThm5zhG8c#Bxlr?RU z_#swIGaf1ELVVd0TX2w&6m{vxYbh_af2qTv+(R9!j`?|eJ68Yn-fud*lv&|7c0Wj< zomBn%!---Iy+~Fy;O}ho=<;NW{LlsRMDMk>+$otxUhMgGZEQf^a~&QSHgNUDyA-Ch z5z{{Hz`)h_jCi6;si%>zHcC7?1}EW6twxE*rZOrOU?lA^jz)_Lsv3HSTR-+Euj~8x z;E59luDWGY#vZ@qy*p}oSo;dLpOs`@ym3KD1OCgP&P~&ZqGL z4j);HQrI^YIy3%}eQyxHD z0|U!6d<(fBJn&PZ@am*KWP58JvN1mAi0;Q(|BbY#yH1{#v+05pNzM=Jq~i94pIUg| zq?H|tQxe$^8LxK6%M@<{Vc14d|_}6#ST-fK@NxNtaZy^uqy(5HB`1k!s z_7BHrZ&oA_9fr(xTmEn4%Ncy?h8KuN*?$9=Qyc_@STGMSM9p1g!~w(f8(y}PAfyD# zFoyTzVCLeXgID{-@wkJ*!Y?gY+xNAU#PkEndRDjmvy3;bJkSYY|Bc5>Q`@OJiq*BC zqbBtI-VhS2-A18{L5iS=`|a)yL+2+SjpG!rb7U2MYt~<~h^ZzRHd#k(c?~5}3`@t+ zCjWRvU88DA0y0lpy@*{pjpz4C>;XO=D7hm>D&P1&U~UUmGnd;Ti9WKqAvulU3Ro7$i8By&Yl z!FDnF%`hoUMExb^(O|3*hIYu1z)95o5BIp{=i{lfN2MHyYdjV)-7nwu^AUyV4c!l% z86Q4V`ktTMs1ah5=-&~@IQjUL0tWk$b@fq;iS?-dglD@9TPh=m<8++-{CrUxE#WC;RG1-R` zJIQN~?i-)#iqmeSrmUdQ9UC}eWm3s}O2;acVgOmxn}qA|w~oEmHek5U9#`-C>7X|Q z@#}}KVzH;4xR9Janp!{Tc=Q2t+%o$Lcbr1Du)uf2@50Ipee6qcU{#O!=vU% zEB@MqDmz=G$8N`K^DT_W+5d97bdnal0VsV$070-MzJ=}_b-&eFfBMLbqDH%m6&=?7 zH76eZkhq7nzx)0uG4WdxXRSQ2Y0!+0r-q{2T&XDsF>jAo)g_7Ry%1K7V4PmQ_XybUVT?m8Rs+ za|Tc}(9!Pb++Mt86vA^8q%tB+aimJ46-qFy#Dkp+#RFS7(h`sAulad#{`p6m#9gfX z*y6&B*!v-ieAoT^5)o4V{V`gQnWWSh7P=EmdsYARnT7J@x?=b1!Pu#!`WD;UU$muZ zO!9N6rh6zZ*9y;p zvcN^sMkREloo?Ulawzb|RmUt147PlK9FBfknG>+&%H7Vo6PyevQXL5`Qy(x^n<_4d=ULz zQ9%9H)s>#4gs55Vttzm@6x?f$Q<<6z^^JSAUx6G$_o}19|N7vh(k400-#@x-DoQ6N zBI2~jIgL496bPw#%Xwd7HFWgMX34Ww(%Ju0<^?;O>|G4?n*N`Ft{?ntL3{RoB3NeP zR0(pURb#Xu<0P-IQ|X!<_oo)Q2|l{*KE9EkRMK>(dDU^l##3oXn}&ZrOJefKQQssmyu)96I{m_}P5Lf|`b__iq8|p_4}P2%2d>>AeS4 zT~2_Nc^_FLU!+z?Lm~hNlk>T-|6T9v1N^e4J(-n~!Dw<~!a@71vbG&wqvdKk z=XgS%Wa@`-SRAVE>9p26n*3c1JupS;2Z8%RjA~%jzXx)CAtntL{Vms~HZSG#d37Cy zB~#sx`@#~yBPY(80^DBs*O(c^?0(EbnZ?CNase+s*k*bAj%KFf%gyBT%w=# zm-I?_=^}oR?8v*IsCi8DszEY*TFB?bpY77WNG_UKPy~mm$9S&BYw>1E`qt)h7tbsC z@sLo=Jty`N@c%@xOt;n2Lp8RaST%QZSK)+?p=`Q*w_U3U4v-o}x zFo?W%g>>GBCTY9{4J%pARYX)8W>r7`rG1hj9oKca5$ebi4lc5L?*x0SYgW_zePO>p zc9sN+2&CnS=a7LOx$TVX&lA(67-kyEpk zxB;!!Q`TqJwL~&;2U+uP$m@JSzBpa>BQqTd6l?qGmOGo8Ad{%MBbepiX!#90TK#k$P$j9xu{-TVBQa_xTEjq5*u@4|jR9WZR+rF$D=kc^ps)sa-lOPpv-BfI=z^lg zqA;Z%+nsEn1^|zti(ZAt&63XdUVa?_1q}6@|5$KaY&kT$=BWF`iCJmpmgN zyNAtoyfGCYUir(ewmno)`se5Q2_7t$?|UN+F^o8UF+Pc>7w;gTLMx*iv@#*&kx=ia z_qKe4?{&Lwt&2aoXvvVLGiuLp$X|WasF_#pZ;YuvbS`JJ?F#lLudB#gn=n(76~kgC zya9+p*`?;+Z7LY-7nZqT-q8D2b#~K%%iI5(-X%UywD?8;%MT@XkbL!SEZ=}Bj>Krj z@zT;wrA6I!y8++IM{6!Hk)Mq1kl4C5#cCf>p>m=iN zY3AtS%tTd9``*chh4mZua)qDq>%;Z8;mfn7bu9b=@KSbC0tJk^t}XRBycDs_Cq8p- z7?UO*jYaI^)G$ap0{=6R^b&q&Z&;$eO6@kk6?Eyx?sjbwnVPG3xF0X!K~`E& z>37lQYA2j5*SJ%Gtw!fNUL`mfE*MBsi zRpLa9%75EcqwGdd51vZ{;p@l|U8F2LF5wX)sucP6h7JKGt=KPlvb$?P)^rIZaIfBJ z?HfL34|4aT=6|QteR0gK7{M#IBS~t`zPs+bR2lM`cl@iPuMmD{x1TqK)5%Z#a*%d^ z-nwXc9%6EHF6N(0ppen$mys2kid4dhg_0tPDOV{$otclMSkKi9zCKTTg>GIFOFzCd zCV?t7l{&tQs@~12_T@3eGXaK)EZ9f56FJ*>|Lc?c1JI zuIHhx9b@T7Y4V3QCGK&Zzla!63^AaV@cTM@vN&&wu{{?lB5(3IlO}jEql3qrHfRR+ zV||Qg7=#tEV!}25-NoJ#iqYU}#UF>jIx)p{?;vlS=)X83Cck-vDsT*C_>K78>l z=L}>w;P75>G><&(=B)D+d)tq0en(8jaeMe*b=M~1(heLi4|YL4-`ZqQyCzwr6Y*!xkKlepmhqGN~J8DMUO z?#JT__re7|?MyfVl8^*F)JZC@q1@s{iWG}~A_ z5;xH=+WST_4}Tz*)WW&4NsM+0oFoN_7=0%xpFGz-MGchs=(ch{_I{j7zI<2W?&7zH z@YoQHVw@xU5rw*|GHf6qaD<9uniib*TFw6|{Es_;j6b!Ze*MT_mAHF&+3vC+ckNIOyVUEpS^Dh3 z>o6CDRB<;SvkBrf(Z>%5X)eix`LSi(f7VZb+50?ToYuq0J|%XdU$qLaRiuKp5R;xW z;*ne|XZ%kkbciIGM~4#2apIg+IdFur zmUTFxLS8q1#1Wk?ZEMOh3WyFQaGj z%x+GILXx85bl^E@2(-&X`mxau2ePiORsXhgQ+y3Q-zoNfZ0?-cdw9iR!#Lq0y}cBX zpnBCIy=fw`h(V5QM70xLPGZ&my-wwo{;@eEkn*GVUR!fqlOsRrX@roM+xy-MFF_~I| zc2xxEp;)a9`K=T4ud%ML@;M$G>`ETJW=pL7_;XgEG{JYV;*Wjg`w{-rmb_LX*7LzB zJSr8BXzXBH`x`OE61}1y3|y!{i6?KrZtK5*cp< z$Pu7~C=&!IJF<2=&+Rj!LKzYP`~EQZqSeYCvGjwyDfcG&9wwRf`2@GS(cg860{8#p zQSRnTM)dIs6NeTTb>9}nJ?RGRp^`0X+B{RqD@qZN;% ze`LtIjmRKb*8GRKX>Ks&tGLEfhj1i#?k2`E(cfDs_q~#Z^8n!kw7_q-Ht_?bN+`0R zBgg&1yd>npjoIl~kKK6KmyNA}WRxXKT0j!?R&s4;6ZXV{_l-ASmB{+?B4Q*Vh>)Pt`wPwK4V^kX5*F@EIQ|rG)(7_iVqv@uqo_si_#-R6-E48JRf|(DzP!i;&lE zbIjML{Ihe*1M)fcr(e3U@$TWL*UQ%`%t2LAjQ-kGjqD44Ky#4cD3e8kik{&+$qN4x zl`P+5?PdL|uobC^0 z#=lV{-S}m@J8Q6S++CDZoWdqL3FMZ@54}sJ33l7PZXT*%nh~F*S8{pk(ay`K&YL=~ z!=lu~$z_SJne!7}GGmhKvKkVSoTbI-S3Ph!vPHco`g=#9?BmNG99o!NJEdOE_*0L* zk0h5i{yog!DBX{`|9dON7jc3)`%VP0s~V~98P7$h$Q2!#|Ae37AKf*R=ZZZ>F|2D} zi>BW7W>HDYzs3K*OTy>XVGKAtY|ubnC|aHTi1?Zfte9 zsXVN!LU$5uFl(cc{TcEn-mo1RToNmerlB;~wo&Vkgb$j3wmVP38cILd(O3qZv$~ev1wwZhQKpZnBqHt~Ec<`e{^r49 zEqbCC6||S!u5HY71C4qO;BRw*C0Qt+1lM$XfNy&s4Or2_C;A%ce$@T{m2wTDa#_Fr?hRVP`{Cp4}GoE$^o>TDWdw zDSYOI|67{cl84BrHu2tT22@GyNiW_&!FbpX5D8A$JyDPoTjay!vB52Sq7Pa^!u_Zn z;TyWhw42mpSm=dVwg|s%u)WVoNxSZA?*vj6#SZ^O6$)JvtgU&yG&!-tK#4tWdw?u9 z4a7`k!+4_Dv|0&BVv@L?6I-_@7NN(VV{NyjvKBqj=Na*S)cpNuzWjN8ml&wsM-0vZ zE4xRx0Caz1?O3aSN>j0ip2^kOzg8WUUf-21cZgJL#_aU2nhR6S z-<66sHGhv;*kXrBXiYwex^@PD9wbPF#9`uK9WLh}5Bb|w|9x3uOP=WK2k||RyjGuH zFMr;&L2dma}0>we7%zD%M_|mVJ?1f>lR&E>&BDaLAYaQC(Wfv z-66pW>b|n8Yhz=X8|uhDfcR)Us1k+Sc25p_s3!|Oa={?6;0&_jxqdjZ#kZG_o|;ix~ydmk)=0rB`aJyL88KFs&Zz-5~LMl-+7>B z?Yl|>?Y6emi9TTtT06t<>~|DearvuA_&mvzPO?&qnCK*k6WaF>gH*KAC8LoqdgOC( zev1TL_{FC(j4(}jsRy2kP8K@_18~n|b&9c7-fh3<+|nb@F=}p0o#+n^H&7X%?g+m^ zv1I+*LJA#COH03#` z6{)gre~kpTJ6X9@#6Dia2j8~dO@(U~=f`zPNvZ+W^YH7ECOZLz*5?}rrbG-Mj@WGz z0oku8nyQQNlkqlm*MK8>?lgjrXZYt{x4z{Kk$cA5)?V#%*|6YK0~2(-36mWw&jT_D z$Qeg36a}dl9&CvdJ%80%-vDjrurAY5JWKoEo9QRDq zT%vCd*U;{2UD9uEgv@)OblEWmjR2>K1c3-H9w3&t zXaFLf)Ge-G*9d#)%Fb(k+w*uz&9|xWH}`0}Enc5KZlcTm_?Th|96SqqpL4i&m+iG! zM<+qv|D1`E%2{>nVot`$4(V9eC((MXfs+q)kG$Z|^zh>{)%_DM_yhzRNi_u}%Sux0 z8!_~j2lMd0H}<%o>fayaCw!Wp{?_p5e*7ivqAl_G^z#lMN+!jXsebL1;3Wz?$o-RD zK&~V%!~}}sB76?7!i8y?5bfff_|G zUDJsbi;_6!Tpd=HnKqtWAF)Gnqf?B>(%L1~ueS7?1I}3c@tf}P@MxPo%2@A|{eJA} z>?<}6>$+;_;gUZeiONtEZs1^758*Pi)qma&rfa5T=y(c&AsG$3d^n*n%06>Se6rM; zkNDZU>-*U$C!fzTYz3RF!hd}2o8O}olhY?4RA8^J^ES$MXbtx{caKiwKK-lnLrgzH z-gWinM0VO%yFcgBsdew*f^;Vix%bL)E-%OCEC^md}h&K(LD!Bz*~Og18nnFO~rN##M%j3dTGFZ%e}7**gi zMT6(A`}2>-Bx*hAyoNKlA#X=UJ=gQo|7wp-bcoSyCDuBDxArqExD}`Wa0`mL#dilK z>K>+Suk`tE^=iy0yX{_riG6%LZ!wArh(Br}gWLAWmCuk>t zk@i)`JHZp^p5&BjScCX*=|Jlx(C9n1llm|5h;xErbs0eA7j(6tK^OYqhXyu<@ zPR&xFpsp%(_5Lv4oPmiMBoB_|dw#}mRS%4pe|Uc;_Cb?0)p>jz+ELr(pb6Gbc-%Jk z06>t~nxFpAKQZ-_s)^3`NZuK*)vB03x$?)O?Cx1ckp-7FQJl{yF|yHQ-M@4B zb1C}ZNG{QMzTS%xaS#2i)lRzhe$1U1`zL>R=dmiIiND@+~8-!u7 zbzfB@YWa=-y(-&Ove&J{b}^@*{KuE!p|^yyBLLzd1C z$*wYLfWkWh8S|z;1j))==3Pc~{d_J zX8c*JDjNZ4Bfmxv8*p<|o`ZDg43df65n@MmyEWq%<~007Pdm0T$KH=$42zYw&~F1HAOUBPZLBA2SoE)AFWXg0sD@%xg;a$7Yx#pQy6 zC)?Q^WJ2DJF{;vDzq&kV7)3U&*v3`YiII6k$ayqp}3L5$U#)Wd(tj-}^+QYqUn9;JnzOQAV2N^%yb7OlqURyYMRH22d#; z8PV{9k>?*>)xXRS@AIy#K@Y^X7F=HVVzz8^YZwHa5jaX%f0VbY=sBJ^(O>f|=1f+9HuUq|_5i7bC`zz8{418!@pS8imv3-(% zdP#Sr-^QZ@`SctHp-OaAP7#hL!|>Y!5_-hROD!D`55&1F<#7G#-wv<3i@$6Co56h+ zrX7hj3HN{boWGrPKZedzC=`?uRA(SI*CIuH9G?QRmn65w51yO3zM@*%Z>j4Zz2+zn z^xBXhC}l-z$$JB`9$fT5#r>m03Qz@KAGK-k5F3_Di8urQM}UG#kSas0=%oG8Q0a{y znJ{(Os=HWLk2G%R8xt!bHk7{~XT4h#1l@~QYkYWk)c~#%!b52wES1FHpA%_LzF6Uw ze&GpwT^3hotAGA;`bAq4G)U^pJ)dGvkr9JSCua4PF>2gTcC5OTEhbo^BUT`kdp-Iy zEB&wD+dQ=7#xDr@1g;IbAICEMqw*QDvk>58DcxJJvXDEX;1TB zvrh#$1QjvdBNC-0B(YzP#K@!CP;icPZwTP&@i}V#f5^XnX%nC3s|u6le(?9>p3y3J zPaeLXSg~Cr?`##GaG|=}KX`3kQiM(;XronN{SYIy0X?CC08; zi+Zw!>?9)Q4QnmLW2OmEJNU|d-QT?%{OZF^H-PW3>V-62Ke*#=g>6Gp!3*hzxq6%r z9uF&%Et(9XscKAOYF|0)wNrou`fNkR90LN=n*aYTJAFq}93X*;Nz+dG#0oLk1s?c! z7`BD|laT|9lzr#GAe;;aK8F#%)meZ1-HDb8(;ZhWKa{NRhy8wBqR3YD+Y}OO<(yO~ z*8U^5McjmDZ&cukGY%JKY4LX49`w(d*KcfcL!|irv0AAPjjtKnccVw7U8@~USWdu- ztB*^MXuD#3 z7|Vd@_ui+mQVQ+rh6we14Y}@XZ>QY+NK>-$Yjfnj-XYKrn|p?akf8H*;dX9XA}p!S zL5g!2hjYB^kIPRkX-fH(PD@`%aqmauwfg-)&B*-C5FXKv_Ui;vxDs*?ip3I`c9AFj zdxmD`HDMwN=x9y`scsNo>dzU|REEiA>tD=4^i!LT7xJy)=p+2Tj04tUD2`zTB*~MU zdkBg!5WDXTfA#6kotsL@v~0rc*!xlU>V0*J=4_93L06Pl{3BMykvGN+!@6rZYn=JO z`|E7*YQ^s4YYQbcSWccH@|*px*EI(keSlQlKK-?14S-RWbg;LunJyvAUb1SslCaN1 z2{&Hjh9IEVz^qXSGs5ZaXj6*e|%av1wYsdYm@7 zRQ>ZxmGsjK$(Rz$oB!CJg0uT)Hwui#FTP{)8>vQ6dqajf{G$y!W*UKgOn`1-pp-ZA zUm>_rAhC904=$_!|7=djb&XYU>#=G}cD(vQ-rBC3bKW~uqpqamp2!<4Mb@1JFiqtD zpUpc@p$GHu5Gffabv*pH#VI4Ev@TBQBrp5OlIJ@heHKl6Q?fbK-AXNek{I4PUp~_K z#b_{gl1psW-){weF|hsqL{IkfPfQv0P9UEBm^edo-i>)uUR|QF#PXUlOd!?Hp{s)V z`<)pa?oO*OA3d0#?J#Ufg32}J*R5XBji7WN0aZ8zcqR4J=lDvJXFAWtB4&3VUDC1l zm`l!KVXC@kd?$I_oo$XKY?@aNSh+1HzWoS!S1F7l{Gy_C;|OHtndb(U=PaQ+g1zcj z+b=2Tq}T42IB|MBZ{5Flbj@GxH)zqVRq2RDD?674jF2Ye19A=K98nPw8`+*fl2dQ` zzZ?1-#yi-q+d@A9h10D43kFszyT-F0XMJBm#JzQGrSEdZ4U3RKxQgh%RXcN1zPh@v zhQszkHnxi}o<=v^cK+RYLmz3~5UHI|?!K=~KA3`JI3&+HxvotjREA3yNxM5zQ&;e+ zhKEvo`mr{z>tg?O1Ex^--Z!2aq#s*5X}-ZXW}w0(F&c6NX;IQ+LWPWz*Ydi*Z6#;H zLtFYHHB%CHx+qMk`rmUIvxncfK7N3jR)qtXJlyYe3V#FadO4gZ1jj+*2=1V~Z*ss8Qcv2tfWzTsWpK*Tl)zHCOcGOGT~bE)eF ze0tiz4Xux9hl^i%{=R`5Zk_%3;PrVGo}|l(S>SlfA+;a@8>azWXrQ)K&lY;458XM! z|M$0yRfBI`bjv2+Wx|L2GXL1cJ8#)EaNVtUuO9qF&oaN^`*Av`5tZ(;OOj|qcbmi( z?97vSNq*_;nbJ;`Kt)}%!iT@|O4ducPjsF-@va9Z&Tq4|&C!&TDW_A4 zQ%X`w-#bpU**@&yDf8}{JN53zdcKtVawUgN*OSv!Qyp(k;IkElN z+Cw8@`lT60#48ve#7&T#M1WrFw6BxgB8*oYxK=?U{e3UZQ{GK$L(Be>Et^qr#8Y<3 z<9%(&!xGvA*==@hcZ7+z`=N36%G^0P0V8Dn#2FX2{C<2-W5Z;AHkcGDt`ctoF$}o} z6}t-|Aq~VO%s#1bhW_5&1oG#aY(FtcFipIcf#iqe9LDna^Q z&7PXS3P=S|!U3?WHNo@~{}?!tEKBvbJ$*b?bH@4=k;scjQc5S;M~Zin!{bu>%}5bv z#itNQ4nQWiceI3l{JMu`J)gKhPP>sJnnaigE^Dty7ir3Cb^otWgbl42q~DWkvEx$E zB~e8hvW1CT3t}Xl4GUA1TQuxBOpM>EIrZc2nVO0I%K^Nk7g-r`#?!SRu?2*65JPyx zPtI8*6slSCwFM$_UT4X9UDlx08mmS9Sjhi75_4ql*Pz_@=jafW5*AgLp`b{tyH?sr z`>q3-zDO|Xw6n@P9D_(*zZuTEqYI3gWju(2JRQ)-Kr({c_2%=i#DXI=dp{l;BE=z` z*%K10Zdv`1ElDY1d+g#oW#psG>$Y0fX8Bv5sn=QL9WkGH z~$21;ZdtAaJ8I%lzQFPYh=e(^)>=JLk~6A630z+GLGVy?R+LTAo7nsw!A(owhNZb zIOE(XPA!ngBe8BM68!}Qz;+Hf!3ZQ8KY?0ciV+C_C4%zN&9fhSb9{yHNydqHeDWkZ z6bU6pZ0&`_z~Kg7lX15LQLYvBJzJGY;Rzp3bK*%PaO+*9k2R9id@CHb>wRiTKqn%j zAQ_)W@Cs+fqS<_Br5r9V@8795Bn?i+zWHZtjTiM|In`?@}DH) z+6W3g5KNJfa|=$k0evg7;`$8mX(7P~O4GuC0+da+#$J}<&f(sox%A@_P2g^34#CnL z#OMdvU_a4oJP9qVDzJQTsW%$bTVfs>MxIy;vLp!nB&!lRmr-x2{n(z?N}+w=pFW;xJY+GAQfUMtU;NwJPhbcE ze6Wkepljc8*1uW|-mf@VR|Ek0fN=S28B9uI+RO-9Oau4cMhrO()CR-f-!0VBqqSp? z)+A#o4QZ%l^<&yN&3gWcH8s?1ZEed=fYSZe_6Z8K8O2oVo+2~TE1$YalRZRvNtv^z zfmUL=g$7Np5ym2?K}rCp(=AnEb_pF&%?%@`BU|P1e4m zRpT%)?WOw!%&#Iq+u(6eqy{+3H3Z3`!&o6XS`8Z=J|TADji%g>k@Gd{`L$a)Ut1mX zOcJxyj8;)-j;C=*7kb4YIU)C09|Ox)@Y?mH?;) z?~`j_3+Wsr^s+hk<4~%9QZvziGDb#5^2INSc9< z87zY$YcHe9hLP*&2!kY(_^bZDb)Iw)E+{_GK?9J%2qpte@a!h>#E3VO6<0+JiI5oA zQM&{Z`b@GGR=7`>@+7t%@gfdLcMMzf5+lFF$1E+WAMXS@?9xp18&;n6kijvwJtiUU zB)Ituvt*L;-UJCDsQk6KyBxfapP@<0=(3?M>$A=V*f_VquCs0}@bg>B7HgFTVkw1b zqYyU+Wb^9BPy5S4s8z=ghiHkXBrLTuRh36)f_*qa;~{w>l2@F3wfM|SIq+hd{h2A3 z_K0QCqbwyxAo04J_B0Pcpsk%WXdn^O?x04j+ZICE^X{L#Cn%p~z~2JQmrg`A-KeI5!m_Tw^5+;#cWTH$KNr9*)0W@Qqn0Jg<7&=UlpYEDfnc#QBARsNC zT%XI3!x!9`s_kU0+fWn9^)!_SKsh+DA7SK zG{3s<-Jg|qLb?>eV=t!iTyLG`mrLzhV^}B&Q)mViF01jI4-&UA1nM9p?sw(d0ZJN? zB23hw|C@I|KD|<9CQ-4xhn(pmxW426ab}*8qX(Gk#R4*ne#htq^5yvItXMzar6y_* z#ZZvg^jjd!0B$3OT2Pa0(e(Q9)84xE{PJCA0nA4u8y>F~Oft&`yO+yZHYDz2o#o5X zwrFD>m&@0H8*%VPOrDWdu2@#O2U)rTi}u_@ARpGF*S#N zypf!@S!WM5eYDG{)x8#53x{yr1>{+KSBg zvh6dmoS*%ZU4Wvzq=j9v1w&c%+ao+G$RW$qT>5c1`KkildOm(0!Spb(Ni`Btsl+A@ z?J9XOlRfmS`zz^}D10U}3y);i_gn_%;CYxTSqG5a&&!tNArRA$2rI&91y1E#%DdWu zHd4aRj=JZ=W3J4I%nh4+KgLc}y1JHZ=)`mHkyg7A09?E_ArGnbWU~eX_fi_d=+rwk z7v_`8&II(5eWc1-VoexQW_O8Am+{FXBzHif-4Cy=kL(L<1Bol-5ZezmtRa34L=Ui3K|n z2nk5`2{XfqNys4`b|eK&tThIrLUQUO?xa;W3>LS}Y2eXDC_mQs+clkjlzBtuYJJ)t z>+a&RXFN&WkURodP=aGL%w$pCP05f{t|t2J8LH&P>1R92a6d_?f;`zP6@gP?ZDAVt z9I}rn0$lvBMLb%p=&RwMR~^@Fst8CFV{-hvJ}rQTs_Rrk>5$`Dna4Dj=R=#`N=2w; z63>O4IEc+(k^rE^Ia+=#=aVCx_cj-gkWyowgmjihL6ieNBZQkyzeTT=-xT}tdRpp! z<$7N6yMe9@a#C>4P0THn`ekkp0b9O4|6bJv3Jsc7DS{QTi=@tOhad`$U=APSzU0e~ z`aw?pAO{5jvX%{vfEgrppLG48X4?<)K0QYpws+m~PS7x!2777HFv&XJgd61~qL-ey z32iic5Fx?T=M*spxN53xx>8GXmOuGy+RQXKlh9^FL&jjUB}u5KKe66ilq zcbQU-t>|b=qG)6qNukin7gBJ}USmnXJ&j3xh!{41%mtqs@olUg;3kBqB`3?6$>!LP z=Yy9PXxHK6?Zrf&?j2VGVigG=c5?CrN)(UR~qU z!u?zE^1htZ1uy>3)uT0{82szHZ{^;iY4xMr`@%f!G*7DdLvOp9ASah1xxbPKA?6K5 zqJ$?%7_M?*Nb*22-i#^gXX3Ks0SSpLiP>vkN1tds$y~wZ6ZPjn>bS0;B8tO1>btdz z$C(?mA4~fsm1@WC^~>H4khu3ZEa=s+cuVpmpIx340imz%ICs3#AI{KVjV0iSU()pwrRJ}foP<`^1l{tEsm#Opv|_M3yS~NQI;Lir2ySq@Tom!-|Yr@&=Xzm z8mUp?mnJyCE5BhNMJdRDq>)jCX=uHdNsI&pH8{?l#%Pm-JVW#B z$LV12_3BY;M$fPp3hgU5^7oOWNIR_9evLfIY{;IcmXZhDF8`GO;oa(sR{FzUhRHHY zq!3vl1`{s`PHqBIoM2fpB{TP0ytbF*vap;9Kt*Z{N zgd{ILl8{t%&pb_dOr9h|r|}f6{%Ms`Qty4D%ZcZ@NIEUDE(cy`b)W?qp>BlY7!K0W zGapyu?T}qL&?C6k5mU0^tI@Xafe9JSqaQ!%A1KqO;eIll6kr$<#uc%Z09;-}U>p_j zg+$D|f#90DxAX15b<6aDP`d;B+DT#(Hqxn1awnSZNZ_eyCq7_-L0hqnK#7t(f>lzk zlFU_|HQX+Hd6ehe@z>D+y))zSCSFaqABWrdNA;t%-|9C48izoFkRT1b+JJJeJ?ezP zEX5cO;n4WmoYa#JQ+>k=L6;gMDR$0wkOd+Z*sBo+aE3Tuk`zl2^RRM{La+-vsSsqk zG@?f2$f&PC)9J^n$yaaCkJRLnUF}FchKy26h@3x-xU9SEFK!@0N0wTS9JJpaqrZGf zX5`X$l98$^%21PsHC?rU7^K;%KZjhPG$LQFVT(%?$9K8%CYoA5&iLPWz+s$*W{=hM z8BKIoQke`Jiqdu0fKZHw1&Z~Q!qQ;JZ7!E^a_Q!Yt`+7a^v zu@<(N5~`vh!k(6FY5(k{U6PBPruyi2e1eHE%+}@0hdLQXJ7AJe8%cJ08Vo!F;@YPb zPmSQMh-~?N-qwh>s~}OHgUgvT&3gD(6 zV^IkN=V|iC4O0?ymYgRdh@8-PH4Sn<(e{SvPQO~^Hg49AT z*cloAifTqwi)rfpc+0zGiqnX!`0fZ&Ok2&=#1BG@_K+VV z5S{Hp!oW&h+=Sed)9Q%ZR8Kp)zNcOh6zi06YcYw#QKM`oTVs%_Hm{2q^iO$-c;u31 zlkS2GxVkCyW5Ecm_+$0)xutIh^|GHD1iXQpIs6K05z`IP=*MX+?u{4new2S+ncGxv zedNi`vFz70a2PrK!p~O;k|SxT;Pj@OjDi90)o7seEs#+Zl664FOiiI5%LWHaT}I-? z`~RJ0KNsp5XGh`QQv3Pm%4>ESg1Z!t`;N2v-QS$%HqnzzYQYm7-OysvRMnKTNU^HB zuUa|`8L3xR_d0^sqS`(Gq3uc2=*RMb=asmO!mJa2?~0|?_&}Zj29i8UW`)z@L$xQx zO(V}0{pOZz`cL4xwXReBg1t|+(|->V`$1w0OyEu2IV58u-V;PlND+GujwhzqRF%)s zxcyk(uk}fn5$G3xzmFdHg1C32keI-1f6Y1KiZG7#>0XeLy2o{@C-47bPW?(R6sNxH z_o<|K9BJYsQehJ7TmI7QGqDsW=MdKP`mwx^f7%VzF=Mc1ToGHDkm2R12#JcV_Uo<; zKQ&?IDX$pD0;mM>SU0!b(fh9Id;i#xM12ubhn<>AB`K1RQcZxH3fRL{&j0pZnf07Ye~S3#UU*=4oryq+r+AzlCYQbMWe?hOHh% z6CiXg3~M-m&*A>98owXQx&@u4xnKT&6VCeBmhR=7M=xvmjq#zd6&#Bg9lLq-52CB>A z7WV&3DMSM?fin7E^4rB)XXM%0c}=Aszv&b_?6#Uunap6{u1f){0oDhy~0rJaI zO06h)($w=12d%ur{%=g(ly%0}1z!h~vo^a=_0+=~y7L)wQUrK1AOk}Zbc%2i0fF0o zNPuJR5r>5pWtjtfUJkDv+pCS$kN2i|x7LT;R`Y3-Dt)6+TC|tVIQ}9rZZlZ$ih%f5 zlnSgS%WHf6KxX$XIyKimYwjO%JYBP=`e#noG54w7`s5S+BoMA!?OJ2lpu-NJf{TXz zuE9_vQ9x;u5_mZBNI>3!2DA1^yqbya$8Y=lMNVU_bjDasKV$`W&NGnUBg@XGH&(Pl z4=U`YpMEj(- zKOU4>)$kLp%|5TdeL4mzI<8#OyEur>7xE@gcO$x-`|Qdu4tdAdkWbvS*GVbU=dv2d zp2L>JGmL0mfNT32{WcQZN3c+rP6VXk`%&sm9o@<)`Fml}-Q7c~esDSoI#3q(huFLW zfiu+}3PMQaZTd%6>TdU`SVPgjcFQ9p4hH$q_BHR?%`ts)*_S3(_(mKCco-UhKtkvy zf;W|e%-C-z0cB5euGLGpJ`_!mD%#U*SDIEo-Ui=4pOzvbR0-)ro}}+zt6dZt!GGpIh;zdn*Dmn~Bry;vFib7L>3S zoNFeEC;rzWyv4hL0khqv`NjKpcha8&n`mKtU&FN!fv=IzrrtB_In8VGmqn9iZ|``< znmG9C_@v6%9w8%_6{f~d-6ioD!PwsKj{2`}3yRY7Y04pGf*C%iT?g^`i3Qc>=J#Jl3=@yv3_Rurh-hP$XiNjL8Bt= z;!J?Xa|OvP;gTM)83oDd)c_wlRBgZxyIhW2N4iK%KX$ge+Hqre(fVgI-1=b`V6%x_ z%Bn|($={cwjQyaLkGSk2smzP{iyIZ+?3{4z6}jo9dho6Bvhso zH8W5RgBY1LowFP+Xi9s6emO22pKTLGhhSdu3z z&5$B{p@HHMOz7yo719mooV7X1ryUiJ$dGmy9*%6u>7gdRA1|hU;StBYPsQhEz8eTX zW4ZTw#_L%>?m4UL{_LVOf}yxkg!zelpFKjt+ff*-eEhE!KX1T)AKO zGhR)ZIjq;pS|r^CM?ODyW1iFx0=Rwy%eovKS{8+|BLdZ$TFYT)=EQ-Yob|I5(u7wDUsA=@YX$hO=@r)0Oel_GR1)9yx+k@0Q zSG@$3{97HELMOi<+i%DV~?K*1Yez+`k;cU z)uGy6^Z`cI{_x$h&@x1g`0^`}HimP19`S zWWo?IWt$KH*Uzl0gVQIh5%uPKKsAw`%J@nCKhq9&87l|b_Nz+3v;lXA;#t5hY z!|1xoKuc0cZX~iFmHty*Kfb<|^SEnOeBap6xjcKN|NY@@9F-AzY(1HVQ4&%(Zy;0# zr6IBWn9RdgWXAbja zGHKfL$roa>vDmu6h`CziBaTQx$RxoiTEx2_XS{VCZ(I}$&H2)!5B8`z&&wH~T^v(6 z_hFkLgoGx*_X8m@NZ{v3h3@k4=pe<#EBQmFJ#c&5|9jr}>6bT+WL{<8RlC|r;kjNk zLQB}Ri(8S>Wi57v8@C|a%}z;f;ciJ$pMJcOF=2L6Q7oh4@_qmARP#xDx?UVJ%zMsw z9)XV;^cpBNs+*HYmSt{B_}{-8rkO&k`k|ckyTL_0@*mDR()g48{NnpZolKU@0;I5- zN6=#{0=rNm5^W-pOv2&SFr|?gH4?14#_5|O&@iXS8oz$Lmi4vivEn?gSoK22IpB3* z>d0Vm>?5mH@l!oGB9$hV(zW2ODFI0-c0~^lr~0d?srf5Qg1BN#-qEysMrCh{CAf<3 ze*Ln|f9&ZZRUnHgV96vbc<=6KdoP7^L!VQU9WRkWh2h)hpIb!Wcg}*WP;|l|^b0W&e zO?Wp@eU3xzJ8z7~xEeDBWfssq=l(@%u>|mzWTUCtLyDSH^k1g!=B-oS49Y8E)xQVZ ze{E*^!A73!r>ER`D9w1t=$TjWg_Dp-P7IpQR6l@|WyQ z)}Vl@jzby07@6D1NxF*;OdNhxksPw?o*FAa!oJtXNGZ&?=DFeo2(1M9^U++zVxb!Pc3>Yx%v~2qWWGcg+CD5UF^=^p^t$C|`%)%(ntR?|<#aMorO$x@=!GSgEwJskLe8t)$j>)Age~@KFCxtxT$aZm0ZfZ!fo*% zO`m<3SgjaRur4$$$a6b~Aj4CA6a+4C)@8kF_wR2i^KrJi?*HEkEbVpGnpo@`;#Xtu zwEG9cUri+zD`wb~hp_@Aj8I51O!$l-BZf`6*b(GBQPQxk9z0!=SojV$U#MR{-ti7* z-#+1z14(fJR<}L$^FDMgmG{tqHm4h6Bqf)W2W0sS?#UUcVxi zRr1Eg|H{D@PvMuN(tD)awJVbv6(^s3@LN+}Y$xls+hqeZpjiy_Bq1umwob$f6OdZy z0#(H|em8C{q<*}TwsLTf6-jZDB<-~M$T>yV!@+TLlFAz@K3v+@jyfoU1_P%mc<5t=)I`;JkHszu;m|2JZH5`YT zclbahFe3mt8uJ_>?-yNfop(uL+{Qv^$o#{p)n_`?uEW|Gsx*Zo7X}WfCi)Nqyj3*V z2E8+j?i?Q0eywTrPO!(Om<5=3FX$8UCHQ3c@yk9}Z%%4tLd*@D?i+SILna?+cvS=< z0|L|^HOI=n_1Hg`fBKPUvUR$qHQ(jU z;G%)o>}+hfZ2Lo>o3pcvY`H|xkDYV7CZPLC;Q5b)Ep&QfeDPC{T$9fwtC~gk<8bDb z(I4IzZyMCa1&{Ttjx0}QeC%Q87@>UR%)->_@42z39PoEttKpOpOJwB)o?R4@tSYd^ zldRpzJLC6QSf5LM{)z8QEJ~4`O6&fnEcolem+Wh7!0h?u9d_L_xge)3fHh(@y0KJ` z7E*FggRJ5cbPd(Qz!tUtKkFaMdT?x?4XN>GI)*M^zx#c;r*i+%o7%c65r(pMuYbQ> zOz#(iZ(Aa_ST)eF=yMWSjRYBzt}W`Y*Z6QRG6PzenzOr}&(+;C2Zbyo4+Eg11<4a9 zUC;!mFi90XHqIFP*Bm5^fFQ8QMT7utHD2aN@iK)X=hx&G&$s{C$XnMokeGPo&~s8^ z2eXIIb56g7K7Mae9iI6w`pT)|tnJ5>4TL_)lFd3c0y(s_x;k=>;sse#rl%ilRiCVQ zMbEWQbgJ}>sJ2n}mBU$Inb)eU2~2hqEV^dP4=3+$S6A_yH)N75jid=8@6qk~T$JzU zTJWM6Tt=P*ojH37sqU*4{`Rz=jP12P{ba-4v9uHa&M@Awz}H7*9Ce1ioPKajah-mg z^{-3?h+QezdGlMp2t&p^=&?Bhv=WY*SKM7T}~%@vGr4qOA*o#TCRhvYq7 z$KTz4Ya+H~;~)6e$h;V0(cA-1b_(yuxI5+Vd);o4kwCb&Celk zeTSRo)u-Ow_Q?3jhdizKWc52L0;~I{ZEq@5{r$b)f!?bYj9H(f5vGvI9oTEzMGQI~ zp*qDWkdN{v4=&HYdH%)wlA4;TV8qd3<-zRmejIGCac+BbfJXG2AM-k=7unV;ay%(- zV{}k`#{0vd`r+8x@B8JzHv{$}&FAhO`pER@Pv%t1%I=js4SziWk=-M@Z=wb_W+V7~| z?_9S1^HZN~7v6=VnFAkaDk^kg;e5qMwhvnV!y!+0FY|eVA}_$GkwkQ}KXhB6UEe{< zZ}qS21p{wel)ba5ui>?#XkQ-_it5LLn-mYI@)iA!z)T=fgiMO9^#WaC$vDZ0k$?8T zoZhCO{+G3N$&dxl2O>Xxb#Cy3)Je+8!4L1sJ?qzgze;~w*LLgUH*neIfLi6 zEeOOT5$7jiNw;vJ)g`^pV&g^3o=TI1`^1XZ^QQ9_l%H#+AD+ zC~5|4crBK*cz)lCsH=M@GfPpH`^fTYzcya$MY@Bg!CH8-*fTLix-qljabYh!Fma6S z3G|14ZR(M58XszXVDz#v zvzGLFw#(4~RsuCJyX|g@s539pNIvR(Luy~&)9$feV`hA2wO`VFruPTn=YnUtNB6P9 z@24;rZB=rxJ>ohVfl0u#Jjp{sp~Of|hM&hFa0ovh9Nntk5LN%g*C!oH=g#nG*H+;Q zH0AwP?CMna9WSJRaYlWE_-j@kO^FOgpU+${sQ(H-o;m)qcKdp4y?xq}k$=e{Pd0Mk z!#rY`-^9{XxLAUiVjcl^FXI`DtWouEwq-6EId;zVyMo6WU9c!~;+>$#M-?8A1 zId{yxWBz;pF1#epq9tv2Z0$f%tON2M?yITyjGT@XUkKg}-XxNHXz-#^c3?GpK5(G< zkaypt`c8uji|)Ala5~04pUC{^=J2pDP?5iIN5@c|?^Mv?BP(MsyLDapTB<#kIqMat zetTl}?s)GkTU92P=57AgyeVV;n)hPvi)m+|20XWNBC7?Ytx7=jfUI;J19b7;_Fl|* zvE%x#Q>G1F-1aY46tgYO0^{K$S(1cpzKAF+AB#$Cxae%ao% z3lx%PdHYVK+po1O%c0TxHI_%K%Q;wT0#CI#=?|Y;&s|s9XXh2<*ewylhxY5KADtI@ zF288#oSoUV->Jg?&A{|sv3!qf*B?tYo?-bw#yjf$zF*I!4OPGQ%l(3zHs7-Dmf35D zJU;00E=O4zMiS7>`emrpcPjXJ@3lk5O#1)qeF0n))%O1|tn0e2NQi_;L_|bH zL_|bJMutR&L_|D7B%aT|na>P~d_E(~XJkfZJTo&QA|LULXJ%$*WMrgFUPZ3efgxhc$23@WgjYXp#6Wf>B5!| zW|q_R++4iL_ax&Wt@5s7PMpC8(=?(rVPU7i(&Bgh+$^<>+}CdZ)kOls8J71?3h()( zuevZ41=h}~LzheQ_$JSa+w-2yes)n#=5whzK`k!rF73|kPVHKPtlnT$6+xIuAX(pm zoJzZ{x98sebNzacBEe3u&Hbv?NyI3$!Ue~Ro z*BZVa&10?SrjsJOevkkNNy316K~?U?vQ-xl#jctvPMP(@&ce}!L#PlacNdxcAdp+eIpCsY(FbQR9M zpLtdI_I@+4BDf-?BJ7LsFDCTvp?5!XVp8U%EK6CNQuJW)gC)r&f$JTs#ZK{}#>*W$ z9#~fCrXKwBe$c4FU@J`btmx9w_k7q%| z&Amrtbm$hldWU7Z*e14!&0>>S@37yi<~pnGsv3tnu~9rIw)TFjL+t83n?1elOn;oz zm)FK!fdr-Nf@!us2y?WX$8~acrxwyVyFHg@51ETh01M1uf3kGuS@9i3JvuHZH1KM( zOOV}E0OM-^9)DaU9PnDW+UryQE2tVTBkP`u|F!&F zAJP4@2qJ(;K%(9L*G<|v3mm%sZ)grFvV7+WEuWYZ{0QC{J3?gyUNp~1KL+--*KjEh zAk4Hi2n%!)2Ce}Y+l0}ngsVQ1D#---{bxgP`p^ybamz%PeCd6f0qJL}lv>PM9pMaM zlyn{U6z9kO*851wPsAM3s5KV+61${^HoKe_AD#I>3GMaA-x5m9Xy%_T)2M8boUCSH zHN2-Vl>nTnMCcJ>SM%&;(qc100Vz>sn0RE9vAwZ!^Rzw$SUtjeyEVt2%Xihzmm*Ja zM)P6$KIPWra>p9@f@)gs-!KMQrw488G^m>`=+)O+N3WBd zog+_cPHWx@T$TRM+5epL&$+$ZoK-2W1T^)&YZ8?Uiqo3MQZ#mDPe-+U?#&}0z$zOg zg0|=XhUZXjK3nVc1W74jNPf%p>el)5d7kFUvyUSyTMjQa~5_lZX6f#JF7;T7*heCvKj)^bnK z81(?i?pG#CsMaY#iJ+|C(E>ijaAQbi$le?F-LNlUpF^G4Dt0*-|If|min?$4!Be7O zzhFXX+K{R>xN+PUcdtAt(x9aMdpRQ9u!(EIv4Q|Z5$rq?E8;*vZXj%(4>QpqywL;A zKL}8nQZh#t?Q@qHZuo~m4`bhZD9P7nS z@eh%(6OA`-p1y8+QSa7!WNw?qX5Qlx&0?oxP2l>}b<+!{uT9HM%X}#I!wEZO+e@)` zZe(9+uez;%gXP8}!l!|b3^M0S_Wt0EDZ4L7=eaAr_W$6bM(~Qy*=Dz3moCwk4`_j8 z|Cb-jRtJhn%{r}tFT}5g&qLQ^SUp9eP>HL;8M+24Ijnl!wXTP*s9 z`~;|%>(=sdikzj<5Rd7O8IC#iZN-7hXFtCnIM;ixSJUX`i%ObhzG~m|$&JnfSn|G~ zC&Xj=zN6acw>Ug=l=JvDpE5PQ!|YsodBsp_g2)ss~l?wdt+W+@p8n#-n+zmXYdV*%NrWpAc8}Q z;r7_Jczq_+&kGq6v&=wZob?0x4<~Gjy(d4oT)f}|epKtDvrlQ+2~M7ZR)S_w<}T)Y z>`ch}+Lc%80dk0MT7h{W4I0>vZmm~2&vD+lihUHY@*FmB2~e;?5G_5Jz0ZnahIJ8d zTB`y-zFv-f>#qar*5$?Av3gA7P_0#m_1szb`8k~W31MCGFy^eT>3BViaK`?59QjEL zpq^d|R#(%FEf%^oKYOj7G=I6i@j|WgwRKwp3C9wKz2G5?EQ%z28o*Bk@?5M*w5N}O zzYaOWbdaLin#4rjhe?>Vi0i!7w>o2?Tmr(tAa(%^3Ij`h@%I51HCo=Aw~GJ9d1kit ze)hWW3C+lyTFeJaRawH=yfCr*9DmE}$&6Xu{PY|G3LHuZ*SZ8eheOJsmF{r^PahIk z2ukxa+Qg!$&}~BxzV)rx?5+;sz$UMPTc6N6*6}YVZZF#7#Ujzbj_z&05nyG)r{Rzt z1rc0OsGi~}yoeAr3{DXAnm@e;x8S&>YS!rb`qX)aX@zM8>9^#B?HqRSg2tPT*R8Md z?5}XU3U#+`-rTN%DHsVey=ePfy#W9NU$vu0L%7^rTwRNjBi8;S&M>4EbYa_$xIk?3 z1SBjbfM6t>2k(qf#C42{!g9+msdAzI!v{r+3AwKJV*9G{t1zQ zHFbnKJ;0<;_>2w1Z*aCEJJM@4z7Br~@dP?G!Gm*ad!4AD?Z_&$zRXqPAX(;7u}v0Y6yRj<*F7vgg2q{K3`R z!uv>y4~p+b-Mj_&rAAfmb5(O=5$@X$V!P}6xyQ>6;SY0g`CCy0&6YrRd=LmO8k_g{ zDO)7Xpe2RNGHcMCun~)rMO< z0y%}|-C?Xt!6#V^amRfbMxCwASC_pVVz&D3No~S1__ozGf9MHGWaIiTYFo%1TB8uW z!@i1%>$ko;u}4FwIB?hwqww$o=q!w@IH0#)7QCK3oGZg|ccDpxTMofk2*Mv^Ukie? zP+kiGQy8&asMNgwnZtZs#VHLh2nG_qv2B@J2+Wg>wXTGTc~XD0YrtFNpP*SO?=+d}I4v9qRuF4)oS-frMJmD+xU6r}Ue>OEU(D zx!+jHil#~`3K?iV!2D!opN5wAC!G>$)l6UcjG;Qp$t#rt6B3m)lBmc~@VqwaTE35^Mt+pLY+7f1* zwd|RLS*8E)H$ru-|Fed#xwBbKYIy z38STNFQHT+)YghvvX6DH_|@%cn_19R= zbI8dFfoN%(Z(O70p29AvIIhJWcC72ozd|RBcn+?iB+;p)U_yEM4&F%mpH-X@ydFZO z5i}MKE1>rOT;~tzl1Q_->c+|SZ#vqMs zHoRlo5xoR5efMo^gKetui;rpuLx_Pe5;?3EG=c=n-%o*g1F^N<-Eyye=km;qODjI6H60cPr7rZuFkGK8S@g=ND%U)s3l<%5aiNz@v^2v&_`^h{Kj{j%*Bk^ z&}qQrmiqSApppAvx~^S8m_QRj5rWv7lW?iVo+a4yf&4CntB zZ@~@M=>L68ylw7uxW%(^ZS1DCQQ%5d<52wuRA zH)1O$Dva@TLTVe9Bl^M1y+$^hZ<6!m5WosKUGqI9c*+L4p|E^`ve<&vqRnu54RqCh z^rhKx$n}}z;O)%2&NR9)p!V9{EueQB+u+bOwD)OxI&3)%X69#nW$U#_h_()$6;{C+ zM^uH2P6!Dzbwid_*B%VsnU?kX#8$*v{47*$(WXmdr4!*M?14~5^gQ=we<%@?nJY0O zuk2(B-5c2JC!;Ae-&J~2M}u10_A49c=3RmGcsfR^Oq(MJvtNQlr0)h<1hQh_{cZM{ zx#Y_I|E;BCKxK!4aM(t`N;UTR#Q1LsuxyeT%rsWoYgnQH8hZJY2AgaXjaQdV%Y8R; zkGq7XzRm(a-5pv~?Y(E|6j}z7z4|81fQlr%91Gs2aXiJG1O-3RJgFM@aWZ6mbZLhH z&v_4QKMB^enon7`M!h%!s}J?t+7##vKAw&D(V>5J!jwYs;%9$Mug0rgZTf>fdpBv~Zfs+&ROd_lwM-c20 zBo}S{Ft`We1~q^0i_6|YvpIyT>VN&CVVrfrj(JjPe<3iP4^$oiTkCCogsYOFVh}pj z0RcP_Izc#IZ*6^?pMVU6nJegaSYHp5aMbW(;%yt9FXC|bEi;NlfTS*ILP-Q6gCtyX zTG$$#egE3-owC}%QBRhwB=*Lwy!i1*6^-v!W`z%t;YHN7iFHVwhVKyRk zi(x?d%-H>hEI9SShUGtxi;I;Zz4Q;ra9Zb3&ecjAeSqpXr z{^Uh-8;O7u+4ui7GegZq`DYAVz7K;y2v-XtGTI5$sQBos+HA`lv{d z+_C1Ez+#TaFvb!Cc~lSu^_;p04z%&2qLokLBZ9$VyD4VH2`xc_Vzvv8KP|HyUw$%+ zY?vVWW*9INTJ*rnpk+WVeRxls1|vv z=(`fbYIuP_uTvLjGOV!LlLTh+Ady%Cf(lqwx&3Xv@Z)#fM>c=$jypy;tuA3Qq-8CK z`ra6a0dok*AVVi{=>Pq%NtP({fr2mT^H66M!MdYdhpv%IvlL*lQ9h=`H z`ky~q*L}1b1P_`KgrkfIm}&}(m9#@I0;}{l)3GT6Ix6|`nuRWHmwrP~?%aEpUAgbF zMu+ynB<05LZ_vW*s!#@( z1SDmI=7awCnw`h(=e->BGoVUtyq1W~He=at2K6K>+>%GGA`HJpY(i7o59*q;UjWVZHle6F7~m@vsG01KVyk9v66R8M2&s( zI#I;dhM>$Pt41ego=T%?CrrK z3yR@@H$3k^)oD;V#}+uF2J+ees@`P`y<)&v84Q(nu-3Nf>^LpNEatttrp?7DAm9}; z-@qg)G*KncV`9lY4JuG(-~7Y79W!Wo(N8@kk$ezDtOl4co~31>U{J^Wy9+_;jM)VZ zEKV9iDfuJmfS`kPf50X1m~g7)_R93k|oyDnzFUybwH-lP(twETw~w_WBFDeagtPA4=Bq`I7)tTqE_fZ zLI50#Y>f^(yR1PD!ygHjDeS(vulp{~%m5@3>331zb5@{^ir}Jxc!d(@E^IJ;H+Bd?T$cicE zg)`)(5X7TkQ|d`bOxcR!S?kpR9z**VzIb6Feks(qz;NaLuRAqvRyF*SOJMvuv?|0~ z;75fc-3MCC1GEB0SAOqA5MH36wc|&xk(_C^hcXE`pkaDXTx$)3MXPB6kwshHWvs<8 zbdSW9FXtuwm&p`_@bYY5zWu0H;)*2@298fc*Hf#fz%WXmnhq4hjtL~FA#LIfh@C}~ zhg+9-OkQUX!Gt<5Ouf%H41PN&kufhI`7=&O}LJD@OJ#{Jc1LJHGPp; zVCkwr0~P0M$Mn`%%S;T9At}irp(rL@)(wtr63)1&gp_Ekj^(Xai!L^cI9`~Cw-lD4 zJuhAnfgRBkb9lH>;lu}wnwF3XFnqf!$NC7P$}U8?(O_HrY8X=upHId5gj~yqpKAgz+X)E~BkAx8=Crrm4M}-j8eQ>XISnC7c+BUqBj0g`c;!Uf?FXOqS z@Zv#2(`uIQ%udJVbJ6^R1}^=Vs3nX%6<-qKywgKpmT$EhE|bwoiu(>2F!fJ*BQLt7&7WP6%_>VO@Nps*nYs1RqP3uF7eZNun}zXJ#UC%53gZ8iEHRMrc7e`oIbt zSS{$c#gD$qB;)~K!dQ^8lA?r(&Bq*RdW!?PQCw^T!@H+0`^w{tU48J{@wJbRk5t@L zG70u^Va_kRCn!CKAt{eL%tHw@=q{kwq(I2j*S*$jonK%pkI<8FSvhLhUi8iJ z*;V>87s!q-y>kO!Qg?m|M)QP&gjs=EwXJ#Ci;#hhX*KdFOb~&Gro!_2iP^mKdC<0I z4zw^{s0f2c`~QFFbpyv^M>N*@!RLuE&m|A83QKR&x5sVdlC|Z$u#O9rdIJ~-lGP0K zvs>$w1(j|W#*P&Ue0QK*eWNS8`Vdcx5=PMvt`~?`XwvMvi1>B<=4^4fF%+wS#J-njWFr~>3Z&Tr7qh=(Bm^~26_*j$CUu^QmBTTo~0{$Iibb3nNp=n>p*aI#ZtWmx;4=e9ZxwH;-l!}!40J==ZQi7*Kbs^X=! z_!TFp4@+yoxdta3@37YQ5bt~6@9jauIH~t9Y2EHatH7p%Y_alH+G#oL^7n-f2l;Qe zNx4wPFr4#*YSvk+e0~?zChFe1-J5f9&~a+Fq!Rn7m_2J&Oc`oB?kM1#ZZ@_G_Ry!P zNtW>yy%J_kgknY~0=ul8#qPl_`KKLZi{{!bycc)&0=0z%5S_LBuRigTb2d zWJn>4&vL>fcz|VbVKJT`y+xbHrpNw+27)5WSQ3o(w7@jS`hTX)9gMt!#(3N2kNu59 zXve4S!#nr_!pjmM)IC9}6&x6?U3U&m&VfPU?M=o1W@{imUN@n&J)0wvHbp3cZqHG-~8+p7S{lwDw*Pu z1$GPk`jkgF3|3X8Co!kqZRqS+kjdBYWp%aA_{v-YL))xlz&V-Wq8_*fH<*st?G_OR z%H61OckgQgSQUvwCfh@+~5HLTzOFRSauy=q*<)7TO+4g9| zON=598z~kTV$bO?NhEz{uXAq*a5=ELGSz`^?eQ8xS!vG7V3QQ` zXMoi_{ISpCmU5l#?n>YOquT42x@bIg&h^<{5I8h;1aMZf1#CK|g>d*h(oGNzCP5dQ zSilc@%ntF4PE^k5XT-!c+$4~5)ZbjnQ*%PQ&+C~C_ubmAL9PqWZujJ|KoSI95wnRV zBn0q-W1+6vL5Wh=p3*?9VpstPn>c~KbYYQZ_5WU-%6GQoR$F>HW_WqZ?VD_`;_%it zVu!+xcfvRYn9Z*w(=g}sC4>ahU=Z$aWBl+EAkavql46%e+M8CRo!+-X9XKWI#{gSE zq`y^mX|b>dj&tk#UmiR^E3n*Q$OLsAq^B3-`3H%S!{?J8A}Zn#mMw>-Zf1Nf^5Q1X zc>1V!<~!RXI=}LfxBSfa@dd6`@+&HK7bXt=sE-R%)^cL-8=G9z<0klut<bySmk=)CxRR`Ico{k?lZQlmKPTXgnSlEf3Uc#%NSanelN}&JMG3_NgXk}UGjUYE9n+uihViMu@eBLXwY%V=C`GfdE$_?ws)%D*`53U;uJ5qQlP>jzc63}uy zb&Y{|?d1xlFCPVnYXt(D;BGv>hPos)I6K#H!X4{doUv%SZP@Qym*C_1jS9=P9n!a$ne+<7Vj@B&o= zV)L;W36IPFX%J0?RIve!rM@tS9%7(9B6w3|p8vF#RVc$IUBs**ifY3)PBkFHYhq_!+z%NA%nb3DSM}U0SF*kN~ABklY94xZ`mJJiQ0ZwrsBU z!Q@p*@7z0qGpaedvD$^cU$byh+oE3?^<1XkR<}mp)jqcQZSK}7Z;WpP;XNPn!13KU zFh*?)!bPB9{n_-TGznlrRW891*cZ0NDIshJ?fm9uJ{GTSr&X?H%xo7gh8-_Pa^F3s zsd*+5V)TbObm>VoHak5=B}I$jZ~#X!QC}YVNE*(&G=fuG&9o+3-g~3wYwxdv{}VQI z(bPi6x}i&z_0b7@=W1*5Cx}})XNiQa#t8@}hRS_$O-ur2-RX7KuO(7oN8MNpNCXm| zt6>ZmyM?8X#O1W}d+#zLRQkJ8XVn`^esH}QcHFs<{|>sXc$cU0+V>QW=@(J^L#}A-(QFHZvJ1p7zMa)0-O8!T7dP#*o_D02(bct%XfOTH}j^| zjG>j`JI`;sZ>}7>S8N-K+1r*y{No9R=?H&?Adi%> z{I6D>EtFH=O*q1(grg*xXEo02>y;!?Q=B`_CJLRVQoX-X`!+^gcso7|c2E)Tac#bH zSy4(+a#7k_X%kn5Z@;R|c$mK@f+i5z;GoT(jua2?>izwno)(`>qd|IyneNJ~a=Wm0n1$HY z7x`}O9r%tthOSXnsmqtd{p{7Q$5{P&{wvn#P!^6b>wfs>pT74FJGtz?cqeUC&10Ec zBX>IuV5#P(J5loLBlkBu(H>@Jgey3R5mHw0@VOCWMNZJzJTKVS_B&T@ur2B=a+f&L zfADvRz$-+6LxAPm7)m&9eEsj|iZDxJG&|j*Bylzu#M0}mPHQ-- z)Xg775>5x3O~ZwOC3ze*`5B*UOVDL13gyex^MjpvS`bEtIf};Y(*CKB<$H%+uP>(F zIp*uOZw|*wa0hG0UCaNA1O43N*{fzw<{?)jlRpmnoTG-Drv{BD$KJocZ`{T;U7VBS zXv|BchiHCs87&Lrg%qFA(ARSZwd{Vps#^^htP-xIA`HyF8fTyW*`q5c13g3?ZKqmF z5yHXvZ~NC%G*pGDY@+oy4+{(CK`Z944W(9teQ532<>K1%UmxmcEBGQ-eGq0wyhk{P zGb9BId_|P~--%v$u};g3Eck-nVWo-f)L!ljp^8?;u={y-CRVUORKbJshzMcGF53o9 zAGOi`*7WSuLJwiWkPLup%Oy%;M*`>{=ytplKs&deL@AHw=9CZhI!h zw#5%F0mV?>MKp!3e=qc;cBt+6)#cLqKg>|?y=R>oGv@8`fc1TB@fX}^Y&yf4#4P5J z5Bj*37w1%r?0nxJ1EZFlXQ{hul}UuFy92hedU|OVM%#-^IzSW@Fyo!tdi+OiW+Fr9 zP;$Q7cT}`@Pl*wfbAA76T8kb{R+<5>cr)F!eQ|8FW@znLFzXLA(9elf_k3+(4f^<7t|gXhp+L^lQ`M^{(pf+5p+>)0@sZ#Zs3 z2hdo*el&-Uj3W$sBRaP!OnwB@QAwyWj`e2=dA8>_p%j{XKlQz32NKjZjj8$DoFh)A zA+zI{PQU*THq?8*^2Wi|Hsp|W6cGz#4})gubC5~AtKeb0DR7Q9vK5a=h_S&Lz$Y`3vW1(sQim&$E(q{=Xc_!Q{d6H z>;AL=XZ&+)j$ZL;$dMtkyb_VdW$ylM!cRw-v z`+$jLlKSfB{NqkE5_s2oe?27dEkgzHMMR=#)B)56^b*L#>0KE_0aK(F9NUv zV4AebPwrS}6QW#+-|RSH14i4xpI4{DWml%#4kWSeuoKotVc-?pSr2{h9m{7N5XqqJ zIB4+L_s1FL&}6$-EEnA(4xt*%Zk zmb-2FVf@PxETjEEK69YQuo=TKxg7eCCMs&t3Cb3$v02GJu^g|T!X7Sk$^(L~5D8dy zEvLzNID+6Q9FY|yu z_?TuS;pH#~Rq=MWZC?dg?_;73iKJWEu5%184;IOXf4z=P@xao+B0`TQoQcR^md=W_ z)8v@&N&vOFZTMT8yljtnK_(wp9ousw+#Vk5^A4vdQ0;Dq2fN<`?o(EA3_(*+WoG7Q zzV_SEsvT83=n$GoYhTz2ja{rLTKqd8^2Om)Hn6NJz0h;k@MZ z+32X|sx4{^@!9aT&U(+Mg_|oBVX&5}^F&U`GMMV@WfD^G2U9;N=hWZaL?#pH6}wyY zSUOT-@OKlHEug|ndiHn)Mee9H1Phr-XdiIE4sSu5JuTa3z7$=V^V{M?2~l<*=%Yvc zAnfL?jvT{fSohfSr)E{Toe}M^BRkFBk}3D5n~I*zdd>fc%MnATXnkHxLc8vW%U-v| z^TzTrBK3R_O2FE8nvx3k=z?KHa|9&+}jnk93{-jR6pK5ne;hrVW890XH+RJuZ zyVGw>#2k0R>B>t&Q8dr7W-jEN>xk--55BcN6aYM`^`S^3H26p;)rBPRkbx0_k3Njm zhfyes8I6*KJeAFFc~11WoqYG1*cDs4HsOGqT02^u+-lT1*`7O>%-!gJ5`64g`e@hP zUd*%439C+uun;GmyN}yBBV4F<`b~aD7{8nU)ZANoLa#iMZ-V3(P}5vorUc&cu{d=StiKJsasyQl&Ds+U+N2{YKuo82F45k@GwtP78 zH!Vi>DEG$lIXLbwIm=KdF@l`R=}pBd%YGK{7ZdsC^ReaMQJ@ZwD5}Ea@-3e|D?2%! z8~GJxu!y)MT*~|G?#P^E0-vTh>W!tbMGNXdnPK_f8g;&vv=w55HxURm*+&C~(tx2f ze3`A2`)Sblo#R^II5!b$+$R_F+z$klD4^SMR4j-N+v314(P8x7_(<9}J}0PlD5EL| z4P!YKcQ32XkAe_)bUTRf99!Vg^i(8a=-Sah$DE8>YHFaBm-L9iw~9Li*Z7q$&4rpR zuL(B4HN|2|h^!QI{t_0q8qR8VUhctJ+rfRSv@e)IFX(^-mf&+vv%bx+F7e*k&F&J? zMrp@G8FV6#1}}_Ggm4iyuoH@wB&4n?Y;vVvCAz3)-|z2S4a{?L?@F@>Yp@Y8-o}Jx4qnY; zRXVq;(@_#kdu*H$b-U&G@`m#vuysN?A#WAid$aNenKS7$N}B6`PuM7s!x>~KB8egV|Opi+Ut8<3m1TX>B-*D9m`K@FfvEl zIW>#P%yQVvtA2ICuL2}~$%MNQkbji`v1|DOoYH#nrXKuv5NC*Tpz~6m;fF7N$s5Uk&P;^r8%%7ZrOjL4jX9u zJfUZ4t@>?h5+EHudZ4oJ_3{K3pln!cp+C%Q0P)6B8D{vZiTOEUiSQ~nZPc}eM>X`^ zb5WrDn|X1T9-?KiTJOET>sjzz0?e+~Esls+m8;H>8fPr|Ma$m@>!=QCXJuKape|{t zbAy!BWt=$T-{M4M(J`*v;(171GdC;GZrx&=*Nx|WuYmFsHcF$Xh5t-2sEk{$OTv$e zZO6}UH?Fq>>SA_lzH(dcyp6$K`jhA5MkZkugBf;M+qb@iIw7kxY8paMw=j&S<2u}hxRhg_4(MkgYJ+Qv1+y)<#a+f@L7vtyzI7_yp1R0&n7sk z^%3pTTfM4mj~9#Qginj*Ux8Ow%J)bB=XtWsr< zr7Bx==@MtDhe6-I6sFkXwWCdY%ToJMZK7%X&h*0c)qh^}=j_QvEV9VE)=CU@BiqbM zjlU<0j>R}*mYVF1a)KG;`kUpTz8uTDLV7a>zI`KQr5SPS&z$E7tMip#zUU$x_9IcM zlxZ{Sl1b2Ude{EHtgDc^DP6jkJ0=x*V)?li$qWcm|Z(!{2*#c{`w~LO^`FtsQM* z>LPmwy1Feto*;oz8Z7!ppUaryPf@FmX(Z4whs7GXc->)r7M`bS-+b=N=>Sh&X%yj9 zRDg^YFk^w`leZ!iL%#qSj30RtVC zQSb}i-ERQ3xJgyvt|;XyL3roojV*}_}0~6jfyi@hFbcVuSj*g7mt%&vTf>r z*KsfKT+JQm`+(E^i(#ErvE!B%pLp>QBFo?X%r9acyjN{=^tPBSF`Hu6$K>C=;_lVG zzc=4rCe{yz=U(i-bmv{$VoI`RzaO_LdYiZUJj<`*!gM#A8EI>f#lA26uHlqH5ZlGw zG;6Niv1Z~am0qRo`(L@~sexf}NJ~}Tk0Xq6VLhFF#CF;xm-0_tc!U%&-Bo5Rm~ayj zD!Via2e35Eup1GgGdbzSaVl-t@V*rNv2M zedK6_j6mBqarMW6$2D}XLU~ljwPj&=%(?Aoj=NTMSU z;|ss`JOf0T3G*EHzrWOG-@2v^q0Jsm9#17ckf(1Mit4nuRcFv)XC*2oK0j#~Q!UW2 z?+aIg#@1nVHyhU-6LO=iEnq!n%b~m&&KG#OqKcrg=tZ(Zg0M2{{&n&QZuiC@9*+d0 z$LQN$qu**$HYDy}*ltFJCi zDM?v3ZOydYX)C6!oVGTl__7_4FOidemZ8V#shOiWxYi7Fa^KJ6 ztf%Ds=xP0^-{Ok`OZP6BViYhbVS+NAKc{$ze-I&Wamd}je6aT4olh9m3cdG@^DV2{ zOD;q6XF*WFO+v$PHZwX;+5}Jb)1ChEFuoewfZ`4j!?7LLTxHE6Vk(?y{f*(WKE}1c zN|q8RHm`iQTezCeB+|7ffKJ=^LYNybSH0P>chi{qe*ST1@FC#T=dx~eV|rG-L76O? z#VjToUH;KTqlPexd}(zKd)-FJuGNN_}IJZ`W zuq3tF0v|Rai%@xlnw;yvUuGe?gzJ>_yc&)R__VrOSB2}k4_o!rAZla--J_XLSVJ^JdsV zl7w3Q{i`pC%Ip8W&2(AK>&d_C>FLjT^LRa-VcoYEeBybEiS|}Te_oc=UZm#Jf>{M| zl%Z(OO*9Ke;jqixENv^tyY&PYZ9TBbXxYbBumEgy~4WZH{xsuI-#=J@(Duszn`)Opg@={p=GUS zghx}H+yDCmNi>O_VvATWR^42F=XPkjzFgef`<>Qaf9awwA^a*{PCBWDrk5|g$EXuO z>X^Q?!Cf7Y>qAd#i!a}Pf>IWJTnsL@<(=E@f1V%M=SnWbr?&cm;-tm@h)&8(%D-bX zKibme;Ai2yH4%hK3qxpmbe5_Uw4Szpf0y5It`blGiW6E~437x+%b+VyWLkgvh9a=y zaS&D`#bo}hffmQK&K2}^S5X|RR+$K&gp91dXsIyMFr&78`0lth^YR{Bo{^amyDeb9 z|9<}ieh0h{xF2vipgV;m61#*0y58@29Pr)m*Vk%EWJbngx%2X_UZ-u)_Bfg@!08#zF=o#7y_$J2XU6R)fpLRY+F!lu-MzozY+ zu+yiFvB;vUx~O6IRkrv|+!^$*=+xC$Y_~fC(GruhD*(RA16y(Ah_1mr2FF&ei^D9p zTC_Gj(l%Fd0){m)Wh7lW_zH7(ti^{LTV+TNIVBg6rY;6Wn09_ zsx_gMP1E?FXzSa{ui~g|J@v*6XrSYoXP9EuQ~>3E=NgQHPr08){X^MKgIlk+d(}m) ze11l?TZh3Y5|p72=awhTvrNzgYyaA1(U_y3Y?&c0j02lvKI#XxVw2^rXIFGLs*6c}XX5)>|E zySMMXXXTDiKiEwkyA$(1i2cwD_Z}oJ9#_KMLgH-keo%NmlaI}OdDWECN7Fy?=I!fC z_u(A$B(CK#8%5^hAb4xxq|+j1$D^`=epw^*%>`LX->TD~wBx?ap&RDcZ`&RipUsTk z2^m+QnQl11)_mQDbwOQJJLChs|GuJ!SFclO^*p}B){c*TMz;)ln44k>PUzssIzfq@ z3A(T*^npGA_q?fMX~sK#1O>y|?zq;+fpg9B=u$`l-16!NZy5@y;{#ts(90(3PN?%6 z;TSgk|D!j713gSaR_LrU8A+(uUb!0eaO%Hv!GoNHOFsu^FeMDuUcw!j`6^&|k1cL} zT_lSM*Vc|nIoxaTt^?kdef;dSF!?$>#np9f;lKTC?U1|f4$?z&B%t17iNa^}uBO@X z*O`M&Mo!K-rjwAG6T*z8!3C=4f^j&|&`>jD$!4#U8s0VfU$S{kdncG375FO1x`k)c z8AS`5JbDXDNEkv-n|L3 ze4m1rRP5O5Yiox|ylUqlhI!BAc_&5u%D^c@-WCGSf!w_9>Ey%CgyU)ASkL)vZmabq z%mSHt3#{9CZCAEXBIEi51Mv7d-9;OCuPh8c;(nTBScriqci*VJ0>V=nKb?T7h7wdR ziKUdIhLM(+0Q8W?Aotx&(`UdmqfijA+Qp+@&7kltq2V^(3b|n&78x4W`R7&N`dRu_ z)_`BSqeTOqT_44=;+15Y9dAe2n|#z5Ursv)%5gwdD3E5V1hy4856JA=a?jfQux>4O zqr4f(Yu_U6if-ydXe+dYxM8G!lc}3abU0ivWF=Oku-=p_shoJ8uLI zqsHOKH2kDx)BF*j6q6uS3^02;Oum7m2+twM6ukG5B_~7zcsH>1|6It!8|few#>s97O3cMOtFxCi>V&1c{21-txC0=!~D>v;Eb=L*}kn(b3)m<*1v zY^L(_dPVh`L)S)s8oNAPUoRh+BM|)Z^tA+uNAuMMoWt%@)^q;*;TA-IwNeI4<4NqA z--+-+fLyRjY~Ajmem_UFV`Ry)@r_TVx1F6z_WFU;i*axt#UVlKQ5>HssI+LxJ(MEU zpM0QW{N7i-N3r2&7h3<{_>5G|iKlBWjK*Q*5JvoKi12l9=V zo;&)uCXwgnbd=54?|axfkn(!;ekv&mAtN9NkLm%ZVaRH6cvqb}4ZBAGjt{6{@Tg%l zB@W=fk081p@B@l~MI&Ffo8uNZ% zurq*Q);7#tSmt$Fv>e{?&4bai!(vNxb#AnPyB|RUFYjM8-}<=oLm0bCy$LmRV12_akUcmqFS9Nu956-6WByVCo1vVp0V)YMu}W)= z52cs@3SYuN-&ucp#A&=u2Kbs1x`HWW2g^-Yi*4*{aZGs6Z$sRy*^@U!ZNIi$tnZyu zIcLWCi+ECfG&^3J_`vcdDU|`X$2T2X2AcmBh1=2h&M8e%!l)|#JNfYy@KWPl1^3~o zN+?>u530gpMLut(vD{rqI7=}!H50FHRB(&krKG3fH3O|FtT6l8*KN!TlY>>(u%PM z9lGqx2Y>E6ltnSIVoTdo?Mk;}ud9wb zhw$)10wUGndD{LKgCFR2Y-8J42(>=N%`0W-=LNH9E7Xh?iWS^OiK-t?cWJFp$5$ad z&fch&k0#Q~KWPpsvFY!eaFV}X0V}#@HBIxIp2>zW+yq6rLoGH2Uso}>Qj!yT%$X(v zCc_LJ6+F9?}bhlQvw#1i#G8lyU%W&E(I$v>Oqsm}8J_4*+`(MxJyhKi|Wf8FSEkWHb zOA9~63iO&>ObVE84MuE5(4~s}MwmQED#Si5JPDNh!;cEKBMr&mydFi6n_PJ$6+RJ6 z3-vvOnpo`r>lsD#CVU-a$1#I*Gv|3Ty*>f2Bg9_wf0O+82RZ@Sully_;P z&hR~j&Z<~n=RyPtsqdkO&3eSEH}D?)@b0ZqY@9ppqQ0bnR$j9h2H4WGq!_Ib&9i`y z>Sx!P*+F~9*Q=L590-%1(6fV*m_h)}4s4)LSZu3ocKS2`dIy(X)+pz6ywQ; zk|9)dy(Anrh$?tgMt7`^Gc!enu{piLRldi2GT}Tte}IjrZ;0jhu)ZSni-^AMmB?x7 z(=w+mp0+$CFQwqY;s;ASwu(pvgJ?dX$JzAM=&cWIX+;=$RI?tk1B#FQ<{ZD!0VX&z zzcmTQ5_V|4$;QTb+Yo3 zTbI%2<)8I>MfLkP0c=@f+y4GJNu>?O^rh`ORt+!=LhaEQWRdrm=Mj$KOrsIP!+}r> z2_X)%jTIc%v^#z8`dnPh$}tVJNJhCEVfMmvRD^S&JDC4HMZFzogrZ;%u3>a!?U=@0p`Mct3r%3GwlGu+Zuegr=-9KDoP93J=aARapcc^zmwSFDp}=7X z;MAR`B3P>43eSx-UJ?THBnY^L2!B~CALukDFj$9^pp|F5)oDEB6Y}pI5}u56uT-9i z7iXx$-t*XzhN3K|6xwIUcdj=V(=!WirehE0{3t~kOyL>u`1g8@~&yDUWANT(9XFx`D1~-)6uIUO)GdJBPbHp!If^q9*;(JiO@gZ63`lnVgLsRk^#PZ19TBC$%u5?SYk?NcXPK0$_n9ILJ$=4+eUR^XWBA zGWOx6R)W5slUF$m)nGT4PJm~jzW1KXkVF6B)yT#P@*t9~MnWoc>9@cZUk8+)+H2cQ z8+7z&YWD++e|N^wOZ3})VXUp|jKpjxipDXUw1EC;&;<98^87D8aIS30RuEShIs zop>&oBNm3ZQj;{pOq18_cEO2;m4jG0(CP74BG3UquqHS`iT&As@%2#^BtHXBr}6Y5 zarbfDz*05QcXVm&x5Ff)DkxHds!pLT3Ma_FA5aSQ(f7X~jCW3dXCmx>5iU|Rwp#kx zHM+PBI)wX*XwQakoISP-exRpkNkGD<@8?(Ua`x1f;n>VIQ*p`iXGok3r~R*5oi4@L z$J%#Ul`!siN?b|5gl-K_&iYl)C)3>b{~rmoU>u8yl?O%6lX4>he{zo2*9YN>`L&cs z_=De$8YdUDh zIOs)-a58UJA6vq#IqJo(JpovYGYS<}2jjL0^gjB3zaGz5H!!@jMhAIjkT)k$$tUpg zVBxQMpzB@=RbzeU={QK<%NxtPK}rZom?z`YzINv^4OX>#L?NOSQiav}*sdM_hVtBn z6x%-RDqO?YL6^~~dU0}-6VA&j32#o2M}Ffocms!gPyc0+YH)6%Su}}Vj?IqyMs7$e zN-KJ(=%Ir2HR;RKGtv|OJn_r$-DkIgi1eiNg%1_@Y^Fu6-wMUO2BLxhg9wi=pimJI~R8KwdK{7+pr^jZZ^%U zX%m;Gix8^WJg)AZWfeh?b9F-d%sic<(BhhGo+>z~9@+L1cj)rteK&|hm=a}ee_tX2 zp>5wyo!Z8bXEgRI0VXF0qLJL1RYh(j=eF7AP9C$ZL z9^*9c3L^p}RHHn3ow|`|*e5@kP+^B=VZZBxYhmIdN0a-$%&c$y>)lSBWydoK^-lF( zZ%oKsHkuLJa3v34w8ay1hbb1`Sw+BJF z8Hj`wR*QvZec}56cr8)k2P2sd>#yZ6>~DL1=09f2lT#mtKn`8KkM$Ahx_0}q9JB|Q z%llgt&n{yb`0VXCbS-6mBM3&68O5Tg(UlC}rTLca(PhzPk!9k3IFfwLMiz!lJtv&S z)^<%<@F!7o<|V8jzfJ78Fav#b`(T;qET3Bw4fs7lMAqu_%7$=7}mKmk_bJ*y|QS)qgl|U zUYuyr$L61v#E!Ht*r8vo82Az=+~udl_@xp8^V)gxBx z?mw?!(P6ArpfI!N;rhDoPvqmQH`JfhLMlHJ2Wd8}5hPPnjVuQxa5iHJsuSXkg*@Px z>~pEKuf46`pSubg=qWNXi5u3ip_Mzr<>8?#Hl7qQgqG#$*QLSz60MOX64>{@N&onq zvHJ%zO#EvIhe{ejFD}GUhf6EJ>-X>Y!sOc*-@bL?4(B@2{>Y25I8@z0hwNx{5xHSm zF@;!-Hmj132=7_p0sfp1fl~(F`NUTa1ixuDbYBg;x`I>KVN)bKj46TWOSVwhS$*_7 zz~|@4&UzQj8Cnb5=aE zMpv$;aQEy$p8y{f#D_;~@j`-%@~Ea?4Y2+kL^x&nPIaKgpGhyGi$_`8Ag7gZX`Wd+@GVl3ZIEwm0n(D&G(m1}x^ z=E1Wzy{=l`Z(o87$C~$VGZ`0R_3QFcIf&v5MFQbW5ihnoA$P7GJGEXYQmq^xmQ6e`jMT#iEtmhcswF(3(}}sKHOWR z)^h7ls$DJ11jn5xG_14x_XNdbCJ_$>nuM6;e0I!DV+U37zi~p*-hZbAS2&qr+#QUa zzTcfN2T#-WL9lg;T_M)jgCE=wx3J^jDHfYYXf#_K7>8w>m z6?D@=)zIepT+)oC9@Qh-_=fxk(d<4T7B)J*9xGjB6|E8glXXn@tLxH)q&(La<$!nI z50h9DZaYqEA~ve?g>=o$F6!+d=Lh1==U35=Ehluact-! zl>=tZfe+sL-8G8kN7Pc5=33ugUS{&jJeDV+<*pJnmu=l*kJEe6ZzN7$8MW=wDh$Aq zxQsCT7M?^JFXaQDKobFHjAQ!A%j84-Pb=8SI9yV*@15tP)B~~Jm}0*jk`Qqo%q#Ys z&=baa?bhBT3tiu~gIy;Wx|PcgQ-2znAa@nc1P?O~o}1AnVF`vTHtzGn+@OcxhlE)! zUemYTU{(Ef{dU_^Q|&pzI^1C@pTL(r{?b|Aw|6C`20rlajHNsS9akS=^3#HQF0rFFa%%oy?64Z> zR@`^69r3ax@yO-_9uhF~4*az;g>w;-EivoRBmj*MV?~H1>MjW_u2@lE;u@^t7gkWc zWrI;vwg|C;n_gAhWpit9iI7zY)gRWeqzwl)xl46eBj8pM$0{!?I8rz-WzyF#}%G+I(SsW9=7;`4Cemrmy;Lo zKq8&`_ILdtB7RBO9S(T{?@h9uQ~qSFW7Sm=s63_VGGJ|B2|H6b@Y_+Al+(5E^nxiMB2BW$CPsBIT#kC5OuM=v>hyF?em15vm1eB+=ef5~N&REZ; z3G5d5QJoRu7TI0&Tx`D`)&qS-I8Tc!WgwW;0=j=b6gC^jO+IIzN+gy{KL z&FFnEzI7UfqLG#+K=ats^entNKZnKuO7w@AU_RoY<=5 zLjk;V>p|gGj`(iI5{aUC1v&2!a7Ec-9)w&lPbzG3gDbh&phro71f1z&mJrz>izec@ z7-$ug@+kkE*`6@)Ub}t7HhTy9n8u&o?6X5L&{GMA9dl*ss*VRuA`KdCeK(vCL%0HR zVso_zEX_?{ut{h!lohZ0z>DM+=sKve){P#)eTYh2q2J$0JyDfT)vA&C4E(sX%bF-_+QCpO;kXCU8X|&M3NpoF4j>fQFk1zhhwZGyGB7TNe zq>%0$L1G+mRT&fjI=lSvwWi}9!HZ*4{#xt=Fh@3)?re8y!wne&b_`&U`|Ww z5#=3a&!w`><(Way-f&qSd3lWGlaD4^gC)ea@4R@Zi3VQ(XwX;7X)_vKFaet}ToqFD zXAp2$U&mBx)0#WD*pn}-ZqrE2mRQDEJ| zW}q9VX72XQiFh*m-nEys4C~?v+~_(l-}ij#0X>#?Crk*eJuS+;wddpG+V;IpgYBcr z-^O0kyJ19F=cT4F$gjpEs<-?t8Qkm2-F$W&)OlF>rP`KV9x&yy<@JjRTmd=b)(Hd! z?&3npPz%NF=+CB-Fi&6Gcy?u7q0HOL+^aCL;&XmW{i+m< zM2O!FgsfDyFba3(Siv^42G>X5-}l+@5Z}0|9}YZR4eq^Xd#D;a`hU)r^?l`jUOVEf z{ZjL16A7ah{;dD&>AtU`**BgdM|9AwVSe`7S)O}opZV5w=`jfY ze7R7(>HX;b&*Ef&m(gthe^TmDK3zwmqQIKm{4l*;sy+m%K#6uL$GrqTinHM#_(TW-ioz z4fh&(ekwuM+yp&FC>VEIyi>7SdpMV+l4cT3noT~d(*HSkcE+_O0ae%3_H6;xajV{p zHzO2MY*|H#$z>zI+3&KtSs%{S`u>0J zD&K=oBd;AlXswG+A6Q3_Oorv%8ZVJ6o4zC#yOt2OwS;EhZEmk4;%%D-G0@vT_a#V4 zA`uQKbWvRQ9_sY~9>|x!`_ybYr4B)!X}k5qWelgIHbV72F>Ck-I)-Fs)r`?=#lP@S zy5yc!CK2xgjtbZ(z(hdg8V)u-%eJFKYpeRR;XEhN{FQ3%J+bFE zH(opXf3H$RYV=K2T3$P@+h$$9)x&xI{kRZvA%LhTJ6;8seNhf=hl}2BqQe`u7#XDt z2fP)Pa^}hN9-X0;$H2|?UTUka`?j$%+MZDd=@I8MB0t!2ehT4W`5+tshJjZ-+Gq-^ z&8jH`h@oP06G6p?Av_ujBTXoEPy^D0cU!5t-=7VM`O+EIY4g%n8?x~cl_$xBk+~ zDfX&2YIDiOO~lEWKv^(4M!H$C5iX<5dHAAG8+W zAeuf;J*m&_D|l8$QOtG9%4im;AN}=W`4Bd~9b`KMyF}j_6$3p9iWAUzyUy~(2{kU? z`F$7o+JrfaA6e~J%DYgwZGDFhil0gV@=4-Q(u7$ z@dLdpZSUPQ7juIR!u|1i*2njwm#9uk%Dgs~ZAbrhT68rst^C@GyT8&dz*G!sOKqrj6F` zJnRGUKP|6dGZ!V;K4;5=)aZ~|208{z*zWf4=#r#=CEuQX`^JeouB#C1?UFY%N3A<% z-~-k2u%y8({WLT z?_9+jH(wiOdw0JU%aXOU-s@T`zT#)Hak5bBpSN-a({6U@9er!`P!unw*#^U=&sKBe%ulrR*r925X?8#4@qyP<@lR!C zFMTrSvO?FIF|95gz0c@Dr6jGW2X=hs0;!D>Y8nj+IpIZ&80nHk4JqYCT2x9LJHK(3 zn8iJ3iL;!>pY@-fUtrtEX)!{f5fquPv?IDq-TCw0*J{A+7&wM0Hqfvew^$d(5azl1 z(cpX@jdAyKcrm2Bj=6C>mV|93;2yBz76KG237DYpY0dNk{*#=ViHq(G{M^8ZJkHC) zpT+PeEpBoM@3S_tc}DT}EoyF@gr)DqBxEKO-nSusqj$O3A{vKgK*O8!{xi>MPrXvoye~BmLMx^2`^V(o`0nL;fHyD4O7Kup*Wr%&OxXF!tlMj&%&h{RAPyvUDS!;X5%aYZeIqeSi-Lu2fK|yLUyyZt1){JR8d^(&4 zM5r%qp+Qjzz^k(-U_>X|62FQrBxwfW?8LT%&r9+W*a zPH4xr{+9->xpy=5E?K&Ix1QctW}YzT$b*a1f73&bI8N&zQP6V)WU!^_xDIUVCRit0 z@5_Z{{jB>iiW3K5&E{7lY^(l`iyD($X0YDbc8~8zs}FO&mV#UUm6tNo;=lWM17rs* zfYkT@mH2^yP8j45^4IxX53sa3IFl~{qwls$GA5?#^)#MCy0xrF1INde?#Dk6$jZ`K zI=-0ZMA=gEe=1w|iLGDD$S3|2Jyg+gXgG`AVtv@w*v(TnO)ZUE8@FI_+?FXHP5CJ1 zqv(&KKAQMZ_}1X9!5@Y9zKZI7C+4Y{Q;TD^;Prx+R%NFibY{VHcpim>vz`2Q{#E0* z*Ls$bpHaBome7GTrgOu77h6v(e}eo`ciCRrKHm>?=u&$Xrmn?6@+ zJXH1u+q*tL>n_S>9|z;tLDo;V_`D$;)TUwGPMxEhoj3h$`NEvPXZ}61+UpmuCXXiP zlOilNVVedq3r#K`2P|BCZJDkUn&rnHygTYls@DV{U01~M4(f{*|@YgI^ z79!6=UWoN!6mC5qFAp!J^|4k_P;&1QnjPP3N2wN}9#?x%BY}R-&fwvSN5?(uyG8rY zRaikkXWjcw`1Mc%U8ep2?yd(esw(?G9^-@~O@t#N5fUL15lciakr}c?WUlL)|JGdB zH8L}FT{G7umn7HBH8V3JGcz+Y@@M9{W=86ol54IR5=$%+84)6jM2Lvv-1lREVcws6 z&b@Euq1ESofMI6td+*-&&b#NH^F811S{^_7l#PG^SpsqZ(v)#P*&fohK8rfWaY9Xn zu>{ejRu9seXmC%}M}t}1Y;@}0&fiA@EQ%-Pkwlpih&80dTjTCsnSAK>Z8C?DU8{5Mm+(9y0a^%y9CuUmUJV$fPkvUm=!X)H* zPMDb#2_c4%Zx>9rD}qMj)({SKKM`t8*LFi;uBfUPX;_GORUiKwOz+1v)%vg3?Ag%u zc{yEu58Ru35$p$H6bW}eLVvc)nMa3NI zX|oAcqXJqXp0Qj7%&oT-5{?uHJfxBktw1nmE_I*L?>una1M41I_+DI_`2tWK%o1hd zYoX39w26No_VcYiU<$_9es$R&m%f^KG7x_FVU8Ux1L=PKgfc$Ut*c7}2|eD@Np5qR zy?|yO=foS7oV{&l0_bNI>VY0x^tQ7iE$e*TKR{JP6Ixzv!-Nr7#Fib?RzTXa4~OB} zr|LcbGm5V#{`DmR?By9YuPf-D>8nWRHhBfl>HIxR;+}mw|4snn?ziP&XkK<8iU=bV z{MiqzbAPj!ekV|h*z;~egTNM9lmG}K8@vd$a0nm9%v^3pLs8a>^n$m>6a{;Wd!xVq zNvLueG-@4RFt5qt7E2VH_E4Dxib6!i)euY00PyM19UgdK%?$yx9%gESz&R83BM^Bi z4p@l$=XrtHj%P;^fnS^(UTB~WUaJ_-k?*q{YcoMw;gBrRM1amde+m&WN@p0w?RvFp zw!##4w2GadzcJK9$GxTB$uap$GOL3B7tm~i5K6GxtEVkGf3}4ut~D{@&VCJHMSq?9 zc0{9vuQr}Xmz4;oEVn3Dk>=(jG3+`*=kKRvH}RcY>>|x1id0PQzuxQsuJ|u=J-KQt z)3tu&mPj@1)qj7B$fmpEH++IHAt@)(L~g|2-5cpVe}qeIfNPE3f&AfENj$iMKSJIu zwg@ZtlsI)5uH_%eIH{)|(i6TYlIxutxWmE&Dry69fDA5P38dGQ+slbO zr?Pse{wLvn0Pp_#cnnraIMz_lD`}&pbABD+A-`)&jZCeH_1Bc#R2)~BvGBziFHL?a zwv~$qhEU5!2?OmU2`%PDqj?t|eNM*hcp>xsFj#3*V(6zhx2My5m7tQ=&2h9@N{a`d zD2t{C?fMQl{+eMbLth0ss7pK&Q@*BL4JlcirVaq#w&uJD_k%?sL92C_Pr&?t2r8%N+^4+4PvWc6ugl@m9_ z`RSG@@1I6zPbI>Vxt=Y!-c6AA ztty~@nVR+BL?{b^z{7_?7(N-z>)ex~=Wd+35-V51mk63*9NpOn;|PQ#?}9l%G4xdI z5vrl5^MLs2kburtZDys$`m-IJC6ib7teLBv{Q4UX4Bz(nq_<1O?D3qE}B+JZ61f-%WO<*Tx&5KT(!wRCnn=XRYe=Mlc|dovV5{jF-AE*J`hu9i&q z>>B)jj*Ki@n56LwX3S+d*{qsLes^Ad)o1H^c=fJ?EeazX2g_8|TdOIABZoP6sy4>Q zBa1VQ7G$w+OvRWqAf-(Ct2UETZZ~eTlc}V4}!CL4^&X>%8_w__U}3$i?6I!~NjHok1-iAt%@Ci^51|FeDJ!NOj$5 z3;Mq&`!GXsrq&N5U_Kc7ny&Nn7bj8;eHgt39Zg~Fd8EG zXNy9pug~hSq8E!0v1RbCv5VhK{Wy$1pFNX)4F!2WYZ_0O>BG>gWfMdA?s^kE+fB|_ z8#=W3dn(S;p8hwy3!Ww3t`5W5Umfd79iaYlE1GY5%3stOFUrki6MnJenGg`=UpZ>G zud0N2tB8Ormwe<;pV1}MjNx^r`_*&>irC!rQOr%-FO!Ko%&p=3=PW!EAb}!-gk#*4 zuWm7*H|F~2(x2r&KjXv53#JRE&d2E~zu5~-Efk5MnlFN2zHry8R?`p#U;RH4u@;k6 zg(U})A?nJ?Ku`Plji>x&bP-m>fd?6sM% zq?{F5{lM9HXrsD#yrJ;~yKnjAb<^{v=ZV1b=JtFRHmoCXJ6S6Lb{B!~Gy&ogYCKR)k6D>HdF_?i%}km6ZW(Mj0G+DL?{1-&g{V zuutLD6g{XbmhLT>J$jS=RwKx=_08@G}%w`u`#d_Z|-@^MhPTu(2jZ-#ESvzI< z?`E$~{W$sKr1mZ0Q1}@UE^5JwcnKvIPBZ+n=u7LD>>R>&RExs=YUIJlJy8}w>14+;w?_fofZ9jv3}q!Br0AZbhG}R{B8-x zYRjs;0O_8cAI#@)5QpqJXVQZfQhr(zqrDY3Owktzp&jp7oN+nx)b$_5Ve>O2VL*4E zaFSkV-cWz@*$gVnT=Dxu<$L}x@*f$?Cf>VY#7@65(F?kb%oPxj*FF9V6zQU?%4-5? z1R?T%-oHtioj4dT0cDmA0bbB~w)tpc4!_NX`$0erIjA2jVrg0tc+g4xJqas3XTt6; z8m%S8b*CBBA2=YRV^-s2gg8q_X`o31nn|C-5>_Mt@>gc)5n`h;W}Z@T!9=L;8V@?j z*cR>!IGj?2cWwFS;S!Y=kO(KOUxq!EJb%fct^SQBy?J=Yz9g)u$oXGEqOTl{@7OSk z)qk1RNCCW;AyJD{`P4&*aHoczKY5Fu`I`v`ohJ$=>L7lW)uW`5_Z0cixp1Uj))n`|kG+hXpBuc(-miXCi2Im3BrK z>kmdiEgCGoWyTr$^OK=|5ELoSH@YxZ&Hd>XHTb)p4+OP*EDIb?$7-lV7S$ao7tJlq zgEzbvuBQxA``HW)#P+rW`p6vU?PT$bY=n9f9J*EZ3_8`+JA;&Qc$kXREA!2Fa!b& zHJHR*Mwxl|kwyzX?w9};tsnHd0RqnOM0i^T0D5ZFd#OPjz_e+L2?&v)Q-3P&JOF#P zKApYJ4KKT>(*V`)zBNjHl%&I+(;B;5+ahnY?^tR<`&@tF!g-cfv5cR0OJLXJNhO;X}+l@b-9f!EC1jp)0Ky*iyM^}F# zhXY}px*V^FrL(rmKR*y~g^ESx>f#DNs9Kv&*+3(2xP7NFx5l)&1jXCqr20@W4Sxr# zDZXGz(O?^fY^(EQKW1^y=<|?k0nEg0JK+cFIbnRJ((Syiz%KGUoSVgKcB)+WaCl<) zjX{P!bbGxn0x)kyvFISLR0ZWq@ZnP(S8w8KChJ4(CxL{^S*I^KfbI?9KwmAN%Ii|Y zDI}X}E)g4!MtH9uHq@XJmfk1d3}yKy1gtvOTcga*DExl9+5@`c*f)U^QvXJn0G+Oo zm*4M~(tNlf`|V)0)|OGq)#kMo6`^?HOt->`#kWFv)elC(gkeS*dR`KXlnlRbvBD7o zf>713hVN4(B6oEWgGTzWmdcKI(&>R*n5jtNy3=Nr?@c#wgtLwXLg;PQde;5wCiP5W zchNoL)k1jCdhj4lHL($+q($$Ba8L3bNnCEx{r*K^ z(Nyh2(y#U2l?QF#i{G1HO&D0Dc@R{*A3{K6H!a7!bdJW*g2qK{D6UPZahFXgB;-O+8@fT|JN4IcL%rp?GW+XYwKfTQf_h*r`c%_OJbSv|hOZ z{7l_jo$&JlI*~)etjYC=exPLhj_|m8n4H>7t8UGc`9=iVJUs>^l5aB|$eP@c?qsm>c^E7`c zZx_(mV}{kDI=&4fEkjwNrfeY`kIBjV7ycqA?OLdV8S2<;YquG$mo%k2+c zQMD_PP>~pr6rl5Wm^!T3<}`GuACvOEy;^^okHx0x041J&@WY;6zg33bvLn%bH+*Mh z6(3oP*k>UD{h$O8p5Xw?Bp!nK-1wv8&fmY@rV2S6ZZ%<&a(FDi+t6!!*3ezE+}-Q) z(#iv1?ocA|PzJ+~u2l)zl8%RSwRFo8${wZ#Y!WJ@)zxCb9KQYs9M~Nq0gf-BrVgEd zi>>||bl-s_xF3`9yuD64qu{fmySqI7pqC`!0$lk@KKq6pq3+`}n*$k1?JJwG%0^l} zq>N!k>nzTg#~!*#%lI_63F1t2et(QA9w0WhntA;M*kK7MzjVY5{^fV9ek%nE!|)W= zLZ4KHSv0IKQH9@_B(&|_XPmkp-Ws~tP!;Sg z101`|ihyZ3`54bqAUyh4H;8hNVe&>6L*x5lY&BK-1C!E~NDfrox~UMh8wO|4hxIJB6Z1010y zQXzV&E<;ynu3cNLBEe>shC2mLBmow`h6CS}6S}^F9e;jx6h4DPD<19hXpRQO*6N2d zptI*nBHWaJX$`%bAfWWut6xbeH?yIrP>#a&-FsqGUROLUjldHf(v!$TK|Q%&+jninj+jP%a-z{;VkR;Hdc zUliDKw)8RkGB}E&k)Ade68yn3721>zZnmBi*u?cIv6`dIOH z`Kc79syp}GL^j(iVL%uRGxWCbqlrh%rDC&wdMdI0p0zKge-R=9WKjexjwp(_jxwL# zvh}Q=gt+LmTNL9VktoHP?oVqieGgNa>Dux}Ycsh}I7@xJ-%X{DV`R};Gt`es%e^&p z2?4Qs)(>8qXmwv^Wor%H-QwdAHa`GfUjnQBXq?ISGI;RcFy0rwndux|g~QYUjpt_Y zmw-SJ#y=Zk=-2GrJ)}+~!l#x`Vh$%ho$>oT^I^RPZ>>q%{n*s~VGj(B_4*J9-Bs`*ILe>b5{55oss@c;9@WiI*&=qj4*V5EE{j-hMv*v4#p`! zF$1fVd-`6CVgy02EdL^awHT%^6g4jQxJ>rY1#RL}nvw)eL!xsl)aa_NoXuuQg|jK$x6H)MSp_i&fRnUql>{$ zwM&LsiXyIom1>)~nUP=9{`qboZ2W?@kRWM_JB=!<9K zs<1@(Cg7E{$jw*i$JIN0>%+^#H~e+MvblfD`dj9*+1c|W3a+k(yo772%0dbEEk(E= zh=jqZ`(dbhSv6rL#jFi{%!)38|jr~xPAmf=BZ5aYpck?xjAnHHop=?mxMM%QL1#_y$hhg2gcs1 zd?!22dqaOYns7X9B;k|L3G>2=dr*Jf@%TiX%Z4S(1Jf(uEiQ<|R59`i>o9oiOKPrHg zP>W~?HJc0Zl|!*&*M66&gf?wOXUs6usB&y}giK#}b-C^bVC%5WyW{2k@SaMlDx62g z2?Pw705i z;IXl=N8ozrTkAV%w`>#JJRkd{9Xp1d^7lkdoVI0;U^(J=Zb;&s!*SeshoLLi=ttFw zyqB3v&Ii^>)TFA!P>fNXGrH2G-7r^oCMv!C*0bmo(?Sqgz*7j;K%FxS# zVHgGv&W+uIAT3^)s6L~cj#LL}TbvV0ZGX+Y_Rtl^QhwNd4DgU=)Q zaqN2E07Gx957aNNE5irG++JXeF8goidn%A&M2j!|%^DXxy69WQ*i`zzo{_MzM_7Ff z*{p>|jFsfk=qf*%HA-RVrE0U}F!ZWc6I)e4JI|}vwj4kcvFe14R~_1E=v{%LA} zp*PkCzmv$15M*w< zuI$#$FHT<*?dGt3d~DAedY|S-5nEqFWDymi`DxHt!Pi5gYOi&^`aDJnm{Et*I1Ih5 z)htmdF<|alNy{0>gAo@GoG{~Mqw;4=r=hQyO;eWc{28p0M2sqCT?U-S#~|0ezCM73 z7srjOi!4jXAL+gD?|`b(XyUbl=?TVbXQ*$RQS0rb-M^W?H}Q;ySmbz^kiLF1<@*! zh^V8UuZj$u(VJX`jw`4V6dKg|`n6;~ubor>n%u;T0;aw#Av|%oqBcZwB?2|pf6BqDR&Bg>(Bn|4E2sia38B7i9;K$l(3nMmr_pAKn9I?45aZfma^=-|{IprrYW@~+)K`rd!^P_{j2qpGvzHwvDh;M>; z&dOnw&^5RvulHoEe$!zMx2!)W8a2A5(ekim*h%#_va;CsC7pqZb$pf4aIMjg(F`5J zWOW+4kK_GRdMJblgH;tfgLlKCjVuhF>%Z*LMgV3AC!!1!i*|iFK~sbE>)Z>97Om#R z(+4uEmtqc-hDcN{Q@V5j?Qw)7zliX)-&-T%_j_#OwYO|tKIuOqVKA(@&+WKXEbUoC zzpV&U1Aq#sezUN1h5tneUq9GY>!ne0ykJFg=*60hgf{pYWFi zoUm9{hivJL%sxLJ3aP2nYlAoOFs?yyfi6SO^8V&#-VZ^bcb@=rFa(D%0L%3$ro zdbmZzm|HKtf7vbe#ICFDgZknOsQre^W=1HyQ?N4PXx-cuJjr2BgbOwY!}l1!Mv zIOXxxdGy5`hF)^Q%CBwO(xv|brt_OYyx{GRnZWr|{YyMD zt=4NxGw1CLX*1FF_AuGuc0-R*m&4w-{6Yu*>w@^Z1;)hPkH$(2b|URqJ=aCn7+l&S z^7MmHi=gD+SBflJYyM+aBJXZK%3QuxYtw5y_)T@y*K7_$FFj%9(In|gubF*2idIH# zhj`D`Sode=uw$9d?Jv{YpNZx50W>Ho4<3M7kDiVRBeIeA%5R((hsvwl9P85|`fBG^ zZ&^BdU>Tkj_{Hi25k}#;8pQXKN9Eg2dNNMemj7nu&ZKjq=V`vEx<2-h?D^nHt4O?+!~v`qCcNgRe= zc_NS{G3$P(O^n;xksdnytsi|B99*Vln86EOoqx{@w>Trhj;WcnZW+NDw|G7ajzSC0 z!H(2;nJQ@S7OhmHdaJk2n0*DBmb;?i?k)Kvn*AIo-2twieO^2sw0KTPv5)?2xubW} z9UGoWtq+ixl0`7)!TEcGYFRUOzP!cXrW;}CnJb_u3yc@v-z^`0-+Z2aKwwn)H&W!u zq zFj71@e)-|}6Mlqa4K9CR9kXpu{{500cX=c+-59%j?WC{$F;pNNGf=M~XUl918WVW1wc@* zQcFC+aoOc%s)Tu|I}P1O`Z|<;BSfhT{kcpY_vmn#3&B=PDL#obKvO$p4*`V`XgqfG zN+)-V0vYSgl$BUDv2xoOJD5#r(Pk^Tb;lFa-xx;An=2PK2LjfQ%$#}ao-3+63v^v` zc>0`wM>Y#u$zkm{;Bp48_a0WL^+X&|o`17xkO49I&L9?@Y69xV{v_8!P;4#_XZHj0 z=;YUSxI$F)?<3`7G=-z2?7@XJ;1*l#Q*~>7Yq|0m2hM0Hq~b|)Yo~R|-sYsz-6l1| z%yUUtz{~Z{-@2g%Ta&eqnSd(qZ;gJn&1MNjys;EAl>OM7DkI~{g%WkNn8yY2jkJDc zobGgsrDDLwxho!BJ}-OT$a%x&jhQ!X-t2h`=H))ReCS$BW$zYKo2N1Lpz@3*2cz3e zQ0$s;N_QxG>JZ`;=)0x!)GdFu&j{yzZy?V)Ti%b4;@!T)yos!S@JT4ADp=vN$H)r0 z#TX8bqF0mfAT_jj(mVl1aVW3NT?VZPXwK*ZU4(eq*)*r;Q%fI}QU2GDFbE)>ua|W{ z{c>`&_<2M0(d}E)VLllgheUfo?M;#Kv`i!@XQ#Hu+Z3CTO? zDFT&$eo<7i^8DWr^)Y^xV?+<0n~E{O(YBAm-5C7QYxr2;VR&tqp(|LH@0CRkj#)W4 zc^pR*SknR**1dft$x*0kD?X2|Rg{N(pbNh{cPVXPK8*5dR73m-2U-5p-D#JR`RZ=H z6KqUv^yoZ1y~g@X`L29M-xST$LQ+vX?1)X=)awSNF2guZGnR2)3mBeySoLG-A8^ySXztz)1Cdv3EZso~-mFg$#rqRa0 zEC!T+8z7<2PXY#?wBMB*`iTfW$4mWCIW7KW@--(fkD=pm6COo8`aGDYNUjAJo-?W2 zzBJW{9w-S-`1%psjq&ic?+d{DA%TcF`97CSmY-8np$q!dU=HS3oFx?zCccp#h}CrY z^MI>r*ar;XRVVV2H-3`XW$3lE;VDc}8fDD(3nB|;=G?-&A)2;&P55fEQMdQR`EaBF zHb?sBd)$WZa2h&9iw*03bQMN~WaMA^d7VC^6A&5-Q7viT%~KR|OkJa$DPJ0GL=XJx z4u!&fp!XwI2IhKYBHj;JuT6ek3&tz_mxjvV&TlNs;JK!mxp3-~AV((x#W)jp@&so7 zGS4C6%-F8~k0f-O@|8{BQPf5X)3hv?Y!+A|gu9^yty>b5-@C&&Emg0Ue{=IMT1GzI-z}(YY$eTP#*2*IvocwpbHqOdkqHvI)qQ@7*K=u>3BU z-GU%juycD`MY6G>!#Dn)DWZ_)ZhUOZ2EVd#d#dNu4f1!!uXnFQPlQ7cFdp65j&TdS zWw(Kb?C?D{Zp&>u#YTSD-5cTydD~d|#L{{=PP^Y4lQB!NU+Y#3h?7B56ApPAbd($W z+bTKgUoUmO``k#L9$K1fEg0k)Myg5z<%VyOpTj?f8SSxW|a3~fK+Yxr%4E( z44hwef1}POwz@&9l{Q`(0fNG6KDG8K^R90bas7oW+OsBEZ_l;9-j*;=(Gb4%p9K??QHTh1F=a}6t?Q;VUB|!v zYss1EV60hg?K1S+HSXU9A$6OQ0@$hl%(KZ2-M6IF>N(B{_Z?(_-qN)L%JN3MnQ=USyV^?yJJX=$pC z)t`NScCCo9VY_DZtBNZu%I|$^5fv4<{mQDWeBlkbp^L4bN4Yxm_6VPzh&nr-9Z3Kc zPJ%#W-S01w)m3Ngh6{0F^)C8|91Za*x4h#bP+q!ZI!_**PX-G6f4pq*aYgGXk-kq; zxlz~g$timJ@uI@ef4hNC5Q88IXCn0T%+TA`rQijr1P$l{9c>eW_UIYDpRN5i0PeWU z(8bozqLrujZIGyNWRec?a;6MaIG6v5>wLG>6tlla3|$H=I?Lg_!;CxO8{GT4XBRs~ zLr?(*_h;o^8Tv!Zp<{e!zID6S&(`y|{_r=8Jxq>KoY*vhq?w#o6`bC*MpJyW4!k8Jz0bpC$iXDZ1) zn*4c-c3^}_P>m<&y*Zr-Y>pfloe8G=FNnWbq3={IrJEDzhjbhId{u&NSB!EWGv*Sm z%(U9jZ|}fUeH(CPnK8G+dqXARjQR0}@}k{e6gW7}i$0qFH&p%rS}+5K$`A&ZnK}Wq z(5r%v&*C7VYviZ^yUUJtEO4s6a` zRRQc|UOv{C$Mlq=|sl zkz)c4w|m39ZvQM6ixgil-LXxbUW1^%5&{Z^kMX2@eC-tx&@t}HGjbWWXf$s>@>LO^ z?Gdb~I#guh{^_q@tfKj~1TCQpLG;-rsHlZ@nv(ERx^6&5>!_vb0{DZV-dm7PZgn_CAP-6a_n|ut)wvJun8i9-A4YG&Ff(39XBBcA_eAOt%$Ri$& z+rxN;DA>C2eZ9yrh%NG%32l};6ltqd2jegfRKgMDQoyBFy3Z!GxXvFs#v_IrdY`gS zgZTRKQi8JO-=^{dej!qVroEu2d|vp)0{zC*ujs?LHrZbG3~}#6 zY`CDPO&XwBdtK2|YT<=m4D7)gu9km77{$|z2-%#F?1PQZnVv4-C63a@PzdHV5E ztg_v*`SgAepy*g_j7NhWo7qR_8oh7T@jyZ)#^4dy0`us~p*~|7w9|uute=(jRrg+| zDJtA6tnR`6R6!(K_OQgggQ>Ib=&Qio~!*;^BDt3BJ zX$+gkdgH~2wV#Dzaz4(j+BEp6HoZoWtO&1St0a8uceU*L&zJ$njEy#QAIGBEJpE7` z`ooI}nhzqBNQ;}$(#*E)L*W)#Ohfl8*Z0UX zwd9Vz$n1yO(8CX~d#?3kT1}f`$F1wr=!qTuaegPmJz<14S+4-Nhx|*0u>mz6-bhWp zmFg*J*SeC`}InK1T&P+(VjH)!9~h(c%d7g99c{_05i(--5a4S$ygr;NjMP> zPC8}VJF~qq^ybf^*zxi2g}d`y!8kUZ1QFO8D6Vh6Ad6}DJ^3|~&0x>y!x!JRQSV&& zml>>nyp*cS!dFQGOUgs(n^lC)offmtQ@`uR&0nLdaRNYPwEiGh+1>6`ON&uPbi*!qQDggOvGijq-}@}Yzs?Pqft^K(x{!gTozrAzpPo+;SaJVNOhCz|MypMyRHJCW zYw0N~){lG@w@u&blOn{~roK{#Ll#a;*xA<$@a~|ZlR*TjVB{&ZB5nvQo)zxgrl%T3 zOenT&>#9#X811Xt=Yq*Ry6cz1&?l`{p2Pl#F0E5XK|0Qqr=A_onWGhwQ@s)u4YJ-0$eTnfs!(N3J&yR6}na|Hz>A?Y!_#`O%TRO z2LZI8WFa@HPMh%HNFRfW1xIf9SMbAQGl-?w7%g1?S=IlSKH+*S-p+NNDwZyDu0 zN~5Vhpx*XD)Z!UWF1&kHNOAvKpC+SF938zm@>M8CrB1tR;Dv-Dy+#nkuWJ|iX;;_w zq;ZKYVl(e%AO(^KQTWsvP zJ1}P5*MaPc3L@aL$y6#S!VyXCpmL12l=ZNI{b05mcd0>#yW;&S7fvT2NJdM=`;v+g z@9Al*7O;|?dT}*lu^~6r&T5gxEf1IGdefp;_fpVSbs`XM@onHW6+DLS8n0IK@7Mf@ z@4L1~7uK0zZwQuB#9N@1Vzl`8zQBk*Ki*;N)e@jX!{xyR=K;hUVf^QMCu5x=ciUVl zV&u+H;zlbMU>6^qW6-?in44o4VWMK3&VVow?gSJ(SSF)Ap1ly-i<8*wu`I5B)a(s% z?V&;p-}d9YKl|1_o{DL&&>>_Ja<>55YcK-XeG8nirmp^R4&%U*=aa^7($S)E=MV`b z(4|p3D#!5~Q(r|SVca^Lrb1$_bY0t@(hZbnYI|&ke(qMj@53z;y6++1COMaZy3OHe zA3ijH^WIo@%hHgr;ve7rnY`mrG(l?wvBGV9DafF*qnA&%ifcv$R{Y3@(J^7o7gi!n z(516#J=WsKA0bzd-1ZbjV935HNkSzX_!PYCLUy8WgOOt+w#=J-!33*oOVpAlLe=Od zATO!%yARFl#(rhN$0+p+;*CSJ?MarmFN+vh{$sj7d)GfP?YtRg7ba-plAy$699VZa zxbk(-FVIhTbTkyRfl+Si<=B&=JZgDF=*b_; zBs8SpUl9bL4uk0^C6qE;`I&&JLP{VncLz0vY1PQUAH`6LEz>7<7Vfvt=*#pvws}*D zz^9_AyiAQ^w1Q%D?o_^YKJ-qf#Bla#v8iW9DW-5o7(w~LF)2T|3Jm)0k%+m=9-rE5 zfhz4UiubM16WQLAs(vu;k171+)mhV6oh8yArfQ=b&qNRSK~F^i6%S3rq`3%{*jWN+a|s}ymIN-k zLt5y`{k$j%8N2*)DtF`CRV5$B%Ll>(Lzn!jUyWwviM5Z*Wl4&PxMl1X^vmVm7GxWP zy28-!cWwH&%{!h~wwM87PAC_|ar5*((q@gI?W;8U+R`7WpII?|W|@t>bmLQNWmzxP zvuCZd)SE*%9x-8^f8!5t?Q?D6M=G(~2_6YBUSIZAs5aS%N!1eJQF)%*{y~^NfQ93O{&p3z{F+H{%?O{M}+D4(xJ65AMnN&R^{mP4G}@HM*$Uib))6XT2L` zIrNai?@Un9EkVesT)&IL2Oqwaq00wWRa(7Iw0S1O*kT&L&=b#PTpqN?FDygX|6uM) zd-TrLW1Ic>sssoU5M{|SG4#=)6$73zcK!cV;PX?1j{N|l@s8-Dn^VtH=FJI9Jl$vP z)~~HB%}V)y78zW4@;rh(956v2=g%u6{4V)Qb6{~nfuG+dbKkxI1T3qd(yuEBbPdB_RTn zlyRw|@VR8lcTWbsm2t~@@sbDWObvgYT@?sB*1FSc%2|E0yLMSZ$by~!$qc=1Uy{TS zZlYuFjs)CQa5?>>h$HU|)6U>9xQ755bOfQcaF4=8@xBe7Kh*Bq3RMKUj#6Pu*>o9v z%YE}^9F4d@rEDZ1HxHK0V9|8}rntiA#-Fm_ z8Se>R!gmK3d}U>4)g@trO}5Uo?@N|+j<6)07oSP(bJ=4aCVN`mVH-Ykz%*2PT6|*t z#a7t6@L`5}@7`)>9^!amPZha3u0NWYT^QYlJg~Z5_jO*^3U8{mz(s9<# za-Wu-za2#mf1!V+OY%SuLQc}v;z)S#KJXk*uFUw$6xLiX?muPbIc9ah+oe&|xnI=Aeu4#`it zdn@Z|_}XvXR1r#dizNypDprA~{md@Dn%obJ>6k0`M-rjL#$r(wPoA>U@Zs9To?SQ) zG%wp$YBJK#wD)ebm1Qn}dTdSbcV>)oLlU&AD|iJ2O#UcQnFVRpZd4&431L>ptC9$i zgj4UQ&iqzN-lUY3cNPvi(5FJwlT2}l4G~3m7T%S+d167FgCk;~9E$OLDqhW#v%?XZ zra?v3Ay68m^H}SZ6jJs#(yUTZn@0N*c9?qY+`jN$tOkEp)JhmkKv`9P{sLnGU&!trsS#nl9 zW&L9goX@?c7KWECB5_$|f<|Eyd%t0qvi;z=6gvyLW5ZSBm zgp#oLhRj?%HeW0fxn_8=&9^dUAw)6WJMei3OpT)^AT=O+eNWI@IQmKt z7vUw$2M?r!Hjc~CAJ1}q|KqqI1d|q6JbT&+Kjp|A410Xq09q|{%k#kzz^OU{~*h>p5Qg3?hC^cd)=;xR+Q0P7JD z<_Y7&u>Bg-g3r)joa%0I*pjgc3066-tiOHx6S<3Wp2(fKxPM_#-4$RNsc-Y%q&CV~ zoilGuOE|#d0qxNc8-Q~dth<@&Haz{KN(cluin4jr@4J8#GNPaQ@b}peoddqWwqE51~DWN z#+^v#6Y#W!r-jJ)JKnWGB!vhi8BbU3h9DD!T53nn`q4Q|Xbe~crMG2;iTDh?QwQ7C zzQy!lrp1^l@WzLxT`MPruqZ13+Er+Z-Wm8|!yVn_E=xVR)i@?wy04w0thjzy* zuiz64S#5GNpqY(-!RcAfiRet0v2cSd;zxoOpV3;NP&{u+6ZGThi0-}ViEeiOe@?iW zhc?jR5Y=tpNxJEfAy8Du;6smQ<;Ptx6-2F`ma!>_qnclAesXq$N$Vv&?4`!2*!Ecr zZLL`n5_;tFn)%QjUj?XFpo`|QS}mFm14m%uC5)vXCRM|Qvk5{52C)yY_}^e<9JsY4 zPp|RdoA9-{7BFv(1egXlxgU>kV8wDa@6qr zN;sT)f>bgRWX{KBlq7H=ppApEPzzl1FCDOlg$Q8O7>4R00-Il6;&N0Vzu6eUFscv-Mzv6IGq*uaNd&$Y= z6?kM3FXaqF$MSd+t8@ylLjJZwzH0=%f^W6wTCkP7bym)huiyK|hdeyGSW*iK@ zD8PK?oqubCS^FB42l1v#h#yJ7vR1wp1l9c|pl67o35{>TL3l{enl>0jLqOh7!T{0o z@td=xA!&O#ZQ}u%EJ`7|s*s|*Wd@KpI&9^$a!&>lMjey$_wiV7kG3ii zpD#`aTvyan6^Fgce!eq*N>w0lz?E(2{rB-jde}i-?^>{;P;A{5Heg2YF_@*NydZr3 z^c>-Gj|d~yPnc*RLRrwKRsKif_9i_rZ`#_Z?P8;DDo2M{^6Pc4 zrB?>fE0DDMC+&vrr6)0CqJ*FzoUgaWnG9u~7471&iEAI9awMU|N&t;EI&kz9+tAD4 ze$c3sB@kvx7~~n49u2I7hR41LKod!i5fEhUPCGUVk!L01z0GS7%nmMnbl30W(V~}ymNOP$b4KUiF}ZpkRzA&FS6yG(xkXk) zmPHD7#YDR`+11#mf|wLyH8>3#52}N8R)qRpf@l>&9<9XS zgpt4ipq^uNlS9I^a(DZ2rl8_=Jbk`!yxw~t==_^9{OcgI(l;V6wBJ~{RK?S{^HNC(xi zz+Zf4K#=SaPd~7XIu>asK-C8iGLC?LHNn$s5F@D`v(htP6Tvua9Q?rIG4x)=|MBOG zY!YBh2#AIzK)?j{AfNjg0bp(upvf~nL)jQWa$Ad*L<**)F!-o;O6<~7EhoRrO zA1;l+vis~7gs~pvsg{zrIUXo_Nvb`44F>FR|9&@-TA9AHELZ}n&n1Lzf^|%~Gum$G ztUzkhmS8q$ih!UiWBNhRMXdxf-O~x7v_VO1hNlB*C`v67#~j3Fo1s$|W0hs>3_2DL z7?zM#8>Ht7OSH;MUN)bHp>NEHxU z!<8p!#debyC$ew;luddv3;do80pY`*W;c%eiATL8bKZJ^vtIG7p(7Vl({U zsdhtWCHm}JGQ6w1G`=5LFCM|Z z`7`EW)I-o*OsG2H^!zs-tf7aCXp~K-gfK7REWRZdL@nPPZRTqeZ`?0$`Lms^Cb$I- zfrgn5AZ4+x8J(HYe|rq89};>DWBMUc_bmaw0N#S=Zb3f=1uDT3dt()z@ZpFFlC0w| z-)E)n)aU%yC2>5n*<=tJTf>dAY?g z?@eN48kC>#tkF~F@$^Ga1WA8C==gRCL7`xe6@onc@qCw#JUV}vPOo2JBo7_oSbVr% z=Q0TfecB3=c%l=q*%JZNuVZ29_+lci*WMd?qX}aIP~I$W#CLPLE#qeb=IIAU^x=BV zW$1Z4{a{6uyzK|k5$obDH)SDI3FC(oTg^hmLGU{mcSSe=rr4au%0JU*h};~u7*jzQ zw323lf(f%91l7KjfHfWb{Py0^SBK-*L;nANqFo@=HPiK94;JnMV_@S1%t)yPJpI6E zPmJgXpP^%sa@eE81K*0^eVbVOhfP6z(z0;Fz5!Kr6fpbMHHL*R~vsgC*174 zp=U}65wh~V|3tY=?d42Kfa|Xbl2agzI0~GFU}N2Vq~V1;{g61$%JY5@Y91c1A}2sO z3mlursn~#`7JYQj?D9gM`4&ziKuHk-JcI~C{zkzh%3#hRu{obJkgzA<1qA;ecm0TXZpcc=#8!T*)3Sd;L1f~%xwgyx#lCo z;j8!|sNgJ=jio&t25e#)dJWs5Bn)VU5iNM&8^^7p*D?zk3t9AE?&FVItu}P;N|f}q zU{guC3|;RrdZr(IMA|cQRJfg3!QkxfRuYJxAV5*$i%K3Az z`ooeu`7zF|+Va>;r|b|xRI}iKjcb426HoX^HymMsj9l;T4Ri;?oar?f z8Auq_da$4nmUpP-Fm$n^G?3K~c0AqNe$c4YSja~_xWNM;gfn zwh)db1Qbxg_&$`wKcI}q(Ai_){OmVtdNmhD6SQ(3a`jeD^0CjK!oz+~kWlFHFtOfi zLocN*x@ZsjHL`-ByOt2}!7G6XZzwH9fFz{yan5Dvb)})Keh@)I&)nO7U;t^th`tae z9}_GP2_+F#N-aFv<2=NEI6V)WX^oYDB8)OsdREl+6I{i*Rr(cU02%kI@-W{SmaoIkSsk+7wrUoQ{4$@v5G)_ANW)2Y36l53 zDVf_1{UqDaA?_FDo}WqY0BEdXVe{zYIjBQ{{FyOuof6^jc)aIz_mTW7J~Q*g2}=F1 zl8H+?i&}aD=7fFvN+gdEbZL~av}Bi}XQW5=uY8Xy{KJ=WU{|EG@Wbr-H~tp%un61G zZN+c^L~f3AmUrZB_h4CY0t!wJDsi^HaZej}3tDB>ajzwroRHtWv2?tb^2&cWRQ^zj zEx91FTEIao&sr?vk(9KTMqk>qB8UgZQ?^a0MO?p zTj&A7@)EkVDlbj3EUI<_+qc9h-K=NN9Quf6NveVd#!M!+0Lv_peg4 zYAK4pbb#hAu+v1I-jqgP>hto17K?Aut4B3}rE%!B>%mh$<_+^JK~qu$EIXkeXFwI- zjLve0h;T(yhpSjs8pN)O(mn_yyAoAXr{g7>cy;sRNk)hy?McPMNzT7bet{**Rj$f#*qDA`@=1g#mx6OK!C+aeScV7;gCyt;=LroSjvL}D zZe_QC#A*gG^Uk&si3`zhws)$!l!n11z3@`PW$43qUl4if3Ki_{y6fJGnLkQTjE2hspvG?%A0r772IP3TKPnl`ME76AQH*rH_(PeHltTC4ZWJ@jCNQ%%R5|AUfxiqs~6W-x}wA< zLtqj~R$y}GVj~QFTs}KZI&Fz2>{Zj~&sWpH8{c6)#usJt89E<8THl%Ji&J4NFi_DF zM%heI01W|>ZmQ}vUT2+ny-Lw1p-7lDzBt7#9m6!BoQ-wI(Z`+J+KkT7lxWwk3E{e8 z*Uw(Fse1|WAG{cKUbH+*>zHu0^KdpMg5Dq=re$udUw~Dw4Jcts8Z^BZ zNKiHy=kJ#@wQEAWg?!jQ2=(bkwDoTK!jIj81_HV$dXJ0i@JfnLSqKUVxqA(*-PN^~ zHkfz;uc^jh(-HuE8oXZb8XZ3?!Z<7A`T@TpXbYmF1zbKw1cYJ6Q;s(y8D@yr`_cL; z1By8~+WdxU+a`39v{Y0!BNnGri_HQiphD z$i$<$9VW717q$2}3P!W#7u<&ac??!7Pg8DbpV4D{n%qaceG{uTnO6Qwgsm~g7`l&i zDwrUEo#kVoc7eyf@h6O`DJa$q1-WPi$epi)s`VH;L0wxSK&s{5_XEpULFL$zAR$Fs zn4F`8MjYIqp%hFk!aj`SoPbX#31H49NYDf>;De7rTlWk&%)V7#nFK9Y-0wV)4P|*X4zt)vejcbQR!{zNu>q{3KVgpzUav2RR9pvl=x0v}thXfOg9 z=PBv9e$$7U_}FGkkU}tceU5!5zzi+~btc(CI-+~{cveX`iip)BV=4P)8M@!W=y|)L z7r4GXD_!CoEBAl96P1QOZ8dwm)jho^-cW3mq5o<*Jf6Q;i5u3OIYz`}*i*umG#@)h zyrI=u)WX*I^;Q*_KTjlqYiJe=BjFaZlF(0y8taZzC*U(KhToxA>=J4`S<!0MWz zFc_O0(usB}3CC{+A|&kTHuNmQ#O|v<**!!lK`hp^neZ0ci@HzY{(HCeFQGpqiBK7A zW2}t()%`01hnezICSqg?Gq_NIVpC~2E(Jz7YA3ww2P9hxDG*`ndkGY2lu*T2LgRJE z=5UPcfgo%etl%60nQA0P%@&;j7B0b8o&|?eixb3>#=0dEy4hD}E#+C?7x1K|pv(*4Co>kV8=@M2AAuqSJ2Jva_t ze}RDEF&vv1S&E2(8cC=B52pP#@effXI=c)G@deLiQ7Qb?>AY7 zY~$;RBVV*H30} z-7xkqg9uvresgzD_mMn!XXZ^r@R6Od1>9!F@ zK%Awb#C-NZ^;g*c1jX#Yb2+3oEdhZ$h!vLB@y`EG+c;U)n%+u*gK_`0xlO=SPB>z@ zs$d)iJ3e+P3=~%G`DoyP1IC(p`<#5F#!q?N^QNKsdVGSX#u`o9M1(H`Cg=F*V#S-3 z;~n~v=x%}4x=9SpG+tU~7#d4k+|iE>-jG;DjHj1RA9+3z*rK+C0X0+~|1`I@$<C4RIT)ubrO&i?|J4!}WG`MZFbw{X4h+*n$gwDuK2q0`cBHz``-4P3-32p=TUyQdtw#iwWPJB(OEy9wE)}+_VbW%zQ*{n2qSsHSKdc-g;M@JQx9?C(EJY* z)t7P65FdpRyflhMys!REH+1vKec>=p@VNDvNs)VWvA#N&jLur2M&jMIU2K^x+qoUEy|@2$2kOixEjMn}W2*g2rY;`5 zN1xL!Pt)y*VkM-MSLT0~I@|McPtKpbY(VkVb^5N1cAGyRM}Y92mA5VU7Y!KAx3Mh* zteP5{mvMj}U)cbze~#mRcv?Wgxzm2GqSd`YQ_$KFReCWayockKxvC*4V_fzAYN36ER9PNQMJ+OuCIpdFeZ72Np#qAsRX4-_D@tekNv6PAq!_WW? z9|!S6u?gpFsOOt_nGA2nh zVfyN<5!tajZafxK+PA`|*~ifn$G&H|owLMAkAZkr!G@_Bvb0g@)DaS*R0wMm6T0gT z&D4YuOXjJVrK8m8)w6iqlvRSyT4lYJcxi5WQ4$@Zow_-C{^)<1;qL$X_};ab%+y<_ zE`NCSjQpFn_*RQ;m(G_MQ2tF2URJH2F~L**Bt0z1FiVWYd|<$^acY?=D~tf03fmm9 z6+b38M^`&100Ayc$4C#|T@=OQOLqfao=rS?G(bM;ua5F|{rdyo9Is|zXi0p&W(zD3 zYNlKQknIu!P{tnwWOLiFD)1DXacl}l+y(dSWF_t=t^O96(kcT4%Kb= zNTyHQPqx2pSu;Gvc<_mVc~ad@1Qz+Z)T1qkdW}vvT?sZJEkP)FFdzdge#B1#O&CBE z`BI_D9J}MEZ06S#pAJ;t5Z{Rzxjv4cWPj}$Grkk`i*obSlX-p<8m$n zGtXJSyEpu_W_7=YMN`gt1cC7!fzRj0Ls2Zfo(Ew_O7H-%6>D^s)Ev*!pMc~P5>VE{ z@76%Yn*=-;f{HG-i8JQ^6j$owHl6i@Z=~-mX`6pKeH~Qxdc5BBn4TmWOY>J>BwZUV zCsYMqct2QfiwL-O0<(;SFbmxY0?(&Y;}EXMJ;uPzTBBnUKd#>w#aUylO+T^Oe1qMD z!7C);oVnf9e*zCU(Q(_luWb^T5T>PadDiCyoy2e%1-_sfKd_rVArd6*4Icvuld<9Z z@Dm9_%@a_~1g=4Yo{D&8k#NxIO2XdczDIvDX5KPk-Z?Yd>HN-|In(qbU$*owT{Oj; z5?IMVm;Le|kLSCb3>gHiMB{gn2wMKns>8*KZ*a*4nCs@0{~zaiQYrOAo6D~}ymja~ z@VcoVach(Nl>S)z>RY%5!Zr07&1?r$uVL6S zI)8;ENC1KjpO3LjI{_(};JKfmvU0H63XK!zE8F8!<97e(V$zNw+ZT`i#vjjr&~K27 z;;y7&+kZSgy>>9;+%VWFRJlgb)w0B( zmLTA&9xh^`1z*@AH&Cq7E|v!_n-RUQ_sJi;!M9#hal_74nI{7;iaeoA2#$17+#8#= z&i2FZZ{u|(56*or_@aoxsxV>)42VUS_QJqkJYX$W)do%@0ShzXWi;3^1e{ippiw@; zQ8z20d{%7P8Ny6>5KdN1wp^6!+lQ?mrp%sy?>4dS2N!F$M;y6tC>bB?gmVZmAs>CXE(*1QA4KIWxT&CDowdQI^^(te4j;Quv$dgLZP5N1OLG=J zF|i`_+u-l~^s0cd>5)4BueH9En3FSPbH5tD#>+UvxAd*=cPwMs6LXG7oc8ymANrkm z)-0;`7#L9$izAp_z0QWjf)H53yj38Vh;X>aI2R`22qf^fVC@GIh{WFQ+-l8lCl-u( zYy4dchi^~Wld}KL{kQHHi^W>8S#0ydD4b=itzx5CBbN3ny0d++Df@=+8ovJC*>8<6 z7+)~v?X zeU7mk)(<0F5FKG#Sab-Bo6XE1|gIsjN0O=4NEwD1do(a#K z7e4p@Jmm9;&!az&`8@XX*plvTS$tN?tWjB$vZiNcWzEQ%mNhwR?5t6<(zAwUCAh!U z-X^Afi~c<7^YHflSWk*StURu3tF*TFF{UK;xwNe8tmTiaek5;Z-ots~Cf_5z)nb#_ z=B%!7-?$(yVw-P+Z<+7T-{sHDdnE6XRax188rD7z(VvHYZIXna&Zf8ECZeqj7+gw6t_eca4Fxph!22p@`Jj=!WkZ z-(OtUcFukJ{LX!zXGCjhC=%jR;orM=k5E}jPUqe|Z1BB%kMeP`fKL|C2Xnxm2iB^J za`$c{6MlBzyBB&-Sx)9HcyTueJA-9r=3{qm=CPUYg6(@^H@rQ%7lydWh`kR8EcUi= zdcvm+uNd(t1lyp{AJ(ybygx>i#w^MzI_&M5p_LUo`p1_ijaGO9D>Hhh@WU0a1SLRL zA{MrgU+tUoRr~QmV_oQ`%Sy%NQAX}u_JtO+kc!Q5>gg%FH?(H*cIz3kny|Up?l>6w zFTf{61mL|FN><|5XgqS;(snZE*CEdbZfI%zJ;wnng?dL5O}RIFw4Szr+i6KAtOvH=4FFR)lC4t54=TN5W!lft1lq+-WBPE8Lci3I&~u zY+d01Oo;MjA=EmggVV;kbvo3#wZSq$6@4=q@Q?G6H`%oTXJ)M|{kBte&4(y+fcl;Y zo_JBf7W6;5*`Q47GvkNt=k+bA)`aYJ{={L&4n`W9uyc9m^HuXnE8~u(01QXyL#$qx zn(@}1;ACUX=<~2&v*P?e%$RYVo|&v&b6hNiTZ8364)*GlZY=0yjBkt85D`~ll0G`S z6nzGYtp@h5qY#X=6`4N{1f#{g960Q_Ybmr3a1*ALjadTeRnjs3EtY$7ELy~?&f_+R zWj$9Z6xe(|Y_03$yC2~;tG%eynBFa~Q}ECibhEyi-cr$}yQL97E7fFoX3m543Zvw7 zwsdGUYVKWmw|ncRfmlWh_{wN?%M14PQhXl_Iho|jYTg}dUaw;bD(m@Z$sSi}C8qT_ z#HMO*j?L;N^^8A8hwUik_APJ!R?$bt2V1_U%fU5|ywlyLMlV`xrt~`0D0c#LN~XLE z<0t5k#j1<_WaA8=zur4@br0B&ZLm~4x;uai zQ3dN4@i|0NKv}cnrkO=$epG4_zg}9O6L8Sx)4X@e)ot8)I~H+cD}y^A>R+=VN>TEz zg}+0m@}p+xTVX(lP&tSFo1=;6KB~GqG1Ig~J54r*?F@m}6y)tPPN7H-^Q z=Db*;l@vrPe8S2q2haM>iPq#U)SEV|vPNo#+i9JdwVSoZC4q#nmu6oo5tl=M@n!^$ ztQNw|sugr9^snYa3{NS?P`{$XOT z%T5mZ1JQ9N&QD+^?|LRW^kXkjN+p<-4vq2zDXcv6w@6XbAShmC?~o>l%*&BtG&neF z*=+5YB0V7$p{((B{!b%h>i3!#Qzu4TJ`RNGT`e0;yNT%Xt(Ic=-&ROdQ>PoF%BqWA zPDLv;g4_EoOt~)PCjU?{>9i*x?j7Q^x`>bI!0=h3#BDA>?{%O?|qxG zB8=2Yjfv-KeQdGAmqmJq7g}GJg?>H8)_dtGk~qQK6j{2%R!<%Gru9jrg@c#b_Da(1 z0c}N7s1ax9nhmnnywLJFAVwP76NGckUeEN7)$gQTWBkc2sR`9pJY@&#=J6z#s66pj zrSj8@QnRL?H|H6%ZU{mQR!d`NM0#jT}YwG^#PX9==!s<<|8BPXr~@1yoOa)=Hqeb3EukB3js z!n$Yk1f!65A~YehxY?US;p=V;1Clc}1yw~+v(vfqUPdQG<8Jbnv~NT#UX&=&onC z%8RZQV|n*sRcK~zwP@Q~-VKaB3ZpTV@m|YjryLdTqpt6@O{uraO>$8-Hj|o{o7dxB z=8hhHWox8&NG1Kyn#nuAMpMtJMw5}>)@@j?( zOwCKnerI*8+f)4pr7l)BE7_96Yw&k5E?(WPjE_9ioWX+%BKFSRt zFWz@b@9ANl5Er|$_1xR4^{Gc{xbTbsk zcTOtnvDO8xDO%{7V1}8OJfM<4LGN`x>Fs41`af#lSw0kA>xdjQNctwc=#CY)j!CXh z*49Vg!np(jcUzytTkKK1S-@#tkqBU-c6g`f8cD48wg0Y4ON$dqn321~xhEMgC|j}Q z^l!ZHD|V=?hxJQ3H`LP&qegOkZw*tJ9n=HULRynBLs;VLg~k#--#^DFifdug$^l*w zRQyv=FKBRz`tQOR)vQZ-)59epc#z_qTLxLis4h6k%HNNy5H&k-519O_m_By4r7im5 zUF$ZnzxXBc+5Y*Mp*;1LZ}%2jiv=^Nyi?wLQ88wd0Oz^r-OSJU>&8$&JL}pdjq<4F;4Dm2llCqC^`Atx7H2~vnLDW!bn!;CXnCY?G(psj@JBA8 zY^Z!kV~~&RYmL@qVW!jb=-aUUOxu2v2aTBWqkXQ$wlmI~njOp1!I6 z)6-V>dw+Z{!0!-)Hpo@jN}uSi*e-Ujg;?9Vt7@h(~V5*ve&$-O`f> zTrG{5n?Jzl^i4*s@T!NjjJAHxu5J#eK8As`kzo6plVvvZ)spVn^FQrV>f0qD8ooCC z$45d>aWEoe+M&r^s`&>se%JA-)>WC7!9!bD{T(fw7b*Wp%m=}BWwVN{`liiA+(B`K zm>hh?=Cm=bh&N-`rh9E&HTpzu!(3v(S%~C{c-`$2mi)+GC0E+1`7xzArd@a_?NLk{ z5D%F9iGaHkgiNX?ht}tMbU#DoZ@a5v5piY*L=#2i2~S7|Z$GuOVUCx^suJ$=>r&$! zggaS(n$=z&cB*vTC1Z|5-iC0_8h2|YcQ$TD#84A0Y34BRvtX>oR(rGe8*iWu2z6Y% z&X=4SeE1AeZpH3P;`LyrV8Q3ZV%?He<3FgC;8M(YZY2b44nE-K?5F>Q4$b>Z@BXT9 zcq-AfNQe?5QeWSYH5mSZc`B`Q!*$gND;=X#MaGLSeeUpaI_NUbb^s4m~y;0{gdx5<_y@o8Gh6j$^Eo2XU0cTPHTn{WvLp}<)@{K&-#cK zYvuRY)U^8d;4va9C%SwW$d7n}m}c2wUFbm)lqua{jF@Ru!Fzvkvh2_@IHm&3$&b%SuSuX&W%=zDprrRRswmA zqlpV}JWYp=ZWoC?#kY4jHP?S6qQO^*S83reE%;krT_fiO7kbv@;6`Otk9;^F72Si0 zM{mabnb$zvLnlYJWwBZB=KPJ`fvG2(CuW1rtnV$&Zzq~`2Xf{h_Tci(PE3{KS$ldM ze--{0x3QMT}ZisTy{x)qjF(tLpY@gtI$S<%!EvLt<|l52^qO49^{{b&gQcawwe0 zMP@;RpRV+3dqlJ8Q=fHDwy*??!{RDtU`P!`@?mF@2|COako=S_`@+$7Kn}~jEM9l{ z#w?&!f(y8)`kPHLgLRjca`fd5!>*UjnW})?xM!WtI;nQSQQ(0Si+A%@)|&UV9{M^o zK|@!Cl~nD1s#F;pVO52+k+zi6dtnZM{t4e`Zd;ouV>@-{PH+tLN9NyyTH#TgmJ^Gm zrp)ixn#80vKLRu>CY=a{&FS>qCm#bs7&O|suX|D=T%#idI=5QMqC|q{;K)wLlY<7$ z5DQs3-8I4b(Lw$$zAn$heP0i zJ#J6?y{{iJMm*d&Uw&O+{jOvVmHH|2IKmO%JjI|$$P`PkjnQwc+`4!n<@yl>bFnRq zO@wUBl#v@KSF;A6VoP0fnD;my_?fL*yh4V46uZt-K$ph(E!a5+5npr;7&6fq^I z1TnaE7IFn(xKwFo9mUn@!$h@sOHC_k_o`-t2@sNy@YFd95OZ((1xQ3w zZ4YP5sn+@9JhKCGWn)GOwIWt(bla*|$pEIMdXK$gJ`g1^98iJl3^0Ud4MfuNcc*=|3ieX!-h0GlR2><>SZ*vxF|9eq_UxA>WJ7f{ zMH<_bR8s6M3rI}DNW2o)!GjE2sObE!Mu`G#RF+zYZhn!7Qa_EPJiAgZ|9DR_8TC+E=HABAT7^j8Fy{4JY>R zl8gSh^TbuQiDydu@V(X#7Xlch2j)p`k$4{kum~ga)QW>E2L9a4x_{QLl1A5rBYcwW zP{(;(x^w1}T2c?5VeEcT&v?zN-A)}&x|5u_Wf@)lzSv0}Q+#j}M1zOoGVM*$ImG7kY;< zHj{tfKU&O%+*{8_H=5viEHK7#Ub7QQ=ImPVU6MaiGaika~O`sOwzGr*yt zjxnDON#j?A?jMHxvwNPoU{{-0A})~KA*BluQsccGo3y=Tjgk%C`7{^uDir{d-0P2Nl;dS%B124gtzGtMs6d)FR+$mn{>==U!9 zIt?G&9T+&!c5e=Hbq6S3Bn~dcyuu6;84%Om%Jip{ooq_82a?>uGSB?I1S`jS&uzVI!{hpcU z9M)!abXg^~ju#Al_&Z&G@@bY<0gZopDd}^nJRJ)OeOns$NZ+TD7zg3ZvyC9vJ=i%4us0d1Wfai@&oxNG_^F>*_WV!XK z=*?jN4(o-k>{olg7C9ukReDUO3E+l(7@Nn@88jI(d8~B5o+zb~WC<`9IHDX0@wuEu z`OQ`FRxM@+x2&D2ox*HuUFR_y(a`7*Q#((7JqhgPi5^xvq;fZ!IQ_3jOeY9@^!#(@ z46FX4Az1F={yq>_L17B&*R_(B73;d6KR3gkI&xNNDdg#BW7P&t1^69hE@!!JNWf0G zejut-=QA*={|VQ8t1af8HBD{awT`7ScxhK6V5cYMD&&OJK8n}bh^BICXQo1fdDrMR z(gc`8$G^{Oqdr}f6754j9JNXHxHA#XmB$cwfS!_cF7#bB{nKX!yP77gy>0M)C1qNr0}5jT4qo$C+SO2`c&$TG{D z9q4w><5y5+VG~eSa5qVM^78%>Y=e>xUKPKX4l6S?`@Q%mD*{shp@aQc-?V*VR-4Db zuX?%+SS@@M z1;SEsU!!&Op3^o@GEwQyd<~C_F59fYm>QL_EY?YMJ=38Rsjug0E=>BS_U)0YaUT^S zZoW(WBII;^ul&HYcW~91r3MfnSh!59lYZmGO23qytED_r>4Xw(l>FMpv0#o1|7mTH z6;jop9V2m=EypMdD2~%7UadGg+)e%YR?1*lesrVT@0y}1!xJ`4)XkVveEbLmp>lPl>Y)7+UrBP;`*#?+VK>o-6 z=0Ec9UjiaNKJA>$s}QG%r|7v%vY=%A%4|!Z`Ksb~ORsxCLu4P)B>^5*LDnfenKvUX z4Cr((4kACPQI@bn)=m9=oI5Bw^Yc$Z5--P$L}AyS%fwfUzfR6lDJuMTeZjRtN~S>m zIFPCB(!MPNjPAKqWCeY3c~G9PS2^Do5P?3lHAA0FH0oV|k@kEf(HI;ilu0?wl5wB= zdgJz$Wno*Tg=OJ|e=XDfcLrWBAR>o0hN1;b3BhCRw|a#iFldB$%qDhPZF1$t@Md*# z982DA{F7y%JR{W!sTc!TAU=n@>R%ipzCwd@*B(TEC!u|4Tj22lmcuoW@xQ87UOLUf z{gC+GNzru9p&!!umWm9iYD6k&22L75XY4e9h{S-eb&4z7s5F+1f@pm6OO2T(rVN6f zY(E!4GhN$mp~FWZwFB}aOaL_2)L%S)xbv*rB)Rhk*D%8a(pr0cnx4y4J8AUB8}y#O zPfk*c$6*AfgD?yk9{uL^Nrkd0Ng8yA+je@}4!B0-P2IzCCu(tfWttPo#pqz>P zM-1p-pQNSb-mR!-vVv8s{4->34Z32KglNu>%nrJxgRm3-IfrG%?C^{o0$KST7%DR@ z%>GC$7*u5=gdAbD$x?E`ElgWB241|L>>{SxK7t+BN10497(H6a-b9a^4~60oz^oFWvUbUS5>h@XV9_P z0A!A<{>1v#JG!N&7h4h&+Y+5NO)s*kkgsfAuzrtIYw&AmD6i^S?bu{;f6l2|tj%)*1GHi_GRJs3*rWhZg#f zh`oM3(Gu5DI)c;p0RgiJl4lVCIQIUFZcSl7U)(VcY1RGHT$)ViPO<0LZ$MUMCAD8CM>xA|DMQ#gSC`c2SC^+`4$Ma1N$h4dE4;4U zIA1&L^<&qT@S5prL*HFSFD1Qxy)=^Zwojpk#|6Cda~4Pg2EM}l{55~*FLEjsiE13~ ztuULzgB5)sYfvH04q{1sJy1PD$ecp&l8-|a2JwB!u$N|607M&ieo9QwaTY>XrG(DH zk}?2;4~1GNYdGJQ^-=RV^HTt{2eLAnXZLZA$vdm9MvQC2yR)OyhPa*{v4J&Z*4Lfh zsmcm94Ca6Ai-^SJ7^R_fpOX(IR0hjEQdrZ}JZpWch@G3FQ!sTj8-e^Ym%dldh}fW8 z=LP&GS%*6pG7DocLi?>c;ISfQ1%{_IH4Rw6>Lt;%27c(3wV6V zK*X45lr;K?|K#}f_>}uMAG98?Rt3h zT!DZ48vv$Dap9&`)D=8$&W*3rcCovk^4%T&eDLXPuQfs=-Si=2yMH(h)hz?sI}8qkMt) zsTqKxdtnm^y9Cw*VPl;IyuB~WC()+ZkU6T0M=W6LD6Ozn0@CGDwwwGzPG}JHhH6(}PXR65gn%(X5U>;y05%iV z)v(+Wjjzp8azS&|#oc~M?v;kND5Bjrj3?zqa57e(;nDdyX79f1`Tr5LE zsu`b<75Fpn3yiU0l+rKvCLH~TK zBM_r&6ewbnlou9y?rY~5RQiPAIU9l^zYd$F5L;Z%xb3iWtCyQt5q0Vu3IV z?!w@M9;{t`@{4BBQ|$5sb}%R=kV_`O3JBxl%$sW6l#Y_7I*pvF3ktsm6wEYYG~!fd ze%)glMemqm|7YXDAaE~{7XB~EPrZViDyA#lhZrU5KeHc@9SI7K%PL(LQufB}Ws5Aj z=~uE67q&y$m;%PXyPp!&xVbW|J8RtBwY{^}jEH9RcgtE`W|`nG9FV@jLPaDgz^*q` zryT+6?=fqu^Q1ns7-tDlQ5e8rwrnT0j~{ZC@`A^Jx_>ODdIl=?BBZ9IcOpvq1uWeV z;&xSI8bbMuiA1PUt>43PV+B)32MZxEonyk*(Bk^)8HuDpnCn#Vv?o(JP$}Qut636F zKl-ZgfXnjt@MdP(0G%~G z`14;k#2QTZ`1EroC_p$!GH#kRMUJ?PVTf^oUCNM8@sp{$D&x#kz>JDPmJRIOWu%fB zI=5CZ{-gStxXA}Zkjz6lPn|9-6pg=>btAbL^;s+$6UtX{=PxdI;@$>Sr4mIFtH`4l z+0DfXdsg=ppN+94ryQ_?Dd!AG6ubPue3V@w!0~1LW7CUN>HbP#u;BEV{3gxiK&sVM zmI+E@Ll*YG&g->*q!z6E=MMvB6wht|n`uNY*-K0d3XU%HxEgvS-?Md}%e}9-`Ip!9 z03u?~9P-bRqPUWU>|ZrmMJ#ZcU%4$I8G&LpaJG-v{En*%WX^mi+HmyB z(j=Xf$^95oJ^qDh9>|GrMn9GKo{zLQF@vSFLQ4Ly3v3+@IV#bU_H`?wH=HUB#*g`3 zj(-k8u`nC1Z>~K7Z4ZGp8zFHi8=E?sw`@XJ-J&53B_V(QRU8j~f2!)2_SU=^C1$2T z2#<*-*4aTWW5@uB)Jl=7{+}1(9|x{RfQc_e=-Wj;yJ6LJ{gF z8d>h=yI=h;7kf8)i`o7vqIs!)d~fCVVz?8pr?x?xwn8@ zr^ZEBZ*zqMvWEbKmdC`UO}R&4f~R9BI{6(N)vUoHr&WS_$#pnhQG$>JL_`s|x)=%v z&|2-_VVZPkm|?~2?+f^cP@I&;R0AeQ!Z?jUVzGefqt(&yj|jkL z9;An*dx%dVr_7@%6%KWpxn_B4eM8U(y=;_NoX&ZJY~VaAW&nE-H4EK=6-bJ0qoTFf z>gf#`f~bQ5-GzG@h!wh?Y=T{*+Oqxn0d!Zw-J!a>#hC_Ayne#X+xs`Hc1 z_HRrrwFH%6{fyx5{cHke%ua=-kH{y%r;x=6VK1h+1>zUrgbtDg!F|heb+V#;o~(N1 z%G3@BUbr@WnoM?y0HU%?)=B(&{B0WxD{(pjoq52Da0Jxa;G8ZM?Dg^_WJ_wyS`7lK z(!c9yZ67l?+K=0r0Wu^9Wnh^vwzL01iOox$?|aiArU3JUrBTi;&fIz;_419%|s_r>nQiDNt4H?e;m7wg{>X;G$_xW*W=T4s=qq3>dKIDE5g{RC;LQMZ zC4xLrA5fNHn0e06q*i4FyP#5WwXF%7;wnG2#W02URPip;*uVsfGbFce4B))?YEPA^ z!I1Kxm%SS%qwJHj4KF}{C3?V9Ldcw(Pj_J^IMjr1szu;HW3t!N=BBZb@+s}IC#cFO zhtXZ@LT_k>NWh6Xd(^xe5P_l1f0u1Bzi{D)G^Cx_ng>Kvmnpc+m#q>Jks^TRY-ZHX z(z0mtkpE|VcyzPRHP#Cd7Ru^Z^!NOo#q#LiUy#OpCri50!hdT7Q-Y)Xu$D|OsP7!Z zx~p~i&YlSzwllB4=cmr&V#Thx=Uc0{Yu?HK78P$`NfEt z*wPv22VZUsgx>qNZsHExQj7Q~tsuGOecAxwOXJ-i@OyH7Ua;{*hR}$tbBMKJMxZBv z@c$0k7w#6YZe8bxji%%8`Kvv2-}}0DOd&DcRHLLl0-vYPPk8EB7Izm!wGN}N)f$m3 zwRmuDmLcaQia>s^C=@Ntzw6gzekuIBj>s?L-|w>HRfHcapI7-CSq6$5uG8z14-Bh@ zwdV#w%)i-es9|h`ld*UMHA~OBDDJF_cW|C;Br2)Gwq8@RE!zHYK4d?64ky3e^q%Hd z+Z#sa&bPV$zus(5D5sUm#5>t!PIUA~05m7nuu*>twSXN> zC@Iru{O@j$3XaV4Wyy&zz0UZ`aTmY?K?A%C(8Hcf5hGOqNbY$6`}EDXy~Z9qsPb;5 zdtvJe*XMW%{;_Kksz%D4n9Cq+OKP6K$GL}`5AT+jB`iXqY71{NzF%=a`YyVZ|Iu%U z&>o>BIVOV`GD+N#Y`L0&KU?j~(*yw&+#3zCi1W}wn6^!;^Q}a+Ud&^$+^nk8G*(= zhV6`Q*A8ua6q5Wbqb0dZb6oh3zE7U5Sg81Rica;vAtwj9_lNzCX;6{VB^%xow~eW< z$^d~%&ky;CCQ~$oayVeKx?K{T4?D$H|6V$un5%;Wcj`tB-%7C=2em>Gu2X||(_a(6 zZfAzV>$C=n&fu?|`fEJp=^ArI{wlXKtfKQNCygghY)8R?q#Y=1!jnD!AO$EMeBYSh zE`=b7>*|!7l|no1;3bWtS;dvE9ju^o#UlgP(uHaEt7r<;0s2qy-5CZ%jqk-5bcn; zG%B5&b8|4B&HTHnDtK(Ft|(Gy_>U)^VLC17Q)-p@HgB|74y`8zu!f( zc<_^{S!}bpR-eDAU;0!llDwQVyd*c{RlLLzOy7=~Gav}F3I2K$r{N3w+wFrF6lw>& zo0oI!@dYhUQfj0lCvtaJOuW+aj0m1qY`=%^t~B4yzSdvCLJ2V475@F<<^->s73s7q z_4CuXOfsr+Qb9PeD~E$gg7fh>e^(x8q_C>UB1fu& zw^aWRd^bNan5k*hY@`o*s-r3T277r34_`oDO~FM~q$1$UUsrN!p4t`pg!xXX*{*$R z1^p)J>gdw^`m0Hz2zl4lwWEcfBDrNXeIbHOm`jyE(a zQpdR(6UH~fY~YSKo~EBrXq2S#{#Eb06YINb@E%B|Em+;lkDROdM>{&3ufurui{NZV zfUEL_)^6JPYz@12Aq8)2mjby78wgk_a>nl!(tEtTdnU`^+9MVw>D$Dypu3GSvnrw+ zeN_Kr#~;LqKBVCOY*=DPZsU^>T=x7L3mCd>u?KR-cGn=h-Cc=DO14pz%9&gooky` zZ1G#FJ*vgU)N*3Q7tuatU?USNp6hlndX-9SKr^wlqHB)-9;_GXIC0?RKC++Dg7Qra zNuQ*fOT+h1rn>wBRN^mKKumG{BQFR|PyKZxh(QJ%ZLS*(nQ@}B$0 z`Wpo}cQqvbnzDk%r3YUqk}C%f$J=BZnBWrM_06YFj&Um09JkX~P*6E>QUa+quE;&- z%`Pek^@9nM1HzDkZs(i^HJ4ulEA{UDd3(gIYHo6c2@@4goyL=Z0z_89Wyqk$-7Eyp zw&rgJ@JK^V?Fo8`b9y72FO`?N*1oyYj!52JcMuip8Xvq98Q7+yyO=v_Z!G=gEx=wJ zg*P(Qt0ddXB^X%bmxQOwf~Ndccz7*})jUw)%i0(>ZDb6~{=)n2DE??x`DXsTz2GA~ z@FV_YOjR+XqgOV+AeuLSh&{oZ0{ix?7%@I}bEuq{GEuRku6)$g$T!;X3}MMmez+MD z&r)&xc+~GL7JxQ@Smn~^M-5)C5u>WDK1P#0pXJ>fZ@ZyeD{cqP7w@VIEyOFQq=Kbn zRMW717zK(97rzsu9Y8Mxhx$)#nQVd89fQ0YFVBYMh;dohN%$++SFZGc#_e~o#iD2W z&6Bl;@T04$5XV0BikZ6(iPDXoW@+o|R#$G<6(TZw%-V&M050@}6yH}Uoe5MK7(Z#r zQxJA6ZCKViZQdrluvA)h6hKHQUt6ikl$fOIzuG>#4)gDvmBEa!#XO&Veg1>Anq^1Z zAhwGw(u=e7mNo+=R@Ap!3>FgG1nJD@tg@iu`pd~MnQyA}v^5h%A76T-nuMvdMF6<> zmJjnTwV^YIrnL$dp<`y4?nA>-mOLdepR%r6&YuO`nXciyN7SL8g_UY>18s~XHk#b$ z_D&w>b0ZZNx8F{62&g{Z!7{3J309~%^Dmp zr!;K|0-+CmT;i@bwzC_2si`sIn;d5Y03at1!fX79Mk%C6jIQii(O2gOd53jvwbYka zB^oK7m+egnuZfjDpP>e48qB2pMJ7S{mUzFgJpf?uo=&RVq{pi2#w;V68#mlA&r??< z1ohZh@t+^)EV5x2SzeCkKAdm?X_+}HK&$=X%tYjBuwNjBO;3=%cyL-da?CRDGd603 zNZ;X=^cm<+$n2;DL@sSM&pStQJE>gwSUR%iZ3|^n0#4*XWRmP@j#Qvh+MdT zgb0?|{nj~9CK{OrVD)Tg;W_vrA%H=#dA zk9zZ!1YBLKMPnk}SjG0zkbSy9A|fub-3J858P7~gAFZr>J_?wyH9~usErCoN`pGh_ z80^h)v*Mo+vEga0M?YF*=FS>J^YyqOO{~tg@itZdsAb&Jn!@mj=G9-WQS?o}qW1`{!jsU^or7rvC$UKn5XyzmOJB4z5YLDcN9ZeJludZ)-qzi=?qh z*|ucRitL7OTuw4>m+X%-Df$G1sr9`*tEXo6IM}F^yC8V;^Lv%&WvpvxT*3>Lx}6(Z z?MFWiG&S>X>36sGQA^RTaEWplO1len>Jv%OJs!dgFeup}z@I^++0!(9uItEl7f_itBA@dl71U6U^O@aj8ocKop- z*|C~AuO#-g`+ob*Rr%IJt*Z>OnBdqrLC4g!Yx8>cbasx{-<9S?QQnXJo}A~89UZ26 z|37YGqqO2YMnYPW_{tdwiiUFEDvuBz7H_Tj_mj4lu)pp}|N6-1GIM)1Br*4=sqM$y zn-!63YuEm}{=o(Q_nu97vZAxA3@)Db$?AHsg9YibsGYujen7D;+s!JktB&GEu4}1& zUd+TXxZ7NHPLyHsm`!tO5Hn4z+ zDb?9AbxPT}^oj|Lq#AV4Ch5ucSzDrs{U)&0nLlVNY{t{Jw{QID4m7(ED+46TR`9xz z)6D~HBRZqe;9WPb(IraiM>}V6ybWAnWdCooEX)tg+8Ds6K7|Uh6Ctj=SI}`G;q>oK zh-M=nZf2k{e8bj$A0+@eRX^^d#E}^M*Z90uaEC0wbryAYPBNfk&~DVOVaF zm-^~Z_KGMWManNW;MZ*oKFaT7m|fWi9eA<$)Lu%q&MJk3+f^HfnIV_9WAAQ|GFo4d zp$H*jIJuArM`&QexV*Qf-xo50&kg$115svxy&~)V1Hh|+g{z_`B87pZYBBQPP9}o= zxoNnO&q-h{(MxIk0B|5aB4AuTJWq8BmHbBB6-hdcX^+nq+4k^`N$;*U8o2N! z+NiZ_Z2@LNORA>Ne^G&=hM;X2oeV#%hF_pMI`@y6GanuBa&agDv1Pr}4HWUO^_2wlT|*J^1Mb?ca&?yMRT+ha^Zl0h0F|5NL_GCnLt}=@m78|;G*^Tu{^}dQD#9su z@AScs)V0<>A*WWD$Bd!e8FS99TK=U|9>0~3nXCx6Uj&$p+`vn`t)4AVTY$r0V?8c^ zfD=u7tRE*lF0mISr}CA5Cau!Y`g!(*fVRSY#H8+(xs6b3`Q4>55g{S+lS@-}FMj-9 z`2Auf-OZgG-E`-`7X=b&b-uA7PCacvf_a2O=xxES8RBUQy^9k}lO&Uru%73AqW&DE z(m6;uX3V5wk5zjPMk&jqjr-TcBQe+gz!huId%;RnsW(D8)KN zxw)9HrG4tk42pQw3UGVilYR*ks`v$&iw_zj-*Q3_>3;w<8mb_9ekcHq=$k|j6k@!? z1s3a^OSmWjKqeil*1J)JppdOfBH!HQFL1!7w`d#4$ z&^u;eL_<(*#EDkca5~FN@i?%O{KMVDxFM>5VId$Z^X`?&R*ti-@}=!X-`?V5v)|*G zBXqjIlY?E~<*{Bt0SQD@7a#l!YV^Q7kkh%|Zh}2aoA;Sf#~>tV$kFO4W+P)Nctqg* zj@Aaebk&p_{QZH43*D7P|2MkJm8G^+;QKwRq}ZK{+-mEb-yoz{3rSGb3ID{UL{0 zfuyIiF7LX_7=N#0u4gho>6?RBRFX66ZYrZZvF0|ud6L+No_{1JX9q6J_u{E8A@P;E z(qYOm6ImuSmk`zYlbv2AuW!MJx}(3oV=7G#t#Ue}j9#?AQm46qs2V0OJoXo9KuE~m z=|>}UGWjIPl(NV9IlAchM;#VfD0N22DKUKBB|)%7@jPkkF7h<};yTXGE@swXbuH6> z(PrzsZB2h0=LnOy(2L(CHNRjU0lre144s{{Y5UCpz?5se9;#dV-X+_Q`Nh<$ zSU;22+SriR6;iU0C+7co5G+A*C>?P!w9IobyT^MV{@Q6)9}Z1W9(EcziE0T+@gWUSZ~e=30d=jEc#~lBG(rxD)i+nrF`sOjq4eH*3v3k{&o9V zY?K;MFdomGMc>Q(=5(gbeE~KR_>KMWkw~DTg81sQqJ@EBVD}T+m=RK$P8Tz+)2ws5 z$H+87VoH6hzFk(v>~gkydh=l!vuk~R=+0A|gg58#-|b#xR{RTJkH<@))g}AE;eORg zTgavnOctynZVQO+6Xq;h^rIloh7lhdnFMEAypkek5~^tcK~OBai{If7snqtm%6g(6 zc(`4u${rhy{-c($jg*it2O31^ADobUNtHI1%;>Ek5^My`#_Gz;-p$t4%TISFTtYx4 zqDKY+ssiBOkWd^C_p4R%f(NzN@UKj70b9R0zL?mU#>MH@;hWEs(;;sAI6NJ3Bs)rH zu4+hKiG4Ro)MQ`Tb+hOy7GBT5=k5^&^}K%3zfq2hmrYUO1*50vna5aSxTg}k1C?V2 ztE=rY23jKEn|u!Wx#dRa#df7iVT$@KAh;Sf60;{7O)QxMsICgDBZG(k>YilliBBw& zSaF|1{DF!G4x(0NhO$j0znk^=ozVLTXV2>8aVPGQgc$j2>Hbm-Zt#LiGOv^0(c+D) zfH+2o<40Duj%>rjn>B)ptO`s116*oHjQMyrkausHv@=pcX-+URuuf?Z^F)M3U`CPU*|^r@ zKcIaewDS~IwYjx^M@#cJGL5l!`|lz3;1vIzn|`2+B!a`3Ej+xBD4RU{x0yv4FtA}d zbXBA3s0P7ghX&B1W+p;1S2?Rskjou{y1@4yfEAC}HdX-@zThz90WFD7%rtM7W_)}N zL`=gZj#+R20wO0t*E1x zJRex+I4w(4%;lP9C{Q;<`9<(t_>pj7H&vVH3FQfkbN%3i``;5fOK;2! z9)I3ud}R8Db@)Z_so!ytW?og}q-J!=z25^FE`I|7>Gs&_KKwhx(-|j7vXbAu03zYN(8tONnpKpn$r$~r= z%6t&e>L?E^kVruKocKv~g)(&Z>A$<4Yop_xsifGUt7#CR5A#IcIvo_Hgqhyji7#YQ zUpDR(WfjdK)z&!)nIwTmgE=wpi&C$#)(nVLDC@Q|-5Bj)|KJeiM!)9LH+VWx$N*d> zrE##_W8o?+R-^CwgL1Ea)Xq(b4!M@5ctbR1ICH&?&Aq%yPt^-fDNTbRt%|q~o70+E z23Wg*!LQ;ow}oc1q7cCU-UzO~x&R^JJVFzoU<=TWDYE)+$F*rJ=usk!SF#&IF&?f& zsJBOfhgnwsvWcw!$C_McHw0^4Hzt}F$(AkZM83(k2&}GNDz$2YPp&q7u;UB~>7_AU zuA`y1`X5VI9S~LXwE=0QQyN4%L>i>KOIkocy1OK#JETDxq&rqvLXdpv&RrVmMVh6) zi@)#hy|Z^_&Y3eOp7Y$Qg%hUFBtEsk*pr<5tP3?H{0@@tyQVdl7M4HPu&DYQ!+j%= zFVfG&8o5Jkc(E??T6jqQA}XhY9_B%ro;~&5mC>3X1@-^j`@sC08Z%J?jJ~`+A zPncn$0b|x0u(8+KHD*d~#P|NbW^EtlW=~WUZ`3ANgtbTPU2Y9%{SG@-m-w|%phdOB zRL>5}_Y7NA@`4NkbO4MgA{YOmmgV&9xUPX1GIk<~GjWJq0~uS1?XC}ekJlL1noN=7x?}+f%+{# zi%DK^rJiHVVCXrDyao!tO+^M#mUkOSN;m4NhTZ8QiSg-vE z`v-RMI`LRSKstwEy@2i_P?B_@9H&bB#z?^vsc>~)2!(ZKiG8=5i(mnnw+20y1AGC@ z1%K%V5{G9m?}NXYk6GNmQGZAHFI*60ETN9iZ07N0Iju(JZ}xtdBh1_3T1;AaXl4yPw1HS_GqRI53L6QS=s*1~IzRyGcn->rzt zAMkc7qU81c1o{2GXPPl^?=`H_B_jTZYZye{f7L+Bz>{^A2}|x{dUc^;By&B#{fEzh zI830UtOndk(|7*aE53fNY0W>p3^0D{UBd!23-%3ICMihvSQB~@J?dtMHLrQcCl0yh zXFKK}eaq5WoV95ark*tVf+NIv{`+T9!xpU?Mt}}EBfzaCjApa76g8E`G`c>G$oH(Y zA_|1=E&edGeb9Gr{}FzyGD%Q02v|S~)So=NnAp08hfM0rbjW@LH@;;vPvJQs=BnQF zkGB#0T+Vv5G3%nWy}Pv9%k!cIfk31I%bK-4HT#N1|%FaJDuFr6sZg;-&)(DxbTZ#WOt+dA=%PTaZ0ds4Lj zmu6gT);nRevX@aCqY;p0ZT3ZqZszP{`YF<8eCr4ZX+j;2)RX?d;{lR zx4r^rYsq)UHLJOrYjUTj%MkJ0JHXWdHXT+RX+hF)Wl~1_f)(nbz;@8}mcIFlp><}~ zh_Xg6v>$Rp(FR_3`=5_JU{0nN*H&2X+bOnCedEmr^~h8>u6nq4GS!SpgH-t@b?B^m zgU_F0H41z3W^>4C>E{3`QId9${3Z*!KQZg=Hp2d@U@Rfvfu&tuO1Lt(Tz~)3Z29^N zOFSmUrv<~p4c-%e;5I&V0~NGgs)msb439v(`NAxqtSq;`L1!l>HHr>iFs41SrvT;@ zonb3jCVJ2NT^;8I)YWHO3*x3(z;76Yk+gsX_3Knavd}3i-5+*;AM^3iw63)oG+}mc zHSbq>(>q)(KLkg-T3iOE_miWpdTYVG0n^04>`#Yx>5ii#QEz;&p_x`$~ zitUB+Q`~!a(8!PH5W~~#4TQ+PH@kC2l-^pDT8QyM2{fD8NAiA#g15;=fm=wWWW03Z zbzGo7y{X^Iv+B4kZ<99H)3A}}dxNA2n~P3a2b!obPBwS_ia+8Pz$YUF?pr~~nJRJN zbVtN2nS=avdW~&>`E{WvcP>XYepYQX*(p26^v3SkXY)XpKaG8JzpHc%CfsB`I7p;1?9Z0yl;eojPB?i~= zeVgPog=G1n;S+pJ#m|aoIjTZKH2j-S2*U-nkrjVxW%CJU<=1KS#mN(!4AaF19a9jT*N_T^_c*Vl3h+?0<$Jyw#W^w;tW zGinK(%R6xQUY!7FpD(+>($tj8NJQ~>{|*w4Yg}y4PS$IaG04(gFgyJqt>V`B$vshIG24!$hHFq)km{*WlNc{E!6gPh?21P)nO#JYU)xV+TE^s)W z{9Y(aAy?j12Ggeg@1ym<^aGgai}(>XH)C~{i%-}^u5c{BZJ=@4d}RoQ@O@cV6M5UK z@aPBQ*9n0uteju}WgVB+N#9$DR{ltDNfCHFCa-JZe)5F2BAYB@UrMwwf83#3#5rNi zp-7ge4?kaY?%LMt{1p3~1E~J+Z9HMt0%)x~d-6Gpb%}tBE2dOGG{=gS)BmJP@+;#n z-*!9$`A-dW`Oc|Zhdz(t=eeEAcfIwFg&TDD-j~VLk7b%L9Uf|o0(I98J^s&@CSX%* z*|@diQ{;2TA_m~j37$cH+S9+Q@;q=Jf)KIjh?z(|l?(o)E8p_ZiQrvKVe_fmzqBej z$G#i8=EJg}1|+Lh=}nsw%4G8gHV!+%6k1tB_nSi2^k* zx$(>q5{QXm0JeMFT+Z)~>;Y_bpGefjDUj#bLs}=YAAh>!@!|BM_L9MQ>w|hLfPgYD zS8x1RME+&$&UHCo+;HnXmUxpFv9abEu`V=PUVJIN*UzEGS6|5SQh|jxEMwth+~F~o zMXvIfh?;v8Cg36?i~}uR^KmsGPbC`JV5lUuL0P5E3J4vvW_;Difb}$}i(RB%kqfwXQC_ROUv&`Q(MW!SS${li{bp-D>eWk^LS3-nn$| z3r1a`Wgjemj#TwdmHzb4Yb+K8JL~16I%(#FGf^un3VD2&3qw0gN$I2tEUYm|gZ=zK z@nkI*XX<>d&WHNH=!1!SpQo%Vj-{aIz42Ca9VlxuvL!7-O{A3 zzv7`LiOfEZO+I-xLcrSA>wCo62jJk`b}5;6Ry#{*NRClVel)Oou4+Ne*D+&m9bdV* zyUqH-T4)Q*u>R?IPUat{V6m8r!g|G07;jK0QPY31d)WL;56!il3z?0Bjm1XMZT=5L zeNt-H!5+rUN5u~3a(0>jA~6?&bA@A)lvV7GzA~V^cqL;x%p(L&m>z+IeU#t(Yb;b| zKHfFM4rkeX`L@SjVC$bkxv!NVKAL&?t2{!~d3lZ*-wBgskK`1r?IVeoOEESZ@V08w-?ijw&L zw&w1L9-X+)3(-sxL7)5B0y?Ob7n&C$smTsReEfC{D|?6Vf~W7nsY;>0vEg&B6b} zG6S#MGLKv9)arXHHQ)M0K>p;d&H0}~r{O?A$$(xoRqImcL?te4IG(Bs({MZ9EH{W-g-`mo=^_bXbgKoXaYpp=ci;z_YGKtkss9>}FBRv)p zMEWjlct*^R)-4=0M?4>{6Z0=skYLueDBic|WbVAN7G6CTY{|=cMrm^$_=Nc{sqIg6 zKaX@Rf8~@mJ(s6V^CXJc$b(iwXaP9kYikL|fcS#_GhI!zw=7OiNkl`}Dpk2}l7##Y zABKp@9UkN6H{+fG*w=4i^Op@Jhl?Vs6()IYgWXsL%V#LWK3fV4Uo!uN)&e^xy>{_* zfeTttMjIhbH=0I9pHh2sOdCPvi|77xm+8g98I48ghSv7U2LaMCyT98S`*WM!Hr?Mj z`Az=JM(MMjw&G6$Pp%g=aM_3jyb4`!L0@;~!`&JBLGM-8dTeu+k1`E<)JK8lnky{v z0==x#vJ$}a!4{e5BEso6#)PAT7_j+NeiFoy16+77ZT_K=ZF4mJZ&L|%<{ut~Q>A!m)t2Q$N4}9mEZ@NzmDf!*} z_p`yzP~Nt*F{2~;E=$m>E-|LGqE&RM|G9w{;Y6{)#E%2j!a3xYWsNP`)Xgo#PWIvG z&P{~T4*K!Jl!{Ps?XRE4pv~9Q>%W(b;%Fu@Zz4Lix$K1T{{;)*iS7cJ!9~8U!m~dv zW$#b6QFKyj&rY=L!y7A7h4~C|ZLP-+y3sr-zx5K#&(HrHa;H`B6_NkvBz`9}5Abw; zxwM~t8AAugOC5=NhPxLLI8H)pBGf?G-+Fsi4+tKzc1BP5V1}7NJoJK zgm7K5c*&jVM`-yccBsF?kv*YSu*6J@jyMVmYRN!V{?HS3yF}FN%C4Aml}b&}!vqXb zGmG$@B8%b93cBGLi^t2UTsJl>?i88RJQu|YJeNIQ@3z1a;2Tg~3o>W^kF+GyHt@ga}0&A4` z$*S&Jd}6tMW^3+soEe;DFaycPUo+_ z8&Op=-^a9kG842P(8{6nNw>yu76GI#T~xJ>QSu}!_8(vO;!+VrrX^DlGR+k}M?Inm z7jO`^3&hdu*Zm5BVe0<^vvL`pByElvkau8qc-%Gi!-(drqva_PlS*kiX)}RuTMg(`*Ov$XsnN{K8bM1QnS}R5+;I+j&?L zl7fMw|MZG(Rs)j$#nL8{F(6Y-^a;8F;-yj)PG+d{XdW1(_2uB*`Mj}JV?{zVTSQ?| zF^T6OgtPl^2uGo(9s9#vU}9g4j0V_;s*{|=C-&++qm5;rJn^+jw)hXsj-L5syXQb& zExP*ErHM?BJ#`J*EFe7gmBf8d*UT%TFg*5z6D zVTGn_jagiFaR9kopCHD4NB7jZxlh2!8_aA5Nhlv%GhHM`x^syS$LMquY+dJ zim7A(GF>S)eU$o1NhQX>U#ud_+~xQ zVNh2^bGwIu_0C`SMRQ$O<-4Cfc@zHGh_`%mV79Sx8Vf>jE_s=OOeHO#m^>lgYf*t5 zgdcKVmmNr~|H(DZxaq-86ZqAVZS7DMpv(Fk5*7U)dDHhx$@_#5GVK#BSdxZdppAyeyWtHdS^aB zV3^)ck(Ctu>H#JnCmf$^5B;NcKU{$kR*M28XrS6?sON0}!=PtASjoj>pK zFHa#bdM}1gUXiWRMbLK0E-6_ZXeh0>A9|XV8;_oYgxj~fcKo60`p-wJe_FzOxRXtx zwg`JD_r=#x6JMK7hL0{Q3izlWC2nR0Ovjr_Vh!vsTLl)@ZAQFz0SMp{c#t?!W&IxsJ$wK|UIJSz%QSk+I6-hYqkf zqr>;I&u~J>8>tP(A&()h*Bopl*IdT_v ze>R)gJ^l(~zWY|w;*%bME11F66JdlF)suMWe_HS{3$>^OwC2^_Iz~7nwjp0%4!x7U*rT=F9fI%Z;3)2*t@7{8B$- zD+6T5wpxV$VV@n@xdIaFoAn(Tb5kjYMpSM(#c^Y}QF%LR%|>y7lj+egv?A8tG|=I3 z4ui6664K%F^mNqg4H_2qK==L9mW6@U0_1OhY+Y5$hpQ~RXLj$rx%ZMHv)-18{T=X- zk9#v5v&@$I44+M^%o6V!=+e3p@vaiUeo2eQ>tF11NG4K^lKji!63Z0{*xw}K&Pz#E zTVdHX($+48p|2e-%A=1Ewa#CvfDS8Aq*Qias(xwsaRWgMyT&{Vj^A7|cVwX)P14Sy z1Kwb3fru?IoBD&$r#Fz}Y=Vx*~06Z$72kbre2*Fs^pS2F|7!9-JWxb-dfBLJv0la~hZxru( z+Jf(u?_Iq7`-WH;<{7;YZfpyZ^E#VYU%th^Wv&~S3}njQ)x`Iiaco;MUd*IG%M(Bh zOX`M-|01W7m{*%yc2_X5Iw<`UhA~cPJ1hjgf1IiOZOPwpr<2DW zsMFEPR=4-M@|Esq{wuxHi5qoe&yoY`;SD!viY@?Xae|4sdEQH?&uVz{Dhy;LF3Jog zuRjf@jC> z|MKbD*v!S`t@fu%Q`ZmRQS`HW#|D0F@{~0qd4A#z?2u1}W4QqaIXEPBLmEQU>u2{l z?u0Vd5CU&;Y+$STAf2rS0NvQ$>m}hjtnhzW8~j+<(;6-i6a0KQKXAACSz()SU5jM@Ht3)2Y*469=$T1;Ui7(1&zFM?SJM=|0KlP0w8K6rE4+?P1R<2$wMj zjJZTiX0quRA-KQt1-bBLi&fvh)kwivcd709)lDoC0MV7iW^Ja}ox)nfv6)U`QeyQ< zp#kOqj|B9Xy*2zdY_=*&vaOT)NmKVDpBgP z{^o=Sk)H7ql3duQ_1!kEM{)5A@Bk`It2+9!PQRGNG$VpAsVd9m!{}eIuUmvxhn?r)ij~lFwEHe1;|{#Cf!r`zrRUB=TxZ^4z=zqmot$qgk zAW_4#$OVG=#}macMr5w-Fs<;2ghg<5i|y;=)n^mQy1SHZWKntQK-{Sp$v+S!ozNP9 za;aJ(6`ID7ql5hPEK^P&f%5;FEWfSmVE@o$-9}w$uPMmCAN#l}I`2e;`0P`%STtVF zRwCCuY*zr)p4%Se{f!3DcXTz?D~W$#P%fX+!Kyf8hq)pEtaiqW=68pP?J5sY{8aCL zu6=%m^Fic(4lBKnD#7YReCYSF5LrSzux$5p+7KXOkF`Dug2|JURFzNGhYFU2-+yvF z8e~{sYw`7rQg{y86<(#(Wa!|7ZN9B*KP8d8#<|7s81v)z>alN8VYETvj|Be4mcq~W zC$zeVECpjL=uiGOY&>N z^2tvy@2}-n&*t^rt}ncs^&YHSdckqoZ-LU*qb3C3e_S+DTXe9UN&g49z1VT03DCC; zkJysE=iF94<*c25{v8}pX!g&?p|#XDoy^wBIxQNo#fO1_7B!|Ge+i^VV=O{ASB^Ar zUYi>PR}U+PR6&`2BKI#0zvw10hpMSgcKn-$2X^PJcVf*Z^TOx9*4Y$IZnE3#J~40vK+8xCcxL%6=C6Yg>as=mnN__y(JDrxl(JNO+S_bpm^ zqWdhDZUMAqLcES3(*0cE0ha0Q&WQcvN_x^UwALyB+4%(n3ZyWu4qo=yzc|nVbB=QAaT|?yy(y$) za^%ik0UC3kw1*Y-m zE=q?1$4mlZnK!&J9NqFrUfb4Oo<3`y_cuqI$2JPCDm$OGZIAKL6RrY9+yA(Rrc*r~ zmUw+aAm0q=jnOGk+29CwvhlGx&usFpQ($@ss*PApm8fUsW6j`Yhsg|>ypZcM zw`vU|ovz}h%7+R(8i8=ln zDu+Ob+@dgGQ0G5)o2mBG6G20lM1LuTd842I^O0@tU4$$?im*Tq~!oMlO@f`PXRp+4nS zB7>0oPdT6mf+X-&g2e?E{v)6d(VC|-Q%F`*!tF9NX*~5So!y=?Z=L%!7(uE;PwKbD)*u!)v#$Wk8>PO- z?@5@5WiRreEuL;^V|0|4^WU~J*wCMB%B!=MtvPXiz5{}6!7|J-O#5~EVZW^N9(}hJ zo$(&~Qtni$RyQ$(Q-Qf)OGMbySJlwxLZg0%LY&)56FjU8~alyr>=`1rUG+H*HsVO%KctLsGwdm{| z44e@Rs19KcOu11M86kLC^}}^@^N#0FCUT|{%3m5a6W*I}9E*!o&IL_)X!;3Vfbts1 z-(sr*lBHF(U5k*obSV#+IZonR#y^hCR_Jz z_~c*V<&=ChTXA(9n!2V7Z{xbBec1qA5`aXe^Ji_f_Es~I7~2qjtFH(HpyXG9*HyUB z2$q6(6)QUnxq2%HPTgw(pEQKs-guN9>0rO_uF(ltYehau#DIcceM_pk>?bgBR+l(k zJ`$07p3^U0Rm}`NZ$HPnstZd{bF;a06l560!$%KZmkT_=4H~VYuDnFk zn5j0OV)h}I0y6EhG;>o2^BuHDxlIPo+y%UfZ9t<5sPkQXtfO9Pk@j(HLfPF3H|oV#TOpKs?=TUPl)oJpMa9(5X)IYx=2cH+sm zy7p1k?nAiz>0CfgHmgi`SivVs0@m&nj09H^DqqD+qrQ;npCCy z<880=FIC_QlaRKkd9VEAlh|?19Yx2gK0v*sxe!yUnMC8jUT0_-aQL4gCaEWnI|@R< zRKF7oUrh%8r1&L9v~3oK?ILPE@isc(J~HA>eyXb}p9Gj4e`h8lCeW2ru$;uY)&7`V1MY!n^G5Hb1 zWKQGRt&86h$GC3~xS)V+U%xwenfMqxt_aD~?Ay9bqn=-DPUN3ug{SS#jFR*AzxaPZGe4#sO7A;bLbv1Q;R0_!bpXL_22hzJFA z@0nB!-+xrNJG?wA^Z|!9v>9*SVTl$VdZY%Hp@=4n-NJy{kYAc9M@m{xCvO@5WOTdf z>-K1%CBuK^zDpCZ|Kfn6(y@a`ojFo448J|sjc>1WQc2*Kp5a{{ynHo_96FXA)N>Wf zO0g;zs+fUCu1!zz+4n@L!_g~JizIC<_rrPO=UD1u`PG9X`RO{&Ky>sQa2hEvA3r|$ zAH5k?5fkS;0F-`vH4NF@+v~XS&x6xhZi3vQ7u?Y;f5CTo7Q7q?=@3hanKy*VDjUGB z7w(0nxcsM=FF$uy_}1tGUVKvioD%u&wbxNpfa4MhP<50riI;abV5qHd9|<(RzR&7^ zTU{jmbWz(J%V>Hq0888F^e?^i=z>m2#K#O{Kqv0s;@;~5v8NH(3J{P(=bfQ@Js&c{ z(Cbp#h@NGgL>q_OPj!2r57KT~ohLC1r`S-~7&_^gQy&JJY}rJoq`z1fqr&0=S1N5i zZT#=n&DM7ouS!o_&@!MnNiQ%VI8PZf!+~Ay^?RgL7n)vSZw4NBXvWY?_TkRhO5F8E zdmC=7M*McXN3jc-ZY_S7(?;BAyo-$8Iyo`=-Uz{znw9F4>LPny>I&(fOJniDjvL~2 z)BmL?T*Cd+l^R_MA-U1(5x?}h*+WR(LgTm7?sclj&J~9!$R!f_5P~~yik z@tq7=D7@@pL8J(@hrHo#uJv9sGllQnTe75vlRArE@TxPA8EDwJjSKi(%njht2nP$c73Qj*Q2)iq#xT*fzVLi{$nh4 zqHo}m-37A%uwIH(yQA@f-xo&=z0;+$&C`6Mg`(hF&vPx7`Fok)m!FOdwp-*FA7d>H zho)GWICh!V1|Sy4WyNbhh5@2*6fcwW`FUc zQ3d$ag0$*l%sPwb3EKW7Wz3D6WDErQnTK3cJ8=OW1S3J~ZHHyp7u_01ts?2)=ihV% z%}Ot8x~IJ{>oW4mPJ)$!wc>a9cphu5@Sn#MtCJU9TZoIW5W@gh5e-F$mb7dJ$8WR; zYAd=GNi*eYSmMxZNKPOApUmz{KTg?Udz1Q=(wFHzFEXdU8;Su5kZqWj3fQ_SVX%2v1`mVy+F?Bn`(p_Kb&Xnx!7JSzbq5-8B+h%f984?a1 zooi#OCO{|1o|3xJq|r^cM|G}DL!{}oqjAOM99PF#dc=EO=POaIwIAtS^M%-e7rJfz zkdmjOaQYa#f`zdDpSh`$sG$kzEj?>gl4n`8%<$lS8|S=kcHtLD{Ff4^Z{Dxt)q7qz z=L;C}ymO_Mck?#sPB9~(!zPWjn*(}x(3vVgx-%SkajtBlKzwzo04ZOy+da)TB4-UY zQ(ENTStRNuckp;W*!PuktR%$iaw2)t~a1{Wfc8glHQUqA}SYU*ug_Rs*p1I_<@$2a#m)!&a- zgkB``KxP1+G@$D4f@w^XU%|(-Y&Qp9!#KjS`c#iZ+ z8h)YyS4!@BhJkmXzhOg;9!UDyE{J1k%MTws^KRkKEO&KJt6ZJkPjuVhFQ3>(dm66w z53@N&o}mW^$}Dka)VYOql(x)MZ$R>gLR7Wkvm(4wynN*s6(qciYaK*~B$KD=PpQFw zUifbG$h5}EigWt7;se=k4FSy$XK&dnn)v?v$_i%m0QWLe&zUxVxwSJojrArH+>i$jKL&gfQznkuDIMr9nJqX4>JUo_nIXCB(e6sXMA& z-2|18@l5git9ByhCRZLwidbpGAvCXV&5j`(;#_v+&jDI_C=q7*GO?$nz6aCT+lZ4i zTIgh{A=ArGZ-eN>l9-Li_cO?I6_x=3dzV-d1HLpG$*!lY)SuU0p7 zr{)$Zmwc=@xukCB2V6adlMFFJv$&yq9yJ7H?_ zqx~)>%$0};wA_fFA7%n;z;_zWw*Mr0V6j)GiO5sABKaFlyqT>^VAl8!pharpGq*JbY|b-G?UM3dP|NER?IvJ zC0JV|VkyBD%Hcy}{P3-M&lwN&FVBI2N11myH}A(f_h{SSI7D6d$1|7&>kYxf^fw*| z%2*L@`|V7M-x!w^IW5gNq7nB~&LscTd^gPlYlj0aB7(VbN2sM|f!8D!q%mwpMa5sv z+Aiyi_*-Ur^LAQ-dh`3N&U2$|;PVY__#HflkzwX*j;&vcUOjJ_E9dH0Ymh8>f8VVb zS~SfnFkg}A(LRqo62GxflYg5hY09Wy)q@jQ_rrs78=0^uhavr7X@m5Q^a&C|AESE+ zif2;moN`4=>K?)3Ymd(Ph4s^esJR-@wkW?OO`T;f4Q=B%op{JKEc%V#GT7u9GZ`hn zj=Yc8*<75*&J;5z5>{Bl?gyJR$u4zJF+4?{e-*oxtGC7o=o!tUAG*oV7<@_InW0s( zS?DJzplGx>z7`iVt$tz`MAi+(w=JA=)z`-2r7e9aVdzCaU=I^a|lam?=KFVeA#>cWs)IJ-|qg!Q|R7<&ztjTc{DE*M2q z&Sby+2{4yPi0wwBgh7bpet0v!GdejlQmbc0LYS>rKzp1)T-KT>on-%6WxuRoTs9}& z?L-(pN7XAg6djEY)l#8Xr#RzQ-<|yvEqj_BCno!xpqi!iGU^TY6A}F;MJAnLdd){w zit@Y4PVQ0AdI$?T^D1A%pwTjH5U45D7*R0jPxa03I`#tzDGBUHbY+bdt%;JP0^OJW zCxW8YV-G)hwPsc!WJ^tLY*&ji6wam@7A=U4;_Bl{%?wM4hKAn(*ZxDpTY+#ScQ#R7 z?+K?2$s4H*+qu*1^4i09yV`K=qlRckAK2EeRD$mO0^#{-cIdjnB~uR}LV;HHZxnY6 zQDMa`=(Z`~e0qCmko8H}8IladH5UcOv}sBElcfyJM81zQw&hk;sAk9-OYe>a$|S*< zq&w?^4%m>LJGz6c0^Q2{$sczuIzCB|I8%YwTR#dA1GOytM|1MW`tgAZiMEf6xtoB# zB*>~2m~100+NPT&MC0le(II8SN=1M039{5%7y%OmFyH6~6nQ1f-A;zwEvJ|3n z^w`I(xqmyyLkOfRL4!2yZV-1@VYt_bB7B62HZIvb~?jkAbkYeDM+OE3izHgtHF3Hc}1-C!;$J zp|n=k9LMi7D1W13dPY2G7i=%(8kE_7W|jn)SNP>fZH5~!ta`@ZdH3!0G)}pG+xAXC z8saCJbkPTz%lHrHNuHs5Da7Ef*4`vk)G?4&;ejKsu<$|7NiM0-Up=1lgDD;k4w@aZ zYf(>#2EQJU8TLU;@$G*p{Az%`lUj-#_Nn<`vsP7UJV zK<3`I`d~nLvzJ-q<9K(;NynL#)7GGA4irisqnE}vd65wca>@#~Uo0xA&qA7v;MGhQ z;K`Tkog*2VGz3N*CKN0JSWleWh(?|hiESLp6EjJ5_ra*GfX+dXtt2V-3WXPzj+Hd; zWc=;$%mqN-t`9QFU*@cfQUX@Xg=djDkh|yGXIaQ|zu+FHSE-JNKWH)pe(R-b7*sRE zJKK$!06mME(-wRuhYQOF%*L>si)%o>4K~zxL~-ajm&j3e`1=j0vsM%Q)%hBeW5ZOI zCok%|IU305?sXN}Z@OdkY*n8cS{jNTQGyh(r0t+@CL6-!UTTpS>2zrzZp(Q4mB!)A zt$FAy>Z49lQMz{zxH2$7@^qOjKbte6}RzU|HXJBU+^);K{MkgJiw=Rbivmtpi0AzpSkrM-+gnZSx2H{aujvgxZ|0G?4fQ+h6=x7C88lsgBqe=;u$}S4EoOO)r6nzM z7VHm>h*fxpu5w>ZPB;wEX@ce!#Uwece&1uX5(P?!%AmhO*!Dogmf7iOm$8s;FjCPu zX}$G)g^C_TnW}Q{@VLk>_jVhAXvQ%QeRVG?=p?WcGGl|8E|MTgh?UCFB3Mmp!o_xo zT25k5;twJPzdD6@d&MVy+IIXPew4@p6vQ1>;-Hy8|8#!yd*IeSYxBsiUe$IM#A{i3 zqv|v-vCVmb*WoS`v(chDyF)L5+9*znnXLw6+{~`Oe3Eods6RuJ7$+Goh+U5GTYl9* zfu16AUkPS!pVU5Wm5)<>kNj*Fw5o|&I;;=g47Umv48He5+Ts^9cc%XVbRmbyeKX?9K6gLsxy8*4xZ3HKK|-P2+NPR1JcU~iZYTPw23+d zKpJ+55bf)tvD%XdudL%{|JAQy%qtR@x;9xV zB0ALCj;KS{^sJU`8B@B|_OIS$z6GvLy@6-KDGW&hl~k4u_!>l@OJ(eGYX1-jo~t_< zlC2p!YZ}1VbaZvr&(#akY+zb5rC9*jYP`k%VjXOChp=0)ZRJIOHdmRvrw=4Wcqg-t zohunBm|MgzBx&B;p<2u*RkvqmT%6LJ6Ii^%mmi_Z5RiQe=U@MsPIARB*Ru0t<#vE4 z@aM~7)W)vMJL@ArQceqW-iWlyMYN};$K@HhYMND?S7$t(C1B46oXPfVM|x*@tX`1h z+lhJ9Yz^Qu@7QxXK!v*zxD+KD1c>?dq^klsfL>hM*UDz~`ZXnYlh$+ZKj2l+u8(Xrx3NS9Ef04(zZq-ob&!IxJ8PC1n zssmS)2Jm2?Gb9qI%Nn>NE0?n2-#~?*od8Y+bAeL>M{2p_)*V_q(6nW{aSf#J!MojL z#XgzZJL1iI@esX(Vl;YmDAQk4$$^~{mj|{&R-u*H8C(PxLu6zY!I8t($)=SlT66Es z;K*9oL)yvg2IR9d)+6-0!%<1wg<||4(qpp>J~^b%ghu;8J0g6-=FZHk6$`vkH^vAq zMo9WKkS4xX$^MfP>a}mzFecF|aeEeQPJem*@g~@sVa2Ws6$L3cnoM@+#IU=yE1{Fj zfYv8AP}=Mu10lG0cMYIUK8BHQ;*!*;nRLAhexCXH#s**^Y|cxX5h1lH+Zc)1baVE%Q2 zwoZPp@u~nGMP@QovML zzz3vkimj&(EbNIoyzstcq1yzr*WMTbbZZqY(AJPaz`sKfv^sPAdstU6a>NgJrofb)rDc3IlUC0lc}?d`q~Uj;hy7W@PyEj%k?!MMG)ceAG1X| z9{nykQ%ITu1ae}(rvIRX3TU0S{G9KQvR z*DT%Vs6cUk<7D-;u9h*fdWT+jK|#FL9tcTAi`b3Bf2Q695--tzed!4>o#KoP?zz=y zU9p*Cu4dv+BRu(liU+V&r?_Mz*TbuaAT@+>kmEC0EemS~fWm{a^PGlyWOlLB44qXb zmi=oWpQ}93;TKElT7M|zGP1|Exsl+BNkSbOfOB!wzzAv^onQ~*({ulDZx3o*ctc5c zKGJ0;vsQ&dHgc?`oQOD8Z6p1qvvvIyXb2@TE@(g?cwa74o7dJM<4}rUI0f3LefB&*rM%3X;W{P zF!H;SwIcdOm-l5Hb4ID`%GxKK5paiB~^b0Euj%OB;v5Muc`)gXRY^Q z>DLR1P7H=wxL@7?0Zv(?=bmKEaV#Uf-(fUW`_(*zFT>_t&s{qlAj@_!JyptBpaTdD z{)zxp+6c3AnP|%61nsr*w(KS+0GZ@d$FTavo}XJ1uU9_lj5^4HZ8)ex484pV8X(J%&`??vSWzTY-8cq&7=;ortJcBCK@YfA^{@yI$Y3s zdepBB$Lp8X^vNhXLOE$ELkmYorvEe&j?Hk2!`D_#NsfdT>&>q*( zZhu26_OG{zYx6Fwr$`b3FMaS@zz%KqWL*$;{<5Dkv0*ONg67m`m&bmVU8CjJ8nlb*DD=WS<-&OL zaJs8Mav^gG9wXUm#Zs3`w`X@gSwpjBAB=&dUiW|LAjpN-*Oz7=9KcU@=l1GeH`EF1 zPcXl49N_b}_a?v6-edwo)yN_dB zhviX}FqsTJWQKXhT?^0q=N685#-0EPI9ZxKw)m_g|1wf?t#5$ua6I{i&Ts3xkhg5* zq{c<7TveMno5SM;TNmaGnEEoDVJCa_}AvxUhD zbd9{ZnJv$Aql)-Ll?!|BqoE3$o%V5+yC~fSRK|N6iAGopo+oQ+0$KOIc;_3g7;O!` z|IhzNi?gMQ)R*;m@&uP22uJSi|8aCy0a0~p7#^fsKm;jiMClIc?vgI)?iw2D1_6=o zj-k6Gq`PzIjsfXD`#+a+@$K2Q_WJU9J%p;nN|Gy8*TG*tarVKH=k79S>CM}e2wF=B zkyXn1_!T#~CSqNj_4p7KbtbmRF=pl1rS_EnzD@g9U2j=!zCg4rwSUO!s~Q`2_G#eF zg6`Gw>q|i$r__=Iyo!J!fz&d2E#_B~EVg9+tzz(3l&n}yiB`ag$uYlcV8|16HDum5x1jElXv@{7e=64#Wta>uZ?iqXZx|T1}5QDOF(&B5_loxlwZOP$j8^HxgX}gFV18 zmsAYS8xPj!yGf0SFPwCK0-i(oIyVhtv-sX>}{cP`%s!HbkL=++(%k z1wKC3RaXS!IO9xiIBpy5S6LK$ph2*~s&7!zdmd)I(XDN3n}r`cyF#rApM_fShpd@HFSecwzY(XhvZDFsq-+^1w`a2gBVae)siQ{&U9{hbof*F`28* zs+HJSt!7pyAdE}?px7})poV<3O>(R9MW%6z&+v=e{%QjWbz*{XGCv3g}g?Y&KRTd9v2XWB7=dzTtB53?2rv+j9BoFH+ov3@Tv z+JW5x*|2{O$HCQ6TJupW9AOJW(#Qv6e4#lAf+MJ{;^;|ZWbzD%1)Ro%dJfkw83C!t zQkvw4eXaK%hEuM=s&K-tHEq?9A*YWG3AdLtr#SA|Uv~Pqmq>^Lx|ijcZ{OZrFMjnY zSL1L2lZHbh5MgVq(r}lyb`cwhQUiBaSs14kBat(uz7oDjpm1^zg(8gUDL)2F!N-zc zWvuPn5o?DopU2;@vwY(ZoAWxxgHDoh83+z0uF$}v6z}7>1qm(72Iii)%muu7Rk{rL#}C_F=rFh=nHx)amG}p>%1ye$!Pnj#vgo= z<-w}4+=DTj40R#ZjD2$|t#yS2du=vk3tz_kp$(;ZPw^dgj_nkW&II8?WIP#2p=0)l zf_@g{2a*idoI~?ETPv%Qr%3CE=%mDCP27qYriba{`T5uKmhpVJ&}jsdk`mnpV=-IC zA{vCEg@GCblqLR?$XsM(dd`fJIRD$Pz)2}bi8O|oB(um=3Jm*|&}iImljn6I8G|3gMS?fwFdQ6=N*6)Q3* z2Y5WRFCuQg%) zT)oTBr#s!(CD78Q)>#OSH~R1NT>d-YzqRK2LcuXBg-~IG)l!L8U)_}X6qK*D zV)dzKw;eAsoMiN#Uh6Z^T|tlgObc}H6-aYW$Vum#)E8_=EM5Ma(x6pL1Xv*@0DAbm z9JQj#`i)?RL!pxD3ho47mcBq_m*e-?pe&zKHg(FVhS!8<$36%7)5(*a{E6u%gfPw% zK#UgMc6Jb!ZLGTAce&Nlo$5sHn1D(~_o*w7ny?@i-Vu3*kRry5g3yh%*IfWb@U3qAH|WCATXf?(o)W?fScjsYUK( zIK`Ak|J3J0vbeI;v-&{YZ)Y}DW${(sv^dDy-7Kv2&Vv!;-~#m0fz%4=qd&FNZE`f^ zt#{aL6~ev<6sNo8-b}0b`B<`2te=_asD4|Cap@zJJ8E`zXlz8t(qkbqShz@Adu_YmPqyAoZyL&4d9#r(%AUg92s&g6<%J^=?Z&m5KS#ic) zN8QB=3G>$hUl9h1I8Qjolv2-^@FAm&Psik=39V6YsWRaR}1?f9RHy2(t|8mTf}yTomA zOan&;^mgBtzVEe&L(ti`o12IMLqyQPo9kysyPA)@1lOTVlb_Qd?I)ZHB4nxJyd}El zBJJvWA7_1fn$r6Z&ZR)>fVLUjhmWYVX<#k9k_tm1CPc&)1)r z^cj8}Dx>M7YWiQ`)h8Sd&EvCO#ExGjHWqdMm3_>T-lug;HoDzV!yM>B9B)xKpI(S) z%Ba3fs41|0^JnKhMN}#&PWpP~@t-3T((;g2)T|y3v)dX}>XFp@kL3bwzPi#jHu!`& zOK#1b@)bQUHLE$s6ClFURjcLk*FIJe}W^^1CZ&-us!~Je;L={h4XYI6KewmI^elo-HuW$eRA4BOBC; z3VR?ptF2j-dpsOK*ggB*DMcKFefE9Z4{v>*`U}{OriB0*83at)1zk635xq%Dh=cy? z>$}>$W&Y;F*vOHI+qQ!WITU~8i*JLZ{rR3`TY-#QHqBpp(6E5Vr%xOT$Nl5H^7jBY zVT9bl8N&1O4TPI@ubb62$lDtDQY}8h`pd9E`nWD11PFo#8PrYglppW4H_KFc5Eh(u zPD0A~oROFgi|KB-C~DkrJ(~Ob`=%=! zH9`{PkxkCKYdq~SLtlP3Z#4Y!FEI4O{m2Y5;C%JJ{fGT+<(*XEvJKy-is}{BLOqn& z2Bqk=wv{t(jkO%>d(6Xozk*IEj~Dk|Bb^$Wo7L$awpY)Jqg`$Y9Z~9-ky?^y#ic#! z+Ry3=Zk6>i!ziNm4P08G{Y$w7H|n$XXKcW&ctgb#1c_z%0#qz-ucXEZnJnkz;PK!d zJuX}S?r(Z&M{REpC%w|KDA##!xrpYyOk|xx+5~se1uD9%2pE?t58WKiI@&em6t=hz zXco6#J1>eSH|XxymY<~`v{~*qGV;yx`+C5w4!4oZSpZhX+m?N?{6T6!CEC6;ulJFU zMgG;QbO_sAZliG%7r(08f3|AmPr~oNrz!P$WU|$IOFN36ms?XeQ6FS)55L*v7IJ#D zaDQJQDSMi8QDi7 zv8(P^zV8>aX1`L*`t;a23BOf)sV#hI_(Xr66#+-(e}bA}XqcI;?^@6&7v*Axx|Uqk z`jkVz`$LQTt#Dp_ZQ|lNwd(=_r9e0i*u2{jRjf-LqnF*cu~UJe*j8C6L`(&*%T7Sj zJj`#F;6d5p5OwV+bym%_`erU`S%Tk^7$DSGv)fhqel7LP8=o?36~R8X^WBR%N5z}N zlrcDKv__Q|1nzr&RQ^}0QM{HBxLyU4X=0{lxs#shBl zvv5w@IyHjF>8xF*<2}}MC3}eG@yUK>y@XRb9r+4XXIzI=jpD*;DK}2rMh@n3E|c@E z*jO+k$nu+xkCRs*%<3q7hyQfeVp$IUn>V4@!WQ>$Xsq^GTIp+fSCd7}NLWPuK964U zolN+t8*a4P?W5wu=NquXVb=nTPa*9;fcZuiekxdGAFAxhj4W5Coo~w zbszM?Tru#Lk#8Pi=_T;tc#EJuUO=>AE)t}PD!Gj2Rc%iJr|rL#p<^gs9i;0neUwjt z3+OMLc@I4hz7ZB14q?2q3XZ;2y{{o|(7Vf;LMTL?7V+!!@*ah*!As1oJ>h_DblQvg zm*Iq}j+s|*eU3Cgi2(6!vZZ#6ERWfk+k+n7w+|mG&{gG)Mk2#*g`En}A!F-- zOqFtKo9umuNWx+`@^|{z3J7sp7XIt(L&?aws z4*0MZj|hz)ILyXwdtk+O@}NSoGi>vgKRm6os2#*Q&u~7(b-_ z{!U!SD#@mKczkK-Ck*=F)0HS*rbA`4UDFhMjV-nEwgnHURF1!Y(RUpCtSxa`&{SgH zWZ!)aHz|!SCxUEg8_Xm1*x|&d7G#2-(1=la+~JTBrsG}+sE-pnC&_i)9P{8Uv(&`d z*Z~Pe9up-w00;7O1a6Wi1>5cY+FObw3a$98i#~Cgg8rrr#+(Tgy?<|DXQ_E-_Efl6 zM(NtGpBD}`)cK}KX=7ahf9X*e_z*0&SA1L+0Sz`Fuz0oxLc=$%(Zv7p66JwAiV(a0 zo#E9mf!!-D&|&r_#8b5E4&odRL%r&=4V(1X0sM_a!zO-7=U*FcU&Jrbu$hJ73H`0; zx~e^N5#kPuA39zYo%%%(RiFM5QZWU6Yj6>x$BeO&K0So(BA=6c2HiIxlQB6?@-ZGa z#n97!efW4zO1_8?Kw^RfX&R3t8FmcY(|n?M6B=$= zC>rE7L5gxKsoQa1mV127Zw{!-#v%Hh;nJxx!I-M4hBWw7E4L@&jCDTgJ&)gB( zurDgU7;mnHFuAmTBOIO?3|#1ZPCWYC4jQPY{p4TSn_OQaRJ{g2Shvc#-IxrmY)KU( z7dO0gY1@wTtNm+X?@N+TSaaKw5%eX+GHEOqnYjJbQHpRbHVj>_PKB|Rag#ET3pQX$ zfzyQ*ulC0Cf$mw)<&W^WuBKIULaDp5vQSrxlTVsuVg`7+@o11QV z?N}LI-8-1AC4ID55Ql8Gx6Y9&M^}J>6S1?vDBY=bIx-`N@$}0NuOQE)w#M+M`3VJw zawpGf#I27+1qH@0Cuo`rcZ;%9i`RaTzY?l%zF0q>jxazo?>nXqiH1jPF1_gf%Ti=S$=wsz8u!qVR9Pb-FbjA3Xm-t8;6%mi zS5ibUVVJw#hO?TF?3{T(DI95xM`7U=4@;q;`EG>giah%Y`e&@TsY_ifKRK6Kuz`mn)zMRpLvxgO| zeR*!*O#+8pbJ=-}dsh3tuR5Db)C=TH>G61+7S8FBeM8JlOH4RP!sKw_=^-?^dI!m& z9kQ7#gN^$>=uA2r(>i6KGmUo#lF77xN*y}PZ=cAYAri*gIa~GmWSqC`&*8hp|GjNG z)A+Jt-8LD z`$^kd%MuU7cASg0%=Yq@LS+nNE7#0lvsQCuYXKS~b+&xFM$C@IYD?IJX=Y@w?IAQp zLV1F`n&C@KYrsfT($n@PkbryzN`Dw1PCDl|AFE1=t7BWr%TSbGE_{|1x*C$QH-*}Q zX;b|to0#=_gZjO3wN%Qt+kV6Wx@629}d^)t%PE2%lI6$MXImv zyWHNeCGl^R;uN&NM z^mq~ZJSNBjCD`K><&br(^nt`}*&+i!ef4zmQ{~_mBCB^=@>KIGCvoHHd4nxxw{|zb zbk(fHx!(@=dvh|7=_C(xF%IWP1Vo%_?nl3DFR+KgnSBW=>vest&AOO4O!O)Ls(Cep zv;L8h4oIhIBEsZ}62aM=c+EZE2}7zDC!RVBOb~G1*bQf?}Abaf;$Ez zdfL!0h}UTqiuYac=iid-j{o(6v(V?3qh<04@;q(wGL3VVyScD#?@? zYC7r8LLy`&>mPp*!pmp`1xp@TTaT$|1!aQti6KNFrtxOGV`}ia@Yx&FF=U!k2rAAF zgppBdUIlQW09gde`( zY;TVSPDAqg*=%_3*fS4jPq^u8+Iuxx;uM0 z2|XRX8U@tpx*s9g4Ly3G!9K-i3I90d(9xYO@F~L_>r2C|ZY5QbW#7u&fVWaIgR~b! zUn|UH{s9bL$k|2Uk?r;fZ&w5Ce;WQmIfjde!&{vYdMM?@Z9EuRv->t-DVW$cV&%%v zS)11-mYs@-eq;g?vT;;}Qf2OeXV-T|G)aXbO(#1_hAF{4D13Cr(mM@aAp zQz5-q5~17R)7GZGKS8}(`*mW+xO}E{MuA*!i-SQ}BWs0E@h>+h#!jYdyY?IEIs-y) znfl|w?8|NF;If>Tgxkr{2e2K~H}3@if2_W5w~*xNsA%juum=DY+%*v`&4SVKS7vPY z=&yvu(8TtQ)Maqw?TW|ZMssF@W*G4@A)!w7c-h?nnD4}R2a)pb2T)Mu#I0by>Mf*=BSOal`~YL z;-Jan8Q$QtwA;3)@19RP@Gc8d8sO+<>$_NsXKwABvKvgZtkQuXjPRF-V<-tV2Ol?V zGb6j}%GYTdk1&2yc~H!?XiYAsUY;+{`4w*7)&B96YYOk5v+_$7xR<;H#%or)sgK&d zs8m_RrV`uuiz)F>QQV5OZcc6<9jv^N5CE~%{ueJ!6G4Nv`4N_r4IcAsjS>^p677B( zr(8feXFpT7NdL8`rijxqM{al0x2yTUDs~OF zJNsG+_g^87p#5=f4(OIePbC0B|)uC>@)8CA2(Y!q1;&ZkyVx|#}&BM_%`;VROy*@zf0;ue5CK|8#!7#_9{Y$ zzqbxhSKqcz$EI4bk`(MQkO%%C2W0C4`LZ$>F~};Hg5wCt-CFpgvZ5F~g1-IYD5^_c zxWCfhxys7>IDSEg!Nt>uk(E0d>EBeMyT~9a*$f%)7A1WkZA2tL%{+7UVn|r!OuaK~ zD0qkT*5`$jdba1PYj)#rU6P%e!k{lt6$>lFd3ml+##tpySu9F|} z_)UBsZ;a0H$a^4qWGuDYyWSj>(|n61;=t(`W$w?v@WGQ`^JCD9CI+8rsK@d0{NIxi zIZPh*rB$c=E|Dwa^W^I%*7%S_zOV0Ct3+e0t|W|Cc(A>lo!pdxy7m2J zG&M8#Rnr9eiq~Y*S;aupe~Q+)tYeg0=4%HAj=AZF^`i(#a7#0E7MxD@(zL2ZQUR(x6q2`W2TS2LFz}4R9Sj(D~^VLEu}Op-8-WwoV!QZ}UdM z{au*C#$NxG(zrL+4k;_>7LAPxyg!%12Xu8;Of0^8Q(z=cFh%fl)Y3aLhPE}ia`UC> zlO)%CQ#tk~#>TDrqA&gO5*7|d$9T*>&r>)xHbq;d30M{`gF|%E@&id_o*S z0<&o?D?19eUx`5ULn`TekE2cdjH680Z6fwv?B0_}D>kqT6%(&uMc zqQXIio=TInl=GxHOpD?GEwuTIO2DMwTXvP!*!`MCG~w65cAhjKlc?Ydp5FOL-t`81j}7+G|)BR5HGY8?ZQy z#6mY<-;&yF2$V;Ks;Q=`>}@}bTqZ(MmE){l_$R(fTtuZg@pHIso4+hF_MTZzsoFso zt^if9nZ<*QwH5PL?c#2TRZ{Z%V(NH&KrNLiGB?AlQZX5?&k99&%P-IDT7O5?RP*zx zmcZozR`{xY&k;(K7cqzuyW6*$U%ZOvXuHA2OEe=vxDf$%o%=|O`4Lv^w?zb^w&Vyq zY8Z6BMOCtX52$@kcm5pf@=ZUU+v;GM-FR5FoAy^_oBRa^V*>&EAeOtJr<(QUP-Noi zNHm=ExM?Gp_A+g-a8PjSjqD1QH5^LsRZEvzY%=7ga1S4N*dK}Wi+NWJn$<7E0SHm@ zMh7Dxx0wCV{D3Qq9FNiXd-V&jh&_1~k9n$@8zzJVl3g1*7R|(HtEm=)GT*f~=B*x1 zP)2i`4v|5BZAC=PA?aWr!^b0tQ2k~7>62%^c;g~|#+2tk1u*7dP?$$Vue=x+0?b|! zAlKuc#vX>sFNx`1!xWqZQ#m^#6exwCwAf@wrrHQTuSxb4I#4|u_PKIK6+A^kPK*sBJOM&S~j=ci;ZV(em z*k!Z6j}gsbDtivupV-I)B<)`xI*8$FP4Mg_1+vOEIEk-Hyi`^Y$PFS0NkSzP+p-_5 z=oJ)w(l06dVQIf@gF|a)h0R$+TJj|M>ASrZsd(+rSMtMT26aZd>>i*k&Lp{e zvZ-Y{^K@EEs+LKqp`A0I2a3?UHT^y#TvMBs)^JHajfe$}Uog)6aEaiGv7&A-R`qUT zpPMg42e%lA0fe;fiJ=GggD-0{Buin-fNHfuw!lBQpRdr=vzfEs|Wl#61Xy zCs5iAU2Wsq!!;da8n0+l%~lYZXvpO;1m&5STP{co=3>FFTiAIQ8$V@yXb}`EhL|g1 zes~A4z{csji8=C8e=Q*ZKV1$4kjLe0xw0t(k!>PNhuu%1+WogXdjM5dpnTyhJzPm$*|S&O$boS6M(R zv{w#aXBG`EFS;Unr?9Rm*(1EH#a$e3w{Ex2QQo#+kn|h;NDYl%9-z7IpbBg{7(0YL zS0nA=3S(|nYxh2D^i1`9aCb3B(|I~Od0_n~_~5_OD{7ADK(VWOpJ@MaVXq#6?KAlI zjM8|$lhti6(Zj=j3P`zdBSvQo&?uB0N=3C?KdAbdOeeQSe9ZWto=Z1z+^Vl(5Wp8P zRXb;@V#T^8Llx1h*ipJU?ix_@X{+Vht?zD4QU4QG40&6iCKjqe z`J^Ks5)s=Mu>QYuBsfu%`giPTIXX^@hRot8mGfnDq-kEsh<-YjrZ&vy&%ey~LGRfZ z!P)NO|4k9~UIQwQg8+0}BcBwvy}SLT%At#w9j?Uq^GBFcwsCNTN1B*((@<3xKU=N8 zhrjzoyjAzg7vscpaLlERUkH-w}B^U(86cx1FYh&?Qy_tW*JTxW2;aP z2h+pfodw6iq@|r~KqTj&O5f6oWU~HcujJcZXQrEUSxR457@Z- z3M-_kI7q#1=ejqXcEyqrYfCVC5wZ71xLk$oO4L)Bu8A{mOPBhfr?f^&y+JVmY;?9* z!rkoR$T9rq)0__v)*+^kgZLq@v8y2@DF<_$;}IOh>1th6IdE(O>3*dSK$Nodt1ZgP zw(qFTf{(_-ZtHeF@u$x`>4Pe|N-PFjY{-r6!Ni&I5kMLBh43zkixwfmwjTbAXXofw z^Ij!W{@i$1S;njV!eGcT>Ii?H<8zBFd5`A#w!I6x7j@c!*D0tgq>lwYkN_|N9D2P< zPR;cOqNZ&iUBu*RccOC^`^_a!jfx}5*)-l0*pl2MW_@TN!JO$a-eH;u@#@|!C7nP6 zi77UoEBv)_@Zrjt9*>wK;}4hR8S)I_)!z6v4^Bo`!7dR4*_08*wRf`h-nUhqa{4kF8Nyk~b?j-ZR| z`SP#!{G}9KkXm{x;#fsouW5n~y2sdl%0z$rJUf{G+!*2Y41tj*HjJD(&;zNf#*x*H zc~=Nh^yay0K1oU3FG}kWjgk1M3L(KC&0X7E5um+xv9C9fn zuZY>Px=J=78+>3u^}FC$&H@Pv&XHA=|E%#SQvkh@+V)9WvE5YMqy9vT0KgnzY`3wA z+gR_24}o_!%i&hkKVNS5iVZ*-49T~Q=T6qgZEwsZfrV`vHGzuAG4|&M({urEN}rhk zuMN$MB`})Vi+|UXWhDMfcbn2t;DX@utN6O;V9>^C;Xo#k(W?yDKuiH7QrQyxZ@u@Y z=iY30dWqNhl!XTe0yx1}7xJxYv2xZra^%1TY8X70D^tdzb7d%CGtJ~yA{eclovS9r zW7&D)WnZ?`tQ+6IpOz_`8_(YDtu!3C@KyQa15iNGZp!4!KXaBpZw$FU2i8R*XQ20j zEyCEBr`q+f(T=+{8cx1IfO=oUKorjozgFR)_P)K*ID_#`2syTv);J?j%vd*R1JPQ% zK|jjpw&4hrcy0I@lsE#}-$^5&yoC9cZ34I;3*D8LEA?TCA^CGi6mO{Be<;GBpg<3Q z95w`&CDJ1-KRAeSjNc^ju0d;>{uqnzJ9_zT;be?Kd|Foj$_s$oor66qKgu{5v(d52 z+HIpdocQ+x+8^qk>YNv^l73Vh=o+gIImVjD*zX3r0>@Afj>T>m;#)hHHqGG|eO(JC z0>J;8$l#kO{fznJxiwk}o`pAiLg(j}$Aq&pot5f!P1MD}P*mj#&L_ zf5#mV`Pq>=&pP^6+_0fzzdr?%|BO*+W4)TKL)b@BLi$%p?mK^t)4z;L^&4LSe6G@ z?u|?j)+doSl}mARXX&Os%JEV&?K)-73tU6_9L2!M$=9(mJ5t=XLe27~7PBn4tCX_i zvN3M4hQ5~0+(XjqxvS|BPC(t!yW_yO>OcG{2Jw`4VOp(uwojB!iQk)+G<9O0U$q*` zdiDV;NIBv_O;0ruj^V*?Qeek8qg>(Q+8S%7I>g4|rx@1#4vgLl9Q7i5DP>NuBY)16 z?re8Fgv_ZdMV6Dk@ymP)n^R#Ht1Hv-Bb@*zZMOp!jVB`@$*SW2RXKi!_thsdbV(xy zcJ<{a7TSGnzRNM)07BJR0|4(wIOt?wYcf}|EN*mkezh!eb@DaL|2X1jp>*>%UMkb5$TsnKaMCT3(C&XZy!C z<3SqxS#*6kJPh6)Ph>Ho&e^OOv}atSE>28`+0UG= z@hm?7$(nt1`R}~v!%h&TjtLiQ*`ow2RQd_7f11sutf_|$R{~!OhuB)82B=4e5TdOlj zd`;dy-g@M$6d7l;??-5M4*xvQ55alIu;W>%x>`@#&*Z)tA5Ee&F0fV z->?hJSf1IEG(~Zvsx#H&lU^)g2Q&^tbR~(sp&tpGX^6n(FvCOdOOITeSHDF);%Nj*xQI ztZRiS|K;(s$9L_3r?eeiHI6~rhTleVs4bcL?8l6Bhz{w7Y7v8;mRh3(t-5m*H+#s> z26A3`lYaw{(kIhP8bb5r%p(V^NZ%t4GB*A?uDVrYAG|58vgG@!5I=KIb3F%-RaCij zoe^UfB$=I~seX#ykp1#ouhQf@?Q?C~dy+>3?EA1vLX*KNWNXjoB%Np@T`-q8=DWe; zP}SCXk#3v$Q%+1gi!YOZ+NMN^RDv#SW`>o`s< z4GI|h$>_UsJ*8(?Ge)lT$w6q=u5}8R#2W0%W4Vns$bS8prZq+k!fN6nW4*U%zAKd$ zqCm5wb53^f-KcgWavY~yeDY7=Z9o~IhgSbW?g3AfcIB$Td(cR7((^*#Ebmx!X$lw$ zUTf?T?hx{~Ee;DAG4RoXPrW4XFhk*pW>;M7enBU5F3v_{A1qIZD=VBh5v)?^{d9Xj z{YK!;zKF7vFH@j%2l6e0wrEZ zK;$5yGDfF>_1`s>(#}RNkZdXA|FcyuE zaUEV%Wlw}w8kN?-t~2XmI6zzJm;MTjIo({pur~kp-Z+ouioL zwt<$k$5!j?Cq=JiIb$|#`;8p>fQO5;k@^ilVMN!2WSETS&}F=9|6#Xkcn8jf^T_=- zs^m71&J}X&K5v?;*HPJ@++(Nn<4%82p!c~m^6$4Tk0_%3F}7&N9pXC51-{Q<{wKX( zKdx{?`YGRf%yC+>BwkngldC%ZBbK?evW;njK0afDqCh(w|*9yKdX`M`}=RLN+)ZX7twNt8x6Hw#3E$*<6VyGu<)sCS{bCfw)f}g=sm%98nM(la6};DO>|xcuwCzCfCX!MO4OwEos{*iE)`JC7Qw=YgRVVA_7akT+X0KE-4rcXdUMJSz| zpU3;$LDTJ5(;wGamea~_xcvHqT^pGaLdBK_4w?=CY|3*$G)z&t!Pmn~chWs(N*4IK zkO-E535e>U`hQfgV?IoMxU#~8etEp_mFWDuLz>=1T)b>@%7Qp%5Bk?plc6|KIN#aE zw{n|em=&$?i37xb@ED1i^Pz%&c4ey6$k~m*E6x5IxpLk5J>a@{kH)#!V{*!Gp;_Lc zI^c5j_1EU8tEO2rNJHUMeoYd-WbHmdO#ep4QDY~z;e&%TV(mEP@8tSezi{+N`wJGL z80ZQZs4{PPP~4_4@~!}h(KF&HEZ-^`2~z=%fUfE8K%1pk!y0p=^=&tsOO-%kFG<0 z9fP{r8H$Z2-3PrE3j#o)vzo=FQyb}{XjnrskIWCrMJ@ZRp8=s~bHCGS0^=Gpor3sLt|FbVOW z#ImxUw&2yuNfrmp-Q<&$YhO$g3v|p)52Bm_wPBK`iFS+@bWGNXz3K31@pE@(<@wSz zr)Ocm8me*hFRKymcTR%OB%In@GOv2QxF&wZY7!^79HA5jC6Vvu?gXT}1~Np&ntwL_ zencFiZ3vNYaMMcUMf1!`ThIjeSs@)Gw4^yt7eKvAio^<>(y(8dIKns9*l5ITiKm!3 zx&Y>$WQrBHneutE`Qb3Gx+jG{f*Y!RxiszanM3=$lk2U;LVbJxx6`&-nnSN0CXPX$ z-%N^!Ih-XMYbV8j{}cdvZt9<;>TH=_Nob;&b%{HtT6;#&TQbrVymBbJZKluFG2QFI zzdd7OOG@83q;q|D7V90oYf?kAWD$F?Fkd!ZfQUBjo9G4#KT-PEwR--<8kHBR9h#V} zS+>smi3(5^_@yT_TEK4A837_5M^5os#q(o=e6f}Z|un#IT1I>JFCkdVMz?qI28 z^Cf4ZOe1UutIo#9(@>fHP(4)Whik8pz$X1DKE5ZrpS(>+H2r=LHe-F86dVY&IS`o| z>ZPHcF8)KjC08_$6ZW;kwHn5HY3v`X(NBYa*aZYl@WI797)P_b(9q~fBtU5+Q`H|LO zTa1*q@8HcBCY#X~Qx16ZnR zebNzn4R_t_HP#SN4u?oZ^_VAOj|k;x;{vY1;VwtH?t_kP+UYf!(;z0Z1eA=*`4B`a ztG6&#vPybzxzFb?)2gmg25#9AGc_a^7NY$n8L_Z`>pOa3j*_#Cl%c_{X$?F<4qbep{@Y_ODzC!(6r|eA)Zx6VA zH%bwl2-AG|*kn|Mi3GJ@MS<8=P!)zP%*e6r!Ft>K<+Sj7+HAn^Tz zib?&UR#xxm40Nr?jE8g%Emxd2E~7FzjTyn49ggxmBB!)LZ~o1dILS5!`i_k zg?XV5rT$q#B9W0yQ1>2wXDL=sx1eJ%Q6k8siOY4`lP?oz2EjM}eAXX-a$JK%z%ZTb z=U9PNp}sJo8!F@&n${}HPlbcmp2zbah+CZt5I{o?o9edWjXF5k?G%{KaiiF!|2`D{SQ zdYUfwp_ZB_3~=H;635oSIXK@ES{A=D^LHN>FXYS+6nZT8{KjmM2b$mNCoz#m>` zQwBwmUR%W2lsE~Cep_;FzdX&nLz;tc5c(swZpuyk_Um&evOw;N--_=x;j!_Eh8;rMr<`ipF6~tgfZbHakQL1^LPvV;{0hENGc# zvq|RV1p}6Fl`}e%al`N6iJC~6PBl4F5e%=9w|w&2%Ew?LIJHPY;CA3rd!(29X2^>G z6`t?RrA@_16%jaP=9Zpb?k)KaLR$xDqG<kYiI2zo8|WEPP)m*Ogr#*k(OPAx`DSGrX~l0; z(miGn%K77M{sp%$POnn6opQJ|VC+xI$POROf5$OX8f0Z|7svM9{jzR^e(u0Fw{d7E z)9_$O5J;+F&H8h^H@OoD+t6K&**+C7ACp~ICml9a{j|d~V#0|At#73(^*cbQlD_Uf z`c|=9+xb{UG@;lnmw*=w`!XrbhF%pACiYCnAS|Vqc*L@-ZI45zG(3!_lgD%9qJ8GbpH9bAfv3!>MW(T^OauC}>ko*roae;V8TnHf-1P6NHY;xVAqrFIMRl;po%v_P%RZpZEUqIZER0 zP1D9Fe%^zin{uC^D;#QaDKEDeIdz0xUX&Gi_45A(|@%h|NnWCx$bs5QO0ZE z_%+u!>F010bL1HtOTDN@D#UOJ?iPXkyYadCDv%vbw*1|T61!qo7pC8UZ^6{RsjwP{ zeqz~9k(N}98B9pfZZTW^Efcf;S(F}(+%jIuUiPsCm4HA7gVHLNUiwq=@_3u&ATcJV zm%u%3`iAn;Mt=Un#bKROLhEa6g60HT{_2>AmcgNHX<7*49kPkjI{;?k9(sv}DLPtMkNs}v3C!m`K;)rz^>f%S-OVmyw|M$ktCx7Oi)_Wd|Z4IIqSmC&L7T-X3L7&o43HM%g&vE|6km`He%{Kv$B7OB6 zYt5nB$X1+s1w5wapA^l#=k#q`DtLTJdOY!hQ}pPcS*k$D)@(;TL3|kBL&dA8RF1dB zeY%Dvs?NR|e-P}U-K_}#N8}XOb(TxqC2tZ{9B9z>1#Ba?u&q6PS*@#|?; z_R(YxN8-u6tyK-hrzVLKYWYz+0rU8EL{-L|Z)fF>6fy#Qm@)`?7RE2*E+-Ki?HIGs1 z*uI7$)r%R&I+WqJ|LYQ9HtnZNxVBbOFNYrqf~Sy)7p<1q_ebPWRx9J8<5d)FraSeS zVXWbY_D`R%SL@At&NQQ9cF%`CQK_I+&K5GgYvN`33!$G7}Sm z_u3i-?{=YbevPz4 z$5yUW;~Ob?YfC3PVS09;7Ju|td8c6;4ZouU zT{ubD+AdNflhOrfYB&_!kSy%8&wcw_<8I7yF{{`4H?N@!pJ1(#^Kka>8cmz$VSli} zhG<^4htv=hm!n6B1g}&swnK@_thzKiT#fFVBx_~&P`#X)fW>YZ=A#YRKvekhKyHYC z51fTb6)u~|=itz9*|Bnbiq(C)zz5kj5dN5IY)IowS|QOJ7PqUx5tg7uh(ec0)~l2Z zcYBCte{hSHBnh$hU0vTts3%Ol!mmFx)X9q8=C~X)D%5amFA^-kk>Jf&)yK^~Si~!Z zjr)=^T(9Cjt&nJ2xK@=lQ~BS_QiE#3lT$avcODuV{tlIFC&WB;?f=dxz>ykxzau!3 zEM#8Zc^bX(NZ!p2&cC>--1CxE)5*&U(k_(5F=fx9Eelpem*DM+f2+(?o$>MYL-koV zah+4k(3mHgA(BMloFRWCaKY;|H1I~lFj|Bo7`DYHc^+ePw(B2OB@cSfRZA~T@FfLr zVBNgmtFuJB36N-5x-F_X+9K}ms}SW?;&iWZ-IFN)dN)>_NCMaOn7})DMP9#QC&?*> zrV!!?h7^uglgw)uy^iF46YW`FTjhHMwoB>Ku3jZeeufp^<$p8B? zZ(t+-sQz$^xWSJ@V_fn%uXmYMJM{}rXdp?fL0uWR;FWj#j-7Uqun#9C8De{Qr!bJI zrEWh9gTcyuuJp1`1m-hXO1>`l)x}?`em@-7Cp*#nS8NV;^AgX96g;5HISpaz;dLf7 zeBoh2!z6TK#fcAzn7U$S2_jWvrm+?M6!x;RDIjh)1 z?sW@!w_K?@6nhvrE|2(FzJscqC@BB)n2_Z3@+k+P#AT)_hccd}lO_q2yN%SQYw)3I zW3XR7I(FiC#@09+Q?ed2Q={Y^n0Rk0GOMSWYm^C@&`sNN&|0Y#1>J|Nmn3vueDoix##<*be961x&!~ za;1>YW4N8E)`n5+<|W>;{FE(ff3CvOS?aJt{m?j?A3bE%v(b`|m?VtiY)}q%^KrSl z6^R#tMlnHhYGs{pT~_o~y_1$!GjG;Ia+_9CO8&hWk_io&3ZG&rzQhAK9(^js&ALkB z7P(rNDJ6l6)$gwQTWeu&T!xYOV>)Ir{Y=Cb(OlbHU-I)K81g_`q_%qs zGxWs3dK_=RF>bcK|B4Odl?w?MB{Z>-lwfQD1#dbV155A{j#XE9=VWKl35e75u;M+N zV>jHAJ^C0_wbv2W?_y1`wSl+_kb75iz-1vC7u>Z;)nOnOxy&2INk7FaCrIKD$+?-@ zkPdp|bzdG3jft#t(w5FR4-+cX?N$hl5+yQH30zrQyT1HLUq#bNJf_*gvRC#A)d@kR zJ*B#R9ok}7JXd}xM-Y9zrRp2mr(DSpl84~!Q;j*5plQJ!n=I{%U2o9J#?Mh;MbEy98QudIW!}0BtqLr7Q_AUn?s6MR{*sx>HE;ot z=xn zxl0ndqMr#^@XC7~1a2zE(Ux+1WX-S}?!mX&84ll}Q^LfBR1bF!SnlQysC|y1*;rN_ zftZ#m|CG&fnz|pgwwwI)v^Ffoxyo93j)2rEdGD@rtH#1M1rB3H*?Lt5ZclLX$_bLx z^!2Ae`wTN`Kcy!_O>Avy#dB?lf!N#jt!pH%oFISgBY`H8PCWht$7QGCN9j7<6{ly4 zu`8a){w*etvN_M?b*zOI4LO8VnY#ifRWrBq$N~#qSvcQ&XlR&?POLbQL<-(Et6T&c zL|}f=cFD9$_QZF6Q2tQ7QXMt3_AH zKql{enagb?C(k>Q0hlCoEgv6fLxa52EOZ4s=1F`Jf&WGz4Gm8Gnu@(*Aa+-X-dtPE zCU`cxlbkWy_dFI`l|MH^DlO)w__oq=mj7uq3RmHkrQSeYdtBbtLTwUL;wsy=pFXMSBdum8MCAmi4Fn)`dUvi*hGV{E0WLGwb?1J4zZY8Ld% zm4szl4g08Hu-c7$s*SM3<%|aT)bFiy(1MU8t>pItJzB#9q9pFfUg7BhOyHGOJ6E*_ z#Z#!QILfw@8Z~O}8+8(gcd!nkb=Z#twxF-C6eh)JCaGMrFa{&;Rj zB?ze?Ef1|!M-@YyhfPq(riQOQT#;&_H`q}~l83C*wba#~LA5kUh2~Kn4*TbX`_eOoCM_|vLg?kL7lM4^tHCV{l0;<99BBMjqm|RkDd@=cMq(Wu z8{$`kg4gNX-496IBx~pIZHg-}-=NoWie~qt3s~nR75J>uqLu+77Z;>{Bil&zRAYPE zbw0N}b2*YoBr!dwnk!^XO=x(QtBu4wiQHAKD06abDz;3Z+(iWex+%Ui*w5=o>ECfc z`4c6H&tieQDhXa$j1MUB88i|?H^Ty#OTgK_=)j5-NhD(RcLZ>nyyDMZteTJ*Ba!GH zV{1wx)=yY3(Mh9oHAN%bs=f`Ab@VbkuIjwJ6Oy6Q{_p52C-II!GX#GGOqT!ouH8(j zGHF(vNK%v5V!RTl;GM3#10nH^i>u*JLqe=n4gTy5Rvn#)y%(k!Y;AB}cQOvoz9U6% z!VRu}mDdZW3vh{pbLGTmuHB4GbU4QT?YexRB$0x*g&jbHn$aNX@Ca}o3W;-EOktRz zF@Lg3f6ZPoL7YQu-PL^nrt7>Tu{zk#oA^w&6DCzMHD|1RAp~z9*G6xtF>1Wzlq6U1 zcEa4iW;DozhFuu0NzB*O^F{=iNUy&V-psHnPM!N4EHF4pJfoiFbX6E*b3>vRb}>d( z+78WRt2n_5UU|1mUAr0i*B?>tk_0EM>jfxx%P?L|xMZD##5XE-RPxI*P~Dx5{*=3w ze3*RagD}BpW0s>UUQ_q5(SId4ySlE@32&#VJ{5R{w+_a561-<}qk<$@@a`6PLj%t| zlQ_r3(+$bp1=xmix7u&LJ3cXZAw}*UwmCQr^}v4KL?V5!hi3a=S8fxSgS8N3-Bg^w z=2+s|(TEZEW5z}jEO^fhNbp)JHQt>eo5XrW72|#F+v^gi7OSfexOS>n04?y8(Qe+{ zQ+*p7mcJ)SSh(DvQU{=O+u9+4R&sJzPNZF%@3&|Qwlt9BuHf4P6ugtwMW}?tS9Wb+ zpeJ6|=lS5?NNv8XNT~p`H{3awe*7;XS2~jlq|}Wv{r|Y>=?}#Y5VA7rwFFKn)95;WACg6pq?drz zUAL0ACw?(pi$(s|sb8t2qgQtNKA$aTK z(<*OU`yn7HDIxS)Pi|(Zx|5tHN|GBEw)8N}&s&}9M>`~>xY|fek^0^ys`!)9CU-5& zvr|&m4!x;@U?gwu9X?T0k|}x<5HxSA@?2^L8^QcmjD*leDzA3m!TTPoyCmTudY|kV zLGvBFH!*ON7$ZlYFVQ-oK~~LWTs1zFNoRZ$h~#zZyd}6>oe5$CO3X8B^j+|E zKvQTOSv>`Zs^`V07UQ5fz!scUjVAjVjf_8d385Fcb~BQZEm3py*i%T7T$8%o=neEM zLidjx{zqait~Nx8vUPnm!vnx-q2H&{5#I(<<;`76QHIyDH?dJ$#1L7It*Ypik&5eL z3^e@MMNBtP2Ay%8jeUdW>?tG(7rggmCn%Ww5kY++LpO7&T)W|C8MDjmhYBS87#@u^HGt zMBsK^|$vFrT zvafcXy;;gA_oO=^i4M?W|8E-?yrt>{a|3W53W;y(n8GG%r^M2BTx|rnC!$X!3=O2I zVdXk+29Ka?`6nPTGN;X}44Sg|hZ;Sql0rTF=t=O-L`#NXl5oM>*p{NE``hHzuK;&b zNPH37zE0yxl6JseJmjhM`JABctg`|Wy-qjxbR2JM6gD@+j0RQS*Cdbpb%U}}%y=sg zb3{cRzeTxA5}&BH!WiSX&da>(zN~VP7^jXYG*P25OSSEZJcc@4F&dwc?B!mQy%H<9 zI%6fsCgAicD@7)^Gv!^I;0q5G zD%slDkToZgc)`0mrWzN#a)CG38j1Hxx>Q?z*GUoUa>ZzWN>YdaD4-E#l@GCv!@lZs z@DN+%k?n#lstla6fiTX_ZA?^CR8Ez;1S&if^!=XkVu{fIH9`ZFg zOM4Hvy+G*xH^fq-H?2v{>mjZ+5^KRL6B=~xh)h63eyzrQ-aZ8oyv~!Acb$W?(O7vz zmH;BXKc~q5r%=?h$Ptfs~wiA@XD zIexAfvrkRD(ue|e@~(V{Z4oX-bMO#bBn9s^)wb?CmkjHZnesZrJp^m{95XnQj1aW~ zZZYWeWo@3o>II2;)Z0?qfwhb}(8Smj#B)p8Dqvi}I(b(<#5P80BN#f`PmC@79jon& zkq7%7>T5c_BCpX<<<(wh@II$LD@}_eUGBESEMuK)vNHcePYPa=fTS^_t+(@RaQ87N zy)qCbs>QLaprSXe?B_vr19-eQT1&qp7SynqEgSlbElfJG6DdOWeGMVkcn5(iaO0Ug52Hh;5mzq1u2GdLy#@_stIes;<5| z3v2k6yv{|&|9I@+eUMcrl5m_J_|MC4csDnJm$fPq-&K@CQ+%auLW8WCL%|zBgO*$xM4=dJRIPt|b1 zRFC6cj{aabB8hi_XEQ9(bTzlU#yqy$1)j9jv7h5BA(FhgPx7QH4bp>#+IXZV=F1w4 zM=DCoRrXTHmx($jW4(+dTZm4qJCVe%RN!612iD#}1Fkd@>$DL#UF($-zo8k|Ou&em z=uu;tt`L&fQddl$p*|fa^oBa;y}!n6$ywGdeOy8zzt^-zSzXh6f5wMI=;ATZ)1@tL zZZkXqY#k!;P8#cIOdBPdy$9BJ3zN7U6e_{HqR#t??WsYl2y-Ystd>A3W_1(1m+GCH zuuD7yYcZiqm_d`ouhJqsZ19FPS($%hHO`bIF&Dg@u|#Y4Ca=6N%#)PF{K-(YH8>WL zR-fr6+n4eXTWFoY8dz5AXs~4%=VZqwXP#e-Al+_kwQJqvqIO_uwt01T;kKK z+<%i$ac`~lS23%*JwLpr_|HRTU8+t#vH_eV8EG?Uiy21eGf3xI9foca>vaTs9ZlA@ z4KC|(GB2Gd=o77vuR|(%ok6eDaavZK(?&v-DIJtYf1sGvJ{}~m{QI4ild(K>@QT53 z${|Vi`aB;d2a?G;I~HslB5_7Gmxp;Ajagkhd|SOL#Y|xkWibfLL)g|(@etd)Sik(! zl8^+{4e~evF0NuKy10IH;u3E3kXOs6nXj(xV<0BUb?Pa!#4iSqXJph3YhCG*N$lg? zO*Pyq>P5~x%CEJ6IO9(53Ek1PRc~Tji1X1f>&r<9%m!$MF?pDcJiM1xi$Ti8tEW&8 zA9@@~HV$=ZBqZT=sYA1;f6dh)9%5QaVjtz2;d_l6;IbonJy_o@bU9!(Ov~PR7go_L z|L^D6qCD*)1$G5e*?By#p+^!NFLw3VoV(m+c|5YlKunSzrt9J}!!-;GTk*geiMcca zHqzP{B+>FLa9^bG&-JK~_VHG{yi4rmYP1CFydgk(WY5maXqN1gZh$V0C(|v~>%T*C6 z7*azWtOjyhdCAb(=;uO+rf{GATcw_I=x*Sa5lOb-ebeZMoxJk#Oe;wO1n(DG1+Pe$ zohe>Ip>Okt*ydT6aq1m1d$3 z1#c+SOA2dXVFYTtxqe;?539?E+0R=*lexe}jwBE5x;z%U{NHc%WQghP)L(8Gk>sw) zUYKQY%A}>X*tTcTCNW29X(K(IB&kX8{_bH&CoxkTFnt%L{<#fPaB#V8lE8zjjBs{i zF!C?Yt|e5;NU~yl<8ea6e^`g^ge2ODUum?yD+kuTuvaM87>Rpy>#7>-^mg!mk5-IO zp-E`EU}~txlU1(s%0Bs;04L-WCb)LMOcy;)PSy5E)Kr5NG29>XJl?VPkuqauKP0JZ zvK3|+ozNh!d;~)_i8&IXcGH|X%4?0uj+r81ki5V}p`X>TviEgPP#jgBl#xIMZ#}%} z%3!2Ev?otBvaK<+q<{nL6)rDc6w{i60o4dor=j6RkDXH)b&`=LK$1w2 zs_%|9eqik~uz#1tU+y-*ADZ_{D0su?GlAnH)5p7Vo%he|+y>fGV94bY`?$`JWZD0L z-c^Q8r*mqSha+q9@8_#&b*w#+Bq9mqLD^jta|b8pqAvw6Nr2$p%*cQ#3f^!%=OQ%G zdU90f=-SG4 z-etD#X#noJ1(qLq8*^V;E_sVrlDi!-$5YsrPp~x~J4tMa2A(&%H77NYZyBmd?2#C< zA?6yX@P314EX#!MX&(_lslentZ0{;1#ZhH}gxMo}l;o$_j%9`9mH+Q)p6a{u8-H=6 zH%aU|Oko|&HtM#;wI0nCMq-bgO*z>>&5wdNtTe=Lk+^fM`uV)Gah$R$7W|ANUvW>* zchQupvxb{yACu%K_3)Ob2@UcaFWG{bGk8f7kyLTG)?1;fN+~C2ueKy4&M{3U1wCtE zHBs<}wOjz47}2JNign(n(G5Jp7C^9_;JLEo+%CSWdKG6QX|bo*64!KbHa90Ec@Dc3 zel|Xk>?Ux^HA%v1T1_3rwhYJ;UOW-TC!RukOpLVTbuN?t1AD8#h;?40JmjNyP=2pO z-Q6T&^*m2TXQ|iNfe)G_)?x2u)LqL8Td3x$aD|ch3*N1bbi>Ol{*JB`yrDz9+NBX3 zPF7W&_m1r4)vWUxGnJ!#ZU1D{e`l+Q^pmGf8hM4O>hvogGD#-y8|~kf74{m}7fFcV zy~dc}oq_czcteNywGQ^PiE1D3%A?A@!{BmDCI$rWW@;#y<)eNi4{A8J@$r~n=L04Q z4bUlUjAcdzue|b(JOxY=B7VLxB6yvh$j!igl5nOu*G0a@o2&CK#>L9U=&y|!N_*8m znDONb#2HNY_!{zCXEGd<#HO707;R|CsKYjmzzQ!(g52$4U?ka!!p;z|6F!_}gVpWl z9`zdUiq?jg&{;(X`fLNHGSc^%R(}=ZGHk$bOcE-0yI_hz!Rw5@Ifiv9k^p4V&*8@I z-CAz{H;jTev^wT)qmyoe991T%)dbbrz;0g6$YHaKwQL4ho_}j1J9tTA1@8$)-E>;H z&UV~EA&C&Yz`e%S({iScp?>n)7;X&~gR7KIqaW8Kcy}nE;C&WsXduZ$Y<{>=_bjjc7zog_)h ziW(5W7V~Npydgynt~JC5MyHw=Qu6+;ra*Jvj&Cyon&Gg|m}m*2PLSb7Q#IKLR zhO|Myf_Hbd1!@J{&fwf!!+e<}Z^hkX_(I_{4r3*NBxpqqL5blQChPS>ij9z$61Fc; zduDK|X*Tv(e=C)f=j?bLwGogBG+W7FIX0pDAPHP=T`|?*dXvEXGn&yQl7t|Qac3Ie zrIhtL9!k~gjXJFh&J5Z6YMvK_|v0iSLZgB*vpd}t(LNP>5Q(Vls! za-Gdok|apxr0}Hib0`wCW}+`+R;aye3bz)ZK{MFa2J!IssFpQaq6cI{==p#JuNdT$ zd|)Jz>urqT8pI5=VHJubNbt5&bv?_l(2y88l&NIMwaji<7RzI6s}3rA6CG%aUTaS0 z2Yj|s#PvAABe<}eaSl*wL*mKd0)ubbur&@vAtHllc-I7ujNp6}7OdnFjbdAT^V-xhePncjF=)gOhLpZpUZ% ztNj0FvexUo$3@6pAx84%!2tVS2tXJ;c z>!``+McMzq9Nln++J7iB)>rH_Rot)sZYrw0O=P>k29sH83!n|dGD%$J;ccVGDa6IU z&viu-7Q03SGNrD68Z`t{-}Y@*2i-{z)yG{7M~?tCdpZ2DfGhi7(FQ*RRO4Nq zy$OmBh9s*Jb)(@GW?>oDVhxHUDsruhc>#=}67h5%*H&bF?aA5hRx@RV&s|E(;;gkE zfNq z@t7-61QeOX4JDFq~UUc}*fZ&zd>}+-5-dh#b(L7t@ zo0W!|=~Y+%s>F;{-Wri0d1fH9>%?`u#RozX$6LlHG%REdiX@IV0yuk;$xdkCrNYrk zgsw1<2#FmW<;%x9J-zjDq3x|rBx-4O_0YytShZ&!yQ&(I6@cp)&c{I#wYpO1Yq$y} zul}_ikXeBuNumL!8;@G@>Lu6+Y!-@=p4m!!A|yWc)mjg#<@f1;!D?KY8Ob|?rH|Sl z!J3#C(ETKX!G;Es93;8B&2Sw`Ui%s*k|aTh?ARcHgVqugzvtH-F+I~Be`xi&(sesa z&D|~1V7VejyJmO&G6rJ7KiGsStF}ilHIQ6+7Mk(vCJA!BDYV1H06Go(v&WDmERj4m zfC5dL6B;7ub&K%0)>BC$Bd^m~QV_}805_Fem~M;x2b+Rb-Ws#z4~%z1$*aG?FPkI? zaXE+Esb1P}BKb2XG?2uttow{7l9pP#{U~@Ng>+k=rBU!&s=nJnYe~U*r6Y`V^NP}% z&f{w}fBAP97reQX**yChM_C9aWUO1^M;^A z+n%*p!KK=5z!_GyIes!Ed3k)T2Im1BZ&>g;RjzIM2uOm~mE5ql-0-@RpY6D=NWvmk zcT5hXNOKXmIUK!0LmO?@A^p44u~zvsUER{WJ>D~LJ+^avZ4=sVb?#$J!*!^Px`DbI zSm7l}T34nItjU`F39Y%VNP=)`c*~95s8w;?-=<6^BWWMHnlvTpc}l&ei7LOy(H3~s zP?dKQdeM5T4$Ny{iBb03IogO*Z*yuxpW%Lzs5zk3bXb4J|~*orn>*?rVC zdbq|?4|8`0W|EjSRfGn3+i;_hr4A=>)E`M&ylRZE0=Q)@jdj5TYtgij4p^+IgZFmh zHsHw78?zN<-8+D9qN$2Uq(X289x|9QTdIY+J)H+h2$I-siW228O>Vkcc$}+>Bu>tB zR;TdG46m)vRBex|D~c9UA0KN-iRAZpgZT=nqromSAbF+W^0+!cO*<|}mi5&J&I+zv zTBqwENpiV28=n&@uf998cakW?+J_jwC`pPxXM=l^(e=MK_%7jzh0PD>sJ-LG&r)7p zf0g$xm^yYBNu;y(G=mjh$(FAKJ4r}#Xo2a5gR;}muo@Fdk~qP8f$=e#78a;UraTTB zMdYa+=v&c9LE-JdF7q23SN>B#Bm)TNDb;i zRT-cgBgqoH&jm2EMsS2Z@D^C=(+A6r_hkVlJt_50Hy9{Aianpq^sS=c-5N^_ zg=l%rx#&c}OA>)d(0cgD@Rh<6@Y6VuPK+dOeL>w%l`*=u$}pU3E7D0(S4{NPR3_!W z@T*H(m+B54r#*q3k=4?8*VLS0H-oBf3L6IJrp{D;0^*)jVSw8rm- z1g|`}E16r6#H^+iZZ&uWbVXnM(_B>~VTiqNr)D%*Mmu=F;#VE{vcu!Pngc{Czgqwg z>&%#VNqd!7UhO-yq~y(IL}QGcD!0_n-r%JZBpE9SXlMMun)Aa64J2tu6geq?4&F1l zwj!Sxo$t#|#D8B@5P2x?*vuB&x_f!Wu3zPSx4C@i9V3EQ4DKKFWL`lMfmr!5#)slt zYE?A{H(g2cB9+<37>OOk-pg}zdU1WZrhy@#1PN!;p^ST)M%wZgKt-a|u{Xd*9oS68>K`PpeJ z+)aD(`Mcu}&6>h{;Mr1=a_wt-d%btBWz>a5 zJ(6wV;z+hNvcQ%?(%F6v-T{%IbjXc5YJ$J#% zZ@o}(D-e4>*T}qvx6v@kmkrAw-qVo#_=1~5Br$^bXd_2IE7u=(&t64INU~Pgxkgu* zGz(n^?w!QLR*v;0cyBU5c&0ZVURv3f@L)+>538EBzP^=s-v! z4y=is{?!iqTvH^85@xW$gi-`WCdnJUvC}jy@wKNCg!u43ZGRq?`loz~VYZ&q zKd)gp5?PKE7X_H6EXr3X^0A-jj?EBwKM;4);t*64%(VhLg$P&>lRp78gxZ zgjYQI^{QIN8Wc$+ zV)Hi`Y#Nf+eVoZ;tnB3;PuEb3M1DsdT@24YS{>9=3awPL#ZR_Vyo5H$J*U-~ICquw z1UFwv+>rxogACuVmfM8-O-*Rvnj%RQFfSP1McJR@{$hjdEMM%&UgS4k$@=y1#2BC( z{?uGlk=J?%3Ek6pK)c`_$5lrXA$Wl&0;uoiCNwZ4lcXWlwqks0titOYv2MvPI#vnN z5uVBrB6y!g*!I!H>+gW&+S?i|HA$;Z(%cDfv)1f6lkMPTEsDfl?xxTkGXkja%FiIK zDU#5nvJ-wYmR8CDtC3t=F~XU|TjIeliwb%K;X8dKf6@5{+8QRHqnh}eFn2OsyWrg$ zJODtFP)RsDfPrL|n$W;0p(LsDrN5dLpE1_KdmzXC#Z1$zkGUQN?;Rl&ywYw{q}qDY znl1mHq$xFVCueFEycQN?O$uHTd$*cG6MPnc;FYy`4ObLNY+AXiF}dqBG^`1>3&jU1 z3v|Oj9&9F^^|awT8Z6PKBPME}*O0-^$%-tp%#ZeTqOp3D`P3EytVWTzubdPHs|lW_ z>$}o#wGB9-fh1M%9;Y!vRZb>fR&s*zDo%p8v8Q8R^6xJQsizfDItV3NudtWDcRX4p zX@5f=?S2~FiM-CuSvf#RyxrOBjb86Q8J8Rx+_ zi$k z=M6Ou$=aQdK4800yb$^JiN|8Z5uufT(#cyFuW7zDA%hZZo17IOVRZZKSyep_WCqru z10e};sTTxt;Y1mhp)c#ZB(Z|G3BEJ5(&RP21l#Z8hNzE`u5HFr|LueD);q;cyJNcc zwuT?jASr7=1n&@Sf>*Neuhoe{LgE~TQ)ue(zIOLRlRo1YP7*A5+p3)uQ|aV|EYHMJ z@ZRZ3@Rq78cf)HBBE=6~1aW&0X z4a+gOTa%Ia<)$3!3f(34!=M8paj!acv0bD2+ttJojZ`f{Tu~&c(&aouf>$OAT@KcF z<7qW}coMv&YA$V^(EoKiYulC2{q4)JVG`DY2;QNZMqNn8@rimxhG!CAau+?Czff43 zCXNPYo{%IKlc?7>4z+3(WO`&*~2;Wo`}4P|)4{<~wplUSV1 zg-SKmqpqUdB{3(3Mi?8wgaf6$IKD3a*p@68;E5Z*M;-9H;elKOj+)GX%W0mJ*pRYz=p)TdQeB5 zk}VCOhqnwnCh6KoLMnyzc9>#lH^stkTwSphyj?vAUa_);5$RpN9BWYg>bbf6+eP3QivE0a^sFWQy|U-sNgM@lYy9Q0 zso@=E6;YAAw`wfE$jOvR=osg9Aui=`O@eohomDYsCh@K~?PnwaR~3-IK|?kOkpv^V zh36UQmls*{H@buSC-D`$Eim4L;FW(r3(X@Vc#F_h{heuzapxSwxNIqZ*io7U@26a8 zB-U=U$PU4;2hz}x$&UDAeU~H`vHn{$%@A6(UFbO_7f4(MZxf91+~2whqEfgM{kE6A zK6EuUNGa!Uw2W_=MZU0OHMkTRp>!GRQ6$F5TG^ zlu;Kj6h~;cTB5`e*&}DV`gqSoO#67{p^osKL(6ZEQb!EvL`ZzY?>Pa?XK>zOHhO{+ zPe{^N+?w`E(7onF()^Fbnb-^7w>=Bq|I`IqMUnYyi|qg2)Rq`ZjrTXSj;oqW;bq@n zl{7>Q2YaGOd|hY}nqaK$3NnGzcSqW5i2<1;8A{UJ_fxviDJ&M&>8*N``#|R})D% z5^lHG-oY!8?kh9`cO&C1c#A#H(~7$HhtEwTaUp-9 z19N5)V;5Q%FBv#5UUfsmUuesTCxn6*M{5_nvVt!M+lAsUct?4(9kGz7mGz|}^cUAb zb9}F9wZyiB=?rLItRZI}O}R^A9C}mehou3uGdQd1KCUK0*<$~9x#lBl63R=kJ-B}o zcgv}RS3Jr;vA6FcbNNILXCiM}RcH02h0kUqU(5`nyOt0=Jg#Fb2vO_g4(+GKtKUu< zz|};!>g@j>(Y)&9wSG~9^|^l%cftFTN5Lye(E@afT+u;3?I=y_MWWCgH3>deJ0bEF zZLYXqtiD78t|mfkMr&Sm(uMyp*w7Gv!CQnEJa6!e7v3V0 zg0~MAYC2~kHQr0(7Kh|38m6J=aE6^TGenbkBAcJ*1XSNGjq?mdLR|8vfi!b>uwf|v zf;WYcz65U+rxeKNy;fTvubd@m952I=FX%)KN7i0a0h)rB#L;=C&=lVUbi-O1W@23i zWI}A3VXk(;I}0rss}do2pZ6tr?~S096xlzkVWJwWrbqG~9HV@Yl#q*^7U2+sx7VS>q&mKi4eTSc-5ES{UVZSQEsw{rDe60 z;e%|+Cq}{iK3|))GBvam+k&TFNql)ZkovCkvo8fNAu4q5s8*d$!CQs{xxNx1c{9!#n>uN+D`tEQNDt?{N``y2yViXc!u2+daGTGAbza) zbxjfCxfpY_R=Mjm46Q}Mn+U;MUmbYNcow{i&_9x@Mn3UCHA2(Ua-!rm#ArB~Ec`Eg z310b1xCu+*TxYr~paW~N3a8c1BL zOs59azgwz~2-E{Byo97w+BZt0;B~gC`!ZG~MDRAlk3N)5?xwLik@Uu5;TLHs(@0~) z<8fIt;!?K9G9P_XPIEWM3YwD zPNowfv2~lTXz7H?ufF>nR}vxR0lSa7&O4)Vg>B3gmKe|78q<6UUa^<0BP;Y|c5Y*Q zui**`QHPIaTZ|%Z6!Pk*-MRD`vS*!eyh62^?WVz_JcRh}3{Q(r^LTRIUELuCKs z)&To=e?fDwIf#(l>Kmr<$ePHP8yTq*BzQaelzQU#Z;iA^mV)L78YHiL6Z=HJ#w$y| z3ugKFLd1zUC-@kqNxYEN(Fv0^-AbO%nc&O8of1M;2;LKYwcD+FXH+Tn0FQ(wNAULY zxjP`L=(R{I8;K^{pu~3s%eC7@T;)kf-^|xYGWjCUKs0C75mMXgI3}PAC&Z!5$EFm# zgj866S8J>AN~D^IHeiJ}Io31Kr{J~J{&l@b%lmd_Q$F|__7~xAH1mnv zh$XQ@^4M*#MD^AN)6j4ygE1j1MW+rZ(Kzz&C9bc83Emrh*EPlapNnXh^c;P5jnCqZ ze2??dKg!>QeDHmI&pBL|fttiF*ou>QOyfwT+6J7LuoIq;RRqgmZ8Laf9UsjVmN5Hy z#JAv;-+lm5-^j0H$Fm;z&*ym*SwqJ}`MZ$G3Rn5i{o;zoqX`8siJ`Mx+ZLf#AoX2w zBp;#yJK+ggX$_vPv7uoOdQcWSFNs}cDTVgfd!-$gxZt38^G_ug%I09b^CcgL^1RLR85hEYc)+Z;pxe$lq|c zud28F=9y89SMsM1_1W997@e3mlh{=T_6bz(PQ_}xx`mKc$}vTFQOg``sqcP^ri@gH z6TG|mzGEO~KQAc%9DVz7`gj-lsPQ_hsd@BjycAt^TohdtHR$e??nYWlBxGr&mTsh_ z*`;e~kZwWg?hYwwq@;T(0f}8ey1x1T=Ev-tH*@dYckVssRBMAREm7LS4i*#1p?Hc(2=C}U%eu=JM_wXEoF z>tL?Pko1I9RKyp9dGq&cXv<$CWQp0Ke84U&A#a11BgW^dXix zna|;cIKOssM%dDGgNd-cZk0aEZ!qmtpWG5_w3^uh+8rvDLii8;nYZo! zR1DCnkC?K9^5YH~#W7Q8TFoJ5(J2R1=_?9CCD=sl^=lDBYuJ9=2Mq_qC<)g%mWhBJ z@3;A7eO$V&HnEN%-31WcUyY>Uz`8W?Q=-hCE|Ij`QmZJ;uOVW;ikbS~ksxu` zKS|!;)wU!glwT|S#Pq#+2%LVk8l`H|r;U#Q#st`gKuB>mowS?9u@vWv&V4^7&2*_H z8}cDPlR1zDm>6o59P{g{eI~AVTvwKJEEO-K!me=k;b=qc)z;X`wvC!E${E!Hy*lYS zqzF<^pOv+3}%!>-d=$+qiO-=&m?kK^Wf0-O| zaN9s{Yd*xsJh<5VZ$5-p?(3+prq!E%aAoqwDhn0*f_$;pdFopg{2?bZ|MdQfa*sf> zbIMn2?Qi+M(iajjYG^Fb>wa}GZWq*K!6Wp@wmW>D4yL3LgPPc6%Z=i)&FT~#>3M?h+1HLWj?e10l zZrxPB!8C~z?V4u0jYfafaC*nrpJ#P_xfp@3Dq=SL9uqC!jAr-SFPT!CJyjRR0u6UC z0fWxy8d}EpqC9gBpWr+8k;XhPz0x`uLyh~3E!~}D&B3+3petYBb z047aD*R~?n^SEZT8^!YurcIMKKj*>y&&g0+uu8QU7wAa=Y@td!u`OtWBcaJ@XSPMz zH>RJj#&$xDcRR8?+0Q=_5nCy&5&_HToMiU+qszrG$NZhmK7o_*E@DRo zXIl!#mio#&d1EO;(Ost3E%w|Co7%H9Msp;J5tku+kGkINE3x>b4!Q`r(%W#*?7O58vf2I+ zf{JkMTCw1Cpu^4{ob{DJU+5I+FeoQ!#|J9jRP-id!1WXUKy?(ucyiTb?M8YXH}@fnQO+R%|)=m`3X8!&ud0&Yj(!~A!9Qp%MWqWh1LQzD-16>e=_bMQBVb^JQ@(j6~-jiAe zFA&zWs*{IUY7fuj;x zVagm}N-`<{72YXRlkKv8Xpd$tYj2cZa#M{Mb;h^RXWfjNv3(f=HbL|*2D%U3=EiA? za!>4lOsxZM+)ZAHMuQp&N0v|paU8wp{LIIU{%IyNU@**8jbAmSQ{QyZ(pS%DDg`|L zAPf*?hMLAcTrDi03z;YhZ#3+FX7_r{vf)1g(K*xuVm|-dz&i21tMCx1WT{9ja1G4V z6MNZE(~oCO%d@N_7l?<~3o`HI91lY?4AbsCO%PkD8#ju^T}DVTHpV6<)?1QJm1)w| z6qb*Wh-|a2A7X;f=OJSsLY`NO6KcxEDsM9LLpDeo6D{YA))5b9Eg! zU{CZPg%dOqkDyc72Y!vkSF{+A?qE2rVI*C0Ft_@?pe8o4%~Ko=n@x)D%G;#3oMj z#`#jP`dXE#4HI*7^eEU)+AwYhqF8Z;hw7}ULUnfD__|ku@WSE|_s_{T&qeE_G^Zb~ z5m_K{?8T<)4t=ktwQ_=V+(*u7T3Xw( z6KoY~!p^`U6XfVJrB#I#pVZVDwaQf26lRIlB=zlIJHqXoXh>z^FyC&$%e0OOvg1vh zKG-b1*3RB@-y-g-H-|nVxBuVaw77$LVj`vFS+w93{qqg;*IlQCY!UfAInRDa`qkCQ z@V&e9a%pCWIulUO6COnOS%WpW1-zpc5hBnGg@^rkUwoUwVJA$+F)(IKWHP%rKNag) zH-7W~NbHg{tMIC(Z4vv}lZxs8X9#@jKdOvfv3wZU?U**lM*XRBAfAzZf(>83`uT}Z z`mCpAGx}(d)3ooe6N09#DttQ&f7!v^bv>THd}zdpBtNHH(J( zk3aL0pa(Pa7Q;nm&>e&(!c_{4$Ivt-qZT!J4Y05xOqqKp(VVA^qUVU?hla4>C^<<_Fj@K z_+8IoN0Nnfc*vz7A(2lZnMd;EiBFWK`xNm}nub9B2d@tS3t!=CeNC)HuMf8@H5@bp zwv9a-Yv?zUtS06B!)`TKpe7&V2ys@J%ReJSdUmTF>v#vukOa64!Zj1~_R5`fyAYI+MX`O%;!_#LQ*I}vRLwTt{g z1BO(EggWt0VhA+dmEJGM-K%RP&UrBwkf8GA3i!DCQBkwWB9&*Chqtpxs@k+R8BP6F zPv7L3KNmr5i3X7xzWmaE)1iqZ(&UQ2Hu^?nVUv#yHth44nwp*Bao|Eh zeDBxOCmi(F{F3BnVw&NswC?7xq4>P3PLK`nob_WB-SH=z`-7mvj{47C@8zngW$g($ zj%QypbziJ~OzHS{u{gY+DD1uKL~LPGFHnP&%sL__Da1Q@5)1OX&i?6stp@?t1i9AuF}_(#lueNa_RT zRndO(E;TbQB)}y4oBx9wxoxjLZ~zvv!C);M#9%qJYz$Z1&)n}}PlY-FJj(UXX0{}) zO{X|IDZg)wP;!f(@NPEDD~Flc zG(yjP@Tq7~ItAKhh;K^C=X?}b*f;?ixc_p5Leer_gM*Gr_q21YeyX#fO+3gPRi1;(g)w6H(Kqp5*k%1*oMBv`rU09KJs_avzkiQ zK8c;fv1qqvIn>Nw^Z@knr1%-&dWU&-bOz~yGlWOG-D8at*g6NbCWrb_2KgOH>??y% zcB@;veX*w|;GNr~@M;W(rihsc;iF!w**g2;^u}V17s%@lW>qBZ_B-Tg_qSlrkhqXD zT_%58Jr&D{N+>siKM1*!x!c%FOD=!!U+Iyz|onY9MND zL5pP{iZdU+;1N%%7s@gY*GXY&yXAO1JSP%O(npSwLfLSu<%Q~or|wEzr6{2$cD*yS z5rN71n+J-vKwUNR=nNLt=n*iQqDZ25NCqvVZ1{XgJY)WXuZfI)ew63YyvyXV@d8j{ z`zS}YWQ@AM?)gXXmjVRMOx7N=s2=^Sei9S6e*I5rl~#3&71Q}MUjBdsnBJ;LRK3WJ zit{D%NAy{z1n7c2zE@hCc&PC@Gg(1i85BValV$`-x|_#gfl~er?o&eS z5A1aAFC|t^bAf~GATC_ro#+RukNdk~K{4LV*VUA=XWuE_-c%w+5$Xym>$UG|oS#}g z(kpzyL1R`%ic8$nc>@Ms^Z3fhXl${yg;ZiPLr4xE{OOAhkIe2ejm3w3EY!5IqGK!_fza;;^{ErA!P7Zj#A-jZlho< z#|I!WpJR?1%btN$wV(r6;;Hq&SS77xQzrez69B-On(fyRc^PApD6Y3Vf5^fUje{mb z2lnb$mXC&63uK^^(auAK#jg(?uvtz>Fzy_{gPue=Cl<1bR&vB<*KM)S+F0Nkc#qj2 zc4L@r5ngg}6q%nJv&bpReH>_)}RtUF_PEWn6@&#DvZn!t;?<3e6tJV zGF>O!lPCh<8p~8#{_~KWF%@6e0m34PI>vrh-fxuL-wCiS`2` zG(2Y!#@MuI^wVxW66W6TiV@;m$26>wupQEGU(VK?IS%iw2icRH7=0y!C_5+qm0u9; z?d{&J$+DEDjnyCcdG=4bDF1~8jGZfas;t2}Xe!$F5orpq(^^~cw97v-mD zRP-vUxS6iZih<5?WM{Nbr)gY*6|qUbP@>e~6(nJxv^1Cs`Cbwr%Lm zb8nyN_nySkD6KGXux$Z`se`(6@Ise0K5ONj%f<`Y{;o-#sHiP%{ z2bzFW!;K~vkwp(hT_a7J6!?R5oi3O>+32tGP0QQT88eRL_(0G5LoFoUmS-HZCT|T7 zD@uKt&Jt>I9mdPvaUIz+B~|>3wF&xeDfFC7t0c$Y?hu)AM@r8YQ-Lb={39;fKSIA` z*^;?*Mu#Hq0Abo1DhJ(U;(Tw8;#$RbJD;n&3?tmq6AVCQ->!gm63GAR&3pEwrZNjZ zYCwxQir<;JwbG-@!J6Z#ogAn6P6^rl)>nFBMQ>MNwa14vDrM%S3D0n_Fz}$te+sVR zGZcO*Boifwk(`XE(Gy*c@=ai_&=Toi4H~66qn%{cLw-KK^-T->5!ZITA8YnFe!iXl ztV9O~QR|*p*fqRak!@2;SP+n8alXrj| z%deloG+?q`Uk6z&o||myO-uZt`D%VHXw79JVtA+{VFD&Ux+^@e5WIU`5x%Z?+$7tk z0&)VWwoOAUIJLm9TP7Tb%8Zn!#&|lFZ7ZsEAL`PfnuXQus>SruH1?*JNHj)fPl6farWmC(;rbk9#{P~CaixQtF@&v z#-14+6M_FlP!avgB8^g~mujXzr~LhHl}H`rIDe2xb$g`J_IFs>ttFWgqW2tY-8_o4 zvp#yI<0vDIO%O~P9=neaem(Bq#Wb-W@PAy|VHO|ZyuHTkq`b4iyIn?1%*ld|e;4M8 z8z!CVZ;XFO38gHEl=aHT$NE=WkFkDC>=;Po-0hLt8Vxs&eO^raB%Rw}tMc$isx~*Ojqo}mJaqeq|-RQlOXOqriKPp}l9bN5G2TnA>)hx*c z^*ykzZV5Wc20tDpMWX&b#BwoHn;-T2hti^pSti9xnwQ6;{U9nd%$!D?`6;Q=_&FeK z9rTieN0_L_IfUoKSI@BZ!%6;dC z&V9b-#q4@aC$vYdJbG+ToO0bY+@G`Yt$eVVb?$s@!~c7P^D&8F2qb~erk>)E-fomCvvq3DJoun6Okb4t*_$Wg0m3X!D$tCEiN_nAXNvan-=K}0ne|a@e^+ts>$Dg_)V@^y}*o2OSgbhOyGBf=Z|>R5bg+)*qGV z^2|e$d2c$hE%Pc$xfZlnE1yCmnUFrcf00fMl(1I^Q7#r z87kHyah6O*T0N%S$8K!p7v0K_r1YC2da-my*z4%3t*LSR8wHk$_)p}^Gnok(JQDD& zXNq6Il?Yl~;DeuJYVpt(HPa#_62%ur-B2AbS7tuK7HTF8><23aYa=rNy zYkfyW&~Dk@UOeyw3&(Cd0kv5x$^A?Tf?Uh*7^k@Oa&6q}SEm&FIeg2@Ji(u)Y>*_o zF5&_-BA!ilC$aBlLjGUX`mw)n(d?!&19OdzPh7E%_y(C606C$Tk)KpBB)SD2_q&3; zR6+~CqFYP2pKs()#>{zOujh5K3Vj_r%9Q6!XvX`8cR?B<(elzT5MzXmv&b<~-0&x_ zs<`h_m|J>fZI#4}&$bP-fxc`;98rrK({rMHXtyORHGl#m)tXi- z=my6c8Vn@#PVhk-)!z=H1?5^Es99X)lmrjbum=~H$SKXN0>;I1vw~iVSaTy3wJC^S zA2QvTpIS!=YAp~wdK+r~Ri3RIT^g%3uUFGuM%1cAg25YYaPIV5_zjMx%5DD_7l0DV zb^jFf;s}vy&O5IH9g&;&q`UJSe!Enw{j&d)&)Yr`y+6~>DvvT)0AYBKwAErg0%Wi- z;lkC=B~>Qi>#1BlpOm6(CPh|>NQ!RuRD&~duq@w4T!OqD;{2}GPD?7BP5pXAZly!M zQg+TqIAjK=8Obp-iWPjR#%m2!!SkAsU(+-`_|r6?tjgb_RM=U*I3$wZLQBdxwTvb9-m%(e%?Gi)OEQ zyzSMoYbaRgR@9!E|J;Ts2j`pxH*fa1F0-Lm8X|iZr?g;aC!Pdu%~t3_->)8Os#m?fz>S(ZF?0Ks{bG(aii5r{dPtSL6>5DTf8{ebQPGr27HE$t3Iem*r*yZ2IETTQU%Eq*U{I|=8IpgwH)&uAAaIpH$?;6oLN+Lig zls7}7_pY?MMS5X7=!@pv%eMsrZsH_Fsb)SxA(A;<*cSr-A7E6KwD$jav--`Es{=SH z=lh_`3_XHg!%?nwPG*{jG;+0bm}Warda}3r0$YRPDSkt@=gKIOWD6gm2jz^Cb+*&yzfEf`y_7% z+l=_fU*DBrX-ma=6lPw0+2s-LVFXE6SjREm2I^BK4PbArlO)n%yuLNmRyWyyNu7Iy zE@+l+Ifx>X!d+RXt_npY-Ua)!{yaZB8!jOFr}aOjqwgPC>G((c0I4pigyK2v-0yBK z9zyI|yhT-cPJ@iEn;DuxTx?$<+ym-nVGB|Ha&w#zCg|H~CMe<}Lt_lF+Bc-gxO0@X zz3_~h%F5VjJV6DtK3&>foEV%mEB%ariJ41Z4@-hf{zM${hWIe_&VNF*w!r?>g$dd{ zcRt_kE%FRoj$#5c$G8b|lNg@H$#8?cz5sj_4D@Vc_DBX$^#}TBd4^Mo+s0BLunwK^Ic089BRv;GQABZh zULHXR_>#06ZSh5xae8R&Y!N(X|H`Sz`&M$2rjv|zn~IUp&8q@cEEqgLI|sicWw@!D z6t|6p2peNEp~FN9;Vh||XKe(5{=y<-=Mt zwSd;Ma;kkamG>mD4*?7=_`aUS!b&lFJqA=|j+RXI+r3ybzg?(FwQiCrin|C!M}`~& zojLPbB_7YTH&f%^fOCR(x0ybc(vjv#uj4oZLQ|zo`sR$x2vP&`4g00Q#6mC3W(!Bg z3cU=YH>ZZdR8+6Vq|=FKwYW$8wy5PFX-Pdk~#a>MM{xt;|eIb=ZFEtn6X^q8#M>;5>k@^V_^IFCww9=?Gey0n*LO~vv zecE3>uztiWB}dyi61bdnbNp$Pn;;3c!5VMG!&${!HD*Y?m@q~q1&05CkxC<^31Ct= zafz@EIFroF11iAZN>-&Yr_bjg)`V|=1*w2?0g{(-GD+gx6|FUDSxESGhm0VUrsMt# z`uu3YYnPluG&$1{@T)#?T4UAsagk=8>GCBZLd>;JT-busVaGVI9m*(8+t86Lyp-Z& z@KSIQg{A_|Vv$*sF5GJetSPER z3H#TbhiT#Z0joGun1QzxO7n>!>GG`El9IkU(KlY@?oN`Ru7 zrD8D1c+PdJ&jUHy!8?d%RuRVp-R%|)CF!(y*0JKeck!i(e2#0XDuI3H((}}fo*3$F z6h{CFODCBhe5EEUKfkeV6j-Qs@(9*QV}nb30a1a|Ydu zq!>RibIp}!DtzH!p-;coLG~yox?cT0GG^-Pd+{eG@3_h7gbQQMuvFop!VooE7onN# zQ31Pbb=A-Db-lni=WpsQEe&d=v@EukH+o;$KI$9oq0e{wqAj2@@Xr4U_Y$zTTM|;Y zY%52&JtXQ}d=bpc`{1?!@Y;f|$SU=&{P`C=wQ~>laDA6gde124yA`DVa54*wIXFH8 zI$uu?5^*~>F0yCI42)`~zZ*qZc>S%0SgPM@rmlPI&MZnGq&iE2JGSu7cpSIQ*Cvsc z&lJ5qgw7-&r_+fbdInF46qJNDl`uQ%TE&=z`-MNvdgWz-U=vHW|FbolZjb#nTY_=d zUnloBC2}>5XYv20J|3$eZ$~O2pUl`2%NW)ud-$om{nd+EnV*{UVHTd6h6@k&&LKmk zO4fyOa0=nPyo8IjrG44E%7hT3J@eg3m|t~V|V z8uE`9%r(xU{g$73y4`UBK}7kVb8`bW;i3&YM+j|?-D7)Lt6ui_u}(org{JP++Os`G z5+^~CoAq<_q+RZRQbH)cP%!&ch4EYBQ_^nl@oK&+zoBITL+*yWV0lY@r>KE&e6Hbq zsjpli1YoXnO{WD+!;DZ9qu|12?^|5-NH2oX!TymN0bhEqnyq`nqFaJR?AzlKxe!B>xxGK_y*If4KRg8eP_iIiI64 z7&U9Y2qQD5^@UQCLi}Kc-qcG6b&D%O2`G5_`;+^FHuG}V;p2E=WkAiI#QdKE>NV^r z(C4SlX>*~8e``KQxUg1(bbPyx&wcf~s*1D}EOTU1;UpjaRugpch2r9J0bZGs_a#VRO|Pc1VwE&_U5+(g7Vqwd~rUUVdHRm7_~VhPJa)JUR45 z4b$O7QED|&PW%-!Fbr?;g4*oM9%bi~Ww9({&?*bG{m1v;@6Rt+_6IRsbv{2gnOf|Y zcpj%0z%VylPO|LV=`3?KDMR*N7qFCvc9{&4GV0<_enxAX)Jiks3WpL!^%&4OQ@whh z{4u-i*jq20037_;!E<)zxAulqtZc~ahks)7*-bY$nK5MWuIteNxQxc(!#h68OQ~FH zs=>O8Ecs_jBR&;zT4QEUC+j%Pu-53eJ&Mb>kTE1F75^^t+@e9^!@0H>)@O4*(tvb@ zKIISypT@?{+6j$1@%_=rZeateH30aqbC=HG&O>%Rzo8+#82i`CSNT?=zgyxKzs6a0 zW}Y)Ng#2RPM_D&w+D|phbeXehs3JN?%Jc6vyF&8Xg+LSk8&Mmgtvar6s<+))rNX_p9641&zai2<`&ew#k|4xR} zoIJ*vT+U9>cb}H|*aFHws~}$g)VhYYvvRs+uHYsX*)xT|PCSikzU+Aj)Imngg5Ubo zDp-EIFs+S;7jbQZCI<(Oe{V;I%im5`&De!2 zE9DmNYzj<|BT4f2H|KTV7FX#~7D=?{#&c`9^FiKE zQEExj>2DYHF=MH4SgUBdKj=By(D(nihiYqKaprrAx1SLZlzmhe?x{hoL5LG>`Y5&LY8_Ds) zWNl|x4jf)6;xq3jNA)%BBDm9@0rFFQ0fw$Z(Un7CvomZfZY ziUyy(=5*Bzn(94Jjma6R2Zr$2>lWgR4hW_)fmxhc=3=0Fdn@NMwCpCV29pTU!Cr6v zYtIgG2*eceIu{w|A4-O++3*c*hca#A*FY%5D(Si44!#dl-h^AcjdQ(<%YP7Qn_%Jo zZ$;#qTGN(+!ta}=XgZ0))I$VmVk$YID^P;YhPfP1@6A<{ub1@D-Hk~_e1JLS7OlNr z+MV}KWJsAmGL=QvsAl+~scOi>i<=P4P&V=>8tH<;DirxSt3+LT+QbvXAU(U& zrj>1h>GL0+S#(1x(Dlzlf|m~+^)IT;`xGO`YIR}76cwlKQ8m**yF*h2XCFq1+O4@* z2Z;Em&l|+SosQoj)#b1JHe~B#Kjxm%9>}-h0N$VZwXvTUc%i#$y;hgzdL1iT(@)r{KHcVr#l|Xlx4!$v*20Q!ukFX0wJN{ zNsC5w>UR~s>O4{y>+#df-J%J9kD_v^sCJx8tb2f`?szXUo~=(*87N~J&<1Nw>&SVn zrB$W2H4mD#CShzE403N0p1wYWHzDyL1H#zrkh5%PqUPIpXqU$p$)|bdf6qn2K3(fp zz*EN;cPqIFx3X4)dK%L_ZOs$g|AmF5&sMtsg;x=%+PX;hE;TMvp*Ulw1Jh&E;IqIR z)(p6-??|7xnRzbkZ3`W&$e^P|d2EH!_5s!k;0FH&xP@_bCZ)%=RV9>YmF9giXhJ0B znX!0!Kfy;z{*v0cj-kSzEX=uve*l;T*B~CH*B41Rs{aAd2{H@1amS*_;a=C!& zumih}%#vV44oOR#b$e3sDz8KbIoepOct-wG(c69}4IM&kj2qSSqCBQX=nHc)XRn20 ze>)KaZPMg;)5XW)hS!mY39}Op45f(bjMa(T&ljidndrT~8jqQi1-X1Vi!u0L_w#Z`Il+R<$C*cM0SB3C}7 z$r}6p|3&9({Di{jeem_;-~8|VLPAC(`+53EWyiN%-^kNPtRuX|>(hMyM;Og(I4*QJ za&oy;2TtP6bfO3}>Mfm}%1SExlY432C&jOM{uDYUAM=%X_!vHJ*VKez0uilxb9=Gx zx7)+BM2m5i!)|!qo0!0axkS0liX5$w#KmJ`>Q`Z}h<}}B6;atwcib~<0!q@r5tH=Y zlru-I(R-w5{QkLI0qNyZ1j;FCcMjlNdsZNYbC}SQ5&qs!ocZ5a39ZSr(3khCb0i*C z2`v4>nnrJao5ys19fP!1nDQu*_2I%r(?&k9bA+;XK!S*&yY3 zrMw?Vb|lYW4)K)1R|8CR64p|e-Z0SWT43>L`-@g_T-@V%wh<|ry#5|)Z(EDCK4oT3 z>Ggv8oda>FLo2|Oo7JK9-dUA^y1hH2j%fI;zz9FOoFcK>75)sKh_<6;I00{cTsDYd z?mwQGP2S10KPC%#G~Oe=4=yh^5n{!uQb$XJ7uB;x znK!UB!gXJGqFXRdyyZnxJcU9mjQf~wc3n**K!V5ojRFZ}ul|}~?#I*<9=`kyYzo#; zK|$bnj3Rd5p3$TCZ~A?<;dHd4H!r~pSg6h1Erc5K4>0x5aH357ZY<-DQy8no4SP9~ zT%2e7e=s($l26JZFCje8CtaULT?3!L@bNW=cM)=Zv0`a)bMkM;N(z!l5EcfK47u~> zSjRg4V6ek<%%$>9AxV;Hc@qTV4?_I4Eml^rb>DKqzvemTY|qxfSg3HCTFd}S#-IUP=O z)V_w?Tb2qj5_fGY!CM!&59R%(4MO2m;d5~DBKuPyD#y)03o5%k(bia?)}dL=wK!t% zjwxwwrr_j)#({kzRpw&p(l8wYKdp3JVt$(^`=-T-xD?3KHhFv*9!L}%HbWYCTa7Gk zXlb2V$2@Jui*OKU;j1@)En-2(Jc2zfCa1jsp{OQ#493OvzKt<9;`$8+xV174u@?z< z!(*oM&t+u4SpAl;nH;=z*NzW?07ohwzoIEhL7@p7sN`KaY&KkP1Q*B@;^hYhwO!fY z;A=A%kULwaXl=J|k9zy3G8%!OIGJBy?_qtz#huvc_j>jIeI#4&N5z7jgyKkrMGQ$v z<+Id|i$>LhX;(9+H$Lt*jFYWDx)0g?4FSCcfjECD6V7`!AlYGvby)cAI&jUkQQFYA zX1|LB$XR_^%YQpE&u)1cZj?&iF=R}_j|93V(UY#UB>J07DNM3W6P~tHX;GZZIO$5* zp-TIx&Jbal$+MpZJ$Gk2qkTNx@-63uypLiSXOcrV4GSb7UNp$$K!uEAtIk&z_65^>gBpv z>z>MN&L+?2j)#!8Dg30>?0`FkKS}gh1}DCQnB1{<>7`|}xQ1 z(WjmL+}ZMG(~gmEa#3p*Z{j>Y!mf!rMeRFHfrc$Imoo`6^3oPIDK|Yuuh7>HFDtpX zyeg_jMfeB<&5r3i$>HZWSL7cs5)znBb^*U9aKNFv&@LGXL+h9(BReU$ zRk`JM(aSQA8ztmQz{R;Q4*sLp`e?$;bJk$A@F72G{9Ba6~Aqujky z`5B~9hc1AVjNoOoQGCg26xW@^TM}X_dBLgyD<0><44;}9HnvkA+V)wJn%$=Gsv4OH(q+l-i%tWYIw(9NRi-@@KzNmETPP+AlPAXMAylThL7pph&U#B;v`*_`MI==%rV^4nW5{x8qt1fV-M@HGs? zNDmEkzBschnE+n)ihjMBWe!4APZ)&HiHhgxh~UL9rIg$J}7S{|Qrff{wUs?h|<9=q?(`~$|0p41iN za?m;MHAW=doC^yO1?)DqszJ{aD-d^J54Ev$d*oYDcaHSq4R6X&)ndtBoMHa%S=qi-TltX8)iE$Or;%kXO& z372y(mxd3XNI7qG%$;pT_*sdu4YwqpN`4~4<@4YN(AGHhQJt@M_b6P<@damkxBPdGp(?_uE$8P(ph6VG3r2&OTdXAvPm$k?c8_$t?Hqd(zzm;H{i)_^Y`e@g_?o zna7a7aXXyfRSAH14wbg;V2jV^o(Q;n$);l zRW)cBX;>Z;FJ;W-_hu0R%C9qW4U>U_Qj7S~qH#}- zIZ;OS$$Z6L1*}hIea-B9g?pSq;Lq_1tLnV-FUz-^j;B1_J4h6I+3d&wuJND09lpWG zVgb1p{+-8e%0Dq?`NJTlK&<&nV-qp-cElS9P^Kazh#%56eSA6Ucp?ZM4v#6wlLZ~H zP4^laWhM1DIT#W;;``q8DRpA47hjJmoP8ZxC-_(dgvah~?kD8B92)=}IsV;VN_D*U z#^E)64kPe);0YVNR#ib1g8nGkPCgoMQ7zd(E56foTKovi13VRPNII)tuZO))McL~F zbZ=sEQ4pJEnYr?ION~80GeVw@1_X&-rXK$ezYHX?ZHEnec$kMV_{C$3t3p4nrElGtLwr^^OuqC9L z@C*Mxrvdj)qGoy8$>yLD%qD+dJSiiZ((0(TRAb+J*ttlOKB4q@jF>ZBdk+G=bp3eq zl&)xo@tQ@b`GedR4-aX*3WZH%Nnr)=Iu~FJpVY*$KVhX73O63Ra9_={uVI+5bKv$o z_^LejL)(*`3S_0g$Iu@O_zit@%1Lg}e&XH6api+9aGH585ZXor-$ja}I0vQ!;sM1v z*d}d7*Pw+7rTQy}hHf&eD=EQUd>bC~O7*AqVzn>NHinFO-KoV|9UPA_xP%%m{CUAqVKAKGHZusMLE=RU1Iu=*8FSZka z)rWN!8|uWJzy%s@<3(_q7@xTxv!AN(jCM@Fz3WxMU?_Z5R&g<4giAfbn5i=L?PEAJ z3B++^@cq$PCF;ns#|tedd`Y#bC<&?-2Ri$zdlI- zYpE`6rEnZ!g%KoG^bMna7iayZ+;gifEcii$fX}Oykc&;>y?b?XdsA zQT4erL^4e(#n)9L#W4_RU=A!9kMV=^i~t6w)6+!tI3&BMl zMjnmF$nxCK*3t$k4FwS3Mh1j@ha zsw!IyFL;}{t{7!Jrhwnauk8xgv2SeY_!ZV4%liH0qNxG~12Z_{jgg8!xNq5&yObb4 z3XJs5w5Or>8=8kb%i1J;!&esBwLQq!aU!GUZYJQ9_ z^vQ!Em$>E*i$Cw3$8?zL3=X~Zl8SNR?=+vwP|mm5t{k|?#IZ`FSIcs6KY8G8htuAo z*%ITG1P#qawiI%OHCMmaJ2+f_7}V8jG!P8A@*=m^PwY^cqlxp>_0;_BDZCD(zI2J4 zOdQ~M!{kF1g@Ng~-taeDgd9IesWpIG_hTwwhj_`&K;hKrR8}5ueC6WdGPF(wsqpM)6vI(Lp(%mH> z-Cfe%-QC?t$fX+r=@d}9yYteWf~2&RNXP}G5x@2O0q?W#?#|B6&dfQd=ed5hgsr@D z2i%N;zvMcVZc{-NA z#t`W5DZ(b;$P*I)vY#?S-S%0XQ)OjI11B}=Ib~dH+psEK-=L$z)Szs$!S0tUBE2xZ zgDcS&KL%oTjCI^H4=CO4OYf1U9Ka%v%jZ)Rme_HnZbZ=x`5$@P<>Nw1QUYk(*Ib@D zFnn~{2vOU+8n^N7co+7m2~GnrAvNG{>+dxw&K$l;&zKXpmO@^OT5Bjh5&|%^!d#Ff zH+civ_PFS_A#79M02b>cFkKQlmXoV@R{{}Xp(~nZ=kIypzWQj z-NX|4O6UnkszA5+F9pCY^P+(BpsVNZSFsL!9XG=rGkC;zX3vQ)OCwP#$Q*SI6BU(| z($&>d=(AFrVmpB5YQ&`>OU$xigdMvO-iE1?b?;o&&pKx1=s})CeB=cC?akDOJ8Ml`0z0^SxpU#OnfEW^Zxc+we!R8Mro0lHzQ)6e`@ib5G=z2q(> za3$pnX=Sku?URXABX5c7RT3bCGK|h-E1|p%D_Q!9naGG4`V>c<1qFCQdbE)D<(yW( zvP0$6fh(3DIA)V`B$3mNGMmf32)&i>4Hn<>g?H10 z!0uOp&r<&`shA8R!0$s4Ls{oe32yHopldOOQVvK~>z#+5`#q<0815#QW!CO&(l9-Q zp6KAXl3(Wnh9j($!^%lw!@b&4%k8AJa zJP6M_@S%Zs^~jVo1@bOc=ufj$oHTJ8pqE~lTJG?8_fH5$^|=3fh6xd z+hJ#;o&w|PW9%`ZVaSY^8W>Q$xaUEP^@SA8rKIyqfKnA(YA4Qc{78_pC7jH7*cWv9 zEY&F=Kj>Yc$SRr;Z5<6HX9rc!RcX_M#SmZ;CFL!h*j&ytJ4vVOLXs*f*emp`r71UQ z=USetY~#aw;k6Hvoy?n%&Vx3@zs}x93b4-41I%<+*+&)m{#3yFqVgwsKS#Hnd~8Jg zw@&tjjDIHNe)YJmG3~p$N3Xt$7zW7~o`t%TRza(Ts?>NY$eCpj@MFm7U5s9%z1hF? znqYA3cyyxkb${S;db;p(I7Cp5VZVM}_>taa2G7u}GU_CO)!h z=A{3k0EO2$lCFz1?8UOc14t-8ju%|Q3Djqd6Fjs7O!6r4_Y33?ntwW&`u#sLFVp_L zps1dHuL^7kO%yVFUeXY5sWsF6C@RUYYV(Q=2rUg!vSTGtRFl~@~w_%FfvLSN$=4xpx8Be)@Ab=PJF zfG-JI13;JgkkHpd*|1K)5ck z5(laVa%x{f$^Kbv^DOoET}qWJ6MC8kcgaTsYq=?R#wh3lsK+B3UbGvAYGw!3=G8+o zeZY5T?XC{(;l3Eaf2f-}hiIzoNJ;~hHQFIWO^DFqeuS=DYxc&{^R@-O!Vd*Vk^q`l zH5~$I&3WCi<+-}3AJ{kj?TYYGUY&WGoheznYiS&c=u_jOiX!O2!U>aOZWzku9Qen< zba*#*Z>gBBdAFP}sc~Rl3KsN{L7<_@NONEgX#{&m>lfdMSrle?I*2| z+X}Evqi=&~eU_OBTCuKP+F&I)e9xm>&((<2%mWw06{7izi{6Rt&im}QulAT9t5E)# zal8e!*Y55{O|nXq>x>Wa zzik;P;RRCRhYpH^=;`T4*hi7;NSovKeP!an%20%E9F8*5q{N9&;8i{8pI93CZHc=* zB-7?Bt5FGVQQqH12Tm@Q3HAF^GI^Wko%+cR>bkQz$HfGXeteUlbr5E_^WiAg4By1V#&4|ehk_)4{yIwFaN^7&ssmmNvV zvGh2~p|Wjw)5Qf#t#0%CkoSv{uunPL+{;}`J5?E$@j8E$IX69MA^T>zvUVUOMMf52 z^cv;?R$4i9Ig41GgB{G*QR^aizy!5Co#Y5Hq<R4TSf|_g^>iyqJ3AXyHob5<=Pj@$;p_qvRN9z}EDglvr2TxW6LCSJi*StdD=cAnR23uBNFu;8w50 z+7de7Yw7*1ja1W-BZFr@XM=?DiWp06m-hHS$5*w-3Vz##NI?^JqgJhJ0Q+?e;?VN< zqxKjPSU3->6B+u|-mkwk#E}iz;UE~hp7doykJ#sEbdEaFkLaeGQXCk< zL_-wmw?ITV#IO+E4AEa(YLJhqcxA5;U`RWQ)JuvFeVCP%x`F}%PE35$yPG^c@Y&hX zurzE_=AYGZ$~qc%*NZy^-dKP}%*WSPJ%3z`xIE2OoPVP~04-7UM8X$K16$-cpS-S{ z4bCgseWRHBnZG@u|BGF4E>|a}XMGFY8Cy%YX?vU-$DVvQX8{WMPZJiLeG#RS`zK;_ zQ;eDot;HtjY&xJq_Js$mrx8Pjzupb8xX6VUs|rV%F`q{+JS@$(AFCTsZOCN+QLm*zDuPe@?-%P<`o79ka!Af+Iz!8XcV*Gqa7B z%t7gK)#%JzEmu>Ef`n)+zV z0pEv+cmPB8t@zNPEy;X+IviCJq zMUGUE16UhA&Nb;yU_9KobiVKx_KaHss2oX$yX48GBdp!&t>UfFI_#wt3I`cqBcCRFXXo&HvnF^fdzVgA@*KytqdQ{S=%+hkJCPKjf#3bJ&=CaK}2V z5GCU+v$^!JOt6iFmgI~23aQI?9MX^ZA3%U@Rm) zT$oNU#j*FZ$}aQw>)F*8zYG2uDiLUjUo{U~f^#C+G%tSL1Aq1{>ZmKPpyAJb^;s0I zFWa)ea^L>PwWe}gH-}KA{w^%iukm0Tos(wWBTl!jkWpjs{ehPXh|QI~9To>lTA?aqIojyGfz4(vD`I@ z3-{i_Fi&Ddg76lqx=N%^E*R`393upHZaR6X!3;p7U-FG~Nne6G=mZC%NCWiq#7za>=5lT%5NL! zLd($_j39B4Z67F-!$}v^Q~6%QRgkrRKhgTxfd$MjXWOIp8qNukTM;YIL0{;xqPu`H zRiyvRk?Ts}*GuwLvUAa3ojm6+UU!-fC#rhj9mFaABG@XRpdsbp<)@KH#~8JK|MKfl zYpT4=a$C}0q7iHKyhF?6c|9evGdh!Agz^2VLa@#MX9riTXZC?cpz*qHG$Sg`Z)EX4uNRy^u2+DV0gP8sDqMNjx61X~F7CDHe0~4X zi|0yCLgA6a6MViK-XD|8nmn(4eQ}|KRfJGn@M4s5-~bbY>&TIm`D> z=c0Xv-l;B&CMi2Viq^LYKbk5e%q3i|+uB$N`01R5ih6-&iT~|6n%Xn~QApUjSqHM8 zE?QjvS`=O_@Ugl(m_J_Ye?i~w)mA(- z1E($e3Jz4=#H!X+E^TmC18<(g7;Nxz#p>Vx@RG~Dq_@`${Q4&a0P@KH9a`z0l&=~l ztZoj@_8&j6nMY>c;tJO}bR!LCku@SYapQk@##OLO@&#~7LB)z5)p$h@8THs%Wg++w znoumsLN=C}+0M70Q2B%jq@kM5YcmKR%Y0wdF?{OEik6Xvy*TqFfO38NPPd?*64 zG&#BPe~^GJ>qmUND^x@Oeg3-Ytv&5Ivrp|@0EiI+*p@8+y3#!2fR3g>9mtnzg@VnW zASO}G1jdFfD}0;4B&<(TWv`_p1!n_)9;L5pq&x?~R&+JH*ELdX2<9h2{&JwdJ;yHa z@Zw>ySl!K?H%<1{-UoM*Nl-43=B_X1kaVI|DIA;MZg{<;1|zRB_!Wzktuvp`gwrVf z$nd4Ddi;!K0E9=g`?W9Y9w&VkKm1Fl%WFr<(0kWFH|L)G)pfBbsR-x%-b88PRI*C-aKhEamhI1xKu;L>sTBFVbs574pOVH4GD9vl zEHj4{bi+00Ps@Huy2QkL0%ZGx2t!5abp|lk-HCIBUpis&S>qrrMiVK6O|t7*V>Ei9 z$RD!H6ysB+T!3!tC{j;X*Npd1-7o!j94t9@5?8GG;74~yl|h_pgD=Pb#)oyjZd53} zyVRhC@GVRu^tN%L34O{ALUOVdPY_h*g;s3w(kRuGgP6NWBLj}~1 z&P%nL7M>XLAKY|UU*zwcDU42Rc|drhpi729^8sx}zq8qHi>8+secF)#YQotMBFscj z^mY~S$eqK5GJ30bC?6Nk<5Vm4D+R1rbDGXUkCXbjPLO`d$i45p4%T}RZoFnZTCP@I zvxuu|OCk(R`x1pENo648t9ty>6r0hl%6ym#={(Fhys>~)jXV3nN340+6}yIu%OR0w zs_sGFUwT;7VC~X1Rt@+rpR7r+zI(;+vt)lA$rR|m|J1Hek$u$PM>_02Fl=q}LG&Rj zw1NM8-i0ZIrRJsM@&!ud){#3Tq?#W8&)N~OOXwXxdo!Jzmpa;SM+2Pc%Yddh^Dkp7;@BW|3mt$#kvJp6eVe7nkO5o2e zQdv1b%P)S1&`6NZ^_eUyS@75QWYP47VJc2HGHrGr`d#_Z_~5&&{tZbvTv1TmGO^)n zKY4+8P6@eLG~U9m5+*)jO3Ze1j8hODp;Zj@;nJ-$$ud3ZU%xK=7X?Jar+;a_R#ap? zeH+v;M0n=ZqJXWO>U=(Q*QIf((j9nT2yjt!EzI+~jUW-C@%2Yojy|{(N|GAcGVDQ? zB5aD|A)OdCnov61xwd6^q+xu%Kg{?y%Xi4uyyfQDkB+knnre`{5No#pcAz2gcon(& zW#DgU0v_QIbqa$>!8bbA$CyP(99LKdte9O1kR)KKKR73ZvNZ*b=WqT~9KgasmwvX; z_NR3d8A$es%e-(U$91E!bD3ILx7Lc9?DsHUfTm6*p8I%dI3Iou$PHkhddmt47Kel7 zgTjfljInbFJP9cY3vI5C)z9$T`tc71{!G8oylevb!!qhcZ6B^x-|z3n8*Wo9v!ZKV z3ZTn@)qELUCn^KZ3e^Z6foQaxrlS92?NzpuZ;4QaGSz!+8_Y?*V#tl`WWCPm7t`4B z;PzGRq)gL@(CyaeU`#3Fg?_|UA&8v1F%|{mO8+W59$SCsr!QCnXEZwBa!e9OO?1-P z#LHIa-8j4Ii~(A|?cskK}fuZL;2cF>YGl@lW-2s6e zkqP-1ll4UgR%?A$q7%|4I# z0{Q4t^1D0=pnl+M8#~*K6Q02x%Q4xzm>z02)zpE@CZLRnAhwenG@d4d5_{8ScLfaP z$kcuiFeD`@7KFTc!%dSNc7u}F`a1hJ(Vj*`da!%3k{(0p6|>?0(;Z7@-E3;nou@HW zmhy4WOoULD&?IbzS`*L}QueM!UvzwlVm_A-zfkZQvT)g@yixPyAO_1TZoDwMX1FgS z=pRpCO%^{dl2@=Y_R%6(KZpI)@jm+_8W2DA2R*<4^|e<#hv(0Dpc z{Xeqs2&D|C_ujI*!oXz3Rg2y@v7F3_TT-eew30&EjP@(LcMMam_>#rxF=+ZW$W6bP z?mVN@%h&2B4E*xm^Ag&p`OZ+5DW*ji;?p` ziFPo&AsuSOgu5XvWCAp~gy`ImztX6C-`rj&IQgfy8L_QJ?E6`4HiNo?g>=A!t^SVR ziS=}3g3ZdS^)>3vlXK=%JUq7~ zApzTkQ{nfyw532IZ2qOLPYk_psw8(njd!oa$vIAa52?=Iw67e=242Tjzh1hLJ|0*A z_`R1+DM)S?;1jz!oqkNS(DVs?pB}^S+w|dGhY%r|{<{zEl22i7e`$2xb4h8L4t7M( zCRW7YvW&oF8HQ_(nVR*FV<0iuUG-BE!WU8C3DCyJ=G3vZW>oqpizx`aHQR1rd zxQ%t1+^FanY)(C;Tm;6PV5pwyPYSQnW%}Iex72z~`4_}VZLe}dWZLYh)Lfic=5`AF zIu!i(;_G}q`^E?def)+G=EsvSTyvGc%jgleH;%qDM{+iAxP|W&fO?glMmHygK>q2c z$7|O`gsoa1n=X!AUIW|)tbnp-Q{Mjr`?b0bibp9@)Z93)#`c0?)*qii-ce?iTP zD_wVquHJ@U{m#AxLfnY0Q`N``F_-;{%z=R(bFqJCM-66r=s)PbXgG0GE*GpO9GxS3 zt8)oW;xbma!Zz6JKwmJ&-q})(JQfT#c?7ul3At|Mbxja&)GNewk|$6t^QD|%fHx*I zIwz2aBCoj~PlORd?SeZPn8)EB6ij%-#1r{Ln9RsOlMZ;X0$eB=r_w`Q6y@)mq40Lb z0mRirHBp-~Q{j378_cWp3}Ptv+_DP^8{3J;G8x|t*~lx+em~_}>!&gBhJxTqL2WZtP`(fcI_jEHZOqdNU{ygI4+yGM zR3s-;nW{m>(&wm99-YRhc9CE-9~-k;h9K+G!%|o3zUFr?#+}JTNegPnSi)h>CR#s^@GXRXC)67`26ec}mW|GC_WF17p=Tf?{`*c^uKc)G&I! zR>h56@au2jQ)tE#*7_YsXTWRj&r5WOd~|zS7bDLwUd@%s9K(&er!-M=S*I!y%jf~j zJytpBy9_4n!MZvUxzRtnMcNcperNxSSNfCS zt*R6W$i52IQ9J`66|t5zlrtiCe-IH3U@M1+lE^+)ztXp@O16$@ZyG9Sl~!>glh=*f z?dy&@q#B+xHDnvYHiC@h+^1nSh!S+ZOUV$NnnkVt*ZlCtdgLklOrLpB=+QEU`4jd# zAkif~M>@a$#e}xtLXGiLDJ>acm*9NPHQr@KVg)&PwY(aS1L-))fsIx@Rj1Q!k@s3^ zF-|ITTZ8rUtD1GB3_jF(Nr_)m5XPCEF%#o;EhXLj537<4pw$0A1n}^zpi4@Tv~+0C zgdj0S7MBsgAxze@wdcJu*&FvBuvQ_1et;T<1EjNX0Gh_P-PtGua8mGy&Ig6sikS2y!T=znoeTGvfM7 zACVM>ioX3CY+*97+R8cLMcT`I7ei)R)Dpa$1v2>UIj?NGu-HAqGd`3Kvv*gYOljTk zuD51fuPD4F{=W-hKu_Iu*-#JnZxD&G*_HxMTzSmmLL@G6jSzZpzKbqTvOS7vKJtkD zx!Pn?Q~$9a|J)MMkMBkDtltqul_ZgnLlD#|)13wL+srpNnXxFLF#%)(y&$ z?=ZYTz`OJ9vobQg(kv1JW4n3{wK~2_-Ms&W=T$Ng9UvG~n2~IqAhUja_N?g5WYp1$ z;i}e?H^cw;Yh+bE&#+H6P=gkS1C`|olsr!Lt-xp1XR@TkF1OXCUdF=nb3wTIf(BaC zknEUicU>Nka7Px+e!Z}9*U?xj3(zY-P_*J;(9tqx&8F-j< zvQNdW(R?bLTJz4f4RuDp^`)_MueIOJlv7s#_;5^ChA{jaU`<$Ge43MQ&Nl$H^QN|>IrB~Is0nDWJo&m8^ph`4fH zM8}O(AZ~h2E#w5iEH=}cgHIi1au-sCPq~J5Ib#Oj@fYwo}1hs~)>JYrOF!UapOeoC7M!-!Khz$0{_Ifd=27R56RkT%oq z7IIUBmC_N8AJ)NE-c$2#*4=r5w>>QBD~T1)JM|1(A&5cG>lW&(R?zg-F~HpVh{MWp zF`lGse=C#YhJz{upI!NMLDj`%XXooow;3}3FMXlj|GfHxFG*r`jMukU>&kR@vDt_` zW(+4ZCJ&(gSeO^*R(r+af)MUivSZoq!ZEr#iVGi%i=GC4ngF`yfcI)=oW2kjc#^w{ zC~`+h`K$xLrsq|RJ|Yg=%x~!tqF~LhnU*m<5J|LS9o~2fYyJQ5=S%Ec$CIU%IC6Jj zsEwuy&)1OhVh(vO427HDu^f%7PqZe!1(LPUiA+BeI}|zJUpx5J8=10-V40)vw?*Ue zO|hYw;aZCdL9qozodvu_zM|ogWNF4!bE;7Yj-N2u_}&rnkXxl0_8)ybew$nLW}VQ0 z7z7vMv}c5?VX-Bu+GjHZ@Z)d|$@48YXR%L2Um}FrxezqitFMMgRIYsC5 z_8oO>6BI{y17uz$ViMwcRK@Wd&f$g;#^DrVu9r4@neC!8E^>RvsF~bRjTCH~$*oVS zm)Z78{4AqJb6?$kK8KKDJ0`;&L<%^QzgUQf6uf9D--x3N8)x&L2qCVJU`5=S4CQ-3QPI z*KTX>ZD>A(4c}fhqCKX$zYt!~ZJ`_Ch{*E$yy2iK+=)j|5{Z*pD8%p9aJN5+>+u8M z_B*Z$k-4MyGONd39+O*>+XDG-Rz|Hz<|!K*JM{H>89QEkE9?itoxp^l z3601HPRD6Ru77=aC-IAQJ8}jb9f?e{4v^6c$+Nk?$G-U*92m0n)*EPRbMIyyl~?v# zn+=X9z~#&!Qx18V6KA{7&U=>}OKh_`6~BvjuQ+&E^sozYEnJV6kkpQ#=gnntXF2uK zs%Zas*$u#JkI8w3oS3!PlF<^t5f<;v^ssSnPI-uL3*);t8 z>O2zEei{ee1@sv{ghvMCUrFNlmiVZD`LvB6AC$zN$|mEc2(AN8B7KtY+%DZ|P?Fre zBJ_~Y=)ccmuULl#%@N(2vCrm6FPn8CC2QM;sktB$uStB%e^Isx=L+gS(E|Nz2=hEq z1gQSAPN{}1{cEx>>mqM%uVl#u^xSg~?t7t2{TzNDb(?>q_e7=s33vTe24eG(mjM4* zX{Kjx5H({S8NX1C+FE~Ftb7@Xn@}#07E5^{$`~notNsuX<-0;8dy~6Y@=!&(3^n5l zz*tEHK{_3l1BZUj?K# z7dvNut|F?;H{TKvK;Kx@%etQV*_|I|fGlf)!A|oq(!datriLL&q-2jLrc;f?HNW6H zJ`sEFM1M;dmDQ@!Too| zhb>-y=$MlU+U|RY;dat0%)ImOl6B(;k)XlhH_9eqGqPz#!zuiX{cB#vu#8)^<9Amm z3f$vH)82)Ji0Z5B`MynmO|ltdq@Z*b8#mDFv$NZW5hia6C=oo`GFP9E*{eiNz}~8-2jAhcUF~b`^Zym+ z*Tq1hlrDPq+|a5LjLn`quymYZvAuYL31~e{+PomJ!GuP1V7CMD4!v4 z2VPl^z3@dv*X{;QA<@CbB(1HdIakh{lP0n^O)L_Q>My)GR{>Ky%`|KE*~shXb%}K? zYA~$h71lRjXk;N3YTTJy-EiwHpwJt5l_sHItN>F?x~GT(LsPNYA&~yu!VhhJB@|8{ zSeS={&ZGG!uZHathBB;lmTca+XzDf%NoAc(j*aB9xUa*wP*|5)TAZ#k@6*yGl9__p!!+GWpWiQ3gy6k5Kngpolx<$Ki@Hov=6XC9lqU7YXmn(3E9fGqgx#L%geb2R?GD5tD8XTgx_aNqg|_7R1FF6hh)umDWaRX zAJ5jd_a`AEOGi-Ocr3}siKI~wCshHtI$jTi_VqEKD#goQyf3p!SW=IjLEI7Q25pSj zo(;xwk4H%T0Y#8Rnxm9F+UAXCMMs=tPi10djnuvUG8=&)u-1ePX{Ep4Jd}lFOuj7{ zMfsj7-S5+Hy=4buxk@)isW*$mk_?FWXnQNEm4sclnHQF+9V$QP*-Rm8*zn`wISHeN zN})2YGItVA%ruz}QEyt8oY1aNS?Y7UQSLIZzh7vP@~P?Uv8ixM2@1I15XEY%R>#hw zST&U}Z2&UMX;eV>HK!JX(e@|FCjZF|eyhK0;5VuHCj|VnPm}98DWJd(8Vg!Dl$3O6 zi|7hk^S5brVza{I#!N;%eiUK0H(_0+l_M3QO~J;>F*Y+IFO)3P{GEtdSUUQQoyVuZ zdtDeMIY==|-w*?%yE=dqp1s1^A&=P69*YcyT*HQtYahtly2LdLyC`=oV~viCPN}Ps zRV!|>zahR}9$E+ALnE?Oc#yHPF5S5UIV&RchcILls`j<}Vm|ee!jX1Njm6pe4$o`! z9nTDpyE+7;Db4e(x*fjY;{!*7(5#w%3IHiul0qXRbf=18r3Lr3V}~V&{nE@|SqL5neNpOrGFtjbv4v)`kpw zm}w~x7?Biy!g)SQ)PObjzPHH5;&iYhOmg}2>IHpwJOYoeB;XRV$5{AkN|zfz&WCWb zI#wChhJ;D zg7^4Z`wdCbA8WkY0oGyw)vQJZ*7MAjHC4yqy#WFSbxk=e7a*95a=q%}BW0b3G(Dl& zRCtlrml|Gw4o9Raa;F<-xbS*!n2ZNYEAa=GMQ(0_-3XRmvkw zvqZVLvdClN)FFAXDL)P_Q(uIf(PrQ6!F~)=P0oR`S(08N9PWh0ps6~63-WUKQsPhF z-+DlxDbB%U`RC5Lmx}z>!#tqHDBD+NO(8baye7U{{HW_aacWn>CxLVY-C}HXh{v72 z)P~SAf(#4Pi1Il_kG>zguD)+ZmupLQ!O95Vim$0Seg5gAAYh4ng^r#xF`gxJl&jM^ zC%r7IX$tEp+t=Jt+$p~vrJi1|5TAL=Pr`bk|n)^I1$Xr(PFmFhKa zS)`v%T(iIYG?;)lllZsXyhy16B*JxgM6Ta66($%PS&y;9Gm+&3{t`GB#Q14?RW*n_ z`|)dZr~c{t;DTH;kDl-Q@OX6a>%3PSdg0sn2A+cYF4O`b&|B4atPj`Qn}Tm{k6s&d z{OGI=6UCWHV*objvU(|Tms+P~#*zsSFmH-dQ}Z;C_n|-b6;!7_R&VSda9)*c$8&Vd z8xcz>B;yHVqEtmj)3`*r@v>u=O$bTyvU*y8RD(<%D$j7hR@kz`Yf|ZcrAE-GJ@e6_ zzK8kkA1zh*80gvECP0dyD$OxNC)7lj?h#zIGwM8xJ5{p&IV~oH&jf}q-G~ng_~F?# zqVbI#VFSo^a^I;|jfzvLI=rUV$wQ#O2<>qW(irr1*mz(2Zpk6Khk!byHXhWaVYY1> zAM+un^Iez>Jk`|(U#rMRTqrSbL8uVQ+_lK?s?Tq|9)vQqwx~5~1$sBKI35`7*uVpd z$_o{!5>_gxO+;5^#M9QwYXgroMOl~G0GKnVa z_B<>d8jWu=*CfP6{?E+5o#BR(1tu5Ck<2|2dv85pAbs1I09y^zMoJO-nS1U5_i*7$ zH%`&9896S!Zqms{ogrcyFZ<8X3zz6I@{sb2<9K;f-{HFz!u0^^2zA(t?t0K_?0pYQ zK)jiW*6(|#@95m>SQ~nv2@_WJ(xNi=47cRJX+ouHL`>3L#_4gEzA$v`|0E6!vtWC; zv*&EEQCr4Ft-dVfnnvSFAsig7pE@LP4lc=t#z(QJ(HfhsdOAnuJ>N__F>sfzYwjVhpv*_qTJ|NwI!-~IYY)sVl%D|{k z&QM~nx)gR(ZOwDX=R zSg#qUyjQ&5^a$N~f990ptzTTELqDjdK_ikP5Gb;&1qa&1vHw1@8>+)Je-&00Q0d4R z870&#P6gqpkQ&VQ1$I`g%Ex4n;&YNFC;E3Zm}2@ap1;zPMN**IF}u+j3}x9OjZS6o z!A%^RV@6;0;XYFB^=od4rt(*8gRe(~p=Zl}p%!3;VUngpFY77Zl*VDDKn<1nAB!k& ztrw=0P&SK|vhZ{VU&7)3)@y0Wv3J65O(y^708wl@*%S+rC*YE~;2Us~rFBF2k@kZe zG^D@m0Nj#hi#rCNsj2WN6$rWe-pYR2{Lxy4$Z$+MCv}}x%jHVFlZ=tzIRAm@oo9gU ziIN9K{YIppIX^okjX=Q<9u+w`u5bQJ43a6I#KAMDX>LVy=e z1l(WAplCwjqf;3ct#N@b4p7U_5jNvupZ-}lN?=;Yg= zyDm_JdL^ZXoja!=kDCa^hIcE}Dz~)JD2XbGi+lj|MfUfvI>Ox&B>uDx5dMi&rLypgupjeHlAaBa%WA;^!9YpmH@?-rkyn^Bajj78!GOHE4hd~5n)ux856IlpIl85FjY}26LPyx~-kJWTXq}c`_;G?C61gWl7hdVYobhJO zlXk=t@s8xjcl|3~%?ljKLXrT@cKxp2wDC;-(3ZA6?5W6sj?F20MjvVi%BypoX9Ov0 z$zn*HuKw6}9f-E1ZtvfbFL{15NX6@pNV30(ygs!92{|gdRQ8z3lzAZbHRiR*g|SWwT%$Sl}hyI zi2LJw@Z(Fmua(c5iGyM{CzakZCV{CR-eoY!kN8oD)0{C!FsOkh{pQZST&K;l6kl^ZT=`}>JudOiit+z-~T zO>m-2nZ>0KzTE97Dt#I@VE(UjD`P@;sWUF#cc~}PsLmAEM+u7I4n;&|wR%~`FgjLN zBVi(X8BaH)G>li8HV;*WDr|BEgsDsp^MOifH$y!qffFIag`*P3$6O8z`JrwuECK4P z4NM@!Tuv+9ryKgp-nd|*Rxu@gjKWaO1qhtJ>_S6z&oI39`VH#+sA+%Dn*$AwL?@H|3h7sglR)IIbz?E>C?5+7(pG1q^V>Z`{y z|M$J;!mf=NIDk`B`@V3L@}_QrOIBa=IF7z&ivRk8^Kh?ys>abHJ35b7fcu1G|K#e* zvlIC3!3Q^rC0=F0a_XhFJo}&=(Z|49<1{dd^cNurx6>ig%Rs*vOTvWV6REAwi zX{fHC%91Mg%5v)FPr^GqXpBVhk9Vyr=tu6idiE*Zi}BKf%Xk*rA%D7Ngt}9mG54Zp z=IdGV2mES{_TT<4KBSF^P=)0>SmPRoP1o6DT7q!G3_#Guw8Or{YlcnkZcOle0TfnWCEouQ7mzbk+d#rK{>)#*R??2cx3~#*U{rE-Xx0nvQuDO^p`k`cOEPn&*K#L=f zP+|kNB#MSVU_*GcymO5`E;w(e*>7><6M)XeP~U&*7ld45JE{GA`ku3~ysDI9CxB$sJ)S-r%yr@H{I z5vrWL592S{Y6C5;xQuN5@ z+n>2vRIRM5$0F9hT}p=maM~2gCN6pm{`qG$4cLe&?8R|8YGh4c@zn7|DFbAX%}2{V zHt$8R@|&^}*$~!3Up5Qu>{EBQ>#C*~)t7=V{3*$0daLK^Y1EK7=^;=Qof;JMzTf1yneq;tC!xUrF5i?jv;0|;CuuN$p zQQE#dyK8MTu$702$~xe9YNQSvMIF4+TkaJiB>h@RMd^dM65|E)SZ)~ZokduOckf6G z)oqtRo;82(sBbXW_43BX-%l&8I|2(X>_#&je^ZV@#nOzZlcrC_sp2ds;{KB%g@<$< z&!8TB#hD;3NiM>JkBALpU&d|YGw;pyhv|_&`a%y5M1p|C2>y%~DD6t&K{*o>T{tQG zn%FKx^+JpmHRL1M*yL|qT`9~lb3-q(EBa;_*!!U?`G9P=y?58A+nh+n03s~e(e`^rZ7u!bs9*KtCp^fkagE-DU} z5}GB}CwQ!vSvpE&lj_Ey-_OmI3)wc6<54jFLv-@`+Mk-DY_3p!@ivpn%+Pkz&IS#*&%NCpL1K?3q$xrwYTe&%EGR}34$Q} zz%mQFc@-`Kt%i-@SLC@wetqUPmommrP{RO``i$_ro3l=Qqf3CnDpf}mG!^c#L%tlOA?!%37Q-MwStrJ?E!^|xpfj%btnqa71fKja!NASY z+2cd}?wN?qzZUPnLU_o1dsQKVuxzz)LSHpFs*LoO7NW)nX@TvUvzr+2{}NzvB-bvBo^`s$ljD~ zi%hA@$eLafqol2j^me_d*&7#9k!2ofouv|*8bmSN@J?Hq48XG`ubY>w*YTBeD}7Bo z9eeXR411+yoy^^f7^H_{n>0n^`Gtn@XEd-!y56$)q0&SAb@BFG)z;`xm?6VI zPV#DaaZlfvIg7lCLxy*9)$geD5%{mAuMUXvdHz2@I;0zF>5l@;h^~=WxUFI z0}16(r29{Ku|xu$g41BZrVxIhry-wVz(wS!wD;HV4f>~w`KD_P>#>f zu@;+A*_+B|+^|bN;osP!J3dTF5!8^WRh1 z(#KWyA0mOZm^9ZSRAo;DyR%zpANW2j+A_PcbDXSWJAGV@$4BaQ#eO?c%-cd@AWnP5 z9p5r78t}-d6A+(xbS=>_@&y3gobaTVQWB9pCqCjLO$2T@R6#*B^O-lB^G^==S|E}m zF?br^*J?jKONihL_$5diX3OoF)1y)P@zB)bGU)WD1?_=?K{6RSC0HUlZznpP68@_t zfeSCj4t$kT#0^m?mFzDL>4b)Qxa5=XidBerNtCSk+LN8aT>hQtAdt#{d4XIDv+j5( zC>MRprN`;%PxC^a{n+IPs?ncz3D{)t#5B)X_7g3P@^h2m683-1Qi%Out)WBo%A37ou>+nD-eA)&l=r;`=9k{*uHUR_ zro>*QBconntVXZ1EDc>CH>n=Ccm46p?Ox8w7pFgBZpUQ%3?z(2CHv#lZYNwEhd`|J zJJ@Btq0w!YGX#x=g+$QFIK|!9x3&&H=iWR-M3KAUGP}O`{^`HdgtAxEcEe9o(<4IHDIz(S#w`Wz5dMd9vS)P z<9^_l^Hjxq8YHjheQVdK8V6PK3|wdQp?<&sb= zdhS0iVbC;OG3@C->1dTax}HYxU*UiF&1GNkfu~r@%=E1&|Ll&O2~vSclyv;uE9yTD zt*%_YMPL;*?ZYt3_i5Hq3*X}MPsEtJ-!xXOk!D6xki#cHIYM1h#Bj6)8cw^0Lofwq z2Sj6@^4KeWyUiglg1gkG3RcK;?}+An-BWFDy?S zc81YO1v)O-4+optEyat}%U1gc)u&PVk+j>1q`}`MgiA;dxFE5Ijr3nI5~vt_pLXJW zM+I+9d9PM|lkt0hulAtNrTh}seKXo~*FwLv7&IwuEGXw?W<5$aJ-qJC0H@(L8 zblcy^#bnga>iKA2m<9_LV&{mS^^UU_xSbe}SaGwrU(od@{5<5D#`}3KskExI)!e_~ zScbWGaGuk1GNb+7m_XHz8Ur^Bn6{j!rF$Fu@{Df8G5lT;UH zcyL{jh=KL>^pL$8j<2ujzLDmJL-8MlCw{qBKA#X+a~tuebUJ)`-Hoqi#H4(OpT6u4 zIZPs+P6?_M&DZ6NRsB9Vh!pz|qb$n90D|c8*iCdF{$;dFT11`B|-O@&7??sl)jfbx8h5Aj1W%)%{>_5+_BC~-NF(Kg1<=e;+ zdtaO&ksfxiTa8L zcj;)gmzdDsl2%b?`yUSc856Pg-@c}lvb7}Yc9Rr+X8cysq=sO=vJC$;tE0hZ?lA*6V2O{1HzGPik9Q_+_SWyobxWD?(N&GsQSnV>-SXK*ZXS%7Tun;x;#wf<8c)}#+57p_bT6?e2 zLw|vgUry4_glj(EpWfDyhkrK0eTm+jxnu<=r@U<*c|7`ikc*pRGNmv#j%(7ZY2a<;+qi|( zU<<32rAyZb(@+V*&!f|f)`2qeCvRyGmd+Y@*>fhx)`H@te>^(wxu1IXkNH54FSj42 zq0cvlV)7{7GHqwnR}#2`-qV##-}l70?t$7lErLv}g#Re#(}0@%5Sm>un=w^<%3o6! z7UQ7xT9S7?Cmx|3pZu*E@ZD?P2ark`KOO>1bvrKlRZ?WAhh@t}loge_UcW@*E?UE~ zcI{Nx&QsTu;g`=ACudNvn+ncoJ zQf&Dz{BTcs!L^641Qnz45J@U~Jv#>9iX=dPo=!Zwu>F2u7`Z>XNNo{Vccds&r`4qN za=#%eqC0nf|N2xLZ)xr_B+!^og!9+ska`8gVY5`}6P!BwH*ynH$)13J>#QcY?|sWG zThZNt2Vd$;Yeq0-(VhK?Db7Byq+^UfDN?Oa%jLwCfA{X}{O~FIJJ0^%vnQTbkk3A+ zM!7b-5}d`1yrG}h7}yUEEmnL0T1DM7@JiN#q_O`xh<->AEm&r69XAQgCa{$ ztV$Q%H&h8bqs>dG+W%;FgqX@mI*vZtU`K9klfu=6jfzcYg&EZ$a?Xk>!{z=Nm zTh5|8#g6DP1)6R)+2R&d*n95v92QQC{_MjkVh=yQr@-^ER z{T|&+1{Qz2;VVeB-HlY}9nCwQEz#G=r_e7U-&nx1?jbQ~kE+cR`%n zmG|-Ed76MvcH_F6I1h`UXAnPe*PkX^XG?o6ZAfhH>Hb1$d8+%(dCH@gq6dA!S>?f$?ui(EGX(R?)C97#Z z)VJ!cz1TBp!7(7im2#*FLsQU^Z(6s_{8>4^n7wCn*{KUOxuwq&)?16~#Vk~Er}%Au z|4)L-ao@4aPUX?*hYk6?HM6~AA6BpwYH8e zzpg*THys%ypz>yuz5xgNVK>52GWe89n=+(yP%OgJ?*r0w+1X80^J9zdhoA@!e>uhD zJ_0FIB8gM(zpXhY;VT-D*@^E*uDC#IS_!XM0^;t~$TE!*sz%T(Nt>0;HBCTj9^CvS zU>l~&t9?kS*9G%ZcdQMDBra3cXK@c*m?<;LE$M}TQ?Qu(w%s-l+XA20;zYua5)wz%Fr{zp-wh%KN&B@_1S?^cONJHh+`(OqHS z%RKp|Wn|r=4iv_YZK!6vcG2wT|MSc4+SHfPcB)8d|n!byM zoVh9g(VD)QlYJ*8axq=}hyg#zwt@V76W$z)oLLN@VyZg1m9BLHK%ruh)D0u2A2I0H z#fgzQ7sR@riVysTM%v%)=H8P@efVjubk-<_ctd^Y?WrrdY;JkNX9^t$XNvdfhP!F| z^M0IDyW0_eP=6LJ?go1?^@c;R7jLQe!Be1=_bzWFph)9`^hi=fAnI&@N@(H6F-j}` z_j8veTh-f0W_1)lB{Wm4vR_38V6Sue6`ReSs)6JlDm(njoapz`he4QfbPUBji*EQ8 zn04daxWz!XMAM36(v#i1&t7fjSyMTKzSorn^h?KxT0Cnt5Bg^t%Q z?-#Qq%_z=gINfV2IDtj%UIDCJ^9SqibPpZC=B~e{We#QE?$Ur@J$~iW`|vSp3IjMjLra2w01=A^@yG?v1ns9y)}x|i$v>eev{FlcEA ziOR9;p)_4tcinUhGG{u3saMmVCL=bGo+PZp2 z9$0+!sVK9Czp@-__>#2oYq}y5IH8I*8cmFkh>xWi4UWpA(Le%XF8_rPl8GE%Yh z;~A{DZbGZe{Y&qGyKmcH)y}tV(Prv-+dpK*7Ny?SXZo3+t2|8W?L?X7z&7~@wotaU zOe3oCb(xo6pxHk@3W!|gWki_a@rFkWB`H37a65ClLmJKlSwMU#@B~Jck3HC4r0vk? z@1^cXVJphEdSB}8rWdm)()^4o1+Z-PRNen{h6__0Jp%VfMX*5dkQjHYA5n?VZVakp zdU*zxNHbkTXRH}^G2tbxbi^a19lI2d_RyQYQG!Wo8Jeg!jrKAM{hKI3;U$S0p1*LcC3$k|3*2;N?@}*nhdQJ0 z?Ga2dGxPxT+Q#y`Q`An)3z^@RRUlD=Bp0q+#=TZD_TFXzWy|A3QiHM0;C47!-%tz% z+j6p-itWIe5RFUR7-G&sOy`pN)30Q_-UsS7Q^IiB<4BRv#qld4i%Er8$56&RlA}2p31e4puQTM7Nz;P*Eo6y@vTloe5EtZ zp^7ZU<_@KBmc6WN8}0=25qgw0LADX-(vG8DA`dyu529lim)g10CdQ4a2;IBsMwdy4 zZwic0JZaD+Hy`lN96{t7#FM=QJ@7{9ONKR#&Oi>(FL)7y@Aq4Txl4LwJ4=bD^Bbjx zEZtd=#}Nx)SaWKyj%Vr!U@}kIwc0_-#SsXBHZ^{HSNW|wkW`}Vy(NaS&T$l|(JdW^ z&7b|QmHQlmxigcMCvSqf5|~8ts{WSk6XO|hj=PeQ*)OS;mC6p!c*rwbZY z?>OB9V`Mpsms>cvt;&m$4pSv_*op?jVdR(ZABy%5a)D5>gw9Qk1Tj#U)n2he# zd%%919Sxlc9;O=i_-lS=MFWdxEz6nWr5_o`uC|S`yhi#+RC_y(o&b+)RjT-^n=w7i01d&?hZrZc~9!q>*agv&8 zzx1;rB30-L`obqL+M#vEGu)16o59lQ?eR{?*q9@D6)H|2bTNF<;IJd2Bl0=CJf5~g z-%eDMd*|gr@j?6(pykS}{sEA6Iqpeq29N&S+d->tnpPyZ#IR1x_j?~I{U`JDvghAs z4Cq=3g|4VvB%IsmFs?m=jX0nU@JL4|N1J;rWvycpV%BJcERsQ&Jb8!$uJ=J3BX?l- zawht@>8Z)@oV5)C%R+vH0sjQp;WBU(Z#~*Kx^8+E^!4cJQI&f<4|4(1q7-?kisnol z&{?`y*|ms}9)sU14^JHyuU7||SAC3Uc*Eo2dGPXkR!D9AEsJ|k*_*SVv^tI3XP@wK zStLVN$2>H39D-A>BBdVFIjtMra9t(ZZGKWcE|7N3c97P2$AbBD(`pzW-$3^8S0I+Y zt-7-qiw4z*8xF~D5Vw+G-&9qG$@>R=dY-__bK)@CV_Gw}9;V3Mpo8y5FqKbPTu)|5W5o{+Vs<0QD@VM5L!T-8GGj@aie&r#E@Sq;vr7g5@V|Q6*&|H1bHqEZfsaeCr$+{bA z>Xol$;YzsDyN?%ncv)s}JKXrSdCpT*7m}53{l0HV!kmRcoDihSA_h2x?|d-aXqfj$7!=*PExsLOBY$x z_BFX_^)@|oJF*vF2@Z3>G38Gz2AA!A;~|A!?4vVhF8z&J=43-hfxNno5on7bajCtVFo&mflKOA4IgF(@_ZuL)oE~EUZVh6AjS4lE(-58{Ltct_O z(LBW_J{21?7F(<`CPxWB<{xdr$JwzLQ4rL1MW)7Y2F^V`hSqse02x?9=aq{r70KJ> z+8ng4RwjQ6)CzX5aZvz(93TNn*e66cwIg(g;Y)+8eJQCxGhJWYML&e z(Lfi}x5ziNjuQz~ju+LI(==WP|8YcG`bsDKhQ@)+)UW2AFVsFkFDU?npPN#urbc zO2t3at*9Vy2BOcn4;Z?Ccjv9aak?1jLZO`@q-wgP`sd(hpe9LLS->bM9GNhsujXvK zmec3_7TR$x{4fhAF=S98_rb)aT>vFh5M8wSRvA0J~`^wwokc^KTweMyn|^xA*(Iv&@!jS zYV1Q@n3K!Z#%Ge3#3EB@&7R@5 zNbnX5o0EhiYem$H?Y7TNT4$nsme2aPBdzd}Vxn>!T`(i5&!V|J$I`@uG3r^;>~ySv zAHOf^O8G8;6Z4X?=rp{Q#5IAe65&voB|VqYw~2OH;upx%SSkuBm)G1iWMnx9%rv`1 z0Cz4<8pQPbth71I7bH3sZZ196kJSLYY~3t|xV74sopnmbTfrP(4k^*^OlI#oxRN$7 z$~Q0bc~;wpdH8^KyiNl$c0Z7mgfVIqnHS<&^VM@nwGLaW$UdwnI1DCrYAFkTJTh{4 z6}GRtg(PnJoEM`{mwmzseP`#M=J^h%LFm5l5Q%ui@!A1C=j zp|kfv*6Ij%%uw2Zp<`y^?II@LXNc0!Z=EVd%eD0lVm_|6YbIAbHpvFdge{j~2l24~ zj?0$EBoXQLzwACihGeEE_af}W-rNY3(1*YpHzp?T69dbqlH+66MlPi`1mwSYL`xiy z%+xA8{pnvK-d>=i0=C9WdZt9)UfF!=X1V3q%O~`CQ{wf1;W0VO!A1w4L<}%kj3%Xw zo|WJa?OZ*-H}3?EGYk^Gd&$Sq<$46sXom-Mee^Cz?t%Jc5iz9D_qT2s<%r33%ZXN~QG2xWq~n1KTeX6~sRfHzMd|I$w6 zH{YEL`l{eP5@v_@L<^<44t(uCEzf~o1jCKXNnZHwWt$sEbhta@y zU!A`=^pz-imZojdRAQyl{LoYbi)#6Uf4;M-53rb7RqUcC69Qimg9@3kS+4xEpJ(rc z)}I6%9O*eLm6D497Tb=~0-da#!V%UD#lM_gZ?3Hn1iJ%H;ZE-1)8n_L>W+Yc;vIKL zr;FDM4v1;DxE4FsQ`~~uZl?n3NUr$(i(8wrmK-}8+Kgnh^qmf?L^~7Ty#Ne!j>A5c z;q=QWcUEd8co;oR!N+$0C6E=>@(J2Q(LQa(?OZow6;g$NF%7M!9gkUeRu%vFnvmtM zKt}E*Y^ z`i@YBIc_k8GA~F(14|if?Zj^ql}GL3Y#4bXM$naQ7kb5YD?*wwh^(D6I2aaqDt~@o zM-JS$pKi(NzRV>dE_)sJl@Lj&1YbOW`(|4v*Yga|(l%#qrTl~8t0Yw`A*l{nSyFzr zp95=b_!V=xKoKLrB2g!{m2+kU6hs5pU9ccg`%b>1`zSzGm^LKa zE7WOj)}ChOC30;Fk?EqMHph#H9G=npxE3c(aP_vq^tIrKdmx+e;h|COkSz_%qW_I* zxof}+`t;H#!&rMX*M5M})*;;8=w$5F;C(q3f1mS!!$bOe_U0I#5e*v`7O<>5CCqZ%x4$r*xxp(OO4RM(??l60G3jqI^ElArQRn*+uxL(MjnS)q zT31~j9#V929N_6im&S=lWQHLQ+CHARdbvMy)jU5p^6T_H&k9|P`oKyFwUZ~>SaZG? z)fd;r>WD)mR)XuW!g)yxDy#3hC$J&F=OGZJ?JDa&c3>uw1`FUL4q>oXPtU=gK?egSw$wO=%SNq-C8!z5|22PbQ_OOD+1@z!>Yi zMC}f8M}4ZNftXuZ2hrn%Is{1Oto&ls3Hd@@=Zns6hT=Boto%Ve(wj7xMyGmu04__} ze3Ofw&Ur)Hx&gaoX*JN0)*@XYcwg89k)soyPCv*&Th$pvzUiWj+^k!%x#v3+kiX~j zlu95xwf|$#efibzX`Y;Xo4V%CIwNq@FQrBK&W9aCmvLPMx>{MIf6dl42h^q<9cF%#dmtO6;8x6SfSO-{--ke1e>W zvp4amW=A8>KCI+|BIm<=Q)cv5H{WnAMO*Q;1fG+Xp*`3~qS4!H0`Lr0`VD>Cor4iFBkMJ1z%yZ`!7$A~| zs(Fg0eo^r}Z+DoUEs;6ONb5&*I_gtHEl;+l9`c3MML!ETR{CnCcH)H1oumMLff@kM z@@buDRv6Tc{p?$}SWlwHm1*;-@#Rx)yhSO8I22R(#^_jgX@b;%cIOHVIY4w_mH3{ z1qQ&DTbb7_6BBa9+h|o7cb2+(PFoS@Q}6fIn#Sdlz8#r?oVZoda)hf$3A5?Fqp5?q z0QDmX-cTAQ=GKLlzZc5(mClD6*a2_eU{31W6E;OGqDrMbBbEkFB@uRE)sPFqL!kj6 zTM|5!Ss#qnbD`E3Qy5uH)tJo|`BzG&Zt`T7^*6BS%@$U3JBqDW)K>1-lu!w+1D7ef z7-{2A+HY{tFL~)7mgPb0%p})kR-?~9{}c-Ewka|Xy&6@2^~o>Rzt+ikwJq{;C@|Ms zj7`s5qVnvXj|>n;kc|r~lui77{^v&N4Ad<7d5rkiIuSEdinvLWfM6>0{rugz!G=Hz zceZ_56gwCr&Rq2zVT1^iN^CU~3p5PES!W_J<>8z$&EKw-Q|ejRK*aP$eH~hHA5~Wp z#!5-LL*GPlNFjK$p9+OoCzhg8eY1x(bY2$8h!raS{+%AP!cgxzBbyQWrem(?AA~xs z^eGu5D@(K3c2590)Vuj;+@8{1 zXAW`xqgGK$YiM z0DE=hoiW9I8}t%sbelrrHTCIFG$DqR256!e&od%u^Ck2~&047^-?mZ` zg^d;yW4Z8)xm^L*3@Hl0UM%6RBzM3cbG6)aLt*Aw-HsT?g3osyJ(__$r=P!TBBa&8E}idowNM%xy@D#}vRuQfTLw{$ca< zO8p7;LBj_vER{)gCCOb%rf64e{By7;AC`a;pHFYqgid&Ih5}?cjL+) zXxF-Lp_XQsuI~Y98E>Q$@MLOTQaVb8Yi-m-G^#=?B-mvEqIw=?NqgW&Qu4pQj~H^R zJwCePd68;Ny$oiM%CyfvcyaJ9FZlJd;!b~E<+hDvp2j?)qdy0j^1~|qc>IYEM(N&x z|(l96o86)t$qzRNj&5PXj3OGIVZsKzpbhWt=a5z_I1IJFlPVWn* zmmi|$vBOMkRR;31io0Ktif)R^6c*32_83NdASgD9MP#)pH%$FFS-0kuolGD5LUk}u z??u$`J@z_bXSQt;q*kbk1z6ml-ml9ibXeDP{}Ngt=PVYQZ2xVfa~`O(Eg3+GD6U1> zIGQwe7I&$2b=Zi->vsBvx=NY3@qCZ|yC%0}()ej`N61fDiJW z>&IS!+K%l?aY~3alErQBNgJi$Upib4~Q+C^2h_`1^Kq=$_w$ z*5A36Hq)z2#NROHDfU34G*R;;l+fAVzv8Q6x)~_8Tz3wXhNMMwR{t_^$=Sz8r>3~W zV&%S_*}@n&qIXo1xE0VS5o!%&aIMgtP7i!zcv0%=Wty!9lqRm;pS{Q>AvrAW=J>t5 zg1g(U72%D$@QKso6uj;vhXW{twKKdLv@}j8QFI@i(PqbtzLQmS5f;SfU10TD>YUM| zaEiNB9lMk;I5k~V!rhfn4SyURn6yl=dVu83RR@~d?BI@$?{=3=X{$DfD&_A6v%A|& z83xYX2bg0~n5yUxv3frH{TxhU^iFqfZsov(TD=9P><3nF_i~H%m@a#T*1}&xOigq&|!W}nFW&{i$5Q|`6S>*hQgOQb!CMQlnlT+9rH`Y*TW%(v-q zhFy26qkLEOi=f9G-%$LXxayBCv&nJD1LBvo*OKYqAK8tBf=yl4{W2Gf2?H^t)yO3#n%+mcp^f_jgcu88a$BQzpJ=0$nmUJN| zo)eHXX{bs#cu$)q-*?KA?YxmT<-eHdr4ndqZa_z0sL+ab)L#I5&tUBik zB~{CJw9nFoZlgWsR;jZwKuuD=_5qfJp1W<^cku)Ulw7fhpHZ=4!U&7WF0cdQ>q(HMX7L z^XG#g&5b{Q_X_8aWYw()=fuOA$1ZIpdMtqQ&&`7>4Hb*D6@LsfpP`;(cl>eU2X$N= z!U6v>VfEr^>!%8hhaMHjf6rqdYlt76^@uWPb9MJQRqAXZWQIw zCb1)@Kw7&Xq6L(Zb4@jW1RZA>*rO%A!I(GJrsj)8_kV5a_v5eR&JrojBuZx79k~14 zJHrWhPEZV6}s zi^LY%ke~*uXD*H6hUMS*uK@*46bwVYGO@%@CSCFo4nGAZXfMvVAC)7Hz<0|yd%s2N zbxB9zQ?8J2&TXnePspJ}HZ*|swbRA>p6YLpctr^+ZZ>;0KM&&8a}TKy#8=tL^*=c+ zQtR`wo^?#WIw)iAB9B`a1;iNJe)1I(5L9SvjX9u5n9)q3z^p84yRgEf_-t7efbiXV zcaV?rU|JL0{SSn}QWi_>Cn>%9Y@K!ozvyC+SS)KD%GXfDLQsUz`r!o8ztt7%Dug_l zG|KZ(S+sg-2}R-6lD)_uKwyW_!rJ`+Qfali;Z6-k-@%ji2U|JF58LNuSUVzE;!kVM;rg@)+vYQvCUQo_0dV6w0pboV0nf^%k#>d&ysS78_cQ zWuwN3aI;wUhL?vLtI_5qX5H`ncJU$tZ5WuT?SA3XLbZ6&EHQHgct^~)^4r&sW^Cp7 zB$YbbU2RG}8ulz%^C=bSZRxiPeG|d${5#T3Lq=ShHp)v<4bQWB))@`+45^d39@03fe()aZqf*`+s|@ib4>E z`YZM`AvT2_xU+PTPGVW*;}~h7_Xe2HEfrU=- z(^o3cbdKuz9V>e{2+myiRxuNXXx1~)9A3~=_|jF@hHlOt#za=l->&Z-DXw(h zsZ)S56DQ~PtQ;K+;{UJ%3e4*H9y$WMh*GIysKN)T7*8JWi>(o_h^O}y*c6}hvvP*W zTuHFr{JrRKc<-^*xl@(vTiI&a`7}gT=wjN#zq-R>xKZBcJl1-)A*8kv^B1g9K7ck$&&;=b@sE!v@)KIS>6j2*w=;^ z|J~&RR-~P~y4N<%6q+B|R|dLZ9lTZk9ji*ZTD={Qmp2`sp8+|KaM_kOm1y~{krg=; z6lihv&GgjrpbmrXGyBajud)C@o+$&GcF0!$kwx)zqe_JF7hP7>Uzc{W`eErr3pwJL!<|A5QQS@ke>Ow$I+aT50!$s<|D*BW>_hD&W{3Xr5+hUudg0uZuH4MIG ztsZ{jQ^8F*6mK=vEdc;0%~MT;!LANzp=Qf%_N1{z9j8S_fa=`Xl+`HOwHLhybwDJ= zvkw=%L1`be@9GZcj+$EAaNe8l^0Jolr-EY(jv+7pOxD~jsW9xC=}frZI$)#gtk?h$ z34MfHd!deo`J3JkHAfeb=6mgRe|JfM?>G)w>D}x@wk5|U$vFC0yf6S+ya5Mc0V#5= zY&v*s_tGQw4Nts{CtoO4i`YLdwdt}a;EhuHzc;7!uMGd$1|*!DK}za}P6f~xd$k!~ zIj81L8wER+95Xi14vtj=J*XubM0@a-pyyu>5eZx6x$Yr{?-rFTUBXhJoC(sXDHHWz z06?8VwRAeA{YlDM{Ag5B3o^P&et&F|LQ*H&WwtSe_x?w<^4g;~hsbbKYFiaLL$P}; zB%vwNZ|Wse(Qo>~Vo;6l#L;=sLs!g(NP?4$8qJPp+YH`({npi)=+OO&m!tu%%d>+T z)(9jiNVdzrV#_sru?xWfbb|w~e&36Qvv^Pa>b{%CWCtbT>0)RpR~=V1 zpVyXT>O<}80#=^_0Iz>htGE@9^S))rW9wBW5%j7u5JcHu5J&?poG}3MiQff|76Xx- z;w%C9;duRs+;aRyDfi{yKhf$i!0Weet|4)!OJpdjK)OH$d$vn-0WQ`F_rL-GlJzo7 zZ$D?}@%$rf`OEq9c^Jvr-Qc1kf3E-lura&YL@~*Fn~5nN&ayb~J2E`~U_#?G{WhX& z0UN!#T(>Xn7!u6mg*&}By0g79W-3n)uCyFlBLD#O)7o8kHU8HY*?>x*7u>~5$F-$F z`zioiH2+&y2OH+ z01!hX-c*>xbtgcQCZ#}1ytz+uML5HwhXn{TLx)d8VvD8dq!jq!6$kxqL;vcQ-_-W= zyg@g9)3SE{)~Lv1~uDQ0`RrDmsCMpj7=p?BL(z0TrmTOGGZ+A*llC$(i4Q zz^LABgOo*41REN&FyDMcHtp4{sD!&o>askk&_Qz$RNNOV@^9n?WRtKTW3yMz)@0~c zc@6H=qg!CD@fgJDCZRzTrv%Z=MO6WiPNDaC%DY%q*V)E9S~PAZEP)c!%G}^xG(9gK zgCNChm{f)`4w{)m%5xB16>o?K%P#Q~vK8g1UCFLapWSA(o=n(IE-CcyVujjr!auSU znU#oJKb-hs000)BGnT)nCL$~!=XiAm6_f{;f^}f6urL1`y-C{8j(a&-;9S0wv$-ni z&Ddho)tXy^f!d@ydi*Tax<`+JU8~BaUlLeO%}Y#bgG47*GGfr!ht<4>SMpC62ENe} z{=kJc@$@DXr*QiLc!|GM9X&smr6LJqtd2~935iEatB*Ui+?pno?P|)XAAZ$9=*s6= zB@SWqR3p?Y!-_r9D(<2-)rE#si zS&Dnnp{H>r`TX8gf7jZO^@UtaRN~j`gm%-YWf=D z#=N;%$vsGjg<fG*ufNyQf74l|PJ&oVEu~=mwlNpcMj-!ezX(Oj4W3=%E$37O$ zZg5hg1uL&xc@YTP!&|X^VjgICzpSz9JMQY^o z3HyuU>?TXQx|!VjyM+H~Onb8Wk}Xi_ZkKCRJzPX& z^IWYeWnL+TZtp2}D(0&4X@6bI^h?)fyls4(ja($!Qxgahw9#-~uYYQu9fJ^-FgWUf1sE8-?nv2M%p}ddNiv9dahKQufZws9|;BBp=f?Qx8MKl zm5sMU6KmJ9D39LOepfC*`9bq+Px~PAz42_vNNruiimcx19@c{%V%L{=I%==_aBn6I zWt_S-!X@DZUP=?p6?X3uuaMCJcxMKp>8&$r@L*Lrd54; z4sR`j;-|HX@*iDWJ@*#S{)-?2VVj4$#hBAdGl7_f0F>|I6NtO4pIC0%9clpK-Y%t~A_ajvn zYT4O(hm^oV+>1o>oEMcD{)&4_sGJqfHtpbtD>^h`+vgRUIT>bC1FN&8y*JQOqYAY( zAM_XbwcAjHK2}q?uBgF*tW5FMxn1$5Cx@IU(r8?Aq45miV++3`cT zAKGhH*S-AG9(i4G?~a}x^#AK<%N;`DJjl0;*#ZxARXV7e22~@c#_86fLwW%`IF8^d z5`DGM%&>o+2*3ltSpM&~rKx*$nr|-ibdc)(oZ$BJ5?iSKe>J^EOxuE(LoK5Jh!W`C zmRYT8+xUpNSG|)|g6>{OZhFjO|GqQ1?S5cq_k;Ujc>VJZfH>vHuXD~O2z{BxCfyl- zok>UMUmmkU{JvT^!MtU=Mj$^(iXG=al5qytU&WG7Un=)I*Kq9)<0MKXhJ0$UaJZ@1A zDf-MNnf>I@sW2$==H|K&fYgx(!|ydL!p{%2^@n}NF1pM0b3CuUw`7Ed)UHrx4} zhxuKK_?>|M&AULSTcTma9duAAx9bNt?FZEoi7gZkjn6fmR0+&DJT~xdw!HmEav<@% z)^cMG$l%oCF(EpNKgE#BO4+jDGhKDB95?RU+C2AM{eQgilE}e%mVxA5=zovx&SPY2 zxee3YUuahoJl=QoxjvJNR3H{LMTa?PlF#uw2l!LCf)O($k{aNhhQ{#bxpZhi?L@oW z_>5c=BoJFJ1AUC~uWRcy|LE}tDJP}eqQ5&b{57V?xtyZL8E|kZu5U%#by8e4+JylC z0bk*nuR9F5-5n#2YHyvef4OU1%~mx++KW->?33z!&kB{ll{< z35zT)uj=4!sIU%EC}dD__5&CyTBwEjzdoSL(c(b@UBwudPXe0-*ww%?h0|H|(l Oa}5<;q#AhKnTW0%k&3E3G9#IZAyV`R&&jEp0W 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¥K{jX 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ֽxB;x~j{AUb_qgz@z56J58~ zUycuj@LiPg*0ApZAO+o{98fw~2oe%9Lkil5ij;7hc^x-Ch+4$(gfBK;0E7qjQ5wt; za&c?TwS$5lCWnAcvEe1fg94HQOMBCyRH-x$&K7Dir7z1lfj7Rg_gGdp~8>E%N#=i8-ddCCH9@L^2020Qb)rnh-EJcgNLycCQ z0V00Hem1VNB~+jJO~NXnaZI6A^8I(>HZ zJ`k^7IsD#Y8*o^|>L#ir-9+`F=Bu*MJZCxh!BeBZA;%4Z9$>dfY@IkZUT7DvSZHbN zn>DrsJ!So*-y4X9w#&YqU`x0M5-Px)PyQGkVPg^hJLhUP-M^;cP!zB#FozPjEgyRo z^;2ceLUJE$M+Qu4Rm81M%8Y{e1?(Ul9AA~E3O|);=uJBGH^{*VPmC<-TPuU4S#ag8tS})45l1*XrN@0Z@+a0{{R3 literal 0 HcmV?d00001 diff --git a/app/boards/arm/nrfmacro/epd_screen/slave/icons/pictures/horse.png b/app/boards/arm/nrfmacro/epd_screen/slave/icons/pictures/horse.png new file mode 100644 index 0000000000000000000000000000000000000000..4c5981672e345f87524c3c8d993858b277033f37 GIT binary patch literal 49337 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 literal 0 HcmV?d00001 diff --git a/app/boards/arm/nrfmacro/epd_screen/slave/icons/pictures/karl_marx.png b/app/boards/arm/nrfmacro/epd_screen/slave/icons/pictures/karl_marx.png new file mode 100644 index 0000000000000000000000000000000000000000..8f419ab92023343f03dce57f205e1e3763f4f8c6 GIT binary patch literal 455938 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

JZaw=NI=8zCgr!iauKp1aBf-gN2r~ zP4v1B1|L_l!z}q>I$@Bh6@cw$u*(7mg?Mv4Z}$k(;1l-@;$D5S(@G-3y2+RVnCKYcJ&j;E5rCUp zx;O&Fv^Qt+AjTn0cGtE#9V4l+{s85O%V{;gijIyeZ=YR-aE4R#vX<7);>hElowoC# zp>kfx2PQ6l8_%Ogmr(?ntF{u&=LW!4mrR|ojyHK(XzK;M`HEL3$A-RR?De{HZcO$l z>`D7VX&=K^vA!lmvRns=;btRjW}ZY4i**lVig)en*t_Db+C87qmu#Q zD-tBkJ*e=pH|i;>$Qy7Kh5uQx1a|r`;1*W^)^i8Wr4MMH?4HAmaRF&ycWc4LmRNIU zaY;RZ^d@~j1_$HopH&IySG@I@15 zETlcnscIeO_;W#>AK~8Kkyg%HfSv?RF446HehfU%YQuJVJA5wHZ`k87d4PSLxa=+Z zV8*E4@eHWMDa+i~^0R7k{%SuhxmoJ=Rj=S=;UP41vYn1*Sdpl1B<>fb{`kfonJVk6 zw3H+N%JP@%A`ei0oXloF2ld}|!>n;2v`4nI12gSquy(zHN>WmM8BC`%mLsE(&c70S z1>xL&sJKF3SO7szZZaWLjk!ueR!k?|)r6C9oh%o=5~5}XZ<@RZ`tL2YSCBT;u}>WE zI0`ly@a^kir;Cbw_g74E>T#XyPoULKyR~OD2E1^2@}|ziI+zLm(i-N3mVG5x$xL^l zFn51|Et7!W`t)(PatYoba3v(ocmN{^nur0p0$tOj193|n{YzapMB~?3xVYT^&WG~t z7jq0fPjl5cf^&BVQ@@_Q zJK~?1!^jjB6~(O&HJu?E;VVDfX0TnVwY}Zr&#NC(Yurqnn4{Y$b&+a5iW2#=FHWZ)hJ*zDP0C8|<) z-Or3NtpN(CZCwC|r2WA6dc#A z4jc<=qQY#sah_orLbUqRFYuPIuiyxPQy0L~Kak7G{?TT-jVQEUkQ7|!7fn{wPx`CS zOX>mJ*B%G}YkpLNF8^5D3;DSw#5@F9iSJ_^Zm+km>rp3&?;M}l)MTG5A%mESAwBok z9|vlpfQcxbSj2P`fzsR~VBvygc~2Ml0wEsU0U7@>cCgdFH^yU`N5zqP!UTK!1<0gP zV|Y9<20a$?JfIlc@W2~SPyEA3i~NhIr?z(pUHlFBOw8}15_4MNF*3X;4Vo3BX)*3| zY>tS~6@k}8c~V|?80`5&&(I4R^_Ri0>S@QtxXS=Qz;bCheKFuS^&^3Qik)8+BaX&4 zEruOaRI*K5#*tdknv*7^@Gk_pa!Y!=2J?T=1xizL_vh2a;3cK}j0 zG+YHTMtTY8+H)ZP`0(I4%D+h{fh|pnG4cVIX~q_T&Myv27jN3SwdwV15AYb#hE(PkgLb)db*u7EFG3OnB9JP2X!Ou+ z-E9-#O^p_j=ZMiR;GSN6BVn+K6`NNy;eaM;ZCrv%;M7s0 zw{PEG7b*GgqUHRf+QIi05ZGnC=mNm+9wAwj1b7l7({;C)L1;4L_baARS!D)avrudRL`&p?#!(9q3H1#Z?jN{;66@TbKOBIQsBGHRt#!ql=&jZRJ;k)Zp z!IZkR(wn{)mxIaSk$N0z9&R44+M8nO7W(%9lWYEg3MY~fVl1-C17W{6>IJT#yAk|u z68`KJG>T;43vC-qU$}r{1;3w<08|Crtu@qe_LZ1@rBtP)o*4DX*PDsj|D|l)xY5%Z zee4fZM4<}+Qch3yPy$w}%gWXfP--}cMHJH`6Nfj-QEM$a`1cE2NsXT%6V0F^VA$qA zhKJ8D?W+v}(CJ=bg&2;dZ5#(SrUPzyf^10CfA6s5WE->SXOq#LhG%t;y=XIMVzwq!{;;+TIuvXriYg*s?VINK@qDr<$2ihX{ z%7KS=4_78Dd{3cq$;q_F<$k6PD7zW2TbXEB2%EVuZ(Fq`b%#mNSL zalC8itBoctZ&KgZ;mN0;?#471MH;aVgu4&fH%#_*CGlZ2 TRr+&*kcvW`HG1!0n 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 MfS+^tiD1XV$A~H+`5N1a}B|h4(zmJ0_t1f{5uN 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

xpgBOCHzz_lF#letF&=(z}* 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 VH6S$OQwez->S5JGi3iSj|9_ODkqrO< literal 0 HcmV?d00001 diff --git a/app/boards/arm/nrfmacro/epd_screen/slave/icons/pictures/test_64x64.svg b/app/boards/arm/nrfmacro/epd_screen/slave/icons/pictures/test_64x64.svg new file mode 100644 index 00000000000..bda5a54c608 --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/slave/icons/pictures/test_64x64.svg @@ -0,0 +1,132 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + TEST + ME + 233!? + + diff --git a/app/boards/arm/nrfmacro/epd_screen/slave/icons/pictures/testlogo_68x62.png b/app/boards/arm/nrfmacro/epd_screen/slave/icons/pictures/testlogo_68x62.png new file mode 100644 index 0000000000000000000000000000000000000000..3a116f7679422d56062255b1fc7600020ed56f3f GIT binary patch literal 2566 zcmV+h3iKfB9bxQPla?1JsGg+)k4HUi3qVTnYM z*3>vj(@fJe&6HwJVP+Y43()a0Hr3Qrlz@qi6h;y_n%zrVQSg$qTvEzHE)oKZET=!d zzr*vq@1FCX_ni0ag}yUq-gBPwoagzyzw`Wl&+m4saF`D)CK{W7>wt9;_vQnC1l|eU zX}UIkWr$6&jEDsThP;Sv9rqH)tQ zqyc!yp6Ryn2bX*z9wUKOxYcqiJy&on(uxce>Z3NKTLRn)f^-ZS1pLhU{|9()IfR#i z%Ymad?%h$7rbQ!#e`qz@g|-1J5W`x4dZb)m%P5Gexhv!%>vss4mPBkuT#i!#EeU-yqaJO&%|vgBRomx7V=%5!pdDkxoJ#V)Q}Wu5)cK%~dg~fWIL^FX@HxX98Pr zV*Mb{+6(jKhER8GNA^cvXr^$>{W~(=dZ2ijya~7rS$(JFNDyeZcpA7D?MP9$(0*4T zyQ5MJ0>n%^H@{^H{$l^-aL*Pfl)@$AFR*plz%)5?Z8vsybl9!O?oIK z>Q1E0jw2^+J@B$!=YeWq3@``ym|foiTwT=fFtScZASW^lMgz-$PoV#|1DX0iFL!`; zoR4yGMk3N+4$fYg;k6IJY5gpy0X~P^|Iqon$;M5E)i%JQjD%#t4L0X}UR#s3{VT(G z(G6KdRx41I{FD?Hy(g|vA!PzxMOK}aR2Mas1sx6=(WLaWg-PZKyQ#wf&?7Vmew zcODly*j|N}7f1&4Qld$y#%-(qZnFasJOSAXy@in!GHbRS4-7POHT0C*>0O5Gv7^JT zM${5XodqmM!J&`PGp90w>@V*hXuA(F6U9P~ zq7=z?NWVgAkgWB#&y)9Y1Tj0r5R@MZP|5eZq* zR*NjoG?~49-uW-!S$VUkzNP~?{dNL1=kcq7c-y-0~@ekBa4K{=TK zd9dXo4MPAX;Zmgmio0Hc?3O5e$ND{wy!h?jvm4Orjzy(*$oXh#9s$lQ^O>{^0Wp68 zS|$GOu@hJWd=CY>9xS>a9?zh}S0eH-%vRVK>&xt)g&`qoQEcNO6jdv=)$T>ge;z4< z)_E{wG^!&BP-#LEDq)C^8sHtk$AQn_VzS#%rZJQAnwRN*wT;t>;-aOH7ejnpfJFCo zoQ;zSc`;-VF3Np_J44O4ojR5}ofV;o1@CQs3D@F?OOpL^Fb0(v#4))6=Nx{y z!nu4=xv|2rPGA^{Wd^89Z&D6pF0}EsQpn^$G05Z%mwRs9GbeN{*TI)U_aXl`2Rh!T^#8jN9 z%t+{a(xDz#hgw`;7S}(lE4{53>FQU^Mt|@C$(2G&a6#q&D^V#%7zkcbqR$jul~H?N z5LF6sd5^U1aedlQ`BZ>Hf5oUrpMwO*Oz#&|A$R&QRFd#0u43;N*TAuWi4zHQIF-g`dd98hKSHM_Y&DtA4xgSfJSi|zT)V-7z?oM-Kq+4Uv3ut1Z|9e%uYR{|qKgbdk%TMa)%C7)gNWJsns z&jK6JFLJ#ViOu&Z)CY@E0{MPi9^`EFCw;Ub@mNivh^#Y0m!mLX+GtwHkZbJsQly_2 zpGObAyoNEM4E^2Q?@^%n?Di&fC`mAFQVj933kX7m99^i`+^G!fv80J)t z6Rl|D5He(v(R~1kTr<%ODHfTgQiHz*6~eUH^`%Hx_&M8kD8E`oVQs}qT%CFqJ@cqj zaaGI32$g7nK0Zu?kRcP%7vBL=v=GgZvPGsDEupJ`k0L#>75$M-NC^%{vbq5=uhZHF zBXc~g8=HZmWWLD#6!>h++(S?tH%s8zXUJ(tbSB!pD25zDn?-mOC~iOZ*@_Ba=6h|M zQQl@F@JqCtY(zfE%V<@sMXTs>oKmepO!9S21M#`mLntJ;ABkjE?8B-FOJFdCA2;qp crUn521GKX$<|-V27XSbN07*qoM6N<$f`N>q+W-In literal 0 HcmV?d00001 diff --git a/app/boards/arm/nrfmacro/epd_screen/slave/icons/pictures/we_need_you.png b/app/boards/arm/nrfmacro/epd_screen/slave/icons/pictures/we_need_you.png new file mode 100644 index 0000000000000000000000000000000000000000..0a75f3d14d8d7ab72a2158cd3e0068e5fde47d0a GIT binary patch literal 30349 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%0ATBpfiA12gu;EU2`K!XVq8x}+qM7DhsQJ+lu;> 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⪙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�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 Q2LCb+?6uq@+2s=b{{}*12mk;8 literal 0 HcmV?d00001 diff --git a/app/boards/arm/nrfmacro/epd_screen/slave/icons/pictures/wukong2.png b/app/boards/arm/nrfmacro/epd_screen/slave/icons/pictures/wukong2.png new file mode 100644 index 0000000000000000000000000000000000000000..091c46efea76151f579ab753829e1d79588a436b GIT binary patch literal 23836 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 Y^~}BFLok1)IH}LI?&@lkL9Ji^7tOR4u>b%7 literal 0 HcmV?d00001 diff --git a/app/boards/arm/nrfmacro/epd_screen/slave/icons/stdlogo.c b/app/boards/arm/nrfmacro/epd_screen/slave/icons/stdlogo.c new file mode 100644 index 00000000000..a04d632acdb --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/slave/icons/stdlogo.c @@ -0,0 +1,95 @@ +#include "lvgl.h" + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_STDLOGO +#define LV_ATTRIBUTE_IMG_STDLOGO +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_STDLOGO uint8_t stdlogo_map[] = { + 0x00, 0x00, 0x00, 0x04, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xef, /*Color of index 1*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0xff, 0xf8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0xff, 0xea, 0xff, 0xc0, 0x00, 0x00, + 0x00, 0x00, 0x0f, 0xf8, 0x00, 0x0f, 0xf0, 0x00, 0x00, + 0x00, 0x00, 0x3f, 0xc0, 0x00, 0x01, 0xfc, 0x00, 0x00, + 0x00, 0x01, 0xfe, 0x01, 0x7d, 0x00, 0x3f, 0x00, 0x00, + 0x00, 0x07, 0xf0, 0x1f, 0xff, 0xf0, 0x0f, 0xe0, 0x00, + 0x00, 0x3f, 0xc0, 0x7f, 0xff, 0xfe, 0x03, 0xfa, 0x20, + 0x15, 0xff, 0x03, 0xff, 0xff, 0xff, 0xc0, 0xff, 0xe0, + 0x1f, 0xf8, 0x0f, 0xff, 0xff, 0xff, 0xf0, 0x1f, 0x80, + 0x07, 0xe0, 0x7f, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, + 0x00, 0x01, 0xff, 0x7f, 0xff, 0xfc, 0x7f, 0x00, 0x00, + 0x00, 0x0f, 0xf8, 0x3f, 0xff, 0xf8, 0x1f, 0xe0, 0x00, + 0x01, 0xff, 0xe0, 0x1f, 0xff, 0xf8, 0x07, 0xfe, 0x00, + 0x01, 0xff, 0xc0, 0x1f, 0xff, 0xf0, 0x01, 0xfc, 0x00, + 0x00, 0x7f, 0xe0, 0x0f, 0xff, 0xe0, 0x03, 0xf0, 0x00, + 0x00, 0x7b, 0xf8, 0x07, 0xff, 0xc0, 0x0f, 0xc0, 0x00, + 0x00, 0x00, 0xfe, 0x01, 0xff, 0x00, 0x3f, 0x80, 0x00, + 0x00, 0x00, 0x3f, 0x80, 0x00, 0x01, 0xfe, 0x00, 0x00, + 0x00, 0x00, 0x0f, 0xe0, 0x00, 0x07, 0xf8, 0x00, 0x00, + 0x00, 0x00, 0x03, 0xfc, 0x00, 0xbf, 0xf0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xe0, 0x00, 0x00, + 0x00, 0x1f, 0xc0, 0x07, 0xff, 0xff, 0xc0, 0x00, 0x00, + 0x00, 0x3f, 0xf0, 0x00, 0x00, 0xff, 0xc0, 0x00, 0x00, + 0x00, 0xff, 0xf0, 0x00, 0x01, 0xff, 0xc0, 0x00, 0x00, + 0x00, 0xff, 0xf8, 0x00, 0x03, 0xff, 0xc0, 0x00, 0x00, + 0x01, 0xff, 0xfc, 0x00, 0x07, 0xef, 0xc0, 0x00, 0x00, + 0x01, 0xff, 0xfc, 0x00, 0x0f, 0xdf, 0xc0, 0x00, 0x00, + 0x03, 0xef, 0xfc, 0x00, 0x0f, 0x9f, 0xe0, 0x00, 0x00, + 0x03, 0xef, 0xfc, 0x00, 0x1f, 0x1f, 0xe0, 0x00, 0x00, + 0x03, 0xe7, 0xfc, 0x00, 0x3e, 0x1f, 0xf0, 0x00, 0x00, + 0x03, 0xc7, 0xf8, 0x00, 0x7c, 0x1f, 0xf8, 0x00, 0x00, + 0x03, 0xe3, 0xf8, 0x00, 0xf8, 0x1f, 0xfc, 0x00, 0x00, + 0x01, 0xe1, 0xe0, 0x01, 0xf8, 0x1f, 0xfc, 0x00, 0x00, + 0x01, 0xe0, 0x00, 0x07, 0xe0, 0x1f, 0xf8, 0x00, 0x00, + 0x01, 0xf0, 0x00, 0x0f, 0xe0, 0x1f, 0xf0, 0x00, 0x00, + 0x00, 0xfc, 0x00, 0x3f, 0x80, 0x1f, 0xe0, 0x00, 0x00, + 0x00, 0x7f, 0x00, 0xff, 0x00, 0x0f, 0xc0, 0x00, 0x00, + 0x00, 0x7f, 0xff, 0xfe, 0x00, 0x0f, 0x80, 0x00, 0x00, + 0x00, 0x1f, 0xff, 0xfc, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x00, 0x0f, 0xff, 0xf0, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x03, 0xff, 0xc0, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t stdlogo = { + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .header.always_zero = 0, + .header.reserved = 0, + .header.w = 70, + .header.h = 70, + .data_size = 638, + .data = stdlogo_map, +}; diff --git a/app/boards/arm/nrfmacro/epd_screen/slave/peripheral_status.c b/app/boards/arm/nrfmacro/epd_screen/slave/peripheral_status.c new file mode 100644 index 00000000000..43acfa38f41 --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/slave/peripheral_status.c @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include + +#include +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#include +#include "peripheral_status.h" +#include +#include +#include + +static sys_slist_t widgets = SYS_SLIST_STATIC_INIT(&widgets); + +struct peripheral_status_state { + bool connected; +}; + +static struct peripheral_status_state get_state(const zmk_event_t *_eh) { + return (struct peripheral_status_state){.connected = zmk_split_bt_peripheral_is_connected()}; +} + +static void set_status_symbol(lv_obj_t *label, struct peripheral_status_state state) { + const char *text = + state.connected ? (LV_SYMBOL_WIFI " " LV_SYMBOL_OK) : (LV_SYMBOL_WIFI " " LV_SYMBOL_CLOSE); + + LOG_DBG("connected? %s", state.connected ? "true" : "false"); + lv_label_set_text(label, text); +} + +static void output_status_update_cb(struct peripheral_status_state state) { + struct zmk_widget_peripheral_status *widget; + SYS_SLIST_FOR_EACH_CONTAINER(&widgets, widget, node) { set_status_symbol(widget->obj, state); } +} + +ZMK_DISPLAY_WIDGET_LISTENER(widget_peripheral_status, struct peripheral_status_state, + output_status_update_cb, get_state) +ZMK_SUBSCRIPTION(widget_peripheral_status, zmk_split_peripheral_status_changed); + +int zmk_widget_peripheral_status_init(struct zmk_widget_peripheral_status *widget, + lv_obj_t *parent) { + widget->obj = lv_label_create(parent, NULL); + + lv_obj_set_size(widget->obj, 40, 15); + + sys_slist_append(&widgets, &widget->node); + + widget_peripheral_status_init(); + return 0; +} + +lv_obj_t *zmk_widget_peripheral_status_obj(struct zmk_widget_peripheral_status *widget) { + return widget->obj; +} diff --git a/app/boards/arm/nrfmacro/epd_screen/slave/peripheral_status.h b/app/boards/arm/nrfmacro/epd_screen/slave/peripheral_status.h new file mode 100644 index 00000000000..e3b41355eb8 --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/slave/peripheral_status.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include +#include + +struct zmk_widget_peripheral_status { + sys_snode_t node; + lv_obj_t *obj; +}; + +int zmk_widget_peripheral_status_init(struct zmk_widget_peripheral_status *widget, + lv_obj_t *parent); +lv_obj_t *zmk_widget_peripheral_status_obj(struct zmk_widget_peripheral_status *widget); \ No newline at end of file diff --git a/app/boards/arm/nrfmacro/epd_screen/slave/status_screen.c b/app/boards/arm/nrfmacro/epd_screen/slave/status_screen.c new file mode 100644 index 00000000000..4c03ab1b25a --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/slave/status_screen.c @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include "peripheral_status.h" +#include "battery_status.h" +#include + +#include +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#include +LV_IMG_DECLARE(marklogo); +#if IS_ENABLED(CONFIG_NRFMACRO_SCREEN_STANDARD_LOGO) +LV_IMG_DECLARE(stdlogo); +#endif +#if IS_ENABLED(CONFIG_NRFMACRO_SCREEN_CUSTOM_LOGO) +LV_IMG_DECLARE(customlogo); +#endif + +#if IS_ENABLED(CONFIG_CUSTOM_WIDGET_BATTERY_STATUS) +static struct zmk_widget_battery_status battery_status_widget; +#endif + +#if IS_ENABLED(CONFIG_CUSTOM_WIDGET_PERIPHERAL_STATUS) +static struct zmk_widget_peripheral_status peripheral_status_widget; +#endif + +lv_obj_t *zmk_display_status_screen() { + lv_obj_t *screen; + + screen = lv_obj_create(NULL, NULL); + +#if IS_ENABLED(CONFIG_CUSTOM_WIDGET_BATTERY_STATUS) + zmk_widget_battery_status_init(&battery_status_widget, screen); + lv_obj_align(zmk_widget_battery_status_obj(&battery_status_widget), NULL, LV_ALIGN_IN_TOP_RIGHT, + 0, 0); +#endif + +#if IS_ENABLED(CONFIG_CUSTOM_WIDGET_PERIPHERAL_STATUS) + zmk_widget_peripheral_status_init(&peripheral_status_widget, screen); + lv_obj_align(zmk_widget_peripheral_status_obj(&peripheral_status_widget), NULL, + LV_ALIGN_IN_TOP_LEFT, 0, 0); +#endif + + // todo: + // 1. a product mark line (sweep-pro) +#if IS_ENABLED(CONFIG_NRFMACRO_SCREEN_MARK_LOGO) + lv_obj_t * marklogo_icon; + marklogo_icon = lv_img_create(screen, NULL); + lv_img_set_src(marklogo_icon, &marklogo); + lv_obj_align(marklogo_icon, NULL, LV_ALIGN_IN_BOTTOM_MID, 0, -3); +#endif + // 2. a product logo +#if IS_ENABLED(CONFIG_NRFMACRO_SCREEN_STANDARD_LOGO) + lv_obj_t * stdlogo_icon; + stdlogo_icon = lv_img_create(screen, NULL); + lv_img_set_src(stdlogo_icon, &stdlogo); + lv_obj_align(stdlogo_icon, NULL, LV_ALIGN_IN_BOTTOM_MID, 0, -32); +#endif + // 3. configurable personal logo, which can replace the product logo +#if IS_ENABLED(CONFIG_NRFMACRO_SCREEN_CUSTOM_LOGO) + lv_obj_t * customlogo_icon; + customlogo_icon = lv_img_create(screen, NULL); + lv_img_set_src(customlogo_icon, &customlogo); + lv_obj_align(customlogo_icon, NULL, LV_ALIGN_IN_BOTTOM_MID, 0, -32); +#endif + + lv_refr_now(NULL); + + return screen; +} diff --git a/app/boards/arm/nrfmacro/nrfmacro.dts b/app/boards/arm/nrfmacro/nrfmacro.dts new file mode 100644 index 00000000000..1f22cb5e113 --- /dev/null +++ b/app/boards/arm/nrfmacro/nrfmacro.dts @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +/dts-v1/; +#include +#include "arduino_pro_micro_pins.dtsi" + +/ { + model = "nrfmacro"; + compatible = "ufan,nrfmacro"; + + chosen { + zephyr,code-partition = &code_partition; + zephyr,sram = &sram0; + zephyr,flash = &flash0; + zephyr,console = &cdc_acm_uart; + zmk,battery = &vbatt; + }; + + leds { + compatible = "gpio-leds"; + blue_led: led_0 { + gpios = <&gpio1 10 GPIO_ACTIVE_HIGH>; + label = "Blue LED"; + }; + }; + + ext-power { + compatible = "zmk,ext-power-generic"; + label = "EXT_POWER"; + control-gpios = <&gpio1 9 GPIO_ACTIVE_LOW>; + }; + + vbatt: vbatt { + compatible = "zmk,battery-voltage-divider"; + label = "BATTERY"; + io-channels = <&adc 2>; + output-ohms = <2000000>; + full-ohms = <(2000000 + 820000)>; + }; +}; + +&adc { + status = "okay"; +}; + +&gpiote { + status = "okay"; +}; + +&gpio0 { + status = "okay"; +}; + +&gpio1 { + status = "okay"; +}; + +&i2c0 { + compatible = "nordic,nrf-twi"; + sda-pin = <15>; + scl-pin = <17>; +}; + +&uart0 { + compatible = "nordic,nrf-uarte"; + tx-pin = <6>; + rx-pin = <8>; +}; + +&usbd { + status = "okay"; + cdc_acm_uart: cdc_acm_uart { + compatible = "zephyr,cdc-acm-uart"; + label = "CDC_ACM_0"; + }; +}; + + +&flash0 { + /* + * For more information, see: + * http://docs.zephyrproject.org/latest/devices/dts/flash_partitions.html + */ + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + sd_partition: partition@0 { + label = "softdevice"; + reg = <0x00000000 0x00026000>; + }; + code_partition: partition@26000 { + label = "code_partition"; + reg = <0x00026000 0x000c6000>; + }; + + /* + * The flash starting at 0x000ec000 and ending at + * 0x000f3fff is reserved for use by the application. + */ + + /* + * Storage partition will be used by FCB/LittleFS/NVS + * if enabled. + */ + storage_partition: partition@ec000 { + label = "storage"; + reg = <0x000ec000 0x00008000>; + }; + + boot_partition: partition@f4000 { + label = "adafruit_boot"; + reg = <0x000f4000 0x0000c000>; + }; + }; +}; + +&spi1 { + /* nrfmacro.dts */ + compatible = "nordic,nrf-spim"; + sck-pin = <22>; + mosi-pin = <7>; + cs-gpios = <&gpio1 0 GPIO_ACTIVE_LOW>; + // not used, but required by spim binding; can be reuesd in the keymap for normal key press + miso-pin = <5>; + + /* shield.dtsi */ + status = "disabled"; + + // spi-device: gooddisplay GDEW0102T4 + epd: il0323@0 { + compatible = "gooddisplay,il0323"; + reg = <0>; + label = "DISPLAY"; + spi-max-frequency = <4000000>; + + height = <128>; + width = <80>; + reset-gpios = <&gpio0 12 GPIO_ACTIVE_LOW>; + dc-gpios = <&gpio1 2 GPIO_ACTIVE_LOW>; + busy-gpios = <&gpio0 26 GPIO_ACTIVE_LOW>; + pwr = [03 00 26 26]; + cdi = <0xd2>; + tcon = <0x22>; + }; +}; + +&i2c1 { + compatible = "nordic,nrf-twi"; + sda-pin = <34>; + scl-pin = <32>; + status = "disabled"; + + oled: ssd1306@3c { + compatible = "solomon,ssd1306fb"; + reg = <0x3c>; + label = "DISPLAY"; + width = <128>; + height = <32>; + segment-offset = <0>; + page-offset = <0>; + display-offset = <0>; + multiplex-ratio = <31>; + segment-remap; + com-invdir; + com-sequential; + prechargep = <0x22>; + }; +}; + +nrfmacro_spi: &spi1 {}; +nrfmacro_i2c: &i2c1 {}; \ No newline at end of file diff --git a/app/boards/arm/nrfmacro/nrfmacro.yaml b/app/boards/arm/nrfmacro/nrfmacro.yaml new file mode 100644 index 00000000000..e1b59a48818 --- /dev/null +++ b/app/boards/arm/nrfmacro/nrfmacro.yaml @@ -0,0 +1,17 @@ +identifier: nrfmacro +name: nrfmacro +type: mcu +arch: arm +toolchain: + - zephyr + - gnuarmemb + - xtools +supported: + - adc + - usb_device + - ble + - ieee802154 + - pwm + - watchdog + - spi + - i2c diff --git a/app/boards/arm/nrfmacro/nrfmacro.zmk.yml b/app/boards/arm/nrfmacro/nrfmacro.zmk.yml new file mode 100644 index 00000000000..12494c8ff6d --- /dev/null +++ b/app/boards/arm/nrfmacro/nrfmacro.zmk.yml @@ -0,0 +1,10 @@ +file_format: "1" +id: nrfmacro +name: nrfMacro (epaper verion) +type: board +arch: arm +outputs: + - usb + - ble +url: https://github.com/ufan/nrfmacro/ +exposes: [pro_micro] diff --git a/app/boards/arm/nrfmacro/nrfmacro_defconfig b/app/boards/arm/nrfmacro/nrfmacro_defconfig new file mode 100644 index 00000000000..2da8d77c47b --- /dev/null +++ b/app/boards/arm/nrfmacro/nrfmacro_defconfig @@ -0,0 +1,23 @@ +# SPDX-License-Identifier: MIT + +CONFIG_SOC_SERIES_NRF52X=y +CONFIG_SOC_NRF52840_QIAA=y +CONFIG_BOARD_NRFMACRO=y + +# Enable MPU +CONFIG_ARM_MPU=y + +# enable GPIO +CONFIG_GPIO=y + +CONFIG_USE_DT_CODE_PARTITION=y +CONFIG_BUILD_OUTPUT_UF2=y + +CONFIG_MPU_ALLOW_FLASH_WRITE=y +CONFIG_NVS=y +CONFIG_SETTINGS_NVS=y +CONFIG_FLASH=y +CONFIG_FLASH_PAGE_LAYOUT=y +CONFIG_FLASH_MAP=y +CONFIG_CLOCK_CONTROL_NRF=y +CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC=y diff --git a/app/boards/arm/nrfmacro/widgets/CMakeLists.txt b/app/boards/arm/nrfmacro/widgets/CMakeLists.txt new file mode 100644 index 00000000000..29c5e2b57cb --- /dev/null +++ b/app/boards/arm/nrfmacro/widgets/CMakeLists.txt @@ -0,0 +1,7 @@ +# +# Copyright (c) 2021 Darryl deHaan +# SPDX-License-Identifier: MIT +# + +# add_subdirectory_ifdef(CONFIG_ZMK_DISPLAY_STATUS_SCREEN_CUSTOM fonts/) +# add_subdirectory_ifdef(CONFIG_ZMK_DISPLAY_STATUS_SCREEN_CUSTOM icons/) diff --git a/app/boards/arm/nrfmacro/widgets/battery_status.c b/app/boards/arm/nrfmacro/widgets/battery_status.c new file mode 100644 index 00000000000..6e8a5282f07 --- /dev/null +++ b/app/boards/arm/nrfmacro/widgets/battery_status.c @@ -0,0 +1,165 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include +#include + +#include +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#include +#include "battery_status.h" +#include +#include +#include +#include +#include + +static sys_slist_t widgets = SYS_SLIST_STATIC_INIT(&widgets); +static lv_style_t label_style; + +LV_IMG_DECLARE(batt_100); +LV_IMG_DECLARE(batt_100_chg); +LV_IMG_DECLARE(batt_75); +LV_IMG_DECLARE(batt_75_chg); +LV_IMG_DECLARE(batt_50); +LV_IMG_DECLARE(batt_50_chg); +LV_IMG_DECLARE(batt_25); +LV_IMG_DECLARE(batt_25_chg); +LV_IMG_DECLARE(batt_5); +LV_IMG_DECLARE(batt_5_chg); +LV_IMG_DECLARE(batt_0); +LV_IMG_DECLARE(batt_0_chg); + +static bool style_initialized = false; + +void battery_status_init() { + + if (style_initialized) { + return; + } + + style_initialized = true; + lv_style_init(&label_style); + lv_style_set_text_font(&label_style, LV_STATE_DEFAULT, &lv_font_montserrat_26); + lv_style_set_text_letter_space(&label_style, LV_STATE_DEFAULT, 1); + lv_style_set_text_line_space(&label_style, LV_STATE_DEFAULT, 1); + lv_style_set_text_color(&label_style, LV_STATE_DEFAULT, LV_COLOR_BLACK); + lv_style_set_bg_color(&label_style, LV_STATE_DEFAULT, LV_COLOR_WHITE); + + //lv_obj_t * batt_full_chg_icon = lv_img_create(lv_scr_act(), NULL); + //lv_img_set_src(batt_full_chg_icon, &batt_full_chg); +} + +K_MUTEX_DEFINE(battery_status_mutex); + +struct { + uint8_t level; +#if IS_ENABLED(CONFIG_USB_DEVICE_STACK) + bool usb_present; +#endif +} battery_status_state; + +void set_battery_symbol(lv_obj_t *icon) { + + k_mutex_lock(&battery_status_mutex, K_FOREVER); + + uint8_t level = battery_status_state.level; + +#if IS_ENABLED(CONFIG_USB_DEVICE_STACK) + if (level > 95) { + if (battery_status_state.usb_present) { + lv_img_set_src(icon, &batt_100_chg); + }else{ + lv_img_set_src(icon, &batt_100); + } + } else if (level > 74) { + if (battery_status_state.usb_present) { + lv_img_set_src(icon, &batt_75_chg); + }else{ + lv_img_set_src(icon, &batt_75); + } + } else if (level > 49) { + if (battery_status_state.usb_present) { + lv_img_set_src(icon, &batt_50_chg); + }else{ + lv_img_set_src(icon, &batt_50); + } + } else if (level > 24) { + if (battery_status_state.usb_present) { + lv_img_set_src(icon, &batt_25_chg); + }else{ + lv_img_set_src(icon, &batt_25); + } + } else if (level > 5) { + if (battery_status_state.usb_present) { + lv_img_set_src(icon, &batt_5_chg); + }else{ + lv_img_set_src(icon, &batt_5); + } + } else { + if (battery_status_state.usb_present) { + lv_img_set_src(icon, &batt_0_chg); + }else{ + lv_img_set_src(icon, &batt_0); + } + } + //lv_label_set_text(label, text); + //lv_img_set_src(icon, ); + +#endif /* IS_ENABLED(CONFIG_USB_DEVICE_STACK) */ + + k_mutex_unlock(&battery_status_mutex); + +} + +int zmk_widget_battery_status_init(struct zmk_widget_battery_status *widget, lv_obj_t *parent) { + battery_status_init(); + //widget->obj = lv_label_create(parent, NULL); + widget->obj = lv_img_create(parent, NULL); + //widget->obj2 = lv_label_create(parent, NULL); + lv_obj_add_style(widget->obj, LV_LABEL_PART_MAIN, &label_style); + + //lv_obj_set_size(widget->obj, 40, 15); + set_battery_symbol(widget->obj); + + sys_slist_append(&widgets, &widget->node); + + return 0; +} + +lv_obj_t *zmk_widget_battery_status_obj(struct zmk_widget_battery_status *widget) { + return widget->obj; +} + +void battery_status_update_cb(struct k_work *work) { + struct zmk_widget_battery_status *widget; + SYS_SLIST_FOR_EACH_CONTAINER(&widgets, widget, node) { set_battery_symbol(widget->obj); } +} + +K_WORK_DEFINE(battery_status_update_work, battery_status_update_cb); + +int battery_status_listener(const zmk_event_t *eh) { + k_mutex_lock(&battery_status_mutex, K_FOREVER); + + battery_status_state.level = bt_bas_get_battery_level(); + +#if IS_ENABLED(CONFIG_USB_DEVICE_STACK) + battery_status_state.usb_present = zmk_usb_is_powered(); +#endif /* IS_ENABLED(CONFIG_USB_DEVICE_STACK) */ + + k_mutex_unlock(&battery_status_mutex); + + k_work_submit_to_queue(zmk_display_work_q(), &battery_status_update_work); + return ZMK_EV_EVENT_BUBBLE; +} + +ZMK_LISTENER(widget_battery_status, battery_status_listener) +ZMK_SUBSCRIPTION(widget_battery_status, zmk_battery_state_changed); +#if IS_ENABLED(CONFIG_USB_DEVICE_STACK) +ZMK_SUBSCRIPTION(widget_battery_status, zmk_usb_conn_state_changed); +#endif /* IS_ENABLED(CONFIG_USB_DEVICE_STACK) */ diff --git a/app/boards/arm/nrfmacro/widgets/battery_status.h b/app/boards/arm/nrfmacro/widgets/battery_status.h new file mode 100644 index 00000000000..c2ad4bf4094 --- /dev/null +++ b/app/boards/arm/nrfmacro/widgets/battery_status.h @@ -0,0 +1,20 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#pragma once + +#include + +#include + +struct zmk_widget_battery_status { + sys_snode_t node; + lv_obj_t *obj; +}; + +int zmk_widget_battery_status_init(struct zmk_widget_battery_status *widget, lv_obj_t *parent); +lv_obj_t *zmk_widget_battery_status_obj(struct zmk_widget_battery_status *widget); \ No newline at end of file diff --git a/app/boards/arm/nrfmacro/widgets/icons/CMakeLists.txt b/app/boards/arm/nrfmacro/widgets/icons/CMakeLists.txt new file mode 100644 index 00000000000..19af8ead415 --- /dev/null +++ b/app/boards/arm/nrfmacro/widgets/icons/CMakeLists.txt @@ -0,0 +1,4 @@ +# +# Copyright (c) 2021 Darryl deHaan +# SPDX-License-Identifier: MIT +# \ No newline at end of file diff --git a/app/boards/arm/nrfmacro/widgets/icons/USB_connected.c b/app/boards/arm/nrfmacro/widgets/icons/USB_connected.c new file mode 100644 index 00000000000..8cf0badc38a --- /dev/null +++ b/app/boards/arm/nrfmacro/widgets/icons/USB_connected.c @@ -0,0 +1,63 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_USB_CONNECTED +#define LV_ATTRIBUTE_IMG_USB_CONNECTED +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_IMG_USB_CONNECTED uint8_t USB_connected_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0f, 0xff, 0xff, 0xc0, + 0x00, 0x3f, 0xff, 0xff, 0xc0, + 0x00, 0xff, 0xff, 0xff, 0xc0, + 0x01, 0xff, 0xff, 0xff, 0x80, + 0x03, 0xff, 0xff, 0xff, 0xfe, + 0x07, 0xff, 0xff, 0xff, 0xff, + 0x07, 0x1e, 0x30, 0x38, 0x07, + 0x0f, 0x1c, 0x20, 0x38, 0x07, + 0x0f, 0x1c, 0x47, 0x10, 0xc3, + 0x3e, 0x1c, 0x43, 0xf1, 0xc7, + 0x7e, 0x3c, 0x60, 0x70, 0x0e, + 0x7e, 0x3c, 0x70, 0x30, 0x0e, + 0x7e, 0x38, 0xfc, 0x33, 0xc7, + 0xfe, 0x18, 0x8f, 0x23, 0x87, + 0x0e, 0x00, 0xc6, 0x20, 0x07, + 0x0f, 0x01, 0xe0, 0x60, 0x0e, + 0x0f, 0x87, 0xf0, 0xe0, 0x3e, + 0x07, 0xff, 0xff, 0xff, 0xfc, + 0x07, 0xff, 0xff, 0xff, 0xf0, + 0x03, 0xff, 0xff, 0xfe, 0x00, + 0x01, 0xff, 0xff, 0xfc, 0x00, + 0x00, 0x7f, 0xff, 0xfc, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t USB_connected = { + .header.always_zero = 0, + .header.w = 40, + .header.h = 31, + .data_size = 164, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = USB_connected_map, +}; diff --git a/app/boards/arm/nrfmacro/widgets/icons/batt_0.c b/app/boards/arm/nrfmacro/widgets/icons/batt_0.c new file mode 100644 index 00000000000..d061f1d87bf --- /dev/null +++ b/app/boards/arm/nrfmacro/widgets/icons/batt_0.c @@ -0,0 +1,65 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_BATT_0 +#define LV_ATTRIBUTE_IMG_BATT_0 +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BATT_0 uint8_t batt_0_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x00, 0x7c, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x01, 0xff, 0x00, 0x00, + 0x00, 0x01, 0xff, 0x00, 0x00, + 0x7f, 0xfd, 0xff, 0x7f, 0xf8, + 0xff, 0xfd, 0xff, 0x7f, 0xfc, + 0xff, 0xfd, 0xff, 0x7f, 0xfc, + 0xff, 0xfc, 0xfe, 0x7f, 0xfc, + 0xf0, 0x00, 0xfe, 0x00, 0x3f, + 0xf0, 0x00, 0xfe, 0x00, 0x3f, + 0xf0, 0x00, 0xfe, 0x00, 0x3f, + 0xf0, 0x00, 0xfe, 0x00, 0x3f, + 0xf0, 0x00, 0xfe, 0x00, 0x0f, + 0xf0, 0x00, 0x7c, 0x00, 0x0f, + 0xf0, 0x00, 0x7c, 0x00, 0x0f, + 0xf0, 0x00, 0x7c, 0x00, 0x0f, + 0xf0, 0x00, 0x7c, 0x00, 0x3f, + 0xf0, 0x00, 0x7c, 0x00, 0x3f, + 0xf0, 0x00, 0x7c, 0x00, 0x3f, + 0xf0, 0x00, 0x00, 0x00, 0x3f, + 0xff, 0xff, 0x01, 0xff, 0xfc, + 0xff, 0xff, 0x01, 0xff, 0xfc, + 0xff, 0xfe, 0x7c, 0xff, 0xfc, + 0x7f, 0xfc, 0xfe, 0x7f, 0xf8, + 0x00, 0x01, 0xff, 0x00, 0x00, + 0x00, 0x01, 0xff, 0x00, 0x00, + 0x00, 0x01, 0xff, 0x00, 0x00, + 0x00, 0x01, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0x00, 0x00, +}; + +const lv_img_dsc_t batt_0 = { + .header.always_zero = 0, + .header.w = 40, + .header.h = 31, + .data_size = 163, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = batt_0_map, +}; + diff --git a/app/boards/arm/nrfmacro/widgets/icons/batt_0_chg.c b/app/boards/arm/nrfmacro/widgets/icons/batt_0_chg.c new file mode 100644 index 00000000000..e05e1524f09 --- /dev/null +++ b/app/boards/arm/nrfmacro/widgets/icons/batt_0_chg.c @@ -0,0 +1,64 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_BATT_0_CHG +#define LV_ATTRIBUTE_IMG_BATT_0_CHG +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BATT_0_CHG uint8_t batt_0_chg_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x00, 0x00, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x00, 0x00, 0xc0, 0x00, + 0x00, 0x00, 0x01, 0xc0, 0x00, + 0x00, 0x00, 0x03, 0x80, 0x00, + 0x7f, 0xff, 0xf7, 0xbf, 0xf8, + 0xff, 0xff, 0xef, 0xbf, 0xfc, + 0xff, 0xff, 0xdf, 0x7f, 0xfc, + 0xff, 0xff, 0xbf, 0x7f, 0xfc, + 0xf0, 0x00, 0x7e, 0x00, 0x3f, + 0xf0, 0x00, 0xfe, 0x00, 0x3f, + 0xf0, 0x01, 0xfe, 0x00, 0x3f, + 0xf0, 0x03, 0xfc, 0x00, 0x3f, + 0xf0, 0x07, 0xfc, 0x00, 0x0f, + 0xf0, 0x0f, 0xff, 0xe0, 0x0f, + 0xf0, 0x1f, 0xff, 0xc0, 0x0f, + 0xf0, 0x00, 0x7f, 0x80, 0x0f, + 0xf0, 0x00, 0x7f, 0x00, 0x3f, + 0xf0, 0x00, 0xfe, 0x00, 0x3f, + 0xf0, 0x00, 0xfc, 0x00, 0x3f, + 0xf0, 0x01, 0xf8, 0x00, 0x3f, + 0xff, 0xfd, 0xf7, 0xff, 0xfc, + 0xff, 0xfb, 0xef, 0xff, 0xfc, + 0xff, 0xfb, 0xdf, 0xff, 0xfc, + 0x7f, 0xfb, 0xbf, 0xff, 0xf8, + 0x00, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t batt_0_chg = { + .header.always_zero = 0, + .header.w = 40, + .header.h = 31, + .data_size = 163, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = batt_0_chg_map, +}; + diff --git a/app/boards/arm/nrfmacro/widgets/icons/batt_100.c b/app/boards/arm/nrfmacro/widgets/icons/batt_100.c new file mode 100644 index 00000000000..915472c3e2b --- /dev/null +++ b/app/boards/arm/nrfmacro/widgets/icons/batt_100.c @@ -0,0 +1,64 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_BATT_100 +#define LV_ATTRIBUTE_IMG_BATT_100 +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BATT_100 uint8_t batt_100_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0xff, 0xff, 0xff, 0xf8, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xf0, 0x00, 0x00, 0x00, 0x3f, + 0xf0, 0x00, 0x00, 0x00, 0x3f, + 0xf3, 0xff, 0xff, 0xff, 0x3f, + 0xf3, 0xff, 0xff, 0xff, 0x3f, + 0xf3, 0xff, 0xff, 0xff, 0x0f, + 0xf3, 0xff, 0xff, 0xff, 0x0f, + 0xf3, 0xff, 0xff, 0xff, 0x0f, + 0xf3, 0xff, 0xff, 0xff, 0x0f, + 0xf3, 0xff, 0xff, 0xff, 0x3f, + 0xf3, 0xff, 0xff, 0xff, 0x3f, + 0xf0, 0x00, 0x00, 0x00, 0x3f, + 0xf0, 0x00, 0x00, 0x00, 0x3f, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0x7f, 0xff, 0xff, 0xff, 0xf8, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t batt_100 = { + .header.always_zero = 0, + .header.w = 40, + .header.h = 31, + .data_size = 163, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = batt_100_map, +}; + diff --git a/app/boards/arm/nrfmacro/widgets/icons/batt_100_chg.c b/app/boards/arm/nrfmacro/widgets/icons/batt_100_chg.c new file mode 100644 index 00000000000..9ad05d61c76 --- /dev/null +++ b/app/boards/arm/nrfmacro/widgets/icons/batt_100_chg.c @@ -0,0 +1,65 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_BATT_100_CHG +#define LV_ATTRIBUTE_IMG_BATT_100_CHG +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BATT_100_CHG uint8_t batt_100_chg_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x00, 0x00, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x00, 0x00, 0xc0, 0x00, + 0x00, 0x00, 0x01, 0xc0, 0x00, + 0x00, 0x00, 0x03, 0x80, 0x00, + 0x7f, 0xff, 0xf7, 0xbf, 0xf8, + 0xff, 0xff, 0xef, 0xbf, 0xfc, + 0xff, 0xff, 0xdf, 0x7f, 0xfc, + 0xff, 0xff, 0xbf, 0x7f, 0xfc, + 0xf0, 0x00, 0x7e, 0x00, 0x3f, + 0xf0, 0x00, 0xfe, 0x00, 0x3f, + 0xf3, 0xfd, 0xfe, 0xff, 0x3f, + 0xf3, 0xfb, 0xfd, 0xff, 0x3f, + 0xf3, 0xf7, 0xfc, 0x07, 0x0f, + 0xf3, 0xef, 0xff, 0xef, 0x0f, + 0xf3, 0xdf, 0xff, 0xdf, 0x0f, + 0xf3, 0x80, 0x7f, 0xbf, 0x0f, + 0xf3, 0xff, 0x7f, 0x7f, 0x3f, + 0xf3, 0xfe, 0xfe, 0xff, 0x3f, + 0xf0, 0x00, 0xfc, 0x00, 0x3f, + 0xf0, 0x01, 0xf8, 0x00, 0x3f, + 0xff, 0xfd, 0xf7, 0xff, 0xfc, + 0xff, 0xfb, 0xef, 0xff, 0xfc, + 0xff, 0xfb, 0xdf, 0xff, 0xfc, + 0x7f, 0xfb, 0xbf, 0xff, 0xf8, + 0x00, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t batt_100_chg = { + .header.always_zero = 0, + .header.w = 40, + .header.h = 31, + .data_size = 163, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = batt_100_chg_map, +}; + diff --git a/app/boards/arm/nrfmacro/widgets/icons/batt_25.c b/app/boards/arm/nrfmacro/widgets/icons/batt_25.c new file mode 100644 index 00000000000..1dba62c2f6c --- /dev/null +++ b/app/boards/arm/nrfmacro/widgets/icons/batt_25.c @@ -0,0 +1,64 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_BATT_25 +#define LV_ATTRIBUTE_IMG_BATT_25 +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BATT_25 uint8_t batt_25_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0xff, 0xff, 0xff, 0xf8, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xf0, 0x00, 0x00, 0x00, 0x3f, + 0xf0, 0x00, 0x00, 0x00, 0x3f, + 0xf3, 0xfc, 0x00, 0x00, 0x3f, + 0xf3, 0xfc, 0x00, 0x00, 0x3f, + 0xf3, 0xfc, 0x00, 0x00, 0x0f, + 0xf3, 0xfc, 0x00, 0x00, 0x0f, + 0xf3, 0xfc, 0x00, 0x00, 0x0f, + 0xf3, 0xfc, 0x00, 0x00, 0x0f, + 0xf3, 0xfc, 0x00, 0x00, 0x3f, + 0xf3, 0xfc, 0x00, 0x00, 0x3f, + 0xf0, 0x00, 0x00, 0x00, 0x3f, + 0xf0, 0x00, 0x00, 0x00, 0x3f, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0x7f, 0xff, 0xff, 0xff, 0xf8, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t batt_25 = { + .header.always_zero = 0, + .header.w = 40, + .header.h = 31, + .data_size = 163, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = batt_25_map, +}; + diff --git a/app/boards/arm/nrfmacro/widgets/icons/batt_25_chg.c b/app/boards/arm/nrfmacro/widgets/icons/batt_25_chg.c new file mode 100644 index 00000000000..544e71db7c5 --- /dev/null +++ b/app/boards/arm/nrfmacro/widgets/icons/batt_25_chg.c @@ -0,0 +1,64 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_BATT_25_CHG +#define LV_ATTRIBUTE_IMG_BATT_25_CHG +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BATT_25_CHG uint8_t batt_25_chg_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x00, 0x00, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x00, 0x00, 0xc0, 0x00, + 0x00, 0x00, 0x01, 0xc0, 0x00, + 0x00, 0x00, 0x03, 0x80, 0x00, + 0x7f, 0xff, 0xf7, 0xbf, 0xf8, + 0xff, 0xff, 0xef, 0xbf, 0xfc, + 0xff, 0xff, 0xdf, 0x7f, 0xfc, + 0xff, 0xff, 0xbf, 0x7f, 0xfc, + 0xf0, 0x00, 0x7e, 0x00, 0x3f, + 0xf0, 0x00, 0xfe, 0x00, 0x3f, + 0xf3, 0xf9, 0xfe, 0x00, 0x3f, + 0xf3, 0xfb, 0xfc, 0x00, 0x3f, + 0xf3, 0xf7, 0xfc, 0x00, 0x0f, + 0xf3, 0xef, 0xff, 0xe0, 0x0f, + 0xf3, 0xdf, 0xff, 0xc0, 0x0f, + 0xf3, 0x80, 0x7f, 0x80, 0x0f, + 0xf3, 0xf8, 0x7f, 0x00, 0x3f, + 0xf3, 0xf8, 0xfe, 0x00, 0x3f, + 0xf0, 0x00, 0xfc, 0x00, 0x3f, + 0xf0, 0x01, 0xf8, 0x00, 0x3f, + 0xff, 0xfd, 0xf7, 0xff, 0xfc, + 0xff, 0xfb, 0xef, 0xff, 0xfc, + 0xff, 0xfb, 0xdf, 0xff, 0xfc, + 0x7f, 0xfb, 0xbf, 0xff, 0xf8, + 0x00, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t batt_25_chg = { + .header.always_zero = 0, + .header.w = 40, + .header.h = 31, + .data_size = 163, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = batt_25_chg_map, +}; + diff --git a/app/boards/arm/nrfmacro/widgets/icons/batt_5.c b/app/boards/arm/nrfmacro/widgets/icons/batt_5.c new file mode 100644 index 00000000000..a8c5ded9289 --- /dev/null +++ b/app/boards/arm/nrfmacro/widgets/icons/batt_5.c @@ -0,0 +1,64 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_BATT_5 +#define LV_ATTRIBUTE_IMG_BATT_5 +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BATT_5 uint8_t batt_5_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0xff, 0xff, 0xff, 0xf8, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xf0, 0x00, 0x00, 0x00, 0x3f, + 0xf0, 0x00, 0x00, 0x00, 0x3f, + 0xf3, 0x00, 0x00, 0x00, 0x3f, + 0xf3, 0x00, 0x00, 0x00, 0x3f, + 0xf3, 0x00, 0x00, 0x00, 0x0f, + 0xf3, 0x00, 0x00, 0x00, 0x0f, + 0xf3, 0x00, 0x00, 0x00, 0x0f, + 0xf3, 0x00, 0x00, 0x00, 0x0f, + 0xf3, 0x00, 0x00, 0x00, 0x3f, + 0xf3, 0x00, 0x00, 0x00, 0x3f, + 0xf0, 0x00, 0x00, 0x00, 0x3f, + 0xf0, 0x00, 0x00, 0x00, 0x3f, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0x7f, 0xff, 0xff, 0xff, 0xf8, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t batt_5 = { + .header.always_zero = 0, + .header.w = 40, + .header.h = 31, + .data_size = 163, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = batt_5_map, +}; + diff --git a/app/boards/arm/nrfmacro/widgets/icons/batt_50.c b/app/boards/arm/nrfmacro/widgets/icons/batt_50.c new file mode 100644 index 00000000000..3c93ae5bc71 --- /dev/null +++ b/app/boards/arm/nrfmacro/widgets/icons/batt_50.c @@ -0,0 +1,64 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_BATT_50 +#define LV_ATTRIBUTE_IMG_BATT_50 +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BATT_50 uint8_t batt_50_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0xff, 0xff, 0xff, 0xf8, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xf0, 0x00, 0x00, 0x00, 0x3f, + 0xf0, 0x00, 0x00, 0x00, 0x3f, + 0xf3, 0xff, 0xf0, 0x00, 0x3f, + 0xf3, 0xff, 0xf0, 0x00, 0x3f, + 0xf3, 0xff, 0xf0, 0x00, 0x0f, + 0xf3, 0xff, 0xf0, 0x00, 0x0f, + 0xf3, 0xff, 0xf0, 0x00, 0x0f, + 0xf3, 0xff, 0xf0, 0x00, 0x0f, + 0xf3, 0xff, 0xf0, 0x00, 0x3f, + 0xf3, 0xff, 0xf0, 0x00, 0x3f, + 0xf0, 0x00, 0x00, 0x00, 0x3f, + 0xf0, 0x00, 0x00, 0x00, 0x3f, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0x7f, 0xff, 0xff, 0xff, 0xf8, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t batt_50 = { + .header.always_zero = 0, + .header.w = 40, + .header.h = 31, + .data_size = 163, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = batt_50_map, +}; + diff --git a/app/boards/arm/nrfmacro/widgets/icons/batt_50_chg.c b/app/boards/arm/nrfmacro/widgets/icons/batt_50_chg.c new file mode 100644 index 00000000000..b21b4e25df3 --- /dev/null +++ b/app/boards/arm/nrfmacro/widgets/icons/batt_50_chg.c @@ -0,0 +1,64 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_BATT_50_CHG +#define LV_ATTRIBUTE_IMG_BATT_50_CHG +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BATT_50_CHG uint8_t batt_50_chg_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x00, 0x00, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x00, 0x00, 0xc0, 0x00, + 0x00, 0x00, 0x01, 0xc0, 0x00, + 0x00, 0x00, 0x03, 0x80, 0x00, + 0x7f, 0xff, 0xf7, 0xbf, 0xf8, + 0xff, 0xff, 0xef, 0xbf, 0xfc, + 0xff, 0xff, 0xdf, 0x7f, 0xfc, + 0xff, 0xff, 0xbf, 0x7f, 0xfc, + 0xf0, 0x00, 0x7e, 0x00, 0x3f, + 0xf0, 0x00, 0xfe, 0x00, 0x3f, + 0xf3, 0xfd, 0xfe, 0x00, 0x3f, + 0xf3, 0xfb, 0xfc, 0x00, 0x3f, + 0xf3, 0xf7, 0xfc, 0x00, 0x0f, + 0xf3, 0xef, 0xff, 0xe0, 0x0f, + 0xf3, 0xdf, 0xff, 0xc0, 0x0f, + 0xf3, 0x80, 0x7f, 0x80, 0x0f, + 0xf3, 0xff, 0x7f, 0x00, 0x3f, + 0xf3, 0xfe, 0xfe, 0x00, 0x3f, + 0xf0, 0x00, 0xfc, 0x00, 0x3f, + 0xf0, 0x01, 0xf8, 0x00, 0x3f, + 0xff, 0xfd, 0xf7, 0xff, 0xfc, + 0xff, 0xfb, 0xef, 0xff, 0xfc, + 0xff, 0xfb, 0xdf, 0xff, 0xfc, + 0x7f, 0xfb, 0xbf, 0xff, 0xf8, + 0x00, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t batt_50_chg = { + .header.always_zero = 0, + .header.w = 40, + .header.h = 31, + .data_size = 163, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = batt_50_chg_map, +}; + diff --git a/app/boards/arm/nrfmacro/widgets/icons/batt_5_chg.c b/app/boards/arm/nrfmacro/widgets/icons/batt_5_chg.c new file mode 100644 index 00000000000..da96fc4da1b --- /dev/null +++ b/app/boards/arm/nrfmacro/widgets/icons/batt_5_chg.c @@ -0,0 +1,64 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_BATT_5_CHG +#define LV_ATTRIBUTE_IMG_BATT_5_CHG +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BATT_5_CHG uint8_t batt_5_chg_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x00, 0x00, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x00, 0x00, 0xc0, 0x00, + 0x00, 0x00, 0x01, 0xc0, 0x00, + 0x00, 0x00, 0x03, 0x80, 0x00, + 0x7f, 0xff, 0xf7, 0xbf, 0xf8, + 0xff, 0xff, 0xef, 0xbf, 0xfc, + 0xff, 0xff, 0xdf, 0x7f, 0xfc, + 0xff, 0xff, 0xbf, 0x7f, 0xfc, + 0xf0, 0x00, 0x7e, 0x00, 0x3f, + 0xf0, 0x00, 0xfe, 0x00, 0x3f, + 0xf3, 0x01, 0xfe, 0x00, 0x3f, + 0xf3, 0x03, 0xfc, 0x00, 0x3f, + 0xf3, 0x07, 0xfc, 0x00, 0x0f, + 0xf3, 0x0f, 0xff, 0xe0, 0x0f, + 0xf3, 0x1f, 0xff, 0xc0, 0x0f, + 0xf3, 0x00, 0x7f, 0x80, 0x0f, + 0xf3, 0x00, 0x7f, 0x00, 0x3f, + 0xf3, 0x00, 0xfe, 0x00, 0x3f, + 0xf0, 0x00, 0xfc, 0x00, 0x3f, + 0xf0, 0x01, 0xf8, 0x00, 0x3f, + 0xff, 0xfd, 0xf7, 0xff, 0xfc, + 0xff, 0xfb, 0xef, 0xff, 0xfc, + 0xff, 0xfb, 0xdf, 0xff, 0xfc, + 0x7f, 0xfb, 0xbf, 0xff, 0xf8, + 0x00, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t batt_5_chg = { + .header.always_zero = 0, + .header.w = 40, + .header.h = 31, + .data_size = 163, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = batt_5_chg_map, +}; + diff --git a/app/boards/arm/nrfmacro/widgets/icons/batt_75.c b/app/boards/arm/nrfmacro/widgets/icons/batt_75.c new file mode 100644 index 00000000000..cbba208ef88 --- /dev/null +++ b/app/boards/arm/nrfmacro/widgets/icons/batt_75.c @@ -0,0 +1,64 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_BATT_75 +#define LV_ATTRIBUTE_IMG_BATT_75 +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BATT_75 uint8_t batt_75_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0xff, 0xff, 0xff, 0xf8, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xf0, 0x00, 0x00, 0x00, 0x3f, + 0xf0, 0x00, 0x00, 0x00, 0x3f, + 0xf3, 0xff, 0xff, 0xe0, 0x3f, + 0xf3, 0xff, 0xff, 0xe0, 0x3f, + 0xf3, 0xff, 0xff, 0xe0, 0x0f, + 0xf3, 0xff, 0xff, 0xe0, 0x0f, + 0xf3, 0xff, 0xff, 0xe0, 0x0f, + 0xf3, 0xff, 0xff, 0xe0, 0x0f, + 0xf3, 0xff, 0xff, 0xe0, 0x3f, + 0xf3, 0xff, 0xff, 0xe0, 0x3f, + 0xf0, 0x00, 0x00, 0x00, 0x3f, + 0xf0, 0x00, 0x00, 0x00, 0x3f, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0xfc, + 0x7f, 0xff, 0xff, 0xff, 0xf8, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t batt_75 = { + .header.always_zero = 0, + .header.w = 40, + .header.h = 31, + .data_size = 163, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = batt_75_map, +}; + diff --git a/app/boards/arm/nrfmacro/widgets/icons/batt_75_chg.c b/app/boards/arm/nrfmacro/widgets/icons/batt_75_chg.c new file mode 100644 index 00000000000..b2c2298d3b2 --- /dev/null +++ b/app/boards/arm/nrfmacro/widgets/icons/batt_75_chg.c @@ -0,0 +1,64 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_BATT_75_CHG +#define LV_ATTRIBUTE_IMG_BATT_75_CHG +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BATT_75_CHG uint8_t batt_75_chg_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x00, 0x00, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x00, 0x00, 0xc0, 0x00, + 0x00, 0x00, 0x01, 0xc0, 0x00, + 0x00, 0x00, 0x03, 0x80, 0x00, + 0x7f, 0xff, 0xf7, 0xbf, 0xf8, + 0xff, 0xff, 0xef, 0xbf, 0xfc, + 0xff, 0xff, 0xdf, 0x7f, 0xfc, + 0xff, 0xff, 0xbf, 0x7f, 0xfc, + 0xf0, 0x00, 0x7e, 0x00, 0x3f, + 0xf0, 0x00, 0xfe, 0x00, 0x3f, + 0xf3, 0xfd, 0xfe, 0xe0, 0x3f, + 0xf3, 0xfb, 0xfd, 0xe0, 0x3f, + 0xf3, 0xf7, 0xfc, 0x00, 0x0f, + 0xf3, 0xef, 0xff, 0xe0, 0x0f, + 0xf3, 0xdf, 0xff, 0xc0, 0x0f, + 0xf3, 0x80, 0x7f, 0xa0, 0x0f, + 0xf3, 0xff, 0x7f, 0x60, 0x3f, + 0xf3, 0xfe, 0xfe, 0xe0, 0x3f, + 0xf0, 0x00, 0xfc, 0x00, 0x3f, + 0xf0, 0x01, 0xf8, 0x00, 0x3f, + 0xff, 0xfd, 0xf7, 0xff, 0xfc, + 0xff, 0xfb, 0xef, 0xff, 0xfc, + 0xff, 0xfb, 0xdf, 0xff, 0xfc, + 0x7f, 0xfb, 0xbf, 0xff, 0xf8, + 0x00, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t batt_75_chg = { + .header.always_zero = 0, + .header.w = 40, + .header.h = 31, + .data_size = 163, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = batt_75_chg_map, +}; + diff --git a/app/boards/arm/nrfmacro/widgets/icons/bluetooth_advertising.c b/app/boards/arm/nrfmacro/widgets/icons/bluetooth_advertising.c new file mode 100644 index 00000000000..89809d1ffbf --- /dev/null +++ b/app/boards/arm/nrfmacro/widgets/icons/bluetooth_advertising.c @@ -0,0 +1,66 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_BLUETOOTH_ADVERTISING +#define LV_ATTRIBUTE_IMG_BLUETOOTH_ADVERTISING +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BLUETOOTH_ADVERTISING uint8_t bluetooth_advertising_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x40, 0x00, 0x00, + 0x00, 0x60, 0x00, 0x00, + 0x00, 0x70, 0x00, 0x00, + 0x00, 0x78, 0x00, 0x00, + 0x00, 0x7c, 0x00, 0x00, + 0x00, 0x7e, 0x00, 0x00, + 0x00, 0x7f, 0x00, 0x00, + 0x20, 0x7f, 0x80, 0x00, + 0x70, 0x77, 0xc0, 0x00, + 0xf8, 0x73, 0xe0, 0xc0, + 0x7c, 0x71, 0xe0, 0xe0, + 0x3e, 0x73, 0xc0, 0x70, + 0x1f, 0x77, 0x86, 0x30, + 0x0f, 0xff, 0x07, 0x30, + 0x07, 0xfe, 0x07, 0x38, + 0x03, 0xfc, 0x23, 0x38, + 0x01, 0xf8, 0x63, 0x18, + 0x01, 0xf8, 0x63, 0x18, + 0x03, 0xfc, 0x23, 0x38, + 0x07, 0xfe, 0x07, 0x38, + 0x0f, 0xff, 0x07, 0x30, + 0x1f, 0x77, 0x86, 0x70, + 0x3e, 0x73, 0xc0, 0x70, + 0x7c, 0x71, 0xe0, 0xe0, + 0xf8, 0x73, 0xe0, 0xc0, + 0x70, 0x77, 0xc0, 0x00, + 0x20, 0x7f, 0x80, 0x00, + 0x00, 0x7f, 0x00, 0x00, + 0x00, 0x7e, 0x00, 0x00, + 0x00, 0x7c, 0x00, 0x00, + 0x00, 0x78, 0x00, 0x00, + 0x00, 0x70, 0x00, 0x00, + 0x00, 0x60, 0x00, 0x00, + 0x00, 0x40, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t bluetooth_advertising = { + .header.always_zero = 0, + .header.w = 29, + .header.h = 35, + .data_size = 148, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = bluetooth_advertising_map, +}; diff --git a/app/boards/arm/nrfmacro/widgets/icons/bluetooth_advertising_1.c b/app/boards/arm/nrfmacro/widgets/icons/bluetooth_advertising_1.c new file mode 100644 index 00000000000..9fdc8e55a47 --- /dev/null +++ b/app/boards/arm/nrfmacro/widgets/icons/bluetooth_advertising_1.c @@ -0,0 +1,64 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif +#ifndef LV_ATTRIBUTE_IMG_BLUETOOTH_ADVERTISING_1 +#define LV_ATTRIBUTE_IMG_BLUETOOTH_ADVERTISING_1 +#endif +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_IMG_BLUETOOTH_ADVERTISING_1 uint8_t bluetooth_advertising_1_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xfc, 0x00, 0x00, 0x01, 0xfc, 0x00, + 0x00, 0xfe, 0x00, 0x00, 0x0f, 0xff, 0x00, + 0x20, 0xff, 0x00, 0x00, 0x1f, 0xff, 0xc0, + 0x70, 0xf7, 0x80, 0x00, 0x3f, 0xff, 0xe0, + 0xf8, 0xf3, 0xc1, 0xc0, 0x7f, 0xff, 0xe0, + 0x7c, 0xf3, 0xe0, 0xc0, 0xff, 0xff, 0xf0, + 0x3e, 0xf7, 0xc0, 0xe0, 0xff, 0x8f, 0xf8, + 0x1f, 0xff, 0x84, 0x61, 0xfe, 0x0f, 0xf8, + 0x0f, 0xff, 0x0e, 0x71, 0xfe, 0x0f, 0xf8, + 0x07, 0xfe, 0x06, 0x31, 0xff, 0xcf, 0xfc, + 0x03, 0xfc, 0x27, 0x33, 0xff, 0xcf, 0xfc, + 0x01, 0xf8, 0x67, 0x33, 0xff, 0xcf, 0xfc, + 0x01, 0xf8, 0x67, 0x33, 0xff, 0xcf, 0xfc, + 0x03, 0xfc, 0x27, 0x33, 0xff, 0xcf, 0xfc, + 0x07, 0xfe, 0x06, 0x31, 0xff, 0xcf, 0xfc, + 0x0f, 0xff, 0x0e, 0x71, 0xff, 0xcf, 0xf8, + 0x1f, 0xff, 0x84, 0x61, 0xff, 0xcf, 0xf8, + 0x3e, 0xf7, 0xc0, 0xe0, 0xff, 0xcf, 0xf8, + 0x7c, 0xf3, 0xe1, 0xc0, 0xff, 0xff, 0xf0, + 0xf8, 0xf3, 0xc0, 0xc0, 0x7f, 0xff, 0xe0, + 0x70, 0xf7, 0x80, 0x00, 0x3f, 0xff, 0xe0, + 0x20, 0xff, 0x00, 0x00, 0x1f, 0xff, 0x80, + 0x00, 0xfe, 0x00, 0x00, 0x0f, 0xff, 0x00, + 0x00, 0xfc, 0x00, 0x00, 0x01, 0xfc, 0x00, + 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t bluetooth_advertising_1 = { + .header.always_zero = 0, + .header.w = 54, + .header.h = 35, + .data_size = 254, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = bluetooth_advertising_1_map, +}; diff --git a/app/boards/arm/nrfmacro/widgets/icons/bluetooth_advertising_2.c b/app/boards/arm/nrfmacro/widgets/icons/bluetooth_advertising_2.c new file mode 100644 index 00000000000..a0c4345d99b --- /dev/null +++ b/app/boards/arm/nrfmacro/widgets/icons/bluetooth_advertising_2.c @@ -0,0 +1,64 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif +#ifndef LV_ATTRIBUTE_IMG_BLUETOOTH_ADVERTISING_2 +#define LV_ATTRIBUTE_IMG_BLUETOOTH_ADVERTISING_2 +#endif +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_IMG_BLUETOOTH_ADVERTISING_2 uint8_t bluetooth_advertising_2_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xfe, 0x00, 0x00, 0x03, 0xfc, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x0f, 0xff, 0x00, + 0x60, 0xff, 0x80, 0x00, 0x1f, 0xff, 0xc0, + 0xf0, 0xf7, 0xc0, 0x80, 0x7f, 0xff, 0xe0, + 0x78, 0xf3, 0xe1, 0xc0, 0x7f, 0xff, 0xf0, + 0x3c, 0xf3, 0xc0, 0xe0, 0xff, 0x0f, 0xf0, + 0x1e, 0xf7, 0x84, 0xe1, 0xfe, 0x07, 0xf8, + 0x0f, 0xff, 0x0e, 0x71, 0xfc, 0x63, 0xf8, + 0x07, 0xfe, 0x0e, 0x71, 0xff, 0xf3, 0xfc, + 0x03, 0xfc, 0x06, 0x33, 0xff, 0xe3, 0xfc, + 0x01, 0xf8, 0x67, 0x33, 0xff, 0xe7, 0xfc, + 0x01, 0xf8, 0xe7, 0x33, 0xff, 0xc7, 0xfc, + 0x01, 0xfc, 0x67, 0x33, 0xff, 0x8f, 0xfc, + 0x03, 0xfe, 0x06, 0x33, 0xff, 0x1f, 0xfc, + 0x07, 0xff, 0x0e, 0x71, 0xfe, 0x3f, 0xfc, + 0x0f, 0xff, 0x8e, 0x61, 0xfc, 0x03, 0xf8, + 0x1e, 0xf7, 0xc0, 0xe1, 0xfc, 0x03, 0xf8, + 0x3c, 0xf3, 0xe0, 0xc0, 0xff, 0xff, 0xf0, + 0x78, 0xf3, 0xe1, 0xc0, 0x7f, 0xff, 0xf0, + 0xf0, 0xf7, 0xc0, 0x80, 0x7f, 0xff, 0xe0, + 0x60, 0xff, 0x80, 0x00, 0x1f, 0xff, 0xc0, + 0x00, 0xff, 0x00, 0x00, 0x0f, 0xff, 0x00, + 0x00, 0xfe, 0x00, 0x00, 0x03, 0xfc, 0x00, + 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t bluetooth_advertising_2 = { + .header.always_zero = 0, + .header.w = 54, + .header.h = 35, + .data_size = 254, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = bluetooth_advertising_2_map, +}; diff --git a/app/boards/arm/nrfmacro/widgets/icons/bluetooth_advertising_3.c b/app/boards/arm/nrfmacro/widgets/icons/bluetooth_advertising_3.c new file mode 100644 index 00000000000..3e70f082868 --- /dev/null +++ b/app/boards/arm/nrfmacro/widgets/icons/bluetooth_advertising_3.c @@ -0,0 +1,64 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif +#ifndef LV_ATTRIBUTE_IMG_BLUETOOTH_ADVERTISING_3 +#define LV_ATTRIBUTE_IMG_BLUETOOTH_ADVERTISING_3 +#endif +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_IMG_BLUETOOTH_ADVERTISING_3 uint8_t bluetooth_advertising_3_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xfc, 0x00, 0x00, 0x01, 0xfc, 0x00, + 0x00, 0xfe, 0x00, 0x00, 0x0f, 0xff, 0x00, + 0x20, 0xff, 0x00, 0x00, 0x1f, 0xff, 0xc0, + 0x70, 0xf7, 0x80, 0x00, 0x3f, 0xff, 0xe0, + 0xf8, 0xf3, 0xc1, 0xc0, 0x7f, 0xff, 0xf0, + 0x7c, 0xf3, 0xe0, 0xc0, 0xff, 0x9f, 0xf0, + 0x3e, 0xf7, 0xc0, 0xe0, 0xfe, 0x07, 0xf8, + 0x1f, 0xff, 0x84, 0x61, 0xfe, 0x03, 0xf8, + 0x0f, 0xff, 0x0e, 0x71, 0xfe, 0xe3, 0xfc, + 0x07, 0xfe, 0x06, 0x31, 0xff, 0xe3, 0xfc, + 0x03, 0xfc, 0x27, 0x33, 0xff, 0x87, 0xfc, + 0x01, 0xf8, 0x67, 0x33, 0xff, 0x87, 0xfc, + 0x01, 0xf8, 0x67, 0x33, 0xff, 0x83, 0xfc, + 0x03, 0xfc, 0x27, 0x33, 0xff, 0xf3, 0xfc, + 0x07, 0xfe, 0x06, 0x31, 0xfe, 0xf3, 0xfc, + 0x0f, 0xff, 0x0e, 0x71, 0xfc, 0x63, 0xfc, + 0x1f, 0xff, 0x84, 0x61, 0xfe, 0x03, 0xf8, + 0x3e, 0xf7, 0xc0, 0xe0, 0xff, 0x0f, 0xf8, + 0x7c, 0xf3, 0xe1, 0xc0, 0xff, 0xff, 0xf0, + 0xf8, 0xf3, 0xc0, 0xc0, 0x7f, 0xff, 0xf0, + 0x70, 0xf7, 0x80, 0x00, 0x3f, 0xff, 0xe0, + 0x20, 0xff, 0x00, 0x00, 0x1f, 0xff, 0xc0, + 0x00, 0xfe, 0x00, 0x00, 0x0f, 0xff, 0x00, + 0x00, 0xfc, 0x00, 0x00, 0x01, 0xfc, 0x00, + 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t bluetooth_advertising_3 = { + .header.always_zero = 0, + .header.w = 54, + .header.h = 35, + .data_size = 254, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = bluetooth_advertising_3_map, +}; diff --git a/app/boards/arm/nrfmacro/widgets/icons/bluetooth_advertising_4.c b/app/boards/arm/nrfmacro/widgets/icons/bluetooth_advertising_4.c new file mode 100644 index 00000000000..3e8441f7b37 --- /dev/null +++ b/app/boards/arm/nrfmacro/widgets/icons/bluetooth_advertising_4.c @@ -0,0 +1,64 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif +#ifndef LV_ATTRIBUTE_IMG_BLUETOOTH_ADVERTISING_4 +#define LV_ATTRIBUTE_IMG_BLUETOOTH_ADVERTISING_4 +#endif +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_IMG_BLUETOOTH_ADVERTISING_4 uint8_t bluetooth_advertising_4_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xfc, 0x00, 0x00, 0x01, 0xfc, 0x00, + 0x00, 0xfe, 0x00, 0x00, 0x0f, 0xff, 0x00, + 0x20, 0xff, 0x00, 0x00, 0x1f, 0xff, 0xc0, + 0x70, 0xf7, 0x80, 0x00, 0x3f, 0xff, 0xe0, + 0xf8, 0xf3, 0xc0, 0xc0, 0x7f, 0xff, 0xe0, + 0x7c, 0xf3, 0xe0, 0xc0, 0xff, 0xff, 0xf0, + 0x3e, 0xf7, 0xc0, 0xe0, 0xff, 0xcf, 0xf8, + 0x1f, 0xff, 0x84, 0x61, 0xff, 0x8f, 0xf8, + 0x0f, 0xff, 0x0e, 0x71, 0xff, 0x0f, 0xf8, + 0x07, 0xfe, 0x06, 0x31, 0xff, 0x0f, 0xfc, + 0x03, 0xfc, 0x27, 0x31, 0xfe, 0x4f, 0xfc, + 0x01, 0xf8, 0x67, 0x31, 0xfc, 0x4f, 0xfc, + 0x01, 0xf8, 0x67, 0x33, 0xfc, 0xcf, 0xfc, + 0x03, 0xfc, 0x27, 0x31, 0xf8, 0x07, 0xfc, + 0x07, 0xfe, 0x06, 0x31, 0xf8, 0x03, 0xfc, + 0x0f, 0xff, 0x0e, 0x71, 0xf8, 0x07, 0xf8, + 0x1f, 0xff, 0x84, 0x61, 0xff, 0xcf, 0xf8, + 0x3e, 0xf7, 0xc0, 0xe0, 0xff, 0xcf, 0xf8, + 0x7c, 0xf3, 0xe1, 0xc0, 0xff, 0xff, 0xf0, + 0xf8, 0xf3, 0xc0, 0xc0, 0x7f, 0xff, 0xe0, + 0x70, 0xf7, 0x80, 0x00, 0x3f, 0xff, 0xc0, + 0x20, 0xff, 0x00, 0x00, 0x1f, 0xff, 0x80, + 0x00, 0xfe, 0x00, 0x00, 0x07, 0xff, 0x00, + 0x00, 0xfc, 0x00, 0x00, 0x01, 0xf8, 0x00, + 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t bluetooth_advertising_4 = { + .header.always_zero = 0, + .header.w = 54, + .header.h = 35, + .data_size = 254, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = bluetooth_advertising_4_map, +}; diff --git a/app/boards/arm/nrfmacro/widgets/icons/bluetooth_advertising_5.c b/app/boards/arm/nrfmacro/widgets/icons/bluetooth_advertising_5.c new file mode 100644 index 00000000000..3b24c9d81e8 --- /dev/null +++ b/app/boards/arm/nrfmacro/widgets/icons/bluetooth_advertising_5.c @@ -0,0 +1,64 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif +#ifndef LV_ATTRIBUTE_IMG_BLUETOOTH_ADVERTISING_5 +#define LV_ATTRIBUTE_IMG_BLUETOOTH_ADVERTISING_5 +#endif +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_IMG_BLUETOOTH_ADVERTISING_5 uint8_t bluetooth_advertising_5_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xfc, 0x00, 0x00, 0x01, 0xfc, 0x00, + 0x00, 0xfe, 0x00, 0x00, 0x0f, 0xff, 0x00, + 0x20, 0xff, 0x00, 0x00, 0x1f, 0xff, 0x80, + 0x70, 0xf7, 0x80, 0x00, 0x3f, 0xff, 0xe0, + 0xf8, 0xf3, 0xc0, 0xc0, 0x7f, 0xff, 0xe0, + 0x7c, 0xf3, 0xe0, 0xe0, 0xff, 0xff, 0xf0, + 0x3e, 0xf7, 0xc0, 0xe1, 0xfe, 0x07, 0xf8, + 0x1f, 0xff, 0x84, 0x61, 0xfe, 0x07, 0xf8, + 0x0f, 0xff, 0x0e, 0x71, 0xfc, 0x7f, 0xf8, + 0x07, 0xfe, 0x06, 0x33, 0xfc, 0x7f, 0xfc, + 0x03, 0xfc, 0x27, 0x33, 0xfc, 0x07, 0xfc, + 0x01, 0xf8, 0x67, 0x33, 0xfc, 0x03, 0xfc, + 0x01, 0xf8, 0x67, 0x33, 0xff, 0x63, 0xfc, + 0x03, 0xfc, 0x27, 0x33, 0xff, 0xf3, 0xfc, + 0x07, 0xfe, 0x06, 0x33, 0xfe, 0xf3, 0xfc, + 0x0f, 0xff, 0x0e, 0x71, 0xfc, 0x63, 0xf8, + 0x1f, 0xff, 0x84, 0x61, 0xfe, 0x07, 0xf8, + 0x3e, 0xf7, 0xc0, 0xe0, 0xff, 0x0f, 0xf8, + 0x7c, 0xf3, 0xe1, 0xc0, 0xff, 0xff, 0xf0, + 0xf8, 0xf3, 0xc0, 0xc0, 0x7f, 0xff, 0xe0, + 0x70, 0xf7, 0x80, 0x00, 0x3f, 0xff, 0xc0, + 0x20, 0xff, 0x00, 0x00, 0x1f, 0xff, 0x80, + 0x00, 0xfe, 0x00, 0x00, 0x0f, 0xff, 0x00, + 0x00, 0xfc, 0x00, 0x00, 0x01, 0xf8, 0x00, + 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t bluetooth_advertising_5 = { + .header.always_zero = 0, + .header.w = 54, + .header.h = 35, + .data_size = 254, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = bluetooth_advertising_5_map, +}; diff --git a/app/boards/arm/nrfmacro/widgets/icons/bluetooth_connected_1.c b/app/boards/arm/nrfmacro/widgets/icons/bluetooth_connected_1.c new file mode 100644 index 00000000000..1cb545aa2b2 --- /dev/null +++ b/app/boards/arm/nrfmacro/widgets/icons/bluetooth_connected_1.c @@ -0,0 +1,64 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif +#ifndef LV_ATTRIBUTE_IMG_BLUETOOTH_CONNECTED_1 +#define LV_ATTRIBUTE_IMG_BLUETOOTH_CONNECTED_1 +#endif +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_IMG_BLUETOOTH_CONNECTED_1 uint8_t bluetooth_connected_1_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7e, 0x00, 0x00, 0x01, 0xfc, 0x00, + 0x00, 0x7f, 0x00, 0x00, 0x0f, 0xff, 0x00, + 0x30, 0x7f, 0x80, 0x00, 0x1f, 0xff, 0xc0, + 0x78, 0x77, 0xc0, 0x00, 0x3f, 0xff, 0xe0, + 0x7c, 0x73, 0xe0, 0x00, 0x7f, 0xff, 0xe0, + 0x3e, 0x71, 0xe0, 0x00, 0xff, 0xff, 0xf0, + 0x1f, 0x73, 0xc0, 0x00, 0xff, 0x8f, 0xf8, + 0x0f, 0xff, 0x80, 0x01, 0xfe, 0x0f, 0xf8, + 0x87, 0xff, 0x08, 0x01, 0xfe, 0x0f, 0xf8, + 0xc3, 0xfe, 0x18, 0x01, 0xff, 0xcf, 0xfc, + 0xe1, 0xfc, 0x38, 0x03, 0xff, 0xcf, 0xfc, + 0xf0, 0xf8, 0x78, 0x03, 0xff, 0xcf, 0xfc, + 0xf0, 0xfc, 0x78, 0x03, 0xff, 0xcf, 0xfc, + 0xe1, 0xfc, 0x38, 0x03, 0xff, 0xcf, 0xfc, + 0xc3, 0xfe, 0x18, 0x01, 0xff, 0xcf, 0xfc, + 0x87, 0xff, 0x08, 0x01, 0xff, 0xcf, 0xf8, + 0x0f, 0xff, 0x80, 0x01, 0xff, 0xcf, 0xf8, + 0x1e, 0x73, 0xc0, 0x00, 0xff, 0xcf, 0xf8, + 0x3c, 0x71, 0xe0, 0x00, 0xff, 0xff, 0xf0, + 0x78, 0x73, 0xe0, 0x00, 0x7f, 0xff, 0xe0, + 0x78, 0x77, 0xc0, 0x00, 0x3f, 0xff, 0xe0, + 0x30, 0x7f, 0x80, 0x00, 0x1f, 0xff, 0x80, + 0x00, 0x7f, 0x00, 0x00, 0x0f, 0xff, 0x00, + 0x00, 0x7e, 0x00, 0x00, 0x01, 0xfc, 0x00, + 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t bluetooth_connected_1 = { + .header.always_zero = 0, + .header.w = 54, + .header.h = 35, + .data_size = 254, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = bluetooth_connected_1_map, +}; diff --git a/app/boards/arm/nrfmacro/widgets/icons/bluetooth_connected_2.c b/app/boards/arm/nrfmacro/widgets/icons/bluetooth_connected_2.c new file mode 100644 index 00000000000..c5775a61167 --- /dev/null +++ b/app/boards/arm/nrfmacro/widgets/icons/bluetooth_connected_2.c @@ -0,0 +1,64 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif +#ifndef LV_ATTRIBUTE_IMG_BLUETOOTH_CONNECTED_2 +#define LV_ATTRIBUTE_IMG_BLUETOOTH_CONNECTED_2 +#endif +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_IMG_BLUETOOTH_CONNECTED_2 uint8_t bluetooth_connected_2_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7e, 0x00, 0x00, 0x03, 0xfc, 0x00, + 0x00, 0x7f, 0x00, 0x00, 0x0f, 0xff, 0x00, + 0x30, 0x7f, 0x80, 0x00, 0x1f, 0xff, 0xc0, + 0x78, 0x73, 0xc0, 0x00, 0x7f, 0xff, 0xe0, + 0x7c, 0x71, 0xe0, 0x00, 0x7f, 0xff, 0xf0, + 0x3e, 0x73, 0xe0, 0x00, 0xff, 0x0f, 0xf0, + 0x1f, 0x77, 0xc0, 0x01, 0xfe, 0x07, 0xf8, + 0x0f, 0xff, 0x80, 0x01, 0xfc, 0x63, 0xf8, + 0x87, 0xff, 0x08, 0x01, 0xff, 0xf3, 0xfc, + 0xc3, 0xfe, 0x18, 0x03, 0xff, 0xe3, 0xfc, + 0xe1, 0xfc, 0x38, 0x03, 0xff, 0xe7, 0xfc, + 0xf0, 0xf8, 0x78, 0x03, 0xff, 0xc7, 0xfc, + 0xe1, 0xfc, 0x38, 0x03, 0xff, 0x8f, 0xfc, + 0xc3, 0xfe, 0x18, 0x03, 0xff, 0x1f, 0xfc, + 0x87, 0xff, 0x08, 0x01, 0xfe, 0x3f, 0xfc, + 0x0f, 0xff, 0x80, 0x01, 0xfc, 0x03, 0xf8, + 0x1f, 0x77, 0xc0, 0x01, 0xfc, 0x03, 0xf8, + 0x3e, 0x73, 0xe0, 0x00, 0xff, 0xff, 0xf0, + 0x7c, 0x71, 0xe0, 0x00, 0x7f, 0xff, 0xf0, + 0x78, 0x73, 0xc0, 0x00, 0x7f, 0xff, 0xe0, + 0x30, 0x7f, 0x80, 0x00, 0x1f, 0xff, 0xc0, + 0x00, 0x7f, 0x00, 0x00, 0x0f, 0xff, 0x00, + 0x00, 0x7e, 0x00, 0x00, 0x03, 0xfc, 0x00, + 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t bluetooth_connected_2 = { + .header.always_zero = 0, + .header.w = 54, + .header.h = 35, + .data_size = 254, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = bluetooth_connected_2_map, +}; diff --git a/app/boards/arm/nrfmacro/widgets/icons/bluetooth_connected_3.c b/app/boards/arm/nrfmacro/widgets/icons/bluetooth_connected_3.c new file mode 100644 index 00000000000..111b9d320a9 --- /dev/null +++ b/app/boards/arm/nrfmacro/widgets/icons/bluetooth_connected_3.c @@ -0,0 +1,64 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif +#ifndef LV_ATTRIBUTE_IMG_BLUETOOTH_CONNECTED_3 +#define LV_ATTRIBUTE_IMG_BLUETOOTH_CONNECTED_3 +#endif +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_IMG_BLUETOOTH_CONNECTED_3 uint8_t bluetooth_connected_3_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7e, 0x00, 0x00, 0x03, 0xfc, 0x00, + 0x00, 0x7f, 0x00, 0x00, 0x0f, 0xff, 0x00, + 0x30, 0x7f, 0x80, 0x00, 0x1f, 0xff, 0xc0, + 0x78, 0x77, 0xc0, 0x00, 0x7f, 0xff, 0xe0, + 0x7c, 0x73, 0xe0, 0x00, 0x7f, 0xff, 0xf0, + 0x3e, 0x71, 0xe0, 0x00, 0xff, 0x9f, 0xf0, + 0x1f, 0x73, 0xc0, 0x01, 0xfe, 0x07, 0xf8, + 0x0f, 0xff, 0x80, 0x01, 0xfc, 0x03, 0xf8, + 0x87, 0xff, 0x08, 0x01, 0xfe, 0xe3, 0xfc, + 0xc3, 0xfe, 0x18, 0x03, 0xff, 0xe3, 0xfc, + 0xe1, 0xfc, 0x38, 0x03, 0xff, 0x87, 0xfc, + 0xf0, 0xf8, 0x78, 0x03, 0xff, 0x87, 0xfc, + 0xf0, 0xfc, 0x78, 0x03, 0xff, 0x83, 0xfc, + 0xe1, 0xfe, 0x38, 0x03, 0xff, 0xf3, 0xfc, + 0xc3, 0xff, 0x18, 0x03, 0xfe, 0xf3, 0xfc, + 0x87, 0xff, 0x88, 0x01, 0xfc, 0x63, 0xf8, + 0x0f, 0xf7, 0xc0, 0x01, 0xfe, 0x03, 0xf8, + 0x1f, 0x73, 0xe0, 0x01, 0xff, 0x0f, 0xf8, + 0x3e, 0x71, 0xe0, 0x00, 0xff, 0xff, 0xf0, + 0x7c, 0x73, 0xe0, 0x00, 0x7f, 0xff, 0xe0, + 0x78, 0x77, 0xc0, 0x00, 0x3f, 0xff, 0xe0, + 0x30, 0x7f, 0x80, 0x00, 0x1f, 0xff, 0x80, + 0x00, 0x7f, 0x00, 0x00, 0x0f, 0xff, 0x00, + 0x00, 0x7e, 0x00, 0x00, 0x01, 0xf8, 0x00, + 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t bluetooth_connected_3 = { + .header.always_zero = 0, + .header.w = 54, + .header.h = 35, + .data_size = 254, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = bluetooth_connected_3_map, +}; diff --git a/app/boards/arm/nrfmacro/widgets/icons/bluetooth_connected_4.c b/app/boards/arm/nrfmacro/widgets/icons/bluetooth_connected_4.c new file mode 100644 index 00000000000..3b2db361515 --- /dev/null +++ b/app/boards/arm/nrfmacro/widgets/icons/bluetooth_connected_4.c @@ -0,0 +1,64 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif +#ifndef LV_ATTRIBUTE_IMG_BLUETOOTH_CONNECTED_4 +#define LV_ATTRIBUTE_IMG_BLUETOOTH_CONNECTED_4 +#endif +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_IMG_BLUETOOTH_CONNECTED_4 uint8_t bluetooth_connected_4_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7e, 0x00, 0x00, 0x01, 0xf8, 0x00, + 0x00, 0x7f, 0x00, 0x00, 0x07, 0xff, 0x00, + 0x30, 0x7f, 0x80, 0x00, 0x1f, 0xff, 0x80, + 0x78, 0x77, 0xc0, 0x00, 0x3f, 0xff, 0xe0, + 0x7c, 0x73, 0xe0, 0x00, 0x7f, 0xff, 0xe0, + 0x3e, 0x71, 0xe0, 0x00, 0xff, 0xff, 0xf0, + 0x1f, 0x73, 0xc0, 0x00, 0xff, 0xcf, 0xf8, + 0x0f, 0xff, 0x80, 0x01, 0xff, 0x8f, 0xf8, + 0x87, 0xff, 0x08, 0x01, 0xff, 0x0f, 0xf8, + 0xc3, 0xfe, 0x18, 0x01, 0xff, 0x0f, 0xfc, + 0xe1, 0xfc, 0x38, 0x03, 0xfe, 0x4f, 0xfc, + 0xf0, 0xf8, 0x78, 0x03, 0xfc, 0x4f, 0xfc, + 0xf0, 0xfc, 0x78, 0x03, 0xfc, 0xcf, 0xfc, + 0xe1, 0xfe, 0x38, 0x03, 0xf8, 0xc7, 0xfc, + 0xc3, 0xff, 0x18, 0x01, 0xf8, 0x03, 0xfc, + 0x87, 0xff, 0x88, 0x01, 0xf8, 0x03, 0xf8, + 0x0f, 0xf7, 0xc0, 0x01, 0xff, 0xcf, 0xf8, + 0x1f, 0x73, 0xe0, 0x00, 0xff, 0xcf, 0xf8, + 0x3e, 0x71, 0xe0, 0x00, 0xff, 0xff, 0xf0, + 0x7c, 0x73, 0xe0, 0x00, 0x7f, 0xff, 0xe0, + 0x78, 0x77, 0xc0, 0x00, 0x3f, 0xff, 0xe0, + 0x30, 0x7f, 0x80, 0x00, 0x1f, 0xff, 0x80, + 0x00, 0x7f, 0x00, 0x00, 0x0f, 0xff, 0x00, + 0x00, 0x7e, 0x00, 0x00, 0x01, 0xf8, 0x00, + 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t bluetooth_connected_4 = { + .header.always_zero = 0, + .header.w = 54, + .header.h = 35, + .data_size = 254, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = bluetooth_connected_4_map, +}; diff --git a/app/boards/arm/nrfmacro/widgets/icons/bluetooth_connected_5.c b/app/boards/arm/nrfmacro/widgets/icons/bluetooth_connected_5.c new file mode 100644 index 00000000000..bab117b89dc --- /dev/null +++ b/app/boards/arm/nrfmacro/widgets/icons/bluetooth_connected_5.c @@ -0,0 +1,64 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif +#ifndef LV_ATTRIBUTE_IMG_BLUETOOTH_CONNECTED_5 +#define LV_ATTRIBUTE_IMG_BLUETOOTH_CONNECTED_5 +#endif +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_IMG_BLUETOOTH_CONNECTED_5 uint8_t bluetooth_connected_5_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7e, 0x00, 0x00, 0x01, 0xfc, 0x00, + 0x00, 0x7f, 0x00, 0x00, 0x0f, 0xff, 0x00, + 0x30, 0x7f, 0x80, 0x00, 0x1f, 0xff, 0x80, + 0x78, 0x77, 0xc0, 0x00, 0x3f, 0xff, 0xe0, + 0x7c, 0x73, 0xe0, 0x00, 0x7f, 0xff, 0xe0, + 0x3e, 0x71, 0xe0, 0x00, 0xff, 0xff, 0xf0, + 0x1f, 0x73, 0xc0, 0x01, 0xfe, 0x07, 0xf8, + 0x0f, 0xff, 0x80, 0x01, 0xfe, 0x07, 0xf8, + 0x87, 0xff, 0x08, 0x01, 0xfc, 0x7f, 0xf8, + 0xc3, 0xfe, 0x18, 0x03, 0xfc, 0x7f, 0xfc, + 0xe1, 0xfc, 0x38, 0x03, 0xfc, 0x07, 0xfc, + 0xf0, 0xf8, 0x78, 0x03, 0xfc, 0x03, 0xfc, + 0xf0, 0xfc, 0x78, 0x03, 0xff, 0x63, 0xfc, + 0xe1, 0xfe, 0x38, 0x03, 0xff, 0xf3, 0xfc, + 0xc3, 0xff, 0x18, 0x03, 0xfe, 0xf3, 0xfc, + 0x87, 0xff, 0x88, 0x01, 0xfc, 0x63, 0xf8, + 0x0f, 0xf7, 0xc0, 0x01, 0xfe, 0x07, 0xf8, + 0x1e, 0x73, 0xc0, 0x00, 0xff, 0x0f, 0xf8, + 0x3c, 0x71, 0xe0, 0x00, 0xff, 0xff, 0xf0, + 0x7c, 0x73, 0xe0, 0x00, 0x7f, 0xff, 0xe0, + 0x78, 0x77, 0xc0, 0x00, 0x3f, 0xff, 0xc0, + 0x30, 0x7f, 0x80, 0x00, 0x1f, 0xff, 0x80, + 0x00, 0x7f, 0x00, 0x00, 0x0f, 0xff, 0x00, + 0x00, 0x7e, 0x00, 0x00, 0x01, 0xf8, 0x00, + 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t bluetooth_connected_5 = { + .header.always_zero = 0, + .header.w = 54, + .header.h = 35, + .data_size = 254, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = bluetooth_connected_5_map, +}; diff --git a/app/boards/arm/nrfmacro/widgets/icons/bluetooth_connected_right.c b/app/boards/arm/nrfmacro/widgets/icons/bluetooth_connected_right.c new file mode 100644 index 00000000000..1e637bdf29a --- /dev/null +++ b/app/boards/arm/nrfmacro/widgets/icons/bluetooth_connected_right.c @@ -0,0 +1,69 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_BLUETOOTH_CONNECTED_RIGHT +#define LV_ATTRIBUTE_IMG_BLUETOOTH_CONNECTED_RIGHT +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BLUETOOTH_CONNECTED_RIGHT uint8_t bluetooth_connected_right_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0x00, 0x00, 0x03, 0xf8, 0x00, + 0x00, 0x3f, 0x80, 0x00, 0x0f, 0xfe, 0x00, + 0x10, 0x3f, 0xc0, 0x00, 0x3f, 0xff, 0x80, + 0x38, 0x3b, 0xe0, 0x00, 0x7f, 0xff, 0xc0, + 0x7c, 0x39, 0xf0, 0x00, 0xff, 0xff, 0xe0, + 0x3e, 0x38, 0xf0, 0x01, 0xff, 0xff, 0xf0, + 0x1f, 0x39, 0xe0, 0x01, 0xff, 0xfe, 0x30, + 0x0f, 0xbb, 0xc0, 0x03, 0xff, 0xfc, 0x38, + 0x87, 0xff, 0x84, 0x03, 0xff, 0xf8, 0x38, + 0xc3, 0xff, 0x0c, 0x07, 0xff, 0xf0, 0x3c, + 0xe1, 0xfe, 0x1c, 0x07, 0xcf, 0xe0, 0x7c, + 0xf0, 0xfc, 0x3c, 0x07, 0x87, 0xc0, 0xfc, + 0xf0, 0xfc, 0x3c, 0x07, 0x83, 0x81, 0xfc, + 0xe1, 0xfe, 0x1c, 0x07, 0x81, 0x03, 0xfc, + 0xc3, 0xff, 0x0c, 0x07, 0xc0, 0x07, 0xfc, + 0x87, 0xff, 0x84, 0x07, 0xe0, 0x0f, 0xfc, + 0x0f, 0xbb, 0xc0, 0x03, 0xf0, 0x1f, 0xf8, + 0x1f, 0x39, 0xe0, 0x03, 0xf8, 0x3f, 0xf8, + 0x3e, 0x38, 0xf0, 0x01, 0xfc, 0xff, 0xf0, + 0x7c, 0x39, 0xf0, 0x01, 0xff, 0xff, 0xf0, + 0x38, 0x3b, 0xe0, 0x00, 0xff, 0xff, 0xe0, + 0x10, 0x3f, 0xc0, 0x00, 0x7f, 0xff, 0xc0, + 0x00, 0x3f, 0x80, 0x00, 0x3f, 0xff, 0x80, + 0x00, 0x3f, 0x00, 0x00, 0x0f, 0xfe, 0x00, + 0x00, 0x3e, 0x00, 0x00, 0x03, 0xf8, 0x00, + 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t bluetooth_connected_right = { + .header.always_zero = 0, + .header.w = 54, + .header.h = 35, + .data_size = 253, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = bluetooth_connected_right_map, +}; + diff --git a/app/boards/arm/nrfmacro/widgets/icons/bluetooth_disconnected_right.c b/app/boards/arm/nrfmacro/widgets/icons/bluetooth_disconnected_right.c new file mode 100644 index 00000000000..027856e2cd6 --- /dev/null +++ b/app/boards/arm/nrfmacro/widgets/icons/bluetooth_disconnected_right.c @@ -0,0 +1,68 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_BLUETOOTH_DISCONNECTED_RIGHT +#define LV_ATTRIBUTE_IMG_BLUETOOTH_DISCONNECTED_RIGHT +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BLUETOOTH_DISCONNECTED_RIGHT uint8_t bluetooth_disconnected_right_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0x00, 0x00, 0x03, 0xf8, 0x00, + 0x00, 0x3f, 0x80, 0x00, 0x0f, 0xfe, 0x00, + 0x10, 0x3f, 0xc0, 0x00, 0x3f, 0xff, 0x80, + 0x38, 0x3b, 0xe0, 0x00, 0x7f, 0xff, 0xc0, + 0x7c, 0x39, 0xf0, 0x00, 0xff, 0xff, 0xe0, + 0x3e, 0x38, 0xf0, 0x01, 0xff, 0xff, 0xf0, + 0x1f, 0x39, 0xe0, 0x01, 0xe3, 0xf8, 0xf0, + 0x0f, 0xbb, 0xc0, 0x03, 0xe1, 0xf0, 0xf8, + 0x07, 0xff, 0x80, 0x03, 0xe0, 0xe0, 0xf8, + 0x03, 0xff, 0x00, 0x07, 0xf0, 0x01, 0xfc, + 0x01, 0xfe, 0x00, 0x07, 0xf8, 0x03, 0xfc, + 0x00, 0xfc, 0x00, 0x07, 0xfc, 0x07, 0xfc, + 0x00, 0xfc, 0x00, 0x07, 0xfc, 0x07, 0xfc, + 0x01, 0xfe, 0x00, 0x07, 0xfc, 0x07, 0xfc, + 0x03, 0xff, 0x00, 0x07, 0xf8, 0x03, 0xfc, + 0x07, 0xff, 0x80, 0x07, 0xf0, 0x01, 0xfc, + 0x0f, 0xbb, 0xc0, 0x03, 0xe0, 0xe0, 0xf8, + 0x1f, 0x39, 0xe0, 0x03, 0xe1, 0xf0, 0xf8, + 0x3e, 0x38, 0xf0, 0x01, 0xe3, 0xf8, 0xf0, + 0x7c, 0x39, 0xf0, 0x01, 0xff, 0xff, 0xf0, + 0x38, 0x3b, 0xe0, 0x00, 0xff, 0xff, 0xe0, + 0x10, 0x3f, 0xc0, 0x00, 0x7f, 0xff, 0xc0, + 0x00, 0x3f, 0x80, 0x00, 0x3f, 0xff, 0x80, + 0x00, 0x3f, 0x00, 0x00, 0x0f, 0xfe, 0x00, + 0x00, 0x3e, 0x00, 0x00, 0x03, 0xf8, 0x00, + 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t bluetooth_disconnected_right = { + .header.always_zero = 0, + .header.w = 54, + .header.h = 35, + .data_size = 253, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = bluetooth_disconnected_right_map, +}; + diff --git a/app/boards/arm/nrfmacro/widgets/icons/layers.c b/app/boards/arm/nrfmacro/widgets/icons/layers.c new file mode 100644 index 00000000000..945b5cc9946 --- /dev/null +++ b/app/boards/arm/nrfmacro/widgets/icons/layers.c @@ -0,0 +1,68 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_LAYERS +#define LV_ATTRIBUTE_IMG_LAYERS +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_LAYERS uint8_t layers_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xe0, 0x00, 0x00, + 0x00, 0x07, 0xfc, 0x00, 0x00, + 0x00, 0x1f, 0xff, 0x00, 0x00, + 0x00, 0x7f, 0xff, 0xc0, 0x00, + 0x01, 0xff, 0xff, 0xf0, 0x00, + 0x07, 0xff, 0xff, 0xfc, 0x00, + 0x1f, 0xff, 0xff, 0xff, 0x00, + 0x3f, 0xff, 0xff, 0xff, 0x80, + 0x1f, 0xff, 0xff, 0xff, 0x00, + 0x0f, 0xff, 0xff, 0xfe, 0x00, + 0x01, 0xff, 0xff, 0xf0, 0x00, + 0x00, 0xff, 0xff, 0xe0, 0x00, + 0x07, 0x9f, 0xff, 0x3c, 0x00, + 0x1e, 0x07, 0xfc, 0x0f, 0x00, + 0x38, 0x01, 0xe0, 0x03, 0x80, + 0x30, 0x00, 0x00, 0x01, 0x80, + 0x38, 0x00, 0x00, 0x03, 0x80, + 0x0e, 0x00, 0x00, 0x0e, 0x00, + 0x03, 0xc0, 0x00, 0x78, 0x00, + 0x01, 0xf0, 0x01, 0xf0, 0x00, + 0x07, 0xfc, 0x07, 0xfc, 0x00, + 0x1f, 0xff, 0x1f, 0xff, 0x00, + 0x3f, 0xff, 0xff, 0xff, 0x80, + 0x3f, 0xff, 0xff, 0xff, 0x80, + 0x1f, 0xff, 0xff, 0xff, 0x00, + 0x07, 0xff, 0xff, 0xfc, 0x00, + 0x00, 0xff, 0xff, 0xf0, 0x00, + 0x00, 0x3f, 0xff, 0x80, 0x00, + 0x00, 0x0f, 0xfe, 0x00, 0x00, + 0x00, 0x03, 0xf8, 0x00, 0x00, + 0x00, 0x00, 0xe0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t layers = { + .header.always_zero = 0, + .header.w = 35, + .header.h = 35, + .data_size = 183, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = layers_map, +}; + diff --git a/app/boards/arm/nrfmacro/widgets/icons/layers2.c b/app/boards/arm/nrfmacro/widgets/icons/layers2.c new file mode 100644 index 00000000000..7a880557715 --- /dev/null +++ b/app/boards/arm/nrfmacro/widgets/icons/layers2.c @@ -0,0 +1,45 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_LAYERS +#define LV_ATTRIBUTE_IMG_LAYERS +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_LAYERS2 uint8_t layers_map[] = { + 0x00, 0x00, 0x00, 0xff, /*Color of index 0*/ + 0xff, 0xff, 0xff, 0xff, /*Color of index 1*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x30, 0x0f, 0x18, 0xdf, 0xdf, 0xc0, 0x04, 0x00, + 0x00, 0x80, 0x30, 0x19, 0x98, 0xd8, 0x18, 0xe0, 0x0e, 0x00, + 0x00, 0x80, 0x30, 0x30, 0xd8, 0xd8, 0x18, 0xe0, 0x1f, 0x00, + 0x00, 0x80, 0x30, 0x30, 0xcf, 0x9f, 0xd9, 0xc0, 0x04, 0x00, + 0x00, 0x80, 0x30, 0x3f, 0xc7, 0x18, 0x1f, 0x80, 0x04, 0x00, + 0x03, 0xe0, 0x30, 0x39, 0xc6, 0x18, 0x1b, 0x80, 0x04, 0x00, + 0x01, 0xc0, 0x3f, 0xb0, 0xc6, 0x1f, 0xd9, 0xc0, 0x04, 0x00, + 0x00, 0x80, 0x3f, 0xb0, 0xc6, 0x1f, 0xd8, 0xe0, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t layers = { + .header.always_zero = 0, + .header.w = 78, + .header.h = 12, + .data_size = 128, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = layers_map, +}; + diff --git a/app/boards/arm/nrfmacro/widgets/icons/zenlogo.c b/app/boards/arm/nrfmacro/widgets/icons/zenlogo.c new file mode 100644 index 00000000000..93eefd482d4 --- /dev/null +++ b/app/boards/arm/nrfmacro/widgets/icons/zenlogo.c @@ -0,0 +1,71 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_ZENLOGO +#define LV_ATTRIBUTE_IMG_ZENLOGO +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_ZENLOGO uint8_t zenlogo_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x03, 0xe3, 0xe3, 0x9e, 0x3e, 0x02, 0x72, 0xe0, 0x00, + 0x00, 0x06, 0x36, 0x36, 0x33, 0x63, 0x02, 0x8b, 0x10, 0x00, + 0x00, 0x04, 0x14, 0x14, 0x21, 0x41, 0x02, 0x82, 0x10, 0x00, + 0x00, 0x04, 0x04, 0x14, 0x21, 0x7f, 0x7a, 0x72, 0x10, 0x00, + 0x00, 0x04, 0x04, 0x14, 0x21, 0x40, 0x02, 0x0a, 0x10, 0x00, + 0x00, 0x04, 0x14, 0x14, 0x21, 0x41, 0x02, 0x0a, 0x10, 0x00, + 0x00, 0x06, 0x36, 0x34, 0x21, 0x63, 0x02, 0x8a, 0x10, 0x00, + 0x00, 0x03, 0xe3, 0xe4, 0x21, 0x3e, 0x02, 0x72, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x03, 0xfc, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x07, 0xe0, + 0x00, 0x00, 0x00, 0xff, 0xf1, 0xf8, 0x00, 0x60, 0x0f, 0xc0, + 0x00, 0x00, 0x0f, 0xff, 0xfb, 0xff, 0xe0, 0xf8, 0x1f, 0x80, + 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xc1, 0xfc, 0x1f, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc1, 0xfc, 0x1f, 0x00, + 0x7f, 0xc0, 0x1f, 0xff, 0xff, 0xe0, 0x01, 0xfc, 0x3e, 0x00, + 0x40, 0x1f, 0xff, 0xe0, 0xff, 0xc0, 0x07, 0xfc, 0xfc, 0x00, + 0xdf, 0xff, 0x80, 0x01, 0xff, 0xc0, 0x0f, 0xdc, 0xf8, 0x00, + 0x00, 0x00, 0x00, 0x0f, 0xff, 0x8f, 0x1f, 0x9f, 0xf8, 0x00, + 0x00, 0x00, 0x00, 0x1f, 0xef, 0xff, 0x1f, 0x9f, 0xf0, 0x00, + 0x00, 0x00, 0x00, 0x7f, 0x8f, 0xff, 0x3f, 0x1f, 0xf0, 0x00, + 0x00, 0x00, 0x00, 0xfe, 0x1f, 0xfe, 0x3f, 0x0f, 0xf0, 0x00, + 0x00, 0x00, 0x03, 0xf8, 0x1f, 0x80, 0x7e, 0x0f, 0xe0, 0x00, + 0x00, 0x00, 0x07, 0xe0, 0x3e, 0x00, 0x7e, 0x0f, 0xe0, 0x00, + 0x00, 0x00, 0x0f, 0xc0, 0x3c, 0x00, 0x7e, 0x0f, 0xc0, 0x00, + 0x00, 0x00, 0x1f, 0xf8, 0x3c, 0x00, 0x78, 0x0f, 0xc0, 0x00, + 0x00, 0x00, 0x1f, 0xff, 0xbc, 0x00, 0x78, 0x0f, 0xc0, 0x00, + 0x00, 0x00, 0x1f, 0xff, 0xfc, 0x00, 0xf8, 0x0f, 0xc0, 0x00, + 0x00, 0x00, 0x1f, 0xff, 0xfc, 0x0f, 0xf0, 0x0f, 0x00, 0x00, + 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xf8, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x0f, 0xff, 0x0f, 0xff, 0xa0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0xf8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t zenlogo = { + .header.always_zero = 0, + .header.w = 80, + .header.h = 38, + .data_size = 388, + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .data = zenlogo_map, +}; + diff --git a/app/boards/arm/nrfmacro/widgets/layer_status.c b/app/boards/arm/nrfmacro/widgets/layer_status.c new file mode 100644 index 00000000000..92022629a12 --- /dev/null +++ b/app/boards/arm/nrfmacro/widgets/layer_status.c @@ -0,0 +1,102 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include +#include +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); +#include +#include "layer_status.h" +#include +#include +#include +#include + +static sys_slist_t widgets = SYS_SLIST_STATIC_INIT(&widgets); +static lv_style_t label_style; + +static bool style_initialized = false; + +K_MUTEX_DEFINE(layer_status_mutex); + +struct { + uint8_t index; + const char *label; +} layer_status_state; + +void layer_status_init() { + if (style_initialized) { + return; + } + style_initialized = true; + lv_style_init(&label_style); + lv_style_set_text_color(&label_style, LV_STATE_DEFAULT, LV_COLOR_BLACK); + lv_style_set_text_font(&label_style, LV_STATE_DEFAULT, &lv_font_montserrat_16); + lv_style_set_text_letter_space(&label_style, LV_STATE_DEFAULT, 1); + lv_style_set_text_line_space(&label_style, LV_STATE_DEFAULT, 1); + +} + +void set_layer_symbol(lv_obj_t *label) { + + k_mutex_lock(&layer_status_mutex, K_FOREVER); + const char *layer_label = layer_status_state.label; + uint8_t active_layer_index = layer_status_state.index; + k_mutex_unlock(&layer_status_mutex); + + //LOG_DBG("Layer Label: %s", layer_label); + + if (layer_label == NULL) { + char text[6] = {}; + + sprintf(text, " %i", active_layer_index); + + lv_label_set_text(label, text); + } else { + lv_label_set_text(label, layer_label); + } +} + +static void update_state() { + k_mutex_lock(&layer_status_mutex, K_FOREVER); + layer_status_state.index = zmk_keymap_highest_layer_active(); + layer_status_state.label = zmk_keymap_layer_label(layer_status_state.index); + LOG_DBG("Layer changed to %i", layer_status_state.index); + + k_mutex_unlock(&layer_status_mutex); +} + +int zmk_widget_layer_status_init(struct zmk_widget_layer_status *widget, lv_obj_t *parent) { + layer_status_init(); + update_state(); + widget->obj = lv_label_create(parent, NULL); + lv_obj_add_style(widget->obj, LV_LABEL_PART_MAIN, &label_style); + set_layer_symbol(widget->obj); + sys_slist_append(&widgets, &widget->node); + + return 0; +} + +lv_obj_t *zmk_widget_layer_status_obj(struct zmk_widget_layer_status *widget) { + return widget->obj; +} + +void layer_status_update_cb(struct k_work *work) { + struct zmk_widget_layer_status *widget; + SYS_SLIST_FOR_EACH_CONTAINER(&widgets, widget, node) { set_layer_symbol(widget->obj); } +} + +K_WORK_DEFINE(layer_status_update_work, layer_status_update_cb); + +int layer_status_listener(const zmk_event_t *eh) { + update_state();; + + k_work_submit_to_queue(zmk_display_work_q(), &layer_status_update_work); + return 0; +} + +ZMK_LISTENER(widget_layer_status, layer_status_listener) +ZMK_SUBSCRIPTION(widget_layer_status, zmk_layer_state_changed); \ No newline at end of file diff --git a/app/boards/arm/nrfmacro/widgets/layer_status.h b/app/boards/arm/nrfmacro/widgets/layer_status.h new file mode 100644 index 00000000000..2616ad225d0 --- /dev/null +++ b/app/boards/arm/nrfmacro/widgets/layer_status.h @@ -0,0 +1,20 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#pragma once + +#include +#include + +struct zmk_widget_layer_status { + sys_snode_t node; + lv_obj_t *obj; + //lv_obj_t *obj2; +}; + +int zmk_widget_layer_status_init(struct zmk_widget_layer_status *widget, lv_obj_t *parent); +lv_obj_t *zmk_widget_layer_status_obj(struct zmk_widget_layer_status *widget); \ No newline at end of file diff --git a/app/boards/arm/nrfmacro/widgets/output_status.c b/app/boards/arm/nrfmacro/widgets/output_status.c new file mode 100644 index 00000000000..cbd83e3110d --- /dev/null +++ b/app/boards/arm/nrfmacro/widgets/output_status.c @@ -0,0 +1,190 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#include +#include + +#include +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#include +#include "output_status.h" +#include +#include +#include +#include +#include +#include +#include + +LV_IMG_DECLARE(bluetooth_advertising); +LV_IMG_DECLARE(bluetooth_connected_right); +LV_IMG_DECLARE(bluetooth_disconnected_right); +LV_IMG_DECLARE(bluetooth_connected_1); +LV_IMG_DECLARE(bluetooth_connected_2); +LV_IMG_DECLARE(bluetooth_connected_3); +LV_IMG_DECLARE(bluetooth_connected_4); +LV_IMG_DECLARE(bluetooth_connected_5); +LV_IMG_DECLARE(bluetooth_advertising_1); +LV_IMG_DECLARE(bluetooth_advertising_2); +LV_IMG_DECLARE(bluetooth_advertising_3); +LV_IMG_DECLARE(bluetooth_advertising_4); +LV_IMG_DECLARE(bluetooth_advertising_5); +LV_IMG_DECLARE(USB_connected); + +static sys_slist_t widgets = SYS_SLIST_STATIC_INIT(&widgets); +static lv_style_t label_style; + +static bool style_initialized = false; + +K_MUTEX_DEFINE(output_status_mutex); + +struct { + enum zmk_endpoint selected_endpoint; + bool active_profile_connected; + bool active_profile_bonded; + uint8_t active_profile_index; +} output_status_state; + +void output_status_init() { + if (style_initialized) { + return; + } + + style_initialized = true; + lv_style_init(&label_style); + lv_style_set_text_color(&label_style, LV_STATE_DEFAULT, LV_COLOR_BLACK); + lv_style_set_text_font(&label_style, LV_STATE_DEFAULT, &lv_font_montserrat_26); + lv_style_set_text_letter_space(&label_style, LV_STATE_DEFAULT, 1); + lv_style_set_text_line_space(&label_style, LV_STATE_DEFAULT, 1); +} + +void set_status_symbol(lv_obj_t *icon) { + + k_mutex_lock(&output_status_mutex, K_FOREVER); + enum zmk_endpoint selected_endpoint = output_status_state.selected_endpoint; + bool active_profile_connected = output_status_state.active_profile_connected; + bool active_profie_bonded = output_status_state.active_profile_bonded; + uint8_t active_profile_index = output_status_state.active_profile_index; + k_mutex_unlock(&output_status_mutex); + + switch (selected_endpoint) { + case ZMK_ENDPOINT_USB: + lv_img_set_src(icon, &USB_connected); + break; + case ZMK_ENDPOINT_BLE: + if (active_profie_bonded) { + if (active_profile_connected) { + //sprintf(text, LV_SYMBOL_BLUETOOTH "%i " LV_SYMBOL_OK, active_profile_index); + switch (active_profile_index) { + case 0: +#if IS_ENABLED(CONFIG_NRFMACRO_SHIELD_SLAVE) + lv_img_set_src(icon, &bluetooth_connected_right); +#else + lv_img_set_src(icon, &bluetooth_connected_1); +#endif + break; + case 1: + lv_img_set_src(icon, &bluetooth_connected_2); + break; + case 2: + lv_img_set_src(icon, &bluetooth_connected_3); + break; + case 3: + lv_img_set_src(icon, &bluetooth_connected_4); + break; + case 4: + lv_img_set_src(icon, &bluetooth_connected_5); + break; + } + } else { + //sprintf(text, LV_SYMBOL_BLUETOOTH "%i " LV_SYMBOL_CLOSE, active_profile_index); + lv_img_set_src(icon, &bluetooth_disconnected_right); + } + } else { + //sprintf(text, LV_SYMBOL_BLUETOOTH "%i " LV_SYMBOL_SETTINGS, active_profile_index); + //lv_img_set_src(icon, &bluetooth_advertising); + switch (active_profile_index) { + case 0: + lv_img_set_src(icon, &bluetooth_advertising_1); + break; + case 1: + lv_img_set_src(icon, &bluetooth_advertising_2); + break; + case 2: + lv_img_set_src(icon, &bluetooth_advertising_3); + break; + case 3: + lv_img_set_src(icon, &bluetooth_advertising_4); + break; + case 4: + lv_img_set_src(icon, &bluetooth_advertising_5); + break; + } + } + break; + } + + //lv_label_set_text(label, text); +} + +static void update_state() { + k_mutex_lock(&output_status_mutex, K_FOREVER); + output_status_state.selected_endpoint = zmk_endpoints_selected(); + output_status_state.active_profile_connected = zmk_ble_active_profile_is_connected(); + output_status_state.active_profile_bonded = !zmk_ble_active_profile_is_open(); + output_status_state.active_profile_index = zmk_ble_active_profile_index(); + k_mutex_unlock(&output_status_mutex); +} + +int zmk_widget_output_status_init(struct zmk_widget_output_status *widget, lv_obj_t *parent) { + output_status_init(); + update_state(); + //widget->obj = lv_label_create(parent, NULL); + widget->obj = lv_img_create(parent, NULL); + + set_status_symbol(widget->obj); + + sys_slist_append(&widgets, &widget->node); + + return 0; +} + +lv_obj_t *zmk_widget_output_status_obj(struct zmk_widget_output_status *widget) { + return widget->obj; +} + +void output_status_update_cb(struct k_work *work) { + struct zmk_widget_output_status *widget; + SYS_SLIST_FOR_EACH_CONTAINER(&widgets, widget, node) { set_status_symbol(widget->obj); } +} + +K_WORK_DEFINE(output_status_update_work, output_status_update_cb); + +int output_status_listener(const zmk_event_t *eh) { + + + // Be sure we have widgets initialized before doing any work, + // since the status event can fire before display code inits. + if (!style_initialized) { + return ZMK_EV_EVENT_BUBBLE; + } + + update_state(); + + k_work_submit_to_queue(zmk_display_work_q(), &output_status_update_work); + return ZMK_EV_EVENT_BUBBLE; +} + +ZMK_LISTENER(widget_output_status, output_status_listener) +ZMK_SUBSCRIPTION(widget_output_status, zmk_endpoint_selection_changed); +#if defined(CONFIG_USB) +ZMK_SUBSCRIPTION(widget_output_status, zmk_usb_conn_state_changed); +#endif +#if defined(CONFIG_ZMK_BLE) +ZMK_SUBSCRIPTION(widget_output_status, zmk_ble_active_profile_changed); +#endif diff --git a/app/boards/arm/nrfmacro/widgets/output_status.h b/app/boards/arm/nrfmacro/widgets/output_status.h new file mode 100644 index 00000000000..d10018b15e3 --- /dev/null +++ b/app/boards/arm/nrfmacro/widgets/output_status.h @@ -0,0 +1,19 @@ +/* +* +* Copyright (c) 2021 Darryl deHaan +* SPDX-License-Identifier: MIT +* +*/ + +#pragma once + +#include +#include + +struct zmk_widget_output_status { + sys_snode_t node; + lv_obj_t *obj; +}; + +int zmk_widget_output_status_init(struct zmk_widget_output_status *widget, lv_obj_t *parent); +lv_obj_t *zmk_widget_output_status_obj(struct zmk_widget_output_status *widget); \ No newline at end of file diff --git a/app/boards/shields/34key-common-colemak.dtsi b/app/boards/shields/34key-common-colemak.dtsi new file mode 100644 index 00000000000..3368b7a7d7d --- /dev/null +++ b/app/boards/shields/34key-common-colemak.dtsi @@ -0,0 +1,60 @@ + +#define DEFAULT 0 +#define QWERT 1 +#define NUM 2 +#define SYM 3 + +&mt { + flavor = "tap-preferred"; + tapping_term_ms = <200>; +}; + +/ { + +#include "key-common.dtsi" + + keymap { + compatible = "zmk,keymap"; + + default_layer { + bindings = < + &kp Q &kp W &kp F &kp P &kp B &kp J &kp L &kp U &kp Y &kp SQT + &kp A &mt LSFT R &mt LALT S &mt LCTL T &kp G &kp M &mt RCTL N &mt RALT E &mt RSFT I &kp O + &kp Z &kp X &kp C &kp D &kp V &kp K &kp H &cmqus &dtsmi &kp RC(B) + < SYM TAB < NUM SPACE < NUM RET < SYM BSPC + >; + sensor-bindings = <&inc_dec_kp RIGHT LEFT &inc_dec_kp UP DOWN>; + }; + + qwert_layer { + bindings = < + &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P + &kp A &mt LSFT S < SYM D < NUM F &kp G &kp H < NUM J < SYM K &mt RSFT L &kp SQT + &kp Z &kp X &kp C &kp V &kp B &kp N &kp M &cmqus &dtsmi &kp SEMI + &mt LALT TAB &mt LCTL SPACE &mt RCTL RET &mt RALT BSPC + >; + sensor-bindings = <&inc_dec_kp PG_UP PG_DN &inc_dec_kp C_VOL_UP C_VOL_DN>; + }; + + number_layer { + bindings = < + &bt BT_NXT &none &kp UP &none &spc1 &kp KP_PLUS &kp KP_N7 &kp KP_N8 &kp KP_N9 &kp KP_MINUS + &bt BT_PRV &kp LEFT &kp DOWN &kp RIGHT &spc2 &kp KP_MULTIPLY &kp KP_N4 &kp KP_N5 &kp KP_N6 &kp KP_DIVIDE + &bt BT_CLR &none &none &none &spc3 &kp KP_EQUAL &kp KP_N1 &kp KP_N2 &kp KP_N3 &kp KP_DOT + &none &kp BSPC &kp RET &kp KP_N0 + >; + sensor-bindings = <&inc_dec_kp PG_UP PG_DN &inc_dec_kp C_VOL_UP C_VOL_DN>; + }; + + symbol_layer { + bindings = < + &none &TILDE &kp LT &kp GT &kp PRCNT &kp AMPS &kp CARET &kp LBRC &kp RBRC &tog QWERT + &kp GRAVE &kp EXCL &kp MINUS &kp PLUS &kp EQUAL &kp PIPE &kp COLON &kp LPAR &kp RPAR &kp AT + &none &none &kp SLASH &kp STAR &kp BSLH &kp HASH &kp DLLR &kp LBKT &kp RBKT &none + &none &none &none &none + >; + sensor-bindings = <&inc_dec_kp PG_UP PG_DN &inc_dec_kp C_VOL_UP C_VOL_DN>; + }; + + }; +}; \ No newline at end of file diff --git a/app/boards/shields/34key-common.dtsi b/app/boards/shields/34key-common.dtsi new file mode 100644 index 00000000000..d3980aa5dfc --- /dev/null +++ b/app/boards/shields/34key-common.dtsi @@ -0,0 +1,289 @@ + +#define DEFAULT 0 +#define QWERT 1 +#define NUM 2 +#define SYM 3 + +&mt { + flavor = "tap-preferred"; + tapping_term_ms = <200>; +}; + +/ { + // macros + macros { + ZMK_MACRO(hello, // hello world + wait-ms = <0>; + tap-ms = <10>; + bindings = <&kp LS(H) &kp E &kp L &kp L &kp O> + , <&kp COMMA> + , <&kp W &kp O &kp R &kp L &kp D &kp EXCL>; + ) + + ZMK_MACRO(spc1, // for spacemacs window nav, Alt+M+1 + wait-ms = <0>; + tap-ms = <10>; + bindings = <&kp LA(M) &kp N1>; + ) + + ZMK_MACRO(spc2, + wait-ms = <0>; + tap-ms = <10>; + bindings = <&kp LA(M) &kp N2>; + ) + + ZMK_MACRO(spc3, + wait-ms = <0>; + tap-ms = <10>; + bindings = <&kp LA(M) &kp N3>; + ) + }; + + // custom shift using mod-morph + behaviors { + cmqus: comma_question { + compatible = "zmk,behavior-mod-morph"; + label = "COMMA_QUESTION"; + #binding-cells = <0>; + bindings = <&kp COMMA>, <&kp QUESTION>; + mods = <(MOD_LSFT|MOD_RSFT)>; + }; + + dtild: dot_tilde { + compatible = "zmk,behavior-mod-morph"; + label = "DOT_TILDE"; + #binding-cells = <0>; + bindings = <&kp DOT>, <&kp TILDE>; + mods = <(MOD_LSFT|MOD_RSFT)>; + }; + }; + + // combos + combos { + compatible = "zmk,combos"; + // right hand + combo_esc { + timeout-ms = <50>; + key-positions = <16 17>; + bindings = <&kp ESC>; + }; + + combo_lgui { + timeout-ms = <50>; + key-positions = <5 6>; + bindings = <&kp LGUI>; + }; + + combo_fullscreen { + timeout-ms = <50>; + key-positions = <25 26>; + bindings = <&kp F11>; + }; + + combo_wkspc_up { + timeout-ms = <50>; + key-positions = <9 19>; + bindings = <&kp LC(UP)>; + }; + + combo_wkspc_down { + timeout-ms = <50>; + key-positions = <19 29>; + bindings = <&kp LC(DOWN)>; + }; + + combo_win_up { + timeout-ms = <50>; + key-positions = <7 8>; + bindings = <&kp LS(LG(UP))>; + }; + + combo_win_down { + timeout-ms = <50>; + key-positions = <27 28>; + bindings = <&kp LS(LG(DOWN))>; + }; + + combo_win_left { + timeout-ms = <50>; + key-positions = <17 18>; + bindings = <&kp LS(LG(LEFT))>; + }; + + combo_win_right { + timeout-ms = <50>; + key-positions = <9 18>; + bindings = <&kp LS(LG(RIGHT))>; + }; + + combo_rdelete { + timeout-ms = <50>; + key-positions = <15 16>; + bindings = <&kp DELETE>; + }; + + // left hand + combo_zoomin { + timeout-ms = <50>; + key-positions = <3 4>; + bindings = <&kp LC(PLUS)>; + }; + + combo_zoomout { + timeout-ms = <50>; + key-positions = <23 24>; + bindings = <&kp LC(MINUS)>; + }; + + combo_pgdn { + timeout-ms = <50>; + key-positions = <22 23>; + bindings = <&kp PG_DN>; + }; + + combo_pgup { + timeout-ms = <50>; + key-positions = <2 3>; + bindings = <&kp PG_UP>; + }; + + combo_ldelete { + timeout-ms = <50>; + key-positions = <13 14>; + bindings = <&kp DELETE>; + }; + + // symbols + combo_underscore { + timeout-ms = <50>; + key-positions = <11 12>; + bindings = <&kp UNDERSCORE>; + }; + + combo_minus { + timeout-ms = <50>; + key-positions = <12 13>; + bindings = <&kp MINUS>; + }; + + // others + combo_capswd { + timeout-ms = <50>; + key-positions = <2 7>; + bindings = <&caps_word>; + }; + + combo_lsym { + timeout-ms = <50>; + key-positions = <30 33>; + bindings = <&tog SYM>; + }; + + combo_lnum { + timeout-ms = <50>; + key-positions = <31 32>; + bindings = <&tog NUM>; + }; + + combo_hello { + timeout-ms = <50>; + key-positions = <30 31>; + bindings = <&hello>; + }; + + // output selection + // combo_outusb { + // timeout-ms = <50>; + // key-positions = <4 14>; + // bindings = <&out OUT_USB>; + // }; + + combo_outble { + timeout-ms = <50>; + key-positions = <4 14>; + bindings = <&out OUT_BLE>; + }; + + combo_outtog { + timeout-ms = <50>; + key-positions = <4 24>; + bindings = <&out OUT_TOG>; + }; + + // ble selection + combo_ble1 { + timeout-ms = <50>; + key-positions = <14 24>; + bindings = <&bt BT_SEL 0>; + }; + + combo_ble2 { + timeout-ms = <50>; + key-positions = <13 23>; + bindings = <&bt BT_SEL 1>; + }; + + combo_ble3 { + timeout-ms = <50>; + key-positions = <12 22>; + bindings = <&bt BT_SEL 2>; + }; + + combo_ble4 { + timeout-ms = <50>; + key-positions = <11 21>; + bindings = <&bt BT_SEL 3>; + }; + + combo_ble5 { + timeout-ms = <50>; + key-positions = <10 20>; + bindings = <&bt BT_SEL 4>; + }; + }; + + keymap { + compatible = "zmk,keymap"; + + default_layer { + bindings = < + &kp Q &kp W &kp F &kp P &kp B &kp J &kp L &kp U &kp Y &kp SQT + &kp A &mt LSFT R < SYM S < NUM T &kp G &kp M < NUM N < SYM E &mt RSFT I &kp O + &kp Z &kp X &kp C &kp D &kp V &kp K &kp H &cmqus &dtild &kp SEMI + &mt LALT TAB &mt LCTL SPACE &mt RCTL RET &mt RALT BSPC + >; + sensor-bindings = <&inc_dec_kp PG_UP PG_DN &inc_dec_kp C_VOL_UP C_VOL_DN>; + }; + + qwert_layer { + bindings = < + &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P + &kp A &mt LSFT S < SYM D < NUM F &kp G &kp H < NUM J < SYM K &mt RSFT L &kp SQT + &kp Z &kp X &kp C &kp V &kp B &kp N &kp M &cmqus &dtild &kp SEMI + &mt LALT TAB &mt LCTL SPACE &mt RCTL RET &mt RALT BSPC + >; + sensor-bindings = <&inc_dec_kp PG_UP PG_DN &inc_dec_kp C_VOL_UP C_VOL_DN>; + }; + + number_layer { + bindings = < + &bt BT_NXT &none &kp UP &none &spc1 &kp KP_PLUS &kp KP_N7 &kp KP_N8 &kp KP_N9 &kp KP_MINUS + &bt BT_PRV &kp LEFT &kp DOWN &kp RIGHT &spc2 &kp KP_MULTIPLY &kp KP_N4 &kp KP_N5 &kp KP_N6 &kp KP_DIVIDE + &bt BT_CLR &none &none &none &spc3 &kp KP_EQUAL &kp KP_N1 &kp KP_N2 &kp KP_N3 &kp KP_DOT + &none &kp BSPC &kp RET &kp KP_N0 + >; + sensor-bindings = <&inc_dec_kp PG_UP PG_DN &inc_dec_kp C_VOL_UP C_VOL_DN>; + }; + + symbol_layer { + bindings = < + &none &none &kp LT &kp GT &kp PRCNT &kp AMPS &kp CARET &kp LBRC &kp RBRC &tog QWERT + &kp GRAVE &kp EXCL &kp MINUS &kp PLUS &kp EQUAL &kp PIPE &kp COLON &kp LPAR &kp RPAR &kp AT + &none &none &kp SLASH &kp STAR &kp BSLH &kp HASH &kp DLLR &kp LBKT &kp RBKT &none + &none &none &none &none + >; + sensor-bindings = <&inc_dec_kp PG_UP PG_DN &inc_dec_kp C_VOL_UP C_VOL_DN>; + }; + + }; +}; \ No newline at end of file diff --git a/app/boards/shields/36key-common-colemak.dtsi b/app/boards/shields/36key-common-colemak.dtsi new file mode 100644 index 00000000000..05f9ec74d7a --- /dev/null +++ b/app/boards/shields/36key-common-colemak.dtsi @@ -0,0 +1,58 @@ + +&mt { + flavor = "tap-preferred"; + tapping_term_ms = <200>; +}; + +/ { +#include "key-common.dtsi" + + keymap { + compatible = "zmk,keymap"; + + default_layer { + label = "CLMK"; + bindings = < + &kp Q &kp W &kp F &kp P &kp B &kp J &kp L &kp U &kp Y &kp SQT + &kp A &mt LSFT R &mt LALT S &mt LCTL T < NUM G < NUM M &mt RCTL N &mt RALT E &mt RSFT I &kp O + &kp Z &kp X &kp C &kp D &kp V &kp K &kp H &cmqus &dtsmi &kp RC(B) + &encoder &kp TAB < SYM SPACE < SYM RET &kp BSPC &encoder + >; + sensor-bindings = <&inc_dec_kp RIGHT LEFT &inc_dec_kp UP DOWN>; + }; + + qwert_layer { + label = "QWRT"; + bindings = < + &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P + &kp A &mt LSFT S < SYM D < NUM F &kp G &kp H < NUM J < SYM K &mt RSFT L &kp SQT + &kp Z &kp X &kp C &kp V &kp B &kp N &kp M &cmqus &dtsmi &kp RC(B) + &encoder &mt LALT TAB &mt LCTL SPACE &mt RCTL RET &mt RALT C &encoder + >; + sensor-bindings = <&inc_dec_kp RIGHT LEFT &inc_dec_kp UP DOWN>; + }; + + number_layer { + label = "NUM"; + bindings = < + &bt BT_NXT &prv_tab &kp UP &nxt_tab &spc1 &kp KP_PLUS &kp KP_N7 &kp KP_N8 &kp KP_N9 &kp KP_MINUS + &bt BT_PRV &kp LEFT &kp DOWN &kp RIGHT &spc2 &kp KP_MULTIPLY &kp KP_N4 &kp KP_N5 &kp KP_N6 &kp KP_DIVIDE + &bt BT_CLR &none &none &none &spc3 &kp KP_EQUAL &kp KP_N1 &kp KP_N2 &kp KP_N3 &kp KP_DOT + &encoder &none &kp BSPC &kp RET &kp KP_N0 &encoder + >; + sensor-bindings = <&inc_dec_kp PG_UP PG_DN &inc_dec_kp C_VOL_UP C_VOL_DN>; + }; + + symbol_layer { + label = "SYM"; + bindings = < + &none &kp TILDE &kp LT &kp GT &kp PRCNT &kp AMPS &kp CARET &kp LBRC &kp RBRC &tog QWERT + &kp GRAVE &kp EXCL &kp MINUS &kp PLUS &kp EQUAL &kp PIPE &kp COLON &kp LPAR &kp RPAR &kp AT + &none &none &kp SLASH &kp STAR &kp BSLH &kp HASH &kp DLLR &kp LBKT &kp RBKT &none + &encoder &none &none &none &none &encoder + >; + sensor-bindings = <&inc_dec_kp PG_UP PG_DN &inc_dec_kp C_VOL_UP C_VOL_DN>; + }; + + }; +}; \ No newline at end of file diff --git a/app/boards/shields/36key-common.dtsi b/app/boards/shields/36key-common.dtsi new file mode 100644 index 00000000000..79464629da8 --- /dev/null +++ b/app/boards/shields/36key-common.dtsi @@ -0,0 +1,296 @@ + +#define DEFAULT 0 +#define QWERT 1 +#define NUM 2 +#define SYM 3 + +&mt { + flavor = "tap-preferred"; + tapping_term_ms = <200>; +}; + +/ { + // macros + macros { + ZMK_MACRO(hello, // hello world + wait-ms = <0>; + tap-ms = <10>; + bindings = <&kp LS(H) &kp E &kp L &kp L &kp O> + , <&kp COMMA> + , <&kp W &kp O &kp R &kp L &kp D &kp EXCL>; + ) + + ZMK_MACRO(encoder, // for encoder switch press + wait-ms = <0>; + tap-ms = <10>; + bindings = <&kp LS(I) &kp SPACE &kp A &kp M &kp SPACE &kp A &kp SPACE> + , <&kp E &kp N &kp C &kp O &kp D &kp E &kp R>; + ) + + ZMK_MACRO(spc1, // for spacemacs window nav, Alt+M+1 + wait-ms = <0>; + tap-ms = <10>; + bindings = <&kp LA(M) &kp N1>; + ) + + ZMK_MACRO(spc2, + wait-ms = <0>; + tap-ms = <10>; + bindings = <&kp LA(M) &kp N2>; + ) + + ZMK_MACRO(spc3, + wait-ms = <0>; + tap-ms = <10>; + bindings = <&kp LA(M) &kp N3>; + ) + }; + + // custom shift using mod-morph + behaviors { + cmqus: comma_question { + compatible = "zmk,behavior-mod-morph"; + label = "COMMA_QUESTION"; + #binding-cells = <0>; + bindings = <&kp COMMA>, <&kp QUESTION>; + mods = <(MOD_LSFT|MOD_RSFT)>; + }; + + dtild: dot_tilde { + compatible = "zmk,behavior-mod-morph"; + label = "DOT_TILDE"; + #binding-cells = <0>; + bindings = <&kp DOT>, <&kp TILDE>; + mods = <(MOD_LSFT|MOD_RSFT)>; + }; + }; + + // combos + combos { + compatible = "zmk,combos"; + // right hand + combo_esc { + timeout-ms = <50>; + key-positions = <16 17>; + bindings = <&kp ESC>; + }; + + combo_lgui { + timeout-ms = <50>; + key-positions = <5 6>; + bindings = <&kp LGUI>; + }; + + combo_fullscreen { + timeout-ms = <50>; + key-positions = <25 26>; + bindings = <&kp F11>; + }; + + combo_wkspc_up { + timeout-ms = <50>; + key-positions = <9 19>; + bindings = <&kp LC(UP)>; + }; + + combo_wkspc_down { + timeout-ms = <50>; + key-positions = <19 29>; + bindings = <&kp LC(DOWN)>; + }; + + combo_win_up { + timeout-ms = <50>; + key-positions = <7 8>; + bindings = <&kp LS(LG(UP))>; + }; + + combo_win_down { + timeout-ms = <50>; + key-positions = <27 28>; + bindings = <&kp LS(LG(DOWN))>; + }; + + combo_win_left { + timeout-ms = <50>; + key-positions = <17 18>; + bindings = <&kp LS(LG(LEFT))>; + }; + + combo_win_right { + timeout-ms = <50>; + key-positions = <9 18>; + bindings = <&kp LS(LG(RIGHT))>; + }; + + combo_rdelete { + timeout-ms = <50>; + key-positions = <15 16>; + bindings = <&kp DELETE>; + }; + + // left hand + combo_zoomin { + timeout-ms = <50>; + key-positions = <3 4>; + bindings = <&kp LC(PLUS)>; + }; + + combo_zoomout { + timeout-ms = <50>; + key-positions = <23 24>; + bindings = <&kp LC(MINUS)>; + }; + + combo_pgdn { + timeout-ms = <50>; + key-positions = <22 23>; + bindings = <&kp PG_DN>; + }; + + combo_pgup { + timeout-ms = <50>; + key-positions = <2 3>; + bindings = <&kp PG_UP>; + }; + + combo_ldelete { + timeout-ms = <50>; + key-positions = <13 14>; + bindings = <&kp DELETE>; + }; + + // symbols + combo_underscore { + timeout-ms = <50>; + key-positions = <11 12>; + bindings = <&kp UNDERSCORE>; + }; + + combo_minus { + timeout-ms = <50>; + key-positions = <12 13>; + bindings = <&kp MINUS>; + }; + + // others + combo_capswd { + timeout-ms = <50>; + key-positions = <2 7>; + bindings = <&caps_word>; + }; + + combo_lsym { + timeout-ms = <50>; + key-positions = <31 34>; + bindings = <&tog SYM>; + }; + + combo_lnum { + timeout-ms = <50>; + key-positions = <32 33>; + bindings = <&tog NUM>; + }; + + combo_hello { + timeout-ms = <50>; + key-positions = <30 31>; + bindings = <&hello>; + }; + + // output selection + // combo_outusb { + // timeout-ms = <50>; + // key-positions = <4 14>; + // bindings = <&out OUT_USB>; + // }; + + combo_outble { + timeout-ms = <50>; + key-positions = <4 14>; + bindings = <&out OUT_BLE>; + }; + + combo_outtog { + timeout-ms = <50>; + key-positions = <4 24>; + bindings = <&out OUT_TOG>; + }; + + // ble selection + combo_ble1 { + timeout-ms = <50>; + key-positions = <14 24>; + bindings = <&bt BT_SEL 0>; + }; + + combo_ble2 { + timeout-ms = <50>; + key-positions = <13 23>; + bindings = <&bt BT_SEL 1>; + }; + + combo_ble3 { + timeout-ms = <50>; + key-positions = <12 22>; + bindings = <&bt BT_SEL 2>; + }; + + combo_ble4 { + timeout-ms = <50>; + key-positions = <11 21>; + bindings = <&bt BT_SEL 3>; + }; + + combo_ble5 { + timeout-ms = <50>; + key-positions = <10 20>; + bindings = <&bt BT_SEL 4>; + }; + }; + + keymap { + compatible = "zmk,keymap"; + + default_layer { + bindings = < + &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P + &kp A &mt LSFT S < SYM D < NUM F &kp G &kp H < NUM J < SYM K &mt RSFT L &kp SQT + &kp Z &kp X &kp C &kp V &kp B &kp N &kp M &cmqus &dtild &kp SEMI + &encoder &mt LALT TAB &mt LCTL SPACE &mt RCTL RET &mt RALT C &encoder + >; + sensor-bindings = <&inc_dec_kp RIGHT LEFT &inc_dec_kp UP DOWN>; + }; + + qwert_layer { + bindings = < + &kp Q &kp W &kp F &kp P &kp B &kp J &kp L &kp U &kp Y &kp SQT + &kp A &mt LSFT R < SYM S < NUM T &kp G &kp M < NUM N < SYM E &mt RSFT I &kp O + &kp Z &kp X &kp C &kp D &kp V &kp K &kp H &cmqus &dtild &kp SEMI + &encoder &mt LALT TAB &mt LCTL SPACE &mt RCTL RET &mt RALT BSPC &encoder + >; + sensor-bindings = <&inc_dec_kp RIGHT LEFT &inc_dec_kp UP DOWN>; + }; + + number_layer { + bindings = < + &bt BT_NXT &none &kp UP &none &spc1 &kp KP_PLUS &kp KP_N7 &kp KP_N8 &kp KP_N9 &kp KP_MINUS + &bt BT_PRV &kp LEFT &kp DOWN &kp RIGHT &spc2 &kp KP_MULTIPLY &kp KP_N4 &kp KP_N5 &kp KP_N6 &kp KP_DIVIDE + &bt BT_CLR &none &none &none &spc3 &kp KP_EQUAL &kp KP_N1 &kp KP_N2 &kp KP_N3 &kp KP_DOT + &encoder &none &kp BSPC &kp RET &kp KP_N0 &encoder + >; + sensor-bindings = <&inc_dec_kp PG_UP PG_DN &inc_dec_kp C_VOL_UP C_VOL_DN>; + }; + + symbol_layer { + bindings = < + &none &none &kp LT &kp GT &kp PRCNT &kp AMPS &kp CARET &kp LBRC &kp RBRC &tog QWERT + &kp GRAVE &kp EXCL &kp MINUS &kp PLUS &kp EQUAL &kp PIPE &kp COLON &kp LPAR &kp RPAR &kp AT + &none &none &kp SLASH &kp STAR &kp BSLH &kp HASH &kp DLLR &kp LBKT &kp RBKT &none + &encoder &none &none &none &none &encoder + >; + sensor-bindings = <&inc_dec_kp PG_UP PG_DN &inc_dec_kp C_VOL_UP C_VOL_DN>; + }; + + }; +}; \ No newline at end of file diff --git a/app/boards/shields/42key-common-colemak.dtsi b/app/boards/shields/42key-common-colemak.dtsi new file mode 100644 index 00000000000..c916313c538 --- /dev/null +++ b/app/boards/shields/42key-common-colemak.dtsi @@ -0,0 +1,291 @@ +#define DEFAULT 0 +#define NUM 1 +#define SYM 2 + +&mt { + flavor = "tap-preferred"; + tapping_term_ms = <200>; +}; + +/ { + + // macros + macros { + ZMK_MACRO(hello, // hello world + wait-ms = <0>; + tap-ms = <10>; + bindings = <&kp LS(H) &kp E &kp L &kp L &kp O> + , <&kp COMMA> + , <&kp W &kp O &kp R &kp L &kp D &kp EXCL>; + ) + + ZMK_MACRO(encoder, // for encoder switch press + wait-ms = <0>; + tap-ms = <10>; + bindings = <&kp LS(I) &kp SPACE &kp A &kp M &kp SPACE &kp A &kp SPACE> + , <&kp E &kp N &kp C &kp O &kp D &kp E &kp R>; + ) + + ZMK_MACRO(spc1, // for spacemacs window nav, Alt+M+1 + wait-ms = <0>; + tap-ms = <10>; + bindings = <&kp LA(M) &kp N1>; + ) + + ZMK_MACRO(spc2, + wait-ms = <0>; + tap-ms = <10>; + bindings = <&kp LA(M) &kp N2>; + ) + + ZMK_MACRO(spc3, + wait-ms = <0>; + tap-ms = <10>; + bindings = <&kp LA(M) &kp N3>; + ) + }; + + // custom shift using mod-morph + // behaviors { + // cmqus: comma_question { + // compatible = "zmk,behavior-mod-morph"; + // label = "COMMA_QUESTION"; + // #binding-cells = <0>; + // bindings = <&kp COMMA>, <&kp QUESTION>; + // mods = <(MOD_LSFT|MOD_RSFT)>; + // }; + + // dtild: dot_tilde { + // compatible = "zmk,behavior-mod-morph"; + // label = "DOT_TILDE"; + // #binding-cells = <0>; + // bindings = <&kp DOT>, <&kp TILDE>; + // mods = <(MOD_LSFT|MOD_RSFT)>; + // }; + // }; + + // combos + combos { + compatible = "zmk,combos"; + // right hand + combo_esc { + timeout-ms = <50>; + key-positions = <19 20>; + bindings = <&kp ESC>; + }; + + combo_lgui { + timeout-ms = <50>; + key-positions = <6 7>; + bindings = <&kp LGUI>; + }; + + combo_fullscreen { + timeout-ms = <50>; + key-positions = <30 31>; + bindings = <&kp F11>; + }; + + combo_wkspc_up { + timeout-ms = <50>; + key-positions = <11 23>; + bindings = <&kp LC(UP)>; + }; + + combo_wkspc_down { + timeout-ms = <50>; + key-positions = <23 35>; + bindings = <&kp LC(DOWN)>; + }; + + combo_win_up { + timeout-ms = <50>; + key-positions = <9 10>; + bindings = <&kp LS(LG(UP))>; + }; + + combo_win_down { + timeout-ms = <50>; + key-positions = <33 34>; + bindings = <&kp LS(LG(DOWN))>; + }; + + combo_win_left { + timeout-ms = <50>; + key-positions = <21 22>; + bindings = <&kp LS(LG(LEFT))>; + }; + + combo_win_right { + timeout-ms = <50>; + key-positions = <22 23>; + bindings = <&kp LS(LG(RIGHT))>; + }; + + combo_rdelete { + timeout-ms = <50>; + key-positions = <18 19>; + bindings = <&kp DELETE>; + }; + + // left hand + combo_zoomin { + timeout-ms = <50>; + key-positions = <4 5>; + bindings = <&kp LC(PLUS)>; + }; + + combo_zoomout { + timeout-ms = <50>; + key-positions = <28 29>; + bindings = <&kp LC(MINUS)>; + }; + + combo_pgdn { + timeout-ms = <50>; + key-positions = <27 28>; + bindings = <&kp PG_DN>; + }; + + combo_pgup { + timeout-ms = <50>; + key-positions = <3 4>; + bindings = <&kp PG_UP>; + }; + + combo_ldelete { + timeout-ms = <50>; + key-positions = <16 17>; + bindings = <&kp DELETE>; + }; + + // symbols + combo_underscore { + timeout-ms = <50>; + key-positions = <14 15>; + bindings = <&kp UNDERSCORE>; + }; + + combo_minus { + timeout-ms = <50>; + key-positions = <15 16>; + bindings = <&kp MINUS>; + }; + + // others + combo_capswd { + timeout-ms = <50>; + key-positions = <3 8>; + bindings = <&caps_word>; + }; + + combo_lsym { + timeout-ms = <50>; + key-positions = <37 40>; + bindings = <&tog SYM>; + }; + + combo_lnum { + timeout-ms = <50>; + key-positions = <38 39>; + bindings = <&tog NUM>; + }; + + combo_hello { + timeout-ms = <50>; + key-positions = <37 38>; + bindings = <&hello>; + }; + + combo_outble { + timeout-ms = <50>; + key-positions = <5 17>; + bindings = <&out OUT_BLE>; + }; + + combo_outtog { + timeout-ms = <50>; + key-positions = <5 29>; + bindings = <&out OUT_TOG>; + }; + + // ble selection + combo_ble1 { + timeout-ms = <50>; + key-positions = <17 29>; + bindings = <&bt BT_SEL 0>; + }; + + combo_ble2 { + timeout-ms = <50>; + key-positions = <16 28>; + bindings = <&bt BT_SEL 1>; + }; + + combo_ble3 { + timeout-ms = <50>; + key-positions = <15 27>; + bindings = <&bt BT_SEL 2>; + }; + + combo_ble4 { + timeout-ms = <50>; + key-positions = <14 26>; + bindings = <&bt BT_SEL 3>; + }; + + combo_ble5 { + timeout-ms = <50>; + key-positions = <13 25>; + bindings = <&bt BT_SEL 4>; + }; + }; + + keymap { + compatible = "zmk,keymap"; + + default_layer { + label = "QWERTY"; +// ----------------------------------------------------------------------------------------- +// | TAB | Q | W | E | R | T | | Y | U | I | O | P | BKSP | +// | CTRL | A | S | D | F | G | | H | J | K | L | ; | ' | +// | SHFT | Z | X | C | V | B | | N | M | , | . | / | ESC | +// | GUI | LWR | SPC | | ENT | RSE | ALT | + bindings = < + &kp TAB &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P &kp BSPC + &kp N0 &kp A &kp S &kp D &kp F &kp G &kp H &kp J &kp K &kp L &kp SEMI &kp SQT + &kp N1 &kp Z &kp X &kp C &kp V &kp B &kp N &kp M &kp COMMA &kp DOT &kp FSLH &kp N3 + &kp LGUI < NUM N5 &kp SPACE &kp RET < SYM N6 &kp RALT + >; + }; + lower_layer { + label = "NUMBER"; +// ----------------------------------------------------------------------------------------- +// | TAB | 1 | 2 | 3 | 4 | 5 | | 6 | 7 | 8 | 9 | 0 | BKSP | +// | BTCLR| BT1 | BT2 | BT3 | BT4 | BT5 | | LFT | DWN | UP | RGT | | | +// | SHFT | | | | | | | | | | | | | +// | GUI | | SPC | | ENT | | ALT | + bindings = < + &kp TAB &kp N1 &kp N2 &kp N3 &kp N4 &kp N5 &kp N6 &kp N7 &kp N8 &kp N9 &kp N0 &kp BSPC + &bt BT_CLR &bt BT_SEL 0 &bt BT_SEL 1 &bt BT_SEL 2 &bt BT_SEL 3 &bt BT_SEL 4 &kp LEFT &kp DOWN &kp UP &kp RIGHT &trans &trans + &kp LSHFT &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans + &kp LGUI &trans &kp SPACE &kp RET &trans &kp RALT + >; + }; + + raise_layer { + label = "SYMBOL"; +// ----------------------------------------------------------------------------------------- +// | TAB | ! | @ | # | $ | % | | ^ | & | * | ( | ) | BKSP | +// | CTRL | | | | | | | - | = | [ | ] | \ | ` | +// | SHFT | | | | | | | _ | + | { | } | "|" | ~ | +// | GUI | | SPC | | ENT | | ALT | + bindings = < + &kp TAB &kp EXCL &kp AT &kp HASH &kp DLLR &kp PRCNT &kp CARET &kp AMPS &kp KP_MULTIPLY &kp LPAR &kp RPAR &kp BSPC + &kp LCTRL &trans &trans &trans &trans &trans &kp MINUS &kp EQUAL &kp LBKT &kp RBKT &kp BSLH &kp GRAVE + &kp LSHFT &trans &trans &trans &trans &trans &kp UNDER &kp PLUS &kp LBRC &kp RBRC &kp PIPE &kp TILDE + &kp LGUI &trans &kp SPACE &kp RET &trans &kp RALT + >; + }; + }; +}; \ No newline at end of file diff --git a/app/boards/shields/42key-common.dtsi b/app/boards/shields/42key-common.dtsi new file mode 100644 index 00000000000..7f07abb44d2 --- /dev/null +++ b/app/boards/shields/42key-common.dtsi @@ -0,0 +1,291 @@ +#define DEFAULT 0 +#define NUM 1 +#define SYM 2 + +&mt { + flavor = "tap-preferred"; + tapping_term_ms = <200>; +}; + +/ { + + // macros + macros { + ZMK_MACRO(hello, // hello world + wait-ms = <0>; + tap-ms = <10>; + bindings = <&kp LS(H) &kp E &kp L &kp L &kp O> + , <&kp COMMA> + , <&kp W &kp O &kp R &kp L &kp D &kp EXCL>; + ) + + ZMK_MACRO(encoder, // for encoder switch press + wait-ms = <0>; + tap-ms = <10>; + bindings = <&kp LS(I) &kp SPACE &kp A &kp M &kp SPACE &kp A &kp SPACE> + , <&kp E &kp N &kp C &kp O &kp D &kp E &kp R>; + ) + + ZMK_MACRO(spc1, // for spacemacs window nav, Alt+M+1 + wait-ms = <0>; + tap-ms = <10>; + bindings = <&kp LA(M) &kp N1>; + ) + + ZMK_MACRO(spc2, + wait-ms = <0>; + tap-ms = <10>; + bindings = <&kp LA(M) &kp N2>; + ) + + ZMK_MACRO(spc3, + wait-ms = <0>; + tap-ms = <10>; + bindings = <&kp LA(M) &kp N3>; + ) + }; + + // custom shift using mod-morph + // behaviors { + // cmqus: comma_question { + // compatible = "zmk,behavior-mod-morph"; + // label = "COMMA_QUESTION"; + // #binding-cells = <0>; + // bindings = <&kp COMMA>, <&kp QUESTION>; + // mods = <(MOD_LSFT|MOD_RSFT)>; + // }; + + // dtild: dot_tilde { + // compatible = "zmk,behavior-mod-morph"; + // label = "DOT_TILDE"; + // #binding-cells = <0>; + // bindings = <&kp DOT>, <&kp TILDE>; + // mods = <(MOD_LSFT|MOD_RSFT)>; + // }; + // }; + + // combos + combos { + compatible = "zmk,combos"; + // right hand + combo_esc { + timeout-ms = <50>; + key-positions = <19 20>; + bindings = <&kp ESC>; + }; + + combo_lgui { + timeout-ms = <50>; + key-positions = <6 7>; + bindings = <&kp LGUI>; + }; + + combo_fullscreen { + timeout-ms = <50>; + key-positions = <30 31>; + bindings = <&kp F11>; + }; + + combo_wkspc_up { + timeout-ms = <50>; + key-positions = <11 23>; + bindings = <&kp LC(UP)>; + }; + + combo_wkspc_down { + timeout-ms = <50>; + key-positions = <23 35>; + bindings = <&kp LC(DOWN)>; + }; + + combo_win_up { + timeout-ms = <50>; + key-positions = <9 10>; + bindings = <&kp LS(LG(UP))>; + }; + + combo_win_down { + timeout-ms = <50>; + key-positions = <33 34>; + bindings = <&kp LS(LG(DOWN))>; + }; + + combo_win_left { + timeout-ms = <50>; + key-positions = <21 22>; + bindings = <&kp LS(LG(LEFT))>; + }; + + combo_win_right { + timeout-ms = <50>; + key-positions = <22 23>; + bindings = <&kp LS(LG(RIGHT))>; + }; + + combo_rdelete { + timeout-ms = <50>; + key-positions = <18 19>; + bindings = <&kp DELETE>; + }; + + // left hand + combo_zoomin { + timeout-ms = <50>; + key-positions = <4 5>; + bindings = <&kp LC(PLUS)>; + }; + + combo_zoomout { + timeout-ms = <50>; + key-positions = <28 29>; + bindings = <&kp LC(MINUS)>; + }; + + combo_pgdn { + timeout-ms = <50>; + key-positions = <27 28>; + bindings = <&kp PG_DN>; + }; + + combo_pgup { + timeout-ms = <50>; + key-positions = <3 4>; + bindings = <&kp PG_UP>; + }; + + combo_ldelete { + timeout-ms = <50>; + key-positions = <16 17>; + bindings = <&kp DELETE>; + }; + + // symbols + combo_underscore { + timeout-ms = <50>; + key-positions = <14 15>; + bindings = <&kp UNDERSCORE>; + }; + + combo_minus { + timeout-ms = <50>; + key-positions = <15 16>; + bindings = <&kp MINUS>; + }; + + // others + combo_capswd { + timeout-ms = <50>; + key-positions = <3 8>; + bindings = <&caps_word>; + }; + + combo_lsym { + timeout-ms = <50>; + key-positions = <37 40>; + bindings = <&tog SYM>; + }; + + combo_lnum { + timeout-ms = <50>; + key-positions = <38 39>; + bindings = <&tog NUM>; + }; + + combo_hello { + timeout-ms = <50>; + key-positions = <37 38>; + bindings = <&hello>; + }; + + combo_outble { + timeout-ms = <50>; + key-positions = <5 17>; + bindings = <&out OUT_BLE>; + }; + + combo_outtog { + timeout-ms = <50>; + key-positions = <5 29>; + bindings = <&out OUT_TOG>; + }; + + // ble selection + combo_ble1 { + timeout-ms = <50>; + key-positions = <17 29>; + bindings = <&bt BT_SEL 0>; + }; + + combo_ble2 { + timeout-ms = <50>; + key-positions = <16 28>; + bindings = <&bt BT_SEL 1>; + }; + + combo_ble3 { + timeout-ms = <50>; + key-positions = <15 27>; + bindings = <&bt BT_SEL 2>; + }; + + combo_ble4 { + timeout-ms = <50>; + key-positions = <14 26>; + bindings = <&bt BT_SEL 3>; + }; + + combo_ble5 { + timeout-ms = <50>; + key-positions = <13 25>; + bindings = <&bt BT_SEL 4>; + }; + }; + + keymap { + compatible = "zmk,keymap"; + + default_layer { + label = "QWERTY"; +// ----------------------------------------------------------------------------------------- +// | TAB | Q | W | E | R | T | | Y | U | I | O | P | BKSP | +// | CTRL | A | S | D | F | G | | H | J | K | L | ; | ' | +// | SHFT | Z | X | C | V | B | | N | M | , | . | / | ESC | +// | GUI | LWR | SPC | | ENT | RSE | ALT | + bindings = < + &kp TAB &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P &kp BSPC + &kp LCTRL &kp A &kp S &kp D &kp F &kp G &kp H &kp J &kp K &kp L &kp SEMI &kp SQT + &kp LSHFT &kp Z &kp X &kp C &kp V &kp B &kp N &kp M &kp COMMA &kp DOT &kp FSLH &kp ESC + &kp LGUI &mo NUM &kp SPACE &kp RET &mo SYM &kp RALT + >; + }; + lower_layer { + label = "NUMBER"; +// ----------------------------------------------------------------------------------------- +// | TAB | 1 | 2 | 3 | 4 | 5 | | 6 | 7 | 8 | 9 | 0 | BKSP | +// | BTCLR| BT1 | BT2 | BT3 | BT4 | BT5 | | LFT | DWN | UP | RGT | | | +// | SHFT | | | | | | | | | | | | | +// | GUI | | SPC | | ENT | | ALT | + bindings = < + &kp TAB &kp N1 &kp N2 &kp N3 &kp N4 &kp N5 &kp N6 &kp N7 &kp N8 &kp N9 &kp N0 &kp BSPC + &bt BT_CLR &bt BT_SEL 0 &bt BT_SEL 1 &bt BT_SEL 2 &bt BT_SEL 3 &bt BT_SEL 4 &kp LEFT &kp DOWN &kp UP &kp RIGHT &trans &trans + &kp LSHFT &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans + &kp LGUI &trans &kp SPACE &kp RET &trans &kp RALT + >; + }; + + raise_layer { + label = "SYMBOL"; +// ----------------------------------------------------------------------------------------- +// | TAB | ! | @ | # | $ | % | | ^ | & | * | ( | ) | BKSP | +// | CTRL | | | | | | | - | = | [ | ] | \ | ` | +// | SHFT | | | | | | | _ | + | { | } | "|" | ~ | +// | GUI | | SPC | | ENT | | ALT | + bindings = < + &kp TAB &kp EXCL &kp AT &kp HASH &kp DLLR &kp PRCNT &kp CARET &kp AMPS &kp KP_MULTIPLY &kp LPAR &kp RPAR &kp BSPC + &kp LCTRL &trans &trans &trans &trans &trans &kp MINUS &kp EQUAL &kp LBKT &kp RBKT &kp BSLH &kp GRAVE + &kp LSHFT &trans &trans &trans &trans &trans &kp UNDER &kp PLUS &kp LBRC &kp RBRC &kp PIPE &kp TILDE + &kp LGUI &trans &kp SPACE &kp RET &trans &kp RALT + >; + }; + }; +}; \ No newline at end of file diff --git a/app/boards/shields/key-common.dtsi b/app/boards/shields/key-common.dtsi new file mode 100644 index 00000000000..003098f6a42 --- /dev/null +++ b/app/boards/shields/key-common.dtsi @@ -0,0 +1,250 @@ +#define DEFAULT 0 +#define QWERT 1 +#define NUM 2 +#define SYM 3 + + // macros + macros { + ZMK_MACRO(hello, // hello world + wait-ms = <0>; + tap-ms = <10>; + bindings = <&kp LS(H) &kp E &kp L &kp L &kp O> + , <&kp COMMA> + , <&kp W &kp O &kp R &kp L &kp D &kp EXCL>; + ) + + ZMK_MACRO(encoder, // for encoder switch press + wait-ms = <0>; + tap-ms = <10>; + bindings = <&kp LS(I) &kp SPACE &kp A &kp M &kp SPACE &kp A &kp SPACE> + , <&kp E &kp N &kp C &kp O &kp D &kp E &kp R>; + ) + + ZMK_MACRO(spc1, // for spacemacs window nav, Alt+M+1 + wait-ms = <0>; + tap-ms = <10>; + bindings = <&kp LA(M) &kp N1>; + ) + + ZMK_MACRO(spc2, + wait-ms = <0>; + tap-ms = <10>; + bindings = <&kp LA(M) &kp N2>; + ) + + ZMK_MACRO(spc3, + wait-ms = <0>; + tap-ms = <10>; + bindings = <&kp LA(M) &kp N3>; + ) + + ZMK_MACRO(nxt_tab, + wait-ms = <0>; + tap-ms = <10>; + bindings = <&kp LC(TAB)>; + ) + + ZMK_MACRO(prv_tab, + wait-ms = <0>; + tap-ms = <10>; + bindings = <&kp LC(LS(TAB))>; + ) + }; + + // custom shift using mod-morph + behaviors { + cmqus: comma_question { + compatible = "zmk,behavior-mod-morph"; + label = "COMMA_QUESTION"; + #binding-cells = <0>; + bindings = <&kp COMMA>, <&kp QUESTION>; + mods = <(MOD_LSFT|MOD_RSFT)>; + }; + + dtsmi: dot_semi { + compatible = "zmk,behavior-mod-morph"; + label = "DOT_SEMI"; + #binding-cells = <0>; + bindings = <&kp DOT>, <&kp SEMI>; + mods = <(MOD_LSFT|MOD_RSFT)>; + masked_mods = <(MOD_LSFT|MOD_RSFT)>; // don't send shift + }; + }; + + // combos + combos { + compatible = "zmk,combos"; + // right hand + combo_esc { + timeout-ms = <50>; + key-positions = <16 17>; + bindings = <&kp ESC>; + }; + + combo_lgui { + timeout-ms = <50>; + key-positions = <5 6>; + bindings = <&kp LGUI>; + }; + + combo_fullscreen { + timeout-ms = <50>; + key-positions = <25 26>; + bindings = <&kp F11>; + }; + + combo_wkspc_up { + timeout-ms = <50>; + key-positions = <9 19>; + bindings = <&kp LC(UP)>; + }; + + combo_wkspc_down { + timeout-ms = <50>; + key-positions = <19 29>; + bindings = <&kp LC(DOWN)>; + }; + + combo_win_up { + timeout-ms = <50>; + key-positions = <7 8>; + bindings = <&kp LS(LG(UP))>; + }; + + combo_win_down { + timeout-ms = <50>; + key-positions = <27 28>; + bindings = <&kp LS(LG(DOWN))>; + }; + + combo_win_left { + timeout-ms = <50>; + key-positions = <17 18>; + bindings = <&kp LS(LG(LEFT))>; + }; + + combo_win_right { + timeout-ms = <50>; + key-positions = <9 18>; + bindings = <&kp LS(LG(RIGHT))>; + }; + + combo_rdelete { + timeout-ms = <50>; + key-positions = <15 16>; + bindings = <&kp DELETE>; + }; + + // left hand + combo_zoomin { + timeout-ms = <50>; + key-positions = <3 4>; + bindings = <&kp LC(PLUS)>; + }; + + combo_zoomout { + timeout-ms = <50>; + key-positions = <23 24>; + bindings = <&kp LC(MINUS)>; + }; + + combo_pgdn { + timeout-ms = <50>; + key-positions = <22 23>; + bindings = <&kp PG_DN>; + }; + + combo_pgup { + timeout-ms = <50>; + key-positions = <2 3>; + bindings = <&kp PG_UP>; + }; + + combo_ldelete { + timeout-ms = <50>; + key-positions = <13 14>; + bindings = <&kp DELETE>; + }; + + // symbols + combo_underscore { + timeout-ms = <50>; + key-positions = <11 12>; + bindings = <&kp UNDERSCORE>; + }; + + combo_minus { + timeout-ms = <50>; + key-positions = <12 13>; + bindings = <&kp MINUS>; + }; + + // toggles + combo_capswd { + timeout-ms = <50>; + key-positions = <2 7>; + bindings = <&caps_word>; + }; + + combo_lsym { + timeout-ms = <50>; + key-positions = <31 34>; + bindings = <&tog SYM>; + }; + + combo_lnum { + timeout-ms = <50>; + key-positions = <32 33>; + bindings = <&tog NUM>; + }; + + // output selection + // combo_outusb { + // timeout-ms = <50>; + // key-positions = <4 14>; + // bindings = <&out OUT_USB>; + // }; + + combo_outble { + timeout-ms = <50>; + key-positions = <4 14>; + bindings = <&out OUT_BLE>; + }; + + combo_outtog { + timeout-ms = <50>; + key-positions = <4 24>; + bindings = <&out OUT_TOG>; + }; + + // ble selection + combo_ble1 { + timeout-ms = <50>; + key-positions = <14 24>; + bindings = <&bt BT_SEL 0>; + }; + + combo_ble2 { + timeout-ms = <50>; + key-positions = <13 23>; + bindings = <&bt BT_SEL 1>; + }; + + combo_ble3 { + timeout-ms = <50>; + key-positions = <12 22>; + bindings = <&bt BT_SEL 2>; + }; + + combo_ble4 { + timeout-ms = <50>; + key-positions = <11 21>; + bindings = <&bt BT_SEL 3>; + }; + + combo_ble5 { + timeout-ms = <50>; + key-positions = <10 20>; + bindings = <&bt BT_SEL 4>; + }; + }; \ No newline at end of file diff --git a/app/boards/shields/sweepro-ce/Kconfig.defconfig b/app/boards/shields/sweepro-ce/Kconfig.defconfig new file mode 100644 index 00000000000..6470bad3cff --- /dev/null +++ b/app/boards/shields/sweepro-ce/Kconfig.defconfig @@ -0,0 +1,29 @@ +# Copyright (c) 2020 The ZMK Contributors +# SPDX-License-Identifier: MIT + +if SWEEPRO_CE_LEFT + +config ZMK_KEYBOARD_NAME + default "SweepPro-CE" + +config ZMK_SPLIT_BLE_ROLE_CENTRAL + default y + +endif + +if SWEEPRO_CE_RIGHT +config ZMK_KEYBOARD_NAME + default "SweepPro-CE-R" + +endif + +if SWEEPRO_CE_LEFT || SWEEPRO_CE_RIGHT + +config ZMK_SPLIT + default y + +config ZMK_USB + default y + +endif + diff --git a/app/boards/shields/sweepro-ce/Kconfig.shield b/app/boards/shields/sweepro-ce/Kconfig.shield new file mode 100644 index 00000000000..457e8f7cd42 --- /dev/null +++ b/app/boards/shields/sweepro-ce/Kconfig.shield @@ -0,0 +1,8 @@ +# Copyright (c) 2020 The ZMK Contributors +# SPDX-License-Identifier: MIT + +config SWEEPRO_CE_LEFT + def_bool $(shields_list_contains,sweepro-ce_left) + +config SWEEPRO_CE_RIGHT + def_bool $(shields_list_contains,sweepro-ce_right) diff --git a/app/boards/shields/sweepro-ce/README.md b/app/boards/shields/sweepro-ce/README.md new file mode 100644 index 00000000000..b1344293d2a --- /dev/null +++ b/app/boards/shields/sweepro-ce/README.md @@ -0,0 +1,35 @@ +# Cradio + +Cradio is a firmware for a few 34 key keyboards, including Cradio, Hypergolic and Sweep. + +## Pin arrangement + +Some revisions of the aforementioned PCBs have slightly different pin arrangements compared to what's defined in [`cradio.dtsi`](./cradio.dtsi). If you need to swap a few keys for your particular PCB, you can easily reorder the `input-gpio` definition in your own keymap file (i.e. in `zmk-config/config/cradio.keymap`): + +```dts +/* Adjusted Cradio pin arrangement */ +/* The position of Q and B keys have been swapped */ +&kscan0 { + input-gpios + = <&pro_micro_d 6 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_a 0 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_a 1 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_a 2 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_a 3 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_d 15 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_d 14 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_d 16 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_d 10 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_d 1 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_d 2 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_d 3 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_d 4 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_d 5 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_d 7 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_d 8 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_d 9 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + ; +}; +``` + +This `&kscan0` block must be placed outside of any blocks surrounded by curly braces (`{...}`). diff --git a/app/boards/shields/sweepro-ce/sweepro-ce.conf.bak b/app/boards/shields/sweepro-ce/sweepro-ce.conf.bak new file mode 100644 index 00000000000..f0d20263707 --- /dev/null +++ b/app/boards/shields/sweepro-ce/sweepro-ce.conf.bak @@ -0,0 +1,29 @@ +# Copyright (c) 2020 The ZMK Contributors +# SPDX-License-Identifier: MIT + +# Enable epaper display +CONFIG_ZMK_DISPLAY=y +CONFIG_NRFMACRO_EPD_DISPLAY=y + +# diplay settings (NOTE: these configurations have to be explicitly set here in shield) +## color scheme: black/white +CONFIG_LVGL_USE_THEME_MONO=y +CONFIG_LVGL_THEME_DEFAULT_COLOR_PRIMARY_BLACK=y +CONFIG_LVGL_THEME_DEFAULT_COLOR_SECONDARY_WHITE=y +CONFIG_LVGL_THEME_DEFAULT_COLOR_PRIMARY_RED=n +CONFIG_LVGL_THEME_DEFAULT_COLOR_SECONDARY_RED=n +## dedicated work queue or not +CONFIG_ZMK_DISPLAY_WORK_QUEUE_DEDICATED=y +## automatically refresh the full screen in a constant interval (unit: second), useful for epd, see pr #969 +CONFIG_ZMK_DISPLAY_FULL_REFRESH_PERIOD=15 + +# Enable encoder support +CONFIG_EC11=y +CONFIG_EC11_TRIGGER_GLOBAL_THREAD=y + +# increase transmission power a little bit +CONFIG_BT_CTLR_TX_PWR_PLUS_8=y + +# auto-sleep after 15 mins +CONFIG_ZMK_SLEEP=y +CONFIG_ZMK_IDLE_SLEEP_TIMEOUT=900000 \ No newline at end of file diff --git a/app/boards/shields/sweepro-ce/sweepro-ce.dtsi b/app/boards/shields/sweepro-ce/sweepro-ce.dtsi new file mode 100644 index 00000000000..0dc0fa1e452 --- /dev/null +++ b/app/boards/shields/sweepro-ce/sweepro-ce.dtsi @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include + +/ { + + chosen: chosen { + zmk,kscan = &kscan; + zmk,matrix_transform = &default_transform; + zephyr,display = &shield_epd; + }; + + default_transform: keymap_transform_0 { + compatible = "zmk,matrix-transform"; + columns = <10>; + rows = <4>; +// | SW1 | SW2 | SW3 | SW4 | SW5 | | SW5 | SW4 | SW3 | SW2 | SW1 | +// | SW6 | SW7 | SW8 | SW9 | SW10 | | SW10 | SW9 | SW8 | SW7 | SW6 | +// | SW11 | SW12 | SW13 | SW14 | SW15 | | SW15 | SW14 | SW13 | SW12 | SW11 | +// | SW16 | SW17 | SW18 | | SW18 | SW17 | SW16 | + map = < +RC(0,0) RC(0,1) RC(0,2) RC(0,3) RC(0,4) RC(0,5) RC(0,6) RC(0,7) RC(0,8) RC(0,9) +RC(1,0) RC(1,1) RC(1,2) RC(1,3) RC(1,4) RC(1,5) RC(1,6) RC(1,7) RC(1,8) RC(1,9) +RC(2,0) RC(2,1) RC(2,2) RC(2,3) RC(2,4) RC(2,5) RC(2,6) RC(2,7) RC(2,8) RC(2,9) + RC(3,2) RC(3,3) RC(3,4) RC(3,5) RC(3,6) RC(3,7) + >; + }; + + // use composite kscan: 1. 34 keys with key matrix; 2. 2 extra keys from encoder with direct connect + kscan: kscan { + compatible = "zmk,kscan-composite"; + label = "KSCAN"; + rows = <4>; + columns = <10>; + + normal_keys { + kscan = <&kscan_matrix>; + }; + + encoder_keys: encoder_keys { + kscan = <&kscan_direct>; + row-offset = <3>; + // column-offset = <2>; + }; + }; + + // 1. scan normal key switches + kscan_matrix: kscan_matrix { + compatible = "zmk,kscan-gpio-matrix"; + label = "KSCAN_MATRIX"; + + diode-direction = "col2row"; + row-gpios + = <&pro_micro 14 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 16 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 10 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 9 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + ; + }; + + // 2. scan encoder switch + kscan_direct: kscan_direct { + compatible = "zmk,kscan-gpio-direct"; + label = "KSCAN_DIRECT"; + input-gpios = <&pro_micro 8 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + }; + + /* encoders */ + left_encoder: encoder_left { + compatible = "alps,ec11"; + status = "disabled"; + label = "LEFT_ENCODER"; + a-gpios = <&pro_micro 7 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + b-gpios = <&pro_micro 6 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + resolution = <2>; + }; + + right_encoder: encoder_right { + compatible = "alps,ec11"; + status = "disabled"; + label = "RIGHT_ENCODER"; + a-gpios = <&pro_micro 7 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + b-gpios = <&pro_micro 6 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + resolution = <2>; + }; + + sensors { + compatible = "zmk,keymap-sensors"; + sensors = <&left_encoder &right_encoder>; + }; +}; + +/* display */ +&pro_micro_spi { +// &spi2 { + compatible = "nordic,nrf-spim"; + sck-pin = <17>; + mosi-pin = <15>; + cs-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>; + // not used, but required by spim binding; can be reuesd in the keymap for normal key press + miso-pin = <10>; + + // activate this device + status = "okay"; + + // spi-device: gooddisplay GDEW0102T4 + shield_epd: il0323@0 { + compatible = "gooddisplay,il0323"; + reg = <0>; + label = "DISPLAY"; + spi-max-frequency = <4000000>; + + height = <128>; + width = <80>; + reset-gpios = <&gpio0 8 GPIO_ACTIVE_LOW>; + dc-gpios = <&gpio0 20 GPIO_ACTIVE_LOW>; + busy-gpios = <&gpio0 6 GPIO_ACTIVE_LOW>; + pwr = [03 00 26 26]; + cdi = <0xd2>; + tcon = <0x22>; + }; +}; diff --git a/app/boards/shields/sweepro-ce/sweepro-ce.keymap b/app/boards/shields/sweepro-ce/sweepro-ce.keymap new file mode 100644 index 00000000000..f12978df75b --- /dev/null +++ b/app/boards/shields/sweepro-ce/sweepro-ce.keymap @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include +#include + +// #include "../36key-common.dtsi" +#include "../36key-common-colemak.dtsi" \ No newline at end of file diff --git a/app/boards/shields/sweepro-ce/sweepro-ce.zmk.yml b/app/boards/shields/sweepro-ce/sweepro-ce.zmk.yml new file mode 100644 index 00000000000..d1ac73f01d8 --- /dev/null +++ b/app/boards/shields/sweepro-ce/sweepro-ce.zmk.yml @@ -0,0 +1,12 @@ +file_format: "1" +id: sweepro-ce +name: SweepPro-CE +type: shield +url: https://github.com/ufan/mykeyboard +requires: [pro_micro] +exposes: [i2c_oled] +features: + - keys: +siblings: + - sweepro-ce_left + - sweepro-ce_right diff --git a/app/boards/shields/sweepro-ce/sweepro-ce_left.conf b/app/boards/shields/sweepro-ce/sweepro-ce_left.conf new file mode 100644 index 00000000000..ad418ee8357 --- /dev/null +++ b/app/boards/shields/sweepro-ce/sweepro-ce_left.conf @@ -0,0 +1,36 @@ +# Copyright (c) 2020 The ZMK Contributors +# SPDX-License-Identifier: MIT + +# Set the role of the split, used in choosing the dedicated status screen +CONFIG_NRFMACRO_SHIELD_MASTER=y + +# Enable epaper display +CONFIG_ZMK_DISPLAY=y +CONFIG_NRFMACRO_EPD_DISPLAY=y +CONFIG_MYCHANGE_DISPLAY_UPDATE_MO_BEHAVIOR=n + +# diplay settings (NOTE: these configurations have to be explicitly set here in shield) +## color scheme: black/white +CONFIG_LVGL_USE_THEME_MONO=y +CONFIG_LVGL_THEME_DEFAULT_COLOR_PRIMARY_BLACK=y +CONFIG_LVGL_THEME_DEFAULT_COLOR_SECONDARY_WHITE=y +CONFIG_LVGL_THEME_DEFAULT_COLOR_PRIMARY_RED=n +CONFIG_LVGL_THEME_DEFAULT_COLOR_SECONDARY_RED=n +## dedicated work queue or not +CONFIG_ZMK_DISPLAY_WORK_QUEUE_DEDICATED=y + +## custom screen or not +### uncommet following for custom status screen +CONFIG_ZMK_DISPLAY_STATUS_SCREEN_CUSTOM=y +CONFIG_LVGL_THEME_DEFAULT_FONT_NORMAL_MONTSERRAT_16=y +CONFIG_CUSTOM_WIDGET_BATTERY_STATUS=y +CONFIG_ZMK_WIDGET_BATTERY_STATUS=n +CONFIG_CUSTOM_WIDGET_OUTPUT_STATUS=y +CONFIG_ZMK_WIDGET_OUTPUT_STATUS=n +CONFIG_CUSTOM_WIDGET_LAYER_STATUS=y +CONFIG_ZMK_WIDGET_LAYER_STATUS=n + +# Enable encoder support +CONFIG_EC11=y +CONFIG_EC11_TRIGGER_GLOBAL_THREAD=y + diff --git a/app/boards/shields/sweepro-ce/sweepro-ce_left.overlay b/app/boards/shields/sweepro-ce/sweepro-ce_left.overlay new file mode 100644 index 00000000000..99a51177ac5 --- /dev/null +++ b/app/boards/shields/sweepro-ce/sweepro-ce_left.overlay @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include "sweepro-ce.dtsi" + +// setup display +// &chosen { +// zephyr,display = &epd; +// }; + +// &nrfmacro_spi { +// status = "okay"; +// }; + +// setup kscan pins +&kscan_matrix { + col-gpios + = <&pro_micro 21 GPIO_ACTIVE_HIGH> + , <&pro_micro 20 GPIO_ACTIVE_HIGH> + , <&pro_micro 19 GPIO_ACTIVE_HIGH> + , <&pro_micro 18 GPIO_ACTIVE_HIGH> + , <&pro_micro 15 GPIO_ACTIVE_HIGH> + ; +}; + +&encoder_keys { + column-offset = <2>; +}; + +// enable left encoder +&left_encoder { + status = "okay"; +}; \ No newline at end of file diff --git a/app/boards/shields/sweepro-ce/sweepro-ce_right.conf b/app/boards/shields/sweepro-ce/sweepro-ce_right.conf new file mode 100644 index 00000000000..33bb5bc07e2 --- /dev/null +++ b/app/boards/shields/sweepro-ce/sweepro-ce_right.conf @@ -0,0 +1,38 @@ +# Copyright (c) 2020 The ZMK Contributors +# SPDX-License-Identifier: MIT + +# Set the role of the split, used in choosing the dedicated status screen +CONFIG_NRFMACRO_SHIELD_SLAVE=y + +# Enable epaper display +CONFIG_ZMK_DISPLAY=y +CONFIG_NRFMACRO_EPD_DISPLAY=y + +# diplay settings (NOTE: these configurations have to be explicitly set here in shield) +## color scheme: black/white +CONFIG_LVGL_USE_THEME_MONO=y +CONFIG_LVGL_THEME_DEFAULT_COLOR_PRIMARY_BLACK=y +CONFIG_LVGL_THEME_DEFAULT_COLOR_SECONDARY_WHITE=y +CONFIG_LVGL_THEME_DEFAULT_COLOR_PRIMARY_RED=n +CONFIG_LVGL_THEME_DEFAULT_COLOR_SECONDARY_RED=n +## dedicated work queue or not +CONFIG_ZMK_DISPLAY_WORK_QUEUE_DEDICATED=y + +# custom screen or not +## uncommet following for custom screen +CONFIG_ZMK_DISPLAY_STATUS_SCREEN_CUSTOM=y +CONFIG_LVGL_THEME_DEFAULT_FONT_SMALL_MONTSERRAT_12=y +CONFIG_LVGL_THEME_DEFAULT_FONT_NORMAL_MONTSERRAT_16=y +CONFIG_CUSTOM_WIDGET_BATTERY_STATUS=y +CONFIG_ZMK_WIDGET_BATTERY_STATUS=n +CONFIG_CUSTOM_WIDGET_PERIPHERAL_STATUS=y +CONFIG_ZMK_WIDGET_PERIPHERAL_STATUS=n +CONFIG_NRFMACRO_SCREEN_MARK_LOGO=y + +# use custom logo or not +# CONFIG_NRFMACRO_SCREEN_STANDARD_LOGO=y +CONFIG_NRFMACRO_SCREEN_CUSTOM_LOGO=y + +# Enable encoder support +CONFIG_EC11=y +CONFIG_EC11_TRIGGER_GLOBAL_THREAD=y \ No newline at end of file diff --git a/app/boards/shields/sweepro-ce/sweepro-ce_right.overlay b/app/boards/shields/sweepro-ce/sweepro-ce_right.overlay new file mode 100644 index 00000000000..4252c2c0b40 --- /dev/null +++ b/app/boards/shields/sweepro-ce/sweepro-ce_right.overlay @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include "sweepro-ce.dtsi" + +// adjust matrix transform for pepripheral split +&default_transform { + col-offset = <5>; +}; + +// setup kscan pins +&kscan_matrix { + col-gpios + = <&pro_micro 15 GPIO_ACTIVE_HIGH> + , <&pro_micro 18 GPIO_ACTIVE_HIGH> + , <&pro_micro 19 GPIO_ACTIVE_HIGH> + , <&pro_micro 20 GPIO_ACTIVE_HIGH> + , <&pro_micro 21 GPIO_ACTIVE_HIGH> + ; +}; + +&encoder_keys { + column-offset = <2>; +}; + + +// enable right encoder +&right_encoder { + status = "okay"; +}; \ No newline at end of file From 1634f5fb7d57e2442629d0ebc525004f1780b8f6 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Mon, 11 Jul 2022 21:26:20 +0800 Subject: [PATCH 031/157] set default transmit power to maximum --- app/boards/shields/36key-common-colemak.dtsi | 4 +++ app/boards/shields/36key-common.dtsi | 11 ++++--- .../shields/sweepro-ce/sweepro-ce.conf.bak | 29 ------------------- .../shields/sweepro-ce/sweepro-ce_left.conf | 2 ++ .../shields/sweepro-ce/sweepro-ce_right.conf | 5 +++- 5 files changed, 17 insertions(+), 34 deletions(-) delete mode 100644 app/boards/shields/sweepro-ce/sweepro-ce.conf.bak diff --git a/app/boards/shields/36key-common-colemak.dtsi b/app/boards/shields/36key-common-colemak.dtsi index 05f9ec74d7a..903b0493058 100644 --- a/app/boards/shields/36key-common-colemak.dtsi +++ b/app/boards/shields/36key-common-colemak.dtsi @@ -1,3 +1,7 @@ +#define DEFAULT 0 +#define QWERT 1 +#define NUM 2 +#define SYM 3 &mt { flavor = "tap-preferred"; diff --git a/app/boards/shields/36key-common.dtsi b/app/boards/shields/36key-common.dtsi index 79464629da8..714edab2f31 100644 --- a/app/boards/shields/36key-common.dtsi +++ b/app/boards/shields/36key-common.dtsi @@ -1,6 +1,5 @@ - #define DEFAULT 0 -#define QWERT 1 +#define COLEMAK 1 #define NUM 2 #define SYM 3 @@ -253,6 +252,7 @@ compatible = "zmk,keymap"; default_layer { + label = "QWRT"; bindings = < &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P &kp A &mt LSFT S < SYM D < NUM F &kp G &kp H < NUM J < SYM K &mt RSFT L &kp SQT @@ -262,7 +262,8 @@ sensor-bindings = <&inc_dec_kp RIGHT LEFT &inc_dec_kp UP DOWN>; }; - qwert_layer { + colemak_layer { + label = "CLMK"; bindings = < &kp Q &kp W &kp F &kp P &kp B &kp J &kp L &kp U &kp Y &kp SQT &kp A &mt LSFT R < SYM S < NUM T &kp G &kp M < NUM N < SYM E &mt RSFT I &kp O @@ -273,6 +274,7 @@ }; number_layer { + label = "NUM"; bindings = < &bt BT_NXT &none &kp UP &none &spc1 &kp KP_PLUS &kp KP_N7 &kp KP_N8 &kp KP_N9 &kp KP_MINUS &bt BT_PRV &kp LEFT &kp DOWN &kp RIGHT &spc2 &kp KP_MULTIPLY &kp KP_N4 &kp KP_N5 &kp KP_N6 &kp KP_DIVIDE @@ -283,8 +285,9 @@ }; symbol_layer { + label = "SYM"; bindings = < - &none &none &kp LT &kp GT &kp PRCNT &kp AMPS &kp CARET &kp LBRC &kp RBRC &tog QWERT + &none &none &kp LT &kp GT &kp PRCNT &kp AMPS &kp CARET &kp LBRC &kp RBRC &tog COLEMAK &kp GRAVE &kp EXCL &kp MINUS &kp PLUS &kp EQUAL &kp PIPE &kp COLON &kp LPAR &kp RPAR &kp AT &none &none &kp SLASH &kp STAR &kp BSLH &kp HASH &kp DLLR &kp LBKT &kp RBKT &none &encoder &none &none &none &none &encoder diff --git a/app/boards/shields/sweepro-ce/sweepro-ce.conf.bak b/app/boards/shields/sweepro-ce/sweepro-ce.conf.bak deleted file mode 100644 index f0d20263707..00000000000 --- a/app/boards/shields/sweepro-ce/sweepro-ce.conf.bak +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (c) 2020 The ZMK Contributors -# SPDX-License-Identifier: MIT - -# Enable epaper display -CONFIG_ZMK_DISPLAY=y -CONFIG_NRFMACRO_EPD_DISPLAY=y - -# diplay settings (NOTE: these configurations have to be explicitly set here in shield) -## color scheme: black/white -CONFIG_LVGL_USE_THEME_MONO=y -CONFIG_LVGL_THEME_DEFAULT_COLOR_PRIMARY_BLACK=y -CONFIG_LVGL_THEME_DEFAULT_COLOR_SECONDARY_WHITE=y -CONFIG_LVGL_THEME_DEFAULT_COLOR_PRIMARY_RED=n -CONFIG_LVGL_THEME_DEFAULT_COLOR_SECONDARY_RED=n -## dedicated work queue or not -CONFIG_ZMK_DISPLAY_WORK_QUEUE_DEDICATED=y -## automatically refresh the full screen in a constant interval (unit: second), useful for epd, see pr #969 -CONFIG_ZMK_DISPLAY_FULL_REFRESH_PERIOD=15 - -# Enable encoder support -CONFIG_EC11=y -CONFIG_EC11_TRIGGER_GLOBAL_THREAD=y - -# increase transmission power a little bit -CONFIG_BT_CTLR_TX_PWR_PLUS_8=y - -# auto-sleep after 15 mins -CONFIG_ZMK_SLEEP=y -CONFIG_ZMK_IDLE_SLEEP_TIMEOUT=900000 \ No newline at end of file diff --git a/app/boards/shields/sweepro-ce/sweepro-ce_left.conf b/app/boards/shields/sweepro-ce/sweepro-ce_left.conf index ad418ee8357..393eadd4b26 100644 --- a/app/boards/shields/sweepro-ce/sweepro-ce_left.conf +++ b/app/boards/shields/sweepro-ce/sweepro-ce_left.conf @@ -34,3 +34,5 @@ CONFIG_ZMK_WIDGET_LAYER_STATUS=n CONFIG_EC11=y CONFIG_EC11_TRIGGER_GLOBAL_THREAD=y +# set transmission power to max +CONFIG_BT_CTLR_TX_PWR_PLUS_8=y \ No newline at end of file diff --git a/app/boards/shields/sweepro-ce/sweepro-ce_right.conf b/app/boards/shields/sweepro-ce/sweepro-ce_right.conf index 33bb5bc07e2..3e39ded6878 100644 --- a/app/boards/shields/sweepro-ce/sweepro-ce_right.conf +++ b/app/boards/shields/sweepro-ce/sweepro-ce_right.conf @@ -35,4 +35,7 @@ CONFIG_NRFMACRO_SCREEN_CUSTOM_LOGO=y # Enable encoder support CONFIG_EC11=y -CONFIG_EC11_TRIGGER_GLOBAL_THREAD=y \ No newline at end of file +CONFIG_EC11_TRIGGER_GLOBAL_THREAD=y + +# set transmission power to max +CONFIG_BT_CTLR_TX_PWR_PLUS_8=y \ No newline at end of file From 29e60e88233937aadc1bcfa6e42986cbd29c9f6e Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Tue, 12 Jul 2022 12:41:21 +0800 Subject: [PATCH 032/157] cleanup logo icons --- .../epd_screen/slave/icons/eye_of_horus1.c | 85 ----------------- .../epd_screen/slave/icons/eye_of_horus2.c | 95 ------------------- .../shields/sweepro-ce/sweepro-ce_right.conf | 2 +- 3 files changed, 1 insertion(+), 181 deletions(-) delete mode 100644 app/boards/arm/nrfmacro/epd_screen/slave/icons/eye_of_horus1.c delete mode 100644 app/boards/arm/nrfmacro/epd_screen/slave/icons/eye_of_horus2.c diff --git a/app/boards/arm/nrfmacro/epd_screen/slave/icons/eye_of_horus1.c b/app/boards/arm/nrfmacro/epd_screen/slave/icons/eye_of_horus1.c deleted file mode 100644 index 455fbc75b47..00000000000 --- a/app/boards/arm/nrfmacro/epd_screen/slave/icons/eye_of_horus1.c +++ /dev/null @@ -1,85 +0,0 @@ -#include "lvgl.h" - -#ifndef LV_ATTRIBUTE_MEM_ALIGN -#define LV_ATTRIBUTE_MEM_ALIGN -#endif - -#ifndef LV_ATTRIBUTE_IMG_STDLOGO -#define LV_ATTRIBUTE_IMG_STDLOGO -#endif - -const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_STDLOGO uint8_t stdlogo_map[] = { - 0x00, 0x00, 0x00, 0x04, /*Color of index 0*/ - 0x00, 0x00, 0x00, 0xed, /*Color of index 1*/ - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xfe, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x7f, 0xff, 0xfc, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xc0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, - 0x00, 0x00, 0x00, 0x03, 0xff, 0xd0, 0x01, 0x7f, 0xfc, 0x00, - 0x00, 0x00, 0x00, 0x1f, 0xfc, 0x00, 0x00, 0x03, 0xff, 0x00, - 0x00, 0x00, 0x00, 0x3f, 0xe0, 0x00, 0x00, 0x00, 0x3f, 0x80, - 0x00, 0x00, 0x01, 0xff, 0x00, 0x00, 0x00, 0x00, 0x07, 0x80, - 0x00, 0x00, 0x0f, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x00, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0xbf, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1f, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1f, 0xff, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x05, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x5f, 0xc0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xfc, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0x80, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xc0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7f, 0x0f, 0xff, 0xfb, 0xe0, 0x00, - 0x00, 0x00, 0x00, 0x03, 0xf8, 0x1f, 0xff, 0xf9, 0xf0, 0x00, - 0x00, 0x00, 0x00, 0x5f, 0xf0, 0x0f, 0xff, 0xf8, 0xf8, 0x00, - 0x00, 0x00, 0x2f, 0xff, 0xe0, 0x0f, 0xff, 0xf8, 0x7c, 0x00, - 0x07, 0xff, 0xff, 0xff, 0xf0, 0x0f, 0xff, 0xf8, 0x3e, 0x00, - 0x0f, 0xff, 0xff, 0xff, 0xfc, 0x0f, 0xff, 0xf0, 0x1e, 0x00, - 0x0f, 0xfa, 0x80, 0x02, 0xff, 0x8f, 0xff, 0xf0, 0x1f, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x1f, 0xf7, 0xff, 0xe0, 0x0f, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xea, 0xbf, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xc0, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbf, 0xff, 0x80, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x3f, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf8, 0x3f, 0x00, 0x00, - 0x01, 0xc0, 0x00, 0x00, 0x00, 0x07, 0xf0, 0x3f, 0x00, 0x00, - 0x07, 0xf0, 0x00, 0x00, 0x00, 0x0f, 0xe0, 0x3f, 0x00, 0x00, - 0x0e, 0x30, 0x00, 0x00, 0x00, 0x3f, 0x80, 0x3f, 0x80, 0x00, - 0x0c, 0x18, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x3f, 0x80, 0x00, - 0x19, 0x08, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x3f, 0x80, 0x00, - 0x19, 0x10, 0x00, 0x00, 0x03, 0xf8, 0x00, 0x3f, 0x80, 0x00, - 0x19, 0x10, 0x00, 0x00, 0x07, 0xf0, 0x00, 0x1f, 0x80, 0x00, - 0x19, 0xe0, 0x00, 0x00, 0x1f, 0xc0, 0x00, 0x1f, 0x80, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x7f, 0x80, 0x00, 0x1f, 0x00, 0x00, - 0x0c, 0x00, 0x00, 0x01, 0xfe, 0x00, 0x00, 0x1f, 0x00, 0x00, - 0x0e, 0x00, 0x00, 0x07, 0xfc, 0x00, 0x00, 0x1f, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00, 0x1e, 0x00, 0x00, - 0x03, 0x80, 0x00, 0x7f, 0xc0, 0x00, 0x00, 0x1e, 0x00, 0x00, - 0x01, 0xf0, 0x07, 0xff, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, - 0x00, 0x7f, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, - 0x00, 0x1f, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, - 0x00, 0x02, 0xfa, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -}; - -const lv_img_dsc_t stdlogo = { - .header.cf = LV_IMG_CF_INDEXED_1BIT, - .header.always_zero = 0, - .header.reserved = 0, - .header.w = 78, - .header.h = 60, - .data_size = 608, - .data = stdlogo_map, -}; diff --git a/app/boards/arm/nrfmacro/epd_screen/slave/icons/eye_of_horus2.c b/app/boards/arm/nrfmacro/epd_screen/slave/icons/eye_of_horus2.c deleted file mode 100644 index a04d632acdb..00000000000 --- a/app/boards/arm/nrfmacro/epd_screen/slave/icons/eye_of_horus2.c +++ /dev/null @@ -1,95 +0,0 @@ -#include "lvgl.h" - -#ifndef LV_ATTRIBUTE_MEM_ALIGN -#define LV_ATTRIBUTE_MEM_ALIGN -#endif - -#ifndef LV_ATTRIBUTE_IMG_STDLOGO -#define LV_ATTRIBUTE_IMG_STDLOGO -#endif - -const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_STDLOGO uint8_t stdlogo_map[] = { - 0x00, 0x00, 0x00, 0x04, /*Color of index 0*/ - 0x00, 0x00, 0x00, 0xef, /*Color of index 1*/ - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x05, 0xff, 0xf8, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0xff, 0xea, 0xff, 0xc0, 0x00, 0x00, - 0x00, 0x00, 0x0f, 0xf8, 0x00, 0x0f, 0xf0, 0x00, 0x00, - 0x00, 0x00, 0x3f, 0xc0, 0x00, 0x01, 0xfc, 0x00, 0x00, - 0x00, 0x01, 0xfe, 0x01, 0x7d, 0x00, 0x3f, 0x00, 0x00, - 0x00, 0x07, 0xf0, 0x1f, 0xff, 0xf0, 0x0f, 0xe0, 0x00, - 0x00, 0x3f, 0xc0, 0x7f, 0xff, 0xfe, 0x03, 0xfa, 0x20, - 0x15, 0xff, 0x03, 0xff, 0xff, 0xff, 0xc0, 0xff, 0xe0, - 0x1f, 0xf8, 0x0f, 0xff, 0xff, 0xff, 0xf0, 0x1f, 0x80, - 0x07, 0xe0, 0x7f, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, - 0x00, 0x01, 0xff, 0x7f, 0xff, 0xfc, 0x7f, 0x00, 0x00, - 0x00, 0x0f, 0xf8, 0x3f, 0xff, 0xf8, 0x1f, 0xe0, 0x00, - 0x01, 0xff, 0xe0, 0x1f, 0xff, 0xf8, 0x07, 0xfe, 0x00, - 0x01, 0xff, 0xc0, 0x1f, 0xff, 0xf0, 0x01, 0xfc, 0x00, - 0x00, 0x7f, 0xe0, 0x0f, 0xff, 0xe0, 0x03, 0xf0, 0x00, - 0x00, 0x7b, 0xf8, 0x07, 0xff, 0xc0, 0x0f, 0xc0, 0x00, - 0x00, 0x00, 0xfe, 0x01, 0xff, 0x00, 0x3f, 0x80, 0x00, - 0x00, 0x00, 0x3f, 0x80, 0x00, 0x01, 0xfe, 0x00, 0x00, - 0x00, 0x00, 0x0f, 0xe0, 0x00, 0x07, 0xf8, 0x00, 0x00, - 0x00, 0x00, 0x03, 0xfc, 0x00, 0xbf, 0xf0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xe0, 0x00, 0x00, - 0x00, 0x1f, 0xc0, 0x07, 0xff, 0xff, 0xc0, 0x00, 0x00, - 0x00, 0x3f, 0xf0, 0x00, 0x00, 0xff, 0xc0, 0x00, 0x00, - 0x00, 0xff, 0xf0, 0x00, 0x01, 0xff, 0xc0, 0x00, 0x00, - 0x00, 0xff, 0xf8, 0x00, 0x03, 0xff, 0xc0, 0x00, 0x00, - 0x01, 0xff, 0xfc, 0x00, 0x07, 0xef, 0xc0, 0x00, 0x00, - 0x01, 0xff, 0xfc, 0x00, 0x0f, 0xdf, 0xc0, 0x00, 0x00, - 0x03, 0xef, 0xfc, 0x00, 0x0f, 0x9f, 0xe0, 0x00, 0x00, - 0x03, 0xef, 0xfc, 0x00, 0x1f, 0x1f, 0xe0, 0x00, 0x00, - 0x03, 0xe7, 0xfc, 0x00, 0x3e, 0x1f, 0xf0, 0x00, 0x00, - 0x03, 0xc7, 0xf8, 0x00, 0x7c, 0x1f, 0xf8, 0x00, 0x00, - 0x03, 0xe3, 0xf8, 0x00, 0xf8, 0x1f, 0xfc, 0x00, 0x00, - 0x01, 0xe1, 0xe0, 0x01, 0xf8, 0x1f, 0xfc, 0x00, 0x00, - 0x01, 0xe0, 0x00, 0x07, 0xe0, 0x1f, 0xf8, 0x00, 0x00, - 0x01, 0xf0, 0x00, 0x0f, 0xe0, 0x1f, 0xf0, 0x00, 0x00, - 0x00, 0xfc, 0x00, 0x3f, 0x80, 0x1f, 0xe0, 0x00, 0x00, - 0x00, 0x7f, 0x00, 0xff, 0x00, 0x0f, 0xc0, 0x00, 0x00, - 0x00, 0x7f, 0xff, 0xfe, 0x00, 0x0f, 0x80, 0x00, 0x00, - 0x00, 0x1f, 0xff, 0xfc, 0x00, 0x0f, 0x00, 0x00, 0x00, - 0x00, 0x0f, 0xff, 0xf0, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x00, 0x03, 0xff, 0xc0, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -}; - -const lv_img_dsc_t stdlogo = { - .header.cf = LV_IMG_CF_INDEXED_1BIT, - .header.always_zero = 0, - .header.reserved = 0, - .header.w = 70, - .header.h = 70, - .data_size = 638, - .data = stdlogo_map, -}; diff --git a/app/boards/shields/sweepro-ce/sweepro-ce_right.conf b/app/boards/shields/sweepro-ce/sweepro-ce_right.conf index 3e39ded6878..6d1fbd4d24b 100644 --- a/app/boards/shields/sweepro-ce/sweepro-ce_right.conf +++ b/app/boards/shields/sweepro-ce/sweepro-ce_right.conf @@ -31,7 +31,7 @@ CONFIG_NRFMACRO_SCREEN_MARK_LOGO=y # use custom logo or not # CONFIG_NRFMACRO_SCREEN_STANDARD_LOGO=y -CONFIG_NRFMACRO_SCREEN_CUSTOM_LOGO=y +# CONFIG_NRFMACRO_SCREEN_CUSTOM_LOGO=y # Enable encoder support CONFIG_EC11=y From 361054f4adb8ab90d02fc72a1968af3b9d212bf0 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Tue, 12 Jul 2022 16:20:46 +0800 Subject: [PATCH 033/157] [Add] support for custom logo icon to display on the slave split How to enable it: 1. enable CONFIG_NRFMACRO_SCREEN_CUSTOM_LOGO in user's zmk_config 2. use the online image converter to convert the logo image to a C source file with designator 'customlogo' (https://lvgl.io/tools/imageconverter) The logo image can be downloaded or created with bitmap format (.png). The available space for custom logo is about 70x70. IMPORTATNT: The filename of the converted logo source file must be 'customlogo.c'. This is the only signature recognized by the build system. --- app/boards/arm/nrfmacro/epd_screen/slave/CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/boards/arm/nrfmacro/epd_screen/slave/CMakeLists.txt b/app/boards/arm/nrfmacro/epd_screen/slave/CMakeLists.txt index 4db91eff76c..73f712d748e 100644 --- a/app/boards/arm/nrfmacro/epd_screen/slave/CMakeLists.txt +++ b/app/boards/arm/nrfmacro/epd_screen/slave/CMakeLists.txt @@ -23,6 +23,10 @@ zephyr_library_sources(icons/stdlogo.c) endif() if (CONFIG_NRFMACRO_SCREEN_CUSTOM_LOGO) -zephyr_library_sources(icons/customlogo.c) +if(ZMK_CONFIG) + zephyr_library_sources(${ZMK_CONFIG}/icons/customlogo.c) +else() + zephyr_library_sources(icons/customlogo.c) +endif() endif() From 5635300e63fd2ce8987ffc755db58f764d826c83 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Tue, 12 Jul 2022 18:18:32 +0800 Subject: [PATCH 034/157] bugfix --- app/boards/arm/nrfmacro/epd_screen/slave/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/boards/arm/nrfmacro/epd_screen/slave/CMakeLists.txt b/app/boards/arm/nrfmacro/epd_screen/slave/CMakeLists.txt index 73f712d748e..cdc1ef61c7b 100644 --- a/app/boards/arm/nrfmacro/epd_screen/slave/CMakeLists.txt +++ b/app/boards/arm/nrfmacro/epd_screen/slave/CMakeLists.txt @@ -23,6 +23,7 @@ zephyr_library_sources(icons/stdlogo.c) endif() if (CONFIG_NRFMACRO_SCREEN_CUSTOM_LOGO) +add_compile_definitions(LV_LVGL_H_INCLUDE_SIMPLE) if(ZMK_CONFIG) zephyr_library_sources(${ZMK_CONFIG}/icons/customlogo.c) else() From b84bebf881e706846b5a766844bc9d1a4ab0a67c Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Tue, 12 Jul 2022 22:06:56 +0800 Subject: [PATCH 035/157] add modified version of stdlogo --- .../epd_screen/slave/icons/pictures/stdlogo.png | Bin 0 -> 1063 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 app/boards/arm/nrfmacro/epd_screen/slave/icons/pictures/stdlogo.png diff --git a/app/boards/arm/nrfmacro/epd_screen/slave/icons/pictures/stdlogo.png b/app/boards/arm/nrfmacro/epd_screen/slave/icons/pictures/stdlogo.png new file mode 100644 index 0000000000000000000000000000000000000000..881749d7610e349539408a0341661f2c5c09f174 GIT binary patch literal 1063 zcmV+?1laqDP)4|UjP6A8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H11EfhrK~z|U<=1Vj*JBt5@K497P_4(4P;}x2H4HOpETuG=hZoFK z%x05$T5RS8BTr+7Hw>GY8Q$2~m^KeHY96wM6j7d;^`Hmlq|)hl@xA}+->-k2=ycNh zzB%WD3gtqc*BwoY2_y9}M(bPS0 zcR7aR8uYHS{e^EZ8OP&0wBrwaj!T7{B|^w=cn8xl1528_Dfq6&MOcqJ>TH{^RuU(b zu^Ah%rr7&nrI4|%_$(CCR_?82oR0Ufv10FoYtggL)`1pWjRPfdLR3B4t7P<-GMk9c zguUl?G4QTN4~)Y-75jb|fV0t0^q~#CFhp`1QhZ>4)bj6v0ky?g+=ubyqwqa`zzf3e z{{>?(2ya#Fz2vmdt=KQeGF&f}vrLj7Sk?nE7^9jnN=mK;SK*-IJWAN?N+L$!e!Pk) z+wmEOyG8BKYw&z3e#Ms+d$XK&6+MM0l2_mIw>QqjJiJ-#CJUnvyF50>k}mP2lqLmw&moKoz_^_Y#n zrSIN`6Uxi=cwMSU`QR}PHa`(xS8O*3;j5*mFR$Oc;7TFscYIZRtHj?VB>gCate4h# zVS_k!8N+ZRK9cM43ucN6lY6&NN^h>Hd6lr2gtQvYC<$~32~Ws%smEApfgRHNX5om6 z&$RNq9Yc$)pH$CA+mhR*7=uSdk>?cKo#p>qB=<+5Q!5!UDXA z)*Zw%Lk6i2EA{~;#>+ZOs4H-*lw)r^gIA#PQg#Mo)oJXCmPXr zCt_KE+w0b&Js!kHcJwV#idg;gW z%7(R`-lO=>D$m=cBA&zHG8xt<^)?>BCneZkqVA)G*rlQ|Q%kao Date: Wed, 13 Jul 2022 07:29:27 +0800 Subject: [PATCH 036/157] bugfix in keymap --- app/boards/shields/36key-common-colemak.dtsi | 12 ++++++------ app/boards/shields/36key-common.dtsi | 4 ++-- app/boards/shields/key-common.dtsi | 6 ++++++ 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/app/boards/shields/36key-common-colemak.dtsi b/app/boards/shields/36key-common-colemak.dtsi index 903b0493058..ae68e7c5fd0 100644 --- a/app/boards/shields/36key-common-colemak.dtsi +++ b/app/boards/shields/36key-common-colemak.dtsi @@ -19,7 +19,7 @@ bindings = < &kp Q &kp W &kp F &kp P &kp B &kp J &kp L &kp U &kp Y &kp SQT &kp A &mt LSFT R &mt LALT S &mt LCTL T < NUM G < NUM M &mt RCTL N &mt RALT E &mt RSFT I &kp O - &kp Z &kp X &kp C &kp D &kp V &kp K &kp H &cmqus &dtsmi &kp RC(B) + &kp Z &kp X &kp C &kp D &kp V &kp K &kp H &cmqus &dtsmi &kp RC(B) &encoder &kp TAB < SYM SPACE < SYM RET &kp BSPC &encoder >; sensor-bindings = <&inc_dec_kp RIGHT LEFT &inc_dec_kp UP DOWN>; @@ -28,10 +28,10 @@ qwert_layer { label = "QWRT"; bindings = < - &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P - &kp A &mt LSFT S < SYM D < NUM F &kp G &kp H < NUM J < SYM K &mt RSFT L &kp SQT - &kp Z &kp X &kp C &kp V &kp B &kp N &kp M &cmqus &dtsmi &kp RC(B) - &encoder &mt LALT TAB &mt LCTL SPACE &mt RCTL RET &mt RALT C &encoder + &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P + &kp A &mt LSFT S &mt LALT D &mt LCTL T < NUM G < NUM H &mt RCTL J &mt RALT K &mt RSFT L &kp SQT + &kp Z &kp X &kp C &kp V &kp B &kp N &kp M &cmqus &dtsmi &kp RC(B) + &encoder &kp TAB < SYM SPACE < SYM RET &kp BSPC &encoder >; sensor-bindings = <&inc_dec_kp RIGHT LEFT &inc_dec_kp UP DOWN>; }; @@ -42,7 +42,7 @@ &bt BT_NXT &prv_tab &kp UP &nxt_tab &spc1 &kp KP_PLUS &kp KP_N7 &kp KP_N8 &kp KP_N9 &kp KP_MINUS &bt BT_PRV &kp LEFT &kp DOWN &kp RIGHT &spc2 &kp KP_MULTIPLY &kp KP_N4 &kp KP_N5 &kp KP_N6 &kp KP_DIVIDE &bt BT_CLR &none &none &none &spc3 &kp KP_EQUAL &kp KP_N1 &kp KP_N2 &kp KP_N3 &kp KP_DOT - &encoder &none &kp BSPC &kp RET &kp KP_N0 &encoder + &encoder &spc4 &kp BSPC &kp RET &kp KP_N0 &encoder >; sensor-bindings = <&inc_dec_kp PG_UP PG_DN &inc_dec_kp C_VOL_UP C_VOL_DN>; }; diff --git a/app/boards/shields/36key-common.dtsi b/app/boards/shields/36key-common.dtsi index 714edab2f31..fc6bec7fe21 100644 --- a/app/boards/shields/36key-common.dtsi +++ b/app/boards/shields/36key-common.dtsi @@ -257,7 +257,7 @@ &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P &kp A &mt LSFT S < SYM D < NUM F &kp G &kp H < NUM J < SYM K &mt RSFT L &kp SQT &kp Z &kp X &kp C &kp V &kp B &kp N &kp M &cmqus &dtild &kp SEMI - &encoder &mt LALT TAB &mt LCTL SPACE &mt RCTL RET &mt RALT C &encoder + &encoder &mt LALT TAB &mt LCTL SPACE &mt RCTL RET &mt RALT BSPC &encoder >; sensor-bindings = <&inc_dec_kp RIGHT LEFT &inc_dec_kp UP DOWN>; }; @@ -279,7 +279,7 @@ &bt BT_NXT &none &kp UP &none &spc1 &kp KP_PLUS &kp KP_N7 &kp KP_N8 &kp KP_N9 &kp KP_MINUS &bt BT_PRV &kp LEFT &kp DOWN &kp RIGHT &spc2 &kp KP_MULTIPLY &kp KP_N4 &kp KP_N5 &kp KP_N6 &kp KP_DIVIDE &bt BT_CLR &none &none &none &spc3 &kp KP_EQUAL &kp KP_N1 &kp KP_N2 &kp KP_N3 &kp KP_DOT - &encoder &none &kp BSPC &kp RET &kp KP_N0 &encoder + &encoder &spc4 &kp BSPC &kp RET &kp KP_N0 &encoder >; sensor-bindings = <&inc_dec_kp PG_UP PG_DN &inc_dec_kp C_VOL_UP C_VOL_DN>; }; diff --git a/app/boards/shields/key-common.dtsi b/app/boards/shields/key-common.dtsi index 003098f6a42..376e48a2849 100644 --- a/app/boards/shields/key-common.dtsi +++ b/app/boards/shields/key-common.dtsi @@ -38,6 +38,12 @@ bindings = <&kp LA(M) &kp N3>; ) + ZMK_MACRO(spc4, + wait-ms = <0>; + tap-ms = <10>; + bindings = <&kp LA(M) &kp N4>; + ) + ZMK_MACRO(nxt_tab, wait-ms = <0>; tap-ms = <10>; From 55db750b7d635e4462ae244332124b7f0e163c4c Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Sat, 16 Jul 2022 16:45:04 +0800 Subject: [PATCH 037/157] add epd rotation config option --- app/boards/arm/nrfmacro/Kconfig.defconfig | 3 +++ app/boards/shields/sweepro-ce/sweepro-ce_left.conf | 1 + app/boards/shields/sweepro-ce/sweepro-ce_right.conf | 1 + app/drivers/display/il0323.c | 8 ++++++-- 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/app/boards/arm/nrfmacro/Kconfig.defconfig b/app/boards/arm/nrfmacro/Kconfig.defconfig index 627f3594421..c4979bec98d 100644 --- a/app/boards/arm/nrfmacro/Kconfig.defconfig +++ b/app/boards/arm/nrfmacro/Kconfig.defconfig @@ -163,6 +163,9 @@ endif # NRFMACRO_OLED_DISPLAY ## epd display configuration if NRFMACRO_EPD_DISPLAY +config NRFMACRO_EPD_ROTATE_180 + bool "rotate the epd upside-down" + config LVGL_HOR_RES_MAX default 80 diff --git a/app/boards/shields/sweepro-ce/sweepro-ce_left.conf b/app/boards/shields/sweepro-ce/sweepro-ce_left.conf index 393eadd4b26..015059a2a62 100644 --- a/app/boards/shields/sweepro-ce/sweepro-ce_left.conf +++ b/app/boards/shields/sweepro-ce/sweepro-ce_left.conf @@ -7,6 +7,7 @@ CONFIG_NRFMACRO_SHIELD_MASTER=y # Enable epaper display CONFIG_ZMK_DISPLAY=y CONFIG_NRFMACRO_EPD_DISPLAY=y +CONFIG_NRFMACRO_EPD_ROTATE_180=y CONFIG_MYCHANGE_DISPLAY_UPDATE_MO_BEHAVIOR=n # diplay settings (NOTE: these configurations have to be explicitly set here in shield) diff --git a/app/boards/shields/sweepro-ce/sweepro-ce_right.conf b/app/boards/shields/sweepro-ce/sweepro-ce_right.conf index 6d1fbd4d24b..e3d13aca048 100644 --- a/app/boards/shields/sweepro-ce/sweepro-ce_right.conf +++ b/app/boards/shields/sweepro-ce/sweepro-ce_right.conf @@ -7,6 +7,7 @@ CONFIG_NRFMACRO_SHIELD_SLAVE=y # Enable epaper display CONFIG_ZMK_DISPLAY=y CONFIG_NRFMACRO_EPD_DISPLAY=y +CONFIG_NRFMACRO_EPD_ROTATE_180=y # diplay settings (NOTE: these configurations have to be explicitly set here in shield) ## color scheme: black/white diff --git a/app/drivers/display/il0323.c b/app/drivers/display/il0323.c index c48e393a2cd..452f88ff32a 100644 --- a/app/drivers/display/il0323.c +++ b/app/drivers/display/il0323.c @@ -304,10 +304,14 @@ static int il0323_controller_init(const struct device *dev) { il0323_busy_wait(driver); /* Pannel settings, KW mode */ - // scanning in from left -> right, up -> down - /* tmp[0] = IL0323_PSR_UD | IL0323_PSR_SHL | IL0323_PSR_SHD | IL0323_PSR_RST; */ +#if IS_ENABLED(CONFIG_NRFMACRO_EPD_ROTATE_180) // rotate the screen upside down by scanning in from right -> left, down -> up tmp[0] = IL0323_PSR_SHD | IL0323_PSR_RST; +#else + // scanning in from left -> right, up -> down + tmp[0] = IL0323_PSR_UD | IL0323_PSR_SHL | IL0323_PSR_SHD | IL0323_PSR_RST; +#endif + #if EPD_PANEL_WIDTH == 80 #if EPD_PANEL_HEIGHT == 128 From bf03cb9f2e3ec4556aad9280e8b973c4b8569a7a Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Sun, 17 Jul 2022 09:15:37 +0800 Subject: [PATCH 038/157] backup --- .../shields/corne-pro/Kconfig.defconfig | 29 +++++++ app/boards/shields/corne-pro/Kconfig.shield | 8 ++ app/boards/shields/corne-pro/README.md | 35 +++++++++ app/boards/shields/corne-pro/corne-pro.dtsi | 76 +++++++++++++++++++ app/boards/shields/corne-pro/corne-pro.keymap | 13 ++++ .../shields/corne-pro/corne-pro.zmk.yml | 12 +++ .../shields/corne-pro/corne-pro_left.conf | 37 +++++++++ .../shields/corne-pro/corne-pro_left.overlay | 19 +++++ .../shields/corne-pro/corne-pro_right.conf | 40 ++++++++++ .../shields/corne-pro/corne-pro_right.overlay | 23 ++++++ 10 files changed, 292 insertions(+) create mode 100644 app/boards/shields/corne-pro/Kconfig.defconfig create mode 100644 app/boards/shields/corne-pro/Kconfig.shield create mode 100644 app/boards/shields/corne-pro/README.md create mode 100644 app/boards/shields/corne-pro/corne-pro.dtsi create mode 100644 app/boards/shields/corne-pro/corne-pro.keymap create mode 100644 app/boards/shields/corne-pro/corne-pro.zmk.yml create mode 100644 app/boards/shields/corne-pro/corne-pro_left.conf create mode 100644 app/boards/shields/corne-pro/corne-pro_left.overlay create mode 100644 app/boards/shields/corne-pro/corne-pro_right.conf create mode 100644 app/boards/shields/corne-pro/corne-pro_right.overlay diff --git a/app/boards/shields/corne-pro/Kconfig.defconfig b/app/boards/shields/corne-pro/Kconfig.defconfig new file mode 100644 index 00000000000..62e6b265050 --- /dev/null +++ b/app/boards/shields/corne-pro/Kconfig.defconfig @@ -0,0 +1,29 @@ +# Copyright (c) 2020 The ZMK Contributors +# SPDX-License-Identifier: MIT + +if CORNE_PRO_LEFT + +config ZMK_KEYBOARD_NAME + default "Corne-Pro" + +config ZMK_SPLIT_BLE_ROLE_CENTRAL + default y + +endif + +if CORNE_PRO_RIGHT +config ZMK_KEYBOARD_NAME + default "Corne-Pro-R" + +endif + +if CORNE_PRO_LEFT || CORNE_PRO_RIGHT + +config ZMK_SPLIT + default y + +config ZMK_USB + default y + +endif + diff --git a/app/boards/shields/corne-pro/Kconfig.shield b/app/boards/shields/corne-pro/Kconfig.shield new file mode 100644 index 00000000000..46cd12da417 --- /dev/null +++ b/app/boards/shields/corne-pro/Kconfig.shield @@ -0,0 +1,8 @@ +# Copyright (c) 2020 The ZMK Contributors +# SPDX-License-Identifier: MIT + +config CORNE_PRO_LEFT + def_bool $(shields_list_contains,corne-pro_left) + +config CORNE_PRO_RIGHT + def_bool $(shields_list_contains,corne-pro_right) diff --git a/app/boards/shields/corne-pro/README.md b/app/boards/shields/corne-pro/README.md new file mode 100644 index 00000000000..b1344293d2a --- /dev/null +++ b/app/boards/shields/corne-pro/README.md @@ -0,0 +1,35 @@ +# Cradio + +Cradio is a firmware for a few 34 key keyboards, including Cradio, Hypergolic and Sweep. + +## Pin arrangement + +Some revisions of the aforementioned PCBs have slightly different pin arrangements compared to what's defined in [`cradio.dtsi`](./cradio.dtsi). If you need to swap a few keys for your particular PCB, you can easily reorder the `input-gpio` definition in your own keymap file (i.e. in `zmk-config/config/cradio.keymap`): + +```dts +/* Adjusted Cradio pin arrangement */ +/* The position of Q and B keys have been swapped */ +&kscan0 { + input-gpios + = <&pro_micro_d 6 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_a 0 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_a 1 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_a 2 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_a 3 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_d 15 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_d 14 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_d 16 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_d 10 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_d 1 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_d 2 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_d 3 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_d 4 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_d 5 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_d 7 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_d 8 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_d 9 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + ; +}; +``` + +This `&kscan0` block must be placed outside of any blocks surrounded by curly braces (`{...}`). diff --git a/app/boards/shields/corne-pro/corne-pro.dtsi b/app/boards/shields/corne-pro/corne-pro.dtsi new file mode 100644 index 00000000000..e2d341d8ac2 --- /dev/null +++ b/app/boards/shields/corne-pro/corne-pro.dtsi @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include + +/ { + + chosen: chosen { + zmk,kscan = &kscan; + zmk,matrix_transform = &default_transform; + zephyr,display = &shield_epd; + }; + + default_transform: keymap_transform_0 { + compatible = "zmk,matrix-transform"; + columns = <12>; + rows = <4>; + + // | SW1 | SW2 | SW3 | SW4 | SW5 | SW6 | | SW6 | SW5 | SW4 | SW3 | SW2 | SW1 | + // | SW7 | SW8 | SW9 | SW10 | SW11 | SW12 | | SW12 | SW11 | SW10 | SW9 | SW8 | SW7 | + // | SW13 | SW14 | SW15 | SW16 | SW17 | SW18 | | SW18 | SW17 | SW16 | SW15 | SW14 | SW13 | + // | SW19 | SW20 | SW21 | | SW21 | SW20 | SW19 | + map = < + RC(0,0) RC(0,1) RC(0,2) RC(0,3) RC(0,4) RC(0,5) RC(0,6) RC(0,7) RC(0,8) RC(0,9) RC(0,10) RC(0,11) + RC(1,0) RC(1,1) RC(1,2) RC(1,3) RC(1,4) RC(1,5) RC(1,6) RC(1,7) RC(1,8) RC(1,9) RC(1,10) RC(1,11) + RC(2,0) RC(2,1) RC(2,2) RC(2,3) RC(2,4) RC(2,5) RC(2,6) RC(2,7) RC(2,8) RC(2,9) RC(2,10) RC(2,11) + RC(3,3) RC(3,4) RC(3,5) RC(3,6) RC(3,7) RC(3,8) + >; + }; + + kscan: kscan { + compatible = "zmk,kscan-gpio-matrix"; + label = "KSCAN"; + + diode-direction = "col2row"; + row-gpios + = <&pro_micro 4 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 5 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 15 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 10 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + ; + }; +}; + +/* display */ +&pro_micro_spi { + compatible = "nordic,nrf-spim"; + sck-pin = <17>; + mosi-pin = <15>; + cs-gpios = <&gpio0 6 GPIO_ACTIVE_LOW>; + // not used, but required by spim binding; can be reuesd in the keymap for normal key press + miso-pin = <22>; + + // activate this device + status = "okay"; + + // spi-device: gooddisplay GDEW0102T4 + shield_epd: il0323@0 { + compatible = "gooddisplay,il0323"; + reg = <0>; + label = "DISPLAY"; + spi-max-frequency = <4000000>; + + height = <128>; + width = <80>; + reset-gpios = <&gpio0 30 GPIO_ACTIVE_LOW>; + dc-gpios = <&gpio0 8 GPIO_ACTIVE_LOW>; + busy-gpios = <&gpio0 31 GPIO_ACTIVE_LOW>; + pwr = [03 00 26 26]; + cdi = <0xd2>; + tcon = <0x22>; + }; +}; diff --git a/app/boards/shields/corne-pro/corne-pro.keymap b/app/boards/shields/corne-pro/corne-pro.keymap new file mode 100644 index 00000000000..c46badd8b4f --- /dev/null +++ b/app/boards/shields/corne-pro/corne-pro.keymap @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include +#include + +// #include "../42key-common.dtsi" +#include "../42key-common-alternative.dtsi" \ No newline at end of file diff --git a/app/boards/shields/corne-pro/corne-pro.zmk.yml b/app/boards/shields/corne-pro/corne-pro.zmk.yml new file mode 100644 index 00000000000..4e35c7c330e --- /dev/null +++ b/app/boards/shields/corne-pro/corne-pro.zmk.yml @@ -0,0 +1,12 @@ +file_format: "1" +id: corne-pro +name: Corne-Pro +type: shield +url: https://github.com/ufan/mykeyboard +requires: [pro_micro] +exposes: [i2c_oled] +features: + - keys: +siblings: + - corne-pro_left + - corne-pro_right diff --git a/app/boards/shields/corne-pro/corne-pro_left.conf b/app/boards/shields/corne-pro/corne-pro_left.conf new file mode 100644 index 00000000000..1b2997a491e --- /dev/null +++ b/app/boards/shields/corne-pro/corne-pro_left.conf @@ -0,0 +1,37 @@ +# Copyright (c) 2020 The ZMK Contributors +# SPDX-License-Identifier: MIT + +# Set the role of the split, used in choosing the dedicated status screen +CONFIG_NRFMACRO_SHIELD_MASTER=y + +# Enable epaper display +CONFIG_ZMK_DISPLAY=y +CONFIG_NRFMACRO_EPD_DISPLAY=y +CONFIG_MYCHANGE_DISPLAY_UPDATE_MO_BEHAVIOR=n + +# diplay settings (NOTE: these configurations have to be explicitly set here in shield) +## color scheme: black/white +CONFIG_LVGL_USE_THEME_MONO=y +CONFIG_LVGL_THEME_DEFAULT_COLOR_PRIMARY_BLACK=y +CONFIG_LVGL_THEME_DEFAULT_COLOR_SECONDARY_WHITE=y +CONFIG_LVGL_THEME_DEFAULT_COLOR_PRIMARY_RED=n +CONFIG_LVGL_THEME_DEFAULT_COLOR_SECONDARY_RED=n +## dedicated work queue or not +CONFIG_ZMK_DISPLAY_WORK_QUEUE_DEDICATED=y + +## custom screen or not +### uncommet following for custom status screen +CONFIG_ZMK_DISPLAY_STATUS_SCREEN_CUSTOM=y +CONFIG_LVGL_THEME_DEFAULT_FONT_NORMAL_MONTSERRAT_16=y +CONFIG_CUSTOM_WIDGET_BATTERY_STATUS=y +CONFIG_ZMK_WIDGET_BATTERY_STATUS=n +CONFIG_CUSTOM_WIDGET_OUTPUT_STATUS=y +CONFIG_ZMK_WIDGET_OUTPUT_STATUS=n +CONFIG_CUSTOM_WIDGET_LAYER_STATUS=y +CONFIG_ZMK_WIDGET_LAYER_STATUS=n + +# enable usb logging +# CONFIG_ZMK_USB_LOGGING=y + +# increase transmission power to max +CONFIG_BT_CTLR_TX_PWR_PLUS_8=y diff --git a/app/boards/shields/corne-pro/corne-pro_left.overlay b/app/boards/shields/corne-pro/corne-pro_left.overlay new file mode 100644 index 00000000000..382ddcd39bc --- /dev/null +++ b/app/boards/shields/corne-pro/corne-pro_left.overlay @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include "corne-pro.dtsi" + +// setup kscan pins +&kscan { + col-gpios + = <&pro_micro 6 GPIO_ACTIVE_HIGH> + , <&pro_micro 7 GPIO_ACTIVE_HIGH> + , <&pro_micro 8 GPIO_ACTIVE_HIGH> + , <&pro_micro 9 GPIO_ACTIVE_HIGH> + , <&pro_micro 16 GPIO_ACTIVE_HIGH> + , <&pro_micro 14 GPIO_ACTIVE_HIGH> + ; +}; \ No newline at end of file diff --git a/app/boards/shields/corne-pro/corne-pro_right.conf b/app/boards/shields/corne-pro/corne-pro_right.conf new file mode 100644 index 00000000000..a1127079a62 --- /dev/null +++ b/app/boards/shields/corne-pro/corne-pro_right.conf @@ -0,0 +1,40 @@ +# Copyright (c) 2020 The ZMK Contributors +# SPDX-License-Identifier: MIT + +# Set the role of the split, used in choosing the dedicated status screen +CONFIG_NRFMACRO_SHIELD_SLAVE=y + +# Enable epaper display +CONFIG_ZMK_DISPLAY=y +CONFIG_NRFMACRO_EPD_DISPLAY=y + +# diplay settings (NOTE: these configurations have to be explicitly set here in shield) +## color scheme: black/white +CONFIG_LVGL_USE_THEME_MONO=y +CONFIG_LVGL_THEME_DEFAULT_COLOR_PRIMARY_BLACK=y +CONFIG_LVGL_THEME_DEFAULT_COLOR_SECONDARY_WHITE=y +CONFIG_LVGL_THEME_DEFAULT_COLOR_PRIMARY_RED=n +CONFIG_LVGL_THEME_DEFAULT_COLOR_SECONDARY_RED=n +## dedicated work queue or not +CONFIG_ZMK_DISPLAY_WORK_QUEUE_DEDICATED=y + +# custom screen or not +## uncommet following for custom screen +CONFIG_ZMK_DISPLAY_STATUS_SCREEN_CUSTOM=y +CONFIG_LVGL_THEME_DEFAULT_FONT_SMALL_MONTSERRAT_12=y +CONFIG_LVGL_THEME_DEFAULT_FONT_NORMAL_MONTSERRAT_16=y +CONFIG_CUSTOM_WIDGET_BATTERY_STATUS=y +CONFIG_ZMK_WIDGET_BATTERY_STATUS=n +CONFIG_CUSTOM_WIDGET_PERIPHERAL_STATUS=y +CONFIG_ZMK_WIDGET_PERIPHERAL_STATUS=n +CONFIG_NRFMACRO_SCREEN_MARK_LOGO=y + +# use custom logo or not +# CONFIG_NRFMACRO_SCREEN_STANDARD_LOGO=y +# CONFIG_NRFMACRO_SCREEN_CUSTOM_LOGO=y + +# enable usb logging +# CONFIG_ZMK_USB_LOGGING=y + +# increase transmission power to max +CONFIG_BT_CTLR_TX_PWR_PLUS_8=y \ No newline at end of file diff --git a/app/boards/shields/corne-pro/corne-pro_right.overlay b/app/boards/shields/corne-pro/corne-pro_right.overlay new file mode 100644 index 00000000000..fd0d47d985d --- /dev/null +++ b/app/boards/shields/corne-pro/corne-pro_right.overlay @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include "corne-pro.dtsi" + +// adjust matrix transform for pepripheral split +&default_transform { + col-offset = <5>; +}; + +// setup kscan pins +&kscan { + col-gpios + = <&pro_micro 21 GPIO_ACTIVE_HIGH> + , <&pro_micro 20 GPIO_ACTIVE_HIGH> + , <&pro_micro 19 GPIO_ACTIVE_HIGH> + , <&pro_micro 18 GPIO_ACTIVE_HIGH> + , <&pro_micro 15 GPIO_ACTIVE_HIGH> + ; +}; \ No newline at end of file From bdb5cc1f29bb11bb8b81ae6cfa5996219e892fea Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Sun, 17 Jul 2022 16:58:14 +0800 Subject: [PATCH 039/157] add corne-pro shield and its markline logo --- .../nrfmacro/epd_screen/slave/CMakeLists.txt | 4 +- .../slave/icons/cornepro-marklogo.c | 48 +++++++++++++++++ .../icons/pictures/cornepro-marklogo.png | Bin 0 -> 1058 bytes .../icons/{marklogo.c => sweepro-marklogo.c} | 0 app/boards/shields/42key-common-colemak.dtsi | 12 ++--- app/boards/shields/42key-common.dtsi | 6 +-- .../shields/corne-pro/Kconfig.defconfig | 4 ++ app/boards/shields/corne-pro/corne-pro.dtsi | 36 ------------- app/boards/shields/corne-pro/corne-pro.keymap | 4 +- .../shields/corne-pro/corne-pro_left.overlay | 41 ++++++++++++++ .../shields/corne-pro/corne-pro_right.conf | 1 - .../shields/corne-pro/corne-pro_right.overlay | 50 ++++++++++++++++-- .../shields/sweepro-ce/Kconfig.defconfig | 3 ++ 13 files changed, 155 insertions(+), 54 deletions(-) create mode 100644 app/boards/arm/nrfmacro/epd_screen/slave/icons/cornepro-marklogo.c create mode 100644 app/boards/arm/nrfmacro/epd_screen/slave/icons/pictures/cornepro-marklogo.png rename app/boards/arm/nrfmacro/epd_screen/slave/icons/{marklogo.c => sweepro-marklogo.c} (100%) diff --git a/app/boards/arm/nrfmacro/epd_screen/slave/CMakeLists.txt b/app/boards/arm/nrfmacro/epd_screen/slave/CMakeLists.txt index cdc1ef61c7b..b876a47de2e 100644 --- a/app/boards/arm/nrfmacro/epd_screen/slave/CMakeLists.txt +++ b/app/boards/arm/nrfmacro/epd_screen/slave/CMakeLists.txt @@ -14,8 +14,9 @@ zephyr_library_sources(status_screen.c) zephyr_library_sources_ifdef(CONFIG_CUSTOM_WIDGET_BATTERY_STATUS battery_status.c) zephyr_library_sources_ifdef(CONFIG_CUSTOM_WIDGET_PERIPHERAL_STATUS peripheral_status.c) +add_compile_definitions(LV_LVGL_H_INCLUDE_SIMPLE) if (CONFIG_NRFMACRO_SCREEN_MARK_LOGO) -zephyr_library_sources(icons/marklogo.c) +zephyr_library_sources(icons/${CONFIG_NRFMACRO_MARKLOGO_NAME}) endif() if (CONFIG_NRFMACRO_SCREEN_STANDARD_LOGO) @@ -23,7 +24,6 @@ zephyr_library_sources(icons/stdlogo.c) endif() if (CONFIG_NRFMACRO_SCREEN_CUSTOM_LOGO) -add_compile_definitions(LV_LVGL_H_INCLUDE_SIMPLE) if(ZMK_CONFIG) zephyr_library_sources(${ZMK_CONFIG}/icons/customlogo.c) else() diff --git a/app/boards/arm/nrfmacro/epd_screen/slave/icons/cornepro-marklogo.c b/app/boards/arm/nrfmacro/epd_screen/slave/icons/cornepro-marklogo.c new file mode 100644 index 00000000000..32b3d61d72d --- /dev/null +++ b/app/boards/arm/nrfmacro/epd_screen/slave/icons/cornepro-marklogo.c @@ -0,0 +1,48 @@ +#if defined(LV_LVGL_H_INCLUDE_SIMPLE) +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_MARKLOGO +#define LV_ATTRIBUTE_IMG_MARKLOGO +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_MARKLOGO uint8_t marklogo_map[] = { + 0x00, 0x00, 0x00, 0x07, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xd9, /*Color of index 1*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x38, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xad, 0xa0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0xeb, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x7a, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x53, 0x60, + 0x0f, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x08, 0x5b, 0xc0, + 0x3f, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x10, 0x48, 0x80, + 0x38, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x07, 0xc1, 0xfc, 0x7e, 0x0f, 0xc0, 0x00, 0x00, + 0x70, 0x0f, 0xe3, 0xfc, 0xff, 0x1f, 0xc0, 0x00, 0x00, + 0xe0, 0x1c, 0xe3, 0xa8, 0xe7, 0x38, 0xe0, 0x00, 0x00, + 0xe0, 0x38, 0xe3, 0x80, 0xc7, 0x30, 0xc0, 0x00, 0x00, + 0xe0, 0x38, 0xe7, 0x01, 0xc6, 0x7f, 0xc0, 0x00, 0x00, + 0xe0, 0x38, 0xe3, 0x01, 0xc6, 0x7f, 0x80, 0x00, 0x00, + 0xe0, 0x30, 0xc7, 0x01, 0xce, 0x70, 0x00, 0x00, 0x00, + 0xf2, 0x3b, 0xc7, 0x01, 0x8e, 0x38, 0x80, 0x00, 0x00, + 0x7f, 0x3f, 0x86, 0x03, 0x8c, 0x3f, 0x80, 0x00, 0x00, + 0x3e, 0x1f, 0x0e, 0x01, 0x8c, 0x1f, 0x80, 0x00, 0x00, +}; + +const lv_img_dsc_t marklogo = { + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .header.always_zero = 0, + .header.reserved = 0, + .header.w = 67, + .header.h = 18, + .data_size = 170, + .data = marklogo_map, +}; diff --git a/app/boards/arm/nrfmacro/epd_screen/slave/icons/pictures/cornepro-marklogo.png b/app/boards/arm/nrfmacro/epd_screen/slave/icons/pictures/cornepro-marklogo.png new file mode 100644 index 0000000000000000000000000000000000000000..7a212e636b7a47df300c63b3943565a314f0d4f7 GIT binary patch literal 1058 zcmV+-1l{|IP)VBcd;59f^L{p z^iq&hl60fngAZMa<|*pI=mMd1LrAhviijX7%dT2tp=G4eWaVYL%hcG|>0#}UvpvnZ zU~?4u!)EXE|Nh_F``df%wbtSPhvTscJFx~c(1s1zg^zG*(q5RfzhJ=uU+DqB0`%iF zu^B7TgCTU`O}vQd7{q+si(M@TW3^32H&)|g{EUIbt;OeP#WviSxTmlY6A#8>n~i>a zg)OOd;tl+cU$F{DVjb>D@7#s;)eLsvEX>5DX05sjI1VLTk69RlvDl4yB5vH4wA*l9 z(rm!}mBwb_tz@uhf8w#`t$$R}iyv@E#?8bq4#%zH!wGL$gXQ=Jlk=F-D>h1xmP6T#*%cO?bDQ)tVXlb&zh#4Nb>U_#$xw75dGI--sJ92bYRy z-kCJ5BIT7tA=H73lt1wK%-#y+P3TVCB&Uo;HEf=d>8{TB!uyt_-=}1{^DEvTO8iZA)?XK`A~(agb+nhK{#B8R3KVyLkN5~U zCwu-*4V&HBcy;PKF+PnyP5s=|4~bwu2Pda-i#X6{>#V;a6g#`5hGs1GCS6~}vv(8! zVa7e0xZUa9YIr&6wq{(fvKP!q+|rE8+h&nUM;Y{VKUcbxjqcu9Gu7vln4CSt_xnQmv&e20s1 zrE)<1TDjhsqU=TY)tI*{oA=s`-t<807*qoM6N<$f^y6OlK=n! literal 0 HcmV?d00001 diff --git a/app/boards/arm/nrfmacro/epd_screen/slave/icons/marklogo.c b/app/boards/arm/nrfmacro/epd_screen/slave/icons/sweepro-marklogo.c similarity index 100% rename from app/boards/arm/nrfmacro/epd_screen/slave/icons/marklogo.c rename to app/boards/arm/nrfmacro/epd_screen/slave/icons/sweepro-marklogo.c diff --git a/app/boards/shields/42key-common-colemak.dtsi b/app/boards/shields/42key-common-colemak.dtsi index c916313c538..d0ff9eafc64 100644 --- a/app/boards/shields/42key-common-colemak.dtsi +++ b/app/boards/shields/42key-common-colemak.dtsi @@ -245,21 +245,21 @@ compatible = "zmk,keymap"; default_layer { - label = "QWERTY"; + label = "CLMK"; // ----------------------------------------------------------------------------------------- // | TAB | Q | W | E | R | T | | Y | U | I | O | P | BKSP | // | CTRL | A | S | D | F | G | | H | J | K | L | ; | ' | // | SHFT | Z | X | C | V | B | | N | M | , | . | / | ESC | // | GUI | LWR | SPC | | ENT | RSE | ALT | bindings = < - &kp TAB &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P &kp BSPC - &kp N0 &kp A &kp S &kp D &kp F &kp G &kp H &kp J &kp K &kp L &kp SEMI &kp SQT - &kp N1 &kp Z &kp X &kp C &kp V &kp B &kp N &kp M &kp COMMA &kp DOT &kp FSLH &kp N3 + &kp TAB &kp Q &kp W &kp F &kp P &kp B &kp J &kp L &kp U &kp Y &kp SEMI &kp BSPC + &kp N0 &kp A &kp R &kp S &kp T &kp G &kp N &kp M &kp E &kp I &kp O &kp SQT + &kp N1 &kp Z &kp X &kp C &kp D &kp V &kp K &kp H &kp COMMA &kp DOT &kp FSLH &kp N3 &kp LGUI < NUM N5 &kp SPACE &kp RET < SYM N6 &kp RALT >; }; lower_layer { - label = "NUMBER"; + label = "NUM"; // ----------------------------------------------------------------------------------------- // | TAB | 1 | 2 | 3 | 4 | 5 | | 6 | 7 | 8 | 9 | 0 | BKSP | // | BTCLR| BT1 | BT2 | BT3 | BT4 | BT5 | | LFT | DWN | UP | RGT | | | @@ -274,7 +274,7 @@ }; raise_layer { - label = "SYMBOL"; + label = "SYM"; // ----------------------------------------------------------------------------------------- // | TAB | ! | @ | # | $ | % | | ^ | & | * | ( | ) | BKSP | // | CTRL | | | | | | | - | = | [ | ] | \ | ` | diff --git a/app/boards/shields/42key-common.dtsi b/app/boards/shields/42key-common.dtsi index 7f07abb44d2..94ddf42384d 100644 --- a/app/boards/shields/42key-common.dtsi +++ b/app/boards/shields/42key-common.dtsi @@ -245,7 +245,7 @@ compatible = "zmk,keymap"; default_layer { - label = "QWERTY"; + label = "QWTY"; // ----------------------------------------------------------------------------------------- // | TAB | Q | W | E | R | T | | Y | U | I | O | P | BKSP | // | CTRL | A | S | D | F | G | | H | J | K | L | ; | ' | @@ -259,7 +259,7 @@ >; }; lower_layer { - label = "NUMBER"; + label = "NUM"; // ----------------------------------------------------------------------------------------- // | TAB | 1 | 2 | 3 | 4 | 5 | | 6 | 7 | 8 | 9 | 0 | BKSP | // | BTCLR| BT1 | BT2 | BT3 | BT4 | BT5 | | LFT | DWN | UP | RGT | | | @@ -274,7 +274,7 @@ }; raise_layer { - label = "SYMBOL"; + label = "SYM"; // ----------------------------------------------------------------------------------------- // | TAB | ! | @ | # | $ | % | | ^ | & | * | ( | ) | BKSP | // | CTRL | | | | | | | - | = | [ | ] | \ | ` | diff --git a/app/boards/shields/corne-pro/Kconfig.defconfig b/app/boards/shields/corne-pro/Kconfig.defconfig index 62e6b265050..f48e921fc06 100644 --- a/app/boards/shields/corne-pro/Kconfig.defconfig +++ b/app/boards/shields/corne-pro/Kconfig.defconfig @@ -25,5 +25,9 @@ config ZMK_SPLIT config ZMK_USB default y +config NRFMACRO_MARKLOGO_NAME + string "Source file name of the shield mark logo" + default "cornepro-marklogo.c" + endif diff --git a/app/boards/shields/corne-pro/corne-pro.dtsi b/app/boards/shields/corne-pro/corne-pro.dtsi index e2d341d8ac2..d03ea978704 100644 --- a/app/boards/shields/corne-pro/corne-pro.dtsi +++ b/app/boards/shields/corne-pro/corne-pro.dtsi @@ -11,7 +11,6 @@ chosen: chosen { zmk,kscan = &kscan; zmk,matrix_transform = &default_transform; - zephyr,display = &shield_epd; }; default_transform: keymap_transform_0 { @@ -36,41 +35,6 @@ label = "KSCAN"; diode-direction = "col2row"; - row-gpios - = <&pro_micro 4 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> - , <&pro_micro 5 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> - , <&pro_micro 15 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> - , <&pro_micro 10 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> - ; }; }; -/* display */ -&pro_micro_spi { - compatible = "nordic,nrf-spim"; - sck-pin = <17>; - mosi-pin = <15>; - cs-gpios = <&gpio0 6 GPIO_ACTIVE_LOW>; - // not used, but required by spim binding; can be reuesd in the keymap for normal key press - miso-pin = <22>; - - // activate this device - status = "okay"; - - // spi-device: gooddisplay GDEW0102T4 - shield_epd: il0323@0 { - compatible = "gooddisplay,il0323"; - reg = <0>; - label = "DISPLAY"; - spi-max-frequency = <4000000>; - - height = <128>; - width = <80>; - reset-gpios = <&gpio0 30 GPIO_ACTIVE_LOW>; - dc-gpios = <&gpio0 8 GPIO_ACTIVE_LOW>; - busy-gpios = <&gpio0 31 GPIO_ACTIVE_LOW>; - pwr = [03 00 26 26]; - cdi = <0xd2>; - tcon = <0x22>; - }; -}; diff --git a/app/boards/shields/corne-pro/corne-pro.keymap b/app/boards/shields/corne-pro/corne-pro.keymap index c46badd8b4f..7588af10831 100644 --- a/app/boards/shields/corne-pro/corne-pro.keymap +++ b/app/boards/shields/corne-pro/corne-pro.keymap @@ -9,5 +9,5 @@ #include #include -// #include "../42key-common.dtsi" -#include "../42key-common-alternative.dtsi" \ No newline at end of file +#include "../42key-common.dtsi" +// #include "../42key-common-colemak.dtsi" \ No newline at end of file diff --git a/app/boards/shields/corne-pro/corne-pro_left.overlay b/app/boards/shields/corne-pro/corne-pro_left.overlay index 382ddcd39bc..5ab99083e00 100644 --- a/app/boards/shields/corne-pro/corne-pro_left.overlay +++ b/app/boards/shields/corne-pro/corne-pro_left.overlay @@ -8,6 +8,13 @@ // setup kscan pins &kscan { + row-gpios + = <&pro_micro 4 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 5 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 15 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 10 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + ; + col-gpios = <&pro_micro 6 GPIO_ACTIVE_HIGH> , <&pro_micro 7 GPIO_ACTIVE_HIGH> @@ -16,4 +23,38 @@ , <&pro_micro 16 GPIO_ACTIVE_HIGH> , <&pro_micro 14 GPIO_ACTIVE_HIGH> ; +}; + +/* display */ +&chosen { + zephyr,display = &shield_epd; +}; + +&pro_micro_spi { + compatible = "nordic,nrf-spim"; + sck-pin = <17>; + mosi-pin = <15>; + cs-gpios = <&gpio0 6 GPIO_ACTIVE_LOW>; + // not used, but required by spim binding; can be reuesd in the keymap for normal key press + miso-pin = <22>; + + // activate this device + status = "okay"; + + // spi-device: gooddisplay GDEW0102T4 + shield_epd: il0323@0 { + compatible = "gooddisplay,il0323"; + reg = <0>; + label = "DISPLAY"; + spi-max-frequency = <4000000>; + + height = <128>; + width = <80>; + reset-gpios = <&gpio0 30 GPIO_ACTIVE_LOW>; + dc-gpios = <&gpio0 8 GPIO_ACTIVE_LOW>; + busy-gpios = <&gpio0 31 GPIO_ACTIVE_LOW>; + pwr = [03 00 26 26]; + cdi = <0xd2>; + tcon = <0x22>; + }; }; \ No newline at end of file diff --git a/app/boards/shields/corne-pro/corne-pro_right.conf b/app/boards/shields/corne-pro/corne-pro_right.conf index a1127079a62..377a63ca81e 100644 --- a/app/boards/shields/corne-pro/corne-pro_right.conf +++ b/app/boards/shields/corne-pro/corne-pro_right.conf @@ -21,7 +21,6 @@ CONFIG_ZMK_DISPLAY_WORK_QUEUE_DEDICATED=y # custom screen or not ## uncommet following for custom screen CONFIG_ZMK_DISPLAY_STATUS_SCREEN_CUSTOM=y -CONFIG_LVGL_THEME_DEFAULT_FONT_SMALL_MONTSERRAT_12=y CONFIG_LVGL_THEME_DEFAULT_FONT_NORMAL_MONTSERRAT_16=y CONFIG_CUSTOM_WIDGET_BATTERY_STATUS=y CONFIG_ZMK_WIDGET_BATTERY_STATUS=n diff --git a/app/boards/shields/corne-pro/corne-pro_right.overlay b/app/boards/shields/corne-pro/corne-pro_right.overlay index fd0d47d985d..af78975a8dc 100644 --- a/app/boards/shields/corne-pro/corne-pro_right.overlay +++ b/app/boards/shields/corne-pro/corne-pro_right.overlay @@ -13,11 +13,53 @@ // setup kscan pins &kscan { + row-gpios + = <&pro_micro 19 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 18 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 15 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 10 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + ; + col-gpios - = <&pro_micro 21 GPIO_ACTIVE_HIGH> + = <&pro_micro 9 GPIO_ACTIVE_HIGH> + , <&pro_micro 8 GPIO_ACTIVE_HIGH> + , <&pro_micro 16 GPIO_ACTIVE_HIGH> + , <&pro_micro 14 GPIO_ACTIVE_HIGH> , <&pro_micro 20 GPIO_ACTIVE_HIGH> - , <&pro_micro 19 GPIO_ACTIVE_HIGH> - , <&pro_micro 18 GPIO_ACTIVE_HIGH> - , <&pro_micro 15 GPIO_ACTIVE_HIGH> + , <&pro_micro 21 GPIO_ACTIVE_HIGH> ; +}; + +/* display */ +&chosen { + zephyr,display = &shield_epd; +}; + +&pro_micro_spi { + compatible = "nordic,nrf-spim"; + sck-pin = <15>; + mosi-pin = <17>; + cs-gpios = <&gpio0 6 GPIO_ACTIVE_LOW>; + // not used, but required by spim binding; can be reuesd in the keymap for normal key press + miso-pin = <22>; + + // activate this device + status = "okay"; + + // spi-device: gooddisplay GDEW0102T4 + shield_epd: il0323@0 { + compatible = "gooddisplay,il0323"; + reg = <0>; + label = "DISPLAY"; + spi-max-frequency = <4000000>; + + height = <128>; + width = <80>; + reset-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>; + dc-gpios = <&gpio0 8 GPIO_ACTIVE_LOW>; + busy-gpios = <&gpio0 20 GPIO_ACTIVE_LOW>; + pwr = [03 00 26 26]; + cdi = <0xd2>; + tcon = <0x22>; + }; }; \ No newline at end of file diff --git a/app/boards/shields/sweepro-ce/Kconfig.defconfig b/app/boards/shields/sweepro-ce/Kconfig.defconfig index 6470bad3cff..79f7f31da1f 100644 --- a/app/boards/shields/sweepro-ce/Kconfig.defconfig +++ b/app/boards/shields/sweepro-ce/Kconfig.defconfig @@ -25,5 +25,8 @@ config ZMK_SPLIT config ZMK_USB default y +config NRFMACRO_MARKLOGO_NAME + string "Source file name of the shield mark logo" + default "sweepro-marklogo.c" endif From ba0d2423ab416902488af969ddbdc93445b36dea Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Mon, 18 Jul 2022 09:32:47 +0800 Subject: [PATCH 040/157] backup --- .../slave/icons/cornepro-marklogo.c | 38 +++++++++--------- .../icons/pictures/cornepro-marklogo.png | Bin 1058 -> 1046 bytes .../epd_screen/slave/icons/pictures/horse.png | Bin 49337 -> 4374 bytes 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/app/boards/arm/nrfmacro/epd_screen/slave/icons/cornepro-marklogo.c b/app/boards/arm/nrfmacro/epd_screen/slave/icons/cornepro-marklogo.c index 32b3d61d72d..3e76645adb4 100644 --- a/app/boards/arm/nrfmacro/epd_screen/slave/icons/cornepro-marklogo.c +++ b/app/boards/arm/nrfmacro/epd_screen/slave/icons/cornepro-marklogo.c @@ -14,27 +14,29 @@ #endif const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_MARKLOGO uint8_t marklogo_map[] = { - 0x00, 0x00, 0x00, 0x07, /*Color of index 0*/ - 0x00, 0x00, 0x00, 0xd9, /*Color of index 1*/ + 0x00, 0x00, 0x00, 0x06, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xd6, /*Color of index 1*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x38, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xad, 0xa0, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0xeb, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0xaf, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x7a, 0x20, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x53, 0x60, - 0x0f, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x08, 0x5b, 0xc0, - 0x3f, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x10, 0x48, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x7b, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x4b, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x49, 0x00, + 0x0f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x70, 0x07, 0xc1, 0xfc, 0x7e, 0x0f, 0xc0, 0x00, 0x00, - 0x70, 0x0f, 0xe3, 0xfc, 0xff, 0x1f, 0xc0, 0x00, 0x00, - 0xe0, 0x1c, 0xe3, 0xa8, 0xe7, 0x38, 0xe0, 0x00, 0x00, - 0xe0, 0x38, 0xe3, 0x80, 0xc7, 0x30, 0xc0, 0x00, 0x00, - 0xe0, 0x38, 0xe7, 0x01, 0xc6, 0x7f, 0xc0, 0x00, 0x00, - 0xe0, 0x38, 0xe3, 0x01, 0xc6, 0x7f, 0x80, 0x00, 0x00, - 0xe0, 0x30, 0xc7, 0x01, 0xce, 0x70, 0x00, 0x00, 0x00, - 0xf2, 0x3b, 0xc7, 0x01, 0x8e, 0x38, 0x80, 0x00, 0x00, - 0x7f, 0x3f, 0x86, 0x03, 0x8c, 0x3f, 0x80, 0x00, 0x00, - 0x3e, 0x1f, 0x0e, 0x01, 0x8c, 0x1f, 0x80, 0x00, 0x00, + 0x70, 0x07, 0x81, 0xf8, 0xfc, 0x0f, 0x80, 0x00, 0x00, + 0x70, 0x0f, 0xc3, 0xf8, 0xfe, 0x3f, 0x80, 0x00, 0x00, + 0xe0, 0x1d, 0xe3, 0xa9, 0xce, 0x39, 0xc0, 0x00, 0x00, + 0xe0, 0x38, 0xe7, 0x01, 0xce, 0x71, 0xc0, 0x00, 0x00, + 0xe0, 0x38, 0xe7, 0x01, 0xce, 0x77, 0x80, 0x00, 0x00, + 0xe0, 0x30, 0xe7, 0x01, 0x8e, 0x7f, 0x00, 0x00, 0x00, + 0xe0, 0x31, 0xc6, 0x03, 0x8c, 0xf0, 0x00, 0x00, 0x00, + 0xf2, 0x3b, 0xce, 0x03, 0x9c, 0x71, 0x00, 0x00, 0x00, + 0x7f, 0x3f, 0x8e, 0x03, 0x9c, 0x7f, 0x00, 0x00, 0x00, + 0x3e, 0x1f, 0x0e, 0x03, 0x18, 0x3f, 0x00, 0x00, 0x00, }; const lv_img_dsc_t marklogo = { @@ -42,7 +44,7 @@ const lv_img_dsc_t marklogo = { .header.always_zero = 0, .header.reserved = 0, .header.w = 67, - .header.h = 18, - .data_size = 170, + .header.h = 20, + .data_size = 188, .data = marklogo_map, }; diff --git a/app/boards/arm/nrfmacro/epd_screen/slave/icons/pictures/cornepro-marklogo.png b/app/boards/arm/nrfmacro/epd_screen/slave/icons/pictures/cornepro-marklogo.png index 7a212e636b7a47df300c63b3943565a314f0d4f7..1455b1b233d974c53c1564d059afd35f67b53923 100644 GIT binary patch delta 979 zcmV;^11$Wa2$l$t7YY;z1^@s6)Laj&YUIuy&Zyg2V6y#^YLCpSa)fNxFU(Z>4)q zMCYEk8LfB{uZ#H5jVJH`rr~>R#NqfEV=)V_j)t+riDN`;I6i;l4n`GIaT$I=1&go_ zZ{kagA6qi;p{d=ud+i-o94HpJvVr?bWRs29ERIkcn#4xT5l5&OPQdf{UF>iTpW>Qf zGyi{znb@wr3HfjSDB3mRn2JBd!q?$ak%!O1LiHolh^7&-ZUpDvOB5kBd?!*+8K&Vi z;YB^-{9V_Ft`&ce;C&In{}g$DweUjN4-ey=G7{YU$w!GIfGwrIToe?ci^BB z-O1v~)vz3=h=QgkagS%(8JVF=Fc&xD4&0gP<|j?JD3yOt7v*zJl*$EeP~U`mghw5K z&l3O2kXBrR57L0`1L$5#y7kGJ0-cFpmT`9{Zm04MUX^t3W!$2~ZBEw}Y)IUKOxK+8 zj}Bn_pisfFslTB_w-2sN{bz(16{x0md&ae%$Lr;O=C0wiL9KEKXvNzi2yPRF(NiK;6llha;$>$CJ{GC6@iV|;tkC;>H)2xK ztPn4s-iYFFY2t2a&^L*cx;oj{g^%!LUOE delta 991 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 From 092ff8c637ec1947c7b93a71b511696ad3b7d43d Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Thu, 21 Jul 2022 12:29:14 +0800 Subject: [PATCH 041/157] add config option for screen margin adjustment --- app/boards/arm/nrfmacro/Kconfig.defconfig | 21 ++++++++++++++++++ .../icons/pictures/cornepro-marklogo.png | Bin 1046 -> 988 bytes .../nrfmacro/epd_screen/slave/status_screen.c | 10 ++++----- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/app/boards/arm/nrfmacro/Kconfig.defconfig b/app/boards/arm/nrfmacro/Kconfig.defconfig index c4979bec98d..7248dc6f885 100644 --- a/app/boards/arm/nrfmacro/Kconfig.defconfig +++ b/app/boards/arm/nrfmacro/Kconfig.defconfig @@ -86,6 +86,27 @@ config NRFMACRO_SCREEN_CUSTOM_LOGO endchoice +# Customizing margin of the display screen +config NRFMACRO_SCREEN_TOP_MARGIN + int "top margin of display screen in pixel" + depends on ZMK_DISPLAY + default 2 + +config NRFMACRO_SCREEN_BOTTOM_MARGIN + int "bottom margin of display screen in pixel" + depends on ZMK_DISPLAY + default 2 + +config NRFMACRO_SCREEN_LEFT_MARGIN + int "left margin of display screen in pixel" + depends on ZMK_DISPLAY + default 2 + +config NRFMACRO_SCREEN_RIGHT_MARGIN + int "right margin of display screen in pixel" + depends on ZMK_DISPLAY + default 2 + # USB STACK setup if USB_DEVICE_STACK diff --git a/app/boards/arm/nrfmacro/epd_screen/slave/icons/pictures/cornepro-marklogo.png b/app/boards/arm/nrfmacro/epd_screen/slave/icons/pictures/cornepro-marklogo.png index 1455b1b233d974c53c1564d059afd35f67b53923..a76d924900c9c0aaa8bb4f2b1b2968d9656766af 100644 GIT binary patch delta 941 zcmV;e15*5!2;2uDiBL{Q4GJ0x0000DNk~Le0000z0000I2nGNE0O*+33Xvfi3l;7F z02S^5DcxYkkwz$gSxH1eR9J=G*K3GXRTKyC-%L}^NRbv%Vicu;#W+|<515%qN@LPK z8firIFi6M>j6Oy5fT9;R5kc9D>|qaz!b0hb~`=^!8z`%RS!i zob)nT_J_;f=YRiuoxS#6YwdkFxG@i3VG|xjD;8rjHez9a>ZV~Gw&MB&Kh4p_3&PoM z{E_Xu!tq7eh7PeXH{(hSxBe?r9X<5mZEVTsEjR=3;Am{X#c08O_!5U5@L7%`CZQri z`DfgO8Oc@fDLSzP%W}*jymv6~bPv`GM~}k;BD~+kr6RvS!Fc?FLvRINsyEbuYp@&x zcokRqZ-&HwQjyO^tQ5IB2HP0i9yyDh+-Y{d03~8q;w?9oLOxvVS%v zWd8!pkq);cIZ>>_1Pq}c-=GU;;auVT{{n5<_6vtM;~aclGUiNNjZ;OnT#je!G-J?< z^Tci(D~^vLJdO`A7PIqx$73A6$1@^K>oFU1aWZ~?6;=BY{JxLA?u9P3Fuv?>w0B~U zzlQ$}b-FhiXev2&N^0hoXnG5_j=+i$v=8r(qea}E2DXYqHWC&TUUo0$pt~#i^(FR4M4MW;Z7k8v5;vUPxTnBx zEA*?mRdsZiD2|OV4pXzgI@@*}mHokNFUj_!)L)31BEN}SR#Vs|R_&G&%^hkXR>YFz z2MgTu$qkg)>r;z{Q)V^Bq3VezkZFC{9$lFHfz;CIme(xSC&fbdnaHwJ`c8oB$+xFkpz0oJWLwAS}T8G<3@v35u zC`{W$SU!i->g+7UDm+>8t|_TmrB*~I6}Zki{WS3nFeDac5HE|}ZVG<_e-Q)jyHu4` P00000NkvXXu0mjftAf3t delta 1000 zcmVAMj=#YFT@^Z#Y}ICu!tT6 zeu*%8KqB<~6j%}^6k+uEP=q8wFN-u`K$~!?4;4K$h3P4oJsh)4OPcb|?ZeuKyXQJ{ zW~Q8(@DG>0&;S0<-fQo(_FC&4_DsBsO<04g64!>V#9f?!xbv|VTXE~inm96;j2h0v z(^!uq5_b_^5Sw+R*o50~7q*NU4Qq!Pt@sA3utj)73w}Ze#^GDsh_A(leU5RJ(Xe)y z(SpP91;*oAT%Wk#@JYIU7H_3{PDJOPxEZZ@60eK+(2XbX0H)!4Y{cRC8DlXEua1VX z!--==Y&brD;|@j@Q*jx7K?RGj4sYU1j2~Mv@S&;QxqIy$Rvai6xUzx!N@SCb*es4v z8=AyM%n?VZ7f!(Q_+9L94WHtgVKe`KikaB1z6tqn{wUft;+Tp*#KPC%Qjv$x!b0^U z(}<=Kv2FzC-b)lAHGC&hP#LD-HQ_});{09LhprWWkKla~!T%I_f3@&J*bfimoium1 zs0)_hs6NKLKm@$PgE!%R?6!L8T=nBHdjRb${B74`)PoB%U6bB%C;$D0iAi@!Kbjhr zHq8BC_FuKZjyZ#0ZaEgF0e9e_65Yw-$ED%8*uEf)CPw?E~muO1kyQm;#-NUzTxqCvKLb{f=t((@sAE*`=C(4v8lhIM7IyFO#Nqs7Zs?cc6-LPrgn`! z@^?1o^wjUj_~TN$HnnHs=+v*_K1{*oqM-VJQF!-%Mn$+A{92-$B3^Ka1s0f~ZhXOy z5pjqJjQwGHHfFII`{IlSZgO_dy;*;&crf$uL&-dYMkNh238$9m_RsozXZ^Y9x`+)# zESZq{`4ewMOKPj?$7F`^7~%*KS*XBG;tjJ)RFyp?v_Y+M2x!IIA_#61h0#+YRupK) zi{fQx2R;_5vhg#(Vyw{nd^ciJ(yS0KpWcY#Z)xIgY0x)`l)5_E*M*Ppm~!tM68-^P WtPft(d!xqy0000 Date: Thu, 21 Jul 2022 14:42:16 +0800 Subject: [PATCH 042/157] change the starting pixel of logo widget to 0 --- app/boards/arm/nrfmacro/epd_screen/slave/status_screen.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/boards/arm/nrfmacro/epd_screen/slave/status_screen.c b/app/boards/arm/nrfmacro/epd_screen/slave/status_screen.c index ad7254a40da..e3dce0b9c14 100644 --- a/app/boards/arm/nrfmacro/epd_screen/slave/status_screen.c +++ b/app/boards/arm/nrfmacro/epd_screen/slave/status_screen.c @@ -58,14 +58,14 @@ lv_obj_t *zmk_display_status_screen() { lv_obj_t * stdlogo_icon; stdlogo_icon = lv_img_create(screen, NULL); lv_img_set_src(stdlogo_icon, &stdlogo); - lv_obj_align(stdlogo_icon, NULL, LV_ALIGN_IN_BOTTOM_MID, 2, -28-CONFIG_NRFMACRO_SCREEN_BOTTOM_MARGIN); + lv_obj_align(stdlogo_icon, NULL, LV_ALIGN_IN_BOTTOM_MID, 0, -28-CONFIG_NRFMACRO_SCREEN_BOTTOM_MARGIN); #endif // 3. configurable personal logo, which can replace the product logo #if IS_ENABLED(CONFIG_NRFMACRO_SCREEN_CUSTOM_LOGO) lv_obj_t * customlogo_icon; customlogo_icon = lv_img_create(screen, NULL); lv_img_set_src(customlogo_icon, &customlogo); - lv_obj_align(customlogo_icon, NULL, LV_ALIGN_IN_BOTTOM_MID, 2, -28-CONFIG_NRFMACRO_SCREEN_BOTTOM_MARGIN); + lv_obj_align(customlogo_icon, NULL, LV_ALIGN_IN_BOTTOM_MID, 0, -28-CONFIG_NRFMACRO_SCREEN_BOTTOM_MARGIN); #endif lv_refr_now(NULL); From 0ab42ab40815b1f44363a458c92b99aad1b48a9d Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Thu, 21 Jul 2022 21:23:22 +0800 Subject: [PATCH 043/157] update corne-pro mark logo --- .../slave/icons/cornepro-marklogo.c | 48 +++++++++---------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/app/boards/arm/nrfmacro/epd_screen/slave/icons/cornepro-marklogo.c b/app/boards/arm/nrfmacro/epd_screen/slave/icons/cornepro-marklogo.c index 3e76645adb4..b3efac4c2fa 100644 --- a/app/boards/arm/nrfmacro/epd_screen/slave/icons/cornepro-marklogo.c +++ b/app/boards/arm/nrfmacro/epd_screen/slave/icons/cornepro-marklogo.c @@ -14,37 +14,35 @@ #endif const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_MARKLOGO uint8_t marklogo_map[] = { - 0x00, 0x00, 0x00, 0x06, /*Color of index 0*/ - 0x00, 0x00, 0x00, 0xd6, /*Color of index 1*/ + 0x00, 0x00, 0x00, 0x09, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xd5, /*Color of index 1*/ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x38, 0xc0, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xad, 0xa0, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0xaf, 0x60, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x7a, 0x20, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x7b, 0x60, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x4b, 0xc0, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x49, 0x00, - 0x0f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1f, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x38, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x70, 0x07, 0x81, 0xf8, 0xfc, 0x0f, 0x80, 0x00, 0x00, - 0x70, 0x0f, 0xc3, 0xf8, 0xfe, 0x3f, 0x80, 0x00, 0x00, - 0xe0, 0x1d, 0xe3, 0xa9, 0xce, 0x39, 0xc0, 0x00, 0x00, - 0xe0, 0x38, 0xe7, 0x01, 0xce, 0x71, 0xc0, 0x00, 0x00, - 0xe0, 0x38, 0xe7, 0x01, 0xce, 0x77, 0x80, 0x00, 0x00, - 0xe0, 0x30, 0xe7, 0x01, 0x8e, 0x7f, 0x00, 0x00, 0x00, - 0xe0, 0x31, 0xc6, 0x03, 0x8c, 0xf0, 0x00, 0x00, 0x00, - 0xf2, 0x3b, 0xce, 0x03, 0x9c, 0x71, 0x00, 0x00, 0x00, - 0x7f, 0x3f, 0x8e, 0x03, 0x9c, 0x7f, 0x00, 0x00, 0x00, - 0x3e, 0x1f, 0x0e, 0x03, 0x18, 0x3f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xee, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xab, 0x68, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x2d, 0x48, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xee, 0xd8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x1a, 0x50, + 0x07, 0x00, 0x00, 0x00, 0x00, 0x02, 0x12, 0xf0, + 0x1f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x07, 0x02, 0xf0, 0xf0, 0x1c, 0x00, 0x00, + 0x70, 0x1f, 0xc7, 0xf1, 0xf8, 0x7f, 0x00, 0x00, + 0xe0, 0x1b, 0xc7, 0xf3, 0xbc, 0xf7, 0x00, 0x00, + 0xe0, 0x38, 0xc6, 0x03, 0x9c, 0xc7, 0x00, 0x00, + 0xe0, 0x31, 0xce, 0x03, 0x19, 0xee, 0x00, 0x00, + 0xe0, 0x70, 0xce, 0x03, 0x19, 0xfe, 0x00, 0x00, + 0xe0, 0x71, 0xce, 0x07, 0x39, 0xe0, 0x00, 0x00, + 0xe2, 0x33, 0x8c, 0x07, 0x39, 0xc0, 0x00, 0x00, + 0x7e, 0x3f, 0x1c, 0x06, 0x30, 0xfe, 0x00, 0x00, + 0x3e, 0x1e, 0x0c, 0x06, 0x30, 0x7c, 0x00, 0x00, }; const lv_img_dsc_t marklogo = { .header.cf = LV_IMG_CF_INDEXED_1BIT, .header.always_zero = 0, .header.reserved = 0, - .header.w = 67, - .header.h = 20, - .data_size = 188, + .header.w = 61, + .header.h = 18, + .data_size = 152, .data = marklogo_map, }; From e3fd0705d79cd8a8ee0ff0db6f0936313ecb10f8 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Thu, 21 Jul 2022 21:23:55 +0800 Subject: [PATCH 044/157] [new shield] sweep-pro --- .../shields/sweep-pro/Kconfig.defconfig | 32 ++++++ app/boards/shields/sweep-pro/Kconfig.shield | 8 ++ app/boards/shields/sweep-pro/README.md | 35 ++++++ app/boards/shields/sweep-pro/sweep-pro.dtsi | 101 ++++++++++++++++++ app/boards/shields/sweep-pro/sweep-pro.keymap | 13 +++ .../shields/sweep-pro/sweep-pro.zmk.yml | 12 +++ .../shields/sweep-pro/sweep-pro_left.conf | 39 +++++++ .../shields/sweep-pro/sweep-pro_left.overlay | 32 ++++++ .../shields/sweep-pro/sweep-pro_right.conf | 41 +++++++ .../shields/sweep-pro/sweep-pro_right.overlay | 28 +++++ 10 files changed, 341 insertions(+) create mode 100644 app/boards/shields/sweep-pro/Kconfig.defconfig create mode 100644 app/boards/shields/sweep-pro/Kconfig.shield create mode 100644 app/boards/shields/sweep-pro/README.md create mode 100644 app/boards/shields/sweep-pro/sweep-pro.dtsi create mode 100644 app/boards/shields/sweep-pro/sweep-pro.keymap create mode 100644 app/boards/shields/sweep-pro/sweep-pro.zmk.yml create mode 100644 app/boards/shields/sweep-pro/sweep-pro_left.conf create mode 100644 app/boards/shields/sweep-pro/sweep-pro_left.overlay create mode 100644 app/boards/shields/sweep-pro/sweep-pro_right.conf create mode 100644 app/boards/shields/sweep-pro/sweep-pro_right.overlay diff --git a/app/boards/shields/sweep-pro/Kconfig.defconfig b/app/boards/shields/sweep-pro/Kconfig.defconfig new file mode 100644 index 00000000000..adf1c989fd3 --- /dev/null +++ b/app/boards/shields/sweep-pro/Kconfig.defconfig @@ -0,0 +1,32 @@ +# Copyright (c) 2020 The ZMK Contributors +# SPDX-License-Identifier: MIT + +if SWEEP_PRO_LEFT + +config ZMK_KEYBOARD_NAME + default "Sweep-Pro" + +config ZMK_SPLIT_BLE_ROLE_CENTRAL + default y + +endif + +if SWEEP_PRO_RIGHT +config ZMK_KEYBOARD_NAME + default "Sweep-Pro-R" + +endif + +if SWEEP_PRO_LEFT || SWEEP_PRO_RIGHT + +config ZMK_SPLIT + default y + +config ZMK_USB + default y + +config NRFMACRO_MARKLOGO_NAME + string "Source file name of the shield mark logo" + default "sweepro-marklogo.c" +endif + diff --git a/app/boards/shields/sweep-pro/Kconfig.shield b/app/boards/shields/sweep-pro/Kconfig.shield new file mode 100644 index 00000000000..f938fe82692 --- /dev/null +++ b/app/boards/shields/sweep-pro/Kconfig.shield @@ -0,0 +1,8 @@ +# Copyright (c) 2020 The ZMK Contributors +# SPDX-License-Identifier: MIT + +config SWEEP_PRO_LEFT + def_bool $(shields_list_contains,sweep-pro_left) + +config SWEEP_PRO_RIGHT + def_bool $(shields_list_contains,sweep-pro_right) diff --git a/app/boards/shields/sweep-pro/README.md b/app/boards/shields/sweep-pro/README.md new file mode 100644 index 00000000000..b1344293d2a --- /dev/null +++ b/app/boards/shields/sweep-pro/README.md @@ -0,0 +1,35 @@ +# Cradio + +Cradio is a firmware for a few 34 key keyboards, including Cradio, Hypergolic and Sweep. + +## Pin arrangement + +Some revisions of the aforementioned PCBs have slightly different pin arrangements compared to what's defined in [`cradio.dtsi`](./cradio.dtsi). If you need to swap a few keys for your particular PCB, you can easily reorder the `input-gpio` definition in your own keymap file (i.e. in `zmk-config/config/cradio.keymap`): + +```dts +/* Adjusted Cradio pin arrangement */ +/* The position of Q and B keys have been swapped */ +&kscan0 { + input-gpios + = <&pro_micro_d 6 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_a 0 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_a 1 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_a 2 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_a 3 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_d 15 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_d 14 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_d 16 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_d 10 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_d 1 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_d 2 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_d 3 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_d 4 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_d 5 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_d 7 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_d 8 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_d 9 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + ; +}; +``` + +This `&kscan0` block must be placed outside of any blocks surrounded by curly braces (`{...}`). diff --git a/app/boards/shields/sweep-pro/sweep-pro.dtsi b/app/boards/shields/sweep-pro/sweep-pro.dtsi new file mode 100644 index 00000000000..29c83339137 --- /dev/null +++ b/app/boards/shields/sweep-pro/sweep-pro.dtsi @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include + +/ { + + chosen: chosen { + zmk,kscan = &kscan; + zmk,matrix_transform = &default_transform; + zephyr,display = &shield_epd; + }; + + default_transform: keymap_transform_0 { + compatible = "zmk,matrix-transform"; + columns = <10>; + rows = <4>; +// | SW1 | SW2 | SW3 | SW4 | SW5 | | SW5 | SW4 | SW3 | SW2 | SW1 | +// | SW6 | SW7 | SW8 | SW9 | SW10 | | SW10 | SW9 | SW8 | SW7 | SW6 | +// | SW11 | SW12 | SW13 | SW14 | SW15 | | SW15 | SW14 | SW13 | SW12 | SW11 | +// | SW16 | SW17 | SW18 | | SW18 | SW17 | SW16 | + map = < +RC(0,0) RC(0,1) RC(0,2) RC(0,3) RC(0,4) RC(0,5) RC(0,6) RC(0,7) RC(0,8) RC(0,9) +RC(1,0) RC(1,1) RC(1,2) RC(1,3) RC(1,4) RC(1,5) RC(1,6) RC(1,7) RC(1,8) RC(1,9) +RC(2,0) RC(2,1) RC(2,2) RC(2,3) RC(2,4) RC(2,5) RC(2,6) RC(2,7) RC(2,8) RC(2,9) + RC(3,2) RC(3,3) RC(3,4) RC(3,5) RC(3,6) RC(3,7) + >; + }; + + // scan matrix + kscan: kscan { + compatible = "zmk,kscan-gpio-matrix"; + label = "KSCAN"; + + diode-direction = "col2row"; + row-gpios + = <&pro_micro 14 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 16 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 10 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 8 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + ; + }; + + /* encoders */ + left_encoder: encoder_left { + compatible = "alps,ec11"; + status = "disabled"; + label = "LEFT_ENCODER"; + a-gpios = <&gpio1 4 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + b-gpios = <&gpio1 6 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + resolution = <2>; + }; + + right_encoder: encoder_right { + compatible = "alps,ec11"; + status = "disabled"; + label = "RIGHT_ENCODER"; + a-gpios = <&gpio1 4 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + b-gpios = <&gpio1 6 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + resolution = <2>; + }; + + sensors { + compatible = "zmk,keymap-sensors"; + sensors = <&left_encoder &right_encoder>; + }; +}; + +/* display */ +&pro_micro_spi { +// &spi2 { + compatible = "nordic,nrf-spim"; + sck-pin = <20>; + mosi-pin = <17>; + cs-gpios = <&gpio0 24 GPIO_ACTIVE_LOW>; + // not used, but required by spim binding; can be reuesd in the keymap for normal key press + miso-pin = <22>; + + // activate this device + status = "okay"; + + // spi-device: gooddisplay GDEW0102T4 + shield_epd: il0323@0 { + compatible = "gooddisplay,il0323"; + reg = <0>; + label = "DISPLAY"; + spi-max-frequency = <4000000>; + + height = <128>; + width = <80>; + reset-gpios = <&gpio0 15 GPIO_ACTIVE_LOW>; + dc-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>; + busy-gpios = <&gpio0 9 GPIO_ACTIVE_LOW>; + pwr = [03 00 26 26]; + cdi = <0xd2>; + tcon = <0x22>; + }; +}; diff --git a/app/boards/shields/sweep-pro/sweep-pro.keymap b/app/boards/shields/sweep-pro/sweep-pro.keymap new file mode 100644 index 00000000000..f12978df75b --- /dev/null +++ b/app/boards/shields/sweep-pro/sweep-pro.keymap @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include +#include + +// #include "../36key-common.dtsi" +#include "../36key-common-colemak.dtsi" \ No newline at end of file diff --git a/app/boards/shields/sweep-pro/sweep-pro.zmk.yml b/app/boards/shields/sweep-pro/sweep-pro.zmk.yml new file mode 100644 index 00000000000..b8ee11cf0ab --- /dev/null +++ b/app/boards/shields/sweep-pro/sweep-pro.zmk.yml @@ -0,0 +1,12 @@ +file_format: "1" +id: sweep-pro +name: Sweep-Pro +type: shield +url: https://github.com/ufan/mykeyboard +requires: [pro_micro] +exposes: [i2c_oled] +features: + - keys: +siblings: + - sweep-pro_left + - sweep-pro_right diff --git a/app/boards/shields/sweep-pro/sweep-pro_left.conf b/app/boards/shields/sweep-pro/sweep-pro_left.conf new file mode 100644 index 00000000000..015059a2a62 --- /dev/null +++ b/app/boards/shields/sweep-pro/sweep-pro_left.conf @@ -0,0 +1,39 @@ +# Copyright (c) 2020 The ZMK Contributors +# SPDX-License-Identifier: MIT + +# Set the role of the split, used in choosing the dedicated status screen +CONFIG_NRFMACRO_SHIELD_MASTER=y + +# Enable epaper display +CONFIG_ZMK_DISPLAY=y +CONFIG_NRFMACRO_EPD_DISPLAY=y +CONFIG_NRFMACRO_EPD_ROTATE_180=y +CONFIG_MYCHANGE_DISPLAY_UPDATE_MO_BEHAVIOR=n + +# diplay settings (NOTE: these configurations have to be explicitly set here in shield) +## color scheme: black/white +CONFIG_LVGL_USE_THEME_MONO=y +CONFIG_LVGL_THEME_DEFAULT_COLOR_PRIMARY_BLACK=y +CONFIG_LVGL_THEME_DEFAULT_COLOR_SECONDARY_WHITE=y +CONFIG_LVGL_THEME_DEFAULT_COLOR_PRIMARY_RED=n +CONFIG_LVGL_THEME_DEFAULT_COLOR_SECONDARY_RED=n +## dedicated work queue or not +CONFIG_ZMK_DISPLAY_WORK_QUEUE_DEDICATED=y + +## custom screen or not +### uncommet following for custom status screen +CONFIG_ZMK_DISPLAY_STATUS_SCREEN_CUSTOM=y +CONFIG_LVGL_THEME_DEFAULT_FONT_NORMAL_MONTSERRAT_16=y +CONFIG_CUSTOM_WIDGET_BATTERY_STATUS=y +CONFIG_ZMK_WIDGET_BATTERY_STATUS=n +CONFIG_CUSTOM_WIDGET_OUTPUT_STATUS=y +CONFIG_ZMK_WIDGET_OUTPUT_STATUS=n +CONFIG_CUSTOM_WIDGET_LAYER_STATUS=y +CONFIG_ZMK_WIDGET_LAYER_STATUS=n + +# Enable encoder support +CONFIG_EC11=y +CONFIG_EC11_TRIGGER_GLOBAL_THREAD=y + +# set transmission power to max +CONFIG_BT_CTLR_TX_PWR_PLUS_8=y \ No newline at end of file diff --git a/app/boards/shields/sweep-pro/sweep-pro_left.overlay b/app/boards/shields/sweep-pro/sweep-pro_left.overlay new file mode 100644 index 00000000000..b99c6d75bcc --- /dev/null +++ b/app/boards/shields/sweep-pro/sweep-pro_left.overlay @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include "sweep-pro.dtsi" + +// setup display +// &chosen { +// zephyr,display = &epd; +// }; + +// &nrfmacro_spi { +// status = "okay"; +// }; + +// setup kscan pins +&kscan { + col-gpios + = <&pro_micro 15 GPIO_ACTIVE_HIGH> + , <&pro_micro 18 GPIO_ACTIVE_HIGH> + , <&pro_micro 19 GPIO_ACTIVE_HIGH> + , <&pro_micro 20 GPIO_ACTIVE_HIGH> + , <&pro_micro 21 GPIO_ACTIVE_HIGH> + ; +}; + +// enable left encoder +&left_encoder { + status = "okay"; +}; \ No newline at end of file diff --git a/app/boards/shields/sweep-pro/sweep-pro_right.conf b/app/boards/shields/sweep-pro/sweep-pro_right.conf new file mode 100644 index 00000000000..6ff996336e7 --- /dev/null +++ b/app/boards/shields/sweep-pro/sweep-pro_right.conf @@ -0,0 +1,41 @@ +# Copyright (c) 2020 The ZMK Contributors +# SPDX-License-Identifier: MIT + +# Set the role of the split, used in choosing the dedicated status screen +CONFIG_NRFMACRO_SHIELD_SLAVE=y + +# Enable epaper display +CONFIG_ZMK_DISPLAY=y +CONFIG_NRFMACRO_EPD_DISPLAY=y +CONFIG_NRFMACRO_EPD_ROTATE_180=y + +# diplay settings (NOTE: these configurations have to be explicitly set here in shield) +## color scheme: black/white +CONFIG_LVGL_USE_THEME_MONO=y +CONFIG_LVGL_THEME_DEFAULT_COLOR_PRIMARY_BLACK=y +CONFIG_LVGL_THEME_DEFAULT_COLOR_SECONDARY_WHITE=y +CONFIG_LVGL_THEME_DEFAULT_COLOR_PRIMARY_RED=n +CONFIG_LVGL_THEME_DEFAULT_COLOR_SECONDARY_RED=n +## dedicated work queue or not +CONFIG_ZMK_DISPLAY_WORK_QUEUE_DEDICATED=y + +# custom screen or not +## uncommet following for custom screen +CONFIG_ZMK_DISPLAY_STATUS_SCREEN_CUSTOM=y +CONFIG_LVGL_THEME_DEFAULT_FONT_NORMAL_MONTSERRAT_16=y +CONFIG_CUSTOM_WIDGET_BATTERY_STATUS=y +CONFIG_ZMK_WIDGET_BATTERY_STATUS=n +CONFIG_CUSTOM_WIDGET_PERIPHERAL_STATUS=y +CONFIG_ZMK_WIDGET_PERIPHERAL_STATUS=n +CONFIG_NRFMACRO_SCREEN_MARK_LOGO=y + +# use custom logo or not +# CONFIG_NRFMACRO_SCREEN_STANDARD_LOGO=y +# CONFIG_NRFMACRO_SCREEN_CUSTOM_LOGO=y + +# Enable encoder support +CONFIG_EC11=y +CONFIG_EC11_TRIGGER_GLOBAL_THREAD=y + +# set transmission power to max +CONFIG_BT_CTLR_TX_PWR_PLUS_8=y \ No newline at end of file diff --git a/app/boards/shields/sweep-pro/sweep-pro_right.overlay b/app/boards/shields/sweep-pro/sweep-pro_right.overlay new file mode 100644 index 00000000000..2ae3d83d8a8 --- /dev/null +++ b/app/boards/shields/sweep-pro/sweep-pro_right.overlay @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include "sweep-pro.dtsi" + +// adjust matrix transform for pepripheral split +&default_transform { + col-offset = <5>; +}; + +// setup kscan pins +&kscan { + col-gpios + = <&pro_micro 21 GPIO_ACTIVE_HIGH> + , <&pro_micro 20 GPIO_ACTIVE_HIGH> + , <&pro_micro 19 GPIO_ACTIVE_HIGH> + , <&pro_micro 18 GPIO_ACTIVE_HIGH> + , <&pro_micro 15 GPIO_ACTIVE_HIGH> + ; +}; + +// enable right encoder +&right_encoder { + status = "okay"; +}; \ No newline at end of file From 2bd7c113078fb61649ae40f4f9c6e4002da32c15 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Sun, 24 Jul 2022 16:48:18 +0800 Subject: [PATCH 045/157] [bugfix] corne-pro and add sweep-pro expansion keys --- app/boards/shields/40key-common-colemak.dtsi | 63 ++++ app/boards/shields/40key-common.dtsi | 299 ++++++++++++++++++ .../shields/corne-pro/corne-pro_right.overlay | 2 +- app/boards/shields/sweep-pro/sweep-pro.dtsi | 2 +- app/boards/shields/sweep-pro/sweep-pro.keymap | 3 +- 5 files changed, 365 insertions(+), 4 deletions(-) create mode 100644 app/boards/shields/40key-common-colemak.dtsi create mode 100644 app/boards/shields/40key-common.dtsi diff --git a/app/boards/shields/40key-common-colemak.dtsi b/app/boards/shields/40key-common-colemak.dtsi new file mode 100644 index 00000000000..958a9200a9b --- /dev/null +++ b/app/boards/shields/40key-common-colemak.dtsi @@ -0,0 +1,63 @@ +#define DEFAULT 0 +#define QWERT 1 +#define NUM 2 +#define SYM 3 + +&mt { + flavor = "tap-preferred"; + tapping_term_ms = <200>; +}; + +/ { +#include "key-common.dtsi" + + keymap { + compatible = "zmk,keymap"; + + default_layer { + label = "CLMK"; + bindings = < + &kp Q &kp W &kp F &kp P &kp B &kp J &kp L &kp U &kp Y &kp SQT + &kp A &mt LSFT R &mt LALT S &mt LCTL T < NUM G < NUM M &mt RCTL N &mt RALT E &mt RSFT I &kp O + &kp Z &kp X &kp C &kp D &kp V &kp K &kp H &cmqus &dtsmi &kp RC(B) + &kp KP_N1 &kp KP_N2 &encoder &kp TAB < SYM SPACE < SYM RET &kp BSPC &encoder &kp KP_N3 &kp KP_N4 + >; + sensor-bindings = <&inc_dec_kp RIGHT LEFT &inc_dec_kp UP DOWN>; + }; + + qwert_layer { + label = "QWRT"; + bindings = < + &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P + &kp A &mt LSFT S &mt LALT D &mt LCTL T < NUM G < NUM H &mt RCTL J &mt RALT K &mt RSFT L &kp SQT + &kp Z &kp X &kp C &kp V &kp B &kp N &kp M &cmqus &dtsmi &kp RC(B) + &kp KP_N1 &kp KP_N2 &encoder &kp TAB < SYM SPACE < SYM RET &kp BSPC &encoder &kp KP_N3 &kp KP_N4 + &encoder &kp TAB < SYM SPACE < SYM RET &kp BSPC &encoder + >; + sensor-bindings = <&inc_dec_kp RIGHT LEFT &inc_dec_kp UP DOWN>; + }; + + number_layer { + label = "NUM"; + bindings = < + &bt BT_NXT &prv_tab &kp UP &nxt_tab &spc1 &kp KP_PLUS &kp KP_N7 &kp KP_N8 &kp KP_N9 &kp KP_MINUS + &bt BT_PRV &kp LEFT &kp DOWN &kp RIGHT &spc2 &kp KP_MULTIPLY &kp KP_N4 &kp KP_N5 &kp KP_N6 &kp KP_DIVIDE + &bt BT_CLR &none &none &none &spc3 &kp KP_EQUAL &kp KP_N1 &kp KP_N2 &kp KP_N3 &kp KP_DOT + &kp KP_N1 &kp KP_N2 &encoder &spc4 &kp BSPC &kp RET &kp KP_N0 &encoder &kp KP_N3 &kp KP_N4 + >; + sensor-bindings = <&inc_dec_kp PG_UP PG_DN &inc_dec_kp C_VOL_UP C_VOL_DN>; + }; + + symbol_layer { + label = "SYM"; + bindings = < + &none &kp TILDE &kp LT &kp GT &kp PRCNT &kp AMPS &kp CARET &kp LBRC &kp RBRC &tog QWERT + &kp GRAVE &kp EXCL &kp MINUS &kp PLUS &kp EQUAL &kp PIPE &kp COLON &kp LPAR &kp RPAR &kp AT + &none &none &kp SLASH &kp STAR &kp BSLH &kp HASH &kp DLLR &kp LBKT &kp RBKT &none + &none &none &encoder &none &none &none &none &encoder &none &none + >; + sensor-bindings = <&inc_dec_kp PG_UP PG_DN &inc_dec_kp C_VOL_UP C_VOL_DN>; + }; + + }; +}; \ No newline at end of file diff --git a/app/boards/shields/40key-common.dtsi b/app/boards/shields/40key-common.dtsi new file mode 100644 index 00000000000..fc6bec7fe21 --- /dev/null +++ b/app/boards/shields/40key-common.dtsi @@ -0,0 +1,299 @@ +#define DEFAULT 0 +#define COLEMAK 1 +#define NUM 2 +#define SYM 3 + +&mt { + flavor = "tap-preferred"; + tapping_term_ms = <200>; +}; + +/ { + // macros + macros { + ZMK_MACRO(hello, // hello world + wait-ms = <0>; + tap-ms = <10>; + bindings = <&kp LS(H) &kp E &kp L &kp L &kp O> + , <&kp COMMA> + , <&kp W &kp O &kp R &kp L &kp D &kp EXCL>; + ) + + ZMK_MACRO(encoder, // for encoder switch press + wait-ms = <0>; + tap-ms = <10>; + bindings = <&kp LS(I) &kp SPACE &kp A &kp M &kp SPACE &kp A &kp SPACE> + , <&kp E &kp N &kp C &kp O &kp D &kp E &kp R>; + ) + + ZMK_MACRO(spc1, // for spacemacs window nav, Alt+M+1 + wait-ms = <0>; + tap-ms = <10>; + bindings = <&kp LA(M) &kp N1>; + ) + + ZMK_MACRO(spc2, + wait-ms = <0>; + tap-ms = <10>; + bindings = <&kp LA(M) &kp N2>; + ) + + ZMK_MACRO(spc3, + wait-ms = <0>; + tap-ms = <10>; + bindings = <&kp LA(M) &kp N3>; + ) + }; + + // custom shift using mod-morph + behaviors { + cmqus: comma_question { + compatible = "zmk,behavior-mod-morph"; + label = "COMMA_QUESTION"; + #binding-cells = <0>; + bindings = <&kp COMMA>, <&kp QUESTION>; + mods = <(MOD_LSFT|MOD_RSFT)>; + }; + + dtild: dot_tilde { + compatible = "zmk,behavior-mod-morph"; + label = "DOT_TILDE"; + #binding-cells = <0>; + bindings = <&kp DOT>, <&kp TILDE>; + mods = <(MOD_LSFT|MOD_RSFT)>; + }; + }; + + // combos + combos { + compatible = "zmk,combos"; + // right hand + combo_esc { + timeout-ms = <50>; + key-positions = <16 17>; + bindings = <&kp ESC>; + }; + + combo_lgui { + timeout-ms = <50>; + key-positions = <5 6>; + bindings = <&kp LGUI>; + }; + + combo_fullscreen { + timeout-ms = <50>; + key-positions = <25 26>; + bindings = <&kp F11>; + }; + + combo_wkspc_up { + timeout-ms = <50>; + key-positions = <9 19>; + bindings = <&kp LC(UP)>; + }; + + combo_wkspc_down { + timeout-ms = <50>; + key-positions = <19 29>; + bindings = <&kp LC(DOWN)>; + }; + + combo_win_up { + timeout-ms = <50>; + key-positions = <7 8>; + bindings = <&kp LS(LG(UP))>; + }; + + combo_win_down { + timeout-ms = <50>; + key-positions = <27 28>; + bindings = <&kp LS(LG(DOWN))>; + }; + + combo_win_left { + timeout-ms = <50>; + key-positions = <17 18>; + bindings = <&kp LS(LG(LEFT))>; + }; + + combo_win_right { + timeout-ms = <50>; + key-positions = <9 18>; + bindings = <&kp LS(LG(RIGHT))>; + }; + + combo_rdelete { + timeout-ms = <50>; + key-positions = <15 16>; + bindings = <&kp DELETE>; + }; + + // left hand + combo_zoomin { + timeout-ms = <50>; + key-positions = <3 4>; + bindings = <&kp LC(PLUS)>; + }; + + combo_zoomout { + timeout-ms = <50>; + key-positions = <23 24>; + bindings = <&kp LC(MINUS)>; + }; + + combo_pgdn { + timeout-ms = <50>; + key-positions = <22 23>; + bindings = <&kp PG_DN>; + }; + + combo_pgup { + timeout-ms = <50>; + key-positions = <2 3>; + bindings = <&kp PG_UP>; + }; + + combo_ldelete { + timeout-ms = <50>; + key-positions = <13 14>; + bindings = <&kp DELETE>; + }; + + // symbols + combo_underscore { + timeout-ms = <50>; + key-positions = <11 12>; + bindings = <&kp UNDERSCORE>; + }; + + combo_minus { + timeout-ms = <50>; + key-positions = <12 13>; + bindings = <&kp MINUS>; + }; + + // others + combo_capswd { + timeout-ms = <50>; + key-positions = <2 7>; + bindings = <&caps_word>; + }; + + combo_lsym { + timeout-ms = <50>; + key-positions = <31 34>; + bindings = <&tog SYM>; + }; + + combo_lnum { + timeout-ms = <50>; + key-positions = <32 33>; + bindings = <&tog NUM>; + }; + + combo_hello { + timeout-ms = <50>; + key-positions = <30 31>; + bindings = <&hello>; + }; + + // output selection + // combo_outusb { + // timeout-ms = <50>; + // key-positions = <4 14>; + // bindings = <&out OUT_USB>; + // }; + + combo_outble { + timeout-ms = <50>; + key-positions = <4 14>; + bindings = <&out OUT_BLE>; + }; + + combo_outtog { + timeout-ms = <50>; + key-positions = <4 24>; + bindings = <&out OUT_TOG>; + }; + + // ble selection + combo_ble1 { + timeout-ms = <50>; + key-positions = <14 24>; + bindings = <&bt BT_SEL 0>; + }; + + combo_ble2 { + timeout-ms = <50>; + key-positions = <13 23>; + bindings = <&bt BT_SEL 1>; + }; + + combo_ble3 { + timeout-ms = <50>; + key-positions = <12 22>; + bindings = <&bt BT_SEL 2>; + }; + + combo_ble4 { + timeout-ms = <50>; + key-positions = <11 21>; + bindings = <&bt BT_SEL 3>; + }; + + combo_ble5 { + timeout-ms = <50>; + key-positions = <10 20>; + bindings = <&bt BT_SEL 4>; + }; + }; + + keymap { + compatible = "zmk,keymap"; + + default_layer { + label = "QWRT"; + bindings = < + &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P + &kp A &mt LSFT S < SYM D < NUM F &kp G &kp H < NUM J < SYM K &mt RSFT L &kp SQT + &kp Z &kp X &kp C &kp V &kp B &kp N &kp M &cmqus &dtild &kp SEMI + &encoder &mt LALT TAB &mt LCTL SPACE &mt RCTL RET &mt RALT BSPC &encoder + >; + sensor-bindings = <&inc_dec_kp RIGHT LEFT &inc_dec_kp UP DOWN>; + }; + + colemak_layer { + label = "CLMK"; + bindings = < + &kp Q &kp W &kp F &kp P &kp B &kp J &kp L &kp U &kp Y &kp SQT + &kp A &mt LSFT R < SYM S < NUM T &kp G &kp M < NUM N < SYM E &mt RSFT I &kp O + &kp Z &kp X &kp C &kp D &kp V &kp K &kp H &cmqus &dtild &kp SEMI + &encoder &mt LALT TAB &mt LCTL SPACE &mt RCTL RET &mt RALT BSPC &encoder + >; + sensor-bindings = <&inc_dec_kp RIGHT LEFT &inc_dec_kp UP DOWN>; + }; + + number_layer { + label = "NUM"; + bindings = < + &bt BT_NXT &none &kp UP &none &spc1 &kp KP_PLUS &kp KP_N7 &kp KP_N8 &kp KP_N9 &kp KP_MINUS + &bt BT_PRV &kp LEFT &kp DOWN &kp RIGHT &spc2 &kp KP_MULTIPLY &kp KP_N4 &kp KP_N5 &kp KP_N6 &kp KP_DIVIDE + &bt BT_CLR &none &none &none &spc3 &kp KP_EQUAL &kp KP_N1 &kp KP_N2 &kp KP_N3 &kp KP_DOT + &encoder &spc4 &kp BSPC &kp RET &kp KP_N0 &encoder + >; + sensor-bindings = <&inc_dec_kp PG_UP PG_DN &inc_dec_kp C_VOL_UP C_VOL_DN>; + }; + + symbol_layer { + label = "SYM"; + bindings = < + &none &none &kp LT &kp GT &kp PRCNT &kp AMPS &kp CARET &kp LBRC &kp RBRC &tog COLEMAK + &kp GRAVE &kp EXCL &kp MINUS &kp PLUS &kp EQUAL &kp PIPE &kp COLON &kp LPAR &kp RPAR &kp AT + &none &none &kp SLASH &kp STAR &kp BSLH &kp HASH &kp DLLR &kp LBKT &kp RBKT &none + &encoder &none &none &none &none &encoder + >; + sensor-bindings = <&inc_dec_kp PG_UP PG_DN &inc_dec_kp C_VOL_UP C_VOL_DN>; + }; + + }; +}; \ No newline at end of file diff --git a/app/boards/shields/corne-pro/corne-pro_right.overlay b/app/boards/shields/corne-pro/corne-pro_right.overlay index af78975a8dc..e060feeec61 100644 --- a/app/boards/shields/corne-pro/corne-pro_right.overlay +++ b/app/boards/shields/corne-pro/corne-pro_right.overlay @@ -8,7 +8,7 @@ // adjust matrix transform for pepripheral split &default_transform { - col-offset = <5>; + col-offset = <6>; }; // setup kscan pins diff --git a/app/boards/shields/sweep-pro/sweep-pro.dtsi b/app/boards/shields/sweep-pro/sweep-pro.dtsi index 29c83339137..120d192445c 100644 --- a/app/boards/shields/sweep-pro/sweep-pro.dtsi +++ b/app/boards/shields/sweep-pro/sweep-pro.dtsi @@ -26,7 +26,7 @@ RC(0,0) RC(0,1) RC(0,2) RC(0,3) RC(0,4) RC(0,5) RC(0,6) RC(0,7) RC(0,8) RC(0,9) RC(1,0) RC(1,1) RC(1,2) RC(1,3) RC(1,4) RC(1,5) RC(1,6) RC(1,7) RC(1,8) RC(1,9) RC(2,0) RC(2,1) RC(2,2) RC(2,3) RC(2,4) RC(2,5) RC(2,6) RC(2,7) RC(2,8) RC(2,9) - RC(3,2) RC(3,3) RC(3,4) RC(3,5) RC(3,6) RC(3,7) +RC(3,0) RC(3,1) RC(3,2) RC(3,3) RC(3,4) RC(3,5) RC(3,6) RC(3,7) RC(3,8) RC(3,9) >; }; diff --git a/app/boards/shields/sweep-pro/sweep-pro.keymap b/app/boards/shields/sweep-pro/sweep-pro.keymap index f12978df75b..76f7ba9e9d0 100644 --- a/app/boards/shields/sweep-pro/sweep-pro.keymap +++ b/app/boards/shields/sweep-pro/sweep-pro.keymap @@ -9,5 +9,4 @@ #include #include -// #include "../36key-common.dtsi" -#include "../36key-common-colemak.dtsi" \ No newline at end of file +#include "../40key-common-colemak.dtsi" \ No newline at end of file From 09b8c1c0e4f9b21fe04d6e3876e472542a4e99af Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Thu, 4 Aug 2022 18:04:46 +0800 Subject: [PATCH 046/157] backup --- app/boards/arm/nrfmacro/CMakeLists.txt | 5 ++ app/boards/arm/nrfmacro/Kconfig.defconfig | 5 ++ app/boards/arm/nrfmacro/nrfmacro.dts | 32 +++++++- .../arm/nrfmacro/trackball/CMakeLists.txt | 4 + app/boards/arm/nrfmacro/trackball/trackball.c | 61 +++++++++++++++ .../arm/nrfmacro/trackpad/CMakeLists.txt | 4 + app/boards/arm/nrfmacro/trackpad/trackpad.c | 78 +++++++++++++++++++ 7 files changed, 188 insertions(+), 1 deletion(-) create mode 100644 app/boards/arm/nrfmacro/trackball/CMakeLists.txt create mode 100644 app/boards/arm/nrfmacro/trackball/trackball.c create mode 100644 app/boards/arm/nrfmacro/trackpad/CMakeLists.txt create mode 100644 app/boards/arm/nrfmacro/trackpad/trackpad.c diff --git a/app/boards/arm/nrfmacro/CMakeLists.txt b/app/boards/arm/nrfmacro/CMakeLists.txt index 085a625b68c..e63d4a9111c 100644 --- a/app/boards/arm/nrfmacro/CMakeLists.txt +++ b/app/boards/arm/nrfmacro/CMakeLists.txt @@ -8,3 +8,8 @@ if(CONFIG_ZMK_DISPLAY AND CONFIG_ZMK_DISPLAY_STATUS_SCREEN_CUSTOM) # add_subdirectory_ifdef(CONFIG_NRFMACRO_LCD_DISPLAY lcd_screen) # add_subdirectory_ifdef(CONFIG_NRFMACRO_OLED_DISPLAY oled_screen) endif() + +if (CONFIG_NRFMACRO_POINTDEVICE) + add_subdirectory_ifdef(CONFIG_PINNACLE trackpad) + add_subdirectory_ifdef(CONFIG_PMW33XX trackball) +endif() diff --git a/app/boards/arm/nrfmacro/Kconfig.defconfig b/app/boards/arm/nrfmacro/Kconfig.defconfig index 7248dc6f885..cb691032f2c 100644 --- a/app/boards/arm/nrfmacro/Kconfig.defconfig +++ b/app/boards/arm/nrfmacro/Kconfig.defconfig @@ -234,4 +234,9 @@ menuconfig CUSTOM_WIDGET_LAYER_STATUS menuconfig CUSTOM_WIDGET_PERIPHERAL_STATUS bool "custom layer status widget" +# add point device +config NRFMACRO_POINTDEVICE + bool "add support for point device" + select SPI + endif # BOARD_NRFMACRO diff --git a/app/boards/arm/nrfmacro/nrfmacro.dts b/app/boards/arm/nrfmacro/nrfmacro.dts index 1f22cb5e113..2b4f3f89615 100644 --- a/app/boards/arm/nrfmacro/nrfmacro.dts +++ b/app/boards/arm/nrfmacro/nrfmacro.dts @@ -174,4 +174,34 @@ }; nrfmacro_spi: &spi1 {}; -nrfmacro_i2c: &i2c1 {}; \ No newline at end of file +nrfmacro_i2c: &i2c1 {}; + +&pro_micro_spi { + status = "disabled"; + sck-pin = <26>; + mosi-pin = <12>; + miso-pin = <7>; + cs-gpios = <&gpio0 22 GPIO_ACTIVE_LOW>; + + // trackpad: trackpad@0 { + // compatible = "cirque,pinnacle"; + // // status = "okay"; + // reg = <0>; + // label = "cirque trackpad"; + // spi-max-frequency = <10000000>; + // dr-gpios = <&gpio0 29 GPIO_ACTIVE_HIGH>; + // invert-x; + // sleep; + // /* no-taps; */ + // }; + + trackball: trackball@0 { + compatible = "pixart,pmw33xx"; + reg = <0>; + label = "Pixart PMW3360"; + cs-gpios = <&gpio0 22 GPIO_ACTIVE_LOW>; + motswk-gpios = <&gpio1 0 GPIO_ACTIVE_LOW>; + spi-max-frequency = <2000000>; + cpi = <600>; + }; +}; \ No newline at end of file diff --git a/app/boards/arm/nrfmacro/trackball/CMakeLists.txt b/app/boards/arm/nrfmacro/trackball/CMakeLists.txt new file mode 100644 index 00000000000..2c27e542eb6 --- /dev/null +++ b/app/boards/arm/nrfmacro/trackball/CMakeLists.txt @@ -0,0 +1,4 @@ +zephyr_library() +zephyr_library_sources(trackball.c) +zephyr_library_include_directories(${CMAKE_SOURCE_DIR}/include) +zephyr_library_include_directories(${ZEPHYR_BASE}/drivers) diff --git a/app/boards/arm/nrfmacro/trackball/trackball.c b/app/boards/arm/nrfmacro/trackball/trackball.c new file mode 100644 index 00000000000..3122c0fb569 --- /dev/null +++ b/app/boards/arm/nrfmacro/trackball/trackball.c @@ -0,0 +1,61 @@ +#include +#include + +#include +#include +#include +#include + +#define SCROLL_DIV_FACTOR 5 + +const struct device *trackball = DEVICE_DT_GET(DT_INST(0, pixart_pmw33xx)); + +LOG_MODULE_REGISTER(trackball, CONFIG_SENSOR_LOG_LEVEL); + +static void handle_trackball(const struct device *dev, const struct sensor_trigger *trig) { + int ret = sensor_sample_fetch(dev); + if (ret < 0) { + LOG_ERR("fetch: %d", ret); + return; + } + struct sensor_value dx, dy; + ret = sensor_channel_get(dev, SENSOR_CHAN_POS_DX, &dx); + if (ret < 0) { + LOG_ERR("get dx: %d", ret); + return; + } + ret = sensor_channel_get(dev, SENSOR_CHAN_POS_DY, &dy); + if (ret < 0) { + LOG_ERR("get dy: %d", ret); + return; + } + LOG_DBG("trackball %d %d", dx.val1, dy.val1); + zmk_hid_mouse_movement_set(0, 0); + zmk_hid_mouse_scroll_set(0, 0); + const uint8_t layer = zmk_keymap_highest_layer_active(); + static int8_t scroll_ver_rem = 0, scroll_hor_rem = 0; + if (layer == 2) { // lower + const int16_t total_hor = dx.val1 + scroll_hor_rem, total_ver = -(dy.val1 + scroll_ver_rem); + scroll_hor_rem = total_hor % SCROLL_DIV_FACTOR; + scroll_ver_rem = total_ver % SCROLL_DIV_FACTOR; + zmk_hid_mouse_scroll_update(total_hor / SCROLL_DIV_FACTOR, total_ver / SCROLL_DIV_FACTOR); + } else { + zmk_hid_mouse_movement_update(CLAMP(dx.val1, INT8_MIN, INT8_MAX), CLAMP(dy.val1, INT8_MIN, INT8_MAX)); + } + zmk_endpoints_send_mouse_report(); +} + +static int trackball_init() { + struct sensor_trigger trigger = { + .type = SENSOR_TRIG_DATA_READY, + .chan = SENSOR_CHAN_ALL, + }; + printk("trackball"); + if (sensor_trigger_set(trackball, &trigger, handle_trackball) < 0) { + LOG_ERR("can't set trigger"); + return -EIO; + }; + return 0; +} + +SYS_INIT(trackball_init, APPLICATION, CONFIG_ZMK_KSCAN_INIT_PRIORITY); diff --git a/app/boards/arm/nrfmacro/trackpad/CMakeLists.txt b/app/boards/arm/nrfmacro/trackpad/CMakeLists.txt new file mode 100644 index 00000000000..6148e7d61ee --- /dev/null +++ b/app/boards/arm/nrfmacro/trackpad/CMakeLists.txt @@ -0,0 +1,4 @@ +zephyr_library() +zephyr_library_sources(trackpad.c) +zephyr_library_include_directories(${CMAKE_SOURCE_DIR}/include) +zephyr_library_include_directories(${ZEPHYR_BASE}/drivers) diff --git a/app/boards/arm/nrfmacro/trackpad/trackpad.c b/app/boards/arm/nrfmacro/trackpad/trackpad.c new file mode 100644 index 00000000000..b547b2cb3f2 --- /dev/null +++ b/app/boards/arm/nrfmacro/trackpad/trackpad.c @@ -0,0 +1,78 @@ +#include +#include + +#include +#include +#include +#include + +#define SCROLL_DIV_FACTOR 5 + +const struct device *trackpad = DEVICE_DT_GET(DT_INST(0, cirque_pinnacle)); + +LOG_MODULE_REGISTER(trackpad, CONFIG_SENSOR_LOG_LEVEL); + +static void handle_trackpad(const struct device *dev, const struct sensor_trigger *trig) { + static uint8_t last_pressed = 0; + int ret = sensor_sample_fetch(dev); + if (ret < 0) { + LOG_ERR("fetch: %d", ret); + return; + } + struct sensor_value dx, dy, btn; + ret = sensor_channel_get(dev, SENSOR_CHAN_POS_DX, &dx); + if (ret < 0) { + LOG_ERR("get dx: %d", ret); + return; + } + ret = sensor_channel_get(dev, SENSOR_CHAN_POS_DY, &dy); + if (ret < 0) { + LOG_ERR("get dy: %d", ret); + return; + } + ret = sensor_channel_get(dev, SENSOR_CHAN_PRESS, &btn); + if (ret < 0) { + LOG_ERR("get btn: %d", ret); + return; + } + LOG_DBG("trackpad %d %d %02x", dx.val1, dy.val1, btn.val1); + zmk_hid_mouse_movement_set(0, 0); + zmk_hid_mouse_scroll_set(0, 0); + const uint8_t layer = zmk_keymap_highest_layer_active(); + uint8_t button; + static uint8_t last_button = 0; + static int8_t scroll_ver_rem = 0, scroll_hor_rem = 0; + if (layer == 2) { // lower + const int16_t total_hor = dx.val1 + scroll_hor_rem, total_ver = -(dy.val1 + scroll_ver_rem); + scroll_hor_rem = total_hor % SCROLL_DIV_FACTOR; + scroll_ver_rem = total_ver % SCROLL_DIV_FACTOR; + zmk_hid_mouse_scroll_update(total_hor / SCROLL_DIV_FACTOR, total_ver / SCROLL_DIV_FACTOR); + button = RCLK; + } else { + zmk_hid_mouse_movement_update(CLAMP(dx.val1, INT8_MIN, INT8_MAX), CLAMP(dy.val1, INT8_MIN, INT8_MAX)); + button = LCLK; + } + if (!last_pressed && btn.val1) { + zmk_hid_mouse_buttons_press(button); + last_button = button; + } else if (last_pressed && !btn.val1) { + zmk_hid_mouse_buttons_release(last_button); + } + zmk_endpoints_send_mouse_report(); + last_pressed = btn.val1; +} + +static int trackpad_init() { + struct sensor_trigger trigger = { + .type = SENSOR_TRIG_DATA_READY, + .chan = SENSOR_CHAN_ALL, + }; + printk("trackpad"); + if (sensor_trigger_set(trackpad, &trigger, handle_trackpad) < 0) { + LOG_ERR("can't set trigger"); + return -EIO; + }; + return 0; +} + +SYS_INIT(trackpad_init, APPLICATION, CONFIG_ZMK_KSCAN_INIT_PRIORITY); From 37b77e5d75dbc173d715dcd625fe5b053efeb409 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Thu, 4 Aug 2022 19:15:53 +0800 Subject: [PATCH 047/157] bugfix --- app/drivers/sensor/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/drivers/sensor/CMakeLists.txt b/app/drivers/sensor/CMakeLists.txt index 0d9fc9bd7ba..80e5dfe69db 100644 --- a/app/drivers/sensor/CMakeLists.txt +++ b/app/drivers/sensor/CMakeLists.txt @@ -3,5 +3,5 @@ add_subdirectory_ifdef(CONFIG_ZMK_BATTERY battery) add_subdirectory_ifdef(CONFIG_EC11 ec11) -add_subdirectory_ifdef(CONFIG_PMW33xx pmw33xx) +add_subdirectory_ifdef(CONFIG_PMW33XX pmw33xx) add_subdirectory_ifdef(CONFIG_PINNACLE cirque_trackpad) From 18176b62ab1516b200cdf95a74973fbebd2d20c3 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Sat, 6 Aug 2022 08:27:35 +0800 Subject: [PATCH 048/157] change trackball cpi --- app/boards/arm/nrfmacro/nrfmacro.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/boards/arm/nrfmacro/nrfmacro.dts b/app/boards/arm/nrfmacro/nrfmacro.dts index 2b4f3f89615..dc05a0d709d 100644 --- a/app/boards/arm/nrfmacro/nrfmacro.dts +++ b/app/boards/arm/nrfmacro/nrfmacro.dts @@ -202,6 +202,6 @@ nrfmacro_i2c: &i2c1 {}; cs-gpios = <&gpio0 22 GPIO_ACTIVE_LOW>; motswk-gpios = <&gpio1 0 GPIO_ACTIVE_LOW>; spi-max-frequency = <2000000>; - cpi = <600>; + cpi = <700>; }; }; \ No newline at end of file From 0cf8c1ba6c6b40b1291f72bfff6d990182669f80 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Wed, 10 Aug 2022 07:00:46 +0800 Subject: [PATCH 049/157] add min interval for mouse report update --- app/boards/arm/nrfmacro/nrfmacro.dts | 2 +- app/boards/arm/nrfmacro/trackball/trackball.c | 47 ++++++++++++++++--- app/drivers/sensor/pmw33xx/pmw33xx.c | 2 +- 3 files changed, 42 insertions(+), 9 deletions(-) diff --git a/app/boards/arm/nrfmacro/nrfmacro.dts b/app/boards/arm/nrfmacro/nrfmacro.dts index dc05a0d709d..3144012800f 100644 --- a/app/boards/arm/nrfmacro/nrfmacro.dts +++ b/app/boards/arm/nrfmacro/nrfmacro.dts @@ -202,6 +202,6 @@ nrfmacro_i2c: &i2c1 {}; cs-gpios = <&gpio0 22 GPIO_ACTIVE_LOW>; motswk-gpios = <&gpio1 0 GPIO_ACTIVE_LOW>; spi-max-frequency = <2000000>; - cpi = <700>; + cpi = <300>; }; }; \ No newline at end of file diff --git a/app/boards/arm/nrfmacro/trackball/trackball.c b/app/boards/arm/nrfmacro/trackball/trackball.c index 3122c0fb569..7a31dedb84e 100644 --- a/app/boards/arm/nrfmacro/trackball/trackball.c +++ b/app/boards/arm/nrfmacro/trackball/trackball.c @@ -8,11 +8,26 @@ #define SCROLL_DIV_FACTOR 5 + +/* test */ +// in ms +#define MOUSE_UPDATE_INTERVAL_MIN 1 +static int64_t last_update_time = 0; +static int64_t current_update_time = 0; +static int64_t acc_interval = 0; +static int32_t acc_dx=0, acc_dy=0; +/*******/ + const struct device *trackball = DEVICE_DT_GET(DT_INST(0, pixart_pmw33xx)); LOG_MODULE_REGISTER(trackball, CONFIG_SENSOR_LOG_LEVEL); static void handle_trackball(const struct device *dev, const struct sensor_trigger *trig) { + /* test */ + current_update_time = k_uptime_get(); + acc_interval += (current_update_time - last_update_time); + /*****/ + int ret = sensor_sample_fetch(dev); if (ret < 0) { LOG_ERR("fetch: %d", ret); @@ -30,19 +45,37 @@ static void handle_trackball(const struct device *dev, const struct sensor_trigg return; } LOG_DBG("trackball %d %d", dx.val1, dy.val1); - zmk_hid_mouse_movement_set(0, 0); - zmk_hid_mouse_scroll_set(0, 0); - const uint8_t layer = zmk_keymap_highest_layer_active(); - static int8_t scroll_ver_rem = 0, scroll_hor_rem = 0; - if (layer == 2) { // lower + + /* test */ + acc_dx += dx.val1; + acc_dy += dy.val1; + + LOG_DBG("interval: %l %l", (current_update_time-last_update_time), acc_interval); + + if(acc_interval > MOUSE_UPDATE_INTERVAL_MIN) { + zmk_hid_mouse_movement_set(0, 0); + zmk_hid_mouse_scroll_set(0, 0); + + const uint8_t layer = zmk_keymap_highest_layer_active(); + static int8_t scroll_ver_rem = 0, scroll_hor_rem = 0; + if (layer == 2) { // lower const int16_t total_hor = dx.val1 + scroll_hor_rem, total_ver = -(dy.val1 + scroll_ver_rem); scroll_hor_rem = total_hor % SCROLL_DIV_FACTOR; scroll_ver_rem = total_ver % SCROLL_DIV_FACTOR; zmk_hid_mouse_scroll_update(total_hor / SCROLL_DIV_FACTOR, total_ver / SCROLL_DIV_FACTOR); - } else { + } else { zmk_hid_mouse_movement_update(CLAMP(dx.val1, INT8_MIN, INT8_MAX), CLAMP(dy.val1, INT8_MIN, INT8_MAX)); + } + zmk_endpoints_send_mouse_report(); + + /* test */ + LOG_DBG("update interval: %l %l", (current_update_time-last_update_time), acc_interval); + acc_dx = 0; + acc_dy = 0; + acc_interval = 0; } - zmk_endpoints_send_mouse_report(); + + last_update_time = current_update_time; } static int trackball_init() { diff --git a/app/drivers/sensor/pmw33xx/pmw33xx.c b/app/drivers/sensor/pmw33xx/pmw33xx.c index 486a885c098..f112f522ce7 100644 --- a/app/drivers/sensor/pmw33xx/pmw33xx.c +++ b/app/drivers/sensor/pmw33xx/pmw33xx.c @@ -382,7 +382,7 @@ static int pmw33xx_init(const struct device *dev) { .bus_name = DT_INST_BUS_LABEL(n), .bus_init = pmw33xx_spi_init, \ .bus_cfg = {.spi_cfg = PMW33XX_SPI_CFG(n)}, \ .disable_rest = DT_INST_NODE_HAS_PROP(n, disable_rest), \ - COND_CODE_0(DT_INST_NODE_HAS_PROP(n, cpi), (0), (DT_INST_PROP(n, cpi))) \ + .cpi = COND_CODE_0(DT_INST_NODE_HAS_PROP(n, cpi), (0), (DT_INST_PROP(n, cpi))) \ COND_CODE_1(CONFIG_PMW33XX_TRIGGER, \ (, PMW33XX_GPIO_DT_SPEC_GET_BY_IDX(DT_DRV_INST(n), motswk_gpios, 0)), ()) \ } From 50575649fcf5073ef8a0c4ef684d28fa21679362 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Wed, 10 Aug 2022 08:02:20 +0800 Subject: [PATCH 050/157] backup --- app/boards/arm/nrfmacro/trackball/trackball.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/boards/arm/nrfmacro/trackball/trackball.c b/app/boards/arm/nrfmacro/trackball/trackball.c index 7a31dedb84e..9a0c4d06866 100644 --- a/app/boards/arm/nrfmacro/trackball/trackball.c +++ b/app/boards/arm/nrfmacro/trackball/trackball.c @@ -11,7 +11,7 @@ /* test */ // in ms -#define MOUSE_UPDATE_INTERVAL_MIN 1 +#define MOUSE_UPDATE_INTERVAL_MIN 0.5 static int64_t last_update_time = 0; static int64_t current_update_time = 0; static int64_t acc_interval = 0; @@ -46,13 +46,13 @@ static void handle_trackball(const struct device *dev, const struct sensor_trigg } LOG_DBG("trackball %d %d", dx.val1, dy.val1); - /* test */ + /* test */ acc_dx += dx.val1; acc_dy += dy.val1; - LOG_DBG("interval: %l %l", (current_update_time-last_update_time), acc_interval); + /* LOG_DBG("interval: %l %l", (current_update_time-last_update_time), acc_interval); */ - if(acc_interval > MOUSE_UPDATE_INTERVAL_MIN) { + if(acc_interval >= MOUSE_UPDATE_INTERVAL_MIN) { zmk_hid_mouse_movement_set(0, 0); zmk_hid_mouse_scroll_set(0, 0); @@ -69,7 +69,7 @@ static void handle_trackball(const struct device *dev, const struct sensor_trigg zmk_endpoints_send_mouse_report(); /* test */ - LOG_DBG("update interval: %l %l", (current_update_time-last_update_time), acc_interval); + /* LOG_DBG("update interval: %l %l", (current_update_time-last_update_time), acc_interval); */ acc_dx = 0; acc_dy = 0; acc_interval = 0; From 74a4fc92d60c8b3a4aee279741acb2b5ab39ea83 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Thu, 11 Aug 2022 09:58:49 +0800 Subject: [PATCH 051/157] backup --- app/boards/arm/nrfmacro/nrfmacro.dts | 4 ++-- app/drivers/sensor/cirque_trackpad/CMakeLists.txt | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/boards/arm/nrfmacro/nrfmacro.dts b/app/boards/arm/nrfmacro/nrfmacro.dts index 3144012800f..9e1d8c4f3c0 100644 --- a/app/boards/arm/nrfmacro/nrfmacro.dts +++ b/app/boards/arm/nrfmacro/nrfmacro.dts @@ -188,8 +188,8 @@ nrfmacro_i2c: &i2c1 {}; // // status = "okay"; // reg = <0>; // label = "cirque trackpad"; - // spi-max-frequency = <10000000>; - // dr-gpios = <&gpio0 29 GPIO_ACTIVE_HIGH>; + // spi-max-frequency = <1000000>; + // dr-gpios = <&gpio1 0 GPIO_ACTIVE_HIGH>; // invert-x; // sleep; // /* no-taps; */ diff --git a/app/drivers/sensor/cirque_trackpad/CMakeLists.txt b/app/drivers/sensor/cirque_trackpad/CMakeLists.txt index 92b6744cc53..fb014ffefb8 100644 --- a/app/drivers/sensor/cirque_trackpad/CMakeLists.txt +++ b/app/drivers/sensor/cirque_trackpad/CMakeLists.txt @@ -1,8 +1,9 @@ # Copyright (c) 2022 The ZMK Contributors # SPDX-License-Identifier: MIT -zephyr_include_directories(.) - zephyr_library() +zephyr_library_include_directories(.) +zephyr_library_include_directories(${CMAKE_SOURCE_DIR}/include) + zephyr_library_sources(cirque_trackpad.c) From 60fe1c64cf2bfc019bec9a8d5491850c45fd4707 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Thu, 11 Aug 2022 12:14:52 +0800 Subject: [PATCH 052/157] backup --- app/boards/arm/nrfmacro/nrfmacro.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/boards/arm/nrfmacro/nrfmacro.dts b/app/boards/arm/nrfmacro/nrfmacro.dts index 9e1d8c4f3c0..63cb1705c6a 100644 --- a/app/boards/arm/nrfmacro/nrfmacro.dts +++ b/app/boards/arm/nrfmacro/nrfmacro.dts @@ -202,6 +202,6 @@ nrfmacro_i2c: &i2c1 {}; cs-gpios = <&gpio0 22 GPIO_ACTIVE_LOW>; motswk-gpios = <&gpio1 0 GPIO_ACTIVE_LOW>; spi-max-frequency = <2000000>; - cpi = <300>; + cpi = <600>; }; }; \ No newline at end of file From f7ae567e150d4343d21bf3dd8e9a6f51c263862c Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Fri, 12 Aug 2022 12:47:07 +0800 Subject: [PATCH 053/157] bugfix --- app/boards/arm/nrfmacro/nrfmacro.dts | 2 +- app/drivers/sensor/pmw33xx/pmw33xx.c | 20 +++++++++++++++++--- app/drivers/sensor/pmw33xx/pmw33xx_trigger.c | 6 +++--- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/app/boards/arm/nrfmacro/nrfmacro.dts b/app/boards/arm/nrfmacro/nrfmacro.dts index 63cb1705c6a..1ed8a395561 100644 --- a/app/boards/arm/nrfmacro/nrfmacro.dts +++ b/app/boards/arm/nrfmacro/nrfmacro.dts @@ -200,7 +200,7 @@ nrfmacro_i2c: &i2c1 {}; reg = <0>; label = "Pixart PMW3360"; cs-gpios = <&gpio0 22 GPIO_ACTIVE_LOW>; - motswk-gpios = <&gpio1 0 GPIO_ACTIVE_LOW>; + motswk-gpios = <&gpio1 0 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; spi-max-frequency = <2000000>; cpi = <600>; }; diff --git a/app/drivers/sensor/pmw33xx/pmw33xx.c b/app/drivers/sensor/pmw33xx/pmw33xx.c index f112f522ce7..21c7168bb7c 100644 --- a/app/drivers/sensor/pmw33xx/pmw33xx.c +++ b/app/drivers/sensor/pmw33xx/pmw33xx.c @@ -74,7 +74,8 @@ static int pmw33xx_access(const struct device *dev, const uint8_t reg, uint8_t * pmw33xx_cs_select(cs_gpio_cfg, 0); int err = spi_write(data->bus, spi_cfg, &tx); - k_sleep(K_USEC(120)); // Tsrad + /* k_sleep(K_USEC(120)); // Tsrad */ + k_sleep(K_USEC(180)); // Tsrad if (err) { pmw33xx_cs_select(cs_gpio_cfg, 1); return err; @@ -85,7 +86,8 @@ static int pmw33xx_access(const struct device *dev, const uint8_t reg, uint8_t * else err = spi_read(data->bus, spi_cfg, &rx); pmw33xx_cs_select(cs_gpio_cfg, 1); - k_sleep(K_USEC(160)); + /* k_sleep(K_USEC(160)); */ + k_sleep(K_USEC(180)); if ((reg & PMW33XX_WR_MASK) == 0 && value != NULL) *value = result[0]; return err; @@ -271,6 +273,8 @@ static int pmw33xx_set_cpi(const struct device *dev, uint16_t cpi) { static int pmw33xx_init_chip(const struct device *dev) { const struct pmw33xx_config *const config = dev->config; const struct pmw33xx_gpio_dt_spec *cs_gpio_cfg = &config->bus_cfg.spi_cfg->cs_spec; + + // power reset pmw33xx_cs_select(cs_gpio_cfg, 1); k_sleep(K_MSEC(1)); @@ -279,6 +283,9 @@ static int pmw33xx_init_chip(const struct device *dev) { LOG_ERR("could not reset %d", err); return -EIO; } + + // confirm pid + k_sleep(K_MSEC(50)); uint8_t pid = 0x0; err = pmw33xx_read_reg(dev, PMW33XX_REG_PID, &pid); if (err) { @@ -289,14 +296,19 @@ static int pmw33xx_init_chip(const struct device *dev) { LOG_ERR("pid does not match expected: got (%x), expected(%x)", pid, PMW33XX_PID); return -EIO; } + + // enable rest pmw33xx_write_reg(dev, PMW33XX_REG_CONFIG2, config->disable_rest ? 0x00 : PMW33XX_RESTEN); // set rest enable + // update fireware err = pmw33xx_write_srom(dev); if (err) { LOG_ERR("could not upload srom %d", err); return -EIO; } + + // confirm firmware update uint8_t srom_run = 0x0; err = pmw33xx_read_reg(dev, PMW33XX_REG_OBSERVATION, &srom_run); if (err) { @@ -319,10 +331,12 @@ static int pmw33xx_init_chip(const struct device *dev) { return -EIO; } + // enbale motion burst and discard initial data pmw33xx_write_reg(dev, PMW33XX_REG_BURST, 0x01); struct pmw33xx_motion_burst val; pmw33xx_read_motion_burst(dev, &val); // read and throwout initial motion data + // set cpi if (config->cpi > PMW33XX_CPI_MIN && config->cpi < PMW33XX_CPI_MAX) return pmw33xx_set_cpi(dev, config->cpi); return 0; @@ -364,7 +378,7 @@ static int pmw33xx_init(const struct device *dev) { { \ .frequency = DT_INST_PROP(n, spi_max_frequency), \ .operation = \ - (SPI_WORD_SET(8) | SPI_OP_MODE_MASTER | SPI_MODE_CPOL | SPI_MODE_CPHA), \ + (SPI_WORD_SET(8) | SPI_TRANSFER_MSB | SPI_OP_MODE_MASTER | SPI_MODE_CPOL | SPI_MODE_CPHA), \ .slave = DT_INST_REG_ADDR(n), \ }, \ .cs_spec = PMW33XX_GPIO_DT_SPEC_GET_BY_IDX(DT_DRV_INST(n), cs_gpios, 0), \ diff --git a/app/drivers/sensor/pmw33xx/pmw33xx_trigger.c b/app/drivers/sensor/pmw33xx/pmw33xx_trigger.c index e254faf1de1..18a7f2ae7d8 100644 --- a/app/drivers/sensor/pmw33xx/pmw33xx_trigger.c +++ b/app/drivers/sensor/pmw33xx/pmw33xx_trigger.c @@ -23,9 +23,8 @@ static inline void setup_int(const struct device *dev, bool enable) { struct pmw33xx_data *data = dev->data; const struct pmw33xx_config *cfg = dev->config; - gpio_pin_configure(cfg->motswk_spec.port, cfg->motswk_spec.pin, cfg->motswk_spec.dt_flags); if (gpio_pin_interrupt_configure(cfg->motswk_spec.port, cfg->motswk_spec.pin, - enable ? GPIO_INT_EDGE_TO_ACTIVE : GPIO_INT_DISABLE)) { + enable ? GPIO_INT_LEVEL_ACTIVE : GPIO_INT_DISABLE)) { LOG_WRN("Unable to set MOTSWK GPIO interrupt"); } } @@ -104,6 +103,7 @@ int pmw33xx_init_interrupt(const struct device *dev) { drv_data->dev = dev; /* setup gpio interrupt */ + gpio_pin_configure(drv_cfg->motswk_spec.port, drv_cfg->motswk_spec.pin, GPIO_INPUT | drv_cfg->motswk_spec.dt_flags); gpio_init_callback(&drv_data->motswk_gpio_cb, pmw33xx_motswk_gpio_callback, BIT(drv_cfg->motswk_spec.pin)); @@ -124,4 +124,4 @@ int pmw33xx_init_interrupt(const struct device *dev) { #endif return 0; -} \ No newline at end of file +} From 4a6702dc57832b98320e0f158d5bcd4b305af4a0 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Fri, 12 Aug 2022 16:54:35 +0800 Subject: [PATCH 054/157] backup --- app/boards/arm/nrfmacro/trackball/trackball.c | 7 ++++--- app/drivers/sensor/pmw33xx/pmw33xx_trigger.c | 1 - app/src/behaviors/behavior_mod_morph.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/boards/arm/nrfmacro/trackball/trackball.c b/app/boards/arm/nrfmacro/trackball/trackball.c index 9a0c4d06866..c38d2aa24de 100644 --- a/app/boards/arm/nrfmacro/trackball/trackball.c +++ b/app/boards/arm/nrfmacro/trackball/trackball.c @@ -11,7 +11,7 @@ /* test */ // in ms -#define MOUSE_UPDATE_INTERVAL_MIN 0.5 +#define MOUSE_UPDATE_INTERVAL_MIN 0.1 static int64_t last_update_time = 0; static int64_t current_update_time = 0; static int64_t acc_interval = 0; @@ -50,7 +50,7 @@ static void handle_trackball(const struct device *dev, const struct sensor_trigg acc_dx += dx.val1; acc_dy += dy.val1; - /* LOG_DBG("interval: %l %l", (current_update_time-last_update_time), acc_interval); */ + LOG_DBG("interval: %lld %lld", (current_update_time-last_update_time), acc_interval); if(acc_interval >= MOUSE_UPDATE_INTERVAL_MIN) { zmk_hid_mouse_movement_set(0, 0); @@ -69,7 +69,8 @@ static void handle_trackball(const struct device *dev, const struct sensor_trigg zmk_endpoints_send_mouse_report(); /* test */ - /* LOG_DBG("update interval: %l %l", (current_update_time-last_update_time), acc_interval); */ + LOG_DBG("update interval: %lld %lld ; trackball: %d %d", (current_update_time-last_update_time), acc_interval, acc_dx, acc_dy); + acc_dx = 0; acc_dy = 0; acc_interval = 0; diff --git a/app/drivers/sensor/pmw33xx/pmw33xx_trigger.c b/app/drivers/sensor/pmw33xx/pmw33xx_trigger.c index 18a7f2ae7d8..df84d819b80 100644 --- a/app/drivers/sensor/pmw33xx/pmw33xx_trigger.c +++ b/app/drivers/sensor/pmw33xx/pmw33xx_trigger.c @@ -20,7 +20,6 @@ LOG_MODULE_DECLARE(PMW33XX, CONFIG_SENSOR_LOG_LEVEL); static inline void setup_int(const struct device *dev, bool enable) { - struct pmw33xx_data *data = dev->data; const struct pmw33xx_config *cfg = dev->config; if (gpio_pin_interrupt_configure(cfg->motswk_spec.port, cfg->motswk_spec.pin, diff --git a/app/src/behaviors/behavior_mod_morph.c b/app/src/behaviors/behavior_mod_morph.c index 3be3ba3de6e..b13a7e6a401 100644 --- a/app/src/behaviors/behavior_mod_morph.c +++ b/app/src/behaviors/behavior_mod_morph.c @@ -46,7 +46,7 @@ static int on_mod_morph_binding_pressed(struct zmk_behavior_binding *binding, } if (zmk_hid_get_explicit_mods() & cfg->mods) { - zmk_mod_flags_t trigger_mods = zmk_hid_get_explicit_mods() & cfg->mods; + /* zmk_mod_flags_t trigger_mods = zmk_hid_get_explicit_mods() & cfg->mods; */ zmk_hid_masked_modifiers_set(cfg->masked_mods); data->pressed_binding = (struct zmk_behavior_binding *)&cfg->morph_binding; } else { From b2f31f4cb913a116092ed58963561c6391e47690 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Fri, 12 Aug 2022 22:04:10 +0800 Subject: [PATCH 055/157] backup --- app/boards/arm/nrfmacro/trackball/trackball.c | 64 ++++++++----------- 1 file changed, 27 insertions(+), 37 deletions(-) diff --git a/app/boards/arm/nrfmacro/trackball/trackball.c b/app/boards/arm/nrfmacro/trackball/trackball.c index c38d2aa24de..3207689998f 100644 --- a/app/boards/arm/nrfmacro/trackball/trackball.c +++ b/app/boards/arm/nrfmacro/trackball/trackball.c @@ -9,31 +9,31 @@ #define SCROLL_DIV_FACTOR 5 -/* test */ +#if IS_ENABLED(CONFIG_ZMK_USB_LOGGING) // in ms -#define MOUSE_UPDATE_INTERVAL_MIN 0.1 static int64_t last_update_time = 0; static int64_t current_update_time = 0; -static int64_t acc_interval = 0; -static int32_t acc_dx=0, acc_dy=0; -/*******/ +#endif + +static struct sensor_value dx, dy; const struct device *trackball = DEVICE_DT_GET(DT_INST(0, pixart_pmw33xx)); LOG_MODULE_REGISTER(trackball, CONFIG_SENSOR_LOG_LEVEL); static void handle_trackball(const struct device *dev, const struct sensor_trigger *trig) { - /* test */ +#if IS_ENABLED(CONFIG_ZMK_USB_LOGGING) current_update_time = k_uptime_get(); - acc_interval += (current_update_time - last_update_time); - /*****/ +#endif + // fetch latest position from sensor int ret = sensor_sample_fetch(dev); if (ret < 0) { LOG_ERR("fetch: %d", ret); return; } - struct sensor_value dx, dy; + + // get the x, y delta ret = sensor_channel_get(dev, SENSOR_CHAN_POS_DX, &dx); if (ret < 0) { LOG_ERR("get dx: %d", ret); @@ -46,37 +46,27 @@ static void handle_trackball(const struct device *dev, const struct sensor_trigg } LOG_DBG("trackball %d %d", dx.val1, dy.val1); - /* test */ - acc_dx += dx.val1; - acc_dy += dy.val1; - - LOG_DBG("interval: %lld %lld", (current_update_time-last_update_time), acc_interval); - - if(acc_interval >= MOUSE_UPDATE_INTERVAL_MIN) { - zmk_hid_mouse_movement_set(0, 0); - zmk_hid_mouse_scroll_set(0, 0); - - const uint8_t layer = zmk_keymap_highest_layer_active(); - static int8_t scroll_ver_rem = 0, scroll_hor_rem = 0; - if (layer == 2) { // lower - const int16_t total_hor = dx.val1 + scroll_hor_rem, total_ver = -(dy.val1 + scroll_ver_rem); - scroll_hor_rem = total_hor % SCROLL_DIV_FACTOR; - scroll_ver_rem = total_ver % SCROLL_DIV_FACTOR; - zmk_hid_mouse_scroll_update(total_hor / SCROLL_DIV_FACTOR, total_ver / SCROLL_DIV_FACTOR); - } else { - zmk_hid_mouse_movement_update(CLAMP(dx.val1, INT8_MIN, INT8_MAX), CLAMP(dy.val1, INT8_MIN, INT8_MAX)); - } - zmk_endpoints_send_mouse_report(); + // update x, y position + zmk_hid_mouse_movement_set(0, 0); + zmk_hid_mouse_scroll_set(0, 0); + + const uint8_t layer = zmk_keymap_highest_layer_active(); + static int8_t scroll_ver_rem = 0, scroll_hor_rem = 0; + if (layer == 2) { // lower + const int16_t total_hor = dx.val1 + scroll_hor_rem, total_ver = -(dy.val1 + scroll_ver_rem); + scroll_hor_rem = total_hor % SCROLL_DIV_FACTOR; + scroll_ver_rem = total_ver % SCROLL_DIV_FACTOR; + zmk_hid_mouse_scroll_update(total_hor / SCROLL_DIV_FACTOR, total_ver / SCROLL_DIV_FACTOR); + } else { + zmk_hid_mouse_movement_update(CLAMP(dx.val1, INT8_MIN, INT8_MAX), CLAMP(dy.val1, INT8_MIN, INT8_MAX)); + } + zmk_endpoints_send_mouse_report(); /* test */ - LOG_DBG("update interval: %lld %lld ; trackball: %d %d", (current_update_time-last_update_time), acc_interval, acc_dx, acc_dy); - - acc_dx = 0; - acc_dy = 0; - acc_interval = 0; - } - +#if IS_ENABLED(CONFIG_ZMK_USB_LOGGING) + LOG_DBG("update interval: %lld ; trackball: %d %d", (current_update_time-last_update_time), dx.val1, dy.val1); last_update_time = current_update_time; +#endif } static int trackball_init() { From 8fcd571eab7db61e6945aa092fbde361779a7e77 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Sun, 14 Aug 2022 15:17:39 +0800 Subject: [PATCH 056/157] [pmw3360] better driver implemtation following datasheet v1.5 --- app/boards/arm/nrfmacro/nrfmacro.dts | 2 +- app/drivers/sensor/pmw33xx/pmw33xx.c | 56 ++++++++++++++++++---------- app/drivers/sensor/pmw33xx/pmw33xx.h | 6 +-- 3 files changed, 40 insertions(+), 24 deletions(-) diff --git a/app/boards/arm/nrfmacro/nrfmacro.dts b/app/boards/arm/nrfmacro/nrfmacro.dts index 1ed8a395561..229b93c248a 100644 --- a/app/boards/arm/nrfmacro/nrfmacro.dts +++ b/app/boards/arm/nrfmacro/nrfmacro.dts @@ -202,6 +202,6 @@ nrfmacro_i2c: &i2c1 {}; cs-gpios = <&gpio0 22 GPIO_ACTIVE_LOW>; motswk-gpios = <&gpio1 0 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; spi-max-frequency = <2000000>; - cpi = <600>; + cpi = <400>; }; }; \ No newline at end of file diff --git a/app/drivers/sensor/pmw33xx/pmw33xx.c b/app/drivers/sensor/pmw33xx/pmw33xx.c index 21c7168bb7c..574fa8aa507 100644 --- a/app/drivers/sensor/pmw33xx/pmw33xx.c +++ b/app/drivers/sensor/pmw33xx/pmw33xx.c @@ -115,10 +115,18 @@ static int pmw33xx_write_srom(const struct device *dev) { .count = 1, }; + // 1. disable rest + pmw33xx_write_reg(dev, PMW33XX_REG_CONFIG2, 0x00); + + // 2. downlaod init cmd pmw33xx_write_reg(dev, PMW33XX_REG_SROM_EN, PMW33XX_SROM_DWNLD_CMD); + k_sleep(K_USEC(15)); + + // 3. download start cmd pmw33xx_write_reg(dev, PMW33XX_REG_SROM_EN, PMW33XX_SROM_DWNLD_START_CMD); + // 4. transmit the srom firmware pmw33xx_cs_select(cs_gpio_cfg, 0); int err = spi_write(data->bus, spi_cfg, &tx); @@ -140,6 +148,8 @@ static int pmw33xx_write_srom(const struct device *dev) { } pmw33xx_cs_select(cs_gpio_cfg, 1); + + // 5. finish and exit k_sleep(K_MSEC(2)); // Tbexit return err; } @@ -274,32 +284,22 @@ static int pmw33xx_init_chip(const struct device *dev) { const struct pmw33xx_config *const config = dev->config; const struct pmw33xx_gpio_dt_spec *cs_gpio_cfg = &config->bus_cfg.spi_cfg->cs_spec; - // power reset + // reset spi port pmw33xx_cs_select(cs_gpio_cfg, 1); + pmw33xx_cs_select(cs_gpio_cfg, 0); k_sleep(K_MSEC(1)); + // power reset int err = pmw33xx_write_reg(dev, PMW33XX_REG_PWR_UP_RST, PMW33XX_RESET_CMD); if (err) { LOG_ERR("could not reset %d", err); return -EIO; } - - // confirm pid k_sleep(K_MSEC(50)); - uint8_t pid = 0x0; - err = pmw33xx_read_reg(dev, PMW33XX_REG_PID, &pid); - if (err) { - LOG_ERR("could not reset %d", err); - return -EIO; - } - if (pid != PMW33XX_PID) { - LOG_ERR("pid does not match expected: got (%x), expected(%x)", pid, PMW33XX_PID); - return -EIO; - } - // enable rest - pmw33xx_write_reg(dev, PMW33XX_REG_CONFIG2, - config->disable_rest ? 0x00 : PMW33XX_RESTEN); // set rest enable + // clear motion data + struct pmw33xx_motion_burst val; + pmw33xx_read_motion_burst(dev, &val); // read and throwout initial motion data // update fireware err = pmw33xx_write_srom(dev); @@ -308,7 +308,7 @@ static int pmw33xx_init_chip(const struct device *dev) { return -EIO; } - // confirm firmware update + // confirm srom running status uint8_t srom_run = 0x0; err = pmw33xx_read_reg(dev, PMW33XX_REG_OBSERVATION, &srom_run); if (err) { @@ -320,6 +320,7 @@ static int pmw33xx_init_chip(const struct device *dev) { return -EIO; } + // confirm firmware update by reading srom version, todo: comparison needed uint8_t srom_id = 0x0; err = pmw33xx_read_reg(dev, PMW33XX_REG_SROM_ID, &srom_id); if (err) { @@ -331,14 +332,29 @@ static int pmw33xx_init_chip(const struct device *dev) { return -EIO; } - // enbale motion burst and discard initial data + // confirm pid + uint8_t pid = 0x0; + err = pmw33xx_read_reg(dev, PMW33XX_REG_PID, &pid); + if (err) { + LOG_ERR("could not reset %d", err); + return -EIO; + } + if (pid != PMW33XX_PID) { + LOG_ERR("pid does not match expected: got (%x), expected(%x)", pid, PMW33XX_PID); + return -EIO; + } + + // enable rest + pmw33xx_write_reg(dev, PMW33XX_REG_CONFIG2, + config->disable_rest ? 0x00 : PMW33XX_RESTEN); // set rest enable + + // enbale motion burst pmw33xx_write_reg(dev, PMW33XX_REG_BURST, 0x01); - struct pmw33xx_motion_burst val; - pmw33xx_read_motion_burst(dev, &val); // read and throwout initial motion data // set cpi if (config->cpi > PMW33XX_CPI_MIN && config->cpi < PMW33XX_CPI_MAX) return pmw33xx_set_cpi(dev, config->cpi); + return 0; } diff --git a/app/drivers/sensor/pmw33xx/pmw33xx.h b/app/drivers/sensor/pmw33xx/pmw33xx.h index 31119e65899..2ecb67bc157 100644 --- a/app/drivers/sensor/pmw33xx/pmw33xx.h +++ b/app/drivers/sensor/pmw33xx/pmw33xx.h @@ -45,9 +45,9 @@ #define PMW33XX_REG_SROM_BURST 0x62 /* SROM CMDs */ -#define PMW33XX_SROM_CRC_CMD 0x15 -#define PMW33XX_SROM_DWNLD_CMD 0x1D -#define PMW33XX_SROM_DWNLD_START_CMD 0x18 +#define PMW33XX_SROM_CRC_CMD 0x15 // self-test +#define PMW33XX_SROM_DWNLD_CMD 0x1D // init downlaod +#define PMW33XX_SROM_DWNLD_START_CMD 0x18 // start download /* CPI Registers */ #define PMW33XX_3360_REG_CPI 0x0F From d1930130d4209203ea7602728a46f4245baef172 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Tue, 16 Aug 2022 11:17:51 +0800 Subject: [PATCH 057/157] [trackball] seperate position fetch and report into two functions --- app/boards/arm/nrfmacro/trackball/trackball.c | 65 ++++++++++++------- 1 file changed, 40 insertions(+), 25 deletions(-) diff --git a/app/boards/arm/nrfmacro/trackball/trackball.c b/app/boards/arm/nrfmacro/trackball/trackball.c index 3207689998f..6d57a324b8d 100644 --- a/app/boards/arm/nrfmacro/trackball/trackball.c +++ b/app/boards/arm/nrfmacro/trackball/trackball.c @@ -7,12 +7,14 @@ #include #define SCROLL_DIV_FACTOR 5 +#define SCROLL_LAYER_INDEX 4 -#if IS_ENABLED(CONFIG_ZMK_USB_LOGGING) -// in ms +#if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) +// in us static int64_t last_update_time = 0; static int64_t current_update_time = 0; +static int64_t update_duration = 0; #endif static struct sensor_value dx, dy; @@ -21,9 +23,33 @@ const struct device *trackball = DEVICE_DT_GET(DT_INST(0, pixart_pmw33xx)); LOG_MODULE_REGISTER(trackball, CONFIG_SENSOR_LOG_LEVEL); +/* update and send report */ +static void trackball_update_handler(struct k_work *work) { + // remaining scroll from last update + static int8_t scroll_ver_rem = 0, scroll_hor_rem = 0; + + // update report with latest position + zmk_hid_mouse_movement_set(0, 0); + zmk_hid_mouse_scroll_set(0, 0); + + const uint8_t layer = zmk_keymap_highest_layer_active(); + if (layer == SCROLL_LAYER_INDEX) { // lower + const int16_t total_hor = dx.val1 + scroll_hor_rem, total_ver = -(dy.val1 + scroll_ver_rem); + scroll_hor_rem = total_hor % SCROLL_DIV_FACTOR; + scroll_ver_rem = total_ver % SCROLL_DIV_FACTOR; + zmk_hid_mouse_scroll_update(total_hor / SCROLL_DIV_FACTOR, total_ver / SCROLL_DIV_FACTOR); + } else { + zmk_hid_mouse_movement_update(CLAMP(dx.val1, INT8_MIN, INT8_MAX), CLAMP(dy.val1, INT8_MIN, INT8_MAX)); + } + + // send the report to host + zmk_endpoints_send_mouse_report(); +} + +/* trigger handler, invoked by gpio interrupt */ static void handle_trackball(const struct device *dev, const struct sensor_trigger *trig) { -#if IS_ENABLED(CONFIG_ZMK_USB_LOGGING) - current_update_time = k_uptime_get(); +#if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) + current_update_time = k_ticks_to_us_floor64(k_uptime_ticks()); #endif // fetch latest position from sensor @@ -44,27 +70,16 @@ static void handle_trackball(const struct device *dev, const struct sensor_trigg LOG_ERR("get dy: %d", ret); return; } - LOG_DBG("trackball %d %d", dx.val1, dy.val1); - - // update x, y position - zmk_hid_mouse_movement_set(0, 0); - zmk_hid_mouse_scroll_set(0, 0); - - const uint8_t layer = zmk_keymap_highest_layer_active(); - static int8_t scroll_ver_rem = 0, scroll_hor_rem = 0; - if (layer == 2) { // lower - const int16_t total_hor = dx.val1 + scroll_hor_rem, total_ver = -(dy.val1 + scroll_ver_rem); - scroll_hor_rem = total_hor % SCROLL_DIV_FACTOR; - scroll_ver_rem = total_ver % SCROLL_DIV_FACTOR; - zmk_hid_mouse_scroll_update(total_hor / SCROLL_DIV_FACTOR, total_ver / SCROLL_DIV_FACTOR); - } else { - zmk_hid_mouse_movement_update(CLAMP(dx.val1, INT8_MIN, INT8_MAX), CLAMP(dy.val1, INT8_MIN, INT8_MAX)); - } - zmk_endpoints_send_mouse_report(); - - /* test */ -#if IS_ENABLED(CONFIG_ZMK_USB_LOGGING) - LOG_DBG("update interval: %lld ; trackball: %d %d", (current_update_time-last_update_time), dx.val1, dy.val1); + + // process the updated position and send to host + /* k_work_submit_to_queue(zmk_mouse_work_q(), &trackball_update); */ + trackball_update_handler(NULL); + +#if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) + update_duration = k_ticks_to_us_floor64(k_uptime_ticks()) - current_update_time; + LOG_DBG("interrupt interval (us): %lld ; update duration (us): %lld ; pos deltas: %d %d",\ + (current_update_time-last_update_time), update_duration, dx.val1, dy.val1); + last_update_time = current_update_time; #endif } From 42f66883e4f9893e2c2b53eea0d776c6f072d723 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Tue, 16 Aug 2022 21:50:27 +0800 Subject: [PATCH 058/157] [pmw33xx] bugfix: no need to reset motion register aftre burst This fix reduces the sample fetch and channel get time cost to 300-400 us. The usb report update cost ~100 us. Thus, the total time handling the interrupt under usb is 400-500 us --- app/boards/arm/nrfmacro/trackball/trackball.c | 29 +++++++++--- app/drivers/sensor/pmw33xx/pmw33xx.c | 45 ++++++++++++++++--- app/drivers/sensor/pmw33xx/pmw33xx.h | 7 ++- .../dts/bindings/sensor/pixart,pmw33xx.yaml | 4 ++ 4 files changed, 73 insertions(+), 12 deletions(-) diff --git a/app/boards/arm/nrfmacro/trackball/trackball.c b/app/boards/arm/nrfmacro/trackball/trackball.c index 6d57a324b8d..6f34db4881e 100644 --- a/app/boards/arm/nrfmacro/trackball/trackball.c +++ b/app/boards/arm/nrfmacro/trackball/trackball.c @@ -1,3 +1,5 @@ +#define DT_DRV_COMPAT pixart_pmw33xx + #include #include @@ -7,7 +9,9 @@ #include #define SCROLL_DIV_FACTOR 5 -#define SCROLL_LAYER_INDEX 4 +/* #define SCROLL_LAYER_INDEX 4 */ +#define SCROLL_LAYER_INDEX COND_CODE_0(DT_INST_NODE_HAS_PROP(0, scroll_layer), (255), \ + (DT_INST_PROP(0, scroll_layer))) #if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) @@ -15,16 +19,21 @@ static int64_t last_update_time = 0; static int64_t current_update_time = 0; static int64_t update_duration = 0; +static int64_t send_report_duration = 0; #endif static struct sensor_value dx, dy; -const struct device *trackball = DEVICE_DT_GET(DT_INST(0, pixart_pmw33xx)); +const struct device *trackball = DEVICE_DT_GET(DT_DRV_INST(0)); LOG_MODULE_REGISTER(trackball, CONFIG_SENSOR_LOG_LEVEL); /* update and send report */ -static void trackball_update_handler(struct k_work *work) { +static int64_t trackball_update_handler(struct k_work *work) { +#if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) + int64_t start_time = k_ticks_to_us_floor64(k_uptime_ticks()); +#endif + // remaining scroll from last update static int8_t scroll_ver_rem = 0, scroll_hor_rem = 0; @@ -44,6 +53,12 @@ static void trackball_update_handler(struct k_work *work) { // send the report to host zmk_endpoints_send_mouse_report(); + +#if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) + return start_time = k_ticks_to_us_floor64(k_uptime_ticks()) - start_time; +#else + return 0; +#endif } /* trigger handler, invoked by gpio interrupt */ @@ -73,12 +88,14 @@ static void handle_trackball(const struct device *dev, const struct sensor_trigg // process the updated position and send to host /* k_work_submit_to_queue(zmk_mouse_work_q(), &trackball_update); */ - trackball_update_handler(NULL); + send_report_duration = trackball_update_handler(NULL); #if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) update_duration = k_ticks_to_us_floor64(k_uptime_ticks()) - current_update_time; - LOG_DBG("interrupt interval (us): %lld ; update duration (us): %lld ; pos deltas: %d %d",\ - (current_update_time-last_update_time), update_duration, dx.val1, dy.val1); + LOG_DBG("interrupt interval (us): %lld; pos deltas: %d %d",\ + (current_update_time-last_update_time), dx.val1, dy.val1); + LOG_DBG("handler duration (us): %lld ; fetch: %lld ; report: %lld", update_duration,\ + (update_duration - send_report_duration), send_report_duration); last_update_time = current_update_time; #endif diff --git a/app/drivers/sensor/pmw33xx/pmw33xx.c b/app/drivers/sensor/pmw33xx/pmw33xx.c index 574fa8aa507..2fae860211d 100644 --- a/app/drivers/sensor/pmw33xx/pmw33xx.c +++ b/app/drivers/sensor/pmw33xx/pmw33xx.c @@ -24,6 +24,7 @@ #include "pmw33xx.h" LOG_MODULE_REGISTER(PMW33XX, CONFIG_SENSOR_LOG_LEVEL); + #define PMW33XX_PID COND_CODE_1(CONFIG_PMW33XX_3389, (PMW33XX_3389_PID), (PMW33XX_3360_PID)) #define PMW33XX_CPI_MAX \ COND_CODE_1(CONFIG_PMW33XX_3389, (PMW33XX_3389_CPI_MAX), (PMW33XX_3360_CPI_MAX)) @@ -181,16 +182,18 @@ static int pmw33xx_read_motion_burst(const struct device *dev, struct pmw33xx_mo pmw33xx_cs_select(cs_gpio_cfg, 0); int err = spi_write(data->bus, spi_cfg, &tx); - k_sleep(K_USEC(35)); // tsrad motbr if (err) { pmw33xx_cs_select(cs_gpio_cfg, 1); return err; } + + // wait needed + k_sleep(K_USEC(35)); // tsrad motbr + + // start reading continuosly err = spi_read(data->bus, spi_cfg, &rx); pmw33xx_cs_select(cs_gpio_cfg, 1); -#ifdef CONFIG_PMW33XX_TRIGGER - pmw33xx_reset_motion(dev); -#endif + return err; } @@ -201,6 +204,32 @@ void pmw33xx_reset_motion(const struct device *dev) { } #endif +#if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) +void pmw33xx_print_registers(const struct device *dev) { + LOG_DBG("print important regiter values of pmw33xx:"); + + uint8_t val; + int err; + + // pid, rid + err = pmw33xx_read_reg(dev, PMW33XX_REG_PID, &val); + if (err) { + LOG_ERR("couldn't read register 0x%x", PMW33XX_REG_PID); + } + else { + LOG_DBG("pmw33xx pid: 0x%x", val); + } + + err = pmw33xx_read_reg(dev, PMW33XX_REG_REV_ID, &val); + if (err) { + LOG_ERR("couldn't read register 0x%x", PMW33XX_REG_REV_ID); + } + else { + LOG_DBG("pmw33xx vid: 0x%x", val); + } +} +#endif + int pmw33xx_spi_init(const struct device *dev) { const struct pmw33xx_config *cfg = dev->config; const struct pmw33xx_gpio_dt_spec *cs_gpio_cfg = &cfg->bus_cfg.spi_cfg->cs_spec; @@ -348,7 +377,12 @@ static int pmw33xx_init_chip(const struct device *dev) { pmw33xx_write_reg(dev, PMW33XX_REG_CONFIG2, config->disable_rest ? 0x00 : PMW33XX_RESTEN); // set rest enable - // enbale motion burst +#if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) + pmw33xx_print_registers(dev); +#endif + + // enbale motion burst by writing something into motion_burst register, only needed once + // WARNING: no regiter reading other than position regiters after this setup pmw33xx_write_reg(dev, PMW33XX_REG_BURST, 0x01); // set cpi @@ -375,6 +409,7 @@ static int pmw33xx_init(const struct device *dev) { return -EIO; } + #ifdef CONFIG_PMW33XX_TRIGGER if (pmw33xx_init_interrupt(dev) < 0) { LOG_DBG("Failed to initialize interrupt!"); diff --git a/app/drivers/sensor/pmw33xx/pmw33xx.h b/app/drivers/sensor/pmw33xx/pmw33xx.h index 2ecb67bc157..a8f73214a00 100644 --- a/app/drivers/sensor/pmw33xx/pmw33xx.h +++ b/app/drivers/sensor/pmw33xx/pmw33xx.h @@ -12,11 +12,15 @@ #include #include +/* wr/rd mask */ #define PMW33XX_WR_MASK 0x80 #define PMW33XX_RD_MASK 0x7F +/* default values */ #define PMW33XX_3389_PID 0x47 #define PMW33XX_3360_PID 0x42 + +// todo: different rev id for different sensor #define PMW33XX_REV 0x01 /* General Registers */ @@ -30,7 +34,6 @@ #define PMW33XX_REG_DX_H 0x04 #define PMW33XX_REG_DY_L 0x05 #define PMW33XX_REG_DY_H 0x06 -#define PMW33XX_REG_BURST 0x50 /* Motion bits */ #define PMW33XX_MOTION (1 << 8) @@ -39,6 +42,8 @@ #define PMW33XX_OPMODE_REST2 (0b10 << 1) #define PMW33XX_OPMODE_REST3 (0b11 << 1) +// todo: +#define PMW33XX_REG_BURST 0x50 /* SROM Registers */ #define PMW33XX_REG_SROM_EN 0x13 #define PMW33XX_REG_SROM_ID 0x2A diff --git a/app/drivers/zephyr/dts/bindings/sensor/pixart,pmw33xx.yaml b/app/drivers/zephyr/dts/bindings/sensor/pixart,pmw33xx.yaml index 6a45218a78f..9c26fcc4c85 100644 --- a/app/drivers/zephyr/dts/bindings/sensor/pixart,pmw33xx.yaml +++ b/app/drivers/zephyr/dts/bindings/sensor/pixart,pmw33xx.yaml @@ -26,3 +26,7 @@ properties: type: boolean description: disables mouse rest mode, will decrease battery life required: false + scroll-layer: + type: int + description: the momentary layer used to modifiy move to scroll + required: false From 2d6649905a6513ded7ad63547cff4f625f806266 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Wed, 17 Aug 2022 19:36:38 +0800 Subject: [PATCH 059/157] add debug log --- app/boards/arm/nrfmacro/trackball/trackball.c | 14 ++++++-- app/src/mouse/key_listener.c | 32 +++++++++++++++++++ 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/app/boards/arm/nrfmacro/trackball/trackball.c b/app/boards/arm/nrfmacro/trackball/trackball.c index 6f34db4881e..7c0959f2d88 100644 --- a/app/boards/arm/nrfmacro/trackball/trackball.c +++ b/app/boards/arm/nrfmacro/trackball/trackball.c @@ -20,6 +20,8 @@ static int64_t last_update_time = 0; static int64_t current_update_time = 0; static int64_t update_duration = 0; static int64_t send_report_duration = 0; +static int64_t idle_interval = 0; +static int64_t time_buffer = 0; #endif static struct sensor_value dx, dy; @@ -65,6 +67,7 @@ static int64_t trackball_update_handler(struct k_work *work) { static void handle_trackball(const struct device *dev, const struct sensor_trigger *trig) { #if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) current_update_time = k_ticks_to_us_floor64(k_uptime_ticks()); + idle_interval = current_update_time - time_buffer; #endif // fetch latest position from sensor @@ -88,12 +91,17 @@ static void handle_trackball(const struct device *dev, const struct sensor_trigg // process the updated position and send to host /* k_work_submit_to_queue(zmk_mouse_work_q(), &trackball_update); */ +#if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) send_report_duration = trackball_update_handler(NULL); +#else + trackball_update_handler(NULL); +#endif #if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) - update_duration = k_ticks_to_us_floor64(k_uptime_ticks()) - current_update_time; - LOG_DBG("interrupt interval (us): %lld; pos deltas: %d %d",\ - (current_update_time-last_update_time), dx.val1, dy.val1); + time_buffer = k_ticks_to_us_floor64(k_uptime_ticks()); + update_duration = time_buffer - current_update_time; + LOG_DBG("interrupt interval (us): %lld ; idle: %lld ; pos deltas: %d %d",\ + (current_update_time-last_update_time), idle_interval, dx.val1, dy.val1); LOG_DBG("handler duration (us): %lld ; fetch: %lld ; report: %lld", update_duration,\ (update_duration - send_report_duration), send_report_duration); diff --git a/app/src/mouse/key_listener.c b/app/src/mouse/key_listener.c index fcb700c23ed..fd25543e198 100644 --- a/app/src/mouse/key_listener.c +++ b/app/src/mouse/key_listener.c @@ -25,6 +25,17 @@ static struct mouse_config move_config = (struct mouse_config){0}; static struct mouse_config scroll_config = (struct mouse_config){0}; static int64_t start_time = 0; + +#if IS_ENABLED(CONFIG_ZMK_USB_LOGGING) +// in us +static int64_t last_update_time = 0; +static int64_t current_update_time = 0; +static int64_t update_duration = 0; +static int64_t send_report_time = 0; +static int64_t idle_interval = 0; +static int64_t time_buffer = 0; +#endif + bool equals(const struct mouse_config *one, const struct mouse_config *other) { return one->delay_ms == other->delay_ms && one->time_to_max_speed_ms == other->time_to_max_speed_ms && @@ -47,12 +58,33 @@ void mouse_clear_cb(struct k_timer *dummy) { } static void mouse_tick_timer_handler(struct k_work *work) { +#if IS_ENABLED(CONFIG_ZMK_USB_LOGGING) + current_update_time = k_ticks_to_us_floor64(k_uptime_ticks()); + idle_interval = current_update_time - time_buffer; +#endif + zmk_hid_mouse_movement_set(0, 0); zmk_hid_mouse_scroll_set(0, 0); + LOG_DBG("Raising mouse tick event"); ZMK_EVENT_RAISE( zmk_mouse_tick(move_speed, scroll_speed, move_config, scroll_config, &start_time)); + +#if IS_ENABLED(CONFIG_ZMK_USB_LOGGING) + send_report_time = k_ticks_to_us_floor64(k_uptime_ticks()); +#endif + zmk_endpoints_send_mouse_report(); + +#if IS_ENABLED(CONFIG_ZMK_USB_LOGGING) + time_buffer = k_ticks_to_us_floor64(k_uptime_ticks()); + update_duration = time_buffer - current_update_time; + LOG_DBG("Interrupt duration: %lld ; update: %lld ; report: %lld ; idle: %lld", \ + (current_update_time - last_update_time), update_duration, \ + (time_buffer - send_report_time), idle_interval); + + last_update_time = current_update_time; +#endif } K_WORK_DEFINE(mouse_tick, &mouse_tick_timer_handler); From 2e7c33d79b507404a058b019009c7eda0f2df509 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Fri, 26 Aug 2022 14:56:57 +0800 Subject: [PATCH 060/157] [debug] temporarily move trackball dts node out of nrfmacro.dts --- app/boards/arm/nrfmacro/nrfmacro.dts | 58 ++++++++++++++-------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/app/boards/arm/nrfmacro/nrfmacro.dts b/app/boards/arm/nrfmacro/nrfmacro.dts index 229b93c248a..96351780a86 100644 --- a/app/boards/arm/nrfmacro/nrfmacro.dts +++ b/app/boards/arm/nrfmacro/nrfmacro.dts @@ -176,32 +176,32 @@ nrfmacro_spi: &spi1 {}; nrfmacro_i2c: &i2c1 {}; -&pro_micro_spi { - status = "disabled"; - sck-pin = <26>; - mosi-pin = <12>; - miso-pin = <7>; - cs-gpios = <&gpio0 22 GPIO_ACTIVE_LOW>; - - // trackpad: trackpad@0 { - // compatible = "cirque,pinnacle"; - // // status = "okay"; - // reg = <0>; - // label = "cirque trackpad"; - // spi-max-frequency = <1000000>; - // dr-gpios = <&gpio1 0 GPIO_ACTIVE_HIGH>; - // invert-x; - // sleep; - // /* no-taps; */ - // }; - - trackball: trackball@0 { - compatible = "pixart,pmw33xx"; - reg = <0>; - label = "Pixart PMW3360"; - cs-gpios = <&gpio0 22 GPIO_ACTIVE_LOW>; - motswk-gpios = <&gpio1 0 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; - spi-max-frequency = <2000000>; - cpi = <400>; - }; -}; \ No newline at end of file +// &pro_micro_spi { +// status = "disabled"; +// sck-pin = <26>; +// mosi-pin = <12>; +// miso-pin = <7>; +// cs-gpios = <&gpio0 22 GPIO_ACTIVE_LOW>; + +// // trackpad: trackpad@0 { +// // compatible = "cirque,pinnacle"; +// // // status = "okay"; +// // reg = <0>; +// // label = "cirque trackpad"; +// // spi-max-frequency = <1000000>; +// // dr-gpios = <&gpio1 0 GPIO_ACTIVE_HIGH>; +// // invert-x; +// // sleep; +// // /* no-taps; */ +// // }; + +// trackball: trackball@0 { +// compatible = "pixart,pmw33xx"; +// reg = <0>; +// label = "Pixart PMW3360"; +// cs-gpios = <&gpio0 22 GPIO_ACTIVE_LOW>; +// motswk-gpios = <&gpio1 0 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; +// spi-max-frequency = <2000000>; +// cpi = <400>; +// }; +// }; \ No newline at end of file From fc03bdd47e1fec784c8afecb5b44bcc94dd47d17 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Sun, 28 Aug 2022 14:29:21 +0800 Subject: [PATCH 061/157] backup --- app/Kconfig | 2 +- app/boards/arm/nrfmacro/trackball/trackball.c | 76 +++++++----- app/boards/arm/nrfmacro/trackpad/trackpad.c | 108 +++++++++++++----- .../sensor/cirque_trackpad/cirque_trackpad.c | 69 +++++------ 4 files changed, 157 insertions(+), 98 deletions(-) diff --git a/app/Kconfig b/app/Kconfig index 3c596058143..ba9c9722014 100644 --- a/app/Kconfig +++ b/app/Kconfig @@ -408,7 +408,7 @@ config ZMK_USB_LOGGING if ZMK_USB_LOGGING config ZMK_LOG_LEVEL - default 4 + default 1 # We do this to avoid log loop where logging to USB generates more log messages. config USB_CDC_ACM_LOG_LEVEL diff --git a/app/boards/arm/nrfmacro/trackball/trackball.c b/app/boards/arm/nrfmacro/trackball/trackball.c index 7c0959f2d88..709b7e6b26b 100644 --- a/app/boards/arm/nrfmacro/trackball/trackball.c +++ b/app/boards/arm/nrfmacro/trackball/trackball.c @@ -14,16 +14,23 @@ (DT_INST_PROP(0, scroll_layer))) -#if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) +/* #if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) */ // in us -static int64_t last_update_time = 0; -static int64_t current_update_time = 0; -static int64_t update_duration = 0; +static int64_t last_interrupt_time = 0; +static int64_t current_interrupt_time = 0; +static int64_t interrupt_interval = 0; +static int64_t handler_duration = 0; static int64_t send_report_duration = 0; static int64_t idle_interval = 0; static int64_t time_buffer = 0; -#endif +/* #endif */ +// TEST: for controlling the update frq +#define MIN_UPDATE_INTERVAL 4000 // in us +static int64_t acc_interval = 0; +static int acc_interrupt_count = 0; + +// static struct sensor_value dx, dy; const struct device *trackball = DEVICE_DT_GET(DT_DRV_INST(0)); @@ -32,9 +39,9 @@ LOG_MODULE_REGISTER(trackball, CONFIG_SENSOR_LOG_LEVEL); /* update and send report */ static int64_t trackball_update_handler(struct k_work *work) { -#if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) +/* #if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) */ int64_t start_time = k_ticks_to_us_floor64(k_uptime_ticks()); -#endif +/* #endif */ // remaining scroll from last update static int8_t scroll_ver_rem = 0, scroll_hor_rem = 0; @@ -56,21 +63,28 @@ static int64_t trackball_update_handler(struct k_work *work) { // send the report to host zmk_endpoints_send_mouse_report(); -#if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) +/* #if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) */ return start_time = k_ticks_to_us_floor64(k_uptime_ticks()) - start_time; -#else - return 0; -#endif +/* #else */ +/* return 0; */ +/* #endif */ } /* trigger handler, invoked by gpio interrupt */ static void handle_trackball(const struct device *dev, const struct sensor_trigger *trig) { -#if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) - current_update_time = k_ticks_to_us_floor64(k_uptime_ticks()); - idle_interval = current_update_time - time_buffer; -#endif +/* #if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) */ + current_interrupt_time = k_ticks_to_us_floor64(k_uptime_ticks()); + interrupt_interval = current_interrupt_time - last_interrupt_time; + idle_interval = current_interrupt_time - time_buffer; +/* #endif */ + + // TEST: control update frq + acc_interval += interrupt_interval; + acc_interrupt_count++; + // fetch latest position from sensor + if(acc_interval > MIN_UPDATE_INTERVAL) { int ret = sensor_sample_fetch(dev); if (ret < 0) { LOG_ERR("fetch: %d", ret); @@ -91,22 +105,28 @@ static void handle_trackball(const struct device *dev, const struct sensor_trigg // process the updated position and send to host /* k_work_submit_to_queue(zmk_mouse_work_q(), &trackball_update); */ -#if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) +/* #if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) */ send_report_duration = trackball_update_handler(NULL); -#else - trackball_update_handler(NULL); -#endif +/* #else */ +/* trackball_update_handler(NULL); */ +/* #endif */ + + LOG_INF("Update interval (us): %lld ; Nr of interrupts: %d ; Position: %d %d",\ + acc_interval, acc_interrupt_count, dx.val1, dy.val1); + LOG_INF("Send report time cost: %lld", send_report_duration); + + acc_interval = 0; + acc_interrupt_count = 0; + } -#if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) +/* #if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) */ time_buffer = k_ticks_to_us_floor64(k_uptime_ticks()); - update_duration = time_buffer - current_update_time; - LOG_DBG("interrupt interval (us): %lld ; idle: %lld ; pos deltas: %d %d",\ - (current_update_time-last_update_time), idle_interval, dx.val1, dy.val1); - LOG_DBG("handler duration (us): %lld ; fetch: %lld ; report: %lld", update_duration,\ - (update_duration - send_report_duration), send_report_duration); - - last_update_time = current_update_time; -#endif + handler_duration = time_buffer - current_interrupt_time; + /* LOG_DBG("interrupt interval (us): %lld ; idle: %lld ; handler time cost: %lld",\ */ + /* interrupt_interval, idle_interval, handler_duration); */ + + last_interrupt_time = current_interrupt_time; +/* #endif */ } static int trackball_init() { diff --git a/app/boards/arm/nrfmacro/trackpad/trackpad.c b/app/boards/arm/nrfmacro/trackpad/trackpad.c index b547b2cb3f2..b60280cbc00 100644 --- a/app/boards/arm/nrfmacro/trackpad/trackpad.c +++ b/app/boards/arm/nrfmacro/trackpad/trackpad.c @@ -1,3 +1,5 @@ +#define DT_DRV_COMPAT cirque_pinnacle + #include #include @@ -7,19 +9,84 @@ #include #define SCROLL_DIV_FACTOR 5 +#define SCROLL_LAYER_INDEX COND_CODE_0(DT_INST_NODE_HAS_PROP(0, scroll_layer), (255), \ + (DT_INST_PROP(0, scroll_layer))) + + +/* #if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) */ +// in us +static int64_t last_interrupt_time = 0; +static int64_t current_interrupt_time = 0; +static int64_t interrupt_interval = 0; +static int64_t handler_duration = 0; +static int64_t send_report_duration = 0; +static int64_t idle_interval = 0; +static int64_t time_buffer = 0; +/* #endif */ -const struct device *trackpad = DEVICE_DT_GET(DT_INST(0, cirque_pinnacle)); +static struct sensor_value dx, dy, btn; + +const struct device *trackpad = DEVICE_DT_GET(DT_DRV_INST(0)); LOG_MODULE_REGISTER(trackpad, CONFIG_SENSOR_LOG_LEVEL); +/* update and send report */ +static int64_t trackpad_update_handler(struct k_work *work) { +/* #if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) */ + int64_t start_time = k_ticks_to_us_floor64(k_uptime_ticks()); +/* #endif */ + + // remaining scroll from last update + static int8_t scroll_ver_rem = 0, scroll_hor_rem = 0; + uint8_t button; + static uint8_t last_button = 0; + static uint8_t last_pressed = 0; + + // update report with latest position + zmk_hid_mouse_movement_set(0, 0); + zmk_hid_mouse_scroll_set(0, 0); + + const uint8_t layer = zmk_keymap_highest_layer_active(); + if (layer == SCROLL_LAYER_INDEX) { // lower + const int16_t total_hor = dx.val1 + scroll_hor_rem, total_ver = -(dy.val1 + scroll_ver_rem); + scroll_hor_rem = total_hor % SCROLL_DIV_FACTOR; + scroll_ver_rem = total_ver % SCROLL_DIV_FACTOR; + zmk_hid_mouse_scroll_update(total_hor / SCROLL_DIV_FACTOR, total_ver / SCROLL_DIV_FACTOR); + button = RCLK; + } else { + zmk_hid_mouse_movement_update(CLAMP(dx.val1, INT8_MIN, INT8_MAX), CLAMP(dy.val1, INT8_MIN, INT8_MAX)); + button = LCLK; + } + + if (!last_pressed && btn.val1) { + zmk_hid_mouse_buttons_press(button); + last_button = button; + } else if (last_pressed && !btn.val1) { + zmk_hid_mouse_buttons_release(last_button); + } + // send the report to host + zmk_endpoints_send_mouse_report(); + last_pressed = btn.val1; + +/* #if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) */ + return start_time = k_ticks_to_us_floor64(k_uptime_ticks()) - start_time; +/* #else */ +/* return 0; */ +/* #endif */ +} + static void handle_trackpad(const struct device *dev, const struct sensor_trigger *trig) { - static uint8_t last_pressed = 0; + current_interrupt_time = k_ticks_to_us_floor64(k_uptime_ticks()); + interrupt_interval = current_interrupt_time - last_interrupt_time; + idle_interval = current_interrupt_time - time_buffer; + + int ret = sensor_sample_fetch(dev); if (ret < 0) { LOG_ERR("fetch: %d", ret); return; } - struct sensor_value dx, dy, btn; + ret = sensor_channel_get(dev, SENSOR_CHAN_POS_DX, &dx); if (ret < 0) { LOG_ERR("get dx: %d", ret); @@ -35,31 +102,16 @@ static void handle_trackpad(const struct device *dev, const struct sensor_trigge LOG_ERR("get btn: %d", ret); return; } - LOG_DBG("trackpad %d %d %02x", dx.val1, dy.val1, btn.val1); - zmk_hid_mouse_movement_set(0, 0); - zmk_hid_mouse_scroll_set(0, 0); - const uint8_t layer = zmk_keymap_highest_layer_active(); - uint8_t button; - static uint8_t last_button = 0; - static int8_t scroll_ver_rem = 0, scroll_hor_rem = 0; - if (layer == 2) { // lower - const int16_t total_hor = dx.val1 + scroll_hor_rem, total_ver = -(dy.val1 + scroll_ver_rem); - scroll_hor_rem = total_hor % SCROLL_DIV_FACTOR; - scroll_ver_rem = total_ver % SCROLL_DIV_FACTOR; - zmk_hid_mouse_scroll_update(total_hor / SCROLL_DIV_FACTOR, total_ver / SCROLL_DIV_FACTOR); - button = RCLK; - } else { - zmk_hid_mouse_movement_update(CLAMP(dx.val1, INT8_MIN, INT8_MAX), CLAMP(dy.val1, INT8_MIN, INT8_MAX)); - button = LCLK; - } - if (!last_pressed && btn.val1) { - zmk_hid_mouse_buttons_press(button); - last_button = button; - } else if (last_pressed && !btn.val1) { - zmk_hid_mouse_buttons_release(last_button); - } - zmk_endpoints_send_mouse_report(); - last_pressed = btn.val1; + + send_report_duration = trackpad_update_handler(NULL); + + time_buffer = k_ticks_to_us_floor64(k_uptime_ticks()); + handler_duration = time_buffer - current_interrupt_time; + + LOG_INF("interrupt interval (us): %lld ; idle: %lld ; handler time cost: %lld",\ + interrupt_interval, idle_interval, handler_duration); + LOG_INF("Send report time cost: %lld; Position: %d %d", send_report_duration, dx.val1, dy.val1); + last_interrupt_time = current_interrupt_time; } static int trackpad_init() { diff --git a/app/drivers/sensor/cirque_trackpad/cirque_trackpad.c b/app/drivers/sensor/cirque_trackpad/cirque_trackpad.c index 779595ce4c5..8b362f9f79e 100644 --- a/app/drivers/sensor/cirque_trackpad/cirque_trackpad.c +++ b/app/drivers/sensor/cirque_trackpad/cirque_trackpad.c @@ -3,7 +3,6 @@ #include #include #include -#include #include #include "cirque_trackpad.h" @@ -45,7 +44,6 @@ static int pinnacle_seq_read(const struct device *dev, const uint8_t start, uint static int pinnacle_write(const struct device *dev, const uint8_t addr, const uint8_t val) { uint8_t tx_buffer[2] = { PINNACLE_WRITE | addr, val }; - uint8_t rx_buffer[2]; const struct spi_buf tx_buf = { .buf = tx_buffer, @@ -55,27 +53,9 @@ static int pinnacle_write(const struct device *dev, const uint8_t addr, const ui .buffers = &tx_buf, .count = 1, }; - const struct spi_buf rx_buf[1] = { - { - .buf = rx_buffer, - .len = sizeof(rx_buffer), - }, - }; - const struct spi_buf_set rx = { - .buffers = rx_buf, - .count = 1, - }; const struct pinnacle_data *data = dev->data; const struct pinnacle_config *config = dev->config; - const int ret = spi_transceive(data->spi, &config->spi_config, &tx, &rx); - if (rx_buffer[1] != 0xFB) { - LOG_ERR("bad ret val"); - return -EIO; - } - if (ret < 0) { - LOG_ERR("spi ret: %d", ret); - } - return ret; + return spi_write(data->spi, &config->spi_config, &tx); } static int pinnacle_channel_get(const struct device *dev, enum sensor_channel chan, struct sensor_value *val) { @@ -89,17 +69,6 @@ static int pinnacle_channel_get(const struct device *dev, enum sensor_channel ch return 0; } -static int pinnacle_attr_set(const struct device *dev, enum sensor_channel chan, enum sensor_attribute attr, const struct sensor_value *val) { - const struct pinnacle_config *config = dev->config; - if (attr == SENSOR_ATTR_PINNACLE_GE) { - const uint8_t ge_set = val->val1 ? 0 : PINNACLE_FEED_CFG2_DIS_GE; - const uint8_t taps_set = config->no_taps ? PINNACLE_FEED_CFG2_DIS_TAP : 0; - pinnacle_write(dev, PINNACLE_FEED_CFG2, ge_set | taps_set); - return 0; - } - return -ENOTSUP; -} - static int pinnacle_sample_fetch(const struct device *dev, enum sensor_channel chan) { uint8_t packet[3]; int res = pinnacle_seq_read(dev, PINNACLE_2_2_PACKET0, packet, 3); @@ -109,8 +78,8 @@ static int pinnacle_sample_fetch(const struct device *dev, enum sensor_channel c } struct pinnacle_data *data = dev->data; data->btn = packet[0] & PINNACLE_PACKET0_BTN_PRIM; - data->dx = (int16_t) (int8_t) packet[1]; - data->dy = (int16_t) (int8_t) packet[2]; + data->dx = ((packet[0] & PINNACLE_PACKET0_X_SIGN) ? 0xFF00 : 0) | packet[1]; + data->dy = ((packet[0] & PINNACLE_PACKET0_Y_SIGN) ? 0xFF00 : 0) | packet[2]; return 0; } @@ -118,11 +87,12 @@ static int pinnacle_sample_fetch(const struct device *dev, enum sensor_channel c static void set_int(const struct device *dev, const bool en) { const struct pinnacle_config *config = dev->config; int ret = gpio_pin_interrupt_configure(config->dr_port, config->dr_pin, en ? GPIO_INT_LEVEL_ACTIVE : GPIO_INT_DISABLE); + /* int ret = gpio_pin_interrupt_configure(config->dr_port, config->dr_pin, en ? GPIO_INT_EDGE_TO_ACTIVE : GPIO_INT_DISABLE); */ if (ret < 0) { LOG_ERR("can't set interrupt"); } } - + static int pinnacle_trigger_set(const struct device *dev, const struct sensor_trigger *trig, sensor_trigger_handler_t handler) { struct pinnacle_data *data = dev->data; @@ -139,6 +109,11 @@ static int pinnacle_trigger_set(const struct device *dev, const struct sensor_tr static void pinnacle_int_cb(const struct device *dev) { struct pinnacle_data *data = dev->data; data->data_ready_handler(dev, data->data_ready_trigger); + + // clear hardware interrupt on trackpad side + pinnacle_write(dev, PINNACLE_STATUS1, 0); + + // enable the inerrupt to the host side set_int(dev, true); } @@ -150,20 +125,23 @@ static void pinnacle_thread(void *arg) { while (1) { k_sem_take(&data->gpio_sem, K_FOREVER); pinnacle_int_cb(dev); - pinnacle_write(dev, PINNACLE_STATUS1, 0); // Clear SW_DR } } #elif defined(CONFIG_PINNACLE_TRIGGER_GLOBAL_THREAD) static void pinnacle_work_cb(struct k_work *work) { struct pinnacle_data *data = CONTAINER_OF(work, struct pinnacle_data, work); pinnacle_int_cb(data->dev); - pinnacle_write(dev, PINNACLE_STATUS1, 0); // Clear SW_DR } #endif static void pinnacle_gpio_cb(const struct device *port, struct gpio_callback *cb, uint32_t pins) { struct pinnacle_data *data = CONTAINER_OF(cb, struct pinnacle_data, gpio_cb); const struct device *dev = data->dev; + set_int(dev, false); // disable interrupt on mcu side + + /* pinnacle_write(dev, PINNACLE_STATUS1, 0); // clear hardware interrupt on trackpad side */ + + // dispatch data handling to work thead #if defined(CONFIG_PINNACLE_TRIGGER_OWN_THREAD) k_sem_give(&data->gpio_sem); #elif defined(CONFIG_PINNACLE_TRIGGER_GLOBAL_THREAD) @@ -180,14 +158,23 @@ static int pinnacle_init(const struct device *dev) { const struct pinnacle_config *config = dev->config; data->spi = DEVICE_DT_GET(SPI_BUS); - pinnacle_write(dev, PINNACLE_STATUS1, 0); // Clear CC + // todo: wait for power-on or not + pinnacle_write(dev, PINNACLE_STATUS1, 0); // Clear CC after power-on + + // disable z-idle packets pinnacle_write(dev, PINNACLE_Z_IDLE, 0); // No Z-Idle packets + + // set sleep-mode if (config->sleep_en) { pinnacle_write(dev, PINNACLE_SYS_CFG, PINNACLE_SYS_CFG_EN_SLEEP); } + + // set tap if (config->no_taps) { pinnacle_write(dev, PINNACLE_FEED_CFG2, PINNACLE_FEED_CFG2_DIS_TAP); } + + // data mode and enable feed uint8_t feed_cfg1 = PINNACLE_FEED_CFG1_EN_FEED; if (config->invert_x) { feed_cfg1 |= PINNACLE_FEED_CFG1_INV_X; @@ -229,7 +216,6 @@ static const struct sensor_driver_api pinnacle_driver_api = { #endif .sample_fetch = pinnacle_sample_fetch, .channel_get = pinnacle_channel_get, - .attr_set = pinnacle_attr_set, }; static struct pinnacle_data pinnacle_data; @@ -244,7 +230,8 @@ static const struct pinnacle_config pinnacle_config = { .cs = &pinnacle_config.spi_cs, .frequency = DT_INST_PROP(0, spi_max_frequency), .slave = DT_INST_REG_ADDR(0), - .operation = (SPI_OP_MODE_MASTER | SPI_WORD_SET(8) | SPI_LINES_SINGLE | SPI_TRANSFER_MSB), + /* .operation = (SPI_OP_MODE_MASTER | SPI_WORD_SET(8) | SPI_TRANSFER_MSB), */ + .operation = (SPI_OP_MODE_MASTER | SPI_WORD_SET(8) | SPI_TRANSFER_MSB | SPI_MODE_CPHA), }, .invert_x = DT_INST_PROP(0, invert_x), .invert_y = DT_INST_PROP(0, invert_y), @@ -257,4 +244,4 @@ static const struct pinnacle_config pinnacle_config = { #endif }; -DEVICE_DT_INST_DEFINE(0, pinnacle_init, device_pm_control_nop, &pinnacle_data, &pinnacle_config, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, &pinnacle_driver_api); +DEVICE_DT_INST_DEFINE(0, pinnacle_init, NULL, &pinnacle_data, &pinnacle_config, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, &pinnacle_driver_api); From cd430425863bf7406de03452736ab3f512794a73 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Sun, 28 Aug 2022 16:31:50 +0800 Subject: [PATCH 062/157] backup --- app/boards/arm/nrfmacro/trackball/trackball.c | 2 +- app/drivers/sensor/pmw33xx/pmw33xx.c | 5 +++-- app/drivers/sensor/pmw33xx/pmw33xx_trigger.c | 10 +++++++++- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/app/boards/arm/nrfmacro/trackball/trackball.c b/app/boards/arm/nrfmacro/trackball/trackball.c index 709b7e6b26b..d4093722be6 100644 --- a/app/boards/arm/nrfmacro/trackball/trackball.c +++ b/app/boards/arm/nrfmacro/trackball/trackball.c @@ -26,7 +26,7 @@ static int64_t time_buffer = 0; /* #endif */ // TEST: for controlling the update frq -#define MIN_UPDATE_INTERVAL 4000 // in us +#define MIN_UPDATE_INTERVAL 12000 // in us static int64_t acc_interval = 0; static int acc_interrupt_count = 0; diff --git a/app/drivers/sensor/pmw33xx/pmw33xx.c b/app/drivers/sensor/pmw33xx/pmw33xx.c index 2fae860211d..abb816e133f 100644 --- a/app/drivers/sensor/pmw33xx/pmw33xx.c +++ b/app/drivers/sensor/pmw33xx/pmw33xx.c @@ -316,7 +316,7 @@ static int pmw33xx_init_chip(const struct device *dev) { // reset spi port pmw33xx_cs_select(cs_gpio_cfg, 1); pmw33xx_cs_select(cs_gpio_cfg, 0); - k_sleep(K_MSEC(1)); + /* k_sleep(K_MSEC(1)); */ // power reset int err = pmw33xx_write_reg(dev, PMW33XX_REG_PWR_UP_RST, PMW33XX_RESET_CMD); @@ -326,7 +326,8 @@ static int pmw33xx_init_chip(const struct device *dev) { } k_sleep(K_MSEC(50)); - // clear motion data + // clear motion data (by reading motion registers from 0x02 to 0x06) + // use burst read or normal read struct pmw33xx_motion_burst val; pmw33xx_read_motion_burst(dev, &val); // read and throwout initial motion data diff --git a/app/drivers/sensor/pmw33xx/pmw33xx_trigger.c b/app/drivers/sensor/pmw33xx/pmw33xx_trigger.c index df84d819b80..5371ccf70bd 100644 --- a/app/drivers/sensor/pmw33xx/pmw33xx_trigger.c +++ b/app/drivers/sensor/pmw33xx/pmw33xx_trigger.c @@ -34,8 +34,10 @@ static void pmw33xx_motswk_gpio_callback(const struct device *dev, struct gpio_c LOG_DBG(""); + // disable the interrput line setup_int(drv_data->dev, false); + // submit work to handler thread #if defined(CONFIG_PMW33XX_TRIGGER_OWN_THREAD) k_sem_give(&drv_data->gpio_sem); #elif defined(CONFIG_PMW33XX_TRIGGER_GLOBAL_THREAD) @@ -91,7 +93,12 @@ int pmw33xx_trigger_set(const struct device *dev, const struct sensor_trigger *t setup_int(dev, true); // reset motion on int setup + // so that old position values can be overwritten by new movements + // this is needed because trigger_set is invoked after device init, thus the sensor + // already starts working when invoking trigger_set, which may have left-over positions + // the motion interrupt is not cleared according to the datasheet. pmw33xx_reset_motion(dev); + return 0; } @@ -100,8 +107,8 @@ int pmw33xx_init_interrupt(const struct device *dev) { const struct pmw33xx_config *drv_cfg = dev->config; drv_data->dev = dev; - /* setup gpio interrupt */ + /* setup isr (interrupt service roution) */ gpio_pin_configure(drv_cfg->motswk_spec.port, drv_cfg->motswk_spec.pin, GPIO_INPUT | drv_cfg->motswk_spec.dt_flags); gpio_init_callback(&drv_data->motswk_gpio_cb, pmw33xx_motswk_gpio_callback, BIT(drv_cfg->motswk_spec.pin)); @@ -112,6 +119,7 @@ int pmw33xx_init_interrupt(const struct device *dev) { return -EIO; } + // create the handler thread or work #if defined(CONFIG_PMW33XX_TRIGGER_OWN_THREAD) k_sem_init(&drv_data->gpio_sem, 0, UINT_MAX); From 647606f9bbe1305b20770ace6b0f416ef0ec0edf Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Sun, 28 Aug 2022 22:11:40 +0800 Subject: [PATCH 063/157] [new feature] init version of interrupt based pmw33xx polling --- .../arm/nrfmacro/trackball/CMakeLists.txt | 1 + app/boards/arm/nrfmacro/trackball/trackball.c | 95 +++++++++++++------ app/drivers/sensor/pmw33xx/pmw33xx.c | 4 +- app/drivers/sensor/pmw33xx/pmw33xx.h | 3 + app/drivers/sensor/pmw33xx/pmw33xx_trigger.c | 3 +- 5 files changed, 77 insertions(+), 29 deletions(-) diff --git a/app/boards/arm/nrfmacro/trackball/CMakeLists.txt b/app/boards/arm/nrfmacro/trackball/CMakeLists.txt index 2c27e542eb6..a6870771d61 100644 --- a/app/boards/arm/nrfmacro/trackball/CMakeLists.txt +++ b/app/boards/arm/nrfmacro/trackball/CMakeLists.txt @@ -1,4 +1,5 @@ zephyr_library() zephyr_library_sources(trackball.c) zephyr_library_include_directories(${CMAKE_SOURCE_DIR}/include) +zephyr_library_include_directories(${CMAKE_SOURCE_DIR}/drivers/sensor) zephyr_library_include_directories(${ZEPHYR_BASE}/drivers) diff --git a/app/boards/arm/nrfmacro/trackball/trackball.c b/app/boards/arm/nrfmacro/trackball/trackball.c index d4093722be6..8673db7cd41 100644 --- a/app/boards/arm/nrfmacro/trackball/trackball.c +++ b/app/boards/arm/nrfmacro/trackball/trackball.c @@ -6,8 +6,11 @@ #include #include #include +#include #include +#include + #define SCROLL_DIV_FACTOR 5 /* #define SCROLL_LAYER_INDEX 4 */ #define SCROLL_LAYER_INDEX COND_CODE_0(DT_INST_NODE_HAS_PROP(0, scroll_layer), (255), \ @@ -25,10 +28,9 @@ static int64_t idle_interval = 0; static int64_t time_buffer = 0; /* #endif */ -// TEST: for controlling the update frq -#define MIN_UPDATE_INTERVAL 12000 // in us -static int64_t acc_interval = 0; -static int acc_interrupt_count = 0; +static int polling_count = 0; +#define MAX_POLLING_COUNT 50 +#define TRACKBALL_POLL_INTERVAL 12 // in ms // static struct sensor_value dx, dy; @@ -37,6 +39,7 @@ const struct device *trackball = DEVICE_DT_GET(DT_DRV_INST(0)); LOG_MODULE_REGISTER(trackball, CONFIG_SENSOR_LOG_LEVEL); + /* update and send report */ static int64_t trackball_update_handler(struct k_work *work) { /* #if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) */ @@ -70,63 +73,101 @@ static int64_t trackball_update_handler(struct k_work *work) { /* #endif */ } -/* trigger handler, invoked by gpio interrupt */ -static void handle_trackball(const struct device *dev, const struct sensor_trigger *trig) { +// polling work +static void trackball_poll_handler(struct k_work *work) { /* #if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) */ current_interrupt_time = k_ticks_to_us_floor64(k_uptime_ticks()); interrupt_interval = current_interrupt_time - last_interrupt_time; idle_interval = current_interrupt_time - time_buffer; /* #endif */ - // TEST: control update frq - acc_interval += interrupt_interval; - acc_interrupt_count++; - // fetch latest position from sensor - if(acc_interval > MIN_UPDATE_INTERVAL) { - int ret = sensor_sample_fetch(dev); + int ret = sensor_sample_fetch(trackball); if (ret < 0) { LOG_ERR("fetch: %d", ret); return; } // get the x, y delta - ret = sensor_channel_get(dev, SENSOR_CHAN_POS_DX, &dx); + ret = sensor_channel_get(trackball, SENSOR_CHAN_POS_DX, &dx); if (ret < 0) { LOG_ERR("get dx: %d", ret); return; } - ret = sensor_channel_get(dev, SENSOR_CHAN_POS_DY, &dy); + ret = sensor_channel_get(trackball, SENSOR_CHAN_POS_DY, &dy); if (ret < 0) { LOG_ERR("get dy: %d", ret); return; } + if(dx.val1 != 0 || dy.val1 != 0) { // process the updated position and send to host /* k_work_submit_to_queue(zmk_mouse_work_q(), &trackball_update); */ /* #if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) */ - send_report_duration = trackball_update_handler(NULL); + send_report_duration = trackball_update_handler(NULL); /* #else */ /* trackball_update_handler(NULL); */ /* #endif */ + LOG_INF("Position updated: poll interval: %lld; send time: %lld ; new pos: %d %d",\ + interrupt_interval, send_report_duration, dx.val1, dy.val1); + } - LOG_INF("Update interval (us): %lld ; Nr of interrupts: %d ; Position: %d %d",\ - acc_interval, acc_interrupt_count, dx.val1, dy.val1); - LOG_INF("Send report time cost: %lld", send_report_duration); - - acc_interval = 0; - acc_interrupt_count = 0; - } - /* #if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) */ time_buffer = k_ticks_to_us_floor64(k_uptime_ticks()); handler_duration = time_buffer - current_interrupt_time; - /* LOG_DBG("interrupt interval (us): %lld ; idle: %lld ; handler time cost: %lld",\ */ - /* interrupt_interval, idle_interval, handler_duration); */ + LOG_DBG("idle time: %lld ; handler time cost: %lld", \ + idle_interval, handler_duration); last_interrupt_time = current_interrupt_time; -/* #endif */ +} + +K_WORK_DEFINE(trackball_poll_work, &trackball_poll_handler); + +// polling timer +void trackball_timer_expiry(struct k_timer *timer); +void trackball_timer_stop(struct k_timer *timer); + +K_TIMER_DEFINE(trackball_timer, trackball_timer_expiry, trackball_timer_stop); + +// timer expiry function +void trackball_timer_expiry(struct k_timer *timer) { + // check whether reaching the polling count limit + if(polling_count < MAX_POLLING_COUNT) { + // submit polling work to mouse work queue + k_work_submit_to_queue(zmk_mouse_work_q(), &trackball_poll_work); + + // update status + polling_count++; + } + else { + // stop timer + k_timer_stop(&trackball_timer); + } +} + +// timer stop function +void trackball_timer_stop(struct k_timer *timer) { + // reset status + polling_count = 0; + + // resume motion interrupt line + const struct pmw33xx_config *cfg = trackball->config; + if (gpio_pin_interrupt_configure(cfg->motswk_spec.port, cfg->motswk_spec.pin, GPIO_INT_LEVEL_ACTIVE)) { + LOG_WRN("Unable to set MOTSWK GPIO interrupt"); + } +} + + +// trigger handler +static void trackball_trigger_handler(const struct device *dev, const struct sensor_trigger *trig) { + struct pmw33xx_data *data = dev->data; + + // do not resume motion interrupt + data->resume_interrupt = false; + + // start the polling timer + k_timer_start(&trackball_timer, K_NO_WAIT, K_MSEC(TRACKBALL_POLL_INTERVAL)); } static int trackball_init() { @@ -135,7 +176,7 @@ static int trackball_init() { .chan = SENSOR_CHAN_ALL, }; printk("trackball"); - if (sensor_trigger_set(trackball, &trigger, handle_trackball) < 0) { + if (sensor_trigger_set(trackball, &trigger, trackball_trigger_handler) < 0) { LOG_ERR("can't set trigger"); return -EIO; }; diff --git a/app/drivers/sensor/pmw33xx/pmw33xx.c b/app/drivers/sensor/pmw33xx/pmw33xx.c index abb816e133f..b661019111e 100644 --- a/app/drivers/sensor/pmw33xx/pmw33xx.c +++ b/app/drivers/sensor/pmw33xx/pmw33xx.c @@ -422,7 +422,9 @@ static int pmw33xx_init(const struct device *dev) { } #define PMW33XX_DATA_SPI(n) \ - { .cs_ctrl = {}, } + { \ + .cs_ctrl = {}, \ +.resume_interrupt = false,} #define PMW33XX_SPI_CFG(n) \ (&(struct pmw33xx_spi_cfg){ \ diff --git a/app/drivers/sensor/pmw33xx/pmw33xx.h b/app/drivers/sensor/pmw33xx/pmw33xx.h index a8f73214a00..a35db7b4ef4 100644 --- a/app/drivers/sensor/pmw33xx/pmw33xx.h +++ b/app/drivers/sensor/pmw33xx/pmw33xx.h @@ -120,6 +120,9 @@ struct pmw33xx_data { int16_t dx; int16_t dy; + struct k_timer poll_timer; + bool resume_interrupt; + const struct pmw33xx_transfer_function *hw_tf; #ifdef CONFIG_PMW33XX_TRIGGER diff --git a/app/drivers/sensor/pmw33xx/pmw33xx_trigger.c b/app/drivers/sensor/pmw33xx/pmw33xx_trigger.c index 5371ccf70bd..eb2efacea45 100644 --- a/app/drivers/sensor/pmw33xx/pmw33xx_trigger.c +++ b/app/drivers/sensor/pmw33xx/pmw33xx_trigger.c @@ -52,7 +52,8 @@ static void pmw33xx_thread_cb(const struct device *dev) { drv_data->handler(dev, drv_data->trigger); // Enable once the wall/spam of interrupts is solved - setup_int(dev, true); + if(drv_data->resume_interrupt) + setup_int(dev, true); } #ifdef CONFIG_PMW33XX_TRIGGER_OWN_THREAD From 1a5b52d6b906847c49cc5c80b5b2a7cf70417200 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Mon, 29 Aug 2022 09:53:42 +0800 Subject: [PATCH 064/157] use 25ms poll interval, and 15 poll counts --- app/boards/arm/nrfmacro/trackball/trackball.c | 4 ++-- app/drivers/sensor/pmw33xx/pmw33xx.h | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/boards/arm/nrfmacro/trackball/trackball.c b/app/boards/arm/nrfmacro/trackball/trackball.c index 8673db7cd41..31e28ecd24c 100644 --- a/app/boards/arm/nrfmacro/trackball/trackball.c +++ b/app/boards/arm/nrfmacro/trackball/trackball.c @@ -29,8 +29,8 @@ static int64_t time_buffer = 0; /* #endif */ static int polling_count = 0; -#define MAX_POLLING_COUNT 50 -#define TRACKBALL_POLL_INTERVAL 12 // in ms +#define MAX_POLLING_COUNT 15 +#define TRACKBALL_POLL_INTERVAL 25 // in ms // static struct sensor_value dx, dy; diff --git a/app/drivers/sensor/pmw33xx/pmw33xx.h b/app/drivers/sensor/pmw33xx/pmw33xx.h index a35db7b4ef4..20bedc1bc3a 100644 --- a/app/drivers/sensor/pmw33xx/pmw33xx.h +++ b/app/drivers/sensor/pmw33xx/pmw33xx.h @@ -120,7 +120,6 @@ struct pmw33xx_data { int16_t dx; int16_t dy; - struct k_timer poll_timer; bool resume_interrupt; const struct pmw33xx_transfer_function *hw_tf; From 2ccebde17da224ba3cab58c881ce9ebaa1a53fd2 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Mon, 29 Aug 2022 16:45:20 +0800 Subject: [PATCH 065/157] change trackball init priority from kscan level to sensor level Signed-off-by: Yong Zhou --- app/boards/arm/nrfmacro/trackball/trackball.c | 71 +++++++++++++++++-- 1 file changed, 65 insertions(+), 6 deletions(-) diff --git a/app/boards/arm/nrfmacro/trackball/trackball.c b/app/boards/arm/nrfmacro/trackball/trackball.c index 31e28ecd24c..af4cafb9da0 100644 --- a/app/boards/arm/nrfmacro/trackball/trackball.c +++ b/app/boards/arm/nrfmacro/trackball/trackball.c @@ -9,6 +9,9 @@ #include #include +#include +#include + #include #define SCROLL_DIV_FACTOR 5 @@ -29,8 +32,12 @@ static int64_t time_buffer = 0; /* #endif */ static int polling_count = 0; -#define MAX_POLLING_COUNT 15 -#define TRACKBALL_POLL_INTERVAL 25 // in ms +static int max_poll_count = 0; +static int polling_interval = 0; +#define BLE_POLL_COUNT 20 +#define BLE_POLL_INTERVAL 15 // in ms +#define USB_POLL_COUNT 300 +#define USB_POLL_INTERVAL 1 // in ms // static struct sensor_value dx, dy; @@ -133,7 +140,7 @@ K_TIMER_DEFINE(trackball_timer, trackball_timer_expiry, trackball_timer_stop); // timer expiry function void trackball_timer_expiry(struct k_timer *timer) { // check whether reaching the polling count limit - if(polling_count < MAX_POLLING_COUNT) { + if(polling_count < max_poll_count) { // submit polling work to mouse work queue k_work_submit_to_queue(zmk_mouse_work_q(), &trackball_poll_work); @@ -167,7 +174,7 @@ static void trackball_trigger_handler(const struct device *dev, const struct sen data->resume_interrupt = false; // start the polling timer - k_timer_start(&trackball_timer, K_NO_WAIT, K_MSEC(TRACKBALL_POLL_INTERVAL)); + k_timer_start(&trackball_timer, K_NO_WAIT, K_MSEC(polling_interval)); } static int trackball_init() { @@ -180,7 +187,59 @@ static int trackball_init() { LOG_ERR("can't set trigger"); return -EIO; }; - return 0; + + // init polling parameters + switch (zmk_endpoints_selected()) { +#if IS_ENABLED(CONFIG_ZMK_USB) + case ZMK_ENDPOINT_USB: { + max_poll_count = USB_POLL_COUNT; + polling_interval = USB_POLL_INTERVAL; + return 0; + } +#endif /* IS_ENABLED(CONFIG_ZMK_USB) */ + +#if IS_ENABLED(CONFIG_ZMK_BLE) + case ZMK_ENDPOINT_BLE: { + max_poll_count = BLE_POLL_COUNT; + polling_interval = BLE_POLL_INTERVAL; + return 0; + } +#endif /* IS_ENABLED(CONFIG_ZMK_BLE) */ + default: + LOG_ERR("Unsupported endpoint"); + return -ENOTSUP; + } +} + +SYS_INIT(trackball_init, APPLICATION, CONFIG_SENSOR_INIT_PRIORITY); + +////////////// +int trackball_endpoint_listener(const zmk_event_t *eh) { + LOG_INF("endpoint changing..."); + + // update polling parameters + switch (zmk_endpoints_selected()) { +#if IS_ENABLED(CONFIG_ZMK_USB) + case ZMK_ENDPOINT_USB: { + max_poll_count = USB_POLL_COUNT; + polling_interval = USB_POLL_INTERVAL; + break; + } +#endif /* IS_ENABLED(CONFIG_ZMK_USB) */ + +#if IS_ENABLED(CONFIG_ZMK_BLE) + case ZMK_ENDPOINT_BLE: { + max_poll_count = BLE_POLL_COUNT; + polling_interval = BLE_POLL_INTERVAL; + break; + } +#endif /* IS_ENABLED(CONFIG_ZMK_BLE) */ + default: + LOG_ERR("Unsupported endpoint"); + } + + return ZMK_EV_EVENT_BUBBLE; } -SYS_INIT(trackball_init, APPLICATION, CONFIG_ZMK_KSCAN_INIT_PRIORITY); +ZMK_LISTENER(trackball, trackball_endpoint_listener) +ZMK_SUBSCRIPTION(trackball, zmk_endpoint_selection_changed) From a273850353cb1e7b13f0e643db19bcefbf8fafa7 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Tue, 30 Aug 2022 06:17:02 +0800 Subject: [PATCH 066/157] add cpi dividor dts property --- app/boards/arm/nrfmacro/trackball/trackball.c | 5 +++++ app/drivers/zephyr/dts/bindings/sensor/pixart,pmw33xx.yaml | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/app/boards/arm/nrfmacro/trackball/trackball.c b/app/boards/arm/nrfmacro/trackball/trackball.c index af4cafb9da0..c4fc2e90a56 100644 --- a/app/boards/arm/nrfmacro/trackball/trackball.c +++ b/app/boards/arm/nrfmacro/trackball/trackball.c @@ -19,6 +19,9 @@ #define SCROLL_LAYER_INDEX COND_CODE_0(DT_INST_NODE_HAS_PROP(0, scroll_layer), (255), \ (DT_INST_PROP(0, scroll_layer))) +#define CPI_DIVIDOR COND_CODE_0(DT_INST_NODE_HAS_PROP(0, cpi_dividor), (1), \ + (DT_INST_PROP(0, cpi_dividor))) + /* #if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) */ // in us @@ -55,6 +58,8 @@ static int64_t trackball_update_handler(struct k_work *work) { // remaining scroll from last update static int8_t scroll_ver_rem = 0, scroll_hor_rem = 0; + dx.val1 = dx.val1/CPI_DIVIDOR; + dy.val1 = dy.val1/CPI_DIVIDOR; // update report with latest position zmk_hid_mouse_movement_set(0, 0); diff --git a/app/drivers/zephyr/dts/bindings/sensor/pixart,pmw33xx.yaml b/app/drivers/zephyr/dts/bindings/sensor/pixart,pmw33xx.yaml index 9c26fcc4c85..7fbe91c89f6 100644 --- a/app/drivers/zephyr/dts/bindings/sensor/pixart,pmw33xx.yaml +++ b/app/drivers/zephyr/dts/bindings/sensor/pixart,pmw33xx.yaml @@ -22,6 +22,10 @@ properties: type: int description: mouse cpi required: false + cpi-dividor: + type: int + description: mouse cpi dividor + required: false disable-rest: type: boolean description: disables mouse rest mode, will decrease battery life From 4c69b267f9f7a700da7a79d0522e28099c38d0c1 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Tue, 30 Aug 2022 07:36:02 +0800 Subject: [PATCH 067/157] [add] pmw3360 firmware id checking in init_chip The firmware id is the second byte of the SROM hex array. The version of pmw3360 srom used in zmk (i.e., pmw3360_srom.h) is 0x05. --- app/drivers/sensor/pmw33xx/pmw3360_srom.h | 3 +++ app/drivers/sensor/pmw33xx/pmw33xx.c | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/drivers/sensor/pmw33xx/pmw3360_srom.h b/app/drivers/sensor/pmw33xx/pmw3360_srom.h index 44a5b316994..8a27e611fd3 100644 --- a/app/drivers/sensor/pmw33xx/pmw3360_srom.h +++ b/app/drivers/sensor/pmw33xx/pmw3360_srom.h @@ -12,6 +12,9 @@ SROM OR THE USE OR OTHER DEALINGS IN THIS SROM. #ifndef ZEPHYR_DRIVERS_SENSOR_PIXART_PMW33XX_PMW3360_SROM_H_ #define ZEPHYR_DRIVERS_SENSOR_PIXART_PMW33XX_PMW3360_SROM_H_ +// firmware vesion id (the second byte in srom hex array) +#define PMW3360_FIRMWARE_ID 0x05 + #include static const uint8_t SROM[] = { 0x01, 0x05, 0x8d, 0x92, 0x66, 0x67, 0x1e, 0xbe, 0xfe, 0x5f, 0x1d, 0xb8, 0xf2, 0x66, 0x4e, diff --git a/app/drivers/sensor/pmw33xx/pmw33xx.c b/app/drivers/sensor/pmw33xx/pmw33xx.c index b661019111e..63c9ce40ccc 100644 --- a/app/drivers/sensor/pmw33xx/pmw33xx.c +++ b/app/drivers/sensor/pmw33xx/pmw33xx.c @@ -357,7 +357,8 @@ static int pmw33xx_init_chip(const struct device *dev) { LOG_ERR("could not check srom id %d", err); return -EIO; } - if (!srom_id) { + LOG_INF("pmw33xx SROM firmware id: %d", srom_id); + if (srom_id != PMW3360_FIRMWARE_ID) { LOG_ERR("srom id invalid %d", srom_id); return -EIO; } From d9b69fa859e564805336c0d39b0b89c0c2dcba9f Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Tue, 30 Aug 2022 21:42:55 +0800 Subject: [PATCH 068/157] [add] init version of standalone pmw3360 driver and a new trakball implementation --- app/boards/arm/nrfmacro/CMakeLists.txt | 1 + .../arm/nrfmacro/trackball/CMakeLists.txt | 3 +- app/boards/arm/nrfmacro/trackball/trackball.c | 2 + .../arm/nrfmacro/trackball/trackball_new.c | 264 ++++ app/drivers/sensor/CMakeLists.txt | 1 + app/drivers/sensor/Kconfig | 1 + app/drivers/sensor/pmw3360/CMakeLists.txt | 7 + app/drivers/sensor/pmw3360/Kconfig | 75 ++ app/drivers/sensor/pmw3360/pmw3360.c | 1074 +++++++++++++++++ app/drivers/sensor/pmw3360/pmw3360.h | 107 ++ app/drivers/sensor/pmw3360/pmw3360_priv.c | 297 +++++ .../dts/bindings/sensor/pixart,pmw3360.yaml | 20 + 12 files changed, 1851 insertions(+), 1 deletion(-) create mode 100644 app/boards/arm/nrfmacro/trackball/trackball_new.c create mode 100644 app/drivers/sensor/pmw3360/CMakeLists.txt create mode 100644 app/drivers/sensor/pmw3360/Kconfig create mode 100644 app/drivers/sensor/pmw3360/pmw3360.c create mode 100644 app/drivers/sensor/pmw3360/pmw3360.h create mode 100644 app/drivers/sensor/pmw3360/pmw3360_priv.c create mode 100644 app/drivers/zephyr/dts/bindings/sensor/pixart,pmw3360.yaml diff --git a/app/boards/arm/nrfmacro/CMakeLists.txt b/app/boards/arm/nrfmacro/CMakeLists.txt index e63d4a9111c..a69f091f34c 100644 --- a/app/boards/arm/nrfmacro/CMakeLists.txt +++ b/app/boards/arm/nrfmacro/CMakeLists.txt @@ -12,4 +12,5 @@ endif() if (CONFIG_NRFMACRO_POINTDEVICE) add_subdirectory_ifdef(CONFIG_PINNACLE trackpad) add_subdirectory_ifdef(CONFIG_PMW33XX trackball) + add_subdirectory_ifdef(CONFIG_PMW3360 trackball) endif() diff --git a/app/boards/arm/nrfmacro/trackball/CMakeLists.txt b/app/boards/arm/nrfmacro/trackball/CMakeLists.txt index a6870771d61..5d7b060667a 100644 --- a/app/boards/arm/nrfmacro/trackball/CMakeLists.txt +++ b/app/boards/arm/nrfmacro/trackball/CMakeLists.txt @@ -1,5 +1,6 @@ zephyr_library() -zephyr_library_sources(trackball.c) +zephyr_library_sources_ifdef(CONFIG_PMW33XX trackball.c) +zephyr_library_sources_ifdef(CONFIG_PMW3360 trackball_new.c) zephyr_library_include_directories(${CMAKE_SOURCE_DIR}/include) zephyr_library_include_directories(${CMAKE_SOURCE_DIR}/drivers/sensor) zephyr_library_include_directories(${ZEPHYR_BASE}/drivers) diff --git a/app/boards/arm/nrfmacro/trackball/trackball.c b/app/boards/arm/nrfmacro/trackball/trackball.c index c4fc2e90a56..6ab91478b2f 100644 --- a/app/boards/arm/nrfmacro/trackball/trackball.c +++ b/app/boards/arm/nrfmacro/trackball/trackball.c @@ -174,6 +174,7 @@ void trackball_timer_stop(struct k_timer *timer) { // trigger handler static void trackball_trigger_handler(const struct device *dev, const struct sensor_trigger *trig) { struct pmw33xx_data *data = dev->data; + LOG_INF("I'm OLD trackball implementation"); // do not resume motion interrupt data->resume_interrupt = false; @@ -183,6 +184,7 @@ static void trackball_trigger_handler(const struct device *dev, const struct sen } static int trackball_init() { + struct sensor_trigger trigger = { .type = SENSOR_TRIG_DATA_READY, .chan = SENSOR_CHAN_ALL, diff --git a/app/boards/arm/nrfmacro/trackball/trackball_new.c b/app/boards/arm/nrfmacro/trackball/trackball_new.c new file mode 100644 index 00000000000..9381bb2fda9 --- /dev/null +++ b/app/boards/arm/nrfmacro/trackball/trackball_new.c @@ -0,0 +1,264 @@ +#define DT_DRV_COMPAT pixart_pmw3360 + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#define SCROLL_DIV_FACTOR 5 +/* #define SCROLL_LAYER_INDEX 4 */ +#define SCROLL_LAYER_INDEX COND_CODE_0(DT_INST_NODE_HAS_PROP(0, scroll_layer), (255), \ + (DT_INST_PROP(0, scroll_layer))) + +#define CPI_DIVIDOR COND_CODE_0(DT_INST_NODE_HAS_PROP(0, cpi_dividor), (1), \ + (DT_INST_PROP(0, cpi_dividor))) + + +/* #if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) */ +// in us +static int64_t last_interrupt_time = 0; +static int64_t current_interrupt_time = 0; +static int64_t interrupt_interval = 0; +static int64_t handler_duration = 0; +static int64_t send_report_duration = 0; +static int64_t idle_interval = 0; +static int64_t time_buffer = 0; +/* #endif */ + +static int polling_count = 0; +static int max_poll_count = 0; +static int polling_interval = 0; +#define BLE_POLL_COUNT 20 +#define BLE_POLL_INTERVAL 15 // in ms +#define USB_POLL_COUNT 300 +#define USB_POLL_INTERVAL 1 // in ms + + +LOG_MODULE_REGISTER(trackball, CONFIG_SENSOR_LOG_LEVEL); + +// polling work +static void trackball_poll_handler(struct k_work *work) { +/* #if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) */ + current_interrupt_time = k_ticks_to_us_floor64(k_uptime_ticks()); + interrupt_interval = current_interrupt_time - last_interrupt_time; + idle_interval = current_interrupt_time - time_buffer; +/* #endif */ + + // get the device pointer + struct pmw3360_data *data = CONTAINER_OF(work, struct pmw3360_data, poll_work); + const struct device *dev = data->dev; + + // fetch dx and dy from sensor and save them into pmw3360_data structure + int ret = sensor_sample_fetch(dev); + if (ret < 0) { + LOG_ERR("fetch: %d", ret); + return; + } + + /* // get the x, y delta */ + /* ret = sensor_channel_get(trackball, SENSOR_CHAN_POS_DX, &dx); */ + /* if (ret < 0) { */ + /* LOG_ERR("get dx: %d", ret); */ + /* return; */ + /* } */ + /* ret = sensor_channel_get(trackball, SENSOR_CHAN_POS_DY, &dy); */ + /* if (ret < 0) { */ + /* LOG_ERR("get dy: %d", ret); */ + /* return; */ + /* } */ + + // remaining scroll from last update + static int16_t scroll_ver_rem = 0, scroll_hor_rem = 0; + static int64_t start_time = 0; + if(data->x != 0 || data->y != 0) { + // process the updated position and send to host + /* k_work_submit_to_queue(zmk_mouse_work_q(), &trackball_update); */ + /* #if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) */ + start_time = k_ticks_to_us_floor64(k_uptime_ticks()); + /* #endif */ + + // update report with latest position + zmk_hid_mouse_movement_set(0, 0); + zmk_hid_mouse_scroll_set(0, 0); + + const uint8_t layer = zmk_keymap_highest_layer_active(); + if (layer == SCROLL_LAYER_INDEX) { // lower + const int16_t total_hor = data->x + scroll_hor_rem, total_ver = -(data->y + scroll_ver_rem); + scroll_hor_rem = total_hor % SCROLL_DIV_FACTOR; + scroll_ver_rem = total_ver % SCROLL_DIV_FACTOR; + zmk_hid_mouse_scroll_update(total_hor / SCROLL_DIV_FACTOR, total_ver / SCROLL_DIV_FACTOR); + } else { + zmk_hid_mouse_movement_update(CLAMP(data->x, INT8_MIN, INT8_MAX), CLAMP(data->y, INT8_MIN, INT8_MAX)); + } + + // send the report to host + zmk_endpoints_send_mouse_report(); + + send_report_duration = k_ticks_to_us_floor64(k_uptime_ticks()) - start_time; + LOG_INF("Position updated: poll interval: %lld; send time: %lld ; new pos: %d %d",\ + interrupt_interval, send_report_duration, data->x, data->y); + } + +/* #if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) */ + time_buffer = k_ticks_to_us_floor64(k_uptime_ticks()); + handler_duration = time_buffer - current_interrupt_time; + LOG_DBG("idle time: %lld ; handler time cost: %lld", \ + idle_interval, handler_duration); + + last_interrupt_time = current_interrupt_time; +} + +// trigger handler +static void trackball_trigger_handler(const struct device *dev, const struct sensor_trigger *trig) { + LOG_INF("I'm new trackball implementation"); + + struct pmw3360_data *data = dev->data; + + // do not resume motion interrupt by passing-in null handler + struct sensor_trigger trigger = { + .type = SENSOR_TRIG_DATA_READY, + .chan = SENSOR_CHAN_ALL, + }; + if (sensor_trigger_set(dev, &trigger, NULL) < 0) { + LOG_ERR("can't stop motion interrupt line"); + }; + + // start the polling timer (the real work now is dispatched to a timer-based polling) + k_timer_start(&data->poll_timer, K_NO_WAIT, K_MSEC(polling_interval)); +} + +// timer expiry function +void trackball_timer_expiry(struct k_timer *timer) { + struct pmw3360_data *data = CONTAINER_OF(timer, struct pmw3360_data, poll_timer); + + // check whether reaching the polling count limit + if(polling_count < max_poll_count) { + // submit polling work to mouse work queue + k_work_submit_to_queue(zmk_mouse_work_q(), &data->poll_work); + + // update status + polling_count++; + } + else { + // stop timer + k_timer_stop(&data->poll_timer); + } +} + +// timer stop function +void trackball_timer_stop(struct k_timer *timer) { + struct pmw3360_data *data = CONTAINER_OF(timer, struct pmw3360_data, poll_timer); + const struct device *dev = data->dev; + + // reset polling count + polling_count = 0; + + // resume motion interrupt line by setting the handler to a real object + struct sensor_trigger trigger = { + .type = SENSOR_TRIG_DATA_READY, + .chan = SENSOR_CHAN_ALL, + }; + if (sensor_trigger_set(dev, &trigger, trackball_trigger_handler) < 0) { + LOG_ERR("can't resume motion interrupt line"); + }; +} + + +/* Setup the trigger handler at system powerup */ +// The device instance should be determined in this step. All other functions should only +// use the device poiter passed as an argument. +// In this applaication, the devcie instance 'trackball' is hard-coded as the first +// pixart_pmw3360 node in dts file and used implicitly here. +static int trackball_init() { + // get the sensor device instance + const struct device *dev = DEVICE_DT_GET(DT_DRV_INST(0)); + struct pmw3360_data *data = dev->data; + + // setup the timer and handler function of the polling work + k_timer_init(&data->poll_timer, trackball_timer_expiry, trackball_timer_stop); + k_work_init(&data->poll_work, trackball_poll_handler); + + // set up the trigger handler (i.e. the entry point to other code in this file) + struct sensor_trigger trigger = { + .type = SENSOR_TRIG_DATA_READY, + .chan = SENSOR_CHAN_ALL, + }; + + int err; + do { + err = sensor_trigger_set(dev, &trigger, trackball_trigger_handler); + if (err == -EBUSY) { + k_sleep(K_MSEC(1)); + } + } while (err == -EBUSY); + + if (err) { + LOG_ERR("Cannot enable trigger"); + return err; + } + + // init polling parameters based on current endpoint + switch (zmk_endpoints_selected()) { +#if IS_ENABLED(CONFIG_ZMK_USB) + case ZMK_ENDPOINT_USB: { + max_poll_count = USB_POLL_COUNT; + polling_interval = USB_POLL_INTERVAL; + return 0; + } +#endif /* IS_ENABLED(CONFIG_ZMK_USB) */ + +#if IS_ENABLED(CONFIG_ZMK_BLE) + case ZMK_ENDPOINT_BLE: { + max_poll_count = BLE_POLL_COUNT; + polling_interval = BLE_POLL_INTERVAL; + return 0; + } +#endif /* IS_ENABLED(CONFIG_ZMK_BLE) */ + default: + LOG_ERR("Unsupported endpoint"); + return -ENOTSUP; + } +} + +SYS_INIT(trackball_init, APPLICATION, CONFIG_SENSOR_INIT_PRIORITY); + +/* the following code dynamically changes poll rate based on endpoint changing */ +// The code execution is driven by the zmk event system instead of zephyr interrupt routine +int trackball_endpoint_listener(const zmk_event_t *eh) { + LOG_INF("endpoint changing..."); + + // update polling parameters + switch (zmk_endpoints_selected()) { +#if IS_ENABLED(CONFIG_ZMK_USB) + case ZMK_ENDPOINT_USB: { + max_poll_count = USB_POLL_COUNT; + polling_interval = USB_POLL_INTERVAL; + break; + } +#endif /* IS_ENABLED(CONFIG_ZMK_USB) */ + +#if IS_ENABLED(CONFIG_ZMK_BLE) + case ZMK_ENDPOINT_BLE: { + max_poll_count = BLE_POLL_COUNT; + polling_interval = BLE_POLL_INTERVAL; + break; + } +#endif /* IS_ENABLED(CONFIG_ZMK_BLE) */ + default: + LOG_ERR("Unsupported endpoint"); + } + + return ZMK_EV_EVENT_BUBBLE; +} + +ZMK_LISTENER(trackball, trackball_endpoint_listener) +ZMK_SUBSCRIPTION(trackball, zmk_endpoint_selection_changed) diff --git a/app/drivers/sensor/CMakeLists.txt b/app/drivers/sensor/CMakeLists.txt index 80e5dfe69db..5e1fa110a5f 100644 --- a/app/drivers/sensor/CMakeLists.txt +++ b/app/drivers/sensor/CMakeLists.txt @@ -4,4 +4,5 @@ add_subdirectory_ifdef(CONFIG_ZMK_BATTERY battery) add_subdirectory_ifdef(CONFIG_EC11 ec11) add_subdirectory_ifdef(CONFIG_PMW33XX pmw33xx) +add_subdirectory_ifdef(CONFIG_PMW3360 pmw3360) add_subdirectory_ifdef(CONFIG_PINNACLE cirque_trackpad) diff --git a/app/drivers/sensor/Kconfig b/app/drivers/sensor/Kconfig index 59c49b15608..beef0e5b32f 100644 --- a/app/drivers/sensor/Kconfig +++ b/app/drivers/sensor/Kconfig @@ -4,4 +4,5 @@ rsource "battery/Kconfig" rsource "ec11/Kconfig" rsource "pmw33xx/Kconfig" +rsource "pmw3360/Kconfig" rsource "cirque_trackpad/Kconfig" diff --git a/app/drivers/sensor/pmw3360/CMakeLists.txt b/app/drivers/sensor/pmw3360/CMakeLists.txt new file mode 100644 index 00000000000..d08e76f799b --- /dev/null +++ b/app/drivers/sensor/pmw3360/CMakeLists.txt @@ -0,0 +1,7 @@ +# Copyright (c) 2022 The ZMK Contributors +# SPDX-License-Identifier: MIT + +zephyr_library() + +zephyr_library_sources(pmw3360.c) +zephyr_library_sources(pmw3360_priv.c) diff --git a/app/drivers/sensor/pmw3360/Kconfig b/app/drivers/sensor/pmw3360/Kconfig new file mode 100644 index 00000000000..97754cfeaec --- /dev/null +++ b/app/drivers/sensor/pmw3360/Kconfig @@ -0,0 +1,75 @@ +# Sensor data simulator +# +# Copyright (c) 2019 Nordic Semiconductor +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +menuconfig PMW3360 + bool "PMW3360 mouse optical sensor" + help + Enable PMW3360 mouse optical sensor. + +if PMW3360 + +config PMW3360_CPI + int "PMW3360's default CPI" + default 1600 + range 100 12000 + help + Default CPI value. + +config PMW3360_CPI_DIVIDOR + int "PMW3360's default CPI dividor" + default 4 + range 1 12 + help + Default CPI dividor value. + +config PMW3360_RUN_DOWNSHIFT_TIME_MS + int "PMW3360's default RUN mode downshift time" + default 500 + range 10 2550 + help + Default RUN mode downshift down time in milliseconds. + Time after which sensor goes from RUN to REST1 mode. + +config PMW3360_REST1_DOWNSHIFT_TIME_MS + int "PMW3360's default REST1 mode downshift time" + default 9220 + range 320 81600 + help + Default REST1 mode downshift down time in milliseconds. + Time after which sensor goes from REST1 to REST2 mode. + +config PMW3360_REST2_DOWNSHIFT_TIME_MS + int "PMW3360's default REST2 mode downshift time" + default 150000 + range 3200 816000 + help + Default REST2 mode downshift down time in milliseconds. + Time after which sensor goes from REST2 to REST3 mode. + +choice + prompt "Select PMW3360 sensor orientation" + default PMW3360_ORIENTATION_0 + +config PMW3360_ORIENTATION_0 + bool "PMW3360 not rotated" + +config PMW3360_ORIENTATION_90 + bool "PMW3360 rotated 90 deg clockwise" + +config PMW3360_ORIENTATION_180 + bool "PMW3360 rotated 180 deg clockwise" + +config PMW3360_ORIENTATION_270 + bool "PMW3360 rotated 270 deg clockwise" + +endchoice + +module = PMW3360 +module-str = PMW3360 +source "${ZEPHYR_BASE}/subsys/logging/Kconfig.template.log_config" + +endif #PMW3360 diff --git a/app/drivers/sensor/pmw3360/pmw3360.c b/app/drivers/sensor/pmw3360/pmw3360.c new file mode 100644 index 00000000000..3b4314765b3 --- /dev/null +++ b/app/drivers/sensor/pmw3360/pmw3360.c @@ -0,0 +1,1074 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#define DT_DRV_COMPAT pixart_pmw3360 + +#include +#include +#include "pmw3360.h" + +#include +LOG_MODULE_REGISTER(pmw3360, CONFIG_PMW3360_LOG_LEVEL); + +/* Timings defined by spec (in us) */ +#define T_NCS_SCLK 1 /* 120 ns (rounded to 1us?)*/ +#define T_SRX (20 - T_NCS_SCLK) /* 20 us */ +#define T_SCLK_NCS_WR (35 - T_NCS_SCLK) /* 35 us */ +#define T_SWX (180 - T_SCLK_NCS_WR) /* 180 us */ +#define T_SRAD 160 /* 160 us */ +#define T_SRAD_MOTBR 35 /* 35 us */ +#define T_BEXIT 1 /* 500 ns (rounded to 1us?)*/ + +/* Timing defined on SROM download burst mode figure */ +#define T_BRSEP 15 /* 15 us */ + + +/* Sensor registers */ +#define PMW3360_REG_PRODUCT_ID 0x00 +#define PMW3360_REG_REVISION_ID 0x01 +#define PMW3360_REG_MOTION 0x02 +#define PMW3360_REG_DELTA_X_L 0x03 +#define PMW3360_REG_DELTA_X_H 0x04 +#define PMW3360_REG_DELTA_Y_L 0x05 +#define PMW3360_REG_DELTA_Y_H 0x06 +#define PMW3360_REG_SQUAL 0x07 +#define PMW3360_REG_RAW_DATA_SUM 0x08 +#define PMW3360_REG_MAXIMUM_RAW_DATA 0x09 +#define PMW3360_REG_MINIMUM_RAW_DATA 0x0A +#define PMW3360_REG_SHUTTER_LOWER 0x0B +#define PMW3360_REG_SHUTTER_UPPER 0x0C +#define PMW3360_REG_CONTROL 0x0D +#define PMW3360_REG_CONFIG1 0x0F +#define PMW3360_REG_CONFIG2 0x10 +#define PMW3360_REG_ANGLE_TUNE 0x11 +#define PMW3360_REG_FRAME_CAPTURE 0x12 +#define PMW3360_REG_SROM_ENABLE 0x13 +#define PMW3360_REG_RUN_DOWNSHIFT 0x14 +#define PMW3360_REG_REST1_RATE_LOWER 0x15 +#define PMW3360_REG_REST1_RATE_UPPER 0x16 +#define PMW3360_REG_REST1_DOWNSHIFT 0x17 +#define PMW3360_REG_REST2_RATE_LOWER 0x18 +#define PMW3360_REG_REST2_RATE_UPPER 0x19 +#define PMW3360_REG_REST2_DOWNSHIFT 0x1A +#define PMW3360_REG_REST3_RATE_LOWER 0x1B +#define PMW3360_REG_REST3_RATE_UPPER 0x1C +#define PMW3360_REG_OBSERVATION 0x24 +#define PMW3360_REG_DATA_OUT_LOWER 0x25 +#define PMW3360_REG_DATA_OUT_UPPER 0x26 +#define PMW3360_REG_RAW_DATA_DUMP 0x29 +#define PMW3360_REG_SROM_ID 0x2A +#define PMW3360_REG_MIN_SQ_RUN 0x2B +#define PMW3360_REG_RAW_DATA_THRESHOLD 0x2C +#define PMW3360_REG_CONFIG5 0x2F +#define PMW3360_REG_POWER_UP_RESET 0x3A +#define PMW3360_REG_SHUTDOWN 0x3B +#define PMW3360_REG_INVERSE_PRODUCT_ID 0x3F +#define PMW3360_REG_LIFTCUTOFF_TUNE3 0x41 +#define PMW3360_REG_ANGLE_SNAP 0x42 +#define PMW3360_REG_LIFTCUTOFF_TUNE1 0x4A +#define PMW3360_REG_MOTION_BURST 0x50 +#define PMW3360_REG_LIFTCUTOFF_TUNE_TIMEOUT 0x58 +#define PMW3360_REG_LIFTCUTOFF_TUNE_MIN_LENGTH 0x5A +#define PMW3360_REG_SROM_LOAD_BURST 0x62 +#define PMW3360_REG_LIFT_CONFIG 0x63 +#define PMW3360_REG_RAW_DATA_BURST 0x64 +#define PMW3360_REG_LIFTCUTOFF_TUNE2 0x65 + +/* Sensor identification values */ +#define PMW3360_PRODUCT_ID 0x42 +#define PMW3360_FIRMWARE_ID 0x04 + +/* Max register count readable in a single motion burst */ +#define PMW3360_MAX_BURST_SIZE 12 + +/* Register count used for reading a single motion burst */ +#define PMW3360_BURST_SIZE 6 + +/* Position of X in motion burst data */ +#define PMW3360_DX_POS 2 +#define PMW3360_DY_POS 4 + +/* Rest_En position in Config2 register. */ +#define PMW3360_REST_EN_POS 5 + +#define PMW3360_MAX_CPI 12000 +#define PMW3360_MIN_CPI 100 + + +#define SPI_WRITE_BIT BIT(7) + +/* Helper macros used to convert sensor values. */ +#define PMW3360_SVALUE_TO_CPI(svalue) ((uint32_t)(svalue).val1) +#define PMW3360_SVALUE_TO_TIME(svalue) ((uint32_t)(svalue).val1) +#define PMW3360_SVALUE_TO_BOOL(svalue) ((svalue).val1 != 0) + + +/* SROM firmware meta-data, defined in pmw3360_piv.c */ +extern const size_t pmw3360_firmware_length; +extern const uint8_t pmw3360_firmware_data[]; + + +/* sensor initialization steps definition */ +// init is done in non-blocking manner (i.e., async), a delayable work is defined for this job +// see pmw3360_init and pmw3360_async_init) + +// delay (ms) in between steps +static const int32_t async_init_delay[ASYNC_INIT_STEP_COUNT] = { + [ASYNC_INIT_STEP_POWER_UP] = 1, + [ASYNC_INIT_STEP_FW_LOAD_START] = 50, // required in spec + [ASYNC_INIT_STEP_FW_LOAD_CONTINUE] = 10, // required in spec + [ASYNC_INIT_STEP_FW_LOAD_VERIFY] = 1, + [ASYNC_INIT_STEP_CONFIGURE] = 0, +}; + +static int pmw3360_async_init_power_up(const struct device *dev); +static int pmw3360_async_init_configure(const struct device *dev); +static int pmw3360_async_init_fw_load_verify(const struct device *dev); +static int pmw3360_async_init_fw_load_continue(const struct device *dev); +static int pmw3360_async_init_fw_load_start(const struct device *dev); + +static int (* const async_init_fn[ASYNC_INIT_STEP_COUNT])(const struct device *dev) = { + [ASYNC_INIT_STEP_POWER_UP] = pmw3360_async_init_power_up, + [ASYNC_INIT_STEP_FW_LOAD_START] = pmw3360_async_init_fw_load_start, + [ASYNC_INIT_STEP_FW_LOAD_CONTINUE] = pmw3360_async_init_fw_load_continue, + [ASYNC_INIT_STEP_FW_LOAD_VERIFY] = pmw3360_async_init_fw_load_verify, + [ASYNC_INIT_STEP_CONFIGURE] = pmw3360_async_init_configure, +}; + +static int spi_cs_ctrl(const struct device *dev, bool enable) +{ + const struct pmw3360_config *config = dev->config; + int err; + + if (!enable) { + k_busy_wait(T_NCS_SCLK); + } + + err = gpio_pin_set_dt(&config->cs_gpio, (int)enable); + if (err) { + LOG_ERR("SPI CS ctrl failed"); + } + + if (enable) { + k_busy_wait(T_NCS_SCLK); + } + + return err; +} + +static int reg_read(const struct device *dev, uint8_t reg, uint8_t *buf) +{ + int err; + struct pmw3360_data *data = dev->data; + const struct pmw3360_config *config = dev->config; + + __ASSERT_NO_MSG((reg & SPI_WRITE_BIT) == 0); + + err = spi_cs_ctrl(dev, true); + if (err) { + return err; + } + + /* Write register address. */ + const struct spi_buf tx_buf = { + .buf = ®, + .len = 1 + }; + const struct spi_buf_set tx = { + .buffers = &tx_buf, + .count = 1 + }; + + err = spi_write_dt(&config->bus, &tx); + if (err) { + LOG_ERR("Reg read failed on SPI write"); + return err; + } + + k_busy_wait(T_SRAD); + + /* Read register value. */ + struct spi_buf rx_buf = { + .buf = buf, + .len = 1, + }; + const struct spi_buf_set rx = { + .buffers = &rx_buf, + .count = 1, + }; + + err = spi_read_dt(&config->bus, &rx); + if (err) { + LOG_ERR("Reg read failed on SPI read"); + return err; + } + + err = spi_cs_ctrl(dev, false); + if (err) { + return err; + } + + k_busy_wait(T_SRX); + + data->last_read_burst = false; + + return 0; +} + +static int reg_write(const struct device *dev, uint8_t reg, uint8_t val) +{ + int err; + struct pmw3360_data *data = dev->data; + const struct pmw3360_config *config = dev->config; + + __ASSERT_NO_MSG((reg & SPI_WRITE_BIT) == 0); + + err = spi_cs_ctrl(dev, true); + if (err) { + return err; + } + + uint8_t buf[] = { + SPI_WRITE_BIT | reg, + val + }; + const struct spi_buf tx_buf = { + .buf = buf, + .len = ARRAY_SIZE(buf) + }; + const struct spi_buf_set tx = { + .buffers = &tx_buf, + .count = 1 + }; + + err = spi_write_dt(&config->bus, &tx); + if (err) { + LOG_ERR("Reg write failed on SPI write"); + return err; + } + + k_busy_wait(T_SCLK_NCS_WR); + + err = spi_cs_ctrl(dev, false); + if (err) { + return err; + } + + k_busy_wait(T_SWX); + + data->last_read_burst = false; + + return 0; +} + +static int motion_burst_read(const struct device *dev, uint8_t *buf, + size_t burst_size) +{ + int err; + struct pmw3360_data *data = dev->data; + const struct pmw3360_config *config = dev->config; + + __ASSERT_NO_MSG(burst_size <= PMW3360_MAX_BURST_SIZE); + + /* Write any value to motion burst register only if there have been + * other SPI transmissions with sensor since last burst read. + */ + if (!data->last_read_burst) { + err = reg_write(dev, PMW3360_REG_MOTION_BURST, 0x00); + if (err) { + return err; + } + } + + err = spi_cs_ctrl(dev, true); + if (err) { + return err; + } + + /* Send motion burst address */ + uint8_t reg_buf[] = { + PMW3360_REG_MOTION_BURST + }; + const struct spi_buf tx_buf = { + .buf = reg_buf, + .len = ARRAY_SIZE(reg_buf) + }; + const struct spi_buf_set tx = { + .buffers = &tx_buf, + .count = 1 + }; + + err = spi_write_dt(&config->bus, &tx); + if (err) { + LOG_ERR("Motion burst failed on SPI write"); + return err; + } + + k_busy_wait(T_SRAD_MOTBR); + + const struct spi_buf rx_buf = { + .buf = buf, + .len = burst_size, + }; + const struct spi_buf_set rx = { + .buffers = &rx_buf, + .count = 1 + }; + + err = spi_read_dt(&config->bus, &rx); + if (err) { + LOG_ERR("Motion burst failed on SPI read"); + return err; + } + + /* Terminate burst */ + err = spi_cs_ctrl(dev, false); + if (err) { + return err; + } + + k_busy_wait(T_BEXIT); + + data->last_read_burst = true; + + return 0; +} + +static int burst_write(const struct device *dev, uint8_t reg, const uint8_t *buf, + size_t size) +{ + int err; + struct pmw3360_data *data = dev->data; + const struct pmw3360_config *config = dev->config; + + /* Write address of burst register */ + uint8_t write_buf = reg | SPI_WRITE_BIT; + struct spi_buf tx_buf = { + .buf = &write_buf, + .len = 1 + }; + const struct spi_buf_set tx = { + .buffers = &tx_buf, + .count = 1 + }; + + err = spi_cs_ctrl(dev, true); + if (err) { + return err; + } + + err = spi_write_dt(&config->bus, &tx); + if (err) { + LOG_ERR("Burst write failed on SPI write"); + return err; + } + + /* Write data */ + for (size_t i = 0; i < size; i++) { + write_buf = buf[i]; + + err = spi_write_dt(&config->bus, &tx); + if (err) { + LOG_ERR("Burst write failed on SPI write (data)"); + return err; + } + + k_busy_wait(T_BRSEP); + } + + /* Terminate burst mode. */ + err = spi_cs_ctrl(dev, false); + if (err) { + return err; + } + + k_busy_wait(T_BEXIT); + + data->last_read_burst = false; + + return 0; +} + +static int update_cpi(const struct device *dev, uint32_t cpi) +{ + /* Set resolution with CPI step of 100 cpi + * 0x00: 100 cpi (minimum cpi) + * 0x01: 200 cpi + * : + * 0x31: 5000 cpi (default cpi) + * : + * 0x77: 12000 cpi (maximum cpi) + */ + + if ((cpi > PMW3360_MAX_CPI) || (cpi < PMW3360_MIN_CPI)) { + LOG_ERR("CPI value %u out of range", cpi); + return -EINVAL; + } + + /* Convert CPI to register value */ + uint8_t value = (cpi / 100) - 1; + + LOG_INF("Setting CPI to %u (reg value 0x%x)", cpi, value); + + int err = reg_write(dev, PMW3360_REG_CONFIG1, value); + if (err) { + LOG_ERR("Failed to change CPI"); + } + + return err; +} + +/* unit: ms */ +static int update_downshift_time(const struct device *dev, uint8_t reg_addr, + uint32_t time) +{ + /* Set downshift time in ms: + * - Run downshift time (from Run to Rest1 mode), default: 500ms + * - Rest 1 downshift time (from Rest1 to Rest2 mode), default: 9.92 s + * - Rest 2 downshift time (from Rest2 to Rest3 mode), default: ~10 min + */ + uint32_t maxtime; + uint32_t mintime; + + switch (reg_addr) { + case PMW3360_REG_RUN_DOWNSHIFT: + /* + * Run downshift time = PMW3360_REG_RUN_DOWNSHIFT * 10 ms + */ + maxtime = 2550; + mintime = 10; + break; + + case PMW3360_REG_REST1_DOWNSHIFT: + /* + * Rest1 downshift time = PMW3360_REG_RUN_DOWNSHIFT + * * 320 * Rest1 rate (default 1 ms) + */ + maxtime = 81600; + mintime = 320; + break; + + case PMW3360_REG_REST2_DOWNSHIFT: + /* + * Rest2 downshift time = PMW3360_REG_REST2_DOWNSHIFT + * * 32 * Rest2 rate (default 100 ms) + */ + maxtime = 816000; + mintime = 3200; + break; + + default: + LOG_ERR("Not supported"); + return -ENOTSUP; + } + + if ((time > maxtime) || (time < mintime)) { + LOG_WRN("Downshift time %u out of range", time); + return -EINVAL; + } + + __ASSERT_NO_MSG((mintime > 0) && (maxtime/mintime <= UINT8_MAX)); + + /* Convert time to register value */ + uint8_t value = time / mintime; + + LOG_INF("Set downshift time to %u ms (reg value 0x%x)", time, value); + + int err = reg_write(dev, reg_addr, value); + if (err) { + LOG_ERR("Failed to change downshift time"); + } + + return err; +} + +/* set sampling rate in each mode (in ms) */ +static int update_sample_time(const struct device *dev, + uint8_t reg_addr_lower, + uint8_t reg_addr_upper, + uint32_t sample_time) +{ + /* Set sample time for the Rest1-Rest3 modes. + * Values above 0x09B0 will trigger internal watchdog reset. + */ + uint32_t maxtime = 0x9B0; + uint32_t mintime = 1; + + if ((sample_time > maxtime) || (sample_time < mintime)) { + LOG_WRN("Sample time %u out of range", sample_time); + return -EINVAL; + } + + LOG_INF("Set sample time to %u ms", sample_time); + + /* The sample time is (reg_value + 1) ms. */ + sample_time--; + uint8_t buf[2]; + + sys_put_le16((uint16_t)sample_time, buf); + + int err = reg_write(dev, reg_addr_lower, buf[0]); + + if (!err) { + err = reg_write(dev, reg_addr_upper, buf[1]); + } else { + LOG_ERR("Failed to change sample time"); + } + + return err; +} + +static int toggle_rest_modes(const struct device *dev, uint8_t reg_addr, + bool enable) +{ + uint8_t value; + int err = reg_read(dev, reg_addr, &value); + + if (err) { + LOG_ERR("Failed to read Config2 register"); + return err; + } + + WRITE_BIT(value, PMW3360_REST_EN_POS, enable); + + LOG_INF("%sable rest modes", (enable) ? ("En") : ("Dis")); + err = reg_write(dev, reg_addr, value); + + if (err) { + LOG_ERR("Failed to set rest mode"); + } + + return err; +} + +static int pmw3360_async_init_fw_load_start(const struct device *dev) +{ + int err = 0; + + /* Read from registers 0x02-0x06 regardless of the motion pin state. */ + for (uint8_t reg = 0x02; (reg <= 0x06) && !err; reg++) { + uint8_t buf[1]; + err = reg_read(dev, reg, buf); + } + + if (err) { + LOG_ERR("Cannot read from data registers"); + return err; + } + + /* Write 0 to Rest_En bit of Config2 register to disable Rest mode. */ + err = reg_write(dev, PMW3360_REG_CONFIG2, 0x00); + if (err) { + LOG_ERR("Cannot disable REST mode"); + return err; + } + + /* Write 0x1D in SROM_enable register to initialize the operation */ + err = reg_write(dev, PMW3360_REG_SROM_ENABLE, 0x1D); + if (err) { + LOG_ERR("Cannot initialize SROM"); + return err; + } + + return err; +} + +static int pmw3360_async_init_fw_load_continue(const struct device *dev) +{ + int err; + + LOG_INF("Uploading optical sensor firmware..."); + + /* Write 0x18 to SROM_enable to start SROM download */ + err = reg_write(dev, PMW3360_REG_SROM_ENABLE, 0x18); + if (err) { + LOG_ERR("Cannot start SROM download"); + return err; + } + + /* Write SROM file into SROM_Load_Burst register. + * Data must start with SROM_Load_Burst address. + */ + err = burst_write(dev, PMW3360_REG_SROM_LOAD_BURST, + pmw3360_firmware_data, pmw3360_firmware_length); + if (err) { + LOG_ERR("Cannot write firmware to sensor"); + } + + return err; +} + +static int pmw3360_async_init_fw_load_verify(const struct device *dev) +{ + int err; + + /* Read the SROM_ID register to verify the firmware ID before any + * other register reads or writes + */ + + uint8_t fw_id; + err = reg_read(dev, PMW3360_REG_SROM_ID, &fw_id); + if (err) { + LOG_ERR("Cannot obtain firmware id"); + return err; + } + + LOG_DBG("Optical chip firmware ID: 0x%x", fw_id); + if (fw_id != PMW3360_FIRMWARE_ID) { + LOG_ERR("Chip is not running from SROM!"); + return -EIO; + } + + uint8_t product_id; + err = reg_read(dev, PMW3360_REG_PRODUCT_ID, &product_id); + if (err) { + LOG_ERR("Cannot obtain product id"); + return err; + } + + if (product_id != PMW3360_PRODUCT_ID) { + LOG_ERR("Invalid product id!"); + return -EIO; + } + + /* Write 0x20 to Config2 register for wireless mouse design. + * This enables entering rest modes. + */ + err = reg_write(dev, PMW3360_REG_CONFIG2, 0x20); + if (err) { + LOG_ERR("Cannot enable REST modes"); + } + + return err; +} + +static void irq_handler(const struct device *gpiob, struct gpio_callback *cb, + uint32_t pins) +{ + int err; + struct pmw3360_data *data = CONTAINER_OF(cb, struct pmw3360_data, + irq_gpio_cb); + const struct device *dev = data->dev; + const struct pmw3360_config *config = dev->config; + + // disable the interrupt line first + err = gpio_pin_interrupt_configure_dt(&config->irq_gpio, + GPIO_INT_DISABLE); + if (unlikely(err)) { + LOG_ERR("Cannot disable IRQ"); + k_panic(); + } + + // submit the real handler work + k_work_submit(&data->trigger_handler_work); +} + +static void trigger_handler(struct k_work *work) +{ + sensor_trigger_handler_t handler; + int err = 0; + struct pmw3360_data *data = CONTAINER_OF(work, struct pmw3360_data, + trigger_handler_work); + const struct device *dev = data->dev; + const struct pmw3360_config *config = dev->config; + + // 1. the first lock period is used to procoss the trigger + // if data_ready_handler is non-NULL, otherwise do nothing + k_spinlock_key_t key = k_spin_lock(&data->lock); + + handler = data->data_ready_handler; + k_spin_unlock(&data->lock, key); + + if (!handler) { + return; + } + + struct sensor_trigger trig = { + .type = SENSOR_TRIG_DATA_READY, + .chan = SENSOR_CHAN_ALL, + }; + + handler(dev, &trig); + + // 2. the second lock period is used to resume the interrupt line + // if data_ready_handler is non-NULL, otherwise keep it inactive + key = k_spin_lock(&data->lock); + if (data->data_ready_handler) { + err = gpio_pin_interrupt_configure_dt(&config->irq_gpio, + GPIO_INT_LEVEL_ACTIVE); + } + k_spin_unlock(&data->lock, key); + + if (unlikely(err)) { + LOG_ERR("Cannot re-enable IRQ"); + k_panic(); + } +} + +static int pmw3360_async_init_power_up(const struct device *dev) +{ + /* Reset sensor */ + + return reg_write(dev, PMW3360_REG_POWER_UP_RESET, 0x5A); +} + +static int pmw3360_async_init_configure(const struct device *dev) +{ + int err; + + err = update_cpi(dev, CONFIG_PMW3360_CPI); + + if (!err) { + err = update_downshift_time(dev, + PMW3360_REG_RUN_DOWNSHIFT, + CONFIG_PMW3360_RUN_DOWNSHIFT_TIME_MS); + } + + if (!err) { + err = update_downshift_time(dev, + PMW3360_REG_REST1_DOWNSHIFT, + CONFIG_PMW3360_REST1_DOWNSHIFT_TIME_MS); + } + + if (!err) { + err = update_downshift_time(dev, + PMW3360_REG_REST2_DOWNSHIFT, + CONFIG_PMW3360_REST2_DOWNSHIFT_TIME_MS); + } + + return err; +} + +static void pmw3360_async_init(struct k_work *work) +{ + struct pmw3360_data *data = CONTAINER_OF(work, struct pmw3360_data, + init_work); + const struct device *dev = data->dev; + + LOG_DBG("PMW3360 async init step %d", data->async_init_step); + + data->err = async_init_fn[data->async_init_step](dev); + if (data->err) { + LOG_ERR("PMW3360 initialization failed"); + } else { + data->async_init_step++; + + if (data->async_init_step == ASYNC_INIT_STEP_COUNT) { + data->ready = true; // sensor is ready to work + LOG_INF("PMW3360 initialized"); + } else { + k_work_schedule(&data->init_work, + K_MSEC(async_init_delay[ + data->async_init_step])); + } + } +} + +static int pmw3360_init_irq(const struct device *dev) +{ + int err; + struct pmw3360_data *data = dev->data; + const struct pmw3360_config *config = dev->config; + + // check readiness of irq gpio pin + if (!device_is_ready(config->irq_gpio.port)) { + LOG_ERR("IRQ GPIO device not ready"); + return -ENODEV; + } + + // init the irq pin + err = gpio_pin_configure_dt(&config->irq_gpio, GPIO_INPUT); + if (err) { + LOG_ERR("Cannot configure IRQ GPIO"); + return err; + } + + // setup and add the irq callback associated + gpio_init_callback(&data->irq_gpio_cb, irq_handler, + BIT(config->irq_gpio.pin)); + + err = gpio_add_callback(config->irq_gpio.port, &data->irq_gpio_cb); + if (err) { + LOG_ERR("Cannot add IRQ GPIO callback"); + } + + return err; +} + +static int pmw3360_init(const struct device *dev) +{ + struct pmw3360_data *data = dev->data; + const struct pmw3360_config *config = dev->config; + int err; + + // init device pointer + data->dev = dev; + + // init trigger handler work + k_work_init(&data->trigger_handler_work, trigger_handler); + + // check readiness of spi bus + if (!spi_is_ready(&config->bus)) { + LOG_ERR("SPI device not ready"); + return -ENODEV; + } + + // check readiness of cs gpio pin and init it to inactive + if (!device_is_ready(config->cs_gpio.port)) { + LOG_ERR("SPI CS device not ready"); + return -ENODEV; + } + + err = gpio_pin_configure_dt(&config->cs_gpio, GPIO_OUTPUT_INACTIVE); + if (err) { + LOG_ERR("Cannot configure SPI CS GPIO"); + return err; + } + + // init irq routine + err = pmw3360_init_irq(dev); + if (err) { + return err; + } + + // Setup delayable and non-blocking init jobs, including following steps: + // 1. power reset + // 2. clear motion registers + // 3. srom firmware download and checking + // 4. eable rest mode + // 5. set cpi and downshift time (not sample rate) + // The sensor is ready to work (i.e., data->ready=true after the above steps are finished) + k_work_init_delayable(&data->init_work, pmw3360_async_init); + + k_work_schedule(&data->init_work, + K_MSEC(async_init_delay[data->async_init_step])); + + return err; +} + +static int pmw3360_sample_fetch(const struct device *dev, enum sensor_channel chan) +{ + struct pmw3360_data *data = dev->data; + uint8_t buf[PMW3360_BURST_SIZE]; + + if (unlikely(chan != SENSOR_CHAN_ALL)) { + return -ENOTSUP; + } + + if (unlikely(!data->ready)) { + LOG_DBG("Device is not initialized yet"); + return -EBUSY; + } + + int err = motion_burst_read(dev, buf, sizeof(buf)); + + if (!err) { + int16_t x = ((int16_t)sys_get_le16(&buf[PMW3360_DX_POS])) / CONFIG_PMW3360_CPI_DIVIDOR; + int16_t y = ((int16_t)sys_get_le16(&buf[PMW3360_DY_POS])) / CONFIG_PMW3360_CPI_DIVIDOR; + /* int16_t x = sys_get_le16(&buf[PMW3360_DX_POS]); */ + /* int16_t y = sys_get_le16(&buf[PMW3360_DY_POS]); */ + + if (IS_ENABLED(CONFIG_PMW3360_ORIENTATION_0)) { + data->x = x; + data->y = y; + } else if (IS_ENABLED(CONFIG_PMW3360_ORIENTATION_90)) { + data->x = y; + data->y = x; + } else if (IS_ENABLED(CONFIG_PMW3360_ORIENTATION_180)) { + data->x = x; + data->y = -y; + } else if (IS_ENABLED(CONFIG_PMW3360_ORIENTATION_270)) { + data->x = -y; + data->y = -x; + } + } + + return err; +} + +static int pmw3360_channel_get(const struct device *dev, enum sensor_channel chan, + struct sensor_value *val) +{ + struct pmw3360_data *data = dev->data; + + if (unlikely(!data->ready)) { + LOG_DBG("Device is not initialized yet"); + return -EBUSY; + } + + switch (chan) { + case SENSOR_CHAN_POS_DX: + val->val1 = data->x; + val->val2 = 0; + break; + + case SENSOR_CHAN_POS_DY: + val->val1 = data->y; + val->val2 = 0; + break; + + default: + return -ENOTSUP; + } + + return 0; +} + +/* Setup the callback for actual trigger handling */ +// handler could be NULL, in which case the effect is disabling the interrupt line +// Thus it has dual function: +// 1. set up a handler callback +// 2. set up a flag (i.e., data_ready_handler) to indicate resuming the interrput line or not +// This feature is useful to pass the resuming of the interrupt to application +static int pmw3360_trigger_set(const struct device *dev, + const struct sensor_trigger *trig, + sensor_trigger_handler_t handler) +{ + struct pmw3360_data *data = dev->data; + const struct pmw3360_config *config = dev->config; + int err; + + if (unlikely(trig->type != SENSOR_TRIG_DATA_READY)) { + return -ENOTSUP; + } + + if (unlikely(trig->chan != SENSOR_CHAN_ALL)) { + return -ENOTSUP; + } + + if (unlikely(!data->ready)) { + LOG_DBG("Device is not initialized yet"); + return -EBUSY; + } + + // spin lock is needed, so that the handler is not invoked before its pointer is assigned + // a valid value + k_spinlock_key_t key = k_spin_lock(&data->lock); + + // if non-NULL (a real handler defined), eanble the interrupt line + // otherwise, disable the interrupt line + if (handler) { + err = gpio_pin_interrupt_configure_dt(&config->irq_gpio, + GPIO_INT_LEVEL_ACTIVE); + } else { + err = gpio_pin_interrupt_configure_dt(&config->irq_gpio, + GPIO_INT_DISABLE); + } + + if (!err) { + data->data_ready_handler = handler; + } + + k_spin_unlock(&data->lock, key); + + return err; +} + +static int pmw3360_attr_set(const struct device *dev, enum sensor_channel chan, + enum sensor_attribute attr, + const struct sensor_value *val) +{ + struct pmw3360_data *data = dev->data; + int err; + + if (unlikely(chan != SENSOR_CHAN_ALL)) { + return -ENOTSUP; + } + + if (unlikely(!data->ready)) { + LOG_DBG("Device is not initialized yet"); + return -EBUSY; + } + + switch ((uint32_t)attr) { + case PMW3360_ATTR_CPI: + err = update_cpi(dev, PMW3360_SVALUE_TO_CPI(*val)); + break; + + case PMW3360_ATTR_REST_ENABLE: + err = toggle_rest_modes(dev, + PMW3360_REG_CONFIG2, + PMW3360_SVALUE_TO_BOOL(*val)); + break; + + case PMW3360_ATTR_RUN_DOWNSHIFT_TIME: + err = update_downshift_time(dev, + PMW3360_REG_RUN_DOWNSHIFT, + PMW3360_SVALUE_TO_TIME(*val)); + break; + + case PMW3360_ATTR_REST1_DOWNSHIFT_TIME: + err = update_downshift_time(dev, + PMW3360_REG_REST1_DOWNSHIFT, + PMW3360_SVALUE_TO_TIME(*val)); + break; + + case PMW3360_ATTR_REST2_DOWNSHIFT_TIME: + err = update_downshift_time(dev, + PMW3360_REG_REST2_DOWNSHIFT, + PMW3360_SVALUE_TO_TIME(*val)); + break; + + case PMW3360_ATTR_REST1_SAMPLE_TIME: + err = update_sample_time(dev, + PMW3360_REG_REST1_RATE_LOWER, + PMW3360_REG_REST1_RATE_UPPER, + PMW3360_SVALUE_TO_TIME(*val)); + break; + + case PMW3360_ATTR_REST2_SAMPLE_TIME: + err = update_sample_time(dev, + PMW3360_REG_REST2_RATE_LOWER, + PMW3360_REG_REST2_RATE_UPPER, + PMW3360_SVALUE_TO_TIME(*val)); + break; + + case PMW3360_ATTR_REST3_SAMPLE_TIME: + err = update_sample_time(dev, + PMW3360_REG_REST3_RATE_LOWER, + PMW3360_REG_REST3_RATE_UPPER, + PMW3360_SVALUE_TO_TIME(*val)); + break; + + default: + LOG_ERR("Unknown attribute"); + return -ENOTSUP; + } + + return err; +} + +static const struct sensor_driver_api pmw3360_driver_api = { + .sample_fetch = pmw3360_sample_fetch, + .channel_get = pmw3360_channel_get, + .trigger_set = pmw3360_trigger_set, + .attr_set = pmw3360_attr_set, +}; + +#define PMW3360_DEFINE(n) \ + static struct pmw3360_data data##n; \ + \ + static const struct pmw3360_config config##n = { \ + .irq_gpio = GPIO_DT_SPEC_INST_GET(n, irq_gpios), \ + .bus = { \ + .bus = DEVICE_DT_GET(DT_INST_BUS(n)), \ + .config = { \ + .frequency = DT_INST_PROP(n, \ + spi_max_frequency), \ + .operation = SPI_WORD_SET(8) | \ + SPI_TRANSFER_MSB | \ + SPI_MODE_CPOL | SPI_MODE_CPHA, \ + .slave = DT_INST_REG_ADDR(n), \ + }, \ + }, \ + .cs_gpio = SPI_CS_GPIOS_DT_SPEC_GET(DT_DRV_INST(n)), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(n, pmw3360_init, NULL, &data##n, &config##n, \ + POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \ + &pmw3360_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(PMW3360_DEFINE) diff --git a/app/drivers/sensor/pmw3360/pmw3360.h b/app/drivers/sensor/pmw3360/pmw3360.h new file mode 100644 index 00000000000..8d665c2b6bf --- /dev/null +++ b/app/drivers/sensor/pmw3360/pmw3360.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#ifndef ZEPHYR_INCLUDE_PMW3360_H_ +#define ZEPHYR_INCLUDE_PMW3360_H_ + +#include +#include +#include +#include + +/** + * @file pmw3360.h + * + * @brief Header file for the pmw3360 driver. + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum async_init_step { + ASYNC_INIT_STEP_POWER_UP, // power up reset + ASYNC_INIT_STEP_FW_LOAD_START, // clear motion registers, disable REST mode, enable SROM register + ASYNC_INIT_STEP_FW_LOAD_CONTINUE, // start SROM download + ASYNC_INIT_STEP_FW_LOAD_VERIFY, // verify SROM pid and fid, enable REST mode + ASYNC_INIT_STEP_CONFIGURE, // set cpi and donwshift time (run, rest1, rest2) + + ASYNC_INIT_STEP_COUNT // end flag +}; + +/* device data structure */ +struct pmw3360_data { + const struct device *dev; + struct gpio_callback irq_gpio_cb; + struct k_spinlock lock; + int16_t x; + int16_t y; + sensor_trigger_handler_t data_ready_handler; + struct k_work trigger_handler_work; + struct k_work_delayable init_work; + + enum async_init_step async_init_step; + int err; + bool ready; + bool last_read_burst; + + /* the design of the driver is based on interrupt purely, to add polling upon it + the following work and timer maybe used in application code */ + struct k_work poll_work; + struct k_timer poll_timer; +}; + +struct pmw3360_config { + struct gpio_dt_spec irq_gpio; + struct spi_dt_spec bus; + struct gpio_dt_spec cs_gpio; +}; + +/** + * @defgroup pmw3360 PMW3360 motion sensor driver + * @{ + * @brief PMW3360 motion sensor driver. + */ + +/** @brief Sensor specific attributes of PMW3360. */ +enum pmw3360_attribute { + /** Sensor CPI for both X and Y axes. */ + PMW3360_ATTR_CPI = SENSOR_ATTR_PRIV_START, + + /** Enable or disable sleep modes. */ + PMW3360_ATTR_REST_ENABLE, + + /** Entering time from Run mode to REST1 mode [ms]. */ + PMW3360_ATTR_RUN_DOWNSHIFT_TIME, + + /** Entering time from REST1 mode to REST2 mode [ms]. */ + PMW3360_ATTR_REST1_DOWNSHIFT_TIME, + + /** Entering time from REST2 mode to REST3 mode [ms]. */ + PMW3360_ATTR_REST2_DOWNSHIFT_TIME, + + /** Sampling frequency time during REST1 mode [ms]. */ + PMW3360_ATTR_REST1_SAMPLE_TIME, + + /** Sampling frequency time during REST2 mode [ms]. */ + PMW3360_ATTR_REST2_SAMPLE_TIME, + + /** Sampling frequency time during REST3 mode [ms]. */ + PMW3360_ATTR_REST3_SAMPLE_TIME, +}; + + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* ZEPHYR_INCLUDE_PMW3360_H_ */ diff --git a/app/drivers/sensor/pmw3360/pmw3360_priv.c b/app/drivers/sensor/pmw3360/pmw3360_priv.c new file mode 100644 index 00000000000..8ed123d2dc3 --- /dev/null +++ b/app/drivers/sensor/pmw3360/pmw3360_priv.c @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +/* +This SROM is provided "AS IS" by PixArt Imaging Inc., WITHOUT WARRANTY +OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL PIXART +IMAGING INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THIS +SROM OR THE USE OR OTHER DEALINGS IN THIS SROM. +*/ + +#include +#include + +const size_t pmw3360_firmware_length = 4094; + +/* Firmware "0x04" */ +const uint8_t pmw3360_firmware_data[] = { +0x01, 0x04, 0x8e, 0x96, 0x6e, 0x77, 0x3e, 0xfe, 0x7e, 0x5f, 0x1d, 0xb8, 0xf2, 0x66, 0x4e, +0xff, 0x5d, 0x19, 0xb0, 0xc2, 0x04, 0x69, 0x54, 0x2a, 0xd6, 0x2e, 0xbf, 0xdd, 0x19, 0xb0, +0xc3, 0xe5, 0x29, 0xb1, 0xe0, 0x23, 0xa5, 0xa9, 0xb1, 0xc1, 0x00, 0x82, 0x67, 0x4c, 0x1a, +0x97, 0x8d, 0x79, 0x51, 0x20, 0xc7, 0x06, 0x8e, 0x7c, 0x7c, 0x7a, 0x76, 0x4f, 0xfd, 0x59, +0x30, 0xe2, 0x46, 0x0e, 0x9e, 0xbe, 0xdf, 0x1d, 0x99, 0x91, 0xa0, 0xa5, 0xa1, 0xa9, 0xd0, +0x22, 0xc6, 0xef, 0x5c, 0x1b, 0x95, 0x89, 0x90, 0xa2, 0xa7, 0xcc, 0xfb, 0x55, 0x28, 0xb3, +0xe4, 0x4a, 0xf7, 0x6c, 0x3b, 0xf4, 0x6a, 0x56, 0x2e, 0xde, 0x1f, 0x9d, 0xb8, 0xd3, 0x05, +0x88, 0x92, 0xa6, 0xce, 0x1e, 0xbe, 0xdf, 0x1d, 0x99, 0xb0, 0xe2, 0x46, 0xef, 0x5c, 0x07, +0x11, 0x5d, 0x98, 0x0b, 0x9d, 0x94, 0x97, 0xee, 0x4e, 0x45, 0x33, 0x6b, 0x44, 0xc7, 0x29, +0x56, 0x27, 0x30, 0xc6, 0xa7, 0xd5, 0xf2, 0x56, 0xdf, 0xb4, 0x38, 0x62, 0xcb, 0xa0, 0xb6, +0xe3, 0x0f, 0x84, 0x06, 0x24, 0x05, 0x65, 0x6f, 0x76, 0x89, 0xb5, 0x77, 0x41, 0x27, 0x82, +0x66, 0x65, 0x82, 0xcc, 0xd5, 0xe6, 0x20, 0xd5, 0x27, 0x17, 0xc5, 0xf8, 0x03, 0x23, 0x7c, +0x5f, 0x64, 0xa5, 0x1d, 0xc1, 0xd6, 0x36, 0xcb, 0x4c, 0xd4, 0xdb, 0x66, 0xd7, 0x8b, 0xb1, +0x99, 0x7e, 0x6f, 0x4c, 0x36, 0x40, 0x06, 0xd6, 0xeb, 0xd7, 0xa2, 0xe4, 0xf4, 0x95, 0x51, +0x5a, 0x54, 0x96, 0xd5, 0x53, 0x44, 0xd7, 0x8c, 0xe0, 0xb9, 0x40, 0x68, 0xd2, 0x18, 0xe9, +0xdd, 0x9a, 0x23, 0x92, 0x48, 0xee, 0x7f, 0x43, 0xaf, 0xea, 0x77, 0x38, 0x84, 0x8c, 0x0a, +0x72, 0xaf, 0x69, 0xf8, 0xdd, 0xf1, 0x24, 0x83, 0xa3, 0xf8, 0x4a, 0xbf, 0xf5, 0x94, 0x13, +0xdb, 0xbb, 0xd8, 0xb4, 0xb3, 0xa0, 0xfb, 0x45, 0x50, 0x60, 0x30, 0x59, 0x12, 0x31, 0x71, +0xa2, 0xd3, 0x13, 0xe7, 0xfa, 0xe7, 0xce, 0x0f, 0x63, 0x15, 0x0b, 0x6b, 0x94, 0xbb, 0x37, +0x83, 0x26, 0x05, 0x9d, 0xfb, 0x46, 0x92, 0xfc, 0x0a, 0x15, 0xd1, 0x0d, 0x73, 0x92, 0xd6, +0x8c, 0x1b, 0x8c, 0xb8, 0x55, 0x8a, 0xce, 0xbd, 0xfe, 0x8e, 0xfc, 0xed, 0x09, 0x12, 0x83, +0x91, 0x82, 0x51, 0x31, 0x23, 0xfb, 0xb4, 0x0c, 0x76, 0xad, 0x7c, 0xd9, 0xb4, 0x4b, 0xb2, +0x67, 0x14, 0x09, 0x9c, 0x7f, 0x0c, 0x18, 0xba, 0x3b, 0xd6, 0x8e, 0x14, 0x2a, 0xe4, 0x1b, +0x52, 0x9f, 0x2b, 0x7d, 0xe1, 0xfb, 0x6a, 0x33, 0x02, 0xfa, 0xac, 0x5a, 0xf2, 0x3e, 0x88, +0x7e, 0xae, 0xd1, 0xf3, 0x78, 0xe8, 0x05, 0xd1, 0xe3, 0xdc, 0x21, 0xf6, 0xe1, 0x9a, 0xbd, +0x17, 0x0e, 0xd9, 0x46, 0x9b, 0x88, 0x03, 0xea, 0xf6, 0x66, 0xbe, 0x0e, 0x1b, 0x50, 0x49, +0x96, 0x40, 0x97, 0xf1, 0xf1, 0xe4, 0x80, 0xa6, 0x6e, 0xe8, 0x77, 0x34, 0xbf, 0x29, 0x40, +0x44, 0xc2, 0xff, 0x4e, 0x98, 0xd3, 0x9c, 0xa3, 0x32, 0x2b, 0x76, 0x51, 0x04, 0x09, 0xe7, +0xa9, 0xd1, 0xa6, 0x32, 0xb1, 0x23, 0x53, 0xe2, 0x47, 0xab, 0xd6, 0xf5, 0x69, 0x5c, 0x3e, +0x5f, 0xfa, 0xae, 0x45, 0x20, 0xe5, 0xd2, 0x44, 0xff, 0x39, 0x32, 0x6d, 0xfd, 0x27, 0x57, +0x5c, 0xfd, 0xf0, 0xde, 0xc1, 0xb5, 0x99, 0xe5, 0xf5, 0x1c, 0x77, 0x01, 0x75, 0xc5, 0x6d, +0x58, 0x92, 0xf2, 0xb2, 0x47, 0x00, 0x01, 0x26, 0x96, 0x7a, 0x30, 0xff, 0xb7, 0xf0, 0xef, +0x77, 0xc1, 0x8a, 0x5d, 0xdc, 0xc0, 0xd1, 0x29, 0x30, 0x1e, 0x77, 0x38, 0x7a, 0x94, 0xf1, +0xb8, 0x7a, 0x7e, 0xef, 0xa4, 0xd1, 0xac, 0x31, 0x4a, 0xf2, 0x5d, 0x64, 0x3d, 0xb2, 0xe2, +0xf0, 0x08, 0x99, 0xfc, 0x70, 0xee, 0x24, 0xa7, 0x7e, 0xee, 0x1e, 0x20, 0x69, 0x7d, 0x44, +0xbf, 0x87, 0x42, 0xdf, 0x88, 0x3b, 0x0c, 0xda, 0x42, 0xc9, 0x04, 0xf9, 0x45, 0x50, 0xfc, +0x83, 0x8f, 0x11, 0x6a, 0x72, 0xbc, 0x99, 0x95, 0xf0, 0xac, 0x3d, 0xa7, 0x3b, 0xcd, 0x1c, +0xe2, 0x88, 0x79, 0x37, 0x11, 0x5f, 0x39, 0x89, 0x95, 0x0a, 0x16, 0x84, 0x7a, 0xf6, 0x8a, +0xa4, 0x28, 0xe4, 0xed, 0x83, 0x80, 0x3b, 0xb1, 0x23, 0xa5, 0x03, 0x10, 0xf4, 0x66, 0xea, +0xbb, 0x0c, 0x0f, 0xc5, 0xec, 0x6c, 0x69, 0xc5, 0xd3, 0x24, 0xab, 0xd4, 0x2a, 0xb7, 0x99, +0x88, 0x76, 0x08, 0xa0, 0xa8, 0x95, 0x7c, 0xd8, 0x38, 0x6d, 0xcd, 0x59, 0x02, 0x51, 0x4b, +0xf1, 0xb5, 0x2b, 0x50, 0xe3, 0xb6, 0xbd, 0xd0, 0x72, 0xcf, 0x9e, 0xfd, 0x6e, 0xbb, 0x44, +0xc8, 0x24, 0x8a, 0x77, 0x18, 0x8a, 0x13, 0x06, 0xef, 0x97, 0x7d, 0xfa, 0x81, 0xf0, 0x31, +0xe6, 0xfa, 0x77, 0xed, 0x31, 0x06, 0x31, 0x5b, 0x54, 0x8a, 0x9f, 0x30, 0x68, 0xdb, 0xe2, +0x40, 0xf8, 0x4e, 0x73, 0xfa, 0xab, 0x74, 0x8b, 0x10, 0x58, 0x13, 0xdc, 0xd2, 0xe6, 0x78, +0xd1, 0x32, 0x2e, 0x8a, 0x9f, 0x2c, 0x58, 0x06, 0x48, 0x27, 0xc5, 0xa9, 0x5e, 0x81, 0x47, +0x89, 0x46, 0x21, 0x91, 0x03, 0x70, 0xa4, 0x3e, 0x88, 0x9c, 0xda, 0x33, 0x0a, 0xce, 0xbc, +0x8b, 0x8e, 0xcf, 0x9f, 0xd3, 0x71, 0x80, 0x43, 0xcf, 0x6b, 0xa9, 0x51, 0x83, 0x76, 0x30, +0x82, 0xc5, 0x6a, 0x85, 0x39, 0x11, 0x50, 0x1a, 0x82, 0xdc, 0x1e, 0x1c, 0xd5, 0x7d, 0xa9, +0x71, 0x99, 0x33, 0x47, 0x19, 0x97, 0xb3, 0x5a, 0xb1, 0xdf, 0xed, 0xa4, 0xf2, 0xe6, 0x26, +0x84, 0xa2, 0x28, 0x9a, 0x9e, 0xdf, 0xa6, 0x6a, 0xf4, 0xd6, 0xfc, 0x2e, 0x5b, 0x9d, 0x1a, +0x2a, 0x27, 0x68, 0xfb, 0xc1, 0x83, 0x21, 0x4b, 0x90, 0xe0, 0x36, 0xdd, 0x5b, 0x31, 0x42, +0x55, 0xa0, 0x13, 0xf7, 0xd0, 0x89, 0x53, 0x71, 0x99, 0x57, 0x09, 0x29, 0xc5, 0xf3, 0x21, +0xf8, 0x37, 0x2f, 0x40, 0xf3, 0xd4, 0xaf, 0x16, 0x08, 0x36, 0x02, 0xfc, 0x77, 0xc5, 0x8b, +0x04, 0x90, 0x56, 0xb9, 0xc9, 0x67, 0x9a, 0x99, 0xe8, 0x00, 0xd3, 0x86, 0xff, 0x97, 0x2d, +0x08, 0xe9, 0xb7, 0xb3, 0x91, 0xbc, 0xdf, 0x45, 0xc6, 0xed, 0x0f, 0x8c, 0x4c, 0x1e, 0xe6, +0x5b, 0x6e, 0x38, 0x30, 0xe4, 0xaa, 0xe3, 0x95, 0xde, 0xb9, 0xe4, 0x9a, 0xf5, 0xb2, 0x55, +0x9a, 0x87, 0x9b, 0xf6, 0x6a, 0xb2, 0xf2, 0x77, 0x9a, 0x31, 0xf4, 0x7a, 0x31, 0xd1, 0x1d, +0x04, 0xc0, 0x7c, 0x32, 0xa2, 0x9e, 0x9a, 0xf5, 0x62, 0xf8, 0x27, 0x8d, 0xbf, 0x51, 0xff, +0xd3, 0xdf, 0x64, 0x37, 0x3f, 0x2a, 0x6f, 0x76, 0x3a, 0x7d, 0x77, 0x06, 0x9e, 0x77, 0x7f, +0x5e, 0xeb, 0x32, 0x51, 0xf9, 0x16, 0x66, 0x9a, 0x09, 0xf3, 0xb0, 0x08, 0xa4, 0x70, 0x96, +0x46, 0x30, 0xff, 0xda, 0x4f, 0xe9, 0x1b, 0xed, 0x8d, 0xf8, 0x74, 0x1f, 0x31, 0x92, 0xb3, +0x73, 0x17, 0x36, 0xdb, 0x91, 0x30, 0xd6, 0x88, 0x55, 0x6b, 0x34, 0x77, 0x87, 0x7a, 0xe7, +0xee, 0x06, 0xc6, 0x1c, 0x8c, 0x19, 0x0c, 0x48, 0x46, 0x23, 0x5e, 0x9c, 0x07, 0x5c, 0xbf, +0xb4, 0x7e, 0xd6, 0x4f, 0x74, 0x9c, 0xe2, 0xc5, 0x50, 0x8b, 0xc5, 0x8b, 0x15, 0x90, 0x60, +0x62, 0x57, 0x29, 0xd0, 0x13, 0x43, 0xa1, 0x80, 0x88, 0x91, 0x00, 0x44, 0xc7, 0x4d, 0x19, +0x86, 0xcc, 0x2f, 0x2a, 0x75, 0x5a, 0xfc, 0xeb, 0x97, 0x2a, 0x70, 0xe3, 0x78, 0xd8, 0x91, +0xb0, 0x4f, 0x99, 0x07, 0xa3, 0x95, 0xea, 0x24, 0x21, 0xd5, 0xde, 0x51, 0x20, 0x93, 0x27, +0x0a, 0x30, 0x73, 0xa8, 0xff, 0x8a, 0x97, 0xe9, 0xa7, 0x6a, 0x8e, 0x0d, 0xe8, 0xf0, 0xdf, +0xec, 0xea, 0xb4, 0x6c, 0x1d, 0x39, 0x2a, 0x62, 0x2d, 0x3d, 0x5a, 0x8b, 0x65, 0xf8, 0x90, +0x05, 0x2e, 0x7e, 0x91, 0x2c, 0x78, 0xef, 0x8e, 0x7a, 0xc1, 0x2f, 0xac, 0x78, 0xee, 0xaf, +0x28, 0x45, 0x06, 0x4c, 0x26, 0xaf, 0x3b, 0xa2, 0xdb, 0xa3, 0x93, 0x06, 0xb5, 0x3c, 0xa5, +0xd8, 0xee, 0x8f, 0xaf, 0x25, 0xcc, 0x3f, 0x85, 0x68, 0x48, 0xa9, 0x62, 0xcc, 0x97, 0x8f, +0x7f, 0x2a, 0xea, 0xe0, 0x15, 0x0a, 0xad, 0x62, 0x07, 0xbd, 0x45, 0xf8, 0x41, 0xd8, 0x36, +0xcb, 0x4c, 0xdb, 0x6e, 0xe6, 0x3a, 0xe7, 0xda, 0x15, 0xe9, 0x29, 0x1e, 0x12, 0x10, 0xa0, +0x14, 0x2c, 0x0e, 0x3d, 0xf4, 0xbf, 0x39, 0x41, 0x92, 0x75, 0x0b, 0x25, 0x7b, 0xa3, 0xce, +0x39, 0x9c, 0x15, 0x64, 0xc8, 0xfa, 0x3d, 0xef, 0x73, 0x27, 0xfe, 0x26, 0x2e, 0xce, 0xda, +0x6e, 0xfd, 0x71, 0x8e, 0xdd, 0xfe, 0x76, 0xee, 0xdc, 0x12, 0x5c, 0x02, 0xc5, 0x3a, 0x4e, +0x4e, 0x4f, 0xbf, 0xca, 0x40, 0x15, 0xc7, 0x6e, 0x8d, 0x41, 0xf1, 0x10, 0xe0, 0x4f, 0x7e, +0x97, 0x7f, 0x1c, 0xae, 0x47, 0x8e, 0x6b, 0xb1, 0x25, 0x31, 0xb0, 0x73, 0xc7, 0x1b, 0x97, +0x79, 0xf9, 0x80, 0xd3, 0x66, 0x22, 0x30, 0x07, 0x74, 0x1e, 0xe4, 0xd0, 0x80, 0x21, 0xd6, +0xee, 0x6b, 0x6c, 0x4f, 0xbf, 0xf5, 0xb7, 0xd9, 0x09, 0x87, 0x2f, 0xa9, 0x14, 0xbe, 0x27, +0xd9, 0x72, 0x50, 0x01, 0xd4, 0x13, 0x73, 0xa6, 0xa7, 0x51, 0x02, 0x75, 0x25, 0xe1, 0xb3, +0x45, 0x34, 0x7d, 0xa8, 0x8e, 0xeb, 0xf3, 0x16, 0x49, 0xcb, 0x4f, 0x8c, 0xa1, 0xb9, 0x36, +0x85, 0x39, 0x75, 0x5d, 0x08, 0x00, 0xae, 0xeb, 0xf6, 0xea, 0xd7, 0x13, 0x3a, 0x21, 0x5a, +0x5f, 0x30, 0x84, 0x52, 0x26, 0x95, 0xc9, 0x14, 0xf2, 0x57, 0x55, 0x6b, 0xb1, 0x10, 0xc2, +0xe1, 0xbd, 0x3b, 0x51, 0xc0, 0xb7, 0x55, 0x4c, 0x71, 0x12, 0x26, 0xc7, 0x0d, 0xf9, 0x51, +0xa4, 0x38, 0x02, 0x05, 0x7f, 0xb8, 0xf1, 0x72, 0x4b, 0xbf, 0x71, 0x89, 0x14, 0xf3, 0x77, +0x38, 0xd9, 0x71, 0x24, 0xf3, 0x00, 0x11, 0xa1, 0xd8, 0xd4, 0x69, 0x27, 0x08, 0x37, 0x35, +0xc9, 0x11, 0x9d, 0x90, 0x1c, 0x0e, 0xe7, 0x1c, 0xff, 0x2d, 0x1e, 0xe8, 0x92, 0xe1, 0x18, +0x10, 0x95, 0x7c, 0xe0, 0x80, 0xf4, 0x96, 0x43, 0x21, 0xf9, 0x75, 0x21, 0x64, 0x38, 0xdd, +0x9f, 0x1e, 0x95, 0x16, 0xda, 0x56, 0x1d, 0x4f, 0x9a, 0x53, 0xb2, 0xe2, 0xe4, 0x18, 0xcb, +0x6b, 0x1a, 0x65, 0xeb, 0x56, 0xc6, 0x3b, 0xe5, 0xfe, 0xd8, 0x26, 0x3f, 0x3a, 0x84, 0x59, +0x72, 0x66, 0xa2, 0xf3, 0x75, 0xff, 0xfb, 0x60, 0xb3, 0x22, 0xad, 0x3f, 0x2d, 0x6b, 0xf9, +0xeb, 0xea, 0x05, 0x7c, 0xd8, 0x8f, 0x6d, 0x2c, 0x98, 0x9e, 0x2b, 0x93, 0xf1, 0x5e, 0x46, +0xf0, 0x87, 0x49, 0x29, 0x73, 0x68, 0xd7, 0x7f, 0xf9, 0xf0, 0xe5, 0x7d, 0xdb, 0x1d, 0x75, +0x19, 0xf3, 0xc4, 0x58, 0x9b, 0x17, 0x88, 0xa8, 0x92, 0xe0, 0xbe, 0xbd, 0x8b, 0x1d, 0x8d, +0x9f, 0x56, 0x76, 0xad, 0xaf, 0x29, 0xe2, 0xd9, 0xd5, 0x52, 0xf6, 0xb5, 0x56, 0x35, 0x57, +0x3a, 0xc8, 0xe1, 0x56, 0x43, 0x19, 0x94, 0xd3, 0x04, 0x9b, 0x6d, 0x35, 0xd8, 0x0b, 0x5f, +0x4d, 0x19, 0x8e, 0xec, 0xfa, 0x64, 0x91, 0x0a, 0x72, 0x20, 0x2b, 0xbc, 0x1a, 0x4a, 0xfe, +0x8b, 0xfd, 0xbb, 0xed, 0x1b, 0x23, 0xea, 0xad, 0x72, 0x82, 0xa1, 0x29, 0x99, 0x71, 0xbd, +0xf0, 0x95, 0xc1, 0x03, 0xdd, 0x7b, 0xc2, 0xb2, 0x3c, 0x28, 0x54, 0xd3, 0x68, 0xa4, 0x72, +0xc8, 0x66, 0x96, 0xe0, 0xd1, 0xd8, 0x7f, 0xf8, 0xd1, 0x26, 0x2b, 0xf7, 0xad, 0xba, 0x55, +0xca, 0x15, 0xb9, 0x32, 0xc3, 0xe5, 0x88, 0x97, 0x8e, 0x5c, 0xfb, 0x92, 0x25, 0x8b, 0xbf, +0xa2, 0x45, 0x55, 0x7a, 0xa7, 0x6f, 0x8b, 0x57, 0x5b, 0xcf, 0x0e, 0xcb, 0x1d, 0xfb, 0x20, +0x82, 0x77, 0xa8, 0x8c, 0xcc, 0x16, 0xce, 0x1d, 0xfa, 0xde, 0xcc, 0x0b, 0x62, 0xfe, 0xcc, +0xe1, 0xb7, 0xf0, 0xc3, 0x81, 0x64, 0x73, 0x40, 0xa0, 0xc2, 0x4d, 0x89, 0x11, 0x75, 0x33, +0x55, 0x33, 0x8d, 0xe8, 0x4a, 0xfd, 0xea, 0x6e, 0x30, 0x0b, 0xd7, 0x31, 0x2c, 0xde, 0x47, +0xe3, 0xbf, 0xf8, 0x55, 0x42, 0xe2, 0x7f, 0x59, 0xe5, 0x17, 0xef, 0x99, 0x34, 0x69, 0x91, +0xb1, 0x23, 0x8e, 0x20, 0x87, 0x2d, 0xa8, 0xfe, 0xd5, 0x8a, 0xf3, 0x84, 0x3a, 0xf0, 0x37, +0xe4, 0x09, 0x00, 0x54, 0xee, 0x67, 0x49, 0x93, 0xe4, 0x81, 0x70, 0xe3, 0x90, 0x4d, 0xef, +0xfe, 0x41, 0xb7, 0x99, 0x7b, 0xc1, 0x83, 0xba, 0x62, 0x12, 0x6f, 0x7d, 0xde, 0x6b, 0xaf, +0xda, 0x16, 0xf9, 0x55, 0x51, 0xee, 0xa6, 0x0c, 0x2b, 0x02, 0xa3, 0xfd, 0x8d, 0xfb, 0x30, +0x17, 0xe4, 0x6f, 0xdf, 0x36, 0x71, 0xc4, 0xca, 0x87, 0x25, 0x48, 0xb0, 0x47, 0xec, 0xea, +0xb4, 0xbf, 0xa5, 0x4d, 0x9b, 0x9f, 0x02, 0x93, 0xc4, 0xe3, 0xe4, 0xe8, 0x42, 0x2d, 0x68, +0x81, 0x15, 0x0a, 0xeb, 0x84, 0x5b, 0xd6, 0xa8, 0x74, 0xfb, 0x7d, 0x1d, 0xcb, 0x2c, 0xda, +0x46, 0x2a, 0x76, 0x62, 0xce, 0xbc, 0x5c, 0x9e, 0x8b, 0xe7, 0xcf, 0xbe, 0x78, 0xf5, 0x7c, +0xeb, 0xb3, 0x3a, 0x9c, 0xaa, 0x6f, 0xcc, 0x72, 0xd1, 0x59, 0xf2, 0x11, 0x23, 0xd6, 0x3f, +0x48, 0xd1, 0xb7, 0xce, 0xb0, 0xbf, 0xcb, 0xea, 0x80, 0xde, 0x57, 0xd4, 0x5e, 0x97, 0x2f, +0x75, 0xd1, 0x50, 0x8e, 0x80, 0x2c, 0x66, 0x79, 0xbf, 0x72, 0x4b, 0xbd, 0x8a, 0x81, 0x6c, +0xd3, 0xe1, 0x01, 0xdc, 0xd2, 0x15, 0x26, 0xc5, 0x36, 0xda, 0x2c, 0x1a, 0xc0, 0x27, 0x94, +0xed, 0xb7, 0x9b, 0x85, 0x0b, 0x5e, 0x80, 0x97, 0xc5, 0xec, 0x4f, 0xec, 0x88, 0x5d, 0x50, +0x07, 0x35, 0x47, 0xdc, 0x0b, 0x3b, 0x3d, 0xdd, 0x60, 0xaf, 0xa8, 0x5d, 0x81, 0x38, 0x24, +0x25, 0x5d, 0x5c, 0x15, 0xd1, 0xde, 0xb3, 0xab, 0xec, 0x05, 0x69, 0xef, 0x83, 0xed, 0x57, +0x54, 0xb8, 0x64, 0x64, 0x11, 0x16, 0x32, 0x69, 0xda, 0x9f, 0x2d, 0x7f, 0x36, 0xbb, 0x44, +0x5a, 0x34, 0xe8, 0x7f, 0xbf, 0x03, 0xeb, 0x00, 0x7f, 0x59, 0x68, 0x22, 0x79, 0xcf, 0x73, +0x6c, 0x2c, 0x29, 0xa7, 0xa1, 0x5f, 0x38, 0xa1, 0x1d, 0xf0, 0x20, 0x53, 0xe0, 0x1a, 0x63, +0x14, 0x58, 0x71, 0x10, 0xaa, 0x08, 0x0c, 0x3e, 0x16, 0x1a, 0x60, 0x22, 0x82, 0x7f, 0xba, +0xa4, 0x43, 0xa0, 0xd0, 0xac, 0x1b, 0xd5, 0x6b, 0x64, 0xb5, 0x14, 0x93, 0x31, 0x9e, 0x53, +0x50, 0xd0, 0x57, 0x66, 0xee, 0x5a, 0x4f, 0xfb, 0x03, 0x2a, 0x69, 0x58, 0x76, 0xf1, 0x83, +0xf7, 0x4e, 0xba, 0x8c, 0x42, 0x06, 0x60, 0x5d, 0x6d, 0xce, 0x60, 0x88, 0xae, 0xa4, 0xc3, +0xf1, 0x03, 0xa5, 0x4b, 0x98, 0xa1, 0xff, 0x67, 0xe1, 0xac, 0xa2, 0xb8, 0x62, 0xd7, 0x6f, +0xa0, 0x31, 0xb4, 0xd2, 0x77, 0xaf, 0x21, 0x10, 0x06, 0xc6, 0x9a, 0xff, 0x1d, 0x09, 0x17, +0x0e, 0x5f, 0xf1, 0xaa, 0x54, 0x34, 0x4b, 0x45, 0x8a, 0x87, 0x63, 0xa6, 0xdc, 0xf9, 0x24, +0x30, 0x67, 0xc6, 0xb2, 0xd6, 0x61, 0x33, 0x69, 0xee, 0x50, 0x61, 0x57, 0x28, 0xe7, 0x7e, +0xee, 0xec, 0x3a, 0x5a, 0x73, 0x4e, 0xa8, 0x8d, 0xe4, 0x18, 0xea, 0xec, 0x41, 0x64, 0xc8, +0xe2, 0xe8, 0x66, 0xb6, 0x2d, 0xb6, 0xfb, 0x6a, 0x6c, 0x16, 0xb3, 0xdd, 0x46, 0x43, 0xb9, +0x73, 0x00, 0x6a, 0x71, 0xed, 0x4e, 0x9d, 0x25, 0x1a, 0xc3, 0x3c, 0x4a, 0x95, 0x15, 0x99, +0x35, 0x81, 0x14, 0x02, 0xd6, 0x98, 0x9b, 0xec, 0xd8, 0x23, 0x3b, 0x84, 0x29, 0xaf, 0x0c, +0x99, 0x83, 0xa6, 0x9a, 0x34, 0x4f, 0xfa, 0xe8, 0xd0, 0x3c, 0x4b, 0xd0, 0xfb, 0xb6, 0x68, +0xb8, 0x9e, 0x8f, 0xcd, 0xf7, 0x60, 0x2d, 0x7a, 0x22, 0xe5, 0x7d, 0xab, 0x65, 0x1b, 0x95, +0xa7, 0xa8, 0x7f, 0xb6, 0x77, 0x47, 0x7b, 0x5f, 0x8b, 0x12, 0x72, 0xd0, 0xd4, 0x91, 0xef, +0xde, 0x19, 0x50, 0x3c, 0xa7, 0x8b, 0xc4, 0xa9, 0xb3, 0x23, 0xcb, 0x76, 0xe6, 0x81, 0xf0, +0xc1, 0x04, 0x8f, 0xa3, 0xb8, 0x54, 0x5b, 0x97, 0xac, 0x19, 0xff, 0x3f, 0x55, 0x27, 0x2f, +0xe0, 0x1d, 0x42, 0x9b, 0x57, 0xfc, 0x4b, 0x4e, 0x0f, 0xce, 0x98, 0xa9, 0x43, 0x57, 0x03, +0xbd, 0xe7, 0xc8, 0x94, 0xdf, 0x6e, 0x36, 0x73, 0x32, 0xb4, 0xef, 0x2e, 0x85, 0x7a, 0x6e, +0xfc, 0x6c, 0x18, 0x82, 0x75, 0x35, 0x90, 0x07, 0xf3, 0xe4, 0x9f, 0x3e, 0xdc, 0x68, 0xf3, +0xb5, 0xf3, 0x19, 0x80, 0x92, 0x06, 0x99, 0xa2, 0xe8, 0x6f, 0xff, 0x2e, 0x7f, 0xae, 0x42, +0xa4, 0x5f, 0xfb, 0xd4, 0x0e, 0x81, 0x2b, 0xc3, 0x04, 0xff, 0x2b, 0xb3, 0x74, 0x4e, 0x36, +0x5b, 0x9c, 0x15, 0x00, 0xc6, 0x47, 0x2b, 0xe8, 0x8b, 0x3d, 0xf1, 0x9c, 0x03, 0x9a, 0x58, +0x7f, 0x9b, 0x9c, 0xbf, 0x85, 0x49, 0x79, 0x35, 0x2e, 0x56, 0x7b, 0x41, 0x14, 0x39, 0x47, +0x83, 0x26, 0xaa, 0x07, 0x89, 0x98, 0x11, 0x1b, 0x86, 0xe7, 0x73, 0x7a, 0xd8, 0x7d, 0x78, +0x61, 0x53, 0xe9, 0x79, 0xf5, 0x36, 0x8d, 0x44, 0x92, 0x84, 0xf9, 0x13, 0x50, 0x58, 0x3b, +0xa4, 0x6a, 0x36, 0x65, 0x49, 0x8e, 0x3c, 0x0e, 0xf1, 0x6f, 0xd2, 0x84, 0xc4, 0x7e, 0x8e, +0x3f, 0x39, 0xae, 0x7c, 0x84, 0xf1, 0x63, 0x37, 0x8e, 0x3c, 0xcc, 0x3e, 0x44, 0x81, 0x45, +0xf1, 0x4b, 0xb9, 0xed, 0x6b, 0x36, 0x5d, 0xbb, 0x20, 0x60, 0x1a, 0x0f, 0xa3, 0xaa, 0x55, +0x77, 0x3a, 0xa9, 0xae, 0x37, 0x4d, 0xba, 0xb8, 0x86, 0x6b, 0xbc, 0x08, 0x50, 0xf6, 0xcc, +0xa4, 0xbd, 0x1d, 0x40, 0x72, 0xa5, 0x86, 0xfa, 0xe2, 0x10, 0xae, 0x3d, 0x58, 0x4b, 0x97, +0xf3, 0x43, 0x74, 0xa9, 0x9e, 0xeb, 0x21, 0xb7, 0x01, 0xa4, 0x86, 0x93, 0x97, 0xee, 0x2f, +0x4f, 0x3b, 0x86, 0xa1, 0x41, 0x6f, 0x41, 0x26, 0x90, 0x78, 0x5c, 0x7f, 0x30, 0x38, 0x4b, +0x3f, 0xaa, 0xec, 0xed, 0x5c, 0x6f, 0x0e, 0xad, 0x43, 0x87, 0xfd, 0x93, 0x35, 0xe6, 0x01, +0xef, 0x41, 0x26, 0x90, 0x99, 0x9e, 0xfb, 0x19, 0x5b, 0xad, 0xd2, 0x91, 0x8a, 0xe0, 0x46, +0xaf, 0x65, 0xfa, 0x4f, 0x84, 0xc1, 0xa1, 0x2d, 0xcf, 0x45, 0x8b, 0xd3, 0x85, 0x50, 0x55, +0x7c, 0xf9, 0x67, 0x88, 0xd4, 0x4e, 0xe9, 0xd7, 0x6b, 0x61, 0x54, 0xa1, 0xa4, 0xa6, 0xa2, +0xc2, 0xbf, 0x30, 0x9c, 0x40, 0x9f, 0x5f, 0xd7, 0x69, 0x2b, 0x24, 0x82, 0x5e, 0xd9, 0xd6, +0xa7, 0x12, 0x54, 0x1a, 0xf7, 0x55, 0x9f, 0x76, 0x50, 0xa9, 0x95, 0x84, 0xe6, 0x6b, 0x6d, +0xb5, 0x96, 0x54, 0xd6, 0xcd, 0xb3, 0xa1, 0x9b, 0x46, 0xa7, 0x94, 0x4d, 0xc4, 0x94, 0xb4, +0x98, 0xe3, 0xe1, 0xe2, 0x34, 0xd5, 0x33, 0x16, 0x07, 0x54, 0xcd, 0xb7, 0x77, 0x53, 0xdb, +0x4f, 0x4d, 0x46, 0x9d, 0xe9, 0xd4, 0x9c, 0x8a, 0x36, 0xb6, 0xb8, 0x38, 0x26, 0x6c, 0x0e, +0xff, 0x9c, 0x1b, 0x43, 0x8b, 0x80, 0xcc, 0xb9, 0x3d, 0xda, 0xc7, 0xf1, 0x8a, 0xf2, 0x6d, +0xb8, 0xd7, 0x74, 0x2f, 0x7e, 0x1e, 0xb7, 0xd3, 0x4a, 0xb4, 0xac, 0xfc, 0x79, 0x48, 0x6c, +0xbc, 0x96, 0xb6, 0x94, 0x46, 0x57, 0x2d, 0xb0, 0xa3, 0xfc, 0x1e, 0xb9, 0x52, 0x60, 0x85, +0x2d, 0x41, 0xd0, 0x43, 0x01, 0x1e, 0x1c, 0xd5, 0x7d, 0xfc, 0xf3, 0x96, 0x0d, 0xc7, 0xcb, +0x2a, 0x29, 0x9a, 0x93, 0xdd, 0x88, 0x2d, 0x37, 0x5d, 0xaa, 0xfb, 0x49, 0x68, 0xa0, 0x9c, +0x50, 0x86, 0x7f, 0x68, 0x56, 0x57, 0xf9, 0x79, 0x18, 0x39, 0xd4, 0xe0, 0x01, 0x84, 0x33, +0x61, 0xca, 0xa5, 0xd2, 0xd6, 0xe4, 0xc9, 0x8a, 0x4a, 0x23, 0x44, 0x4e, 0xbc, 0xf0, 0xdc, +0x24, 0xa1, 0xa0, 0xc4, 0xe2, 0x07, 0x3c, 0x10, 0xc4, 0xb5, 0x25, 0x4b, 0x65, 0x63, 0xf4, +0x80, 0xe7, 0xcf, 0x61, 0xb1, 0x71, 0x82, 0x21, 0x87, 0x2c, 0xf5, 0x91, 0x00, 0x32, 0x0c, +0xec, 0xa9, 0xb5, 0x9a, 0x74, 0x85, 0xe3, 0x36, 0x8f, 0x76, 0x4f, 0x9c, 0x6d, 0xce, 0xbc, +0xad, 0x0a, 0x4b, 0xed, 0x76, 0x04, 0xcb, 0xc3, 0xb9, 0x33, 0x9e, 0x01, 0x93, 0x96, 0x69, +0x7d, 0xc5, 0xa2, 0x45, 0x79, 0x9b, 0x04, 0x5c, 0x84, 0x09, 0xed, 0x88, 0x43, 0xc7, 0xab, +0x93, 0x14, 0x26, 0xa1, 0x40, 0xb5, 0xce, 0x4e, 0xbf, 0x2a, 0x42, 0x85, 0x3e, 0x2c, 0x3b, +0x54, 0xe8, 0x12, 0x1f, 0x0e, 0x97, 0x59, 0xb2, 0x27, 0x89, 0xfa, 0xf2, 0xdf, 0x8e, 0x68, +0x59, 0xdc, 0x06, 0xbc, 0xb6, 0x85, 0x0d, 0x06, 0x22, 0xec, 0xb1, 0xcb, 0xe5, 0x04, 0xe6, +0x3d, 0xb3, 0xb0, 0x41, 0x73, 0x08, 0x3f, 0x3c, 0x58, 0x86, 0x63, 0xeb, 0x50, 0xee, 0x1d, +0x2c, 0x37, 0x74, 0xa9, 0xd3, 0x18, 0xa3, 0x47, 0x6e, 0x93, 0x54, 0xad, 0x0a, 0x5d, 0xb8, +0x2a, 0x55, 0x5d, 0x78, 0xf6, 0xee, 0xbe, 0x8e, 0x3c, 0x76, 0x69, 0xb9, 0x40, 0xc2, 0x34, +0xec, 0x2a, 0xb9, 0xed, 0x7e, 0x20, 0xe4, 0x8d, 0x00, 0x38, 0xc7, 0xe6, 0x8f, 0x44, 0xa8, +0x86, 0xce, 0xeb, 0x2a, 0xe9, 0x90, 0xf1, 0x4c, 0xdf, 0x32, 0xfb, 0x73, 0x1b, 0x6d, 0x92, +0x1e, 0x95, 0xfe, 0xb4, 0xdb, 0x65, 0xdf, 0x4d, 0x23, 0x54, 0x89, 0x48, 0xbf, 0x4a, 0x2e, +0x70, 0xd6, 0xd7, 0x62, 0xb4, 0x33, 0x29, 0xb1, 0x3a, 0x33, 0x4c, 0x23, 0x6d, 0xa6, 0x76, +0xa5, 0x21, 0x63, 0x48, 0xe6, 0x90, 0x5d, 0xed, 0x90, 0x95, 0x0b, 0x7a, 0x84, 0xbe, 0xb8, +0x0d, 0x5e, 0x63, 0x0c, 0x62, 0x26, 0x4c, 0x14, 0x5a, 0xb3, 0xac, 0x23, 0xa4, 0x74, 0xa7, +0x6f, 0x33, 0x30, 0x05, 0x60, 0x01, 0x42, 0xa0, 0x28, 0xb7, 0xee, 0x19, 0x38, 0xf1, 0x64, +0x80, 0x82, 0x43, 0xe1, 0x41, 0x27, 0x1f, 0x1f, 0x90, 0x54, 0x7a, 0xd5, 0x23, 0x2e, 0xd1, +0x3d, 0xcb, 0x28, 0xba, 0x58, 0x7f, 0xdc, 0x7c, 0x91, 0x24, 0xe9, 0x28, 0x51, 0x83, 0x6e, +0xc5, 0x56, 0x21, 0x42, 0xed, 0xa0, 0x56, 0x22, 0xa1, 0x40, 0x80, 0x6b, 0xa8, 0xf7, 0x94, +0xca, 0x13, 0x6b, 0x0c, 0x39, 0xd9, 0xfd, 0xe9, 0xf3, 0x6f, 0xa6, 0x9e, 0xfc, 0x70, 0x8a, +0xb3, 0xbc, 0x59, 0x3c, 0x1e, 0x1d, 0x6c, 0xf9, 0x7c, 0xaf, 0xf9, 0x88, 0x71, 0x95, 0xeb, +0x57, 0x00, 0xbd, 0x9f, 0x8c, 0x4f, 0xe1, 0x24, 0x83, 0xc5, 0x22, 0xea, 0xfd, 0xd3, 0x0c, +0xe2, 0x17, 0x18, 0x7c, 0x6a, 0x4c, 0xde, 0x77, 0xb4, 0x53, 0x9b, 0x4c, 0x81, 0xcd, 0x23, +0x60, 0xaa, 0x0e, 0x25, 0x73, 0x9c, 0x02, 0x79, 0x32, 0x30, 0xdf, 0x74, 0xdf, 0x75, 0x19, +0xf4, 0xa5, 0x14, 0x5c, 0xf7, 0x7a, 0xa8, 0xa5, 0x91, 0x84, 0x7c, 0x60, 0x03, 0x06, 0x3b, +0xcd, 0x50, 0xb6, 0x27, 0x9c, 0xfe, 0xb1, 0xdd, 0xcc, 0xd3, 0xb0, 0x59, 0x24, 0xb2, 0xca, +0xe2, 0x1c, 0x81, 0x22, 0x9d, 0x07, 0x8f, 0x8e, 0xb9, 0xbe, 0x4e, 0xfa, 0xfc, 0x39, 0x65, +0xba, 0xbf, 0x9d, 0x12, 0x37, 0x5e, 0x97, 0x7e, 0xf3, 0x89, 0xf5, 0x5d, 0xf5, 0xe3, 0x09, +0x8c, 0x62, 0xb5, 0x20, 0x9d, 0x0c, 0x53, 0x8a, 0x68, 0x1b, 0xd2, 0x8f, 0x75, 0x17, 0x5d, +0xd4, 0xe5, 0xda, 0x75, 0x62, 0x19, 0x14, 0x6a, 0x26, 0x2d, 0xeb, 0xf8, 0xaf, 0x37, 0xf0, +0x6c, 0xa4, 0x55, 0xb1, 0xbc, 0xe2, 0x33, 0xc0, 0x9a, 0xca, 0xb0, 0x11, 0x49, 0x4f, 0x68, +0x9b, 0x3b, 0x6b, 0x3c, 0xcc, 0x13, 0xf6, 0xc7, 0x85, 0x61, 0x68, 0x42, 0xae, 0xbb, 0xdd, +0xcd, 0x45, 0x16, 0x29, 0x1d, 0xea, 0xdb, 0xc8, 0x03, 0x94, 0x3c, 0xee, 0x4f, 0x82, 0x11, +0xc3, 0xec, 0x28, 0xbd, 0x97, 0x05, 0x99, 0xde, 0xd7, 0xbb, 0x5e, 0x22, 0x1f, 0xd4, 0xeb, +0x64, 0xd9, 0x92, 0xd9, 0x85, 0xb7, 0x6a, 0x05, 0x6a, 0xe4, 0x24, 0x41, 0xf1, 0xcd, 0xf0, +0xd8, 0x3f, 0xf8, 0x9e, 0x0e, 0xcd, 0x0b, 0x7a, 0x70, 0x6b, 0x5a, 0x75, 0x0a, 0x6a, 0x33, +0x88, 0xec, 0x17, 0x75, 0x08, 0x70, 0x10, 0x2f, 0x24, 0xcf, 0xc4, 0xe9, 0x42, 0x00, 0x61, +0x94, 0xca, 0x1f, 0x3a, 0x76, 0x06, 0xfa, 0xd2, 0x48, 0x81, 0xf0, 0x77, 0x60, 0x03, 0x45, +0xd9, 0x61, 0xf4, 0xa4, 0x6f, 0x3d, 0xd9, 0x30, 0xc3, 0x04, 0x6b, 0x54, 0x2a, 0xb7, 0xec, +0x3b, 0xf4, 0x4b, 0xf5, 0x68, 0x52, 0x26, 0xce, 0xff, 0x5d, 0x19, 0x91, 0xa0, 0xa3, 0xa5, +0xa9, 0xb1, 0xe0, 0x23, 0xc4, 0x0a, 0x77, 0x4d, 0xf9, 0x51, 0x20, 0xa3, 0xa5, 0xa9, 0xb1, +0xc1, 0x00, 0x82, 0x86, 0x8e, 0x7f, 0x5d, 0x19, 0x91, 0xa0, 0xa3, 0xc4, 0xeb, 0x54, 0x0b, +0x75, 0x68, 0x52, 0x07, 0x8c, 0x9a, 0x97, 0x8d, 0x79, 0x70, 0x62, 0x46, 0xef, 0x5c, 0x1b, +0x95, 0x89, 0x71, 0x41, 0xe1, 0x21, 0xa1, 0xa1, 0xa1, 0xc0, 0x02, 0x67, 0x4c, 0x1a, 0xb6, +0xcf, 0xfd, 0x78, 0x53, 0x24, 0xab, 0xb5, 0xc9, 0xf1, 0x60, 0x23, 0xa5, 0xc8, 0x12, 0x87, +0x6d, 0x58, 0x13, 0x85, 0x88, 0x92, 0x87, 0x6d, 0x58, 0x32, 0xc7, 0x0c, 0x9a, 0x97, 0xac, +0xda, 0x36, 0xee, 0x5e, 0x3e, 0xdf, 0x1d, 0xb8, 0xf2, 0x66, 0x2f, 0xbd, 0xf8, 0x72, 0x47, +0xed, 0x58, 0x13, 0x85, 0x88, 0x92, 0x87, 0x8c, 0x7b, 0x55, 0x09, 0x90, 0xa2, 0xc6, 0xef, +0x3d, 0xf8, 0x53, 0x24, 0xab, 0xd4, 0x2a, 0xb7, 0xec, 0x5a, 0x36, 0xee, 0x5e, 0x3e, 0xdf, +0x3c, 0xfa, 0x76, 0x4f, 0xfd, 0x59, 0x30, 0xe2, 0x46, 0xef, 0x3d, 0xf8, 0x53, 0x05, 0x69, +0x31, 0xc1, 0x00, 0x82, 0x86, 0x8e, 0x7f, 0x5d, 0x19, 0xb0, 0xe2, 0x27, 0xcc, 0xfb, 0x74, +0x4b, 0x14, 0x8b, 0x94, 0x8b, 0x75, 0x68, 0x33, 0xc5, 0x08, 0x92, 0x87, 0x8c, 0x9a, 0xb6, +0xcf, 0x1c, 0xba, 0xd7, 0x0d, 0x98, 0xb2, 0xe6, 0x2f, 0xdc, 0x1b, 0x95, 0x89, 0x71, 0x60, +0x23, 0xc4, 0x0a, 0x96, 0x8f, 0x9c, 0xba, 0xf6, 0x6e, 0x3f, 0xfc, 0x5b, 0x15, 0xa8, 0xd2, +0x26, 0xaf, 0xbd, 0xf8, 0x72, 0x66, 0x2f, 0xdc, 0x1b, 0xb4, 0xcb, 0x14, 0x8b, 0x94, 0xaa, +0xb7, 0xcd, 0xf9, 0x51, 0x01, 0x80, 0x82, 0x86, 0x6f, 0x3d, 0xd9, 0x30, 0xe2, 0x27, 0xcc, +0xfb, 0x74, 0x4b, 0x14, 0xaa, 0xb7, 0xcd, 0xf9, 0x70, 0x43, 0x04, 0x6b, 0x35, 0xc9, 0xf1, +0x60, 0x23, 0xa5, 0xc8, 0xf3, 0x45, 0x08, 0x92, 0x87, 0x6d, 0x58, 0x32, 0xe6, 0x2f, 0xbd, +0xf8, 0x72, 0x66, 0x4e, 0x1e, 0xbe, 0xfe, 0x7e, 0x7e, 0x7e, 0x5f, 0x1d, 0x99, 0x91, 0xa0, +0xa3, 0xc4, 0x0a, 0x77, 0x4d, 0x18, 0x93, 0xa4, 0xab, 0xd4, 0x0b, 0x75, 0x49, 0x10, 0xa2, +0xc6, 0xef, 0x3d, 0xf8, 0x53, 0x24, 0xab, 0xb5, 0xe8, 0x33, 0xe4, 0x4a, 0x16, 0xae, 0xde, +0x1f, 0xbc, 0xdb, 0x15, 0xa8, 0xb3, 0xc5, 0x08, 0x73, 0x45, 0xe9, 0x31, 0xc1, 0xe1, 0x21, +0xa1, 0xa1, 0xa1, 0xc0, 0x02, 0x86, 0x6f, 0x5c, 0x3a, 0xd7, 0x0d, 0x98, 0x93, 0xa4, 0xca, +0x16, 0xae, 0xde, 0x1f, 0x9d, 0x99, 0xb0, 0xe2, 0x46, 0xef, 0x3d, 0xf8, 0x72, 0x47, 0x0c, +0x9a, 0xb6, 0xcf, 0xfd, 0x59, 0x11, 0xa0, 0xa3, 0xa5, 0xc8, 0xf3, 0x45, 0x08, 0x92, 0x87, +0x6d, 0x39, 0xf0, 0x43, 0x04, 0x8a, 0x96, 0xae, 0xde, 0x3e, 0xdf, 0x1d, 0x99, 0x91, 0xa0, +0xc2, 0x06, 0x6f, 0x3d, 0xf8, 0x72, 0x47, 0x0c, 0x9a, 0x97, 0x8d, 0x98, 0x93, 0x85, 0x88, +0x73, 0x45, 0xe9, 0x31, 0xe0, 0x23, 0xa5, 0xa9, 0xd0, 0x03, 0x84, 0x8a, 0x96, 0xae, 0xde, +0x1f, 0xbc, 0xdb, 0x15, 0xa8, 0xd2, 0x26, 0xce, 0xff, 0x5d, 0x19, 0x91, 0x81, 0x80, 0x82, +0x67, 0x2d, 0xd8, 0x13, 0xa4, 0xab, 0xd4, 0x0b, 0x94, 0xaa, 0xb7, 0xcd, 0xf9, 0x51, 0x20, +0xa3, 0xa5, 0xc8, 0xf3, 0x45, 0xe9, 0x50, 0x22, 0xc6, 0xef, 0x5c, 0x3a, 0xd7, 0x0d, 0x98, +0x93, 0x85, 0x88, 0x73, 0x64, 0x4a, 0xf7, 0x4d, 0xf9, 0x51, 0x20, 0xa3, 0xc4, 0x0a, 0x96, +0xae, 0xde, 0x3e, 0xfe, 0x7e, 0x7e, 0x7e, 0x5f, 0x3c, 0xfa, 0x76, 0x4f, 0xfd, 0x78, 0x72, +0x66, 0x2f, 0xbd, 0xd9, 0x30, 0xc3, 0xe5, 0x48, 0x12, 0x87, 0x8c, 0x7b, 0x55, 0x28, 0xd2, +0x07, 0x8c, 0x9a, 0x97, 0xac, 0xda, 0x17, 0x8d, 0x79, 0x51, 0x20, 0xa3, 0xc4, 0xeb, 0x54, +0x0b, 0x94, 0x8b, 0x94, 0xaa, 0xd6, 0x2e, 0xbf, 0xfc, 0x5b, 0x15, 0xa8, 0xd2, 0x26, 0xaf, +0xdc, 0x1b, 0xb4, 0xea, 0x37, 0xec, 0x3b, 0xf4, 0x6a, 0x37, 0xcd, 0x18, 0x93, 0x85, 0x69, +0x31, 0xc1, 0xe1, 0x40, 0xe3, 0x25, 0xc8, 0x12, 0x87, 0x8c, 0x9a, 0xb6, 0xcf, 0xfd, 0x59, +0x11, 0xa0, 0xc2, 0x06, 0x8e, 0x7f, 0x5d, 0x38, 0xf2, 0x47, 0x0c, 0x7b, 0x74, 0x6a, 0x37, +0xec, 0x5a, 0x36, 0xee, 0x3f, 0xfc, 0x7a, 0x76, 0x4f, 0x1c, 0x9b, 0x95, 0x89, 0x71, 0x41, +0x00, 0x63, 0x44, 0xeb, 0x54, 0x2a, 0xd6, 0x0f, 0x9c, 0xba, 0xd7, 0x0d, 0x98, 0x93, 0x85, +0x69, 0x31, 0xc1, 0x00, 0x82, 0x86, 0x8e, 0x9e, 0xbe, 0xdf, 0x3c, 0xfa, 0x57, 0x2c, 0xda, +0x36, 0xee, 0x3f, 0xfc, 0x5b, 0x15, 0x89, 0x71, 0x41, 0x00, 0x82, 0x86, 0x8e, 0x7f, 0x5d, +0x38, 0xf2, 0x47, 0xed, 0x58, 0x13, 0xa4, 0xca, 0xf7, 0x4d, 0xf9, 0x51, 0x01, 0x80, 0x63, +0x44, 0xeb, 0x54, 0x2a, 0xd6, 0x2e, 0xbf, 0xdd, 0x19, 0x91, 0xa0, 0xa3, 0xa5, 0xa9, 0xb1, +0xe0, 0x42, 0x06, 0x8e, 0x7f, 0x5d, 0x19, 0x91, 0xa0, 0xa3, 0xc4, 0x0a, 0x96, 0x8f, 0x7d, +0x78, 0x72, 0x47, 0x0c, 0x7b, 0x74, 0x6a, 0x56, 0x2e, 0xde, 0x1f, 0xbc, 0xfa, 0x57, 0x0d, +0x79, 0x51, 0x01, 0x61, 0x21, 0xa1, 0xc0, 0xe3, 0x25, 0xa9, 0xb1, 0xc1, 0xe1, 0x40, 0x02, +0x67, 0x4c, 0x1a, 0x97, 0x8d, 0x98, 0x93, 0xa4, 0xab, 0xd4, 0x2a, 0xd6, 0x0f, 0x9c, 0x9b, +0xb4, 0xcb, 0x14, 0xaa, 0xb7, 0xcd, 0xf9, 0x51, 0x20, 0xa3, 0xc4, 0xeb, 0x35, 0xc9, 0xf1, +0x60, 0x42, 0x06, 0x8e, 0x7f, 0x7c, 0x7a, 0x76, 0x6e, 0x3f, 0xfc, 0x7a, 0x76, 0x6e, 0x5e, +0x3e, 0xfe, 0x7e, 0x5f, 0x3c, 0xdb, 0x15, 0x89, 0x71, 0x41, 0xe1, 0x21, 0xc0, 0xe3, 0x44, +0xeb, 0x54, 0x2a, 0xb7, 0xcd, 0xf9, 0x70, 0x62, 0x27, 0xad, 0xd8, 0x32, 0xc7, 0x0c, 0x7b, +0x74, 0x4b, 0x14, 0xaa, 0xb7, 0xec, 0x3b, 0xd5, 0x28, 0xd2, 0x07, 0x6d, 0x39, 0xd1, 0x20, +0xc2, 0xe7, 0x4c, 0x1a, 0x97, 0x8d, 0x98, 0xb2, 0xc7, 0x0c, 0x59, 0x28, 0xf3, 0x9b }; diff --git a/app/drivers/zephyr/dts/bindings/sensor/pixart,pmw3360.yaml b/app/drivers/zephyr/dts/bindings/sensor/pixart,pmw3360.yaml new file mode 100644 index 00000000000..7d86b6dca93 --- /dev/null +++ b/app/drivers/zephyr/dts/bindings/sensor/pixart,pmw3360.yaml @@ -0,0 +1,20 @@ +# Copyright (c) 2022 The ZMK Contributors +# SPDX-License-Identifier: MIT +description: | + Sensor driver for the pixart PMW3360 optical mouse sensor + +compatible: "pixart,pmw3360" + +include: spi-device.yaml + +properties: + label: + type: string + required: true + irq-gpios: + type: phandle-array + required: true + scroll-layer: + type: int + description: the momentary layer used to modifiy move to scroll + required: false From df5a3a4ffa3c5875ae132445d9ad997bcc02f542 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Thu, 1 Sep 2022 21:52:34 +0800 Subject: [PATCH 069/157] backup --- app/drivers/sensor/paw3395/CMakeLists.txt | 7 + app/drivers/sensor/paw3395/Kconfig | 75 ++ app/drivers/sensor/paw3395/paw3395.c | 1000 +++++++++++++++++ app/drivers/sensor/paw3395/paw3395.h | 121 ++ app/drivers/sensor/paw3395/paw3395_priv.c | 62 + .../dts/bindings/sensor/pixart,paw3395.yaml | 20 + 6 files changed, 1285 insertions(+) create mode 100644 app/drivers/sensor/paw3395/CMakeLists.txt create mode 100644 app/drivers/sensor/paw3395/Kconfig create mode 100644 app/drivers/sensor/paw3395/paw3395.c create mode 100644 app/drivers/sensor/paw3395/paw3395.h create mode 100644 app/drivers/sensor/paw3395/paw3395_priv.c create mode 100644 app/drivers/zephyr/dts/bindings/sensor/pixart,paw3395.yaml diff --git a/app/drivers/sensor/paw3395/CMakeLists.txt b/app/drivers/sensor/paw3395/CMakeLists.txt new file mode 100644 index 00000000000..66079047d64 --- /dev/null +++ b/app/drivers/sensor/paw3395/CMakeLists.txt @@ -0,0 +1,7 @@ +# Copyright (c) 2022 The ZMK Contributors +# SPDX-License-Identifier: MIT + +zephyr_library() + +zephyr_library_sources(paw3395.c) +zephyr_library_sources(paw3395_priv.c) diff --git a/app/drivers/sensor/paw3395/Kconfig b/app/drivers/sensor/paw3395/Kconfig new file mode 100644 index 00000000000..9a53d9f2751 --- /dev/null +++ b/app/drivers/sensor/paw3395/Kconfig @@ -0,0 +1,75 @@ +# Sensor data simulator +# +# Copyright (c) 2019 Nordic Semiconductor +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +menuconfig PAW3395 + bool "PAW3395 mouse optical sensor" + help + Enable PAW3395 mouse optical sensor. + +if PAW3395 + +config PAW3395_CPI + int "PAW3395's default CPI" + default 1600 + range 100 12000 + help + Default CPI value. + +config PAW3395_CPI_DIVIDOR + int "PAW3395's default CPI dividor" + default 4 + range 1 12 + help + Default CPI dividor value. + +config PAW3395_RUN_DOWNSHIFT_TIME_MS + int "PAW3395's default RUN mode downshift time" + default 500 + range 10 2550 + help + Default RUN mode downshift down time in milliseconds. + Time after which sensor goes from RUN to REST1 mode. + +config PAW3395_REST1_DOWNSHIFT_TIME_MS + int "PAW3395's default REST1 mode downshift time" + default 9220 + range 320 81600 + help + Default REST1 mode downshift down time in milliseconds. + Time after which sensor goes from REST1 to REST2 mode. + +config PAW3395_REST2_DOWNSHIFT_TIME_MS + int "PAW3395's default REST2 mode downshift time" + default 150000 + range 3200 816000 + help + Default REST2 mode downshift down time in milliseconds. + Time after which sensor goes from REST2 to REST3 mode. + +choice + prompt "Select PAW3395 sensor orientation" + default PAW3395_ORIENTATION_0 + +config PAW3395_ORIENTATION_0 + bool "PAW3395 not rotated" + +config PAW3395_ORIENTATION_90 + bool "PAW3395 rotated 90 deg clockwise" + +config PAW3395_ORIENTATION_180 + bool "PAW3395 rotated 180 deg clockwise" + +config PAW3395_ORIENTATION_270 + bool "PAW3395 rotated 270 deg clockwise" + +endchoice + +module = PAW3395 +module-str = PAW3395 +source "${ZEPHYR_BASE}/subsys/logging/Kconfig.template.log_config" + +endif #PAW3395 diff --git a/app/drivers/sensor/paw3395/paw3395.c b/app/drivers/sensor/paw3395/paw3395.c new file mode 100644 index 00000000000..b31d820b6e0 --- /dev/null +++ b/app/drivers/sensor/paw3395/paw3395.c @@ -0,0 +1,1000 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#define DT_DRV_COMPAT pixart_paw3395 + +#include +#include +#include "paw3395.h" + +#include +LOG_MODULE_REGISTER(paw3395, CONFIG_PAW3395_LOG_LEVEL); + +/* Timings defined by spec (in us) */ +// sub-us time is rounded to us, due to the limitation of k_busy_wait +// see the discusssion here: https://github.com/zephyrproject-rtos/zephyr/issues/6498 +#define T_NCS_SCLK 1 /* 120 ns (rounded to 1us) */ +#define T_SCLK_NCS_WR 1 /* 1 us */ +#define T_SRAD 2 /* 2 us */ +#define T_SRAD_MOTBR 2 /* same as T_SRAD */ +#define T_SRX 2 /* 2 us */ +#define T_SWX 5 /* 5 us */ +#define T_BEXIT 1 /* 500 ns (rounded to 1us)*/ + +/* Sensor registers */ +#define PAW3395_REG_PRODUCT_ID 0x00 +#define PAW3395_REG_REVISION_ID 0x01 +#define PAW3395_REG_MOTION 0x02 +#define PAW3395_REG_DELTA_X_L 0x03 +#define PAW3395_REG_DELTA_X_H 0x04 +#define PAW3395_REG_DELTA_Y_L 0x05 +#define PAW3395_REG_DELTA_Y_H 0x06 +#define PAW3395_REG_SQUAL 0x07 +#define PAW3395_REG_RAW_DATA_SUM 0x08 +#define PAW3395_REG_MAXIMUM_RAW_DATA 0x09 +#define PAW3395_REG_MINIMUM_RAW_DATA 0x0A +#define PAW3395_REG_SHUTTER_LOWER 0x0B +#define PAW3395_REG_SHUTTER_UPPER 0x0C +#define PAW3395_REG_CONTROL 0x0D +#define PAW3395_REG_CONFIG1 0x0F +#define PAW3395_REG_CONFIG2 0x10 +#define PAW3395_REG_ANGLE_TUNE 0x11 +#define PAW3395_REG_FRAME_CAPTURE 0x12 +#define PAW3395_REG_SROM_ENABLE 0x13 +#define PAW3395_REG_RUN_DOWNSHIFT 0x14 +#define PAW3395_REG_REST1_RATE_LOWER 0x15 +#define PAW3395_REG_REST1_RATE_UPPER 0x16 +#define PAW3395_REG_REST1_DOWNSHIFT 0x17 +#define PAW3395_REG_REST2_RATE_LOWER 0x18 +#define PAW3395_REG_REST2_RATE_UPPER 0x19 +#define PAW3395_REG_REST2_DOWNSHIFT 0x1A +#define PAW3395_REG_REST3_RATE_LOWER 0x1B +#define PAW3395_REG_REST3_RATE_UPPER 0x1C +#define PAW3395_REG_OBSERVATION 0x24 +#define PAW3395_REG_DATA_OUT_LOWER 0x25 +#define PAW3395_REG_DATA_OUT_UPPER 0x26 +#define PAW3395_REG_RAW_DATA_DUMP 0x29 +#define PAW3395_REG_SROM_ID 0x2A +#define PAW3395_REG_MIN_SQ_RUN 0x2B +#define PAW3395_REG_RAW_DATA_THRESHOLD 0x2C +#define PAW3395_REG_CONFIG5 0x2F +#define PAW3395_REG_POWER_UP_RESET 0x3A +#define PAW3395_REG_SHUTDOWN 0x3B +#define PAW3395_REG_INVERSE_PRODUCT_ID 0x3F +#define PAW3395_REG_LIFTCUTOFF_TUNE3 0x41 +#define PAW3395_REG_ANGLE_SNAP 0x42 +#define PAW3395_REG_LIFTCUTOFF_TUNE1 0x4A +#define PAW3395_REG_MOTION_BURST 0x50 +#define PAW3395_REG_LIFTCUTOFF_TUNE_TIMEOUT 0x58 +#define PAW3395_REG_LIFTCUTOFF_TUNE_MIN_LENGTH 0x5A +#define PAW3395_REG_SROM_LOAD_BURST 0x62 +#define PAW3395_REG_LIFT_CONFIG 0x63 +#define PAW3395_REG_RAW_DATA_BURST 0x64 +#define PAW3395_REG_LIFTCUTOFF_TUNE2 0x65 + +/* Sensor identification values */ +#define PAW3395_PRODUCT_ID 0x42 +#define PAW3395_FIRMWARE_ID 0x04 + +/* Max register count readable in a single motion burst */ +#define PAW3395_MAX_BURST_SIZE 12 + +/* Register count used for reading a single motion burst */ +#define PAW3395_BURST_SIZE 6 + +/* Position of X in motion burst data */ +#define PAW3395_DX_POS 2 +#define PAW3395_DY_POS 4 + +/* Rest_En position in Config2 register. */ +#define PAW3395_REST_EN_POS 5 + +#define PAW3395_MAX_CPI 12000 +#define PAW3395_MIN_CPI 100 + + +#define SPI_WRITE_BIT BIT(7) + +/* Helper macros used to convert sensor values. */ +#define PAW3395_SVALUE_TO_CPI(svalue) ((uint32_t)(svalue).val1) +#define PAW3395_SVALUE_TO_TIME(svalue) ((uint32_t)(svalue).val1) +#define PAW3395_SVALUE_TO_BOOL(svalue) ((svalue).val1 != 0) + + +/* SROM firmware meta-data, defined in paw3395_piv.c */ +extern const size_t paw3395_firmware_length; +extern const uint8_t paw3395_firmware_data[]; + + +/* sensor initialization steps definition */ +// init is done in non-blocking manner (i.e., async), a delayable work is defined for this job +// see paw3395_init and paw3395_async_init) + +// delay (ms) in between steps +static const int32_t async_init_delay[ASYNC_INIT_STEP_COUNT] = { + [ASYNC_INIT_STEP_POWER_UP] = 50, // required in spec + [ASYNC_INIT_STEP_LOAD_SETTING] = 5, // required in spec + [ASYNC_INIT_STEP_CONFIGURE] = K_NO_WAIT, +}; + +static int paw3395_async_init_power_up(const struct device *dev); +static int paw3395_async_init_load_setting(const struct device *dev); +static int paw3395_async_init_configure(const struct device *dev); + +static int (* const async_init_fn[ASYNC_INIT_STEP_COUNT])(const struct device *dev) = { + [ASYNC_INIT_STEP_POWER_UP] = paw3395_async_init_power_up, + [ASYNC_INIT_STEP_LOAD_SETTING] = paw3395_async_init_load_setting, + [ASYNC_INIT_STEP_CONFIGURE] = paw3395_async_init_configure, +}; + +// checked and keep +static int spi_cs_ctrl(const struct device *dev, bool enable) +{ + const struct paw3395_config *config = dev->config; + int err; + + if (!enable) { + k_busy_wait(T_NCS_SCLK); + } + + err = gpio_pin_set_dt(&config->cs_gpio, (int)enable); + if (err) { + LOG_ERR("SPI CS ctrl failed"); + } + + if (enable) { + k_busy_wait(T_NCS_SCLK); + } + + return err; +} + +// checked and keep +static int reg_read(const struct device *dev, uint8_t reg, uint8_t *buf) +{ + int err; + struct paw3395_data *data = dev->data; + const struct paw3395_config *config = dev->config; + + __ASSERT_NO_MSG((reg & SPI_WRITE_BIT) == 0); + + err = spi_cs_ctrl(dev, true); + if (err) { + return err; + } + + /* Write register address. */ + const struct spi_buf tx_buf = { + .buf = ®, + .len = 1 + }; + const struct spi_buf_set tx = { + .buffers = &tx_buf, + .count = 1 + }; + + err = spi_write_dt(&config->bus, &tx); + if (err) { + LOG_ERR("Reg read failed on SPI write"); + return err; + } + + k_busy_wait(T_SRAD); + + /* Read register value. */ + struct spi_buf rx_buf = { + .buf = buf, + .len = 1, + }; + const struct spi_buf_set rx = { + .buffers = &rx_buf, + .count = 1, + }; + + err = spi_read_dt(&config->bus, &rx); + if (err) { + LOG_ERR("Reg read failed on SPI read"); + return err; + } + + err = spi_cs_ctrl(dev, false); + if (err) { + return err; + } + + k_busy_wait(T_SRX); + + // todo: needed? + /* data->last_read_burst = false; */ + + return 0; +} + +// checked and keep +static int reg_write(const struct device *dev, uint8_t reg, uint8_t val) +{ + int err; + struct paw3395_data *data = dev->data; + const struct paw3395_config *config = dev->config; + + __ASSERT_NO_MSG((reg & SPI_WRITE_BIT) == 0); + + err = spi_cs_ctrl(dev, true); + if (err) { + return err; + } + + uint8_t buf[] = { + SPI_WRITE_BIT | reg, + val + }; + const struct spi_buf tx_buf = { + .buf = buf, + .len = ARRAY_SIZE(buf) + }; + const struct spi_buf_set tx = { + .buffers = &tx_buf, + .count = 1 + }; + + err = spi_write_dt(&config->bus, &tx); + if (err) { + LOG_ERR("Reg write failed on SPI write"); + return err; + } + + k_busy_wait(T_SCLK_NCS_WR); + + err = spi_cs_ctrl(dev, false); + if (err) { + return err; + } + + k_busy_wait(T_SWX); + + /* data->last_read_burst = false; */ + + return 0; +} + +static int motion_burst_read(const struct device *dev, uint8_t *buf, + size_t burst_size) +{ + int err; + struct paw3395_data *data = dev->data; + const struct paw3395_config *config = dev->config; + + __ASSERT_NO_MSG(burst_size <= PAW3395_MAX_BURST_SIZE); + + /* Write any value to motion burst register only if there have been + * other SPI transmissions with sensor since last burst read. + */ + // todo: needed or not? + /* if (!data->last_read_burst) { */ + /* err = reg_write(dev, PAW3395_REG_MOTION_BURST, 0x00); */ + /* if (err) { */ + /* return err; */ + /* } */ + /* } */ + + err = spi_cs_ctrl(dev, true); + if (err) { + return err; + } + + /* Send motion burst address */ + uint8_t reg_buf[] = { + PAW3395_REG_MOTION_BURST + }; + const struct spi_buf tx_buf = { + .buf = reg_buf, + .len = ARRAY_SIZE(reg_buf) + }; + const struct spi_buf_set tx = { + .buffers = &tx_buf, + .count = 1 + }; + + // todo: after sending the address, mosi should be held static until burst read is complete? how to do it in zephyr + err = spi_write_dt(&config->bus, &tx); + if (err) { + LOG_ERR("Motion burst failed on SPI write"); + return err; + } + + k_busy_wait(T_SRAD_MOTBR); + + const struct spi_buf rx_buf = { + .buf = buf, + .len = burst_size, + }; + const struct spi_buf_set rx = { + .buffers = &rx_buf, + .count = 1 + }; + + err = spi_read_dt(&config->bus, &rx); + if (err) { + LOG_ERR("Motion burst failed on SPI read"); + return err; + } + + err = spi_cs_ctrl(dev, false); + if (err) { + return err; + } + + /* Terminate burst */ + k_busy_wait(T_BEXIT); + + /* data->last_read_burst = true; */ + + return 0; +} + +/** Writing an array of registers in sequence, used in power-up register initialization and running mode switching */ +// this impl is totally new, different from pmw3360 +static int burst_write(const struct device *dev, uint8_t *addr, const uint8_t *buf, + size_t size) +{ + int err; + struct paw3395_data *data = dev->data; + const struct paw3395_config *config = dev->config; + + /* Write address of burst register */ + uint8_t write_buf = reg | SPI_WRITE_BIT; + struct spi_buf tx_buf = { + .buf = &write_buf, + .len = 1 + }; + const struct spi_buf_set tx = { + .buffers = &tx_buf, + .count = 1 + }; + + err = spi_cs_ctrl(dev, true); + if (err) { + return err; + } + + err = spi_write_dt(&config->bus, &tx); + if (err) { + LOG_ERR("Burst write failed on SPI write"); + return err; + } + + /* Write data */ + for (size_t i = 0; i < size; i++) { + write_buf = buf[i]; + + err = spi_write_dt(&config->bus, &tx); + if (err) { + LOG_ERR("Burst write failed on SPI write (data)"); + return err; + } + + k_busy_wait(T_BRSEP); + } + + /* Terminate burst mode. */ + err = spi_cs_ctrl(dev, false); + if (err) { + return err; + } + + // todo: better keep it (no requirement in datasheet)? + k_busy_wait(T_BEXIT); + + data->last_read_burst = false; + + return 0; +} + +static int update_cpi(const struct device *dev, uint32_t cpi) +{ + /* Set resolution with CPI step of 100 cpi + * 0x00: 100 cpi (minimum cpi) + * 0x01: 200 cpi + * : + * 0x31: 5000 cpi (default cpi) + * : + * 0x77: 12000 cpi (maximum cpi) + */ + + if ((cpi > PAW3395_MAX_CPI) || (cpi < PAW3395_MIN_CPI)) { + LOG_ERR("CPI value %u out of range", cpi); + return -EINVAL; + } + + /* Convert CPI to register value */ + uint8_t value = (cpi / 100) - 1; + + LOG_INF("Setting CPI to %u (reg value 0x%x)", cpi, value); + + int err = reg_write(dev, PAW3395_REG_CONFIG1, value); + if (err) { + LOG_ERR("Failed to change CPI"); + } + + return err; +} + +/* unit: ms */ +static int update_downshift_time(const struct device *dev, uint8_t reg_addr, + uint32_t time) +{ + /* Set downshift time in ms: + * - Run downshift time (from Run to Rest1 mode), default: 500ms + * - Rest 1 downshift time (from Rest1 to Rest2 mode), default: 9.92 s + * - Rest 2 downshift time (from Rest2 to Rest3 mode), default: ~10 min + */ + uint32_t maxtime; + uint32_t mintime; + + switch (reg_addr) { + case PAW3395_REG_RUN_DOWNSHIFT: + /* + * Run downshift time = PAW3395_REG_RUN_DOWNSHIFT * 10 ms + */ + maxtime = 2550; + mintime = 10; + break; + + case PAW3395_REG_REST1_DOWNSHIFT: + /* + * Rest1 downshift time = PAW3395_REG_RUN_DOWNSHIFT + * * 320 * Rest1 rate (default 1 ms) + */ + maxtime = 81600; + mintime = 320; + break; + + case PAW3395_REG_REST2_DOWNSHIFT: + /* + * Rest2 downshift time = PAW3395_REG_REST2_DOWNSHIFT + * * 32 * Rest2 rate (default 100 ms) + */ + maxtime = 816000; + mintime = 3200; + break; + + default: + LOG_ERR("Not supported"); + return -ENOTSUP; + } + + if ((time > maxtime) || (time < mintime)) { + LOG_WRN("Downshift time %u out of range", time); + return -EINVAL; + } + + __ASSERT_NO_MSG((mintime > 0) && (maxtime/mintime <= UINT8_MAX)); + + /* Convert time to register value */ + uint8_t value = time / mintime; + + LOG_INF("Set downshift time to %u ms (reg value 0x%x)", time, value); + + int err = reg_write(dev, reg_addr, value); + if (err) { + LOG_ERR("Failed to change downshift time"); + } + + return err; +} + +/* set sampling rate in each mode (in ms) */ +static int update_sample_time(const struct device *dev, + uint8_t reg_addr_lower, + uint8_t reg_addr_upper, + uint32_t sample_time) +{ + /* Set sample time for the Rest1-Rest3 modes. + * Values above 0x09B0 will trigger internal watchdog reset. + */ + uint32_t maxtime = 0x9B0; + uint32_t mintime = 1; + + if ((sample_time > maxtime) || (sample_time < mintime)) { + LOG_WRN("Sample time %u out of range", sample_time); + return -EINVAL; + } + + LOG_INF("Set sample time to %u ms", sample_time); + + /* The sample time is (reg_value + 1) ms. */ + sample_time--; + uint8_t buf[2]; + + sys_put_le16((uint16_t)sample_time, buf); + + int err = reg_write(dev, reg_addr_lower, buf[0]); + + if (!err) { + err = reg_write(dev, reg_addr_upper, buf[1]); + } else { + LOG_ERR("Failed to change sample time"); + } + + return err; +} + +static int toggle_rest_modes(const struct device *dev, uint8_t reg_addr, + bool enable) +{ + uint8_t value; + int err = reg_read(dev, reg_addr, &value); + + if (err) { + LOG_ERR("Failed to read Config2 register"); + return err; + } + + WRITE_BIT(value, PAW3395_REST_EN_POS, enable); + + LOG_INF("%sable rest modes", (enable) ? ("En") : ("Dis")); + err = reg_write(dev, reg_addr, value); + + if (err) { + LOG_ERR("Failed to set rest mode"); + } + + return err; +} + +static int paw3395_async_init_load_setting(const struct device *dev) +{ + int err; + + LOG_INF("Uploading optical sensor firmware..."); + + /* Write 0x18 to SROM_enable to start SROM download */ + err = reg_write(dev, PAW3395_REG_SROM_ENABLE, 0x18); + if (err) { + LOG_ERR("Cannot start SROM download"); + return err; + } + + /* Write SROM file into SROM_Load_Burst register. + * Data must start with SROM_Load_Burst address. + */ + err = burst_write(dev, PAW3395_REG_SROM_LOAD_BURST, + paw3395_firmware_data, paw3395_firmware_length); + if (err) { + LOG_ERR("Cannot write firmware to sensor"); + } + + return err; +} + +static void irq_handler(const struct device *gpiob, struct gpio_callback *cb, + uint32_t pins) +{ + int err; + struct paw3395_data *data = CONTAINER_OF(cb, struct paw3395_data, + irq_gpio_cb); + const struct device *dev = data->dev; + const struct paw3395_config *config = dev->config; + + // disable the interrupt line first + err = gpio_pin_interrupt_configure_dt(&config->irq_gpio, + GPIO_INT_DISABLE); + if (unlikely(err)) { + LOG_ERR("Cannot disable IRQ"); + k_panic(); + } + + // submit the real handler work + k_work_submit(&data->trigger_handler_work); +} + +static void trigger_handler(struct k_work *work) +{ + sensor_trigger_handler_t handler; + int err = 0; + struct paw3395_data *data = CONTAINER_OF(work, struct paw3395_data, + trigger_handler_work); + const struct device *dev = data->dev; + const struct paw3395_config *config = dev->config; + + // 1. the first lock period is used to procoss the trigger + // if data_ready_handler is non-NULL, otherwise do nothing + k_spinlock_key_t key = k_spin_lock(&data->lock); + + handler = data->data_ready_handler; + k_spin_unlock(&data->lock, key); + + if (!handler) { + return; + } + + struct sensor_trigger trig = { + .type = SENSOR_TRIG_DATA_READY, + .chan = SENSOR_CHAN_ALL, + }; + + handler(dev, &trig); + + // 2. the second lock period is used to resume the interrupt line + // if data_ready_handler is non-NULL, otherwise keep it inactive + key = k_spin_lock(&data->lock); + if (data->data_ready_handler) { + err = gpio_pin_interrupt_configure_dt(&config->irq_gpio, + GPIO_INT_LEVEL_ACTIVE); + } + k_spin_unlock(&data->lock, key); + + if (unlikely(err)) { + LOG_ERR("Cannot re-enable IRQ"); + k_panic(); + } +} + +static int paw3395_async_init_power_up(const struct device *dev) +{ + /* Reset sensor */ + + return reg_write(dev, PAW3395_REG_POWER_UP_RESET, 0x5A); +} + +static int paw3395_async_init_configure(const struct device *dev) +{ + int err; + + err = update_cpi(dev, CONFIG_PAW3395_CPI); + + if (!err) { + err = update_downshift_time(dev, + PAW3395_REG_RUN_DOWNSHIFT, + CONFIG_PAW3395_RUN_DOWNSHIFT_TIME_MS); + } + + if (!err) { + err = update_downshift_time(dev, + PAW3395_REG_REST1_DOWNSHIFT, + CONFIG_PAW3395_REST1_DOWNSHIFT_TIME_MS); + } + + if (!err) { + err = update_downshift_time(dev, + PAW3395_REG_REST2_DOWNSHIFT, + CONFIG_PAW3395_REST2_DOWNSHIFT_TIME_MS); + } + + return err; +} + +// checked and keep +static void paw3395_async_init(struct k_work *work) +{ + struct paw3395_data *data = CONTAINER_OF(work, struct paw3395_data, + init_work); + const struct device *dev = data->dev; + + LOG_DBG("PAW3395 async init step %d", data->async_init_step); + + data->err = async_init_fn[data->async_init_step](dev); + if (data->err) { + LOG_ERR("PAW3395 initialization failed"); + } else { + data->async_init_step++; + + if (data->async_init_step == ASYNC_INIT_STEP_COUNT) { + data->ready = true; // sensor is ready to work + LOG_INF("PAW3395 initialized"); + } else { + k_work_schedule(&data->init_work, + K_MSEC(async_init_delay[ + data->async_init_step])); + } + } +} + +static int paw3395_init_irq(const struct device *dev) +{ + int err; + struct paw3395_data *data = dev->data; + const struct paw3395_config *config = dev->config; + + // check readiness of irq gpio pin + if (!device_is_ready(config->irq_gpio.port)) { + LOG_ERR("IRQ GPIO device not ready"); + return -ENODEV; + } + + // init the irq pin + err = gpio_pin_configure_dt(&config->irq_gpio, GPIO_INPUT); + if (err) { + LOG_ERR("Cannot configure IRQ GPIO"); + return err; + } + + // setup and add the irq callback associated + gpio_init_callback(&data->irq_gpio_cb, irq_handler, + BIT(config->irq_gpio.pin)); + + err = gpio_add_callback(config->irq_gpio.port, &data->irq_gpio_cb); + if (err) { + LOG_ERR("Cannot add IRQ GPIO callback"); + } + + return err; +} + +static int paw3395_init(const struct device *dev) +{ + struct paw3395_data *data = dev->data; + const struct paw3395_config *config = dev->config; + int err; + + // init device pointer + data->dev = dev; + + // init trigger handler work + k_work_init(&data->trigger_handler_work, trigger_handler); + + // check readiness of spi bus + if (!spi_is_ready(&config->bus)) { + LOG_ERR("SPI device not ready"); + return -ENODEV; + } + + // check readiness of cs gpio pin and init it to inactive + if (!device_is_ready(config->cs_gpio.port)) { + LOG_ERR("SPI CS device not ready"); + return -ENODEV; + } + + err = gpio_pin_configure_dt(&config->cs_gpio, GPIO_OUTPUT_INACTIVE); + if (err) { + LOG_ERR("Cannot configure SPI CS GPIO"); + return err; + } + + // init irq routine + err = paw3395_init_irq(dev); + if (err) { + return err; + } + + // Setup delayable and non-blocking init jobs, including following steps: + // 1. power reset + // 2. clear motion registers + // 3. srom firmware download and checking + // 4. eable rest mode + // 5. set cpi and downshift time (not sample rate) + // The sensor is ready to work (i.e., data->ready=true after the above steps are finished) + k_work_init_delayable(&data->init_work, paw3395_async_init); + + k_work_schedule(&data->init_work, + K_MSEC(async_init_delay[data->async_init_step])); + + return err; +} + +static int paw3395_sample_fetch(const struct device *dev, enum sensor_channel chan) +{ + struct paw3395_data *data = dev->data; + uint8_t buf[PAW3395_BURST_SIZE]; + + if (unlikely(chan != SENSOR_CHAN_ALL)) { + return -ENOTSUP; + } + + if (unlikely(!data->ready)) { + LOG_DBG("Device is not initialized yet"); + return -EBUSY; + } + + int err = motion_burst_read(dev, buf, sizeof(buf)); + + if (!err) { + int16_t x = ((int16_t)sys_get_le16(&buf[PAW3395_DX_POS])) / CONFIG_PAW3395_CPI_DIVIDOR; + int16_t y = ((int16_t)sys_get_le16(&buf[PAW3395_DY_POS])) / CONFIG_PAW3395_CPI_DIVIDOR; + /* int16_t x = sys_get_le16(&buf[PAW3395_DX_POS]); */ + /* int16_t y = sys_get_le16(&buf[PAW3395_DY_POS]); */ + + if (IS_ENABLED(CONFIG_PAW3395_ORIENTATION_0)) { + data->x = x; + data->y = y; + } else if (IS_ENABLED(CONFIG_PAW3395_ORIENTATION_90)) { + data->x = y; + data->y = x; + } else if (IS_ENABLED(CONFIG_PAW3395_ORIENTATION_180)) { + data->x = x; + data->y = -y; + } else if (IS_ENABLED(CONFIG_PAW3395_ORIENTATION_270)) { + data->x = -y; + data->y = -x; + } + } + + return err; +} + +static int paw3395_channel_get(const struct device *dev, enum sensor_channel chan, + struct sensor_value *val) +{ + struct paw3395_data *data = dev->data; + + if (unlikely(!data->ready)) { + LOG_DBG("Device is not initialized yet"); + return -EBUSY; + } + + switch (chan) { + case SENSOR_CHAN_POS_DX: + val->val1 = data->x; + val->val2 = 0; + break; + + case SENSOR_CHAN_POS_DY: + val->val1 = data->y; + val->val2 = 0; + break; + + default: + return -ENOTSUP; + } + + return 0; +} + +/* Setup the callback for actual trigger handling */ +// handler could be NULL, in which case the effect is disabling the interrupt line +// Thus it has dual function: +// 1. set up a handler callback +// 2. set up a flag (i.e., data_ready_handler) to indicate resuming the interrput line or not +// This feature is useful to pass the resuming of the interrupt to application +static int paw3395_trigger_set(const struct device *dev, + const struct sensor_trigger *trig, + sensor_trigger_handler_t handler) +{ + struct paw3395_data *data = dev->data; + const struct paw3395_config *config = dev->config; + int err; + + if (unlikely(trig->type != SENSOR_TRIG_DATA_READY)) { + return -ENOTSUP; + } + + if (unlikely(trig->chan != SENSOR_CHAN_ALL)) { + return -ENOTSUP; + } + + if (unlikely(!data->ready)) { + LOG_DBG("Device is not initialized yet"); + return -EBUSY; + } + + // spin lock is needed, so that the handler is not invoked before its pointer is assigned + // a valid value + k_spinlock_key_t key = k_spin_lock(&data->lock); + + // if non-NULL (a real handler defined), eanble the interrupt line + // otherwise, disable the interrupt line + if (handler) { + err = gpio_pin_interrupt_configure_dt(&config->irq_gpio, + GPIO_INT_LEVEL_ACTIVE); + } else { + err = gpio_pin_interrupt_configure_dt(&config->irq_gpio, + GPIO_INT_DISABLE); + } + + if (!err) { + data->data_ready_handler = handler; + } + + k_spin_unlock(&data->lock, key); + + return err; +} + +static int paw3395_attr_set(const struct device *dev, enum sensor_channel chan, + enum sensor_attribute attr, + const struct sensor_value *val) +{ + struct paw3395_data *data = dev->data; + int err; + + if (unlikely(chan != SENSOR_CHAN_ALL)) { + return -ENOTSUP; + } + + if (unlikely(!data->ready)) { + LOG_DBG("Device is not initialized yet"); + return -EBUSY; + } + + switch ((uint32_t)attr) { + case PAW3395_ATTR_CPI: + err = update_cpi(dev, PAW3395_SVALUE_TO_CPI(*val)); + break; + + case PAW3395_ATTR_REST_ENABLE: + err = toggle_rest_modes(dev, + PAW3395_REG_CONFIG2, + PAW3395_SVALUE_TO_BOOL(*val)); + break; + + case PAW3395_ATTR_RUN_DOWNSHIFT_TIME: + err = update_downshift_time(dev, + PAW3395_REG_RUN_DOWNSHIFT, + PAW3395_SVALUE_TO_TIME(*val)); + break; + + case PAW3395_ATTR_REST1_DOWNSHIFT_TIME: + err = update_downshift_time(dev, + PAW3395_REG_REST1_DOWNSHIFT, + PAW3395_SVALUE_TO_TIME(*val)); + break; + + case PAW3395_ATTR_REST2_DOWNSHIFT_TIME: + err = update_downshift_time(dev, + PAW3395_REG_REST2_DOWNSHIFT, + PAW3395_SVALUE_TO_TIME(*val)); + break; + + case PAW3395_ATTR_REST1_SAMPLE_TIME: + err = update_sample_time(dev, + PAW3395_REG_REST1_RATE_LOWER, + PAW3395_REG_REST1_RATE_UPPER, + PAW3395_SVALUE_TO_TIME(*val)); + break; + + case PAW3395_ATTR_REST2_SAMPLE_TIME: + err = update_sample_time(dev, + PAW3395_REG_REST2_RATE_LOWER, + PAW3395_REG_REST2_RATE_UPPER, + PAW3395_SVALUE_TO_TIME(*val)); + break; + + case PAW3395_ATTR_REST3_SAMPLE_TIME: + err = update_sample_time(dev, + PAW3395_REG_REST3_RATE_LOWER, + PAW3395_REG_REST3_RATE_UPPER, + PAW3395_SVALUE_TO_TIME(*val)); + break; + + default: + LOG_ERR("Unknown attribute"); + return -ENOTSUP; + } + + return err; +} + +static const struct sensor_driver_api paw3395_driver_api = { + .sample_fetch = paw3395_sample_fetch, + .channel_get = paw3395_channel_get, + .trigger_set = paw3395_trigger_set, + .attr_set = paw3395_attr_set, +}; + +#define PAW3395_DEFINE(n) \ + static struct paw3395_data data##n; \ + \ + static const struct paw3395_config config##n = { \ + .irq_gpio = GPIO_DT_SPEC_INST_GET(n, irq_gpios), \ + .bus = { \ + .bus = DEVICE_DT_GET(DT_INST_BUS(n)), \ + .config = { \ + .frequency = DT_INST_PROP(n, \ + spi_max_frequency), \ + .operation = SPI_WORD_SET(8) | \ + SPI_TRANSFER_MSB | \ + SPI_MODE_CPOL | SPI_MODE_CPHA, \ + .slave = DT_INST_REG_ADDR(n), \ + }, \ + }, \ + .cs_gpio = SPI_CS_GPIOS_DT_SPEC_GET(DT_DRV_INST(n)), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(n, paw3395_init, NULL, &data##n, &config##n, \ + POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \ + &paw3395_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(PAW3395_DEFINE) diff --git a/app/drivers/sensor/paw3395/paw3395.h b/app/drivers/sensor/paw3395/paw3395.h new file mode 100644 index 00000000000..ff100456147 --- /dev/null +++ b/app/drivers/sensor/paw3395/paw3395.h @@ -0,0 +1,121 @@ +#ifndef ZEPHYR_INCLUDE_PAW3395_H_ +#define ZEPHYR_INCLUDE_PAW3395_H_ + +#include +#include +#include +#include + +/** + * @file paw3395.h + * + * @brief Header file for the paw3395 driver. + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + // todo: update init steps + enum paw3395_init_step { + ASYNC_INIT_STEP_POWER_UP, // reset cs line and assert power-up reset + ASYNC_INIT_STEP_LOAD_SETTING, // load register setting + ASYNC_INIT_STEP_CONFIGURE, // set other registes like cpi and donwshift time (run, rest1, rest2) and clear motion registers + + ASYNC_INIT_STEP_COUNT // end flag + }; + + enum paw3395_run_mode { + HP_MODE, // high performance mode (the default mode using standard power-up register sets) + LP_MODE, // low power mode + OFFICE_MODE, // office mode (reduced to 10 ips, most power-efficient) + GAME_MODE, // corded game mode (with best performance and highest power consumption) + + RUN_MODE_COUNT // end flag + }; + +/* device data structure */ +struct paw3395_data { + const struct device *dev; + int16_t x; + int16_t y; + + // lock is needed to keep atomic of the trigger handler upadting + struct k_spinlock lock; + // motion interrupt isr + struct gpio_callback irq_gpio_cb; + // the actual trigger handler. This handler also used to flag whether resuming the motion interrupt line + sensor_trigger_handler_t data_ready_handler; + // the work structure holding the trigger handler job + struct k_work trigger_handler_work; + + // the work structure for delayable init steps + struct k_work_delayable init_work; + enum async_init_step async_init_step; + + // + bool ready; // whether init is finished successfully + bool last_read_burst; // todo: needed? + int err; // error code during async init + + /* the design of the driver is based on interrupt purely, to add polling upon it + the following work and timer maybe used in application code */ + struct k_work poll_work; + struct k_timer poll_timer; +}; + +struct paw3395_config { + struct spi_dt_spec bus; + struct gpio_dt_spec irq_gpio; + struct gpio_dt_spec cs_gpio; +}; + +/** + * @defgroup paw3395 PAW3395 motion sensor driver + * @{ + * @brief PAW3395 motion sensor driver. + */ + +/** @brief Sensor specific attributes of PAW3395. */ +enum paw3395_attribute { + /** Sensor CPI for both X and Y axes. */ + PAW3395_ATTR_CPI = SENSOR_ATTR_PRIV_START, + + /** Enable or disable sleep modes. */ + PAW3395_ATTR_REST_ENABLE, + + /** Entering time from Run mode to REST1 mode [ms]. */ + PAW3395_ATTR_RUN_DOWNSHIFT_TIME, + + /** Entering time from REST1 mode to REST2 mode [ms]. */ + PAW3395_ATTR_REST1_DOWNSHIFT_TIME, + + /** Entering time from REST2 mode to REST3 mode [ms]. */ + PAW3395_ATTR_REST2_DOWNSHIFT_TIME, + + /** Sampling frequency time during REST1 mode [ms]. */ + PAW3395_ATTR_REST1_SAMPLE_TIME, + + /** Sampling frequency time during REST2 mode [ms]. */ + PAW3395_ATTR_REST2_SAMPLE_TIME, + + /** Sampling frequency time during REST3 mode [ms]. */ + PAW3395_ATTR_REST3_SAMPLE_TIME, + + /** Select the running mode. */ + // todo: implement it + PAW3395_ATTR_RUNNING_MODE, +}; + + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* ZEPHYR_INCLUDE_PAW3395_H_ */ diff --git a/app/drivers/sensor/paw3395/paw3395_priv.c b/app/drivers/sensor/paw3395/paw3395_priv.c new file mode 100644 index 00000000000..edcbd2e635f --- /dev/null +++ b/app/drivers/sensor/paw3395/paw3395_priv.c @@ -0,0 +1,62 @@ +#include +#include +#include "paw3395.h" + +const size_t paw3395_pwrup_registers_length1 = 137; +const size_t paw3395_pwrup_registers_length2 = 5; +const size_t paw3395_runmode_registers_length[RUN_MODE_COUNT] = { + [HP_MODE] = 21, + [LP_MODE] = 21, + [OFFICE_MODE] = 21, + [GAME_MODE] = 20, +}; + +/* power up registers init: group1 */ +const uint8_t paw3395_pwrup_register_addr1[] = { +}; +const uint8_t paw3395_pwrup_register_data1[] = { +}; + +/* power up registers init: group2 */ +const uint8_t paw3395_pwrup_register_addr2[] = { +}; +const uint8_t paw3395_pwrup_register_data2[] = { +}; + +/* hp mode registers */ +const uint8_t paw3395_hp_register_addr[] = { +}; +const uint8_t paw3395_hp_register_data[] = { +}; + +/* lp mode registers */ +const uint8_t paw3395_lp_register_addr[] = { +}; +const uint8_t paw3395_lp_register_data[] = { +}; + +/* office mode registers */ +const uint8_t paw3395_office_register_addr[] = { +}; +const uint8_t paw3395_office_register_data[] = { +}; + +/* game mode registers */ +const uint8_t paw3395_game_register_addr[] = { +}; +const uint8_t paw3395_game_register_data[] = { +}; + +/* aggregation of all run modes registers */ +const uint8_t* paw3395_rumode_register_addr[] = { + [HP_MODE] = paw3395_hp_register_addr, + [LP_MODE] = paw3395_lp_register_addr, + [OFFICE_MODE] = paw3395_office_register_addr, + [GAME_MODE] = paw3395_game_register_addr, +}; +const uint8_t* paw3395_rumode_register_data[] = { + [HP_MODE] = paw3395_hp_register_data, + [LP_MODE] = paw3395_lp_register_data, + [OFFICE_MODE] = paw3395_office_register_data, + [GAME_MODE] = paw3395_game_register_data, +}; diff --git a/app/drivers/zephyr/dts/bindings/sensor/pixart,paw3395.yaml b/app/drivers/zephyr/dts/bindings/sensor/pixart,paw3395.yaml new file mode 100644 index 00000000000..ad0994a54af --- /dev/null +++ b/app/drivers/zephyr/dts/bindings/sensor/pixart,paw3395.yaml @@ -0,0 +1,20 @@ +# Copyright (c) 2022 The ZMK Contributors +# SPDX-License-Identifier: MIT +description: | + Sensor driver for the pixart PAW3395 optical mouse sensor + +compatible: "pixart,paw3395" + +include: spi-device.yaml + +properties: + label: + type: string + required: true + irq-gpios: + type: phandle-array + required: true + scroll-layer: + type: int + description: the momentary layer used to modifiy move to scroll + required: false From a9cacc58f6544e0f57d331804b3417582abc7751 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Fri, 2 Sep 2022 05:29:37 +0800 Subject: [PATCH 070/157] [bugfxi-pmw33xx] use k_busy_wait instead of k_sleep for spi bus related timing --- app/drivers/sensor/pmw33xx/pmw33xx.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/drivers/sensor/pmw33xx/pmw33xx.c b/app/drivers/sensor/pmw33xx/pmw33xx.c index 63c9ce40ccc..0994c9e2e2a 100644 --- a/app/drivers/sensor/pmw33xx/pmw33xx.c +++ b/app/drivers/sensor/pmw33xx/pmw33xx.c @@ -76,7 +76,7 @@ static int pmw33xx_access(const struct device *dev, const uint8_t reg, uint8_t * int err = spi_write(data->bus, spi_cfg, &tx); /* k_sleep(K_USEC(120)); // Tsrad */ - k_sleep(K_USEC(180)); // Tsrad + k_busy_wait(180); // Tsrad if (err) { pmw33xx_cs_select(cs_gpio_cfg, 1); return err; @@ -88,7 +88,7 @@ static int pmw33xx_access(const struct device *dev, const uint8_t reg, uint8_t * err = spi_read(data->bus, spi_cfg, &rx); pmw33xx_cs_select(cs_gpio_cfg, 1); /* k_sleep(K_USEC(160)); */ - k_sleep(K_USEC(180)); + k_busy_wait(180); if ((reg & PMW33XX_WR_MASK) == 0 && value != NULL) *value = result[0]; return err; @@ -122,7 +122,7 @@ static int pmw33xx_write_srom(const struct device *dev) { // 2. downlaod init cmd pmw33xx_write_reg(dev, PMW33XX_REG_SROM_EN, PMW33XX_SROM_DWNLD_CMD); - k_sleep(K_USEC(15)); + k_busy_wait(15); // 3. download start cmd pmw33xx_write_reg(dev, PMW33XX_REG_SROM_EN, PMW33XX_SROM_DWNLD_START_CMD); @@ -132,7 +132,7 @@ static int pmw33xx_write_srom(const struct device *dev) { int err = spi_write(data->bus, spi_cfg, &tx); - k_sleep(K_USEC(15)); + k_busy_wait(15); if (err) { pmw33xx_cs_select(cs_gpio_cfg, 1); return err; @@ -141,7 +141,7 @@ static int pmw33xx_write_srom(const struct device *dev) { for (uint16_t i = 0; i < sizeof(SROM); i++) { access[0] = SROM[i]; err = spi_write(data->bus, spi_cfg, &tx); - k_sleep(K_USEC(15)); + k_busy_wait(15); if (err) { pmw33xx_cs_select(cs_gpio_cfg, 1); return err; @@ -151,7 +151,7 @@ static int pmw33xx_write_srom(const struct device *dev) { pmw33xx_cs_select(cs_gpio_cfg, 1); // 5. finish and exit - k_sleep(K_MSEC(2)); // Tbexit + k_busy_wait(2000); // Tbexit return err; } @@ -188,7 +188,7 @@ static int pmw33xx_read_motion_burst(const struct device *dev, struct pmw33xx_mo } // wait needed - k_sleep(K_USEC(35)); // tsrad motbr + k_busy_wait(35); // tsrad motbr // start reading continuosly err = spi_read(data->bus, spi_cfg, &rx); @@ -316,7 +316,7 @@ static int pmw33xx_init_chip(const struct device *dev) { // reset spi port pmw33xx_cs_select(cs_gpio_cfg, 1); pmw33xx_cs_select(cs_gpio_cfg, 0); - /* k_sleep(K_MSEC(1)); */ + k_sleep(K_MSEC(1)); // power reset int err = pmw33xx_write_reg(dev, PMW33XX_REG_PWR_UP_RST, PMW33XX_RESET_CMD); From 80122b85b5a51ca4c6705b23bb30ecf8110622c4 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Fri, 2 Sep 2022 06:07:04 +0800 Subject: [PATCH 071/157] [paw3395] add register arrays --- app/drivers/sensor/paw3395/paw3395.c | 4 +- app/drivers/sensor/paw3395/paw3395_priv.c | 66 ++++++++++++++++++++--- 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/app/drivers/sensor/paw3395/paw3395.c b/app/drivers/sensor/paw3395/paw3395.c index b31d820b6e0..7037b67e8ad 100644 --- a/app/drivers/sensor/paw3395/paw3395.c +++ b/app/drivers/sensor/paw3395/paw3395.c @@ -13,7 +13,7 @@ #include LOG_MODULE_REGISTER(paw3395, CONFIG_PAW3395_LOG_LEVEL); -/* Timings defined by spec (in us) */ +/* Timings defined by spec (in us), they are used in SPI communication and MCU should not do other tasks during waiting, thus using k_busy_wain instead of k_sleep */ // sub-us time is rounded to us, due to the limitation of k_busy_wait // see the discusssion here: https://github.com/zephyrproject-rtos/zephyr/issues/6498 #define T_NCS_SCLK 1 /* 120 ns (rounded to 1us) */ @@ -113,7 +113,7 @@ extern const uint8_t paw3395_firmware_data[]; // init is done in non-blocking manner (i.e., async), a delayable work is defined for this job // see paw3395_init and paw3395_async_init) -// delay (ms) in between steps +// delay (ms) in between steps, MCU is allowed to do other tasks static const int32_t async_init_delay[ASYNC_INIT_STEP_COUNT] = { [ASYNC_INIT_STEP_POWER_UP] = 50, // required in spec [ASYNC_INIT_STEP_LOAD_SETTING] = 5, // required in spec diff --git a/app/drivers/sensor/paw3395/paw3395_priv.c b/app/drivers/sensor/paw3395/paw3395_priv.c index edcbd2e635f..b70343254d1 100644 --- a/app/drivers/sensor/paw3395/paw3395_priv.c +++ b/app/drivers/sensor/paw3395/paw3395_priv.c @@ -2,49 +2,103 @@ #include #include "paw3395.h" +///////// Init setting registers ////////////////////// const size_t paw3395_pwrup_registers_length1 = 137; const size_t paw3395_pwrup_registers_length2 = 5; -const size_t paw3395_runmode_registers_length[RUN_MODE_COUNT] = { - [HP_MODE] = 21, - [LP_MODE] = 21, - [OFFICE_MODE] = 21, - [GAME_MODE] = 20, -}; /* power up registers init: group1 */ const uint8_t paw3395_pwrup_register_addr1[] = { + 0x7F, 0x40, 0x7F, 0x40, 0x7F, 0x55, 0x56, 0x57, 0x58, 0x7F, + 0x42, 0x43, 0x4B, 0x4D, 0x53, 0x7F, 0x44, 0x4D, 0x51, 0x53, + 0x55, 0x5A, 0x5B, 0x61, 0x62, 0x6D, 0x6E, 0x70, 0x4A, 0x60, + 0x7F, 0x6C, 0x6D, 0x6E, 0x53, 0x55, 0x7A, 0x7D, 0x7F, 0x41, + 0x42, 0x43, 0x7F, 0x71, 0x7F, 0x62, 0x63, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6A, 0x6C, 0x6D, 0x51, 0x53, 0x54, 0x71, 0x72, + 0x73, 0x7F, 0x4A, 0x4C, 0x55, 0x7F, 0x4B, 0x4C, 0x61, 0x62, + 0x63, 0x7F, 0x4C, 0x56, 0x41, 0x4D, 0x7F, 0x4A, 0x4B, 0x4C, + 0x41, 0x55, 0x56, 0x49, 0x42, 0x43, 0x44, 0x54, 0x5A, 0x5F, + 0x5B, 0x5E, 0x7F, 0x48, 0x4F, 0x52, 0x51, 0x54, 0x53, 0x56, + 0x55, 0x58, 0x57, 0x5A, 0x5B, 0x5C, 0x5D, 0x71, 0x70, 0x73, + 0x72, 0x75, 0x74, 0x77, 0x76, 0x7F, 0x4C, 0x7F, 0x4F, 0x4E, + 0x52, 0x51, 0x54, 0x5A, 0x77, 0x47, 0x5B, 0x64, 0x65, 0x66, + 0x67, 0x78, 0x79, 0x40, 0x55, 0x23, 0x22 }; const uint8_t paw3395_pwrup_register_data1[] = { + 0x07, 0x41, 0x00, 0x80, 0x0E, 0x0D, 0x1B, 0xE8, 0xD5, 0x14, + 0xBC, 0x74, 0x20, 0x00, 0x0E, 0x05, 0x04, 0x06, 0x40, 0x40, + 0xCA, 0xE8, 0xEA, 0x31, 0x64, 0xB8, 0x0F, 0x02, 0x2A, 0x26, + 0x06, 0x70, 0x60, 0x04, 0x02, 0x11, 0x01, 0x51, 0x07, 0x10, + 0x32, 0x00, 0x08, 0x4F, 0x09, 0x1F, 0x1F, 0x03, 0x03, 0x1F, + 0x1F, 0x03, 0x03, 0x1F, 0x1F, 0x04, 0x20, 0x20, 0x0C, 0x07, + 0x07, 0x0A, 0x14, 0x14, 0x19, 0x14, 0x30, 0x03, 0x0B, 0x0A, + 0x02, 0x15, 0x02, 0x02, 0x91, 0x0A, 0x0C, 0x10, 0x0C, 0x40, + 0x25, 0x18, 0x14, 0x0A, 0x00, 0x2D, 0x0C, 0x1A, 0x0D, 0x1E, + 0x05, 0x0F, 0x0D, 0xDD, 0x03, 0x49, 0x00, 0x5B, 0x00, 0x64, + 0x00, 0xA5, 0x02, 0x29, 0x47, 0x81, 0x40, 0xDC, 0x07, 0x00, + 0x08, 0xDC, 0x07, 0x00, 0x08, 0x10, 0xD0, 0x00, 0x63, 0x00, + 0x63, 0x00, 0x54, 0x10, 0x4F, 0x01, 0x40, 0x60, 0x06, 0x13, + 0x0F, 0x01, 0x9C, 0x00, 0x02, 0x70, 0x01 }; /* power up registers init: group2 */ const uint8_t paw3395_pwrup_register_addr2[] = { + 0x22, 0x55, 0x7F, 0x40, 0x7F }; const uint8_t paw3395_pwrup_register_data2[] = { + 0x00, 0x00, 0x07, 0x40, 0x00 }; +///////// Run mode registers ////////////////////// +const size_t paw3395_runmode_registers_length[RUN_MODE_COUNT] = { + [HP_MODE] = 21, + [LP_MODE] = 21, + [OFFICE_MODE] = 21, + [GAME_MODE] = 20, +}; /* hp mode registers */ const uint8_t paw3395_hp_register_addr[] = { + 0x7F, 0x51, 0x53, 0x61, 0x6E, 0x7F, 0x42, 0x43, 0x7F, 0x51, + 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x7F, 0x54, 0x78, + 0x79 }; const uint8_t paw3395_hp_register_data[] = { + 0x05, 0x40, 0x40, 0x31, 0x0F, 0x07, 0x32, 0x00, 0x0D, 0x00, + 0x49, 0x00, 0x5B, 0x00, 0x64, 0x02, 0xA5, 0x00, 0x54, 0x01, + 0x9C }; /* lp mode registers */ const uint8_t paw3395_lp_register_addr[] = { + 0x7F, 0x51, 0x53, 0x61, 0x6E, 0x7F, 0x42, 0x43, 0x7F, 0x51, + 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x7F, 0x54, 0x78, + 0x79 }; const uint8_t paw3395_lp_register_data[] = { + 0x05, 0x40, 0x40, 0x3B, 0x1F, 0x07, 0x32, 0x00, 0x0D, 0x00, + 0x49, 0x00, 0x5B, 0x00, 0x64, 0x02, 0xA5, 0x00, 0x54, 0x01, + 0x9C }; /* office mode registers */ const uint8_t paw3395_office_register_addr[] = { + 0x7F, 0x51, 0x53, 0x61, 0x6E, 0x7F, 0x42, 0x43, 0x7F, 0x51, + 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x7F, 0x54, 0x78, + 0x79 }; const uint8_t paw3395_office_register_data[] = { + 0x05, 0x28, 0x30, 0x3B, 0x1F, 0x07, 0x32, 0x00, 0x0D, 0x00, + 0x49, 0x00, 0x5B, 0x00, 0x64, 0x02, 0xA5, 0x00, 0x52, 0x0A, + 0x0F }; /* game mode registers */ const uint8_t paw3395_game_register_addr[] = { + 0x7F, 0x51, 0x53, 0x61, 0x6E, 0x7F, 0x42, 0x43, 0x7F, 0x51, + 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x7F, 0x54, 0x40 }; const uint8_t paw3395_game_register_data[] = { + 0x05, 0x40, 0x40, 0x31, 0x0F, 0x07, 0x2F, 0x00, 0x0D, 0x12, + 0xDB, 0x12, 0xDC, 0x12, 0xEA, 0x15, 0x2D, 0x00, 0x55, 0x83 }; /* aggregation of all run modes registers */ From 3cb2cfa73afc8d53d6c2e7fcad79c3cabf8cb596 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Fri, 2 Sep 2022 10:29:30 +0800 Subject: [PATCH 072/157] [bugfix-pmw33xx] use v4 srom from nrf desktop sdk --- app/drivers/sensor/pmw33xx/pmw3360_srom.h | 548 +++++++++++----------- 1 file changed, 274 insertions(+), 274 deletions(-) diff --git a/app/drivers/sensor/pmw33xx/pmw3360_srom.h b/app/drivers/sensor/pmw33xx/pmw3360_srom.h index 8a27e611fd3..f0290128386 100644 --- a/app/drivers/sensor/pmw33xx/pmw3360_srom.h +++ b/app/drivers/sensor/pmw33xx/pmw3360_srom.h @@ -13,282 +13,282 @@ SROM OR THE USE OR OTHER DEALINGS IN THIS SROM. #define ZEPHYR_DRIVERS_SENSOR_PIXART_PMW33XX_PMW3360_SROM_H_ // firmware vesion id (the second byte in srom hex array) -#define PMW3360_FIRMWARE_ID 0x05 +#define PMW3360_FIRMWARE_ID 0x04 #include static const uint8_t SROM[] = { - 0x01, 0x05, 0x8d, 0x92, 0x66, 0x67, 0x1e, 0xbe, 0xfe, 0x5f, 0x1d, 0xb8, 0xf2, 0x66, 0x4e, - 0xff, 0x5d, 0x38, 0xf2, 0x46, 0x0c, 0x98, 0x97, 0x8d, 0x79, 0x51, 0x20, 0xc2, 0x06, 0x8e, - 0x9e, 0xbe, 0xfe, 0x7e, 0x5f, 0x1d, 0xb8, 0xd3, 0x24, 0xca, 0x16, 0x8f, 0x7d, 0x78, 0x53, - 0x05, 0x88, 0x73, 0x45, 0xe9, 0x55, 0x22, 0xa7, 0xae, 0xd8, 0x32, 0xe6, 0x2f, 0xbd, 0xf8, - 0x72, 0x47, 0x0c, 0x7b, 0x74, 0x6a, 0x56, 0x2e, 0xbf, 0xdd, 0x38, 0xd5, 0x20, 0xca, 0x16, - 0xae, 0xde, 0x1f, 0x9d, 0xb8, 0xf2, 0x47, 0x0c, 0x7b, 0x55, 0x28, 0xd2, 0x26, 0xaf, 0xdc, - 0x3a, 0xd7, 0x2c, 0xbb, 0xd5, 0x09, 0x90, 0xa2, 0xa7, 0xcc, 0xfb, 0x74, 0x4b, 0xf5, 0x49, - 0x10, 0x83, 0x84, 0x8a, 0x77, 0x4d, 0x18, 0xb2, 0xe6, 0x4e, 0x1e, 0x9f, 0xbc, 0xfa, 0x6a, - 0xea, 0xab, 0x55, 0x91, 0xa9, 0xda, 0x58, 0xa9, 0xe1, 0x1a, 0xc2, 0x52, 0x1c, 0x0d, 0xef, - 0x50, 0x71, 0x04, 0x21, 0x93, 0x87, 0x0e, 0xb1, 0xa9, 0x4a, 0xc0, 0x24, 0xaf, 0xe3, 0x26, - 0xa2, 0x8d, 0xa1, 0x6d, 0xf2, 0x88, 0x9e, 0x99, 0xbb, 0xf2, 0x62, 0x38, 0xfe, 0x78, 0x1d, - 0x58, 0x19, 0x9b, 0xbe, 0x10, 0x6c, 0x15, 0x9e, 0x50, 0x8b, 0x99, 0x40, 0x92, 0x01, 0x38, - 0xb6, 0x97, 0x83, 0x51, 0x59, 0xa6, 0xb7, 0xc9, 0x48, 0xfd, 0x68, 0x21, 0x78, 0xf4, 0x2e, - 0x86, 0x61, 0xb0, 0xb2, 0x2b, 0x5b, 0xf0, 0x1b, 0x71, 0xa3, 0x0a, 0xd5, 0xd6, 0x30, 0x7a, - 0x0c, 0xf8, 0xaf, 0xc6, 0x54, 0x6b, 0x89, 0x30, 0x58, 0xe8, 0xe2, 0x0d, 0x39, 0x2f, 0x47, - 0xa0, 0x41, 0x74, 0x5d, 0xd6, 0xf3, 0x85, 0xb7, 0x66, 0x59, 0x51, 0x74, 0x3d, 0xfe, 0xee, - 0x3a, 0x1e, 0x2a, 0x5f, 0x93, 0x0c, 0x3f, 0xd4, 0x4d, 0x24, 0x13, 0x2c, 0xf2, 0x7b, 0x4d, - 0x06, 0x60, 0x2e, 0x39, 0xa9, 0xd4, 0x72, 0x36, 0xb6, 0x4d, 0xea, 0x8c, 0xd9, 0xa7, 0xbc, - 0x38, 0xe7, 0xbb, 0xb7, 0x7b, 0x04, 0x08, 0x83, 0x5a, 0x27, 0x6f, 0x42, 0xc6, 0x7e, 0xbd, - 0x97, 0x0e, 0x55, 0xdc, 0x79, 0x63, 0xd8, 0x89, 0xc1, 0x83, 0x3d, 0x34, 0x01, 0x76, 0xff, - 0xde, 0x7f, 0x65, 0x4b, 0xb3, 0x46, 0x56, 0x8d, 0x9e, 0x6f, 0x1f, 0x2b, 0xa4, 0x69, 0x54, - 0xde, 0x1c, 0x2d, 0xa8, 0x51, 0x7e, 0xdf, 0xbb, 0x18, 0x90, 0x27, 0xaf, 0x79, 0xd1, 0x86, - 0x4f, 0x44, 0x88, 0x7f, 0xb9, 0x80, 0x21, 0xa9, 0x5d, 0x7b, 0xd4, 0x81, 0x21, 0xa9, 0x13, - 0x59, 0x72, 0x58, 0x11, 0xe3, 0xf2, 0xf7, 0xae, 0xc3, 0xc2, 0x64, 0x1d, 0xfb, 0xd8, 0x91, - 0x2d, 0x13, 0xb5, 0x0e, 0x67, 0xdb, 0xbc, 0x63, 0xde, 0xa0, 0x86, 0x49, 0x6c, 0x40, 0x82, - 0x82, 0x3f, 0xeb, 0x38, 0x22, 0xa8, 0x20, 0x1e, 0xc5, 0x5f, 0x25, 0xea, 0xaf, 0xe6, 0x78, - 0x1c, 0x41, 0x0b, 0x5a, 0x94, 0xcc, 0x8d, 0x0d, 0xc5, 0xb8, 0x9c, 0x1a, 0x9f, 0x6d, 0xa8, - 0x57, 0xe9, 0x1a, 0xf0, 0xad, 0x6c, 0x26, 0x5f, 0xb8, 0x38, 0x16, 0x0e, 0x28, 0xd6, 0x76, - 0x5a, 0xc2, 0xb7, 0x6a, 0x9e, 0x26, 0x9d, 0x3e, 0x1f, 0x25, 0xb0, 0x14, 0x46, 0xcd, 0x67, - 0x49, 0x93, 0x13, 0x81, 0x01, 0x46, 0x72, 0x31, 0x42, 0x91, 0x87, 0x6c, 0x1c, 0xb8, 0x08, - 0x72, 0x6a, 0x33, 0x64, 0xed, 0xc0, 0x99, 0xb9, 0xf6, 0x34, 0xe9, 0xa5, 0x6c, 0x57, 0x9a, - 0x72, 0x18, 0x48, 0x62, 0xc1, 0x88, 0xcf, 0xa3, 0x50, 0x53, 0x50, 0xdf, 0xc8, 0x9f, 0xf0, - 0xb6, 0xa2, 0x23, 0xc4, 0xaa, 0xe3, 0xd0, 0xe2, 0xab, 0x76, 0xc8, 0x4a, 0x02, 0x21, 0xdd, - 0xf3, 0x59, 0xdc, 0x30, 0x6e, 0xb5, 0x42, 0xe2, 0x9d, 0x8a, 0xdf, 0xfd, 0xa5, 0x41, 0x75, - 0x01, 0x83, 0xbe, 0x47, 0xa5, 0x21, 0x30, 0x3b, 0x0e, 0x33, 0x2e, 0x74, 0xaf, 0x27, 0x90, - 0xf4, 0x20, 0xa7, 0x1b, 0xff, 0x77, 0x01, 0xab, 0x79, 0x0a, 0xfb, 0x86, 0x1e, 0x1f, 0x22, - 0x6d, 0x75, 0x75, 0xfc, 0xe0, 0x8c, 0xa2, 0x72, 0x0f, 0x45, 0x48, 0xf9, 0x43, 0x10, 0xf0, - 0x57, 0x15, 0x17, 0x74, 0x40, 0x8f, 0x77, 0x69, 0x8a, 0xd4, 0x0f, 0x25, 0x27, 0x0a, 0xe8, - 0x84, 0x73, 0x41, 0xb9, 0x5f, 0xb6, 0xc8, 0x2f, 0x02, 0x83, 0xdc, 0x1e, 0x1c, 0xdb, 0x3f, - 0x2d, 0x58, 0xf7, 0x5e, 0xfd, 0x6f, 0xda, 0xad, 0xdc, 0x43, 0x01, 0x22, 0xc6, 0x45, 0x9c, - 0xec, 0x56, 0x8a, 0x1a, 0x4e, 0xaa, 0xae, 0x3a, 0xa1, 0xf3, 0xf1, 0x9a, 0x97, 0x8d, 0x79, - 0x51, 0x01, 0xf5, 0x31, 0x04, 0xcd, 0x2a, 0xbc, 0xfd, 0xac, 0x59, 0x1b, 0x0a, 0xe2, 0x26, - 0xdd, 0x2f, 0x96, 0xaa, 0x62, 0xc5, 0x6d, 0xb8, 0xe1, 0x72, 0x4e, 0x4e, 0xb7, 0x4f, 0x9f, - 0xaa, 0x33, 0x75, 0x8b, 0x43, 0x44, 0x0a, 0xc3, 0x0a, 0x5e, 0x9c, 0xdb, 0x1e, 0x4e, 0xbd, - 0x0f, 0xec, 0x09, 0x96, 0x5a, 0x37, 0x6d, 0x10, 0x44, 0xd4, 0x91, 0xc0, 0xc3, 0x0d, 0x35, - 0x62, 0xcf, 0x0a, 0xb1, 0x1a, 0x8a, 0xda, 0xa8, 0xcf, 0xbc, 0x3a, 0xb2, 0x4f, 0x59, 0x29, - 0xd9, 0x91, 0x96, 0x0d, 0x6b, 0x9c, 0xcf, 0x34, 0x5b, 0x76, 0x7b, 0xb2, 0xb2, 0xce, 0xbf, - 0x72, 0xd9, 0x96, 0x4a, 0xc0, 0x2d, 0x89, 0x33, 0xd0, 0xe4, 0xbe, 0x69, 0x7f, 0x1c, 0x9e, - 0x71, 0x19, 0x12, 0xf6, 0x74, 0xfb, 0xd6, 0x41, 0x95, 0x69, 0xf0, 0xe9, 0xa7, 0x50, 0x82, - 0x04, 0x78, 0x4d, 0x99, 0xb3, 0xa7, 0xfe, 0xee, 0x7f, 0xed, 0x56, 0x57, 0x57, 0xe9, 0xd3, - 0x6a, 0x43, 0xf4, 0x0b, 0x6d, 0xba, 0x34, 0xde, 0xf8, 0x4c, 0x45, 0xae, 0x00, 0x32, 0x1a, - 0x4e, 0xff, 0x54, 0x60, 0x6a, 0xd9, 0x59, 0x18, 0xd3, 0xbe, 0x5a, 0x94, 0x16, 0x9d, 0xec, - 0xdf, 0xb4, 0x48, 0x8e, 0x6f, 0x19, 0xf8, 0xe6, 0xec, 0x7f, 0xb7, 0x49, 0x92, 0xb3, 0xb6, - 0xec, 0x7e, 0xdc, 0x08, 0x3b, 0xe5, 0x5b, 0x88, 0xf9, 0x92, 0xf3, 0x7c, 0x9d, 0x7c, 0x22, - 0xf2, 0x5d, 0x7d, 0x76, 0x07, 0x2e, 0x42, 0xd6, 0x9e, 0x5a, 0xbc, 0x7d, 0xfd, 0xb5, 0x92, - 0x4a, 0x7f, 0xbf, 0xac, 0xc0, 0xb6, 0x18, 0x8b, 0x02, 0x71, 0xae, 0xc7, 0xa8, 0x8c, 0x19, - 0xc1, 0x0a, 0x36, 0x3e, 0xa4, 0x2d, 0xa6, 0xbb, 0xe8, 0x77, 0x34, 0x45, 0xca, 0x60, 0x52, - 0xf0, 0x26, 0xf7, 0x58, 0x68, 0x34, 0x28, 0xb5, 0x29, 0xe5, 0xb8, 0x65, 0x0f, 0xc8, 0xc2, - 0x64, 0x50, 0x91, 0x12, 0xa1, 0x7b, 0xd7, 0x09, 0x74, 0x5f, 0xcc, 0xac, 0x9d, 0xac, 0x0a, - 0xd1, 0x46, 0xbe, 0xc2, 0x2d, 0xa6, 0x67, 0x69, 0x69, 0x55, 0x8a, 0xbd, 0x67, 0xd9, 0x31, - 0xc7, 0x80, 0x7c, 0xd5, 0xb2, 0xbe, 0x83, 0x17, 0x7a, 0x2e, 0xf4, 0x3e, 0x14, 0xa5, 0x62, - 0x77, 0xa5, 0xdb, 0x77, 0x58, 0x35, 0x5f, 0xc4, 0x6c, 0xd2, 0xf2, 0x38, 0x91, 0x74, 0x80, - 0x95, 0x52, 0xd2, 0xaf, 0xe2, 0x1b, 0x12, 0xfe, 0x4b, 0x5f, 0x84, 0x5f, 0x7d, 0x7e, 0x0b, - 0xd9, 0x42, 0x72, 0xf1, 0x32, 0x1d, 0x8d, 0x5c, 0xcb, 0x20, 0x05, 0x4f, 0xbe, 0xbf, 0x40, - 0xe9, 0xa6, 0x9f, 0xff, 0x05, 0xe1, 0x73, 0x52, 0x65, 0xb4, 0x59, 0x45, 0x04, 0x10, 0xe1, - 0xdc, 0x0b, 0x30, 0xaa, 0x23, 0x4d, 0xb9, 0x58, 0xe5, 0xf1, 0xa3, 0x9c, 0xf2, 0xf7, 0xaf, - 0xbc, 0xe3, 0xa7, 0x0d, 0x68, 0x97, 0x6c, 0x2b, 0x33, 0x60, 0xe3, 0xce, 0x7c, 0xda, 0xd1, - 0x2d, 0x78, 0x52, 0x10, 0xa0, 0xf7, 0x9a, 0xf4, 0x79, 0xdb, 0xa5, 0x2a, 0x71, 0xc6, 0xcf, - 0xa0, 0x68, 0xd0, 0x32, 0x2a, 0xd3, 0xb2, 0xe8, 0x03, 0x27, 0xbe, 0x55, 0xfd, 0x0e, 0x90, - 0xa2, 0x76, 0x8c, 0xdc, 0xbd, 0x69, 0x9c, 0xb6, 0x18, 0x92, 0xe3, 0xb3, 0x23, 0x1d, 0x3c, - 0x85, 0x2a, 0x2b, 0x04, 0x3a, 0xf5, 0xee, 0xf8, 0xd2, 0xdd, 0x6d, 0x52, 0xa2, 0x45, 0x75, - 0xd9, 0xa1, 0x03, 0x23, 0x83, 0x24, 0x04, 0xe7, 0xee, 0xa2, 0x14, 0x4e, 0xa9, 0xde, 0x4e, - 0x9d, 0x24, 0x1a, 0x63, 0xd3, 0x4b, 0xf7, 0x71, 0xc1, 0xe7, 0xfb, 0x48, 0xa4, 0x7c, 0x39, - 0xcb, 0x80, 0xdd, 0x5e, 0xaf, 0x0d, 0xca, 0xef, 0x58, 0x17, 0xf4, 0xca, 0x0c, 0x00, 0x30, - 0x68, 0x3e, 0x5d, 0x9b, 0xc3, 0x19, 0x27, 0x7a, 0x35, 0x32, 0x5c, 0x7b, 0x92, 0x16, 0x7f, - 0x6e, 0x86, 0x6b, 0x50, 0x7a, 0xd6, 0x15, 0x10, 0x16, 0xa8, 0x28, 0xb1, 0xa1, 0xd8, 0xda, - 0xc8, 0xfd, 0x5b, 0xe2, 0xc0, 0xd6, 0x8d, 0x75, 0xbd, 0x1c, 0x0b, 0x27, 0xfe, 0x1d, 0x09, - 0x23, 0x13, 0x4f, 0x5a, 0x5a, 0xf8, 0xbe, 0x5d, 0x31, 0x8a, 0x81, 0xaf, 0xbe, 0xed, 0x00, - 0x43, 0x14, 0x8f, 0xa5, 0x4a, 0xfe, 0x90, 0xe1, 0x67, 0x25, 0x29, 0x52, 0x2f, 0x26, 0x17, - 0xef, 0x6e, 0xe6, 0x1e, 0xaf, 0x7f, 0x4a, 0x40, 0x15, 0xc7, 0x6e, 0x8d, 0x60, 0x92, 0xd6, - 0x8d, 0x95, 0xca, 0xff, 0x8e, 0xfe, 0x4b, 0x8d, 0x1a, 0x83, 0x40, 0xe6, 0xb7, 0xbc, 0x6b, - 0xd6, 0x39, 0x53, 0xd0, 0x4a, 0xe6, 0x1f, 0x1f, 0xb1, 0x16, 0x6a, 0xae, 0x6a, 0x6d, 0xe3, - 0xe6, 0xed, 0x4e, 0xff, 0x49, 0x28, 0xc7, 0xaf, 0xb4, 0x14, 0x9f, 0x85, 0x9f, 0xfe, 0x6a, - 0x92, 0x93, 0x9c, 0x6f, 0xff, 0x4a, 0xd4, 0x3e, 0x26, 0xd9, 0x72, 0x4f, 0x01, 0xe2, 0x54, - 0x06, 0xc6, 0x7d, 0xb8, 0xef, 0x0b, 0x04, 0x56, 0x3a, 0x51, 0x41, 0xe2, 0x1d, 0xc2, 0x96, - 0x95, 0xd1, 0x66, 0x16, 0x3b, 0x50, 0x36, 0x3f, 0x2e, 0xf2, 0xb2, 0x25, 0x2d, 0x59, 0x0f, - 0x23, 0xf2, 0x9d, 0xb0, 0xee, 0x59, 0x89, 0xd0, 0x79, 0x30, 0xc7, 0x54, 0x9b, 0xec, 0xf8, - 0xd7, 0xdc, 0x5a, 0x11, 0x1d, 0xfb, 0xf0, 0x82, 0x33, 0x3c, 0xff, 0x17, 0xff, 0xfc, 0x92, - 0xc6, 0x6f, 0x3d, 0x5d, 0x99, 0x85, 0x2a, 0xa1, 0x04, 0xe8, 0x21, 0xed, 0x12, 0x2b, 0x7d, - 0xbc, 0xa3, 0x36, 0x9b, 0x9f, 0xfd, 0x5d, 0x60, 0x66, 0xe9, 0x39, 0x5e, 0x34, 0x29, 0xa7, - 0x08, 0x56, 0xf7, 0x4d, 0x19, 0x8d, 0x91, 0xff, 0xc8, 0x8a, 0xe7, 0xe8, 0x22, 0x00, 0x35, - 0x68, 0x34, 0x93, 0xe7, 0x3b, 0x41, 0xbb, 0xf6, 0x58, 0x2f, 0x10, 0x66, 0x77, 0x69, 0xcd, - 0x94, 0xb9, 0xbe, 0x59, 0x73, 0x4f, 0xc3, 0x11, 0xa1, 0xb3, 0x13, 0xe2, 0x82, 0xf1, 0x05, - 0xcb, 0xa7, 0x75, 0xf6, 0x01, 0xb2, 0x24, 0xa9, 0x59, 0x05, 0x59, 0x46, 0xc9, 0x25, 0x39, - 0x36, 0xee, 0x48, 0x90, 0x83, 0x89, 0xc4, 0xdb, 0xa3, 0x83, 0xd0, 0xd3, 0x62, 0x3f, 0x5e, - 0x0e, 0x0c, 0xb7, 0x77, 0xb3, 0xd6, 0xbb, 0x37, 0x70, 0xb2, 0xb3, 0x87, 0x60, 0xd7, 0x8a, - 0xe2, 0x99, 0xa9, 0x0f, 0x18, 0x16, 0xf6, 0xcd, 0x14, 0x4e, 0x2c, 0x7e, 0xfe, 0xf9, 0xa4, - 0x48, 0xfb, 0xb9, 0x60, 0x13, 0x01, 0xd2, 0x6e, 0xdc, 0x3f, 0xe7, 0x2d, 0x7f, 0xe1, 0x11, - 0x96, 0x12, 0x9a, 0x90, 0x48, 0x2b, 0x76, 0x78, 0xa6, 0xdd, 0x67, 0x48, 0x68, 0x42, 0xfd, - 0x74, 0xb6, 0x51, 0xd7, 0x99, 0x07, 0x7d, 0x8b, 0x02, 0xd6, 0x68, 0x36, 0x97, 0x6d, 0x49, - 0x28, 0xbf, 0xcc, 0x23, 0x8f, 0xed, 0x78, 0x4c, 0x49, 0xb0, 0xd1, 0x9a, 0x3d, 0x1c, 0xdd, - 0xf0, 0xeb, 0xd5, 0xd4, 0xc2, 0x12, 0xae, 0x5d, 0x00, 0xe0, 0xf4, 0x84, 0x10, 0x08, 0x76, - 0x28, 0x5a, 0x5e, 0xde, 0xe3, 0xad, 0x4e, 0x56, 0xcc, 0x22, 0x85, 0x5f, 0xa9, 0xca, 0xa5, - 0x4e, 0xc6, 0x82, 0xf0, 0xbb, 0x5a, 0x82, 0xbf, 0x32, 0x54, 0xd2, 0x8a, 0x12, 0x05, 0x6f, - 0xd8, 0xd1, 0xc0, 0x60, 0x00, 0x41, 0x03, 0xf4, 0xa8, 0xd6, 0xee, 0x9e, 0x5a, 0x75, 0xea, - 0x76, 0x88, 0xf1, 0x2d, 0x8d, 0x63, 0xfc, 0x14, 0xe9, 0x7b, 0xeb, 0x0b, 0x49, 0xa4, 0x8b, - 0x86, 0xf4, 0x97, 0x61, 0x22, 0x29, 0x32, 0x23, 0x80, 0x2e, 0x59, 0x44, 0xea, 0xf5, 0x56, - 0x18, 0x51, 0xc0, 0x60, 0xe1, 0x83, 0x66, 0x1f, 0x1f, 0xa1, 0x13, 0x64, 0xae, 0xbc, 0x59, - 0xd0, 0xc4, 0x38, 0x70, 0x3f, 0xb5, 0xb5, 0xa2, 0xdc, 0x89, 0x77, 0x70, 0x5a, 0x86, 0xb7, - 0xde, 0x67, 0x3c, 0x22, 0x83, 0xc8, 0x62, 0x77, 0xb6, 0x5d, 0x7e, 0x41, 0x99, 0x70, 0x7d, - 0xc3, 0x27, 0x90, 0x7f, 0x3c, 0xc6, 0x90, 0xc7, 0xb9, 0x32, 0xd8, 0x01, 0xdf, 0x02, 0x20, - 0x27, 0x8f, 0x7b, 0x76, 0xb3, 0x0d, 0xfa, 0x93, 0x46, 0x6f, 0xde, 0x23, 0x12, 0xb9, 0x4e, - 0xb8, 0x36, 0x8e, 0x9c, 0x3a, 0x2f, 0xd4, 0x2b, 0x54, 0xab, 0x63, 0xe6, 0xf1, 0x62, 0x8c, - 0xd1, 0x1e, 0x87, 0x48, 0x2d, 0x83, 0x93, 0xfe, 0xac, 0x29, 0x82, 0xc2, 0x3c, 0x4f, 0xd0, - 0x19, 0xe7, 0xa3, 0x99, 0x8d, 0x7d, 0x64, 0x15, 0xde, 0x64, 0xdb, 0xc1, 0x73, 0xe0, 0x78, - 0x24, 0x23, 0x9e, 0x8a, 0xfb, 0xe2, 0xea, 0xd5, 0x0f, 0x93, 0x51, 0x62, 0x38, 0x41, 0x3e, - 0x40, 0xa4, 0x2f, 0x7e, 0xb8, 0x11, 0x5c, 0xd3, 0x46, 0x0a, 0x74, 0xcb, 0xb7, 0xd0, 0x15, - 0x96, 0xf1, 0xe7, 0x48, 0x32, 0x05, 0x69, 0xa8, 0xda, 0x56, 0xee, 0x9f, 0x6a, 0x95, 0xe6, - 0x0d, 0xd2, 0x2c, 0xc1, 0xc2, 0xa5, 0xf4, 0xd8, 0xc6, 0xd7, 0xf6, 0x1f, 0x6f, 0x88, 0x78, - 0xab, 0xd4, 0x1f, 0x53, 0xa0, 0xa1, 0x3b, 0x03, 0xb1, 0x4a, 0xcd, 0xbe, 0x25, 0x1c, 0x6f, - 0xb4, 0x30, 0x41, 0xb5, 0x4a, 0xaa, 0xa5, 0x38, 0x91, 0x77, 0xce, 0x48, 0xc8, 0xe1, 0x34, - 0xc2, 0x85, 0xdc, 0x9b, 0x24, 0x6f, 0x63, 0x12, 0xaa, 0x97, 0xaf, 0x9e, 0x37, 0x1e, 0xaf, - 0x94, 0x2c, 0x5c, 0x43, 0xa6, 0x93, 0x17, 0x7d, 0x1c, 0x77, 0x18, 0x0f, 0xb5, 0xf2, 0x66, - 0x4a, 0xaf, 0xd9, 0x92, 0xfb, 0x47, 0xfc, 0x1f, 0x7a, 0xdb, 0x77, 0x0b, 0x11, 0x61, 0xe0, - 0x85, 0x12, 0x96, 0xf7, 0x55, 0x09, 0x90, 0xa2, 0x77, 0xdb, 0x9c, 0x9c, 0x8b, 0x04, 0x55, - 0x45, 0x39, 0x64, 0x4e, 0x46, 0x5f, 0xa2, 0x98, 0xbc, 0xd6, 0x1b, 0x5d, 0x5f, 0x67, 0x21, - 0xc7, 0x12, 0xe5, 0xe6, 0x13, 0x93, 0x7b, 0x83, 0xd8, 0x5b, 0xcc, 0x2e, 0x77, 0xce, 0x56, - 0x97, 0xdc, 0x96, 0x70, 0xcc, 0x47, 0x1a, 0x79, 0x1f, 0xb0, 0x6a, 0xe4, 0x7c, 0xd6, 0xa7, - 0x28, 0x72, 0x7a, 0x1e, 0xac, 0x54, 0xc0, 0x5d, 0x8c, 0xa1, 0x00, 0xf4, 0x2e, 0x86, 0x28, - 0x3a, 0x82, 0xce, 0xbc, 0xf0, 0xfd, 0x2d, 0xb1, 0xc4, 0x9f, 0x6d, 0x6b, 0xba, 0x9e, 0x9b, - 0xec, 0xb5, 0x38, 0xe2, 0xe9, 0xf8, 0x57, 0x74, 0xa6, 0x9b, 0x7b, 0xec, 0xe2, 0x90, 0x0d, - 0xec, 0x3e, 0xf0, 0x1b, 0x2b, 0x38, 0x9c, 0x82, 0xd9, 0xd1, 0xbe, 0xe1, 0x08, 0x7f, 0x02, - 0x54, 0xbc, 0x0b, 0x90, 0x14, 0x57, 0xa2, 0x4e, 0x56, 0xbe, 0x1b, 0xdf, 0x48, 0x9c, 0x17, - 0xa1, 0x70, 0xa2, 0x8c, 0x74, 0x74, 0x24, 0xbf, 0xe0, 0x46, 0xb7, 0xdc, 0xa4, 0xb5, 0x66, - 0x44, 0xb3, 0x53, 0x0e, 0xa4, 0xe5, 0xb4, 0x83, 0x06, 0x1f, 0x78, 0x2a, 0xe2, 0x16, 0x5c, - 0x79, 0x00, 0xab, 0x0a, 0x08, 0x57, 0xe5, 0xbc, 0x2c, 0x9b, 0x91, 0x19, 0xf6, 0x1d, 0x31, - 0x25, 0xbb, 0x9b, 0xb5, 0x89, 0x32, 0xb7, 0x53, 0xb9, 0x49, 0xa4, 0x55, 0xc5, 0x9d, 0x2b, - 0xa1, 0xeb, 0xd5, 0xec, 0x02, 0x40, 0x14, 0x44, 0x35, 0xc1, 0x65, 0xd7, 0xae, 0x4c, 0xf8, - 0x16, 0xf5, 0x70, 0x62, 0x28, 0xcd, 0x58, 0x71, 0x01, 0xcb, 0x85, 0x09, 0x24, 0x91, 0xf0, - 0x18, 0xfa, 0xc6, 0x37, 0x07, 0xde, 0xe7, 0x43, 0xed, 0x3a, 0xeb, 0x98, 0x97, 0xf4, 0xc5, - 0xf3, 0x60, 0xa5, 0x83, 0x60, 0x1a, 0xf1, 0x81, 0x03, 0x06, 0x2c, 0x1f, 0x38, 0x71, 0x61, - 0x30, 0x21, 0x8a, 0x49, 0xb4, 0x2a, 0xa6, 0x03, 0x0e, 0x1d, 0x1d, 0x79, 0xc0, 0xe5, 0x5d, - 0xd1, 0x82, 0x37, 0x1b, 0x70, 0x3a, 0xd2, 0x65, 0x48, 0x59, 0xc0, 0xa2, 0xf3, 0x8b, 0x82, - 0x24, 0xf5, 0xfe, 0x2a, 0xdd, 0x8d, 0x3a, 0xe9, 0xe5, 0x1c, 0x92, 0xc7, 0x6a, 0x43, 0xde, - 0x78, 0xb3, 0x33, 0xa0, 0x9a, 0x78, 0x4a, 0x54, 0xba, 0x01, 0x84, 0xd2, 0xb2, 0xa4, 0x6f, - 0x28, 0x21, 0xf2, 0xc4, 0x21, 0x9f, 0x18, 0x72, 0x16, 0x46, 0x6d, 0xfa, 0xf2, 0xa7, 0x7c, - 0x99, 0xa4, 0x2c, 0x93, 0x4d, 0xba, 0xdd, 0xa7, 0x59, 0x50, 0x0b, 0x34, 0x48, 0x63, 0x6f, - 0x0d, 0x4b, 0x81, 0x22, 0x3b, 0x24, 0x9b, 0x37, 0x4b, 0xd3, 0x94, 0x23, 0xc0, 0x71, 0x39, - 0x91, 0xd0, 0x06, 0x2e, 0xd2, 0xae, 0x7c, 0xfd, 0xfd, 0x4d, 0x4a, 0x95, 0x0a, 0xd0, 0x84, - 0xbf, 0x8c, 0x5c, 0xe4, 0xc1, 0x10, 0xfa, 0xe6, 0x26, 0x18, 0xea, 0x75, 0x3e, 0x7d, 0x0b, - 0xc5, 0x9d, 0xbb, 0x14, 0x3c, 0xa5, 0xe3, 0x19, 0x2b, 0xdd, 0xcd, 0xbb, 0xb4, 0x48, 0x9e, - 0x8c, 0xa5, 0xf2, 0xcb, 0x19, 0xa9, 0x8f, 0x0c, 0x0b, 0x36, 0x73, 0xd7, 0x78, 0x2b, 0x64, - 0xc9, 0x8c, 0x4a, 0xb2, 0x8f, 0xe9, 0xd9, 0x51, 0xb1, 0x84, 0x49, 0xd1, 0xec, 0x73, 0xa6, - 0xb1, 0x41, 0x70, 0xa0, 0xd3, 0xea, 0xb4, 0xf8, 0x50, 0x57, 0xb6, 0x69, 0xda, 0x4a, 0xe6, - 0xe0, 0x73, 0xba, 0xdc, 0x98, 0xc4, 0x0e, 0xab, 0xc7, 0xbd, 0x8a, 0xf6, 0xe3, 0xc1, 0x0b, - 0xc3, 0x5b, 0x48, 0xa1, 0x79, 0xab, 0xae, 0x9b, 0x17, 0xbd, 0x56, 0x3b, 0x0c, 0x38, 0xbd, - 0xed, 0x86, 0x95, 0x05, 0xf4, 0x32, 0xf9, 0xdc, 0x87, 0x4f, 0x78, 0x9a, 0xb0, 0x6f, 0x2b, - 0x76, 0xd2, 0xf4, 0x1f, 0x65, 0xf8, 0xd0, 0x9f, 0x8d, 0x3c, 0xb2, 0x53, 0x8c, 0xca, 0xa0, - 0x2b, 0x6d, 0xfb, 0x65, 0x96, 0xde, 0x7c, 0x64, 0xbb, 0xf7, 0x7e, 0x3d, 0x8d, 0xfb, 0xd1, - 0x2d, 0xf1, 0xc3, 0x1a, 0x66, 0x8a, 0x8f, 0x5e, 0x89, 0x37, 0x19, 0xe7, 0x9b, 0xee, 0xf0, - 0xc1, 0xe7, 0xac, 0xe5, 0x15, 0x2f, 0x6d, 0xfb, 0x57, 0xca, 0x78, 0xd0, 0xaa, 0x19, 0xc0, - 0x80, 0x67, 0xc9, 0x2f, 0x81, 0x26, 0x7f, 0xbe, 0xdf, 0xde, 0x50, 0x00, 0x2b, 0x1b, 0x17, - 0x2a, 0x5d, 0x54, 0xfa, 0x46, 0xc1, 0x3f, 0xb7, 0x2f, 0xc8, 0x31, 0xf4, 0x58, 0x43, 0x36, - 0xae, 0x91, 0xc4, 0x46, 0xc4, 0x93, 0xaf, 0x05, 0x9e, 0xa3, 0xba, 0x0c, 0x07, 0x2f, 0x0c, - 0xeb, 0x2f, 0x4d, 0x4a, 0x90, 0xd8, 0xc0, 0x51, 0x0f, 0xa5, 0x97, 0x09, 0x16, 0xfa, 0x24, - 0xc3, 0x99, 0xe6, 0x1c, 0x9e, 0xed, 0x03, 0x76, 0x57, 0xce, 0x29, 0x73, 0x54, 0x19, 0x5a, - 0x20, 0x65, 0x52, 0xa6, 0xfa, 0x97, 0x00, 0x52, 0x23, 0xbb, 0x74, 0x9b, 0x6e, 0xf3, 0x25, - 0x94, 0x9c, 0x66, 0x0f, 0x8b, 0x67, 0x62, 0x1a, 0x13, 0xdb, 0xd5, 0xdb, 0xdb, 0xb9, 0x11, - 0x02, 0x13, 0x63, 0xe4, 0x6e, 0x41, 0x8a, 0xa0, 0xc2, 0x52, 0x71, 0x66, 0xde, 0x3b, 0x3d, - 0x6c, 0xd3, 0xaa, 0x5f, 0x78, 0x3b, 0xf2, 0x7b, 0xad, 0x71, 0x48, 0x2e, 0x06, 0x90, 0x4c, - 0x76, 0x89, 0x99, 0xb5, 0x91, 0x61, 0x29, 0x74, 0x8d, 0x90, 0xd6, 0x1b, 0x60, 0x11, 0xf6, - 0xc0, 0xa0, 0xc7, 0x3c, 0x1e, 0xf6, 0xec, 0x80, 0x91, 0xe8, 0x6d, 0x46, 0xe0, 0x2c, 0xda, - 0x25, 0xdb, 0x18, 0xdb, 0xb1, 0xbe, 0x7c, 0x87, 0x14, 0xa9, 0xa3, 0x46, 0xdb, 0x6e, 0xeb, - 0x5d, 0x98, 0xd2, 0x4c, 0xfa, 0x09, 0x13, 0x71, 0xee, 0x51, 0xae, 0x5d, 0x57, 0x79, 0x80, - 0x02, 0x43, 0x7b, 0xf7, 0x92, 0x3c, 0xf9, 0x03, 0x07, 0x4f, 0x6a, 0x53, 0x9e, 0x35, 0x2d, - 0x40, 0x29, 0x7d, 0x44, 0x39, 0xd2, 0xb4, 0x48, 0xa7, 0x4f, 0xa5, 0x90, 0x9d, 0xb7, 0x4e, - 0xa0, 0xb5, 0xb7, 0x92, 0x0f, 0x1c, 0x18, 0xdd, 0x4c, 0xff, 0xd4, 0xf9, 0x53, 0xd1, 0x82, - 0x38, 0xc5, 0xcc, 0xe2, 0x36, 0x2d, 0xf7, 0x18, 0x07, 0xda, 0x5b, 0xf6, 0xef, 0xe5, 0xcb, - 0xaa, 0x90, 0xd2, 0x84, 0x25, 0xbc, 0x4f, 0x4a, 0x3b, 0x10, 0x0f, 0x81, 0x92, 0xe4, 0xda, - 0x84, 0xad, 0x8b, 0xbb, 0x94, 0xc5, 0xf4, 0x4c, 0x25, 0xd2, 0x08, 0x97, 0xe0, 0x98, 0x0b, - 0xbe, 0xe7, 0xaa, 0x40, 0x06, 0xfa, 0xe4, 0x79, 0xab, 0x76, 0x1f, 0xb6, 0x7f, 0x72, 0x48, - 0x5c, 0x27, 0x8b, 0xab, 0xef, 0x13, 0xe1, 0x0c, 0xb1, 0x79, 0x7b, 0x6d, 0x8f, 0x0b, 0x90, - 0x22, 0x7f, 0x2f, 0xa5, 0x71, 0xca, 0xb7, 0xc0, 0xbb, 0x31, 0x23, 0x03, 0x44, 0xd2, 0x51, - 0x74, 0x08, 0x85, 0xad, 0x1e, 0xfb, 0xc3, 0xa2, 0xa1, 0xe1, 0x3e, 0x14, 0x0e, 0xf2, 0x0a, - 0xb3, 0x9d, 0x74, 0x14, 0x50, 0xff, 0xfe, 0x95, 0xed, 0x02, 0x76, 0xe6, 0xf7, 0x0b, 0x32, - 0x4e, 0x40, 0xb0, 0xd2, 0x67, 0x74, 0x92, 0x51, 0x1e, 0x54, 0x6f, 0x30, 0x6f, 0x58, 0x6a, - 0xbb, 0x8a, 0x6c, 0xa6, 0x6d, 0x92, 0xe3, 0x3e, 0x0c, 0x33, 0x7c, 0xfc, 0x87, 0x12, 0xb8, - 0x61, 0x70, 0xa6, 0x6c, 0x28, 0xad, 0xa2, 0x55, 0x81, 0x58, 0xd5, 0xc7, 0xb6, 0xd1, 0xdf, - 0x47, 0x5d, 0x07, 0x1f, 0xed, 0xfd, 0x4e, 0x0f, 0x3c, 0xe3, 0x9d, 0xae, 0xeb, 0xf6, 0x3a, - 0xb6, 0x0e, 0x5e, 0xc9, 0x9f, 0xc9, 0x5e, 0xbc, 0x37, 0xcf, 0x81, 0x04, 0x60, 0x26, 0xe4, - 0x8b, 0x90, 0xdb, 0xf1, 0xc2, 0x66, 0x87, 0xd2, 0x29, 0x72, 0x01, 0xa6, 0xcd, 0x01, 0xdf, - 0x9a, 0xfb, 0x4d, 0x62, 0xcb, 0x4d, 0x59, 0x18, 0xd2, 0x3f, 0x79, 0xf7, 0xaf, 0xda, 0x10, - 0x66, 0xb6, 0x3e, 0x7e, 0x7b, 0xcb, 0xb6, 0xa0, 0xd7, 0xa8, 0x5b, 0xe7, 0x8e, 0xa0, 0x24, - 0xf4, 0x55, 0x43, 0x00, 0xda, 0x86, 0x50, 0xec, 0x0a, 0x04, 0x27, 0xb1, 0x98, 0x40, 0x5c, - 0xb1, 0xa5, 0x18, 0x87, 0xa9, 0x88, 0x87, 0x85, 0xe0, 0x89, 0xd5, 0xe2, 0x24, 0xef, 0x65, - 0xac, 0x2c, 0xd1, 0x3a, 0xe7, 0x67, 0xee, 0xc4, 0xd6, 0x3a, 0x76, 0x2a, 0x55, 0x03, 0x5b, - 0x0b, 0xde, 0x5e, 0x38, 0x5a, 0xb7, 0x7d, 0xb3, 0x82, 0x39, 0x5c, 0x98, 0xc3, 0x2f, 0x8d, - 0x53, 0xa0, 0x41, 0x7d, 0xa8, 0x83, 0x47, 0x4a, 0xb0, 0x42, 0xc8, 0xeb, 0xf4, 0xfb, 0x9e, - 0xe9, 0x82, 0xe3, 0xf6, 0x4d, 0x16, 0xe4, 0xd9, 0xd1, 0xca, 0x56, 0xae, 0x09, 0xe7, 0xcf, - 0xaa, 0x25, 0x15, 0x91, 0xce, 0x15, 0xa9, 0x87, 0x50, 0x80, 0x38, 0xcd, 0x6a, 0x64, 0x4d, - 0xb5, 0x55, 0x63, 0xc7, 0x9c, 0xb0, 0x52, 0xb0, 0x3d, 0x5a, 0x8b, 0x65, 0x19, 0x33, 0x43, - 0xa2, 0x97, 0x20, 0xad, 0x79, 0xa6, 0xe8, 0x82, 0x06, 0x39, 0x45, 0x8b, 0x22, 0x35, 0x28, - 0x72, 0x45, 0xc8, 0xca, 0xa4, 0xc3, 0x10, 0x20, 0x22, 0x83, 0x8f, 0x39, 0x49, 0x83, 0x72, - 0x6d, 0xf4, 0x56, 0xfd, 0x55, 0x43, 0x60, 0x3d, 0x75, 0xa3, 0x4e, 0x14, 0x5b, 0xb8, 0xff, - 0x4f, 0x17, 0xd9, 0x93, 0xd7, 0xc0, 0xaf, 0x5c, 0xb9, 0x65, 0xa5, 0x86, 0x8a, 0x9a, 0x35, - 0x5d, 0x95, 0x30, 0x21, 0xc3, 0x38, 0x82, 0x05, 0x0b, 0xb8, 0x02, 0x20, 0xf1, 0x8a, 0x37, - 0x15, 0x0a, 0xc2, 0x46, 0x8a, 0x16, 0x6f, 0x0d, 0x3a, 0x5b, 0x12, 0x6e, 0xee, 0xbf, 0xaf, - 0xfb, 0x04, 0xe5, 0x34, 0xcc, 0x44, 0x2c, 0x3c, 0x22, 0x7c, 0x4f, 0xd7, 0xad, 0xe1, 0xe2, - 0x12, 0xe6, 0xda, 0x69, 0x6f, 0x9c, 0xc8, 0x1f, 0xb9, 0x7e, 0x42, 0x20, 0x7d, 0x21, 0x0a, - 0x0f, 0xb6, 0x16, 0x59, 0x01, 0x91, 0x16, 0xfe, 0xa4, 0x7b, 0xb6, 0x36, 0x43, 0x72, 0xbf, - 0x33, 0x71, 0xb8, 0x5d, 0x8a, 0x4f, 0x91, 0xe0, 0x79, 0xb5, 0x40, 0xda, 0x9f, 0x02, 0xbe, - 0x35, 0x60, 0xc0, 0xef, 0xc9, 0x2f, 0x42, 0xd3, 0xcd, 0xef, 0xde, 0x10, 0x71, 0x14, 0x8d, - 0x86, 0x29, 0xcd, 0x31, 0x39, 0x90, 0xe0, 0x2d, 0x28, 0x97, 0xe4, 0xb3, 0xe5, 0xd7, 0x61, - 0xe3, 0x2b, 0x21, 0xe7, 0xc5, 0x4b, 0xd6, 0xe9, 0xdb, 0x51, 0x03, 0xa2, 0x0e, 0x7c, 0x7e, - 0x95, 0xa8, 0xf6, 0x8b, 0x95, 0x8f, 0x6e, 0xfd, 0x54, 0xfd, 0x28, 0x71, 0x4a, 0xfc, 0x75, - 0x54, 0x19, 0x8c, 0xd3, 0x8a, 0x9b, 0x94, 0xcd, 0x33, 0x60, 0x80, 0x7a, 0xd4, 0x68, 0x38, - 0x29, 0x0e, 0xf0, 0xc1, 0xeb, 0xac, 0xaf, 0xb1, 0xe6, 0xce, 0xbd, 0xd1, 0xfd, 0xbc, 0x23, - 0x85, 0x8d, 0x97, 0xf4, 0xc9, 0xf9, 0xbd, 0x69, 0xd2, 0x0c, 0x60, 0x12, 0x05, 0x82, 0x6e, - 0x5a, 0x6e, 0xfd, 0x55, 0xcc, 0xd8, 0xe6, 0xed, 0x54, 0xde, 0xdb, 0xcc, 0xf9, 0x85, 0x0b, - 0xf6, 0x42, 0x39, 0x76, 0xde, 0xab, 0x08, 0xd7, 0x65, 0xe8, 0x19, 0xbd, 0x72, 0xec, 0x53, - 0x7d, 0xc8, 0xa6, 0x00, 0xb3, 0x3e, 0x5b, 0x7d, 0xa0, 0x7b, 0xaa, 0x6e, 0xc5, 0x45, 0x12, - 0xe3, 0xac, 0x02, 0x1c, 0x01, 0xb8, 0x2b, 0x2b, 0xd6, 0xf8, 0x2e, 0x96, 0x3a, 0x5e, 0x6e, - 0xda, 0xf9, 0x6c, 0xac, 0x36, 0xf8, 0xcd, 0xef, 0x05, 0x8d, 0x8f, 0xe7, 0x6c, 0x39, 0x24, - 0xb3, 0x8d, 0xed, 0xd1, 0x70, 0xa6, 0x20, 0x1e, 0x2b, 0x1d, 0x2c, 0x40, 0x16, 0xd6, 0x23, - 0xb0, 0x99, 0xaa, 0x74, 0x31, 0xbe, 0xdc, 0x28, 0xd4, 0x64, 0xfa, 0xd2, 0x8d, 0xdb, 0xa1, - 0x32, 0xaf, 0xdc, 0xb1, 0x71, 0xb3, 0x4d, 0x0a, 0x3a, 0x3a, 0x53, 0x11, 0x8c, 0x11, 0x8c, - 0xe7, 0xd5, 0xdb, 0xdd, 0x67, 0xb8, 0x1a, 0x39, 0x0e, 0xf6, 0x69, 0xd8, 0x1b, 0xcb, 0xe5, - 0xc5, 0xa6, 0x3d, 0x3b, 0xfe, 0xe1, 0x05, 0x48, 0x83, 0x67, 0x7f, 0xf1, 0x9c, 0x70, 0x7e, - 0x70, 0x5c, 0xc4, 0xd3, 0x78, 0x8b, 0x80, 0x87, 0xf5, 0x94, 0x2d, 0x0a, 0xbf, 0x82, 0x13, - 0xad, 0x36, 0x10, 0x0a, 0x60, 0xaa, 0xc4, 0xa3, 0xcd, 0xf1, 0xa9, 0xa1, 0x49, 0xf5, 0x41, - 0x14, 0xd6, 0xd1, 0xa7, 0xe0, 0x50, 0xce, 0xe2, 0x76, 0xbe, 0x09, 0xc4, 0x3a, 0x68, 0xc5, - 0x8a, 0x1f, 0x8e, 0xd0, 0x4d, 0x90, 0x92, 0xc8, 0x3b, 0x63, 0xae, 0xc2, 0xfe, 0x77, 0x8c, - 0x4b, 0xa6, 0x6c, 0x04, 0xff, 0x62, 0xe1, 0x32, 0x04, 0xb8, 0xfd, 0xb3, 0x34, 0x47, 0x85, - 0x32, 0x6b, 0x50, 0xce, 0xcf, 0x4f, 0x0f, 0x72, 0x41, 0x09, 0x7f, 0xde, 0x5c, 0xe7, 0x08, - 0xdf, 0x63, 0x09, 0x89, 0xd1, 0x23, 0xb6, 0xe3, 0xbc, 0xff, 0xe4, 0x88, 0x61, 0x67, 0x8e, - 0x4a, 0xb6, 0x86, 0x08, 0x33, 0xad, 0xac, 0x9f, 0xf3, 0x37, 0xbd, 0xcc, 0xd6, 0x0e, 0x3e, - 0x4e, 0x4f, 0xc9, 0xff, 0xc8, 0xf6, 0xd8, 0x2a, 0x43, 0x3d, 0x79, 0xf2, 0x5d, 0x87, 0x33, - 0x1d, 0xf4, 0xfd, 0x80, 0x2c, 0x4f, 0xc4, 0xa5, 0x3a, 0x2f, 0x51, 0xb1, 0x19, 0xf8, 0xed, - 0xe0, 0xc9, 0x6c, 0x82, 0x2d, 0x43, 0xdd, 0x71, 0x27, 0x32, 0x8b, 0x51, 0x3d, 0xbc, 0x83, - 0x6b, 0xa8, 0xda, 0x2b, 0xbb, 0xb0, 0xba, 0x5b, 0x30, 0x4f, 0x44, 0xad, 0x99, 0xb7, 0x47, - 0x35, 0x6f, 0x1f, 0xbe, 0x5f, 0x24, 0x4e, 0xba, 0x92, 0x89, 0xc8, 0xb6, 0x45, 0x12, 0x19, - 0xee, 0xe7, 0xd8, 0x74, 0xb3, 0x33, 0x41, 0x39, 0x26, 0x4c, 0xc3, 0xb0, 0x22, 0x1e, 0xee, - 0xd0, 0xfb, 0xc6, 0xa3, 0x7d, 0xaa, 0x7d, 0xa1, 0x51, 0x98, 0xe8, 0xcc, 0x58, 0x39, 0x22, - 0x1e, 0xe7, 0xf0, 0xb4, 0xd3, 0x7c, 0xa4, 0xa6, 0x10, 0xba, 0x2f, 0x41, 0xd6, 0x17, 0x95, - 0x76, 0xe1, 0xbb, 0xec, 0x43, 0x99, 0x87, 0xd4, 0x13, 0x9b, 0x85, 0x4f, 0x44, 0x32, 0x5a, - 0xe0, 0x9a, 0xee, 0x29, 0x15, 0x45, 0x05, 0xf7, 0xe4, 0xc9, 0x4d, 0x6b, 0x45, 0xad, 0x1f, - 0x13, 0x06, 0x29, 0xb4, 0x2b, 0x74, 0x83, 0xe1, 0xdb, 0x3d, 0x99, 0x28, 0xbd, 0x4f, 0xfb, - 0x0d, 0x03, 0x4d, 0x79, 0x09, 0x7f, 0x12, 0x91, 0xe9, 0x69, 0x40, 0x84, 0xc7, 0x10, 0xd5, - 0xce, 0xae, 0xbb, 0xa5, 0xb2, 0x35, 0xdb, 0x24, 0xa4, 0x0e, 0xe4, 0x6f, 0x80, 0xb4, 0xb6, - 0x94, 0xf7, 0x51, 0x5b, 0x0c, 0x5b, 0xae, 0x3f, 0xa5, 0x17, 0xe0, 0x9c, 0xd1, 0x05, 0x31, - 0x05, 0xc4, 0x14, 0xdd, 0xde, 0x6f, 0x39, 0xa1, 0x9a, 0x46, 0x3d, 0x68, 0x3c, 0xfe, 0x04, - 0x8e, 0xc2, 0x11, 0x9d, 0xc2, 0x3a, 0xaa, 0xcf, 0x22, 0x26, 0x54, 0xcb, 0x8d, 0x47, 0x61, - 0x9e, 0xf4, 0x2e, 0xe7, 0x3c, 0xe6, 0xf0, 0xd6, 0x1a, 0xe5, 0x18, 0xbd, 0x9f, 0xa4, 0x3e, - 0x47, 0xdb, 0x85, 0xe0, 0x8c, 0xbf, 0xdd, 0x19, 0x91, 0x81, 0x80, 0x63, 0x44, 0x0a, 0x96, - 0xae, 0xde, 0x1f, 0xbc, 0xdb, 0x15, 0x89, 0x71, 0x60, 0x23, 0xc4, 0x0a, 0x96, 0xae, 0xbf, - 0xfc, 0x7a, 0x76, 0x4f, 0x1c, 0x9b, 0xb4, 0xcb, 0x14, 0x8b, 0x94, 0x8b, 0x94, 0xaa, 0xd6, - 0x0f, 0x7d, 0x78, 0x72, 0x47, 0x0c, 0x9a, 0x97, 0xac, 0xbb, 0xf4, 0x6a, 0x56, 0x0f, 0x7d, - 0x59, 0x30, 0xc3, 0x04, 0x6b, 0x54, 0x0b, 0x75, 0x49, 0x10, 0xa2, 0xa7, 0xad, 0xd8, 0x13, - 0x85, 0x88, 0x73, 0x45, 0xe9, 0x31, 0xc1, 0x00, 0x82, 0x67, 0x4c, 0xfb, 0x74, 0x6a, 0x56, - 0x2e, 0xbf, 0xdd, 0x19, 0x91, 0x81, 0x80, 0x82, 0x86, 0x6f, 0x5c, 0x3a, 0xf6, 0x4f, 0x1c, - 0xba, 0xf6, 0x4f, 0xfd, 0x59, 0x11, 0x81, 0x61, 0x21, 0xa1, 0xc0, 0x02, 0x86, 0x6f, 0x3d, - 0xf8, 0x53, 0x24, 0xca, 0xf7, 0x6c, 0x5a, 0x36, 0xcf, 0xfd, 0x59, 0x11, 0xa0, 0xa3, 0xc4, - 0x0a, 0x96, 0x8f, 0x9c, 0xba, 0xd7, 0x0d, 0x98, 0xb2, 0xc7, 0xed, 0x58, 0x32, 0xc7, 0xed, - 0x39, 0xf0, 0x43, 0x04, 0x6b, 0x54, 0x2a, 0xb7, 0xec, 0x5a, 0x36, 0xee, 0x3f, 0xfc, 0x7a, - 0x76, 0x6e, 0x5e, 0x1f, 0x9d, 0x99, 0x91, 0x81, 0x80, 0x82, 0x86, 0x6f, 0x5c, 0x1b, 0x95, - 0x89, 0x90, 0xa2, 0xa7, 0xcc, 0x1a, 0x97, 0xac, 0xbb, 0xf4, 0x6a, 0x37, 0xec, 0x3b, 0xf4, - 0x6a, 0x37, 0xec, 0x3b, 0xf4, 0x4b, 0xf5, 0x49, 0xf1, 0x60, 0x23, 0xa5, 0xa9, 0xb1, 0xc1, - 0xe1, 0x21, 0xa1, 0xa1, 0xc0, 0xe3, 0x25, 0xa9, 0xb1, 0xe0, 0x42, 0xe7, 0x4c, 0xfb, 0x74, - 0x6a, 0x37, 0xec, 0x5a, 0x17, 0xac, 0xbb, 0xd5, 0x28, 0xd2, 0x07, 0x6d, 0x58, 0x13, 0x85, - 0x69, 0x31, 0xe0, 0x42, 0xe7, 0x4c, 0xfb, 0x55, 0x28, 0xd2, 0x07, 0x8c, 0x7b, 0x74, 0x4b, - 0x14, 0xaa, 0xd6, 0x2e, 0xde, 0x3e, 0xfe, 0x7e, 0x5f, 0x1d, 0xb8, 0xf2, 0x47, 0x0c, 0x7b, - 0x55, 0x09, 0x90, 0x83, 0x65, 0x48, 0x12, 0x87, 0x6d, 0x39, 0xf0, 0x62, 0x46, 0x0e, 0x9e, - 0x9f, 0xbc, 0xfa, 0x57, 0x2c, 0xbb, 0xd5, 0x09, 0x71, 0x60, 0x5b, 0x08, 0xf7, 0x2b, +0x01, 0x04, 0x8e, 0x96, 0x6e, 0x77, 0x3e, 0xfe, 0x7e, 0x5f, 0x1d, 0xb8, 0xf2, 0x66, 0x4e, +0xff, 0x5d, 0x19, 0xb0, 0xc2, 0x04, 0x69, 0x54, 0x2a, 0xd6, 0x2e, 0xbf, 0xdd, 0x19, 0xb0, +0xc3, 0xe5, 0x29, 0xb1, 0xe0, 0x23, 0xa5, 0xa9, 0xb1, 0xc1, 0x00, 0x82, 0x67, 0x4c, 0x1a, +0x97, 0x8d, 0x79, 0x51, 0x20, 0xc7, 0x06, 0x8e, 0x7c, 0x7c, 0x7a, 0x76, 0x4f, 0xfd, 0x59, +0x30, 0xe2, 0x46, 0x0e, 0x9e, 0xbe, 0xdf, 0x1d, 0x99, 0x91, 0xa0, 0xa5, 0xa1, 0xa9, 0xd0, +0x22, 0xc6, 0xef, 0x5c, 0x1b, 0x95, 0x89, 0x90, 0xa2, 0xa7, 0xcc, 0xfb, 0x55, 0x28, 0xb3, +0xe4, 0x4a, 0xf7, 0x6c, 0x3b, 0xf4, 0x6a, 0x56, 0x2e, 0xde, 0x1f, 0x9d, 0xb8, 0xd3, 0x05, +0x88, 0x92, 0xa6, 0xce, 0x1e, 0xbe, 0xdf, 0x1d, 0x99, 0xb0, 0xe2, 0x46, 0xef, 0x5c, 0x07, +0x11, 0x5d, 0x98, 0x0b, 0x9d, 0x94, 0x97, 0xee, 0x4e, 0x45, 0x33, 0x6b, 0x44, 0xc7, 0x29, +0x56, 0x27, 0x30, 0xc6, 0xa7, 0xd5, 0xf2, 0x56, 0xdf, 0xb4, 0x38, 0x62, 0xcb, 0xa0, 0xb6, +0xe3, 0x0f, 0x84, 0x06, 0x24, 0x05, 0x65, 0x6f, 0x76, 0x89, 0xb5, 0x77, 0x41, 0x27, 0x82, +0x66, 0x65, 0x82, 0xcc, 0xd5, 0xe6, 0x20, 0xd5, 0x27, 0x17, 0xc5, 0xf8, 0x03, 0x23, 0x7c, +0x5f, 0x64, 0xa5, 0x1d, 0xc1, 0xd6, 0x36, 0xcb, 0x4c, 0xd4, 0xdb, 0x66, 0xd7, 0x8b, 0xb1, +0x99, 0x7e, 0x6f, 0x4c, 0x36, 0x40, 0x06, 0xd6, 0xeb, 0xd7, 0xa2, 0xe4, 0xf4, 0x95, 0x51, +0x5a, 0x54, 0x96, 0xd5, 0x53, 0x44, 0xd7, 0x8c, 0xe0, 0xb9, 0x40, 0x68, 0xd2, 0x18, 0xe9, +0xdd, 0x9a, 0x23, 0x92, 0x48, 0xee, 0x7f, 0x43, 0xaf, 0xea, 0x77, 0x38, 0x84, 0x8c, 0x0a, +0x72, 0xaf, 0x69, 0xf8, 0xdd, 0xf1, 0x24, 0x83, 0xa3, 0xf8, 0x4a, 0xbf, 0xf5, 0x94, 0x13, +0xdb, 0xbb, 0xd8, 0xb4, 0xb3, 0xa0, 0xfb, 0x45, 0x50, 0x60, 0x30, 0x59, 0x12, 0x31, 0x71, +0xa2, 0xd3, 0x13, 0xe7, 0xfa, 0xe7, 0xce, 0x0f, 0x63, 0x15, 0x0b, 0x6b, 0x94, 0xbb, 0x37, +0x83, 0x26, 0x05, 0x9d, 0xfb, 0x46, 0x92, 0xfc, 0x0a, 0x15, 0xd1, 0x0d, 0x73, 0x92, 0xd6, +0x8c, 0x1b, 0x8c, 0xb8, 0x55, 0x8a, 0xce, 0xbd, 0xfe, 0x8e, 0xfc, 0xed, 0x09, 0x12, 0x83, +0x91, 0x82, 0x51, 0x31, 0x23, 0xfb, 0xb4, 0x0c, 0x76, 0xad, 0x7c, 0xd9, 0xb4, 0x4b, 0xb2, +0x67, 0x14, 0x09, 0x9c, 0x7f, 0x0c, 0x18, 0xba, 0x3b, 0xd6, 0x8e, 0x14, 0x2a, 0xe4, 0x1b, +0x52, 0x9f, 0x2b, 0x7d, 0xe1, 0xfb, 0x6a, 0x33, 0x02, 0xfa, 0xac, 0x5a, 0xf2, 0x3e, 0x88, +0x7e, 0xae, 0xd1, 0xf3, 0x78, 0xe8, 0x05, 0xd1, 0xe3, 0xdc, 0x21, 0xf6, 0xe1, 0x9a, 0xbd, +0x17, 0x0e, 0xd9, 0x46, 0x9b, 0x88, 0x03, 0xea, 0xf6, 0x66, 0xbe, 0x0e, 0x1b, 0x50, 0x49, +0x96, 0x40, 0x97, 0xf1, 0xf1, 0xe4, 0x80, 0xa6, 0x6e, 0xe8, 0x77, 0x34, 0xbf, 0x29, 0x40, +0x44, 0xc2, 0xff, 0x4e, 0x98, 0xd3, 0x9c, 0xa3, 0x32, 0x2b, 0x76, 0x51, 0x04, 0x09, 0xe7, +0xa9, 0xd1, 0xa6, 0x32, 0xb1, 0x23, 0x53, 0xe2, 0x47, 0xab, 0xd6, 0xf5, 0x69, 0x5c, 0x3e, +0x5f, 0xfa, 0xae, 0x45, 0x20, 0xe5, 0xd2, 0x44, 0xff, 0x39, 0x32, 0x6d, 0xfd, 0x27, 0x57, +0x5c, 0xfd, 0xf0, 0xde, 0xc1, 0xb5, 0x99, 0xe5, 0xf5, 0x1c, 0x77, 0x01, 0x75, 0xc5, 0x6d, +0x58, 0x92, 0xf2, 0xb2, 0x47, 0x00, 0x01, 0x26, 0x96, 0x7a, 0x30, 0xff, 0xb7, 0xf0, 0xef, +0x77, 0xc1, 0x8a, 0x5d, 0xdc, 0xc0, 0xd1, 0x29, 0x30, 0x1e, 0x77, 0x38, 0x7a, 0x94, 0xf1, +0xb8, 0x7a, 0x7e, 0xef, 0xa4, 0xd1, 0xac, 0x31, 0x4a, 0xf2, 0x5d, 0x64, 0x3d, 0xb2, 0xe2, +0xf0, 0x08, 0x99, 0xfc, 0x70, 0xee, 0x24, 0xa7, 0x7e, 0xee, 0x1e, 0x20, 0x69, 0x7d, 0x44, +0xbf, 0x87, 0x42, 0xdf, 0x88, 0x3b, 0x0c, 0xda, 0x42, 0xc9, 0x04, 0xf9, 0x45, 0x50, 0xfc, +0x83, 0x8f, 0x11, 0x6a, 0x72, 0xbc, 0x99, 0x95, 0xf0, 0xac, 0x3d, 0xa7, 0x3b, 0xcd, 0x1c, +0xe2, 0x88, 0x79, 0x37, 0x11, 0x5f, 0x39, 0x89, 0x95, 0x0a, 0x16, 0x84, 0x7a, 0xf6, 0x8a, +0xa4, 0x28, 0xe4, 0xed, 0x83, 0x80, 0x3b, 0xb1, 0x23, 0xa5, 0x03, 0x10, 0xf4, 0x66, 0xea, +0xbb, 0x0c, 0x0f, 0xc5, 0xec, 0x6c, 0x69, 0xc5, 0xd3, 0x24, 0xab, 0xd4, 0x2a, 0xb7, 0x99, +0x88, 0x76, 0x08, 0xa0, 0xa8, 0x95, 0x7c, 0xd8, 0x38, 0x6d, 0xcd, 0x59, 0x02, 0x51, 0x4b, +0xf1, 0xb5, 0x2b, 0x50, 0xe3, 0xb6, 0xbd, 0xd0, 0x72, 0xcf, 0x9e, 0xfd, 0x6e, 0xbb, 0x44, +0xc8, 0x24, 0x8a, 0x77, 0x18, 0x8a, 0x13, 0x06, 0xef, 0x97, 0x7d, 0xfa, 0x81, 0xf0, 0x31, +0xe6, 0xfa, 0x77, 0xed, 0x31, 0x06, 0x31, 0x5b, 0x54, 0x8a, 0x9f, 0x30, 0x68, 0xdb, 0xe2, +0x40, 0xf8, 0x4e, 0x73, 0xfa, 0xab, 0x74, 0x8b, 0x10, 0x58, 0x13, 0xdc, 0xd2, 0xe6, 0x78, +0xd1, 0x32, 0x2e, 0x8a, 0x9f, 0x2c, 0x58, 0x06, 0x48, 0x27, 0xc5, 0xa9, 0x5e, 0x81, 0x47, +0x89, 0x46, 0x21, 0x91, 0x03, 0x70, 0xa4, 0x3e, 0x88, 0x9c, 0xda, 0x33, 0x0a, 0xce, 0xbc, +0x8b, 0x8e, 0xcf, 0x9f, 0xd3, 0x71, 0x80, 0x43, 0xcf, 0x6b, 0xa9, 0x51, 0x83, 0x76, 0x30, +0x82, 0xc5, 0x6a, 0x85, 0x39, 0x11, 0x50, 0x1a, 0x82, 0xdc, 0x1e, 0x1c, 0xd5, 0x7d, 0xa9, +0x71, 0x99, 0x33, 0x47, 0x19, 0x97, 0xb3, 0x5a, 0xb1, 0xdf, 0xed, 0xa4, 0xf2, 0xe6, 0x26, +0x84, 0xa2, 0x28, 0x9a, 0x9e, 0xdf, 0xa6, 0x6a, 0xf4, 0xd6, 0xfc, 0x2e, 0x5b, 0x9d, 0x1a, +0x2a, 0x27, 0x68, 0xfb, 0xc1, 0x83, 0x21, 0x4b, 0x90, 0xe0, 0x36, 0xdd, 0x5b, 0x31, 0x42, +0x55, 0xa0, 0x13, 0xf7, 0xd0, 0x89, 0x53, 0x71, 0x99, 0x57, 0x09, 0x29, 0xc5, 0xf3, 0x21, +0xf8, 0x37, 0x2f, 0x40, 0xf3, 0xd4, 0xaf, 0x16, 0x08, 0x36, 0x02, 0xfc, 0x77, 0xc5, 0x8b, +0x04, 0x90, 0x56, 0xb9, 0xc9, 0x67, 0x9a, 0x99, 0xe8, 0x00, 0xd3, 0x86, 0xff, 0x97, 0x2d, +0x08, 0xe9, 0xb7, 0xb3, 0x91, 0xbc, 0xdf, 0x45, 0xc6, 0xed, 0x0f, 0x8c, 0x4c, 0x1e, 0xe6, +0x5b, 0x6e, 0x38, 0x30, 0xe4, 0xaa, 0xe3, 0x95, 0xde, 0xb9, 0xe4, 0x9a, 0xf5, 0xb2, 0x55, +0x9a, 0x87, 0x9b, 0xf6, 0x6a, 0xb2, 0xf2, 0x77, 0x9a, 0x31, 0xf4, 0x7a, 0x31, 0xd1, 0x1d, +0x04, 0xc0, 0x7c, 0x32, 0xa2, 0x9e, 0x9a, 0xf5, 0x62, 0xf8, 0x27, 0x8d, 0xbf, 0x51, 0xff, +0xd3, 0xdf, 0x64, 0x37, 0x3f, 0x2a, 0x6f, 0x76, 0x3a, 0x7d, 0x77, 0x06, 0x9e, 0x77, 0x7f, +0x5e, 0xeb, 0x32, 0x51, 0xf9, 0x16, 0x66, 0x9a, 0x09, 0xf3, 0xb0, 0x08, 0xa4, 0x70, 0x96, +0x46, 0x30, 0xff, 0xda, 0x4f, 0xe9, 0x1b, 0xed, 0x8d, 0xf8, 0x74, 0x1f, 0x31, 0x92, 0xb3, +0x73, 0x17, 0x36, 0xdb, 0x91, 0x30, 0xd6, 0x88, 0x55, 0x6b, 0x34, 0x77, 0x87, 0x7a, 0xe7, +0xee, 0x06, 0xc6, 0x1c, 0x8c, 0x19, 0x0c, 0x48, 0x46, 0x23, 0x5e, 0x9c, 0x07, 0x5c, 0xbf, +0xb4, 0x7e, 0xd6, 0x4f, 0x74, 0x9c, 0xe2, 0xc5, 0x50, 0x8b, 0xc5, 0x8b, 0x15, 0x90, 0x60, +0x62, 0x57, 0x29, 0xd0, 0x13, 0x43, 0xa1, 0x80, 0x88, 0x91, 0x00, 0x44, 0xc7, 0x4d, 0x19, +0x86, 0xcc, 0x2f, 0x2a, 0x75, 0x5a, 0xfc, 0xeb, 0x97, 0x2a, 0x70, 0xe3, 0x78, 0xd8, 0x91, +0xb0, 0x4f, 0x99, 0x07, 0xa3, 0x95, 0xea, 0x24, 0x21, 0xd5, 0xde, 0x51, 0x20, 0x93, 0x27, +0x0a, 0x30, 0x73, 0xa8, 0xff, 0x8a, 0x97, 0xe9, 0xa7, 0x6a, 0x8e, 0x0d, 0xe8, 0xf0, 0xdf, +0xec, 0xea, 0xb4, 0x6c, 0x1d, 0x39, 0x2a, 0x62, 0x2d, 0x3d, 0x5a, 0x8b, 0x65, 0xf8, 0x90, +0x05, 0x2e, 0x7e, 0x91, 0x2c, 0x78, 0xef, 0x8e, 0x7a, 0xc1, 0x2f, 0xac, 0x78, 0xee, 0xaf, +0x28, 0x45, 0x06, 0x4c, 0x26, 0xaf, 0x3b, 0xa2, 0xdb, 0xa3, 0x93, 0x06, 0xb5, 0x3c, 0xa5, +0xd8, 0xee, 0x8f, 0xaf, 0x25, 0xcc, 0x3f, 0x85, 0x68, 0x48, 0xa9, 0x62, 0xcc, 0x97, 0x8f, +0x7f, 0x2a, 0xea, 0xe0, 0x15, 0x0a, 0xad, 0x62, 0x07, 0xbd, 0x45, 0xf8, 0x41, 0xd8, 0x36, +0xcb, 0x4c, 0xdb, 0x6e, 0xe6, 0x3a, 0xe7, 0xda, 0x15, 0xe9, 0x29, 0x1e, 0x12, 0x10, 0xa0, +0x14, 0x2c, 0x0e, 0x3d, 0xf4, 0xbf, 0x39, 0x41, 0x92, 0x75, 0x0b, 0x25, 0x7b, 0xa3, 0xce, +0x39, 0x9c, 0x15, 0x64, 0xc8, 0xfa, 0x3d, 0xef, 0x73, 0x27, 0xfe, 0x26, 0x2e, 0xce, 0xda, +0x6e, 0xfd, 0x71, 0x8e, 0xdd, 0xfe, 0x76, 0xee, 0xdc, 0x12, 0x5c, 0x02, 0xc5, 0x3a, 0x4e, +0x4e, 0x4f, 0xbf, 0xca, 0x40, 0x15, 0xc7, 0x6e, 0x8d, 0x41, 0xf1, 0x10, 0xe0, 0x4f, 0x7e, +0x97, 0x7f, 0x1c, 0xae, 0x47, 0x8e, 0x6b, 0xb1, 0x25, 0x31, 0xb0, 0x73, 0xc7, 0x1b, 0x97, +0x79, 0xf9, 0x80, 0xd3, 0x66, 0x22, 0x30, 0x07, 0x74, 0x1e, 0xe4, 0xd0, 0x80, 0x21, 0xd6, +0xee, 0x6b, 0x6c, 0x4f, 0xbf, 0xf5, 0xb7, 0xd9, 0x09, 0x87, 0x2f, 0xa9, 0x14, 0xbe, 0x27, +0xd9, 0x72, 0x50, 0x01, 0xd4, 0x13, 0x73, 0xa6, 0xa7, 0x51, 0x02, 0x75, 0x25, 0xe1, 0xb3, +0x45, 0x34, 0x7d, 0xa8, 0x8e, 0xeb, 0xf3, 0x16, 0x49, 0xcb, 0x4f, 0x8c, 0xa1, 0xb9, 0x36, +0x85, 0x39, 0x75, 0x5d, 0x08, 0x00, 0xae, 0xeb, 0xf6, 0xea, 0xd7, 0x13, 0x3a, 0x21, 0x5a, +0x5f, 0x30, 0x84, 0x52, 0x26, 0x95, 0xc9, 0x14, 0xf2, 0x57, 0x55, 0x6b, 0xb1, 0x10, 0xc2, +0xe1, 0xbd, 0x3b, 0x51, 0xc0, 0xb7, 0x55, 0x4c, 0x71, 0x12, 0x26, 0xc7, 0x0d, 0xf9, 0x51, +0xa4, 0x38, 0x02, 0x05, 0x7f, 0xb8, 0xf1, 0x72, 0x4b, 0xbf, 0x71, 0x89, 0x14, 0xf3, 0x77, +0x38, 0xd9, 0x71, 0x24, 0xf3, 0x00, 0x11, 0xa1, 0xd8, 0xd4, 0x69, 0x27, 0x08, 0x37, 0x35, +0xc9, 0x11, 0x9d, 0x90, 0x1c, 0x0e, 0xe7, 0x1c, 0xff, 0x2d, 0x1e, 0xe8, 0x92, 0xe1, 0x18, +0x10, 0x95, 0x7c, 0xe0, 0x80, 0xf4, 0x96, 0x43, 0x21, 0xf9, 0x75, 0x21, 0x64, 0x38, 0xdd, +0x9f, 0x1e, 0x95, 0x16, 0xda, 0x56, 0x1d, 0x4f, 0x9a, 0x53, 0xb2, 0xe2, 0xe4, 0x18, 0xcb, +0x6b, 0x1a, 0x65, 0xeb, 0x56, 0xc6, 0x3b, 0xe5, 0xfe, 0xd8, 0x26, 0x3f, 0x3a, 0x84, 0x59, +0x72, 0x66, 0xa2, 0xf3, 0x75, 0xff, 0xfb, 0x60, 0xb3, 0x22, 0xad, 0x3f, 0x2d, 0x6b, 0xf9, +0xeb, 0xea, 0x05, 0x7c, 0xd8, 0x8f, 0x6d, 0x2c, 0x98, 0x9e, 0x2b, 0x93, 0xf1, 0x5e, 0x46, +0xf0, 0x87, 0x49, 0x29, 0x73, 0x68, 0xd7, 0x7f, 0xf9, 0xf0, 0xe5, 0x7d, 0xdb, 0x1d, 0x75, +0x19, 0xf3, 0xc4, 0x58, 0x9b, 0x17, 0x88, 0xa8, 0x92, 0xe0, 0xbe, 0xbd, 0x8b, 0x1d, 0x8d, +0x9f, 0x56, 0x76, 0xad, 0xaf, 0x29, 0xe2, 0xd9, 0xd5, 0x52, 0xf6, 0xb5, 0x56, 0x35, 0x57, +0x3a, 0xc8, 0xe1, 0x56, 0x43, 0x19, 0x94, 0xd3, 0x04, 0x9b, 0x6d, 0x35, 0xd8, 0x0b, 0x5f, +0x4d, 0x19, 0x8e, 0xec, 0xfa, 0x64, 0x91, 0x0a, 0x72, 0x20, 0x2b, 0xbc, 0x1a, 0x4a, 0xfe, +0x8b, 0xfd, 0xbb, 0xed, 0x1b, 0x23, 0xea, 0xad, 0x72, 0x82, 0xa1, 0x29, 0x99, 0x71, 0xbd, +0xf0, 0x95, 0xc1, 0x03, 0xdd, 0x7b, 0xc2, 0xb2, 0x3c, 0x28, 0x54, 0xd3, 0x68, 0xa4, 0x72, +0xc8, 0x66, 0x96, 0xe0, 0xd1, 0xd8, 0x7f, 0xf8, 0xd1, 0x26, 0x2b, 0xf7, 0xad, 0xba, 0x55, +0xca, 0x15, 0xb9, 0x32, 0xc3, 0xe5, 0x88, 0x97, 0x8e, 0x5c, 0xfb, 0x92, 0x25, 0x8b, 0xbf, +0xa2, 0x45, 0x55, 0x7a, 0xa7, 0x6f, 0x8b, 0x57, 0x5b, 0xcf, 0x0e, 0xcb, 0x1d, 0xfb, 0x20, +0x82, 0x77, 0xa8, 0x8c, 0xcc, 0x16, 0xce, 0x1d, 0xfa, 0xde, 0xcc, 0x0b, 0x62, 0xfe, 0xcc, +0xe1, 0xb7, 0xf0, 0xc3, 0x81, 0x64, 0x73, 0x40, 0xa0, 0xc2, 0x4d, 0x89, 0x11, 0x75, 0x33, +0x55, 0x33, 0x8d, 0xe8, 0x4a, 0xfd, 0xea, 0x6e, 0x30, 0x0b, 0xd7, 0x31, 0x2c, 0xde, 0x47, +0xe3, 0xbf, 0xf8, 0x55, 0x42, 0xe2, 0x7f, 0x59, 0xe5, 0x17, 0xef, 0x99, 0x34, 0x69, 0x91, +0xb1, 0x23, 0x8e, 0x20, 0x87, 0x2d, 0xa8, 0xfe, 0xd5, 0x8a, 0xf3, 0x84, 0x3a, 0xf0, 0x37, +0xe4, 0x09, 0x00, 0x54, 0xee, 0x67, 0x49, 0x93, 0xe4, 0x81, 0x70, 0xe3, 0x90, 0x4d, 0xef, +0xfe, 0x41, 0xb7, 0x99, 0x7b, 0xc1, 0x83, 0xba, 0x62, 0x12, 0x6f, 0x7d, 0xde, 0x6b, 0xaf, +0xda, 0x16, 0xf9, 0x55, 0x51, 0xee, 0xa6, 0x0c, 0x2b, 0x02, 0xa3, 0xfd, 0x8d, 0xfb, 0x30, +0x17, 0xe4, 0x6f, 0xdf, 0x36, 0x71, 0xc4, 0xca, 0x87, 0x25, 0x48, 0xb0, 0x47, 0xec, 0xea, +0xb4, 0xbf, 0xa5, 0x4d, 0x9b, 0x9f, 0x02, 0x93, 0xc4, 0xe3, 0xe4, 0xe8, 0x42, 0x2d, 0x68, +0x81, 0x15, 0x0a, 0xeb, 0x84, 0x5b, 0xd6, 0xa8, 0x74, 0xfb, 0x7d, 0x1d, 0xcb, 0x2c, 0xda, +0x46, 0x2a, 0x76, 0x62, 0xce, 0xbc, 0x5c, 0x9e, 0x8b, 0xe7, 0xcf, 0xbe, 0x78, 0xf5, 0x7c, +0xeb, 0xb3, 0x3a, 0x9c, 0xaa, 0x6f, 0xcc, 0x72, 0xd1, 0x59, 0xf2, 0x11, 0x23, 0xd6, 0x3f, +0x48, 0xd1, 0xb7, 0xce, 0xb0, 0xbf, 0xcb, 0xea, 0x80, 0xde, 0x57, 0xd4, 0x5e, 0x97, 0x2f, +0x75, 0xd1, 0x50, 0x8e, 0x80, 0x2c, 0x66, 0x79, 0xbf, 0x72, 0x4b, 0xbd, 0x8a, 0x81, 0x6c, +0xd3, 0xe1, 0x01, 0xdc, 0xd2, 0x15, 0x26, 0xc5, 0x36, 0xda, 0x2c, 0x1a, 0xc0, 0x27, 0x94, +0xed, 0xb7, 0x9b, 0x85, 0x0b, 0x5e, 0x80, 0x97, 0xc5, 0xec, 0x4f, 0xec, 0x88, 0x5d, 0x50, +0x07, 0x35, 0x47, 0xdc, 0x0b, 0x3b, 0x3d, 0xdd, 0x60, 0xaf, 0xa8, 0x5d, 0x81, 0x38, 0x24, +0x25, 0x5d, 0x5c, 0x15, 0xd1, 0xde, 0xb3, 0xab, 0xec, 0x05, 0x69, 0xef, 0x83, 0xed, 0x57, +0x54, 0xb8, 0x64, 0x64, 0x11, 0x16, 0x32, 0x69, 0xda, 0x9f, 0x2d, 0x7f, 0x36, 0xbb, 0x44, +0x5a, 0x34, 0xe8, 0x7f, 0xbf, 0x03, 0xeb, 0x00, 0x7f, 0x59, 0x68, 0x22, 0x79, 0xcf, 0x73, +0x6c, 0x2c, 0x29, 0xa7, 0xa1, 0x5f, 0x38, 0xa1, 0x1d, 0xf0, 0x20, 0x53, 0xe0, 0x1a, 0x63, +0x14, 0x58, 0x71, 0x10, 0xaa, 0x08, 0x0c, 0x3e, 0x16, 0x1a, 0x60, 0x22, 0x82, 0x7f, 0xba, +0xa4, 0x43, 0xa0, 0xd0, 0xac, 0x1b, 0xd5, 0x6b, 0x64, 0xb5, 0x14, 0x93, 0x31, 0x9e, 0x53, +0x50, 0xd0, 0x57, 0x66, 0xee, 0x5a, 0x4f, 0xfb, 0x03, 0x2a, 0x69, 0x58, 0x76, 0xf1, 0x83, +0xf7, 0x4e, 0xba, 0x8c, 0x42, 0x06, 0x60, 0x5d, 0x6d, 0xce, 0x60, 0x88, 0xae, 0xa4, 0xc3, +0xf1, 0x03, 0xa5, 0x4b, 0x98, 0xa1, 0xff, 0x67, 0xe1, 0xac, 0xa2, 0xb8, 0x62, 0xd7, 0x6f, +0xa0, 0x31, 0xb4, 0xd2, 0x77, 0xaf, 0x21, 0x10, 0x06, 0xc6, 0x9a, 0xff, 0x1d, 0x09, 0x17, +0x0e, 0x5f, 0xf1, 0xaa, 0x54, 0x34, 0x4b, 0x45, 0x8a, 0x87, 0x63, 0xa6, 0xdc, 0xf9, 0x24, +0x30, 0x67, 0xc6, 0xb2, 0xd6, 0x61, 0x33, 0x69, 0xee, 0x50, 0x61, 0x57, 0x28, 0xe7, 0x7e, +0xee, 0xec, 0x3a, 0x5a, 0x73, 0x4e, 0xa8, 0x8d, 0xe4, 0x18, 0xea, 0xec, 0x41, 0x64, 0xc8, +0xe2, 0xe8, 0x66, 0xb6, 0x2d, 0xb6, 0xfb, 0x6a, 0x6c, 0x16, 0xb3, 0xdd, 0x46, 0x43, 0xb9, +0x73, 0x00, 0x6a, 0x71, 0xed, 0x4e, 0x9d, 0x25, 0x1a, 0xc3, 0x3c, 0x4a, 0x95, 0x15, 0x99, +0x35, 0x81, 0x14, 0x02, 0xd6, 0x98, 0x9b, 0xec, 0xd8, 0x23, 0x3b, 0x84, 0x29, 0xaf, 0x0c, +0x99, 0x83, 0xa6, 0x9a, 0x34, 0x4f, 0xfa, 0xe8, 0xd0, 0x3c, 0x4b, 0xd0, 0xfb, 0xb6, 0x68, +0xb8, 0x9e, 0x8f, 0xcd, 0xf7, 0x60, 0x2d, 0x7a, 0x22, 0xe5, 0x7d, 0xab, 0x65, 0x1b, 0x95, +0xa7, 0xa8, 0x7f, 0xb6, 0x77, 0x47, 0x7b, 0x5f, 0x8b, 0x12, 0x72, 0xd0, 0xd4, 0x91, 0xef, +0xde, 0x19, 0x50, 0x3c, 0xa7, 0x8b, 0xc4, 0xa9, 0xb3, 0x23, 0xcb, 0x76, 0xe6, 0x81, 0xf0, +0xc1, 0x04, 0x8f, 0xa3, 0xb8, 0x54, 0x5b, 0x97, 0xac, 0x19, 0xff, 0x3f, 0x55, 0x27, 0x2f, +0xe0, 0x1d, 0x42, 0x9b, 0x57, 0xfc, 0x4b, 0x4e, 0x0f, 0xce, 0x98, 0xa9, 0x43, 0x57, 0x03, +0xbd, 0xe7, 0xc8, 0x94, 0xdf, 0x6e, 0x36, 0x73, 0x32, 0xb4, 0xef, 0x2e, 0x85, 0x7a, 0x6e, +0xfc, 0x6c, 0x18, 0x82, 0x75, 0x35, 0x90, 0x07, 0xf3, 0xe4, 0x9f, 0x3e, 0xdc, 0x68, 0xf3, +0xb5, 0xf3, 0x19, 0x80, 0x92, 0x06, 0x99, 0xa2, 0xe8, 0x6f, 0xff, 0x2e, 0x7f, 0xae, 0x42, +0xa4, 0x5f, 0xfb, 0xd4, 0x0e, 0x81, 0x2b, 0xc3, 0x04, 0xff, 0x2b, 0xb3, 0x74, 0x4e, 0x36, +0x5b, 0x9c, 0x15, 0x00, 0xc6, 0x47, 0x2b, 0xe8, 0x8b, 0x3d, 0xf1, 0x9c, 0x03, 0x9a, 0x58, +0x7f, 0x9b, 0x9c, 0xbf, 0x85, 0x49, 0x79, 0x35, 0x2e, 0x56, 0x7b, 0x41, 0x14, 0x39, 0x47, +0x83, 0x26, 0xaa, 0x07, 0x89, 0x98, 0x11, 0x1b, 0x86, 0xe7, 0x73, 0x7a, 0xd8, 0x7d, 0x78, +0x61, 0x53, 0xe9, 0x79, 0xf5, 0x36, 0x8d, 0x44, 0x92, 0x84, 0xf9, 0x13, 0x50, 0x58, 0x3b, +0xa4, 0x6a, 0x36, 0x65, 0x49, 0x8e, 0x3c, 0x0e, 0xf1, 0x6f, 0xd2, 0x84, 0xc4, 0x7e, 0x8e, +0x3f, 0x39, 0xae, 0x7c, 0x84, 0xf1, 0x63, 0x37, 0x8e, 0x3c, 0xcc, 0x3e, 0x44, 0x81, 0x45, +0xf1, 0x4b, 0xb9, 0xed, 0x6b, 0x36, 0x5d, 0xbb, 0x20, 0x60, 0x1a, 0x0f, 0xa3, 0xaa, 0x55, +0x77, 0x3a, 0xa9, 0xae, 0x37, 0x4d, 0xba, 0xb8, 0x86, 0x6b, 0xbc, 0x08, 0x50, 0xf6, 0xcc, +0xa4, 0xbd, 0x1d, 0x40, 0x72, 0xa5, 0x86, 0xfa, 0xe2, 0x10, 0xae, 0x3d, 0x58, 0x4b, 0x97, +0xf3, 0x43, 0x74, 0xa9, 0x9e, 0xeb, 0x21, 0xb7, 0x01, 0xa4, 0x86, 0x93, 0x97, 0xee, 0x2f, +0x4f, 0x3b, 0x86, 0xa1, 0x41, 0x6f, 0x41, 0x26, 0x90, 0x78, 0x5c, 0x7f, 0x30, 0x38, 0x4b, +0x3f, 0xaa, 0xec, 0xed, 0x5c, 0x6f, 0x0e, 0xad, 0x43, 0x87, 0xfd, 0x93, 0x35, 0xe6, 0x01, +0xef, 0x41, 0x26, 0x90, 0x99, 0x9e, 0xfb, 0x19, 0x5b, 0xad, 0xd2, 0x91, 0x8a, 0xe0, 0x46, +0xaf, 0x65, 0xfa, 0x4f, 0x84, 0xc1, 0xa1, 0x2d, 0xcf, 0x45, 0x8b, 0xd3, 0x85, 0x50, 0x55, +0x7c, 0xf9, 0x67, 0x88, 0xd4, 0x4e, 0xe9, 0xd7, 0x6b, 0x61, 0x54, 0xa1, 0xa4, 0xa6, 0xa2, +0xc2, 0xbf, 0x30, 0x9c, 0x40, 0x9f, 0x5f, 0xd7, 0x69, 0x2b, 0x24, 0x82, 0x5e, 0xd9, 0xd6, +0xa7, 0x12, 0x54, 0x1a, 0xf7, 0x55, 0x9f, 0x76, 0x50, 0xa9, 0x95, 0x84, 0xe6, 0x6b, 0x6d, +0xb5, 0x96, 0x54, 0xd6, 0xcd, 0xb3, 0xa1, 0x9b, 0x46, 0xa7, 0x94, 0x4d, 0xc4, 0x94, 0xb4, +0x98, 0xe3, 0xe1, 0xe2, 0x34, 0xd5, 0x33, 0x16, 0x07, 0x54, 0xcd, 0xb7, 0x77, 0x53, 0xdb, +0x4f, 0x4d, 0x46, 0x9d, 0xe9, 0xd4, 0x9c, 0x8a, 0x36, 0xb6, 0xb8, 0x38, 0x26, 0x6c, 0x0e, +0xff, 0x9c, 0x1b, 0x43, 0x8b, 0x80, 0xcc, 0xb9, 0x3d, 0xda, 0xc7, 0xf1, 0x8a, 0xf2, 0x6d, +0xb8, 0xd7, 0x74, 0x2f, 0x7e, 0x1e, 0xb7, 0xd3, 0x4a, 0xb4, 0xac, 0xfc, 0x79, 0x48, 0x6c, +0xbc, 0x96, 0xb6, 0x94, 0x46, 0x57, 0x2d, 0xb0, 0xa3, 0xfc, 0x1e, 0xb9, 0x52, 0x60, 0x85, +0x2d, 0x41, 0xd0, 0x43, 0x01, 0x1e, 0x1c, 0xd5, 0x7d, 0xfc, 0xf3, 0x96, 0x0d, 0xc7, 0xcb, +0x2a, 0x29, 0x9a, 0x93, 0xdd, 0x88, 0x2d, 0x37, 0x5d, 0xaa, 0xfb, 0x49, 0x68, 0xa0, 0x9c, +0x50, 0x86, 0x7f, 0x68, 0x56, 0x57, 0xf9, 0x79, 0x18, 0x39, 0xd4, 0xe0, 0x01, 0x84, 0x33, +0x61, 0xca, 0xa5, 0xd2, 0xd6, 0xe4, 0xc9, 0x8a, 0x4a, 0x23, 0x44, 0x4e, 0xbc, 0xf0, 0xdc, +0x24, 0xa1, 0xa0, 0xc4, 0xe2, 0x07, 0x3c, 0x10, 0xc4, 0xb5, 0x25, 0x4b, 0x65, 0x63, 0xf4, +0x80, 0xe7, 0xcf, 0x61, 0xb1, 0x71, 0x82, 0x21, 0x87, 0x2c, 0xf5, 0x91, 0x00, 0x32, 0x0c, +0xec, 0xa9, 0xb5, 0x9a, 0x74, 0x85, 0xe3, 0x36, 0x8f, 0x76, 0x4f, 0x9c, 0x6d, 0xce, 0xbc, +0xad, 0x0a, 0x4b, 0xed, 0x76, 0x04, 0xcb, 0xc3, 0xb9, 0x33, 0x9e, 0x01, 0x93, 0x96, 0x69, +0x7d, 0xc5, 0xa2, 0x45, 0x79, 0x9b, 0x04, 0x5c, 0x84, 0x09, 0xed, 0x88, 0x43, 0xc7, 0xab, +0x93, 0x14, 0x26, 0xa1, 0x40, 0xb5, 0xce, 0x4e, 0xbf, 0x2a, 0x42, 0x85, 0x3e, 0x2c, 0x3b, +0x54, 0xe8, 0x12, 0x1f, 0x0e, 0x97, 0x59, 0xb2, 0x27, 0x89, 0xfa, 0xf2, 0xdf, 0x8e, 0x68, +0x59, 0xdc, 0x06, 0xbc, 0xb6, 0x85, 0x0d, 0x06, 0x22, 0xec, 0xb1, 0xcb, 0xe5, 0x04, 0xe6, +0x3d, 0xb3, 0xb0, 0x41, 0x73, 0x08, 0x3f, 0x3c, 0x58, 0x86, 0x63, 0xeb, 0x50, 0xee, 0x1d, +0x2c, 0x37, 0x74, 0xa9, 0xd3, 0x18, 0xa3, 0x47, 0x6e, 0x93, 0x54, 0xad, 0x0a, 0x5d, 0xb8, +0x2a, 0x55, 0x5d, 0x78, 0xf6, 0xee, 0xbe, 0x8e, 0x3c, 0x76, 0x69, 0xb9, 0x40, 0xc2, 0x34, +0xec, 0x2a, 0xb9, 0xed, 0x7e, 0x20, 0xe4, 0x8d, 0x00, 0x38, 0xc7, 0xe6, 0x8f, 0x44, 0xa8, +0x86, 0xce, 0xeb, 0x2a, 0xe9, 0x90, 0xf1, 0x4c, 0xdf, 0x32, 0xfb, 0x73, 0x1b, 0x6d, 0x92, +0x1e, 0x95, 0xfe, 0xb4, 0xdb, 0x65, 0xdf, 0x4d, 0x23, 0x54, 0x89, 0x48, 0xbf, 0x4a, 0x2e, +0x70, 0xd6, 0xd7, 0x62, 0xb4, 0x33, 0x29, 0xb1, 0x3a, 0x33, 0x4c, 0x23, 0x6d, 0xa6, 0x76, +0xa5, 0x21, 0x63, 0x48, 0xe6, 0x90, 0x5d, 0xed, 0x90, 0x95, 0x0b, 0x7a, 0x84, 0xbe, 0xb8, +0x0d, 0x5e, 0x63, 0x0c, 0x62, 0x26, 0x4c, 0x14, 0x5a, 0xb3, 0xac, 0x23, 0xa4, 0x74, 0xa7, +0x6f, 0x33, 0x30, 0x05, 0x60, 0x01, 0x42, 0xa0, 0x28, 0xb7, 0xee, 0x19, 0x38, 0xf1, 0x64, +0x80, 0x82, 0x43, 0xe1, 0x41, 0x27, 0x1f, 0x1f, 0x90, 0x54, 0x7a, 0xd5, 0x23, 0x2e, 0xd1, +0x3d, 0xcb, 0x28, 0xba, 0x58, 0x7f, 0xdc, 0x7c, 0x91, 0x24, 0xe9, 0x28, 0x51, 0x83, 0x6e, +0xc5, 0x56, 0x21, 0x42, 0xed, 0xa0, 0x56, 0x22, 0xa1, 0x40, 0x80, 0x6b, 0xa8, 0xf7, 0x94, +0xca, 0x13, 0x6b, 0x0c, 0x39, 0xd9, 0xfd, 0xe9, 0xf3, 0x6f, 0xa6, 0x9e, 0xfc, 0x70, 0x8a, +0xb3, 0xbc, 0x59, 0x3c, 0x1e, 0x1d, 0x6c, 0xf9, 0x7c, 0xaf, 0xf9, 0x88, 0x71, 0x95, 0xeb, +0x57, 0x00, 0xbd, 0x9f, 0x8c, 0x4f, 0xe1, 0x24, 0x83, 0xc5, 0x22, 0xea, 0xfd, 0xd3, 0x0c, +0xe2, 0x17, 0x18, 0x7c, 0x6a, 0x4c, 0xde, 0x77, 0xb4, 0x53, 0x9b, 0x4c, 0x81, 0xcd, 0x23, +0x60, 0xaa, 0x0e, 0x25, 0x73, 0x9c, 0x02, 0x79, 0x32, 0x30, 0xdf, 0x74, 0xdf, 0x75, 0x19, +0xf4, 0xa5, 0x14, 0x5c, 0xf7, 0x7a, 0xa8, 0xa5, 0x91, 0x84, 0x7c, 0x60, 0x03, 0x06, 0x3b, +0xcd, 0x50, 0xb6, 0x27, 0x9c, 0xfe, 0xb1, 0xdd, 0xcc, 0xd3, 0xb0, 0x59, 0x24, 0xb2, 0xca, +0xe2, 0x1c, 0x81, 0x22, 0x9d, 0x07, 0x8f, 0x8e, 0xb9, 0xbe, 0x4e, 0xfa, 0xfc, 0x39, 0x65, +0xba, 0xbf, 0x9d, 0x12, 0x37, 0x5e, 0x97, 0x7e, 0xf3, 0x89, 0xf5, 0x5d, 0xf5, 0xe3, 0x09, +0x8c, 0x62, 0xb5, 0x20, 0x9d, 0x0c, 0x53, 0x8a, 0x68, 0x1b, 0xd2, 0x8f, 0x75, 0x17, 0x5d, +0xd4, 0xe5, 0xda, 0x75, 0x62, 0x19, 0x14, 0x6a, 0x26, 0x2d, 0xeb, 0xf8, 0xaf, 0x37, 0xf0, +0x6c, 0xa4, 0x55, 0xb1, 0xbc, 0xe2, 0x33, 0xc0, 0x9a, 0xca, 0xb0, 0x11, 0x49, 0x4f, 0x68, +0x9b, 0x3b, 0x6b, 0x3c, 0xcc, 0x13, 0xf6, 0xc7, 0x85, 0x61, 0x68, 0x42, 0xae, 0xbb, 0xdd, +0xcd, 0x45, 0x16, 0x29, 0x1d, 0xea, 0xdb, 0xc8, 0x03, 0x94, 0x3c, 0xee, 0x4f, 0x82, 0x11, +0xc3, 0xec, 0x28, 0xbd, 0x97, 0x05, 0x99, 0xde, 0xd7, 0xbb, 0x5e, 0x22, 0x1f, 0xd4, 0xeb, +0x64, 0xd9, 0x92, 0xd9, 0x85, 0xb7, 0x6a, 0x05, 0x6a, 0xe4, 0x24, 0x41, 0xf1, 0xcd, 0xf0, +0xd8, 0x3f, 0xf8, 0x9e, 0x0e, 0xcd, 0x0b, 0x7a, 0x70, 0x6b, 0x5a, 0x75, 0x0a, 0x6a, 0x33, +0x88, 0xec, 0x17, 0x75, 0x08, 0x70, 0x10, 0x2f, 0x24, 0xcf, 0xc4, 0xe9, 0x42, 0x00, 0x61, +0x94, 0xca, 0x1f, 0x3a, 0x76, 0x06, 0xfa, 0xd2, 0x48, 0x81, 0xf0, 0x77, 0x60, 0x03, 0x45, +0xd9, 0x61, 0xf4, 0xa4, 0x6f, 0x3d, 0xd9, 0x30, 0xc3, 0x04, 0x6b, 0x54, 0x2a, 0xb7, 0xec, +0x3b, 0xf4, 0x4b, 0xf5, 0x68, 0x52, 0x26, 0xce, 0xff, 0x5d, 0x19, 0x91, 0xa0, 0xa3, 0xa5, +0xa9, 0xb1, 0xe0, 0x23, 0xc4, 0x0a, 0x77, 0x4d, 0xf9, 0x51, 0x20, 0xa3, 0xa5, 0xa9, 0xb1, +0xc1, 0x00, 0x82, 0x86, 0x8e, 0x7f, 0x5d, 0x19, 0x91, 0xa0, 0xa3, 0xc4, 0xeb, 0x54, 0x0b, +0x75, 0x68, 0x52, 0x07, 0x8c, 0x9a, 0x97, 0x8d, 0x79, 0x70, 0x62, 0x46, 0xef, 0x5c, 0x1b, +0x95, 0x89, 0x71, 0x41, 0xe1, 0x21, 0xa1, 0xa1, 0xa1, 0xc0, 0x02, 0x67, 0x4c, 0x1a, 0xb6, +0xcf, 0xfd, 0x78, 0x53, 0x24, 0xab, 0xb5, 0xc9, 0xf1, 0x60, 0x23, 0xa5, 0xc8, 0x12, 0x87, +0x6d, 0x58, 0x13, 0x85, 0x88, 0x92, 0x87, 0x6d, 0x58, 0x32, 0xc7, 0x0c, 0x9a, 0x97, 0xac, +0xda, 0x36, 0xee, 0x5e, 0x3e, 0xdf, 0x1d, 0xb8, 0xf2, 0x66, 0x2f, 0xbd, 0xf8, 0x72, 0x47, +0xed, 0x58, 0x13, 0x85, 0x88, 0x92, 0x87, 0x8c, 0x7b, 0x55, 0x09, 0x90, 0xa2, 0xc6, 0xef, +0x3d, 0xf8, 0x53, 0x24, 0xab, 0xd4, 0x2a, 0xb7, 0xec, 0x5a, 0x36, 0xee, 0x5e, 0x3e, 0xdf, +0x3c, 0xfa, 0x76, 0x4f, 0xfd, 0x59, 0x30, 0xe2, 0x46, 0xef, 0x3d, 0xf8, 0x53, 0x05, 0x69, +0x31, 0xc1, 0x00, 0x82, 0x86, 0x8e, 0x7f, 0x5d, 0x19, 0xb0, 0xe2, 0x27, 0xcc, 0xfb, 0x74, +0x4b, 0x14, 0x8b, 0x94, 0x8b, 0x75, 0x68, 0x33, 0xc5, 0x08, 0x92, 0x87, 0x8c, 0x9a, 0xb6, +0xcf, 0x1c, 0xba, 0xd7, 0x0d, 0x98, 0xb2, 0xe6, 0x2f, 0xdc, 0x1b, 0x95, 0x89, 0x71, 0x60, +0x23, 0xc4, 0x0a, 0x96, 0x8f, 0x9c, 0xba, 0xf6, 0x6e, 0x3f, 0xfc, 0x5b, 0x15, 0xa8, 0xd2, +0x26, 0xaf, 0xbd, 0xf8, 0x72, 0x66, 0x2f, 0xdc, 0x1b, 0xb4, 0xcb, 0x14, 0x8b, 0x94, 0xaa, +0xb7, 0xcd, 0xf9, 0x51, 0x01, 0x80, 0x82, 0x86, 0x6f, 0x3d, 0xd9, 0x30, 0xe2, 0x27, 0xcc, +0xfb, 0x74, 0x4b, 0x14, 0xaa, 0xb7, 0xcd, 0xf9, 0x70, 0x43, 0x04, 0x6b, 0x35, 0xc9, 0xf1, +0x60, 0x23, 0xa5, 0xc8, 0xf3, 0x45, 0x08, 0x92, 0x87, 0x6d, 0x58, 0x32, 0xe6, 0x2f, 0xbd, +0xf8, 0x72, 0x66, 0x4e, 0x1e, 0xbe, 0xfe, 0x7e, 0x7e, 0x7e, 0x5f, 0x1d, 0x99, 0x91, 0xa0, +0xa3, 0xc4, 0x0a, 0x77, 0x4d, 0x18, 0x93, 0xa4, 0xab, 0xd4, 0x0b, 0x75, 0x49, 0x10, 0xa2, +0xc6, 0xef, 0x3d, 0xf8, 0x53, 0x24, 0xab, 0xb5, 0xe8, 0x33, 0xe4, 0x4a, 0x16, 0xae, 0xde, +0x1f, 0xbc, 0xdb, 0x15, 0xa8, 0xb3, 0xc5, 0x08, 0x73, 0x45, 0xe9, 0x31, 0xc1, 0xe1, 0x21, +0xa1, 0xa1, 0xa1, 0xc0, 0x02, 0x86, 0x6f, 0x5c, 0x3a, 0xd7, 0x0d, 0x98, 0x93, 0xa4, 0xca, +0x16, 0xae, 0xde, 0x1f, 0x9d, 0x99, 0xb0, 0xe2, 0x46, 0xef, 0x3d, 0xf8, 0x72, 0x47, 0x0c, +0x9a, 0xb6, 0xcf, 0xfd, 0x59, 0x11, 0xa0, 0xa3, 0xa5, 0xc8, 0xf3, 0x45, 0x08, 0x92, 0x87, +0x6d, 0x39, 0xf0, 0x43, 0x04, 0x8a, 0x96, 0xae, 0xde, 0x3e, 0xdf, 0x1d, 0x99, 0x91, 0xa0, +0xc2, 0x06, 0x6f, 0x3d, 0xf8, 0x72, 0x47, 0x0c, 0x9a, 0x97, 0x8d, 0x98, 0x93, 0x85, 0x88, +0x73, 0x45, 0xe9, 0x31, 0xe0, 0x23, 0xa5, 0xa9, 0xd0, 0x03, 0x84, 0x8a, 0x96, 0xae, 0xde, +0x1f, 0xbc, 0xdb, 0x15, 0xa8, 0xd2, 0x26, 0xce, 0xff, 0x5d, 0x19, 0x91, 0x81, 0x80, 0x82, +0x67, 0x2d, 0xd8, 0x13, 0xa4, 0xab, 0xd4, 0x0b, 0x94, 0xaa, 0xb7, 0xcd, 0xf9, 0x51, 0x20, +0xa3, 0xa5, 0xc8, 0xf3, 0x45, 0xe9, 0x50, 0x22, 0xc6, 0xef, 0x5c, 0x3a, 0xd7, 0x0d, 0x98, +0x93, 0x85, 0x88, 0x73, 0x64, 0x4a, 0xf7, 0x4d, 0xf9, 0x51, 0x20, 0xa3, 0xc4, 0x0a, 0x96, +0xae, 0xde, 0x3e, 0xfe, 0x7e, 0x7e, 0x7e, 0x5f, 0x3c, 0xfa, 0x76, 0x4f, 0xfd, 0x78, 0x72, +0x66, 0x2f, 0xbd, 0xd9, 0x30, 0xc3, 0xe5, 0x48, 0x12, 0x87, 0x8c, 0x7b, 0x55, 0x28, 0xd2, +0x07, 0x8c, 0x9a, 0x97, 0xac, 0xda, 0x17, 0x8d, 0x79, 0x51, 0x20, 0xa3, 0xc4, 0xeb, 0x54, +0x0b, 0x94, 0x8b, 0x94, 0xaa, 0xd6, 0x2e, 0xbf, 0xfc, 0x5b, 0x15, 0xa8, 0xd2, 0x26, 0xaf, +0xdc, 0x1b, 0xb4, 0xea, 0x37, 0xec, 0x3b, 0xf4, 0x6a, 0x37, 0xcd, 0x18, 0x93, 0x85, 0x69, +0x31, 0xc1, 0xe1, 0x40, 0xe3, 0x25, 0xc8, 0x12, 0x87, 0x8c, 0x9a, 0xb6, 0xcf, 0xfd, 0x59, +0x11, 0xa0, 0xc2, 0x06, 0x8e, 0x7f, 0x5d, 0x38, 0xf2, 0x47, 0x0c, 0x7b, 0x74, 0x6a, 0x37, +0xec, 0x5a, 0x36, 0xee, 0x3f, 0xfc, 0x7a, 0x76, 0x4f, 0x1c, 0x9b, 0x95, 0x89, 0x71, 0x41, +0x00, 0x63, 0x44, 0xeb, 0x54, 0x2a, 0xd6, 0x0f, 0x9c, 0xba, 0xd7, 0x0d, 0x98, 0x93, 0x85, +0x69, 0x31, 0xc1, 0x00, 0x82, 0x86, 0x8e, 0x9e, 0xbe, 0xdf, 0x3c, 0xfa, 0x57, 0x2c, 0xda, +0x36, 0xee, 0x3f, 0xfc, 0x5b, 0x15, 0x89, 0x71, 0x41, 0x00, 0x82, 0x86, 0x8e, 0x7f, 0x5d, +0x38, 0xf2, 0x47, 0xed, 0x58, 0x13, 0xa4, 0xca, 0xf7, 0x4d, 0xf9, 0x51, 0x01, 0x80, 0x63, +0x44, 0xeb, 0x54, 0x2a, 0xd6, 0x2e, 0xbf, 0xdd, 0x19, 0x91, 0xa0, 0xa3, 0xa5, 0xa9, 0xb1, +0xe0, 0x42, 0x06, 0x8e, 0x7f, 0x5d, 0x19, 0x91, 0xa0, 0xa3, 0xc4, 0x0a, 0x96, 0x8f, 0x7d, +0x78, 0x72, 0x47, 0x0c, 0x7b, 0x74, 0x6a, 0x56, 0x2e, 0xde, 0x1f, 0xbc, 0xfa, 0x57, 0x0d, +0x79, 0x51, 0x01, 0x61, 0x21, 0xa1, 0xc0, 0xe3, 0x25, 0xa9, 0xb1, 0xc1, 0xe1, 0x40, 0x02, +0x67, 0x4c, 0x1a, 0x97, 0x8d, 0x98, 0x93, 0xa4, 0xab, 0xd4, 0x2a, 0xd6, 0x0f, 0x9c, 0x9b, +0xb4, 0xcb, 0x14, 0xaa, 0xb7, 0xcd, 0xf9, 0x51, 0x20, 0xa3, 0xc4, 0xeb, 0x35, 0xc9, 0xf1, +0x60, 0x42, 0x06, 0x8e, 0x7f, 0x7c, 0x7a, 0x76, 0x6e, 0x3f, 0xfc, 0x7a, 0x76, 0x6e, 0x5e, +0x3e, 0xfe, 0x7e, 0x5f, 0x3c, 0xdb, 0x15, 0x89, 0x71, 0x41, 0xe1, 0x21, 0xc0, 0xe3, 0x44, +0xeb, 0x54, 0x2a, 0xb7, 0xcd, 0xf9, 0x70, 0x62, 0x27, 0xad, 0xd8, 0x32, 0xc7, 0x0c, 0x7b, +0x74, 0x4b, 0x14, 0xaa, 0xb7, 0xec, 0x3b, 0xd5, 0x28, 0xd2, 0x07, 0x6d, 0x39, 0xd1, 0x20, +0xc2, 0xe7, 0x4c, 0x1a, 0x97, 0x8d, 0x98, 0xb2, 0xc7, 0x0c, 0x59, 0x28, 0xf3, 0x9b }; #endif From 11305f5b52817bf78c99108f0096ab182e764e67 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Fri, 2 Sep 2022 12:49:09 +0800 Subject: [PATCH 073/157] backup --- app/drivers/sensor/paw3395/Kconfig | 11 + app/drivers/sensor/paw3395/paw3395.c | 496 ++++++++++++++-------- app/drivers/sensor/paw3395/paw3395.h | 129 +++--- app/drivers/sensor/paw3395/paw3395_priv.c | 21 +- 4 files changed, 396 insertions(+), 261 deletions(-) diff --git a/app/drivers/sensor/paw3395/Kconfig b/app/drivers/sensor/paw3395/Kconfig index 9a53d9f2751..e0d04101970 100644 --- a/app/drivers/sensor/paw3395/Kconfig +++ b/app/drivers/sensor/paw3395/Kconfig @@ -26,6 +26,17 @@ config PAW3395_CPI_DIVIDOR help Default CPI dividor value. +config PAW3395_REST_SAMPLE_TIME_MS + int "PAW3395's sample time in REST1 stage" + default 10 + range 1 255 + help + Default REST1 mode sample period in millisecond. + Smaller value means less power consumption. + Only sample time in REST1 is customizable. + The sample time in REST2 and REST3 is fixed to 100 ms + and 504 ms respectively. + config PAW3395_RUN_DOWNSHIFT_TIME_MS int "PAW3395's default RUN mode downshift time" default 500 diff --git a/app/drivers/sensor/paw3395/paw3395.c b/app/drivers/sensor/paw3395/paw3395.c index 7037b67e8ad..645c0debacd 100644 --- a/app/drivers/sensor/paw3395/paw3395.c +++ b/app/drivers/sensor/paw3395/paw3395.c @@ -13,9 +13,8 @@ #include LOG_MODULE_REGISTER(paw3395, CONFIG_PAW3395_LOG_LEVEL); -/* Timings defined by spec (in us), they are used in SPI communication and MCU should not do other tasks during waiting, thus using k_busy_wain instead of k_sleep */ -// sub-us time is rounded to us, due to the limitation of k_busy_wait -// see the discusssion here: https://github.com/zephyrproject-rtos/zephyr/issues/6498 +/* Timings (in us) used in SPI communication. Since MCU should not do other tasks during wait, k_busy_wait is used instead of k_sleep */ +// - sub-us time is rounded to us, due to the limitation of k_busy_wait, see : https://github.com/zephyrproject-rtos/zephyr/issues/6498 #define T_NCS_SCLK 1 /* 120 ns (rounded to 1us) */ #define T_SCLK_NCS_WR 1 /* 1 us */ #define T_SRAD 2 /* 2 us */ @@ -24,60 +23,71 @@ LOG_MODULE_REGISTER(paw3395, CONFIG_PAW3395_LOG_LEVEL); #define T_SWX 5 /* 5 us */ #define T_BEXIT 1 /* 500 ns (rounded to 1us)*/ -/* Sensor registers */ +/* Sensor registers (addresses) */ +// common registers as pmw3360 #define PAW3395_REG_PRODUCT_ID 0x00 #define PAW3395_REG_REVISION_ID 0x01 -#define PAW3395_REG_MOTION 0x02 +#define PAW3395_REG_MOTION 0x02 #define PAW3395_REG_DELTA_X_L 0x03 #define PAW3395_REG_DELTA_X_H 0x04 #define PAW3395_REG_DELTA_Y_L 0x05 #define PAW3395_REG_DELTA_Y_H 0x06 -#define PAW3395_REG_SQUAL 0x07 +#define PAW3395_REG_SQUAL 0x07 #define PAW3395_REG_RAW_DATA_SUM 0x08 #define PAW3395_REG_MAXIMUM_RAW_DATA 0x09 #define PAW3395_REG_MINIMUM_RAW_DATA 0x0A #define PAW3395_REG_SHUTTER_LOWER 0x0B #define PAW3395_REG_SHUTTER_UPPER 0x0C -#define PAW3395_REG_CONTROL 0x0D -#define PAW3395_REG_CONFIG1 0x0F -#define PAW3395_REG_CONFIG2 0x10 -#define PAW3395_REG_ANGLE_TUNE 0x11 -#define PAW3395_REG_FRAME_CAPTURE 0x12 -#define PAW3395_REG_SROM_ENABLE 0x13 -#define PAW3395_REG_RUN_DOWNSHIFT 0x14 -#define PAW3395_REG_REST1_RATE_LOWER 0x15 -#define PAW3395_REG_REST1_RATE_UPPER 0x16 -#define PAW3395_REG_REST1_DOWNSHIFT 0x17 -#define PAW3395_REG_REST2_RATE_LOWER 0x18 -#define PAW3395_REG_REST2_RATE_UPPER 0x19 -#define PAW3395_REG_REST2_DOWNSHIFT 0x1A -#define PAW3395_REG_REST3_RATE_LOWER 0x1B -#define PAW3395_REG_REST3_RATE_UPPER 0x1C -#define PAW3395_REG_OBSERVATION 0x24 -#define PAW3395_REG_DATA_OUT_LOWER 0x25 -#define PAW3395_REG_DATA_OUT_UPPER 0x26 -#define PAW3395_REG_RAW_DATA_DUMP 0x29 -#define PAW3395_REG_SROM_ID 0x2A -#define PAW3395_REG_MIN_SQ_RUN 0x2B -#define PAW3395_REG_RAW_DATA_THRESHOLD 0x2C -#define PAW3395_REG_CONFIG5 0x2F + +#define PAW3395_REG_OBSERVATION 0x15 +#define PAW3395_REG_MOTION_BURST 0x16 + +// power-up reset and shutdown registers #define PAW3395_REG_POWER_UP_RESET 0x3A -#define PAW3395_REG_SHUTDOWN 0x3B -#define PAW3395_REG_INVERSE_PRODUCT_ID 0x3F -#define PAW3395_REG_LIFTCUTOFF_TUNE3 0x41 -#define PAW3395_REG_ANGLE_SNAP 0x42 -#define PAW3395_REG_LIFTCUTOFF_TUNE1 0x4A -#define PAW3395_REG_MOTION_BURST 0x50 -#define PAW3395_REG_LIFTCUTOFF_TUNE_TIMEOUT 0x58 -#define PAW3395_REG_LIFTCUTOFF_TUNE_MIN_LENGTH 0x5A -#define PAW3395_REG_SROM_LOAD_BURST 0x62 -#define PAW3395_REG_LIFT_CONFIG 0x63 -#define PAW3395_REG_RAW_DATA_BURST 0x64 -#define PAW3395_REG_LIFTCUTOFF_TUNE2 0x65 +#define PAW3395_REG_SHUTDOWN 0x3B + +// rest mode register +#define PAW3395_REG_PERFORMANCE 0x40 + +// resolution/cpi registers +#define PAW3395_REG_SET_RESOLUTION 0x47 +#define PAW3395_REG_RESOLUTION_X_LOW 0x48 +#define PAW3395_REG_RESOLUTION_X_HIGH 0x49 +#define PAW3395_REG_RESOLUTION_Y_LOW 0x4A +#define PAW3395_REG_RESOLUTION_Y_HIGH 0x4B + +// other registers +#define PAW3395_REG_ANGLE_SNAP 0x56 +#define PAW3395_REG_RAWDATA_OUTPUT 0x58 +#define PAW3395_REG_RAWDATA_STATUS 0x59 +#define PAW3395_REG_RIPPLE_CONTROL 0x5A +#define PAW3395_REG_AXIS_CONTROL 0x5B +#define PAW3395_REG_MOTION_CONTROL 0x5C +#define PAW3395_REG_INVERSE_PRODUCT_ID 0x5F + +// rest mode related +#define PAW3395_REG_RUN_DOWNSHIFT 0x77 +#define PAW3395_REG_REST1_PERIOD 0x78 +#define PAW3395_REG_REST1_DOWNSHIFT 0x79 +#define PAW3395_REG_REST2_PERIOD 0x7A +#define PAW3395_REG_REST2_DOWNSHIFT 0x7B +#define PAW3395_REG_REST3_PERIOD 0x7C +#define PAW3395_REG_RUN_DOWNSHIFT_MULT 0x7D +#define PAW3395_REG_REST_DOWNSHIFT_MULT 0x7E + +// the following registers need special setting procedure +#define PAW3395_REG_ANGLE_TUNE1_H 0x05 +#define PAW3395_REG_ANGLE_TUNE1_L 0x77 +#define PAW3395_REG_ANGLE_TUNE2_H 0x05 +#define PAW3395_REG_ANGLE_TUNE2_L 0x78 +#define PAW3395_REG_LIFT_CONFIG_H 0x0C +#define PAW3395_REG_LIFT_CONFIG_L 0x4E + +// the mode register is used in run mode selection +#define PAW3395_REG_RUN_MODE 0x40 /* Sensor identification values */ -#define PAW3395_PRODUCT_ID 0x42 -#define PAW3395_FIRMWARE_ID 0x04 +#define PAW3395_PRODUCT_ID 0x51 /* Max register count readable in a single motion burst */ #define PAW3395_MAX_BURST_SIZE 12 @@ -89,13 +99,16 @@ LOG_MODULE_REGISTER(paw3395, CONFIG_PAW3395_LOG_LEVEL); #define PAW3395_DX_POS 2 #define PAW3395_DY_POS 4 -/* Rest_En position in Config2 register. */ -#define PAW3395_REST_EN_POS 5 - -#define PAW3395_MAX_CPI 12000 -#define PAW3395_MIN_CPI 100 +/* Rest_En position in Performance register. */ +#define PAW3395_REST_EN_POS 7 +/* cpi/resolution range */ +#define PAW3395_MAX_CPI 26000 +#define PAW3395_MIN_CPI 50 +#define PAW3395_SET_RESOLUTION_CMD 0x01 +#define PAW3395_RIPPLE_CONTROL_EN_POS 7 +/* write command bit position */ #define SPI_WRITE_BIT BIT(7) /* Helper macros used to convert sensor values. */ @@ -104,16 +117,32 @@ LOG_MODULE_REGISTER(paw3395, CONFIG_PAW3395_LOG_LEVEL); #define PAW3395_SVALUE_TO_BOOL(svalue) ((svalue).val1 != 0) -/* SROM firmware meta-data, defined in paw3395_piv.c */ -extern const size_t paw3395_firmware_length; -extern const uint8_t paw3395_firmware_data[]; +/* setting registers, defined in paw3395_priv.c */ +extern const size_t paw3395_pwrup_registers_length1; +extern const uint8_t paw3395_pwrup_registers_addr1[]; +extern const uint8_t paw3395_pwrup_registers_data1[]; +extern const size_t paw3395_pwrup_registers_length2; +extern const uint8_t paw3395_pwrup_registers_addr2[]; +extern const uint8_t paw3395_pwrup_registers_data2[]; + + +extern const size_t paw3395_mode_registers_length[]; +extern const uint8_t* paw3395_mode_registers_addr[]; +//////// Sensor initialization steps definition ////////// +// init is done in non-blocking manner (i.e., async), a // +// delayable work is defined for this purpose // + enum paw3395_init_step { + ASYNC_INIT_STEP_POWER_UP, // reset cs line and assert power-up reset + ASYNC_INIT_STEP_LOAD_SETTING, // load register setting + ASYNC_INIT_STEP_CONFIGURE, // set other registes like cpi and donwshift time (run, rest1, rest2) and clear motion registers -/* sensor initialization steps definition */ -// init is done in non-blocking manner (i.e., async), a delayable work is defined for this job -// see paw3395_init and paw3395_async_init) + ASYNC_INIT_STEP_COUNT // end flag + }; -// delay (ms) in between steps, MCU is allowed to do other tasks +/* Timings (in ms) needed in between steps to allow each step finishes succussfully. */ +// - Since MCU is not involved in the sensor init process, i is allowed to do other tasks. +// Thus, k_sleep or delayed schedule can be used. static const int32_t async_init_delay[ASYNC_INIT_STEP_COUNT] = { [ASYNC_INIT_STEP_POWER_UP] = 50, // required in spec [ASYNC_INIT_STEP_LOAD_SETTING] = 5, // required in spec @@ -130,6 +159,8 @@ static int (* const async_init_fn[ASYNC_INIT_STEP_COUNT])(const struct device *d [ASYNC_INIT_STEP_CONFIGURE] = paw3395_async_init_configure, }; +//////// Function definitions ////////// + // checked and keep static int spi_cs_ctrl(const struct device *dev, bool enable) { @@ -152,6 +183,7 @@ static int spi_cs_ctrl(const struct device *dev, bool enable) return err; } + // checked and keep static int reg_read(const struct device *dev, uint8_t reg, uint8_t *buf) { @@ -336,72 +368,151 @@ static int motion_burst_read(const struct device *dev, uint8_t *buf, } /** Writing an array of registers in sequence, used in power-up register initialization and running mode switching */ -// this impl is totally new, different from pmw3360 -static int burst_write(const struct device *dev, uint8_t *addr, const uint8_t *buf, - size_t size) +static int burst_write(const struct device *dev, uint8_t *addr, const uint8_t *buf, size_t size) { int err; struct paw3395_data *data = dev->data; const struct paw3395_config *config = dev->config; - /* Write address of burst register */ - uint8_t write_buf = reg | SPI_WRITE_BIT; - struct spi_buf tx_buf = { - .buf = &write_buf, - .len = 1 - }; - const struct spi_buf_set tx = { - .buffers = &tx_buf, - .count = 1 - }; - - err = spi_cs_ctrl(dev, true); - if (err) { - return err; - } - - err = spi_write_dt(&config->bus, &tx); - if (err) { - LOG_ERR("Burst write failed on SPI write"); - return err; - } - /* Write data */ for (size_t i = 0; i < size; i++) { - write_buf = buf[i]; + err = reg_write(dev, addr[i], buf[i]); - err = spi_write_dt(&config->bus, &tx); if (err) { LOG_ERR("Burst write failed on SPI write (data)"); return err; } - - k_busy_wait(T_BRSEP); } - /* Terminate burst mode. */ - err = spi_cs_ctrl(dev, false); - if (err) { - return err; - } + /* data->last_read_burst = false; */ - // todo: better keep it (no requirement in datasheet)? - k_busy_wait(T_BEXIT); + return 0; +} + +static int set_run_mode(const struct device *dev, enum paw3395_run_mode run_mode) +{ + int err; + uint32_t mode_idx = (uint32_t)run_mode; - data->last_read_burst = false; + if(mode_idx >= RUN_MODE_COUNT) { + LOG_ERR("Unknown attribute"); + return -ENOTSUP; + } - return 0; + // stage 1: write a series of registers + err = burst_write(dev, paw3395_mode_registers_addr[mode_idx],\ + paw3395_mode_registers_data[mode_idx],\ + paw3395_mode_registers_length[mode_idx]); + + // stage 2: read run_mode register and set corresponding bits + uint8_t value; + if(mode_idx == GAME_MODE) { + value = 0x83; + } + else{ + err = reg_read(dev, PAW3395_REG_RUN_MODE, &value); + if (err) { + LOG_ERR("Failed to read RUN_MODE register"); + return err; + } + } + + switch (mode_idx) { + case HP_MODE: + WRITE_BIT(value, 0, 0); + WRITE_BIT(value, 1, 0); + LOG_INF("Enable high-performance mode"); + break; + case LP_MODE: + WRITE_BIT(value, 0, 1); + WRITE_BIT(value, 1, 0); + LOG_INF("Enable low-performance mode"); + break; + case OFFICE_MODE: + WRITE_BIT(value, 0, 0); + WRITE_BIT(value, 1, 1); + LOG_INF("Enable office mode"); + break; + case GAME_MODE: + LOG_INF("Enable gaming mode"); + break; + default: + LOG_ERR("Unknown RUN mode"); + return -ENOTSUP; + } + + // stage 3: write back to run_mode register + err = reg_write(dev, PAW3395_REG_RUN_MODE, value); + if (err) { + LOG_ERR("Failed to set run mode"); + } + return err; +} + +static int upload_pwrup_settings(const struct device *dev) +{ + LOG_INF(""); + + // stage 1: configure the first 137 registers + int err; + err = burst_write(dev, paw3395_pwrup_registers_addr1, \ + paw3395_pwrup_registers_data1, paw3395_pwrup_registers_length1); + if(err) { + LOG_ERR("Can't setting first group of registers"); + return err; + } + + // stage 2: read register 0x6C at 1ms interval until value 0x80 is returned + // or timeout after 60 times + uint8_t value == 0x00; + int count = 0; + while( count < 60 ) { + // wait for 1ms befor read (timing accuracy 1% is required) + k_msleep(1); + + if (reg_read(dev, 0x6C, &value)) { + LOG_ERR("Failed to read register 0x6C"); + return err; + } + + if( value == 0x80 ) + break; + } + + // do some setting if 0x80 is not returned within 60 poll + if( value != 0x80 ) { + LOG_INF("vale 0x80 is not returned within 60 poll times"); + uint8_t addr[] = {0x7F, 0x6C, 0x7F}; + uint8_t data[] = {0x14, 0x00, 0x00}; + + err = burst_write(dev, addr, data, 3); + if(err) { + LOG_ERR("Can't setting the backup registers"); + return err; + } + } + + // stage 3: configure the remaining 5 rigisters + err = burst_write(dev, paw3395_pwrup_registers_addr2,\ + paw3395_pwrup_registers_data2, paw3395_pwrup_registers_length2); + if(err) { + LOG_ERR("Can't setting the second group of registers"); + return err; + } + + return 0; } -static int update_cpi(const struct device *dev, uint32_t cpi) +/* set cpi (x, y seperately) */ +static int set_cpi(const struct device *dev, uint32_t xcpi, uint32_t ycpi) { - /* Set resolution with CPI step of 100 cpi - * 0x00: 100 cpi (minimum cpi) - * 0x01: 200 cpi + /* Set resolution with CPI step of 50 cpi + * 0x0000: 50 cpi (minimum cpi) + * 0x0001: 100 cpi * : - * 0x31: 5000 cpi (default cpi) + * 0x0063: 5000 cpi (default cpi) * : - * 0x77: 12000 cpi (maximum cpi) + * 0x0207: 26000 cpi (maximum cpi) */ if ((cpi > PAW3395_MAX_CPI) || (cpi < PAW3395_MIN_CPI)) { @@ -409,22 +520,102 @@ static int update_cpi(const struct device *dev, uint32_t cpi) return -EINVAL; } - /* Convert CPI to register value */ - uint8_t value = (cpi / 100) - 1; + /* set x cpi */ + // Convert CPI to register value + uint16_t value = (xcpi / 50) - 1; + LOG_INF("Setting X-CPI to %u (reg value 0x%x)", xcpi, value); + + // seperate the two bytes + uint8_t buf[2]; + sys_put_le16(value, buf); + + // upload the new value + uint8_t addr[2] = {PAW3395_REG_RESOLUTION_X_LOW, PAW3395_REG_RESOLUTION_X_HIGH}; + int err = burst_write(dev, addr, buf, 2); + if (err) { + LOG_ERR("Failed to upload X CPI"); + } + + /* set y cpi */ + value = (ycpi / 50) - 1; + LOG_INF("Setting Y-CPI to %u (reg value 0x%x)", ycpi, value); + + sys_put_le16(value, buf); + addr[2] = {PAW3395_REG_RESOLUTION_Y_LOW, PAW3395_REG_RESOLUTION_Y_HIGH}; - LOG_INF("Setting CPI to %u (reg value 0x%x)", cpi, value); + err = burst_write(dev, addr, buf, 2); + if (err) { + LOG_ERR("Failed to upload Y CPI"); + } - int err = reg_write(dev, PAW3395_REG_CONFIG1, value); + /* set the cpi */ + if ( xcpi > 9000 || ycpi > 9000 ) { + LOG_INF("Enable ripple control, since cpi is too large"); + + err = reg_read(dev, PAW3395_REG_RIPPLE_CONTROL, buf); + if (err) { + LOG_ERR("Failed to read RIPPLE_CONTROL register"); + return err; + } + + WRITE_BIT(buf[0], PAW3395_RIPPLE_CONTROL_EN_POS, 1); + err = reg_write(dev, PAW3395_REG_RIPPLE_CONTROL, buf[0]); + if (err) { + LOG_ERR("Failed to enable ripple control"); + } + } + + /* set the cpi */ + err = reg_write(dev, PAW3395_REG_SET_RESOLUTION, 0x01); if (err) { - LOG_ERR("Failed to change CPI"); + LOG_ERR("Failed to set CPI"); } return err; } -/* unit: ms */ -static int update_downshift_time(const struct device *dev, uint8_t reg_addr, - uint32_t time) +/* set sampling rate in each mode (in ms) */ +// This function is implemented to be able to change the sample period of each rest mode. +// However, only rest1 sample period is exposed to end user and customizable through CONFIG_PAW3395_REST_SAMPLE_TIME_MS +// Sample periods in rest2 and rest3 are hard-coded (using the defaut value currently: 100ms and 504ms respectively) +static int set_sample_time(const struct device *dev, + uint8_t reg_addr_lower, + uint8_t reg_addr_upper, + uint32_t sample_time) +{ + /* Set sample time for the Rest1-Rest3 modes. + * Values above 0x09B0 will trigger internal watchdog reset. + */ + uint32_t maxtime = 0x9B0; + uint32_t mintime = 1; + + if ((sample_time > maxtime) || (sample_time < mintime)) { + LOG_WRN("Sample time %u out of range", sample_time); + return -EINVAL; + } + + LOG_INF("Set sample time to %u ms", sample_time); + + /* The sample time is (reg_value + 1) ms. */ + sample_time--; + uint8_t buf[2]; + + sys_put_le16((uint16_t)sample_time, buf); + + int err = reg_write(dev, reg_addr_lower, buf[0]); + + if (!err) { + err = reg_write(dev, reg_addr_upper, buf[1]); + } else { + LOG_ERR("Failed to change sample time"); + } + + return err; +} + + + +static int set_downshift_time(const struct device *dev, uint8_t reg_addr, uint32_t time) { /* Set downshift time in ms: * - Run downshift time (from Run to Rest1 mode), default: 500ms @@ -437,7 +628,7 @@ static int update_downshift_time(const struct device *dev, uint8_t reg_addr, switch (reg_addr) { case PAW3395_REG_RUN_DOWNSHIFT: /* - * Run downshift time = PAW3395_REG_RUN_DOWNSHIFT * 10 ms + * Run downshift time = PAW3395_REG_RUN_DOWNSHIFT * 256 * 0.05 ms */ maxtime = 2550; mintime = 10; @@ -486,54 +677,18 @@ static int update_downshift_time(const struct device *dev, uint8_t reg_addr, return err; } -/* set sampling rate in each mode (in ms) */ -static int update_sample_time(const struct device *dev, - uint8_t reg_addr_lower, - uint8_t reg_addr_upper, - uint32_t sample_time) -{ - /* Set sample time for the Rest1-Rest3 modes. - * Values above 0x09B0 will trigger internal watchdog reset. - */ - uint32_t maxtime = 0x9B0; - uint32_t mintime = 1; - - if ((sample_time > maxtime) || (sample_time < mintime)) { - LOG_WRN("Sample time %u out of range", sample_time); - return -EINVAL; - } - - LOG_INF("Set sample time to %u ms", sample_time); - - /* The sample time is (reg_value + 1) ms. */ - sample_time--; - uint8_t buf[2]; - - sys_put_le16((uint16_t)sample_time, buf); - - int err = reg_write(dev, reg_addr_lower, buf[0]); - - if (!err) { - err = reg_write(dev, reg_addr_upper, buf[1]); - } else { - LOG_ERR("Failed to change sample time"); - } - - return err; -} - -static int toggle_rest_modes(const struct device *dev, uint8_t reg_addr, - bool enable) +static int set_rest_mode(const struct device *dev, bool enable) { uint8_t value; - int err = reg_read(dev, reg_addr, &value); + int err = reg_read(dev, PAW3395_REG_PERFORMANCE, &value); if (err) { - LOG_ERR("Failed to read Config2 register"); + LOG_ERR("Failed to read PERFORMANCE register"); return err; } - WRITE_BIT(value, PAW3395_REST_EN_POS, enable); + // be aware: 0 is enable, 1 is disable + WRITE_BIT(value, PAW3395_REST_EN_POS, !enable); LOG_INF("%sable rest modes", (enable) ? ("En") : ("Dis")); err = reg_write(dev, reg_addr, value); @@ -644,22 +799,26 @@ static int paw3395_async_init_configure(const struct device *dev) { int err; - err = update_cpi(dev, CONFIG_PAW3395_CPI); + // cpi + err = set_cpi(dev, CONFIG_PAW3395_CPI); + + //todo: rest1 sample period, which affects scaling of rest1 downshift time + // downshift time for each rest mode if (!err) { - err = update_downshift_time(dev, + err = set_downshift_time(dev, PAW3395_REG_RUN_DOWNSHIFT, CONFIG_PAW3395_RUN_DOWNSHIFT_TIME_MS); } if (!err) { - err = update_downshift_time(dev, + err = set_downshift_time(dev, PAW3395_REG_REST1_DOWNSHIFT, CONFIG_PAW3395_REST1_DOWNSHIFT_TIME_MS); } if (!err) { - err = update_downshift_time(dev, + err = set_downshift_time(dev, PAW3395_REG_REST2_DOWNSHIFT, CONFIG_PAW3395_REST2_DOWNSHIFT_TIME_MS); } @@ -911,54 +1070,33 @@ static int paw3395_attr_set(const struct device *dev, enum sensor_channel chan, switch ((uint32_t)attr) { case PAW3395_ATTR_CPI: - err = update_cpi(dev, PAW3395_SVALUE_TO_CPI(*val)); + err = set_cpi(dev, PAW3395_SVALUE_TO_CPI(*val)); break; case PAW3395_ATTR_REST_ENABLE: - err = toggle_rest_modes(dev, + err = set_rest_modes(dev, PAW3395_REG_CONFIG2, PAW3395_SVALUE_TO_BOOL(*val)); break; case PAW3395_ATTR_RUN_DOWNSHIFT_TIME: - err = update_downshift_time(dev, + err = set_downshift_time(dev, PAW3395_REG_RUN_DOWNSHIFT, PAW3395_SVALUE_TO_TIME(*val)); break; case PAW3395_ATTR_REST1_DOWNSHIFT_TIME: - err = update_downshift_time(dev, + err = set_downshift_time(dev, PAW3395_REG_REST1_DOWNSHIFT, PAW3395_SVALUE_TO_TIME(*val)); break; case PAW3395_ATTR_REST2_DOWNSHIFT_TIME: - err = update_downshift_time(dev, + err = set_downshift_time(dev, PAW3395_REG_REST2_DOWNSHIFT, PAW3395_SVALUE_TO_TIME(*val)); break; - case PAW3395_ATTR_REST1_SAMPLE_TIME: - err = update_sample_time(dev, - PAW3395_REG_REST1_RATE_LOWER, - PAW3395_REG_REST1_RATE_UPPER, - PAW3395_SVALUE_TO_TIME(*val)); - break; - - case PAW3395_ATTR_REST2_SAMPLE_TIME: - err = update_sample_time(dev, - PAW3395_REG_REST2_RATE_LOWER, - PAW3395_REG_REST2_RATE_UPPER, - PAW3395_SVALUE_TO_TIME(*val)); - break; - - case PAW3395_ATTR_REST3_SAMPLE_TIME: - err = update_sample_time(dev, - PAW3395_REG_REST3_RATE_LOWER, - PAW3395_REG_REST3_RATE_UPPER, - PAW3395_SVALUE_TO_TIME(*val)); - break; - default: LOG_ERR("Unknown attribute"); return -ENOTSUP; diff --git a/app/drivers/sensor/paw3395/paw3395.h b/app/drivers/sensor/paw3395/paw3395.h index ff100456147..46966f1deac 100644 --- a/app/drivers/sensor/paw3395/paw3395.h +++ b/app/drivers/sensor/paw3395/paw3395.h @@ -18,15 +18,6 @@ extern "C" { #endif - // todo: update init steps - enum paw3395_init_step { - ASYNC_INIT_STEP_POWER_UP, // reset cs line and assert power-up reset - ASYNC_INIT_STEP_LOAD_SETTING, // load register setting - ASYNC_INIT_STEP_CONFIGURE, // set other registes like cpi and donwshift time (run, rest1, rest2) and clear motion registers - - ASYNC_INIT_STEP_COUNT // end flag - }; - enum paw3395_run_mode { HP_MODE, // high performance mode (the default mode using standard power-up register sets) LP_MODE, // low power mode @@ -36,79 +27,73 @@ extern "C" { RUN_MODE_COUNT // end flag }; -/* device data structure */ -struct paw3395_data { - const struct device *dev; - int16_t x; - int16_t y; - - // lock is needed to keep atomic of the trigger handler upadting - struct k_spinlock lock; - // motion interrupt isr - struct gpio_callback irq_gpio_cb; - // the actual trigger handler. This handler also used to flag whether resuming the motion interrupt line - sensor_trigger_handler_t data_ready_handler; - // the work structure holding the trigger handler job - struct k_work trigger_handler_work; - - // the work structure for delayable init steps - struct k_work_delayable init_work; - enum async_init_step async_init_step; - - // - bool ready; // whether init is finished successfully - bool last_read_burst; // todo: needed? - int err; // error code during async init - - /* the design of the driver is based on interrupt purely, to add polling upon it - the following work and timer maybe used in application code */ - struct k_work poll_work; - struct k_timer poll_timer; -}; - -struct paw3395_config { - struct spi_dt_spec bus; - struct gpio_dt_spec irq_gpio; - struct gpio_dt_spec cs_gpio; -}; + /** Sensor specific attributes of PAW3395. */ + enum paw3395_attribute { + /** Sensor CPI for both X and Y axes. */ + PAW3395_ATTR_CPI = SENSOR_ATTR_PRIV_START, -/** - * @defgroup paw3395 PAW3395 motion sensor driver - * @{ - * @brief PAW3395 motion sensor driver. - */ + /** Enable or disable sleep modes. */ + PAW3395_ATTR_REST_ENABLE, -/** @brief Sensor specific attributes of PAW3395. */ -enum paw3395_attribute { - /** Sensor CPI for both X and Y axes. */ - PAW3395_ATTR_CPI = SENSOR_ATTR_PRIV_START, + /** Entering time from Run mode to REST1 mode [ms]. */ + PAW3395_ATTR_RUN_DOWNSHIFT_TIME, - /** Enable or disable sleep modes. */ - PAW3395_ATTR_REST_ENABLE, + /** Entering time from REST1 mode to REST2 mode [ms]. */ + PAW3395_ATTR_REST1_DOWNSHIFT_TIME, - /** Entering time from Run mode to REST1 mode [ms]. */ - PAW3395_ATTR_RUN_DOWNSHIFT_TIME, + /** Entering time from REST2 mode to REST3 mode [ms]. */ + PAW3395_ATTR_REST2_DOWNSHIFT_TIME, - /** Entering time from REST1 mode to REST2 mode [ms]. */ - PAW3395_ATTR_REST1_DOWNSHIFT_TIME, + /** Sampling frequency time during REST1 mode [ms]. */ + PAW3395_ATTR_REST1_SAMPLE_TIME, - /** Entering time from REST2 mode to REST3 mode [ms]. */ - PAW3395_ATTR_REST2_DOWNSHIFT_TIME, + /** Sampling frequency time during REST2 mode [ms]. */ + PAW3395_ATTR_REST2_SAMPLE_TIME, - /** Sampling frequency time during REST1 mode [ms]. */ - PAW3395_ATTR_REST1_SAMPLE_TIME, + /** Sampling frequency time during REST3 mode [ms]. */ + PAW3395_ATTR_REST3_SAMPLE_TIME, - /** Sampling frequency time during REST2 mode [ms]. */ - PAW3395_ATTR_REST2_SAMPLE_TIME, - - /** Sampling frequency time during REST3 mode [ms]. */ - PAW3395_ATTR_REST3_SAMPLE_TIME, + /** Select the running mode. */ + // todo: implement it + PAW3395_ATTR_RUN_MODE, + }; - /** Select the running mode. */ - // todo: implement it - PAW3395_ATTR_RUNNING_MODE, -}; + /* device data structure */ + struct paw3395_data { + const struct device *dev; + int16_t x; + int16_t y; + + // lock is needed to keep atomic of the trigger handler upadting + struct k_spinlock lock; + // motion interrupt isr + struct gpio_callback irq_gpio_cb; + // the actual trigger handler. This handler also used to flag whether resuming the motion interrupt line + sensor_trigger_handler_t data_ready_handler; + // the work structure holding the trigger handler job + struct k_work trigger_handler_work; + + // the work structure for delayable init steps + struct k_work_delayable init_work; + enum async_init_step async_init_step; + + // + bool ready; // whether init is finished successfully + bool last_read_burst; // todo: needed? + int err; // error code during async init + + /* the design of the driver is based on interrupt purely, to add polling upon it + the following work and timer maybe used in application code */ + struct k_work poll_work; + struct k_timer poll_timer; + }; + /* device config structure */ + struct paw3395_config { + struct spi_dt_spec bus; + struct gpio_dt_spec irq_gpio; + struct gpio_dt_spec cs_gpio; + }; #ifdef __cplusplus } diff --git a/app/drivers/sensor/paw3395/paw3395_priv.c b/app/drivers/sensor/paw3395/paw3395_priv.c index b70343254d1..a2445400bcd 100644 --- a/app/drivers/sensor/paw3395/paw3395_priv.c +++ b/app/drivers/sensor/paw3395/paw3395_priv.c @@ -7,7 +7,7 @@ const size_t paw3395_pwrup_registers_length1 = 137; const size_t paw3395_pwrup_registers_length2 = 5; /* power up registers init: group1 */ -const uint8_t paw3395_pwrup_register_addr1[] = { +const uint8_t paw3395_pwrup_registers_addr1[] = { 0x7F, 0x40, 0x7F, 0x40, 0x7F, 0x55, 0x56, 0x57, 0x58, 0x7F, 0x42, 0x43, 0x4B, 0x4D, 0x53, 0x7F, 0x44, 0x4D, 0x51, 0x53, 0x55, 0x5A, 0x5B, 0x61, 0x62, 0x6D, 0x6E, 0x70, 0x4A, 0x60, @@ -23,7 +23,7 @@ const uint8_t paw3395_pwrup_register_addr1[] = { 0x52, 0x51, 0x54, 0x5A, 0x77, 0x47, 0x5B, 0x64, 0x65, 0x66, 0x67, 0x78, 0x79, 0x40, 0x55, 0x23, 0x22 }; -const uint8_t paw3395_pwrup_register_data1[] = { +const uint8_t paw3395_pwrup_registers_data1[] = { 0x07, 0x41, 0x00, 0x80, 0x0E, 0x0D, 0x1B, 0xE8, 0xD5, 0x14, 0xBC, 0x74, 0x20, 0x00, 0x0E, 0x05, 0x04, 0x06, 0x40, 0x40, 0xCA, 0xE8, 0xEA, 0x31, 0x64, 0xB8, 0x0F, 0x02, 0x2A, 0x26, @@ -41,20 +41,21 @@ const uint8_t paw3395_pwrup_register_data1[] = { }; /* power up registers init: group2 */ -const uint8_t paw3395_pwrup_register_addr2[] = { +const uint8_t paw3395_pwrup_registers_addr2[] = { 0x22, 0x55, 0x7F, 0x40, 0x7F }; -const uint8_t paw3395_pwrup_register_data2[] = { +const uint8_t paw3395_pwrup_registers_data2[] = { 0x00, 0x00, 0x07, 0x40, 0x00 }; ///////// Run mode registers ////////////////////// -const size_t paw3395_runmode_registers_length[RUN_MODE_COUNT] = { +const size_t paw3395_mode_registers_length[RUN_MODE_COUNT] = { [HP_MODE] = 21, [LP_MODE] = 21, [OFFICE_MODE] = 21, - [GAME_MODE] = 20, + [GAME_MODE] = 19, }; + /* hp mode registers */ const uint8_t paw3395_hp_register_addr[] = { 0x7F, 0x51, 0x53, 0x61, 0x6E, 0x7F, 0x42, 0x43, 0x7F, 0x51, @@ -94,21 +95,21 @@ const uint8_t paw3395_office_register_data[] = { /* game mode registers */ const uint8_t paw3395_game_register_addr[] = { 0x7F, 0x51, 0x53, 0x61, 0x6E, 0x7F, 0x42, 0x43, 0x7F, 0x51, - 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x7F, 0x54, 0x40 + 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x7F, 0x54 }; const uint8_t paw3395_game_register_data[] = { 0x05, 0x40, 0x40, 0x31, 0x0F, 0x07, 0x2F, 0x00, 0x0D, 0x12, - 0xDB, 0x12, 0xDC, 0x12, 0xEA, 0x15, 0x2D, 0x00, 0x55, 0x83 + 0xDB, 0x12, 0xDC, 0x12, 0xEA, 0x15, 0x2D, 0x00, 0x55 }; /* aggregation of all run modes registers */ -const uint8_t* paw3395_rumode_register_addr[] = { +const uint8_t* paw3395_mode_registers_addr[] = { [HP_MODE] = paw3395_hp_register_addr, [LP_MODE] = paw3395_lp_register_addr, [OFFICE_MODE] = paw3395_office_register_addr, [GAME_MODE] = paw3395_game_register_addr, }; -const uint8_t* paw3395_rumode_register_data[] = { +const uint8_t* paw3395_mode_registers_data[] = { [HP_MODE] = paw3395_hp_register_data, [LP_MODE] = paw3395_lp_register_data, [OFFICE_MODE] = paw3395_office_register_data, From 1f221061a8aca474fb9491695ac569a0cfa52e24 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Fri, 2 Sep 2022 20:24:21 +0800 Subject: [PATCH 074/157] backup --- app/drivers/sensor/paw3395/Kconfig | 63 +++- app/drivers/sensor/paw3395/paw3395.c | 546 +++++++++++++++------------ app/drivers/sensor/paw3395/paw3395.h | 6 +- 3 files changed, 356 insertions(+), 259 deletions(-) diff --git a/app/drivers/sensor/paw3395/Kconfig b/app/drivers/sensor/paw3395/Kconfig index e0d04101970..d27fe2f037a 100644 --- a/app/drivers/sensor/paw3395/Kconfig +++ b/app/drivers/sensor/paw3395/Kconfig @@ -12,51 +12,86 @@ menuconfig PAW3395 if PAW3395 -config PAW3395_CPI - int "PAW3395's default CPI" +config PAW3395_ENABLE_REST + bool "Enable the rest mode of PAW3395" + default y + help + The sensor downshift to rest mode with lower sampling rate + and frame rate. This saves power. + +config PAW3395_RUN_MODE + int "PAW3395's default run mode when power-on" + default 0 + help + The power-on run mode of PAW3395. + 0: high-performance mode + 1: low-performance mode + 2: office mode + 3: gaming mode + +config PAW3395_X_CPI + int "PAW3395's default CPI in x-axis" default 1600 - range 100 12000 + range 50 9000 help - Default CPI value. + Default X-CPI value. + +config PAW3395_Y_CPI + int "PAW3395's default CPI in y-axis" + default 1600 + range 50 9000 + help + Default Y-CPI value. config PAW3395_CPI_DIVIDOR int "PAW3395's default CPI dividor" default 4 - range 1 12 + range 1 100 help Default CPI dividor value. -config PAW3395_REST_SAMPLE_TIME_MS +config PAW3395_REST1_SAMPLE_TIME_MS int "PAW3395's sample time in REST1 stage" default 10 range 1 255 help Default REST1 mode sample period in millisecond. Smaller value means less power consumption. - Only sample time in REST1 is customizable. - The sample time in REST2 and REST3 is fixed to 100 ms - and 504 ms respectively. + +config PAW3395_REST2_SAMPLE_TIME_MS + int "PAW3395's sample time in REST2 stage" + default 100 + range 4 1020 + help + Default REST2 mode sample period in millisecond. + Smaller value means less power consumption. + +config PAW3395_REST3_SAMPLE_TIME_MS + int "PAW3395's sample time in REST3 stage" + default 504 + range 8 2040 + help + Default REST2 mode sample period in millisecond. + Smaller value means less power consumption. config PAW3395_RUN_DOWNSHIFT_TIME_MS int "PAW3395's default RUN mode downshift time" default 500 - range 10 2550 + range 13 3264 help Default RUN mode downshift down time in milliseconds. Time after which sensor goes from RUN to REST1 mode. config PAW3395_REST1_DOWNSHIFT_TIME_MS int "PAW3395's default REST1 mode downshift time" - default 9220 - range 320 81600 + default 10000 help Default REST1 mode downshift down time in milliseconds. Time after which sensor goes from REST1 to REST2 mode. config PAW3395_REST2_DOWNSHIFT_TIME_MS int "PAW3395's default REST2 mode downshift time" - default 150000 - range 3200 816000 + default 600000 help Default REST2 mode downshift down time in milliseconds. Time after which sensor goes from REST2 to REST3 mode. diff --git a/app/drivers/sensor/paw3395/paw3395.c b/app/drivers/sensor/paw3395/paw3395.c index 645c0debacd..e02841dad7a 100644 --- a/app/drivers/sensor/paw3395/paw3395.c +++ b/app/drivers/sensor/paw3395/paw3395.c @@ -114,6 +114,7 @@ LOG_MODULE_REGISTER(paw3395, CONFIG_PAW3395_LOG_LEVEL); /* Helper macros used to convert sensor values. */ #define PAW3395_SVALUE_TO_CPI(svalue) ((uint32_t)(svalue).val1) #define PAW3395_SVALUE_TO_TIME(svalue) ((uint32_t)(svalue).val1) +#define PAW3395_SVALUE_TO_RUNMODE(svalue) ((uint32_t)(svalue).val1) #define PAW3395_SVALUE_TO_BOOL(svalue) ((svalue).val1 != 0) @@ -389,6 +390,73 @@ static int burst_write(const struct device *dev, uint8_t *addr, const uint8_t *b return 0; } +static int upload_pwrup_settings(const struct device *dev) +{ + LOG_INF(""); + + // stage 1: configure the first 137 registers + int err; + err = burst_write(dev, paw3395_pwrup_registers_addr1, \ + paw3395_pwrup_registers_data1, paw3395_pwrup_registers_length1); + if(err) { + LOG_ERR("Can't setting first group of registers"); + return err; + } + + // stage 2: read register 0x6C at 1ms interval until value 0x80 is returned + // or timeout after 60 times + uint8_t value == 0x00; + int count = 0; + while( count < 60 ) { + // wait for 1ms befor read (timing accuracy 1% is required) + k_msleep(1); + + if (reg_read(dev, 0x6C, &value)) { + LOG_ERR("Failed to read register 0x6C"); + return err; + } + + if( value == 0x80 ) + break; + } + + // do some setting if 0x80 is not returned within 60 poll + if( value != 0x80 ) { + LOG_INF("vale 0x80 is not returned within 60 poll times"); + uint8_t addr[] = {0x7F, 0x6C, 0x7F}; + uint8_t data[] = {0x14, 0x00, 0x00}; + + err = burst_write(dev, addr, data, 3); + if(err) { + LOG_ERR("Can't setting the backup registers"); + return err; + } + } + + // stage 3: configure the remaining 5 rigisters + err = burst_write(dev, paw3395_pwrup_registers_addr2,\ + paw3395_pwrup_registers_data2, paw3395_pwrup_registers_length2); + if(err) { + LOG_ERR("Can't setting the second group of registers"); + return err; + } + + // stage 4: check the product id + uint8_t product_id; + err = reg_read(dev, PAW3395_REG_PRODUCT_ID, &product_id); + if (err) { + LOG_ERR("Cannot obtain product id"); + return err; + } + + if (product_id != PAW3395_PRODUCT_ID) { + LOG_ERR("Invalid product id!"); + return -EIO; + } + + return 0; +} + static int set_run_mode(const struct device *dev, enum paw3395_run_mode run_mode) { int err; @@ -449,62 +517,8 @@ static int set_run_mode(const struct device *dev, enum paw3395_run_mode run_mode return err; } -static int upload_pwrup_settings(const struct device *dev) -{ - LOG_INF(""); - - // stage 1: configure the first 137 registers - int err; - err = burst_write(dev, paw3395_pwrup_registers_addr1, \ - paw3395_pwrup_registers_data1, paw3395_pwrup_registers_length1); - if(err) { - LOG_ERR("Can't setting first group of registers"); - return err; - } - - // stage 2: read register 0x6C at 1ms interval until value 0x80 is returned - // or timeout after 60 times - uint8_t value == 0x00; - int count = 0; - while( count < 60 ) { - // wait for 1ms befor read (timing accuracy 1% is required) - k_msleep(1); - - if (reg_read(dev, 0x6C, &value)) { - LOG_ERR("Failed to read register 0x6C"); - return err; - } - - if( value == 0x80 ) - break; - } - - // do some setting if 0x80 is not returned within 60 poll - if( value != 0x80 ) { - LOG_INF("vale 0x80 is not returned within 60 poll times"); - uint8_t addr[] = {0x7F, 0x6C, 0x7F}; - uint8_t data[] = {0x14, 0x00, 0x00}; - - err = burst_write(dev, addr, data, 3); - if(err) { - LOG_ERR("Can't setting the backup registers"); - return err; - } - } - - // stage 3: configure the remaining 5 rigisters - err = burst_write(dev, paw3395_pwrup_registers_addr2,\ - paw3395_pwrup_registers_data2, paw3395_pwrup_registers_length2); - if(err) { - LOG_ERR("Can't setting the second group of registers"); - return err; - } - - return 0; -} - -/* set cpi (x, y seperately) */ -static int set_cpi(const struct device *dev, uint32_t xcpi, uint32_t ycpi) +/* set cpi (x, y seperately): axis true for x, false for y */ +static int set_cpi(const struct device *dev, uint32_t cpi, bool_t axis) { /* Set resolution with CPI step of 50 cpi * 0x0000: 50 cpi (minimum cpi) @@ -520,36 +534,29 @@ static int set_cpi(const struct device *dev, uint32_t xcpi, uint32_t ycpi) return -EINVAL; } - /* set x cpi */ // Convert CPI to register value - uint16_t value = (xcpi / 50) - 1; - LOG_INF("Setting X-CPI to %u (reg value 0x%x)", xcpi, value); + uint16_t value = (cpi / 50) - 1; + LOG_INF("Setting %s-CPI to %u (reg value 0x%x)", axis ? "X" : "Y", cpi, value); // seperate the two bytes uint8_t buf[2]; sys_put_le16(value, buf); - // upload the new value - uint8_t addr[2] = {PAW3395_REG_RESOLUTION_X_LOW, PAW3395_REG_RESOLUTION_X_HIGH}; - int err = burst_write(dev, addr, buf, 2); - if (err) { - LOG_ERR("Failed to upload X CPI"); - } - - /* set y cpi */ - value = (ycpi / 50) - 1; - LOG_INF("Setting Y-CPI to %u (reg value 0x%x)", ycpi, value); - - sys_put_le16(value, buf); - addr[2] = {PAW3395_REG_RESOLUTION_Y_LOW, PAW3395_REG_RESOLUTION_Y_HIGH}; - - err = burst_write(dev, addr, buf, 2); - if (err) { - LOG_ERR("Failed to upload Y CPI"); - } + // upload the new value + if (axis) { // x-cpi + uint8_t addr[2] = {PAW3395_REG_RESOLUTION_X_LOW, PAW3395_REG_RESOLUTION_X_HIGH}; + int err = burst_write(dev, addr, buf, 2); + } + else { // y-cpi + uint8_t addr[2] = {PAW3395_REG_RESOLUTION_Y_LOW, PAW3395_REG_RESOLUTION_Y_HIGH}; + int err = burst_write(dev, addr, buf, 2); + } + if (err) { + LOG_ERR("Failed to upload %s-CPI", axis ? "X" : "Y"); + } /* set the cpi */ - if ( xcpi > 9000 || ycpi > 9000 ) { + if ( cpi > 9000 ) { LOG_INF("Enable ripple control, since cpi is too large"); err = reg_read(dev, PAW3395_REG_RIPPLE_CONTROL, buf); @@ -574,39 +581,38 @@ static int set_cpi(const struct device *dev, uint32_t xcpi, uint32_t ycpi) return err; } -/* set sampling rate in each mode (in ms) */ -// This function is implemented to be able to change the sample period of each rest mode. -// However, only rest1 sample period is exposed to end user and customizable through CONFIG_PAW3395_REST_SAMPLE_TIME_MS -// Sample periods in rest2 and rest3 are hard-coded (using the defaut value currently: 100ms and 504ms respectively) -static int set_sample_time(const struct device *dev, - uint8_t reg_addr_lower, - uint8_t reg_addr_upper, - uint32_t sample_time) +/* Set sampling rate in each mode (in ms) */ +static int set_sample_time(const struct device *dev, uint8_t reg_addr, uint32_t sample_time) { - /* Set sample time for the Rest1-Rest3 modes. - * Values above 0x09B0 will trigger internal watchdog reset. - */ - uint32_t maxtime = 0x9B0; - uint32_t mintime = 1; + uint32_t maxtime, mintime; + switch (reg_addr) { + case PAW3395_REG_REST1_PERIOD: + mintime = 1; + maxtime = 255; + break; + case PAW3395_REG_REST2_PERIOD: + mintime = 4; + maxtime = 4*255; + break; + case PAW3395_REG_REST3_PERIOD: + mintime = 8; + maxtime = 8*255; + break; + default: + LOG_WRN("unrecognizable rest mode register"); + return -ENOTSUP; + } if ((sample_time > maxtime) || (sample_time < mintime)) { - LOG_WRN("Sample time %u out of range", sample_time); + LOG_WRN("Sample time %u out of range [%u, %u]", sample_time, mintime, maxtime); return -EINVAL; } LOG_INF("Set sample time to %u ms", sample_time); - /* The sample time is (reg_value + 1) ms. */ - sample_time--; - uint8_t buf[2]; - - sys_put_le16((uint16_t)sample_time, buf); - - int err = reg_write(dev, reg_addr_lower, buf[0]); - + /* The sample time is (reg_value * mintime ) ms. 0x00 is rounded to 0x1 */ + int err = reg_write(dev, reg_addr, (uint8_t)(sample_time / mintime)); if (!err) { - err = reg_write(dev, reg_addr_upper, buf[1]); - } else { LOG_ERR("Failed to change sample time"); } @@ -614,12 +620,12 @@ static int set_sample_time(const struct device *dev, } - +/* time unit: ms */ static int set_downshift_time(const struct device *dev, uint8_t reg_addr, uint32_t time) { /* Set downshift time in ms: - * - Run downshift time (from Run to Rest1 mode), default: 500ms - * - Rest 1 downshift time (from Rest1 to Rest2 mode), default: 9.92 s + * - Run downshift time (from Run to Rest1 mode), default: 1 s + * - Rest 1 downshift time (from Rest1 to Rest2 mode), default: ~10 s * - Rest 2 downshift time (from Rest2 to Rest3 mode), default: ~10 min */ uint32_t maxtime; @@ -630,26 +636,26 @@ static int set_downshift_time(const struct device *dev, uint8_t reg_addr, uint32 /* * Run downshift time = PAW3395_REG_RUN_DOWNSHIFT * 256 * 0.05 ms */ - maxtime = 2550; - mintime = 10; + maxtime = 3264; + mintime = 13; // real value is 12.8, rounded to 13 break; case PAW3395_REG_REST1_DOWNSHIFT: /* * Rest1 downshift time = PAW3395_REG_RUN_DOWNSHIFT - * * 320 * Rest1 rate (default 1 ms) + * * 64 * Rest1_sample_period (default 1 ms) */ - maxtime = 81600; - mintime = 320; + maxtime = 255 * 64 * CONFIG_PAW3395_REST1_SAMPLE_TIME_MS; + mintime = 64 * CONFIG_PAW3395_REST1_SAMPLE_TIME_MS; break; case PAW3395_REG_REST2_DOWNSHIFT: /* * Rest2 downshift time = PAW3395_REG_REST2_DOWNSHIFT - * * 32 * Rest2 rate (default 100 ms) + * * 64 * Rest2 rate (default 100 ms) */ - maxtime = 816000; - mintime = 3200; + maxtime = 255 * 64 * CONFIG_PAW3395_REST2_SAMPLE_TIME_MS; + mintime = 64 * CONFIG_PAW3395_REST2_SAMPLE_TIME_MS; break; default: @@ -700,109 +706,136 @@ static int set_rest_mode(const struct device *dev, bool enable) return err; } -static int paw3395_async_init_load_setting(const struct device *dev) +static int paw3395_attr_set(const struct device *dev, enum sensor_channel chan, + enum sensor_attribute attr, + const struct sensor_value *val) { + struct paw3395_data *data = dev->data; int err; - LOG_INF("Uploading optical sensor firmware..."); - - /* Write 0x18 to SROM_enable to start SROM download */ - err = reg_write(dev, PAW3395_REG_SROM_ENABLE, 0x18); - if (err) { - LOG_ERR("Cannot start SROM download"); - return err; + if (unlikely(chan != SENSOR_CHAN_ALL)) { + return -ENOTSUP; } - /* Write SROM file into SROM_Load_Burst register. - * Data must start with SROM_Load_Burst address. - */ - err = burst_write(dev, PAW3395_REG_SROM_LOAD_BURST, - paw3395_firmware_data, paw3395_firmware_length); - if (err) { - LOG_ERR("Cannot write firmware to sensor"); + if (unlikely(!data->ready)) { + LOG_DBG("Device is not initialized yet"); + return -EBUSY; } - return err; -} - -static void irq_handler(const struct device *gpiob, struct gpio_callback *cb, - uint32_t pins) -{ - int err; - struct paw3395_data *data = CONTAINER_OF(cb, struct paw3395_data, - irq_gpio_cb); - const struct device *dev = data->dev; - const struct paw3395_config *config = dev->config; + switch ((uint32_t)attr) { + case PAW3395_ATTR_XCPI: + err = set_cpi(dev, PAW3395_SVALUE_TO_CPI(*val), true); + break; + case PAW3395_ATTR_YCPI: + err = set_cpi(dev, PAW3395_SVALUE_TO_CPI(*val), false); + break; - // disable the interrupt line first - err = gpio_pin_interrupt_configure_dt(&config->irq_gpio, - GPIO_INT_DISABLE); - if (unlikely(err)) { - LOG_ERR("Cannot disable IRQ"); - k_panic(); - } + case PAW3395_ATTR_REST_ENABLE: + err = set_rest_modes(dev, PAW3395_SVALUE_TO_BOOL(*val)); + break; - // submit the real handler work - k_work_submit(&data->trigger_handler_work); -} + case PAW3395_ATTR_RUN_DOWNSHIFT_TIME: + err = set_downshift_time(dev, + PAW3395_REG_RUN_DOWNSHIFT, + PAW3395_SVALUE_TO_TIME(*val)); + break; -static void trigger_handler(struct k_work *work) -{ - sensor_trigger_handler_t handler; - int err = 0; - struct paw3395_data *data = CONTAINER_OF(work, struct paw3395_data, - trigger_handler_work); - const struct device *dev = data->dev; - const struct paw3395_config *config = dev->config; + case PAW3395_ATTR_REST1_DOWNSHIFT_TIME: + err = set_downshift_time(dev, + PAW3395_REG_REST1_DOWNSHIFT, + PAW3395_SVALUE_TO_TIME(*val)); + break; - // 1. the first lock period is used to procoss the trigger - // if data_ready_handler is non-NULL, otherwise do nothing - k_spinlock_key_t key = k_spin_lock(&data->lock); + case PAW3395_ATTR_REST2_DOWNSHIFT_TIME: + err = set_downshift_time(dev, + PAW3395_REG_REST2_DOWNSHIFT, + PAW3395_SVALUE_TO_TIME(*val)); + break; - handler = data->data_ready_handler; - k_spin_unlock(&data->lock, key); + case PAW3395_ATTR_REST1_SAMPLE_TIME: + err = set_sample_time(dev, + PAW3395_REG_REST1_PERIOD, + PAW3395_SVALUE_TO_TIME(*val)); + break; - if (!handler) { - return; - } + case PAW3395_ATTR_REST2_SAMPLE_TIME: + err = set_sample_time(dev, + PAW3395_REG_REST2_PERIOD, + PAW3395_SVALUE_TO_TIME(*val)); + break; - struct sensor_trigger trig = { - .type = SENSOR_TRIG_DATA_READY, - .chan = SENSOR_CHAN_ALL, - }; + case PAW3395_ATTR_REST3_SAMPLE_TIME: + err = set_sample_time(dev, + PAW3395_REG_REST3_PERIOD, + PAW3395_SVALUE_TO_TIME(*val)); + break; - handler(dev, &trig); + case PAW3395_ATTR_RUN_MODE: + err = set_run_mode(dev, + PAW3395_SVALUE_TO_RUNMODE(*val)); + break; - // 2. the second lock period is used to resume the interrupt line - // if data_ready_handler is non-NULL, otherwise keep it inactive - key = k_spin_lock(&data->lock); - if (data->data_ready_handler) { - err = gpio_pin_interrupt_configure_dt(&config->irq_gpio, - GPIO_INT_LEVEL_ACTIVE); + default: + LOG_ERR("Unknown attribute"); + return -ENOTSUP; } - k_spin_unlock(&data->lock, key); - if (unlikely(err)) { - LOG_ERR("Cannot re-enable IRQ"); - k_panic(); - } + return err; } static int paw3395_async_init_power_up(const struct device *dev) { - /* Reset sensor */ + /* needed? Reset spi port */ + spi_cs_ctrl(dev, false); + spi_cs_ctrl(dev, true); + /* Reset sensor */ return reg_write(dev, PAW3395_REG_POWER_UP_RESET, 0x5A); } +static int paw3395_async_init_load_setting(const struct device *dev) +{ + int err; + + LOG_INF("Uploading optical sensor firmware..."); + err = upload_pwrup_settings(dev); + + return err; +} + static int paw3395_async_init_configure(const struct device *dev) { int err; + // run mode + err = set_run_mode(dev, CONFIG_PAW3395_RUN_MODE); + // cpi - err = set_cpi(dev, CONFIG_PAW3395_CPI); + if (!err) { + err = set_cpi(dev, CONFIG_PAW3395_X_CPI, true); + } + + if (!err) { + err = set_cpi(dev, CONFIG_PAW3395_Y_CPI, false); + } - //todo: rest1 sample period, which affects scaling of rest1 downshift time + // sample period, which affects scaling of rest1 downshift time + if (!err) { + err = set_sample_time(dev, + PAW3395_REG_REST1_PERIOD, + CONFIG_PAW3395_REST1_SAMPLE_TIME_MS); + } + + if (!err) { + err = set_sample_time(dev, + PAW3395_REG_REST2_PERIOD, + CONFIG_PAW3395_REST2_SAMPLE_TIME_MS); + } + if (!err) { + err = set_sample_time(dev, + PAW3395_REG_REST3_PERIOD, + CONFIG_PAW3395_REST3_SAMPLE_TIME_MS); + } // downshift time for each rest mode if (!err) { @@ -823,7 +856,27 @@ static int paw3395_async_init_configure(const struct device *dev) CONFIG_PAW3395_REST2_DOWNSHIFT_TIME_MS); } - return err; + + // rest mode + if (!err) { + if(IS_ENABLED(CONFIG_PAW3395_ENABLE_REST)) + err = set_rest_mode(dev, true); + else + err = set_rest_mode(dev, false); + } + + // clear motion registers and ready to go + for (uint8_t reg = 0x02; (reg <= 0x06) && !err; reg++) { + uint8_t buf[1]; + err = reg_read(dev, reg, buf); + } + + if (err) { + LOG_ERR("Config the sensor failed"); + return err; + } + + return 0; } // checked and keep @@ -852,6 +905,70 @@ static void paw3395_async_init(struct k_work *work) } } + +static void irq_handler(const struct device *gpiob, struct gpio_callback *cb, + uint32_t pins) +{ + int err; + struct paw3395_data *data = CONTAINER_OF(cb, struct paw3395_data, + irq_gpio_cb); + const struct device *dev = data->dev; + const struct paw3395_config *config = dev->config; + + // disable the interrupt line first + err = gpio_pin_interrupt_configure_dt(&config->irq_gpio, + GPIO_INT_DISABLE); + if (unlikely(err)) { + LOG_ERR("Cannot disable IRQ"); + k_panic(); + } + + // submit the real handler work + k_work_submit(&data->trigger_handler_work); +} + +static void trigger_handler(struct k_work *work) +{ + sensor_trigger_handler_t handler; + int err = 0; + struct paw3395_data *data = CONTAINER_OF(work, struct paw3395_data, + trigger_handler_work); + const struct device *dev = data->dev; + const struct paw3395_config *config = dev->config; + + // 1. the first lock period is used to procoss the trigger + // if data_ready_handler is non-NULL, otherwise do nothing + k_spinlock_key_t key = k_spin_lock(&data->lock); + + handler = data->data_ready_handler; + k_spin_unlock(&data->lock, key); + + if (!handler) { + return; + } + + struct sensor_trigger trig = { + .type = SENSOR_TRIG_DATA_READY, + .chan = SENSOR_CHAN_ALL, + }; + + handler(dev, &trig); + + // 2. the second lock period is used to resume the interrupt line + // if data_ready_handler is non-NULL, otherwise keep it inactive + key = k_spin_lock(&data->lock); + if (data->data_ready_handler) { + err = gpio_pin_interrupt_configure_dt(&config->irq_gpio, + GPIO_INT_LEVEL_ACTIVE); + } + k_spin_unlock(&data->lock, key); + + if (unlikely(err)) { + LOG_ERR("Cannot re-enable IRQ"); + k_panic(); + } +} + static int paw3395_init_irq(const struct device *dev) { int err; @@ -921,10 +1038,8 @@ static int paw3395_init(const struct device *dev) // Setup delayable and non-blocking init jobs, including following steps: // 1. power reset - // 2. clear motion registers - // 3. srom firmware download and checking - // 4. eable rest mode - // 5. set cpi and downshift time (not sample rate) + // 2. upload initial settings + // 3. other configs like cpi, downshift time, sample time etc. // The sensor is ready to work (i.e., data->ready=true after the above steps are finished) k_work_init_delayable(&data->init_work, paw3395_async_init); @@ -1052,59 +1167,6 @@ static int paw3395_trigger_set(const struct device *dev, return err; } -static int paw3395_attr_set(const struct device *dev, enum sensor_channel chan, - enum sensor_attribute attr, - const struct sensor_value *val) -{ - struct paw3395_data *data = dev->data; - int err; - - if (unlikely(chan != SENSOR_CHAN_ALL)) { - return -ENOTSUP; - } - - if (unlikely(!data->ready)) { - LOG_DBG("Device is not initialized yet"); - return -EBUSY; - } - - switch ((uint32_t)attr) { - case PAW3395_ATTR_CPI: - err = set_cpi(dev, PAW3395_SVALUE_TO_CPI(*val)); - break; - - case PAW3395_ATTR_REST_ENABLE: - err = set_rest_modes(dev, - PAW3395_REG_CONFIG2, - PAW3395_SVALUE_TO_BOOL(*val)); - break; - - case PAW3395_ATTR_RUN_DOWNSHIFT_TIME: - err = set_downshift_time(dev, - PAW3395_REG_RUN_DOWNSHIFT, - PAW3395_SVALUE_TO_TIME(*val)); - break; - - case PAW3395_ATTR_REST1_DOWNSHIFT_TIME: - err = set_downshift_time(dev, - PAW3395_REG_REST1_DOWNSHIFT, - PAW3395_SVALUE_TO_TIME(*val)); - break; - - case PAW3395_ATTR_REST2_DOWNSHIFT_TIME: - err = set_downshift_time(dev, - PAW3395_REG_REST2_DOWNSHIFT, - PAW3395_SVALUE_TO_TIME(*val)); - break; - - default: - LOG_ERR("Unknown attribute"); - return -ENOTSUP; - } - - return err; -} - static const struct sensor_driver_api paw3395_driver_api = { .sample_fetch = paw3395_sample_fetch, .channel_get = paw3395_channel_get, diff --git a/app/drivers/sensor/paw3395/paw3395.h b/app/drivers/sensor/paw3395/paw3395.h index 46966f1deac..487eaf7edff 100644 --- a/app/drivers/sensor/paw3395/paw3395.h +++ b/app/drivers/sensor/paw3395/paw3395.h @@ -29,8 +29,9 @@ extern "C" { /** Sensor specific attributes of PAW3395. */ enum paw3395_attribute { - /** Sensor CPI for both X and Y axes. */ - PAW3395_ATTR_CPI = SENSOR_ATTR_PRIV_START, + /** Sensor CPI for X and Y axes. */ + PAW3395_ATTR_XCPI = SENSOR_ATTR_PRIV_START, + PAW3395_ATTR_YCPI, /** Enable or disable sleep modes. */ PAW3395_ATTR_REST_ENABLE, @@ -54,7 +55,6 @@ extern "C" { PAW3395_ATTR_REST3_SAMPLE_TIME, /** Select the running mode. */ - // todo: implement it PAW3395_ATTR_RUN_MODE, }; From 0ed4bfa8ab9b3ff848dc0dd5367498dde1cf1f90 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Fri, 2 Sep 2022 22:30:44 +0800 Subject: [PATCH 075/157] backup --- app/boards/arm/nrfmacro/CMakeLists.txt | 1 + .../arm/nrfmacro/trackball/CMakeLists.txt | 1 + .../arm/nrfmacro/trackball/trackball_new.c | 24 ++-- app/drivers/sensor/CMakeLists.txt | 5 +- app/drivers/sensor/Kconfig | 3 +- app/drivers/sensor/paw3395/paw3395.h | 106 ----------------- .../{ => pixart}/paw3395/CMakeLists.txt | 0 .../sensor/{ => pixart}/paw3395/Kconfig | 3 - .../sensor/{ => pixart}/paw3395/paw3395.c | 99 ++++++++-------- app/drivers/sensor/pixart/paw3395/paw3395.h | 35 ++++++ .../{ => pixart}/paw3395/paw3395_priv.c | 0 app/drivers/sensor/pixart/pixart.h | 99 ++++++++++++++++ .../{ => pixart}/pmw3360/CMakeLists.txt | 0 .../sensor/{ => pixart}/pmw3360/Kconfig | 0 .../sensor/{ => pixart}/pmw3360/pmw3360.c | 76 +++++++------ app/drivers/sensor/pixart/pmw3360/pmw3360.h | 31 +++++ .../{ => pixart}/pmw3360/pmw3360_priv.c | 0 app/drivers/sensor/pmw3360/pmw3360.h | 107 ------------------ 18 files changed, 278 insertions(+), 312 deletions(-) delete mode 100644 app/drivers/sensor/paw3395/paw3395.h rename app/drivers/sensor/{ => pixart}/paw3395/CMakeLists.txt (100%) rename app/drivers/sensor/{ => pixart}/paw3395/Kconfig (94%) rename app/drivers/sensor/{ => pixart}/paw3395/paw3395.c (92%) create mode 100644 app/drivers/sensor/pixart/paw3395/paw3395.h rename app/drivers/sensor/{ => pixart}/paw3395/paw3395_priv.c (100%) create mode 100644 app/drivers/sensor/pixart/pixart.h rename app/drivers/sensor/{ => pixart}/pmw3360/CMakeLists.txt (100%) rename app/drivers/sensor/{ => pixart}/pmw3360/Kconfig (100%) rename app/drivers/sensor/{ => pixart}/pmw3360/pmw3360.c (92%) create mode 100644 app/drivers/sensor/pixart/pmw3360/pmw3360.h rename app/drivers/sensor/{ => pixart}/pmw3360/pmw3360_priv.c (100%) delete mode 100644 app/drivers/sensor/pmw3360/pmw3360.h diff --git a/app/boards/arm/nrfmacro/CMakeLists.txt b/app/boards/arm/nrfmacro/CMakeLists.txt index a69f091f34c..1a850bdd128 100644 --- a/app/boards/arm/nrfmacro/CMakeLists.txt +++ b/app/boards/arm/nrfmacro/CMakeLists.txt @@ -13,4 +13,5 @@ if (CONFIG_NRFMACRO_POINTDEVICE) add_subdirectory_ifdef(CONFIG_PINNACLE trackpad) add_subdirectory_ifdef(CONFIG_PMW33XX trackball) add_subdirectory_ifdef(CONFIG_PMW3360 trackball) + add_subdirectory_ifdef(CONFIG_PMW3395 trackball) endif() diff --git a/app/boards/arm/nrfmacro/trackball/CMakeLists.txt b/app/boards/arm/nrfmacro/trackball/CMakeLists.txt index 5d7b060667a..d592c9db80d 100644 --- a/app/boards/arm/nrfmacro/trackball/CMakeLists.txt +++ b/app/boards/arm/nrfmacro/trackball/CMakeLists.txt @@ -1,6 +1,7 @@ zephyr_library() zephyr_library_sources_ifdef(CONFIG_PMW33XX trackball.c) zephyr_library_sources_ifdef(CONFIG_PMW3360 trackball_new.c) +zephyr_library_sources_ifdef(CONFIG_PMW3395 trackball_new.c) zephyr_library_include_directories(${CMAKE_SOURCE_DIR}/include) zephyr_library_include_directories(${CMAKE_SOURCE_DIR}/drivers/sensor) zephyr_library_include_directories(${ZEPHYR_BASE}/drivers) diff --git a/app/boards/arm/nrfmacro/trackball/trackball_new.c b/app/boards/arm/nrfmacro/trackball/trackball_new.c index 9381bb2fda9..1c0d1049408 100644 --- a/app/boards/arm/nrfmacro/trackball/trackball_new.c +++ b/app/boards/arm/nrfmacro/trackball/trackball_new.c @@ -1,8 +1,12 @@ +#ifdef CONFIG_PMW3360 #define DT_DRV_COMPAT pixart_pmw3360 +#include +#elif defined(CONFIG_PAW3395) +#define DT_DRV_COMPAT pixart_paw3395 +#include +#endif -#include #include - #include #include #include @@ -12,16 +16,12 @@ #include #include -#include #define SCROLL_DIV_FACTOR 5 /* #define SCROLL_LAYER_INDEX 4 */ #define SCROLL_LAYER_INDEX COND_CODE_0(DT_INST_NODE_HAS_PROP(0, scroll_layer), (255), \ (DT_INST_PROP(0, scroll_layer))) -#define CPI_DIVIDOR COND_CODE_0(DT_INST_NODE_HAS_PROP(0, cpi_dividor), (1), \ - (DT_INST_PROP(0, cpi_dividor))) - /* #if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) */ // in us @@ -54,10 +54,10 @@ static void trackball_poll_handler(struct k_work *work) { /* #endif */ // get the device pointer - struct pmw3360_data *data = CONTAINER_OF(work, struct pmw3360_data, poll_work); + struct pixart_data *data = CONTAINER_OF(work, struct pixart_data, poll_work); const struct device *dev = data->dev; - // fetch dx and dy from sensor and save them into pmw3360_data structure + // fetch dx and dy from sensor and save them into pixart_data structure int ret = sensor_sample_fetch(dev); if (ret < 0) { LOG_ERR("fetch: %d", ret); @@ -121,7 +121,7 @@ static void trackball_poll_handler(struct k_work *work) { static void trackball_trigger_handler(const struct device *dev, const struct sensor_trigger *trig) { LOG_INF("I'm new trackball implementation"); - struct pmw3360_data *data = dev->data; + struct pixart_data *data = dev->data; // do not resume motion interrupt by passing-in null handler struct sensor_trigger trigger = { @@ -138,7 +138,7 @@ static void trackball_trigger_handler(const struct device *dev, const struct sen // timer expiry function void trackball_timer_expiry(struct k_timer *timer) { - struct pmw3360_data *data = CONTAINER_OF(timer, struct pmw3360_data, poll_timer); + struct pixart_data *data = CONTAINER_OF(timer, struct pixart_data, poll_timer); // check whether reaching the polling count limit if(polling_count < max_poll_count) { @@ -156,7 +156,7 @@ void trackball_timer_expiry(struct k_timer *timer) { // timer stop function void trackball_timer_stop(struct k_timer *timer) { - struct pmw3360_data *data = CONTAINER_OF(timer, struct pmw3360_data, poll_timer); + struct pixart_data *data = CONTAINER_OF(timer, struct pixart_data, poll_timer); const struct device *dev = data->dev; // reset polling count @@ -181,7 +181,7 @@ void trackball_timer_stop(struct k_timer *timer) { static int trackball_init() { // get the sensor device instance const struct device *dev = DEVICE_DT_GET(DT_DRV_INST(0)); - struct pmw3360_data *data = dev->data; + struct pixart_data *data = dev->data; // setup the timer and handler function of the polling work k_timer_init(&data->poll_timer, trackball_timer_expiry, trackball_timer_stop); diff --git a/app/drivers/sensor/CMakeLists.txt b/app/drivers/sensor/CMakeLists.txt index 5e1fa110a5f..acb55f27ea7 100644 --- a/app/drivers/sensor/CMakeLists.txt +++ b/app/drivers/sensor/CMakeLists.txt @@ -4,5 +4,8 @@ add_subdirectory_ifdef(CONFIG_ZMK_BATTERY battery) add_subdirectory_ifdef(CONFIG_EC11 ec11) add_subdirectory_ifdef(CONFIG_PMW33XX pmw33xx) -add_subdirectory_ifdef(CONFIG_PMW3360 pmw3360) add_subdirectory_ifdef(CONFIG_PINNACLE cirque_trackpad) + +# new impl +add_subdirectory_ifdef(CONFIG_PMW3360 pixart/pmw3360) +add_subdirectory_ifdef(CONFIG_PAW3395 pixart/paw3395) diff --git a/app/drivers/sensor/Kconfig b/app/drivers/sensor/Kconfig index beef0e5b32f..3c299d01b8d 100644 --- a/app/drivers/sensor/Kconfig +++ b/app/drivers/sensor/Kconfig @@ -4,5 +4,6 @@ rsource "battery/Kconfig" rsource "ec11/Kconfig" rsource "pmw33xx/Kconfig" -rsource "pmw3360/Kconfig" +rsource "pixart/pmw3360/Kconfig" +rsource "pixart/paw3395/Kconfig" rsource "cirque_trackpad/Kconfig" diff --git a/app/drivers/sensor/paw3395/paw3395.h b/app/drivers/sensor/paw3395/paw3395.h deleted file mode 100644 index 487eaf7edff..00000000000 --- a/app/drivers/sensor/paw3395/paw3395.h +++ /dev/null @@ -1,106 +0,0 @@ -#ifndef ZEPHYR_INCLUDE_PAW3395_H_ -#define ZEPHYR_INCLUDE_PAW3395_H_ - -#include -#include -#include -#include - -/** - * @file paw3395.h - * - * @brief Header file for the paw3395 driver. - */ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - - enum paw3395_run_mode { - HP_MODE, // high performance mode (the default mode using standard power-up register sets) - LP_MODE, // low power mode - OFFICE_MODE, // office mode (reduced to 10 ips, most power-efficient) - GAME_MODE, // corded game mode (with best performance and highest power consumption) - - RUN_MODE_COUNT // end flag - }; - - /** Sensor specific attributes of PAW3395. */ - enum paw3395_attribute { - /** Sensor CPI for X and Y axes. */ - PAW3395_ATTR_XCPI = SENSOR_ATTR_PRIV_START, - PAW3395_ATTR_YCPI, - - /** Enable or disable sleep modes. */ - PAW3395_ATTR_REST_ENABLE, - - /** Entering time from Run mode to REST1 mode [ms]. */ - PAW3395_ATTR_RUN_DOWNSHIFT_TIME, - - /** Entering time from REST1 mode to REST2 mode [ms]. */ - PAW3395_ATTR_REST1_DOWNSHIFT_TIME, - - /** Entering time from REST2 mode to REST3 mode [ms]. */ - PAW3395_ATTR_REST2_DOWNSHIFT_TIME, - - /** Sampling frequency time during REST1 mode [ms]. */ - PAW3395_ATTR_REST1_SAMPLE_TIME, - - /** Sampling frequency time during REST2 mode [ms]. */ - PAW3395_ATTR_REST2_SAMPLE_TIME, - - /** Sampling frequency time during REST3 mode [ms]. */ - PAW3395_ATTR_REST3_SAMPLE_TIME, - - /** Select the running mode. */ - PAW3395_ATTR_RUN_MODE, - }; - - /* device data structure */ - struct paw3395_data { - const struct device *dev; - int16_t x; - int16_t y; - - // lock is needed to keep atomic of the trigger handler upadting - struct k_spinlock lock; - // motion interrupt isr - struct gpio_callback irq_gpio_cb; - // the actual trigger handler. This handler also used to flag whether resuming the motion interrupt line - sensor_trigger_handler_t data_ready_handler; - // the work structure holding the trigger handler job - struct k_work trigger_handler_work; - - // the work structure for delayable init steps - struct k_work_delayable init_work; - enum async_init_step async_init_step; - - // - bool ready; // whether init is finished successfully - bool last_read_burst; // todo: needed? - int err; // error code during async init - - /* the design of the driver is based on interrupt purely, to add polling upon it - the following work and timer maybe used in application code */ - struct k_work poll_work; - struct k_timer poll_timer; - }; - - /* device config structure */ - struct paw3395_config { - struct spi_dt_spec bus; - struct gpio_dt_spec irq_gpio; - struct gpio_dt_spec cs_gpio; - }; - -#ifdef __cplusplus -} -#endif - -/** - * @} - */ - -#endif /* ZEPHYR_INCLUDE_PAW3395_H_ */ diff --git a/app/drivers/sensor/paw3395/CMakeLists.txt b/app/drivers/sensor/pixart/paw3395/CMakeLists.txt similarity index 100% rename from app/drivers/sensor/paw3395/CMakeLists.txt rename to app/drivers/sensor/pixart/paw3395/CMakeLists.txt diff --git a/app/drivers/sensor/paw3395/Kconfig b/app/drivers/sensor/pixart/paw3395/Kconfig similarity index 94% rename from app/drivers/sensor/paw3395/Kconfig rename to app/drivers/sensor/pixart/paw3395/Kconfig index d27fe2f037a..a4b4922fb18 100644 --- a/app/drivers/sensor/paw3395/Kconfig +++ b/app/drivers/sensor/pixart/paw3395/Kconfig @@ -56,7 +56,6 @@ config PAW3395_REST1_SAMPLE_TIME_MS range 1 255 help Default REST1 mode sample period in millisecond. - Smaller value means less power consumption. config PAW3395_REST2_SAMPLE_TIME_MS int "PAW3395's sample time in REST2 stage" @@ -64,7 +63,6 @@ config PAW3395_REST2_SAMPLE_TIME_MS range 4 1020 help Default REST2 mode sample period in millisecond. - Smaller value means less power consumption. config PAW3395_REST3_SAMPLE_TIME_MS int "PAW3395's sample time in REST3 stage" @@ -72,7 +70,6 @@ config PAW3395_REST3_SAMPLE_TIME_MS range 8 2040 help Default REST2 mode sample period in millisecond. - Smaller value means less power consumption. config PAW3395_RUN_DOWNSHIFT_TIME_MS int "PAW3395's default RUN mode downshift time" diff --git a/app/drivers/sensor/paw3395/paw3395.c b/app/drivers/sensor/pixart/paw3395/paw3395.c similarity index 92% rename from app/drivers/sensor/paw3395/paw3395.c rename to app/drivers/sensor/pixart/paw3395/paw3395.c index e02841dad7a..64df2399e83 100644 --- a/app/drivers/sensor/paw3395/paw3395.c +++ b/app/drivers/sensor/pixart/paw3395/paw3395.c @@ -129,17 +129,18 @@ extern const uint8_t paw3395_pwrup_registers_data2[]; extern const size_t paw3395_mode_registers_length[]; extern const uint8_t* paw3395_mode_registers_addr[]; +extern const uint8_t* paw3395_mode_registers_data[]; //////// Sensor initialization steps definition ////////// // init is done in non-blocking manner (i.e., async), a // // delayable work is defined for this purpose // - enum paw3395_init_step { - ASYNC_INIT_STEP_POWER_UP, // reset cs line and assert power-up reset - ASYNC_INIT_STEP_LOAD_SETTING, // load register setting - ASYNC_INIT_STEP_CONFIGURE, // set other registes like cpi and donwshift time (run, rest1, rest2) and clear motion registers +enum paw3395_init_step { + ASYNC_INIT_STEP_POWER_UP, // reset cs line and assert power-up reset + ASYNC_INIT_STEP_LOAD_SETTING, // load register setting + ASYNC_INIT_STEP_CONFIGURE, // set other registes like cpi and donwshift time (run, rest1, rest2) and clear motion registers - ASYNC_INIT_STEP_COUNT // end flag - }; + ASYNC_INIT_STEP_COUNT // end flag +}; /* Timings (in ms) needed in between steps to allow each step finishes succussfully. */ // - Since MCU is not involved in the sensor init process, i is allowed to do other tasks. @@ -147,7 +148,7 @@ extern const uint8_t* paw3395_mode_registers_addr[]; static const int32_t async_init_delay[ASYNC_INIT_STEP_COUNT] = { [ASYNC_INIT_STEP_POWER_UP] = 50, // required in spec [ASYNC_INIT_STEP_LOAD_SETTING] = 5, // required in spec - [ASYNC_INIT_STEP_CONFIGURE] = K_NO_WAIT, + [ASYNC_INIT_STEP_CONFIGURE] = 0, }; static int paw3395_async_init_power_up(const struct device *dev); @@ -165,7 +166,7 @@ static int (* const async_init_fn[ASYNC_INIT_STEP_COUNT])(const struct device *d // checked and keep static int spi_cs_ctrl(const struct device *dev, bool enable) { - const struct paw3395_config *config = dev->config; + const struct pixart_config *config = dev->config; int err; if (!enable) { @@ -189,8 +190,8 @@ static int spi_cs_ctrl(const struct device *dev, bool enable) static int reg_read(const struct device *dev, uint8_t reg, uint8_t *buf) { int err; - struct paw3395_data *data = dev->data; - const struct paw3395_config *config = dev->config; + /* struct pixart_data *data = dev->data; */ + const struct pixart_config *config = dev->config; __ASSERT_NO_MSG((reg & SPI_WRITE_BIT) == 0); @@ -250,8 +251,8 @@ static int reg_read(const struct device *dev, uint8_t reg, uint8_t *buf) static int reg_write(const struct device *dev, uint8_t reg, uint8_t val) { int err; - struct paw3395_data *data = dev->data; - const struct paw3395_config *config = dev->config; + /* struct pixart_data *data = dev->data; */ + const struct pixart_config *config = dev->config; __ASSERT_NO_MSG((reg & SPI_WRITE_BIT) == 0); @@ -297,8 +298,8 @@ static int motion_burst_read(const struct device *dev, uint8_t *buf, size_t burst_size) { int err; - struct paw3395_data *data = dev->data; - const struct paw3395_config *config = dev->config; + /* struct pixart_data *data = dev->data; */ + const struct pixart_config *config = dev->config; __ASSERT_NO_MSG(burst_size <= PAW3395_MAX_BURST_SIZE); @@ -369,11 +370,9 @@ static int motion_burst_read(const struct device *dev, uint8_t *buf, } /** Writing an array of registers in sequence, used in power-up register initialization and running mode switching */ -static int burst_write(const struct device *dev, uint8_t *addr, const uint8_t *buf, size_t size) +static int burst_write(const struct device *dev, const uint8_t *addr, const uint8_t *buf, size_t size) { int err; - struct paw3395_data *data = dev->data; - const struct paw3395_config *config = dev->config; /* Write data */ for (size_t i = 0; i < size; i++) { @@ -385,6 +384,7 @@ static int burst_write(const struct device *dev, uint8_t *addr, const uint8_t *b } } + /* struct pixart_data *data = dev->data; */ /* data->last_read_burst = false; */ return 0; @@ -405,7 +405,7 @@ static int upload_pwrup_settings(const struct device *dev) // stage 2: read register 0x6C at 1ms interval until value 0x80 is returned // or timeout after 60 times - uint8_t value == 0x00; + uint8_t value = 0; int count = 0; while( count < 60 ) { // wait for 1ms befor read (timing accuracy 1% is required) @@ -518,7 +518,7 @@ static int set_run_mode(const struct device *dev, enum paw3395_run_mode run_mode } /* set cpi (x, y seperately): axis true for x, false for y */ -static int set_cpi(const struct device *dev, uint32_t cpi, bool_t axis) +static int set_cpi(const struct device *dev, uint32_t cpi, bool axis) { /* Set resolution with CPI step of 50 cpi * 0x0000: 50 cpi (minimum cpi) @@ -543,13 +543,14 @@ static int set_cpi(const struct device *dev, uint32_t cpi, bool_t axis) sys_put_le16(value, buf); // upload the new value + int err; if (axis) { // x-cpi uint8_t addr[2] = {PAW3395_REG_RESOLUTION_X_LOW, PAW3395_REG_RESOLUTION_X_HIGH}; - int err = burst_write(dev, addr, buf, 2); + err = burst_write(dev, addr, buf, 2); } else { // y-cpi uint8_t addr[2] = {PAW3395_REG_RESOLUTION_Y_LOW, PAW3395_REG_RESOLUTION_Y_HIGH}; - int err = burst_write(dev, addr, buf, 2); + err = burst_write(dev, addr, buf, 2); } if (err) { LOG_ERR("Failed to upload %s-CPI", axis ? "X" : "Y"); @@ -697,7 +698,7 @@ static int set_rest_mode(const struct device *dev, bool enable) WRITE_BIT(value, PAW3395_REST_EN_POS, !enable); LOG_INF("%sable rest modes", (enable) ? ("En") : ("Dis")); - err = reg_write(dev, reg_addr, value); + err = reg_write(dev, PAW3395_REG_PERFORMANCE, value); if (err) { LOG_ERR("Failed to set rest mode"); @@ -710,7 +711,7 @@ static int paw3395_attr_set(const struct device *dev, enum sensor_channel chan, enum sensor_attribute attr, const struct sensor_value *val) { - struct paw3395_data *data = dev->data; + struct pixart_data *data = dev->data; int err; if (unlikely(chan != SENSOR_CHAN_ALL)) { @@ -723,54 +724,54 @@ static int paw3395_attr_set(const struct device *dev, enum sensor_channel chan, } switch ((uint32_t)attr) { - case PAW3395_ATTR_XCPI: + case PIXART_ATTR_XCPI: err = set_cpi(dev, PAW3395_SVALUE_TO_CPI(*val), true); break; - case PAW3395_ATTR_YCPI: + case PIXART_ATTR_YCPI: err = set_cpi(dev, PAW3395_SVALUE_TO_CPI(*val), false); break; - case PAW3395_ATTR_REST_ENABLE: - err = set_rest_modes(dev, PAW3395_SVALUE_TO_BOOL(*val)); + case PIXART_ATTR_REST_ENABLE: + err = set_rest_mode(dev, PAW3395_SVALUE_TO_BOOL(*val)); break; - case PAW3395_ATTR_RUN_DOWNSHIFT_TIME: + case PIXART_ATTR_RUN_DOWNSHIFT_TIME: err = set_downshift_time(dev, PAW3395_REG_RUN_DOWNSHIFT, PAW3395_SVALUE_TO_TIME(*val)); break; - case PAW3395_ATTR_REST1_DOWNSHIFT_TIME: + case PIXART_ATTR_REST1_DOWNSHIFT_TIME: err = set_downshift_time(dev, PAW3395_REG_REST1_DOWNSHIFT, PAW3395_SVALUE_TO_TIME(*val)); break; - case PAW3395_ATTR_REST2_DOWNSHIFT_TIME: + case PIXART_ATTR_REST2_DOWNSHIFT_TIME: err = set_downshift_time(dev, PAW3395_REG_REST2_DOWNSHIFT, PAW3395_SVALUE_TO_TIME(*val)); break; - case PAW3395_ATTR_REST1_SAMPLE_TIME: + case PIXART_ATTR_REST1_SAMPLE_TIME: err = set_sample_time(dev, PAW3395_REG_REST1_PERIOD, PAW3395_SVALUE_TO_TIME(*val)); break; - case PAW3395_ATTR_REST2_SAMPLE_TIME: + case PIXART_ATTR_REST2_SAMPLE_TIME: err = set_sample_time(dev, PAW3395_REG_REST2_PERIOD, PAW3395_SVALUE_TO_TIME(*val)); break; - case PAW3395_ATTR_REST3_SAMPLE_TIME: + case PIXART_ATTR_REST3_SAMPLE_TIME: err = set_sample_time(dev, PAW3395_REG_REST3_PERIOD, PAW3395_SVALUE_TO_TIME(*val)); break; - case PAW3395_ATTR_RUN_MODE: + case PIXART_ATTR_RUN_MODE: err = set_run_mode(dev, PAW3395_SVALUE_TO_RUNMODE(*val)); break; @@ -882,7 +883,7 @@ static int paw3395_async_init_configure(const struct device *dev) // checked and keep static void paw3395_async_init(struct k_work *work) { - struct paw3395_data *data = CONTAINER_OF(work, struct paw3395_data, + struct pixart_data *data = CONTAINER_OF(work, struct pixart_data, init_work); const struct device *dev = data->dev; @@ -910,10 +911,10 @@ static void irq_handler(const struct device *gpiob, struct gpio_callback *cb, uint32_t pins) { int err; - struct paw3395_data *data = CONTAINER_OF(cb, struct paw3395_data, + struct pixart_data *data = CONTAINER_OF(cb, struct pixart_data, irq_gpio_cb); const struct device *dev = data->dev; - const struct paw3395_config *config = dev->config; + const struct pixart_config *config = dev->config; // disable the interrupt line first err = gpio_pin_interrupt_configure_dt(&config->irq_gpio, @@ -931,10 +932,10 @@ static void trigger_handler(struct k_work *work) { sensor_trigger_handler_t handler; int err = 0; - struct paw3395_data *data = CONTAINER_OF(work, struct paw3395_data, + struct pixart_data *data = CONTAINER_OF(work, struct pixart_data, trigger_handler_work); const struct device *dev = data->dev; - const struct paw3395_config *config = dev->config; + const struct pixart_config *config = dev->config; // 1. the first lock period is used to procoss the trigger // if data_ready_handler is non-NULL, otherwise do nothing @@ -972,8 +973,8 @@ static void trigger_handler(struct k_work *work) static int paw3395_init_irq(const struct device *dev) { int err; - struct paw3395_data *data = dev->data; - const struct paw3395_config *config = dev->config; + struct pixart_data *data = dev->data; + const struct pixart_config *config = dev->config; // check readiness of irq gpio pin if (!device_is_ready(config->irq_gpio.port)) { @@ -1002,8 +1003,8 @@ static int paw3395_init_irq(const struct device *dev) static int paw3395_init(const struct device *dev) { - struct paw3395_data *data = dev->data; - const struct paw3395_config *config = dev->config; + struct pixart_data *data = dev->data; + const struct pixart_config *config = dev->config; int err; // init device pointer @@ -1051,7 +1052,7 @@ static int paw3395_init(const struct device *dev) static int paw3395_sample_fetch(const struct device *dev, enum sensor_channel chan) { - struct paw3395_data *data = dev->data; + struct pixart_data *data = dev->data; uint8_t buf[PAW3395_BURST_SIZE]; if (unlikely(chan != SENSOR_CHAN_ALL)) { @@ -1092,7 +1093,7 @@ static int paw3395_sample_fetch(const struct device *dev, enum sensor_channel ch static int paw3395_channel_get(const struct device *dev, enum sensor_channel chan, struct sensor_value *val) { - struct paw3395_data *data = dev->data; + struct pixart_data *data = dev->data; if (unlikely(!data->ready)) { LOG_DBG("Device is not initialized yet"); @@ -1127,8 +1128,8 @@ static int paw3395_trigger_set(const struct device *dev, const struct sensor_trigger *trig, sensor_trigger_handler_t handler) { - struct paw3395_data *data = dev->data; - const struct paw3395_config *config = dev->config; + struct pixart_data *data = dev->data; + const struct pixart_config *config = dev->config; int err; if (unlikely(trig->type != SENSOR_TRIG_DATA_READY)) { @@ -1175,9 +1176,9 @@ static const struct sensor_driver_api paw3395_driver_api = { }; #define PAW3395_DEFINE(n) \ - static struct paw3395_data data##n; \ + static struct pixart_data data##n; \ \ - static const struct paw3395_config config##n = { \ + static const struct pixart_config config##n = { \ .irq_gpio = GPIO_DT_SPEC_INST_GET(n, irq_gpios), \ .bus = { \ .bus = DEVICE_DT_GET(DT_INST_BUS(n)), \ diff --git a/app/drivers/sensor/pixart/paw3395/paw3395.h b/app/drivers/sensor/pixart/paw3395/paw3395.h new file mode 100644 index 00000000000..e548aa3db9e --- /dev/null +++ b/app/drivers/sensor/pixart/paw3395/paw3395.h @@ -0,0 +1,35 @@ +#ifndef ZEPHYR_INCLUDE_PAW3395_H_ +#define ZEPHYR_INCLUDE_PAW3395_H_ + +#include "../pixart.h" + +/** + * @file paw3395.h + * + * @brief Header file for the paw3395 driver. + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + enum paw3395_run_mode { + HP_MODE, // high performance mode (the default mode using standard power-up register sets) + LP_MODE, // low power mode + OFFICE_MODE, // office mode (reduced to 10 ips, most power-efficient) + GAME_MODE, // corded game mode (with best performance and highest power consumption) + + RUN_MODE_COUNT // end flag + }; + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* ZEPHYR_INCLUDE_PAW3395_H_ */ diff --git a/app/drivers/sensor/paw3395/paw3395_priv.c b/app/drivers/sensor/pixart/paw3395/paw3395_priv.c similarity index 100% rename from app/drivers/sensor/paw3395/paw3395_priv.c rename to app/drivers/sensor/pixart/paw3395/paw3395_priv.c diff --git a/app/drivers/sensor/pixart/pixart.h b/app/drivers/sensor/pixart/pixart.h new file mode 100644 index 00000000000..22f4dd12689 --- /dev/null +++ b/app/drivers/sensor/pixart/pixart.h @@ -0,0 +1,99 @@ +#ifndef ZEPHYR_INCLUDE_PIXART_H_ +#define ZEPHYR_INCLUDE_PIXART_H_ + +/** + * @file pixart.h + * + * @brief Common header file for all optical motion sensor by PIXART + */ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* device data structure */ + struct pixart_data { + const struct device *dev; + int16_t x; + int16_t y; + + // lock is needed to keep atomic of the trigger handler upadting + struct k_spinlock lock; + // motion interrupt isr + struct gpio_callback irq_gpio_cb; + // the actual trigger handler. This handler also used to flag whether resuming the motion interrupt line + sensor_trigger_handler_t data_ready_handler; + // the work structure holding the trigger handler job + struct k_work trigger_handler_work; + + // the work structure for delayable init steps + struct k_work_delayable init_work; + int async_init_step; + + // + bool ready; // whether init is finished successfully + bool last_read_burst; // todo: needed? + int err; // error code during async init + + /* the design of the driver is based on interrupt purely, to add polling upon it + the following work and timer maybe used in application code */ + struct k_work poll_work; + struct k_timer poll_timer; + }; + + // device config data structure + struct pixart_config { + struct gpio_dt_spec irq_gpio; + struct spi_dt_spec bus; + struct gpio_dt_spec cs_gpio; + }; + +/** @brief Sensor specific attributes of PIXART. */ +enum pixart_attribute { + /** Sensor CPI for both X and Y axes. */ + PIXART_ATTR_CPI = SENSOR_ATTR_PRIV_START, + + /** Sensor CPI for X and Y axes. */ + PIXART_ATTR_XCPI, + PIXART_ATTR_YCPI, + + /** Enable or disable sleep modes. */ + PIXART_ATTR_REST_ENABLE, + + /** Entering time from Run mode to REST1 mode [ms]. */ + PIXART_ATTR_RUN_DOWNSHIFT_TIME, + + /** Entering time from REST1 mode to REST2 mode [ms]. */ + PIXART_ATTR_REST1_DOWNSHIFT_TIME, + + /** Entering time from REST2 mode to REST3 mode [ms]. */ + PIXART_ATTR_REST2_DOWNSHIFT_TIME, + + /** Sampling frequency time during REST1 mode [ms]. */ + PIXART_ATTR_REST1_SAMPLE_TIME, + + /** Sampling frequency time during REST2 mode [ms]. */ + PIXART_ATTR_REST2_SAMPLE_TIME, + + /** Sampling frequency time during REST3 mode [ms]. */ + PIXART_ATTR_REST3_SAMPLE_TIME, + + /** Select the running mode. */ + PIXART_ATTR_RUN_MODE, +}; + + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* ZEPHYR_INCLUDE_PIXART_H_ */ diff --git a/app/drivers/sensor/pmw3360/CMakeLists.txt b/app/drivers/sensor/pixart/pmw3360/CMakeLists.txt similarity index 100% rename from app/drivers/sensor/pmw3360/CMakeLists.txt rename to app/drivers/sensor/pixart/pmw3360/CMakeLists.txt diff --git a/app/drivers/sensor/pmw3360/Kconfig b/app/drivers/sensor/pixart/pmw3360/Kconfig similarity index 100% rename from app/drivers/sensor/pmw3360/Kconfig rename to app/drivers/sensor/pixart/pmw3360/Kconfig diff --git a/app/drivers/sensor/pmw3360/pmw3360.c b/app/drivers/sensor/pixart/pmw3360/pmw3360.c similarity index 92% rename from app/drivers/sensor/pmw3360/pmw3360.c rename to app/drivers/sensor/pixart/pmw3360/pmw3360.c index 3b4314765b3..304a3c4020a 100644 --- a/app/drivers/sensor/pmw3360/pmw3360.c +++ b/app/drivers/sensor/pixart/pmw3360/pmw3360.c @@ -115,6 +115,16 @@ extern const uint8_t pmw3360_firmware_data[]; // init is done in non-blocking manner (i.e., async), a delayable work is defined for this job // see pmw3360_init and pmw3360_async_init) +enum async_init_step { + ASYNC_INIT_STEP_POWER_UP, // power up reset + ASYNC_INIT_STEP_FW_LOAD_START, // clear motion registers, disable REST mode, enable SROM register + ASYNC_INIT_STEP_FW_LOAD_CONTINUE, // start SROM download + ASYNC_INIT_STEP_FW_LOAD_VERIFY, // verify SROM pid and fid, enable REST mode + ASYNC_INIT_STEP_CONFIGURE, // set cpi and donwshift time (run, rest1, rest2) + + ASYNC_INIT_STEP_COUNT // end flag +}; + // delay (ms) in between steps static const int32_t async_init_delay[ASYNC_INIT_STEP_COUNT] = { [ASYNC_INIT_STEP_POWER_UP] = 1, @@ -140,7 +150,7 @@ static int (* const async_init_fn[ASYNC_INIT_STEP_COUNT])(const struct device *d static int spi_cs_ctrl(const struct device *dev, bool enable) { - const struct pmw3360_config *config = dev->config; + const struct pixart_config *config = dev->config; int err; if (!enable) { @@ -162,8 +172,8 @@ static int spi_cs_ctrl(const struct device *dev, bool enable) static int reg_read(const struct device *dev, uint8_t reg, uint8_t *buf) { int err; - struct pmw3360_data *data = dev->data; - const struct pmw3360_config *config = dev->config; + struct pixart_data *data = dev->data; + const struct pixart_config *config = dev->config; __ASSERT_NO_MSG((reg & SPI_WRITE_BIT) == 0); @@ -221,8 +231,8 @@ static int reg_read(const struct device *dev, uint8_t reg, uint8_t *buf) static int reg_write(const struct device *dev, uint8_t reg, uint8_t val) { int err; - struct pmw3360_data *data = dev->data; - const struct pmw3360_config *config = dev->config; + struct pixart_data *data = dev->data; + const struct pixart_config *config = dev->config; __ASSERT_NO_MSG((reg & SPI_WRITE_BIT) == 0); @@ -268,8 +278,8 @@ static int motion_burst_read(const struct device *dev, uint8_t *buf, size_t burst_size) { int err; - struct pmw3360_data *data = dev->data; - const struct pmw3360_config *config = dev->config; + struct pixart_data *data = dev->data; + const struct pixart_config *config = dev->config; __ASSERT_NO_MSG(burst_size <= PMW3360_MAX_BURST_SIZE); @@ -341,8 +351,8 @@ static int burst_write(const struct device *dev, uint8_t reg, const uint8_t *buf size_t size) { int err; - struct pmw3360_data *data = dev->data; - const struct pmw3360_config *config = dev->config; + struct pixart_data *data = dev->data; + const struct pixart_config *config = dev->config; /* Write address of burst register */ uint8_t write_buf = reg | SPI_WRITE_BIT; @@ -649,10 +659,10 @@ static void irq_handler(const struct device *gpiob, struct gpio_callback *cb, uint32_t pins) { int err; - struct pmw3360_data *data = CONTAINER_OF(cb, struct pmw3360_data, + struct pixart_data *data = CONTAINER_OF(cb, struct pixart_data, irq_gpio_cb); const struct device *dev = data->dev; - const struct pmw3360_config *config = dev->config; + const struct pixart_config *config = dev->config; // disable the interrupt line first err = gpio_pin_interrupt_configure_dt(&config->irq_gpio, @@ -670,10 +680,10 @@ static void trigger_handler(struct k_work *work) { sensor_trigger_handler_t handler; int err = 0; - struct pmw3360_data *data = CONTAINER_OF(work, struct pmw3360_data, + struct pixart_data *data = CONTAINER_OF(work, struct pixart_data, trigger_handler_work); const struct device *dev = data->dev; - const struct pmw3360_config *config = dev->config; + const struct pixart_config *config = dev->config; // 1. the first lock period is used to procoss the trigger // if data_ready_handler is non-NULL, otherwise do nothing @@ -744,7 +754,7 @@ static int pmw3360_async_init_configure(const struct device *dev) static void pmw3360_async_init(struct k_work *work) { - struct pmw3360_data *data = CONTAINER_OF(work, struct pmw3360_data, + struct pixart_data *data = CONTAINER_OF(work, struct pixart_data, init_work); const struct device *dev = data->dev; @@ -770,8 +780,8 @@ static void pmw3360_async_init(struct k_work *work) static int pmw3360_init_irq(const struct device *dev) { int err; - struct pmw3360_data *data = dev->data; - const struct pmw3360_config *config = dev->config; + struct pixart_data *data = dev->data; + const struct pixart_config *config = dev->config; // check readiness of irq gpio pin if (!device_is_ready(config->irq_gpio.port)) { @@ -800,8 +810,8 @@ static int pmw3360_init_irq(const struct device *dev) static int pmw3360_init(const struct device *dev) { - struct pmw3360_data *data = dev->data; - const struct pmw3360_config *config = dev->config; + struct pixart_data *data = dev->data; + const struct pixart_config *config = dev->config; int err; // init device pointer @@ -851,7 +861,7 @@ static int pmw3360_init(const struct device *dev) static int pmw3360_sample_fetch(const struct device *dev, enum sensor_channel chan) { - struct pmw3360_data *data = dev->data; + struct pixart_data *data = dev->data; uint8_t buf[PMW3360_BURST_SIZE]; if (unlikely(chan != SENSOR_CHAN_ALL)) { @@ -892,7 +902,7 @@ static int pmw3360_sample_fetch(const struct device *dev, enum sensor_channel ch static int pmw3360_channel_get(const struct device *dev, enum sensor_channel chan, struct sensor_value *val) { - struct pmw3360_data *data = dev->data; + struct pixart_data *data = dev->data; if (unlikely(!data->ready)) { LOG_DBG("Device is not initialized yet"); @@ -927,8 +937,8 @@ static int pmw3360_trigger_set(const struct device *dev, const struct sensor_trigger *trig, sensor_trigger_handler_t handler) { - struct pmw3360_data *data = dev->data; - const struct pmw3360_config *config = dev->config; + struct pixart_data *data = dev->data; + const struct pixart_config *config = dev->config; int err; if (unlikely(trig->type != SENSOR_TRIG_DATA_READY)) { @@ -971,7 +981,7 @@ static int pmw3360_attr_set(const struct device *dev, enum sensor_channel chan, enum sensor_attribute attr, const struct sensor_value *val) { - struct pmw3360_data *data = dev->data; + struct pixart_data *data = dev->data; int err; if (unlikely(chan != SENSOR_CHAN_ALL)) { @@ -984,49 +994,49 @@ static int pmw3360_attr_set(const struct device *dev, enum sensor_channel chan, } switch ((uint32_t)attr) { - case PMW3360_ATTR_CPI: + case PIXART_ATTR_CPI: err = update_cpi(dev, PMW3360_SVALUE_TO_CPI(*val)); break; - case PMW3360_ATTR_REST_ENABLE: + case PIXART_ATTR_REST_ENABLE: err = toggle_rest_modes(dev, PMW3360_REG_CONFIG2, PMW3360_SVALUE_TO_BOOL(*val)); break; - case PMW3360_ATTR_RUN_DOWNSHIFT_TIME: + case PIXART_ATTR_RUN_DOWNSHIFT_TIME: err = update_downshift_time(dev, PMW3360_REG_RUN_DOWNSHIFT, PMW3360_SVALUE_TO_TIME(*val)); break; - case PMW3360_ATTR_REST1_DOWNSHIFT_TIME: + case PIXART_ATTR_REST1_DOWNSHIFT_TIME: err = update_downshift_time(dev, PMW3360_REG_REST1_DOWNSHIFT, PMW3360_SVALUE_TO_TIME(*val)); break; - case PMW3360_ATTR_REST2_DOWNSHIFT_TIME: + case PIXART_ATTR_REST2_DOWNSHIFT_TIME: err = update_downshift_time(dev, PMW3360_REG_REST2_DOWNSHIFT, PMW3360_SVALUE_TO_TIME(*val)); break; - case PMW3360_ATTR_REST1_SAMPLE_TIME: + case PIXART_ATTR_REST1_SAMPLE_TIME: err = update_sample_time(dev, PMW3360_REG_REST1_RATE_LOWER, PMW3360_REG_REST1_RATE_UPPER, PMW3360_SVALUE_TO_TIME(*val)); break; - case PMW3360_ATTR_REST2_SAMPLE_TIME: + case PIXART_ATTR_REST2_SAMPLE_TIME: err = update_sample_time(dev, PMW3360_REG_REST2_RATE_LOWER, PMW3360_REG_REST2_RATE_UPPER, PMW3360_SVALUE_TO_TIME(*val)); break; - case PMW3360_ATTR_REST3_SAMPLE_TIME: + case PIXART_ATTR_REST3_SAMPLE_TIME: err = update_sample_time(dev, PMW3360_REG_REST3_RATE_LOWER, PMW3360_REG_REST3_RATE_UPPER, @@ -1049,9 +1059,9 @@ static const struct sensor_driver_api pmw3360_driver_api = { }; #define PMW3360_DEFINE(n) \ - static struct pmw3360_data data##n; \ + static struct pixart_data data##n; \ \ - static const struct pmw3360_config config##n = { \ + static const struct pixart_config config##n = { \ .irq_gpio = GPIO_DT_SPEC_INST_GET(n, irq_gpios), \ .bus = { \ .bus = DEVICE_DT_GET(DT_INST_BUS(n)), \ diff --git a/app/drivers/sensor/pixart/pmw3360/pmw3360.h b/app/drivers/sensor/pixart/pmw3360/pmw3360.h new file mode 100644 index 00000000000..48395d73c3d --- /dev/null +++ b/app/drivers/sensor/pixart/pmw3360/pmw3360.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#ifndef ZEPHYR_INCLUDE_PMW3360_H_ +#define ZEPHYR_INCLUDE_PMW3360_H_ + +/** + * @file pmw3360.h + * + * @brief Header file for the pmw3360 driver. + */ + +#include "../pixart.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* ZEPHYR_INCLUDE_PMW3360_H_ */ diff --git a/app/drivers/sensor/pmw3360/pmw3360_priv.c b/app/drivers/sensor/pixart/pmw3360/pmw3360_priv.c similarity index 100% rename from app/drivers/sensor/pmw3360/pmw3360_priv.c rename to app/drivers/sensor/pixart/pmw3360/pmw3360_priv.c diff --git a/app/drivers/sensor/pmw3360/pmw3360.h b/app/drivers/sensor/pmw3360/pmw3360.h deleted file mode 100644 index 8d665c2b6bf..00000000000 --- a/app/drivers/sensor/pmw3360/pmw3360.h +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (c) 2019 Nordic Semiconductor ASA - * - * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - */ - -#ifndef ZEPHYR_INCLUDE_PMW3360_H_ -#define ZEPHYR_INCLUDE_PMW3360_H_ - -#include -#include -#include -#include - -/** - * @file pmw3360.h - * - * @brief Header file for the pmw3360 driver. - */ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -enum async_init_step { - ASYNC_INIT_STEP_POWER_UP, // power up reset - ASYNC_INIT_STEP_FW_LOAD_START, // clear motion registers, disable REST mode, enable SROM register - ASYNC_INIT_STEP_FW_LOAD_CONTINUE, // start SROM download - ASYNC_INIT_STEP_FW_LOAD_VERIFY, // verify SROM pid and fid, enable REST mode - ASYNC_INIT_STEP_CONFIGURE, // set cpi and donwshift time (run, rest1, rest2) - - ASYNC_INIT_STEP_COUNT // end flag -}; - -/* device data structure */ -struct pmw3360_data { - const struct device *dev; - struct gpio_callback irq_gpio_cb; - struct k_spinlock lock; - int16_t x; - int16_t y; - sensor_trigger_handler_t data_ready_handler; - struct k_work trigger_handler_work; - struct k_work_delayable init_work; - - enum async_init_step async_init_step; - int err; - bool ready; - bool last_read_burst; - - /* the design of the driver is based on interrupt purely, to add polling upon it - the following work and timer maybe used in application code */ - struct k_work poll_work; - struct k_timer poll_timer; -}; - -struct pmw3360_config { - struct gpio_dt_spec irq_gpio; - struct spi_dt_spec bus; - struct gpio_dt_spec cs_gpio; -}; - -/** - * @defgroup pmw3360 PMW3360 motion sensor driver - * @{ - * @brief PMW3360 motion sensor driver. - */ - -/** @brief Sensor specific attributes of PMW3360. */ -enum pmw3360_attribute { - /** Sensor CPI for both X and Y axes. */ - PMW3360_ATTR_CPI = SENSOR_ATTR_PRIV_START, - - /** Enable or disable sleep modes. */ - PMW3360_ATTR_REST_ENABLE, - - /** Entering time from Run mode to REST1 mode [ms]. */ - PMW3360_ATTR_RUN_DOWNSHIFT_TIME, - - /** Entering time from REST1 mode to REST2 mode [ms]. */ - PMW3360_ATTR_REST1_DOWNSHIFT_TIME, - - /** Entering time from REST2 mode to REST3 mode [ms]. */ - PMW3360_ATTR_REST2_DOWNSHIFT_TIME, - - /** Sampling frequency time during REST1 mode [ms]. */ - PMW3360_ATTR_REST1_SAMPLE_TIME, - - /** Sampling frequency time during REST2 mode [ms]. */ - PMW3360_ATTR_REST2_SAMPLE_TIME, - - /** Sampling frequency time during REST3 mode [ms]. */ - PMW3360_ATTR_REST3_SAMPLE_TIME, -}; - - -#ifdef __cplusplus -} -#endif - -/** - * @} - */ - -#endif /* ZEPHYR_INCLUDE_PMW3360_H_ */ From 46b89283aaa1cf4d5bdf8cf4f36c0c054ab452b4 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Sat, 3 Sep 2022 20:51:54 +0800 Subject: [PATCH 076/157] [new-feature] init working version of pixart paw3395 driver --- app/boards/arm/nrfmacro/CMakeLists.txt | 2 +- .../arm/nrfmacro/trackball/CMakeLists.txt | 2 +- .../arm/nrfmacro/trackball/trackball_new.c | 2 + app/drivers/sensor/pixart/paw3395/paw3395.c | 76 ++++++++++++++----- 4 files changed, 59 insertions(+), 23 deletions(-) diff --git a/app/boards/arm/nrfmacro/CMakeLists.txt b/app/boards/arm/nrfmacro/CMakeLists.txt index 1a850bdd128..573f799b3e2 100644 --- a/app/boards/arm/nrfmacro/CMakeLists.txt +++ b/app/boards/arm/nrfmacro/CMakeLists.txt @@ -13,5 +13,5 @@ if (CONFIG_NRFMACRO_POINTDEVICE) add_subdirectory_ifdef(CONFIG_PINNACLE trackpad) add_subdirectory_ifdef(CONFIG_PMW33XX trackball) add_subdirectory_ifdef(CONFIG_PMW3360 trackball) - add_subdirectory_ifdef(CONFIG_PMW3395 trackball) + add_subdirectory_ifdef(CONFIG_PAW3395 trackball) endif() diff --git a/app/boards/arm/nrfmacro/trackball/CMakeLists.txt b/app/boards/arm/nrfmacro/trackball/CMakeLists.txt index d592c9db80d..2663ede83bd 100644 --- a/app/boards/arm/nrfmacro/trackball/CMakeLists.txt +++ b/app/boards/arm/nrfmacro/trackball/CMakeLists.txt @@ -1,7 +1,7 @@ zephyr_library() zephyr_library_sources_ifdef(CONFIG_PMW33XX trackball.c) zephyr_library_sources_ifdef(CONFIG_PMW3360 trackball_new.c) -zephyr_library_sources_ifdef(CONFIG_PMW3395 trackball_new.c) +zephyr_library_sources_ifdef(CONFIG_PAW3395 trackball_new.c) zephyr_library_include_directories(${CMAKE_SOURCE_DIR}/include) zephyr_library_include_directories(${CMAKE_SOURCE_DIR}/drivers/sensor) zephyr_library_include_directories(${ZEPHYR_BASE}/drivers) diff --git a/app/boards/arm/nrfmacro/trackball/trackball_new.c b/app/boards/arm/nrfmacro/trackball/trackball_new.c index 1c0d1049408..f5f6d22e0a2 100644 --- a/app/boards/arm/nrfmacro/trackball/trackball_new.c +++ b/app/boards/arm/nrfmacro/trackball/trackball_new.c @@ -179,6 +179,8 @@ void trackball_timer_stop(struct k_timer *timer) { // In this applaication, the devcie instance 'trackball' is hard-coded as the first // pixart_pmw3360 node in dts file and used implicitly here. static int trackball_init() { + LOG_INF("Init trackball_new"); + // get the sensor device instance const struct device *dev = DEVICE_DT_GET(DT_DRV_INST(0)); struct pixart_data *data = dev->data; diff --git a/app/drivers/sensor/pixart/paw3395/paw3395.c b/app/drivers/sensor/pixart/paw3395/paw3395.c index 64df2399e83..d6abf5ca4ce 100644 --- a/app/drivers/sensor/pixart/paw3395/paw3395.c +++ b/app/drivers/sensor/pixart/paw3395/paw3395.c @@ -390,11 +390,29 @@ static int burst_write(const struct device *dev, const uint8_t *addr, const uint return 0; } +static int check_product_id(const struct device *dev) +{ + uint8_t product_id=0x01; + int err = reg_read(dev, PAW3395_REG_PRODUCT_ID, &product_id); + if (err) { + LOG_ERR("Cannot obtain product id"); + return err; + } + + if (product_id != PAW3395_PRODUCT_ID) { + LOG_ERR("Incorrect product id 0x%x (expecting 0x%x)!", product_id, PAW3395_PRODUCT_ID); + return -EIO; + } + + return 0; +} + static int upload_pwrup_settings(const struct device *dev) { - LOG_INF(""); + LOG_INF("Upload firmware settings..."); // stage 1: configure the first 137 registers + LOG_INF("stag 1: upload the first 137 registers"); int err; err = burst_write(dev, paw3395_pwrup_registers_addr1, \ paw3395_pwrup_registers_data1, paw3395_pwrup_registers_length1); @@ -405,24 +423,30 @@ static int upload_pwrup_settings(const struct device *dev) // stage 2: read register 0x6C at 1ms interval until value 0x80 is returned // or timeout after 60 times + LOG_INF("stag 2: poll until 0x80 returned"); uint8_t value = 0; int count = 0; while( count < 60 ) { + // wait for 1ms befor read (timing accuracy 1% is required) - k_msleep(1); + k_busy_wait(1000); + /* k_msleep(1); */ if (reg_read(dev, 0x6C, &value)) { LOG_ERR("Failed to read register 0x6C"); return err; } + LOG_DBG("%d poll returns 0x%x", count+1, value); if( value == 0x80 ) break; + + count++; } // do some setting if 0x80 is not returned within 60 poll if( value != 0x80 ) { - LOG_INF("vale 0x80 is not returned within 60 poll times"); + LOG_INF("value 0x80 is not returned within 60 polls, use alternative setting"); uint8_t addr[] = {0x7F, 0x6C, 0x7F}; uint8_t data[] = {0x14, 0x00, 0x00}; @@ -434,6 +458,7 @@ static int upload_pwrup_settings(const struct device *dev) } // stage 3: configure the remaining 5 rigisters + LOG_INF("stag 3: upload the remaining 5 registers"); err = burst_write(dev, paw3395_pwrup_registers_addr2,\ paw3395_pwrup_registers_data2, paw3395_pwrup_registers_length2); if(err) { @@ -442,17 +467,14 @@ static int upload_pwrup_settings(const struct device *dev) } // stage 4: check the product id - uint8_t product_id; - err = reg_read(dev, PAW3395_REG_PRODUCT_ID, &product_id); + LOG_INF("stag 4: confirm the setting by checking the product id"); + err = check_product_id(dev); if (err) { - LOG_ERR("Cannot obtain product id"); + LOG_ERR("Failed checking product id"); return err; } - if (product_id != PAW3395_PRODUCT_ID) { - LOG_ERR("Invalid product id!"); - return -EIO; - } + LOG_INF("Upload power-up register settins done"); return 0; } @@ -609,10 +631,11 @@ static int set_sample_time(const struct device *dev, uint8_t reg_addr, uint32_t return -EINVAL; } - LOG_INF("Set sample time to %u ms", sample_time); + uint8_t value = sample_time / mintime; + LOG_INF("Set sample time to %u ms (reg value: 0x%x)", sample_time, value); /* The sample time is (reg_value * mintime ) ms. 0x00 is rounded to 0x1 */ - int err = reg_write(dev, reg_addr, (uint8_t)(sample_time / mintime)); + int err = reg_write(dev, reg_addr, value); if (!err) { LOG_ERR("Failed to change sample time"); } @@ -786,7 +809,9 @@ static int paw3395_attr_set(const struct device *dev, enum sensor_channel chan, static int paw3395_async_init_power_up(const struct device *dev) { - /* needed? Reset spi port */ + LOG_INF("async_init_power_up"); + + /* needed or not? Reset spi port */ spi_cs_ctrl(dev, false); spi_cs_ctrl(dev, true); @@ -796,16 +821,15 @@ static int paw3395_async_init_power_up(const struct device *dev) static int paw3395_async_init_load_setting(const struct device *dev) { - int err; - - LOG_INF("Uploading optical sensor firmware..."); - err = upload_pwrup_settings(dev); + LOG_INF("async_init_load_setting"); - return err; + return upload_pwrup_settings(dev); } static int paw3395_async_init_configure(const struct device *dev) { + LOG_INF("async_init_configure"); + int err; // run mode @@ -887,7 +911,7 @@ static void paw3395_async_init(struct k_work *work) init_work); const struct device *dev = data->dev; - LOG_DBG("PAW3395 async init step %d", data->async_init_step); + LOG_INF("PAW3395 async init step %d", data->async_init_step); data->err = async_init_fn[data->async_init_step](dev); if (data->err) { @@ -900,8 +924,7 @@ static void paw3395_async_init(struct k_work *work) LOG_INF("PAW3395 initialized"); } else { k_work_schedule(&data->init_work, - K_MSEC(async_init_delay[ - data->async_init_step])); + K_MSEC(async_init_delay[data->async_init_step])); } } } @@ -930,6 +953,8 @@ static void irq_handler(const struct device *gpiob, struct gpio_callback *cb, static void trigger_handler(struct k_work *work) { + LOG_DBG("trigger_handler"); + sensor_trigger_handler_t handler; int err = 0; struct pixart_data *data = CONTAINER_OF(work, struct pixart_data, @@ -945,6 +970,7 @@ static void trigger_handler(struct k_work *work) k_spin_unlock(&data->lock, key); if (!handler) { + LOG_DBG("no trigger handler set by application code"); return; } @@ -972,6 +998,8 @@ static void trigger_handler(struct k_work *work) static int paw3395_init_irq(const struct device *dev) { + LOG_INF("Configure irq..."); + int err; struct pixart_data *data = dev->data; const struct pixart_config *config = dev->config; @@ -998,11 +1026,15 @@ static int paw3395_init_irq(const struct device *dev) LOG_ERR("Cannot add IRQ GPIO callback"); } + LOG_INF("Configure irq done"); + return err; } static int paw3395_init(const struct device *dev) { + LOG_INF("Start initializing..."); + struct pixart_data *data = dev->data; const struct pixart_config *config = dev->config; int err; @@ -1128,6 +1160,8 @@ static int paw3395_trigger_set(const struct device *dev, const struct sensor_trigger *trig, sensor_trigger_handler_t handler) { + LOG_INF("trigger_set"); + struct pixart_data *data = dev->data; const struct pixart_config *config = dev->config; int err; From 9a929654fc364e896025630d8b9ebc865ad83125 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Mon, 5 Sep 2022 10:33:55 +0800 Subject: [PATCH 077/157] backup --- app/boards/arm/nrfmacro/CMakeLists.txt | 1 + .../arm/nrfmacro/trackball/CMakeLists.txt | 2 + .../arm/nrfmacro/trackball/trackball_new.c | 3 + app/drivers/sensor/CMakeLists.txt | 1 + app/drivers/sensor/Kconfig | 4 +- app/drivers/sensor/pixart/paw3395/paw3395.c | 25 +- app/drivers/sensor/pixart/paw3395/paw3395.h | 30 + app/drivers/sensor/pixart/pixart.h | 3 + app/drivers/sensor/pixart/pmw3360/pmw3360.c | 5 +- app/drivers/sensor/pixart/pmw3360/pmw3360.h | 26 + .../sensor/pixart/pmw3610/CMakeLists.txt | 6 + app/drivers/sensor/pixart/pmw3610/Kconfig | 102 ++ app/drivers/sensor/pixart/pmw3610/pmw3610.c | 1044 +++++++++++++++++ app/drivers/sensor/pixart/pmw3610/pmw3610.h | 48 + .../dts/bindings/sensor/pixart,pmw3610.yml | 20 + 15 files changed, 1307 insertions(+), 13 deletions(-) create mode 100644 app/drivers/sensor/pixart/pmw3610/CMakeLists.txt create mode 100644 app/drivers/sensor/pixart/pmw3610/Kconfig create mode 100644 app/drivers/sensor/pixart/pmw3610/pmw3610.c create mode 100644 app/drivers/sensor/pixart/pmw3610/pmw3610.h create mode 100644 app/drivers/zephyr/dts/bindings/sensor/pixart,pmw3610.yml diff --git a/app/boards/arm/nrfmacro/CMakeLists.txt b/app/boards/arm/nrfmacro/CMakeLists.txt index 573f799b3e2..541f6bc5406 100644 --- a/app/boards/arm/nrfmacro/CMakeLists.txt +++ b/app/boards/arm/nrfmacro/CMakeLists.txt @@ -14,4 +14,5 @@ if (CONFIG_NRFMACRO_POINTDEVICE) add_subdirectory_ifdef(CONFIG_PMW33XX trackball) add_subdirectory_ifdef(CONFIG_PMW3360 trackball) add_subdirectory_ifdef(CONFIG_PAW3395 trackball) + add_subdirectory_ifdef(CONFIG_PMW3610 trackball) endif() diff --git a/app/boards/arm/nrfmacro/trackball/CMakeLists.txt b/app/boards/arm/nrfmacro/trackball/CMakeLists.txt index 2663ede83bd..d9449b5615c 100644 --- a/app/boards/arm/nrfmacro/trackball/CMakeLists.txt +++ b/app/boards/arm/nrfmacro/trackball/CMakeLists.txt @@ -2,6 +2,8 @@ zephyr_library() zephyr_library_sources_ifdef(CONFIG_PMW33XX trackball.c) zephyr_library_sources_ifdef(CONFIG_PMW3360 trackball_new.c) zephyr_library_sources_ifdef(CONFIG_PAW3395 trackball_new.c) +zephyr_library_sources_ifdef(CONFIG_PMW3610 trackball_new.c) + zephyr_library_include_directories(${CMAKE_SOURCE_DIR}/include) zephyr_library_include_directories(${CMAKE_SOURCE_DIR}/drivers/sensor) zephyr_library_include_directories(${ZEPHYR_BASE}/drivers) diff --git a/app/boards/arm/nrfmacro/trackball/trackball_new.c b/app/boards/arm/nrfmacro/trackball/trackball_new.c index f5f6d22e0a2..f6889399fe6 100644 --- a/app/boards/arm/nrfmacro/trackball/trackball_new.c +++ b/app/boards/arm/nrfmacro/trackball/trackball_new.c @@ -4,6 +4,9 @@ #elif defined(CONFIG_PAW3395) #define DT_DRV_COMPAT pixart_paw3395 #include +#elif defined(CONFIG_PMW3610) +#define DT_DRV_COMPAT pixart_pmw3610 +#include #endif #include diff --git a/app/drivers/sensor/CMakeLists.txt b/app/drivers/sensor/CMakeLists.txt index acb55f27ea7..d045db840bf 100644 --- a/app/drivers/sensor/CMakeLists.txt +++ b/app/drivers/sensor/CMakeLists.txt @@ -9,3 +9,4 @@ add_subdirectory_ifdef(CONFIG_PINNACLE cirque_trackpad) # new impl add_subdirectory_ifdef(CONFIG_PMW3360 pixart/pmw3360) add_subdirectory_ifdef(CONFIG_PAW3395 pixart/paw3395) +add_subdirectory_ifdef(CONFIG_PMW3610 pixart/pmw3610) diff --git a/app/drivers/sensor/Kconfig b/app/drivers/sensor/Kconfig index 3c299d01b8d..c82ef2cd6dd 100644 --- a/app/drivers/sensor/Kconfig +++ b/app/drivers/sensor/Kconfig @@ -3,7 +3,9 @@ rsource "battery/Kconfig" rsource "ec11/Kconfig" + +rsource "cirque_trackpad/Kconfig" rsource "pmw33xx/Kconfig" rsource "pixart/pmw3360/Kconfig" rsource "pixart/paw3395/Kconfig" -rsource "cirque_trackpad/Kconfig" +rsource "pixart/pmw3610/Kconfig" diff --git a/app/drivers/sensor/pixart/paw3395/paw3395.c b/app/drivers/sensor/pixart/paw3395/paw3395.c index d6abf5ca4ce..4c30d783726 100644 --- a/app/drivers/sensor/pixart/paw3395/paw3395.c +++ b/app/drivers/sensor/pixart/paw3395/paw3395.c @@ -89,6 +89,9 @@ LOG_MODULE_REGISTER(paw3395, CONFIG_PAW3395_LOG_LEVEL); /* Sensor identification values */ #define PAW3395_PRODUCT_ID 0x51 +/* Power-up register commands */ +#define PAW3395_POWERUP_CMD_RESET 0x5A + /* Max register count readable in a single motion burst */ #define PAW3395_MAX_BURST_SIZE 12 @@ -747,54 +750,54 @@ static int paw3395_attr_set(const struct device *dev, enum sensor_channel chan, } switch ((uint32_t)attr) { - case PIXART_ATTR_XCPI: + case PAW3395_ATTR_X_CPI: err = set_cpi(dev, PAW3395_SVALUE_TO_CPI(*val), true); break; - case PIXART_ATTR_YCPI: + case PAW3395_ATTR_Y_CPI: err = set_cpi(dev, PAW3395_SVALUE_TO_CPI(*val), false); break; - case PIXART_ATTR_REST_ENABLE: + case PAW3395_ATTR_REST_ENABLE: err = set_rest_mode(dev, PAW3395_SVALUE_TO_BOOL(*val)); break; - case PIXART_ATTR_RUN_DOWNSHIFT_TIME: + case PAW3395_ATTR_RUN_DOWNSHIFT_TIME: err = set_downshift_time(dev, PAW3395_REG_RUN_DOWNSHIFT, PAW3395_SVALUE_TO_TIME(*val)); break; - case PIXART_ATTR_REST1_DOWNSHIFT_TIME: + case PAW3395_ATTR_REST1_DOWNSHIFT_TIME: err = set_downshift_time(dev, PAW3395_REG_REST1_DOWNSHIFT, PAW3395_SVALUE_TO_TIME(*val)); break; - case PIXART_ATTR_REST2_DOWNSHIFT_TIME: + case PAW3395_ATTR_REST2_DOWNSHIFT_TIME: err = set_downshift_time(dev, PAW3395_REG_REST2_DOWNSHIFT, PAW3395_SVALUE_TO_TIME(*val)); break; - case PIXART_ATTR_REST1_SAMPLE_TIME: + case PAW3395_ATTR_REST1_SAMPLE_TIME: err = set_sample_time(dev, PAW3395_REG_REST1_PERIOD, PAW3395_SVALUE_TO_TIME(*val)); break; - case PIXART_ATTR_REST2_SAMPLE_TIME: + case PAW3395_ATTR_REST2_SAMPLE_TIME: err = set_sample_time(dev, PAW3395_REG_REST2_PERIOD, PAW3395_SVALUE_TO_TIME(*val)); break; - case PIXART_ATTR_REST3_SAMPLE_TIME: + case PAW3395_ATTR_REST3_SAMPLE_TIME: err = set_sample_time(dev, PAW3395_REG_REST3_PERIOD, PAW3395_SVALUE_TO_TIME(*val)); break; - case PIXART_ATTR_RUN_MODE: + case PAW3395_ATTR_RUN_MODE: err = set_run_mode(dev, PAW3395_SVALUE_TO_RUNMODE(*val)); break; @@ -816,7 +819,7 @@ static int paw3395_async_init_power_up(const struct device *dev) spi_cs_ctrl(dev, true); /* Reset sensor */ - return reg_write(dev, PAW3395_REG_POWER_UP_RESET, 0x5A); + return reg_write(dev, PAW3395_REG_POWER_UP_RESET, PAW3395_POWERUP_CMD_RESET); } static int paw3395_async_init_load_setting(const struct device *dev) diff --git a/app/drivers/sensor/pixart/paw3395/paw3395.h b/app/drivers/sensor/pixart/paw3395/paw3395.h index e548aa3db9e..7c849c66200 100644 --- a/app/drivers/sensor/pixart/paw3395/paw3395.h +++ b/app/drivers/sensor/pixart/paw3395/paw3395.h @@ -24,6 +24,36 @@ extern "C" { RUN_MODE_COUNT // end flag }; +enum paw3395_attribute { + /** Sensor CPI for both X and Y axes. */ + PAW3395_ATTR_X_CPI = SENSOR_ATTR_PRIV_START, + + PAW3395_ATTR_Y_CPI, + + /** Enable or disable sleep modes. */ + PAW3395_ATTR_REST_ENABLE, + + /** Entering time from Run mode to REST1 mode [ms]. */ + PAW3395_ATTR_RUN_DOWNSHIFT_TIME, + + /** Entering time from REST1 mode to REST2 mode [ms]. */ + PAW3395_ATTR_REST1_DOWNSHIFT_TIME, + + /** Entering time from REST2 mode to REST3 mode [ms]. */ + PAW3395_ATTR_REST2_DOWNSHIFT_TIME, + + /** Sampling frequency time during REST1 mode [ms]. */ + PAW3395_ATTR_REST1_SAMPLE_TIME, + + /** Sampling frequency time during REST2 mode [ms]. */ + PAW3395_ATTR_REST2_SAMPLE_TIME, + + /** Sampling frequency time during REST3 mode [ms]. */ + PAW3395_ATTR_REST3_SAMPLE_TIME, + + /** Select run mde. */ + PAW3395_ATTR_RUN_MODE, +}; #ifdef __cplusplus } #endif diff --git a/app/drivers/sensor/pixart/pixart.h b/app/drivers/sensor/pixart/pixart.h index 22f4dd12689..76f2196d8a5 100644 --- a/app/drivers/sensor/pixart/pixart.h +++ b/app/drivers/sensor/pixart/pixart.h @@ -44,6 +44,9 @@ extern "C" { the following work and timer maybe used in application code */ struct k_work poll_work; struct k_timer poll_timer; + + // for pmw3610 smart algorithm + bool sw_smart_flag; }; // device config data structure diff --git a/app/drivers/sensor/pixart/pmw3360/pmw3360.c b/app/drivers/sensor/pixart/pmw3360/pmw3360.c index 304a3c4020a..ab982e8aedf 100644 --- a/app/drivers/sensor/pixart/pmw3360/pmw3360.c +++ b/app/drivers/sensor/pixart/pmw3360/pmw3360.c @@ -81,6 +81,9 @@ LOG_MODULE_REGISTER(pmw3360, CONFIG_PMW3360_LOG_LEVEL); #define PMW3360_PRODUCT_ID 0x42 #define PMW3360_FIRMWARE_ID 0x04 +/* Power-up register commands */ +#define PMW3360_POWERUP_CMD_RESET 0x5A + /* Max register count readable in a single motion burst */ #define PMW3360_MAX_BURST_SIZE 12 @@ -722,7 +725,7 @@ static int pmw3360_async_init_power_up(const struct device *dev) { /* Reset sensor */ - return reg_write(dev, PMW3360_REG_POWER_UP_RESET, 0x5A); + return reg_write(dev, PMW3360_REG_POWER_UP_RESET, PMW3360_POWERUP_CMD_RESET); } static int pmw3360_async_init_configure(const struct device *dev) diff --git a/app/drivers/sensor/pixart/pmw3360/pmw3360.h b/app/drivers/sensor/pixart/pmw3360/pmw3360.h index 48395d73c3d..46de5fc90ea 100644 --- a/app/drivers/sensor/pixart/pmw3360/pmw3360.h +++ b/app/drivers/sensor/pixart/pmw3360/pmw3360.h @@ -19,6 +19,32 @@ extern "C" { #endif +/** @brief Sensor specific attributes of PMW3360. */ +enum pmw3360_attribute { + /** Sensor CPI for both X and Y axes. */ + PMW3360_ATTR_CPI = SENSOR_ATTR_PRIV_START, + + /** Enable or disable sleep modes. */ + PMW3360_ATTR_REST_ENABLE, + + /** Entering time from Run mode to REST1 mode [ms]. */ + PMW3360_ATTR_RUN_DOWNSHIFT_TIME, + + /** Entering time from REST1 mode to REST2 mode [ms]. */ + PMW3360_ATTR_REST1_DOWNSHIFT_TIME, + + /** Entering time from REST2 mode to REST3 mode [ms]. */ + PMW3360_ATTR_REST2_DOWNSHIFT_TIME, + + /** Sampling frequency time during REST1 mode [ms]. */ + PMW3360_ATTR_REST1_SAMPLE_TIME, + + /** Sampling frequency time during REST2 mode [ms]. */ + PMW3360_ATTR_REST2_SAMPLE_TIME, + + /** Sampling frequency time during REST3 mode [ms]. */ + PMW3360_ATTR_REST3_SAMPLE_TIME, +}; #ifdef __cplusplus } diff --git a/app/drivers/sensor/pixart/pmw3610/CMakeLists.txt b/app/drivers/sensor/pixart/pmw3610/CMakeLists.txt new file mode 100644 index 00000000000..6d9c8a6e549 --- /dev/null +++ b/app/drivers/sensor/pixart/pmw3610/CMakeLists.txt @@ -0,0 +1,6 @@ +# Copyright (c) 2022 The ZMK Contributors +# SPDX-License-Identifier: MIT + +zephyr_library() + +zephyr_library_sources(pmw3610.c) diff --git a/app/drivers/sensor/pixart/pmw3610/Kconfig b/app/drivers/sensor/pixart/pmw3610/Kconfig new file mode 100644 index 00000000000..93f5306c389 --- /dev/null +++ b/app/drivers/sensor/pixart/pmw3610/Kconfig @@ -0,0 +1,102 @@ +# Sensor data simulator +# +# Copyright (c) 2019 Nordic Semiconductor +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +menuconfig PMW3610 + bool "PMW3610 mouse optical sensor" + help + Enable PMW3610 mouse optical sensor. + +if PMW3610 + +config PMW3610_SMART_ALGORITHM + bool "Enable an algorithm of PMW3610 to enhance surface coverage" + default y + help + Enable the integration of the smart algorithm as the datasheet implementation. + The algorithm is used to extend the tracking acrocss a wider range of surfaces + such as graniles and tiles. + +config PMW3610_CPI + int "PMW3610's default CPI" + default 1600 + range 200 3200 + help + Default CPI value, with 200 step + +config PMW3610_CPI_DIVIDOR + int "PMW3610's default CPI dividor" + default 4 + range 1 100 + help + Default CPI dividor value. + +config PMW3610_REST1_SAMPLE_TIME_MS + int "PMW3610's sample time in REST1 stage" + default 40 + range 10 2550 + help + Default REST1 mode sample period in millisecond + +config PMW3610_REST2_SAMPLE_TIME_MS + int "PMW3610's sample time in REST2 stage" + default 100 + range 10 2550 + help + Default REST2 mode sample period in millisecond. + +config PMW3610_REST3_SAMPLE_TIME_MS + int "PMW3610's sample time in REST3 stage" + default 500 + range 10 2550 + help + Default REST2 mode sample period in millisecond. + +config PMW3610_RUN_DOWNSHIFT_TIME_MS + int "PMW3610's default RUN mode downshift time" + default 500 + range 13 3264 + help + Default RUN mode downshift down time in milliseconds. + Time after which sensor goes from RUN to REST1 mode. + +config PMW3610_REST1_DOWNSHIFT_TIME_MS + int "PMW3610's default REST1 mode downshift time" + default 10000 + help + Default REST1 mode downshift down time in milliseconds. + Time after which sensor goes from REST1 to REST2 mode. + +config PMW3610_REST2_DOWNSHIFT_TIME_MS + int "PMW3610's default REST2 mode downshift time" + default 600000 + help + Default REST2 mode downshift down time in milliseconds. + Time after which sensor goes from REST2 to REST3 mode. + +choice + prompt "Select PMW3610 sensor orientation" + default PMW3610_ORIENTATION_0 + +config PMW3610_ORIENTATION_0 + bool "PMW3610 not rotated" + +config PMW3610_ORIENTATION_90 + bool "PMW3610 rotated 90 deg clockwise" + +config PMW3610_ORIENTATION_180 + bool "PMW3610 rotated 180 deg clockwise" + +config PMW3610_ORIENTATION_270 + bool "PMW3610 rotated 270 deg clockwise" + +endchoice + +module = PMW3610 +module-str = PMW3610 +source "${ZEPHYR_BASE}/subsys/logging/Kconfig.template.log_config" + +endif #PMW3610 diff --git a/app/drivers/sensor/pixart/pmw3610/pmw3610.c b/app/drivers/sensor/pixart/pmw3610/pmw3610.c new file mode 100644 index 00000000000..0df4fb5d49d --- /dev/null +++ b/app/drivers/sensor/pixart/pmw3610/pmw3610.c @@ -0,0 +1,1044 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#define DT_DRV_COMPAT pixart_pmw3610 + +#include +#include +#include "pmw3610.h" + +#include +LOG_MODULE_REGISTER(pmw3610, CONFIG_PMW3610_LOG_LEVEL); + +/* Timings (in us) used in SPI communication. Since MCU should not do other tasks during wait, k_busy_wait is used instead of k_sleep */ +// - sub-us time is rounded to us, due to the limitation of k_busy_wait, see : https://github.com/zephyrproject-rtos/zephyr/issues/6498 +#define T_NCS_SCLK 1 /* 120 ns (rounded to 1us) */ +#define T_SCLK_NCS_WR 10 /* 10 us */ +#define T_SRAD 4 /* 4 us */ +#define T_SRAD_MOTBR 4 /* same as T_SRAD */ +#define T_SRX 1 /* 250 ns (rounded to 1 us) */ +#define T_SWX 30 /* SWW: 30 us, SWR: 20 us */ +#define T_BEXIT 1 /* 250 ns (rounded to 1us)*/ + +/* Sensor registers (addresses) */ +#define PMW3610_REG_PRODUCT_ID 0x00 +#define PMW3610_REG_REVISION_ID 0x01 +#define PMW3610_REG_MOTION 0x02 +#define PMW3610_REG_DELTA_X_L 0x03 +#define PMW3610_REG_DELTA_Y_L 0x04 +#define PMW3610_REG_DELTA_XY_H 0x05 +#define PMW3610_REG_SQUAL 0x06 +#define PMW3610_REG_SHUTTER_HIGHER 0x07 +#define PMW3610_REG_SHUTTER_LOWER 0x08 +#define PMW3610_REG_PIX_MAX 0x09 +#define PMW3610_REG_PIX_AVG 0x0A +#define PMW3610_REG_PIX_MIN 0x0B + +#define PMW3610_REG_CRC0 0x0C +#define PMW3610_REG_CRC1 0x0D +#define PMW3610_REG_CRC2 0x0E +#define PMW3610_REG_CRC3 0x0F +#define PMW3610_REG_SELF_TEST 0x10 + +#define PMW3610_REG_PERFORMANCE 0x11 +#define PMW3610_REG_MOTION_BURST 0x12 + +#define PMW3610_REG_RUN_DOWNSHIFT 0x1B +#define PMW3610_REG_REST1_PERIOD 0x1C +#define PMW3610_REG_REST1_DOWNSHIFT 0x1D +#define PMW3610_REG_REST2_PERIOD 0x1E +#define PMW3610_REG_REST2_DOWNSHIFT 0x1F +#define PMW3610_REG_REST3_PERIOD 0x20 +#define PMW3610_REG_OBSERVATION 0x2D + +#define PMW3610_REG_PIXEL_GRAB 0x35 +#define PMW3610_REG_FRAME_GRAB 0x36 + +#define PMW3610_REG_POWER_UP_RESET 0x3A +#define PMW3610_REG_SHUTDOWN 0x3B + +#define PMW3610_REG_SPI_CLK_ON_REQ 0x41 +#define PMW3610_REG_RES_STEP 0x85 + +#define PMW3610_REG_NOT_REV_ID 0x3E +#define PMW3610_REG_NOT_PROD_ID 0x3F + +#define PMW3610_REG_PRBS_TEST_CTL 0x47 +#define PMW3610_REG_SPI_PAGE0 0x7F +#define PMW3610_REG_VCSEL_CTL 0x9E +#define PMW3610_REG_LSR_CONTROL 0x9F +#define PMW3610_REG_SPI_PAGE1 0xFF + +/* Sensor identification values */ +#define PMW3610_PRODUCT_ID 0x3E + +/* Power-up register commands */ +#define PMW3610_POWERUP_CMD_RESET 0x5A +#define PMW3610_POWERUP_CMD_WAKEUP 0x96 + +/* spi clock enable/disable commands */ +#define PMW3610_SPI_CLOCK_CMD_ENABLE 0xBA +#define PMW3610_SPI_CLOCK_CMD_DISABLE 0xB5 + +/* Max register count readable in a single motion burst */ +#define PMW3610_MAX_BURST_SIZE 10 + +/* Register count used for reading a single motion burst */ +#define PMW3610_BURST_SIZE 7 + +/* Position in the motion registers */ +#define PMW3610_X_L_POS 1 +#define PMW3610_Y_L_POS 2 +#define PMW3610_XY_H_POS 3 +#define PMW3610_SHUTTER_H_POS 5 +#define PMW3610_SHUTTER_L_POS 6 + +/* cpi/resolution range */ +#define PMW3610_MAX_CPI 3200 +#define PMW3610_MIN_CPI 200 + +/* write command bit position */ +#define SPI_WRITE_BIT BIT(7) + +/* Helper macros used to convert sensor values. */ +#define PMW3610_SVALUE_TO_CPI(svalue) ((uint32_t)(svalue).val1) +#define PMW3610_SVALUE_TO_TIME(svalue) ((uint32_t)(svalue).val1) + +//////// Sensor initialization steps definition ////////// +// init is done in non-blocking manner (i.e., async), a // +// delayable work is defined for this purpose // +enum pmw3610_init_step { + ASYNC_INIT_STEP_POWER_UP, // reset cs line and assert power-up reset + ASYNC_INIT_STEP_CLEAR_OB1, // clear observation1 register for self-test check + ASYNC_INIT_STEP_CHECK_OB1, // check the value of observation1 register after self-test check + ASYNC_INIT_STEP_CONFIGURE, // set other registes like cpi and donwshift time (run, rest1, rest2) and clear motion registers + + ASYNC_INIT_STEP_COUNT // end flag +}; + +/* Timings (in ms) needed in between steps to allow each step finishes succussfully. */ +// - Since MCU is not involved in the sensor init process, i is allowed to do other tasks. +// Thus, k_sleep or delayed schedule can be used. +static const int32_t async_init_delay[ASYNC_INIT_STEP_COUNT] = { + [ASYNC_INIT_STEP_POWER_UP] = 50, + [ASYNC_INIT_STEP_CLEAR_OB1] = 1, // 150 us, rounded to 1 ms + [ASYNC_INIT_STEP_CHECK_OB1] = 10, // required in spec + [ASYNC_INIT_STEP_CONFIGURE] = 0, +}; + +static int pmw3610_async_init_power_up(const struct device *dev); +static int pmw3610_async_init_clear_ob1(const struct device *dev); +static int pmw3610_async_init_check_ob1(const struct device *dev); +static int pmw3610_async_init_configure(const struct device *dev); + +static int (* const async_init_fn[ASYNC_INIT_STEP_COUNT])(const struct device *dev) = { + [ASYNC_INIT_STEP_POWER_UP] = pmw3610_async_init_power_up, + [ASYNC_INIT_STEP_CLEAR_OB1] = pmw3610_async_init_clear_ob1, + [ASYNC_INIT_STEP_CHECK_OB1] = pmw3610_async_init_check_ob1, + [ASYNC_INIT_STEP_CONFIGURE] = pmw3610_async_init_configure, +}; + +//////// Function definitions ////////// + +// checked and keep +static int spi_cs_ctrl(const struct device *dev, bool enable) +{ + const struct pixart_config *config = dev->config; + int err; + + if (!enable) { + k_busy_wait(T_NCS_SCLK); + } + + err = gpio_pin_set_dt(&config->cs_gpio, (int)enable); + if (err) { + LOG_ERR("SPI CS ctrl failed"); + } + + if (enable) { + k_busy_wait(T_NCS_SCLK); + } + + return err; +} + + +// checked and keep +static int reg_read(const struct device *dev, uint8_t reg, uint8_t *buf) +{ + int err; + /* struct pixart_data *data = dev->data; */ + const struct pixart_config *config = dev->config; + + __ASSERT_NO_MSG((reg & SPI_WRITE_BIT) == 0); + + err = spi_cs_ctrl(dev, true); + if (err) { + return err; + } + + /* Write register address. */ + const struct spi_buf tx_buf = { + .buf = ®, + .len = 1 + }; + const struct spi_buf_set tx = { + .buffers = &tx_buf, + .count = 1 + }; + + err = spi_write_dt(&config->bus, &tx); + if (err) { + LOG_ERR("Reg read failed on SPI write"); + return err; + } + + k_busy_wait(T_SRAD); + + /* Read register value. */ + struct spi_buf rx_buf = { + .buf = buf, + .len = 1, + }; + const struct spi_buf_set rx = { + .buffers = &rx_buf, + .count = 1, + }; + + err = spi_read_dt(&config->bus, &rx); + if (err) { + LOG_ERR("Reg read failed on SPI read"); + return err; + } + + err = spi_cs_ctrl(dev, false); + if (err) { + return err; + } + + k_busy_wait(T_SRX); + + return 0; +} + +// primitive write without enable/disable spi clock on the sensor +static int _reg_write(const struct device *dev, uint8_t reg, uint8_t val) +{ + int err; + /* struct pixart_data *data = dev->data; */ + const struct pixart_config *config = dev->config; + + __ASSERT_NO_MSG((reg & SPI_WRITE_BIT) == 0); + + err = spi_cs_ctrl(dev, true); + if (err) { + return err; + } + + uint8_t buf[] = { + SPI_WRITE_BIT | reg, + val + }; + const struct spi_buf tx_buf = { + .buf = buf, + .len = ARRAY_SIZE(buf) + }; + const struct spi_buf_set tx = { + .buffers = &tx_buf, + .count = 1 + }; + + err = spi_write_dt(&config->bus, &tx); + if (err) { + LOG_ERR("Reg write failed on SPI write"); + return err; + } + + k_busy_wait(T_SCLK_NCS_WR); + + err = spi_cs_ctrl(dev, false); + if (err) { + return err; + } + + k_busy_wait(T_SWX); + + return 0; +} + +static int reg_write(const struct device *dev, uint8_t reg, uint8_t val) { + int err; + + // enable spi clock + err = _reg_write(dev, PMW3610_REG_SPI_CLK_ON_REQ, PMW3610_SPI_CLOCK_CMD_ENABLE); + if (unlikely(err != 0)) { + return err; + } + + // write the target register + err = _reg_write(dev, reg, val); + if (unlikely(err != 0)) { + return err; + } + + // disable spi clock to save power + err = _reg_write(dev, PMW3610_REG_SPI_CLK_ON_REQ, PMW3610_SPI_CLOCK_CMD_DISABLE); + if (unlikely(err != 0)) { + return err; + } + + return 0; +} + +static int motion_burst_read(const struct device *dev, uint8_t *buf, + size_t burst_size) +{ + int err; + /* struct pixart_data *data = dev->data; */ + const struct pixart_config *config = dev->config; + + __ASSERT_NO_MSG(burst_size <= PMW3610_MAX_BURST_SIZE); + + err = spi_cs_ctrl(dev, true); + if (err) { + return err; + } + + /* Send motion burst address */ + uint8_t reg_buf[] = { + PMW3610_REG_MOTION_BURST + }; + const struct spi_buf tx_buf = { + .buf = reg_buf, + .len = ARRAY_SIZE(reg_buf) + }; + const struct spi_buf_set tx = { + .buffers = &tx_buf, + .count = 1 + }; + + err = spi_write_dt(&config->bus, &tx); + if (err) { + LOG_ERR("Motion burst failed on SPI write"); + return err; + } + + k_busy_wait(T_SRAD_MOTBR); + + const struct spi_buf rx_buf = { + .buf = buf, + .len = burst_size, + }; + const struct spi_buf_set rx = { + .buffers = &rx_buf, + .count = 1 + }; + + err = spi_read_dt(&config->bus, &rx); + if (err) { + LOG_ERR("Motion burst failed on SPI read"); + return err; + } + + err = spi_cs_ctrl(dev, false); + if (err) { + return err; + } + + /* Terminate burst */ + k_busy_wait(T_BEXIT); + + return 0; +} + +/** Writing an array of registers in sequence, used in power-up register initialization and running mode switching */ +static int burst_write(const struct device *dev, const uint8_t *addr, const uint8_t *buf, size_t size) +{ + int err; + + // enable spi clock + err = _reg_write(dev, PMW3610_REG_SPI_CLK_ON_REQ, PMW3610_SPI_CLOCK_CMD_ENABLE); + if (unlikely(err != 0)) { + return err; + } + + /* Write data */ + for (size_t i = 0; i < size; i++) { + err = _reg_write(dev, addr[i], buf[i]); + + if (err) { + LOG_ERR("Burst write failed on SPI write (data)"); + return err; + } + } + + // disable spi clock to save power + err = _reg_write(dev, PMW3610_REG_SPI_CLK_ON_REQ, PMW3610_SPI_CLOCK_CMD_DISABLE); + if (unlikely(err != 0)) { + return err; + } + + return 0; +} + +static int check_product_id(const struct device *dev) +{ + uint8_t product_id=0x01; + int err = reg_read(dev, PMW3610_REG_PRODUCT_ID, &product_id); + if (err) { + LOG_ERR("Cannot obtain product id"); + return err; + } + + if (product_id != PMW3610_PRODUCT_ID) { + LOG_ERR("Incorrect product id 0x%x (expecting 0x%x)!", product_id, PMW3610_PRODUCT_ID); + return -EIO; + } + + return 0; +} + +static int set_cpi(const struct device *dev, uint32_t cpi) +{ + /* Set resolution with CPI step of 200 cpi + * 0x1: 200 cpi (minimum cpi) + * 0x2: 400 cpi + * 0x3: 600 cpi + * : + */ + + if ((cpi > PMW3610_MAX_CPI) || (cpi < PMW3610_MIN_CPI)) { + LOG_ERR("CPI value %u out of range", cpi); + return -EINVAL; + } + + // Convert CPI to register value + uint8_t value = (cpi / 200); + LOG_INF("Setting CPI to %u (reg value 0x%x)", cpi, value); + + /* set the cpi */ + uint8_t addr[] = {0x7F, PMW3610_REG_RES_STEP, 0x7F}; + uint8_t data[] = {0xFF, value, 0x00}; + int err = burst_write(dev, addr, data, 3); + if (err) { + LOG_ERR("Failed to set CPI"); + return err; + } + + return 0; +} + +/* Set sampling rate in each mode (in ms) */ +static int set_sample_time(const struct device *dev, uint8_t reg_addr, uint32_t sample_time) +{ + uint32_t maxtime = 2550; + uint32_t mintime = 10; + if ((sample_time > maxtime) || (sample_time < mintime)) { + LOG_WRN("Sample time %u out of range [%u, %u]", sample_time, mintime, maxtime); + return -EINVAL; + } + + uint8_t value = sample_time / mintime; + LOG_INF("Set sample time to %u ms (reg value: 0x%x)", sample_time, value); + + /* The sample time is (reg_value * mintime ) ms. 0x00 is rounded to 0x1 */ + int err = reg_write(dev, reg_addr, value); + if (!err) { + LOG_ERR("Failed to change sample time"); + } + + return err; +} + +/* Set downshift time in ms. */ +// NOTE: The unit of run-mode downshift is related to pos mode rate, which is hard coded to be 4 ms +// The pos-mode rate is configured in pmw3610_async_init_configure +static int set_downshift_time(const struct device *dev, uint8_t reg_addr, uint32_t time) +{ + uint32_t maxtime; + uint32_t mintime; + + switch (reg_addr) { + case PMW3610_REG_RUN_DOWNSHIFT: + /* + * Run downshift time = PMW3610_REG_RUN_DOWNSHIFT + * * 8 * pos-rate (fixed to 4ms) + */ + maxtime = 32*255; + mintime = 32; // hard-coded in pmw3610_async_init_configure + break; + + case PMW3610_REG_REST1_DOWNSHIFT: + /* + * Rest1 downshift time = PMW3610_REG_RUN_DOWNSHIFT + * * 16 * Rest1_sample_period (default 40 ms) + */ + maxtime = 255 * 16 * CONFIG_PMW3610_REST1_SAMPLE_TIME_MS; + mintime = 16 * CONFIG_PMW3610_REST1_SAMPLE_TIME_MS; + break; + + case PMW3610_REG_REST2_DOWNSHIFT: + /* + * Rest2 downshift time = PMW3610_REG_REST2_DOWNSHIFT + * * 128 * Rest2 rate (default 100 ms) + */ + maxtime = 255 * 128 * CONFIG_PMW3610_REST2_SAMPLE_TIME_MS; + mintime = 128 * CONFIG_PMW3610_REST2_SAMPLE_TIME_MS; + break; + + default: + LOG_ERR("Not supported"); + return -ENOTSUP; + } + + if ((time > maxtime) || (time < mintime)) { + LOG_WRN("Downshift time %u out of range", time); + return -EINVAL; + } + + __ASSERT_NO_MSG((mintime > 0) && (maxtime/mintime <= UINT8_MAX)); + + /* Convert time to register value */ + uint8_t value = time / mintime; + + LOG_INF("Set downshift time to %u ms (reg value 0x%x)", time, value); + + int err = reg_write(dev, reg_addr, value); + if (err) { + LOG_ERR("Failed to change downshift time"); + } + + return err; +} + +static int pmw3610_attr_set(const struct device *dev, enum sensor_channel chan, + enum sensor_attribute attr, + const struct sensor_value *val) +{ + struct pixart_data *data = dev->data; + int err; + + if (unlikely(chan != SENSOR_CHAN_ALL)) { + return -ENOTSUP; + } + + if (unlikely(!data->ready)) { + LOG_DBG("Device is not initialized yet"); + return -EBUSY; + } + + switch ((uint32_t)attr) { + case PMW3610_ATTR_CPI: + err = set_cpi(dev, PMW3610_SVALUE_TO_CPI(*val)); + break; + + case PMW3610_ATTR_RUN_DOWNSHIFT_TIME: + err = set_downshift_time(dev, + PMW3610_REG_RUN_DOWNSHIFT, + PMW3610_SVALUE_TO_TIME(*val)); + break; + + case PMW3610_ATTR_REST1_DOWNSHIFT_TIME: + err = set_downshift_time(dev, + PMW3610_REG_REST1_DOWNSHIFT, + PMW3610_SVALUE_TO_TIME(*val)); + break; + + case PMW3610_ATTR_REST2_DOWNSHIFT_TIME: + err = set_downshift_time(dev, + PMW3610_REG_REST2_DOWNSHIFT, + PMW3610_SVALUE_TO_TIME(*val)); + break; + + case PMW3610_ATTR_REST1_SAMPLE_TIME: + err = set_sample_time(dev, + PMW3610_REG_REST1_PERIOD, + PMW3610_SVALUE_TO_TIME(*val)); + break; + + case PMW3610_ATTR_REST2_SAMPLE_TIME: + err = set_sample_time(dev, + PMW3610_REG_REST2_PERIOD, + PMW3610_SVALUE_TO_TIME(*val)); + break; + + case PMW3610_ATTR_REST3_SAMPLE_TIME: + err = set_sample_time(dev, + PMW3610_REG_REST3_PERIOD, + PMW3610_SVALUE_TO_TIME(*val)); + break; + + default: + LOG_ERR("Unknown attribute"); + return -ENOTSUP; + } + + return err; +} + +static int pmw3610_async_init_power_up(const struct device *dev) +{ + LOG_INF("async_init_power_up"); + + /* Reset spi port */ + spi_cs_ctrl(dev, false); + spi_cs_ctrl(dev, true); + + /* to be tested: not required in datashet Reset sensor */ + /* return reg_write(dev, PMW3610_REG_POWER_UP_RESET, PMW3610_POWERUP_CMD_RESET); */ + return 0; +} + +static int pmw3610_async_init_clear_ob1(const struct device *dev) +{ + LOG_INF("async_init_clear_ob1"); + + return reg_write(dev, PMW3610_REG_OBSERVATION, 0x00); +} + +static int pmw3610_async_init_check_ob1(const struct device *dev) +{ + LOG_INF("async_init_check_ob1"); + + uint8_t value; + int err = reg_read(dev, PMW3610_REG_OBSERVATION, &value); + if(err) { + LOG_ERR("Can't do self-test"); + return err; + } + + if( (value & BIT_MASK(3)) != 0x0F ) { + LOG_ERR("Failed self-test (0x%x)", value); + return -EINVAL; + } + + err = check_product_id(dev); + if (err) { + LOG_ERR("Failed checking product id"); + return err; + } + + return 0; +} + +static int pmw3610_async_init_configure(const struct device *dev) +{ + LOG_INF("async_init_configure"); + + int err; + + // clear motion registers first (required in datasheet) + for (uint8_t reg = 0x02; (reg <= 0x05) && !err; reg++) { + uint8_t buf[1]; + err = reg_read(dev, reg, buf); + } + + // cpi + if (!err) { + err = set_cpi(dev, CONFIG_PMW3610_CPI); + } + + // set performace register: run mode, vel_rate, poshi_rate, poslo_rate + if (!err) { + // use the recommended value in datasheet: normal, 4ms, 4ms, 4ms + err = reg_write(dev, PMW3610_REG_PERFORMANCE, 0x0D); + } + + // sample period, which affects scaling of rest1 downshift time + if (!err) { + err = set_sample_time(dev, + PMW3610_REG_REST1_PERIOD, + CONFIG_PMW3610_REST1_SAMPLE_TIME_MS); + } + + if (!err) { + err = set_sample_time(dev, + PMW3610_REG_REST2_PERIOD, + CONFIG_PMW3610_REST2_SAMPLE_TIME_MS); + } + if (!err) { + err = set_sample_time(dev, + PMW3610_REG_REST3_PERIOD, + CONFIG_PMW3610_REST3_SAMPLE_TIME_MS); + } + + // downshift time for each rest mode + if (!err) { + err = set_downshift_time(dev, + PMW3610_REG_RUN_DOWNSHIFT, + CONFIG_PMW3610_RUN_DOWNSHIFT_TIME_MS); + } + + if (!err) { + err = set_downshift_time(dev, + PMW3610_REG_REST1_DOWNSHIFT, + CONFIG_PMW3610_REST1_DOWNSHIFT_TIME_MS); + } + + if (!err) { + err = set_downshift_time(dev, + PMW3610_REG_REST2_DOWNSHIFT, + CONFIG_PMW3610_REST2_DOWNSHIFT_TIME_MS); + } + + if (err) { + LOG_ERR("Config the sensor failed"); + return err; + } + + return 0; +} + +// checked and keep +static void pmw3610_async_init(struct k_work *work) +{ + struct pixart_data *data = CONTAINER_OF(work, struct pixart_data, + init_work); + const struct device *dev = data->dev; + + LOG_INF("PMW3610 async init step %d", data->async_init_step); + + data->err = async_init_fn[data->async_init_step](dev); + if (data->err) { + LOG_ERR("PMW3610 initialization failed"); + } else { + data->async_init_step++; + + if (data->async_init_step == ASYNC_INIT_STEP_COUNT) { + data->ready = true; // sensor is ready to work + LOG_INF("PMW3610 initialized"); + } else { + k_work_schedule(&data->init_work, + K_MSEC(async_init_delay[data->async_init_step])); + } + } +} + + +static void irq_handler(const struct device *gpiob, struct gpio_callback *cb, + uint32_t pins) +{ + int err; + struct pixart_data *data = CONTAINER_OF(cb, struct pixart_data, + irq_gpio_cb); + const struct device *dev = data->dev; + const struct pixart_config *config = dev->config; + + // disable the interrupt line first + err = gpio_pin_interrupt_configure_dt(&config->irq_gpio, + GPIO_INT_DISABLE); + if (unlikely(err)) { + LOG_ERR("Cannot disable IRQ"); + k_panic(); + } + + // submit the real handler work + k_work_submit(&data->trigger_handler_work); +} + +static void trigger_handler(struct k_work *work) +{ + LOG_DBG("trigger_handler"); + + sensor_trigger_handler_t handler; + int err = 0; + struct pixart_data *data = CONTAINER_OF(work, struct pixart_data, + trigger_handler_work); + const struct device *dev = data->dev; + const struct pixart_config *config = dev->config; + + // 1. the first lock period is used to procoss the trigger + // if data_ready_handler is non-NULL, otherwise do nothing + k_spinlock_key_t key = k_spin_lock(&data->lock); + + handler = data->data_ready_handler; + k_spin_unlock(&data->lock, key); + + if (!handler) { + LOG_DBG("no trigger handler set by application code"); + return; + } + + struct sensor_trigger trig = { + .type = SENSOR_TRIG_DATA_READY, + .chan = SENSOR_CHAN_ALL, + }; + + handler(dev, &trig); + + // 2. the second lock period is used to resume the interrupt line + // if data_ready_handler is non-NULL, otherwise keep it inactive + key = k_spin_lock(&data->lock); + if (data->data_ready_handler) { + err = gpio_pin_interrupt_configure_dt(&config->irq_gpio, + GPIO_INT_LEVEL_ACTIVE); + } + k_spin_unlock(&data->lock, key); + + if (unlikely(err)) { + LOG_ERR("Cannot re-enable IRQ"); + k_panic(); + } +} + +static int pmw3610_init_irq(const struct device *dev) +{ + LOG_INF("Configure irq..."); + + int err; + struct pixart_data *data = dev->data; + const struct pixart_config *config = dev->config; + + // check readiness of irq gpio pin + if (!device_is_ready(config->irq_gpio.port)) { + LOG_ERR("IRQ GPIO device not ready"); + return -ENODEV; + } + + // init the irq pin + err = gpio_pin_configure_dt(&config->irq_gpio, GPIO_INPUT); + if (err) { + LOG_ERR("Cannot configure IRQ GPIO"); + return err; + } + + // setup and add the irq callback associated + gpio_init_callback(&data->irq_gpio_cb, irq_handler, + BIT(config->irq_gpio.pin)); + + err = gpio_add_callback(config->irq_gpio.port, &data->irq_gpio_cb); + if (err) { + LOG_ERR("Cannot add IRQ GPIO callback"); + } + + LOG_INF("Configure irq done"); + + return err; +} + +static int pmw3610_init(const struct device *dev) +{ + LOG_INF("Start initializing..."); + + struct pixart_data *data = dev->data; + const struct pixart_config *config = dev->config; + int err; + + // init device pointer + data->dev = dev; + + // init smart algorithm flag; + data->sw_smart_flag = false; + + // init trigger handler work + k_work_init(&data->trigger_handler_work, trigger_handler); + + // check readiness of spi bus + if (!spi_is_ready(&config->bus)) { + LOG_ERR("SPI device not ready"); + return -ENODEV; + } + + // check readiness of cs gpio pin and init it to inactive + if (!device_is_ready(config->cs_gpio.port)) { + LOG_ERR("SPI CS device not ready"); + return -ENODEV; + } + + err = gpio_pin_configure_dt(&config->cs_gpio, GPIO_OUTPUT_INACTIVE); + if (err) { + LOG_ERR("Cannot configure SPI CS GPIO"); + return err; + } + + // init irq routine + err = pmw3610_init_irq(dev); + if (err) { + return err; + } + + // Setup delayable and non-blocking init jobs, including following steps: + // 1. power reset + // 2. upload initial settings + // 3. other configs like cpi, downshift time, sample time etc. + // The sensor is ready to work (i.e., data->ready=true after the above steps are finished) + k_work_init_delayable(&data->init_work, pmw3610_async_init); + + k_work_schedule(&data->init_work, + K_MSEC(async_init_delay[data->async_init_step])); + + return err; +} + +static int pmw3610_sample_fetch(const struct device *dev, enum sensor_channel chan) +{ + struct pixart_data *data = dev->data; + uint8_t buf[PMW3610_BURST_SIZE]; + + if (unlikely(chan != SENSOR_CHAN_ALL)) { + return -ENOTSUP; + } + + if (unlikely(!data->ready)) { + LOG_DBG("Device is not initialized yet"); + return -EBUSY; + } + + int err = motion_burst_read(dev, buf, sizeof(buf)); + + if (!err) { + int16_t x = (buf[PMW3610_X_L_POS] + + ((int16_t)((buf[PMW3610_XY_H_POS] & 0xF0) << 4))) + / CONFIG_PMW3610_CPI_DIVIDOR; + int16_t y = (buf[PMW3610_Y_L_POS] + + ((int16_t)((buf[PMW3610_XY_H_POS] & 0x0F) << 8))) + / CONFIG_PMW3610_CPI_DIVIDOR; + + if (IS_ENABLED(CONFIG_PMW3610_ORIENTATION_0)) { + data->x = x; + data->y = y; + } else if (IS_ENABLED(CONFIG_PMW3610_ORIENTATION_90)) { + data->x = y; + data->y = x; + } else if (IS_ENABLED(CONFIG_PMW3610_ORIENTATION_180)) { + data->x = x; + data->y = -y; + } else if (IS_ENABLED(CONFIG_PMW3610_ORIENTATION_270)) { + data->x = -y; + data->y = -x; + } + +#ifdef CONFIG_PMW3610_SMART_ALGORITHM + int16_t shutter = ((int16_t)(buf[PMW3610_SHUTTER_H_POS] & 0x01) << 8) + + buf[PMW3610_SHUTTER_L_POS]; + if ( data->sw_smart_flag && shutter < 45 ) { + reg_write(dev, 0x32, 0x00); + + data->sw_smart_flag = false; + } + + if ( !data->sw_smart_flag && shutter > 45 ) { + reg_write(dev, 0x32, 0x80); + + data->sw_smart_flag = true; + } +#endif + } + + return err; +} + +static int pmw3610_channel_get(const struct device *dev, enum sensor_channel chan, + struct sensor_value *val) +{ + struct pixart_data *data = dev->data; + + if (unlikely(!data->ready)) { + LOG_DBG("Device is not initialized yet"); + return -EBUSY; + } + + switch (chan) { + case SENSOR_CHAN_POS_DX: + val->val1 = data->x; + val->val2 = 0; + break; + + case SENSOR_CHAN_POS_DY: + val->val1 = data->y; + val->val2 = 0; + break; + + default: + return -ENOTSUP; + } + + return 0; +} + +/* Setup the callback for actual trigger handling */ +// handler could be NULL, in which case the effect is disabling the interrupt line +// Thus it has dual function: +// 1. set up a handler callback +// 2. set up a flag (i.e., data_ready_handler) to indicate resuming the interrput line or not +// This feature is useful to pass the resuming of the interrupt to application +static int pmw3610_trigger_set(const struct device *dev, + const struct sensor_trigger *trig, + sensor_trigger_handler_t handler) +{ + /* LOG_INF("trigger_set"); */ + + struct pixart_data *data = dev->data; + const struct pixart_config *config = dev->config; + int err; + + if (unlikely(trig->type != SENSOR_TRIG_DATA_READY)) { + return -ENOTSUP; + } + + if (unlikely(trig->chan != SENSOR_CHAN_ALL)) { + return -ENOTSUP; + } + + if (unlikely(!data->ready)) { + LOG_DBG("Device is not initialized yet"); + return -EBUSY; + } + + // spin lock is needed, so that the handler is not invoked before its pointer is assigned + // a valid value + k_spinlock_key_t key = k_spin_lock(&data->lock); + + // if non-NULL (a real handler defined), eanble the interrupt line + // otherwise, disable the interrupt line + if (handler) { + err = gpio_pin_interrupt_configure_dt(&config->irq_gpio, + GPIO_INT_LEVEL_ACTIVE); + } else { + err = gpio_pin_interrupt_configure_dt(&config->irq_gpio, + GPIO_INT_DISABLE); + } + + if (!err) { + data->data_ready_handler = handler; + } + + k_spin_unlock(&data->lock, key); + + return err; +} + +static const struct sensor_driver_api pmw3610_driver_api = { + .sample_fetch = pmw3610_sample_fetch, + .channel_get = pmw3610_channel_get, + .trigger_set = pmw3610_trigger_set, + .attr_set = pmw3610_attr_set, +}; + +#define PMW3610_DEFINE(n) \ + static struct pixart_data data##n; \ + \ + static const struct pixart_config config##n = { \ + .irq_gpio = GPIO_DT_SPEC_INST_GET(n, irq_gpios), \ + .bus = { \ + .bus = DEVICE_DT_GET(DT_INST_BUS(n)), \ + .config = { \ + .frequency = DT_INST_PROP(n, \ + spi_max_frequency), \ + .operation = SPI_WORD_SET(8) | \ + SPI_TRANSFER_MSB | \ + SPI_MODE_CPOL | SPI_MODE_CPHA, \ + .slave = DT_INST_REG_ADDR(n), \ + }, \ + }, \ + .cs_gpio = SPI_CS_GPIOS_DT_SPEC_GET(DT_DRV_INST(n)), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(n, pmw3610_init, NULL, &data##n, &config##n, \ + POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \ + &pmw3610_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(PMW3610_DEFINE) diff --git a/app/drivers/sensor/pixart/pmw3610/pmw3610.h b/app/drivers/sensor/pixart/pmw3610/pmw3610.h new file mode 100644 index 00000000000..92fd1b11eef --- /dev/null +++ b/app/drivers/sensor/pixart/pmw3610/pmw3610.h @@ -0,0 +1,48 @@ +#ifndef ZEPHYR_INCLUDE_PMW3610_H_ +#define ZEPHYR_INCLUDE_PMW3610_H_ + +#include "../pixart.h" + +/** + * @file pmw3610.h + * + * @brief Header file for the pmw3610 driver. + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + enum pmw3610_attribute { + /** Sensor CPI for both X and Y axes. */ + PMW3610_ATTR_CPI = SENSOR_ATTR_PRIV_START, + + /** Entering time from Run mode to REST1 mode [ms]. */ + PMW3610_ATTR_RUN_DOWNSHIFT_TIME, + + /** Entering time from REST1 mode to REST2 mode [ms]. */ + PMW3610_ATTR_REST1_DOWNSHIFT_TIME, + + /** Entering time from REST2 mode to REST3 mode [ms]. */ + PMW3610_ATTR_REST2_DOWNSHIFT_TIME, + + /** Sampling frequency time during REST1 mode [ms]. */ + PMW3610_ATTR_REST1_SAMPLE_TIME, + + /** Sampling frequency time during REST2 mode [ms]. */ + PMW3610_ATTR_REST2_SAMPLE_TIME, + + /** Sampling frequency time during REST3 mode [ms]. */ + PMW3610_ATTR_REST3_SAMPLE_TIME, + }; + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* ZEPHYR_INCLUDE_PMW3610_H_ */ diff --git a/app/drivers/zephyr/dts/bindings/sensor/pixart,pmw3610.yml b/app/drivers/zephyr/dts/bindings/sensor/pixart,pmw3610.yml new file mode 100644 index 00000000000..6af454d989e --- /dev/null +++ b/app/drivers/zephyr/dts/bindings/sensor/pixart,pmw3610.yml @@ -0,0 +1,20 @@ +# Copyright (c) 2022 The ZMK Contributors +# SPDX-License-Identifier: MIT +description: | + Sensor driver for the pixart PMW3610 optical mouse sensor + +compatible: "pixart,pmw3610" + +include: spi-device.yaml + +properties: + label: + type: string + required: true + irq-gpios: + type: phandle-array + required: true + scroll-layer: + type: int + description: the momentary layer used to modifiy move to scroll + required: false From 2daea44451a28d56580b6c3f723469d92fe339ea Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Mon, 5 Sep 2022 16:17:07 +0800 Subject: [PATCH 078/157] [new-driver] init working version of pixart pmw3610 driver --- .../arm/nrfmacro/trackball/trackball_new.c | 6 +++-- app/drivers/sensor/pixart/pmw3610/pmw3610.c | 27 +++++++++++-------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/app/boards/arm/nrfmacro/trackball/trackball_new.c b/app/boards/arm/nrfmacro/trackball/trackball_new.c index f6889399fe6..7c8004cbb6c 100644 --- a/app/boards/arm/nrfmacro/trackball/trackball_new.c +++ b/app/boards/arm/nrfmacro/trackball/trackball_new.c @@ -199,12 +199,14 @@ static int trackball_init() { }; int err; + int count = 0; do { err = sensor_trigger_set(dev, &trigger, trackball_trigger_handler); if (err == -EBUSY) { - k_sleep(K_MSEC(1)); + count++; + k_sleep(K_MSEC(10)); } - } while (err == -EBUSY); + } while (err == -EBUSY && count < 50); if (err) { LOG_ERR("Cannot enable trigger"); diff --git a/app/drivers/sensor/pixart/pmw3610/pmw3610.c b/app/drivers/sensor/pixart/pmw3610/pmw3610.c index 0df4fb5d49d..4ad349c8932 100644 --- a/app/drivers/sensor/pixart/pmw3610/pmw3610.c +++ b/app/drivers/sensor/pixart/pmw3610/pmw3610.c @@ -6,6 +6,9 @@ #define DT_DRV_COMPAT pixart_pmw3610 +// 12-bit two's complement value to int16_t +#define TOINT16(val, bits) (((struct {int16_t value: bits;}){val}).value) + #include #include #include "pmw3610.h" @@ -123,8 +126,8 @@ enum pmw3610_init_step { // - Since MCU is not involved in the sensor init process, i is allowed to do other tasks. // Thus, k_sleep or delayed schedule can be used. static const int32_t async_init_delay[ASYNC_INIT_STEP_COUNT] = { - [ASYNC_INIT_STEP_POWER_UP] = 50, - [ASYNC_INIT_STEP_CLEAR_OB1] = 1, // 150 us, rounded to 1 ms + [ASYNC_INIT_STEP_POWER_UP] = 0, + [ASYNC_INIT_STEP_CLEAR_OB1] = 50, // 150 us, rounded to 1 ms [ASYNC_INIT_STEP_CHECK_OB1] = 10, // required in spec [ASYNC_INIT_STEP_CONFIGURE] = 0, }; @@ -588,8 +591,8 @@ static int pmw3610_async_init_power_up(const struct device *dev) spi_cs_ctrl(dev, true); /* to be tested: not required in datashet Reset sensor */ - /* return reg_write(dev, PMW3610_REG_POWER_UP_RESET, PMW3610_POWERUP_CMD_RESET); */ - return 0; + return reg_write(dev, PMW3610_REG_POWER_UP_RESET, PMW3610_POWERUP_CMD_RESET); + /* return 0; */ } static int pmw3610_async_init_clear_ob1(const struct device *dev) @@ -610,7 +613,7 @@ static int pmw3610_async_init_check_ob1(const struct device *dev) return err; } - if( (value & BIT_MASK(3)) != 0x0F ) { + if( (value & 0x0F) != 0x0F ) { LOG_ERR("Failed self-test (0x%x)", value); return -EINVAL; } @@ -890,12 +893,14 @@ static int pmw3610_sample_fetch(const struct device *dev, enum sensor_channel ch int err = motion_burst_read(dev, buf, sizeof(buf)); if (!err) { - int16_t x = (buf[PMW3610_X_L_POS] - + ((int16_t)((buf[PMW3610_XY_H_POS] & 0xF0) << 4))) - / CONFIG_PMW3610_CPI_DIVIDOR; - int16_t y = (buf[PMW3610_Y_L_POS] - + ((int16_t)((buf[PMW3610_XY_H_POS] & 0x0F) << 8))) - / CONFIG_PMW3610_CPI_DIVIDOR; + /* int16_t x = (buf[PMW3610_X_L_POS] */ + /* + ((int16_t)((buf[PMW3610_XY_H_POS] & 0xF0) << 4))) */ + /* / CONFIG_PMW3610_CPI_DIVIDOR; */ + /* int16_t y = (buf[PMW3610_Y_L_POS] */ + /* + ((int16_t)((buf[PMW3610_XY_H_POS] & 0x0F) << 8))) */ + /* / CONFIG_PMW3610_CPI_DIVIDOR; */ + int16_t x = TOINT16((buf[PMW3610_X_L_POS] + ((buf[PMW3610_XY_H_POS] & 0xF0) << 4)),12); + int16_t y = TOINT16((buf[PMW3610_Y_L_POS] + ((buf[PMW3610_XY_H_POS] & 0x0F) << 8)),12); if (IS_ENABLED(CONFIG_PMW3610_ORIENTATION_0)) { data->x = x; From abfd29383fdabfbd2c88a04e2ff55b9266c12187 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Mon, 5 Sep 2022 16:38:11 +0800 Subject: [PATCH 079/157] change the default cpi and orientation of pmw3610 --- app/drivers/sensor/pixart/pmw3610/Kconfig | 2 +- app/drivers/sensor/pixart/pmw3610/pmw3610.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/drivers/sensor/pixart/pmw3610/Kconfig b/app/drivers/sensor/pixart/pmw3610/Kconfig index 93f5306c389..beaeb9c3c50 100644 --- a/app/drivers/sensor/pixart/pmw3610/Kconfig +++ b/app/drivers/sensor/pixart/pmw3610/Kconfig @@ -22,7 +22,7 @@ config PMW3610_SMART_ALGORITHM config PMW3610_CPI int "PMW3610's default CPI" - default 1600 + default 2400 range 200 3200 help Default CPI value, with 200 step diff --git a/app/drivers/sensor/pixart/pmw3610/pmw3610.c b/app/drivers/sensor/pixart/pmw3610/pmw3610.c index 4ad349c8932..b14a1b93934 100644 --- a/app/drivers/sensor/pixart/pmw3610/pmw3610.c +++ b/app/drivers/sensor/pixart/pmw3610/pmw3610.c @@ -899,8 +899,8 @@ static int pmw3610_sample_fetch(const struct device *dev, enum sensor_channel ch /* int16_t y = (buf[PMW3610_Y_L_POS] */ /* + ((int16_t)((buf[PMW3610_XY_H_POS] & 0x0F) << 8))) */ /* / CONFIG_PMW3610_CPI_DIVIDOR; */ - int16_t x = TOINT16((buf[PMW3610_X_L_POS] + ((buf[PMW3610_XY_H_POS] & 0xF0) << 4)),12); - int16_t y = TOINT16((buf[PMW3610_Y_L_POS] + ((buf[PMW3610_XY_H_POS] & 0x0F) << 8)),12); + int16_t x = TOINT16((buf[PMW3610_X_L_POS] + ((buf[PMW3610_XY_H_POS] & 0xF0) << 4)),12) / CONFIG_PMW3610_CPI_DIVIDOR; + int16_t y = TOINT16((buf[PMW3610_Y_L_POS] + ((buf[PMW3610_XY_H_POS] & 0x0F) << 8)),12) / CONFIG_PMW3610_CPI_DIVIDOR; if (IS_ENABLED(CONFIG_PMW3610_ORIENTATION_0)) { data->x = x; From 42e1d11d3eabf425f2526aecc0840f7da7c36da4 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Mon, 5 Sep 2022 17:31:19 +0800 Subject: [PATCH 080/157] [bugfix] in set_sample_time of pmw3610 and paw3395 --- app/drivers/sensor/pixart/paw3395/paw3395.c | 2 +- app/drivers/sensor/pixart/pmw3610/pmw3610.c | 14 ++++---------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/app/drivers/sensor/pixart/paw3395/paw3395.c b/app/drivers/sensor/pixart/paw3395/paw3395.c index 4c30d783726..063adf9e757 100644 --- a/app/drivers/sensor/pixart/paw3395/paw3395.c +++ b/app/drivers/sensor/pixart/paw3395/paw3395.c @@ -639,7 +639,7 @@ static int set_sample_time(const struct device *dev, uint8_t reg_addr, uint32_t /* The sample time is (reg_value * mintime ) ms. 0x00 is rounded to 0x1 */ int err = reg_write(dev, reg_addr, value); - if (!err) { + if (err) { LOG_ERR("Failed to change sample time"); } diff --git a/app/drivers/sensor/pixart/pmw3610/pmw3610.c b/app/drivers/sensor/pixart/pmw3610/pmw3610.c index b14a1b93934..9636a8e603b 100644 --- a/app/drivers/sensor/pixart/pmw3610/pmw3610.c +++ b/app/drivers/sensor/pixart/pmw3610/pmw3610.c @@ -7,6 +7,7 @@ #define DT_DRV_COMPAT pixart_pmw3610 // 12-bit two's complement value to int16_t +// adapted from https://stackoverflow.com/questions/70802306/convert-a-12-bit-signed-number-in-c #define TOINT16(val, bits) (((struct {int16_t value: bits;}){val}).value) #include @@ -127,7 +128,7 @@ enum pmw3610_init_step { // Thus, k_sleep or delayed schedule can be used. static const int32_t async_init_delay[ASYNC_INIT_STEP_COUNT] = { [ASYNC_INIT_STEP_POWER_UP] = 0, - [ASYNC_INIT_STEP_CLEAR_OB1] = 50, // 150 us, rounded to 1 ms + [ASYNC_INIT_STEP_CLEAR_OB1] = 50, // 150 us required, test shows too short, also power-up reset is added in this step, thus using 50 ms [ASYNC_INIT_STEP_CHECK_OB1] = 10, // required in spec [ASYNC_INIT_STEP_CONFIGURE] = 0, }; @@ -449,7 +450,7 @@ static int set_sample_time(const struct device *dev, uint8_t reg_addr, uint32_t /* The sample time is (reg_value * mintime ) ms. 0x00 is rounded to 0x1 */ int err = reg_write(dev, reg_addr, value); - if (!err) { + if (err) { LOG_ERR("Failed to change sample time"); } @@ -590,9 +591,8 @@ static int pmw3610_async_init_power_up(const struct device *dev) spi_cs_ctrl(dev, false); spi_cs_ctrl(dev, true); - /* to be tested: not required in datashet Reset sensor */ + /* not required in datashet, but added any way to have a clear state */ return reg_write(dev, PMW3610_REG_POWER_UP_RESET, PMW3610_POWERUP_CMD_RESET); - /* return 0; */ } static int pmw3610_async_init_clear_ob1(const struct device *dev) @@ -893,12 +893,6 @@ static int pmw3610_sample_fetch(const struct device *dev, enum sensor_channel ch int err = motion_burst_read(dev, buf, sizeof(buf)); if (!err) { - /* int16_t x = (buf[PMW3610_X_L_POS] */ - /* + ((int16_t)((buf[PMW3610_XY_H_POS] & 0xF0) << 4))) */ - /* / CONFIG_PMW3610_CPI_DIVIDOR; */ - /* int16_t y = (buf[PMW3610_Y_L_POS] */ - /* + ((int16_t)((buf[PMW3610_XY_H_POS] & 0x0F) << 8))) */ - /* / CONFIG_PMW3610_CPI_DIVIDOR; */ int16_t x = TOINT16((buf[PMW3610_X_L_POS] + ((buf[PMW3610_XY_H_POS] & 0xF0) << 4)),12) / CONFIG_PMW3610_CPI_DIVIDOR; int16_t y = TOINT16((buf[PMW3610_Y_L_POS] + ((buf[PMW3610_XY_H_POS] & 0x0F) << 8)),12) / CONFIG_PMW3610_CPI_DIVIDOR; From 18a737aeb009e702cfd210d1a6a3c21aab4d8157 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Mon, 5 Sep 2022 17:36:25 +0800 Subject: [PATCH 081/157] [modify] the pixart optical sesor drivers to have the same calling interface There is potential to integrate these driver code more tightly. TBD. --- app/drivers/sensor/pixart/pmw3360/pmw3360.c | 48 ++++++++++----------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/app/drivers/sensor/pixart/pmw3360/pmw3360.c b/app/drivers/sensor/pixart/pmw3360/pmw3360.c index ab982e8aedf..a457f08fea8 100644 --- a/app/drivers/sensor/pixart/pmw3360/pmw3360.c +++ b/app/drivers/sensor/pixart/pmw3360/pmw3360.c @@ -405,7 +405,7 @@ static int burst_write(const struct device *dev, uint8_t reg, const uint8_t *buf return 0; } -static int update_cpi(const struct device *dev, uint32_t cpi) +static int set_cpi(const struct device *dev, uint32_t cpi) { /* Set resolution with CPI step of 100 cpi * 0x00: 100 cpi (minimum cpi) @@ -435,7 +435,7 @@ static int update_cpi(const struct device *dev, uint32_t cpi) } /* unit: ms */ -static int update_downshift_time(const struct device *dev, uint8_t reg_addr, +static int set_downshift_time(const struct device *dev, uint8_t reg_addr, uint32_t time) { /* Set downshift time in ms: @@ -499,7 +499,7 @@ static int update_downshift_time(const struct device *dev, uint8_t reg_addr, } /* set sampling rate in each mode (in ms) */ -static int update_sample_time(const struct device *dev, +static int set_sample_time(const struct device *dev, uint8_t reg_addr_lower, uint8_t reg_addr_upper, uint32_t sample_time) @@ -534,7 +534,7 @@ static int update_sample_time(const struct device *dev, return err; } -static int toggle_rest_modes(const struct device *dev, uint8_t reg_addr, +static int set_rest_modes(const struct device *dev, uint8_t reg_addr, bool enable) { uint8_t value; @@ -732,22 +732,22 @@ static int pmw3360_async_init_configure(const struct device *dev) { int err; - err = update_cpi(dev, CONFIG_PMW3360_CPI); + err = set_cpi(dev, CONFIG_PMW3360_CPI); if (!err) { - err = update_downshift_time(dev, + err = set_downshift_time(dev, PMW3360_REG_RUN_DOWNSHIFT, CONFIG_PMW3360_RUN_DOWNSHIFT_TIME_MS); } if (!err) { - err = update_downshift_time(dev, + err = set_downshift_time(dev, PMW3360_REG_REST1_DOWNSHIFT, CONFIG_PMW3360_REST1_DOWNSHIFT_TIME_MS); } if (!err) { - err = update_downshift_time(dev, + err = set_downshift_time(dev, PMW3360_REG_REST2_DOWNSHIFT, CONFIG_PMW3360_REST2_DOWNSHIFT_TIME_MS); } @@ -997,50 +997,50 @@ static int pmw3360_attr_set(const struct device *dev, enum sensor_channel chan, } switch ((uint32_t)attr) { - case PIXART_ATTR_CPI: - err = update_cpi(dev, PMW3360_SVALUE_TO_CPI(*val)); + case PMW3360_ATTR_CPI: + err = set_cpi(dev, PMW3360_SVALUE_TO_CPI(*val)); break; - case PIXART_ATTR_REST_ENABLE: - err = toggle_rest_modes(dev, + case PMW3360_ATTR_REST_ENABLE: + err = set_rest_modes(dev, PMW3360_REG_CONFIG2, PMW3360_SVALUE_TO_BOOL(*val)); break; - case PIXART_ATTR_RUN_DOWNSHIFT_TIME: - err = update_downshift_time(dev, + case PMW3360_ATTR_RUN_DOWNSHIFT_TIME: + err = set_downshift_time(dev, PMW3360_REG_RUN_DOWNSHIFT, PMW3360_SVALUE_TO_TIME(*val)); break; - case PIXART_ATTR_REST1_DOWNSHIFT_TIME: - err = update_downshift_time(dev, + case PMW3360_ATTR_REST1_DOWNSHIFT_TIME: + err = set_downshift_time(dev, PMW3360_REG_REST1_DOWNSHIFT, PMW3360_SVALUE_TO_TIME(*val)); break; - case PIXART_ATTR_REST2_DOWNSHIFT_TIME: - err = update_downshift_time(dev, + case PMW3360_ATTR_REST2_DOWNSHIFT_TIME: + err = set_downshift_time(dev, PMW3360_REG_REST2_DOWNSHIFT, PMW3360_SVALUE_TO_TIME(*val)); break; - case PIXART_ATTR_REST1_SAMPLE_TIME: - err = update_sample_time(dev, + case PMW3360_ATTR_REST1_SAMPLE_TIME: + err = set_sample_time(dev, PMW3360_REG_REST1_RATE_LOWER, PMW3360_REG_REST1_RATE_UPPER, PMW3360_SVALUE_TO_TIME(*val)); break; - case PIXART_ATTR_REST2_SAMPLE_TIME: - err = update_sample_time(dev, + case PMW3360_ATTR_REST2_SAMPLE_TIME: + err = set_sample_time(dev, PMW3360_REG_REST2_RATE_LOWER, PMW3360_REG_REST2_RATE_UPPER, PMW3360_SVALUE_TO_TIME(*val)); break; - case PIXART_ATTR_REST3_SAMPLE_TIME: - err = update_sample_time(dev, + case PMW3360_ATTR_REST3_SAMPLE_TIME: + err = set_sample_time(dev, PMW3360_REG_REST3_RATE_LOWER, PMW3360_REG_REST3_RATE_UPPER, PMW3360_SVALUE_TO_TIME(*val)); From 861b0ce9c9fce11aff79b27d324d7fa7325582ed Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Wed, 7 Sep 2022 05:44:36 +0800 Subject: [PATCH 082/157] [change] add default zmk log level to info and add log output of connection parameters on connection --- app/Kconfig | 4 ++-- app/src/ble.c | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/Kconfig b/app/Kconfig index ba9c9722014..26c7f300ebb 100644 --- a/app/Kconfig +++ b/app/Kconfig @@ -407,8 +407,8 @@ config ZMK_USB_LOGGING if ZMK_USB_LOGGING -config ZMK_LOG_LEVEL - default 1 +# config ZMK_LOG_LEVEL +# default 1 # We do this to avoid log loop where logging to USB generates more log messages. config USB_CDC_ACM_LOG_LEVEL diff --git a/app/src/ble.c b/app/src/ble.c index b10aa20b5f7..db1b5eeb223 100644 --- a/app/src/ble.c +++ b/app/src/ble.c @@ -389,6 +389,9 @@ static void connected(struct bt_conn *conn, uint8_t err) { update_advertising(); + LOG_INF("New connection params: Interval: %d, Latency: %d, RX: %d, TX: %d", info.le.interval, + info.le.latency, info.le.phy->rx_phy, info.le.phy->tx_phy); + if (is_conn_active_profile(conn)) { LOG_DBG("Active profile connected"); k_work_submit(&raise_profile_changed_event_work); @@ -438,7 +441,7 @@ static void le_param_updated(struct bt_conn *conn, uint16_t interval, uint16_t l bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); - LOG_DBG("%s: interval %d latency %d timeout %d", log_strdup(addr), interval, latency, timeout); + LOG_INF("%s: interval %d latency %d timeout %d", log_strdup(addr), interval, latency, timeout); } static struct bt_conn_cb conn_callbacks = { From f7ec5eb98b498388fbc06db92ee0d709e94f987e Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Wed, 7 Sep 2022 08:17:48 +0800 Subject: [PATCH 083/157] change pmw3610 delayed init timing --- app/drivers/sensor/pixart/pmw3610/pmw3610.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/drivers/sensor/pixart/pmw3610/pmw3610.c b/app/drivers/sensor/pixart/pmw3610/pmw3610.c index 9636a8e603b..eb4fd8a2718 100644 --- a/app/drivers/sensor/pixart/pmw3610/pmw3610.c +++ b/app/drivers/sensor/pixart/pmw3610/pmw3610.c @@ -127,9 +127,9 @@ enum pmw3610_init_step { // - Since MCU is not involved in the sensor init process, i is allowed to do other tasks. // Thus, k_sleep or delayed schedule can be used. static const int32_t async_init_delay[ASYNC_INIT_STEP_COUNT] = { - [ASYNC_INIT_STEP_POWER_UP] = 0, + [ASYNC_INIT_STEP_POWER_UP] = 1, //wain a short time before init [ASYNC_INIT_STEP_CLEAR_OB1] = 50, // 150 us required, test shows too short, also power-up reset is added in this step, thus using 50 ms - [ASYNC_INIT_STEP_CHECK_OB1] = 10, // required in spec + [ASYNC_INIT_STEP_CHECK_OB1] = 20, // 10 ms required in spec, change it to 20 ms for certainty [ASYNC_INIT_STEP_CONFIGURE] = 0, }; From db82302cc76862078ab6e55cc7947e8bdbe194da Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Fri, 9 Sep 2022 21:39:19 +0800 Subject: [PATCH 084/157] [optimize] il0323 driver, comment out the using of epd reset pin, thus only 7 pins needed for epd connection 1. reset pin is only used at the beginning of il0323_init, as power-on reset 2. tests show that disabling the usage does not affect the correct functioning of epd --- app/drivers/display/il0323.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/app/drivers/display/il0323.c b/app/drivers/display/il0323.c index 452f88ff32a..5fd5be0955a 100644 --- a/app/drivers/display/il0323.c +++ b/app/drivers/display/il0323.c @@ -37,9 +37,9 @@ LOG_MODULE_REGISTER(il0323, CONFIG_DISPLAY_LOG_LEVEL); #define IL0323_BUSY_PIN DT_INST_GPIO_PIN(0, busy_gpios) #define IL0323_BUSY_CNTRL DT_INST_GPIO_LABEL(0, busy_gpios) #define IL0323_BUSY_FLAGS DT_INST_GPIO_FLAGS(0, busy_gpios) -#define IL0323_RESET_PIN DT_INST_GPIO_PIN(0, reset_gpios) -#define IL0323_RESET_CNTRL DT_INST_GPIO_LABEL(0, reset_gpios) -#define IL0323_RESET_FLAGS DT_INST_GPIO_FLAGS(0, reset_gpios) +/* #define IL0323_RESET_PIN DT_INST_GPIO_PIN(0, reset_gpios) */ +/* #define IL0323_RESET_CNTRL DT_INST_GPIO_LABEL(0, reset_gpios) */ +/* #define IL0323_RESET_FLAGS DT_INST_GPIO_FLAGS(0, reset_gpios) */ #define EPD_PANEL_WIDTH DT_INST_PROP(0, width) #define EPD_PANEL_HEIGHT DT_INST_PROP(0, height) @@ -283,11 +283,11 @@ static int il0323_controller_init(const struct device *dev) { LOG_DBG(""); - gpio_pin_set(driver->reset, IL0323_RESET_PIN, 1); - k_msleep(IL0323_RESET_DELAY); - gpio_pin_set(driver->reset, IL0323_RESET_PIN, 0); - k_msleep(IL0323_RESET_DELAY); - il0323_busy_wait(driver); + /* gpio_pin_set(driver->reset, IL0323_RESET_PIN, 1); */ + /* k_msleep(IL0323_RESET_DELAY); */ + /* gpio_pin_set(driver->reset, IL0323_RESET_PIN, 0); */ + /* k_msleep(IL0323_RESET_DELAY); */ + /* il0323_busy_wait(driver); */ LOG_DBG("Initialize IL0323 controller"); @@ -380,13 +380,13 @@ static int il0323_init(const struct device *dev) { driver->spi_config.slave = DT_INST_REG_ADDR(0); driver->spi_config.cs = NULL; - driver->reset = device_get_binding(IL0323_RESET_CNTRL); - if (driver->reset == NULL) { - LOG_ERR("Could not get GPIO port for IL0323 reset"); - return -EIO; - } + /* driver->reset = device_get_binding(IL0323_RESET_CNTRL); */ + /* if (driver->reset == NULL) { */ + /* LOG_ERR("Could not get GPIO port for IL0323 reset"); */ + /* return -EIO; */ + /* } */ - gpio_pin_configure(driver->reset, IL0323_RESET_PIN, GPIO_OUTPUT_INACTIVE | IL0323_RESET_FLAGS); + /* gpio_pin_configure(driver->reset, IL0323_RESET_PIN, GPIO_OUTPUT_INACTIVE | IL0323_RESET_FLAGS); */ driver->dc = device_get_binding(IL0323_DC_CNTRL); if (driver->dc == NULL) { From f8377890e7aaf0eead9fd8d8c80bf91b91780b5b Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Sat, 10 Sep 2022 16:21:27 +0800 Subject: [PATCH 085/157] [bug-fix] pwm3610 driver init timing parameter tuning, now can be integrated with displays --- app/drivers/sensor/pixart/paw3395/paw3395.c | 8 ++++++++ app/drivers/sensor/pixart/pmw3610/pmw3610.c | 18 +++++++++++------- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/app/drivers/sensor/pixart/paw3395/paw3395.c b/app/drivers/sensor/pixart/paw3395/paw3395.c index 063adf9e757..0d7aa19cfa4 100644 --- a/app/drivers/sensor/pixart/paw3395/paw3395.c +++ b/app/drivers/sensor/pixart/paw3395/paw3395.c @@ -13,6 +13,14 @@ #include LOG_MODULE_REGISTER(paw3395, CONFIG_PAW3395_LOG_LEVEL); +// The following claim needs more support from tests, comment out at the moment +/* /\* It is observed that sensors should be initialized before the diplay drivers. */ +/* * Here, we assure the correct sequence in the build time. */ +/* * Setting sensor priority to 80 in the shield conf file shall be fine *\/ */ +/* BUILD_ASSERT(CONFIG_SENSOR_INIT_PRIORITY < CONFIG_APPLICATION_INIT_PRIORITY, */ +/* "CONFIG_SENSOR_INIT_PRIORITY must be less than CONFIG_APPLICATION_INIT_PRIORITY.\ */ +/* Try setting the sensor priority to 80"); */ + /* Timings (in us) used in SPI communication. Since MCU should not do other tasks during wait, k_busy_wait is used instead of k_sleep */ // - sub-us time is rounded to us, due to the limitation of k_busy_wait, see : https://github.com/zephyrproject-rtos/zephyr/issues/6498 #define T_NCS_SCLK 1 /* 120 ns (rounded to 1us) */ diff --git a/app/drivers/sensor/pixart/pmw3610/pmw3610.c b/app/drivers/sensor/pixart/pmw3610/pmw3610.c index eb4fd8a2718..515131795fb 100644 --- a/app/drivers/sensor/pixart/pmw3610/pmw3610.c +++ b/app/drivers/sensor/pixart/pmw3610/pmw3610.c @@ -127,9 +127,13 @@ enum pmw3610_init_step { // - Since MCU is not involved in the sensor init process, i is allowed to do other tasks. // Thus, k_sleep or delayed schedule can be used. static const int32_t async_init_delay[ASYNC_INIT_STEP_COUNT] = { - [ASYNC_INIT_STEP_POWER_UP] = 1, //wain a short time before init - [ASYNC_INIT_STEP_CLEAR_OB1] = 50, // 150 us required, test shows too short, also power-up reset is added in this step, thus using 50 ms - [ASYNC_INIT_STEP_CHECK_OB1] = 20, // 10 ms required in spec, change it to 20 ms for certainty + [ASYNC_INIT_STEP_POWER_UP] = 5, // test shows > 5ms needed + [ASYNC_INIT_STEP_CLEAR_OB1] = 50, // 150 us required, test shows too short, + // also power-up reset is added in this step, thus using 50 ms + [ASYNC_INIT_STEP_CHECK_OB1] = 50, // 10 ms required in spec, + // test shows too short,\ + especially when integrated with display,\ + > 50ms is needed [ASYNC_INIT_STEP_CONFIGURE] = 0, }; @@ -631,7 +635,7 @@ static int pmw3610_async_init_configure(const struct device *dev) { LOG_INF("async_init_configure"); - int err; + int err=0; // clear motion registers first (required in datasheet) for (uint8_t reg = 0x02; (reg <= 0x05) && !err; reg++) { @@ -897,17 +901,17 @@ static int pmw3610_sample_fetch(const struct device *dev, enum sensor_channel ch int16_t y = TOINT16((buf[PMW3610_Y_L_POS] + ((buf[PMW3610_XY_H_POS] & 0x0F) << 8)),12) / CONFIG_PMW3610_CPI_DIVIDOR; if (IS_ENABLED(CONFIG_PMW3610_ORIENTATION_0)) { - data->x = x; + data->x = -x; data->y = y; } else if (IS_ENABLED(CONFIG_PMW3610_ORIENTATION_90)) { data->x = y; - data->y = x; + data->y = -x; } else if (IS_ENABLED(CONFIG_PMW3610_ORIENTATION_180)) { data->x = x; data->y = -y; } else if (IS_ENABLED(CONFIG_PMW3610_ORIENTATION_270)) { data->x = -y; - data->y = -x; + data->y = x; } #ifdef CONFIG_PMW3610_SMART_ALGORITHM From bf4c07733f59807ecd6c9a1d1f4e2329b0174ce3 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Sat, 10 Sep 2022 16:55:00 +0800 Subject: [PATCH 086/157] [bugfix] tuning the paw3395 init timing --- app/drivers/sensor/pixart/paw3395/paw3395.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/app/drivers/sensor/pixart/paw3395/paw3395.c b/app/drivers/sensor/pixart/paw3395/paw3395.c index 0d7aa19cfa4..6d97f485293 100644 --- a/app/drivers/sensor/pixart/paw3395/paw3395.c +++ b/app/drivers/sensor/pixart/paw3395/paw3395.c @@ -13,13 +13,7 @@ #include LOG_MODULE_REGISTER(paw3395, CONFIG_PAW3395_LOG_LEVEL); -// The following claim needs more support from tests, comment out at the moment -/* /\* It is observed that sensors should be initialized before the diplay drivers. */ -/* * Here, we assure the correct sequence in the build time. */ -/* * Setting sensor priority to 80 in the shield conf file shall be fine *\/ */ -/* BUILD_ASSERT(CONFIG_SENSOR_INIT_PRIORITY < CONFIG_APPLICATION_INIT_PRIORITY, */ -/* "CONFIG_SENSOR_INIT_PRIORITY must be less than CONFIG_APPLICATION_INIT_PRIORITY.\ */ -/* Try setting the sensor priority to 80"); */ +/* todo: add reset pin into the init sequence */ /* Timings (in us) used in SPI communication. Since MCU should not do other tasks during wait, k_busy_wait is used instead of k_sleep */ // - sub-us time is rounded to us, due to the limitation of k_busy_wait, see : https://github.com/zephyrproject-rtos/zephyr/issues/6498 @@ -158,7 +152,7 @@ enum paw3395_init_step { // Thus, k_sleep or delayed schedule can be used. static const int32_t async_init_delay[ASYNC_INIT_STEP_COUNT] = { [ASYNC_INIT_STEP_POWER_UP] = 50, // required in spec - [ASYNC_INIT_STEP_LOAD_SETTING] = 5, // required in spec + [ASYNC_INIT_STEP_LOAD_SETTING] = 10, // 5ms required in spec, tests show >10ms [ASYNC_INIT_STEP_CONFIGURE] = 0, }; From dffdf6aa10d511343dcb90f7bced90b6792d3d5c Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Sat, 10 Sep 2022 17:16:38 +0800 Subject: [PATCH 087/157] [change] default cpi of pmw3610 to maximum 3200 --- app/drivers/sensor/pixart/pmw3610/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/drivers/sensor/pixart/pmw3610/Kconfig b/app/drivers/sensor/pixart/pmw3610/Kconfig index beaeb9c3c50..78cb8d4695b 100644 --- a/app/drivers/sensor/pixart/pmw3610/Kconfig +++ b/app/drivers/sensor/pixart/pmw3610/Kconfig @@ -22,14 +22,14 @@ config PMW3610_SMART_ALGORITHM config PMW3610_CPI int "PMW3610's default CPI" - default 2400 + default 3200 range 200 3200 help Default CPI value, with 200 step config PMW3610_CPI_DIVIDOR int "PMW3610's default CPI dividor" - default 4 + default 6 range 1 100 help Default CPI dividor value. From bef5254ca1bef5718498fcc1170cd86691bfa038 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Tue, 13 Sep 2022 07:33:42 +0800 Subject: [PATCH 088/157] pmw3610, init timing parameter tuning --- app/drivers/sensor/pixart/pmw3610/pmw3610.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/drivers/sensor/pixart/pmw3610/pmw3610.c b/app/drivers/sensor/pixart/pmw3610/pmw3610.c index 515131795fb..a2e971a5e63 100644 --- a/app/drivers/sensor/pixart/pmw3610/pmw3610.c +++ b/app/drivers/sensor/pixart/pmw3610/pmw3610.c @@ -128,7 +128,7 @@ enum pmw3610_init_step { // Thus, k_sleep or delayed schedule can be used. static const int32_t async_init_delay[ASYNC_INIT_STEP_COUNT] = { [ASYNC_INIT_STEP_POWER_UP] = 5, // test shows > 5ms needed - [ASYNC_INIT_STEP_CLEAR_OB1] = 50, // 150 us required, test shows too short, + [ASYNC_INIT_STEP_CLEAR_OB1] = 100, // 150 us required, test shows too short, // also power-up reset is added in this step, thus using 50 ms [ASYNC_INIT_STEP_CHECK_OB1] = 50, // 10 ms required in spec, // test shows too short,\ From f5697e9ff507dab01396f68b9722cefda8caa6ab Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Wed, 28 Sep 2022 20:34:45 +0800 Subject: [PATCH 089/157] [touch-bar] add init version of cap1203 driver --- app/drivers/kscan/CMakeLists.txt | 1 + app/drivers/kscan/Kconfig | 22 ++ app/drivers/kscan/kscan_cap1203.c | 324 ++++++++++++++++++ app/drivers/sensor/pixart/pmw3610/pmw3610.c | 6 +- .../dts/bindings/kscan/zmk,kscan-cap1203.yaml | 13 + 5 files changed, 363 insertions(+), 3 deletions(-) create mode 100644 app/drivers/kscan/kscan_cap1203.c create mode 100644 app/drivers/zephyr/dts/bindings/kscan/zmk,kscan-cap1203.yaml diff --git a/app/drivers/kscan/CMakeLists.txt b/app/drivers/kscan/CMakeLists.txt index ced31e6f67e..878d48267b4 100644 --- a/app/drivers/kscan/CMakeLists.txt +++ b/app/drivers/kscan/CMakeLists.txt @@ -10,3 +10,4 @@ zephyr_library_sources_ifdef(CONFIG_ZMK_KSCAN_GPIO_DIRECT kscan_gpio_direct.c) zephyr_library_sources_ifdef(CONFIG_ZMK_KSCAN_GPIO_DEMUX kscan_gpio_demux.c) zephyr_library_sources_ifdef(CONFIG_ZMK_KSCAN_MOCK_DRIVER kscan_mock.c) zephyr_library_sources_ifdef(CONFIG_ZMK_KSCAN_COMPOSITE_DRIVER kscan_composite.c) +zephyr_library_sources_ifdef(CONFIG_ZMK_KSCAN_CAP1203 kscan_cap1203.c) diff --git a/app/drivers/kscan/Kconfig b/app/drivers/kscan/Kconfig index c9ace0a30c6..05b1925d906 100644 --- a/app/drivers/kscan/Kconfig +++ b/app/drivers/kscan/Kconfig @@ -6,6 +6,7 @@ DT_COMPAT_ZMK_KSCAN_GPIO_DEMUX := zmk,kscan-gpio-demux DT_COMPAT_ZMK_KSCAN_GPIO_DIRECT := zmk,kscan-gpio-direct DT_COMPAT_ZMK_KSCAN_GPIO_MATRIX := zmk,kscan-gpio-matrix DT_COMPAT_ZMK_KSCAN_MOCK := zmk,kscan-mock +DT_COMPAT_ZMK_KSCAN_CAP1203 := zmk,kscan-cap1203 config ZMK_KSCAN_COMPOSITE_DRIVER bool @@ -34,6 +35,27 @@ config ZMK_KSCAN_MOCK_DRIVER bool default $(dt_compat_enabled,$(DT_COMPAT_ZMK_KSCAN_MOCK)) +config ZMK_KSCAN_CAP1203 + bool + default $(dt_compat_enabled,$(DT_COMPAT_ZMK_KSCAN_CAP1203)) + select I2C + +if ZMK_KSCAN_CAP1203 + +config ZMK_KSCAN_CAP1203_POLL + bool "Polling" + help + Enable polling mode when interrupt GPIO is not specified. + +config ZMK_KSCAN_CAP1203_PERIOD + int "Sample period" + depends on ZMK_KSCAN_CAP1203_POLL + default 10 + help + Sample period in milliseconds when in polling mode. + +endif # ZMK_KSCAN_CAP1203 + if ZMK_KSCAN_GPIO_DRIVER config ZMK_KSCAN_MATRIX_POLLING diff --git a/app/drivers/kscan/kscan_cap1203.c b/app/drivers/kscan/kscan_cap1203.c new file mode 100644 index 00000000000..bdeabb2b1d1 --- /dev/null +++ b/app/drivers/kscan/kscan_cap1203.c @@ -0,0 +1,324 @@ +/* + * Copyright (c) 2022 Keiya Nobuta + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT zmk_kscan_cap1203 + +#define USE_POLLING IS_ENABLED(CONFIG_ZMK_KSCAN_CAP1203_POLL) +#define USE_INTERRUPTS (!USE_POLLING) + +#include +#include +#include +#include + +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#define REG_MAIN_CONTROL 0x0 +#define CONTROL_INT 0x1 + +#define REG_INPUT_STATUS 0x03 + +#define REG_INTERRUPT_ENABLE 0x27 +#define INTERRUPT_ENABLE 0x7 +#define INTERRUPT_DISABLE 0x0 + + +struct kscan_cap1203_config { + struct i2c_dt_spec i2c; + struct gpio_dt_spec int_gpio; +}; + +struct kscan_cap1203_data { + struct device *dev; + kscan_callback_t callback; + struct k_work work; // actual processing work + struct k_work_delayable release_work_a; + struct k_work_delayable release_work_b; + struct k_work_delayable release_work_c; + /* Interrupt GPIO callback. */ + struct gpio_callback int_gpio_cb; +#if USE_POLLING + /* Timer (polling mode). */ + struct k_timer timer; +#endif +}; + +/* De-assert alert line and clear the sensor status input register */ +static int kscan_cap1203_clear_interrupt(const struct i2c_dt_spec *i2c) +{ + uint8_t ctrl; + int r; + + r = i2c_reg_read_byte_dt(i2c, REG_MAIN_CONTROL, &ctrl); + if (r < 0) { + return r; + } + + ctrl = ctrl & ~CONTROL_INT; + return i2c_reg_write_byte_dt(i2c, REG_MAIN_CONTROL, ctrl); +} + +/* Enable/disable the alert line */ +static int kscan_cap1203_enable_interrupt(const struct i2c_dt_spec *i2c, bool enable) +{ + uint8_t intr = enable ? INTERRUPT_ENABLE : INTERRUPT_DISABLE; + + return i2c_reg_write_byte_dt(i2c, REG_INTERRUPT_ENABLE, intr); +} + +/* read the current touch status */ +static int kscan_cap1203_read(const struct device *dev) +{ + const struct kscan_cap1203_config *config = dev->config; + struct kscan_cap1203_data *data = dev->data; + int r; + uint8_t input; + bool pressed; + + /* todo: release previus presses if not yet released */ + /* k_work_flush_delayable(&data->release_work_a, NULL); */ + /* k_work_flush_delayable(&data->release_work_b, NULL); */ + /* k_work_flush_delayable(&data->release_work_c, NULL); */ + + r = i2c_reg_read_byte_dt(&config->i2c, REG_INPUT_STATUS, &input); + if (r < 0) { + return r; + } + + // + LOG_INF("event: input: %d", input); + pressed = !!input; // !! can turn an arbitraty integer into 1 or 0 + + if (input & BIT(2)) { + data->callback(dev, 0, 2, pressed); + + LOG_INF("scheduled to release for button c"); + k_work_schedule(&data->release_work_c, K_MSEC(1)); + } + if (input & BIT(1)) { + data->callback(dev, 0, 1, pressed); + + LOG_INF("scheduled to release for button b"); + k_work_schedule(&data->release_work_b, K_MSEC(1)); + } + if (input & BIT(0)) { + data->callback(dev, 0, 0, pressed); + + LOG_INF("scheduled to release for button a"); + k_work_schedule(&data->release_work_a, K_MSEC(1)); + } + + + /* + * Clear INT bit to clear SENSOR INPUT STATUS bits. + * Note that this is also required in polling mode. + */ + r = kscan_cap1203_clear_interrupt(&config->i2c); + if (r < 0) { + return r; + } + + return 0; +} + +static void kscan_cap1203_release_handler_a(struct k_work *work) +{ + struct kscan_cap1203_data *data = CONTAINER_OF(work, struct kscan_cap1203_data, release_work_a); + + data->callback(data->dev, 0, 0, false); + LOG_INF("rlease button a\n"); +} + +static void kscan_cap1203_release_handler_b(struct k_work *work) +{ + struct kscan_cap1203_data *data = CONTAINER_OF(work, struct kscan_cap1203_data, release_work_b); + + data->callback(data->dev, 0, 1, false); + LOG_INF("rlease button b\n"); +} + +static void kscan_cap1203_release_handler_c(struct k_work *work) +{ + struct kscan_cap1203_data *data = CONTAINER_OF(work, struct kscan_cap1203_data, release_work_c); + + data->callback(data->dev, 0, 2, false); + LOG_INF("rlease button c\n"); +} + +/* the actual work to process each alert or polling */ +static void kscan_cap1203_work_handler(struct k_work *work) +{ + struct kscan_cap1203_data *data = CONTAINER_OF(work, struct kscan_cap1203_data, work); + + kscan_cap1203_read(data->dev); +} + +/* ISR handler: immediatly dispatch the job to the work handler */ +static void kscan_cap1203_irq_handler(const struct device *dev, + struct gpio_callback *cb, uint32_t pins) +{ + struct kscan_cap1203_data *data = CONTAINER_OF(cb, struct kscan_cap1203_data, int_gpio_cb); + + k_work_submit(&data->work); +} + +/* Timer handler in poll mode: immediately dispatch the job to the actual work handler */ +#if USE_POLLING +static void kscan_cap1203_timer_handler(struct k_timer *timer) +{ + struct kscan_cap1203_data *data = CONTAINER_OF(timer, struct kscan_cap1203_data, timer); + + k_work_submit(&data->work); +} +#endif + +/* api callbacks */ +static int kscan_cap1203_configure(const struct device *dev, + kscan_callback_t callback) +{ + struct kscan_cap1203_data *data = dev->data; + const struct kscan_cap1203_config *config = dev->config; + + data->callback = callback; + + if (config->int_gpio.port != NULL) { + int r; + + /* Clear pending interrupt */ + r = kscan_cap1203_clear_interrupt(&config->i2c); + if (r < 0) { + LOG_ERR("Could not clear interrupt"); + return r; + } + + r = kscan_cap1203_enable_interrupt(&config->i2c, true); + if (r < 0) { + LOG_ERR("Could not configure interrupt"); + return r; + } + } + + return 0; +} + +static int kscan_cap1203_enable_callback(const struct device *dev) +{ + struct kscan_cap1203_data *data = dev->data; + + const struct kscan_cap1203_config *config = dev->config; + + if (config->int_gpio.port != NULL) { + gpio_add_callback(config->int_gpio.port, &data->int_gpio_cb); + } +#if USE_POLLING + else { + k_timer_start(&data->timer, K_MSEC(CONFIG_ZMK_KSCAN_CAP1203_PERIOD), + K_MSEC(CONFIG_ZMK_KSCAN_CAP1203_PERIOD)); + } +#endif + return 0; +} + +static int kscan_cap1203_disable_callback(const struct device *dev) +{ + struct kscan_cap1203_data *data = dev->data; + + const struct kscan_cap1203_config *config = dev->config; + + if (config->int_gpio.port != NULL) { + gpio_remove_callback(config->int_gpio.port, &data->int_gpio_cb); + } +#if USE_POLLING + else { + k_timer_stop(&data->timer); + } +#endif + return 0; +} + +static int kscan_cap1203_init(const struct device *dev) +{ + const struct kscan_cap1203_config *config = dev->config; + struct kscan_cap1203_data *data = dev->data; + int r; + + // check readiness of i2c bus + if (!device_is_ready(config->i2c.bus)) { + LOG_ERR("I2C controller device not ready"); + return -ENODEV; + } + + // init data structure + data->dev = dev; + + k_work_init(&data->work, kscan_cap1203_work_handler); + + k_work_init_delayable(&data->release_work_a, kscan_cap1203_release_handler_a); + k_work_init_delayable(&data->release_work_b, kscan_cap1203_release_handler_b); + k_work_init_delayable(&data->release_work_c, kscan_cap1203_release_handler_c); + + + // init alert interrupt pin or poll timer + if (config->int_gpio.port != NULL) { + if (!device_is_ready(config->int_gpio.port)) { + LOG_ERR("Interrupt GPIO controller device not ready"); + return -ENODEV; + } + + r = gpio_pin_configure_dt(&config->int_gpio, GPIO_INPUT); + if (r < 0) { + LOG_ERR("Could not confighure interrupt GPIO pin"); + return r; + } + + r = gpio_pin_interrupt_configure_dt(&config->int_gpio, + GPIO_INT_EDGE_TO_ACTIVE); + if (r < 0) { + LOG_ERR("Could not configure interrupt GPIO interrupt"); + return r; + } + + gpio_init_callback(&data->int_gpio_cb, kscan_cap1203_irq_handler, + BIT(config->int_gpio.pin)); + } +#if USE_POLLING + else { + k_timer_init(&data->timer, kscan_cap1203_timer_handler, NULL); + + // disable alert function in poll mode + r = kscan_cap1203_enable_interrupt(&config->i2c, false); + if (r < 0) { + LOG_ERR("Could not configure interrupt"); + return r; + } + } +#endif + + LOG_INF("Init success"); + + return 0; +} + +static const struct kscan_driver_api kscan_cap1203_driver_api = { + .config = kscan_cap1203_configure, + .enable_callback = kscan_cap1203_enable_callback, + .disable_callback = kscan_cap1203_disable_callback, +}; + +#define KSCAN_CAP1203_INIT(index) \ + static const struct kscan_cap1203_config kscan_cap1203_config_##index = { \ + .i2c = I2C_DT_SPEC_INST_GET(index), \ + .int_gpio = GPIO_DT_SPEC_INST_GET_OR(index, int_gpios, {0}), \ + }; \ + static struct kscan_cap1203_data kscan_cap1203_data_##index; \ + DEVICE_DT_INST_DEFINE(index, kscan_cap1203_init, NULL, \ + &kscan_cap1203_data_##index, &kscan_cap1203_config_##index, \ + APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY , \ + &kscan_cap1203_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(KSCAN_CAP1203_INIT) diff --git a/app/drivers/sensor/pixart/pmw3610/pmw3610.c b/app/drivers/sensor/pixart/pmw3610/pmw3610.c index a2e971a5e63..fdbe037f2ca 100644 --- a/app/drivers/sensor/pixart/pmw3610/pmw3610.c +++ b/app/drivers/sensor/pixart/pmw3610/pmw3610.c @@ -131,9 +131,9 @@ static const int32_t async_init_delay[ASYNC_INIT_STEP_COUNT] = { [ASYNC_INIT_STEP_CLEAR_OB1] = 100, // 150 us required, test shows too short, // also power-up reset is added in this step, thus using 50 ms [ASYNC_INIT_STEP_CHECK_OB1] = 50, // 10 ms required in spec, - // test shows too short,\ - especially when integrated with display,\ - > 50ms is needed + // test shows too short, + // especially when integrated with display, + // > 50ms is needed [ASYNC_INIT_STEP_CONFIGURE] = 0, }; diff --git a/app/drivers/zephyr/dts/bindings/kscan/zmk,kscan-cap1203.yaml b/app/drivers/zephyr/dts/bindings/kscan/zmk,kscan-cap1203.yaml new file mode 100644 index 00000000000..fb974866aea --- /dev/null +++ b/app/drivers/zephyr/dts/bindings/kscan/zmk,kscan-cap1203.yaml @@ -0,0 +1,13 @@ +# Copyright (c) 2022 Keiya Nobuta +# SPDX-License-Identifier: Apache-2.0 + +description: CAP1203 3-channel capacitive touch sensor + +compatible: "zmk,kscan-cap1203" + +include: [kscan.yaml, i2c-device.yaml] + +properties: + int-gpios: + type: phandle-array + required: false From c85fd1608a794c72acd1f2c75763a02f36414bdc Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Thu, 29 Sep 2022 09:26:38 +0800 Subject: [PATCH 090/157] backup --- app/drivers/kscan/kscan_cap1203.c | 36 ++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/app/drivers/kscan/kscan_cap1203.c b/app/drivers/kscan/kscan_cap1203.c index bdeabb2b1d1..14a37523072 100644 --- a/app/drivers/kscan/kscan_cap1203.c +++ b/app/drivers/kscan/kscan_cap1203.c @@ -16,8 +16,9 @@ #include -LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); +LOG_MODULE_REGISTER(kscan_cap1203, CONFIG_ZMK_LOG_LEVEL); +/* List of important registers and associated commands */ #define REG_MAIN_CONTROL 0x0 #define CONTROL_INT 0x1 @@ -27,7 +28,10 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #define INTERRUPT_ENABLE 0x7 #define INTERRUPT_DISABLE 0x0 +#define CONFIGURATION_2 0x44 +#define RELEASE_INT_POS 0 +/* device driver data */ struct kscan_cap1203_config { struct i2c_dt_spec i2c; struct gpio_dt_spec int_gpio; @@ -48,6 +52,29 @@ struct kscan_cap1203_data { #endif }; +/* Write a single bit in a register without touching other bits */ +static int kscan_cap1203_bit_write(const struct i2c_dt_spec *i2c, uint8_t reg, \ + uint8_t pos, bool enable) +{ + uint8_t val; + int err; + + if(err=i2c_reg_read_byte_dt(i2c, reg, &val)) { + return err; + } + + WRITE_BIT(reg, pos, enable); + return i2c_reg_write_byte_dt(i2c, reg, val); +} + +/* Enable/disable generation of release interrupt */ +static int kscan_cap1203_enable_release_interrupt(const struct i2c_dt_spec *i2c,\ + bool enable) +{ + return kscan_cap1203_bit_write(i2c, CONFIGURATION_2, RELEASE_INT_POS, !enable); +} + + /* De-assert alert line and clear the sensor status input register */ static int kscan_cap1203_clear_interrupt(const struct i2c_dt_spec *i2c) { @@ -299,6 +326,13 @@ static int kscan_cap1203_init(const struct device *dev) } #endif + // other configuration + r = kscan_cap1203_enable_release_interrupt(&config->i2c, false); + if(r) { + LOG_ERR("Could not disable release interrupt"); + return r; + } + LOG_INF("Init success"); return 0; From 0b4638ea41f5c7573fcca264d6e09a40c6b940c8 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Thu, 29 Sep 2022 21:22:12 +0800 Subject: [PATCH 091/157] [cap1203] bugfix --- app/drivers/kscan/kscan_cap1203.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/drivers/kscan/kscan_cap1203.c b/app/drivers/kscan/kscan_cap1203.c index 14a37523072..20e592c8fbb 100644 --- a/app/drivers/kscan/kscan_cap1203.c +++ b/app/drivers/kscan/kscan_cap1203.c @@ -63,7 +63,8 @@ static int kscan_cap1203_bit_write(const struct i2c_dt_spec *i2c, uint8_t reg, \ return err; } - WRITE_BIT(reg, pos, enable); + WRITE_BIT(val, pos, enable); + return i2c_reg_write_byte_dt(i2c, reg, val); } @@ -87,6 +88,7 @@ static int kscan_cap1203_clear_interrupt(const struct i2c_dt_spec *i2c) } ctrl = ctrl & ~CONTROL_INT; + return i2c_reg_write_byte_dt(i2c, REG_MAIN_CONTROL, ctrl); } From 52931c19875989f2de3229c1d49b4d6fac90e78c Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Sun, 2 Oct 2022 21:14:07 +0800 Subject: [PATCH 092/157] [cap1203] single button is working --- app/drivers/kscan/kscan_cap1203.c | 193 +++++++++++++++++++----------- app/src/hog.c | 4 + 2 files changed, 128 insertions(+), 69 deletions(-) diff --git a/app/drivers/kscan/kscan_cap1203.c b/app/drivers/kscan/kscan_cap1203.c index 20e592c8fbb..28cdf0620f9 100644 --- a/app/drivers/kscan/kscan_cap1203.c +++ b/app/drivers/kscan/kscan_cap1203.c @@ -19,16 +19,28 @@ LOG_MODULE_REGISTER(kscan_cap1203, CONFIG_ZMK_LOG_LEVEL); /* List of important registers and associated commands */ +#define REG_PRODUCT_ID 0xFD +#define PRODUCT_ID 0x6D +#define REG_VENDOR_ID 0xFE +#define VENDOR_ID 0x5D +#define REG_REVISION 0xFF +#define REVISION_ID 0x00 + #define REG_MAIN_CONTROL 0x0 #define CONTROL_INT 0x1 +#define REG_GENERAL_STATUS 0x02 #define REG_INPUT_STATUS 0x03 #define REG_INTERRUPT_ENABLE 0x27 #define INTERRUPT_ENABLE 0x7 #define INTERRUPT_DISABLE 0x0 -#define CONFIGURATION_2 0x44 +#define REG_REPEAT_ENABLE 0x28 +#define REPEAT_ENABLE 0x7 +#define REPEAT_DISABLE 0x0 + +#define REG_CONFIGURATION_2 0x44 #define RELEASE_INT_POS 0 /* device driver data */ @@ -41,19 +53,33 @@ struct kscan_cap1203_data { struct device *dev; kscan_callback_t callback; struct k_work work; // actual processing work - struct k_work_delayable release_work_a; - struct k_work_delayable release_work_b; - struct k_work_delayable release_work_c; /* Interrupt GPIO callback. */ struct gpio_callback int_gpio_cb; #if USE_POLLING /* Timer (polling mode). */ struct k_timer timer; #endif + + uint8_t touch_state; }; +/* Functions and variables for debugging usage */ +#ifdef CONFIG_ZMK_USB_LOGGING +static inline void print_register(const struct i2c_dt_spec *i2c, uint8_t reg, const char* prefix) +{ + uint8_t status; + int err = i2c_reg_read_byte_dt(i2c, reg, &status); + if (err < 0) { + LOG_ERR("Debug can's read register: 0x%x", reg); + return; + } + LOG_INF("%s: register 0x%x = 0x%x", prefix, reg, status); + return; +} +#endif + /* Write a single bit in a register without touching other bits */ -static int kscan_cap1203_bit_write(const struct i2c_dt_spec *i2c, uint8_t reg, \ +static inline int kscan_cap1203_bit_write(const struct i2c_dt_spec *i2c, uint8_t reg, \ uint8_t pos, bool enable) { uint8_t val; @@ -63,16 +89,48 @@ static int kscan_cap1203_bit_write(const struct i2c_dt_spec *i2c, uint8_t reg, \ return err; } - WRITE_BIT(val, pos, enable); + if( enable ) + WRITE_BIT(val, pos, 1); + else + WRITE_BIT(val, pos, 0); return i2c_reg_write_byte_dt(i2c, reg, val); } +/* Check the status of the sensor by comparing the vid, revision and pid with the datasheet */ +static inline int kscan_cap1203_check_firmware(const struct device *dev) +{ + const struct kscan_cap1203_config *config = dev->config; + + uint8_t val; + int err; + + if(err=i2c_reg_read_byte_dt(&config->i2c, REG_PRODUCT_ID, &val)) { + LOG_INF("Can't read register: product id"); + return err; + } + else if( val != PRODUCT_ID ) { + LOG_INF("Unequal product id 0x%x (expected: 0x%x)", val, PRODUCT_ID); + return -EIO; + } + + if(err=i2c_reg_read_byte_dt(&config->i2c, REG_VENDOR_ID, &val)) { + LOG_INF("Can't read register: vendor id"); + return err; + } + else if( val != VENDOR_ID ) { + LOG_INF("Unequal vendor id 0x%x (expected: 0x%x)", val, VENDOR_ID); + return -EIO; + } + + return 0; +} + /* Enable/disable generation of release interrupt */ static int kscan_cap1203_enable_release_interrupt(const struct i2c_dt_spec *i2c,\ bool enable) { - return kscan_cap1203_bit_write(i2c, CONFIGURATION_2, RELEASE_INT_POS, !enable); + return kscan_cap1203_bit_write(i2c, REG_CONFIGURATION_2, RELEASE_INT_POS, !enable); } @@ -100,6 +158,24 @@ static int kscan_cap1203_enable_interrupt(const struct i2c_dt_spec *i2c, bool en return i2c_reg_write_byte_dt(i2c, REG_INTERRUPT_ENABLE, intr); } +/* determine the press status based on latest sensor input */ +// arg1 new_input: the current status input register value +// arg2 ch: the sensor channel to be tested +// arg3 pressed: whether the state change is pressed or released +// return: true if there is state change, otherwise false +inline static bool kscan_cap1203_change_state(uint8_t *old_input, \ + uint8_t *new_input, \ + int ch, \ + bool *pressed) +{ + + if((*old_input & BIT(ch) ) != (*new_input & BIT(ch))) { + *pressed = *new_input & BIT(ch); + return true; + } + return false; +} + /* read the current touch status */ static int kscan_cap1203_read(const struct device *dev) { @@ -109,76 +185,52 @@ static int kscan_cap1203_read(const struct device *dev) uint8_t input; bool pressed; - /* todo: release previus presses if not yet released */ - /* k_work_flush_delayable(&data->release_work_a, NULL); */ - /* k_work_flush_delayable(&data->release_work_b, NULL); */ - /* k_work_flush_delayable(&data->release_work_c, NULL); */ + // read general status +#ifdef CONFIG_ZMK_USB_LOGGING + print_register(&config->i2c, REG_GENERAL_STATUS, "General Status"); + print_register(&config->i2c, REG_CONFIGURATION_2, "CONFIGURATION_2"); +#endif + // read sensor input status r = i2c_reg_read_byte_dt(&config->i2c, REG_INPUT_STATUS, &input); if (r < 0) { return r; } + LOG_INF("Initial input status: 0x%x (old: 0x%x)", input, data->touch_state); + + // check the state change, only pressed event can be discovered at this stage + for( int ch=0; ch < 3; ch++ ) { + if(kscan_cap1203_change_state(&data->touch_state, &input, ch, &pressed)) { + data->callback(dev, 0, ch, pressed); + LOG_INF("Pad %d %s", ch+1, pressed ? "pressed" : "released"); + } + } - // - LOG_INF("event: input: %d", input); - pressed = !!input; // !! can turn an arbitraty integer into 1 or 0 - - if (input & BIT(2)) { - data->callback(dev, 0, 2, pressed); - - LOG_INF("scheduled to release for button c"); - k_work_schedule(&data->release_work_c, K_MSEC(1)); - } - if (input & BIT(1)) { - data->callback(dev, 0, 1, pressed); - - LOG_INF("scheduled to release for button b"); - k_work_schedule(&data->release_work_b, K_MSEC(1)); - } - if (input & BIT(0)) { - data->callback(dev, 0, 0, pressed); - - LOG_INF("scheduled to release for button a"); - k_work_schedule(&data->release_work_a, K_MSEC(1)); + // Clear INT bit to clear SENSOR INPUT STATUS bits and dis-claim interrupt line + r = kscan_cap1203_clear_interrupt(&config->i2c); + if (r < 0) { + return r; } - - /* - * Clear INT bit to clear SENSOR INPUT STATUS bits. - * Note that this is also required in polling mode. - */ - r = kscan_cap1203_clear_interrupt(&config->i2c); + // read sensor input status again, this time to discover release event + r = i2c_reg_read_byte_dt(&config->i2c, REG_INPUT_STATUS, &data->touch_state); if (r < 0) { return r; } + LOG_INF("Input status after clearing: 0x%x (old: 0x%x)", data->touch_state, input); + + // if interrput is triggered by release, there will be state change after claering + for( int ch=0; ch < 3; ch++ ) { + if(kscan_cap1203_change_state(&input, &data->touch_state, ch, &pressed)) { + data->callback(dev, 0, ch, pressed); + LOG_INF("Pad %d %s", ch+1, pressed ? "pressed" : "released"); + } + } + LOG_INF(""); return 0; } -static void kscan_cap1203_release_handler_a(struct k_work *work) -{ - struct kscan_cap1203_data *data = CONTAINER_OF(work, struct kscan_cap1203_data, release_work_a); - - data->callback(data->dev, 0, 0, false); - LOG_INF("rlease button a\n"); -} - -static void kscan_cap1203_release_handler_b(struct k_work *work) -{ - struct kscan_cap1203_data *data = CONTAINER_OF(work, struct kscan_cap1203_data, release_work_b); - - data->callback(data->dev, 0, 1, false); - LOG_INF("rlease button b\n"); -} - -static void kscan_cap1203_release_handler_c(struct k_work *work) -{ - struct kscan_cap1203_data *data = CONTAINER_OF(work, struct kscan_cap1203_data, release_work_c); - - data->callback(data->dev, 0, 2, false); - LOG_INF("rlease button c\n"); -} - /* the actual work to process each alert or polling */ static void kscan_cap1203_work_handler(struct k_work *work) { @@ -287,11 +339,6 @@ static int kscan_cap1203_init(const struct device *dev) k_work_init(&data->work, kscan_cap1203_work_handler); - k_work_init_delayable(&data->release_work_a, kscan_cap1203_release_handler_a); - k_work_init_delayable(&data->release_work_b, kscan_cap1203_release_handler_b); - k_work_init_delayable(&data->release_work_c, kscan_cap1203_release_handler_c); - - // init alert interrupt pin or poll timer if (config->int_gpio.port != NULL) { if (!device_is_ready(config->int_gpio.port)) { @@ -328,13 +375,21 @@ static int kscan_cap1203_init(const struct device *dev) } #endif - // other configuration - r = kscan_cap1203_enable_release_interrupt(&config->i2c, false); + // enable release interrupt + r = kscan_cap1203_enable_release_interrupt(&config->i2c, true); if(r) { LOG_ERR("Could not disable release interrupt"); return r; } + // disable repeat interrupt + r = i2c_reg_write_byte_dt(&config->i2c, REG_REPEAT_ENABLE, REPEAT_DISABLE); + if (r < 0) { + LOG_ERR("Could not disable repeat-rate interrupt"); + return r; + } + + // End of init LOG_INF("Init success"); return 0; diff --git a/app/src/hog.c b/app/src/hog.c index f915d27a91a..4977f2e65a4 100644 --- a/app/src/hog.c +++ b/app/src/hog.c @@ -309,6 +309,8 @@ void send_mouse_report_callback(struct k_work *work) { K_WORK_DEFINE(hog_mouse_work, send_mouse_report_callback); int zmk_hog_send_mouse_report(struct zmk_hid_mouse_report_body *report) { + LOG_DBG("send mouse report using shared queue"); + int err = k_msgq_put(&zmk_hog_mouse_msgq, report, K_NO_WAIT); if (err) { switch (err) { @@ -328,6 +330,8 @@ int zmk_hog_send_mouse_report(struct zmk_hid_mouse_report_body *report) { }; int zmk_hog_send_mouse_report_direct(struct zmk_hid_mouse_report_body *report) { + LOG_DBG("send mouse report directly"); + struct bt_conn *conn = destination_connection(); if (conn == NULL) { return 1; From 31a0a1e460e5f7211ec42f4af35594fed682c654 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Sun, 2 Oct 2022 21:50:25 +0800 Subject: [PATCH 093/157] [cap1203] multi-touch circuitry is working --- app/drivers/kscan/kscan_cap1203.c | 54 ++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/app/drivers/kscan/kscan_cap1203.c b/app/drivers/kscan/kscan_cap1203.c index 28cdf0620f9..0d9b9174a8d 100644 --- a/app/drivers/kscan/kscan_cap1203.c +++ b/app/drivers/kscan/kscan_cap1203.c @@ -40,6 +40,11 @@ LOG_MODULE_REGISTER(kscan_cap1203, CONFIG_ZMK_LOG_LEVEL); #define REPEAT_ENABLE 0x7 #define REPEAT_DISABLE 0x0 +#define REG_MULT_CONFIG 0x2A +#define MULT_BLK_EN_POS 7 +#define B_MULT_T1_POS 3 +#define B_MULT_T0_POS 2 + #define REG_CONFIGURATION_2 0x44 #define RELEASE_INT_POS 0 @@ -126,6 +131,40 @@ static inline int kscan_cap1203_check_firmware(const struct device *dev) return 0; } +/* Config multiple touch circuitry */ +inline static int kscan_cap1203_configure_multiple_touch(const struct i2c_dt_spec *i2c, \ + bool enable, \ + int num) +{ + uint8_t val=0; + + if(enable) { + WRITE_BIT(val, MULT_BLK_EN_POS, 1); + + switch (num) { + case 1: + WRITE_BIT(val, B_MULT_T1_POS, 0); + WRITE_BIT(val, B_MULT_T0_POS, 0); + break; + case 2: + WRITE_BIT(val, B_MULT_T1_POS, 0); + WRITE_BIT(val, B_MULT_T0_POS, 1); + break; + case 3: + WRITE_BIT(val, B_MULT_T1_POS, 1); + WRITE_BIT(val, B_MULT_T0_POS, 0); + break; + default: + LOG_ERR("Number of channels is out of range: max=3"); + return -EINVAL; + } + } + else { + WRITE_BIT(val, MULT_BLK_EN_POS, 0); + } + + return i2c_reg_write_byte_dt(i2c, REG_MULT_CONFIG, val); +} /* Enable/disable generation of release interrupt */ static int kscan_cap1203_enable_release_interrupt(const struct i2c_dt_spec *i2c,\ bool enable) @@ -375,17 +414,24 @@ static int kscan_cap1203_init(const struct device *dev) } #endif - // enable release interrupt + // configure release interrupt r = kscan_cap1203_enable_release_interrupt(&config->i2c, true); if(r) { - LOG_ERR("Could not disable release interrupt"); + LOG_ERR("Could not configure release interrupt"); return r; } - // disable repeat interrupt + // configure repeat interrupt r = i2c_reg_write_byte_dt(&config->i2c, REG_REPEAT_ENABLE, REPEAT_DISABLE); if (r < 0) { - LOG_ERR("Could not disable repeat-rate interrupt"); + LOG_ERR("Could not configure repeat-rate interrupt"); + return r; + } + + // configure multi-touch circuitry + r = kscan_cap1203_configure_multiple_touch(&config->i2c, true, 3); + if (r < 0) { + LOG_ERR("Could not configure multi-touch circuitry"); return r; } From 8c2170f3a7661dbc2fbab642c0c4d9e1b1a356a0 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Mon, 3 Oct 2022 22:21:00 +0800 Subject: [PATCH 094/157] backup --- app/drivers/kscan/Kconfig | 19 ++++++++ app/drivers/kscan/kscan_cap1203.c | 79 ++++++++++++++++++++++++++++++- 2 files changed, 96 insertions(+), 2 deletions(-) diff --git a/app/drivers/kscan/Kconfig b/app/drivers/kscan/Kconfig index 05b1925d906..28201ace616 100644 --- a/app/drivers/kscan/Kconfig +++ b/app/drivers/kscan/Kconfig @@ -54,6 +54,25 @@ config ZMK_KSCAN_CAP1203_PERIOD help Sample period in milliseconds when in polling mode. +choice + prompt "CAP1203 Touch-key Work Mode" + help + Select the work mode of CAP1203 touche sensor. + +config CAP1203_BUTTON_MODE + prompt "Each channel works as a independent button" + bool + +config CAP1203_SLIDER_MODE + prompt "All channels cooperate as a single slider" + bool + +config CAP1203_MIX_MODE + prompt "Combination of buttons and slider in the same mode" + bool + +endchoice + endif # ZMK_KSCAN_CAP1203 if ZMK_KSCAN_GPIO_DRIVER diff --git a/app/drivers/kscan/kscan_cap1203.c b/app/drivers/kscan/kscan_cap1203.c index 0d9b9174a8d..d501e8e46a2 100644 --- a/app/drivers/kscan/kscan_cap1203.c +++ b/app/drivers/kscan/kscan_cap1203.c @@ -66,8 +66,22 @@ struct kscan_cap1203_data { #endif uint8_t touch_state; + +#ifdef CONFIG_CAP1203_SLIDER_MODE || CONFIG_CAP1203_MIX_MODE + uint8_t slider_position; + int16_t delta_position; +#endif }; +#ifdef CONFIG_CAP1203_SLIDER_MODE || CONFIG_CAP1203_MIX_MODE +/* array of the status patterns, the array_index+1 is the position indicator */ +static uint8_t const slider_pattern[5] = {1, 3, 2, 6, 4}; +/* array of slider positions with slider pattern as array index */ +/* The position of unknown slider patterns is defined to be 0 */ +static uint8_t const slider_position[8] = {0, 1, 3, 2, 5, 0, 4, 0}; +static int update_counter; +#endif + /* Functions and variables for debugging usage */ #ifdef CONFIG_ZMK_USB_LOGGING static inline void print_register(const struct i2c_dt_spec *i2c, uint8_t reg, const char* prefix) @@ -222,12 +236,14 @@ static int kscan_cap1203_read(const struct device *dev) struct kscan_cap1203_data *data = dev->data; int r; uint8_t input; + + LOG_INF("Beginning of ISR:"); +#ifdef CONFIG_CAP1203_BUTTON_MODE bool pressed; // read general status #ifdef CONFIG_ZMK_USB_LOGGING print_register(&config->i2c, REG_GENERAL_STATUS, "General Status"); - print_register(&config->i2c, REG_CONFIGURATION_2, "CONFIGURATION_2"); #endif // read sensor input status @@ -265,8 +281,67 @@ static int kscan_cap1203_read(const struct device *dev) LOG_INF("Pad %d %s", ch+1, pressed ? "pressed" : "released"); } } - LOG_INF(""); +#elif defined(CONFIG_CAP1203_SLIDER_MODE) + // read general status +#ifdef CONFIG_ZMK_USB_LOGGING + print_register(&config->i2c, REG_GENERAL_STATUS, "General Status"); +#endif + + // First reading of sensor input status, only pressed info updated + r = i2c_reg_read_byte_dt(&config->i2c, REG_INPUT_STATUS, &input); + if (r < 0) { + return r; + } + + // Clear INT bit to update release info + r = kscan_cap1203_clear_interrupt(&config->i2c); + if (r < 0) { + return r; + } + + // Get the updated status + update_counter++; + if(data->touch_state == 0) { // initial pos + LOG_INF("Update 1 status: 0x%x", input); + } + else { // later state change + // Second reading of sensor input status, which includes release info + r = i2c_reg_read_byte_dt(&config->i2c, REG_INPUT_STATUS, &input); + if (r < 0) { + return r; + } + LOG_INF("Update %d status: 0x%x", update_counter, input); + } + + // Process the slider pattern, unknown patterns are skipped + if(input && data->touch_state != 0) { // the first and last update should not be used + data->delta_position = slider_position[input] - data->slider_position; + if (data->delta_position != 0) { + // todo: callback to process the deltas + } + LOG_INF("Delta: %d", data->delta_position); + } + + // book-keeping stuff + data->touch_state = input; + + if(!input) { // reach the end of slide gesture + data->slider_position = 0; + data->delta_position = 0; + update_counter = 0; + LOG_INF("Last update"); + } + else { // for all other updates before last one + // keep the current status for next update usage + data->slider_position = slider_position[input]; + } + +#elif defined(CONFIG_CAP1203_MIX_MODE) + +#endif + + LOG_INF("End of ISR.\n"); return 0; } From 3885d14827f14fda69312b38e76f989095e3a8a3 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Tue, 4 Oct 2022 09:31:30 +0800 Subject: [PATCH 095/157] [cap1203] working template with functional slider and mix mode NOTE: in mix mode, the button behavior is implemented differently from pure button mode: 1. Taps with duration longer than an pre-defined time is classified as cancelled slide movement. The use case is that users may put their fingers on the bar but then decide not to do the slide, thus it's a cancelling behavior. 2. Press state is sent to kscan listener only at the end of one update cycle. While in pure button mode, the state change is sent immediately when an interrput is received. This is a compromise, since to determine a slider movement, the previous state is needed. 3. Slide movement is determined by the linear relative status pattern change. 4. Multi-touch is supported, but not recommended for reliable usage in daily routine. The timing requirement is so strict and all presses and releases should be performed at the same time to generate a single interrupt. Otherwise, it may be recognized as a slide. 5. The algorithm design is from top to bottom, i.e. only the supported movements are well recognized. If un-regular movements is performed, the output may not be what users expected. For example, when initially tapping the 1st and 3rd pad, and then touch the 2nd pad, there will be no output; on the other hand, if pressing the 1st and 2nd pads first, then 3rd pad, this is recognized as slide with delta 2/-2. However, this pattern is not within the design of algorithm. And is not a useful movement anyway. --- app/drivers/kscan/Kconfig | 12 --- app/drivers/kscan/kscan_cap1203.c | 129 +++++++++++++++++++++--------- 2 files changed, 91 insertions(+), 50 deletions(-) diff --git a/app/drivers/kscan/Kconfig b/app/drivers/kscan/Kconfig index 28201ace616..bb36e0152f5 100644 --- a/app/drivers/kscan/Kconfig +++ b/app/drivers/kscan/Kconfig @@ -42,18 +42,6 @@ config ZMK_KSCAN_CAP1203 if ZMK_KSCAN_CAP1203 -config ZMK_KSCAN_CAP1203_POLL - bool "Polling" - help - Enable polling mode when interrupt GPIO is not specified. - -config ZMK_KSCAN_CAP1203_PERIOD - int "Sample period" - depends on ZMK_KSCAN_CAP1203_POLL - default 10 - help - Sample period in milliseconds when in polling mode. - choice prompt "CAP1203 Touch-key Work Mode" help diff --git a/app/drivers/kscan/kscan_cap1203.c b/app/drivers/kscan/kscan_cap1203.c index d501e8e46a2..1c9baad82af 100644 --- a/app/drivers/kscan/kscan_cap1203.c +++ b/app/drivers/kscan/kscan_cap1203.c @@ -7,7 +7,6 @@ #define DT_DRV_COMPAT zmk_kscan_cap1203 #define USE_POLLING IS_ENABLED(CONFIG_ZMK_KSCAN_CAP1203_POLL) -#define USE_INTERRUPTS (!USE_POLLING) #include #include @@ -56,30 +55,31 @@ struct kscan_cap1203_config { struct kscan_cap1203_data { struct device *dev; - kscan_callback_t callback; + kscan_callback_t callback; // zmk's kscan callback struct k_work work; // actual processing work - /* Interrupt GPIO callback. */ - struct gpio_callback int_gpio_cb; -#if USE_POLLING - /* Timer (polling mode). */ - struct k_timer timer; -#endif + struct gpio_callback int_gpio_cb; // alert gpio pin callback + uint8_t touch_state; // current sensor status - uint8_t touch_state; - -#ifdef CONFIG_CAP1203_SLIDER_MODE || CONFIG_CAP1203_MIX_MODE +#if defined(CONFIG_CAP1203_MIX_MODE) || defined(CONFIG_CAP1203_SLIDER_MODE) uint8_t slider_position; int16_t delta_position; + int16_t acc_delta_position; + int update_counter; +#endif + +#ifdef CONFIG_CAP1203_MIX_MODE + bool is_slide; // slide or button manueover + uint8_t press_state; // accumulated sensor status at end of cycle + int64_t update_duration; #endif }; -#ifdef CONFIG_CAP1203_SLIDER_MODE || CONFIG_CAP1203_MIX_MODE +#if defined(CONFIG_CAP1203_SLIDER_MODE) || defined(CONFIG_CAP1203_MIX_MODE) /* array of the status patterns, the array_index+1 is the position indicator */ static uint8_t const slider_pattern[5] = {1, 3, 2, 6, 4}; /* array of slider positions with slider pattern as array index */ /* The position of unknown slider patterns is defined to be 0 */ static uint8_t const slider_position[8] = {0, 1, 3, 2, 5, 0, 4, 0}; -static int update_counter; #endif /* Functions and variables for debugging usage */ @@ -282,10 +282,20 @@ static int kscan_cap1203_read(const struct device *dev) } } -#elif defined(CONFIG_CAP1203_SLIDER_MODE) +#else // slider or mix mode // read general status #ifdef CONFIG_ZMK_USB_LOGGING + data->update_counter++; print_register(&config->i2c, REG_GENERAL_STATUS, "General Status"); + if(!data->touch_state) + LOG_INF("Start of one cycle"); +#endif + +#ifdef CONFIG_CAP1203_MIX_MODE + // get cycle start time + if(data->touch_state == 0) { + data->update_duration = k_uptime_get(); + } #endif // First reading of sensor input status, only pressed info updated @@ -293,52 +303,95 @@ static int kscan_cap1203_read(const struct device *dev) if (r < 0) { return r; } - // Clear INT bit to update release info r = kscan_cap1203_clear_interrupt(&config->i2c); if (r < 0) { return r; } - - // Get the updated status - update_counter++; - if(data->touch_state == 0) { // initial pos - LOG_INF("Update 1 status: 0x%x", input); - } - else { // later state change - // Second reading of sensor input status, which includes release info - r = i2c_reg_read_byte_dt(&config->i2c, REG_INPUT_STATUS, &input); - if (r < 0) { - return r; - } - LOG_INF("Update %d status: 0x%x", update_counter, input); + // Second reading of sensor input status, which includes release info + r = i2c_reg_read_byte_dt(&config->i2c, REG_INPUT_STATUS, &input); + if (r < 0) { + return r; } + LOG_INF("Update %d status: 0x%x", data->update_counter, input); // Process the slider pattern, unknown patterns are skipped - if(input && data->touch_state != 0) { // the first and last update should not be used - data->delta_position = slider_position[input] - data->slider_position; - if (data->delta_position != 0) { - // todo: callback to process the deltas + if(input) { // skip last update + +#ifdef CONFIG_CAP1203_MIX_MODE + data->press_state |= input; +#endif + + if(data->touch_state != 0) { // also skip the first update for slider + data->delta_position = slider_position[input] - data->slider_position; + if (data->delta_position != 0) { +#ifdef CONFIG_ZMK_USB_LOGGING + data->acc_delta_position += data->delta_position; +#endif + +#ifdef CONFIG_CAP1203_MIX_MODE + if(!data->is_slide) { + data->is_slide = true; + LOG_INF("Discover slide movement!"); + } +#endif + + // todo: callback to process the deltas + } + LOG_INF("Delta: %d", data->delta_position); } - LOG_INF("Delta: %d", data->delta_position); } // book-keeping stuff data->touch_state = input; + data->slider_position = slider_position[input]; if(!input) { // reach the end of slide gesture +#ifdef CONFIG_CAP1203_MIX_MODE + // calculate the cycle duration in ms + data->update_duration = k_uptime_get() - data->update_duration; + + if(data->is_slide) { + LOG_INF("End of one cycle. Slide manuover, total delta: %d", data->acc_delta_position); + data->is_slide = false; + } + else if( data->update_duration <= 200 ) { // send button tap + // todo: callback + LOG_INF("End of one cycle. Tap manuover, pressed buttons: %d", data->press_state); + + // send press state + for(int i=0; i < 3; i++) { + if(BIT(i) & data->press_state) { + data->callback(dev, 0, i, true); + } + } + + k_msleep(5); + + // send release state + for(int i=0; i < 3; i++) { + if(BIT(i) & data->press_state) { + data->callback(dev, 0, i, false); + } + } + + data->press_state = 0; + } + else + LOG_INF("End of one cycle. Cancelled slide manueover"); +#else + LOG_INF("End of one cycle. Total delta: %d", data->acc_delta_position); +#endif + data->slider_position = 0; data->delta_position = 0; - update_counter = 0; - LOG_INF("Last update"); + data->acc_delta_position = 0; + data->update_counter = 0; } else { // for all other updates before last one // keep the current status for next update usage - data->slider_position = slider_position[input]; } -#elif defined(CONFIG_CAP1203_MIX_MODE) - #endif LOG_INF("End of ISR.\n"); From 1345b06902a8565aada97648b7e3000f4bd80f44 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Wed, 5 Oct 2022 20:55:07 +0800 Subject: [PATCH 096/157] [cap1203] very first implementation of sending scroll deltas --- app/drivers/kscan/kscan_cap1203.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/app/drivers/kscan/kscan_cap1203.c b/app/drivers/kscan/kscan_cap1203.c index 1c9baad82af..420a63aec3f 100644 --- a/app/drivers/kscan/kscan_cap1203.c +++ b/app/drivers/kscan/kscan_cap1203.c @@ -15,6 +15,12 @@ #include +#include +#include +#include +#include +#include + LOG_MODULE_REGISTER(kscan_cap1203, CONFIG_ZMK_LOG_LEVEL); /* List of important registers and associated commands */ @@ -229,6 +235,20 @@ inline static bool kscan_cap1203_change_state(uint8_t *old_input, \ return false; } +/* send scroll info */ +inline static void kscan_cap1203_update_scroll(int16_t delta, bool is_hoz) +{ + zmk_hid_mouse_scroll_set(0, 0); + + int8_t scaled_delta = delta * 10; + if( is_hoz ) + zmk_hid_mouse_scroll_update(scaled_delta, 0); + else + zmk_hid_mouse_scroll_update(0, scaled_delta); + + zmk_endpoints_send_mouse_report(); +} + /* read the current touch status */ static int kscan_cap1203_read(const struct device *dev) { @@ -337,6 +357,7 @@ static int kscan_cap1203_read(const struct device *dev) #endif // todo: callback to process the deltas + kscan_cap1203_update_scroll(data->delta_position, false); } LOG_INF("Delta: %d", data->delta_position); } @@ -356,7 +377,6 @@ static int kscan_cap1203_read(const struct device *dev) data->is_slide = false; } else if( data->update_duration <= 200 ) { // send button tap - // todo: callback LOG_INF("End of one cycle. Tap manuover, pressed buttons: %d", data->press_state); // send press state From c13afa7f5fc27b5e8809caf49e40bd96e79377d9 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Sat, 8 Oct 2022 10:06:46 +0800 Subject: [PATCH 097/157] [slider] init work version of slider driver interface --- app/CMakeLists.txt | 2 + app/drivers/kscan/CMakeLists.txt | 1 + app/drivers/kscan/kscan_cap1203.c | 110 ++++++++++++++--------- app/drivers/kscan/kscan_wrapper.c | 16 ++++ app/dts/bindings/zmk,keymap-sliders.yaml | 12 +++ app/dts/bindings/zmk,keymap.yaml | 3 + app/include/drivers/kscan_wrapper.h | 32 +++++++ app/include/drivers/slider.h | 33 +++++++ app/include/zmk/events/slider_event.h | 18 ++++ app/include/zmk/sliders.h | 12 +++ app/src/events/slider_event.c | 10 +++ app/src/sliders.c | 73 +++++++++++++++ docs/docs/features/slider.md | 106 ++++++++++++++++++++++ 13 files changed, 385 insertions(+), 43 deletions(-) create mode 100644 app/drivers/kscan/kscan_wrapper.c create mode 100644 app/dts/bindings/zmk,keymap-sliders.yaml create mode 100644 app/include/drivers/kscan_wrapper.h create mode 100644 app/include/drivers/slider.h create mode 100644 app/include/zmk/events/slider_event.h create mode 100644 app/include/zmk/sliders.h create mode 100644 app/src/events/slider_event.c create mode 100644 app/src/sliders.c create mode 100644 docs/docs/features/slider.md diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 351505ad777..7f617da3e1d 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -28,12 +28,14 @@ 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(app PRIVATE src/sensors.c) +target_sources(app PRIVATE src/sliders.c) target_sources_ifdef(CONFIG_ZMK_WPM app PRIVATE src/wpm.c) target_sources(app PRIVATE src/event_manager.c) target_sources_ifdef(CONFIG_ZMK_EXT_POWER app PRIVATE src/ext_power_generic.c) target_sources(app PRIVATE src/events/activity_state_changed.c) target_sources(app PRIVATE src/events/position_state_changed.c) target_sources(app PRIVATE src/events/sensor_event.c) +target_sources(app PRIVATE src/events/slider_event.c) target_sources(app PRIVATE src/events/mouse_button_state_changed.c) target_sources(app PRIVATE src/events/mouse_move_state_changed.c) target_sources(app PRIVATE src/events/mouse_tick.c) diff --git a/app/drivers/kscan/CMakeLists.txt b/app/drivers/kscan/CMakeLists.txt index 878d48267b4..0454eb544b5 100644 --- a/app/drivers/kscan/CMakeLists.txt +++ b/app/drivers/kscan/CMakeLists.txt @@ -4,6 +4,7 @@ zephyr_library_named(zmk__drivers__kscan) zephyr_library_include_directories(${CMAKE_SOURCE_DIR}/include) +zephyr_library_sources(kscan_wrapper.c) zephyr_library_sources_ifdef(CONFIG_ZMK_KSCAN_GPIO_DRIVER debounce.c) zephyr_library_sources_ifdef(CONFIG_ZMK_KSCAN_GPIO_MATRIX kscan_gpio_matrix.c) zephyr_library_sources_ifdef(CONFIG_ZMK_KSCAN_GPIO_DIRECT kscan_gpio_direct.c) diff --git a/app/drivers/kscan/kscan_cap1203.c b/app/drivers/kscan/kscan_cap1203.c index 420a63aec3f..85c62576331 100644 --- a/app/drivers/kscan/kscan_cap1203.c +++ b/app/drivers/kscan/kscan_cap1203.c @@ -9,7 +9,6 @@ #define USE_POLLING IS_ENABLED(CONFIG_ZMK_KSCAN_CAP1203_POLL) #include -#include #include #include @@ -20,6 +19,11 @@ #include #include #include +#include + +#if defined(CONFIG_CAP1203_MIX_MODE) || defined(CONFIG_CAP1203_SLIDER_MODE) +#include +#endif LOG_MODULE_REGISTER(kscan_cap1203, CONFIG_ZMK_LOG_LEVEL); @@ -60,17 +64,8 @@ struct kscan_cap1203_config { }; struct kscan_cap1203_data { - struct device *dev; - kscan_callback_t callback; // zmk's kscan callback - struct k_work work; // actual processing work - struct gpio_callback int_gpio_cb; // alert gpio pin callback - uint8_t touch_state; // current sensor status - #if defined(CONFIG_CAP1203_MIX_MODE) || defined(CONFIG_CAP1203_SLIDER_MODE) - uint8_t slider_position; - int16_t delta_position; - int16_t acc_delta_position; - int update_counter; + struct slider_data slider_data; #endif #ifdef CONFIG_CAP1203_MIX_MODE @@ -78,6 +73,13 @@ struct kscan_cap1203_data { uint8_t press_state; // accumulated sensor status at end of cycle int64_t update_duration; #endif + + struct device *dev; + kscan_callback_t callback; // zmk's kscan callback + struct k_work work; // actual processing work + struct gpio_callback int_gpio_cb; // alert gpio pin callback + uint8_t touch_state; // current sensor status + int update_counter; }; #if defined(CONFIG_CAP1203_SLIDER_MODE) || defined(CONFIG_CAP1203_MIX_MODE) @@ -235,20 +237,6 @@ inline static bool kscan_cap1203_change_state(uint8_t *old_input, \ return false; } -/* send scroll info */ -inline static void kscan_cap1203_update_scroll(int16_t delta, bool is_hoz) -{ - zmk_hid_mouse_scroll_set(0, 0); - - int8_t scaled_delta = delta * 10; - if( is_hoz ) - zmk_hid_mouse_scroll_update(scaled_delta, 0); - else - zmk_hid_mouse_scroll_update(0, scaled_delta); - - zmk_endpoints_send_mouse_report(); -} - /* read the current touch status */ static int kscan_cap1203_read(const struct device *dev) { @@ -311,6 +299,10 @@ static int kscan_cap1203_read(const struct device *dev) LOG_INF("Start of one cycle"); #endif + struct slider_data *slider_data = dev->data; + // get the curren timestamp + int64_t timestamp = k_uptime_get(); + #ifdef CONFIG_CAP1203_MIX_MODE // get cycle start time if(data->touch_state == 0) { @@ -343,10 +335,12 @@ static int kscan_cap1203_read(const struct device *dev) #endif if(data->touch_state != 0) { // also skip the first update for slider - data->delta_position = slider_position[input] - data->slider_position; - if (data->delta_position != 0) { + slider_data->delta_position = slider_position[input] - slider_data->step; + slider_data->delta_time = timestamp - slider_data->pre_ts; + + if (slider_data->delta_position != 0) { #ifdef CONFIG_ZMK_USB_LOGGING - data->acc_delta_position += data->delta_position; + slider_data->acc_position += slider_data->delta_position; #endif #ifdef CONFIG_CAP1203_MIX_MODE @@ -356,16 +350,20 @@ static int kscan_cap1203_read(const struct device *dev) } #endif - // todo: callback to process the deltas - kscan_cap1203_update_scroll(data->delta_position, false); + // slider callback to process the deltas + if (slider_data->callback) + slider_data->callback(dev, slider_data->delta_position, + slider_data->delta_time); } - LOG_INF("Delta: %d", data->delta_position); + LOG_INF("dPos: %d, dT: %d us", slider_data->delta_position, slider_data->delta_time); } + } // book-keeping stuff data->touch_state = input; - data->slider_position = slider_position[input]; + slider_data->step = slider_position[input]; + slider_data->pre_ts = timestamp; if(!input) { // reach the end of slide gesture #ifdef CONFIG_CAP1203_MIX_MODE @@ -373,7 +371,7 @@ static int kscan_cap1203_read(const struct device *dev) data->update_duration = k_uptime_get() - data->update_duration; if(data->is_slide) { - LOG_INF("End of one cycle. Slide manuover, total delta: %d", data->acc_delta_position); + LOG_INF("End of one cycle. Slide manuover, total delta: %d", slider_data->acc_position); data->is_slide = false; } else if( data->update_duration <= 200 ) { // send button tap @@ -400,18 +398,15 @@ static int kscan_cap1203_read(const struct device *dev) else LOG_INF("End of one cycle. Cancelled slide manueover"); #else - LOG_INF("End of one cycle. Total delta: %d", data->acc_delta_position); + LOG_INF("End of one cycle. Total delta: %d", slider_data->acc_position); #endif - data->slider_position = 0; - data->delta_position = 0; - data->acc_delta_position = 0; + slider_data->step = 0; + slider_data->delta_position = 0; + slider_data->acc_position = 0; + slider_data->delta_time = 0; data->update_counter = 0; } - else { // for all other updates before last one - // keep the current status for next update usage - } - #endif LOG_INF("End of ISR.\n"); @@ -445,7 +440,6 @@ static void kscan_cap1203_timer_handler(struct k_timer *timer) } #endif -/* api callbacks */ static int kscan_cap1203_configure(const struct device *dev, kscan_callback_t callback) { @@ -589,13 +583,42 @@ static int kscan_cap1203_init(const struct device *dev) return 0; } -static const struct kscan_driver_api kscan_cap1203_driver_api = { +#if defined(CONFIG_CAP1203_MIX_MODE) || defined(CONFIG_CAP1203_SLIDER_MODE) +static void kscan_cap1203_slider_configure(const struct device *dev, + kscan_slider_callback_t callback, + int id) +{ + struct slider_data *slider_data = dev->data; + slider_data->callback = callback; + slider_data->id = id; +} + +static struct kscan_slider_api kscan_cap1203_slider_api = { + { .config = kscan_cap1203_configure, .enable_callback = kscan_cap1203_enable_callback, .disable_callback = kscan_cap1203_disable_callback, + }, + .slider_config = kscan_cap1203_slider_configure }; #define KSCAN_CAP1203_INIT(index) \ + static const struct kscan_cap1203_config kscan_cap1203_config_##index = { \ + .i2c = I2C_DT_SPEC_INST_GET(index), \ + .int_gpio = GPIO_DT_SPEC_INST_GET_OR(index, int_gpios, {0}), \ + }; \ + static struct kscan_cap1203_data kscan_cap1203_data_##index; \ + DEVICE_DT_INST_DEFINE(index, kscan_cap1203_init, NULL, \ + &kscan_cap1203_data_##index, &kscan_cap1203_config_##index, \ + APPLICATION, CONFIG_ZMK_KSCAN_INIT_PRIORITY, \ + &(kscan_cap1203_slider_api.kscan_api)); +#else // pure button mode +static const struct kscan_driver_api kscan_cap1203_driver_api = { + .config = kscan_cap1203_configure, + .enable_callback = kscan_cap1203_enable_callback, + .disable_callback = kscan_cap1203_disable_callback, +}; +#define KSCAN_CAP1203_INIT(index) \ static const struct kscan_cap1203_config kscan_cap1203_config_##index = { \ .i2c = I2C_DT_SPEC_INST_GET(index), \ .int_gpio = GPIO_DT_SPEC_INST_GET_OR(index, int_gpios, {0}), \ @@ -605,5 +628,6 @@ static const struct kscan_driver_api kscan_cap1203_driver_api = { &kscan_cap1203_data_##index, &kscan_cap1203_config_##index, \ APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY , \ &kscan_cap1203_driver_api); +#endif DT_INST_FOREACH_STATUS_OKAY(KSCAN_CAP1203_INIT) diff --git a/app/drivers/kscan/kscan_wrapper.c b/app/drivers/kscan/kscan_wrapper.c new file mode 100644 index 00000000000..d15f0c1fc7d --- /dev/null +++ b/app/drivers/kscan/kscan_wrapper.c @@ -0,0 +1,16 @@ + +#include + +void kscan_slider_config(const struct device *dev, + kscan_slider_callback_t callback, + int id) +{ + const struct kscan_driver_api *api = + (struct kscan_driver_api *)dev->api; + + struct kscan_slider_api *slider_api = CONTAINER_OF(api, struct kscan_slider_api, kscan_api); + __ASSERT(slider_api ? true : false, "The kscan_slider_api is not used."); + + slider_api->slider_config(dev, callback, id); + return; +}; diff --git a/app/dts/bindings/zmk,keymap-sliders.yaml b/app/dts/bindings/zmk,keymap-sliders.yaml new file mode 100644 index 00000000000..0cfda72c5fa --- /dev/null +++ b/app/dts/bindings/zmk,keymap-sliders.yaml @@ -0,0 +1,12 @@ +# Copyright (c) 2020 The ZMK Contributors +# SPDX-License-Identifier: MIT + +description: | + Collection of sliders bound in the keymap layers, the layer key is used to switch slider mode. + +compatible: "zmk,keymap-sliders" + +properties: + sliders: + type: phandles + required: true diff --git a/app/dts/bindings/zmk,keymap.yaml b/app/dts/bindings/zmk,keymap.yaml index 56821ded17d..7acb60baa04 100644 --- a/app/dts/bindings/zmk,keymap.yaml +++ b/app/dts/bindings/zmk,keymap.yaml @@ -16,3 +16,6 @@ child-binding: sensor-bindings: type: phandle-array required: false + slider-bindings: + type: phandle-array + required: false diff --git a/app/include/drivers/kscan_wrapper.h b/app/include/drivers/kscan_wrapper.h new file mode 100644 index 00000000000..5d8672ec595 --- /dev/null +++ b/app/include/drivers/kscan_wrapper.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + typedef void (*kscan_slider_callback_t)(const struct device *dev, + int16_t dPos, int dT); + typedef void (*kscan_slider_config_t)(const struct device *dev, + kscan_slider_callback_t callback, + int id); + + struct kscan_slider_api { + struct kscan_driver_api kscan_api; + kscan_slider_config_t slider_config; + }; + + void kscan_slider_config(const struct device *dev, + kscan_slider_callback_t callback, + int id); + +#ifdef __cplusplus +} +#endif diff --git a/app/include/drivers/slider.h b/app/include/drivers/slider.h new file mode 100644 index 00000000000..4d414c6bc7c --- /dev/null +++ b/app/include/drivers/slider.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +/** + * @file slider.h + * + * @brief Common header file for all devices supporting slide manueover + */ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + struct slider_data { + kscan_slider_callback_t callback; // callback to process when slider mode is enabled + uint8_t id; // needed when multiple sliders exist to get the corresponding behavior + uint8_t step; + int16_t delta_position; + int16_t acc_position; + int64_t pre_ts; + int delta_time; + }; + +#ifdef __cplusplus +} + +#endif diff --git a/app/include/zmk/events/slider_event.h b/app/include/zmk/events/slider_event.h new file mode 100644 index 00000000000..63e54504137 --- /dev/null +++ b/app/include/zmk/events/slider_event.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include + +struct zmk_slider_event { + uint8_t slider_id; + int16_t delta_position; + int delta_time; // in us + int64_t update_time; +}; + +ZMK_EVENT_DECLARE(zmk_slider_event); diff --git a/app/include/zmk/sliders.h b/app/include/zmk/sliders.h new file mode 100644 index 00000000000..dd9fddc4ee7 --- /dev/null +++ b/app/include/zmk/sliders.h @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#define ZMK_KEYMAP_SLIDERS_NODE DT_INST(0, zmk_keymap_sliders) +#define ZMK_KEYMAP_HAS_SLIDERS DT_NODE_HAS_STATUS(ZMK_KEYMAP_SLIDERS_NODE, okay) +#define ZMK_KEYMAP_SLIDERS_LEN DT_PROP_LEN(ZMK_KEYMAP_SLIDERS_NODE, sliders) +#define ZMK_KEYMAP_SLIDERS_BY_IDX(idx) DT_PHANDLE_BY_IDX(ZMK_KEYMAP_SLIDERS_NODE, sliders, idx) diff --git a/app/src/events/slider_event.c b/app/src/events/slider_event.c new file mode 100644 index 00000000000..0835fd103cc --- /dev/null +++ b/app/src/events/slider_event.c @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include + +ZMK_EVENT_IMPL(zmk_slider_event); diff --git a/app/src/sliders.c b/app/src/sliders.c new file mode 100644 index 00000000000..2edb31670e0 --- /dev/null +++ b/app/src/sliders.c @@ -0,0 +1,73 @@ +/* Description: +* 1. setup sliders callback +* 2. The callback will set up the correct information and raise a slider event +* 3. The processing of slider event is dispatched to listening processors, notably keymap +*/ + +#include +#include + +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#include +#include +#include +#include + +#include +#include + +#if ZMK_KEYMAP_HAS_SLIDERS + +/* the slider callback */ +void zmk_slider_callback(const struct device *dev, int16_t dPos, int dT) +{ + /* + * Test + */ + zmk_hid_mouse_scroll_set(0, 0); + + int8_t scaled_delta = dPos * 10; + zmk_hid_mouse_scroll_update(0, scaled_delta); + + zmk_endpoints_send_mouse_report(); + /* + * + */ + + struct slider_data *slider_data = dev->data; + + LOG_INF("Event from slider_%d: dPos: %d, dT: %d ms", slider_data->id+1, dPos, dT); + ZMK_EVENT_RAISE(new_zmk_slider_event((struct zmk_slider_event){ + .slider_id = slider_data->id, .delta_position = dPos, + .delta_time = dT, .update_time = k_uptime_get()})); + return; +} + +/* config the sliders: setup callback and assign the slider id */ +static void zmk_sliders_init_item(const char *node, uint8_t abs_i) { + LOG_DBG("Init %s with slider_id %d", node, abs_i); + + const struct device *dev = device_get_binding(node); + if (!dev) { + LOG_WRN("Failed to find device for %s", node); + return; + } + + kscan_slider_config(dev, zmk_slider_callback, abs_i); +} + +#define SLIDER_INIT(idx, _i) \ + COND_CODE_1(DT_NODE_HAS_STATUS(ZMK_KEYMAP_SLIDERS_BY_IDX(idx), okay), \ + (zmk_sliders_init_item(DT_LABEL(ZMK_KEYMAP_SLIDERS_BY_IDX(idx)), idx);), (;)) + +static int zmk_sliders_init(const struct device *_arg) { + UTIL_LISTIFY(ZMK_KEYMAP_SLIDERS_LEN, SLIDER_INIT, 0) + return 0; +} + +SYS_INIT(zmk_sliders_init, APPLICATION, 91); +#endif + diff --git a/docs/docs/features/slider.md b/docs/docs/features/slider.md new file mode 100644 index 00000000000..ad3e76fa99a --- /dev/null +++ b/docs/docs/features/slider.md @@ -0,0 +1,106 @@ +--- +title: Slider +sidebar_label: Slider +--- + +All devices which can generate 1-d continuous incremental position changes in one manueover +are classified as a slider. +Examples are capacitive touchbar, potentialmeter slider etc.. +Rotary encoder is not considered as slider, since the manueover generates an on/off state change +by nature. + +The implementation of the slider feature uses a capacitive slider as the reference design. +The slider is composed of multiple discrete capacitive sensors in a row. +These sensors can be used as buttons seperately or as a single slider cooperatively. +The implementation support three modes: +- button : only button usage is activated +- slider : only slider usage is activeated +- mix : both button and slider usages are activated + + +The reference hardware is based on [CAP1203](https://www.microchip.com/en-us/product/CAP1203), which supports up to three input sensors. +The driver of CAP1203 is basically a kscan device, with support of slide moanuover. +The device node is a I2C slave defined as: +``` + &i2c0 { + compatible = "nordic,nrf-twi"; + sda-pin = ; + scl-pin = ; + status = "okay"; + + kscan1: kscan1@28 { + compatible = "zmk,kscan-cap1203"; + status = "okay"; + reg = <0x28>; + int-gpios = <&gpio1 11 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + }; + }; +``` + +The different working mode is selected using Kconfig options: +- CONFIG_CAP1203_BUTTON_MODE +- CONFIG_CAP1203_SLIDER_MODE +- CONFIG_CAP1203_MIX_MODE + +## Button Mode +The driver node works as a normal kscan driver like others. +The keypress related behaviors are all supported naturally. +It can be set as the main chosen node by itself: +``` +chosen { + zmk,kscan = &kscan1; + zmk,matrix_transform = &default_transform; +}; +``` + +Or more likely, it can be a component of a composite kscan node: +``` +``` +To enable the driver code, the following configuration should also be added in config file: +``` +chosen { + zmk,kscan = &kscan; + zmk,matrix_transform = &default_transform; +}; + +kscan: kscan { + compatible = "zmk,kscan-composite"; + label = "KSCAN"; + rows = <2>; + columns = <7>; + + normal_keys { + kscan = <&kscan0>; + }; + + touch_keys: touch_keys { + kscan = <&kscan1>; + row-offset = <1>; + }; + }; + +kscan0: kscan0 { + compatible = "zmk,kscan-gpio-direct"; + label = "KEYBOARD-MATRIX"; + input-gpios + = <&gpio0 6 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&gpio0 8 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&gpio0 24 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&gpio0 9 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&gpio0 10 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&gpio1 4 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&gpio1 6 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + ; + }; +``` + +## Slider Mode + +## Mix Mode + + +## Programming Interface +A dedicated data structure `slider_data` needs to be inserted at the beginning of the +device driver's data structure. +A kscan wrapper of the kscan api is used to hold a `kscan_slider_configure` function. +`kscan_slider_configure` is used to setup the slider callback and the slider id in the system. From 572f796198b22e76f8a76587bad86f3a17b5f878 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Sat, 8 Oct 2022 22:48:30 +0800 Subject: [PATCH 098/157] backup --- ...zmk,behavior-point-device-incremental.yaml | 49 ++++++++ app/include/drivers/behavior.h | 32 ++++++ .../zmk/events/pd_position_state_changed.h | 16 +++ .../zmk/events/pd_scroll_state_changed.h | 16 +++ .../behavior_point_device_incremental.c | 107 ++++++++++++++++++ app/src/events/pd_position_state_changed.c | 10 ++ app/src/events/pd_scroll_state_changed.c | 10 ++ app/src/mouse/pd_listener.c | 70 ++++++++++++ app/src/sliders.c | 13 --- 9 files changed, 310 insertions(+), 13 deletions(-) create mode 100644 app/dts/bindings/behaviors/zmk,behavior-point-device-incremental.yaml create mode 100644 app/include/zmk/events/pd_position_state_changed.h create mode 100644 app/include/zmk/events/pd_scroll_state_changed.h create mode 100644 app/src/behaviors/behavior_point_device_incremental.c create mode 100644 app/src/events/pd_position_state_changed.c create mode 100644 app/src/events/pd_scroll_state_changed.c create mode 100644 app/src/mouse/pd_listener.c diff --git a/app/dts/bindings/behaviors/zmk,behavior-point-device-incremental.yaml b/app/dts/bindings/behaviors/zmk,behavior-point-device-incremental.yaml new file mode 100644 index 00000000000..5e59319283b --- /dev/null +++ b/app/dts/bindings/behaviors/zmk,behavior-point-device-incremental.yaml @@ -0,0 +1,49 @@ +# Copyright (c) 2020 The ZMK Contributors +# SPDX-License-Identifier: MIT + +description: Scroll behavior of point devices + +compatible: "zmk,behavior-point-device-scroll" + +properties: + label: + type: string + required: true + "#slider-binding-cells": + type: int + required: true + const: 0 + "#pt-binding-cells": + type: int + required: true + const: 0 + mode: + type: string + required: false + default: "move-mode" + enum: + - "move-mode" + - "scroll-mode" + flavor: + type: string + required: false + default: "default" + enum: + - "default" + - "swap" + - "x-only" + - "y-only" + scale_mode: + type: string + required: false + default: "multiplier" + enum: + - "multiplier" + - "dividor" + scale_factor: + type: int + required: false + default: 10 + smoothing: + type: boolean + description: Add interleaved delta and inertial delta diff --git a/app/include/drivers/behavior.h b/app/include/drivers/behavior.h index 5c78ac21ea3..1438a391141 100644 --- a/app/include/drivers/behavior.h +++ b/app/include/drivers/behavior.h @@ -28,6 +28,8 @@ typedef int (*behavior_keymap_binding_callback_t)(struct zmk_behavior_binding *b typedef int (*behavior_sensor_keymap_binding_callback_t)(struct zmk_behavior_binding *binding, const struct sensor_value value, int64_t timestamp); +typedef int (*behavior_pd_keymap_binding_callback_t)(struct zmk_behavior_binding *binding, + int16_t dx, int16_t dy, int dt); enum behavior_locality { BEHAVIOR_LOCALITY_CENTRAL, @@ -41,6 +43,7 @@ __subsystem struct behavior_driver_api { behavior_keymap_binding_callback_t binding_pressed; behavior_keymap_binding_callback_t binding_released; behavior_sensor_keymap_binding_callback_t sensor_binding_triggered; + behavior_pd_keymap_binding_callback_t pd_binding_triggered; }; /** * @endcond @@ -180,6 +183,35 @@ static inline int z_impl_behavior_sensor_keymap_binding_triggered( return api->sensor_binding_triggered(binding, value, timestamp); } +/** + * @brief Handle the a point device keymap binding being triggered + * @param binding Pointer to the data structure for the behavior binding. + * @param sensor Pointer to the sensor device structure for the sensor driver instance. + * @param param1 User parameter specified at time of behavior binding. + * @param param2 User parameter specified at time of behavior binding. + * + * @retval 0 If successful. + * @retval Negative errno code if failure. + */ +__syscall int behavior_pd_keymap_binding_triggered(struct zmk_behavior_binding *binding, + int16_t dx, int16_t dy, int dt); + +static inline int z_impl_behavior_pd_keymap_binding_triggered(struct zmk_behavior_binding *binding, + int16_t dx, int16_t dy, int dt) { + const struct device *dev = device_get_binding(binding->behavior_dev); + + if (dev == NULL) { + return -EINVAL; + } + + const struct behavior_driver_api *api = (const struct behavior_driver_api *)dev->api; + + if (api->pd_binding_triggered == NULL) { + return -ENOTSUP; + } + + return api->pd_binding_triggered(binding, dx, dy, dt); +} /** * @} */ diff --git a/app/include/zmk/events/pd_position_state_changed.h b/app/include/zmk/events/pd_position_state_changed.h new file mode 100644 index 00000000000..ed47dfa5ef5 --- /dev/null +++ b/app/include/zmk/events/pd_position_state_changed.h @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include + +struct zmk_pd_position_state_changed { + int16_t x; + int16_t y; +}; + +ZMK_EVENT_DECLARE(zmk_pd_position_state_changed); diff --git a/app/include/zmk/events/pd_scroll_state_changed.h b/app/include/zmk/events/pd_scroll_state_changed.h new file mode 100644 index 00000000000..4ede6e0698b --- /dev/null +++ b/app/include/zmk/events/pd_scroll_state_changed.h @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include + +struct zmk_pd_scroll_state_changed { + int16_t x; + int16_t y; +}; + +ZMK_EVENT_DECLARE(zmk_pd_scroll_state_changed); diff --git a/app/src/behaviors/behavior_point_device_incremental.c b/app/src/behaviors/behavior_point_device_incremental.c new file mode 100644 index 00000000000..1b8ddea4321 --- /dev/null +++ b/app/src/behaviors/behavior_point_device_incremental.c @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#define DT_DRV_COMPAT zmk_behavior_point_device_incremental + +#include +#include +#include + +#include +#include +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) + +enum pd_incr_mode { + MOVE_MODE, + SCROLL_MODE +}; + +enum pd_incr_flavor { DEFAULT, SWAP, X_ONLY, Y_ONLY }; + +enum pd_scale_mode { MULTIPLIER, DIVIDOR }; + +struct behavior_point_device_incremental_config { + enum pd_incr_mode mode; + enum pd_incr_flavor flavor; + enum pd_scale_mode scale_mode; + int scale_factor; + bool smoothing; // todo +}; + +static int behavior_point_device_incremental_init(const struct device *dev) { return 0; }; + +static int on_pd_binding_triggered(struct zmk_behavior_binding *binding, + int16_t dx, int16_t dy, int dt) +{ + const struct device *dev = device_get_binding(binding->behavior_dev); + const struct behavior_point_device_incremental_config *cfg = dev->config; + + // set the real dx, dy + int16_t x, y; + switch (cfg->flavor) { + case SWAP: + x = dy; + y = dx; + break; + case X_ONLY: + x = dx; + y = 0; + break; + case Y_ONLY: + x = 0; + y = dy; + break; + default: + x = dx; + y = dy; + } + + // + switch (cfg->scale_mode) { + case MULTIPLIER: + x = x * cfg->scale_factor; + y = y * cfg->scale_factor; + break; + case DIVIDOR: + x = x / cfg->scale_factor; + y = y / cfg->scale_factor; + break; + } + + // choose the report type + switch (cfg->mode) { + case MOVE_MODE: + return ZMK_EVENT_RAISE(new_zmk_pd_position_state_changed( + (struct zmk_pd_position_state_changed){.x=x, .y=y})); + case SCROLL_MODE: + return ZMK_EVENT_RAISE(new_zmk_pd_scroll_state_changed( + (struct zmk_pd_scroll_state_changed){.x=x, .y=y})); + } +} + +static const struct behavior_driver_api behavior_point_device_incremental_driver_api = { + .pd_binding_triggered = on_pd_binding_triggered +}; + +#define KP_INST(n) \ + static struct behavior_point_device_incremental_config behavior_pd_incr_config_##n = { \ + .mode = DT_ENUM_IDX(DT_DRV_INST(n), mode), \ + .flavor = DT_ENUM_IDX(DT_DRV_INST(n), flavor), \ + .scale_mode = DT_ENUM_IDX(DT_DRV_INST(n), scale_mode), \ + .scale_factor = DT_INST_PROP(n, scale_factor), \ + .smoothing = DT_INST_PROP(n, smoothing), \ + }; \ + DEVICE_DT_INST_DEFINE(n, behavior_point_device_incremental_init, NULL, NULL, &behavior_pd_incr_config_##n, \ + APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \ + &behavior_point_device_incremental_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(KP_INST) + +#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */ diff --git a/app/src/events/pd_position_state_changed.c b/app/src/events/pd_position_state_changed.c new file mode 100644 index 00000000000..71e323ddb83 --- /dev/null +++ b/app/src/events/pd_position_state_changed.c @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include + +ZMK_EVENT_IMPL(zmk_pd_position_state_changed); diff --git a/app/src/events/pd_scroll_state_changed.c b/app/src/events/pd_scroll_state_changed.c new file mode 100644 index 00000000000..a2b6b077d68 --- /dev/null +++ b/app/src/events/pd_scroll_state_changed.c @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include + +ZMK_EVENT_IMPL(zmk_pd_scroll_state_changed); diff --git a/app/src/mouse/pd_listener.c b/app/src/mouse/pd_listener.c new file mode 100644 index 00000000000..8fdcafb61a7 --- /dev/null +++ b/app/src/mouse/pd_listener.c @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2021 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +// Listener for position and scroll change + +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#include +#include +#include +#include +#include +#include + +struct zmk_pd_msg { + int16_t x; + int16_t y; + bool scroll; +}; + +K_MSGQ_DEFINE(zmk_pd_msgq, sizeof(struct zmk_pd_msg), CONFIG_ZMK_KSCAN_EVENT_QUEUE_SIZE, 4); + +static void pd_process_msgq(struct k_work *work) { + struct zmk_pd_msg msg; + + while (k_msgq_get(&zmk_pd_msgq, &msg, K_NO_WAIT) == 0) { + if (msg.scroll) { + LOG_INF("Send pd scroll data (%d, %d)", msg.x, msg.y); + zmk_hid_mouse_scroll_set(0, 0); + zmk_hid_mouse_scroll_update(msg.x, msg.y); + } + else { + LOG_INF("Send pd position data (%d, %d)", msg.x, msg.y); + zmk_hid_mouse_movement_set(0, 0); + zmk_hid_mouse_movement_update(CLAMP(msg.x, INT8_MIN, INT8_MAX), CLAMP(msg.y, INT8_MIN, INT8_MAX)); + } + + zmk_endpoints_send_mouse_report(); + } +} + +K_WORK_DEFINE(msg_processor, pd_process_msgq); + +int pd_listener(const zmk_event_t *eh) { + const struct zmk_pd_position_state_changed *mv_ev = as_zmk_pd_position_state_changed(eh); + if (mv_ev) { + struct zmk_pd_msg msg = {.x = eh.x, .y = eh.y, .scroll = false}; + k_msgq_put(&zmk_pd_msgq, &msg, K_NO_WAIT); + k_work_submit_to_queue(zmk_mouse_work_q(), &msg_processor); + return 0; + } + + const struct zmk_pd_scroll_state_changed *sc_ev = as_zmk_pd_scroll_state_changed(eh); + if (sc_ev) { + struct zmk_pd_msg msg = {.x = eh.x, .y = eh.y, .scroll = true}; + k_msgq_put(&zmk_pd_msgq, &msg, K_NO_WAIT); + k_work_submit_to_queue(zmk_mouse_work_q(), &msg_processor); + return 0; + } + return 0; +} + +ZMK_LISTENER(pd_listener, pd_listener); +ZMK_SUBSCRIPTION(pd_listener, zmk_pd_position_state_changed); +ZMK_SUBSCRIPTION(pd_listener, zmk_pd_scroll_state_changed); diff --git a/app/src/sliders.c b/app/src/sliders.c index 2edb31670e0..049d27177a0 100644 --- a/app/src/sliders.c +++ b/app/src/sliders.c @@ -24,19 +24,6 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); /* the slider callback */ void zmk_slider_callback(const struct device *dev, int16_t dPos, int dT) { - /* - * Test - */ - zmk_hid_mouse_scroll_set(0, 0); - - int8_t scaled_delta = dPos * 10; - zmk_hid_mouse_scroll_update(0, scaled_delta); - - zmk_endpoints_send_mouse_report(); - /* - * - */ - struct slider_data *slider_data = dev->data; LOG_INF("Event from slider_%d: dPos: %d, dT: %d ms", slider_data->id+1, dPos, dT); From 798d7dbe0f3673319dd6f527ed3e022d47fabb7e Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Sun, 9 Oct 2022 07:53:34 +0800 Subject: [PATCH 099/157] [point-device] init working version of pd event through zmk evnt process chain --- app/CMakeLists.txt | 4 + app/dts/behaviors.dtsi | 1 + app/dts/behaviors/slider.dtsi | 34 +++++++++ ...zmk,behavior-point-device-incremental.yaml | 6 +- app/include/zmk/events/slider_event.h | 2 +- .../behavior_point_device_incremental.c | 4 +- app/src/keymap.c | 76 ++++++++++++++++++- app/src/mouse/pd_listener.c | 14 ++-- 8 files changed, 127 insertions(+), 14 deletions(-) create mode 100644 app/dts/behaviors/slider.dtsi diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 7f617da3e1d..3d3e831cf20 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -65,6 +65,7 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL) target_sources(app PRIVATE src/behaviors/behavior_mouse_key_press.c) target_sources(app PRIVATE src/behaviors/behavior_mouse_move.c) target_sources(app PRIVATE src/behaviors/behavior_mouse_scroll.c) + target_sources(app PRIVATE src/behaviors/behavior_point_device_incremental.c) target_sources(app PRIVATE src/combo.c) target_sources(app PRIVATE src/behavior_queue.c) target_sources(app PRIVATE src/conditional_layer.c) @@ -75,6 +76,9 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL) target_sources(app PRIVATE src/events/layer_state_changed.c) target_sources(app PRIVATE src/events/modifiers_state_changed.c) target_sources(app PRIVATE src/events/keycode_state_changed.c) + target_sources(app PRIVATE src/events/pd_scroll_state_changed.c) + target_sources(app PRIVATE src/events/pd_position_state_changed.c) + target_sources(app PRIVATE src/mouse/pd_listener.c) if (CONFIG_ZMK_BLE) target_sources(app PRIVATE src/events/ble_active_profile_changed.c) diff --git a/app/dts/behaviors.dtsi b/app/dts/behaviors.dtsi index 77eccf912fd..da7cd54636a 100644 --- a/app/dts/behaviors.dtsi +++ b/app/dts/behaviors.dtsi @@ -22,3 +22,4 @@ #include #include #include +#include diff --git a/app/dts/behaviors/slider.dtsi b/app/dts/behaviors/slider.dtsi new file mode 100644 index 00000000000..c5378b9e5f9 --- /dev/null +++ b/app/dts/behaviors/slider.dtsi @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include + +/ { + behaviors { + /omit-if-no-ref/ slh: behavior_slider_horizontal { + compatible = "zmk,behavior-point-device-incremental"; + label = "SLIDER_HORIZONTAL"; + #slider-binding-cells = <0>; + mode = "scroll-mode"; + flavor = "x-only"; + scale_mode = "multiplier"; + scale_factor = <10>; + }; + }; + + behaviors { + /omit-if-no-ref/ slv: behavior_slider_vertical { + compatible = "zmk,behavior-point-device-incremental"; + label = "SLIDER_VERTICAL"; + #slider-binding-cells = <0>; + mode = "scroll-mode"; + flavor = "y-only"; + scale_mode = "multiplier"; + scale_factor = <10>; + }; + }; +}; + diff --git a/app/dts/bindings/behaviors/zmk,behavior-point-device-incremental.yaml b/app/dts/bindings/behaviors/zmk,behavior-point-device-incremental.yaml index 5e59319283b..c9af2b01b34 100644 --- a/app/dts/bindings/behaviors/zmk,behavior-point-device-incremental.yaml +++ b/app/dts/bindings/behaviors/zmk,behavior-point-device-incremental.yaml @@ -3,7 +3,7 @@ description: Scroll behavior of point devices -compatible: "zmk,behavior-point-device-scroll" +compatible: "zmk,behavior-point-device-incremental" properties: label: @@ -11,11 +11,11 @@ properties: required: true "#slider-binding-cells": type: int - required: true + required: false const: 0 "#pt-binding-cells": type: int - required: true + required: false const: 0 mode: type: string diff --git a/app/include/zmk/events/slider_event.h b/app/include/zmk/events/slider_event.h index 63e54504137..d8ea49d00ec 100644 --- a/app/include/zmk/events/slider_event.h +++ b/app/include/zmk/events/slider_event.h @@ -11,7 +11,7 @@ struct zmk_slider_event { uint8_t slider_id; int16_t delta_position; - int delta_time; // in us + int delta_time; // in ms int64_t update_time; }; diff --git a/app/src/behaviors/behavior_point_device_incremental.c b/app/src/behaviors/behavior_point_device_incremental.c index 1b8ddea4321..5244473b80e 100644 --- a/app/src/behaviors/behavior_point_device_incremental.c +++ b/app/src/behaviors/behavior_point_device_incremental.c @@ -11,8 +11,8 @@ #include #include -#include -#include +#include +#include LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); diff --git a/app/src/keymap.c b/app/src/keymap.c index 4f396cf1ba4..c254868e481 100644 --- a/app/src/keymap.c +++ b/app/src/keymap.c @@ -11,6 +11,7 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #include #include +#include #include #include #include @@ -24,6 +25,7 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #include #include #include +#include static zmk_keymap_layers_state_t _zmk_keymap_layer_state = 0; static uint8_t _zmk_keymap_layer_default = 0; @@ -54,9 +56,25 @@ static uint8_t _zmk_keymap_layer_default = 0; DT_NODE_HAS_PROP(node, sensor_bindings), \ ({UTIL_LISTIFY(DT_PROP_LEN(node, sensor_bindings), _TRANSFORM_SENSOR_ENTRY, node)}), \ ({})), - #endif /* ZMK_KEYMAP_HAS_SENSORS */ +#if ZMK_KEYMAP_HAS_SLIDERS +#define _TRANSFORM_SLIDER_ENTRY(idx, layer) \ + { \ + .behavior_dev = DT_LABEL(DT_PHANDLE_BY_IDX(layer, slider_bindings, idx)), \ + .param1 = COND_CODE_0(DT_PHA_HAS_CELL_AT_IDX(layer, slider_bindings, idx, param1), (0), \ + (DT_PHA_BY_IDX(layer, slider_bindings, idx, param1))), \ + .param2 = COND_CODE_0(DT_PHA_HAS_CELL_AT_IDX(layer, slider_bindings, idx, param2), (0), \ + (DT_PHA_BY_IDX(layer, slider_bindings, idx, param2))), \ + }, + +#define SLIDER_LAYER(node) \ + COND_CODE_1( \ + DT_NODE_HAS_PROP(node, slider_bindings), \ + ({UTIL_LISTIFY(DT_PROP_LEN(node, slider_bindings), _TRANSFORM_SLIDER_ENTRY, node)}), \ + ({})), +#endif /* ZMK_KEYMAP_HAS_SLIDERS */ + #define LAYER_LABEL(node) COND_CODE_0(DT_NODE_HAS_PROP(node, label), (NULL), (DT_LABEL(node))), // State @@ -80,6 +98,14 @@ static struct zmk_behavior_binding zmk_sensor_keymap[ZMK_KEYMAP_LAYERS_LEN] #endif /* ZMK_KEYMAP_HAS_SENSORS */ +#if ZMK_KEYMAP_HAS_SLIDERS + +static struct zmk_behavior_binding zmk_slider_keymap[ZMK_KEYMAP_LAYERS_LEN] + [ZMK_KEYMAP_SLIDERS_LEN] = { + DT_INST_FOREACH_CHILD(0, SLIDER_LAYER)}; + +#endif /* ZMK_KEYMAP_HAS_SLIDERS */ + static inline int set_layer_state(uint8_t layer, bool state, bool flag_event) { if (layer >= ZMK_KEYMAP_LAYERS_LEN) { return -EINVAL; @@ -292,6 +318,43 @@ int zmk_keymap_sensor_triggered(uint8_t sensor_number, const struct sensor_value #endif /* ZMK_KEYMAP_HAS_SENSORS */ +#if ZMK_KEYMAP_HAS_SLIDERS +int zmk_keymap_slider_triggered(uint8_t id, int16_t dPos, int dT) +{ + for (int layer = ZMK_KEYMAP_LAYERS_LEN - 1; layer >= _zmk_keymap_layer_default; layer--) { + if (zmk_keymap_layer_active(layer) && zmk_slider_keymap[layer] != NULL) { + struct zmk_behavior_binding *binding = &zmk_slider_keymap[layer][id]; + const struct device *behavior; + int ret; + + LOG_INF("layer: %d slider_id: %d, binding name: %s", layer, id, + log_strdup(binding->behavior_dev)); + + behavior = device_get_binding(binding->behavior_dev); + + if (!behavior) { + LOG_DBG("No behavior assigned to %d on layer %d", id, layer); + continue; + } + + ret = behavior_pd_keymap_binding_triggered(binding, dPos, dPos, dT); + + if (ret > 0) { + LOG_DBG("behavior processing to continue to next layer"); + continue; + } else if (ret < 0) { + LOG_DBG("Behavior returned error: %d", ret); + return ret; + } else { + return ret; + } + } + } + + return -ENOTSUP; +} +#endif /* ZMK_KEYMAP_HAS_SLIDERS */ + int keymap_listener(const zmk_event_t *eh) { const struct zmk_position_state_changed *pos_ev; if ((pos_ev = as_zmk_position_state_changed(eh)) != NULL) { @@ -307,6 +370,13 @@ int keymap_listener(const zmk_event_t *eh) { } #endif /* ZMK_KEYMAP_HAS_SENSORS */ +#if ZMK_KEYMAP_HAS_SLIDERS + const struct zmk_slider_event *slider_ev; + if ((slider_ev = as_zmk_slider_event(eh)) != NULL) { + return zmk_keymap_slider_triggered(slider_ev->slider_id, slider_ev->delta_position, + slider_ev->delta_time); + } +#endif /* ZMK_KEYMAP_HAS_SLIDERS */ return -ENOTSUP; } @@ -316,3 +386,7 @@ ZMK_SUBSCRIPTION(keymap, zmk_position_state_changed); #if ZMK_KEYMAP_HAS_SENSORS ZMK_SUBSCRIPTION(keymap, zmk_sensor_event); #endif /* ZMK_KEYMAP_HAS_SENSORS */ + +#if ZMK_KEYMAP_HAS_SLIDERS +ZMK_SUBSCRIPTION(keymap, zmk_slider_event); +#endif diff --git a/app/src/mouse/pd_listener.c b/app/src/mouse/pd_listener.c index 8fdcafb61a7..2126f5923df 100644 --- a/app/src/mouse/pd_listener.c +++ b/app/src/mouse/pd_listener.c @@ -11,8 +11,8 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #include -#include -#include +#include +#include #include #include #include @@ -44,22 +44,22 @@ static void pd_process_msgq(struct k_work *work) { } } -K_WORK_DEFINE(msg_processor, pd_process_msgq); +K_WORK_DEFINE(pd_msg_processor, pd_process_msgq); int pd_listener(const zmk_event_t *eh) { const struct zmk_pd_position_state_changed *mv_ev = as_zmk_pd_position_state_changed(eh); if (mv_ev) { - struct zmk_pd_msg msg = {.x = eh.x, .y = eh.y, .scroll = false}; + struct zmk_pd_msg msg = {.x = mv_ev->x, .y = mv_ev->y, .scroll = false}; k_msgq_put(&zmk_pd_msgq, &msg, K_NO_WAIT); - k_work_submit_to_queue(zmk_mouse_work_q(), &msg_processor); + k_work_submit_to_queue(zmk_mouse_work_q(), &pd_msg_processor); return 0; } const struct zmk_pd_scroll_state_changed *sc_ev = as_zmk_pd_scroll_state_changed(eh); if (sc_ev) { - struct zmk_pd_msg msg = {.x = eh.x, .y = eh.y, .scroll = true}; + struct zmk_pd_msg msg = {.x = sc_ev->x, .y = sc_ev->y, .scroll = true}; k_msgq_put(&zmk_pd_msgq, &msg, K_NO_WAIT); - k_work_submit_to_queue(zmk_mouse_work_q(), &msg_processor); + k_work_submit_to_queue(zmk_mouse_work_q(), &pd_msg_processor); return 0; } return 0; From 4a65e0963d4de576cb2c488fa47aeab874e3c265 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Sun, 9 Oct 2022 18:40:11 +0800 Subject: [PATCH 100/157] add dedicated work queue to process point device events --- app/CMakeLists.txt | 6 +- app/dts/bindings/zmk,keymap.yaml | 6 + app/include/zmk/events/pd_raw_event.h | 21 ++ app/include/zmk/events/slider_event.h | 18 -- app/include/zmk/point_device.h | 10 + app/src/endpoints.c | 2 +- .../events/{slider_event.c => pd_raw_event.c} | 4 +- app/src/keymap.c | 189 +++++++++++++++--- app/src/main.c | 8 + app/src/point_device/Kconfig | 54 +++++ app/src/point_device/main.c | 34 ++++ app/src/{mouse => point_device}/pd_listener.c | 12 +- app/src/point_device/sliders.c | 60 ++++++ app/src/sliders.c | 38 +++- 14 files changed, 393 insertions(+), 69 deletions(-) create mode 100644 app/include/zmk/events/pd_raw_event.h delete mode 100644 app/include/zmk/events/slider_event.h create mode 100644 app/include/zmk/point_device.h rename app/src/events/{slider_event.c => pd_raw_event.c} (60%) create mode 100644 app/src/point_device/Kconfig create mode 100644 app/src/point_device/main.c rename app/src/{mouse => point_device}/pd_listener.c (85%) create mode 100644 app/src/point_device/sliders.c diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 3d3e831cf20..cfe67caeb62 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -28,14 +28,14 @@ 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(app PRIVATE src/sensors.c) -target_sources(app PRIVATE src/sliders.c) +target_sources(app PRIVATE src/point_device/sliders.c) target_sources_ifdef(CONFIG_ZMK_WPM app PRIVATE src/wpm.c) target_sources(app PRIVATE src/event_manager.c) target_sources_ifdef(CONFIG_ZMK_EXT_POWER app PRIVATE src/ext_power_generic.c) target_sources(app PRIVATE src/events/activity_state_changed.c) target_sources(app PRIVATE src/events/position_state_changed.c) target_sources(app PRIVATE src/events/sensor_event.c) -target_sources(app PRIVATE src/events/slider_event.c) +target_sources(app PRIVATE src/events/pd_raw_event.c) target_sources(app PRIVATE src/events/mouse_button_state_changed.c) target_sources(app PRIVATE src/events/mouse_move_state_changed.c) target_sources(app PRIVATE src/events/mouse_tick.c) @@ -78,7 +78,7 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL) target_sources(app PRIVATE src/events/keycode_state_changed.c) target_sources(app PRIVATE src/events/pd_scroll_state_changed.c) target_sources(app PRIVATE src/events/pd_position_state_changed.c) - target_sources(app PRIVATE src/mouse/pd_listener.c) + target_sources(app PRIVATE src/point_device/pd_listener.c) if (CONFIG_ZMK_BLE) target_sources(app PRIVATE src/events/ble_active_profile_changed.c) diff --git a/app/dts/bindings/zmk,keymap.yaml b/app/dts/bindings/zmk,keymap.yaml index 7acb60baa04..3ca05b6aa5a 100644 --- a/app/dts/bindings/zmk,keymap.yaml +++ b/app/dts/bindings/zmk,keymap.yaml @@ -19,3 +19,9 @@ child-binding: slider-bindings: type: phandle-array required: false + trackball-bindings: + type: phandle-array + required: false + joystick-bindings: + type: phandle-array + required: false diff --git a/app/include/zmk/events/pd_raw_event.h b/app/include/zmk/events/pd_raw_event.h new file mode 100644 index 00000000000..6b81a9df326 --- /dev/null +++ b/app/include/zmk/events/pd_raw_event.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include +#include + +struct zmk_pd_raw_event { + enum pd_type type; + uint8_t id; + int16_t dx; + int16_t dy; + int dt; // in ms + int64_t update_time; // in ms +}; + +ZMK_EVENT_DECLARE(zmk_pd_raw_event); diff --git a/app/include/zmk/events/slider_event.h b/app/include/zmk/events/slider_event.h deleted file mode 100644 index d8ea49d00ec..00000000000 --- a/app/include/zmk/events/slider_event.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (c) 2020 The ZMK Contributors - * - * SPDX-License-Identifier: MIT - */ - -#pragma once - -#include - -struct zmk_slider_event { - uint8_t slider_id; - int16_t delta_position; - int delta_time; // in ms - int64_t update_time; -}; - -ZMK_EVENT_DECLARE(zmk_slider_event); diff --git a/app/include/zmk/point_device.h b/app/include/zmk/point_device.h new file mode 100644 index 00000000000..1bc51f01b2b --- /dev/null +++ b/app/include/zmk/point_device.h @@ -0,0 +1,10 @@ +#pragma once + +enum pd_type { + SLIDER, + TRACKBALL, + JOYSTICK +}; + +struct k_work_q *zmk_pd_work_q(); +int zmk_pd_init(); diff --git a/app/src/endpoints.c b/app/src/endpoints.c index 0728fff3bde..6bf964f5e86 100644 --- a/app/src/endpoints.c +++ b/app/src/endpoints.c @@ -160,7 +160,7 @@ int zmk_endpoints_send_mouse_report() { #if IS_ENABLED(CONFIG_ZMK_BLE) case ZMK_ENDPOINT_BLE: { -#if IS_ENABLED(CONFIG_ZMK_MOUSE_WORK_QUEUE_DEDICATED) +#if defined(CONFIG_ZMK_MOUSE_WORK_QUEUE_DEDICATED) || defined(CONFIG_ZMK_PD_SEND_THREAD_DEDICATED) int err = zmk_hog_send_mouse_report_direct(&mouse_report->body); #else int err = zmk_hog_send_mouse_report(&mouse_report->body); diff --git a/app/src/events/slider_event.c b/app/src/events/pd_raw_event.c similarity index 60% rename from app/src/events/slider_event.c rename to app/src/events/pd_raw_event.c index 0835fd103cc..6626f850c8c 100644 --- a/app/src/events/slider_event.c +++ b/app/src/events/pd_raw_event.c @@ -5,6 +5,6 @@ */ #include -#include +#include -ZMK_EVENT_IMPL(zmk_slider_event); +ZMK_EVENT_IMPL(zmk_pd_raw_event); diff --git a/app/src/keymap.c b/app/src/keymap.c index c254868e481..ceaddbdde0b 100644 --- a/app/src/keymap.c +++ b/app/src/keymap.c @@ -25,7 +25,7 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #include #include #include -#include +#include static zmk_keymap_layers_state_t _zmk_keymap_layer_state = 0; static uint8_t _zmk_keymap_layer_default = 0; @@ -41,6 +41,21 @@ static uint8_t _zmk_keymap_layer_default = 0; #define TRANSFORMED_LAYER(node) \ {UTIL_LISTIFY(DT_PROP_LEN(node, bindings), BINDING_WITH_COMMA, node)}, +#define LAYER_LABEL(node) COND_CODE_0(DT_NODE_HAS_PROP(node, label), (NULL), (DT_LABEL(node))), + +// State + +// When a behavior handles a key position "down" event, we record the layer state +// here so that even if that layer is deactivated before the "up", event, we +// still send the release event to the behavior in that layer also. +static uint32_t zmk_keymap_active_behavior_layer[ZMK_KEYMAP_LEN]; + +static struct zmk_behavior_binding zmk_keymap[ZMK_KEYMAP_LAYERS_LEN][ZMK_KEYMAP_LEN] = { + DT_INST_FOREACH_CHILD(0, TRANSFORMED_LAYER)}; + +static const char *zmk_keymap_layer_names[ZMK_KEYMAP_LAYERS_LEN] = { + DT_INST_FOREACH_CHILD(0, LAYER_LABEL)}; + #if ZMK_KEYMAP_HAS_SENSORS #define _TRANSFORM_SENSOR_ENTRY(idx, layer) \ { \ @@ -56,6 +71,10 @@ static uint8_t _zmk_keymap_layer_default = 0; DT_NODE_HAS_PROP(node, sensor_bindings), \ ({UTIL_LISTIFY(DT_PROP_LEN(node, sensor_bindings), _TRANSFORM_SENSOR_ENTRY, node)}), \ ({})), + +static struct zmk_behavior_binding zmk_sensor_keymap[ZMK_KEYMAP_LAYERS_LEN] + [ZMK_KEYMAP_SENSORS_LEN] = { + DT_INST_FOREACH_CHILD(0, SENSOR_LAYER)}; #endif /* ZMK_KEYMAP_HAS_SENSORS */ #if ZMK_KEYMAP_HAS_SLIDERS @@ -73,38 +92,56 @@ static uint8_t _zmk_keymap_layer_default = 0; DT_NODE_HAS_PROP(node, slider_bindings), \ ({UTIL_LISTIFY(DT_PROP_LEN(node, slider_bindings), _TRANSFORM_SLIDER_ENTRY, node)}), \ ({})), -#endif /* ZMK_KEYMAP_HAS_SLIDERS */ - -#define LAYER_LABEL(node) COND_CODE_0(DT_NODE_HAS_PROP(node, label), (NULL), (DT_LABEL(node))), -// State +static struct zmk_behavior_binding zmk_slider_keymap[ZMK_KEYMAP_LAYERS_LEN] + [ZMK_KEYMAP_SLIDERS_LEN] = { + DT_INST_FOREACH_CHILD(0, SLIDER_LAYER)}; -// When a behavior handles a key position "down" event, we record the layer state -// here so that even if that layer is deactivated before the "up", event, we -// still send the release event to the behavior in that layer also. -static uint32_t zmk_keymap_active_behavior_layer[ZMK_KEYMAP_LEN]; +#endif /* ZMK_KEYMAP_HAS_SLIDERS */ -static struct zmk_behavior_binding zmk_keymap[ZMK_KEYMAP_LAYERS_LEN][ZMK_KEYMAP_LEN] = { - DT_INST_FOREACH_CHILD(0, TRANSFORMED_LAYER)}; +#if ZMK_KEYMAP_HAS_TRACKBALLS +#define _TRANSFORM_TRACKBALL_ENTRY(idx, layer) \ + { \ + .behavior_dev = DT_LABEL(DT_PHANDLE_BY_IDX(layer, trackball_bindings, idx)), \ + .param1 = COND_CODE_0(DT_PHA_HAS_CELL_AT_IDX(layer, trackball_bindings, idx, param1), (0), \ + (DT_PHA_BY_IDX(layer, trackball_bindings, idx, param1))), \ + .param2 = COND_CODE_0(DT_PHA_HAS_CELL_AT_IDX(layer, trackball_bindings, idx, param2), (0), \ + (DT_PHA_BY_IDX(layer, trackball_bindings, idx, param2))), \ + }, -static const char *zmk_keymap_layer_names[ZMK_KEYMAP_LAYERS_LEN] = { - DT_INST_FOREACH_CHILD(0, LAYER_LABEL)}; +#define TRACKBALL_LAYER(node) \ + COND_CODE_1( \ + DT_NODE_HAS_PROP(node, trackball_bindings), \ + ({UTIL_LISTIFY(DT_PROP_LEN(node, trackball_bindings), _TRANSFORM_TRACKBALL_ENTRY, node)}), \ + ({})), -#if ZMK_KEYMAP_HAS_SENSORS +static struct zmk_behavior_binding zmk_trackball_keymap[ZMK_KEYMAP_LAYERS_LEN] + [ZMK_KEYMAP_TRACKBALLS_LEN] = { + DT_INST_FOREACH_CHILD(0, TRACKBALL_LAYER)}; -static struct zmk_behavior_binding zmk_sensor_keymap[ZMK_KEYMAP_LAYERS_LEN] - [ZMK_KEYMAP_SENSORS_LEN] = { - DT_INST_FOREACH_CHILD(0, SENSOR_LAYER)}; +#endif /* ZMK_KEYMAP_HAS_TRACKBALLS */ -#endif /* ZMK_KEYMAP_HAS_SENSORS */ +#if ZMK_KEYMAP_HAS_JOYSTICKS +#define _TRANSFORM_JOYSTICK_ENTRY(idx, layer) \ + { \ + .behavior_dev = DT_LABEL(DT_PHANDLE_BY_IDX(layer, joystick_bindings, idx)), \ + .param1 = COND_CODE_0(DT_PHA_HAS_CELL_AT_IDX(layer, joystick_bindings, idx, param1), (0), \ + (DT_PHA_BY_IDX(layer, joystick_bindings, idx, param1))), \ + .param2 = COND_CODE_0(DT_PHA_HAS_CELL_AT_IDX(layer, joystick_bindings, idx, param2), (0), \ + (DT_PHA_BY_IDX(layer, joystick_bindings, idx, param2))), \ + }, -#if ZMK_KEYMAP_HAS_SLIDERS +#define JOYSTICK_LAYER(node) \ + COND_CODE_1( \ + DT_NODE_HAS_PROP(node, joystick_bindings), \ + ({UTIL_LISTIFY(DT_PROP_LEN(node, joystick_bindings), _TRANSFORM_JOYSTICK_ENTRY, node)}), \ + ({})), -static struct zmk_behavior_binding zmk_slider_keymap[ZMK_KEYMAP_LAYERS_LEN] - [ZMK_KEYMAP_SLIDERS_LEN] = { - DT_INST_FOREACH_CHILD(0, SLIDER_LAYER)}; +static struct zmk_behavior_binding zmk_joystick_keymap[ZMK_KEYMAP_LAYERS_LEN] + [ZMK_KEYMAP_JOYSTICKS_LEN] = { + DT_INST_FOREACH_CHILD(0, JOYSTICK_LAYER)}; -#endif /* ZMK_KEYMAP_HAS_SLIDERS */ +#endif /* ZMK_KEYMAP_HAS_JOYSTICKS */ static inline int set_layer_state(uint8_t layer, bool state, bool flag_event) { if (layer >= ZMK_KEYMAP_LAYERS_LEN) { @@ -281,7 +318,7 @@ int zmk_keymap_position_state_changed(uint8_t source, uint32_t position, bool pr } #if ZMK_KEYMAP_HAS_SENSORS -int zmk_keymap_sensor_triggered(uint8_t sensor_number, const struct sensor_value value, +inline static int zmk_keymap_sensor_triggered(uint8_t sensor_number, const struct sensor_value value, int64_t timestamp) { for (int layer = ZMK_KEYMAP_LAYERS_LEN - 1; layer >= _zmk_keymap_layer_default; layer--) { if (zmk_keymap_layer_active(layer) && zmk_sensor_keymap[layer] != NULL) { @@ -319,7 +356,7 @@ int zmk_keymap_sensor_triggered(uint8_t sensor_number, const struct sensor_value #endif /* ZMK_KEYMAP_HAS_SENSORS */ #if ZMK_KEYMAP_HAS_SLIDERS -int zmk_keymap_slider_triggered(uint8_t id, int16_t dPos, int dT) +inline static int zmk_keymap_slider_triggered(uint8_t id, int16_t dx, int16_t dy, int dt) { for (int layer = ZMK_KEYMAP_LAYERS_LEN - 1; layer >= _zmk_keymap_layer_default; layer--) { if (zmk_keymap_layer_active(layer) && zmk_slider_keymap[layer] != NULL) { @@ -337,7 +374,7 @@ int zmk_keymap_slider_triggered(uint8_t id, int16_t dPos, int dT) continue; } - ret = behavior_pd_keymap_binding_triggered(binding, dPos, dPos, dT); + ret = behavior_pd_keymap_binding_triggered(binding, dx, dy, dt); if (ret > 0) { LOG_DBG("behavior processing to continue to next layer"); @@ -355,6 +392,80 @@ int zmk_keymap_slider_triggered(uint8_t id, int16_t dPos, int dT) } #endif /* ZMK_KEYMAP_HAS_SLIDERS */ +#if ZMK_KEYMAP_HAS_TRACKBALLS +inline static int zmk_keymap_trackball_triggered(uint8_t id, int16_t dx, int16_t dy, int dt) +{ + for (int layer = ZMK_KEYMAP_LAYERS_LEN - 1; layer >= _zmk_keymap_layer_default; layer--) { + if (zmk_keymap_layer_active(layer) && zmk_trackball_keymap[layer] != NULL) { + struct zmk_behavior_binding *binding = &zmk_trackball_keymap[layer][id]; + const struct device *behavior; + int ret; + + LOG_INF("layer: %d slider_id: %d, binding name: %s", layer, id, + log_strdup(binding->behavior_dev)); + + behavior = device_get_binding(binding->behavior_dev); + + if (!behavior) { + LOG_DBG("No behavior assigned to %d on layer %d", id, layer); + continue; + } + + ret = behavior_pd_keymap_binding_triggered(binding, dx, dy, dt); + + if (ret > 0) { + LOG_DBG("behavior processing to continue to next layer"); + continue; + } else if (ret < 0) { + LOG_DBG("Behavior returned error: %d", ret); + return ret; + } else { + return ret; + } + } + } + + return -ENOTSUP; +} +#endif /* ZMK_KEYMAP_HAS_TRACKBALLS */ + +#if ZMK_KEYMAP_HAS_JOYSTICKS +inline static int zmk_keymap_joystick_triggered(uint8_t id, int16_t dx, int16_t dy, int dt) +{ + for (int layer = ZMK_KEYMAP_LAYERS_LEN - 1; layer >= _zmk_keymap_layer_default; layer--) { + if (zmk_keymap_layer_active(layer) && zmk_joystick_keymap[layer] != NULL) { + struct zmk_behavior_binding *binding = &zmk_joystick_keymap[layer][id]; + const struct device *behavior; + int ret; + + LOG_INF("layer: %d slider_id: %d, binding name: %s", layer, id, + log_strdup(binding->behavior_dev)); + + behavior = device_get_binding(binding->behavior_dev); + + if (!behavior) { + LOG_DBG("No behavior assigned to %d on layer %d", id, layer); + continue; + } + + ret = behavior_pd_keymap_binding_triggered(binding, dx, dy, dt); + + if (ret > 0) { + LOG_DBG("behavior processing to continue to next layer"); + continue; + } else if (ret < 0) { + LOG_DBG("Behavior returned error: %d", ret); + return ret; + } else { + return ret; + } + } + } + + return -ENOTSUP; +} +#endif /* ZMK_KEYMAP_HAS_JOYSTICKS */ + int keymap_listener(const zmk_event_t *eh) { const struct zmk_position_state_changed *pos_ev; if ((pos_ev = as_zmk_position_state_changed(eh)) != NULL) { @@ -370,13 +481,25 @@ int keymap_listener(const zmk_event_t *eh) { } #endif /* ZMK_KEYMAP_HAS_SENSORS */ + const struct zmk_pd_raw_event *pd_ev; + if ((pd_ev = as_zmk_pd_raw_event(eh)) != NULL) { + switch (pd_ev->type) { #if ZMK_KEYMAP_HAS_SLIDERS - const struct zmk_slider_event *slider_ev; - if ((slider_ev = as_zmk_slider_event(eh)) != NULL) { - return zmk_keymap_slider_triggered(slider_ev->slider_id, slider_ev->delta_position, - slider_ev->delta_time); - } + case SLIDER: + return zmk_keymap_slider_triggered(pd_ev->id, pd_ev->dx, pd_ev->dy, pd_ev->dt); #endif /* ZMK_KEYMAP_HAS_SLIDERS */ +#if ZMK_KEYMAP_HAS_TRACKBALLS + case TRACKBALL: + return zmk_keymap_trackball_triggered(pd_ev->id, pd_ev->dx, pd_ev->dy, pd_ev->dt); +#endif /* ZMK_KEYMAP_HAS_TRACKBALLS */ +#if ZMK_KEYMAP_HAS_JOYSTICKS + case JOYSTICK: + return zmk_keymap_joystick_triggered(pd_ev->id, pd_ev->dx, pd_ev->dy, pd_ev->dt); +#endif /* ZMK_KEYMAP_HAS_JOYSTICKS */ + default: + break; + } + } return -ENOTSUP; } @@ -387,6 +510,6 @@ ZMK_SUBSCRIPTION(keymap, zmk_position_state_changed); ZMK_SUBSCRIPTION(keymap, zmk_sensor_event); #endif /* ZMK_KEYMAP_HAS_SENSORS */ -#if ZMK_KEYMAP_HAS_SLIDERS -ZMK_SUBSCRIPTION(keymap, zmk_slider_event); +#if defined(ZMK_KEYMAP_HAS_SLIDERS) || defined(ZMK_KEYMAP_HAS_TRACKBALLS) || defined(ZMK_KEYMAP_HAS_JOYSTICKS) +ZMK_SUBSCRIPTION(keymap, zmk_pd_raw_event); #endif diff --git a/app/src/main.c b/app/src/main.c index d3b3e578b3e..2173230423e 100644 --- a/app/src/main.c +++ b/app/src/main.c @@ -21,6 +21,10 @@ LOG_MODULE_REGISTER(zmk, CONFIG_ZMK_LOG_LEVEL); #include #endif /* CONFIG_ZMK_MOUSE */ +#ifdef CONFIG_ZMK_POINT_DEVICE +#include +#endif /* CONFIG_ZMK_POINT_DEVICE */ + #define ZMK_KSCAN_DEV DT_LABEL(ZMK_MATRIX_NODE_ID) void main(void) { @@ -37,4 +41,8 @@ void main(void) { #ifdef CONFIG_ZMK_MOUSE zmk_mouse_init(); #endif /* CONFIG_ZMK_MOUSE */ + +#ifdef CONFIG_ZMK_POINT_DEVICE + zmk_pd_init(); +#endif /* CONFIG_ZMK_POINT_DEVICE */ } diff --git a/app/src/point_device/Kconfig b/app/src/point_device/Kconfig new file mode 100644 index 00000000000..dc1d6bb8b84 --- /dev/null +++ b/app/src/point_device/Kconfig @@ -0,0 +1,54 @@ +# Copyright (c) 2021 The ZMK Contributors +# SPDX-License-Identifier: MIT + +menuconfig ZMK_POINT_DEVICE + bool "Enable ZMK general point device support" + default n + +if ZMK_POINT_DEVICE + +choice ZMK_PD_WORK_QUEUE + prompt "Work queue used to process point device events" + +config ZMK_PD_WORK_QUEUE_DEDICATED + bool "Use dedicated work queue for point device events" + +config ZMK_PD_WORK_QUEUE_SYSTEM + bool "Use default system work queue for point device events" +endchoice + +if ZMK_PD_WORK_QUEUE_DEDICATED + +config ZMK_PD_DEDICATED_WORK_QUEUE_STACK_SIZE + int "Stack size for dedicated point device thread/queue" + default 2048 + +config ZMK_PD_DEDICATED_WORK_QUEUE_PRIORITY + int "Thread priority for dedicated point device thread/queue" + default 2 + +endif # ZMK_PD_WORK_QUEUE_DEDICATED + +choice ZMK_PD_SEND_THREAD + prompt "Thread used to send mouse report" + +config ZMK_PD_SEND_THREAD_DEDICATED + bool "Use dedicated thread to send mouse report" + +config ZMK_PD_SEND_THREAD_SHARED + bool "Use default zmk work queue to send mouse report" +endchoice + +if ZMK_PD_SEND_THREAD_DEDICATED + +config ZMK_PD_DEDICATED_SEND_THREAD_STACK_SIZE + int "Stack size for dedicated point device sending thread" + default 2048 + +config ZMK_PD_DEDICATED_SEND_THREAD_PRIORITY + int "Thread priority for dedicated point device sending thread" + default -1 + +endif # ZMK_PD_SEND_THREAD_DEDICATED + +endif # ZMK_POINT_DEVICE diff --git a/app/src/point_device/main.c b/app/src/point_device/main.c new file mode 100644 index 00000000000..25dc096478c --- /dev/null +++ b/app/src/point_device/main.c @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include + +#if IS_ENABLED(CONFIG_ZMK_PD_WORK_QUEUE_DEDICATED) +static const struct k_work_queue_config pd_work_q_config = {.name = "Processor for events from all point devices", + .no_yield = false }; +static struct k_work_q pd_work_q; +K_THREAD_STACK_DEFINE(pd_work_stack_area, CONFIG_ZMK_PD_DEDICATED_WORK_QUEUE_STACK_SIZE); +#endif + +struct k_work_q *zmk_pd_work_q() { +#if IS_ENABLED(CONFIG_ZMK_PD_WORK_QUEUE_DEDICATED) + return &pd_work_q; +#else + return &k_sys_work_q; +#endif +} + +int zmk_pd_init() { +#if IS_ENABLED(CONFIG_ZMK_PD_WORK_QUEUE_DEDICATED) + k_work_queue_init(&pd_work_q); + k_work_queue_start(&pd_work_q, pd_work_stack_area, + K_THREAD_STACK_SIZEOF(pd_work_stack_area), + CONFIG_ZMK_MOUSE_DEDICATED_WORK_QUEUE_PRIORITY, + &pd_work_q_config); +#endif + return 0; +} diff --git a/app/src/mouse/pd_listener.c b/app/src/point_device/pd_listener.c similarity index 85% rename from app/src/mouse/pd_listener.c rename to app/src/point_device/pd_listener.c index 2126f5923df..e7682e18739 100644 --- a/app/src/mouse/pd_listener.c +++ b/app/src/point_device/pd_listener.c @@ -17,16 +17,16 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #include #include -struct zmk_pd_msg { +typedef struct { int16_t x; int16_t y; bool scroll; -}; +}__attribute__((aligned(4))) zmk_pd_msg; -K_MSGQ_DEFINE(zmk_pd_msgq, sizeof(struct zmk_pd_msg), CONFIG_ZMK_KSCAN_EVENT_QUEUE_SIZE, 4); +K_MSGQ_DEFINE(zmk_pd_msgq, sizeof(zmk_pd_msg), CONFIG_ZMK_KSCAN_EVENT_QUEUE_SIZE, 4); static void pd_process_msgq(struct k_work *work) { - struct zmk_pd_msg msg; + zmk_pd_msg msg; while (k_msgq_get(&zmk_pd_msgq, &msg, K_NO_WAIT) == 0) { if (msg.scroll) { @@ -49,7 +49,7 @@ K_WORK_DEFINE(pd_msg_processor, pd_process_msgq); int pd_listener(const zmk_event_t *eh) { const struct zmk_pd_position_state_changed *mv_ev = as_zmk_pd_position_state_changed(eh); if (mv_ev) { - struct zmk_pd_msg msg = {.x = mv_ev->x, .y = mv_ev->y, .scroll = false}; + zmk_pd_msg msg = {.x = mv_ev->x, .y = mv_ev->y, .scroll = false}; k_msgq_put(&zmk_pd_msgq, &msg, K_NO_WAIT); k_work_submit_to_queue(zmk_mouse_work_q(), &pd_msg_processor); return 0; @@ -57,7 +57,7 @@ int pd_listener(const zmk_event_t *eh) { const struct zmk_pd_scroll_state_changed *sc_ev = as_zmk_pd_scroll_state_changed(eh); if (sc_ev) { - struct zmk_pd_msg msg = {.x = sc_ev->x, .y = sc_ev->y, .scroll = true}; + zmk_pd_msg msg = {.x = sc_ev->x, .y = sc_ev->y, .scroll = true}; k_msgq_put(&zmk_pd_msgq, &msg, K_NO_WAIT); k_work_submit_to_queue(zmk_mouse_work_q(), &pd_msg_processor); return 0; diff --git a/app/src/point_device/sliders.c b/app/src/point_device/sliders.c new file mode 100644 index 00000000000..284891fd52f --- /dev/null +++ b/app/src/point_device/sliders.c @@ -0,0 +1,60 @@ +/* Description: +* 1. setup sliders callback +* 2. The callback will set up the correct information and raise a slider event +* 3. The processing of slider event is dispatched to listening processors, notably keymap +*/ + +#include +#include + +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#include +#include +#include +#include + +#include +#include + +#if ZMK_KEYMAP_HAS_SLIDERS + +/* the slider callback */ +void zmk_slider_callback(const struct device *dev, int16_t dPos, int dT) +{ + struct slider_data *slider_data = dev->data; + + LOG_INF("Event from slider_%d: dPos: %d, dT: %d ms", slider_data->id, dPos, dT); + ZMK_EVENT_RAISE(new_zmk_pd_raw_event((struct zmk_pd_raw_event){ + .id = slider_data->id, .dx = dPos, .dy = dPos, + .dt = dT, .update_time = k_uptime_get()})); + return; +} + +/* config the sliders: setup callback and assign the slider id */ +static void zmk_sliders_init_item(const char *node, uint8_t abs_i) { + LOG_DBG("Init %s with slider_id %d", node, abs_i); + + const struct device *dev = device_get_binding(node); + if (!dev) { + LOG_WRN("Failed to find device for %s", node); + return; + } + + kscan_slider_config(dev, zmk_slider_callback, abs_i); +} + +#define SLIDER_INIT(idx, _i) \ + COND_CODE_1(DT_NODE_HAS_STATUS(ZMK_KEYMAP_SLIDERS_BY_IDX(idx), okay), \ + (zmk_sliders_init_item(DT_LABEL(ZMK_KEYMAP_SLIDERS_BY_IDX(idx)), idx);), (;)) + +static int zmk_sliders_init(const struct device *_arg) { + UTIL_LISTIFY(ZMK_KEYMAP_SLIDERS_LEN, SLIDER_INIT, 0) + return 0; +} + +SYS_INIT(zmk_sliders_init, APPLICATION, 91); +#endif + diff --git a/app/src/sliders.c b/app/src/sliders.c index 049d27177a0..f04d2142ac6 100644 --- a/app/src/sliders.c +++ b/app/src/sliders.c @@ -11,26 +11,52 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); +#include #include #include #include -#include +#include #include #include #if ZMK_KEYMAP_HAS_SLIDERS +typedef struct { + uint8_t id; + int16_t dPos; + int dT; +}__attribute__((aligned(4))) zmk_slider_msg; + +K_MSGQ_DEFINE(zmk_slider_msgq, sizeof(struct zmk_slider_msg), CONFIG_ZMK_KSCAN_EVENT_QUEUE_SIZE, 4); + +/* the slider msg processor */ +void zmk_slider_process_msgq(struct k_work *item) { + struct zmk_slider_msg msg; + + while (k_msgq_get(&zmk_slider_msgq, &msg, K_NO_WAIT) == 0) { + LOG_INF("Event from slider_%d: dPos: %d, dT: %d ms", msg.id, msg.dPos. msg.dT); + ZMK_EVENT_RAISE(new_zmk_pd_raw_event((struct zmk_pd_raw_event){ + .type = SLIDER, + .id = msg.id, .dx = msg.dPos, .dy = msg.dPos, + .dt = msg.dT, .update_time = k_uptime_get()})); + } +} + +K_WORK_DEFINE(slider_msgq_work, zmk_slider_process_msgq); + /* the slider callback */ void zmk_slider_callback(const struct device *dev, int16_t dPos, int dT) { struct slider_data *slider_data = dev->data; + struct zmk_slider_msg msg = { + .id = slider_data->id, + .dPos = dPos, + .dT = dT + }; - LOG_INF("Event from slider_%d: dPos: %d, dT: %d ms", slider_data->id+1, dPos, dT); - ZMK_EVENT_RAISE(new_zmk_slider_event((struct zmk_slider_event){ - .slider_id = slider_data->id, .delta_position = dPos, - .delta_time = dT, .update_time = k_uptime_get()})); - return; + k_msgq_put(&zmk_slider_msgq, &msg, K_NO_WAIT); + k_work_submit_to_queue(zmk_pd_work_q(), &slider_msgq_work); } /* config the sliders: setup callback and assign the slider id */ From f7c6a5fa45a7e77a9781ca4136a52db4b776f846 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Sun, 9 Oct 2022 18:54:12 +0800 Subject: [PATCH 101/157] bugfix --- app/CMakeLists.txt | 1 + app/drivers/kscan/kscan_cap1203.c | 2 +- app/src/point_device/sliders.c | 36 ++++++++++++++++--- .../{sliders.c => point_device/trackballs.c} | 36 +++---------------- 4 files changed, 38 insertions(+), 37 deletions(-) rename app/src/{sliders.c => point_device/trackballs.c} (61%) diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index cfe67caeb62..0d6ca7daa12 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -28,6 +28,7 @@ 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(app PRIVATE src/sensors.c) +target_sources(app PRIVATE src/point_device/main.c) target_sources(app PRIVATE src/point_device/sliders.c) target_sources_ifdef(CONFIG_ZMK_WPM app PRIVATE src/wpm.c) target_sources(app PRIVATE src/event_manager.c) diff --git a/app/drivers/kscan/kscan_cap1203.c b/app/drivers/kscan/kscan_cap1203.c index 85c62576331..0176293e576 100644 --- a/app/drivers/kscan/kscan_cap1203.c +++ b/app/drivers/kscan/kscan_cap1203.c @@ -355,7 +355,7 @@ static int kscan_cap1203_read(const struct device *dev) slider_data->callback(dev, slider_data->delta_position, slider_data->delta_time); } - LOG_INF("dPos: %d, dT: %d us", slider_data->delta_position, slider_data->delta_time); + LOG_INF("dPos: %d, dT: %d ms", slider_data->delta_position, slider_data->delta_time); } } diff --git a/app/src/point_device/sliders.c b/app/src/point_device/sliders.c index 284891fd52f..a77956f2053 100644 --- a/app/src/point_device/sliders.c +++ b/app/src/point_device/sliders.c @@ -11,6 +11,7 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); +#include #include #include #include @@ -21,16 +22,41 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #if ZMK_KEYMAP_HAS_SLIDERS +typedef struct { + uint8_t id; + int16_t dPos; + int dT; +}__attribute__((aligned(4))) zmk_slider_msg; + +K_MSGQ_DEFINE(zmk_slider_msgq, sizeof(zmk_slider_msg), CONFIG_ZMK_KSCAN_EVENT_QUEUE_SIZE, 4); + +/* the slider msg processor */ +void zmk_slider_process_msgq(struct k_work *item) { + zmk_slider_msg msg; + + while (k_msgq_get(&zmk_slider_msgq, &msg, K_NO_WAIT) == 0) { + LOG_INF("Event from slider_%d: dPos: %d, dT: %d ms", msg.id, msg.dPos, msg.dT); + ZMK_EVENT_RAISE(new_zmk_pd_raw_event((struct zmk_pd_raw_event){ + .type = SLIDER, + .id = msg.id, .dx = msg.dPos, .dy = msg.dPos, + .dt = msg.dT, .update_time = k_uptime_get()})); + } +} + +K_WORK_DEFINE(slider_msgq_work, zmk_slider_process_msgq); + /* the slider callback */ void zmk_slider_callback(const struct device *dev, int16_t dPos, int dT) { struct slider_data *slider_data = dev->data; + zmk_slider_msg msg = { + .id = slider_data->id, + .dPos = dPos, + .dT = dT + }; - LOG_INF("Event from slider_%d: dPos: %d, dT: %d ms", slider_data->id, dPos, dT); - ZMK_EVENT_RAISE(new_zmk_pd_raw_event((struct zmk_pd_raw_event){ - .id = slider_data->id, .dx = dPos, .dy = dPos, - .dt = dT, .update_time = k_uptime_get()})); - return; + k_msgq_put(&zmk_slider_msgq, &msg, K_NO_WAIT); + k_work_submit_to_queue(zmk_pd_work_q(), &slider_msgq_work); } /* config the sliders: setup callback and assign the slider id */ diff --git a/app/src/sliders.c b/app/src/point_device/trackballs.c similarity index 61% rename from app/src/sliders.c rename to app/src/point_device/trackballs.c index f04d2142ac6..284891fd52f 100644 --- a/app/src/sliders.c +++ b/app/src/point_device/trackballs.c @@ -11,7 +11,6 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); -#include #include #include #include @@ -22,41 +21,16 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #if ZMK_KEYMAP_HAS_SLIDERS -typedef struct { - uint8_t id; - int16_t dPos; - int dT; -}__attribute__((aligned(4))) zmk_slider_msg; - -K_MSGQ_DEFINE(zmk_slider_msgq, sizeof(struct zmk_slider_msg), CONFIG_ZMK_KSCAN_EVENT_QUEUE_SIZE, 4); - -/* the slider msg processor */ -void zmk_slider_process_msgq(struct k_work *item) { - struct zmk_slider_msg msg; - - while (k_msgq_get(&zmk_slider_msgq, &msg, K_NO_WAIT) == 0) { - LOG_INF("Event from slider_%d: dPos: %d, dT: %d ms", msg.id, msg.dPos. msg.dT); - ZMK_EVENT_RAISE(new_zmk_pd_raw_event((struct zmk_pd_raw_event){ - .type = SLIDER, - .id = msg.id, .dx = msg.dPos, .dy = msg.dPos, - .dt = msg.dT, .update_time = k_uptime_get()})); - } -} - -K_WORK_DEFINE(slider_msgq_work, zmk_slider_process_msgq); - /* the slider callback */ void zmk_slider_callback(const struct device *dev, int16_t dPos, int dT) { struct slider_data *slider_data = dev->data; - struct zmk_slider_msg msg = { - .id = slider_data->id, - .dPos = dPos, - .dT = dT - }; - k_msgq_put(&zmk_slider_msgq, &msg, K_NO_WAIT); - k_work_submit_to_queue(zmk_pd_work_q(), &slider_msgq_work); + LOG_INF("Event from slider_%d: dPos: %d, dT: %d ms", slider_data->id, dPos, dT); + ZMK_EVENT_RAISE(new_zmk_pd_raw_event((struct zmk_pd_raw_event){ + .id = slider_data->id, .dx = dPos, .dy = dPos, + .dt = dT, .update_time = k_uptime_get()})); + return; } /* config the sliders: setup callback and assign the slider id */ From 95f8ddcd92b26cd30ca8dc0a4254fbe7593bedcc Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Sun, 9 Oct 2022 19:04:45 +0800 Subject: [PATCH 102/157] bugfix --- app/Kconfig | 6 ++++++ app/src/point_device/main.c | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/Kconfig b/app/Kconfig index 26c7f300ebb..125fa17b393 100644 --- a/app/Kconfig +++ b/app/Kconfig @@ -296,6 +296,12 @@ rsource "src/mouse/Kconfig" #Mouse Options endmenu +menu "Point Device Options" + +rsource "src/point_device/Kconfig" + +endmenu + menu "Power Management" config ZMK_IDLE_TIMEOUT diff --git a/app/src/point_device/main.c b/app/src/point_device/main.c index 25dc096478c..3362b49483c 100644 --- a/app/src/point_device/main.c +++ b/app/src/point_device/main.c @@ -27,7 +27,7 @@ int zmk_pd_init() { k_work_queue_init(&pd_work_q); k_work_queue_start(&pd_work_q, pd_work_stack_area, K_THREAD_STACK_SIZEOF(pd_work_stack_area), - CONFIG_ZMK_MOUSE_DEDICATED_WORK_QUEUE_PRIORITY, + CONFIG_ZMK_PD_DEDICATED_WORK_QUEUE_PRIORITY, &pd_work_q_config); #endif return 0; From 6d89ddc1cfa90d4673780e614491721526e74b0c Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Mon, 10 Oct 2022 09:57:34 +0800 Subject: [PATCH 103/157] [trackballs] init working build with new implementation of trackball applaication code --- app/CMakeLists.txt | 1 + app/drivers/kscan/kscan_cap1203.c | 10 +- app/drivers/kscan/kscan_gpio_direct.c | 2 +- app/drivers/sensor/pixart/paw3395/Kconfig | 1 + app/drivers/sensor/pixart/paw3395/paw3395.c | 9 +- app/drivers/sensor/pixart/pixart.h | 1 + app/drivers/sensor/pixart/pmw3360/Kconfig | 1 + app/drivers/sensor/pixart/pmw3360/pmw3360.c | 9 +- app/drivers/sensor/pixart/pmw3610/Kconfig | 1 + app/drivers/sensor/pixart/pmw3610/pmw3610.c | 23 +- .../dts/bindings/sensor/pixart,pmw3610.yml | 4 - app/dts/behaviors.dtsi | 1 + app/dts/behaviors/trackball.dtsi | 25 ++ ...zmk,behavior-point-device-incremental.yaml | 2 +- app/dts/bindings/zmk,keymap-trackballs.yaml | 12 + app/include/zmk/trackballs.h | 12 + .../behavior_point_device_incremental.c | 2 + app/src/keymap.c | 1 + app/src/point_device/Kconfig | 12 + app/src/point_device/sliders.c | 2 +- app/src/point_device/trackballs.c | 242 +++++++++++++++--- 21 files changed, 304 insertions(+), 69 deletions(-) create mode 100644 app/dts/behaviors/trackball.dtsi create mode 100644 app/dts/bindings/zmk,keymap-trackballs.yaml create mode 100644 app/include/zmk/trackballs.h diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 0d6ca7daa12..334e77008c1 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -30,6 +30,7 @@ target_sources(app PRIVATE src/mouse/tick_listener.c) target_sources(app PRIVATE src/sensors.c) target_sources(app PRIVATE src/point_device/main.c) target_sources(app PRIVATE src/point_device/sliders.c) +target_sources(app PRIVATE src/point_device/trackballs.c) target_sources_ifdef(CONFIG_ZMK_WPM app PRIVATE src/wpm.c) target_sources(app PRIVATE src/event_manager.c) target_sources_ifdef(CONFIG_ZMK_EXT_POWER app PRIVATE src/ext_power_generic.c) diff --git a/app/drivers/kscan/kscan_cap1203.c b/app/drivers/kscan/kscan_cap1203.c index 0176293e576..f1a785cf514 100644 --- a/app/drivers/kscan/kscan_cap1203.c +++ b/app/drivers/kscan/kscan_cap1203.c @@ -74,7 +74,7 @@ struct kscan_cap1203_data { int64_t update_duration; #endif - struct device *dev; + const struct device *dev; kscan_callback_t callback; // zmk's kscan callback struct k_work work; // actual processing work struct gpio_callback int_gpio_cb; // alert gpio pin callback @@ -84,7 +84,7 @@ struct kscan_cap1203_data { #if defined(CONFIG_CAP1203_SLIDER_MODE) || defined(CONFIG_CAP1203_MIX_MODE) /* array of the status patterns, the array_index+1 is the position indicator */ -static uint8_t const slider_pattern[5] = {1, 3, 2, 6, 4}; +/* static uint8_t const slider_pattern[5] = {1, 3, 2, 6, 4}; */ /* array of slider positions with slider pattern as array index */ /* The position of unknown slider patterns is defined to be 0 */ static uint8_t const slider_position[8] = {0, 1, 3, 2, 5, 0, 4, 0}; @@ -112,7 +112,7 @@ static inline int kscan_cap1203_bit_write(const struct i2c_dt_spec *i2c, uint8_t uint8_t val; int err; - if(err=i2c_reg_read_byte_dt(i2c, reg, &val)) { + if((err=i2c_reg_read_byte_dt(i2c, reg, &val))) { return err; } @@ -132,7 +132,7 @@ static inline int kscan_cap1203_check_firmware(const struct device *dev) uint8_t val; int err; - if(err=i2c_reg_read_byte_dt(&config->i2c, REG_PRODUCT_ID, &val)) { + if((err=i2c_reg_read_byte_dt(&config->i2c, REG_PRODUCT_ID, &val))) { LOG_INF("Can't read register: product id"); return err; } @@ -141,7 +141,7 @@ static inline int kscan_cap1203_check_firmware(const struct device *dev) return -EIO; } - if(err=i2c_reg_read_byte_dt(&config->i2c, REG_VENDOR_ID, &val)) { + if((err=i2c_reg_read_byte_dt(&config->i2c, REG_VENDOR_ID, &val))) { LOG_INF("Can't read register: vendor id"); return err; } diff --git a/app/drivers/kscan/kscan_gpio_direct.c b/app/drivers/kscan/kscan_gpio_direct.c index ee7b13f61a6..a4972ea3f1c 100644 --- a/app/drivers/kscan/kscan_gpio_direct.c +++ b/app/drivers/kscan/kscan_gpio_direct.c @@ -350,7 +350,7 @@ static const struct kscan_driver_api kscan_direct_api = { }; \ \ DEVICE_DT_INST_DEFINE(n, &kscan_direct_init, NULL, &kscan_direct_data_##n, \ - &kscan_direct_config_##n, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY, \ + &kscan_direct_config_##n, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \ &kscan_direct_api); DT_INST_FOREACH_STATUS_OKAY(KSCAN_DIRECT_INIT); diff --git a/app/drivers/sensor/pixart/paw3395/Kconfig b/app/drivers/sensor/pixart/paw3395/Kconfig index a4b4922fb18..7e20bad4375 100644 --- a/app/drivers/sensor/pixart/paw3395/Kconfig +++ b/app/drivers/sensor/pixart/paw3395/Kconfig @@ -7,6 +7,7 @@ menuconfig PAW3395 bool "PAW3395 mouse optical sensor" + select SPI help Enable PAW3395 mouse optical sensor. diff --git a/app/drivers/sensor/pixart/paw3395/paw3395.c b/app/drivers/sensor/pixart/paw3395/paw3395.c index 6d97f485293..cdcd31c542d 100644 --- a/app/drivers/sensor/pixart/paw3395/paw3395.c +++ b/app/drivers/sensor/pixart/paw3395/paw3395.c @@ -979,12 +979,7 @@ static void trigger_handler(struct k_work *work) return; } - struct sensor_trigger trig = { - .type = SENSOR_TRIG_DATA_READY, - .chan = SENSOR_CHAN_ALL, - }; - - handler(dev, &trig); + handler(dev, data->trigger); // 2. the second lock period is used to resume the interrupt line // if data_ready_handler is non-NULL, otherwise keep it inactive @@ -1202,6 +1197,8 @@ static int paw3395_trigger_set(const struct device *dev, data->data_ready_handler = handler; } + data->trigger = trig; + k_spin_unlock(&data->lock, key); return err; diff --git a/app/drivers/sensor/pixart/pixart.h b/app/drivers/sensor/pixart/pixart.h index 76f2196d8a5..13caf385dbc 100644 --- a/app/drivers/sensor/pixart/pixart.h +++ b/app/drivers/sensor/pixart/pixart.h @@ -28,6 +28,7 @@ extern "C" { struct gpio_callback irq_gpio_cb; // the actual trigger handler. This handler also used to flag whether resuming the motion interrupt line sensor_trigger_handler_t data_ready_handler; + const struct sensor_trigger *trigger; // the work structure holding the trigger handler job struct k_work trigger_handler_work; diff --git a/app/drivers/sensor/pixart/pmw3360/Kconfig b/app/drivers/sensor/pixart/pmw3360/Kconfig index 97754cfeaec..863835cc567 100644 --- a/app/drivers/sensor/pixart/pmw3360/Kconfig +++ b/app/drivers/sensor/pixart/pmw3360/Kconfig @@ -7,6 +7,7 @@ menuconfig PMW3360 bool "PMW3360 mouse optical sensor" + select SPI help Enable PMW3360 mouse optical sensor. diff --git a/app/drivers/sensor/pixart/pmw3360/pmw3360.c b/app/drivers/sensor/pixart/pmw3360/pmw3360.c index a457f08fea8..c67e6a245fe 100644 --- a/app/drivers/sensor/pixart/pmw3360/pmw3360.c +++ b/app/drivers/sensor/pixart/pmw3360/pmw3360.c @@ -699,12 +699,7 @@ static void trigger_handler(struct k_work *work) return; } - struct sensor_trigger trig = { - .type = SENSOR_TRIG_DATA_READY, - .chan = SENSOR_CHAN_ALL, - }; - - handler(dev, &trig); + handler(dev, data->trigger); // 2. the second lock period is used to resume the interrupt line // if data_ready_handler is non-NULL, otherwise keep it inactive @@ -975,6 +970,8 @@ static int pmw3360_trigger_set(const struct device *dev, data->data_ready_handler = handler; } + data->trigger = trig; + k_spin_unlock(&data->lock, key); return err; diff --git a/app/drivers/sensor/pixart/pmw3610/Kconfig b/app/drivers/sensor/pixart/pmw3610/Kconfig index 78cb8d4695b..f4f1db37061 100644 --- a/app/drivers/sensor/pixart/pmw3610/Kconfig +++ b/app/drivers/sensor/pixart/pmw3610/Kconfig @@ -7,6 +7,7 @@ menuconfig PMW3610 bool "PMW3610 mouse optical sensor" + select SPI help Enable PMW3610 mouse optical sensor. diff --git a/app/drivers/sensor/pixart/pmw3610/pmw3610.c b/app/drivers/sensor/pixart/pmw3610/pmw3610.c index fdbe037f2ca..5f127a34ee1 100644 --- a/app/drivers/sensor/pixart/pmw3610/pmw3610.c +++ b/app/drivers/sensor/pixart/pmw3610/pmw3610.c @@ -127,8 +127,8 @@ enum pmw3610_init_step { // - Since MCU is not involved in the sensor init process, i is allowed to do other tasks. // Thus, k_sleep or delayed schedule can be used. static const int32_t async_init_delay[ASYNC_INIT_STEP_COUNT] = { - [ASYNC_INIT_STEP_POWER_UP] = 5, // test shows > 5ms needed - [ASYNC_INIT_STEP_CLEAR_OB1] = 100, // 150 us required, test shows too short, + [ASYNC_INIT_STEP_POWER_UP] = 10, // test shows > 5ms needed + [ASYNC_INIT_STEP_CLEAR_OB1] = 200, // 150 us required, test shows too short, // also power-up reset is added in this step, thus using 50 ms [ASYNC_INIT_STEP_CHECK_OB1] = 50, // 10 ms required in spec, // test shows too short, @@ -622,11 +622,11 @@ static int pmw3610_async_init_check_ob1(const struct device *dev) return -EINVAL; } - err = check_product_id(dev); - if (err) { - LOG_ERR("Failed checking product id"); - return err; - } + /* err = check_product_id(dev); */ + /* if (err) { */ + /* LOG_ERR("Failed checking product id"); */ + /* return err; */ + /* } */ return 0; } @@ -769,12 +769,7 @@ static void trigger_handler(struct k_work *work) return; } - struct sensor_trigger trig = { - .type = SENSOR_TRIG_DATA_READY, - .chan = SENSOR_CHAN_ALL, - }; - - handler(dev, &trig); + handler(dev, data->trigger); // 2. the second lock period is used to resume the interrupt line // if data_ready_handler is non-NULL, otherwise keep it inactive @@ -1009,6 +1004,8 @@ static int pmw3610_trigger_set(const struct device *dev, data->data_ready_handler = handler; } + data->trigger = trig; + k_spin_unlock(&data->lock, key); return err; diff --git a/app/drivers/zephyr/dts/bindings/sensor/pixart,pmw3610.yml b/app/drivers/zephyr/dts/bindings/sensor/pixart,pmw3610.yml index 6af454d989e..eec36383049 100644 --- a/app/drivers/zephyr/dts/bindings/sensor/pixart,pmw3610.yml +++ b/app/drivers/zephyr/dts/bindings/sensor/pixart,pmw3610.yml @@ -14,7 +14,3 @@ properties: irq-gpios: type: phandle-array required: true - scroll-layer: - type: int - description: the momentary layer used to modifiy move to scroll - required: false diff --git a/app/dts/behaviors.dtsi b/app/dts/behaviors.dtsi index da7cd54636a..1fe1ef4e252 100644 --- a/app/dts/behaviors.dtsi +++ b/app/dts/behaviors.dtsi @@ -23,3 +23,4 @@ #include #include #include +#include diff --git a/app/dts/behaviors/trackball.dtsi b/app/dts/behaviors/trackball.dtsi new file mode 100644 index 00000000000..38fdc8347a0 --- /dev/null +++ b/app/dts/behaviors/trackball.dtsi @@ -0,0 +1,25 @@ +/ { + behaviors { + /omit-if-no-ref/ tmv: behavior_trackball_move { + compatible = "zmk,behavior-point-device-incremental"; + label = "TRACKBALL_MOVE"; + #trackball-binding-cells = <0>; + mode = "move-mode"; + flavor = "default"; + scale_mode = "multiplier"; + scale_factor = <1>; + }; + }; + + behaviors { + /omit-if-no-ref/ tsl: behavior_trackball_scroll { + compatible = "zmk,behavior-point-device-incremental"; + label = "TRACKBALL_SCROLL"; + #trackball-binding-cells = <0>; + mode = "scroll-mode"; + flavor = "default"; + scale_mode = "dividor"; + scale_factor = <5>; + }; + }; +}; diff --git a/app/dts/bindings/behaviors/zmk,behavior-point-device-incremental.yaml b/app/dts/bindings/behaviors/zmk,behavior-point-device-incremental.yaml index c9af2b01b34..cb6de1d859e 100644 --- a/app/dts/bindings/behaviors/zmk,behavior-point-device-incremental.yaml +++ b/app/dts/bindings/behaviors/zmk,behavior-point-device-incremental.yaml @@ -13,7 +13,7 @@ properties: type: int required: false const: 0 - "#pt-binding-cells": + "#trackball-binding-cells": type: int required: false const: 0 diff --git a/app/dts/bindings/zmk,keymap-trackballs.yaml b/app/dts/bindings/zmk,keymap-trackballs.yaml new file mode 100644 index 00000000000..cffb527d71d --- /dev/null +++ b/app/dts/bindings/zmk,keymap-trackballs.yaml @@ -0,0 +1,12 @@ +# Copyright (c) 2020 The ZMK Contributors +# SPDX-License-Identifier: MIT + +description: | + Collection of trackballs bound in the keymap layers, the layer key is used to switch working mode. + +compatible: "zmk,keymap-trackballs" + +properties: + trackballs: + type: phandles + required: true diff --git a/app/include/zmk/trackballs.h b/app/include/zmk/trackballs.h new file mode 100644 index 00000000000..496c226b535 --- /dev/null +++ b/app/include/zmk/trackballs.h @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#define ZMK_KEYMAP_TRACKBALLS_NODE DT_INST(0, zmk_keymap_trackballs) +#define ZMK_KEYMAP_HAS_TRACKBALLS DT_NODE_HAS_STATUS(ZMK_KEYMAP_TRACKBALLS_NODE, okay) +#define ZMK_KEYMAP_TRACKBALLS_LEN DT_PROP_LEN(ZMK_KEYMAP_TRACKBALLS_NODE, trackballs) +#define ZMK_KEYMAP_TRACKBALLS_BY_IDX(idx) DT_PHANDLE_BY_IDX(ZMK_KEYMAP_TRACKBALLS_NODE, trackballs, idx) diff --git a/app/src/behaviors/behavior_point_device_incremental.c b/app/src/behaviors/behavior_point_device_incremental.c index 5244473b80e..a9f3d267701 100644 --- a/app/src/behaviors/behavior_point_device_incremental.c +++ b/app/src/behaviors/behavior_point_device_incremental.c @@ -84,6 +84,8 @@ static int on_pd_binding_triggered(struct zmk_behavior_binding *binding, return ZMK_EVENT_RAISE(new_zmk_pd_scroll_state_changed( (struct zmk_pd_scroll_state_changed){.x=x, .y=y})); } + + return 0; } static const struct behavior_driver_api behavior_point_device_incremental_driver_api = { diff --git a/app/src/keymap.c b/app/src/keymap.c index ceaddbdde0b..e492e94cec2 100644 --- a/app/src/keymap.c +++ b/app/src/keymap.c @@ -12,6 +12,7 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #include #include #include +#include #include #include #include diff --git a/app/src/point_device/Kconfig b/app/src/point_device/Kconfig index dc1d6bb8b84..593972012e7 100644 --- a/app/src/point_device/Kconfig +++ b/app/src/point_device/Kconfig @@ -51,4 +51,16 @@ config ZMK_PD_DEDICATED_SEND_THREAD_PRIORITY endif # ZMK_PD_SEND_THREAD_DEDICATED +config ZMK_TRACKBALL_USB_POLL_INTERVAL + int "Poll interval in ms under wired connection" + default 1 + +config ZMK_TRACKBALL_BLE_POLL_INTERVAL + int "Poll interval in ms under bluetooth connection" + default 15 + +config ZMK_TRACKBALL_POLL_DURATION + int "Duration of polling after receiving an interrupt in ms" + default 300 + endif # ZMK_POINT_DEVICE diff --git a/app/src/point_device/sliders.c b/app/src/point_device/sliders.c index a77956f2053..f314e9cfd33 100644 --- a/app/src/point_device/sliders.c +++ b/app/src/point_device/sliders.c @@ -81,6 +81,6 @@ static int zmk_sliders_init(const struct device *_arg) { return 0; } -SYS_INIT(zmk_sliders_init, APPLICATION, 91); +SYS_INIT(zmk_sliders_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY); #endif diff --git a/app/src/point_device/trackballs.c b/app/src/point_device/trackballs.c index 284891fd52f..f69c5ad7976 100644 --- a/app/src/point_device/trackballs.c +++ b/app/src/point_device/trackballs.c @@ -1,9 +1,4 @@ -/* Description: -* 1. setup sliders callback -* 2. The callback will set up the correct information and raise a slider event -* 3. The processing of slider event is dispatched to listening processors, notably keymap -*/ - +#include #include #include @@ -11,50 +6,233 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); -#include -#include -#include -#include +#include +#include #include #include +#include +#include +#include + +#if ZMK_KEYMAP_HAS_TRACKBALLS + +static int max_poll_count = 0; +static int polling_interval = 0; + +struct trackballs_data_item { + uint8_t id; + const struct device *dev; + struct sensor_trigger trigger; + struct k_work poll_work; + struct k_timer poll_timer; + int polling_count; + + struct sensor_value dx; + struct sensor_value dy; +}; + +#define _TRACKBALL_ITEM(node) \ + {.dev = NULL, .polling_count=0, .trigger = {.type = SENSOR_TRIG_DATA_READY, .chan = SENSOR_CHAN_ALL}}, +#define TRACKBALL_ITEM(idx, _) \ + COND_CODE_1(DT_NODE_HAS_STATUS(ZMK_KEYMAP_TRACKBALLS_BY_IDX(idx), okay), \ + (_TRACKBALL_ITEM(ZMK_KEYMAP_TRACKBALLS_BY_IDX(idx))), ()) + +static struct trackballs_data_item trackballs[] = {UTIL_LISTIFY(ZMK_KEYMAP_TRACKBALLS_LEN, TRACKBALL_ITEM, 0)}; + +// msg structure +typedef struct { + uint8_t id; + int16_t dx; + int16_t dy; + int dt; +}__attribute__((aligned(4))) zmk_trackballs_msg; + +K_MSGQ_DEFINE(zmk_trackballs_msgq, sizeof(zmk_trackballs_msg), CONFIG_ZMK_KSCAN_EVENT_QUEUE_SIZE, 4); + +/* the msg processor */ +void zmk_trackballs_process_msgq(struct k_work *work) { + zmk_trackballs_msg msg; + while (k_msgq_get(&zmk_trackballs_msgq, &msg, K_NO_WAIT) == 0) { + LOG_INF("Process event from trackball_%d: dx: %d, dy: %d, dt: %d ms", msg.id, msg.dx, msg.dy, msg.dt); + ZMK_EVENT_RAISE(new_zmk_pd_raw_event((struct zmk_pd_raw_event){ + .type = TRACKBALL, + .id = msg.id, .dx = msg.dx, .dy = msg.dy, + .dt = msg.dt, .update_time = k_uptime_get()})); + } +} + +K_WORK_DEFINE(zmk_trackballs_msgq_work, zmk_trackballs_process_msgq); + +// polling work +static void zmk_trackballs_poll_handler(struct k_work *work) { + struct trackballs_data_item *item = CONTAINER_OF(work, struct trackballs_data_item, poll_work); + const struct device *dev = item->dev; + + // fetch dx and dy from sensor and save them into pixart_data structure + int ret = sensor_sample_fetch(dev); + if (ret < 0) { + LOG_ERR("fetch: %d", ret); + return; + } + + // get the x, y delta + ret = sensor_channel_get(dev, SENSOR_CHAN_POS_DX, &item->dx); + if (unlikely(ret < 0)) { + LOG_ERR("get dx: %d", ret); + return; + } + ret = sensor_channel_get(dev, SENSOR_CHAN_POS_DY, &item->dy); + if (unlikely(ret < 0)) { + LOG_ERR("get dy: %d", ret); + return; + } + + // put the msg + zmk_trackballs_msg msg = { + .id = item->id, + .dx = item->dx.val1, + .dy = item->dy.val1, + .dt = polling_interval + }; + + if(msg.dx != 0 || msg.dy != 0) { + LOG_INF("New position received: dx = %d, dy = %d", msg.dx, msg.dy); + k_msgq_put(&zmk_trackballs_msgq, &msg, K_NO_WAIT); + k_work_submit_to_queue(zmk_pd_work_q(), &zmk_trackballs_msgq_work); + } +} -#if ZMK_KEYMAP_HAS_SLIDERS +// trigger handler +static void zmk_trackballs_trigger_handler(const struct device *dev, const struct sensor_trigger *trig) { + LOG_INF("New interrupt received"); -/* the slider callback */ -void zmk_slider_callback(const struct device *dev, int16_t dPos, int dT) -{ - struct slider_data *slider_data = dev->data; + struct trackballs_data_item *item = CONTAINER_OF(trig, struct trackballs_data_item, trigger); - LOG_INF("Event from slider_%d: dPos: %d, dT: %d ms", slider_data->id, dPos, dT); - ZMK_EVENT_RAISE(new_zmk_pd_raw_event((struct zmk_pd_raw_event){ - .id = slider_data->id, .dx = dPos, .dy = dPos, - .dt = dT, .update_time = k_uptime_get()})); - return; + // do not resume motion interrupt by passing-in null handler + if (sensor_trigger_set(item->dev, trig, NULL) < 0) { + LOG_ERR("can't stop motion interrupt line"); + }; + + // start the polling timer (the real work now is dispatched to a timer-based polling) + LOG_INF("Polling start ..."); + k_timer_start(&item->poll_timer, K_NO_WAIT, K_MSEC(polling_interval)); +} + +// timer expiry function +void zmk_trackballs_timer_expiry(struct k_timer *timer) { + struct trackballs_data_item *item = CONTAINER_OF(timer, struct trackballs_data_item, poll_timer); + + // check whether reaching the polling count limit + if(item->polling_count < max_poll_count) { + LOG_DBG("Poll %d", item->polling_count); + // submit polling work to mouse work queue + k_work_submit_to_queue(zmk_pd_work_q(), &item->poll_work); + + // update status + item->polling_count++; + } + else { + LOG_INF("Polling end."); + // stop timer + k_timer_stop(&item->poll_timer); + } +} + +// timer stop function +void zmk_trackballs_timer_stop(struct k_timer *timer) { + struct trackballs_data_item *item = CONTAINER_OF(timer, struct trackballs_data_item, poll_timer); + + // reset polling count + item->polling_count = 0; + + // resume motion interrupt line by setting the handler to a real object + if (sensor_trigger_set(item->dev, &item->trigger, zmk_trackballs_trigger_handler) < 0) { + LOG_ERR("can't resume motion interrupt line"); + }; +} + +int zmk_trackballs_endpoint_listener(const zmk_event_t *eh) { + LOG_INF("Update polling parameters based on current endpoint"); + + // update polling parameters + switch (zmk_endpoints_selected()) { +#if IS_ENABLED(CONFIG_ZMK_USB) + case ZMK_ENDPOINT_USB: { + max_poll_count = CONFIG_ZMK_TRACKBALL_POLL_DURATION / CONFIG_ZMK_TRACKBALL_USB_POLL_INTERVAL; + polling_interval = CONFIG_ZMK_TRACKBALL_USB_POLL_INTERVAL; + break; + } +#endif /* IS_ENABLED(CONFIG_ZMK_USB) */ + +#if IS_ENABLED(CONFIG_ZMK_BLE) + case ZMK_ENDPOINT_BLE: { + max_poll_count = CONFIG_ZMK_TRACKBALL_POLL_DURATION / CONFIG_ZMK_TRACKBALL_BLE_POLL_INTERVAL; + polling_interval = CONFIG_ZMK_TRACKBALL_BLE_POLL_INTERVAL; + break; + } +#endif /* IS_ENABLED(CONFIG_ZMK_BLE) */ + default: + LOG_ERR("Unsupported endpoint"); + } + + return ZMK_EV_EVENT_BUBBLE; } -/* config the sliders: setup callback and assign the slider id */ -static void zmk_sliders_init_item(const char *node, uint8_t abs_i) { - LOG_DBG("Init %s with slider_id %d", node, abs_i); +static void zmk_trackballs_init_item(const char *node, uint8_t i, uint8_t abs_i) { + LOG_INF("Init %s at index %d with trackball id %d", node, i, abs_i); - const struct device *dev = device_get_binding(node); - if (!dev) { + // init item structure + trackballs[i].dev = device_get_binding(node); + trackballs[i].id = abs_i; + + if (!trackballs[i].dev) { LOG_WRN("Failed to find device for %s", node); return; } - kscan_slider_config(dev, zmk_slider_callback, abs_i); + // setup the timer and handler function of the polling work + k_timer_init(&trackballs[i].poll_timer, zmk_trackballs_timer_expiry, zmk_trackballs_timer_stop); + k_work_init(&trackballs[i].poll_work, zmk_trackballs_poll_handler); + + // init trigger handler + int err; + int count = 0; + do { + err = sensor_trigger_set(trackballs[i].dev, &trackballs[i].trigger, zmk_trackballs_trigger_handler); + if (err == -EBUSY) { + count++; + k_sleep(K_MSEC(20)); + } + } while (err == -EBUSY && count < 100); + + if (err) { + LOG_ERR("Cannot enable trigger"); + return; + } + + // init the polling parameters + zmk_trackballs_endpoint_listener(NULL); } -#define SLIDER_INIT(idx, _i) \ - COND_CODE_1(DT_NODE_HAS_STATUS(ZMK_KEYMAP_SLIDERS_BY_IDX(idx), okay), \ - (zmk_sliders_init_item(DT_LABEL(ZMK_KEYMAP_SLIDERS_BY_IDX(idx)), idx);), (;)) +#define _TRACKBALL_INIT(node) zmk_trackballs_init_item(DT_LABEL(node), local_index++, absolute_index++); +#define TRACKBALL_INIT(idx, _i) \ + COND_CODE_1(DT_NODE_HAS_STATUS(ZMK_KEYMAP_TRACKBALLS_BY_IDX(idx), okay), \ + (_TRACKBALL_INIT(ZMK_KEYMAP_TRACKBALLS_BY_IDX(idx))), (absolute_index++;)) + +static int zmk_trackballs_init(const struct device *_arg) { + LOG_INF("Init trackballs ..."); + int local_index = 0; + int absolute_index = 0; -static int zmk_sliders_init(const struct device *_arg) { - UTIL_LISTIFY(ZMK_KEYMAP_SLIDERS_LEN, SLIDER_INIT, 0) + UTIL_LISTIFY(ZMK_KEYMAP_TRACKBALLS_LEN, TRACKBALL_INIT, 0) return 0; } -SYS_INIT(zmk_sliders_init, APPLICATION, 91); +SYS_INIT(zmk_trackballs_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY); + +ZMK_LISTENER(zmk_trackballs, zmk_trackballs_endpoint_listener) +ZMK_SUBSCRIPTION(zmk_trackballs, zmk_endpoint_selection_changed) + #endif From bcba9886aef8c84c70e14a399b791acb4e3634ab Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Mon, 10 Oct 2022 10:48:47 +0800 Subject: [PATCH 104/157] bugfix --- app/dts/behaviors/trackball.dtsi | 2 +- app/src/behaviors/behavior_point_device_incremental.c | 7 +++++++ app/src/point_device/pd_listener.c | 2 ++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/app/dts/behaviors/trackball.dtsi b/app/dts/behaviors/trackball.dtsi index 38fdc8347a0..56b7d53868b 100644 --- a/app/dts/behaviors/trackball.dtsi +++ b/app/dts/behaviors/trackball.dtsi @@ -19,7 +19,7 @@ mode = "scroll-mode"; flavor = "default"; scale_mode = "dividor"; - scale_factor = <5>; + scale_factor = <3>; }; }; }; diff --git a/app/src/behaviors/behavior_point_device_incremental.c b/app/src/behaviors/behavior_point_device_incremental.c index a9f3d267701..11569ad6fc2 100644 --- a/app/src/behaviors/behavior_point_device_incremental.c +++ b/app/src/behaviors/behavior_point_device_incremental.c @@ -61,6 +61,7 @@ static int on_pd_binding_triggered(struct zmk_behavior_binding *binding, default: x = dx; y = dy; + break; } // @@ -73,6 +74,9 @@ static int on_pd_binding_triggered(struct zmk_behavior_binding *binding, x = x / cfg->scale_factor; y = y / cfg->scale_factor; break; + default: + LOG_ERR("unsupported scale mode %d", cfg->scale_mode); + return -ENOTSUP; } // choose the report type @@ -83,6 +87,9 @@ static int on_pd_binding_triggered(struct zmk_behavior_binding *binding, case SCROLL_MODE: return ZMK_EVENT_RAISE(new_zmk_pd_scroll_state_changed( (struct zmk_pd_scroll_state_changed){.x=x, .y=y})); + default: + LOG_ERR("unsupported work mode %d", cfg->mode); + return -ENOTSUP; } return 0; diff --git a/app/src/point_device/pd_listener.c b/app/src/point_device/pd_listener.c index e7682e18739..ebc75d31a52 100644 --- a/app/src/point_device/pd_listener.c +++ b/app/src/point_device/pd_listener.c @@ -32,11 +32,13 @@ static void pd_process_msgq(struct k_work *work) { if (msg.scroll) { LOG_INF("Send pd scroll data (%d, %d)", msg.x, msg.y); zmk_hid_mouse_scroll_set(0, 0); + zmk_hid_mouse_movement_set(0, 0); zmk_hid_mouse_scroll_update(msg.x, msg.y); } else { LOG_INF("Send pd position data (%d, %d)", msg.x, msg.y); zmk_hid_mouse_movement_set(0, 0); + zmk_hid_mouse_scroll_set(0, 0); zmk_hid_mouse_movement_update(CLAMP(msg.x, INT8_MIN, INT8_MAX), CLAMP(msg.y, INT8_MIN, INT8_MAX)); } From 9467c9186c531cb87ab3a0f94f2d26156eb7565d Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Mon, 10 Oct 2022 11:42:20 +0800 Subject: [PATCH 105/157] [trackball] optimize default parameters pmw3610 has max interrupt rate of 8 ms --- app/drivers/sensor/pixart/pmw3610/Kconfig | 12 ++++++------ app/src/point_device/Kconfig | 24 +++++++++++++++++++---- app/src/point_device/trackballs.c | 2 +- 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/app/drivers/sensor/pixart/pmw3610/Kconfig b/app/drivers/sensor/pixart/pmw3610/Kconfig index f4f1db37061..7b3d4b74641 100644 --- a/app/drivers/sensor/pixart/pmw3610/Kconfig +++ b/app/drivers/sensor/pixart/pmw3610/Kconfig @@ -30,28 +30,28 @@ config PMW3610_CPI config PMW3610_CPI_DIVIDOR int "PMW3610's default CPI dividor" - default 6 + default 4 range 1 100 help Default CPI dividor value. config PMW3610_REST1_SAMPLE_TIME_MS int "PMW3610's sample time in REST1 stage" - default 40 + default 100 range 10 2550 help Default REST1 mode sample period in millisecond config PMW3610_REST2_SAMPLE_TIME_MS int "PMW3610's sample time in REST2 stage" - default 100 + default 200 range 10 2550 help Default REST2 mode sample period in millisecond. config PMW3610_REST3_SAMPLE_TIME_MS int "PMW3610's sample time in REST3 stage" - default 500 + default 300 range 10 2550 help Default REST2 mode sample period in millisecond. @@ -66,14 +66,14 @@ config PMW3610_RUN_DOWNSHIFT_TIME_MS config PMW3610_REST1_DOWNSHIFT_TIME_MS int "PMW3610's default REST1 mode downshift time" - default 10000 + default 3000 help Default REST1 mode downshift down time in milliseconds. Time after which sensor goes from REST1 to REST2 mode. config PMW3610_REST2_DOWNSHIFT_TIME_MS int "PMW3610's default REST2 mode downshift time" - default 600000 + default 30000 help Default REST2 mode downshift down time in milliseconds. Time after which sensor goes from REST2 to REST3 mode. diff --git a/app/src/point_device/Kconfig b/app/src/point_device/Kconfig index 593972012e7..66489a5920d 100644 --- a/app/src/point_device/Kconfig +++ b/app/src/point_device/Kconfig @@ -1,8 +1,11 @@ # Copyright (c) 2021 The ZMK Contributors # SPDX-License-Identifier: MIT +DT_COMPAT_ZMK_KEYMAP_TRACKBALLS := zmk,keymap-trackballs +DT_COMPAT_ZMK_KEYMAP_SLIDERS := zmk,keymap-sliders + menuconfig ZMK_POINT_DEVICE - bool "Enable ZMK general point device support" + bool "Enable support for ZMK general point device" default n if ZMK_POINT_DEVICE @@ -51,16 +54,29 @@ config ZMK_PD_DEDICATED_SEND_THREAD_PRIORITY endif # ZMK_PD_SEND_THREAD_DEDICATED +endif # ZMK_POINT_DEVICE + +config ZMK_SLIDERS + bool + default $(dt_compat_enabled,$(DT_COMPAT_ZMK_KEYMAP_SLIDERS)) + select ZMK_POINT_DEVICE + +config ZMK_TRACKBALLS + bool + default $(dt_compat_enabled,$(DT_COMPAT_ZMK_KEYMAP_TRACKBALLS)) + select ZMK_POINT_DEVICE + +if ZMK_TRACKBALLS config ZMK_TRACKBALL_USB_POLL_INTERVAL int "Poll interval in ms under wired connection" - default 1 + default 8 config ZMK_TRACKBALL_BLE_POLL_INTERVAL int "Poll interval in ms under bluetooth connection" - default 15 + default 8 config ZMK_TRACKBALL_POLL_DURATION int "Duration of polling after receiving an interrupt in ms" default 300 -endif # ZMK_POINT_DEVICE +endif # ZMK_TRACKBALLS diff --git a/app/src/point_device/trackballs.c b/app/src/point_device/trackballs.c index f69c5ad7976..54c7ae89fa8 100644 --- a/app/src/point_device/trackballs.c +++ b/app/src/point_device/trackballs.c @@ -125,7 +125,7 @@ void zmk_trackballs_timer_expiry(struct k_timer *timer) { // check whether reaching the polling count limit if(item->polling_count < max_poll_count) { - LOG_DBG("Poll %d", item->polling_count); + LOG_INF("Poll %d", item->polling_count); // submit polling work to mouse work queue k_work_submit_to_queue(zmk_pd_work_q(), &item->poll_work); From 06a270ec90ebce2733f527c36479e609ec6623fd Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Mon, 10 Oct 2022 22:26:43 +0800 Subject: [PATCH 106/157] [point-device] init working version of directional behavior --- app/CMakeLists.txt | 1 + app/dts/behaviors/slider.dtsi | 33 +++ app/dts/behaviors/trackball.dtsi | 22 ++ ...zmk,behavior-point-device-directional.yaml | 48 ++++ ...zmk,behavior-point-device-incremental.yaml | 2 +- app/include/drivers/behavior.h | 10 +- app/include/zmk/behavior.h | 4 +- .../behavior_point_device_directional.c | 248 ++++++++++++++++++ .../behavior_point_device_incremental.c | 2 +- app/src/keymap.c | 31 ++- app/src/point_device/Kconfig | 4 + 11 files changed, 389 insertions(+), 16 deletions(-) create mode 100644 app/dts/bindings/behaviors/zmk,behavior-point-device-directional.yaml create mode 100644 app/src/behaviors/behavior_point_device_directional.c diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 334e77008c1..4d2774d8703 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -68,6 +68,7 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL) target_sources(app PRIVATE src/behaviors/behavior_mouse_move.c) target_sources(app PRIVATE src/behaviors/behavior_mouse_scroll.c) target_sources(app PRIVATE src/behaviors/behavior_point_device_incremental.c) + target_sources(app PRIVATE src/behaviors/behavior_point_device_directional.c) target_sources(app PRIVATE src/combo.c) target_sources(app PRIVATE src/behavior_queue.c) target_sources(app PRIVATE src/conditional_layer.c) diff --git a/app/dts/behaviors/slider.dtsi b/app/dts/behaviors/slider.dtsi index c5378b9e5f9..7d46e859673 100644 --- a/app/dts/behaviors/slider.dtsi +++ b/app/dts/behaviors/slider.dtsi @@ -30,5 +30,38 @@ scale_factor = <10>; }; }; + + behaviors { + /omit-if-no-ref/ skp: behavior_slider_key_press { + compatible = "zmk,behavior-point-device-directional"; + label = "SLIDER_KEY_PRESS"; + #slider-binding-cells = <2>; + mode = "eager-mode"; + flavor = "1-dim"; + step_size = <200>; + }; + }; + + behaviors { + /omit-if-no-ref/ skp2: behavior_slider_key_press2 { + compatible = "zmk,behavior-point-device-directional"; + label = "SLIDER_KEY_PRESS_2"; + #slider-binding-cells = <2>; + mode = "distance-mode"; + flavor = "1-dim"; + step_size = <2>; + }; + }; + + behaviors { + /omit-if-no-ref/ skp3: behavior_slider_key_press3 { + compatible = "zmk,behavior-point-device-directional"; + label = "SLIDER_KEY_PRESS_3"; + #slider-binding-cells = <2>; + mode = "time-mode"; + flavor = "1-dim"; + step_size = <2>; + }; + }; }; diff --git a/app/dts/behaviors/trackball.dtsi b/app/dts/behaviors/trackball.dtsi index 56b7d53868b..50135ec7a8c 100644 --- a/app/dts/behaviors/trackball.dtsi +++ b/app/dts/behaviors/trackball.dtsi @@ -22,4 +22,26 @@ scale_factor = <3>; }; }; + + behaviors { + /omit-if-no-ref/ tkp: behavior_trackball_key_press { + compatible = "zmk,behavior-point-device-directional"; + label = "TRACKBALL_KEY_PRESS"; + #trackball-binding-cells = <4>; + mode = "distance-mode"; + flavor = "2-dim"; + step_size = <100>; + }; + }; + + behaviors { + /omit-if-no-ref/ tkp2: behavior_trackball_key_press2 { + compatible = "zmk,behavior-point-device-directional"; + label = "TRACKBALL_KEY_PRESS_2"; + #trackball-binding-cells = <4>; + mode = "time-mode"; + flavor = "2-dim"; + step_size = <150>; + }; + }; }; diff --git a/app/dts/bindings/behaviors/zmk,behavior-point-device-directional.yaml b/app/dts/bindings/behaviors/zmk,behavior-point-device-directional.yaml new file mode 100644 index 00000000000..3fbf496cc7c --- /dev/null +++ b/app/dts/bindings/behaviors/zmk,behavior-point-device-directional.yaml @@ -0,0 +1,48 @@ +# Copyright (c) 2020 The ZMK Contributors +# SPDX-License-Identifier: MIT + +description: Directional behavior of point devices + +compatible: "zmk,behavior-point-device-directional" + +properties: + label: + type: string + required: true + "#slider-binding-cells": + type: int + required: false + const: 2 + "#trackball-binding-cells": + type: int + required: false + const: 4 + mode: + type: string + required: false + default: "distance-mode" + enum: + - "time-mode" + - "distance-mode" + - "eager-mode" + flavor: + type: string + required: false + default: "2-dim" + enum: + - "1-dim" + - "2-dim" + step_size: + type: int + default: 10 + description: the threshold to determine one step, unit is ms in time-mode + +slider-binding-cells: + - param1 + - param2 + +trackball-binding-cells: + - param1 + - param2 + - param3 + - param4 diff --git a/app/dts/bindings/behaviors/zmk,behavior-point-device-incremental.yaml b/app/dts/bindings/behaviors/zmk,behavior-point-device-incremental.yaml index cb6de1d859e..6d2adb8d8ef 100644 --- a/app/dts/bindings/behaviors/zmk,behavior-point-device-incremental.yaml +++ b/app/dts/bindings/behaviors/zmk,behavior-point-device-incremental.yaml @@ -1,7 +1,7 @@ # Copyright (c) 2020 The ZMK Contributors # SPDX-License-Identifier: MIT -description: Scroll behavior of point devices +description: Scroll and move behavior of point devices compatible: "zmk,behavior-point-device-incremental" diff --git a/app/include/drivers/behavior.h b/app/include/drivers/behavior.h index 1438a391141..3b92b739b2a 100644 --- a/app/include/drivers/behavior.h +++ b/app/include/drivers/behavior.h @@ -29,7 +29,7 @@ typedef int (*behavior_sensor_keymap_binding_callback_t)(struct zmk_behavior_bin const struct sensor_value value, int64_t timestamp); typedef int (*behavior_pd_keymap_binding_callback_t)(struct zmk_behavior_binding *binding, - int16_t dx, int16_t dy, int dt); + int16_t dx, int16_t dy, int dt, int64_t timestamp); enum behavior_locality { BEHAVIOR_LOCALITY_CENTRAL, @@ -194,10 +194,12 @@ static inline int z_impl_behavior_sensor_keymap_binding_triggered( * @retval Negative errno code if failure. */ __syscall int behavior_pd_keymap_binding_triggered(struct zmk_behavior_binding *binding, - int16_t dx, int16_t dy, int dt); + int16_t dx, int16_t dy, int dt, int64_t timestamp); static inline int z_impl_behavior_pd_keymap_binding_triggered(struct zmk_behavior_binding *binding, - int16_t dx, int16_t dy, int dt) { + int16_t dx, int16_t dy, int dt, + int64_t timestamp + ) { const struct device *dev = device_get_binding(binding->behavior_dev); if (dev == NULL) { @@ -210,7 +212,7 @@ static inline int z_impl_behavior_pd_keymap_binding_triggered(struct zmk_behavio return -ENOTSUP; } - return api->pd_binding_triggered(binding, dx, dy, dt); + return api->pd_binding_triggered(binding, dx, dy, dt, timestamp); } /** * @} diff --git a/app/include/zmk/behavior.h b/app/include/zmk/behavior.h index 31fb43edb35..9f591a6f787 100644 --- a/app/include/zmk/behavior.h +++ b/app/include/zmk/behavior.h @@ -13,10 +13,12 @@ struct zmk_behavior_binding { char *behavior_dev; uint32_t param1; uint32_t param2; + uint32_t param3; + uint32_t param4; }; struct zmk_behavior_binding_event { int layer; uint32_t position; int64_t timestamp; -}; \ No newline at end of file +}; diff --git a/app/src/behaviors/behavior_point_device_directional.c b/app/src/behaviors/behavior_point_device_directional.c new file mode 100644 index 00000000000..4354214bf83 --- /dev/null +++ b/app/src/behaviors/behavior_point_device_directional.c @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#define DT_DRV_COMPAT zmk_behavior_point_device_directional + +#include +#include +#include +#include + +#include +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) + +enum pd_dir_mode { + TIME_MODE, + DISTANCE_MODE, + EAGER_MODE +}; + +enum pd_dir_flavor { ONE_DIM, TWO_DIM }; + +struct behavior_point_device_directional_config { + enum pd_dir_mode mode; + enum pd_dir_flavor flavor; + int step_size; +}; + +struct behavior_point_device_directional_data { + int64_t previous_t; + int16_t dt; + int16_t dx; + int16_t dy; +}; + +enum pd_dir_type { X_PLUS = 1, X_MINUS, Y_PLUS, Y_MINUS }; + +static int behavior_point_device_directional_init(const struct device *dev) { return 0; }; + +static inline void pd_dir_reset_distance(const struct behavior_point_device_directional_config *cfg, + struct behavior_point_device_directional_data *data) +{ + if ( data->dx > cfg->step_size ) + data->dx -= cfg->step_size; + else + data->dx = 0; + + if ( data->dy > cfg->step_size ) + data->dy -= cfg->step_size; + else + data->dy = 0; +} + +static inline void pd_dir_reset(const struct behavior_point_device_directional_config *cfg, + struct behavior_point_device_directional_data *data) +{ + switch (cfg->mode) { + case EAGER_MODE: + data->dt = 0; + case TIME_MODE: + data->dx = 0; + data->dy = 0; + data->dt = 0; + break; + case DISTANCE_MODE: + pd_dir_reset_distance(cfg, data); + break; + } + +} + +static inline int pd_dir_time_mode(const struct behavior_point_device_directional_config *cfg, + struct behavior_point_device_directional_data *data, + int16_t dx, int16_t dy, int dt) +{ + // 1. accumulate + data->dx += dx; + data->dy += dy; + data->dt += dt; + + // 2. determine the direction + if( data->dt >= cfg->step_size ) { + switch( cfg->flavor ) { + case ONE_DIM: + return data->dx > 0 ? X_PLUS : X_MINUS; + break; + case TWO_DIM: + if( abs(data->dx) >= abs(data->dy) ) + return data->dx > 0 ? X_PLUS : X_MINUS; + else + return data->dy > 0 ? Y_PLUS : Y_MINUS; + break; + default: + LOG_ERR("unsupported flavor %d", cfg->flavor); + return -ENOTSUP; + } + } + + // 3. direction can't be determied in this event + return 0; +} + +static inline int pd_dir_distance_mode(const struct behavior_point_device_directional_config *cfg, + struct behavior_point_device_directional_data *data, + int16_t dx, int16_t dy) +{ + // 1. accumulate + data->dx += dx; + data->dy += dy; + + // 2. determine the direction + switch (cfg->flavor) { + case ONE_DIM: + { + if( abs(data->dx) >= cfg->step_size ) + return data->dx > 0 ? X_PLUS : X_MINUS; + } + break; + case TWO_DIM: + { + if( abs(data->dx) >= cfg->step_size + && abs(data->dx) >= abs(data->dy) ) + return data->dx > 0 ? X_PLUS : X_MINUS; + else if ( abs(data->dy) >= cfg->step_size + && abs(data->dy) >= abs(data->dx) ) + return data->dy > 0 ? Y_PLUS : Y_MINUS; + } + break; + default: + LOG_ERR("unsupported flavor %d", cfg->flavor); + return -ENOTSUP; + } + + // 3. direction can't be determied in this event + return 0; +} + +static inline int pd_dir_eager_mode(const struct behavior_point_device_directional_config *cfg, + struct behavior_point_device_directional_data *data, + int16_t dx, int16_t dy, int dt) +{ + if ( cfg->flavor != ONE_DIM ) + return -ENOTSUP; + + data->dt += dt; + if( data->dx == 0 + || (data->dx ^ dx) < 0 + || data->dt > cfg->step_size ) { + data->dx = dx; + return dx > 0 ? X_PLUS : X_MINUS; + } + + return 0; +} + +static int on_pd_binding_triggered(struct zmk_behavior_binding *binding, + int16_t dx, int16_t dy, int dt, int64_t timestamp) +{ + const struct device *dev = device_get_binding(binding->behavior_dev); + const struct behavior_point_device_directional_config *cfg = dev->config; + struct behavior_point_device_directional_data *data = dev->data; + + // event arriving outside of the time window is considered a new manueover + if( (timestamp - data->previous_t) > CONFIG_ZMK_BHV_PD_DIR_TIME_WINDOW ) { + data->dx = 0; + data->dy = 0; + data->dt = 0; + } + + // check the direction of the manueover + int index; + switch (cfg->mode) { + case TIME_MODE: + index = pd_dir_time_mode(cfg, data, dx, dy, dt); + break; + case DISTANCE_MODE: + index = pd_dir_distance_mode(cfg, data, dx, dy); + break; + case EAGER_MODE: + index = pd_dir_eager_mode(cfg, data, dx, dy, dt); + break; + default: + LOG_ERR("unsupported work mode %d", cfg->mode); + return -ENOTSUP; + } + + uint32_t keycode; + switch (index) { + case X_PLUS: + keycode = binding->param1; + break; + case X_MINUS: + keycode = binding->param2; + break; + case Y_PLUS: + keycode = binding->param3; + break; + case Y_MINUS: + keycode = binding->param4; + break; + case 0: // undetermied state, wait for next event directly + data->previous_t = timestamp; + return 0; + default: + return -ENOTSUP; + } + + // keycode determined, send press and release + LOG_DBG("SEND keycode: %d", keycode); + ZMK_EVENT_RAISE(zmk_keycode_state_changed_from_encoded(keycode, true, timestamp)); + + k_msleep(5); + + ZMK_EVENT_RAISE(zmk_keycode_state_changed_from_encoded(keycode, false, timestamp)); + + // reset state after sending + data->previous_t = timestamp; + pd_dir_reset(cfg, data); + + return 0; +} + +// +static const struct behavior_driver_api behavior_point_device_directional_driver_api = { + .pd_binding_triggered = on_pd_binding_triggered +}; + +#define KP_INST(n) \ + static struct behavior_point_device_directional_data behavior_pd_dir_data_##n; \ + static struct behavior_point_device_directional_config behavior_pd_dir_config_##n = { \ + .mode = DT_ENUM_IDX(DT_DRV_INST(n), mode), \ + .flavor = DT_ENUM_IDX(DT_DRV_INST(n), flavor), \ + .step_size = DT_INST_PROP(n, step_size), \ + }; \ + DEVICE_DT_INST_DEFINE(n, behavior_point_device_directional_init, NULL, &behavior_pd_dir_data_##n, \ + &behavior_pd_dir_config_##n, \ + APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \ + &behavior_point_device_directional_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(KP_INST) + +#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */ diff --git a/app/src/behaviors/behavior_point_device_incremental.c b/app/src/behaviors/behavior_point_device_incremental.c index 11569ad6fc2..6ed0e424721 100644 --- a/app/src/behaviors/behavior_point_device_incremental.c +++ b/app/src/behaviors/behavior_point_device_incremental.c @@ -38,7 +38,7 @@ struct behavior_point_device_incremental_config { static int behavior_point_device_incremental_init(const struct device *dev) { return 0; }; static int on_pd_binding_triggered(struct zmk_behavior_binding *binding, - int16_t dx, int16_t dy, int dt) + int16_t dx, int16_t dy, int dt, int64_t timestamp) { const struct device *dev = device_get_binding(binding->behavior_dev); const struct behavior_point_device_incremental_config *cfg = dev->config; diff --git a/app/src/keymap.c b/app/src/keymap.c index e492e94cec2..1c7337b2099 100644 --- a/app/src/keymap.c +++ b/app/src/keymap.c @@ -108,6 +108,10 @@ static struct zmk_behavior_binding zmk_slider_keymap[ZMK_KEYMAP_LAYERS_LEN] (DT_PHA_BY_IDX(layer, trackball_bindings, idx, param1))), \ .param2 = COND_CODE_0(DT_PHA_HAS_CELL_AT_IDX(layer, trackball_bindings, idx, param2), (0), \ (DT_PHA_BY_IDX(layer, trackball_bindings, idx, param2))), \ + .param3 = COND_CODE_0(DT_PHA_HAS_CELL_AT_IDX(layer, trackball_bindings, idx, param3), (0), \ + (DT_PHA_BY_IDX(layer, trackball_bindings, idx, param3))), \ + .param4 = COND_CODE_0(DT_PHA_HAS_CELL_AT_IDX(layer, trackball_bindings, idx, param4), (0), \ + (DT_PHA_BY_IDX(layer, trackball_bindings, idx, param4))), \ }, #define TRACKBALL_LAYER(node) \ @@ -130,6 +134,10 @@ static struct zmk_behavior_binding zmk_trackball_keymap[ZMK_KEYMAP_LAYERS_LEN] (DT_PHA_BY_IDX(layer, joystick_bindings, idx, param1))), \ .param2 = COND_CODE_0(DT_PHA_HAS_CELL_AT_IDX(layer, joystick_bindings, idx, param2), (0), \ (DT_PHA_BY_IDX(layer, joystick_bindings, idx, param2))), \ + .param3 = COND_CODE_0(DT_PHA_HAS_CELL_AT_IDX(layer, joystick_bindings, idx, param3), (0), \ + (DT_PHA_BY_IDX(layer, joystick_bindings, idx, param3))), \ + .param4 = COND_CODE_0(DT_PHA_HAS_CELL_AT_IDX(layer, joystick_bindings, idx, param4), (0), \ + (DT_PHA_BY_IDX(layer, joystick_bindings, idx, param4))), \ }, #define JOYSTICK_LAYER(node) \ @@ -357,7 +365,8 @@ inline static int zmk_keymap_sensor_triggered(uint8_t sensor_number, const struc #endif /* ZMK_KEYMAP_HAS_SENSORS */ #if ZMK_KEYMAP_HAS_SLIDERS -inline static int zmk_keymap_slider_triggered(uint8_t id, int16_t dx, int16_t dy, int dt) +inline static int zmk_keymap_slider_triggered(uint8_t id, int16_t dx, int16_t dy, int dt, + int64_t timestamp) { for (int layer = ZMK_KEYMAP_LAYERS_LEN - 1; layer >= _zmk_keymap_layer_default; layer--) { if (zmk_keymap_layer_active(layer) && zmk_slider_keymap[layer] != NULL) { @@ -375,7 +384,7 @@ inline static int zmk_keymap_slider_triggered(uint8_t id, int16_t dx, int16_t dy continue; } - ret = behavior_pd_keymap_binding_triggered(binding, dx, dy, dt); + ret = behavior_pd_keymap_binding_triggered(binding, dx, dy, dt, timestamp); if (ret > 0) { LOG_DBG("behavior processing to continue to next layer"); @@ -394,7 +403,8 @@ inline static int zmk_keymap_slider_triggered(uint8_t id, int16_t dx, int16_t dy #endif /* ZMK_KEYMAP_HAS_SLIDERS */ #if ZMK_KEYMAP_HAS_TRACKBALLS -inline static int zmk_keymap_trackball_triggered(uint8_t id, int16_t dx, int16_t dy, int dt) +inline static int zmk_keymap_trackball_triggered(uint8_t id, int16_t dx, int16_t dy, int dt, + int64_t timestamp) { for (int layer = ZMK_KEYMAP_LAYERS_LEN - 1; layer >= _zmk_keymap_layer_default; layer--) { if (zmk_keymap_layer_active(layer) && zmk_trackball_keymap[layer] != NULL) { @@ -412,7 +422,7 @@ inline static int zmk_keymap_trackball_triggered(uint8_t id, int16_t dx, int16_t continue; } - ret = behavior_pd_keymap_binding_triggered(binding, dx, dy, dt); + ret = behavior_pd_keymap_binding_triggered(binding, dx, dy, dt, timestamp); if (ret > 0) { LOG_DBG("behavior processing to continue to next layer"); @@ -431,7 +441,8 @@ inline static int zmk_keymap_trackball_triggered(uint8_t id, int16_t dx, int16_t #endif /* ZMK_KEYMAP_HAS_TRACKBALLS */ #if ZMK_KEYMAP_HAS_JOYSTICKS -inline static int zmk_keymap_joystick_triggered(uint8_t id, int16_t dx, int16_t dy, int dt) +inline static int zmk_keymap_joystick_triggered(uint8_t id, int16_t dx, int16_t dy, int dt, + int64_t timestamp) { for (int layer = ZMK_KEYMAP_LAYERS_LEN - 1; layer >= _zmk_keymap_layer_default; layer--) { if (zmk_keymap_layer_active(layer) && zmk_joystick_keymap[layer] != NULL) { @@ -449,7 +460,7 @@ inline static int zmk_keymap_joystick_triggered(uint8_t id, int16_t dx, int16_t continue; } - ret = behavior_pd_keymap_binding_triggered(binding, dx, dy, dt); + ret = behavior_pd_keymap_binding_triggered(binding, dx, dy, dt, timestamp); if (ret > 0) { LOG_DBG("behavior processing to continue to next layer"); @@ -487,15 +498,17 @@ int keymap_listener(const zmk_event_t *eh) { switch (pd_ev->type) { #if ZMK_KEYMAP_HAS_SLIDERS case SLIDER: - return zmk_keymap_slider_triggered(pd_ev->id, pd_ev->dx, pd_ev->dy, pd_ev->dt); + return zmk_keymap_slider_triggered(pd_ev->id, pd_ev->dx, pd_ev->dy, pd_ev->dt, pd_ev->update_time); #endif /* ZMK_KEYMAP_HAS_SLIDERS */ #if ZMK_KEYMAP_HAS_TRACKBALLS case TRACKBALL: - return zmk_keymap_trackball_triggered(pd_ev->id, pd_ev->dx, pd_ev->dy, pd_ev->dt); + return zmk_keymap_trackball_triggered(pd_ev->id, pd_ev->dx, pd_ev->dy, pd_ev->dt, + pd_ev->update_time); #endif /* ZMK_KEYMAP_HAS_TRACKBALLS */ #if ZMK_KEYMAP_HAS_JOYSTICKS case JOYSTICK: - return zmk_keymap_joystick_triggered(pd_ev->id, pd_ev->dx, pd_ev->dy, pd_ev->dt); + return zmk_keymap_joystick_triggered(pd_ev->id, pd_ev->dx, pd_ev->dy, pd_ev->dt, + pd_ev->update_time); #endif /* ZMK_KEYMAP_HAS_JOYSTICKS */ default: break; diff --git a/app/src/point_device/Kconfig b/app/src/point_device/Kconfig index 66489a5920d..9e26ec3d9f3 100644 --- a/app/src/point_device/Kconfig +++ b/app/src/point_device/Kconfig @@ -54,6 +54,10 @@ config ZMK_PD_DEDICATED_SEND_THREAD_PRIORITY endif # ZMK_PD_SEND_THREAD_DEDICATED +config ZMK_BHV_PD_DIR_TIME_WINDOW + int "Time window (ms) of directional behavior of point device" + default 300 + endif # ZMK_POINT_DEVICE config ZMK_SLIDERS From b44317f582a7f7fe0a6623f49bce463e748da287 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Fri, 14 Oct 2022 16:34:02 +0800 Subject: [PATCH 107/157] backup --- app/boards/shields/blade/Kconfig.shield | 8 ++ app/boards/shields/blade/blade.dtsi | 121 +++++++++++++++++++ app/boards/shields/blade/blade.keymap | 75 ++++++++++++ app/boards/shields/blade/blade_left.conf | 39 ++++++ app/boards/shields/blade/blade_left.overlay | 32 +++++ app/boards/shields/blade/blade_right.conf | 53 ++++++++ app/boards/shields/blade/blade_right.overlay | 83 +++++++++++++ 7 files changed, 411 insertions(+) create mode 100644 app/boards/shields/blade/Kconfig.shield create mode 100644 app/boards/shields/blade/blade.dtsi create mode 100644 app/boards/shields/blade/blade.keymap create mode 100644 app/boards/shields/blade/blade_left.conf create mode 100644 app/boards/shields/blade/blade_left.overlay create mode 100644 app/boards/shields/blade/blade_right.conf create mode 100644 app/boards/shields/blade/blade_right.overlay diff --git a/app/boards/shields/blade/Kconfig.shield b/app/boards/shields/blade/Kconfig.shield new file mode 100644 index 00000000000..9a54bef2267 --- /dev/null +++ b/app/boards/shields/blade/Kconfig.shield @@ -0,0 +1,8 @@ +# Copyright (c) 2020 The ZMK Contributors +# SPDX-License-Identifier: MIT + +config BLADE_LEFT + def_bool $(shields_list_contains,blade_left) + +config BLADE_RIGHT + def_bool $(shields_list_contains,blade_right) diff --git a/app/boards/shields/blade/blade.dtsi b/app/boards/shields/blade/blade.dtsi new file mode 100644 index 00000000000..beac60783f9 --- /dev/null +++ b/app/boards/shields/blade/blade.dtsi @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include + +/* overrides nrfmacro.dts defaults */ +&blue_led { + gpios = <&gpio1 11 GPIO_ACTIVE_HIGH>; +}; + +&vcc_power { + control-gpios = <&gpio1 10 GPIO_ACTIVE_LOW>; +}; + +&vbatt { + status = "disabled"; +}; + +/* blade-specific: common settings */ +/ { + + chosen: chosen { + zmk,kscan = &kscan; + zmk,matrix_transform = &default_transform; + }; + +/* global matrix transform */ + default_transform: keymap_transform_0 { + compatible = "zmk,matrix-transform"; + columns = <10>; + rows = <5>; +// | SW1 | SW2 | SW3 | SW4 | SW5 | | SW5 | SW4 | SW3 | SW2 | SW1 | +// | SW6 | SW7 | SW8 | SW9 | SW10 | | SW10 | SW9 | SW8 | SW7 | SW6 | +// | SW11 | SW12 | SW13 | SW14 | SW15 | | SW15 | SW14 | SW13 | SW12 | SW11 | +// | SW16 | SW17 | SW18 | | SW18 | SW17 | SW16 | +// map = < +// RC(0,0) RC(0,1) RC(0,2) RC(0,3) RC(0,4) RC(0,5) RC(0,6) RC(0,7) RC(0,8) RC(0,9) +// RC(1,0) RC(1,1) RC(1,2) RC(1,3) RC(1,4) RC(1,5) RC(1,6) RC(1,7) RC(1,8) RC(1,9) +// RC(2,0) RC(2,1) RC(2,2) RC(2,3) RC(2,4) RC(2,5) RC(2,6) RC(2,7) RC(2,8) RC(2,9) +// RC(3,0) RC(3,1) RC(3,2) RC(3,3) RC(3,4) RC(3,5) RC(3,6) RC(3,7) RC(3,8) RC(3,9) +// >; + map = < +RC(0,0) RC(0,1) RC(0,2) RC(0,3) RC(0,4) RC(0,5) RC(0,6) RC(0,7) RC(0,8) RC(0,9) +RC(1,0) RC(1,1) RC(1,2) RC(1,3) RC(1,4) RC(1,5) RC(1,6) RC(1,7) RC(1,8) RC(1,9) +RC(2,0) RC(2,1) RC(2,2) RC(2,3) RC(2,4) RC(2,5) RC(2,6) RC(2,7) RC(2,8) RC(2,9) +RC(3,0) RC(3,1) RC(3,2) RC(3,3) RC(3,4) RC(3,5) RC(3,6) RC(3,7) + RC(4,5) RC(4,6) RC(4,7) + >; + + }; + + /* Scan matrix declaration */ + kscan: kscan { + compatible = "zmk,kscan-composite"; + label = "KSCAN"; + rows = <5>; + columns = <10>; + + normal_keys { + kscan = <&kscan0>; + }; + + touch_keys { + kscan = <&kscan1>; + row-offset = <4>; + }; + }; + + /* Configure Sliders */ + sliders { + compatible = "zmk,keymap-sliders"; + sliders = <&slider_left &slider_right>; + }; + + /* Configure Encoders */ + sensors { + compatible = "zmk,keymap-sensors"; + sensors = <&left_encoder &right_encoder>; + }; + + left_encoder: encoder_left { + compatible = "alps,ec11"; + status = "disabled"; + label = "LEFT_ENCODER"; + a-gpios = <&gpio1 4 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + b-gpios = <&gpio1 6 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + resolution = <2>; + }; + + right_encoder: encoder_right { + compatible = "alps,ec11"; + status = "disabled"; + label = "RIGHT_ENCODER"; + a-gpios = <&gpio1 4 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + b-gpios = <&gpio1 6 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + resolution = <2>; + }; + +}; + +&i2c0 { + compatible = "nordic,nrf-twi"; + status = "okay"; + + slider_left: slider_left@28 { + compatible = "zmk,kscan-cap1203"; + label = "SLIDER_LEFT"; + status = "disabled"; + reg = <0x28>; + }; + + slider_right: slider_right@28 { + compatible = "zmk,kscan-cap1203"; + label = "SLIDER_RIGHT"; + status = "disabled"; + reg = <0x28>; + }; +}; diff --git a/app/boards/shields/blade/blade.keymap b/app/boards/shields/blade/blade.keymap new file mode 100644 index 00000000000..6ab99727ecb --- /dev/null +++ b/app/boards/shields/blade/blade.keymap @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include +#include + +#define DEFAULT 0 +#define QWERT 1 +#define NUM 2 +#define SYM 3 + +&mt { + flavor = "tap-preferred"; + tapping_term_ms = <200>; +}; + +/ { +#include "../key-common.dtsi" + + keymap { + compatible = "zmk,keymap"; + + default_layer { + label = "CLMK"; + bindings = < + &kp Q &kp W &kp F &kp P &kp B &kp J &kp L &kp U &kp Y &kp SQT + &kp A &mt LSFT R &mt LALT S &mt LCTL T &kp G &kp M &mt RCTL N &mt RALT E &mt RSFT I &kp O + &kp Z &kp X &kp C &kp D &kp V &kp K &kp H &cmqus &dtsmi &kp RC(B) + &kp KP_N1 &kp KP_N2 &encoder &kp TAB &kp SPACE < SYM RET &kp BSPC &encoder + >; + // sensor-bindings = <&inc_dec_kp RIGHT LEFT &inc_dec_kp UP DOWN>; + slider-bindings = <&slv &slv>; + }; + + // qwert_layer { + // label = "QWRT"; + // bindings = < + // &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P + // &kp A &mt LSFT S &mt LALT D &mt LCTL T < NUM G < NUM H &mt RCTL J &mt RALT K &mt RSFT L &kp SQT + // &kp Z &kp X &kp C &kp V &kp B &kp N &kp M &cmqus &dtsmi &kp RC(B) + // &kp KP_N1 &kp KP_N2 &encoder &kp TAB < SYM SPACE < SYM RET &kp BSPC &encoder &kp KP_N3 &kp KP_N4 + // &encoder &kp TAB < SYM SPACE < SYM RET &kp BSPC &encoder + // >; + // sensor-bindings = <&inc_dec_kp RIGHT LEFT &inc_dec_kp UP DOWN>; + // }; + + // number_layer { + // label = "NUM"; + // bindings = < + // &bt BT_NXT &prv_tab &kp UP &nxt_tab &spc1 &kp KP_PLUS &kp KP_N7 &kp KP_N8 &kp KP_N9 &kp KP_MINUS + // &bt BT_PRV &kp LEFT &kp DOWN &kp RIGHT &spc2 &kp KP_MULTIPLY &kp KP_N4 &kp KP_N5 &kp KP_N6 &kp KP_DIVIDE + // &bt BT_CLR &none &none &none &spc3 &kp KP_EQUAL &kp KP_N1 &kp KP_N2 &kp KP_N3 &kp KP_DOT + // &kp KP_N1 &kp KP_N2 &encoder &spc4 &kp BSPC &kp RET &kp KP_N0 &encoder &kp KP_N3 &kp KP_N4 + // >; + // sensor-bindings = <&inc_dec_kp PG_UP PG_DN &inc_dec_kp C_VOL_UP C_VOL_DN>; + // }; + + // symbol_layer { + // label = "SYM"; + // bindings = < + // &none &kp TILDE &kp LT &kp GT &kp PRCNT &kp AMPS &kp CARET &kp LBRC &kp RBRC &tog QWERT + // &kp GRAVE &kp EXCL &kp MINUS &kp PLUS &kp EQUAL &kp PIPE &kp COLON &kp LPAR &kp RPAR &kp AT + // &none &none &kp SLASH &kp STAR &kp BSLH &kp HASH &kp DLLR &kp LBKT &kp RBKT &none + // &none &none &encoder &none &none &none &none &encoder &none &none + // >; + // sensor-bindings = <&inc_dec_kp PG_UP PG_DN &inc_dec_kp C_VOL_UP C_VOL_DN>; + // }; + + }; +}; \ No newline at end of file diff --git a/app/boards/shields/blade/blade_left.conf b/app/boards/shields/blade/blade_left.conf new file mode 100644 index 00000000000..c5b193988d7 --- /dev/null +++ b/app/boards/shields/blade/blade_left.conf @@ -0,0 +1,39 @@ +# Copyright (c) 2020 The ZMK Contributors +# SPDX-License-Identifier: MIT + +# Set the role of the split, used in choosing the dedicated status screen +# CONFIG_NRFMACRO_SHIELD_MASTER=y + +# # Enable epaper display +# CONFIG_ZMK_DISPLAY=y +# CONFIG_NRFMACRO_EPD_DISPLAY=y +# CONFIG_NRFMACRO_EPD_ROTATE_180=y +# CONFIG_MYCHANGE_DISPLAY_UPDATE_MO_BEHAVIOR=n + +# # diplay settings (NOTE: these configurations have to be explicitly set here in shield) +# ## color scheme: black/white +# CONFIG_LVGL_USE_THEME_MONO=y +# CONFIG_LVGL_THEME_DEFAULT_COLOR_PRIMARY_BLACK=y +# CONFIG_LVGL_THEME_DEFAULT_COLOR_SECONDARY_WHITE=y +# CONFIG_LVGL_THEME_DEFAULT_COLOR_PRIMARY_RED=n +# CONFIG_LVGL_THEME_DEFAULT_COLOR_SECONDARY_RED=n +# ## dedicated work queue or not +# CONFIG_ZMK_DISPLAY_WORK_QUEUE_DEDICATED=y + +# ## custom screen or not +# ### uncommet following for custom status screen +# CONFIG_ZMK_DISPLAY_STATUS_SCREEN_CUSTOM=y +# CONFIG_LVGL_THEME_DEFAULT_FONT_NORMAL_MONTSERRAT_16=y +# CONFIG_CUSTOM_WIDGET_BATTERY_STATUS=y +# CONFIG_ZMK_WIDGET_BATTERY_STATUS=n +# CONFIG_CUSTOM_WIDGET_OUTPUT_STATUS=y +# CONFIG_ZMK_WIDGET_OUTPUT_STATUS=n +# CONFIG_CUSTOM_WIDGET_LAYER_STATUS=y +# CONFIG_ZMK_WIDGET_LAYER_STATUS=n + +# Enable encoder support +# CONFIG_EC11=y +# CONFIG_EC11_TRIGGER_GLOBAL_THREAD=y + +# set transmission power to max +CONFIG_BT_CTLR_TX_PWR_PLUS_8=y \ No newline at end of file diff --git a/app/boards/shields/blade/blade_left.overlay b/app/boards/shields/blade/blade_left.overlay new file mode 100644 index 00000000000..b99c6d75bcc --- /dev/null +++ b/app/boards/shields/blade/blade_left.overlay @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include "sweep-pro.dtsi" + +// setup display +// &chosen { +// zephyr,display = &epd; +// }; + +// &nrfmacro_spi { +// status = "okay"; +// }; + +// setup kscan pins +&kscan { + col-gpios + = <&pro_micro 15 GPIO_ACTIVE_HIGH> + , <&pro_micro 18 GPIO_ACTIVE_HIGH> + , <&pro_micro 19 GPIO_ACTIVE_HIGH> + , <&pro_micro 20 GPIO_ACTIVE_HIGH> + , <&pro_micro 21 GPIO_ACTIVE_HIGH> + ; +}; + +// enable left encoder +&left_encoder { + status = "okay"; +}; \ No newline at end of file diff --git a/app/boards/shields/blade/blade_right.conf b/app/boards/shields/blade/blade_right.conf new file mode 100644 index 00000000000..1506cb84dc0 --- /dev/null +++ b/app/boards/shields/blade/blade_right.conf @@ -0,0 +1,53 @@ +# Copyright (c) 2020 The ZMK Contributors +# SPDX-License-Identifier: MIT + +# Set the role of the split, used in choosing the dedicated status screen +# CONFIG_NRFMACRO_SHIELD_SLAVE=y + +#### Enable display #### +CONFIG_ZMK_DISPLAY=y +CONFIG_LVGL_USE_THEME_MONO=y +CONFIG_ZMK_DISPLAY_WORK_QUEUE_DEDICATED=y +CONFIG_ZMK_DISPLAY_FULL_REFRESH_PERIOD=10 + +# use LCD display +CONFIG_NRFMACRO_LCD_DISPLAY=y + +# or use EPD display +# CONFIG_NRFMACRO_EPD_DISPLAY=y +# CONFIG_NRFMACRO_EPD_ROTATE_180=y +# CONFIG_LVGL_THEME_DEFAULT_COLOR_PRIMARY_BLACK=y +# CONFIG_LVGL_THEME_DEFAULT_COLOR_SECONDARY_WHITE=y +# CONFIG_LVGL_THEME_DEFAULT_COLOR_PRIMARY_RED=n +# CONFIG_LVGL_THEME_DEFAULT_COLOR_SECONDARY_RED=n +# # custom screen or not +# ## uncommet following for custom screen +# CONFIG_ZMK_DISPLAY_STATUS_SCREEN_CUSTOM=y +# CONFIG_LVGL_THEME_DEFAULT_FONT_NORMAL_MONTSERRAT_16=y +# CONFIG_CUSTOM_WIDGET_BATTERY_STATUS=y +# CONFIG_ZMK_WIDGET_BATTERY_STATUS=n +# CONFIG_CUSTOM_WIDGET_PERIPHERAL_STATUS=y +# CONFIG_ZMK_WIDGET_PERIPHERAL_STATUS=n +# CONFIG_NRFMACRO_SCREEN_MARK_LOGO=y +# # use custom logo or not +# # CONFIG_NRFMACRO_SCREEN_STANDARD_LOGO=y +# # CONFIG_NRFMACRO_SCREEN_CUSTOM_LOGO=y + +#### Enable encoder support #### +# CONFIG_EC11=y +# CONFIG_EC11_TRIGGER_GLOBAL_THREAD=y + +# # set transmission power to max +CONFIG_BT_CTLR_TX_PWR_PLUS_8=y + +##### Slider #### +CONFIG_CAP1203_MIX_MODE=y + +##### Point Devices #### +# CONFIG_ZMK_PD_WORK_QUEUE_DEDICATED=n +# CONFIG_ZMK_PD_DEDICATED_WORK_QUEUE_PRIORITY=2 +# CONFIG_ZMK_TRACKBALL_POLL_DURATION=8 + +### logging +CONFIG_ZMK_USB_LOGGING=y +CONFIG_ZMK_LOG_LEVEL_INF=y \ No newline at end of file diff --git a/app/boards/shields/blade/blade_right.overlay b/app/boards/shields/blade/blade_right.overlay new file mode 100644 index 00000000000..4251f758e00 --- /dev/null +++ b/app/boards/shields/blade/blade_right.overlay @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include "blade.dtsi" + +/* adjust matrix transform for pepripheral split */ +&default_transform { + col-offset = <5>; +}; + +/* Define kscan pins */ +/ { + kscan0: kscan0 { + compatible = "zmk,kscan-gpio-matrix"; + label = "KSCAN0"; + + diode-direction = "col2row"; + row-gpios + = <&gpio0 2 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&gpio0 3 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&gpio0 6 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&gpio0 5 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + ; + col-gpios + = <&gpio0 26 GPIO_ACTIVE_HIGH> + , <&gpio0 28 GPIO_ACTIVE_HIGH> + , <&gpio1 13 GPIO_ACTIVE_HIGH> + , <&gpio0 30 GPIO_ACTIVE_HIGH> + , <&gpio0 31 GPIO_ACTIVE_HIGH> + ; + }; +}; + +kscan1: &slider_right {}; + + +/* Define the right slider pins */ +&i2c0 { + sda-pin = <8>; + scl-pin = <4>; +}; + +&slider_right { + status = "okay"; + int-gpios = <&gpio1 9 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; +}; + +/* Define display */ +/ { + chosen { + zephyr,display = &lcd; + }; +}; + +&nrfmacro_spi { + status = "okay"; + compatible = "nordic,nrf-spim"; + sck-pin = <32>; + mosi-pin = <22>; + cs-gpios = <&gpio1 2 GPIO_ACTIVE_HIGH>; + // Unused pin, needed for SPI definition, but not used by the display driver itself. + miso-pin = <37>; // 1.05 is not broken out on the E73 + + lcd: ls0xx@0 { + compatible = "sharp,ls0xx"; + label = "DISPLAY"; + spi-max-frequency = <2000000>; + reg = <0>; + width = <160>; + height = <68>; + // extcomin-gpios = <&pro_micro 2 GPIO_ACTIVE_HIGH>; + // extcomin-frequency = <60>; + // disp-en-gpios = <&pro_micro 3 GPIO_ACTIVE_HIGH>; + }; +}; + +/* Enable the right encoder */ +// &right_encoder { +// status = "okay"; +// }; \ No newline at end of file From 80f8a1ac18b5e4e710fd4f0b640e80c494c3196b Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Fri, 14 Oct 2022 16:44:25 +0800 Subject: [PATCH 108/157] backup --- app/boards/shields/blade/Kconfig.defconfig | 32 ++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 app/boards/shields/blade/Kconfig.defconfig diff --git a/app/boards/shields/blade/Kconfig.defconfig b/app/boards/shields/blade/Kconfig.defconfig new file mode 100644 index 00000000000..039a70f92fa --- /dev/null +++ b/app/boards/shields/blade/Kconfig.defconfig @@ -0,0 +1,32 @@ +# Copyright (c) 2020 The ZMK Contributors +# SPDX-License-Identifier: MIT + +if BLADE_RIGHT + +config ZMK_KEYBOARD_NAME + default "Blade" + +config ZMK_SPLIT_BLE_ROLE_CENTRAL + default y + +endif + +if BLADE_LEFT +config ZMK_KEYBOARD_NAME + default "Blade-slave" + +endif + +if BLADE_LEFT || BLADE_RIGHT + +config ZMK_SPLIT + default y + +config ZMK_USB + default y + +config NRFMACRO_MARKLOGO_NAME + string "Source file name of the shield mark logo" + default "sweepro-marklogo.c" +endif + From 57ae56eea3ceeefa78600f2cb56dc86d1e526cb2 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Fri, 14 Oct 2022 17:02:46 +0800 Subject: [PATCH 109/157] [nrfmacro] delete default chosen node: zmk,battery This node should be configured in shield level, so that the measuring method can be customized --- app/boards/arm/nrfmacro/nrfmacro.dts | 1 - app/boards/shields/corne-pro/corne-pro.dtsi | 1 + app/boards/shields/sweep-pro/sweep-pro.dtsi | 1 + app/boards/shields/sweepro-ce/sweepro-ce.dtsi | 1 + 4 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/boards/arm/nrfmacro/nrfmacro.dts b/app/boards/arm/nrfmacro/nrfmacro.dts index 1f22cb5e113..a94e0c2774c 100644 --- a/app/boards/arm/nrfmacro/nrfmacro.dts +++ b/app/boards/arm/nrfmacro/nrfmacro.dts @@ -17,7 +17,6 @@ zephyr,sram = &sram0; zephyr,flash = &flash0; zephyr,console = &cdc_acm_uart; - zmk,battery = &vbatt; }; leds { diff --git a/app/boards/shields/corne-pro/corne-pro.dtsi b/app/boards/shields/corne-pro/corne-pro.dtsi index d03ea978704..1f35a1b25fe 100644 --- a/app/boards/shields/corne-pro/corne-pro.dtsi +++ b/app/boards/shields/corne-pro/corne-pro.dtsi @@ -9,6 +9,7 @@ / { chosen: chosen { + zmk,battery = &vbatt; zmk,kscan = &kscan; zmk,matrix_transform = &default_transform; }; diff --git a/app/boards/shields/sweep-pro/sweep-pro.dtsi b/app/boards/shields/sweep-pro/sweep-pro.dtsi index 120d192445c..8c5305f930c 100644 --- a/app/boards/shields/sweep-pro/sweep-pro.dtsi +++ b/app/boards/shields/sweep-pro/sweep-pro.dtsi @@ -9,6 +9,7 @@ / { chosen: chosen { + zmk,battery = &vbatt; zmk,kscan = &kscan; zmk,matrix_transform = &default_transform; zephyr,display = &shield_epd; diff --git a/app/boards/shields/sweepro-ce/sweepro-ce.dtsi b/app/boards/shields/sweepro-ce/sweepro-ce.dtsi index 0dc0fa1e452..3f21c69899c 100644 --- a/app/boards/shields/sweepro-ce/sweepro-ce.dtsi +++ b/app/boards/shields/sweepro-ce/sweepro-ce.dtsi @@ -9,6 +9,7 @@ / { chosen: chosen { + zmk,battery = &vbatt; zmk,kscan = &kscan; zmk,matrix_transform = &default_transform; zephyr,display = &shield_epd; From 912894ab951a5f6e80b2259d059606887f74c0fd Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Fri, 14 Oct 2022 17:17:30 +0800 Subject: [PATCH 110/157] [nrfmacro] add an alias label 'vcc_ctrl' to ext-power node --- app/boards/arm/nrfmacro/nrfmacro.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/boards/arm/nrfmacro/nrfmacro.dts b/app/boards/arm/nrfmacro/nrfmacro.dts index a94e0c2774c..74f50572095 100644 --- a/app/boards/arm/nrfmacro/nrfmacro.dts +++ b/app/boards/arm/nrfmacro/nrfmacro.dts @@ -27,7 +27,7 @@ }; }; - ext-power { + vcc_ctrl: ext-power { compatible = "zmk,ext-power-generic"; label = "EXT_POWER"; control-gpios = <&gpio1 9 GPIO_ACTIVE_LOW>; From 11e4cd9f7885244cd85f1bbae61ce832d41be5ea Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Fri, 14 Oct 2022 17:26:04 +0800 Subject: [PATCH 111/157] [blade] init working shield with touch-bar --- app/boards/shields/blade/blade.dtsi | 2 +- app/boards/shields/blade/blade.keymap | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/boards/shields/blade/blade.dtsi b/app/boards/shields/blade/blade.dtsi index beac60783f9..700583c0ce1 100644 --- a/app/boards/shields/blade/blade.dtsi +++ b/app/boards/shields/blade/blade.dtsi @@ -11,7 +11,7 @@ gpios = <&gpio1 11 GPIO_ACTIVE_HIGH>; }; -&vcc_power { +&vcc_ctrl { control-gpios = <&gpio1 10 GPIO_ACTIVE_LOW>; }; diff --git a/app/boards/shields/blade/blade.keymap b/app/boards/shields/blade/blade.keymap index 6ab99727ecb..67e5cdab8d8 100644 --- a/app/boards/shields/blade/blade.keymap +++ b/app/boards/shields/blade/blade.keymap @@ -32,6 +32,7 @@ &kp A &mt LSFT R &mt LALT S &mt LCTL T &kp G &kp M &mt RCTL N &mt RALT E &mt RSFT I &kp O &kp Z &kp X &kp C &kp D &kp V &kp K &kp H &cmqus &dtsmi &kp RC(B) &kp KP_N1 &kp KP_N2 &encoder &kp TAB &kp SPACE < SYM RET &kp BSPC &encoder + &kp A &kp B &kp C >; // sensor-bindings = <&inc_dec_kp RIGHT LEFT &inc_dec_kp UP DOWN>; slider-bindings = <&slv &slv>; From da73a7c04a4d4900d7ceb53b45a18a871c89c471 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Sat, 15 Oct 2022 08:22:50 +0800 Subject: [PATCH 112/157] backup --- app/boards/shields/blade/blade.dtsi | 8 + app/boards/shields/blade/blade_right.conf | 5 +- app/boards/shields/blade/blade_right.overlay | 4 + app/drivers/kscan/kscan_cap1203.c | 4 +- app/drivers/sensor/CMakeLists.txt | 3 - app/drivers/sensor/Kconfig | 1 - app/drivers/sensor/battery/CMakeLists.txt | 3 +- app/drivers/sensor/battery/Kconfig | 10 + app/drivers/sensor/battery/battery_max17048.c | 202 ++++++++++++++++++ app/drivers/sensor/max17048/CMakeLists.txt | 9 - app/drivers/sensor/max17048/Kconfig | 9 - app/drivers/sensor/max17048/max17048.c | 196 ----------------- app/drivers/sensor/max17048/max17048.h | 42 ---- .../dts/bindings/kscan/zmk,kscan-cap1203.yaml | 4 +- .../bindings/sensor/zmk,battery-max17048.yaml | 8 + 15 files changed, 242 insertions(+), 266 deletions(-) create mode 100644 app/drivers/sensor/battery/battery_max17048.c delete mode 100644 app/drivers/sensor/max17048/CMakeLists.txt delete mode 100644 app/drivers/sensor/max17048/Kconfig delete mode 100644 app/drivers/sensor/max17048/max17048.c delete mode 100644 app/drivers/sensor/max17048/max17048.h create mode 100644 app/drivers/zephyr/dts/bindings/sensor/zmk,battery-max17048.yaml diff --git a/app/boards/shields/blade/blade.dtsi b/app/boards/shields/blade/blade.dtsi index 700583c0ce1..2761a65c13a 100644 --- a/app/boards/shields/blade/blade.dtsi +++ b/app/boards/shields/blade/blade.dtsi @@ -25,6 +25,7 @@ chosen: chosen { zmk,kscan = &kscan; zmk,matrix_transform = &default_transform; + zmk,battery = &fuel_gauge; }; /* global matrix transform */ @@ -118,4 +119,11 @@ RC(3,0) RC(3,1) RC(3,2) RC(3,3) RC(3,4) RC(3,5) RC(3,6) RC(3,7) status = "disabled"; reg = <0x28>; }; + + fuel_gauge: fuel_gauge@36 { + compatible = "zmk,battery-max17048"; + label = "BATTERY_FUELGAUGE"; + status = "disabled"; + reg = <0x36>; + }; }; diff --git a/app/boards/shields/blade/blade_right.conf b/app/boards/shields/blade/blade_right.conf index 1506cb84dc0..497115cc01f 100644 --- a/app/boards/shields/blade/blade_right.conf +++ b/app/boards/shields/blade/blade_right.conf @@ -50,4 +50,7 @@ CONFIG_CAP1203_MIX_MODE=y ### logging CONFIG_ZMK_USB_LOGGING=y -CONFIG_ZMK_LOG_LEVEL_INF=y \ No newline at end of file +# CONFIG_ZMK_LOG_LEVEL_INF=y +CONFIG_ZMK_LOG_LEVEL_DBG=y + +CONFIG_ZMK_BATTERY_REPORT_INTERVAL=10 diff --git a/app/boards/shields/blade/blade_right.overlay b/app/boards/shields/blade/blade_right.overlay index 4251f758e00..f00f813e457 100644 --- a/app/boards/shields/blade/blade_right.overlay +++ b/app/boards/shields/blade/blade_right.overlay @@ -48,6 +48,10 @@ kscan1: &slider_right {}; int-gpios = <&gpio1 9 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; }; +&fuel_gauge { + status = "okay"; +}; + /* Define display */ / { chosen { diff --git a/app/drivers/kscan/kscan_cap1203.c b/app/drivers/kscan/kscan_cap1203.c index f1a785cf514..d0d8e28deae 100644 --- a/app/drivers/kscan/kscan_cap1203.c +++ b/app/drivers/kscan/kscan_cap1203.c @@ -1,7 +1,7 @@ /* - * Copyright (c) 2022 Keiya Nobuta + * Copyright (c) 2022 Yong Zhou * - * SPDX-License-Identifier: Apache-2.0 + * SPDX-License-Identifier: MIT */ #define DT_DRV_COMPAT zmk_kscan_cap1203 diff --git a/app/drivers/sensor/CMakeLists.txt b/app/drivers/sensor/CMakeLists.txt index 44aeba80632..d045db840bf 100644 --- a/app/drivers/sensor/CMakeLists.txt +++ b/app/drivers/sensor/CMakeLists.txt @@ -10,6 +10,3 @@ add_subdirectory_ifdef(CONFIG_PINNACLE cirque_trackpad) add_subdirectory_ifdef(CONFIG_PMW3360 pixart/pmw3360) add_subdirectory_ifdef(CONFIG_PAW3395 pixart/paw3395) add_subdirectory_ifdef(CONFIG_PMW3610 pixart/pmw3610) - -# fuel gauge: max17048 -add_subdirectory_ifdef(CONFIG_MAX17048 max17048) diff --git a/app/drivers/sensor/Kconfig b/app/drivers/sensor/Kconfig index b5a978bb8c5..c82ef2cd6dd 100644 --- a/app/drivers/sensor/Kconfig +++ b/app/drivers/sensor/Kconfig @@ -9,4 +9,3 @@ rsource "pmw33xx/Kconfig" rsource "pixart/pmw3360/Kconfig" rsource "pixart/paw3395/Kconfig" rsource "pixart/pmw3610/Kconfig" -rsource "max17048/Kconfig" diff --git a/app/drivers/sensor/battery/CMakeLists.txt b/app/drivers/sensor/battery/CMakeLists.txt index 1203e53a6be..5281981fdea 100644 --- a/app/drivers/sensor/battery/CMakeLists.txt +++ b/app/drivers/sensor/battery/CMakeLists.txt @@ -7,4 +7,5 @@ zephyr_library() zephyr_library_sources(battery_common.c) zephyr_library_sources_ifdef(CONFIG_ZMK_BATTERY_NRF_VDDH battery_nrf_vddh.c) -zephyr_library_sources_ifdef(CONFIG_ZMK_BATTERY_VOLTAGE_DIVIDER battery_voltage_divider.c) \ No newline at end of file +zephyr_library_sources_ifdef(CONFIG_ZMK_BATTERY_VOLTAGE_DIVIDER battery_voltage_divider.c) +zephyr_library_sources_ifdef(CONFIG_ZMK_BATTERY_MAX17048 battery_max17048.c) diff --git a/app/drivers/sensor/battery/Kconfig b/app/drivers/sensor/battery/Kconfig index d7c82bb0429..ca7fa3abecf 100644 --- a/app/drivers/sensor/battery/Kconfig +++ b/app/drivers/sensor/battery/Kconfig @@ -3,6 +3,7 @@ DT_COMPAT_ZMK_BATTERY_NRF_VDDH := zmk,battery-nrf-vddh DT_COMPAT_ZMK_BATTERY_VOLTAGE_DIVIDER := zmk,battery-voltage-divider +DT_COMPAT_ZMK_BATTERY_MAX17048 := zmk,battery-max17048 config ZMK_BATTERY bool "ZMK battery monitoring" @@ -24,3 +25,12 @@ config ZMK_BATTERY_VOLTAGE_DIVIDER select ZMK_BATTERY help Enable ZMK battery voltage divider driver for battery monitoring. + +config ZMK_BATTERY_MAX17048 + bool "MAX17048 Fuel Gauge" + default $(dt_compat_enabled,$(DT_COMPAT_ZMK_BATTERY_MAX17048)) + select I2C + select ZMK_BATTERY + help + Enable I2C-based driver for MAX17048/9 Fuel Gauge. Supports measuring + battery voltage and state-of-charge. \ No newline at end of file diff --git a/app/drivers/sensor/battery/battery_max17048.c b/app/drivers/sensor/battery/battery_max17048.c new file mode 100644 index 00000000000..143b26dd153 --- /dev/null +++ b/app/drivers/sensor/battery/battery_max17048.c @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#define DT_DRV_COMPAT zmk_battery_max17048 + +#include +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(max17048, CONFIG_ZMK_LOG_LEVEL); + +#define REG_VCELL 0x02 +#define REG_STATE_OF_CHARGE 0x04 +#define REG_MODE 0x06 +#define REG_VERSION 0x08 +#define REG_HIBERNATE 0x0A +#define REG_CONFIG 0x0C +#define REG_VALERT 0x14 +#define REG_CHARGE_RATE 0x16 +#define REG_VRESET 0x18 +#define REG_STATUS 0x1A + +struct max17048_config { + struct i2c_dt_spec i2c; + const char *i2c_bus_name; +}; + +struct max17048_data { + const struct device *dev; + uint16_t state_of_charge; + uint16_t vcell; +}; + +static int max17048_read_register(const struct device *dev, uint8_t reg, uint16_t *value) { + const struct max17048_config *config = dev->config; + + uint8_t i2c_data[2]; + int ret = i2c_burst_read_dt(&config->i2c, reg, i2c_data, 2); + if (ret != 0) { + LOG_DBG("Unable to read register"); + return ret; + } + + *value = (i2c_data[1] << 8) | i2c_data[0]; + + return 0; +} + +static int max17048_write_register(const struct device *dev, uint8_t reg, uint16_t value) { + const struct max17048_config *config = dev->config; + + uint8_t buf[2]; + sys_put_le16(value, buf); + + return i2c_burst_write_dt(&config->i2c, reg, buf, 2); +} + +static int max17048_set_rcomp_value(const struct device *dev, uint8_t rcomp_value) { + uint16_t tmp = 0; + int err = max17048_read_register(dev, REG_CONFIG, &tmp); + if (err != 0) { + return err; + } + + tmp = ((uint16_t)rcomp_value << 8) | (tmp & 0xFF); + err = max17048_write_register(dev, REG_CONFIG, tmp); + if (err != 0) { + return err; + } + + LOG_DBG("set RCOMP to %d", rcomp_value); + return 0; +} + +static int max17048_set_sleep_enabled(const struct device *dev, bool sleep) { + uint16_t tmp = 0; + int err = max17048_read_register(dev, REG_CONFIG, &tmp); + if (err != 0) { + return err; + } + + if (sleep) { + tmp |= 0x80; + } else { + tmp &= ~0x0080; + } + + err = max17048_write_register(dev, REG_CONFIG, tmp); + if (err != 0) { + return err; + } + + LOG_DBG("sleep mode %s", sleep ? "enabled" : "disabled"); + return 0; +} + +static int max17048_sample_fetch(const struct device *dev, enum sensor_channel chan) { + struct max17048_data *const data = dev->data; + + if (chan != SENSOR_CHAN_GAUGE_VOLTAGE && chan != SENSOR_CHAN_GAUGE_STATE_OF_CHARGE) { + LOG_DBG("unsupported channel %d", chan); + return -ENOTSUP; + } + + int err = max17048_read_register(dev, REG_STATE_OF_CHARGE, &data->state_of_charge); + if (err != 0) { + LOG_WRN("failed to read state-of-charge: %d", err); + return err; + } + + err = max17048_read_register(dev, REG_VCELL, &data->vcell); + if (err != 0) { + LOG_WRN("failed to read vcell: %d", err); + return err; + } + + LOG_DBG("read values: soc=%d, vcell=%d", data->state_of_charge, data->vcell); + + return 0; +} + +static int max17048_channel_get(const struct device *dev, enum sensor_channel chan, + struct sensor_value *val) { + struct max17048_data *const data = dev->data; + unsigned int tmp = 0; + + switch (chan) { + case SENSOR_CHAN_GAUGE_VOLTAGE: + // 1250 / 16 = 78.125 + tmp = data->vcell * 1250 / 16; + val->val1 = tmp / 1000000; // in V + val->val2 = tmp % 1000000; // in uV + break; + + case SENSOR_CHAN_GAUGE_STATE_OF_CHARGE: + val->val1 = (data->state_of_charge >> 8); // in 1% + val->val2 = (data->state_of_charge & 0xFF) * 1000 / 256; // in 0.001% + break; + + default: + return -ENOTSUP; + } + + return 0; +} + +static int max17048_init(const struct device *dev) { + struct max17048_data *data = dev->data; + const struct max17048_config *config = dev->config; + + // check readiness of i2c bus + if (!device_is_ready(config->i2c.bus)) { + LOG_ERR("I2C controller (%s) not ready", config->i2c_bus_name); + return -ENODEV; + } + + // init data structure + data->dev = dev; + + // check version register + uint16_t ic_version = 0; + int err = max17048_read_register(dev, REG_VERSION, &ic_version); + if (err != 0) { + LOG_WRN("could not get IC version!"); + return err; + } + + // bring the device out of sleep + max17048_set_sleep_enabled(dev, false); + + // set the default rcomp value -- 0x97, as stated in the datasheet + max17048_set_rcomp_value(dev, 0x97); + + LOG_INF("device initialised at 0x%x (i2c=%s) (version %d)", config->i2c.addr, + config->i2c_bus_name, ic_version); + + return 0; +} + +static const struct sensor_driver_api max17048_api = {.sample_fetch = max17048_sample_fetch, + .channel_get = max17048_channel_get}; + +#define MAX17048_INIT(inst) \ + static struct max17048_config max17048_config_##inst = { \ + .i2c_bus_name = DT_INST_BUS_LABEL(inst), \ + .i2c = I2C_DT_SPEC_INST_GET(inst), \ + }; \ + \ + static struct max17048_data max17048_data_##inst = {}; \ + \ + DEVICE_DT_INST_DEFINE(inst, max17048_init, NULL, &max17048_data_##inst, \ + &max17048_config_##inst, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \ + &max17048_api); + +DT_INST_FOREACH_STATUS_OKAY(MAX17048_INIT) diff --git a/app/drivers/sensor/max17048/CMakeLists.txt b/app/drivers/sensor/max17048/CMakeLists.txt deleted file mode 100644 index e895fa11fb8..00000000000 --- a/app/drivers/sensor/max17048/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2022 The ZMK Contributors -# SPDX-License-Identifier: MIT - -zephyr_include_directories(.) - -zephyr_library() - -zephyr_library_sources_ifdef(CONFIG_MAX17048 max17048.c) -zephyr_library_sources_ifndef(CONFIG_MAX17048 ${ZEPHYR_BASE}/misc/empty_file.c) diff --git a/app/drivers/sensor/max17048/Kconfig b/app/drivers/sensor/max17048/Kconfig deleted file mode 100644 index 6d3de88b9bd..00000000000 --- a/app/drivers/sensor/max17048/Kconfig +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2022 The ZMK Contributors -# SPDX-License-Identifier: MIT - -config MAX17048 - bool "MAX17048 Fuel Gauge" - depends on I2C - help - Enable I2C-based driver for MAX17048/9 Fuel Gauge. Supports measuring - battery voltage and state-of-charge. diff --git a/app/drivers/sensor/max17048/max17048.c b/app/drivers/sensor/max17048/max17048.c deleted file mode 100644 index 6540873c2b5..00000000000 --- a/app/drivers/sensor/max17048/max17048.c +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright (c) 2022 The ZMK Contributors - * - * SPDX-License-Identifier: MIT - */ - -#define DT_DRV_COMPAT maxim_max17048 - -#include -#include -#include -#include -#include -#include -#include - -#include "max17048.h" - -LOG_MODULE_REGISTER(MAX17048, CONFIG_SENSOR_LOG_LEVEL); - -static int read_register(const struct device *dev, uint8_t reg, uint16_t *value) { - - if (k_is_in_isr()) { - return -EWOULDBLOCK; - } - - struct max17048_drv_data *const drv_data = dev->data; - uint16_t dev_addr = ((struct max17048_config *)dev->config)->device_addr; - - uint16_t data = 0; - int ret = i2c_burst_read(drv_data->i2c, dev_addr, reg, (uint8_t *)&data, sizeof(data)); - if (ret != 0) { - LOG_DBG("i2c_write_read FAIL %d\n", ret); - return ret; - } - - *value = sys_le16_to_cpu(data); - return 0; -} - -static int write_register(const struct device *dev, uint8_t reg, uint16_t value) { - - if (k_is_in_isr()) { - return -EWOULDBLOCK; - } - - struct max17048_drv_data *const drv_data = dev->data; - - uint16_t data = sys_cpu_to_le16(value); - uint16_t dev_addr = ((struct max17048_config *)dev->config)->device_addr; - - return i2c_burst_write(drv_data->i2c, dev_addr, reg, (uint8_t *)&data, sizeof(data)); -} - -static int set_rcomp_value(const struct device *dev, uint8_t rcomp_value) { - - uint16_t tmp = 0; - int err = read_register(dev, REG_CONFIG, &tmp); - if (err != 0) { - return err; - } - - tmp = ((uint16_t)rcomp_value << 8) | (tmp & 0xFF); - err = write_register(dev, REG_CONFIG, tmp); - if (err != 0) { - return err; - } - - LOG_DBG("set RCOMP to %d", rcomp_value); - return 0; -} - -static int set_sleep_enabled(const struct device *dev, bool sleep) { - uint16_t tmp = 0; - int err = read_register(dev, REG_CONFIG, &tmp); - if (err != 0) { - return err; - } - - if (sleep) { - tmp |= 0x80; - } else { - tmp &= ~0x0080; - } - - err = write_register(dev, REG_CONFIG, tmp); - if (err != 0) { - return err; - } - - LOG_DBG("sleep mode %s", sleep ? "enabled" : "disabled"); - return 0; -} - -static int max17048_sample_fetch(const struct device *dev, enum sensor_channel chan) { - struct max17048_drv_data *const data = dev->data; - - if (chan != SENSOR_CHAN_GAUGE_VOLTAGE && chan != SENSOR_CHAN_GAUGE_STATE_OF_CHARGE) { - LOG_DBG("unsupported channel %d", chan); - return -ENOTSUP; - } - - int err = read_register(dev, REG_STATE_OF_CHARGE, &data->raw_state_of_charge); - if (err != 0) { - LOG_WRN("failed to read state-of-charge: %d", err); - return err; - } - - err = read_register(dev, REG_VCELL, &data->raw_vcell); - if (err != 0) { - LOG_WRN("failed to read vcell: %d", err); - return err; - } - - LOG_DBG("read values: soc=%d, vcell=%d", data->raw_state_of_charge, data->raw_vcell); - - return 0; -} - -static int max17048_channel_get(const struct device *dev, enum sensor_channel chan, - struct sensor_value *val) { - - struct max17048_drv_data *const data = dev->data; - unsigned int tmp = 0; - - switch (chan) { - case SENSOR_CHAN_GAUGE_VOLTAGE: - // 1250 / 16 = 78.125 - tmp = data->raw_vcell * 1250 / 16; - val->val1 = tmp / 1000000; - val->val2 = tmp % 1000000; - break; - - case SENSOR_CHAN_GAUGE_STATE_OF_CHARGE: - val->val1 = (data->raw_state_of_charge >> 8); - val->val2 = (data->raw_state_of_charge & 0xFF) * 1000000 / 256; - break; - - default: - return -ENOTSUP; - } - - return 0; -} - -static int max17048_init(const struct device *dev) { - struct max17048_drv_data *driver_data = dev->data; - const struct max17048_config *driver_config = dev->config; - - driver_data->i2c = device_get_binding((char *)driver_config->i2c_device_name); - if (!driver_data->i2c) { - LOG_DBG("Unable to get i2c device"); - return -ENODEV; - } - - if (!device_is_ready(driver_data->i2c)) { - LOG_WRN("i2c bus not ready!"); - return -EINVAL; - } - - uint16_t ic_version = 0; - int err = read_register(dev, REG_VERSION, &ic_version); - if (err != 0) { - LOG_WRN("could not get IC version!"); - return err; - } - - // bring the device out of sleep - set_sleep_enabled(dev, false); - - // set the default rcomp value -- 0x97, as stated in the datasheet - set_rcomp_value(dev, 0x97); - - LOG_INF("device initialised at 0x%x (i2c=%s) (version %d)", driver_config->device_addr, - driver_config->i2c_device_name, ic_version); - - return 0; -} - -static const struct sensor_driver_api max17048_api_table = {.sample_fetch = max17048_sample_fetch, - .channel_get = max17048_channel_get}; - -#define MAX17048_INIT(inst) \ - static struct max17048_config max17048_##inst##_config = { \ - .i2c_device_name = DT_INST_BUS_LABEL(inst), \ - .device_addr = DT_INST_REG_ADDR(inst), \ - }; \ - \ - static struct max17048_drv_data max17048_##inst##_drvdata = {}; \ - \ - /* This has to init after SPI master */ \ - DEVICE_DT_INST_DEFINE(inst, max17048_init, NULL, &max17048_##inst##_drvdata, \ - &max17048_##inst##_config, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \ - &max17048_api_table); - -DT_INST_FOREACH_STATUS_OKAY(MAX17048_INIT) diff --git a/app/drivers/sensor/max17048/max17048.h b/app/drivers/sensor/max17048/max17048.h deleted file mode 100644 index db728994016..00000000000 --- a/app/drivers/sensor/max17048/max17048.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2022 The ZMK Contributors - * - * SPDX-License-Identifier: MIT - */ - -#pragma once - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define REG_VCELL 0x02 -#define REG_STATE_OF_CHARGE 0x04 -#define REG_MODE 0x06 -#define REG_VERSION 0x08 -#define REG_HIBERNATE 0x0A -#define REG_CONFIG 0x0C -#define REG_VALERT 0x14 -#define REG_CHARGE_RATE 0x16 -#define REG_VRESET 0x18 -#define REG_STATUS 0x1A - -struct max17048_config { - const char *i2c_device_name; - uint16_t device_addr; -}; - -struct max17048_drv_data { - const struct device *i2c; - - uint16_t raw_state_of_charge; - uint16_t raw_charge_rate; - uint16_t raw_vcell; -}; - -#ifdef __cplusplus -} -#endif diff --git a/app/drivers/zephyr/dts/bindings/kscan/zmk,kscan-cap1203.yaml b/app/drivers/zephyr/dts/bindings/kscan/zmk,kscan-cap1203.yaml index fb974866aea..723c9732afa 100644 --- a/app/drivers/zephyr/dts/bindings/kscan/zmk,kscan-cap1203.yaml +++ b/app/drivers/zephyr/dts/bindings/kscan/zmk,kscan-cap1203.yaml @@ -1,5 +1,5 @@ -# Copyright (c) 2022 Keiya Nobuta -# SPDX-License-Identifier: Apache-2.0 +# Copyright (c) 2022 Yong Zhou +# SPDX-License-Identifier: MIT description: CAP1203 3-channel capacitive touch sensor diff --git a/app/drivers/zephyr/dts/bindings/sensor/zmk,battery-max17048.yaml b/app/drivers/zephyr/dts/bindings/sensor/zmk,battery-max17048.yaml new file mode 100644 index 00000000000..43c10299bb1 --- /dev/null +++ b/app/drivers/zephyr/dts/bindings/sensor/zmk,battery-max17048.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2022 Yong Zhou +# SPDX-License-Identifier: MIT + +description: Maxim MAX17048 Fuel Gauge + +compatible: "zmk,battery-max17048" + +include: [i2c-device.yaml] From c8e67170ff8951f70109a242f7a9f6afba09eb2f Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Sat, 15 Oct 2022 11:23:55 +0800 Subject: [PATCH 113/157] [max17048] init working version --- app/boards/shields/blade/blade_right.conf | 4 +- app/drivers/sensor/battery/Kconfig | 6 ++- app/drivers/sensor/battery/battery_max17048.c | 41 +++++++++++++------ app/src/battery.c | 21 +++++++--- 4 files changed, 51 insertions(+), 21 deletions(-) diff --git a/app/boards/shields/blade/blade_right.conf b/app/boards/shields/blade/blade_right.conf index 497115cc01f..56d52d0d7a4 100644 --- a/app/boards/shields/blade/blade_right.conf +++ b/app/boards/shields/blade/blade_right.conf @@ -50,7 +50,7 @@ CONFIG_CAP1203_MIX_MODE=y ### logging CONFIG_ZMK_USB_LOGGING=y -# CONFIG_ZMK_LOG_LEVEL_INF=y -CONFIG_ZMK_LOG_LEVEL_DBG=y +CONFIG_ZMK_LOG_LEVEL_INF=y +# CONFIG_BATTERY_LOG_LEVEL_DBG=y CONFIG_ZMK_BATTERY_REPORT_INTERVAL=10 diff --git a/app/drivers/sensor/battery/Kconfig b/app/drivers/sensor/battery/Kconfig index ca7fa3abecf..8978f131f91 100644 --- a/app/drivers/sensor/battery/Kconfig +++ b/app/drivers/sensor/battery/Kconfig @@ -33,4 +33,8 @@ config ZMK_BATTERY_MAX17048 select ZMK_BATTERY help Enable I2C-based driver for MAX17048/9 Fuel Gauge. Supports measuring - battery voltage and state-of-charge. \ No newline at end of file + battery voltage and state-of-charge. + +module = BATTERY +module-str = battery +source "${ZEPHYR_BASE}/subsys/logging/Kconfig.template.log_config" diff --git a/app/drivers/sensor/battery/battery_max17048.c b/app/drivers/sensor/battery/battery_max17048.c index 143b26dd153..bcbbb0d5560 100644 --- a/app/drivers/sensor/battery/battery_max17048.c +++ b/app/drivers/sensor/battery/battery_max17048.c @@ -16,16 +16,17 @@ LOG_MODULE_REGISTER(max17048, CONFIG_ZMK_LOG_LEVEL); -#define REG_VCELL 0x02 -#define REG_STATE_OF_CHARGE 0x04 +#define REG_VCELL 0x02 // 78.125 uV +#define REG_SOC 0x04 // 1%/256 #define REG_MODE 0x06 #define REG_VERSION 0x08 -#define REG_HIBERNATE 0x0A +#define REG_HIBRT 0x0A #define REG_CONFIG 0x0C #define REG_VALERT 0x14 -#define REG_CHARGE_RATE 0x16 -#define REG_VRESET 0x18 -#define REG_STATUS 0x1A +#define REG_CRATE 0x16 // 0.208%/hr +#define REG_VRESET 0x18 // vcell threshold +#define REG_STATUS 0x1A // ov, uv, soc change, soc low, reset alert +#define REG_CMD 0xFE struct max17048_config { struct i2c_dt_spec i2c; @@ -48,7 +49,7 @@ static int max17048_read_register(const struct device *dev, uint8_t reg, uint16_ return ret; } - *value = (i2c_data[1] << 8) | i2c_data[0]; + *value = sys_get_be16(i2c_data); return 0; } @@ -57,7 +58,7 @@ static int max17048_write_register(const struct device *dev, uint8_t reg, uint16 const struct max17048_config *config = dev->config; uint8_t buf[2]; - sys_put_le16(value, buf); + sys_put_be16(value, buf); return i2c_burst_write_dt(&config->i2c, reg, buf, 2); } @@ -104,12 +105,14 @@ static int max17048_set_sleep_enabled(const struct device *dev, bool sleep) { static int max17048_sample_fetch(const struct device *dev, enum sensor_channel chan) { struct max17048_data *const data = dev->data; - if (chan != SENSOR_CHAN_GAUGE_VOLTAGE && chan != SENSOR_CHAN_GAUGE_STATE_OF_CHARGE) { + if (chan != SENSOR_CHAN_ALL + && chan != SENSOR_CHAN_GAUGE_VOLTAGE + && chan != SENSOR_CHAN_GAUGE_STATE_OF_CHARGE) { LOG_DBG("unsupported channel %d", chan); return -ENOTSUP; } - int err = max17048_read_register(dev, REG_STATE_OF_CHARGE, &data->state_of_charge); + int err = max17048_read_register(dev, REG_SOC, &data->state_of_charge); if (err != 0) { LOG_WRN("failed to read state-of-charge: %d", err); return err; @@ -121,7 +124,7 @@ static int max17048_sample_fetch(const struct device *dev, enum sensor_channel c return err; } - LOG_DBG("read values: soc=%d, vcell=%d", data->state_of_charge, data->vcell); + LOG_INF("read values: soc=%d, vcell=%d", data->state_of_charge, data->vcell); return 0; } @@ -137,11 +140,23 @@ static int max17048_channel_get(const struct device *dev, enum sensor_channel ch tmp = data->vcell * 1250 / 16; val->val1 = tmp / 1000000; // in V val->val2 = tmp % 1000000; // in uV + LOG_INF("Vcell = %d.%d", val->val1, val->val2); break; case SENSOR_CHAN_GAUGE_STATE_OF_CHARGE: val->val1 = (data->state_of_charge >> 8); // in 1% val->val2 = (data->state_of_charge & 0xFF) * 1000 / 256; // in 0.001% + LOG_INF("SOC = %d.%d%", val->val1, val->val2); + break; + + case SENSOR_CHAN_ALL: + tmp = data->vcell * 1250 / 16; + val->val1 = tmp / 1000000; // in V + val->val2 = tmp % 1000000; // in uV + LOG_INF("Vcell = %d.%d", val->val1, val->val2); + val->val1 = (data->state_of_charge >> 8); // in 1% + val->val2 = (data->state_of_charge & 0xFF) * 1000 / 256; // in 0.001% + LOG_INF("SOC = %d.%d%", val->val1, val->val2); break; default: @@ -173,10 +188,10 @@ static int max17048_init(const struct device *dev) { } // bring the device out of sleep - max17048_set_sleep_enabled(dev, false); + /* max17048_set_sleep_enabled(dev, false); */ // set the default rcomp value -- 0x97, as stated in the datasheet - max17048_set_rcomp_value(dev, 0x97); + /* max17048_set_rcomp_value(dev, 0x97); */ LOG_INF("device initialised at 0x%x (i2c=%s) (version %d)", config->i2c.addr, config->i2c_bus_name, ic_version); diff --git a/app/src/battery.c b/app/src/battery.c index 3f66224126e..e98b2f1637b 100644 --- a/app/src/battery.c +++ b/app/src/battery.c @@ -13,7 +13,7 @@ #include -LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); +LOG_MODULE_REGISTER(battery, CONFIG_ZMK_LOG_LEVEL); #include #include @@ -34,6 +34,8 @@ static const struct device *battery; static int zmk_battery_update(const struct device *battery) { struct sensor_value state_of_charge; + LOG_INF("update soc"); + int rc = sensor_sample_fetch_chan(battery, SENSOR_CHAN_GAUGE_STATE_OF_CHARGE); if (rc != 0) { @@ -41,17 +43,20 @@ static int zmk_battery_update(const struct device *battery) { return rc; } - rc = sensor_channel_get(battery, SENSOR_CHAN_GAUGE_STATE_OF_CHARGE, &state_of_charge); + /* rc = sensor_channel_get(battery, SENSOR_CHAN_GAUGE_STATE_OF_CHARGE, &state_of_charge); */ + rc = sensor_channel_get(battery, SENSOR_CHAN_ALL, &state_of_charge); if (rc != 0) { LOG_DBG("Failed to get battery state of charge: %d", rc); return rc; } + else + LOG_INF("Latest battery values: %d", state_of_charge.val1); if (last_state_of_charge != state_of_charge.val1) { last_state_of_charge = state_of_charge.val1; - LOG_DBG("Setting BAS GATT battery level to %d.", last_state_of_charge); + LOG_INF("Setting BAS GATT battery level to %d.", last_state_of_charge); rc = bt_bas_set_battery_level(last_state_of_charge); @@ -77,7 +82,10 @@ static void zmk_battery_work(struct k_work *work) { K_WORK_DEFINE(battery_work, zmk_battery_work); -static void zmk_battery_timer(struct k_timer *timer) { k_work_submit(&battery_work); } +static void zmk_battery_timer(struct k_timer *timer) { + LOG_INF("battery timer trigger"); + k_work_submit(&battery_work); +} K_TIMER_DEFINE(battery_timer, zmk_battery_timer, NULL); @@ -104,7 +112,10 @@ static int zmk_battery_init(const struct device *_arg) { return rc; } - k_timer_start(&battery_timer, K_MINUTES(1), K_SECONDS(CONFIG_ZMK_BATTERY_REPORT_INTERVAL)); + /* k_timer_start(&battery_timer, K_MINUTES(1), K_SECONDS(CONFIG_ZMK_BATTERY_REPORT_INTERVAL)); */ + k_timer_start(&battery_timer, K_SECONDS(10), K_SECONDS(CONFIG_ZMK_BATTERY_REPORT_INTERVAL)); + + LOG_INF("battery thread initiated"); return 0; } From f04b582fc8641bacb4e47d6ed9c7b1607b6ce5e7 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Sat, 15 Oct 2022 13:37:01 +0800 Subject: [PATCH 114/157] [max17048] the minimal functions of max17048 is implemented todo: 1. temperature compensation (rcomp) 2. voltage level 3. some interface functions using attr_set API --- app/boards/shields/blade/blade_right.conf | 6 +++--- app/drivers/sensor/battery/battery_max17048.c | 2 +- app/src/battery.c | 8 +++++--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/app/boards/shields/blade/blade_right.conf b/app/boards/shields/blade/blade_right.conf index 56d52d0d7a4..26a7e022341 100644 --- a/app/boards/shields/blade/blade_right.conf +++ b/app/boards/shields/blade/blade_right.conf @@ -49,8 +49,8 @@ CONFIG_CAP1203_MIX_MODE=y # CONFIG_ZMK_TRACKBALL_POLL_DURATION=8 ### logging -CONFIG_ZMK_USB_LOGGING=y -CONFIG_ZMK_LOG_LEVEL_INF=y +# CONFIG_ZMK_USB_LOGGING=y +# CONFIG_ZMK_LOG_LEVEL_INF=y # CONFIG_BATTERY_LOG_LEVEL_DBG=y -CONFIG_ZMK_BATTERY_REPORT_INTERVAL=10 +# CONFIG_ZMK_BATTERY_REPORT_INTERVAL=10 diff --git a/app/drivers/sensor/battery/battery_max17048.c b/app/drivers/sensor/battery/battery_max17048.c index bcbbb0d5560..c1a03aef030 100644 --- a/app/drivers/sensor/battery/battery_max17048.c +++ b/app/drivers/sensor/battery/battery_max17048.c @@ -14,7 +14,7 @@ #include #include -LOG_MODULE_REGISTER(max17048, CONFIG_ZMK_LOG_LEVEL); +LOG_MODULE_REGISTER(max17048, CONFIG_BATTERY_LOG_LEVEL); #define REG_VCELL 0x02 // 78.125 uV #define REG_SOC 0x04 // 1%/256 diff --git a/app/src/battery.c b/app/src/battery.c index e98b2f1637b..860a177c87a 100644 --- a/app/src/battery.c +++ b/app/src/battery.c @@ -13,7 +13,7 @@ #include -LOG_MODULE_REGISTER(battery, CONFIG_ZMK_LOG_LEVEL); +LOG_MODULE_REGISTER(battery, CONFIG_BATTERY_LOG_LEVEL); #include #include @@ -42,9 +42,11 @@ static int zmk_battery_update(const struct device *battery) { LOG_DBG("Failed to fetch battery values: %d", rc); return rc; } - - /* rc = sensor_channel_get(battery, SENSOR_CHAN_GAUGE_STATE_OF_CHARGE, &state_of_charge); */ +#ifdef CONFIG_ZMK_USB_LOGGING rc = sensor_channel_get(battery, SENSOR_CHAN_ALL, &state_of_charge); +#else + rc = sensor_channel_get(battery, SENSOR_CHAN_GAUGE_STATE_OF_CHARGE, &state_of_charge); +#endif if (rc != 0) { LOG_DBG("Failed to get battery state of charge: %d", rc); From 2e89655e48b583fd01221a70aad0e18d7300603f Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Sun, 16 Oct 2022 09:40:01 +0800 Subject: [PATCH 115/157] [cap1203] add dts option: invert_direction --- app/boards/shields/blade/blade.dtsi | 49 ++++++++++++++----- app/boards/shields/blade/blade.keymap | 1 + app/boards/shields/blade/blade_right.conf | 24 +++++++-- app/boards/shields/blade/blade_right.overlay | 20 +++++++- app/drivers/kscan/kscan_cap1203.c | 4 +- .../dts/bindings/kscan/zmk,kscan-cap1203.yaml | 2 + 6 files changed, 84 insertions(+), 16 deletions(-) diff --git a/app/boards/shields/blade/blade.dtsi b/app/boards/shields/blade/blade.dtsi index 2761a65c13a..b1d1575206e 100644 --- a/app/boards/shields/blade/blade.dtsi +++ b/app/boards/shields/blade/blade.dtsi @@ -70,38 +70,40 @@ RC(3,0) RC(3,1) RC(3,2) RC(3,3) RC(3,4) RC(3,5) RC(3,6) RC(3,7) }; }; - /* Configure Sliders */ + /* Configure Sliders (place holder) */ sliders { compatible = "zmk,keymap-sliders"; sliders = <&slider_left &slider_right>; }; - /* Configure Encoders */ + /* Configure Trackballs (place holder) */ + trackballs { + compatible = "zmk,keymap-trackballs"; + trackballs = <&trackball_left &trackball_right>; + }; + + /* Configure Encoders (place holder) */ sensors { compatible = "zmk,keymap-sensors"; - sensors = <&left_encoder &right_encoder>; + sensors = <&encoder_left &encoder_right>; }; - left_encoder: encoder_left { + encoder_left: encoder_left { compatible = "alps,ec11"; status = "disabled"; label = "LEFT_ENCODER"; - a-gpios = <&gpio1 4 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; - b-gpios = <&gpio1 6 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; resolution = <2>; }; - right_encoder: encoder_right { + encoder_right: encoder_right { compatible = "alps,ec11"; status = "disabled"; label = "RIGHT_ENCODER"; - a-gpios = <&gpio1 4 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; - b-gpios = <&gpio1 6 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; - resolution = <2>; + resolution = <2>; }; - }; +/* slider declaration */ &i2c0 { compatible = "nordic,nrf-twi"; status = "okay"; @@ -120,6 +122,7 @@ RC(3,0) RC(3,1) RC(3,2) RC(3,3) RC(3,4) RC(3,5) RC(3,6) RC(3,7) reg = <0x28>; }; +/* fuel gauge declaration */ fuel_gauge: fuel_gauge@36 { compatible = "zmk,battery-max17048"; label = "BATTERY_FUELGAUGE"; @@ -127,3 +130,27 @@ RC(3,0) RC(3,1) RC(3,2) RC(3,3) RC(3,4) RC(3,5) RC(3,6) RC(3,7) reg = <0x36>; }; }; + +/* trackball declaration */ +&spi2 { + compatible = "nordic,nrf-spim"; + status = "okay"; + + trackball_left: trackball_left@0 { + compatible = "pixart,pmw3610"; + status = "disabled"; + reg = <0>; + label = "TRACKBALL_LEFT"; + spi-max-frequency = <2000000>; + duplex = <2048>; // required by 3-wire spi + }; + + trackball_right: trackball_right@0 { + compatible = "pixart,pmw3610"; + status = "disabled"; + reg = <0>; + label = "TRACKBALL_RIGHT"; + spi-max-frequency = <2000000>; + duplex = <2048>; // required by 3-wire spi + }; +}; diff --git a/app/boards/shields/blade/blade.keymap b/app/boards/shields/blade/blade.keymap index 67e5cdab8d8..f429c49e804 100644 --- a/app/boards/shields/blade/blade.keymap +++ b/app/boards/shields/blade/blade.keymap @@ -36,6 +36,7 @@ >; // sensor-bindings = <&inc_dec_kp RIGHT LEFT &inc_dec_kp UP DOWN>; slider-bindings = <&slv &slv>; + trackball-bindings = <&tmv &tmv>; }; // qwert_layer { diff --git a/app/boards/shields/blade/blade_right.conf b/app/boards/shields/blade/blade_right.conf index 26a7e022341..e671617332b 100644 --- a/app/boards/shields/blade/blade_right.conf +++ b/app/boards/shields/blade/blade_right.conf @@ -45,12 +45,30 @@ CONFIG_CAP1203_MIX_MODE=y ##### Point Devices #### # CONFIG_ZMK_PD_WORK_QUEUE_DEDICATED=n -# CONFIG_ZMK_PD_DEDICATED_WORK_QUEUE_PRIORITY=2 -# CONFIG_ZMK_TRACKBALL_POLL_DURATION=8 +CONFIG_ZMK_PD_DEDICATED_WORK_QUEUE_PRIORITY=2 +CONFIG_ZMK_TRACKBALL_POLL_DURATION=8 + +CONFIG_ZMK_MOUSE=y +CONFIG_ZMK_MOUSE_TICK_DURATION=10 +CONFIG_ZMK_MOUSE_WORK_QUEUE_DEDICATED=y +CONFIG_ZMK_MOUSE_DEDICATED_THREAD_PRIORITY=1 + +CONFIG_PMW3610=y +CONFIG_PMW3610_CPI=3200 +CONFIG_PMW3610_CPI_DIVIDOR=4 +CONFIG_PMW3610_ORIENTATION_90=y +CONFIG_PMW3610_REST1_SAMPLE_TIME_MS=100 +CONFIG_PMW3610_REST2_SAMPLE_TIME_MS=200 +CONFIG_PMW3610_REST3_SAMPLE_TIME_MS=300 +CONFIG_PMW3610_RUN_DOWNSHIFT_TIME_MS=500 +CONFIG_PMW3610_REST1_DOWNSHIFT_TIME_MS=3000 +CONFIG_PMW3610_REST2_DOWNSHIFT_TIME_MS=30000 + +# CONFIG_ZMK_BATTERY_REPORT_INTERVAL=10 ### logging # CONFIG_ZMK_USB_LOGGING=y # CONFIG_ZMK_LOG_LEVEL_INF=y # CONFIG_BATTERY_LOG_LEVEL_DBG=y +# CONFIG_PMW3610_LOG_LEVEL_INF=y -# CONFIG_ZMK_BATTERY_REPORT_INTERVAL=10 diff --git a/app/boards/shields/blade/blade_right.overlay b/app/boards/shields/blade/blade_right.overlay index f00f813e457..1b7ffc7d5c9 100644 --- a/app/boards/shields/blade/blade_right.overlay +++ b/app/boards/shields/blade/blade_right.overlay @@ -45,9 +45,11 @@ kscan1: &slider_right {}; &slider_right { status = "okay"; + invert-direction; int-gpios = <&gpio1 9 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; }; +/* Enable fuel gauge */ &fuel_gauge { status = "okay"; }; @@ -81,7 +83,23 @@ kscan1: &slider_right {}; }; }; -/* Enable the right encoder */ +/* Define trackball pins */ +&spi2 { + sck-pin = <17>; + mosi-pin = <12>; + miso-pin = <12>; // same as mosi (3-wire spi) + cs-gpios = <&gpio0 24 GPIO_ACTIVE_LOW>; +}; + +// enable the right trackball +&trackball_right { + status = "okay"; + irq-gpios = <&gpio0 13 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; +}; + +/* Define encoder pins */ // &right_encoder { // status = "okay"; +// a-gpios = <&gpio1 4 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; +// b-gpios = <&gpio1 6 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; // }; \ No newline at end of file diff --git a/app/drivers/kscan/kscan_cap1203.c b/app/drivers/kscan/kscan_cap1203.c index d0d8e28deae..b4d70470abe 100644 --- a/app/drivers/kscan/kscan_cap1203.c +++ b/app/drivers/kscan/kscan_cap1203.c @@ -61,6 +61,7 @@ LOG_MODULE_REGISTER(kscan_cap1203, CONFIG_ZMK_LOG_LEVEL); struct kscan_cap1203_config { struct i2c_dt_spec i2c; struct gpio_dt_spec int_gpio; + bool invert_direction; }; struct kscan_cap1203_data { @@ -352,7 +353,7 @@ static int kscan_cap1203_read(const struct device *dev) // slider callback to process the deltas if (slider_data->callback) - slider_data->callback(dev, slider_data->delta_position, + slider_data->callback(dev, config->invert_direction ? -slider_data->delta_position : slider_data->delta_position, slider_data->delta_time); } LOG_INF("dPos: %d, dT: %d ms", slider_data->delta_position, slider_data->delta_time); @@ -606,6 +607,7 @@ static struct kscan_slider_api kscan_cap1203_slider_api = { static const struct kscan_cap1203_config kscan_cap1203_config_##index = { \ .i2c = I2C_DT_SPEC_INST_GET(index), \ .int_gpio = GPIO_DT_SPEC_INST_GET_OR(index, int_gpios, {0}), \ + .invert_direction = DT_INST_PROP(index, invert_direction), \ }; \ static struct kscan_cap1203_data kscan_cap1203_data_##index; \ DEVICE_DT_INST_DEFINE(index, kscan_cap1203_init, NULL, \ diff --git a/app/drivers/zephyr/dts/bindings/kscan/zmk,kscan-cap1203.yaml b/app/drivers/zephyr/dts/bindings/kscan/zmk,kscan-cap1203.yaml index 723c9732afa..f866f4f4067 100644 --- a/app/drivers/zephyr/dts/bindings/kscan/zmk,kscan-cap1203.yaml +++ b/app/drivers/zephyr/dts/bindings/kscan/zmk,kscan-cap1203.yaml @@ -8,6 +8,8 @@ compatible: "zmk,kscan-cap1203" include: [kscan.yaml, i2c-device.yaml] properties: + invert-direction: + type: boolean int-gpios: type: phandle-array required: false From 70383bb8b3986e177aab1b684a149f14de704bba Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Tue, 18 Oct 2022 07:40:06 +0800 Subject: [PATCH 116/157] backup --- app/boards/shields/blade/blade_right.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/boards/shields/blade/blade_right.conf b/app/boards/shields/blade/blade_right.conf index e671617332b..1ae1d841b6e 100644 --- a/app/boards/shields/blade/blade_right.conf +++ b/app/boards/shields/blade/blade_right.conf @@ -41,7 +41,7 @@ CONFIG_NRFMACRO_LCD_DISPLAY=y CONFIG_BT_CTLR_TX_PWR_PLUS_8=y ##### Slider #### -CONFIG_CAP1203_MIX_MODE=y +CONFIG_CAP1203_SLIDER_MODE=y ##### Point Devices #### # CONFIG_ZMK_PD_WORK_QUEUE_DEDICATED=n From 11c778b926c2a48bf7cdd80b0752f2450f5f0ce3 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Tue, 18 Oct 2022 20:06:08 +0800 Subject: [PATCH 117/157] [blade] merge sweep-pro left and blade-right into a single keyboard --- app/CMakeLists.txt | 2 +- app/boards/shields/blade/blade.dtsi | 66 ++--------- app/boards/shields/blade/blade.keymap | 6 +- app/boards/shields/blade/blade_left.conf | 110 +++++++++++++------ app/boards/shields/blade/blade_left.overlay | 89 +++++++++++---- app/boards/shields/blade/blade_right.conf | 6 + app/boards/shields/blade/blade_right.overlay | 59 +++++++--- app/src/ble.c | 4 +- 8 files changed, 207 insertions(+), 135 deletions(-) diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 4d2774d8703..f4f329381f4 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -30,7 +30,6 @@ target_sources(app PRIVATE src/mouse/tick_listener.c) target_sources(app PRIVATE src/sensors.c) target_sources(app PRIVATE src/point_device/main.c) target_sources(app PRIVATE src/point_device/sliders.c) -target_sources(app PRIVATE src/point_device/trackballs.c) target_sources_ifdef(CONFIG_ZMK_WPM app PRIVATE src/wpm.c) target_sources(app PRIVATE src/event_manager.c) target_sources_ifdef(CONFIG_ZMK_EXT_POWER app PRIVATE src/ext_power_generic.c) @@ -82,6 +81,7 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL) target_sources(app PRIVATE src/events/pd_scroll_state_changed.c) target_sources(app PRIVATE src/events/pd_position_state_changed.c) target_sources(app PRIVATE src/point_device/pd_listener.c) + target_sources(app PRIVATE src/point_device/trackballs.c) if (CONFIG_ZMK_BLE) target_sources(app PRIVATE src/events/ble_active_profile_changed.c) diff --git a/app/boards/shields/blade/blade.dtsi b/app/boards/shields/blade/blade.dtsi index b1d1575206e..10d8bb783f7 100644 --- a/app/boards/shields/blade/blade.dtsi +++ b/app/boards/shields/blade/blade.dtsi @@ -6,26 +6,12 @@ #include -/* overrides nrfmacro.dts defaults */ -&blue_led { - gpios = <&gpio1 11 GPIO_ACTIVE_HIGH>; -}; - -&vcc_ctrl { - control-gpios = <&gpio1 10 GPIO_ACTIVE_LOW>; -}; - -&vbatt { - status = "disabled"; -}; - /* blade-specific: common settings */ / { chosen: chosen { zmk,kscan = &kscan; zmk,matrix_transform = &default_transform; - zmk,battery = &fuel_gauge; }; /* global matrix transform */ @@ -33,16 +19,6 @@ compatible = "zmk,matrix-transform"; columns = <10>; rows = <5>; -// | SW1 | SW2 | SW3 | SW4 | SW5 | | SW5 | SW4 | SW3 | SW2 | SW1 | -// | SW6 | SW7 | SW8 | SW9 | SW10 | | SW10 | SW9 | SW8 | SW7 | SW6 | -// | SW11 | SW12 | SW13 | SW14 | SW15 | | SW15 | SW14 | SW13 | SW12 | SW11 | -// | SW16 | SW17 | SW18 | | SW18 | SW17 | SW16 | -// map = < -// RC(0,0) RC(0,1) RC(0,2) RC(0,3) RC(0,4) RC(0,5) RC(0,6) RC(0,7) RC(0,8) RC(0,9) -// RC(1,0) RC(1,1) RC(1,2) RC(1,3) RC(1,4) RC(1,5) RC(1,6) RC(1,7) RC(1,8) RC(1,9) -// RC(2,0) RC(2,1) RC(2,2) RC(2,3) RC(2,4) RC(2,5) RC(2,6) RC(2,7) RC(2,8) RC(2,9) -// RC(3,0) RC(3,1) RC(3,2) RC(3,3) RC(3,4) RC(3,5) RC(3,6) RC(3,7) RC(3,8) RC(3,9) -// >; map = < RC(0,0) RC(0,1) RC(0,2) RC(0,3) RC(0,4) RC(0,5) RC(0,6) RC(0,7) RC(0,8) RC(0,9) RC(1,0) RC(1,1) RC(1,2) RC(1,3) RC(1,4) RC(1,5) RC(1,6) RC(1,7) RC(1,8) RC(1,9) @@ -60,34 +36,31 @@ RC(3,0) RC(3,1) RC(3,2) RC(3,3) RC(3,4) RC(3,5) RC(3,6) RC(3,7) rows = <5>; columns = <10>; + /* There is a key matrix by default */ normal_keys { kscan = <&kscan0>; }; - - touch_keys { - kscan = <&kscan1>; - row-offset = <4>; - }; }; - /* Configure Sliders (place holder) */ + /* Configure sliders (both split can have a slider) */ sliders { compatible = "zmk,keymap-sliders"; sliders = <&slider_left &slider_right>; }; - /* Configure Trackballs (place holder) */ + /* Configure trackballs (either split can have the trackball) */ trackballs { compatible = "zmk,keymap-trackballs"; - trackballs = <&trackball_left &trackball_right>; + trackballs = <&trackball>; }; - /* Configure Encoders (place holder) */ + /* Configure encoders (both splits can have a encoder) */ sensors { compatible = "zmk,keymap-sensors"; sensors = <&encoder_left &encoder_right>; }; - + + // define the two encoders encoder_left: encoder_left { compatible = "alps,ec11"; status = "disabled"; @@ -103,7 +76,7 @@ RC(3,0) RC(3,1) RC(3,2) RC(3,3) RC(3,4) RC(3,5) RC(3,6) RC(3,7) }; }; -/* slider declaration */ +/* Declare the two sliders */ &i2c0 { compatible = "nordic,nrf-twi"; status = "okay"; @@ -122,34 +95,17 @@ RC(3,0) RC(3,1) RC(3,2) RC(3,3) RC(3,4) RC(3,5) RC(3,6) RC(3,7) reg = <0x28>; }; -/* fuel gauge declaration */ - fuel_gauge: fuel_gauge@36 { - compatible = "zmk,battery-max17048"; - label = "BATTERY_FUELGAUGE"; - status = "disabled"; - reg = <0x36>; - }; }; -/* trackball declaration */ +/* Declare the trackball */ &spi2 { compatible = "nordic,nrf-spim"; - status = "okay"; - - trackball_left: trackball_left@0 { - compatible = "pixart,pmw3610"; - status = "disabled"; - reg = <0>; - label = "TRACKBALL_LEFT"; - spi-max-frequency = <2000000>; - duplex = <2048>; // required by 3-wire spi - }; - trackball_right: trackball_right@0 { + trackball: trackball@0 { compatible = "pixart,pmw3610"; status = "disabled"; reg = <0>; - label = "TRACKBALL_RIGHT"; + label = "TRACKBALL"; spi-max-frequency = <2000000>; duplex = <2048>; // required by 3-wire spi }; diff --git a/app/boards/shields/blade/blade.keymap b/app/boards/shields/blade/blade.keymap index f429c49e804..e1694f10e9b 100644 --- a/app/boards/shields/blade/blade.keymap +++ b/app/boards/shields/blade/blade.keymap @@ -29,12 +29,12 @@ label = "CLMK"; bindings = < &kp Q &kp W &kp F &kp P &kp B &kp J &kp L &kp U &kp Y &kp SQT - &kp A &mt LSFT R &mt LALT S &mt LCTL T &kp G &kp M &mt RCTL N &mt RALT E &mt RSFT I &kp O + &kp A &mt LSFT R &mt LALT S &mt LCTL T &kp G &kp M &mt RCTL N &mt RALT E &mt RSFT I &kp O &kp Z &kp X &kp C &kp D &kp V &kp K &kp H &cmqus &dtsmi &kp RC(B) - &kp KP_N1 &kp KP_N2 &encoder &kp TAB &kp SPACE < SYM RET &kp BSPC &encoder + &kp KP_N1 &kp KP_N2 &encoder &kp TAB &kp SPACE < SYM RET &kp BSPC &encoder &kp A &kp B &kp C >; - // sensor-bindings = <&inc_dec_kp RIGHT LEFT &inc_dec_kp UP DOWN>; + sensor-bindings = <&inc_dec_kp UP DOWN &inc_dec_kp UP DOWN>; slider-bindings = <&slv &slv>; trackball-bindings = <&tmv &tmv>; }; diff --git a/app/boards/shields/blade/blade_left.conf b/app/boards/shields/blade/blade_left.conf index c5b193988d7..32ac9b9b641 100644 --- a/app/boards/shields/blade/blade_left.conf +++ b/app/boards/shields/blade/blade_left.conf @@ -2,38 +2,78 @@ # SPDX-License-Identifier: MIT # Set the role of the split, used in choosing the dedicated status screen -# CONFIG_NRFMACRO_SHIELD_MASTER=y - -# # Enable epaper display -# CONFIG_ZMK_DISPLAY=y -# CONFIG_NRFMACRO_EPD_DISPLAY=y -# CONFIG_NRFMACRO_EPD_ROTATE_180=y -# CONFIG_MYCHANGE_DISPLAY_UPDATE_MO_BEHAVIOR=n - -# # diplay settings (NOTE: these configurations have to be explicitly set here in shield) -# ## color scheme: black/white -# CONFIG_LVGL_USE_THEME_MONO=y -# CONFIG_LVGL_THEME_DEFAULT_COLOR_PRIMARY_BLACK=y -# CONFIG_LVGL_THEME_DEFAULT_COLOR_SECONDARY_WHITE=y -# CONFIG_LVGL_THEME_DEFAULT_COLOR_PRIMARY_RED=n -# CONFIG_LVGL_THEME_DEFAULT_COLOR_SECONDARY_RED=n -# ## dedicated work queue or not -# CONFIG_ZMK_DISPLAY_WORK_QUEUE_DEDICATED=y - -# ## custom screen or not -# ### uncommet following for custom status screen -# CONFIG_ZMK_DISPLAY_STATUS_SCREEN_CUSTOM=y -# CONFIG_LVGL_THEME_DEFAULT_FONT_NORMAL_MONTSERRAT_16=y -# CONFIG_CUSTOM_WIDGET_BATTERY_STATUS=y -# CONFIG_ZMK_WIDGET_BATTERY_STATUS=n -# CONFIG_CUSTOM_WIDGET_OUTPUT_STATUS=y -# CONFIG_ZMK_WIDGET_OUTPUT_STATUS=n -# CONFIG_CUSTOM_WIDGET_LAYER_STATUS=y -# CONFIG_ZMK_WIDGET_LAYER_STATUS=n - -# Enable encoder support -# CONFIG_EC11=y -# CONFIG_EC11_TRIGGER_GLOBAL_THREAD=y - -# set transmission power to max -CONFIG_BT_CTLR_TX_PWR_PLUS_8=y \ No newline at end of file +CONFIG_NRFMACRO_SHIELD_SLAVE=y + +#### Enable display #### +CONFIG_ZMK_DISPLAY=y +CONFIG_LVGL_USE_THEME_MONO=y +CONFIG_ZMK_DISPLAY_WORK_QUEUE_DEDICATED=y +CONFIG_ZMK_DISPLAY_FULL_REFRESH_PERIOD=10 + +# use LCD display +# CONFIG_NRFMACRO_LCD_DISPLAY=y + +# or use EPD display +CONFIG_NRFMACRO_EPD_DISPLAY=y +CONFIG_NRFMACRO_EPD_ROTATE_180=y +CONFIG_LVGL_THEME_DEFAULT_COLOR_PRIMARY_BLACK=y +CONFIG_LVGL_THEME_DEFAULT_COLOR_SECONDARY_WHITE=y +CONFIG_LVGL_THEME_DEFAULT_COLOR_PRIMARY_RED=n +CONFIG_LVGL_THEME_DEFAULT_COLOR_SECONDARY_RED=n +# custom screen or not +## uncommet following for custom screen +CONFIG_ZMK_DISPLAY_STATUS_SCREEN_CUSTOM=y +CONFIG_LVGL_THEME_DEFAULT_FONT_NORMAL_MONTSERRAT_16=y +CONFIG_CUSTOM_WIDGET_BATTERY_STATUS=y +CONFIG_ZMK_WIDGET_BATTERY_STATUS=n +CONFIG_CUSTOM_WIDGET_PERIPHERAL_STATUS=y +CONFIG_ZMK_WIDGET_PERIPHERAL_STATUS=n +CONFIG_NRFMACRO_SCREEN_MARK_LOGO=y +# use custom logo or not +# CONFIG_NRFMACRO_SCREEN_STANDARD_LOGO=y +# CONFIG_NRFMACRO_SCREEN_CUSTOM_LOGO=y + +#### Enable encoder support #### +CONFIG_EC11=y +CONFIG_EC11_TRIGGER_GLOBAL_THREAD=y + +# # set transmission power to max +CONFIG_BT_CTLR_TX_PWR_PLUS_8=y + +##### Slider #### +# CONFIG_CAP1203_SLIDER_MODE=y + +##### Point Devices #### +# CONFIG_ZMK_PD_WORK_QUEUE_DEDICATED=n +# CONFIG_ZMK_PD_DEDICATED_WORK_QUEUE_PRIORITY=2 +# CONFIG_ZMK_TRACKBALL_POLL_DURATION=8 + +# CONFIG_ZMK_MOUSE=y +# CONFIG_ZMK_MOUSE_TICK_DURATION=10 +# CONFIG_ZMK_MOUSE_WORK_QUEUE_DEDICATED=y +# CONFIG_ZMK_MOUSE_DEDICATED_THREAD_PRIORITY=1 + +# CONFIG_PMW3610=y +# CONFIG_PMW3610_CPI=3200 +# CONFIG_PMW3610_CPI_DIVIDOR=4 +# CONFIG_PMW3610_ORIENTATION_90=y +# CONFIG_PMW3610_REST1_SAMPLE_TIME_MS=100 +# CONFIG_PMW3610_REST2_SAMPLE_TIME_MS=200 +# CONFIG_PMW3610_REST3_SAMPLE_TIME_MS=300 +# CONFIG_PMW3610_RUN_DOWNSHIFT_TIME_MS=500 +# CONFIG_PMW3610_REST1_DOWNSHIFT_TIME_MS=3000 +# CONFIG_PMW3610_REST2_DOWNSHIFT_TIME_MS=30000 + +### Bluetooth ### +# CONFIG_BT_PERIPHERAL_PREF_MAX_INT=9 +# CONFIG_BT_PERIPHERAL_PREF_LATENCY=16 +# CONFIG_BT_BUF_ACL_TX_COUNT=32 +# CONFIG_BT_L2CAP_TX_BUF_COUNT=32 + +# CONFIG_ZMK_BATTERY_REPORT_INTERVAL=10 + +### logging +# CONFIG_ZMK_USB_LOGGING=y +# CONFIG_ZMK_LOG_LEVEL_INF=y +# CONFIG_BATTERY_LOG_LEVEL_DBG=y +# CONFIG_PMW3610_LOG_LEVEL_INF=y diff --git a/app/boards/shields/blade/blade_left.overlay b/app/boards/shields/blade/blade_left.overlay index b99c6d75bcc..9409287152e 100644 --- a/app/boards/shields/blade/blade_left.overlay +++ b/app/boards/shields/blade/blade_left.overlay @@ -4,29 +4,74 @@ * SPDX-License-Identifier: MIT */ -#include "sweep-pro.dtsi" - -// setup display -// &chosen { -// zephyr,display = &epd; -// }; - -// &nrfmacro_spi { -// status = "okay"; -// }; - -// setup kscan pins -&kscan { - col-gpios - = <&pro_micro 15 GPIO_ACTIVE_HIGH> - , <&pro_micro 18 GPIO_ACTIVE_HIGH> - , <&pro_micro 19 GPIO_ACTIVE_HIGH> - , <&pro_micro 20 GPIO_ACTIVE_HIGH> - , <&pro_micro 21 GPIO_ACTIVE_HIGH> - ; +#include "blade.dtsi" + +/* Define kscan pins */ +/ { + kscan0: kscan0 { + compatible = "zmk,kscan-gpio-matrix"; + label = "KSCAN0"; + + diode-direction = "col2row"; + row-gpios + = <&gpio0 3 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&gpio0 28 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&gpio1 11 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&gpio0 10 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + ; + col-gpios + = <&gpio1 13 GPIO_ACTIVE_HIGH> + , <&gpio0 2 GPIO_ACTIVE_HIGH> + , <&gpio0 29 GPIO_ACTIVE_HIGH> + , <&gpio0 31 GPIO_ACTIVE_HIGH> + , <&gpio0 30 GPIO_ACTIVE_HIGH> + ; + }; +}; + +/* Voltage divider based fuel gauge */ +&chosen { + zmk,battery = &vbatt; +}; + +/* Define display */ +&chosen { + zephyr,display = &epd; +}; + +&nrfmacro_spi { + // activate this device + status = "okay"; + + compatible = "nordic,nrf-spim"; + sck-pin = <20>; + mosi-pin = <17>; + cs-gpios = <&gpio0 24 GPIO_ACTIVE_LOW>; + // not used, but required by spim binding; can be reuesd in the keymap for normal key press + miso-pin = <22>; + + + // spi-device: gooddisplay GDEW0102T4 + epd: il0323@0 { + compatible = "gooddisplay,il0323"; + reg = <0>; + label = "DISPLAY"; + spi-max-frequency = <4000000>; + + height = <128>; + width = <80>; + reset-gpios = <&gpio0 15 GPIO_ACTIVE_LOW>; + dc-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>; + busy-gpios = <&gpio0 9 GPIO_ACTIVE_LOW>; + pwr = [03 00 26 26]; + cdi = <0xd2>; + tcon = <0x22>; + }; }; -// enable left encoder -&left_encoder { +// Define encoder pins +&encoder_left { status = "okay"; + a-gpios = <&gpio1 4 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + b-gpios = <&gpio1 6 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; }; \ No newline at end of file diff --git a/app/boards/shields/blade/blade_right.conf b/app/boards/shields/blade/blade_right.conf index 1ae1d841b6e..2c4cc606a7f 100644 --- a/app/boards/shields/blade/blade_right.conf +++ b/app/boards/shields/blade/blade_right.conf @@ -64,6 +64,12 @@ CONFIG_PMW3610_RUN_DOWNSHIFT_TIME_MS=500 CONFIG_PMW3610_REST1_DOWNSHIFT_TIME_MS=3000 CONFIG_PMW3610_REST2_DOWNSHIFT_TIME_MS=30000 +### Bluetooth ### +CONFIG_BT_PERIPHERAL_PREF_MAX_INT=9 +CONFIG_BT_PERIPHERAL_PREF_LATENCY=16 +CONFIG_BT_BUF_ACL_TX_COUNT=32 +CONFIG_BT_L2CAP_TX_BUF_COUNT=32 + # CONFIG_ZMK_BATTERY_REPORT_INTERVAL=10 ### logging diff --git a/app/boards/shields/blade/blade_right.overlay b/app/boards/shields/blade/blade_right.overlay index 1b7ffc7d5c9..280d2d8f95d 100644 --- a/app/boards/shields/blade/blade_right.overlay +++ b/app/boards/shields/blade/blade_right.overlay @@ -6,6 +6,19 @@ #include "blade.dtsi" +/* overrides nrfmacro.dts defaults */ +&blue_led { + gpios = <&gpio1 11 GPIO_ACTIVE_HIGH>; +}; + +&vcc_ctrl { + control-gpios = <&gpio1 10 GPIO_ACTIVE_LOW>; +}; + +&vbatt { + status = "disabled"; +}; + /* adjust matrix transform for pepripheral split */ &default_transform { col-offset = <5>; @@ -34,15 +47,14 @@ }; }; -kscan1: &slider_right {}; - - -/* Define the right slider pins */ -&i2c0 { - sda-pin = <8>; - scl-pin = <4>; +&kscan { + touch_keys { + kscan = <&slider_right>; + row-offset = <4>; + }; }; +/* Enable the right slider and define its pins */ &slider_right { status = "okay"; invert-direction; @@ -50,19 +62,32 @@ kscan1: &slider_right {}; }; /* Enable fuel gauge */ -&fuel_gauge { - status = "okay"; +&chosen { + zmk,battery = &fuel_gauge; }; -/* Define display */ -/ { - chosen { - zephyr,display = &lcd; +/* Define sda and scl pins */ +&i2c0 { + sda-pin = <8>; + scl-pin = <4>; + + /* Define the fuel gauge */ + fuel_gauge: fuel_gauge@36 { + compatible = "zmk,battery-max17048"; + label = "BATTERY_FUELGAUGE"; + reg = <0x36>; }; }; +/* Define display */ +&chosen { + zephyr,display = &lcd; +}; + &nrfmacro_spi { + // activate this device status = "okay"; + compatible = "nordic,nrf-spim"; sck-pin = <32>; mosi-pin = <22>; @@ -83,21 +108,21 @@ kscan1: &slider_right {}; }; }; -/* Define trackball pins */ +/* Enable spi2 bus and the trackball, and define their pins */ &spi2 { + status = "okay"; sck-pin = <17>; mosi-pin = <12>; miso-pin = <12>; // same as mosi (3-wire spi) cs-gpios = <&gpio0 24 GPIO_ACTIVE_LOW>; }; -// enable the right trackball -&trackball_right { +&trackball { status = "okay"; irq-gpios = <&gpio0 13 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; }; -/* Define encoder pins */ +/* Enable the right encoder and define its pins */ // &right_encoder { // status = "okay"; // a-gpios = <&gpio1 4 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; diff --git a/app/src/ble.c b/app/src/ble.c index db1b5eeb223..19cc86d6868 100644 --- a/app/src/ble.c +++ b/app/src/ble.c @@ -389,8 +389,8 @@ static void connected(struct bt_conn *conn, uint8_t err) { update_advertising(); - LOG_INF("New connection params: Interval: %d, Latency: %d, RX: %d, TX: %d", info.le.interval, - info.le.latency, info.le.phy->rx_phy, info.le.phy->tx_phy); + /* LOG_DBG("New connection params: Interval: %d, Latency: %d, RX: %d, TX: %d", info.le.interval, */ + /* info.le.latency, info.le.phy->rx_phy, info.le.phy->tx_phy); */ if (is_conn_active_profile(conn)) { LOG_DBG("Active profile connected"); From 38b9751dd1bd0da384b6cbdbebf7f0097bcdb6de Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Tue, 18 Oct 2022 22:06:19 +0800 Subject: [PATCH 118/157] [blade] add full keymap --- app/boards/shields/blade/blade.dtsi | 2 +- app/boards/shields/blade/blade.keymap | 337 ++++++++++++++++++++++---- 2 files changed, 295 insertions(+), 44 deletions(-) diff --git a/app/boards/shields/blade/blade.dtsi b/app/boards/shields/blade/blade.dtsi index 10d8bb783f7..4f2af147b3e 100644 --- a/app/boards/shields/blade/blade.dtsi +++ b/app/boards/shields/blade/blade.dtsi @@ -23,7 +23,7 @@ RC(0,0) RC(0,1) RC(0,2) RC(0,3) RC(0,4) RC(0,5) RC(0,6) RC(0,7) RC(0,8) RC(0,9) RC(1,0) RC(1,1) RC(1,2) RC(1,3) RC(1,4) RC(1,5) RC(1,6) RC(1,7) RC(1,8) RC(1,9) RC(2,0) RC(2,1) RC(2,2) RC(2,3) RC(2,4) RC(2,5) RC(2,6) RC(2,7) RC(2,8) RC(2,9) -RC(3,0) RC(3,1) RC(3,2) RC(3,3) RC(3,4) RC(3,5) RC(3,6) RC(3,7) +RC(3,0) RC(3,1) RC(3,2) RC(3,3) RC(3,4) RC(3,5) RC(3,6) RC(3,7) RC(3,8) RC(3,9) RC(4,5) RC(4,6) RC(4,7) >; diff --git a/app/boards/shields/blade/blade.keymap b/app/boards/shields/blade/blade.keymap index e1694f10e9b..6a60c3654d6 100644 --- a/app/boards/shields/blade/blade.keymap +++ b/app/boards/shields/blade/blade.keymap @@ -8,11 +8,12 @@ #include #include #include +#include #define DEFAULT 0 -#define QWERT 1 -#define NUM 2 -#define SYM 3 +#define NUM 1 +#define SYM 2 +#define FUN 3 &mt { flavor = "tap-preferred"; @@ -20,58 +21,308 @@ }; / { -#include "../key-common.dtsi" + // macros + macros { + ZMK_MACRO(hello, + wait-ms = <0>; + tap-ms = <10>; + bindings = <&kp H &kp E &kp L &kp L &kp O>; + ) + ZMK_MACRO(encoder, + wait-ms = <0>; + tap-ms = <10>; + bindings = <&kp E &kp N &kp C &kp O &kp D &kp E &kp R>; + ) + + ZMK_MACRO(spc1, + wait-ms = <0>; + tap-ms = <10>; + bindings = <&kp LA(M) &kp N1>; + ) + + ZMK_MACRO(spc2, + wait-ms = <0>; + tap-ms = <10>; + bindings = <&kp LA(M) &kp N2>; + ) + + ZMK_MACRO(spc3, + wait-ms = <0>; + tap-ms = <10>; + bindings = <&kp LA(M) &kp N3>; + ) + + ZMK_MACRO(spc4, + wait-ms = <0>; + tap-ms = <10>; + bindings = <&kp LA(M) &kp N4>; + ) + + ZMK_MACRO(nxt_tab, + wait-ms = <0>; + tap-ms = <10>; + bindings = <&kp LC(TAB)>; + ) + + ZMK_MACRO(prv_tab, + wait-ms = <0>; + tap-ms = <10>; + bindings = <&kp LC(LS(TAB))>; + ) + }; + + // custom shift using mod-morph + behaviors { + cmqus: comma_question { + compatible = "zmk,behavior-mod-morph"; + label = "COMMA_QUESTION"; + #binding-cells = <0>; + bindings = <&kp COMMA>, <&kp QUESTION>; + mods = <(MOD_LSFT|MOD_RSFT)>; + }; + + dtsmi: dot_semi { + compatible = "zmk,behavior-mod-morph"; + label = "DOT_SEMI"; + #binding-cells = <0>; + bindings = <&kp DOT>, <&kp SEMI>; + mods = <(MOD_LSFT|MOD_RSFT)>; + masked_mods = <(MOD_LSFT|MOD_RSFT)>; // don't send shift + }; + }; + + // combos + combos { + compatible = "zmk,combos"; + // right hand + combo_esc { + timeout-ms = <50>; + key-positions = <16 17>; + bindings = <&kp ESC>; + }; + + combo_lgui { + timeout-ms = <50>; + key-positions = <5 6>; + bindings = <&kp LGUI>; + }; + + combo_fullscreen { + timeout-ms = <50>; + key-positions = <25 26>; + bindings = <&kp F11>; + }; + + combo_wkspc_up { + timeout-ms = <50>; + key-positions = <6 7>; + bindings = <&kp LG(PG_UP)>; + }; + + combo_wkspc_down { + timeout-ms = <50>; + key-positions = <26 27>; + bindings = <&kp LG(PG_DN)>; + }; + + combo_win_up { + timeout-ms = <50>; + key-positions = <7 8>; + bindings = <&kp LS(LG(PG_UP))>; + }; + + combo_win_down { + timeout-ms = <50>; + key-positions = <27 28>; + bindings = <&kp LS(LG(PG_DN))>; + }; + + combo_win_left { + timeout-ms = <50>; + key-positions = <17 18>; + bindings = <&kp LS(LG(LEFT))>; + }; + + combo_win_right { + timeout-ms = <50>; + key-positions = <9 18>; + bindings = <&kp LS(LG(RIGHT))>; + }; + + combo_rdelete { + timeout-ms = <50>; + key-positions = <15 16>; + bindings = <&kp DELETE>; + }; + + // left hand + combo_ldelete { + timeout-ms = <50>; + key-positions = <13 14>; + bindings = <&kp BSPC>; + }; + + combo_zoomin { + timeout-ms = <50>; + key-positions = <3 4>; + bindings = <&kp LC(PLUS)>; + }; + + combo_zoomout { + timeout-ms = <50>; + key-positions = <23 24>; + bindings = <&kp LC(MINUS)>; + }; + + combo_pgdn { + timeout-ms = <50>; + key-positions = <21 22>; + bindings = <&kp PG_DN>; + }; + + combo_pgup { + timeout-ms = <50>; + key-positions = <1 2>; + bindings = <&kp PG_UP>; + }; + + combo_mb_lclick { + timeout-ms = <50>; + key-positions = <12 13>; + bindings = <&mkp LCLK>; + }; + + combo_mb_rclick { + timeout-ms = <50>; + key-positions = <22 23>; + bindings = <&mkp RCLK>; + }; + + combo_mb_mclick { + timeout-ms = <50>; + key-positions = <2 3>; + bindings = <&mkp MCLK>; + }; + + // symbols + combo_underscore { + timeout-ms = <50>; + key-positions = <11 12>; + bindings = <&kp UNDERSCORE>; + }; + + // others + combo_capswd { + timeout-ms = <50>; + key-positions = <2 7>; + bindings = <&caps_word>; + }; + + // output selection + combo_outble { + timeout-ms = <50>; + key-positions = <4 14>; + bindings = <&out OUT_USB>; + }; + + combo_outtog { + timeout-ms = <50>; + key-positions = <4 24>; + bindings = <&out OUT_TOG>; + }; + + // ble selection + combo_ble1 { + timeout-ms = <50>; + key-positions = <14 24>; + bindings = <&bt BT_SEL 0>; + }; + + combo_ble2 { + timeout-ms = <50>; + key-positions = <13 23>; + bindings = <&bt BT_SEL 1>; + }; + + combo_ble3 { + timeout-ms = <50>; + key-positions = <12 22>; + bindings = <&bt BT_SEL 2>; + }; + + combo_ble4 { + timeout-ms = <50>; + key-positions = <11 21>; + bindings = <&bt BT_SEL 3>; + }; + + combo_ble5 { + timeout-ms = <50>; + key-positions = <10 20>; + bindings = <&bt BT_SEL 4>; + }; + }; + +// keymap keymap { compatible = "zmk,keymap"; default_layer { label = "CLMK"; bindings = < - &kp Q &kp W &kp F &kp P &kp B &kp J &kp L &kp U &kp Y &kp SQT - &kp A &mt LSFT R &mt LALT S &mt LCTL T &kp G &kp M &mt RCTL N &mt RALT E &mt RSFT I &kp O - &kp Z &kp X &kp C &kp D &kp V &kp K &kp H &cmqus &dtsmi &kp RC(B) - &kp KP_N1 &kp KP_N2 &encoder &kp TAB &kp SPACE < SYM RET &kp BSPC &encoder - &kp A &kp B &kp C + &kp Q &kp W &kp F &kp P &kp B &kp J &kp L &kp U &kp Y &kp SQT + < SYM A &mt LALT R &mt LSFT S &mt LCTL T &kp G &kp M &mt RCTL N &mt RSFT E &mt RALT I < SYM O + &kp Z &kp X &kp C &kp D &kp V &kp K &kp H &cmqus &dtsmi < FUN BSPC + &none &none &hello < FUN TAB < NUM SPACE < NUM RET < FUN BSPC &encoder &none &none + &none &none &none >; sensor-bindings = <&inc_dec_kp UP DOWN &inc_dec_kp UP DOWN>; slider-bindings = <&slv &slv>; - trackball-bindings = <&tmv &tmv>; + trackball-bindings = <&tmv>; }; - // qwert_layer { - // label = "QWRT"; - // bindings = < - // &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P - // &kp A &mt LSFT S &mt LALT D &mt LCTL T < NUM G < NUM H &mt RCTL J &mt RALT K &mt RSFT L &kp SQT - // &kp Z &kp X &kp C &kp V &kp B &kp N &kp M &cmqus &dtsmi &kp RC(B) - // &kp KP_N1 &kp KP_N2 &encoder &kp TAB < SYM SPACE < SYM RET &kp BSPC &encoder &kp KP_N3 &kp KP_N4 - // &encoder &kp TAB < SYM SPACE < SYM RET &kp BSPC &encoder - // >; - // sensor-bindings = <&inc_dec_kp RIGHT LEFT &inc_dec_kp UP DOWN>; - // }; - - // number_layer { - // label = "NUM"; - // bindings = < - // &bt BT_NXT &prv_tab &kp UP &nxt_tab &spc1 &kp KP_PLUS &kp KP_N7 &kp KP_N8 &kp KP_N9 &kp KP_MINUS - // &bt BT_PRV &kp LEFT &kp DOWN &kp RIGHT &spc2 &kp KP_MULTIPLY &kp KP_N4 &kp KP_N5 &kp KP_N6 &kp KP_DIVIDE - // &bt BT_CLR &none &none &none &spc3 &kp KP_EQUAL &kp KP_N1 &kp KP_N2 &kp KP_N3 &kp KP_DOT - // &kp KP_N1 &kp KP_N2 &encoder &spc4 &kp BSPC &kp RET &kp KP_N0 &encoder &kp KP_N3 &kp KP_N4 - // >; - // sensor-bindings = <&inc_dec_kp PG_UP PG_DN &inc_dec_kp C_VOL_UP C_VOL_DN>; - // }; - - // symbol_layer { - // label = "SYM"; - // bindings = < - // &none &kp TILDE &kp LT &kp GT &kp PRCNT &kp AMPS &kp CARET &kp LBRC &kp RBRC &tog QWERT - // &kp GRAVE &kp EXCL &kp MINUS &kp PLUS &kp EQUAL &kp PIPE &kp COLON &kp LPAR &kp RPAR &kp AT - // &none &none &kp SLASH &kp STAR &kp BSLH &kp HASH &kp DLLR &kp LBKT &kp RBKT &none - // &none &none &encoder &none &none &none &none &encoder &none &none - // >; - // sensor-bindings = <&inc_dec_kp PG_UP PG_DN &inc_dec_kp C_VOL_UP C_VOL_DN>; - // }; + number_layer { + label = "NUM"; + bindings = < + &bt BT_NXT &mkp MB4 &kp UP &mkp MB5 &spc1 &kp PLUS &kp N7 &kp N8 &kp N9 &kp MINUS + &bt BT_PRV &kp LEFT &kp DOWN &kp RIGHT &spc2 &kp ASTRK &kp N4 &kp N5 &kp N6 &kp FSLH + &bt BT_CLR &prv_tab &none &nxt_tab &spc3 &kp N0 &kp N1 &kp N2 &kp N3 &kp DOT + &none &none &hello &spc4 &kp HOME &kp END &kp N0 &encoder &none &none + &none &none &none + >; + sensor-bindings = <&inc_dec_kp UP DOWN &inc_dec_kp UP DOWN>; + slider-bindings = <&slv &slv>; + trackball-bindings = <&tmv>; + }; + + symbol_layer { + label = "SYM"; + bindings = < + &none &kp TILDE &kp LT &kp GT &kp PRCNT &kp AMPS &kp CARET &kp LBRC &kp RBRC &none + &kp GRAVE &kp EXCL &kp MINUS &kp PLUS &kp EQUAL &kp PIPE &kp COLON &kp LPAR &kp RPAR &kp AT + &none &none &kp SLASH &kp STAR &kp BSLH &kp HASH &kp DLLR &kp LBKT &kp RBKT &none + &none &none &hello &none &none &none &none &encoder &none &none + &none &none &none + >; + sensor-bindings = <&inc_dec_kp UP DOWN &inc_dec_kp UP DOWN>; + slider-bindings = <&slv &slv>; + trackball-bindings = <&tmv>; + }; + + function_layer { + label = "FUN"; + bindings = < + &none &kp F1 &kp F2 &kp F3 &none &none &kp F4 &kp F5 &kp F6 &none + &none &kp F7 &kp F8 &kp F9 &none &none &kp F10 &kp F11 &kp F12 &none + &none &none &none &none &none &none &none &none &none &none + &none &none &none &none &none &none &none &none &none &none + &none &none &none + >; + sensor-bindings = <&inc_dec_kp UP DOWN &inc_dec_kp UP DOWN>; + slider-bindings = <&slv &slv>; + trackball-bindings = <&tmv>; + }; }; }; \ No newline at end of file From 503c3120464fc9a480d72e44eb05051f0bc431fa Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Wed, 19 Oct 2022 21:54:30 +0800 Subject: [PATCH 119/157] backup --- app/boards/shields/blade/blade.keymap | 16 ++++++++-------- app/boards/shields/blade/blade_right.conf | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/boards/shields/blade/blade.keymap b/app/boards/shields/blade/blade.keymap index 6a60c3654d6..99475fae23e 100644 --- a/app/boards/shields/blade/blade.keymap +++ b/app/boards/shields/blade/blade.keymap @@ -160,7 +160,7 @@ combo_ldelete { timeout-ms = <50>; key-positions = <13 14>; - bindings = <&kp BSPC>; + bindings = <&kp DELETE>; }; combo_zoomin { @@ -273,8 +273,8 @@ bindings = < &kp Q &kp W &kp F &kp P &kp B &kp J &kp L &kp U &kp Y &kp SQT < SYM A &mt LALT R &mt LSFT S &mt LCTL T &kp G &kp M &mt RCTL N &mt RSFT E &mt RALT I < SYM O - &kp Z &kp X &kp C &kp D &kp V &kp K &kp H &cmqus &dtsmi < FUN BSPC - &none &none &hello < FUN TAB < NUM SPACE < NUM RET < FUN BSPC &encoder &none &none + &kp Z &kp X &kp C &kp D &kp V &kp K &kp H &cmqus &dtsmi < FUN TAB + &none &none &hello < FUN BSPC < NUM SPACE < NUM RET < FUN BSPC &encoder &none &none &none &none &none >; sensor-bindings = <&inc_dec_kp UP DOWN &inc_dec_kp UP DOWN>; @@ -292,8 +292,8 @@ &none &none &none >; sensor-bindings = <&inc_dec_kp UP DOWN &inc_dec_kp UP DOWN>; - slider-bindings = <&slv &slv>; - trackball-bindings = <&tmv>; + slider-bindings = <&slv &skp2 LC(PLUS) LC(MINUS)>; + trackball-bindings = <&tsl>; }; symbol_layer { @@ -306,8 +306,8 @@ &none &none &none >; sensor-bindings = <&inc_dec_kp UP DOWN &inc_dec_kp UP DOWN>; - slider-bindings = <&slv &slv>; - trackball-bindings = <&tmv>; + slider-bindings = <&slv &skp2 LC(LS(TAB)) LC(TAB)>; + trackball-bindings = <&tkp2 LC(TAB) LC(LS(TAB)) LG(PG_UP) LG(PG_DN)>; }; function_layer { @@ -321,7 +321,7 @@ >; sensor-bindings = <&inc_dec_kp UP DOWN &inc_dec_kp UP DOWN>; slider-bindings = <&slv &slv>; - trackball-bindings = <&tmv>; + trackball-bindings = <&tkp2 LC(LS(TAB)) LC(TAB) LG(PG_UP) LG(PG_DN)>; }; }; diff --git a/app/boards/shields/blade/blade_right.conf b/app/boards/shields/blade/blade_right.conf index 2c4cc606a7f..212d97c9cd8 100644 --- a/app/boards/shields/blade/blade_right.conf +++ b/app/boards/shields/blade/blade_right.conf @@ -59,9 +59,9 @@ CONFIG_PMW3610_CPI_DIVIDOR=4 CONFIG_PMW3610_ORIENTATION_90=y CONFIG_PMW3610_REST1_SAMPLE_TIME_MS=100 CONFIG_PMW3610_REST2_SAMPLE_TIME_MS=200 -CONFIG_PMW3610_REST3_SAMPLE_TIME_MS=300 +CONFIG_PMW3610_REST3_SAMPLE_TIME_MS=250 CONFIG_PMW3610_RUN_DOWNSHIFT_TIME_MS=500 -CONFIG_PMW3610_REST1_DOWNSHIFT_TIME_MS=3000 +CONFIG_PMW3610_REST1_DOWNSHIFT_TIME_MS=5000 CONFIG_PMW3610_REST2_DOWNSHIFT_TIME_MS=30000 ### Bluetooth ### From 1b17dfe4cdd000c30505305c489712b192bffba6 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Tue, 25 Oct 2022 08:07:54 +0800 Subject: [PATCH 120/157] [blade] tuning trackball parameters --- app/boards/shields/blade/blade.keymap | 2 +- app/boards/shields/blade/blade_right.conf | 4 ++-- app/dts/behaviors/slider.dtsi | 2 +- app/dts/behaviors/trackball.dtsi | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/boards/shields/blade/blade.keymap b/app/boards/shields/blade/blade.keymap index 99475fae23e..7d7903eaaa4 100644 --- a/app/boards/shields/blade/blade.keymap +++ b/app/boards/shields/blade/blade.keymap @@ -307,7 +307,7 @@ >; sensor-bindings = <&inc_dec_kp UP DOWN &inc_dec_kp UP DOWN>; slider-bindings = <&slv &skp2 LC(LS(TAB)) LC(TAB)>; - trackball-bindings = <&tkp2 LC(TAB) LC(LS(TAB)) LG(PG_UP) LG(PG_DN)>; + trackball-bindings = <&tkp LC(TAB) LC(LS(TAB)) LG(PG_UP) LG(PG_DN)>; }; function_layer { diff --git a/app/boards/shields/blade/blade_right.conf b/app/boards/shields/blade/blade_right.conf index 212d97c9cd8..3bcefb53ed5 100644 --- a/app/boards/shields/blade/blade_right.conf +++ b/app/boards/shields/blade/blade_right.conf @@ -54,8 +54,8 @@ CONFIG_ZMK_MOUSE_WORK_QUEUE_DEDICATED=y CONFIG_ZMK_MOUSE_DEDICATED_THREAD_PRIORITY=1 CONFIG_PMW3610=y -CONFIG_PMW3610_CPI=3200 -CONFIG_PMW3610_CPI_DIVIDOR=4 +CONFIG_PMW3610_CPI=2000 +CONFIG_PMW3610_CPI_DIVIDOR=1 CONFIG_PMW3610_ORIENTATION_90=y CONFIG_PMW3610_REST1_SAMPLE_TIME_MS=100 CONFIG_PMW3610_REST2_SAMPLE_TIME_MS=200 diff --git a/app/dts/behaviors/slider.dtsi b/app/dts/behaviors/slider.dtsi index 7d46e859673..15a5cea633b 100644 --- a/app/dts/behaviors/slider.dtsi +++ b/app/dts/behaviors/slider.dtsi @@ -27,7 +27,7 @@ mode = "scroll-mode"; flavor = "y-only"; scale_mode = "multiplier"; - scale_factor = <10>; + scale_factor = <25>; }; }; diff --git a/app/dts/behaviors/trackball.dtsi b/app/dts/behaviors/trackball.dtsi index 50135ec7a8c..d9fb8816c73 100644 --- a/app/dts/behaviors/trackball.dtsi +++ b/app/dts/behaviors/trackball.dtsi @@ -19,7 +19,7 @@ mode = "scroll-mode"; flavor = "default"; scale_mode = "dividor"; - scale_factor = <3>; + scale_factor = <13>; }; }; @@ -30,7 +30,7 @@ #trackball-binding-cells = <4>; mode = "distance-mode"; flavor = "2-dim"; - step_size = <100>; + step_size = <160>; }; }; From ff5b38c53b2412844f82259dcdf936b94f354cc5 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Fri, 28 Oct 2022 09:57:07 +0800 Subject: [PATCH 121/157] [point-device] behaveior default parameter tuning --- app/boards/shields/blade/blade.keymap | 75 ++++++++++++++++++++--- app/boards/shields/blade/blade_right.conf | 3 +- app/dts/behaviors/trackball.dtsi | 73 ++++++++++++++++++++-- 3 files changed, 137 insertions(+), 14 deletions(-) diff --git a/app/boards/shields/blade/blade.keymap b/app/boards/shields/blade/blade.keymap index 7d7903eaaa4..9a570c90777 100644 --- a/app/boards/shields/blade/blade.keymap +++ b/app/boards/shields/blade/blade.keymap @@ -14,6 +14,10 @@ #define NUM 1 #define SYM 2 #define FUN 3 +#define PTD_CRS 4 +#define PTD_NAV 5 +#define PTD_HOR 6 +#define PTD_VER 7 &mt { flavor = "tap-preferred"; @@ -271,11 +275,11 @@ default_layer { label = "CLMK"; bindings = < - &kp Q &kp W &kp F &kp P &kp B &kp J &kp L &kp U &kp Y &kp SQT - < SYM A &mt LALT R &mt LSFT S &mt LCTL T &kp G &kp M &mt RCTL N &mt RSFT E &mt RALT I < SYM O - &kp Z &kp X &kp C &kp D &kp V &kp K &kp H &cmqus &dtsmi < FUN TAB - &none &none &hello < FUN BSPC < NUM SPACE < NUM RET < FUN BSPC &encoder &none &none - &none &none &none + < PTD_NAV Q &kp W &kp F &kp P &kp B &kp J &kp L &kp U &kp Y < PTD_NAV SQT + < SYM A &mt LALT R &mt LSFT S &mt LCTL T &kp G &kp M &mt RCTL N &mt RSFT E &mt RALT I < SYM O + < PTD_HOR Z < PTD_VER X &kp C < PTD_CRS D &kp V &kp K < PTD_CRS H &cmqus &dtsmi < FUN TAB + &none &none &hello < FUN BSPC < NUM SPACE < NUM RET < FUN BSPC &encoder &none &none + &none &none &mkp LCLK >; sensor-bindings = <&inc_dec_kp UP DOWN &inc_dec_kp UP DOWN>; slider-bindings = <&slv &slv>; @@ -292,7 +296,7 @@ &none &none &none >; sensor-bindings = <&inc_dec_kp UP DOWN &inc_dec_kp UP DOWN>; - slider-bindings = <&slv &skp2 LC(PLUS) LC(MINUS)>; + slider-bindings = <&slv &skp2 LC(MINUS) LC(PLUS)>; trackball-bindings = <&tsl>; }; @@ -307,7 +311,7 @@ >; sensor-bindings = <&inc_dec_kp UP DOWN &inc_dec_kp UP DOWN>; slider-bindings = <&slv &skp2 LC(LS(TAB)) LC(TAB)>; - trackball-bindings = <&tkp LC(TAB) LC(LS(TAB)) LG(PG_UP) LG(PG_DN)>; + trackball-bindings = <&tkp BSPC DELETE LC(MINUS) LC(PLUS)>; }; function_layer { @@ -321,8 +325,63 @@ >; sensor-bindings = <&inc_dec_kp UP DOWN &inc_dec_kp UP DOWN>; slider-bindings = <&slv &slv>; - trackball-bindings = <&tkp2 LC(LS(TAB)) LC(TAB) LG(PG_UP) LG(PG_DN)>; + trackball-bindings = <&tkp_fast RIGHT LEFT UP DOWN>; }; + ptd_coarse_layer { + label = "PTD_COARSE"; + bindings = < + &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans + &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans + &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans + &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans + &trans &trans &trans + >; + sensor-bindings = <&inc_dec_kp UP DOWN &inc_dec_kp UP DOWN>; + slider-bindings = <&slv &slv>; + trackball-bindings = <&tmv_coarse>; + }; + + ptd_navigation_layer { + label = "PTD_NAVIGATION"; + bindings = < + &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans + &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans + &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans + &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans + &trans &trans &trans + >; + sensor-bindings = <&inc_dec_kp UP DOWN &inc_dec_kp UP DOWN>; + slider-bindings = <&slv &slv>; + trackball-bindings = <&tkp LC(TAB) LC(LS(TAB)) LG(PG_UP) LG(PG_DN)>; + }; + + ptd_horizontal_layer { + label = "PTD_HORIZONTAL"; + bindings = < + &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans + &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans + &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans + &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans + &trans &trans &trans + >; + sensor-bindings = <&inc_dec_kp UP DOWN &inc_dec_kp UP DOWN>; + slider-bindings = <&slv &slv>; + trackball-bindings = <&tmv_x>; + }; + + ptd_vertical_layer { + label = "PTD_VERTICAL"; + bindings = < + &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans + &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans + &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans + &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans + &trans &trans &trans + >; + sensor-bindings = <&inc_dec_kp UP DOWN &inc_dec_kp UP DOWN>; + slider-bindings = <&slv &slv>; + trackball-bindings = <&tmv_y>; + }; }; }; \ No newline at end of file diff --git a/app/boards/shields/blade/blade_right.conf b/app/boards/shields/blade/blade_right.conf index 3bcefb53ed5..19a9788175e 100644 --- a/app/boards/shields/blade/blade_right.conf +++ b/app/boards/shields/blade/blade_right.conf @@ -41,7 +41,8 @@ CONFIG_NRFMACRO_LCD_DISPLAY=y CONFIG_BT_CTLR_TX_PWR_PLUS_8=y ##### Slider #### -CONFIG_CAP1203_SLIDER_MODE=y +# CONFIG_CAP1203_SLIDER_MODE=y +CONFIG_CAP1203_MIX_MODE=y ##### Point Devices #### # CONFIG_ZMK_PD_WORK_QUEUE_DEDICATED=n diff --git a/app/dts/behaviors/trackball.dtsi b/app/dts/behaviors/trackball.dtsi index d9fb8816c73..38f852e4bb3 100644 --- a/app/dts/behaviors/trackball.dtsi +++ b/app/dts/behaviors/trackball.dtsi @@ -1,4 +1,6 @@ / { + + /* trackball move */ behaviors { /omit-if-no-ref/ tmv: behavior_trackball_move { compatible = "zmk,behavior-point-device-incremental"; @@ -6,11 +8,60 @@ #trackball-binding-cells = <0>; mode = "move-mode"; flavor = "default"; - scale_mode = "multiplier"; + scale_mode = "dividor"; + scale_factor = <2>; + }; + }; + + behaviors { + /omit-if-no-ref/ tmv_fine: behavior_trackball_move_fine { + compatible = "zmk,behavior-point-device-incremental"; + label = "TRACKBALL_MOVE_FINE"; + #trackball-binding-cells = <0>; + mode = "move-mode"; + flavor = "default"; + scale_mode = "dividor"; + scale_factor = <4>; + }; + }; + + behaviors { + /omit-if-no-ref/ tmv_coarse: behavior_trackball_move_coarse { + compatible = "zmk,behavior-point-device-incremental"; + label = "TRACKBALL_MOVE_COARSE"; + #trackball-binding-cells = <0>; + mode = "move-mode"; + flavor = "default"; + scale_mode = "dividor"; scale_factor = <1>; }; }; + behaviors { + /omit-if-no-ref/ tmv_x: behavior_trackball_move_x_only { + compatible = "zmk,behavior-point-device-incremental"; + label = "TRACKBALL_MOVE_X_ONLY"; + #trackball-binding-cells = <0>; + mode = "move-mode"; + flavor = "x-only"; + scale_mode = "dividor"; + scale_factor = <2>; + }; + }; + + behaviors { + /omit-if-no-ref/ tmv_y: behavior_trackball_move_y_only { + compatible = "zmk,behavior-point-device-incremental"; + label = "TRACKBALL_MOVE_Y_ONLY"; + #trackball-binding-cells = <0>; + mode = "move-mode"; + flavor = "y-only"; + scale_mode = "dividor"; + scale_factor = <2>; + }; + }; + + /* trackball scroll */ behaviors { /omit-if-no-ref/ tsl: behavior_trackball_scroll { compatible = "zmk,behavior-point-device-incremental"; @@ -19,10 +70,11 @@ mode = "scroll-mode"; flavor = "default"; scale_mode = "dividor"; - scale_factor = <13>; + scale_factor = <10>; }; }; + /* trackball key press */ behaviors { /omit-if-no-ref/ tkp: behavior_trackball_key_press { compatible = "zmk,behavior-point-device-directional"; @@ -30,18 +82,29 @@ #trackball-binding-cells = <4>; mode = "distance-mode"; flavor = "2-dim"; - step_size = <160>; + step_size = <200>; + }; + }; + + behaviors { + /omit-if-no-ref/ tkp_fast: behavior_trackball_key_press { + compatible = "zmk,behavior-point-device-directional"; + label = "TRACKBALL_KEY_PRESS"; + #trackball-binding-cells = <4>; + mode = "distance-mode"; + flavor = "2-dim"; + step_size = <100>; }; }; behaviors { - /omit-if-no-ref/ tkp2: behavior_trackball_key_press2 { + /omit-if-no-ref/ tkp_dt: behavior_trackball_key_press2 { compatible = "zmk,behavior-point-device-directional"; label = "TRACKBALL_KEY_PRESS_2"; #trackball-binding-cells = <4>; mode = "time-mode"; flavor = "2-dim"; - step_size = <150>; + step_size = <200>; }; }; }; From a1e7f2600c2cd8b0541b295ca830c5c2633328ef Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Fri, 28 Oct 2022 10:12:38 +0800 Subject: [PATCH 122/157] backup --- app/dts/behaviors/trackball.dtsi | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/app/dts/behaviors/trackball.dtsi b/app/dts/behaviors/trackball.dtsi index 38f852e4bb3..def5ad23076 100644 --- a/app/dts/behaviors/trackball.dtsi +++ b/app/dts/behaviors/trackball.dtsi @@ -74,6 +74,18 @@ }; }; + behaviors { + /omit-if-no-ref/ tsl_fine: behavior_trackball_scroll { + compatible = "zmk,behavior-point-device-incremental"; + label = "TRACKBALL_SCROLL_FINE"; + #trackball-binding-cells = <0>; + mode = "scroll-mode"; + flavor = "default"; + scale_mode = "dividor"; + scale_factor = <1>; + }; + }; + /* trackball key press */ behaviors { /omit-if-no-ref/ tkp: behavior_trackball_key_press { @@ -82,7 +94,7 @@ #trackball-binding-cells = <4>; mode = "distance-mode"; flavor = "2-dim"; - step_size = <200>; + step_size = <250>; }; }; From 5a9cdd7d54ed7e7f36318ad2114c9356e15c6411 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Fri, 28 Oct 2022 17:22:12 +0800 Subject: [PATCH 123/157] backup --- app/boards/shields/blade/blade.keymap | 4 ++-- app/boards/shields/blade/blade_right.conf | 4 ++-- app/dts/behaviors/trackball.dtsi | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/boards/shields/blade/blade.keymap b/app/boards/shields/blade/blade.keymap index 9a570c90777..bcf22980933 100644 --- a/app/boards/shields/blade/blade.keymap +++ b/app/boards/shields/blade/blade.keymap @@ -279,7 +279,7 @@ < SYM A &mt LALT R &mt LSFT S &mt LCTL T &kp G &kp M &mt RCTL N &mt RSFT E &mt RALT I < SYM O < PTD_HOR Z < PTD_VER X &kp C < PTD_CRS D &kp V &kp K < PTD_CRS H &cmqus &dtsmi < FUN TAB &none &none &hello < FUN BSPC < NUM SPACE < NUM RET < FUN BSPC &encoder &none &none - &none &none &mkp LCLK + &none &none &none >; sensor-bindings = <&inc_dec_kp UP DOWN &inc_dec_kp UP DOWN>; slider-bindings = <&slv &slv>; @@ -325,7 +325,7 @@ >; sensor-bindings = <&inc_dec_kp UP DOWN &inc_dec_kp UP DOWN>; slider-bindings = <&slv &slv>; - trackball-bindings = <&tkp_fast RIGHT LEFT UP DOWN>; + trackball-bindings = <&tkp_fast RIGHT LEFT DOWN UP>; }; ptd_coarse_layer { diff --git a/app/boards/shields/blade/blade_right.conf b/app/boards/shields/blade/blade_right.conf index 19a9788175e..33b5add8f67 100644 --- a/app/boards/shields/blade/blade_right.conf +++ b/app/boards/shields/blade/blade_right.conf @@ -41,8 +41,8 @@ CONFIG_NRFMACRO_LCD_DISPLAY=y CONFIG_BT_CTLR_TX_PWR_PLUS_8=y ##### Slider #### -# CONFIG_CAP1203_SLIDER_MODE=y -CONFIG_CAP1203_MIX_MODE=y +CONFIG_CAP1203_SLIDER_MODE=y +# CONFIG_CAP1203_MIX_MODE=y ##### Point Devices #### # CONFIG_ZMK_PD_WORK_QUEUE_DEDICATED=n diff --git a/app/dts/behaviors/trackball.dtsi b/app/dts/behaviors/trackball.dtsi index def5ad23076..cce8d498c2e 100644 --- a/app/dts/behaviors/trackball.dtsi +++ b/app/dts/behaviors/trackball.dtsi @@ -75,7 +75,7 @@ }; behaviors { - /omit-if-no-ref/ tsl_fine: behavior_trackball_scroll { + /omit-if-no-ref/ tsl_fine: behavior_trackball_scroll_fine { compatible = "zmk,behavior-point-device-incremental"; label = "TRACKBALL_SCROLL_FINE"; #trackball-binding-cells = <0>; From 3a6d6c05488df3cb2ccd28b0ffdf9850471e5b27 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Sat, 29 Oct 2022 14:12:18 +0800 Subject: [PATCH 124/157] backup --- app/boards/shields/blade/blade.keymap | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/app/boards/shields/blade/blade.keymap b/app/boards/shields/blade/blade.keymap index bcf22980933..b4c5d66ae23 100644 --- a/app/boards/shields/blade/blade.keymap +++ b/app/boards/shields/blade/blade.keymap @@ -18,6 +18,7 @@ #define PTD_NAV 5 #define PTD_HOR 6 #define PTD_VER 7 +#define PTD_FIN 8 &mt { flavor = "tap-preferred"; @@ -275,9 +276,9 @@ default_layer { label = "CLMK"; bindings = < - < PTD_NAV Q &kp W &kp F &kp P &kp B &kp J &kp L &kp U &kp Y < PTD_NAV SQT - < SYM A &mt LALT R &mt LSFT S &mt LCTL T &kp G &kp M &mt RCTL N &mt RSFT E &mt RALT I < SYM O - < PTD_HOR Z < PTD_VER X &kp C < PTD_CRS D &kp V &kp K < PTD_CRS H &cmqus &dtsmi < FUN TAB + < PTD_NAV Q &kp W &kp F &kp P &kp B &kp J &kp L &kp U &kp Y < PTD_NAV SQT + < SYM A &mt LALT R &mt LSFT S &mt LCTL T &kp G &kp M &mt RCTL N &mt RSFT E &mt RALT I < SYM O + < PTD_HOR Z < PTD_VER X < PTD_FIN C < PTD_CRS D &kp V &kp K < PTD_CRS H &cmqus &dtsmi < FUN TAB &none &none &hello < FUN BSPC < NUM SPACE < NUM RET < FUN BSPC &encoder &none &none &none &none &none >; @@ -383,5 +384,19 @@ slider-bindings = <&slv &slv>; trackball-bindings = <&tmv_y>; }; + + ptd_fine_layer { + label = "PTD_FINE"; + bindings = < + &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans + &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans + &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans + &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans + &trans &trans &trans + >; + sensor-bindings = <&inc_dec_kp UP DOWN &inc_dec_kp UP DOWN>; + slider-bindings = <&slv &slv>; + trackball-bindings = <&tmv_fine>; + }; }; }; \ No newline at end of file From 9fa560b3ff2f87471825bc5d11adfd27d84bc6c4 Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Thu, 10 Nov 2022 17:59:24 +0800 Subject: [PATCH 125/157] [cap1203] add polling mode and activation by sliders.c solely Previously, cap1203 is only activated by the kscan module even used as pure slider. Now it is also activated by the sliders module, so no need to add it into kscan matrix when used as a pure slider. --- app/drivers/kscan/Kconfig | 19 +++++++++++++++++++ app/drivers/kscan/kscan_cap1203.c | 9 +++++++++ 2 files changed, 28 insertions(+) diff --git a/app/drivers/kscan/Kconfig b/app/drivers/kscan/Kconfig index bb36e0152f5..f0eb655701a 100644 --- a/app/drivers/kscan/Kconfig +++ b/app/drivers/kscan/Kconfig @@ -61,6 +61,25 @@ config CAP1203_MIX_MODE endchoice +choice + prompt "CAP1203 updating mechanism" + +config ZMK_KSCAN_CAP1203_INTERRUPT + prompt "Interrupt mode" + bool + +config ZMK_KSCAN_CAP1203_POLL + prompt "Polling mode" + bool + +endchoice + +if ZMK_KSCAN_CAP1203_POLL +config ZMK_KSCAN_CAP1203_PERIOD + int "Polling interval in ms" + default 10 +endif + endif # ZMK_KSCAN_CAP1203 if ZMK_KSCAN_GPIO_DRIVER diff --git a/app/drivers/kscan/kscan_cap1203.c b/app/drivers/kscan/kscan_cap1203.c index b4d70470abe..2969936c834 100644 --- a/app/drivers/kscan/kscan_cap1203.c +++ b/app/drivers/kscan/kscan_cap1203.c @@ -77,6 +77,7 @@ struct kscan_cap1203_data { const struct device *dev; kscan_callback_t callback; // zmk's kscan callback + struct k_timer timer; struct k_work work; // actual processing work struct gpio_callback int_gpio_cb; // alert gpio pin callback uint8_t touch_state; // current sensor status @@ -476,10 +477,12 @@ static int kscan_cap1203_enable_callback(const struct device *dev) const struct kscan_cap1203_config *config = dev->config; if (config->int_gpio.port != NULL) { + LOG_DBG("enable interrupt callback"); gpio_add_callback(config->int_gpio.port, &data->int_gpio_cb); } #if USE_POLLING else { + LOG_DBG("enable timer callback"); k_timer_start(&data->timer, K_MSEC(CONFIG_ZMK_KSCAN_CAP1203_PERIOD), K_MSEC(CONFIG_ZMK_KSCAN_CAP1203_PERIOD)); } @@ -494,10 +497,12 @@ static int kscan_cap1203_disable_callback(const struct device *dev) const struct kscan_cap1203_config *config = dev->config; if (config->int_gpio.port != NULL) { + LOG_DBG("disable interrupt callback"); gpio_remove_callback(config->int_gpio.port, &data->int_gpio_cb); } #if USE_POLLING else { + LOG_DBG("disable timer callback"); k_timer_stop(&data->timer); } #endif @@ -523,6 +528,7 @@ static int kscan_cap1203_init(const struct device *dev) // init alert interrupt pin or poll timer if (config->int_gpio.port != NULL) { + LOG_DBG("Init interrupt mode"); if (!device_is_ready(config->int_gpio.port)) { LOG_ERR("Interrupt GPIO controller device not ready"); return -ENODEV; @@ -546,6 +552,7 @@ static int kscan_cap1203_init(const struct device *dev) } #if USE_POLLING else { + LOG_DBG("Init poll mode"); k_timer_init(&data->timer, kscan_cap1203_timer_handler, NULL); // disable alert function in poll mode @@ -592,6 +599,8 @@ static void kscan_cap1203_slider_configure(const struct device *dev, struct slider_data *slider_data = dev->data; slider_data->callback = callback; slider_data->id = id; + + kscan_cap1203_enable_callback(dev); } static struct kscan_slider_api kscan_cap1203_slider_api = { From acc9e98f4885435a49967639aebaf2c247e264cd Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Fri, 30 Dec 2022 17:36:08 +0800 Subject: [PATCH 126/157] [pmw3360] update orientation layout --- app/drivers/sensor/pixart/pmw3360/pmw3360.c | 1793 +++++++++---------- 1 file changed, 848 insertions(+), 945 deletions(-) diff --git a/app/drivers/sensor/pixart/pmw3360/pmw3360.c b/app/drivers/sensor/pixart/pmw3360/pmw3360.c index c67e6a245fe..34cf12a9846 100644 --- a/app/drivers/sensor/pixart/pmw3360/pmw3360.c +++ b/app/drivers/sensor/pixart/pmw3360/pmw3360.c @@ -14,127 +14,123 @@ LOG_MODULE_REGISTER(pmw3360, CONFIG_PMW3360_LOG_LEVEL); /* Timings defined by spec (in us) */ -#define T_NCS_SCLK 1 /* 120 ns (rounded to 1us?)*/ -#define T_SRX (20 - T_NCS_SCLK) /* 20 us */ -#define T_SCLK_NCS_WR (35 - T_NCS_SCLK) /* 35 us */ -#define T_SWX (180 - T_SCLK_NCS_WR) /* 180 us */ -#define T_SRAD 160 /* 160 us */ -#define T_SRAD_MOTBR 35 /* 35 us */ -#define T_BEXIT 1 /* 500 ns (rounded to 1us?)*/ +#define T_NCS_SCLK 1 /* 120 ns (rounded to 1us?)*/ +#define T_SRX (20 - T_NCS_SCLK) /* 20 us */ +#define T_SCLK_NCS_WR (35 - T_NCS_SCLK) /* 35 us */ +#define T_SWX (180 - T_SCLK_NCS_WR) /* 180 us */ +#define T_SRAD 160 /* 160 us */ +#define T_SRAD_MOTBR 35 /* 35 us */ +#define T_BEXIT 1 /* 500 ns (rounded to 1us?)*/ /* Timing defined on SROM download burst mode figure */ -#define T_BRSEP 15 /* 15 us */ - +#define T_BRSEP 15 /* 15 us */ /* Sensor registers */ -#define PMW3360_REG_PRODUCT_ID 0x00 -#define PMW3360_REG_REVISION_ID 0x01 -#define PMW3360_REG_MOTION 0x02 -#define PMW3360_REG_DELTA_X_L 0x03 -#define PMW3360_REG_DELTA_X_H 0x04 -#define PMW3360_REG_DELTA_Y_L 0x05 -#define PMW3360_REG_DELTA_Y_H 0x06 -#define PMW3360_REG_SQUAL 0x07 -#define PMW3360_REG_RAW_DATA_SUM 0x08 -#define PMW3360_REG_MAXIMUM_RAW_DATA 0x09 -#define PMW3360_REG_MINIMUM_RAW_DATA 0x0A -#define PMW3360_REG_SHUTTER_LOWER 0x0B -#define PMW3360_REG_SHUTTER_UPPER 0x0C -#define PMW3360_REG_CONTROL 0x0D -#define PMW3360_REG_CONFIG1 0x0F -#define PMW3360_REG_CONFIG2 0x10 -#define PMW3360_REG_ANGLE_TUNE 0x11 -#define PMW3360_REG_FRAME_CAPTURE 0x12 -#define PMW3360_REG_SROM_ENABLE 0x13 -#define PMW3360_REG_RUN_DOWNSHIFT 0x14 -#define PMW3360_REG_REST1_RATE_LOWER 0x15 -#define PMW3360_REG_REST1_RATE_UPPER 0x16 -#define PMW3360_REG_REST1_DOWNSHIFT 0x17 -#define PMW3360_REG_REST2_RATE_LOWER 0x18 -#define PMW3360_REG_REST2_RATE_UPPER 0x19 -#define PMW3360_REG_REST2_DOWNSHIFT 0x1A -#define PMW3360_REG_REST3_RATE_LOWER 0x1B -#define PMW3360_REG_REST3_RATE_UPPER 0x1C -#define PMW3360_REG_OBSERVATION 0x24 -#define PMW3360_REG_DATA_OUT_LOWER 0x25 -#define PMW3360_REG_DATA_OUT_UPPER 0x26 -#define PMW3360_REG_RAW_DATA_DUMP 0x29 -#define PMW3360_REG_SROM_ID 0x2A -#define PMW3360_REG_MIN_SQ_RUN 0x2B -#define PMW3360_REG_RAW_DATA_THRESHOLD 0x2C -#define PMW3360_REG_CONFIG5 0x2F -#define PMW3360_REG_POWER_UP_RESET 0x3A -#define PMW3360_REG_SHUTDOWN 0x3B -#define PMW3360_REG_INVERSE_PRODUCT_ID 0x3F -#define PMW3360_REG_LIFTCUTOFF_TUNE3 0x41 -#define PMW3360_REG_ANGLE_SNAP 0x42 -#define PMW3360_REG_LIFTCUTOFF_TUNE1 0x4A -#define PMW3360_REG_MOTION_BURST 0x50 -#define PMW3360_REG_LIFTCUTOFF_TUNE_TIMEOUT 0x58 -#define PMW3360_REG_LIFTCUTOFF_TUNE_MIN_LENGTH 0x5A -#define PMW3360_REG_SROM_LOAD_BURST 0x62 -#define PMW3360_REG_LIFT_CONFIG 0x63 -#define PMW3360_REG_RAW_DATA_BURST 0x64 -#define PMW3360_REG_LIFTCUTOFF_TUNE2 0x65 +#define PMW3360_REG_PRODUCT_ID 0x00 +#define PMW3360_REG_REVISION_ID 0x01 +#define PMW3360_REG_MOTION 0x02 +#define PMW3360_REG_DELTA_X_L 0x03 +#define PMW3360_REG_DELTA_X_H 0x04 +#define PMW3360_REG_DELTA_Y_L 0x05 +#define PMW3360_REG_DELTA_Y_H 0x06 +#define PMW3360_REG_SQUAL 0x07 +#define PMW3360_REG_RAW_DATA_SUM 0x08 +#define PMW3360_REG_MAXIMUM_RAW_DATA 0x09 +#define PMW3360_REG_MINIMUM_RAW_DATA 0x0A +#define PMW3360_REG_SHUTTER_LOWER 0x0B +#define PMW3360_REG_SHUTTER_UPPER 0x0C +#define PMW3360_REG_CONTROL 0x0D +#define PMW3360_REG_CONFIG1 0x0F +#define PMW3360_REG_CONFIG2 0x10 +#define PMW3360_REG_ANGLE_TUNE 0x11 +#define PMW3360_REG_FRAME_CAPTURE 0x12 +#define PMW3360_REG_SROM_ENABLE 0x13 +#define PMW3360_REG_RUN_DOWNSHIFT 0x14 +#define PMW3360_REG_REST1_RATE_LOWER 0x15 +#define PMW3360_REG_REST1_RATE_UPPER 0x16 +#define PMW3360_REG_REST1_DOWNSHIFT 0x17 +#define PMW3360_REG_REST2_RATE_LOWER 0x18 +#define PMW3360_REG_REST2_RATE_UPPER 0x19 +#define PMW3360_REG_REST2_DOWNSHIFT 0x1A +#define PMW3360_REG_REST3_RATE_LOWER 0x1B +#define PMW3360_REG_REST3_RATE_UPPER 0x1C +#define PMW3360_REG_OBSERVATION 0x24 +#define PMW3360_REG_DATA_OUT_LOWER 0x25 +#define PMW3360_REG_DATA_OUT_UPPER 0x26 +#define PMW3360_REG_RAW_DATA_DUMP 0x29 +#define PMW3360_REG_SROM_ID 0x2A +#define PMW3360_REG_MIN_SQ_RUN 0x2B +#define PMW3360_REG_RAW_DATA_THRESHOLD 0x2C +#define PMW3360_REG_CONFIG5 0x2F +#define PMW3360_REG_POWER_UP_RESET 0x3A +#define PMW3360_REG_SHUTDOWN 0x3B +#define PMW3360_REG_INVERSE_PRODUCT_ID 0x3F +#define PMW3360_REG_LIFTCUTOFF_TUNE3 0x41 +#define PMW3360_REG_ANGLE_SNAP 0x42 +#define PMW3360_REG_LIFTCUTOFF_TUNE1 0x4A +#define PMW3360_REG_MOTION_BURST 0x50 +#define PMW3360_REG_LIFTCUTOFF_TUNE_TIMEOUT 0x58 +#define PMW3360_REG_LIFTCUTOFF_TUNE_MIN_LENGTH 0x5A +#define PMW3360_REG_SROM_LOAD_BURST 0x62 +#define PMW3360_REG_LIFT_CONFIG 0x63 +#define PMW3360_REG_RAW_DATA_BURST 0x64 +#define PMW3360_REG_LIFTCUTOFF_TUNE2 0x65 /* Sensor identification values */ -#define PMW3360_PRODUCT_ID 0x42 -#define PMW3360_FIRMWARE_ID 0x04 +#define PMW3360_PRODUCT_ID 0x42 +#define PMW3360_FIRMWARE_ID 0x04 /* Power-up register commands */ -#define PMW3360_POWERUP_CMD_RESET 0x5A +#define PMW3360_POWERUP_CMD_RESET 0x5A /* Max register count readable in a single motion burst */ -#define PMW3360_MAX_BURST_SIZE 12 +#define PMW3360_MAX_BURST_SIZE 12 /* Register count used for reading a single motion burst */ -#define PMW3360_BURST_SIZE 6 +#define PMW3360_BURST_SIZE 6 /* Position of X in motion burst data */ -#define PMW3360_DX_POS 2 -#define PMW3360_DY_POS 4 +#define PMW3360_DX_POS 2 +#define PMW3360_DY_POS 4 /* Rest_En position in Config2 register. */ -#define PMW3360_REST_EN_POS 5 - -#define PMW3360_MAX_CPI 12000 -#define PMW3360_MIN_CPI 100 +#define PMW3360_REST_EN_POS 5 +#define PMW3360_MAX_CPI 12000 +#define PMW3360_MIN_CPI 100 -#define SPI_WRITE_BIT BIT(7) +#define SPI_WRITE_BIT BIT(7) /* Helper macros used to convert sensor values. */ #define PMW3360_SVALUE_TO_CPI(svalue) ((uint32_t)(svalue).val1) #define PMW3360_SVALUE_TO_TIME(svalue) ((uint32_t)(svalue).val1) #define PMW3360_SVALUE_TO_BOOL(svalue) ((svalue).val1 != 0) - /* SROM firmware meta-data, defined in pmw3360_piv.c */ extern const size_t pmw3360_firmware_length; extern const uint8_t pmw3360_firmware_data[]; - /* sensor initialization steps definition */ // init is done in non-blocking manner (i.e., async), a delayable work is defined for this job // see pmw3360_init and pmw3360_async_init) enum async_init_step { - ASYNC_INIT_STEP_POWER_UP, // power up reset - ASYNC_INIT_STEP_FW_LOAD_START, // clear motion registers, disable REST mode, enable SROM register - ASYNC_INIT_STEP_FW_LOAD_CONTINUE, // start SROM download - ASYNC_INIT_STEP_FW_LOAD_VERIFY, // verify SROM pid and fid, enable REST mode - ASYNC_INIT_STEP_CONFIGURE, // set cpi and donwshift time (run, rest1, rest2) - - ASYNC_INIT_STEP_COUNT // end flag + ASYNC_INIT_STEP_POWER_UP, // power up reset + ASYNC_INIT_STEP_FW_LOAD_START, // clear motion registers, disable REST mode, enable SROM + // register + ASYNC_INIT_STEP_FW_LOAD_CONTINUE, // start SROM download + ASYNC_INIT_STEP_FW_LOAD_VERIFY, // verify SROM pid and fid, enable REST mode + ASYNC_INIT_STEP_CONFIGURE, // set cpi and donwshift time (run, rest1, rest2) + + ASYNC_INIT_STEP_COUNT // end flag }; // delay (ms) in between steps static const int32_t async_init_delay[ASYNC_INIT_STEP_COUNT] = { - [ASYNC_INIT_STEP_POWER_UP] = 1, - [ASYNC_INIT_STEP_FW_LOAD_START] = 50, // required in spec - [ASYNC_INIT_STEP_FW_LOAD_CONTINUE] = 10, // required in spec - [ASYNC_INIT_STEP_FW_LOAD_VERIFY] = 1, - [ASYNC_INIT_STEP_CONFIGURE] = 0, + [ASYNC_INIT_STEP_POWER_UP] = 1, + [ASYNC_INIT_STEP_FW_LOAD_START] = 50, // required in spec + [ASYNC_INIT_STEP_FW_LOAD_CONTINUE] = 10, // required in spec + [ASYNC_INIT_STEP_FW_LOAD_VERIFY] = 1, [ASYNC_INIT_STEP_CONFIGURE] = 0, }; static int pmw3360_async_init_power_up(const struct device *dev); @@ -143,786 +139,714 @@ static int pmw3360_async_init_fw_load_verify(const struct device *dev); static int pmw3360_async_init_fw_load_continue(const struct device *dev); static int pmw3360_async_init_fw_load_start(const struct device *dev); -static int (* const async_init_fn[ASYNC_INIT_STEP_COUNT])(const struct device *dev) = { - [ASYNC_INIT_STEP_POWER_UP] = pmw3360_async_init_power_up, - [ASYNC_INIT_STEP_FW_LOAD_START] = pmw3360_async_init_fw_load_start, - [ASYNC_INIT_STEP_FW_LOAD_CONTINUE] = pmw3360_async_init_fw_load_continue, - [ASYNC_INIT_STEP_FW_LOAD_VERIFY] = pmw3360_async_init_fw_load_verify, - [ASYNC_INIT_STEP_CONFIGURE] = pmw3360_async_init_configure, +static int (*const async_init_fn[ASYNC_INIT_STEP_COUNT])(const struct device *dev) = { + [ASYNC_INIT_STEP_POWER_UP] = pmw3360_async_init_power_up, + [ASYNC_INIT_STEP_FW_LOAD_START] = pmw3360_async_init_fw_load_start, + [ASYNC_INIT_STEP_FW_LOAD_CONTINUE] = pmw3360_async_init_fw_load_continue, + [ASYNC_INIT_STEP_FW_LOAD_VERIFY] = pmw3360_async_init_fw_load_verify, + [ASYNC_INIT_STEP_CONFIGURE] = pmw3360_async_init_configure, }; -static int spi_cs_ctrl(const struct device *dev, bool enable) -{ - const struct pixart_config *config = dev->config; - int err; +static int spi_cs_ctrl(const struct device *dev, bool enable) { + const struct pixart_config *config = dev->config; + int err; - if (!enable) { - k_busy_wait(T_NCS_SCLK); - } + if (!enable) { + k_busy_wait(T_NCS_SCLK); + } - err = gpio_pin_set_dt(&config->cs_gpio, (int)enable); - if (err) { - LOG_ERR("SPI CS ctrl failed"); - } + err = gpio_pin_set_dt(&config->cs_gpio, (int)enable); + if (err) { + LOG_ERR("SPI CS ctrl failed"); + } - if (enable) { - k_busy_wait(T_NCS_SCLK); - } + if (enable) { + k_busy_wait(T_NCS_SCLK); + } - return err; + return err; } -static int reg_read(const struct device *dev, uint8_t reg, uint8_t *buf) -{ - int err; - struct pixart_data *data = dev->data; - const struct pixart_config *config = dev->config; - - __ASSERT_NO_MSG((reg & SPI_WRITE_BIT) == 0); - - err = spi_cs_ctrl(dev, true); - if (err) { - return err; - } - - /* Write register address. */ - const struct spi_buf tx_buf = { - .buf = ®, - .len = 1 - }; - const struct spi_buf_set tx = { - .buffers = &tx_buf, - .count = 1 - }; - - err = spi_write_dt(&config->bus, &tx); - if (err) { - LOG_ERR("Reg read failed on SPI write"); - return err; - } - - k_busy_wait(T_SRAD); - - /* Read register value. */ - struct spi_buf rx_buf = { - .buf = buf, - .len = 1, - }; - const struct spi_buf_set rx = { - .buffers = &rx_buf, - .count = 1, - }; - - err = spi_read_dt(&config->bus, &rx); - if (err) { - LOG_ERR("Reg read failed on SPI read"); - return err; - } - - err = spi_cs_ctrl(dev, false); - if (err) { - return err; - } - - k_busy_wait(T_SRX); - - data->last_read_burst = false; - - return 0; +static int reg_read(const struct device *dev, uint8_t reg, uint8_t *buf) { + int err; + struct pixart_data *data = dev->data; + const struct pixart_config *config = dev->config; + + __ASSERT_NO_MSG((reg & SPI_WRITE_BIT) == 0); + + err = spi_cs_ctrl(dev, true); + if (err) { + return err; + } + + /* Write register address. */ + const struct spi_buf tx_buf = {.buf = ®, .len = 1}; + const struct spi_buf_set tx = {.buffers = &tx_buf, .count = 1}; + + err = spi_write_dt(&config->bus, &tx); + if (err) { + LOG_ERR("Reg read failed on SPI write"); + return err; + } + + k_busy_wait(T_SRAD); + + /* Read register value. */ + struct spi_buf rx_buf = { + .buf = buf, + .len = 1, + }; + const struct spi_buf_set rx = { + .buffers = &rx_buf, + .count = 1, + }; + + err = spi_read_dt(&config->bus, &rx); + if (err) { + LOG_ERR("Reg read failed on SPI read"); + return err; + } + + err = spi_cs_ctrl(dev, false); + if (err) { + return err; + } + + k_busy_wait(T_SRX); + + data->last_read_burst = false; + + return 0; } -static int reg_write(const struct device *dev, uint8_t reg, uint8_t val) -{ - int err; - struct pixart_data *data = dev->data; - const struct pixart_config *config = dev->config; - - __ASSERT_NO_MSG((reg & SPI_WRITE_BIT) == 0); - - err = spi_cs_ctrl(dev, true); - if (err) { - return err; - } - - uint8_t buf[] = { - SPI_WRITE_BIT | reg, - val - }; - const struct spi_buf tx_buf = { - .buf = buf, - .len = ARRAY_SIZE(buf) - }; - const struct spi_buf_set tx = { - .buffers = &tx_buf, - .count = 1 - }; - - err = spi_write_dt(&config->bus, &tx); - if (err) { - LOG_ERR("Reg write failed on SPI write"); - return err; - } - - k_busy_wait(T_SCLK_NCS_WR); - - err = spi_cs_ctrl(dev, false); - if (err) { - return err; - } - - k_busy_wait(T_SWX); - - data->last_read_burst = false; - - return 0; +static int reg_write(const struct device *dev, uint8_t reg, uint8_t val) { + int err; + struct pixart_data *data = dev->data; + const struct pixart_config *config = dev->config; + + __ASSERT_NO_MSG((reg & SPI_WRITE_BIT) == 0); + + err = spi_cs_ctrl(dev, true); + if (err) { + return err; + } + + uint8_t buf[] = {SPI_WRITE_BIT | reg, val}; + const struct spi_buf tx_buf = {.buf = buf, .len = ARRAY_SIZE(buf)}; + const struct spi_buf_set tx = {.buffers = &tx_buf, .count = 1}; + + err = spi_write_dt(&config->bus, &tx); + if (err) { + LOG_ERR("Reg write failed on SPI write"); + return err; + } + + k_busy_wait(T_SCLK_NCS_WR); + + err = spi_cs_ctrl(dev, false); + if (err) { + return err; + } + + k_busy_wait(T_SWX); + + data->last_read_burst = false; + + return 0; } -static int motion_burst_read(const struct device *dev, uint8_t *buf, - size_t burst_size) -{ - int err; - struct pixart_data *data = dev->data; - const struct pixart_config *config = dev->config; - - __ASSERT_NO_MSG(burst_size <= PMW3360_MAX_BURST_SIZE); - - /* Write any value to motion burst register only if there have been - * other SPI transmissions with sensor since last burst read. - */ - if (!data->last_read_burst) { - err = reg_write(dev, PMW3360_REG_MOTION_BURST, 0x00); - if (err) { - return err; - } - } - - err = spi_cs_ctrl(dev, true); - if (err) { - return err; - } - - /* Send motion burst address */ - uint8_t reg_buf[] = { - PMW3360_REG_MOTION_BURST - }; - const struct spi_buf tx_buf = { - .buf = reg_buf, - .len = ARRAY_SIZE(reg_buf) - }; - const struct spi_buf_set tx = { - .buffers = &tx_buf, - .count = 1 - }; - - err = spi_write_dt(&config->bus, &tx); - if (err) { - LOG_ERR("Motion burst failed on SPI write"); - return err; - } - - k_busy_wait(T_SRAD_MOTBR); - - const struct spi_buf rx_buf = { - .buf = buf, - .len = burst_size, - }; - const struct spi_buf_set rx = { - .buffers = &rx_buf, - .count = 1 - }; - - err = spi_read_dt(&config->bus, &rx); - if (err) { - LOG_ERR("Motion burst failed on SPI read"); - return err; - } - - /* Terminate burst */ - err = spi_cs_ctrl(dev, false); - if (err) { - return err; - } - - k_busy_wait(T_BEXIT); - - data->last_read_burst = true; - - return 0; +static int motion_burst_read(const struct device *dev, uint8_t *buf, size_t burst_size) { + int err; + struct pixart_data *data = dev->data; + const struct pixart_config *config = dev->config; + + __ASSERT_NO_MSG(burst_size <= PMW3360_MAX_BURST_SIZE); + + /* Write any value to motion burst register only if there have been + * other SPI transmissions with sensor since last burst read. + */ + if (!data->last_read_burst) { + err = reg_write(dev, PMW3360_REG_MOTION_BURST, 0x00); + if (err) { + return err; + } + } + + err = spi_cs_ctrl(dev, true); + if (err) { + return err; + } + + /* Send motion burst address */ + uint8_t reg_buf[] = {PMW3360_REG_MOTION_BURST}; + const struct spi_buf tx_buf = {.buf = reg_buf, .len = ARRAY_SIZE(reg_buf)}; + const struct spi_buf_set tx = {.buffers = &tx_buf, .count = 1}; + + err = spi_write_dt(&config->bus, &tx); + if (err) { + LOG_ERR("Motion burst failed on SPI write"); + return err; + } + + k_busy_wait(T_SRAD_MOTBR); + + const struct spi_buf rx_buf = { + .buf = buf, + .len = burst_size, + }; + const struct spi_buf_set rx = {.buffers = &rx_buf, .count = 1}; + + err = spi_read_dt(&config->bus, &rx); + if (err) { + LOG_ERR("Motion burst failed on SPI read"); + return err; + } + + /* Terminate burst */ + err = spi_cs_ctrl(dev, false); + if (err) { + return err; + } + + k_busy_wait(T_BEXIT); + + data->last_read_burst = true; + + return 0; } -static int burst_write(const struct device *dev, uint8_t reg, const uint8_t *buf, - size_t size) -{ - int err; - struct pixart_data *data = dev->data; - const struct pixart_config *config = dev->config; - - /* Write address of burst register */ - uint8_t write_buf = reg | SPI_WRITE_BIT; - struct spi_buf tx_buf = { - .buf = &write_buf, - .len = 1 - }; - const struct spi_buf_set tx = { - .buffers = &tx_buf, - .count = 1 - }; - - err = spi_cs_ctrl(dev, true); - if (err) { - return err; - } - - err = spi_write_dt(&config->bus, &tx); - if (err) { - LOG_ERR("Burst write failed on SPI write"); - return err; - } - - /* Write data */ - for (size_t i = 0; i < size; i++) { - write_buf = buf[i]; - - err = spi_write_dt(&config->bus, &tx); - if (err) { - LOG_ERR("Burst write failed on SPI write (data)"); - return err; - } - - k_busy_wait(T_BRSEP); - } - - /* Terminate burst mode. */ - err = spi_cs_ctrl(dev, false); - if (err) { - return err; - } - - k_busy_wait(T_BEXIT); - - data->last_read_burst = false; - - return 0; +static int burst_write(const struct device *dev, uint8_t reg, const uint8_t *buf, size_t size) { + int err; + struct pixart_data *data = dev->data; + const struct pixart_config *config = dev->config; + + /* Write address of burst register */ + uint8_t write_buf = reg | SPI_WRITE_BIT; + struct spi_buf tx_buf = {.buf = &write_buf, .len = 1}; + const struct spi_buf_set tx = {.buffers = &tx_buf, .count = 1}; + + err = spi_cs_ctrl(dev, true); + if (err) { + return err; + } + + err = spi_write_dt(&config->bus, &tx); + if (err) { + LOG_ERR("Burst write failed on SPI write"); + return err; + } + + /* Write data */ + for (size_t i = 0; i < size; i++) { + write_buf = buf[i]; + + err = spi_write_dt(&config->bus, &tx); + if (err) { + LOG_ERR("Burst write failed on SPI write (data)"); + return err; + } + + k_busy_wait(T_BRSEP); + } + + /* Terminate burst mode. */ + err = spi_cs_ctrl(dev, false); + if (err) { + return err; + } + + k_busy_wait(T_BEXIT); + + data->last_read_burst = false; + + return 0; } -static int set_cpi(const struct device *dev, uint32_t cpi) -{ - /* Set resolution with CPI step of 100 cpi - * 0x00: 100 cpi (minimum cpi) - * 0x01: 200 cpi - * : - * 0x31: 5000 cpi (default cpi) - * : - * 0x77: 12000 cpi (maximum cpi) - */ - - if ((cpi > PMW3360_MAX_CPI) || (cpi < PMW3360_MIN_CPI)) { - LOG_ERR("CPI value %u out of range", cpi); - return -EINVAL; - } - - /* Convert CPI to register value */ - uint8_t value = (cpi / 100) - 1; - - LOG_INF("Setting CPI to %u (reg value 0x%x)", cpi, value); - - int err = reg_write(dev, PMW3360_REG_CONFIG1, value); - if (err) { - LOG_ERR("Failed to change CPI"); - } - - return err; +static int set_cpi(const struct device *dev, uint32_t cpi) { + /* Set resolution with CPI step of 100 cpi + * 0x00: 100 cpi (minimum cpi) + * 0x01: 200 cpi + * : + * 0x31: 5000 cpi (default cpi) + * : + * 0x77: 12000 cpi (maximum cpi) + */ + + if ((cpi > PMW3360_MAX_CPI) || (cpi < PMW3360_MIN_CPI)) { + LOG_ERR("CPI value %u out of range", cpi); + return -EINVAL; + } + + /* Convert CPI to register value */ + uint8_t value = (cpi / 100) - 1; + + LOG_INF("Setting CPI to %u (reg value 0x%x)", cpi, value); + + int err = reg_write(dev, PMW3360_REG_CONFIG1, value); + if (err) { + LOG_ERR("Failed to change CPI"); + } + + return err; } /* unit: ms */ -static int set_downshift_time(const struct device *dev, uint8_t reg_addr, - uint32_t time) -{ - /* Set downshift time in ms: - * - Run downshift time (from Run to Rest1 mode), default: 500ms - * - Rest 1 downshift time (from Rest1 to Rest2 mode), default: 9.92 s - * - Rest 2 downshift time (from Rest2 to Rest3 mode), default: ~10 min - */ - uint32_t maxtime; - uint32_t mintime; - - switch (reg_addr) { - case PMW3360_REG_RUN_DOWNSHIFT: - /* - * Run downshift time = PMW3360_REG_RUN_DOWNSHIFT * 10 ms - */ - maxtime = 2550; - mintime = 10; - break; - - case PMW3360_REG_REST1_DOWNSHIFT: - /* - * Rest1 downshift time = PMW3360_REG_RUN_DOWNSHIFT - * * 320 * Rest1 rate (default 1 ms) - */ - maxtime = 81600; - mintime = 320; - break; - - case PMW3360_REG_REST2_DOWNSHIFT: - /* - * Rest2 downshift time = PMW3360_REG_REST2_DOWNSHIFT - * * 32 * Rest2 rate (default 100 ms) - */ - maxtime = 816000; - mintime = 3200; - break; - - default: - LOG_ERR("Not supported"); - return -ENOTSUP; - } - - if ((time > maxtime) || (time < mintime)) { - LOG_WRN("Downshift time %u out of range", time); - return -EINVAL; - } - - __ASSERT_NO_MSG((mintime > 0) && (maxtime/mintime <= UINT8_MAX)); - - /* Convert time to register value */ - uint8_t value = time / mintime; - - LOG_INF("Set downshift time to %u ms (reg value 0x%x)", time, value); - - int err = reg_write(dev, reg_addr, value); - if (err) { - LOG_ERR("Failed to change downshift time"); - } - - return err; +static int set_downshift_time(const struct device *dev, uint8_t reg_addr, uint32_t time) { + /* Set downshift time in ms: + * - Run downshift time (from Run to Rest1 mode), default: 500ms + * - Rest 1 downshift time (from Rest1 to Rest2 mode), default: 9.92 s + * - Rest 2 downshift time (from Rest2 to Rest3 mode), default: ~10 min + */ + uint32_t maxtime; + uint32_t mintime; + + switch (reg_addr) { + case PMW3360_REG_RUN_DOWNSHIFT: + /* + * Run downshift time = PMW3360_REG_RUN_DOWNSHIFT * 10 ms + */ + maxtime = 2550; + mintime = 10; + break; + + case PMW3360_REG_REST1_DOWNSHIFT: + /* + * Rest1 downshift time = PMW3360_REG_RUN_DOWNSHIFT + * * 320 * Rest1 rate (default 1 ms) + */ + maxtime = 81600; + mintime = 320; + break; + + case PMW3360_REG_REST2_DOWNSHIFT: + /* + * Rest2 downshift time = PMW3360_REG_REST2_DOWNSHIFT + * * 32 * Rest2 rate (default 100 ms) + */ + maxtime = 816000; + mintime = 3200; + break; + + default: + LOG_ERR("Not supported"); + return -ENOTSUP; + } + + if ((time > maxtime) || (time < mintime)) { + LOG_WRN("Downshift time %u out of range", time); + return -EINVAL; + } + + __ASSERT_NO_MSG((mintime > 0) && (maxtime / mintime <= UINT8_MAX)); + + /* Convert time to register value */ + uint8_t value = time / mintime; + + LOG_INF("Set downshift time to %u ms (reg value 0x%x)", time, value); + + int err = reg_write(dev, reg_addr, value); + if (err) { + LOG_ERR("Failed to change downshift time"); + } + + return err; } /* set sampling rate in each mode (in ms) */ -static int set_sample_time(const struct device *dev, - uint8_t reg_addr_lower, - uint8_t reg_addr_upper, - uint32_t sample_time) -{ - /* Set sample time for the Rest1-Rest3 modes. - * Values above 0x09B0 will trigger internal watchdog reset. - */ - uint32_t maxtime = 0x9B0; - uint32_t mintime = 1; - - if ((sample_time > maxtime) || (sample_time < mintime)) { - LOG_WRN("Sample time %u out of range", sample_time); - return -EINVAL; - } - - LOG_INF("Set sample time to %u ms", sample_time); - - /* The sample time is (reg_value + 1) ms. */ - sample_time--; - uint8_t buf[2]; - - sys_put_le16((uint16_t)sample_time, buf); - - int err = reg_write(dev, reg_addr_lower, buf[0]); - - if (!err) { - err = reg_write(dev, reg_addr_upper, buf[1]); - } else { - LOG_ERR("Failed to change sample time"); - } - - return err; +static int set_sample_time(const struct device *dev, uint8_t reg_addr_lower, uint8_t reg_addr_upper, + uint32_t sample_time) { + /* Set sample time for the Rest1-Rest3 modes. + * Values above 0x09B0 will trigger internal watchdog reset. + */ + uint32_t maxtime = 0x9B0; + uint32_t mintime = 1; + + if ((sample_time > maxtime) || (sample_time < mintime)) { + LOG_WRN("Sample time %u out of range", sample_time); + return -EINVAL; + } + + LOG_INF("Set sample time to %u ms", sample_time); + + /* The sample time is (reg_value + 1) ms. */ + sample_time--; + uint8_t buf[2]; + + sys_put_le16((uint16_t)sample_time, buf); + + int err = reg_write(dev, reg_addr_lower, buf[0]); + + if (!err) { + err = reg_write(dev, reg_addr_upper, buf[1]); + } else { + LOG_ERR("Failed to change sample time"); + } + + return err; } -static int set_rest_modes(const struct device *dev, uint8_t reg_addr, - bool enable) -{ - uint8_t value; - int err = reg_read(dev, reg_addr, &value); +static int set_rest_modes(const struct device *dev, uint8_t reg_addr, bool enable) { + uint8_t value; + int err = reg_read(dev, reg_addr, &value); - if (err) { - LOG_ERR("Failed to read Config2 register"); - return err; - } + if (err) { + LOG_ERR("Failed to read Config2 register"); + return err; + } - WRITE_BIT(value, PMW3360_REST_EN_POS, enable); + WRITE_BIT(value, PMW3360_REST_EN_POS, enable); - LOG_INF("%sable rest modes", (enable) ? ("En") : ("Dis")); - err = reg_write(dev, reg_addr, value); + LOG_INF("%sable rest modes", (enable) ? ("En") : ("Dis")); + err = reg_write(dev, reg_addr, value); - if (err) { - LOG_ERR("Failed to set rest mode"); - } + if (err) { + LOG_ERR("Failed to set rest mode"); + } - return err; + return err; } -static int pmw3360_async_init_fw_load_start(const struct device *dev) -{ - int err = 0; - - /* Read from registers 0x02-0x06 regardless of the motion pin state. */ - for (uint8_t reg = 0x02; (reg <= 0x06) && !err; reg++) { - uint8_t buf[1]; - err = reg_read(dev, reg, buf); - } - - if (err) { - LOG_ERR("Cannot read from data registers"); - return err; - } - - /* Write 0 to Rest_En bit of Config2 register to disable Rest mode. */ - err = reg_write(dev, PMW3360_REG_CONFIG2, 0x00); - if (err) { - LOG_ERR("Cannot disable REST mode"); - return err; - } - - /* Write 0x1D in SROM_enable register to initialize the operation */ - err = reg_write(dev, PMW3360_REG_SROM_ENABLE, 0x1D); - if (err) { - LOG_ERR("Cannot initialize SROM"); - return err; - } - - return err; +static int pmw3360_async_init_fw_load_start(const struct device *dev) { + int err = 0; + + /* Read from registers 0x02-0x06 regardless of the motion pin state. */ + for (uint8_t reg = 0x02; (reg <= 0x06) && !err; reg++) { + uint8_t buf[1]; + err = reg_read(dev, reg, buf); + } + + if (err) { + LOG_ERR("Cannot read from data registers"); + return err; + } + + /* Write 0 to Rest_En bit of Config2 register to disable Rest mode. */ + err = reg_write(dev, PMW3360_REG_CONFIG2, 0x00); + if (err) { + LOG_ERR("Cannot disable REST mode"); + return err; + } + + /* Write 0x1D in SROM_enable register to initialize the operation */ + err = reg_write(dev, PMW3360_REG_SROM_ENABLE, 0x1D); + if (err) { + LOG_ERR("Cannot initialize SROM"); + return err; + } + + return err; } -static int pmw3360_async_init_fw_load_continue(const struct device *dev) -{ - int err; - - LOG_INF("Uploading optical sensor firmware..."); - - /* Write 0x18 to SROM_enable to start SROM download */ - err = reg_write(dev, PMW3360_REG_SROM_ENABLE, 0x18); - if (err) { - LOG_ERR("Cannot start SROM download"); - return err; - } - - /* Write SROM file into SROM_Load_Burst register. - * Data must start with SROM_Load_Burst address. - */ - err = burst_write(dev, PMW3360_REG_SROM_LOAD_BURST, - pmw3360_firmware_data, pmw3360_firmware_length); - if (err) { - LOG_ERR("Cannot write firmware to sensor"); - } - - return err; +static int pmw3360_async_init_fw_load_continue(const struct device *dev) { + int err; + + LOG_INF("Uploading optical sensor firmware..."); + + /* Write 0x18 to SROM_enable to start SROM download */ + err = reg_write(dev, PMW3360_REG_SROM_ENABLE, 0x18); + if (err) { + LOG_ERR("Cannot start SROM download"); + return err; + } + + /* Write SROM file into SROM_Load_Burst register. + * Data must start with SROM_Load_Burst address. + */ + err = burst_write(dev, PMW3360_REG_SROM_LOAD_BURST, pmw3360_firmware_data, + pmw3360_firmware_length); + if (err) { + LOG_ERR("Cannot write firmware to sensor"); + } + + return err; } -static int pmw3360_async_init_fw_load_verify(const struct device *dev) -{ - int err; - - /* Read the SROM_ID register to verify the firmware ID before any - * other register reads or writes - */ - - uint8_t fw_id; - err = reg_read(dev, PMW3360_REG_SROM_ID, &fw_id); - if (err) { - LOG_ERR("Cannot obtain firmware id"); - return err; - } - - LOG_DBG("Optical chip firmware ID: 0x%x", fw_id); - if (fw_id != PMW3360_FIRMWARE_ID) { - LOG_ERR("Chip is not running from SROM!"); - return -EIO; - } - - uint8_t product_id; - err = reg_read(dev, PMW3360_REG_PRODUCT_ID, &product_id); - if (err) { - LOG_ERR("Cannot obtain product id"); - return err; - } - - if (product_id != PMW3360_PRODUCT_ID) { - LOG_ERR("Invalid product id!"); - return -EIO; - } - - /* Write 0x20 to Config2 register for wireless mouse design. - * This enables entering rest modes. - */ - err = reg_write(dev, PMW3360_REG_CONFIG2, 0x20); - if (err) { - LOG_ERR("Cannot enable REST modes"); - } - - return err; +static int pmw3360_async_init_fw_load_verify(const struct device *dev) { + int err; + + /* Read the SROM_ID register to verify the firmware ID before any + * other register reads or writes + */ + + uint8_t fw_id; + err = reg_read(dev, PMW3360_REG_SROM_ID, &fw_id); + if (err) { + LOG_ERR("Cannot obtain firmware id"); + return err; + } + + LOG_DBG("Optical chip firmware ID: 0x%x", fw_id); + if (fw_id != PMW3360_FIRMWARE_ID) { + LOG_ERR("Chip is not running from SROM!"); + return -EIO; + } + + uint8_t product_id; + err = reg_read(dev, PMW3360_REG_PRODUCT_ID, &product_id); + if (err) { + LOG_ERR("Cannot obtain product id"); + return err; + } + + if (product_id != PMW3360_PRODUCT_ID) { + LOG_ERR("Invalid product id!"); + return -EIO; + } + + /* Write 0x20 to Config2 register for wireless mouse design. + * This enables entering rest modes. + */ + err = reg_write(dev, PMW3360_REG_CONFIG2, 0x20); + if (err) { + LOG_ERR("Cannot enable REST modes"); + } + + return err; } -static void irq_handler(const struct device *gpiob, struct gpio_callback *cb, - uint32_t pins) -{ - int err; - struct pixart_data *data = CONTAINER_OF(cb, struct pixart_data, - irq_gpio_cb); - const struct device *dev = data->dev; - const struct pixart_config *config = dev->config; - - // disable the interrupt line first - err = gpio_pin_interrupt_configure_dt(&config->irq_gpio, - GPIO_INT_DISABLE); - if (unlikely(err)) { - LOG_ERR("Cannot disable IRQ"); - k_panic(); - } - - // submit the real handler work - k_work_submit(&data->trigger_handler_work); +static void irq_handler(const struct device *gpiob, struct gpio_callback *cb, uint32_t pins) { + int err; + struct pixart_data *data = CONTAINER_OF(cb, struct pixart_data, irq_gpio_cb); + const struct device *dev = data->dev; + const struct pixart_config *config = dev->config; + + // disable the interrupt line first + err = gpio_pin_interrupt_configure_dt(&config->irq_gpio, GPIO_INT_DISABLE); + if (unlikely(err)) { + LOG_ERR("Cannot disable IRQ"); + k_panic(); + } + + // submit the real handler work + k_work_submit(&data->trigger_handler_work); } -static void trigger_handler(struct k_work *work) -{ - sensor_trigger_handler_t handler; - int err = 0; - struct pixart_data *data = CONTAINER_OF(work, struct pixart_data, - trigger_handler_work); - const struct device *dev = data->dev; - const struct pixart_config *config = dev->config; - - // 1. the first lock period is used to procoss the trigger - // if data_ready_handler is non-NULL, otherwise do nothing - k_spinlock_key_t key = k_spin_lock(&data->lock); - - handler = data->data_ready_handler; - k_spin_unlock(&data->lock, key); - - if (!handler) { - return; - } - - handler(dev, data->trigger); - - // 2. the second lock period is used to resume the interrupt line - // if data_ready_handler is non-NULL, otherwise keep it inactive - key = k_spin_lock(&data->lock); - if (data->data_ready_handler) { - err = gpio_pin_interrupt_configure_dt(&config->irq_gpio, - GPIO_INT_LEVEL_ACTIVE); - } - k_spin_unlock(&data->lock, key); - - if (unlikely(err)) { - LOG_ERR("Cannot re-enable IRQ"); - k_panic(); - } +static void trigger_handler(struct k_work *work) { + sensor_trigger_handler_t handler; + int err = 0; + struct pixart_data *data = CONTAINER_OF(work, struct pixart_data, trigger_handler_work); + const struct device *dev = data->dev; + const struct pixart_config *config = dev->config; + + // 1. the first lock period is used to procoss the trigger + // if data_ready_handler is non-NULL, otherwise do nothing + k_spinlock_key_t key = k_spin_lock(&data->lock); + + handler = data->data_ready_handler; + k_spin_unlock(&data->lock, key); + + if (!handler) { + return; + } + + handler(dev, data->trigger); + + // 2. the second lock period is used to resume the interrupt line + // if data_ready_handler is non-NULL, otherwise keep it inactive + key = k_spin_lock(&data->lock); + if (data->data_ready_handler) { + err = gpio_pin_interrupt_configure_dt(&config->irq_gpio, GPIO_INT_LEVEL_ACTIVE); + } + k_spin_unlock(&data->lock, key); + + if (unlikely(err)) { + LOG_ERR("Cannot re-enable IRQ"); + k_panic(); + } } -static int pmw3360_async_init_power_up(const struct device *dev) -{ - /* Reset sensor */ +static int pmw3360_async_init_power_up(const struct device *dev) { + /* Reset sensor */ - return reg_write(dev, PMW3360_REG_POWER_UP_RESET, PMW3360_POWERUP_CMD_RESET); + return reg_write(dev, PMW3360_REG_POWER_UP_RESET, PMW3360_POWERUP_CMD_RESET); } -static int pmw3360_async_init_configure(const struct device *dev) -{ - int err; +static int pmw3360_async_init_configure(const struct device *dev) { + int err; - err = set_cpi(dev, CONFIG_PMW3360_CPI); + err = set_cpi(dev, CONFIG_PMW3360_CPI); - if (!err) { - err = set_downshift_time(dev, - PMW3360_REG_RUN_DOWNSHIFT, - CONFIG_PMW3360_RUN_DOWNSHIFT_TIME_MS); - } + if (!err) { + err = set_downshift_time(dev, PMW3360_REG_RUN_DOWNSHIFT, + CONFIG_PMW3360_RUN_DOWNSHIFT_TIME_MS); + } - if (!err) { - err = set_downshift_time(dev, - PMW3360_REG_REST1_DOWNSHIFT, - CONFIG_PMW3360_REST1_DOWNSHIFT_TIME_MS); - } + if (!err) { + err = set_downshift_time(dev, PMW3360_REG_REST1_DOWNSHIFT, + CONFIG_PMW3360_REST1_DOWNSHIFT_TIME_MS); + } - if (!err) { - err = set_downshift_time(dev, - PMW3360_REG_REST2_DOWNSHIFT, - CONFIG_PMW3360_REST2_DOWNSHIFT_TIME_MS); - } + if (!err) { + err = set_downshift_time(dev, PMW3360_REG_REST2_DOWNSHIFT, + CONFIG_PMW3360_REST2_DOWNSHIFT_TIME_MS); + } - return err; + return err; } -static void pmw3360_async_init(struct k_work *work) -{ - struct pixart_data *data = CONTAINER_OF(work, struct pixart_data, - init_work); - const struct device *dev = data->dev; - - LOG_DBG("PMW3360 async init step %d", data->async_init_step); - - data->err = async_init_fn[data->async_init_step](dev); - if (data->err) { - LOG_ERR("PMW3360 initialization failed"); - } else { - data->async_init_step++; - - if (data->async_init_step == ASYNC_INIT_STEP_COUNT) { - data->ready = true; // sensor is ready to work - LOG_INF("PMW3360 initialized"); - } else { - k_work_schedule(&data->init_work, - K_MSEC(async_init_delay[ - data->async_init_step])); - } - } +static void pmw3360_async_init(struct k_work *work) { + struct pixart_data *data = CONTAINER_OF(work, struct pixart_data, init_work); + const struct device *dev = data->dev; + + LOG_DBG("PMW3360 async init step %d", data->async_init_step); + + data->err = async_init_fn[data->async_init_step](dev); + if (data->err) { + LOG_ERR("PMW3360 initialization failed"); + } else { + data->async_init_step++; + + if (data->async_init_step == ASYNC_INIT_STEP_COUNT) { + data->ready = true; // sensor is ready to work + LOG_INF("PMW3360 initialized"); + } else { + k_work_schedule(&data->init_work, K_MSEC(async_init_delay[data->async_init_step])); + } + } } -static int pmw3360_init_irq(const struct device *dev) -{ - int err; - struct pixart_data *data = dev->data; - const struct pixart_config *config = dev->config; - - // check readiness of irq gpio pin - if (!device_is_ready(config->irq_gpio.port)) { - LOG_ERR("IRQ GPIO device not ready"); - return -ENODEV; - } - - // init the irq pin - err = gpio_pin_configure_dt(&config->irq_gpio, GPIO_INPUT); - if (err) { - LOG_ERR("Cannot configure IRQ GPIO"); - return err; - } - - // setup and add the irq callback associated - gpio_init_callback(&data->irq_gpio_cb, irq_handler, - BIT(config->irq_gpio.pin)); - - err = gpio_add_callback(config->irq_gpio.port, &data->irq_gpio_cb); - if (err) { - LOG_ERR("Cannot add IRQ GPIO callback"); - } - - return err; +static int pmw3360_init_irq(const struct device *dev) { + int err; + struct pixart_data *data = dev->data; + const struct pixart_config *config = dev->config; + + // check readiness of irq gpio pin + if (!device_is_ready(config->irq_gpio.port)) { + LOG_ERR("IRQ GPIO device not ready"); + return -ENODEV; + } + + // init the irq pin + err = gpio_pin_configure_dt(&config->irq_gpio, GPIO_INPUT); + if (err) { + LOG_ERR("Cannot configure IRQ GPIO"); + return err; + } + + // setup and add the irq callback associated + gpio_init_callback(&data->irq_gpio_cb, irq_handler, BIT(config->irq_gpio.pin)); + + err = gpio_add_callback(config->irq_gpio.port, &data->irq_gpio_cb); + if (err) { + LOG_ERR("Cannot add IRQ GPIO callback"); + } + + return err; } -static int pmw3360_init(const struct device *dev) -{ - struct pixart_data *data = dev->data; - const struct pixart_config *config = dev->config; - int err; - - // init device pointer - data->dev = dev; - - // init trigger handler work - k_work_init(&data->trigger_handler_work, trigger_handler); - - // check readiness of spi bus - if (!spi_is_ready(&config->bus)) { - LOG_ERR("SPI device not ready"); - return -ENODEV; - } - - // check readiness of cs gpio pin and init it to inactive - if (!device_is_ready(config->cs_gpio.port)) { - LOG_ERR("SPI CS device not ready"); - return -ENODEV; - } - - err = gpio_pin_configure_dt(&config->cs_gpio, GPIO_OUTPUT_INACTIVE); - if (err) { - LOG_ERR("Cannot configure SPI CS GPIO"); - return err; - } - - // init irq routine - err = pmw3360_init_irq(dev); - if (err) { - return err; - } - - // Setup delayable and non-blocking init jobs, including following steps: - // 1. power reset - // 2. clear motion registers - // 3. srom firmware download and checking - // 4. eable rest mode - // 5. set cpi and downshift time (not sample rate) - // The sensor is ready to work (i.e., data->ready=true after the above steps are finished) - k_work_init_delayable(&data->init_work, pmw3360_async_init); - - k_work_schedule(&data->init_work, - K_MSEC(async_init_delay[data->async_init_step])); - - return err; +static int pmw3360_init(const struct device *dev) { + struct pixart_data *data = dev->data; + const struct pixart_config *config = dev->config; + int err; + + // init device pointer + data->dev = dev; + + // init trigger handler work + k_work_init(&data->trigger_handler_work, trigger_handler); + + // check readiness of spi bus + if (!spi_is_ready(&config->bus)) { + LOG_ERR("SPI device not ready"); + return -ENODEV; + } + + // check readiness of cs gpio pin and init it to inactive + if (!device_is_ready(config->cs_gpio.port)) { + LOG_ERR("SPI CS device not ready"); + return -ENODEV; + } + + err = gpio_pin_configure_dt(&config->cs_gpio, GPIO_OUTPUT_INACTIVE); + if (err) { + LOG_ERR("Cannot configure SPI CS GPIO"); + return err; + } + + // init irq routine + err = pmw3360_init_irq(dev); + if (err) { + return err; + } + + // Setup delayable and non-blocking init jobs, including following steps: + // 1. power reset + // 2. clear motion registers + // 3. srom firmware download and checking + // 4. eable rest mode + // 5. set cpi and downshift time (not sample rate) + // The sensor is ready to work (i.e., data->ready=true after the above steps are finished) + k_work_init_delayable(&data->init_work, pmw3360_async_init); + + k_work_schedule(&data->init_work, K_MSEC(async_init_delay[data->async_init_step])); + + return err; } -static int pmw3360_sample_fetch(const struct device *dev, enum sensor_channel chan) -{ - struct pixart_data *data = dev->data; - uint8_t buf[PMW3360_BURST_SIZE]; - - if (unlikely(chan != SENSOR_CHAN_ALL)) { - return -ENOTSUP; - } - - if (unlikely(!data->ready)) { - LOG_DBG("Device is not initialized yet"); - return -EBUSY; - } - - int err = motion_burst_read(dev, buf, sizeof(buf)); - - if (!err) { - int16_t x = ((int16_t)sys_get_le16(&buf[PMW3360_DX_POS])) / CONFIG_PMW3360_CPI_DIVIDOR; - int16_t y = ((int16_t)sys_get_le16(&buf[PMW3360_DY_POS])) / CONFIG_PMW3360_CPI_DIVIDOR; - /* int16_t x = sys_get_le16(&buf[PMW3360_DX_POS]); */ - /* int16_t y = sys_get_le16(&buf[PMW3360_DY_POS]); */ - - if (IS_ENABLED(CONFIG_PMW3360_ORIENTATION_0)) { - data->x = x; - data->y = y; - } else if (IS_ENABLED(CONFIG_PMW3360_ORIENTATION_90)) { - data->x = y; - data->y = x; - } else if (IS_ENABLED(CONFIG_PMW3360_ORIENTATION_180)) { - data->x = x; - data->y = -y; - } else if (IS_ENABLED(CONFIG_PMW3360_ORIENTATION_270)) { - data->x = -y; - data->y = -x; - } - } - - return err; +static int pmw3360_sample_fetch(const struct device *dev, enum sensor_channel chan) { + struct pixart_data *data = dev->data; + uint8_t buf[PMW3360_BURST_SIZE]; + + if (unlikely(chan != SENSOR_CHAN_ALL)) { + return -ENOTSUP; + } + + if (unlikely(!data->ready)) { + LOG_DBG("Device is not initialized yet"); + return -EBUSY; + } + + int err = motion_burst_read(dev, buf, sizeof(buf)); + + if (!err) { + int16_t x = ((int16_t)sys_get_le16(&buf[PMW3360_DX_POS])) / CONFIG_PMW3360_CPI_DIVIDOR; + int16_t y = ((int16_t)sys_get_le16(&buf[PMW3360_DY_POS])) / CONFIG_PMW3360_CPI_DIVIDOR; + /* int16_t x = sys_get_le16(&buf[PMW3360_DX_POS]); */ + /* int16_t y = sys_get_le16(&buf[PMW3360_DY_POS]); */ + + if (IS_ENABLED(CONFIG_PMW3360_ORIENTATION_0)) { + data->x = -x; + data->y = y; + } else if (IS_ENABLED(CONFIG_PMW3360_ORIENTATION_90)) { + data->x = y; + data->y = -x; + } else if (IS_ENABLED(CONFIG_PMW3360_ORIENTATION_180)) { + data->x = x; + data->y = -y; + } else if (IS_ENABLED(CONFIG_PMW3360_ORIENTATION_270)) { + data->x = -y; + data->y = x; + } + } + + return err; } static int pmw3360_channel_get(const struct device *dev, enum sensor_channel chan, - struct sensor_value *val) -{ - struct pixart_data *data = dev->data; - - if (unlikely(!data->ready)) { - LOG_DBG("Device is not initialized yet"); - return -EBUSY; - } - - switch (chan) { - case SENSOR_CHAN_POS_DX: - val->val1 = data->x; - val->val2 = 0; - break; - - case SENSOR_CHAN_POS_DY: - val->val1 = data->y; - val->val2 = 0; - break; - - default: - return -ENOTSUP; - } - - return 0; + struct sensor_value *val) { + struct pixart_data *data = dev->data; + + if (unlikely(!data->ready)) { + LOG_DBG("Device is not initialized yet"); + return -EBUSY; + } + + switch (chan) { + case SENSOR_CHAN_POS_DX: + val->val1 = data->x; + val->val2 = 0; + break; + + case SENSOR_CHAN_POS_DY: + val->val1 = data->y; + val->val2 = 0; + break; + + default: + return -ENOTSUP; + } + + return 0; } /* Setup the callback for actual trigger handling */ @@ -931,154 +855,133 @@ static int pmw3360_channel_get(const struct device *dev, enum sensor_channel cha // 1. set up a handler callback // 2. set up a flag (i.e., data_ready_handler) to indicate resuming the interrput line or not // This feature is useful to pass the resuming of the interrupt to application -static int pmw3360_trigger_set(const struct device *dev, - const struct sensor_trigger *trig, - sensor_trigger_handler_t handler) -{ - struct pixart_data *data = dev->data; - const struct pixart_config *config = dev->config; - int err; - - if (unlikely(trig->type != SENSOR_TRIG_DATA_READY)) { - return -ENOTSUP; - } - - if (unlikely(trig->chan != SENSOR_CHAN_ALL)) { - return -ENOTSUP; - } - - if (unlikely(!data->ready)) { - LOG_DBG("Device is not initialized yet"); - return -EBUSY; - } - - // spin lock is needed, so that the handler is not invoked before its pointer is assigned - // a valid value - k_spinlock_key_t key = k_spin_lock(&data->lock); - - // if non-NULL (a real handler defined), eanble the interrupt line - // otherwise, disable the interrupt line - if (handler) { - err = gpio_pin_interrupt_configure_dt(&config->irq_gpio, - GPIO_INT_LEVEL_ACTIVE); - } else { - err = gpio_pin_interrupt_configure_dt(&config->irq_gpio, - GPIO_INT_DISABLE); - } - - if (!err) { - data->data_ready_handler = handler; - } - - data->trigger = trig; - - k_spin_unlock(&data->lock, key); - - return err; +static int pmw3360_trigger_set(const struct device *dev, const struct sensor_trigger *trig, + sensor_trigger_handler_t handler) { + struct pixart_data *data = dev->data; + const struct pixart_config *config = dev->config; + int err; + + if (unlikely(trig->type != SENSOR_TRIG_DATA_READY)) { + return -ENOTSUP; + } + + if (unlikely(trig->chan != SENSOR_CHAN_ALL)) { + return -ENOTSUP; + } + + if (unlikely(!data->ready)) { + LOG_DBG("Device is not initialized yet"); + return -EBUSY; + } + + // spin lock is needed, so that the handler is not invoked before its pointer is assigned + // a valid value + k_spinlock_key_t key = k_spin_lock(&data->lock); + + // if non-NULL (a real handler defined), eanble the interrupt line + // otherwise, disable the interrupt line + if (handler) { + err = gpio_pin_interrupt_configure_dt(&config->irq_gpio, GPIO_INT_LEVEL_ACTIVE); + } else { + err = gpio_pin_interrupt_configure_dt(&config->irq_gpio, GPIO_INT_DISABLE); + } + + if (!err) { + data->data_ready_handler = handler; + } + + data->trigger = trig; + + k_spin_unlock(&data->lock, key); + + return err; } static int pmw3360_attr_set(const struct device *dev, enum sensor_channel chan, - enum sensor_attribute attr, - const struct sensor_value *val) -{ - struct pixart_data *data = dev->data; - int err; - - if (unlikely(chan != SENSOR_CHAN_ALL)) { - return -ENOTSUP; - } - - if (unlikely(!data->ready)) { - LOG_DBG("Device is not initialized yet"); - return -EBUSY; - } - - switch ((uint32_t)attr) { - case PMW3360_ATTR_CPI: - err = set_cpi(dev, PMW3360_SVALUE_TO_CPI(*val)); - break; - - case PMW3360_ATTR_REST_ENABLE: - err = set_rest_modes(dev, - PMW3360_REG_CONFIG2, - PMW3360_SVALUE_TO_BOOL(*val)); - break; - - case PMW3360_ATTR_RUN_DOWNSHIFT_TIME: - err = set_downshift_time(dev, - PMW3360_REG_RUN_DOWNSHIFT, - PMW3360_SVALUE_TO_TIME(*val)); - break; - - case PMW3360_ATTR_REST1_DOWNSHIFT_TIME: - err = set_downshift_time(dev, - PMW3360_REG_REST1_DOWNSHIFT, - PMW3360_SVALUE_TO_TIME(*val)); - break; - - case PMW3360_ATTR_REST2_DOWNSHIFT_TIME: - err = set_downshift_time(dev, - PMW3360_REG_REST2_DOWNSHIFT, - PMW3360_SVALUE_TO_TIME(*val)); - break; - - case PMW3360_ATTR_REST1_SAMPLE_TIME: - err = set_sample_time(dev, - PMW3360_REG_REST1_RATE_LOWER, - PMW3360_REG_REST1_RATE_UPPER, - PMW3360_SVALUE_TO_TIME(*val)); - break; - - case PMW3360_ATTR_REST2_SAMPLE_TIME: - err = set_sample_time(dev, - PMW3360_REG_REST2_RATE_LOWER, - PMW3360_REG_REST2_RATE_UPPER, - PMW3360_SVALUE_TO_TIME(*val)); - break; - - case PMW3360_ATTR_REST3_SAMPLE_TIME: - err = set_sample_time(dev, - PMW3360_REG_REST3_RATE_LOWER, - PMW3360_REG_REST3_RATE_UPPER, - PMW3360_SVALUE_TO_TIME(*val)); - break; - - default: - LOG_ERR("Unknown attribute"); - return -ENOTSUP; - } - - return err; + enum sensor_attribute attr, const struct sensor_value *val) { + struct pixart_data *data = dev->data; + int err; + + if (unlikely(chan != SENSOR_CHAN_ALL)) { + return -ENOTSUP; + } + + if (unlikely(!data->ready)) { + LOG_DBG("Device is not initialized yet"); + return -EBUSY; + } + + switch ((uint32_t)attr) { + case PMW3360_ATTR_CPI: + err = set_cpi(dev, PMW3360_SVALUE_TO_CPI(*val)); + break; + + case PMW3360_ATTR_REST_ENABLE: + err = set_rest_modes(dev, PMW3360_REG_CONFIG2, PMW3360_SVALUE_TO_BOOL(*val)); + break; + + case PMW3360_ATTR_RUN_DOWNSHIFT_TIME: + err = set_downshift_time(dev, PMW3360_REG_RUN_DOWNSHIFT, PMW3360_SVALUE_TO_TIME(*val)); + break; + + case PMW3360_ATTR_REST1_DOWNSHIFT_TIME: + err = set_downshift_time(dev, PMW3360_REG_REST1_DOWNSHIFT, PMW3360_SVALUE_TO_TIME(*val)); + break; + + case PMW3360_ATTR_REST2_DOWNSHIFT_TIME: + err = set_downshift_time(dev, PMW3360_REG_REST2_DOWNSHIFT, PMW3360_SVALUE_TO_TIME(*val)); + break; + + case PMW3360_ATTR_REST1_SAMPLE_TIME: + err = set_sample_time(dev, PMW3360_REG_REST1_RATE_LOWER, PMW3360_REG_REST1_RATE_UPPER, + PMW3360_SVALUE_TO_TIME(*val)); + break; + + case PMW3360_ATTR_REST2_SAMPLE_TIME: + err = set_sample_time(dev, PMW3360_REG_REST2_RATE_LOWER, PMW3360_REG_REST2_RATE_UPPER, + PMW3360_SVALUE_TO_TIME(*val)); + break; + + case PMW3360_ATTR_REST3_SAMPLE_TIME: + err = set_sample_time(dev, PMW3360_REG_REST3_RATE_LOWER, PMW3360_REG_REST3_RATE_UPPER, + PMW3360_SVALUE_TO_TIME(*val)); + break; + + default: + LOG_ERR("Unknown attribute"); + return -ENOTSUP; + } + + return err; } static const struct sensor_driver_api pmw3360_driver_api = { - .sample_fetch = pmw3360_sample_fetch, - .channel_get = pmw3360_channel_get, - .trigger_set = pmw3360_trigger_set, - .attr_set = pmw3360_attr_set, + .sample_fetch = pmw3360_sample_fetch, + .channel_get = pmw3360_channel_get, + .trigger_set = pmw3360_trigger_set, + .attr_set = pmw3360_attr_set, }; -#define PMW3360_DEFINE(n) \ - static struct pixart_data data##n; \ - \ - static const struct pixart_config config##n = { \ - .irq_gpio = GPIO_DT_SPEC_INST_GET(n, irq_gpios), \ - .bus = { \ - .bus = DEVICE_DT_GET(DT_INST_BUS(n)), \ - .config = { \ - .frequency = DT_INST_PROP(n, \ - spi_max_frequency), \ - .operation = SPI_WORD_SET(8) | \ - SPI_TRANSFER_MSB | \ - SPI_MODE_CPOL | SPI_MODE_CPHA, \ - .slave = DT_INST_REG_ADDR(n), \ - }, \ - }, \ - .cs_gpio = SPI_CS_GPIOS_DT_SPEC_GET(DT_DRV_INST(n)), \ - }; \ - \ - DEVICE_DT_INST_DEFINE(n, pmw3360_init, NULL, &data##n, &config##n, \ - POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \ - &pmw3360_driver_api); +#define PMW3360_DEFINE(n) \ + static struct pixart_data data##n; \ + \ + static const struct pixart_config config##n = { \ + .irq_gpio = GPIO_DT_SPEC_INST_GET(n, irq_gpios), \ + .bus = \ + { \ + .bus = DEVICE_DT_GET(DT_INST_BUS(n)), \ + .config = \ + { \ + .frequency = DT_INST_PROP(n, spi_max_frequency), \ + .operation = \ + SPI_WORD_SET(8) | SPI_TRANSFER_MSB | SPI_MODE_CPOL | SPI_MODE_CPHA, \ + .slave = DT_INST_REG_ADDR(n), \ + }, \ + }, \ + .cs_gpio = SPI_CS_GPIOS_DT_SPEC_GET(DT_DRV_INST(n)), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(n, pmw3360_init, NULL, &data##n, &config##n, POST_KERNEL, \ + CONFIG_SENSOR_INIT_PRIORITY, &pmw3360_driver_api); DT_INST_FOREACH_STATUS_OKAY(PMW3360_DEFINE) From 5177db3e4a6ef990aa8f9eca8330613446d7c053 Mon Sep 17 00:00:00 2001 From: Angzhi Li Date: Sat, 22 Apr 2023 14:31:07 -0700 Subject: [PATCH 127/157] start over with tracktyl changes --- app/boards/arm/nice_nano/CMakeLists.txt | 12 + app/boards/arm/nice_nano/Kconfig.defconfig | 22 ++ app/boards/arm/nice_nano/nice_nano.yaml | 2 + .../arm/nice_nano/nice_nano_v2_defconfig | 6 +- .../arm/nice_nano/trackball/CMakeLists.txt | 9 + .../arm/nice_nano/trackball/trackball.c | 252 ++++++++++++++++ .../arm/nice_nano/trackball/trackball_new.c | 271 ++++++++++++++++++ app/boards/shields/tracktyl/Kconfig.defconfig | 16 ++ app/boards/shields/tracktyl/Kconfig.shield | 5 + app/boards/shields/tracktyl/tracktyl.conf | 0 app/boards/shields/tracktyl/tracktyl.dtsi | 85 ++++++ .../shields/tracktyl/tracktyl_left.conf | 3 + .../shields/tracktyl/tracktyl_left.overlay | 22 ++ .../shields/tracktyl/tracktyl_right.conf | 21 ++ .../shields/tracktyl/tracktyl_right.overlay | 30 ++ 15 files changed, 755 insertions(+), 1 deletion(-) create mode 100644 app/boards/arm/nice_nano/CMakeLists.txt create mode 100644 app/boards/arm/nice_nano/trackball/CMakeLists.txt create mode 100644 app/boards/arm/nice_nano/trackball/trackball.c create mode 100644 app/boards/arm/nice_nano/trackball/trackball_new.c create mode 100644 app/boards/shields/tracktyl/Kconfig.defconfig create mode 100644 app/boards/shields/tracktyl/Kconfig.shield create mode 100644 app/boards/shields/tracktyl/tracktyl.conf create mode 100644 app/boards/shields/tracktyl/tracktyl.dtsi create mode 100644 app/boards/shields/tracktyl/tracktyl_left.conf create mode 100644 app/boards/shields/tracktyl/tracktyl_left.overlay create mode 100644 app/boards/shields/tracktyl/tracktyl_right.conf create mode 100644 app/boards/shields/tracktyl/tracktyl_right.overlay diff --git a/app/boards/arm/nice_nano/CMakeLists.txt b/app/boards/arm/nice_nano/CMakeLists.txt new file mode 100644 index 00000000000..422dd57386e --- /dev/null +++ b/app/boards/arm/nice_nano/CMakeLists.txt @@ -0,0 +1,12 @@ +# +# Copyright (c) 2021 Darryl deHaan +# SPDX-License-Identifier: MIT +# + +if (CONFIG_NICE_NANO_POINTDEVICE) + add_subdirectory_ifdef(CONFIG_PINNACLE trackpad) + add_subdirectory_ifdef(CONFIG_PMW33XX trackball) + add_subdirectory_ifdef(CONFIG_PMW3360 trackball) + add_subdirectory_ifdef(CONFIG_PAW3395 trackball) + add_subdirectory_ifdef(CONFIG_PMW3610 trackball) +endif() \ No newline at end of file diff --git a/app/boards/arm/nice_nano/Kconfig.defconfig b/app/boards/arm/nice_nano/Kconfig.defconfig index ad3fefef315..2cdf8c92b04 100644 --- a/app/boards/arm/nice_nano/Kconfig.defconfig +++ b/app/boards/arm/nice_nano/Kconfig.defconfig @@ -22,4 +22,26 @@ config ZMK_BLE config ZMK_USB default y +# Role of the keyboard in split setup, default is master (i.e., central) +## NOTE: this is a dedicated config option for nrfmacro board, so that all shields +## using this board can share the some configuration. It's mainly used for controlling +## which widget to show on different side of the keyboard +choice + prompt "Role of the keyboard" + help + Specify the role of the keyboard + +config NICE_NANO_SHIELD_MASTER + bool "master side, in charge of communication with host" + +config NICE_NANO_SHIELD_SLAVE + bool "slave side, acting as a peripheral to master side" + +endchoice + +# add point device +config NICE_NANO_POINTDEVICE + bool "add support for point device" + select SPI + endif # BOARD_NICE_NANO || BOARD_NICE_NANO_V2 diff --git a/app/boards/arm/nice_nano/nice_nano.yaml b/app/boards/arm/nice_nano/nice_nano.yaml index 1c367324a3c..2484e8898f6 100644 --- a/app/boards/arm/nice_nano/nice_nano.yaml +++ b/app/boards/arm/nice_nano/nice_nano.yaml @@ -13,3 +13,5 @@ supported: - ieee802154 - pwm - watchdog + - spi + - i2c diff --git a/app/boards/arm/nice_nano/nice_nano_v2_defconfig b/app/boards/arm/nice_nano/nice_nano_v2_defconfig index 206e51c2f17..6aaf8acc9f1 100644 --- a/app/boards/arm/nice_nano/nice_nano_v2_defconfig +++ b/app/boards/arm/nice_nano/nice_nano_v2_defconfig @@ -18,4 +18,8 @@ CONFIG_NVS=y CONFIG_SETTINGS_NVS=y CONFIG_FLASH=y CONFIG_FLASH_PAGE_LAYOUT=y -CONFIG_FLASH_MAP=y \ No newline at end of file +CONFIG_FLASH_MAP=y + +# added from nrfmacro, not sure if needed for trackball +CONFIG_CLOCK_CONTROL_NRF=y +CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC=y \ No newline at end of file diff --git a/app/boards/arm/nice_nano/trackball/CMakeLists.txt b/app/boards/arm/nice_nano/trackball/CMakeLists.txt new file mode 100644 index 00000000000..d9449b5615c --- /dev/null +++ b/app/boards/arm/nice_nano/trackball/CMakeLists.txt @@ -0,0 +1,9 @@ +zephyr_library() +zephyr_library_sources_ifdef(CONFIG_PMW33XX trackball.c) +zephyr_library_sources_ifdef(CONFIG_PMW3360 trackball_new.c) +zephyr_library_sources_ifdef(CONFIG_PAW3395 trackball_new.c) +zephyr_library_sources_ifdef(CONFIG_PMW3610 trackball_new.c) + +zephyr_library_include_directories(${CMAKE_SOURCE_DIR}/include) +zephyr_library_include_directories(${CMAKE_SOURCE_DIR}/drivers/sensor) +zephyr_library_include_directories(${ZEPHYR_BASE}/drivers) diff --git a/app/boards/arm/nice_nano/trackball/trackball.c b/app/boards/arm/nice_nano/trackball/trackball.c new file mode 100644 index 00000000000..6ab91478b2f --- /dev/null +++ b/app/boards/arm/nice_nano/trackball/trackball.c @@ -0,0 +1,252 @@ +#define DT_DRV_COMPAT pixart_pmw33xx + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#define SCROLL_DIV_FACTOR 5 +/* #define SCROLL_LAYER_INDEX 4 */ +#define SCROLL_LAYER_INDEX COND_CODE_0(DT_INST_NODE_HAS_PROP(0, scroll_layer), (255), \ + (DT_INST_PROP(0, scroll_layer))) + +#define CPI_DIVIDOR COND_CODE_0(DT_INST_NODE_HAS_PROP(0, cpi_dividor), (1), \ + (DT_INST_PROP(0, cpi_dividor))) + + +/* #if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) */ +// in us +static int64_t last_interrupt_time = 0; +static int64_t current_interrupt_time = 0; +static int64_t interrupt_interval = 0; +static int64_t handler_duration = 0; +static int64_t send_report_duration = 0; +static int64_t idle_interval = 0; +static int64_t time_buffer = 0; +/* #endif */ + +static int polling_count = 0; +static int max_poll_count = 0; +static int polling_interval = 0; +#define BLE_POLL_COUNT 20 +#define BLE_POLL_INTERVAL 15 // in ms +#define USB_POLL_COUNT 300 +#define USB_POLL_INTERVAL 1 // in ms + +// +static struct sensor_value dx, dy; + +const struct device *trackball = DEVICE_DT_GET(DT_DRV_INST(0)); + +LOG_MODULE_REGISTER(trackball, CONFIG_SENSOR_LOG_LEVEL); + + +/* update and send report */ +static int64_t trackball_update_handler(struct k_work *work) { +/* #if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) */ + int64_t start_time = k_ticks_to_us_floor64(k_uptime_ticks()); +/* #endif */ + + // remaining scroll from last update + static int8_t scroll_ver_rem = 0, scroll_hor_rem = 0; + dx.val1 = dx.val1/CPI_DIVIDOR; + dy.val1 = dy.val1/CPI_DIVIDOR; + + // update report with latest position + zmk_hid_mouse_movement_set(0, 0); + zmk_hid_mouse_scroll_set(0, 0); + + const uint8_t layer = zmk_keymap_highest_layer_active(); + if (layer == SCROLL_LAYER_INDEX) { // lower + const int16_t total_hor = dx.val1 + scroll_hor_rem, total_ver = -(dy.val1 + scroll_ver_rem); + scroll_hor_rem = total_hor % SCROLL_DIV_FACTOR; + scroll_ver_rem = total_ver % SCROLL_DIV_FACTOR; + zmk_hid_mouse_scroll_update(total_hor / SCROLL_DIV_FACTOR, total_ver / SCROLL_DIV_FACTOR); + } else { + zmk_hid_mouse_movement_update(CLAMP(dx.val1, INT8_MIN, INT8_MAX), CLAMP(dy.val1, INT8_MIN, INT8_MAX)); + } + + // send the report to host + zmk_endpoints_send_mouse_report(); + +/* #if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) */ + return start_time = k_ticks_to_us_floor64(k_uptime_ticks()) - start_time; +/* #else */ +/* return 0; */ +/* #endif */ +} + +// polling work +static void trackball_poll_handler(struct k_work *work) { +/* #if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) */ + current_interrupt_time = k_ticks_to_us_floor64(k_uptime_ticks()); + interrupt_interval = current_interrupt_time - last_interrupt_time; + idle_interval = current_interrupt_time - time_buffer; +/* #endif */ + + + // fetch latest position from sensor + int ret = sensor_sample_fetch(trackball); + if (ret < 0) { + LOG_ERR("fetch: %d", ret); + return; + } + + // get the x, y delta + ret = sensor_channel_get(trackball, SENSOR_CHAN_POS_DX, &dx); + if (ret < 0) { + LOG_ERR("get dx: %d", ret); + return; + } + ret = sensor_channel_get(trackball, SENSOR_CHAN_POS_DY, &dy); + if (ret < 0) { + LOG_ERR("get dy: %d", ret); + return; + } + + if(dx.val1 != 0 || dy.val1 != 0) { + // process the updated position and send to host + /* k_work_submit_to_queue(zmk_mouse_work_q(), &trackball_update); */ +/* #if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) */ + send_report_duration = trackball_update_handler(NULL); +/* #else */ +/* trackball_update_handler(NULL); */ +/* #endif */ + LOG_INF("Position updated: poll interval: %lld; send time: %lld ; new pos: %d %d",\ + interrupt_interval, send_report_duration, dx.val1, dy.val1); + } + +/* #if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) */ + time_buffer = k_ticks_to_us_floor64(k_uptime_ticks()); + handler_duration = time_buffer - current_interrupt_time; + LOG_DBG("idle time: %lld ; handler time cost: %lld", \ + idle_interval, handler_duration); + + last_interrupt_time = current_interrupt_time; +} + +K_WORK_DEFINE(trackball_poll_work, &trackball_poll_handler); + +// polling timer +void trackball_timer_expiry(struct k_timer *timer); +void trackball_timer_stop(struct k_timer *timer); + +K_TIMER_DEFINE(trackball_timer, trackball_timer_expiry, trackball_timer_stop); + +// timer expiry function +void trackball_timer_expiry(struct k_timer *timer) { + // check whether reaching the polling count limit + if(polling_count < max_poll_count) { + // submit polling work to mouse work queue + k_work_submit_to_queue(zmk_mouse_work_q(), &trackball_poll_work); + + // update status + polling_count++; + } + else { + // stop timer + k_timer_stop(&trackball_timer); + } +} + +// timer stop function +void trackball_timer_stop(struct k_timer *timer) { + // reset status + polling_count = 0; + + // resume motion interrupt line + const struct pmw33xx_config *cfg = trackball->config; + if (gpio_pin_interrupt_configure(cfg->motswk_spec.port, cfg->motswk_spec.pin, GPIO_INT_LEVEL_ACTIVE)) { + LOG_WRN("Unable to set MOTSWK GPIO interrupt"); + } +} + + +// trigger handler +static void trackball_trigger_handler(const struct device *dev, const struct sensor_trigger *trig) { + struct pmw33xx_data *data = dev->data; + LOG_INF("I'm OLD trackball implementation"); + + // do not resume motion interrupt + data->resume_interrupt = false; + + // start the polling timer + k_timer_start(&trackball_timer, K_NO_WAIT, K_MSEC(polling_interval)); +} + +static int trackball_init() { + + struct sensor_trigger trigger = { + .type = SENSOR_TRIG_DATA_READY, + .chan = SENSOR_CHAN_ALL, + }; + printk("trackball"); + if (sensor_trigger_set(trackball, &trigger, trackball_trigger_handler) < 0) { + LOG_ERR("can't set trigger"); + return -EIO; + }; + + // init polling parameters + switch (zmk_endpoints_selected()) { +#if IS_ENABLED(CONFIG_ZMK_USB) + case ZMK_ENDPOINT_USB: { + max_poll_count = USB_POLL_COUNT; + polling_interval = USB_POLL_INTERVAL; + return 0; + } +#endif /* IS_ENABLED(CONFIG_ZMK_USB) */ + +#if IS_ENABLED(CONFIG_ZMK_BLE) + case ZMK_ENDPOINT_BLE: { + max_poll_count = BLE_POLL_COUNT; + polling_interval = BLE_POLL_INTERVAL; + return 0; + } +#endif /* IS_ENABLED(CONFIG_ZMK_BLE) */ + default: + LOG_ERR("Unsupported endpoint"); + return -ENOTSUP; + } +} + +SYS_INIT(trackball_init, APPLICATION, CONFIG_SENSOR_INIT_PRIORITY); + +////////////// +int trackball_endpoint_listener(const zmk_event_t *eh) { + LOG_INF("endpoint changing..."); + + // update polling parameters + switch (zmk_endpoints_selected()) { +#if IS_ENABLED(CONFIG_ZMK_USB) + case ZMK_ENDPOINT_USB: { + max_poll_count = USB_POLL_COUNT; + polling_interval = USB_POLL_INTERVAL; + break; + } +#endif /* IS_ENABLED(CONFIG_ZMK_USB) */ + +#if IS_ENABLED(CONFIG_ZMK_BLE) + case ZMK_ENDPOINT_BLE: { + max_poll_count = BLE_POLL_COUNT; + polling_interval = BLE_POLL_INTERVAL; + break; + } +#endif /* IS_ENABLED(CONFIG_ZMK_BLE) */ + default: + LOG_ERR("Unsupported endpoint"); + } + + return ZMK_EV_EVENT_BUBBLE; +} + +ZMK_LISTENER(trackball, trackball_endpoint_listener) +ZMK_SUBSCRIPTION(trackball, zmk_endpoint_selection_changed) diff --git a/app/boards/arm/nice_nano/trackball/trackball_new.c b/app/boards/arm/nice_nano/trackball/trackball_new.c new file mode 100644 index 00000000000..7c8004cbb6c --- /dev/null +++ b/app/boards/arm/nice_nano/trackball/trackball_new.c @@ -0,0 +1,271 @@ +#ifdef CONFIG_PMW3360 +#define DT_DRV_COMPAT pixart_pmw3360 +#include +#elif defined(CONFIG_PAW3395) +#define DT_DRV_COMPAT pixart_paw3395 +#include +#elif defined(CONFIG_PMW3610) +#define DT_DRV_COMPAT pixart_pmw3610 +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include + + +#define SCROLL_DIV_FACTOR 5 +/* #define SCROLL_LAYER_INDEX 4 */ +#define SCROLL_LAYER_INDEX COND_CODE_0(DT_INST_NODE_HAS_PROP(0, scroll_layer), (255), \ + (DT_INST_PROP(0, scroll_layer))) + + +/* #if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) */ +// in us +static int64_t last_interrupt_time = 0; +static int64_t current_interrupt_time = 0; +static int64_t interrupt_interval = 0; +static int64_t handler_duration = 0; +static int64_t send_report_duration = 0; +static int64_t idle_interval = 0; +static int64_t time_buffer = 0; +/* #endif */ + +static int polling_count = 0; +static int max_poll_count = 0; +static int polling_interval = 0; +#define BLE_POLL_COUNT 20 +#define BLE_POLL_INTERVAL 15 // in ms +#define USB_POLL_COUNT 300 +#define USB_POLL_INTERVAL 1 // in ms + + +LOG_MODULE_REGISTER(trackball, CONFIG_SENSOR_LOG_LEVEL); + +// polling work +static void trackball_poll_handler(struct k_work *work) { +/* #if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) */ + current_interrupt_time = k_ticks_to_us_floor64(k_uptime_ticks()); + interrupt_interval = current_interrupt_time - last_interrupt_time; + idle_interval = current_interrupt_time - time_buffer; +/* #endif */ + + // get the device pointer + struct pixart_data *data = CONTAINER_OF(work, struct pixart_data, poll_work); + const struct device *dev = data->dev; + + // fetch dx and dy from sensor and save them into pixart_data structure + int ret = sensor_sample_fetch(dev); + if (ret < 0) { + LOG_ERR("fetch: %d", ret); + return; + } + + /* // get the x, y delta */ + /* ret = sensor_channel_get(trackball, SENSOR_CHAN_POS_DX, &dx); */ + /* if (ret < 0) { */ + /* LOG_ERR("get dx: %d", ret); */ + /* return; */ + /* } */ + /* ret = sensor_channel_get(trackball, SENSOR_CHAN_POS_DY, &dy); */ + /* if (ret < 0) { */ + /* LOG_ERR("get dy: %d", ret); */ + /* return; */ + /* } */ + + // remaining scroll from last update + static int16_t scroll_ver_rem = 0, scroll_hor_rem = 0; + static int64_t start_time = 0; + if(data->x != 0 || data->y != 0) { + // process the updated position and send to host + /* k_work_submit_to_queue(zmk_mouse_work_q(), &trackball_update); */ + /* #if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) */ + start_time = k_ticks_to_us_floor64(k_uptime_ticks()); + /* #endif */ + + // update report with latest position + zmk_hid_mouse_movement_set(0, 0); + zmk_hid_mouse_scroll_set(0, 0); + + const uint8_t layer = zmk_keymap_highest_layer_active(); + if (layer == SCROLL_LAYER_INDEX) { // lower + const int16_t total_hor = data->x + scroll_hor_rem, total_ver = -(data->y + scroll_ver_rem); + scroll_hor_rem = total_hor % SCROLL_DIV_FACTOR; + scroll_ver_rem = total_ver % SCROLL_DIV_FACTOR; + zmk_hid_mouse_scroll_update(total_hor / SCROLL_DIV_FACTOR, total_ver / SCROLL_DIV_FACTOR); + } else { + zmk_hid_mouse_movement_update(CLAMP(data->x, INT8_MIN, INT8_MAX), CLAMP(data->y, INT8_MIN, INT8_MAX)); + } + + // send the report to host + zmk_endpoints_send_mouse_report(); + + send_report_duration = k_ticks_to_us_floor64(k_uptime_ticks()) - start_time; + LOG_INF("Position updated: poll interval: %lld; send time: %lld ; new pos: %d %d",\ + interrupt_interval, send_report_duration, data->x, data->y); + } + +/* #if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) */ + time_buffer = k_ticks_to_us_floor64(k_uptime_ticks()); + handler_duration = time_buffer - current_interrupt_time; + LOG_DBG("idle time: %lld ; handler time cost: %lld", \ + idle_interval, handler_duration); + + last_interrupt_time = current_interrupt_time; +} + +// trigger handler +static void trackball_trigger_handler(const struct device *dev, const struct sensor_trigger *trig) { + LOG_INF("I'm new trackball implementation"); + + struct pixart_data *data = dev->data; + + // do not resume motion interrupt by passing-in null handler + struct sensor_trigger trigger = { + .type = SENSOR_TRIG_DATA_READY, + .chan = SENSOR_CHAN_ALL, + }; + if (sensor_trigger_set(dev, &trigger, NULL) < 0) { + LOG_ERR("can't stop motion interrupt line"); + }; + + // start the polling timer (the real work now is dispatched to a timer-based polling) + k_timer_start(&data->poll_timer, K_NO_WAIT, K_MSEC(polling_interval)); +} + +// timer expiry function +void trackball_timer_expiry(struct k_timer *timer) { + struct pixart_data *data = CONTAINER_OF(timer, struct pixart_data, poll_timer); + + // check whether reaching the polling count limit + if(polling_count < max_poll_count) { + // submit polling work to mouse work queue + k_work_submit_to_queue(zmk_mouse_work_q(), &data->poll_work); + + // update status + polling_count++; + } + else { + // stop timer + k_timer_stop(&data->poll_timer); + } +} + +// timer stop function +void trackball_timer_stop(struct k_timer *timer) { + struct pixart_data *data = CONTAINER_OF(timer, struct pixart_data, poll_timer); + const struct device *dev = data->dev; + + // reset polling count + polling_count = 0; + + // resume motion interrupt line by setting the handler to a real object + struct sensor_trigger trigger = { + .type = SENSOR_TRIG_DATA_READY, + .chan = SENSOR_CHAN_ALL, + }; + if (sensor_trigger_set(dev, &trigger, trackball_trigger_handler) < 0) { + LOG_ERR("can't resume motion interrupt line"); + }; +} + + +/* Setup the trigger handler at system powerup */ +// The device instance should be determined in this step. All other functions should only +// use the device poiter passed as an argument. +// In this applaication, the devcie instance 'trackball' is hard-coded as the first +// pixart_pmw3360 node in dts file and used implicitly here. +static int trackball_init() { + LOG_INF("Init trackball_new"); + + // get the sensor device instance + const struct device *dev = DEVICE_DT_GET(DT_DRV_INST(0)); + struct pixart_data *data = dev->data; + + // setup the timer and handler function of the polling work + k_timer_init(&data->poll_timer, trackball_timer_expiry, trackball_timer_stop); + k_work_init(&data->poll_work, trackball_poll_handler); + + // set up the trigger handler (i.e. the entry point to other code in this file) + struct sensor_trigger trigger = { + .type = SENSOR_TRIG_DATA_READY, + .chan = SENSOR_CHAN_ALL, + }; + + int err; + int count = 0; + do { + err = sensor_trigger_set(dev, &trigger, trackball_trigger_handler); + if (err == -EBUSY) { + count++; + k_sleep(K_MSEC(10)); + } + } while (err == -EBUSY && count < 50); + + if (err) { + LOG_ERR("Cannot enable trigger"); + return err; + } + + // init polling parameters based on current endpoint + switch (zmk_endpoints_selected()) { +#if IS_ENABLED(CONFIG_ZMK_USB) + case ZMK_ENDPOINT_USB: { + max_poll_count = USB_POLL_COUNT; + polling_interval = USB_POLL_INTERVAL; + return 0; + } +#endif /* IS_ENABLED(CONFIG_ZMK_USB) */ + +#if IS_ENABLED(CONFIG_ZMK_BLE) + case ZMK_ENDPOINT_BLE: { + max_poll_count = BLE_POLL_COUNT; + polling_interval = BLE_POLL_INTERVAL; + return 0; + } +#endif /* IS_ENABLED(CONFIG_ZMK_BLE) */ + default: + LOG_ERR("Unsupported endpoint"); + return -ENOTSUP; + } +} + +SYS_INIT(trackball_init, APPLICATION, CONFIG_SENSOR_INIT_PRIORITY); + +/* the following code dynamically changes poll rate based on endpoint changing */ +// The code execution is driven by the zmk event system instead of zephyr interrupt routine +int trackball_endpoint_listener(const zmk_event_t *eh) { + LOG_INF("endpoint changing..."); + + // update polling parameters + switch (zmk_endpoints_selected()) { +#if IS_ENABLED(CONFIG_ZMK_USB) + case ZMK_ENDPOINT_USB: { + max_poll_count = USB_POLL_COUNT; + polling_interval = USB_POLL_INTERVAL; + break; + } +#endif /* IS_ENABLED(CONFIG_ZMK_USB) */ + +#if IS_ENABLED(CONFIG_ZMK_BLE) + case ZMK_ENDPOINT_BLE: { + max_poll_count = BLE_POLL_COUNT; + polling_interval = BLE_POLL_INTERVAL; + break; + } +#endif /* IS_ENABLED(CONFIG_ZMK_BLE) */ + default: + LOG_ERR("Unsupported endpoint"); + } + + return ZMK_EV_EVENT_BUBBLE; +} + +ZMK_LISTENER(trackball, trackball_endpoint_listener) +ZMK_SUBSCRIPTION(trackball, zmk_endpoint_selection_changed) diff --git a/app/boards/shields/tracktyl/Kconfig.defconfig b/app/boards/shields/tracktyl/Kconfig.defconfig new file mode 100644 index 00000000000..e4dfaa3d8a7 --- /dev/null +++ b/app/boards/shields/tracktyl/Kconfig.defconfig @@ -0,0 +1,16 @@ +if SHIELD_TRACKTYL_RIGHT + + config ZMK_KEYBOARD_NAME + default "Tracktyl" + + config ZMK_SPLIT_BLE_ROLE_CENTRAL + default y + +endif + +if SHIELD_TRACKTYL_LEFT || SHIELD_TRACKTYL_RIGHT + + config ZMK_SPLIT + default y + +endif diff --git a/app/boards/shields/tracktyl/Kconfig.shield b/app/boards/shields/tracktyl/Kconfig.shield new file mode 100644 index 00000000000..42f86ffb065 --- /dev/null +++ b/app/boards/shields/tracktyl/Kconfig.shield @@ -0,0 +1,5 @@ +config SHIELD_TRACKTYL_LEFT + def_bool $(shields_list_contains,tracktyl_left) + +config SHIELD_TRACKTYL_RIGHT + def_bool $(shields_list_contains,tracktyl_right) diff --git a/app/boards/shields/tracktyl/tracktyl.conf b/app/boards/shields/tracktyl/tracktyl.conf new file mode 100644 index 00000000000..e69de29bb2d diff --git a/app/boards/shields/tracktyl/tracktyl.dtsi b/app/boards/shields/tracktyl/tracktyl.dtsi new file mode 100644 index 00000000000..72faa9cea4e --- /dev/null +++ b/app/boards/shields/tracktyl/tracktyl.dtsi @@ -0,0 +1,85 @@ +#include + +/ { + chosen { + zmk,kscan = &kscan0; + zmk,matrix_transform = &default_transform; + }; + + default_transform: keymap_transform_0 { + compatible = "zmk,matrix-transform"; + columns = <10>; + rows = <4>; + // | MX1 | MX2 | MX3 | MX4 | MX5 | | MX6 | MX7 | MX8 | MX9 | MX10 | + // | MX11 | MX12 | MX13 | MX14 | MX15 | | MX16 | MX17 | MX18 | MX19 | MX20 | + // | MX21 | MX22 | MX23 | MX24 | MX25 | | MX26 | MX27 | MX28 | MX29 | MX30 | + // | MX31 | MX32 | MX33 | | MX34 | MX35 | + map = < + RC(0,0) RC(0,1) RC(0,2) RC(0,3) RC(0,4) RC(0,5) RC(0,6) RC(0,7) RC(0,8) RC(0,9) + RC(1,0) RC(1,1) RC(1,2) RC(1,3) RC(1,4) RC(1,5) RC(1,6) RC(1,7) RC(1,8) RC(1,9) + RC(2,0) RC(2,1) RC(2,2) RC(2,3) RC(2,4) RC(2,5) RC(2,6) RC(2,7) RC(2,8) RC(2,9) + RC(3,2) RC(3,3) RC(3,4) RC(3,5) RC(3,6) + >; + }; + + kscan0: kscan { + compatible = "zmk,kscan-gpio-matrix"; + label = "KSCAN"; + + diode-direction = "row2col"; // amoeba royale needs to do row2col + + row-gpios + = <&pro_micro 7 GPIO_ACTIVE_HIGH> + , <&pro_micro 8 GPIO_ACTIVE_HIGH> + , <&pro_micro 9 GPIO_ACTIVE_HIGH> + , <&pro_micro 10 GPIO_ACTIVE_HIGH> + ; + + }; + +/* Configure trackballs (either split can have the trackball) */ + trackballs { + compatible = "zmk,keymap-trackballs"; + trackballs = <&trackball>; + }; + +/* Configure encoders (both splits can have a encoder) */ + sensors { + compatible = "zmk,keymap-sensors"; + sensors = <&encoder_left &encoder_right>; + }; + + // define the two encoders + encoder_left: encoder_left { + compatible = "alps,ec11"; + status = "disabled"; + label = "LEFT_ENCODER"; + resolution = <2>; + }; + + encoder_right: encoder_right { + compatible = "alps,ec11"; + status = "disabled"; + label = "RIGHT_ENCODER"; + resolution = <2>; + }; +}; + +/* Declare the trackball */ +&spi2 { + status = "disabled"; + + miso-pin = <17>; // P0.17 + mosi-pin = <20>; // P0.20 + sck-pin = <22>; // P0.22 + cs-gpios = <&gpio0 24 GPIO_ACTIVE_LOW>; // P0.24 + + trackball: trackball@0 { + compatible = "pixart,pmw3360"; + reg = <0>; + label = "Pixart PMW3360"; + irq-gpios = <&gpio1 0 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + spi-max-frequency = <2000000>; + scroll-layer = <5>; + }; +}; diff --git a/app/boards/shields/tracktyl/tracktyl_left.conf b/app/boards/shields/tracktyl/tracktyl_left.conf new file mode 100644 index 00000000000..131e81df07c --- /dev/null +++ b/app/boards/shields/tracktyl/tracktyl_left.conf @@ -0,0 +1,3 @@ +#### Enable encoder support #### +# CONFIG_EC11=y +# CONFIG_EC11_TRIGGER_GLOBAL_THREAD=y \ No newline at end of file diff --git a/app/boards/shields/tracktyl/tracktyl_left.overlay b/app/boards/shields/tracktyl/tracktyl_left.overlay new file mode 100644 index 00000000000..b5d58ebf99c --- /dev/null +++ b/app/boards/shields/tracktyl/tracktyl_left.overlay @@ -0,0 +1,22 @@ +#include "tracktyl.dtsi" // Notice that the main dtsi files are included in the overlay. + +&default_transform { + col-offset = <0>; +}; + +&kscan0 { + col-gpios + = <&pro_micro 16 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 14 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 15 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 18 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 19 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + ; +}; + +// Define encoder pins +&encoder_left { + status = "disabled"; // disable encoders until further hardware impl + a-gpios = <&gpio1 4 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + b-gpios = <&gpio1 6 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; +}; diff --git a/app/boards/shields/tracktyl/tracktyl_right.conf b/app/boards/shields/tracktyl/tracktyl_right.conf new file mode 100644 index 00000000000..e3a470aa3ca --- /dev/null +++ b/app/boards/shields/tracktyl/tracktyl_right.conf @@ -0,0 +1,21 @@ +CONFIG_ZMK_MOUSE=y +CONFIG_SPI=y +CONFIG_PMW3360=y +CONFIG_ZMK_TRACKBALL_POLL_DURATION=8 +CONFIG_PMW3360=y +CONFIG_PMW3360_CPI=2000 +CONFIG_PMW3360_CPI_DIVIDOR=1 +# CONFIG_PMW3360_ORIENTATION_90=y +CONFIG_PMW3610_RUN_DOWNSHIFT_TIME_MS=500 +CONFIG_PMW3610_REST1_DOWNSHIFT_TIME_MS=5000 +CONFIG_PMW3610_REST2_DOWNSHIFT_TIME_MS=30000 + +### Bluetooth ### +CONFIG_BT_PERIPHERAL_PREF_MAX_INT=9 +CONFIG_BT_PERIPHERAL_PREF_LATENCY=16 +CONFIG_BT_BUF_ACL_TX_COUNT=32 +CONFIG_BT_L2CAP_TX_BUF_COUNT=32 + +#### Enable encoder support #### +# CONFIG_EC11=y +# CONFIG_EC11_TRIGGER_GLOBAL_THREAD=y \ No newline at end of file diff --git a/app/boards/shields/tracktyl/tracktyl_right.overlay b/app/boards/shields/tracktyl/tracktyl_right.overlay new file mode 100644 index 00000000000..ec8d5eca85a --- /dev/null +++ b/app/boards/shields/tracktyl/tracktyl_right.overlay @@ -0,0 +1,30 @@ +#include "tracktyl.dtsi" + +&default_transform { // The matrix transform for this board is 5 columns over because the left half is 5 columns wide according to the matrix. + col-offset = <5>; +}; + +&kscan0 { + col-gpios + = <&pro_micro 16 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 14 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 15 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 18 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 19 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + ; +}; + +&spi2 { + status = "okay"; +}; + +&trackball { + status = "okay"; +}; + +/* Enable the right encoder and define its pins */ +&right_encoder { + status = "disabled"; // disable encoders until further hardware impl + a-gpios = <&gpio1 4 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + b-gpios = <&gpio1 6 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; +}; \ No newline at end of file From c013d0f412830b3faf3c9759fd88d8b4295fcc3d Mon Sep 17 00:00:00 2001 From: Angzhi Li Date: Sat, 22 Apr 2023 14:41:19 -0700 Subject: [PATCH 128/157] change to encoder_right --- app/boards/shields/tracktyl/tracktyl_right.overlay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/boards/shields/tracktyl/tracktyl_right.overlay b/app/boards/shields/tracktyl/tracktyl_right.overlay index ec8d5eca85a..0b79987806d 100644 --- a/app/boards/shields/tracktyl/tracktyl_right.overlay +++ b/app/boards/shields/tracktyl/tracktyl_right.overlay @@ -23,7 +23,7 @@ }; /* Enable the right encoder and define its pins */ -&right_encoder { +&encoder_right { status = "disabled"; // disable encoders until further hardware impl a-gpios = <&gpio1 4 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; b-gpios = <&gpio1 6 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; From 390ecc52f2fddcad794045b570bfaf02822e0161 Mon Sep 17 00:00:00 2001 From: Angzhi Li Date: Sat, 22 Apr 2023 14:55:20 -0700 Subject: [PATCH 129/157] change to pro_micro_spi --- app/boards/shields/tracktyl/tracktyl.dtsi | 3 ++- app/boards/shields/tracktyl/tracktyl_right.overlay | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/boards/shields/tracktyl/tracktyl.dtsi b/app/boards/shields/tracktyl/tracktyl.dtsi index 72faa9cea4e..9b36d3e45b2 100644 --- a/app/boards/shields/tracktyl/tracktyl.dtsi +++ b/app/boards/shields/tracktyl/tracktyl.dtsi @@ -66,7 +66,7 @@ }; /* Declare the trackball */ -&spi2 { +&pro_micro_spi { status = "disabled"; miso-pin = <17>; // P0.17 @@ -75,6 +75,7 @@ cs-gpios = <&gpio0 24 GPIO_ACTIVE_LOW>; // P0.24 trackball: trackball@0 { + status = "disabled"; compatible = "pixart,pmw3360"; reg = <0>; label = "Pixart PMW3360"; diff --git a/app/boards/shields/tracktyl/tracktyl_right.overlay b/app/boards/shields/tracktyl/tracktyl_right.overlay index 0b79987806d..144a6bbb4f2 100644 --- a/app/boards/shields/tracktyl/tracktyl_right.overlay +++ b/app/boards/shields/tracktyl/tracktyl_right.overlay @@ -14,7 +14,7 @@ ; }; -&spi2 { +&pro_micro_spi { status = "okay"; }; From 48cd717247c6a765995127e400a3ae173b599d89 Mon Sep 17 00:00:00 2001 From: Angzhi Li Date: Sat, 22 Apr 2023 15:01:24 -0700 Subject: [PATCH 130/157] include CONFIG_NICE_NANO_POINTDEVICE flag in tracktyl_right.conf --- app/boards/shields/tracktyl/tracktyl_right.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/app/boards/shields/tracktyl/tracktyl_right.conf b/app/boards/shields/tracktyl/tracktyl_right.conf index e3a470aa3ca..058c2f1956d 100644 --- a/app/boards/shields/tracktyl/tracktyl_right.conf +++ b/app/boards/shields/tracktyl/tracktyl_right.conf @@ -5,6 +5,7 @@ CONFIG_ZMK_TRACKBALL_POLL_DURATION=8 CONFIG_PMW3360=y CONFIG_PMW3360_CPI=2000 CONFIG_PMW3360_CPI_DIVIDOR=1 +CONFIG_NICE_NANO_POINTDEVICE=1 # CONFIG_PMW3360_ORIENTATION_90=y CONFIG_PMW3610_RUN_DOWNSHIFT_TIME_MS=500 CONFIG_PMW3610_REST1_DOWNSHIFT_TIME_MS=5000 From 048ea879fcc8a87e599199fad2a4b341ee03317a Mon Sep 17 00:00:00 2001 From: Angzhi Li Date: Sat, 22 Apr 2023 15:06:29 -0700 Subject: [PATCH 131/157] omg --- app/boards/shields/tracktyl/tracktyl_right.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/boards/shields/tracktyl/tracktyl_right.conf b/app/boards/shields/tracktyl/tracktyl_right.conf index 058c2f1956d..378e7a84ad9 100644 --- a/app/boards/shields/tracktyl/tracktyl_right.conf +++ b/app/boards/shields/tracktyl/tracktyl_right.conf @@ -5,7 +5,7 @@ CONFIG_ZMK_TRACKBALL_POLL_DURATION=8 CONFIG_PMW3360=y CONFIG_PMW3360_CPI=2000 CONFIG_PMW3360_CPI_DIVIDOR=1 -CONFIG_NICE_NANO_POINTDEVICE=1 +CONFIG_NICE_NANO_POINTDEVICE=y # CONFIG_PMW3360_ORIENTATION_90=y CONFIG_PMW3610_RUN_DOWNSHIFT_TIME_MS=500 CONFIG_PMW3610_REST1_DOWNSHIFT_TIME_MS=5000 From f2a5138667380309f3ffbed1087b17b87dc86710 Mon Sep 17 00:00:00 2001 From: Angzhi Li Date: Sat, 22 Apr 2023 15:23:47 -0700 Subject: [PATCH 132/157] 3610 -> 3360 --- app/boards/shields/tracktyl/tracktyl_right.conf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/boards/shields/tracktyl/tracktyl_right.conf b/app/boards/shields/tracktyl/tracktyl_right.conf index 378e7a84ad9..c8528d1896f 100644 --- a/app/boards/shields/tracktyl/tracktyl_right.conf +++ b/app/boards/shields/tracktyl/tracktyl_right.conf @@ -7,9 +7,9 @@ CONFIG_PMW3360_CPI=2000 CONFIG_PMW3360_CPI_DIVIDOR=1 CONFIG_NICE_NANO_POINTDEVICE=y # CONFIG_PMW3360_ORIENTATION_90=y -CONFIG_PMW3610_RUN_DOWNSHIFT_TIME_MS=500 -CONFIG_PMW3610_REST1_DOWNSHIFT_TIME_MS=5000 -CONFIG_PMW3610_REST2_DOWNSHIFT_TIME_MS=30000 +CONFIG_PMW3360_RUN_DOWNSHIFT_TIME_MS=500 +CONFIG_PMW3360_REST1_DOWNSHIFT_TIME_MS=5000 +CONFIG_PMW3360_REST2_DOWNSHIFT_TIME_MS=30000 ### Bluetooth ### CONFIG_BT_PERIPHERAL_PREF_MAX_INT=9 From 8b3d2373dd9307ce83e31574b3065e81c9f85fab Mon Sep 17 00:00:00 2001 From: Angzhi Li Date: Sat, 22 Apr 2023 15:31:08 -0700 Subject: [PATCH 133/157] orientation 0 --- app/boards/shields/tracktyl/tracktyl_right.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/boards/shields/tracktyl/tracktyl_right.conf b/app/boards/shields/tracktyl/tracktyl_right.conf index c8528d1896f..0b1bd86c9bb 100644 --- a/app/boards/shields/tracktyl/tracktyl_right.conf +++ b/app/boards/shields/tracktyl/tracktyl_right.conf @@ -6,7 +6,7 @@ CONFIG_PMW3360=y CONFIG_PMW3360_CPI=2000 CONFIG_PMW3360_CPI_DIVIDOR=1 CONFIG_NICE_NANO_POINTDEVICE=y -# CONFIG_PMW3360_ORIENTATION_90=y +CONFIG_PMW3360_ORIENTATION_0=y CONFIG_PMW3360_RUN_DOWNSHIFT_TIME_MS=500 CONFIG_PMW3360_REST1_DOWNSHIFT_TIME_MS=5000 CONFIG_PMW3360_REST2_DOWNSHIFT_TIME_MS=30000 From 6585365ddbe5f136e29776c4731bef3ee3bd40e8 Mon Sep 17 00:00:00 2001 From: Angzhi Li Date: Sat, 22 Apr 2023 20:29:30 -0700 Subject: [PATCH 134/157] move trackball logic into nice_nano_v2.dts --- app/boards/arm/nice_nano/nice_nano_v2.dts | 29 +++++++++++++++++++ app/boards/shields/tracktyl/tracktyl.dtsi | 5 ++-- .../shields/tracktyl/tracktyl_right.overlay | 2 +- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/app/boards/arm/nice_nano/nice_nano_v2.dts b/app/boards/arm/nice_nano/nice_nano_v2.dts index ed2b35f4ebf..10d214fed67 100644 --- a/app/boards/arm/nice_nano/nice_nano_v2.dts +++ b/app/boards/arm/nice_nano/nice_nano_v2.dts @@ -24,3 +24,32 @@ label = "BATTERY"; }; }; + +&spi0 { + /* + 1 -> sck -> P0.22 -> left 7 + 2 -> miso -> P0.17 -> left 5 + 3 -> cs -> P0.24 -> left 8 + 4 -> dr -> P1.0 -> left 9 + 5 -> mosi -> P0.20 -> left 6 + 11 -> gnd -> left 3, left 4, right 2 + 12 -> vdd -> right 4 + */ + status = "disabled"; + miso-pin = <17>; // P0.17 + mosi-pin = <20>; // P0.20 + sck-pin = <22>; // P0.22 + cs-gpios = <&gpio0 24 GPIO_ACTIVE_LOW>; // P0.24 + + trackball: trackball@0 { + status = "disabled"; + compatible = "pixart,pmw3360"; + reg = <0>; + label = "Pixart PMW3360"; + irq-gpios = <&gpio1 0 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + spi-max-frequency = <2000000>; + scroll-layer = <5>; + }; +}; + +nice_nano_spi: &spi0 {}; \ No newline at end of file diff --git a/app/boards/shields/tracktyl/tracktyl.dtsi b/app/boards/shields/tracktyl/tracktyl.dtsi index 9b36d3e45b2..3cb0daa8a50 100644 --- a/app/boards/shields/tracktyl/tracktyl.dtsi +++ b/app/boards/shields/tracktyl/tracktyl.dtsi @@ -65,8 +65,8 @@ }; }; -/* Declare the trackball */ -&pro_micro_spi { +/* +&nice_nano_spi { status = "disabled"; miso-pin = <17>; // P0.17 @@ -84,3 +84,4 @@ scroll-layer = <5>; }; }; +*/ diff --git a/app/boards/shields/tracktyl/tracktyl_right.overlay b/app/boards/shields/tracktyl/tracktyl_right.overlay index 144a6bbb4f2..8a12e5f21f3 100644 --- a/app/boards/shields/tracktyl/tracktyl_right.overlay +++ b/app/boards/shields/tracktyl/tracktyl_right.overlay @@ -14,7 +14,7 @@ ; }; -&pro_micro_spi { +&nice_nano_spi { status = "okay"; }; From abacd1816c58b571f4ef06832d336c966f94c2e9 Mon Sep 17 00:00:00 2001 From: Angzhi Li Date: Sat, 22 Apr 2023 20:46:52 -0700 Subject: [PATCH 135/157] turn on logging --- app/boards/shields/tracktyl/tracktyl.conf | 3 +++ .../shields/tracktyl/tracktyl_right.conf | 25 +++++++++---------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/app/boards/shields/tracktyl/tracktyl.conf b/app/boards/shields/tracktyl/tracktyl.conf index e69de29bb2d..3db0ba76657 100644 --- a/app/boards/shields/tracktyl/tracktyl.conf +++ b/app/boards/shields/tracktyl/tracktyl.conf @@ -0,0 +1,3 @@ +CONFIG_ZMK_USB_LOGGING=y +CONFIG_LOG=y +CONFIG_ZMK_LOG_LEVEL_DBG=y \ No newline at end of file diff --git a/app/boards/shields/tracktyl/tracktyl_right.conf b/app/boards/shields/tracktyl/tracktyl_right.conf index 0b1bd86c9bb..f4885586b83 100644 --- a/app/boards/shields/tracktyl/tracktyl_right.conf +++ b/app/boards/shields/tracktyl/tracktyl_right.conf @@ -1,21 +1,20 @@ CONFIG_ZMK_MOUSE=y CONFIG_SPI=y CONFIG_PMW3360=y -CONFIG_ZMK_TRACKBALL_POLL_DURATION=8 -CONFIG_PMW3360=y -CONFIG_PMW3360_CPI=2000 -CONFIG_PMW3360_CPI_DIVIDOR=1 -CONFIG_NICE_NANO_POINTDEVICE=y -CONFIG_PMW3360_ORIENTATION_0=y -CONFIG_PMW3360_RUN_DOWNSHIFT_TIME_MS=500 -CONFIG_PMW3360_REST1_DOWNSHIFT_TIME_MS=5000 -CONFIG_PMW3360_REST2_DOWNSHIFT_TIME_MS=30000 +# CONFIG_ZMK_TRACKBALL_POLL_DURATION=8 +# CONFIG_PMW3360_CPI=2000 +# CONFIG_PMW3360_CPI_DIVIDOR=1 +# CONFIG_NICE_NANO_POINTDEVICE=y +# CONFIG_PMW3360_ORIENTATION_0=y +# CONFIG_PMW3360_RUN_DOWNSHIFT_TIME_MS=500 +# CONFIG_PMW3360_REST1_DOWNSHIFT_TIME_MS=5000 +# CONFIG_PMW3360_REST2_DOWNSHIFT_TIME_MS=30000 ### Bluetooth ### -CONFIG_BT_PERIPHERAL_PREF_MAX_INT=9 -CONFIG_BT_PERIPHERAL_PREF_LATENCY=16 -CONFIG_BT_BUF_ACL_TX_COUNT=32 -CONFIG_BT_L2CAP_TX_BUF_COUNT=32 +# CONFIG_BT_PERIPHERAL_PREF_MAX_INT=9 +# CONFIG_BT_PERIPHERAL_PREF_LATENCY=16 +# CONFIG_BT_BUF_ACL_TX_COUNT=32 +# CONFIG_BT_L2CAP_TX_BUF_COUNT=32 #### Enable encoder support #### # CONFIG_EC11=y From 47b297efab541d7e7bf83a8a6aad4818833967cf Mon Sep 17 00:00:00 2001 From: Angzhi Li Date: Sat, 22 Apr 2023 21:11:58 -0700 Subject: [PATCH 136/157] update --- app/boards/shields/tracktyl/tracktyl.conf | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/boards/shields/tracktyl/tracktyl.conf b/app/boards/shields/tracktyl/tracktyl.conf index 3db0ba76657..e69de29bb2d 100644 --- a/app/boards/shields/tracktyl/tracktyl.conf +++ b/app/boards/shields/tracktyl/tracktyl.conf @@ -1,3 +0,0 @@ -CONFIG_ZMK_USB_LOGGING=y -CONFIG_LOG=y -CONFIG_ZMK_LOG_LEVEL_DBG=y \ No newline at end of file From f80282a6ddfe07edd0a57945e89e82416c1ee4f8 Mon Sep 17 00:00:00 2001 From: Angzhi Date: Sat, 22 Apr 2023 22:42:57 -0700 Subject: [PATCH 137/157] add supported spi and i2c in nice_nano_v2.yaml --- app/boards/arm/nice_nano/nice_nano_v2.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/boards/arm/nice_nano/nice_nano_v2.yaml b/app/boards/arm/nice_nano/nice_nano_v2.yaml index d050ce993f9..170dab5cc8c 100644 --- a/app/boards/arm/nice_nano/nice_nano_v2.yaml +++ b/app/boards/arm/nice_nano/nice_nano_v2.yaml @@ -13,3 +13,5 @@ supported: - ieee802154 - pwm - watchdog + - spi + - i2c From 51ae4c566be49a9c74048e61d0ae1f9c12b1a116 Mon Sep 17 00:00:00 2001 From: Angzhi Date: Sat, 22 Apr 2023 23:22:57 -0700 Subject: [PATCH 138/157] move some of the tracktyl dt to the user config --- app/boards/arm/nice_nano/nice_nano.dtsi | 4 ++ app/boards/arm/nice_nano/nice_nano_v2.dts | 27 ------------- app/boards/shields/tracktyl/tracktyl.dtsi | 27 ++----------- .../shields/tracktyl/tracktyl_left.overlay | 4 +- .../shields/tracktyl/tracktyl_right.overlay | 40 ++++++++++++++----- 5 files changed, 39 insertions(+), 63 deletions(-) diff --git a/app/boards/arm/nice_nano/nice_nano.dtsi b/app/boards/arm/nice_nano/nice_nano.dtsi index 6c9d081c75b..9724121b494 100644 --- a/app/boards/arm/nice_nano/nice_nano.dtsi +++ b/app/boards/arm/nice_nano/nice_nano.dtsi @@ -49,6 +49,10 @@ scl-pin = <20>; }; +&spi0 { + compatible = "nordic,nrf-spim"; +}; + &uart0 { compatible = "nordic,nrf-uarte"; tx-pin = <6>; diff --git a/app/boards/arm/nice_nano/nice_nano_v2.dts b/app/boards/arm/nice_nano/nice_nano_v2.dts index 10d214fed67..f00d777b805 100644 --- a/app/boards/arm/nice_nano/nice_nano_v2.dts +++ b/app/boards/arm/nice_nano/nice_nano_v2.dts @@ -25,31 +25,4 @@ }; }; -&spi0 { - /* - 1 -> sck -> P0.22 -> left 7 - 2 -> miso -> P0.17 -> left 5 - 3 -> cs -> P0.24 -> left 8 - 4 -> dr -> P1.0 -> left 9 - 5 -> mosi -> P0.20 -> left 6 - 11 -> gnd -> left 3, left 4, right 2 - 12 -> vdd -> right 4 - */ - status = "disabled"; - miso-pin = <17>; // P0.17 - mosi-pin = <20>; // P0.20 - sck-pin = <22>; // P0.22 - cs-gpios = <&gpio0 24 GPIO_ACTIVE_LOW>; // P0.24 - - trackball: trackball@0 { - status = "disabled"; - compatible = "pixart,pmw3360"; - reg = <0>; - label = "Pixart PMW3360"; - irq-gpios = <&gpio1 0 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; - spi-max-frequency = <2000000>; - scroll-layer = <5>; - }; -}; - nice_nano_spi: &spi0 {}; \ No newline at end of file diff --git a/app/boards/shields/tracktyl/tracktyl.dtsi b/app/boards/shields/tracktyl/tracktyl.dtsi index 3cb0daa8a50..0676678855e 100644 --- a/app/boards/shields/tracktyl/tracktyl.dtsi +++ b/app/boards/shields/tracktyl/tracktyl.dtsi @@ -52,36 +52,15 @@ // define the two encoders encoder_left: encoder_left { compatible = "alps,ec11"; - status = "disabled"; + status = "disabled"; label = "LEFT_ENCODER"; resolution = <2>; }; encoder_right: encoder_right { compatible = "alps,ec11"; - status = "disabled"; + status = "disabled"; label = "RIGHT_ENCODER"; - resolution = <2>; + resolution = <2>; }; }; - -/* -&nice_nano_spi { - status = "disabled"; - - miso-pin = <17>; // P0.17 - mosi-pin = <20>; // P0.20 - sck-pin = <22>; // P0.22 - cs-gpios = <&gpio0 24 GPIO_ACTIVE_LOW>; // P0.24 - - trackball: trackball@0 { - status = "disabled"; - compatible = "pixart,pmw3360"; - reg = <0>; - label = "Pixart PMW3360"; - irq-gpios = <&gpio1 0 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; - spi-max-frequency = <2000000>; - scroll-layer = <5>; - }; -}; -*/ diff --git a/app/boards/shields/tracktyl/tracktyl_left.overlay b/app/boards/shields/tracktyl/tracktyl_left.overlay index b5d58ebf99c..24a9c6016c0 100644 --- a/app/boards/shields/tracktyl/tracktyl_left.overlay +++ b/app/boards/shields/tracktyl/tracktyl_left.overlay @@ -17,6 +17,6 @@ // Define encoder pins &encoder_left { status = "disabled"; // disable encoders until further hardware impl - a-gpios = <&gpio1 4 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; - b-gpios = <&gpio1 6 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + a-gpios = <&gpio0 31 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + b-gpios = <&gpio0 29 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; }; diff --git a/app/boards/shields/tracktyl/tracktyl_right.overlay b/app/boards/shields/tracktyl/tracktyl_right.overlay index 8a12e5f21f3..5b0c9f4e7d4 100644 --- a/app/boards/shields/tracktyl/tracktyl_right.overlay +++ b/app/boards/shields/tracktyl/tracktyl_right.overlay @@ -14,17 +14,37 @@ ; }; -&nice_nano_spi { - status = "okay"; -}; - -&trackball { - status = "okay"; -}; - /* Enable the right encoder and define its pins */ &encoder_right { status = "disabled"; // disable encoders until further hardware impl - a-gpios = <&gpio1 4 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; - b-gpios = <&gpio1 6 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + a-gpios = <&gpio0 31 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + b-gpios = <&gpio0 29 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; +}; + +&nice_nano_spi { + /* + sck -> P0.22 -> left 7 + miso -> P0.17 -> left 5 + cs -> P0.24 -> left 8 + irq -> P1.0 -> left 9 + mosi -> P0.20 -> left 6 + gnd -> left 3, left 4, right 2 + vdd -> right 4 + */ + compatible = "nordic,nrf-spim"; + status = "disabled"; + miso-pin = <17>; // P0.17 + mosi-pin = <20>; // P0.20 + sck-pin = <22>; // P0.22 + cs-gpios = <&gpio0 24 GPIO_ACTIVE_LOW>; // P0.24 + + trackball: trackball@0 { + status = "disabled"; + compatible = "pixart,pmw3360"; + reg = <0>; + label = "Pixart PMW3360"; + irq-gpios = <&gpio1 0 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + spi-max-frequency = <2000000>; + scroll-layer = <5>; + }; }; \ No newline at end of file From 9e8248a8ab5968c814dec4658c1e7aa2320e8bf2 Mon Sep 17 00:00:00 2001 From: Angzhi Date: Sat, 22 Apr 2023 23:32:50 -0700 Subject: [PATCH 139/157] move trackball setting to tracktyl.dtsi --- app/boards/shields/tracktyl/tracktyl.dtsi | 28 +++++++++++++++++++ .../shields/tracktyl/tracktyl_right.overlay | 28 ------------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/app/boards/shields/tracktyl/tracktyl.dtsi b/app/boards/shields/tracktyl/tracktyl.dtsi index 0676678855e..b057a18819d 100644 --- a/app/boards/shields/tracktyl/tracktyl.dtsi +++ b/app/boards/shields/tracktyl/tracktyl.dtsi @@ -64,3 +64,31 @@ resolution = <2>; }; }; + +&nice_nano_spi { + /* + sck -> P0.22 -> left 7 + miso -> P0.17 -> left 5 + cs -> P0.24 -> left 8 + irq -> P1.0 -> left 9 + mosi -> P0.20 -> left 6 + gnd -> left 3, left 4, right 2 + vdd -> right 4 + */ + compatible = "nordic,nrf-spim"; + status = "disabled"; + miso-pin = <17>; // P0.17 + mosi-pin = <20>; // P0.20 + sck-pin = <22>; // P0.22 + cs-gpios = <&gpio0 24 GPIO_ACTIVE_LOW>; // P0.24 + + trackball: trackball@0 { + status = "disabled"; + compatible = "pixart,pmw3360"; + reg = <0>; + label = "Pixart PMW3360"; + irq-gpios = <&gpio1 0 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + spi-max-frequency = <2000000>; + scroll-layer = <5>; + }; +}; diff --git a/app/boards/shields/tracktyl/tracktyl_right.overlay b/app/boards/shields/tracktyl/tracktyl_right.overlay index 5b0c9f4e7d4..f6921a09d0b 100644 --- a/app/boards/shields/tracktyl/tracktyl_right.overlay +++ b/app/boards/shields/tracktyl/tracktyl_right.overlay @@ -20,31 +20,3 @@ a-gpios = <&gpio0 31 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; b-gpios = <&gpio0 29 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; }; - -&nice_nano_spi { - /* - sck -> P0.22 -> left 7 - miso -> P0.17 -> left 5 - cs -> P0.24 -> left 8 - irq -> P1.0 -> left 9 - mosi -> P0.20 -> left 6 - gnd -> left 3, left 4, right 2 - vdd -> right 4 - */ - compatible = "nordic,nrf-spim"; - status = "disabled"; - miso-pin = <17>; // P0.17 - mosi-pin = <20>; // P0.20 - sck-pin = <22>; // P0.22 - cs-gpios = <&gpio0 24 GPIO_ACTIVE_LOW>; // P0.24 - - trackball: trackball@0 { - status = "disabled"; - compatible = "pixart,pmw3360"; - reg = <0>; - label = "Pixart PMW3360"; - irq-gpios = <&gpio1 0 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; - spi-max-frequency = <2000000>; - scroll-layer = <5>; - }; -}; \ No newline at end of file From 9ced3e4ad271c0fe245c99c68e0d5fce96e9a44f Mon Sep 17 00:00:00 2001 From: Angzhi Date: Sat, 22 Apr 2023 23:39:27 -0700 Subject: [PATCH 140/157] move tracktyl_right back here --- app/boards/shields/tracktyl/tracktyl_right.conf | 16 ++++++++-------- .../shields/tracktyl/tracktyl_right.overlay | 8 ++++++++ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/app/boards/shields/tracktyl/tracktyl_right.conf b/app/boards/shields/tracktyl/tracktyl_right.conf index f4885586b83..9c068d1e5a2 100644 --- a/app/boards/shields/tracktyl/tracktyl_right.conf +++ b/app/boards/shields/tracktyl/tracktyl_right.conf @@ -1,14 +1,14 @@ CONFIG_ZMK_MOUSE=y CONFIG_SPI=y +CONFIG_NICE_NANO_POINTDEVICE=y CONFIG_PMW3360=y -# CONFIG_ZMK_TRACKBALL_POLL_DURATION=8 -# CONFIG_PMW3360_CPI=2000 -# CONFIG_PMW3360_CPI_DIVIDOR=1 -# CONFIG_NICE_NANO_POINTDEVICE=y -# CONFIG_PMW3360_ORIENTATION_0=y -# CONFIG_PMW3360_RUN_DOWNSHIFT_TIME_MS=500 -# CONFIG_PMW3360_REST1_DOWNSHIFT_TIME_MS=5000 -# CONFIG_PMW3360_REST2_DOWNSHIFT_TIME_MS=30000 +CONFIG_ZMK_TRACKBALL_POLL_DURATION=8 +CONFIG_PMW3360_CPI=2000 +CONFIG_PMW3360_CPI_DIVIDOR=1 +CONFIG_PMW3360_ORIENTATION_0=y +CONFIG_PMW3360_RUN_DOWNSHIFT_TIME_MS=500 +CONFIG_PMW3360_REST1_DOWNSHIFT_TIME_MS=5000 +CONFIG_PMW3360_REST2_DOWNSHIFT_TIME_MS=30000 ### Bluetooth ### # CONFIG_BT_PERIPHERAL_PREF_MAX_INT=9 diff --git a/app/boards/shields/tracktyl/tracktyl_right.overlay b/app/boards/shields/tracktyl/tracktyl_right.overlay index f6921a09d0b..7dd4ad6e125 100644 --- a/app/boards/shields/tracktyl/tracktyl_right.overlay +++ b/app/boards/shields/tracktyl/tracktyl_right.overlay @@ -20,3 +20,11 @@ a-gpios = <&gpio0 31 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; b-gpios = <&gpio0 29 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; }; + +&nice_nano_spi { + status = "okay"; +}; + +&trackball { + status = "okay"; +}; \ No newline at end of file From 2a5ec634ec9439239700f649075830db23f3fc40 Mon Sep 17 00:00:00 2001 From: Angzhi Date: Sat, 22 Apr 2023 23:59:17 -0700 Subject: [PATCH 141/157] add tracktyl left name --- app/boards/shields/tracktyl/Kconfig.defconfig | 5 +++++ app/boards/shields/tracktyl/tracktyl_left.conf | 3 ++- app/boards/shields/tracktyl/tracktyl_right.conf | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/boards/shields/tracktyl/Kconfig.defconfig b/app/boards/shields/tracktyl/Kconfig.defconfig index e4dfaa3d8a7..d638f636826 100644 --- a/app/boards/shields/tracktyl/Kconfig.defconfig +++ b/app/boards/shields/tracktyl/Kconfig.defconfig @@ -8,6 +8,11 @@ if SHIELD_TRACKTYL_RIGHT endif +if SHIELD_TRACKTYL_LEFT + + config ZMK_KEYBOARD_NAME + default "Tracktyl_left" + if SHIELD_TRACKTYL_LEFT || SHIELD_TRACKTYL_RIGHT config ZMK_SPLIT diff --git a/app/boards/shields/tracktyl/tracktyl_left.conf b/app/boards/shields/tracktyl/tracktyl_left.conf index 131e81df07c..80684aebe84 100644 --- a/app/boards/shields/tracktyl/tracktyl_left.conf +++ b/app/boards/shields/tracktyl/tracktyl_left.conf @@ -1,3 +1,4 @@ #### Enable encoder support #### # CONFIG_EC11=y -# CONFIG_EC11_TRIGGER_GLOBAL_THREAD=y \ No newline at end of file +# CONFIG_EC11_TRIGGER_GLOBAL_THREAD=y +CONFIG_NICE_NANO_SHIELD_SLAVE=y \ No newline at end of file diff --git a/app/boards/shields/tracktyl/tracktyl_right.conf b/app/boards/shields/tracktyl/tracktyl_right.conf index 9c068d1e5a2..990177349db 100644 --- a/app/boards/shields/tracktyl/tracktyl_right.conf +++ b/app/boards/shields/tracktyl/tracktyl_right.conf @@ -1,6 +1,7 @@ CONFIG_ZMK_MOUSE=y CONFIG_SPI=y CONFIG_NICE_NANO_POINTDEVICE=y +CONFIG_NICE_NANO_SHIELD_MASTER=y CONFIG_PMW3360=y CONFIG_ZMK_TRACKBALL_POLL_DURATION=8 CONFIG_PMW3360_CPI=2000 From 12b169878ffc718f57496280e8b4286bdba2c711 Mon Sep 17 00:00:00 2001 From: Angzhi Li Date: Sun, 23 Apr 2023 10:56:50 -0700 Subject: [PATCH 142/157] update to v3.2.0 --- app/west.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/west.yml b/app/west.yml index a0c56501faa..aef4d749f1a 100644 --- a/app/west.yml +++ b/app/west.yml @@ -7,7 +7,7 @@ manifest: projects: - name: zephyr remote: zmkfirmware - revision: v3.0.0+zmk-fixes + revision: v3.2.0+zmk-fixes clone-depth: 1 import: # TODO: Rename once upstream offers option like `exclude` or `denylist` From 3528c143ff2725814b666b72ecfa2d155f940ed8 Mon Sep 17 00:00:00 2001 From: Angzhi Date: Sun, 23 Apr 2023 13:29:31 -0700 Subject: [PATCH 143/157] add dts report in build-user-config --- .github/workflows/build-user-config.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/build-user-config.yml b/.github/workflows/build-user-config.yml index c31b6783c92..3172fb72189 100644 --- a/.github/workflows/build-user-config.yml +++ b/.github/workflows/build-user-config.yml @@ -109,6 +109,12 @@ jobs: set -x west build -s zmk/app -b ${{ matrix.board }} -- -DZMK_CONFIG=${GITHUB_WORKSPACE}/${{ inputs.config_path }} ${{ steps.variables.outputs.extra-cmake-args }} ${{ matrix.cmake-args }} + - name: ${{ steps.variables.outputs.display-name }} DTS File + if: ${{ always() }} + run: | + if [ -f "build/zephyr/${{ matrix.board }}.dts.pre.tmp" ]; then cat -n build/zephyr/${{ matrix.board }}.dts.pre.tmp; fi + if [ -f "build/zephyr/zephyr.dts" ]; then cat -n build/zephyr/zephyr.dts; fi + - name: ${{ steps.variables.outputs.display-name }} Kconfig file run: grep -v -e "^#" -e "^$" build/zephyr/.config | sort From e6f7856398830e89d57154c0f530a56ae2ee61b1 Mon Sep 17 00:00:00 2001 From: Angzhi Date: Sun, 23 Apr 2023 13:33:38 -0700 Subject: [PATCH 144/157] update build.yml to that of zmk main branch --- .github/workflows/build.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a5989cef845..fc6c72d012a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,14 +17,14 @@ jobs: if: ${{ always() }} runs-on: ubuntu-latest container: - image: docker.io/zmkfirmware/zmk-build-arm:3.0 + image: docker.io/zmkfirmware/zmk-build-arm:3.2 needs: compile-matrix strategy: matrix: include: ${{ fromJSON(needs.compile-matrix.outputs.include-list) }} steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Cache west modules uses: actions/cache@v3.0.2 env: @@ -151,7 +151,7 @@ jobs: core-include: ${{ steps.core-list.outputs.result }} steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Use Node.js uses: actions/setup-node@v2 with: @@ -180,7 +180,7 @@ jobs: boards-include: ${{ steps.boards-list.outputs.result }} steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Use Node.js uses: actions/setup-node@v2 with: @@ -304,7 +304,7 @@ jobs: organized-metadata: ${{ steps.organize-metadata.outputs.result }} steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Use Node.js uses: actions/setup-node@v2 with: From 581c8a2390a83fa64e832a78eb722f17f1b9274e Mon Sep 17 00:00:00 2001 From: Angzhi Date: Sun, 23 Apr 2023 13:44:36 -0700 Subject: [PATCH 145/157] try to fix build --- app/boards/shields/tracktyl/Kconfig.defconfig | 1 + .../dts/bindings/sensor/zmk,battery-voltage-divider.yaml | 5 ----- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/app/boards/shields/tracktyl/Kconfig.defconfig b/app/boards/shields/tracktyl/Kconfig.defconfig index d638f636826..af6defba297 100644 --- a/app/boards/shields/tracktyl/Kconfig.defconfig +++ b/app/boards/shields/tracktyl/Kconfig.defconfig @@ -12,6 +12,7 @@ if SHIELD_TRACKTYL_LEFT config ZMK_KEYBOARD_NAME default "Tracktyl_left" +endif if SHIELD_TRACKTYL_LEFT || SHIELD_TRACKTYL_RIGHT diff --git a/app/drivers/zephyr/dts/bindings/sensor/zmk,battery-voltage-divider.yaml b/app/drivers/zephyr/dts/bindings/sensor/zmk,battery-voltage-divider.yaml index c4c6f80c5e8..d9e07b797e8 100644 --- a/app/drivers/zephyr/dts/bindings/sensor/zmk,battery-voltage-divider.yaml +++ b/app/drivers/zephyr/dts/bindings/sensor/zmk,battery-voltage-divider.yaml @@ -6,8 +6,3 @@ description: Battery SoC monitoring using voltage divider compatible: "zmk,battery-voltage-divider" include: voltage-divider.yaml - -properties: - label: - required: true - type: string From eff8a33b8e497ad40162bdc8763577c88aba05a5 Mon Sep 17 00:00:00 2001 From: Angzhi Li Date: Sun, 23 Apr 2023 16:30:42 -0700 Subject: [PATCH 146/157] revert build.yml change --- .github/workflows/build.yml | 2 +- .../dts/bindings/sensor/zmk,battery-voltage-divider.yaml | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fc6c72d012a..927ab409fc9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,7 +17,7 @@ jobs: if: ${{ always() }} runs-on: ubuntu-latest container: - image: docker.io/zmkfirmware/zmk-build-arm:3.2 + image: docker.io/zmkfirmware/zmk-build-arm:3.0 needs: compile-matrix strategy: matrix: diff --git a/app/drivers/zephyr/dts/bindings/sensor/zmk,battery-voltage-divider.yaml b/app/drivers/zephyr/dts/bindings/sensor/zmk,battery-voltage-divider.yaml index d9e07b797e8..c4c6f80c5e8 100644 --- a/app/drivers/zephyr/dts/bindings/sensor/zmk,battery-voltage-divider.yaml +++ b/app/drivers/zephyr/dts/bindings/sensor/zmk,battery-voltage-divider.yaml @@ -6,3 +6,8 @@ description: Battery SoC monitoring using voltage divider compatible: "zmk,battery-voltage-divider" include: voltage-divider.yaml + +properties: + label: + required: true + type: string From 1e37cd65b0bf0785849bc21853b6e4ef50427c41 Mon Sep 17 00:00:00 2001 From: Angzhi Li Date: Sun, 23 Apr 2023 16:35:20 -0700 Subject: [PATCH 147/157] revert west.yml --- app/west.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/west.yml b/app/west.yml index aef4d749f1a..a0c56501faa 100644 --- a/app/west.yml +++ b/app/west.yml @@ -7,7 +7,7 @@ manifest: projects: - name: zephyr remote: zmkfirmware - revision: v3.2.0+zmk-fixes + revision: v3.0.0+zmk-fixes clone-depth: 1 import: # TODO: Rename once upstream offers option like `exclude` or `denylist` From 5fdb21cd893162b864d96756de7ae83e6d0c2a97 Mon Sep 17 00:00:00 2001 From: Angzhi Li Date: Sun, 23 Apr 2023 20:48:07 -0700 Subject: [PATCH 148/157] add more information in logging --- app/boards/arm/nice_nano/trackball/trackball_new.c | 4 ++-- app/drivers/sensor/pixart/pmw3360/pmw3360.c | 4 ++-- app/src/endpoints.c | 6 +++--- app/src/point_device/trackballs.c | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/boards/arm/nice_nano/trackball/trackball_new.c b/app/boards/arm/nice_nano/trackball/trackball_new.c index 7c8004cbb6c..62f93ef2259 100644 --- a/app/boards/arm/nice_nano/trackball/trackball_new.c +++ b/app/boards/arm/nice_nano/trackball/trackball_new.c @@ -209,7 +209,7 @@ static int trackball_init() { } while (err == -EBUSY && count < 50); if (err) { - LOG_ERR("Cannot enable trigger"); + LOG_ERR("Cannot enable trigger in nice_nano trackball_new.c %d ", err); return err; } @@ -241,7 +241,7 @@ SYS_INIT(trackball_init, APPLICATION, CONFIG_SENSOR_INIT_PRIORITY); /* the following code dynamically changes poll rate based on endpoint changing */ // The code execution is driven by the zmk event system instead of zephyr interrupt routine int trackball_endpoint_listener(const zmk_event_t *eh) { - LOG_INF("endpoint changing..."); + LOG_INF("endpoint changing... in trackball_new.c"); // update polling parameters switch (zmk_endpoints_selected()) { diff --git a/app/drivers/sensor/pixart/pmw3360/pmw3360.c b/app/drivers/sensor/pixart/pmw3360/pmw3360.c index 34cf12a9846..26963806fa4 100644 --- a/app/drivers/sensor/pixart/pmw3360/pmw3360.c +++ b/app/drivers/sensor/pixart/pmw3360/pmw3360.c @@ -575,9 +575,9 @@ static int pmw3360_async_init_fw_load_verify(const struct device *dev) { return err; } - LOG_DBG("Optical chip firmware ID: 0x%x", fw_id); + LOG_DBG("Optical chip firmware ID: 0x%x ", fw_id); if (fw_id != PMW3360_FIRMWARE_ID) { - LOG_ERR("Chip is not running from SROM!"); + LOG_ERR("Chip is not running from SROM!, got fw_id as 0x%x, the expected fw_id is 0x%x ", fw_id, PMW3360_FIRMWARE_ID); return -EIO; } diff --git a/app/src/endpoints.c b/app/src/endpoints.c index 6bf964f5e86..d13c75a25bb 100644 --- a/app/src/endpoints.c +++ b/app/src/endpoints.c @@ -78,7 +78,7 @@ static int send_keyboard_report() { case ZMK_ENDPOINT_USB: { int err = zmk_usb_hid_send_report((uint8_t *)keyboard_report, sizeof(*keyboard_report)); if (err) { - LOG_ERR("FAILED TO SEND OVER USB: %d", err); + LOG_ERR("FAILED TO SEND OVER USB DUE TO KEYBOARD_REPORT: %d ", err); } return err; } @@ -108,7 +108,7 @@ static int send_consumer_report() { case ZMK_ENDPOINT_USB: { int err = zmk_usb_hid_send_report((uint8_t *)consumer_report, sizeof(*consumer_report)); if (err) { - LOG_ERR("FAILED TO SEND OVER USB: %d", err); + LOG_ERR("FAILED TO SEND OVER USB DUE TO CONSUMER: %d ", err); } return err; } @@ -152,7 +152,7 @@ int zmk_endpoints_send_mouse_report() { case ZMK_ENDPOINT_USB: { int err = zmk_usb_hid_send_report((uint8_t *)mouse_report, sizeof(*mouse_report)); if (err) { - LOG_ERR("FAILED TO SEND OVER USB: %d", err); + LOG_ERR("FAILED TO SEND OVER USB DUE TO MOUSE: %d ", err); } return err; } diff --git a/app/src/point_device/trackballs.c b/app/src/point_device/trackballs.c index 54c7ae89fa8..7e2059e1722 100644 --- a/app/src/point_device/trackballs.c +++ b/app/src/point_device/trackballs.c @@ -207,7 +207,7 @@ static void zmk_trackballs_init_item(const char *node, uint8_t i, uint8_t abs_i) } while (err == -EBUSY && count < 100); if (err) { - LOG_ERR("Cannot enable trigger"); + LOG_ERR("Cannot enable trigger in app/src/point_device/trackball.c %d ", err); return; } From e6c226bb3f347d000adc7ce4663c3228bb471e2c Mon Sep 17 00:00:00 2001 From: Angzhi Li Date: Mon, 24 Apr 2023 20:30:26 -0700 Subject: [PATCH 149/157] set the srom upload verify a bit longer --- app/drivers/sensor/pixart/pmw3360/pmw3360.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/drivers/sensor/pixart/pmw3360/pmw3360.c b/app/drivers/sensor/pixart/pmw3360/pmw3360.c index 26963806fa4..a742ffa052f 100644 --- a/app/drivers/sensor/pixart/pmw3360/pmw3360.c +++ b/app/drivers/sensor/pixart/pmw3360/pmw3360.c @@ -130,7 +130,8 @@ static const int32_t async_init_delay[ASYNC_INIT_STEP_COUNT] = { [ASYNC_INIT_STEP_POWER_UP] = 1, [ASYNC_INIT_STEP_FW_LOAD_START] = 50, // required in spec [ASYNC_INIT_STEP_FW_LOAD_CONTINUE] = 10, // required in spec - [ASYNC_INIT_STEP_FW_LOAD_VERIFY] = 1, [ASYNC_INIT_STEP_CONFIGURE] = 0, + [ASYNC_INIT_STEP_FW_LOAD_VERIFY] = 50, + [ASYNC_INIT_STEP_CONFIGURE] = 0, }; static int pmw3360_async_init_power_up(const struct device *dev); From 7becf1c1c11d9c42c5f18c80d07df4ffb59ce4af Mon Sep 17 00:00:00 2001 From: Angzhi Date: Tue, 25 Apr 2023 21:41:07 -0700 Subject: [PATCH 150/157] adding trackpad folder to nice_nano --- app/boards/arm/nice_nano/nice_nano_v2.dts | 2 +- .../arm/nice_nano/trackpad/CMakeLists.txt | 4 + app/boards/arm/nice_nano/trackpad/trackpad.c | 130 ++++++++++++++++++ 3 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 app/boards/arm/nice_nano/trackpad/CMakeLists.txt create mode 100644 app/boards/arm/nice_nano/trackpad/trackpad.c diff --git a/app/boards/arm/nice_nano/nice_nano_v2.dts b/app/boards/arm/nice_nano/nice_nano_v2.dts index f00d777b805..a32a897d833 100644 --- a/app/boards/arm/nice_nano/nice_nano_v2.dts +++ b/app/boards/arm/nice_nano/nice_nano_v2.dts @@ -25,4 +25,4 @@ }; }; -nice_nano_spi: &spi0 {}; \ No newline at end of file +nice_nano_spi: &spi2 {}; \ No newline at end of file diff --git a/app/boards/arm/nice_nano/trackpad/CMakeLists.txt b/app/boards/arm/nice_nano/trackpad/CMakeLists.txt new file mode 100644 index 00000000000..6148e7d61ee --- /dev/null +++ b/app/boards/arm/nice_nano/trackpad/CMakeLists.txt @@ -0,0 +1,4 @@ +zephyr_library() +zephyr_library_sources(trackpad.c) +zephyr_library_include_directories(${CMAKE_SOURCE_DIR}/include) +zephyr_library_include_directories(${ZEPHYR_BASE}/drivers) diff --git a/app/boards/arm/nice_nano/trackpad/trackpad.c b/app/boards/arm/nice_nano/trackpad/trackpad.c new file mode 100644 index 00000000000..b60280cbc00 --- /dev/null +++ b/app/boards/arm/nice_nano/trackpad/trackpad.c @@ -0,0 +1,130 @@ +#define DT_DRV_COMPAT cirque_pinnacle + +#include +#include + +#include +#include +#include +#include + +#define SCROLL_DIV_FACTOR 5 +#define SCROLL_LAYER_INDEX COND_CODE_0(DT_INST_NODE_HAS_PROP(0, scroll_layer), (255), \ + (DT_INST_PROP(0, scroll_layer))) + + +/* #if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) */ +// in us +static int64_t last_interrupt_time = 0; +static int64_t current_interrupt_time = 0; +static int64_t interrupt_interval = 0; +static int64_t handler_duration = 0; +static int64_t send_report_duration = 0; +static int64_t idle_interval = 0; +static int64_t time_buffer = 0; +/* #endif */ + +static struct sensor_value dx, dy, btn; + +const struct device *trackpad = DEVICE_DT_GET(DT_DRV_INST(0)); + +LOG_MODULE_REGISTER(trackpad, CONFIG_SENSOR_LOG_LEVEL); + +/* update and send report */ +static int64_t trackpad_update_handler(struct k_work *work) { +/* #if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) */ + int64_t start_time = k_ticks_to_us_floor64(k_uptime_ticks()); +/* #endif */ + + // remaining scroll from last update + static int8_t scroll_ver_rem = 0, scroll_hor_rem = 0; + uint8_t button; + static uint8_t last_button = 0; + static uint8_t last_pressed = 0; + + // update report with latest position + zmk_hid_mouse_movement_set(0, 0); + zmk_hid_mouse_scroll_set(0, 0); + + const uint8_t layer = zmk_keymap_highest_layer_active(); + if (layer == SCROLL_LAYER_INDEX) { // lower + const int16_t total_hor = dx.val1 + scroll_hor_rem, total_ver = -(dy.val1 + scroll_ver_rem); + scroll_hor_rem = total_hor % SCROLL_DIV_FACTOR; + scroll_ver_rem = total_ver % SCROLL_DIV_FACTOR; + zmk_hid_mouse_scroll_update(total_hor / SCROLL_DIV_FACTOR, total_ver / SCROLL_DIV_FACTOR); + button = RCLK; + } else { + zmk_hid_mouse_movement_update(CLAMP(dx.val1, INT8_MIN, INT8_MAX), CLAMP(dy.val1, INT8_MIN, INT8_MAX)); + button = LCLK; + } + + if (!last_pressed && btn.val1) { + zmk_hid_mouse_buttons_press(button); + last_button = button; + } else if (last_pressed && !btn.val1) { + zmk_hid_mouse_buttons_release(last_button); + } + // send the report to host + zmk_endpoints_send_mouse_report(); + last_pressed = btn.val1; + +/* #if IS_ENABLED(CONFIG_SENSOR_LOG_LEVEL_DBG) */ + return start_time = k_ticks_to_us_floor64(k_uptime_ticks()) - start_time; +/* #else */ +/* return 0; */ +/* #endif */ +} + +static void handle_trackpad(const struct device *dev, const struct sensor_trigger *trig) { + current_interrupt_time = k_ticks_to_us_floor64(k_uptime_ticks()); + interrupt_interval = current_interrupt_time - last_interrupt_time; + idle_interval = current_interrupt_time - time_buffer; + + + int ret = sensor_sample_fetch(dev); + if (ret < 0) { + LOG_ERR("fetch: %d", ret); + return; + } + + ret = sensor_channel_get(dev, SENSOR_CHAN_POS_DX, &dx); + if (ret < 0) { + LOG_ERR("get dx: %d", ret); + return; + } + ret = sensor_channel_get(dev, SENSOR_CHAN_POS_DY, &dy); + if (ret < 0) { + LOG_ERR("get dy: %d", ret); + return; + } + ret = sensor_channel_get(dev, SENSOR_CHAN_PRESS, &btn); + if (ret < 0) { + LOG_ERR("get btn: %d", ret); + return; + } + + send_report_duration = trackpad_update_handler(NULL); + + time_buffer = k_ticks_to_us_floor64(k_uptime_ticks()); + handler_duration = time_buffer - current_interrupt_time; + + LOG_INF("interrupt interval (us): %lld ; idle: %lld ; handler time cost: %lld",\ + interrupt_interval, idle_interval, handler_duration); + LOG_INF("Send report time cost: %lld; Position: %d %d", send_report_duration, dx.val1, dy.val1); + last_interrupt_time = current_interrupt_time; +} + +static int trackpad_init() { + struct sensor_trigger trigger = { + .type = SENSOR_TRIG_DATA_READY, + .chan = SENSOR_CHAN_ALL, + }; + printk("trackpad"); + if (sensor_trigger_set(trackpad, &trigger, handle_trackpad) < 0) { + LOG_ERR("can't set trigger"); + return -EIO; + }; + return 0; +} + +SYS_INIT(trackpad_init, APPLICATION, CONFIG_ZMK_KSCAN_INIT_PRIORITY); From e4b117a063b01fb6f94b41345a5cb66d09f674cd Mon Sep 17 00:00:00 2001 From: Angzhi Li Date: Thu, 27 Apr 2023 09:42:34 +0000 Subject: [PATCH 151/157] finally working version of pmw3360 --- .vscode/settings.json | 4 +- app/boards/arm/nice_nano/Kconfig.defconfig | 58 ++++---- app/boards/shields/tracktyl/Kconfig.defconfig | 3 + app/boards/shields/tracktyl/tracktyl.conf | 0 app/boards/shields/tracktyl/tracktyl.dtsi | 32 ++--- app/boards/shields/tracktyl/tracktyl.keymap | 127 ++++++++++++++++++ .../shields/tracktyl/tracktyl_left.conf | 4 +- .../shields/tracktyl/tracktyl_left.overlay | 21 ++- .../shields/tracktyl/tracktyl_right.conf | 33 +++-- .../shields/tracktyl/tracktyl_right.overlay | 21 ++- app/drivers/sensor/pixart/pmw3360/pmw3360.c | 2 +- 11 files changed, 241 insertions(+), 64 deletions(-) delete mode 100644 app/boards/shields/tracktyl/tracktyl.conf create mode 100644 app/boards/shields/tracktyl/tracktyl.keymap diff --git a/.vscode/settings.json b/.vscode/settings.json index 2730549a234..ef683552358 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,9 @@ { "files.associations": { "*.overlay": "dts", - "*.keymap": "dts" + "*.keymap": "dts", + "usb_conn_state_changed.h": "c", + "ble_active_profile_changed.h": "c" }, "python.formatting.provider": "black" } \ No newline at end of file diff --git a/app/boards/arm/nice_nano/Kconfig.defconfig b/app/boards/arm/nice_nano/Kconfig.defconfig index 2cdf8c92b04..1e2e1891255 100644 --- a/app/boards/arm/nice_nano/Kconfig.defconfig +++ b/app/boards/arm/nice_nano/Kconfig.defconfig @@ -3,45 +3,45 @@ if BOARD_NICE_NANO || BOARD_NICE_NANO_V2 -config BOARD - default "nice_nano" + config BOARD + default "nice_nano" -if USB_DEVICE_STACK + if USB_DEVICE_STACK -config USB_NRFX - default y + config USB_NRFX + default y -endif # USB_DEVICE_STACK + endif # USB_DEVICE_STACK -config BT_CTLR - default BT + config BT_CTLR + default BT -config ZMK_BLE - default y + config ZMK_BLE + default y -config ZMK_USB - default y + config ZMK_USB + default y -# Role of the keyboard in split setup, default is master (i.e., central) -## NOTE: this is a dedicated config option for nrfmacro board, so that all shields -## using this board can share the some configuration. It's mainly used for controlling -## which widget to show on different side of the keyboard -choice - prompt "Role of the keyboard" - help - Specify the role of the keyboard + # Role of the keyboard in split setup, default is master (i.e., central) + ## NOTE: this is a dedicated config option for nrfmacro board, so that all shields + ## using this board can share the some configuration. It's mainly used for controlling + ## which widget to show on different side of the keyboard + choice + prompt "Role of the keyboard" + help + Specify the role of the keyboard -config NICE_NANO_SHIELD_MASTER - bool "master side, in charge of communication with host" + config NICE_NANO_SHIELD_MASTER + bool "master side, in charge of communication with host" -config NICE_NANO_SHIELD_SLAVE - bool "slave side, acting as a peripheral to master side" + config NICE_NANO_SHIELD_SLAVE + bool "slave side, acting as a peripheral to master side" -endchoice + endchoice -# add point device -config NICE_NANO_POINTDEVICE - bool "add support for point device" - select SPI + # add point device + config NICE_NANO_POINTDEVICE + bool "add support for point device" + select SPI endif # BOARD_NICE_NANO || BOARD_NICE_NANO_V2 diff --git a/app/boards/shields/tracktyl/Kconfig.defconfig b/app/boards/shields/tracktyl/Kconfig.defconfig index af6defba297..e6e122c54f1 100644 --- a/app/boards/shields/tracktyl/Kconfig.defconfig +++ b/app/boards/shields/tracktyl/Kconfig.defconfig @@ -19,4 +19,7 @@ if SHIELD_TRACKTYL_LEFT || SHIELD_TRACKTYL_RIGHT config ZMK_SPLIT default y + config ZMK_USB + default y + endif diff --git a/app/boards/shields/tracktyl/tracktyl.conf b/app/boards/shields/tracktyl/tracktyl.conf deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/app/boards/shields/tracktyl/tracktyl.dtsi b/app/boards/shields/tracktyl/tracktyl.dtsi index b057a18819d..fba1dc0c365 100644 --- a/app/boards/shields/tracktyl/tracktyl.dtsi +++ b/app/boards/shields/tracktyl/tracktyl.dtsi @@ -1,8 +1,8 @@ #include / { - chosen { - zmk,kscan = &kscan0; + chosen: chosen { + zmk,kscan = &kscan; zmk,matrix_transform = &default_transform; }; @@ -10,10 +10,6 @@ compatible = "zmk,matrix-transform"; columns = <10>; rows = <4>; - // | MX1 | MX2 | MX3 | MX4 | MX5 | | MX6 | MX7 | MX8 | MX9 | MX10 | - // | MX11 | MX12 | MX13 | MX14 | MX15 | | MX16 | MX17 | MX18 | MX19 | MX20 | - // | MX21 | MX22 | MX23 | MX24 | MX25 | | MX26 | MX27 | MX28 | MX29 | MX30 | - // | MX31 | MX32 | MX33 | | MX34 | MX35 | map = < RC(0,0) RC(0,1) RC(0,2) RC(0,3) RC(0,4) RC(0,5) RC(0,6) RC(0,7) RC(0,8) RC(0,9) RC(1,0) RC(1,1) RC(1,2) RC(1,3) RC(1,4) RC(1,5) RC(1,6) RC(1,7) RC(1,8) RC(1,9) @@ -22,20 +18,18 @@ >; }; - kscan0: kscan { - compatible = "zmk,kscan-gpio-matrix"; - label = "KSCAN"; + /* Scan matrix declaration */ + kscan: kscan { + compatible = "zmk,kscan-composite"; + label = "KSCAN"; + rows = <5>; + columns = <10>; - diode-direction = "row2col"; // amoeba royale needs to do row2col - - row-gpios - = <&pro_micro 7 GPIO_ACTIVE_HIGH> - , <&pro_micro 8 GPIO_ACTIVE_HIGH> - , <&pro_micro 9 GPIO_ACTIVE_HIGH> - , <&pro_micro 10 GPIO_ACTIVE_HIGH> - ; - - }; + /* There is a key matrix by default */ + normal_keys { + kscan = <&kscan0>; + }; + }; /* Configure trackballs (either split can have the trackball) */ trackballs { diff --git a/app/boards/shields/tracktyl/tracktyl.keymap b/app/boards/shields/tracktyl/tracktyl.keymap new file mode 100644 index 00000000000..8f423ff071c --- /dev/null +++ b/app/boards/shields/tracktyl/tracktyl.keymap @@ -0,0 +1,127 @@ +#include +#include +#include +#include +#include + +/ { + + behaviors { + u_mt: u_mt { + compatible = "zmk,behavior-hold-tap"; + label = "U_MT"; + bindings = + <&kp>, + <&kp>; + + #binding-cells = <2>; + tapping-term-ms = <200>; + flavor = "tap-preferred"; + }; + u_lt: u_lt { + compatible = "zmk,behavior-hold-tap"; + label = "U_LT"; + bindings = + <&mo>, + <&kp>; + + #binding-cells = <2>; + tapping-term-ms = <200>; + flavor = "tap-preferred"; + }; + u_out_tog: u_out_tog { + compatible = "zmk,behavior-mod-morph"; + label = "U_OUT_TOG"; + bindings = + <&out OUT_TOG>, + <&out OUT_USB>; + + #binding-cells = <0>; + mods = <(MOD_LSFT|MOD_RSFT)>; + }; + u_caps_word: u_caps_word { + compatible = "zmk,behavior-mod-morph"; + label = "U_CAPS_WORD"; + bindings = + <&caps_word>, + <&kp CAPS>; + + #binding-cells = <0>; + mods = <(MOD_LSFT|MOD_RSFT)>; + }; + }; + + keymap { + compatible = "zmk,keymap"; + + default_layer { + bindings = < + &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P +&u_mt LGUI A &u_mt LEFT_ALT S &u_mt LEFT_SHIFT D &u_mt LCTRL F &kp G &kp H &u_mt RCTRL J &u_mt RIGHT_SHIFT K &u_mt RIGHT_ALT L &u_mt RIGHT_GUI SEMI + &u_lt 6 Z &kp X &kp C &kp V &kp B &kp N &kp M &kp COMMA &kp DOT &u_lt 6 SLASH + &u_lt 5 ESC &u_lt 2 TAB &u_lt 4 BACKSPACE &u_lt 3 ENTER &u_lt 1 SPACE + >; + trackball-bindings = <&tmv>; + }; + + layer_num { + bindings = < + &kp NUMBER_1 &kp NUMBER_2 &kp NUMBER_3 &kp NUMBER_4 &kp NUMBER_5 &kp NUMBER_6 &kp NUMBER_7 &kp NUMBER_8 &kp NUMBER_9 &kp NUMBER_0 +&u_mt LGUI NUMBER_1 &u_mt LEFT_ALT NUMBER_2 &u_mt LEFT_SHIFT NUMBER_3 &u_mt LCTRL NUMBER_4 &kp NUMBER_5 &kp EQUAL &u_mt RCTRL NUMBER_4 &u_mt RIGHT_SHIFT NUMBER_5 &u_mt RIGHT_ALT NUMBER_6 &u_mt RIGHT_GUI NUMBER_0 + &kp NUMBER_1 &kp NUMBER_2 &kp NUMBER_3 &kp NUMBER_4 &kp NUMBER_5 &kp MINUS &kp NUMBER_1 &kp NUMBER_2 &kp NUMBER_3 &kp NUMBER_0 + &kp ESC &kp TAB &kp BACKSPACE &none &none + >; + trackball-bindings = <&tsl>; + }; + + layer_nav { + bindings = < +&kp LC(Z) &kp LC(X) &kp LC(C) &kp LC(V) &kp LC(Y) &kp INS &kp HOME &kp UP &kp END &kp PG_UP + &kp LGUI &kp LALT &kp LSHFT &kp LCTRL &none &u_caps_word &kp LEFT &kp DOWN &kp RIGHT &kp PG_DN +&kp LC(Z) &kp LC(X) &kp LC(C) &kp LC(V) &kp LS(LC(Z)) &kp DEL &kp DEL &none &none &none + &none &none &none &kp RET &kp SPACE + >; + trackball-bindings = <&tkp LC(TAB) LC(LS(TAB)) LG(PG_UP) LG(PG_DN)>; + }; + + layer_symb { + bindings = < + &kp LS(EXCL) &kp LS(AT) &kp LEFT_BRACE &kp RIGHT_BRACE &kp LS(TILDE) &kp LS(PIPE) &kp BSLH &kp LS(DQT) &kp LS(UNDER) &kp LS(PLUS) +&kp LS(POUND) &kp LS(DLLR) &kp LBKT &kp RIGHT_BRACKET &kp GRAVE &kp EQUAL &kp RCTRL &kp RSHFT &kp RALT &kp MINUS +&kp LS(PRCNT) &kp LS(CARET) &kp LS(LPAR) &kp LS(RPAR) &kp LS(AMPS) &kp LS(STAR) &kp LS(COLON) &kp LS(LT) &kp LS(GT) &kp LS(QMARK) + &kp LS(LPAR) &kp LS(RPAR) &kp SQT &none &none + >; + trackball-bindings = <&tkp BSPC DELETE LC(MINUS) LC(PLUS)>; + }; + + layer_fn { + bindings = < + &kp F1 &kp F2 &kp F3 &kp F4 &kp F5 &kp F6 &kp F7 &kp F8 &kp F9 &kp F10 +&kp LGUI &kp LALT &kp LSHFT &kp LCTRL &kp K_APP &kp F11 &kp F12 &kp F13 &kp F14 &kp F15 + &kp F16 &kp F17 &kp F18 &kp F19 &kp F20 &kp F21 &kp F22 &kp F23 &kp F24 &kp PAUSE_BREAK + &none &none &none &kp SLCK &kp PAUSE_BREAK + >; + trackball-bindings = <&tkp_fast RIGHT LEFT DOWN UP>; + }; + + layer_pad { + bindings = < +&kp LC(Z) &kp LC(X) &kp LC(C) &kp LC(V) &kp LC(Y) &kp LOCKING_NUM &kp KP_N7 &kp KP_N8 &kp KP_N9 &kp KP_MINUS + &kp LGUI &kp LALT &kp LSHFT &kp LCTRL &kp RET &kp KP_SLASH &kp KP_N4 &kp KP_N5 &kp KP_N6 &kp KP_PLUS +&kp LC(Z) &kp LC(X) &kp LC(C) &kp LC(V) &kp LS(LC(Z)) &kp KP_ASTERISK &kp N1 &kp N2 &kp N3 &kp RET + &none &none &none &kp KP_N0 &kp KP_DOT + >; + trackball-bindings = <&tmv_fine>; + }; + + layer_media { + bindings = < + &u_out_tog &none &kp C_VOL_UP &none &none &bt BT_SEL 0 &bt BT_SEL 1 &bt BT_SEL 2 &bt BT_SEL 3 &bt BT_SEL 4 +&out OUT_BLE &kp C_PREV &kp C_VOL_DN &kp C_NEXT &bootloader &bootloader &bt BT_PRV &bt BT_NXT &bt BT_CLR &none + &none &kp C_PP &kp C_MUTE &kp C_STOP &none &none &none &none &none &none + &none &none &none &none &none + >; + }; + trackball-bindings = <&tmv_x>; + }; +}; \ No newline at end of file diff --git a/app/boards/shields/tracktyl/tracktyl_left.conf b/app/boards/shields/tracktyl/tracktyl_left.conf index 80684aebe84..9ea8adafe68 100644 --- a/app/boards/shields/tracktyl/tracktyl_left.conf +++ b/app/boards/shields/tracktyl/tracktyl_left.conf @@ -1,4 +1,6 @@ #### Enable encoder support #### # CONFIG_EC11=y # CONFIG_EC11_TRIGGER_GLOBAL_THREAD=y -CONFIG_NICE_NANO_SHIELD_SLAVE=y \ No newline at end of file +CONFIG_NICE_NANO_SHIELD_SLAVE=y + +CONFIG_BT_CTLR_TX_PWR_PLUS_8=y \ No newline at end of file diff --git a/app/boards/shields/tracktyl/tracktyl_left.overlay b/app/boards/shields/tracktyl/tracktyl_left.overlay index 24a9c6016c0..aaf374fd39b 100644 --- a/app/boards/shields/tracktyl/tracktyl_left.overlay +++ b/app/boards/shields/tracktyl/tracktyl_left.overlay @@ -4,14 +4,27 @@ col-offset = <0>; }; -&kscan0 { - col-gpios +/ { + kscan0: kscan0 { + compatible = "zmk,kscan-gpio-matrix"; + label = "KSCAN0"; + + diode-direction = "row2col"; // amoeba royale needs to do row2col + + row-gpios + = <&pro_micro 7 GPIO_ACTIVE_HIGH> + , <&pro_micro 8 GPIO_ACTIVE_HIGH> + , <&pro_micro 9 GPIO_ACTIVE_HIGH> + , <&pro_micro 10 GPIO_ACTIVE_HIGH> + ; + col-gpios = <&pro_micro 16 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> , <&pro_micro 14 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> , <&pro_micro 15 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> , <&pro_micro 18 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> , <&pro_micro 19 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> ; + }; }; // Define encoder pins @@ -20,3 +33,7 @@ a-gpios = <&gpio0 31 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; b-gpios = <&gpio0 29 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; }; + +&chosen { + zmk,battery = &vbatt; +}; diff --git a/app/boards/shields/tracktyl/tracktyl_right.conf b/app/boards/shields/tracktyl/tracktyl_right.conf index 990177349db..c1c2fd2cbad 100644 --- a/app/boards/shields/tracktyl/tracktyl_right.conf +++ b/app/boards/shields/tracktyl/tracktyl_right.conf @@ -1,9 +1,14 @@ +##### Point Devices #### +# CONFIG_ZMK_PD_WORK_QUEUE_DEDICATED=n +CONFIG_ZMK_PD_DEDICATED_WORK_QUEUE_PRIORITY=2 +CONFIG_ZMK_TRACKBALL_POLL_DURATION=8 + CONFIG_ZMK_MOUSE=y -CONFIG_SPI=y -CONFIG_NICE_NANO_POINTDEVICE=y -CONFIG_NICE_NANO_SHIELD_MASTER=y +CONFIG_ZMK_MOUSE_TICK_DURATION=10 +CONFIG_ZMK_MOUSE_WORK_QUEUE_DEDICATED=y +CONFIG_ZMK_MOUSE_DEDICATED_THREAD_PRIORITY=1 + CONFIG_PMW3360=y -CONFIG_ZMK_TRACKBALL_POLL_DURATION=8 CONFIG_PMW3360_CPI=2000 CONFIG_PMW3360_CPI_DIVIDOR=1 CONFIG_PMW3360_ORIENTATION_0=y @@ -12,11 +17,21 @@ CONFIG_PMW3360_REST1_DOWNSHIFT_TIME_MS=5000 CONFIG_PMW3360_REST2_DOWNSHIFT_TIME_MS=30000 ### Bluetooth ### -# CONFIG_BT_PERIPHERAL_PREF_MAX_INT=9 -# CONFIG_BT_PERIPHERAL_PREF_LATENCY=16 -# CONFIG_BT_BUF_ACL_TX_COUNT=32 -# CONFIG_BT_L2CAP_TX_BUF_COUNT=32 +CONFIG_BT_PERIPHERAL_PREF_MAX_INT=9 +CONFIG_BT_PERIPHERAL_PREF_LATENCY=16 +CONFIG_BT_BUF_ACL_TX_COUNT=32 +CONFIG_BT_L2CAP_TX_BUF_COUNT=32 #### Enable encoder support #### # CONFIG_EC11=y -# CONFIG_EC11_TRIGGER_GLOBAL_THREAD=y \ No newline at end of file +# CONFIG_EC11_TRIGGER_GLOBAL_THREAD=y + +# set transmission power to max +CONFIG_BT_CTLR_TX_PWR_PLUS_8=y + +CONFIG_NICE_NANO_SHIELD_MASTER=y + +# Turn on logging +CONFIG_ZMK_USB_LOGGING=y +CONFIG_LOG=y +CONFIG_ZMK_LOG_LEVEL_DBG=y \ No newline at end of file diff --git a/app/boards/shields/tracktyl/tracktyl_right.overlay b/app/boards/shields/tracktyl/tracktyl_right.overlay index 7dd4ad6e125..38a428cb6ae 100644 --- a/app/boards/shields/tracktyl/tracktyl_right.overlay +++ b/app/boards/shields/tracktyl/tracktyl_right.overlay @@ -4,14 +4,27 @@ col-offset = <5>; }; -&kscan0 { - col-gpios +/ { + kscan0: kscan0 { + compatible = "zmk,kscan-gpio-matrix"; + label = "KSCAN0"; + + diode-direction = "row2col"; // amoeba royale needs to do row2col + + row-gpios + = <&pro_micro 7 GPIO_ACTIVE_HIGH> + , <&pro_micro 8 GPIO_ACTIVE_HIGH> + , <&pro_micro 9 GPIO_ACTIVE_HIGH> + , <&pro_micro 10 GPIO_ACTIVE_HIGH> + ; + col-gpios = <&pro_micro 16 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> , <&pro_micro 14 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> , <&pro_micro 15 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> , <&pro_micro 18 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> , <&pro_micro 19 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> ; + }; }; /* Enable the right encoder and define its pins */ @@ -27,4 +40,8 @@ &trackball { status = "okay"; +}; + +&chosen { + zmk,battery = &vbatt; }; \ No newline at end of file diff --git a/app/drivers/sensor/pixart/pmw3360/pmw3360.c b/app/drivers/sensor/pixart/pmw3360/pmw3360.c index a742ffa052f..5361d4f354f 100644 --- a/app/drivers/sensor/pixart/pmw3360/pmw3360.c +++ b/app/drivers/sensor/pixart/pmw3360/pmw3360.c @@ -130,7 +130,7 @@ static const int32_t async_init_delay[ASYNC_INIT_STEP_COUNT] = { [ASYNC_INIT_STEP_POWER_UP] = 1, [ASYNC_INIT_STEP_FW_LOAD_START] = 50, // required in spec [ASYNC_INIT_STEP_FW_LOAD_CONTINUE] = 10, // required in spec - [ASYNC_INIT_STEP_FW_LOAD_VERIFY] = 50, + [ASYNC_INIT_STEP_FW_LOAD_VERIFY] = 1, [ASYNC_INIT_STEP_CONFIGURE] = 0, }; From 2c20c52d8a17b097f91c2c719567e1c80fba1d1d Mon Sep 17 00:00:00 2001 From: Angzhi Li Date: Thu, 27 Apr 2023 09:50:28 +0000 Subject: [PATCH 152/157] turning off debug for tracktyl --- app/boards/shields/tracktyl/tracktyl_right.conf | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/boards/shields/tracktyl/tracktyl_right.conf b/app/boards/shields/tracktyl/tracktyl_right.conf index c1c2fd2cbad..386e73a0f36 100644 --- a/app/boards/shields/tracktyl/tracktyl_right.conf +++ b/app/boards/shields/tracktyl/tracktyl_right.conf @@ -31,7 +31,7 @@ CONFIG_BT_CTLR_TX_PWR_PLUS_8=y CONFIG_NICE_NANO_SHIELD_MASTER=y -# Turn on logging -CONFIG_ZMK_USB_LOGGING=y -CONFIG_LOG=y -CONFIG_ZMK_LOG_LEVEL_DBG=y \ No newline at end of file +# # Turn on logging +# CONFIG_ZMK_USB_LOGGING=y +# CONFIG_LOG=y +# CONFIG_ZMK_LOG_LEVEL_DBG=y \ No newline at end of file From 3879c73161c5295a2bfebad396d05abfe6188ac7 Mon Sep 17 00:00:00 2001 From: Angzhi Li Date: Sun, 30 Jul 2023 14:36:49 -0700 Subject: [PATCH 153/157] # disabling auto build until fixing all build errors to prevent excessive emails --- .github/workflows/build.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 927ab409fc9..a467a3ef7c6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,8 +9,9 @@ on: paths: - ".github/workflows/build.yml" - "app/**" - schedule: - - cron: '22 4 * * *' +# disabling auto build until fixing all build errors to prevent excessive emails +# schedule: +# - cron: '22 4 * * *' jobs: build: From 04dea5978957c1dd2d4711a7d6fcaa04671a2283 Mon Sep 17 00:00:00 2001 From: Angzhi Li Date: Thu, 27 Apr 2023 22:25:52 +0000 Subject: [PATCH 154/157] adding mouse keys in combos --- app/boards/shields/tracktyl/tracktyl.keymap | 27 ++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/app/boards/shields/tracktyl/tracktyl.keymap b/app/boards/shields/tracktyl/tracktyl.keymap index 8f423ff071c..d1cc75035b6 100644 --- a/app/boards/shields/tracktyl/tracktyl.keymap +++ b/app/boards/shields/tracktyl/tracktyl.keymap @@ -5,6 +5,27 @@ #include / { + combos { + compatible = "zmk,combos"; + + combo_mb_lclick { + timeout-ms = <50>; + key-positions = <12 13>; + bindings = <&mkp LCLK>; + }; + + combo_mb_rclick { + timeout-ms = <50>; + key-positions = <22 23>; + bindings = <&mkp RCLK>; + }; + + combo_mb_mclick { + timeout-ms = <50>; + key-positions = <2 3>; + bindings = <&mkp MCLK>; + }; + }; behaviors { u_mt: u_mt { @@ -71,7 +92,7 @@ &kp NUMBER_1 &kp NUMBER_2 &kp NUMBER_3 &kp NUMBER_4 &kp NUMBER_5 &kp MINUS &kp NUMBER_1 &kp NUMBER_2 &kp NUMBER_3 &kp NUMBER_0 &kp ESC &kp TAB &kp BACKSPACE &none &none >; - trackball-bindings = <&tsl>; + trackball-bindings = <&tkp LC(TAB) LC(LS(TAB)) LG(PG_UP) LG(PG_DN)>; }; layer_nav { @@ -81,7 +102,7 @@ &kp LC(Z) &kp LC(X) &kp LC(C) &kp LC(V) &kp LS(LC(Z)) &kp DEL &kp DEL &none &none &none &none &none &none &kp RET &kp SPACE >; - trackball-bindings = <&tkp LC(TAB) LC(LS(TAB)) LG(PG_UP) LG(PG_DN)>; + trackball-bindings = <&tsl>; }; layer_symb { @@ -122,6 +143,6 @@ &none &none &none &none &none >; }; - trackball-bindings = <&tmv_x>; + trackball-bindings = <&tkp LC(TAB) LC(LS(TAB)) LG(PG_UP) LG(PG_DN)>; }; }; \ No newline at end of file From 72aeffbf960d70b8921f96ba083af384058a0bef Mon Sep 17 00:00:00 2001 From: Angzhi Li Date: Fri, 28 Apr 2023 00:37:10 +0000 Subject: [PATCH 155/157] make trackball scroll/move slower --- app/boards/shields/tracktyl/tracktyl.dtsi | 2 +- app/boards/shields/tracktyl/tracktyl.keymap | 20 ++++++++++---------- app/dts/behaviors/trackball.dtsi | 19 +++++++++++++++---- app/src/hid.c | 4 ++-- 4 files changed, 28 insertions(+), 17 deletions(-) diff --git a/app/boards/shields/tracktyl/tracktyl.dtsi b/app/boards/shields/tracktyl/tracktyl.dtsi index fba1dc0c365..3a2e7260ee5 100644 --- a/app/boards/shields/tracktyl/tracktyl.dtsi +++ b/app/boards/shields/tracktyl/tracktyl.dtsi @@ -83,6 +83,6 @@ label = "Pixart PMW3360"; irq-gpios = <&gpio1 0 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; spi-max-frequency = <2000000>; - scroll-layer = <5>; + scroll-layer = <1>; }; }; diff --git a/app/boards/shields/tracktyl/tracktyl.keymap b/app/boards/shields/tracktyl/tracktyl.keymap index d1cc75035b6..98c38b56e27 100644 --- a/app/boards/shields/tracktyl/tracktyl.keymap +++ b/app/boards/shields/tracktyl/tracktyl.keymap @@ -77,9 +77,9 @@ default_layer { bindings = < - &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P -&u_mt LGUI A &u_mt LEFT_ALT S &u_mt LEFT_SHIFT D &u_mt LCTRL F &kp G &kp H &u_mt RCTRL J &u_mt RIGHT_SHIFT K &u_mt RIGHT_ALT L &u_mt RIGHT_GUI SEMI - &u_lt 6 Z &kp X &kp C &kp V &kp B &kp N &kp M &kp COMMA &kp DOT &u_lt 6 SLASH + &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P +&u_mt LGUI A &u_mt LEFT_ALT S &u_mt LEFT_SHIFT D &u_mt LCTRL F &kp G &kp H &u_mt RCTRL J &u_mt RIGHT_SHIFT K &u_mt RIGHT_ALT L &u_mt RIGHT_GUI SEMI + &kp Z &kp X &kp C &u_lt 6 V &kp B &u_lt 6 N &kp M &kp COMMA &kp DOT &kp SLASH &u_lt 5 ESC &u_lt 2 TAB &u_lt 4 BACKSPACE &u_lt 3 ENTER &u_lt 1 SPACE >; trackball-bindings = <&tmv>; @@ -97,12 +97,12 @@ layer_nav { bindings = < -&kp LC(Z) &kp LC(X) &kp LC(C) &kp LC(V) &kp LC(Y) &kp INS &kp HOME &kp UP &kp END &kp PG_UP - &kp LGUI &kp LALT &kp LSHFT &kp LCTRL &none &u_caps_word &kp LEFT &kp DOWN &kp RIGHT &kp PG_DN -&kp LC(Z) &kp LC(X) &kp LC(C) &kp LC(V) &kp LS(LC(Z)) &kp DEL &kp DEL &none &none &none - &none &none &none &kp RET &kp SPACE +&kp LC(Z) &kp LC(X) &kp LC(C) &kp LC(V) &kp LC(Y) &kp INS &kp HOME &kp UP &kp END &kp PG_UP +&kp LGUI &kp LALT &kp LSHFT &kp LCTRL &mwh SCROLL_UP &u_caps_word &kp LEFT &kp DOWN &kp RIGHT &kp PG_DN +&kp LC(Z) &mkp RCLK &mkp MCLK &mkp LCLK &mwh SCROLL_DOWN &kp DEL &mkp LCLK &mkp MCLK &mkp RCLK &kp DEL + &none &none &none &kp RET &kp SPACE >; - trackball-bindings = <&tsl>; + trackball-bindings = <&tmv>; }; layer_symb { @@ -139,10 +139,10 @@ bindings = < &u_out_tog &none &kp C_VOL_UP &none &none &bt BT_SEL 0 &bt BT_SEL 1 &bt BT_SEL 2 &bt BT_SEL 3 &bt BT_SEL 4 &out OUT_BLE &kp C_PREV &kp C_VOL_DN &kp C_NEXT &bootloader &bootloader &bt BT_PRV &bt BT_NXT &bt BT_CLR &none - &none &kp C_PP &kp C_MUTE &kp C_STOP &none &none &none &none &none &none + &kp C_PP &kp C_MUTE &none &kp C_STOP &none &none &none &none &none &none &none &none &none &none &none >; + trackball-bindings = <&tsl>; }; - trackball-bindings = <&tkp LC(TAB) LC(LS(TAB)) LG(PG_UP) LG(PG_DN)>; }; }; \ No newline at end of file diff --git a/app/dts/behaviors/trackball.dtsi b/app/dts/behaviors/trackball.dtsi index cce8d498c2e..1e289570c3a 100644 --- a/app/dts/behaviors/trackball.dtsi +++ b/app/dts/behaviors/trackball.dtsi @@ -9,7 +9,7 @@ mode = "move-mode"; flavor = "default"; scale_mode = "dividor"; - scale_factor = <2>; + scale_factor = <3>; }; }; @@ -21,7 +21,7 @@ mode = "move-mode"; flavor = "default"; scale_mode = "dividor"; - scale_factor = <4>; + scale_factor = <5>; }; }; @@ -70,7 +70,7 @@ mode = "scroll-mode"; flavor = "default"; scale_mode = "dividor"; - scale_factor = <10>; + scale_factor = <5>; }; }; @@ -82,10 +82,21 @@ mode = "scroll-mode"; flavor = "default"; scale_mode = "dividor"; - scale_factor = <1>; + scale_factor = <50>; }; }; + behaviors { + /omit-if-no-ref/ tsl_coarse: behavior_trackball_scroll_coarse { + compatible = "zmk,behavior-point-device-incremental"; + label = "TRACKBALL_SCROLL_COARSE"; + #trackball-binding-cells = <0>; + mode = "scroll-mode"; + flavor = "default"; + scale_mode = "dividor"; + scale_factor = <5>; + }; + }; /* trackball key press */ behaviors { /omit-if-no-ref/ tkp: behavior_trackball_key_press { diff --git a/app/src/hid.c b/app/src/hid.c index b287ed721a0..9f2fa6f19d1 100644 --- a/app/src/hid.c +++ b/app/src/hid.c @@ -339,8 +339,8 @@ void zmk_hid_mouse_scroll_set(int8_t x, int8_t y) { } void zmk_hid_mouse_scroll_update(int8_t x, int8_t y) { - mouse_report.body.scroll_x += x; - mouse_report.body.scroll_y += y; + mouse_report.body.scroll_x += x / 8; + mouse_report.body.scroll_y += y / 8 ; LOG_DBG("Mouse scroll updated to 0x%02X 0x%02X ", mouse_report.body.scroll_x, mouse_report.body.scroll_y); } From 70f96082c058cbc6d1922dd082b3c9f5b07c5af4 Mon Sep 17 00:00:00 2001 From: Angzhi Li Date: Tue, 1 Aug 2023 06:26:43 +0000 Subject: [PATCH 156/157] corne_cirque change tryout --- .../shields/corne_cirque/Kconfig.defconfig | 49 ++++++++ .../shields/corne_cirque/Kconfig.shield | 8 ++ .../shields/corne_cirque/corne_cirque.conf | 6 + .../shields/corne_cirque/corne_cirque.dtsi | 76 +++++++++++ .../shields/corne_cirque/corne_cirque.keymap | 119 ++++++++++++++++++ .../shields/corne_cirque/corne_cirque.zmk.yml | 14 +++ .../corne_cirque/corne_cirque_left.conf | 7 ++ .../corne_cirque/corne_cirque_left.overlay | 54 ++++++++ .../corne_cirque/corne_cirque_right.conf | 20 +++ .../corne_cirque/corne_cirque_right.overlay | 45 +++++++ .../sensor/cirque_trackpad/cirque_trackpad.c | 118 ++++++++++------- 11 files changed, 468 insertions(+), 48 deletions(-) create mode 100644 app/boards/shields/corne_cirque/Kconfig.defconfig create mode 100644 app/boards/shields/corne_cirque/Kconfig.shield create mode 100644 app/boards/shields/corne_cirque/corne_cirque.conf create mode 100644 app/boards/shields/corne_cirque/corne_cirque.dtsi create mode 100644 app/boards/shields/corne_cirque/corne_cirque.keymap create mode 100644 app/boards/shields/corne_cirque/corne_cirque.zmk.yml create mode 100644 app/boards/shields/corne_cirque/corne_cirque_left.conf create mode 100644 app/boards/shields/corne_cirque/corne_cirque_left.overlay create mode 100644 app/boards/shields/corne_cirque/corne_cirque_right.conf create mode 100644 app/boards/shields/corne_cirque/corne_cirque_right.overlay diff --git a/app/boards/shields/corne_cirque/Kconfig.defconfig b/app/boards/shields/corne_cirque/Kconfig.defconfig new file mode 100644 index 00000000000..ddfd2f019f5 --- /dev/null +++ b/app/boards/shields/corne_cirque/Kconfig.defconfig @@ -0,0 +1,49 @@ +if SHIELD_CORNE_CIRQUE_RIGHT + +config ZMK_KEYBOARD_NAME + default "Corne_cirque" + +config ZMK_SPLIT_ROLE_CENTRAL + default y + +endif + +if SHIELD_CORNE_CIRQUE_LEFT || SHIELD_CORNE_RIGHT + +config ZMK_SPLIT + default y + +config ZMK_USB + default y + +if ZMK_DISPLAY + +config I2C + default y + +config SSD1306 + default y + +config SSD1306_REVERSE_MODE + default y + +endif # ZMK_DISPLAY + +if LVGL + +config LVGL_VDB_SIZE + default 64 + +config LVGL_DPI + default 148 + +config LVGL_BITS_PER_PIXEL + default 1 + +choice LVGL_COLOR_DEPTH + default LVGL_COLOR_DEPTH_1 +endchoice + +endif # LVGL + +endif diff --git a/app/boards/shields/corne_cirque/Kconfig.shield b/app/boards/shields/corne_cirque/Kconfig.shield new file mode 100644 index 00000000000..e957bb0fc92 --- /dev/null +++ b/app/boards/shields/corne_cirque/Kconfig.shield @@ -0,0 +1,8 @@ +# Copyright (c) 2020 Pete Johanson +# SPDX-License-Identifier: MIT + +config SHIELD_CORNE_CIRQUE_LEFT + def_bool $(shields_list_contains,corne_cirque_left) + +config SHIELD_CORNE_CIRQUE_RIGHT + def_bool $(shields_list_contains,corne_cirque_right) diff --git a/app/boards/shields/corne_cirque/corne_cirque.conf b/app/boards/shields/corne_cirque/corne_cirque.conf new file mode 100644 index 00000000000..1201cb11a85 --- /dev/null +++ b/app/boards/shields/corne_cirque/corne_cirque.conf @@ -0,0 +1,6 @@ +# Uncomment the following lines to enable the corne_cirque RGB Underglow +# CONFIG_ZMK_RGB_UNDERGLOW=y +# CONFIG_WS2812_STRIP=y + +# Uncomment the following line to enable the corne_cirque OLED Display +# CONFIG_ZMK_DISPLAY=y diff --git a/app/boards/shields/corne_cirque/corne_cirque.dtsi b/app/boards/shields/corne_cirque/corne_cirque.dtsi new file mode 100644 index 00000000000..04f9957d37e --- /dev/null +++ b/app/boards/shields/corne_cirque/corne_cirque.dtsi @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2020 Pete Johanson + * + * SPDX-License-Identifier: MIT + */ + +#include + +/ { + chosen: chosen { + zmk,kscan = &kscan; + zmk,matrix_transform = &default_transform; + }; + + default_transform: keymap_transform_0 { + compatible = "zmk,matrix-transform"; + columns = <10>; + rows = <4>; + map = < +RC(0,0) RC(0,1) RC(0,2) RC(0,3) RC(0,4) RC(0,5) RC(0,6) RC(0,7) RC(0,8) RC(0,9) +RC(1,0) RC(1,1) RC(1,2) RC(1,3) RC(1,4) RC(1,5) RC(1,6) RC(1,7) RC(1,8) RC(1,9) +RC(2,0) RC(2,1) RC(2,2) RC(2,3) RC(2,4) RC(2,5) RC(2,6) RC(2,7) RC(2,8) RC(2,9) + RC(3,2) RC(3,3) RC(3,4) RC(3,5) RC(3,6) RC(3,7) + >; + }; + + /* Scan matrix declaration */ + kscan: kscan { + compatible = "zmk,kscan-composite"; + label = "KSCAN"; + rows = <4>; + columns = <10>; + + /* There is a key matrix by default */ + normal_keys { + kscan = <&kscan0>; + }; + }; + + trackballs { + compatible = "zmk,keymap-trackballs"; + trackballs = <&trackpad>; + }; +}; + +&nice_nano_spi { + + /* + 1 -> sck -> P0.06 -> left 1 + 2 -> miso -> P0.17 -> left 5 + 3 -> dr -> P1.04 -> left 11 + 4 -> cs -> P0.08 -> left 2 + 5 -> mosi -> P0.20 -> left 6 + 11 -> gnd -> left 3, left 4, right 2 + 12 -> vdd -> right 4 + */ + compatible = "nordic,nrf-spim"; + status = "disabled"; + miso-pin = <17>; // P0.17 + mosi-pin = <20>; // P0.20 + sck-pin = <6>; // P0.6 + cs-gpios = <&gpio1 4 GPIO_ACTIVE_LOW>; // P1.04 + + trackpad: trackpad@0 { + compatible = "cirque,pinnacle"; + status = "disabled"; + reg = <0>; + label = "Cirque Trackpad"; + spi-max-frequency = <10000000>; + dr-gpios = <&gpio0 8 GPIO_ACTIVE_HIGH>; // P0.8 + invert-y; + sleep; + /* no-taps; */ + }; +}; + \ No newline at end of file diff --git a/app/boards/shields/corne_cirque/corne_cirque.keymap b/app/boards/shields/corne_cirque/corne_cirque.keymap new file mode 100644 index 00000000000..0fb864bf9dd --- /dev/null +++ b/app/boards/shields/corne_cirque/corne_cirque.keymap @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + + #include + #include + #include + #include + #include + + / { + behaviors { + u_mt: u_mt { + compatible = "zmk,behavior-hold-tap"; + label = "U_MT"; + bindings = + <&kp>, + <&kp>; + + #binding-cells = <2>; + tapping-term-ms = <200>; + flavor = "tap-preferred"; + }; + u_lt: u_lt { + compatible = "zmk,behavior-hold-tap"; + label = "U_LT"; + bindings = + <&mo>, + <&kp>; + + #binding-cells = <2>; + tapping-term-ms = <200>; + flavor = "tap-preferred"; + }; + u_out_tog: u_out_tog { + compatible = "zmk,behavior-mod-morph"; + label = "U_OUT_TOG"; + bindings = + <&out OUT_TOG>, + <&out OUT_USB>; + + #binding-cells = <0>; + mods = <(MOD_LSFT|MOD_RSFT)>; + }; + u_caps_word: u_caps_word { + compatible = "zmk,behavior-mod-morph"; + label = "U_CAPS_WORD"; + bindings = + <&caps_word>, + <&kp CAPS>; + + #binding-cells = <0>; + mods = <(MOD_LSFT|MOD_RSFT)>; + }; + }; + + keymap { + compatible = "zmk,keymap"; + + default_layer { + bindings = < + &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P &u_mt LGUI A &u_mt LEFT_ALT S + &u_mt LEFT_SHIFT D &u_mt LCTRL F &kp G &kp H &u_mt RCTRL J &u_mt RIGHT_SHIFT K &u_mt RIGHT_ALT L &u_mt RIGHT_GUI SEMI &u_lt 6 Z &kp X &kp C &kp V + &kp B &kp N &kp M &kp COMMA &kp DOT &u_lt 6 SLASH &u_lt 5 ESC &u_lt 2 TAB &u_lt 4 BACKSPACE &u_lt 3 ENTER &u_lt 1 SPACE + >; + trackball-bindings = <&tmv>; + }; + + layer_1 { + bindings = < + &kp NUMBER_1 &kp NUMBER_2 &kp NUMBER_3 &kp NUMBER_4 &kp NUMBER_5 &kp NUMBER_6 &kp NUMBER_7 &kp NUMBER_8 &kp NUMBER_9 &kp NUMBER_0 &u_mt LGUI NUMBER_1 &u_mt LEFT_ALT NUMBER_2 + &u_mt LEFT_SHIFT NUMBER_3 &u_mt LCTRL NUMBER_4 &kp NUMBER_5 &kp EQUAL &u_mt RCTRL NUMBER_4 &u_mt RIGHT_SHIFT NUMBER_5 &u_mt RIGHT_ALT NUMBER_6 &u_mt RIGHT_GUI NUMBER_0 &kp NUMBER_1 &kp NUMBER_2 &kp NUMBER_3 &kp NUMBER_4 + &kp NUMBER_5 &kp MINUS &kp NUMBER_1 &kp NUMBER_2 &kp NUMBER_3 &kp NUMBER_0 &kp ESC &kp TAB &kp BACKSPACE &none &none + >; + }; + + layer_2 { + bindings = < + &kp LC(Z) &kp LC(X) &kp LC(C) &kp LC(V) &kp LC(Y) &kp INS &kp HOME &kp UP &kp END &kp PG_UP &kp LGUI &kp LALT + &kp LSHFT &kp LCTRL &none &u_caps_word &kp LEFT &kp DOWN &kp RIGHT &kp PG_DN &kp LC(Z) &kp LC(X) &kp LC(C) &kp LC(V) + &kp LS(LC(Z)) &kp DEL &kp DEL &none &none &none &none &none &none &kp RET &kp SPACE + >; + }; + + layer_3 { + bindings = < + &kp LS(EXCL) &kp LS(AT) &kp LEFT_BRACE &kp RIGHT_BRACE &kp LS(TILDE) &kp LS(PIPE) &kp BSLH &kp LS(DQT) &kp LS(UNDER) &kp LS(PLUS) &kp LS(POUND) &kp LS(DLLR) + &kp LBKT &kp RIGHT_BRACKET &kp GRAVE &kp EQUAL &kp RCTRL &kp RSHFT &kp RALT &kp MINUS &kp LS(PRCNT) &kp LS(CARET) &kp LS(LPAR) &kp LS(RPAR) + &kp LS(AMPS) &kp LS(STAR) &kp LS(COLON) &kp LS(LT) &kp LS(GT) &kp LS(QMARK) &kp LS(LPAR) &kp LS(RPAR) &kp SQT &none &none + >; + }; + + layer_4 { + bindings = < + &kp F1 &kp F2 &kp F3 &kp F4 &kp F5 &kp F6 &kp F7 &kp F8 &kp F9 &kp F10 &kp LGUI &kp LALT + &kp LSHFT &kp LCTRL &kp K_APP &kp F11 &kp F12 &kp F13 &kp F14 &kp F15 &kp F16 &kp F17 &kp F18 &kp F19 + &kp F20 &kp F21 &kp F22 &kp F23 &kp F24 &kp PAUSE_BREAK &none &none &none &kp SLCK &kp PAUSE_BREAK + >; + }; + + layer_5 { + bindings = < + &kp LC(Z) &kp LC(X) &kp LC(C) &kp LC(V) &kp LC(Y) &kp LOCKING_NUM &kp KP_N7 &kp KP_N8 &kp KP_N9 &kp KP_MINUS &kp LGUI &kp LALT + &kp LSHFT &kp LCTRL &kp RET &kp KP_SLASH &kp KP_N4 &kp KP_N5 &kp KP_N6 &kp KP_PLUS &kp LC(Z) &kp LC(X) &kp LC(C) &kp LC(V) + &kp LS(LC(Z)) &kp KP_ASTERISK &kp N1 &kp N2 &kp N3 &kp RET &none &none &none &kp KP_N0 &kp KP_DOT + >; + }; + + layer_6 { + bindings = < + &u_out_tog &none &kp C_VOL_UP &none &none &bt BT_SEL 0 &bt BT_SEL 1 &bt BT_SEL 2 &bt BT_SEL 3 &bt BT_SEL 4 &out OUT_BLE &kp C_PREV + &kp C_VOL_DN &kp C_NEXT &bootloader &bootloader &bt BT_PRV &bt BT_NXT &bt BT_CLR &none &none &kp C_PP &kp C_MUTE &kp C_STOP + &none &none &none &none &none &none &none &none &none &none &none + >; + }; + }; + }; diff --git a/app/boards/shields/corne_cirque/corne_cirque.zmk.yml b/app/boards/shields/corne_cirque/corne_cirque.zmk.yml new file mode 100644 index 00000000000..136ed3e46b8 --- /dev/null +++ b/app/boards/shields/corne_cirque/corne_cirque.zmk.yml @@ -0,0 +1,14 @@ +file_format: "1" +id: corne_cirque +name: corne_cirque +type: shield +url: https://github.com/foostan/crkbd/ +requires: [pro_micro] +exposes: [i2c_oled] +features: + - keys + - display + - underglow +siblings: + - corne_cirque_left + - corne_cirque_right diff --git a/app/boards/shields/corne_cirque/corne_cirque_left.conf b/app/boards/shields/corne_cirque/corne_cirque_left.conf new file mode 100644 index 00000000000..f5516fb953b --- /dev/null +++ b/app/boards/shields/corne_cirque/corne_cirque_left.conf @@ -0,0 +1,7 @@ +CONFIG_NICE_NANO_SHIELD_SLAVE=y + +# # set transmission power to max +CONFIG_BT_CTLR_TX_PWR_PLUS_8=y + +# Uncomment the following line to enable the corne_cirque OLED Display +# CONFIG_ZMK_DISPLAY=y \ No newline at end of file diff --git a/app/boards/shields/corne_cirque/corne_cirque_left.overlay b/app/boards/shields/corne_cirque/corne_cirque_left.overlay new file mode 100644 index 00000000000..072a8a7643f --- /dev/null +++ b/app/boards/shields/corne_cirque/corne_cirque_left.overlay @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2020 Pete Johanson + * + * SPDX-License-Identifier: MIT + */ + +#include "corne_cirque.dtsi" + +/ { + kscan0: kscan { + compatible = "zmk,kscan-gpio-matrix"; + label = "KSCAN0"; + + diode-direction = "col2row"; + row-gpios + = <&pro_micro 4 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 5 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 6 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 7 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + ; + col-gpios + = <&pro_micro 20 GPIO_ACTIVE_HIGH> + , <&pro_micro 19 GPIO_ACTIVE_HIGH> + , <&pro_micro 18 GPIO_ACTIVE_HIGH> + , <&pro_micro 15 GPIO_ACTIVE_HIGH> + , <&pro_micro 14 GPIO_ACTIVE_HIGH> + ; + }; +}; + +&chosen { + zmk,battery = &vbatt; + zephyr,display = &oled; +}; + +&pro_micro_i2c { + status = "disabled"; + + oled: ssd1306@3c { + compatible = "solomon,ssd1306fb"; + reg = <0x3c>; + label = "DISPLAY"; + width = <128>; + height = <32>; + segment-offset = <0>; + page-offset = <0>; + display-offset = <0>; + multiplex-ratio = <31>; + segment-remap; + com-invdir; + com-sequential; + prechargep = <0x22>; + }; +}; diff --git a/app/boards/shields/corne_cirque/corne_cirque_right.conf b/app/boards/shields/corne_cirque/corne_cirque_right.conf new file mode 100644 index 00000000000..33c643f998b --- /dev/null +++ b/app/boards/shields/corne_cirque/corne_cirque_right.conf @@ -0,0 +1,20 @@ +CONFIG_ZMK_MOUSE=y +CONFIG_SPI=y +CONFIG_PINNACLE=y +CONFIG_PINNACLE_TRIGGER_OWN_THREAD=y + +### Bluetooth ### +CONFIG_BT_PERIPHERAL_PREF_MAX_INT=9 +CONFIG_BT_PERIPHERAL_PREF_LATENCY=16 +CONFIG_BT_BUF_ACL_TX_COUNT=32 +CONFIG_BT_L2CAP_TX_BUF_COUNT=32 + +# set transmission power to max +CONFIG_BT_CTLR_TX_PWR_PLUS_8=y + +CONFIG_NICE_NANO_SHIELD_MASTER=y + +# Turn on logging +# CONFIG_ZMK_USB_LOGGING=y +# CONFIG_LOG=y +# CONFIG_ZMK_LOG_LEVEL_DBG=y \ No newline at end of file diff --git a/app/boards/shields/corne_cirque/corne_cirque_right.overlay b/app/boards/shields/corne_cirque/corne_cirque_right.overlay new file mode 100644 index 00000000000..9d640d4f84a --- /dev/null +++ b/app/boards/shields/corne_cirque/corne_cirque_right.overlay @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2020 Pete Johanson + * + * SPDX-License-Identifier: MIT + */ + +#include "corne_cirque.dtsi" + +&default_transform { + col-offset = <5>; +}; + +/ { + kscan0: kscan0 { + compatible = "zmk,kscan-gpio-matrix"; + label = "KSCAN0"; + + diode-direction = "col2row"; + row-gpios + = <&pro_micro 4 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 5 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 6 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 7 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + ; + col-gpios + = <&pro_micro 14 GPIO_ACTIVE_HIGH> + , <&pro_micro 15 GPIO_ACTIVE_HIGH> + , <&pro_micro 18 GPIO_ACTIVE_HIGH> + , <&pro_micro 19 GPIO_ACTIVE_HIGH> + , <&pro_micro 20 GPIO_ACTIVE_HIGH> + ; + }; +}; + +&nice_nano_spi { + status = "okay"; +}; + +&trackpad { + status = "okay"; +}; + +&chosen { + zmk,battery = &vbatt; +}; diff --git a/app/drivers/sensor/cirque_trackpad/cirque_trackpad.c b/app/drivers/sensor/cirque_trackpad/cirque_trackpad.c index 8b362f9f79e..c6c94dbeec7 100644 --- a/app/drivers/sensor/cirque_trackpad/cirque_trackpad.c +++ b/app/drivers/sensor/cirque_trackpad/cirque_trackpad.c @@ -9,7 +9,8 @@ LOG_MODULE_REGISTER(pinnacle, CONFIG_SENSOR_LOG_LEVEL); -static int pinnacle_seq_read(const struct device *dev, const uint8_t start, uint8_t *buf, const uint8_t len) { +static int pinnacle_seq_read(const struct device *dev, const uint8_t start, uint8_t *buf, + const uint8_t len) { uint8_t tx_buffer[len + 3], rx_dummy[3]; tx_buffer[0] = PINNACLE_READ | start; memset(&tx_buffer[1], PINNACLE_AUTOINC, len + 1); @@ -23,27 +24,27 @@ static int pinnacle_seq_read(const struct device *dev, const uint8_t start, uint .buffers = &tx_buf, .count = 1, }; - struct spi_buf rx_buf[2] = { - { - .buf = rx_dummy, - .len = 3, - }, - { - .buf = buf, - .len = len, - }, - }; - const struct spi_buf_set rx = { - .buffers = rx_buf, - .count = 2, - }; + struct spi_buf rx_buf[2] = { + { + .buf = rx_dummy, + .len = 3, + }, + { + .buf = buf, + .len = len, + }, + }; + const struct spi_buf_set rx = { + .buffers = rx_buf, + .count = 2, + }; const struct pinnacle_data *data = dev->data; const struct pinnacle_config *config = dev->config; return spi_transceive(data->spi, &config->spi_config, &tx, &rx); } static int pinnacle_write(const struct device *dev, const uint8_t addr, const uint8_t val) { - uint8_t tx_buffer[2] = { PINNACLE_WRITE | addr, val }; + uint8_t tx_buffer[2] = {PINNACLE_WRITE | addr, val}; const struct spi_buf tx_buf = { .buf = tx_buffer, @@ -58,13 +59,24 @@ static int pinnacle_write(const struct device *dev, const uint8_t addr, const ui return spi_write(data->spi, &config->spi_config, &tx); } -static int pinnacle_channel_get(const struct device *dev, enum sensor_channel chan, struct sensor_value *val) { +static int pinnacle_channel_get(const struct device *dev, enum sensor_channel chan, + struct sensor_value *val) { const struct pinnacle_data *data = dev->data; switch (chan) { - case SENSOR_CHAN_POS_DX: val->val1 = data->dx; break; - case SENSOR_CHAN_POS_DY: val->val1 = data->dy; break; - case SENSOR_CHAN_PRESS: val->val1 = data->btn; break; - default: return -ENOTSUP; + case SENSOR_CHAN_POS_DX: + val->val1 = data->dx; + val->val2 = 0; + break; + case SENSOR_CHAN_POS_DY: + val->val1 = data->dy; + val->val2 = 0; + break; + case SENSOR_CHAN_PRESS: + val->val1 = data->btn; + val->val2 = 0; + break; + default: + return -ENOTSUP; } return 0; } @@ -86,23 +98,30 @@ static int pinnacle_sample_fetch(const struct device *dev, enum sensor_channel c #ifdef CONFIG_PINNACLE_TRIGGER static void set_int(const struct device *dev, const bool en) { const struct pinnacle_config *config = dev->config; - int ret = gpio_pin_interrupt_configure(config->dr_port, config->dr_pin, en ? GPIO_INT_LEVEL_ACTIVE : GPIO_INT_DISABLE); - /* int ret = gpio_pin_interrupt_configure(config->dr_port, config->dr_pin, en ? GPIO_INT_EDGE_TO_ACTIVE : GPIO_INT_DISABLE); */ + int ret = gpio_pin_interrupt_configure(config->dr_port, config->dr_pin, + en ? GPIO_INT_LEVEL_ACTIVE : GPIO_INT_DISABLE); + /* int ret = gpio_pin_interrupt_configure(config->dr_port, config->dr_pin, en ? + * GPIO_INT_EDGE_TO_ACTIVE : GPIO_INT_DISABLE); */ if (ret < 0) { LOG_ERR("can't set interrupt"); } } -static int pinnacle_trigger_set(const struct device *dev, const struct sensor_trigger *trig, sensor_trigger_handler_t handler) { +static int pinnacle_trigger_set(const struct device *dev, const struct sensor_trigger *trig, + sensor_trigger_handler_t handler) { struct pinnacle_data *data = dev->data; - set_int(dev, false); if (trig->type != SENSOR_TRIG_DATA_READY) { return -ENOTSUP; } - data->data_ready_trigger = trig; - data->data_ready_handler = handler; - set_int(dev, true); + + set_int(dev, false); + + if (handler) { + data->data_ready_trigger = trig; + data->data_ready_handler = handler; + set_int(dev, true); + } return 0; } @@ -159,10 +178,10 @@ static int pinnacle_init(const struct device *dev) { data->spi = DEVICE_DT_GET(SPI_BUS); // todo: wait for power-on or not - pinnacle_write(dev, PINNACLE_STATUS1, 0); // Clear CC after power-on + pinnacle_write(dev, PINNACLE_STATUS1, 0); // Clear CC after power-on // disable z-idle packets - pinnacle_write(dev, PINNACLE_Z_IDLE, 0); // No Z-Idle packets + pinnacle_write(dev, PINNACLE_Z_IDLE, 0); // No Z-Idle packets // set sleep-mode if (config->sleep_en) { @@ -200,7 +219,7 @@ static int pinnacle_init(const struct device *dev) { k_sem_init(&data->gpio_sem, 0, UINT_MAX); k_thread_create(&data->thread, data->thread_stack, CONFIG_PINNACLE_THREAD_STACK_SIZE, - (k_thread_entry_t) pinnacle_thread, (void *) dev, 0, NULL, + (k_thread_entry_t)pinnacle_thread, (void *)dev, 0, NULL, K_PRIO_COOP(CONFIG_PINNACLE_THREAD_PRIORITY), 0, K_NO_WAIT); #elif defined(CONFIG_PINNACLE_TRIGGER_GLOBAL_THREAD) k_work_init(&data->work, pinnacle_work_cb); @@ -212,27 +231,29 @@ static int pinnacle_init(const struct device *dev) { static const struct sensor_driver_api pinnacle_driver_api = { #if CONFIG_PINNACLE_TRIGGER - .trigger_set = pinnacle_trigger_set, + .trigger_set = pinnacle_trigger_set, #endif - .sample_fetch = pinnacle_sample_fetch, - .channel_get = pinnacle_channel_get, + .sample_fetch = pinnacle_sample_fetch, + .channel_get = pinnacle_channel_get, }; static struct pinnacle_data pinnacle_data; static const struct pinnacle_config pinnacle_config = { - .spi_cs = { - .gpio_dev = DEVICE_DT_GET(DT_GPIO_CTLR_BY_IDX(SPI_BUS, cs_gpios, SPI_REG)), - .gpio_pin = DT_GPIO_PIN_BY_IDX(SPI_BUS, cs_gpios, SPI_REG), - .delay = 0, - .gpio_dt_flags = DT_GPIO_FLAGS_BY_IDX(SPI_BUS, cs_gpios, SPI_REG), - }, - .spi_config = { - .cs = &pinnacle_config.spi_cs, - .frequency = DT_INST_PROP(0, spi_max_frequency), - .slave = DT_INST_REG_ADDR(0), - /* .operation = (SPI_OP_MODE_MASTER | SPI_WORD_SET(8) | SPI_TRANSFER_MSB), */ - .operation = (SPI_OP_MODE_MASTER | SPI_WORD_SET(8) | SPI_TRANSFER_MSB | SPI_MODE_CPHA), - }, + .spi_cs = + { + .gpio_dev = DEVICE_DT_GET(DT_GPIO_CTLR_BY_IDX(SPI_BUS, cs_gpios, SPI_REG)), + .gpio_pin = DT_GPIO_PIN_BY_IDX(SPI_BUS, cs_gpios, SPI_REG), + .delay = 0, + .gpio_dt_flags = DT_GPIO_FLAGS_BY_IDX(SPI_BUS, cs_gpios, SPI_REG), + }, + .spi_config = + { + .cs = &pinnacle_config.spi_cs, + .frequency = DT_INST_PROP(0, spi_max_frequency), + .slave = DT_INST_REG_ADDR(0), + /* .operation = (SPI_OP_MODE_MASTER | SPI_WORD_SET(8) | SPI_TRANSFER_MSB), */ + .operation = (SPI_OP_MODE_MASTER | SPI_WORD_SET(8) | SPI_TRANSFER_MSB | SPI_MODE_CPHA), + }, .invert_x = DT_INST_PROP(0, invert_x), .invert_y = DT_INST_PROP(0, invert_y), .sleep_en = DT_INST_PROP(0, sleep), @@ -244,4 +265,5 @@ static const struct pinnacle_config pinnacle_config = { #endif }; -DEVICE_DT_INST_DEFINE(0, pinnacle_init, NULL, &pinnacle_data, &pinnacle_config, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, &pinnacle_driver_api); +DEVICE_DT_INST_DEFINE(0, pinnacle_init, NULL, &pinnacle_data, &pinnacle_config, POST_KERNEL, + CONFIG_SENSOR_INIT_PRIORITY, &pinnacle_driver_api); \ No newline at end of file From 5591ade36fef72969c7328b61dd0da901d713048 Mon Sep 17 00:00:00 2001 From: Angzhi Li Date: Wed, 9 Aug 2023 02:51:46 +0000 Subject: [PATCH 157/157] update tracktyl.keymap to include a scroll layer --- app/boards/shields/tracktyl/tracktyl.keymap | 22 +++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/app/boards/shields/tracktyl/tracktyl.keymap b/app/boards/shields/tracktyl/tracktyl.keymap index 98c38b56e27..0e52e83971f 100644 --- a/app/boards/shields/tracktyl/tracktyl.keymap +++ b/app/boards/shields/tracktyl/tracktyl.keymap @@ -79,7 +79,7 @@ bindings = < &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P &u_mt LGUI A &u_mt LEFT_ALT S &u_mt LEFT_SHIFT D &u_mt LCTRL F &kp G &kp H &u_mt RCTRL J &u_mt RIGHT_SHIFT K &u_mt RIGHT_ALT L &u_mt RIGHT_GUI SEMI - &kp Z &kp X &kp C &u_lt 6 V &kp B &u_lt 6 N &kp M &kp COMMA &kp DOT &kp SLASH +&u_lt 7 Z &kp X &kp C &u_lt 6 V &kp B &u_lt 6 N &kp M &kp COMMA &kp DOT &u_lt 7 SLASH &u_lt 5 ESC &u_lt 2 TAB &u_lt 4 BACKSPACE &u_lt 3 ENTER &u_lt 1 SPACE >; trackball-bindings = <&tmv>; @@ -135,14 +135,24 @@ trackball-bindings = <&tmv_fine>; }; - layer_media { + layer_scroll { bindings = < - &u_out_tog &none &kp C_VOL_UP &none &none &bt BT_SEL 0 &bt BT_SEL 1 &bt BT_SEL 2 &bt BT_SEL 3 &bt BT_SEL 4 -&out OUT_BLE &kp C_PREV &kp C_VOL_DN &kp C_NEXT &bootloader &bootloader &bt BT_PRV &bt BT_NXT &bt BT_CLR &none - &kp C_PP &kp C_MUTE &none &kp C_STOP &none &none &none &none &none &none - &none &none &none &none &none +&kp LC(Z) &kp LC(X) &kp LC(C) &kp LC(V) &kp LC(Y) &kp INS &kp HOME &kp UP &kp END &kp PG_UP +&kp LGUI &kp LALT &kp LSHFT &kp LCTRL &mwh SCROLL_UP &u_caps_word &kp LEFT &kp DOWN &kp RIGHT &kp PG_DN +&none &none &none &none &mwh SCROLL_DOWN &none &mkp LCLK &mkp MCLK &mkp RCLK &kp DEL + &none &none &none &kp RET &kp SPACE >; trackball-bindings = <&tsl>; }; + + layer_media { + bindings = < +&u_out_tog &none &kp C_VOL_UP &none &none &bt BT_SEL 0 &bt BT_SEL 1 &bt BT_SEL 2 &bt BT_SEL 3 &bt BT_SEL 4 +&out OUT_BLE &kp C_PREV &kp C_VOL_DN &kp C_NEXT &bootloader &bootloader &bt BT_PRV &bt BT_NXT &bt BT_CLR &none +&none &kp C_PP &kp C_MUTE &kp C_STOP &none &none &none &none &none &none + &none &none &none &none &none + >; + trackball-bindings = <&tmv>; + }; }; }; \ No newline at end of file