Compare commits

...

2 Commits

Author SHA1 Message Date
Ken Van Hoeylandt
75be23eca2 Basic CPU affinity configuration 2025-08-19 23:45:40 +02:00
Ken Van Hoeylandt
c809cea6c8 Cleanup/improvements of Boot.cpp 2025-08-19 22:15:39 +02:00
10 changed files with 173 additions and 46 deletions

View File

@ -13,7 +13,6 @@
- Mutex: Implement give/take from ISR support (works only for non-recursive ones)
- Extend unPhone power driver: add charging status, usb connection status, etc.
- Expose app::Paths to TactilityC
- Make a ledger for setting CPU affinity of various services and tasks
- CrashHandler: use "corrupted" flag
- CrashHandler: process other types of crashes (WDT?)
- Call tt::lvgl::isSyncSet after HAL init and show an error (and crash?) when it is not set.

View File

@ -11,6 +11,7 @@
#include <Tactility/kernel/SystemEvents.h>
#include <lvgl.h>
#include <Tactility/CpuAffinity.h>
#ifdef ESP_PLATFORM
#include "Tactility/app/crashdiagnostics/CrashDiagnostics.h"
@ -24,26 +25,25 @@
namespace tt::app::boot {
static std::shared_ptr<tt::hal::display::DisplayDevice> getHalDisplay() {
static std::shared_ptr<hal::display::DisplayDevice> getHalDisplay() {
return hal::findFirstDevice<hal::display::DisplayDevice>(hal::Device::Type::Display);
}
class BootApp : public App {
private:
Thread thread = Thread(
"boot",
4096,
[] { return bootThreadCallback(); },
getCpuAffinityConfiguration().system
);
Thread thread = Thread("boot", 4096, [this]() { return bootThreadCallback(); });
int32_t bootThreadCallback() {
TickType_t start_time = kernel::getTicks();
kernel::publishSystemEvent(kernel::SystemEvent::BootSplash);
auto hal_display = getHalDisplay();
static void setupDisplay() {
const auto hal_display = getHalDisplay();
assert(hal_display != nullptr);
if (hal_display->supportsBacklightDuty()) {
uint8_t backlight_duty = 200;
app::display::getBacklightDuty(backlight_duty);
display::getBacklightDuty(backlight_duty);
TT_LOG_I(TAG, "backlight %du", backlight_duty);
hal_display->setBacklightDuty(backlight_duty);
} else {
@ -52,27 +52,45 @@ private:
if (hal_display->getGammaCurveCount() > 0) {
uint8_t gamma_curve;
if (app::display::getGammaCurve(gamma_curve)) {
if (display::getGammaCurve(gamma_curve)) {
hal_display->setGammaCurve(gamma_curve);
TT_LOG_I(TAG, "gamma %du", gamma_curve);
}
}
}
if (hal::usb::isUsbBootMode()) {
TT_LOG_I(TAG, "Rebooting into mass storage device mode");
hal::usb::resetUsbBootMode();
hal::usb::startMassStorageWithSdmmc();
} else {
static bool setupUsbBootMode() {
if (!hal::usb::isUsbBootMode()) {
return false;
}
TT_LOG_I(TAG, "Rebooting into mass storage device mode");
hal::usb::resetUsbBootMode();
hal::usb::startMassStorageWithSdmmc();
return true;
}
static void waitForMinimalSplashDuration(TickType_t startTime) {
const auto end_time = kernel::getTicks();
const auto ticks_passed = end_time - startTime;
constexpr auto minimum_ticks = (CONFIG_TT_SPLASH_DURATION / portTICK_PERIOD_MS);
if (minimum_ticks > ticks_passed) {
kernel::delayTicks(minimum_ticks - ticks_passed);
}
}
static int32_t bootThreadCallback() {
const auto start_time = kernel::getTicks();
kernel::publishSystemEvent(kernel::SystemEvent::BootSplash);
setupDisplay();
if (!setupUsbBootMode()) {
initFromBootApp();
TickType_t end_time = tt::kernel::getTicks();
TickType_t ticks_passed = end_time - start_time;
TickType_t minimum_ticks = (CONFIG_TT_SPLASH_DURATION / portTICK_PERIOD_MS);
if (minimum_ticks > ticks_passed) {
kernel::delayTicks(minimum_ticks - ticks_passed);
}
tt::service::loader::stopApp();
waitForMinimalSplashDuration(start_time);
service::loader::stopApp();
startNextApp();
}
@ -81,16 +99,15 @@ private:
static void startNextApp() {
#ifdef ESP_PLATFORM
esp_reset_reason_t reason = esp_reset_reason();
if (reason == ESP_RST_PANIC) {
app::crashdiagnostics::start();
if (esp_reset_reason() == ESP_RST_PANIC) {
crashdiagnostics::start();
return;
}
#endif
auto* config = tt::getConfiguration();
const auto* config = getConfiguration();
assert(!config->launcherAppId.empty());
tt::service::loader::startApp(config->launcherAppId);
service::loader::startApp(config->launcherAppId);
}
public:
@ -99,9 +116,9 @@ public:
auto* image = lv_image_create(parent);
lv_obj_set_size(image, LV_PCT(100), LV_PCT(100));
auto paths = app.getPaths();
const auto paths = app.getPaths();
const char* logo = hal::usb::isUsbBootMode() ? "logo_usb.png" : "logo.png";
auto logo_path = paths->getSystemPathLvgl(logo);
const auto logo_path = paths->getSystemPathLvgl(logo);
TT_LOG_I(TAG, "%s", logo_path.c_str());
lv_image_set_src(image, logo_path.c_str());

View File

@ -24,8 +24,6 @@ extern const AppManifest manifest;
class I2cScannerApp : public App {
private:
// Core
Mutex mutex = Mutex(Mutex::Type::Recursive);
std::unique_ptr<Timer> scanTimer = nullptr;
@ -286,7 +284,7 @@ void I2cScannerApp::startScanning() {
lv_obj_clean(scanListWidget);
scanState = ScanStateScanning;
scanTimer = std::make_unique<Timer>(Timer::Type::Once, [](){
scanTimer = std::make_unique<Timer>(Timer::Type::Once, []{
onScanTimerCallback();
});
scanTimer->start(10);

View File

@ -2,23 +2,23 @@
#include <Tactility/lvgl/LvglSync.h>
#include <esp_lvgl_port.h>
#include <Tactility/CpuAffinity.h>
#include <Tactility/Mutex.h>
// LVGL
// The minimum task stack seems to be about 3500, but that crashes the wifi app in some scenarios
// At 8192, it sometimes crashes when wifi-auto enables and is busy connecting and then you open WifiManage
#define TDECK_LVGL_TASK_STACK_DEPTH 9216
#define TAG "lvgl"
auto constexpr TAG = "lvgl";
namespace tt::lvgl {
bool initEspLvglPort() {
TT_LOG_D(TAG, "Port init");
static lv_disp_t* display = nullptr;
const lvgl_port_cfg_t lvgl_cfg = {
.task_priority = static_cast<UBaseType_t>(Thread::Priority::Critical),
.task_stack = TDECK_LVGL_TASK_STACK_DEPTH,
.task_affinity = 1, // -1 = disabled, 0 = core 1, 1 = core 2
.task_affinity = getCpuAffinityConfiguration().graphics,
.task_max_sleep_ms = 500,
.timer_period_ms = 5
};
@ -28,7 +28,7 @@ bool initEspLvglPort() {
return false;
}
tt::lvgl::syncSet(&lvgl_port_lock, &lvgl_port_unlock);
syncSet(&lvgl_port_lock, &lvgl_port_unlock);
return true;
}

View File

@ -13,7 +13,6 @@
#endif
#include <lvgl.h>
#include <Tactility/Tactility.h>
#include <Tactility/TactilityHeadless.h>
#include <Tactility/lvgl/LvglSync.h>
#include <Tactility/service/ServiceRegistry.h>

View File

@ -11,6 +11,7 @@
#include <Tactility/TactilityCore.h>
#include <format>
#include <Tactility/CpuAffinity.h>
namespace tt::service::screenshot {
@ -109,10 +110,11 @@ void ScreenshotTask::taskStart() {
thread = new Thread(
"screenshot",
8192,
[this]() {
[this] {
this->taskMain();
return 0;
}
},
getCpuAffinityConfiguration().graphics
);
thread->start();
}

View File

@ -0,0 +1,27 @@
#pragma once
#include "RtosCompat.h"
namespace tt {
typedef portBASE_TYPE CpuAffinity;
constexpr static CpuAffinity None = -1;
/**
* Determines the preferred affinity for certain (sub)systems.
*/
struct CpuAffinityConfiguration {
CpuAffinity system;
CpuAffinity graphics; // Display, LVGL
CpuAffinity wifi;
CpuAffinity mainDispatcher;
CpuAffinity apps;
CpuAffinity timer; // Tactility Timer (based on FreeRTOS)
};
void setCpuAffinityConfiguration(const CpuAffinityConfiguration& config);
const CpuAffinityConfiguration& getCpuAffinityConfiguration();
}

View File

@ -18,7 +18,7 @@ public:
private:
struct TimerHandleDeleter {
void operator()(TimerHandle_t handleToDelete) {
void operator()(TimerHandle_t handleToDelete) const {
xTimerDelete(handleToDelete, portMAX_DELAY);
}
};

View File

@ -0,0 +1,85 @@
#include "Tactility/CpuAffinity.h"
#include <Tactility/Check.h>
namespace tt {
#ifdef ESP_PLATFORM
static CpuAffinity getEspWifiAffinity() {
#ifdef CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_0
return 0;
#elif defined(CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_1)
return 1;
#endif
}
// Warning: Must watch ESP WiFi, as this task is used by WiFi
static CpuAffinity getEspMainSchedulerAffinity() {
#ifdef CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_0
return 0;
#elif defined(CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_1)
return 1;
#endif
}
static CpuAffinity getFreeRtosTimerAffinity() {
#if defined(CONFIG_FREERTOS_TIMER_TASK_NO_AFFINITY)
return None;
#elif defined(CONFIG_FREERTOS_TIMER_TASK_AFFINITY_CPU0)
return 0;
#elif defined(CONFIG_FREERTOS_TIMER_TASK_AFFINITY_CPU1)
return 1;
#else
static_assert(false);
#endif
}
#if CONFIG_FREERTOS_NUMBER_OF_CORES == 1
static const CpuAffinityConfiguration esp = {
.system = 0,
.graphics = 0,
.wifi = 0,
.mainDispatcher = 0,
.apps = 0,
.timer = getFreeRtosTimerAffinity()
};
#elif CONFIG_FREERTOS_NUMBER_OF_CORES == 2
static const CpuAffinityConfiguration esp = {
.system = 0,
.graphics = 1,
.wifi = getEspWifiAffinity(),
.mainDispatcher = getEspMainSchedulerAffinity(),
.apps = 1,
.timer = getFreeRtosTimerAffinity()
};
#endif
#else
static const CpuAffinityConfiguration simulator = {
.system = None,
.graphics = None,
.wifi = None,
.mainDispatcher = 0,
.apps = None,
.timer = None
};
#endif
const CpuAffinityConfiguration& getCpuAffinityConfiguration() {
#ifdef ESP_PLATFORM
#if CONFIG_FREERTOS_NUMBER_OF_CORES == 2
// WiFi uses the main dispatcher to defer operations in the background
assert(esp.wifi == esp.mainDispatcher);
#endif // CORES
return esp;
#else
return simulator;
#endif
}
}

View File

@ -13,7 +13,7 @@ void Timer::onCallback(TimerHandle_t hTimer) {
}
}
static inline TimerHandle_t createTimer(Timer::Type type, void* timerId, TimerCallbackFunction_t callback) {
static TimerHandle_t createTimer(Timer::Type type, void* timerId, TimerCallbackFunction_t callback) {
assert(timerId != nullptr);
assert(callback != nullptr);