From 6af8f36ff5bba725aa56d636cfdfb8886c54e266 Mon Sep 17 00:00:00 2001 From: amarotica Date: Thu, 16 Apr 2026 07:59:56 -0700 Subject: [PATCH 1/3] Add AVR16DD20/AVR16DD32 boards and fix DxCore DD fuses --- boards/AVR16DD20.json | 24 ++++++++++++++++++++++++ boards/AVR16DD32.json | 24 ++++++++++++++++++++++++ builder/fuses.py | 30 ++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+) create mode 100644 boards/AVR16DD20.json create mode 100644 boards/AVR16DD32.json diff --git a/boards/AVR16DD20.json b/boards/AVR16DD20.json new file mode 100644 index 0000000..7b5af32 --- /dev/null +++ b/boards/AVR16DD20.json @@ -0,0 +1,24 @@ +{ + "build": { + "core": "dxcore", + "extra_flags": "-DARDUINO_AVR_AVR16DD20 -DARDUINO_avrdd", + "f_cpu": "24000000L", + "mcu": "avr16dd20", + "variant": "20pin-ddseries" + }, + "hardware": { + "oscillator": "internal" + }, + "frameworks": [ + "arduino" + ], + "name": "AVR16DD20", + "upload": { + "maximum_ram_size": 2048, + "maximum_size": 16384, + "protocol": "jtag2updi", + "speed": 115200 + }, + "url": "https://www.microchip.com/wwwproducts/en/AVR16DD20", + "vendor": "Microchip" +} diff --git a/boards/AVR16DD32.json b/boards/AVR16DD32.json new file mode 100644 index 0000000..a606def --- /dev/null +++ b/boards/AVR16DD32.json @@ -0,0 +1,24 @@ +{ + "build": { + "core": "dxcore", + "extra_flags": "-DARDUINO_AVR_AVR16DD32 -DARDUINO_avrdd", + "f_cpu": "24000000L", + "mcu": "avr16dd32", + "variant": "32pin-ddseries" + }, + "hardware": { + "oscillator": "internal" + }, + "frameworks": [ + "arduino" + ], + "name": "AVR16DD32", + "upload": { + "maximum_ram_size": 2048, + "maximum_size": 16384, + "protocol": "jtag2updi", + "speed": 115200 + }, + "url": "https://www.microchip.com/wwwproducts/en/AVR16DD32", + "vendor": "Microchip" +} diff --git a/builder/fuses.py b/builder/fuses.py index edda251..d8cb886 100644 --- a/builder/fuses.py +++ b/builder/fuses.py @@ -10,6 +10,10 @@ def get_wdtcfg_fuse(): return 0x00 +def is_dxcore_dd_target(): + return core == "dxcore" and "dd" in board.get("build.mcu").lower() + + def get_bodcfg_fuse(bod): if core == "dxcore": if bod == "2.85v": @@ -45,11 +49,23 @@ def get_osccfg_fuse(f_cpu, oscillator): def get_tcd0cfg_fuse(): + if core == "dxcore": + # DxCore leaves TCD0CFG erased by default. + return 0xFF return 0x00 def get_syscfg0_fuse(eesave, pin, uart): eesave_bit = 1 if eesave == "yes" else 0 + if is_dxcore_dd_target(): + if pin == "gpio" and uart == "no_bootloader": + resetpin_bits = 0b01 + else: + # PlatformIO currently exposes only reset/gpio, so map these to the + # safe AVR-DD defaults: RESET+UPDI when reset is selected or a + # bootloader is in use. + resetpin_bits = 0b11 + return 0xC0 | (resetpin_bits << 3) | eesave_bit if core in ("MegaCoreX", "dxcore"): if pin == "gpio": if uart == "no_bootloader": @@ -75,6 +91,20 @@ def get_syscfg0_fuse(eesave, pin, uart): # Handle AVR-DB's differently since these has MVIO pins def get_syscfg1_fuse(mvio): + if is_dxcore_dd_target(): + startuptime = str(board.get("hardware.startuptime", "8")).lower().replace("ms", "") + sut_bits = { + "0": 0b000, + "1": 0b001, + "2": 0b010, + "4": 0b011, + "8": 0b100, + "16": 0b101, + "32": 0b110, + "64": 0b111, + }.get(startuptime, 0b100) + mvio_bits = 0b01 if mvio == "yes" else 0b10 + return (mvio_bits << 3) | sut_bits if core == "dxcore" and ("db" in board.get("build.mcu").lower()): if(mvio == "yes"): return 0x0E From 6e6e74d531095c4c8a32b88d662c6e6620279a36 Mon Sep 17 00:00:00 2001 From: amarotica Date: Thu, 16 Apr 2026 08:20:35 -0700 Subject: [PATCH 2/3] Align DxCore DD fuse writes with safe defaults --- builder/fuses.py | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/builder/fuses.py b/builder/fuses.py index d8cb886..d31fe10 100644 --- a/builder/fuses.py +++ b/builder/fuses.py @@ -50,8 +50,8 @@ def get_osccfg_fuse(f_cpu, oscillator): def get_tcd0cfg_fuse(): if core == "dxcore": - # DxCore leaves TCD0CFG erased by default. - return 0xFF + # DxCore's own bootloader/fuse flows do not write TCD0CFG. + return None return 0x00 @@ -140,7 +140,8 @@ def get_lockbit_fuse(): if core in ("arduino", "MegaCoreX", "megatinycore"): return 0xC5 elif core == "dxcore": - return 0x5CC5C55C + # DxCore's bootloader/fuse flows do not write lockbits by default. + return None else: sys.stderr.write("Error: Couldn't calculate lockbit for %s\n" % target) env.Exit(1) @@ -159,6 +160,12 @@ def print_fuses_info(fuse_values, fuse_names, lock_fuse): print("-------------------------\n") +def format_fuse_value(value): + if value is None: + return "" + return "0x%.2X" % value + + def calculate_fuses(board_config, predefined_fuses): f_cpu = board_config.get("build.f_cpu", "16000000L").upper() oscillator = board_config.get("hardware.oscillator", "internal").lower() @@ -189,15 +196,15 @@ def calculate_fuses(board_config, predefined_fuses): print("-------------------------") return ( - predefined_fuses[0] or "0x%.2X" % get_wdtcfg_fuse(), - predefined_fuses[1] or "0x%.2X" % get_bodcfg_fuse(bod), - predefined_fuses[2] or "0x%.2X" % get_osccfg_fuse(f_cpu, oscillator), + predefined_fuses[0] or format_fuse_value(get_wdtcfg_fuse()), + predefined_fuses[1] or format_fuse_value(get_bodcfg_fuse(bod)), + predefined_fuses[2] or format_fuse_value(get_osccfg_fuse(f_cpu, oscillator)), "", # reserved - predefined_fuses[4] or "0x%.2X" % get_tcd0cfg_fuse(), - predefined_fuses[5] or "0x%.2X" % get_syscfg0_fuse(eesave, pin, uart), - predefined_fuses[6] or "0x%.2X" % get_syscfg1_fuse(mvio), - predefined_fuses[7] or "0x%.2X" % get_append_fuse(), - predefined_fuses[8] or "0x%.2X" % get_bootend_fuse(uart), + predefined_fuses[4] or format_fuse_value(get_tcd0cfg_fuse()), + predefined_fuses[5] or format_fuse_value(get_syscfg0_fuse(eesave, pin, uart)), + predefined_fuses[6] or format_fuse_value(get_syscfg1_fuse(mvio)), + predefined_fuses[7] or format_fuse_value(get_append_fuse()), + predefined_fuses[8] or format_fuse_value(get_bootend_fuse(uart)), ) @@ -241,7 +248,7 @@ def calculate_fuses(board_config, predefined_fuses): env.Exit(1) fuse_values = [board_fuses.get(fname, "") for fname in fuse_names] -lock_fuse = board_fuses.get("lockbit", "0x%.2X" % get_lockbit_fuse()) +lock_fuse = board_fuses.get("lockbit", format_fuse_value(get_lockbit_fuse())) if core in ("MegaCoreX", "megatinycore", "dxcore"): fuse_values = calculate_fuses(board, fuse_values) From fba88b4f2b5725c10e776c915c35fc2ad2e75af3 Mon Sep 17 00:00:00 2001 From: amarotica Date: Thu, 16 Apr 2026 08:23:44 -0700 Subject: [PATCH 3/3] Compose DxCore BODCFG from level and mode --- builder/fuses.py | 63 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 51 insertions(+), 12 deletions(-) diff --git a/builder/fuses.py b/builder/fuses.py index d31fe10..b09b126 100644 --- a/builder/fuses.py +++ b/builder/fuses.py @@ -14,18 +14,54 @@ def is_dxcore_dd_target(): return core == "dxcore" and "dd" in board.get("build.mcu").lower() -def get_bodcfg_fuse(bod): +def get_dxcore_bodcfg_fuse(bod, bodmode): + bod = bod.lower() + bodmode = bodmode.lower() + + bodlev_bits = { + "1.9v": 0b000, + "1v9": 0b000, + "2.45v": 0b001, + "2v45": 0b001, + "2.7v": 0b010, + "2.70v": 0b010, + "2v7": 0b010, + "2v70": 0b010, + "2.85v": 0b011, + "2v85": 0b011, + } + + bodmode_bits = { + "disabled": None, + "enabled": 0b00101, + "ensampfast": 0b00110, + "ensampslow": 0b10110, + "samplefast": 0b01010, + "sampledfast": 0b01010, + "sampleslow": 0b11010, + "sampledslow": 0b11010, + "sampdisfast": 0b01000, + "sampdisslow": 0b11000, + "endisholdwake": 0b01100, + } + + if bodmode not in bodmode_bits: + sys.stderr.write("Error: Unsupported DxCore BOD mode '%s' for %s\n" % (bodmode, target)) + env.Exit(1) + + if bodmode_bits[bodmode] is None: + return 0x00 + + if bod not in bodlev_bits: + sys.stderr.write("Error: Unsupported DxCore BOD level '%s' for %s\n" % (bod, target)) + env.Exit(1) + + return (bodlev_bits[bod] << 5) | bodmode_bits[bodmode] + + +def get_bodcfg_fuse(bod, bodmode="disabled"): if core == "dxcore": - if bod == "2.85v": - return 0x74 - elif bod == "2.7v": - return 0x54 - elif bod == "2.45v": - return 0x34 - elif bod == "1.9v": - return 0x14 - else: # bod disabled - return 0x00 + return get_dxcore_bodcfg_fuse(bod, bodmode) elif core in ("MegaCoreX", "megatinycore"): if bod == "4.3v": return 0xF4 @@ -170,6 +206,7 @@ def calculate_fuses(board_config, predefined_fuses): f_cpu = board_config.get("build.f_cpu", "16000000L").upper() oscillator = board_config.get("hardware.oscillator", "internal").lower() bod = board_config.get("hardware.bod", "2.6v").lower() + bodmode = board_config.get("hardware.bodmode", "disabled").lower() uart = board_config.get("hardware.uart", "no_bootloader").lower() eesave = board_config.get("hardware.eesave", "yes").lower() mvio = board_config.get("hardware.mvio_enable", "no").lower() @@ -188,6 +225,8 @@ def calculate_fuses(board_config, predefined_fuses): print("Clock speed = %s" % f_cpu) print("Oscillator = %s" % oscillator) print("BOD level = %s" % bod) + if core == "dxcore": + print("BOD mode = %s" % bodmode) print("Save EEPROM = %s" % eesave) if core == "dxcore" and "db" in board.get("build.mcu").lower(): print("MVIO enable = %s" % mvio) @@ -197,7 +236,7 @@ def calculate_fuses(board_config, predefined_fuses): return ( predefined_fuses[0] or format_fuse_value(get_wdtcfg_fuse()), - predefined_fuses[1] or format_fuse_value(get_bodcfg_fuse(bod)), + predefined_fuses[1] or format_fuse_value(get_bodcfg_fuse(bod, bodmode)), predefined_fuses[2] or format_fuse_value(get_osccfg_fuse(f_cpu, oscillator)), "", # reserved predefined_fuses[4] or format_fuse_value(get_tcd0cfg_fuse()),