/* * Copyright (c) 2020 The ZMK Contributors * * SPDX-License-Identifier: MIT */ #define DT_DRV_COMPAT joystick #include #include #include #include #include #include #include #include #include #include #include "../../../include/drivers/ext_power.h" #include "joystick.h" static void zmk_joy_work(struct k_work *work); LOG_MODULE_REGISTER(JOYSTICK, CONFIG_ZMK_LOG_LEVEL); static const struct device *ext_power; static int joy_get_state(const struct device *dev) { struct joy_data *drv_data = dev->data; const struct joy_config *drv_cfg = dev->config; struct adc_sequence *as = &drv_data->as; int disable_power = 0; if (drv_data->adc==NULL) return 0; if (ext_power != NULL) { int power = ext_power_get(ext_power); if (!power) { // power is off but must be turned on for ADC int rc = ext_power_enable(ext_power); if (rc != 0) { LOG_ERR("Unable to enable EXT_POWER: %d", rc); } disable_power = 1; } } int rc = adc_read(drv_data->adc, as); as->calibrate = false; if (disable_power) { int rc = ext_power_disable(ext_power); if (rc != 0) { LOG_ERR("Unable to disable EXT_POWER: %d", rc); } } if (rc == 0) { int32_t val = drv_data->adc_raw; if (val > 4096) val = 4096; return val; } else { LOG_DBG("Joy failed to read ADC: %d", rc); return 0; } } static int joy_sample_fetch(const struct device *dev, enum sensor_channel chan) { struct joy_data *drv_data = dev->data; const struct joy_config *drv_cfg = dev->config; int val; val = joy_get_state(dev); val -= drv_data->zero_value; if (drv_cfg->reverse) { val = -val; } drv_data->delta = val - drv_data->value; drv_data->value = val; if (abs (val) >= drv_cfg->min_on) { LOG_DBG ("Joystick chan: %d = %d", drv_cfg->io_channel, val); } return 0; } static int joy_channel_get(const struct device *dev, enum sensor_channel chan, struct sensor_value *val) { struct joy_data *drv_data = dev->data; const struct joy_config *drv_cfg = dev->config; int value = drv_data->value; if (chan == SENSOR_CHAN_ROTATION) { val->val1 = 0; val->val2 = 0; if (value >= drv_data->last_rotate + drv_cfg->resolution) { drv_data->last_rotate += drv_cfg->resolution; val->val1 = 1; } else if (value <= drv_data->last_rotate - drv_cfg->resolution) { drv_data->last_rotate -= drv_cfg->resolution; val->val1 = -1; } return 0; } else if (chan == SENSOR_CHAN_PRESS) { val->val1 = 0; // calibration adjusted val->val2 = value; // raw value if (value >= drv_cfg->min_on) { val->val1 = 1 + value - drv_cfg->min_on; } else if (value <= -drv_cfg->min_on) { val->val1 = -1 + value + drv_cfg->min_on; } return 0; } return -ENOTSUP; } static void zmk_joy_work(struct k_work *work) { struct joy_data *drv_data = CONTAINER_OF(work, struct joy_data, work); if (drv_data->setup) { int rc = joy_sample_fetch (drv_data->dev, 0); // I think this might be unnecessary if (rc != 0) { LOG_DBG("Failed to update joystick value: %d.", rc); } drv_data->handler (drv_data->dev, drv_data->trigger); } } static void zmk_joy_timer(struct k_timer *timer) { const struct device *dev = k_timer_user_data_get(timer); struct joy_data *drv_data = CONTAINER_OF(timer, struct joy_data, timer); k_work_submit(&drv_data->work); } int joy_trigger_set(const struct device *dev, const struct sensor_trigger *trig, sensor_trigger_handler_t handler) { struct joy_data *drv_data = dev->data; const struct joy_config *drv_cfg = dev->config; k_timer_stop (&drv_data->timer); drv_data->trigger = trig; drv_data->handler = handler; k_work_init (&drv_data->work, zmk_joy_work); k_timer_init (&drv_data->timer, zmk_joy_timer, NULL); k_timer_user_data_set (&drv_data->timer, dev); k_timer_start(&drv_data->timer, K_MSEC(1000/drv_cfg->frequency), K_MSEC(1000/drv_cfg->frequency)); return 0; } static const struct sensor_driver_api joy_driver_api = { .trigger_set = joy_trigger_set, .sample_fetch = joy_sample_fetch, .channel_get = joy_channel_get, }; int joy_init(const struct device *dev) { struct joy_data *drv_data = dev->data; const struct joy_config *drv_cfg = dev->config; drv_data->dev = dev; drv_data->setup = false; drv_data->adc = drv_cfg->adc; if (drv_data->adc == NULL) { LOG_ERR("Joy: Failed to get pointer to ADC device"); return -EINVAL; } drv_data->as = (struct adc_sequence){ .channels = BIT(drv_cfg->io_channel+1), /* Has to be channel +1 because channel 0 is used for the battery */ .buffer = &drv_data->adc_raw, .buffer_size = sizeof(drv_data->adc_raw), .oversampling = 4, .calibrate = true, }; #ifdef CONFIG_ADC_NRFX_SAADC drv_data->acc = (struct adc_channel_cfg){ .gain = ADC_GAIN_1_4, .reference = ADC_REF_VDD_1_4, .acquisition_time = ADC_ACQ_TIME_DEFAULT, .input_positive = SAADC_CH_PSELP_PSELP_AnalogInput0 + drv_cfg->io_channel, .channel_id = drv_cfg->io_channel+1 }; drv_data->as.resolution = 12; #else #error Unsupported ADC #endif int rc = adc_channel_setup(drv_data->adc, &drv_data->acc); LOG_DBG("Joy AIN%u setup returned %d", drv_cfg->io_channel, rc); ext_power = device_get_binding("EXT_POWER"); if (ext_power == NULL) { LOG_ERR("Unable to retrieve ext_power device: EXT_POWER"); } drv_data->setup = true; drv_data->zero_value = drv_data->value = joy_get_state(dev); drv_data->delta = 0; drv_data->last_rotate = 0; return 0; } #define JOY_INST(n) \ struct joy_data joy_data_##n; \ const struct joy_config joy_cfg_##n = { \ .io_channel = DT_INST_IO_CHANNELS_INPUT(n), \ .adc = DEVICE_DT_GET(DT_IO_CHANNELS_CTLR(DT_DRV_INST(n))), \ COND_CODE_0(DT_INST_NODE_HAS_PROP(n, resolution), (1), (DT_INST_PROP(n, resolution))), \ COND_CODE_0(DT_INST_NODE_HAS_PROP(n, min_on), (1), (DT_INST_PROP(n, min_on))), \ COND_CODE_0(DT_INST_NODE_HAS_PROP(n, frequency), (1), (DT_INST_PROP(n, frequency))), \ COND_CODE_0(DT_INST_NODE_HAS_PROP(n, reverse), (1), (DT_INST_PROP(n, reverse))), \ }; \ DEVICE_DT_INST_DEFINE(n, joy_init, device_pm_control_nop, &joy_data_##n, &joy_cfg_##n, \ POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, &joy_driver_api); DT_INST_FOREACH_STATUS_OKAY(JOY_INST)