Add new target (pinetime-graphics) that flash the bootloader logo into the spi flash memory.
This commit is contained in:
parent
e7723598a6
commit
46b8bf9fc1
|
@ -1,5 +1,22 @@
|
||||||
# Bootloader
|
# 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
|
## Bootloader binary
|
||||||
The binary comes from https://github.com/lupyuen/pinetime-rust-mynewt/releases/tag/v4.1.7
|
The binary comes from https://github.com/lupyuen/pinetime-rust-mynewt/releases/tag/v4.1.7
|
||||||
|
|
||||||
|
|
7206
bootloader/boot_graphics.h
Normal file
7206
bootloader/boot_graphics.h
Normal file
File diff suppressed because it is too large
Load diff
|
@ -372,6 +372,26 @@ list(APPEND SOURCE_FILES
|
||||||
SystemTask/SystemTask.cpp
|
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
|
set(INCLUDE_FILES
|
||||||
Logging/Logger.h
|
Logging/Logger.h
|
||||||
Logging/NrfLogger.h
|
Logging/NrfLogger.h
|
||||||
|
@ -581,6 +601,32 @@ add_custom_command(TARGET ${EXECUTABLE_MCUBOOT_NAME}
|
||||||
COMMENT "post build steps for ${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
|
# FLASH
|
||||||
if(USE_JLINK)
|
if(USE_JLINK)
|
||||||
add_custom_target(FLASH_ERASE
|
add_custom_target(FLASH_ERASE
|
||||||
|
|
|
@ -43,6 +43,20 @@ void Gfx::FillRectangle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint16_t col
|
||||||
WaitTransfertFinished();
|
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) {
|
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)) {
|
if (y > (height - p_font->height)) {
|
||||||
// Not enough space to write even single char.
|
// Not enough space to write even single char.
|
||||||
|
|
|
@ -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 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 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 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 SetScrollArea(uint16_t topFixedLines, uint16_t scrollLines, uint16_t bottomFixedLines);
|
||||||
void SetScrollStartLine(uint16_t line);
|
void SetScrollStartLine(uint16_t line);
|
||||||
|
|
||||||
|
@ -26,6 +27,8 @@ namespace Pinetime {
|
||||||
void Sleep();
|
void Sleep();
|
||||||
void Wakeup();
|
void Wakeup();
|
||||||
bool GetNextBuffer(uint8_t **buffer, size_t &size) override;
|
bool GetNextBuffer(uint8_t **buffer, size_t &size) override;
|
||||||
|
void pixel_draw(uint8_t x, uint8_t y, uint16_t color);
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr uint8_t width = 240;
|
static constexpr uint8_t width = 240;
|
||||||
|
@ -49,7 +52,6 @@ namespace Pinetime {
|
||||||
uint16_t buffer[width]; // 1 line buffer
|
uint16_t buffer[width]; // 1 line buffer
|
||||||
Drivers::St7789& lcd;
|
Drivers::St7789& lcd;
|
||||||
|
|
||||||
void pixel_draw(uint8_t x, uint8_t y, uint16_t color);
|
|
||||||
void SetBackgroundColor(uint16_t color);
|
void SetBackgroundColor(uint16_t color);
|
||||||
void WaitTransfertFinished() const;
|
void WaitTransfertFinished() const;
|
||||||
void NotifyEndOfTransfert(TaskHandle_t task);
|
void NotifyEndOfTransfert(TaskHandle_t task);
|
||||||
|
|
135
src/graphics.cpp
Normal file
135
src/graphics.cpp
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue