Device migrations, drivers and fixes (#571)
Device migrations: - cyd-4848S040c - guition-jc1060p470ciwy - guition-jc2432w328c - guition-jc3248w535c (known issue with display and touch, Shadowtrance will look into it) - guition-jc8048w550c - heltec-wifi-lora-32-v3 - lilygo-tdeck-max (excluding graphics) - lilygo-tdisplay - lilygo-tdisplay-s3 - lilygo-tdongle-s3 - lilygo-tlora-pager Driver migrations: - Implemented haptic driver interface in kernel - AXS15231b - BQ25896 - BQ27220 - CST328 - CST6xx - DRV2605 - JD9165 - Custom LilyGO driver for T-Lora Pager - SSD1306 - ST7701 - ST7735 - SY6970 - TCA8418 Fixes/improvements: - Boot app: support for multiple power devices, improved UI - lvgl_devices and related code: fixes for mapping - Support for arrays in dts parser
This commit is contained in:
committed by
GitHub
parent
3b5a401594
commit
d896657bf9
@@ -0,0 +1,213 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#include <lilygo/drivers/tpager_encoder.h>
|
||||
|
||||
#include <tactility/device.h>
|
||||
#include <tactility/driver.h>
|
||||
#include <tactility/drivers/gpio_controller.h>
|
||||
#include <tactility/drivers/gpio_descriptor.h>
|
||||
#include <tactility/log.h>
|
||||
|
||||
#include <driver/pulse_cnt.h>
|
||||
|
||||
#include <new>
|
||||
|
||||
#define TAG "tpager_encoder"
|
||||
#define GET_CONFIG(device) (static_cast<const TpagerEncoderConfig*>((device)->config))
|
||||
#define GET_INTERNAL(device) (static_cast<TpagerEncoderInternal*>(device_get_driver_data(device)))
|
||||
|
||||
struct TpagerEncoderInternal {
|
||||
pcnt_unit_handle_t pcnt_unit = nullptr;
|
||||
GpioDescriptor* pin_enter = nullptr;
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
|
||||
static error_t read_delta(Device* device, int32_t* out_pulses) {
|
||||
auto* internal = GET_INTERNAL(device);
|
||||
int pulses = 0;
|
||||
pcnt_unit_get_count(internal->pcnt_unit, &pulses);
|
||||
pcnt_unit_clear_count(internal->pcnt_unit);
|
||||
*out_pulses = pulses;
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
static error_t get_button_pressed(Device* device, bool* out_pressed) {
|
||||
auto* internal = GET_INTERNAL(device);
|
||||
bool high = true;
|
||||
error_t error = gpio_descriptor_get_level(internal->pin_enter, &high);
|
||||
if (error != ERROR_NONE) {
|
||||
return error;
|
||||
}
|
||||
// Active low: pressed when level is low.
|
||||
*out_pressed = !high;
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
error_t tpager_encoder_read_delta(Device* device, int32_t* out_pulses) {
|
||||
return read_delta(device, out_pulses);
|
||||
}
|
||||
|
||||
error_t tpager_encoder_get_button_pressed(Device* device, bool* out_pressed) {
|
||||
return get_button_pressed(device, out_pressed);
|
||||
}
|
||||
|
||||
// region Driver lifecycle
|
||||
|
||||
// Accumulating count makes over-/underflow automatically compensated; requires watch points at
|
||||
// the low and high limits (see pcnt_unit_add_watch_point() below). Ported from the deprecated
|
||||
// HAL's TpagerEncoder::initEncoder().
|
||||
static constexpr int PCNT_LOW_LIMIT = -127;
|
||||
static constexpr int PCNT_HIGH_LIMIT = 126;
|
||||
|
||||
static error_t init_pcnt_unit(const TpagerEncoderConfig* config, pcnt_unit_handle_t* out_unit) {
|
||||
pcnt_unit_config_t unit_config = {
|
||||
.low_limit = PCNT_LOW_LIMIT,
|
||||
.high_limit = PCNT_HIGH_LIMIT,
|
||||
.intr_priority = 0,
|
||||
.flags = { .accum_count = 1 },
|
||||
};
|
||||
|
||||
pcnt_unit_handle_t unit = nullptr;
|
||||
if (pcnt_new_unit(&unit_config, &unit) != ESP_OK) {
|
||||
LOG_E(TAG, "Pulse counter initialization failed");
|
||||
return ERROR_RESOURCE;
|
||||
}
|
||||
|
||||
pcnt_glitch_filter_config_t filter_config = { .max_glitch_ns = 1000 };
|
||||
if (pcnt_unit_set_glitch_filter(unit, &filter_config) != ESP_OK) {
|
||||
LOG_E(TAG, "Pulse counter glitch filter config failed");
|
||||
pcnt_del_unit(unit);
|
||||
return ERROR_RESOURCE;
|
||||
}
|
||||
|
||||
pcnt_chan_config_t chan_a_config = {
|
||||
.edge_gpio_num = static_cast<int>(config->pin_b.pin),
|
||||
.level_gpio_num = static_cast<int>(config->pin_a.pin),
|
||||
.flags = {},
|
||||
};
|
||||
pcnt_chan_config_t chan_b_config = {
|
||||
.edge_gpio_num = static_cast<int>(config->pin_a.pin),
|
||||
.level_gpio_num = static_cast<int>(config->pin_b.pin),
|
||||
.flags = {},
|
||||
};
|
||||
|
||||
pcnt_channel_handle_t chan_a = nullptr;
|
||||
pcnt_channel_handle_t chan_b = nullptr;
|
||||
if (pcnt_new_channel(unit, &chan_a_config, &chan_a) != ESP_OK ||
|
||||
pcnt_new_channel(unit, &chan_b_config, &chan_b) != ESP_OK) {
|
||||
LOG_E(TAG, "Pulse counter channel config failed");
|
||||
pcnt_del_unit(unit);
|
||||
return ERROR_RESOURCE;
|
||||
}
|
||||
|
||||
// Standard quadrature decode: each channel counts on its edge, direction decided by the
|
||||
// other channel's level.
|
||||
if (pcnt_channel_set_edge_action(chan_a, PCNT_CHANNEL_EDGE_ACTION_DECREASE, PCNT_CHANNEL_EDGE_ACTION_INCREASE) != ESP_OK ||
|
||||
pcnt_channel_set_edge_action(chan_b, PCNT_CHANNEL_EDGE_ACTION_INCREASE, PCNT_CHANNEL_EDGE_ACTION_DECREASE) != ESP_OK) {
|
||||
LOG_E(TAG, "Pulse counter edge action config failed");
|
||||
pcnt_del_unit(unit);
|
||||
return ERROR_RESOURCE;
|
||||
}
|
||||
if (pcnt_channel_set_level_action(chan_a, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_INVERSE) != ESP_OK ||
|
||||
pcnt_channel_set_level_action(chan_b, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_INVERSE) != ESP_OK) {
|
||||
LOG_E(TAG, "Pulse counter level action config failed");
|
||||
pcnt_del_unit(unit);
|
||||
return ERROR_RESOURCE;
|
||||
}
|
||||
|
||||
if (pcnt_unit_add_watch_point(unit, PCNT_LOW_LIMIT) != ESP_OK ||
|
||||
pcnt_unit_add_watch_point(unit, PCNT_HIGH_LIMIT) != ESP_OK) {
|
||||
LOG_E(TAG, "Pulse counter watch point config failed");
|
||||
pcnt_del_unit(unit);
|
||||
return ERROR_RESOURCE;
|
||||
}
|
||||
|
||||
if (pcnt_unit_enable(unit) != ESP_OK ||
|
||||
pcnt_unit_clear_count(unit) != ESP_OK ||
|
||||
pcnt_unit_start(unit) != ESP_OK) {
|
||||
LOG_E(TAG, "Pulse counter could not be started");
|
||||
pcnt_del_unit(unit);
|
||||
return ERROR_RESOURCE;
|
||||
}
|
||||
|
||||
*out_unit = unit;
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
static error_t start(Device* device) {
|
||||
const auto* config = GET_CONFIG(device);
|
||||
|
||||
auto* internal = new (std::nothrow) TpagerEncoderInternal();
|
||||
if (internal == nullptr) {
|
||||
return ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
error_t error = init_pcnt_unit(config, &internal->pcnt_unit);
|
||||
if (error != ERROR_NONE) {
|
||||
delete internal;
|
||||
return error;
|
||||
}
|
||||
|
||||
internal->pin_enter = gpio_descriptor_acquire(config->pin_enter.gpio_controller, config->pin_enter.pin, GPIO_OWNER_GPIO);
|
||||
if (internal->pin_enter == nullptr) {
|
||||
pcnt_unit_stop(internal->pcnt_unit);
|
||||
pcnt_del_unit(internal->pcnt_unit);
|
||||
delete internal;
|
||||
return ERROR_RESOURCE;
|
||||
}
|
||||
|
||||
error = gpio_descriptor_set_flags(internal->pin_enter, GPIO_FLAG_DIRECTION_INPUT);
|
||||
if (error != ERROR_NONE) {
|
||||
gpio_descriptor_release(internal->pin_enter);
|
||||
pcnt_unit_stop(internal->pcnt_unit);
|
||||
pcnt_del_unit(internal->pcnt_unit);
|
||||
delete internal;
|
||||
return error;
|
||||
}
|
||||
|
||||
device_set_driver_data(device, internal);
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
static error_t stop(Device* device) {
|
||||
auto* internal = GET_INTERNAL(device);
|
||||
|
||||
gpio_descriptor_release(internal->pin_enter);
|
||||
|
||||
if (pcnt_unit_stop(internal->pcnt_unit) != ESP_OK) {
|
||||
LOG_W(TAG, "Failed to stop encoder");
|
||||
}
|
||||
if (pcnt_del_unit(internal->pcnt_unit) != ESP_OK) {
|
||||
LOG_W(TAG, "Failed to delete encoder");
|
||||
}
|
||||
|
||||
device_set_driver_data(device, nullptr);
|
||||
delete internal;
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
static constexpr TpagerEncoderApi TPAGER_ENCODER_API = {
|
||||
.read_delta = read_delta,
|
||||
.get_button_pressed = get_button_pressed,
|
||||
};
|
||||
|
||||
const struct DeviceType TPAGER_ENCODER_TYPE {
|
||||
.name = "tpager-encoder"
|
||||
};
|
||||
|
||||
extern Module lilygo_module;
|
||||
|
||||
Driver tpager_encoder_driver = {
|
||||
.name = "tpager_encoder",
|
||||
.compatible = (const char*[]) { "lilygo,tpager-encoder", nullptr },
|
||||
.start_device = start,
|
||||
.stop_device = stop,
|
||||
.api = &TPAGER_ENCODER_API,
|
||||
.device_type = &TPAGER_ENCODER_TYPE,
|
||||
.owner = &lilygo_module,
|
||||
.internal = nullptr
|
||||
};
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user