diff --git a/bootloader/bootloader-display.c b/bootloader/bootloader-display.c index 2c1e34fd81319f7761eb577d12a0afff7962b0cb..1fe0cae0ffee19dcf226db64248af754e3c87ac7 100644 --- a/bootloader/bootloader-display.c +++ b/bootloader/bootloader-display.c @@ -1,14 +1,42 @@ #include "bootloader.h" +/* Autogenerated */ +#include "splash-screen.h" #include "GUI_Paint.h" #include "display.h" +/* + * "Decompress" splash-screen image. The algorithm works as follows: + * + * Each byte encodes up to 127 pixels in either white or black. The most + * significant bit determines the color, the remaining 7 bits determine the + * amount. + */ +static void bootloader_display_splash(void) +{ + int idx = 0; + + for (int i = 0; i < sizeof(splash); i++) { + uint16_t color = (splash[i] & 0x80) ? 0xffff : 0x0000; + uint8_t length = splash[i] & 0x7f; + + for (int j = 0; j < length; j++) { + uint16_t x = idx % 160; + uint16_t y = idx / 160; + Paint_SetPixel(x, y, color); + idx++; + } + } + + LCD_Update(); +} + /* * Initialize the display. */ void bootloader_display_init(void) { - ; + bootloader_display_splash(); } /* diff --git a/bootloader/main.c b/bootloader/main.c index 1a5f08e0da2fc7a2b3867c8c0fec072ba2a67e50..d3577542ede90d0840f6606be5df36764903c333 100644 --- a/bootloader/main.c +++ b/bootloader/main.c @@ -205,10 +205,11 @@ int main(void) */ pmic_set_button_callback(pmic_button); - bootloader_display_header(); + bootloader_display_init(); // If the button is pressed, we go into MSC mode. if (PB_Get(3)) { + bootloader_display_header(); bootloader_display_line(2, "USB activated.", 0xffff); bootloader_display_line(3, "Ready.", 0xffff); run_usbmsc(); @@ -224,36 +225,42 @@ int main(void) int res = check_integrity(); if (res == -ENOENT) { printf("card10.bin not found!\n"); - bootloader_display_line( - 2, "card10.bin not found", 0xffff - ); } else if (res == -EINVAL) { printf("card10.bin CRC is invalid!\n"); + bootloader_display_header(); bootloader_display_line( 2, "Integrity check failed", 0xffff ); + + bootloader_display_line(4, "Trying to boot", 0xffff); } else if (res == 0) { printf("Found valid application image\n"); if (is_update_needed()) { printf("Trying to update firmware from external flash\n"); + bootloader_display_header(); bootloader_display_line( - 4, "Updating ...", 0xffff + 3, "Updating ...", 0xffff ); erase_partition(); flash_partition(); + bootloader_display_line( + 4, "Trying to boot", 0xffff + ); } else { printf("No update needed\n"); } } } else { + bootloader_display_header(); bootloader_display_line( 2, "Failed to mount filesystem", 0xffff ); printf("Failed to mount the external flash\n"); + + bootloader_display_line(4, "Trying to boot", 0xffff); } printf("Trying to boot\n"); - bootloader_display_line(4, "Trying to boot", 0xffff); boot((uintptr_t *)PARTITION_START); diff --git a/bootloader/meson.build b/bootloader/meson.build index 9a863cdfa8de1bb99c79bad8f783b4cd29a9e239..23c387943125a45c20b40b685edb22fc5f2936dc 100644 --- a/bootloader/meson.build +++ b/bootloader/meson.build @@ -1,5 +1,18 @@ name = 'bootloader' +splash_screen = custom_target( + 'splash-screen.h', + output: 'splash-screen.h', + input: 'splash-screen.png', + command: [ + python3, + meson.current_source_dir() + '../tools/bootloader-image.py', + '-n', 'splash', + '@INPUT@', + '@OUTPUT@', + ], +) + executable( name + '.elf', 'main.c', @@ -7,6 +20,7 @@ executable( 'bootloader-display.c', 'bootloader-usb.c', 'crc16-ccitt.c', + splash_screen, dependencies: [ libcard10, max32665_startup_boot, diff --git a/bootloader/splash-screen.png b/bootloader/splash-screen.png new file mode 100644 index 0000000000000000000000000000000000000000..02a632049624f5b96e54ac2d4c7d408fd41ae796 Binary files /dev/null and b/bootloader/splash-screen.png differ diff --git a/tools/bootloader-image.py b/tools/bootloader-image.py new file mode 100755 index 0000000000000000000000000000000000000000..f2ce2bf8b974578217b3f6d4ebc9a3bb080fed3a --- /dev/null +++ b/tools/bootloader-image.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 +import argparse +import os +from PIL import Image + + +def main() -> None: + parser = argparse.ArgumentParser( + description="""\ +\"Compress\" an image for the boot-splash. +The boot-splash is a two-color image.""" + ) + + parser.add_argument("image", help="Boot-splash image") + parser.add_argument("-n", "--name", help="Name of the data-block") + parser.add_argument("output", help="Output file name") + + args = parser.parse_args() + + im = Image.open(args.image) + + assert im.size[0] == 160, "Image must be 160 pixels wide" + assert im.size[1] == 80, "Image must be 80 pixels high)" + + if args.name is not None: + name = args.name + else: + name = os.path.splitext(os.path.basename(args.image))[0].replace("-", "_") + + with open(args.output, "w") as f: + tmp = """\ +#include <stdint.h> + +/* + * This is the splash-screen image, compressed using a very primitive algorithm: + * + * Each byte encodes up to 127 pixels in either white or black. The most + * significant bit determines the color, the remaining 7 bits determine the + * amount. + */ +const uint8_t {name}[] = {{ +""" + f.write(tmp.format(name=name)) + + total = 0 + start = 0 + previous_white = False + for i in range(160 * 80): + x = i % 160 + y = i // 160 + white = im.getpixel((x, y))[0] > 0 + + if white != previous_white or i - start == 127: + length = i - start + assert length < 128, "Internal error" + value = (length & 0x7F) | (0x80 if previous_white else 0x00) + + tmp = """\ + /* {length} pixels in {color} */ + 0x{value:x}, +""" + f.write( + tmp.format( + length=length, + color="white" if previous_white else "black", + value=value, + ) + ) + + previous_white = white + start = i + total += length + + tmp = """\ +}}; +""" + f.write(tmp.format()) + + assert total < (160 * 80), "Internal error" + + +if __name__ == "__main__": + main()