|
| 1 | +--- |
| 2 | +type: post |
| 3 | +categories: code |
| 4 | +header_background: /img/atmega32u4_closeup.jpeg |
| 5 | +tags: [ bootloader, avr ] |
| 6 | +title: "AVR bootloaders: reboot into bootloader" |
| 7 | +date: 2014-01-15T10:42:53+02:00 |
| 8 | +summary: "I needed to tweak the bootloader to get a working reboot into bootloader feature. With help from the AVRFreaks community, I thought it'd be a good idea to share my experience working with AVR bootloaders." |
| 9 | +lang: english |
| 10 | +logo: "/img/avr.png" |
| 11 | +aliases: |
| 12 | + - /code/on/avr-for-ble-and-ios/ |
| 13 | +--- |
| 14 | + |
| 15 | +When dealing with firmware updates, you need to make the AVR reboot into bootloader. And |
| 16 | +there are lots of solution when you try to do that: |
| 17 | + |
| 18 | + - either you force a reboot using the 'reset' button, and try to launch the avrdude command |
| 19 | + just 42ms after hitting the switch, or |
| 20 | + - you can use the not always wired up and not always working properly DTR line pushed down, or |
| 21 | + - you can wire a pin to the reset button — which is the worst solution, as avr pins are in an undefined state |
| 22 | + at startup time) or |
| 23 | + - you can use the watchdog reset to reboot. |
| 24 | + |
| 25 | +My preferred solution would be the DTR line pulled down, though it's mostly an electronic |
| 26 | +solution and it does not work with newer atmega USB MCUs. So, let's have fun hacking the |
| 27 | +bootloader. |
| 28 | + |
| 29 | +As I hacked this on the stk500 bootloader from Peter Fleury, I'm going to show the modifications |
| 30 | +based on that bootloader, which is known to work on all AVR from the atmega168 to the 2560. But |
| 31 | +that kind of hack could be easily patched on optiboot, the newer smaller (less than 1k) bootloader. |
| 32 | + |
| 33 | +In that latest version of the firmware, patched by Mark Sproul, he fixed an issue with the watchdog |
| 34 | +timer, which we can thank him for, as we won't have to do write it. Indeed, when you set up a watchdog |
| 35 | +in your firmware for something like 1 second, that for a reason your code gets reboot, and then you |
| 36 | +have a 2 seconds timeout in bootloader mode, the watchdog will get triggered during boot time and |
| 37 | +won't be reset unless you hard reset the AVR. |
| 38 | + |
| 39 | +So his code in the `_FIX_ISSUE_181_` preprocessor block fixes that: |
| 40 | + |
| 41 | + uint8_t mcuStatusReg; |
| 42 | + mcuStatusReg = MCUSR; |
| 43 | + |
| 44 | + __asm__ __volatile__ ("cli"); |
| 45 | + __asm__ __volatile__ ("wdr"); |
| 46 | + MCUSR = 0; |
| 47 | + WDTCSR |= _BV(WDCE) | _BV(WDE); |
| 48 | + WDTCSR = 0; |
| 49 | + __asm__ __volatile__ ("sei"); |
| 50 | + // check if WDT generated the reset, if so, go straight to app |
| 51 | + if (mcuStatusReg & _BV(WDRF)) |
| 52 | + { |
| 53 | + app_start(); |
| 54 | + } |
| 55 | + |
| 56 | +This code stores the reboot status register in `mcuStatusReg`, clears the watchdog reset |
| 57 | +registers, and if a watchdog caused the reboot, jump straight to address `0x00` (which |
| 58 | +`app_start()` points to). |
| 59 | + |
| 60 | +Though the only minor modification we need to do here is: |
| 61 | + |
| 62 | + if (mcuStatusReg & _BV(WDRF)) |
| 63 | + { |
| 64 | + boot_timeout = 200000; // sixteen LED blinks |
| 65 | + } else { |
| 66 | + boot_timeout = 50000; // four LED blinks |
| 67 | + } |
| 68 | + |
| 69 | +So there's a really long timeout at boot time in order for the flashing to be done. |
| 70 | + |
| 71 | +And voila `\o/` |
| 72 | + |
| 73 | +Don't forget to merge the bootloader with your firmware before uploading using your |
| 74 | +favorite flasher and now you can reboot into bootloader using the following code in |
| 75 | +your firmware: |
| 76 | + |
| 77 | + #include <avr/wdt.h> |
| 78 | + |
| 79 | + void reboot_into_bootloader(void) { |
| 80 | + wdt_enable(WDTO_15MS); |
| 81 | + for(;;); |
| 82 | + } |
| 83 | + |
0 commit comments