adafruit / uf2-samdx1

MSC bootloader (based on UF2) for SAMD21
Other
210 stars 182 forks source link

Adding a timeout to the bootloader #209

Open Abdul-Origo opened 7 months ago

Abdul-Origo commented 7 months ago

We are using SamD21 in weather stations but ESD and Electrostatic becomes an issue sometimes. High Frequency Transient voltages somehow put SamD21 into bootloader mode. I was wondering if anyone can help me implement a timeout to the bootloader. Whereby after in bootloader mode for lets say 1 minutes, It automatically exits the bootloader mode.

dhalbert commented 6 months ago

This should not be hard to add.

So the use case is that ESD has done the equivalent of a double-click, and you want it to reset instead after a period of time?

Are these your own boards? Are you using this bootloader instead of the Arduino one because you're using UF2's?

Abdul-Origo commented 6 months ago

We tried replicating it using ESD gun which forces it into the bootloader mode, @lodhi-origo tried to remove the double-tap-magic from the main.c but the issue still persists. Now we think having it reset after it stalls would be the ideal solution for our application and adding a timer after which it replicates the reset button would make it perfect for outdoors remote use.

Yes these are custom boards and we are using SAMD21G18A.

Thanks a bunch

AnniePannie commented 6 months ago

Hi Dan, The ESD has made the SAMD21 reboot to the point where it needs a double-click to continue execution. We would like to implement a timeout at this point, so if it is not getting a double-click, it will continue execution/boot and start the normal bin code/application after a number of seconds e.g. 15 seconds, which should be configurable.

dhalbert commented 6 months ago

@lodhi-origo tried to remove the double-tap-magic from the main.c but the issue still persists.

That will make it worse, I think, because any reset will switch immediately to the bootloader and stay there.

I think it might be a question of whether it's really in the bootloader or is stuck in some other way. Does your custom board have an LED that pulses like the Adafruit boards when it's in the bootloader: slow "breathing" pulse when connected to USB, faster pulse when not connected?

More in the next post.

dhalbert commented 6 months ago

from @lodhi-origo in discord:

I'm trying to add a timer in the bootloader uf2-samdx1 which is compatible with SAMD21G18A because our bootloader is getting stuck in the boot mode if exposed with ESD and we want it to jump to the firmware after x amount of time let's say 15 sec (for testing).

NOTE: We can use any bootloader as far as it is compatible with SAMD21.

The repo from adafruit/uf2-samdx1 is not compatible with ATmel/Microcip Studio so how can I use the debugerr J-Link EDU mini or ATMEL ICE to debug the code? Can you suggest alternative ways. Thanks

Right now building it through make which I've installed using chocolatey and I'm using VS Code with python3.

// Define the timeout duration in milliseconds
#define BOOTLOADER_TIMEOUT 15000 // 15 seconds

static volatile uint32_t millis = 0;

// Timer function to get current time in milliseconds
static uint32_t get_current_millis(void) {
    // This should return the number of milliseconds since the system started
    return millis;
}

void SysTick_Handler(void) {
    millis++;
}

void init_systick(void) {
    SysTick_Config(SystemCoreClock / 1000);
}

this is the code I'm using it to add a timer, but it needs to be adjusted in the repo in such a way that some parts will be going into different files and should be commented out from the main.c

Also, I'm initializing the below two lines in the very start of the main(void)

    SystemInit();
    init_systick();

and then initializing the start_time as below just before the while(1) loop

uint32_t start_time = get_current_millis();

and lastly editing in the main while(1) loop like below

// Check if the timeout has been reached
        if (get_current_millis() - start_time > BOOTLOADER_TIMEOUT) {
            //LED_MSC_ON();
            check_start_application(); // Jump to the main application
            break; // Exit the loop after jumping
        }

Re the above: The systick is already running: it's used to do the LED pulsing. There is already a systick handler in init_samd21.c:

void SysTick_Handler(void) { LED_TICK(); }

You could add timeout checking there. Another thing I thought you might do is just count the number of LED pulses and stop after a few hundred or so.

You may not want to timeout the bootloader if you are connected to USB (check USB_OK()), since that would mean someone is trying to update.

Re your proposed code:

// Check if the timeout has been reached
        if (get_current_millis() - start_time > BOOTLOADER_TIMEOUT) {
            //LED_MSC_ON();
            check_start_application(); // Jump to the main application
            break; // Exit the loop after jumping
        }

Instead of check_start_application(), I'd suggest doing resetIntoApp(). That will set *DBL_TAP_PTR = DBL_TAP_MAGIC_QUICK_BOOT; so it will go directly into the app, but it will do a hard reset first, which I think would be very good to reset any bad state caused by the ESD or power glitch or whatever.

BTW, in the above, if the break executes, you will exit main(), which may does nothing interesting. It doesn't necessarily do a reset. It might even do an infinite loop.

lodhi-origo commented 6 months ago

t

That's also the thing that electronically I'm not sure if it is getting into the bootloader or stuck in some other way. However, we do have a pulsating led on our board and it will start pulsating when hit with ESD, so from this we are concluding that it is halting into the bootloader mode.

dhalbert commented 6 months ago

For debugging, there is a "log to RAM" mechanism in the UF2 code, but I have not been successful at getting it to work. However, I did not try for very long. Usually I instrument things by adding calls to the LED color changer or blinker, or toggle output conditions. For instance, I might blink the LED different numbers of times depending on where I am in the code. There is a helper routine for that.

I don't use Atmel Studio. I use a J-Link directly or from gdb. Writing the bootloader is a nuisance because of the fuses. It's tricky to write to the fuses from JLinkExe: you need to do the write and then step the program one instruction to get the write to complete.

lodhi-origo commented 6 months ago

Hi Dan

I initialized ledOnToggleCount in utils.c and increment it under curr == limit is true when signal_end is non-zero.

volatile uint32_t ledOnToggleCount = 0;

so the led_tick() will become like this as below:

void led_tick() {
    led_tick_on = true;
    now++;
    if (signal_end) {
        if (now == signal_end - 1000) {
            LED_MSC_ON();
        }
        if (now == signal_end) {
            signal_end = 0;
        }
    } else {
        uint8_t curr = now & 0xff;
        if (curr == 0) {
            LED_MSC_ON();
            if (limit < 10 || limit > 250) {
                led_tick_step = -led_tick_step;
            }
            limit += led_tick_step;
        } else if (curr == limit) {
            LED_MSC_OFF();
            `ledOnToggleCount++;  // Increment the counter here`
        }
    }
}

and then simply adding the below code in main.c after the USB_OK() in while(1)

// Check if the timeout has been reached
    if (ledOnToggleCount >= 200) {
        resetIntoApp();
    }

The code compiles successfully given that the ledOnToggleCount is defined in uf2.h as extern but the board is still not getting reset after waiting 200 seconds or even 5 minutes.

dhalbert commented 6 months ago

Could you push your code to a repo I can see? Thanks.

lodhi-origo commented 6 months ago

Here is the fork from adafruit in which I add ledOnToggleCount and use two files from \lib\samd21\samd21a\gcc\gcc which I'm not sure if the linker files are correct for SAMD21G18A.

https://github.com/lodhi-origo/uf2-samdx1

lodhi-origo commented 6 months ago

Hi Dan

By removing the brownout protection in the main.c file it will still work fine.

What are the scenarios that it will be useful ?

dhalbert commented 6 months ago

By removing the brownout protection in the main.c file it will still work fine.

Hi - I don't know what you mean here. Did you mean that if you remove the brownout protection your problem goes away? It was added here: https://github.com/adafruit/uf2-samdx1/pull/198. See the (long) forum thread referenced there for why it was added.