diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..efc7e17
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,29 @@
+BasedOnStyle: Google
+IndentWidth: 4
+TabWidth: 4
+UseTab: Never
+ColumnLimit: 240
+
+PointerAlignment: Left
+ReferenceAlignment: Left
+
+AlignConsecutiveAssignments: false
+AlignConsecutiveDeclarations: false
+
+AllowShortFunctionsOnASingleLine: Inline
+AllowShortIfStatementsOnASingleLine: true
+AllowShortLoopsOnASingleLine: true
+
+BreakBeforeBraces: Allman
+
+DerivePointerAlignment: false
+
+SpacesBeforeTrailingComments: 1
+
+SortIncludes: false
+IncludeBlocks: Preserve
+
+Cpp11BracedListStyle: true
+
+AlwaysBreakAfterReturnType: None
+AlwaysBreakTemplateDeclarations: MultiLine
diff --git a/.clang-tidy b/.clang-tidy
new file mode 100644
index 0000000..1d55d17
--- /dev/null
+++ b/.clang-tidy
@@ -0,0 +1,82 @@
+Checks: >
+ google-*,
+ readability-identifier-naming,
+ modernize-redundant-void-arg,
+ modernize-concat-nested-namespaces,
+ modernize-use-nullptr,
+ modernize-use-default-member-init,
+ modernize-use-override,
+ -google-runtime-references,
+ -clang-analyzer-*,
+ -misc-unused-include
+
+CheckOptions:
+ - key: readability-identifier-naming.ClassCase
+ value: CamelCase
+
+ - key: readability-identifier-naming.StructCase
+ value: CamelCase
+
+ - key: readability-identifier-naming.FunctionCase
+ value: CamelCase
+
+ - key: readability-identifier-naming.IgnoreMainLikeFunctions
+ value: 'true'
+
+ - key: readability-identifier-naming.IgnoreOverrideFunctions
+ value: 'true'
+
+ - key: readability-identifier-naming.FunctionIgnoredRegexp
+ value: '^([A-Za-z0-9_]+_(IRQHandler|Handler))$'
+
+ - key: readability-identifier-naming.VariableCase
+ value: lower_case
+
+ - key: readability-identifier-naming.ParameterCase
+ value: lower_case
+
+ - key: readability-identifier-naming.PrivateMemberCase
+ value: lower_case
+ - key: readability-identifier-naming.PrivateMemberSuffix
+ value: _
+
+ - key: readability-identifier-naming.PublicMemberCase
+ value: lower_case
+
+ - key: readability-identifier-naming.ConstantCase
+ value: CamelCase
+ - key: readability-identifier-naming.ConstantPrefix
+ value: k
+
+ - key: readability-identifier-naming.GlobalConstantCase
+ value: CamelCase
+ - key: readability-identifier-naming.GlobalConstantPrefix
+ value: k
+
+ - key: readability-identifier-naming.LocalConstantCase
+ value: CamelCase
+ - key: readability-identifier-naming.LocalConstantPrefix
+ value: k
+
+ - key: readability-identifier-naming.EnumCase
+ value: CamelCase
+
+ - key: readability-identifier-naming.EnumConstantCase
+ value: CamelCase
+ - key: readability-identifier-naming.EnumConstantPrefix
+ value: k
+
+ - key: readability-identifier-naming.EnumConstantMemberCase
+ value: CamelCase
+ - key: readability-identifier-naming.EnumConstantMemberPrefix
+ value: k
+
+ - key: readability-identifier-naming.MacroDefinitionCase
+ value: UPPER_CASE
+
+ - key: readability-identifier-naming.TreatAsConstant
+ value: 'constexpr'
+
+WarningsAsErrors: ''
+HeaderFilterRegex: '.*'
+FormatStyle: none
diff --git a/.clangd b/.clangd
new file mode 100644
index 0000000..9b2c4d5
--- /dev/null
+++ b/.clangd
@@ -0,0 +1,7 @@
+CompileFlags:
+ CompilationDatabase: .
+
+Diagnostics:
+ ClangTidy:
+ Add: []
+
diff --git a/.gitignore b/.gitignore
index e716008..b4553d9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -77,3 +77,6 @@ Artnet*.*
do-tftp.sh
udp_send
+/lib-pixel/jbc
+/.cache
+compile_commands.json
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 0000000..de312f5
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,47 @@
+{
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "Build RDM Responder",
+ "type": "shell",
+ "group": {
+ "kind": "build",
+ "isDefault": true
+ },
+ "command": "make -f Makefile.GD32",
+ "options": {
+ "cwd": "${workspaceFolder}/gd32_rdm_responder/"
+ }
+ },
+ {
+ "label": "Clean RDM Responder",
+ "type": "shell",
+ "command": "make -f Makefile.GD32 clean",
+ "options": {
+ "cwd": "${workspaceFolder}/gd32_rdm_responder/"
+ }
+ },
+ {
+ "label": "Build DMX USB PRO",
+ "type": "shell",
+ "group": {
+ "kind": "build"
+ },
+ "command": "make -f Makefile.GD32",
+ "options": {
+ "cwd": "${workspaceFolder}/gd32_dmx_usb_pro/"
+ }
+ },
+ {
+ "label": "Clean DMX USB PRO",
+ "type": "shell",
+ "group": {
+ "kind": "build"
+ },
+ "command": "make -f Makefile.GD32 clean",
+ "options": {
+ "cwd": "${workspaceFolder}/gd32_dmx_usb_pro/"
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/common/.cproject b/common/.cproject
new file mode 100755
index 0000000..a8acf27
--- /dev/null
+++ b/common/.cproject
@@ -0,0 +1,149 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lib-network/.project b/common/.project
old mode 100644
new mode 100755
similarity index 96%
rename from lib-network/.project
rename to common/.project
index a88c43b..2f9476d
--- a/lib-network/.project
+++ b/common/.project
@@ -1,6 +1,6 @@
- lib-network
+ common
diff --git a/lib-lightset/.settings/language.settings.xml b/common/.settings/language.settings.xml
old mode 100644
new mode 100755
similarity index 67%
rename from lib-lightset/.settings/language.settings.xml
rename to common/.settings/language.settings.xml
index 1e83fda..34912f3
--- a/lib-lightset/.settings/language.settings.xml
+++ b/common/.settings/language.settings.xml
@@ -1,14 +1,14 @@
-
+
-
+
+
+
+
-
-
-
diff --git a/lib-lightset/.settings/org.eclipse.core.resources.prefs b/common/.settings/org.eclipse.core.resources.prefs
old mode 100644
new mode 100755
similarity index 100%
rename from lib-lightset/.settings/org.eclipse.core.resources.prefs
rename to common/.settings/org.eclipse.core.resources.prefs
diff --git a/lib-network/include/net/protocol/ieee.h b/common/include/common/utils/utils_array.h
similarity index 75%
rename from lib-network/include/net/protocol/ieee.h
rename to common/include/common/utils/utils_array.h
index c65ef05..27a889f 100755
--- a/lib-network/include/net/protocol/ieee.h
+++ b/common/include/common/utils/utils_array.h
@@ -1,8 +1,8 @@
/**
- * @file ieee.h
+ * @file utils_array.h
*
*/
-/* Copyright (C) 2024 by Arjan van Vught mailto:info@gd32-dmx.org
+/* Copyright (C) 2025 by Arjan van Vught mailto:info@gd32-dmx.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -23,15 +23,17 @@
* THE SOFTWARE.
*/
-#ifndef NET_PROTOCOL_IEEE_H_
-#define NET_PROTOCOL_IEEE_H_
+#ifndef COMMON_UTILS_UTILS_ARRAY_H_
+#define COMMON_UTILS_UTILS_ARRAY_H_
-#include
+#include
-enum ETHER_TYPE {
- ETHER_TYPE_IPv4 = 0x0800,
- ETHER_TYPE_ARP = 0x0806,
- ETHER_TYPE_PTP = 0x88F7 /* IEEE1588v2 (PTPv2) over Ethernet */
-};
+namespace common
+{
+template constexpr size_t ArraySize(const T (&)[N]) noexcept
+{
+ return N;
+}
+} // namespace common
-#endif /* NET_PROTOCOL_IEEE_H_ */
+#endif // COMMON_UTILS_UTILS_ARRAY_H_
diff --git a/lib-gd32/src/uart0.cpp b/common/include/common/utils/utils_enum.h
old mode 100644
new mode 100755
similarity index 62%
rename from lib-gd32/src/uart0.cpp
rename to common/include/common/utils/utils_enum.h
index 7562f3b..2d93ffe
--- a/lib-gd32/src/uart0.cpp
+++ b/common/include/common/utils/utils_enum.h
@@ -1,8 +1,8 @@
/**
- * @file uart0.cpp
+ * @file utils_enum.h
*
*/
-/* Copyright (C) 2023-2024 by Arjan van Vught mailto:info@gd32-dmx.org
+/* Copyright (C) 2025 by Arjan van Vught mailto:info@gd32-dmx.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -23,32 +23,26 @@
* THE SOFTWARE.
*/
-#include
-#include
+#ifndef COMMON_UTILS_UTILS_ENUM_H_
+#define COMMON_UTILS_UTILS_ENUM_H_
-static char s_buffer[128];
+#include
-extern "C" {
-void uart0_putc(int);
+namespace common
+{
-int uart0_printf(const char *fmt, ...) {
- va_list arp;
-
- va_start(arp, fmt);
-
- int i = vsnprintf(s_buffer, sizeof(s_buffer) - 1, fmt, arp);
-
- va_end(arp);
+/// Converts an enum class value to its underlying integer type.
+template
+inline auto ToValue(Enum e) noexcept {
+ return static_cast>(e);
+}
- char *s = s_buffer;
+/// Converts an integer value to the corresponding enum class value.
+template
+inline Enum FromValue(std::underlying_type_t value) noexcept {
+ return static_cast(value);
+}
- while (*s != '\0') {
- if (*s == '\n') {
- uart0_putc('\r');
- }
- uart0_putc(*s++);
- }
+} // namespace common
- return i;
-}
-}
+#endif // COMMON_UTILS_UTILS_ENUM_H_
diff --git a/common/include/common/utils/utils_flags.h b/common/include/common/utils/utils_flags.h
new file mode 100755
index 0000000..6091f97
--- /dev/null
+++ b/common/include/common/utils/utils_flags.h
@@ -0,0 +1,110 @@
+/**
+ * @file utils_flags.h
+ * Generic enum class bitmask helpers (C++20, freestanding-safe, Google Style)
+ */
+/* Copyright (C) 2025 by Arjan van Vught mailto:info@gd32-dmx.org
+ *
+ * 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.
+ */
+
+#ifndef COMMON_UTILS_UTILS_FLAGS_H_
+#define COMMON_UTILS_UTILS_FLAGS_H_
+
+#include
+#include
+
+#include "common/utils/utils_enum.h" // Ensure this provides ToValue and FromValue
+
+namespace common
+{
+
+template
+ requires std::is_enum_v
+constexpr E operator|(E lhs, E rhs)
+{
+ return static_cast(ToValue(lhs) | ToValue(rhs));
+}
+
+template
+ requires std::is_enum_v
+constexpr E operator&(E lhs, E rhs)
+{
+ return static_cast(ToValue(lhs) & ToValue(rhs));
+}
+
+template
+ requires std::is_enum_v
+constexpr E operator~(E e)
+{
+ return static_cast(~ToValue(e));
+}
+
+template
+ requires std::is_enum_v
+constexpr E& operator|=(E& lhs, E rhs)
+{
+ lhs = lhs | rhs;
+ return lhs;
+}
+
+template
+ requires std::is_enum_v
+constexpr E& operator&=(E& lhs, E rhs)
+{
+ lhs = lhs & rhs;
+ return lhs;
+}
+
+template
+ requires std::is_enum_v
+constexpr void SetFlag(uint32_t& flags, E bit, bool enable)
+{
+ if (enable)
+ {
+ flags |= ToValue(bit);
+ }
+ else
+ {
+ flags &= ~ToValue(bit);
+ }
+}
+
+template
+ requires std::is_enum_v
+constexpr uint32_t SetFlagValue(uint32_t flags, E bit, bool enable)
+{
+ if (enable)
+ {
+ return flags | ToValue(bit);
+ }
+ else
+ {
+ return flags & ~ToValue(bit);
+ }
+}
+
+template
+requires std::is_enum_v
+constexpr bool IsFlagSet(uint32_t flags, E bit) {
+ return (flags & ToValue(bit)) != 0;
+}
+
+} // namespace common
+
+#endif // COMMON_UTILS_UTILS_FLAGS_H_
diff --git a/lib-properties/src/sscanchar.cpp b/common/include/common/utils/utils_hash.h
old mode 100644
new mode 100755
similarity index 59%
rename from lib-properties/src/sscanchar.cpp
rename to common/include/common/utils/utils_hash.h
index eacb303..525c669
--- a/lib-properties/src/sscanchar.cpp
+++ b/common/include/common/utils/utils_hash.h
@@ -1,8 +1,8 @@
/**
- * @file sscanchar.cpp
+ * @file utils_hash.h
*
*/
-/* Copyright (C) 2020 by Arjan van Vught mailto:info@orangepi-dmx.nl
+/* Copyright (C) 2025 by Arjan van Vught mailto:info@gd32-dmx.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -23,39 +23,33 @@
* THE SOFTWARE.
*/
-#if !defined(__clang__) // Needed for compiling on MacOS
-# pragma GCC push_options
-# pragma GCC optimize ("Os")
-#endif
+#ifndef COMMON_UTILS_UTILS_HASH_H_
+#define COMMON_UTILS_UTILS_HASH_H_
#include
-#include
-#include "sscan.h"
-
-Sscan::ReturnCode Sscan::Char(const char *pBuffer, const char *pName, char *pValue, uint32_t& nLength) {
- assert(pBuffer != nullptr);
- assert(pName != nullptr);
- assert(pValue != nullptr);
-
- const char *p;
-
- if ((p = Sscan::checkName(pBuffer, pName)) == nullptr) {
- return Sscan::NAME_ERROR;
- }
-
- uint16_t k = 0;
-
- while ((*p != 0) && (k < nLength)) {
- *pValue++ = *p++;
- k++;
- }
-
- if ((k < nLength) || (*p == '\0') || (*p == '\n')) {
- nLength = k;
- return Sscan::OK;
- }
+// Compile-time FNV-1a 32-bit hash
+consteval uint32_t Fnv1a32(const char* str, uint8_t length)
+{
+ uint32_t hash = 0x811c9dc5u;
+ for (uint8_t i = 0; i < length; ++i)
+ {
+ hash ^= static_cast(str[i]);
+ hash *= 0x01000193u;
+ }
+ return hash;
+}
- return Sscan::VALUE_ERROR;
+// Runtime version for raw filenames
+inline uint32_t Fnv1a32Runtime(const char* str, uint32_t length)
+{
+ uint32_t hash = 0x811c9dc5u;
+ for (uint32_t i = 0; i < length; ++i)
+ {
+ hash ^= static_cast(str[i]);
+ hash *= 0x01000193u;
+ }
+ return hash;
}
+#endif // COMMON_UTILS_UTILS_HASH_H_
diff --git a/common/include/common/utils/utils_hex.h b/common/include/common/utils/utils_hex.h
new file mode 100755
index 0000000..8106af3
--- /dev/null
+++ b/common/include/common/utils/utils_hex.h
@@ -0,0 +1,117 @@
+/**
+ * @file utils_hex.h
+ *
+ */
+/* Copyright (C) 2025 by Arjan van Vught mailto:info@gd32-dmx.org
+ *
+ * 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.
+ */
+
+#ifndef COMMON_UTILS_UTILS_HEX_H_
+#define COMMON_UTILS_UTILS_HEX_H_
+
+#include
+#include
+#include
+
+namespace common::hex
+{
+constexpr char ToCharLowercase(uint32_t value)
+{
+ return "0123456789abcdef"[value & 0xf];
+}
+
+constexpr char ToCharUppercase(uint32_t value)
+{
+ return "0123456789ABCDEF"[value & 0xf];
+}
+
+enum class Case
+{
+ kLower,
+ kUpper
+};
+
+template
+char* ToString(char (&string)[N + 1], uint32_t value)
+{
+ static_assert(N % 2 == 0, "Hex string length must be even");
+ static_assert(N <= 8, "Cannot represent more than 32 bits");
+
+ for (size_t i = 0; i < N; ++i)
+ {
+ size_t shift = (N - 1 - i) * 4;
+ uint32_t nybble = (value >> shift);
+
+ if constexpr (letter_case == Case::kLower)
+ {
+ string[i] = hex::ToCharLowercase(nybble);
+ }
+ else
+ {
+ string[i] = hex::ToCharUppercase(nybble);
+ }
+ }
+
+ string[N] = '\0';
+ return string;
+}
+
+template
+char* ToStringLower(char (&s)[N + 1], uint32_t v)
+{
+ return ToString(s, v);
+}
+
+template
+char* ToStringUpper(char (&s)[N + 1], uint32_t v)
+{
+ return ToString(s, v);
+}
+
+constexpr uint8_t FromChar(char c)
+{
+ return (c >= '0' && c <= '9') ? static_cast(c - '0')
+ : (c >= 'a' && c <= 'f') ? static_cast(c - 'a' + 10)
+ : (c >= 'A' && c <= 'F') ? static_cast(c - 'A' + 10)
+ : 0xFF; // Invalid
+}
+
+template constexpr uint32_t FromHex(const char (&string)[N])
+{
+ static_assert(N > 1, "Hex string must not be empty (must include \\0)");
+ static_assert(N <= 9, "Too many hex digits for uint32_t");
+ assert(string[N - 1] == '\0');
+
+ uint32_t result = 0;
+ for (size_t i = 0; i < N - 1; ++i)
+ {
+ const uint8_t kNibble = FromChar(string[i]);
+ if (kNibble == 0xFF)
+ {
+ return 0; // Or: static_assert(false, "Invalid hex digit"); if desired
+ }
+ result = (result << 4) | kNibble;
+ }
+ return result;
+}
+
+} // namespace common::hex
+
+#endif // COMMON_UTILS_UTILS_HEX_H_
diff --git a/lib-dmx/include/gd32/board_gd32f303rc.h b/common/include/dmx/board_gd32f303rc.h
similarity index 84%
rename from lib-dmx/include/gd32/board_gd32f303rc.h
rename to common/include/dmx/board_gd32f303rc.h
index 65103e5..6162461 100644
--- a/lib-dmx/include/gd32/board_gd32f303rc.h
+++ b/common/include/dmx/board_gd32f303rc.h
@@ -1,5 +1,8 @@
+#ifndef DMX_BOARD_GD32F303RC_H_
+#define DMX_BOARD_GD32F303RC_H_
+
/**
- * @file board_gd32f303r.h
+ * @file board_gd32f303rc.h
*
*/
/* Copyright (C) 2022 by Arjan van Vught mailto:info@gd32-dmx.org
@@ -23,16 +26,15 @@
* THE SOFTWARE.
*/
-#ifndef GD32_BOARD_GD32F303RC_H_
-#define GD32_BOARD_GD32F303RC_H_
-
+#include
#include "gd32_board.h"
-#define DMX_MAX_PORTS 1
+#define DMX_MAX_PORTS 1
-namespace max {
- static const uint32_t PORTS = DMX_MAX_PORTS;
-} // namespace max
+namespace max
+{
+static constexpr uint32_t PORTS = DMX_MAX_PORTS;
+} // namespace max
#define DMX_USE_USART2
@@ -41,4 +43,4 @@ static constexpr auto USART2_PORT = 0;
static constexpr auto DIR_PORT_0_GPIO_PORT = GPIOB;
static constexpr auto DIR_PORT_0_GPIO_PIN = GPIO_PIN_10;
-#endif /* GD32_BOARD_GD32F303RC_H_ */
+#endif // DMX_BOARD_GD32F303RC_H_
diff --git a/common/include/dmx/dmx_config.h b/common/include/dmx/dmx_config.h
new file mode 100644
index 0000000..ba45c78
--- /dev/null
+++ b/common/include/dmx/dmx_config.h
@@ -0,0 +1,71 @@
+#ifndef DMX_DMX_CONFIG_H_
+#define DMX_DMX_CONFIG_H_
+
+/**
+ * @file dmx_config.h
+ *
+ */
+/* Copyright (C) 2021-2025 by Arjan van Vught mailto:info@gd32-dmx.org
+ *
+ * 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 "gd32.h" // IWYU pragma: keep
+
+namespace dmx::config
+{
+#if defined(BOARD_GD32F103RC)
+#include "board_gd32f103rc.h"
+#elif defined(BOARD_GD32F107RC)
+#include "board_gd32f107rc.h"
+#elif defined(BOARD_GD32F207RG)
+#include "board_gd32f207rg.h"
+#elif defined(BOARD_GD32F303RC)
+#include "board_gd32f303rc.h"
+#elif defined(BOARD_GD32F407RE)
+#include "board_gd32f407re.h"
+#elif defined(BOARD_GD32F450VI)
+#include "board_gd32f450vi.h"
+#elif defined(BOARD_GD32H757ZM)
+#include "board_gd32h757zm.h"
+#elif defined(BOARD_GD32F470Z_EVAL)
+#include "board_gd32f470z_eval.h"
+#elif defined(BOARD_GD32F207C_EVAL)
+#include "board_gd32f207c_eval.h"
+#elif defined(BOARD_GD32H759I_EVAL)
+#include "board_gd32h759i_eval.h"
+#elif defined(BOARD_BW_OPIDMX4)
+#include "board_bw_opidmx4.h"
+#elif defined(BOARD_DMX3)
+#include "board_dmx3.h"
+#elif defined(BOARD_DMX4)
+#include "board_dmx4.h"
+#else
+#error
+#endif
+} // namespace dmx::config
+
+namespace dmx::buffer
+{
+static constexpr auto SIZE = 516; // multiple of uint32_t
+} // namespace dmx::buffer
+
+#include "gd32/dmx_dma_check.h"
+
+#endif // DMX_DMX_CONFIG_H_
diff --git a/common/include/firmware/debug/debug_debug.h b/common/include/firmware/debug/debug_debug.h
new file mode 100755
index 0000000..872b75d
--- /dev/null
+++ b/common/include/firmware/debug/debug_debug.h
@@ -0,0 +1,61 @@
+/**
+ * @file debug_debug.h
+ *
+ */
+/* Copyright (C) 2018-2025 by Arjan van Vught mailto:info@gd32-dmx.org */
+
+#ifndef FIRMWARE_DEBUG_DEBUG_H_
+#define FIRMWARE_DEBUG_DEBUG_H_
+
+#if !defined(NDEBUG)
+#include
+
+#define DEBUG_ENTRY() \
+ do \
+ { \
+ printf("-> %s:%s:%d\n", __FILE__, __func__, __LINE__); \
+ } while (0)
+
+#define DEBUG_EXIT() \
+ do \
+ { \
+ printf("<- %s:%s:%d\n", __FILE__, __func__, __LINE__); \
+ } while (0)
+
+#define DEBUG_PRINTF(fmt, ...) \
+ do \
+ { \
+ printf("%s() %s:%d: " fmt "\n", __func__, __FILE__, __LINE__ __VA_OPT__(, ) __VA_ARGS__); \
+ } while (0)
+
+#define DEBUG_PUTS(msg) \
+ do \
+ { \
+ DEBUG_PRINTF("%s", (msg)); \
+ } while (0)
+
+#else
+
+#define DEBUG_ENTRY() \
+ do \
+ { \
+ } while (0)
+
+#define DEBUG_EXIT() \
+ do \
+ { \
+ } while (0)
+
+#define DEBUG_PRINTF(...) \
+ do \
+ { \
+ } while (0)
+
+#define DEBUG_PUTS(...) \
+ do \
+ { \
+ } while (0)
+
+#endif
+
+#endif // FIRMWARE_DEBUG_DEBUG_H_
diff --git a/common/include/firmware/debug/debug_dump.h b/common/include/firmware/debug/debug_dump.h
new file mode 100755
index 0000000..ef6f4f6
--- /dev/null
+++ b/common/include/firmware/debug/debug_dump.h
@@ -0,0 +1,120 @@
+/**
+ * @file debug_dump.h
+ *
+ */
+/* Copyright (C) 2018-2025 by Arjan van Vught mailto:info@gd32-dmx.org
+ *
+ * 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.
+ */
+
+#ifndef COMMON_DEBUG_DEBUG_DUMP_H_
+#define COMMON_DEBUG_DEBUG_DUMP_H_
+
+#include
+#include
+#include
+
+#if defined(H3)
+namespace uart0
+{
+int Printf(const char* fmt, ...);
+}
+#define printf uart0::Printf // NOLINT
+#endif
+
+namespace debug
+{
+namespace dump
+{
+inline constexpr uint32_t kCharsPerLine = 16;
+}
+#ifdef NDEBUG
+inline void Dump([[maybe_unused]] const void* data, [[maybe_unused]] uint32_t size) {}
+#else
+inline void Dump(const void* data, uint32_t size)
+{
+ uint32_t chars = 0;
+ const auto* p = reinterpret_cast(data);
+
+ printf("%p:%d\n", data, size);
+
+ do
+ {
+ uint32_t chars_this_line = 0;
+
+ printf("%04x ", chars);
+
+ const auto* q = p;
+
+ while ((chars_this_line < dump::kCharsPerLine) && (chars < size))
+ {
+ if (chars_this_line % 8 == 0)
+ {
+ printf(" ");
+ }
+
+ printf("%02x ", *p);
+
+ chars_this_line++;
+ chars++;
+ p++;
+ }
+
+ auto chars_dot_line = chars_this_line;
+
+ for (; chars_this_line < dump::kCharsPerLine; chars_this_line++)
+ {
+ if (chars_this_line % 8 == 0)
+ {
+ printf(" ");
+ }
+ printf(" ");
+ }
+
+ chars_this_line = 0;
+
+ while (chars_this_line < chars_dot_line)
+ {
+ if (chars_this_line % 8 == 0)
+ {
+ printf(" ");
+ }
+
+ int ch = *q;
+ if (isprint(ch))
+ {
+ printf("%c", ch);
+ }
+ else
+ {
+ printf(".");
+ }
+
+ chars_this_line++;
+ q++;
+ }
+
+ puts("");
+
+ } while (chars < size);
+}
+#endif
+} // namespace debug
+
+#endif /* COMMON_DEBUG_DEBUG_DUMP_H_ */
diff --git a/lib-ws28xx/src/json_get_satus.cpp b/common/include/firmware/debug/debug_printbits.h
similarity index 60%
rename from lib-ws28xx/src/json_get_satus.cpp
rename to common/include/firmware/debug/debug_printbits.h
index 080a568..196db92 100755
--- a/lib-ws28xx/src/json_get_satus.cpp
+++ b/common/include/firmware/debug/debug_printbits.h
@@ -1,8 +1,8 @@
/**
- * @file json_get.cpp
+ * @file debug_printbits.h
*
*/
-/* Copyright (C) 2024 by Arjan van Vught mailto:info@gd32-dmx.org
+ /* Copyright (C) 2025 by Arjan van Vught mailto:info@gd32-dmx.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -23,14 +23,42 @@
* THE SOFTWARE.
*/
-#include
+#ifndef COMMON_DEBUG_DEBUG_PRINTBITS_H_
+#define COMMON_DEBUG_DEBUG_PRINTBITS_H_
+
#include
+#include
-#include "pixelconfiguration.h"
+#if defined(H3)
+namespace uart0
+{
+int Printf(const char* fmt, ...);
+}
+#define printf uart0::Printf // NOLINT
+#endif
-namespace remoteconfig::pixel {
-uint32_t json_get_status(char *pOutBuffer, const uint32_t nOutBufferSize) {
- auto& pixelConfiguration = PixelConfiguration::Get();
- return static_cast(snprintf(pOutBuffer, nOutBufferSize, "{\"refresh_rate\":\"%u\"}", pixelConfiguration.GetRefreshRate()));
+namespace debug
+{
+#ifdef NDEBUG
+inline void PrintBits([[maybe_unused]] uint32_t u) {}
+#else
+inline void PrintBits(uint32_t u)
+{
+ uint32_t b = 1U << 31;
+
+ for (uint32_t i = 0; i < 32; i++)
+ {
+ if ((b & u) == b)
+ {
+ uint32_t bit_number = 31 - i;
+ printf("%-2d ", bit_number);
+ }
+ b = b >> 1;
+ }
+
+ puts("");
}
-} // namespace remoteconfig::pixel
+#endif
+} // namespace debug
+
+#endif /* COMMON_DEBUG_DEBUG_PRINTBITS_H_ */
diff --git a/common/include/firmware/debug/debug_stack.h b/common/include/firmware/debug/debug_stack.h
new file mode 100755
index 0000000..ed7fb04
--- /dev/null
+++ b/common/include/firmware/debug/debug_stack.h
@@ -0,0 +1,15 @@
+/**
+ * @file debug_stack.h
+ *
+ */
+
+#ifndef FIRMWARE_DEBUG_DEBUG_STACK_H_
+#define FIRMWARE_DEBUG_DEBUG_STACK_H_
+
+namespace debug::stack
+{
+ void Print();
+ void Run();
+}
+
+#endif // FIRMWARE_DEBUG_DEBUG_STACK_H_
diff --git a/lib-rdmsubdevice/src/rdm_subdevices.cpp b/common/include/firmware/pixeldmx/show.h
similarity index 51%
rename from lib-rdmsubdevice/src/rdm_subdevices.cpp
rename to common/include/firmware/pixeldmx/show.h
index dfe423d..5f34a5a 100755
--- a/lib-rdmsubdevice/src/rdm_subdevices.cpp
+++ b/common/include/firmware/pixeldmx/show.h
@@ -1,8 +1,8 @@
-/**
- * @file rdm_subdevices.cpp
+/*
+ * display.h
*
*/
-/* Copyright (C) 2023 by Arjan van Vught mailto:info@gd32-dmx.org
+/* Copyright (C) 2025 by Arjan van Vught mailto:info@gd32-dmx.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -23,39 +23,34 @@
* THE SOFTWARE.
*/
-#include
-#include
-#include
-
-#include "rdm _subdevices.h"
-
-namespace rdm {
-namespace subdevices {
-static constexpr char TYPE[static_cast(rdm::subdevices::Types::UNDEFINED)][9] = {
- "bw7fets", "bwdimmer", "bwdio", "bwlcd", "bwrelay",
- "mcp23s08", "mcp23s17",
- "mcp4822", "mcp4902" };
-
-
-const char* get_type_string(rdm::subdevices::Types type) {
- if (type < rdm::subdevices::Types::UNDEFINED) {
- return TYPE[static_cast(type)];
- }
+#ifndef COMMON_FIRMWARE_PIXELDMX_SHOW_H_
+#define COMMON_FIRMWARE_PIXELDMX_SHOW_H_
- return "Unknown";
-}
-
-rdm::subdevices::Types get_type_string(const char *pValue) {
- assert(pValue != nullptr);
-
- for (uint32_t i = 0; i < static_cast(rdm::subdevices::Types::UNDEFINED); i++) {
- if (strcasecmp(pValue, TYPE[i]) == 0) {
- return static_cast(i);
- }
- }
+#include
- return rdm::subdevices::Types::UNDEFINED;
+#include "pixeldmxconfiguration.h"
+#include "display.h"
+#include "pixelpatterns.h"
+
+namespace common::firmware::pixeldmx
+{
+inline void Show(uint32_t line, pixelpatterns::Pattern pattern = pixelpatterns::Pattern::kNone)
+{
+ auto& configuration = PixelDmxConfiguration::Get();
+ auto* display = Display::Get();
+ assert(display != nullptr);
+
+ display->ClearEndOfLine();
+ display->Printf(line, "%s:%d G%d %s", pixel::GetType(configuration.GetType()), configuration.GetCount(), configuration.GetGroupingCount(),
+ pixel::GetMap(configuration.GetMap()));
+ display->ClearLine(8); // Status line
+
+ if (pattern != pixelpatterns::Pattern::kNone)
+ {
+ display->ClearLine(6);
+ display->Printf(6, "%s:%u", PixelPatterns::GetName(pattern), static_cast(pattern));
+ }
}
-} // namespace subdevices
-} // namespace rdm
+} // namespace common::firmware::pixeldmx
+#endif // COMMON_FIRMWARE_PIXELDMX_SHOW_H_
diff --git a/lib-properties/include/propertiesconfig.h b/common/include/json/json_format_helpers.h
old mode 100644
new mode 100755
similarity index 54%
rename from lib-properties/include/propertiesconfig.h
rename to common/include/json/json_format_helpers.h
index 6ae6963..d621e7a
--- a/lib-properties/include/propertiesconfig.h
+++ b/common/include/json/json_format_helpers.h
@@ -1,8 +1,8 @@
/**
- * @file propertiesconfig.h
+ * @file json_format_helpers.h
*
*/
-/* Copyright (C) 2021 by Arjan van Vught mailto:info@orangepi-dmx.nl
+/* Copyright (C) 2025 by Arjan van Vught mailto:info@gd32-dmx.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -23,44 +23,36 @@
* THE SOFTWARE.
*/
-#ifndef PROPERTIESCONFIG_H_
-#define PROPERTIESCONFIG_H_
+#ifndef JSON_JSON_FORMAT_HELPERS_H_
+#define JSON_JSON_FORMAT_HELPERS_H_
+#include
#include
-#if defined(ENABLE_JSON_ONLY) && defined(DISABLE_JSON)
-# error
-#endif
-
-struct PropertiesConfig {
-public:
- static void EnableJSON(bool enableJSON) {
- if (enableJSON) {
- s_Config |= Mask::ENABLE_JSON;
- } else {
- s_Config &= static_cast(~Mask::ENABLE_JSON);
- }
- }
-
- static bool IsJSON() {
-#if defined(ENABLE_JSON_ONLY)
- return true;
-#elif defined(DISABLE_JSON)
- return false;
-#else
- return isMaskSet(Mask::ENABLE_JSON);
-#endif
- }
-
-private:
- struct Mask {
- static constexpr uint8_t ENABLE_JSON = (1U << 0);
- };
- static bool isMaskSet(uint8_t nMask) {
- return (s_Config & nMask) == nMask;
+namespace format
+{
+constexpr size_t kFloatBufferSize = 8; // For "%.2f", "%.1f"
+constexpr size_t kOffsetBufferSize = 12; // For timezone offsets e.g. "+01:00"
+
+[[nodiscard]] inline const char* FormatFloat(float value, char (&buf)[kFloatBufferSize], const char* fmt = "%.2f")
+{
+ snprintf(buf, sizeof(buf) - 1, fmt, value);
+ return buf;
+}
+
+[[nodiscard]] inline const char* FormatUtcOffset(int32_t hours, uint32_t minutes, char (&buf)[kOffsetBufferSize])
+{
+ if (hours == 0)
+ {
+ snprintf(buf, sizeof(buf) - 1, "%.2d:%.2u", hours, minutes);
+ }
+ else
+ {
+ snprintf(buf, sizeof(buf) - 1, "%c%.2d:%.2u", hours < 0 ? '-' : '+', hours, minutes);
}
+ return buf;
+}
- static uint8_t s_Config;
-};
+} // namespace format
-#endif /* PROPERTIESCONFIG_H_ */
+#endif // JSON_JSON_FORMAT_HELPERS_H_
diff --git a/common/include/json/json_helpers.h b/common/include/json/json_helpers.h
new file mode 100755
index 0000000..86ad893
--- /dev/null
+++ b/common/include/json/json_helpers.h
@@ -0,0 +1,50 @@
+/**
+ * @file json_helpers.h
+ *
+ */
+/* Copyright (C) 2025 by Arjan van Vught mailto:info@gd32-dmx.org
+ *
+ * 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.
+ */
+
+#ifndef JSON_JSON_HELPERS_H_
+#define JSON_JSON_HELPERS_H_
+
+#include
+#include
+
+#include "json/json_jsondoc.h"
+
+namespace json::helpers
+{
+// Template wrapper for consistent JSON serialization pattern.
+template
+uint32_t Serialize(char* buffer, uint32_t length, Callback&& callback)
+{
+ assert(buffer != nullptr);
+ assert(length != 0);
+
+ JsonDoc doc(buffer, length);
+ callback(doc);
+ doc.End();
+ return doc.Size();
+}
+} // namespace json::helpers
+
+#endif // JSON_JSON_HELPERS_H_
diff --git a/common/include/json/json_jsondoc.h b/common/include/json/json_jsondoc.h
new file mode 100755
index 0000000..c943dd3
--- /dev/null
+++ b/common/include/json/json_jsondoc.h
@@ -0,0 +1,165 @@
+/**
+ * @file json_jsondoc.h
+ *
+ */
+/* Copyright (C) 2025 by Arjan van Vught mailto:info@gd32-dmx.org
+ *
+ * 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.
+ */
+
+#ifndef JSON_JSON_JSONDOC_H_
+#define JSON_JSON_JSONDOC_H_
+
+#include
+#include
+#include
+
+class JsonDoc
+{
+ public:
+ JsonDoc(char* buf, uint32_t max_len) : buf_(buf), max_len_(max_len)
+ {
+ assert(buf != nullptr);
+ assert(max_len > 2); // Need at least space for {}
+ Write("{");
+ }
+
+ ~JsonDoc() = default;
+
+ class KeyProxy
+ {
+ public:
+ KeyProxy(JsonDoc& doc, const char* key) : doc_(doc), key_(key) {}
+
+ KeyProxy& operator=(const char* value)
+ {
+ doc_.WriteField(key_, value);
+ return *this;
+ }
+
+ KeyProxy& operator=(uint32_t value)
+ {
+ doc_.WriteField(key_, value);
+ return *this;
+ }
+
+ private:
+ JsonDoc& doc_;
+ const char* key_;
+ };
+
+ KeyProxy operator[](const char* key) { return KeyProxy(*this, key); }
+
+ void End() { Write("}"); }
+
+ uint32_t Size() const { return pos_; }
+
+ private:
+ int CopyString(char* dst, size_t size, const char* src)
+ {
+ if (size == 0)
+ {
+ return 0;
+ }
+
+ size_t length = 0;
+ while ((length < size - 1) && (*src != '\0'))
+ {
+ *dst++ = *src++;
+ ++length;
+ }
+
+ *dst = '\0'; // Always null-terminate
+ return static_cast(length);
+ }
+
+ void WriteField(const char* key, const char* value)
+ {
+ if (!first_)
+ {
+ Write(",");
+ }
+
+ Write("\"");
+ Write(key);
+ Write("\":\"");
+ Write(value);
+ Write("\"");
+ first_ = false;
+ }
+
+ void WriteField(const char* key, uint32_t value)
+ {
+ if (!first_)
+ {
+ Write(",");
+ }
+
+ Write("\"");
+ Write(key);
+ Write("\":");
+
+ char num_buf[11]; // Enough for 32-bit uint
+ char* p = num_buf + sizeof(num_buf);
+ *--p = '\0';
+
+ if (value == 0)
+ {
+ *--p = '0';
+ }
+ else
+ {
+ while (value != 0)
+ {
+ *--p = static_cast('0' + (value % 10));
+ value /= 10;
+ }
+ }
+
+ Write(p); // Already null-terminated
+ first_ = false;
+ }
+
+ void Write(const char* s)
+ {
+ if (pos_ >= max_len_)
+ {
+ return;
+ }
+
+ int ret = CopyString(buf_ + pos_, max_len_ - pos_, s);
+
+ // CopyString always null-terminates and returns number of chars copied (excluding null)
+ if (ret >= 0 && static_cast(ret) + pos_ < max_len_)
+ {
+ pos_ += static_cast(ret);
+ }
+ else
+ {
+ pos_ = max_len_; // Clamp to signal overflow
+ }
+ }
+
+ char* buf_;
+ uint32_t max_len_;
+ bool first_{true};
+ uint32_t pos_{0};
+};
+
+#endif // JSON_JSON_JSONDOC_H_
diff --git a/common/include/json/json_key.h b/common/include/json/json_key.h
new file mode 100755
index 0000000..57c205e
--- /dev/null
+++ b/common/include/json/json_key.h
@@ -0,0 +1,102 @@
+/**
+ * @file json_key.h
+ *
+ */
+/* Copyright (C) 2025 by Arjan van Vught mailto:info@gd32-dmx.org
+ *
+ * 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.
+ */
+
+#ifndef JSON_JSON_KEY_H_
+#define JSON_JSON_KEY_H_
+
+#include
+#include
+
+#include "common/utils/utils_hash.h"
+
+namespace json
+{
+struct SimpleKey
+{
+ const char* name;
+ uint8_t length;
+ uint32_t hash;
+};
+
+struct PortKey
+{
+ const char* name;
+ uint8_t length;
+ uint32_t hash;
+};
+
+struct Key
+{
+ union
+ {
+ const SimpleKey* simple_key;
+ const PortKey* port_key;
+ };
+
+ union
+ {
+ void (*set_simple)(const char*, uint32_t);
+ void (*set_keyed)(const char*, uint32_t, const char*, uint32_t);
+ };
+
+ enum
+ {
+ kSimple,
+ kKeyed
+ } type;
+
+ constexpr const char* GetName() const noexcept {
+ return type == kSimple ? simple_key->name : port_key->name;
+ }
+
+ constexpr uint8_t GetLength() const noexcept {
+ return type == kSimple ? simple_key->length : port_key->length;
+ }
+
+ constexpr uint32_t GetHash() const noexcept {
+ return type == kSimple ? simple_key->hash : port_key->hash;
+ }
+};
+
+constexpr Key MakeKey(void (*set)(const char*, uint32_t), const SimpleKey& simple) noexcept
+{
+ return Key{
+ .simple_key = &simple,
+ .set_simple = set,
+ .type = Key::kSimple
+ };
+}
+
+constexpr Key MakeKey(void (*set)(const char*, uint32_t, const char*, uint32_t), const PortKey& port) noexcept
+{
+ return Key{
+ .port_key = &port,
+ .set_keyed = set,
+ .type = Key::kKeyed
+ };
+}
+} // namespace json
+
+#endif // JSON_JSON_KEY_H_
diff --git a/common/include/json/json_params_base.h b/common/include/json/json_params_base.h
new file mode 100755
index 0000000..01c0787
--- /dev/null
+++ b/common/include/json/json_params_base.h
@@ -0,0 +1,76 @@
+/**
+ * @file json_params_base.h
+ *
+ */
+/* Copyright (C) 2025 by Arjan van Vught mailto:info@gd32-dmx.org
+ *
+ * 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.
+ */
+
+#ifndef JSON_JSON_PARAMS_BASE_H_
+#define JSON_JSON_PARAMS_BASE_H_
+
+#include
+#include
+#include
+
+ #include "firmware/debug/debug_debug.h"
+
+namespace json
+{
+template class JsonParamsBase
+{
+ public:
+ void Load([[maybe_unused]] const char* file_name)
+ {
+#if !defined(DISABLE_FS)
+ FILE* fp = fopen(file_name, "r");
+ if (fp != nullptr)
+ {
+ char buffer[512]; // Adjust as needed for max config size
+ size_t size = fread(buffer, 1, sizeof(buffer), fp);
+ fclose(fp);
+
+ if (size > 0)
+ {
+ static_cast(this)->Store(buffer, static_cast(size));
+ }
+ else
+ {
+ DEBUG_PUTS("Empty or failed read");
+ }
+#ifndef NDEBUG
+ static_cast(this)->Dump();
+#endif
+ }
+ else
+ {
+ DEBUG_PUTS("Failed to open file");
+ }
+#endif
+ }
+
+ protected:
+ JsonParamsBase() = default;
+ ~JsonParamsBase() = default;
+};
+
+} // namespace json
+
+#endif // JSON_JSON_PARAMS_BASE_H_
diff --git a/common/include/json/json_parsehelper.h b/common/include/json/json_parsehelper.h
new file mode 100755
index 0000000..0e2ea76
--- /dev/null
+++ b/common/include/json/json_parsehelper.h
@@ -0,0 +1,134 @@
+/**
+ * @file json_parsehelper.h
+ *
+ */
+/* Copyright (C) 2025 by Arjan van Vught mailto:info@gd32-dmx.org
+ *
+ * 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.
+ */
+
+#ifndef JSON_JSON_PARSEHELPER_H_
+#define JSON_JSON_PARSEHELPER_H_
+
+#include
+#include
+
+namespace json
+{
+inline int32_t Atoi(const char* buffer, uint32_t size)
+{
+ const char* p = buffer;
+ int32_t sign = 1;
+ int32_t res = 0;
+
+ if (size == 0)
+ {
+ return 0;
+ }
+
+ if (*p == '-')
+ {
+ sign = -1;
+ p++;
+ size--;
+ }
+ else if (*p == '+')
+ {
+ p++;
+ size--;
+ }
+
+ for (; (size > 0) && (*p >= '0' && *p <= '9'); size--, p++)
+ {
+ res = res * 10 + (*p - '0');
+ }
+
+ return sign * res;
+}
+
+inline float Atof(const char* buffer, uint32_t size)
+{
+ const char* p = buffer;
+ float sign = 1.0f;
+ float result = 0.0f;
+
+ if (size == 0)
+ {
+ return 0.0f;
+ }
+
+ if (*p == '-')
+ {
+ sign = -1.0f;
+ ++p;
+ --size;
+ }
+ else if (*p == '+')
+ {
+ ++p;
+ --size;
+ }
+
+ // Parse integer part
+ while (size > 0 && *p >= '0' && *p <= '9')
+ {
+ result = result * 10.0f + static_cast(*p - '0');
+ ++p;
+ --size;
+ }
+
+ // Parse fractional part
+ if (size > 0 && *p == '.')
+ {
+ ++p;
+ --size;
+
+ float divisor = 10.0f;
+ while (size > 0 && *p >= '0' && *p <= '9')
+ {
+ result += static_cast(*p - '0') / divisor;
+ divisor *= 10.0f;
+ ++p;
+ --size;
+ }
+ }
+
+ return sign * result;
+}
+
+template T ParseValue(const char* val, uint32_t len)
+{
+ int32_t v = Atoi(val, len);
+ if constexpr (std::is_unsigned_v)
+ {
+ if (v < 0)
+ {
+ return 0; // or handle error
+ }
+ }
+ return static_cast(v);
+}
+
+template void ParseAndApply(const char* val, uint32_t len, F&& apply)
+{
+ apply(ParseValue(val, len));
+}
+} // namespace json
+
+#endif // JSON_JSON_PARSEHELPER_H_
diff --git a/common/include/json/json_parser.h b/common/include/json/json_parser.h
new file mode 100755
index 0000000..00acfaf
--- /dev/null
+++ b/common/include/json/json_parser.h
@@ -0,0 +1,99 @@
+/**
+ * @file json_parser.h
+ *
+ */
+/* Copyright (C) 2025 by Arjan van Vught mailto:info@gd32-dmx.org
+ *
+ * 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.
+ */
+
+#ifndef JSON_JSON_PARSER_H_
+#define JSON_JSON_PARSER_H_
+
+#include
+#include
+#include
+
+#include "common/utils/utils_hash.h"
+#include "json/json_key.h"
+#include "json/json_tokenizer.h"
+
+inline void ParseJsonWithTable(const char* buffer, size_t size, const json::Key* keys, size_t key_count)
+{
+ JsonTokenizer tok(buffer, size);
+ tok.SkipWhitespace();
+
+ if (tok.p >= tok.end || *tok.p != '{') return;
+ ++tok.p;
+
+ while (tok.p < tok.end)
+ {
+ const char* json_key;
+ size_t json_key_len;
+ if (!tok.NextString(json_key, json_key_len)) break;
+
+ if (!tok.Expect(':')) break;
+
+ const char* val;
+ size_t val_len;
+ if (!tok.NextValue(val, val_len)) break;
+
+ uint32_t h = Fnv1a32Runtime(json_key, static_cast(json_key_len));
+ bool matched = false;
+
+ for (size_t i = 0; i < key_count; ++i)
+ {
+ if (keys[i].GetHash() == h)
+ {
+ if (keys[i].type == json::Key::kSimple)
+ {
+ keys[i].set_simple(val, val_len);
+ }
+ else
+ {
+ keys[i].set_keyed(json_key, json_key_len, val, val_len);
+ }
+ matched = true;
+ break;
+ }
+ }
+
+ if (!matched)
+ {
+ // Unknown key
+ }
+
+ tok.SkipWhitespace();
+ if (tok.p < tok.end && *tok.p == ',')
+ {
+ ++tok.p;
+ }
+ else if (tok.p < tok.end && *tok.p == '}')
+ {
+ break;
+ }
+ }
+}
+
+template inline void ParseJsonWithTable(const char* buffer, size_t size, const json::Key (&keys)[N])
+{
+ ParseJsonWithTable(buffer, size, keys, N);
+}
+
+#endif // JSON_JSON_PARSER_H_
diff --git a/common/include/json/json_tokenizer.h b/common/include/json/json_tokenizer.h
new file mode 100755
index 0000000..1fcfdfc
--- /dev/null
+++ b/common/include/json/json_tokenizer.h
@@ -0,0 +1,82 @@
+/**
+ * @file json_tokenizer.h
+ *
+ */
+/* Copyright (C) 2025 by Arjan van Vught mailto:info@gd32-dmx.org
+ *
+ * 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.
+ */
+
+
+#ifndef JSON_JSON_TOKENIZER_H_
+#define JSON_JSON_TOKENIZER_H_
+
+#include
+
+struct JsonTokenizer
+{
+ const char* p;
+ const char* end;
+
+ constexpr JsonTokenizer(const char* buffer, size_t size) : p(buffer), end(buffer + size) {}
+
+ constexpr void SkipWhitespace()
+ {
+ while (p < end && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')) ++p;
+ }
+
+ bool NextString(const char*& out, size_t& len)
+ {
+ SkipWhitespace();
+ if (p >= end || *p != '"') return false;
+ ++p; // skip "
+ out = p;
+ while (p < end && *p != '"') ++p;
+ if (p >= end) return false;
+ len = static_cast(p - out);
+ ++p; // skip "
+ return true;
+ }
+
+ bool Expect(char c)
+ {
+ SkipWhitespace();
+ if (p >= end || *p != c) return false;
+ ++p;
+ return true;
+ }
+
+ bool NextValue(const char*& out, size_t& len)
+ {
+ SkipWhitespace();
+ if (p >= end) return false;
+
+ if (*p == '"')
+ {
+ return NextString(out, len);
+ }
+
+ out = p;
+ while (p < end && *p != ',' && *p != '}' && *p != ' ' && *p != '\t' && *p != '\n' && *p != '\r') ++p;
+ len = static_cast(p - out);
+ return len > 0;
+ }
+};
+
+#endif // JSON_JSON_TOKENIZER_H_
diff --git a/common/make/CppOps.mk b/common/make/CppOps.mk
new file mode 100755
index 0000000..ed076fc
--- /dev/null
+++ b/common/make/CppOps.mk
@@ -0,0 +1,7 @@
+$(info "CppOpts.mk")
+
+CPPOPS=-std=c++20
+CPPOPS+=-Wnon-virtual-dtor -Woverloaded-virtual -Wnull-dereference -fno-rtti -fno-exceptions -fno-unwind-tables
+CPPOPS+=-Wuseless-cast -Wold-style-cast
+CPPOPS+=-fno-threadsafe-statics -fno-use-cxa-atexit
+CPPOPS+=-Wshadow -Wshadow=local
\ No newline at end of file
diff --git a/common/make/DmxNodeNodeType.mk b/common/make/DmxNodeNodeType.mk
new file mode 100755
index 0000000..f83afcb
--- /dev/null
+++ b/common/make/DmxNodeNodeType.mk
@@ -0,0 +1,30 @@
+$(info "DmxNodeNodeType.mk")
+$(info $$MAKE_FLAGS [${MAKE_FLAGS}])
+$(info $$DEFINES [${DEFINES}])
+
+FLAGS:=$(MAKE_FLAGS)
+ifeq ($(FLAGS),)
+ FLAGS:=$(DEFINES)
+endif
+
+ifeq ($(findstring NODE_ARTNET,$(FLAGS)), NODE_ARTNET)
+ EXTRA_INCLUDES+=../lib-artnet/include ../lib-e131/include ../lib-rdm/include
+ ifeq ($(findstring CONFIG_RDM_ENABLE_SENSORS,$(FLAGS)), CONFIG_RDM_ENABLE_SENSORS)
+ EXTRA_INCLUDES+=../lib-rdmsensor/include
+ endif
+endif
+
+ifeq ($(findstring NODE_E131,$(FLAGS)), NODE_E131)
+ EXTRA_INCLUDES+=../lib-e131/include
+endif
+
+ifeq ($(findstring NODE_DDP_DISPLAY,$(FLAGS)), NODE_DDP_DISPLAY)
+ EXTRA_INCLUDES+=../lib-ddp/include
+endif
+
+ifeq ($(findstring NODE_PP,$(FLAGS)), NODE_PP)
+ EXTRA_INCLUDES+=../lib-pp/include
+endif
+
+EXTRA_INCLUDES:= $(strip $(sort $(EXTRA_INCLUDES)))
+$(info $$EXTRA_INCLUDES [${EXTRA_INCLUDES}])
\ No newline at end of file
diff --git a/common/make/DmxNodeOutputType.mk b/common/make/DmxNodeOutputType.mk
new file mode 100755
index 0000000..8d4fc9d
--- /dev/null
+++ b/common/make/DmxNodeOutputType.mk
@@ -0,0 +1,49 @@
+$(info "DmxNodeOutputType.mk")
+$(info $$MAKE_FLAGS [${MAKE_FLAGS}])
+$(info $$DEFINES [${DEFINES}])
+
+FLAGS:=$(MAKE_FLAGS)
+ifeq ($(FLAGS),)
+ FLAGS:=$(DEFINES)
+endif
+
+EXTRA_INCLUDES+=../lib-dmxnode/include
+
+ifeq ($(findstring OUTPUT_DMX_ARTNET,$(FLAGS)), OUTPUT_DMX_ARTNET)
+ EXTRA_INCLUDES+=../lib-artnet/include ../lib-e131/include
+endif
+
+ifeq ($(findstring OUTPUT_DMX_SEND,$(FLAGS)), OUTPUT_DMX_SEND)
+ EXTRA_INCLUDES+=../lib-dmx/include ../lib-network/include
+endif
+
+ifeq ($(findstring OUTPUT_DMX_PIXEL,$(FLAGS)), OUTPUT_DMX_PIXEL)
+ EXTRA_INCLUDES+=../lib-pixel/include ../lib-pixeldmx/include
+endif
+
+ifeq ($(findstring OUTPUT_DMX_MONITOR,$(MAKE_FLAGS)), OUTPUT_DMX_MONITOR)
+ EXTRA_INCLUDES+=../lib-dmxmonitor/include
+endif
+
+ifeq ($(findstring OUTPUT_DMX_NULL,$(MAKE_FLAGS)), OUTPUT_DMX_NULL)
+ EXTRA_INCLUDES+=../lib-rdm/include ../lib-e131/include
+endif
+
+ifeq ($(findstring OUTPUT_DMX_SHOWFILE,$(MAKE_FLAGS)), OUTPUT_DMX_SHOWFILE)
+ EXTRA_INCLUDES+=../lib-showfile/include
+endif
+
+ifeq ($(findstring OUTPUT_DMX_SERIAL,$(FLAGS)), OUTPUT_DMX_SERIAL)
+ EXTRA_INCLUDES+=../lib-dmxserial/include ../lib-network/include
+endif
+
+ifeq ($(findstring OUTPUT_DMX_STEPPER,$(FLAGS)), OUTPUT_DMX_STEPPER)
+ EXTRA_INCLUDES+=../lib-l6470/include ../lib-l6470dmx/include
+ EXTRA_INCLUDES+=../lib-tlc59711/include ../lib-tlc59711dmx/include
+endif
+
+ifeq ($(findstring OUTPUT_DMX_PCA9685,$(FLAGS)), OUTPUT_DMX_PCA9685)
+ EXTRA_INCLUDES+=../lib-pca9685/include ../lib-pca9685dmx/include
+endif
+
+$(info $$EXTRA_INCLUDES [${EXTRA_INCLUDES}])
\ No newline at end of file
diff --git a/common/make/Timestamp.mk b/common/make/Timestamp.mk
new file mode 100755
index 0000000..ab00aa9
--- /dev/null
+++ b/common/make/Timestamp.mk
@@ -0,0 +1,8 @@
+$(info "Timestamp.mk")
+
+ifneq ($(findstring _TIME_STAMP_YEAR_,$(DEFINES)), _TIME_STAMP_YEAR_)
+ DEFINES += \
+ -D_TIME_STAMP_YEAR_=$(shell date +"%Y") \
+ -D_TIME_STAMP_MONTH_=$(shell date +"%m" | sed 's/^0*//') \
+ -D_TIME_STAMP_DAY_=$(shell date +"%d" | sed 's/^0*//')
+endif
\ No newline at end of file
diff --git a/firmware-template-gd32/Board.mk b/common/make/gd32/Board.mk
similarity index 84%
rename from firmware-template-gd32/Board.mk
rename to common/make/gd32/Board.mk
index bc44a93..e7dd895 100644
--- a/firmware-template-gd32/Board.mk
+++ b/common/make/gd32/Board.mk
@@ -24,16 +24,10 @@ ifeq ($(strip $(BOARD)),BOARD_GD32F207RG)
DEFINES+=-DCONFIG_STORE_USE_SPI
endif
-ifeq ($(strip $(BOARD)),BOARD_GD32F207VC_2)
- MCU=GD32F207VC
- DEFINES+=-DCONFIG_STORE_USE_ROM
- BITBANGING595=1
-endif
-
-ifeq ($(strip $(BOARD)),BOARD_GD32F207VC_4)
- MCU=GD32F207VC
+ifeq ($(strip $(BOARD)),BOARD_GD32F303CB)
+ MCU=GD32F303CB
DEFINES+=-DCONFIG_STORE_USE_ROM
- BITBANGING595=1
+ DEFINES+=-DNO_EMAC
endif
ifeq ($(strip $(BOARD)),BOARD_GD32F303RC)
@@ -47,12 +41,6 @@ ifeq ($(strip $(BOARD)),BOARD_GD32F407RE)
DEFINES+=-DCONFIG_STORE_USE_SPI
endif
-ifeq ($(strip $(BOARD)),BOARD_GD32F450VE)
- MCU=GD32F450VE
- DEFINES+=-DCONFIG_STORE_USE_RAM
- BITBANGING595=1
-endif
-
ifeq ($(strip $(BOARD)),BOARD_GD32F450VI)
MCU=GD32F450VI
endif
diff --git a/common/make/gd32/Includes.mk b/common/make/gd32/Includes.mk
new file mode 100644
index 0000000..a9f2a69
--- /dev/null
+++ b/common/make/gd32/Includes.mk
@@ -0,0 +1,101 @@
+$(info "Includes.mk")
+
+INCLUDES:=-I./include
+INCLUDES+=-I../common/include -I../include
+INCLUDES+=-I../firmware-template-gd32/include
+INCLUDES+=-I../CMSIS/Core/Include
+INCLUDES+=-I../lib-gd32/${FAMILY}/${FAMILY_UC}_standard_peripheral/Include
+INCLUDES+=-I../lib-gd32/${FAMILY}/CMSIS/GD/${FAMILY_UC}/Include
+INCLUDES+=-I../lib-gd32/include
+INCLUDES+=$(addprefix -I,$(EXTRA_INCLUDES))
+
+ALL_FLAGS := $(DEFINES) $(MAKE_FLAGS)
+$(info $$ALL_FLAGS [${ALL_FLAGS}])
+
+# $(call set_if_present,FLAG,VAR) -> sets VAR=1 if FLAG or -DFLAG is present
+set_if_present = $(eval $(2) := $(if $(filter $(1) -D$(1),$(ALL_FLAGS)),1,))
+
+$(call set_if_present,ENABLE_USB_HOST,USB_HOST)
+$(call set_if_present,CONFIG_USB_HOST_MSC,USB_HOST_MSC)
+$(call set_if_present,ENABLE_USB_DEVICE,USB_DEVICE)
+$(call set_if_present,CONFIG_USB_DEVICE_CDC,USB_DEVICE_CDC)
+$(call set_if_present,CONFIG_USB_DEVICE_HID,USB_DEVICE_HID)
+
+ifdef USB_HOST
+ INCLUDES+=-I../lib-gd32/device/usb
+ INCLUDES+=-I../lib-hal/device/usb/host/gd32
+endif
+
+ifdef USB_DEVICE
+ INCLUDES+=-I../lib-gd32/device/usb
+ INCLUDES+=-I../lib-hal/device/usb/device/gd32
+endif
+
+ifeq ($(findstring gd32f20x,$(FAMILY)), gd32f20x)
+ ifdef USB_HOST
+ INCLUDES+=-I../lib-gd32/${FAMILY}/${FAMILY_UC}_usbfs_library/driver/Include
+ INCLUDES+=-I../lib-gd32/${FAMILY}/${FAMILY_UC}_usbfs_library/host/core/Include
+ INCLUDES+=-I../lib-gd32/${FAMILY}/${FAMILY_UC}_usbfs_library/ustd/common
+ ifdef USB_HOST_MSC
+ INCLUDES+=-I../lib-gd32/${FAMILY}/${FAMILY_UC}_usbfs_library/host/class/msc/Include
+ INCLUDES+=-I../lib-gd32/${FAMILY}/${FAMILY_UC}_usbfs_library/ustd/class/msc
+ endif
+ endif
+ ifdef USB_DEVICE
+ INCLUDES+=-I../lib-gd32/${FAMILY}/${FAMILY_UC}_usbfs_library/driver/Include
+ INCLUDES+=-I../lib-gd32/${FAMILY}/${FAMILY_UC}_usbfs_library/device/core/Include
+ INCLUDES+=-I../lib-gd32/${FAMILY}/${FAMILY_UC}_usbfs_library/ustd/common
+ ifdef USB_DEVICE_CDC
+ INCLUDES+=-I../lib-gd32/${FAMILY}/${FAMILY_UC}_usbfs_library/device/class/cdc/Include
+ INCLUDES+=-I../lib-gd32/${FAMILY}/${FAMILY_UC}_usbfs_library/ustd/class/cdc
+ endif
+ ifdef USB_DEVICE_HID
+ INCLUDES+=-I../lib-gd32/${FAMILY}/${FAMILY_UC}_usbfs_library/device/class/hid/Include
+ INCLUDES+=-I../lib-gd32/${FAMILY}/${FAMILY_UC}_usbfs_library/ustd/class/hid
+ endif
+ endif
+endif
+
+ifeq ($(findstring gd32f4xx,$(FAMILY)), gd32f4xx)
+ ifdef USB_HOST
+ INCLUDES+=-I../lib-gd32/${FAMILY}/${FAMILY_UC}_usb_library/driver/Include
+ INCLUDES+=-I../lib-gd32/${FAMILY}/${FAMILY_UC}_usb_library/host/core/Include
+ INCLUDES+=-I../lib-gd32/${FAMILY}/${FAMILY_UC}_usb_library/ustd/common
+ ifdef USB_HOST_MSC
+ INCLUDES+=-I../lib-gd32/${FAMILY}/${FAMILY_UC}_usb_library/host/class/msc/Include
+ INCLUDES+=-I../lib-gd32/${FAMILY}/${FAMILY_UC}_usb_library/ustd/class/msc
+ endif
+ endif
+ ifdef USB_DEVICE
+ INCLUDES+=-I../lib-gd32/${FAMILY}/${FAMILY_UC}_usb_library/driver/Include
+ INCLUDES+=-I../lib-gd32/${FAMILY}/${FAMILY_UC}_usb_library/device/core/Include
+ INCLUDES+=-I../lib-gd32/${FAMILY}/${FAMILY_UC}_usb_library/ustd/common
+ ifdef USB_DEVICE_CDC
+ INCLUDES+=-I../lib-gd32/${FAMILY}/${FAMILY_UC}_usb_library/device/class/cdc/Include
+ INCLUDES+=-I../lib-gd32/${FAMILY}/${FAMILY_UC}_usb_library/ustd/class/cdc
+ endif
+ ifdef USB_DEVICE_HID
+ INCLUDES+=-I../lib-gd32/${FAMILY}/${FAMILY_UC}_usb_library/device/class/hid/Include
+ INCLUDES+=-I../lib-gd32/${FAMILY}/${FAMILY_UC}_usb_library/ustd/class/hid
+ endif
+ endif
+endif
+
+ifeq ($(findstring gd32h7xx,$(FAMILY)), gd32h7xx)
+ ifdef USB_HOST
+ INCLUDES+=-I../lib-gd32/${FAMILY}/${FAMILY_UC}_usbhs_library/driver/Include
+ INCLUDES+=-I../lib-gd32/${FAMILY}/${FAMILY_UC}_usbhs_library/host/core/Include
+ INCLUDES+=-I../lib-gd32/${FAMILY}/${FAMILY_UC}_usbhs_library/ustd/common
+ ifdef USB_HOST_MSC
+ INCLUDES+=-I../lib-gd32/${FAMILY}/${FAMILY_UC}_usbhs_library/host/class/msc/Include
+ INCLUDES+=-I../lib-gd32/${FAMILY}/${FAMILY_UC}_usbhs_library/ustd/class/msc
+ endif
+ endif
+endif
+
+ifdef USB_HOST_MSC
+ INCLUDES+=-I../lib-hal/ff14b/source
+endif
+
+INCLUDES:= $(strip -I../${PROJECT}/include $(sort $(INCLUDES)))
+$(info $$INCLUDES [${INCLUDES}])
\ No newline at end of file
diff --git a/firmware-template-gd32/Mcu.mk b/common/make/gd32/Mcu.mk
similarity index 86%
rename from firmware-template-gd32/Mcu.mk
rename to common/make/gd32/Mcu.mk
index 46058e8..fd02431 100644
--- a/firmware-template-gd32/Mcu.mk
+++ b/common/make/gd32/Mcu.mk
@@ -20,12 +20,14 @@ ifeq ($(strip $(MCU)),GD32F103RC)
LINKER=$(FIRMWARE_DIR)gd32f103rc_flash.ld
FAMILY=gd32f10x
LINE=gd32f10x_hd
+ TARGET=gd32f103rc.bin
endif
ifeq ($(strip $(MCU)),GD32F107RC)
LINKER=$(FIRMWARE_DIR)gd32f107rc_flash.ld
FAMILY=gd32f10x
LINE=gd32f10x_cl
+ TARGET=gd32f107.bin
endif
ifeq ($(strip $(MCU)),GD32F207VC)
@@ -40,10 +42,18 @@ ifeq ($(strip $(MCU)),GD32F207RG)
LINE=gd32f20x_cl
endif
+ifeq ($(strip $(MCU)),GD32F303CB)
+ LINKER=$(FIRMWARE_DIR)gd32f303cb_flash.ld
+ FAMILY=gd32f30x
+ LINE=gd32f30x_hd
+ TARGET=gd32f303cb.bin
+endif
+
ifeq ($(strip $(MCU)),GD32F303RC)
LINKER=$(FIRMWARE_DIR)gd32f303rc_flash.ld
FAMILY=gd32f30x
LINE=gd32f30x_hd
+ TARGET=gd32f303rc.bin
endif
ifeq ($(strip $(MCU)),GD32F407RE)
@@ -76,6 +86,12 @@ ifeq ($(strip $(MCU)),GD32F470ZK)
LINE=gd32f470
endif
+ifeq ($(strip $(MCU)),GD32H757ZM)
+ LINKER=$(FIRMWARE_DIR)gd32h7xx_xM_flash.ld
+ FAMILY=gd32h7xx
+ LINE=gd32h757
+endif
+
ifeq ($(strip $(MCU)),GD32H759IM)
LINKER=$(FIRMWARE_DIR)gd32h7xx_xM_flash.ld
FAMILY=gd32h7xx
@@ -96,7 +112,7 @@ ARMOPS_CM4=-mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 -fsingle-p
ARMOPS_CM7=-mcpu=cortex-m7 -mthumb -mfloat-abi=hard -mfpu=fpv5-d16 -fsingle-precision-constant
# CMSIS options for FPU present
-CMSISOPS_FPU_PRESENT = -D__FPU_PRESENT=1 -DARM_MATH_CM4
+CMSISOPS_FPU_PRESENT=-D__FPU_PRESENT=1 -DARM_MATH_CM4
# Common CMSIS options
CMSISOPS=-D__Vendor_SysTickConfig=0
@@ -109,6 +125,7 @@ endif
ifeq ($(FAMILY),gd32f20x)
ARMOPS=$(ARMOPS_CM3)
+ TARGET=gd32f207.bin
endif
ifeq ($(FAMILY),gd32f30x)
@@ -119,11 +136,13 @@ endif
ifeq ($(FAMILY),gd32f4xx)
ARMOPS=$(ARMOPS_CM4)
CMSISOPS+=$(CMSISOPS_FPU_PRESENT)
+ TARGET=gd32f4xx.bin
endif
ifeq ($(FAMILY),gd32h7xx)
ARMOPS=$(ARMOPS_CM7)
CMSISOPS+=-D__FPU_PRESENT=1 -DARM_MATH_CM7
+ TARGET=gd32h7xx.bin
endif
FAMILY_UC=$(shell echo $(FAMILY) | tr a-w A-W)
diff --git a/firmware-template-gd32/Validate.mk b/common/make/gd32/Validate.mk
similarity index 64%
rename from firmware-template-gd32/Validate.mk
rename to common/make/gd32/Validate.mk
index 9531da3..d0e993a 100644
--- a/firmware-template-gd32/Validate.mk
+++ b/common/make/gd32/Validate.mk
@@ -7,6 +7,10 @@ ifeq ($(FLAGS),)
FLAGS:=$(DEFINES)
endif
+ifneq ($(findstring _TIME_STAMP_YEAR_,$(FLAGS)),_TIME_STAMP_YEAR_)
+ DEFINES+=-D_TIME_STAMP_YEAR_=$(shell date +"%Y") -D_TIME_STAMP_MONTH_=$(shell date +"%-m") -D_TIME_STAMP_DAY_=$(shell date +"%-d")
+endif
+
ifneq (,$(findstring OUTPUT_DMX_SEND,$(FLAGS))$(findstring CONFIG_RDM,$(FLAGS))$(findstring RDM_CONTROLLER,$(FLAGS))$(findstring LTC,$(FLAGS)))
TIMER6_HAVE_IRQ_HANDLER=1
ifneq (,$(findstring CONFIG_TIMER6_HAVE_NO_IRQ_HANDLER,$(MAKE_FLAGS)))
@@ -20,8 +24,6 @@ endif
ifneq ($(findstring USE_FREE_RTOS,$(FLAGS)),USE_FREE_RTOS)
DEFINES+=-DCONFIG_HAL_USE_SYSTICK
-else
- DEFINES+=-DCONFIG_HAL_USE_SYSTICK # Temporarily need to fix TIMER10
endif
ifeq ($(findstring ENABLE_TFTP_SERVER,$(FLAGS)),ENABLE_TFTP_SERVER)
@@ -30,4 +32,14 @@ ifeq ($(findstring ENABLE_TFTP_SERVER,$(FLAGS)),ENABLE_TFTP_SERVER)
endif
endif
+ifeq ($(findstring NODE_LTC_SMPTE,$(FLAGS)),NODE_LTC_SMPTE)
+ DEFINES+=-DCONFIG_USE_EXTI10_15_IRQHandler
+endif
+
+ifeq ($(findstring CONFIG_REMOTECONFIG_MINIMUM,$(FLAGS)),CONFIG_REMOTECONFIG_MINIMUM)
+ DEFINES+=-DCONFIG_NET_APPS_NO_MDNS
+else
+ DEFINES+=-DCONFIG_EMAC_HASH_MULTICAST_FILTER
+endif
+
$(info $$DEFINES [${DEFINES}])
diff --git a/common/make/gd32/mbedtls.mk b/common/make/gd32/mbedtls.mk
new file mode 100644
index 0000000..1276e78
--- /dev/null
+++ b/common/make/gd32/mbedtls.mk
@@ -0,0 +1,16 @@
+$(info "mbedtls.mk")
+
+ifeq ($(findstring CONFIG_USE_MBEDTLS,$(DEFINES)), CONFIG_USE_MBEDTLS)
+ USEMBEDTLS=1
+endif
+ifeq ($(findstring CONFIG_USE_MBEDTLS,$(MAKE_FLAGS)), CONFIG_USE_MBEDTLS)
+ USEMBEDTLSS=1
+endif
+
+MBEDTLSS_ROOT=../mbedtls
+
+ifdef USEMBEDTLS
+ INCLUDES+=-I$(MBEDTLSS_ROOT)/mbedtls/include
+endif
+
+$(info $$INCLUDES [${INCLUDES}])
\ No newline at end of file
diff --git a/common/make/lib/Objects.mk b/common/make/lib/Objects.mk
new file mode 100755
index 0000000..37dfa4d
--- /dev/null
+++ b/common/make/lib/Objects.mk
@@ -0,0 +1,19 @@
+$(info "lib/Objects.mk")
+
+C_OBJECTS=$(foreach sdir,$(SRCDIR),$(patsubst $(sdir)/%.c,$(BUILD)$(sdir)/%.o,$(wildcard $(sdir)/*.c)))
+CPP_OBJECTS=$(foreach sdir,$(SRCDIR),$(patsubst $(sdir)/%.cpp,$(BUILD)$(sdir)/%.o,$(wildcard $(sdir)/*.cpp)))
+ASM_OBJECTS=$(foreach sdir,$(SRCDIR),$(patsubst $(sdir)/%.S,$(BUILD)$(sdir)/%.o,$(wildcard $(sdir)/*.S)))
+
+ifneq ($(EXTRA_C_SOURCE_FILES),)
+ EXTRA_C_OBJECTS := $(patsubst %.c,$(BUILD)%.o,$(EXTRA_C_SOURCE_FILES))
+ EXTRA_C_DIRECTORIES := $(sort $(shell dirname $(EXTRA_C_SOURCE_FILES)))
+ EXTRA_C_BUILD_DIRS := $(addprefix $(BUILD),$(EXTRA_C_DIRECTORIES))
+endif
+
+ifneq ($(EXTRA_CPP_SOURCE_FILES),)
+ EXTRA_CPP_OBJECTS := $(patsubst %.cpp,$(BUILD)%.o,$(EXTRA_CPP_SOURCE_FILES))
+ EXTRA_CPP_DIRECTORIES := $(sort $(shell dirname $(EXTRA_CPP_SOURCE_FILES)))
+ EXTRA_CPP_BUILD_DIRS := $(addprefix $(BUILD),$(EXTRA_CPP_DIRECTORIES))
+endif
+
+OBJECTS:=$(strip $(ASM_OBJECTS) $(C_OBJECTS) $(CPP_OBJECTS) $(EXTRA_C_OBJECTS) $(EXTRA_CPP_OBJECTS))
diff --git a/firmware-template-gd32/Includes.mk b/firmware-template-gd32/Includes.mk
deleted file mode 100644
index 71aadc0..0000000
--- a/firmware-template-gd32/Includes.mk
+++ /dev/null
@@ -1,69 +0,0 @@
-$(info "Includes.mk")
-
-INCLUDES:=-I./include -I../include
-INCLUDES+=-I../firmware-template-gd32/include
-INCLUDES+=-I../firmware-template-gd32/template
-INCLUDES+=-I../CMSIS/Core/Include
-INCLUDES+=-I../lib-gd32/${FAMILY}/${FAMILY_UC}_standard_peripheral/Include
-INCLUDES+=-I../lib-gd32/${FAMILY}/CMSIS/GD/${FAMILY_UC}/Include
-INCLUDES+=-I../lib-gd32/include
-INCLUDES+=$(addprefix -I,$(EXTRA_INCLUDES))
-
-ifeq ($(findstring ENABLE_USB_HOST,$(DEFINES)), ENABLE_USB_HOST)
- USB_HOST=1
-endif
-ifeq ($(findstring ENABLE_USB_HOST,$(MAKE_FLAGS)), ENABLE_USB_HOST)
- USB_HOST=1
-endif
-
-ifeq ($(findstring ENABLE_USB_HOST,$(DEFINES)), ENABLE_USB_HOST)
- USB_HOST_MSC=1
-endif
-ifeq ($(findstring ENABLE_USB_HOST,$(MAKE_FLAGS)), ENABLE_USB_HOST)
- USB_HOST_MSC=1
-endif
-
-ifdef USB_HOST
- INCLUDES+=-I../lib-gd32/device/usb
- INCLUDES+=-I../lib-hal/device/usb/host/gd32
-endif
-
-ifeq ($(findstring gd32f20x,$(FAMILY)), gd32f20x)
- ifdef USB_HOST
- INCLUDES+=-I../lib-gd32/${FAMILY}/GD32F20x_usbfs_library/driver/Include
- INCLUDES+=-I../lib-gd32/${FAMILY}/GD32F20x_usbfs_library/host/core/Include
- INCLUDES+=-I../lib-gd32/${FAMILY}/GD32F20x_usbfs_library/ustd/common
- ifdef USB_HOST_MSC
- INCLUDES+=-I../lib-gd32/${FAMILY}/GD32F20x_usbfs_library/host/class/msc/Include
- INCLUDES+=-I../lib-gd32/${FAMILY}/GD32F20x_usbfs_library/ustd/class/msc
- endif
- endif
-endif
-
-ifeq ($(findstring gd32f4xx,$(FAMILY)), gd32f4xx)
- ifdef USB_HOST
- INCLUDES+=-I../lib-gd32/${FAMILY}/GD32F4xx_usb_library/driver/Include
- INCLUDES+=-I../lib-gd32/${FAMILY}/GD32F4xx_usb_library/host/core/Include
- INCLUDES+=-I../lib-gd32/${FAMILY}/GD32F4xx_usb_library/ustd/common
- ifdef USB_HOST_MSC
- INCLUDES+=-I../lib-gd32/${FAMILY}/GD32F4xx_usb_library/host/class/msc/Include
- INCLUDES+=-I../lib-gd32/${FAMILY}/GD32F4xx_usb_library/ustd/class/msc
- endif
- endif
-endif
-
-ifeq ($(findstring gd32h7xx,$(FAMILY)), gd32h7xx)
- ifdef USB_HOST
- INCLUDES+=-I../lib-gd32/${FAMILY}/GD32H7xx_usbhs_library/driver/Include
- INCLUDES+=-I../lib-gd32/${FAMILY}/GD32H7xx_usbhs_library/host/core/Include
- INCLUDES+=-I../lib-gd32/${FAMILY}/GD32H7xx_usbhs_library/ustd/common
- ifdef USB_HOST_MSC
- INCLUDES+=-I../lib-gd32/${FAMILY}/GD32H7xx_usbhs_library/host/class/msc/Include
- INCLUDES+=-I../lib-gd32/${FAMILY}/GD32H7xx_usbhs_library/ustd/class/msc
- endif
- endif
-endif
-
-ifdef USB_HOST_MSC
- INCLUDES+=-I../lib-hal/ff14b/source
-endif
\ No newline at end of file
diff --git a/firmware-template-gd32/Rules.mk b/firmware-template-gd32/Rules.mk
index 9ad6b3b..ac2d1d3 100644
--- a/firmware-template-gd32/Rules.mk
+++ b/firmware-template-gd32/Rules.mk
@@ -9,22 +9,28 @@ LD = $(PREFIX)ld
AR = $(PREFIX)ar
BOARD?=BOARD_GD32F303RC
+MCU?=GD32F303RC
TARGET=$(FAMILY).bin
LIST=$(FAMILY).list
MAP=$(FAMILY).map
SIZE=$(FAMILY).size
BUILD=build_gd32/
-
FIRMWARE_DIR=./../firmware-template-gd32/
+PROJECT=$(notdir $(patsubst %/,%,$(CURDIR)))
+$(info $$PROJECT [${PROJECT}])
+
DEFINES:=$(addprefix -D,$(DEFINES))
-include ../firmware-template-gd32/Board.mk
-include ../firmware-template-gd32/Mcu.mk
+include ../common/make/gd32/Board.mk
+include ../common/make/gd32/Mcu.mk
include ../firmware-template/libs.mk
-include ../firmware-template-gd32/Includes.mk
-include ../firmware-template-gd32/Validate.mk
+include ../common/make/DmxNodeNodeType.mk
+include ../common/make/DmxNodeOutputType.mk
+include ../common/make/gd32/Includes.mk
+include ../common/make/gd32/Validate.mk
+include ../common/make/Timestamp.mk
LIBS+=gd32 clib
@@ -49,27 +55,23 @@ COPS+=-Os -nostartfiles -ffreestanding -nostdlib
COPS+=-fstack-usage
COPS+=-ffunction-sections -fdata-sections
COPS+=-Wall -Werror -Wpedantic -Wextra -Wunused -Wsign-conversion -Wconversion -Wduplicated-cond -Wlogical-op
+COPS+=--specs=nosys.specs
-CPPOPS=-std=c++20
-CPPOPS+=-Wnon-virtual-dtor -Woverloaded-virtual -Wnull-dereference -fno-rtti -fno-exceptions -fno-unwind-tables
-CPPOPS+=-Wuseless-cast -Wold-style-cast
-CPPOPS+=-fno-threadsafe-statics
+include ../common/make/CppOps.mk
LDOPS=--gc-sections --print-gc-sections --print-memory-usage
PLATFORM_LIBGCC+= -L $(shell dirname `$(CC) $(COPS) -print-libgcc-file-name`)
-PLATFORM_LIBC+= -L $(shell dirname `$(CC) $(COPS) --print-file-name=libc.a`)
$(info $$PLATFORM_LIBGCC [${PLATFORM_LIBGCC}])
-$(info $$PLATFORM_LIBC [${PLATFORM_LIBC}])
C_OBJECTS=$(foreach sdir,$(SRCDIR),$(patsubst $(sdir)/%.c,$(BUILD)$(sdir)/%.o,$(wildcard $(sdir)/*.c)))
-C_OBJECTS+=$(foreach sdir,$(SRCDIR),$(patsubst $(sdir)/%.cpp,$(BUILD)$(sdir)/%.o,$(wildcard $(sdir)/*.cpp)))
+CPP_OBJECTS+=$(foreach sdir,$(SRCDIR),$(patsubst $(sdir)/%.cpp,$(BUILD)$(sdir)/%.o,$(wildcard $(sdir)/*.cpp)))
ASM_OBJECTS=$(foreach sdir,$(SRCDIR),$(patsubst $(sdir)/%.S,$(BUILD)$(sdir)/%.o,$(wildcard $(sdir)/*.S)))
BUILD_DIRS:=$(addprefix $(BUILD),$(SRCDIR))
-OBJECTS:=$(ASM_OBJECTS) $(C_OBJECTS)
+OBJECTS:=$(strip $(ASM_OBJECTS) $(C_OBJECTS) $(CPP_OBJECTS))
define compile-objects
$(BUILD)$1/%.o: $1/%.cpp
@@ -106,20 +108,17 @@ clean: $(LIBDEP)
lisdep: $(LIBDEP)
$(LIBDEP):
- $(MAKE) -f Makefile.GD32 $(MAKECMDGOALS) 'FAMILY=${FAMILY}' 'BOARD=${BOARD}' 'MAKE_FLAGS=$(DEFINES)' -C $@
+ $(MAKE) -f Makefile.GD32 $(MAKECMDGOALS) 'PROJECT=${PROJECT}' 'FAMILY=${FAMILY}' 'MCU=${MCU}' 'BOARD=${BOARD}' 'MAKE_FLAGS=$(DEFINES)' -C $@
#
# Build bin
#
-$(BUILD_DIRS) :
- mkdir -p $(BUILD_DIRS)
-
$(BUILD)startup_$(LINE).o : $(FIRMWARE_DIR)/startup_$(LINE).S
$(AS) $(COPS) -D__ASSEMBLY__ -c $(FIRMWARE_DIR)/startup_$(LINE).S -o $(BUILD)startup_$(LINE).o
-$(BUILD)hardfault_handler.o : $(FIRMWARE_DIR)/hardfault_handler.c
- $(CC) $(COPS) -c $(FIRMWARE_DIR)/hardfault_handler.c -o $(BUILD)hardfault_handler.o
+$(BUILD)hardfault_handler.o : $(FIRMWARE_DIR)/hardfault_handler.cpp
+ $(CPP) $(COPS) $(CPPOPS) -c $(FIRMWARE_DIR)/hardfault_handler.cpp -o $(BUILD)hardfault_handler.o
$(BUILD)main.elf: Makefile.GD32 $(LINKER) $(BUILD)startup_$(LINE).o $(BUILD)hardfault_handler.o $(OBJECTS) $(LIBDEP)
$(LD) $(BUILD)startup_$(LINE).o $(BUILD)hardfault_handler.o $(OBJECTS) -Map $(MAP) -T $(LINKER) $(LDOPS) -o $(BUILD)main.elf $(LIBGD32) $(LDLIBS) $(PLATFORM_LIBGCC) -lgcc
diff --git a/firmware-template-gd32/hardfault_handler.c b/firmware-template-gd32/hardfault_handler.c
deleted file mode 100644
index 039d1f9..0000000
--- a/firmware-template-gd32/hardfault_handler.c
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * hardfault_handler.c
- */
-/**
- * Using Cortex-M3/M4/M7 Fault Exceptions
- * MDK Tutorial
- * AN209, Summer 2017, V 5.0
- */
-
-#include
-
-#include "gd32.h"
-
-void HardFault_Handler() {
- __asm volatile(
- "TST LR, #4\n"
- "ITE EQ\n"
- "MRSEQ R0, MSP\n"
- "MRSNE R0, PSP\n"
- "MOV R1, LR\n"
- "B hardfault_handler\n"
- );
-}
-
-void hardfault_handler(unsigned long *hardfault_args, unsigned int lr_value) {
- unsigned long stacked_r0;
- unsigned long stacked_r1;
- unsigned long stacked_r2;
- unsigned long stacked_r3;
- unsigned long stacked_r12;
- unsigned long stacked_lr;
- unsigned long stacked_pc;
- unsigned long stacked_psr;
- unsigned long cfsr;
- unsigned long bus_fault_address;
- unsigned long memmanage_fault_address;
-
- bus_fault_address = SCB->BFAR;
- memmanage_fault_address = SCB->MMFAR;
- cfsr = SCB->CFSR;
-
- stacked_r0 = ((unsigned long) hardfault_args[0]);
- stacked_r1 = ((unsigned long) hardfault_args[1]);
- stacked_r2 = ((unsigned long) hardfault_args[2]);
- stacked_r3 = ((unsigned long) hardfault_args[3]);
- stacked_r12 = ((unsigned long) hardfault_args[4]);
- stacked_lr = ((unsigned long) hardfault_args[5]);
- stacked_pc = ((unsigned long) hardfault_args[6]);
- stacked_psr = ((unsigned long) hardfault_args[7]);
-
- printf("[HardFault]\n");
- printf("- Stack frame:\n");
- printf(" R0 = %x\n", (unsigned int) stacked_r0);
- printf(" R1 = %x\n", (unsigned int) stacked_r1);
- printf(" R2 = %x\n", (unsigned int) stacked_r2);
- printf(" R3 = %x\n", (unsigned int) stacked_r3);
- printf(" R12 = %x\n", (unsigned int) stacked_r12);
- printf(" LR = %x\n", (unsigned int) stacked_lr);
- printf(" PC = %x\n", (unsigned int) stacked_pc);
- printf(" PSR = %x\n", (unsigned int) stacked_psr);
- printf("- FSR/FAR:\n");
- printf(" CFSR = %x\n", (unsigned int) cfsr);
- printf(" HFSR = %x\n", (unsigned int) SCB->HFSR);
- printf(" DFSR = %x\n", (unsigned int) SCB->DFSR);
- printf(" AFSR = %x\n", (unsigned int) SCB->AFSR);
- if (cfsr & 0x0080) {
- printf(" MMFAR = %x\n", (unsigned int) memmanage_fault_address);
- }
- if (cfsr & 0x8000) {
- printf(" BFAR = %x\n", (unsigned int) bus_fault_address);
- }
- printf("- Misc\n");
- printf(" LR/EXC_RETURN= %x\n", lr_value);
-
- while (1)
- ;
-}
diff --git a/firmware-template-gd32/hardfault_handler.cpp b/firmware-template-gd32/hardfault_handler.cpp
new file mode 100644
index 0000000..5b29a63
--- /dev/null
+++ b/firmware-template-gd32/hardfault_handler.cpp
@@ -0,0 +1,72 @@
+/*
+ * hardfault_handler.cpp
+ */
+/**
+ * Using Cortex-M3/M4/M7 Fault Exceptions
+ * MDK Tutorial
+ * AN209, Summer 2017, V 5.0
+ */
+
+#include
+#include
+
+#include "gd32.h"
+
+extern "C" void HardFault_Handler()
+{
+ __asm volatile(
+ "TST LR, #4\n"
+ "ITE EQ\n"
+ "MRSEQ R0, MSP\n"
+ "MRSNE R0, PSP\n"
+ "MOV R1, LR\n"
+ "B HardfaultHandler\n");
+}
+
+extern "C" void HardfaultHandler(uint32_t* hardfault_args, uint32_t lr_value)
+{
+ uint32_t cfsr;
+ uint32_t bus_fault_address;
+ uint32_t memmanage_fault_address;
+
+ bus_fault_address = SCB->BFAR;
+ memmanage_fault_address = SCB->MMFAR;
+ cfsr = SCB->CFSR;
+
+ const auto kStackedR0 = hardfault_args[0];
+ const auto kStackedR1 = hardfault_args[1];
+ const auto kStackedR2 = hardfault_args[2];
+ const auto kStackedR3 = hardfault_args[3];
+ const auto kStackedR12 = hardfault_args[4];
+ const auto kStackedLr = hardfault_args[5];
+ const auto kStackedPc = hardfault_args[6];
+ const auto kStackedPsr = hardfault_args[7];
+
+ printf("[HardFault]\n");
+ printf("- Stack frame:\n");
+ printf(" R0 = %x\n", (unsigned int)kStackedR0);
+ printf(" R1 = %x\n", (unsigned int)kStackedR1);
+ printf(" R2 = %x\n", (unsigned int)kStackedR2);
+ printf(" R3 = %x\n", (unsigned int)kStackedR3);
+ printf(" R12 = %x\n", (unsigned int)kStackedR12);
+ printf(" LR = %x\n", (unsigned int)kStackedLr);
+ printf(" PC = %x\n", (unsigned int)kStackedPc);
+ printf(" PSR = %x\n", (unsigned int)kStackedPsr);
+ printf("- FSR/FAR:\n");
+ printf(" CFSR = %x\n", (unsigned int)cfsr);
+ printf(" HFSR = %x\n", (unsigned int)SCB->HFSR);
+ printf(" DFSR = %x\n", (unsigned int)SCB->DFSR);
+ printf(" AFSR = %x\n", (unsigned int)SCB->AFSR);
+ if (cfsr & 0x0080)
+ {
+ printf(" MMFAR = %x\n", (unsigned int)memmanage_fault_address);
+ }
+ if (cfsr & 0x8000)
+ {
+ printf(" BFAR = %x\n", (unsigned int)bus_fault_address);
+ }
+ printf("- Misc\n");
+ printf(" LR/EXC_RETURN= %x\n", lr_value);
+
+ while (1);
+}
diff --git a/firmware-template-gd32/template/gd32f30x_libopt.h b/firmware-template-gd32/include/gd32f30x_libopt.h
similarity index 100%
rename from firmware-template-gd32/template/gd32f30x_libopt.h
rename to firmware-template-gd32/include/gd32f30x_libopt.h
diff --git a/firmware-template-gd32/include/software_version.h b/firmware-template-gd32/include/software_version.h
index 58b5682..7a743c5 100644
--- a/firmware-template-gd32/include/software_version.h
+++ b/firmware-template-gd32/include/software_version.h
@@ -2,7 +2,7 @@
* @file software_version.h
*
*/
-/* Copyright (C) 2022-2024 by Arjan van Vught mailto:info@gd32-dmx.org
+/* Copyright (C) 2022-2025 by Arjan van Vught mailto:info@gd32-dmx.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -26,6 +26,6 @@
#ifndef SOFTWARE_VERSION_H_
#define SOFTWARE_VERSION_H_
-constexpr char SOFTWARE_VERSION[] = "2.1";
+constexpr char SOFTWARE_VERSION[] = "2.2";
#endif /* SOFTWARE_VERSION_H_ */
diff --git a/firmware-template-gd32/lib/Rules.mk b/firmware-template-gd32/lib/Rules.mk
index 0c05259..d6c09c2 100644
--- a/firmware-template-gd32/lib/Rules.mk
+++ b/firmware-template-gd32/lib/Rules.mk
@@ -10,69 +10,63 @@ LD = $(PREFIX)ld
AR = $(PREFIX)ar
BOARD?=BOARD_GD32F303RC
+MCU?=GD32F303RC
$(info $$BOARD [${BOARD}])
SRCDIR=src src/gd32 $(EXTRA_SRCDIR)
DEFINES:=$(addprefix -D,$(DEFINES))
-DEFINES+=-D_TIME_STAMP_YEAR_=$(shell date +"%Y") -D_TIME_STAMP_MONTH_=$(shell date +"%-m") -D_TIME_STAMP_DAY_=$(shell date +"%-d")
-include ../firmware-template-gd32/Board.mk
-include ../firmware-template-gd32/Mcu.mk
-include ../firmware-template-gd32/Includes.mk
-include ../firmware-template-gd32/Validate.mk
+include ../common/make/gd32/Board.mk
+include ../common/make/gd32/Mcu.mk
+include ../common/make/DmxNodeNodeType.mk
+include ../common/make/DmxNodeOutputType.mk
+include ../common/make/gd32/Includes.mk
+include ../common/make/gd32/Validate.mk
-INCLUDES+=-I../lib-configstore/include -I../lib-device/include -I../lib-display/include -I../lib-flash/include -I../lib-flashcode/include -I../lib-hal/include -I../lib-lightset/include -I../lib-network/include
-
-$(info $$DEFINES [${DEFINES}])
-$(info $$MAKE_FLAGS [${MAKE_FLAGS}])
+INCLUDES+=-I../lib-configstore/include -I../lib-device/include -I../lib-display/include -I../lib-flash/include -I../lib-flashcode/include -I../lib-hal/include -I../lib-network/include
COPS=-DGD32 -D$(FAMILY_UCA) -D$(LINE_UC) -D$(MCU) -D$(BOARD)
-COPS+=$(strip $(DEFINES)) $(MAKE_FLAGS) $(INCLUDES)
+COPS+=$(strip $(DEFINES) $(MAKE_FLAGS) $(VALIDATE_FLAGS) $(INCLUDES))
COPS+=$(strip $(ARMOPS) $(CMSISOPS))
COPS+=-Os -nostartfiles -ffreestanding -nostdlib
COPS+=-fstack-usage
COPS+=-ffunction-sections -fdata-sections
-COPS+=-Wall -Werror -Wpedantic -Wextra -Wunused -Wsign-conversion -Wconversion -Wduplicated-cond -Wlogical-op
+COPS+=-Wall -Werror -Wpedantic -Wextra -Wunused -Wsign-conversion -Wduplicated-cond -Wlogical-op
+ifndef FREE_RTOS_PORTABLE
+COPS+=-Wconversion
+endif
-CPPOPS=-std=c++20
-CPPOPS+=-Wnon-virtual-dtor -Woverloaded-virtual -Wnull-dereference -fno-rtti -fno-exceptions -fno-unwind-tables
-CPPOPS+=-Wuseless-cast -Wold-style-cast
-CPPOPS+=-fno-threadsafe-statics
-
-CURR_DIR:=$(notdir $(patsubst %/,%,$(CURDIR)))
-LIB_NAME:=$(patsubst lib-%,%,$(CURR_DIR))
+include ../common/make/CppOps.mk
BUILD=build_gd32/
BUILD_DIRS:=$(addprefix build_gd32/,$(SRCDIR))
-$(info $$BUILD_DIRS [${BUILD_DIRS}])
-
-C_OBJECTS=$(foreach sdir,$(SRCDIR),$(patsubst $(sdir)/%.c,$(BUILD)$(sdir)/%.o,$(wildcard $(sdir)/*.c)))
-CPP_OBJECTS=$(foreach sdir,$(SRCDIR),$(patsubst $(sdir)/%.cpp,$(BUILD)$(sdir)/%.o,$(wildcard $(sdir)/*.cpp)))
-ASM_OBJECTS=$(foreach sdir,$(SRCDIR),$(patsubst $(sdir)/%.S,$(BUILD)$(sdir)/%.o,$(wildcard $(sdir)/*.S)))
-EXTRA_C_OBJECTS=$(patsubst %.c,$(BUILD)%.o,$(EXTRA_C_SOURCE_FILES))
-EXTRA_C_DIRECTORIES=$(shell dirname $(EXTRA_C_SOURCE_FILES))
-EXTRA_BUILD_DIRS:=$(addsuffix $(EXTRA_C_DIRECTORIES), $(BUILD))
+include ../common/make/lib/Objects.mk
-OBJECTS:=$(strip $(ASM_OBJECTS) $(C_OBJECTS) $(CPP_OBJECTS) $(EXTRA_C_OBJECTS))
+CURR_DIR:=$(notdir $(patsubst %/,%,$(CURDIR)))
+LIB_NAME:=$(patsubst lib-%,%,$(CURR_DIR))
+TARGET=lib_gd32/lib$(LIB_NAME).a
+$(info $$BUILD_DIRS [${BUILD_DIRS}])
+$(info $$EXTRA_C_BUILD_DIRS [${EXTRA_C_BUILD_DIRS}])
+$(info $$EXTRA_CPP_BUILD_DIRS [${EXTRA_CPP_BUILD_DIRS}])
+$(info $$DEFINES [${DEFINES}])
+$(info $$MAKE_FLAGS [${MAKE_FLAGS}])
$(info $$OBJECTS [${OBJECTS}])
-
-TARGET=lib_gd32/lib$(LIB_NAME).a
$(info $$TARGET [${TARGET}])
-LIST=lib.list
-
define compile-objects
$(info $1)
$(BUILD)$1/%.o: $1/%.c
- $(CC) $(COPS) -c $$< -o $$@
-
+ $(CC) -MD -MP $(COPS) -c $$< -o $$@
+
$(BUILD)$1/%.o: $1/%.cpp
- $(CPP) $(COPS) $(CPPOPS) -c $$< -o $$@
-
+ $(CPP) -MD -MP $(COPS) $(CPPOPS) -c $$< -o $$@
+
+-include $(BUILD)$1/*.d
+
$(BUILD)$1/%.o: $1/%.S
$(CC) $(COPS) -D__ASSEMBLY__ -c $$< -o $$@
endef
@@ -82,20 +76,20 @@ all : builddirs $(TARGET)
.PHONY: clean builddirs
builddirs:
- mkdir -p $(BUILD_DIRS)
- mkdir -p $(EXTRA_BUILD_DIRS)
+ mkdir -p $(BUILD_DIRS)
+ if [[ -n "${EXTRA_C_BUILD_DIRS}" ]]; then mkdir -p $(EXTRA_C_BUILD_DIRS); fi
+ if [[ -n "${EXTRA_CPP_BUILD_DIRS}" ]]; then mkdir -p $(EXTRA_CPP_BUILD_DIRS); fi
mkdir -p lib_gd32
clean:
rm -rf build_gd32
rm -rf lib_gd32
-$(BUILD_DIRS) :
- mkdir -p $(BUILD_DIRS)
- mkdir -p lib_gd32
+$(BUILD)%.o: %.c
+ $(CC) $(COPS) -c $< -o $@
$(TARGET): Makefile.GD32 $(OBJECTS)
$(AR) -r $(TARGET) $(OBJECTS)
- $(PREFIX)objdump -d $(TARGET) | $(PREFIX)c++filt > lib_gd32/$(LIST)
+ $(PREFIX)objdump -d $(TARGET) | $(PREFIX)c++filt > lib_gd32/lib.list
$(foreach bdir,$(SRCDIR),$(eval $(call compile-objects,$(bdir))))
\ No newline at end of file
diff --git a/firmware-template/libs.mk b/firmware-template/libs.mk
index 395670d..6dc1cbc 100644
--- a/firmware-template/libs.mk
+++ b/firmware-template/libs.mk
@@ -11,10 +11,6 @@ ifeq ($(findstring RDM_RESPONDER,$(DEFINES)),RDM_RESPONDER)
endif
endif
-ifeq ($(findstring NODE_DMX,$(DEFINES)),NODE_DMX)
- LIBS+=
-endif
-
ifeq ($(findstring OUTPUT_DMX_SEND,$(DEFINES)),OUTPUT_DMX_SEND)
LIBS+=
endif
@@ -26,15 +22,13 @@ ifeq ($(findstring ENABLE_RDM_SUBDEVICES,$(DEFINES)),ENABLE_RDM_SUBDEVICES)
endif
ifeq ($(findstring OUTPUT_DMX_PIXEL,$(DEFINES)),OUTPUT_DMX_PIXEL)
- LIBS+=ws28xxdmx ws28xx
+ LIBS+=dmxled pixeldmx pixel
endif
-LIBS+=network
-
ifeq ($(findstring DISPLAY_UDF,$(DEFINES)),DISPLAY_UDF)
LIBS+=displayudf
endif
-LIBS+=configstore flashcode properties lightset display hal
+LIBS+=configstore flashcode display hal
$(info $$LIBS [${LIBS}])
\ No newline at end of file
diff --git a/gd32_dmx_usb_pro/.cproject b/gd32_dmx_usb_pro/.cproject
index 7ebf6dc..c2fee85 100755
--- a/gd32_dmx_usb_pro/.cproject
+++ b/gd32_dmx_usb_pro/.cproject
@@ -101,7 +101,6 @@
-
@@ -116,7 +115,6 @@
-
@@ -132,7 +130,6 @@
@@ -116,7 +115,6 @@
-
@@ -132,7 +130,6 @@