Merge develop into main (#368)

New boards:
- LilyGO T-Dongle S3
- M5Stack StickC Plus
- M5Stack StickC Plus2

New drivers:
- AXP192: power control via I2C
- ButtonControl: GPIO button input as LVGL device

Other changes:
- Updated implementation of AXP192 driver for Core2 board
- Fix launcher UX for vertical layout
- Fix error when properties file had an empty line
- Add `__floatsidf` to `tt_init.cpp`
This commit is contained in:
Ken Van Hoeylandt
2025-10-14 20:39:23 +02:00
committed by GitHub
parent 3a59540365
commit d8346998ce
68 changed files with 1857 additions and 142 deletions
+117
View File
@@ -0,0 +1,117 @@
#include "Axp192.h"
constexpr auto TAG = "Axp192Power";
int32_t Axp192::i2cRead(void* handle, uint8_t address, uint8_t reg, uint8_t* buffer, uint16_t size) {
const auto* device = static_cast<Axp192*>(handle);
if (tt::hal::i2c::masterReadRegister(device->configuration->port, address, reg, buffer, size, device->configuration->readTimeout)) {
return AXP192_OK;
} else {
return 1;
}
}
int32_t Axp192::i2cWrite(void* handle, uint8_t address, uint8_t reg, const uint8_t* buffer, uint16_t size) {
const auto* device = static_cast<Axp192*>(handle);
if (tt::hal::i2c::masterWriteRegister(device->configuration->port, address, reg, buffer, size, device->configuration->writeTimeout)) {
return AXP192_OK;
} else {
return 1;
}
}
bool Axp192::supportsMetric(MetricType type) const {
if (!isInitialized) {
return false;
}
switch (type) {
using enum MetricType;
case BatteryVoltage:
case ChargeLevel:
case IsCharging:
return true;
default:
return false;
}
}
bool Axp192::getMetric(MetricType type, MetricData& data) {
switch (type) {
using enum MetricType;
case BatteryVoltage: {
float voltage;
if (axp192_read(&axpDevice, AXP192_BATTERY_VOLTAGE, &voltage) == ESP_OK) {
data.valueAsUint32 = (uint32_t)std::max((voltage * 1000.f), 0.0f);
return true;
} else {
return false;
}
}
case ChargeLevel: {
float vbat, charge_current;
if (
axp192_read(&axpDevice, AXP192_BATTERY_VOLTAGE, &vbat) == ESP_OK &&
axp192_read(&axpDevice, AXP192_CHARGE_CURRENT, &charge_current) == ESP_OK
) {
float max_voltage = 4.20f;
float min_voltage = 2.69f; // From M5Unified
float voltage_correction = (charge_current > 0.01f) ? -0.1f : 0.f; // Roughly 0.1V drop when ccharging
float corrected_voltage = vbat + voltage_correction;
if (corrected_voltage > 2.69f) {
float charge_factor = (corrected_voltage - min_voltage) / (max_voltage - min_voltage);
data.valueAsUint8 = (uint8_t)(charge_factor * 100.f);
} else {
data.valueAsUint8 = 0;
}
return true;
} else {
return false;
}
}
case IsCharging: {
float charge_current;
if (axp192_read(&axpDevice, AXP192_CHARGE_CURRENT, &charge_current) == ESP_OK) {
data.valueAsBool = charge_current > 0.001f;
return true;
} else {
return false;
}
}
case Current: {
float charge_current, discharge_current;
if (
axp192_read(&axpDevice, AXP192_CHARGE_CURRENT, &charge_current) == ESP_OK &&
axp192_read(&axpDevice, AXP192_DISCHARGE_CURRENT, &discharge_current) == ESP_OK
) {
if (charge_current > 0.0f) {
data.valueAsInt32 = (int32_t) (charge_current * 1000.0f);
} else {
data.valueAsInt32 = -(int32_t) (discharge_current * 1000.0f);
}
return true;
} else {
return false;
}
}
default:
return false;
}
}
bool Axp192::isAllowedToCharge() const {
uint8_t buffer;
if (axp192_read(&axpDevice, AXP192_CHARGE_CONTROL_1, &buffer) == ESP_OK) {
return buffer & 0x80;
} else {
return false;
}
}
void Axp192::setAllowedToCharge(bool canCharge) {
uint8_t buffer;
if (axp192_read(&axpDevice, AXP192_CHARGE_CONTROL_1, &buffer) == ESP_OK) {
buffer = (buffer & 0x7F) + (canCharge ? 0x80 : 0x00);
axp192_write(&axpDevice, AXP192_CHARGE_CONTROL_1, buffer);
}
}
+440
View File
@@ -0,0 +1,440 @@
/*
MIT License
Copyright (c) 2019-2021 Mika Tuupola
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", 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 THE
AUTHORS OR COPYRIGHT HOLDERS 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 THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-cut-
This file is part of hardware agnostic I2C driver for AXP192:
https://github.com/tuupola/axp192
SPDX-License-Identifier: MIT
Version: 0.6.0
*/
#include <stdarg.h>
#include <stdint.h>
#include "axp192/axp192_config.h"
#include "axp192/axp192.h"
static axp192_err_t read_coloumb_counter(const axp192_t *axp, float *buffer);
static axp192_err_t read_battery_power(const axp192_t *axp, float *buffer);
static const axp192_init_command_t init_commands[] = {
#ifdef AXP192_INCLUDE_SDKCONFIG_H
/* Currently you have to use menuconfig to be able to use axp192_init() */
{AXP192_DCDC1_VOLTAGE, {CONFIG_AXP192_DCDC1_VOLTAGE}, 1},
{AXP192_DCDC3_VOLTAGE, {CONFIG_AXP192_DCDC3_VOLTAGE}, 1},
{AXP192_LDO23_VOLTAGE, {CONFIG_AXP192_LDO23_VOLTAGE}, 1},
{AXP192_GPIO0_LDOIO0_VOLTAGE, {CONFIG_AXP192_GPIO0_LDOIO0_VOLTAGE}, 1},
{AXP192_DCDC13_LDO23_CONTROL, {CONFIG_AXP192_DCDC13_LDO23_CONTROL}, 1},
{AXP192_EXTEN_DCDC2_CONTROL, {CONFIG_AXP192_EXTEN_DCDC2_CONTROL}, 1},
{AXP192_GPIO0_CONTROL, {CONFIG_AXP192_GPIO0_CONTROL}, 1},
{AXP192_GPIO1_CONTROL, {CONFIG_AXP192_GPIO1_CONTROL}, 1},
{AXP192_GPIO2_CONTROL, {CONFIG_AXP192_GPIO2_CONTROL}, 1},
{AXP192_GPIO43_FUNCTION_CONTROL, {CONFIG_AXP192_GPIO43_FUNCTION_CONTROL}, 1},
{AXP192_ADC_ENABLE_1, {CONFIG_AXP192_ADC_ENABLE_1}, 1},
{AXP192_CHARGE_CONTROL_1, {CONFIG_AXP192_CHARGE_CONTROL_1}, 1},
{AXP192_BATTERY_CHARGE_CONTROL, {CONFIG_AXP192_BATTERY_CHARGE_CONTROL}, 1},
#endif /* AXP192_INCLUDE_SDKCONFIG_H */
/* End of commands. */
{0, {0}, 0xff},
};
axp192_err_t axp192_init(const axp192_t *axp)
{
uint8_t cmd = 0;
axp192_err_t status;
/* Send all the commands. */
while (init_commands[cmd].count != 0xff) {
status = axp->write(
axp->handle,
AXP192_ADDRESS,
init_commands[cmd].command,
init_commands[cmd].data,
init_commands[cmd].count & 0x1f
);
if (AXP192_OK != status) {
return status;
}
cmd++;
}
return AXP192_OK;
}
static axp192_err_t axp192_read_adc(const axp192_t *axp, uint8_t reg, float *buffer)
{
uint8_t tmp[4];
float sensitivity = 1.0;
float offset = 0.0;
axp192_err_t status;
switch (reg) {
case AXP192_ACIN_VOLTAGE:
case AXP192_VBUS_VOLTAGE:
/* 1.7mV per LSB */
sensitivity = 1.7 / 1000;
break;
case AXP192_ACIN_CURRENT:
/* 0.375mA per LSB */
sensitivity = 0.625 / 1000;
break;
case AXP192_VBUS_CURRENT:
/* 0.375mA per LSB */
sensitivity = 0.375 / 1000;
break;
case AXP192_TEMP:
/* 0.1C per LSB, 0x00 = -144.7C */
sensitivity = 0.1;
offset = -144.7;
break;
case AXP192_TS_INPUT:
/* 0.8mV per LSB */
sensitivity = 0.8 / 1000;
break;
case AXP192_BATTERY_POWER:
/* 1.1mV * 0.5mA per LSB */
return read_battery_power(axp, buffer);
break;
case AXP192_BATTERY_VOLTAGE:
/* 1.1mV per LSB */
sensitivity = 1.1 / 1000;
break;
case AXP192_CHARGE_CURRENT:
case AXP192_DISCHARGE_CURRENT:
/* 0.5mV per LSB */
sensitivity = 0.5 / 1000;
break;
case AXP192_APS_VOLTAGE:
/* 1.4mV per LSB */
sensitivity = 1.4 / 1000;
break;
case AXP192_COULOMB_COUNTER:
/* This is currently untested. */
return read_coloumb_counter(axp, buffer);
break;
}
status = axp->read(axp->handle, AXP192_ADDRESS, reg, tmp, 2);
if (AXP192_OK != status) {
return status;
}
*buffer = (((tmp[0] << 4) + tmp[1]) * sensitivity) + offset;
return AXP192_OK;
}
axp192_err_t axp192_read(const axp192_t *axp, uint8_t reg, void *buffer) {
switch (reg) {
case AXP192_ACIN_VOLTAGE:
case AXP192_VBUS_VOLTAGE:
case AXP192_ACIN_CURRENT:
case AXP192_VBUS_CURRENT:
case AXP192_TEMP:
case AXP192_TS_INPUT:
case AXP192_BATTERY_POWER:
case AXP192_BATTERY_VOLTAGE:
case AXP192_CHARGE_CURRENT:
case AXP192_DISCHARGE_CURRENT:
case AXP192_APS_VOLTAGE:
case AXP192_COULOMB_COUNTER:
/* Return ADC value. */
return axp192_read_adc(axp, reg, buffer);
break;
default:
/* Return raw register value. */
return axp->read(axp->handle, AXP192_ADDRESS, reg, buffer, 1);
}
}
axp192_err_t axp192_write(const axp192_t *axp, uint8_t reg, uint8_t value) {
switch (reg) {
case AXP192_ACIN_VOLTAGE:
case AXP192_VBUS_VOLTAGE:
case AXP192_ACIN_CURRENT:
case AXP192_VBUS_CURRENT:
case AXP192_TEMP:
case AXP192_TS_INPUT:
case AXP192_BATTERY_POWER:
case AXP192_BATTERY_VOLTAGE:
case AXP192_CHARGE_CURRENT:
case AXP192_DISCHARGE_CURRENT:
case AXP192_APS_VOLTAGE:
case AXP192_COULOMB_COUNTER:
/* Read only register. */
return AXP192_ERROR_ENOTSUP;
break;
default:
/* Write raw register value. */
return axp->write(axp->handle, AXP192_ADDRESS, reg, &value, 1);
}
}
axp192_err_t axp192_ioctl(const axp192_t *axp, int command, ...)
{
uint8_t reg = command >> 8;
uint8_t tmp;
uint16_t argument;
va_list ap;
switch (command) {
case AXP192_COULOMB_COUNTER_ENABLE:
tmp = 0b10000000;
return axp->write(axp->handle, AXP192_ADDRESS, reg, &tmp, 1);
break;
case AXP192_COULOMB_COUNTER_DISABLE:
tmp = 0b00000000;
return axp->write(axp->handle, AXP192_ADDRESS, reg, &tmp, 1);
break;
case AXP192_COULOMB_COUNTER_SUSPEND:
tmp = 0b11000000;
return axp->write(axp->handle, AXP192_ADDRESS, reg, &tmp, 1);
break;
case AXP192_COULOMB_COUNTER_CLEAR:
tmp = 0b10100000;
return axp->write(axp->handle, AXP192_ADDRESS, reg, &tmp, 1);
break;
/* This is currently untested. */
case AXP192_LDOIO0_ENABLE:
/* 0x02 = LDO */
tmp = 0b00000010;
return axp->write(axp->handle, AXP192_ADDRESS, reg, &tmp, 1);
break;
/* This is currently untested. */
case AXP192_LDOIO0_DISABLE:
/* 0x07 = float */
tmp = 0b00000111;
return axp->write(axp->handle, AXP192_ADDRESS, reg, &tmp, 1);
break;
case AXP192_LDO2_ENABLE:
/* This is currently untested. */
case AXP192_EXTEN_ENABLE:
axp->read(axp->handle, AXP192_ADDRESS, reg, &tmp, 1);
tmp |= 0b00000100;
return axp->write(axp->handle, AXP192_ADDRESS, reg, &tmp, 1);
break;
case AXP192_LDO2_DISABLE:
/* This is currently untested. */
case AXP192_EXTEN_DISABLE:
axp->read(axp->handle, AXP192_ADDRESS, reg, &tmp, 1);
tmp &= ~0b00000100;
return axp->write(axp->handle, AXP192_ADDRESS, reg, &tmp, 1);
break;
/* This is currently untested. */
case AXP192_GPIO2_SET_LEVEL:
axp->read(axp->handle, AXP192_ADDRESS, reg, &tmp, 1);
va_start(ap, command);
argument = (uint8_t) va_arg(ap, int);
va_end(ap);
if (argument) {
tmp |= 0b00000100;
} else {
tmp &= ~0b00000100;
}
return axp->write(axp->handle, AXP192_ADDRESS, reg, &tmp, 1);
break;
case AXP192_LDO3_ENABLE:
axp->read(axp->handle, AXP192_ADDRESS, reg, &tmp, 1);
tmp |= 0b00001000;
return axp->write(axp->handle, AXP192_ADDRESS, reg, &tmp, 1);
break;
case AXP192_LDO3_DISABLE:
axp->read(axp->handle, AXP192_ADDRESS, reg, &tmp, 1);
tmp &= ~0b00001000;
return axp->write(axp->handle, AXP192_ADDRESS, reg, &tmp, 1);
break;
/* This is currently untested. */
case AXP192_DCDC1_ENABLE:
/* This is currently untested. */
case AXP192_DCDC2_ENABLE:
axp->read(axp->handle, AXP192_ADDRESS, reg, &tmp, 1);
tmp |= 0b00000001;
return axp->write(axp->handle, AXP192_ADDRESS, reg, &tmp, 1);
break;
/* This is currently untested. */
case AXP192_DCDC1_DISABLE:
/* This is currently untested. */
case AXP192_DCDC2_DISABLE:
axp->read(axp->handle, AXP192_ADDRESS, reg, &tmp, 1);
tmp &= ~0b00000001;
return axp->write(axp->handle, AXP192_ADDRESS, reg, &tmp, 1);
break;
case AXP192_DCDC3_ENABLE:
axp->read(axp->handle, AXP192_ADDRESS, reg, &tmp, 1);
tmp |= 0b00000010;
return axp->write(axp->handle, AXP192_ADDRESS, reg, &tmp, 1);
break;
case AXP192_DCDC3_DISABLE:
axp->read(axp->handle, AXP192_ADDRESS, reg, &tmp, 1);
tmp &= ~0b00000010;
return axp->write(axp->handle, AXP192_ADDRESS, reg, &tmp, 1);
break;
case AXP192_GPIO1_SET_LEVEL:
case AXP192_GPIO4_SET_LEVEL:
axp->read(axp->handle, AXP192_ADDRESS, reg, &tmp, 1);
va_start(ap, command);
argument = (uint8_t) va_arg(ap, int);
va_end(ap);
if (argument) {
tmp |= 0b00000010;
} else {
tmp &= ~0b00000010;
}
return axp->write(axp->handle, AXP192_ADDRESS, reg, &tmp, 1);
break;
/* This is currently untested. */
case AXP192_GPIO0_SET_LEVEL:
axp->read(axp->handle, AXP192_ADDRESS, reg, &tmp, 1);
va_start(ap, command);
argument = (uint8_t) va_arg(ap, int);
va_end(ap);
if (argument) {
tmp |= 0b00000001;
} else {
tmp &= ~0b00000001;
}
return axp->write(axp->handle, AXP192_ADDRESS, reg, &tmp, 1);
break;
case AXP192_DCDC1_SET_VOLTAGE:
case AXP192_DCDC3_SET_VOLTAGE:
va_start(ap, command);
argument = (uint16_t) va_arg(ap, int);
va_end(ap);
/* 700-3500mv 25mV per step */
if ((argument < 700) || (argument > 3500)) {
return AXP192_ERROR_EINVAL;
}
tmp = (argument - 700) / 25;
return axp->write(axp->handle, AXP192_ADDRESS, reg, &tmp, 1);
break;
/* This is currently untested. */
case AXP192_DCDC2_SET_VOLTAGE:
va_start(ap, command);
argument = (uint16_t) va_arg(ap, int);
va_end(ap);
/* 700-2275mV 25mV per step */
if ((argument < 700) || (argument > 2275)) {
return AXP192_ERROR_EINVAL;
}
tmp = (argument - 700) / 25;
return axp->write(axp->handle, AXP192_ADDRESS, reg, &tmp, 1);
break;
/* This is currently untested. */
case AXP192_LDO2_SET_VOLTAGE:
va_start(ap, command);
argument = (uint16_t) va_arg(ap, int);
va_end(ap);
/* 1800-3300mV 100mV per step */
if ((argument < 1800) || (argument > 3300)) {
return AXP192_ERROR_EINVAL;
}
axp->read(axp->handle, AXP192_ADDRESS, reg, &tmp, 1);
tmp &= ~0xf0;
tmp |= (((argument - 1800) / 100) << 4);
return axp->write(axp->handle, AXP192_ADDRESS, reg, &tmp, 1);
break;
/* This is currently untested. */
case AXP192_LDO3_SET_VOLTAGE:
va_start(ap, command);
argument = (uint16_t) va_arg(ap, int);
va_end(ap);
/* 1800-3300mV 100mV per step */
if ((argument < 1800) || (argument > 3300)) {
return AXP192_ERROR_EINVAL;
}
axp->read(axp->handle, AXP192_ADDRESS, reg, &tmp, 1);
tmp &= ~0x0f;
tmp |= ((argument - 1800) / 100);
return axp->write(axp->handle, AXP192_ADDRESS, reg, &tmp, 1);
break;
/* This is currently untested. */
case AXP192_LDOIO0_SET_VOLTAGE:
va_start(ap, command);
argument = (uint16_t) va_arg(ap, int);
va_end(ap);
/* 1800-3300mV 100mV per step, 2800mV default. */
if ((argument < 1800) || (argument > 3300)) {
return AXP192_ERROR_EINVAL;
}
tmp = (((argument - 1800) / 100) << 4);
return axp->write(axp->handle, AXP192_ADDRESS, reg, &tmp, 1);
break;
}
return AXP192_ERROR_NOTTY;
}
static axp192_err_t read_coloumb_counter(const axp192_t *axp, float *buffer)
{
uint8_t tmp[4];
int32_t coin, coout;
axp192_err_t status;
status = axp->read(axp->handle, AXP192_ADDRESS, AXP192_CHARGE_COULOMB, tmp, sizeof(coin));
if (AXP192_OK != status) {
return status;
}
coin = (tmp[0] << 24) + (tmp[1] << 16) + (tmp[2] << 8) + tmp[3];
status = axp->read(axp->handle, AXP192_ADDRESS, AXP192_DISCHARGE_COULOMB, tmp, sizeof(coout));
if (AXP192_OK != status) {
return status;
}
coout = (tmp[0] << 24) + (tmp[1] << 16) + (tmp[2] << 8) + tmp[3];
/* CmAh = 65536 * 0.5mA *coin - cout) / 3600 / ADC sample rate */
*buffer = 32768 * (coin - coout) / 3600 / 25;
return AXP192_OK;
}
static axp192_err_t read_battery_power(const axp192_t *axp, float *buffer)
{
uint8_t tmp[4];
float sensitivity;
axp192_err_t status;
/* 1.1mV * 0.5mA per LSB */
sensitivity = 1.1 * 0.5 / 1000;
status = axp->read(axp->handle, AXP192_ADDRESS, AXP192_BATTERY_POWER, tmp, 3);
if (AXP192_OK != status) {
return status;
}
*buffer = (((tmp[0] << 16) + (tmp[1] << 8) + tmp[2]) * sensitivity);
return AXP192_OK;
}