diff --git a/fw/src/kfd-avr/.gitignore b/fw/src/kfd-avr/.gitignore
new file mode 100644
index 0000000..d163863
--- /dev/null
+++ b/fw/src/kfd-avr/.gitignore
@@ -0,0 +1 @@
+build/
\ No newline at end of file
diff --git a/fw/src/kfd-avr/.vscode/arduino.json b/fw/src/kfd-avr/.vscode/arduino.json
new file mode 100644
index 0000000..5b45047
--- /dev/null
+++ b/fw/src/kfd-avr/.vscode/arduino.json
@@ -0,0 +1,6 @@
+{
+ "sketch": "kfd-avr.ino",
+ "board": "arduino:avr:uno",
+ "port": "COM12",
+ "output": "build"
+}
\ No newline at end of file
diff --git a/fw/src/kfd-avr/.vscode/c_cpp_properties.json b/fw/src/kfd-avr/.vscode/c_cpp_properties.json
new file mode 100644
index 0000000..24d2068
--- /dev/null
+++ b/fw/src/kfd-avr/.vscode/c_cpp_properties.json
@@ -0,0 +1,538 @@
+{
+ "version": 4,
+ "configurations": [
+ {
+ "name": "Arduino",
+ "compilerPath": "C:\\Program Files (x86)\\Arduino\\hardware\\tools\\avr\\bin\\avr-g++",
+ "compilerArgs": [
+ "-w",
+ "-std=gnu++11",
+ "-fpermissive",
+ "-fno-exceptions",
+ "-ffunction-sections",
+ "-fdata-sections",
+ "-fno-threadsafe-statics",
+ "-Wno-error=narrowing"
+ ],
+ "intelliSenseMode": "gcc-x64",
+ "includePath": [
+ "C:\\Program Files (x86)\\Arduino\\hardware\\arduino\\avr\\cores\\arduino",
+ "C:\\Program Files (x86)\\Arduino\\hardware\\arduino\\avr\\variants\\standard",
+ "C:\\Program Files (x86)\\Arduino\\hardware\\arduino\\avr\\libraries\\EEPROM\\src",
+ "c:\\program files (x86)\\arduino\\hardware\\tools\\avr\\lib\\gcc\\avr\\7.3.0\\include",
+ "c:\\program files (x86)\\arduino\\hardware\\tools\\avr\\lib\\gcc\\avr\\7.3.0\\include-fixed",
+ "c:\\program files (x86)\\arduino\\hardware\\tools\\avr\\avr\\include"
+ ],
+ "forcedInclude": [
+ "C:\\Program Files (x86)\\Arduino\\hardware\\arduino\\avr\\cores\\arduino\\Arduino.h"
+ ],
+ "cStandard": "c11",
+ "cppStandard": "c++11",
+ "defines": [
+ "F_CPU=16000000L",
+ "ARDUINO=10813",
+ "ARDUINO_AVR_UNO",
+ "ARDUINO_ARCH_AVR",
+ "__DBL_MIN_EXP__=(-125)",
+ "__HQ_FBIT__=15",
+ "__cpp_attributes=200809",
+ "__UINT_LEAST16_MAX__=0xffffU",
+ "__ATOMIC_ACQUIRE=2",
+ "__SFRACT_IBIT__=0",
+ "__FLT_MIN__=1.17549435e-38F",
+ "__GCC_IEC_559_COMPLEX=0",
+ "__BUILTIN_AVR_SLEEP=1",
+ "__BUILTIN_AVR_COUNTLSULLK=1",
+ "__cpp_aggregate_nsdmi=201304",
+ "__BUILTIN_AVR_COUNTLSULLR=1",
+ "__UFRACT_MAX__=0XFFFFP-16UR",
+ "__UINT_LEAST8_TYPE__=unsigned char",
+ "__DQ_FBIT__=63",
+ "__INTMAX_C(c)=c ## LL",
+ "__ULFRACT_FBIT__=32",
+ "__SACCUM_EPSILON__=0x1P-7HK",
+ "__CHAR_BIT__=8",
+ "__USQ_IBIT__=0",
+ "__UINT8_MAX__=0xff",
+ "__ACCUM_FBIT__=15",
+ "__WINT_MAX__=0x7fff",
+ "__FLT32_MIN_EXP__=(-125)",
+ "__cpp_static_assert=200410",
+ "__USFRACT_FBIT__=8",
+ "__ORDER_LITTLE_ENDIAN__=1234",
+ "__SIZE_MAX__=0xffffU",
+ "__WCHAR_MAX__=0x7fff",
+ "__LACCUM_IBIT__=32",
+ "__DBL_DENORM_MIN__=double(1.40129846e-45L)",
+ "__GCC_ATOMIC_CHAR_LOCK_FREE=1",
+ "__GCC_IEC_559=0",
+ "__FLT_EVAL_METHOD__=0",
+ "__BUILTIN_AVR_LLKBITS=1",
+ "__cpp_binary_literals=201304",
+ "__LLACCUM_MAX__=0X7FFFFFFFFFFFFFFFP-47LLK",
+ "__GCC_ATOMIC_CHAR32_T_LOCK_FREE=1",
+ "__BUILTIN_AVR_HKBITS=1",
+ "__BUILTIN_AVR_BITSLLK=1",
+ "__FRACT_FBIT__=15",
+ "__BUILTIN_AVR_BITSLLR=1",
+ "__cpp_variadic_templates=200704",
+ "__UINT_FAST64_MAX__=0xffffffffffffffffULL",
+ "__SIG_ATOMIC_TYPE__=char",
+ "__BUILTIN_AVR_UHKBITS=1",
+ "__UACCUM_FBIT__=16",
+ "__DBL_MIN_10_EXP__=(-37)",
+ "__FINITE_MATH_ONLY__=0",
+ "__cpp_variable_templates=201304",
+ "__LFRACT_IBIT__=0",
+ "__GNUC_PATCHLEVEL__=0",
+ "__FLT32_HAS_DENORM__=1",
+ "__LFRACT_MAX__=0X7FFFFFFFP-31LR",
+ "__UINT_FAST8_MAX__=0xff",
+ "__has_include(STR)=__has_include__(STR)",
+ "__DEC64_MAX_EXP__=385",
+ "__INT8_C(c)=c",
+ "__INT_LEAST8_WIDTH__=8",
+ "__UINT_LEAST64_MAX__=0xffffffffffffffffULL",
+ "__SA_FBIT__=15",
+ "__SHRT_MAX__=0x7fff",
+ "__LDBL_MAX__=3.40282347e+38L",
+ "__FRACT_MAX__=0X7FFFP-15R",
+ "__UFRACT_FBIT__=16",
+ "__UFRACT_MIN__=0.0UR",
+ "__UINT_LEAST8_MAX__=0xff",
+ "__GCC_ATOMIC_BOOL_LOCK_FREE=1",
+ "__UINTMAX_TYPE__=long long unsigned int",
+ "__LLFRACT_EPSILON__=0x1P-63LLR",
+ "__BUILTIN_AVR_DELAY_CYCLES=1",
+ "__DEC32_EPSILON__=1E-6DF",
+ "__FLT_EVAL_METHOD_TS_18661_3__=0",
+ "__UINT32_MAX__=0xffffffffUL",
+ "__GXX_EXPERIMENTAL_CXX0X__=1",
+ "__ULFRACT_MAX__=0XFFFFFFFFP-32ULR",
+ "__TA_IBIT__=16",
+ "__LDBL_MAX_EXP__=128",
+ "__WINT_MIN__=(-__WINT_MAX__ - 1)",
+ "__INT_LEAST16_WIDTH__=16",
+ "__ULLFRACT_MIN__=0.0ULLR",
+ "__SCHAR_MAX__=0x7f",
+ "__WCHAR_MIN__=(-__WCHAR_MAX__ - 1)",
+ "__INT64_C(c)=c ## LL",
+ "__DBL_DIG__=6",
+ "__GCC_ATOMIC_POINTER_LOCK_FREE=1",
+ "__AVR_HAVE_SPH__=1",
+ "__LLACCUM_MIN__=(-0X1P15LLK-0X1P15LLK)",
+ "__BUILTIN_AVR_KBITS=1",
+ "__BUILTIN_AVR_ABSK=1",
+ "__BUILTIN_AVR_ABSR=1",
+ "__SIZEOF_INT__=2",
+ "__SIZEOF_POINTER__=2",
+ "__GCC_ATOMIC_CHAR16_T_LOCK_FREE=1",
+ "__USACCUM_IBIT__=8",
+ "__USER_LABEL_PREFIX__",
+ "__STDC_HOSTED__=1",
+ "__LDBL_HAS_INFINITY__=1",
+ "__LFRACT_MIN__=(-0.5LR-0.5LR)",
+ "__HA_IBIT__=8",
+ "__FLT32_DIG__=6",
+ "__TQ_IBIT__=0",
+ "__FLT_EPSILON__=1.19209290e-7F",
+ "__GXX_WEAK__=1",
+ "__SHRT_WIDTH__=16",
+ "__USFRACT_IBIT__=0",
+ "__LDBL_MIN__=1.17549435e-38L",
+ "__FRACT_MIN__=(-0.5R-0.5R)",
+ "__AVR_SFR_OFFSET__=0x20",
+ "__DEC32_MAX__=9.999999E96DF",
+ "__cpp_threadsafe_static_init=200806",
+ "__DA_IBIT__=32",
+ "__INT32_MAX__=0x7fffffffL",
+ "__UQQ_FBIT__=8",
+ "__INT_WIDTH__=16",
+ "__SIZEOF_LONG__=4",
+ "__UACCUM_MAX__=0XFFFFFFFFP-16UK",
+ "__UINT16_C(c)=c ## U",
+ "__PTRDIFF_WIDTH__=16",
+ "__DECIMAL_DIG__=9",
+ "__LFRACT_EPSILON__=0x1P-31LR",
+ "__AVR_2_BYTE_PC__=1",
+ "__ULFRACT_MIN__=0.0ULR",
+ "__INTMAX_WIDTH__=64",
+ "__has_include_next(STR)=__has_include_next__(STR)",
+ "__BUILTIN_AVR_ULLRBITS=1",
+ "__LDBL_HAS_QUIET_NAN__=1",
+ "__ULACCUM_IBIT__=32",
+ "__UACCUM_EPSILON__=0x1P-16UK",
+ "__BUILTIN_AVR_SEI=1",
+ "__GNUC__=7",
+ "__ULLACCUM_MAX__=0XFFFFFFFFFFFFFFFFP-48ULLK",
+ "__cpp_delegating_constructors=200604",
+ "__HQ_IBIT__=0",
+ "__BUILTIN_AVR_SWAP=1",
+ "__FLT_HAS_DENORM__=1",
+ "__SIZEOF_LONG_DOUBLE__=4",
+ "__BIGGEST_ALIGNMENT__=1",
+ "__STDC_UTF_16__=1",
+ "__UINT24_MAX__=16777215UL",
+ "__BUILTIN_AVR_NOP=1",
+ "__GNUC_STDC_INLINE__=1",
+ "__DQ_IBIT__=0",
+ "__FLT32_HAS_INFINITY__=1",
+ "__DBL_MAX__=double(3.40282347e+38L)",
+ "__ULFRACT_IBIT__=0",
+ "__cpp_raw_strings=200710",
+ "__INT_FAST32_MAX__=0x7fffffffL",
+ "__DBL_HAS_INFINITY__=1",
+ "__INT64_MAX__=0x7fffffffffffffffLL",
+ "__ACCUM_IBIT__=16",
+ "__DEC32_MIN_EXP__=(-94)",
+ "__BUILTIN_AVR_UKBITS=1",
+ "__INTPTR_WIDTH__=16",
+ "__BUILTIN_AVR_FMULSU=1",
+ "__LACCUM_MAX__=0X7FFFFFFFFFFFFFFFP-31LK",
+ "__INT_FAST16_TYPE__=int",
+ "__LDBL_HAS_DENORM__=1",
+ "__BUILTIN_AVR_BITSK=1",
+ "__BUILTIN_AVR_BITSR=1",
+ "__cplusplus=201402L",
+ "__cpp_ref_qualifiers=200710",
+ "__DEC128_MAX__=9.999999999999999999999999999999999E6144DL",
+ "__INT_LEAST32_MAX__=0x7fffffffL",
+ "__USING_SJLJ_EXCEPTIONS__=1",
+ "__DEC32_MIN__=1E-95DF",
+ "__ACCUM_MAX__=0X7FFFFFFFP-15K",
+ "__DEPRECATED=1",
+ "__cpp_rvalue_references=200610",
+ "__DBL_MAX_EXP__=128",
+ "__USACCUM_EPSILON__=0x1P-8UHK",
+ "__WCHAR_WIDTH__=16",
+ "__FLT32_MAX__=3.40282347e+38F32",
+ "__DEC128_EPSILON__=1E-33DL",
+ "__SFRACT_MAX__=0X7FP-7HR",
+ "__FRACT_IBIT__=0",
+ "__PTRDIFF_MAX__=0x7fff",
+ "__UACCUM_MIN__=0.0UK",
+ "__UACCUM_IBIT__=16",
+ "__BUILTIN_AVR_NOPS=1",
+ "__BUILTIN_AVR_WDR=1",
+ "__FLT32_HAS_QUIET_NAN__=1",
+ "__GNUG__=7",
+ "__LONG_LONG_MAX__=0x7fffffffffffffffLL",
+ "__SIZEOF_SIZE_T__=2",
+ "__ULACCUM_MAX__=0XFFFFFFFFFFFFFFFFP-32ULK",
+ "__cpp_rvalue_reference=200610",
+ "__cpp_nsdmi=200809",
+ "__SIZEOF_WINT_T__=2",
+ "__LONG_LONG_WIDTH__=64",
+ "__cpp_initializer_lists=200806",
+ "__FLT32_MAX_EXP__=128",
+ "__SA_IBIT__=16",
+ "__ULLACCUM_MIN__=0.0ULLK",
+ "__BUILTIN_AVR_ROUNDUHK=1",
+ "__BUILTIN_AVR_ROUNDUHR=1",
+ "__cpp_hex_float=201603",
+ "__GXX_ABI_VERSION=1011",
+ "__INT24_MAX__=8388607L",
+ "__UTA_FBIT__=48",
+ "__FLT_MIN_EXP__=(-125)",
+ "__USFRACT_MAX__=0XFFP-8UHR",
+ "__UFRACT_IBIT__=0",
+ "__BUILTIN_AVR_ROUNDFX=1",
+ "__BUILTIN_AVR_ROUNDULK=1",
+ "__BUILTIN_AVR_ROUNDULR=1",
+ "__cpp_lambdas=200907",
+ "__BUILTIN_AVR_COUNTLSLLK=1",
+ "__BUILTIN_AVR_COUNTLSLLR=1",
+ "__BUILTIN_AVR_ROUNDHK=1",
+ "__INT_FAST64_TYPE__=long long int",
+ "__BUILTIN_AVR_ROUNDHR=1",
+ "__DBL_MIN__=double(1.17549435e-38L)",
+ "__BUILTIN_AVR_COUNTLSK=1",
+ "__BUILTIN_AVR_ROUNDLK=1",
+ "__BUILTIN_AVR_COUNTLSR=1",
+ "__BUILTIN_AVR_ROUNDLR=1",
+ "__LACCUM_MIN__=(-0X1P31LK-0X1P31LK)",
+ "__ULLACCUM_FBIT__=48",
+ "__BUILTIN_AVR_LKBITS=1",
+ "__ULLFRACT_EPSILON__=0x1P-64ULLR",
+ "__DEC128_MIN__=1E-6143DL",
+ "__REGISTER_PREFIX__",
+ "__UINT16_MAX__=0xffffU",
+ "__DBL_HAS_DENORM__=1",
+ "__BUILTIN_AVR_ULKBITS=1",
+ "__ACCUM_MIN__=(-0X1P15K-0X1P15K)",
+ "__AVR_ARCH__=2",
+ "__SQ_IBIT__=0",
+ "__FLT32_MIN__=1.17549435e-38F32",
+ "__UINT8_TYPE__=unsigned char",
+ "__BUILTIN_AVR_ROUNDUK=1",
+ "__BUILTIN_AVR_ROUNDUR=1",
+ "__UHA_FBIT__=8",
+ "__NO_INLINE__=1",
+ "__SFRACT_MIN__=(-0.5HR-0.5HR)",
+ "__UTQ_FBIT__=128",
+ "__FLT_MANT_DIG__=24",
+ "__LDBL_DECIMAL_DIG__=9",
+ "__VERSION__=\"7.3.0\"",
+ "__UINT64_C(c)=c ## ULL",
+ "__ULLFRACT_FBIT__=64",
+ "__cpp_unicode_characters=200704",
+ "__FRACT_EPSILON__=0x1P-15R",
+ "__ULACCUM_MIN__=0.0ULK",
+ "__UDA_FBIT__=32",
+ "__cpp_decltype_auto=201304",
+ "__LLACCUM_EPSILON__=0x1P-47LLK",
+ "__GCC_ATOMIC_INT_LOCK_FREE=1",
+ "__FLT32_MANT_DIG__=24",
+ "__BUILTIN_AVR_BITSUHK=1",
+ "__BUILTIN_AVR_BITSUHR=1",
+ "__FLOAT_WORD_ORDER__=__ORDER_LITTLE_ENDIAN__",
+ "__USFRACT_MIN__=0.0UHR",
+ "__BUILTIN_AVR_BITSULK=1",
+ "__ULLACCUM_IBIT__=16",
+ "__BUILTIN_AVR_BITSULR=1",
+ "__UQQ_IBIT__=0",
+ "__BUILTIN_AVR_LLRBITS=1",
+ "__SCHAR_WIDTH__=8",
+ "__BUILTIN_AVR_BITSULLK=1",
+ "__BUILTIN_AVR_BITSULLR=1",
+ "__INT32_C(c)=c ## L",
+ "__DEC64_EPSILON__=1E-15DD",
+ "__ORDER_PDP_ENDIAN__=3412",
+ "__DEC128_MIN_EXP__=(-6142)",
+ "__UHQ_FBIT__=16",
+ "__LLACCUM_FBIT__=47",
+ "__FLT32_MAX_10_EXP__=38",
+ "__BUILTIN_AVR_ROUNDULLK=1",
+ "__BUILTIN_AVR_ROUNDULLR=1",
+ "__INT_FAST32_TYPE__=long int",
+ "__BUILTIN_AVR_HRBITS=1",
+ "__UINT_LEAST16_TYPE__=unsigned int",
+ "__BUILTIN_AVR_UHRBITS=1",
+ "__INT16_MAX__=0x7fff",
+ "__SIZE_TYPE__=unsigned int",
+ "__UINT64_MAX__=0xffffffffffffffffULL",
+ "__UDQ_FBIT__=64",
+ "__INT8_TYPE__=signed char",
+ "__cpp_digit_separators=201309",
+ "__ELF__=1",
+ "__ULFRACT_EPSILON__=0x1P-32ULR",
+ "__LLFRACT_FBIT__=63",
+ "__FLT_RADIX__=2",
+ "__INT_LEAST16_TYPE__=int",
+ "__BUILTIN_AVR_ABSFX=1",
+ "__LDBL_EPSILON__=1.19209290e-7L",
+ "__UINTMAX_C(c)=c ## ULL",
+ "__INT24_MIN__=(-__INT24_MAX__-1)",
+ "__SACCUM_MAX__=0X7FFFP-7HK",
+ "__BUILTIN_AVR_ABSHR=1",
+ "__SIG_ATOMIC_MAX__=0x7f",
+ "__GCC_ATOMIC_WCHAR_T_LOCK_FREE=1",
+ "__cpp_sized_deallocation=201309",
+ "__SIZEOF_PTRDIFF_T__=2",
+ "__AVR=1",
+ "__BUILTIN_AVR_ABSLK=1",
+ "__BUILTIN_AVR_ABSLR=1",
+ "__LACCUM_EPSILON__=0x1P-31LK",
+ "__DEC32_SUBNORMAL_MIN__=0.000001E-95DF",
+ "__INT_FAST16_MAX__=0x7fff",
+ "__UINT_FAST32_MAX__=0xffffffffUL",
+ "__UINT_LEAST64_TYPE__=long long unsigned int",
+ "__USACCUM_MAX__=0XFFFFP-8UHK",
+ "__SFRACT_EPSILON__=0x1P-7HR",
+ "__FLT_HAS_QUIET_NAN__=1",
+ "__FLT_MAX_10_EXP__=38",
+ "__LONG_MAX__=0x7fffffffL",
+ "__DEC128_SUBNORMAL_MIN__=0.000000000000000000000000000000001E-6143DL",
+ "__FLT_HAS_INFINITY__=1",
+ "__cpp_unicode_literals=200710",
+ "__USA_FBIT__=16",
+ "__UINT_FAST16_TYPE__=unsigned int",
+ "__DEC64_MAX__=9.999999999999999E384DD",
+ "__INT_FAST32_WIDTH__=32",
+ "__BUILTIN_AVR_RBITS=1",
+ "__CHAR16_TYPE__=unsigned int",
+ "__PRAGMA_REDEFINE_EXTNAME=1",
+ "__SIZE_WIDTH__=16",
+ "__INT_LEAST16_MAX__=0x7fff",
+ "__DEC64_MANT_DIG__=16",
+ "__UINT_LEAST32_MAX__=0xffffffffUL",
+ "__SACCUM_FBIT__=7",
+ "__FLT32_DENORM_MIN__=1.40129846e-45F32",
+ "__GCC_ATOMIC_LONG_LOCK_FREE=1",
+ "__SIG_ATOMIC_WIDTH__=8",
+ "__INT_LEAST64_TYPE__=long long int",
+ "__INT16_TYPE__=int",
+ "__INT_LEAST8_TYPE__=signed char",
+ "__SQ_FBIT__=31",
+ "__DEC32_MAX_EXP__=97",
+ "__INT_FAST8_MAX__=0x7f",
+ "__INTPTR_MAX__=0x7fff",
+ "__QQ_FBIT__=7",
+ "__cpp_range_based_for=200907",
+ "__UTA_IBIT__=16",
+ "__AVR_ERRATA_SKIP__=1",
+ "__FLT32_MIN_10_EXP__=(-37)",
+ "__LDBL_MANT_DIG__=24",
+ "__SFRACT_FBIT__=7",
+ "__SACCUM_MIN__=(-0X1P7HK-0X1P7HK)",
+ "__DBL_HAS_QUIET_NAN__=1",
+ "__SIG_ATOMIC_MIN__=(-__SIG_ATOMIC_MAX__ - 1)",
+ "AVR=1",
+ "__BUILTIN_AVR_FMULS=1",
+ "__cpp_return_type_deduction=201304",
+ "__INTPTR_TYPE__=int",
+ "__UINT16_TYPE__=unsigned int",
+ "__WCHAR_TYPE__=int",
+ "__SIZEOF_FLOAT__=4",
+ "__AVR__=1",
+ "__BUILTIN_AVR_INSERT_BITS=1",
+ "__USQ_FBIT__=32",
+ "__UINTPTR_MAX__=0xffffU",
+ "__INT_FAST64_WIDTH__=64",
+ "__DEC64_MIN_EXP__=(-382)",
+ "__cpp_decltype=200707",
+ "__FLT32_DECIMAL_DIG__=9",
+ "__INT_FAST64_MAX__=0x7fffffffffffffffLL",
+ "__GCC_ATOMIC_TEST_AND_SET_TRUEVAL=1",
+ "__FLT_DIG__=6",
+ "__UINT_FAST64_TYPE__=long long unsigned int",
+ "__BUILTIN_AVR_BITSHK=1",
+ "__BUILTIN_AVR_BITSHR=1",
+ "__INT_MAX__=0x7fff",
+ "__LACCUM_FBIT__=31",
+ "__USACCUM_MIN__=0.0UHK",
+ "__UHA_IBIT__=8",
+ "__INT64_TYPE__=long long int",
+ "__BUILTIN_AVR_BITSLK=1",
+ "__BUILTIN_AVR_BITSLR=1",
+ "__FLT_MAX_EXP__=128",
+ "__UTQ_IBIT__=0",
+ "__DBL_MANT_DIG__=24",
+ "__cpp_inheriting_constructors=201511",
+ "__BUILTIN_AVR_ULLKBITS=1",
+ "__INT_LEAST64_MAX__=0x7fffffffffffffffLL",
+ "__DEC64_MIN__=1E-383DD",
+ "__WINT_TYPE__=int",
+ "__UINT_LEAST32_TYPE__=long unsigned int",
+ "__SIZEOF_SHORT__=2",
+ "__ULLFRACT_IBIT__=0",
+ "__LDBL_MIN_EXP__=(-125)",
+ "__UDA_IBIT__=32",
+ "__WINT_WIDTH__=16",
+ "__INT_LEAST8_MAX__=0x7f",
+ "__LFRACT_FBIT__=31",
+ "__LDBL_MAX_10_EXP__=38",
+ "__ATOMIC_RELAXED=0",
+ "__DBL_EPSILON__=double(1.19209290e-7L)",
+ "__BUILTIN_AVR_BITSUK=1",
+ "__BUILTIN_AVR_BITSUR=1",
+ "__UINT8_C(c)=c",
+ "__INT_LEAST32_TYPE__=long int",
+ "__BUILTIN_AVR_URBITS=1",
+ "__SIZEOF_WCHAR_T__=2",
+ "__LLFRACT_MAX__=0X7FFFFFFFFFFFFFFFP-63LLR",
+ "__TQ_FBIT__=127",
+ "__INT_FAST8_TYPE__=signed char",
+ "__ULLACCUM_EPSILON__=0x1P-48ULLK",
+ "__BUILTIN_AVR_ROUNDK=1",
+ "__BUILTIN_AVR_ROUNDR=1",
+ "__UHQ_IBIT__=0",
+ "__LLACCUM_IBIT__=16",
+ "__FLT32_EPSILON__=1.19209290e-7F32",
+ "__DBL_DECIMAL_DIG__=9",
+ "__STDC_UTF_32__=1",
+ "__INT_FAST8_WIDTH__=8",
+ "__DEC_EVAL_METHOD__=2",
+ "__TA_FBIT__=47",
+ "__UDQ_IBIT__=0",
+ "__ORDER_BIG_ENDIAN__=4321",
+ "__cpp_runtime_arrays=198712",
+ "__WITH_AVRLIBC__=1",
+ "__UINT64_TYPE__=long long unsigned int",
+ "__ACCUM_EPSILON__=0x1P-15K",
+ "__UINT32_C(c)=c ## UL",
+ "__BUILTIN_AVR_COUNTLSUHK=1",
+ "__INTMAX_MAX__=0x7fffffffffffffffLL",
+ "__cpp_alias_templates=200704",
+ "__BUILTIN_AVR_COUNTLSUHR=1",
+ "__BYTE_ORDER__=__ORDER_LITTLE_ENDIAN__",
+ "__FLT_DENORM_MIN__=1.40129846e-45F",
+ "__LLFRACT_IBIT__=0",
+ "__INT8_MAX__=0x7f",
+ "__LONG_WIDTH__=32",
+ "__UINT_FAST32_TYPE__=long unsigned int",
+ "__CHAR32_TYPE__=long unsigned int",
+ "__BUILTIN_AVR_COUNTLSULK=1",
+ "__BUILTIN_AVR_COUNTLSULR=1",
+ "__FLT_MAX__=3.40282347e+38F",
+ "__cpp_constexpr=201304",
+ "__USACCUM_FBIT__=8",
+ "__BUILTIN_AVR_COUNTLSFX=1",
+ "__INT32_TYPE__=long int",
+ "__SIZEOF_DOUBLE__=4",
+ "__FLT_MIN_10_EXP__=(-37)",
+ "__UFRACT_EPSILON__=0x1P-16UR",
+ "__INT_LEAST32_WIDTH__=32",
+ "__BUILTIN_AVR_COUNTLSHK=1",
+ "__BUILTIN_AVR_COUNTLSHR=1",
+ "__INTMAX_TYPE__=long long int",
+ "__BUILTIN_AVR_ABSLLK=1",
+ "__BUILTIN_AVR_ABSLLR=1",
+ "__DEC128_MAX_EXP__=6145",
+ "__AVR_HAVE_16BIT_SP__=1",
+ "__ATOMIC_CONSUME=1",
+ "__GNUC_MINOR__=3",
+ "__INT_FAST16_WIDTH__=16",
+ "__UINTMAX_MAX__=0xffffffffffffffffULL",
+ "__DEC32_MANT_DIG__=7",
+ "__HA_FBIT__=7",
+ "__BUILTIN_AVR_COUNTLSLK=1",
+ "__BUILTIN_AVR_COUNTLSLR=1",
+ "__BUILTIN_AVR_CLI=1",
+ "__DBL_MAX_10_EXP__=38",
+ "__LDBL_DENORM_MIN__=1.40129846e-45L",
+ "__INT16_C(c)=c",
+ "__cpp_generic_lambdas=201304",
+ "__STDC__=1",
+ "__PTRDIFF_TYPE__=int",
+ "__LLFRACT_MIN__=(-0.5LLR-0.5LLR)",
+ "__BUILTIN_AVR_LRBITS=1",
+ "__ATOMIC_SEQ_CST=5",
+ "__DA_FBIT__=31",
+ "__UINT32_TYPE__=long unsigned int",
+ "__BUILTIN_AVR_ROUNDLLK=1",
+ "__UINTPTR_TYPE__=unsigned int",
+ "__BUILTIN_AVR_ROUNDLLR=1",
+ "__USA_IBIT__=16",
+ "__BUILTIN_AVR_ULRBITS=1",
+ "__DEC64_SUBNORMAL_MIN__=0.000000000000001E-383DD",
+ "__DEC128_MANT_DIG__=34",
+ "__LDBL_MIN_10_EXP__=(-37)",
+ "__BUILTIN_AVR_COUNTLSUK=1",
+ "__BUILTIN_AVR_COUNTLSUR=1",
+ "__SIZEOF_LONG_LONG__=8",
+ "__ULACCUM_EPSILON__=0x1P-32ULK",
+ "__cpp_user_defined_literals=200809",
+ "__SACCUM_IBIT__=8",
+ "__GCC_ATOMIC_LLONG_LOCK_FREE=1",
+ "__LDBL_DIG__=6",
+ "__FLT_DECIMAL_DIG__=9",
+ "__UINT_FAST16_MAX__=0xffffU",
+ "__GCC_ATOMIC_SHORT_LOCK_FREE=1",
+ "__BUILTIN_AVR_ABSHK=1",
+ "__BUILTIN_AVR_FLASH_SEGMENT=1",
+ "__INT_LEAST64_WIDTH__=64",
+ "__ULLFRACT_MAX__=0XFFFFFFFFFFFFFFFFP-64ULLR",
+ "__UINT_FAST8_TYPE__=unsigned char",
+ "__USFRACT_EPSILON__=0x1P-8UHR",
+ "__ULACCUM_FBIT__=32",
+ "__QQ_IBIT__=0",
+ "__cpp_init_captures=201304",
+ "__ATOMIC_ACQ_REL=4",
+ "__ATOMIC_RELEASE=3",
+ "__BUILTIN_AVR_FMUL=1",
+ "USBCON"
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/fw/src/kfd-avr/ControlOpCodes.h b/fw/src/kfd-avr/ControlOpCodes.h
new file mode 100644
index 0000000..38b00ac
--- /dev/null
+++ b/fw/src/kfd-avr/ControlOpCodes.h
@@ -0,0 +1,94 @@
+/**
+* KFDTool - KFD-AVR
+* GPLv2 Open Source. Use is subject to license terms.
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+*
+* @package KFDTool / KFD-AVR Firmware
+*
+*/
+//
+// Based on code from the KFDtool project. (https://github.com/KFDtool/KFDtool)
+// Licensed under the MIT License (https://opensource.org/licenses/MIT)
+//
+/*
+* Copyright (C) 2021-2022 Nat Moore
+* Copyright (C) 2022 Bryan Biedenkapp N2PLL
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+* and associated documentation files (the "Software"), to deal in the Software without restriction,
+* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+* subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in all copies or substantial
+* portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
+* LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+* NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+* OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+#if !defined(__CONTROL_OPCODES_H__)
+#define __CONTROL_OPCODES_H__
+
+// ---------------------------------------------------------------------------
+// Constants
+// ---------------------------------------------------------------------------
+
+/*
+** Command Opcodes
+*/
+#define CMD_READ 0x11U
+#define CMD_WRITE_INFO 0x12U
+#define CMD_ENTER_BSL_MODE 0x13U
+#define CMD_RESET 0x14U
+#define CMD_SELF_TEST 0x15U
+#define CMD_SEND_KEY_SIG 0x16U
+#define CMD_SEND_BYTE 0x17U
+
+/*
+** Response Opcodes
+*/
+#define RSP_ERROR 0x20U
+#define RSP_READ 0x21U
+#define RSP_WRITE_INFO 0x22U
+#define RSP_ENTER_BSL_MODE 0x23U
+#define RSP_RESET 0x24U
+#define RSP_SELF_TEST 0x25U
+#define RSP_SEND_KEY_SIG 0x26U
+#define RSP_SEND_BYTE 0x27U
+
+/*
+** Broadcast Opcodes
+*/
+#define BCST_RECEIVE_BYTE 0x31U
+
+/*
+** Read Opcodes
+*/
+#define READ_AP_VER 0x01U
+#define READ_FW_VER 0x02U
+#define READ_UNIQUE_ID 0x03U
+#define READ_MODEL_ID 0x04U
+#define READ_HW_REV 0x05U
+#define READ_SER_NUM 0x06U
+
+/*
+** Write Opcodes
+*/
+#define WRITE_MDL_REV 0x01U
+#define WRITE_SER 0x02U
+
+/*
+** Error Opcodes
+*/
+#define ERR_OTHER 0x00U
+#define ERR_INVALID_CMD_LENGTH 0x01U
+#define ERR_INVALID_CMD_OPCODE 0x02U
+#define ERR_INVALID_READ_OPCODE 0x03U
+#define ERR_READ_FAILED 0x04U
+#define ERR_INVALID_WRITE_OPCODE 0x05U
+#define ERR_WRITE_FAILED 0x06U
+
+#endif // __CONTROL_OPCODES_H__
diff --git a/fw/src/kfd-avr/FirmwareMain.cpp b/fw/src/kfd-avr/FirmwareMain.cpp
new file mode 100644
index 0000000..b1d0d74
--- /dev/null
+++ b/fw/src/kfd-avr/FirmwareMain.cpp
@@ -0,0 +1,406 @@
+/**
+* KFDTool - KFD-AVR
+* GPLv2 Open Source. Use is subject to license terms.
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+*
+* @package KFDTool / KFD-AVR Firmware
+*
+*/
+//
+// Based on code from the KFDtool project. (https://github.com/KFDtool/KFDtool)
+// Licensed under the MIT License (https://opensource.org/licenses/MIT)
+//
+/*
+* Copyright (C) 2019-2020 Daniel Dugger
+* Copyright (C) 2021-2022 Nat Moore
+* Copyright (C) 2022 Bryan Biedenkapp N2PLL
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+* and associated documentation files (the "Software"), to deal in the Software without restriction,
+* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+* subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in all copies or substantial
+* portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
+* LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+* NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+* OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+#include "hal.h"
+#include "TwiProtocol.h"
+#include "SerialProtocol.h"
+#include "InfoData.h"
+#include "ControlOpCodes.h"
+#include "Versions.h"
+#include "UID.h"
+
+// ---------------------------------------------------------------------------
+// Globals
+// ---------------------------------------------------------------------------
+
+uint16_t cmdCount;
+uint8_t cmdData[128];
+uint16_t rxReady;
+uint8_t rxTemp;
+
+// ---------------------------------------------------------------------------
+// Global Functions
+// ---------------------------------------------------------------------------
+
+///
+///
+///
+///
+void writeSpTxError(uint8_t error)
+{
+ uint8_t rspData[2U];
+ rspData[0U] = RSP_ERROR;
+ rspData[1U] = error;
+
+ spTxDataWait(rspData, sizeof(rspData));
+}
+
+void setup()
+{
+ halInit();
+ spConnect();
+
+ twiInit();
+
+ halActLedOn();
+}
+
+void loop()
+{
+ cmdCount = spRxData(cmdData);
+
+ // do we have a command?
+ if (cmdCount > 0U) {
+ // which command do we have?
+ switch (cmdData[0U]) {
+ case CMD_READ: // Read Command
+ {
+ if (cmdCount == 2U) {
+ switch (cmdData[1U]) {
+ case READ_AP_VER: // Read Adapter Protocol Version
+ {
+ uint8_t rspData[5U];
+ rspData[0U] = RSP_READ;
+ rspData[1U] = READ_AP_VER;
+ rspData[2U] = VER_AP_MAJOR;
+ rspData[3U] = VER_AP_MINOR;
+ rspData[4U] = VER_AP_PATCH;
+ spTxDataWait(rspData, sizeof(rspData));
+ break;
+ }
+ case READ_FW_VER: // Read Firmware Version
+ {
+ uint8_t rspData[5U];
+ rspData[0U] = RSP_READ;
+ rspData[1U] = READ_FW_VER;
+ rspData[2U] = VER_FW_MAJOR;
+ rspData[3U] = VER_FW_MINOR;
+ rspData[4U] = VER_FW_PATCH;
+ spTxDataWait(rspData, sizeof(rspData));
+ break;
+ }
+ case READ_UNIQUE_ID: // Read Unique ID
+ {
+ uint8_t serial[8U];
+ ::memset(serial, 0x00U, 8U);
+
+ getUID8(&serial[0], &serial[1], &serial[2], &serial[3],
+ &serial[4], &serial[5], &serial[6], &serial[7]);
+
+ uint8_t rspData[12U];
+ rspData[0U] = RSP_READ;
+ rspData[1U] = READ_UNIQUE_ID;
+ rspData[2U] = 0x09U; // id length
+ rspData[3U] = 0x10U; // id source
+ rspData[4U] = serial[0U];
+ rspData[5U] = serial[1U];
+ rspData[6U] = serial[2U];
+ rspData[7U] = serial[3U];
+ rspData[8U] = serial[4U];
+ rspData[9U] = serial[5U];
+ rspData[10U] = serial[6U];
+ rspData[11U] = serial[7U];
+ spTxDataWait(rspData, sizeof(rspData));
+ break;
+ }
+ case READ_MODEL_ID: // Read Model ID
+ {
+ uint8_t hwId;
+ uint16_t status = idReadModelId(&hwId);
+
+ // check if available
+ if (status) {
+ uint8_t rspData[3U];
+ rspData[0U] = RSP_READ;
+ rspData[1U] = READ_MODEL_ID;
+ rspData[2U] = hwId;
+ spTxDataWait(rspData, sizeof(rspData));
+ }
+ else {
+ // no model id available
+ uint8_t rspData[3U];
+ rspData[0U] = RSP_READ;
+ rspData[1U] = READ_MODEL_ID;
+ rspData[2U] = 0x00U;
+ spTxDataWait(rspData, sizeof(rspData));
+ }
+ break;
+ }
+ case READ_HW_REV: // Read Hardware Revision
+ {
+ uint8_t hwRevMaj;
+ uint8_t hwRevMin;
+ uint16_t status = idReadHwRev(&hwRevMaj, &hwRevMin);
+
+ // check if available
+ if (status == 1U) {
+ uint8_t rspData[4U];
+ rspData[0U] = RSP_READ;
+ rspData[1U] = READ_HW_REV;
+ rspData[2U] = hwRevMaj;
+ rspData[3U] = hwRevMin;
+ spTxDataWait(rspData, sizeof(rspData));
+ }
+ else {
+ // no hardware revision available
+ uint8_t rspData[4];
+ rspData[0U] = RSP_READ;
+ rspData[1U] = READ_HW_REV;
+ rspData[2U] = 0x00;
+ rspData[3U] = 0x00;
+ spTxDataWait(rspData, sizeof(rspData));
+ }
+ break;
+ }
+ case READ_SER_NUM:
+ {
+ uint8_t serial[6U];
+ ::memset(serial, 0x00U, 6U);
+
+ uint16_t status = idReadSerNum(&serial[0], &serial[1], &serial[2],
+ &serial[3], &serial[4], &serial[5]);
+
+ // check if available
+ if (status == 1U) {
+ uint8_t rspData[9U];
+ rspData[0U] = RSP_READ;
+ rspData[1U] = READ_SER_NUM;
+ rspData[2U] = 0x06U; // serial length
+ rspData[3U] = serial[0U];
+ rspData[4U] = serial[1U];
+ rspData[5U] = serial[2U];
+ rspData[6U] = serial[3U];
+ rspData[7U] = serial[4U];
+ rspData[8U] = serial[5U];
+ spTxDataWait(rspData, sizeof(rspData));
+ }
+ else {
+ // no serial number available
+ uint8_t rspData[3U];
+ rspData[0U] = RSP_READ;
+ rspData[1U] = READ_SER_NUM;
+ rspData[2U] = 0x00U; // serial length
+ spTxDataWait(rspData, sizeof(rspData));
+ }
+ break;
+ }
+ default:
+ writeSpTxError(ERR_INVALID_READ_OPCODE);
+ break;
+ }
+ }
+ else {
+ // invalid command length
+ writeSpTxError(ERR_INVALID_CMD_LENGTH);
+ }
+ break;
+ }
+ case CMD_WRITE_INFO: // Write Info
+ {
+ if (cmdCount > 1U) {
+ switch (cmdData[1U]) {
+ case WRITE_MDL_REV: // Write Model ID and Hardware Rev
+ {
+ if (cmdCount == 5U) {
+ uint16_t result = idWriteModelIdHwRev(cmdData[2], cmdData[3], cmdData[4]);
+ if (result == 1U) {
+ uint8_t rspData[1U];
+ rspData[0U] = RSP_WRITE_INFO;
+ spTxDataWait(rspData, sizeof(rspData));
+ }
+ else {
+ // write failed
+ writeSpTxError(ERR_WRITE_FAILED);
+ }
+ }
+ else {
+ // invalid command length
+ writeSpTxError(ERR_INVALID_CMD_LENGTH);
+ }
+ break;
+ }
+ case WRITE_SER: // Write Serial Number
+ {
+ if (cmdCount == 8U) {
+ uint16_t result = idWriteSerNum(cmdData[2], cmdData[3], cmdData[4],
+ cmdData[5], cmdData[6], cmdData[7]);
+ if (result == 1U) {
+ uint8_t rspData[1U];
+ rspData[0U] = RSP_WRITE_INFO;
+ spTxDataWait(rspData, sizeof(rspData));
+ }
+ else {
+ // write failed
+ writeSpTxError(ERR_WRITE_FAILED);
+ }
+ }
+ else {
+ // invalid command length
+ writeSpTxError(ERR_INVALID_CMD_LENGTH);
+ }
+ break;
+ }
+ default:
+ writeSpTxError(ERR_INVALID_WRITE_OPCODE);
+ break;
+ }
+ }
+ else {
+ // invalid command length
+ writeSpTxError(ERR_INVALID_CMD_LENGTH);
+ }
+ break;
+ }
+ case CMD_ENTER_BSL_MODE: // Enter BSL Mode
+ {
+ if (cmdCount == 1U) {
+ uint8_t rspData[1U];
+ rspData[0U] = RSP_ENTER_BSL_MODE;
+ spTxDataWait(rspData, sizeof(rspData));
+
+ halDelayMs(1000); // wait 1 second
+ spDisconnect(); // disconnect usb
+ halDelayMs(3000); // wait 3 seconds
+ halActLedOff();
+ halEnterBsl();
+ }
+ else {
+ // invalid command length
+ writeSpTxError(ERR_INVALID_CMD_LENGTH);
+ }
+ break;
+ }
+ case CMD_RESET: // Reset
+ {
+ if (cmdCount == 1U) {
+ uint8_t rspData[1U];
+ rspData[0U] = RSP_RESET;
+ spTxDataWait(rspData, sizeof(rspData));
+
+ halDelayMs(1000); // wait 1 second
+ spDisconnect(); // disconnect usb
+ halDelayMs(3000); // wait 3 seconds
+ halActLedOff();
+ halReset();
+ }
+ else {
+ // invalid command length
+ writeSpTxError(ERR_INVALID_CMD_LENGTH);
+ }
+ break;
+ }
+ case CMD_SELF_TEST: // Self Test
+ {
+ if (cmdCount == 1U) {
+ uint8_t result = twiSelfTest();
+
+ uint8_t rspData[2U];
+ rspData[0U] = RSP_SELF_TEST;
+ rspData[1U] = result;
+ spTxDataWait(rspData, sizeof(rspData));
+ }
+ else {
+ // invalid command length
+ writeSpTxError(ERR_INVALID_CMD_LENGTH);
+ }
+ break;
+ }
+ case CMD_SEND_KEY_SIG: // Send Key Signature
+ {
+ if (cmdCount == 2U) {
+ twiSendKeySig();
+
+ uint8_t rspData[1U];
+ rspData[0U] = RSP_SEND_KEY_SIG;
+ spTxDataWait(rspData, sizeof(rspData));
+ }
+ else {
+ // invalid command length
+ writeSpTxError(ERR_INVALID_CMD_LENGTH);
+ }
+ break;
+ }
+ case CMD_SEND_BYTE:
+ {
+ if (cmdCount == 3U) {
+ twiSendPhyByte(cmdData[2U]);
+
+ uint8_t rspData[1U];
+ rspData[0U] = RSP_SEND_BYTE;
+ spTxDataWait(rspData, sizeof(rspData));
+ }
+ else {
+ // invalid command length
+ writeSpTxError(ERR_INVALID_CMD_LENGTH);
+ }
+ break;
+ }
+ default:
+ writeSpTxError(ERR_INVALID_CMD_OPCODE);
+ break;
+ }
+ }
+
+ rxReady = twiReceiveByte(&rxTemp);
+ if (rxReady == 1)
+ {
+ uint8_t bcstData[3];
+
+ bcstData[0] = BCST_RECEIVE_BYTE;
+ bcstData[1] = 0x00; // reserved (set to 0x00)
+ bcstData[2] = rxTemp;
+
+ spTxDataBack(bcstData, sizeof(bcstData));
+ }
+}
+
+// ---------------------------------------------------------------------------
+// Firmware Entry Point
+// ---------------------------------------------------------------------------
+
+int main(void)
+{
+ init();
+
+#if defined(USBCON)
+ USBDevice.attach();
+#endif
+
+ setup();
+
+ for (;;) {
+ loop();
+ if (serialEventRun) serialEventRun();
+ }
+
+ return 0;
+}
diff --git a/fw/src/kfd-avr/InfoData.cpp b/fw/src/kfd-avr/InfoData.cpp
new file mode 100644
index 0000000..84dff5f
--- /dev/null
+++ b/fw/src/kfd-avr/InfoData.cpp
@@ -0,0 +1,181 @@
+/**
+* KFDTool - KFD-AVR
+* GPLv2 Open Source. Use is subject to license terms.
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+*
+* @package KFDTool / KFD-AVR Firmware
+*
+*/
+//
+// Based on code from the KFDtool project. (https://github.com/KFDtool/KFDtool)
+// Licensed under the MIT License (https://opensource.org/licenses/MIT)
+//
+/*
+* Copyright (C) 2019-2020 Daniel Dugger
+* Copyright (C) 2021-2022 Nat Moore
+* Copyright (C) 2022 Bryan Biedenkapp N2PLL
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+* and associated documentation files (the "Software"), to deal in the Software without restriction,
+* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+* subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in all copies or substantial
+* portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
+* LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+* NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+* OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+#include "InfoData.h"
+
+// ---------------------------------------------------------------------------
+// Constants
+// ---------------------------------------------------------------------------
+
+#define INFOB_START 0x0
+#define INFOB_LENGTH 5
+#define INFOB_HEADER 0x10
+#define INFOB_FOOTER 0x11
+
+#define INFOC_START 0x128
+#define INFOC_LENGTH 8
+#define INFOC_HEADER 0x20
+#define INFOC_FOOTER 0x22
+
+// ---------------------------------------------------------------------------
+// Global Functions
+// ---------------------------------------------------------------------------
+
+///
+///
+///
+///
+///
+///
+///
+uint16_t idWriteModelIdHwRev(uint8_t hwId, uint8_t hwRevMaj, uint8_t hwRevMin)
+{
+ uint8_t data[5U];
+ data[0U] = INFOB_HEADER;
+ data[1U] = hwId;
+ data[2U] = hwRevMaj;
+ data[3U] = hwRevMin;
+ data[4U] = INFOB_FOOTER;
+
+ for (int i = 0; i < INFOB_LENGTH; i++) {
+ // use update instead of write to possibly save eeprom cells from unnecessary writes
+ EEPROM.update(INFOB_START+i, data[i]);
+ }
+
+ return 1U;
+}
+
+///
+///
+///
+///
+///
+uint16_t idWriteSerNum(uint8_t ser0, uint8_t ser1, uint8_t ser2, uint8_t ser3, uint8_t ser4, uint8_t ser5)
+{
+ // TODO: Fix this mess and accept a array pointer for sanity reasons...
+ uint8_t data[8U];
+ data[0U] = INFOC_HEADER;
+ data[1U] = ser0;
+ data[2U] = ser1;
+ data[3U] = ser2;
+ data[4U] = ser3;
+ data[5U] = ser4;
+ data[6U] = ser5;
+ data[7U] = INFOC_FOOTER;
+
+ for (int i = 0; i < INFOC_LENGTH; i++) {
+ // use update instead of write to possibly save eeprom cells from unnecessary writes
+ EEPROM.update(INFOC_START+i, data[i]);
+ }
+
+ return 1U;
+}
+
+///
+///
+///
+///
+///
+uint16_t idReadModelId(uint8_t *hwId)
+{
+ uint8_t data[INFOB_LENGTH];
+ for (int i = 0; i < INFOB_LENGTH; i++) {
+ data[i] = EEPROM.read(INFOB_START + i);
+ }
+
+ uint8_t header = data[0];
+ uint8_t footer = data[INFOB_LENGTH - 1];
+ *hwId = data[1];
+
+ if (header == INFOB_HEADER && footer == INFOB_FOOTER) {
+ return 1U;
+ }
+ else {
+ return 0U;
+ }
+}
+
+///
+///
+///
+///
+///
+///
+uint16_t idReadHwRev(uint8_t *hwRevMaj, uint8_t *hwRevMin)
+{
+ uint8_t data[INFOB_LENGTH];
+ for (int i = 0; i < INFOB_LENGTH; i++) {
+ data[i] = EEPROM.read(INFOB_START + i);
+ }
+
+ uint8_t header = data[0];
+ uint8_t footer = data[INFOB_LENGTH - 1];
+ *hwRevMaj = data[2];
+ *hwRevMin = data[3];
+
+ if (header == INFOB_HEADER && footer == INFOB_FOOTER) {
+ return 1U;
+ }
+ else {
+ return 0U;
+ }
+}
+
+///
+///
+///
+///
+///
+uint16_t idReadSerNum(uint8_t *ser0, uint8_t *ser1, uint8_t *ser2, uint8_t *ser3, uint8_t *ser4, uint8_t *ser5)
+{
+ // TODO: Fix this mess and return a array pointer for sanity reasons...
+ uint8_t data[INFOC_LENGTH];
+ for (int i = 0; i < INFOC_LENGTH; i++) {
+ data[i] = EEPROM.read(INFOC_START + i);
+ }
+
+ uint8_t header = data[0];
+ uint8_t footer = data[INFOC_LENGTH - 1];
+ *ser0 = data[1];
+ *ser1 = data[2];
+ *ser2 = data[3];
+ *ser3 = data[4];
+ *ser4 = data[5];
+ *ser5 = data[6];
+
+ if (header == INFOC_HEADER && footer == INFOC_FOOTER) {
+ return 1U;
+ }
+ else {
+ return 0U;
+ }
+}
diff --git a/fw/src/kfd-avr/InfoData.h b/fw/src/kfd-avr/InfoData.h
new file mode 100644
index 0000000..f3be384
--- /dev/null
+++ b/fw/src/kfd-avr/InfoData.h
@@ -0,0 +1,56 @@
+/**
+* KFDTool - KFD-AVR
+* GPLv2 Open Source. Use is subject to license terms.
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+*
+* @package KFDTool / KFD-AVR Firmware
+*
+*/
+//
+// Based on code from the KFDtool project. (https://github.com/KFDtool/KFDtool)
+// Licensed under the MIT License (https://opensource.org/licenses/MIT)
+//
+/*
+* Copyright (C) 2019-2020 Daniel Dugger
+* Copyright (C) 2022 Bryan Biedenkapp N2PLL
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+* and associated documentation files (the "Software"), to deal in the Software without restriction,
+* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+* subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in all copies or substantial
+* portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
+* LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+* NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+* OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+#if !defined(__INFO_DATA_H__)
+#define __INFO_DATA_H__
+
+#include "stdint.h"
+#include
+
+// ---------------------------------------------------------------------------
+// Global Functions
+// ---------------------------------------------------------------------------
+///
+uint16_t idWriteModelIdHwRev(uint8_t hwId, uint8_t hwRevMaj, uint8_t hwRevMin);
+
+///
+uint16_t idWriteSerNum(uint8_t ser0, uint8_t ser1, uint8_t ser2, uint8_t ser3, uint8_t ser4, uint8_t ser5);
+
+///
+uint16_t idReadModelId(uint8_t *hwId);
+
+///
+uint16_t idReadHwRev(uint8_t *hwRevMaj, uint8_t *hwRevMin);
+
+///
+uint16_t idReadSerNum(uint8_t *ser0, uint8_t *ser1, uint8_t *ser2, uint8_t *ser3, uint8_t *ser4, uint8_t *ser5);
+
+#endif // __INFO_DATA_H__
diff --git a/fw/src/kfd-avr/SerialProtocol.cpp b/fw/src/kfd-avr/SerialProtocol.cpp
new file mode 100644
index 0000000..42eb46e
--- /dev/null
+++ b/fw/src/kfd-avr/SerialProtocol.cpp
@@ -0,0 +1,203 @@
+/**
+* KFDTool - KFD-AVR
+* GPLv2 Open Source. Use is subject to license terms.
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+*
+* @package KFDTool / KFD-AVR Firmware
+*
+*/
+//
+// Based on code from the KFDtool project. (https://github.com/KFDtool/KFDtool)
+// Licensed under the MIT License (https://opensource.org/licenses/MIT)
+//
+/*
+* Copyright (C) 2019-2020 Daniel Dugger
+* Copyright (C) 2021-2022 Nat Moore
+* Copyright (C) 2022 Bryan Biedenkapp N2PLL
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+* and associated documentation files (the "Software"), to deal in the Software without restriction,
+* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+* subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in all copies or substantial
+* portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
+* LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+* NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+* OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+#include "SerialProtocol.h"
+#include "hal.h"
+#include
+
+// ---------------------------------------------------------------------------
+// Constants
+// ---------------------------------------------------------------------------
+
+#define SOM 0x61
+#define SOM_PLACEHOLDER 0x62
+#define EOM 0x63
+#define EOM_PLACEHOLDER 0x64
+#define ESC 0x70
+#define ESC_PLACEHOLDER 0x71
+
+// ---------------------------------------------------------------------------
+// Globals
+// ---------------------------------------------------------------------------
+
+uint16_t inDataCount = 0;
+uint8_t inData[128];
+
+// ---------------------------------------------------------------------------
+// Global Functions
+// ---------------------------------------------------------------------------
+
+///
+///
+///
+void spConnect(void)
+{
+ Serial.begin(115200);
+}
+
+///
+///
+///
+void spDisconnect(void)
+{
+ //__disable_interrupt();
+ Serial.end();
+}
+
+///
+///
+///
+///
+///
+uint16_t spRxData(uint8_t* outData)
+{
+ while (Serial.available() > 0) {
+ uint8_t inByte = Serial.read();
+
+ // reset the buffer if we have a start of message flag coming in
+ if (inByte == SOM) {
+ inDataCount = 0;
+ }
+
+ inData[inDataCount] = inByte;
+ inDataCount++;
+ }
+
+ // don't process until we receive EOM
+ if (inData[inDataCount - 1] != EOM) {
+ return 0U;
+ }
+
+ uint16_t outIndex = 0U;
+ for (uint16_t inIndex = 1; inIndex < inDataCount - 1; inIndex++) {
+ // skip SOM and EOM
+ if (inData[inIndex] == ESC) {
+ inIndex++;
+
+ if (inData[inIndex] == SOM_PLACEHOLDER) {
+ outData[outIndex] = SOM;
+ }
+ else if (inData[inIndex] == EOM_PLACEHOLDER) {
+ outData[outIndex] = EOM;
+ }
+ else if (inData[inIndex] == ESC_PLACEHOLDER) {
+ outData[outIndex] = ESC;
+ }
+ }
+ else {
+ outData[outIndex] = inData[inIndex];
+ }
+
+ outIndex++;
+ }
+
+ // we've already processed the message and set the pointer
+ // reset the count (mark the buffer as free)
+ inDataCount = 0;
+ return outIndex;
+}
+
+///
+///
+///
+///
+///
+///
+///
+uint16_t spFrameData(const uint8_t* inData, uint16_t inLength, uint8_t* outData)
+{
+ uint16_t escCharsNeeded = 0;
+ for (uint16_t i = 0; i < inLength; i++) {
+ if ((inData[i] == SOM) || (inData[i] == EOM) || (inData[i] == ESC)) {
+ escCharsNeeded++;
+ }
+ }
+
+ uint16_t totalCharsNeeded = 1 + inLength + escCharsNeeded + 1;
+ *(outData + 0) = SOM;
+
+ uint16_t k = 1U;
+ for (uint16_t j = 0; j < inLength; j++)
+ {
+ if (inData[j] == SOM) {
+ *(outData + k) = ESC;
+ k++;
+ *(outData + k) = SOM_PLACEHOLDER;
+ k++;
+ }
+ else if (inData[j] == EOM) {
+ *(outData + k) = ESC;
+ k++;
+ *(outData + k) = EOM_PLACEHOLDER;
+ k++;
+ }
+ else if (inData[j] == ESC) {
+ *(outData + k) = ESC;
+ k++;
+ *(outData + k) = ESC_PLACEHOLDER;
+ k++;
+ }
+ else {
+ *(outData + k) = inData[j];
+ k++;
+ }
+ }
+
+ *(outData + (totalCharsNeeded - 1)) = EOM;
+ return totalCharsNeeded;
+}
+
+///
+///
+///
+///
+///
+///
+void spTxDataBack(const uint8_t* inData, uint16_t inLength)
+{
+ uint8_t outData[128];
+ uint16_t outLength = spFrameData(inData, inLength, outData);
+ Serial.write(outData, outLength);
+}
+
+///
+///
+///
+///
+///
+///
+void spTxDataWait(const uint8_t* inData, uint16_t inLength)
+{
+ uint8_t outData[128];
+ uint16_t outLength = spFrameData(inData, inLength, outData);
+ Serial.write(outData, outLength);
+}
diff --git a/fw/src/kfd-avr/SerialProtocol.h b/fw/src/kfd-avr/SerialProtocol.h
new file mode 100644
index 0000000..e4529f2
--- /dev/null
+++ b/fw/src/kfd-avr/SerialProtocol.h
@@ -0,0 +1,58 @@
+/**
+* KFDTool - KFD-AVR
+* GPLv2 Open Source. Use is subject to license terms.
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+*
+* @package KFDTool / KFD-AVR Firmware
+*
+*/
+//
+// Based on code from the KFDtool project. (https://github.com/KFDtool/KFDtool)
+// Licensed under the MIT License (https://opensource.org/licenses/MIT)
+//
+/*
+* Copyright (C) 2019-2020 Daniel Dugger
+* Copyright (C) 2022 Bryan Biedenkapp N2PLL
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+* and associated documentation files (the "Software"), to deal in the Software without restriction,
+* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+* subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in all copies or substantial
+* portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
+* LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+* NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+* OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+#if !defined(__SERIALPROTOCOL_H__)
+#define __SERIALPROTOCOL_H__
+
+#include "stdint.h"
+
+// ---------------------------------------------------------------------------
+// Global Functions
+// ---------------------------------------------------------------------------
+///
+void spConnect(void);
+
+///
+void spDisconnect(void);
+
+///
+uint16_t spRxData(uint8_t* outData);
+
+///
+uint16_t spFrameData(const uint8_t* inData, uint16_t inLength, uint8_t* outData);
+
+///
+void spTxDataBack(const uint8_t* inData, uint16_t inLength);
+
+///
+void spTxDataWait(const uint8_t* inData, uint16_t inLength);
+
+#endif // __SERIALPROTOCOL_H__
diff --git a/fw/src/kfd-avr/TwiProtocol.cpp b/fw/src/kfd-avr/TwiProtocol.cpp
new file mode 100644
index 0000000..853ecc0
--- /dev/null
+++ b/fw/src/kfd-avr/TwiProtocol.cpp
@@ -0,0 +1,494 @@
+/**
+* KFDTool - KFD-AVR
+* GPLv2 Open Source. Use is subject to license terms.
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+*
+* @package KFDTool / KFD-AVR Firmware
+*
+*/
+//
+// Based on code from the KFDtool project. (https://github.com/KFDtool/KFDtool)
+// Licensed under the MIT License (https://opensource.org/licenses/MIT)
+//
+/*
+* Copyright (C) 2019-2020 Daniel Dugger
+* Copyright (C) 2021-2022 Nat Moore
+* Copyright (C) 2022 Bryan Biedenkapp N2PLL
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+* and associated documentation files (the "Software"), to deal in the Software without restriction,
+* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+* subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in all copies or substantial
+* portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
+* LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+* NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+* OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+#include "hal.h"
+#include "TwiProtocol.h"
+
+// ---------------------------------------------------------------------------
+// Constants
+// ---------------------------------------------------------------------------
+
+#define TIME_OFFSET 1
+#define BIT_TIME FCPU / 4000 * TIME_OFFSET
+#define HALF_BIT_TIME BIT_TIME/2 * TIME_OFFSET
+#define SIG_TIME BIT_TIME*4 * TIME_OFFSET
+
+// ---------------------------------------------------------------------------
+// Macros
+// ---------------------------------------------------------------------------
+
+// set EIFR to 2 clears the interrupt flag
+// probably don't need to do it in the detach side but it keeps anything latent from firing off
+#define ENABLE_KFD_RX_INT cli(); EIFR = 2; attachInterrupt(digitalPinToInterrupt(DATA_RX), Port_1, FALLING); sei();
+#define DISABLE_KFD_RX_INT cli(); EIFR = 2; detachInterrupt(digitalPinToInterrupt(DATA_RX)); sei();
+
+#define KFD_RX_IS_BUSY digitalRead(DATA_RX) == LOW
+#define KFD_RX_IS_IDLE digitalRead(DATA_RX) == HIGH
+
+#define SEN_RX_IS_CONN digitalRead(SNS_RX) == LOW
+#define SEN_RX_IS_DISC digitalRead(SNS_RX) == HIGH
+
+// ---------------------------------------------------------------------------
+// Globals
+// ---------------------------------------------------------------------------
+
+volatile uint16_t busySending;
+volatile uint16_t timerType;
+volatile uint16_t rxBitsLeft;
+volatile uint16_t txNumLeft;
+volatile uint16_t bitCount;
+volatile uint16_t TXByte;
+volatile uint16_t RXByte;
+volatile uint16_t hasReceived;
+
+// ---------------------------------------------------------------------------
+// Global Functions
+// ---------------------------------------------------------------------------
+
+///
+///
+///
+///
+///
+uint8_t reverseByte(uint8_t b)
+{
+ const uint8_t table[] = {
+ 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,
+ 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
+ 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
+ 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
+ 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4,
+ 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
+ 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC,
+ 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
+ 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
+ 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
+ 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
+ 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
+ 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
+ 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
+ 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
+ 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
+ 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1,
+ 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
+ 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9,
+ 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
+ 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
+ 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
+ 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED,
+ 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
+ 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3,
+ 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
+ 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
+ 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
+ 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7,
+ 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
+ 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,
+ 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
+ };
+
+ return table[b];
+}
+
+/*
+///
+///
+///
+///
+///
+uint8_t reverseByte(uint8_t b)
+{
+ b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
+ b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
+ b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
+ return b;
+}
+*/
+
+///
+///
+///
+///
+///
+uint16_t isEvenParity(uint16_t inByte)
+{
+ uint16_t numOnes = 0;
+ for (uint16_t i = 0; i < 8; i++) {
+ if (inByte & 0x01) {
+ numOnes++;
+ }
+
+ inByte = inByte >> 1;
+ }
+
+ if (numOnes % 2) {
+ return 0U;
+ }
+ else {
+ return 1U;
+ }
+}
+
+///
+///
+///
+void twiInit(void)
+{
+ halGpio1Low();
+ ENABLE_KFD_RX_INT;
+ halSenTxConn();
+}
+
+///
+///
+///
+///
+uint8_t twiSelfTest(void)
+{
+ uint16_t error = 0U; // the first error encountered should be returned
+ uint8_t result = 0x00U;
+
+ // disable normal operation
+ DISABLE_KFD_RX_INT;
+ halGpio1High();
+ halActLedOff();
+
+ // test case 1 - KFD shorted to GNDISO
+ // KFD_RX should be IDLE (5VISO)
+ if (!error) {
+ halKfdTxIdle();
+ halSenTxDisc();
+ halDelayMs(10);
+
+ if (KFD_RX_IS_BUSY) {
+ error = 1;
+ result = 0x01;
+ }
+
+ halKfdTxIdle();
+ halSenTxDisc();
+ }
+
+ // test case 2 - SEN shorted to GNDISO
+ // SEN_RX should be DISC (5VISO)
+ if (!error) {
+ halKfdTxIdle();
+ halSenTxDisc();
+ halDelayMs(10);
+
+ if (SEN_RX_IS_CONN) {
+ error = 1;
+ result = 0x02;
+ }
+
+ halKfdTxIdle();
+ halSenTxDisc();
+ }
+
+ // test case 3 - KFD shorted to 5VISO
+ // KFD_RX should be BUSY (GNDISO)
+ if (!error) {
+ halKfdTxBusy();
+ halSenTxDisc();
+ halDelayMs(10);
+
+ if (KFD_RX_IS_IDLE) {
+ error = 1;
+ result = 0x03;
+ }
+
+ halKfdTxIdle();
+ halSenTxDisc();
+ }
+
+ // test case 4 - SEN shorted to 5VISO
+ // SEN_RX should be CONN (GNDISO)
+ if (!error) {
+ halKfdTxIdle();
+ halSenTxConn();
+ halDelayMs(10);
+
+ if (SEN_RX_IS_DISC) {
+ error = 1;
+ result = 0x04;
+ }
+
+ halKfdTxIdle();
+ halSenTxDisc();
+ }
+
+ /*
+ // test cases 5 and 6 _should_ be identical in theory
+ // but strange things happen in the real world...
+
+ // test case 5 - KFD/SEN shorted
+ // SEN_RX should be DISC (5VISO) while KFL_TX is BUSY (GNDISO)
+ if (!error) {
+ halKfdTxBusy();
+ halSenTxDisc();
+ halDelayMs(10);
+
+ if (SEN_RX_IS_CONN) {
+ error = 1;
+ result = 0x05;
+ }
+
+ halKfdTxIdle();
+ halSenTxDisc();
+ }
+
+ // test case 6 - SEN/KFD shorted
+ // KFD_RX should be IDLE (5VISO) while SEN_TX is CONN (GNDISO)
+ if (!error) {
+ halKfdTxIdle();
+ halSenTxConn();
+ halDelayMs(10);
+
+ if (KFD_RX_IS_BUSY) {
+ error = 1;
+ result = 0x06;
+ }
+
+ halKfdTxIdle();
+ halSenTxDisc();
+ }
+ */
+
+ // return to normal operation
+ halKfdTxIdle();
+ halGpio1Low();
+ halActLedOn();
+ ENABLE_KFD_RX_INT;
+ halSenTxConn();
+
+ return result;
+}
+
+///
+///
+///
+///
+///
+uint16_t twiReceiveByte(uint8_t *c)
+{
+ if (hasReceived == 0) {
+ return 0U;
+ }
+
+ *c = reverseByte(RXByte);
+ hasReceived = 0U;
+
+ return 1U;
+}
+
+///
+///
+///
+void twiSendKeySig(void)
+{
+ DISABLE_KFD_RX_INT;
+ halGpio1High();
+ halActLedOff();
+
+ busySending = 1;
+ timerType = 1;
+ txNumLeft = 100;
+
+ // pause interrupts; clear and init registers
+ noInterrupts();
+ TCCR1A = 0;
+ TCCR1B = 0;
+ TCNT1 = 0; // clear counter value
+
+ TCCR1B = 0b00000001; // set prescaler and CTC mode
+ TIMSK1 = 0b00000010; // set interrupt callback
+ OCR1A = SIG_TIME; // set value to count up to
+ interrupts(); // burn baby burn!
+
+ while (busySending); // wait for completion
+
+ halGpio1Low();
+ halActLedOn();
+ ENABLE_KFD_RX_INT
+}
+
+///
+///
+///
+///
+void twiSendPhyByte(uint8_t byteToSend)
+{
+ DISABLE_KFD_RX_INT;
+ halGpio1High();
+ halActLedOff();
+
+ busySending = 1;
+ timerType = 2;
+ txNumLeft = 4;
+
+ TXByte = reverseByte(byteToSend);
+
+ if (isEvenParity(byteToSend) == 0) {
+ TXByte |= 0x100; // unset even parity bit
+ }
+
+ TXByte = TXByte << 1; // add start bit
+ bitCount = 10;
+
+ // pause interrupts; clear and init registers
+ noInterrupts();
+ TCCR1A = 0;
+ TCCR1B = 0;
+ TCNT1 = 0; // clear counter value
+
+ TCCR1B = 0b00000001; // set prescaler
+ TIMSK1 = 0b00000010; // set compare match mode
+ OCR1A = BIT_TIME; // set value to count up to
+ interrupts(); // burn baby burn!
+
+ while (busySending); // wait for completion
+
+ halGpio1Low();
+ halActLedOn();
+ ENABLE_KFD_RX_INT
+}
+
+///
+///
+///
+void Port_1(void)
+{
+ DISABLE_KFD_RX_INT;
+ halGpio1High();
+ halActLedOff();
+
+ timerType = 0;
+ rxBitsLeft = 10;
+ RXByte = 0;
+
+ // pause interrupts; clear and init registers
+ noInterrupts();
+ TCCR1A = 0; // clear registers
+ TCCR1B = 0; // clear registers
+ TCNT1 = 0; // clear counter value
+
+ TCCR1B = 0b00000001; // set prescaler
+ TIMSK1 = 0b00000010; // set compare match mode
+ OCR1A = HALF_BIT_TIME; // set value to count up to
+ interrupts(); // burn baby burn!
+}
+
+///
+///
+///
+ISR(TIMER1_COMPA_vect)
+{
+ TCNT1 = 0; // clear counter value
+ if (timerType == 0) {
+ // receive byte mode
+ OCR1A = BIT_TIME; // set value to count up to
+ if (rxBitsLeft == 0)
+ {
+ TCCR1B = 0; // stop timer by declocking
+ TIMSK1 = 0; // disconnect interrupts
+
+ // wait for idle
+ while (KFD_RX_IS_BUSY);
+
+ halGpio1Low();
+ halActLedOn();
+ ENABLE_KFD_RX_INT;
+ RXByte = RXByte >> 1; // remove start bit
+ RXByte &= 0xFF; // remove parity bit
+ // TODO check parity bit
+ hasReceived = 1;
+ }
+ else {
+ halGpio2Toggle();
+
+ if (KFD_RX_IS_IDLE) {
+ RXByte |= 0x400; // set the value in the RXByte
+ }
+
+ RXByte = RXByte >> 1; // shift the bits down
+ rxBitsLeft--;
+ }
+ }
+ else if (timerType == 1) {
+ // send key signature mode
+ if (txNumLeft == 0) {
+ TCCR1B = 0; // stop timer by declocking
+ TIMSK1 = 0; // disconnect interrupts
+ halKfdTxIdle();
+ busySending = 0;
+ }
+ else {
+ // bryanb: eah? wat?
+ //if (txNumLeft > 5) {
+ if (true) {
+ halKfdTxBusy();
+ }
+ else if (txNumLeft <= 5) {
+ halKfdTxIdle();
+ }
+
+ txNumLeft--;
+ }
+ }
+ else if (timerType == 2) {
+ // send byte mode
+ if (bitCount == 0) {
+ halKfdTxBusy();
+
+ if (txNumLeft == 0) {
+ TCCR1B = 0; // stop timer by declocking
+ TIMSK1 = 0; // disconnect interrupts
+ halKfdTxIdle();
+ busySending = 0;
+ }
+ else {
+ txNumLeft--;
+ }
+ }
+ else {
+ if (TXByte & 0x01) {
+ halKfdTxIdle();
+ }
+ else {
+ halKfdTxBusy();
+ }
+
+ TXByte = TXByte >> 1;
+ bitCount--;
+ }
+ }
+}
diff --git a/fw/src/kfd-avr/TwiProtocol.h b/fw/src/kfd-avr/TwiProtocol.h
new file mode 100644
index 0000000..b7dddfe
--- /dev/null
+++ b/fw/src/kfd-avr/TwiProtocol.h
@@ -0,0 +1,62 @@
+/**
+* KFDTool - KFD-AVR
+* GPLv2 Open Source. Use is subject to license terms.
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+*
+* @package KFDTool / KFD-AVR Firmware
+*
+*/
+//
+// Based on code from the KFDtool project. (https://github.com/KFDtool/KFDtool)
+// Licensed under the MIT License (https://opensource.org/licenses/MIT)
+//
+/*
+* Copyright (C) 2019-2020 Daniel Dugger
+* Copyright (C) 2021-2022 Nat Moore
+* Copyright (C) 2022 Bryan Biedenkapp N2PLL
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+* and associated documentation files (the "Software"), to deal in the Software without restriction,
+* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+* subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in all copies or substantial
+* portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
+* LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+* NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+* OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+#if !defined(__TWIPROTOCOL_H__)
+#define __TWIPROTOCOL_H__
+
+#include
+
+// ---------------------------------------------------------------------------
+// Global Functions
+// ---------------------------------------------------------------------------
+///
+void twiInit(void);
+
+///
+uint8_t twiSelfTest(void);
+
+///
+uint16_t twiReceiveByte(uint8_t *c);
+
+///
+void twiSendKeySig(void);
+
+///
+void twiSendPhyByte(uint8_t byteToSend);
+
+///
+void Port_1(void);
+
+///
+void TimerCallback(void);
+
+#endif // __TWIPROTOCOL_H__
diff --git a/fw/src/kfd-avr/UID.cpp b/fw/src/kfd-avr/UID.cpp
new file mode 100644
index 0000000..4168774
--- /dev/null
+++ b/fw/src/kfd-avr/UID.cpp
@@ -0,0 +1,79 @@
+/**
+* KFDTool - KFD-AVR
+* GPLv2 Open Source. Use is subject to license terms.
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+*
+* @package KFDTool / KFD-AVR Firmware
+*
+*/
+/*
+* Copyright (C) Luiz Henrique Cassettari. All rights reserved.
+* Copyright (C) 2021-2022 Nat Moore
+* Copyright (C) 2022 Bryan Biedenkapp N2PLL
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+* and associated documentation files (the "Software"), to deal in the Software without restriction,
+* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+* subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in all copies or substantial
+* portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
+* LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+* NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+* OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+#include "UID.h"
+
+// ---------------------------------------------------------------------------
+// Global Functions
+// ---------------------------------------------------------------------------
+
+///
+///
+///
+///
+///
+void getUID8(uint8_t *id0, uint8_t *id1, uint8_t *id2, uint8_t *id3, uint8_t *id4, uint8_t *id5, uint8_t *id6, uint8_t *id7)
+{
+ // TODO: Fix this mess and return a array pointer for sanity reasons...
+ uint8_t id[8];
+
+#if defined(ARDUINO_ARCH_AVR)
+ for (size_t i = 0; i < 8; i++) {
+ id[i] = boot_signature_byte_get(0x0E + i + (UniqueIDsize == 9 && i > 5 ? 1 : 0));
+ }
+#elif defined(ARDUINO_ARCH_ESP8266)
+ uint32_t chipid = ESP.getChipId();
+ id[0] = 0;
+ id[1] = 0;
+ id[2] = 0;
+ id[3] = 0;
+ id[4] = chipid >> 24;
+ id[5] = chipid >> 16;
+ id[6] = chipid >> 8;
+ id[7] = chipid;
+#elif defined(ARDUINO_ARCH_ESP32)
+ uint64_t chipid = ESP.getEfuseMac();
+ id[0] = 0;
+ id[1] = 0;
+ id[2] = chipid;
+ id[3] = chipid >> 8;
+ id[4] = chipid >> 16;
+ id[5] = chipid >> 24;
+ id[6] = chipid >> 32;
+ id[7] = chipid >> 40;
+#endif
+
+ *id0 = id[0];
+ *id1 = id[1];
+ *id2 = id[2];
+ *id3 = id[3];
+ *id4 = id[4];
+ *id5 = id[5];
+ *id6 = id[6];
+ *id7 = id[7];
+}
diff --git a/fw/src/kfd-avr/UID.h b/fw/src/kfd-avr/UID.h
new file mode 100644
index 0000000..9416831
--- /dev/null
+++ b/fw/src/kfd-avr/UID.h
@@ -0,0 +1,80 @@
+/**
+* KFDTool - KFD-AVR
+* GPLv2 Open Source. Use is subject to license terms.
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+*
+* @package KFDTool / KFD-AVR Firmware
+*
+*/
+/*
+* Copyright (C) 2021-2022 Nat Moore
+* Copyright (C) 2022 Bryan Biedenkapp N2PLL
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+* and associated documentation files (the "Software"), to deal in the Software without restriction,
+* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+* subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in all copies or substantial
+* portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
+* LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+* NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+* OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+#if !defined(__UID_H__)
+#define __UID_H__
+
+#include
+
+#if defined(ARDUINO_ARCH_AVR)
+#include
+#ifndef SIGRD
+#define SIGRD 5
+#endif
+#elif defined(ARDUINO_ARCH_ESP8266)
+#elif defined(ARDUINO_ARCH_ESP32)
+#elif defined(ARDUINO_ARCH_SAM)
+#elif defined(ARDUINO_ARCH_SAMD)
+#elif defined(ARDUINO_ARCH_STM32)
+#else
+#error "ArduinoUniqueID only works on AVR, SAM, SAMD, STM32 and ESP Architecture"
+#endif
+
+#if defined(ARDUINO_ARCH_AVR)
+
+#if defined(__AVR_ATmega328PB__)
+#define UniqueIDsize 10
+#else
+#define UniqueIDsize 9
+#endif
+
+#define UniqueIDbuffer UniqueIDsize
+
+#elif defined(ARDUINO_ARCH_ESP8266)
+#define UniqueIDsize 4
+#define UniqueIDbuffer 8
+#elif defined(ARDUINO_ARCH_ESP32)
+#define UniqueIDsize 6
+#define UniqueIDbuffer 8
+#elif defined(ARDUINO_ARCH_SAM)
+#define UniqueIDsize 16
+#define UniqueIDbuffer 16
+#elif defined(ARDUINO_ARCH_SAMD)
+#define UniqueIDsize 16
+#define UniqueIDbuffer 16
+#elif defined(ARDUINO_ARCH_STM32)
+#define UniqueIDsize 12
+#define UniqueIDbuffer 12
+#endif
+
+// ---------------------------------------------------------------------------
+// Global Functions
+// ---------------------------------------------------------------------------
+///
+void getUID8(uint8_t *id0, uint8_t *id1, uint8_t *id2, uint8_t *id3, uint8_t *id4, uint8_t *id5, uint8_t *id6, uint8_t *id7);
+
+#endif // __UID_H__
diff --git a/fw/src/kfd-avr/Versions.h b/fw/src/kfd-avr/Versions.h
new file mode 100644
index 0000000..e5b072f
--- /dev/null
+++ b/fw/src/kfd-avr/Versions.h
@@ -0,0 +1,50 @@
+/**
+* KFDTool - KFD-AVR
+* GPLv2 Open Source. Use is subject to license terms.
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+*
+* @package KFDTool / KFD-AVR Firmware
+*
+*/
+//
+// Based on code from the KFDtool project. (https://github.com/KFDtool/KFDtool)
+// Licensed under the MIT License (https://opensource.org/licenses/MIT)
+//
+/*
+* Copyright (C) 2019-2020 Daniel Dugger
+* Copyright (C) 2021-2022 Nat Moore
+* Copyright (C) 2022 Bryan Biedenkapp N2PLL
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+* and associated documentation files (the "Software"), to deal in the Software without restriction,
+* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+* subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in all copies or substantial
+* portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
+* LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+* NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+* OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+#if !defined(__VERSIONS_H__)
+#define __VERSIONS_H__
+
+/*
+** Firmware Version
+*/
+#define VER_FW_MAJOR 0x01U
+#define VER_FW_MINOR 0x04U
+#define VER_FW_PATCH 0x00U
+
+/*
+** Adapter Protocol Version
+*/
+#define VER_AP_MAJOR 0x02U
+#define VER_AP_MINOR 0x00U
+#define VER_AP_PATCH 0x00U
+
+#endif // __VERSIONS_H__
diff --git a/fw/src/kfd-avr/hal.cpp b/fw/src/kfd-avr/hal.cpp
new file mode 100644
index 0000000..760bd22
--- /dev/null
+++ b/fw/src/kfd-avr/hal.cpp
@@ -0,0 +1,277 @@
+/**
+* KFDTool - KFD-AVR
+* GPLv2 Open Source. Use is subject to license terms.
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+*
+* @package KFDTool / KFD-AVR Firmware
+*
+*/
+//
+// Based on code from the KFDtool project. (https://github.com/KFDtool/KFDtool)
+// Licensed under the MIT License (https://opensource.org/licenses/MIT)
+//
+/*
+* Copyright (C) 2021-2022 Nat Moore
+* Copyright (C) 2022 Bryan Biedenkapp N2PLL
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+* and associated documentation files (the "Software"), to deal in the Software without restriction,
+* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+* subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in all copies or substantial
+* portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
+* LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+* NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+* OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+#include "hal.h"
+
+// ---------------------------------------------------------------------------
+// Global Functions
+// ---------------------------------------------------------------------------
+void(*resetFunc)(void) = 0;
+
+///
+///
+///
+void halInit(void)
+{
+ // ACT_LED
+ pinMode(ACTIVITY_LED_PIN, OUTPUT);
+ digitalWrite(ACTIVITY_LED_PIN, LOW);
+
+ // SNS_LED
+ pinMode(SENSE_LED_PIN, OUTPUT);
+ digitalWrite(SENSE_LED_PIN, LOW);
+
+ // MCU_DATA_OUT_3V3
+ pinMode(DATA_TX, OUTPUT);
+ digitalWrite(DATA_TX, LOW);
+
+ // MCU_DATA_IN_3V3
+ pinMode(DATA_RX, INPUT);
+ // make it an interrupt later
+
+ // MCU_SENSE_OUT_3V3
+ pinMode(SNS_TX, OUTPUT);
+ digitalWrite(SNS_TX, LOW);
+
+ // MCU_SENSE_IN_3V3
+ pinMode(SNS_RX, INPUT);
+
+ // GPIO1
+ pinMode(GPIO1, OUTPUT);
+ digitalWrite(GPIO1, LOW);
+
+ // GPIO2
+ pinMode(GPIO2, OUTPUT);
+ digitalWrite(GPIO2, LOW);
+
+ // UP_BUTTON
+ pinMode(UP_BUTTON_PIN, INPUT);
+
+ // DOWN_BUTTON
+ pinMode(DOWN_BUTTON_PIN, INPUT);
+
+ // ENTER_BUTTON
+ pinMode(ENTER_BUTTON_PIN, INPUT);
+
+ // blink the LEDs
+ for (int i = 0; i < 3; i++) {
+ halActLedOn();
+ halSnsLedOn();
+ halDelayMs(250);
+ halActLedOff();
+ halSnsLedOff();
+ halDelayMs(250);
+ }
+
+}
+
+///
+///
+///
+void halDelayUs(uint16_t us)
+{
+ delayMicroseconds(us);
+}
+
+///
+///
+///
+void halDelayMs(uint16_t ms)
+{
+ delay(ms);
+}
+
+///
+///
+///
+void halEnterBsl(void)
+{
+ resetFunc();
+}
+
+///
+///
+///
+void halReset(void)
+{
+ resetFunc();
+}
+
+/*
+** BEGIN LED macros
+*/
+///
+///
+///
+void halActLedOn(void)
+{
+ digitalWrite(ACTIVITY_LED_PIN, HIGH);
+}
+
+///
+///
+///
+void halActLedOff(void)
+{
+ digitalWrite(ACTIVITY_LED_PIN, LOW);
+}
+
+///
+///
+///
+void halActLedToggle(void)
+{
+ digitalWrite(ACTIVITY_LED_PIN, !digitalRead(ACTIVITY_LED_PIN));
+}
+
+///
+///
+///
+void halSnsLedOn(void)
+{
+ digitalWrite(SENSE_LED_PIN, HIGH);
+}
+
+///
+///
+///
+void halSnsLedOff(void)
+{
+ digitalWrite(SENSE_LED_PIN, LOW);
+}
+
+///
+///
+///
+void halSnsLedToggle(void)
+{
+ digitalWrite(SENSE_LED_PIN, !digitalRead(SENSE_LED_PIN));
+}
+
+/*
+** BEGIN GPIO macros
+*/
+///
+///
+///
+void halGpio1High(void)
+{
+ digitalWrite(GPIO1, HIGH);
+}
+
+///
+///
+///
+void halGpio1Low(void)
+{
+ digitalWrite(GPIO1, LOW);
+}
+
+///
+///
+///
+void halGpio1Toggle(void)
+{
+ digitalWrite(GPIO1, !digitalRead(GPIO1));
+}
+
+///
+///
+///
+void halGpio2High(void)
+{
+ digitalWrite(GPIO2, HIGH);
+}
+
+///
+///
+///
+void halGpio2Low(void)
+{
+ digitalWrite(GPIO2, LOW);
+}
+
+///
+///
+///
+void halGpio2Toggle(void)
+{
+ digitalWrite(GPIO2, !digitalRead(GPIO2));
+}
+
+/*
+** BEGIN KFD macros
+*/
+
+///
+///
+///
+void halKfdTxBusy(void)
+{
+ digitalWrite(DATA_TX, HIGH);
+}
+
+///
+///
+///
+void halKfdTxIdle(void)
+{
+ digitalWrite(DATA_TX, LOW);
+}
+
+///
+///
+///
+void halSenTxConn(void)
+{
+ digitalWrite(SNS_TX, HIGH);
+}
+
+///
+///
+///
+void halSenTxDisc(void)
+{
+ digitalWrite(SNS_TX, LOW);
+}
+
+/*
+** BEGIN BUTTON macros
+*/
+
+#define BUTTON_UP_PRESSED digitalRead(UP_BUTTON_PIN) == 0
+#define BUTTON_UP_RELEASED digitalRead(UP_BUTTON_PIN) == 1
+
+#define BUTTON_DOWN_PRESSED digitalRead(DOWN_BUTTON_PIN) == 0
+#define BUTTON_DOWN_RELEASED digitalRead(DOWN_BUTTON_PIN) == 1
+
+#define BUTTON_ENTER_PRESSED digitalRead(ENTER_BUTTON_PIN) == 0
+#define BUTTON_ENTER_RELEASED digitalRead(ENTER_BUTTON_PIN) == 1
diff --git a/fw/src/kfd-avr/hal.h b/fw/src/kfd-avr/hal.h
new file mode 100644
index 0000000..8bfe2f8
--- /dev/null
+++ b/fw/src/kfd-avr/hal.h
@@ -0,0 +1,122 @@
+/**
+* KFDTool - KFD-AVR
+* GPLv2 Open Source. Use is subject to license terms.
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+*
+* @package KFDTool / KFD-AVR Firmware
+*
+*/
+//
+// Based on code from the KFDtool project. (https://github.com/KFDtool/KFDtool)
+// Licensed under the MIT License (https://opensource.org/licenses/MIT)
+//
+/*
+* Copyright (C) 2021-2022 Nat Moore
+* Copyright (C) 2022 Bryan Biedenkapp N2PLL
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+* and associated documentation files (the "Software"), to deal in the Software without restriction,
+* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+* subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in all copies or substantial
+* portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
+* LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+* NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+* OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+#if !defined(__HAL_H__)
+#define __HAL_H__
+
+#include
+
+// ---------------------------------------------------------------------------
+// Constants
+// ---------------------------------------------------------------------------
+
+// 16MHz, as most avr arduinos are
+#define FCPU 16000000
+
+#define ACTIVITY_LED_PIN 7 // if building without the shield, change this to 13 for an activity indicator on the built-in LED
+#define SENSE_LED_PIN 6
+#define DATA_TX 5 // TWI Data TX
+#define DATA_RX 3 // TWI Data RX (INT0)
+#define SNS_TX 4 // TWI Sense TX
+#define SNS_RX 2 // TWI Sense RX (INT1)
+#define GPIO1 8
+#define GPIO2 9
+#define UP_BUTTON_PIN 14
+#define DOWN_BUTTON_PIN 15
+#define ENTER_BUTTON_PIN 16
+
+// ---------------------------------------------------------------------------
+// Global Functions
+// ---------------------------------------------------------------------------
+///
+void halInit(void);
+
+///
+void halDelayUs(uint16_t us);
+
+///
+void halDelayMs(uint16_t ms);
+
+///
+void halEnterBsl(void);
+
+///
+void halReset(void);
+
+///
+void halActLedOn(void);
+
+///
+void halActLedOff(void);
+
+///
+void halActLedToggle(void);
+
+///
+void halSnsLedOn(void);
+
+///
+void halSnsLedOff(void);
+
+///
+void halSnsLedToggle(void);
+
+///
+void halGpio1High(void);
+
+///
+void halGpio1Low(void);
+
+///
+void halGpio1Toggle(void);
+
+///
+void halGpio2High(void);
+
+///
+void halGpio2Low(void);
+
+///
+void halGpio2Toggle(void);
+
+///
+void halKfdTxBusy(void);
+
+///
+void halKfdTxIdle(void);
+
+///
+void halSenTxConn(void);
+
+///
+void halSenTxDisc(void);
+
+#endif // __HAL_H__