InfiniTime/src/recoveryLoader.cpp
Jean-François Milants 25f35c7d0e Generate pinetime-recovery : a light version of InfiniTime design to be used as a recovery firmware : it only provides basic UI and BLE connectivity for OTA.
This new FW is build on the same codebasse than the actual InfiniTime. Only the display task is different (this allows to remove lvgl from the recovery fw, which is very heavy).

CMake builds and docker have been modified accordingly.
Note than the fw is converted into an image and then into a DFU in the cmake build (previously, it was only done in the
2021-01-26 20:31:45 +01:00

168 lines
4.8 KiB
C++

#include <legacy/nrf_drv_clock.h>
#include <softdevice/common/nrf_sdh.h>
#include <drivers/SpiMaster.h>
#include <drivers/Spi.h>
#include <drivers/SpiNorFlash.h>
#include <libraries/log/nrf_log.h>
#include <FreeRTOS.h>
#include <task.h>
#include <legacy/nrf_drv_gpiote.h>
#include <libraries/gpiote/app_gpiote.h>
#include <hal/nrf_wdt.h>
#include <cstring>
#include <components/gfx/Gfx.h>
#include <drivers/St7789.h>
#include <components/brightness/BrightnessController.h>
#include <algorithm>
#include "recoveryImage.h"
#include "displayapp/icons/infinitime/infinitime-nb.c"
#include "components/rle/RleDecoder.h"
#if NRF_LOG_ENABLED
#include "logging/NrfLogger.h"
Pinetime::Logging::NrfLogger logger;
#else
#include "logging/DummyLogger.h"
Pinetime::Logging::DummyLogger logger;
#endif
static constexpr uint8_t pinSpiSck = 2;
static constexpr uint8_t pinSpiMosi = 3;
static constexpr uint8_t pinSpiMiso = 4;
static constexpr uint8_t pinSpiFlashCsn = 5;
static constexpr uint8_t pinLcdCsn = 25;
static constexpr uint8_t pinLcdDataCommand = 18;
static constexpr uint8_t displayWidth = 240;
static constexpr uint8_t displayHeight = 240;
static constexpr uint8_t bytesPerPixel = 2;
static constexpr uint16_t colorWhite = 0xFFFF;
static constexpr uint16_t colorGreen = 0xE007;
Pinetime::Drivers::SpiMaster spi{Pinetime::Drivers::SpiMaster::SpiModule::SPI0, {
Pinetime::Drivers::SpiMaster::BitOrder::Msb_Lsb,
Pinetime::Drivers::SpiMaster::Modes::Mode3,
Pinetime::Drivers::SpiMaster::Frequencies::Freq8Mhz,
pinSpiSck,
pinSpiMosi,
pinSpiMiso
}
};
Pinetime::Drivers::Spi flashSpi{spi, pinSpiFlashCsn};
Pinetime::Drivers::SpiNorFlash spiNorFlash{flashSpi};
Pinetime::Drivers::Spi lcdSpi {spi, pinLcdCsn};
Pinetime::Drivers::St7789 lcd {lcdSpi, pinLcdDataCommand};
Pinetime::Components::Gfx gfx{lcd};
Pinetime::Controllers::BrightnessController brightnessController;
void DisplayProgressBar(uint8_t percent, uint16_t color);
void DisplayLogo();
extern "C" {
void vApplicationIdleHook(void) {
}
void SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler(void) {
if(((NRF_SPIM0->INTENSET & (1<<6)) != 0) && NRF_SPIM0->EVENTS_END == 1) {
NRF_SPIM0->EVENTS_END = 0;
spi.OnEndEvent();
}
if(((NRF_SPIM0->INTENSET & (1<<19)) != 0) && NRF_SPIM0->EVENTS_STARTED == 1) {
NRF_SPIM0->EVENTS_STARTED = 0;
spi.OnStartedEvent();
}
if(((NRF_SPIM0->INTENSET & (1<<1)) != 0) && NRF_SPIM0->EVENTS_STOPPED == 1) {
NRF_SPIM0->EVENTS_STOPPED = 0;
}
}
}
void RefreshWatchdog() {
NRF_WDT->RR[0] = WDT_RR_RR_Reload;
}
uint8_t displayBuffer[displayWidth * bytesPerPixel];
void Process(void* instance) {
RefreshWatchdog();
APP_GPIOTE_INIT(2);
NRF_LOG_INFO("Init...");
spi.Init();
spiNorFlash.Init();
spiNorFlash.Wakeup();
brightnessController.Init();
lcd.Init();
gfx.Init();
NRF_LOG_INFO("Display logo")
DisplayLogo();
NRF_LOG_INFO("Erasing...");
for (uint32_t erased = 0; erased < sizeof(recoveryImage); erased += 0x1000) {
spiNorFlash.SectorErase(erased);
RefreshWatchdog();
}
NRF_LOG_INFO("Writing factory image...");
static constexpr uint32_t memoryChunkSize = 200;
uint8_t writeBuffer[memoryChunkSize];
for(size_t offset = 0; offset < sizeof(recoveryImage); offset+=memoryChunkSize) {
std::memcpy(writeBuffer, &recoveryImage[offset], memoryChunkSize);
spiNorFlash.Write(offset, writeBuffer, memoryChunkSize);
DisplayProgressBar((static_cast<float>(offset) / static_cast<float>(sizeof(recoveryImage))) * 100.0f, colorWhite);
RefreshWatchdog();
}
NRF_LOG_INFO("Writing factory image done!");
DisplayProgressBar(100.0f, colorGreen);
while(1) {
asm("nop" );
}
}
void DisplayLogo() {
Pinetime::Tools::RleDecoder rleDecoder(infinitime_nb, sizeof(infinitime_nb));
for(int i = 0; i < displayWidth; i++) {
rleDecoder.DecodeNext(displayBuffer, displayWidth * bytesPerPixel);
ulTaskNotifyTake(pdTRUE, 500);
lcd.BeginDrawBuffer(0, i, displayWidth, 1);
lcd.NextDrawBuffer(reinterpret_cast<const uint8_t *>(displayBuffer), displayWidth * bytesPerPixel);
}
}
void DisplayProgressBar(uint8_t percent, uint16_t color) {
static constexpr uint8_t barHeight = 20;
std::fill(displayBuffer, displayBuffer+(displayWidth * bytesPerPixel), color);
for(int i = 0; i < barHeight; i++) {
ulTaskNotifyTake(pdTRUE, 500);
uint16_t barWidth = std::min(static_cast<float>(percent) * 2.4f, static_cast<float>(displayWidth));
lcd.BeginDrawBuffer(0, displayWidth - barHeight + i, barWidth, 1);
lcd.NextDrawBuffer(reinterpret_cast<const uint8_t *>(displayBuffer), barWidth * bytesPerPixel);
}
}
int main(void) {
TaskHandle_t taskHandle;
RefreshWatchdog();
logger.Init();
nrf_drv_clock_init();
if (pdPASS != xTaskCreate(Process, "MAIN", 512, nullptr, 0, &taskHandle))
APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
vTaskStartScheduler();
for (;;) {
APP_ERROR_HANDLER(NRF_ERROR_FORBIDDEN);
}
}