This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

DFU over USB - nRF52840 running Zephyr

Hello, gentleman!

We are developing our nRF52840-based application using nRF Connect SDK v1.4.2. I know there is a NCS v1.5.1 already available, we plan to switch to that version pretty soon. 

We would like to have the ability to update nRF52840 firmware by connecting it to the PC over USB and transferring updated firmware over USB serial link (USB DFU profile). 

I've been researching a little bit and came across this guide explaining how to perform DFU with MCUBoot bootloader using a serial USB connection to a host PC. Even though this example contains everything we need and helped us a lot to understand the DFU process, it contains two parts that we would like to be able to automate:

  1. Once we have our initial firmware version together with the MCUBoot bootloader flashed into nRF52840, we would like to be able to put the nRF52840 into MCUBoot's DFU mode from our firmware (e.g. at the moment we detect USB power). This process is performed manually in the guide by pressing a Reset button while holding a Button 1 on DK (see step 27). 
  2. Similarly, once the DFU process is finished and new firmware binary file transferred over USB, we need to hit Reset button on nRF52840 DK board in order to restart CPU and boot updated firmware (step 35). Again, we would like to be able to automatically restart the nRF52840 once the DFU process is done because we will not have Reset button exposed to the end user in our final product design. 

With that said, I would have a few questions related to the above limitations:

- Is it possible to somehow put the MCUBoot in DFU mode from inside the firmware? I know that, in the case of nRF SDK and baremetal C approach, we had the possibility to play with the GPREGRET register and put the nRF52 in DFU mode with the following piece of code, for example:

void reboot_into_bootloader(void) {
  uint32_t gp_reg_ret;

  APP_ERROR_CHECK(sd_power_gpregret_set(0, BOOTLOADER_DFU_START));
  nrf_delay_ms(1500);
  APP_ERROR_CHECK(sd_power_gpregret_get(0, &gp_reg_ret));
  perform_system_reset();
}

Can we do something similar from the NCS/Zephyr environment?

- Would it be possible to catch from the NCS/Zephyr side when USB Power is detected/removed on nRF52840 (APP_USBD_EVT_POWER_DETECTED event from nRF SDK world)?

- Finally, is there a way to automatically restart the board once DCU process is finished without a need to manually press Reset button?

Thank you very much for your time and efforts, guys. It is really appreciated. 

Cheers,

Bojan.

  • Hi Bojan,

    The guide you refer to demonstrates the serial recovery feature of MCUBoot, where the DFU process is handled entirely in the bootloader. There is no option to enter DFU by GPREGret there, but you can add it yourself by modifying MCUBoot to check for a magic word in the retention register that you set in your application before resetting. In that case you could look at how this is handled in main() in bootloader\mcuboot\boot\zephyr\main.c and add your logic there.

    Regarding the second question, you can reset via mcumgr using "mcumgr reset".

    Einar

  • Hello, Einar. 

    Thanks for your explanations! 

    I can report that resetting nRF via mcumgr  using:

    mcumgr --conntype=serial --connstring=<USB Serial Device> reset

    after binary file is transferred over USB works fine! 

    Concerning the first question (entering DFU by GPREGRET), I tried what you suggested. Inside the application code, I tried to write the 0xB1 value into GPREGRET register and restart the board after that with the following piece of code:

    static void reboot_into_bootloader(void) {  
      #define BOOTLOADER_DFU_GPREGRET_MASK            (0xB0)      
      #define BOOTLOADER_DFU_START_BIT_MASK           (0x01)  
      #define BOOTLOADER_DFU_START    (BOOTLOADER_DFU_GPREGRET_MASK | BOOTLOADER_DFU_START_BIT_MASK)
      NRF_POWER_Type gp_reg_ret;
      nrf_power_gpregret_set((NRF_POWER_Type *)&gp_reg_ret, BOOTLOADER_DFU_START);
      k_msleep(1500);
      uint8_t gp_reg;
      gp_reg = nrf_power_gpregret_get(&gp_reg_ret);
      printk("System resetting. GPREGRET: %d\n", gp_reg_ret.GPREGRET);
      printk("System resetting. GPREGRET: %d\n", gp_reg);
      NVIC_SystemReset();
    }

    On the other side, inside main.c file located at bootloader\mcuboot\boot\zephyr\main.c, I tried to read the content of GPREGRET register during the boot process with the following piece of code:

    BOOT_LOG_INF("Checking GPREGRET register...");
    NRF_POWER_Type gp_reg_ret;
    uint8_t gp_reg;
    gp_reg = nrf_power_gpregret_get(&gp_reg_ret);
    BOOT_LOG_INF("System booting. GPREGRET: %d\n", (gp_reg_ret.GPREGRET & 0xFF));
    BOOT_LOG_INF("System booting. GPREGRET: %d\n", gp_reg);

    The content of GPREGRET register after restart is equal to 0xC3!

    Do you have any idea why those two values differ? According to some threads (link), GPREGRET register is a RAM mapped retained register which will lose it's content after power-cycle. However, it keeps its content after a wakeup from SYSTEMOFF mode. I was using NVIC_SystemReset() function to reset the CPU. Could it be that the register lost its value because of the way CPU is restarted?

    Also, should I clear the GPREGRET register before writing any value there? nRF SDK has a function for that (sd_power_gpregret_clr()) but I was unable to find anything similar in <hal/nrf_power.h>

    Cheers,

    Bojan.

  • Hi Bojan,

    The retention registers are retained during a soft reset (NVIC_SystemReset()) but not during a pin reset. I did not think about that. GPREGRET is allready in use by sys_arch_reboot() implementatino for nRF52 in zephyr\soc\arm\nordic_nrf\nrf52\soc.c, which is called by sys_reboot(). But GPREGRET2 seems to be unused, so if you use that instead I think you should be good.

    The retention register is not cleared automatically, so you you want to clear it once you have read it in MCUboot and decided to enter DFU mode so that it does not repeat again on next reset and so on.

    Einar

  • Hello, Einar. 

    I tried the same thing with GPREGRET2 register but the same thing happens. No matter what I write into GPREGRET/GPREGRET2 registers, I always read 0xC3 and 0xF6, respectively, after soft resetting the CPU with NVIC_SystemReset() function. 

  • I tried the same thing with the latest NCS v1.5.1 as well. Again, GPREGRET/GPREGRET2 registers have some constant value after soft reset no matter what I write into them before the soft reset. In the case of NCS v1.5.1 the value of those registers differ compared to the v1.4.2:


    v1.4.2

    Button has changed. State: 1
    Button 1 pressed
    Setting GPREGRET/GPREGRET2 registers...
    Before soft reset. GPREGRET: 86
    Before soft reset. GPREGRET2: 172
    *** Booting Zephyr OS build v2.4.0-ncs2  ***
    [00:00:00.254,730] <inf> mcuboot: Starting bootloader. Test!
    [00:00:00.254,760] <inf> mcuboot: Checking GPREGRET/GPREGRET2 registers...
    [00:00:00.254,760] <inf> mcuboot: System booting. GPREGRET: 195
    [00:00:00.254,760] <inf> mcuboot: System booting. GPREGRET2: 246
    [00:00:00.255,249] <inf> mcuboot: Primary image: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3
    [00:00:00.255,249] <inf> mcuboot: Boot source: none
    [00:00:00.255,371] <inf> mcuboot: Swap type: none
    *** Booting Zephyr OS build v2.4.0-ncs2  ***


    v1.5.1

    Button has changed. State: 1
    Button 1 pressed
    Setting GPREGRET/GPREGRET2 registers...
    Before soft reset. GPREGRET: 86
    Before soft reset. GPREGRET2: 172
    *** Booting Zephyr OS build v2.4.99-ncs2  ***
    I: Starting bootloader
    I: Checking GPREGRET/GPREGRET2 registers...
    I: System booting. GPREGRET: 88
    I: System booting. GPREGRET2: 191
    I: Primary image: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3
    I: Secondary image: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3
    I: Boot source: none
    I: Swap type: none
    I: Bootloader chainload address offset: 0x10000
    *** Booting Zephyr OS build v2.4.99-ncs2  ***

Related