From fe4ae541bcf29564f4c17a0d8faa59cea6e26333 Mon Sep 17 00:00:00 2001 From: Armando Visconti Date: Mon, 12 Feb 2024 17:25:52 +0100 Subject: [PATCH 1/3] boards: arm: sensortile_box_pro: Add usart2 support The usart2 is used to communicate with the BLE chip bootloader. Signed-off-by: Armando Visconti --- boards/st/sensortile_box_pro/sensortile_box_pro.dts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/boards/st/sensortile_box_pro/sensortile_box_pro.dts b/boards/st/sensortile_box_pro/sensortile_box_pro.dts index 7014fd86a7e535..2d8096091fa303 100644 --- a/boards/st/sensortile_box_pro/sensortile_box_pro.dts +++ b/boards/st/sensortile_box_pro/sensortile_box_pro.dts @@ -17,6 +17,7 @@ zephyr,console = &cdc_acm_uart0; zephyr,shell-uart = &cdc_acm_uart0; zephyr,bt-c2h-uart = &cdc_acm_uart0; + zephyr,bootloader-uart = &usart2; zephyr,sram = &sram0; zephyr,flash = &flash0; zephyr,code-partition = &slot0_partition; @@ -135,6 +136,13 @@ stm32_lp_tick_source: &lptim1 { status = "okay"; }; +&usart2 { + pinctrl-0 = <&usart2_tx_pd5 &usart2_rx_pd6>; + pinctrl-names = "default"; + current-speed = <115200>; + status = "okay"; +}; + &usart3 { pinctrl-0 = <&usart3_tx_pd8 &usart3_rx_pd9>; pinctrl-names = "default"; From fcd2a41cf83becc64de96e7758bb59dbf6250a6f Mon Sep 17 00:00:00 2001 From: Armando Visconti Date: Mon, 12 Feb 2024 17:34:07 +0100 Subject: [PATCH 2/3] sample: board: sensortile_box_pro: add ble-fw-upgrade This sample implement the ble firmware upgrade using the AN5471 protocol on uart2 channel. Signed-off-by: Armando Visconti --- .../ble-fw-upgrade/CMakeLists.txt | 11 + .../sensortile_box_pro/ble-fw-upgrade/Kconfig | 10 + .../ble-fw-upgrade/README.rst | 109 +++++ .../ble-fw-upgrade/prj.conf | 2 + .../ble-fw-upgrade/sample.yaml | 13 + .../ble-fw-upgrade/src/ble_fw.c | 16 + .../ble-fw-upgrade/src/main.c | 378 ++++++++++++++++++ 7 files changed, 539 insertions(+) create mode 100644 samples/boards/sensortile_box_pro/ble-fw-upgrade/CMakeLists.txt create mode 100644 samples/boards/sensortile_box_pro/ble-fw-upgrade/Kconfig create mode 100644 samples/boards/sensortile_box_pro/ble-fw-upgrade/README.rst create mode 100644 samples/boards/sensortile_box_pro/ble-fw-upgrade/prj.conf create mode 100644 samples/boards/sensortile_box_pro/ble-fw-upgrade/sample.yaml create mode 100644 samples/boards/sensortile_box_pro/ble-fw-upgrade/src/ble_fw.c create mode 100644 samples/boards/sensortile_box_pro/ble-fw-upgrade/src/main.c diff --git a/samples/boards/sensortile_box_pro/ble-fw-upgrade/CMakeLists.txt b/samples/boards/sensortile_box_pro/ble-fw-upgrade/CMakeLists.txt new file mode 100644 index 00000000000000..e53af2044bf743 --- /dev/null +++ b/samples/boards/sensortile_box_pro/ble-fw-upgrade/CMakeLists.txt @@ -0,0 +1,11 @@ +# Copyright (c) 2024 STMicroelectronics +# +# SPDX-License-Identifier: Apache-2.0 +# +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(sensortile_box_pro) + +target_sources(app PRIVATE src/main.c) +target_sources(app PRIVATE src/ble_fw.c) diff --git a/samples/boards/sensortile_box_pro/ble-fw-upgrade/Kconfig b/samples/boards/sensortile_box_pro/ble-fw-upgrade/Kconfig new file mode 100644 index 00000000000000..7f036ad4122690 --- /dev/null +++ b/samples/boards/sensortile_box_pro/ble-fw-upgrade/Kconfig @@ -0,0 +1,10 @@ +# Copyright (c) 2024 STMicroelectronics +# SPDX-License-Identifier: Apache-2.0 + +mainmenu "Sensortile.box PRO ble f/w upgrade" + +config BLOB_ENABLED + bool "Use ble f/w image blob" + default y + +source "Kconfig.zephyr" diff --git a/samples/boards/sensortile_box_pro/ble-fw-upgrade/README.rst b/samples/boards/sensortile_box_pro/ble-fw-upgrade/README.rst new file mode 100644 index 00000000000000..6d532c1da48c8b --- /dev/null +++ b/samples/boards/sensortile_box_pro/ble-fw-upgrade/README.rst @@ -0,0 +1,109 @@ +.. zephyr:code-sample:: sensortile_box_pro_sample_ble_fwupg + :name: ST SensorTile.box Pro BLE Firmware Upgrade + + Upgrade the BLE firmware on STEVAL-MKBOXPRO board + +Overview +******** +This sample provides a simple method to upgrade the firmware for +the BLE chip present on :ref:`sensortile_box_pro_board` board. +It is based on the BlueNRG-LPS UART bootloader protocol explained in `AN5471`_ . + +Requirements +************ + +The application requires a SensorTile.box Pro board connected to the PC +through USB. + +To run this sample the console is not strictly needed, but might be useful. +Following is an example for minicom. + +.. code-block:: console + + $ minicom -D -b 115200 + +Replace :code:`` with the correct device path automatically created on +the host after the SensorTile.box Pro board gets connected to it, +usually :samp:`/dev/ttyUSB{x}` or :samp:`/dev/ttyACM{x}` (with x being 0, 1, 2, ...). +The ``-b`` option sets baud rate ignoring the value from config. + +Building and Running +******************** + +To build and flash the sample, it is required fetching the controller-only BLE f/w +image for sensortile_box_pro board, which is hosted by external repo +(see `stsw-mkbox-bleco`_ for more information): + +To fetch Binary Blobs: + +.. code-block:: console + + west blobs fetch stm32 + +Then, build the sample: + +.. zephyr-app-commands:: + :zephyr-app: samples/boards/sensortile_box_pro/ble-fw-upgrade + :board: sensortile_box_pro + :goals: build flash + +Please note that flashing the board requires a few preliminary steps described +in :ref:`sensortile_box_pro_board`. + +After flashing, the sample starts and asks the user to acknowledge the f/w update. +User should press :kbd:`y` or :kbd:`Y` to proceed in upgrading the BLE f/w. + + .. code-block:: console + + Start BLE f/w update (press Y to proceed): + +Nevertheless it is not strictly necessary to use the console, as the user may also acknowledge +the f/w update pressing the User BT2 button (see :ref:`sensortile_box_pro_board` board and +check on user manual and/or schematic to see where the button is located and how +it works). + +LEDs status +----------- + +The blue LED blinks three times with 200ms interval to indicate the procedure is starting. +Then blue LED start blinking very fast to indicate the BLE flash activity is on going. + +After BLE flashing is complete: + +- If status is OK the green LED blinks three times with 200ms interval and remains on. +- If flashing failed the red LED blinks three times with 200ms interval and remains on. + +Console messages +---------------- + +To properly see messages on your terminale emulatore you may also need to set lineWrap to on. +In case of minicom just enter the menu with :kbd:`Ctrl-A Z` an then press :kbd:`W`. + +The sample outputs following messages. + + .. code-block:: console + + SensorTile.box Pro BLE f/w upgrade + bootloader activated! + ble bootloader version is 4.0 + MASS ERASE ok + .............................................................................................. + .............................................................................................. + .............................................................................................. + .............................................................................................. + .............................................................................................. + .............................................................................................. + .............................................................................................. + ............................................ + BLE f/w upgrade ok + +References +********** + +.. target-notes:: + +.. _AN5471: + https://www.st.com/resource/en/application_note/an5471-the-bluenrglp-bluenrglps-uart-bootloader-protocol-stmicroelectronics.pdf + +.. _stsw-mkbox-bleco: + https://www.st.com/en/embedded-software/stsw-mkbox-bleco.html diff --git a/samples/boards/sensortile_box_pro/ble-fw-upgrade/prj.conf b/samples/boards/sensortile_box_pro/ble-fw-upgrade/prj.conf new file mode 100644 index 00000000000000..244cc784eab0ae --- /dev/null +++ b/samples/boards/sensortile_box_pro/ble-fw-upgrade/prj.conf @@ -0,0 +1,2 @@ +CONFIG_GPIO=y +CONFIG_LOG=y diff --git a/samples/boards/sensortile_box_pro/ble-fw-upgrade/sample.yaml b/samples/boards/sensortile_box_pro/ble-fw-upgrade/sample.yaml new file mode 100644 index 00000000000000..f2ddcf75a00a19 --- /dev/null +++ b/samples/boards/sensortile_box_pro/ble-fw-upgrade/sample.yaml @@ -0,0 +1,13 @@ +sample: + description: SensorTile.box Pro board ble firmware upgrade + name: SensorTile.box Pro fw upgrade +tests: + sample.board.sensortile_box_pro.ble-fw-upgrade: + harness: bluetooth + platform_allow: sensortile_box_pro + tags: bluetooth + depends_on: + - gpio + - uart + extra_configs: + - CONFIG_BLOB_ENABLED=n diff --git a/samples/boards/sensortile_box_pro/ble-fw-upgrade/src/ble_fw.c b/samples/boards/sensortile_box_pro/ble-fw-upgrade/src/ble_fw.c new file mode 100644 index 00000000000000..f85c0bf807dda0 --- /dev/null +++ b/samples/boards/sensortile_box_pro/ble-fw-upgrade/src/ble_fw.c @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2024 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +const uint8_t ble_fw_img[] = { +/* if not enabled do nothing, just remove blob dependency */ +#ifdef CONFIG_BLOB_ENABLED +#include +#endif +}; + +const uint32_t ble_fw_img_len = sizeof(ble_fw_img); diff --git a/samples/boards/sensortile_box_pro/ble-fw-upgrade/src/main.c b/samples/boards/sensortile_box_pro/ble-fw-upgrade/src/main.c new file mode 100644 index 00000000000000..da74c834614a8e --- /dev/null +++ b/samples/boards/sensortile_box_pro/ble-fw-upgrade/src/main.c @@ -0,0 +1,378 @@ +/* + * Copyright (c) 2024 STMicroelectronics + * + * The BlueNRG-LP, BlueNRG-LPS UART bootloader protocol (AN5471) + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include +#include +#include + +#include +#include + +#include +LOG_MODULE_REGISTER(ble_fw_upgrade, LOG_LEVEL_DBG); + +#define BLE_FLASH_START_ADDRESS 0x10040000 + +/* Defined in ${ZEPHYR_HAL_ST_MODULE_DIR}/ble_firmware/ble_fw.c */ +extern const uint8_t ble_fw_img[]; +extern const int ble_fw_img_len; + +const struct gpio_dt_spec green_gpio = GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios); +const struct gpio_dt_spec red_gpio = GPIO_DT_SPEC_GET(DT_ALIAS(led1), gpios); +const struct gpio_dt_spec blue_gpio = GPIO_DT_SPEC_GET(DT_ALIAS(led3), gpios); + +typedef enum { + BLE_FW_UPG_START = 0x0, + BLE_FW_UPG_OK = 0x1, + BLE_FW_UPG_ERR = 0x2, +} ble_fw_upg_status; + +static void led_pattern_out(ble_fw_upg_status status) +{ + int i; + + gpio_pin_set_dt(&blue_gpio, 0); + gpio_pin_set_dt(&green_gpio, 0); + gpio_pin_set_dt(&red_gpio, 0); + + switch (status) { + case BLE_FW_UPG_START: + for (i = 0; i < 7; i++) { + gpio_pin_toggle(blue_gpio.port, blue_gpio.pin); + k_sleep(K_MSEC(200)); + } + break; + + case BLE_FW_UPG_OK: + for (i = 0; i < 7; i++) { + gpio_pin_toggle(green_gpio.port, green_gpio.pin); + k_sleep(K_MSEC(200)); + } + break; + + case BLE_FW_UPG_ERR: + for (i = 0; i < 7; i++) { + gpio_pin_toggle(red_gpio.port, red_gpio.pin); + k_sleep(K_MSEC(200)); + } + break; + } +} + +static int led_init(void) +{ + /* led 0 */ + if (!gpio_is_ready_dt(&green_gpio)) { + LOG_ERR("%s: device not ready.\n", green_gpio.port->name); + return -1; + } + gpio_pin_configure_dt(&green_gpio, GPIO_OUTPUT_INACTIVE); + + /* led 1 */ + if (!gpio_is_ready_dt(&red_gpio)) { + LOG_ERR("%s: device not ready.\n", red_gpio.port->name); + return -1; + } + gpio_pin_configure_dt(&red_gpio, GPIO_OUTPUT_INACTIVE); + + /* led 3 */ + if (!gpio_is_ready_dt(&blue_gpio)) { + LOG_ERR("%s: device not ready.\n", blue_gpio.port->name); + return -1; + } + gpio_pin_configure_dt(&blue_gpio, GPIO_OUTPUT_INACTIVE); + + return 0; +} + +#define BLE_UART_BOOTLOADER_ACK 0x79 +#define BLE_UART_BOOTLOADER_NACK 0x1F + +#define BLE_UART_BOOTLOADER_AUTO_BAUDRATE 0x7F +#define BLE_BL_CMD_GET_LIST 0x00 +#define BLE_BL_CMD_GET_VERSION 0x01 +#define BLE_BL_CMD_WRITE_MEMORY 0x31 +#define BLE_BL_CMD_ERASE 0x43 + +static uint8_t checksum(uint8_t *buffer, uint16_t len) +{ + uint8_t checksum = 0; + uint16_t i; + + if (len > 1) { + for (i = 0; i < len; i++) { + checksum = checksum ^ buffer[i]; + } + } else { + checksum = ~buffer[0]; + } + + return checksum; +} + +static int ble_uart_check_ack(const struct device *bl_uart) +{ + int ret; + uint8_t ack; + + while ((ret = uart_poll_in(bl_uart, &ack)) == -1) { + } + + if (ret != 0 || ack != BLE_UART_BOOTLOADER_ACK) { + LOG_ERR("ble_uart_send_data: NAK (%02x)\n", ack); + return -1; + } + + return 0; +} + +static int ble_uart_send_data(const struct device *bl_uart, uint8_t *data, uint16_t len) +{ + uint16_t i; + + for (i = 0; i < len; i++) { + uart_poll_out(bl_uart, data[i]); + } + + uart_poll_out(bl_uart, checksum(data, len)); + + return ble_uart_check_ack(bl_uart); +} + +static int ble_uart_send_cmd(const struct device *bl_uart, uint8_t cmd) +{ + return ble_uart_send_data(bl_uart, &cmd, 1); +} + +static int ble_uart_bootloader_activate(const struct device *bl_uart) +{ + const struct gpio_dt_spec bt_rst = GPIO_DT_SPEC_GET(DT_NODELABEL(hci_spi), reset_gpios); + const struct gpio_dt_spec bt_boot = GPIO_DT_SPEC_GET(DT_NODELABEL(hci_spi), irq_gpios); + + /* Configure RST pin and hold BLE in Reset */ + gpio_pin_configure_dt(&bt_rst, GPIO_OUTPUT_ACTIVE); + gpio_pin_configure_dt(&bt_boot, GPIO_OUTPUT_ACTIVE); + + k_sleep(K_MSEC(1)); + + /* Take BLE out of reset */ + gpio_pin_set_dt(&bt_rst, 0); + k_sleep(K_MSEC(1)); + + /* Send bootloader activate command */ + uart_poll_out(bl_uart, BLE_UART_BOOTLOADER_AUTO_BAUDRATE); + + return ble_uart_check_ack(bl_uart); +} + +static int ble_bl_cmd_get_version(const struct device *bl_uart, uint8_t *fw_ver) +{ + uint8_t version = 0, opt[2]; + + if (ble_uart_send_cmd(bl_uart, BLE_BL_CMD_GET_VERSION) < 0) { + return -1; + } + + while (uart_poll_in(bl_uart, &version) == -1) { + } + + while (uart_poll_in(bl_uart, &opt[0]) == -1) { + } + + while (uart_poll_in(bl_uart, &opt[1]) == -1) { + } + + *fw_ver = version; + + return ble_uart_check_ack(bl_uart); +} + +static int ble_bl_cmd_mass_erase(const struct device *bl_uart) +{ + if (ble_uart_send_cmd(bl_uart, BLE_BL_CMD_ERASE) < 0) { + return -1; + } + + if (ble_uart_send_cmd(bl_uart, 0xFF) < 0) { + return -1; + } + + return 0; +} + +static int ble_bl_cmd_write_memory(const struct device *bl_uart, uint32_t address, + const uint8_t *buf, uint16_t len) +{ + uint32_t address_be = sys_cpu_to_be32(address); + uint8_t data[256 + 1]; /* buflen-1 (1 byte) + buf (max 256) */ + + if (len == 0) { + return 0; + } + + if (ble_uart_send_cmd(bl_uart, BLE_BL_CMD_WRITE_MEMORY) < 0) { + return -1; + } + + /* send address */ + if (ble_uart_send_data(bl_uart, (uint8_t *)&address_be, 4) < 0) { + LOG_ERR("send address fail\n"); + return -1; + } + + /* send firmware data */ + data[0] = (uint8_t)len - 1; + memcpy(&data[1], buf, len); + if (ble_uart_send_data(bl_uart, data, len + 1) < 0) { + LOG_ERR("send data fail\n"); + return -1; + } + + printf("."); + gpio_pin_toggle(blue_gpio.port, blue_gpio.pin); + return 0; +} + +static int ble_bl_fw_upgrade(const struct device *bl_uart, const uint8_t *buf, const uint32_t len) +{ + int i; + uint32_t nb = len / 256; + uint16_t r = len % 256; + uint32_t address; + const uint8_t *datap; + + for (i = 0; i < nb; i++) { + address = BLE_FLASH_START_ADDRESS + i * 256; + datap = buf + i * 256; + if (ble_bl_cmd_write_memory(bl_uart, address, datap, 256) < 0) { + return -1; + } + } + + address = BLE_FLASH_START_ADDRESS + nb * 256; + datap = buf + nb * 256; + if (ble_bl_cmd_write_memory(bl_uart, address, datap, r) < 0) { + return -1; + } + printf("\n"); + + return 0; +} + +/* + * BLE f/w upgrade would proceed if user gives his feedback either + * pressing 'y' (or 'Y') on console or pressing button BT2 on the + * board. + */ +static int check_user_feedback(void) +{ + int in, ret = 0; + uint8_t go = 0; + + static const struct device *const console = DEVICE_DT_GET(DT_CHOSEN(zephyr_console)); + const struct gpio_dt_spec sw2 = GPIO_DT_SPEC_GET(DT_ALIAS(sw2), gpios); + + if (!device_is_ready(console)) { + LOG_ERR("console not ready.\n"); + return -1; + } + + if (!gpio_is_ready_dt(&sw2)) { + LOG_ERR("%s: device not ready.\n", sw2.port->name); + return -1; + } + gpio_pin_configure_dt(&sw2, GPIO_INPUT); + + while (1) { + + if (ret == 0) { + printf("Start BLE f/w update (press Y to proceed): "); + } + + ret = uart_poll_in(console, &go); + if (ret == 0) { + printf("%c\n", go); + + if (go == 'y' || go == 'Y') { + break; + } + } + + in = gpio_pin_get_dt(&sw2); + if (in == 1) { + break; + } + } + + return 0; +} + +int main(void) +{ + uint8_t fw_ver; + + printf("SensorTile.box Pro BLE f/w upgrade\n"); + + led_init(); + + /* signal that sample is started */ + if (led_init() < 0) { + return -1; + } + + led_pattern_out(BLE_FW_UPG_START); + + static const struct device *const bl_uart = + DEVICE_DT_GET(DT_CHOSEN(zephyr_bootloader_uart)); + + if (!device_is_ready(bl_uart)) { + LOG_ERR("bl_uart device not ready.\n"); + goto err; + } + + /* check if user acknowledge the upgrade */ + if (check_user_feedback() < 0) { + LOG_ERR("user feedback not working\n"); + goto err; + } + + if (ble_uart_bootloader_activate(bl_uart) < 0) { + LOG_ERR("activation failed\n"); + goto err; + } + printf("bootloader activated!\n"); + + if (ble_bl_cmd_get_version(bl_uart, &fw_ver) < 0) { + LOG_ERR("get_version failed\n"); + goto err; + } + printf("ble bootloader version is %d.0\n", fw_ver); + + if (ble_bl_cmd_mass_erase(bl_uart) < 0) { + LOG_ERR("mass_erase failed\n"); + goto err; + } + printf("MASS ERASE ok\n"); + + if (ble_bl_fw_upgrade(bl_uart, ble_fw_img, ble_fw_img_len) < 0) { + LOG_ERR("write memory failed\n"); + goto err; + } + printf("BLE f/w upgrade ok\n"); + + led_pattern_out(BLE_FW_UPG_OK); + + return 0; + +err: + led_pattern_out(BLE_FW_UPG_ERR); + return -1; +} From fab9d07e2aa49d91d806889c0f41c7c6dbcf1bf2 Mon Sep 17 00:00:00 2001 From: Armando Visconti Date: Fri, 16 Feb 2024 16:49:25 +0100 Subject: [PATCH 3/3] boards: arm: sensortile_box_pro: add bluenrg-lp chip description Extend sensortile_box_pro document adding BlueNRG-LP chip description. Signed-off-by: Armando Visconti --- boards/st/sensortile_box_pro/doc/index.rst | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/boards/st/sensortile_box_pro/doc/index.rst b/boards/st/sensortile_box_pro/doc/index.rst index 50540f91f77343..d1b729eb198ae6 100644 --- a/boards/st/sensortile_box_pro/doc/index.rst +++ b/boards/st/sensortile_box_pro/doc/index.rst @@ -41,6 +41,9 @@ sensors on board) Hardware ******** +The following is a summary of the main board features. More info can be find on `UM3133`_ +and the `schematic`_. + The STM32U585xx devices are an ultra-low-power microcontrollers family (STM32U5 Series) based on the high-performance Arm|reg| Cortex|reg|-M33 32-bit RISC core. They operate at a frequency of up to 160 MHz. @@ -212,6 +215,15 @@ The final application may use it to declare SensorTile.box PRO device as belongi certain standard or vendor class, e.g. a CDC, a mass storage or a composite device with both functions. +BlueNRG-LP chip +=============== + +The board is equipped with an STMicroelectronics `BlueNRG-LP`_ chip. Before using the board Bluetooth +functionality, a firmware upgrade of the chip may be necessary, in order to run a firmware which is +compatible with the Zephyr BT stack (see `stsw-mkbox-bleco`_ for more information). +The upgrade may be easily performed following the procedure explained in +:zephyr:code-sample:`sensortile_box_pro_sample_ble_fwupg` sample. + Console ======= @@ -333,12 +345,21 @@ References .. _SensorTile.box PRO website: https://www.st.com/en/evaluation-tools/steval-mkboxpro.html +.. _UM3133: + https://www.st.com/resource/en/user_manual/um3133-getting-started-with-sensortilebox-pro-multisensors-and-wireless-connectivity-development-kit-for-any-intelligent-iot-node-stmicroelectronics.pdf + +.. _schematic: + https://www.st.com/resource/en/schematic_pack/steval-mkboxpro-schematic.pdf + .. _STM32U585 on www.st.com: https://www.st.com/en/microcontrollers-microprocessors/stm32u575-585.html .. _STM32U585 reference manual: https://www.st.com/resource/en/reference_manual/rm0456-stm32u575585-armbased-32bit-mcus-stmicroelectronics.pdf +.. _BlueNRG-LP: + https://www.st.com/en/wireless-connectivity/bluenrg-lp.html + .. _lsm6dsv16x datasheet: https://www.st.com/en/mems-and-sensors/lsm6dsv16x.html @@ -362,3 +383,6 @@ References .. _DFU-UTIL website: http://dfu-util.sourceforge.net/ + +.. _stsw-mkbox-bleco: + https://www.st.com/en/embedded-software/stsw-mkbox-bleco.html