diff options
Diffstat (limited to 'src/systemtask')
-rw-r--r-- | src/systemtask/SystemMonitor.h | 46 | ||||
-rw-r--r-- | src/systemtask/SystemTask.cpp | 257 | ||||
-rw-r--r-- | src/systemtask/SystemTask.h | 93 |
3 files changed, 396 insertions, 0 deletions
diff --git a/src/systemtask/SystemMonitor.h b/src/systemtask/SystemMonitor.h new file mode 100644 index 00000000..029a1364 --- /dev/null +++ b/src/systemtask/SystemMonitor.h @@ -0,0 +1,46 @@ +#pragma once +#include <FreeRTOS.h> +#include <task.h> +#include <nrf_log.h> + + +namespace Pinetime { + namespace System { + struct DummyMonitor {}; + struct FreeRtosMonitor {}; + + template<class T> + class SystemMonitor { + public: + SystemMonitor() = delete; + }; + + template<> + class SystemMonitor<DummyMonitor> { + public: + void Process() const {} + }; + + template<> + class SystemMonitor<FreeRtosMonitor> { + public: + void Process() const { + if(xTaskGetTickCount() - lastTick > 10000) { + NRF_LOG_INFO("---------------------------------------\nFree heap : %d", xPortGetFreeHeapSize()); + auto nb = uxTaskGetSystemState(tasksStatus, 10, nullptr); + for (uint32_t i = 0; i < nb; i++) { + NRF_LOG_INFO("Task [%s] - %d", tasksStatus[i].pcTaskName, tasksStatus[i].usStackHighWaterMark); + if (tasksStatus[i].usStackHighWaterMark < 20) + NRF_LOG_INFO("WARNING!!! Task %s task is nearly full, only %dB available", tasksStatus[i].pcTaskName, + tasksStatus[i].usStackHighWaterMark * 4); + } + lastTick = xTaskGetTickCount(); + } + } + + private: + mutable TickType_t lastTick = 0; + mutable TaskStatus_t tasksStatus[10]; + }; + } +}
\ No newline at end of file diff --git a/src/systemtask/SystemTask.cpp b/src/systemtask/SystemTask.cpp new file mode 100644 index 00000000..dac4ce29 --- /dev/null +++ b/src/systemtask/SystemTask.cpp @@ -0,0 +1,257 @@ +#include <libraries/log/nrf_log.h> +#include <libraries/gpiote/app_gpiote.h> +#include <drivers/Cst816s.h> +#include "displayapp/LittleVgl.h" +#include <hal/nrf_rtc.h> +#include "components/ble/NotificationManager.h" +#include <host/ble_gatt.h> +#include <host/ble_hs_adv.h> +#include "SystemTask.h" +#include <nimble/hci_common.h> +#include <host/ble_gap.h> +#include <host/util/util.h> +#include <drivers/InternalFlash.h> +#include "main.h" +#include "components/ble/NimbleController.h" +#include "../BootloaderVersion.h" + +using namespace Pinetime::System; + +void IdleTimerCallback(TimerHandle_t xTimer) { + + NRF_LOG_INFO("IdleTimerCallback"); + auto sysTask = static_cast<SystemTask *>(pvTimerGetTimerID(xTimer)); + sysTask->OnIdle(); +} + + +SystemTask::SystemTask(Drivers::SpiMaster &spi, Drivers::St7789 &lcd, + Pinetime::Drivers::SpiNorFlash& spiNorFlash, + Drivers::TwiMaster& twiMaster, Drivers::Cst816S &touchPanel, + Components::LittleVgl &lvgl, + Controllers::Battery &batteryController, Controllers::Ble &bleController, + Controllers::DateTime &dateTimeController, + Pinetime::Controllers::NotificationManager& notificationManager) : + spi{spi}, lcd{lcd}, spiNorFlash{spiNorFlash}, + twiMaster{twiMaster}, touchPanel{touchPanel}, lvgl{lvgl}, batteryController{batteryController}, + bleController{bleController}, dateTimeController{dateTimeController}, + watchdog{}, watchdogView{watchdog}, notificationManager{notificationManager}, + nimbleController(*this, bleController,dateTimeController, notificationManager, batteryController, spiNorFlash) { + systemTasksMsgQueue = xQueueCreate(10, 1); +} + +void SystemTask::Start() { + if (pdPASS != xTaskCreate(SystemTask::Process, "MAIN", 350, this, 0, &taskHandle)) + APP_ERROR_HANDLER(NRF_ERROR_NO_MEM); +} + +void SystemTask::Process(void *instance) { + auto *app = static_cast<SystemTask *>(instance); + NRF_LOG_INFO("systemtask task started!"); + app->Work(); +} + +void SystemTask::Work() { + watchdog.Setup(7); + watchdog.Start(); + NRF_LOG_INFO("Last reset reason : %s", Pinetime::Drivers::Watchdog::ResetReasonToString(watchdog.ResetReason())); + APP_GPIOTE_INIT(2); + + spi.Init(); + spiNorFlash.Init(); + spiNorFlash.Wakeup(); + nimbleController.Init(); + nimbleController.StartAdvertising(); + lcd.Init(); + + twiMaster.Init(); + touchPanel.Init(); + batteryController.Init(); + + displayApp.reset(new Pinetime::Applications::DisplayApp(lcd, lvgl, touchPanel, batteryController, bleController, + dateTimeController, watchdogView, *this, notificationManager)); + displayApp->Start(); + + batteryController.Update(); + displayApp->PushMessage(Pinetime::Applications::DisplayApp::Messages::UpdateBatteryLevel); + + nrf_gpio_cfg_sense_input(pinButton, (nrf_gpio_pin_pull_t)GPIO_PIN_CNF_PULL_Pulldown, (nrf_gpio_pin_sense_t)GPIO_PIN_CNF_SENSE_High); + nrf_gpio_cfg_output(15); + nrf_gpio_pin_set(15); + + nrfx_gpiote_in_config_t pinConfig; + pinConfig.skip_gpio_setup = true; + pinConfig.hi_accuracy = false; + pinConfig.is_watcher = false; + pinConfig.sense = (nrf_gpiote_polarity_t)NRF_GPIOTE_POLARITY_HITOLO; + pinConfig.pull = (nrf_gpio_pin_pull_t)GPIO_PIN_CNF_PULL_Pulldown; + + nrfx_gpiote_in_init(pinButton, &pinConfig, nrfx_gpiote_evt_handler); + + nrf_gpio_cfg_sense_input(pinTouchIrq, (nrf_gpio_pin_pull_t)GPIO_PIN_CNF_PULL_Pullup, (nrf_gpio_pin_sense_t)GPIO_PIN_CNF_SENSE_Low); + + pinConfig.skip_gpio_setup = true; + pinConfig.hi_accuracy = false; + pinConfig.is_watcher = false; + pinConfig.sense = (nrf_gpiote_polarity_t)NRF_GPIOTE_POLARITY_HITOLO; + pinConfig.pull = (nrf_gpio_pin_pull_t)GPIO_PIN_CNF_PULL_Pullup; + + nrfx_gpiote_in_init(pinTouchIrq, &pinConfig, nrfx_gpiote_evt_handler); + + idleTimer = xTimerCreate ("idleTimer", idleTime, pdFALSE, this, IdleTimerCallback); + xTimerStart(idleTimer, 0); + + // Suppress endless loop diagnostic + #pragma clang diagnostic push + #pragma ide diagnostic ignored "EndlessLoop" + while(true) { + uint8_t msg; + if (xQueueReceive(systemTasksMsgQueue, &msg, isSleeping ? 2500 : 1000)) { + Messages message = static_cast<Messages >(msg); + switch(message) { + case Messages::GoToRunning: + spi.Wakeup(); + twiMaster.Wakeup(); + + spiNorFlash.Wakeup(); + lcd.Wakeup(); + touchPanel.Wakeup(); + + displayApp->PushMessage(Applications::DisplayApp::Messages::GoToRunning); + displayApp->PushMessage(Applications::DisplayApp::Messages::UpdateBatteryLevel); + + xTimerStart(idleTimer, 0); + nimbleController.StartAdvertising(); + isSleeping = false; + isWakingUp = false; + break; + case Messages::GoToSleep: + isGoingToSleep = true; + NRF_LOG_INFO("[systemtask] Going to sleep"); + xTimerStop(idleTimer, 0); + displayApp->PushMessage(Pinetime::Applications::DisplayApp::Messages::GoToSleep); + break; + case Messages::OnNewTime: + ReloadIdleTimer(); + displayApp->PushMessage(Pinetime::Applications::DisplayApp::Messages::UpdateDateTime); + break; + case Messages::OnNewNotification: + if(isSleeping && !isWakingUp) GoToRunning(); + displayApp->PushMessage(Pinetime::Applications::DisplayApp::Messages::NewNotification); + break; + case Messages::BleConnected: + ReloadIdleTimer(); + isBleDiscoveryTimerRunning = true; + bleDiscoveryTimer = 5; + break; + case Messages::BleFirmwareUpdateStarted: + doNotGoToSleep = true; + if(isSleeping && !isWakingUp) GoToRunning(); + displayApp->PushMessage(Pinetime::Applications::DisplayApp::Messages::BleFirmwareUpdateStarted); + break; + case Messages::BleFirmwareUpdateFinished: + doNotGoToSleep = false; + xTimerStart(idleTimer, 0); + if(bleController.State() == Pinetime::Controllers::Ble::FirmwareUpdateStates::Validated) + NVIC_SystemReset(); + break; + case Messages::OnTouchEvent: + ReloadIdleTimer(); + break; + case Messages::OnButtonEvent: + ReloadIdleTimer(); + break; + case Messages::OnDisplayTaskSleeping: + if(BootloaderVersion::IsValid()) { + // First versions of the bootloader do not expose their version and cannot initialize the SPI NOR FLASH + // if it's in sleep mode. Avoid bricked device by disabling sleep mode on these versions. + spiNorFlash.Sleep(); + } + lcd.Sleep(); + touchPanel.Sleep(); + + spi.Sleep(); + twiMaster.Sleep(); + isSleeping = true; + isGoingToSleep = false; + break; + default: break; + } + } + + if(isBleDiscoveryTimerRunning) { + if(bleDiscoveryTimer == 0) { + isBleDiscoveryTimerRunning = false; + // Services discovery is deffered from 3 seconds to avoid the conflicts between the host communicating with the + // tharget and vice-versa. I'm not sure if this is the right way to handle this... + nimbleController.StartDiscovery(); + } else { + bleDiscoveryTimer--; + } + } + + uint32_t systick_counter = nrf_rtc_counter_get(portNRF_RTC_REG); + dateTimeController.UpdateTime(systick_counter); + batteryController.Update(); + + monitor.Process(); + + if(!nrf_gpio_pin_read(pinButton)) + watchdog.Kick(); + } + // Clear diagnostic suppression + #pragma clang diagnostic pop +} + +void SystemTask::OnButtonPushed() { + if(isGoingToSleep) return; + if(!isSleeping) { + NRF_LOG_INFO("[systemtask] Button pushed"); + PushMessage(Messages::OnButtonEvent); + displayApp->PushMessage(Pinetime::Applications::DisplayApp::Messages::ButtonPushed); + } + else { + if(!isWakingUp) { + NRF_LOG_INFO("[systemtask] Button pushed, waking up"); + GoToRunning(); + } + } +} + +void SystemTask::GoToRunning() { + isWakingUp = true; + PushMessage(Messages::GoToRunning); +} + +void SystemTask::OnTouchEvent() { + if(isGoingToSleep) return ; + NRF_LOG_INFO("[systemtask] Touch event"); + if(!isSleeping) { + PushMessage(Messages::OnTouchEvent); + displayApp->PushMessage(Pinetime::Applications::DisplayApp::Messages::TouchEvent); + } +} + +void SystemTask::PushMessage(SystemTask::Messages msg) { + if(msg == Messages::GoToSleep) { + isGoingToSleep = true; + } + BaseType_t xHigherPriorityTaskWoken; + xHigherPriorityTaskWoken = pdFALSE; + xQueueSendFromISR(systemTasksMsgQueue, &msg, &xHigherPriorityTaskWoken); + if (xHigherPriorityTaskWoken) { + /* Actual macro used here is port specific. */ + // TODO: should I do something here? + } +} + +void SystemTask::OnIdle() { + if(doNotGoToSleep) return; + NRF_LOG_INFO("Idle timeout -> Going to sleep") + PushMessage(Messages::GoToSleep); +} + +void SystemTask::ReloadIdleTimer() const { + if(isSleeping || isGoingToSleep) return; + xTimerReset(idleTimer, 0); +} diff --git a/src/systemtask/SystemTask.h b/src/systemtask/SystemTask.h new file mode 100644 index 00000000..6ef0cfbf --- /dev/null +++ b/src/systemtask/SystemTask.h @@ -0,0 +1,93 @@ +#pragma once + +#include <memory> + +#include <FreeRTOS.h> +#include <task.h> +#include <drivers/SpiMaster.h> +#include <drivers/St7789.h> +#include "components/battery/BatteryController.h" +#include "displayapp/DisplayApp.h" +#include <drivers/Watchdog.h> +#include <drivers/SpiNorFlash.h> +#include "SystemMonitor.h" +#include "components/ble/NimbleController.h" +#include "timers.h" + +namespace Pinetime { + namespace System { + class SystemTask { + public: + enum class Messages {GoToSleep, GoToRunning, OnNewTime, OnNewNotification, BleConnected, + BleFirmwareUpdateStarted, BleFirmwareUpdateFinished, OnTouchEvent, OnButtonEvent, OnDisplayTaskSleeping + }; + + SystemTask(Drivers::SpiMaster &spi, Drivers::St7789 &lcd, + Pinetime::Drivers::SpiNorFlash& spiNorFlash, + Drivers::TwiMaster& twiMaster, Drivers::Cst816S &touchPanel, + Components::LittleVgl &lvgl, + Controllers::Battery &batteryController, Controllers::Ble &bleController, + Controllers::DateTime &dateTimeController, + Pinetime::Controllers::NotificationManager& manager); + + + void Start(); + void PushMessage(Messages msg); + + void OnButtonPushed(); + void OnTouchEvent(); + + void OnIdle(); + + Pinetime::Controllers::NimbleController& nimble() {return nimbleController;}; + + private: + TaskHandle_t taskHandle; + + Pinetime::Drivers::SpiMaster& spi; + Pinetime::Drivers::St7789& lcd; + Pinetime::Drivers::SpiNorFlash& spiNorFlash; + Pinetime::Drivers::TwiMaster& twiMaster; + Pinetime::Drivers::Cst816S& touchPanel; + Pinetime::Components::LittleVgl& lvgl; + Pinetime::Controllers::Battery& batteryController; + std::unique_ptr<Pinetime::Applications::DisplayApp> displayApp; + Pinetime::Controllers::Ble& bleController; + Pinetime::Controllers::DateTime& dateTimeController; + QueueHandle_t systemTasksMsgQueue; + std::atomic<bool> isSleeping{false}; + std::atomic<bool> isGoingToSleep{false}; + std::atomic<bool> isWakingUp{false}; + Pinetime::Drivers::Watchdog watchdog; + Pinetime::Drivers::WatchdogView watchdogView; + Pinetime::Controllers::NotificationManager& notificationManager; + Pinetime::Controllers::NimbleController nimbleController; + + + static constexpr uint8_t pinSpiSck = 2; + static constexpr uint8_t pinSpiMosi = 3; + static constexpr uint8_t pinSpiMiso = 4; + static constexpr uint8_t pinSpiCsn = 25; + static constexpr uint8_t pinLcdDataCommand = 18; + static constexpr uint8_t pinButton = 13; + static constexpr uint8_t pinTouchIrq = 28; + + static void Process(void* instance); + void Work(); + void ReloadIdleTimer() const; + bool isBleDiscoveryTimerRunning = false; + uint8_t bleDiscoveryTimer = 0; + static constexpr uint32_t idleTime = 15000; + TimerHandle_t idleTimer; + bool doNotGoToSleep = false; + + void GoToRunning(); + +#if configUSE_TRACE_FACILITY == 1 + SystemMonitor<FreeRtosMonitor> monitor; +#else + SystemMonitor<DummyMonitor> monitor; +#endif + }; + } +} |