diff --git a/.gitignore b/.gitignore index 9004ee19..fd07a44d 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,6 @@ megaavr/extras/.DS_Store *.pyc /megaavr/tools/libs/pymcuprog/deviceinfo/__pycache__ *.bak + +# Local environment-specific tool paths (not for upstream) +megaavr/platform.local.txt diff --git a/README.md b/README.md index d95ff14a..e05f8ab5 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,7 @@ In a couple of sentences, all the "modern" AVRs. * `AVR DA` - Dx-class mid-high pincount general purpose MCU, pincounts 28-64, flash 32k-128k. No HF crystal. Yes QTouch (which we now have a library for!). * `AVR DB` - Dx-class mid-high pincount **MVIO** and **OPAMP** MCU, pincounts 28-64, flash 32k-128k. Yes HF crystal. No QTouch. * `AVR DD` - Dx-class low-mid pincount, low-cost **MVIO** MCU, pincounts 14-32, flash 16-64k. - * `AVR DU` - Newly supported, Dx-class, low-mid pincount **USB** MCU. (anyone know anything about making USB work?) + * `AVR DU` - Newly supported, Dx-class, low-mid pincount **USB** MCU. Native USB CDC serial, Pluggable USB (HID/MIDI), and a USB CDC bootloader are included. * `AVR Ex family` - **Supported by DxCore** Newer line of AVRs. These have a clock and ALU apparently derived more closely from the tinyAVRs than the Dx-series, complete with the speedgrades. Features the new ADC from the 2-series. * `AVR EA` - The first EA-series part, a mid-range, mid-pincount general purpose MCU with proper differential ADC. * `AVR EB` - A lower flash, low-mid pincount MCU with new advanced timers which take advantage of a fancy new PLL, intended for BLDC motor control applications. @@ -408,6 +408,7 @@ The analog comparators are exposed through the [**Comparator library**](https:// * 2 on 28 and 32 pin DA/DB * 3 on 48/64 pin DA/DB * 1 on all DD-series +* 1 on all DU-series * 2 on all EA-series #### Configurable Custom Logic @@ -426,6 +427,7 @@ The ZCD(s) are exposed through the [**ZCD library**](https://github.com/SpenceKo * 2 on 28 and 32 pin DA/DB * 3 on 48/64 pin DA/DB * 1 on all DD +* The DU-series does not have a ZCD * The EA-series does not have a ZCD #### Timers @@ -588,6 +590,8 @@ The USARTs (Serial) have some additional features not seen on the official cores There are two ways to access constants stored in flash on DxCore. Which ones can read data stored where can be confusing; this document should make this clear. #### [Optiboot Bootloader](https://github.com/SpenceKonde/DxCore/blob/master/megaavr/extras/Ref_Optiboot.md) An Optiboot-derived bootloader is provided and may be optionally used. How that impacts operations is described here. This covers relevant considerations for deciding whether to use it as well. +#### [USB CDC Bootloader (AVR DU-series)](https://github.com/SpenceKonde/DxCore/blob/master/megaavr/extras/Ref_USBCDCboot.md) +The DU-series can be bootloaded over its native USB instead - no serial adapter needed. Entry conditions, uploading, the LED, and writing to flash from the app are described here. #### [SerialUPDI](https://github.com/SpenceKonde/AVR-Guidance/blob/master/UPDI/jtag2updi.md) Serial UPDI is our recommended tool for UPDI programming. #### [Clock Information](https://github.com/SpenceKonde/DxCore/blob/master/megaavr/extras/Ref_Clocks.md) diff --git a/megaavr/boards.txt b/megaavr/boards.txt index 107986f4..9ffc5186 100644 --- a/megaavr/boards.txt +++ b/megaavr/boards.txt @@ -99,6 +99,7 @@ menu.mvio=MultiVoltage I/O (MVIO) menu.mvioopti=MultiVoltage I/O (MVIO) menu.attach=attachInterrupt() Version menu.printf=printf() +menu.usbvreg=VUSB Power Source (AVR DU) menu.wiremode=Wire (Wire.h/I2C) Library mode menu.flmap=How to set FLMAP: (64k+ parts only) menu.bootloader-class=Bootloader type (installed by burn + must match 2 upload) @@ -123,6 +124,7 @@ avreb.name=AVR EB-series (no bootloader) avrdaopti.name=AVR DA-series (Optiboot) avrdbopti.name=AVR DB-series (Optiboot) avrddopti.name=AVR DD-series (Optiboot) +avrduusb.name=AVR DU-series (USB CDC Bootloader) #avrduopti.name=AVR DU-series (Optiboot) #avrduusb.name=AVR DU-series with USB Bootloader TBD. #avreaopti.name=AVR EA-series (Optiboot) @@ -450,6 +452,9 @@ avrda.menu.bodmode.sampdisfast.bootloader.bodmodebits=01000 avrda.menu.bodmode.sampdisslow.bootloader.bodmodebits=11000 avrda.menu.bodmode.endisholdwake.bootloader.bodmodebits=01100 +#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^# +# AVR-DU (USB CDC Bootloader) submenu options# +#____________________________________________# #----------------------------------------# # Retain EEPROM (EESAVE) # #________________________________________# @@ -1707,9 +1712,12 @@ avrdu.upload.maximum_data_size=0 avrdu.build.f_cpu={build.speed}000000L avrdu.build.text_section_start=.text=0x0 avrdu.build.defaultswaps= -avrdu.build.extra_flags= {bootloader.appspm} {build.defaultswaps} +avrdu.build.extra_flags= {bootloader.appspm} {build.defaultswaps} {build.usbvreg} avrdu.build.export_merged_output=false -avrdu.build.highestcb=B2 +# Every DU has exactly two TCBs (TCB0/TCB1) at every pin count, unlike the DD +# where the 28/32-pin parts get a TCB2 - so highestcb is B1 for the whole +# family and no chip needs to override it. +avrdu.build.highestcb=B1 avrdu.build.printf= avrdu.build.flmapopts= avrdu.build.attachmode=-DCORE_ATTACH_ALL @@ -1729,6 +1737,27 @@ avrdu.bootloader.CODESIZE=0x00 avrdu.bootloader.BOOTSIZE=0x01 avrdu.bootloader.avrdudestring= avrdu.bootloader.pymcuprogstring=-aerase +# The SYSCFG0 template above expands {bootloader.resetpinbits} and +# {bootloader.eesavebit}, but neither was defined for the DU section, and +# fuse 5 (SYSCFG0) was never written. Add the missing pieces, matching the +# DA/DB/DD sections. +avrdu.bootloader.resetpinbits={bootloader.resetpinbit} +avrdu.bootloader.avrdudefuse5="-Ufuse5:w:{bootloader.SYSCFG0}:m" +avrdu.program.avrdudefuse5="-Ufuse5:w:{bootloader.SYSCFG0}:m" + +avrdu.menu.eesave.enable=EEPROM retained +avrdu.menu.eesave.enable.bootloader.eesavebit=1 +avrdu.menu.eesave.disable=EEPROM not retained +avrdu.menu.eesave.disable.bootloader.eesavebit=0 + +# VUSB power source (SYSCFG.VUSBCTRL). The internal regulator resets to +# disabled; anything using the USB peripheral needs VUSB powered. Select +# "External" when the board feeds 3.3 V into VUSB directly instead of +# using the internal regulator. +avrdu.menu.usbvreg.internal=Internal regulator from VDD +avrdu.menu.usbvreg.internal.build.usbvreg=-DUSB_VREG_INTERNAL +avrdu.menu.usbvreg.external=External 3.3V supplied on VUSB +avrdu.menu.usbvreg.external.build.usbvreg= #----------------------------------------# # Exported hex/lst/map names will # @@ -1763,7 +1792,7 @@ avrdu.menu.chip.avr64du32.build.mcu=avr64du32 avrdu.menu.chip.avr64du32.upload.maximum_size=65536 avrdu.menu.chip.avr64du32.upload.maximum_data_size=8192 avrdu.menu.chip.avr64du32.build.variant=32pin-duseries -avrdu.menu.chip.avr64du32.build.highestcb=B2 +avrdu.menu.chip.avr64du32.build.highestcb=B1 avrdu.menu.chip.avr64du28.build.mcu=avr64du28 avrdu.menu.chip.avr64du28.upload.maximum_size=65536 avrdu.menu.chip.avr64du28.upload.maximum_data_size=8192 @@ -4697,6 +4726,373 @@ avrddopti.menu.flmap.locksection0.build.flmapabr=.fl0 #avreb.bootloader.OSCCFG=0b0000{bootloader.oschffrq}000 # Can probably be binary identical with EA's +########################################################################## +# # +# ### # # #### #### # # # # ### #### # +# # # # # # # # # # # # # # # # # +# ##### # # #### ### # # # # # # ### #### # +# # # # # # # # # # # # # # # # # +# # # # # # #### ### ### ### #### # +#________________________________________________________________________# + +avrduusb.upload.tool=avrdude +avrduusb.upload.prog_interlock= +# USB identification for "Get Board Info" (BN). The running sketch enumerates +# as the application CDC (0x1209:0x0002); in bootloader mode it enumerates as +# usbcdcboot (0x1209:0x0001). Register both so the board is recognized either way. +avrduusb.vid.0=0x1209 +avrduusb.pid.0=0x0002 +avrduusb.vid.1=0x1209 +avrduusb.pid.1=0x0001 +avrduusb.build.board=avrdu +avrduusb.build.core=dxcore +avrduusb.upload.maximum_size=0 +avrduusb.upload.maximum_data_size=0 +avrduusb.build.f_cpu={build.speed}000000L +avrduusb.build.text_section_start=.text=0x1000 +avrduusb.build.defaultswaps= +avrduusb.build.extra_flags= {build.defaultswaps} {build.usbvreg} -DUSING_AVRDU_CDC_BOOTLOADER -DUSING_OPTIBOOT +avrduusb.build.export_merged_output=false +avrduusb.build.highestcb=B2 +avrduusb.build.printf= +avrduusb.build.flmapopts= +avrduusb.build.attachmode=-DCORE_ATTACH_ALL + +#----------------------------------------# +# Bootloading parameters and fuses # +#________________________________________# +avrduusb.bootloader.tool=avrdude +avrduusb.bootloader.WDTCFG=0b{bootloader.wdtwindowbits}{bootloader.wdttimeoutbits} +avrduusb.bootloader.wdtwindowbits=0000 +avrduusb.bootloader.wdttimeotbits=0000 +avrduusb.bootloader.BODCFG=0b{bootloader.bodlevbits}{bootloader.bodmodebits} +avrduusb.bootloader.updipinbit=1 +avrduusb.bootloader.SYSCFG0=0b110{bootloader.updipinbit}{bootloader.resetpinbits}00{bootloader.eesavebit} +avrduusb.bootloader.SYSCFG1=0b00001{bootloader.sutbits} +avrduusb.bootloader.CODESIZE=0x00 +avrduusb.bootloader.BOOTSIZE=0x08 +avrduusb.bootloader.OSCCFG=0b00000000 +avrduusb.bootloader.resetpinbits={bootloader.resetpinbit} +avrduusb.bootloader.avrdudefuse5="-Ufuse5:w:{bootloader.SYSCFG0}:m" +avrduusb.program.avrdudefuse5="-Ufuse5:w:{bootloader.SYSCFG0}:m" + +#----------------------------------------# +# Retain EEPROM (EESAVE) # +#________________________________________# +avrduusb.menu.eesave.enable=EEPROM retained +avrduusb.menu.eesave.enable.bootloader.eesavebit=1 +avrduusb.menu.eesave.disable=EEPROM not retained +avrduusb.menu.eesave.disable.bootloader.eesavebit=0 + +#----------------------------------------# +# VUSB Power Source (regulator) # +#________________________________________# +avrduusb.menu.usbvreg.internal=Internal regulator by VDD +avrduusb.menu.usbvreg.internal.build.usbvreg=-DUSB_VREG_INTERNAL +avrduusb.menu.usbvreg.internal.bootloader.vregsuffix= +avrduusb.menu.usbvreg.external=External 3.3V on VUSB +avrduusb.menu.usbvreg.external.build.usbvreg= +avrduusb.menu.usbvreg.external.bootloader.vregsuffix=_novreg +avrduusb.bootloader.avrdudestring=-Uflash:w:{bootloader.file}:i +avrduusb.bootloader.pymcuprogstring=-aerase + +#----------------------------------------# +# Exported hex/lst/map name postfixes # +#________________________________________# +avrduusb.build.mcupostfix= +avrduusb.build.printfabr= +avrduusb.build.flmapabr=.ful +avrduusb.build.attachabr= +avrduusb.build.wireabr= +avrduusb.build.appspmabr= + +#----------------------------------------# +# Clock speed and source options # +#________________________________________# +avrduusb.menu.clock.24internal=24 MHz internal +avrduusb.menu.clock.24internal.build.speed=24 +avrduusb.menu.clock.24internal.build.clocksource=0 +avrduusb.menu.clock.20internal=20 MHz internal +avrduusb.menu.clock.20internal.build.speed=20 +avrduusb.menu.clock.20internal.build.clocksource=0 +avrduusb.menu.clock.16internal=16 MHz internal +avrduusb.menu.clock.16internal.build.speed=16 +avrduusb.menu.clock.16internal.build.clocksource=0 +avrduusb.menu.clock.12internal=12 MHz internal +avrduusb.menu.clock.12internal.build.speed=12 +avrduusb.menu.clock.12internal.build.clocksource=0 +avrduusb.menu.clock.24crystal=24 MHz crystal +avrduusb.menu.clock.24crystal.build.speed=24 +avrduusb.menu.clock.24crystal.build.clocksource=1 +avrduusb.menu.clock.20crystal=20 MHz crystal +avrduusb.menu.clock.20crystal.build.speed=20 +avrduusb.menu.clock.20crystal.build.clocksource=1 +avrduusb.menu.clock.16crystal=16 MHz crystal +avrduusb.menu.clock.16crystal.build.speed=16 +avrduusb.menu.clock.16crystal.build.clocksource=1 +avrduusb.menu.clock.12crystal=12 MHz crystal +avrduusb.menu.clock.12crystal.build.speed=12 +avrduusb.menu.clock.12crystal.build.clocksource=1 +avrduusb.menu.clock.16extclock=16 MHz External Clock +avrduusb.menu.clock.16extclock.build.speed=16 +avrduusb.menu.clock.16extclock.build.clocksource=2 +avrduusb.menu.clock.20extclock=20 MHz External Clock +avrduusb.menu.clock.20extclock.build.speed=20 +avrduusb.menu.clock.20extclock.build.clocksource=2 +avrduusb.menu.clock.24extclock=24 MHz External Clock +avrduusb.menu.clock.24extclock.build.speed=24 +avrduusb.menu.clock.24extclock.build.clocksource=2 + +#----------------------------------------# +# BrownOut detection (BOD) options # +#________________________________________# +avrduusb.menu.bodvoltage.1v9=1.9V +avrduusb.menu.bodvoltage.1v9.bootloader.bodlevbits=000 +avrduusb.menu.bodvoltage.2v45=2.45V +avrduusb.menu.bodvoltage.2v45.bootloader.bodlevbits=001 +avrduusb.menu.bodvoltage.2v70=2.70V +avrduusb.menu.bodvoltage.2v70.bootloader.bodlevbits=010 +avrduusb.menu.bodvoltage.2v85=2.85V +avrduusb.menu.bodvoltage.2v85.bootloader.bodlevbits=011 +avrduusb.menu.bodmode.disabled=Disabled/Disabled +avrduusb.menu.bodmode.enabled=Enabled/Enabled +avrduusb.menu.bodmode.ensampfast=Enabled/Sampled (128 Hz) +avrduusb.menu.bodmode.ensampslow=Enabled/Sampled (32 Hz) +avrduusb.menu.bodmode.sampledfast=Sampled/Sampled (128 Hz) +avrduusb.menu.bodmode.sampledslow=Sampled/Sampled (32 Hz) +avrduusb.menu.bodmode.sampdisfast=Sampled/Disabled (128 Hz) +avrduusb.menu.bodmode.sampdisslow=Sampled/Disabled (32 Hz) +avrduusb.menu.bodmode.endisholdwake=Enabled hold wakeup/Disabled +avrduusb.menu.bodmode.disabled.bootloader.BODCFG=0x00 +avrduusb.menu.bodmode.enabled.bootloader.bodmodebits=00101 +avrduusb.menu.bodmode.ensampfast.bootloader.bodmodebits=00110 +avrduusb.menu.bodmode.ensampslow.bootloader.bodmodebits=10110 +avrduusb.menu.bodmode.samplefast.bootloader.bodmodebits=01010 +avrduusb.menu.bodmode.sampleslow.bootloader.bodmodebits=11010 +avrduusb.menu.bodmode.sampdisfast.bootloader.bodmodebits=01000 +avrduusb.menu.bodmode.sampdisslow.bootloader.bodmodebits=11000 +avrduusb.menu.bodmode.endisholdwake.bootloader.bodmodebits=01100 + +#----------------------------------------# +# Reset pin as input option # +#________________________________________# +avrduusb.menu.resetpin.reset=Hardware Reset (recommended) +avrduusb.menu.resetpin.reset.bootloader.resetpinbit=1 +avrduusb.menu.resetpin.input=Input (no output, ever) +avrduusb.menu.resetpin.input.bootloader.resetpinbit=0 + +#----------------------------------------# +# millis/micros timing source # +#________________________________________# +avrduusb.menu.millis.tcbhighest=Default (recommended) +avrduusb.menu.millis.disabled=Disabled (delay() only) +avrduusb.menu.millis.tcb0=TCB0 +avrduusb.menu.millis.tcb1=TCB1 (default) +avrduusb.menu.millis.tca0=TCA0 +avrduusb.menu.millis.tcbhighest.build.millistimer=B1 +avrduusb.menu.millis.tcb0.build.millistimer=B0 +avrduusb.menu.millis.tcb1.build.millistimer=B1 +avrduusb.menu.millis.tca0.build.millistimer=A0 +avrduusb.menu.millis.disabled.build.millistimer=NONE + +#----------------------------------------# +# printf() version options # +#________________________________________# +avrduusb.menu.printf.default=Default, 1.5k flash, no floats +avrduusb.menu.printf.full=Full, 2.6k, prints floats +avrduusb.menu.printf.full.build.printf=-Wl,-u,vfprintf -lprintf_flt +avrduusb.menu.printf.full.build.printfabr=.pfF +avrduusb.menu.printf.minimal=Minimal, 1.1k flash used +avrduusb.menu.printf.minimal.build.printf=-Wl,-u,vfprintf -lprintf_min +avrduusb.menu.printf.minimal.build.printfabr=.pfM + +#----------------------------------------# +# attachInterrupt Mode # +#________________________________________# +avrduusb.menu.attach.allenabled=On all pins, with new implementation. +avrduusb.menu.attach.allenabled.build.attachmode=-DCORE_ATTACH_ALL +avrduusb.menu.attach.manual=Only enabled ports. Please see documentation. +avrduusb.menu.attach.manual.build.attachmode=-DCORE_ATTACH_NONE +avrduusb.menu.attach.oldversion=Old version, may fix bugs. +avrduusb.menu.attach.oldversion.build.attachmode=-DCORE_ATTACH_OLD +avrduusb.menu.attach.manual.build.attachabr=.aMan +avrduusb.menu.attach.oldversion.build.attachabr=.aOld + +#----------------------------------------# +# Startup time options # +#________________________________________# +avrduusb.menu.startuptime.8=8ms +avrduusb.menu.startuptime.0=0ms +avrduusb.menu.startuptime.1=1ms +avrduusb.menu.startuptime.2=2ms +avrduusb.menu.startuptime.4=4ms +avrduusb.menu.startuptime.16=16ms +avrduusb.menu.startuptime.32=32ms +avrduusb.menu.startuptime.64=64ms +avrduusb.menu.startuptime.0.bootloader.sutbits=000 +avrduusb.menu.startuptime.1.bootloader.sutbits=001 +avrduusb.menu.startuptime.2.bootloader.sutbits=010 +avrduusb.menu.startuptime.4.bootloader.sutbits=011 +avrduusb.menu.startuptime.8.bootloader.sutbits=100 +avrduusb.menu.startuptime.16.bootloader.sutbits=101 +avrduusb.menu.startuptime.32.bootloader.sutbits=110 +avrduusb.menu.startuptime.64.bootloader.sutbits=111 + +#----------------------------------------# +# Wire Mode Selection # +#________________________________________# +avrduusb.menu.wiremode.mors= Master or Slave +avrduusb.menu.wiremode.mors.build.wiremode=MORS_SINGLE +avrduusb.menu.wiremode.mors.build.wireabr=.wO +avrduusb.menu.wiremode.mands=Master and Slave +avrduusb.menu.wiremode.mands.build.wiremode=MANDS_SINGLE +avrduusb.menu.wiremode.mands.build.wireabr=.wA + +#----------------------------------------# +# WDT MODE # +#________________________________________# +avrduusb.menu.WDTtimeout.disabled=Disabled (recommended) +avrduusb.menu.WDTtimeout.8seconds=Window remains open for 8 Seconds +avrduusb.menu.WDTtimeout.4seconds=Window remains open for 4 Seconds +avrduusb.menu.WDTtimeout.2seconds=Window remains open for 2 Seconds +avrduusb.menu.WDTtimeout.1second=Window remains open for 1 Second +avrduusb.menu.WDTtimeout.500msec=Window remains open for 500 milliseconds +avrduusb.menu.WDTtimeout.250msec=Window remains open for 250 milliseconds +avrduusb.menu.WDTtimeout.125msec=Window remains open for 125 milliseconds +avrduusb.menu.WDTtimeout.63msec=Window remains open for 62.5 milliseconds +avrduusb.menu.WDTwindow.disabled=No delay before window "opens" +avrduusb.menu.WDTwindow.8seconds=8 Second before window "opens" +avrduusb.menu.WDTwindow.4seconds=4 Second before window "opens" +avrduusb.menu.WDTwindow.2seconds=2 Second before window "opens" +avrduusb.menu.WDTwindow.1second=1 Second before window "opens" +avrduusb.menu.WDTwindow.500msec=500 millisecond before window "opens" +avrduusb.menu.WDTwindow.250msec=250 millisecond before window "opens" +avrduusb.menu.WDTwindow.125msec=125 millisecond before window "opens" +avrduusb.menu.WDTwindow.63msec=62.5 millisecond before window "opens" +avrduusb.menu.WDTtimeout.disabled.bootloader.wdttimeoutbits=0000 +avrduusb.menu.WDTtimeout.8seconds.bootloader.wdttimeoutbits=1011 +avrduusb.menu.WDTtimeout.4seconds.bootloader.wdttimeoutbits=1010 +avrduusb.menu.WDTtimeout.2seconds.bootloader.wdttimeoutbits=1001 +avrduusb.menu.WDTtimeout.1second.bootloader.wdttimeoutbits=1000 +avrduusb.menu.WDTtimeout.500msec.bootloader.wdttimeoutbits=0111 +avrduusb.menu.WDTtimeout.250msec.bootloader.wdttimeoutbits=0110 +avrduusb.menu.WDTtimeout.125msec.bootloader.wdttimeoutbits=0101 +avrduusb.menu.WDTtimeout.63msec.bootloader.wdttimeoutbits=0100 +avrduusb.menu.WDTwindow.disabled.bootloader.wdtwindowbits=0000 +avrduusb.menu.WDTwindow.8seconds.bootloader.wdtwindowbits=1011 +avrduusb.menu.WDTwindow.4seconds.bootloader.wdtwindowbits=1010 +avrduusb.menu.WDTwindow.2seconds.bootloader.wdtwindowbits=1001 +avrduusb.menu.WDTwindow.1second.bootloader.wdtwindowbits=1000 +avrduusb.menu.WDTwindow.500msec.bootloader.wdtwindowbits=0111 +avrduusb.menu.WDTwindow.250msec.bootloader.wdtwindowbits=0110 +avrduusb.menu.WDTwindow.125msec.bootloader.wdtwindowbits=0101 +avrduusb.menu.WDTwindow.63msec.bootloader.wdtwindowbits=0100 + +# FLMAP menu. Ignored on <=32K DU parts (fully mapped; FLMAP has no effect there). +# On 64K parts lockdefault is listed first => it is the default selection. +# Section addressing (see Ref_PROGMEM.md): .FLMAP_SECTION1 (odd, the default) is linked +# at 0x8000, so PROGMEM_MAPPED there is read directly like RAM. .FLMAP_SECTION0 (even) +# is linked low, after the code, so reading its data through the map window needs a +# manual +0x8000 offset on the address. locksection0 is therefore an advanced option, +# kept for parity with avrda/avrdb/avrdd; it is harmless to normal sketches that do not +# place data in mapped section 0. +#----------------------------------------# +# Flash Mapping options # +#________________________________________# +avrduusb.menu.flmap.lockdefault=Use last FLMAP section, lock FLMAP, provide PROGMEM_MAPPED +avrduusb.menu.flmap.unlocked=Start w/default, do not lock flmap. No PROGMEM_MAPPED. +avrduusb.menu.flmap.locksection0=Set FLMAP to section 0, lock FLMAP, provide PROGMEM_MAPPED +avrduusb.menu.flmap.lockdefault.build.flmapopts=-DLOCK_FLMAP -DFLMAPSECTION1 +avrduusb.menu.flmap.unlocked.build.flmapopts= +avrduusb.menu.flmap.locksection0.build.flmapopts=-DLOCK_FLMAP -DFLMAPSECTION0 +avrduusb.menu.flmap.lockdefault.build.flmapabr=.fld +avrduusb.menu.flmap.unlocked.build.flmapabr=.ful +avrduusb.menu.flmap.locksection0.build.flmapabr=.fl0 +avrduusb.upload.protocol=arduino +avrduusb.upload.speed=115200 +avrduusb.upload.extra_params=-D +avrduusb.upload.use_1200bps_touch=true +avrduusb.upload.wait_for_upload_port=true + +# --- Chip selection: each chip maps to its USB-CDC bootloader class. +# LED is PA7 on 20/28/32-pin (usbcdcboot_*du) and PD6 on 14-pin +# (usbcdcboot_*du14). 16du and 32du are distinct binaries (RAMSTART +# differs by flash size). max_size = flash - 4096 (4 KB boot section). +#----------------------------------------# +# Chip (memory and pincount) # +#________________________________________# +avrduusb.menu.chip.avr64du32=AVR64DU32 +avrduusb.menu.chip.avr64du28=AVR64DU28 +avrduusb.menu.chip.avr32du32=AVR32DU32 +avrduusb.menu.chip.avr32du28=AVR32DU28 +avrduusb.menu.chip.avr32du20=AVR32DU20 +avrduusb.menu.chip.avr32du14=AVR32DU14 +avrduusb.menu.chip.avr16du32=AVR16DU32 +avrduusb.menu.chip.avr16du28=AVR16DU28 +avrduusb.menu.chip.avr16du20=AVR16DU20 +avrduusb.menu.chip.avr16du14=AVR16DU14 +avrduusb.menu.chip.avr64du32.build.mcu=avr64du32 +avrduusb.menu.chip.avr64du32.bootloader.class=usbcdcboot_64du +avrduusb.menu.chip.avr64du32.upload.maximum_size=61440 +avrduusb.menu.chip.avr64du32.upload.maximum_data_size=8192 +avrduusb.menu.chip.avr64du32.build.highestcb=B2 +avrduusb.menu.chip.avr64du32.build.variant=32pin-duseries +avrduusb.menu.chip.avr64du28.build.mcu=avr64du28 +avrduusb.menu.chip.avr64du28.bootloader.class=usbcdcboot_64du +avrduusb.menu.chip.avr64du28.upload.maximum_size=61440 +avrduusb.menu.chip.avr64du28.upload.maximum_data_size=8192 +avrduusb.menu.chip.avr64du28.build.variant=28pin-duseries +avrduusb.menu.chip.avr32du32.build.mcu=avr32du32 +avrduusb.menu.chip.avr32du32.bootloader.class=usbcdcboot_32du +avrduusb.menu.chip.avr32du32.upload.maximum_size=28672 +avrduusb.menu.chip.avr32du32.upload.maximum_data_size=4096 +avrduusb.menu.chip.avr32du32.build.variant=32pin-duseries +avrduusb.menu.chip.avr32du28.build.mcu=avr32du28 +avrduusb.menu.chip.avr32du28.bootloader.class=usbcdcboot_32du +avrduusb.menu.chip.avr32du28.upload.maximum_size=28672 +avrduusb.menu.chip.avr32du28.upload.maximum_data_size=4096 +avrduusb.menu.chip.avr32du28.build.variant=28pin-duseries +avrduusb.menu.chip.avr32du20.build.mcu=avr32du20 +avrduusb.menu.chip.avr32du20.bootloader.class=usbcdcboot_32du +avrduusb.menu.chip.avr32du20.upload.maximum_size=28672 +avrduusb.menu.chip.avr32du20.upload.maximum_data_size=4096 +avrduusb.menu.chip.avr32du20.build.highestcb=B1 +avrduusb.menu.chip.avr32du20.build.variant=20pin-duseries +avrduusb.menu.chip.avr32du20.build.defaultswaps=-DMUX_DEFAULT_USART1=2 +avrduusb.menu.chip.avr32du14.build.mcu=avr32du14 +avrduusb.menu.chip.avr32du14.bootloader.class=usbcdcboot_32du14 +avrduusb.menu.chip.avr32du14.upload.maximum_size=28672 +avrduusb.menu.chip.avr32du14.upload.maximum_data_size=4096 +avrduusb.menu.chip.avr32du14.build.highestcb=B1 +avrduusb.menu.chip.avr32du14.build.variant=14pin-duseries +avrduusb.menu.chip.avr32du14.build.defaultswaps=-DMUX_DEFAULT_USART1=2 +avrduusb.menu.chip.avr16du32.build.mcu=avr16du32 +avrduusb.menu.chip.avr16du32.bootloader.class=usbcdcboot_16du +avrduusb.menu.chip.avr16du32.upload.maximum_size=12288 +avrduusb.menu.chip.avr16du32.upload.maximum_data_size=2048 +avrduusb.menu.chip.avr16du32.build.variant=32pin-duseries +avrduusb.menu.chip.avr16du28.build.mcu=avr16du28 +avrduusb.menu.chip.avr16du28.bootloader.class=usbcdcboot_16du +avrduusb.menu.chip.avr16du28.upload.maximum_size=12288 +avrduusb.menu.chip.avr16du28.upload.maximum_data_size=2048 +avrduusb.menu.chip.avr16du28.build.variant=28pin-duseries +avrduusb.menu.chip.avr16du20.build.mcu=avr16du20 +avrduusb.menu.chip.avr16du20.bootloader.class=usbcdcboot_16du +avrduusb.menu.chip.avr16du20.upload.maximum_size=12288 +avrduusb.menu.chip.avr16du20.upload.maximum_data_size=2048 +avrduusb.menu.chip.avr16du20.build.highestcb=B1 +avrduusb.menu.chip.avr16du20.build.variant=20pin-duseries +avrduusb.menu.chip.avr16du20.build.defaultswaps=-DMUX_DEFAULT_USART1=2 +avrduusb.menu.chip.avr16du14.build.mcu=avr16du14 +avrduusb.menu.chip.avr16du14.bootloader.class=usbcdcboot_16du14 +avrduusb.menu.chip.avr16du14.upload.maximum_size=12288 +avrduusb.menu.chip.avr16du14.upload.maximum_data_size=2048 +avrduusb.menu.chip.avr16du14.build.highestcb=B1 +avrduusb.menu.chip.avr16du14.build.variant=14pin-duseries +avrduusb.menu.chip.avr16du14.build.defaultswaps=-DMUX_DEFAULT_USART1=2 +avrduusb.bootloader.file={runtime.platform.path}/bootloaders/hex/{bootloader.class}{bootloader.vregsuffix}.hex +avrduusb.bootloader.appspm= + ######################################################################################################## # # # ### #### #### #### // #### #### # diff --git a/megaavr/bootloaders/hex/usbcdcboot_16du.hex b/megaavr/bootloaders/hex/usbcdcboot_16du.hex new file mode 100644 index 00000000..7e864cbd --- /dev/null +++ b/megaavr/bootloaders/hex/usbcdcboot_16du.hex @@ -0,0 +1,255 @@ +:1000000043C000005CC000005AC0000058C000009F +:1000100056C0000054C0000052C0000050C0000094 +:100020004EC000004CC000004AC0000048C00000A4 +:1000300046C0000044C0000042C0000040C00000B4 +:100040003EC000003CC000003AC0000038C00000C4 +:1000500036C0000034C0000032C0000030C00000D4 +:100060002EC000002CC000002AC0000028C00000E4 +:1000700026C0000024C0000022C0000020C00000F4 +:100080001EC000001CC0000011241FBECFEFCDBF5A +:10009000DFE7DEBF18E7A0E0B8E7EEE9FFE002C067 +:1000A00005900D92A830B107D9F72CE7A8E0B8E782 +:1000B00001C01D92A433B207E1F72BD0F1C6A0CF47 +:1000C000CF93DF9300D0CDB7DEB78AE190E0019700 +:1000D00029F40F900F90DF91CF91089519821A8221 +:1000E00029813A81283E334090F7000029813A81E6 +:1000F0002F5F3F4F29833A83F3CF80E88093050435 +:1001000080930204ABD288ED91E084BF909300010C +:10011000FFCF209140002CBB8FEF80934000809157 +:100120001001806490E038ED34BF8093100124FF0B +:1001300034C01092347C1092357CC0E4DFE18091B1 +:100140006800837C846298ED94BF8093680080E0AF +:1001500094BF809361008091650081FFFCCF4AD2FB +:10016000C9D295D3C0930878D093097880E880935A +:100170000104809305046CD210E8D0E0C0E013D1F4 +:10018000BAD2A9D321968091087890910978C8179E +:10019000D907A8F310930704F0CF22FF25C0809160 +:1001A000347C9091357C8D399B4639F41092347CA7 +:1001B0001092357CC0E8DEE3C2CF8DE99BE68093E8 +:1001C000347C9093357C80E880930104DAE0C0E8C9 +:1001D000C093060475DFC093050472DFD150C1F7E8 +:1001E000C0930504C09302041092347C1092357CB5 +:1001F000E0E0F0E185919491C0E0DDE7019609F43B +:100200009ECFF89480E88093050480930204809147 +:10021000100128ED8F7B90E024BF80931001109295 +:10022000000C1092010C80910110806324BF809318 +:100230000110E0E0F8E0099480910B0C82FDFCCF06 +:100240000895EAEEF8E781A1846081A381A5846026 +:1002500081A710921078109211780895CF939B0186 +:100260004617570708F49A01FC01A2EAB8E72134BF +:10027000310580F080931679909317792093147943 +:1002800030931579109218791092197983E717C075 +:1002900081918D93CD01825A984782179307C0F3BD +:1002A00082EA98E7809316799093177920931479CE +:1002B00030931579109218791092197983E480930C +:1002C0001379BADFCAE6C093420CB6DFC093400C84 +:1002D00081E090E08093107890931178CF91089509 +:1002E000EAEEF8E712A613A616A617A683E481A7DE +:1002F000A3DF8AE68093420C83E090E0809310783D +:10030000909311780895EAEEF8E782E291E0DF0138 +:100310009C011D9221503040E1F783E481A322EE3D +:1003200038E724A335A310A281A782EA98E784A71F +:1003300095A782E080A791E891AF22E938E724AF42 +:1003400035AF80AF93E890932B7922E538E720937F +:100350002E7930932F7910922A799093437922E164 +:1003600038E720934679309347798093427908950E +:10037000B7CF74CFEAEEF8E784A795A712A613A625 +:1003800066A777A783E681A757DF8AE68093400CAC +:1003900082E090E0809310789093117881E08093D0 +:1003A0000D7808954ECFCF93DF9380910A0CC091C2 +:1003B0000B0C84FF11C01092030CA5DF1092107873 +:1003C0001092117810920E7810920F7810920D788A +:1003D0006FD180E180930A0CC0FF17C083E4809343 +:1003E0000B798093137928DFD0E1D093400C24DF80 +:1003F000D093420C8091E278807609F1803209F442 +:10040000A9C01FDF81E080930B0CC5FFE4C0809181 +:10041000127985FFA9C010DF80E28093420C8091A1 +:100420001078909111788130910509F497C0059763 +:1004300009F4D4C0109210781092117895C0E09110 +:10044000E378EC30F0F6F0E0E95DFD4F1CC5980272 +:10045000A5020102A5020102740233020102810217 +:100460008E02A702A5028091E4789091E578292F69 +:1004700033272230310561F02330310599F02130E6 +:10048000310509F0BECF62E170E08EE39FE804C061 +:1004900063E470E08BEF9EE84091E8785091E97852 +:1004A000DDDEB0CF99278130910569F0C0F0823050 +:1004B000910579F0039709F0A4CF6091A48E70E0C4 +:1004C00085EA9EE8E9CF6091E68E70E087EE9EE8CF +:1004D000E3CF6091BB8E70E08CEB9EE8DDCF64E0F3 +:1004E00070E087EF9EE8D8CF8091E4788F77809393 +:1004F0000F78F6DE85E090E0809310789093117885 +:1005000081CF80910E7880930A784091E87850915D +:10051000E97861E070E08AE098E7C2CF8091E47802 +:1005200080930E78DDDE88DE82E08093480C6ACF0F +:1005300010920B7810920C784091E8785091E978FD +:1005400062E070E08BE098E7ABCFCADE5BCF109241 +:100550000A78DBCF82EE98E787D054CF84E090E032 +:10056000809310789093117880910A7985FF12C05A +:1005700063DE80E28093400C809110789091117836 +:10058000029799F580910D78882319F010920D78D3 +:1005900089D0A6DE8091227985FF04C04DDE80E2FD +:1005A0008093460C80912A7985FF09C045DE80E260 +:1005B0008093480C80912C7990912D798AD08091EC +:1005C000427985FF05C038DE80E280934E0C8FD0E3 +:1005D00080E280930B0CDF91CF91089580910F788A +:1005E0008093030C10920F7825CF10921078109200 +:1005F0001178D0CF809168008C7B826498ED94BF95 +:100600008093680081E08093060F8FE697E1019761 +:10061000F1F700C0000077DE8AE099E78093060CCE +:100620009093070C1092080C1092090C83E88093A9 +:10063000000C80E492E4AFE0B0E02091850020FD62 +:1006400004C00197A109B109C1F71092010C0895E6 +:10065000E0E0FCE08181816081830895E0E0FCE0DE +:1006600081818E7F81830895FC019181913299F07F +:1006700018F4903221F096CE9432E8F779CE868144 +:1006800097810797C1F781E08093137A67E070E064 +:1006900080E098E76FCE4681578167E070E080E0A8 +:1006A00098E767CE8091137A81111092137A08959A +:1006B0001092117A1092127A10920F7A1092107A88 +:1006C00010920E7A10920C7A10920D7A1092137A80 +:1006D00008958134910510F080E490E08093117AC0 +:1006E0009093127A10920F7A1092107A08951092C5 +:1006F0000E7A0895DDCF8091117A9091127A892B2C +:10070000F1F020910F7A3091107A8091117A9091C6 +:10071000127A2817390798F01092117A1092127AEB +:1007200010920F7A1092107A10922C7910922D79E3 +:1007300080910B0C82FDFCCF82E08093480C80916D +:100740000E7A811120C080910C7A90910D7A892BBC +:10075000D1F08091427981FF16C080910C7A9091FE +:100760000D7A809344799093457980910B0C82FDAA +:10077000FCCF82E080934E0C81E080930E7A109241 +:100780000C7A10920D7A089520910F7A3091107A98 +:100790008091117A9091127A2817390788F4809104 +:1007A0000F7A9091107A9C012F5F3F4F20930F7A20 +:1007B0003093107A8E5A9748FC01808190E008951A +:1007C0008FEF9FEF0895CF93C82F80910C7A90916F +:1007D0000D7A8034910598F480910C7A90910D7A7D +:1007E0009C012F5F3F4F20930C7A30930D7A8E5EE1 +:1007F0009748FC01C083CF9108957DDFD4DD8091BF +:100800000E7A8111FACF80910C7A90910D7A803412 +:10081000910598F7E1CF80910C7A90910D7A892B10 +:1008200089F480910E7A81110DC0089564DFBBDDDB +:1008300080910E7A811106C080910C7A90910D7A88 +:10084000892B01F180910E7A8111F0CF80910C7A81 +:1008500090910D7A892B51F380910C7A90910D7AB9 +:10086000809344799093457980910B0C82FDFCCF65 +:1008700082E080934E0C81E080930E7A10920C7A85 +:1008800010920D7A089584E19EDF80E19CCF109252 +:10089000327C1092337C10922F7C1092307C10921C +:1008A0002E7C1092197C1092177C1092187C10925A +:1008B000167C1092147A1092157AEAE1FCE784E132 +:1008C000DF011D928A95E9F740E052E06FEF70E09A +:1008D00086E19AE7DEC23F924F925F926F927F92DB +:1008E0008F929F92AF92BF92CF92DF92EF92FF9240 +:1008F0000F931F93CF93DF9300D000D0CDB7DEB717 +:1009000083E0382E92E0E92EF12C20EF922E34E095 +:10091000C32ED12C41EF842EAA24A394B12C34DF12 +:1009200097FF15C02496CDBFDEBFDF91CF911F91F9 +:100930000F91FF90EF90DF90CF90BF90AF909F907E +:100940008F907F906F905F904F903F900895482FC9 +:100950002091327C3091337C2230310509F477C00C +:10096000C8F42115310509F445C021303105B9F627 +:10097000E091197C91E09E0F9093197CF0E0E65E87 +:10098000F348808380912E7C815080932E7C882335 +:10099000E9F1C5CF2430310509F465C108F432C14D +:1009A0002530310509F0BBCFE091197C91E09E0F15 +:1009B0009093197CF0E0E65EF348808380912E7C72 +:1009C000815080932E7C8111AACF80911A7C9091C6 +:1009D0001B7C9827892798278093177C9093187CF5 +:1009E00020911C7C2093167C8130924078F4809278 +:1009F000317C0CC08093317CE0EDE80FE63430F4BC +:100A0000F0E0E05BF04780818E3F29F4E092327C99 +:100A1000F092337C84CF8F3F79F41092197C30921E +:100A20002E7C83E090E0443611F085E090E08093E6 +:100A3000327C9093337C73CF882341F31092197CDE +:100A400080932E7CA092327CB092337C68CF80322F +:100A500009F069C08091317C803F31F58091167C2E +:100A60008F7D863499F42091177C3091187C6091A9 +:100A70002F7C7091307C90E080E046E15AE716D1FF +:100A800002DF1092327C1092337C49CF8534C1F75B +:100A90004091177C5091187C66E17AE780912F7C19 +:100AA0009091307CA8D1ECCF813F81F584E18BDE41 +:100AB00040902F7C5090307C712C612C10E000E035 +:100AC0008091177C9091187C0817190718F080E125 +:100AD0007ADED7CF8091167C8F7D863459F4C3019E +:100AE000B201600F711F811D911D68D16CDE0F5F17 +:100AF0001F4FE6CF853441F480912F7C9091307C5C +:100B0000800F911FB6D1F2CF8FEFF0CF853409F46B +:100B1000B7CF90F4803409F4B3CF38F4803309F4BC +:100B2000AFCF8133A9F185E1D3CF813409F43EC041 +:100B3000823409F4A5CFF7CF823509F4A1CFC0F4F0 +:100B4000803509F49DCF813571F784E13CDE80E189 +:100B50003ADE61DE19821A821B821C8289819A81A7 +:100B6000AB81BC8180349D40A340B10580F1C5DAE2 +:100B70008635C9F1853709F43AC08535A1F68091EB +:100B80001A7C90911B7C80932F7C9093307C78CF43 +:100B900084E119DE06E91FE8FFE80D391F0709F4B3 +:100BA00096CFF80181918F010EDEF6CF80911A7CED +:100BB00011E0803839F018E0813821F012E08238F5 +:100BC00009F013E084E1FFDD812FFDDD80CF898115 +:100BD0009A81AB81BC810196A11DB11D89839A8345 +:100BE000AB83BC83BBCF84E1EEDD80E0EECF84E15C +:100BF000EADD80918010E7DD80918110E4DD809155 +:100C00008210E3CFE091197C91E09E0F9093197CC4 +:100C1000F0E0E65EF348808380912E7C81508093E3 +:100C20002E7C81117CCE80911A7C90911B7C982720 +:100C3000892798278093177C9093187C20911C7C9F +:100C40002093167C8130E2E09E0708F0DFCE109200 +:100C5000147A1092157A892B09F4D8CEC092327C7E +:100C6000D092337C5CCE2091147A3091157AA90110 +:100C70004F5F5F4F4093147A5093157A2A5E354840 +:100C8000F90180838091177C9091187C481759074F +:100C900008F445CE9092317CB9CE9091061091FD2A +:100CA000FCCF9DE994BF809300100895CF92DF920E +:100CB000EF92FF920F931F93CF93DF936115E0E1C3 +:100CC0007E078105910508F470C02115310509F4EE +:100CD0006CC089012130324010F000E012E0209118 +:100CE0000110E1E0F0E0611530E87307810591053E +:100CF00010F4F0E0E0E07A016F0184E0CC0CDD1C40 +:100D00008A95E1F72F7C2C2938ED34BF2093011010 +:100D1000EB01D06888E0C1DF8FEF888380910610F7 +:100D200081FDFCCF81E0B9DF809101108F7C8C299F +:100D300098ED94BF8093011082E0AFDFF70181E06E +:100D400090E0AE014E195F09DA01AE0FBF1F8017A8 +:100D50009107E8F0A8014E7FC40FD51F4017510737 +:100D600060F4E40EF51EF701808190E09F6F8883A8 +:100D700099838091061081FDFCCF81E0DF91CF91B6 +:100D80001F910F91FF90EF90DF90CF9086CF218140 +:100D900030812327322723272D933C93209106105F +:100DA00021FDFCCF02963296CFCFDF91CF911F91DC +:100DB0000F91FF90EF90DF90CF9008954091011038 +:100DC00021E030E0611550E875078105910510F4C8 +:100DD00030E020E084E0220F331F8A95E1F7342FC2 +:100DE0003F7C322B28ED24BF30930110AB015068BB +:100DF000FA01808108950F931F938F3F910509F0A9 +:100E000058F58A0130E020E0080F191F211D311D1F +:100E10000130114021053105E0F040E051E0481B70 +:100E2000590B209106102370E1F72DE924BF23E12F +:100E300020930010460F571FFB01861B970B9C5EEB +:100E4000E417F50761F48DE984BF81E08093001019 +:100E500003C04115510529F71F910F910895DC0139 +:100E6000AE0FBF1F21912C932091061020FDFCCFC7 +:100E7000E7CF8F3F910509F020F49C5EFC01808153 +:100E800008958FEF0895EE0FFF1F0590F491E02D68 +:100E90000994DC0101C06D9341505040E0F7089582 +:040EA000F894FFCFF4 +:100EA40016160342004C0030003000300030003091 +:100EB400003000300031002A2A034100560052005D +:100EC4004400550020004300440043002000420039 +:100ED4006F006F0074006C006F00610064006500B7 +:100EE40072001010034100560052004400550042A5 +:100EF400004C00040309040902430002010080328B +:100F04000904000001020201000524001001052467 +:100F140001000104240202052406000107058103DF +:100F240010004009040100020A0000000705020243 +:100F34004000000705830240000012010002020085 +:100F44000040091201000001010203010000FEFE3D +:100F5400FEFEFEFEFEFEFEFEFEFEFEFE020114FE90 +:100F6400FE05FEFEFEFEFEFEFEFEFEFE000000FE90 +:100F7400FE0204FEFEFEFEFEFEFEFEFEFEFEFEFE83 +:100F8400FFFEFEFEFEFEFEFEFEFEFEFEFEFEFEFE7C +:0A0F9400FF004156522053544B0059 +:080F9E0000C201000000080080 +:040FFA00F8950895C9 +:020FFE00011AD6 +:00000001FF diff --git a/megaavr/bootloaders/hex/usbcdcboot_16du14.hex b/megaavr/bootloaders/hex/usbcdcboot_16du14.hex new file mode 100644 index 00000000..1c948b55 --- /dev/null +++ b/megaavr/bootloaders/hex/usbcdcboot_16du14.hex @@ -0,0 +1,255 @@ +:1000000043C000005CC000005AC0000058C000009F +:1000100056C0000054C0000052C0000050C0000094 +:100020004EC000004CC000004AC0000048C00000A4 +:1000300046C0000044C0000042C0000040C00000B4 +:100040003EC000003CC000003AC0000038C00000C4 +:1000500036C0000034C0000032C0000030C00000D4 +:100060002EC000002CC000002AC0000028C00000E4 +:1000700026C0000024C0000022C0000020C00000F4 +:100080001EC000001CC0000011241FBECFEFCDBF5A +:10009000DFE7DEBF18E7A0E0B8E7EEE9FFE002C067 +:1000A00005900D92A830B107D9F72CE7A8E0B8E782 +:1000B00001C01D92A433B207E1F72BD0F1C6A0CF47 +:1000C000CF93DF9300D0CDB7DEB78AE190E0019700 +:1000D00029F40F900F90DF91CF91089519821A8221 +:1000E00029813A81283E334090F7000029813A81E6 +:1000F0002F5F3F4F29833A83F3CF80E480936504D9 +:1001000080936204ABD288ED91E084BF90930001AC +:10011000FFCF209140002CBB8FEF80934000809157 +:100120001001806490E038ED34BF8093100124FF0B +:1001300034C01092347C1092357CC0E4DFE18091B1 +:100140006800837C846298ED94BF8093680080E0AF +:1001500094BF809361008091650081FFFCCF4AD2FB +:10016000C9D295D3C0930878D093097880E480935E +:100170006104809365046CD210E4D0E0C0E013D138 +:10018000BAD2A9D321968091087890910978C8179E +:10019000D907A8F310936704F0CF22FF25C0809100 +:1001A000347C9091357C8D399B4639F41092347CA7 +:1001B0001092357CC0E8DEE3C2CF8DE99BE68093E8 +:1001C000347C9093357C80E480936104DAE0C0E471 +:1001D000C093660475DFC093650472DFD150C1F728 +:1001E000C0936504C09362041092347C1092357CF5 +:1001F000E0E0F0E185919491C0E0DDE7019609F43B +:100200009ECFF89480E4809365048093620480918B +:10021000100128ED8F7B90E024BF80931001109295 +:10022000000C1092010C80910110806324BF809318 +:100230000110E0E0F8E0099480910B0C82FDFCCF06 +:100240000895EAEEF8E781A1846081A381A5846026 +:1002500081A710921078109211780895CF939B0186 +:100260004617570708F49A01FC01A2EAB8E72134BF +:10027000310580F080931679909317792093147943 +:1002800030931579109218791092197983E717C075 +:1002900081918D93CD01825A984782179307C0F3BD +:1002A00082EA98E7809316799093177920931479CE +:1002B00030931579109218791092197983E480930C +:1002C0001379BADFCAE6C093420CB6DFC093400C84 +:1002D00081E090E08093107890931178CF91089509 +:1002E000EAEEF8E712A613A616A617A683E481A7DE +:1002F000A3DF8AE68093420C83E090E0809310783D +:10030000909311780895EAEEF8E782E291E0DF0138 +:100310009C011D9221503040E1F783E481A322EE3D +:1003200038E724A335A310A281A782EA98E784A71F +:1003300095A782E080A791E891AF22E938E724AF42 +:1003400035AF80AF93E890932B7922E538E720937F +:100350002E7930932F7910922A799093437922E164 +:1003600038E720934679309347798093427908950E +:10037000B7CF74CFEAEEF8E784A795A712A613A625 +:1003800066A777A783E681A757DF8AE68093400CAC +:1003900082E090E0809310789093117881E08093D0 +:1003A0000D7808954ECFCF93DF9380910A0CC091C2 +:1003B0000B0C84FF11C01092030CA5DF1092107873 +:1003C0001092117810920E7810920F7810920D788A +:1003D0006FD180E180930A0CC0FF17C083E4809343 +:1003E0000B798093137928DFD0E1D093400C24DF80 +:1003F000D093420C8091E278807609F1803209F442 +:10040000A9C01FDF81E080930B0CC5FFE4C0809181 +:10041000127985FFA9C010DF80E28093420C8091A1 +:100420001078909111788130910509F497C0059763 +:1004300009F4D4C0109210781092117895C0E09110 +:10044000E378EC30F0F6F0E0E95DFD4F1CC5980272 +:10045000A5020102A5020102740233020102810217 +:100460008E02A702A5028091E4789091E578292F69 +:1004700033272230310561F02330310599F02130E6 +:10048000310509F0BECF62E170E08EE39FE804C061 +:1004900063E470E08BEF9EE84091E8785091E97852 +:1004A000DDDEB0CF99278130910569F0C0F0823050 +:1004B000910579F0039709F0A4CF6091A48E70E0C4 +:1004C00085EA9EE8E9CF6091E68E70E087EE9EE8CF +:1004D000E3CF6091BB8E70E08CEB9EE8DDCF64E0F3 +:1004E00070E087EF9EE8D8CF8091E4788F77809393 +:1004F0000F78F6DE85E090E0809310789093117885 +:1005000081CF80910E7880930A784091E87850915D +:10051000E97861E070E08AE098E7C2CF8091E47802 +:1005200080930E78DDDE88DE82E08093480C6ACF0F +:1005300010920B7810920C784091E8785091E978FD +:1005400062E070E08BE098E7ABCFCADE5BCF109241 +:100550000A78DBCF82EE98E787D054CF84E090E032 +:10056000809310789093117880910A7985FF12C05A +:1005700063DE80E28093400C809110789091117836 +:10058000029799F580910D78882319F010920D78D3 +:1005900089D0A6DE8091227985FF04C04DDE80E2FD +:1005A0008093460C80912A7985FF09C045DE80E260 +:1005B0008093480C80912C7990912D798AD08091EC +:1005C000427985FF05C038DE80E280934E0C8FD0E3 +:1005D00080E280930B0CDF91CF91089580910F788A +:1005E0008093030C10920F7825CF10921078109200 +:1005F0001178D0CF809168008C7B826498ED94BF95 +:100600008093680081E08093060F8FE697E1019761 +:10061000F1F700C0000077DE8AE099E78093060CCE +:100620009093070C1092080C1092090C83E88093A9 +:10063000000C80E492E4AFE0B0E02091850020FD62 +:1006400004C00197A109B109C1F71092010C0895E6 +:10065000E0E0FCE08181816081830895E0E0FCE0DE +:1006600081818E7F81830895FC019181913299F07F +:1006700018F4903221F096CE9432E8F779CE868144 +:1006800097810797C1F781E08093137A67E070E064 +:1006900080E098E76FCE4681578167E070E080E0A8 +:1006A00098E767CE8091137A81111092137A08959A +:1006B0001092117A1092127A10920F7A1092107A88 +:1006C00010920E7A10920C7A10920D7A1092137A80 +:1006D00008958134910510F080E490E08093117AC0 +:1006E0009093127A10920F7A1092107A08951092C5 +:1006F0000E7A0895DDCF8091117A9091127A892B2C +:10070000F1F020910F7A3091107A8091117A9091C6 +:10071000127A2817390798F01092117A1092127AEB +:1007200010920F7A1092107A10922C7910922D79E3 +:1007300080910B0C82FDFCCF82E08093480C80916D +:100740000E7A811120C080910C7A90910D7A892BBC +:10075000D1F08091427981FF16C080910C7A9091FE +:100760000D7A809344799093457980910B0C82FDAA +:10077000FCCF82E080934E0C81E080930E7A109241 +:100780000C7A10920D7A089520910F7A3091107A98 +:100790008091117A9091127A2817390788F4809104 +:1007A0000F7A9091107A9C012F5F3F4F20930F7A20 +:1007B0003093107A8E5A9748FC01808190E008951A +:1007C0008FEF9FEF0895CF93C82F80910C7A90916F +:1007D0000D7A8034910598F480910C7A90910D7A7D +:1007E0009C012F5F3F4F20930C7A30930D7A8E5EE1 +:1007F0009748FC01C083CF9108957DDFD4DD8091BF +:100800000E7A8111FACF80910C7A90910D7A803412 +:10081000910598F7E1CF80910C7A90910D7A892B10 +:1008200089F480910E7A81110DC0089564DFBBDDDB +:1008300080910E7A811106C080910C7A90910D7A88 +:10084000892B01F180910E7A8111F0CF80910C7A81 +:1008500090910D7A892B51F380910C7A90910D7AB9 +:10086000809344799093457980910B0C82FDFCCF65 +:1008700082E080934E0C81E080930E7A10920C7A85 +:1008800010920D7A089584E19EDF80E19CCF109252 +:10089000327C1092337C10922F7C1092307C10921C +:1008A0002E7C1092197C1092177C1092187C10925A +:1008B000167C1092147A1092157AEAE1FCE784E132 +:1008C000DF011D928A95E9F740E052E06FEF70E09A +:1008D00086E19AE7DEC23F924F925F926F927F92DB +:1008E0008F929F92AF92BF92CF92DF92EF92FF9240 +:1008F0000F931F93CF93DF9300D000D0CDB7DEB717 +:1009000083E0382E92E0E92EF12C20EF922E34E095 +:10091000C32ED12C41EF842EAA24A394B12C34DF12 +:1009200097FF15C02496CDBFDEBFDF91CF911F91F9 +:100930000F91FF90EF90DF90CF90BF90AF909F907E +:100940008F907F906F905F904F903F900895482FC9 +:100950002091327C3091337C2230310509F477C00C +:10096000C8F42115310509F445C021303105B9F627 +:10097000E091197C91E09E0F9093197CF0E0E65E87 +:10098000F348808380912E7C815080932E7C882335 +:10099000E9F1C5CF2430310509F465C108F432C14D +:1009A0002530310509F0BBCFE091197C91E09E0F15 +:1009B0009093197CF0E0E65EF348808380912E7C72 +:1009C000815080932E7C8111AACF80911A7C9091C6 +:1009D0001B7C9827892798278093177C9093187CF5 +:1009E00020911C7C2093167C8130924078F4809278 +:1009F000317C0CC08093317CE0EDE80FE63430F4BC +:100A0000F0E0E05BF04780818E3F29F4E092327C99 +:100A1000F092337C84CF8F3F79F41092197C30921E +:100A20002E7C83E090E0443611F085E090E08093E6 +:100A3000327C9093337C73CF882341F31092197CDE +:100A400080932E7CA092327CB092337C68CF80322F +:100A500009F069C08091317C803F31F58091167C2E +:100A60008F7D863499F42091177C3091187C6091A9 +:100A70002F7C7091307C90E080E046E15AE716D1FF +:100A800002DF1092327C1092337C49CF8534C1F75B +:100A90004091177C5091187C66E17AE780912F7C19 +:100AA0009091307CA8D1ECCF813F81F584E18BDE41 +:100AB00040902F7C5090307C712C612C10E000E035 +:100AC0008091177C9091187C0817190718F080E125 +:100AD0007ADED7CF8091167C8F7D863459F4C3019E +:100AE000B201600F711F811D911D68D16CDE0F5F17 +:100AF0001F4FE6CF853441F480912F7C9091307C5C +:100B0000800F911FB6D1F2CF8FEFF0CF853409F46B +:100B1000B7CF90F4803409F4B3CF38F4803309F4BC +:100B2000AFCF8133A9F185E1D3CF813409F43EC041 +:100B3000823409F4A5CFF7CF823509F4A1CFC0F4F0 +:100B4000803509F49DCF813571F784E13CDE80E189 +:100B50003ADE61DE19821A821B821C8289819A81A7 +:100B6000AB81BC8180349D40A340B10580F1C5DAE2 +:100B70008635C9F1853709F43AC08535A1F68091EB +:100B80001A7C90911B7C80932F7C9093307C78CF43 +:100B900084E119DE06E91FE8FFE80D391F0709F4B3 +:100BA00096CFF80181918F010EDEF6CF80911A7CED +:100BB00011E0803839F018E0813821F012E08238F5 +:100BC00009F013E084E1FFDD812FFDDD80CF898115 +:100BD0009A81AB81BC810196A11DB11D89839A8345 +:100BE000AB83BC83BBCF84E1EEDD80E0EECF84E15C +:100BF000EADD80918010E7DD80918110E4DD809155 +:100C00008210E3CFE091197C91E09E0F9093197CC4 +:100C1000F0E0E65EF348808380912E7C81508093E3 +:100C20002E7C81117CCE80911A7C90911B7C982720 +:100C3000892798278093177C9093187C20911C7C9F +:100C40002093167C8130E2E09E0708F0DFCE109200 +:100C5000147A1092157A892B09F4D8CEC092327C7E +:100C6000D092337C5CCE2091147A3091157AA90110 +:100C70004F5F5F4F4093147A5093157A2A5E354840 +:100C8000F90180838091177C9091187C481759074F +:100C900008F445CE9092317CB9CE9091061091FD2A +:100CA000FCCF9DE994BF809300100895CF92DF920E +:100CB000EF92FF920F931F93CF93DF936115E0E1C3 +:100CC0007E078105910508F470C02115310509F4EE +:100CD0006CC089012130324010F000E012E0209118 +:100CE0000110E1E0F0E0611530E87307810591053E +:100CF00010F4F0E0E0E07A016F0184E0CC0CDD1C40 +:100D00008A95E1F72F7C2C2938ED34BF2093011010 +:100D1000EB01D06888E0C1DF8FEF888380910610F7 +:100D200081FDFCCF81E0B9DF809101108F7C8C299F +:100D300098ED94BF8093011082E0AFDFF70181E06E +:100D400090E0AE014E195F09DA01AE0FBF1F8017A8 +:100D50009107E8F0A8014E7FC40FD51F4017510737 +:100D600060F4E40EF51EF701808190E09F6F8883A8 +:100D700099838091061081FDFCCF81E0DF91CF91B6 +:100D80001F910F91FF90EF90DF90CF9086CF218140 +:100D900030812327322723272D933C93209106105F +:100DA00021FDFCCF02963296CFCFDF91CF911F91DC +:100DB0000F91FF90EF90DF90CF9008954091011038 +:100DC00021E030E0611550E875078105910510F4C8 +:100DD00030E020E084E0220F331F8A95E1F7342FC2 +:100DE0003F7C322B28ED24BF30930110AB015068BB +:100DF000FA01808108950F931F938F3F910509F0A9 +:100E000058F58A0130E020E0080F191F211D311D1F +:100E10000130114021053105E0F040E051E0481B70 +:100E2000590B209106102370E1F72DE924BF23E12F +:100E300020930010460F571FFB01861B970B9C5EEB +:100E4000E417F50761F48DE984BF81E08093001019 +:100E500003C04115510529F71F910F910895DC0139 +:100E6000AE0FBF1F21912C932091061020FDFCCFC7 +:100E7000E7CF8F3F910509F020F49C5EFC01808153 +:100E800008958FEF0895EE0FFF1F0590F491E02D68 +:100E90000994DC0101C06D9341505040E0F7089582 +:040EA000F894FFCFF4 +:100EA40016160342004C0030003000300030003091 +:100EB400003000300031002A2A034100560052005D +:100EC4004400550020004300440043002000420039 +:100ED4006F006F0074006C006F00610064006500B7 +:100EE40072001010034100560052004400550042A5 +:100EF400004C00040309040902430002010080328B +:100F04000904000001020201000524001001052467 +:100F140001000104240202052406000107058103DF +:100F240010004009040100020A0000000705020243 +:100F34004000000705830240000012010002020085 +:100F44000040091201000001010203010000FEFE3D +:100F5400FEFEFEFEFEFEFEFEFEFEFEFE020114FE90 +:100F6400FE05FEFEFEFEFEFEFEFEFEFE000000FE90 +:100F7400FE0204FEFEFEFEFEFEFEFEFEFEFEFEFE83 +:100F8400FFFEFEFEFEFEFEFEFEFEFEFEFEFEFEFE7C +:0A0F9400FF004156522053544B0059 +:080F9E0000C201000000080080 +:040FFA00F8950895C9 +:020FFE00011AD6 +:00000001FF diff --git a/megaavr/bootloaders/hex/usbcdcboot_16du14_novreg.hex b/megaavr/bootloaders/hex/usbcdcboot_16du14_novreg.hex new file mode 100644 index 00000000..ed1b74ba --- /dev/null +++ b/megaavr/bootloaders/hex/usbcdcboot_16du14_novreg.hex @@ -0,0 +1,255 @@ +:1000000043C000005CC000005AC0000058C000009F +:1000100056C0000054C0000052C0000050C0000094 +:100020004EC000004CC000004AC0000048C00000A4 +:1000300046C0000044C0000042C0000040C00000B4 +:100040003EC000003CC000003AC0000038C00000C4 +:1000500036C0000034C0000032C0000030C00000D4 +:100060002EC000002CC000002AC0000028C00000E4 +:1000700026C0000024C0000022C0000020C00000F4 +:100080001EC000001CC0000011241FBECFEFCDBF5A +:10009000DFE7DEBF18E7A0E0B8E7ECE9FFE002C069 +:1000A00005900D92A830B107D9F72CE7A8E0B8E782 +:1000B00001C01D92A433B207E1F72BD0F0C6A0CF48 +:1000C000CF93DF9300D0CDB7DEB78AE190E0019700 +:1000D00029F40F900F90DF91CF91089519821A8221 +:1000E00029813A81283E334090F7000029813A81E6 +:1000F0002F5F3F4F29833A83F3CF80E480936504D9 +:1001000080936204AAD288ED91E084BF90930001AD +:10011000FFCF209140002CBB8FEF80934000809157 +:100120001001806490E038ED34BF8093100124FF0B +:1001300034C01092347C1092357CC0E4DFE18091B1 +:100140006800837C846298ED94BF8093680080E0AF +:1001500094BF809361008091650081FFFCCF4AD2FB +:10016000C8D294D3C0930878D093097880E4809360 +:100170006104809365046BD210E4D0E0C0E013D139 +:10018000B9D2A8D321968091087890910978C817A0 +:10019000D907A8F310936704F0CF22FF25C0809100 +:1001A000347C9091357C8D399B4639F41092347CA7 +:1001B0001092357CC0E8DEE3C2CF8DE99BE68093E8 +:1001C000347C9093357C80E480936104DAE0C0E471 +:1001D000C093660475DFC093650472DFD150C1F728 +:1001E000C0936504C09362041092347C1092357CF5 +:1001F000E0E0F0E185919491C0E0DDE7019609F43B +:100200009ECFF89480E4809365048093620480918B +:10021000100128ED8F7B90E024BF80931001109295 +:10022000000C1092010C80910110806324BF809318 +:100230000110E0E0F8E0099480910B0C82FDFCCF06 +:100240000895EAEEF8E781A1846081A381A5846026 +:1002500081A710921078109211780895CF939B0186 +:100260004617570708F49A01FC01A2EAB8E72134BF +:10027000310580F080931679909317792093147943 +:1002800030931579109218791092197983E717C075 +:1002900081918D93CD01825A984782179307C0F3BD +:1002A00082EA98E7809316799093177920931479CE +:1002B00030931579109218791092197983E480930C +:1002C0001379BADFCAE6C093420CB6DFC093400C84 +:1002D00081E090E08093107890931178CF91089509 +:1002E000EAEEF8E712A613A616A617A683E481A7DE +:1002F000A3DF8AE68093420C83E090E0809310783D +:10030000909311780895EAEEF8E782E291E0DF0138 +:100310009C011D9221503040E1F783E481A322EE3D +:1003200038E724A335A310A281A782EA98E784A71F +:1003300095A782E080A791E891AF22E938E724AF42 +:1003400035AF80AF93E890932B7922E538E720937F +:100350002E7930932F7910922A799093437922E164 +:1003600038E720934679309347798093427908950E +:10037000B7CF74CFEAEEF8E784A795A712A613A625 +:1003800066A777A783E681A757DF8AE68093400CAC +:1003900082E090E0809310789093117881E08093D0 +:1003A0000D7808954ECFCF93DF9380910A0CC091C2 +:1003B0000B0C84FF11C01092030CA5DF1092107873 +:1003C0001092117810920E7810920F7810920D788A +:1003D0006ED180E180930A0CC0FF17C083E4809344 +:1003E0000B798093137928DFD0E1D093400C24DF80 +:1003F000D093420C8091E278807609F1803209F442 +:10040000A9C01FDF81E080930B0CC5FFE4C0809181 +:10041000127985FFA9C010DF80E28093420C8091A1 +:100420001078909111788130910509F497C0059763 +:1004300009F4D4C0109210781092117895C0E09110 +:10044000E378EC30F0F6F0E0E95DFD4F1BC5980273 +:10045000A5020102A5020102740233020102810217 +:100460008E02A702A5028091E4789091E578292F69 +:1004700033272230310561F02330310599F02130E6 +:10048000310509F0BECF62E170E08CE39FE804C063 +:1004900063E470E089EF9EE84091E8785091E97854 +:1004A000DDDEB0CF99278130910569F0C0F0823050 +:1004B000910579F0039709F0A4CF6091A28E70E0C6 +:1004C00083EA9EE8E9CF6091E48E70E085EE9EE8D5 +:1004D000E3CF6091B98E70E08AEB9EE8DDCF64E0F7 +:1004E00070E085EF9EE8D8CF8091E4788F77809395 +:1004F0000F78F6DE85E090E0809310789093117885 +:1005000081CF80910E7880930A784091E87850915D +:10051000E97861E070E08AE098E7C2CF8091E47802 +:1005200080930E78DDDE88DE82E08093480C6ACF0F +:1005300010920B7810920C784091E8785091E978FD +:1005400062E070E08BE098E7ABCFCADE5BCF109241 +:100550000A78DBCF82EE98E786D054CF84E090E033 +:10056000809310789093117880910A7985FF12C05A +:1005700063DE80E28093400C809110789091117836 +:10058000029799F580910D78882319F010920D78D3 +:1005900088D0A6DE8091227985FF04C04DDE80E2FE +:1005A0008093460C80912A7985FF09C045DE80E260 +:1005B0008093480C80912C7990912D7989D08091ED +:1005C000427985FF05C038DE80E280934E0C8ED0E4 +:1005D00080E280930B0CDF91CF91089580910F788A +:1005E0008093030C10920F7825CF10921078109200 +:1005F0001178D0CF809168008C7B826498ED94BF95 +:10060000809368001092060F8FE697E10197F1F74B +:1006100000C0000078DE8AE099E78093060C909392 +:10062000070C1092080C1092090C83E88093000CC0 +:1006300080E492E4AFE0B0E02091850020FD04C0AA +:100640000197A109B109C1F71092010C0895E0E0EA +:10065000FCE08181816081830895E0E0FCE081819C +:100660008E7F81830895FC019181913299F018F475 +:10067000903221F097CE9432E8F77ACE8681978136 +:100680000797C1F781E08093137A67E070E080E01C +:1006900098E770CE4681578167E070E080E098E788 +:1006A00068CE8091137A81111092137A0895109276 +:1006B000117A1092127A10920F7A1092107A109288 +:1006C0000E7A10920C7A10920D7A1092137A089585 +:1006D0008134910510F080E490E08093117A90933A +:1006E000127A10920F7A1092107A089510920E7A60 +:1006F0000895DDCF8091117A9091127A892BF1F0D3 +:1007000020910F7A3091107A8091117A9091127A1B +:100710002817390798F01092117A1092127A1092D5 +:100720000F7A1092107A10922C7910922D79809174 +:100730000B0C82FDFCCF82E08093480C80910E7AF6 +:10074000811120C080910C7A90910D7A892BD1F083 +:100750008091427981FF16C080910C7A90910D7A38 +:10076000809344799093457980910B0C82FDFCCF66 +:1007700082E080934E0C81E080930E7A10920C7A86 +:1007800010920D7A089520910F7A3091107A80910D +:10079000117A9091127A2817390788F480910F7A8C +:1007A0009091107A9C012F5F3F4F20930F7A3093E6 +:1007B000107A8E5A9748FC01808190E008958FEF5F +:1007C0009FEF0895CF93C82F80910C7A90910D7A66 +:1007D0008034910598F480910C7A90910D7A9C0167 +:1007E0002F5F3F4F20930C7A30930D7A8E5E97489F +:1007F000FC01C083CF9108957DDFD5DD80910E7A15 +:100800008111FACF80910C7A90910D7A8034910504 +:1008100098F7E1CF80910C7A90910D7A892B89F429 +:1008200080910E7A81110DC0089564DFBCDD809146 +:100830000E7A811106C080910C7A90910D7A892BE5 +:1008400001F180910E7A8111F0CF80910C7A909114 +:100850000D7A892B51F380910C7A90910D7A8093C7 +:1008600044799093457980910B0C82FDFCCF82E016 +:1008700080934E0C81E080930E7A10920C7A109245 +:100880000D7A089584E19EDF80E19CCF1092327C46 +:100890001092337C10922F7C1092307C10922E7C20 +:1008A0001092197C1092177C1092187C1092167C72 +:1008B0001092147A1092157AEAE1FCE784E1DF01E4 +:1008C0001D928A95E9F740E052E06FEF70E086E113 +:1008D0009AE7DEC23F924F925F926F927F928F9221 +:1008E0009F92AF92BF92CF92DF92EF92FF920F93BF +:1008F0001F93CF93DF9300D000D0CDB7DEB783E056 +:10090000382E92E0E92EF12C20EF922E34E0C32E07 +:10091000D12C41EF842EAA24A394B12C34DF97FF6D +:1009200015C02496CDBFDEBFDF91CF911F910F91EF +:10093000FF90EF90DF90CF90BF90AF909F908F90FF +:100940007F906F905F904F903F900895482F209137 +:10095000327C3091337C2230310509F477C0C8F401 +:100960002115310509F445C021303105B9F6E09172 +:10097000197C91E09E0F9093197CF0E0E65EF348BD +:10098000808380912E7C815080932E7C8823E9F196 +:10099000C5CF2430310509F465C108F432C12530D2 +:1009A000310509F0BBCFE091197C91E09E0F909347 +:1009B000197CF0E0E65EF348808380912E7C8150C4 +:1009C00080932E7C8111AACF80911A7C90911B7C00 +:1009D0009827892798278093177C9093187C2091DB +:1009E0001C7C2093167C8130924078F48092317C7C +:1009F0000CC08093317CE0EDE80FE63430F4F0E099 +:100A0000E25BF04780818E3F29F4E092327CF092E5 +:100A1000337C84CF8F3F79F41092197C30922E7CF6 +:100A200083E090E0443611F085E090E08093327CE2 +:100A30009093337C73CF882341F31092197C809379 +:100A40002E7CA092327CB092337C68CF803209F049 +:100A500069C08091317C803F31F58091167C8F7D1B +:100A6000863499F42091177C3091187C60912F7C0A +:100A70007091307C90E080E046E15AE716D102DFC9 +:100A80001092327C1092337C49CF8534C1F740916B +:100A9000177C5091187C66E17AE780912F7C9091C9 +:100AA000307CA8D1ECCF813F81F584E18BDE409092 +:100AB0002F7C5090307C712C612C10E000E08091F4 +:100AC000177C9091187C0817190718F080E17ADEDE +:100AD000D7CF8091167C8F7D863459F4C301B20143 +:100AE000600F711F811D911D68D16CDE0F5F1F4F5C +:100AF000E6CF853441F480912F7C9091307C800F3B +:100B0000911FB6D1F2CF8FEFF0CF853409F4B7CF74 +:100B100090F4803409F4B3CF38F4803309F4AFCFC4 +:100B20008133A9F185E1D3CF813409F43EC0823409 +:100B300009F4A5CFF7CF823509F4A1CFC0F48035F1 +:100B400009F49DCF813571F784E13CDE80E13ADE26 +:100B500061DE19821A821B821C8289819A81AB8193 +:100B6000BC8180349D40A340B10580F1C6DA863552 +:100B7000C9F1853709F43AC08535A1F680911A7C10 +:100B800090911B7C80932F7C9093307C78CF84E174 +:100B900019DE04E91FE8FFE80B391F0709F496CFB7 +:100BA000F80181918F010EDEF6CF80911A7C11E061 +:100BB000803839F018E0813821F012E0823809F0ED +:100BC00013E084E1FFDD812FFDDD80CF89819A81F3 +:100BD000AB81BC810196A11DB11D89839A83AB8332 +:100BE000BC83BBCF84E1EEDD80E0EECF84E1EADDC3 +:100BF00080918010E7DD80918110E4DD809182108A +:100C0000E3CFE091197C91E09E0F9093197CF0E086 +:100C1000E65EF348808380912E7C815080932E7C09 +:100C200081117CCE80911A7C90911B7C982789271A +:100C300098278093177C9093187C20911C7C20939C +:100C4000167C8130E2E09E0708F0DFCE1092147A25 +:100C50001092157A892B09F4D8CEC092327CD092AA +:100C6000337C5CCE2091147A3091157AA9014F5FC4 +:100C70005F4F4093147A5093157A2A5E3548F901F4 +:100C800080838091177C9091187C4817590708F44D +:100C900045CE9092317CB9CE9091061091FDFCCF5B +:100CA0009DE994BF809300100895CF92DF92EF9258 +:100CB000FF920F931F93CF93DF936115E0E17E07BF +:100CC0008105910508F470C02115310509F46CC047 +:100CD00089012130324010F000E012E02091011033 +:100CE000E1E0F0E0611530E873078105910510F44B +:100CF000F0E0E0E07A016F0184E0CC0CDD1C8A9525 +:100D0000E1F72F7C2C2938ED34BF20930110EB0143 +:100D1000D06888E0C1DF8FEF88838091061081FD65 +:100D2000FCCF81E0B9DF809101108F7C8C2998ED98 +:100D300094BF8093011082E0AFDFF70181E090E083 +:100D4000AE014E195F09DA01AE0FBF1F8017910780 +:100D5000E8F0A8014E7FC40FD51F4017510760F47B +:100D6000E40EF51EF701808190E09F6F88839983E0 +:100D70008091061081FDFCCF81E0DF91CF911F9122 +:100D80000F91FF90EF90DF90CF9086CF218130813F +:100D90002327322723272D933C932091061021FDF2 +:100DA000FCCF02963296CFCFDF91CF911F910F915A +:100DB000FF90EF90DF90CF9008954091011021E0D7 +:100DC00030E0611550E875078105910510F430E0B9 +:100DD00020E084E0220F331F8A95E1F7342F3F7C17 +:100DE000322B28ED24BF30930110AB015068FA017B +:100DF000808108950F931F938F3F910509F058F557 +:100E00008A0130E020E0080F191F211D311D01303B +:100E1000114021053105E0F040E051E0481B590B3D +:100E2000209106102370E1F72DE924BF23E12093E0 +:100E30000010460F571FFB01861B970B9C5EE417A3 +:100E4000F50761F48DE984BF81E08093001003C051 +:100E50004115510529F71F910F910895DC01AE0F3F +:100E6000BF1F21912C932091061020FDFCCFE7CFCE +:100E70008F3F910509F020F49C5EFC01808108956C +:100E80008FEF0895EE0FFF1F0590F491E02D099468 +:100E9000DC0101C06D9341505040E0F70895F89493 +:020EA000FFCF82 +:100EA20016160342004C0030003000300030003093 +:100EB200003000300031002A2A034100560052005F +:100EC200440055002000430044004300200042003B +:100ED2006F006F0074006C006F00610064006500B9 +:100EE20072001010034100560052004400550042A7 +:100EF200004C00040309040902430002010080328D +:100F02000904000001020201000524001001052469 +:100F120001000104240202052406000107058103E1 +:100F220010004009040100020A0000000705020245 +:100F32004000000705830240000012010002020087 +:100F42000040091201000001010203010000FEFE3F +:100F5200FEFEFEFEFEFEFEFEFEFEFEFE020114FE92 +:100F6200FE05FEFEFEFEFEFEFEFEFEFE000000FE92 +:100F7200FE0204FEFEFEFEFEFEFEFEFEFEFEFEFE85 +:100F8200FFFEFEFEFEFEFEFEFEFEFEFEFEFEFEFE7E +:0A0F9200FF004156522053544B005B +:080F9C0000C201000000080082 +:040FFA00F8950895C9 +:020FFE00011AD6 +:00000001FF diff --git a/megaavr/bootloaders/hex/usbcdcboot_16du_novreg.hex b/megaavr/bootloaders/hex/usbcdcboot_16du_novreg.hex new file mode 100644 index 00000000..c706f4e0 --- /dev/null +++ b/megaavr/bootloaders/hex/usbcdcboot_16du_novreg.hex @@ -0,0 +1,255 @@ +:1000000043C000005CC000005AC0000058C000009F +:1000100056C0000054C0000052C0000050C0000094 +:100020004EC000004CC000004AC0000048C00000A4 +:1000300046C0000044C0000042C0000040C00000B4 +:100040003EC000003CC000003AC0000038C00000C4 +:1000500036C0000034C0000032C0000030C00000D4 +:100060002EC000002CC000002AC0000028C00000E4 +:1000700026C0000024C0000022C0000020C00000F4 +:100080001EC000001CC0000011241FBECFEFCDBF5A +:10009000DFE7DEBF18E7A0E0B8E7ECE9FFE002C069 +:1000A00005900D92A830B107D9F72CE7A8E0B8E782 +:1000B00001C01D92A433B207E1F72BD0F0C6A0CF48 +:1000C000CF93DF9300D0CDB7DEB78AE190E0019700 +:1000D00029F40F900F90DF91CF91089519821A8221 +:1000E00029813A81283E334090F7000029813A81E6 +:1000F0002F5F3F4F29833A83F3CF80E88093050435 +:1001000080930204AAD288ED91E084BF909300010D +:10011000FFCF209140002CBB8FEF80934000809157 +:100120001001806490E038ED34BF8093100124FF0B +:1001300034C01092347C1092357CC0E4DFE18091B1 +:100140006800837C846298ED94BF8093680080E0AF +:1001500094BF809361008091650081FFFCCF4AD2FB +:10016000C8D294D3C0930878D093097880E880935C +:100170000104809305046BD210E8D0E0C0E013D1F5 +:10018000B9D2A8D321968091087890910978C817A0 +:10019000D907A8F310930704F0CF22FF25C0809160 +:1001A000347C9091357C8D399B4639F41092347CA7 +:1001B0001092357CC0E8DEE3C2CF8DE99BE68093E8 +:1001C000347C9093357C80E880930104DAE0C0E8C9 +:1001D000C093060475DFC093050472DFD150C1F7E8 +:1001E000C0930504C09302041092347C1092357CB5 +:1001F000E0E0F0E185919491C0E0DDE7019609F43B +:100200009ECFF89480E88093050480930204809147 +:10021000100128ED8F7B90E024BF80931001109295 +:10022000000C1092010C80910110806324BF809318 +:100230000110E0E0F8E0099480910B0C82FDFCCF06 +:100240000895EAEEF8E781A1846081A381A5846026 +:1002500081A710921078109211780895CF939B0186 +:100260004617570708F49A01FC01A2EAB8E72134BF +:10027000310580F080931679909317792093147943 +:1002800030931579109218791092197983E717C075 +:1002900081918D93CD01825A984782179307C0F3BD +:1002A00082EA98E7809316799093177920931479CE +:1002B00030931579109218791092197983E480930C +:1002C0001379BADFCAE6C093420CB6DFC093400C84 +:1002D00081E090E08093107890931178CF91089509 +:1002E000EAEEF8E712A613A616A617A683E481A7DE +:1002F000A3DF8AE68093420C83E090E0809310783D +:10030000909311780895EAEEF8E782E291E0DF0138 +:100310009C011D9221503040E1F783E481A322EE3D +:1003200038E724A335A310A281A782EA98E784A71F +:1003300095A782E080A791E891AF22E938E724AF42 +:1003400035AF80AF93E890932B7922E538E720937F +:100350002E7930932F7910922A799093437922E164 +:1003600038E720934679309347798093427908950E +:10037000B7CF74CFEAEEF8E784A795A712A613A625 +:1003800066A777A783E681A757DF8AE68093400CAC +:1003900082E090E0809310789093117881E08093D0 +:1003A0000D7808954ECFCF93DF9380910A0CC091C2 +:1003B0000B0C84FF11C01092030CA5DF1092107873 +:1003C0001092117810920E7810920F7810920D788A +:1003D0006ED180E180930A0CC0FF17C083E4809344 +:1003E0000B798093137928DFD0E1D093400C24DF80 +:1003F000D093420C8091E278807609F1803209F442 +:10040000A9C01FDF81E080930B0CC5FFE4C0809181 +:10041000127985FFA9C010DF80E28093420C8091A1 +:100420001078909111788130910509F497C0059763 +:1004300009F4D4C0109210781092117895C0E09110 +:10044000E378EC30F0F6F0E0E95DFD4F1BC5980273 +:10045000A5020102A5020102740233020102810217 +:100460008E02A702A5028091E4789091E578292F69 +:1004700033272230310561F02330310599F02130E6 +:10048000310509F0BECF62E170E08CE39FE804C063 +:1004900063E470E089EF9EE84091E8785091E97854 +:1004A000DDDEB0CF99278130910569F0C0F0823050 +:1004B000910579F0039709F0A4CF6091A28E70E0C6 +:1004C00083EA9EE8E9CF6091E48E70E085EE9EE8D5 +:1004D000E3CF6091B98E70E08AEB9EE8DDCF64E0F7 +:1004E00070E085EF9EE8D8CF8091E4788F77809395 +:1004F0000F78F6DE85E090E0809310789093117885 +:1005000081CF80910E7880930A784091E87850915D +:10051000E97861E070E08AE098E7C2CF8091E47802 +:1005200080930E78DDDE88DE82E08093480C6ACF0F +:1005300010920B7810920C784091E8785091E978FD +:1005400062E070E08BE098E7ABCFCADE5BCF109241 +:100550000A78DBCF82EE98E786D054CF84E090E033 +:10056000809310789093117880910A7985FF12C05A +:1005700063DE80E28093400C809110789091117836 +:10058000029799F580910D78882319F010920D78D3 +:1005900088D0A6DE8091227985FF04C04DDE80E2FE +:1005A0008093460C80912A7985FF09C045DE80E260 +:1005B0008093480C80912C7990912D7989D08091ED +:1005C000427985FF05C038DE80E280934E0C8ED0E4 +:1005D00080E280930B0CDF91CF91089580910F788A +:1005E0008093030C10920F7825CF10921078109200 +:1005F0001178D0CF809168008C7B826498ED94BF95 +:10060000809368001092060F8FE697E10197F1F74B +:1006100000C0000078DE8AE099E78093060C909392 +:10062000070C1092080C1092090C83E88093000CC0 +:1006300080E492E4AFE0B0E02091850020FD04C0AA +:100640000197A109B109C1F71092010C0895E0E0EA +:10065000FCE08181816081830895E0E0FCE081819C +:100660008E7F81830895FC019181913299F018F475 +:10067000903221F097CE9432E8F77ACE8681978136 +:100680000797C1F781E08093137A67E070E080E01C +:1006900098E770CE4681578167E070E080E098E788 +:1006A00068CE8091137A81111092137A0895109276 +:1006B000117A1092127A10920F7A1092107A109288 +:1006C0000E7A10920C7A10920D7A1092137A089585 +:1006D0008134910510F080E490E08093117A90933A +:1006E000127A10920F7A1092107A089510920E7A60 +:1006F0000895DDCF8091117A9091127A892BF1F0D3 +:1007000020910F7A3091107A8091117A9091127A1B +:100710002817390798F01092117A1092127A1092D5 +:100720000F7A1092107A10922C7910922D79809174 +:100730000B0C82FDFCCF82E08093480C80910E7AF6 +:10074000811120C080910C7A90910D7A892BD1F083 +:100750008091427981FF16C080910C7A90910D7A38 +:10076000809344799093457980910B0C82FDFCCF66 +:1007700082E080934E0C81E080930E7A10920C7A86 +:1007800010920D7A089520910F7A3091107A80910D +:10079000117A9091127A2817390788F480910F7A8C +:1007A0009091107A9C012F5F3F4F20930F7A3093E6 +:1007B000107A8E5A9748FC01808190E008958FEF5F +:1007C0009FEF0895CF93C82F80910C7A90910D7A66 +:1007D0008034910598F480910C7A90910D7A9C0167 +:1007E0002F5F3F4F20930C7A30930D7A8E5E97489F +:1007F000FC01C083CF9108957DDFD5DD80910E7A15 +:100800008111FACF80910C7A90910D7A8034910504 +:1008100098F7E1CF80910C7A90910D7A892B89F429 +:1008200080910E7A81110DC0089564DFBCDD809146 +:100830000E7A811106C080910C7A90910D7A892BE5 +:1008400001F180910E7A8111F0CF80910C7A909114 +:100850000D7A892B51F380910C7A90910D7A8093C7 +:1008600044799093457980910B0C82FDFCCF82E016 +:1008700080934E0C81E080930E7A10920C7A109245 +:100880000D7A089584E19EDF80E19CCF1092327C46 +:100890001092337C10922F7C1092307C10922E7C20 +:1008A0001092197C1092177C1092187C1092167C72 +:1008B0001092147A1092157AEAE1FCE784E1DF01E4 +:1008C0001D928A95E9F740E052E06FEF70E086E113 +:1008D0009AE7DEC23F924F925F926F927F928F9221 +:1008E0009F92AF92BF92CF92DF92EF92FF920F93BF +:1008F0001F93CF93DF9300D000D0CDB7DEB783E056 +:10090000382E92E0E92EF12C20EF922E34E0C32E07 +:10091000D12C41EF842EAA24A394B12C34DF97FF6D +:1009200015C02496CDBFDEBFDF91CF911F910F91EF +:10093000FF90EF90DF90CF90BF90AF909F908F90FF +:100940007F906F905F904F903F900895482F209137 +:10095000327C3091337C2230310509F477C0C8F401 +:100960002115310509F445C021303105B9F6E09172 +:10097000197C91E09E0F9093197CF0E0E65EF348BD +:10098000808380912E7C815080932E7C8823E9F196 +:10099000C5CF2430310509F465C108F432C12530D2 +:1009A000310509F0BBCFE091197C91E09E0F909347 +:1009B000197CF0E0E65EF348808380912E7C8150C4 +:1009C00080932E7C8111AACF80911A7C90911B7C00 +:1009D0009827892798278093177C9093187C2091DB +:1009E0001C7C2093167C8130924078F48092317C7C +:1009F0000CC08093317CE0EDE80FE63430F4F0E099 +:100A0000E25BF04780818E3F29F4E092327CF092E5 +:100A1000337C84CF8F3F79F41092197C30922E7CF6 +:100A200083E090E0443611F085E090E08093327CE2 +:100A30009093337C73CF882341F31092197C809379 +:100A40002E7CA092327CB092337C68CF803209F049 +:100A500069C08091317C803F31F58091167C8F7D1B +:100A6000863499F42091177C3091187C60912F7C0A +:100A70007091307C90E080E046E15AE716D102DFC9 +:100A80001092327C1092337C49CF8534C1F740916B +:100A9000177C5091187C66E17AE780912F7C9091C9 +:100AA000307CA8D1ECCF813F81F584E18BDE409092 +:100AB0002F7C5090307C712C612C10E000E08091F4 +:100AC000177C9091187C0817190718F080E17ADEDE +:100AD000D7CF8091167C8F7D863459F4C301B20143 +:100AE000600F711F811D911D68D16CDE0F5F1F4F5C +:100AF000E6CF853441F480912F7C9091307C800F3B +:100B0000911FB6D1F2CF8FEFF0CF853409F4B7CF74 +:100B100090F4803409F4B3CF38F4803309F4AFCFC4 +:100B20008133A9F185E1D3CF813409F43EC0823409 +:100B300009F4A5CFF7CF823509F4A1CFC0F48035F1 +:100B400009F49DCF813571F784E13CDE80E13ADE26 +:100B500061DE19821A821B821C8289819A81AB8193 +:100B6000BC8180349D40A340B10580F1C6DA863552 +:100B7000C9F1853709F43AC08535A1F680911A7C10 +:100B800090911B7C80932F7C9093307C78CF84E174 +:100B900019DE04E91FE8FFE80B391F0709F496CFB7 +:100BA000F80181918F010EDEF6CF80911A7C11E061 +:100BB000803839F018E0813821F012E0823809F0ED +:100BC00013E084E1FFDD812FFDDD80CF89819A81F3 +:100BD000AB81BC810196A11DB11D89839A83AB8332 +:100BE000BC83BBCF84E1EEDD80E0EECF84E1EADDC3 +:100BF00080918010E7DD80918110E4DD809182108A +:100C0000E3CFE091197C91E09E0F9093197CF0E086 +:100C1000E65EF348808380912E7C815080932E7C09 +:100C200081117CCE80911A7C90911B7C982789271A +:100C300098278093177C9093187C20911C7C20939C +:100C4000167C8130E2E09E0708F0DFCE1092147A25 +:100C50001092157A892B09F4D8CEC092327CD092AA +:100C6000337C5CCE2091147A3091157AA9014F5FC4 +:100C70005F4F4093147A5093157A2A5E3548F901F4 +:100C800080838091177C9091187C4817590708F44D +:100C900045CE9092317CB9CE9091061091FDFCCF5B +:100CA0009DE994BF809300100895CF92DF92EF9258 +:100CB000FF920F931F93CF93DF936115E0E17E07BF +:100CC0008105910508F470C02115310509F46CC047 +:100CD00089012130324010F000E012E02091011033 +:100CE000E1E0F0E0611530E873078105910510F44B +:100CF000F0E0E0E07A016F0184E0CC0CDD1C8A9525 +:100D0000E1F72F7C2C2938ED34BF20930110EB0143 +:100D1000D06888E0C1DF8FEF88838091061081FD65 +:100D2000FCCF81E0B9DF809101108F7C8C2998ED98 +:100D300094BF8093011082E0AFDFF70181E090E083 +:100D4000AE014E195F09DA01AE0FBF1F8017910780 +:100D5000E8F0A8014E7FC40FD51F4017510760F47B +:100D6000E40EF51EF701808190E09F6F88839983E0 +:100D70008091061081FDFCCF81E0DF91CF911F9122 +:100D80000F91FF90EF90DF90CF9086CF218130813F +:100D90002327322723272D933C932091061021FDF2 +:100DA000FCCF02963296CFCFDF91CF911F910F915A +:100DB000FF90EF90DF90CF9008954091011021E0D7 +:100DC00030E0611550E875078105910510F430E0B9 +:100DD00020E084E0220F331F8A95E1F7342F3F7C17 +:100DE000322B28ED24BF30930110AB015068FA017B +:100DF000808108950F931F938F3F910509F058F557 +:100E00008A0130E020E0080F191F211D311D01303B +:100E1000114021053105E0F040E051E0481B590B3D +:100E2000209106102370E1F72DE924BF23E12093E0 +:100E30000010460F571FFB01861B970B9C5EE417A3 +:100E4000F50761F48DE984BF81E08093001003C051 +:100E50004115510529F71F910F910895DC01AE0F3F +:100E6000BF1F21912C932091061020FDFCCFE7CFCE +:100E70008F3F910509F020F49C5EFC01808108956C +:100E80008FEF0895EE0FFF1F0590F491E02D099468 +:100E9000DC0101C06D9341505040E0F70895F89493 +:020EA000FFCF82 +:100EA20016160342004C0030003000300030003093 +:100EB200003000300031002A2A034100560052005F +:100EC200440055002000430044004300200042003B +:100ED2006F006F0074006C006F00610064006500B9 +:100EE20072001010034100560052004400550042A7 +:100EF200004C00040309040902430002010080328D +:100F02000904000001020201000524001001052469 +:100F120001000104240202052406000107058103E1 +:100F220010004009040100020A0000000705020245 +:100F32004000000705830240000012010002020087 +:100F42000040091201000001010203010000FEFE3F +:100F5200FEFEFEFEFEFEFEFEFEFEFEFE020114FE92 +:100F6200FE05FEFEFEFEFEFEFEFEFEFE000000FE92 +:100F7200FE0204FEFEFEFEFEFEFEFEFEFEFEFEFE85 +:100F8200FFFEFEFEFEFEFEFEFEFEFEFEFEFEFEFE7E +:0A0F9200FF004156522053544B005B +:080F9C0000C201000000080082 +:040FFA00F8950895C9 +:020FFE00011AD6 +:00000001FF diff --git a/megaavr/bootloaders/hex/usbcdcboot_32du.hex b/megaavr/bootloaders/hex/usbcdcboot_32du.hex new file mode 100644 index 00000000..9addead9 --- /dev/null +++ b/megaavr/bootloaders/hex/usbcdcboot_32du.hex @@ -0,0 +1,255 @@ +:1000000043C000005CC000005AC0000058C000009F +:1000100056C0000054C0000052C0000050C0000094 +:100020004EC000004CC000004AC0000048C00000A4 +:1000300046C0000044C0000042C0000040C00000B4 +:100040003EC000003CC000003AC0000038C00000C4 +:1000500036C0000034C0000032C0000030C00000D4 +:100060002EC000002CC000002AC0000028C00000E4 +:1000700026C0000024C0000022C0000020C00000F4 +:100080001EC000001CC0000011241FBECFEFCDBF5A +:10009000DFE7DEBF10E7A0E0B0E7EEE9FFE002C077 +:1000A00005900D92A830B107D9F724E7A8E0B0E792 +:1000B00001C01D92A433B207E1F72BD0F1C6A0CF47 +:1000C000CF93DF9300D0CDB7DEB78AE190E0019700 +:1000D00029F40F900F90DF91CF91089519821A8221 +:1000E00029813A81283E334090F7000029813A81E6 +:1000F0002F5F3F4F29833A83F3CF80E88093050435 +:1001000080930204ABD288ED91E084BF909300010C +:10011000FFCF209140002CBB8FEF80934000809157 +:100120001001806490E038ED34BF8093100124FF0B +:1001300034C01092347410923574C0E4DFE18091C1 +:100140006800837C846298ED94BF8093680080E0AF +:1001500094BF809361008091650081FFFCCF4AD2FB +:10016000C9D295D3C0930870D093097080E880936A +:100170000104809305046CD210E8D0E0C0E013D1F4 +:10018000BAD2A9D321968091087090910970C817AE +:10019000D907A8F310930704F0CF22FF25C0809160 +:1001A0003474909135748D399B4639F410923474BF +:1001B00010923574C0E8DEE3C2CF8DE99BE68093F0 +:1001C00034749093357480E880930104DAE0C0E8D9 +:1001D000C093060475DFC093050472DFD150C1F7E8 +:1001E000C0930504C09302041092347410923574C5 +:1001F000E0E0F0E185919491C0E0DDE7019609F43B +:100200009ECFF89480E88093050480930204809147 +:10021000100128ED8F7B90E024BF80931001109295 +:10022000000C1092010C80910110806324BF809318 +:100230000110E0E0F8E0099480910B0C82FDFCCF06 +:100240000895EAEEF0E781A1846081A381A584602E +:1002500081A710921070109211700895CF939B0196 +:100260004617570708F49A01FC01A2EAB0E72134C7 +:10027000310580F08093167190931771209314715B +:1002800030931571109218711092197183E717C08D +:1002900081918D93CD01825A904782179307C0F3C5 +:1002A00082EA90E7809316719093177120931471EE +:1002B00030931571109218711092197183E4809324 +:1002C0001371BADFCAE6C093420CB6DFC093400C8C +:1002D00081E090E08093107090931170CF91089519 +:1002E000EAEEF0E712A613A616A617A683E481A7E6 +:1002F000A3DF8AE68093420C83E090E08093107045 +:10030000909311700895EAEEF0E782E291E0DF0148 +:100310009C011D9221503040E1F783E481A322EE3D +:1003200030E724A335A310A281A782EA90E784A72F +:1003300095A782E080A791E891AF22E930E724AF4A +:1003400035AF80AF93E890932B7122E530E720938F +:100350002E7130932F7110922A719093437122E184 +:1003600030E720934671309347718093427108952E +:10037000B7CF74CFEAEEF0E784A795A712A613A62D +:1003800066A777A783E681A757DF8AE68093400CAC +:1003900082E090E0809310709093117081E08093E0 +:1003A0000D7008954ECFCF93DF9380910A0CC091CA +:1003B0000B0C84FF11C01092030CA5DF109210707B +:1003C0001092117010920E7010920F7010920D70AA +:1003D0006FD180E180930A0CC0FF17C083E4809343 +:1003E0000B718093137128DFD0E1D093400C24DF90 +:1003F000D093420C8091E270807609F1803209F44A +:10040000A9C01FDF81E080930B0CC5FFE4C0809181 +:10041000127185FFA9C010DF80E28093420C8091A9 +:100420001070909111708130910509F497C0059773 +:1004300009F4D4C0109210701092117095C0E09120 +:10044000E370EC30F0F6F0E0E95DFD4F1CC598027A +:10045000A5020102A5020102740233020102810217 +:100460008E02A702A5028091E4709091E570292F79 +:1004700033272230310561F02330310599F02130E6 +:10048000310509F0BECF62E170E08EE39FE804C061 +:1004900063E470E08BEF9EE84091E8705091E97062 +:1004A000DDDEB0CF99278130910569F0C0F0823050 +:1004B000910579F0039709F0A4CF6091A48E70E0C4 +:1004C00085EA9EE8E9CF6091E68E70E087EE9EE8CF +:1004D000E3CF6091BB8E70E08CEB9EE8DDCF64E0F3 +:1004E00070E087EF9EE8D8CF8091E4708F7780939B +:1004F0000F70F6DE85E090E080931070909311709D +:1005000081CF80910E7080930A704091E870509175 +:10051000E97061E070E08AE090E7C2CF8091E4701A +:1005200080930E70DDDE88DE82E08093480C6ACF17 +:1005300010920B7010920C704091E8705091E9701D +:1005400062E070E08BE090E7ABCFCADE5BCF109249 +:100550000A70DBCF82EE90E787D054CF84E090E042 +:10056000809310709093117080910A7185FF12C072 +:1005700063DE80E28093400C809110709091117046 +:10058000029799F580910D70882319F010920D70E3 +:1005900089D0A6DE8091227185FF04C04DDE80E205 +:1005A0008093460C80912A7185FF09C045DE80E268 +:1005B0008093480C80912C7190912D718AD08091FC +:1005C000427185FF05C038DE80E280934E0C8FD0EB +:1005D00080E280930B0CDF91CF91089580910F7092 +:1005E0008093030C10920F7025CF10921070109210 +:1005F0001170D0CF809168008C7B826498ED94BF9D +:100600008093680081E08093060F8FE697E1019761 +:10061000F1F700C0000077DE8AE091E78093060CD6 +:100620009093070C1092080C1092090C83E88093A9 +:10063000000C80E492E4AFE0B0E02091850020FD62 +:1006400004C00197A109B109C1F71092010C0895E6 +:10065000E0E0FCE08181816081830895E0E0FCE0DE +:1006600081818E7F81830895FC019181913299F07F +:1006700018F4903221F096CE9432E8F779CE868144 +:1006800097810797C1F781E08093137267E070E06C +:1006900080E090E76FCE4681578167E070E080E0B0 +:1006A00090E767CE809113728111109213720895B2 +:1006B000109211721092127210920F7210921072A8 +:1006C00010920E7210920C7210920D7210921372A0 +:1006D00008958134910510F080E490E080931172C8 +:1006E0009093127210920F721092107208951092DD +:1006F0000E720895DDCF8091117290911272892B44 +:10070000F1F020910F7230911072809111729091DE +:1007100012722817390798F0109211721092127203 +:1007200010920F721092107210922C7110922D7103 +:1007300080910B0C82FDFCCF82E08093480C80916D +:100740000E72811120C080910C7290910D72892BD4 +:10075000D1F08091427181FF16C080910C7290910E +:100760000D72809344719093457180910B0C82FDC2 +:10077000FCCF82E080934E0C81E080930E72109249 +:100780000C7210920D72089520910F7230911072B8 +:1007900080911172909112722817390788F4809114 +:1007A0000F72909110729C012F5F3F4F20930F7238 +:1007B000309310728E5A9F48FC01808190E008951A +:1007C0008FEF9FEF0895CF93C82F80910C72909177 +:1007D0000D728034910598F480910C7290910D7295 +:1007E0009C012F5F3F4F20930C7230930D728E5EF1 +:1007F0009F48FC01C083CF9108957DDFD4DD8091B7 +:100800000E728111FACF80910C7290910D7280342A +:10081000910598F7E1CF80910C7290910D72892B20 +:1008200089F480910E7281110DC0089564DFBBDDE3 +:1008300080910E72811106C080910C7290910D72A0 +:10084000892B01F180910E728111F0CF80910C7291 +:1008500090910D72892B51F380910C7290910D72D1 +:10086000809344719093457180910B0C82FDFCCF75 +:1008700082E080934E0C81E080930E7210920C7295 +:1008800010920D72089584E19EDF80E19CCF10925A +:1008900032741092337410922F741092307410923C +:1008A0002E7410921974109217741092187410927A +:1008B00016741092147210921572EAE1F4E784E152 +:1008C000DF011D928A95E9F740E052E06FEF70E09A +:1008D00086E192E7DEC23F924F925F926F927F92E3 +:1008E0008F929F92AF92BF92CF92DF92EF92FF9240 +:1008F0000F931F93CF93DF9300D000D0CDB7DEB717 +:1009000083E0382E92E0E92EF12C20EF922E34E095 +:10091000C32ED12C41EF842EAA24A394B12C34DF12 +:1009200097FF15C02496CDBFDEBFDF91CF911F91F9 +:100930000F91FF90EF90DF90CF90BF90AF909F907E +:100940008F907F906F905F904F903F900895482FC9 +:1009500020913274309133742230310509F477C01C +:10096000C8F42115310509F445C021303105B9F627 +:10097000E091197491E09E0F90931974F0E0E65E97 +:10098000FB48808380912E74815080932E7488233D +:10099000E9F1C5CF2430310509F465C108F432C14D +:1009A0002530310509F0BBCFE091197491E09E0F1D +:1009B00090931974F0E0E65EFB48808380912E747A +:1009C000815080932E748111AACF80911A749091D6 +:1009D0001B7498278927982780931774909318740D +:1009E00020911C74209316748130924078F4809288 +:1009F00031740CC080933174E0EDE80FE63430F4CC +:100A0000F0E0E05BF04780818E3F29F4E0923274A1 +:100A1000F092337484CF8F3F79F41092197430922E +:100A20002E7483E090E0443611F085E090E08093EE +:100A300032749093337473CF882341F310921974F6 +:100A400080932E74A0923274B092337468CF803247 +:100A500009F069C080913174803F31F5809116743E +:100A60008F7D863499F420911774309118746091B9 +:100A70002F747091307490E080E046E152E716D117 +:100A800002DF109232741092337449CF8534C1F76B +:100A9000409117745091187466E172E780912F7439 +:100AA00090913074A8D1ECCF813F81F584E18BDE49 +:100AB00040902F7450903074712C612C10E000E045 +:100AC00080911774909118740817190718F080E135 +:100AD0007ADED7CF809116748F7D863459F4C301A6 +:100AE000B201600F711F811D911D68D16CDE0F5F17 +:100AF0001F4FE6CF853441F480912F74909130746C +:100B0000800F911FB6D1F2CF8FEFF0CF853409F46B +:100B1000B7CF90F4803409F4B3CF38F4803309F4BC +:100B2000AFCF8133A9F185E1D3CF813409F43EC041 +:100B3000823409F4A5CFF7CF823509F4A1CFC0F4F0 +:100B4000803509F49DCF813571F784E13CDE80E189 +:100B50003ADE61DE19821A821B821C8289819A81A7 +:100B6000AB81BC8180349D40A340B10580F1C5DAE2 +:100B70008635C9F1853709F43AC08535A1F68091EB +:100B80001A7490911B7480932F749093307478CF63 +:100B900084E119DE06E91FE8FFE80D391F0709F4B3 +:100BA00096CFF80181918F010EDEF6CF80911A74F5 +:100BB00011E0803839F018E0813821F012E08238F5 +:100BC00009F013E084E1FFDD812FFDDD80CF898115 +:100BD0009A81AB81BC810196A11DB11D89839A8345 +:100BE000AB83BC83BBCF84E1EEDD80E0EECF84E15C +:100BF000EADD80918010E7DD80918110E4DD809155 +:100C00008210E3CFE091197491E09E0F90931974D4 +:100C1000F0E0E65EFB48808380912E7481508093E3 +:100C20002E7481117CCE80911A7490911B74982738 +:100C300089279827809317749093187420911C74B7 +:100C4000209316748130E2E09E0708F0DFCE109208 +:100C5000147210921572892B09F4D8CEC092327496 +:100C6000D09233745CCE2091147230911572A90128 +:100C70004F5F5F4F40931472509315722A5E3D4848 +:100C8000F90180838091177490911874481759075F +:100C900008F445CE90923174B9CE9091061091FD32 +:100CA000FCCF9DE994BF809300100895CF92DF920E +:100CB000EF92FF920F931F93CF93DF936115E0E1C3 +:100CC0007E078105910508F470C02115310509F4EE +:100CD0006CC089012130324010F000E012E0209118 +:100CE0000110E1E0F0E0611530E87307810591053E +:100CF00010F4F0E0E0E07A016F0184E0CC0CDD1C40 +:100D00008A95E1F72F7C2C2938ED34BF2093011010 +:100D1000EB01D06888E0C1DF8FEF888380910610F7 +:100D200081FDFCCF81E0B9DF809101108F7C8C299F +:100D300098ED94BF8093011082E0AFDFF70181E06E +:100D400090E0AE014E195F09DA01AE0FBF1F8017A8 +:100D50009107E8F0A8014E7FC40FD51F4017510737 +:100D600060F4E40EF51EF701808190E09F6F8883A8 +:100D700099838091061081FDFCCF81E0DF91CF91B6 +:100D80001F910F91FF90EF90DF90CF9086CF218140 +:100D900030812327322723272D933C93209106105F +:100DA00021FDFCCF02963296CFCFDF91CF911F91DC +:100DB0000F91FF90EF90DF90CF9008954091011038 +:100DC00021E030E0611550E875078105910510F4C8 +:100DD00030E020E084E0220F331F8A95E1F7342FC2 +:100DE0003F7C322B28ED24BF30930110AB015068BB +:100DF000FA01808108950F931F938F3F910509F0A9 +:100E000058F58A0130E020E0080F191F211D311D1F +:100E10000130114021053105E0F040E051E0481B70 +:100E2000590B209106102370E1F72DE924BF23E12F +:100E300020930010460F571FFB01861B970B9C5EEB +:100E4000E417F50761F48DE984BF81E08093001019 +:100E500003C04115510529F71F910F910895DC0139 +:100E6000AE0FBF1F21912C932091061020FDFCCFC7 +:100E7000E7CF8F3F910509F020F49C5EFC01808153 +:100E800008958FEF0895EE0FFF1F0590F491E02D68 +:100E90000994DC0101C06D9341505040E0F7089582 +:040EA000F894FFCFF4 +:100EA40016160342004C0030003000300030003091 +:100EB400003000300031002A2A034100560052005D +:100EC4004400550020004300440043002000420039 +:100ED4006F006F0074006C006F00610064006500B7 +:100EE40072001010034100560052004400550042A5 +:100EF400004C00040309040902430002010080328B +:100F04000904000001020201000524001001052467 +:100F140001000104240202052406000107058103DF +:100F240010004009040100020A0000000705020243 +:100F34004000000705830240000012010002020085 +:100F44000040091201000001010203010000FEFE3D +:100F5400FEFEFEFEFEFEFEFEFEFEFEFE020114FE90 +:100F6400FE05FEFEFEFEFEFEFEFEFEFE000000FE90 +:100F7400FE0204FEFEFEFEFEFEFEFEFEFEFEFEFE83 +:100F8400FFFEFEFEFEFEFEFEFEFEFEFEFEFEFEFE7C +:0A0F9400FF004156522053544B0059 +:080F9E0000C201000000080080 +:040FFA00F8950895C9 +:020FFE00011AD6 +:00000001FF diff --git a/megaavr/bootloaders/hex/usbcdcboot_32du14.hex b/megaavr/bootloaders/hex/usbcdcboot_32du14.hex new file mode 100644 index 00000000..3baa86e9 --- /dev/null +++ b/megaavr/bootloaders/hex/usbcdcboot_32du14.hex @@ -0,0 +1,255 @@ +:1000000043C000005CC000005AC0000058C000009F +:1000100056C0000054C0000052C0000050C0000094 +:100020004EC000004CC000004AC0000048C00000A4 +:1000300046C0000044C0000042C0000040C00000B4 +:100040003EC000003CC000003AC0000038C00000C4 +:1000500036C0000034C0000032C0000030C00000D4 +:100060002EC000002CC000002AC0000028C00000E4 +:1000700026C0000024C0000022C0000020C00000F4 +:100080001EC000001CC0000011241FBECFEFCDBF5A +:10009000DFE7DEBF10E7A0E0B0E7EEE9FFE002C077 +:1000A00005900D92A830B107D9F724E7A8E0B0E792 +:1000B00001C01D92A433B207E1F72BD0F1C6A0CF47 +:1000C000CF93DF9300D0CDB7DEB78AE190E0019700 +:1000D00029F40F900F90DF91CF91089519821A8221 +:1000E00029813A81283E334090F7000029813A81E6 +:1000F0002F5F3F4F29833A83F3CF80E480936504D9 +:1001000080936204ABD288ED91E084BF90930001AC +:10011000FFCF209140002CBB8FEF80934000809157 +:100120001001806490E038ED34BF8093100124FF0B +:1001300034C01092347410923574C0E4DFE18091C1 +:100140006800837C846298ED94BF8093680080E0AF +:1001500094BF809361008091650081FFFCCF4AD2FB +:10016000C9D295D3C0930870D093097080E480936E +:100170006104809365046CD210E4D0E0C0E013D138 +:10018000BAD2A9D321968091087090910970C817AE +:10019000D907A8F310936704F0CF22FF25C0809100 +:1001A0003474909135748D399B4639F410923474BF +:1001B00010923574C0E8DEE3C2CF8DE99BE68093F0 +:1001C00034749093357480E480936104DAE0C0E481 +:1001D000C093660475DFC093650472DFD150C1F728 +:1001E000C0936504C0936204109234741092357405 +:1001F000E0E0F0E185919491C0E0DDE7019609F43B +:100200009ECFF89480E4809365048093620480918B +:10021000100128ED8F7B90E024BF80931001109295 +:10022000000C1092010C80910110806324BF809318 +:100230000110E0E0F8E0099480910B0C82FDFCCF06 +:100240000895EAEEF0E781A1846081A381A584602E +:1002500081A710921070109211700895CF939B0196 +:100260004617570708F49A01FC01A2EAB0E72134C7 +:10027000310580F08093167190931771209314715B +:1002800030931571109218711092197183E717C08D +:1002900081918D93CD01825A904782179307C0F3C5 +:1002A00082EA90E7809316719093177120931471EE +:1002B00030931571109218711092197183E4809324 +:1002C0001371BADFCAE6C093420CB6DFC093400C8C +:1002D00081E090E08093107090931170CF91089519 +:1002E000EAEEF0E712A613A616A617A683E481A7E6 +:1002F000A3DF8AE68093420C83E090E08093107045 +:10030000909311700895EAEEF0E782E291E0DF0148 +:100310009C011D9221503040E1F783E481A322EE3D +:1003200030E724A335A310A281A782EA90E784A72F +:1003300095A782E080A791E891AF22E930E724AF4A +:1003400035AF80AF93E890932B7122E530E720938F +:100350002E7130932F7110922A719093437122E184 +:1003600030E720934671309347718093427108952E +:10037000B7CF74CFEAEEF0E784A795A712A613A62D +:1003800066A777A783E681A757DF8AE68093400CAC +:1003900082E090E0809310709093117081E08093E0 +:1003A0000D7008954ECFCF93DF9380910A0CC091CA +:1003B0000B0C84FF11C01092030CA5DF109210707B +:1003C0001092117010920E7010920F7010920D70AA +:1003D0006FD180E180930A0CC0FF17C083E4809343 +:1003E0000B718093137128DFD0E1D093400C24DF90 +:1003F000D093420C8091E270807609F1803209F44A +:10040000A9C01FDF81E080930B0CC5FFE4C0809181 +:10041000127185FFA9C010DF80E28093420C8091A9 +:100420001070909111708130910509F497C0059773 +:1004300009F4D4C0109210701092117095C0E09120 +:10044000E370EC30F0F6F0E0E95DFD4F1CC598027A +:10045000A5020102A5020102740233020102810217 +:100460008E02A702A5028091E4709091E570292F79 +:1004700033272230310561F02330310599F02130E6 +:10048000310509F0BECF62E170E08EE39FE804C061 +:1004900063E470E08BEF9EE84091E8705091E97062 +:1004A000DDDEB0CF99278130910569F0C0F0823050 +:1004B000910579F0039709F0A4CF6091A48E70E0C4 +:1004C00085EA9EE8E9CF6091E68E70E087EE9EE8CF +:1004D000E3CF6091BB8E70E08CEB9EE8DDCF64E0F3 +:1004E00070E087EF9EE8D8CF8091E4708F7780939B +:1004F0000F70F6DE85E090E080931070909311709D +:1005000081CF80910E7080930A704091E870509175 +:10051000E97061E070E08AE090E7C2CF8091E4701A +:1005200080930E70DDDE88DE82E08093480C6ACF17 +:1005300010920B7010920C704091E8705091E9701D +:1005400062E070E08BE090E7ABCFCADE5BCF109249 +:100550000A70DBCF82EE90E787D054CF84E090E042 +:10056000809310709093117080910A7185FF12C072 +:1005700063DE80E28093400C809110709091117046 +:10058000029799F580910D70882319F010920D70E3 +:1005900089D0A6DE8091227185FF04C04DDE80E205 +:1005A0008093460C80912A7185FF09C045DE80E268 +:1005B0008093480C80912C7190912D718AD08091FC +:1005C000427185FF05C038DE80E280934E0C8FD0EB +:1005D00080E280930B0CDF91CF91089580910F7092 +:1005E0008093030C10920F7025CF10921070109210 +:1005F0001170D0CF809168008C7B826498ED94BF9D +:100600008093680081E08093060F8FE697E1019761 +:10061000F1F700C0000077DE8AE091E78093060CD6 +:100620009093070C1092080C1092090C83E88093A9 +:10063000000C80E492E4AFE0B0E02091850020FD62 +:1006400004C00197A109B109C1F71092010C0895E6 +:10065000E0E0FCE08181816081830895E0E0FCE0DE +:1006600081818E7F81830895FC019181913299F07F +:1006700018F4903221F096CE9432E8F779CE868144 +:1006800097810797C1F781E08093137267E070E06C +:1006900080E090E76FCE4681578167E070E080E0B0 +:1006A00090E767CE809113728111109213720895B2 +:1006B000109211721092127210920F7210921072A8 +:1006C00010920E7210920C7210920D7210921372A0 +:1006D00008958134910510F080E490E080931172C8 +:1006E0009093127210920F721092107208951092DD +:1006F0000E720895DDCF8091117290911272892B44 +:10070000F1F020910F7230911072809111729091DE +:1007100012722817390798F0109211721092127203 +:1007200010920F721092107210922C7110922D7103 +:1007300080910B0C82FDFCCF82E08093480C80916D +:100740000E72811120C080910C7290910D72892BD4 +:10075000D1F08091427181FF16C080910C7290910E +:100760000D72809344719093457180910B0C82FDC2 +:10077000FCCF82E080934E0C81E080930E72109249 +:100780000C7210920D72089520910F7230911072B8 +:1007900080911172909112722817390788F4809114 +:1007A0000F72909110729C012F5F3F4F20930F7238 +:1007B000309310728E5A9F48FC01808190E008951A +:1007C0008FEF9FEF0895CF93C82F80910C72909177 +:1007D0000D728034910598F480910C7290910D7295 +:1007E0009C012F5F3F4F20930C7230930D728E5EF1 +:1007F0009F48FC01C083CF9108957DDFD4DD8091B7 +:100800000E728111FACF80910C7290910D7280342A +:10081000910598F7E1CF80910C7290910D72892B20 +:1008200089F480910E7281110DC0089564DFBBDDE3 +:1008300080910E72811106C080910C7290910D72A0 +:10084000892B01F180910E728111F0CF80910C7291 +:1008500090910D72892B51F380910C7290910D72D1 +:10086000809344719093457180910B0C82FDFCCF75 +:1008700082E080934E0C81E080930E7210920C7295 +:1008800010920D72089584E19EDF80E19CCF10925A +:1008900032741092337410922F741092307410923C +:1008A0002E7410921974109217741092187410927A +:1008B00016741092147210921572EAE1F4E784E152 +:1008C000DF011D928A95E9F740E052E06FEF70E09A +:1008D00086E192E7DEC23F924F925F926F927F92E3 +:1008E0008F929F92AF92BF92CF92DF92EF92FF9240 +:1008F0000F931F93CF93DF9300D000D0CDB7DEB717 +:1009000083E0382E92E0E92EF12C20EF922E34E095 +:10091000C32ED12C41EF842EAA24A394B12C34DF12 +:1009200097FF15C02496CDBFDEBFDF91CF911F91F9 +:100930000F91FF90EF90DF90CF90BF90AF909F907E +:100940008F907F906F905F904F903F900895482FC9 +:1009500020913274309133742230310509F477C01C +:10096000C8F42115310509F445C021303105B9F627 +:10097000E091197491E09E0F90931974F0E0E65E97 +:10098000FB48808380912E74815080932E7488233D +:10099000E9F1C5CF2430310509F465C108F432C14D +:1009A0002530310509F0BBCFE091197491E09E0F1D +:1009B00090931974F0E0E65EFB48808380912E747A +:1009C000815080932E748111AACF80911A749091D6 +:1009D0001B7498278927982780931774909318740D +:1009E00020911C74209316748130924078F4809288 +:1009F00031740CC080933174E0EDE80FE63430F4CC +:100A0000F0E0E05BF04780818E3F29F4E0923274A1 +:100A1000F092337484CF8F3F79F41092197430922E +:100A20002E7483E090E0443611F085E090E08093EE +:100A300032749093337473CF882341F310921974F6 +:100A400080932E74A0923274B092337468CF803247 +:100A500009F069C080913174803F31F5809116743E +:100A60008F7D863499F420911774309118746091B9 +:100A70002F747091307490E080E046E152E716D117 +:100A800002DF109232741092337449CF8534C1F76B +:100A9000409117745091187466E172E780912F7439 +:100AA00090913074A8D1ECCF813F81F584E18BDE49 +:100AB00040902F7450903074712C612C10E000E045 +:100AC00080911774909118740817190718F080E135 +:100AD0007ADED7CF809116748F7D863459F4C301A6 +:100AE000B201600F711F811D911D68D16CDE0F5F17 +:100AF0001F4FE6CF853441F480912F74909130746C +:100B0000800F911FB6D1F2CF8FEFF0CF853409F46B +:100B1000B7CF90F4803409F4B3CF38F4803309F4BC +:100B2000AFCF8133A9F185E1D3CF813409F43EC041 +:100B3000823409F4A5CFF7CF823509F4A1CFC0F4F0 +:100B4000803509F49DCF813571F784E13CDE80E189 +:100B50003ADE61DE19821A821B821C8289819A81A7 +:100B6000AB81BC8180349D40A340B10580F1C5DAE2 +:100B70008635C9F1853709F43AC08535A1F68091EB +:100B80001A7490911B7480932F749093307478CF63 +:100B900084E119DE06E91FE8FFE80D391F0709F4B3 +:100BA00096CFF80181918F010EDEF6CF80911A74F5 +:100BB00011E0803839F018E0813821F012E08238F5 +:100BC00009F013E084E1FFDD812FFDDD80CF898115 +:100BD0009A81AB81BC810196A11DB11D89839A8345 +:100BE000AB83BC83BBCF84E1EEDD80E0EECF84E15C +:100BF000EADD80918010E7DD80918110E4DD809155 +:100C00008210E3CFE091197491E09E0F90931974D4 +:100C1000F0E0E65EFB48808380912E7481508093E3 +:100C20002E7481117CCE80911A7490911B74982738 +:100C300089279827809317749093187420911C74B7 +:100C4000209316748130E2E09E0708F0DFCE109208 +:100C5000147210921572892B09F4D8CEC092327496 +:100C6000D09233745CCE2091147230911572A90128 +:100C70004F5F5F4F40931472509315722A5E3D4848 +:100C8000F90180838091177490911874481759075F +:100C900008F445CE90923174B9CE9091061091FD32 +:100CA000FCCF9DE994BF809300100895CF92DF920E +:100CB000EF92FF920F931F93CF93DF936115E0E1C3 +:100CC0007E078105910508F470C02115310509F4EE +:100CD0006CC089012130324010F000E012E0209118 +:100CE0000110E1E0F0E0611530E87307810591053E +:100CF00010F4F0E0E0E07A016F0184E0CC0CDD1C40 +:100D00008A95E1F72F7C2C2938ED34BF2093011010 +:100D1000EB01D06888E0C1DF8FEF888380910610F7 +:100D200081FDFCCF81E0B9DF809101108F7C8C299F +:100D300098ED94BF8093011082E0AFDFF70181E06E +:100D400090E0AE014E195F09DA01AE0FBF1F8017A8 +:100D50009107E8F0A8014E7FC40FD51F4017510737 +:100D600060F4E40EF51EF701808190E09F6F8883A8 +:100D700099838091061081FDFCCF81E0DF91CF91B6 +:100D80001F910F91FF90EF90DF90CF9086CF218140 +:100D900030812327322723272D933C93209106105F +:100DA00021FDFCCF02963296CFCFDF91CF911F91DC +:100DB0000F91FF90EF90DF90CF9008954091011038 +:100DC00021E030E0611550E875078105910510F4C8 +:100DD00030E020E084E0220F331F8A95E1F7342FC2 +:100DE0003F7C322B28ED24BF30930110AB015068BB +:100DF000FA01808108950F931F938F3F910509F0A9 +:100E000058F58A0130E020E0080F191F211D311D1F +:100E10000130114021053105E0F040E051E0481B70 +:100E2000590B209106102370E1F72DE924BF23E12F +:100E300020930010460F571FFB01861B970B9C5EEB +:100E4000E417F50761F48DE984BF81E08093001019 +:100E500003C04115510529F71F910F910895DC0139 +:100E6000AE0FBF1F21912C932091061020FDFCCFC7 +:100E7000E7CF8F3F910509F020F49C5EFC01808153 +:100E800008958FEF0895EE0FFF1F0590F491E02D68 +:100E90000994DC0101C06D9341505040E0F7089582 +:040EA000F894FFCFF4 +:100EA40016160342004C0030003000300030003091 +:100EB400003000300031002A2A034100560052005D +:100EC4004400550020004300440043002000420039 +:100ED4006F006F0074006C006F00610064006500B7 +:100EE40072001010034100560052004400550042A5 +:100EF400004C00040309040902430002010080328B +:100F04000904000001020201000524001001052467 +:100F140001000104240202052406000107058103DF +:100F240010004009040100020A0000000705020243 +:100F34004000000705830240000012010002020085 +:100F44000040091201000001010203010000FEFE3D +:100F5400FEFEFEFEFEFEFEFEFEFEFEFE020114FE90 +:100F6400FE05FEFEFEFEFEFEFEFEFEFE000000FE90 +:100F7400FE0204FEFEFEFEFEFEFEFEFEFEFEFEFE83 +:100F8400FFFEFEFEFEFEFEFEFEFEFEFEFEFEFEFE7C +:0A0F9400FF004156522053544B0059 +:080F9E0000C201000000080080 +:040FFA00F8950895C9 +:020FFE00011AD6 +:00000001FF diff --git a/megaavr/bootloaders/hex/usbcdcboot_32du14_novreg.hex b/megaavr/bootloaders/hex/usbcdcboot_32du14_novreg.hex new file mode 100644 index 00000000..5ea2b818 --- /dev/null +++ b/megaavr/bootloaders/hex/usbcdcboot_32du14_novreg.hex @@ -0,0 +1,255 @@ +:1000000043C000005CC000005AC0000058C000009F +:1000100056C0000054C0000052C0000050C0000094 +:100020004EC000004CC000004AC0000048C00000A4 +:1000300046C0000044C0000042C0000040C00000B4 +:100040003EC000003CC000003AC0000038C00000C4 +:1000500036C0000034C0000032C0000030C00000D4 +:100060002EC000002CC000002AC0000028C00000E4 +:1000700026C0000024C0000022C0000020C00000F4 +:100080001EC000001CC0000011241FBECFEFCDBF5A +:10009000DFE7DEBF10E7A0E0B0E7ECE9FFE002C079 +:1000A00005900D92A830B107D9F724E7A8E0B0E792 +:1000B00001C01D92A433B207E1F72BD0F0C6A0CF48 +:1000C000CF93DF9300D0CDB7DEB78AE190E0019700 +:1000D00029F40F900F90DF91CF91089519821A8221 +:1000E00029813A81283E334090F7000029813A81E6 +:1000F0002F5F3F4F29833A83F3CF80E480936504D9 +:1001000080936204AAD288ED91E084BF90930001AD +:10011000FFCF209140002CBB8FEF80934000809157 +:100120001001806490E038ED34BF8093100124FF0B +:1001300034C01092347410923574C0E4DFE18091C1 +:100140006800837C846298ED94BF8093680080E0AF +:1001500094BF809361008091650081FFFCCF4AD2FB +:10016000C8D294D3C0930870D093097080E4809370 +:100170006104809365046BD210E4D0E0C0E013D139 +:10018000B9D2A8D321968091087090910970C817B0 +:10019000D907A8F310936704F0CF22FF25C0809100 +:1001A0003474909135748D399B4639F410923474BF +:1001B00010923574C0E8DEE3C2CF8DE99BE68093F0 +:1001C00034749093357480E480936104DAE0C0E481 +:1001D000C093660475DFC093650472DFD150C1F728 +:1001E000C0936504C0936204109234741092357405 +:1001F000E0E0F0E185919491C0E0DDE7019609F43B +:100200009ECFF89480E4809365048093620480918B +:10021000100128ED8F7B90E024BF80931001109295 +:10022000000C1092010C80910110806324BF809318 +:100230000110E0E0F8E0099480910B0C82FDFCCF06 +:100240000895EAEEF0E781A1846081A381A584602E +:1002500081A710921070109211700895CF939B0196 +:100260004617570708F49A01FC01A2EAB0E72134C7 +:10027000310580F08093167190931771209314715B +:1002800030931571109218711092197183E717C08D +:1002900081918D93CD01825A904782179307C0F3C5 +:1002A00082EA90E7809316719093177120931471EE +:1002B00030931571109218711092197183E4809324 +:1002C0001371BADFCAE6C093420CB6DFC093400C8C +:1002D00081E090E08093107090931170CF91089519 +:1002E000EAEEF0E712A613A616A617A683E481A7E6 +:1002F000A3DF8AE68093420C83E090E08093107045 +:10030000909311700895EAEEF0E782E291E0DF0148 +:100310009C011D9221503040E1F783E481A322EE3D +:1003200030E724A335A310A281A782EA90E784A72F +:1003300095A782E080A791E891AF22E930E724AF4A +:1003400035AF80AF93E890932B7122E530E720938F +:100350002E7130932F7110922A719093437122E184 +:1003600030E720934671309347718093427108952E +:10037000B7CF74CFEAEEF0E784A795A712A613A62D +:1003800066A777A783E681A757DF8AE68093400CAC +:1003900082E090E0809310709093117081E08093E0 +:1003A0000D7008954ECFCF93DF9380910A0CC091CA +:1003B0000B0C84FF11C01092030CA5DF109210707B +:1003C0001092117010920E7010920F7010920D70AA +:1003D0006ED180E180930A0CC0FF17C083E4809344 +:1003E0000B718093137128DFD0E1D093400C24DF90 +:1003F000D093420C8091E270807609F1803209F44A +:10040000A9C01FDF81E080930B0CC5FFE4C0809181 +:10041000127185FFA9C010DF80E28093420C8091A9 +:100420001070909111708130910509F497C0059773 +:1004300009F4D4C0109210701092117095C0E09120 +:10044000E370EC30F0F6F0E0E95DFD4F1BC598027B +:10045000A5020102A5020102740233020102810217 +:100460008E02A702A5028091E4709091E570292F79 +:1004700033272230310561F02330310599F02130E6 +:10048000310509F0BECF62E170E08CE39FE804C063 +:1004900063E470E089EF9EE84091E8705091E97064 +:1004A000DDDEB0CF99278130910569F0C0F0823050 +:1004B000910579F0039709F0A4CF6091A28E70E0C6 +:1004C00083EA9EE8E9CF6091E48E70E085EE9EE8D5 +:1004D000E3CF6091B98E70E08AEB9EE8DDCF64E0F7 +:1004E00070E085EF9EE8D8CF8091E4708F7780939D +:1004F0000F70F6DE85E090E080931070909311709D +:1005000081CF80910E7080930A704091E870509175 +:10051000E97061E070E08AE090E7C2CF8091E4701A +:1005200080930E70DDDE88DE82E08093480C6ACF17 +:1005300010920B7010920C704091E8705091E9701D +:1005400062E070E08BE090E7ABCFCADE5BCF109249 +:100550000A70DBCF82EE90E786D054CF84E090E043 +:10056000809310709093117080910A7185FF12C072 +:1005700063DE80E28093400C809110709091117046 +:10058000029799F580910D70882319F010920D70E3 +:1005900088D0A6DE8091227185FF04C04DDE80E206 +:1005A0008093460C80912A7185FF09C045DE80E268 +:1005B0008093480C80912C7190912D7189D08091FD +:1005C000427185FF05C038DE80E280934E0C8ED0EC +:1005D00080E280930B0CDF91CF91089580910F7092 +:1005E0008093030C10920F7025CF10921070109210 +:1005F0001170D0CF809168008C7B826498ED94BF9D +:10060000809368001092060F8FE697E10197F1F74B +:1006100000C0000078DE8AE091E78093060C90939A +:10062000070C1092080C1092090C83E88093000CC0 +:1006300080E492E4AFE0B0E02091850020FD04C0AA +:100640000197A109B109C1F71092010C0895E0E0EA +:10065000FCE08181816081830895E0E0FCE081819C +:100660008E7F81830895FC019181913299F018F475 +:10067000903221F097CE9432E8F77ACE8681978136 +:100680000797C1F781E08093137267E070E080E024 +:1006900090E770CE4681578167E070E080E090E798 +:1006A00068CE809113728111109213720895109286 +:1006B00011721092127210920F72109210721092A8 +:1006C0000E7210920C7210920D72109213720895A5 +:1006D0008134910510F080E490E080931172909342 +:1006E000127210920F7210921072089510920E7280 +:1006F0000895DDCF8091117290911272892BF1F0E3 +:1007000020910F723091107280911172909112723B +:100710002817390798F010921172109212721092E5 +:100720000F721092107210922C7110922D71809194 +:100730000B0C82FDFCCF82E08093480C80910E72FE +:10074000811120C080910C7290910D72892BD1F093 +:100750008091427181FF16C080910C7290910D7250 +:10076000809344719093457180910B0C82FDFCCF76 +:1007700082E080934E0C81E080930E7210920C7296 +:1007800010920D72089520910F7230911072809125 +:100790001172909112722817390788F480910F72A4 +:1007A000909110729C012F5F3F4F20930F723093F6 +:1007B00010728E5A9F48FC01808190E008958FEF5F +:1007C0009FEF0895CF93C82F80910C7290910D7276 +:1007D0008034910598F480910C7290910D729C0177 +:1007E0002F5F3F4F20930C7230930D728E5E9F48A7 +:1007F000FC01C083CF9108957DDFD5DD80910E721D +:100800008111FACF80910C7290910D728034910514 +:1008100098F7E1CF80910C7290910D72892B89F439 +:1008200080910E7281110DC0089564DFBCDD80914E +:100830000E72811106C080910C7290910D72892BFD +:1008400001F180910E728111F0CF80910C72909124 +:100850000D72892B51F380910C7290910D728093DF +:1008600044719093457180910B0C82FDFCCF82E026 +:1008700080934E0C81E080930E7210920C72109255 +:100880000D72089584E19EDF80E19CCF1092327456 +:100890001092337410922F741092307410922E7440 +:1008A0001092197410921774109218741092167492 +:1008B0001092147210921572EAE1F4E784E1DF01FC +:1008C0001D928A95E9F740E052E06FEF70E086E113 +:1008D00092E7DEC23F924F925F926F927F928F9229 +:1008E0009F92AF92BF92CF92DF92EF92FF920F93BF +:1008F0001F93CF93DF9300D000D0CDB7DEB783E056 +:10090000382E92E0E92EF12C20EF922E34E0C32E07 +:10091000D12C41EF842EAA24A394B12C34DF97FF6D +:1009200015C02496CDBFDEBFDF91CF911F910F91EF +:10093000FF90EF90DF90CF90BF90AF909F908F90FF +:100940007F906F905F904F903F900895482F209137 +:100950003274309133742230310509F477C0C8F411 +:100960002115310509F445C021303105B9F6E09172 +:10097000197491E09E0F90931974F0E0E65EFB48C5 +:10098000808380912E74815080932E748823E9F1A6 +:10099000C5CF2430310509F465C108F432C12530D2 +:1009A000310509F0BBCFE091197491E09E0F90934F +:1009B0001974F0E0E65EFB48808380912E748150CC +:1009C00080932E748111AACF80911A7490911B7418 +:1009D00098278927982780931774909318742091EB +:1009E0001C74209316748130924078F48092317494 +:1009F0000CC080933174E0EDE80FE63430F4F0E0A1 +:100A0000E25BF04780818E3F29F4E0923274F092ED +:100A1000337484CF8F3F79F41092197430922E740E +:100A200083E090E0443611F085E090E080933274EA +:100A30009093337473CF882341F310921974809389 +:100A40002E74A0923274B092337468CF803209F061 +:100A500069C080913174803F31F5809116748F7D2B +:100A6000863499F4209117743091187460912F7422 +:100A70007091307490E080E046E152E716D102DFD9 +:100A8000109232741092337449CF8534C1F740917B +:100A900017745091187466E172E780912F749091E9 +:100AA0003074A8D1ECCF813F81F584E18BDE40909A +:100AB0002F7450903074712C612C10E000E0809104 +:100AC0001774909118740817190718F080E17ADEEE +:100AD000D7CF809116748F7D863459F4C301B2014B +:100AE000600F711F811D911D68D16CDE0F5F1F4F5C +:100AF000E6CF853441F480912F7490913074800F4B +:100B0000911FB6D1F2CF8FEFF0CF853409F4B7CF74 +:100B100090F4803409F4B3CF38F4803309F4AFCFC4 +:100B20008133A9F185E1D3CF813409F43EC0823409 +:100B300009F4A5CFF7CF823509F4A1CFC0F48035F1 +:100B400009F49DCF813571F784E13CDE80E13ADE26 +:100B500061DE19821A821B821C8289819A81AB8193 +:100B6000BC8180349D40A340B10580F1C6DA863552 +:100B7000C9F1853709F43AC08535A1F680911A7418 +:100B800090911B7480932F749093307478CF84E18C +:100B900019DE04E91FE8FFE80B391F0709F496CFB7 +:100BA000F80181918F010EDEF6CF80911A7411E069 +:100BB000803839F018E0813821F012E0823809F0ED +:100BC00013E084E1FFDD812FFDDD80CF89819A81F3 +:100BD000AB81BC810196A11DB11D89839A83AB8332 +:100BE000BC83BBCF84E1EEDD80E0EECF84E1EADDC3 +:100BF00080918010E7DD80918110E4DD809182108A +:100C0000E3CFE091197491E09E0F90931974F0E096 +:100C1000E65EFB48808380912E74815080932E7411 +:100C200081117CCE80911A7490911B74982789272A +:100C30009827809317749093187420911C742093B4 +:100C400016748130E2E09E0708F0DFCE1092147235 +:100C500010921572892B09F4D8CEC0923274D092BA +:100C600033745CCE2091147230911572A9014F5FDC +:100C70005F4F40931472509315722A5E3D48F901FC +:100C8000808380911774909118744817590708F45D +:100C900045CE90923174B9CE9091061091FDFCCF63 +:100CA0009DE994BF809300100895CF92DF92EF9258 +:100CB000FF920F931F93CF93DF936115E0E17E07BF +:100CC0008105910508F470C02115310509F46CC047 +:100CD00089012130324010F000E012E02091011033 +:100CE000E1E0F0E0611530E873078105910510F44B +:100CF000F0E0E0E07A016F0184E0CC0CDD1C8A9525 +:100D0000E1F72F7C2C2938ED34BF20930110EB0143 +:100D1000D06888E0C1DF8FEF88838091061081FD65 +:100D2000FCCF81E0B9DF809101108F7C8C2998ED98 +:100D300094BF8093011082E0AFDFF70181E090E083 +:100D4000AE014E195F09DA01AE0FBF1F8017910780 +:100D5000E8F0A8014E7FC40FD51F4017510760F47B +:100D6000E40EF51EF701808190E09F6F88839983E0 +:100D70008091061081FDFCCF81E0DF91CF911F9122 +:100D80000F91FF90EF90DF90CF9086CF218130813F +:100D90002327322723272D933C932091061021FDF2 +:100DA000FCCF02963296CFCFDF91CF911F910F915A +:100DB000FF90EF90DF90CF9008954091011021E0D7 +:100DC00030E0611550E875078105910510F430E0B9 +:100DD00020E084E0220F331F8A95E1F7342F3F7C17 +:100DE000322B28ED24BF30930110AB015068FA017B +:100DF000808108950F931F938F3F910509F058F557 +:100E00008A0130E020E0080F191F211D311D01303B +:100E1000114021053105E0F040E051E0481B590B3D +:100E2000209106102370E1F72DE924BF23E12093E0 +:100E30000010460F571FFB01861B970B9C5EE417A3 +:100E4000F50761F48DE984BF81E08093001003C051 +:100E50004115510529F71F910F910895DC01AE0F3F +:100E6000BF1F21912C932091061020FDFCCFE7CFCE +:100E70008F3F910509F020F49C5EFC01808108956C +:100E80008FEF0895EE0FFF1F0590F491E02D099468 +:100E9000DC0101C06D9341505040E0F70895F89493 +:020EA000FFCF82 +:100EA20016160342004C0030003000300030003093 +:100EB200003000300031002A2A034100560052005F +:100EC200440055002000430044004300200042003B +:100ED2006F006F0074006C006F00610064006500B9 +:100EE20072001010034100560052004400550042A7 +:100EF200004C00040309040902430002010080328D +:100F02000904000001020201000524001001052469 +:100F120001000104240202052406000107058103E1 +:100F220010004009040100020A0000000705020245 +:100F32004000000705830240000012010002020087 +:100F42000040091201000001010203010000FEFE3F +:100F5200FEFEFEFEFEFEFEFEFEFEFEFE020114FE92 +:100F6200FE05FEFEFEFEFEFEFEFEFEFE000000FE92 +:100F7200FE0204FEFEFEFEFEFEFEFEFEFEFEFEFE85 +:100F8200FFFEFEFEFEFEFEFEFEFEFEFEFEFEFEFE7E +:0A0F9200FF004156522053544B005B +:080F9C0000C201000000080082 +:040FFA00F8950895C9 +:020FFE00011AD6 +:00000001FF diff --git a/megaavr/bootloaders/hex/usbcdcboot_32du_novreg.hex b/megaavr/bootloaders/hex/usbcdcboot_32du_novreg.hex new file mode 100644 index 00000000..f6b5e895 --- /dev/null +++ b/megaavr/bootloaders/hex/usbcdcboot_32du_novreg.hex @@ -0,0 +1,255 @@ +:1000000043C000005CC000005AC0000058C000009F +:1000100056C0000054C0000052C0000050C0000094 +:100020004EC000004CC000004AC0000048C00000A4 +:1000300046C0000044C0000042C0000040C00000B4 +:100040003EC000003CC000003AC0000038C00000C4 +:1000500036C0000034C0000032C0000030C00000D4 +:100060002EC000002CC000002AC0000028C00000E4 +:1000700026C0000024C0000022C0000020C00000F4 +:100080001EC000001CC0000011241FBECFEFCDBF5A +:10009000DFE7DEBF10E7A0E0B0E7ECE9FFE002C079 +:1000A00005900D92A830B107D9F724E7A8E0B0E792 +:1000B00001C01D92A433B207E1F72BD0F0C6A0CF48 +:1000C000CF93DF9300D0CDB7DEB78AE190E0019700 +:1000D00029F40F900F90DF91CF91089519821A8221 +:1000E00029813A81283E334090F7000029813A81E6 +:1000F0002F5F3F4F29833A83F3CF80E88093050435 +:1001000080930204AAD288ED91E084BF909300010D +:10011000FFCF209140002CBB8FEF80934000809157 +:100120001001806490E038ED34BF8093100124FF0B +:1001300034C01092347410923574C0E4DFE18091C1 +:100140006800837C846298ED94BF8093680080E0AF +:1001500094BF809361008091650081FFFCCF4AD2FB +:10016000C8D294D3C0930870D093097080E880936C +:100170000104809305046BD210E8D0E0C0E013D1F5 +:10018000B9D2A8D321968091087090910970C817B0 +:10019000D907A8F310930704F0CF22FF25C0809160 +:1001A0003474909135748D399B4639F410923474BF +:1001B00010923574C0E8DEE3C2CF8DE99BE68093F0 +:1001C00034749093357480E880930104DAE0C0E8D9 +:1001D000C093060475DFC093050472DFD150C1F7E8 +:1001E000C0930504C09302041092347410923574C5 +:1001F000E0E0F0E185919491C0E0DDE7019609F43B +:100200009ECFF89480E88093050480930204809147 +:10021000100128ED8F7B90E024BF80931001109295 +:10022000000C1092010C80910110806324BF809318 +:100230000110E0E0F8E0099480910B0C82FDFCCF06 +:100240000895EAEEF0E781A1846081A381A584602E +:1002500081A710921070109211700895CF939B0196 +:100260004617570708F49A01FC01A2EAB0E72134C7 +:10027000310580F08093167190931771209314715B +:1002800030931571109218711092197183E717C08D +:1002900081918D93CD01825A904782179307C0F3C5 +:1002A00082EA90E7809316719093177120931471EE +:1002B00030931571109218711092197183E4809324 +:1002C0001371BADFCAE6C093420CB6DFC093400C8C +:1002D00081E090E08093107090931170CF91089519 +:1002E000EAEEF0E712A613A616A617A683E481A7E6 +:1002F000A3DF8AE68093420C83E090E08093107045 +:10030000909311700895EAEEF0E782E291E0DF0148 +:100310009C011D9221503040E1F783E481A322EE3D +:1003200030E724A335A310A281A782EA90E784A72F +:1003300095A782E080A791E891AF22E930E724AF4A +:1003400035AF80AF93E890932B7122E530E720938F +:100350002E7130932F7110922A719093437122E184 +:1003600030E720934671309347718093427108952E +:10037000B7CF74CFEAEEF0E784A795A712A613A62D +:1003800066A777A783E681A757DF8AE68093400CAC +:1003900082E090E0809310709093117081E08093E0 +:1003A0000D7008954ECFCF93DF9380910A0CC091CA +:1003B0000B0C84FF11C01092030CA5DF109210707B +:1003C0001092117010920E7010920F7010920D70AA +:1003D0006ED180E180930A0CC0FF17C083E4809344 +:1003E0000B718093137128DFD0E1D093400C24DF90 +:1003F000D093420C8091E270807609F1803209F44A +:10040000A9C01FDF81E080930B0CC5FFE4C0809181 +:10041000127185FFA9C010DF80E28093420C8091A9 +:100420001070909111708130910509F497C0059773 +:1004300009F4D4C0109210701092117095C0E09120 +:10044000E370EC30F0F6F0E0E95DFD4F1BC598027B +:10045000A5020102A5020102740233020102810217 +:100460008E02A702A5028091E4709091E570292F79 +:1004700033272230310561F02330310599F02130E6 +:10048000310509F0BECF62E170E08CE39FE804C063 +:1004900063E470E089EF9EE84091E8705091E97064 +:1004A000DDDEB0CF99278130910569F0C0F0823050 +:1004B000910579F0039709F0A4CF6091A28E70E0C6 +:1004C00083EA9EE8E9CF6091E48E70E085EE9EE8D5 +:1004D000E3CF6091B98E70E08AEB9EE8DDCF64E0F7 +:1004E00070E085EF9EE8D8CF8091E4708F7780939D +:1004F0000F70F6DE85E090E080931070909311709D +:1005000081CF80910E7080930A704091E870509175 +:10051000E97061E070E08AE090E7C2CF8091E4701A +:1005200080930E70DDDE88DE82E08093480C6ACF17 +:1005300010920B7010920C704091E8705091E9701D +:1005400062E070E08BE090E7ABCFCADE5BCF109249 +:100550000A70DBCF82EE90E786D054CF84E090E043 +:10056000809310709093117080910A7185FF12C072 +:1005700063DE80E28093400C809110709091117046 +:10058000029799F580910D70882319F010920D70E3 +:1005900088D0A6DE8091227185FF04C04DDE80E206 +:1005A0008093460C80912A7185FF09C045DE80E268 +:1005B0008093480C80912C7190912D7189D08091FD +:1005C000427185FF05C038DE80E280934E0C8ED0EC +:1005D00080E280930B0CDF91CF91089580910F7092 +:1005E0008093030C10920F7025CF10921070109210 +:1005F0001170D0CF809168008C7B826498ED94BF9D +:10060000809368001092060F8FE697E10197F1F74B +:1006100000C0000078DE8AE091E78093060C90939A +:10062000070C1092080C1092090C83E88093000CC0 +:1006300080E492E4AFE0B0E02091850020FD04C0AA +:100640000197A109B109C1F71092010C0895E0E0EA +:10065000FCE08181816081830895E0E0FCE081819C +:100660008E7F81830895FC019181913299F018F475 +:10067000903221F097CE9432E8F77ACE8681978136 +:100680000797C1F781E08093137267E070E080E024 +:1006900090E770CE4681578167E070E080E090E798 +:1006A00068CE809113728111109213720895109286 +:1006B00011721092127210920F72109210721092A8 +:1006C0000E7210920C7210920D72109213720895A5 +:1006D0008134910510F080E490E080931172909342 +:1006E000127210920F7210921072089510920E7280 +:1006F0000895DDCF8091117290911272892BF1F0E3 +:1007000020910F723091107280911172909112723B +:100710002817390798F010921172109212721092E5 +:100720000F721092107210922C7110922D71809194 +:100730000B0C82FDFCCF82E08093480C80910E72FE +:10074000811120C080910C7290910D72892BD1F093 +:100750008091427181FF16C080910C7290910D7250 +:10076000809344719093457180910B0C82FDFCCF76 +:1007700082E080934E0C81E080930E7210920C7296 +:1007800010920D72089520910F7230911072809125 +:100790001172909112722817390788F480910F72A4 +:1007A000909110729C012F5F3F4F20930F723093F6 +:1007B00010728E5A9F48FC01808190E008958FEF5F +:1007C0009FEF0895CF93C82F80910C7290910D7276 +:1007D0008034910598F480910C7290910D729C0177 +:1007E0002F5F3F4F20930C7230930D728E5E9F48A7 +:1007F000FC01C083CF9108957DDFD5DD80910E721D +:100800008111FACF80910C7290910D728034910514 +:1008100098F7E1CF80910C7290910D72892B89F439 +:1008200080910E7281110DC0089564DFBCDD80914E +:100830000E72811106C080910C7290910D72892BFD +:1008400001F180910E728111F0CF80910C72909124 +:100850000D72892B51F380910C7290910D728093DF +:1008600044719093457180910B0C82FDFCCF82E026 +:1008700080934E0C81E080930E7210920C72109255 +:100880000D72089584E19EDF80E19CCF1092327456 +:100890001092337410922F741092307410922E7440 +:1008A0001092197410921774109218741092167492 +:1008B0001092147210921572EAE1F4E784E1DF01FC +:1008C0001D928A95E9F740E052E06FEF70E086E113 +:1008D00092E7DEC23F924F925F926F927F928F9229 +:1008E0009F92AF92BF92CF92DF92EF92FF920F93BF +:1008F0001F93CF93DF9300D000D0CDB7DEB783E056 +:10090000382E92E0E92EF12C20EF922E34E0C32E07 +:10091000D12C41EF842EAA24A394B12C34DF97FF6D +:1009200015C02496CDBFDEBFDF91CF911F910F91EF +:10093000FF90EF90DF90CF90BF90AF909F908F90FF +:100940007F906F905F904F903F900895482F209137 +:100950003274309133742230310509F477C0C8F411 +:100960002115310509F445C021303105B9F6E09172 +:10097000197491E09E0F90931974F0E0E65EFB48C5 +:10098000808380912E74815080932E748823E9F1A6 +:10099000C5CF2430310509F465C108F432C12530D2 +:1009A000310509F0BBCFE091197491E09E0F90934F +:1009B0001974F0E0E65EFB48808380912E748150CC +:1009C00080932E748111AACF80911A7490911B7418 +:1009D00098278927982780931774909318742091EB +:1009E0001C74209316748130924078F48092317494 +:1009F0000CC080933174E0EDE80FE63430F4F0E0A1 +:100A0000E25BF04780818E3F29F4E0923274F092ED +:100A1000337484CF8F3F79F41092197430922E740E +:100A200083E090E0443611F085E090E080933274EA +:100A30009093337473CF882341F310921974809389 +:100A40002E74A0923274B092337468CF803209F061 +:100A500069C080913174803F31F5809116748F7D2B +:100A6000863499F4209117743091187460912F7422 +:100A70007091307490E080E046E152E716D102DFD9 +:100A8000109232741092337449CF8534C1F740917B +:100A900017745091187466E172E780912F749091E9 +:100AA0003074A8D1ECCF813F81F584E18BDE40909A +:100AB0002F7450903074712C612C10E000E0809104 +:100AC0001774909118740817190718F080E17ADEEE +:100AD000D7CF809116748F7D863459F4C301B2014B +:100AE000600F711F811D911D68D16CDE0F5F1F4F5C +:100AF000E6CF853441F480912F7490913074800F4B +:100B0000911FB6D1F2CF8FEFF0CF853409F4B7CF74 +:100B100090F4803409F4B3CF38F4803309F4AFCFC4 +:100B20008133A9F185E1D3CF813409F43EC0823409 +:100B300009F4A5CFF7CF823509F4A1CFC0F48035F1 +:100B400009F49DCF813571F784E13CDE80E13ADE26 +:100B500061DE19821A821B821C8289819A81AB8193 +:100B6000BC8180349D40A340B10580F1C6DA863552 +:100B7000C9F1853709F43AC08535A1F680911A7418 +:100B800090911B7480932F749093307478CF84E18C +:100B900019DE04E91FE8FFE80B391F0709F496CFB7 +:100BA000F80181918F010EDEF6CF80911A7411E069 +:100BB000803839F018E0813821F012E0823809F0ED +:100BC00013E084E1FFDD812FFDDD80CF89819A81F3 +:100BD000AB81BC810196A11DB11D89839A83AB8332 +:100BE000BC83BBCF84E1EEDD80E0EECF84E1EADDC3 +:100BF00080918010E7DD80918110E4DD809182108A +:100C0000E3CFE091197491E09E0F90931974F0E096 +:100C1000E65EFB48808380912E74815080932E7411 +:100C200081117CCE80911A7490911B74982789272A +:100C30009827809317749093187420911C742093B4 +:100C400016748130E2E09E0708F0DFCE1092147235 +:100C500010921572892B09F4D8CEC0923274D092BA +:100C600033745CCE2091147230911572A9014F5FDC +:100C70005F4F40931472509315722A5E3D48F901FC +:100C8000808380911774909118744817590708F45D +:100C900045CE90923174B9CE9091061091FDFCCF63 +:100CA0009DE994BF809300100895CF92DF92EF9258 +:100CB000FF920F931F93CF93DF936115E0E17E07BF +:100CC0008105910508F470C02115310509F46CC047 +:100CD00089012130324010F000E012E02091011033 +:100CE000E1E0F0E0611530E873078105910510F44B +:100CF000F0E0E0E07A016F0184E0CC0CDD1C8A9525 +:100D0000E1F72F7C2C2938ED34BF20930110EB0143 +:100D1000D06888E0C1DF8FEF88838091061081FD65 +:100D2000FCCF81E0B9DF809101108F7C8C2998ED98 +:100D300094BF8093011082E0AFDFF70181E090E083 +:100D4000AE014E195F09DA01AE0FBF1F8017910780 +:100D5000E8F0A8014E7FC40FD51F4017510760F47B +:100D6000E40EF51EF701808190E09F6F88839983E0 +:100D70008091061081FDFCCF81E0DF91CF911F9122 +:100D80000F91FF90EF90DF90CF9086CF218130813F +:100D90002327322723272D933C932091061021FDF2 +:100DA000FCCF02963296CFCFDF91CF911F910F915A +:100DB000FF90EF90DF90CF9008954091011021E0D7 +:100DC00030E0611550E875078105910510F430E0B9 +:100DD00020E084E0220F331F8A95E1F7342F3F7C17 +:100DE000322B28ED24BF30930110AB015068FA017B +:100DF000808108950F931F938F3F910509F058F557 +:100E00008A0130E020E0080F191F211D311D01303B +:100E1000114021053105E0F040E051E0481B590B3D +:100E2000209106102370E1F72DE924BF23E12093E0 +:100E30000010460F571FFB01861B970B9C5EE417A3 +:100E4000F50761F48DE984BF81E08093001003C051 +:100E50004115510529F71F910F910895DC01AE0F3F +:100E6000BF1F21912C932091061020FDFCCFE7CFCE +:100E70008F3F910509F020F49C5EFC01808108956C +:100E80008FEF0895EE0FFF1F0590F491E02D099468 +:100E9000DC0101C06D9341505040E0F70895F89493 +:020EA000FFCF82 +:100EA20016160342004C0030003000300030003093 +:100EB200003000300031002A2A034100560052005F +:100EC200440055002000430044004300200042003B +:100ED2006F006F0074006C006F00610064006500B9 +:100EE20072001010034100560052004400550042A7 +:100EF200004C00040309040902430002010080328D +:100F02000904000001020201000524001001052469 +:100F120001000104240202052406000107058103E1 +:100F220010004009040100020A0000000705020245 +:100F32004000000705830240000012010002020087 +:100F42000040091201000001010203010000FEFE3F +:100F5200FEFEFEFEFEFEFEFEFEFEFEFE020114FE92 +:100F6200FE05FEFEFEFEFEFEFEFEFEFE000000FE92 +:100F7200FE0204FEFEFEFEFEFEFEFEFEFEFEFEFE85 +:100F8200FFFEFEFEFEFEFEFEFEFEFEFEFEFEFEFE7E +:0A0F9200FF004156522053544B005B +:080F9C0000C201000000080082 +:040FFA00F8950895C9 +:020FFE00011AD6 +:00000001FF diff --git a/megaavr/bootloaders/hex/usbcdcboot_64du.hex b/megaavr/bootloaders/hex/usbcdcboot_64du.hex new file mode 100644 index 00000000..bec9987a --- /dev/null +++ b/megaavr/bootloaders/hex/usbcdcboot_64du.hex @@ -0,0 +1,255 @@ +:1000000043C000005CC000005AC0000058C000009F +:1000100056C0000054C0000052C0000050C0000094 +:100020004EC000004CC000004AC0000048C00000A4 +:1000300046C0000044C0000042C0000040C00000B4 +:100040003EC000003CC000003AC0000038C00000C4 +:1000500036C0000034C0000032C0000030C00000D4 +:100060002EC000002CC000002AC0000028C00000E4 +:1000700026C0000024C0000022C0000020C00000F4 +:100080001EC000001CC0000011241FBECFEFCDBF5A +:10009000DFE7DEBF11E6A0E0B0E6E4EAFEE002C082 +:1000A00005900D92A230B107D9F725E6A2E0B1E69E +:1000B00001C01D92AE32B207E1F72BD0F1C6A0CF3E +:1000C000CF93DF9300D0CDB7DEB78AE190E0019700 +:1000D00029F40F900F90DF91CF91089519821A8221 +:1000E00029813A81283E334090F7000029813A81E6 +:1000F0002F5F3F4F29833A83F3CF80E88093050435 +:1001000080930204ABD288ED91E084BF909300010C +:10011000FFCF209140002CBB8FEF80934000809157 +:100120001001806490E038ED34BF8093100124FF0B +:1001300034C010922E6510922F65C0E4DFE18091EB +:100140006800837C846298ED94BF8093680080E0AF +:1001500094BF809361008091650081FFFCCF4AD2FB +:10016000C9D295D3C0930261D093036180E8809394 +:100170000104809305046CD210E8D0E0C0E013D1F4 +:10018000BAD2A9D321968091026190910361C817D8 +:10019000D907A8F310930704F0CF22FF25C0809160 +:1001A0002E6590912F658D399B4639F410922E65FE +:1001B00010922F65C0E8DEE3C2CF8DE99BE6809305 +:1001C0002E6590932F6580E880930104DAE0C0E803 +:1001D000C093060475DFC093050472DFD150C1F7E8 +:1001E000C0930504C093020410922E6510922F65EF +:1001F000E0E0F0E185919491C0E0DDE7019609F43B +:100200009ECFF89480E88093050480930204809147 +:10021000100128ED8F7B90E024BF80931001109295 +:10022000000C1092010C80910110806324BF809318 +:100230000110E0E0F8E0099480910B0C82FDFCCF06 +:100240000895E4EEF1E681A1846081A381A5846034 +:1002500081A710920A6110920B610895CF939B01C0 +:100260004617570708F49A01FC01ACE9B1E62134BE +:10027000310580F0809310629093116220930E629A +:1002800030930F62109212621092136283E717C0CC +:1002900081918D93CD018C59914682179307C0F3BC +:1002A0008CE991E6809310629093116220930E6224 +:1002B00030930F62109212621092136283E4809363 +:1002C0000D62BADFCAE6C093420CB6DFC093400CA1 +:1002D00081E090E080930A6190930B61CF91089543 +:1002E000E4EEF1E612A613A616A617A683E481A7EC +:1002F000A3DF8AE68093420C83E090E080930A615A +:1003000090930B610895E4EEF1E682E291E0DF0163 +:100310009C011D9221503040E1F783E481A32CED34 +:1003200031E624A335A310A281A78CE991E684A726 +:1003300095A782E080A791E891AF2CE831E624AF41 +:1003400035AF80AF93E8909325622CE431E620939B +:100350002862309329621092246290933D622CE0CF +:1003600031E6209340623093416280933C6208956D +:10037000B7CF74CFE4EEF1E684A795A712A613A633 +:1003800066A777A783E681A757DF8AE68093400CAC +:1003900082E090E080930A6190930B6181E080930A +:1003A000076108954ECFCF93DF9380910A0CC091DF +:1003B0000B0C84FF11C01092030CA5DF10920A6190 +:1003C00010920B61109208611092096110920761FE +:1003D0006FD180E180930A0CC0FF17C083E4809343 +:1003E000056280930D6228DFD0E1D093400C24DFBA +:1003F000D093420C8091DC61807609F1803209F45F +:10040000A9C01FDF81E080930B0CC5FFE4C0809181 +:100410000C6285FFA9C010DF80E28093420C8091BE +:100420000A6190910B618130910509F497C005979D +:1004300009F4D4C010920A6110920B6195C0E0914A +:10044000DD61EC30F0F6F0E0E95DFD4F1CC598028F +:10045000A5020102A5020102740233020102810217 +:100460008E02A702A5028091DE619091DF61292FA3 +:1004700033272230310561F02330310599F02130E6 +:10048000310509F0BECF62E170E081EA90E604C078 +:1004900063E470E08EE590E64091E2615091E361A3 +:1004A000DDDEB0CF99278130910569F0C0F0823050 +:1004B000910579F0039709F0A4CF6091076070E08F +:1004C00088E090E6E9CF6091496070E08AE490E6C8 +:1004D000E3CF60911E6070E08FE190E6DDCF64E0D5 +:1004E00070E08AE590E6D8CF8091DE618F778093C7 +:1004F0000961F6DE85E090E080930A6190930B61DC +:1005000081CF80910861809304614091E2615091B4 +:10051000E36161E070E084E091E6C2CF8091DE614A +:1005200080930861DDDE88DE82E08093480C6ACF2C +:1005300010920561109206614091E2615091E36171 +:1005400062E070E085E091E6ABCFCADE5BCF10924F +:100550000461DBCF8CED91E687D054CF84E090E04E +:1005600080930A6190930B618091046285FF12C0B1 +:1005700063DE80E28093400C80910A6190910B6170 +:10058000029799F580910761882319F0109207610D +:1005900089D0A6DE80911C6285FF04C04DDE80E21A +:1005A0008093460C8091246285FF09C045DE80E27D +:1005B0008093480C80912662909127628AD0809126 +:1005C0003C6285FF05C038DE80E280934E0C8FD000 +:1005D00080E280930B0CDF91CF91089580910961A7 +:1005E0008093030C1092096125CF10920A6110923A +:1005F0000B61D0CF809168008C7B826498ED94BFB2 +:100600008093680081E08093060F8FE697E1019761 +:10061000F1F700C0000077DE84E092E68093060CDC +:100620009093070C1092080C1092090C83E88093A9 +:10063000000C80E492E4AFE0B0E02091850020FD62 +:1006400004C00197A109B109C1F71092010C0895E6 +:10065000E0E0FCE08181816081830895E0E0FCE0DE +:1006600081818E7F81830895FC019181913299F07F +:1006700018F4903221F096CE9432E8F779CE868144 +:1006800097810797C1F781E080930D6367E070E081 +:1006900080E090E66FCE4681578167E070E080E0B1 +:1006A00090E667CE80910D63811110920D630895DD +:1006B00010920B6310920C631092096310920A63FC +:1006C00010920863109206631092076310920D63F4 +:1006D00008958134910510F080E490E080930B63DD +:1006E00090930C631092096310920A63089510921C +:1006F00008630895DDCF80910B6390910C63892B83 +:10070000F1F02091096330910A6380910B6390911D +:100710000C632817390798F010920B6310920C6342 +:100720001092096310920A63109226621092276257 +:1007300080910B0C82FDFCCF82E08093480C80916D +:100740000863811120C08091066390910763892B13 +:10075000D1F080913C6281FF16C080910663909138 +:10076000076380933E6290933F6280910B0C82FD01 +:10077000FCCF82E080934E0C81E08093086310925E +:1007800006631092076308952091096330910A630C +:1007900080910B6390910C632817390788F480913E +:1007A000096390910A639C012F5F3F4F2093096377 +:1007B00030930A63845B9E49FC01808190E0089538 +:1007C0008FEF9FEF0895CF93C82F8091066390918C +:1007D00007638034910598F48091066390910763D4 +:1007E0009C012F5F3F4F2093066330930763845F24 +:1007F0009E49FC01C083CF9108957DDFD4DD8091B7 +:1008000008638111FACF8091066390910763803469 +:10081000910598F7E1CF8091066390910763892B4A +:1008200089F48091086381110DC0089564DFBBDDF8 +:1008300080910863811106C08091066390910763DF +:10084000892B01F1809108638111F0CF80910663BB +:1008500090910763892B51F3809106639091076310 +:1008600080933E6290933F6280910B0C82FDFCCF9F +:1008700082E080934E0C81E08093086310920663BF +:1008800010920763089584E19EDF80E19CCF10926F +:100890002C6510922D651092296510922A65109290 +:1008A00028651092136510921165109212651092CE +:1008B000106510920E6310920F63E4E1F5E684E197 +:1008C000DF011D928A95E9F740E052E06FEF70E09A +:1008D00080E193E6DEC23F924F925F926F927F92E9 +:1008E0008F929F92AF92BF92CF92DF92EF92FF9240 +:1008F0000F931F93CF93DF9300D000D0CDB7DEB717 +:1009000083E0382E92E0E92EF12C20EF922E34E095 +:10091000C32ED12C41EF842EAA24A394B12C34DF12 +:1009200097FF15C02496CDBFDEBFDF91CF911F91F9 +:100930000F91FF90EF90DF90CF90BF90AF909F907E +:100940008F907F906F905F904F903F900895482FC9 +:1009500020912C6530912D652230310509F477C046 +:10096000C8F42115310509F445C021303105B9F627 +:10097000E091136591E09E0F90931365F0E0EC5EBB +:10098000FA49808380912865815080932865882367 +:10099000E9F1C5CF2430310509F465C108F432C14D +:1009A0002530310509F0BBCFE091136591E09E0F32 +:1009B00090931365F0E0EC5EFA498083809128659E +:1009C0008150809328658111AACF80911465909100 +:1009D000156598278927982780931165909312654C +:1009E00020911665209310658130924078F48092B2 +:1009F0002B650CC080932B65E0EDE80FE63430F4F6 +:100A0000F0E0ED54FF4980818E3F29F4E0922C659F +:100A1000F0922D6584CF8F3F79F410921365309258 +:100A2000286583E090E0443611F085E090E0809303 +:100A30002C6590932D6573CF882341F31092136535 +:100A400080932865A0922C65B0922D6568CF803286 +:100A500009F069C080912B65803F31F58091106568 +:100A60008F7D863499F420911165309112656091E3 +:100A7000296570912A6590E080E040E153E616D147 +:100A800002DF10922C6510922D6549CF8534C1F795 +:100A9000409111655091126560E173E6809129657E +:100AA00090912A65A8D1ECCF813F81F584E18BDE5E +:100AB0004090296550902A65712C612C10E000E06F +:100AC00080911165909112650817190718F080E15F +:100AD0007ADED7CF809110658F7D863459F4C301BB +:100AE000B201600F711F811D911D68D16CDE0F5F17 +:100AF0001F4FE6CF853441F48091296590912A6596 +:100B0000800F911FB6D1F2CF8FEFF0CF853409F46B +:100B1000B7CF90F4803409F4B3CF38F4803309F4BC +:100B2000AFCF8133A9F185E1D3CF813409F43EC041 +:100B3000823409F4A5CFF7CF823509F4A1CFC0F4F0 +:100B4000803509F49DCF813571F784E13CDE80E189 +:100B50003ADE61DE19821A821B821C8289819A81A7 +:100B6000AB81BC8180349D40A340B10580F1C5DAE2 +:100B70008635C9F1853709F43AC08535A1F68091EB +:100B80001465909115658093296590932A6578CFB7 +:100B900084E119DE09EF10E6F1E600301F0709F4E1 +:100BA00096CFF80181918F010EDEF6CF809114650A +:100BB00011E0803839F018E0813821F012E08238F5 +:100BC00009F013E084E1FFDD812FFDDD80CF898115 +:100BD0009A81AB81BC810196A11DB11D89839A8345 +:100BE000AB83BC83BBCF84E1EEDD80E0EECF84E15C +:100BF000EADD80918010E7DD80918110E4DD809155 +:100C00008210E3CFE091136591E09E0F90931365FE +:100C1000F0E0EC5EFA4980838091286581508093F2 +:100C2000286581117CCE8091146590911565982777 +:100C300089279827809311659093126520911665F6 +:100C4000209310658130E2E09E0708F0DFCE10921D +:100C50000E6310920F63892B09F4D8CEC0922C65D5 +:100C6000D0922D655CCE20910E6330910F63A90167 +:100C70004F5F5F4F40930E6350930F63205F3C497B +:100C8000F901808380911165909112654817590789 +:100C900008F445CE90922B65B9CE9091061091FD47 +:100CA000FCCF9DE994BF809300100895CF92DF920E +:100CB000EF92FF920F931F93CF93DF936115E0E1C3 +:100CC0007E078105910508F470C02115310509F4EE +:100CD0006CC089012130324010F000E012E0209118 +:100CE0000110E1E0F0E0611530E87307810591053E +:100CF00010F4F0E0E0E07A016F0184E0CC0CDD1C40 +:100D00008A95E1F72F7C2C2938ED34BF2093011010 +:100D1000EB01D06888E0C1DF8FEF888380910610F7 +:100D200081FDFCCF81E0B9DF809101108F7C8C299F +:100D300098ED94BF8093011082E0AFDFF70181E06E +:100D400090E0AE014E195F09DA01AE0FBF1F8017A8 +:100D50009107E8F0A8014E7FC40FD51F4017510737 +:100D600060F4E40EF51EF701808190E09F6F8883A8 +:100D700099838091061081FDFCCF81E0DF91CF91B6 +:100D80001F910F91FF90EF90DF90CF9086CF218140 +:100D900030812327322723272D933C93209106105F +:100DA00021FDFCCF02963296CFCFDF91CF911F91DC +:100DB0000F91FF90EF90DF90CF9008954091011038 +:100DC00021E030E0611550E875078105910510F4C8 +:100DD00030E020E084E0220F331F8A95E1F7342FC2 +:100DE0003F7C322B28ED24BF30930110AB015068BB +:100DF000FA01808108950F931F938F3F910509F0A9 +:100E000058F58A0130E020E0080F191F211D311D1F +:100E10000130114021053105E0F040E051E0481B70 +:100E2000590B209106102370E1F72DE924BF23E12F +:100E300020930010460F571FFB01861B970B9C5EEB +:100E4000E417F50761F48DE984BF81E08093001019 +:100E500003C04115510529F71F910F910895DC0139 +:100E6000AE0FBF1F21912C932091061020FDFCCFC7 +:100E7000E7CF8F3F910509F020F49C5EFC01808153 +:100E800008958FEF0895EE0FFF1F0590F491E02D68 +:100E90000994DC0101C06D9341505040E0F7089582 +:040EA000F894FFCFF4 +:100EA40000C2010000000816160342004C00300086 +:100EB40030003000300030003000300031002A2A89 +:100EC40003410056005200440055002000430044F2 +:100ED400004300200042006F006F0074006C006F3C +:100EE40000610064006500720010100341005600A8 +:100EF40052004400550042004C0004030904090256 +:100F040043000201008032090400000102020100D2 +:100F14000524001001052401000104240202052413 +:100F24000600010705810310004009040100020ABC +:100F3400000000070502024000000705830240008C +:100F44000012010002020000400912010000010128 +:100F54000203010000FEFEFEFEFEFEFEFEFEFEFE9D +:100F6400FEFEFE020114FEFE05FEFEFEFEFEFEFE79 +:100F7400FEFEFE000000FEFE0204FEFEFEFEFEFE7D +:100F8400FEFEFEFEFEFEFEFFFEFEFEFEFEFEFEFE7C +:100F9400FEFEFEFEFEFEFEFF004156522053544B61 +:020FA40000004B +:040FFA00F8950895C9 +:020FFE00011AD6 +:00000001FF diff --git a/megaavr/bootloaders/hex/usbcdcboot_64du_novreg.hex b/megaavr/bootloaders/hex/usbcdcboot_64du_novreg.hex new file mode 100644 index 00000000..217d03e7 --- /dev/null +++ b/megaavr/bootloaders/hex/usbcdcboot_64du_novreg.hex @@ -0,0 +1,255 @@ +:1000000043C000005CC000005AC0000058C000009F +:1000100056C0000054C0000052C0000050C0000094 +:100020004EC000004CC000004AC0000048C00000A4 +:1000300046C0000044C0000042C0000040C00000B4 +:100040003EC000003CC000003AC0000038C00000C4 +:1000500036C0000034C0000032C0000030C00000D4 +:100060002EC000002CC000002AC0000028C00000E4 +:1000700026C0000024C0000022C0000020C00000F4 +:100080001EC000001CC0000011241FBECFEFCDBF5A +:10009000DFE7DEBF11E6A0E0B0E6E2EAFEE002C084 +:1000A00005900D92A230B107D9F725E6A2E0B1E69E +:1000B00001C01D92AE32B207E1F72BD0F0C6A0CF3F +:1000C000CF93DF9300D0CDB7DEB78AE190E0019700 +:1000D00029F40F900F90DF91CF91089519821A8221 +:1000E00029813A81283E334090F7000029813A81E6 +:1000F0002F5F3F4F29833A83F3CF80E88093050435 +:1001000080930204AAD288ED91E084BF909300010D +:10011000FFCF209140002CBB8FEF80934000809157 +:100120001001806490E038ED34BF8093100124FF0B +:1001300034C010922E6510922F65C0E4DFE18091EB +:100140006800837C846298ED94BF8093680080E0AF +:1001500094BF809361008091650081FFFCCF4AD2FB +:10016000C8D294D3C0930261D093036180E8809396 +:100170000104809305046BD210E8D0E0C0E013D1F5 +:10018000B9D2A8D321968091026190910361C817DA +:10019000D907A8F310930704F0CF22FF25C0809160 +:1001A0002E6590912F658D399B4639F410922E65FE +:1001B00010922F65C0E8DEE3C2CF8DE99BE6809305 +:1001C0002E6590932F6580E880930104DAE0C0E803 +:1001D000C093060475DFC093050472DFD150C1F7E8 +:1001E000C0930504C093020410922E6510922F65EF +:1001F000E0E0F0E185919491C0E0DDE7019609F43B +:100200009ECFF89480E88093050480930204809147 +:10021000100128ED8F7B90E024BF80931001109295 +:10022000000C1092010C80910110806324BF809318 +:100230000110E0E0F8E0099480910B0C82FDFCCF06 +:100240000895E4EEF1E681A1846081A381A5846034 +:1002500081A710920A6110920B610895CF939B01C0 +:100260004617570708F49A01FC01ACE9B1E62134BE +:10027000310580F0809310629093116220930E629A +:1002800030930F62109212621092136283E717C0CC +:1002900081918D93CD018C59914682179307C0F3BC +:1002A0008CE991E6809310629093116220930E6224 +:1002B00030930F62109212621092136283E4809363 +:1002C0000D62BADFCAE6C093420CB6DFC093400CA1 +:1002D00081E090E080930A6190930B61CF91089543 +:1002E000E4EEF1E612A613A616A617A683E481A7EC +:1002F000A3DF8AE68093420C83E090E080930A615A +:1003000090930B610895E4EEF1E682E291E0DF0163 +:100310009C011D9221503040E1F783E481A32CED34 +:1003200031E624A335A310A281A78CE991E684A726 +:1003300095A782E080A791E891AF2CE831E624AF41 +:1003400035AF80AF93E8909325622CE431E620939B +:100350002862309329621092246290933D622CE0CF +:1003600031E6209340623093416280933C6208956D +:10037000B7CF74CFE4EEF1E684A795A712A613A633 +:1003800066A777A783E681A757DF8AE68093400CAC +:1003900082E090E080930A6190930B6181E080930A +:1003A000076108954ECFCF93DF9380910A0CC091DF +:1003B0000B0C84FF11C01092030CA5DF10920A6190 +:1003C00010920B61109208611092096110920761FE +:1003D0006ED180E180930A0CC0FF17C083E4809344 +:1003E000056280930D6228DFD0E1D093400C24DFBA +:1003F000D093420C8091DC61807609F1803209F45F +:10040000A9C01FDF81E080930B0CC5FFE4C0809181 +:100410000C6285FFA9C010DF80E28093420C8091BE +:100420000A6190910B618130910509F497C005979D +:1004300009F4D4C010920A6110920B6195C0E0914A +:10044000DD61EC30F0F6F0E0E95DFD4F1BC5980290 +:10045000A5020102A5020102740233020102810217 +:100460008E02A702A5028091DE619091DF61292FA3 +:1004700033272230310561F02330310599F02130E6 +:10048000310509F0BECF62E170E081EA90E604C078 +:1004900063E470E08EE590E64091E2615091E361A3 +:1004A000DDDEB0CF99278130910569F0C0F0823050 +:1004B000910579F0039709F0A4CF6091076070E08F +:1004C00088E090E6E9CF6091496070E08AE490E6C8 +:1004D000E3CF60911E6070E08FE190E6DDCF64E0D5 +:1004E00070E08AE590E6D8CF8091DE618F778093C7 +:1004F0000961F6DE85E090E080930A6190930B61DC +:1005000081CF80910861809304614091E2615091B4 +:10051000E36161E070E084E091E6C2CF8091DE614A +:1005200080930861DDDE88DE82E08093480C6ACF2C +:1005300010920561109206614091E2615091E36171 +:1005400062E070E085E091E6ABCFCADE5BCF10924F +:100550000461DBCF8CED91E686D054CF84E090E04F +:1005600080930A6190930B618091046285FF12C0B1 +:1005700063DE80E28093400C80910A6190910B6170 +:10058000029799F580910761882319F0109207610D +:1005900088D0A6DE80911C6285FF04C04DDE80E21B +:1005A0008093460C8091246285FF09C045DE80E27D +:1005B0008093480C809126629091276289D0809127 +:1005C0003C6285FF05C038DE80E280934E0C8ED001 +:1005D00080E280930B0CDF91CF91089580910961A7 +:1005E0008093030C1092096125CF10920A6110923A +:1005F0000B61D0CF809168008C7B826498ED94BFB2 +:10060000809368001092060F8FE697E10197F1F74B +:1006100000C0000078DE84E092E68093060C9093A0 +:10062000070C1092080C1092090C83E88093000CC0 +:1006300080E492E4AFE0B0E02091850020FD04C0AA +:100640000197A109B109C1F71092010C0895E0E0EA +:10065000FCE08181816081830895E0E0FCE081819C +:100660008E7F81830895FC019181913299F018F475 +:10067000903221F097CE9432E8F77ACE8681978136 +:100680000797C1F781E080930D6367E070E080E039 +:1006900090E670CE4681578167E070E080E090E69A +:1006A00068CE80910D63811110920D6308951092B0 +:1006B0000B6310920C631092096310920A631092FC +:1006C0000863109206631092076310920D630895F9 +:1006D0008134910510F080E490E080930B63909357 +:1006E0000C631092096310920A63089510920863D4 +:1006F0000895DDCF80910B6390910C63892BF1F00D +:100700002091096330910A6380910B6390910C638F +:100710002817390798F010920B6310920C6310920F +:10072000096310920A6310922662109227628091E8 +:100730000B0C82FDFCCF82E08093480C8091086313 +:10074000811120C08091066390910763892BD1F0BD +:1007500080913C6281FF16C080910663909107638F +:1007600080933E6290933F6280910B0C82FDFCCFA0 +:1007700082E080934E0C81E08093086310920663C0 +:100780001092076308952091096330910A63809164 +:100790000B6390910C632817390788F480910963E3 +:1007A00090910A639C012F5F3F4F20930963309320 +:1007B0000A63845B9E49FC01808190E008958FEF7D +:1007C0009FEF0895CF93C82F8091066390910763A0 +:1007D0008034910598F480910663909107639C01A1 +:1007E0002F5F3F4F2093066330930763845F9E49DA +:1007F000FC01C083CF9108957DDFD5DD8091086332 +:100800008111FACF8091066390910763803491053E +:1008100098F7E1CF8091066390910763892B89F463 +:100820008091086381110DC0089564DFBCDD809163 +:100830000863811106C08091066390910763892B3C +:1008400001F1809108638111F0CF8091066390914E +:100850000763892B51F3809106639091076380931E +:100860003E6290933F6280910B0C82FDFCCF82E050 +:1008700080934E0C81E0809308631092066310927F +:100880000763089584E19EDF80E19CCF10922C6580 +:1008900010922D651092296510922A651092286594 +:1008A00010921365109211651092126510921065E6 +:1008B00010920E6310920F63E4E1F5E684E1DF012C +:1008C0001D928A95E9F740E052E06FEF70E080E119 +:1008D00093E6DEC23F924F925F926F927F928F9229 +:1008E0009F92AF92BF92CF92DF92EF92FF920F93BF +:1008F0001F93CF93DF9300D000D0CDB7DEB783E056 +:10090000382E92E0E92EF12C20EF922E34E0C32E07 +:10091000D12C41EF842EAA24A394B12C34DF97FF6D +:1009200015C02496CDBFDEBFDF91CF911F910F91EF +:10093000FF90EF90DF90CF90BF90AF909F908F90FF +:100940007F906F905F904F903F900895482F209137 +:100950002C6530912D652230310509F477C0C8F43B +:100960002115310509F445C021303105B9F6E09172 +:10097000136591E09E0F90931365F0E0EC5EFA49E9 +:100980008083809128658150809328658823E9F1D0 +:10099000C5CF2430310509F465C108F432C12530D2 +:1009A000310509F0BBCFE091136591E09E0F909364 +:1009B0001365F0E0EC5EFA498083809128658150F0 +:1009C000809328658111AACF809114659091156557 +:1009D0009827892798278093116590931265209115 +:1009E0001665209310658130924078F480922B65D3 +:1009F0000CC080932B65E0EDE80FE63430F4F0E0B6 +:100A0000ED54FF4980818E3F29F4E0922C65F092ED +:100A10002D6584CF8F3F79F410921365309228654D +:100A200083E090E0443611F085E090E080932C65FF +:100A300090932D6573CF882341F3109213658093B3 +:100A40002865A0922C65B0922D6568CF803209F0A0 +:100A500069C080912B65803F31F5809110658F7D55 +:100A6000863499F420911165309112656091296561 +:100A700070912A6590E080E040E153E616D102DFF4 +:100A800010922C6510922D6549CF8534C1F74091A5 +:100A900011655091126560E173E68091296590912E +:100AA0002A65A8D1ECCF813F81F584E18BDE4090AF +:100AB000296550902A65712C612C10E000E080912E +:100AC0001165909112650817190718F080E17ADE18 +:100AD000D7CF809110658F7D863459F4C301B20160 +:100AE000600F711F811D911D68D16CDE0F5F1F4F5C +:100AF000E6CF853441F48091296590912A65800F75 +:100B0000911FB6D1F2CF8FEFF0CF853409F4B7CF74 +:100B100090F4803409F4B3CF38F4803309F4AFCFC4 +:100B20008133A9F185E1D3CF813409F43EC0823409 +:100B300009F4A5CFF7CF823509F4A1CFC0F48035F1 +:100B400009F49DCF813571F784E13CDE80E13ADE26 +:100B500061DE19821A821B821C8289819A81AB8193 +:100B6000BC8180349D40A340B10580F1C6DA863552 +:100B7000C9F1853709F43AC08535A1F6809114652D +:100B8000909115658093296590932A6578CF84E1CB +:100B900019DE09EF10E6F1E600301F0709F496CFE1 +:100BA000F80181918F010EDEF6CF8091146511E07E +:100BB000803839F018E0813821F012E0823809F0ED +:100BC00013E084E1FFDD812FFDDD80CF89819A81F3 +:100BD000AB81BC810196A11DB11D89839A83AB8332 +:100BE000BC83BBCF84E1EEDD80E0EECF84E1EADDC3 +:100BF00080918010E7DD80918110E4DD809182108A +:100C0000E3CFE091136591E09E0F90931365F0E0C0 +:100C1000EC5EFA4980838091286581508093286535 +:100C200081117CCE80911465909115659827892754 +:100C300098278093116590931265209116652093F3 +:100C400010658130E2E09E0708F0DFCE10920E635F +:100C500010920F63892B09F4D8CEC0922C65D092E4 +:100C60002D655CCE20910E6330910F63A9014F5F1B +:100C70005F4F40930E6350930F63205F3C49F9012F +:100C8000808380911165909112654817590708F487 +:100C900045CE90922B65B9CE9091061091FDFCCF78 +:100CA0009DE994BF809300100895CF92DF92EF9258 +:100CB000FF920F931F93CF93DF936115E0E17E07BF +:100CC0008105910508F470C02115310509F46CC047 +:100CD00089012130324010F000E012E02091011033 +:100CE000E1E0F0E0611530E873078105910510F44B +:100CF000F0E0E0E07A016F0184E0CC0CDD1C8A9525 +:100D0000E1F72F7C2C2938ED34BF20930110EB0143 +:100D1000D06888E0C1DF8FEF88838091061081FD65 +:100D2000FCCF81E0B9DF809101108F7C8C2998ED98 +:100D300094BF8093011082E0AFDFF70181E090E083 +:100D4000AE014E195F09DA01AE0FBF1F8017910780 +:100D5000E8F0A8014E7FC40FD51F4017510760F47B +:100D6000E40EF51EF701808190E09F6F88839983E0 +:100D70008091061081FDFCCF81E0DF91CF911F9122 +:100D80000F91FF90EF90DF90CF9086CF218130813F +:100D90002327322723272D933C932091061021FDF2 +:100DA000FCCF02963296CFCFDF91CF911F910F915A +:100DB000FF90EF90DF90CF9008954091011021E0D7 +:100DC00030E0611550E875078105910510F430E0B9 +:100DD00020E084E0220F331F8A95E1F7342F3F7C17 +:100DE000322B28ED24BF30930110AB015068FA017B +:100DF000808108950F931F938F3F910509F058F557 +:100E00008A0130E020E0080F191F211D311D01303B +:100E1000114021053105E0F040E051E0481B590B3D +:100E2000209106102370E1F72DE924BF23E12093E0 +:100E30000010460F571FFB01861B970B9C5EE417A3 +:100E4000F50761F48DE984BF81E08093001003C051 +:100E50004115510529F71F910F910895DC01AE0F3F +:100E6000BF1F21912C932091061020FDFCCFE7CFCE +:100E70008F3F910509F020F49C5EFC01808108956C +:100E80008FEF0895EE0FFF1F0590F491E02D099468 +:100E9000DC0101C06D9341505040E0F70895F89493 +:020EA000FFCF82 +:100EA20000C2010000000816160342004C00300088 +:100EB20030003000300030003000300031002A2A8B +:100EC20003410056005200440055002000430044F4 +:100ED200004300200042006F006F0074006C006F3E +:100EE20000610064006500720010100341005600AA +:100EF20052004400550042004C0004030904090258 +:100F020043000201008032090400000102020100D4 +:100F12000524001001052401000104240202052415 +:100F22000600010705810310004009040100020ABE +:100F3200000000070502024000000705830240008E +:100F4200001201000202000040091201000001012A +:100F52000203010000FEFEFEFEFEFEFEFEFEFEFE9F +:100F6200FEFEFE020114FEFE05FEFEFEFEFEFEFE7B +:100F7200FEFEFE000000FEFE0204FEFEFEFEFEFE7F +:100F8200FEFEFEFEFEFEFEFFFEFEFEFEFEFEFEFE7E +:100F9200FEFEFEFEFEFEFEFF004156522053544B63 +:020FA20000004D +:040FFA00F8950895C9 +:020FFE00011AD6 +:00000001FF diff --git a/megaavr/bootloaders/usbcdcboot/.gitignore b/megaavr/bootloaders/usbcdcboot/.gitignore new file mode 100644 index 00000000..ce68ef25 --- /dev/null +++ b/megaavr/bootloaders/usbcdcboot/.gitignore @@ -0,0 +1,7 @@ +*.o +*.elf +*.lst +*.map +src/*.o +# Keep the prebuilt hex shipped to users, in version control: +!usbcdcboot_64du32.hex diff --git a/megaavr/bootloaders/usbcdcboot/DESIGN.md b/megaavr/bootloaders/usbcdcboot/DESIGN.md new file mode 100644 index 00000000..ebc37cbb --- /dev/null +++ b/megaavr/bootloaders/usbcdcboot/DESIGN.md @@ -0,0 +1,346 @@ +# AVRDU CDC Bootloader  EDesign Document + +**Target**: AVR64DU32 on Microchip Curiosity Nano (EV59F82A) and similar +boards exposing the AVR DU's native USB-D+/D- to a host PC. + +**Goal**: Allow `avrdude -c arduino -P COMx` to write the application +section over the on-chip USB CDC port, using the 1200 bps touch reset +convention shared with Arduino Leonardo / Pro Micro. + +**License**: This bootloader is a clean-room implementation. It does not +reuse code from Optiboot, LUFA, TinyUSB, V-USB, or any other USB or AVR +bootloader project. Implementation references are limited to: + + - USB 2.0 specification (chapter 9) + - USB CDC 1.20 / PSTN 1.20 specifications + - AVR64DU32 datasheet (DS40002676 rev. A or later) + - AVR061: STK500 Communication Protocol Application Note + - Microchip ATPACK device headers (`avr/io.h`, `avr/iousbxxx.h`) + +A two-line per-file provenance header in every source file restates this. + +--- + +## 1. Memory Layout + +The AVR64DU32 has 64 KB of program flash divided by the BOOTSIZE fuse +into two contiguous regions, with optional APPDATA on top: + +``` + 0x0000 ┌─────────────────────────────━E ━E ━E ━E BOOT section (4 KB) ━E ↁEthis bootloader lives here + ━E - reset / vector table ━E ━E - usb_min_* ━E ━E - cdc_min_* ━E ━E - stk500_parser ━E ━E - nvm_self_program ━E ━E ━E 0x1000 ├─────────────────────────────┤ ↁEBOOTEND = BOOTSIZE * 512 + ━E ━E ━E APPCODE section (60 KB) ━E ↁEthe user sketch + ━E - relocated vector table ━E ━E - .text ━E ━E - .data init image ━E ━E ━E 0xFFFF └─────────────────────────────━E``` + +`BOOTSIZE = 0x08` (8 blocks ÁE512 B = 4096 B) is the boards.txt fuse +value. The corresponding linker flag is `-Wl,--section-start=.text=0x0` +for the bootloader and `-Wl,--section-start=.text=0x1000` for the app. + +SRAM (8 KB, 0x6000..0x7FFF) is shared between bootloader and app at +different times. The bootloader uses a magic word at the very top of +SRAM (`0x7FFE`, two bytes) to coordinate entry mode with the app  Esee +section 3. + +--- + +## 2. Entry Decision + +On every reset, the CPU starts at 0x0000 inside the BOOT section. The +bootloader's reset handler must promptly decide between staying in the +bootloader to accept a flash upload, or jumping to the application. + +### 2.1 RSTCTRL.RSTFR reset cause flags + +The AVR DU records the last reset cause in `RSTCTRL.RSTFR`. The +bootloader reads and clears this register first. Possible flags: + + - `PORF`  Epower-on reset (cold boot) + - `BORF`  Ebrown-out reset + - `EXTRF`  Eexternal reset (RESET pin) + - `WDRF`  Ewatchdog timer reset (used by the 1200 bps touch path) + - `SWRF`  Esoftware reset + - `UPDIRF` EUPDI reset (after a chip-erase, app may be invalid) + +### 2.2 Magic-word handshake + +A two-byte magic at `AVRDU_BL_MAGIC_ADDR = 0x7FFE` distinguishes +"please stay in the bootloader" from "this was just a normal reset": + + - `AVRDU_BL_MAGIC_STAY = 0xB007`  Erequest set by the app right + before a WDT reset (the avrdude + 1200 bps touch path uses this) + - anything else  Eproceed to the application + +The runtime's `usb_cdc.c` does the following when the host opens the CDC +port at 1200 bps and then drops DTR: + +```c +*(volatile uint16_t *)AVRDU_BL_MAGIC_ADDR = AVRDU_BL_MAGIC_STAY; +USB0.CTRLA &= ~USB_ENABLE_bm; // physical detach +_PROTECTED_WRITE(WDT.CTRLA, WDT_PERIOD_8CLK_gc); // ~8 ms WDT +while (1) { } // wait for reset +``` + +The 8 ms WDT timeout is long enough for the host to notice the +disconnect, and short enough that avrdude's port-reappear timeout +(several seconds) easily covers the round trip. + +### 2.3 Decision flow on bootloader entry + +``` +on reset: + save_rstfr = RSTCTRL.RSTFR + RSTCTRL.RSTFR = 0xFF // clear all flags + relocate_vector_table_to_BOOT() + + magic = *(uint16_t*)0x7FFE + *(uint16_t*)0x7FFE = 0 // consume the request + + if (magic == 0xB007) goto stay_in_bootloader + if (save_rstfr & EXTRF) goto stay_in_bootloader // user pressed RESET + if (app_appears_invalid()) goto stay_in_bootloader // 0xFFFF at 0x1000 + goto jump_to_app + +stay_in_bootloader: + enable_USB() + run_cdc_stk500_loop() + +jump_to_app: + point_vector_table_back_to_APPCODE() + ((void (*)(void))0x1000)() // far jump into app reset vector +``` + +`app_appears_invalid()` looks for `0xFFFF` at the application's reset +vector (offset 0 of APPCODE), which is what blank flash reads as. + +--- + +## 3. USB CDC subset + +The bootloader's USB stack is a subset of the runtime stack already +written in `AVRDU_CDC/`. Features kept: + + - USB 2.0 Full-Speed device, single configuration + - One control endpoint (EP0) + - One bulk IN endpoint (EP1 IN, 64 B, application data ↁEhost) + - One bulk OUT endpoint (EP2 OUT, 64 B, host ↁEapplication data) + - SET_LINE_CODING / GET_LINE_CODING (no actual UART, but avrdude reads + the current line coding to compare against 1200; the bootloader + doesn't itself need this, but the runtime does) + - SET_CONTROL_LINE_STATE (DTR / RTS) + - GET_DESCRIPTOR (device, config, string, language ID) + +Features dropped from the runtime stack: + + - Interrupt notification endpoint (EP1 IN bulk replaces it; some hosts + tolerate a CDC ACM without the notification EP, and avrdude only + does bulk transfers) + - HID interfaces and endpoints (no Keyboard/Mouse/Joystick in the BL) + - Ring buffers (the bootloader processes one packet at a time) + - Remote wakeup / suspend / resume handling (bootloader stays running) + +The descriptor set is rebuilt for the bootloader to advertise only the +two CDC interfaces (control + data), and uses a different product +string ("AVRDU CDC Bootloader") so the host can distinguish. + +VID/PID: + + - VID = 0x1209 (pid.codes) + - PID = 0xDA33 (one more than the runtime's 0xDA32, so it appears as a + separate COM port and avrdude can target it explicitly) + +--- + +## 4. STK500v1 protocol + +avrdude's `-c arduino` driver speaks STK500v1, a small ASCII-byte +protocol documented in Atmel AVR061. Each request is a single command +byte (sometimes with parameters), terminated by `CRC_EOP = 0x20`. The +bootloader replies `INSYNC = 0x14`, optional data, then `OK = 0x10`. + +Implemented commands (the minimum avrdude actually uses for the +`arduino` programmer): + +| Cmd | Hex | What we do | +|------|------|---------------------------------------------------------| +| GET_SYNC | 0x30 | reply INSYNC, OK | +| GET_SIGN_ON | 0x31 | reply INSYNC, "AVR STK", OK | +| GET_PARAMETER | 0x41 | reply INSYNC, parameter byte, OK | +| SET_PARAMETER | 0x42 | discard parameter byte; reply INSYNC, OK | +| SET_DEVICE | 0x42 | (alias) consume 20 bytes; reply INSYNC, OK | +| SET_DEVICE_EXT | 0x45 | consume 5 bytes; reply INSYNC, OK | +| ENTER_PROGMODE | 0x50 | reply INSYNC, OK | +| LEAVE_PROGMODE | 0x51 | reply INSYNC, OK; arm WDT to exit bootloader | +| CHIP_ERASE | 0x52 | NVMCTRL chip erase via APP+BOOT all FFs (skipped)| +| LOAD_ADDRESS | 0x55 | consume 2-byte little-endian word address | +| UNIVERSAL | 0x56 | consume 4 bytes; reply INSYNC, 0x00, OK | +| PROG_PAGE | 0x64 | consume N+3 (size hi/lo, memtype, payload); write| +| READ_PAGE | 0x74 | consume 3 (size hi/lo, memtype); reply payload | +| READ_SIGN | 0x75 | reply 3-byte signature (1E 96 22 for 64DU32) | + +Unknown commands trigger `NOSYNC = 0x15` and the host re-syncs. + +The address loaded by `LOAD_ADDRESS` is a **word** address (ÁE for +bytes). For PROG_PAGE the data is written byte-for-byte to flash at +`load_addr * 2`. For 64 KB flash we must also handle the +`UNIVERSAL` command `0x4D xx xx` which avrdude uses to set the +high-order byte (since STK500v1 addresses are only 16 bits and we have +a 16-bit byte address space already  Ethe AVR64DU32 has a 64 KB +program space which is 32 K *words*, so a 16-bit word address covers +the whole part and the byte-extension command can be safely treated as +a no-op for the 64DU32). + +--- + +## 5. NVMCTRL self-programming + +AVR DU NVMCTRL operations are issued via the `CCP` protection +mechanism: + +```c +ccp_spm_unlock(); // write 0x9D to CCP +NVMCTRL.CTRLA = NVMCTRL_CMD_FLPER_gc; // page-erase command +*(volatile uint8_t*)addr = 0; // dummy write triggers operation +while (NVMCTRL.STATUS & NVMCTRL_FBUSY_bm) { } + +ccp_spm_unlock(); +NVMCTRL.CTRLA = NVMCTRL_CMD_FLWR_gc; // word-write command +while (page_offset < 512) { + *(volatile uint16_t*)addr = word_from_host; + addr += 2; + page_offset += 2; + while (NVMCTRL.STATUS & NVMCTRL_FBUSY_bm) { } +} +``` + +The 512 B page size is hard-coded for AVR64DU32. The bootloader's +`nvm_write_page()` accepts a 256-word buffer and walks it. + +Importantly: code running in BOOT can write to APPCODE/APPDATA but NOT +to BOOT itself. The BOOTLOCK fuse bit is not set (we don't lock the +bootloader against UPDI rewrites), but the silicon block on BOOT +self-write is unconditional. This is desirable  Eit protects the +bootloader from a runaway app. + +--- + +## 6. File / Module Layout + +``` +bootloaders/usbcdcboot/ + Makefile GNU make build rules; produces hex/elf/lst + usbcdcboot_64du32.hex compiled bootloader for AVR64DU32 + src/ + main.c reset handler, RSTFR check, jump_to_app + usb_min.c USB peripheral init, EP polling + usb_min.h + usb_desc.c descriptors (device/config/strings) + usb_desc.h + cdc_min.c CDC class request handler + cdc_min.h + stk500.c STK500v1 parser + stk500.h + nvm.c NVMCTRL self-program routines + nvm.h + link/ + avr64du32_bl.x linker script (.text=0, BOOTEND markers) + LICENSE.md MIT (or LGPL2.1 to match DxCore) + PROVENANCE.md clean-room provenance statement + README.md build / flash / test instructions +``` + +--- + +## 7. Size Budget + +| Module | Estimated bytes | Notes | +|-----------------|-----------------|------------------------------------| +| usb_min | 1400 | EP table init, control transfers | +| usb_desc | 300 | device + config + strings | +| cdc_min | 400 | line coding, DTR/RTS, bulk pumps | +| stk500 | 600 | command dispatch + addr/page state | +| nvm | 250 | erase, word-write, signature read | +| main + plumbing | 450 | reset, jump, RSTFR, magic, WDT | +| **Subtotal** | **3400** | ~83% of the 4 KB BOOT section | + +Slack: ~700 B for compiler-generated overhead, vector table padding, +and future additions (e.g. EEPROM programming). + +If the build overshoots 4 KB, raising `BOOTSIZE` from 0x08 to 0x0C +(6 KB) is the next step; the app's `.text` start moves from 0x1000 to +0x1800. Memory budget allows up to 0x10 (8 KB) before serious +encroachment on app space. + +--- + +## 8. Integration with the runtime CDC stack + +Two integration points between bootloader and the runtime application: + +### 8.1 1200 bps touch in the runtime + +The runtime's `usb_cdc.c` already implements the touch path (DTR drop +while line coding is 1200 baud ↁEset magic, detach, WDT reset). The +only contract addition is the magic address/value: + +```c +// usb_cdc.h additions: +#define AVRDU_BL_MAGIC_ADDR 0x7FFE +#define AVRDU_BL_MAGIC_STAY 0xB007 +``` + +### 8.2 Vector table relocation + +When the app gains control via the long jump from the bootloader, its +startup code must point the vector table back into APPCODE. The +toolchain's default startup (`crt*`) already does this when the link +script places `.text` at 0x1000 and sets `__vectors` accordingly. +No app source change required. + +--- + +## 9. Open questions / known limitations (v1) + +1. **EEPROM** writes via avrdude (`-Ueeprom:w:...`) are not implemented + in v1. avrdude with `-c arduino` is not commonly used for EEPROM + anyway; users wanting EEPROM should use UPDI via nEDBG. + +2. **FUSE** writes are unsupported. Use UPDI for fuse changes. + +3. **USERROW** writes are unsupported. Use UPDI. + +4. **48 MHz USB clock**: AVR DU has an internal 48 MHz oscillator + intended for USB. The bootloader must explicitly select it (via + `CLKCTRL.MCLKCTRLA`), then unlock the 12/16/20/24 MHz core clock + via `CLKCTRL.MCLKCTRLB`. This is duplicated in the runtime; the app + reinits it on startup, but the bootloader must do it independently + on every cold boot. + +5. **Suspend / resume**: ignored. avrdude opens-uses-closes the port + in well under the 3 ms suspend threshold so this is fine in + practice. + +6. **Self-update of the bootloader**: not supported (silicon-enforced). + To update the bootloader itself, reprogram via UPDI through nEDBG. + +--- + +## 10. Build & flash + +A separate `README.md` in the bootloader directory will spell out the +exact build steps. The high level is: + +``` +cd hardware/megaavr/1.6.2/bootloaders/usbcdcboot +make TOOLROOT=../../../../tools # uses DxCore's avr-gcc 7.3.0 +# produces usbcdcboot_64du32.hex + +# burn via nEDBG: +arduino-cli --burn-bootloader --board "DxCore:megaavr:avrduusb" \ + --programmer "atmel_ice" # or "nedbg", "serialupdi", etc. +``` + +After "burn bootloader" succeeds, the AVR64DU32 reboots into the +bootloader, the host enumerates a new "AVRDU CDC Bootloader" COM port, +and avrdude can then push the user's sketch using the standard +Leonardo-style 1200 bps touch dance. diff --git a/megaavr/bootloaders/usbcdcboot/LICENSE.md b/megaavr/bootloaders/usbcdcboot/LICENSE.md new file mode 100644 index 00000000..d6ee2df2 --- /dev/null +++ b/megaavr/bootloaders/usbcdcboot/LICENSE.md @@ -0,0 +1,36 @@ +# AVRDU CDC Bootloader — License + +This bootloader is distributed under the terms of the **GNU Lesser +General Public License version 2.1** (LGPL 2.1), the same license +that covers the host DxCore project. + +A copy of the LGPL 2.1 license text is included in DxCore's top-level +`LICENSE.md`. In short, the LGPL 2.1 permits the following: + +- You may use, copy, modify and distribute this bootloader. +- You may link applications against it without those applications + having to be LGPL-licensed. +- Modifications to the bootloader itself must remain LGPL-licensed. +- The full source of the bootloader must be available to anyone + receiving a binary copy. + +You are encouraged to upstream improvements via pull request to the +DxCore repository. + +## Why LGPL 2.1 and not GPL 2 + +GPL 2 is the license that covers Optiboot (the existing UART +bootloader in DxCore). Because this bootloader does **not** copy or +derive from Optiboot code, it is free to choose its own compatible +license. LGPL 2.1 was chosen so that user sketches running on top of +the bootloader (which technically constitute "linking" via the reset +vector hand-off) are not automatically forced under GPL. + +## Author and contact + +Clean-room implementation prepared for the DxCore AVR DU integration +effort. See `PROVENANCE.md` in this directory for the full list of +reference documents consulted while writing this code. + +For issues, bug reports, or contributions: open an issue or pull +request against the DxCore repository on GitHub. diff --git a/megaavr/bootloaders/usbcdcboot/Makefile b/megaavr/bootloaders/usbcdcboot/Makefile new file mode 100644 index 00000000..cf24102c --- /dev/null +++ b/megaavr/bootloaders/usbcdcboot/Makefile @@ -0,0 +1,169 @@ +# avrdu_cdc_bl/Makefile +# -------------------------------------------------------------------- +# Build rules for the AVRDU CDC bootloader. +# Produces usbcdcboot_64du.hex, .elf, .lst, .map (one hex per build +# class; see build_all_du.bat for the 16du/16du14/32du/32du14/64du set). +# +# Usage from inside this directory: +# +# make TOOLROOT=../../../../tools # use DxCore's toolchain +# make clean +# make all # default target +# make size # report .text size vs 4 KB budget +# +# The DxCore toolchain ships avr-gcc 7.3.0 / atmel3.6.1 / azduino8 under +# tools/avr-gcc//bin/avr-gcc. If TOOLROOT is not set we look for it +# on $PATH. License: LGPL 2.1. +# -------------------------------------------------------------------- + +MCU := avr64du32 +F_CPU := 24000000UL +TARGET := usbcdcboot_64du +BOOT_BASE := 0x0000 +BOOTSIZE := 4096 # bytes reserved for the bootloader (BOOTSIZE fuse * 512) + +# --- Toolchain ------------------------------------------------------ +ifeq ($(strip $(TOOLROOT)),) +CC := avr-gcc +OBJCOPY := avr-objcopy +OBJDUMP := avr-objdump +SIZE := avr-size +else +AVR_GCC_DIR := $(firstword $(wildcard $(TOOLROOT)/avr-gcc/*)) +CC := $(AVR_GCC_DIR)/bin/avr-gcc +OBJCOPY := $(AVR_GCC_DIR)/bin/avr-objcopy +OBJDUMP := $(AVR_GCC_DIR)/bin/avr-objdump +SIZE := $(AVR_GCC_DIR)/bin/avr-size +endif + +# --- Sources -------------------------------------------------------- +SRCS := src/main.c \ + src/spm_entry.c \ + src/usb_min.c \ + src/usb_desc.c \ + src/cdc_min.c \ + src/stk500.c \ + src/nvm.c + +OBJS := $(SRCS:.c=.o) + +# --- Compile / link flags ------------------------------------------- +# --- .rodata containment -------------------------------------------- +# The hex must contain nothing outside the boot section: uploading an +# application would erase whatever lies beyond it. +# +# * avr64du* (avrxmega2): flash is not fully mapped, so .rodata has no +# natural home in flash. avr-gcc <= 14 - including the +# 7.3.0-atmel3.6.1-azduino* toolchain that DxCore ships - folds it +# into the .data init image (LMA right after .text), which is exactly +# what a bootloader needs, and REJECTS -mrodata-in-ram as an unknown +# option. avr-gcc >= 15 instead defaults to putting .rodata in the +# FLMAP window at flash 0x8000, i.e. OUTSIDE the boot section, so +# there we have to ask for the old behaviour explicitly. +# * avr16du*/avr32du* (avrxmega3): all of flash is mapped; .rodata is +# loaded right after .text and is contained on every compiler. +# +# Detected automatically; override with e.g. make RODATA_FLAGS=-mrodata-in-ram +CC_MAJOR := $(firstword $(subst ., ,$(shell $(CC) -dumpversion))) +ifneq ($(filter avr64du%,$(MCU)),) + ifneq ($(filter 15 16 17 18 19 20 21 22 23 24 25,$(CC_MAJOR)),) + RODATA_FLAGS ?= -mrodata-in-ram + endif +endif +RODATA_FLAGS ?= + +CFLAGS := -mmcu=$(MCU) \ + -DF_CPU=$(F_CPU) \ + -DBOOTLOADER_BUILD=1 \ + $(RODATA_FLAGS) \ + \ + -Os \ + -ffunction-sections -fdata-sections \ + -fno-common \ + -mrelax \ + -Wall -Wextra -Wstrict-prototypes \ + -std=gnu11 \ + -Isrc + +# --- LED pin override (per-build) ------------------------------------ +# main.c defaults the DFU LED to PA7 (present on 20/28/32-pin DU). The +# 14-pin DU has no PA7, so its build classes pass LED_PORT/LED_PIN to move +# the LED to PD6, e.g.: make ... LED_PORT=PORTD LED_PIN=6 +# (Kept as plain tokens -- no spaces/parens -- so Windows cmd passes them +# through cleanly.) +ifneq ($(strip $(LED_PORT)),) +CFLAGS += -DBL_LED_PORT=$(LED_PORT) +endif +ifneq ($(strip $(LED_PIN)),) +CFLAGS += -DBL_LED_PIN=$(LED_PIN) +endif + +# --- VUSB regulator ------------------------------------------------- +# VREG=1 (default): enable the internal 5V->3.3V USB voltage regulator at +# startup (5V VDD systems; power configurations 5b/5s). +# VREG=0: leave the regulator off; the board feeds 3.3V into the +# VUSB pin externally (configuration 3s). Built as the +# *_novreg hex variants, e.g.: make ... VREG=0 +VREG ?= 1 +ifeq ($(VREG),1) +CFLAGS += -DUSB_VREG_INTERNAL +endif + +# The toolchain's specs-avr64du32 file handles the avrxmega2 paths +# automatically when -mmcu=avr64du32 is set, so we don't need any +# explicit -B options here. + +# The SPM entry stub sits in the last 6 bytes of the boot section. +SPM_ADDR := 0x0FFA +VER_ADDR := 0x0FFE + +LDFLAGS := -mmcu=$(MCU) \ + $(RODATA_FLAGS) \ + -Wl,--section-start=.spmtarg=$(SPM_ADDR) \ + -Wl,--section-start=.blversion=$(VER_ADDR) \ + -Wl,--undefined=bl_spm_entry \ + -Wl,--undefined=bl_version \ + -Wl,--gc-sections \ + -Wl,--section-start=.text=$(BOOT_BASE) \ + -Wl,-Map,$(TARGET).map \ + -mrelax +# NOTE: we intentionally do NOT pass -nostartfiles. The standard +# crt provides _start which calls main, and is what makes +# --gc-sections see main as reachable. Crt overhead is ~50 bytes, +# more than compensated by the section-level dead-code elimination. + +# Optional: a custom linker script could replace --section-start above +# if we wanted finer control over .data init image placement. For a +# 4 KB bootloader the default link with .text=0 is sufficient. + +# --- Build rules ---------------------------------------------------- +.PHONY: all clean size + +all: $(TARGET).hex $(TARGET).lst + +$(TARGET).elf: $(OBJS) + $(CC) $(LDFLAGS) -o $@ $(OBJS) + $(SIZE) --mcu=$(MCU) -A $@ + +$(TARGET).hex: $(TARGET).elf + $(OBJCOPY) -O ihex -R .eeprom -R .fuse -R .signature -R .user_signatures \ + --set-section-flags .spmtarg=alloc,load \ + --set-section-flags .blversion=alloc,load $< $@ + +$(TARGET).lst: $(TARGET).elf + $(OBJDUMP) -h -S $< > $@ + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + +size: $(TARGET).elf + @echo "" + @echo "=== Bootloader size vs $(BOOTSIZE)-byte budget ===" + @$(SIZE) --mcu=$(MCU) -A $< \ + | awk '/^.text/ { t=$$2 } \ + /^.data/ { d=$$2 } \ + END { printf " .text = %u\n .data = %u (init image)\n total = %u bytes\n free = %d bytes\n", \ + t, d, t+d, $(BOOTSIZE)-(t+d) }' + +clean: + rm -f $(OBJS) $(TARGET).elf $(TARGET).hex $(TARGET).lst $(TARGET).map diff --git a/megaavr/bootloaders/usbcdcboot/PROVENANCE.md b/megaavr/bootloaders/usbcdcboot/PROVENANCE.md new file mode 100644 index 00000000..35fdfe5e --- /dev/null +++ b/megaavr/bootloaders/usbcdcboot/PROVENANCE.md @@ -0,0 +1,86 @@ +# AVRDU CDC Bootloader — Provenance Statement + +This file documents the clean-room development of the AVRDU CDC +Bootloader. It exists so that downstream maintainers can verify the +absence of accidental code reuse from incompatibly-licensed projects. + +## Summary + +The bootloader was written from public protocol and silicon +specifications **without consulting source code from any other USB +or AVR bootloader project**. In particular, no source was viewed +from: + +- **Optiboot** (the existing UART bootloader in DxCore - GPL 2+) +- **LUFA** (Lightweight USB Framework for AVRs - MIT-with-exception) +- **TinyUSB** (cross-platform USB stack - MIT) +- **V-USB** (software USB for AVRs - GPL 2 or commercial) +- **Arduino USB Host Shield 2.0** +- **Arduino's own ArduinoCore-avr** USB code (for 32u4 / Leonardo) +- **CircuitPython** USB stack +- **NicoHood/HID** library +- **MHeironimus/ArduinoJoystickLibrary** + +Code organisation, naming, and comments are original to this work. + +## Reference documents (all publicly available) + +### USB protocol level + +| Document | Use | +|---|---| +| USB 2.0 Specification, chapter 9 (Device Framework) | Standard requests, descriptor types, control transfer state machine | +| USB CDC 1.20 Specification | Class identifiers, functional descriptor framework | +| USB CDC PSTN Subclass 1.20 Specification | ACM class request codes (Table 13), Line Coding format (Table 17) | + +### AVR DU silicon level + +| Document | Use | +|---|---| +| Microchip DS40002676 (AVR64DU32 datasheet) | NVMCTRL section 11, USB peripheral section 28, RSTCTRL section 12, CLKCTRL section 11.5, fuse layout | +| Microchip DS40002683 (AVR DU family datasheet) | Cross-reference for shared peripherals | +| Atmel AVR061 - STK500 Communication Protocol Application Note | STK500v1 command set (Section 4), used by avrdude's "arduino" programmer mode | +| Microchip ATPACK device headers (`avr/io.h`, `ioavr64du32.h`) | Register and bit-field names | + +### avrdude protocol behaviour + +The exact byte sequences avrdude emits when invoked as `-c arduino` +were inferred from the AVR061 application note and verified against +the protocol diagrams in the same note. No source code from avrdude +itself was read. + +## Author's statement + +I declare that, to the best of my knowledge, every source file in +`src/` is original work derived solely from the reference documents +listed above, and contains no code copied or paraphrased from any of +the projects in the "absence of reuse" list above. + +Where the runtime AVRDU CDC stack (the sibling project in this same +repository) shares architectural patterns with this bootloader, the +shared lineage is **the same single clean-room development effort** +that produced both - not a transfer from any external source. + +## Per-file provenance header + +Every `.c` and `.h` file in `src/` opens with a comment block +explicitly restating that it is a clean-room implementation and listing +which specifications it consulted. Maintainers introducing new files +to this directory should follow the same convention. + +## Verification by review + +This bootloader is offered for upstream contribution to DxCore. If +the DxCore maintainers (or any third party) wish to verify the +clean-room provenance independently, the following are acceptable +review approaches: + +1. **Side-by-side comparison** with Optiboot's `optiboot_dx.c` - + structural and naming differences should be obvious throughout. +2. **Search for any verbatim phrase** longer than ~15 characters in + the comments or identifiers, against the candidate prior-art + codebases - none should match. +3. **Audit of the per-file headers** to confirm each file's + provenance statement is consistent with this document. + +If a discrepancy is found, please open an issue. diff --git a/megaavr/bootloaders/usbcdcboot/README.md b/megaavr/bootloaders/usbcdcboot/README.md new file mode 100644 index 00000000..55abdde2 --- /dev/null +++ b/megaavr/bootloaders/usbcdcboot/README.md @@ -0,0 +1,139 @@ +# AVRDU CDC Bootloader + +USB CDC bootloader for AVR DU-series microcontrollers, primarily the +**AVR64DU32 Curiosity Nano (EV59F82A)**. Lets `avrdude -c arduino` push +sketches over the on-target USB port using the same 1200 bps touch reset +convention as Arduino Leonardo / Pro Micro. + +## Provenance and license + +Clean-room implementation. No source from Optiboot, LUFA, TinyUSB, +V-USB, or any other USB or AVR bootloader project was consulted while +writing this code. References are limited to: + +- USB 2.0 specification +- USB CDC 1.20 / PSTN 1.20 specifications +- AVR64DU32 datasheet (Microchip DS40002676) +- Atmel AVR061 - STK500 Communication Protocol Application Note +- Microchip ATPACK device headers (`avr/io.h`, group/group-mask defines) + +License: LGPL 2.1 (to match the host DxCore repository). See `LICENSE.md`. + +## Building + +The DxCore distribution bundles the exact toolchain we developed and +test against (`tools/avr-gcc/7.3.0-atmel3.6.1-azduino8`). From inside +this directory: + +``` +make TOOLROOT=../../../../tools +``` + +This produces: + +``` +usbcdcboot_64du32.elf +usbcdcboot_64du32.hex +usbcdcboot_64du32.lst +usbcdcboot_64du32.map +``` + +To check that the build fits in the 4 KB BOOT section: + +``` +make TOOLROOT=../../../../tools size +``` + +Sample output (target): + +``` +=== Bootloader size vs 4096-byte budget === + .text = 3380 + .data = 36 (init image) + total = 3416 bytes + free = 680 bytes +``` + +## Burning to a Curiosity Nano + +The Curiosity Nano has an on-board nEDBG that exposes UPDI. From +Arduino IDE: + +1. **Tools > Board > DxCore > AVR DU-series Curiosity Nano (USB CDC Bootloader)** +2. **Tools > Board > AVR64DU32 Curiosity Nano (EV59F82A)** (sub-menu) +3. **Tools > Programmer > Atmel mEDBG** (or "Curiosity Nano" / + "nEDBG" depending on your avrdude.conf version) +4. **Tools > Burn Bootloader** + +Burn-bootloader writes both the fuse set (`BOOTSIZE=0x08` is the +critical one - it reserves the 4 KB BOOT section) and the +`usbcdcboot_64du32.hex` payload. + +After burn, the AVR64DU32 reboots into the bootloader. Windows / macOS +will enumerate a new "AVRDU CDC Bootloader" COM port (separate from the +nEDBG's own CDC port and separate from the runtime's CDC port). + +## Uploading a sketch + +With the bootloader resident and the board selected as above, plain +**Sketch > Upload** works. Arduino IDE's upload flow: + +1. Detects current CDC port for the board. +2. Opens it at 1200 bps and drops DTR (the "touch"). +3. Waits for the port to reappear (it does, courtesy of `cdc_min`'s + re-attach after the WDT reset hits the bootloader). +4. Calls `avrdude -c arduino -P -b 115200 -U flash:w:sketch.hex`. + +The runtime application's `usb_cdc.c` participates in step 2 - if the +host opens the COM port at 1200 baud and then drops DTR, it sets the +`AVRDU_BL_MAGIC_STAY = 0xB007` magic word at SRAM address `0x7FFE` and +triggers an 8 ms watchdog reset. On reboot the bootloader sees the +magic word and stays resident. + +## What this bootloader supports + +| Feature | Supported? | +|-------------------------------------|-----------------| +| Flash write via avrdude `-c arduino` | yes | +| Flash read-back verify | yes | +| Signature read (`-Usignature:r`) | yes | +| 1200 bps touch from runtime | yes | +| EXTRF (RESET button) -> stay in BL | yes | +| Blank-app guard | yes | +| EEPROM write/read | **no** (use UPDI)| +| FUSE write | **no** (use UPDI)| +| USERROW write | **no** (use UPDI)| +| Self-update of bootloader | **no** (silicon block)| +| Suspend / Resume / Remote wakeup | **no** | + +## Source tree + +``` +src/ + main.c reset handler, entry decision, jump-to-app + usb_min.c USB peripheral setup, EP table, standard requests + usb_desc.c device / config / string descriptors + cdc_min.c CDC ACM class requests, bulk pipe pumps + stk500.c STK500v1 protocol state machine + nvm.c NVMCTRL self-program (erase + word write + read) +Makefile build rules; see top of file for variables +DESIGN.md architecture write-up +LICENSE.md LGPL 2.1 +PROVENANCE.md clean-room provenance statement +``` + +The `.h` headers describe the public API of each module; `.c` files +hold the implementations. + +## Known limitations / future work + +- v1 targets AVR64DU32 only. Adding 32DU32 / 16DU32 needs the + signature byte table (DEVICEID2) updated and the upload size shrunk + to match smaller flash. +- The 4 KB BOOT section is a comfortable size budget for AVR64DU32 but + marginal for smaller-flash siblings. A 28-pin variant would need a + rebuild against `28pin-duseries` plus a re-check of the size budget. +- Suspend / Remote Wakeup are unimplemented. This means hosts that + aggressively suspend idle CDC devices will see the port go away + while the bootloader is waiting at startup; avrdude's port-reappear + loop normally retries fast enough that this is invisible. diff --git a/megaavr/bootloaders/usbcdcboot/build_all_du.bat b/megaavr/bootloaders/usbcdcboot/build_all_du.bat new file mode 100644 index 00000000..05fd214b --- /dev/null +++ b/megaavr/bootloaders/usbcdcboot/build_all_du.bat @@ -0,0 +1,86 @@ +@echo off +setlocal +pushd "%~dp0" +REM ============================================================ +REM build_all_du.bat (cmd-native, ASCII-only auto-detect) +REM Builds the USB CDC bootloader as FIVE LED/flash-size classes +REM (optiboot dd/dd14-style) and collects them into ..\hex\ : +REM +REM class LED covers compiled with +REM ----------- ---- -------------------- ------------- +REM 16du PA7 16du20/28/32 avr16du32 +REM 16du14 PD6 16du14 avr16du14 +REM 32du PA7 32du20/28/32 avr32du32 +REM 32du14 PD6 32du14 avr32du14 +REM 64du PA7 64du28/32 avr64du32 +REM +REM The signature is read from SIGROW at runtime (stk500.c), so one +REM class hex serves every package that shares its flash size -- the +REM bootloader always reports the true DEVICEID to avrdude. +REM main.c defaults the LED to PA7; the 14-pin classes pass +REM LED_PORT=PORTD LED_PIN=6 to move it to PD6 (no PA7 on 14-pin). +REM Put this in DxCore\megaavr\bootloaders\usbcdcboot\ and run it. +REM ============================================================ + +REM --- find avr-gcc: 5 levels up (Arduino\tools), version auto-detected +set "GCCBIN=" +for /d %%d in ("%~dp0..\..\..\..\..\tools\avr-gcc\*") do set "GCCBIN=%%~fd\bin" +if not defined GCCBIN ( + echo ERROR: avr-gcc toolchain folder not found under + echo %~dp0..\..\..\..\..\tools\avr-gcc\ + echo Adjust the path in the for /d line to match your install. + popd & exit /b 1 +) +if not exist "%GCCBIN%\avr-gcc.exe" ( + echo ERROR: avr-gcc.exe missing in "%GCCBIN%" + popd & exit /b 1 +) +set "PATH=%GCCBIN%;%PATH%" +if not defined MAKE set MAKE=make + +REM class mcu LEDport LEDpin +call :build 16du avr16du32 +call :build 16du14 avr16du14 PORTD 6 +call :build 32du avr32du32 +call :build 32du14 avr32du14 PORTD 6 +call :build 64du avr64du32 + +REM VREG-off variants (*_novreg): for boards feeding 3.3V into VUSB +REM externally (power configuration 3s). Selected in boards.txt by the +REM Tools -> "VUSB Power Source" menu ({bootloader.vregsuffix}=_novreg). +call :buildnv 16du avr16du32 +call :buildnv 16du14 avr16du14 PORTD 6 +call :buildnv 32du avr32du32 +call :buildnv 32du14 avr32du14 PORTD 6 +call :buildnv 64du avr64du32 + +echo. +echo === collecting hex files into ..\hex\ === +if not exist "..\hex" mkdir "..\hex" +move /y usbcdcboot_*.hex "..\hex\" >nul + +echo. +echo === hex files in ..\hex\ === +dir /b "..\hex\usbcdcboot_*.hex" +popd +endlocal +goto :eof + +:build +REM %1=class tag %2=mcu %3=LED port (opt) %4=LED pin (opt) +echo. +echo ------ building %2 -^> usbcdcboot_%1.hex (LED %3 %4) ------ +del /q src\*.o 2>nul +del /q usbcdcboot_%1.elf usbcdcboot_%1.hex usbcdcboot_%1.lst usbcdcboot_%1.map 2>nul +"%MAKE%" MCU=%2 TARGET=usbcdcboot_%1 LED_PORT=%3 LED_PIN=%4 all +goto :eof + +:buildnv +REM %1=class tag %2=mcu %3=LED port (opt) %4=LED pin (opt) +REM Same as :build but VREG=0 -> usbcdcboot_%1_novreg.hex (external 3.3V VUSB) +echo. +echo ------ building %2 -^> usbcdcboot_%1_novreg.hex (LED %3 %4, VREG off) ------ +del /q src\*.o 2>nul +del /q usbcdcboot_%1_novreg.elf usbcdcboot_%1_novreg.hex usbcdcboot_%1_novreg.lst usbcdcboot_%1_novreg.map 2>nul +"%MAKE%" MCU=%2 TARGET=usbcdcboot_%1_novreg VREG=0 LED_PORT=%3 LED_PIN=%4 all +goto :eof diff --git a/megaavr/bootloaders/usbcdcboot/src/cdc_min.c b/megaavr/bootloaders/usbcdcboot/src/cdc_min.c new file mode 100644 index 00000000..bcd99cfb --- /dev/null +++ b/megaavr/bootloaders/usbcdcboot/src/cdc_min.c @@ -0,0 +1,271 @@ +/* + * usbcdcboot/src/cdc_min.c + * -------------------------------------------------------------------- + * Clean-room implementation. References: + * - USB CDC 1.20 specification + * - USB CDC PSTN subclass 1.20 specification (Table 13: class + * request codes for ACM) + * + * No source from LUFA, TinyUSB, Optiboot or any other CDC stack + * was consulted while writing this file. + * + * Minimal CDC ACM for the bootloader. No ring buffers: RX is + * drained directly from the EP2 OUT buffer one byte at a time; + * TX accumulates bytes into a small scratch and pushes a packet + * when the scratch fills or cdc_min_flush() is called. + * + * License: LGPL 2.1. + */ + +#include +#include +#include +#include + +#include "cdc_min.h" +#include "usb_desc.h" + +/* ============================================================ + * Bring in the EP table and buffers declared in usb_min.c so we + * can drive EP2 OUT / EP3 IN directly without going through an + * abstraction layer. These are not in usb_min.h to avoid + * polluting the public API. + * ============================================================ */ +extern USB_EP_TABLE_t g_bl_ep_table; +extern uint8_t g_bl_ep2_out[USB_BL_EP2_SIZE]; +extern uint8_t g_bl_ep3_in [USB_BL_EP3_SIZE]; + +/* Helpers from usb_min.c (usb_setup_t comes from usb_desc.h). */ +void usb_min_ep0_send_zlp(void); +void usb_min_ep0_start_data_in (const uint8_t *d, uint16_t l, uint16_t h); +void usb_min_ep0_start_data_out(uint8_t *b, uint16_t l); +void usb_min_ep0_stall(void); + +/* ============================================================ + * CDC PSTN class request codes (CDC PSTN 1.20 Table 13) + * ============================================================ */ +#define CDC_REQ_SET_LINE_CODING 0x20 +#define CDC_REQ_GET_LINE_CODING 0x21 +#define CDC_REQ_SET_CONTROL_LINE_STATE 0x22 +#define CDC_REQ_SEND_BREAK 0x23 + +#define CDC_CTRL_LINE_DTR 0x01 +#define CDC_CTRL_LINE_RTS 0x02 + +/* ============================================================ + * CDC line coding (PSTN 1.20 Table 17): 7 bytes. + * The bootloader doesn't drive a UART so this is purely cosmetic, + * but the host reads/writes it during enumeration and during the + * 1200 bps touch detection logic. + * ============================================================ */ +typedef struct __attribute__((packed)) { + uint32_t dwDTERate; + uint8_t bCharFormat; + uint8_t bParityType; + uint8_t bDataBits; +} cdc_line_coding_t; + +static cdc_line_coding_t s_line_coding = { + .dwDTERate = 115200, + .bCharFormat = 0, + .bParityType = 0, + .bDataBits = 8 +}; + +static uint8_t s_control_line_state = 0; +static bool s_pending_set_line_coding = false; + +/* ============================================================ + * RX state + * The runtime stack uses a ring buffer. For the bootloader we + * leave the bytes inside the EP2 OUT buffer and consume them + * linearly. When all bytes are consumed we re-arm EP2 OUT by + * clearing BUSNAK. + * ============================================================ */ +static volatile uint16_t s_rx_cnt = 0; /* total bytes in current OUT packet */ +static volatile uint16_t s_rx_idx = 0; /* next byte to deliver to caller */ + +/* ============================================================ + * TX state + * Bytes pile up in g_bl_ep3_in until a full 64 B packet is + * ready, or cdc_min_flush() is called. When the EP3 IN + * transaction completes, s_tx_in_flight clears and the next + * pump tick can ship the next packet. + * ============================================================ */ +static volatile bool s_tx_in_flight = false; +static volatile uint16_t s_tx_len = 0; /* bytes staged in g_bl_ep3_in */ + +/* ============================================================ + * Note: bl_exit_via_wdt() is intentionally NOT called from this + * file anymore. The 1200-bps + DTR-fall pattern is the host's + * way of saying "enter bootloader"; if we are already here, the + * correct reaction is no reaction. + * ============================================================ */ + +/* ============================================================ + * Class request handler (called from usb_min) + * ============================================================ */ +void cdc_min_handle_class_request(const usb_setup_t *s) { + switch (s->bRequest) { + + case CDC_REQ_SET_LINE_CODING: + if (s->wLength == sizeof(cdc_line_coding_t)) { + s_pending_set_line_coding = true; + usb_min_ep0_start_data_out((uint8_t *)&s_line_coding, + sizeof(cdc_line_coding_t)); + } else { + usb_min_ep0_stall(); + } + break; + + case CDC_REQ_GET_LINE_CODING: + usb_min_ep0_start_data_in((const uint8_t *)&s_line_coding, + sizeof(cdc_line_coding_t), s->wLength); + break; + + case CDC_REQ_SET_CONTROL_LINE_STATE: { + uint8_t new_state = s->wValue & 0x03; + bool dtr_falling = (s_control_line_state & CDC_CTRL_LINE_DTR) + && !(new_state & CDC_CTRL_LINE_DTR); + s_control_line_state = new_state; + usb_min_ep0_send_zlp(); + /* 1200 bps + DTR-fall is the Arduino upload "touch": the host + * is telling us to BE in the bootloader so it can upload. We + * are already here, so the correct response is to do nothing + * and let avrdude continue with STK500v1 on this same port. + * (Earlier versions reset out of BL here, which broke upload.) */ + (void)dtr_falling; + break; + } + + case CDC_REQ_SEND_BREAK: + usb_min_ep0_send_zlp(); + break; + + default: + usb_min_ep0_stall(); + break; + } +} + +/* Called from usb_min after the EP0 DATA-OUT stage completes for + * SET_LINE_CODING. The bytes have already been written directly + * into s_line_coding (we passed its address to ep0_start_data_out). */ +void cdc_min_data_out_complete(void) { + if (s_pending_set_line_coding) { + s_pending_set_line_coding = false; + /* Nothing else to do - DTE rate / data bits etc. are not + * actually applied to any UART here. */ + } +} + +/* ============================================================ + * Lifecycle hooks + * ============================================================ */ +void cdc_min_on_reset(void) { + s_rx_cnt = 0; + s_rx_idx = 0; + s_tx_in_flight = false; + s_tx_len = 0; + s_control_line_state = 0; + s_pending_set_line_coding = false; +} + +void cdc_min_on_ep2_out(uint16_t cnt) { + /* Latch this packet; cdc_min_rx_pop() will deliver bytes from it + * one at a time. We do NOT re-arm EP2 OUT yet - that happens + * inside cdc_min_poll() once the last byte has been consumed. */ + if (cnt > USB_BL_EP2_SIZE) cnt = USB_BL_EP2_SIZE; + s_rx_cnt = cnt; + s_rx_idx = 0; +} + +void cdc_min_on_ep3_in_done(void) { + s_tx_in_flight = false; +} + +/* ============================================================ + * Public init - nothing dynamic to clear here that cdc_min_on_reset + * doesn't already cover, but expose the symbol for main.c's call. */ +void cdc_min_init(void) { + cdc_min_on_reset(); +} + +/* ============================================================ + * Poll: re-arm EP2 OUT if the current OUT packet has been + * fully consumed, and push the staging TX buffer to EP3 IN + * if it is non-empty and the previous transmit has completed. + * ============================================================ */ +void cdc_min_poll(void) { + /* RX re-arm */ + if (s_rx_cnt != 0 && s_rx_idx >= s_rx_cnt) { + s_rx_cnt = 0; + s_rx_idx = 0; + g_bl_ep_table.EP[2].OUT.CNT = 0; + while (USB0.INTFLAGSB & USB_RMWBUSY_bm) {} + USB0.STATUS[2].OUTCLR = USB_BUSNAK_bm; + } + + /* TX pump */ + if (!s_tx_in_flight + && s_tx_len > 0 + && (g_bl_ep_table.EP[3].IN.STATUS & USB_BUSNAK_bm)) { + g_bl_ep_table.EP[3].IN.CNT = s_tx_len; + while (USB0.INTFLAGSB & USB_RMWBUSY_bm) {} + USB0.STATUS[3].INCLR = USB_BUSNAK_bm; + s_tx_in_flight = true; + s_tx_len = 0; + } +} + +/* ============================================================ + * RX byte fetch. Returns -1 if no byte is currently buffered. + * ============================================================ */ +int cdc_min_rx_pop(void) { + if (s_rx_idx >= s_rx_cnt) return -1; + return g_bl_ep2_out[s_rx_idx++]; +} + +/* ============================================================ + * TX byte push. Buffers into the EP3 IN packet. If the packet + * fills, transparently waits for the previous packet to drain + * by spinning on usb_min_poll() until BUSNAK comes back. + * ============================================================ */ +extern void usb_min_poll(void); + +void cdc_min_tx_byte(uint8_t b) { + /* If the staging buffer is full, ship it and wait. */ + if (s_tx_len >= USB_BL_EP3_SIZE) { + /* Force the pump; spin on USB until the in-flight transfer + * completes and BUSNAK is set again. */ + while (s_tx_in_flight || s_tx_len >= USB_BL_EP3_SIZE) { + cdc_min_poll(); + usb_min_poll(); + } + } + g_bl_ep3_in[s_tx_len++] = b; +} + +/* ============================================================ + * TX flush. Push whatever's staged regardless of how full the + * packet is. Caller is responsible for polling afterwards if + * it cares about the bytes being on the wire before returning. + * ============================================================ */ +void cdc_min_flush(void) { + /* If the staging buffer is empty there is nothing to flush. */ + if (s_tx_len == 0 && !s_tx_in_flight) return; + + /* Wait until the endpoint is ready and we have something to send. */ + while (s_tx_in_flight || s_tx_len == 0) { + cdc_min_poll(); + usb_min_poll(); + if (!s_tx_in_flight && s_tx_len == 0) return; + } + + /* Arm the partial packet. */ + g_bl_ep_table.EP[3].IN.CNT = s_tx_len; + while (USB0.INTFLAGSB & USB_RMWBUSY_bm) {} + USB0.STATUS[3].INCLR = USB_BUSNAK_bm; + s_tx_in_flight = true; + s_tx_len = 0; +} diff --git a/megaavr/bootloaders/usbcdcboot/src/cdc_min.h b/megaavr/bootloaders/usbcdcboot/src/cdc_min.h new file mode 100644 index 00000000..9c19a55e --- /dev/null +++ b/megaavr/bootloaders/usbcdcboot/src/cdc_min.h @@ -0,0 +1,30 @@ +/* + * usbcdcboot/src/cdc_min.h + * -------------------------------------------------------------------- + * Clean-room implementation. Reference: USB CDC 1.20 + PSTN 1.20. + * License: LGPL 2.1. + * + * Minimal CDC ACM API for the bootloader. Provides byte-level + * push/pop functions on top of the bulk IN/OUT pipes managed by + * usb_min. No ring buffers - the bootloader processes one packet + * at a time. + */ +#ifndef AVRDU_BL_CDC_MIN_H +#define AVRDU_BL_CDC_MIN_H + +#include + +void cdc_min_init(void); +void cdc_min_poll(void); /* called from main loop */ + +/* Pop one received byte; returns -1 if none available. */ +int cdc_min_rx_pop(void); + +/* Push one byte to the host. Buffered internally; flush via the + * regular poll cycle or explicitly via cdc_min_flush(). */ +void cdc_min_tx_byte(uint8_t b); + +/* Force any buffered TX bytes onto the wire immediately. */ +void cdc_min_flush(void); + +#endif /* AVRDU_BL_CDC_MIN_H */ diff --git a/megaavr/bootloaders/usbcdcboot/src/main.c b/megaavr/bootloaders/usbcdcboot/src/main.c new file mode 100644 index 00000000..827fd508 --- /dev/null +++ b/megaavr/bootloaders/usbcdcboot/src/main.c @@ -0,0 +1,386 @@ +/* + * avrdu_cdc_bl/src/main.c + * -------------------------------------------------------------------- + * Clean-room implementation. References: + * - AVR64DU32 datasheet (Microchip DS40002676A or later) + * - Atmel AVR061 - STK500 Communication Protocol App Note + * - USB 2.0 specification, USB CDC PSTN 1.20 specification + * - Microchip ATPACK device headers (avr/io.h) + * No source from Optiboot, LUFA, TinyUSB, V-USB or any other + * bootloader / USB project was consulted while writing this file. + * + * Role: + * Entry point of the AVRDU CDC bootloader. Decides whether to + * remain in the bootloader for an avrdude session or jump to the + * application at 0x1000, and runs the main poll loop while + * resident. + * + * License: LGPL 2.1 (matches the host DxCore repository). + */ + +#include +#include +#include +#include +#include + +#include "usb_min.h" +#include "cdc_min.h" +#include "stk500.h" +#include "nvm.h" + +/* ----- Coordination with the runtime (mirrored in runtime/usb_cdc.c) ----- + * + * Stay-in-bootloader handshake: the runtime's 1200 bps touch issues a + * SOFTWARE RESET (RSTCTRL.SWRR). That sets RSTCTRL.RSTFR.SWRF, which the + * datasheet guarantees is retained across the reset ("After any Reset, the + * source that caused the Reset is found in the Reset Flag"). main() stays + * resident whenever it sees SWRF. + * + * An earlier scheme used a magic word in GPR1/GPR2 (and before that, SRAM + * 0x7FFE). Both were dead ends: the datasheet lists the GPRn reset value as + * 0x00, so the reset clears the GPRs before main() can read them, and 0x7FFE + * is clobbered by the first crt0 CALL push (RAMEND is 0x7FFF). The reset + * flag is the only store that survives the reset by design. + * + * GPR.GPR0 carries the reset cause to the application (the Optiboot/DxCore + * convention); main() writes it there before clearing RSTFR. */ +#define APP_RESET_VECTOR_BYTE 0x1000 /* must match BOOTEND in fuse */ +#define APP_RESET_VECTOR_WORD (APP_RESET_VECTOR_BYTE >> 1) + +/* Double-tap reset state. Kept in .noinit so it survives the button-press + * reset: SRAM is retained across a non-POR reset and crt0 never touches the + * .noinit section. DBLTAP_ARMED is a distinctive value, so random SRAM at + * power-on is very unlikely to look armed - and the POR/BOD path in main() + * clears it before the first button press can ever be evaluated. */ +#define DBLTAP_ARMED 0x6B9Du +static volatile uint16_t s_dbltap_flag __attribute__((section(".noinit"))); + +/* -------------------------------------------------------------------- + * DFU-mode LED indicator (default PA7, active LOW) + * + * The LED is driven only while the bootloader is in its "stay" loop, so + * the user can see at a glance whether the chip is in BL mode or running + * the application. We encode WHY the BL is staying in the blink rate: + * - software reset (1200 bps touch from the host) -> fast ~4-5 Hz + * - double-tap on RESET (button) -> medium ~2 Hz + * - blank application slot -> slow ~1 Hz + * This is the equivalent of the Caterina/Pro-Micro pulse pattern, but + * much simpler: just a counter-driven OUTTGL, no timer needed. + * + * Default pin is PA7, matching DxCore's optiboot LED convention + * (LED=A7) for the parts that have it: the 20/28/32-pin DU packages. + * The 14-pin DU (16/32DU14) has no PA7, so those builds override the + * pin to PD6 -- the same split optiboot uses for its *dd vs *dd14 + * classes. There is no PD4 fallback as in optiboot's 14-pin UART + * case: this is a USB bootloader, so the LED never collides with a + * UART pin position. Active LOW: drive the pin LOW to light the LED; + * if your board's LED is active HIGH, swap OUTCLR/OUTSET in + * bl_led_on()/bl_led_off_state() below. Override the pin at build + * time with -DBL_LED_PORT=PORTx -DBL_LED_PIN=n, e.g. PD6 for a 14-pin + * DU (-DBL_LED_PORT=PORTD -DBL_LED_PIN=6) or PF2 for the Curiosity + * Nano LED0 (-DBL_LED_PORT=PORTF -DBL_LED_PIN=2). + * -------------------------------------------------------------------- */ +#ifndef BL_LED_PORT +#define BL_LED_PORT PORTA +#endif +#ifndef BL_LED_PIN +#define BL_LED_PIN 7 +#endif +#define BL_LED_PIN_BM (1u << BL_LED_PIN) + +static uint16_t s_led_period_counts; /* main-loop iters per toggle */ + +static inline void bl_led_init(uint16_t period_counts) { + s_led_period_counts = period_counts; + BL_LED_PORT.DIRSET = BL_LED_PIN_BM; + BL_LED_PORT.OUTSET = BL_LED_PIN_BM; /* LED off (output high) */ +} + +static inline void bl_led_deinit(void) { + BL_LED_PORT.OUTSET = BL_LED_PIN_BM; /* off */ + BL_LED_PORT.DIRCLR = BL_LED_PIN_BM; /* back to input */ +} + +static inline void bl_led_toggle(void) { + BL_LED_PORT.OUTTGL = BL_LED_PIN_BM; +} + +static inline void bl_led_on(void) { + BL_LED_PORT.OUTCLR = BL_LED_PIN_BM; /* active LOW */ +} + +static inline void bl_led_off_state(void) { + BL_LED_PORT.OUTSET = BL_LED_PIN_BM; +} + +/* ------------------------------------------------------------------ + * Coarse busy-wait calibrated for the 4 MHz reset-default clock. + * + * The double-tap window runs in the decision phase, BEFORE clocks_init() + * promotes OSCHF to 24 MHz (the reset-default main clock is OSCHF at + * 4 MHz with our OSCCFG fuse - datasheet 12.3.3). A volatile inner loop + * compiles to ~4 cycles/iteration, so ~1000 iterations per ms at 4 MHz. + * The exact length is not critical; the window only needs to be roughly + * half a second. + * ------------------------------------------------------------------ */ +static void bl_wait_ms_4mhz(uint16_t ms) { + while (ms--) { + for (volatile uint16_t i = 0; i < 1000U; i++) { + __asm__ __volatile__("nop"); + } + } +} + +/* Double-tap reset window. Called on the FIRST external (button) reset, + * after s_dbltap_flag has been armed: wait briefly with a visible LED + * flicker that invites a second tap. If the user presses RESET again + * within this window, that reset re-enters main() with the flag still + * armed, and we go straight to DFU. If the window elapses, the caller + * disarms and the application is started. + * + * ~500 ms is the usual compromise: long enough to double-tap comfortably, + * short enough that an ordinary single press does not noticeably delay the + * application restart. */ +#define DBLTAP_WINDOW_MS 500U +static void bl_dbltap_window(void) { + BL_LED_PORT.DIRSET = BL_LED_PIN_BM; + for (uint8_t i = 0; i < (DBLTAP_WINDOW_MS / 50U); i++) { + bl_led_on(); + bl_wait_ms_4mhz(25); + bl_led_off_state(); + bl_wait_ms_4mhz(25); + } + BL_LED_PORT.OUTSET = BL_LED_PIN_BM; /* off */ + BL_LED_PORT.DIRCLR = BL_LED_PIN_BM; /* release the LED pin for the application */ +} + +/* ----- WDT timeout we ask the silicon to apply when leaving prog mode ----- */ +#define WDT_PERIOD_EXIT_gc WDT_PERIOD_8CLK_gc /* ~8 ms */ + +/* -------------------------------------------------------------------- + * Vector-table relocation + * + * The AVR DU has IVSEL (in CPUINT.CTRLA) which makes the CPU read + * interrupt vectors from the BOOT section instead of APPCODE. The + * bootloader does not actually use any interrupts (we poll), but + * setting IVSEL is still required so that the reset vector at 0x0000 + * takes us into BOOT, not into APPCODE. This is normally the silicon + * default after a reset when running from BOOT, but we make it + * explicit so we don't depend on undocumented startup behaviour. + * Conversely, before jumping to the app we clear IVSEL so that the + * app's own interrupt handlers in APPCODE are reachable. + * -------------------------------------------------------------------- */ +static inline void vectors_to_boot(void) { + _PROTECTED_WRITE(CPUINT.CTRLA, CPUINT.CTRLA | CPUINT_IVSEL_bm); +} + +static inline void vectors_to_app(void) { + _PROTECTED_WRITE(CPUINT.CTRLA, CPUINT.CTRLA & (uint8_t)~CPUINT_IVSEL_bm); +} + +/* -------------------------------------------------------------------- + * Clock setup - AVR DU needs OSCHF running at one of the + * USB-compatible frequencies (12/16/20/24 MHz) and the USB peripheral + * selects its own 48 MHz internal oscillator automatically when + * enabled. We pick 24 MHz here for the core. + * See datasheet 11.5 (CLKCTRL) and 28.3.4 (USB clocking). + * -------------------------------------------------------------------- */ +static void clocks_init(void) { + /* Read-modify-write OSCHFCTRLA: keep the AUTOTUNE field's reset + * value (will be promoted to SOF tracking by usb_min_init), and + * set FRQSEL to 0x9 = 24 MHz. Bits [5:2] hold FRQSEL. */ + uint8_t oschf = CLKCTRL.OSCHFCTRLA; + oschf = (oschf & ~(0x0F << 2)) | CLKCTRL_FRQSEL_24M_gc; + _PROTECTED_WRITE(CLKCTRL.OSCHFCTRLA, oschf); + + /* Disable main clock prescaler so the CPU runs at the full + * OSCHF rate (24 MHz). */ + _PROTECTED_WRITE(CLKCTRL.MCLKCTRLB, 0x00); + + /* Wait until OSCHF is stable. */ + while (!(CLKCTRL.MCLKSTATUS & CLKCTRL_OSCHFS_bm)) { /* spin */ } +} + +/* -------------------------------------------------------------------- + * Heuristic: is the application slot blank? + * + * Blank flash reads as 0xFFFF. If the first instruction of the app + * (the RJMP / JMP at byte 0x1000) is 0xFFFF, there is nothing to jump + * to and we must stay in the bootloader. This protects users who + * fuse the bootloader before any sketch has been uploaded. + * + * IMPORTANT: byte 0x1000 is in Flash *code* space. Per datasheet 8.2 + * / Figure 8-1, data space 0x1000 is the extended I/O area, NOT + * Flash. Flash is visible in data space only via the FLMAP window at + * 0x8000-0xFFFF. We therefore use LPM (via pgm_read_word_near) to + * read the app reset vector directly from code space, where address + * 0x1000 means Flash byte 0x1000 regardless of FLMAP. + * -------------------------------------------------------------------- */ +#include +static bool app_appears_invalid(void) { + return (pgm_read_word_near(APP_RESET_VECTOR_BYTE) == 0xFFFF); +} + +/* -------------------------------------------------------------------- + * Jump to application. + * + * Implementation note (datasheet 15.5.1): + * + * "A system reset will cause the Program Counter to be reset to + * 0x0000, regardless of the IVSEL bit value." + * + * This rules out a SW reset as the exit path - we would end up back + * in the bootloader's reset vector. Instead we IJMP directly to the + * app's reset vector at byte 0x1000 == word 0x0800. The app's + * startup code (crt*) sets up its own SP, copies .data, clears .bss + * and lands in main, so we do not need to scrub SRAM here. + * + * AVR64DU32 has a 16-bit PC (max byte address 0xFFFF = word 0x7FFF), + * so IJMP via Z is sufficient; EIJMP/EIND is not needed. + * -------------------------------------------------------------------- */ +__attribute__((noreturn)) +static void jump_to_app(void) { + /* Disable interrupts; the app re-enables them when ready. */ + cli(); + + /* Turn the DFU LED off and release the LED pin back to input so the app sees + * a clean GPIO state (and the LED doesn't stick "on" mid-startup). */ + bl_led_deinit(); + + /* Clear IVSEL so the CPU reads vectors from APPCODE (start at 0x1000). */ + vectors_to_app(); + + /* Quiesce any peripheral we may have started up. Just USB so far. */ + USB0.CTRLA = 0; + USB0.CTRLB = 0; + + /* Restore FLMAP to its reset value (SECTION3) so the app starts with + * a cold-boot FLMAP state. Per datasheet table 11-8, NVMCTRL.CTRLB + * requires the CCP IOREG key (0xD8). */ + { + uint8_t new_ctrlb = (NVMCTRL.CTRLB & ~NVMCTRL_FLMAP_gm) | (3u << NVMCTRL_FLMAP_gp); + _PROTECTED_WRITE(NVMCTRL.CTRLB, new_ctrlb); + } + + /* IJMP to word address (APP_RESET_VECTOR_BYTE / 2) = 0x0800. + * Z = (high << 8) | low. */ + __asm__ __volatile__ ( + "ldi r30, %[lo] \n\t" + "ldi r31, %[hi] \n\t" + "ijmp \n\t" + : + : [lo] "M" ((uint8_t)(APP_RESET_VECTOR_WORD & 0xFF)), + [hi] "M" ((uint8_t)((APP_RESET_VECTOR_WORD >> 8) & 0xFF)) + : "r30", "r31" + ); + + __builtin_unreachable(); +} + +/* -------------------------------------------------------------------- + * Trigger WDT, used after avrdude sends LEAVE_PROGMODE. + * -------------------------------------------------------------------- */ +__attribute__((noreturn)) +void bl_exit_via_wdt(void) { + /* LED off and pin released. The chip is about to WDT-reset and the + * post-reset app will configure the LED pin as it wishes. The WDT reset sets + * WDRF (not SWRF), so the next boot runs the freshly uploaded app. */ + bl_led_deinit(); + + /* Detach USB so the host sees us go away. */ + usb_min_detach(); + + _PROTECTED_WRITE(WDT.CTRLA, WDT_PERIOD_EXIT_gc); + while (1) { } +} + +/* ==================================================================== + * Entry point + * ==================================================================== */ +int main(void) { + /* 1. Read the reset cause, hand it to the application via GPR0 (the + * Optiboot/DxCore convention - the app is built with -DUSING_OPTIBOOT + * so its own init_reset_flags() is compiled out and it relies on us + * to have stashed the cause here), then clear the flags so the next + * reset's cause is unambiguous and a stale SWRF cannot pin us in DFU. */ + uint8_t rstfr = RSTCTRL.RSTFR; + GPR.GPR0 = rstfr; + RSTCTRL.RSTFR = 0xFF; + + /* 2. Make sure the CPU is reading vectors from BOOT. Required for + * correct ISR dispatch even though we currently poll. */ + vectors_to_boot(); + + /* 3. Decide which path to take. Priority: the runtime's explicit + * request (software reset -> SWRF) wins; then a double tap on the + * RESET button; then the blank-app guard; otherwise run the app. + * + * SWRF is the host handshake: the runtime's 1200 bps touch issues a + * software reset (RSTCTRL.SWRR), the only ordinary source of SWRF, so + * seeing it here means "the host asked for the bootloader". (This + * replaced an earlier GPR-magic scheme that could never work, because + * the GPRs are reset to 0x00 before main() can read them.) + * + * EXTRF is the button. Arduino-style double tap: a lone press runs + * the application; a second press within a short window enters DFU. + * The "armed" state is held in s_dbltap_flag across the second reset. */ + bool stay_in_bl = false; + uint16_t led_period = 0; /* main-loop iters per LED toggle */ + + if (rstfr & RSTCTRL_SWRF_bm) { + s_dbltap_flag = 0; + stay_in_bl = true; + led_period = 8000U; /* fast blink (~4-5 Hz) - host (1200bps touch) */ + } else if (rstfr & RSTCTRL_EXTRF_bm) { + if (s_dbltap_flag == DBLTAP_ARMED) { + s_dbltap_flag = 0; /* consume the armed state */ + stay_in_bl = true; + led_period = 16000U; /* medium blink (~2 Hz) - double tap */ + } else { + s_dbltap_flag = DBLTAP_ARMED; + bl_dbltap_window(); /* ~500 ms; a 2nd reset re-enters main */ + s_dbltap_flag = 0; /* window elapsed: it was a lone press */ + /* fall through to the app / blank-app handling below */ + } + } else { + /* POR / BOD / WDT / undefined: clear any stale double-tap state so a + * subsequent first press starts clean (POR leaves SRAM random). */ + s_dbltap_flag = 0; + } + + if (!stay_in_bl) { + if (app_appears_invalid()) { + stay_in_bl = true; + led_period = 32000U; /* slow blink (~1 Hz) - blank app */ + } else { + jump_to_app(); /* normal boot to the application */ + } + } + + /* 4. Stay in the bootloader. Bring up the clock tree and USB. */ + clocks_init(); + usb_min_init(); + cdc_min_init(); + stk500_init(); + bl_led_init(led_period); + usb_min_attach(); + + /* 6. Polling loop. All three modules cooperate via the bulk + * OUT/IN pipes managed by usb_min. stk500_poll() consumes + * one byte from CDC RX, runs the state machine, and pushes + * responses back to CDC TX. The LED counter is purely a visual + * indicator that we are in DFU mode; the encoded blink rate also + * tells the user which entry path was taken (magic vs button vs + * blank-app). */ + uint16_t led_count = 0; + for (;;) { + usb_min_poll(); + cdc_min_poll(); + stk500_poll(); + if (++led_count >= s_led_period_counts) { + led_count = 0; + bl_led_toggle(); + } + } +} diff --git a/megaavr/bootloaders/usbcdcboot/src/nvm.c b/megaavr/bootloaders/usbcdcboot/src/nvm.c new file mode 100644 index 00000000..0283d901 --- /dev/null +++ b/megaavr/bootloaders/usbcdcboot/src/nvm.c @@ -0,0 +1,177 @@ +/* + * usbcdcboot/src/nvm.c + * -------------------------------------------------------------------- + * Clean-room implementation. Reference: AVR64DU32 datasheet + * section 11 (NVMCTRL) and section 6.5 (CCP protection). No code + * from Optiboot or any other AVR Dx self-program implementation was + * consulted. + * + * License: LGPL 2.1. + */ + +#include +#include +#include + +#include "nvm.h" + +/* -------------------------------------------------------------------- + * Issue a CCP-protected NVMCTRL command. Datasheet 11.5.1.1: + * certain NVMCTRL.CTRLA writes require the prior write of 0x9D to + * the CCP register within four clocks. + * -------------------------------------------------------------------- */ +static inline void nvm_cmd(uint8_t cmd) { + while (NVMCTRL.STATUS & NVMCTRL_FLBUSY_bm) { /* spin */ } + CCP = CCP_SPM_gc; /* 0x9D */ + NVMCTRL.CTRLA = cmd; +} + +/* -------------------------------------------------------------------- + * Flash page erase. Datasheet 11.3.3: write FLPER then perform a + * store to any address inside the target page. + * + * AVR DU has the data flash mapped at the start of program memory, + * so a plain ST instruction (which `*p = ...` becomes) triggers the + * programming sequence as long as the FLMAP setting points at the + * right 32 KB window. For 64 KB AVR64DU32 we explicitly select + * the right FLMAP section before the store. + * -------------------------------------------------------------------- */ +static void flash_page_erase(uint32_t byte_addr) { + /* Select FLMAP section so that byte_addr is reachable via the + * lower 32 KB data-space window. Section 0 covers 0x0000..0x7FFF, + * Section 1 covers 0x8000..0xFFFF. */ + uint8_t section = (byte_addr >= 0x8000UL) ? 1 : 0; + /* NVMCTRL.CTRLB is CCP-IOREG protected (datasheet table 11-8). + * Compute the new value first, then do the protected write so the + * CCP-unlock-and-store sequence stays inside the four-cycle window. */ + uint8_t new_ctrlb = (NVMCTRL.CTRLB & ~NVMCTRL_FLMAP_gm) + | (section << NVMCTRL_FLMAP_gp); + _PROTECTED_WRITE(NVMCTRL.CTRLB, new_ctrlb); + + /* Compute the data-space pointer corresponding to byte_addr. + * Per datasheet 8.3 / Table 8-1, Flash is mapped to data space at + * 0x8000..0xFFFF, so we OR the low 15 bits of byte_addr with 0x8000. */ + volatile uint8_t *p = + (volatile uint8_t *)(0x8000u | (uint16_t)(byte_addr & 0x7FFFu)); + + nvm_cmd(NVMCTRL_CMD_FLPER_gc); + *p = 0xFF; /* dummy store triggers erase */ + while (NVMCTRL.STATUS & NVMCTRL_FLBUSY_bm) { /* spin */ } + nvm_cmd(NVMCTRL_CMD_NOOP_gc); +} + +/* -------------------------------------------------------------------- + * Flash word-write. Datasheet 11.3.4: FLWR command + word stores. + * We perform N two-byte stores; the silicon programs them as it + * goes. Hardware enforces that no programming happens to BOOT. + * -------------------------------------------------------------------- */ +static void flash_page_write(uint32_t byte_addr, + const uint8_t *data, + uint16_t nbytes) { + uint8_t section = (byte_addr >= 0x8000UL) ? 1 : 0; + uint8_t new_ctrlb = (NVMCTRL.CTRLB & ~NVMCTRL_FLMAP_gm) + | (section << NVMCTRL_FLMAP_gp); + _PROTECTED_WRITE(NVMCTRL.CTRLB, new_ctrlb); + + volatile uint16_t *p = + (volatile uint16_t *)(0x8000u | (uint16_t)(byte_addr & 0x7FFFu)); + + nvm_cmd(NVMCTRL_CMD_FLWR_gc); + uint16_t i = 0; + while (i + 1 < nbytes) { + uint16_t w = (uint16_t)data[i] | ((uint16_t)data[i+1] << 8); + *p++ = w; + i += 2; + while (NVMCTRL.STATUS & NVMCTRL_FLBUSY_bm) { /* spin */ } + } + if (i < nbytes) { + /* Tail byte: write paired with 0xFF padding so silicon + * receives a full word. */ + uint16_t w = (uint16_t)data[i] | 0xFF00u; + *p = w; + while (NVMCTRL.STATUS & NVMCTRL_FLBUSY_bm) { /* spin */ } + } + nvm_cmd(NVMCTRL_CMD_NOOP_gc); +} + +/* -------------------------------------------------------------------- + * Public: erase target page and write `nbytes` bytes into it. + * + * The caller already aligns byte_addr to a 512-byte boundary; + * STK500's LOAD_ADDRESS + PROG_PAGE sequence from avrdude does this + * naturally for the AVR-style ".hex" record stream. + * -------------------------------------------------------------------- */ +void nvm_write_page(uint32_t byte_addr, + const uint8_t *data, + uint16_t nbytes) { + if (byte_addr < 0x1000UL) { + /* Refuse to clobber the BOOT section. Silicon already + * enforces this, but bailing early avoids confusing + * FBUSY hangs. */ + return; + } + if (nbytes == 0) return; + if (nbytes > NVM_FLASH_PAGE_SIZE) nbytes = NVM_FLASH_PAGE_SIZE; + + flash_page_erase(byte_addr); + flash_page_write(byte_addr, data, nbytes); +} + +/* -------------------------------------------------------------------- + * Public: read one byte from flash, via the FLMAP window. + * -------------------------------------------------------------------- */ +uint8_t nvm_read_byte(uint32_t byte_addr) { + uint8_t section = (byte_addr >= 0x8000UL) ? 1 : 0; + uint8_t new_ctrlb = (NVMCTRL.CTRLB & ~NVMCTRL_FLMAP_gm) + | (section << NVMCTRL_FLMAP_gp); + _PROTECTED_WRITE(NVMCTRL.CTRLB, new_ctrlb); + + volatile const uint8_t *p = + (volatile const uint8_t *)(0x8000u | (uint16_t)(byte_addr & 0x7FFFu)); + return *p; +} + +/* -------------------------------------------------------------------- + * EEPROM erase+write. Datasheet 11.3.2.3.5 (EEERWR) and 8.6: + * - EEPROM is mapped into data space at 0x1400 (256 B), always + * visible - no FLMAP window needed (it sits below 0x8000). + * - With EEERWR enabled, each ST* erases+writes one byte. + * Sequence (datasheet 11.3): wait not-busy -> CCP(SPM) -> write + * EEERWR to CTRLA -> ST per byte -> NOOP. CTRLA is SPM-protected, + * matching nvm_cmd() above; STATUS.EEBUSY mirrors FLBUSY. + * -------------------------------------------------------------------- */ +#define EE_MAPPED_BASE 0x1400u /* AVR64DU32 EEPROM mapped address (datasheet 8.6) */ + +void nvm_write_eeprom(uint16_t ee_addr, + const uint8_t *data, + uint16_t nbytes) { + if (ee_addr >= NVM_EEPROM_SIZE) return; + if ((uint32_t)ee_addr + nbytes > NVM_EEPROM_SIZE) { + nbytes = (uint16_t)(NVM_EEPROM_SIZE - ee_addr); + } + if (nbytes == 0) return; + + volatile uint8_t *p = (volatile uint8_t *)(EE_MAPPED_BASE + ee_addr); + + /* No NVM op in progress (datasheet 11.5.6 CMDCOLLISION guard). */ + while (NVMCTRL.STATUS & (NVMCTRL_EEBUSY_bm | NVMCTRL_FLBUSY_bm)) { /* spin */ } + + /* Enable EEPROM erase+write mode (CCP-protected CTRLA write). */ + CCP = CCP_SPM_gc; /* 0x9D */ + NVMCTRL.CTRLA = NVMCTRL_CMD_EEERWR_gc; + + for (uint16_t i = 0; i < nbytes; i++) { + p[i] = data[i]; /* each store erases+writes one byte */ + while (NVMCTRL.STATUS & NVMCTRL_EEBUSY_bm) { /* spin */ } + } + + CCP = CCP_SPM_gc; + NVMCTRL.CTRLA = NVMCTRL_CMD_NOOP_gc; +} + +uint8_t nvm_read_eeprom(uint16_t ee_addr) { + if (ee_addr >= NVM_EEPROM_SIZE) return 0xFF; + volatile const uint8_t *p = + (volatile const uint8_t *)(EE_MAPPED_BASE + ee_addr); + return *p; +} diff --git a/megaavr/bootloaders/usbcdcboot/src/nvm.h b/megaavr/bootloaders/usbcdcboot/src/nvm.h new file mode 100644 index 00000000..4c5c69c9 --- /dev/null +++ b/megaavr/bootloaders/usbcdcboot/src/nvm.h @@ -0,0 +1,60 @@ +/* + * usbcdcboot/src/nvm.h + * -------------------------------------------------------------------- + * Clean-room implementation. Reference: AVR64DU32 datasheet + * section 11 (NVMCTRL). License: LGPL 2.1. + */ +#ifndef AVRDU_BL_NVM_H +#define AVRDU_BL_NVM_H + +#include + +/* AVR64DU32 flash organization (datasheet section 11.2): + * - Page size: 512 bytes + * - 64 KB total, 128 pages + * - BOOT, APPCODE, APPDATA regions divided by BOOTSIZE/CODESIZE fuses + * + * Self-write rules (datasheet 11.3.7): + * - Code in BOOT can write to APPCODE / APPDATA only + * - Code in APPCODE can write to APPDATA only (and only if APPCODE + * is unlocked) + * - BOOT is always locked against self-write + * + * The functions below assume they are called from BOOT and that the + * target byte_addr lies in APPCODE (>= 0x1000 with BOOTSIZE=0x08). + */ +#define NVM_FLASH_PAGE_SIZE 512u + +/* Erase the flash page containing byte_addr, then write `nbytes` + * bytes from `data` starting at byte_addr. `nbytes` is typically a + * multiple of 2 (word writes); if it is odd, the trailing byte is + * written as a byte and then padded with 0xFF. */ +void nvm_write_page(uint32_t byte_addr, + const uint8_t *data, + uint16_t nbytes); + +/* Read one byte from flash. Use mapped flash or LPM as appropriate. + * The bootloader only needs this for READ_PAGE responses, which the + * arduino programmer uses for verify passes. */ +uint8_t nvm_read_byte(uint32_t byte_addr); + +/* -------------------------------------------------------------------- + * EEPROM (datasheet 11.3.2.3.5 EEERWR, 8.6 mapped at 0x1400, 256 B). + * + * Addressing: this bootloader uses the DxCore/Optiboot_dx convention + * where avrdude sends BYTE addresses in STK_LOAD_ADDRESS for every + * memory. So `ee_addr` is the byte offset into EEPROM (0..255), + * taken straight from g_byte_addr - no scaling, exactly like flash. + * -------------------------------------------------------------------- */ +#define NVM_EEPROM_SIZE 256u + +/* Erase+write `nbytes` into EEPROM from byte offset `ee_addr`. + * Out-of-range requests are clamped/ignored. */ +void nvm_write_eeprom(uint16_t ee_addr, + const uint8_t *data, + uint16_t nbytes); + +/* Read one EEPROM byte at offset `ee_addr` (0..255). */ +uint8_t nvm_read_eeprom(uint16_t ee_addr); + +#endif /* AVRDU_BL_NVM_H */ diff --git a/megaavr/bootloaders/usbcdcboot/src/spm_entry.c b/megaavr/bootloaders/usbcdcboot/src/spm_entry.c new file mode 100644 index 00000000..30d6466c --- /dev/null +++ b/megaavr/bootloaders/usbcdcboot/src/spm_entry.c @@ -0,0 +1,26 @@ +/* spm_entry.c - Application-callable SPM entry point. + * + * Generalizes the Optiboot_dx convention (spm z+; ret at the last 6 bytes + * of the boot section) to this 4 KB bootloader: the stub lives at + * BOOTSIZE-6 = 0x0FFA and a version word at BOOTSIZE-2 = 0x0FFE. + * The application's Flash library sets up NVMCTRL.CTRLA and Z (+RAMPZ), + * then calls 0x0FFA; because the stub executes from the BOOT section it + * is allowed to program the APPCODE/APPDATA sections. + * + * Build with -DAPP_NOSPM to ship the bootloader with this entry disabled + * (nop; ret - the 0x0000 opcode signals "disabled" to the Flash library). + */ +#include + +#ifndef APP_NOSPM +const uint32_t __attribute__((section(".spmtarg"), used)) + bl_spm_entry = 0x950895F8UL; /* spm z+ ; ret */ +#else +const uint32_t __attribute__((section(".spmtarg"), used)) + bl_spm_entry = 0x95080000UL; /* nop ; ret */ +#endif + +/* Bootloader identity/version word, read by FlashClass::checkWritable(). + * 0x1Axx = Wazamono AVR-DU CDC bootloader family (Optiboot_dx uses 0x19xx). */ +const uint16_t __attribute__((section(".blversion"), used)) + bl_version = 0x1A01; diff --git a/megaavr/bootloaders/usbcdcboot/src/stk500.c b/megaavr/bootloaders/usbcdcboot/src/stk500.c new file mode 100644 index 00000000..81d677d9 --- /dev/null +++ b/megaavr/bootloaders/usbcdcboot/src/stk500.c @@ -0,0 +1,355 @@ +/* + * avrdu_cdc_bl/src/stk500.c + * -------------------------------------------------------------------- + * Clean-room implementation. Reference: Atmel AVR061 STK500 + * Communication Protocol application note (publicly published). + * No code from Optiboot or any other STK500 implementation was + * consulted while writing this. + * + * Implements just enough of STK500v1 for avrdude's "-c arduino" + * upload protocol to write the application section over USB CDC. + * + * License: LGPL 2.1. + */ + +#include +#include +#include +#include + +#include "stk500.h" +#include "cdc_min.h" +#include "nvm.h" + +/* Forward decl from main.c. */ +extern void bl_exit_via_wdt(void) __attribute__((noreturn)); + +/* ---------------------------------------------------------- + * Receive state machine. STK500v1 messages have the form + * CMD [args ...] CRC_EOP + * where the CMD byte determines how many argument bytes + * follow. We use a small lookup to remember the number of + * argument bytes to absorb. + * ---------------------------------------------------------- */ +typedef enum { + ST_IDLE, + ST_READ_ARGS, + ST_WAIT_EOP, + ST_PROG_PAGE_HEADER, /* 3 bytes: size_hi, size_lo, memtype */ + ST_PROG_PAGE_DATA, /* size bytes of payload, then CRC_EOP */ + ST_READ_PAGE_HEADER, /* 3 bytes: size_hi, size_lo, memtype */ +} state_t; + +static state_t g_state; +static uint8_t g_cmd; +static uint16_t g_byte_addr; /* current load address (in BYTES) */ +static uint8_t g_args_pending; +static uint8_t g_args[20]; /* SET_DEVICE is the longest arg list */ +static uint8_t g_args_idx; +static uint16_t g_pp_size; +static uint8_t g_pp_memtype; +static uint8_t g_page_buf[512]; /* AVR64DU32 page size */ +static uint16_t g_page_buf_idx; + +/* ---------------------------------------------------------- + * Small helpers (TX side) - push individual bytes through + * cdc_min's bulk-IN buffer. + * ---------------------------------------------------------- */ +static inline void put1(uint8_t b) { cdc_min_tx_byte(b); } +static inline void put2(uint8_t a, uint8_t b) { put1(a); put1(b); } +static inline void put_n(const uint8_t *p, uint16_t n) { + while (n--) put1(*p++); +} + +static inline void reply_insync_ok(void) { put2(STK_INSYNC, STK_OK); } +static inline void reply_insync_x_ok(uint8_t x) { put1(STK_INSYNC); put1(x); put1(STK_OK); } +static inline void reply_nosync(void) { put1(STK_NOSYNC); } + +/* ---------------------------------------------------------- + * How many argument bytes does each command carry? + * Returns 0xFF for "variable length, handled specially". + * ---------------------------------------------------------- */ +static uint8_t cmd_arglen(uint8_t cmd) { + switch (cmd) { + case STK_GET_SYNC: return 0; + case STK_GET_SIGN_ON: return 0; + case STK_GET_PARAMETER: return 1; + case STK_SET_PARAMETER: return 2; + case STK_SET_DEVICE: return 20; + case STK_SET_DEVICE_EXT: return 5; + case STK_ENTER_PROGMODE: return 0; + case STK_LEAVE_PROGMODE: return 0; + case STK_CHIP_ERASE: return 0; + case STK_LOAD_ADDRESS: return 2; + case STK_UNIVERSAL: return 4; + case STK_READ_SIGN: return 0; + case STK_PROG_PAGE: return 0xFF; /* handled specially */ + case STK_READ_PAGE: return 0xFF; /* handled specially */ + default: return 0xFE; /* unknown */ + } +} + +/* Forward decls for sentinels invoked from dispatch(). */ +static void prog_page_finalise(void); +static void read_page_emit(void); + +/* ---------------------------------------------------------- + * Dispatch a fully-received command (after CRC_EOP). + * ---------------------------------------------------------- */ +static void dispatch(void) { + /* Sentinels for the PROG_PAGE_DATA / READ_PAGE_HEADER paths land + * here too - handle them before falling through to the regular + * command table. */ + if (g_cmd == 0xF0) { prog_page_finalise(); return; } + if (g_cmd == 0xF1) { read_page_emit(); return; } + + switch (g_cmd) { + + case STK_GET_SYNC: + reply_insync_ok(); + break; + + case STK_GET_SIGN_ON: { + /* AVR061 says "AVR STK" (7 bytes) followed by OK. */ + static const uint8_t sig[] = "AVR STK"; + put1(STK_INSYNC); + put_n(sig, sizeof(sig) - 1); + put1(STK_OK); + break; + } + + case STK_GET_PARAMETER: { + /* Return a sane stub value for any parameter avrdude asks + * about. Real STK500 parameters are not used by the + * arduino programmer mode. */ + uint8_t param = g_args[0]; + uint8_t value; + if (param == 0x80) { value = 1; } /* HW VER major */ + else if (param == 0x81) { value = 8; } /* SW VER major */ + else if (param == 0x82) { value = 2; } /* SW VER minor */ + else { value = 0x03; } /* generic; matches optiboot_dx */ + reply_insync_x_ok(value); + break; + } + + case STK_SET_PARAMETER: + case STK_SET_DEVICE: + case STK_SET_DEVICE_EXT: + reply_insync_ok(); + break; + + case STK_ENTER_PROGMODE: + reply_insync_ok(); + break; + + case STK_LEAVE_PROGMODE: + put2(STK_INSYNC, STK_OK); + cdc_min_flush(); + /* Give the host a moment to receive the OK, then bail. */ + for (volatile uint32_t i = 0; i < 200000; i++) { /* ~10 ms @24 MHz */ } + bl_exit_via_wdt(); + /* not reached */ + break; + + case STK_CHIP_ERASE: + /* avrdude in arduino mode does not actually require this + * to do anything - the per-page write does erase+program. + * For safety we acknowledge it without action. */ + reply_insync_ok(); + break; + + case STK_LOAD_ADDRESS: + /* Little-endian word address. */ + /* AVR Dx/DU: avrdude sends a BYTE address here, not a word + * address. Store it as-is. */ + g_byte_addr = (uint16_t)g_args[0] | ((uint16_t)g_args[1] << 8); + reply_insync_ok(); + break; + + case STK_UNIVERSAL: + /* avrdude sends low-level SPI commands here; for the DU + * we have nothing to do, so return a dummy 0x00 byte. */ + reply_insync_x_ok(0x00); + break; + + case STK_READ_SIGN: + /* Report the running device's signature read live from SIGROW + * (datasheet sec. 33 Device IDs), not a build-time constant, + * so one per-class hex serves every package/flash-size variant + * sharing this build's RAM layout: avrdude always sees the true + * DEVICEID of the chip it is talking to (cf. STK_SIG_BYTE_* in + * stk500.h, kept only as the AVR64DU32 reference value). */ + put1(STK_INSYNC); + put1(SIGROW.DEVICEID0); + put1(SIGROW.DEVICEID1); + put1(SIGROW.DEVICEID2); + put1(STK_OK); + break; + + default: + reply_nosync(); + break; + } +} + +/* ---------------------------------------------------------- + * PROG_PAGE and READ_PAGE need their own micro-state machine + * because the size byte determines how many payload bytes + * follow before CRC_EOP. + * ---------------------------------------------------------- */ +static void prog_page_finalise(void) { + /* Write the buffer at the byte address avrdude loaded (AVR Dx/DU + * send byte addresses, so no doubling - same for flash and EEPROM). */ + if (g_pp_memtype == 'F' || g_pp_memtype == 'f') { + /* On AVR Dx/DU, avrdude sends BYTE addresses in STK_LOAD_ADDRESS + * (confirmed by DxCore Optiboot_dx "byte addressed!"). Use the + * loaded value directly - do NOT double it. */ + nvm_write_page((uint32_t)g_byte_addr, g_page_buf, g_pp_size); + } else if (g_pp_memtype == 'E' || g_pp_memtype == 'e') { + /* EEPROM byte offset, same byte-addressing convention as flash. */ + nvm_write_eeprom(g_byte_addr, g_page_buf, g_pp_size); + } + /* Other memtypes are acknowledged without action. */ + reply_insync_ok(); +} + +static void read_page_emit(void) { + put1(STK_INSYNC); + uint32_t byte_addr = (uint32_t)g_byte_addr; + for (uint16_t i = 0; i < g_pp_size; i++) { + uint8_t b; + if (g_pp_memtype == 'F' || g_pp_memtype == 'f') { + b = nvm_read_byte(byte_addr + i); + } else if (g_pp_memtype == 'E' || g_pp_memtype == 'e') { + b = nvm_read_eeprom((uint16_t)(g_byte_addr + i)); + } else { + b = 0xFF; + } + put1(b); + } + put1(STK_OK); +} + +/* ---------------------------------------------------------- + * Per-byte feeder. cdc_min_rx_pop() returns -1 if no byte + * is available, otherwise 0..255. + * ---------------------------------------------------------- */ +static void feed_byte(uint8_t b) { + switch (g_state) { + + case ST_IDLE: + g_cmd = b; + { + uint8_t n = cmd_arglen(g_cmd); + if (n == 0xFE) { + /* Unknown command. Wait for the EOP to clear the + * line, then NOSYNC. */ + g_state = ST_WAIT_EOP; + } else if (n == 0xFF) { + /* PROG_PAGE / READ_PAGE - read 3-byte header. */ + g_args_idx = 0; + g_args_pending = 3; + g_state = (g_cmd == STK_PROG_PAGE) + ? ST_PROG_PAGE_HEADER + : ST_READ_PAGE_HEADER; + } else if (n == 0) { + g_state = ST_WAIT_EOP; + } else { + g_args_idx = 0; + g_args_pending = n; + g_state = ST_READ_ARGS; + } + } + break; + + case ST_READ_ARGS: + g_args[g_args_idx++] = b; + if (--g_args_pending == 0) { + g_state = ST_WAIT_EOP; + } + break; + + case ST_WAIT_EOP: + if (b == STK_CRC_EOP) { + dispatch(); + } else { + reply_nosync(); + } + g_state = ST_IDLE; + break; + + case ST_PROG_PAGE_HEADER: + g_args[g_args_idx++] = b; + if (--g_args_pending == 0) { + /* avrdude sends size big-endian. */ + g_pp_size = ((uint16_t)g_args[0] << 8) | g_args[1]; + g_pp_memtype = g_args[2]; + if (g_pp_size > sizeof(g_page_buf)) { + /* Bad packet; abort and re-sync next round. */ + g_state = ST_WAIT_EOP; + } else { + g_page_buf_idx = 0; + if (g_pp_size == 0) { + g_state = ST_WAIT_EOP; + } else { + g_state = ST_PROG_PAGE_DATA; + } + } + } + break; + + case ST_PROG_PAGE_DATA: + g_page_buf[g_page_buf_idx++] = b; + if (g_page_buf_idx >= g_pp_size) { + /* All payload bytes received; next byte is CRC_EOP. */ + g_state = ST_IDLE; /* will be set below before dispatch */ + /* Re-use ST_IDLE -> ST_WAIT_EOP path: peek for next + * byte = EOP, then run prog_page_finalise. */ + g_cmd = 0xF0; /* sentinel for finalise */ + g_state = ST_WAIT_EOP; + } + break; + + case ST_READ_PAGE_HEADER: + g_args[g_args_idx++] = b; + if (--g_args_pending == 0) { + g_pp_size = ((uint16_t)g_args[0] << 8) | g_args[1]; + g_pp_memtype = g_args[2]; + if (g_pp_size > sizeof(g_page_buf)) { + g_state = ST_WAIT_EOP; + } else { + g_cmd = 0xF1; /* sentinel for read finalise */ + g_state = ST_WAIT_EOP; + } + } + break; + } + + /* If we hit the EOP sentinel for a finalise, do it now. */ + if (g_state == ST_IDLE) return; +} + +/* ---- Per-byte feeder, continued from feed_byte() below. The + * 0xF0 (PROG_PAGE finalise) and 0xF1 (READ_PAGE finalise) sentinel + * values used in g_cmd are routed through ST_WAIT_EOP and handled + * at the top of dispatch(). ---- */ + + +void stk500_init(void) { + g_state = ST_IDLE; + g_byte_addr = 0; + g_args_pending = 0; + g_args_idx = 0; + g_pp_size = 0; + g_pp_memtype = 0; + g_page_buf_idx = 0; + memset(g_args, 0, sizeof(g_args)); + memset(g_page_buf, 0xFF, sizeof(g_page_buf)); +} + +void stk500_poll(void) { + int c; + while ((c = cdc_min_rx_pop()) >= 0) { + feed_byte((uint8_t)c); + } +} diff --git a/megaavr/bootloaders/usbcdcboot/src/stk500.h b/megaavr/bootloaders/usbcdcboot/src/stk500.h new file mode 100644 index 00000000..4ef4d8d2 --- /dev/null +++ b/megaavr/bootloaders/usbcdcboot/src/stk500.h @@ -0,0 +1,50 @@ +/* + * avrdu_cdc_bl/src/stk500.h + * -------------------------------------------------------------------- + * Clean-room implementation. Reference: Atmel AVR061 - "STK500 + * Communication Protocol" application note (publicly published by + * Atmel/Microchip). No source consulted from Optiboot or any other + * STK500 implementation. + * License: LGPL 2.1. + */ +#ifndef AVRDU_BL_STK500_H +#define AVRDU_BL_STK500_H + +#include + +/* Protocol literals from AVR061 section 4 ("Command set"). */ +#define STK_OK 0x10 +#define STK_INSYNC 0x14 +#define STK_NOSYNC 0x15 +#define STK_CRC_EOP 0x20 + +#define STK_GET_SYNC 0x30 +#define STK_GET_SIGN_ON 0x31 +#define STK_GET_PARAMETER 0x41 +#define STK_SET_PARAMETER 0x40 +#define STK_SET_DEVICE 0x42 +#define STK_SET_DEVICE_EXT 0x45 +#define STK_ENTER_PROGMODE 0x50 +#define STK_LEAVE_PROGMODE 0x51 +#define STK_CHIP_ERASE 0x52 +#define STK_LOAD_ADDRESS 0x55 +#define STK_UNIVERSAL 0x56 +#define STK_PROG_PAGE 0x64 +#define STK_READ_PAGE 0x74 +#define STK_READ_SIGN 0x75 + +/* AVR64DU32 signature row from datasheet section 33 (Device IDs). + * Reference value only: STK_READ_SIGN now reports SIGROW.DEVICEID0..2 + * read at runtime, so the same hex serves all variants of a build class. + * SIGROW.DEVICEID0 = 0x1E + * SIGROW.DEVICEID1 = 0x96 + * SIGROW.DEVICEID2 = 0x21 (DU32, per datasheet Table 8-4). + * Note: 0x22 is the AVR64DU28 (28-pin) variant. */ +#define STK_SIG_BYTE_0 0x1E +#define STK_SIG_BYTE_1 0x96 +#define STK_SIG_BYTE_2 0x21 + +void stk500_init(void); +void stk500_poll(void); /* called from main loop; non-blocking */ + +#endif /* AVRDU_BL_STK500_H */ diff --git a/megaavr/bootloaders/usbcdcboot/src/usb_desc.c b/megaavr/bootloaders/usbcdcboot/src/usb_desc.c new file mode 100644 index 00000000..abaa3264 --- /dev/null +++ b/megaavr/bootloaders/usbcdcboot/src/usb_desc.c @@ -0,0 +1,168 @@ +/* + * usbcdcboot/src/usb_desc.c + * -------------------------------------------------------------------- + * Clean-room. Reference: USB 2.0 spec chapter 9, USB CDC 1.20, + * PSTN 1.20. No code from any other USB stack was consulted. + * License: LGPL 2.1. + * + * Descriptor tables for the AVRDU CDC bootloader. Plain CDC ACM + * device, two interfaces, one notification + two bulk endpoints. + */ + +#include "usb_desc.h" + +/* ============================================================ + * Device Descriptor (USB 2.0 Table 9-8) + * bDeviceClass = 0x02 (CDC) + * bDeviceSubClass = 0x00 + * bDeviceProtocol = 0x00 + * For a simple single-function CDC device this is the canonical + * way. No IAD needed since the bootloader has only one function. + * ============================================================ */ +const uint8_t g_bl_device_descriptor[18] = { + 18, /* bLength */ + DESC_TYPE_DEVICE, /* bDescriptorType */ + 0x00, 0x02, /* bcdUSB = 2.00 */ + 0x02, /* bDeviceClass = CDC */ + 0x00, /* bDeviceSubClass */ + 0x00, /* bDeviceProtocol */ + USB_BL_EP0_SIZE, /* bMaxPacketSize0 */ + (uint8_t)(USB_BL_VID & 0xFF), + (uint8_t)((USB_BL_VID >> 8) & 0xFF), + (uint8_t)(USB_BL_PID & 0xFF), + (uint8_t)((USB_BL_PID >> 8) & 0xFF), + (uint8_t)(USB_BL_DEVICE_VER & 0xFF), + (uint8_t)((USB_BL_DEVICE_VER >> 8) & 0xFF), + 0x01, /* iManufacturer */ + 0x02, /* iProduct */ + 0x03, /* iSerialNumber */ + 0x01 /* bNumConfigurations */ +}; + +/* ============================================================ + * Configuration Descriptor (67 bytes) + * ============================================================ */ +const uint8_t g_bl_config_descriptor[USB_BL_CONFIG_TOTAL_LEN] = { + + /* === Configuration Descriptor (9) === */ + 9, /* bLength */ + DESC_TYPE_CONFIG, /* bDescriptorType */ + (uint8_t)(USB_BL_CONFIG_TOTAL_LEN & 0xFF), + (uint8_t)(USB_BL_CONFIG_TOTAL_LEN >> 8), + USB_BL_NUM_INTERFACES, /* bNumInterfaces = 2 */ + 1, /* bConfigurationValue */ + 0, /* iConfiguration */ + 0x80, /* bmAttributes: bus-powered */ + 50, /* bMaxPower = 100 mA */ + + /* ====================================================== + * Interface 0: CDC Communication + * ====================================================== */ + 9, /* bLength */ + DESC_TYPE_INTERFACE, /* bDescriptorType */ + USB_BL_IF_CDC_COMM, /* bInterfaceNumber */ + 0, /* bAlternateSetting */ + 1, /* bNumEndpoints = 1 (notify) */ + 0x02, /* bInterfaceClass = CDC */ + 0x02, /* bInterfaceSubClass = ACM */ + 0x01, /* bInterfaceProtocol = AT cmds */ + 0, /* iInterface */ + + /* CDC Header functional descriptor (5) - CDC PSTN Table 15 */ + 5, /* bFunctionLength */ + DESC_TYPE_CS_INTERFACE, /* bDescriptorType (CS_IF) */ + 0x00, /* bDescriptorSubtype = Header */ + 0x10, 0x01, /* bcdCDC = 1.10 */ + + /* CDC Call Management functional descriptor (5) */ + 5, /* bFunctionLength */ + DESC_TYPE_CS_INTERFACE, + 0x01, /* bDescriptorSubtype = CallMgmt */ + 0x00, /* bmCapabilities: no call mgmt */ + USB_BL_IF_CDC_DATA, /* bDataInterface = IF1 */ + + /* CDC ACM functional descriptor (4) */ + 4, /* bFunctionLength */ + DESC_TYPE_CS_INTERFACE, + 0x02, /* bDescriptorSubtype = ACM */ + 0x02, /* bmCapabilities: line coding + + * serial state notification */ + + /* CDC Union functional descriptor (5) */ + 5, /* bFunctionLength */ + DESC_TYPE_CS_INTERFACE, + 0x06, /* bDescriptorSubtype = Union */ + USB_BL_IF_CDC_COMM, /* bMasterInterface */ + USB_BL_IF_CDC_DATA, /* bSlaveInterface0 */ + + /* EP1 IN - Notification, interrupt, 16 B */ + 7, /* bLength */ + DESC_TYPE_ENDPOINT, + 0x81, /* bEndpointAddress = EP1 IN */ + 0x03, /* bmAttributes = Interrupt */ + USB_BL_EP1_SIZE, 0, /* wMaxPacketSize = 16 */ + 64, /* bInterval = 64 ms */ + + /* ====================================================== + * Interface 1: CDC Data + * ====================================================== */ + 9, /* bLength */ + DESC_TYPE_INTERFACE, + USB_BL_IF_CDC_DATA, /* bInterfaceNumber */ + 0, /* bAlternateSetting */ + 2, /* bNumEndpoints = 2 (bulk pair) */ + 0x0A, /* bInterfaceClass = CDC Data */ + 0x00, 0x00, /* SubClass, Protocol */ + 0, /* iInterface */ + + /* EP2 OUT - bulk, 64 B */ + 7, /* bLength */ + DESC_TYPE_ENDPOINT, + 0x02, /* bEndpointAddress = EP2 OUT */ + 0x02, /* bmAttributes = Bulk */ + USB_BL_EP2_SIZE, 0, /* wMaxPacketSize = 64 */ + 0, /* bInterval (ignored for bulk) */ + + /* EP3 IN - bulk, 64 B */ + 7, /* bLength */ + DESC_TYPE_ENDPOINT, + 0x83, /* bEndpointAddress = EP3 IN */ + 0x02, /* bmAttributes = Bulk */ + USB_BL_EP3_SIZE, 0, /* wMaxPacketSize = 64 */ + 0 /* bInterval (ignored for bulk) */ +}; + +/* ============================================================ + * String descriptors + * USB 2.0 chapter 9: strings are UTF-16LE; descriptor type 3. + * String index 0 returns the supported language IDs. + * ============================================================ */ +const uint8_t g_bl_string_langid[4] = { + 4, DESC_TYPE_STRING, + 0x09, 0x04 /* LANGID = 0x0409 (en-US) */ +}; + +/* Manufacturer "Anthropic-clean-room" (placeholder; users can edit + * before building if they want a different name on the COM port). */ +const uint8_t g_bl_string_manufacturer[] = { + 2 + 2*7, DESC_TYPE_STRING, + 'A',0, 'V',0, 'R',0, 'D',0, 'U',0, 'B',0, 'L',0 +}; +const uint8_t g_bl_string_manufacturer_len = sizeof(g_bl_string_manufacturer); + +/* Product "AVRDU CDC Bootloader" - 20 ASCII chars */ +const uint8_t g_bl_string_product[] = { + 2 + 2*20, DESC_TYPE_STRING, + 'A',0, 'V',0, 'R',0, 'D',0, 'U',0, ' ',0, + 'C',0, 'D',0, 'C',0, ' ',0, + 'B',0, 'o',0, 'o',0, 't',0, 'l',0, 'o',0, 'a',0, 'd',0, 'e',0, 'r',0 +}; +const uint8_t g_bl_string_product_len = sizeof(g_bl_string_product); + +/* Serial "BL00000001" - 10 ASCII chars (stable so the host caches the + * driver association) */ +const uint8_t g_bl_string_serial[] = { + 2 + 2*10, DESC_TYPE_STRING, + 'B',0, 'L',0, '0',0, '0',0, '0',0, '0',0, '0',0, '0',0, '0',0, '1',0 +}; +const uint8_t g_bl_string_serial_len = sizeof(g_bl_string_serial); diff --git a/megaavr/bootloaders/usbcdcboot/src/usb_desc.h b/megaavr/bootloaders/usbcdcboot/src/usb_desc.h new file mode 100644 index 00000000..7c69c364 --- /dev/null +++ b/megaavr/bootloaders/usbcdcboot/src/usb_desc.h @@ -0,0 +1,91 @@ +/* + * usbcdcboot/src/usb_desc.h + * -------------------------------------------------------------------- + * Clean-room. Reference: USB 2.0, USB CDC 1.20 / PSTN 1.20. + * License: LGPL 2.1. + * + * Descriptor constants and externs for the bootloader. The + * bootloader exposes a single plain CDC ACM function (no IAD, no HID). + * + * EP map: + * EP0 IN/OUT Control 64 B + * EP1 IN CDC notification 16 B (Interrupt, never sent) + * EP2 OUT CDC data host->device 64 B (Bulk) + * EP3 IN CDC data device->host 64 B (Bulk) + * + * Interface map: + * IF 0 CDC Communication + * IF 1 CDC Data + * + * VID / PID = 0x1209 / 0x0001 (pid.codes test PID) + */ +#ifndef AVRDU_BL_USB_DESC_H +#define AVRDU_BL_USB_DESC_H + +#include + +/* Identity */ +#define USB_BL_VID 0x1209 +#define USB_BL_PID 0x0001 +#define USB_BL_DEVICE_VER 0x0100 + +/* Endpoints */ +#define USB_BL_EP0_SIZE 64 +#define USB_BL_EP1_SIZE 16 /* CDC notify (interrupt) */ +#define USB_BL_EP2_SIZE 64 /* CDC bulk RX */ +#define USB_BL_EP3_SIZE 64 /* CDC bulk TX */ +#define USB_BL_MAXEP 3 /* highest EP number used */ + +/* Interfaces */ +#define USB_BL_IF_CDC_COMM 0 +#define USB_BL_IF_CDC_DATA 1 +#define USB_BL_NUM_INTERFACES 2 + +/* Descriptor type codes (USB 2.0 Table 9-5) */ +#define DESC_TYPE_DEVICE 0x01 +#define DESC_TYPE_CONFIG 0x02 +#define DESC_TYPE_STRING 0x03 +#define DESC_TYPE_INTERFACE 0x04 +#define DESC_TYPE_ENDPOINT 0x05 +#define DESC_TYPE_CS_INTERFACE 0x24 /* CDC functional descriptor */ + +/* Total configuration descriptor length: + * 9 Configuration + * + 9 IF0 CDC Comm + * + 5 CDC Header functional + * + 5 CDC Call Mgmt functional + * + 4 CDC ACM functional + * + 5 CDC Union functional + * + 7 EP1 IN (notify) + * + 9 IF1 CDC Data + * + 7 EP2 OUT (bulk RX) + * + 7 EP3 IN (bulk TX) + * ---- + * 67 bytes + */ +#define USB_BL_CONFIG_TOTAL_LEN 67 + +extern const uint8_t g_bl_device_descriptor[18]; +extern const uint8_t g_bl_config_descriptor[USB_BL_CONFIG_TOTAL_LEN]; +extern const uint8_t g_bl_string_langid[4]; +extern const uint8_t g_bl_string_manufacturer[]; +extern const uint8_t g_bl_string_product[]; +extern const uint8_t g_bl_string_serial[]; + +extern const uint8_t g_bl_string_manufacturer_len; +extern const uint8_t g_bl_string_product_len; +extern const uint8_t g_bl_string_serial_len; + +/* ============================================================ + * USB SETUP packet structure (USB 2.0 Table 9-2) + * Defined here so both usb_min.c and cdc_min.c share the same type. + * ============================================================ */ +typedef struct __attribute__((packed)) { + uint8_t bmRequestType; + uint8_t bRequest; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; +} usb_setup_t; + +#endif /* AVRDU_BL_USB_DESC_H */ diff --git a/megaavr/bootloaders/usbcdcboot/src/usb_min.c b/megaavr/bootloaders/usbcdcboot/src/usb_min.c new file mode 100644 index 00000000..60aacaa8 --- /dev/null +++ b/megaavr/bootloaders/usbcdcboot/src/usb_min.c @@ -0,0 +1,482 @@ +/* + * usbcdcboot/src/usb_min.c + * -------------------------------------------------------------------- + * Clean-room implementation. References: + * - USB 2.0 specification (chapter 9: Device Framework) + * - AVR64DU32 datasheet section 28 (USB peripheral) - DS40002676 + * - Microchip ATPACK device headers (avr/io.h, ioavr64du32.h) + * + * No source from LUFA, TinyUSB, V-USB, Optiboot or any other USB + * or bootloader project was consulted while writing this file. + * + * Polled USB peripheral driver: handles bus reset, SETUP packets, + * the Standard request set, and dispatches Class requests to + * cdc_min_handle_class_request(). EP table layout: + * + * EP0 IN/OUT Control 64 B + * EP1 IN CDC notification 16 B (Interrupt, never sent) + * EP2 OUT CDC data host->device 64 B (Bulk) + * EP3 IN CDC data device->host 64 B (Bulk) + * + * License: LGPL 2.1. + */ + +#include +#include +#include +#include +#include + +#include "usb_min.h" +#include "usb_desc.h" + +/* Hooks for cdc_min */ +void cdc_min_handle_class_request(const usb_setup_t *s); +void cdc_min_on_ep2_out(uint16_t cnt); +void cdc_min_on_ep3_in_done(void); +void cdc_min_on_reset(void); + +/* ============================================================ + * Control transfer state machine - mirrors runtime but smaller + * ============================================================ */ +typedef enum { + CTRL_IDLE, + CTRL_DATA_IN_STAGE, + CTRL_DATA_OUT_STAGE, + CTRL_STATUS_IN_STAGE, + CTRL_STATUS_OUT_STAGE, + CTRL_STATUS_PENDING_ADDR +} ctrl_state_t; + +/* ============================================================ + * Global state - exported for cdc_min to drive EP2/EP3 directly + * ============================================================ */ +USB_EP_TABLE_t g_bl_ep_table __attribute__((aligned(2))); + +uint8_t g_bl_ep0_setup[8] __attribute__((aligned(2))); +uint8_t g_bl_ep0_data[USB_BL_EP0_SIZE] __attribute__((aligned(2))); +uint8_t g_bl_ep1_in [USB_BL_EP1_SIZE] __attribute__((aligned(2))); +uint8_t g_bl_ep2_out[USB_BL_EP2_SIZE] __attribute__((aligned(2))); +uint8_t g_bl_ep3_in [USB_BL_EP3_SIZE] __attribute__((aligned(2))); + +static ctrl_state_t s_ctrl_state = CTRL_IDLE; +static uint8_t s_pending_address = 0; +static uint8_t s_current_configuration = 0; +static bool s_pending_class_data_out = false; + +/* ============================================================ + * Small helpers + * ============================================================ */ +static inline void rmw_wait(void) { + while (USB0.INTFLAGSB & USB_RMWBUSY_bm) {} +} + +/* EP table reset - called on bus reset. */ +static void ep_table_init(void) { + memset(&g_bl_ep_table, 0, sizeof(g_bl_ep_table)); + + /* EP0 OUT: Control, armed for SETUP */ + g_bl_ep_table.EP[0].OUT.CTRL = USB_TYPE_CONTROL_gc | USB_BUFSIZE_DEFAULT_BUF64_gc; + g_bl_ep_table.EP[0].OUT.DATAPTR = (uint16_t)g_bl_ep0_setup; + g_bl_ep_table.EP[0].OUT.STATUS = 0x00; + + /* EP0 IN: Control, NAK until response queued */ + g_bl_ep_table.EP[0].IN.CTRL = USB_TYPE_CONTROL_gc | USB_BUFSIZE_DEFAULT_BUF64_gc; + g_bl_ep_table.EP[0].IN.DATAPTR = (uint16_t)g_bl_ep0_data; + g_bl_ep_table.EP[0].IN.STATUS = USB_BUSNAK_bm; + + /* EP1 IN: CDC notification, NAK forever (we never send a notify) */ + g_bl_ep_table.EP[1].IN.CTRL = USB_TYPE_BULKINT_gc | USB_BUFSIZE_DEFAULT_BUF16_gc; + g_bl_ep_table.EP[1].IN.DATAPTR = (uint16_t)g_bl_ep1_in; + g_bl_ep_table.EP[1].IN.STATUS = USB_BUSNAK_bm; + + /* EP2 OUT: CDC bulk RX, armed for first packet */ + g_bl_ep_table.EP[2].OUT.CTRL = USB_TYPE_BULKINT_gc | USB_BUFSIZE_DEFAULT_BUF64_gc; + g_bl_ep_table.EP[2].OUT.DATAPTR = (uint16_t)g_bl_ep2_out; + g_bl_ep_table.EP[2].OUT.STATUS = 0x00; + + /* EP3 IN: CDC bulk TX, NAK until cdc_min arms a packet */ + g_bl_ep_table.EP[3].IN.CTRL = USB_TYPE_BULKINT_gc | USB_BUFSIZE_DEFAULT_BUF64_gc; + g_bl_ep_table.EP[3].IN.DATAPTR = (uint16_t)g_bl_ep3_in; + g_bl_ep_table.EP[3].IN.STATUS = USB_BUSNAK_bm; +} + +/* ============================================================ + * EP0 helpers + * ============================================================ */ +static void ep0_stall(void) { + g_bl_ep_table.EP[0].OUT.CTRL |= USB_DOSTALL_bm; + g_bl_ep_table.EP[0].IN.CTRL |= USB_DOSTALL_bm; + s_ctrl_state = CTRL_IDLE; +} + +static void ep0_start_data_in(const uint8_t *data, uint16_t len, uint16_t host_requested) { + if (len > host_requested) len = host_requested; + + if (len <= USB_BL_EP0_SIZE) { + /* Single-packet: copy to scratch */ + for (uint16_t i = 0; i < len; i++) g_bl_ep0_data[i] = data[i]; + g_bl_ep_table.EP[0].IN.DATAPTR = (uint16_t)g_bl_ep0_data; + g_bl_ep_table.EP[0].IN.CNT = len; + g_bl_ep_table.EP[0].IN.MCNT = 0; + g_bl_ep_table.EP[0].IN.CTRL = USB_TYPE_CONTROL_gc | USB_BUFSIZE_DEFAULT_BUF64_gc; + } else { + /* Multi-packet (configuration descriptor at 67 B is the only + * case here, but coding it general lets us avoid a special + * path for any future descriptor that might grow). */ + g_bl_ep_table.EP[0].IN.DATAPTR = (uint16_t)data; + g_bl_ep_table.EP[0].IN.CNT = len; + g_bl_ep_table.EP[0].IN.MCNT = 0; + g_bl_ep_table.EP[0].IN.CTRL = USB_TYPE_CONTROL_gc | USB_MULTIPKT_bm + | USB_AZLP_bm | USB_BUFSIZE_DEFAULT_BUF64_gc; + } + + rmw_wait(); + USB0.STATUS[0].INCLR = USB_UNFOVF_bm | USB_TRNCOMPL_bm | USB_STALLED_bm | USB_BUSNAK_bm; + rmw_wait(); + USB0.STATUS[0].OUTCLR = USB_UNFOVF_bm | USB_TRNCOMPL_bm | USB_STALLED_bm | USB_BUSNAK_bm; + + s_ctrl_state = CTRL_DATA_IN_STAGE; +} + +static void ep0_start_data_out(uint8_t *buffer, uint16_t len) { + /* On AVR DU, Data-stage OUT lands in the EP0.IN buffer (datasheet + * 28.3.2.2 - control transfer OUT data flow). */ + g_bl_ep_table.EP[0].IN.DATAPTR = (uint16_t)buffer; + g_bl_ep_table.EP[0].IN.CNT = 0; + g_bl_ep_table.EP[0].IN.MCNT = len; + g_bl_ep_table.EP[0].IN.CTRL = USB_TYPE_CONTROL_gc | USB_MULTIPKT_bm | USB_BUFSIZE_DEFAULT_BUF64_gc; + + rmw_wait(); + USB0.STATUS[0].OUTCLR = USB_UNFOVF_bm | USB_TRNCOMPL_bm | USB_STALLED_bm | USB_BUSNAK_bm; + + s_ctrl_state = CTRL_DATA_OUT_STAGE; +} + +static void ep0_send_zlp(void) { + g_bl_ep_table.EP[0].IN.CNT = 0; + g_bl_ep_table.EP[0].IN.MCNT = 0; + g_bl_ep_table.EP[0].IN.CTRL = USB_TYPE_CONTROL_gc | USB_BUFSIZE_DEFAULT_BUF64_gc; + + rmw_wait(); + USB0.STATUS[0].INCLR = USB_UNFOVF_bm | USB_TRNCOMPL_bm | USB_STALLED_bm | USB_BUSNAK_bm; + + s_ctrl_state = CTRL_STATUS_IN_STAGE; +} + +/* Expose helpers to cdc_min via these wrapper names. */ +void usb_min_ep0_send_zlp(void) { ep0_send_zlp(); } +void usb_min_ep0_start_data_in (const uint8_t *d, uint16_t l, uint16_t h) { ep0_start_data_in(d,l,h); } +void usb_min_ep0_start_data_out(uint8_t *b, uint16_t l) { ep0_start_data_out(b,l); s_pending_class_data_out = true; } +void usb_min_ep0_stall(void) { ep0_stall(); } + +/* ============================================================ + * Standard request handler + * + * The arduino programmer in avrdude does not need anything exotic. + * We implement the bare-minimum subset that all USB hosts will + * exercise during enumeration: + * GET_DESCRIPTOR (device, config, string) + * SET_ADDRESS + * GET_CONFIGURATION + * SET_CONFIGURATION + * GET_STATUS (device / interface / endpoint, return 0) + * CLEAR/SET_FEATURE (endpoint halt only, no-op) + * ============================================================ */ +#define REQ_GET_STATUS 0x00 +#define REQ_CLEAR_FEATURE 0x01 +#define REQ_SET_FEATURE 0x03 +#define REQ_SET_ADDRESS 0x05 +#define REQ_GET_DESCRIPTOR 0x06 +#define REQ_GET_CONFIGURATION 0x08 +#define REQ_SET_CONFIGURATION 0x09 +#define REQ_GET_INTERFACE 0x0A +#define REQ_SET_INTERFACE 0x0B + +static uint8_t s_status_buf[2]; +static uint8_t s_single_byte_buf; + +static void handle_get_descriptor(const usb_setup_t *s) { + uint8_t type = (s->wValue >> 8) & 0xFF; + uint8_t index = s->wValue & 0xFF; + const uint8_t *p = NULL; + uint16_t len = 0; + + switch (type) { + case DESC_TYPE_DEVICE: + p = g_bl_device_descriptor; + len = sizeof(g_bl_device_descriptor); + break; + + case DESC_TYPE_CONFIG: + p = g_bl_config_descriptor; + len = sizeof(g_bl_config_descriptor); + break; + + case DESC_TYPE_STRING: + switch (index) { + case 0: p = g_bl_string_langid; len = sizeof(g_bl_string_langid); break; + case 1: p = g_bl_string_manufacturer; len = g_bl_string_manufacturer_len; break; + case 2: p = g_bl_string_product; len = g_bl_string_product_len; break; + case 3: p = g_bl_string_serial; len = g_bl_string_serial_len; break; + default: ep0_stall(); return; + } + break; + + default: + ep0_stall(); + return; + } + + ep0_start_data_in(p, len, s->wLength); +} + +static void handle_standard_request(const usb_setup_t *s) { + switch (s->bRequest) { + + case REQ_GET_DESCRIPTOR: + handle_get_descriptor(s); + break; + + case REQ_SET_ADDRESS: + /* USB 2.0 9.4.6: address must be applied AFTER the status-IN + * stage completes (host expects to ACK at the old address). */ + s_pending_address = s->wValue & 0x7F; + ep0_send_zlp(); + s_ctrl_state = CTRL_STATUS_PENDING_ADDR; + break; + + case REQ_GET_CONFIGURATION: + s_single_byte_buf = s_current_configuration; + ep0_start_data_in(&s_single_byte_buf, 1, s->wLength); + break; + + case REQ_SET_CONFIGURATION: + s_current_configuration = s->wValue & 0xFF; + ep0_send_zlp(); + /* Re-arm EP2 OUT so it accepts the first STK500 byte from + * the host as soon as the host starts pumping. */ + rmw_wait(); + USB0.STATUS[2].OUTCLR = USB_BUSNAK_bm; + break; + + case REQ_GET_STATUS: + s_status_buf[0] = 0; /* not self-powered, not RW */ + s_status_buf[1] = 0; + ep0_start_data_in(s_status_buf, 2, s->wLength); + break; + + case REQ_CLEAR_FEATURE: + case REQ_SET_FEATURE: + /* Endpoint halt clear/set: we don't STALL any application EP, + * so this is effectively a no-op. ACK with ZLP. */ + ep0_send_zlp(); + break; + + case REQ_GET_INTERFACE: + s_single_byte_buf = 0; + ep0_start_data_in(&s_single_byte_buf, 1, s->wLength); + break; + + case REQ_SET_INTERFACE: + ep0_send_zlp(); + break; + + default: + ep0_stall(); + break; + } +} + +/* ============================================================ + * SETUP packet handler - dispatch by request type + * ============================================================ */ +static void handle_setup(void) { + usb_setup_t *s = (usb_setup_t *)g_bl_ep0_setup; + + /* Clear stale DOSTALL on both directions for the new transfer */ + g_bl_ep_table.EP[0].OUT.CTRL = USB_TYPE_CONTROL_gc | USB_BUFSIZE_DEFAULT_BUF64_gc; + g_bl_ep_table.EP[0].IN.CTRL = USB_TYPE_CONTROL_gc | USB_BUFSIZE_DEFAULT_BUF64_gc; + + rmw_wait(); + USB0.STATUS[0].OUTCLR = USB_EPSETUP_bm; + rmw_wait(); + USB0.STATUS[0].INCLR = USB_EPSETUP_bm; + + switch (s->bmRequestType & 0x60) { + case 0x00: handle_standard_request(s); break; + case 0x20: cdc_min_handle_class_request(s); break; + default: ep0_stall(); break; + } +} + +/* ============================================================ + * EP completion handlers + * ============================================================ */ +static void handle_ep0_in_complete(void) { + switch (s_ctrl_state) { + case CTRL_DATA_IN_STAGE: + s_ctrl_state = CTRL_STATUS_OUT_STAGE; + break; + + case CTRL_STATUS_PENDING_ADDR: + USB0.ADDR = s_pending_address; + s_pending_address = 0; + s_ctrl_state = CTRL_IDLE; + break; + + case CTRL_STATUS_IN_STAGE: + default: + s_ctrl_state = CTRL_IDLE; + break; + } +} + +extern void cdc_min_data_out_complete(void); + +static void handle_ep0_out_complete(void) { + switch (s_ctrl_state) { + case CTRL_DATA_OUT_STAGE: + if (s_pending_class_data_out) { + s_pending_class_data_out = false; + cdc_min_data_out_complete(); + } + ep0_send_zlp(); + break; + + case CTRL_STATUS_OUT_STAGE: + default: + s_ctrl_state = CTRL_IDLE; + break; + } +} + +/* ============================================================ + * Polled event loop + * ============================================================ */ +void usb_min_poll(void) { + uint8_t flags_a = USB0.INTFLAGSA; + uint8_t flags_b = USB0.INTFLAGSB; + + if (flags_a & USB_RESET_bm) { + USB0.ADDR = 0; + ep_table_init(); + s_ctrl_state = CTRL_IDLE; + s_current_configuration = 0; + s_pending_address = 0; + s_pending_class_data_out = false; + cdc_min_on_reset(); + USB0.INTFLAGSA = USB_RESET_bm; + } + + if (flags_b & USB_SETUP_bm) { + handle_setup(); + USB0.INTFLAGSB = USB_SETUP_bm; + } + + if (flags_b & USB_TRNCOMPL_bm) { + if (g_bl_ep_table.EP[0].IN.STATUS & USB_TRNCOMPL_bm) { + rmw_wait(); + USB0.STATUS[0].INCLR = USB_TRNCOMPL_bm; + handle_ep0_in_complete(); + } + if (g_bl_ep_table.EP[0].OUT.STATUS & USB_TRNCOMPL_bm) { + rmw_wait(); + USB0.STATUS[0].OUTCLR = USB_TRNCOMPL_bm; + handle_ep0_out_complete(); + } + if (g_bl_ep_table.EP[1].IN.STATUS & USB_TRNCOMPL_bm) { + /* Notification EP - never sent, but clear flag defensively. */ + rmw_wait(); + USB0.STATUS[1].INCLR = USB_TRNCOMPL_bm; + } + if (g_bl_ep_table.EP[2].OUT.STATUS & USB_TRNCOMPL_bm) { + rmw_wait(); + USB0.STATUS[2].OUTCLR = USB_TRNCOMPL_bm; + uint16_t cnt = g_bl_ep_table.EP[2].OUT.CNT; + cdc_min_on_ep2_out(cnt); + /* cdc_min will re-arm EP2 OUT when it has consumed + * the bytes (clears BUSNAK). We do NOT clear it here. */ + } + if (g_bl_ep_table.EP[3].IN.STATUS & USB_TRNCOMPL_bm) { + rmw_wait(); + USB0.STATUS[3].INCLR = USB_TRNCOMPL_bm; + cdc_min_on_ep3_in_done(); + } + USB0.INTFLAGSB = USB_TRNCOMPL_bm; + } +} + +/* ============================================================ + * Initialization / attach / detach + * ============================================================ */ +void usb_min_init(void) { + /* OSCHF SOF auto-tune so the USB peripheral can lock onto host + * frame timing without an external crystal. + * + * CRITICAL: ALGSEL must be set to INCR (incremental search) together + * with AUTOTUNE=SOF. Per datasheet, ALGSEL "must be written + * simultaneously with writing the SOF setting to the AUTOTUNE bit + * field". ALGSEL=0 (binary search, reset default) "may overshoot + * oscillator output frequency up to HALF the tune range", which + * corrupts the 48 MHz USB clock mid-enumeration and causes the host + * to reject the device. ALGSEL=1 (incremental) nudges at most 5 + * tune steps after a reset, keeping the USB clock stable. + * + * Without this, cold-boot enumeration fails because the factory- + * default OSCHF tune is ~+/-2% off and the binary search swings + * wildly trying to refine it. */ + uint8_t oschf = CLKCTRL.OSCHFCTRLA; + oschf &= ~(CLKCTRL_AUTOTUNE_gm | CLKCTRL_ALGSEL_bm); + oschf |= (CLKCTRL_AUTOTUNE_SOF_gc | CLKCTRL_ALGSEL_INCR_gc); + _PROTECTED_WRITE(CLKCTRL.OSCHFCTRLA, oschf); + + /* VUSB supply. VREG=1 builds (default hexes): the internal regulator + * derives the 3.3 V VUSB rail (and D+ pull-up reference) from a 5 V VDD + * (power configurations 5b/5s). VREG=0 builds (*_novreg hexes): the board + * feeds 3.3 V into VUSB externally (configuration 3s, datasheet directs + * USBVREG = 0). See Makefile / build_all_du.bat. */ +#if defined(USB_VREG_INTERNAL) + SYSCFG.VUSBCTRL = SYSCFG_USBVREG_bm; +#else + SYSCFG.VUSBCTRL = 0; +#endif + + _delay_ms(1); + + ep_table_init(); + + /* Point the USB peripheral at the EP table's EP[0]. */ + USB0.EPPTR = (uint16_t)&g_bl_ep_table.EP[0]; + + /* Polled - no interrupts. */ + USB0.INTCTRLA = 0; + USB0.INTCTRLB = 0; + + /* Enable + advertise highest endpoint number. */ + USB0.CTRLA = USB_ENABLE_bm | USB_BL_MAXEP; + + /* Wait for the USB PLL. Cap the wait so we don't deadlock if + * the USB clock domain is misconfigured. */ + { + /* Wait for PLL48M to lock. If it doesn't, we have no USB clock + * and there is nothing useful we can do here - just keep trying. + * (The recovery path is for the user to UPDI-reflash.) */ + uint32_t timeout = 1000000UL; + while (!(CLKCTRL.USBPLLSTATUS & CLKCTRL_PLLS_bm) && --timeout) { } + } + + USB0.CTRLB = 0; /* GNAUTO/GNAK off; we manage NAK manually. */ +} + +void usb_min_attach(void) { + USB0.CTRLB |= USB_ATTACH_bm; +} + +void usb_min_detach(void) { + USB0.CTRLB &= ~USB_ATTACH_bm; +} + +bool usb_min_is_configured(void) { + return s_current_configuration != 0; +} diff --git a/megaavr/bootloaders/usbcdcboot/src/usb_min.h b/megaavr/bootloaders/usbcdcboot/src/usb_min.h new file mode 100644 index 00000000..cc6efff9 --- /dev/null +++ b/megaavr/bootloaders/usbcdcboot/src/usb_min.h @@ -0,0 +1,38 @@ +/* + * usbcdcboot/src/usb_min.h + * -------------------------------------------------------------------- + * Clean-room implementation. License: LGPL 2.1. + * + * Minimal USB device API for the AVRDU CDC bootloader. Implements + * USB 2.0 Full-Speed enumeration, Standard control transfers, and + * bulk IN/OUT pipes only. No interrupt EP, no HID, no Suspend + * handling. + * + * Implementation is a reduced port of the runtime stack + * (AVRDU_CDC/usb_core.c) trimmed for size. Both files share the + * same authorship lineage (clean-room from USB 2.0 spec + AVR DU + * datasheet section 21). + */ +#ifndef AVRDU_BL_USB_MIN_H +#define AVRDU_BL_USB_MIN_H + +#include +#include + +/* Bring up the USB peripheral and EP table. Called once at startup. */ +void usb_min_init(void); + +/* Drive the D+ pull-up and start enumeration. */ +void usb_min_attach(void); + +/* Drop D+ to force the host to re-enumerate or notice us going away. */ +void usb_min_detach(void); + +/* Service control / bulk EPs. Non-blocking; called from main loop. */ +void usb_min_poll(void); + +/* True once the host has selected configuration 1. Bulk EPs are + * armed after this transition. */ +bool usb_min_is_configured(void); + +#endif /* AVRDU_BL_USB_MIN_H */ diff --git a/megaavr/cores/dxcore/Arduino.h b/megaavr/cores/dxcore/Arduino.h index 52675f59..cdfc9ac4 100644 --- a/megaavr/cores/dxcore/Arduino.h +++ b/megaavr/cores/dxcore/Arduino.h @@ -977,6 +977,15 @@ uint8_t getAnalogSampleDuration(); #ifdef __cplusplus #include "UART.h" + #if defined(USB0) + /* Native USB present: expose USBSetup, the low-level USB_* API and the + * Leonardo / UNO R4-compatible USBDevice object to every sketch and library, + * exactly as ArduinoCore-avr's Arduino.h pulls in USBAPI.h. Libraries that + * reference the global `USBDevice` (HID-Project, MIDIUSB, ...) rely on it + * being declared via Arduino.h regardless of their own include order. */ + #include "USBAPI.h" + #endif + //uint8_t digitalPinToTimerNow(uint8_t p);= int32_t analogReadEnh( uint8_t pin, uint8_t res = ADC_NATIVE_RESOLUTION, uint8_t gain = 0); int32_t analogReadDiff(uint8_t pos, uint8_t neg, uint8_t res = ADC_NATIVE_RESOLUTION, uint8_t gain = 0); diff --git a/megaavr/cores/dxcore/HardwareSerial.h b/megaavr/cores/dxcore/HardwareSerial.h index 57aadc58..0fa455a4 100644 --- a/megaavr/cores/dxcore/HardwareSerial.h +++ b/megaavr/cores/dxcore/HardwareSerial.h @@ -419,5 +419,8 @@ class HardwareSerial : public Stream { #if defined(USART5) extern HardwareSerial Serial5; #endif +#if defined(USB0) + #include "USBSerial.h" +#endif // Why was there ever a class called UpdiClass? It was UartClass... diff --git a/megaavr/cores/dxcore/PluggableUSB.h b/megaavr/cores/dxcore/PluggableUSB.h new file mode 100644 index 00000000..0e3e77dc --- /dev/null +++ b/megaavr/cores/dxcore/PluggableUSB.h @@ -0,0 +1,4 @@ +/* DxCore-style passthrough wrapper. See USBAPI.h for rationale. */ +#pragma once +#include "api/PluggableUSB.h" +#include "usb_ep_types.h" diff --git a/megaavr/cores/dxcore/USBAPI.h b/megaavr/cores/dxcore/USBAPI.h new file mode 100644 index 00000000..fcf1ee83 --- /dev/null +++ b/megaavr/cores/dxcore/USBAPI.h @@ -0,0 +1,7 @@ +/* DxCore-style passthrough wrapper. The actual API lives at api/USBAPI.h + * (vendored from arduino/ArduinoCore-API). This lets the bundled HID library + * (and user libraries that follow the upstream Arduino convention) write + * `#include "USBAPI.h"` and have it resolve from the core include path. */ +#pragma once +#include "api/USBAPI.h" +#include "usb_ep_types.h" diff --git a/megaavr/cores/dxcore/USBCore_DU.cpp b/megaavr/cores/dxcore/USBCore_DU.cpp new file mode 100644 index 00000000..feaa06c1 --- /dev/null +++ b/megaavr/cores/dxcore/USBCore_DU.cpp @@ -0,0 +1,512 @@ +/** + * USBCore_DU.cpp - PluggableUSB bridge for the AVR DU native-USB stack. + * + * Provides: + * - PluggableUSB_::PluggableUSB_() (the API declares it, core must define it) + * - epBuffer() (per-EP type storage queried by plug()) + * - USB_SendControl / USB_Send / ... (the USBAPI consumed by HID & friends) + * - usbcore_*() helpers called from usb_standard.c when handling SETUP + * + * Design notes: + * * Endpoint allocation: EP0=control, EP1..3=CDC (notify/RX/TX), + * EP4..7=dynamic PluggableUSB. totalEP=8 slots, plug() refuses any + * module that would push lastEp past it. + * * USB_SendControl appends to a 256B static accumulator. The caller in + * usb_standard.c (after PluggableUSB().setup() / getInterface() returns) + * emits the accumulator via the existing ep0_start_data_in() multi-packet + * control-IN path. No change to the EP0 state machine itself. + * * USB_Send (HID reports) appends to a per-EP staging buffer; when the + * caller sets TRANSFER_RELEASE we move the bytes into the EP DATAPTR, + * arm the IN, and busy-wait for TRNCOMPL. Reports are small (HID = 8B + * typical) and only sent at host poll cadence, so the blocking cost is + * bounded and matches the standard Arduino HID semantics. + */ +#include +#if defined(USB0) + +#include +#include +#include + +extern "C" { + #include "usb_core.h" + #include "usb_descriptors.h" +} +#include "USBCore_DU.h" +#include "usb_ep_types.h" + +#include "api/USBAPI.h" +#include "api/PluggableUSB.h" + +/* ============================================================ + * Constants + * ============================================================ */ +#define USBCORE_NUM_EP 8 /* EP0..EP7 (logical max) */ +#define USBCORE_DYN_EP_BASE 4 /* first plugged EP */ +#define USBCORE_DYN_EP_COUNT (USBCORE_NUM_EP - USBCORE_DYN_EP_BASE) +#define USBCORE_CDC_LAST_EP 3 /* CDC owns EP1..EP3 */ +#define USBCORE_CDC_NUM_IF 2 /* CDC owns IF0..IF1 */ +#define USBCORE_ACC_SIZE 192 /* CDC IAD+CDC*2+HID ~= 100 B, with headroom */ +#define USBCORE_DYN_EP_BUF USB_EP_SIZE /* per-dynamic-EP staging */ + +/* ============================================================ + * Accumulator buffer for USB_SendControl + * ============================================================ */ +static uint8_t s_acc[USBCORE_ACC_SIZE]; +static uint16_t s_acc_pos = 0; + +void usbcore_acc_reset(void) { s_acc_pos = 0; } +const uint8_t *usbcore_acc_buf(void) { return s_acc; } +uint16_t usbcore_acc_len(void) { return s_acc_pos; } + +/* Stage `n` bytes from a PROGMEM source into s_acc at the current write + * position. Used by usb_standard.c (which is C, hence cannot call the + * C++ USB_SendControl directly) to ship device/string descriptors that + * now live in flash. Truncates silently if s_acc would overflow. */ +void usbcore_acc_load_P(const uint8_t *src_pgm, uint16_t n) { + uint16_t room = USBCORE_ACC_SIZE - s_acc_pos; + if (room == 0) return; + if (n > room) n = room; + memcpy_P(&s_acc[s_acc_pos], src_pgm, n); + s_acc_pos += n; +} + +/* ============================================================ + * EP-type storage queried by PluggableUSB::plug() + * + * The framework writes `node->endpointType[i]` as `unsigned int` + * to *(unsigned int *)epBuffer(lastEp), so we hand back a pointer + * to a uint16_t per slot. CDC slots (1..3) are pre-filled with + * EP_TYPE_INTERRUPT_IN / EP_TYPE_BULK_OUT / EP_TYPE_BULK_IN so a + * scan over the whole table can apply CDC and dynamic EPs uniformly + * if ever needed. + * ============================================================ */ +static uint8_t s_ep_types[USBCORE_NUM_EP] = { + EP_TYPE_CONTROL, /* EP0 control */ + EP_TYPE_INTERRUPT_IN, /* EP1 CDC notify */ + EP_TYPE_BULK_OUT, /* EP2 CDC data RX */ + EP_TYPE_BULK_IN, /* EP3 CDC data TX */ + 0, 0, 0, 0 /* EP4..EP7 dynamic */ +}; + +void *epBuffer(unsigned int n) { + if (n >= USBCORE_NUM_EP) return NULL; + return &s_ep_types[n]; +} + +/* ============================================================ + * PluggableUSB_ constructor (the API only declares it) + * + * CDC occupies IF0..1 and EP1..3, so plugged modules start at + * lastIf = 2, lastEp = 4. totalEP is the per-class member that + * plug() compares against (lastEp + numEndpoints > totalEP rejects). + * ============================================================ */ +PluggableUSB_::PluggableUSB_() : + lastIf(USBCORE_CDC_NUM_IF), + lastEp(USBCORE_CDC_LAST_EP + 1), + rootNode(NULL), + totalEP(USBCORE_NUM_EP - 1) /* highest EP number usable = 7 */ +{ +} + +/* ============================================================ + * USBSetup conversion (our packed usb_setup_t -> framework type) + * ============================================================ */ +static inline USBSetup as_usbsetup(const usb_setup_t *s) { + USBSetup u; + u.bmRequestType = s->bmRequestType; + u.bRequest = s->bRequest; + u.wValueL = (uint8_t)(s->wValue & 0xFF); + u.wValueH = (uint8_t)((s->wValue >> 8) & 0xFF); + u.wIndex = s->wIndex; + u.wLength = s->wLength; + return u; +} + +/* ============================================================ + * USBAPI - control transfers + * ============================================================ */ +int USB_SendControl(uint8_t flags, const void* d, int len) { + if (len <= 0) return 0; + int room = USBCORE_ACC_SIZE - s_acc_pos; + if (room <= 0) return -1; + int n = (len > room) ? room : len; + if (flags & TRANSFER_PGM) { + memcpy_P(&s_acc[s_acc_pos], d, n); + } else { + memcpy(&s_acc[s_acc_pos], d, n); + } + s_acc_pos += n; + return n; +} + +/* ============================================================ + * Control-OUT data stage staging (plugged / HID host->device) + * + * Flow (see usb_standard.c usb_handle_class_request): + * SETUP(host->device, wLength>0, non-CDC IF) + * -> usbcore_ctrl_out_begin() saves the SETUP and returns the length + * to arm; usb_standard.c arms EP0 OUT at usbcore_ctrl_out_buf(). + * EP0 OUT data lands -> handle_ep0_out_complete() + * -> usb_class_data_out_complete() -> usbcore_ctrl_out_dispatch() + * re-runs the owning module's setup(), which reads the staged + * bytes synchronously via USB_RecvControl(). + * Only one control transfer is in flight at a time, so a single set of + * static state is sufficient (and is mutually exclusive with the CDC + * SET_LINE_CODING path). + * ============================================================ */ +static usb_setup_t s_ctrl_out_setup; /* SETUP to re-dispatch */ +static uint8_t s_ctrl_out_buf[USB_EP_SIZE]; /* EP0 OUT landing buffer */ +static uint16_t s_ctrl_out_len = 0; /* bytes the host will send */ +static uint16_t s_ctrl_out_pos = 0; /* USB_RecvControl read cursor */ +static bool s_ctrl_out_pending = false; + +uint16_t usbcore_ctrl_out_begin(const usb_setup_t *s) { + s_ctrl_out_setup = *s; + uint16_t n = s->wLength; + if (n > sizeof(s_ctrl_out_buf)) n = sizeof(s_ctrl_out_buf); /* single-packet cap */ + s_ctrl_out_len = n; + s_ctrl_out_pos = 0; + s_ctrl_out_pending = true; + return n; +} + +uint8_t *usbcore_ctrl_out_buf(void) { return s_ctrl_out_buf; } +bool usbcore_ctrl_out_pending(void) { return s_ctrl_out_pending; } + +void usbcore_ctrl_out_dispatch(void) { + /* EP0 OUT has filled s_ctrl_out_buf. Hand it to the owning module: its + * setup() calls USB_RecvControl() below, which now returns the staged + * bytes. The status-stage ZLP is issued by handle_ep0_out_complete() + * after we return. */ + s_ctrl_out_pending = false; + s_ctrl_out_pos = 0; + USBSetup u = as_usbsetup(&s_ctrl_out_setup); + PluggableUSB().setup(u); +} + +/* SET_REPORT / HID feature report: return bytes staged by the EP0 OUT data + * stage (see above). Sequential reads, like the Arduino AVR core. */ +int USB_RecvControl(void* d, int len) { + if (len <= 0) return 0; + uint16_t avail = s_ctrl_out_len - s_ctrl_out_pos; + uint16_t n = ((uint16_t)len > avail) ? avail : (uint16_t)len; + memcpy(d, &s_ctrl_out_buf[s_ctrl_out_pos], n); + s_ctrl_out_pos += n; + return (int)n; +} +int USB_RecvControlLong(void* d, int len) { return USB_RecvControl(d, len); } + +/* ============================================================ + * Dynamic-EP staging buffers and USBAPI + * ============================================================ */ +/* Staging buffers only allocated for plugged EPs (EP_BASE..EP_BASE+COUNT-1). + * Indexed by (ep - USBCORE_DYN_EP_BASE). EP0..EP3 are owned by CDC and use + * usb_core's g_ep0..g_ep3 buffers directly, so no staging is needed here. */ +static uint8_t s_dyn_ep_buf[USBCORE_DYN_EP_COUNT][USBCORE_DYN_EP_BUF]; +static uint8_t s_dyn_ep_pos[USBCORE_DYN_EP_COUNT]; + +/* Per dynamic OUT (host->device) endpoint receive ring. The TRNCOMPL ISR + * copies each received packet out of the endpoint buffer (s_dyn_ep_buf[idx], + * which is the OUT EP's DATAPTR) into this ring and immediately re-arms the + * endpoint; USB_Recv()/USB_Available() consume it from main context. Single + * producer (ISR) / single consumer (main) with uint8_t head/tail = lock-free + * on this 8-bit core (same pattern as the CDC RX ring). */ +#define USBCORE_DYN_RX_RING 64 /* >= one max packet; tune to RAM budget */ +static uint8_t s_dyn_rx_ring[USBCORE_DYN_EP_COUNT][USBCORE_DYN_RX_RING]; +static volatile uint8_t s_dyn_rx_head[USBCORE_DYN_EP_COUNT]; /* written by ISR */ +static volatile uint8_t s_dyn_rx_tail[USBCORE_DYN_EP_COUNT]; /* written by main */ + +/* Forward declaration: dyn_ep_release() is defined further down, but + * USB_Flush() below needs it to push a staged short packet. */ +static int dyn_ep_release(uint8_t ep); + +uint8_t USB_Available(uint8_t ep) { + ep &= 0x07; + if (ep < USBCORE_DYN_EP_BASE || ep >= USBCORE_NUM_EP) return 0; + uint8_t i = ep - USBCORE_DYN_EP_BASE; + int16_t n = (int16_t)s_dyn_rx_head[i] - (int16_t)s_dyn_rx_tail[i]; + if (n < 0) n += USBCORE_DYN_RX_RING; + return (uint8_t)n; +} + +/* TBD when an OUT EP is plugged */ +uint8_t USB_SendSpace(uint8_t ep) { + ep &= 0x07; + if (ep < USBCORE_DYN_EP_BASE || ep >= USBCORE_NUM_EP) return 0; + return USBCORE_DYN_EP_BUF - s_dyn_ep_pos[ep - USBCORE_DYN_EP_BASE]; +} + +int USB_Recv(uint8_t ep, void* data, int len) { + ep &= 0x07; + if (ep < USBCORE_DYN_EP_BASE || ep >= USBCORE_NUM_EP) return -1; + if (len <= 0) return 0; + uint8_t i = ep - USBCORE_DYN_EP_BASE; + uint8_t *dst = (uint8_t *)data; + int got = 0; + while (got < len && s_dyn_rx_tail[i] != s_dyn_rx_head[i]) { + dst[got++] = s_dyn_rx_ring[i][s_dyn_rx_tail[i]]; + s_dyn_rx_tail[i] = (uint8_t)((s_dyn_rx_tail[i] + 1) % USBCORE_DYN_RX_RING); + } + return got; +} +int USB_Recv(uint8_t ep) { + uint8_t b; + return (USB_Recv(ep, &b, 1) == 1) ? (int)b : -1; +} + +/* Push whatever is staged on a dynamic IN endpoint out to the bus. The + * Arduino USBAPI lets a library stage payload with USB_Send() (without + * TRANSFER_RELEASE) and then emit a sub-maxpacket packet with USB_Flush(); + * MIDIUSB sends every event this way. HID never does - it ORs + * TRANSFER_RELEASE into each report - so USB_Flush() is first exercised here. */ +void USB_Flush(uint8_t ep) { + ep &= 0x07; + if (ep < USBCORE_DYN_EP_BASE || ep >= USBCORE_NUM_EP) return; + if (s_dyn_ep_pos[ep - USBCORE_DYN_EP_BASE] > 0) { + dyn_ep_release(ep); + } +} + +/* Issue an IN packet on a dynamic EP from its staging buffer. + * Blocking - waits for HW to be idle, sets CNT/clears BUSNAK, then waits + * for TRNCOMPL. Times out (returns) if the host never IN-polls; HID will + * just retry next report. */ +static int dyn_ep_release(uint8_t ep) { + if (ep < 4 || ep >= USBCORE_NUM_EP) return -1; + + /* Wait for HW idle (BUSNAK = 1 means no packet armed). */ + uint16_t tmo = 0; + while (!(g_ep_table.EP[ep].IN.STATUS & USB_BUSNAK_bm)) { + if (++tmo == 0) return -1; + } + + /* Arm the packet. */ + g_ep_table.EP[ep].IN.DATAPTR = (uint16_t)s_dyn_ep_buf[ep - USBCORE_DYN_EP_BASE]; + g_ep_table.EP[ep].IN.CNT = s_dyn_ep_pos[ep - USBCORE_DYN_EP_BASE]; + /* Activate: clear BUSNAK (+ stale flags) via INCLR, but PRESERVE TOGGLE so + * the hardware keeps DATA0/DATA1 in sync with the host (see usb_core.c). */ + while (USB0.INTFLAGSB & USB_RMWBUSY_bm) {} + USB0.STATUS[ep].INCLR = USB_UNFOVF_bm | USB_TRNCOMPL_bm | USB_STALLED_bm | USB_BUSNAK_bm; + + /* Wait for completion. */ + tmo = 0; + while (!(g_ep_table.EP[ep].IN.STATUS & USB_TRNCOMPL_bm)) { + if (++tmo == 0) { s_dyn_ep_pos[ep - USBCORE_DYN_EP_BASE] = 0; return -1; } + } + /* Clear TRNCOMPL only; the hardware re-sets BUSNAK after the IN completes. + * Never write STATUS directly here - it would clear TOGGLE. */ + while (USB0.INTFLAGSB & USB_RMWBUSY_bm) {} + USB0.STATUS[ep].INCLR = USB_TRNCOMPL_bm; + + int sent = s_dyn_ep_pos[ep - USBCORE_DYN_EP_BASE]; + s_dyn_ep_pos[ep - USBCORE_DYN_EP_BASE] = 0; + return sent; +} + +int USB_Send(uint8_t ep_with_flags, const void* data, int len) { + uint8_t ep = ep_with_flags & 0x07; + uint8_t flags = ep_with_flags & 0xF8; + if (ep < 4 || ep >= USBCORE_NUM_EP) return -1; + if (len < 0) return -1; + + /* Append payload to the staging buffer. */ + int total = 0; + const uint8_t *src = (const uint8_t *)data; + while (len > 0) { + int room = USBCORE_DYN_EP_BUF - s_dyn_ep_pos[ep - USBCORE_DYN_EP_BASE]; + if (room == 0) { + /* Flush a full packet, then keep going. */ + int r = dyn_ep_release(ep); + if (r < 0) return -1; + room = USBCORE_DYN_EP_BUF; + } + int n = (len > room) ? room : len; + if (flags & TRANSFER_PGM) { + memcpy_P(&s_dyn_ep_buf[ep - USBCORE_DYN_EP_BASE][s_dyn_ep_pos[ep - USBCORE_DYN_EP_BASE]], src, n); + } else { + memcpy(&s_dyn_ep_buf[ep - USBCORE_DYN_EP_BASE][s_dyn_ep_pos[ep - USBCORE_DYN_EP_BASE]], src, n); + } + s_dyn_ep_pos[ep - USBCORE_DYN_EP_BASE] += n; + src += n; + len -= n; + total += n; + } + + if (flags & TRANSFER_RELEASE) { + int r = dyn_ep_release(ep); + if (r < 0) return -1; + } + return total; +} + +/* ============================================================ + * Dynamic config descriptor builder - emits into accumulator + * ============================================================ */ +/* Emit the 66-byte CDC block (IAD + IF0 + IF1 + 3 EPs) by slicing it out + * of the existing g_config_descriptor (bytes 9..74). Sharing the single + * source of truth means we cannot drift between the static and dynamic + * representations of CDC. */ +static void emit_cdc_interfaces(uint8_t *ifCount) { + USB_SendControl(TRANSFER_PGM, &g_config_descriptor[9], 66); + *ifCount += 2; +} + +void usbcore_build_config_descriptor(void) { + s_acc_pos = 0; + + /* Skeleton CONFIGURATION header (wTotalLength + bNumInterfaces patched later) */ + static const uint8_t cfg_hdr[9] PROGMEM = { + 9, /* bLength */ + 0x02, /* bDescriptorType = CONFIGURATION */ + 0, 0, /* wTotalLength (patched below) */ + 0, /* bNumInterfaces (patched below) */ + 1, /* bConfigurationValue */ + 0, /* iConfiguration */ + 0xA0, /* bmAttributes: bus-powered, rem.wakeup */ + 50 /* bMaxPower = 100 mA */ + }; + USB_SendControl(TRANSFER_PGM, cfg_hdr, sizeof(cfg_hdr)); + + uint8_t ifCount = 0; + emit_cdc_interfaces(&ifCount); + PluggableUSB().getInterface(&ifCount); + + /* Patch wTotalLength and bNumInterfaces. */ + s_acc[2] = (uint8_t)(s_acc_pos & 0xFF); + s_acc[3] = (uint8_t)((s_acc_pos >> 8) & 0xFF); + s_acc[4] = ifCount; +} + +/* ============================================================ + * Dispatch helpers (called from usb_standard.c) + * ============================================================ */ +bool usbcore_try_plugged_setup(const usb_setup_t *s) { + USBSetup u = as_usbsetup(s); + return PluggableUSB().setup(u); +} + +bool usbcore_try_plugged_get_descriptor(const usb_setup_t *s) { + USBSetup u = as_usbsetup(s); + return PluggableUSB().getDescriptor(u) > 0; +} + +/* ============================================================ + * SET_CONFIGURATION : program dynamic EPs from epBuffer types + * (CDC EPs are programmed inline in handle_set_configuration.) + * ============================================================ */ +void usbcore_init_plugged_endpoints(void) { + for (uint8_t ep = 4; ep < USBCORE_NUM_EP; ep++) { + uint8_t t = s_ep_types[ep]; + if (t == 0) continue; /* slot unused */ + + uint8_t dir_in = (t & 0x80) ? 1 : 0; + /* DU TYPE: control / iso / bulkint (bulk and interrupt are the same + * register value; the descriptor type bits 1..0 distinguish them). */ + uint8_t type = (t & 0x03); + uint8_t ctrl_tp = (type == 0) ? USB_TYPE_CONTROL_gc + : (type == 1) ? USB_TYPE_ISO_gc + : USB_TYPE_BULKINT_gc; + + /* Per-EP buffer size selector. We use 64-byte buffers for all + * dynamic EPs; HID/Keyboard/Mouse only send a handful per report. */ + uint8_t bufsz = USB_BUFSIZE_DEFAULT_BUF64_gc; + + s_dyn_ep_pos[ep - USBCORE_DYN_EP_BASE] = 0; + if (dir_in) { + g_ep_table.EP[ep].IN.CTRL = ctrl_tp | bufsz; + g_ep_table.EP[ep].IN.DATAPTR = (uint16_t)s_dyn_ep_buf[ep - USBCORE_DYN_EP_BASE]; + g_ep_table.EP[ep].IN.CNT = 0; + g_ep_table.EP[ep].IN.STATUS = USB_BUSNAK_bm; /* idle, owned by SW */ + } else { + g_ep_table.EP[ep].OUT.CTRL = ctrl_tp | bufsz; + g_ep_table.EP[ep].OUT.DATAPTR = (uint16_t)s_dyn_ep_buf[ep - USBCORE_DYN_EP_BASE]; + g_ep_table.EP[ep].OUT.CNT = 0; + g_ep_table.EP[ep].OUT.STATUS = 0; /* armed for RX */ + } + } +} + +/* Service dynamic OUT (host->device) endpoints. Called from the TRNCOMPL ISR. + * Dynamic IN reports still complete synchronously inside USB_Send(); only OUT + * endpoints need servicing here. For each plugged OUT EP that completed a + * transaction: ack TRNCOMPL, copy the packet into its RX ring, then re-arm. + * All STATUS access is OUTCLR (atomic, preserves the DATA0/DATA1 TOGGLE) - + * never a direct STATUS write. */ +void usbcore_service_dynamic_ep_trncompl(void) { + for (uint8_t ep = USBCORE_DYN_EP_BASE; ep < USBCORE_NUM_EP; ep++) { + uint8_t t = s_ep_types[ep]; + if (t == 0 || (t & 0x80)) continue; /* unused or IN endpoint */ + if (!(g_ep_table.EP[ep].OUT.STATUS & USB_TRNCOMPL_bm)) continue; + + uint8_t i = ep - USBCORE_DYN_EP_BASE; + while (USB0.INTFLAGSB & USB_RMWBUSY_bm) {} + USB0.STATUS[ep].OUTCLR = USB_TRNCOMPL_bm; /* ack the transaction */ + + uint16_t cnt = g_ep_table.EP[ep].OUT.CNT; + if (cnt > USBCORE_DYN_EP_BUF) cnt = USBCORE_DYN_EP_BUF; + for (uint16_t k = 0; k < cnt; k++) { + uint8_t next = (uint8_t)((s_dyn_rx_head[i] + 1) % USBCORE_DYN_RX_RING); + if (next == s_dyn_rx_tail[i]) break; /* ring full: drop the rest */ + s_dyn_rx_ring[i][s_dyn_rx_head[i]] = s_dyn_ep_buf[i][k]; + s_dyn_rx_head[i] = next; + } + + g_ep_table.EP[ep].OUT.CNT = 0; + while (USB0.INTFLAGSB & USB_RMWBUSY_bm) {} + USB0.STATUS[ep].OUTCLR = USB_BUSNAK_bm; /* re-arm for next packet */ + } +} + +/* ============================================================ + * USBDevice control object (Arduino Leonardo / UNO R4 compatible) + * Declared in api/USBAPI.h; implemented here for the AVR-DU USB peripheral + * so libraries referencing the global USBDevice (HID-Project System, + * MIDIUSB, ...) link and run unchanged. + * ============================================================ */ + +/* USB0 bus-signalling bits for device-initiated remote wakeup. + * Datasheet DS40002548A: USB.CTRLB and USB.BUSSTATE. Fallbacks match the + * documented bit positions if the I/O header names them differently. */ +#ifndef USB_URESUME_bm +#define USB_URESUME_bm (1 << 3) /* CTRLB / BUSSTATE bit 3: Upstream Resume */ +#endif +#ifndef USB_SUSPENDED_bm +#define USB_SUSPENDED_bm (1 << 1) /* BUSSTATE bit 1: Bus Suspended */ +#endif +#ifndef USB_WTRSM_bm +#define USB_WTRSM_bm (1 << 4) /* BUSSTATE bit 4: Wait Time Resume elapsed */ +#endif + +USBDevice_::USBDevice_() { } + +bool USBDevice_::configured() { return usbIsConfigured(); } +void USBDevice_::attach() { usbAttach(); } +void USBDevice_::detach() { usbDetach(); } +void USBDevice_::poll() { usbPoll(); } + +bool USBDevice_::isSuspended() { + return (USB0.BUSSTATE & USB_SUSPENDED_bm) != 0; +} + +/* Device-initiated remote wakeup (upstream resume). + * Returns false unless the host enabled it (SET_FEATURE DEVICE_REMOTE_WAKEUP, + * tracked in g_remote_wakeup_enabled) AND the bus is suspended. Per the + * datasheet, an upstream resume must not start until the bus has been + * suspended >= 5 ms (T_WTRSM); BUSSTATE.WTRSM signals that. We wait (bounded) + * for WTRSM, then write CTRLB.URESUME (hardware self-clears it). */ +bool USBDevice_::wakeupHost() { + if (!g_remote_wakeup_enabled) return false; + if (!(USB0.BUSSTATE & USB_SUSPENDED_bm)) return false; + + for (uint16_t guard = 0; !(USB0.BUSSTATE & USB_WTRSM_bm); ++guard) { + if (guard == 0xFFFF) return false; /* WTRSM never set: bail out */ + } + + USB0.CTRLB |= USB_URESUME_bm; /* initiate upstream resume (self-clearing) */ + return true; +} + +USBDevice_ USBDevice; + +#endif /* USB0 */ diff --git a/megaavr/cores/dxcore/USBCore_DU.h b/megaavr/cores/dxcore/USBCore_DU.h new file mode 100644 index 00000000..1c6e4b26 --- /dev/null +++ b/megaavr/cores/dxcore/USBCore_DU.h @@ -0,0 +1,75 @@ +/** + * USBCore_DU.h - C-callable bridge between the AVR DU low-level USB stack + * (usb_core.c / usb_standard.c / usb_descriptors.c, all C) + * and the C++ PluggableUSB framework (api/PluggableUSB.cpp). + * + * Implementation in USBCore_DU.cpp. + */ +#pragma once +#if defined(USB0) + +#include +#include +#include "usb_core.h" /* for usb_setup_t */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* --- Accumulator buffer (control-IN descriptor assembly) ---------------- */ +/* The accumulator backs USB_SendControl: each call appends bytes to a + * static buffer; the caller (handle_get_descriptor in usb_standard.c) + * then sends the buffer with the existing ep0_start_data_in(). */ +void usbcore_acc_reset(void); +const uint8_t *usbcore_acc_buf(void); +uint16_t usbcore_acc_len(void); + +/* --- Dynamic CONFIGURATION descriptor builder --------------------------- * + * Resets the accumulator, emits the CDC interfaces (always present), then + * iterates the PluggableUSB modules' getInterface() to append the rest. + * Fixes up wTotalLength and bNumInterfaces in the accumulator header. */ +void usbcore_build_config_descriptor(void); + +/* --- PluggableUSB dispatch hooks (called from usb_standard.c) ----------- * + * `try_setup` - non-CDC class / vendor requests + * `try_get_descriptor` - non-standard descriptor types (HID report, etc.) * + * Both return true if a registered PluggableUSB module handled it. * + * On true return, send accumulator buffer (which the module filled via * + * USB_SendControl) as the control-IN response; on false, STALL. */ +bool usbcore_try_plugged_setup(const usb_setup_t *s); +bool usbcore_try_plugged_get_descriptor(const usb_setup_t *s); + +/* --- Endpoint configuration ---------------------------------------------- * + * Iterate the epBuffer[] entries (filled by PluggableUSB.plug at static * + * init) and program the DU endpoint table accordingly. Call from * + * handle_set_configuration after CDC EPs have been configured. */ +void usbcore_init_plugged_endpoints(void); + +/* --- TRNCOMPL servicing for dynamic EPs (called from USB ISR) ----------- * + * For now this just clears flags; data movement for those EPs is handled * + * by USB_Send/Recv via polling (blocking writes). */ +void usbcore_service_dynamic_ep_trncompl(void); + +/* --- Control-OUT data stage for plugged (HID) host->device requests ----- * + * HID SET_REPORT (keyboard LED state, feature reports, ...) carries its * + * payload in an EP0 OUT data stage that must be received BEFORE the owning * + * PluggableUSB module can consume it. Because our SETUP handler runs in ISR * + * context we cannot block inside the module's setup() the way the 32U4 core * + * does; instead we stage the OUT data into a buffer, then re-dispatch * + * setup() once it has landed, so the module's USB_RecvControl() reads it * + * synchronously - identical API semantics to the Arduino AVR core. * + * * + * usbcore_ctrl_out_begin() - save SETUP, return armed length (0=reject) * + * usbcore_ctrl_out_buf() - EP0 OUT landing buffer to arm * + * usbcore_ctrl_out_pending() - a plugged ctrl-OUT is awaiting its data * + * usbcore_ctrl_out_dispatch()- data landed: re-run the module's setup() */ +uint16_t usbcore_ctrl_out_begin(const usb_setup_t *s); +uint8_t *usbcore_ctrl_out_buf(void); +bool usbcore_ctrl_out_pending(void); +void usbcore_ctrl_out_dispatch(void); + +#ifdef __cplusplus +} +#endif + +#endif /* USB0 */ diff --git a/megaavr/cores/dxcore/USBSerial.cpp b/megaavr/cores/dxcore/USBSerial.cpp new file mode 100644 index 00000000..0f0b3624 --- /dev/null +++ b/megaavr/cores/dxcore/USBSerial.cpp @@ -0,0 +1,155 @@ +/* USBSerial.cpp - global instance + USB bring-up for the AVR DU native-USB + * CDC stack, plus the printHex()/printHexln() bodies. + * + * This library is free software released under LGPL 2.1. + * See LICENSE.md for more information. + * This file is part of DxCore. + * + * Copyright (c) 2025-2026 Yusuke Shimizu (Workshop Asahi) + * + * The printHex()/_prtHxdw() bodies below mirror DxCore's UART.cpp so the USB + * Serial formats hex output identically to a hardware UART: + * Copyright (c) 2017-2021 Spence Konde and contributors (see UART.h). + * + * Originally MIT (c) Yusuke Shimizu; relicensed to LGPL 2.1 to match DxCore + * when the shared printHex() implementation was adopted from the UART class. + */ +#include /* defines USB0 on parts that have USB; must precede the guard */ +#if defined(USB0) + +/** + * The USB stack is INTERRUPT-DRIVEN: usbInit() enables USB0_BUSEVENT + * and USB0_TRNCOMPL, whose ISRs (in usb_core.c) service bus reset, SETUP, + * and all endpoint transactions. The sketch needs NO usbPoll() calls and + * no yield() hook - enumeration and data transfer happen in the background, + * even while the sketch sits in delay(). + * + * On startup, main() calls usb_auto_init() (declared extern "C" below) so the + * device enumerates at boot regardless of whether the sketch ever calls + * Serial.begin() - matching Arduino Leonardo / Micro / UNO R4, where the + * native USB CDC stays present (and the COM port stays visible) on every + * sketch including Blink. usb_auto_init() is idempotent, so Serial.begin() + * remains safe. + */ +#include "USBSerial.h" + +extern "C" { + #include "usb_core.h" + #include "usb_cdc.h" +} + +USBSerial_ USBSerial; + +/* ============================================================ + * usb_auto_init - bring up native USB CDC (idempotent) + * ============================================================ + * Called automatically from main() after init()/initVariant() but + * before sei() on USB-capable parts (USB0 defined). Also called by + * USBSerial_::begin() so explicit init from the sketch is harmless. + * usbInit() configures OSCHF SOF autotune + the VUSB regulator, sets + * up the endpoint table, and enables the USB peripheral; usbAttach() + * connects the D+ pull-up so the host starts enumeration. + */ +extern "C" void usb_auto_init(void) { + static bool s_usb_started = false; + if (s_usb_started) return; + s_usb_started = true; + usbInit(); + usbAttach(); +} + +void USBSerial_::begin(unsigned long baud) { + (void)baud; + usb_auto_init(); +} + +void USBSerial_::begin(unsigned long baud, uint8_t config) { + (void)baud; + (void)config; + usb_auto_init(); +} + +void USBSerial_::end(void) { + /* Leave the peripheral attached; a full detach (usbDetach) would drop + * the COM port mid-run, which is rarely what a sketch wants. */ +} + +/* ============================================================ + * printHex family - mirrors DxCore HardwareSerial (UART.cpp) + * ============================================================ + * Bodies only depend on write()/println(), so they are byte-for-byte + * equivalent to the UART versions; only the class name differs. */ +void USBSerial_::printHex(const uint8_t b) { + char x = (b >> 4) | '0'; + if (x > '9') + x += 7; + write(x); + x = (b & 0x0F) | '0'; + if (x > '9') + x += 7; + write(x); +} + +void USBSerial_::printHex(const uint16_t w, bool swaporder) { + uint8_t *ptr = (uint8_t *) &w; + if (swaporder) { + printHex(*(ptr++)); + printHex(*(ptr)); + } else { + printHex(*(ptr + 1)); + printHex(*(ptr)); + } +} + +void USBSerial_::_prtHxdw(uint8_t * ptr, bool swaporder) { + if (swaporder) { + printHex(*(ptr++)); + printHex(*(ptr++)); + printHex(*(ptr++)); + printHex(*(ptr)); + } else { + ptr += 3; + printHex(*(ptr--)); + printHex(*(ptr--)); + printHex(*(ptr--)); + printHex(*(ptr)); + } +} + +uint8_t * USBSerial_::printHex(uint8_t* p, uint8_t len, char sep) { + for (uint8_t i = 0; i < len; i++) { + if (sep && i) write(sep); + printHex(*p++); + } + println(); + return p; +} + +uint16_t * USBSerial_::printHex(uint16_t* p, uint8_t len, char sep, bool swaporder) { + for (uint8_t i = 0; i < len; i++) { + if (sep && i) write(sep); + printHex(*p++, swaporder); + } + println(); + return p; +} + +volatile uint8_t * USBSerial_::printHex(volatile uint8_t* p, uint8_t len, char sep) { + for (uint8_t i = 0; i < len; i++) { + if (sep && i) write(sep); + uint8_t t = *p++; + printHex(t); + } + return p; +} + +volatile uint16_t * USBSerial_::printHex(volatile uint16_t* p, uint8_t len, char sep, bool swaporder) { + for (uint8_t i = 0; i < len; i++) { + if (sep && i) write(sep); + uint16_t t = *p++; + printHex(t, swaporder); + } + return p; +} + +#endif /* USB0 */ diff --git a/megaavr/cores/dxcore/USBSerial.h b/megaavr/cores/dxcore/USBSerial.h new file mode 100644 index 00000000..a97b0693 --- /dev/null +++ b/megaavr/cores/dxcore/USBSerial.h @@ -0,0 +1,191 @@ +/* USBSerial.h - Arduino Stream wrapper for the AVR DU native-USB CDC stack. + * + * This library is free software released under LGPL 2.1. + * See LICENSE.md for more information. + * This file is part of DxCore. + * + * Copyright (c) 2025-2026 Yusuke Shimizu (Workshop Asahi) + * + * The printHex()/printHexln() member family (lightweight overloads inline + * here, heavy bodies in USBSerial.cpp) mirrors the implementation in DxCore's + * HardwareSerial.h / UART.cpp so that UART-oriented example sketches compile + * and behave identically against the native-USB Serial: + * Copyright (c) 2017-2021 Spence Konde and contributors (see UART.h). + * + * This file was originally distributed under the MIT license (c) Yusuke + * Shimizu; it was relicensed to LGPL 2.1 to match DxCore when the shared + * printHex() implementation was adopted from the UART class. + * + * ------------------------------------------------------------------------ + * On parts that have the USB peripheral (AVR DU family -> USB0 defined) this + * provides the `USBSerial` instance, which the DU variant aliases to `Serial` + * (Arduino Leonardo convention: Serial = native USB CDC, Serial1 = the first + * hardware UART). + * + * The stack is fully interrupt-driven (see usb_core.c): enumeration and data + * transfer happen in the USB ISRs, so a sketch needs no usbPoll() calls: + * + * Serial.begin(115200); + * if (Serial) { // host has opened the COM port (DTR) + * Serial.println("Hello, USB!"); + * } + * + * Included near the end of HardwareSerial.h (guarded by USB0) so the + * Serial/USBSerial symbol is declared wherever Serial is used. It depends only + * on the ArduinoCore-API Stream/Print classes, which HardwareSerial.h has + * already pulled in by that point - it must NOT include (that + * would be a circular include during core compilation). + */ +#pragma once + +#include /* ensure the USB0 macro is defined before we test for it */ +#if defined(USB0) + +#include +#include +#include "api/Stream.h" + +extern "C" { + #include "usb_core.h" /* usbInit / usbAttach / usbPoll / usbIsConfigured */ + #include "usb_cdc.h" /* usbCdc* ring-buffer API */ +} + +class USBSerial_ : public Stream { +public: + USBSerial_(void) {} + + /* ---- HardwareSerial pin/mux API: compatibility shims ---- + * The native USB CDC has no remappable pins, so pins()/swap() are no-ops + * that report "not applied" (false). They mirror the HardwareSerial + * signatures only so that UART-oriented sketches calling Serial.swap() / + * Serial.pins() still compile against the USB Serial. */ + bool pins(uint8_t tx, uint8_t rx) { (void)tx; (void)rx; return false; } + bool swap(uint8_t mux_level = 1) { (void)mux_level; return false; } + + /** Bring up the native USB CDC (usbInit + usbAttach). The baud argument is + * accepted for Arduino compatibility but ignored - USB CDC always runs at + * full speed. Safe to call repeatedly; heavy init runs once (guarded in + * the .cpp). */ + void begin(unsigned long baud = 115200); + void begin(unsigned long baud, uint8_t config); + void end(void); + + /** True once the host has opened the virtual COM port (DTR asserted). */ + operator bool(void) const { return usbCdcReady(); } + + /* ---- Serial_ compatibility (native-USB Arduino cores) ----------------- + * The Serial_ class of the official native-USB cores exposes the CDC + * control-line state and the host's requested line coding. Sketches use + * them to detect a serial monitor opening (dtr()) or to mirror the + * host's baud onto a hardware UART (a USB-to-serial bridge is the + * classic example). Same names, same semantics, same raw encodings as + * ArduinoCore-avr USBAPI.h / CDC.cpp. */ + bool dtr(void) const { return (usbCdcLineState() & 0x01) != 0; } + bool rts(void) const { return (usbCdcLineState() & 0x02) != 0; } + uint32_t baud(void) const { return usbCdcLineCodingBaud(); } + uint8_t stopbits(void) const { return usbCdcLineCodingStopBits(); } + uint8_t paritytype(void) const { return usbCdcLineCodingParity(); } + uint8_t numbits(void) const { return usbCdcLineCodingDataBits(); } + + /** Most recent host SEND_BREAK duration, consumed on read (-1 = none). */ + int32_t readBreak(void) { return usbCdcReadBreak(); } + + enum { + ONE_STOP_BIT = 0, + ONE_AND_HALF_STOP_BIT = 1, + TWO_STOP_BITS = 2, + }; + enum { + NO_PARITY = 0, + ODD_PARITY = 1, + EVEN_PARITY = 2, + MARK_PARITY = 3, + SPACE_PARITY = 4, + }; + + /* ---- printHex! ---- mirrors HardwareSerial (DxCore UART.cpp) ---------- + * Lightweight casting overloads are inline; the uint8_t / uint16_t bodies + * and the pointer/array versions live in USBSerial.cpp. */ + void printHex(const uint8_t b); // in the cpp + void printHex(const int8_t b) { printHex((uint8_t ) b); } + void printHex(const char b) { printHex((uint8_t ) b); } + void printHex(const uint16_t w, bool s = 0); // in the cpp + void printHex(const int16_t w, bool s = 0) { printHex((uint16_t)w, s); } + void printHex(const uint32_t l, bool s = 0) { _prtHxdw((uint8_t *) &l, s); } // this lets all three 4-byte datatypes + void printHex(const int32_t d, bool s = 0) { _prtHxdw((uint8_t *) &d, s); } // share the body + void printHex(const float f, bool s = 0) { _prtHxdw((uint8_t *) &f, s); } + void printHex(const double f, bool s = 0) { _prtHxdw((uint8_t *) &f, s); } + + /* ---- printHexln! - like printHex() with an added newline ---- */ + void printHexln(const char b) { printHex((uint8_t ) b); println(); } + void printHexln(const int8_t b) { printHex((uint8_t ) b); println(); } + void printHexln(const uint8_t b) { printHex( b); println(); } + void printHexln(const int16_t w, bool s = 0) { printHex((uint16_t)w, s); println(); } + void printHexln(const uint16_t w, bool s = 0) { printHex( w, s); println(); } + void printHexln(const float f, bool s = 0) { _prtHxdw((uint8_t *) &f, s); println(); } + void printHexln(const double f, bool s = 0) { _prtHxdw((uint8_t *) &f, s); println(); } + void printHexln(const int32_t d, bool s = 0) { _prtHxdw((uint8_t *) &d, s); println(); } + void printHexln(const uint32_t l, bool s = 0) { _prtHxdw((uint8_t *) &l, s); println(); } + + /* Pointer/array versions: gnaw off len elements and return the advanced + * pointer (typically used to dump a peripheral register block). Bodies in + * the cpp; the ln-variants wrap them like HardwareSerial does. */ + uint8_t * printHex( uint8_t* p, uint8_t len, char sep = 0 ); + uint16_t * printHex( uint16_t* p, uint8_t len, char sep = 0, bool s = 0); + volatile uint8_t * printHex(volatile uint8_t* p, uint8_t len, char sep = 0 ); + volatile uint16_t * printHex(volatile uint16_t* p, uint8_t len, char sep = 0, bool s = 0); + + uint8_t * printHexln( uint8_t* p, uint8_t len, char sep = 0 ) { + uint8_t* ret = printHex(p, len, sep); + println(); return ret; + } + uint16_t * printHexln( uint16_t* p, uint8_t len, char sep = 0, bool s = 0) { + uint16_t* ret = printHex(p, len, sep, s); + println(); return ret; + } + volatile uint8_t * printHexln(volatile uint8_t* p, uint8_t len, char sep = 0 ) { + volatile uint8_t* ret = printHex(p, len, sep); + println(); return ret; + } + volatile uint16_t * printHexln(volatile uint16_t* p, uint8_t len, char sep = 0, bool s = 0) { + volatile uint16_t* ret = printHex(p, len, sep, s); + println(); return ret; + } + + /* ---- Stream / Print API ---- */ + int availableForWrite(void) override { return (int)usbCdcTxFree(); } + int available(void) override { return (int)usbCdcAvailable(); } + int read(void) override { return usbCdcRead(); } + int peek(void) override { return -1; } /* ring buffer has no peek */ + + void flush(void) override { + /* TX is drained by the USB ISR in the background; wait until the ring + * is empty and the last packet has gone out (bounded spin). */ + for (uint32_t guard = 0; guard < 2000000UL; guard++) { + if (!usbCdcReady()) return; /* port closed: nothing to wait for */ + if (usbCdcTxIdle()) return; /* all data sent */ + } + } + size_t write(uint8_t b) override { + return usbCdcWriteByte(b) ? 1 : 0; /* enqueue; ISR sends it */ + } + size_t write(const uint8_t *buffer, size_t size) override { + return usbCdcWrite(buffer, (uint16_t)size); /* enqueue; ISR sends */ + } + + using Print::write; /* pull in write(const char*), write(str,len), etc. */ + +private: + /* Internal printHex for a 4-byte datatype: reads it as bytes and prints. + * Spelled to line up with the public overloads (mirrors UART.cpp). */ + void _prtHxdw(uint8_t* p, bool s = 0); +}; + +extern USBSerial_ USBSerial; + +/* Bring up the native USB CDC at boot (Arduino Leonardo-style USBDevice.attach()). + * Defined in USBSerial.cpp; idempotent. Called from main() so even sketches that + * never touch Serial (e.g. Blink) still enumerate as the COM port. */ +extern "C" void usb_auto_init(void); + +#endif /* USB0 */ diff --git a/megaavr/cores/dxcore/api/PluggableUSB.cpp b/megaavr/cores/dxcore/api/PluggableUSB.cpp index 08cfe0d2..92aca7d5 100644 --- a/megaavr/cores/dxcore/api/PluggableUSB.cpp +++ b/megaavr/cores/dxcore/api/PluggableUSB.cpp @@ -82,7 +82,7 @@ bool PluggableUSB_::plug(PluggableUSBModule *node) { node->pluggedEndpoint = lastEp; lastIf += node->numInterfaces; for (uint8_t i = 0; i < node->numEndpoints; i++) { - *(unsigned int *)(epBuffer(lastEp)) = node->endpointType[i]; + *(uint8_t *)(epBuffer(lastEp)) = node->endpointType[i]; lastEp++; } return true; diff --git a/megaavr/cores/dxcore/api/PluggableUSB.h b/megaavr/cores/dxcore/api/PluggableUSB.h index 056aed8e..87c4f9c1 100644 --- a/megaavr/cores/dxcore/api/PluggableUSB.h +++ b/megaavr/cores/dxcore/api/PluggableUSB.h @@ -29,7 +29,7 @@ void *epBuffer(unsigned int n); // -> returns a pointer to the Nth element of th class PluggableUSBModule { public: - PluggableUSBModule(uint8_t numEps, uint8_t numIfs, unsigned int *epType) : + PluggableUSBModule(uint8_t numEps, uint8_t numIfs, uint8_t *epType) : numEndpoints(numEps), numInterfaces(numIfs), endpointType(epType) { } @@ -47,7 +47,7 @@ class PluggableUSBModule { const uint8_t numEndpoints; const uint8_t numInterfaces; - const unsigned int *endpointType; + const uint8_t *endpointType; PluggableUSBModule *next = NULL; diff --git a/megaavr/cores/dxcore/api/USBAPI.h b/megaavr/cores/dxcore/api/USBAPI.h index d3639683..768a0852 100644 --- a/megaavr/cores/dxcore/api/USBAPI.h +++ b/megaavr/cores/dxcore/api/USBAPI.h @@ -54,4 +54,29 @@ int USB_Recv(uint8_t ep, void* data, int len); // non-blocking int USB_Recv(uint8_t ep); // non-blocking void USB_Flush(uint8_t ep); + +//================================================================================ +//================================================================================ +// USB Device control object -- Arduino Leonardo / UNO R4 compatible +// +// Mirrors the surface of ArduinoCore-avr's USBDevice_ (Leonardo/Micro) and the +// equivalent device object on ArduinoCore-renesas (UNO R4), so that libraries +// written for native-USB Arduinos (HID-Project, MIDIUSB, Keyboard, Mouse, ...) +// compile and behave unchanged on the AVR-DU. +// Implemented for the AVR-DU USB peripheral in USBCore_DU.cpp. + +class USBDevice_ +{ +public: + USBDevice_(); + bool configured(); // true once the host has issued SET_CONFIGURATION + void attach(); // connect to the bus (CTRLB.ATTACH) + void detach(); // disconnect from the bus + void poll(); // service the polled USB stack + bool wakeupHost(); // device-initiated remote wakeup; false if not allowed + bool isSuspended(); // true while the bus is in Suspend +}; + +extern USBDevice_ USBDevice; + #endif diff --git a/megaavr/cores/dxcore/main.cpp b/megaavr/cores/dxcore/main.cpp index 10d551c3..8be20829 100644 --- a/megaavr/cores/dxcore/main.cpp +++ b/megaavr/cores/dxcore/main.cpp @@ -87,10 +87,40 @@ int main() __attribute__((weak)); * overrides main. In the past there was a USB-related function here, that is removed, as work * * will be needed in any event at the core level if VUSB-based "stuff" arrives, but really I'm * * just waiting for the DU-series now */ +/* AVR DU native USB auto-init: forward declaration at file scope so the + * call in main() does not depend on the Arduino.h -> USBAPI.h -> USBSerial.h + * include chain (an upstream merge can break it). A linkage specification + * is only legal at namespace scope, hence here rather than inside main(). + * The definition lives in USBSerial.cpp (extern "C"). */ +#if defined(USB0) +extern "C" void usb_auto_init(void); +#endif + int main() { onBeforeInit(); // Emnpty callback called before init but after the .init stuff. First normal code executed init(); // Interrupts are turned on just prior to init() returning. initVariant(); +/* >>> AVR DU native USB auto-init ===================================== */ +#if defined(USB0) + /* Auto-start native USB CDC at boot only when the board wants Serial == USB + * active out of the box. The USB-CDC bootloader board needs it so the CDC + * port enumerates for the 1200bps-touch upload reset (and Serial works at + * once); a plain no-bootloader DU board leaves USB inactive until the sketch + * calls Serial.begin(), so Serial emits nothing unless the port is opened. + * Default follows the CDC-bootloader flag; a board may force it on/off via + * -DUSB_AUTO_INIT=1 / 0 from boards.txt (e.g. a no-bootloader USB-native board). */ + #if !defined(USB_AUTO_INIT) + #if defined(USING_AVRDU_CDC_BOOTLOADER) + #define USB_AUTO_INIT 1 + #else + #define USB_AUTO_INIT 0 + #endif + #endif + #if USB_AUTO_INIT + usb_auto_init(); /* declared above (extern "C"); defined in USBSerial.cpp */ + #endif +#endif +/* <<< AVR DU native USB auto-init ====================================== */ if (!onAfterInit()) sei(); // enable interrupts. setup(); for (;;) { diff --git a/megaavr/cores/dxcore/usb_cdc.c b/megaavr/cores/dxcore/usb_cdc.c new file mode 100644 index 00000000..ad585265 --- /dev/null +++ b/megaavr/cores/dxcore/usb_cdc.c @@ -0,0 +1,515 @@ +/* AVR DU native-USB stack. Compiled only on parts with the USB peripheral. */ +#include /* defines USB0 on parts that have USB; must precede the guard */ +#if defined(USB0) +/** + * usb_cdc.c + * CDC-ACM implementation: line coding, control line state, ring buffers, + * Leonardo-style 1200bps touch reset. + * + * EP1 IN (16B) - notification (unused, but initialised) + * EP2 OUT (64B) - data RX (host -> device) + * EP3 IN (64B) - data TX (device -> host) + */ +#include +#include +#include +#include +#include "usb_core.h" +#include "usb_cdc.h" + +/* ============================================================ + * CDC class request codes (USB CDC PSTN 1.20 Table 13) + * ============================================================ */ +#define CDC_REQ_SET_LINE_CODING 0x20 +#define CDC_REQ_GET_LINE_CODING 0x21 +#define CDC_REQ_SET_CONTROL_LINE_STATE 0x22 +#define CDC_REQ_SEND_BREAK 0x23 + +#define CDC_CTRL_LINE_DTR 0x01 +#define CDC_CTRL_LINE_RTS 0x02 + +/* ============================================================ + * Bootloader handshake contract + * + * When USING_AVRDU_CDC_BOOTLOADER is defined (set by boards.txt for + * the avrduusb.* board family), the 1200 bps touch path writes a + * known magic value into GPR.GPR1 (low) + GPR.GPR2 (high) just before + * triggering a watchdog reset. The bootloader reads those GPRs on + * every boot and stays resident if it sees AVRDU_BL_MAGIC_STAY. + * + * Why GPRs and not an SRAM address? AVR64DU32 SRAM ends at 0x7FFF, + * and the very first CALL in the bootloader's crt0 (the call to + * main()) pushes the return address to 0x7FFE/0x7FFF, destroying any + * 16-bit value we had stored there. The four general-purpose I/O + * registers (datasheet section 9 "General Purpose Registers", at I/O + * 0x001C..0x001F) survive software/watchdog/BOD resets and are never + * touched by crt0 or the stack, so they are the natural place for + * this kind of cross-reset handshake. + * + * GPR.GPR0 is reserved by the Optiboot/DxCore convention to carry the + * reset cause to the application (see cores/dxcore/main.cpp's + * init_reset_flags), so we use GPR1 + GPR2 here. GPR3 stays free. + * + * The mapping must match exactly between this file and + * bootloaders/avrdu_cdc_bl/src/main.c. + * ============================================================ */ +#define AVRDU_BL_MAGIC_STAY 0xB007u + +/* ============================================================ + * Diagnostic bit flags stored in GPR.GPR3. + * + * GPR.GPR3 is preserved across software / WDT / BOD resets and is not + * touched by the BL's normal startup sequence, so we can use it as a + * cross-reset breadcrumb trail: the runtime sets bits as it makes + * progress through the 1200 bps touch path, and the BL displays them + * on the diagnostic LED at startup. After the user pulls the breadcrumb + * pattern off the LED they can press PF6 to re-enter the BL via EXTRF; + * the bits will be cleared then so the next attempt starts fresh. + * + * Bit layout: + * 0 any SET_LINE_CODING was received + * 1 SET_LINE_CODING completed with dwDTERate == 1200 + * 2 any SET_CONTROL_LINE_STATE was received + * 3 SET_CONTROL_LINE_STATE delivered DTR=0 while baud==1200 + * 4 arm_1200bps_reset() was entered (reset deferred to the SOF hook) + * 5 reserved + * 6 reserved + * 7 reserved + * ============================================================ */ +#define AVRDU_DIAG_SLC_SEEN (1u << 0) +#define AVRDU_DIAG_SLC_1200 (1u << 1) +#define AVRDU_DIAG_CLS_SEEN (1u << 2) +#define AVRDU_DIAG_CLS_DTR0 (1u << 3) +#define AVRDU_DIAG_TRIG_ENTER (1u << 4) + +static inline void diag_set(uint8_t bm) { +#ifdef USING_AVRDU_CDC_BOOTLOADER + GPR.GPR3 |= bm; +#else + (void)bm; +#endif +} + +/* ============================================================ + * CDC Line Coding structure (7 bytes) + * ============================================================ */ +typedef struct __attribute__((packed)) { + uint32_t dwDTERate; /* baud rate */ + uint8_t bCharFormat; /* 0=1 stop, 1=1.5, 2=2 */ + uint8_t bParityType; /* 0=none, 1=odd, 2=even, 3=mark, 4=space */ + uint8_t bDataBits; /* 5,6,7,8,16 */ +} cdc_line_coding_t; + +static cdc_line_coding_t g_line_coding = { + .dwDTERate = 115200, + .bCharFormat = 0, + .bParityType = 0, + .bDataBits = 8 +}; + +static uint8_t volatile g_control_line_state = 0; /* DTR/RTS */ +static bool g_pending_set_line_coding = false; +static volatile int32_t g_break_value = -1; /* last CDC SEND_BREAK, -1 = none pending */ + + +/* ============================================================ + * RX / TX ring buffers + * ============================================================ */ +#define CDC_RX_RING_SIZE 192 +#define CDC_TX_RING_SIZE 192 + +static uint8_t g_rx_ring[CDC_RX_RING_SIZE]; +static volatile uint8_t g_rx_head = 0; /* write by USB stack */ +static volatile uint8_t g_rx_tail = 0; /* read by app */ + +static uint8_t g_tx_ring[CDC_TX_RING_SIZE]; +static volatile uint8_t g_tx_head = 0; /* write by app */ +static volatile uint8_t g_tx_tail = 0; /* read by USB stack */ + +static volatile bool g_tx_in_flight = false; /* EP3 currently transmitting */ +static volatile bool g_tx_last_full = false; /* last armed EP3 IN packet was exactly USB_EP3_SIZE */ + +/* ============================================================ + * 1200 bps touch-reset (Leonardo style) + * + * The IDE drops DTR low while baud=1200 to request the bootloader. + * We hand off to the bootloader by issuing a SOFTWARE RESET: this + * sets RSTCTRL.RSTFR.SWRF, which - unlike the GPR registers, whose + * reset value is 0x00 - is RETAINED across the reset by design (the + * datasheet: "After any Reset, the source that caused the Reset is + * found in the Reset Flag"). The bootloader stays resident whenever + * it sees SWRF. The earlier GPR-magic scheme could never work because + * the reset clears the GPRs before the BL can read them. + * ============================================================ */ +/* Deferred-reset countdown, decremented once per SOF (~1 ms). + * 0 : no reset pending + * 1..TIMEOUT : reset armed; fires early when the EP0 status stage + * completes (g_ctrl_state back to CTRL_IDLE), or at + * countdown expiry as a fallback. + * + * WHY DEFERRED: the touch request (SET_CONTROL_LINE_STATE or + * SET_LINE_CODING) is a control transfer. ep0_send_zlp() only ARMS the + * status-stage ZLP; the host has not fetched it yet. Detaching inside + * the request handler therefore yanks the device off the bus with the + * host's control request still outstanding, and the Windows usbser + * driver sits on that request until its own multi-second timeout - which + * is exactly the "long pause after the 1200 bps touch" seen during + * uploads. Caterina (Leonardo) avoids this by arming a 120 ms WDT in the + * handler and letting USB run on, so the status stage completes on the + * wire before the reset. We do the same, but SOF-driven and completion- + * aware, so the added latency is 1-2 ms in the normal case instead of a + * fixed 120 ms. (A WDT is not usable here: the bootloader stays resident + * on RSTFR.SWRF, and a watchdog reset would set WDRF instead.) */ +#define CDC_RESET_TIMEOUT_TICKS 15u /* ~15 ms fallback ceiling */ +static volatile uint8_t g_reset_countdown = 0; + +static void arm_1200bps_reset(void) { + diag_set(AVRDU_DIAG_TRIG_ENTER); + g_reset_countdown = CDC_RESET_TIMEOUT_TICKS; + /* The actual detach + SWRST happens in usb_cdc_on_sof() once the + * status stage of THIS control transfer has gone out (or on timeout). */ +} + +static void perform_1200bps_reset(void) { + /* Detach from the bus so the host sees a clean disconnect, then give + * it a moment before we reset and re-enumerate as the bootloader. + * ~10 ms is ample for disconnect detection (a couple of frames); + * the previous 120000-iteration wait (~40 ms) was longer than needed. */ + USB0.CTRLB &= ~USB_ATTACH_bm; + for (volatile uint32_t i = 0; i < 30000UL; i++) { __asm__ __volatile__("nop"); } + + /* Software reset -> RSTFR.SWRF set on next boot -> bootloader stays. */ + _PROTECTED_WRITE(RSTCTRL.SWRR, RSTCTRL_SWRST_bm); + while (1) { /* await reset */ } +} + +/* ============================================================ + * EP2 OUT (RX from host) — invoked from the TRNCOMPL ISR + * ============================================================ */ +void usb_cdc_on_ep2_out(uint16_t cnt) { + if (cnt > USB_EP2_SIZE) cnt = USB_EP2_SIZE; + for (uint16_t i = 0; i < cnt; i++) { + uint8_t next = (uint8_t)((g_rx_head + 1) % CDC_RX_RING_SIZE); + if (next == g_rx_tail) break; /* overflow: drop rest */ + g_rx_ring[g_rx_head] = g_ep2_out_buf[i]; + g_rx_head = next; + } +} + +/* ============================================================ + * EP3 IN done — clear in-flight flag so next packet can be queued + * ============================================================ */ +static void cdc_tx_pump(void); /* fwd decl */ + +void usb_cdc_on_ep3_in_done(void) { + g_tx_in_flight = false; + cdc_tx_pump(); /* send next queued chunk, if any (ISR context) */ +} + +/* ============================================================ + * TX pump — fills EP3 IN buffer from g_tx_ring. + * Runs in ISR context (EP3-IN-done and SOF hooks); from main context it + * is reached via cdc_tx_kick(), which masks interrupts to stay race-free. + * ============================================================ */ +static void cdc_tx_pump(void) { + if (g_tx_in_flight) return; + if (g_current_configuration != 1) return; + + /* Endpoint must be NAK (= ready) before we touch CNT */ + if (!(g_ep_table.EP[3].IN.STATUS & USB_BUSNAK_bm)) return; + + uint16_t n = 0; + while (n < USB_EP3_SIZE) { + if (g_tx_tail == g_tx_head) break; + g_ep3_in_buf[n++] = g_tx_ring[g_tx_tail]; + g_tx_tail = (g_tx_tail + 1) % CDC_TX_RING_SIZE; + } + if (n == 0) return; + + g_ep_table.EP[3].IN.CNT = n; + while (USB0.INTFLAGSB & USB_RMWBUSY_bm) {} + USB0.STATUS[3].INCLR = USB_BUSNAK_bm; + g_tx_in_flight = true; + /* Remember whether this was a maximum-length packet: a full 64-byte + * bulk-IN packet does NOT terminate the host's transfer, so if the + * ring then drains we must emit a ZLP (see usb_cdc_on_sof). */ + g_tx_last_full = (n == USB_EP3_SIZE); +} + +/* Start a TX transfer from NON-interrupt (main) context, race-free against + * the EP3-done ISR by briefly masking interrupts around the pump. */ +static inline void cdc_tx_kick(void) { + uint8_t sreg = SREG; + cli(); + cdc_tx_pump(); + SREG = sreg; +} + +/* Enqueue one byte into the TX ring WITHOUT kicking (internal helper). */ +static bool tx_ring_put(uint8_t b) { + uint8_t next = (uint8_t)((g_tx_head + 1) % CDC_TX_RING_SIZE); + if (next == g_tx_tail) return false; /* ring full */ + g_tx_ring[g_tx_head] = b; + g_tx_head = next; + return true; +} + +/* ============================================================ + * Start-of-Frame hook (called from the bus-event ISR every 1 ms). + * + * A USB bulk-IN transfer is only delivered to the host application when it + * is terminated by a short packet (< wMaxPacketSize) or a zero-length + * packet (ZLP). A tight sketch loop keeps the TX ring full, so every packet + * is a full 64-byte packet and the host never sees a terminator - the data + * sits buffered on the host until ~64-byte boundaries pile up. This is why + * inserting delay() (which lets the ring drain to a short final packet) + * "fixes" output. + * + * Here we flush automatically: once per frame, if nothing is in flight and + * the ring is empty but the last packet we sent was full, arm a ZLP to + * close the transfer so the host releases the buffered bytes. The flag is + * cleared after one ZLP, so a continuous stream (ring never empty) is never + * interrupted and an idle link emits exactly one terminating ZLP. + * ============================================================ */ +void usb_cdc_on_sof(void) { + /* Deferred 1200 bps touch reset: fire as soon as the touch request's + * status stage has completed (control state machine back to IDLE), or + * after the countdown expires as a fallback. Checked before any early + * return below so it cannot be starved. Runs in the BUSEVENT ISR; the + * TRNCOMPL ISR that moves g_ctrl_state to CTRL_IDLE cannot preempt us + * (no nested interrupts), so the state read is consistent. Relies on + * SOFs still arriving, which holds in practice: the host does not + * suspend the bus in the milliseconds between the touch and the + * reset. */ + if (g_reset_countdown) { + if (g_ctrl_state == CTRL_IDLE || --g_reset_countdown == 0) { + perform_1200bps_reset(); /* never returns */ + } + } + if (g_current_configuration != 1) return; + if (g_tx_in_flight) return; + /* Data queued but not yet shipped: ensure forward progress. */ + if (g_tx_head != g_tx_tail) { cdc_tx_pump(); return; } + /* Ring empty: terminate a dangling full-packet transfer with a ZLP. */ + if (g_tx_last_full && (g_ep_table.EP[3].IN.STATUS & USB_BUSNAK_bm)) { + g_ep_table.EP[3].IN.CNT = 0; + while (USB0.INTFLAGSB & USB_RMWBUSY_bm) {} + USB0.STATUS[3].INCLR = USB_BUSNAK_bm; + g_tx_in_flight = true; + g_tx_last_full = false; + } +} + +void usbCdcPoll(void) { + /* Interrupt-driven now; retained for API compatibility. A kick here is + * harmless and guarantees forward progress if ever called manually. */ + cdc_tx_kick(); +} + +/* ============================================================ + * Reset / configured hooks + * ============================================================ */ +void usb_cdc_on_reset(void) { + g_rx_head = g_rx_tail = 0; + g_tx_head = g_tx_tail = 0; + g_tx_in_flight = false; + g_tx_last_full = false; + g_control_line_state = 0; + g_pending_set_line_coding = false; +} + +void usb_cdc_on_configured(void) { + g_tx_in_flight = false; + g_tx_last_full = false; +} + +/* ============================================================ + * Class request handling + * ============================================================ */ +void usb_cdc_handle_class_request(const usb_setup_t *s) { + switch (s->bRequest) { + case CDC_REQ_SET_LINE_CODING: + diag_set(AVRDU_DIAG_SLC_SEEN); + if (s->wLength == sizeof(cdc_line_coding_t)) { + g_pending_set_line_coding = true; + ep0_start_data_out((uint8_t *)&g_line_coding, sizeof(cdc_line_coding_t)); + } else { + ep0_stall(); + } + break; + + case CDC_REQ_GET_LINE_CODING: + ep0_start_data_in((const uint8_t *)&g_line_coding, + sizeof(cdc_line_coding_t), s->wLength); + break; + + case CDC_REQ_SET_CONTROL_LINE_STATE: { + diag_set(AVRDU_DIAG_CLS_SEEN); + uint8_t new_state = s->wValue & 0x03; + g_control_line_state = new_state; + ep0_send_zlp(); + /* Caterina-style touch-reset condition: do NOT require a falling + * edge on DTR. The Windows USB CDC driver does not always raise + * DTR on port open (especially when the previous host close left + * DTR low), so the "DTR was 1, now 0" edge we used to require + * is unreliable. Arduino Leonardo (Caterina) and Pro Micro both + * use the simpler "DTR currently low AND baud == 1200" test, and + * 1200 bps is by convention reserved for this purpose - no real + * application uses 1200 bps for ongoing communication - so a false + * positive is in practice impossible. + */ + if ((new_state & CDC_CTRL_LINE_DTR) == 0 + && g_line_coding.dwDTERate == 1200) { + diag_set(AVRDU_DIAG_CLS_DTR0); + arm_1200bps_reset(); /* reset deferred to usb_cdc_on_sof() */ + } + break; + } + + case CDC_REQ_SEND_BREAK: + /* wValue carries the break duration in ms (0 = end break, + * 0xFFFF = indefinite). Latched for usbCdcReadBreak(); as in the + * official CDC cores, an unread value is overwritten by the next + * request. */ + g_break_value = (int32_t)s->wValue; + ep0_send_zlp(); + break; + + default: + ep0_stall(); + break; + } +} + +/* Called from usb_class_data_out_complete after EP0 DATA-OUT stage */ +void usb_cdc_data_out_complete(void) { + if (g_pending_set_line_coding) { + g_pending_set_line_coding = false; + if (g_line_coding.dwDTERate == 1200) { + diag_set(AVRDU_DIAG_SLC_1200); + /* Order-independent touch detection. The host may set 1200 baud + * AFTER it has already driven DTR low: the order of + * SET_LINE_CODING vs SET_CONTROL_LINE_STATE during a port open + * is host-specific, and a redundant DTR=0 (when DTR was already + * 0) is frequently not re-sent. In that case the + * SET_CONTROL_LINE_STATE handler never sees "DTR=0 while + * baud==1200", because at the time it ran the baud was not yet + * 1200. So we evaluate the same condition here, when the baud + * becomes 1200, using the DTR state captured so far. Whichever + * of the two requests completes the (DTR==0 && baud==1200) pair + * last is the one that fires. */ + if ((g_control_line_state & CDC_CTRL_LINE_DTR) == 0) { + diag_set(AVRDU_DIAG_CLS_DTR0); + /* Do NOT arm the status-stage ZLP here: we are inside + * usb_class_data_out_complete(), and when we return, + * handle_ep0_out_complete() (usb_core.c) restores EP0 OUT + * to its SETUP-receive configuration and arms the ZLP + * itself. The old code short-circuited that (zlp + reset + * that never returned), leaving EP0 OUT in the data-stage + * configuration and detaching before the host could fetch + * the status stage. */ + arm_1200bps_reset(); /* reset deferred to usb_cdc_on_sof() */ + } + } + /* g_line_coding has been filled by the EP0 OUT stage already + * (ep0_start_data_out pointed the endpoint at it). */ + } +} + +/* ============================================================ + * Public API + * ============================================================ */ +bool usbCdcReady(void) { + return (g_current_configuration == 1) + && (g_control_line_state & CDC_CTRL_LINE_DTR); +} + +bool usbCdcTxReady(void) { + if (g_current_configuration != 1) return false; + return !g_tx_in_flight && (g_ep_table.EP[3].IN.STATUS & USB_BUSNAK_bm); +} + +/* True when nothing is queued and no packet is in flight (flush() target). */ +bool usbCdcTxIdle(void) { + return (!g_tx_in_flight) && (g_tx_head == g_tx_tail); +} + +/* diagnostics */ +uint8_t usbCdcLineState(void) { return g_control_line_state; } +bool usbCdcTxInFlight(void){ return g_tx_in_flight; } +uint32_t usbCdcLineCodingBaud(void) { return g_line_coding.dwDTERate; } + +uint8_t usbCdcLineCodingStopBits(void) { return g_line_coding.bCharFormat; } +uint8_t usbCdcLineCodingParity(void) { return g_line_coding.bParityType; } +uint8_t usbCdcLineCodingDataBits(void) { return g_line_coding.bDataBits; } + +int32_t usbCdcReadBreak(void) { + /* Read-and-clear must be atomic against the EP0 ISR that latches it. */ + uint8_t oldsreg = SREG; + cli(); + int32_t v = g_break_value; + g_break_value = -1; + SREG = oldsreg; + return v; +} + +uint16_t usbCdcAvailable(void) { + int16_t n = (int16_t)g_rx_head - (int16_t)g_rx_tail; + if (n < 0) n += CDC_RX_RING_SIZE; + return (uint16_t)n; +} + +uint16_t usbCdcTxFree(void) { + int16_t used = (int16_t)g_tx_head - (int16_t)g_tx_tail; + if (used < 0) used += CDC_TX_RING_SIZE; + /* tx_ring_put() keeps one slot empty to tell "full" from "empty" */ + return (uint16_t)((CDC_TX_RING_SIZE - 1) - used); +} + +int usbCdcRead(void) { + if (g_rx_head == g_rx_tail) return -1; + uint8_t b = g_rx_ring[g_rx_tail]; + g_rx_tail = (g_rx_tail + 1) % CDC_RX_RING_SIZE; + return b; +} + +uint16_t usbCdcReadBytes(uint8_t *dst, uint16_t maxlen) { + uint16_t n = 0; + while (n < maxlen) { + int c = usbCdcRead(); + if (c < 0) break; + dst[n++] = (uint8_t)c; + } + return n; +} + +bool usbCdcWriteByte(uint8_t b) { + if (!tx_ring_put(b)) return false; + cdc_tx_kick(); /* start TX now; ISR drives the rest */ + return true; +} + +uint16_t usbCdcWrite(const uint8_t *src, uint16_t len) { + uint16_t n = 0; + while (n < len && tx_ring_put(src[n])) n++; + if (n) cdc_tx_kick(); /* one kick for the whole buffer */ + return n; +} + +uint16_t usbCdcPrint(const char *s) { + uint16_t n = 0; + while (s[n]) { + if (!usbCdcWriteByte((uint8_t)s[n])) break; + n++; + } + return n; +} + +uint16_t usbCdcPrintln(const char *s) { + uint16_t n = usbCdcPrint(s); + usbCdcWriteByte('\r'); + usbCdcWriteByte('\n'); + return n + 2; +} + +#endif /* USB0 */ diff --git a/megaavr/cores/dxcore/usb_cdc.h b/megaavr/cores/dxcore/usb_cdc.h new file mode 100644 index 00000000..6fe44f83 --- /dev/null +++ b/megaavr/cores/dxcore/usb_cdc.h @@ -0,0 +1,94 @@ +/** + * usb_cdc.h + * CDC-ACM (Virtual COM Port) implementation + * + * EP1 IN notification (interrupt 16B) — initialised but unused + * EP2 OUT data RX from host (bulk 64B) + * EP3 IN data TX to host (bulk 64B) + */ +#ifndef USB_CDC_H +#define USB_CDC_H + +#include +#include +#include "usb_core.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* ============================================================ + * Public API + * ============================================================ */ + +/* True when host has SET_CONFIGURATION and asserted DTR */ +bool usbCdcReady(void); +bool usbCdcTxIdle(void); /* TX ring empty AND no packet in flight */ + +uint8_t usbCdcLineState(void); +bool usbCdcTxInFlight(void); + +/* Diagnostic: current host-requested line coding baud (dwDTERate). Useful + * for confirming the 1200 bps touch path from a user sketch via Serial1. */ +uint32_t usbCdcLineCodingBaud(void); + +/* Remaining fields of the host-requested line coding, exposed so that + * USBSerial can offer the standard Serial_ accessors (stopbits(), + * paritytype(), numbits()). Raw CDC-ACM encodings: + * stop bits : 0 = 1, 1 = 1.5, 2 = 2 + * parity : 0 = none, 1 = odd, 2 = even, 3 = mark, 4 = space + * data bits : 5, 6, 7, 8 or 16 (the literal bit count) */ +uint8_t usbCdcLineCodingStopBits(void); +uint8_t usbCdcLineCodingParity(void); +uint8_t usbCdcLineCodingDataBits(void); + +/* Most recent CDC SEND_BREAK duration requested by the host, consumed on + * read: returns 0..0xFFFF once, then -1 until the next request arrives + * (0 = end break, 0xFFFF = indefinite break). Matches Serial_::readBreak() + * in the official native-USB cores. */ +int32_t usbCdcReadBreak(void); + +/* True when EP3 IN is ready to accept a fresh buffer */ +bool usbCdcTxReady(void); + +/* Returns number of bytes available to read from RX buffer */ +uint16_t usbCdcAvailable(void); + +/* Returns free slots in the TX ring (for availableForWrite()) */ +uint16_t usbCdcTxFree(void); + +/* Read one byte from RX buffer, returns -1 if empty */ +int usbCdcRead(void); + +/* Read up to maxlen bytes into dst, returns bytes actually read */ +uint16_t usbCdcReadBytes(uint8_t *dst, uint16_t maxlen); + +/* Write a single byte (queues; returns false on overflow) */ +bool usbCdcWriteByte(uint8_t b); + +/* Write a buffer; returns number actually queued */ +uint16_t usbCdcWrite(const uint8_t *src, uint16_t len); + +/* Convenience helpers */ +uint16_t usbCdcPrint(const char *s); +uint16_t usbCdcPrintln(const char *s); + +/* ============================================================ + * Internal hooks called by USB stack + * ============================================================ */ +void usb_cdc_handle_class_request(const usb_setup_t *s); +void usb_cdc_data_out_complete(void); /* SET_LINE_CODING data done */ +void usb_cdc_on_configured(void); /* SET_CONFIGURATION(1) */ +void usb_cdc_on_reset(void); /* Bus reset */ +void usb_cdc_on_ep2_out(uint16_t cnt); /* EP2 OUT TRNCOMPL */ +void usb_cdc_on_ep3_in_done(void); /* EP3 IN TRNCOMPL */ +void usb_cdc_on_sof(void); /* SOF: idle TX flush (ZLP) */ + +/* Call from main loop to drive the TX pump */ +void usbCdcPoll(void); + +#ifdef __cplusplus +} +#endif + +#endif /* USB_CDC_H */ diff --git a/megaavr/cores/dxcore/usb_core.c b/megaavr/cores/dxcore/usb_core.c new file mode 100644 index 00000000..a2e23c22 --- /dev/null +++ b/megaavr/cores/dxcore/usb_core.c @@ -0,0 +1,531 @@ +/* AVR DU native-USB stack. Compiled only on parts with the USB peripheral. */ +#include /* defines USB0 on parts that have USB; must precede the guard */ +#if defined(USB0) +/** + * usb_core.c + * USB stack core: initialization, interrupt service routines, control + * state machine + * + * Target: AVR64DU32 with DxCore + * + * Architecture: + * - Uses official USB_EP_TABLE_t (FIFO[32] + EP[16] + FRAMENUM) + * - EPPTR set to &g_ep_table.EP[0] (FIFO occupies negative offsets) + * - Fully interrupt-driven: USB0_BUSEVENT_vect (reset/SOF) and + * USB0_TRNCOMPL_vect (SETUP + per-endpoint TRNCOMPL) drive all activity; + * usbPoll() is a no-op kept for source compatibility. + * - Uses official USB_*_bm macros from ioavr64du32.h + * + * EP map (CDC fixed on EP1..EP3; HID and other classes are allocated + * dynamically on EP4+ by PluggableUSB modules - see USBCore_DU.cpp): + * EP0 Control + * EP1 IN CDC notify (interrupt 16B) + * EP2 OUT CDC data RX (bulk 64B) + * EP3 IN CDC data TX (bulk 64B) + * EP4..EP7 dynamic (PluggableUSB: HID Keyboard/Mouse share one + * interrupt-IN EP via Report IDs, Leonardo-style) + */ +#include +#include +#include +#include +#include "usb_core.h" +#include "usb_standard.h" +#include "USBCore_DU.h" + +/* ============================================================ + * Global state + * ============================================================ */ +USB_EP_TABLE_t g_ep_table __attribute__((aligned(2))); + +uint8_t g_ep0_setup_buf[8] __attribute__((aligned(2))); +uint8_t g_ep0_data_buf[USB_EP0_SIZE] __attribute__((aligned(2))); +uint8_t g_ep1_in_buf[USB_EP1_SIZE] __attribute__((aligned(2))); +uint8_t g_ep2_out_buf[USB_EP2_SIZE] __attribute__((aligned(2))); +uint8_t g_ep3_in_buf[USB_EP3_SIZE] __attribute__((aligned(2))); +uint8_t g_ep4_in_buf[USB_EP4_SIZE] __attribute__((aligned(2))); +uint8_t g_ep5_in_buf[USB_EP5_SIZE] __attribute__((aligned(2))); +uint8_t g_ep6_in_buf[USB_EP6_SIZE] __attribute__((aligned(2))); + +ctrl_state_t g_ctrl_state = CTRL_IDLE; + +/* --- diagnostic snapshot (read by the test sketch over Serial1) --- */ +volatile uint8_t g_dbg_ctrl_state = 0; +volatile uint8_t g_dbg_ep0out_status = 0; +volatile uint8_t g_dbg_ep0in_status = 0; +volatile uint8_t g_dbg_intflagsb = 0; +volatile uint8_t g_dbg_usbaddr = 0; +uint8_t g_pending_address = 0; +volatile uint8_t g_current_configuration = 0; +uint8_t g_remote_wakeup_enabled = 0; + +volatile uint16_t g_reset_count = 0; +volatile uint16_t g_setup_count = 0; +volatile uint16_t g_get_desc_count = 0; +volatile uint16_t g_stall_count = 0; +volatile uint16_t g_setcfg_count = 0; +volatile uint16_t g_class_req_count = 0; +volatile uint16_t g_mpkt_count = 0; /* multipacket IN transfers started */ +volatile uint16_t g_ep0in_tc_count = 0; /* EP0 IN transaction-complete events */ +volatile uint8_t g_last_cfg_value = 0xFF; +volatile uint8_t g_last_bmRequestType = 0; +volatile uint8_t g_last_bRequest = 0; +volatile uint16_t g_last_wValue = 0; +volatile uint16_t g_last_wLength = 0; + +/* ============================================================ + * RMW busy wait + * Datasheet 28.3.3.1: "The result of a write to STATUS{In/Out}{Clear/Set} + * while RMWBUSY is set is undefined." + * ============================================================ */ +static inline void rmw_wait(void) { + while (USB0.INTFLAGSB & USB_RMWBUSY_bm) {} +} + +/* ============================================================ + * Forward declarations (CDC hooks; see usb_cdc.c) + * + * These are weak-linked so a no-CDC build still links. + * usb_cdc.c provides strong implementations. + * ============================================================ */ +__attribute__((weak)) void usb_cdc_on_configured(void) { } +__attribute__((weak)) void usb_cdc_on_reset(void) { } +__attribute__((weak)) void usb_cdc_on_ep2_out(uint16_t cnt) { (void)cnt; } +__attribute__((weak)) void usb_cdc_on_ep3_in_done(void) { } +__attribute__((weak)) void usb_cdc_on_sof(void) { } + +/* ============================================================ + * Endpoint table initialization + * + * On bus-reset all endpoints are wiped and rebuilt: + * EP0 control armed for SETUP, + * EP1/EP3/EP4/EP5/EP6 IN NAK until data is queued, + * EP2 OUT armed to receive bulk data from host. + * ============================================================ */ +static void usb_ep_table_init(void) { + /* Zero entire table (FIFO + EP[16] + FRAMENUM) */ + memset(&g_ep_table, 0, sizeof(g_ep_table)); + + /* EP0 OUT: Control, ready to receive SETUP */ + g_ep_table.EP[0].OUT.CTRL = USB_TYPE_CONTROL_gc | USB_BUFSIZE_DEFAULT_BUF64_gc; + g_ep_table.EP[0].OUT.DATAPTR = (uint16_t)g_ep0_setup_buf; + g_ep_table.EP[0].OUT.STATUS = 0x00; + + /* EP0 IN: Control, NAK until response queued */ + g_ep_table.EP[0].IN.CTRL = USB_TYPE_CONTROL_gc | USB_BUFSIZE_DEFAULT_BUF64_gc; + g_ep_table.EP[0].IN.DATAPTR = (uint16_t)g_ep0_data_buf; + g_ep_table.EP[0].IN.STATUS = USB_BUSNAK_bm; + + /* EP1 IN: CDC notification (interrupt 16B), NAK initially */ + g_ep_table.EP[1].IN.CTRL = USB_TYPE_BULKINT_gc | USB_BUFSIZE_DEFAULT_BUF16_gc; + g_ep_table.EP[1].IN.DATAPTR = (uint16_t)g_ep1_in_buf; + g_ep_table.EP[1].IN.STATUS = USB_BUSNAK_bm; + + /* EP2 OUT: CDC data RX (bulk 64B), armed to receive */ + g_ep_table.EP[2].OUT.CTRL = USB_TYPE_BULKINT_gc | USB_BUFSIZE_DEFAULT_BUF64_gc; + g_ep_table.EP[2].OUT.DATAPTR = (uint16_t)g_ep2_out_buf; + g_ep_table.EP[2].OUT.STATUS = 0x00; + + /* EP3 IN: CDC data TX (bulk 64B), NAK until data queued */ + g_ep_table.EP[3].IN.CTRL = USB_TYPE_BULKINT_gc | USB_BUFSIZE_DEFAULT_BUF64_gc; + g_ep_table.EP[3].IN.DATAPTR = (uint16_t)g_ep3_in_buf; + g_ep_table.EP[3].IN.STATUS = USB_BUSNAK_bm; + + /* HID and other classes are NOT hardcoded here. EP4..EP7 stay zeroed by + * the memset above (TYPE = DISABLED) and are programmed dynamically at + * SET_CONFIGURATION by usbcore_init_plugged_endpoints() from the EP types + * the PluggableUSB modules registered via plug(). Keyboard and Mouse use + * the bundled HID library, which is a single PluggableUSB module owning + * one interrupt-IN EP and one interface (Report IDs multiplex the two), + * exactly as on the Arduino Leonardo. */ +} + +/* ============================================================ + * EP0 control transfer helpers + * ============================================================ */ + +/* ---- EP0 IN data stage: software-driven single-packet chunking ---- + * The AVR DU hardware MULTIPKT feature did not reliably complete EP0 control + * reads larger than the endpoint size on this part. Instead we stream the + * data stage as a sequence of single-packet IN transactions, each copied into + * the word-aligned g_ep0_data_buf (the exact path already proven to work for + * short descriptors). The hardware auto-toggles DATA0/DATA1 between + * consecutive IN packets, so we must NOT touch TOGGLE between chunks. */ +static const uint8_t *g_ep0_in_src; /* next byte to send */ +static uint16_t g_ep0_in_rem; /* bytes still to send */ +static bool g_ep0_in_need_zlp; /* terminating ZLP required? */ + +/* Load and fire one IN packet (n <= USB_EP0_SIZE). EP0.IN must be deactivated + * (BUSNAK set) on entry — true right after SETUP and after each IN TRNCOMPL, + * which satisfies the datasheet rule that CNT/MCNT are written while NAKed. */ +static void ep0_send_chunk(uint16_t n) { + for (uint16_t i = 0; i < n; i++) g_ep0_data_buf[i] = g_ep0_in_src[i]; + g_ep0_in_src += n; + g_ep0_in_rem -= n; + + g_ep_table.EP[0].IN.DATAPTR = (uint16_t)g_ep0_data_buf; + g_ep_table.EP[0].IN.CNT = n; + g_ep_table.EP[0].IN.MCNT = 0; + g_ep_table.EP[0].IN.CTRL = USB_TYPE_CONTROL_gc | USB_BUFSIZE_DEFAULT_BUF64_gc; + + /* Activate: clear flags incl. BUSNAK, but preserve TOGGLE (DATA0/1 seq) */ + rmw_wait(); + USB0.STATUS[0].INCLR = USB_UNFOVF_bm | USB_TRNCOMPL_bm | USB_STALLED_bm | USB_BUSNAK_bm; +} + +void ep0_start_data_in(const uint8_t *data, uint16_t len, uint16_t host_requested) { + if (len > host_requested) len = host_requested; + + g_ep0_in_src = data; + g_ep0_in_rem = len; + /* A short final packet ends the transfer. If we send a whole number of + * max-size packets yet fewer bytes than requested, a ZLP is needed. */ + g_ep0_in_need_zlp = (len < host_requested) && (len != 0) + && ((len % USB_EP0_SIZE) == 0); + + if (len > USB_EP0_SIZE) g_mpkt_count++; /* diagnostic: multi-chunk read */ + + /* Pre-arm EP0.OUT once to ACK the status-stage ZLP that follows the data */ + rmw_wait(); + USB0.STATUS[0].OUTCLR = USB_UNFOVF_bm | USB_TRNCOMPL_bm | USB_STALLED_bm | USB_BUSNAK_bm; + + g_ctrl_state = CTRL_DATA_IN_STAGE; + + /* Fire the first packet (len==0 sends a single ZLP) */ + uint16_t n = (g_ep0_in_rem > USB_EP0_SIZE) ? USB_EP0_SIZE : g_ep0_in_rem; + ep0_send_chunk(n); +} + +void ep0_start_data_out(uint8_t *buffer, uint16_t len) { + /* Control-transfer data stage, OUT direction (host -> device, e.g. + * SET_LINE_CODING's 7-byte payload). + * + * Datasheet 28.3.2.2 says the data stage uses the IN buffer pointer, + * but the OUT-transaction machinery 28.3.2.4 says received data lands + * at EP[n].OUT.DATAPTR - and that is what the silicon actually does + * (confirmed empirically: with IN.DATAPTR-only the line-coding payload + * never reached our buffer, so SET_LINE_CODING(1200) never updated the + * baud and the touch-reset never fired). We therefore point BOTH + * direction pointers at the caller's buffer with identical multipacket + * settings, so the bytes land in `buffer` no matter which the silicon + * consults, then restore OUT.DATAPTR to the SETUP buffer when the data + * stage completes (see handle_ep0_out_complete). */ + g_ep_table.EP[0].IN.DATAPTR = (uint16_t)buffer; + g_ep_table.EP[0].IN.CNT = 0; + g_ep_table.EP[0].IN.MCNT = len; + g_ep_table.EP[0].IN.CTRL = USB_TYPE_CONTROL_gc | USB_MULTIPKT_bm | USB_BUFSIZE_DEFAULT_BUF64_gc; + + g_ep_table.EP[0].OUT.DATAPTR = (uint16_t)buffer; + g_ep_table.EP[0].OUT.CNT = 0; + g_ep_table.EP[0].OUT.MCNT = len; + g_ep_table.EP[0].OUT.CTRL = USB_TYPE_CONTROL_gc | USB_MULTIPKT_bm | USB_BUFSIZE_DEFAULT_BUF64_gc; + + /* Clear OUT BUSNAK to accept incoming OUT tokens. + * IN.BUSNAK stays set; the status-stage ZLP IN is armed later. */ + rmw_wait(); + USB0.STATUS[0].OUTCLR = USB_UNFOVF_bm | USB_TRNCOMPL_bm | USB_STALLED_bm | USB_BUSNAK_bm; + + g_ctrl_state = CTRL_DATA_OUT_STAGE; +} + +void ep0_send_zlp(void) { + g_ep_table.EP[0].IN.CNT = 0; + g_ep_table.EP[0].IN.MCNT = 0; + g_ep_table.EP[0].IN.CTRL = USB_TYPE_CONTROL_gc | USB_BUFSIZE_DEFAULT_BUF64_gc; + + rmw_wait(); + USB0.STATUS[0].INCLR = USB_UNFOVF_bm | USB_TRNCOMPL_bm | USB_STALLED_bm | USB_BUSNAK_bm; + + g_ctrl_state = CTRL_STATUS_IN_STAGE; +} + +void ep0_stall(void) { + g_stall_count++; + g_ep_table.EP[0].OUT.CTRL |= USB_DOSTALL_bm; + g_ep_table.EP[0].IN.CTRL |= USB_DOSTALL_bm; + g_ctrl_state = CTRL_IDLE; +} + +/* ============================================================ + * SETUP packet handler + * ============================================================ */ + +static void handle_setup(void) { + usb_setup_t *s = (usb_setup_t *)g_ep0_setup_buf; + + g_last_bmRequestType = s->bmRequestType; + g_last_bRequest = s->bRequest; + g_last_wValue = s->wValue; + g_last_wLength = s->wLength; + + /* Clear DOSTALL on both EP0 directions for the NEW transfer. + * While EPSETUP=1 the HW overrides DOSTALL (datasheet 28.7.4), + * but once we clear EPSETUP below, a stale DOSTALL would STALL + * the data/status stage. CTRL is plain SRAM, direct write is safe. */ + g_ep_table.EP[0].OUT.CTRL = USB_TYPE_CONTROL_gc | USB_BUFSIZE_DEFAULT_BUF64_gc; + g_ep_table.EP[0].IN.CTRL = USB_TYPE_CONTROL_gc | USB_BUFSIZE_DEFAULT_BUF64_gc; + + /* Clear EPSETUP on both directions (required before decoding) */ + rmw_wait(); + USB0.STATUS[0].OUTCLR = USB_EPSETUP_bm; + rmw_wait(); + USB0.STATUS[0].INCLR = USB_EPSETUP_bm; + + uint8_t type = s->bmRequestType & 0x60; + switch (type) { + case 0x00: usb_handle_standard_request(s); break; + case 0x20: usb_handle_class_request(s); break; + default: ep0_stall(); break; + } +} + +/* ============================================================ + * EP completion handlers + * ============================================================ */ +extern void usb_class_data_out_complete(void); /* in usb_standard.c */ + +static void handle_ep0_in_complete(void) { + switch (g_ctrl_state) { + case CTRL_DATA_IN_STAGE: + if (g_ep0_in_rem > 0) { + /* more data to send: fire the next single-packet chunk */ + uint16_t n = (g_ep0_in_rem > USB_EP0_SIZE) ? USB_EP0_SIZE : g_ep0_in_rem; + ep0_send_chunk(n); + } else if (g_ep0_in_need_zlp) { + /* exact multiple of max packet but short of wLength: terminate */ + g_ep0_in_need_zlp = false; + ep0_send_chunk(0); + } else { + /* all data sent — EP0.OUT was pre-armed in ep0_start_data_in() */ + g_ctrl_state = CTRL_STATUS_OUT_STAGE; + } + break; + + case CTRL_STATUS_PENDING_ADDR: + USB0.ADDR = g_pending_address; + g_pending_address = 0; + g_ctrl_state = CTRL_IDLE; + break; + + case CTRL_STATUS_IN_STAGE: + default: + g_ctrl_state = CTRL_IDLE; + break; + } +} + +static void handle_ep0_out_complete(void) { + switch (g_ctrl_state) { + case CTRL_DATA_OUT_STAGE: + /* DATA-OUT received — let class handler inspect, then ZLP status. + * Restore the OUT direction to its SETUP-receive configuration: + * we repointed DATAPTR and enabled multipacket in + * ep0_start_data_out(), and the next SETUP packet must be DMA'd + * back into g_ep0_setup_buf with the plain control config. */ + usb_class_data_out_complete(); + g_ep_table.EP[0].OUT.DATAPTR = (uint16_t)g_ep0_setup_buf; + g_ep_table.EP[0].OUT.CNT = 0; + g_ep_table.EP[0].OUT.MCNT = 0; + g_ep_table.EP[0].OUT.CTRL = USB_TYPE_CONTROL_gc | USB_BUFSIZE_DEFAULT_BUF64_gc; + ep0_send_zlp(); + break; + + case CTRL_STATUS_OUT_STAGE: + default: + g_ctrl_state = CTRL_IDLE; + break; + } +} + +/* ============================================================ + * Polled event loop — call from loop() as often as possible + * ============================================================ */ +/* ============================================================ + * Event service routines (invoked from the USB interrupt vectors) + * ============================================================ */ + +/* Bus events — currently just bus RESET (INTFLAGSA / USB0_BUSEVENT_vect). */ +static void usb_service_busevent(void) { + uint8_t flags_a = USB0.INTFLAGSA; + if (flags_a & USB_RESET_bm) { + g_reset_count++; + USB0.ADDR = 0; + usb_ep_table_init(); + g_ctrl_state = CTRL_IDLE; + g_current_configuration = 0; + g_pending_address = 0; + usb_cdc_on_reset(); + USB0.INTFLAGSA = USB_RESET_bm; + } + /* Start-of-Frame (1 ms): drive the CDC idle-flush so a paused TX + * stream that ended on a full 64-byte packet gets a terminating ZLP. */ + if (flags_a & USB_SOF_bm) { + usb_cdc_on_sof(); + USB0.INTFLAGSA = USB_SOF_bm; + } +} + +/* Transaction events — SETUP and per-endpoint TRNCOMPL + * (INTFLAGSB / USB0_TRNCOMPL_vect). */ +static void usb_service_trncompl(void) { + uint8_t flags_b = USB0.INTFLAGSB; + + /* SETUP arrived */ + if (flags_b & USB_SETUP_bm) { + g_setup_count++; + handle_setup(); + USB0.INTFLAGSB = USB_SETUP_bm; + } + + /* Transaction complete on any endpoint */ + if (flags_b & USB_TRNCOMPL_bm) { + if (g_ep_table.EP[0].IN.STATUS & USB_TRNCOMPL_bm) { + rmw_wait(); + USB0.STATUS[0].INCLR = USB_TRNCOMPL_bm; + g_ep0in_tc_count++; + handle_ep0_in_complete(); + } + if (g_ep_table.EP[0].OUT.STATUS & USB_TRNCOMPL_bm) { + rmw_wait(); + USB0.STATUS[0].OUTCLR = USB_TRNCOMPL_bm; + handle_ep0_out_complete(); + } + /* EP1 IN: CDC notification — HW re-sets BUSNAK, no action */ + if (g_ep_table.EP[1].IN.STATUS & USB_TRNCOMPL_bm) { + rmw_wait(); + USB0.STATUS[1].INCLR = USB_TRNCOMPL_bm; + } + /* EP2 OUT: CDC data from host */ + if (g_ep_table.EP[2].OUT.STATUS & USB_TRNCOMPL_bm) { + rmw_wait(); + USB0.STATUS[2].OUTCLR = USB_TRNCOMPL_bm; + uint16_t cnt = g_ep_table.EP[2].OUT.CNT; + usb_cdc_on_ep2_out(cnt); + /* Re-arm EP2 OUT for next packet */ + g_ep_table.EP[2].OUT.CNT = 0; + rmw_wait(); + USB0.STATUS[2].OUTCLR = USB_BUSNAK_bm; + } + /* EP3 IN: CDC TX done */ + if (g_ep_table.EP[3].IN.STATUS & USB_TRNCOMPL_bm) { + rmw_wait(); + USB0.STATUS[3].INCLR = USB_TRNCOMPL_bm; + usb_cdc_on_ep3_in_done(); + } + /* Dynamic PluggableUSB EPs (EP4+): IN reports are completed + * synchronously inside USB_Send() (it polls the per-EP TRNCOMPL in + * the SRAM endpoint table itself), so no servicing is required in the + * ISR. Dynamic OUT (host->device) data is drained here. */ + usbcore_service_dynamic_ep_trncompl(); + USB0.INTFLAGSB = USB_TRNCOMPL_bm; + } + + /* diagnostic snapshot of EP0 / control state for the test sketch */ + g_dbg_ctrl_state = (uint8_t)g_ctrl_state; + g_dbg_ep0out_status = g_ep_table.EP[0].OUT.STATUS; + g_dbg_ep0in_status = g_ep_table.EP[0].IN.STATUS; + g_dbg_intflagsb = USB0.INTFLAGSB; + g_dbg_usbaddr = USB0.ADDR; +} + +/* The USB stack is interrupt-driven; the application no longer needs to call + * usbPoll() from loop(). Retained as a no-op for source compatibility. */ +void usbPoll(void) { } + +/* Bus-event vector: RESET (and, in future, SUSPEND/RESUME). */ +ISR(USB0_BUSEVENT_vect) { + usb_service_busevent(); +} + +/* Transaction-complete vector: SETUP + per-endpoint TRNCOMPL. */ +ISR(USB0_TRNCOMPL_vect) { + usb_service_trncompl(); +} + +/* ============================================================ + * Initialization + * ============================================================ */ +void usbInit(void) { + /* 1. Enable OSCHF SOF auto-tune, INCREMENTAL search algorithm. + * + * Per datasheet (CLKCTRL.OSCHFCTRLA, sec 12.5.8 / 12.3.5): + * - AUTOTUNE=SOF makes OSCHF (and its fixed 4 MHz tap feeding the + * PLL48M USB clock) tune itself against the USB Start-of-Frame. + * This is REQUIRED for crystal-less USB. + * - ALGSEL selects the tuning algorithm used after each USB bus reset: + * 0 = Binary search (DEFAULT) -- "may temporarily change the + * oscillator output frequency up to HALF the tune range", + * which corrupts the 48 MHz USB clock mid-enumeration. The + * host then resets, the search restarts and swings again -> a + * reset storm with intermittent control-transfer failures. + * 1 = Incremental search -- nudges at most 5 tune steps after a + * reset, keeping the USB clock stable enough to enumerate. + * - The datasheet states ALGSEL "must be written simultaneously with + * writing the SOF setting to the AUTOTUNE bit field", so we set both + * fields in a single write below. + */ + uint8_t oschf = CLKCTRL.OSCHFCTRLA; + oschf &= ~(CLKCTRL_AUTOTUNE_gm | CLKCTRL_ALGSEL_bm); + oschf |= (CLKCTRL_AUTOTUNE_SOF_gc | CLKCTRL_ALGSEL_INCR_gc); + _PROTECTED_WRITE(CLKCTRL.OSCHFCTRLA, oschf); + + /* 2. Enable VUSB regulator (5V VDD -> 3.3V VUSB) */ + SYSCFG.VUSBCTRL = SYSCFG_USBVREG_bm; + + /* 3. Settle */ + _delay_ms(1); + + /* 4. Build EP table */ + usb_ep_table_init(); + + /* 5. Point USB peripheral at &g_ep_table.EP[0] + * Hardware layout (datasheet 28.7.1): + * EPPTR - 32 .. -1 : FIFO[31..0] (negative offsets) + * EPPTR + 0 : EP[0].OUT.STATUS + * EPPTR + (N+1)*16 : FRAMENUM + * USB_EP_TABLE_t has FIFO[32] first, so EPPTR points past it. */ + USB0.EPPTR = (uint16_t)&g_ep_table.EP[0]; + + /* 6. Enable USB interrupts (interrupt-driven; loop() need not poll). + * INTCTRLA -> USB0_BUSEVENT_vect ; INTCTRLB -> USB0_TRNCOMPL_vect */ + USB0.INTCTRLA = USB_RESET_bm | USB_SOF_bm; /* bus reset + SOF */ + USB0.INTCTRLB = USB_SETUP_bm | USB_TRNCOMPL_bm; /* setup + trncompl */ + + /* 7. Enable USB peripheral; MAXEP in bits [3:0] */ + USB0.CTRLA = USB_ENABLE_bm | USB_MAXEP; + + /* 8. Wait for PLL lock (with timeout for debug safety) */ + { + uint32_t timeout = 1000000UL; + while (!(CLKCTRL.USBPLLSTATUS & CLKCTRL_PLLS_bm) && --timeout) {} + if (timeout == 0) { + /* PLL lock failed - panic-blink the LED on PF2 (Curiosity Nano) */ + VPORTF.DIR |= (1 << 2); + while (1) { + PORTF.OUTTGL = (1 << 2); + _delay_ms(100); + } + } + } + + /* 9. Disable GNAUTO/GNAK (we manage NAK manually) */ + USB0.CTRLB = 0; + + /* 10. Make sure global interrupts are enabled so the USB ISRs run. + * (Arduino normally has them on already; this is belt-and-braces.) */ + sei(); +} + +void usbAttach(void) { + USB0.CTRLB |= USB_ATTACH_bm; +} + +void usbDetach(void) { + USB0.CTRLB &= ~USB_ATTACH_bm; +} + +bool usbIsConfigured(void) { + return g_current_configuration != 0; +} + +#endif /* USB0 */ diff --git a/megaavr/cores/dxcore/usb_core.h b/megaavr/cores/dxcore/usb_core.h new file mode 100644 index 00000000..00463a3d --- /dev/null +++ b/megaavr/cores/dxcore/usb_core.h @@ -0,0 +1,109 @@ +/** + * usb_core.h + * Public USB stack API + * + * Uses official USB_EP_TABLE_t and bit definitions from ioavr64du32.h. + * Architecture: fully interrupt-driven (USB0_BUSEVENT + USB0_TRNCOMPL). + * A sketch needs no usbPoll() calls; usbPoll() is a no-op kept only for + * source compatibility. + */ +#ifndef USB_CORE_H +#define USB_CORE_H + +#include +#include +#include +#include "usb_descriptors.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* ============================================================ + * USB SETUP packet structure (USB 2.0 spec 9.3, Table 9-2) + * ============================================================ */ +typedef struct __attribute__((packed)) { + uint8_t bmRequestType; + uint8_t bRequest; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; +} usb_setup_t; + +/* ============================================================ + * Control transfer state machine + * ============================================================ */ +typedef enum { + CTRL_IDLE, + CTRL_DATA_IN_STAGE, + CTRL_DATA_OUT_STAGE, + CTRL_STATUS_IN_STAGE, + CTRL_STATUS_OUT_STAGE, + CTRL_STATUS_PENDING_ADDR +} ctrl_state_t; + +/* ============================================================ + * Public API + * ============================================================ */ +void usbInit(void); +void usbAttach(void); +void usbDetach(void); +bool usbIsConfigured(void); + +/* Stage `n` bytes from a PROGMEM source into the config-descriptor accumulator. + * Used by usb_standard.c to ship device/string descriptors that live in flash. */ +void usbcore_acc_reset(void); +void usbcore_acc_load_P(const uint8_t *src_pgm, uint16_t n); +const uint8_t *usbcore_acc_buf(void); +uint16_t usbcore_acc_len(void); + +/* No-op kept for source compatibility. The stack is interrupt-driven, so + * calling this from loop() is neither required nor has any effect. */ +void usbPoll(void); + +/* ============================================================ + * Internal globals shared between USB modules + * + * g_ep_table is USB_EP_TABLE_t (from ioavr64du32.h): + * FIFO[32] : transaction-complete FIFO area + * EP[16] : endpoint descriptors (16 x 16 bytes) + * FRAMENUM : frame number (2 bytes) + * + * Access endpoints as: g_ep_table.EP[n].OUT.STATUS, .CTRL, .CNT, etc. + * EPPTR must be set to &g_ep_table.EP[0] (NOT the struct base). + * ============================================================ */ +extern USB_EP_TABLE_t g_ep_table; + +extern uint8_t g_ep0_setup_buf[8]; +extern uint8_t g_ep0_data_buf[USB_EP0_SIZE]; +extern uint8_t g_ep1_in_buf[USB_EP1_SIZE]; /* CDC notify */ +extern uint8_t g_ep2_out_buf[USB_EP2_SIZE]; /* CDC data RX */ +extern uint8_t g_ep3_in_buf[USB_EP3_SIZE]; /* CDC data TX */ + +extern ctrl_state_t g_ctrl_state; +extern uint8_t g_pending_address; +extern volatile uint8_t g_current_configuration; +extern uint8_t g_remote_wakeup_enabled; + +/* Diagnostic counters */ + +/* diagnostic snapshot globals (captured at the end of the TRNCOMPL ISR) */ + +/* ============================================================ + * Internal helpers (shared between USB modules) + * ============================================================ */ +void ep0_start_data_in(const uint8_t *data, uint16_t len, uint16_t host_requested); +void ep0_start_data_out(uint8_t *buffer, uint16_t len); +void ep0_send_zlp(void); +void ep0_stall(void); + +/* RMWBUSY wait - call before any STATUS[n].xxxCLR/xxxSET write */ +static inline void usb_rmw_wait(void) { + while (USB0.INTFLAGSB & USB_RMWBUSY_bm) {} +} + +#ifdef __cplusplus +} +#endif + +#endif /* USB_CORE_H */ diff --git a/megaavr/cores/dxcore/usb_descriptors.c b/megaavr/cores/dxcore/usb_descriptors.c new file mode 100644 index 00000000..29d4fe08 --- /dev/null +++ b/megaavr/cores/dxcore/usb_descriptors.c @@ -0,0 +1,144 @@ +/* AVR DU native-USB stack. Compiled only on parts with the USB peripheral. */ +#include /* defines USB0 on parts that have USB; must precede the guard */ +#if defined(USB0) +/** + * usb_descriptors.c + * USB descriptor data: CDC-ACM + HID Composite (Keyboard + Mouse + Gamepad) + * + * Reference: USB 2.0, USB CDC 1.20, HID 1.11, USB IAD ECN + */ +#include "usb_descriptors.h" + +/* ============================================================ + * Device Descriptor + * bDeviceClass = 0xEF (Miscellaneous) + * bDeviceSubClass = 0x02 (Common Class) + * bDeviceProtocol = 0x01 (Interface Association Descriptor) + * Required for Windows to recognize multiple class interfaces + * tied together by IAD. + * ============================================================ */ +const uint8_t g_device_descriptor[18] PROGMEM = { + 18, /* bLength */ + DESC_TYPE_DEVICE, /* bDescriptorType */ + 0x00, 0x02, /* bcdUSB = 2.00 */ + 0xEF, 0x02, 0x01, /* class/subclass/protocol = IAD */ + USB_EP0_SIZE, /* bMaxPacketSize0 */ + (uint8_t)(USB_VID & 0xFF), + (uint8_t)((USB_VID >> 8) & 0xFF), + (uint8_t)(USB_PID & 0xFF), + (uint8_t)((USB_PID >> 8) & 0xFF), + (uint8_t)(USB_DEVICE_VER & 0xFF), + (uint8_t)((USB_DEVICE_VER >> 8) & 0xFF), + 0x01, /* iManufacturer */ + 0x02, /* iProduct */ + 0x03, /* iSerialNumber */ + 0x01 /* bNumConfigurations */ +}; + +/* ============================================================ + * Configuration Descriptor (150 bytes) + * ============================================================ */ +const uint8_t g_config_descriptor[CONFIG_TOTAL_LEN] PROGMEM = { + /* === Configuration Descriptor (9) === */ + 9, DESC_TYPE_CONFIG, + (uint8_t)(CONFIG_TOTAL_LEN & 0xFF), (uint8_t)(CONFIG_TOTAL_LEN >> 8), + USB_NUM_INTERFACES, /* 2 interfaces (CDC Comm + CDC Data) */ + 1, /* bConfigurationValue */ + 0, /* iConfiguration */ + 0xA0, /* bmAttributes: bus-powered, remote-wakeup */ + 50, /* bMaxPower = 100mA */ + + /* === Interface Association Descriptor (IAD) for CDC (8) === + * Tells the host: IF0 and IF1 form one CDC ACM function. */ + 8, DESC_TYPE_IAD, + CDC_COMM_INTERFACE, /* bFirstInterface */ + 2, /* bInterfaceCount (CDC=2 IFs) */ + 0x02, /* bFunctionClass = CDC */ + 0x02, /* bFunctionSubClass = ACM */ + 0x01, /* bFunctionProtocol = AT commands */ + 0, /* iFunction */ + + /* ====================================================== + * Interface 0: CDC Communication + * ====================================================== */ + 9, DESC_TYPE_INTERFACE, + CDC_COMM_INTERFACE, 0, /* bInterfaceNumber, bAlternateSetting */ + 1, /* bNumEndpoints = 1 (notify) */ + 0x02, 0x02, 0x01, /* CDC / ACM / AT commands */ + 0, /* iInterface */ + + /* CDC Header functional descriptor (5) */ + 5, DESC_TYPE_CS_INTERFACE, 0x00, + 0x10, 0x01, /* bcdCDC = 1.10 */ + + /* CDC Call Management functional descriptor (5) */ + 5, DESC_TYPE_CS_INTERFACE, 0x01, + 0x00, /* bmCapabilities: no call mgmt */ + CDC_DATA_INTERFACE, /* bDataInterface */ + + /* CDC ACM functional descriptor (4) */ + 4, DESC_TYPE_CS_INTERFACE, 0x02, + 0x02, /* bmCapabilities: line coding + serial state */ + + /* CDC Union functional descriptor (5) */ + 5, DESC_TYPE_CS_INTERFACE, 0x06, + CDC_COMM_INTERFACE, /* bMasterInterface */ + CDC_DATA_INTERFACE, /* bSlaveInterface0 */ + + /* EP1 IN (notify) - interrupt, 16B, 64ms poll */ + 7, DESC_TYPE_ENDPOINT, + 0x81, /* bEndpointAddress = 1 IN */ + 0x03, /* bmAttributes = Interrupt */ + USB_EP1_SIZE, 0, /* wMaxPacketSize = 16 */ + 64, /* bInterval = 64ms */ + + /* ====================================================== + * Interface 1: CDC Data + * ====================================================== */ + 9, DESC_TYPE_INTERFACE, + CDC_DATA_INTERFACE, 0, + 2, /* bNumEndpoints = 2 (RX + TX) */ + 0x0A, 0x00, 0x00, /* CDC Data class */ + 0, + + /* EP2 OUT (RX from host) - bulk, 64B */ + 7, DESC_TYPE_ENDPOINT, + 0x02, /* bEndpointAddress = 2 OUT */ + 0x02, /* bmAttributes = Bulk */ + USB_EP2_SIZE, 0, + 0, /* bInterval (ignored for bulk) */ + + /* EP3 IN (TX to host) - bulk, 64B */ + 7, DESC_TYPE_ENDPOINT, + 0x83, /* bEndpointAddress = 3 IN */ + 0x02, /* bmAttributes = Bulk */ + USB_EP3_SIZE, 0, + 0, + /* === Total: 9+8 + 9+5+5+4+5+7 + 9+7+7 = 75 bytes (matches CONFIG_TOTAL_LEN) === */ +}; + +/* ============================================================ + * String Descriptors (UTF-16LE) + * ============================================================ */ +const uint8_t g_string_langid[4] PROGMEM = { 4, DESC_TYPE_STRING, 0x09, 0x04 }; + +const uint8_t g_string_manufacturer[2 + 14 * 2] PROGMEM = { + 2 + 14 * 2, DESC_TYPE_STRING, + 'D', 0, 'x', 0, 'C', 0, 'o', 0, 'r', 0, 'e', 0, ' ', 0, '-', 0, + ' ', 0, 'A', 0, 'V', 0, 'R', 0, 'D', 0, 'U', 0 +}; +/* g_string_manufacturer_len replaced by a #define in usb_descriptors.h (sizeof at every call site, zero RAM). */ + +const uint8_t g_string_product[2 + 9 * 2] PROGMEM = { + 2 + 9 * 2, DESC_TYPE_STRING, + 'A', 0, 'V', 0, 'R', 0, 'D', 0, 'U', 0, ' ', 0, 'C', 0, 'D', 0, 'C', 0 +}; +/* g_string_product_len replaced by a #define in usb_descriptors.h (sizeof at every call site, zero RAM). */ + +const uint8_t g_string_serial[2 + 8 * 2] PROGMEM = { + 2 + 8 * 2, DESC_TYPE_STRING, + '0', 0, '0', 0, '0', 0, '0', 0, '0', 0, '0', 0, '0', 0, '1', 0 +}; +/* g_string_serial_len replaced by a #define in usb_descriptors.h (sizeof at every call site, zero RAM). */ + +#endif /* USB0 */ diff --git a/megaavr/cores/dxcore/usb_descriptors.h b/megaavr/cores/dxcore/usb_descriptors.h new file mode 100644 index 00000000..72f9b58b --- /dev/null +++ b/megaavr/cores/dxcore/usb_descriptors.h @@ -0,0 +1,139 @@ +/** + * usb_descriptors.h + * CDC-ACM + HID Composite (IAD) + * + * EP map: + * EP0 IN/OUT Control 64B + * EP1 IN CDC ACM notification (Interrupt 16B) + * EP2 OUT CDC data host->device (Bulk 64B) + * EP3 IN CDC data device->host (Bulk 64B) + * EP4 IN HID Keyboard (Interrupt 8B) + * EP5 IN HID Mouse (Interrupt 8B, 4B reports) + * EP6 IN HID Gamepad (Interrupt 8B, 7B reports) + * + * Interface map: + * IF 0 CDC Comm (IAD covers IF0..IF1) + * IF 1 CDC Data + * IF 2 HID Keyboard + * IF 3 HID Mouse + * IF 4 HID Gamepad + * + * VID/PID: pid.codes test PID (0x1209:0x0002) + */ +#ifndef USB_DESCRIPTORS_H +#define USB_DESCRIPTORS_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* ============================================================ + * Device identity + * ============================================================ */ +/* Identity (override via boards.txt build flags or variant pins_arduino.h) */ +#ifndef USB_VID + #define USB_VID 0x1209 +#endif +#ifndef USB_PID + #define USB_PID 0x0002 +#endif +#define USB_DEVICE_VER 0x0100 + +/* ============================================================ + * Endpoint configuration + * ============================================================ */ +#define USB_EP0_SIZE 64 /* Control */ +#define USB_EP1_SIZE 16 /* CDC notify (interrupt) */ +#define USB_EP2_SIZE 64 /* CDC RX (bulk) */ +#define USB_EP3_SIZE 64 /* CDC TX (bulk) */ +#define USB_EP4_SIZE 8 /* HID Keyboard */ +#define USB_EP5_SIZE 8 /* HID Mouse buffer */ +#define USB_EP6_SIZE 8 /* HID Gamepad buffer */ + +/* Logical report sizes (what we put in CNT, what the descriptor advertises) */ +#define HID_KB_REPORT_SIZE 8 +#define HID_MOUSE_REPORT_SIZE 4 +#define HID_GAMEPAD_REPORT_SIZE 7 + +#define USB_MAXEP 6 /* Highest endpoint number used */ +#define USB_NUM_EP (USB_MAXEP + 1) + +/* ============================================================ + * Interface numbers + * ============================================================ */ +#define CDC_COMM_INTERFACE 0 +#define CDC_DATA_INTERFACE 1 +#define HID_KEYBOARD_INTERFACE 2 +#define HID_MOUSE_INTERFACE 3 +#define HID_GAMEPAD_INTERFACE 4 +#define USB_NUM_INTERFACES 2 /* Phase 1: CDC only (HID gone, dynamic in Phase 2) */ + +/* ============================================================ + * Descriptor types + * ============================================================ */ +#define DESC_TYPE_DEVICE 0x01 +#define DESC_TYPE_CONFIG 0x02 +#define DESC_TYPE_STRING 0x03 +#define DESC_TYPE_INTERFACE 0x04 +#define DESC_TYPE_ENDPOINT 0x05 +#define DESC_TYPE_IAD 0x0B +#define DESC_TYPE_CS_INTERFACE 0x24 /* CDC class-specific Interface */ +#define DESC_TYPE_HID 0x21 +#define DESC_TYPE_HID_REPORT 0x22 + +/* ============================================================ + * HID report descriptor lengths + * ============================================================ */ + +/* ============================================================ + * Configuration descriptor layout + * 0 ( 9) Configuration + * 9 ( 8) Interface Association (CDC IF0+IF1) + * 17 ( 9) CDC Comm IF + * 26 ( 5) CDC Header functional desc + * 31 ( 5) CDC Call Management functional desc + * 36 ( 4) CDC ACM functional desc + * 40 ( 5) CDC Union functional desc + * 45 ( 7) EP1 IN (notify) + * 52 ( 9) CDC Data IF + * 61 ( 7) EP2 OUT (RX) + * 68 ( 7) EP3 IN (TX) + * 75 ( 9) HID Keyboard IF + * 84 ( 9) HID class <- KB HID at offset 84 + * 93 ( 7) EP4 IN + * 100 ( 9) HID Mouse IF + * 109 ( 9) HID class <- Mouse HID at offset 109 + * 118 ( 7) EP5 IN + * 125 ( 9) HID Gamepad IF + * 134 ( 9) HID class <- GP HID at offset 134 + * 143 ( 7) EP6 IN + * Total: 150 + * ============================================================ */ +#define CONFIG_TOTAL_LEN 75 /* Phase 1: CDC + IAD only */ + +#define HID_KB_HID_DESC_OFFSET 84 +#define HID_MOUSE_HID_DESC_OFFSET 109 +#define HID_GAMEPAD_HID_DESC_OFFSET 134 + +/* ============================================================ + * External descriptor data + * ============================================================ */ +extern const uint8_t g_device_descriptor[18] PROGMEM; +extern const uint8_t g_config_descriptor[CONFIG_TOTAL_LEN] PROGMEM; +extern const uint8_t g_string_langid[4] PROGMEM; +extern const uint8_t g_string_manufacturer[2 + 14 * 2] PROGMEM; +extern const uint8_t g_string_product[2 + 9 * 2] PROGMEM; +extern const uint8_t g_string_serial[2 + 8 * 2] PROGMEM; + +#define g_string_manufacturer_len ((uint8_t)sizeof(g_string_manufacturer)) +#define g_string_product_len ((uint8_t)sizeof(g_string_product)) +#define g_string_serial_len ((uint8_t)sizeof(g_string_serial)) + +#ifdef __cplusplus +} +#endif + +#endif /* USB_DESCRIPTORS_H */ diff --git a/megaavr/cores/dxcore/usb_ep_types.h b/megaavr/cores/dxcore/usb_ep_types.h new file mode 100644 index 00000000..4ead1bf4 --- /dev/null +++ b/megaavr/cores/dxcore/usb_ep_types.h @@ -0,0 +1,147 @@ +/** + * usb_ep_types.h - PluggableUSB endpoint type & transfer-flag macros + * + * The standard Arduino HID / Keyboard / Mouse libraries (and mheironimus + * DynamicHID) reference these constants. They are AVR-32U4-specific in the + * upstream Arduino core, where they encode bits of the UECFG0X register. + * + * On the AVR DU the USB peripheral is completely different (a per-endpoint + * descriptor table in SRAM rather than per-EP I/O registers), so we redefine + * the symbolic values to a compact encoding that USBCore_DU.cpp decodes when + * it programs the EP descriptor table at SET_CONFIGURATION: + * + * bit 7 = direction (1 = IN, 0 = OUT) + * bits [1:0] = transfer type (0 ctrl, 1 iso, 2 bulk, 3 interrupt) + * + * The actual numeric values don't matter to the libraries - they treat them + * as opaque - they only have to come back unchanged through PluggableUSB's + * `endpointType[]` array. + */ +#pragma once +#if defined(USB0) + +/* Endpoint type encoding (see file header) */ +#define EP_TYPE_CONTROL 0x00 +#define EP_TYPE_BULK_IN 0x82 +#define EP_TYPE_BULK_OUT 0x02 +#define EP_TYPE_INTERRUPT_IN 0x83 +#define EP_TYPE_INTERRUPT_OUT 0x03 +#define EP_TYPE_ISOCHRONOUS_IN 0x81 +#define EP_TYPE_ISOCHRONOUS_OUT 0x01 + +/* Default endpoint max-packet size used by PluggableUSB modules. + * HID reports are small (<=8 B) so 64 is comfortable. */ +#ifndef USB_EP_SIZE +#define USB_EP_SIZE 64 +#endif + +/* Transfer-control flags for USB_SendControl / USB_Send (must match Arduino) */ +#define TRANSFER_PGM 0x80 +#define TRANSFER_RELEASE 0x40 +#define TRANSFER_ZERO 0x20 + +#endif /* USB0 */ + +/* ============================================================ + * Minimal Arduino-AVR USBCore.h shims for the bundled HID library. + * + * The standard HID / Keyboard / Mouse libraries (and friends like + * mheironimus DynamicHID, Adafruit's NeoPixel-HID demos, etc.) rely on + * symbols that live in the AVR core's cores/arduino/USBCore.h. DxCore's + * vendored api/ * does not provide them. We supply the strict subset that + * HID.cpp/.h actually reference, in the same shape so no library patch + * is required. + * ============================================================ */ + +#ifndef USBCORE_COMPAT_DEFINED +#define USBCORE_COMPAT_DEFINED +#include + +#ifndef _AVR_TYPEDEFS_U8_U16 +#define _AVR_TYPEDEFS_U8_U16 +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +#endif + +/* Class codes referenced by HID library */ +#define USB_DEVICE_CLASS_COMMUNICATIONS 0x02 +#define USB_DEVICE_CLASS_HUMAN_INTERFACE 0x03 + +/* Endpoint helpers used by D_ENDPOINT() arguments */ +#ifndef lowByte + #define lowByte(w) ((uint8_t)((w) & 0xff)) +#endif +#ifndef highByte + #define highByte(w) ((uint8_t)((w) >> 8)) +#endif +#define USB_ENDPOINT_DIRECTION_MASK 0x80 +#define USB_ENDPOINT_OUT(addr) (lowByte((addr) | 0x00)) +#define USB_ENDPOINT_IN(addr) (lowByte((addr) | 0x80)) +#define USB_ENDPOINT_TYPE_MASK 0x03 +#define USB_ENDPOINT_TYPE_CONTROL 0x00 +#define USB_ENDPOINT_TYPE_ISOCHRONOUS 0x01 +#define USB_ENDPOINT_TYPE_BULK 0x02 +#define USB_ENDPOINT_TYPE_INTERRUPT 0x03 + +/* Standard request types used by HID class request handling */ +#define REQUEST_HOSTTODEVICE 0x00 +#define REQUEST_DEVICETOHOST 0x80 +#define REQUEST_DIRECTION 0x80 +#define REQUEST_STANDARD 0x00 +#define REQUEST_CLASS 0x20 +#define REQUEST_VENDOR 0x40 +#define REQUEST_TYPE 0x60 +#define REQUEST_DEVICE 0x00 +#define REQUEST_INTERFACE 0x01 +#define REQUEST_ENDPOINT 0x02 +#define REQUEST_OTHER 0x03 +#define REQUEST_RECIPIENT 0x03 +#define REQUEST_DEVICETOHOST_CLASS_INTERFACE (REQUEST_DEVICETOHOST | REQUEST_CLASS | REQUEST_INTERFACE) +#define REQUEST_HOSTTODEVICE_CLASS_INTERFACE (REQUEST_HOSTTODEVICE | REQUEST_CLASS | REQUEST_INTERFACE) +#define REQUEST_DEVICETOHOST_STANDARD_INTERFACE (REQUEST_DEVICETOHOST | REQUEST_STANDARD | REQUEST_INTERFACE) + +/* Descriptor structs used by D_INTERFACE / D_ENDPOINT */ +typedef struct { + u8 len; /* 9 */ + u8 dtype; /* 4 */ + u8 number; + u8 alternate; + u8 numEndpoints; + u8 interfaceClass; + u8 interfaceSubClass; + u8 protocol; + u8 iInterface; +} InterfaceDescriptor; + +typedef struct { + u8 len; /* 7 */ + u8 dtype; /* 5 */ + u8 addr; + u8 attr; + u16 packetSize; + u8 interval; +} EndpointDescriptor; + +typedef struct { + u8 len; /* 8 */ + u8 dtype; /* 11 */ + u8 firstInterface; + u8 interfaceCount; + u8 functionClass; + u8 functionSubClass; + u8 functionProtocol; + u8 iInterface; +} IADDescriptor; + +/* Descriptor initializer macros */ +#define D_INTERFACE(_n,_numEndpoints,_class,_subClass,_protocol) \ + { 9, 4, _n, 0, _numEndpoints, _class, _subClass, _protocol, 0 } + +#define D_ENDPOINT(_addr,_attr,_packetSize,_interval) \ + { 7, 5, _addr, _attr, _packetSize, _interval } + +#define D_IAD(_firstInterface, _count, _class, _subClass, _protocol) \ + { 8, 11, _firstInterface, _count, _class, _subClass, _protocol, 0 } + +#endif /* USBCORE_COMPAT_DEFINED */ diff --git a/megaavr/cores/dxcore/usb_standard.c b/megaavr/cores/dxcore/usb_standard.c new file mode 100644 index 00000000..008ab9a1 --- /dev/null +++ b/megaavr/cores/dxcore/usb_standard.c @@ -0,0 +1,332 @@ +/* AVR DU native-USB stack. Compiled only on parts with the USB peripheral. */ +#include /* defines USB0 on parts that have USB; must precede the guard */ +#if defined(USB0) +/** + * usb_standard.c + * USB Standard Request handlers + Class request dispatcher + * + * Class dispatch is by interface: + * IF 0 CDC Comm -> usb_cdc_handle_class_request + * IF 2..N (HID etc.) -> usbcore_try_plugged_setup (PluggableUSB bridge) + */ +#include +#include +#include "usb_core.h" +#include "usb_standard.h" +#include "usb_cdc.h" +#include "USBCore_DU.h" /* Phase 2: PluggableUSB bridge helpers */ + +/* ============================================================ + * Standard request codes (USB 2.0 Table 9-4) + * ============================================================ */ +#define REQ_GET_STATUS 0x00 +#define REQ_CLEAR_FEATURE 0x01 +#define REQ_SET_FEATURE 0x03 +#define REQ_SET_ADDRESS 0x05 +#define REQ_GET_DESCRIPTOR 0x06 +#define REQ_SET_DESCRIPTOR 0x07 +#define REQ_GET_CONFIGURATION 0x08 +#define REQ_SET_CONFIGURATION 0x09 +#define REQ_GET_INTERFACE 0x0A +#define REQ_SET_INTERFACE 0x0B + +#define REQ_RCPT_DEVICE 0x00 +#define REQ_RCPT_INTERFACE 0x01 +#define REQ_RCPT_ENDPOINT 0x02 +#define REQ_RCPT_MASK 0x1F + +#define FEATURE_ENDPOINT_HALT 0x00 +#define FEATURE_DEVICE_REMOTE_WAKEUP 0x01 + +/* Static buffers for short responses */ +static uint8_t s_status_buf[2]; +static uint8_t s_single_byte_buf; + +/* ============================================================ + * GET_DESCRIPTOR + * ============================================================ */ + +static void handle_get_descriptor(const usb_setup_t *s) { + + uint8_t type = (s->wValue >> 8) & 0xFF; + uint8_t index = s->wValue & 0xFF; + const uint8_t *p = NULL; + uint16_t len = 0; + + switch (type) { + case DESC_TYPE_DEVICE: + /* Stage from PROGMEM into s_acc; ep0_start_data_in() reads from RAM. */ + usbcore_acc_reset(); + usbcore_acc_load_P(g_device_descriptor, sizeof(g_device_descriptor)); + p = usbcore_acc_buf(); + len = usbcore_acc_len(); + break; + + case DESC_TYPE_CONFIG: + /* Phase 2: build descriptor on the fly so PluggableUSB modules + * (HID Keyboard, Mouse, etc., when present) can append their + * interfaces/endpoints after the CDC IFs. With no modules + * registered the result is byte-identical to the Phase 1 static + * descriptor (75 B CDC-only). */ + usbcore_build_config_descriptor(); + p = usbcore_acc_buf(); + len = usbcore_acc_len(); + break; + + case DESC_TYPE_STRING: { + /* Stage the requested string descriptor from PROGMEM into s_acc. + * Each USB string descriptor is small (<=30 B) so this trivially fits. */ + const uint8_t *src = NULL; + uint16_t sz = 0; + switch (index) { + case 0: src = g_string_langid; sz = sizeof(g_string_langid); break; + case 1: src = g_string_manufacturer; sz = g_string_manufacturer_len; break; + case 2: src = g_string_product; sz = g_string_product_len; break; + case 3: src = g_string_serial; sz = g_string_serial_len; break; + default: ep0_stall(); return; + } + usbcore_acc_reset(); + usbcore_acc_load_P(src, sz); + p = usbcore_acc_buf(); + len = usbcore_acc_len(); + break; + } + + default: + /* Phase 2: legacy DESC_TYPE_HID / DESC_TYPE_HID_REPORT branches + * (which sliced into the static g_config_descriptor) have been + * removed. HID modules now own those descriptors via PluggableUSB. + * + * Ask the registered PluggableUSB modules (HID report descriptors + * come in as wValue[H]=0x22 here). */ + usbcore_acc_reset(); + if (usbcore_try_plugged_get_descriptor(s)) { + p = usbcore_acc_buf(); + len = usbcore_acc_len(); + } else { + ep0_stall(); + return; + } + break; + } + + ep0_start_data_in(p, len, s->wLength); +} + +/* ============================================================ + * GET_STATUS + * ============================================================ */ +static void handle_get_status(const usb_setup_t *s) { + if (!(s->bmRequestType & 0x80)) { ep0_stall(); return; } + s_status_buf[1] = 0; + + switch (s->bmRequestType & REQ_RCPT_MASK) { + case REQ_RCPT_DEVICE: + s_status_buf[0] = (g_remote_wakeup_enabled << 1); + break; + case REQ_RCPT_INTERFACE: + s_status_buf[0] = 0; + break; + case REQ_RCPT_ENDPOINT: { + uint8_t ep_num = s->wIndex & 0x0F; + if (ep_num > USB_MAXEP) { ep0_stall(); return; } + if (s->wIndex & 0x80) + s_status_buf[0] = (g_ep_table.EP[ep_num].IN.CTRL & USB_DOSTALL_bm) ? 1 : 0; + else + s_status_buf[0] = (g_ep_table.EP[ep_num].OUT.CTRL & USB_DOSTALL_bm) ? 1 : 0; + break; + } + default: + ep0_stall(); + return; + } + ep0_start_data_in(s_status_buf, 2, s->wLength); +} + +/* ============================================================ + * CLEAR_FEATURE / SET_FEATURE + * ============================================================ */ +static void handle_set_or_clear_feature(const usb_setup_t *s, uint8_t set) { + switch (s->bmRequestType & REQ_RCPT_MASK) { + case REQ_RCPT_DEVICE: + if (s->wValue == FEATURE_DEVICE_REMOTE_WAKEUP) { + g_remote_wakeup_enabled = set; + ep0_send_zlp(); + } else { ep0_stall(); } + break; + case REQ_RCPT_ENDPOINT: { + if (s->wValue != FEATURE_ENDPOINT_HALT) { ep0_stall(); return; } + uint8_t ep_num = s->wIndex & 0x0F; + if (ep_num > USB_MAXEP) { ep0_stall(); return; } + if (s->wIndex & 0x80) { + if (set) g_ep_table.EP[ep_num].IN.CTRL |= USB_DOSTALL_bm; + else g_ep_table.EP[ep_num].IN.CTRL &= ~USB_DOSTALL_bm; + } else { + if (set) g_ep_table.EP[ep_num].OUT.CTRL |= USB_DOSTALL_bm; + else g_ep_table.EP[ep_num].OUT.CTRL &= ~USB_DOSTALL_bm; + } + ep0_send_zlp(); + break; + } + default: + ep0_stall(); + } +} + +/* ============================================================ + * SET_ADDRESS — address is applied AFTER status-stage IN ZLP completes + * ============================================================ */ +static void handle_set_address(const usb_setup_t *s) { + g_pending_address = s->wValue & 0x7F; + g_ep_table.EP[0].IN.CNT = 0; + g_ep_table.EP[0].IN.MCNT = 0; + g_ep_table.EP[0].IN.CTRL = USB_TYPE_CONTROL_gc | USB_BUFSIZE_DEFAULT_BUF64_gc; + + while (USB0.INTFLAGSB & USB_RMWBUSY_bm) {} + USB0.STATUS[0].INCLR = USB_BUSNAK_bm; + + g_ctrl_state = CTRL_STATUS_PENDING_ADDR; +} + +/* ============================================================ + * GET_CONFIGURATION / SET_CONFIGURATION + * ============================================================ */ +static void handle_get_configuration(const usb_setup_t *s) { + s_single_byte_buf = g_current_configuration; + ep0_start_data_in(&s_single_byte_buf, 1, s->wLength); +} + +static void handle_set_configuration(const usb_setup_t *s) { + uint8_t cfg = s->wValue & 0xFF; + + if (cfg == 0) { + g_ep_table.EP[1].IN.CTRL = USB_TYPE_DISABLE_gc; + g_ep_table.EP[2].OUT.CTRL = USB_TYPE_DISABLE_gc; + g_ep_table.EP[3].IN.CTRL = USB_TYPE_DISABLE_gc; + g_ep_table.EP[4].IN.CTRL = USB_TYPE_DISABLE_gc; + g_ep_table.EP[5].IN.CTRL = USB_TYPE_DISABLE_gc; + g_ep_table.EP[6].IN.CTRL = USB_TYPE_DISABLE_gc; + g_current_configuration = 0; + ep0_send_zlp(); + } else if (cfg == 1) { + /* CDC: EP1 IN (notify), EP2 OUT (RX), EP3 IN (TX) */ + g_ep_table.EP[1].IN.CTRL = USB_TYPE_BULKINT_gc | USB_BUFSIZE_DEFAULT_BUF16_gc; + g_ep_table.EP[1].IN.DATAPTR = (uint16_t)g_ep1_in_buf; + g_ep_table.EP[1].IN.STATUS = USB_BUSNAK_bm; + + g_ep_table.EP[2].OUT.CTRL = USB_TYPE_BULKINT_gc | USB_BUFSIZE_DEFAULT_BUF64_gc; + g_ep_table.EP[2].OUT.DATAPTR = (uint16_t)g_ep2_out_buf; + g_ep_table.EP[2].OUT.STATUS = 0x00; /* armed for RX */ + + g_ep_table.EP[3].IN.CTRL = USB_TYPE_BULKINT_gc | USB_BUFSIZE_DEFAULT_BUF64_gc; + g_ep_table.EP[3].IN.DATAPTR = (uint16_t)g_ep3_in_buf; + g_ep_table.EP[3].IN.STATUS = USB_BUSNAK_bm; + + /* Program any PluggableUSB-allocated dynamic EPs (HID etc.). */ + usbcore_init_plugged_endpoints(); + g_current_configuration = 1; + usb_cdc_on_configured(); + ep0_send_zlp(); + } else { + ep0_stall(); + } +} + +/* ============================================================ + * GET_INTERFACE / SET_INTERFACE + * ============================================================ */ +static void handle_get_interface(const usb_setup_t *s) { + uint8_t iface = s->wIndex & 0xFF; + if (iface >= USB_NUM_INTERFACES) { ep0_stall(); return; } + s_single_byte_buf = 0; + ep0_start_data_in(&s_single_byte_buf, 1, s->wLength); +} + +static void handle_set_interface(const usb_setup_t *s) { + uint8_t iface = s->wIndex & 0xFF; + if (iface < USB_NUM_INTERFACES && (s->wValue & 0xFF) == 0) { + ep0_send_zlp(); + } else { + ep0_stall(); + } +} + +/* ============================================================ + * Dispatchers + * ============================================================ */ +void usb_handle_standard_request(const usb_setup_t *s) { + switch (s->bRequest) { + case REQ_GET_STATUS: handle_get_status(s); break; + case REQ_CLEAR_FEATURE: handle_set_or_clear_feature(s, 0); break; + case REQ_SET_FEATURE: handle_set_or_clear_feature(s, 1); break; + case REQ_SET_ADDRESS: handle_set_address(s); break; + case REQ_GET_DESCRIPTOR: handle_get_descriptor(s); break; + case REQ_GET_CONFIGURATION: handle_get_configuration(s); break; + case REQ_SET_CONFIGURATION: handle_set_configuration(s); break; + case REQ_GET_INTERFACE: handle_get_interface(s); break; + case REQ_SET_INTERFACE: handle_set_interface(s); break; + default: ep0_stall(); break; + } +} + +void usb_handle_class_request(const usb_setup_t *s) { + uint8_t iface = s->wIndex & 0xFF; + if (iface == CDC_COMM_INTERFACE || iface == CDC_DATA_INTERFACE) { + usb_cdc_handle_class_request(s); + return; + } + /* Phase 2: defer all other interfaces to whichever PluggableUSB module + * claimed them at static init time (HID via the bundled HID library, + * XInput in a future build mode, etc.). + * + * host->device request WITH a data stage (HID SET_REPORT carrying the + * keyboard-LED byte, feature reports, ...): the payload arrives in an + * EP0 OUT data stage. Receive it first, then re-dispatch to the module + * from usb_class_data_out_complete() so its setup() can read it via + * USB_RecvControl(). This mirrors the CDC SET_LINE_CODING flow. */ + if (!(s->bmRequestType & 0x80) && s->wLength > 0) { + uint16_t n = usbcore_ctrl_out_begin(s); + if (n == 0) { ep0_stall(); return; } + ep0_start_data_out(usbcore_ctrl_out_buf(), n); + return; /* continued in usb_class_data_out_complete() */ + } + + /* No data stage, or device->host: dispatch immediately. The module + * either calls USB_SendControl to push response bytes into the + * accumulator (which we then ship as a control-IN), or just consumes + * the SETUP and we ZLP. */ + if (usbcore_try_plugged_setup(s)) { + uint16_t n = usbcore_acc_len(); + if (n > 0) { + ep0_start_data_in(usbcore_acc_buf(), n, s->wLength); + usbcore_acc_reset(); + } else { + ep0_send_zlp(); + } + } else { + ep0_stall(); + } +} + +/* ============================================================ + * EP0 data-out completion -> dispatch to whichever class needs it + * + * Two consumers share the EP0 OUT data stage (mutually exclusive, one + * control transfer at a time): + * - plugged PluggableUSB modules (HID SET_REPORT / LED, feature reports) + * - CDC SET_LINE_CODING (7-byte line coding) + * ============================================================ */ +extern void usb_cdc_data_out_complete(void); /* in usb_cdc.c */ + +void usb_class_data_out_complete(void) { + /* A plugged (HID) host->device data stage takes priority: deliver the + * staged bytes to the owning module, which reads them via USB_RecvControl. */ + if (usbcore_ctrl_out_pending()) { + usbcore_ctrl_out_dispatch(); + return; /* status-stage ZLP is sent by handle_ep0_out_complete() */ + } + /* Otherwise it is the CDC SET_LINE_CODING payload. */ + usb_cdc_data_out_complete(); +} + +#endif /* USB0 */ diff --git a/megaavr/cores/dxcore/usb_standard.h b/megaavr/cores/dxcore/usb_standard.h new file mode 100644 index 00000000..fbc31a4e --- /dev/null +++ b/megaavr/cores/dxcore/usb_standard.h @@ -0,0 +1,25 @@ +/** + * usb_standard.h + * USB Standard / Class request dispatcher declarations + */ +#ifndef USB_STANDARD_H +#define USB_STANDARD_H + +#include "usb_core.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Dispatcher entry points - called from SETUP handler */ +void usb_handle_standard_request(const usb_setup_t *s); +void usb_handle_class_request(const usb_setup_t *s); + +/* Called when EP0 DATA-OUT stage completes */ +void usb_class_data_out_complete(void); + +#ifdef __cplusplus +} +#endif + +#endif /* USB_STANDARD_H */ diff --git a/megaavr/cores/dxcore/wiring.c b/megaavr/cores/dxcore/wiring.c index 92afe16b..9e393f0d 100644 --- a/megaavr/cores/dxcore/wiring.c +++ b/megaavr/cores/dxcore/wiring.c @@ -1559,6 +1559,13 @@ void init() { // but due to a silicon bug, the input buffer is on, but it's input is floating. Per errata, we are supposed to turn it off. PORTD.PIN0CTRL = PORT_ISC_INPUT_DISABLE_gc; #endif + #if defined(USB_VREG_INTERNAL) + /* AVR DU: enable the internal USB voltage regulator (SYSCFG.VUSBCTRL). + * It resets to disabled, and anything using the USB peripheral needs + * VUSB powered. Controlled by the "VUSB Power Source" menu; boards + * feeding external 3.3 V into VUSB leave this macro undefined. */ + SYSCFG.VUSBCTRL = SYSCFG_USBVREG_bm; + #endif init_clock(); init_timers(); #if defined(ADC0) diff --git a/megaavr/cores/dxcore/wiring_analog.c b/megaavr/cores/dxcore/wiring_analog.c index ef69aee1..107be3a4 100644 --- a/megaavr/cores/dxcore/wiring_analog.c +++ b/megaavr/cores/dxcore/wiring_analog.c @@ -418,7 +418,7 @@ inline __attribute__((always_inline)) void check_valid_resolution(uint8_t res) { #endif return ADC_ERROR_BAD_PIN_OR_CHANNEL; } - if (!ADC0.CTRLA & 0x01) return ADC_ERROR_DISABLED; + if (!(ADC0.CTRLA & 0x01)) return ADC_ERROR_DISABLED; if (ADC0.COMMAND & ADC_START_gm) return ADC_ERROR_BUSY; // gotta be careful here - don't want to shit ongoing conversion - unlikle classic AVRs @@ -431,10 +431,10 @@ inline __attribute__((always_inline)) void check_valid_resolution(uint8_t res) { /* Wait for result ready */ while (!(ADC0.INTFLAGS & ADC_RESRDY_bm)); - // if it's 10 bit compatibility mode, have to rightshift twice. + // native resolution is ADC_NATIVE_RESOLUTION bits; shift down to the requested 10-bit if needed. if ((_analog_options & 0x0F) == 10) { int16_t temp = ADC0.RESULT; - temp >>= 2; + temp >>= (ADC_NATIVE_RESOLUTION - 10); return temp; } return ADC0.RESULT; @@ -721,16 +721,20 @@ inline __attribute__((always_inline)) void check_valid_resolution(uint8_t res) { // If high bit set, it's a channel, otherwise it's a digital pin so we look it up.. pin = digitalPinToAnalogInput(pin); } else { - pin &= 0x3F; + /* The DU has a 7-bit MUXPOS with its internal channels at 0x40 (GND), + * 0x42 (TEMPSENSE) and 0x44 (VDDDIV10) - DS40002548B 32.4.12 - not at + * 0x30-0x33 like the parts around it. Masking with 0x3F turned + * ADC_GROUND / ADC_TEMPERATURE / ADC_VDDDIV10 into AIN0 / AIN2 / AIN4: + * it silently read three ordinary I/O pins and returned whatever they + * floated at, with no error. */ + pin &= 0x7F; } - #if PROGMEM_SIZE < 8096 - if (pin > 0x33) { // covers most ways a bad channel could come about - #else - if (pin > NUM_ANALOG_INPUTS && ((pin < 0x30) || (pin > 0x33))) { - #endif + /* Valid DU internal channels are exactly GND, TEMPSENSE and VDDDIV10; + * the reserved values in between must be rejected, not handed to the mux. */ + if (pin > NUM_ANALOG_INPUTS && pin != 0x40 && pin != 0x42 && pin != 0x44) { return ADC_ERROR_BAD_PIN_OR_CHANNEL; } - if (!ADC0.CTRLA & 0x01) return ADC_ERROR_DISABLED; + if (!(ADC0.CTRLA & 0x01)) return ADC_ERROR_DISABLED; if (ADC0.COMMAND & ADC_START_gm) return ADC_ERROR_BUSY; // gotta be careful here - don't want to shit ongoing conversion - unlikle classic AVRs @@ -743,10 +747,10 @@ inline __attribute__((always_inline)) void check_valid_resolution(uint8_t res) { /* Wait for result ready */ while (!(ADC0.INTFLAGS & ADC_RESRDY_bm)); - // if it's 10 bit compatibility mode, have to rightshift twice. + // native resolution is ADC_NATIVE_RESOLUTION bits; shift down to the requested 10-bit if needed. if ((_analog_options & 0x0F) == 10) { int16_t temp = ADC0.RESULT; - temp >>= 2; + temp >>= (ADC_NATIVE_RESOLUTION - 10); return temp; } return ADC0.RESULT; @@ -836,17 +840,13 @@ inline __attribute__((always_inline)) void check_valid_resolution(uint8_t res) { // If high bit set, it's a channel, otherwise it's a digital pin so we look it up.. pin = digitalPinToAnalogInput(pin); } else { - pin &= 0x3F; + pin &= 0x7F; /* see analogRead() above - DU internal channels live at 0x40/0x42/0x44 */ } - #if PROGMEM_SIZE < 8096 - if (pin > 0x33) // covers most ways a bad channel could come about - #else - if (pin > NUM_ANALOG_INPUTS && ((pin < 0x30) || (pin > 0x33))) - #endif + if (pin > NUM_ANALOG_INPUTS && pin != 0x40 && pin != 0x42 && pin != 0x44) { return ADC_ENH_ERROR_BAD_PIN_OR_CHANNEL; } - pin &= 0x3F; + pin &= 0x7F; if (ADC0.COMMAND & ADC_START_gm) return ADC_ENH_ERROR_BUSY; @@ -920,9 +920,16 @@ inline __attribute__((always_inline)) void check_valid_resolution(uint8_t res) { } uint8_t prescale = 0; for (uint8_t i = 0; i < 16; i++) { - int16_t clkadc = pgm_read_byte_near(&adc_prescale_to_clkadc[i]); + /* The table is int16_t in PROGMEM. It was read with pgm_read_byte_near() + * (low byte only) here, and with plain array indexing below and in the + * return - which dereferences the code-space address in the data space. + * The result was a garbage clock speed (e.g. -9227 kHz on a 24 MHz + * part where the answer is 2000) and a wrong prescaler whenever a + * frequency was requested. The EA/EB branch of this same function + * already reads the table correctly. */ + int16_t clkadc = pgm_read_word_near(&adc_prescale_to_clkadc[i]); prescale = i; - if ((frequency >= clkadc) || (adc_prescale_to_clkadc[i + 1] < ((options & 0x01) ? 2 : 300))) { + if ((frequency >= clkadc) || ((int16_t)pgm_read_word_near(&adc_prescale_to_clkadc[i + 1]) < ((options & 0x01) ? 2 : 300))) { ADC0.CTRLB = prescale; break; } @@ -931,7 +938,7 @@ inline __attribute__((always_inline)) void check_valid_resolution(uint8_t res) { if (frequency < 0) { return ADC_ERROR_INVALID_CLOCK; } - return adc_prescale_to_clkadc[ADC0.CTRLB]; + return pgm_read_word_near(&adc_prescale_to_clkadc[ADC0.CTRLB & 0x0F]); } diff --git a/megaavr/cores/dxcore/wiring_extra.cpp b/megaavr/cores/dxcore/wiring_extra.cpp index 6a4e99bc..2f9fce21 100644 --- a/megaavr/cores/dxcore/wiring_extra.cpp +++ b/megaavr/cores/dxcore/wiring_extra.cpp @@ -154,11 +154,21 @@ void pinConfigure(uint8_t digital_pin, uint16_t pin_config) { _pinconfigure(digital_pin, pin_config); } /* This may end up somewhere else (like in the library*/ -#if defined(PORTA_EVGENCTRL) //Ex-series only - this all may belong in the Event library anyway, but since the conditional is never met, this code is never used. +#if defined(PORTA_EVGENCTRL) // Parts with the version 3 event system (EA/EB/DU-series). This all may belong in the Event library anyway. + /* Note: On the DU-series, bits 3 and 7 of PORTx.EVGENCTRLA are read-only zero (the EVGENnSEL + * fields are 3 bits wide, per DS40002548A section 18.5.10), so they CANNOT be used as "in use" + * flags and the automatic channel selection (chan = 255) is not available here - pass an + * explicit generator number, or better, use the Event library (assign_generator_pin()), which + * tracks usage in software instead. */ uint8_t _setEventPin(uint8_t pin, uint8_t chan) { // Works the same was as uint8_t temp = digitalPinToPort(pin); - if (temp != NOT_A_PIN && (chan + 1) < 3) { + if (temp != NOT_A_PIN && (chan == 255 || chan < 2)) { // (chan + 1) < 3 promoted to int, so chan = 255 could never take this path + #if defined(__AVR_DU__) + if (chan == 255) { + return 255; // see note above - no free/in-use flags exist on the DU-series. + } + #endif volatile uint8_t* p; p = (volatile uint8_t*) (uint16_t) (digitalPinToPortStruct(temp)); p += 0x18; // now p pointing to evgenctrl. diff --git a/megaavr/extras/AboutDxSeries.md b/megaavr/extras/AboutDxSeries.md index dbd66fa6..705909d9 100644 --- a/megaavr/extras/AboutDxSeries.md +++ b/megaavr/extras/AboutDxSeries.md @@ -119,7 +119,7 @@ Notes: ## Big Picture These parts depart from the naming scheme used for AVR devices in the past; these are named AVR followed by the size of the flash, in KB, followed by DA, DB, or DD (depending on the "series" or "family", then the number of pins. Note that the pin count also determines how many of certain peripherals the parts have available - parts with more pins have more peripherals to do things with those pins. 64-pin parts are not available in 32k flash size. The 128k flash size is the highest that can be supported with a 16-bit program counter (above that, a number of instructions become slower, and everything gets more complicated), and with the current scheme for interacting with the pins, the 55 I/O pins (56 less the UPDI pin which takes the place of PF7) are the limit of what a modern AVR can accommodate while allowing single cycle access to all pins - so these take them to the top end of what is possible without extensions to the architecture. -At present, there are three lines of AVR Dx-series parts currently available: The "baseline" DA, the DB with multivoltage (MVIO) and on-chip OPAMPs, and the "budget" DD-series which has MVIO but otherwise far fewer peripherals, and is available in pincounts as low as 14, bringing them dangerouly close to the tinyAVR parts (dangerous for the tinyAVRs, that is). There is also a *confirmed* new DU-series on the way. The U is for USB - it'll have native USB, and DD-like pincounts. This is no doubt very exciting for many of us, who have longed for more sophisticated native USB parts than the m32u4 used in the Arduino Micro - though it looks to have had to make significant sacrifices at the altar of native USB, in terms of peripheral count and selection. This core supports the DA, DB, DD, and EA-series (excepting AVR8EA). Pending availability, support for the AVR8EA parts, as well as the EB-series and DU-series is planned. +At present, there are three lines of AVR Dx-series parts currently available: The "baseline" DA, the DB with multivoltage (MVIO) and on-chip OPAMPs, and the "budget" DD-series which has MVIO but otherwise far fewer peripherals, and is available in pincounts as low as 14, bringing them dangerouly close to the tinyAVR parts (dangerous for the tinyAVRs, that is). There is also the DU-series: the U is for USB - native USB with DD-like pincounts. This is no doubt very exciting for many of us, who have longed for more sophisticated native USB parts than the m32u4 used in the Arduino Micro - though it made significant sacrifices at the altar of native USB, in terms of peripheral count and selection. This core supports the DA, DB, DD, DU, and EA-series (excepting AVR8EA). Pending availability, support for the AVR8EA parts and the EB-series is planned. ### DA-series The "baseline" full-size parts - however much I was in awe of these when they were first released, having seen the DB-series, it now appears that these are more akin to a 0-series than a 1-series - in every arena, the DB has the same or better. They do not support using an external crystal for the main clock, like the other Dx parts do, but the internal oscillator on these parts is still WAY better than the classic AVRs had - all the ones I've tested are weithin half a percent at room temp and typical operating voltages, even without autotune... To make sure autotune was working, I had to point a torch at it, because I couldn't get enough of a change in the internal oscillator frequency from changing the supply voltage - in fact, it didn't seem to change at all. It is also the only currently announced Dx series without `MVIO`. While they may not shine as brightly next to the other Dx lines. The fact that these look less than stellar beside the DB doesn't change the fact that *they absolutely bury any AVR from before 2020*. There is only one thing that they have and the DB doesn't - a peripheral touch controller (which we can't use in Arduino land because they won't share the source code or the equivalent to the datasheet, but rather, they continue Atmel's practices here: They force you to use their IDE in order to make use of it). Word is that there is going to be an official Arduino library based on Q-touch from Microchip. It will then fall to the deep hackers to wrangle that into a smaller and more performant version, I know several already awaiting it. I have been told stories of the sorts of things that Q-touch does internally - but as there may be children reading this I will refrain from an enumeration of these things. diff --git a/megaavr/extras/CORE_PART_ID.md b/megaavr/extras/CORE_PART_ID.md index dae7bb94..7e91ad52 100644 --- a/megaavr/extras/CORE_PART_ID.md +++ b/megaavr/extras/CORE_PART_ID.md @@ -9,12 +9,12 @@ Do not get them confused with things you test with `#ifdef` or `#if defined()` t ## Flash size and Series The 5 high bits identify the flash size and series -| Flash | DA | DB | DD | -|---------|------|------|------| -| 128k | 0x20 | 0x28 | -- | -| 64k | 0x10 | 0x18 | 0x60 | -| 32k | 0x00 | 0x08 | 0x50 | -| 16k | -- | -- | 0x40 | +| Flash | DA | DB | DD | DU | +|---------|------|------|------|------| +| 128k | 0x20 | 0x28 | -- | -- | +| 64k | 0x10 | 0x18 | 0x60 | 0x68 | +| 32k | 0x00 | 0x08 | 0x50 | 0x58 | +| 16k | -- | -- | 0x40 | 0x48 | Where high bit is 1, it is a not a Dx part. The following are defined to help mastching these: @@ -22,6 +22,7 @@ The following are defined to help mastching these: * `ID_AVR_DA` * `ID_AVR_DB` * `ID_AVR_DD` +* `ID_AVR_DU` ## Pincount The low 3 bits identify pincount: diff --git a/megaavr/extras/DU14.md b/megaavr/extras/DU14.md index 45270248..d2055451 100644 --- a/megaavr/extras/DU14.md +++ b/megaavr/extras/DU14.md @@ -7,10 +7,7 @@ These are one of those parts that leave you very little ambiguity about whether * For the true wizards, the option to make an entirely custom USB device awaits you. -Currently, there is no support for the USB available as part of the core yet, that is the top priority new feature, but one which I feel totally unqualified to write. - -**Until there is is at least one library available with some sort of USB functionality, the only reasonable thing to do with this core and a DU microcontroller is to develop such a library.** -Once that has been done, it will open scores of new use cases. +USB support is now part of the core: USB CDC `Serial`, the standard Arduino Pluggable USB API (HID works out of the box; MIDI via MIDIUSB), and an optional USB CDC bootloader - select the "AVR DU-series (USB CDC Bootloader)" board to upload sketches over USB with no programmer needed. diff --git a/megaavr/extras/DU20.md b/megaavr/extras/DU20.md index a9822d1c..040f6b21 100644 --- a/megaavr/extras/DU20.md +++ b/megaavr/extras/DU20.md @@ -8,10 +8,7 @@ These are one of those parts that leave you very little ambiguity about whether * For the true wizards, the option to make an entirely custom USB device awaits you. -Currently, there is no support for the USB available as part of the core yet, that is the top priority new feature, but one which I feel totally unqualified to write. - -**Until there is is at least one library available with some sort of USB functionality, the only reasonable thing to do with this core and a DU microcontroller is to develop such a library.** -Once that has been done, it will open scores of new use cases. +USB support is now part of the core: USB CDC `Serial`, the standard Arduino Pluggable USB API (HID works out of the box; MIDI via MIDIUSB), and an optional USB CDC bootloader - select the "AVR DU-series (USB CDC Bootloader)" board to upload sketches over USB with no programmer needed. ## Pin Mapping / Pinout `[DU28 Pin Mapping](DU28.png "Arduino Pin Mapping for AVR DU28")` @@ -44,7 +41,7 @@ Once that has been done, it will open scores of new use cases. | USART (pin mappings) | 2: 4/1 | | SPI (pin mappings) | 1: 2 | | TWI/I2C (pin mappings) | 1: 2 | -| 12-bit ADC input pins | 11 | +| 10-bit ADC input pins | 11 | | Of those, neg. diff. inputs | all | | 10-bit DAC | None | | Analog Comparator (AC) | 1 | diff --git a/megaavr/extras/DU28.md b/megaavr/extras/DU28.md index 349a4eb4..68fae067 100644 --- a/megaavr/extras/DU28.md +++ b/megaavr/extras/DU28.md @@ -7,10 +7,7 @@ These are one of those parts that leave you very little ambiguity about whether * Program over USB pending availability of a bootloader suitable for such * For the true wizards, the option to make an entirely custom USB device awaits you. -Currently, there is no support for the USB available as part of the core yet, that is the top priority new feature, but one which I feel totally unqualified to write. - -**Until there is is at least one library available with some sort of USB functionality, the only reasonable thing to do with this core and a DU microcontroller is to develop such a library.** -Once that has been done, it will open scores of new use cases. +USB support is now part of the core: USB CDC `Serial`, the standard Arduino Pluggable USB API (HID works out of the box; MIDI via MIDIUSB), and an optional USB CDC bootloader - select the "AVR DU-series (USB CDC Bootloader)" board to upload sketches over USB with no programmer needed. ## Pin Mapping / Pinout @@ -43,7 +40,7 @@ Once that has been done, it will open scores of new use cases. | USART (pin mappings) | 2: 4/1 | | SPI (pin mappings) | 1: 5 | | TWI/I2C (pin mappings) | 1: 3 | -| 12-bit ADC input pins | 18/22 | +| 10-bit ADC input pins | 18/22 | | Of those, neg. diff. inputs | all | | 10-bit DAC | 1 | | Analog Comparator (AC) | 1 | diff --git a/megaavr/extras/DU32.md b/megaavr/extras/DU32.md index a639a847..cb4a13f4 100644 --- a/megaavr/extras/DU32.md +++ b/megaavr/extras/DU32.md @@ -8,10 +8,7 @@ These are one of those parts that leave you very little ambiguity about whether * For the true wizards, the option to make an entirely custom USB device awaits you. -Currently, there is no support for the USB available as part of the core yet, that is the top priority new feature, but one which I feel totally unqualified to write. - -**Until there is is at least one library available with some sort of USB functionality, the only reasonable thing to do with this core and a DU microcontroller is to develop such a library.** -Once that has been done, it will open scores of new use cases. +USB support is now part of the core: USB CDC `Serial`, the standard Arduino Pluggable USB API (HID works out of the box; MIDI via MIDIUSB), and an optional USB CDC bootloader - select the "AVR DU-series (USB CDC Bootloader)" board to upload sketches over USB with no programmer needed. ## Pin Mapping / Pinout `[DU28 Pin Mapping](DU28.png "Arduino Pin Mapping for AVR DU28")` @@ -42,7 +39,7 @@ Once that has been done, it will open scores of new use cases. | USART (pin mappings) | 2: 4/1 | | SPI (pin mappings) | 1: 5 | | TWI/I2C (pin mappings) | 1: 3 | -| 12-bit ADC input pins | 18/22 | +| 10-bit ADC input pins | 18/22 | | Of those, neg. diff. inputs | all | | 10-bit DAC | 1 | | Analog Comparator (AC) | 1 | diff --git a/megaavr/extras/Ref_Analog.md b/megaavr/extras/Ref_Analog.md index 436b835d..3cd47aa0 100644 --- a/megaavr/extras/Ref_Analog.md +++ b/megaavr/extras/Ref_Analog.md @@ -121,14 +121,15 @@ In addition to reading from pins, you can read from a number of internal sources * On the AVR LA series, they;re changing the name of the DACREF to AC0REFSCALER. I think this may be nomenclative only (makes sense, to avoid confusion about how many DACs are available) ### MUXNEG internal options -| AVR DA | AVR DB | AVR DD | AVR EA | AVR EB | AVR EC | -|------------------|-------------------|-------------------|------------------|------------------|------------------| -| `ADC_GROUND` | `ADC_GROUND` | `ADC_GROUND` | `ADC_GROUND` | `ADC_GROUND` | `ADC_GROUND` | -| `ADC_DAC0` | `ADC_DAC0` | `ADC_DAC0` | `ADC_DACREF0` | `ADC_DACREF0` | ??? | -| - | - | - | `ADC_DACREF1` | `ADC_DACREF1` | ??? | -| - | - | - | `ADC_DAC0` | `ADC_DAC0` ?! | ??? | - -Note that the AVR EB-series does not have a DAC. +| AVR DA | AVR DB | AVR DD | AVR DU | AVR EA | AVR EB | AVR EC | +|------------------|-------------------|-------------------|-------------------|------------------|------------------|------------------| +| `ADC_GROUND` | `ADC_GROUND` | `ADC_GROUND` | - | `ADC_GROUND` | `ADC_GROUND` | `ADC_GROUND` | +| `ADC_DAC0` | `ADC_DAC0` | `ADC_DAC0` | - | `ADC_DACREF0` | `ADC_DACREF0` | ??? | +| - | - | - | - | `ADC_DACREF1` | `ADC_DACREF1` | ??? | +| - | - | - | - | `ADC_DAC0` | `ADC_DAC0` ?! | ??? | + +Note that the AVR EB-series and the AVR DU-series do not have a DAC (the DU has only the DACREF inside its analog comparator). +The DU-series is absent from this table because it has no MUXNEG at all: its ADC is single-ended only, with no differential capability. | tinyAVR 0/1-series | tinyAVR 2-series | |----------------------------------------|-------------------------------------| diff --git a/megaavr/extras/Ref_Defines.md b/megaavr/extras/Ref_Defines.md index 3c65c5e3..696e5f29 100644 --- a/megaavr/extras/Ref_Defines.md +++ b/megaavr/extras/Ref_Defines.md @@ -129,6 +129,10 @@ This core provides an additional set of defines depending on the number of pins * `DD_20_PINS` (Deprecated - use `_AVR_FAMILY` and `_AVR_PINCOUNT`) * `DD_28_PINS` (Deprecated - use `_AVR_FAMILY` and `_AVR_PINCOUNT`) * `DD_32_PINS` (Deprecated - use `_AVR_FAMILY` and `_AVR_PINCOUNT`) +* `DU_14_PINS` (Deprecated - use `_AVR_FAMILY` and `_AVR_PINCOUNT`) +* `DU_20_PINS` (Deprecated - use `_AVR_FAMILY` and `_AVR_PINCOUNT`) +* `DU_28_PINS` (Deprecated - use `_AVR_FAMILY` and `_AVR_PINCOUNT`) +* `DU_32_PINS` (Deprecated - use `_AVR_FAMILY` and `_AVR_PINCOUNT`) * `DX_14_PINS` (Deprecated - use `_AVR_FAMILY` and `_AVR_PINCOUNT`) * `DX_20_PINS` (Deprecated - use `_AVR_FAMILY` and `_AVR_PINCOUNT`) * `DX_28_PINS` (Deprecated - use `_AVR_FAMILY` and `_AVR_PINCOUNT`) @@ -163,6 +167,7 @@ Most of these need no explanation - they usually just give you the number of ins * `_AVR_TCD_COUNT` * `_AVR_TWI_COUNT` * `_AVR_USART_COUNT` +* `USBCON` - Defined (by the variant) on boards whose USB peripheral is driven by the core's USB stack (the DU-series USB boards). Test this to know whether `Serial` is the native USB CDC port and the Pluggable USB API is available; it is not defined on the generic no-bootloader DU board. The EVSYS defines are very useful if you are using EVSYS without event, but even if you are using event, you want to, for example, select channels to minimize the functionality you're blocking off with your configuration, unless you're using an Ex-series, where all channels are created equal. diff --git a/megaavr/extras/Ref_Digital.md b/megaavr/extras/Ref_Digital.md index 269fa546..021f6ade 100644 --- a/megaavr/extras/Ref_Digital.md +++ b/megaavr/extras/Ref_Digital.md @@ -41,6 +41,27 @@ What tinyAVR calls current injection, Dx/Ex-series call "Clamp current" - it's c If you think the problem with blown pins on arduinos with classic AVRs is bad among hobby folks (based on the forums), it's apparently WAY worse in schools - I've heard of highschool instructors who grab an arduino from the shared bin to do a demo, intentionally choosing weird unpopular pins - *because so many of the shared boards have blown pins*... and apparently they don't replace them - or even the pluggable chip on most Unos. I think it's a public school thing - they get funding to set up something new, but not for maintaining existing things that's a much longer term and less . And though I think any of us could give a dead simple flow chart for determining if an uno is bad, and if so whether replacing the chip will fix it.) It remains to be seen what the pin drive strength will be like on the Ex-series or any other future part, and until the IO pin output current is added to the characteristics graphs section of the datasheet, you don't really have much information; preliminary datasheets typically omit this sort of data, because these properties have not yet been characterized. (I'm not sure how they manage that. Automating that testing, while not trivial, would be far from of reach for a few microcontroller experts. Now, I'm not a Microchip personenle department worker, nor am I an employee there at all (I am pretty sure some of the things I've said would get me fired or prevent me from getting hired there - but the things to which I refer are both accurate (if a bit dramatic) and criticisms which have to be heard), but from the outside, I'm pretty sure there are a considerable number of microcontroller experts employed by Microchip, so I'm unsure why this information takes so long +### The DU-series PC3 is special +On the DU-series, PC3 is the only surviving PORTC pin. The datasheet's +pinout legend groups it with the USB supply pins, which is easy to misread +as PC3 depending on VUSB - it does not. Its digital I/O is powered from +VDD and is entirely independent of VUSB and the USB voltage regulator: +confirmed on hardware, PC3 drives a full-VDD-swing output and reads +correctly with the regulator disabled and VUSB unpowered. Treat it as a +normal VDD-domain GPIO. + +### INLVL on the DU-series: works, but no longer documented +The DU io headers define the INLVL bit (PINnCTRL bit 6 - Schmitt-trigger +vs. TTL input levels, as on the MVIO parts), and the feature is functional +on actual silicon: feeding VDD/2 into a pin through a resistor divider, +the input reads 0 with the Schmitt-trigger threshold and 1 after +switching that pin to TTL levels (verified on an AVR64DU32). Datasheet +Rev. B (DS40002548B / DS40002576B, 06/2026), however, removed every INLVL +bit from the PORT chapter: Microchip no longer documents or characterizes +the feature on the DU. It works today, but treat it as an undocumented +feature - the thresholds carry no guaranteed specs, and nothing prevents +it from disappearing in a future silicon revision. + ## Ballpark overhead figures The digital I/O functions are astonishingly inefficient. This isn't my fault (not on mTC - on DxC I have definitely not helped...) - it's the Arduino API's fault These figures are the difference between a sketch containing a single call to the function, with volatile variables used as arguments to prevent compiler from making assumptions about their values, which may substantially reduce the size of the binary otherwise. diff --git a/megaavr/extras/Ref_Serial.md b/megaavr/extras/Ref_Serial.md index a0f74f69..4b410c27 100644 --- a/megaavr/extras/Ref_Serial.md +++ b/megaavr/extras/Ref_Serial.md @@ -60,6 +60,10 @@ The pinsets are shown on the pinout charts and/or part specific reference pages. * The DD has made things a lot more complicated/exciting with regards to pin mapping for many peripherals, but nothing benefited more than USART0. The options are: * USART0: PA0-PA3, PA4-PA7, PA2-PA3 (no XCK/XDIR), PD4-7, PC1-PC3 (no XDIR). (note that PA2-PA7 are not available on 14-pin parts, so valid swap options are 0, 3, 4, with only 4 having all pins) * USART1: PC0-PC3 (note that 14/20 pin parts have no PC0), PC4-PC7 (not available because pins not present.), PD6-PD7 (no XCK/XDIR). +* The DU-series has a subset of the DD's options - PORTC lost all pins except PC3, taking those mux options with it (per DS40002548A): + * USART0: PA0-PA3, PA4-PA7, PA2-PA3 (no XCK/XDIR), or PD4-7. PA0/PA1 double as the HF crystal pins, so the core defaults USART0 to the PD4-7 mapping on DU variants. + * USART1: PD6-PD7 (no XCK/XDIR) is the only pin position - the hardware reset default is "no pin connection" - so the core defaults Serial1 to PD6/PD7. + * On the DU USB boards (`USBCON` defined), `Serial` is not USART0 like on every other part - it is the native USB CDC port (`USBSerial`). USART0 remains available as `Serial0` (and USART1 as `Serial1`). On the generic no-bootloader DU board, `Serial` is USART0 as usual. * The EA-series has the same options as the DDs, plus USART2 and maybe USART3 on 48-pin parts. The product brief is all we have to go on, and it's not self-consistent on the matter of how many USARTs these will have. It looks like they just copy-pasted pieces of the DD and DB's table together, . * On tinyAVR parts with more than 8 pins, the two mapping options for Serial are PB2, PB3, PB1, PB0 and PA1, PA2, PA3, PA4 for TX, RX, XCK, XDIR. * On 8-pin parts, they are PA6, PA7, PA3, PA0 (yes, that means no XDIR for the 8-pin parts without setting UPDI as GPIO), and PA1, PA2 (no XCK or XDIR with alt mapping) diff --git a/megaavr/extras/Ref_Timers.md b/megaavr/extras/Ref_Timers.md index 9c9f6fd2..18c5e73b 100644 --- a/megaavr/extras/Ref_Timers.md +++ b/megaavr/extras/Ref_Timers.md @@ -16,23 +16,23 @@ TCA or TCD pins; these timers are much better for generation of PWM. Only use TC ## Meet the Modern AVR timers -| TIMER | On DA/DB | On DD | On EA | On EB | Pins: |Relevant Errata: | -|-------------|----------|-------|-------|-------|--------------------------------------------------------|-----------------| -| TCA0 | Yes | Yes | No | No | Pins 0-5 on your choice of ports. | Restart Command not intended to reset direction and on DD and future revisions will not | -| TCA1 | >32 pin | No | Yes | No | Pins 0-5 on PB or PG, else pins 4-6 of PC, PE, PA* PD* | AVR128DA64 cannot output TCA1 compare match (pwm) on PORTG or PORTE | -| TCD0 | Yes | Yes | No | No | PA4-7 on DA/DB. DD has special split-port mux option | Only default portmux works on DA and older DB parts (major die rev B corrects this issue on DB, and if/when it comes out for DA, DA as well). -| TCD1 | No | No | No | No | TBD, not expected to be seen ever. | No announced or released parts have had more than 1 TCD. I would be surprised to see a part with two of these. -| TCB0 | Yes | Yes | Yes | Yes | PA2 or PF4. All TCBs are poor PWM timers | DA/DB and 0/1-series must write both duty cycle and period to write either one, because it acts like a 16-bit register even in PWM mode. Fixed on newer DBs| -| TCB1 | Yes | Yes | Yes | Yes | PA3 or PF5. | And you get just 1 channel of 8-bit PWM for your 16-bit utility timer! (not errata, just lame) | -| TCB2 | Yes | 28/32 | Yes | No | PC0 or PB4. Default millis timer `**` | as above | -| TCB3 | 48/64 pin| No | Yes | No | PB5 or PC1. | as above | -| TCB4 | 64 pin | No | No | No | PG3 ~or PC6~ | On the only parts that have it, the alt portmux is broken. | -| TCE0 | No | No | No | Yes | Pins 0-3 of your choice of ports | None yet. It took years for the TCD errata to start flowing, and TCE is way more complicated. | -| TCF0 | No | No | No | Yes | PA0/1, PA6/7, PF4/5 | Issues reading count accurately. Prescaler issues in some of the NCO modes. -| TCE1 | No | No | No | No | TBD | No announced or released parts have had more than 1 TCE. Prospects for a second are uncertain, but any large chip w/new PLL will surely have 1, possibly 2 | -| TCF1-n | No | No | No | No | TBD | No announced or released parts have had more than 1 TCF. I think if a larger chip w/new PLL is released, it will almost certainly have at least 2. | -| TCA2 | No | No | No | No | TBD | No announced or released parts have had more than 2 TCAs. Unclear if this is something they'd want to do. | -| TCB5-7 | No | No | No | No | TBD | No announced or released parts have had more than 5 TCBs. I expect to see them IFF we get a 100-pin m2560 successor. | +| TIMER | On DA/DB | On DD | On DU| On EA | On EB | Pins: |Relevant Errata: | +|-------------|----------|-------|-------|-------|-------|--------------------------------------------------------|-----------------| +| TCA0 | Yes | Yes | Yes | No | No | Pins 0-5 on your choice of ports. | Restart Command not intended to reset direction and on DD and future revisions will not | +| TCA1 | >32 pin | No | No | Yes | No | Pins 0-5 on PB or PG, else pins 4-6 of PC, PE, PA* PD* | AVR128DA64 cannot output TCA1 compare match (pwm) on PORTG or PORTE | +| TCD0 | Yes | Yes | No | No | No | PA4-7 on DA/DB. DD has special split-port mux option | Only default portmux works on DA and older DB parts (major die rev B corrects this issue on DB, and if/when it comes out for DA, DA as well). +| TCD1 | No | No | No | No | No | TBD, not expected to be seen ever. | No announced or released parts have had more than 1 TCD. I would be surprised to see a part with two of these. +| TCB0 | Yes | Yes | Yes | Yes | Yes | PA2 or PF4. All TCBs are poor PWM timers | DA/DB and 0/1-series must write both duty cycle and period to write either one, because it acts like a 16-bit register even in PWM mode. Fixed on newer DBs| +| TCB1 | Yes | Yes | Yes | Yes | Yes | PA3 or PF5. | And you get just 1 channel of 8-bit PWM for your 16-bit utility timer! (not errata, just lame) | +| TCB2 | Yes | 28/32 | No | Yes | No | PC0 or PB4. Default millis timer `**` | as above | +| TCB3 | 48/64 pin| No | No | Yes | No | PB5 or PC1. | as above | +| TCB4 | 64 pin | No | No | No | No | PG3 ~or PC6~ | On the only parts that have it, the alt portmux is broken. | +| TCE0 | No | No | No | No | Yes | Pins 0-3 of your choice of ports | None yet. It took years for the TCD errata to start flowing, and TCE is way more complicated. | +| TCF0 | No | No | No | No | Yes | PA0/1, PA6/7, PF4/5 | Issues reading count accurately. Prescaler issues in some of the NCO modes. +| TCE1 | No | No | No | No | No | TBD | No announced or released parts have had more than 1 TCE. Prospects for a second are uncertain, but any large chip w/new PLL will surely have 1, possibly 2 | +| TCF1-n | No | No | No | No | No | TBD | No announced or released parts have had more than 1 TCF. I think if a larger chip w/new PLL is released, it will almost certainly have at least 2. | +| TCA2 | No | No | No | No | No | TBD | No announced or released parts have had more than 2 TCAs. Unclear if this is something they'd want to do. | +| TCB5-7 | No | No | No | No | No | TBD | No announced or released parts have had more than 5 TCBs. I expect to see them IFF we get a 100-pin m2560 successor. | | TIMER @@ -41,7 +41,7 @@ TCA or TCD pins; these timers are much better for generation of PWM. Only use TC `**` A TCB cannot be used for PWM if being used for millis. On DD-series with 14 or 20 pins, TCB1 is the default millis timer as it likely will be on the EB-series (unless it's clear that we want to use TCF0). Otherwise, TCB2 is: TCB2 is always the default millis timer if it exists (again, unless it turns out that TCF is better for that). A TCA can be used for millis, though it is less accurate, and it can output PWM and act as the millis timer. TCD as millis is not supported because TCD is *really* not meant for that kind of thing. and is an outstanding PWM timer (see [TCD reference for specific "can I change this without breaking stuff" questions re: TCD0](Ref_TCD.md). We are more forgiving of users tweaking the settings of TCD and wanting to use analogWrite with it because TCD0 is very confusing to configure, and we believe that the number of people who want to be able to tweak it's PWM is markedly larger than the number of people who can figure out how to manually configure it after taking over TCD0. ### Why TCB2 as default millis timer? -Simple - it's the highest numbered timer that's widely distributed (our servo library and tone function check one of TCB1 and TCB0 for being millis, and use that timer if not (hence, since each checks a different timer, if you have three TCBs and one is doing millis, both servo and tone will work only if millis is on TCB2 - otherwise only one of them will). It's also what everyone else seems to be doing, and we should do it the same way for compatibility. Some parts (the smaller pincount DD and all of the future DU and EB parts) do not have a TCB2. In this case, we will instead use TCB1 by default. Servo/tone will notice that TCB1 is used by millis and fall back to TCB0, but that means you can only use one of those at a time with TCB timekeeping +Simple - it's the highest numbered timer that's widely distributed (our servo library and tone function check one of TCB1 and TCB0 for being millis, and use that timer if not (hence, since each checks a different timer, if you have three TCBs and one is doing millis, both servo and tone will work only if millis is on TCB2 - otherwise only one of them will). It's also what everyone else seems to be doing, and we should do it the same way for compatibility. Some parts (the smaller pincount DD and all of the DU and EB parts) do not have a TCB2. In this case, we will instead use TCB1 by default. Servo/tone will notice that TCB1 is used by millis and fall back to TCB0, but that means you can only use one of those at a time with TCB timekeeping Remember, you can change which timer is used to any type A or B timer from the millis timer menu, and the TCD or RTC on the tinyAVR parts. On the AVR EB-series the TCE and TCF may be options pending release of more information. diff --git a/megaavr/extras/Ref_USBCDCboot.md b/megaavr/extras/Ref_USBCDCboot.md new file mode 100644 index 00000000..92017bb1 --- /dev/null +++ b/megaavr/extras/Ref_USBCDCboot.md @@ -0,0 +1,52 @@ +# The USB CDC bootloader for the AVR DU-series (usbcdcboot) + +The DU-series has native USB, so unlike every other Dx/Ex part, it can be bootloaded without a serial adapter: select the **AVR DU-series (USB CDC Bootloader)** board, +plug the part's USB port into the computer, and upload. This document is the user-facing reference, the counterpart of [the Optiboot reference](Ref_Optiboot.md) for the classic serial bootloader. +Developer documentation (design, build instructions, provenance) lives with the sources in [`megaavr/bootloaders/usbcdcboot/`](../bootloaders/usbcdcboot/). + +## What it is + +A small (4 KB boot section) USB CDC-ACM bootloader speaking the same STK500v1 protocol as Optiboot, so avrdude uploads work the usual way. +It is a clean-room implementation written from the USB 2.0 specification and the DU datasheet (DS40002548A) - see `PROVENANCE.md` next to the sources. + +While the bootloader is active it enumerates as a CDC serial port with VID/PID `0x1209:0x0001`; a running sketch enumerates as the application CDC with `0x1209:0x0002`. +(These are pid.codes test IDs, to be replaced for release.) The board definition registers both, so "Get Board Info" recognizes the device in either state. + +## Burning it + +"Burn Bootloader" with a UPDI programmer writes the bootloader hex and the fuses (including `BOOTSIZE = 8`, i.e. a 4 KB boot section). +After that, no programmer is needed for day-to-day work. As with Optiboot, uploading via "Upload Using Programmer" (UPDI) erases the chip, +bootloader included - re-burn it if you want USB uploads back. + +Prebuilt hex files live in `megaavr/bootloaders/hex/` - one per flash size, with `_novreg` variants for boards that feed external 3.3 V into VUSB instead of using the internal regulator (this matches the "VUSB Power Source" tools menu). + +## Entry conditions + +On every reset the bootloader decides between staying resident (USB active, waiting for an upload) and jumping to the application: + +* **1200 bps touch** - when the host opens the application's CDC port at 1200 baud and drops DTR (which is what avrdude does at the start of an upload), +the running sketch's USB stack writes a magic word, detaches from USB, and triggers a watchdog reset. The bootloader sees the magic word and stays. This is the normal, hands-free upload path. + +* **Reset button** - an external reset (RESET pin, `EXTRF`) enters the bootloader. Useful when the sketch has crashed or its USB stack is not functional. +* **Empty application** - if the application reset vector reads as blank flash (`0xFFFF`), the bootloader stays, so a freshly bootloaded part is immediately uploadable. + +Any other reset cause (power-on, brown-out, software reset, watchdog reset without the magic word) starts the application directly, so a deployed device does not sit in the bootloader after a power blip. + +There is no timeout in the stay state: once entered, the bootloader waits until an upload arrives or the part is reset. + +## LED + +While (and only while) the bootloader is resident, it drives an indicator LED - by default PA7, active LOW, matching DxCore's Optiboot LED convention (`LED=A7`) on the 20/28/32-pin DU packages. +Since this is a USB bootloader, the LED never collides with a UART pin position. Builds for the 14-pin parts use a different pin (see the build scripts). + +## Writing to the flash from the app + +The last page of the boot section contains an app-callable SPM stub, and the last two bytes hold a bootloader version word - the same general scheme Optiboot uses,at different addresses. +The Flash library supports this out of the box when the board is the USB CDC Bootloader one (`USING_AVRDU_CDC_BOOTLOADER` is defined): `Flash.writeBytes()` and friends work from application code, with the boot section itself protected. + +## Differences from Optiboot in practice + +* No serial adapter, no autoreset circuit, no DTR capacitor: the USB cable is the whole story. +* Upload speed is not a menu option - USB CDC ignores the baud rate. +* The bootloader does not run on any UART, so all USARTs remain fully available to the sketch. +* Sketches start at 0x1000 (4 KB in) instead of 0x200; the board definition accounts for this automatically. diff --git a/megaavr/libraries/DxCore/examples/PWMTest/PWMTest.ino b/megaavr/libraries/DxCore/examples/PWMTest/PWMTest.ino index 0eca301a..43c23548 100644 --- a/megaavr/libraries/DxCore/examples/PWMTest/PWMTest.ino +++ b/megaavr/libraries/DxCore/examples/PWMTest/PWMTest.ino @@ -59,7 +59,9 @@ // #define MYSERIALSWAP // Override below logic: #if !defined MYSERIALSWAP - #if defined(__AVR_DD__) && defined(_AVR_PINCOUNT) && _AVR_PINCOUNT == 14 && MYSERIAL == SERIAL + #if defined(__AVR_DU__) + #define MYSERIALSWAP 0 + #elif defined(__AVR_DD__) && defined(_AVR_PINCOUNT) && _AVR_PINCOUNT == 14 && MYSERIAL == SERIAL #define MYSERIALSWAP 3 // Too likely I'll be using a clock, and it's a pain to have to wire up VDDIO2 to use serial. #elif MYSERIAL == SERIAL && ((CLOCK_SOURCE & 0x03) == 0) #define MYSERIALSWAP 1 @@ -236,6 +238,10 @@ void setup() { */ +#if defined(DAC0) + /* DAC turn-on/turn-off test - only on parts that have a DAC0 peripheral + * (AVR DA/DB/DD). Parts without a DAC (e.g. AVR DU) skip this entirely, so + * it is not counted as an attempt or a failure there. */ analogWrite(PIN_PD6, 128); delay(100); uint8_t dacpassed = 0; @@ -260,6 +266,7 @@ void setup() { MYSERIAL.print("DAC not turned off, voltage reads as "); MYSERIAL.println(dacread); } +#endif } @@ -322,7 +329,7 @@ void loop() { case 1: { //TCB - if (MILLIS_TIMER & 0x20) { + if ((MILLIS_TIMER & 0xF0) == 0x10) { // TCB family is encoded 0x10-0x1F (TIMERB0..B7); was incorrectly 0x20 (TCF range) if ((MILLIS_TIMER & 0x07) == timernum) { MYSERIAL.print("Millis is using TCB"); SkipCount++; diff --git a/megaavr/libraries/Flash/examples/FlashDemo/FlashDemo.ino b/megaavr/libraries/Flash/examples/FlashDemo/FlashDemo.ino index cb65e84a..ada4d997 100644 --- a/megaavr/libraries/Flash/examples/FlashDemo/FlashDemo.ino +++ b/megaavr/libraries/Flash/examples/FlashDemo/FlashDemo.ino @@ -72,9 +72,10 @@ void Demo() { uint8_t *ptr = Flash.mappedPointer(BASE_ADDRESS + 0x40); if (ptr != NULL) { Serial.println((char *)ptr); - Serial.print(F("No check for addresses being erased - write 0x2F to base + 0x43: ")); + Serial.print(F("No check for addresses being erased - write 0x18 to base + 0x43: ")); Serial.println(Flash.writeByte(BASE_ADDRESS + 0x43, 0x18)); - Serial.println(F("And the string is now mangled:")); + Serial.println(F("Flash bits only go 1 -> 0 without an erase, so that 'l' (0x6C) becomes")); + Serial.println(F("0x6C & 0x18 = 0x08 - a backspace, which your terminal will act on:")); Serial.println((char *)ptr); } } diff --git a/megaavr/libraries/Flash/src/Flash.cpp b/megaavr/libraries/Flash/src/Flash.cpp index d813fe1f..d2bc0109 100644 --- a/megaavr/libraries/Flash/src/Flash.cpp +++ b/megaavr/libraries/Flash/src/Flash.cpp @@ -13,8 +13,22 @@ -#if defined(USING_OPTIBOOT) +/* Geometry of the boot section that mediates our writes: + * REQUIRED_BOOTSIZE - the BOOTSIZE fuse value we expect (units of 512 bytes) + * BOOTLOADER_END - first flash address the application may write to + * The classic Optiboot numbers (BOOTSIZE = 1, 512 bytes) are the default; + * the AVR-DU CDC bootloader is a 4 KB boot section instead. */ +#if defined(USING_AVRDU_CDC_BOOTLOADER) + /* AVR-DU USB CDC bootloader: 4 KB boot section with the app-callable + * SPM stub (spm z+; ret) in its last 6 bytes, and a version word in + * the last 2 (see bootloaders/usbcdcboot/src/spm_entry.c). */ + #define SPMCOMMAND "call 0x0ffa" + #define REQUIRED_BOOTSIZE (0x08) + #define BOOTLOADER_END (4096) +#elif defined(USING_OPTIBOOT) #define SPMCOMMAND "call 0x1FA" + #define REQUIRED_BOOTSIZE (0x01) + #define BOOTLOADER_END (512) #elif defined(SPM_FROM_APP) #if SPM_FROM_APP == -1 #if defined(LTODISABLED) @@ -36,6 +50,13 @@ #error "You must also enable writing to flash from app in tools menu." #endif +#if !defined(REQUIRED_BOOTSIZE) + /* SPM_FROM_APP: the core puts the SPM routine in the first page, and the + * BOOTSIZE fuse is set to 1 so that page forms the boot section. */ + #define REQUIRED_BOOTSIZE (0x01) + #define BOOTLOADER_END (512) +#endif + #ifdef SPMCOMMAND // this way, if we can't write to flash, hopefully, it will make fewer errors so they'll see the real ones! /* My go-to NVMCTRL.CTRLA write function - check status only at start @@ -55,7 +76,27 @@ void do_nvmctrl(uint8_t command) { uint8_t FlashClass::checkWritable() { - #ifndef USING_OPTIBOOT + #if defined(USING_AVRDU_CDC_BOOTLOADER) + if (FUSE.BOOTSIZE != REQUIRED_BOOTSIZE) { + // The CDC bootloader occupies a 4 KB boot section (BOOTSIZE = 8). + return FLASHWRITE_CFGMISMATCH; + } + uint16_t blversion = pgm_read_word_near(0x0ffe); + if ((blversion >> 8) != 0x1A) { + // Not a CDC bootloader that publishes the SPM entry convention. + return FLASHWRITE_UNRECOGNIZED; + } + uint16_t blentry = pgm_read_word_near(0x0ffa); + if (blentry == 0x0000 || blentry == 0xFFFF) { + // Bootloader built with APP_NOSPM - entry deliberately disabled. + return FLASHWRITE_DISABLED; + } + if (blentry == 0x95f8) { + // spm z+ - the expected entry stub. + return FLASHWRITE_OK; + } + return FLASHWRITE_BADENTRYPOINT; + #elif !defined(USING_OPTIBOOT) if (FUSE.BOOTSIZE == 0x00) { return FLASHWRITE_NOBOOTSIZE; } @@ -139,9 +180,9 @@ uint8_t FlashClass::checkWritable() { uint8_t FlashClass::erasePage(const uint32_t address, const uint8_t size) { #if (defined(USING_OPTIBOOT) || SPM_FROM_APP==-1) - if ((FUSE.BOOTSIZE != 0x01)) { + if ((FUSE.BOOTSIZE != REQUIRED_BOOTSIZE)) { #else - if ((FUSE.BOOTSIZE != 0x01) || (FUSE.CODESIZE != SPM_FROM_APP)) { + if ((FUSE.BOOTSIZE != REQUIRED_BOOTSIZE) || (FUSE.CODESIZE != SPM_FROM_APP)) { #endif return FLASHWRITE_NOBOOT; } @@ -223,13 +264,13 @@ uint8_t FlashClass::erasePage(const uint32_t address, const uint8_t size) { uint8_t FlashClass::writeWord(const uint32_t address, const uint16_t data) { #if (defined(USING_OPTIBOOT) || SPM_FROM_APP==-1) - if ((FUSE.BOOTSIZE != 0x01)) { + if ((FUSE.BOOTSIZE != REQUIRED_BOOTSIZE)) { #else - if ((FUSE.BOOTSIZE != 0x01) || (FUSE.CODESIZE != SPM_FROM_APP)) { + if ((FUSE.BOOTSIZE != REQUIRED_BOOTSIZE) || (FUSE.CODESIZE != SPM_FROM_APP)) { #endif return FLASHWRITE_NOBOOT; } - if (address > (PROGMEM_SIZE - 2) || address < 512) { + if (address > (PROGMEM_SIZE - 2) || address < BOOTLOADER_END) { return FLASHWRITE_BADADDR; } @@ -271,14 +312,14 @@ uint8_t FlashClass::writeWord(const uint32_t address, const uint16_t data) { uint8_t FlashClass::writeByte(const uint32_t address, const uint8_t data) { #if (defined(USING_OPTIBOOT) || SPM_FROM_APP == -1) - if ((FUSE.BOOTSIZE != 0x01)) + if ((FUSE.BOOTSIZE != REQUIRED_BOOTSIZE)) #else - if ((FUSE.BOOTSIZE != 0x01) || (FUSE.CODESIZE != SPM_FROM_APP)) + if ((FUSE.BOOTSIZE != REQUIRED_BOOTSIZE) || (FUSE.CODESIZE != SPM_FROM_APP)) #endif { return FLASHWRITE_NOBOOT; } - if ((address > PROGMEM_SIZE - 2) || address < 512) { + if ((address > PROGMEM_SIZE - 2) || address < BOOTLOADER_END) { return FLASHWRITE_BADADDR; } #if !defined(NO_CORE_RESERVED) @@ -337,13 +378,13 @@ uint8_t FlashClass::writeWords(const uint32_t address, const uint16_t* data, uin return FLASHWRITE_0LENGTH; } #if (defined(USING_OPTIBOOT) || SPM_FROM_APP==-1) - if ((FUSE.BOOTSIZE != 0x01)) { + if ((FUSE.BOOTSIZE != REQUIRED_BOOTSIZE)) { #else - if ((FUSE.BOOTSIZE != 0x01) || (FUSE.CODESIZE != SPM_FROM_APP)) { + if ((FUSE.BOOTSIZE != REQUIRED_BOOTSIZE) || (FUSE.CODESIZE != SPM_FROM_APP)) { #endif return FLASHWRITE_NOBOOT; } - if (address > (PROGMEM_SIZE - 2) || address < 512) { + if (address > (PROGMEM_SIZE - 2) || address < BOOTLOADER_END) { return FLASHWRITE_BADADDR; } if (address & 0x01) { @@ -405,20 +446,28 @@ uint8_t FlashClass::writeWords(const uint32_t address, const uint16_t* data, uin uint8_t FlashClass::writeBytes(const uint32_t address, const uint8_t* data, uint16_t length) { uint32_t tAddress = address; - uint8_t status; - if(address & 0x01) { - status = writeByte(tAddress++, *(data)); + uint8_t status = FLASHWRITE_OK; + if (length == 0) { + return FLASHWRITE_0LENGTH; + } + if (tAddress & 0x01) { + // Unaligned start: write the leading byte, then continue word-aligned. + status = writeByte(tAddress++, *data++); if (status) return status; length--; } - if(length > 1) { - status = writeWords(tAddress, (uint16_t*) data, (length >> 1)); + if (length > 1) { + // The bulk of the data, as whole words. + uint16_t words = length >> 1; + status = writeWords(tAddress, (uint16_t*) data, words); if (status) return status; + tAddress += ((uint32_t) words) << 1; + data += words << 1; + length -= words << 1; // 0 or 1 byte left } - // there may be one more byte... if (length & 1) { - data += (length & 0xFFFE); // what we wrote with the word above... - status = writeByte(tAddress + length - 2, *data); + // And finally the trailing byte, if the length was odd. + status = writeByte(tAddress, *data); } return status; } diff --git a/megaavr/libraries/HID/library.properties b/megaavr/libraries/HID/library.properties new file mode 100644 index 00000000..17791dcc --- /dev/null +++ b/megaavr/libraries/HID/library.properties @@ -0,0 +1,10 @@ +name=HID +version=1.0.0 +author=Arduino +maintainer=Arduino +sentence=Pluggable HID library, bundled for DxCore AVR DU. +paragraph=Provides the HID_ PluggableUSBModule used by Keyboard, Mouse, and other libraries. Bundled here so DxCore's AVR DU native USB stack supplies it without conflicting with the AVR core's copy on hosts that have both installed. +category=Device Control +url= +architectures=megaavr +includes=HID.h diff --git a/megaavr/libraries/HID/src/HID.cpp b/megaavr/libraries/HID/src/HID.cpp new file mode 100644 index 00000000..3dd85fc1 --- /dev/null +++ b/megaavr/libraries/HID/src/HID.cpp @@ -0,0 +1,162 @@ +/* + Copyright (c) 2015, Arduino LLC + Original code (pre-library): Copyright (c) 2011, Peter Barrett + + Permission to use, copy, modify, and/or distribute this software for + any purpose with or without fee is hereby granted, provided that the + above copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR + BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES + OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + SOFTWARE. + */ + +#include "HID.h" + +#if defined(USBCON) + +HID_& HID() +{ + static HID_ obj; + return obj; +} + +int HID_::getInterface(uint8_t* interfaceCount) +{ + *interfaceCount += 1; // uses 1 + HIDDescriptor hidInterface = { + D_INTERFACE(pluggedInterface, 1, USB_DEVICE_CLASS_HUMAN_INTERFACE, HID_SUBCLASS_NONE, HID_PROTOCOL_NONE), + D_HIDREPORT(descriptorSize), + D_ENDPOINT(USB_ENDPOINT_IN(pluggedEndpoint), USB_ENDPOINT_TYPE_INTERRUPT, USB_EP_SIZE, 0x01) + }; + return USB_SendControl(0, &hidInterface, sizeof(hidInterface)); +} + +int HID_::getDescriptor(USBSetup& setup) +{ + // Check if this is a HID Class Descriptor request + if (setup.bmRequestType != REQUEST_DEVICETOHOST_STANDARD_INTERFACE) { return 0; } + if (setup.wValueH != HID_REPORT_DESCRIPTOR_TYPE) { return 0; } + + // In a HID Class Descriptor wIndex contains the interface number + if (setup.wIndex != pluggedInterface) { return 0; } + + int total = 0; + HIDSubDescriptor* node; + for (node = rootNode; node; node = node->next) { + int res = USB_SendControl(TRANSFER_PGM, node->data, node->length); + if (res == -1) + return -1; + total += res; + } + + // Reset the protocol on reenumeration. Normally the host should not assume the state of the protocol + // due to the USB specs, but Windows and Linux just assumes its in report mode. + protocol = HID_REPORT_PROTOCOL; + + return total; +} + +uint8_t HID_::getShortName(char *name) +{ + name[0] = 'H'; + name[1] = 'I'; + name[2] = 'D'; + name[3] = 'A' + (descriptorSize & 0x0F); + name[4] = 'A' + ((descriptorSize >> 4) & 0x0F); + return 5; +} + +void HID_::AppendDescriptor(HIDSubDescriptor *node) +{ + if (!rootNode) { + rootNode = node; + } else { + HIDSubDescriptor *current = rootNode; + while (current->next) { + current = current->next; + } + current->next = node; + } + descriptorSize += node->length; +} + +int HID_::SendReport(uint8_t id, const void* data, int len) +{ + auto ret = USB_Send(pluggedEndpoint, &id, 1); + if (ret < 0) return ret; + auto ret2 = USB_Send(pluggedEndpoint | TRANSFER_RELEASE, data, len); + if (ret2 < 0) return ret2; + return ret + ret2; +} + +bool HID_::setup(USBSetup& setup) +{ + if (pluggedInterface != setup.wIndex) { + return false; + } + + uint8_t request = setup.bRequest; + uint8_t requestType = setup.bmRequestType; + + if (requestType == REQUEST_DEVICETOHOST_CLASS_INTERFACE) + { + if (request == HID_GET_REPORT) { + // TODO: HID_GetReport(); + return true; + } + if (request == HID_GET_PROTOCOL) { + // TODO: Send8(protocol); + return true; + } + if (request == HID_GET_IDLE) { + // TODO: Send8(idle); + } + } + + if (requestType == REQUEST_HOSTTODEVICE_CLASS_INTERFACE) + { + if (request == HID_SET_PROTOCOL) { + // The USB Host tells us if we are in boot or report mode. + // This only works with a real boot compatible device. + protocol = setup.wValueL; + return true; + } + if (request == HID_SET_IDLE) { + idle = setup.wValueL; + return true; + } + if (request == HID_SET_REPORT) + { + //uint8_t reportID = setup.wValueL; + //uint16_t length = setup.wLength; + //uint8_t data[length]; + // Make sure to not read more data than USB_EP_SIZE. + // You can read multiple times through a loop. + // The first byte (may!) contain the reportID on a multreport. + //USB_RecvControl(data, length); + } + } + + return false; +} + +HID_::HID_(void) : PluggableUSBModule(1, 1, epType), + rootNode(NULL), descriptorSize(0), + protocol(HID_REPORT_PROTOCOL), idle(1) +{ + epType[0] = EP_TYPE_INTERRUPT_IN; + PluggableUSB().plug(this); +} + +int HID_::begin(void) +{ + return 0; +} + +#endif /* if defined(USBCON) */ diff --git a/megaavr/libraries/HID/src/HID.h b/megaavr/libraries/HID/src/HID.h new file mode 100644 index 00000000..7ebce38e --- /dev/null +++ b/megaavr/libraries/HID/src/HID.h @@ -0,0 +1,126 @@ +/* + Copyright (c) 2015, Arduino LLC + Original code (pre-library): Copyright (c) 2011, Peter Barrett + + Permission to use, copy, modify, and/or distribute this software for + any purpose with or without fee is hereby granted, provided that the + above copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR + BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES + OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + SOFTWARE. + */ + +#ifndef HID_h +#define HID_h + +#include +#include +#include "PluggableUSB.h" +#include "usb_ep_types.h" /* DxCore AVR DU: EP_TYPE_*, USB_EP_SIZE, TRANSFER_PGM/RELEASE */ + +#if defined(USBCON) + +#define _USING_HID + +// HID 'Driver' +// ------------ +#define HID_GET_REPORT 0x01 +#define HID_GET_IDLE 0x02 +#define HID_GET_PROTOCOL 0x03 +#define HID_SET_REPORT 0x09 +#define HID_SET_IDLE 0x0A +#define HID_SET_PROTOCOL 0x0B + +#define HID_HID_DESCRIPTOR_TYPE 0x21 +#define HID_REPORT_DESCRIPTOR_TYPE 0x22 +#define HID_PHYSICAL_DESCRIPTOR_TYPE 0x23 + +// HID subclass HID1.11 Page 8 4.2 Subclass +#define HID_SUBCLASS_NONE 0 +#define HID_SUBCLASS_BOOT_INTERFACE 1 + +// HID Keyboard/Mouse bios compatible protocols HID1.11 Page 9 4.3 Protocols +#define HID_PROTOCOL_NONE 0 +#define HID_PROTOCOL_KEYBOARD 1 +#define HID_PROTOCOL_MOUSE 2 + +// Normal or bios protocol (Keyboard/Mouse) HID1.11 Page 54 7.2.5 Get_Protocol Request +// "protocol" variable is used for this purpose. +#define HID_BOOT_PROTOCOL 0 +#define HID_REPORT_PROTOCOL 1 + +// HID Request Type HID1.11 Page 51 7.2.1 Get_Report Request +#define HID_REPORT_TYPE_INPUT 1 +#define HID_REPORT_TYPE_OUTPUT 2 +#define HID_REPORT_TYPE_FEATURE 3 + +typedef struct +{ + uint8_t len; // 9 + uint8_t dtype; // 0x21 + uint8_t addr; + uint8_t versionL; // 0x101 + uint8_t versionH; // 0x101 + uint8_t country; + uint8_t desctype; // 0x22 report + uint8_t descLenL; + uint8_t descLenH; +} HIDDescDescriptor; + +typedef struct +{ + InterfaceDescriptor hid; + HIDDescDescriptor desc; + EndpointDescriptor in; +} HIDDescriptor; + +class HIDSubDescriptor { +public: + HIDSubDescriptor *next = NULL; + HIDSubDescriptor(const void *d, const uint16_t l) : data(d), length(l) { } + + const void* data; + const uint16_t length; +}; + +class HID_ : public PluggableUSBModule +{ +public: + HID_(void); + int begin(void); + int SendReport(uint8_t id, const void* data, int len); + void AppendDescriptor(HIDSubDescriptor* node); + +protected: + // Implementation of the PluggableUSBModule + int getInterface(uint8_t* interfaceCount); + int getDescriptor(USBSetup& setup); + bool setup(USBSetup& setup); + uint8_t getShortName(char* name); + +private: + uint8_t epType[1]; /* DxCore: api/PluggableUSB.h declares epType as uint8_t* */ + + HIDSubDescriptor* rootNode; + uint16_t descriptorSize; + + uint8_t protocol; + uint8_t idle; +}; + +// Replacement for global singleton. +// This function prevents static-initialization-order-fiasco +// https://isocpp.org/wiki/faq/ctors#static-init-order-on-first-use +HID_& HID(); + +#define D_HIDREPORT(length) { 9, 0x21, 0x01, 0x01, 0, 1, 0x22, lowByte(length), highByte(length) } + +#endif // USBCON + +#endif // HID_h diff --git a/megaavr/variants/14pin-duseries/pins_arduino.h b/megaavr/variants/14pin-duseries/pins_arduino.h index ebcca10a..c7df931e 100644 --- a/megaavr/variants/14pin-duseries/pins_arduino.h +++ b/megaavr/variants/14pin-duseries/pins_arduino.h @@ -72,7 +72,7 @@ Include guard and include basic libraries. We are normally including this inside #if !defined(LED_BUILTIN) - #define LED_BUILTIN (PIN_PD6) /* warning: gets overridden when using Serial1 on 14-pin parts, as that uses PD4. */ + #define LED_BUILTIN (PIN_PD6) /* PD6 is USART1(ALT2) TxD on the DU; LED_BUILTIN is unavailable while Serial1 is in use. */ #endif #ifdef CORE_ATTACH_OLD #define EXTERNAL_NUM_INTERRUPTS (32) @@ -152,30 +152,47 @@ Include guard and include basic libraries. We are normally including this inside // USART 0 #define HWSERIAL0_MUX (0x00 /* PORTMUX_USART0_DEFAULT_gc */) +#define HWSERIAL0_MUX_PINSWAP_1 (0x01 /* PORTMUX_USART0_ALT1_gc - PA4/PA5 absent on 14-pin; placeholder so the PINSWAP_3 row is built into _usart0_pins[] */) +#define HWSERIAL0_MUX_PINSWAP_2 (0x02 /* PORTMUX_USART0_ALT2_gc - PA2/PA3 absent on 14-pin; placeholder */) #define HWSERIAL0_MUX_PINSWAP_3 (0x03 /* PORTMUX_USART0_ALT3_gc */) #define HWSERIAL0_MUX_PINSWAP_NONE (0x05) #define PIN_HWSERIAL0_TX (PIN_PA0) #define PIN_HWSERIAL0_RX (PIN_PA1) #define PIN_HWSERIAL0_XCK (NOT_A_PIN) #define PIN_HWSERIAL0_XDIR (NOT_A_PIN) +#define PIN_HWSERIAL0_TX_PINSWAP_1 (NOT_A_PIN) /* ALT1 placeholder (PA4 absent on 14-pin) */ +#define PIN_HWSERIAL0_RX_PINSWAP_1 (NOT_A_PIN) +#define PIN_HWSERIAL0_XCK_PINSWAP_1 (NOT_A_PIN) +#define PIN_HWSERIAL0_XDIR_PINSWAP_1 (NOT_A_PIN) +#define PIN_HWSERIAL0_TX_PINSWAP_2 (NOT_A_PIN) /* ALT2 placeholder (PA2 absent on 14-pin) */ +#define PIN_HWSERIAL0_RX_PINSWAP_2 (NOT_A_PIN) +#define PIN_HWSERIAL0_XCK_PINSWAP_2 (NOT_A_PIN) +#define PIN_HWSERIAL0_XDIR_PINSWAP_2 (NOT_A_PIN) #define PIN_HWSERIAL0_TX_PINSWAP_3 (PIN_PD4) #define PIN_HWSERIAL0_RX_PINSWAP_3 (PIN_PD5) #define PIN_HWSERIAL0_XCK_PINSWAP_3 (PIN_PD6) #define PIN_HWSERIAL0_XDIR_PINSWAP_3 (PIN_PD7) +#define HWSERIAL0_MUX_DEFAULT (3) /* DU default: USART0 ALT3 (PD4/PD5); row index of PINSWAP_3 */ // USART1 #define HWSERIAL1_MUX (0x00 /* PORTMUX_USART1_DEFAULT_gc */) +#define HWSERIAL1_MUX_PINSWAP_1 (0x01 << 3 /* PORTMUX_USART1_ALT1_gc - absent on DU (PC4/PC5 not present); placeholder so the PINSWAP_2 row is built into _usart1_pins[] */) #define HWSERIAL1_MUX_PINSWAP_2 (0x02 << 3 /* PORTMUX_USART1_ALT2_gc */) -#define HWSERIAL1_MUX_PINSWAP_NONE (0x03 << 2 /* PORTMUX_USART1_NONE_gc */) +#define HWSERIAL1_MUX_PINSWAP_NONE (0x03 << 3 /* PORTMUX_USART1_NONE_gc */) #define PIN_HWSERIAL1_TX (NOT_A_PIN) #define PIN_HWSERIAL1_RX (NOT_A_PIN) #define PIN_HWSERIAL1_XCK (NOT_A_PIN) #define PIN_HWSERIAL1_XDIR (NOT_A_PIN) +#define PIN_HWSERIAL1_TX_PINSWAP_1 (NOT_A_PIN) /* ALT1 placeholder (absent on DU) */ +#define PIN_HWSERIAL1_RX_PINSWAP_1 (NOT_A_PIN) +#define PIN_HWSERIAL1_XCK_PINSWAP_1 (NOT_A_PIN) +#define PIN_HWSERIAL1_XDIR_PINSWAP_1 (NOT_A_PIN) #define PIN_HWSERIAL1_TX_PINSWAP_2 (PIN_PD6) #define PIN_HWSERIAL1_RX_PINSWAP_2 (PIN_PD7) #define PIN_HWSERIAL1_XCK_PINSWAP_2 (NOT_A_PIN) #define PIN_HWSERIAL1_XDIR_PINSWAP_2 (NOT_A_PIN) +#define HWSERIAL1_MUX_DEFAULT (2) /* DU default: USART1 ALT2 (PD6/PD7); row index of PINSWAP_2 (USART1 has no usable DEFAULT position on DU) */ /*## # # ### # ### ### #### ### # # ### # # ## # # # # # # # # # # ## # # @@ -379,4 +396,66 @@ const uint8_t digital_pin_to_bit_mask[] = { // *INDENT-OFF* }; #endif + +/* ================================================================= + * USB identity (AVR DU = USB-native part) + * ================================================================= + * Every AVR DU has the USB0 peripheral, so - like the ATmega32U4 on + * the Leonardo/Micro - the DU is treated as a USB-native board. USBCON + * tells Arduino's HID / Keyboard / Mouse / Joystick libraries this board + * has USB. USB_VID / USB_PID are the board identity and may be overridden + * from boards.txt (all #ifndef) - e.g. a ProMicro clone, or the USB-CDC + * bootloader board (0xDA33). Defaults: pid.codes test VID/PID 0x1209:0xDA32. + */ +#ifndef USBCON + #define USBCON +#endif +#ifndef USB_VID + #define USB_VID 0x1209 +#endif +#ifndef USB_PID + #define USB_PID 0x0002 +#endif +#ifndef USB_MANUFACTURER + #define USB_MANUFACTURER "DxCore" +#endif +#ifndef USB_PRODUCT + #define USB_PRODUCT "AVRDU" +#endif + +/* ================================================================= + * Serial -> native USB CDC (Arduino Leonardo convention) + * ================================================================= + * DxCore's cores/dxcore/Arduino.h later does: + * #ifndef Serial + * #define Serial Serial0 + * #endif + * Pre-defining Serial here points it at the on-chip USB CDC instance, + * USBSerial (class USBSerial_, declared in core USBSerial.h, guarded by + * USB0) - exactly as the ATmega32U4 cores make Serial the native USB port + * on the Leonardo/Micro. USART0 stays reachable as Serial0 and USART1 as + * Serial1 on every DU board, so the hardware UART port names never shift + * between one DU board and another. + * + * Whether the USB CDC is actually started at boot is decided by the core + * (usb_auto_init()), not here: the USB-CDC bootloader board auto-starts it + * (upload port + 1200bps touch), while a plain no-bootloader board leaves + * it inactive until the sketch calls Serial.begin() - so on such a board + * Serial simply produces no output unless USB is opened. + * + * Define HAVE_NO_USB_SERIAL_REDIRECT (e.g. from boards.txt) to keep the + * legacy Serial == USART0 behaviour instead. + */ +#if defined(USB0) && !defined(HAVE_NO_USB_SERIAL_REDIRECT) + #ifndef Serial + #define Serial USBSerial /* Serial = native USB CDC */ + #endif + #ifndef SERIAL_PORT_MONITOR + #define SERIAL_PORT_MONITOR Serial /* Serial Monitor -> USB */ + #endif + #ifndef SERIAL_PORT_USBVIRTUAL + #define SERIAL_PORT_USBVIRTUAL Serial /* native USB virtual serial */ + #endif +#endif + #endif diff --git a/megaavr/variants/20pin-duseries/pins_arduino.h b/megaavr/variants/20pin-duseries/pins_arduino.h index f0a3000f..c694f6f9 100644 --- a/megaavr/variants/20pin-duseries/pins_arduino.h +++ b/megaavr/variants/20pin-duseries/pins_arduino.h @@ -151,6 +151,7 @@ Include guard and include basic libraries. We are normally including this inside #define HWSERIAL0_MUX_PINSWAP_2 (0x02 /* PORTMUX_USART0_ALT2_gc */) #define HWSERIAL0_MUX_PINSWAP_3 (0x03 /* PORTMUX_USART0_ALT3_gc */) #define HWSERIAL0_MUX_PINSWAP_NONE (0x05) +#define HWSERIAL0_MUX_DEFAULT (3) /* DU default: USART0 ALT3 (PD4/PD5); row index of PINSWAP_3 */ #define PIN_HWSERIAL0_TX (PIN_PA0) #define PIN_HWSERIAL0_RX (PIN_PA1) #define PIN_HWSERIAL0_XCK (PIN_PA2) @@ -170,16 +171,22 @@ Include guard and include basic libraries. We are normally including this inside // USART1 #define HWSERIAL1_MUX (0x00 /* PORTMUX_USART1_DEFAULT_gc */) +#define HWSERIAL1_MUX_PINSWAP_1 (0x01 << 3 /* PORTMUX_USART1_ALT1_gc - absent on DU (PC4/PC5 not present); placeholder so the PINSWAP_2 row is built into _usart1_pins[] */) #define HWSERIAL1_MUX_PINSWAP_2 (0x02 << 3 /* PORTMUX_USART1_ALT2_gc */) #define HWSERIAL1_MUX_PINSWAP_NONE (0x03 << 3) #define PIN_HWSERIAL1_TX (NOT_A_PIN) #define PIN_HWSERIAL1_RX (NOT_A_PIN) #define PIN_HWSERIAL1_XCK (NOT_A_PIN) #define PIN_HWSERIAL1_XDIR (NOT_A_PIN) +#define PIN_HWSERIAL1_TX_PINSWAP_1 (NOT_A_PIN) /* ALT1 placeholder (absent on DU) */ +#define PIN_HWSERIAL1_RX_PINSWAP_1 (NOT_A_PIN) +#define PIN_HWSERIAL1_XCK_PINSWAP_1 (NOT_A_PIN) +#define PIN_HWSERIAL1_XDIR_PINSWAP_1 (NOT_A_PIN) #define PIN_HWSERIAL1_TX_PINSWAP_2 (PIN_PD6) #define PIN_HWSERIAL1_RX_PINSWAP_2 (PIN_PD7) #define PIN_HWSERIAL1_XCK_PINSWAP_2 (NOT_A_PIN) #define PIN_HWSERIAL1_XDIR_PINSWAP_2 (NOT_A_PIN) +#define HWSERIAL1_MUX_DEFAULT (2) /* DU default: USART1 ALT2 (PD6/PD7); row index of PINSWAP_2 (USART1 has no usable DEFAULT position on DU) */ /*## # # ### # ### ### #### ### # # ### # # ## # # # # # # # # # # ## # # @@ -395,4 +402,66 @@ static const uint8_t A31 = PIN_A31; }; #endif + +/* ================================================================= + * USB identity (AVR DU = USB-native part) + * ================================================================= + * Every AVR DU has the USB0 peripheral, so - like the ATmega32U4 on + * the Leonardo/Micro - the DU is treated as a USB-native board. USBCON + * tells Arduino's HID / Keyboard / Mouse / Joystick libraries this board + * has USB. USB_VID / USB_PID are the board identity and may be overridden + * from boards.txt (all #ifndef) - e.g. a ProMicro clone, or the USB-CDC + * bootloader board (0xDA33). Defaults: pid.codes test VID/PID 0x1209:0xDA32. + */ +#ifndef USBCON + #define USBCON +#endif +#ifndef USB_VID + #define USB_VID 0x1209 +#endif +#ifndef USB_PID + #define USB_PID 0x0002 +#endif +#ifndef USB_MANUFACTURER + #define USB_MANUFACTURER "DxCore" +#endif +#ifndef USB_PRODUCT + #define USB_PRODUCT "AVRDU" +#endif + +/* ================================================================= + * Serial -> native USB CDC (Arduino Leonardo convention) + * ================================================================= + * DxCore's cores/dxcore/Arduino.h later does: + * #ifndef Serial + * #define Serial Serial0 + * #endif + * Pre-defining Serial here points it at the on-chip USB CDC instance, + * USBSerial (class USBSerial_, declared in core USBSerial.h, guarded by + * USB0) - exactly as the ATmega32U4 cores make Serial the native USB port + * on the Leonardo/Micro. USART0 stays reachable as Serial0 and USART1 as + * Serial1 on every DU board, so the hardware UART port names never shift + * between one DU board and another. + * + * Whether the USB CDC is actually started at boot is decided by the core + * (usb_auto_init()), not here: the USB-CDC bootloader board auto-starts it + * (upload port + 1200bps touch), while a plain no-bootloader board leaves + * it inactive until the sketch calls Serial.begin() - so on such a board + * Serial simply produces no output unless USB is opened. + * + * Define HAVE_NO_USB_SERIAL_REDIRECT (e.g. from boards.txt) to keep the + * legacy Serial == USART0 behaviour instead. + */ +#if defined(USB0) && !defined(HAVE_NO_USB_SERIAL_REDIRECT) + #ifndef Serial + #define Serial USBSerial /* Serial = native USB CDC */ + #endif + #ifndef SERIAL_PORT_MONITOR + #define SERIAL_PORT_MONITOR Serial /* Serial Monitor -> USB */ + #endif + #ifndef SERIAL_PORT_USBVIRTUAL + #define SERIAL_PORT_USBVIRTUAL Serial /* native USB virtual serial */ + #endif +#endif + #endif diff --git a/megaavr/variants/28pin-duseries/pins_arduino.h b/megaavr/variants/28pin-duseries/pins_arduino.h index 3dc0c863..8278ef45 100644 --- a/megaavr/variants/28pin-duseries/pins_arduino.h +++ b/megaavr/variants/28pin-duseries/pins_arduino.h @@ -166,6 +166,7 @@ Include guard and include basic libraries. We are normally including this inside #define HWSERIAL0_MUX_PINSWAP_2 (0x02 /* PORTMUX_USART0_ALT2_gc */) #define HWSERIAL0_MUX_PINSWAP_3 (0x03 /* PORTMUX_USART0_ALT3_gc */) #define HWSERIAL0_MUX_PINSWAP_NONE (0x05) +#define HWSERIAL0_MUX_DEFAULT (3) /* DU default: USART0 ALT3 (PD4/PD5); row index of PINSWAP_3 */ #define PIN_HWSERIAL0_TX (PIN_PA0) #define PIN_HWSERIAL0_RX (PIN_PA1) #define PIN_HWSERIAL0_XCK (PIN_PA2) @@ -185,16 +186,22 @@ Include guard and include basic libraries. We are normally including this inside // USART1 #define HWSERIAL1_MUX (0x00 /* PORTMUX_USART1_DEFAULT_gc */) +#define HWSERIAL1_MUX_PINSWAP_1 (0x01 << 3 /* PORTMUX_USART1_ALT1_gc - absent on DU (PC4/PC5 not present); placeholder so the PINSWAP_2 row is built into _usart1_pins[] */) #define HWSERIAL1_MUX_PINSWAP_2 (0x02 << 3 /* PORTMUX_USART1_ALT2_gc */) #define HWSERIAL1_MUX_PINSWAP_NONE (0x03 << 3) #define PIN_HWSERIAL1_TX (NOT_A_PIN) #define PIN_HWSERIAL1_RX (NOT_A_PIN) #define PIN_HWSERIAL1_XCK (NOT_A_PIN) #define PIN_HWSERIAL1_XDIR (NOT_A_PIN) +#define PIN_HWSERIAL1_TX_PINSWAP_1 (NOT_A_PIN) /* ALT1 placeholder (absent on DU) */ +#define PIN_HWSERIAL1_RX_PINSWAP_1 (NOT_A_PIN) +#define PIN_HWSERIAL1_XCK_PINSWAP_1 (NOT_A_PIN) +#define PIN_HWSERIAL1_XDIR_PINSWAP_1 (NOT_A_PIN) #define PIN_HWSERIAL1_TX_PINSWAP_2 (PIN_PD6) #define PIN_HWSERIAL1_RX_PINSWAP_2 (PIN_PD7) #define PIN_HWSERIAL1_XCK_PINSWAP_2 (NOT_A_PIN) #define PIN_HWSERIAL1_XDIR_PINSWAP_2 (NOT_A_PIN) +#define HWSERIAL1_MUX_DEFAULT (2) /* DU default: USART1 ALT2 (PD6/PD7); row index of PINSWAP_2 (USART1 has no usable DEFAULT position on DU) */ /*## # # ### # ### ### #### ### # # ### # # ## # # # # # # # # # # ## # # @@ -443,4 +450,66 @@ static const uint8_t A31 = PIN_A31; }; #endif + +/* ================================================================= + * USB identity (AVR DU = USB-native part) + * ================================================================= + * Every AVR DU has the USB0 peripheral, so - like the ATmega32U4 on + * the Leonardo/Micro - the DU is treated as a USB-native board. USBCON + * tells Arduino's HID / Keyboard / Mouse / Joystick libraries this board + * has USB. USB_VID / USB_PID are the board identity and may be overridden + * from boards.txt (all #ifndef) - e.g. a ProMicro clone, or the USB-CDC + * bootloader board (0xDA33). Defaults: pid.codes test VID/PID 0x1209:0xDA32. + */ +#ifndef USBCON + #define USBCON +#endif +#ifndef USB_VID + #define USB_VID 0x1209 +#endif +#ifndef USB_PID + #define USB_PID 0x0002 +#endif +#ifndef USB_MANUFACTURER + #define USB_MANUFACTURER "DxCore" +#endif +#ifndef USB_PRODUCT + #define USB_PRODUCT "AVRDU" +#endif + +/* ================================================================= + * Serial -> native USB CDC (Arduino Leonardo convention) + * ================================================================= + * DxCore's cores/dxcore/Arduino.h later does: + * #ifndef Serial + * #define Serial Serial0 + * #endif + * Pre-defining Serial here points it at the on-chip USB CDC instance, + * USBSerial (class USBSerial_, declared in core USBSerial.h, guarded by + * USB0) - exactly as the ATmega32U4 cores make Serial the native USB port + * on the Leonardo/Micro. USART0 stays reachable as Serial0 and USART1 as + * Serial1 on every DU board, so the hardware UART port names never shift + * between one DU board and another. + * + * Whether the USB CDC is actually started at boot is decided by the core + * (usb_auto_init()), not here: the USB-CDC bootloader board auto-starts it + * (upload port + 1200bps touch), while a plain no-bootloader board leaves + * it inactive until the sketch calls Serial.begin() - so on such a board + * Serial simply produces no output unless USB is opened. + * + * Define HAVE_NO_USB_SERIAL_REDIRECT (e.g. from boards.txt) to keep the + * legacy Serial == USART0 behaviour instead. + */ +#if defined(USB0) && !defined(HAVE_NO_USB_SERIAL_REDIRECT) + #ifndef Serial + #define Serial USBSerial /* Serial = native USB CDC */ + #endif + #ifndef SERIAL_PORT_MONITOR + #define SERIAL_PORT_MONITOR Serial /* Serial Monitor -> USB */ + #endif + #ifndef SERIAL_PORT_USBVIRTUAL + #define SERIAL_PORT_USBVIRTUAL Serial /* native USB virtual serial */ + #endif +#endif + #endif diff --git a/megaavr/variants/32pin-duseries/pins_arduino.h b/megaavr/variants/32pin-duseries/pins_arduino.h index 4a3f8616..fc5225f5 100644 --- a/megaavr/variants/32pin-duseries/pins_arduino.h +++ b/megaavr/variants/32pin-duseries/pins_arduino.h @@ -166,6 +166,7 @@ Include guard and include basic libraries. We are normally including this inside #define HWSERIAL0_MUX_PINSWAP_2 (0x02 /* PORTMUX_USART0_ALT2_gc */) #define HWSERIAL0_MUX_PINSWAP_3 (0x03 /* PORTMUX_USART0_ALT3_gc */) #define HWSERIAL0_MUX_PINSWAP_NONE (0x05) +#define HWSERIAL0_MUX_DEFAULT (3) /* DU default: USART0 ALT3 (PD4/PD5); row index of PINSWAP_3 */ #define PIN_HWSERIAL0_TX (PIN_PA0) #define PIN_HWSERIAL0_RX (PIN_PA1) #define PIN_HWSERIAL0_XCK (PIN_PA2) @@ -185,16 +186,22 @@ Include guard and include basic libraries. We are normally including this inside // USART1 #define HWSERIAL1_MUX (0x00 /* PORTMUX_USART1_DEFAULT_gc */) +#define HWSERIAL1_MUX_PINSWAP_1 (0x01 << 3 /* PORTMUX_USART1_ALT1_gc - absent on DU (PC4/PC5 not present); placeholder so the PINSWAP_2 row is built into _usart1_pins[] */) #define HWSERIAL1_MUX_PINSWAP_2 (0x02 << 3 /* PORTMUX_USART1_ALT2_gc */) #define HWSERIAL1_MUX_PINSWAP_NONE (0x03 << 3) #define PIN_HWSERIAL1_TX (NOT_A_PIN) #define PIN_HWSERIAL1_RX (NOT_A_PIN) #define PIN_HWSERIAL1_XCK (NOT_A_PIN) #define PIN_HWSERIAL1_XDIR (NOT_A_PIN) +#define PIN_HWSERIAL1_TX_PINSWAP_1 (NOT_A_PIN) /* ALT1 placeholder (absent on DU) */ +#define PIN_HWSERIAL1_RX_PINSWAP_1 (NOT_A_PIN) +#define PIN_HWSERIAL1_XCK_PINSWAP_1 (NOT_A_PIN) +#define PIN_HWSERIAL1_XDIR_PINSWAP_1 (NOT_A_PIN) #define PIN_HWSERIAL1_TX_PINSWAP_2 (PIN_PD6) #define PIN_HWSERIAL1_RX_PINSWAP_2 (PIN_PD7) #define PIN_HWSERIAL1_XCK_PINSWAP_2 (NOT_A_PIN) #define PIN_HWSERIAL1_XDIR_PINSWAP_2 (NOT_A_PIN) +#define HWSERIAL1_MUX_DEFAULT (2) /* DU default: USART1 ALT2 (PD6/PD7); row index of PINSWAP_2 (USART1 has no usable DEFAULT position on DU) */ /*## # # ### # ### ### #### ### # # ### # # ## # # # # # # # # # # ## # # @@ -448,4 +455,66 @@ static const uint8_t A31 = PIN_A31; }; #endif + +/* ================================================================= + * USB identity (AVR DU = USB-native part) + * ================================================================= + * Every AVR DU has the USB0 peripheral, so - like the ATmega32U4 on + * the Leonardo/Micro - the DU is treated as a USB-native board. USBCON + * tells Arduino's HID / Keyboard / Mouse / Joystick libraries this board + * has USB. USB_VID / USB_PID are the board identity and may be overridden + * from boards.txt (all #ifndef) - e.g. a ProMicro clone, or the USB-CDC + * bootloader board (0xDA33). Defaults: pid.codes test VID/PID 0x1209:0xDA32. + */ +#ifndef USBCON + #define USBCON +#endif +#ifndef USB_VID + #define USB_VID 0x1209 +#endif +#ifndef USB_PID + #define USB_PID 0x0002 +#endif +#ifndef USB_MANUFACTURER + #define USB_MANUFACTURER "DxCore" +#endif +#ifndef USB_PRODUCT + #define USB_PRODUCT "AVRDU" +#endif + +/* ================================================================= + * Serial -> native USB CDC (Arduino Leonardo convention) + * ================================================================= + * DxCore's cores/dxcore/Arduino.h later does: + * #ifndef Serial + * #define Serial Serial0 + * #endif + * Pre-defining Serial here points it at the on-chip USB CDC instance, + * USBSerial (class USBSerial_, declared in core USBSerial.h, guarded by + * USB0) - exactly as the ATmega32U4 cores make Serial the native USB port + * on the Leonardo/Micro. USART0 stays reachable as Serial0 and USART1 as + * Serial1 on every DU board, so the hardware UART port names never shift + * between one DU board and another. + * + * Whether the USB CDC is actually started at boot is decided by the core + * (usb_auto_init()), not here: the USB-CDC bootloader board auto-starts it + * (upload port + 1200bps touch), while a plain no-bootloader board leaves + * it inactive until the sketch calls Serial.begin() - so on such a board + * Serial simply produces no output unless USB is opened. + * + * Define HAVE_NO_USB_SERIAL_REDIRECT (e.g. from boards.txt) to keep the + * legacy Serial == USART0 behaviour instead. + */ +#if defined(USB0) && !defined(HAVE_NO_USB_SERIAL_REDIRECT) + #ifndef Serial + #define Serial USBSerial /* Serial = native USB CDC */ + #endif + #ifndef SERIAL_PORT_MONITOR + #define SERIAL_PORT_MONITOR Serial /* Serial Monitor -> USB */ + #endif + #ifndef SERIAL_PORT_USBVIRTUAL + #define SERIAL_PORT_USBVIRTUAL Serial /* native USB virtual serial */ + #endif +#endif + #endif