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
15.94k stars 19.08k forks source link

[FR] Restart motherboard to DFU mode #27047

Open rondlh opened 2 weeks ago

rondlh commented 2 weeks 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)
{
  #define DFU_BOOT_ADDRESS 0x1FFF0000
  void (*SysMemBootJump)(void) = (void (*)(void)) (*((uint32_t *) (DFU_BOOT_ADDRESS + 4)));

/* 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 */

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

  __disable_irq();

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

  HAL_RCC_DeInit(); // Set the clock to the default state (OPTIONAL)
  HAL_DeInit();     // (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;
  }

  //RCC->CIR = 0x00000000; // Disable all interrupts related to clock

  __enable_irq();

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

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

  while (1) { }
}

The code below seems to put the MKS EBB42 (STM32G0) in DFU mode (a reported by the host system), but DFU FW update does NOT work. The buttons on the EBB42 are tiny, it's a bit of a hassle to press them.

void jump_to_dfu_bootloader(void) {
  void (*SysMemBootJump) (void); // Prototype

  volatile uint32_t addr = 0x1FFF0000;

  __disable_irq();
  SysTick->CTRL = SysTick->LOAD = SysTick->VAL = 0;  
  HAL_RCC_DeInit();

  USB->CNTR = 0x0003; // Reset USB

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

  __HAL_RCC_SYSCFG_CLK_ENABLE();          // make sure syscfg clocked
  __DSB();
  __HAL_SYSCFG_REMAPMEMORY_SYSTEMFLASH(); // remap system memory to address 0x0000000
  //SYSCFG->CFGR1 = 0x01;  // REMAP FLASH G0
  __DSB();
  __ISB();
  SCB->VTOR = 0;  // set vector table offset to 0
  FLASH->ACR |= FLASH_ACR_PROGEMPTY;

  SysMemBootJump = (void(*) (void)) (*((uint32_t*) (addr + 4)));
  __set_MSP (*(uint32_t*) addr);
  SysMemBootJump();
}