Firmware update without button...

Status
Not open for further replies.

edsut

Well-known member
Hi,
I have been working with an NXP evaluation board for the '1062, and recently decided to take a go at
the Teensy4.x. I am working on a 6-channel microphone board that the Teensy4.x will plug into
and the application will essentially be a 6-channel USB audio device. I have pieces of this working on
the eval board, but want to transition over to Teensy4.x.
Anyway, my board needs to be update-able without manual intervention (pushing the button). I've
seen some discussion of this on the forum but nothing that appeared to be applicable when Teensy
looks like a usb audio device.
The only solution that occurs to me is to create a composite USB device (Audio-in and COM) so
that a host could connect to the COM port and issue a command that would toggle a pulled-up
GPIO pin that would be tied to the PROG pin on the board (causing the MKL02 to reboot the '1062
running HalfKay).
I saw some mention of _reboot_Teensyduino_ but didn't see it in the core library. Does that apply
to the 4.x hardware?
Any suggestions would be helpful!
Thanks,
Ed
 
Replying to my own question here...

Ok, I realize now that the external connection to the PROG pin isn't necessary (at least not for cases where the processor has remained sane). I now use the __asm__ volatile ("bkpt") instruction which puts the '1062 in debug mode and does some magic on JTAG to let the MKL02 know that has happened. Apparently (please correct me if I'm wrong), the MKL02 detects this transition, and takes over control of the '1062 with JTAG and boots HalfKay automatically. Very nice!

So, assuming the running code is sane, for now I can use some unusual combination of USB-audio control (say vol=0, vol=max, vol=0 within 1 second) to call the "bkpt" instruction. Once that occurs, teensy_loader_cli (or some customized version of that) can be used to do the upgrade; hence, I now have the ability to do a "hands-free" firmware update my Teensy4.x based audio hardware.

If anyone reads this and disagrees please let me know...
Thanks in advance,
 
The "teensy loader" software can load firmware into the Teensy without you pressing the button, and without you doing special things in your firmware.

That being said -- the whole brilliance of the Teensy ecosystem, is that it uses a small, second, microcontroller, whose entire job is to push a small USB firmware into the RAM of the MCU, that the teensyloader can then talk to to download the firmware. If you're not using a teensy or board with that microcontroller (or something equivalent,) then you're going to want to use a JTAG programmer to load the firmware instead.

You are right that _reboot_Teensyduino_() seems declared, but never defined, in the teensy4 firmware:

Code:
PS C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores> grep -r _reboot_ .
./teensy/core_pins.h:void _reboot_Teensyduino_(void) __attribute__((noreturn));
./teensy/pins_teensy.c:void _reboot_Teensyduino_(void)
./teensy/usb_common.h:extern void _reboot_Teensyduino_(void) __attribute__((noreturn));
./teensy3/core_pins.h:void _reboot_Teensyduino_(void) __attribute__((noreturn));
./teensy3/usb_dev.c:volatile uint8_t usb_reboot_timer = 0;
./teensy3/usb_dev.c:                            if (line_coding[0] == 134) usb_reboot_timer = 15;
./teensy3/usb_dev.c:                    usb_reboot_timer = 5;
./teensy3/usb_dev.c:void _reboot_Teensyduino_(void)
./teensy3/usb_dev.c:                    t = usb_reboot_timer;
./teensy3/usb_dev.c:                            usb_reboot_timer = --t;
./teensy3/usb_dev.c:                            if (!t) _reboot_Teensyduino_();
./teensy4/core_pins.h:void _reboot_Teensyduino_(void) __attribute__((noreturn));
./teensy4/usb.c:static uint8_t usb_reboot_timer = 0;
./teensy4/usb.c:                //printf("sof %d\n", usb_reboot_timer);
./teensy4/usb.c:                if (usb_reboot_timer) {
./teensy4/usb.c:                        if (--usb_reboot_timer == 0) {
./teensy4/usb.c:                        usb_reboot_timer = 80; // TODO: 10 if only 12 Mbit/sec
./teensy4/usb.c:                        usb_reboot_timer = 80; // TODO: 10 if only 12 Mbit/sec
./teensy4/usb.c:                        usb_reboot_timer = 80; // TODO: 10 if only 12 Mbit/sec
./teensy4/usb.c:                usb_reboot_timer = 80; // TODO: 10 if only 12 Mbit/sec
./usb_disk/usb.c:                                               _reboot_Teensyduino_();
./usb_flightsim/usb.c:                                          _reboot_Teensyduino_();
./usb_hid/usb.c:                                                _reboot_Teensyduino_();
./usb_midi/usb.c:                                               _reboot_Teensyduino_();
./usb_rawhid/usb.c:                                             _reboot_Teensyduino_();
./usb_serial/usb.c:                             if (!t) _reboot_Teensyduino_();
./usb_serial_hid/usb.c:                         if (!t) _reboot_Teensyduino_();

But, attempting to use it, I get a linker error:

Code:
#include <core_pins.h>

void setup() {
}

void loop() {
  if (milliseconds() > 300000) {
    _reboot_Teensyduino_();
  }
}

Code:
C:\Users\jwatte\AppData\Local\Temp\arduino_build_967071\sketch\led_strip_teensy40.ino.cpp.o: In function `loop':
C:\Users\jwatte\code\led_strip_teensy40/led_strip_teensy40.ino:62: undefined reference to `_reboot_Teensyduino_'
 
The teensy_loader_cli code issues some USB magic to cause the board to call __asm__ volatile ("bkpt");
So, in your code, just replace _reboot_Teenshduino_(); with __asm__ volatile ("bkpt");
and you'll transition to HalfKay.

Not sure why _reboot_Teensyduino_() isn't in the core anymore, but that function was just a wrapper for
the "bkpt" instruction as far as I can tell.
 
Indeed usb.c watches for some incoming variation of this:: if (baud == 134UL) reboot_timer = 15; ... on T_4.x :: if (usb_cdc_line_coding[0] == 134) {
Then when the timer is ripe on T_4.x it does :: asm("bkpt #251"); // run bootloader
 
This is probably a question only the writer of HalfKay can answer...
Is there any significance to the '251' argument?
 
I haven't worked at the bit level with JTAG, but I'm guessing you have access to that value...
One interesting feature of HalfKay would be to provide a "bkpt NNN" that simply told
it to hard-reset the main '1062 processor.
 
Replying to my own question here...

Ok, I realize now that the external connection to the PROG pin isn't necessary (at least not for cases where the processor has remained sane). I now use the __asm__ volatile ("bkpt") instruction which puts the '1062 in debug mode and does some magic on JTAG to let the MKL02 know that has happened. Apparently (please correct me if I'm wrong), the MKL02 detects this transition, and takes over control of the '1062 with JTAG and boots HalfKay automatically. Very nice!

So, assuming the running code is sane, for now I can use some unusual combination of USB-audio control (say vol=0, vol=max, vol=0 within 1 second) to call the "bkpt" instruction. Once that occurs, teensy_loader_cli (or some customized version of that) can be used to do the upgrade; hence, I now have the ability to do a "hands-free" firmware update my Teensy4.x based audio hardware.

If anyone reads this and disagrees please let me know...
Thanks in advance,

Hello @edsut, were you able to update with this process? Certainly, updating w/o pressing the button is attractive in a remote environment.

I was looking at the schematic for Teensy 3.6 and I noticed it uses the same chip to deal with the same MKL02Z32VFG4 chip. Is the same functionality available on Teensy 3.6? (i.e., use asm("bkpt #251") to reboot and wait for the firmware update)
 
Hi,
Yes, that worked just fine for me; I used it alot. My environment gave me easy command line access to invoke
this, so that just made it more convenient than the button. The only obvious caveat is that it assumes your
running code is sane so that you are able to invoke the asm("bkpt #251") as needed.

Note1: Its probably best to stick with asm("bkpt #251") (i.e. don't omit the #251 even though right
now it will work the same way) since Paul mentioned (in above comment) that while it isn't important now, it
may be in the future.

Note: I've only used it on Teensy4.1, but I'm pretty sure this will work on 3.6.

HTH,
Ed
 
Hi,
Yes, that worked just fine for me; I used it alot. My environment gave me easy command line access to invoke
this, so that just made it more convenient than the button. The only obvious caveat is that it assumes your
running code is sane so that you are able to invoke the asm("bkpt #251") as needed.

Note1: Its probably best to stick with asm("bkpt #251") (i.e. don't omit the #251 even though right
now it will work the same way) since Paul mentioned (in above comment) that while it isn't important now, it
may be in the future.

Note: I've only used it on Teensy4.1, but I'm pretty sure this will work on 3.6.

HTH,
Ed

Thanks @edsut. I confirm that it works on 3.6 too.
 
Status
Not open for further replies.
Back
Top