diff --git a/.gitignore b/.gitignore index ee7b7522..191cdf78 100644 --- a/.gitignore +++ b/.gitignore @@ -16,8 +16,6 @@ build* # Built Visual Studio Code Extensions *.vsix -*.bin - # Zephyr workspace /.west /bootloader diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 00000000..28d45ab5 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,17 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "Upload Raw FS", + "type": "shell", + "command": "west upload_fs --type raw" + }, + { + "label": "Upload LittleFS", + "type": "shell", + "command": "west upload_fs --type lfs" + } + ] +} \ No newline at end of file diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index bfc71c94..5e8811bd 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -57,7 +57,9 @@ target_sources(app PRIVATE src/ui/notification/zsw_popup_notifcation.c) target_sources(app PRIVATE src/ui/popup/zsw_popup_window.c) target_sources(app PRIVATE src/ui/utils/zsw_ui_utils.c) -target_sources(app PRIVATE src/zsw_flash.c) +target_sources_ifdef(CONFIG_SPI_FLASH_LOADER app PRIVATE src/zsw_rtt_flash_loader.c) +target_sources_ifdef(CONFIG_FILE_SYSTEM_LITTLEFS app PRIVATE src/zsw_filesystem.c) +target_sources_ifdef(CONFIG_FILE_SYSTEM_LITTLEFS app PRIVATE src/zsw_lvgl_spi_decoder.c) if (DFU_BUILD) target_sources(app PRIVATE src/dfu.c) diff --git a/app/Kconfig b/app/Kconfig index e1e75f3e..51c1f1fb 100644 --- a/app/Kconfig +++ b/app/Kconfig @@ -32,12 +32,12 @@ menu "ZSWatch" config APPLICATIONS_USE_QR_CODE bool prompt "Activate the application 'QR-Code'" - default n + default y if LV_Z_USE_FILESYSTEM config APPLICATIONS_USE_X_RAY bool prompt "Activate the application 'X-Ray'" - default n + default y if LV_Z_USE_FILESYSTEM config APPLICATIONS_USE_ZDS bool @@ -94,6 +94,34 @@ menu "ZSWatch" endmenu +menu "SPI RTT Flash Loader" + config SPI_FLASH_LOADER + bool + prompt "Enable SPI flash loader" + default y + select USE_SEGGER_RTT + #select SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL + help + "Enable SPI flash loader" +if SPI_FLASH_LOADER + config ERASE_PROGRESSIVELY + bool + depends on SPI_FLASH_LOADER + prompt "Erase sectors one by one when writing instead of full partition before starting." + default y + help + "If not writing to the full partition this will be faster" + + config RTT_TRANSFER_CHANNEL + int + depends on SPI_FLASH_LOADER + prompt "The RTT channel to use for transfer of data to and form flash" + default 2 + help + "If RTT logging is enabled, channel 0 is used for this, hence avoid 1 in this scenario." +endif +endmenu + rsource "drivers/Kconfig" module = APP diff --git a/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_common.dts b/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_common.dts index b7ff2ad7..b1c8270e 100644 --- a/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_common.dts +++ b/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_common.dts @@ -16,7 +16,6 @@ zephyr,bt-hci-rpmsg-ipc = &ipc0; zephyr,ieee802154 = &ieee802154; zephyr,display = &gc9a01; - //zephyr,keyboard-scan = &cst816s; }; vbatt { @@ -38,11 +37,11 @@ }; lvgl_pointer { - compatible = "zephyr,lvgl-pointer-input"; - input = <&cst816s>; + compatible = "zephyr,lvgl-pointer-input"; + input = <&cst816s>; swap-xy; - invert-x; - }; + invert-x; + }; aliases { display-blk = &display_blk; @@ -133,11 +132,11 @@ pinctrl-names = "default", "sleep"; cst816s: cst816s@15 { - compatible = "hynitron,cst816s"; - reg = <0x15>; - irq-gpios = <&gpio1 0 GPIO_ACTIVE_LOW>; - rst-gpios = <&gpio0 20 GPIO_ACTIVE_LOW>; - }; + compatible = "hynitron,cst816s"; + reg = <0x15>; + irq-gpios = <&gpio1 0 GPIO_ACTIVE_LOW>; + rst-gpios = <&gpio0 20 GPIO_ACTIVE_LOW>; + }; }; /* UART0 GPIOs can be configured for other use-cases */ @@ -220,5 +219,39 @@ zephyr_udc0: &usbd { }; }; +/ { + sram@2007FFFF { + compatible = "zephyr,memory-region", "mmio-sram"; + reg = <0x2007FFFF 0x1>; + zephyr,memory-region = "RetainedMem"; + status = "okay"; + + retainedmem { + compatible = "zephyr,retained-ram"; + status = "okay"; + #address-cells = <1>; + #size-cells = <1>; + + retention0: retention@0 { + compatible = "zephyr,retention"; + status = "okay"; + reg = <0x0 0x1>; + }; + }; + }; + + chosen { + zephyr,boot-mode = &retention0; + }; +}; + +/* Reduce SRAM0 usage by 4 byte to account for non-init area +* Even though boot mode is only one byte, nrfjprog only allows +* 4 byte writes, hence reserve 4. +*/ +&sram0 { + reg = <0x20000000 0x7FFFC>; +}; + /* Include partition configuration file */ #include "zswatch_nrf5340_partition_conf.dts" diff --git a/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_3.conf b/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_3.conf index c3db9be4..9b228a84 100644 --- a/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_3.conf +++ b/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_3.conf @@ -11,5 +11,14 @@ CONFIG_SPI_NOR_SFDP_RUNTIME=y CONFIG_SPI_NOR_IDLE_IN_DPD=y CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 -# DIsplay RST is connected to NFC GPIO -CONFIG_NFCT_PINS_AS_GPIOS=y \ No newline at end of file +# Display RST is connected to NFC GPIO +CONFIG_NFCT_PINS_AS_GPIOS=y + +CONFIG_MPU_ALLOW_FLASH_WRITE=y + +CONFIG_FILE_SYSTEM=y +CONFIG_FILE_SYSTEM_LITTLEFS=y +CONFIG_LV_Z_USE_FILESYSTEM=y +CONFIG_LV_IMG_CACHE_DEF_SIZE=10 + +CONFIG_FS_LITTLEFS_FC_HEAP_SIZE=16384 \ No newline at end of file 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 3c8e4807..2516fc96 100644 --- a/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_3.overlay +++ b/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_3.overlay @@ -21,6 +21,7 @@ / { chosen { nordic,pm-ext-flash = &at25sl128a; + zephyr,settings-partition = &settings_partition; }; aliases { @@ -144,4 +145,42 @@ t-exit-dpd = <3000>; }; +}; + +/ { + fstab { + compatible = "zephyr,fstab"; + lvgl_lfs: lvgl_lfs { + compatible = "zephyr,fstab,littlefs"; + mount-point = "/lvgl_lfs"; + partition = <&lvgl_lfs_partition>; + automount; + read-size = <1024>; + prog-size = <512>; + cache-size = <4096>; + lookahead-size = <4096>; + block-cycles = <512>; + }; + }; +}; + +&at25sl128a { + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + lvgl_lfs_partition: partition@0 { + label = "lvgl_lfs_partition"; + reg = <0x00000000 0x00200000>; + }; + lvgl_raw_partition: partition@200000 { + label = "lvgl_raw_partition"; + reg = <0x00200000 0x00200000>; + }; + settings_partition: partition@400000 { + label = "settings_partition"; + reg = <0x400000 0x100000 >; + }; + }; }; \ No newline at end of file diff --git a/app/boards/native_posix.conf b/app/boards/native_posix.conf index b8678265..3551b65f 100644 --- a/app/boards/native_posix.conf +++ b/app/boards/native_posix.conf @@ -16,6 +16,7 @@ CONFIG_MAX30101=n CONFIG_APDS9306=n CONFIG_APDS9306_IS_APDS9306_065=n CONFIG_MAX30101_MULTI_LED_MODE=n +CONFIG_APDS9306=n CONFIG_PINCTRL=n CONFIG_SPI=n @@ -42,3 +43,4 @@ CONFIG_LOG_PRINTK=y CONFIG_LOG=y CONFIG_DEBUG_OPTIMIZATIONS=y +CONFIG_SPI_FLASH_LOADER=n diff --git a/app/prj.conf b/app/prj.conf index b94d3f28..76fdf798 100644 --- a/app/prj.conf +++ b/app/prj.conf @@ -1,5 +1,5 @@ CONFIG_LV_Z_MEM_POOL_NUMBER_BLOCKS=8 -CONFIG_MAIN_STACK_SIZE=1024 +CONFIG_MAIN_STACK_SIZE=2048 CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096 CONFIG_HEAP_MEM_POOL_SIZE=25000 CONFIG_NEWLIB_LIBC_FLOAT_PRINTF=y @@ -10,6 +10,10 @@ CONFIG_REBOOT=y CONFIG_DISPLAY=y CONFIG_BASE64=y +CONFIG_RETAINED_MEM=y +CONFIG_RETENTION=y +CONFIG_RETENTION_BOOT_MODE=y + CONFIG_I2C=y CONFIG_SENSOR=y CONFIG_ADC=y @@ -110,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_custom_resource_image.py b/app/scripts/create_custom_resource_image.py new file mode 100644 index 00000000..196885f4 --- /dev/null +++ b/app/scripts/create_custom_resource_image.py @@ -0,0 +1,90 @@ +import os +import argparse +from struct import * + +MAX_FILE_NAME = 16 +FILE_TABLE_MAX_LEN = 1024 +""" +magic_number:uint32 +header_len:uint32 +total_length:uint32 +num_files:uint32 +filename[MAX_FILE_NAME + 1] +offset:uint32 +len:uint32 +filename[MAX_FILE_NAME + 1] +offset:uint32 +len:uint32 +... +""" + + +def create_custom_raw_fs_image(img_filename, source_dir, block_size=4096): + table = {} + offset = 0 + files_image = bytearray() + header_images = bytearray() + for root, dirs, files in os.walk(source_dir): + print(f"root {root} dirs {dirs} files {files}") + for filename in files: + path = os.path.join(root, filename) + relpath = os.path.relpath(path, start=source_dir) + print(f"Adding {path}") + with open(path, "rb") as infile: + files_image.extend(infile.read()) + table[filename] = {"offset": offset, "len": infile.tell()} + offset = offset + infile.tell() + print(table) + for name, data in table.items(): + if len(name) <= MAX_FILE_NAME: + header_images = header_images + pack( + f"<{MAX_FILE_NAME}sII", + bytes(name, "utf-8"), + data["offset"], + data["len"], + ) + else: + print("Filename to long, skipping", name, len(name)) + + # Insert dummy values as header length and total length so we can get the size of the header + fake_header = header_image = ( + pack(" FILE_TABLE_MAX_LEN: + print( + "File table is to big, increase the size on target size", + FILE_TABLE_MAX_LEN, + "<", + len(header_images), + ) + exit(1) + # Add header length + with open(img_filename, "wb") as f: + f.write(real_header) + f.write(files_image) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--img-filename", default="littlefs.img") + parser.add_argument("--block-size", type=int, default=4096) + parser.add_argument("source") + args = parser.parse_args() + + img_filename = args.img_filename + block_size = args.block_size + source_dir = args.source + + create_custom_raw_fs_image(img_filename, source_dir, block_size) diff --git a/app/scripts/create_littlefs_resouce_image.py b/app/scripts/create_littlefs_resouce_image.py new file mode 100644 index 00000000..73158c1a --- /dev/null +++ b/app/scripts/create_littlefs_resouce_image.py @@ -0,0 +1,102 @@ +import os +import argparse +from littlefs import LittleFS + + +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") + exit(1) + + if disk_version is None: + disk_version = 0 # 0 means the latest + else: + # "2.1" -> 0x00020001 + try: + major, minor = disk_version.split(".") + disk_version = int(major) * 0x10000 + int(minor) + except: + print(f"failed to parse disk version: {disk_version}") + exit(1) + + fs = LittleFS( + block_size=block_size, + block_count=block_count, + read_size=read_size, + prog_size=prog_size, + name_max=name_max, + file_max=file_max, + attr_max=attr_max, + disk_version=disk_version, + ) + + # Note: path component separator etc are assumed to be compatible + # between littlefs and host. + for root, dirs, files in os.walk(source_dir): + print(f"root {root} dirs {dirs} files {files}") + for dir in dirs: + path = os.path.join(root, dir) + relpath = os.path.relpath(path, start=source_dir) + print(f"Mkdir {relpath}") + fs.mkdir(relpath) + for f in files: + path = os.path.join(root, f) + relpath = os.path.relpath(path, start=source_dir) + print(f"Copying {path} to {relpath}") + with open(path, "rb") as infile: + with fs.open(relpath, "wb") as outfile: + outfile.write(infile.read()) + + with open(img_filename, "wb") as f: + f.write(fs.context.buffer) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--img-filename", default="littlefs.img") + parser.add_argument("--img-size", type=int, default=2 * 1024 * 1024) + parser.add_argument("--block-size", type=int, default=4096) + parser.add_argument("--read-size", type=int, default=256) + parser.add_argument("--prog-size", type=int, default=256) + # Note: 0 means to use the build-time default. + parser.add_argument("--name-max", type=int, default=0) + parser.add_argument("--file-max", type=int, default=0) + parser.add_argument("--attr-max", type=int, default=0) + parser.add_argument("--disk-version", default=None) + parser.add_argument("source") + args = parser.parse_args() + + img_filename = args.img_filename + img_size = args.img_size + block_size = args.block_size + read_size = args.read_size + prog_size = args.prog_size + name_max = args.name_max + file_max = args.file_max + attr_max = args.attr_max + source_dir = args.source + + 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, + ) diff --git a/app/scripts/rtt_flash_loader.py b/app/scripts/rtt_flash_loader.py new file mode 100644 index 00000000..432a8d5f --- /dev/null +++ b/app/scripts/rtt_flash_loader.py @@ -0,0 +1,262 @@ +import pylink +import argparse +import sys +import time +import os +from struct import * + +from threading import Thread + +RAM_ADDR = 0x2007FFFC +RTT_FLASH_LOAD_BOOT_MODE = 0x0A0A0A0A +RTT_HEADER_MAGIC = 0x0A0A0A0A + + +def read_rtt(jlink): + """Reads the JLink RTT buffer #0 at 10Hz and prints to stdout. + + This method is a polling loop against the connected JLink unit. If + the JLink is disconnected, it will exit. Additionally, if any exceptions + are raised, they will be caught and re-raised after interrupting the + main thread. + + sys.stdout.write and sys.stdout.flush are used since target terminals + are expected to transmit newlines, which may or may not line up with the + arbitrarily-chosen 1024-byte buffer that this loop uses to read. + + Args: + jlink (pylink.JLink): The JLink to read. + + Raises: + Exception on error. + """ + try: + while jlink.connected(): + terminal_bytes = jlink.rtt_read(0, 1024) + if terminal_bytes and terminal_bytes != "": + sys.stdout.write("".join(map(chr, terminal_bytes))) + sys.stdout.flush() + time.sleep(0.1) + except Exception: + print("IO read thread exception, exiting...") + raise + + +def dump_flash(jlink, file, partition): + print(jlink, file) + with open(file, "wb") as file: + try: + bytes = list(bytearray(f"DUMP_START:{partition}", "utf-8")) + [0x0] + jlink.rtt_write(2, bytes) + time.sleep(2) + + block_number = 0 + read_data_len = 0 + start_ms = round(time.time() * 1000) + while jlink.connected(): + bytes = jlink.rtt_read(2, 4096 * 2) + if len(bytes) == 8: + data = "".join(map(chr, bytes)) + if data == "DUMP_END": + print("Received DUMP_END, storing...", read_data_len, "bytes") + end_ms = round(time.time() * 1000) + print( + "Took", + end_ms - start_ms, + "ms", + "Througput: ", + (read_data_len * 8 / (end_ms - start_ms)) * 1000, + "kbps", + ) + return + if len(bytes) > 0: + read_data_len = read_data_len + len(bytes) + file.write(bytearray(bytes)) + block_number = block_number + 1 + if block_number % 10 == 0: + print("Received", read_data_len, "bytes", end="\r") + except Exception: + print("IO read thread exception, exiting...") + raise + + +def load_data(jlink, file, partition): + try: + buffer_size = 4096 + block_number = 0 + print("FILENAME", file) + bytes = list(bytearray(f"LOADER_START:{partition}", "utf-8")) + [0x0] + jlink.rtt_write(2, bytes) + time.sleep(2) + + with open(file, mode="rb") as f: + num_sent = 0 + chunk_index = 0 + file_size = os.fstat(f.fileno()).st_size + print("Filesize:", file_size) + chunk = f.read(buffer_size) + start_ms = round(time.time() * 1000) + while chunk and chunk != "" and jlink.connected(): + to_send = list(bytearray(chunk[chunk_index:])) + if chunk_index == 0: + to_send = ( + list( + bytearray( + pack( + " #include #include +#include #include "settings_ui.h" #include "ble/ble_aoa.h" @@ -161,6 +162,8 @@ static void on_brightness_changed(lv_setting_value_t value, bool final) { // Slider have values 0-10 hence multiply with 10 to get brightness in percent display_control_set_brightness(value.item.slider * 10); + value.item.slider *= 10; + settings_save_one("settings/brightness", &value.item.slider, sizeof(value.item.slider)); } static void on_display_on_changed(lv_setting_value_t value, bool final) @@ -204,6 +207,39 @@ static void on_reset_steps_changed(lv_setting_value_t value, bool final) } } +static int setting_on_commit(void) +{ + printk("Settings loaded\n"); + return 0; +} + +static int settings_load_cb(const char *name, size_t len, + settings_read_cb read_cb, void *cb_arg) +{ + const char *next; + int rc; + int32_t bri; + + if (settings_name_steq(name, "brightness", &next) && !next) { + if (len != sizeof(bri)) { + return -EINVAL; + } + + rc = read_cb(cb_arg, &bri, sizeof(bri)); + printk("Read br: %d\n", bri); + general_page_items[0].item.slider.inital_val = bri / 10; + display_control_set_brightness(bri); + if (rc >= 0) { + return 0; + } + + return rc; + } + + + return -ENOENT; +} + static int settings_app_add(void) { application_manager_add_application(&app); @@ -211,4 +247,7 @@ static int settings_app_add(void) return 0; } +SETTINGS_STATIC_HANDLER_DEFINE(my_name, "settings", NULL, + settings_load_cb, setting_on_commit, NULL); + SYS_INIT(settings_app_add, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY); diff --git a/app/src/applications/x_ray/CMakeLists.txt b/app/src/applications/x_ray/CMakeLists.txt index bc4a1c6a..cf2e55fc 100644 --- a/app/src/applications/x_ray/CMakeLists.txt +++ b/app/src/applications/x_ray/CMakeLists.txt @@ -1,4 +1,4 @@ -if(CONFIG_APPLICATIONS_X_RAY) +if(CONFIG_APPLICATIONS_USE_X_RAY) FILE(GLOB app_sources *.c) target_sources(app PRIVATE ${app_sources}) endif() \ No newline at end of file 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/battery/zsw_charger.c b/app/src/battery/zsw_charger.c index 65ecfe9b..87e51891 100644 --- a/app/src/battery/zsw_charger.c +++ b/app/src/battery/zsw_charger.c @@ -40,6 +40,7 @@ static void send_chg_status_event(void) struct chg_state_event evt = { .is_charging = is_charging, }; + LOG_ERR("Charging status: %d", is_charging); zbus_chan_pub(&chg_state_data_chan, &evt, K_MSEC(250)); } diff --git a/app/src/ble/ble_comm.c b/app/src/ble/ble_comm.c index 58c59b4b..88514ece 100644 --- a/app/src/ble/ble_comm.c +++ b/app/src/ble/ble_comm.c @@ -511,11 +511,11 @@ static int parse_notify(char *data, int len) memset(&cb, 0, sizeof(cb)); cb.type = BLE_COMM_DATA_TYPE_NOTIFY; - cb.data.notify.id = extract_value_uint32("id:", data); - cb.data.notify.src = extract_value_str("src:", data, &cb.data.notify.src_len); - cb.data.notify.sender = extract_value_str("sender:", data, &cb.data.notify.sender_len); - cb.data.notify.title = extract_value_str("title:", data, &cb.data.notify.title_len); - cb.data.notify.body = extract_value_str("body:", data, &cb.data.notify.body_len); + cb.data.notify.id = extract_value_uint32("\"id\":", data); + cb.data.notify.src = extract_value_str("\"src\":", data, &cb.data.notify.src_len); + cb.data.notify.sender = extract_value_str("\"sender\":", data, &cb.data.notify.sender_len); + cb.data.notify.title = extract_value_str("\"title\":", data, &cb.data.notify.title_len); + cb.data.notify.body = extract_value_str("\"body\":", data, &cb.data.notify.body_len); // Little hack since we know it's JSON, we can terminate all values in the data // which saves us some hassle and we can just pass all values null terminated @@ -544,8 +544,8 @@ static int parse_notify_delete(char *data, int len) memset(&cb, 0, sizeof(cb)); cb.type = BLE_COMM_DATA_TYPE_NOTIFY_REMOVE; - cb.data.notify.id = extract_value_uint32("id:", data); - data_parsed_cb(&cb); + cb.data.notify.id = extract_value_uint32("\"id\":", data); + send_ble_data_event(&cb); return 0; } @@ -559,12 +559,12 @@ static int parse_weather(char *data, int len) memset(&cb, 0, sizeof(cb)); cb.type = BLE_COMM_DATA_TYPE_WEATHER; - int32_t temperature_k = extract_value_uint32("temp:", data); - cb.data.weather.humidity = extract_value_uint32("hum:", data); - cb.data.weather.weather_code = extract_value_uint32("code:", data); - cb.data.weather.wind = extract_value_uint32("wind:", data); - cb.data.weather.wind_direction = extract_value_uint32("wdir:", data); - temp_value = extract_value_str("txt:", data, &temp_len); + int32_t temperature_k = extract_value_uint32("\"temp\":", data); + cb.data.weather.humidity = extract_value_uint32("\"hum\":", data); + cb.data.weather.weather_code = extract_value_uint32("\"code\":", data); + cb.data.weather.wind = extract_value_uint32("\"wind\":", data); + cb.data.weather.wind_direction = extract_value_uint32("\"wdir\":", data); + temp_value = extract_value_str("\"txt\":", data, &temp_len); strncpy(cb.data.weather.report_text, temp_value, MIN(temp_len, MAX_MUSIC_FIELD_LENGTH)); @@ -584,14 +584,14 @@ static int parse_musicinfo(char *data, int len) memset(&cb, 0, sizeof(cb)); cb.type = BLE_COMM_DATA_TYPE_MUSTIC_INFO; - cb.data.music_info.duration = extract_value_int32("dur:", data); - cb.data.music_info.track_count = extract_value_int32("c:", data); - cb.data.music_info.track_num = extract_value_int32("n:", data); - temp_value = extract_value_str("artist:", data, &temp_len); + cb.data.music_info.duration = extract_value_int32("\"dur\":", data); + cb.data.music_info.track_count = extract_value_int32("\"c\":", data); + cb.data.music_info.track_num = extract_value_int32("\"n\":", data); + temp_value = extract_value_str("\"artist\":", data, &temp_len); strncpy(cb.data.music_info.artist, temp_value, MIN(temp_len, MAX_MUSIC_FIELD_LENGTH)); - temp_value = extract_value_str("album:", data, &temp_len); + temp_value = extract_value_str("\"album\":", data, &temp_len); strncpy(cb.data.music_info.album, temp_value, MIN(temp_len, MAX_MUSIC_FIELD_LENGTH)); - temp_value = extract_value_str("track:", data, &temp_len); + temp_value = extract_value_str("\"track\":", data, &temp_len); strncpy(cb.data.music_info.track_name, temp_value, MIN(temp_len, MAX_MUSIC_FIELD_LENGTH)); send_ble_data_event(&cb); @@ -608,11 +608,11 @@ static int parse_musicstate(char *data, int len) memset(&cb, 0, sizeof(cb)); cb.type = BLE_COMM_DATA_TYPE_MUSTIC_STATE; - cb.data.music_state.position = extract_value_int32("position:", data); - cb.data.music_state.shuffle = extract_value_int32("shuffle:", data); - cb.data.music_state.repeat = extract_value_int32("repeat:", data); + cb.data.music_state.position = extract_value_int32("\"position\":", data); + cb.data.music_state.shuffle = extract_value_int32("\"shuffle\":", data); + cb.data.music_state.repeat = extract_value_int32("\"repeat\":", data); - temp_value = extract_value_str("state:", data, &temp_len); + temp_value = extract_value_str("\"state\":", data, &temp_len); if (strncmp(temp_value, "play", temp_len) == 0) { cb.data.music_state.playing = true; } else { @@ -629,7 +629,7 @@ static int parse_data(char *data, int len) int type_len; char *type; - type = extract_value_str("t:", data, &type_len); + type = extract_value_str("\"t\":", data, &type_len); if (type == NULL) { return -1; } diff --git a/app/src/images/binaries/README.md b/app/src/images/binaries/README.md new file mode 100644 index 00000000..286f9a12 --- /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 raw` +- `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 lfs` + +## 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/images/binaries/S/x_ray.bin b/app/src/images/binaries/S/x_ray.bin new file mode 100644 index 00000000..de62f3c9 Binary files /dev/null and b/app/src/images/binaries/S/x_ray.bin differ diff --git a/app/src/images/binaries/lvgl_lfs/qr_code.bin b/app/src/images/binaries/lvgl_lfs/qr_code.bin new file mode 100755 index 00000000..5db0ff31 Binary files /dev/null and b/app/src/images/binaries/lvgl_lfs/qr_code.bin differ diff --git a/app/src/main.c b/app/src/main.c index f53b18f5..f5d9c0a5 100644 --- a/app/src/main.c +++ b/app/src/main.c @@ -25,7 +25,9 @@ #include #include #include - +#include +#include +#include #include "dfu.h" #include "ui/zsw_ui.h" #include "ble/ble_comm.h" @@ -199,6 +201,9 @@ static void run_init_work(struct k_work *item) load_retention_ram(); notification_manager_init(); enable_bluetoth(); + //uint32_t br = 1337; + //int rc = settings_save_one("settings/brightness", &br, sizeof(br)); + //printk("RC: %d\n", rc); zsw_imu_init(); zsw_magnetometer_init(); zsw_pressure_sensor_init(); @@ -250,6 +255,14 @@ void run_wdt_work(struct k_work *item) int main(void) { +#ifdef CONFIG_SPI_FLASH_LOADER + if (bootmode_check(0xA)) { + LOG_WRN("SPI Flash Loader Boot Mode"); + bootmode_clear(); + zsw_rtt_flash_loader_start(); + return 0; + } +#endif #if defined(CONFIG_TASK_WDT) && !defined(CONFIG_BOARD_NATIVE_POSIX) const struct device *hw_wdt_dev = DEVICE_DT_GET(DT_ALIAS(watchdog0)); if (!device_is_ready(hw_wdt_dev)) { @@ -291,14 +304,14 @@ static void enable_bluetoth(void) #endif err = bt_enable(NULL); - if (err != 0) { - LOG_ERR("Failed to enable Bluetooth, err: %d", err); - return; - } #ifdef CONFIG_SETTINGS settings_load(); #endif + if (err != 0) { + LOG_ERR("Failed to enable Bluetooth, err: %d", err); + return; + } __ASSERT_NO_MSG(ble_comm_init(on_ble_data_callback) == 0); bleAoaInit(); diff --git a/app/src/zsw_filesystem.c b/app/src/zsw_filesystem.c new file mode 100644 index 00000000..d0572a97 --- /dev/null +++ b/app/src/zsw_filesystem.c @@ -0,0 +1,83 @@ +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(zsw_filesystem, LOG_LEVEL_DBG); + +#define PARTITION_NODE DT_NODELABEL(lvgl_lfs) +FS_FSTAB_DECLARE_ENTRY(PARTITION_NODE); + +struct fs_mount_t *mountpoint = &FS_FSTAB_ENTRY(PARTITION_NODE); + +static int lsdir(const char *path) +{ + int res; + struct fs_dir_t dirp; + static struct fs_dirent entry; + + fs_dir_t_init(&dirp); + + /* Verify fs_opendir() */ + res = fs_opendir(&dirp, path); + if (res) { + LOG_ERR("Error opening dir %s [%d]\n", path, res); + return res; + } + + LOG_WRN("Listing dir %s ...", path); + for (;;) { + /* Verify fs_readdir() */ + res = fs_readdir(&dirp, &entry); + + /* entry.name[0] == 0 means end-of-dir */ + if (res || entry.name[0] == 0) { + if (res < 0) { + LOG_ERR("Error reading dir [%d]\n", res); + } + break; + } + + if (entry.type == FS_DIR_ENTRY_DIR) { + LOG_PRINTK("[DIR ] %s\n", entry.name); + } else { + LOG_PRINTK("[FILE] %s (size = %zu)\n", + entry.name, entry.size); + } + } + + /* Verify fs_closedir() */ + fs_closedir(&dirp); + + return res; +} + +int zsw_filesystem_ls(void) +{ + struct fs_statvfs sbuf; + int rc; + + rc = fs_statvfs(mountpoint->mnt_point, &sbuf); + if (rc < 0) { + LOG_PRINTK("FAIL: statvfs: %d\n", rc); + return -1; + } + + LOG_WRN("%s: bsize = %lu ; frsize = %lu ;" + " blocks = %lu ; bfree = %lu", + mountpoint->mnt_point, + sbuf.f_bsize, sbuf.f_frsize, + sbuf.f_blocks, sbuf.f_bfree); + + rc = lsdir(mountpoint->mnt_point); + if (rc < 0) { + LOG_PRINTK("FAIL: lsdir %s: %d\n", mountpoint->mnt_point, rc); + return -1; + } + + return 0; +} + +SYS_INIT(zsw_filesystem_ls, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY); \ No newline at end of file diff --git a/app/src/zsw_flash.c b/app/src/zsw_flash.c deleted file mode 100644 index af6943cf..00000000 --- a/app/src/zsw_flash.c +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2016 Intel Corporation. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include -#include -#include -#include -#include -#include - -LOG_MODULE_REGISTER(zsw_flash, LOG_LEVEL_DBG); - -#define SPI_FLASH_TEST_REGION_OFFSET 0xff000 -#define SPI_FLASH_SECTOR_SIZE 4096 - -static int zsw_flash_test(void) -{ - const struct device *flash_dev = DEVICE_DT_GET_OR_NULL(DT_ALIAS(spi_flash0)); - - if (!flash_dev || !device_is_ready(flash_dev)) { - LOG_ERR("No external flash present: %s", flash_dev ? flash_dev->name : "spi_flash0"); - return 0; - } - - return 0; -} - -SYS_INIT(zsw_flash_test, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY); diff --git a/app/src/zsw_flash.h b/app/src/zsw_flash.h deleted file mode 100644 index 1d75712b..00000000 --- a/app/src/zsw_flash.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -int zsw_flash_test(void); diff --git a/app/src/zsw_lvgl_spi_decoder.c b/app/src/zsw_lvgl_spi_decoder.c new file mode 100644 index 00000000..859cade8 --- /dev/null +++ b/app/src/zsw_lvgl_spi_decoder.c @@ -0,0 +1,250 @@ +#include +#include +#include +#include +#include +#include +#include "lv_conf.h" +#include LV_MEM_CUSTOM_INCLUDE + + +#define SPI_FLASH_SECTOR_SIZE 4096 + +#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) +#define FLASH_PARTITION_OFFSET FIXED_PARTITION_OFFSET(FLASH_PARTITION_NAME) + +#define FILE_TABLE_MAX_LEN 1024 +#define MAX_FILE_NAME_LEN 16 +#define MAX_OPENED_FILES 8 + +typedef struct file_header_t { + uint8_t filename[MAX_FILE_NAME_LEN]; + uint32_t offset; + uint32_t len; +} file_header_t; + +typedef struct file_table_t { + uint32_t magic; + uint32_t header_length; // Image offset counted from after this. + uint32_t total_length; + uint32_t num_files; + file_header_t file_headers[FILE_TABLE_MAX_LEN / sizeof(file_header_t)]; +} file_table_t; + +typedef struct opened_file_t { + file_header_t *header; + uint32_t index; +} opened_file_t; + +static file_table_t file_table; +static opened_file_t opened_files[MAX_OPENED_FILES]; + +static const struct flash_area *flash_area; + +static file_header_t *find_file(const char *name) +{ + for (int i = 0; i < file_table.num_files; i++) { + if (strncmp(name, file_table.file_headers[i].filename, MAX_FILE_NAME_LEN) == 0) { + return &file_table.file_headers[i]; + } + } + return NULL; +} + +static opened_file_t *find_free_opened_file(void) +{ + for (int i = 0; i < MAX_OPENED_FILES; i++) { + if (opened_files[i].header == NULL) { + return &opened_files[i]; + } + } + return NULL; +} + +static bool lvgl_fs_ready(struct _lv_fs_drv_t *drv) +{ + return true; +} + +static lv_fs_res_t errno_to_lv_fs_res(int err) +{ + switch (err) { + case 0: + return LV_FS_RES_OK; + case -EIO: + /*Low level hardware error*/ + return LV_FS_RES_HW_ERR; + case -EBADF: + /*Error in the file system structure */ + return LV_FS_RES_FS_ERR; + case -ENOENT: + /*Driver, file or directory is not exists*/ + return LV_FS_RES_NOT_EX; + case -EFBIG: + /*Disk full*/ + return LV_FS_RES_FULL; + case -EACCES: + /*Access denied. Check 'fs_open' modes and write protect*/ + return LV_FS_RES_DENIED; + case -EBUSY: + /*The file system now can't handle it, try later*/ + return LV_FS_RES_BUSY; + case -ENOMEM: + /*Not enough memory for an internal operation*/ + return LV_FS_RES_OUT_OF_MEM; + case -EINVAL: + /*Invalid parameter among arguments*/ + return LV_FS_RES_INV_PARAM; + case -ENOTSUP: + /*Not supported by the filesystem*/ + return LV_FS_RES_NOT_IMP; + default: + return LV_FS_RES_UNKNOWN; + } +} + +static void *lvgl_fs_open(struct _lv_fs_drv_t *drv, const char *path, lv_fs_mode_t mode) +{ + file_header_t *file = find_file(path); + opened_file_t *open_file; + + if (!file) { + return NULL; + } + + open_file = find_free_opened_file(); + if (!file) { + return NULL; + } + + open_file->header = file; + open_file->index = 0; + + return open_file; +} + +static lv_fs_res_t lvgl_fs_close(struct _lv_fs_drv_t *drv, void *file) +{ + opened_file_t *open_file = (opened_file_t *)file; + open_file->header = NULL; + open_file->index = 0; + return errno_to_lv_fs_res(0); +} + +static lv_fs_res_t lvgl_fs_read(struct _lv_fs_drv_t *drv, void *file, void *buf, uint32_t btr, + uint32_t *br) +{ + int rc; + opened_file_t *open_file = (opened_file_t *)file; + + rc = flash_area_read(flash_area, open_file->header->offset + open_file->index + file_table.header_length, buf, btr); + if (rc != 0) { + printk("Flash read failed! %d\n", rc); + *br = 0; + return errno_to_lv_fs_res(rc); + } + *br = btr; + return errno_to_lv_fs_res(0); +} + +static lv_fs_res_t lvgl_fs_write(struct _lv_fs_drv_t *drv, void *file, const void *buf, + uint32_t btw, uint32_t *bw) +{ + return LV_FS_RES_NOT_IMP; +} + +static lv_fs_res_t lvgl_fs_seek(struct _lv_fs_drv_t *drv, void *file, uint32_t pos, + lv_fs_whence_t whence) +{ + opened_file_t *open_file = (opened_file_t *)file; + + switch (whence) { + case LV_FS_SEEK_END: + open_file->index = open_file->header->len; + break; + case LV_FS_SEEK_CUR: + // We are already there? + break; + case LV_FS_SEEK_SET: + default: + open_file->index = pos; + break; + } + + return errno_to_lv_fs_res(0); +} + +static lv_fs_res_t lvgl_fs_tell(struct _lv_fs_drv_t *drv, void *file, uint32_t *pos_p) +{ + opened_file_t *open_file = (opened_file_t *)file; + *pos_p = open_file->index; + return LV_FS_RES_OK; +} + +static void *lvgl_fs_dir_open(struct _lv_fs_drv_t *drv, const char *path) +{ + return NULL; +} + +static lv_fs_res_t lvgl_fs_dir_read(struct _lv_fs_drv_t *drv, void *dir, char *fn) +{ + return LV_FS_RES_NOT_IMP; +} + +static lv_fs_res_t lvgl_fs_dir_close(struct _lv_fs_drv_t *drv, void *dir) +{ + int err; + err = 0; + return errno_to_lv_fs_res(err); +} + +static lv_fs_drv_t fs_drv; + +int zsw_decoder_init(void) +{ + int rc; + lv_fs_drv_init(&fs_drv); + + /* LVGL uses letter based mount points, just pass the root slash as a + * letter. Note that LVGL will remove the drive letter, or in this case + * the root slash, from the path passed via the FS callbacks. + * Zephyr FS API assumes this slash is present so we will need to add + * it back. + */ + fs_drv.letter = 'S'; + fs_drv.ready_cb = lvgl_fs_ready; + + fs_drv.open_cb = lvgl_fs_open; + fs_drv.close_cb = lvgl_fs_close; + fs_drv.read_cb = lvgl_fs_read; + fs_drv.write_cb = lvgl_fs_write; + fs_drv.seek_cb = lvgl_fs_seek; + fs_drv.tell_cb = lvgl_fs_tell; + + fs_drv.dir_open_cb = lvgl_fs_dir_open; + fs_drv.dir_read_cb = lvgl_fs_dir_read; + fs_drv.dir_close_cb = lvgl_fs_dir_close; + + lv_fs_drv_register(&fs_drv); + + memset(opened_files, 0, sizeof(opened_files)); + + 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; + } + + rc = flash_area_read(flash_area, 0, &file_table, FILE_TABLE_MAX_LEN); + if (rc != 0) { + printk("Flash read failed! %d\n", rc); + return rc; + } + return 0; +} + +SYS_INIT(zsw_decoder_init, APPLICATION, 99); diff --git a/app/src/zsw_rtt_flash_loader.c b/app/src/zsw_rtt_flash_loader.c new file mode 100644 index 00000000..4606f366 --- /dev/null +++ b/app/src/zsw_rtt_flash_loader.c @@ -0,0 +1,340 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(zsw_rtt_flash_loader, LOG_LEVEL_DBG); + +#define SPI_FLASH_SECTOR_SIZE 4096 + +#define RTT_RECIVE_BUFFER_SIZE SPI_FLASH_SECTOR_SIZE + +#define TRANSFER_TIMEOUT_MS 5000 + +#define START_LOAD_SEQUENCE "LOADER_START" +#define STOP_LOAD_SEQUENCE "LOADER_END" +#define DUMP_FLASH_SEQUENCE "DUMP_START" +#define READ_DONE_SEQUENCE "DUMP_END" + +#define RTT_CHANNEL_NAME "FlashLoaderChannel" +#define RTT_MAGIC 0x0A0A0A0A + +struct flash_partition_search_user_data { + int found_part_id; + char *search_label_name; +}; + +struct rtt_rx_data_header { + uint32_t magic; + uint32_t address; +}; + +#define DATA_BUFFER_SIZE (SPI_FLASH_SECTOR_SIZE + sizeof(struct rtt_rx_data_header)) +#define UP_BUFFER_SIZE (SPI_FLASH_SECTOR_SIZE + 1 + sizeof(struct rtt_rx_data_header)) +#define DOWN_BUFFER_SIZE (SPI_FLASH_SECTOR_SIZE + 1 + sizeof(struct rtt_rx_data_header)) + +static void rtt_load_flash_thread(void *, void *, void *); +static void rtt_dump_flash_thread(void *, void *, void *); + +K_THREAD_STACK_DEFINE(rtt_work_thread_stack, 8192); +static struct k_thread rtt_work_thread; + +static const struct flash_area *flash_area; + +static uint8_t* data_buf; +static uint8_t* up_buffer; +static uint8_t* down_buffer; + +static int loader_write_flash(int partition_id, int buf_idx, uint8_t *buf, int len) +{ + int rc; + if (len != SPI_FLASH_SECTOR_SIZE) { + LOG_ERR("Buflen must be same size as SPI_FLASH_SECTOR_SIZE: %d", SPI_FLASH_SECTOR_SIZE); + return -EINVAL; + } + +#ifdef CONFIG_ERASE_PROGRESSIVELY + rc = flash_area_erase(flash_area, buf_idx * SPI_FLASH_SECTOR_SIZE, SPI_FLASH_SECTOR_SIZE); + if (rc != 0) { + printk("Flash erase failed! %d", rc); + return rc; + } +#endif + + rc = flash_area_write(flash_area, buf_idx * SPI_FLASH_SECTOR_SIZE, buf, len); + if (rc != 0) { + printk("Flash write failed! %d\n", rc); + return rc; + } + + return 0; +} + +static int loader_read_flash(int partition_id, int buf_idx, uint8_t *buf, int len) +{ + int rc; + + rc = flash_area_read(flash_area, buf_idx * SPI_FLASH_SECTOR_SIZE, buf, len); + if (rc != 0) { + printk("Flash read failed! %d\n", rc); + return rc; + } + + return 0; +} + +static void flash_part_iter_cb(const struct flash_area *fa, void *user_data) +{ + 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); + } + + 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; +} + +static bool check_end_sequence(uint8_t *buf, uint32_t len) +{ + if (memcmp(buf, STOP_LOAD_SEQUENCE, strlen(STOP_LOAD_SEQUENCE)) == 0) { + printk("End sequence received\n"); + return true; + } + + return false; +} + +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) { + 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 *partition_id_param, void *, void *) +{ + int ret; + int len; + int block_index = 0; + int buffer_index = 0; + int bytes_flashed = 0; + struct rtt_rx_data_header *header; + 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 = DATA_BUFFER_SIZE - buffer_index; + len = SEGGER_RTT_Read(CONFIG_RTT_TRANSFER_CHANNEL, data_buf, len_to_read); + + if (len <= 0) { + if (k_uptime_get_32() - last_activity_ms > TRANSFER_TIMEOUT_MS) { + printk("RTT Transfer timeout. Aborting.\n"); + break; + } + k_msleep(100); + continue; + } + + last_activity_ms = k_uptime_get_32(); + if (check_end_sequence(data_buf, len)) { + printk("RTT Transfer done: %d bytes flashed\n", bytes_flashed); + break; + } else if (len == len_to_read) { + header = (struct rtt_rx_data_header *)data_buf; + if (header->magic == RTT_MAGIC) { + if (header->address % SPI_FLASH_SECTOR_SIZE != 0) { + LOG_ERR("Anvalid addres, must be multiple of %d", SPI_FLASH_SECTOR_SIZE); + break; + } + } else { + LOG_ERR("RTT: Invalid magic: %x\n", header->magic); + break; + } + ret = loader_write_flash((int)partition_id, header->address / SPI_FLASH_SECTOR_SIZE, + data_buf + sizeof(struct rtt_rx_data_header), DATA_BUFFER_SIZE - sizeof(struct rtt_rx_data_header)); + if (ret != 0) { + printk("loader_write_flash failed: %dn", ret); + break; + } + block_index++; + buffer_index = 0; + bytes_flashed += DATA_BUFFER_SIZE; + if (block_index % 10 == 0) { + printk("RTT: Received %d (%d)\n", bytes_flashed, block_index); + } + } else { + buffer_index += len; + if (buffer_index > DATA_BUFFER_SIZE) { + __ASSERT(false, "Something wrong got size %d", buffer_index); + } + } + } + + flash_area_close(flash_area); +} + +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(); + + 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, SPI_FLASH_SECTOR_SIZE); + if (ret != 0) { + printk("loader_read_flash failed: %dn", ret); + break; + } + len_to_send = SPI_FLASH_SECTOR_SIZE; + while (len_to_send > 0) { + len = SEGGER_RTT_Write(CONFIG_RTT_TRANSFER_CHANNEL, data_buf, SPI_FLASH_SECTOR_SIZE); + bytes_sent += len; + len_to_send -= len; + if (len > 0) { + last_activity_ms = k_uptime_get_32(); + } else { + if (k_uptime_get_32() - last_activity_ms > TRANSFER_TIMEOUT_MS) { + printk("RTT Transfer timeout. Aborting.\n"); + } + } + if (buffer_index % 10 == 0) { + printk("RTT: Sent %d (%d)\n", bytes_sent, buffer_index); + } + k_msleep(1); + } + buffer_index++; + } + printk("Done sending %d bytes\n", bytes_sent); + + // Sleep a bit so python code can read the last bytes + 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 partition_id; + + data_buf = k_malloc(DATA_BUFFER_SIZE); + up_buffer = k_malloc(UP_BUFFER_SIZE); + down_buffer = k_malloc(DOWN_BUFFER_SIZE); + + __ASSERT(data_buf && up_buffer && down_buffer, "Failed to allocate buffers for RTT file tarnsfer"); + + SEGGER_RTT_ConfigUpBuffer(CONFIG_RTT_TRANSFER_CHANNEL, RTT_CHANNEL_NAME, + up_buffer, UP_BUFFER_SIZE, + SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL); + SEGGER_RTT_ConfigDownBuffer(CONFIG_RTT_TRANSFER_CHANNEL, RTT_CHANNEL_NAME, + down_buffer, DOWN_BUFFER_SIZE, + SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL); + + while (1) { + int len = SEGGER_RTT_Read(CONFIG_RTT_TRANSFER_CHANNEL, data_buf, DATA_BUFFER_SIZE); + + if (len <= 0) { + k_msleep(100); + //SEGGER_RTT_Write(CONFIG_RTT_TRANSFER_CHANNEL, "READY", strlen("READY")); + continue; + } + + 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, (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, &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, (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 { + printk("Unknown sequence received\n"); + } + } + + return 0; +} diff --git a/app/src/zsw_rtt_flash_loader.h b/app/src/zsw_rtt_flash_loader.h new file mode 100644 index 00000000..47ae757c --- /dev/null +++ b/app/src/zsw_rtt_flash_loader.h @@ -0,0 +1,3 @@ +#pragma once + +int zsw_rtt_flash_loader_start(void); diff --git a/app/tools/format_code.sh b/app/tools/format_code.sh index a5be5805..18fbbb3c 100755 --- a/app/tools/format_code.sh +++ b/app/tools/format_code.sh @@ -8,5 +8,6 @@ astyle \ --exclude=src/images \ --exclude=src/ext_drivers \ --exclude=drivers/input \ +--exclude=drivers/sensor \ --exclude=src/applications/2048/2048_lib \ $1 $2 $3 # addtional args such as --dry-run etc. \ No newline at end of file