mirror of
https://github.com/ByteWelder/Tactility.git
synced 2026-10-17 02:45:05 +00:00
IMU Updates (#694)
* IMU Updates Updated BMI270 and MPU6886 with the mount-matrix. Updated devices i can test with the mount-matrix. papers3, stackchan (cores3), sticks3, tab5, waveshare-s3-lcd-13, waveshare-s3-lcd-128 Devices not changed and not tested because i don't own them are: MPU6886 - core2, stickc-plus2. BMI270 - cardputer-adv * Update device.properties * No USB here! CH343P
This commit is contained in:
parent
6a3c549324
commit
d0ad1b014c
@ -65,7 +65,9 @@
|
|||||||
bmi270 {
|
bmi270 {
|
||||||
compatible = "bosch,bmi270";
|
compatible = "bosch,bmi270";
|
||||||
reg = <0x69>;
|
reg = <0x69>;
|
||||||
invert-x;
|
mount-matrix = <1 0 0
|
||||||
|
0 1 0
|
||||||
|
0 0 1>;
|
||||||
};
|
};
|
||||||
|
|
||||||
bm8563 {
|
bm8563 {
|
||||||
|
|||||||
@ -44,6 +44,9 @@
|
|||||||
bmi270 {
|
bmi270 {
|
||||||
compatible = "bosch,bmi270";
|
compatible = "bosch,bmi270";
|
||||||
reg = <0x68>;
|
reg = <0x68>;
|
||||||
|
mount-matrix = <0 1 0
|
||||||
|
1 0 0
|
||||||
|
0 0 -1>;
|
||||||
};
|
};
|
||||||
|
|
||||||
bm8563 {
|
bm8563 {
|
||||||
|
|||||||
@ -70,7 +70,9 @@
|
|||||||
bmi270 {
|
bmi270 {
|
||||||
compatible = "bosch,bmi270";
|
compatible = "bosch,bmi270";
|
||||||
reg = <0x69>;
|
reg = <0x69>;
|
||||||
invert-x;
|
mount-matrix = <1 0 0
|
||||||
|
0 1 0
|
||||||
|
0 0 1>;
|
||||||
};
|
};
|
||||||
|
|
||||||
bm8563 {
|
bm8563 {
|
||||||
|
|||||||
@ -63,6 +63,9 @@
|
|||||||
bmi270 {
|
bmi270 {
|
||||||
compatible = "bosch,bmi270";
|
compatible = "bosch,bmi270";
|
||||||
reg = <0x68>;
|
reg = <0x68>;
|
||||||
|
mount-matrix = <0 1 0
|
||||||
|
-1 0 0
|
||||||
|
0 0 1>;
|
||||||
};
|
};
|
||||||
|
|
||||||
es8311 {
|
es8311 {
|
||||||
|
|||||||
@ -83,6 +83,9 @@
|
|||||||
bmi270 {
|
bmi270 {
|
||||||
compatible = "bosch,bmi270";
|
compatible = "bosch,bmi270";
|
||||||
reg = <0x68>;
|
reg = <0x68>;
|
||||||
|
mount-matrix = <-1 0 0
|
||||||
|
0 1 0
|
||||||
|
0 0 -1>;
|
||||||
};
|
};
|
||||||
|
|
||||||
es8388 {
|
es8388 {
|
||||||
|
|||||||
@ -8,7 +8,6 @@ hardware.flashSize=16MB
|
|||||||
hardware.spiRam=true
|
hardware.spiRam=true
|
||||||
hardware.spiRamMode=OCT
|
hardware.spiRamMode=OCT
|
||||||
hardware.spiRamSpeed=120M
|
hardware.spiRamSpeed=120M
|
||||||
hardware.tinyUsbMsc=true
|
|
||||||
hardware.esptoolFlashFreq=120M
|
hardware.esptoolFlashFreq=120M
|
||||||
hardware.bluetooth=true
|
hardware.bluetooth=true
|
||||||
|
|
||||||
|
|||||||
@ -9,7 +9,6 @@
|
|||||||
#include <bindings/qmi8658.h>
|
#include <bindings/qmi8658.h>
|
||||||
#include <tactility/bindings/esp32_sdspi.h>
|
#include <tactility/bindings/esp32_sdspi.h>
|
||||||
#include <tactility/bindings/esp32_pwm_ledc.h>
|
#include <tactility/bindings/esp32_pwm_ledc.h>
|
||||||
#include <tactility/bindings/esp32_usbdevice.h>
|
|
||||||
#include <tactility/bindings/pwm_backlight.h>
|
#include <tactility/bindings/pwm_backlight.h>
|
||||||
#include <bindings/st7789.h>
|
#include <bindings/st7789.h>
|
||||||
|
|
||||||
@ -41,6 +40,9 @@
|
|||||||
qmi8658 {
|
qmi8658 {
|
||||||
compatible = "qst,qmi8658";
|
compatible = "qst,qmi8658";
|
||||||
reg = <0x6B>;
|
reg = <0x6B>;
|
||||||
|
mount-matrix = <-1 0 0
|
||||||
|
0 -1 0
|
||||||
|
0 0 -1>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -89,12 +91,4 @@
|
|||||||
frequency-khz = <20000>;
|
frequency-khz = <20000>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
usbdevice0 {
|
|
||||||
compatible = "espressif,esp32-usbdevice";
|
|
||||||
|
|
||||||
usbdevicemsc0 {
|
|
||||||
compatible = "espressif,esp32-usbdevice-msc";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -9,7 +9,6 @@ hardware.spiRam=true
|
|||||||
hardware.spiRamMode=QUAD
|
hardware.spiRamMode=QUAD
|
||||||
hardware.spiRamSpeed=120M
|
hardware.spiRamSpeed=120M
|
||||||
hardware.spiRamXipDisabled=true
|
hardware.spiRamXipDisabled=true
|
||||||
hardware.tinyUsbMsc=true
|
|
||||||
hardware.esptoolFlashFreq=120M
|
hardware.esptoolFlashFreq=120M
|
||||||
hardware.bluetooth=true
|
hardware.bluetooth=true
|
||||||
|
|
||||||
|
|||||||
@ -9,7 +9,6 @@
|
|||||||
#include <bindings/qmi8658.h>
|
#include <bindings/qmi8658.h>
|
||||||
#include <tactility/bindings/esp32_sdspi.h>
|
#include <tactility/bindings/esp32_sdspi.h>
|
||||||
#include <tactility/bindings/esp32_pwm_ledc.h>
|
#include <tactility/bindings/esp32_pwm_ledc.h>
|
||||||
#include <tactility/bindings/esp32_usbdevice.h>
|
|
||||||
#include <tactility/bindings/pwm_backlight.h>
|
#include <tactility/bindings/pwm_backlight.h>
|
||||||
#include <bindings/cst816t.h>
|
#include <bindings/cst816t.h>
|
||||||
#include <bindings/gc9a01.h>
|
#include <bindings/gc9a01.h>
|
||||||
@ -42,6 +41,9 @@
|
|||||||
qmi8658 {
|
qmi8658 {
|
||||||
compatible = "qst,qmi8658";
|
compatible = "qst,qmi8658";
|
||||||
reg = <0x6B>;
|
reg = <0x6B>;
|
||||||
|
mount-matrix = <0 1 0
|
||||||
|
1 0 0
|
||||||
|
0 0 1>;
|
||||||
};
|
};
|
||||||
|
|
||||||
touch0 {
|
touch0 {
|
||||||
@ -93,12 +95,4 @@
|
|||||||
backlight = <&display_backlight>;
|
backlight = <&display_backlight>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
usbdevice0 {
|
|
||||||
compatible = "espressif,esp32-usbdevice";
|
|
||||||
|
|
||||||
usbdevicemsc0 {
|
|
||||||
compatible = "espressif,esp32-usbdevice-msc";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -5,27 +5,11 @@ include: [ "i2c-device.yaml" ]
|
|||||||
compatible: "bosch,bmi270"
|
compatible: "bosch,bmi270"
|
||||||
|
|
||||||
properties:
|
properties:
|
||||||
swap-xy:
|
mount-matrix:
|
||||||
type: boolean
|
type: values
|
||||||
default: false
|
default: [1, 0, 0, 0, 1, 0, 0, 0, 1]
|
||||||
description: Swap the X and Y axes
|
description: >
|
||||||
swap-xz:
|
Row-major 3x3 rotation matrix that maps the sensor frame onto the device frame, following the
|
||||||
type: boolean
|
Linux IIO "mount-matrix" convention (x' = m[0]*x + m[1]*y + m[2]*z, and so on). It is applied
|
||||||
default: false
|
to the accelerometer and gyroscope readings so consumers always see the device frame,
|
||||||
description: Swap the X and Z axes
|
regardless of how the IMU is physically oriented on the PCB. Defaults to the identity matrix.
|
||||||
swap-yz:
|
|
||||||
type: boolean
|
|
||||||
default: false
|
|
||||||
description: Swap the Y and Z axes
|
|
||||||
invert-x:
|
|
||||||
type: boolean
|
|
||||||
default: false
|
|
||||||
description: Invert the X axis
|
|
||||||
invert-y:
|
|
||||||
type: boolean
|
|
||||||
default: false
|
|
||||||
description: Invert the Y axis
|
|
||||||
invert-z:
|
|
||||||
type: boolean
|
|
||||||
default: false
|
|
||||||
description: Invert the Z axis
|
|
||||||
|
|||||||
@ -14,12 +14,12 @@ extern "C" {
|
|||||||
struct Bmi270Config {
|
struct Bmi270Config {
|
||||||
/** Address on bus */
|
/** Address on bus */
|
||||||
uint8_t address;
|
uint8_t address;
|
||||||
bool swap_xy;
|
/**
|
||||||
bool swap_xz;
|
* Row-major 3x3 rotation matrix mapping the sensor frame onto the device frame (Linux IIO
|
||||||
bool swap_yz;
|
* "mount-matrix" convention: `out[i] = m[i * 3] * x + m[i * 3 + 1] * y + m[i * 3 + 2] * z`).
|
||||||
bool invert_x;
|
* Identity means the sensor frame already matches the device frame.
|
||||||
bool invert_y;
|
*/
|
||||||
bool invert_z;
|
int32_t mount_matrix[9];
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -5,8 +5,6 @@
|
|||||||
#include <tactility/drivers/i2c_controller.h>
|
#include <tactility/drivers/i2c_controller.h>
|
||||||
#include <tactility/log.h>
|
#include <tactility/log.h>
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
#define TAG "BMI270"
|
#define TAG "BMI270"
|
||||||
|
|
||||||
static constexpr uint8_t REG_CHIP_ID = 0x00; // read: expect 0x24
|
static constexpr uint8_t REG_CHIP_ID = 0x00; // read: expect 0x24
|
||||||
@ -90,6 +88,18 @@ static bool configure(Device* i2c_controller, uint8_t address) {
|
|||||||
|
|
||||||
// region Driver lifecycle
|
// region Driver lifecycle
|
||||||
|
|
||||||
|
// Applies the devicetree mount matrix (sensor frame -> device frame) to a 3-axis reading.
|
||||||
|
// Flat row-major [9]: x' = m0*x + m1*y + m2*z, y' = m3*x + m4*y + m5*z, z' = m6*x + m7*y + m8*z.
|
||||||
|
static void applyMountMatrix(const Bmi270Config& config, float& x, float& y, float& z) {
|
||||||
|
const int32_t* m = config.mount_matrix;
|
||||||
|
const float rx = m[0] * x + m[1] * y + m[2] * z;
|
||||||
|
const float ry = m[3] * x + m[4] * y + m[5] * z;
|
||||||
|
const float rz = m[6] * x + m[7] * y + m[8] * z;
|
||||||
|
x = rx;
|
||||||
|
y = ry;
|
||||||
|
z = rz;
|
||||||
|
}
|
||||||
|
|
||||||
static error_t start(Device* device) {
|
static error_t start(Device* device) {
|
||||||
auto* i2c_controller = device_get_parent(device);
|
auto* i2c_controller = device_get_parent(device);
|
||||||
if (device_get_type(i2c_controller) != &I2C_CONTROLLER_TYPE) {
|
if (device_get_type(i2c_controller) != &I2C_CONTROLLER_TYPE) {
|
||||||
@ -171,13 +181,7 @@ error_t bmi270_read_accel(Device* device, ImuAccelData* data) {
|
|||||||
toI16(buffer[2], buffer[3]) * ACCEL_SCALE,
|
toI16(buffer[2], buffer[3]) * ACCEL_SCALE,
|
||||||
toI16(buffer[4], buffer[5]) * ACCEL_SCALE,
|
toI16(buffer[4], buffer[5]) * ACCEL_SCALE,
|
||||||
};
|
};
|
||||||
const auto& config = *GET_CONFIG(device);
|
applyMountMatrix(*GET_CONFIG(device), axes[0], axes[1], axes[2]);
|
||||||
if (config.swap_xy) std::swap(axes[0], axes[1]);
|
|
||||||
if (config.swap_xz) std::swap(axes[0], axes[2]);
|
|
||||||
if (config.swap_yz) std::swap(axes[1], axes[2]);
|
|
||||||
if (config.invert_x) axes[0] = -axes[0];
|
|
||||||
if (config.invert_y) axes[1] = -axes[1];
|
|
||||||
if (config.invert_z) axes[2] = -axes[2];
|
|
||||||
data->ax = axes[0];
|
data->ax = axes[0];
|
||||||
data->ay = axes[1];
|
data->ay = axes[1];
|
||||||
data->az = axes[2];
|
data->az = axes[2];
|
||||||
@ -197,9 +201,13 @@ error_t bmi270_read_gyro(Device* device, ImuGyroData* data) {
|
|||||||
return static_cast<int16_t>(static_cast<uint16_t>(hi) << 8 | lo);
|
return static_cast<int16_t>(static_cast<uint16_t>(hi) << 8 | lo);
|
||||||
};
|
};
|
||||||
|
|
||||||
data->gx = toI16(buffer[0], buffer[1]) * GYRO_SCALE;
|
float gx = toI16(buffer[0], buffer[1]) * GYRO_SCALE;
|
||||||
data->gy = toI16(buffer[2], buffer[3]) * GYRO_SCALE;
|
float gy = toI16(buffer[2], buffer[3]) * GYRO_SCALE;
|
||||||
data->gz = toI16(buffer[4], buffer[5]) * GYRO_SCALE;
|
float gz = toI16(buffer[4], buffer[5]) * GYRO_SCALE;
|
||||||
|
applyMountMatrix(*GET_CONFIG(device), gx, gy, gz);
|
||||||
|
data->gx = gx;
|
||||||
|
data->gy = gy;
|
||||||
|
data->gz = gz;
|
||||||
|
|
||||||
return ERROR_NONE;
|
return ERROR_NONE;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,27 +5,11 @@ include: [ "i2c-device.yaml" ]
|
|||||||
compatible: "invensense,mpu6886"
|
compatible: "invensense,mpu6886"
|
||||||
|
|
||||||
properties:
|
properties:
|
||||||
swap-xy:
|
mount-matrix:
|
||||||
type: boolean
|
type: values
|
||||||
default: false
|
default: [1, 0, 0, 0, 1, 0, 0, 0, 1]
|
||||||
description: Swap the X and Y axes
|
description: >
|
||||||
swap-xz:
|
Row-major 3x3 rotation matrix that maps the sensor frame onto the device frame, following the
|
||||||
type: boolean
|
Linux IIO "mount-matrix" convention (x' = m[0]*x + m[1]*y + m[2]*z, and so on). It is applied
|
||||||
default: false
|
to the accelerometer and gyroscope readings so consumers always see the device frame,
|
||||||
description: Swap the X and Z axes
|
regardless of how the IMU is physically oriented on the PCB. Defaults to the identity matrix.
|
||||||
swap-yz:
|
|
||||||
type: boolean
|
|
||||||
default: false
|
|
||||||
description: Swap the Y and Z axes
|
|
||||||
invert-x:
|
|
||||||
type: boolean
|
|
||||||
default: false
|
|
||||||
description: Invert the X axis
|
|
||||||
invert-y:
|
|
||||||
type: boolean
|
|
||||||
default: false
|
|
||||||
description: Invert the Y axis
|
|
||||||
invert-z:
|
|
||||||
type: boolean
|
|
||||||
default: false
|
|
||||||
description: Invert the Z axis
|
|
||||||
|
|||||||
@ -14,12 +14,12 @@ extern "C" {
|
|||||||
struct Mpu6886Config {
|
struct Mpu6886Config {
|
||||||
/** Address on bus */
|
/** Address on bus */
|
||||||
uint8_t address;
|
uint8_t address;
|
||||||
bool swap_xy;
|
/**
|
||||||
bool swap_xz;
|
* Row-major 3x3 rotation matrix mapping the sensor frame onto the device frame (Linux IIO
|
||||||
bool swap_yz;
|
* "mount-matrix" convention: `out[i] = m[i * 3] * x + m[i * 3 + 1] * y + m[i * 3 + 2] * z`).
|
||||||
bool invert_x;
|
* Identity means the sensor frame already matches the device frame.
|
||||||
bool invert_y;
|
*/
|
||||||
bool invert_z;
|
int32_t mount_matrix[9];
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -4,8 +4,6 @@
|
|||||||
#include <tactility/drivers/i2c_controller.h>
|
#include <tactility/drivers/i2c_controller.h>
|
||||||
#include <tactility/log.h>
|
#include <tactility/log.h>
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
#define TAG "MPU6886"
|
#define TAG "MPU6886"
|
||||||
|
|
||||||
// Register map
|
// Register map
|
||||||
@ -48,6 +46,18 @@ static constexpr TickType_t I2C_TIMEOUT_TICKS = pdMS_TO_TICKS(10);
|
|||||||
|
|
||||||
// region Driver lifecycle
|
// region Driver lifecycle
|
||||||
|
|
||||||
|
// Applies the devicetree mount matrix (sensor frame -> device frame) to a 3-axis reading.
|
||||||
|
// Flat row-major [9]: x' = m0*x + m1*y + m2*z, y' = m3*x + m4*y + m5*z, z' = m6*x + m7*y + m8*z.
|
||||||
|
static void applyMountMatrix(const Mpu6886Config& config, float& x, float& y, float& z) {
|
||||||
|
const int32_t* m = config.mount_matrix;
|
||||||
|
const float rx = m[0] * x + m[1] * y + m[2] * z;
|
||||||
|
const float ry = m[3] * x + m[4] * y + m[5] * z;
|
||||||
|
const float rz = m[6] * x + m[7] * y + m[8] * z;
|
||||||
|
x = rx;
|
||||||
|
y = ry;
|
||||||
|
z = rz;
|
||||||
|
}
|
||||||
|
|
||||||
static error_t start(Device* device) {
|
static error_t start(Device* device) {
|
||||||
auto* i2c_controller = device_get_parent(device);
|
auto* i2c_controller = device_get_parent(device);
|
||||||
if (device_get_type(i2c_controller) != &I2C_CONTROLLER_TYPE) {
|
if (device_get_type(i2c_controller) != &I2C_CONTROLLER_TYPE) {
|
||||||
@ -145,13 +155,7 @@ error_t mpu6886_read_accel(Device* device, ImuAccelData* data) {
|
|||||||
toI16(buf[2], buf[3]) * ACCEL_SCALE,
|
toI16(buf[2], buf[3]) * ACCEL_SCALE,
|
||||||
toI16(buf[4], buf[5]) * ACCEL_SCALE,
|
toI16(buf[4], buf[5]) * ACCEL_SCALE,
|
||||||
};
|
};
|
||||||
const auto& config = *GET_CONFIG(device);
|
applyMountMatrix(*GET_CONFIG(device), axes[0], axes[1], axes[2]);
|
||||||
if (config.swap_xy) std::swap(axes[0], axes[1]);
|
|
||||||
if (config.swap_xz) std::swap(axes[0], axes[2]);
|
|
||||||
if (config.swap_yz) std::swap(axes[1], axes[2]);
|
|
||||||
if (config.invert_x) axes[0] = -axes[0];
|
|
||||||
if (config.invert_y) axes[1] = -axes[1];
|
|
||||||
if (config.invert_z) axes[2] = -axes[2];
|
|
||||||
data->ax = axes[0];
|
data->ax = axes[0];
|
||||||
data->ay = axes[1];
|
data->ay = axes[1];
|
||||||
data->az = axes[2];
|
data->az = axes[2];
|
||||||
@ -171,9 +175,13 @@ error_t mpu6886_read_gyro(Device* device, ImuGyroData* data) {
|
|||||||
error_t error = i2c_controller_read_register(i2c_controller, address, REG_GYRO_XOUT_H, buf, sizeof(buf), I2C_TIMEOUT_TICKS);
|
error_t error = i2c_controller_read_register(i2c_controller, address, REG_GYRO_XOUT_H, buf, sizeof(buf), I2C_TIMEOUT_TICKS);
|
||||||
if (error != ERROR_NONE) return error;
|
if (error != ERROR_NONE) return error;
|
||||||
|
|
||||||
data->gx = toI16(buf[0], buf[1]) * GYRO_SCALE;
|
float gx = toI16(buf[0], buf[1]) * GYRO_SCALE;
|
||||||
data->gy = toI16(buf[2], buf[3]) * GYRO_SCALE;
|
float gy = toI16(buf[2], buf[3]) * GYRO_SCALE;
|
||||||
data->gz = toI16(buf[4], buf[5]) * GYRO_SCALE;
|
float gz = toI16(buf[4], buf[5]) * GYRO_SCALE;
|
||||||
|
applyMountMatrix(*GET_CONFIG(device), gx, gy, gz);
|
||||||
|
data->gx = gx;
|
||||||
|
data->gy = gy;
|
||||||
|
data->gz = gz;
|
||||||
|
|
||||||
return ERROR_NONE;
|
return ERROR_NONE;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -343,6 +343,10 @@ const struct ModuleSymbol lvgl_module_symbols[] = {
|
|||||||
DEFINE_MODULE_SYMBOL(lv_obj_set_style_height),
|
DEFINE_MODULE_SYMBOL(lv_obj_set_style_height),
|
||||||
DEFINE_MODULE_SYMBOL(lv_obj_set_style_clip_corner),
|
DEFINE_MODULE_SYMBOL(lv_obj_set_style_clip_corner),
|
||||||
DEFINE_MODULE_SYMBOL(lv_obj_set_style_shadow_color),
|
DEFINE_MODULE_SYMBOL(lv_obj_set_style_shadow_color),
|
||||||
|
DEFINE_MODULE_SYMBOL(lv_obj_set_parent),
|
||||||
|
DEFINE_MODULE_SYMBOL(lv_obj_set_style_bg_image_tiled),
|
||||||
|
DEFINE_MODULE_SYMBOL(lv_obj_set_style_image_recolor),
|
||||||
|
DEFINE_MODULE_SYMBOL(lv_obj_set_style_image_opa),
|
||||||
// lv_font
|
// lv_font
|
||||||
DEFINE_MODULE_SYMBOL(lv_font_get_bitmap_fmt_txt),
|
DEFINE_MODULE_SYMBOL(lv_font_get_bitmap_fmt_txt),
|
||||||
DEFINE_MODULE_SYMBOL(lv_font_get_default),
|
DEFINE_MODULE_SYMBOL(lv_font_get_default),
|
||||||
|
|||||||
@ -84,7 +84,7 @@ struct Context {
|
|||||||
lv_obj_t* timeoutValueLabel = nullptr;
|
lv_obj_t* timeoutValueLabel = nullptr;
|
||||||
lv_obj_t* screensaverValueLabel = nullptr;
|
lv_obj_t* screensaverValueLabel = nullptr;
|
||||||
// Shown when auto-rotate is off or unavailable: the orientation label and chips
|
// Shown when auto-rotate is off or unavailable: the orientation label and chips
|
||||||
lv_obj_t* orientationGroup = nullptr;
|
lv_obj_t* orientationCard = nullptr;
|
||||||
lv_obj_t* orientationChips = nullptr;
|
lv_obj_t* orientationChips = nullptr;
|
||||||
lv_obj_t* autoRotateSwitch = nullptr;
|
lv_obj_t* autoRotateSwitch = nullptr;
|
||||||
};
|
};
|
||||||
@ -151,10 +151,10 @@ void setCheckedChip(lv_obj_t* chips, settings::display::Orientation orientation)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Shows or hides the manual orientation chips, depending on whether auto-rotate is on */
|
/** Shows or hides the manual orientation card, depending on whether auto-rotate is on */
|
||||||
void updateOrientationVisibility(Context* ctx) {
|
void updateOrientationVisibility(Context* ctx) {
|
||||||
const bool auto_rotate = ctx->autoRotateSwitch != nullptr && ctx->displaySettings.autoRotateEnabled;
|
const bool auto_rotate = ctx->autoRotateSwitch != nullptr && ctx->displaySettings.autoRotateEnabled;
|
||||||
lv_obj_set_hidden(ctx->orientationGroup, auto_rotate);
|
lv_obj_set_hidden(ctx->orientationCard, auto_rotate);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onOrientationChipPressed(lv_event_t* event) {
|
void onOrientationChipPressed(lv_event_t* event) {
|
||||||
@ -396,12 +396,12 @@ void createWidgets(lv_obj_t* parent, void* userData) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The card shows the manual orientation; auto-rotate hides it while it drives the rotation
|
// The card shows the manual orientation; auto-rotate hides it while it drives the rotation
|
||||||
auto* orientation_card = lvgl_card_create(main_wrapper);
|
ctx->orientationCard = lvgl_card_create(main_wrapper);
|
||||||
lv_obj_set_size(orientation_card, LV_PCT(100), LV_SIZE_CONTENT);
|
lv_obj_set_size(ctx->orientationCard, LV_PCT(100), LV_SIZE_CONTENT);
|
||||||
lv_obj_set_flex_flow(orientation_card, LV_FLEX_FLOW_COLUMN);
|
lv_obj_set_flex_flow(ctx->orientationCard, LV_FLEX_FLOW_COLUMN);
|
||||||
|
|
||||||
ctx->orientationGroup = createCardGroup(orientation_card, "Orientation");
|
auto* orientation_group = createCardGroup(ctx->orientationCard, "Orientation");
|
||||||
ctx->orientationChips = createOrientationChips(ctx->orientationGroup, ctx->displaySettings.orientation, onOrientationChipPressed, ctx);
|
ctx->orientationChips = createOrientationChips(orientation_group, ctx->displaySettings.orientation, onOrientationChipPressed, ctx);
|
||||||
|
|
||||||
updateOrientationVisibility(ctx);
|
updateOrientationVisibility(ctx);
|
||||||
|
|
||||||
@ -447,7 +447,7 @@ void destroyWidgets(void* userData) {
|
|||||||
ctx->timeoutCard = nullptr;
|
ctx->timeoutCard = nullptr;
|
||||||
ctx->timeoutValueLabel = nullptr;
|
ctx->timeoutValueLabel = nullptr;
|
||||||
ctx->screensaverValueLabel = nullptr;
|
ctx->screensaverValueLabel = nullptr;
|
||||||
ctx->orientationGroup = nullptr;
|
ctx->orientationCard = nullptr;
|
||||||
ctx->orientationChips = nullptr;
|
ctx->orientationChips = nullptr;
|
||||||
ctx->autoRotateSwitch = nullptr;
|
ctx->autoRotateSwitch = nullptr;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,10 @@ namespace tt::service::autorotate {
|
|||||||
constexpr float FLAT_THRESHOLD = 0.45F; // gravity in the screen plane below this = lying flat
|
constexpr float FLAT_THRESHOLD = 0.45F; // gravity in the screen plane below this = lying flat
|
||||||
constexpr int DEBOUNCE = 5; // consecutive readings before switching
|
constexpr int DEBOUNCE = 5; // consecutive readings before switching
|
||||||
|
|
||||||
// gx toward +Y -> rotation 0; each +90 degrees of gx/gy angle is one more quarter-turn.
|
// ax/ay is the accelerometer support-force vector (points up, opposite gravity).
|
||||||
|
// The UI top should point along (ax,ay): +Y -> rotation 0.
|
||||||
|
// LVGL ROT90 is 90 deg counter-clockwise (UI top -> device left, -X), ROT270 is
|
||||||
|
// 90 deg clockwise (UI top -> device right, +X), so +X maps to 270, not 90.
|
||||||
static lv_display_rotation_t gravityToRotation(float gx, float gy, lv_display_rotation_t fallback) {
|
static lv_display_rotation_t gravityToRotation(float gx, float gy, lv_display_rotation_t fallback) {
|
||||||
float plane = sqrtf((gx * gx) + (gy * gy));
|
float plane = sqrtf((gx * gx) + (gy * gy));
|
||||||
if (plane < FLAT_THRESHOLD) {
|
if (plane < FLAT_THRESHOLD) {
|
||||||
@ -22,9 +25,9 @@ static lv_display_rotation_t gravityToRotation(float gx, float gy, lv_display_ro
|
|||||||
int idx = (int)((ang + 45.0F) / 90.0F) % 4;
|
int idx = (int)((ang + 45.0F) / 90.0F) % 4;
|
||||||
static constexpr lv_display_rotation_t map[4] = {
|
static constexpr lv_display_rotation_t map[4] = {
|
||||||
LV_DISPLAY_ROTATION_0,
|
LV_DISPLAY_ROTATION_0,
|
||||||
LV_DISPLAY_ROTATION_90,
|
|
||||||
LV_DISPLAY_ROTATION_180,
|
|
||||||
LV_DISPLAY_ROTATION_270,
|
LV_DISPLAY_ROTATION_270,
|
||||||
|
LV_DISPLAY_ROTATION_180,
|
||||||
|
LV_DISPLAY_ROTATION_90,
|
||||||
};
|
};
|
||||||
return map[idx];
|
return map[idx];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,7 +9,7 @@ TEST_CASE("a flat reading below the threshold keeps the current rotation") {
|
|||||||
CHECK_EQ(math.update(0.1F, 0.1F), LV_DISPLAY_ROTATION_0);
|
CHECK_EQ(math.update(0.1F, 0.1F), LV_DISPLAY_ROTATION_0);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("gravity toward +X classifies as rotation 90 when no debounce is needed yet") {
|
TEST_CASE("accel toward +X classifies as rotation 270 when no debounce is needed yet") {
|
||||||
OrientationMath math;
|
OrientationMath math;
|
||||||
math.reset(LV_DISPLAY_ROTATION_0);
|
math.reset(LV_DISPLAY_ROTATION_0);
|
||||||
// A single reading only sets the debounce target - the previous rotation is still returned.
|
// A single reading only sets the debounce target - the previous rotation is still returned.
|
||||||
@ -27,32 +27,34 @@ TEST_CASE("a new rotation is only applied after enough consecutive stable readin
|
|||||||
CHECK_EQ(last, LV_DISPLAY_ROTATION_0); // not yet debounced
|
CHECK_EQ(last, LV_DISPLAY_ROTATION_0); // not yet debounced
|
||||||
}
|
}
|
||||||
last = math.update(1.0F, 0.0F);
|
last = math.update(1.0F, 0.0F);
|
||||||
CHECK_EQ(last, LV_DISPLAY_ROTATION_90); // 6th call (5th consecutive stable reading) switches
|
CHECK_EQ(last, LV_DISPLAY_ROTATION_270); // 6th call (5th consecutive stable reading) switches
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("all four gravity directions map to the expected rotation") {
|
TEST_CASE("all four accel directions map to the expected rotation") {
|
||||||
OrientationMath math;
|
OrientationMath math;
|
||||||
math.reset(LV_DISPLAY_ROTATION_0);
|
math.reset(LV_DISPLAY_ROTATION_0);
|
||||||
|
|
||||||
auto settle = [&](float gx, float gy) {
|
auto settle = [&](float ax, float ay) {
|
||||||
lv_display_rotation_t result = LV_DISPLAY_ROTATION_0;
|
lv_display_rotation_t result = LV_DISPLAY_ROTATION_0;
|
||||||
for (int i = 0; i < 6; i++) {
|
for (int i = 0; i < 6; i++) {
|
||||||
result = math.update(gx, gy);
|
result = math.update(ax, ay);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Accel points up (opposite gravity); UI top follows it.
|
||||||
|
// LVGL ROT90 is CCW (top -> -X), ROT270 is CW (top -> +X).
|
||||||
CHECK_EQ(settle(0.0F, 1.0F), LV_DISPLAY_ROTATION_0);
|
CHECK_EQ(settle(0.0F, 1.0F), LV_DISPLAY_ROTATION_0);
|
||||||
CHECK_EQ(settle(1.0F, 0.0F), LV_DISPLAY_ROTATION_90);
|
CHECK_EQ(settle(1.0F, 0.0F), LV_DISPLAY_ROTATION_270);
|
||||||
CHECK_EQ(settle(0.0F, -1.0F), LV_DISPLAY_ROTATION_180);
|
CHECK_EQ(settle(0.0F, -1.0F), LV_DISPLAY_ROTATION_180);
|
||||||
CHECK_EQ(settle(-1.0F, 0.0F), LV_DISPLAY_ROTATION_270);
|
CHECK_EQ(settle(-1.0F, 0.0F), LV_DISPLAY_ROTATION_90);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("an unstable target resets the debounce counter") {
|
TEST_CASE("an unstable target resets the debounce counter") {
|
||||||
OrientationMath math;
|
OrientationMath math;
|
||||||
math.reset(LV_DISPLAY_ROTATION_0);
|
math.reset(LV_DISPLAY_ROTATION_0);
|
||||||
|
|
||||||
// Get close to switching to rotation 90...
|
// Get close to switching to rotation 270...
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
math.update(1.0F, 0.0F);
|
math.update(1.0F, 0.0F);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user