Add new target (pinetime-graphics) that flash the bootloader logo into the spi flash memory.

This commit is contained in:
JF 2020-06-06 19:28:01 +02:00
parent e7723598a6
commit 46b8bf9fc1
6 changed files with 7421 additions and 1 deletions

View File

@ -1,5 +1,22 @@
# Bootloader
## Bootloader graphic
The bootloader loads a graphic (Pinetime logo) from the SPI Flash memory. If this graphic is not loaded in the memory, the LCD will display garbage (the content of the SPI flash memory).
The SPI Flash memory is not accessible via the SWD debugger. Use the firmware 'pinetime-graphics' to load the graphic into memory. All you have to do is build it and program it at address 0x00 :
- Build:
```
$ make pinetime-graphics
```
- Program (using OpenOCD for example) :
```
program pinetime-graphics.bin 0
```
- Let it run for ~10s (it does nothing for 5 seconds, then write the logo into the SPI memory, then (slowly) displays it on the LCD).
## Bootloader binary
The binary comes from https://github.com/lupyuen/pinetime-rust-mynewt/releases/tag/v4.1.7

7206
bootloader/boot_graphics.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -372,6 +372,26 @@ list(APPEND SOURCE_FILES
SystemTask/SystemTask.cpp
)
list(APPEND GRAPHICS_SOURCE_FILES
${SDK_SOURCE_FILES}
# FreeRTOS
FreeRTOS/port.c
FreeRTOS/port_cmsis_systick.c
FreeRTOS/port_cmsis.c
drivers/SpiNorFlash.cpp
drivers/SpiMaster.cpp
drivers/Spi.cpp
Logging/NrfLogger.cpp
Components/Gfx/Gfx.cpp
drivers/St7789.cpp
Components/Brightness/BrightnessController.cpp
graphics.cpp
)
set(INCLUDE_FILES
Logging/Logger.h
Logging/NrfLogger.h
@ -581,6 +601,32 @@ add_custom_command(TARGET ${EXECUTABLE_MCUBOOT_NAME}
COMMENT "post build steps for ${EXECUTABLE_MCUBOOT_NAME}"
)
# Build binary that writes the graphic assets for the bootloader
set(EXECUTABLE_GRAPHICS_NAME "pinetime-graphics")
add_executable(${EXECUTABLE_GRAPHICS_NAME} ${GRAPHICS_SOURCE_FILES})
target_compile_options(${EXECUTABLE_GRAPHICS_NAME} PUBLIC
$<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -O0 -g3>
$<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -O3>
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -O0 -g3>
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -O3>
$<$<COMPILE_LANGUAGE:ASM>: -MP -MD -std=c99 -x assembler-with-cpp>
)
set_target_properties(${EXECUTABLE_GRAPHICS_NAME} PROPERTIES
SUFFIX ".out"
LINK_FLAGS "-mthumb -mabi=aapcs -std=gnu++98 -std=c99 -L ${NRF5_SDK_PATH}/modules/nrfx/mdk -T${NRF5_LINKER_SCRIPT} -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -Wl,--gc-sections --specs=nano.specs -lc -lnosys -lm -Wl,-Map=${EXECUTABLE_GRAPHICS_NAME}.map"
CXX_STANDARD 11
C_STANDARD 99
)
add_custom_command(TARGET ${EXECUTABLE_GRAPHICS_NAME}
POST_BUILD
COMMAND ${CMAKE_SIZE_UTIL} ${EXECUTABLE_GRAPHICS_NAME}.out
COMMAND ${CMAKE_OBJCOPY} -O binary ${EXECUTABLE_GRAPHICS_NAME}.out "${EXECUTABLE_GRAPHICS_NAME}.bin"
COMMAND ${CMAKE_OBJCOPY} -O ihex ${EXECUTABLE_GRAPHICS_NAME}.out "${EXECUTABLE_GRAPHICS_NAME}.hex"
COMMENT "post build steps for ${EXECUTABLE_GRAPHICS_NAME}"
)
# FLASH
if(USE_JLINK)
add_custom_target(FLASH_ERASE

View File

@ -43,6 +43,20 @@ void Gfx::FillRectangle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint16_t col
WaitTransfertFinished();
}
void Gfx::FillRectangle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t* b) {
state.remainingIterations = h;
state.currentIteration = 0;
state.busy = true;
state.action = Action::FillRectangle;
state.color = 0x00;
state.taskToNotify = xTaskGetCurrentTaskHandle();
lcd.BeginDrawBuffer(x, y, w, h);
lcd.NextDrawBuffer(reinterpret_cast<const uint8_t *>(b), width * 2);
WaitTransfertFinished();
}
void Gfx::DrawString(uint8_t x, uint8_t y, uint16_t color, const char *text, const FONT_INFO *p_font, bool wrap) {
if (y > (height - p_font->height)) {
// Not enough space to write even single char.

View File

@ -19,6 +19,7 @@ namespace Pinetime {
void DrawString(uint8_t x, uint8_t y, uint16_t color, const char* text, const FONT_INFO *p_font, bool wrap);
void DrawChar(const FONT_INFO *font, uint8_t c, uint8_t *x, uint8_t y, uint16_t color);
void FillRectangle(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint16_t color);
void FillRectangle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t* b);
void SetScrollArea(uint16_t topFixedLines, uint16_t scrollLines, uint16_t bottomFixedLines);
void SetScrollStartLine(uint16_t line);
@ -26,6 +27,8 @@ namespace Pinetime {
void Sleep();
void Wakeup();
bool GetNextBuffer(uint8_t **buffer, size_t &size) override;
void pixel_draw(uint8_t x, uint8_t y, uint16_t color);
private:
static constexpr uint8_t width = 240;
@ -49,7 +52,6 @@ namespace Pinetime {
uint16_t buffer[width]; // 1 line buffer
Drivers::St7789& lcd;
void pixel_draw(uint8_t x, uint8_t y, uint16_t color);
void SetBackgroundColor(uint16_t color);
void WaitTransfertFinished() const;
void NotifyEndOfTransfert(TaskHandle_t task);

135
src/graphics.cpp Normal file
View File

@ -0,0 +1,135 @@
#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 <sdk/components/libraries/log/nrf_log.h>
#include "bootloader/boot_graphics.h"
#include <FreeRTOS.h>
#include <task.h>
#include <sdk/integration/nrfx/legacy/nrf_drv_gpiote.h>
#include <libraries/gpiote/app_gpiote.h>
#include <sdk/modules/nrfx/hal/nrf_wdt.h>
#include <cstring>
#include <Components/Gfx/Gfx.h>
#include <drivers/St7789.h>
#include <Components/Brightness/BrightnessController.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;
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;
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 Process(void* instance) {
// Wait before erasing the memory to let the time to the SWD debugger to flash a new firmware before running this one.
vTaskDelay(5000);
APP_GPIOTE_INIT(2);
NRF_LOG_INFO("Init...");
spi.Init();
spiNorFlash.Init();
brightnessController.Init();
lcd.Init();
gfx.Init();
NRF_LOG_INFO("Init Done!")
NRF_LOG_INFO("Erasing...");
for (uint32_t erased = 0; erased < graphicSize; erased += 0x1000) {
spiNorFlash.SectorErase(erased);
}
NRF_LOG_INFO("Erase done!");
NRF_LOG_INFO("Writing graphic...");
static constexpr uint32_t memoryChunkSize = 200;
uint8_t writeBuffer[memoryChunkSize];
for(int offset = 0; offset < 115200; offset+=memoryChunkSize) {
std::memcpy(writeBuffer, &graphicBuffer[offset], memoryChunkSize);
spiNorFlash.Write(offset, writeBuffer, memoryChunkSize);
}
NRF_LOG_INFO("Writing graphic done!");
NRF_LOG_INFO("Read memory and display the graphic...");
static constexpr uint32_t screenWidth = 240;
static constexpr uint32_t screenWidthInBytes = screenWidth*2; // LCD display 16bits color (1 pixel = 2 bytes)
uint16_t displayLineBuffer[screenWidth];
for(int line = 0; line < screenWidth; line++) {
spiNorFlash.Read(line*screenWidthInBytes, reinterpret_cast<uint8_t *>(displayLineBuffer), screenWidth);
spiNorFlash.Read((line*screenWidthInBytes)+screenWidth, reinterpret_cast<uint8_t *>(displayLineBuffer) + screenWidth, screenWidth);
for(int col = 0; col < screenWidth; col++) {
gfx.pixel_draw(col, line, displayLineBuffer[col]);
}
}
NRF_LOG_INFO("Done!");
while(1) {
asm("nop" );
}
}
int main(void) {
TaskHandle_t taskHandle;
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);
}
}