Disable yellow LED driver for Pro-Micro
[pub/lufa.git] / Bootloaders / DFU / BootloaderDFU.c
index 3e2f330..acd0296 100644 (file)
@@ -45,8 +45,9 @@ static bool IsSecure = SECURE_MODE;
 /** Flag to indicate if the bootloader should be running, or should exit and allow the application code to run
  *  via a soft reset. When cleared, the bootloader will abort, the USB interface will shut down and the application
  *  jumped to via an indirect jump to location 0x0000 (or other location specified by the host).
+ *  Use volatile to prevent compiler to do optimization, because we change this variable from interrupt context.
  */
-static bool RunBootloader = true;
+static volatile bool RunBootloader = true;
 
 /** Flag to indicate if the bootloader is waiting to exit. When the host requests the bootloader to exit and
  *  jump to the application address it specifies, it sends two sequential commands which must be properly
@@ -55,6 +56,21 @@ static bool RunBootloader = true;
  */
 static bool WaitForExit = false;
 
+/** Minimum time in seconds stay forced into bootloader mode before application will be started. This especially
+ *  helpful if the application does not support any update or reflashing or jumping back into bootloader. It prevents
+ *  to brick the device if now ISP programmer is available.
+ */
+#ifndef BL_TIME
+#define BL_TIME 5 /* seconds */
+#endif
+
+/** Minimum time ticks stay in bootloader before application is started or negative value to prevent leaving
+ *  bootloader. Timer 1 is a 16 bit timer, which overflows after 65536 cycles if it is load with zero. The
+ *  timer 1 prescaler is programmed to divide by 64 and the prescaler engine uses a clock_div_1 divisor, means
+ *  no divisor of the CPU clock frequency F_CPU.
+ */
+static int8_t ForceBootloaderTime = ((BL_TIME) > 0) ? (F_CPU / 64 * (BL_TIME) / 65536 + 1) : -1;
+
 /** Current DFU state machine state, one of the values in the DFU_State_t enum. */
 static uint8_t DFU_State = dfuIDLE;
 
@@ -131,7 +147,53 @@ void Application_Jump_Check(void)
 
                /* Re-enable JTAG debugging */
                JTAG_ENABLE();
+       #elif ((BOARD == BOARD_PROMICRO) || (BOARD == BOARD_MICRO))
+               /* Pro-Micro and Arduino Micro board use power-on reset, but no external reset. Both boards have
+                * the hardware bootloader pin HWBE enabled. Unfortunately only the external reset allows together
+                * with an enabled HWBE that the CPU start at the bootloader address independent of the FUSE_BOOTRST.
+                * That means the power-on reset will start just controlled by the FUSE_BOOTRST the bootloader or
+                * direct in the application and cannot be overridden by HWBE signal. Therfore FUSE_BOOTRST shall
+                * be enabled, otherwise the bootloader will not be reached for these boards.
+                * The bootloader checks FUSE_HWBE as *unprogammed* instead of FUSE_BOOTRST as programmed on other
+                * board variants to decide fast application start, without waiting the dedicted bootloader timeout
+                * in case of a USB, watchdog, brown-out or JTAG reset. If the watchdog reset was initiated from
+                * the bootloader marked with the MAGIC_BOOT_KEY this reset flag is reset. All other reset flags
+                * are left untouched to allow the application code checking the reset signals, especially in case
+                * of application fast start.
+                * The bootloader is entered always for external reset and power-on reset. But the bootloader is
+                * anyway exited after that dedicted timeout, if a reset-vector to the application is programmed.
+                * Once a DFU program interacts this the bootloader during this dedicted timeout, the timer stops
+                * and the application needs to be started by DFU bootloader command manually or using a reset.
+                */
+
+               /* Check if the device's forced Bootloader via Hardware Bootenable is unprogrammed */
+               if (BootloaderAPI_ReadFuse(GET_EXTENDED_FUSE_BITS) & ~FUSE_HWBE)
+               {
+                       /* If the reset source was not an external or power-on reset jump to the application */
+                       if (!(MCUSR & ((1 << EXTRF) || (1 << PORF))))
+                         JumpToApplication = true;
+               }
+               /* If the reset source was the bootloader and the key is correct, clear it and jump to the application;
+                * this can happen in the HWBE fuse is set, and the HBE pin is low during the watchdog reset */
+               if ((MCUSR & (1 << WDRF)) && (MagicBootKey == MAGIC_BOOT_KEY))
+               {
+                       JumpToApplication = true;
+
+                       /* Clear reset source */
+                       MCUSR &= ~(1 << WDRF);
+               }
        #else
+               /* Check if the device's BOOTRST fuse is set */
+               if (!(BootloaderAPI_ReadFuse(GET_HIGH_FUSE_BITS) & ~FUSE_BOOTRST))
+               {
+                       /* If the reset source was not an external reset or the key is correct, clear it and jump to the application */
+                       if (!(MCUSR & (1 << EXTRF)) || (MagicBootKey == MAGIC_BOOT_KEY))
+                         JumpToApplication = true;
+
+                       /* Clear reset source */
+                       MCUSR &= ~(1 << EXTRF);
+               }
+               else
                {
                        /* If the reset source was the bootloader and the key is correct, clear it and jump to the application;
                         * this can happen in the HWBE fuse is set, and the HBE pin is low during the watchdog reset */
@@ -143,6 +205,9 @@ void Application_Jump_Check(void)
                }
        #endif
 
+       /* Clear the boot key in any case */
+       MagicBootKey = 0;
+
        /* Don't run the user application if the reset vector is blank (no app loaded) */
        bool ApplicationValid = (pgm_read_word_near(0) != 0xFFFF);
 
@@ -150,20 +215,13 @@ void Application_Jump_Check(void)
        if (JumpToApplication && ApplicationValid)
        {
                /* Turn off the watchdog */
-               MCUSR &= ~(1 << WDRF);
                wdt_disable();
 
-               /* Clear the boot key and jump to the user application */
-               MagicBootKey = 0;
-
                // cppcheck-suppress constStatement
                ((void (*)(void))0x0000)();
        }
 }
 
-
-static volatile bool stayinbootloader;
-
 /** Main program entry point. This routine configures the hardware required by the bootloader, then continuously
  *  runs the bootloader processing routine until instructed to soft-exit, or hard-reset via the watchdog to start
  *  the loaded application code.
@@ -174,42 +232,14 @@ int main(void)
        SetupHardware();
 
        /* Turn on first LED on the board to indicate that the bootloader has started */
-       //LEDs_SetAllLEDs(LEDS_LED1);
+       LEDs_SetAllLEDs(LEDS_LED1);
 
        /* Enable global interrupts so that the USB stack can function */
        GlobalInterruptEnable();
 
        /* Run the USB management task while the bootloader is supposed to be running */
-       /*if bit_is_clear(PINB,PB5)     // PB5 is Digital 9 on Arduino Pro Micro
-       {
-               loop_until_bit_is_set(PINB,PB5);
-
-               while ((RunBootloader || WaitForExit) && bit_is_set(PINB,PB5))
-                       USB_USBTask();
-
-               loop_until_bit_is_clear(PINB,PB5);
-       }*/
-
-       stayinbootloader = false;
-
-       uint16_t i = 0;
        while (RunBootloader || WaitForExit)
-       {
-               USB_USBTask();
-
-               if (!stayinbootloader)
-               {
-                       _delay_ms(1);
-                       if (i++ > 5000)
-                       {
-                               break;
-                       }
-               }
-               else
-               {
-                       i = 0;
-               }
-       }
+         USB_USBTask();
 
        /* Wait a short time to end all USB transactions and then disconnect */
        _delay_us(1000);
@@ -237,14 +267,11 @@ static void SetupHardware(void)
 
        /* Initialize the USB and other board hardware drivers */
        USB_Init();
-       //LEDs_Init();
-       DDRB = 1;
-       PORTB = _BV(PB5);
-       DDRD = 0b00100000;
-       PORTD = 0;
+       LEDs_Init();
 
        /* Bootloader active LED toggle timer initialization */
        TIMSK1 = (1 << TOIE1);
+       /* config timer 1 prescaler to F_CPU / clock_div_1 / 64 */
        TCCR1B = ((1 << CS11) | (1 << CS10));
 }
 
@@ -253,11 +280,7 @@ static void ResetHardware(void)
 {
        /* Shut down the USB and other board hardware drivers */
        USB_Disable();
-       //LEDs_Disable();
-       DDRB = 0;
-       PORTB = 0;
-       DDRD = 0;
-       PORTD = 0;
+       LEDs_Disable();
 
        /* Disable Bootloader active LED toggle timer */
        TIMSK1 = 0;
@@ -271,10 +294,13 @@ static void ResetHardware(void)
 /** ISR to periodically toggle the LEDs on the board to indicate that the bootloader is active. */
 ISR(TIMER1_OVF_vect, ISR_BLOCK)
 {
-       //LEDs_ToggleLEDs(LEDS_LED1 | LEDS_LED2);
-       PORTB &= ~_BV(PB0);
-       _delay_ms(5);
-       PORTB |= _BV(PB0);
+       LEDs_ToggleLEDs(LEDS_LED1 | LEDS_LED2);
+       /* Count number for forced ticks not below zero */
+       if (ForceBootloaderTime > 0)
+         ForceBootloaderTime--;
+       /* check if it is time to leave the bootloader and a valid application exists */
+       if ((ForceBootloaderTime == 0) && (pgm_read_word_near(0) != 0xFFFF))
+         RunBootloader = false;
 }
 
 /** Event handler for the USB_ControlRequest event. This is used to catch and process control requests sent to
@@ -290,13 +316,11 @@ void EVENT_USB_Device_ControlRequest(void)
                return;
        }
 
-stayinbootloader = true;
+       /* prevent counter to reach zero */
+       ForceBootloaderTime = -1;
 
        /* Activity - toggle indicator LEDs */
-       //LEDs_ToggleLEDs(LEDS_LED1 | LEDS_LED2);
-       PORTB &= ~_BV(PB0);
-       _delay_ms(5);
-       PORTB |= _BV(PB0);
+       LEDs_ToggleLEDs(LEDS_LED1 | LEDS_LED2);
 
        /* Get the size of the command and data from the wLength value */
        SentCommand.DataSize = USB_ControlRequest.wLength;
@@ -815,7 +839,7 @@ static void ProcessWriteCommand(void)
                        else                                                               // Start via jump
                        {
                                /* Set the flag to terminate the bootloader at next opportunity if a valid application has been loaded */
-                               if (pgm_read_word_near(0) == 0xFFFF)
+                               if (pgm_read_word_near(0) != 0xFFFF)
                                  RunBootloader = false;
                        }
                }