diff --git a/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_3.overlay b/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_3.overlay index c03240db..5818d340 100644 --- a/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_3.overlay +++ b/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_3.overlay @@ -132,10 +132,10 @@ / { fstab { compatible = "zephyr,fstab"; - lvgl_fs: lvgl_fs { + lvgl_lfs: lvgl_lfs { compatible = "zephyr,fstab,littlefs"; - mount-point = "/lvgl_fs"; - partition = <&lvgl_fs_partition>; + mount-point = "/lvgl_lfs"; + partition = <&lvgl_lfs_partition>; automount; read-size = <1024>; prog-size = <512>; @@ -152,9 +152,13 @@ #address-cells = <1>; #size-cells = <1>; - lvgl_fs_partition: partition@0 { - label = "storagelvgl"; + lvgl_lfs_partition: partition@0 { + label = "lvgl_lfs_partition"; reg = <0x00000000 0x00200000>; }; + lvgl_raw_partition: partition@200000 { + label = "lvgl_raw_partition"; + reg = <0x00200000 0x00200000>; + }; }; }; \ No newline at end of file diff --git a/app/prj.conf b/app/prj.conf index f70d3304..e4fad9d8 100644 --- a/app/prj.conf +++ b/app/prj.conf @@ -114,6 +114,7 @@ CONFIG_FLASH=y CONFIG_FLASH_MAP=y CONFIG_MPU_ALLOW_FLASH_WRITE=y CONFIG_FLASH_PAGE_LAYOUT=y +CONFIG_FLASH_MAP_LABELS=y # Filesystem #CONFIG_FILE_SYSTEM=y diff --git a/app/scripts/create_lvgl_image.py b/app/scripts/create_custom_resource_image.py similarity index 92% rename from app/scripts/create_lvgl_image.py rename to app/scripts/create_custom_resource_image.py index 5657a9a1..dffe4680 100644 --- a/app/scripts/create_lvgl_image.py +++ b/app/scripts/create_custom_resource_image.py @@ -17,9 +17,8 @@ len:uint32 ... ''' -# TODO add total length ijn the beginning? -def create_fs_image(img_filename, source_dir, block_size=4096): +def create_custom_raw_fs_image(img_filename, source_dir, block_size=4096): table = {} offset = 0 files_image = bytearray() @@ -67,4 +66,4 @@ def create_fs_image(img_filename, source_dir, block_size=4096): block_size = args.block_size source_dir = args.source - create_fs_image(img_filename, source_dir, block_size) \ No newline at end of file + create_custom_raw_fs_image(img_filename, source_dir, block_size) \ No newline at end of file diff --git a/app/scripts/create_resource_fs.py b/app/scripts/create_littlefs_resouce_image.py similarity index 90% rename from app/scripts/create_resource_fs.py rename to app/scripts/create_littlefs_resouce_image.py index 35617764..969181da 100644 --- a/app/scripts/create_resource_fs.py +++ b/app/scripts/create_littlefs_resouce_image.py @@ -2,7 +2,7 @@ import argparse from littlefs import LittleFS -def create_fs_image(img_filename, img_size, block_size, read_size, prog_size, name_max, file_max, attr_max, source_dir, disk_version): +def create_littlefs_fs_image(img_filename, img_size, block_size, read_size, prog_size, name_max, file_max, attr_max, source_dir, disk_version): block_count = img_size // block_size if block_count * block_size != img_size: print("image size should be a multiple of block size") @@ -76,4 +76,4 @@ def create_fs_image(img_filename, img_size, block_size, read_size, prog_size, na attr_max = args.attr_max source_dir = args.source - create_fs_image(img_filename, img_size, block_size, read_size, prog_size, name_max, file_max, attr_max, source_dir, args.disk_version) \ No newline at end of file + create_littlefs_fs_image(img_filename, img_size, block_size, read_size, prog_size, name_max, file_max, attr_max, source_dir, args.disk_version) \ No newline at end of file diff --git a/app/scripts/rtt_flash_loader.py b/app/scripts/rtt_flash_loader.py index 24133449..3ad5c88c 100644 --- a/app/scripts/rtt_flash_loader.py +++ b/app/scripts/rtt_flash_loader.py @@ -40,11 +40,12 @@ def read_rtt(jlink): raise -def dump_flash(jlink, file): +def dump_flash(jlink, file, partition): print(jlink, file) with open(file, "wb") as file: try: - bytes = list(bytearray("DUMP_START", "utf-8")) + bytes = list(bytearray(f"DUMP_START:{partition}", "utf-8")) + print("Start:", bytes) jlink.rtt_write(2, bytes) time.sleep(2) @@ -78,12 +79,12 @@ def dump_flash(jlink, file): raise -def load_data(jlink, file): +def load_data(jlink, file, partition): try: buffer_size = 4096 * 2 counter = 0 print("FILENAME", file) - bytes = list(bytearray("LOADER_START", "utf-8")) + bytes = list(bytearray(f"LOADER_START:{partition}", "utf-8")) jlink.rtt_write(2, bytes) time.sleep(2) @@ -129,7 +130,7 @@ def load_data(jlink, file): raise -def run_loader(target_device, file, read_data_only=False): +def run_loader(target_device, file, partition, read_data_only=False): """Creates connection to target via RTT and either writes a file or reads from flash. Args: @@ -191,9 +192,9 @@ def run_loader(target_device, file, read_data_only=False): work_thread = None if read_data_only: - work_thread = Thread(target=dump_flash, args=(jlink, file)) + work_thread = Thread(target=dump_flash, args=(jlink, file, partition)) else: - work_thread = Thread(target=load_data, args=(jlink, file)) + work_thread = Thread(target=load_data, args=(jlink, file, partition)) work_thread.daemon = True work_thread.start() work_thread.join() @@ -218,14 +219,16 @@ def run_loader(target_device, file, read_data_only=False): "--file", help="Binary file to send to target or in case of read the filename to store the data in.", ) + parser.add_argument( + "-p", "--partition", type=int, help="Label of partition in DTS to write to.", default="lvgl_raw_partition" + ) parser.add_argument( "--read_data", help="Read data from flash block", action="store_true" ) - parser.add_argument( "-s", "--serial", type=int, help="Serial number of ZSWatch attached debugger" ) args = parser.parse_args() - sys.exit(run_loader(args.target_cpu, args.file, args.read_data)) + sys.exit(run_loader(args.target_cpu, args.file, partition, args.read_data)) diff --git a/app/scripts/upload_fs_west_command.py b/app/scripts/upload_fs_west_command.py index 24a178a7..25c097f6 100644 --- a/app/scripts/upload_fs_west_command.py +++ b/app/scripts/upload_fs_west_command.py @@ -1,9 +1,11 @@ -from west.commands import WestCommand # your extension must subclass this -from west import log # use this for user output -#from create_resource_fs import create_fs_image +from west.commands import WestCommand +from west import log +from create_custom_resource_image import create_custom_raw_fs_image from rtt_flash_loader import run_loader -from create_lvgl_image import create_fs_image +from create_littlefs_resouce_image import create_littlefs_fs_image import sys +import os +from pathlib import Path class UploadFsWestCommand(WestCommand): @@ -19,7 +21,11 @@ def do_add_parser(self, parser_adder): help=self.help, description=self.description) + parser.add_argument('--type', type=str, default='raw', help='raw or fs. fs to load littlefs image, raw to load custom binary') parser.add_argument('--read_file', type=str, help='If set dump flash to this filename') + parser.add_argument( + "-p", "--partition", type=str, help="Label of partition in DTS to write to. Leave blank to use auto guess name.", + ) return parser def do_run(self, args, unknown_args): @@ -32,12 +38,21 @@ def do_run(self, args, unknown_args): name_max = 255 file_max = 0 attr_max = 0 - source_dir = "../src/images/binaries/" disk_version = "2.0" filename = "lvgl_resources" + partition = args.partition + zephyr_base = Path(os.environ.get("ZEPHYR_BASE")) + images_path = f'{zephyr_base.parent.absolute()}/app/src/images/binaries' + print(images_path) if (args.read_file): filename = args.read_file if args.read_file is None: - create_fs_image(img_filename, source_dir, block_size) - #create_fs_image(img_filename, img_size, block_size, read_size, prog_size, name_max, file_max, attr_max, source_dir, disk_version) - sys.exit(run_loader("nRF5340_XXAA", filename, args.read_file)) + if args.type == 'raw': + source_dir = f"{images_path}/S" + partition = partition if partition else "lvgl_raw_partition" + create_custom_raw_fs_image(img_filename, source_dir, block_size) + elif args.type == 'lfs': + source_dir = f"{images_path}/lvgl_lfs" + partition = partition if partition else "lvgl_lfs_partition" + create_littlefs_fs_image(img_filename, img_size, block_size, read_size, prog_size, name_max, file_max, attr_max, source_dir, disk_version) + sys.exit(run_loader("nRF5340_XXAA", filename, partition, args.read_file)) diff --git a/app/src/applications/qr_code/qr_code_ui.c b/app/src/applications/qr_code/qr_code_ui.c index 814b9c7e..04543fec 100644 --- a/app/src/applications/qr_code/qr_code_ui.c +++ b/app/src/applications/qr_code/qr_code_ui.c @@ -20,7 +20,7 @@ void qr_code_ui_show(lv_obj_t *root) lv_obj_t *img = lv_img_create(root_page); #ifdef CONFIG_LV_Z_USE_FILESYSTEM - lv_img_set_src(img, "S:x_ray.bin"); + lv_img_set_src(img, "/lvgl_lfs/qr_code.bin"); #else LV_IMG_DECLARE(round_qr_full); lv_img_set_src(img, &round_qr_full); diff --git a/app/src/applications/x_ray/x_ray_ui.c b/app/src/applications/x_ray/x_ray_ui.c index 7505b722..5cfe905f 100644 --- a/app/src/applications/x_ray/x_ray_ui.c +++ b/app/src/applications/x_ray/x_ray_ui.c @@ -18,10 +18,13 @@ void x_ray_ui_show(lv_obj_t *root) // Does not loog very good on the round display. lv_obj_set_scrollbar_mode(root_page, LV_SCROLLBAR_MODE_OFF); - LV_IMG_DECLARE(x_ray); - lv_obj_t *img = lv_img_create(root_page); +#ifdef CONFIG_LV_Z_USE_FILESYSTEM + lv_img_set_src(img, "S:x_ray.bin"); +#else + LV_IMG_DECLARE(x_ray); lv_img_set_src(img, &x_ray); +#endif lv_obj_align(img, LV_ALIGN_CENTER, 0, 0); lv_obj_set_size(img, 240, 240); } diff --git a/app/src/images/binaries/README.md b/app/src/images/binaries/README.md new file mode 100644 index 00000000..e6f1330c --- /dev/null +++ b/app/src/images/binaries/README.md @@ -0,0 +1,13 @@ +# External SPI Flash resource storage + +## Folder options + +- `filename.bin` put into `lvgl_lfs` goes into littlefs filesystem into one partition of external flash. + - Usage: `lv_img_set_src(img, "S:filename.bin");` + - Upload: `west upload_fs --type fs` +- `filename.bin` put into `S` goes into a basic readonly filesystem into one other partition of external flash. + - Usage: `lv_img_set_src(img, "/lvgl_lfs/filename.bin");` + - Upload: `west upload_fs --type fs` + +## Which one to use? +For now those options are mostly for experimentation. Using littlefs may be faster due to littlefs caching. However the other custom filesystem allows us to do more optimization for ZSWatch in the future. \ No newline at end of file diff --git a/app/src/zsw_lvgl_spi_decoder.c b/app/src/zsw_lvgl_spi_decoder.c index ea10e143..7af5e95b 100644 --- a/app/src/zsw_lvgl_spi_decoder.c +++ b/app/src/zsw_lvgl_spi_decoder.c @@ -10,7 +10,7 @@ #define SPI_FLASH_SECTOR_SIZE 4096 -#define FLASH_PARTITION_NAME lvgl_fs_partition +#define FLASH_PARTITION_NAME lvgl_raw_partition #define FLASH_PARTITION_ID FIXED_PARTITION_ID(FLASH_PARTITION_NAME) #define FLASH_PARTITION_DEVICE FIXED_PARTITION_DEVICE(FLASH_PARTITION_NAME) diff --git a/app/src/zsw_rtt_flash_loader.c b/app/src/zsw_rtt_flash_loader.c index f5f8f9d4..0e01b85f 100644 --- a/app/src/zsw_rtt_flash_loader.c +++ b/app/src/zsw_rtt_flash_loader.c @@ -13,12 +13,6 @@ LOG_MODULE_REGISTER(zsw_rtt_flash_loader); #define SPI_FLASH_SECTOR_SIZE 4096 -#define FLASH_PARTITION_NAME lvgl_fs_partition - -#define FLASH_PARTITION_ID FIXED_PARTITION_ID(FLASH_PARTITION_NAME) -#define FLASH_PARTITION_DEVICE FIXED_PARTITION_DEVICE(FLASH_PARTITION_NAME) -#define FLASH_PARTITION_OFFSET FIXED_PARTITION_OFFSET(FLASH_PARTITION_NAME) - #define RTT_RECIVE_BUFFER_SIZE SPI_FLASH_SECTOR_SIZE #define TRANSFER_TIMEOUT_MS 5000 @@ -30,6 +24,11 @@ LOG_MODULE_REGISTER(zsw_rtt_flash_loader); #define RTT_CHANNEL_NAME "FlashLoaderChannel" +struct flash_partition_search_user_data { + int found_part_id; + char* search_label_name; +}; + static void rtt_load_flash_thread(void *, void *, void *); static void rtt_dump_flash_thread(void *, void *, void *); @@ -80,11 +79,47 @@ static int loader_read_flash(int partition_id, int buf_idx, uint8_t* buf, int le return 0; } -static bool check_start_sequence(uint8_t* buf, uint32_t len) +static void flash_part_iter_cb(const struct flash_area *fa, void *user_data) { - if (memcmp(buf, START_LOAD_SEQUENCE, strlen(START_LOAD_SEQUENCE)) == 0) { - printk("Start sequence received\n"); - return true; + struct flash_partition_search_user_data* search_data = (struct flash_partition_search_user_data*)user_data; + const char *fa_label = flash_area_label(fa); + if (fa_label == NULL) { + return; + } + if (strcmp(search_data->search_label_name, fa_label) == 0) { + search_data->found_part_id = fa->fa_id; + } +} + +static int find_partition_id_from_label(char* label) +{ + struct flash_partition_search_user_data search_data = { + .search_label_name = label, + .found_part_id = -ENOENT + }; + + flash_area_foreach(flash_part_iter_cb, &search_data); + + if (search_data.found_part_id >= 0) { + LOG_DBG("Found target partition id: %d", search_data.found_part_id); + } else { + LOG_ERR("Partition label not found: %s", label + 1); + } + + return search_data.found_part_id; +} + +static bool check_start_sequence(uint8_t* buf, uint32_t len, int* partition_id) +{ + char* partition_label; + if (strncmp(buf, START_LOAD_SEQUENCE, strlen(START_LOAD_SEQUENCE)) == 0) { + partition_label = strchr(buf, ':'); + if (partition_label) { + *partition_id = find_partition_id_from_label(partition_label + 1); + return true; + } else { + LOG_WRN("No partition label found"); + } } return false; @@ -100,26 +135,45 @@ static bool check_end_sequence(uint8_t* buf, uint32_t len) return false; } -static bool check_read_sequence(uint8_t* buf, uint32_t len) +static bool check_read_sequence(uint8_t* buf, uint32_t len, int* partition_id) { + char* partition_label; if (memcmp(buf, DUMP_FLASH_SEQUENCE, strlen(DUMP_FLASH_SEQUENCE)) == 0) { - printk("Read sequence received\n"); - return true; + partition_label = strchr(buf, ':'); + if (partition_label) { + *partition_id = find_partition_id_from_label(partition_label + 1); + return true; + } else { + LOG_WRN("No partition label found"); + } } return false; } -static void rtt_load_flash_thread(void *, void *, void *) +static void rtt_load_flash_thread(void * partition_id_param, void *, void *) { int ret; int len; int block_index = 0; int buffer_index = 0; int bytes_flashed = 0; + uint8_t partition_id = (uint8_t)((uint32_t)partition_id_param); uint32_t len_to_read; uint32_t last_activity_ms = k_uptime_get_32(); + ret = flash_area_open(partition_id, &flash_area); + + if (ret != 0) { + LOG_ERR("FAIL: unable to find flash area %d: %d\n", (int)partition_id, ret); + return; + } + +#ifndef CONFIG_ERASE_PROGRESSIVELY + ret = flash_area_erase(flash_area, 0, flash_area->fa_size); + LOG_WRN("Erasing flash area ... %d", ret); +#endif + while (1) { len_to_read = sizeof(data_buf) - buffer_index; len = SEGGER_RTT_Read(CONFIG_RTT_TRANSFER_CHANNEL, data_buf, len_to_read); @@ -127,7 +181,7 @@ static void rtt_load_flash_thread(void *, void *, void *) if (len <= 0) { if (k_uptime_get_32() - last_activity_ms > TRANSFER_TIMEOUT_MS) { printk("RTT Transfer timeout. Aborting.\n"); - return; + break; } k_msleep(100); continue; @@ -136,12 +190,12 @@ static void rtt_load_flash_thread(void *, void *, void *) last_activity_ms = k_uptime_get_32(); if (check_end_sequence(data_buf, len)) { printk("RTT Transfer done: %d bytes flashed\n", bytes_flashed); - return; + break; } else if (len == len_to_read) { - ret = loader_write_flash(FLASH_PARTITION_ID, block_index, data_buf, sizeof(data_buf)); + ret = loader_write_flash((int)partition_id, block_index, data_buf, sizeof(data_buf)); if (ret != 0) { printk("loader_write_flash failed: %dn", ret); - return; + break; } block_index++; buffer_index = 0; @@ -156,22 +210,37 @@ static void rtt_load_flash_thread(void *, void *, void *) } } } + + flash_area_close(flash_area); } -static void rtt_dump_flash_thread(void *, void *, void *) +static void rtt_dump_flash_thread(void * partition_id_param, void *, void *) { int ret; int len; int buffer_index = 0; int bytes_sent = 0; + uint8_t partition_id = (uint8_t)((uint32_t)partition_id_param); uint32_t len_to_send; uint32_t last_activity_ms = k_uptime_get_32(); - while (buffer_index < FIXED_PARTITION_SIZE(FLASH_PARTITION_NAME) / SPI_FLASH_SECTOR_SIZE) { - ret = loader_read_flash(FLASH_PARTITION_ID, buffer_index, data_buf, sizeof(data_buf)); + ret = flash_area_open(partition_id, &flash_area); + + if (ret != 0) { + LOG_ERR("FAIL: unable to find flash area %d: %d\n", (int)partition_id, ret); + return; + } + +#ifndef CONFIG_ERASE_PROGRESSIVELY + ret = flash_area_erase(flash_area, 0, flash_area->fa_size); + LOG_WRN("Erasing flash area ... %d", ret); +#endif + + while (buffer_index < flash_area->fa_size / SPI_FLASH_SECTOR_SIZE) { + ret = loader_read_flash((int)partition_id, buffer_index, data_buf, sizeof(data_buf)); if (ret != 0) { printk("loader_read_flash failed: %dn", ret); - return; + break; } len_to_send = sizeof(data_buf); while (len_to_send > 0) { @@ -198,46 +267,13 @@ static void rtt_dump_flash_thread(void *, void *, void *) k_msleep(2000); SEGGER_RTT_Write(CONFIG_RTT_TRANSFER_CHANNEL, READ_DONE_SEQUENCE, strlen(READ_DONE_SEQUENCE)); k_msleep(2000); + + flash_area_close(flash_area); } int zsw_rtt_flash_loader_start(void) { - int rc; - int size; - int pages; - struct flash_pages_info info; - - const struct device *flash_device = FLASH_PARTITION_DEVICE; - - if (!device_is_ready(flash_device)) { - printk("Flash device %s is not ready\n", flash_device->name); - return 0; - } - - size = flash_get_write_block_size(flash_device); - pages = flash_get_page_count(flash_device); - - printk("Size: %d, pages: %d, offset: %d\n", size, pages, FLASH_PARTITION_OFFSET); - - rc = flash_get_page_info_by_offs(flash_device, FLASH_PARTITION_OFFSET, &info); - if (rc) { - printk("Unable to get page info\n"); - return 0; - } - printk("Info: %d, %d, %d\n", info.size, info.index, (int)info.start_offset); - - rc = flash_area_open(FLASH_PARTITION_ID, &flash_area); - - if (rc != 0) { - printk("FAIL: unable to find flash area %u: %d\n", FLASH_PARTITION_ID, rc); - return 0; - } - - printk("Sector size: %d, sector_count: %d\n", info.size, FIXED_PARTITION_SIZE(FLASH_PARTITION_NAME) / info.size); -#ifndef CONFIG_ERASE_PROGRESSIVELY - rc = flash_area_erase(flash_area, 0, flash_area->fa_size); - printk("Erasing flash area ... %d\n", rc); -#endif + int partition_id; SEGGER_RTT_ConfigUpBuffer(CONFIG_RTT_TRANSFER_CHANNEL, RTT_CHANNEL_NAME, up_buffer, sizeof(up_buffer), @@ -255,16 +291,16 @@ int zsw_rtt_flash_loader_start(void) continue; } - if (check_start_sequence(data_buf, len)) { - printk("Start sequence received\n"); + if (check_start_sequence(data_buf, len, &partition_id)) { + printk("Load sequence received: %s partition ID: %d\n", data_buf, partition_id); k_tid_t tid = k_thread_create(&rtt_work_thread, rtt_work_thread_stack, K_KERNEL_STACK_SIZEOF(rtt_work_thread_stack), - rtt_load_flash_thread, NULL, NULL, NULL, CONFIG_NUM_COOP_PRIORITIES-2, 0, K_NO_WAIT); + rtt_load_flash_thread, (void*)partition_id, NULL, NULL, CONFIG_NUM_COOP_PRIORITIES-2, 0, K_NO_WAIT); k_thread_join(tid, K_FOREVER); printk("Load thread done\n"); - } else if (check_read_sequence(data_buf, len)) { - printk("Read sequence received\n"); + } else if (check_read_sequence(data_buf, len, &partition_id)) { + printk("Read sequence received: %s partition ID: %d\n", data_buf, partition_id); k_tid_t tid = k_thread_create(&rtt_work_thread, rtt_work_thread_stack, K_KERNEL_STACK_SIZEOF(rtt_work_thread_stack), - rtt_dump_flash_thread, NULL, NULL, NULL, CONFIG_NUM_COOP_PRIORITIES-2, 0, K_NO_WAIT); + rtt_dump_flash_thread, (void*)partition_id, NULL, NULL, CONFIG_NUM_COOP_PRIORITIES-2, 0, K_NO_WAIT); k_thread_join(tid, K_FOREVER); printk("Read thread done\n"); } else {