MarlinFirmware / Marlin

Marlin is an optimized firmware for RepRap 3D printers based on the Arduino platform. Many commercial 3D printers come with Marlin installed. Check with your vendor if you need source code for your specific machine.
https://marlinfw.org
GNU General Public License v3.0
16.34k stars 19.25k forks source link

[FR] Restart motherboard to DFU mode #27047

Open rondlh opened 6 months ago

rondlh commented 6 months ago

Is your feature request related to a problem? Please describe.

No

Are you looking for hardware support?

No

Describe the feature you want

Allow to restart the motherboard to the DFU mode (STM32 specific I believe). This would offer an alternative way to update the motherboard firmware which if typically done via an SD card. In DFU mode the firmware can be updated via a USB cable connected to a host system.

Benefits:

  1. Alternative way to update the motherboard firmware
  2. No need to access the motherboard SD card slot
  3. No need to have an SD card available
  4. Many motherboards rely on a bootloader to update the firmware (via SD CARD), this bootloader is not needed anymore, which allows for more flash space.
  5. Some motherboards (MKS EBB42) use DFU FW update by default, but 2 buttons needs to be pressed to activate the DFU mode. Handling this in software would be easier.

Additional context

I played around with this a bit and came up with the code below. It works about 50% of the time on an MKS Monster8 (STM32F407), if it doesn't work then the motherboard is just restarted and you can try again. It seems to be important that the USB connection to the host is available during the motherboard startup.

void jump_to_dfu_bootloader(void)
{
/* STM32C0   0x1FFF0000 | STM32F030x8 0x1FFFEC00 | STM32F030xC 0x1FFFD800 | STM32F03xx 0x1FFFEC00
   STM32F05  0x1FFFEC00 | STM32F07    0x1FFFC800 | STM32F09    0x1FFFD800 | STM32F10xx 0x1FFFF000
   STM32F105 0x1FFFB000 | STM32F107   0x1FFFB000 | STM32F10XL  0x1FFFE000 | STM32F2    0x1FFF0000
   STM32F3   0x1FFFD800 | STM32F4     0x1FFF0000 | STM32F7     0x1FF00000 | STM32G0    0x1FFF0000
   STM32G4   0x1FFF0000 | STM32H503   0x0BF87000 | STM32H563   0x0BF97000 | STM32H573  0x0BF97000
   STM32H7x  0x1FF09800 | STM32H7A    0x1FF0A800 | STM32H7B    0x1FF0A000 | STM32L0    0x1FF00000
   STM32L1   0x1FF00000 | STM32L4     0x1FFF0000 | STM32L5     0x0BF90000 | STM32WBA   0x0BF88000
   STM32WBX  0x1FFF0000 | STM32WL     0x1FFF0000 | STM32U5     0x0BF90000 */
  #define DFU_BOOT_ADDRESS 0x1FFF0000
  void (*SysMemBootJump)(void) = (void (*)(void)) (*((uint32_t *) (DFU_BOOT_ADDRESS + 4)));

  SERIAL_FLUSH();
  #ifdef SERIAL_PORT
    MYSERIAL1.end();
  #endif
  #ifdef SERIAL_PORT_2
    MYSERIAL2.end();
  #endif
  #ifdef SERIAL_PORT_3
    MYSERIAL3.end();
  #endif

  HAL_RCC_DeInit(); // Set the clock to the default state (OPTIONAL)
  HAL_DeInit();        // (ESSENTIAL)
  HAL_SuspendTick();

  __disable_irq();
  RCC->CIR = 0x00000000; // Disable all interrupts related to clock  
  SysTick->CTRL = SysTick->LOAD = SysTick->VAL = 0; // Disable systick timer and reset it to default values (ESSENTIAL)

  // Clear Interrupt Enable Register & Interrupt Pending Register
  for (uint32_t i = 0; i < sizeof(NVIC->ICER) / sizeof(NVIC->ICER[0]); i++)
  {
    NVIC->ICER[i] = 0xFFFFFFFF;
    NVIC->ICPR[i] = 0xFFFFFFFF;
  }
  __enable_irq();

  __DSB();
  __HAL_SYSCFG_REMAPMEMORY_SYSTEMFLASH();
  //SYSCFG->MEMRMP = 0x01; // REMAP F4
  //SYSCFG->CFGR1  = 0x01; // REMAP G0
  __DSB();
  __ISB();
  SCB->VTOR = 0; // Reset vector table

  __set_MSP(*(uint32_t *)DFU_BOOT_ADDRESS); // Set the Main Stack Pointer to the boot loader stack
  SysMemBootJump();

  while (1);
}

UPDATE: The code below seems to put the MKS EBB42 (STM32G0) in DFU mode (as reported by the host system), and DFU FW update does now work. So no more need to push the tiny boot and reset buttons on the MKS EBB42.

static void jump_to_dfu_bootloader(void)
{
/* STM32C0   0x1FFF0000 | STM32F030x8 0x1FFFEC00 | STM32F030xC 0x1FFFD800 | STM32F03xx 0x1FFFEC00
   STM32F05  0x1FFFEC00 | STM32F07    0x1FFFC800 | STM32F09    0x1FFFD800 | STM32F10xx 0x1FFFF000
   STM32F105 0x1FFFB000 | STM32F107   0x1FFFB000 | STM32F10XL  0x1FFFE000 | STM32F2    0x1FFF0000
   STM32F3   0x1FFFD800 | STM32F4     0x1FFF0000 | STM32F7     0x1FF00000 | STM32G0    0x1FFF0000
   STM32G4   0x1FFF0000 | STM32H503   0x0BF87000 | STM32H563   0x0BF97000 | STM32H573  0x0BF97000
   STM32H7x  0x1FF09800 | STM32H7A    0x1FF0A800 | STM32H7B    0x1FF0A000 | STM32L0    0x1FF00000
   STM32L1   0x1FF00000 | STM32L4     0x1FFF0000 | STM32L5     0x0BF90000 | STM32WBA   0x0BF88000
   STM32WBX  0x1FFF0000 | STM32WL     0x1FFF0000 | STM32U5     0x0BF90000 */
  #define DFU_BOOT_ADDRESS 0x1FFF0000
  static void (*SysMemBootJump)(void) = (void (*)(void)) (*((uint32_t *) (DFU_BOOT_ADDRESS + 4)));

  SERIAL_FLUSH();
  #ifdef SERIAL_PORT
    MYSERIAL1.end();
  #endif
  #ifdef SERIAL_PORT_2
    MYSERIAL2.end();
  #endif
  #ifdef SERIAL_PORT_3
    MYSERIAL3.end();
  #endif

  HAL_RCC_DeInit(); // Set the clock to the default state (OPTIONAL)
  HAL_DeInit();     // ESSENTIAL!
  HAL_SuspendTick();
  RCC->CIER = 0x00000000; // Disable all interrupts related to clock

  SysTick->CTRL = SysTick->LOAD = SysTick->VAL = 0; // Disable systick timer and reset it to default values (ESSENTIAL)

  __disable_irq();
  // Clear Interrupt Enable Register & Interrupt Pending Register
  for (uint32_t i = 0; i < sizeof(NVIC->ICER) / sizeof(NVIC->ICER[0]); i++)
  {
    NVIC->ICER[i] = 0xFFFFFFFF;
    NVIC->ICPR[i] = 0xFFFFFFFF;
  }
  __enable_irq();

  __DSB();
  __HAL_SYSCFG_REMAPMEMORY_SYSTEMFLASH();
  //SYSCFG->MEMRMP = 0x01; // REMAP F4
  //SYSCFG->CFGR1  = 0x01; // REMAP G0

  __DSB();
  __ISB();
  SCB->VTOR = 0; // Reset vector table

  __set_MSP(*(uint32_t *)DFU_BOOT_ADDRESS); // Set the Main Stack Pointer to the boot loader stack
  SysMemBootJump();

  while (1);
}
rondlh commented 1 month ago

UPDATE:

  1. Better STM32F4 version
  2. Working STM32G0 version.