Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add capability to force stay in DFU (and erase app) with button if #294

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions ports/mimxrt10xx/boards.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@
#include "tusb.h"
#endif

// allow board.h to change the pin configuration for the button
#ifndef BUTTON_PIN_CONFIG
// default to 22k pull up
#define BUTTON_PIN_CONFIG ((1<<16) | (3<<14) | (1<<13) | (1<<12))
#endif

static bool _dfu_mode = false;

// needed by fsl_flexspi_nor_boot
Expand Down Expand Up @@ -80,11 +86,27 @@ void board_init(void)
GPIO_PinInit(NEOPIXEL_PORT, NEOPIXEL_PIN, &neopixel_config);
#endif

#if TINYUF2_DFU_BUTTON
// Button
IOMUXC_SetPinMux( BUTTON_PINMUX, 1U);
IOMUXC_SetPinConfig(BUTTON_PINMUX, BUTTON_PIN_CONFIG);
gpio_pin_config_t button_config = { kGPIO_DigitalInput, 0, kGPIO_NoIntmode };
GPIO_PinInit(BUTTON_PORT, BUTTON_PIN, &button_config);
#endif

#if TUF2_LOG
board_uart_init(BOARD_UART_BAUDRATE);
#endif
}

#if TINYUF2_DFU_BUTTON
int board_button_read(void)
{
// active low
return BUTTON_STATE_ACTIVE == GPIO_PinRead(BUTTON_PORT, BUTTON_PIN);
}
#endif

void board_teardown(void)
{
// no GPIO deinit for GPIO: LED, Neopixel, Button
Expand Down
15 changes: 15 additions & 0 deletions src/board_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,16 @@
#define TINYUF2_DFU_DOUBLE_TAP 0
#endif

// Force boot to DFU mode when button is pressed
#ifndef TINYUF2_DFU_BUTTON
#define TINYUF2_DFU_BUTTON 0
// Should holding the DFU button perform an erase as well?
# ifndef TINYUF2_DFU_BUTTON_ERASE
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a good idea, though we should only erase app if button is hold long enough e.g 5 seconds, there would be an sequence of indicator e.g

  • within second 0-4 the led will start to blink slowly, and
  • at 5 second it will either blink faster or solid light. Also change RGB color as well.
  • Only start erasing if user left off the button to prevent re-enter this mode once erasing complete

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking about how to implement this, and I'm not 100% sure.

The system timer ticks fire at different intervals depending on what state things are in. Does it make sense to have a 1ms tick, and base everything off of that? That would make timing specific times like 5 seconds for erase a little more straightforward.

The alternative is to keep track of the current tick interval, and use that to count. But that will fail if the ticks are turned off during any time you want to keep track of time intervals.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add a new indicator state for holding button, that would make thing in sync with other. The timer interval could be choose to be a bit different than other state (along with RGB) mostly to inform user.

Copy link
Contributor Author

@ccrome ccrome Mar 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm actually working on 2 separate projects: 1 deeply embedded with no access to buttons, and one handheld with too-easy access to buttons. The latter case has exactly 2 buttons total, one of which is used for the DFU mode as well as a standard meaning once the app starts.

Here's the scenario:

  • The user powers up the device, and is accidentally pressing the DFU button, because that button happens to have function during normal device operation.
  • The device is battery powered, so there's not a trivial way to completely remove power.
  • The user doesn't know anything about DFU, upgrades, or anything else. He just wants his device to work.
  • If there was an immediate erase, that would be bad -- the user would have a device stuck in DFU mode, nowhere near a computer, nor any idea how to recover it.
  • The device will stay in DFU mode forever unless we put some other timeout in there.

So, here's my proposed behavior that I think works for everything:

  • If the button is pressed at check_dfu_mode(), then stay in DFU mode
  • If the button is released within the timeout, then boot the app if it exists.
  • If the button is held longer than 5 seconds, erase the app.

Is there really any point in staying in the DFU and not erasing the app? I can't really imagine why you'd want to force an 'enter DFU state' but not erase the app.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just pushed a version with my proposed behavior. It's a bit more of a change than I thought it would be -- but I'm not sure I could make it a whole lot simpler -- just the way all the bits interact seem to reach into a few more places than expected.

I also updated the LED specifications so the timer interval is specified in the same spot as the RGB colors. I hope that's okay. If not, I can revert it back.

# define TINYUF2_DFU_BUTTON_ERASE 0
# endif
#endif


// Use Display to draw DFU image
#ifndef TINYUF2_DISPLAY
#define TINYUF2_DISPLAY 0
Expand Down Expand Up @@ -91,6 +101,11 @@ void board_reset(void);
// Write PWM duty value to LED
void board_led_write(uint32_t value);

#if TINYUF2_DFU_BUTTON
// Read button. Return true if pressed
int board_button_read(void);
ccrome marked this conversation as resolved.
Show resolved Hide resolved
#endif

// Write color to rgb strip
void board_rgb_write(uint8_t const rgb[]);

Expand Down
23 changes: 22 additions & 1 deletion src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,15 @@
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF PROTYPES
//--------------------------------------------------------------------+
//#define USE_DFU_BUTTON 1

// timeout for double tap detection
#define DBL_TAP_DELAY 500

// when sensing the button state, wait this long before sampling the pin
#define BUTTON_SETTLE_DELAY 20
ccrome marked this conversation as resolved.
Show resolved Hide resolved

#ifndef DBL_TAP_REG

// defined by linker script
extern uint32_t _board_dfu_dbl_tap[];
#define DBL_TAP_REG _board_dfu_dbl_tap[0]
Expand Down Expand Up @@ -104,6 +107,24 @@ int main(void)
static bool check_dfu_mode(void)
{
// TODO enable for all port instead of one with double tap
#if TINYUF2_DFU_BUTTON
// always stay in dfu mode if the button is pressed.
// wait for a few milliseconds for the switch pin to reach its pulled value.
_timer_count = 0;
board_timer_start(1);
while(_timer_count < BUTTON_SETTLE_DELAY) {}
board_timer_stop();
if (board_button_read()) {
// force erase app if forced into bootloader mode.
#if TINYUF2_DFU_BUTTON_ERASE
indicator_set(STATE_WRITING_STARTED);
board_flash_erase_app();
indicator_set(STATE_WRITING_FINISHED);
#endif
return true;
}
#endif

#if TINYUF2_DFU_DOUBLE_TAP
// TUF2_LOG1_HEX(&DBL_TAP_REG);

Expand Down