diff --git a/nanoBoot.S b/nanoBoot.S index fccf016..13dc630 100644 --- a/nanoBoot.S +++ b/nanoBoot.S @@ -48,34 +48,77 @@ ; hfuse memory = 0xDF ; efuse memory = 0xC4 -; LED -- Configure this for the LED -; -; Teensy 2.0 compatible board (Adjust for other board) + +; ========================================================== +; LED SUPPORT START + +; Turn LED on while bootloader is active +; NOTE: This feature uses 6 bytes for active high and 8 bytes for active low; +; see "Enable LED" in the "run_bootloader" section for details. + +; Uncomment the following line to enable LED feature +; #define LED_ENABLED + +; LED Configuration +; Uncomment or add a new LED configuration for your specific board + +; Adafruit's Atmega32u4 Breakout Board (Product ID: 296) - Now discontinued +; https://www.adafruit.com/product/296 +; -- LED is ON with ATmega32u4 PIN E6 HIGH +; #define LED_BIT 6 +; #define LED_CONF DDRE +; #define LED_PORT PORTE +; #define LED_ACTIVE_LEVEL 1 + +; Teensy 2.0 compatible board ; -- LED is ON with ATmega32u4 PIN D6 HIGH -#define LED_BIT 6 -#define LED_CONF DDRD -#define LED_PORT PORTD +; #define LED_BIT 6 +; #define LED_CONF DDRD +; #define LED_PORT PORTD +; #define LED_ACTIVE_LEVEL 1 -; Leonardo/Nano compatible board (Adjust for other board) +; Leonardo/Nano compatible board ; -- LED is ON with ATmega32u4 PIN C7 HIGH -; #define LED_BIT 7 -; #define LED_CONF DDRC -; #define LED_PORT PORTC +; #define LED_BIT 7 +; #define LED_CONF DDRC +; #define LED_PORT PORTC +; #define LED_ACTIVE_LEVEL 1 -; Pro Micro compatible board (Adjust for other board) +; Pro Micro compatible board ; -- LED is ON with ATmega32u4 PIN D5 LOW -; #define LED_BIT 5 -; #define LED_CONF DDRD -; #define LED_PORT PORTD +; #define LED_BIT 5 +; #define LED_CONF DDRD +; #define LED_PORT PORTD +; #define LED_ACTIVE_LEVEL 0 -; Pro Micro compatible board (Adjust for other board) +; Pro Micro compatible board ; -- LED is ON with ATmega32u4 PIN B3 LOW -; #define LED_BIT 3 -; #define LED_CONF DDRB -; #define LED_PORT PORTB +; #define LED_BIT 3 +; #define LED_CONF DDRB +; #define LED_PORT PORTB +; #define LED_ACTIVE_LEVEL 0 + +#if defined(LED_ENABLED) + #if !defined(LED_PORT) || !defined(LED_CONF) || !defined(LED_BIT) || !defined(LED_ACTIVE_LEVEL) + #error "If LED feature is enabled, the following need to be defined: LED_BIT, LED_CONF, LED_PORT, LED_ACTIVE_LEVEL" + #else + ; Set IO register as output for LED + #define ENABLE_LED_OUTPUT sbi _SFR_IO_ADDR(LED_CONF), LED_BIT + #if LED_ACTIVE_LEVEL == 1 + #define TURN_LED_ON sbi _SFR_IO_ADDR(LED_PORT), LED_BIT + #define TURN_LED_OFF cbi _SFR_IO_ADDR(LED_PORT), LED_BIT + #elif LED_ACTIVE_LEVEL == 0 + #define TURN_LED_ON cbi _SFR_IO_ADDR(LED_PORT), LED_BIT + #define TURN_LED_OFF sbi _SFR_IO_ADDR(LED_PORT), LED_BIT + #else + #error "LED_ACTIVE_LEVEL needs to be either 1 (active high) or 0 (active low)" + #endif + #endif +#endif + +; LED SUPPORT END +; ========================================================== -; Except for Pro Micro compatible board, initially LED is off. -; This code assumes Teensy 2.0 or Leonardo/Nano compatible board ; SW assumptions: ; The bootloader only "needs" endpoint 0; however, the HID spec requires any HID device to have an @@ -162,9 +205,6 @@ # define BOOT_ADDRESS 0 #endif -; For debugging purposes -.equ LED_PIN, 6 - .section .vectors ; We still want the reset vector to jump to "main" @@ -343,12 +383,9 @@ main: ; We MUST disable the Watchdog Timer first, otherwise it will remain enabled and will keep resetting the system, so... ; Disable Watchdog Timer - ldi r16, _BV(WDCE) | _BV(WDE) ; Load r16 with the value needed to "unlock" the Watchdog Timer Configuration - ; Write a logic one to the Watchdog Change Enable bit (WDCE) and Watchdog System Reset Enable (WDE) - mov r17, rZERO ; Load r17 with zero to disable the Watchdog Timer completely - rcall set_watchdog_timer ; Call the subroutine that sets the watchdog timer with the values loaded in r16 and r17 + rcall set_watchdog_timer ; Call the subroutine that sets the watchdog timer with the value loaded in r17 ; check_reset_flags: sbrs rMCUSR, EXTRF ; Skip the next instruction if EXTRF is set (if External Reset Flag, skip next instruction, go to run_bootloader) @@ -357,11 +394,21 @@ run_application: ; We get here if the cause of th jmp 0 ; Simply jump to 0x0000 (application) IMPORTANT NOTE!! This CANNOT be an 'rjmp'!! run_bootloader: - sbi _SFR_IO_ADDR(LED_CONF), LED_BIT ; Set IO register as output for LED - ; No need to trun off LED initially (non-promicro) -- MCU port is initialized as 0 - ;sbi _SFR_IO_ADDR(LED_PORT), LED_BIT ; Trun off LED initially (promicro) set ; Initialize BootLoaderActive flag (T flag in SREG) +; Enable LED +#if defined(LED_ENABLED) + ; Set IO register as output for LED + ENABLE_LED_OUTPUT + + ; If the LED is active low, we need to turn it off here (set LED_BIT) since the MCU IO port is + ; initialized as 0. This is a 2-byte penalty when using active low LED. + #if LED_ACTIVE_LEVEL == 0 + TURN_LED_OFF + #endif +#endif + + ; ================================================================= ; = Setup IRQ Vector Table @@ -551,23 +598,22 @@ exit_bootloader: ori r16, _BV(DETACH) ; Set the DETACH bit to enable the detachment std Y+oUDCON, r16 ; Store r16 to the USB Device Configuration Register (UDCON) +#if defined(LED_ENABLED) + ; Turn LED off before exiting + TURN_LED_OFF +#endif + ; ================================================================= ; = Watchdog Timer initialization ; ================================================================= - cbi _SFR_IO_ADDR(LED_PORT), LED_BIT ; Trun off LED before exiting (non-promicro) - ;sbi _SFR_IO_ADDR(LED_PORT), LED_BIT ; Trun off LED before exiting (promicro) - ; NOTE!! This part of the code assumes MCUSR has already been cleared ; Enable WDT, ~250 ms timeout (force a timeout to reset the AVR) - ldi r16, _BV(WDCE) | _BV(WDE) ; Load r16 with the value needed to "unlock" the Watchdog Timer Configuration - ; Write a logic one to the Watchdog Change Enable bit (WDCE) and Watchdog System Reset Enable (WDE) - ldi r17, _BV(WDE) | _BV(WDP2) ; Load r17 with the value needed to set the desired Watchdog Configuration (WDCE = 0, not set!) ; Write the WDE and Watchdog prescaler bits (WDP); System Reset Mode (WDE = 1) and ~250 ms timeout (WDP2 = 1) - rcall set_watchdog_timer ; Call the subroutine that sets the wathdog timer with the values loaded in r16 and r17 + rcall set_watchdog_timer ; Call the subroutine that sets the watchdog timer with the value loaded in r17 ; for (;;); final_loop: @@ -719,6 +765,14 @@ HANDLE_USB_STANDARD_DEVICE: ; fallthrough to SET_CONFIGURATION if equal SET_CONFIGURATION: +#if defined(LED_ENABLED) + ; Turn LED on towards the end of enumeration (SET_CONFIGURATION is done after SET_ADDRESS) + ; TODO: If we ever have space, we could add a flag here to mark the fact that we have entered + ; this state, and turn the LED on at the end of the setup request. For now this is the best we + ; can do. + TURN_LED_ON +#endif + ; Optimization by "sigprof" that saves 2 bytes ; Dirty trick: We don't need to do anything for SET_CONFIGURATION except process_Host2Device, ; so we reuse the SET_ADDRESS code by making it reload the same value to UDADDR. @@ -937,9 +991,6 @@ verifyMaxDescriptorLength: send_descriptor: - sbi _SFR_IO_ADDR(LED_PORT), LED_BIT ; Trun on LED before exiting (non-promicro) - ;cbi _SFR_IO_ADDR(LED_PORT), LED_BIT ; Trun on LED before exiting (promicro) - ; Abort if RXSTPI is set ldd r17, Y+oUEINTX ; Load r17 with the value in the USB Endpoint Interrupt Register (UEINTX); sbrc r17, RXSTPI ; Skip the next instruction if the Received SETUP Interrupt Flag (RXSTPI) is cleared @@ -1020,8 +1071,11 @@ EP_ISR_END: set_watchdog_timer: - ; IMPORTANT!! This function assumes the correct values for the WDTCSR register - ; configuration are already loaded onto r16 and 17. + ; IMPORTANT!! This function assumes the correct value for the WDTCSR register + ; configuration is already loaded onto r17; it also modifies r16. + + ldi r16, _BV(WDCE) | _BV(WDE) ; Load r16 with the value needed to "unlock" the Watchdog Timer Configuration + ; Write a logic one to the Watchdog Change Enable bit (WDCE) and Watchdog System Reset Enable (WDE) wdr ; Reset the Watchdog Timer