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 @@ diff --git a/lib-configstore/.settings/language.settings.xml b/lib-configstore/.settings/language.settings.xml index 1b9626a..9c584fa 100755 --- a/lib-configstore/.settings/language.settings.xml +++ b/lib-configstore/.settings/language.settings.xml @@ -5,7 +5,7 @@ - + @@ -14,13 +14,14 @@ - + - + + \ No newline at end of file diff --git a/lib-configstore/Makefile.GD32 b/lib-configstore/Makefile.GD32 index feb2b55..18dcaa3 100644 --- a/lib-configstore/Makefile.GD32 +++ b/lib-configstore/Makefile.GD32 @@ -1,11 +1,16 @@ DEFINES =NDEBUG ifneq ($(MAKE_FLAGS),) + ifneq (,$(findstring CONFIG_STORE_USE_RAM,$(MAKE_FLAGS))) + EXTRA_SRCDIR+=device/gd32/ram + endif + ifneq (,$(findstring CONFIG_STORE_USE_ROM,$(MAKE_FLAGS))) + EXTRA_SRCDIR+=device/gd32/rom + endif else DEFINES+=CONFIG_STORE_USE_RAM + EXTRA_SRCDIR=device/gd32/ram endif -EXTRA_SRCDIR=device/ram/gd32 - include Rules.mk include ../firmware-template-gd32/lib/Rules.mk diff --git a/lib-configstore/Rules.mk b/lib-configstore/Rules.mk index af70068..300242f 100755 --- a/lib-configstore/Rules.mk +++ b/lib-configstore/Rules.mk @@ -1,6 +1,6 @@ $(info $$MAKE_FLAGS [${MAKE_FLAGS}]) -EXTRA_INCLUDES+=../lib-properties/include +EXTRA_INCLUDES+= ifneq ($(MAKE_FLAGS),) ifneq (,$(findstring CONFIG_STORE_USE_FILE,$(MAKE_FLAGS))) @@ -11,21 +11,12 @@ ifneq ($(MAKE_FLAGS),) EXTRA_SRCDIR+=device/i2c endif - ifneq (,$(findstring CONFIG_STORE_USE_RAM,$(MAKE_FLAGS))) - EXTRA_SRCDIR+=device/ram - endif - - ifneq (,$(findstring CONFIG_STORE_USE_ROM,$(MAKE_FLAGS))) - EXTRA_SRCDIR+=device/rom - endif - + ifneq (,$(findstring CONFIG_STORE_USE_SPI,$(MAKE_FLAGS))) EXTRA_SRCDIR+=device/spi endif else EXTRA_SRCDIR+=device/file EXTRA_SRCDIR+=device/i2c - EXTRA_SRCDIR+=device/ram - EXTRA_SRCDIR+=device/rom EXTRA_SRCDIR+=device/spi endif diff --git a/lib-configstore/device/gd32/ram/storedevice.cpp b/lib-configstore/device/gd32/ram/storedevice.cpp new file mode 100644 index 0000000..648266b --- /dev/null +++ b/lib-configstore/device/gd32/ram/storedevice.cpp @@ -0,0 +1,94 @@ +/** + * @file storedevice.cpp + * + */ +/* 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 + * 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 +#include +#include + +#include "configstoredevice.h" +#include "gd32.h" + +#include "firmware/debug/debug_debug.h" + +static constexpr uint32_t kFlashSectorSize = 4096U; +static constexpr uint32_t kBsramSize = 4096U; + +StoreDevice::StoreDevice() +{ + DEBUG_ENTRY(); + + detected_ = true; + + printf("StoreDevice: BSRAM with total %d bytes [%d kB]\n", GetSize(), GetSize() / 1024U); + DEBUG_EXIT(); +} + +StoreDevice::~StoreDevice(){DEBUG_ENTRY(); + + DEBUG_EXIT();} + +uint32_t StoreDevice::GetSize() const +{ + return kBsramSize; +} + +uint32_t StoreDevice::GetSectorSize() const +{ + return kFlashSectorSize; +} + +bool StoreDevice::Read(__attribute__((unused)) uint32_t offset, __attribute__((unused)) uint32_t length, __attribute__((unused)) uint8_t* buffer, storedevice::Result& result) +{ + DEBUG_ENTRY(); + DEBUG_PRINTF("offset=%p[%d], len=%u[%d], data=%p[%d]", offset, (((uint32_t)(offset) & 0x3) == 0), length, (((uint32_t)(length) & 0x3) == 0), buffer, (((uint32_t)(buffer) & 0x3) == 0)); + assert((offset + length) <= BSRAM_SIZE); + + result = storedevice::Result::kOk; + + DEBUG_EXIT(); + return true; +} + +bool StoreDevice::Erase(__attribute__((unused)) uint32_t offset, __attribute__((unused)) uint32_t length, storedevice::Result& result) +{ + DEBUG_ENTRY(); + + result = storedevice::Result::kOk; + + DEBUG_EXIT(); + return true; +} + +bool StoreDevice::Write(__attribute__((unused)) uint32_t offset, __attribute__((unused)) uint32_t length, __attribute__((unused)) const uint8_t* buffer, storedevice::Result& result) +{ + DEBUG_ENTRY(); + DEBUG_PRINTF("offset=%p[%d], len=%u[%d], data=%p[%d]", offset, (((uint32_t)(offset) & 0x3) == 0), length, (((uint32_t)(length) & 0x3) == 0), buffer, (((uint32_t)(buffer) & 0x3) == 0)); + assert((offset + length) <= BSRAM_SIZE); + + result = storedevice::Result::kOk; + + DEBUG_EXIT(); + return true; +} diff --git a/lib-configstore/device/gd32/rom/storedevice.cpp b/lib-configstore/device/gd32/rom/storedevice.cpp new file mode 100755 index 0000000..7ce5123 --- /dev/null +++ b/lib-configstore/device/gd32/rom/storedevice.cpp @@ -0,0 +1,94 @@ +/** + * @file storedevice.cpp + * + */ +/* 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 + * 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 + +#include "configstoredevice.h" +#include "flashcode.h" +#include "firmware/debug/debug_debug.h" + +StoreDevice::StoreDevice() +{ + DEBUG_ENTRY(); + + detected_ = FlashCode::IsDetected(); + + DEBUG_EXIT(); +} + +StoreDevice::~StoreDevice() +{ + DEBUG_ENTRY(); + DEBUG_EXIT(); +} + +uint32_t StoreDevice::GetSize() const +{ + return FlashCode::GetSize(); +} + +uint32_t StoreDevice::GetSectorSize() const +{ + return FlashCode::GetSectorSize(); +} + +bool StoreDevice::Read(uint32_t offset, uint32_t length, uint8_t* buffer, storedevice::Result& result) +{ + DEBUG_ENTRY(); + + flashcode::Result flashrom_result; + const auto kState = FlashCode::Read(offset, length, buffer, flashrom_result); + + result = static_cast(flashrom_result); + + DEBUG_EXIT(); + return kState; +} + +bool StoreDevice::Erase(uint32_t offset, uint32_t length, storedevice::Result& result) +{ + DEBUG_ENTRY(); + + flashcode::Result flashrom_result; + const auto kState = FlashCode::Erase(offset, length, flashrom_result); + + result = static_cast(flashrom_result); + + DEBUG_EXIT(); + return kState; +} + +bool StoreDevice::Write(uint32_t offset, uint32_t length, const uint8_t* buffer, storedevice::Result& result) +{ + DEBUG_ENTRY(); + + flashcode::Result flashrom_result; + const auto kState = FlashCode::Write(offset, length, buffer, flashrom_result); + + result = static_cast(flashrom_result); + + DEBUG_EXIT(); + return kState; +} diff --git a/lib-configstore/device/i2c/storedevice.cpp b/lib-configstore/device/i2c/storedevice.cpp new file mode 100755 index 0000000..901a2b9 --- /dev/null +++ b/lib-configstore/device/i2c/storedevice.cpp @@ -0,0 +1,113 @@ +/** + * @file storedevice.cpp + * + */ +/* 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 + * 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(DEBUG_CONFIGSTORE) +#undef NDEBUG +#endif + +#include +#include +#include + +#include "configstoredevice.h" +#include "i2c/at24cxx.h" + #include "firmware/debug/debug_debug.h" + +namespace storedevice +{ +#if !defined(CONFIG_FLASHROM_I2C_INDEX) +#define CONFIG_FLASHROM_I2C_INDEX 0 +#endif +static constexpr uint8_t kI2CIndex = CONFIG_FLASHROM_I2C_INDEX; +/* Backwards compatibility with SPI FLASH */ +static constexpr auto kFlashSectorSize = 4096U; +static constexpr auto kRomSize = 4096U; +} // namespace storedevice + +StoreDevice::StoreDevice() : AT24C32(storedevice::kI2CIndex) +{ + DEBUG_ENTRY(); + + detected_ = AT24C32::IsConnected(); + + if (!detected_) + { + printf("StoreDevice: No AT24C32 at %2x", AT24C32::GetAddress()); + } + else + { + printf("StoreDevice: AT24C32 total %u bytes [%u kB]\n", GetSize(), GetSize() / 1024U); + } + + DEBUG_EXIT(); +} + +StoreDevice::~StoreDevice(){DEBUG_ENTRY(); DEBUG_EXIT();} + +uint32_t StoreDevice::GetSize() const +{ + return storedevice::kRomSize; +} + +uint32_t StoreDevice::GetSectorSize() const +{ + return storedevice::kFlashSectorSize; +} + +bool StoreDevice::Read(uint32_t offset, uint32_t length, uint8_t* buffer, storedevice::Result& result) +{ + DEBUG_ENTRY(); + assert((offset + length) <= storedevice::ROM_SIZE); + + AT24C32::Read(offset, buffer, length); + + result = storedevice::Result::kOk; + + DEBUG_EXIT(); + return true; +} + +bool StoreDevice::Erase([[maybe_unused]] uint32_t offset, [[maybe_unused]] uint32_t length, storedevice::Result& result) +{ + DEBUG_ENTRY(); + + result = storedevice::Result::kOk; + + DEBUG_EXIT(); + return true; +} + +bool StoreDevice::Write(uint32_t offset, uint32_t length, const uint8_t* buffer, storedevice::Result& result) +{ + DEBUG_ENTRY(); + assert((offset + length) <= ROM_SIZE); + + AT24C32::Write(offset, buffer, length); + + result = storedevice::Result::kOk; + + DEBUG_EXIT(); + return true; +} diff --git a/lib-configstore/device/rom/storedevice.cpp b/lib-configstore/device/rom/storedevice.cpp deleted file mode 100755 index f06bad4..0000000 --- a/lib-configstore/device/rom/storedevice.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/** - * @file storedevice.cpp - * - */ -/* Copyright (C) 2022 by Arjan van Vught mailto:info@orangepi-dmx.nl - * - * 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 -#include - -#include "configstoredevice.h" -#include "flashcode.h" - -#include "debug.h" - -StoreDevice::StoreDevice() { - DEBUG_ENTRY - - m_IsDetected = FlashCode::IsDetected(); - - DEBUG_EXIT -} - -StoreDevice::~StoreDevice() { - DEBUG_ENTRY - - DEBUG_EXIT -} - -uint32_t StoreDevice::GetSize() const { - return FlashCode::GetSize(); -} - -uint32_t StoreDevice::GetSectorSize() const { - return FlashCode::GetSectorSize(); -} - -bool StoreDevice::Read(uint32_t nOffset, uint32_t nLength, uint8_t *pBuffer, storedevice::result& nResult) { - DEBUG_ENTRY - - flashcode::result flashromResult; - const auto state = FlashCode::Read(nOffset, nLength, pBuffer, flashromResult); - - nResult = static_cast(flashromResult); - - DEBUG_EXIT - return state; -} - -bool StoreDevice::Erase(uint32_t nOffset, uint32_t nLength, storedevice::result& nResult) { - DEBUG_ENTRY - - flashcode::result flashromResult; - const auto state = FlashCode::Erase(nOffset, nLength, flashromResult); - - nResult = static_cast(flashromResult); - - DEBUG_EXIT - return state; -} - -bool StoreDevice::Write(uint32_t nOffset, uint32_t nLength, const uint8_t *pBuffer, storedevice::result& nResult) { - DEBUG_ENTRY - - flashcode::result flashromResult; - const auto state = FlashCode::Write(nOffset, nLength, pBuffer, flashromResult); - - nResult = static_cast(flashromResult); - - DEBUG_EXIT - return state; -} diff --git a/lib-configstore/device/spi/storedevice.cpp b/lib-configstore/device/spi/storedevice.cpp index cb3078f..97e65a9 100755 --- a/lib-configstore/device/spi/storedevice.cpp +++ b/lib-configstore/device/spi/storedevice.cpp @@ -2,7 +2,7 @@ * @file storedevice.cpp * */ -/* Copyright (C) 2022-2024 by Arjan van Vught mailto:info@orangepi-dmx.nl +/* 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 @@ -23,74 +23,76 @@ * THE SOFTWARE. */ +#if defined(DEBUG_CONFIGSTORE) +#undef NDEBUG +#endif + #include #include #include "configstoredevice.h" #include "spi/spi_flash.h" - -#include "debug.h" - -StoreDevice::StoreDevice() { - DEBUG_ENTRY - - if (spi_flash_probe(0, 0, 0) < 0) { - DEBUG_PUTS("No SPI flash chip"); - } else { - printf("StoreDevice: Detected %s with sector size %u total %u bytes [%u kB]\n", - spi_flash_get_name(), - static_cast(spi_flash_get_sector_size()), - static_cast(spi_flash_get_size()), - static_cast(spi_flash_get_size() / 1024U)); - m_IsDetected = true; - } - - DEBUG_EXIT + #include "firmware/debug/debug_debug.h" + +StoreDevice::StoreDevice() +{ + DEBUG_ENTRY(); + + if (!spi_flash_probe()) + { + puts("StoreDevice: No SPI flash chip."); + } + else + { + printf("StoreDevice: %s sector size %u total %u bytes [%u kB]\n", spi_flash_get_name(), static_cast(spi_flash_get_sector_size()), + static_cast(spi_flash_get_size()), static_cast(spi_flash_get_size() / 1024U)); + detected_ = true; + } + + DEBUG_EXIT(); } -StoreDevice::~StoreDevice() { - DEBUG_ENTRY - - DEBUG_EXIT -} +StoreDevice::~StoreDevice(){DEBUG_ENTRY(); DEBUG_EXIT();} -uint32_t StoreDevice::GetSize() const { - return spi_flash_get_size(); +uint32_t StoreDevice::GetSize() const +{ + return spi_flash_get_size(); } -uint32_t StoreDevice::GetSectorSize() const { - return spi_flash_get_sector_size(); +uint32_t StoreDevice::GetSectorSize() const +{ + return spi_flash_get_sector_size(); } -bool StoreDevice::Read(uint32_t nOffset, uint32_t nLength, uint8_t *pBuffer, storedevice::result& nResult) { - DEBUG_ENTRY +bool StoreDevice::Read(uint32_t offset, uint32_t length, uint8_t* buffer, storedevice::Result& result) +{ + DEBUG_ENTRY(); - const auto nReturn = spi_flash_cmd_read_fast(nOffset, nLength, pBuffer); - nResult = (nReturn < 0) ? storedevice::result::ERROR : storedevice::result::OK; + result = spi_flash_cmd_read_fast(offset, length, buffer) ? storedevice::Result::kOk : storedevice::Result::kError; - DEBUG_PRINTF("nResult=%d", static_cast(nResult)); - DEBUG_EXIT - return true; + DEBUG_PRINTF("result=%d", static_cast(result)); + DEBUG_EXIT(); + return true; } -bool StoreDevice::Erase(uint32_t nOffset, uint32_t nLength, storedevice::result& nResult) { - DEBUG_ENTRY +bool StoreDevice::Erase(uint32_t offset, uint32_t length, storedevice::Result& result) +{ + DEBUG_ENTRY(); - const auto nReturn = spi_flash_cmd_erase(nOffset, nLength); - nResult = (nReturn < 0) ? storedevice::result::ERROR : storedevice::result::OK; + result = spi_flash_cmd_erase(offset, length) ? storedevice::Result::kOk : storedevice::Result::kError; - DEBUG_PRINTF("nResult=%d", static_cast(nResult)); - DEBUG_EXIT - return true; + DEBUG_PRINTF("result=%d", static_cast(result)); + DEBUG_EXIT(); + return true; } -bool StoreDevice::Write(uint32_t nOffset, uint32_t nLength, const uint8_t *pBuffer, storedevice::result& nResult) { - DEBUG_ENTRY +bool StoreDevice::Write(uint32_t offset, uint32_t length, const uint8_t* buffer, storedevice::Result& result) +{ + DEBUG_ENTRY(); - const auto nReturn = spi_flash_cmd_write_multi(nOffset, nLength, pBuffer); - nResult = (nReturn < 0) ? storedevice::result::ERROR : storedevice::result::OK; + result = spi_flash_cmd_write_multi(offset, length, buffer) ? storedevice::Result::kOk : storedevice::Result::kError; - DEBUG_PRINTF("nResult=%d", static_cast(nResult)); - DEBUG_EXIT - return true; + DEBUG_PRINTF("result=%d", static_cast(result)); + DEBUG_EXIT(); + return true; } diff --git a/lib-configstore/include/configstore.h b/lib-configstore/include/configstore.h old mode 100644 new mode 100755 index bb69861..8dc7cef --- a/lib-configstore/include/configstore.h +++ b/lib-configstore/include/configstore.h @@ -2,7 +2,7 @@ * @file configstore.h * */ -/* Copyright (C) 2018-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 @@ -31,156 +31,660 @@ #include #include "configstoredevice.h" +#include "configurationstore.h" +#include "global.h" +#include "softwaretimers.h" + #include "firmware/debug/debug_debug.h" -#include "utc.h" - -#include "debug.h" - -namespace configstore { -enum class Store { - NETWORK, - DMXSEND, - WS28XXDMX, - LTC, - MIDI, - LTCETC, - OSC, - TLC5711DMX, - WIDGET, - RDMDEVICE, - RCONFIG, - TCNET, - OSC_CLIENT, - DISPLAYUDF, - LTCDISPLAY, - MONITOR, - SPARKFUN, - SLUSH, - MOTORS, - SHOW, - SERIAL, - RDMSENSORS, - RDMSUBDEVICES, - GPS, - RGBPANEL, - NODE, - PCA9685, - LAST -}; - -enum class State { - IDLE, CHANGED, CHANGED_WAITING, ERASING, ERASED, ERASED_WAITING, WRITING -}; -} // namespace configstore - -class ConfigStore: StoreDevice { -public: - ConfigStore(); - ~ConfigStore() { - while (Flash()) - ; - } - - void Update(configstore::Store store, uint32_t nOffset, const void *pData, uint32_t nDataLength, uint32_t nSetList = 0, uint32_t nOffsetSetList = 0); - void Update(configstore::Store store, const void *pData, uint32_t nDataLength) { - Update(store, 0, pData, nDataLength); - } - - void Copy(const configstore::Store store, void *pData, uint32_t nDataLength, uint32_t nOffset = 0, const bool doUpdate = true); - - void ResetSetList(configstore::Store store); - void ResetSetListAll() { - for (uint32_t i = 0; i < static_cast(configstore::Store::LAST); i++) { - ResetSetList(static_cast(i)); - } - } - - bool Flash(); - - void Dump(); - - void Delay(); - - /* - * Environment - */ - - bool SetEnvUtcOffset(const int8_t nHours, const uint8_t nMinutes) { - int32_t nUtcOffset; - - DEBUG_PRINTF("nHours=%d, nMinutes =%u", nHours, nMinutes); - - if (hal::utc_validate(nHours, nMinutes, nUtcOffset)) { - auto *p = reinterpret_cast(&s_SpiFlashData[FlashStore::SIGNATURE_SIZE]); - - if (p->nUtcOffset != nUtcOffset) { - p->nUtcOffset = nUtcOffset; - s_State = configstore::State::CHANGED; - } - - DEBUG_EXIT - return true; - } +class ConfigStore : StoreDevice +{ + static constexpr uint32_t kStoreSize = 4 * 1024; + static constexpr uint8_t kMagicNumber[configurationstore::kMagicNumberSize] = {'A', 'v', 'V', '\0'}; + static constexpr uint8_t kVersion[configurationstore::kVersionSize] = {0, 1}; - DEBUG_EXIT - return false; - } + static_assert(sizeof(ConfigurationStore) <= kStoreSize); - void GetEnvUtcOffset(int8_t& nHours, uint8_t& nMinutes) { - const auto *p = reinterpret_cast(&s_SpiFlashData[FlashStore::SIGNATURE_SIZE]); - - DEBUG_PRINTF("p->nUtcOffset=%d", p->nUtcOffset); - - assert((p->nUtcOffset / 3600) <= INT8_MAX); - assert((p->nUtcOffset / 3600) >= INT8_MIN); - - nHours = static_cast(p->nUtcOffset / 3600); - - if (nHours > 0) { - nMinutes = static_cast(static_cast(p->nUtcOffset - (nHours * 3600)) / 60U); - } else { - nMinutes = static_cast(static_cast((nHours * 3600) - p->nUtcOffset) / 60U); - } - } - - int32_t GetEnvUtcOffset() const { - const auto *p = reinterpret_cast(&s_SpiFlashData[FlashStore::SIGNATURE_SIZE]); - return p->nUtcOffset; - } - - static ConfigStore *Get() { - return s_pThis; - } - -private: - uint32_t GetStoreOffset(configstore::Store tStore); - -private: - struct Env { - int32_t nUtcOffset; - uint8_t filler[12]; - }; - - struct FlashStore { - static constexpr uint32_t SIGNATURE_SIZE = 16; - static constexpr uint32_t ENV_SIZE = 16; - static constexpr uint32_t OFFSET_STORES = SIGNATURE_SIZE + ENV_SIZE; - static constexpr uint32_t SIZE = 4096; - }; - - static_assert(sizeof(struct Env) == FlashStore::ENV_SIZE, ""); - - static bool s_bHaveFlashChip; - - static configstore::State s_State; - static uint32_t s_nStartAddress; - - static uint32_t s_nSpiFlashStoreSize; - static uint8_t s_SpiFlashData[FlashStore::SIZE]; - - static uint32_t s_nWaitMillis; - - static ConfigStore *s_pThis; + enum class State + { + kIdle, + kChanged, + kChangedWaiting, + kErasing, + kErased, + kErasedWaiting, + kWriting + }; + + [[maybe_unused]] static constexpr char kStateNames[7][16] = {"IDLE", "CHANGED", "CHANGED_WAITING", "ERASING", "ERASED", "ERASED_WAITING", "WRITING"}; + + public: + ConfigStore() + { + DEBUG_ENTRY(); + + assert(s_this == nullptr); + s_this = this; + + memset(s_store, 0, sizeof(s_store)); + + s_have_device = StoreDevice::IsDetected(); + + DEBUG_PRINTF("s_have_device=%u", s_have_device); + + if (s_have_device) + { + assert(kStoreSize <= StoreDevice::GetSize()); + + const auto kEraseSize = StoreDevice::GetSectorSize(); + assert(kEraseSize <= kStoreSize); + + const auto kSectors = kStoreSize / kEraseSize; + + DEBUG_PRINTF("kStoreSize=%u, kEraseSize=%u, kSectors=%u", kStoreSize, kEraseSize, kSectors); + + assert((kSectors * kEraseSize) <= StoreDevice::GetSize()); + + s_start_address = StoreDevice::GetSize() - (kSectors * kEraseSize); + + DEBUG_PRINTF("s_start_address=%p", reinterpret_cast(s_start_address)); + + storedevice::Result result; + while (!StoreDevice::Read(s_start_address, kStoreSize, reinterpret_cast(&s_store), result)); + assert(result == storedevice::Result::kOk); + } + + auto* store = GetStore(); + + if (!IsValid()) + { + DEBUG_PUTS("Wrong Magic number or version"); + + memset(s_store, 0, sizeof(s_store)); + memcpy(store->magic_number, &kMagicNumber, sizeof(kMagicNumber)); + memcpy(store->version, &kVersion, sizeof(kVersion)); + + SetStatusChanged(); + } + + // Set global + Global::Instance().SetUtcOffsetIfValid(store->global.utc_offset); + + DEBUG_EXIT(); + } + + ConfigStore(const ConfigStore&) = delete; + ConfigStore& operator=(const ConfigStore&) = delete; + ConfigStore(ConfigStore&&) = delete; + ConfigStore& operator=(ConfigStore&&) = delete; + + ~ConfigStore() = default; + + bool Commit() { return Flash(); } + + template void Copy(TMember* dest, const TMember ConfigurationStore::* member) + { + assert(dest != nullptr); + memcpy(dest, &(GetStore()->*member), sizeof(TMember)); + } + + template void Store(const TMember* source, TMember ConfigurationStore::* member) + { + assert(source != nullptr); + + auto* destination = &(GetStore()->*member); + + if (__builtin_memcmp(destination, source, sizeof(TMember)) != 0) + { + __builtin_memcpy(destination, source, sizeof(TMember)); + SetStatusChanged(); + } + } + +#define DEFINE_STORE_GETTERS_HELPERS(HumanName, StoreName, StoreType) \ + template TField HumanName##Get(TField common::store::StoreType::* pField) const { return Get(GetStore()->StoreName, pField); } + + DEFINE_STORE_GETTERS_HELPERS(Global, global, Global) + DEFINE_STORE_GETTERS_HELPERS(Network, network, Network) + DEFINE_STORE_GETTERS_HELPERS(RemoteConfig, remote_config, RemoteConfig) + DEFINE_STORE_GETTERS_HELPERS(DisplayUdf, display_udf, DisplayUdf) + DEFINE_STORE_GETTERS_HELPERS(DmxNode, dmx_node, DmxNode) + DEFINE_STORE_GETTERS_HELPERS(OscClient, osc_client, OscClient) + DEFINE_STORE_GETTERS_HELPERS(OscServer, osc_server, OscServer) + DEFINE_STORE_GETTERS_HELPERS(DmxSend, dmx_send, DmxSend) + DEFINE_STORE_GETTERS_HELPERS(DmxL6470, dmx_l6470, DmxL6470) + DEFINE_STORE_GETTERS_HELPERS(DmxLed, dmx_led, DmxLed) + DEFINE_STORE_GETTERS_HELPERS(DmxPwm, dmx_pwm, DmxPwm) + DEFINE_STORE_GETTERS_HELPERS(DmxSerial, dmx_serial, DmxSerial) + DEFINE_STORE_GETTERS_HELPERS(DmxMonitor, dmx_monitor, DmxMonitor) + DEFINE_STORE_GETTERS_HELPERS(RdmDevice, rdm_device, RdmDevice) + DEFINE_STORE_GETTERS_HELPERS(RdmSensors, rdm_sensors, RdmSensors) + DEFINE_STORE_GETTERS_HELPERS(RdmSubdevices, rdm_subdevices, RdmSubdevices) + DEFINE_STORE_GETTERS_HELPERS(ShowFile, show_file, ShowFile) + DEFINE_STORE_GETTERS_HELPERS(Ltc, ltc, Ltc) + DEFINE_STORE_GETTERS_HELPERS(LtcDisplay, ltc_display, LtcDisplay) + DEFINE_STORE_GETTERS_HELPERS(LtcEtc, ltc_etc, LtcEtc) + DEFINE_STORE_GETTERS_HELPERS(TCNet, tcnet, TcNet) + DEFINE_STORE_GETTERS_HELPERS(Gps, gps, Gps) + DEFINE_STORE_GETTERS_HELPERS(Midi, midi, Midi) + DEFINE_STORE_GETTERS_HELPERS(RgbPanel, rgb_panel, RgbPanel) + DEFINE_STORE_GETTERS_HELPERS(Widget, widget, Widget) + +#undef DEFINE_STORE_GETTERS_HELPERS + +#define DEFINE_STORE_UPDATE_HELPERS(HumanName, StoreName, StoreType) \ + template void HumanName##Update(TField common::store::StoreType::* pField, const TField& value) { Update(GetStore()->StoreName, pField, value); } + + DEFINE_STORE_UPDATE_HELPERS(Global, global, Global) + DEFINE_STORE_UPDATE_HELPERS(RemoteConfig, remote_config, RemoteConfig) + DEFINE_STORE_UPDATE_HELPERS(Network, network, Network) + DEFINE_STORE_UPDATE_HELPERS(DisplayUdf, display_udf, DisplayUdf) + DEFINE_STORE_UPDATE_HELPERS(DmxNode, dmx_node, DmxNode) + DEFINE_STORE_UPDATE_HELPERS(OscClient, osc_client, OscClient) + DEFINE_STORE_UPDATE_HELPERS(OscServer, osc_server, OscServer) + DEFINE_STORE_UPDATE_HELPERS(DmxSend, dmx_send, DmxSend) + DEFINE_STORE_UPDATE_HELPERS(DmxL6470, dmx_l6470, DmxL6470) + DEFINE_STORE_UPDATE_HELPERS(DmxLed, dmx_led, DmxLed) + DEFINE_STORE_UPDATE_HELPERS(DmxPwm, dmx_pwm, DmxPwm) + DEFINE_STORE_UPDATE_HELPERS(DmxSerial, dmx_serial, DmxSerial) + DEFINE_STORE_UPDATE_HELPERS(DmxMonitor, dmx_monitor, DmxMonitor) + DEFINE_STORE_UPDATE_HELPERS(RdmDevice, rdm_device, RdmDevice) + DEFINE_STORE_UPDATE_HELPERS(RdmSensors, rdm_sensors, RdmSensors) + DEFINE_STORE_UPDATE_HELPERS(RdmSubdevices, rdm_subdevices, RdmSubdevices) + DEFINE_STORE_UPDATE_HELPERS(ShowFile, show_file, ShowFile) + DEFINE_STORE_UPDATE_HELPERS(Ltc, ltc, Ltc) + DEFINE_STORE_UPDATE_HELPERS(LtcDisplay, ltc_display, LtcDisplay) + DEFINE_STORE_UPDATE_HELPERS(LtcEtc, ltc_etc, LtcEtc) + DEFINE_STORE_UPDATE_HELPERS(TCNet, tcnet, TcNet) + DEFINE_STORE_UPDATE_HELPERS(Gps, gps, Gps) + DEFINE_STORE_UPDATE_HELPERS(Midi, midi, Midi) + DEFINE_STORE_UPDATE_HELPERS(RgbPanel, rgb_panel, RgbPanel) + DEFINE_STORE_UPDATE_HELPERS(Widget, widget, Widget) + +#undef DEFINE_STORE_UPDATE_HELPERS + + void SetFlagRemoteConfig(uint32_t flag) { SetFlagInternal(GetStore()->remote_config, &common::store::RemoteConfig::flags, flag); } + void SetFlagNetwork(uint32_t flag) { SetFlagInternal(GetStore()->network, &common::store::Network::flags, flag); } + void SetFlagDisplayUdf(uint32_t flag) { SetFlagInternal(GetStore()->display_udf, &common::store::DisplayUdf::flags, flag); } + void SetFlagDmxNode(uint32_t flag) { SetFlagInternal(GetStore()->dmx_node, &common::store::DmxNode::flags, flag); } + void SetFlagOscClient(uint32_t flag) { SetFlagInternal(GetStore()->osc_client, &common::store::OscClient::flags, flag); } + void SetFlagOscServer(uint32_t flag) { SetFlagInternal(GetStore()->osc_server, &common::store::OscServer::flags, flag); } + void SetFlagDmxSend(uint32_t flag) { SetFlagInternal(GetStore()->dmx_send, &common::store::DmxSend::flags, flag); } + void SetFlagDmxLed(uint32_t flag) { SetFlagInternal(GetStore()->dmx_led, &common::store::DmxLed::flags, flag); } + void SetFlagDmxPwm(uint32_t flag) { SetFlagInternal(GetStore()->dmx_pwm, &common::store::DmxPwm::flags, flag); } + void SetFlagDmxSerial(uint32_t flag) { SetFlagInternal(GetStore()->dmx_serial, &common::store::DmxSerial::set_list, flag); } + void SetFlagDmxMonitor(uint32_t flag) { SetFlagInternal(GetStore()->dmx_monitor, &common::store::DmxMonitor::set_list, flag); } + void SetFlagRdmDevice(uint32_t flag) { SetFlagInternal(GetStore()->rdm_device, &common::store::RdmDevice::set_list, flag); } + void SetFlagShowFile(uint32_t flag) { SetFlagInternal(GetStore()->show_file, &common::store::ShowFile::flags, flag); } + void SetFlagLtc(uint32_t flag) { SetFlagInternal(GetStore()->ltc, &common::store::Ltc::flags, flag); } + void SetFlagLtcDisplay(uint32_t flag) { SetFlagInternal(GetStore()->ltc_display, &common::store::LtcDisplay::flags, flag); } + void SetFlagLtcEtc(uint32_t flag) { SetFlagInternal(GetStore()->ltc_etc, &common::store::LtcEtc::set_list, flag); } + void SetFlagTCNet(uint32_t flag) { SetFlagInternal(GetStore()->tcnet, &common::store::TcNet::flags, flag); } + void SetFlagGps(uint32_t flag) { SetFlagInternal(GetStore()->gps, &common::store::Gps::flags, flag); } + void SetFlagMidi(uint32_t flag) { SetFlagInternal(GetStore()->midi, &common::store::Midi::flags, flag); } + void SetFlagRgbPanel(uint32_t flag) { SetFlagInternal(GetStore()->rgb_panel, &common::store::RgbPanel::set_list, flag); } + void SetFlagWidget(uint32_t flag) { SetFlagInternal(GetStore()->widget, &common::store::Widget::set_list, flag); } + + void ClearFlagRemoteConfig(uint32_t flag) { ClearFlagInternal(GetStore()->remote_config, &common::store::RemoteConfig::flags, flag); } + void ClearFlagNetwork(uint32_t flag) { ClearFlagInternal(GetStore()->network, &common::store::Network::flags, flag); } + void ClearFlagDisplayUdf(uint32_t flag) { ClearFlagInternal(GetStore()->display_udf, &common::store::DisplayUdf::flags, flag); } + void ClearFlagDmxNode(uint32_t flag) { ClearFlagInternal(GetStore()->dmx_node, &common::store::DmxNode::flags, flag); } + void ClearFlagOscClient(uint32_t flag) { ClearFlagInternal(GetStore()->osc_client, &common::store::OscClient::flags, flag); } + void ClearFlagOscServer(uint32_t flag) { ClearFlagInternal(GetStore()->osc_server, &common::store::OscServer::flags, flag); } + void ClearFlagDmxSend(uint32_t flag) { ClearFlagInternal(GetStore()->dmx_send, &common::store::DmxSend::flags, flag); } + void ClearFlagDmxLed(uint32_t flag) { ClearFlagInternal(GetStore()->dmx_led, &common::store::DmxLed::flags, flag); } + void ClearFlagDmxPwm(uint32_t flag) { ClearFlagInternal(GetStore()->dmx_pwm, &common::store::DmxPwm::flags, flag); } + void ClearFlagDmxSerial(uint32_t flag) { ClearFlagInternal(GetStore()->dmx_serial, &common::store::DmxSerial::set_list, flag); } + void ClearFlagDmxMonitor(uint32_t flag) { ClearFlagInternal(GetStore()->dmx_monitor, &common::store::DmxMonitor::set_list, flag); } + void ClearFlagRdmDevice(uint32_t flag) { ClearFlagInternal(GetStore()->rdm_device, &common::store::RdmDevice::set_list, flag); } + void ClearFlagShowFile(uint32_t flag) { ClearFlagInternal(GetStore()->show_file, &common::store::ShowFile::flags, flag); } + void ClearFlagLtc(uint32_t flag) { ClearFlagInternal(GetStore()->ltc, &common::store::Ltc::flags, flag); } + void ClearFlagLtcDisplay(uint32_t flag) { ClearFlagInternal(GetStore()->ltc_display, &common::store::LtcDisplay::flags, flag); } + void ClearFlagLtcEtc(uint32_t flag) { ClearFlagInternal(GetStore()->ltc_etc, &common::store::LtcEtc::set_list, flag); } + void ClearFlagTCNet(uint32_t flag) { ClearFlagInternal(GetStore()->tcnet, &common::store::TcNet::flags, flag); } + void ClearFlagGps(uint32_t flag) { ClearFlagInternal(GetStore()->gps, &common::store::Gps::flags, flag); } + void ClearFlagMidi(uint32_t flag) { ClearFlagInternal(GetStore()->midi, &common::store::Midi::flags, flag); } + void ClearFlagRgbPanel(uint32_t flag) { ClearFlagInternal(GetStore()->rgb_panel, &common::store::RgbPanel::set_list, flag); } + void ClearFlagWidget(uint32_t flag) { ClearFlagInternal(GetStore()->widget, &common::store::Widget::set_list, flag); } + + bool IsFlagSetRemoteConfig(uint32_t flag) const { return IsFlagSetInternal(GetStore()->remote_config, &common::store::RemoteConfig::flags, flag); } + bool IsFlagSetNetwork(uint32_t flag) const { return IsFlagSetInternal(GetStore()->network, &common::store::Network::flags, flag); } + bool IsFlagSetDisplayUdf(uint32_t flag) const { return IsFlagSetInternal(GetStore()->display_udf, &common::store::DisplayUdf::flags, flag); } + bool IsFlagSetDmxNode(uint32_t flag) const { return IsFlagSetInternal(GetStore()->dmx_node, &common::store::DmxNode::flags, flag); } + bool IsFlagSetOscClient(uint32_t flag) const { return IsFlagSetInternal(GetStore()->osc_client, &common::store::OscClient::flags, flag); } + bool IsFlagSetOscServer(uint32_t flag) const { return IsFlagSetInternal(GetStore()->osc_server, &common::store::OscServer::flags, flag); } + bool IsFlagSetDmxSend(uint32_t flag) const { return IsFlagSetInternal(GetStore()->dmx_send, &common::store::DmxSend::flags, flag); } + bool IsFlagSetDmxLed(uint32_t flag) const { return IsFlagSetInternal(GetStore()->dmx_led, &common::store::DmxLed::flags, flag); } + bool IsFlagSetDmxPwm(uint32_t flag) const { return IsFlagSetInternal(GetStore()->dmx_pwm, &common::store::DmxPwm::flags, flag); } + bool IsFlagSetDmxSerial(uint32_t flag) const { return IsFlagSetInternal(GetStore()->dmx_serial, &common::store::DmxSerial::set_list, flag); } + bool IsFlagSetDmxMonitor(uint32_t flag) const { return IsFlagSetInternal(GetStore()->dmx_monitor, &common::store::DmxMonitor::set_list, flag); } + bool IsFlagSetRdmDevice(uint32_t flag) const { return IsFlagSetInternal(GetStore()->rdm_device, &common::store::RdmDevice::set_list, flag); } + bool IsFlagSetShowFile(uint32_t flag) const { return IsFlagSetInternal(GetStore()->show_file, &common::store::ShowFile::flags, flag); } + bool IsFlagSetLtc(uint32_t flag) const { return IsFlagSetInternal(GetStore()->ltc, &common::store::Ltc::flags, flag); } + bool IsFlagSetLtcDisplay(uint32_t flag) const { return IsFlagSetInternal(GetStore()->ltc_display, &common::store::LtcDisplay::flags, flag); } + bool IsFlagSetLtcEtc(uint32_t flag) const { return IsFlagSetInternal(GetStore()->ltc_etc, &common::store::LtcEtc::set_list, flag); } + bool IsFlagSetTCNet(uint32_t flag) const { return IsFlagSetInternal(GetStore()->tcnet, &common::store::TcNet::flags, flag); } + bool IsFlagSetGps(uint32_t flag) const { return IsFlagSetInternal(GetStore()->gps, &common::store::Gps::flags, flag); } + bool IsFlagSetMidi(uint32_t flag) const { return IsFlagSetInternal(GetStore()->midi, &common::store::Midi::flags, flag); } + bool IsFlagSetRgbPanel(uint32_t flag) const { return IsFlagSetInternal(GetStore()->rgb_panel, &common::store::RgbPanel::set_list, flag); } + bool IsFlagSetWidget(uint32_t flag) const { return IsFlagSetInternal(GetStore()->widget, &common::store::Widget::set_list, flag); } + + template void RemoteConfigUpdateArray(uint8_t (common::store::RemoteConfig::*field)[N], const char* src, uint32_t length) + { + UpdateArray(GetStore()->remote_config, field, reinterpret_cast(src), length); + } + + template void RemoteConfigCopyArray(uint8_t (&dest)[N], const uint8_t (common::store::RemoteConfig::*field)[N]) const + { + static_assert(N == common::store::remoteconfig::kDisplayNameLength, "Size mismatch"); + memcpy(dest, (GetStore()->remote_config.*field), N); + } + + template void NetworkUpdateArray(uint8_t (common::store::Network::*field)[N], const char* src, uint32_t length) + { + UpdateArray(GetStore()->network, field, reinterpret_cast(src), length); + } + + template void LtcDisplayCopyArray(char (&dest)[N], const char (common::store::LtcDisplay::*field)[N]) const + { + static_assert(N == common::store::ltc::display::kMaxInfoMessage, "Size mismatch"); + memcpy(dest, (GetStore()->ltc_display.*field), N); + } + + template void RdmDeviceUpdateArray(uint8_t (common::store::RdmDevice::*field)[N], const char* src, uint32_t length) + { + UpdateArray(GetStore()->rdm_device, field, reinterpret_cast(src), length); + } + + uint8_t RdmSensorsIndexedGetType(uint32_t index) const + { + assert(index < common::store::rdm::sensors::kMaxDevices); + return GetStore()->rdm_sensors.entry[index].type; + } + + uint8_t RdmSensorsIndexedGetAddress(uint32_t index) const + { + assert(index < common::store::rdm::sensors::kMaxDevices); + return GetStore()->rdm_sensors.entry[index].address; + } + + template void RdmSensorsUpdateIndexed(T (common::store::RdmSensors::*field)[N], uint32_t index, const T& value) + { + static_assert(N == common::store::rdm::sensors::kMaxSensors, "Array size mismatch"); + assert(index < N); + + auto& array = GetStore()->rdm_sensors.*field; + + if (array[index] != value) + { + array[index] = value; + SetStatusChanged(); + } + } + + template void DmxNodeUpdateArray(uint8_t (common::store::DmxNode::*field)[N], const char* src, uint32_t length) + { + UpdateArray(GetStore()->dmx_node, field, reinterpret_cast(src), length); + } + + template + void DmxNodeUpdateLabel(uint8_t (common::store::DmxNode::*field)[common::store::dmxnode::kParamPorts][N], uint32_t index, const char* src, uint32_t length) + { + static_assert(N == common::store::dmxnode::kLabelNameLength, "Label size mismatch"); + assert(index < common::store::dmxnode::kParamPorts); + assert(src != nullptr); + + auto& labels = GetStore()->dmx_node.*field; + + if (length > N) + { + length = N; + } + + if (__builtin_memcmp(labels[index], src, length) != 0) + { + memset(labels[index], 0, N); + memcpy(labels[index], src, length); + SetStatusChanged(); + } + } + + template void DmxNodeUpdateIndexed(T (common::store::DmxNode::*field)[N], uint32_t index, const T& value) + { + static_assert(N == common::store::dmxnode::kParamPorts, "Array size mismatch"); + assert(index < N); + + auto& array = GetStore()->dmx_node.*field; + + if (array[index] != value) + { + array[index] = value; + SetStatusChanged(); + } + } + + uint16_t DmxLedIndexedGetStartUniverse(uint32_t index) const + { + assert(index < 16); + return GetStore()->dmx_led.start_universe[index]; + } + + void DmxL6470CopySparkFunGlobal(common::store::l6470dmx::SparkFun* dest) const + { + assert(dest != nullptr); + *dest = GetStore()->dmx_l6470.spark_fun_global; + } + + void DmxL6470CopySparkFunIndexed(uint32_t index, common::store::l6470dmx::SparkFun* dest) const + { + assert(index < common::store::l6470dmx::kMaxMotors); + assert(dest != nullptr); + *dest = GetStore()->dmx_l6470.store[index].spark_fun; + } + + void DmxL6470CopyModeIndexed(uint32_t index, common::store::l6470dmx::Mode* dest) const + { + assert(index < common::store::l6470dmx::kMaxMotors); + assert(dest != nullptr); + *dest = GetStore()->dmx_l6470.store[index].mode; + } + + void DmxL6470CopyL6470Indexed(uint32_t index, common::store::l6470dmx::L6470* dest) const + { + assert(index < common::store::l6470dmx::kMaxMotors); + assert(dest != nullptr); + *dest = GetStore()->dmx_l6470.store[index].l6470; + } + + void DmxL6470CopyMotorIndexed(uint32_t index, common::store::l6470dmx::Motor* dest) const + { + assert(index < common::store::l6470dmx::kMaxMotors); + assert(dest != nullptr); + *dest = GetStore()->dmx_l6470.store[index].motor; + } + + void DmxL6470StoreSparkFunGlobal(const common::store::l6470dmx::SparkFun* src) + { + assert(src != nullptr); + auto& dest = GetStore()->dmx_l6470.spark_fun_global; + + if (__builtin_memcmp(&dest, src, sizeof(common::store::l6470dmx::SparkFun)) != 0) + { + __builtin_memcpy(&dest, src, sizeof(common::store::l6470dmx::SparkFun)); + SetStatusChanged(); + } + } + + void DmxL6470StoreSparkFunIndexed(uint32_t index, const common::store::l6470dmx::SparkFun* src) + { + assert(index < common::store::l6470dmx::kMaxMotors); + assert(src != nullptr); + auto& ref = GetStore()->dmx_l6470.store[index].spark_fun; + if (__builtin_memcmp(&ref, src, sizeof(common::store::l6470dmx::SparkFun)) != 0) + { + __builtin_memcpy(&ref, src, sizeof(common::store::l6470dmx::SparkFun)); + SetStatusChanged(); + } + } + + void DmxL6470StoreModeIndexed(uint32_t index, const common::store::l6470dmx::Mode* src) + { + assert(index < common::store::l6470dmx::kMaxMotors); + assert(src != nullptr); + + auto& ref = GetStore()->dmx_l6470.store[index].mode; + if (__builtin_memcmp(&ref, src, sizeof(common::store::l6470dmx::Mode)) != 0) + { + __builtin_memcpy(&ref, src, sizeof(common::store::l6470dmx::Mode)); + SetStatusChanged(); + } + } + + void DmxL6470StoreL6470Indexed(uint32_t index, const common::store::l6470dmx::L6470* src) + { + assert(index < common::store::l6470dmx::kMaxMotors); + assert(src != nullptr); + auto& ref = GetStore()->dmx_l6470.store[index].l6470; + if (__builtin_memcmp(&ref, src, sizeof(common::store::l6470dmx::L6470)) != 0) + { + __builtin_memcpy(&ref, src, sizeof(common::store::l6470dmx::L6470)); + SetStatusChanged(); + } + } + + void DmxL6470StoreMotorIndexed(uint32_t index, const common::store::l6470dmx::Motor* src) + { + assert(index < common::store::l6470dmx::kMaxMotors); + assert(src != nullptr); + auto& ref = GetStore()->dmx_l6470.store[index].motor; + if (__builtin_memcmp(&ref, src, sizeof(common::store::l6470dmx::Motor)) != 0) + { + __builtin_memcpy(&ref, src, sizeof(common::store::l6470dmx::Motor)); + SetStatusChanged(); + } + } + + template + TField DmxL6470GetModeIndexed(uint32_t index, TField common::store::l6470dmx::Mode::* field) const + { + assert(index < common::store::l6470dmx::kMaxMotors); + return (GetStore()->dmx_l6470.store[index].mode).*field; + } + + template + TField DmxL6470GetMotorIndexed(uint32_t index, TField common::store::l6470dmx::Motor::* field) const + { + assert(index < common::store::l6470dmx::kMaxMotors); + return (GetStore()->dmx_l6470.store[index].motor).*field; + } + + static ConfigStore& Instance() + { + assert(s_this != nullptr); + return *s_this; + } + + private: + template TField Get(const TObject& object, TField TObject::* field) const { return object.*field; } + + template void Update(TObject& object, TField TObject::* field, const TField& value) + { + assert(field != nullptr); + + auto* dest = &(object.*field); + + if (__builtin_memcmp(dest, &value, sizeof(TField)) != 0) + { + __builtin_memcpy(dest, &value, sizeof(TField)); + SetStatusChanged(); + } + } + + template + void UpdateArray(TObject& object, TArray (TObject::*field)[N], const TArray* src, uint32_t length) + { + assert(src != nullptr); + + auto* dest = &(object.*field); + + if (length > N) + { + length = N; + } + + if (__builtin_memcmp(dest, src, length * sizeof(TArray)) != 0) + { + memset(dest, 0, sizeof(TArray) * N); + memcpy(dest, src, length * sizeof(TArray)); + SetStatusChanged(); + } + } + + private: + void SetStatusChanged() + { + s_state = State::kChanged; + TimerStart(); + } + + static void Timer([[maybe_unused]] TimerHandle_t timer_handle) + { + DEBUG_ENTRY(); + + if (!Instance().Commit()) + { + Instance().TimerStop(); + + DEBUG_EXIT(); + return; + } + + DEBUG_EXIT(); + } + + void TimerStart() + { + DEBUG_ENTRY(); + DEBUG_PRINTF("s_timer_id=%d", s_timer_id); + + if (s_timer_id != kTimerIdNone) + { + DEBUG_EXIT(); + return; + } + + s_timer_id = SoftwareTimerAdd(100, Timer); + + DEBUG_PRINTF("s_timer_id=%d", s_timer_id); + DEBUG_EXIT(); + } + + void TimerStop() + { + DEBUG_ENTRY(); + DEBUG_PRINTF("s_timer_id=%d", s_timer_id); + + if (s_timer_id == kTimerIdNone) + { + return; + DEBUG_EXIT(); + } + + SoftwareTimerDelete(s_timer_id); + + DEBUG_PRINTF("s_timer_id=%d", s_timer_id); + DEBUG_EXIT(); + } + + bool Flash() + { + DEBUG_PUTS(kStateNames[static_cast(s_state)]); + + if (__builtin_expect((s_state == State::kIdle), 1)) + { + return false; + } + + switch (s_state) + { + case State::kChanged: + s_state = State::kChangedWaiting; + return true; + case State::kChangedWaiting: + s_state = State::kErasing; + return true; + break; + case State::kErasing: + { + storedevice::Result result; + if (StoreDevice::Erase(s_start_address, kStoreSize, result)) + { + s_state = State::kErasedWaiting; + } + assert(result == storedevice::Result::kOk); + return true; + } + break; + case State::kErasedWaiting: + s_state = State::kErased; + return true; + break; + case State::kErased: + s_state = State::kWriting; + SoftwareTimerChange(s_timer_id, 0); + return true; + break; + case State::kWriting: + { + storedevice::Result result; + if (StoreDevice::Write(s_start_address, sizeof(ConfigurationStore), reinterpret_cast(&s_store), result)) + { + s_state = State::kIdle; + return false; + } + assert(result == storedevice::Result::kOk); + return true; + } + break; + default: + assert(0); + __builtin_unreachable(); + break; + } + + assert(0); + __builtin_unreachable(); + return false; + } + + ConfigurationStore* GetStore() { return reinterpret_cast(s_store); } + const ConfigurationStore* GetStore() const { return reinterpret_cast(s_store); } + + template void SetFlagInternal(TObject& object, uint32_t TObject::* field, uint32_t flag) + { + auto& flags = object.*field; + if ((flags & flag) == 0) + { + flags |= flag; + SetStatusChanged(); + } + } + + template void ClearFlagInternal(TObject& object, uint32_t TObject::* field, uint32_t flag) + { + auto& flags = object.*field; + if ((flags & flag) != 0) + { + flags &= ~flag; + SetStatusChanged(); + } + } + + template bool IsFlagSetInternal(const TObject& object, uint32_t TObject::* field, uint32_t flag) const + { + const auto& flags = object.*field; + return (flags & flag) != 0; + } + + bool IsValid() + { + const auto* store = GetStore(); + return memcmp(store->magic_number, kMagicNumber, sizeof(kMagicNumber)) == 0 && memcmp(store->version, kVersion, sizeof(kVersion)) == 0; + } + + private: + static inline uint8_t s_store[kStoreSize]; + static inline uint32_t s_start_address{0}; + static inline bool s_have_device{false}; + static inline State s_state{State::kIdle}; + static inline TimerHandle_t s_timer_id = kTimerIdNone; + static inline ConfigStore* s_this; }; -#endif /* CONFIGSTORE_H_ */ +inline void ConfigstoreCommit() +{ + while (ConfigStore::Instance().Commit()) + { + } +} + +#endif // CONFIGSTORE_H_ diff --git a/lib-configstore/include/configstoredevice.h b/lib-configstore/include/configstoredevice.h index a573772..c54336b 100755 --- a/lib-configstore/include/configstoredevice.h +++ b/lib-configstore/include/configstoredevice.h @@ -1,43 +1,68 @@ -/* - * storedevice.h +/** + * @file configstoredevice.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: -#ifndef STOREDEVICE_H_ -#define STOREDEVICE_H_ + * 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 CONFIGSTOREDEVICE_H_ +#define CONFIGSTOREDEVICE_H_ #include -namespace storedevice { -enum class result { - OK, ERROR +namespace storedevice +{ +enum class Result +{ + kOk, + kError }; -} // namespace storedevice - -#if defined (CONFIG_STORE_USE_I2C) -# include "i2c/at24cxx.h" -class StoreDevice: AT24C32 { -#elif defined (CONFIG_STORE_USE_ROM) -# include "flashcode.h" -class StoreDevice: FlashCode { +} // namespace storedevice + +#if defined(CONFIG_STORE_USE_I2C) +#include "i2c/at24cxx.h" +class StoreDevice : AT24C32 +{ +#elif defined(CONFIG_STORE_USE_ROM) +#include "flashcode.h" +class StoreDevice : FlashCode +{ #else -class StoreDevice { +class StoreDevice +{ #endif -public: - StoreDevice(); - ~StoreDevice(); - - bool IsDetected() const { - return m_IsDetected; - } - uint32_t GetSectorSize() const; - uint32_t GetSize() const; - - bool Read(uint32_t nOffset, uint32_t nLength, uint8_t *pBuffer, storedevice::result& nResult); - bool Erase(uint32_t nOffset, uint32_t nLength, storedevice::result& nResult); - bool Write(uint32_t nOffset, uint32_t nLength, const uint8_t *pBuffer, storedevice::result& nResult); - -private: - bool m_IsDetected { false }; + public: + StoreDevice(); + ~StoreDevice(); + + bool IsDetected() const { return detected_; } + uint32_t GetSectorSize() const; + uint32_t GetSize() const; + + bool Read(uint32_t offset, uint32_t length, uint8_t* buffer, storedevice::Result& result); + bool Erase(uint32_t offset, uint32_t length, storedevice::Result& result); + bool Write(uint32_t offset, uint32_t length, const uint8_t* buffer, storedevice::Result& result); + + private: + bool detected_{false}; }; -#endif /* STOREDEVICE_H_ */ +#endif // CONFIGSTOREDEVICE_H_ diff --git a/lib-configstore/include/configurationstore.h b/lib-configstore/include/configurationstore.h new file mode 100755 index 0000000..cddd51c --- /dev/null +++ b/lib-configstore/include/configurationstore.h @@ -0,0 +1,854 @@ +/** + * @file configurationstore.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 CONFIGURATIONSTORE_H_ +#define CONFIGURATIONSTORE_H_ + +#include +#include + +#if defined(_MSC_VER) +#pragma pack(push, 1) +#define PACKED +#elif defined(__GNUC__) || defined(__clang__) +#define PACKED __attribute__((packed)) +#else +#pragma pack(1) +#define PACKED +#endif + +namespace common::store +{ + +inline constexpr size_t kGlobalSize = 16; +inline constexpr size_t kRemoteConfigSize = 32; +inline constexpr size_t kNetworkSize = 96; +inline constexpr size_t kDisplaySize = 48; +inline constexpr size_t kDmxNodeSize = 212; +inline constexpr size_t kOscClientSize = 912; +inline constexpr size_t kOscServerSize = 400; +inline constexpr size_t kDmxSendSize = 16; +inline constexpr size_t kDmxL6470Size = 848; +inline constexpr size_t kDmxLedSize = 64; +inline constexpr size_t kDmxPwmSize = 24; +inline constexpr size_t kDmxSerialSize = 24; +inline constexpr size_t kDmxMonitorSize = 16; +inline constexpr size_t kRdmDeviceSize = 48; +inline constexpr size_t kRdmSensorsSize = 68; +inline constexpr size_t kRdmSubdevicesSize = 84; +inline constexpr size_t kShowSize = 16; +inline constexpr size_t kLtcSize = 48; +inline constexpr size_t kLtcDisplaySize = 48; +inline constexpr size_t kLtcEtcSize = 20; +inline constexpr size_t kTcNetSize = 16; +inline constexpr size_t kGpsSize = 16; +inline constexpr size_t kMidiSize = 16; +inline constexpr size_t kRgbPanelSize = 16; +inline constexpr size_t kWidgetSize = 16; + +struct Global +{ + int32_t utc_offset; + uint8_t reserved[12]; +} PACKED; + +static_assert(sizeof(Global) == kGlobalSize); + +namespace remoteconfig +{ +inline constexpr uint32_t kDisplayNameLength = 24; +} + +struct RemoteConfig +{ + uint32_t flags; + uint8_t reserved[4]; + uint8_t display_name[remoteconfig::kDisplayNameLength]; +} PACKED; + +static_assert(sizeof(RemoteConfig) == kRemoteConfigSize); + +namespace network +{ +inline constexpr uint32_t kHostnameSize = 64; + +struct Flags +{ + enum class Flag : uint32_t + { + kUseStaticIp = (1U << 0), + }; + + static constexpr bool Has(uint32_t value, Flag flag) noexcept { return (value & static_cast(flag)) != 0; } +}; +} // namespace network + +struct Network +{ + uint32_t flags; + uint32_t local_ip; + uint32_t netmask; + uint32_t gateway_ip; + uint32_t name_server_ip; + uint32_t ntp_server_ip; + uint8_t host_name[network::kHostnameSize]; + uint8_t reserved[8]; +} PACKED; + +static_assert(sizeof(Network) == kNetworkSize); + +namespace displayudf +{ +inline constexpr uint32_t kLabelIndexSize = 28; + +struct Flags +{ + enum class Flag : uint32_t + { + kFlipVertically = (1U << 0), + }; + + static constexpr bool Has(uint32_t value, Flag flag) noexcept { return (value & static_cast(flag)) != 0; } +}; +} // namespace displayudf + +struct DisplayUdf +{ + uint32_t flags; + uint8_t label_index[displayudf::kLabelIndexSize]; + uint8_t sleep_timeout; + uint8_t intensity; + uint8_t reserved[14]; +} PACKED; + +static_assert(sizeof(DisplayUdf) == kDisplaySize); + +namespace dmxnode +{ +inline constexpr uint32_t kParamPorts = 4; +inline constexpr uint32_t kNodeNameLength = 64; +inline constexpr uint32_t kLabelNameLength = 18; + +struct Flags +{ + enum class Flag : uint32_t + { + kEnableRdm = (1U << 0), + kMapUniverse0 = (1U << 1), + kDisableMergeTimeout = (1U << 2) + }; + + static constexpr bool Has(uint32_t value, Flag flag) noexcept { return (value & static_cast(flag)) != 0; } +}; +} // namespace dmxnode + +struct DmxNode +{ + uint32_t flags; + uint8_t personality; + uint8_t reserved; + uint16_t universe[dmxnode::kParamPorts]; + uint16_t direction; + uint16_t merge_mode; + uint8_t output_style; + uint8_t fail_safe; + uint8_t long_name[dmxnode::kNodeNameLength]; + uint8_t label[dmxnode::kParamPorts][dmxnode::kLabelNameLength]; + uint8_t reserved1[2]; + uint16_t protocol; + uint16_t rdm; + uint32_t destination_ip[dmxnode::kParamPorts]; + uint8_t reserved2[4]; + uint8_t priority[dmxnode::kParamPorts]; + uint8_t reserved3[4]; + uint8_t reserved4[22]; +} PACKED; + +static_assert(offsetof(DmxNode, universe) % alignof(uint16_t) == 0, "universe must be uint16_t-aligned"); +static_assert(offsetof(DmxNode, protocol) % alignof(uint16_t) == 0, "protocol must be uint16_t-aligned"); +static_assert(sizeof(DmxNode) == kDmxNodeSize); + +namespace osc::client +{ +inline constexpr uint32_t kCmdCount = 8; +inline constexpr uint32_t kCmdPathLength = 64; +inline constexpr uint32_t kLedCount = 8; +inline constexpr uint32_t kLedPathLength = 48; + +struct Flags +{ + enum class Flag : uint32_t + { + kPingDisable = (1U << 0), + }; + + static constexpr bool Has(uint32_t value, Flag flag) noexcept { return (value & static_cast(flag)) != 0; } +}; +} // namespace osc::client + +struct OscClient +{ + uint32_t flags; + uint16_t outgoing_port; + uint16_t incoming_port; + uint32_t server_ip; + uint8_t ping_delay; + uint8_t reserved[3]; + char cmd[osc::client::kCmdCount][osc::client::kCmdPathLength]; + char led[osc::client::kLedCount][osc::client::kLedPathLength]; +} PACKED; + +static_assert(sizeof(OscClient) == kOscClientSize); + +namespace osc::server +{ +inline constexpr uint32_t kPathLength = 128; + +struct Flags +{ + enum class Flag : uint32_t + { + kPartialTransmission = (1U << 0), + }; + + static constexpr bool Has(uint32_t value, Flag flag) noexcept { return (value & static_cast(flag)) != 0; } +}; +} // namespace osc::server + +struct OscServer +{ + uint32_t flags; + uint16_t outgoing_port; + uint16_t incoming_port; + uint8_t output_type; + uint8_t reserved1[3]; + uint8_t reserved2[4]; + char path[osc::server::kPathLength]; + char path_info[osc::server::kPathLength]; + char path_blackout[osc::server::kPathLength]; +} PACKED; + +static_assert(sizeof(OscServer) == kOscServerSize); + +struct DmxSend +{ + uint32_t flags; + uint16_t break_time; + uint16_t mab_time; + uint8_t refresh_rate; + uint8_t slots_count; + uint8_t reserved2[6]; +} PACKED; + +static_assert(offsetof(DmxSend, break_time) % alignof(uint16_t) == 0, "break_time must be uint16_t-aligned"); +static_assert(sizeof(DmxSend) == kDmxSendSize); + +namespace dmxled +{ + +inline constexpr uint32_t kMaxUniverses = 16; + +struct Flags +{ + enum class Flag : uint32_t + { + kEnableGamma = (1U << 0), + }; + + static constexpr bool Has(uint32_t value, Flag flag) noexcept { return (value & static_cast(flag)) != 0; } +}; +} // namespace dmxled + +struct DmxLed +{ + uint32_t flags; + uint8_t type; + uint8_t map; + uint16_t count; + uint16_t grouping_count; + uint16_t dmx_start_address; + uint8_t reserved1[4]; + uint32_t spi_speed_hz; + uint8_t global_brightness; + uint8_t active_outputs; + uint8_t test_pattern; + uint8_t gamma_value; + uint8_t low_code; + uint8_t high_code; + uint8_t reserved2[6]; + uint16_t start_universe[dmxled::kMaxUniverses]; +} PACKED; + +static_assert(offsetof(DmxLed, count) % alignof(uint16_t) == 0, "count must be uint16_t-aligned"); +static_assert(offsetof(DmxLed, spi_speed_hz) % alignof(uint32_t) == 0, "spi_speed_hz must be uint32_t-aligned"); +static_assert(offsetof(DmxLed, start_universe) % alignof(uint16_t) == 0, "start_universe must be uint16_t-aligned"); +static_assert(sizeof(DmxLed) == kDmxLedSize); + +namespace dmxpwm +{ +struct Flags +{ + enum class Flag : uint32_t + { + kModeServo = 1U << 0, + kUse8Bit = 1U << 1, + kLedOutputInvert = 1U << 2, + kLedOutputOpendrain = 1U << 3, + }; + + static constexpr bool Has(uint32_t value, Flag flag) noexcept { return (value & static_cast(flag)) != 0; } +}; +} // namespace dmxpwm + +struct DmxPwm +{ + uint32_t flags; + uint8_t address; + uint8_t reserved1; + uint16_t channel_count; + uint16_t dmx_start_address; + uint16_t led_pwm_frequency; + uint16_t servo_left_us; + uint16_t servo_center_us; + uint16_t servo_right_us; + uint8_t reserved2[6]; +} PACKED; + +static_assert(sizeof(DmxPwm) == kDmxPwmSize); + +struct DmxSerial +{ + uint32_t set_list; + uint8_t type; + uint8_t reserved1[3]; + uint32_t baud; + uint8_t bits; + uint8_t parity; + uint8_t stop_bits; + uint8_t reserved2; + uint32_t spi_speed_hz; + uint8_t spi_mode; + uint8_t i2c_address; + uint8_t i2c_speed_mode; + uint8_t reserved3; +} PACKED; + +static_assert(offsetof(DmxSerial, baud) % alignof(uint32_t) == 0, "baud must be uint32_t-aligned"); +static_assert(offsetof(DmxSerial, spi_speed_hz) % alignof(uint32_t) == 0, "spi_speed_hz must be uint32_t-aligned"); +static_assert(sizeof(DmxSerial) == kDmxSerialSize); + +struct DmxMonitor +{ + uint32_t set_list; + uint16_t dmx_start_address; + uint16_t dmx_max_channels; + uint8_t format; + uint8_t reserved[7]; +} PACKED; + +static_assert(sizeof(DmxMonitor) == kDmxMonitorSize); + +namespace rdmdevice +{ +inline constexpr uint32_t kLabelMaxLength = 32; +} + +struct RdmDevice +{ + uint32_t set_list; + uint8_t device_root_label[rdmdevice::kLabelMaxLength]; + uint8_t device_root_label_length; + uint8_t reserved; + uint16_t product_category; + uint16_t product_detail; + uint8_t reserved2[6]; +} PACKED; + +static_assert(offsetof(RdmDevice, product_category) % alignof(uint16_t) == 0, "product_category must be uint16_t-aligned"); +static_assert(sizeof(RdmDevice) == kRdmDeviceSize); + +namespace rdm::sensors +{ +inline constexpr uint32_t kMaxSensors = 16; +inline constexpr uint32_t kMaxDevices = 8; +} // namespace rdm::sensors + +struct RdmSensors +{ + uint32_t devices; + struct Entry + { + uint8_t type; + uint8_t address; + uint8_t reserved[2]; + } PACKED entry[rdm::sensors::kMaxDevices]; + int16_t calibrate[rdm::sensors::kMaxSensors]; +} PACKED; + +static_assert(offsetof(RdmSensors, calibrate) % alignof(uint16_t) == 0, "calibrate must be uint16_t-aligned"); +static_assert(sizeof(RdmSensors) == kRdmSensorsSize); + +namespace rdm::subdevices +{ +inline constexpr uint32_t kMaxSubdevices = 8; +} + +struct RdmSubdevices +{ + uint32_t count; + struct Entry + { + uint8_t type; + uint8_t chip_select; + uint8_t address; + uint8_t reserved; + uint32_t speed_hz; + uint16_t dmx_start_address; + } PACKED entry[rdm::subdevices::kMaxSubdevices]; +} PACKED; + +static_assert(offsetof(RdmSubdevices::Entry, speed_hz) % alignof(uint32_t) == 0, "speed_hz must be uint32_t-aligned"); +static_assert(offsetof(RdmSubdevices::Entry, dmx_start_address) % alignof(uint16_t) == 0, "dmx_start_address must be uint16_t-aligned"); +static_assert(sizeof(RdmSubdevices) == kRdmSubdevicesSize); + +namespace showfile +{ +struct Flags +{ + enum class Flag : uint32_t + { + kOptionAutoPlay = 1U << 0, + kOptionLoop = 1U << 1, + kOptionDisableSync = 1U << 2, + kOptionArtnetDisableUnicast = 1U << 3, + kOptionSacnSyncUniverse = 1U << 4 + }; +}; +} // namespace showfile + +struct ShowFile +{ + uint32_t flags; + uint8_t show; + uint8_t reserved; + uint16_t osc_port_incoming; + uint16_t osc_port_outgoing; + uint16_t universe; + uint8_t reserved2[2]; + uint8_t disable_unicast; + uint8_t dmx_master; +} PACKED; + +static_assert(offsetof(ShowFile, osc_port_incoming) % alignof(uint16_t) == 0, "osc_port_incoming must be uint16_t-aligned"); +static_assert(sizeof(ShowFile) == kShowSize); + +namespace ltc +{ +struct Flags +{ + enum class Flag : uint32_t + { + kNtpEnable = 1U << 0, + kAutoStart = 1U << 1, + kIsAltFuntion = 1U << 2, + kSkipFree = 1U << 3, + kShowSystime = 1U << 4, + kTimeSyncDisabled = 1U << 5, + kOscEnabled = 1U << 6, + kGpsStart = 1U << 7, + kIgnoreStart = 1U << 8, + kIgnoreStop = 1U << 9, + kWS28xxEnable = 1U << 10, + kRgbpanelEnable = 1U << 11, + }; + + static constexpr bool Has(uint32_t value, Flag flag) noexcept { return (value & static_cast(flag)) != 0; } +}; +} // namespace ltc + +struct Ltc +{ + uint32_t flags; + uint8_t source; + uint8_t volume; + uint8_t disabled_outputs; + uint8_t ntp_year; + uint8_t ntp_month; + uint8_t ntp_day; + uint8_t fps; + uint8_t start_frame; + uint8_t start_second; + uint8_t start_minute; + uint8_t start_hour; + uint8_t stop_frame; + uint8_t stop_second; + uint8_t stop_minute; + uint8_t stop_hour; + uint8_t reserved6; + uint8_t rgb_led_type; + uint8_t reserved7; + uint8_t skip_seconds; + uint8_t reserved8; + uint8_t reserved1[12]; + uint8_t reserved2[2]; + uint16_t osc_port; + int32_t utc_offset; + uint32_t time_code_ip; +} PACKED; + +static_assert(offsetof(Ltc, osc_port) % alignof(uint16_t) == 0, "osc_port must be uint16_t-aligned"); +static_assert(sizeof(Ltc) == kLtcSize); + +namespace ltc::display +{ +inline constexpr uint32_t kMaxColours = 6; +inline constexpr uint32_t kMaxInfoMessage = 8; + +struct Flags +{ + enum class Flag : uint32_t + { + kRotaryFullStep = 1U << 0, + }; + + static constexpr bool Has(uint32_t value, Flag flag) noexcept { return (value & static_cast(flag)) != 0; } +}; +} // namespace ltc::display + +struct LtcDisplay +{ + uint32_t flags; + uint8_t max7219_type; + uint8_t max7219_intensity; + uint8_t ws28xx_type; + uint8_t ws28xx_rgb_mapping; + uint8_t ws28xx_display_type; + uint8_t reserved1; + uint8_t display_rgb_intensity; + uint8_t display_rgb_colon_blink_mode; + uint32_t display_rgb_colour[ltc::display::kMaxColours]; + char info_message[ltc::display::kMaxInfoMessage]; + uint8_t oled_intensity; + uint8_t reserved2[3]; +} PACKED; + +static_assert(offsetof(LtcDisplay, display_rgb_colour) % alignof(uint32_t) == 0, "display_rgb_colour must be uint32_t-aligned"); +static_assert(sizeof(LtcDisplay) == kLtcDisplaySize); + +struct LtcEtc +{ + uint32_t set_list; + uint32_t destination_ip; + uint32_t source_multicast_ip; + uint16_t destination_port; + uint16_t source_port; + uint8_t udp_terminator; + uint8_t reserved1[3]; +} PACKED; + +static_assert(sizeof(LtcEtc) == kLtcEtcSize); + +namespace tcnet +{ +inline constexpr uint32_t kNodeNameLength = 8; + +struct Flags +{ + enum class Flag : uint32_t + { + kUseTimecode = 1U << 0, + }; + + static constexpr bool Has(uint32_t value, Flag flag) noexcept { return (value & static_cast(flag)) != 0; } +}; +} // namespace tcnet + +struct TcNet +{ + uint32_t flags; + char node_name[tcnet::kNodeNameLength]; + uint8_t layer; + uint8_t time_code_type; + uint8_t reserved[2]; +} PACKED; + +static_assert(sizeof(TcNet) == kTcNetSize); + +namespace gps +{ +struct Flags +{ + enum class Flag : uint32_t + { + kEnable = 1U << 0, + }; + + static constexpr bool Has(uint32_t value, Flag flag) noexcept { return (value & static_cast(flag)) != 0; } +}; +} // namespace gps + +struct Gps +{ + uint32_t flags; + int32_t utc_offset; + uint8_t module; + uint8_t reserved[7]; +} PACKED; + +static_assert(sizeof(Gps) == kGpsSize); + +namespace midi { +struct Flags +{ + enum class Flag : uint32_t + { + kActiveSense = 1U << 0, + }; + + static constexpr bool Has(uint32_t value, Flag flag) noexcept { return (value & static_cast(flag)) != 0; } +}; +} // namespace midi + +struct Midi +{ + uint32_t flags; + uint32_t baudrate; + uint8_t reserved[8]; +} PACKED; + +static_assert(sizeof(Midi) == kMidiSize); + +struct RgbPanel +{ + uint32_t set_list; + uint8_t cols; + uint8_t rows; + uint8_t chain; + uint8_t type; + uint8_t reserved[8]; +} PACKED; + +static_assert(sizeof(RgbPanel) == kRgbPanelSize); + +struct Widget +{ + uint32_t set_list; + uint8_t break_time; + uint8_t mab_time; + uint8_t refresh_rate; + uint8_t mode; + uint8_t throttle; + uint8_t reserved[7]; +} PACKED; + +static_assert(sizeof(Widget) == kWidgetSize); + +namespace l6470dmx +{ +inline constexpr uint32_t kMaxMotors = 8; + +namespace sparkfun +{ +struct Flags +{ + enum class Flag : uint32_t + { + kIsSetPosition = 1U << 0, + kIsSetSpiCs = 1U << 1, + kIsSetResetPin = 1U << 2, + kIsSetBusyPin = 1U << 3, + }; + + static constexpr bool Has(uint32_t value, Flag flag) noexcept { return (value & static_cast(flag)) != 0; } +}; +} // namespace sparkfun + +struct SparkFun +{ + uint32_t flags; + uint8_t position; + uint8_t spi_cs; + uint8_t reset_pin; + uint8_t busy_pin; + uint8_t reserved[8]; +} PACKED; + +struct SlotInfo +{ + uint16_t category; + uint8_t type; + uint8_t reserved; +}; + +namespace mode +{ +inline constexpr uint16_t kMaxDmxFootprint = 4; + +struct Flags +{ + enum class Flag : uint32_t + { + kUseSwitch = 1U << 0, + }; + + static constexpr bool Has(uint32_t value, Flag flag) noexcept { return (value & static_cast(flag)) != 0; } +}; +} // namespace mode + +struct Mode +{ + uint32_t flags; + uint8_t dmx_mode; + uint8_t reserved1; + uint16_t dmx_start_address; + uint32_t max_steps; + uint32_t switch_steps_per_sec; + uint8_t switch_action; + uint8_t switch_dir; + uint8_t reserved2[2]; + SlotInfo slot_info[mode::kMaxDmxFootprint]; +} PACKED; + +namespace l6470 +{ +struct Flags +{ + enum class Flag : uint32_t + { + kIsSetMinSpeed = 1U << 0, + kIsSetMaxSpeed = 1U << 1, + kIsSetAcc = 1U << 2, + kIsSetDec = 1U << 3, + kIsSetKvalHold = 1U << 4, + kIsSetKvalRun = 1U << 5, + kIsSetKvalAcc = 1U << 6, + kIsSetKvalDec = 1U << 7, + kIsSetMicroSteps = 1U << 8 + }; + + static constexpr bool Has(uint32_t value, Flag flag) noexcept { return (value & static_cast(flag)) != 0; } +}; +} // namespace l6470 + +struct L6470 +{ + uint32_t flags; + uint32_t min_speed; + uint32_t max_speed; + uint32_t acc; + uint32_t dec; + uint8_t kval_hold; + uint8_t kval_run; + uint8_t kval_acc; + uint8_t kval_dec; + uint8_t micro_steps; + uint8_t reserved[3]; +} PACKED; + +struct Motor +{ + uint32_t set_list; + float step_angel; + float voltage; + float current; + float resistance; + float inductance; +} PACKED; + +struct Store +{ + l6470dmx::SparkFun spark_fun; + l6470dmx::Mode mode; + l6470dmx::L6470 l6470; + l6470dmx::Motor motor; +} PACKED; + +static_assert(offsetof(Store, mode) % alignof(uint32_t) == 0, "mode must be uint32_t-aligned"); +static_assert(offsetof(Store, l6470) % alignof(uint32_t) == 0, "l6470 must be uint32_t-aligned"); +static_assert(offsetof(Store, motor) % alignof(uint32_t) == 0, "motor must be uint32_t-aligned"); +} // namespace l6470dmx + +struct DmxL6470 +{ + l6470dmx::SparkFun spark_fun_global; + l6470dmx::Store store[l6470dmx::kMaxMotors]; +} PACKED; + +static_assert(offsetof(DmxL6470, store) % alignof(uint32_t) == 0, "store must be uint32_t-aligned"); +static_assert(sizeof(DmxL6470) == kDmxL6470Size); + +} // namespace common::store + +namespace configurationstore +{ +inline constexpr uint32_t kMagicNumberSize = 4; +inline constexpr uint32_t kVersionSize = 2; +} // namespace configurationstore + +struct ConfigurationStore +{ + uint8_t magic_number[configurationstore::kMagicNumberSize]; + uint8_t version[configurationstore::kVersionSize]; + uint8_t reserved[10]; + + common::store::Global global; + common::store::RemoteConfig remote_config; + common::store::Network network; + common::store::DisplayUdf display_udf; + common::store::DmxNode dmx_node; + common::store::OscClient osc_client; + common::store::OscServer osc_server; + common::store::DmxSend dmx_send; + common::store::DmxL6470 dmx_l6470; + common::store::DmxLed dmx_led; + common::store::DmxPwm dmx_pwm; + common::store::DmxSerial dmx_serial; + common::store::DmxMonitor dmx_monitor; + common::store::RdmDevice rdm_device; + common::store::RdmSensors rdm_sensors; + common::store::RdmSubdevices rdm_subdevices; + common::store::ShowFile show_file; + common::store::Ltc ltc; + common::store::LtcDisplay ltc_display; + common::store::LtcEtc ltc_etc; + common::store::TcNet tcnet; + common::store::Gps gps; + common::store::Midi midi; + common::store::RgbPanel rgb_panel; + common::store::Widget widget; +} PACKED; + +static_assert(offsetof(ConfigurationStore, global) == 16, "Wrong offset: global"); + +#if defined(_MSC_VER) +#pragma pack(pop) +#elif defined(__GNUC__) || defined(__clang__) +#undef PACKED +#else +#pragma pack() +#undef PACKED +#endif + +#endif // CONFIGURATIONSTORE_H_ diff --git a/lib-configstore/src/config_delay.cpp b/lib-configstore/src/config_delay.cpp index 1ff2fb4..a0cd4f5 100755 --- a/lib-configstore/src/config_delay.cpp +++ b/lib-configstore/src/config_delay.cpp @@ -1,13 +1,38 @@ -/* - * config_delay.cpp +/** + * @file config_delay.cpp + * */ +/* 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: -#include "configstore.h" + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. -namespace configstore { - void delay() { - ConfigStore::Get()->Delay(); - } -} // namespace configstore + * 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(DEBUG_CONFIGSTORE) +#undef NDEBUG +#endif + #include "firmware/debug/debug_debug.h" +namespace configstore +{ +void Delay() +{ + DEBUG_PUTS("not implemented"); +} +} // namespace configstore diff --git a/lib-configstore/src/configstore.cpp b/lib-configstore/src/configstore.cpp deleted file mode 100755 index 4b6bbf2..0000000 --- a/lib-configstore/src/configstore.cpp +++ /dev/null @@ -1,335 +0,0 @@ -/** - * @file configstore.cpp - * - */ -/* Copyright (C) 2018-2024 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 -#include -#include -#include - -#include "configstore.h" - -#include "hardware.h" - -#include "platform_configstore.h" - -#include "debug.h" - -namespace global { -extern int32_t *gp_nUtcOffset; -} // namespace global - -using namespace configstore; - -static constexpr uint8_t s_aSignature[] = {'A', 'v', 'V', 0x01}; -static constexpr uint32_t s_aStorSize[static_cast(Store::LAST)] = {96, 32, 64, 64, 32, 32, 480, 64, 32, 96, 48, 32, 944, 48, 64, 32, 96, 32, 1024, 32, 32, 64, 96, 32, 32, 320, 32}; -#ifndef NDEBUG -static constexpr char s_aStoreName[static_cast(Store::LAST)][16] = {"Network", "DMX", "Pixel", "LTC", "MIDI", "LTC ETC", "OSC Server", "TLC59711", "USB Pro", "RDM Device", "RConfig", "TCNet", "OSC Client", "Display", "LTC Display", "Monitor", "SparkFun", "Slush", "Motors", "Show", "Serial", "RDM Sensors", "RDM SubDevices", "GPS", "RGB Panel", "Node", "PCA9685"}; -#endif - -bool ConfigStore::s_bHaveFlashChip; -State ConfigStore::s_State; -uint32_t ConfigStore::s_nStartAddress; -uint32_t ConfigStore::s_nSpiFlashStoreSize; -uint32_t ConfigStore::s_nWaitMillis; -uint8_t ConfigStore::s_SpiFlashData[FlashStore::SIZE] SECTION_CONFIGSTORE; - -ConfigStore *ConfigStore::s_pThis; - -ConfigStore::ConfigStore() { - DEBUG_ENTRY - - static_assert(sizeof(s_aSignature) <= FlashStore::SIGNATURE_SIZE); - - assert(s_pThis == nullptr); - s_pThis = this; - - global::gp_nUtcOffset = reinterpret_cast(&s_SpiFlashData[FlashStore::SIGNATURE_SIZE]); - - s_bHaveFlashChip = StoreDevice::IsDetected(); - - assert(FlashStore::SIZE <= StoreDevice::GetSize()); - - const auto nEraseSize = StoreDevice::GetSectorSize(); - assert(nEraseSize <= FlashStore::SIZE); - - const auto nSectors = FlashStore::SIZE / nEraseSize; - - DEBUG_PRINTF("KB_NEEDED=%u, nEraseSize=%u, nSectors=%u", FlashStore::SIZE, nEraseSize, nSectors); - - assert((nSectors * nEraseSize) <= StoreDevice::GetSize()); - - s_nStartAddress = StoreDevice::GetSize() - (nSectors * nEraseSize); - - DEBUG_PRINTF("s_nStartAddress=%p", reinterpret_cast(s_nStartAddress)); - - if (s_bHaveFlashChip) { - storedevice::result result; - StoreDevice::Read(s_nStartAddress, FlashStore::SIZE, reinterpret_cast(&s_SpiFlashData), result); - assert(result == storedevice::result::OK); - } - - bool bSignatureOK = true; - - for (uint32_t i = 0; i < sizeof(s_aSignature); i++) { - if (s_aSignature[i] != s_SpiFlashData[i]) { - s_SpiFlashData[i] = s_aSignature[i]; - bSignatureOK = false; - } - } - - if (!bSignatureOK) { - DEBUG_PUTS("No signature"); - memset(&s_SpiFlashData[FlashStore::SIGNATURE_SIZE], 0, FlashStore::SIZE - FlashStore::SIGNATURE_SIZE); - s_State = State::CHANGED; - } - - s_nSpiFlashStoreSize = FlashStore::OFFSET_STORES; - - for (uint32_t j = 0; j < static_cast(Store::LAST); j++) { - s_nSpiFlashStoreSize += s_aStorSize[j]; - } - - DEBUG_PRINTF("FlashStore::OFFSET_STORES=%d, m_nSpiFlashStoreSize=%d", static_cast(FlashStore::OFFSET_STORES), s_nSpiFlashStoreSize); - - assert(s_nSpiFlashStoreSize <= FlashStore::SIZE); - - for (uint32_t nStore = 0; nStore < static_cast(Store::LAST); nStore++) { - auto *pSet = reinterpret_cast((&s_SpiFlashData[GetStoreOffset(static_cast(nStore))])); - if (*pSet == UINT32_MAX) { - *pSet = 0; - } - } - - auto *p = reinterpret_cast(&s_SpiFlashData[FlashStore::SIGNATURE_SIZE]); - if (p->nUtcOffset == -1) { - p->nUtcOffset = 0; - } - - DEBUG_PUTS(""); - debug_dump(s_SpiFlashData, FlashStore::SIZE); - - DEBUG_EXIT -} - -uint32_t ConfigStore::GetStoreOffset(Store store) { - assert(store < Store::LAST); - - uint32_t nOffset = FlashStore::OFFSET_STORES; - - for (uint32_t i = 0; i < static_cast(store); i++) { - nOffset += s_aStorSize[i]; - } - - DEBUG_PRINTF("nOffset=%d", nOffset); - - return nOffset; -} - -void ConfigStore::ResetSetList(Store store) { - assert(store < Store::LAST); - - auto *pbSetList = &s_SpiFlashData[GetStoreOffset(store)]; - - *pbSetList++ = 0x00; - *pbSetList++ = 0x00; - *pbSetList++ = 0x00; - *pbSetList = 0x00; - - s_State = State::CHANGED; -} - -void ConfigStore::Update(Store store, uint32_t nOffset, const void *pData, uint32_t nDataLength, uint32_t nSetList, uint32_t nOffsetSetList) { - DEBUG_ENTRY - DEBUG_PRINTF("[%s]:%u:%p, nOffset=%d, nDataLength=%d-%u, bSetList=0x%x, nOffsetSetList=%d", s_aStoreName[static_cast(store)], static_cast(store), pData, nOffset, nDataLength, static_cast(s_State), nSetList, nOffsetSetList); - - assert(store < Store::LAST); - assert(pData != nullptr); - assert((nOffset + nDataLength) <= s_aStorSize[static_cast(store)]); - - auto bIsChanged = false; - const auto nBase = nOffset + GetStoreOffset(store); - - const auto *pSrc = static_cast(pData); - auto *pDst = &s_SpiFlashData[nBase]; - - DEBUG_PRINTF("pSrc=%p [pData], pDst=%p", reinterpret_cast(pSrc), reinterpret_cast(pDst)); - -#if defined(__linux__) || defined (__APPLE__) -// debug_dump(pSrc, nDataLength); -// debug_dump(pDst, nDataLength); -#endif - - for (uint32_t i = 0; i < nDataLength; i++) { - if (*pSrc != *pDst) { - bIsChanged = true; - *pDst = *pSrc; - } - pDst++; - pSrc++; - } - - if (bIsChanged){ - auto *pSet = reinterpret_cast((&s_SpiFlashData[GetStoreOffset(store)] + nOffsetSetList)); - *pSet |= nSetList; - } - - if (bIsChanged) { - s_State = State::CHANGED; - } - - debug_dump(&s_SpiFlashData[GetStoreOffset(store)] + nOffsetSetList, 8); - DEBUG_EXIT -} - -void ConfigStore::Copy(const Store store, void *pData, uint32_t nDataLength, uint32_t nOffset, const bool doUpdate) { - DEBUG_ENTRY - DEBUG_PRINTF("[%s]:%u pData=%p, nDataLength=%u, nOffset=%u, doUpdate=%u", s_aStoreName[static_cast(store)], static_cast(store), pData, nDataLength, nOffset, doUpdate); - - assert(store < Store::LAST); - assert(pData != nullptr); - assert((nDataLength + nOffset) <= s_aStorSize[static_cast(store)]); - - const auto *pSrc = const_cast(&s_SpiFlashData[GetStoreOffset(store)]) + nOffset; - auto *pDst = static_cast(pData); - - auto isEmpty = true; - - for (uint32_t nIndex = 0; nIndex < nDataLength; nIndex++) { - if (pSrc[nIndex] != 0) { - isEmpty = false; - break; - } - } - - if (!isEmpty) { - memcpy(pDst, pSrc, nDataLength); - DEBUG_EXIT - return; - } - - if (doUpdate) { - Update(store, pData, nDataLength); - } - - DEBUG_EXIT -} - -void ConfigStore::Delay() { - if (s_State != State::IDLE) { - s_State = State::CHANGED; - } -} - -bool ConfigStore::Flash() { - if (__builtin_expect((s_State == State::IDLE), 1)) { - return false; - } - - switch (s_State) { - case State::CHANGED: - s_nWaitMillis = Hardware::Get()->Millis(); - s_State = State::CHANGED_WAITING; - return true; - case State::CHANGED_WAITING: - if ((Hardware::Get()->Millis() - s_nWaitMillis) < 100) { - return true; - } - s_State = State::ERASING; - return true; - break; - case State::ERASING: { - storedevice::result result; - if (StoreDevice::Erase(s_nStartAddress, FlashStore::SIZE, result)) { - s_nWaitMillis = Hardware::Get()->Millis(); - s_State = State::ERASED_WAITING; - } - assert(result == storedevice::result::OK); - DEBUG_PRINTF("s_State=%u", static_cast(s_State)); - return true; - } - break; - case State::ERASED_WAITING: - if ((Hardware::Get()->Millis() - s_nWaitMillis) < 100) { - return true; - } - s_State = State::ERASED; - return true; - break; - case State::ERASED: - s_State = State::WRITING; - return true; - break; - case State::WRITING: { - storedevice::result result; - if (StoreDevice::Write(s_nStartAddress, s_nSpiFlashStoreSize, reinterpret_cast(&s_SpiFlashData), result)) { - s_State = State::IDLE; - return false; - } - assert(result == storedevice::result::OK); - return true; - } - break; - default: - assert(0); - __builtin_unreachable(); - break; - } - - assert(0); - __builtin_unreachable(); - return false; -} - -void ConfigStore::Dump() { -#ifndef NDEBUG - if (!s_bHaveFlashChip) { - return; - } - - const auto IsWatchDog = Hardware::Get()->IsWatchdog(); - - if (IsWatchDog) { - Hardware::Get()->WatchdogStop(); - } - - debug_dump(s_SpiFlashData, FlashStore::OFFSET_STORES); - puts(""); - - for (uint32_t j = 0; j < static_cast(Store::LAST); j++) { - printf("Store [%s]:%d\n", s_aStoreName[j], j); - - auto *p = &s_SpiFlashData[GetStoreOffset(static_cast(j))]; - debug_dump(p, static_cast(s_aStorSize[j])); - - puts(""); - } - - if (IsWatchDog) { - Hardware::Get()->WatchdogInit(); - } - - printf("m_tState=%d\n", static_cast(s_State)); -#endif -} diff --git a/lib-configstore/src/envparams.cpp b/lib-configstore/src/envparams.cpp deleted file mode 100755 index cead222..0000000 --- a/lib-configstore/src/envparams.cpp +++ /dev/null @@ -1,132 +0,0 @@ -/** - * @file envparams.cpp - * - */ -/* Copyright (C) 2024 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. - */ - -#if !defined(__clang__) // Needed for compiling on MacOS -# pragma GCC push_options -# pragma GCC optimize ("Os") -#endif - -#include -#include -#include - -#include "envparams.h" -#include "envparamsconst.h" - -#include "readconfigfile.h" -#include "sscan.h" - -#include "propertiesbuilder.h" - -#include "configstore.h" - -#include "debug.h" - -EnvParams::EnvParams() { - DEBUG_ENTRY - - DEBUG_EXIT -} - -void EnvParams::LoadAndSet() { - DEBUG_ENTRY - - assert(ConfigStore::Get() != nullptr); - -#if !defined(DISABLE_FS) - ReadConfigFile configfile(EnvParams::staticCallbackFunction, this); - configfile.Read(EnvParamsConst::FILE_NAME); -#endif - -#ifndef NDEBUG - Dump(); -#endif - - DEBUG_EXIT -} - -void EnvParams::LoadAndSet(const char *pBuffer, uint32_t nLength) { - DEBUG_ENTRY - - assert(ConfigStore::Get() != nullptr); - - assert(pBuffer != nullptr); - assert(nLength != 0); - - ReadConfigFile config(EnvParams::staticCallbackFunction, this); - config.Read(pBuffer, nLength); - -#ifndef NDEBUG - Dump(); -#endif - DEBUG_EXIT -} - -void EnvParams::callbackFunction(const char *pLine) { - assert(pLine != nullptr); - - int8_t nHours; - uint8_t nMinutes; - - if (Sscan::UtcOffset(pLine, EnvParamsConst::UTC_OFFSET, nHours, nMinutes) == Sscan::OK) { - ConfigStore::Get()->SetEnvUtcOffset(nHours, nMinutes); - return; - } -} - -void EnvParams::staticCallbackFunction(void *p, const char *s) { - assert(p != nullptr); - assert(s != nullptr); - - (static_cast(p))->callbackFunction(s); -} - -void EnvParams::Builder(char *pBuffer, uint32_t nLength, uint32_t& nSize) { - DEBUG_ENTRY - - assert(pBuffer != nullptr); - - PropertiesBuilder builder(EnvParamsConst::FILE_NAME, pBuffer, nLength); - - int8_t nHours; - uint8_t nMinutes; - ConfigStore::Get()->GetEnvUtcOffset(nHours, nMinutes); - builder.AddUtcOffset(EnvParamsConst::UTC_OFFSET, nHours, nMinutes); - - nSize = builder.GetSize(); - - DEBUG_PRINTF("nSize=%d", nSize); - DEBUG_EXIT -} - -void EnvParams::Dump() { - printf("%s::%s \'%s\':\n", __FILE__, __FUNCTION__, EnvParamsConst::FILE_NAME); - - puts("UTC Offset"); - int8_t nHours; - uint8_t nMinutes; - ConfigStore::Get()->GetEnvUtcOffset(nHours, nMinutes); - printf(" %s=%.2d:%.2u [%d]\n", EnvParamsConst::UTC_OFFSET, nHours, nMinutes, ConfigStore::Get()->GetEnvUtcOffset()); -} diff --git a/lib-configstore/include/envparamsconst.h b/lib-configstore/src/set_factory_defaults.cpp similarity index 78% rename from lib-configstore/include/envparamsconst.h rename to lib-configstore/src/set_factory_defaults.cpp index dcc2ea2..bc66971 100755 --- a/lib-configstore/include/envparamsconst.h +++ b/lib-configstore/src/set_factory_defaults.cpp @@ -1,8 +1,8 @@ /** - * @file envparamsconst.h + * @file set_factory_defaults.cpp * */ -/* 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,13 +23,16 @@ * THE SOFTWARE. */ -#ifndef ENVPARAMSCONST_H_ -#define ENVPARAMSCONST_H_ +#if defined(DEBUG_CONFIGSTORE) +#undef NDEBUG +#endif -struct EnvParamsConst { - static const char FILE_NAME[]; + #include "firmware/debug/debug_debug.h" - static const char UTC_OFFSET[]; -}; - -#endif /* ENVPARAMSCONST_H_ */ +namespace configstore +{ +void SetFactoryDefaults() +{ + DEBUG_PUTS("Not implemented"); +} +} // namespace configstore diff --git a/lib-device/.cproject b/lib-device/.cproject index 5b820ff..66d010b 100644 --- a/lib-device/.cproject +++ b/lib-device/.cproject @@ -101,7 +101,6 @@ - @@ -116,7 +115,6 @@ - @@ -131,7 +129,6 @@ @@ -122,7 +120,6 @@