mirror of
https://github.com/ByteWelder/Tactility.git
synced 2026-08-17 23:55:04 +00:00
Implement ADC controller and battery sense drivers
This commit is contained in:
parent
77e6872191
commit
f9384d3533
@ -3,5 +3,5 @@ file(GLOB_RECURSE SOURCE_FILES Source/*.c*)
|
||||
idf_component_register(
|
||||
SRCS ${SOURCE_FILES}
|
||||
INCLUDE_DIRS "Source"
|
||||
REQUIRES Tactility EstimatedPower driver
|
||||
REQUIRES Tactility driver
|
||||
)
|
||||
|
||||
@ -1,19 +0,0 @@
|
||||
#include "devices/Power.h"
|
||||
#include "devices/TrackballDevice.h"
|
||||
|
||||
#include <Tactility/hal/Configuration.h>
|
||||
|
||||
bool initBoot();
|
||||
|
||||
using namespace tt::hal;
|
||||
|
||||
static std::vector<std::shared_ptr<tt::hal::Device>> createDevices() {
|
||||
return {
|
||||
createPower()
|
||||
// std::make_shared<TrackballDevice>(),
|
||||
};
|
||||
}
|
||||
|
||||
extern const Configuration hardwareConfiguration = {
|
||||
.createDevices = createDevices
|
||||
};
|
||||
@ -1,10 +0,0 @@
|
||||
#include <ChargeFromAdcVoltage.h>
|
||||
#include <EstimatedPower.h>
|
||||
|
||||
std::shared_ptr<PowerDevice> createPower() {
|
||||
ChargeFromAdcVoltage::Configuration configuration;
|
||||
// 2.0 ratio, but +.11 added as display voltage sag compensation.
|
||||
configuration.adcMultiplier = 2.11;
|
||||
|
||||
return std::make_shared<EstimatedPower>(configuration);
|
||||
}
|
||||
@ -1,5 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <Tactility/hal/power/PowerDevice.h>
|
||||
|
||||
std::shared_ptr<tt::hal::power::PowerDevice> createPower();
|
||||
@ -6,6 +6,7 @@
|
||||
|
||||
#include <Tactility/SystemEvents.h>
|
||||
#include <Tactility/LogMessages.h>
|
||||
#include <Tactility/hal/Configuration.h>
|
||||
#include <Tactility/hal/gps/GpsConfiguration.h>
|
||||
#include <Tactility/kernel/Kernel.h>
|
||||
#include <Tactility/service/gps/GpsService.h>
|
||||
@ -18,6 +19,9 @@ constexpr auto* TAG = "T-Deck";
|
||||
|
||||
constexpr auto TDECK_POWERON_GPIO = GPIO_NUM_10;
|
||||
|
||||
// Legacy placeholder (required until legacy HAL is cleaned up everywhere)
|
||||
extern const tt::hal::Configuration hardwareConfiguration = {};
|
||||
|
||||
extern "C" {
|
||||
|
||||
extern Driver tdeck_keyboard_driver;
|
||||
|
||||
@ -87,7 +87,7 @@ static const KeyboardApi tdeck_keyboard_api = {
|
||||
.read_key = tdeck_keyboard_read_key,
|
||||
};
|
||||
|
||||
extern struct Module lilygo_tdeck_module;
|
||||
extern Module lilygo_tdeck_module;
|
||||
|
||||
Driver tdeck_keyboard_driver = {
|
||||
.name = "tdeck_keyboard",
|
||||
|
||||
@ -1,26 +1,43 @@
|
||||
/dts-v1/;
|
||||
|
||||
#include <tactility/bindings/battery_sense.h>
|
||||
#include <tactility/bindings/root.h>
|
||||
#include <tactility/bindings/esp32_adc_oneshot.h>
|
||||
#include <tactility/bindings/esp32_ble.h>
|
||||
#include <tactility/bindings/esp32_wifi_pinned.h>
|
||||
#include <tactility/bindings/esp32_gpio.h>
|
||||
#include <tactility/bindings/esp32_grove.h>
|
||||
#include <tactility/bindings/esp32_i2c.h>
|
||||
#include <tactility/bindings/esp32_i2s.h>
|
||||
#include <tactility/bindings/esp32_ledc_backlight.h>
|
||||
#include <tactility/bindings/esp32_sdspi.h>
|
||||
#include <tactility/bindings/esp32_spi.h>
|
||||
#include <tactility/bindings/esp32_wifi_pinned.h>
|
||||
#include <tactility/bindings/esp32_uart.h>
|
||||
|
||||
#include <bindings/gt911.h>
|
||||
#include <bindings/st7789.h>
|
||||
#include <bindings/tdeck_keyboard.h>
|
||||
#include <bindings/tdeck_keyboard_backlight.h>
|
||||
#include <tactility/bindings/esp32_spi.h>
|
||||
#include <tactility/bindings/esp32_uart.h>
|
||||
#include <tactility/bindings/esp32_sdspi.h>
|
||||
#include <bindings/st7789.h>
|
||||
|
||||
// Reference: https://wiki.lilygo.cc/get_started/en/Wearable/T-Deck-Plus/T-Deck-Plus.html#Pin-Overview
|
||||
/ {
|
||||
compatible = "root";
|
||||
model = "LilyGO T-Deck";
|
||||
|
||||
adc0 {
|
||||
compatible = "espressif,esp32-adc-oneshot";
|
||||
unit-id = <ADC_UNIT_1>;
|
||||
clk-src = <ADC_RTC_CLK_SRC_DEFAULT>;
|
||||
channels = <ADC_CHANNEL_3 ADC_ATTEN_DB_12 ADC_BITWIDTH_DEFAULT>;
|
||||
};
|
||||
|
||||
battery-sense {
|
||||
compatible = "battery-sense";
|
||||
io-channels = <&adc0 0>;
|
||||
reference-voltage-mv = <3300>;
|
||||
multiplier = <2110>;
|
||||
};
|
||||
|
||||
wifi0 {
|
||||
compatible = "espressif,esp32-wifi-pinned";
|
||||
status = "disabled";
|
||||
|
||||
@ -6,7 +6,7 @@ idf_component_register(
|
||||
SRCS ${SOURCES}
|
||||
INCLUDE_DIRS "include/"
|
||||
PRIV_INCLUDE_DIRS "private/"
|
||||
REQUIRES TactilityKernel driver esp_driver_i2c vfs fatfs esp_wifi esp_netif esp_event
|
||||
REQUIRES TactilityKernel driver esp_adc esp_driver_i2c vfs fatfs esp_wifi esp_netif esp_event
|
||||
)
|
||||
|
||||
idf_component_optional_requires(PRIVATE bt usb espressif__usb_host_hid espressif__usb_host_msc)
|
||||
|
||||
@ -0,0 +1,30 @@
|
||||
description: ESP32 ADC Controller (oneshot mode)
|
||||
|
||||
include: ["adc-controller.yaml"]
|
||||
|
||||
compatible: "espressif,esp32-adc-oneshot"
|
||||
|
||||
properties:
|
||||
unit-id:
|
||||
type: int
|
||||
required: true
|
||||
description: |
|
||||
The ADC unit, defined by adc_unit_t (e.g. ADC_UNIT_1, ADC_UNIT_2).
|
||||
clk-src:
|
||||
type: int
|
||||
default: 0
|
||||
description: |
|
||||
Clock source, defined by adc_oneshot_clk_src_t. 0 selects the default clock source.
|
||||
ulp-mode:
|
||||
type: int
|
||||
default: 0
|
||||
description: |
|
||||
ULP control mode, defined by adc_ulp_mode_t. 0 means ADC_ULP_MODE_DISABLE.
|
||||
channels:
|
||||
type: phandle-array
|
||||
element-type: "struct Esp32AdcOneshotChannelConfig"
|
||||
default: "{ }"
|
||||
description: |
|
||||
Per-channel configuration. Each entry is written as <channel atten bitwidth>,
|
||||
e.g. <ADC_CHANNEL_3 ADC_ATTEN_DB_12 ADC_BITWIDTH_DEFAULT>.
|
||||
A consumer device refers to a channel with a phandle and channel number, e.g. `<&adc0 3>`.
|
||||
@ -0,0 +1,15 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#pragma once
|
||||
|
||||
#include <tactility/bindings/bindings.h>
|
||||
#include <tactility/drivers/esp32_adc_oneshot.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
DEFINE_DEVICETREE(esp32_adc_oneshot, struct Esp32AdcOneshotConfig)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@ -0,0 +1,31 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#pragma once
|
||||
|
||||
#include <tactility/device.h>
|
||||
|
||||
#include <esp_adc/adc_oneshot.h>
|
||||
#include <hal/adc_types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct Esp32AdcOneshotChannelConfig {
|
||||
adc_channel_t channel;
|
||||
adc_atten_t atten;
|
||||
adc_bitwidth_t bitwidth;
|
||||
};
|
||||
|
||||
struct Esp32AdcOneshotConfig {
|
||||
adc_unit_t unit_id;
|
||||
adc_oneshot_clk_src_t clk_src;
|
||||
adc_ulp_mode_t ulp_mode;
|
||||
/** Per-channel configuration */
|
||||
const struct Esp32AdcOneshotChannelConfig* channels;
|
||||
/** The item count of channels */
|
||||
size_t channel_count;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@ -0,0 +1,99 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#include <esp_adc/adc_oneshot.h>
|
||||
|
||||
#include <tactility/error_esp32.h>
|
||||
#include <tactility/driver.h>
|
||||
#include <tactility/drivers/adc_controller.h>
|
||||
#include <tactility/drivers/esp32_adc_oneshot.h>
|
||||
#include <tactility/log.h>
|
||||
#include <tactility/time.h>
|
||||
|
||||
#define TAG "esp32_adc_oneshot"
|
||||
|
||||
#define GET_CONFIG(device) ((Esp32AdcOneshotConfig*)device->config)
|
||||
#define GET_HANDLE(device) ((adc_oneshot_unit_handle_t)device_get_driver_data(device))
|
||||
|
||||
static const Esp32AdcOneshotChannelConfig* find_channel_config(const Esp32AdcOneshotConfig* dts_config, uint8_t channel_index) {
|
||||
if (channel_index >= dts_config->channel_count) {
|
||||
return nullptr;
|
||||
}
|
||||
return &dts_config->channels[channel_index];
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
static error_t read_raw(Device* device, uint8_t channel, int* out_raw, TickType_t timeout) {
|
||||
if (xPortInIsrContext()) return ERROR_ISR_STATUS;
|
||||
auto* dts_config = GET_CONFIG(device);
|
||||
auto* channel_config = find_channel_config(dts_config, channel);
|
||||
if (channel_config == nullptr) {
|
||||
return ERROR_OUT_OF_RANGE;
|
||||
}
|
||||
|
||||
esp_err_t esp_error = adc_oneshot_read(GET_HANDLE(device), channel_config->channel, out_raw);
|
||||
if (esp_error != ESP_OK) {
|
||||
LOG_E(TAG, "read(channel=%u) failed: %s", channel, esp_err_to_name(esp_error));
|
||||
}
|
||||
return esp_err_to_error(esp_error);
|
||||
}
|
||||
|
||||
static error_t start(Device* device) {
|
||||
LOG_I(TAG, "start %s", device->name);
|
||||
auto* dts_config = GET_CONFIG(device);
|
||||
|
||||
adc_oneshot_unit_init_cfg_t init_config = {
|
||||
.unit_id = dts_config->unit_id,
|
||||
.clk_src = dts_config->clk_src,
|
||||
.ulp_mode = dts_config->ulp_mode,
|
||||
};
|
||||
|
||||
adc_oneshot_unit_handle_t handle;
|
||||
esp_err_t esp_error = adc_oneshot_new_unit(&init_config, &handle);
|
||||
if (esp_error != ESP_OK) {
|
||||
LOG_E(TAG, "Failed to create ADC unit %d: %s", (int)dts_config->unit_id, esp_err_to_name(esp_error));
|
||||
return ERROR_RESOURCE;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < dts_config->channel_count; i++) {
|
||||
const auto& channel_config = dts_config->channels[i];
|
||||
adc_oneshot_chan_cfg_t chan_cfg = {
|
||||
.atten = channel_config.atten,
|
||||
.bitwidth = channel_config.bitwidth,
|
||||
};
|
||||
esp_error = adc_oneshot_config_channel(handle, channel_config.channel, &chan_cfg);
|
||||
if (esp_error != ESP_OK) {
|
||||
LOG_E(TAG, "Failed to configure channel %d: %s", (int)channel_config.channel, esp_err_to_name(esp_error));
|
||||
adc_oneshot_del_unit(handle);
|
||||
return ERROR_RESOURCE;
|
||||
}
|
||||
}
|
||||
|
||||
device_set_driver_data(device, handle);
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
static error_t stop(Device* device) {
|
||||
LOG_I(TAG, "stop %s", device->name);
|
||||
adc_oneshot_del_unit(GET_HANDLE(device));
|
||||
device_set_driver_data(device, nullptr);
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
static constexpr AdcControllerApi ESP32_ADC_ONESHOT_API = {
|
||||
.read_raw = read_raw
|
||||
};
|
||||
|
||||
extern Module platform_esp32_module;
|
||||
|
||||
Driver esp32_adc_oneshot_driver = {
|
||||
.name = "esp32_adc_oneshot",
|
||||
.compatible = (const char*[]) { "espressif,esp32-adc-oneshot", nullptr },
|
||||
.start_device = start,
|
||||
.stop_device = stop,
|
||||
.api = &ESP32_ADC_ONESHOT_API,
|
||||
.device_type = &ADC_CONTROLLER_TYPE,
|
||||
.owner = &platform_esp32_module,
|
||||
.internal = nullptr
|
||||
};
|
||||
|
||||
} // extern "C"
|
||||
@ -11,6 +11,7 @@
|
||||
|
||||
extern "C" {
|
||||
|
||||
extern Driver esp32_adc_oneshot_driver;
|
||||
extern Driver esp32_gpio_driver;
|
||||
extern Driver esp32_i2c_driver;
|
||||
extern Driver esp32_i2c_master_driver;
|
||||
@ -43,6 +44,7 @@ extern Driver esp32_usbhost_msc_driver;
|
||||
static error_t start() {
|
||||
/* We crash when construct fails, because if a single driver fails to construct,
|
||||
* there is no guarantee that the previously constructed drivers can be destroyed */
|
||||
check(driver_construct_add(&esp32_adc_oneshot_driver) == ERROR_NONE);
|
||||
check(driver_construct_add(&esp32_gpio_driver) == ERROR_NONE);
|
||||
check(driver_construct_add(&esp32_i2c_driver) == ERROR_NONE);
|
||||
check(driver_construct_add(&esp32_i2c_master_driver) == ERROR_NONE);
|
||||
@ -93,6 +95,7 @@ static error_t stop() {
|
||||
check(driver_remove_destruct(&esp32_ble_serial_driver) == ERROR_NONE);
|
||||
check(driver_remove_destruct(&esp32_bluetooth_driver) == ERROR_NONE);
|
||||
#endif
|
||||
check(driver_remove_destruct(&esp32_adc_oneshot_driver) == ERROR_NONE);
|
||||
check(driver_remove_destruct(&esp32_gpio_driver) == ERROR_NONE);
|
||||
check(driver_remove_destruct(&esp32_i2c_driver) == ERROR_NONE);
|
||||
check(driver_remove_destruct(&esp32_i2c_master_driver) == ERROR_NONE);
|
||||
|
||||
@ -231,7 +231,7 @@ static void registerInternalApps() {
|
||||
addAppManifest(app::gpssettings::manifest);
|
||||
}
|
||||
|
||||
if (hal::hasDevice(hal::Device::Type::Power)) {
|
||||
if (device_exists_of_type(&POWER_SUPPLY_TYPE)) {
|
||||
addAppManifest(app::power::manifest);
|
||||
}
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
#include <Tactility/Mutex.h>
|
||||
#include <Tactility/Timer.h>
|
||||
#include <Tactility/hal/power/PowerDevice.h>
|
||||
#include <tactility/drivers/power_supply.h>
|
||||
#include <tactility/filesystem/file_system.h>
|
||||
#include <Tactility/lvgl/Lvgl.h>
|
||||
#include <Tactility/lvgl/LvglSync.h>
|
||||
@ -90,10 +90,10 @@ static const char* getSdCardStatusIcon(bool mounted) {
|
||||
|
||||
static const char* getPowerStatusIcon() {
|
||||
// TODO: Support multiple power devices?
|
||||
std::shared_ptr<hal::power::PowerDevice> power;
|
||||
hal::findDevices<hal::power::PowerDevice>(hal::Device::Type::Power, [&power](const auto& device) {
|
||||
if (device->supportsMetric(hal::power::PowerDevice::MetricType::ChargeLevel)) {
|
||||
power = device;
|
||||
Device* power = nullptr;
|
||||
device_for_each_of_type(&POWER_SUPPLY_TYPE, &power, [](Device* device, void* context) {
|
||||
if (device_is_ready(device) && power_supply_supports_property(device, POWER_SUPPLY_PROP_CAPACITY)) {
|
||||
*static_cast<Device**>(context) = device;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@ -103,12 +103,12 @@ static const char* getPowerStatusIcon() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
hal::power::PowerDevice::MetricData charge_level;
|
||||
if (!power->getMetric(hal::power::PowerDevice::MetricType::ChargeLevel, charge_level)) {
|
||||
PowerSupplyPropertyValue charge_level;
|
||||
if (power_supply_get_property(power, POWER_SUPPLY_PROP_CAPACITY, &charge_level) != ERROR_NONE) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint8_t charge = charge_level.valueAsUint8;
|
||||
int charge = charge_level.int_value;
|
||||
|
||||
if (charge >= 95) {
|
||||
return LVGL_ICON_STATUSBAR_BATTERY_ANDROID_FRAME_FULL;
|
||||
|
||||
1
TactilityKernel/bindings/adc-controller.yaml
Normal file
1
TactilityKernel/bindings/adc-controller.yaml
Normal file
@ -0,0 +1 @@
|
||||
description: ADC controller
|
||||
19
TactilityKernel/bindings/battery-sense.yaml
Normal file
19
TactilityKernel/bindings/battery-sense.yaml
Normal file
@ -0,0 +1,19 @@
|
||||
description: Battery voltage sense via an ADC channel behind a voltage divider
|
||||
|
||||
compatible: "battery-sense"
|
||||
|
||||
properties:
|
||||
io-channels:
|
||||
type: phandles
|
||||
required: true
|
||||
description: The ADC channel connected to the battery sense circuit, e.g. `<&adc1 3>`
|
||||
reference-voltage-mv:
|
||||
type: int
|
||||
required: true
|
||||
description: The ADC's effective full-scale reference voltage at the channel's configured attenuation, in mV.
|
||||
multiplier:
|
||||
type: int
|
||||
default: 1000
|
||||
description: |
|
||||
Voltage divider correction factor, fixed-point with 3 decimals (1000 = 1.000, i.e. no division).
|
||||
battery_mv = adc_mv * multiplier / 1000.
|
||||
15
TactilityKernel/include/tactility/bindings/battery_sense.h
Normal file
15
TactilityKernel/include/tactility/bindings/battery_sense.h
Normal file
@ -0,0 +1,15 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#pragma once
|
||||
|
||||
#include <tactility/bindings/bindings.h>
|
||||
#include <tactility/drivers/battery_sense.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
DEFINE_DEVICETREE(battery_sense, struct BatterySenseConfig)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
30
TactilityKernel/include/tactility/drivers/adc.h
Normal file
30
TactilityKernel/include/tactility/drivers/adc.h
Normal file
@ -0,0 +1,30 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct Device;
|
||||
|
||||
#define ADC_CHANNEL_SPEC_NONE ((struct AdcChannelSpec) { NULL, 0 })
|
||||
|
||||
/** The index of a channel on an ADC controller */
|
||||
typedef uint8_t adc_channel_index_t;
|
||||
|
||||
/**
|
||||
* Specifies a channel on a specific ADC controller.
|
||||
* Used by the devicetree, drivers and application code to refer to ADC channels.
|
||||
*/
|
||||
struct AdcChannelSpec {
|
||||
/** ADC device controlling the channel */
|
||||
struct Device* adc_controller;
|
||||
/** The channel's index on the device */
|
||||
adc_channel_index_t channel;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
56
TactilityKernel/include/tactility/drivers/adc_controller.h
Normal file
56
TactilityKernel/include/tactility/drivers/adc_controller.h
Normal file
@ -0,0 +1,56 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "adc.h"
|
||||
|
||||
#include <tactility/freertos/freertos.h>
|
||||
#include <tactility/error.h>
|
||||
|
||||
struct Device;
|
||||
|
||||
/**
|
||||
* @brief API for ADC controller drivers.
|
||||
*/
|
||||
struct AdcControllerApi {
|
||||
/**
|
||||
* @brief Reads the raw conversion result of an ADC channel.
|
||||
* @param[in] device the ADC controller device
|
||||
* @param[in] channel the channel index to read
|
||||
* @param[out] out_raw the raw conversion result
|
||||
* @param[in] timeout the maximum time to wait for the operation to complete
|
||||
* @retval ERROR_NONE when the read operation was successful
|
||||
* @retval ERROR_OUT_OF_RANGE when the channel index is not configured
|
||||
* @retval ERROR_TIMEOUT when the operation timed out
|
||||
*/
|
||||
error_t (*read_raw)(struct Device* device, uint8_t channel, int* out_raw, TickType_t timeout);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Reads the raw conversion result of an ADC channel using the specified controller.
|
||||
* @param[in] device the ADC controller device
|
||||
* @param[in] channel the channel index to read
|
||||
* @param[out] out_raw the raw conversion result
|
||||
* @param[in] timeout the maximum time to wait for the operation to complete
|
||||
* @retval ERROR_NONE when the read operation was successful
|
||||
*/
|
||||
error_t adc_controller_read_raw(struct Device* device, uint8_t channel, int* out_raw, TickType_t timeout);
|
||||
|
||||
/**
|
||||
* @brief Reads the raw conversion result of the ADC channel described by the given spec.
|
||||
* @param[in] spec the channel spec, as acquired from a devicetree phandle reference (e.g. `<&adc0 3>`)
|
||||
* @param[out] out_raw the raw conversion result
|
||||
* @param[in] timeout the maximum time to wait for the operation to complete
|
||||
* @retval ERROR_NONE when the read operation was successful
|
||||
*/
|
||||
error_t adc_channel_read_raw(const struct AdcChannelSpec* spec, int* out_raw, TickType_t timeout);
|
||||
|
||||
extern const struct DeviceType ADC_CONTROLLER_TYPE;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
22
TactilityKernel/include/tactility/drivers/battery_sense.h
Normal file
22
TactilityKernel/include/tactility/drivers/battery_sense.h
Normal file
@ -0,0 +1,22 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#pragma once
|
||||
|
||||
#include <tactility/drivers/adc.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct BatterySenseConfig {
|
||||
/** The ADC channel connected to the battery sense circuit */
|
||||
struct AdcChannelSpec io_channel;
|
||||
/** The ADC's effective full-scale reference voltage at the channel's configured attenuation, in mV */
|
||||
uint32_t reference_voltage_mv;
|
||||
/** Voltage divider correction factor, fixed-point with 3 decimals (1000 = 1.000, i.e. no division) */
|
||||
uint32_t multiplier;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
22
TactilityKernel/source/drivers/adc_controller.cpp
Normal file
22
TactilityKernel/source/drivers/adc_controller.cpp
Normal file
@ -0,0 +1,22 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#include <tactility/drivers/adc_controller.h>
|
||||
#include <tactility/device.h>
|
||||
|
||||
#define ADC_CONTROLLER_DRIVER_API(driver) ((struct AdcControllerApi*)driver->api)
|
||||
|
||||
extern "C" {
|
||||
|
||||
error_t adc_controller_read_raw(Device* device, uint8_t channel, int* out_raw, TickType_t timeout) {
|
||||
const auto* driver = device_get_driver(device);
|
||||
return ADC_CONTROLLER_DRIVER_API(driver)->read_raw(device, channel, out_raw, timeout);
|
||||
}
|
||||
|
||||
error_t adc_channel_read_raw(const struct AdcChannelSpec* spec, int* out_raw, TickType_t timeout) {
|
||||
return adc_controller_read_raw(spec->adc_controller, spec->channel, out_raw, timeout);
|
||||
}
|
||||
|
||||
const struct DeviceType ADC_CONTROLLER_TYPE {
|
||||
.name = "adc-controller"
|
||||
};
|
||||
|
||||
}
|
||||
155
TactilityKernel/source/drivers/battery_sense.cpp
Normal file
155
TactilityKernel/source/drivers/battery_sense.cpp
Normal file
@ -0,0 +1,155 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#include <tactility/check.h>
|
||||
#include <tactility/device.h>
|
||||
#include <tactility/driver.h>
|
||||
#include <tactility/drivers/adc_controller.h>
|
||||
#include <tactility/drivers/battery_sense.h>
|
||||
#include <tactility/drivers/power_supply.h>
|
||||
|
||||
#include <new>
|
||||
|
||||
// Raw-to-millivolt conversion assumes a 12-bit ADC, since the generic ADC API doesn't expose resolution.
|
||||
#define ADC_MAX_RAW 4095
|
||||
|
||||
// The power-supply child's config pointer isn't set; it reads its settings from its parent's config instead.
|
||||
#define GET_PARENT_CONFIG(device) ((const BatterySenseConfig*)device_get_parent(device)->config)
|
||||
|
||||
extern "C" {
|
||||
|
||||
static bool supports_property(Device*, PowerSupplyProperty property) {
|
||||
return property == POWER_SUPPLY_PROP_VOLTAGE;
|
||||
}
|
||||
|
||||
static error_t get_property(Device* device, PowerSupplyProperty property, PowerSupplyPropertyValue* out_value) {
|
||||
if (property != POWER_SUPPLY_PROP_VOLTAGE) {
|
||||
return ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
const auto* config = GET_PARENT_CONFIG(device);
|
||||
int raw;
|
||||
error_t error = adc_channel_read_raw(&config->io_channel, &raw, portMAX_DELAY);
|
||||
if (error != ERROR_NONE) {
|
||||
return error;
|
||||
}
|
||||
|
||||
int64_t adc_mv = ((int64_t)raw * config->reference_voltage_mv) / ADC_MAX_RAW;
|
||||
out_value->int_value = (int)((adc_mv * config->multiplier) / 1000);
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
static bool supports_charge_control(Device*) { return false; }
|
||||
static bool is_allowed_to_charge(Device*) { return false; }
|
||||
static error_t set_allowed_to_charge(Device*, bool) { return ERROR_NOT_SUPPORTED; }
|
||||
static bool supports_quick_charge(Device*) { return false; }
|
||||
static bool is_quick_charge_enabled(Device*) { return false; }
|
||||
static error_t set_quick_charge_enabled(Device*, bool) { return ERROR_NOT_SUPPORTED; }
|
||||
static bool supports_power_off(Device*) { return false; }
|
||||
static error_t power_off(Device*) { return ERROR_NOT_SUPPORTED; }
|
||||
|
||||
static constexpr PowerSupplyApi BATTERY_SENSE_POWER_SUPPLY_API = {
|
||||
.supports_property = supports_property,
|
||||
.get_property = get_property,
|
||||
.supports_charge_control = supports_charge_control,
|
||||
.is_allowed_to_charge = is_allowed_to_charge,
|
||||
.set_allowed_to_charge = set_allowed_to_charge,
|
||||
.supports_quick_charge = supports_quick_charge,
|
||||
.is_quick_charge_enabled = is_quick_charge_enabled,
|
||||
.set_quick_charge_enabled = set_quick_charge_enabled,
|
||||
.supports_power_off = supports_power_off,
|
||||
.power_off = power_off,
|
||||
};
|
||||
|
||||
// Registered (driver_construct_add() in kernel_init.cpp) so driver_bind() has a valid ->internal,
|
||||
// but never matched against a devicetree node: battery_sense_driver wires it up directly by pointer.
|
||||
Driver battery_sense_power_supply_driver = {
|
||||
.name = "battery-sense-power-supply",
|
||||
.compatible = (const char*[]) { "battery-sense-power-supply", nullptr },
|
||||
.start_device = nullptr,
|
||||
.stop_device = nullptr,
|
||||
.api = &BATTERY_SENSE_POWER_SUPPLY_API,
|
||||
.device_type = &POWER_SUPPLY_TYPE,
|
||||
.owner = nullptr,
|
||||
.internal = nullptr
|
||||
};
|
||||
|
||||
struct BatterySenseInternal {
|
||||
Device* power_supply_device = nullptr;
|
||||
};
|
||||
|
||||
static error_t create_power_supply_child(Device* parent, Device*& out_child) {
|
||||
auto* child = new(std::nothrow) Device { .address = 0, .name = "battery-sense-power-supply", .config = nullptr, .parent = nullptr, .internal = nullptr };
|
||||
if (child == nullptr) {
|
||||
return ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
error_t error = device_construct(child);
|
||||
if (error != ERROR_NONE) {
|
||||
delete child;
|
||||
return error;
|
||||
}
|
||||
|
||||
device_set_parent(child, parent);
|
||||
device_set_driver(child, &battery_sense_power_supply_driver);
|
||||
|
||||
error = device_add(child);
|
||||
if (error != ERROR_NONE) {
|
||||
device_destruct(child);
|
||||
delete child;
|
||||
return error;
|
||||
}
|
||||
|
||||
error = device_start(child);
|
||||
if (error != ERROR_NONE) {
|
||||
device_remove(child);
|
||||
device_destruct(child);
|
||||
delete child;
|
||||
return error;
|
||||
}
|
||||
|
||||
out_child = child;
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
static void destroy_power_supply_child(Device* child) {
|
||||
check(device_stop(child) == ERROR_NONE);
|
||||
check(device_remove(child) == ERROR_NONE);
|
||||
check(device_destruct(child) == ERROR_NONE);
|
||||
delete child;
|
||||
}
|
||||
|
||||
static error_t start(Device* device) {
|
||||
auto* internal = new(std::nothrow) BatterySenseInternal();
|
||||
if (internal == nullptr) {
|
||||
return ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
error_t error = create_power_supply_child(device, internal->power_supply_device);
|
||||
if (error != ERROR_NONE) {
|
||||
delete internal;
|
||||
return error;
|
||||
}
|
||||
|
||||
device_set_driver_data(device, internal);
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
static error_t stop(Device* device) {
|
||||
auto* internal = (BatterySenseInternal*)device_get_driver_data(device);
|
||||
destroy_power_supply_child(internal->power_supply_device);
|
||||
device_set_driver_data(device, nullptr);
|
||||
delete internal;
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
Driver battery_sense_driver = {
|
||||
.name = "battery-sense",
|
||||
.compatible = (const char*[]) { "battery-sense", nullptr },
|
||||
.start_device = start,
|
||||
.stop_device = stop,
|
||||
.api = nullptr,
|
||||
.device_type = nullptr,
|
||||
.owner = nullptr,
|
||||
.internal = nullptr
|
||||
};
|
||||
|
||||
}
|
||||
@ -20,6 +20,10 @@ static error_t start() {
|
||||
if (driver_construct_add(&pointer_placeholder_driver) != ERROR_NONE) return ERROR_RESOURCE;
|
||||
extern Driver spi_peripheral_driver;
|
||||
if (driver_construct_add(&spi_peripheral_driver) != ERROR_NONE) return ERROR_RESOURCE;
|
||||
extern Driver battery_sense_driver;
|
||||
if (driver_construct_add(&battery_sense_driver) != ERROR_NONE) return ERROR_RESOURCE;
|
||||
extern Driver battery_sense_power_supply_driver;
|
||||
if (driver_construct_add(&battery_sense_power_supply_driver) != ERROR_NONE) return ERROR_RESOURCE;
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user