Hello,
We are currently helping one of our client develop a product using the nRF5340.
DK versions:
- nRF5340-DK
- PCA10095
- 2.0.1
Host OS:
- Ubuntu 22.04.2
- Linux 6.5.0-41-generic
nRF SDK and toolchains version:
- nRF SDK 2.6.1
- toolchains 2.6.1
We need to implement a custom upgrade process, where the application, or the bootloader if possible, would check if an upgrade file exists on the SD-Card, and flash if existing and valid. Both bootloader and application must be upgradable.
We currently have implemented a dual bootloader setup, with an application which reads the update file from the UART (no SD-card at the moment, so purely evaluation purpose) and uses DFUTarget to flash the memory chunk by chunk. This application can upgrade itself without any issue using the build/zephyr/app_update.bin file as the upgrade file sent via UART. I attach you the application and the prj.conf.
Now I'm trying to understand how MCUBoot is upgraded. From what I read, it uses both S0 and S1 slots, and only boots the latest valid MCUBoot flashed in one of those.
I can successfully upgrade MCUBoot by alternating with the s0 and s1 update file that I send to my test application implementing DFUTarget. I need clarification about the usage of those two images and how they are flashed:
- Why is it needed to switch between build/zephyr/signed_by_mcuboot_and_b0_s0_image_update.bin and build/zephyr/signed_by_mcuboot_and_b0_s1_image_update.bin?
- How will it then be possible to manage multiple devices when deployed on field?
- Should we always send both and do a check of which on is currently in use to select the correct one to flash?
- Why nothing happens when sending s0 image when the device currently uses s0 as the bootloader slot?
Thanks a lot in advance for you help, and please correct me if I'm wrong on any topic I covered asking my questions!
Best regards
David TRUAN
#include <stdio.h> #include <string.h> #include <zephyr/drivers/uart.h> #include <zephyr/sys/crc.h> #include <zephyr/kernel.h> #include <zephyr/sys/reboot.h> #include <dfu/dfu_target.h> #include <dfu/dfu_target_mcuboot.h> #include <errno.h> #include <zephyr/device.h> #include <zephyr/drivers/flash.h> #include <zephyr/storage/flash_map.h> #include <logging.h> #define PRG_BUFSIZ 32000 #define VERSION 1 #define CHUNK_SZ 1024 #define MCUBOOT_BUF_SZ 512 static const struct device *uart_dev; /* The buffer to receive */ static char mcuboot_buf[MCUBOOT_BUF_SZ] __aligned(4); /** * @brief Helper to read the update size using UART * * @param uart_dev the UART device to use to read * @return int32_t >= 0 on success, -1 otherwise */ int32_t read_update_size_uart(const struct device *uart_dev) { int err; uint32_t prg_size = 0; uint32_t bytes_read = 0; uint8_t byte; do { err = uart_poll_in(uart_dev, &byte); if (err == 0) { prg_size |= (int)byte << (bytes_read * 8); bytes_read++; } else if (err == -1) { continue; } else { log_error("Error detected %d", err); return -1; } } while (bytes_read != sizeof(uint32_t)); return prg_size; } /** * @brief Callback handler for DFUTarget events * * @param evt the event which called the callback */ static void dfu_target_callback_handler(enum dfu_target_evt_id evt) { switch (evt) { case DFU_TARGET_EVT_TIMEOUT: printf("DFU_TARGET_EVT_TIMEOUT"); break; case DFU_TARGET_EVT_ERASE_DONE: printf("DFU_TARGET_EVT_ERASE_DONE"); break; case DFU_TARGET_EVT_ERASE_PENDING: printf("DFU_TARGET_EVT_ERASE_PENDING"); break; } } int main(void) { static char prg_data_buf[CHUNK_SZ]; char *prg_data; int update_size_full; int err; int total_bytes_read; uart_dev = DEVICE_DT_GET(DT_NODELABEL(uart0)); prg_data = prg_data_buf; total_bytes_read = 0; update_size_full = 0; k_msleep(1000); log_set_level(LOG_LEVEL_DEBUG); log_info("current version: %d board: %s", VERSION, CONFIG_BOARD); log_info("Waiting for playload..."); /* Retrieve the update size */ update_size_full = read_update_size_uart(uart_dev); if (update_size_full == -1) { log_error("Error while getting update size!"); return 1; } log_info("Update size: %d", update_size_full); /* Set the buffer used by DFUTarget in MCUBoot mode */ if ((err = dfu_target_mcuboot_set_buf(mcuboot_buf, MCUBOOT_BUF_SZ)) != 0) { log_error("dfu_target_mcuboot_set_buf error: %d", err); return -1; } /* Initialize the DFUTarget library with our image info */ if ((err = dfu_target_init(DFU_TARGET_IMAGE_TYPE_ANY_APPLICATION, 0, update_size_full, dfu_target_callback_handler)) != 0) { log_error("dfu_target_init error: %d", err); return -1; } while (1) { err = uart_poll_in(uart_dev, prg_data); if (err == 0) { prg_data++; total_bytes_read++; if (total_bytes_read == update_size_full) { log_info("Received the full file!"); dfu_target_write(prg_data_buf, total_bytes_read % CHUNK_SZ); break; } if (total_bytes_read % CHUNK_SZ == 0) { dfu_target_write(prg_data_buf, CHUNK_SZ); prg_data = prg_data_buf; } } else if (err == -ENOSYS) { log_error("Error RX"); return 1; } else if (err == -EBUSY) { log_error("Error UART busy!"); return 1; } } /* Ask DFU target to clean its data */ if ((err = dfu_target_done(true)) != 0) { log_error("dfu_target_done failed: %d", err); return 1; } else { log_info("DFU target download done!"); } /* Indicate to DFUTarget that the update is ready and reboot to boot into the updated app if successful */ if ((err = dfu_target_schedule_update(-1)) != 0) { log_error("dfu_target_schedule_update failed: %d", err); return 1; } else { log_info("DFU target successfully scheduled the update!"); sys_reboot(SYS_REBOOT_WARM); } return 0; }