From 9f0788fd9e3236c154d5850d23cbad1f2caa8013 Mon Sep 17 00:00:00 2001 From: James Smith Date: Thu, 8 Jan 2026 14:09:28 -0800 Subject: [PATCH 01/15] Drop cmake build wrapper, just use SLC --- firmware/.gitignore | 2 +- firmware/cmake/openocd.cmake | 11 - firmware/cmake/ota.cmake | 10 - .../cmake/toolchains/arm-cortex-m33.cmake | 32 -- firmware/cmake/toolchains/arm-cortex-m4.cmake | 32 -- firmware/ldscripts/efr32xg22.ld | 251 --------------- firmware/ldscripts/efr32xg24.ld | 251 --------------- firmware/libsi/libsi.slcc | 23 ++ firmware/libsi/libsi.slce | 10 + firmware/libsi/src/platform/efr32/si_efr32.c | 2 +- firmware/libwavebird/CMakeLists.txt | 32 +- firmware/libwavebird/README.md | 16 +- firmware/libwavebird/libwavebird.slcc | 26 ++ firmware/libwavebird/libwavebird.slce | 10 + .../platform/efr32/efr32xg22/rail_config.c | 292 ++++++++++++++++++ .../platform/efr32/efr32xg22/rail_config.h | 49 +++ firmware/libwavebird/test/CMakeLists.txt | 18 +- firmware/libwavebird/test/test_bch3121.c | 14 +- firmware/libwavebird/test/test_main.c | 28 -- firmware/libwavebird/test/test_packet.c | 14 +- firmware/receiver/CMakeLists.txt | 50 --- firmware/receiver/boards/efr32xg22e.slcc | 32 ++ firmware/receiver/boards/rf-bm-bg22c3.slcc | 31 ++ .../receiver/config/efr32xg22e/board_config.h | 29 -- .../config/rf-bm-bg22c3/board_config.h | 29 -- firmware/receiver/src/main.c | 67 ++-- firmware/receiver/src/serial.c | 93 ------ firmware/receiver/src/serial.h | 13 - firmware/receiver/src/version.h | 12 +- firmware/receiver/wavephoenix.slce | 10 + firmware/receiver/wavephoenix.slcp | 32 ++ 31 files changed, 589 insertions(+), 932 deletions(-) delete mode 100644 firmware/cmake/openocd.cmake delete mode 100644 firmware/cmake/ota.cmake delete mode 100644 firmware/cmake/toolchains/arm-cortex-m33.cmake delete mode 100644 firmware/cmake/toolchains/arm-cortex-m4.cmake delete mode 100644 firmware/ldscripts/efr32xg22.ld delete mode 100644 firmware/ldscripts/efr32xg24.ld create mode 100644 firmware/libsi/libsi.slcc create mode 100644 firmware/libsi/libsi.slce create mode 100644 firmware/libwavebird/libwavebird.slcc create mode 100644 firmware/libwavebird/libwavebird.slce create mode 100644 firmware/libwavebird/src/platform/efr32/efr32xg22/rail_config.c create mode 100644 firmware/libwavebird/src/platform/efr32/efr32xg22/rail_config.h delete mode 100644 firmware/libwavebird/test/test_main.c delete mode 100644 firmware/receiver/CMakeLists.txt create mode 100644 firmware/receiver/boards/efr32xg22e.slcc create mode 100644 firmware/receiver/boards/rf-bm-bg22c3.slcc delete mode 100644 firmware/receiver/config/efr32xg22e/board_config.h delete mode 100644 firmware/receiver/config/rf-bm-bg22c3/board_config.h delete mode 100644 firmware/receiver/src/serial.c delete mode 100644 firmware/receiver/src/serial.h create mode 100644 firmware/receiver/wavephoenix.slce create mode 100644 firmware/receiver/wavephoenix.slcp diff --git a/firmware/.gitignore b/firmware/.gitignore index 209e0f1..1cc3589 100644 --- a/firmware/.gitignore +++ b/firmware/.gitignore @@ -1,5 +1,5 @@ # Autogenerated sources -autogen/ +target/ # Python .venv/ diff --git a/firmware/cmake/openocd.cmake b/firmware/cmake/openocd.cmake deleted file mode 100644 index 748efa6..0000000 --- a/firmware/cmake/openocd.cmake +++ /dev/null @@ -1,11 +0,0 @@ -function(add_openocd_flash_target TARGET) - set(BINARY "${TARGET}.hex") - - add_custom_target( - "${TARGET}_flash_openocd" - COMMAND - openocd -f "interface/cmsis-dap.cfg" -c "transport select swd" -f "target/efm32s2.cfg" -c - "init; halt; flash write_image erase ${BINARY}; reset run; exit" - DEPENDS ${TARGET} - ) -endfunction() diff --git a/firmware/cmake/ota.cmake b/firmware/cmake/ota.cmake deleted file mode 100644 index 79fd2d0..0000000 --- a/firmware/cmake/ota.cmake +++ /dev/null @@ -1,10 +0,0 @@ -function(add_ota_flash_target TARGET) - set(BINARY "${TARGET}.gbl") - - add_custom_target( - "${TARGET}_flash_ota" - COMMAND - wavephoenix flash ${BINARY} - DEPENDS ${TARGET} - ) -endfunction() diff --git a/firmware/cmake/toolchains/arm-cortex-m33.cmake b/firmware/cmake/toolchains/arm-cortex-m33.cmake deleted file mode 100644 index f2fd982..0000000 --- a/firmware/cmake/toolchains/arm-cortex-m33.cmake +++ /dev/null @@ -1,32 +0,0 @@ -# Toolchain file for ARM Cortex-M33 Gecko SoCs - -# Configure for a generic ARM embedded build -set(CMAKE_SYSTEM_NAME Generic) -set(CMAKE_SYSTEM_PROCESSOR arm) -set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) - -# Point to the arm-none-eabi toolchain -set(CMAKE_C_COMPILER arm-none-eabi-gcc) -set(CMAKE_CXX_COMPILER arm-none-eabi-g++) -set(CMAKE_ASM_COMPILER arm-none-eabi-g++) - -# Determine the ARM sysroot -execute_process( - COMMAND arm-none-eabi-gcc --print-sysroot - OUTPUT_VARIABLE ARM_SYSROOT - OUTPUT_STRIP_TRAILING_WHITESPACE -) -set(CMAKE_SYSROOT ${ARM_SYSROOT}) - -# Set the (Cortex-M33 specific) compiler and linker flags -set(CMAKE_C_FLAGS "-mcpu=cortex-m33 -mthumb -mfpu=fpv5-sp-d16 -mfloat-abi=hard -mcmse -Wall -ffunction-sections -fdata-sections -fomit-frame-pointer -fno-builtin") -set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS}") -set(CMAKE_ASM_FLAGS "${CMAKE_C_FLAGS} -x assembler-with-cpp") -set(CMAKE_EXE_LINKER_FLAGS "-mcpu=cortex-m33 -mthumb -mfpu=fpv5-sp-d16 -mfloat-abi=hard -mcmse -Wl,--gc-sections,--print-memory-usage,--no-warn-rwx-segments -specs=nano.specs") - -# Set flags based on the build type -if(CMAKE_BUILD_TYPE STREQUAL "Debug") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Og -g -DDEBUG") -elseif(CMAKE_BUILD_TYPE STREQUAL "Release") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -DNDEBUG") -endif() \ No newline at end of file diff --git a/firmware/cmake/toolchains/arm-cortex-m4.cmake b/firmware/cmake/toolchains/arm-cortex-m4.cmake deleted file mode 100644 index ca3d5ce..0000000 --- a/firmware/cmake/toolchains/arm-cortex-m4.cmake +++ /dev/null @@ -1,32 +0,0 @@ -# Toolchain file for ARM Cortex-M4 Gecko SoCs - -# Configure for a generic ARM embedded build -set(CMAKE_SYSTEM_NAME Generic) -set(CMAKE_SYSTEM_PROCESSOR arm) -set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) - -# Point to the arm-none-eabi toolchain -set(CMAKE_C_COMPILER arm-none-eabi-gcc) -set(CMAKE_CXX_COMPILER arm-none-eabi-g++) -set(CMAKE_ASM_COMPILER arm-none-eabi-g++) - -# Determine the ARM sysroot -execute_process( - COMMAND arm-none-eabi-gcc --print-sysroot - OUTPUT_VARIABLE ARM_SYSROOT - OUTPUT_STRIP_TRAILING_WHITESPACE -) -set(CMAKE_SYSROOT ${ARM_SYSROOT}) - -# Set the (Cortex-M33 specific) compiler and linker flags -set(CMAKE_C_FLAGS "-mcpu=cortex-m4 -mthumb -Wall -ffunction-sections -fdata-sections -fomit-frame-pointer -fno-builtin") -set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS}") -set(CMAKE_ASM_FLAGS "${CMAKE_C_FLAGS} -x assembler-with-cpp") -set(CMAKE_EXE_LINKER_FLAGS "-mcpu=cortex-m4 -mthumb -Wl,--gc-sections,--print-memory-usage,--no-warn-rwx-segments -specs=nano.specs") - -# Set flags based on the build type -if(CMAKE_BUILD_TYPE STREQUAL "Debug") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Og -g -DDEBUG") -elseif(CMAKE_BUILD_TYPE STREQUAL "Release") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -DNDEBUG") -endif() \ No newline at end of file diff --git a/firmware/ldscripts/efr32xg22.ld b/firmware/ldscripts/efr32xg22.ld deleted file mode 100644 index 26307db..0000000 --- a/firmware/ldscripts/efr32xg22.ld +++ /dev/null @@ -1,251 +0,0 @@ -/***************************************************************************//** - * GCC Linker script for Silicon Labs devices - ******************************************************************************* - * # License - * Copyright 2020 Silicon Laboratories Inc. www.silabs.com - ******************************************************************************* - * - * SPDX-License-Identifier: Zlib - * - * The licensor of this software is Silicon Laboratories Inc. - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - ******************************************************************************/ - -MEMORY -{ - FLASH (rx) : ORIGIN = 0x12000, LENGTH = 432K - RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 32K -} - -ENTRY(Reset_Handler) - -SECTIONS -{ - - .text : - { - linker_vectors_begin = .; - KEEP(*(.vectors)) - linker_vectors_end = .; - - __Vectors_End = .; - __Vectors_Size = __Vectors_End - __Vectors; - - linker_code_begin = .; - *(SORT_BY_ALIGNMENT(.text*)) - . = ALIGN(32); - linker_code_end = .; - - KEEP(*(.init)) - KEEP(*(.fini)) - - /* .ctors */ - *crtbegin.o(.ctors) - *crtbegin?.o(.ctors) - *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) - *(SORT(.ctors.*)) - *(.ctors) - - /* .dtors */ - *crtbegin.o(.dtors) - *crtbegin?.o(.dtors) - *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) - *(SORT(.dtors.*)) - *(.dtors) - - __code_classification_validator_start__ = .; - . = . + 0x20; - *(code_classification_validator) - . = ALIGN(32); - __code_classification_validator_end__ = .; - - *(.rodata*) - *(.eh_frame*) - } > FLASH - - .ARM.extab : - { - *(.ARM.extab* .gnu.linkonce.armextab.*) - } > FLASH - - __exidx_start = .; - .ARM.exidx : - { - *(.ARM.exidx* .gnu.linkonce.armexidx.*) - } > FLASH - __exidx_end = .; - - .copy.table : - { - . = ALIGN(4); - __copy_table_start__ = .; - - LONG (__etext) - LONG (__data_start__) - LONG ((__data_end__ - __data_start__) / 4) - - /* Add each additional data section here */ -/* - LONG (__etext2) - LONG (__data2_start__) - LONG ((__data2_end__ - __data2_start__) / 4) -*/ - __copy_table_end__ = .; - } > FLASH - - .zero.table : - { - . = ALIGN(4); - __zero_table_start__ = .; - /* Add each additional bss section here */ -/* - LONG (__bss2_start__) - LONG ((__bss2_end__ - __bss2_start__) / 4) -*/ - __zero_table_end__ = .; - } > FLASH - - __etext = .; - - /* Start placing output sections which are loaded into RAM */ - . = ORIGIN(RAM); - - .stack ALIGN(8) (NOLOAD): - { - __StackLimit = .; - KEEP(*(.stack*)) - . = ALIGN(4); - __StackTop = .; - PROVIDE(__stack = __StackTop); - } > RAM - - - .noinit . (NOLOAD): - { - *(.noinit*); - } > RAM - - .data . : AT (__etext) - { - . = ALIGN(4); - __data_start__ = .; - *(vtable) - *(SORT_BY_ALIGNMENT(.data*)) - . = ALIGN(4); - - PROVIDE(__ram_func_section_start = .); - *(.ram) - PROVIDE(__ram_func_section_end = .); - - . = ALIGN(4); - /* preinit data */ - PROVIDE_HIDDEN (__preinit_array_start = .); - KEEP(*(.preinit_array)) - PROVIDE_HIDDEN (__preinit_array_end = .); - - . = ALIGN(4); - /* init data */ - PROVIDE_HIDDEN (__init_array_start = .); - KEEP(*(SORT(.init_array.*))) - KEEP(*(.init_array)) - PROVIDE_HIDDEN (__init_array_end = .); - - . = ALIGN(4); - /* finit data */ - PROVIDE_HIDDEN (__fini_array_start = .); - KEEP(*(SORT(.fini_array.*))) - KEEP(*(.fini_array)) - PROVIDE_HIDDEN (__fini_array_end = .); - - . = ALIGN(4); - /* All data end */ - __data_end__ = .; - - } > RAM - - .bss . : - { - . = ALIGN(4); - __bss_start__ = .; - *(SORT_BY_ALIGNMENT(.bss*)) - *(COMMON) - . = ALIGN(4); - __bss_end__ = .; - } > RAM - - __ramfuncs_start__ = .; - - __vma_ramfuncs_start__ = .; - __lma_ramfuncs_start__ = __etext + SIZEOF(.data); - - __text_application_ram_offset__ = . - __vma_ramfuncs_start__; - text_application_ram . : AT(__lma_ramfuncs_start__ + __text_application_ram_offset__) - { - . = ALIGN(4); - __text_application_ram_start__ = .; - *(text_application_ram) - . = ALIGN(4); - __text_application_ram_end__ = .; - } > RAM - - . = ALIGN(4); - __vma_ramfuncs_end__ = .; - __lma_ramfuncs_end__ = __lma_ramfuncs_start__ + __text_application_ram_offset__ + SIZEOF(text_application_ram); - - __ramfuncs_end__ = .; - - .heap (COPY): - { - __HeapBase = .; - __end__ = .; - end = __end__; - _end = __end__; - KEEP(*(.heap*)) - __HeapLimit = ORIGIN(RAM) + LENGTH(RAM); - } > RAM - - __heap_size = __HeapLimit - __HeapBase; - __ram_end__ = 0x20000000 + 0x8000; - __main_flash_end__ = 0x6000 + 0x78000; - - /* This is where we handle flash storage blocks. We use dummy sections for finding the configured - * block sizes and then "place" them at the end of flash when the size is known. */ - .internal_storage (DSECT) : { - KEEP(*(.internal_storage*)) - } > FLASH - - - .nvm (DSECT) : { - KEEP(*(.simee*)) - } > FLASH - - linker_nvm_end = __main_flash_end__; - linker_nvm_begin = linker_nvm_end - SIZEOF(.nvm); - linker_nvm_size = SIZEOF(.nvm); - linker_storage_end = linker_nvm_begin; - __nvm3Base = linker_nvm_begin; - - linker_storage_begin = linker_storage_end - SIZEOF(.internal_storage); - linker_storage_size = SIZEOF(.internal_storage); - ASSERT((linker_storage_begin >= (__etext + SIZEOF(.data))), "FLASH memory overflowed !") - - - app_flash_end = 0x6000 + 0x78000; - ASSERT( (linker_nvm_begin + SIZEOF(.nvm)) <= app_flash_end, "NVM3 is excessing the flash size !") -} diff --git a/firmware/ldscripts/efr32xg24.ld b/firmware/ldscripts/efr32xg24.ld deleted file mode 100644 index b242ce7..0000000 --- a/firmware/ldscripts/efr32xg24.ld +++ /dev/null @@ -1,251 +0,0 @@ -/***************************************************************************//** - * GCC Linker script for Silicon Labs devices - ******************************************************************************* - * # License - * Copyright 2020 Silicon Laboratories Inc. www.silabs.com - ******************************************************************************* - * - * SPDX-License-Identifier: Zlib - * - * The licensor of this software is Silicon Laboratories Inc. - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - ******************************************************************************/ - -MEMORY -{ - FLASH (rx) : ORIGIN = 0x8012000, LENGTH = 0x16e000 - RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x40000 -} - -ENTRY(Reset_Handler) - -SECTIONS -{ - - .text : - { - linker_vectors_begin = .; - KEEP(*(.vectors)) - linker_vectors_end = .; - - __Vectors_End = .; - __Vectors_Size = __Vectors_End - __Vectors; - - linker_code_begin = .; - *(SORT_BY_ALIGNMENT(.text*)) - . = ALIGN(32); - linker_code_end = .; - - KEEP(*(.init)) - KEEP(*(.fini)) - - /* .ctors */ - *crtbegin.o(.ctors) - *crtbegin?.o(.ctors) - *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) - *(SORT(.ctors.*)) - *(.ctors) - - /* .dtors */ - *crtbegin.o(.dtors) - *crtbegin?.o(.dtors) - *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) - *(SORT(.dtors.*)) - *(.dtors) - - __code_classification_validator_start__ = .; - . = . + 0x20; - *(code_classification_validator) - . = ALIGN(32); - __code_classification_validator_end__ = .; - - *(.rodata*) - *(.eh_frame*) - } > FLASH - - .ARM.extab : - { - *(.ARM.extab* .gnu.linkonce.armextab.*) - } > FLASH - - __exidx_start = .; - .ARM.exidx : - { - *(.ARM.exidx* .gnu.linkonce.armexidx.*) - } > FLASH - __exidx_end = .; - - .copy.table : - { - . = ALIGN(4); - __copy_table_start__ = .; - - LONG (__etext) - LONG (__data_start__) - LONG ((__data_end__ - __data_start__) / 4) - - /* Add each additional data section here */ -/* - LONG (__etext2) - LONG (__data2_start__) - LONG ((__data2_end__ - __data2_start__) / 4) -*/ - __copy_table_end__ = .; - } > FLASH - - .zero.table : - { - . = ALIGN(4); - __zero_table_start__ = .; - /* Add each additional bss section here */ -/* - LONG (__bss2_start__) - LONG ((__bss2_end__ - __bss2_start__) / 4) -*/ - __zero_table_end__ = .; - } > FLASH - - __etext = .; - - /* Start placing output sections which are loaded into RAM */ - . = ORIGIN(RAM); - - .stack ALIGN(8) (NOLOAD): - { - __StackLimit = .; - KEEP(*(.stack*)) - . = ALIGN(4); - __StackTop = .; - PROVIDE(__stack = __StackTop); - } > RAM - - - .noinit . (NOLOAD): - { - *(.noinit*); - } > RAM - - .data . : AT (__etext) - { - . = ALIGN(4); - __data_start__ = .; - *(vtable) - *(SORT_BY_ALIGNMENT(.data*)) - . = ALIGN(4); - - PROVIDE(__ram_func_section_start = .); - *(.ram) - PROVIDE(__ram_func_section_end = .); - - . = ALIGN(4); - /* preinit data */ - PROVIDE_HIDDEN (__preinit_array_start = .); - KEEP(*(.preinit_array)) - PROVIDE_HIDDEN (__preinit_array_end = .); - - . = ALIGN(4); - /* init data */ - PROVIDE_HIDDEN (__init_array_start = .); - KEEP(*(SORT(.init_array.*))) - KEEP(*(.init_array)) - PROVIDE_HIDDEN (__init_array_end = .); - - . = ALIGN(4); - /* finit data */ - PROVIDE_HIDDEN (__fini_array_start = .); - KEEP(*(SORT(.fini_array.*))) - KEEP(*(.fini_array)) - PROVIDE_HIDDEN (__fini_array_end = .); - - . = ALIGN(4); - /* All data end */ - __data_end__ = .; - - } > RAM - - .bss . : - { - . = ALIGN(4); - __bss_start__ = .; - *(SORT_BY_ALIGNMENT(.bss*)) - *(COMMON) - . = ALIGN(4); - __bss_end__ = .; - } > RAM - - __ramfuncs_start__ = .; - - __vma_ramfuncs_start__ = .; - __lma_ramfuncs_start__ = __etext + SIZEOF(.data); - - __text_application_ram_offset__ = . - __vma_ramfuncs_start__; - text_application_ram . : AT(__lma_ramfuncs_start__ + __text_application_ram_offset__) - { - . = ALIGN(4); - __text_application_ram_start__ = .; - *(text_application_ram) - . = ALIGN(4); - __text_application_ram_end__ = .; - } > RAM - - . = ALIGN(4); - __vma_ramfuncs_end__ = .; - __lma_ramfuncs_end__ = __lma_ramfuncs_start__ + __text_application_ram_offset__ + SIZEOF(text_application_ram); - - __ramfuncs_end__ = .; - - .heap (COPY): - { - __HeapBase = .; - __end__ = .; - end = __end__; - _end = __end__; - KEEP(*(.heap*)) - __HeapLimit = ORIGIN(RAM) + LENGTH(RAM); - } > RAM - - __heap_size = __HeapLimit - __HeapBase; - __ram_end__ = 0x20000000 + 0x40000; - __main_flash_end__ = 0x8012000 + 0x16e000; - - /* This is where we handle flash storage blocks. We use dummy sections for finding the configured - * block sizes and then "place" them at the end of flash when the size is known. */ - .internal_storage (DSECT) : { - KEEP(*(.internal_storage*)) - } > FLASH - - - .nvm (DSECT) : { - KEEP(*(.simee*)) - } > FLASH - - linker_nvm_end = __main_flash_end__; - linker_nvm_begin = linker_nvm_end - SIZEOF(.nvm); - linker_nvm_size = SIZEOF(.nvm); - linker_storage_end = linker_nvm_begin; - __nvm3Base = linker_nvm_begin; - - linker_storage_begin = linker_storage_end - SIZEOF(.internal_storage); - linker_storage_size = SIZEOF(.internal_storage); - ASSERT((linker_storage_begin >= (__etext + SIZEOF(.data))), "FLASH memory overflowed !") - - - app_flash_end = 0x8012000 + 0x16e000; - ASSERT( (linker_nvm_begin + SIZEOF(.nvm)) <= app_flash_end, "NVM3 is excessing the flash size !") -} diff --git a/firmware/libsi/libsi.slcc b/firmware/libsi/libsi.slcc new file mode 100644 index 0000000..d430ea4 --- /dev/null +++ b/firmware/libsi/libsi.slcc @@ -0,0 +1,23 @@ +id: libsi +label: libsi +package: "ext-comp" +description: > + Implementation of the Joybus protocol used by N64 and GameCube controllers. +category: External +quality: production + +provides: + - name: libsi + +requires: + - name: emlib + - name: dmadrv + +include: + - path: include + +source: + - path: src/commands.c + - path: src/crc8.c + - path: src/device/gc_controller.c + - path: src/platform/efr32/si_efr32.c diff --git a/firmware/libsi/libsi.slce b/firmware/libsi/libsi.slce new file mode 100644 index 0000000..1c9ee24 --- /dev/null +++ b/firmware/libsi/libsi.slce @@ -0,0 +1,10 @@ +id: libsi +label: libsi +version: 1.0.0 + +sdk: + id: simplicity_sdk + version: 2025.6.2 + +component_path: + - path: . diff --git a/firmware/libsi/src/platform/efr32/si_efr32.c b/firmware/libsi/src/platform/efr32/si_efr32.c index d65f678..01851e5 100644 --- a/firmware/libsi/src/platform/efr32/si_efr32.c +++ b/firmware/libsi/src/platform/efr32/si_efr32.c @@ -194,7 +194,7 @@ static void init_rx(uint8_t port, uint8_t pin, uint32_t freq) (port << _GPIO_TIMER_CC0ROUTE_PORT_SHIFT) | (pin << _GPIO_TIMER_CC0ROUTE_PIN_SHIFT); // Set LDMA interrupts as high priority, since we need to reply immediately on completed RX - NVIC_SetPriority(LDMA_IRQn, CORE_INTERRUPT_HIGHEST_PRIORITY); + NVIC_SetPriority(LDMA_IRQn, 0); } // Initialize for SI data transmission diff --git a/firmware/libwavebird/CMakeLists.txt b/firmware/libwavebird/CMakeLists.txt index c73d246..790dfe6 100644 --- a/firmware/libwavebird/CMakeLists.txt +++ b/firmware/libwavebird/CMakeLists.txt @@ -4,36 +4,14 @@ cmake_minimum_required(VERSION "3.21") # Configure project and languages project(wavebird LANGUAGES C) +# Add CTest for running tests +include(CTest) + # Define the target and add the source files add_library(wavebird STATIC "src/bch3121.c" "src/packet.c") # Specify the include paths target_include_directories(wavebird PRIVATE src/autogen PUBLIC include) -# EFR32 platform specific settings -if(CMAKE_CROSSCOMPILING) - # Download and make the GeckoSDK CMake targets available - if(NOT GeckoSDK_FOUND) - include(FetchContent) - FetchContent_Declare( - GeckoSDK - GIT_REPOSITORY https://github.com/loopj/gecko-sdk-cmake.git - GIT_TAG main - ) - FetchContent_MakeAvailable(GeckoSDK) - endif() - - # Configure automatic generation of rail_config.c and rail_config.h files - set_rail_config_paths("config/rail/radio_settings_${GECKO_SDK_RAIL_LIB_NAME}.radioconf" "src/autogen") - - # Add platform-specific source files - target_sources(wavebird PRIVATE "src/platform/efr32/radio_efr32.c" "src/autogen/rail_config.c") - - # Depend on emlib and rail_lib from the Gecko SDK - target_link_libraries(wavebird GeckoSDK::emlib GeckoSDK::rail_lib) -endif() - -# Add the test target -if(NOT CMAKE_CROSSCOMPILING) - add_subdirectory(test) -endif() +# Add the test targets +add_subdirectory(test) diff --git a/firmware/libwavebird/README.md b/firmware/libwavebird/README.md index 1af0e37..4719465 100644 --- a/firmware/libwavebird/README.md +++ b/firmware/libwavebird/README.md @@ -16,14 +16,14 @@ I currently don't know of any other SoCs which support the WaveBird's FSK+DSSS 1 ## Running tests -- Build the test suite +Build the test suite - ```bash - cmake -Bbuild && cmake --build build --target test_wavebird - ``` +```bash +cmake -Bbuild && cmake --build build +``` -- Run the tests +Run the tests - ```bash - ./build/test/test_wavebird - ``` +```bash +ctest --test-dir build --output-on-failure +``` \ No newline at end of file diff --git a/firmware/libwavebird/libwavebird.slcc b/firmware/libwavebird/libwavebird.slcc new file mode 100644 index 0000000..3520537 --- /dev/null +++ b/firmware/libwavebird/libwavebird.slcc @@ -0,0 +1,26 @@ +id: libwavebird +label: libwavebird +package: "ext-comp" +description: > + An open source implementation of the WaveBird protocol. +category: External +quality: production + +requires: + - name: rail_lib + - name: rail_util_pa + +provides: + - name: libwavebird + +include: + - path: include + - path: src/platform/efr32/efr32xg22 + condition: [device_generic_family_efr32xg22] + +source: + - path: src/bch3121.c + - path: src/packet.c + - path: src/platform/efr32/radio_efr32.c + - path: src/platform/efr32/efr32xg22/rail_config.c + condition: [device_generic_family_efr32xg22] \ No newline at end of file diff --git a/firmware/libwavebird/libwavebird.slce b/firmware/libwavebird/libwavebird.slce new file mode 100644 index 0000000..56f7c52 --- /dev/null +++ b/firmware/libwavebird/libwavebird.slce @@ -0,0 +1,10 @@ +id: libwavebird +label: libwavebird +version: 1.0.0 + +sdk: + id: simplicity_sdk + version: 2025.6.2 + +component_path: + - path: . diff --git a/firmware/libwavebird/src/platform/efr32/efr32xg22/rail_config.c b/firmware/libwavebird/src/platform/efr32/efr32xg22/rail_config.c new file mode 100644 index 0000000..d36353f --- /dev/null +++ b/firmware/libwavebird/src/platform/efr32/efr32xg22/rail_config.c @@ -0,0 +1,292 @@ +/***************************************************************************//** + * @brief RAIL Configuration + * @details + * WARNING: Auto-Generated Radio Config - DO NOT EDIT + * Radio Configurator Version: 2304.5.2 + * RAIL Adapter Version: 2.4.33 + * RAIL Compatibility: 2.x + ******************************************************************************* + * # License + * Copyright 2019 Silicon Laboratories Inc. www.silabs.com + ******************************************************************************* + * + * SPDX-License-Identifier: Zlib + * + * The licensor of this software is Silicon Laboratories Inc. + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + ******************************************************************************/ +#include "em_device.h" +#include "rail_config.h" + +uint32_t RAILCb_CalcSymbolRate(RAIL_Handle_t railHandle) +{ + (void) railHandle; + return 0U; +} + +uint32_t RAILCb_CalcBitRate(RAIL_Handle_t railHandle) +{ + (void) railHandle; + return 0U; +} + +void RAILCb_ConfigFrameTypeLength(RAIL_Handle_t railHandle, + const RAIL_FrameType_t *frameType) +{ + (void) railHandle; + (void) frameType; +} + +static const uint8_t irCalConfig[] = { + 25, 63, 1, 6, 4, 16, 1, 0, 0, 1, 1, 6, 0, 16, 39, 0, 0, 5, 0, 1, 1, 0, 0, 0, 0, 0 +}; + +static const int32_t timingConfig[] = { + 0, 0, 0 +}; + +static const uint8_t hfxoRetimingConfigEntries[] = { + 2, 0, 0, 0, 0x00, 0xf0, 0x49, 0x02, 6, 20, 0, 0, 0x00, 0xe0, 0x93, 0x04, 5, 56, 0, 0, 0xa0, 0x08, 0, 0, 0, 0, 0x58, 0x09, 1, 4, 7, 6, 0x10, 0x0a, 1, 4, 7, 7, 0xc8, 0x0a, 0, 4, 8, 7, 0x80, 0x0b, 0, 4, 8, 8, 0x38, 0x0c, 0, 4, 9, 8, 0x61, 0x08, 0, 0, 0, 0, 0x68, 0x08, 0, 0, 0, 0, 0xc7, 0x09, 1, 4, 4, 3, 0x2c, 0x0b, 1, 4, 4, 4, 0x92, 0x0c, 1, 4, 5, 4 +}; + +static RAIL_ChannelConfigEntryAttr_t channelConfigEntryAttr = { +#if RAIL_SUPPORTS_OFDM_PA + { + { 0xFFFFFFFFUL }, + { 0xFFFFFFFFUL, 0xFFFFFFFFUL } + } +#else // RAIL_SUPPORTS_OFDM_PA + { 0xFFFFFFFFUL }, +#endif // RAIL_SUPPORTS_OFDM_PA +}; + +static const uint32_t phyInfo[] = { + 16UL, + 0x00888888UL, // 136.53333333333333 + (uint32_t) NULL, + (uint32_t) irCalConfig, + (uint32_t) timingConfig, + 0x00000000UL, + 10800000UL, + 34560000UL, + 1440000UL, + 0x00F50F01UL, + 0x02103A3DUL, + (uint32_t) NULL, + (uint32_t) hfxoRetimingConfigEntries, + (uint32_t) NULL, + 0UL, + 0UL, + 1440010UL, + (uint32_t) NULL, + (uint32_t) NULL, + (uint32_t) NULL, +}; + +const uint32_t WaveBird_modemConfigBase[] = { + 0x0002400CUL, 0x00000000UL, + /* 4010 */ 0x00004000UL, + 0x00024020UL, 0x00000012UL, + /* 4024 */ 0x00000000UL, + 0x00074030UL, 0x00000000UL, + /* 4034 */ 0x00000000UL, + /* 4038 */ 0x00000000UL, + /* 403C */ 0x00000000UL, + /* 4040 */ 0x00000000UL, + /* 4044 */ 0x00004000UL, + /* 4048 */ 0x00040704UL, + 0x00014050UL, 0x00000000UL, + 0x0002405CUL, 0x00000000UL, + /* 4060 */ 0x00000000UL, + 0x000140A8UL, 0x00000000UL, + 0x000440BCUL, 0x00000000UL, + /* 40C0 */ 0x00000000UL, + /* 40C4 */ 0x00000000UL, + /* 40C8 */ 0x00000000UL, + 0x00044104UL, 0x000040FFUL, + /* 4108 */ 0x00000000UL, + /* 410C */ 0x000041FFUL, + /* 4110 */ 0x00000000UL, + 0x1001C020UL, 0x0007F800UL, + 0x3001C020UL, 0x000802F5UL, + 0x1001C024UL, 0x000000FFUL, + 0x3001C024UL, 0x00001300UL, + 0x0007C028UL, 0x83B380ECUL, + /* C02C */ 0x51407543UL, + /* C030 */ 0x48000FA0UL, + /* C034 */ 0x00004030UL, + /* C038 */ 0x00000000UL, + /* C03C */ 0x00000000UL, + /* C040 */ 0x0000022EUL, + 0x0004C050UL, 0x04301151UL, + /* C054 */ 0xE6092D0EUL, + /* C058 */ 0x08070654UL, + /* C05C */ 0x0002B6D1UL, + 0x000AC064UL, 0x1C003004UL, + /* C068 */ 0x09183040UL, + /* C06C */ 0x2079640DUL, + /* C070 */ 0x01FBFCEBUL, + /* C074 */ 0x03E8F67FUL, + /* C078 */ 0x15724BBDUL, + /* C07C */ 0x0518A311UL, + /* C080 */ 0x76543210UL, + /* C084 */ 0x00000A98UL, + /* C088 */ 0x00000000UL, + 0x01010008UL, 0x00000700UL, + 0x01010018UL, 0x00000000UL, + 0x01010020UL, 0x00000000UL, + 0x0108401CUL, 0x00000010UL, + /* 4020 */ 0x00087020UL, + /* 4024 */ 0x0001000BUL, + /* 4028 */ 0x00002000UL, + /* 402C */ 0x000A0000UL, + /* 4030 */ 0x03000000UL, + /* 4034 */ 0x00000000UL, + /* 4038 */ 0x00000000UL, + 0x01064058UL, 0x00FF0352UL, + /* 405C */ 0x00000840UL, + /* 4060 */ 0x00000008UL, + /* 4064 */ 0x00320411UL, + /* 4068 */ 0x000002C4UL, + /* 406C */ 0x00000000UL, + 0x01114080UL, 0x11E00106UL, + /* 4084 */ 0x0000164FUL, + /* 4088 */ 0x002B03D3UL, + /* 408C */ 0x00000000UL, + /* 4090 */ 0x00000000UL, + /* 4094 */ 0x00000000UL, + /* 4098 */ 0x00000000UL, + /* 409C */ 0x00000000UL, + /* 40A0 */ 0x00000000UL, + /* 40A4 */ 0x00000000UL, + /* 40A8 */ 0x00000000UL, + /* 40AC */ 0x00000000UL, + /* 40B0 */ 0x00000000UL, + /* 40B4 */ 0x00000000UL, + /* 40B8 */ 0x00000000UL, + /* 40BC */ 0x00000000UL, + /* 40C0 */ 0x00000000UL, + 0x010240E0UL, 0x00000033UL, + /* 40E4 */ 0x00000000UL, + 0x010140ECUL, 0x8C74987DUL, + 0x010540F4UL, 0x07830464UL, + /* 40F8 */ 0x3AC81388UL, + /* 40FC */ 0x000A209CUL, + /* 4100 */ 0x00206100UL, + /* 4104 */ 0x123556B7UL, + 0x0103410CUL, 0x001164F0UL, + /* 4110 */ 0x29043020UL, + /* 4114 */ 0x4040BB88UL, + 0x01024124UL, 0x00000000UL, + /* 4128 */ 0x00000000UL, + 0x010A4130UL, 0x0C660664UL, + /* 4134 */ 0x0000010CUL, + /* 4138 */ 0x00FA53E8UL, + /* 413C */ 0x00000000UL, + /* 4140 */ 0x00000000UL, + /* 4144 */ 0x00000000UL, + /* 4148 */ 0x00000000UL, + /* 414C */ 0x00000000UL, + /* 4150 */ 0x00000000UL, + /* 4154 */ 0x00000101UL, + 0x01034168UL, 0x07830464UL, + /* 416C */ 0x00821388UL, + /* 4170 */ 0x00000000UL, + 0x01044230UL, 0x00000000UL, + /* 4234 */ 0x0E000000UL, + /* 4238 */ 0x00000000UL, + /* 423C */ 0x00000000UL, + 0x01024244UL, 0x00000000UL, + /* 4248 */ 0x001F81F4UL, + 0x010C4254UL, 0x00000000UL, + /* 4258 */ 0x003C0000UL, + /* 425C */ 0x00000000UL, + /* 4260 */ 0x00000000UL, + /* 4264 */ 0x55555555UL, + /* 4268 */ 0x00000017UL, + /* 426C */ 0x00000000UL, + /* 4270 */ 0x00000000UL, + /* 4274 */ 0x0006AAAAUL, + /* 4278 */ 0x00000000UL, + /* 427C */ 0x00000000UL, + /* 4280 */ 0x00000000UL, + 0x01018010UL, 0x00000003UL, + 0x01028038UL, 0x00103A3DUL, + /* 803C */ 0x00000001UL, + 0x0103809CUL, 0x00000000UL, + /* 80A0 */ 0x0003B870UL, + /* 80A4 */ 0x0002C0FFUL, + 0x110180A8UL, 0x000001F0UL, + 0x310180A8UL, 0x01014205UL, + 0x110180ACUL, 0x000001F0UL, + 0x310180ACUL, 0x000D0A05UL, + 0x010280B0UL, 0x02000300UL, + /* 80B4 */ 0x01000037UL, + 0x02020098UL, 0x04000C00UL, + /* 009C */ 0x0000004CUL, + 0x020100A4UL, 0x00000400UL, + 0x020200D0UL, 0xAA400005UL, + /* 00D4 */ 0x00000188UL, + 0x020100E4UL, 0x11512C6CUL, + 0x020200F4UL, 0x00000000UL, + /* 00F8 */ 0x1108213DUL, + 0x120100FCUL, 0x0000003FUL, + 0x320100FCUL, 0x00045400UL, + 0x02010130UL, 0x02510060UL, + 0x02010154UL, 0x00003FC4UL, + 0x02010168UL, 0x00000400UL, + 0x03014FFCUL, (uint32_t) &phyInfo, + 0xFFFFFFFFUL, +}; + +const RAIL_ChannelConfigEntry_t WaveBird_channels[] = { + { + .phyConfigDeltaAdd = NULL, + .baseFrequency = 2404800000, + .channelSpacing = 2400000, + .physicalChannelOffset = 0, + .channelNumberStart = 0, + .channelNumberEnd = 31, + .maxPower = RAIL_TX_POWER_MAX, + .attr = &channelConfigEntryAttr, +#ifdef RADIO_CONFIG_ENABLE_CONC_PHY + .entryType = 0, +#endif +#ifdef RADIO_CONFIG_ENABLE_STACK_INFO + .stackInfo = NULL, +#endif + .alternatePhy = NULL, + }, +}; + +const RAIL_ChannelConfig_t WaveBird_channelConfig = { + .phyConfigBase = WaveBird_modemConfigBase, + .phyConfigDeltaSubtract = NULL, + .configs = WaveBird_channels, + .length = 1U, + .signature = 0UL, + .xtalFrequencyHz = 38400000UL, +}; + +const RAIL_ChannelConfig_t *channelConfigs[] = { + &WaveBird_channelConfig, + NULL +}; + +uint32_t wavebirdAccelerationBuffer[305]; \ No newline at end of file diff --git a/firmware/libwavebird/src/platform/efr32/efr32xg22/rail_config.h b/firmware/libwavebird/src/platform/efr32/efr32xg22/rail_config.h new file mode 100644 index 0000000..e2151e3 --- /dev/null +++ b/firmware/libwavebird/src/platform/efr32/efr32xg22/rail_config.h @@ -0,0 +1,49 @@ +/***************************************************************************//** + * @brief RAIL Configuration + * @details + * WARNING: Auto-Generated Radio Config Header - DO NOT EDIT + * Radio Configurator Version: 2304.5.2 + * RAIL Adapter Version: 2.4.33 + * RAIL Compatibility: 2.x + ******************************************************************************* + * # License + * Copyright 2019 Silicon Laboratories Inc. www.silabs.com + ******************************************************************************* + * + * SPDX-License-Identifier: Zlib + * + * The licensor of this software is Silicon Laboratories Inc. + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + ******************************************************************************/ + +#ifndef __RAIL_CONFIG_H__ +#define __RAIL_CONFIG_H__ + +#include +#include "rail_types.h" + +#define WAVEBIRD_ACCELERATION_BUFFER wavebirdAccelerationBuffer +extern uint32_t wavebirdAccelerationBuffer[]; + +#define RADIO_CONFIG_XTAL_FREQUENCY 38400000UL + +#define RAIL0_CHANNELS_PROFILE_BASE +extern const RAIL_ChannelConfig_t *channelConfigs[]; + +#endif // __RAIL_CONFIG_H__ \ No newline at end of file diff --git a/firmware/libwavebird/test/CMakeLists.txt b/firmware/libwavebird/test/CMakeLists.txt index 0149732..b66af50 100644 --- a/firmware/libwavebird/test/CMakeLists.txt +++ b/firmware/libwavebird/test/CMakeLists.txt @@ -5,8 +5,18 @@ if(NOT unity_FOUND) FetchContent_MakeAvailable(Unity) endif() -# Define the test and set the sources -add_executable(test_wavebird "test_main.c" "test_bch3121.c" "test_packet.c") +# Helper function to create a test +function(add_libwavebird_test TEST_NAME) + # Create the test executable + add_executable(${TEST_NAME} ${ARGN}) -# Link dependencies -target_link_libraries(test_wavebird wavebird unity::framework) \ No newline at end of file + # Link against the wavebird library and Unity + target_link_libraries(${TEST_NAME} wavebird unity::framework) + + # Add the test to CTest + add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME}) +endfunction() + +# GameCube controller target tests +add_libwavebird_test(test_bch3121 test_bch3121.c) +add_libwavebird_test(test_packet test_packet.c) diff --git a/firmware/libwavebird/test/test_bch3121.c b/firmware/libwavebird/test/test_bch3121.c index 9495ce1..c6eba6f 100644 --- a/firmware/libwavebird/test/test_bch3121.c +++ b/firmware/libwavebird/test/test_bch3121.c @@ -5,6 +5,14 @@ static const uint32_t valid_codeword = 0x0394a9d0; static const uint32_t valid_message = 0x00015620; +void setUp(void) +{ +} + +void tearDown(void) +{ +} + // Test bch3121_decode decodes a valid codeword static void test_decode() { @@ -85,9 +93,9 @@ static void test_encode_decode() TEST_ASSERT_EQUAL_HEX32(0x12345, message); } -void test_bch3121(void) +int main(int argc, char **argv) { - Unity.TestFile = __FILE_NAME__; + UNITY_BEGIN(); RUN_TEST(test_decode); RUN_TEST(test_decode_failure); @@ -96,4 +104,6 @@ void test_bch3121(void) RUN_TEST(test_decode_correct_triple_error); RUN_TEST(test_encode); RUN_TEST(test_encode_decode); + + return UNITY_END(); } \ No newline at end of file diff --git a/firmware/libwavebird/test/test_main.c b/firmware/libwavebird/test/test_main.c deleted file mode 100644 index 8a996ab..0000000 --- a/firmware/libwavebird/test/test_main.c +++ /dev/null @@ -1,28 +0,0 @@ -#include "unity.h" - -extern void test_bch3121(); -extern void test_packet(); - -__attribute__((weak)) void suiteSetUp(void) -{ -} - -void setUp(void) -{ -} - -void tearDown(void) -{ -} - -int main(int argc, char **argv) -{ - UNITY_BEGIN(); - - suiteSetUp(); - - test_bch3121(); - test_packet(); - - return UNITY_END(); -} diff --git a/firmware/libwavebird/test/test_packet.c b/firmware/libwavebird/test/test_packet.c index de5c4a5..e3670fe 100644 --- a/firmware/libwavebird/test/test_packet.c +++ b/firmware/libwavebird/test/test_packet.c @@ -7,6 +7,14 @@ #include "fixtures.h" +void setUp(void) +{ +} + +void tearDown(void) +{ +} + static void test_deinterleave() { uint32_t codewords[4] = {0}; @@ -224,9 +232,9 @@ static void test_encode_decode() TEST_ASSERT_EQUAL_HEX8_ARRAY(message_input_state_resting, message, WAVEBIRD_MESSAGE_BYTES); } -void test_packet(void) +int main(int argc, char **argv) { - Unity.TestFile = __FILE_NAME__; + UNITY_BEGIN(); RUN_TEST(test_deinterleave); RUN_TEST(test_interleave); @@ -240,4 +248,6 @@ void test_packet(void) RUN_TEST(test_decode_failure); RUN_TEST(test_decode_crc_mismatch); RUN_TEST(test_encode_decode); + + return UNITY_END(); } diff --git a/firmware/receiver/CMakeLists.txt b/firmware/receiver/CMakeLists.txt deleted file mode 100644 index 9486c7e..0000000 --- a/firmware/receiver/CMakeLists.txt +++ /dev/null @@ -1,50 +0,0 @@ -# CMake target for the WavePhoenix receiver application -set(TARGET receiver) - -# Download and make the GeckoSDK CMake targets available -if(NOT GeckoSDK_FOUND) - include(FetchContent) - FetchContent_Declare( - GeckoSDK - GIT_REPOSITORY https://github.com/loopj/gecko-sdk-cmake.git - GIT_TAG main - ) - FetchContent_MakeAvailable(GeckoSDK) -endif() - -# Create the target and set the sources -add_executable( - ${TARGET} - "src/app_properties.c" - "src/button.c" - "src/channel_wheel.c" - "src/led.c" - "src/main.c" - "src/serial.c" - "src/settings.c" -) - -# Set dependencies -target_link_libraries( - ${TARGET} - PUBLIC GeckoSDK::bootloader::interface GeckoSDK::emlib GeckoSDK::emdrv::gpioint si wavebird -) - -# Set the linker script -target_link_options(${TARGET} PRIVATE "-T${LINKER_SCRIPT}") - -# Include the board configuration -target_include_directories(${TARGET} PRIVATE "config/${BOARD}") - -# Enable debug logging if DEBUG is set -if(DEBUG) - target_compile_definitions(${TARGET} PUBLIC DEBUG) -endif() - -# Generate firmware files after building the target -gecko_sdk_generate_hex(${TARGET}) -gecko_sdk_generate_gbl(${TARGET}) - -# Create OpenOCD flash target -add_openocd_flash_target(${TARGET}) -add_ota_flash_target(${TARGET}) diff --git a/firmware/receiver/boards/efr32xg22e.slcc b/firmware/receiver/boards/efr32xg22e.slcc new file mode 100644 index 0000000..891b78c --- /dev/null +++ b/firmware/receiver/boards/efr32xg22e.slcc @@ -0,0 +1,32 @@ +id: efr32xg22e +label: efr32xg22e +package: "ext-comp" +description: WavePhoenix board support for EFR32xG22E +category: External +quality: production + +requires: + - name: brd2710a + - name: iostream_recommended_stream + - name: iostream_retarget_stdio + - name: printf + +define: + - name: SI_DATA_PORT + value: gpioPortD + - name: SI_DATA_PIN + value: 3 + - name: HAS_PAIR_BTN + value: 1 + - name: PAIR_BTN_PORT + value: gpioPortC + - name: PAIR_BTN_PIN + value: 7 + - name: HAS_STATUS_LED + value: 1 + - name: STATUS_LED_PORT + value: gpioPortA + - name: STATUS_LED_PIN + value: 4 + - name: STATUS_LED_INVERT + value: 0 diff --git a/firmware/receiver/boards/rf-bm-bg22c3.slcc b/firmware/receiver/boards/rf-bm-bg22c3.slcc new file mode 100644 index 0000000..63a73ab --- /dev/null +++ b/firmware/receiver/boards/rf-bm-bg22c3.slcc @@ -0,0 +1,31 @@ +id: rf-bm-bg22c3 +label: rf-bm-bg22c3 +package: "ext-comp" +description: WavePhoenix board support for RF-BM-BG22C3 +category: External +quality: production + +requires: + - name: efr32bg22c224f512gm32 + +define: + - name: SI_DATA_PORT + value: gpioPortA + - name: SI_DATA_PIN + value: 2 + - name: HAS_PAIR_BTN + value: 1 + - name: PAIR_BTN_PORT + value: gpioPortB + - name: PAIR_BTN_PIN + value: 0 + - name: HAS_STATUS_LED + value: 1 + - name: STATUS_LED_PORT + value: gpioPortC + - name: STATUS_LED_PIN + value: 1 + - name: STATUS_LED_INVERT + value: 0 + - name: SL_CLOCK_MANAGER_HFXO_CTUNE + value: 72 diff --git a/firmware/receiver/config/efr32xg22e/board_config.h b/firmware/receiver/config/efr32xg22e/board_config.h deleted file mode 100644 index a61fa4b..0000000 --- a/firmware/receiver/config/efr32xg22e/board_config.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -// Board config -#define HFXO_FREQ 38400000 -#define HFXO_CTUNE 121 - -// GPIO config -#define SI_DATA_PORT gpioPortD -#define SI_DATA_PIN 3 - -// Serial debug config -#define SERIAL_USART USART1 -#define SERIAL_USART_CLK cmuClock_USART1 -#define SERIAL_USART_IDX 1 -#define SERIAL_RXPORT gpioPortA -#define SERIAL_RXPIN 6 -#define SERIAL_TXPORT gpioPortA -#define SERIAL_TXPIN 5 - -// Pair button -#define HAS_PAIR_BTN 1 -#define PAIR_BTN_PORT gpioPortC -#define PAIR_BTN_PIN 7 - -// Status LED -#define HAS_STATUS_LED 1 -#define STATUS_LED_PORT gpioPortA -#define STATUS_LED_PIN 4 -#define STATUS_LED_INVERT 0 \ No newline at end of file diff --git a/firmware/receiver/config/rf-bm-bg22c3/board_config.h b/firmware/receiver/config/rf-bm-bg22c3/board_config.h deleted file mode 100644 index 898bebe..0000000 --- a/firmware/receiver/config/rf-bm-bg22c3/board_config.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -// Board config -#define HFXO_FREQ 38400000 -#define HFXO_CTUNE 72 - -// GPIO config -#define SI_DATA_PORT gpioPortA -#define SI_DATA_PIN 2 - -// Serial debug config -#define SERIAL_USART USART1 -#define SERIAL_USART_CLK cmuClock_USART1 -#define SERIAL_USART_IDX 1 -#define SERIAL_RXPORT gpioPortA -#define SERIAL_RXPIN 6 -#define SERIAL_TXPORT gpioPortA -#define SERIAL_TXPIN 5 - -// Pair button -#define HAS_PAIR_BTN 1 -#define PAIR_BTN_PORT gpioPortB -#define PAIR_BTN_PIN 0 - -// Status LED -#define HAS_STATUS_LED 1 -#define STATUS_LED_PORT gpioPortC -#define STATUS_LED_PIN 1 -#define STATUS_LED_INVERT 0 \ No newline at end of file diff --git a/firmware/receiver/src/main.c b/firmware/receiver/src/main.c index 9959bf5..b3b2e45 100644 --- a/firmware/receiver/src/main.c +++ b/firmware/receiver/src/main.c @@ -1,9 +1,10 @@ +#include #include #include "btl_interface.h" -#include "em_chip.h" #include "em_cmu.h" #include "em_gpio.h" +#include "sl_main_init.h" #include "si/commands.h" #include "si/device/gc_controller.h" @@ -14,12 +15,9 @@ #include "button.h" #include "channel_wheel.h" #include "led.h" -#include "serial.h" #include "settings.h" #include "version.h" -#include "board_config.h" - #define INPUT_VALID_MS 100 // Controller types @@ -127,9 +125,7 @@ static void handle_pair_button_press() // Reboot into the bootloader when the pair button is held static void handle_pair_button_hold() { - DEBUG_PRINT("Rebooting into bootloader...\n\n"); - DEBUG_FLUSH(); - + printf("Rebooting into bootloader...\n\n"); bootloader_rebootAndInstall(); } #endif @@ -152,7 +148,7 @@ static void handle_wavebird_packet(const uint8_t *packet) uint8_t message[WAVEBIRD_MESSAGE_BYTES]; int rc = wavebird_packet_decode(message, packet); if (rc < 0) { - // DEBUG_PRINT("Failed to decode WaveBird packet: %d\n", rcode); + // printf("Failed to decode WaveBird packet: %d\n", rc); packet_stats.decode_errors++; return; } @@ -243,7 +239,7 @@ static void handle_wavebird_error(int error) // Handle pairing start events static void handle_pairing_started(void) { - DEBUG_PRINT("Pairing started\n"); + printf("Pairing started\n"); // Set the pairing active flag pairing_active = true; @@ -264,7 +260,7 @@ static void handle_pairing_finished(uint8_t status, uint8_t channel) // Store the new channel in NVM if pairing was successful if (status == WB_RADIO_PAIRING_SUCCESS) { - DEBUG_PRINT("Pairing successful, new channel: %d\n", channel + 1); + printf("Pairing successful, new channel: %d\n", channel + 1); // Set the new channel and save to NVM settings.chan = channel; @@ -277,7 +273,7 @@ static void handle_pairing_finished(uint8_t status, uint8_t channel) // Reset the controller initialize_controller(settings.cont_type); } else if (status == WB_RADIO_PAIRING_TIMEOUT) { - DEBUG_PRINT("Pairing timed out\n"); + printf("Pairing timed out\n"); // Slow-blink the status LED to indicate pairing timeout if (status_led) @@ -286,7 +282,7 @@ static void handle_pairing_finished(uint8_t status, uint8_t channel) // Immediately reenable SI command handling enable_si_command_handling = true; } else { - DEBUG_PRINT("Pairing cancelled\n"); + printf("Pairing cancelled\n"); // Turn off the status LED if (status_led) @@ -311,30 +307,6 @@ static bool qualify_packet(const uint8_t *packet) return (buttons & settings.pair_btns) == settings.pair_btns; } -void system_init(void) -{ - // Chip errata - CHIP_Init(); - - // HFXO initialization - CMU_HFXOInit_TypeDef hfxoInit = CMU_HFXOINIT_DEFAULT; - hfxoInit.ctuneXoAna = HFXO_CTUNE; - hfxoInit.ctuneXiAna = HFXO_CTUNE; - CMU_HFXOInit(&hfxoInit); - SystemHFXOClockSet(HFXO_FREQ); - - // PLL initialization - CMU_DPLLInit_TypeDef dpllInit = CMU_DPLL_HFXO_TO_76_8MHZ; - bool dpllLock = false; - while (!dpllLock) - dpllLock = CMU_DPLLLock(&dpllInit); - CMU_ClockSelectSet(cmuClock_SYSCLK, cmuSelect_HFRCODPLL); - - // Set default NVIC priorities - for (IRQn_Type i = SVCall_IRQn; i < EXT_IRQ_COUNT; i++) - NVIC_SetPriority(i, CORE_INTERRUPT_DEFAULT_PRIORITY); -} - // Initialize the various GPIOs static void gpio_init(void) { @@ -343,7 +315,7 @@ static void gpio_init(void) // Make SWDIO available as a GPIO, if necessary if (SI_DATA_PORT == GPIO_SWDIO_PORT && SI_DATA_PIN == GPIO_SWDIO_PIN) { - DEBUG_PRINT("[WARNING] SI is using SWDIO as GPIO, disabling SWD\n"); + printf("[WARNING] SI is using SWDIO as GPIO, disabling SWD\n"); GPIO_DbgSWDIOEnable(false); } @@ -376,11 +348,8 @@ static void gpio_init(void) int main(void) { - // Initialize the system - system_init(); - - // Initialize the debug console - serial_init(115200); + // Initialize the device + sl_main_init(); // Initialize the GPIOs gpio_init(); @@ -413,13 +382,13 @@ int main(void) initialize_controller(settings.cont_type); // Lets-a-go! - DEBUG_PRINT("WavePhoenix receiver ready!\n"); - DEBUG_PRINT("- Firmware version: %d.%d.%d\n", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH); - DEBUG_PRINT("- Radio channel: %u\n", settings.chan + 1); - DEBUG_PRINT("- Controller type: %s\n", (settings.cont_type == WP_CONT_TYPE_GC_WAVEBIRD) ? "WaveBird" - : (settings.cont_type == WP_CONT_TYPE_GC_WIRED) ? "Wired" - : "Wired (no motor)"); - DEBUG_PRINT("\n"); + printf("WavePhoenix receiver ready!\n"); + printf("- Firmware version: %d.%d.%d\n", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH); + printf("- Radio channel: %u\n", settings.chan + 1); + printf("- Controller type: %s\n", (settings.cont_type == WP_CONT_TYPE_GC_WAVEBIRD) ? "WaveBird" + : (settings.cont_type == WP_CONT_TYPE_GC_WIRED) ? "Wired" + : "Wired (no motor)"); + printf("\n"); // Wait for the SI bus to be idle before starting the main loop si_await_bus_idle(); diff --git a/firmware/receiver/src/serial.c b/firmware/receiver/src/serial.c deleted file mode 100644 index 5061a89..0000000 --- a/firmware/receiver/src/serial.c +++ /dev/null @@ -1,93 +0,0 @@ -#include - -#include "em_cmu.h" -#include "em_gpio.h" - -#include "board_config.h" - -#include "serial.h" - -#if defined(SERIAL_USART) -#include "em_usart.h" -#elif defined(SERIAL_EUSART) -#include "em_eusart.h" -#endif - -// Newlib _write implementation for printf -int _write(int fd, char *ptr, int len) -{ - for (int i = 0; i < len; i++) { - serial_putc(ptr[i]); - } - - return len; -} - -// Initialize the UART peripheral for RX and TX -void serial_init(uint32_t baudrate) -{ - // Enable clocks - CMU_ClockEnable(cmuClock_GPIO, true); - - // Configure GPIO pins - GPIO_PinModeSet(SERIAL_RXPORT, SERIAL_RXPIN, gpioModeInputPull, 1); - GPIO_PinModeSet(SERIAL_TXPORT, SERIAL_TXPIN, gpioModePushPull, 1); - -#if defined(SERIAL_USART) - // Enable clocks - CMU_ClockEnable(SERIAL_USART_CLK, true); - - // Configure USART peripheral, defaults to 115200 bits/s, 8N1 - USART_InitAsync_TypeDef usart_init = USART_INITASYNC_DEFAULT; - usart_init.enable = usartDisable; - usart_init.baudrate = baudrate; - USART_InitAsync(SERIAL_USART, &usart_init); - - // Route USART signals to the correct GPIO pins - GPIO->USARTROUTE[SERIAL_USART_IDX].ROUTEEN = GPIO_USART_ROUTEEN_RXPEN | GPIO_USART_ROUTEEN_TXPEN; - GPIO->USARTROUTE[SERIAL_USART_IDX].RXROUTE = - (SERIAL_RXPORT << _GPIO_USART_RXROUTE_PORT_SHIFT) | (SERIAL_RXPIN << _GPIO_USART_RXROUTE_PIN_SHIFT); - GPIO->USARTROUTE[SERIAL_USART_IDX].TXROUTE = - (SERIAL_TXPORT << _GPIO_USART_TXROUTE_PORT_SHIFT) | (SERIAL_TXPIN << _GPIO_USART_TXROUTE_PIN_SHIFT); - - // Enable EUSART - USART_Enable(SERIAL_USART, usartEnable); -#elif defined(SERIAL_EUSART) - // Enable clocks - CMU_ClockEnable(SERIAL_EUSART_CLK, true); - - // Configure EUSART peripheral, defaults to 115200 bits/s, 8N1 - EUSART_UartInit_TypeDef eusart_init = EUSART_UART_INIT_DEFAULT_HF; - eusart_init.enable = eusartDisable; - eusart_init.baudrate = baudrate; - EUSART_UartInitHf(SERIAL_EUSART, &eusart_init); - - // Route USART signals to the correct GPIO pins - GPIO->EUSARTROUTE[SERIAL_EUSART_IDX].ROUTEEN = GPIO_EUSART_ROUTEEN_RXPEN | GPIO_EUSART_ROUTEEN_TXPEN; - GPIO->EUSARTROUTE[SERIAL_EUSART_IDX].RXROUTE = - (SERIAL_RXPORT << _GPIO_EUSART_RXROUTE_PORT_SHIFT) | (SERIAL_RXPIN << _GPIO_EUSART_RXROUTE_PIN_SHIFT); - GPIO->EUSARTROUTE[SERIAL_EUSART_IDX].TXROUTE = - (SERIAL_TXPORT << _GPIO_EUSART_TXROUTE_PORT_SHIFT) | (SERIAL_TXPIN << _GPIO_EUSART_TXROUTE_PIN_SHIFT); - - // Enable EUSART - EUSART_Enable(SERIAL_EUSART, eusartEnable); -#endif -} - -char serial_getc() -{ -#if defined(SERIAL_USART) - return USART_Rx(SERIAL_USART); -#elif defined(SERIAL_EUSART) - return EUSART_Rx(SERIAL_EUSART); -#endif -} - -void serial_putc(char c) -{ -#if defined(SERIAL_USART) - USART_Tx(SERIAL_USART, c); -#elif defined(SERIAL_EUSART) - EUSART_Tx(SERIAL_EUSART, c); -#endif -} \ No newline at end of file diff --git a/firmware/receiver/src/serial.h b/firmware/receiver/src/serial.h deleted file mode 100644 index 6fadd79..0000000 --- a/firmware/receiver/src/serial.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -void serial_init(uint32_t baudrate); -char serial_getc(); -void serial_putc(char c); - -#if defined(DEBUG) -#include -#define DEBUG_PRINT(msg, ...) printf(msg, ##__VA_ARGS__) -#define DEBUG_FLUSH() -#else -#define DEBUG_PRINT(msg, ...) -#endif diff --git a/firmware/receiver/src/version.h b/firmware/receiver/src/version.h index e501f7a..f0080e4 100644 --- a/firmware/receiver/src/version.h +++ b/firmware/receiver/src/version.h @@ -1,11 +1,5 @@ #pragma once -#include - -// App version components -static const uint8_t VERSION_MAJOR = 0; -static const uint8_t VERSION_MINOR = 9; -static const uint8_t VERSION_PATCH = 3; - -// Full version number -static const uint32_t VERSION_FULL = (VERSION_MAJOR << 24) | (VERSION_MINOR << 16) | (VERSION_PATCH << 8); \ No newline at end of file +#define VERSION_MAJOR 0 +#define VERSION_MINOR 9 +#define VERSION_PATCH 3 \ No newline at end of file diff --git a/firmware/receiver/wavephoenix.slce b/firmware/receiver/wavephoenix.slce new file mode 100644 index 0000000..fa77d13 --- /dev/null +++ b/firmware/receiver/wavephoenix.slce @@ -0,0 +1,10 @@ +id: wavephoenix +label: wavephoenix +version: 1.0.0 + +sdk: + id: simplicity_sdk + version: 2025.6.2 + +component_path: + - path: boards \ No newline at end of file diff --git a/firmware/receiver/wavephoenix.slcp b/firmware/receiver/wavephoenix.slcp new file mode 100644 index 0000000..084424e --- /dev/null +++ b/firmware/receiver/wavephoenix.slcp @@ -0,0 +1,32 @@ +project_name: wavephoenix +description: WavePhoenix +quality: production + +sdk: + id: simplicity_sdk + version: 2025.6.2 + +component: + - id: clock_manager + - id: device_init + - id: sl_main + - id: sl_main_custom_main + - id: apploader + - id: bootloader_interface + - id: gpiointerrupt + - id: emlib + - id: libsi + from: libsi + - id: libwavebird + from: libwavebird + +source: + - path: src/button.c + - path: src/channel_wheel.c + - path: src/led.c + - path: src/main.c + - path: src/settings.c + +configuration: + - name: SL_BOARD_ENABLE_VCOM + value: 1 From 6019bf7802e23e6595d545c13d06b2724efb8417 Mon Sep 17 00:00:00 2001 From: James Smith Date: Thu, 8 Jan 2026 14:36:47 -0800 Subject: [PATCH 02/15] Rail config generation script, add other configs --- firmware/libwavebird/.gitignore | 1 + firmware/libwavebird/README.md | 8 +- ..._efr32xg1.radioconf => efr32xg1.radioconf} | 0 ...fr32xg14.radioconf => efr32xg14.radioconf} | 0 ...fr32xg22.radioconf => efr32xg22.radioconf} | 0 firmware/libwavebird/libwavebird.slcc | 8 + .../scripts/generate_rail_configs.sh | 33 +++ .../src/platform/efr32/efr32xg1/rail_config.c | 203 +++++++++++++++ .../src/platform/efr32/efr32xg1/rail_config.h | 49 ++++ .../platform/efr32/efr32xg14/rail_config.c | 240 ++++++++++++++++++ .../platform/efr32/efr32xg14/rail_config.h | 49 ++++ .../platform/efr32/efr32xg22/rail_config.c | 2 +- .../platform/efr32/efr32xg22/rail_config.h | 2 +- 13 files changed, 592 insertions(+), 3 deletions(-) create mode 100644 firmware/libwavebird/.gitignore rename firmware/libwavebird/config/rail/{radio_settings_efr32xg1.radioconf => efr32xg1.radioconf} (100%) rename firmware/libwavebird/config/rail/{radio_settings_efr32xg14.radioconf => efr32xg14.radioconf} (100%) rename firmware/libwavebird/config/rail/{radio_settings_efr32xg22.radioconf => efr32xg22.radioconf} (100%) create mode 100755 firmware/libwavebird/scripts/generate_rail_configs.sh create mode 100644 firmware/libwavebird/src/platform/efr32/efr32xg1/rail_config.c create mode 100644 firmware/libwavebird/src/platform/efr32/efr32xg1/rail_config.h create mode 100644 firmware/libwavebird/src/platform/efr32/efr32xg14/rail_config.c create mode 100644 firmware/libwavebird/src/platform/efr32/efr32xg14/rail_config.h diff --git a/firmware/libwavebird/.gitignore b/firmware/libwavebird/.gitignore new file mode 100644 index 0000000..fbd9f44 --- /dev/null +++ b/firmware/libwavebird/.gitignore @@ -0,0 +1 @@ +radioconf_generation_log.json \ No newline at end of file diff --git a/firmware/libwavebird/README.md b/firmware/libwavebird/README.md index 4719465..03faf33 100644 --- a/firmware/libwavebird/README.md +++ b/firmware/libwavebird/README.md @@ -1,6 +1,6 @@ # libwavebird -An open source implementation of the WaveBird protocol. +An open source implementation of the WaveBird protocol for Silicon Labs Gecko SoCs. Currently supports Silicon Labs Gecko Series 1 and Series 2 SoCs. Tested with the EFR32FG1, EFR32FG14, EFR32MG22, and EFR32BG22 SoCs. @@ -8,6 +8,12 @@ Currently supports Silicon Labs Gecko Series 1 and Series 2 SoCs. Tested with th In theory this library should work with any Gecko Series 1 or Series 2 SoC that has proprietary 2.4GHz support. A `.radioconf` file for that platform will need to be created in `config/rail`. +## Generating `rail_config` files + +The `rail_config.c` and `rail_config.h` files are generated from `.radioconf` files using the Silicon Labs multiphy configurator script. + +To regenerate the files, run `scripts/generate_rail_config.py`. + ## Adding support for additional platforms The BCH(31,21) logic and packet decoding logic is platform agnostic. diff --git a/firmware/libwavebird/config/rail/radio_settings_efr32xg1.radioconf b/firmware/libwavebird/config/rail/efr32xg1.radioconf similarity index 100% rename from firmware/libwavebird/config/rail/radio_settings_efr32xg1.radioconf rename to firmware/libwavebird/config/rail/efr32xg1.radioconf diff --git a/firmware/libwavebird/config/rail/radio_settings_efr32xg14.radioconf b/firmware/libwavebird/config/rail/efr32xg14.radioconf similarity index 100% rename from firmware/libwavebird/config/rail/radio_settings_efr32xg14.radioconf rename to firmware/libwavebird/config/rail/efr32xg14.radioconf diff --git a/firmware/libwavebird/config/rail/radio_settings_efr32xg22.radioconf b/firmware/libwavebird/config/rail/efr32xg22.radioconf similarity index 100% rename from firmware/libwavebird/config/rail/radio_settings_efr32xg22.radioconf rename to firmware/libwavebird/config/rail/efr32xg22.radioconf diff --git a/firmware/libwavebird/libwavebird.slcc b/firmware/libwavebird/libwavebird.slcc index 3520537..daca952 100644 --- a/firmware/libwavebird/libwavebird.slcc +++ b/firmware/libwavebird/libwavebird.slcc @@ -15,6 +15,10 @@ provides: include: - path: include + - path: src/platform/efr32/efr32xg1 + condition: [device_generic_family_efr32xg1] + - path: src/platform/efr32/efr32xg14 + condition: [device_generic_family_efr32xg14] - path: src/platform/efr32/efr32xg22 condition: [device_generic_family_efr32xg22] @@ -22,5 +26,9 @@ source: - path: src/bch3121.c - path: src/packet.c - path: src/platform/efr32/radio_efr32.c + - path: src/platform/efr32/efr32xg1/rail_config.c + condition: [device_generic_family_efr32xg1] + - path: src/platform/efr32/efr32xg14/rail_config.c + condition: [device_generic_family_efr32xg14] - path: src/platform/efr32/efr32xg22/rail_config.c condition: [device_generic_family_efr32xg22] \ No newline at end of file diff --git a/firmware/libwavebird/scripts/generate_rail_configs.sh b/firmware/libwavebird/scripts/generate_rail_configs.sh new file mode 100755 index 0000000..9040fc8 --- /dev/null +++ b/firmware/libwavebird/scripts/generate_rail_configs.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +VENV_DIR=".venv" + +# Check if GECKO_SDK_PATH is set +if [ -z "$GECKO_SDK_PATH" ]; then + echo "Error: GECKO_SDK_PATH environment variable not set" + exit 1 +fi + +# Create the virtual environment if it doesn't exist +if [ ! -d "$VENV_DIR" ]; then + echo "Creating Python 3.10 virtual environment..." + python3.10 -m venv $VENV_DIR +fi + +# Save the path to the configurator script +CONFIGURATOR_SCRIPT="${GECKO_SDK_PATH}/platform/radio/efr32_multiphy_configurator/Efr32RadioConfiguratorUcAdapter.py" + +# Activate the virtual environment +source $VENV_DIR/bin/activate + +# Install required Python packages +pip install jinja2 numpy pyyaml scipy + +# For each .radioconf file in config/rail, generate the corresponding rail config +for radioconf_file in config/rail/*.radioconf; do + base_name=$(basename "$radioconf_file" .radioconf) + output_path="src/platform/efr32/${base_name}" + + python ${CONFIGURATOR_SCRIPT} ${radioconf_file} -o ${output_path} + echo "Generated rail config for ${base_name} at ${output_path}" +done \ No newline at end of file diff --git a/firmware/libwavebird/src/platform/efr32/efr32xg1/rail_config.c b/firmware/libwavebird/src/platform/efr32/efr32xg1/rail_config.c new file mode 100644 index 0000000..171b677 --- /dev/null +++ b/firmware/libwavebird/src/platform/efr32/efr32xg1/rail_config.c @@ -0,0 +1,203 @@ +/***************************************************************************//** + * @brief RAIL Configuration + * @details + * WARNING: Auto-Generated Radio Config - DO NOT EDIT + * Radio Configurator Version: 2304.5.2 + * RAIL Adapter Version: 2.4.33 + * RAIL Compatibility: 2.x + ******************************************************************************* + * # License + * Copyright 2019 Silicon Laboratories Inc. www.silabs.com + ******************************************************************************* + * + * SPDX-License-Identifier: Zlib + * + * The licensor of this software is Silicon Laboratories Inc. + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + ******************************************************************************/ +#include "em_device.h" +#include "rail_config.h" + +uint32_t RAILCb_CalcSymbolRate(RAIL_Handle_t railHandle) +{ + (void) railHandle; + return 0U; +} + +uint32_t RAILCb_CalcBitRate(RAIL_Handle_t railHandle) +{ + (void) railHandle; + return 0U; +} + +void RAILCb_ConfigFrameTypeLength(RAIL_Handle_t railHandle, + const RAIL_FrameType_t *frameType) +{ + (void) railHandle; + (void) frameType; +} + +static const uint8_t irCalConfig[] = { + 25, 63, 1, 6, 4, 16, 1, 0, 0, 1, 1, 6, 0, 16, 39, 0, 0, 5, 0, 1, 1, 0, 0, 0, 0, 0 +}; + +static const int32_t timingConfig[] = { + 0, 0, 0 +}; + +static RAIL_ChannelConfigEntryAttr_t channelConfigEntryAttr = { +#if RAIL_SUPPORTS_OFDM_PA + { + { 0xFFFFFFFFUL }, + { 0xFFFFFFFFUL, 0xFFFFFFFFUL } + } +#else // RAIL_SUPPORTS_OFDM_PA + { 0xFFFFFFFFUL }, +#endif // RAIL_SUPPORTS_OFDM_PA +}; + +static const uint32_t phyInfo[] = { + 16UL, + 0x00666666UL, // 102.4 + (uint32_t) NULL, + (uint32_t) irCalConfig, + (uint32_t) timingConfig, + 0x00000000UL, + 9760000UL, + 38400000UL, + 1440000UL, + 0x00000F01UL, + 0x02004924UL, + (uint32_t) NULL, + (uint32_t) NULL, + (uint32_t) NULL, + 0UL, + 0UL, + 1440000UL, + (uint32_t) NULL, + (uint32_t) NULL, + (uint32_t) NULL, +}; + +const uint32_t WaveBird_modemConfigBase[] = { + 0x01040FF0UL, (uint32_t) &phyInfo, + /* 0FF4 */ 0x00000000UL, + /* 0FF8 */ 0x0003C000UL, + /* 0FFC */ 0x0003C00FUL, + 0x00020004UL, 0x00000000UL, + /* 0008 */ 0x00000000UL, + 0x00020018UL, 0x00000012UL, + /* 001C */ 0x00000000UL, + 0x00070028UL, 0x00000000UL, + /* 002C */ 0x00000000UL, + /* 0030 */ 0x00000000UL, + /* 0034 */ 0x00000000UL, + /* 0038 */ 0x00000000UL, + /* 003C */ 0x00000000UL, + /* 0040 */ 0x00000704UL, + 0x00010048UL, 0x00000000UL, + 0x00020054UL, 0x00000000UL, + /* 0058 */ 0x00000000UL, + 0x000400A0UL, 0x000040FFUL, + /* 00A4 */ 0x00000000UL, + /* 00A8 */ 0x000041FFUL, + /* 00AC */ 0x00000000UL, + 0x00012000UL, 0x00000700UL, + 0x00012010UL, 0x00000000UL, + 0x00012018UL, 0x00000000UL, + 0x00013008UL, 0x0000AC3FUL, + 0x00023030UL, 0x00104924UL, + /* 3034 */ 0x00000001UL, + 0x00013040UL, 0x00000000UL, + 0x000140A0UL, 0x0F00277AUL, + 0x000140F4UL, 0x00001020UL, + 0x00024134UL, 0x00000880UL, + /* 4138 */ 0x000087E6UL, + 0x00024140UL, 0x0088006DUL, + /* 4144 */ 0x1153E6C0UL, + 0x00156014UL, 0x00000010UL, + /* 6018 */ 0x00087020UL, + /* 601C */ 0x0000000BUL, + /* 6020 */ 0x00003000UL, + /* 6024 */ 0x000A0000UL, + /* 6028 */ 0x03000000UL, + /* 602C */ 0x00000000UL, + /* 6030 */ 0x00FF0352UL, + /* 6034 */ 0x00000C61UL, + /* 6038 */ 0x00000001UL, + /* 603C */ 0x00320411UL, + /* 6040 */ 0x000002C4UL, + /* 6044 */ 0x00000000UL, + /* 6048 */ 0x13C0011DUL, + /* 604C */ 0x0000164FUL, + /* 6050 */ 0x002A03D0UL, + /* 6054 */ 0x0094905AUL, + /* 6058 */ 0x00000001UL, + /* 605C */ 0x00000000UL, + /* 6060 */ 0x00000000UL, + /* 6064 */ 0x00000000UL, + 0x10017014UL, 0x0007F800UL, + 0x30017014UL, 0x000000F8UL, + 0x10017018UL, 0x000000FFUL, + 0x30017018UL, 0x00000300UL, + 0x0001701CUL, 0x82730060UL, + 0x00017028UL, 0x01800000UL, + 0x00027048UL, 0x00003D3CUL, + /* 704C */ 0x000019BCUL, + 0x00037070UL, 0x00020105UL, + /* 7074 */ 0x00000433UL, + /* 7078 */ 0x00552300UL, + 0xFFFFFFFFUL, +}; + +const RAIL_ChannelConfigEntry_t WaveBird_channels[] = { + { + .phyConfigDeltaAdd = NULL, + .baseFrequency = 2404800000, + .channelSpacing = 2400000, + .physicalChannelOffset = 0, + .channelNumberStart = 0, + .channelNumberEnd = 31, + .maxPower = RAIL_TX_POWER_MAX, + .attr = &channelConfigEntryAttr, +#ifdef RADIO_CONFIG_ENABLE_CONC_PHY + .entryType = 0, +#endif +#ifdef RADIO_CONFIG_ENABLE_STACK_INFO + .stackInfo = NULL, +#endif + .alternatePhy = NULL, + }, +}; + +const RAIL_ChannelConfig_t WaveBird_channelConfig = { + .phyConfigBase = WaveBird_modemConfigBase, + .phyConfigDeltaSubtract = NULL, + .configs = WaveBird_channels, + .length = 1U, + .signature = 0UL, + .xtalFrequencyHz = 38400000UL, +}; + +const RAIL_ChannelConfig_t *channelConfigs[] = { + &WaveBird_channelConfig, + NULL +}; + +uint32_t wavebirdAccelerationBuffer[135]; diff --git a/firmware/libwavebird/src/platform/efr32/efr32xg1/rail_config.h b/firmware/libwavebird/src/platform/efr32/efr32xg1/rail_config.h new file mode 100644 index 0000000..bb52beb --- /dev/null +++ b/firmware/libwavebird/src/platform/efr32/efr32xg1/rail_config.h @@ -0,0 +1,49 @@ +/***************************************************************************//** + * @brief RAIL Configuration + * @details + * WARNING: Auto-Generated Radio Config Header - DO NOT EDIT + * Radio Configurator Version: 2304.5.2 + * RAIL Adapter Version: 2.4.33 + * RAIL Compatibility: 2.x + ******************************************************************************* + * # License + * Copyright 2019 Silicon Laboratories Inc. www.silabs.com + ******************************************************************************* + * + * SPDX-License-Identifier: Zlib + * + * The licensor of this software is Silicon Laboratories Inc. + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + ******************************************************************************/ + +#ifndef __RAIL_CONFIG_H__ +#define __RAIL_CONFIG_H__ + +#include +#include "rail_types.h" + +#define WAVEBIRD_ACCELERATION_BUFFER wavebirdAccelerationBuffer +extern uint32_t wavebirdAccelerationBuffer[]; + +#define RADIO_CONFIG_XTAL_FREQUENCY 38400000UL + +#define RAIL0_CHANNELS_PROFILE_BASE +extern const RAIL_ChannelConfig_t *channelConfigs[]; + +#endif // __RAIL_CONFIG_H__ diff --git a/firmware/libwavebird/src/platform/efr32/efr32xg14/rail_config.c b/firmware/libwavebird/src/platform/efr32/efr32xg14/rail_config.c new file mode 100644 index 0000000..acbfc1b --- /dev/null +++ b/firmware/libwavebird/src/platform/efr32/efr32xg14/rail_config.c @@ -0,0 +1,240 @@ +/***************************************************************************//** + * @brief RAIL Configuration + * @details + * WARNING: Auto-Generated Radio Config - DO NOT EDIT + * Radio Configurator Version: 2304.5.2 + * RAIL Adapter Version: 2.4.33 + * RAIL Compatibility: 2.x + ******************************************************************************* + * # License + * Copyright 2019 Silicon Laboratories Inc. www.silabs.com + ******************************************************************************* + * + * SPDX-License-Identifier: Zlib + * + * The licensor of this software is Silicon Laboratories Inc. + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + ******************************************************************************/ +#include "em_device.h" +#include "rail_config.h" + +uint32_t RAILCb_CalcSymbolRate(RAIL_Handle_t railHandle) +{ + (void) railHandle; + return 0U; +} + +uint32_t RAILCb_CalcBitRate(RAIL_Handle_t railHandle) +{ + (void) railHandle; + return 0U; +} + +void RAILCb_ConfigFrameTypeLength(RAIL_Handle_t railHandle, + const RAIL_FrameType_t *frameType) +{ + (void) railHandle; + (void) frameType; +} + +static const uint8_t irCalConfig[] = { + 25, 63, 1, 6, 4, 16, 1, 0, 0, 1, 1, 6, 0, 16, 39, 0, 0, 5, 0, 1, 1, 0, 0, 0, 0, 0 +}; + +static const int32_t timingConfig[] = { + 0, 0, 0 +}; + +static RAIL_ChannelConfigEntryAttr_t channelConfigEntryAttr = { +#if RAIL_SUPPORTS_OFDM_PA + { + { 0xFFFFFFFFUL }, + { 0xFFFFFFFFUL, 0xFFFFFFFFUL } + } +#else // RAIL_SUPPORTS_OFDM_PA + { 0xFFFFFFFFUL }, +#endif // RAIL_SUPPORTS_OFDM_PA +}; + +static const uint32_t phyInfo[] = { + 16UL, + 0x0071C71CUL, // 113.77777777777779 + (uint32_t) NULL, + (uint32_t) irCalConfig, + (uint32_t) timingConfig, + 0x00000000UL, + 7320000UL, + 34560000UL, + 1440000UL, + 0x00000F01UL, + 0x02004C44UL, + (uint32_t) NULL, + (uint32_t) NULL, + (uint32_t) NULL, + 0UL, + 0UL, + 1439718UL, + (uint32_t) NULL, + (uint32_t) NULL, + (uint32_t) NULL, +}; + +const uint32_t WaveBird_modemConfigBase[] = { + 0x01041FF0UL, 0x0037003FUL, + /* 1FF4 */ 0x00000000UL, + /* 1FF8 */ (uint32_t) &phyInfo, + /* 1FFC */ 0x00000000UL, + 0x00020004UL, 0x00000000UL, + /* 0008 */ 0x00000000UL, + 0x00020018UL, 0x00000012UL, + /* 001C */ 0x00000000UL, + 0x00070028UL, 0x00000000UL, + /* 002C */ 0x00000000UL, + /* 0030 */ 0x00000000UL, + /* 0034 */ 0x00000000UL, + /* 0038 */ 0x00000000UL, + /* 003C */ 0x00000000UL, + /* 0040 */ 0x00000704UL, + 0x00010048UL, 0x00000000UL, + 0x00020054UL, 0x00000000UL, + /* 0058 */ 0x00000000UL, + 0x000400A0UL, 0x000040FFUL, + /* 00A4 */ 0x00000000UL, + /* 00A8 */ 0x000041FFUL, + /* 00AC */ 0x00000000UL, + 0x00012000UL, 0x00000700UL, + 0x00012010UL, 0x00000000UL, + 0x00012018UL, 0x00000000UL, + 0x00013008UL, 0x0100AC13UL, + 0x00023030UL, 0x00104C44UL, + /* 3034 */ 0x00000001UL, + 0x00013040UL, 0x00000000UL, + 0x000140A0UL, 0x0F00277AUL, + 0x000140B8UL, 0x00F3C000UL, + 0x000140F4UL, 0x00001020UL, + 0x00024134UL, 0x00000880UL, + /* 4138 */ 0x000087E6UL, + 0x00024140UL, 0x0088006DUL, + /* 4144 */ 0x4D52E6C0UL, + 0x00044160UL, 0x00000000UL, + /* 4164 */ 0x00000000UL, + /* 4168 */ 0x00000006UL, + /* 416C */ 0x00000006UL, + 0x00086014UL, 0x00000010UL, + /* 6018 */ 0x00087020UL, + /* 601C */ 0x0000000FUL, + /* 6020 */ 0x00006000UL, + /* 6024 */ 0x000A0000UL, + /* 6028 */ 0x03000000UL, + /* 602C */ 0x00000000UL, + /* 6030 */ 0x00000000UL, + 0x00066050UL, 0x00FF0352UL, + /* 6054 */ 0x00001040UL, + /* 6058 */ 0x00000000UL, + /* 605C */ 0x00D20412UL, + /* 6060 */ 0x00002C48UL, + /* 6064 */ 0x00000000UL, + 0x000C6078UL, 0x11E00100UL, + /* 607C */ 0x0000164FUL, + /* 6080 */ 0x003203D0UL, + /* 6084 */ 0x00000000UL, + /* 6088 */ 0x00000000UL, + /* 608C */ 0x00000000UL, + /* 6090 */ 0x00000000UL, + /* 6094 */ 0x00000000UL, + /* 6098 */ 0x00000000UL, + /* 609C */ 0x00000000UL, + /* 60A0 */ 0x00000000UL, + /* 60A4 */ 0x00000000UL, + 0x000760E4UL, 0xCC720080UL, + /* 60E8 */ 0x00000000UL, + /* 60EC */ 0x07830464UL, + /* 60F0 */ 0x3AC81388UL, + /* 60F4 */ 0x000A209CUL, + /* 60F8 */ 0x00206100UL, + /* 60FC */ 0x123556B7UL, + 0x00036104UL, 0x0011C997UL, + /* 6108 */ 0x29043020UL, + /* 610C */ 0x0040BB88UL, + 0x00016120UL, 0x00000000UL, + 0x00016128UL, 0x0C660664UL, + 0x000C6130UL, 0x00FA53E8UL, + /* 6134 */ 0x00000000UL, + /* 6138 */ 0x00000000UL, + /* 613C */ 0x00000000UL, + /* 6140 */ 0x00000000UL, + /* 6144 */ 0x00000000UL, + /* 6148 */ 0x00000000UL, + /* 614C */ 0x00000001UL, + /* 6150 */ 0x00000000UL, + /* 6154 */ 0x00000000UL, + /* 6158 */ 0x00000000UL, + /* 615C */ 0x00000000UL, + 0x10017014UL, 0x0007F800UL, + 0x30017014UL, 0x000000FEUL, + 0x10017018UL, 0x000000FFUL, + 0x30017018UL, 0x00001300UL, + 0x0005701CUL, 0x862A0060UL, + /* 7020 */ 0x00000000UL, + /* 7024 */ 0x00000082UL, + /* 7028 */ 0x01800000UL, + /* 702C */ 0x000000D5UL, + 0x00027048UL, 0x00003D3CUL, + /* 704C */ 0x000019BCUL, + 0x00037070UL, 0x00220103UL, + /* 7074 */ 0x0008300AUL, + /* 7078 */ 0x00552300UL, + 0xFFFFFFFFUL, +}; + +const RAIL_ChannelConfigEntry_t WaveBird_channels[] = { + { + .phyConfigDeltaAdd = NULL, + .baseFrequency = 2404800000, + .channelSpacing = 2400000, + .physicalChannelOffset = 0, + .channelNumberStart = 0, + .channelNumberEnd = 31, + .maxPower = RAIL_TX_POWER_MAX, + .attr = &channelConfigEntryAttr, +#ifdef RADIO_CONFIG_ENABLE_CONC_PHY + .entryType = 0, +#endif +#ifdef RADIO_CONFIG_ENABLE_STACK_INFO + .stackInfo = NULL, +#endif + .alternatePhy = NULL, + }, +}; + +const RAIL_ChannelConfig_t WaveBird_channelConfig = { + .phyConfigBase = WaveBird_modemConfigBase, + .phyConfigDeltaSubtract = NULL, + .configs = WaveBird_channels, + .length = 1U, + .signature = 0UL, + .xtalFrequencyHz = 38400000UL, +}; + +const RAIL_ChannelConfig_t *channelConfigs[] = { + &WaveBird_channelConfig, + NULL +}; + +uint32_t wavebirdAccelerationBuffer[209]; diff --git a/firmware/libwavebird/src/platform/efr32/efr32xg14/rail_config.h b/firmware/libwavebird/src/platform/efr32/efr32xg14/rail_config.h new file mode 100644 index 0000000..bb52beb --- /dev/null +++ b/firmware/libwavebird/src/platform/efr32/efr32xg14/rail_config.h @@ -0,0 +1,49 @@ +/***************************************************************************//** + * @brief RAIL Configuration + * @details + * WARNING: Auto-Generated Radio Config Header - DO NOT EDIT + * Radio Configurator Version: 2304.5.2 + * RAIL Adapter Version: 2.4.33 + * RAIL Compatibility: 2.x + ******************************************************************************* + * # License + * Copyright 2019 Silicon Laboratories Inc. www.silabs.com + ******************************************************************************* + * + * SPDX-License-Identifier: Zlib + * + * The licensor of this software is Silicon Laboratories Inc. + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + ******************************************************************************/ + +#ifndef __RAIL_CONFIG_H__ +#define __RAIL_CONFIG_H__ + +#include +#include "rail_types.h" + +#define WAVEBIRD_ACCELERATION_BUFFER wavebirdAccelerationBuffer +extern uint32_t wavebirdAccelerationBuffer[]; + +#define RADIO_CONFIG_XTAL_FREQUENCY 38400000UL + +#define RAIL0_CHANNELS_PROFILE_BASE +extern const RAIL_ChannelConfig_t *channelConfigs[]; + +#endif // __RAIL_CONFIG_H__ diff --git a/firmware/libwavebird/src/platform/efr32/efr32xg22/rail_config.c b/firmware/libwavebird/src/platform/efr32/efr32xg22/rail_config.c index d36353f..a52cea0 100644 --- a/firmware/libwavebird/src/platform/efr32/efr32xg22/rail_config.c +++ b/firmware/libwavebird/src/platform/efr32/efr32xg22/rail_config.c @@ -289,4 +289,4 @@ const RAIL_ChannelConfig_t *channelConfigs[] = { NULL }; -uint32_t wavebirdAccelerationBuffer[305]; \ No newline at end of file +uint32_t wavebirdAccelerationBuffer[305]; diff --git a/firmware/libwavebird/src/platform/efr32/efr32xg22/rail_config.h b/firmware/libwavebird/src/platform/efr32/efr32xg22/rail_config.h index e2151e3..bb52beb 100644 --- a/firmware/libwavebird/src/platform/efr32/efr32xg22/rail_config.h +++ b/firmware/libwavebird/src/platform/efr32/efr32xg22/rail_config.h @@ -46,4 +46,4 @@ extern uint32_t wavebirdAccelerationBuffer[]; #define RAIL0_CHANNELS_PROFILE_BASE extern const RAIL_ChannelConfig_t *channelConfigs[]; -#endif // __RAIL_CONFIG_H__ \ No newline at end of file +#endif // __RAIL_CONFIG_H__ From b0831b8df103118d60789face97bbb23a445dabd Mon Sep 17 00:00:00 2001 From: James Smith Date: Thu, 8 Jan 2026 14:48:48 -0800 Subject: [PATCH 03/15] Remove cmake remnants --- firmware/.python-version | 1 - firmware/CMakeLists.txt | 22 ---------- firmware/CMakePresets.json | 56 -------------------------- firmware/receiver/src/app_properties.c | 21 ---------- firmware/requirements.txt | 4 -- 5 files changed, 104 deletions(-) delete mode 100644 firmware/.python-version delete mode 100644 firmware/CMakeLists.txt delete mode 100644 firmware/CMakePresets.json delete mode 100644 firmware/receiver/src/app_properties.c delete mode 100644 firmware/requirements.txt diff --git a/firmware/.python-version b/firmware/.python-version deleted file mode 100644 index c8cfe39..0000000 --- a/firmware/.python-version +++ /dev/null @@ -1 +0,0 @@ -3.10 diff --git a/firmware/CMakeLists.txt b/firmware/CMakeLists.txt deleted file mode 100644 index 0917cbf..0000000 --- a/firmware/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -# Set minimum CMake version -cmake_minimum_required(VERSION "3.21") - -# Configure project and languages -project(wavephoenix LANGUAGES C ASM) - -# Export compile commands for use with clangd -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) - -# Include build targets for the common libraries -add_subdirectory(libsi) -add_subdirectory(libwavebird) - -# Include build targets for the applications -if(CMAKE_CROSSCOMPILING) - # Include openocd flashing utils - include(${CMAKE_SOURCE_DIR}/cmake/openocd.cmake) - include(${CMAKE_SOURCE_DIR}/cmake/ota.cmake) - - # Include build targets for the applications - add_subdirectory(receiver) -endif() diff --git a/firmware/CMakePresets.json b/firmware/CMakePresets.json deleted file mode 100644 index e1926fc..0000000 --- a/firmware/CMakePresets.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "version": 6, - "configurePresets": [ - { - "name": "base", - "hidden": true, - "generator": "Ninja", - "binaryDir": "${sourceDir}/build/${presetName}" - }, - { - "name": "rf-bm-bg22c3", - "inherits": "base", - "toolchainFile": "${sourceDir}/cmake/toolchains/arm-cortex-m33.cmake", - "cacheVariables": { - "CMAKE_BUILD_TYPE": "Release", - "LINKER_SCRIPT": "${sourceDir}/ldscripts/efr32xg22.ld", - "GECKO_DEVICE": "EFR32BG22C224F512GM32", - "GECKO_CPU_FAMILY": "EFR32BG22", - "BOARD": "rf-bm-bg22c3", - "DEBUG": true - } - }, - { - "name": "efr32xg22e", - "inherits": "base", - "toolchainFile": "${sourceDir}/cmake/toolchains/arm-cortex-m33.cmake", - "cacheVariables": { - "CMAKE_BUILD_TYPE": "Release", - "LINKER_SCRIPT": "${sourceDir}/ldscripts/efr32xg22.ld", - "GECKO_DEVICE": "EFR32MG22C224F512IM40", - "GECKO_CPU_FAMILY": "EFR32MG22", - "BOARD": "efr32xg22e", - "DEBUG": true - } - }, - { - "name": "host", - "description": "Configure build for compiling for the host system (for tests).", - "inherits": "base" - } - ], - "buildPresets": [ - { - "name": "rf-bm-bg22c3", - "configurePreset": "rf-bm-bg22c3" - }, - { - "name": "efr32xg22e", - "configurePreset": "efr32xg22e" - }, - { - "name": "test_host", - "configurePreset": "host" - } - ] -} \ No newline at end of file diff --git a/firmware/receiver/src/app_properties.c b/firmware/receiver/src/app_properties.c deleted file mode 100644 index 39ae0aa..0000000 --- a/firmware/receiver/src/app_properties.c +++ /dev/null @@ -1,21 +0,0 @@ -// Required application properties for the Gecko bootloader - -#include "application_properties.h" -#include "version.h" - -// Application UUID for WavePhoenix receiver firmware -#define PRODUCT_ID {0xcb, 0x39, 0xea, 0xcc, 0x71, 0x90, 0x44, 0x35, 0x8f, 0x77, 0xfc, 0xed, 0x4d, 0x0b, 0x96, 0xeb} - -const ApplicationProperties_t sl_app_properties = { - .magic = APPLICATION_PROPERTIES_MAGIC, - .structVersion = APPLICATION_PROPERTIES_VERSION, - .signatureType = APPLICATION_SIGNATURE_NONE, - .signatureLocation = 0, - .app = - { - .type = APPLICATION_TYPE_MCU, - .version = VERSION_FULL, - .capabilities = 0, - .productId = PRODUCT_ID, - }, -}; \ No newline at end of file diff --git a/firmware/requirements.txt b/firmware/requirements.txt deleted file mode 100644 index 5976bab..0000000 --- a/firmware/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ -jinja2 -numpy -pyyaml -scipy \ No newline at end of file From 1ca9657bcb9c277b9d6df1737e17596926abe73f Mon Sep 17 00:00:00 2001 From: James Smith Date: Thu, 8 Jan 2026 15:12:15 -0800 Subject: [PATCH 04/15] Working app properties/version --- firmware/receiver/src/app_properties.h | 28 ++++++++++++++++++++++++++ firmware/receiver/wavephoenix.slcp | 7 +++++++ 2 files changed, 35 insertions(+) create mode 100644 firmware/receiver/src/app_properties.h diff --git a/firmware/receiver/src/app_properties.h b/firmware/receiver/src/app_properties.h new file mode 100644 index 0000000..0306c4a --- /dev/null +++ b/firmware/receiver/src/app_properties.h @@ -0,0 +1,28 @@ +#pragma once + +#include "sl_application_type.h" + +#include "version.h" + +// Type of signature this application is signed with +// Default: APPLICATION_SIGNATURE_NONE(0) +#define SL_APPLICATION_SIGNATURE 0 + +// Location of the signature +// Default: 0xFFFFFFFF +#define SL_APPLICATION_SIGNATURE_LOCATION 0xFFFFFFFF + +// Bitfield representing type of application +#define SL_APPLICATION_TYPE APPLICATION_TYPE + +// Version number for this application +// <0-4294967295:1> +// Default: 1 [0-4294967295] +#define SL_APPLICATION_VERSION (VERSION_MAJOR << 24) | (VERSION_MINOR << 16) | (VERSION_PATCH << 8) + +// Capabilities of this application +// Default: 0 +#define SL_APPLICATION_CAPABILITIES 0 + +// Product ID of the device for which the application is built +#define SL_APPLICATION_PRODUCT_ID {0xcb, 0x39, 0xea, 0xcc, 0x71, 0x90, 0x44, 0x35, 0x8f, 0x77, 0xfc, 0xed, 0x4d, 0x0b, 0x96, 0xeb} diff --git a/firmware/receiver/wavephoenix.slcp b/firmware/receiver/wavephoenix.slcp index 084424e..dc1f34a 100644 --- a/firmware/receiver/wavephoenix.slcp +++ b/firmware/receiver/wavephoenix.slcp @@ -20,6 +20,9 @@ component: - id: libwavebird from: libwavebird +include: + - path: src + source: - path: src/button.c - path: src/channel_wheel.c @@ -30,3 +33,7 @@ source: configuration: - name: SL_BOARD_ENABLE_VCOM value: 1 + +define: + - name: APP_PROPERTIES_CONFIG_FILE + value: '"app_properties.h"' From c3d8ad48116f77e3e6a6b42b4837cfdcf63684b9 Mon Sep 17 00:00:00 2001 From: James Smith Date: Thu, 8 Jan 2026 15:14:46 -0800 Subject: [PATCH 05/15] Controller type string helper --- firmware/receiver/src/main.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/firmware/receiver/src/main.c b/firmware/receiver/src/main.c index b3b2e45..035ff45 100644 --- a/firmware/receiver/src/main.c +++ b/firmware/receiver/src/main.c @@ -293,6 +293,21 @@ static void handle_pairing_finished(uint8_t status, uint8_t channel) } } +// Get a printable name for the controller type +static const char *get_controller_type_name(uint8_t type) +{ + switch (type) { + case WP_CONT_TYPE_GC_WAVEBIRD: + return "WaveBird"; + case WP_CONT_TYPE_GC_WIRED: + return "Wired GameCube"; + case WP_CONT_TYPE_GC_WIRED_NOMOTOR: + return "Wired GameCube (no motor)"; + default: + return "Unknown"; + } +} + // Qualify a WaveBird packet during pairing static bool qualify_packet(const uint8_t *packet) { @@ -385,9 +400,7 @@ int main(void) printf("WavePhoenix receiver ready!\n"); printf("- Firmware version: %d.%d.%d\n", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH); printf("- Radio channel: %u\n", settings.chan + 1); - printf("- Controller type: %s\n", (settings.cont_type == WP_CONT_TYPE_GC_WAVEBIRD) ? "WaveBird" - : (settings.cont_type == WP_CONT_TYPE_GC_WIRED) ? "Wired" - : "Wired (no motor)"); + printf("- Controller type: %s\n", get_controller_type_name(settings.cont_type)); printf("\n"); // Wait for the SI bus to be idle before starting the main loop From 14e59d201fd4a8284e86d06cd2e91d1e4a804ced Mon Sep 17 00:00:00 2001 From: James Smith Date: Thu, 8 Jan 2026 15:43:08 -0800 Subject: [PATCH 06/15] Configure clocks as they were previously --- firmware/receiver/wavephoenix.slcp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/firmware/receiver/wavephoenix.slcp b/firmware/receiver/wavephoenix.slcp index dc1f34a..8668450 100644 --- a/firmware/receiver/wavephoenix.slcp +++ b/firmware/receiver/wavephoenix.slcp @@ -33,6 +33,12 @@ source: configuration: - name: SL_BOARD_ENABLE_VCOM value: 1 + - name: SL_CLOCK_MANAGER_HFRCO_DPLL_EN + value: 1 + - name: SL_CLOCK_MANAGER_DEFAULT_HF_CLOCK_SOURCE + value: SL_CLOCK_MANAGER_DEFAULT_HF_CLOCK_SOURCE_HFRCODPLL + - name: SL_CLOCK_MANAGER_EM01GRPACLK_SOURCE + value: CMU_EM01GRPACLKCTRL_CLKSEL_HFXO define: - name: APP_PROPERTIES_CONFIG_FILE From 1d9d61fd6b76905d64a07ed29c98f47d59921774 Mon Sep 17 00:00:00 2001 From: James Smith Date: Thu, 8 Jan 2026 16:05:28 -0800 Subject: [PATCH 07/15] Flatten firmware folder --- firmware/.gitignore | 22 +------------------ firmware/{receiver => }/README.md | 0 .../{receiver => }/boards/efr32xg22e.slcc | 0 .../{receiver => }/boards/rf-bm-bg22c3.slcc | 0 firmware/libwavebird/.gitignore | 1 + firmware/{receiver => }/src/app_properties.h | 0 firmware/{receiver => }/src/button.c | 0 firmware/{receiver => }/src/button.h | 0 firmware/{receiver => }/src/channel_wheel.c | 0 firmware/{receiver => }/src/channel_wheel.h | 0 firmware/{receiver => }/src/led.c | 0 firmware/{receiver => }/src/led.h | 0 firmware/{receiver => }/src/main.c | 0 firmware/{receiver => }/src/settings.c | 0 firmware/{receiver => }/src/settings.h | 0 firmware/{receiver => }/src/version.h | 0 firmware/{receiver => }/wavephoenix.slce | 0 firmware/{receiver => }/wavephoenix.slcp | 0 18 files changed, 2 insertions(+), 21 deletions(-) rename firmware/{receiver => }/README.md (100%) rename firmware/{receiver => }/boards/efr32xg22e.slcc (100%) rename firmware/{receiver => }/boards/rf-bm-bg22c3.slcc (100%) rename firmware/{receiver => }/src/app_properties.h (100%) rename firmware/{receiver => }/src/button.c (100%) rename firmware/{receiver => }/src/button.h (100%) rename firmware/{receiver => }/src/channel_wheel.c (100%) rename firmware/{receiver => }/src/channel_wheel.h (100%) rename firmware/{receiver => }/src/led.c (100%) rename firmware/{receiver => }/src/led.h (100%) rename firmware/{receiver => }/src/main.c (100%) rename firmware/{receiver => }/src/settings.c (100%) rename firmware/{receiver => }/src/settings.h (100%) rename firmware/{receiver => }/src/version.h (100%) rename firmware/{receiver => }/wavephoenix.slce (100%) rename firmware/{receiver => }/wavephoenix.slcp (100%) diff --git a/firmware/.gitignore b/firmware/.gitignore index 1cc3589..9f97022 100644 --- a/firmware/.gitignore +++ b/firmware/.gitignore @@ -1,21 +1 @@ -# Autogenerated sources -target/ - -# Python -.venv/ - -# CMake -build/ -CMakeLists.txt.user -CMakeCache.txt -CMakeFiles/ -CMakeScripts/ -Makefile -cmake_install.cmake -install_manifest.txt -compile_commands.json -CTestTestfile.cmake -CMakeUserPresets.json - -# clangd -.cache/ \ No newline at end of file +target/ \ No newline at end of file diff --git a/firmware/receiver/README.md b/firmware/README.md similarity index 100% rename from firmware/receiver/README.md rename to firmware/README.md diff --git a/firmware/receiver/boards/efr32xg22e.slcc b/firmware/boards/efr32xg22e.slcc similarity index 100% rename from firmware/receiver/boards/efr32xg22e.slcc rename to firmware/boards/efr32xg22e.slcc diff --git a/firmware/receiver/boards/rf-bm-bg22c3.slcc b/firmware/boards/rf-bm-bg22c3.slcc similarity index 100% rename from firmware/receiver/boards/rf-bm-bg22c3.slcc rename to firmware/boards/rf-bm-bg22c3.slcc diff --git a/firmware/libwavebird/.gitignore b/firmware/libwavebird/.gitignore index fbd9f44..478423c 100644 --- a/firmware/libwavebird/.gitignore +++ b/firmware/libwavebird/.gitignore @@ -1 +1,2 @@ +.venv/ radioconf_generation_log.json \ No newline at end of file diff --git a/firmware/receiver/src/app_properties.h b/firmware/src/app_properties.h similarity index 100% rename from firmware/receiver/src/app_properties.h rename to firmware/src/app_properties.h diff --git a/firmware/receiver/src/button.c b/firmware/src/button.c similarity index 100% rename from firmware/receiver/src/button.c rename to firmware/src/button.c diff --git a/firmware/receiver/src/button.h b/firmware/src/button.h similarity index 100% rename from firmware/receiver/src/button.h rename to firmware/src/button.h diff --git a/firmware/receiver/src/channel_wheel.c b/firmware/src/channel_wheel.c similarity index 100% rename from firmware/receiver/src/channel_wheel.c rename to firmware/src/channel_wheel.c diff --git a/firmware/receiver/src/channel_wheel.h b/firmware/src/channel_wheel.h similarity index 100% rename from firmware/receiver/src/channel_wheel.h rename to firmware/src/channel_wheel.h diff --git a/firmware/receiver/src/led.c b/firmware/src/led.c similarity index 100% rename from firmware/receiver/src/led.c rename to firmware/src/led.c diff --git a/firmware/receiver/src/led.h b/firmware/src/led.h similarity index 100% rename from firmware/receiver/src/led.h rename to firmware/src/led.h diff --git a/firmware/receiver/src/main.c b/firmware/src/main.c similarity index 100% rename from firmware/receiver/src/main.c rename to firmware/src/main.c diff --git a/firmware/receiver/src/settings.c b/firmware/src/settings.c similarity index 100% rename from firmware/receiver/src/settings.c rename to firmware/src/settings.c diff --git a/firmware/receiver/src/settings.h b/firmware/src/settings.h similarity index 100% rename from firmware/receiver/src/settings.h rename to firmware/src/settings.h diff --git a/firmware/receiver/src/version.h b/firmware/src/version.h similarity index 100% rename from firmware/receiver/src/version.h rename to firmware/src/version.h diff --git a/firmware/receiver/wavephoenix.slce b/firmware/wavephoenix.slce similarity index 100% rename from firmware/receiver/wavephoenix.slce rename to firmware/wavephoenix.slce diff --git a/firmware/receiver/wavephoenix.slcp b/firmware/wavephoenix.slcp similarity index 100% rename from firmware/receiver/wavephoenix.slcp rename to firmware/wavephoenix.slcp From 17b4a28c32913f1bfb2ec659f4c13b9ac2b4b8e5 Mon Sep 17 00:00:00 2001 From: James Smith Date: Fri, 9 Jan 2026 17:11:09 -0800 Subject: [PATCH 08/15] Remove libsi/libwavebird test running --- .github/workflows/test.yml | 33 --------------------------------- 1 file changed, 33 deletions(-) delete mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index f3df442..0000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: Run Tests - -on: - workflow_dispatch: - push: - paths: - - "firmware/libsi/**" - - "firmware/libwavebird/**" - -jobs: - test: - runs-on: ubuntu-latest - - steps: - - name: Install build dependencies - run: | - sudo apt-get update - sudo apt-get install -y cmake ninja-build - - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Run libsi tests - run: | - cd firmware/libsi - cmake -Bbuild && cmake --build build --target test_si - ./build/test/test_si - - - name: Run libwavebird tests - run: | - cd firmware/libwavebird - cmake -Bbuild && cmake --build build --target test_wavebird - ./build/test/test_wavebird From d1ae5033662b8ddd34bf85bf6aa2ef65e9adb0df Mon Sep 17 00:00:00 2001 From: James Smith Date: Sat, 10 Jan 2026 11:48:49 -0800 Subject: [PATCH 09/15] Remove internal libwavebird --- firmware/libwavebird/.gitignore | 2 - firmware/libwavebird/CMakeLists.txt | 17 - firmware/libwavebird/README.md | 35 -- .../config/rail/efr32xg1.radioconf | 183 ---------- .../config/rail/efr32xg14.radioconf | 175 ---------- .../config/rail/efr32xg22.radioconf | 235 ------------- .../libwavebird/include/wavebird/bch3121.h | 64 ---- .../libwavebird/include/wavebird/message.h | 288 ---------------- .../libwavebird/include/wavebird/packet.h | 144 -------- firmware/libwavebird/include/wavebird/radio.h | 136 -------- firmware/libwavebird/libwavebird.slcc | 34 -- firmware/libwavebird/libwavebird.slce | 10 - .../scripts/generate_rail_configs.sh | 33 -- firmware/libwavebird/src/bch3121.c | 167 --------- firmware/libwavebird/src/packet.c | 167 --------- .../src/platform/efr32/efr32xg1/rail_config.c | 203 ----------- .../src/platform/efr32/efr32xg1/rail_config.h | 49 --- .../platform/efr32/efr32xg14/rail_config.c | 240 ------------- .../platform/efr32/efr32xg14/rail_config.h | 49 --- .../platform/efr32/efr32xg22/rail_config.c | 292 ---------------- .../platform/efr32/efr32xg22/rail_config.h | 49 --- .../src/platform/efr32/radio_efr32.c | 322 ------------------ firmware/libwavebird/test/CMakeLists.txt | 22 -- firmware/libwavebird/test/fixtures.h | 37 -- firmware/libwavebird/test/test_bch3121.c | 109 ------ firmware/libwavebird/test/test_packet.c | 253 -------------- 26 files changed, 3315 deletions(-) delete mode 100644 firmware/libwavebird/.gitignore delete mode 100644 firmware/libwavebird/CMakeLists.txt delete mode 100644 firmware/libwavebird/README.md delete mode 100644 firmware/libwavebird/config/rail/efr32xg1.radioconf delete mode 100644 firmware/libwavebird/config/rail/efr32xg14.radioconf delete mode 100644 firmware/libwavebird/config/rail/efr32xg22.radioconf delete mode 100644 firmware/libwavebird/include/wavebird/bch3121.h delete mode 100644 firmware/libwavebird/include/wavebird/message.h delete mode 100644 firmware/libwavebird/include/wavebird/packet.h delete mode 100644 firmware/libwavebird/include/wavebird/radio.h delete mode 100644 firmware/libwavebird/libwavebird.slcc delete mode 100644 firmware/libwavebird/libwavebird.slce delete mode 100755 firmware/libwavebird/scripts/generate_rail_configs.sh delete mode 100644 firmware/libwavebird/src/bch3121.c delete mode 100644 firmware/libwavebird/src/packet.c delete mode 100644 firmware/libwavebird/src/platform/efr32/efr32xg1/rail_config.c delete mode 100644 firmware/libwavebird/src/platform/efr32/efr32xg1/rail_config.h delete mode 100644 firmware/libwavebird/src/platform/efr32/efr32xg14/rail_config.c delete mode 100644 firmware/libwavebird/src/platform/efr32/efr32xg14/rail_config.h delete mode 100644 firmware/libwavebird/src/platform/efr32/efr32xg22/rail_config.c delete mode 100644 firmware/libwavebird/src/platform/efr32/efr32xg22/rail_config.h delete mode 100644 firmware/libwavebird/src/platform/efr32/radio_efr32.c delete mode 100644 firmware/libwavebird/test/CMakeLists.txt delete mode 100644 firmware/libwavebird/test/fixtures.h delete mode 100644 firmware/libwavebird/test/test_bch3121.c delete mode 100644 firmware/libwavebird/test/test_packet.c diff --git a/firmware/libwavebird/.gitignore b/firmware/libwavebird/.gitignore deleted file mode 100644 index 478423c..0000000 --- a/firmware/libwavebird/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -.venv/ -radioconf_generation_log.json \ No newline at end of file diff --git a/firmware/libwavebird/CMakeLists.txt b/firmware/libwavebird/CMakeLists.txt deleted file mode 100644 index 790dfe6..0000000 --- a/firmware/libwavebird/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -# Set minimum CMake version -cmake_minimum_required(VERSION "3.21") - -# Configure project and languages -project(wavebird LANGUAGES C) - -# Add CTest for running tests -include(CTest) - -# Define the target and add the source files -add_library(wavebird STATIC "src/bch3121.c" "src/packet.c") - -# Specify the include paths -target_include_directories(wavebird PRIVATE src/autogen PUBLIC include) - -# Add the test targets -add_subdirectory(test) diff --git a/firmware/libwavebird/README.md b/firmware/libwavebird/README.md deleted file mode 100644 index 03faf33..0000000 --- a/firmware/libwavebird/README.md +++ /dev/null @@ -1,35 +0,0 @@ -# libwavebird - -An open source implementation of the WaveBird protocol for Silicon Labs Gecko SoCs. - -Currently supports Silicon Labs Gecko Series 1 and Series 2 SoCs. Tested with the EFR32FG1, EFR32FG14, EFR32MG22, and EFR32BG22 SoCs. - -## Adding support for additional Gecko SoCs - -In theory this library should work with any Gecko Series 1 or Series 2 SoC that has proprietary 2.4GHz support. A `.radioconf` file for that platform will need to be created in `config/rail`. - -## Generating `rail_config` files - -The `rail_config.c` and `rail_config.h` files are generated from `.radioconf` files using the Silicon Labs multiphy configurator script. - -To regenerate the files, run `scripts/generate_rail_config.py`. - -## Adding support for additional platforms - -The BCH(31,21) logic and packet decoding logic is platform agnostic. - -I currently don't know of any other SoCs which support the WaveBird's FSK+DSSS 15-chip modulation, but in case they do exist, I've tried to keep the code modular. The Silicon Labs Gecko specific code is restricted to `radio_efr32.c`. A new platform would need to provide implementations for the functions defined in `radio.h`. - -## Running tests - -Build the test suite - -```bash -cmake -Bbuild && cmake --build build -``` - -Run the tests - -```bash -ctest --test-dir build --output-on-failure -``` \ No newline at end of file diff --git a/firmware/libwavebird/config/rail/efr32xg1.radioconf b/firmware/libwavebird/config/rail/efr32xg1.radioconf deleted file mode 100644 index e59516d..0000000 --- a/firmware/libwavebird/config/rail/efr32xg1.radioconf +++ /dev/null @@ -1,183 +0,0 @@ - - - - - - - 0 - 31 - SAME_AS_FIRST_CHANNEL - RAIL_TX_POWER_MAX - {"selectedPhy":"PHY_Datasheet_2450M_2GFSK_1Mbps_500K"} - - - {"selectedPhy":"PHY_Datasheet_2450M_2GFSK_1Mbps_500K"} - - - base_frequency_hz - 2404800000 - - - channel_spacing_hz - 2400000 - - - xtal_frequency_hz - 38400000 - - - rx_xtal_error_ppm - 0 - - - tx_xtal_error_ppm - 0 - - - syncword_0 - 564 - - - syncword_1 - 0 - - - syncword_length - 12 - - - preamble_pattern_len - 2 - - - preamble_length - 100 - - - preamble_pattern - 10 - - - modulation_type - 0 - - - deviation - 500000 - - - bitrate - 96000 - - - baudrate_tol_ppm - 20000 - - - shaping_filter - 0 - - - fsk_symbol_map - 0 - - - shaping_filter_param - 0.5 - - - diff_encoding_mode - 0 - - - dsss_chipping_code - 5711 - - - dsss_len - 15 - - - dsss_spreading_factor - 15 - - - frame_bitendian - 1 - - - frame_length_type - 0 - - - header_en - false - - - payload_white_en - false - - - payload_crc_en - false - - - fixed_length_size - 19 - - - crc_poly - 0 - - - crc_seed - 0 - - - crc_byte_endian - 0 - - - crc_bit_endian - 1 - - - crc_pad_input - false - - - crc_input_order - 0 - - - crc_invert - false - - - symbol_encoding - 2 - - - white_poly - 0 - - - agc_power_target - -8 - - - agc_speed - 1 - - - agc_period - 0 - - - agc_settling_delay - 39 - - - - - \ No newline at end of file diff --git a/firmware/libwavebird/config/rail/efr32xg14.radioconf b/firmware/libwavebird/config/rail/efr32xg14.radioconf deleted file mode 100644 index 42c4dbf..0000000 --- a/firmware/libwavebird/config/rail/efr32xg14.radioconf +++ /dev/null @@ -1,175 +0,0 @@ - - - - - - - 0 - 31 - SAME_AS_FIRST_CHANNEL - RAIL_TX_POWER_MAX - {"selectedPhy":"PHY_Datasheet_2450M_2GFSK_2Mbps_1M"} - - - {"selectedPhy":"PHY_Datasheet_2450M_2GFSK_2Mbps_1M"} - - - base_frequency_hz - 2404800000 - - - channel_spacing_hz - 2400000 - - - xtal_frequency_hz - 38400000 - - - rx_xtal_error_ppm - 0 - - - tx_xtal_error_ppm - 0 - - - syncword_0 - 4660 - - - syncword_1 - 0 - - - syncword_length - 16 - - - preamble_pattern_len - 2 - - - preamble_length - 420 - - - preamble_pattern - 1 - - - modulation_type - 0 - - - deviation - 500000 - - - bitrate - 96000 - - - baudrate_tol_ppm - 20000 - - - shaping_filter - 0 - - - fsk_symbol_map - 0 - - - shaping_filter_param - 0.5 - - - diff_encoding_mode - 0 - - - dsss_chipping_code - 5711 - - - dsss_len - 15 - - - dsss_spreading_factor - 15 - - - frame_bitendian - 1 - - - frame_length_type - 0 - - - header_en - false - - - payload_white_en - false - - - payload_crc_en - false - - - fixed_length_size - 19 - - - crc_poly - 0 - - - crc_seed - 0 - - - crc_byte_endian - 0 - - - crc_bit_endian - 1 - - - crc_pad_input - false - - - crc_input_order - 0 - - - crc_invert - false - - - symbol_encoding - 2 - - - asynchronous_rx_enable - false - - - frequency_comp_mode - 0 - - - timing_detection_threshold - 0 - - - - - \ No newline at end of file diff --git a/firmware/libwavebird/config/rail/efr32xg22.radioconf b/firmware/libwavebird/config/rail/efr32xg22.radioconf deleted file mode 100644 index 786ab1a..0000000 --- a/firmware/libwavebird/config/rail/efr32xg22.radioconf +++ /dev/null @@ -1,235 +0,0 @@ - - - - - - - 0 - 31 - SAME_AS_FIRST_CHANNEL - RAIL_TX_POWER_MAX - {"selectedPhy":"PHY_Datasheet_2450M_2GFSK_2Mbps_1M"} - - - {"selectedPhy":"PHY_Datasheet_2450M_2GFSK_2Mbps_1M"} - - - base_frequency_hz - 2404800000 - - - channel_spacing_hz - 2400000 - - - xtal_frequency_hz - 38400000 - - - rx_xtal_error_ppm - 50 - - - tx_xtal_error_ppm - 50 - - - syncword_0 - 564 - - - syncword_1 - 0 - - - syncword_length - 12 - - - preamble_pattern_len - 2 - - - preamble_length - 100 - - - preamble_pattern - 10 - - - modulation_type - 0 - - - deviation - 580000 - - - bitrate - 96000 - - - baudrate_tol_ppm - 0 - - - shaping_filter - 0 - - - fsk_symbol_map - 0 - - - shaping_filter_param - 0.5 - - - diff_encoding_mode - 0 - - - dsss_chipping_code - 5711 - - - dsss_len - 15 - - - dsss_spreading_factor - 15 - - - frame_bitendian - 1 - - - frame_length_type - 0 - - - header_en - false - - - payload_white_en - false - - - payload_crc_en - false - - - fixed_length_size - 19 - - - crc_poly - 0 - - - crc_seed - 0 - - - crc_byte_endian - 0 - - - crc_bit_endian - 1 - - - crc_pad_input - false - - - crc_input_order - 0 - - - crc_invert - false - - - header_white_en - false - - - symbol_encoding - 2 - - - pll_bandwidth_tx - 8 - - - agc_power_target - -8 - - - agc_hysteresis - 3 - - - agc_settling_delay - 39 - - - agc_speed - 1 - - - agc_period - 0 - - - agc_scheme - 0 - - - frequency_comp_mode - 0 - - - pll_bandwidth_rx - 6 - - - bandwidth_hz - 1800000 - - - target_osr - 3 - - - src_disable - 0 - - - symbols_in_timing_window - 1 - - - timing_detection_threshold - 6 - - - errors_in_timing_window - 8 - - - number_of_timing_windows - 1 - - - timing_resync_period - 1 - - - - - \ No newline at end of file diff --git a/firmware/libwavebird/include/wavebird/bch3121.h b/firmware/libwavebird/include/wavebird/bch3121.h deleted file mode 100644 index fe3392e..0000000 --- a/firmware/libwavebird/include/wavebird/bch3121.h +++ /dev/null @@ -1,64 +0,0 @@ -/** - * BCH(31,21) encoder and decoder, with error correction. - * - * WaveBird input states are comprised of 4, 31-bit BCH codewords, which are - * interleaved to protect against burst errors. - * - * The BCH coding is (31,21), which means that 10 bits are used for error - * correction, allowing for up to 2 errors to be corrected per codeword. - * - * We are using non-systematic BCH encoding. - */ -#pragma once - -#include -#include - -#define BCH3121_CODEWORD_LEN 31 -#define BCH3121_MESSAGE_LEN 21 -#define BCH3121_ORDER (1 << 10) - -// Error codes -enum { - BCH3121_ERR_DETECTED = 1, - BCH3121_ERR_UNCORRECTABLE, -}; - -/** - * Encode a 21-bit input into a 31-bit BCH codeword. - * - * @param message 21-bit input message - * - * @return 31-bit BCH codeword - */ -uint32_t bch3121_encode(uint32_t message); - -/** - * Decode a 31-bit BCH codeword. - * - * @param message 21-bit decoded message, which may contain errors - * @param codeword 31-bit BCH codeword - * - * @return the syndrome of the codeword - */ -uint32_t bch3121_decode(uint32_t *message, uint32_t codeword); - -/** - * Decode a 31-bit BCH codeword, applying error correction if possible. - * - * @param message 21-bit decoded message, if successful - * @param codeword 31-bit BCH codeword - * - * @return successful decodes will return the number of corrected errors, - * otherwise a negative error code will be returned - */ -int bch3121_decode_and_correct(uint32_t *message, uint32_t codeword); - -/** - * Generate a syndrome table for BCH(31,21) error correction. - * - * The syndrome table is a lookup table that maps syndromes to error patterns. - * - * @param syndrome_table 1024-element table to populate - */ -void bch3121_generate_syndrome_table(uint16_t *syndrome_table); \ No newline at end of file diff --git a/firmware/libwavebird/include/wavebird/message.h b/firmware/libwavebird/include/wavebird/message.h deleted file mode 100644 index 1f3f7a3..0000000 --- a/firmware/libwavebird/include/wavebird/message.h +++ /dev/null @@ -1,288 +0,0 @@ -/** - * WaveBird message unpacking functions. - * - * After WaveBird packets are decoded, they result in an 84-bit message. - * Messages have a 16-bit header, followed by a 68-bit body padded with zeros. - * There are two types of messages, "input state" messages and "origin" messages. - * - * Message headers are structured as follows: - * Bits 15-12: Unknown, always seems to be 0 - * Bit 11: Unknown, always seems to be 1 - * Bit 10: Message type (0 = input state, 1 = origin) - * Bits 9-0: Controller ID - * - * Header examples: - * 0x0AB1 (input state message, controller ID 0x2B1) - * 0x0C38 (origin message, controller ID 0x038) - * - * Input state messages describe the current state of a controller's buttons, sticks, - * and triggers. They are broadcast 250 times per second. - * - * Input state messages are structured as follows: - * 0xHHHHBBBXXYYCXCYLLRRFF - * - HHHH: 16-bit message header (see above) - * - BBB: 12-bit button state (Start, Y, X, B, A, L, R, Z, Up, Down, Right, Left) - * - XX: 8-bit stick X position - * - YY: 8-bit stick Y position - * - CX: 8-bit C-stick X position - * - CY: 8-bit C-stick Y position - * - LL: 8-bit left analog trigger position - * - RR: 8-bit right analog trigger position - * - FF: footer, likely just padding - * - * Input state message example: - * 0x0AB1180DA568A831A1300 - * - * Origin messages describe the state of a controller's analog sticks and triggers when - * it was first powered on. They are broadcast once when the controller is powered on, - * and then repeated every second. - * - * Origin messages are structured as follows: - * 0xHHHHXXYYCXCYLLRRFFFFF - * - HHHH: 16-bit message header (see above) - * - XX: 8-bit stick X origin - * - YY: 8-bit stick Y origin - * - CX: 8-bit C-stick X origin - * - CY: 8-bit C-stick Y positiooriginn - * - LL: 8-bit left analog trigger origin - * - RR: 8-bit right analog trigger origin - * - FFFFF: footer, likely just padding - * - * Origin message example: - * 0x0EB1867F8B831B1300000 - * - * Things to note: - * - When decoding a packet the 84 bits are stored "right aligned" in an 11-byte buffer. - * - We're explicitly not using bitfield structs here, since input and origin messages - * have different byte alignments for the stick and trigger values. - */ - -#pragma once - -#include -#include - -/** - * WaveBird message types. - */ -enum { - WB_MESSAGE_TYPE_INPUT_STATE = 0, - WB_MESSAGE_TYPE_ORIGIN, -}; - -/** - * Button state bitfield struct for WaveBird input state messages. - */ -typedef union { - struct { - uint16_t left : 1; - uint16_t right : 1; - uint16_t down : 1; - uint16_t up : 1; - uint16_t z : 1; - uint16_t r : 1; - uint16_t l : 1; - uint16_t a : 1; - uint16_t b : 1; - uint16_t x : 1; - uint16_t y : 1; - uint16_t start : 1; - }; - uint16_t raw; -} wavebird_buttons_t; - -/** - * Button state bitmask for WaveBird input state messages. - */ -#define WB_BUTTONS_LEFT (1 << 0) -#define WB_BUTTONS_RIGHT (1 << 1) -#define WB_BUTTONS_DOWN (1 << 2) -#define WB_BUTTONS_UP (1 << 3) -#define WB_BUTTONS_Z (1 << 4) -#define WB_BUTTONS_R (1 << 5) -#define WB_BUTTONS_L (1 << 6) -#define WB_BUTTONS_A (1 << 7) -#define WB_BUTTONS_B (1 << 8) -#define WB_BUTTONS_X (1 << 9) -#define WB_BUTTONS_Y (1 << 10) -#define WB_BUTTONS_START (1 << 11) - -/** - * Get the controller ID from the header of a WaveBird message. - * - * @param message the message decoded from a WaveBird packet - * - * @return the controller ID - */ -static inline uint16_t wavebird_message_get_controller_id(const uint8_t *message) -{ - return ((message[0] & 0x0F) << 12 | message[1] << 4 | message[2] >> 4) & 0x3FF; -} - -/** - * Get the message type from a WaveBird message. - * - * @param message the message decoded from a WaveBird packet - * - * @return the message type - */ -static inline uint8_t wavebird_message_get_type(const uint8_t *message) -{ - return message[1] & 0x40 ? WB_MESSAGE_TYPE_ORIGIN : WB_MESSAGE_TYPE_INPUT_STATE; -} - -/** - * Get the buttons from a WaveBird input state message. - * - * @param message the message decoded from a WaveBird packet - * - * @return the button state - */ -static inline uint16_t wavebird_input_state_get_buttons(const uint8_t *message) -{ - return (message[2] & 0x0F) << 8 | message[3]; -} - -/** - * Get the stick X position from a WaveBird input state message. - * - * @param message the message decoded from a WaveBird packet - * - * @return the stick X position - */ -static inline uint8_t wavebird_input_state_get_stick_x(const uint8_t *message) -{ - return message[4]; -} - -/** - * Get the stick Y position from a WaveBird input state message. - * - * @param message the message decoded from a WaveBird packet - * - * @return the stick Y position - */ -static inline uint8_t wavebird_input_state_get_stick_y(const uint8_t *message) -{ - return message[5]; -} - -/** - * Get the C-stick X position from a WaveBird input state message. - * - * @param message the message decoded from a WaveBird packet - * - * @return the C-stick X position - */ -static inline uint8_t wavebird_input_state_get_substick_x(const uint8_t *message) -{ - return message[6]; -} - -/** - * Get the C-stick Y position from a WaveBird input state message. - * - * @param message the message decoded from a WaveBird packet - * - * @return the C-stick Y position - */ -static inline uint8_t wavebird_input_state_get_substick_y(const uint8_t *message) -{ - return message[7]; -} - -/** - * Get the left analog trigger position from a WaveBird input state message. - * - * @param message the message decoded from a WaveBird packet - * - * @return the left analog trigger position - */ -static inline uint8_t wavebird_input_state_get_trigger_left(const uint8_t *message) -{ - return message[8]; -} - -/** - * Get the right analog trigger position from a WaveBird input state message. - * - * @param message the message decoded from a WaveBird packet - * - * @return the right analog trigger position - */ -static inline uint8_t wavebird_input_state_get_trigger_right(const uint8_t *message) -{ - return message[9]; -} - -/** - * Get the stick X origin from a WaveBird origin message. - * - * @param message the message decoded from a WaveBird packet - * - * @return the stick X origin - */ -static inline uint8_t wavebird_origin_get_stick_x(const uint8_t *message) -{ - return (message[2] & 0x0F) << 4 | message[3] >> 4; -} - -/** - * Get the stick Y origin from a WaveBird origin message. - * - * @param message the message decoded from a WaveBird packet - * - * @return the stick Y origin - */ -static inline uint8_t wavebird_origin_get_stick_y(const uint8_t *message) -{ - return (message[3] & 0x0F) << 4 | message[4] >> 4; -} - -/** - * Get the C-stick X origin from a WaveBird origin message. - * - * @param message the message decoded from a WaveBird packet - * - * @return the C-stick X origin - */ -static inline uint8_t wavebird_origin_get_substick_x(const uint8_t *message) -{ - return (message[4] & 0x0F) << 4 | message[5] >> 4; -} - -/** - * Get the C-stick Y origin from a WaveBird origin message. - * - * @param message the message decoded from a WaveBird packet - * - * @return the C-stick Y origin - */ -static inline uint8_t wavebird_origin_get_substick_y(const uint8_t *message) -{ - return (message[5] & 0x0F) << 4 | message[6] >> 4; -} - -/** - * Get the left analog trigger origin from a WaveBird origin message. - * - * @param message the message decoded from a WaveBird packet - * - * @return the left analog trigger origin - */ -static inline uint8_t wavebird_origin_get_trigger_left(const uint8_t *message) -{ - return (message[6] & 0x0F) << 4 | message[7] >> 4; -} - -/** - * Get the right analog trigger origin from a WaveBird origin message. - * - * @param message the message decoded from a WaveBird packet - * - * @return the right analog trigger origin - */ -static inline uint8_t wavebird_origin_get_trigger_right(const uint8_t *message) -{ - return (message[7] & 0x0F) << 4 | message[8] >> 4; -} \ No newline at end of file diff --git a/firmware/libwavebird/include/wavebird/packet.h b/firmware/libwavebird/include/wavebird/packet.h deleted file mode 100644 index 457919a..0000000 --- a/firmware/libwavebird/include/wavebird/packet.h +++ /dev/null @@ -1,144 +0,0 @@ -/** - * WaveBird packet decoding functions. - * - * After FSK-DSSS demodulation, a WaveBird frame is 25 bytes / 200 bits long, - * and is structured as follows: - * - * 0xFAAAAAAA1234XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXYYYYZZZ - * - * The first 6 bytes are the preamble (FAAAAAAA) and sync word (1234). These - * are removed by the PHY layer (radio). - * - * The remaining 19 bytes are the WaveBird packet, structured as follows: - * - * 0xXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXYYYYZZZ - * - X: encoded payload (124 bits) - * - YYYY: CRC (16 bits) - * - ZZZ: footer - seems to be a fixed value, but varies between controllers - * seen values 0x000, 0x010, 0x110, 0x120 - * - * The 124-bit encoded payload is comprised of 4 BCH(31,21) codewords, interleaved to - * protect against burst errors. - * - * After deinterleaving and decoding, we are left with an 84-bit message. - */ - -#pragma once - -#include -#include - -#define WAVEBIRD_PACKET_BYTES 19 -#define WAVEBIRD_MESSAGE_BYTES 11 - -/** - * Packet decoding error codes - */ -enum { - WB_PACKET_ERR_CRC_MISMATCH = 1, - WB_PACKET_ERR_DECODE_FAILED, -}; - -// CRC function prototype, to allow for hardware CRC calculation -typedef uint16_t (*wavebird_packet_crc_fn_t)(const uint8_t *data, size_t length); - -/** - * Get the CRC value from a WaveBird packet. - * - * @param packet the 19-byte packet from the radio - * - * @return the CRC value - */ -static inline uint16_t wavebird_packet_get_crc(const uint8_t *packet) -{ - return (packet[15] & 0x0F) << 12 | packet[16] << 4 | (packet[17] & 0xF0) >> 4; -} - -/** - * Set the CRC value in a WaveBird packet. - * - * @param packet the 19-byte packet - * @param crc the CRC value to set - */ -static inline void wavebird_packet_set_crc(uint8_t *packet, uint16_t crc) -{ - packet[15] &= 0xF0; - packet[15] |= (crc >> 12) & 0x0F; - packet[16] = (crc >> 4) & 0xFF; - packet[17] &= 0x0F; - packet[17] |= (crc << 4) & 0xF0; -} - -/** - * Get the footer from a WaveBird packet. - * - * @param packet the 19-byte packet from the radio - * @return the footer value - */ -static inline uint16_t wavebird_packet_get_footer(const uint8_t *packet) -{ - return (packet[17] & 0x0F) << 8 | packet[18]; -} - -/** - * Set the footer in a WaveBird packet. - * - * @param packet the 19-byte packet - * @param footer the footer value to set - */ -static inline void wavebird_packet_set_footer(uint8_t *packet, uint16_t footer) -{ - packet[17] &= 0xF0; - packet[17] |= (footer >> 8) & 0x0F; - packet[18] = footer & 0xFF; -} - -/** - * Set the CRC function to use for packet encoding and decoding, to allow for - * hardware CRC calculation when available. - * - * Functions must provide a CRC-CCITT implementation, with polynomial 0x1021, - * and initial value 0x0000. - * - * @param crc_fn function to calculate the CRC - */ -void wavebird_packet_set_crc_fn(wavebird_packet_crc_fn_t crc_fn); - -/** - * Deinterleave the payload from a WaveBird packet into 4 BCH(31,21) codewords. - * - * @param codewords array to store the 4, 31-bit codewords - * @param packet the 19-byte packet from the radio - */ -void wavebird_packet_deinterleave(uint32_t *codewords, const uint8_t *packet); - -/** - * Interleave 4 BCH(31,21) codewords, and add them to a WaveBird packet. - * - * @param packet byte array to store the 19-byte packet - * @param codewords the 4, 31-bit codewords - */ -void wavebird_packet_interleave(uint8_t *packet, const uint32_t *codewords); - -/** - * Decode a WaveBird packet into an 84-bit message. - * - * @param message byte array to store the decoded message - * @param packet the 19-byte packet from the radio - * @param crc_fn function to calculate the CRC - * - * @return negative error code on failure, or the packet type on success - * @retval WB_PACKET_INPUT_STATE if the packet is a valid "input state" packet - * @retval WB_PACKET_ORIGIN if the packet is a valid "origin" packet - * @retval -WB_PACKET_ERR_CRC_MISMATCH if CRC check failed - * @retval -WB_PACKET_ERR_DECODE_FAILED if BCH decoding failed - */ -int wavebird_packet_decode(uint8_t *message, const uint8_t *packet); - -/** - * Encode a 84-bit message into a WaveBird packet. - * - * @param packet byte array to store the 19-byte packet - * @param message the 84-bit message to encode - */ -void wavebird_packet_encode(uint8_t *packet, const uint8_t *message); \ No newline at end of file diff --git a/firmware/libwavebird/include/wavebird/radio.h b/firmware/libwavebird/include/wavebird/radio.h deleted file mode 100644 index 4414967..0000000 --- a/firmware/libwavebird/include/wavebird/radio.h +++ /dev/null @@ -1,136 +0,0 @@ -/** - * WaveBird radio functions. - * - * Frequency and modulation: - * - Modulation: FSK+DSSS - * - Base frequency: 2404.8 Mhz - * - Channel spacing: 2.4 Mhz - * - Number of channels: 32 channels (16 channels used) - * - * DSSS chipping: - * - DSSS spreading factor: 15 - * - DSSS chipping code: 0x164F = 0b01011001001111 - * - * Data rate: - * - 96,000 bits/s (1,440,000 chip/s) - * - * Message timing: - * - 4ms per transmission (250 packets/s) - - * - ~100us of unmodulated carrier before preamble - * - 3,000 chips, at 1,440,000 chip/s (2083us) - * - Silence until next transmission - * - * Message framing: - * - Bit endianness: MSB first - * - Frame length: 25 bytes / 200 bits - * - Preamble: 0xFAAAAAAA (32 bits) - * - Sync word: 0x1234 (16 bits) - * - Packet: Remaining 19 bytes (includes CRC and footer) - * - * Virtual pairing: - * This library adds support for "virtual pairing" of WaveBird controllers. - * In contrast to an OEM WaveBird receiver, which has a channel selection wheel, - * virtual pairing allows for pairing via software, or a single button press. - * Once pairing is initiated, the receiver will scan all channels for activity, - * and qualify packets based on a user-defined qualification function. Once the - * qualification threshold is met, the channel is set. - */ - -#pragma once - -#include -#include - -// Radio error codes -enum { - WB_RADIO_ERR = 1, - WB_RADIO_ERR_CALIBRATION, - WB_RADIO_ERR_NO_PACKET, - WB_RADIO_ERR_INVALID_PACKET_LENGTH, - WB_RADIO_ERR_INVALID_CHANNEL, -}; - -// Pairing callback states -enum { - WB_RADIO_PAIRING_SUCCESS, - WB_RADIO_PAIRING_CANCELLED, - WB_RADIO_PAIRING_TIMEOUT, -}; - -// Packet ready callback function -typedef void (*wavebird_radio_packet_fn_t)(const uint8_t *packet); - -// Radio error callback function -typedef void (*wavebird_radio_error_fn_t)(int error); - -// Packet qualification callback function -typedef bool (*wavebird_radio_qualify_fn_t)(const uint8_t *packet); - -// Pairing callback functions -typedef void (*wavebird_radio_pairing_started_fn_t)(void); -typedef void (*wavebird_radio_pairing_finished_fn_t)(uint8_t status, uint8_t channel); - -/** - * Initialize the radio. - * - * @param packet_fn callback function for received packets - * @param error_fn callback function for radio errors - * - * @return 0 on success, negative error code on failure - */ -int wavebird_radio_init(wavebird_radio_packet_fn_t packet_fn, wavebird_radio_error_fn_t error_fn); - -/** - * Get the current radio channel. - * - * @return the current channel, 0-15 - */ -uint8_t wavebird_radio_get_channel(void); - -/** - * Set the radio channel, and start packet reception. - * - * @param channel the channel to set, 0-15 - * - * @return 0 on success, -WB_RADIO_ERR_INVALID_CHANNEL on failure - */ -int wavebird_radio_set_channel(uint8_t channel); - -/** - * Configure pairing packet qualification. - * - * @param qualify_fn callback function to qualify packets - * @param qualify_threshold number of packets that must qualify for pairing to succeed - */ -void wavebird_radio_configure_qualification(wavebird_radio_qualify_fn_t qualify_fn, uint8_t qualify_threshold); - -/** - * Set the pairing started callback function. - * - * @param callback callback function to call when pairing starts - */ -void wavebird_radio_set_pairing_started_callback(wavebird_radio_pairing_started_fn_t callback); - -/** - * Set the pairing finished callback function. - * - * @param callback callback function to call when pairing finishes - */ -void wavebird_radio_set_pairing_finished_callback(wavebird_radio_pairing_finished_fn_t callback); - -/** - * Start the virtual pairing process. - */ -void wavebird_radio_start_pairing(void); - -/** - * Stop the virtual pairing process. - */ -void wavebird_radio_stop_pairing(void); - -/** - * Process radio events. - * - * This function should be called periodically to process radio events. - */ -void wavebird_radio_process(void); \ No newline at end of file diff --git a/firmware/libwavebird/libwavebird.slcc b/firmware/libwavebird/libwavebird.slcc deleted file mode 100644 index daca952..0000000 --- a/firmware/libwavebird/libwavebird.slcc +++ /dev/null @@ -1,34 +0,0 @@ -id: libwavebird -label: libwavebird -package: "ext-comp" -description: > - An open source implementation of the WaveBird protocol. -category: External -quality: production - -requires: - - name: rail_lib - - name: rail_util_pa - -provides: - - name: libwavebird - -include: - - path: include - - path: src/platform/efr32/efr32xg1 - condition: [device_generic_family_efr32xg1] - - path: src/platform/efr32/efr32xg14 - condition: [device_generic_family_efr32xg14] - - path: src/platform/efr32/efr32xg22 - condition: [device_generic_family_efr32xg22] - -source: - - path: src/bch3121.c - - path: src/packet.c - - path: src/platform/efr32/radio_efr32.c - - path: src/platform/efr32/efr32xg1/rail_config.c - condition: [device_generic_family_efr32xg1] - - path: src/platform/efr32/efr32xg14/rail_config.c - condition: [device_generic_family_efr32xg14] - - path: src/platform/efr32/efr32xg22/rail_config.c - condition: [device_generic_family_efr32xg22] \ No newline at end of file diff --git a/firmware/libwavebird/libwavebird.slce b/firmware/libwavebird/libwavebird.slce deleted file mode 100644 index 56f7c52..0000000 --- a/firmware/libwavebird/libwavebird.slce +++ /dev/null @@ -1,10 +0,0 @@ -id: libwavebird -label: libwavebird -version: 1.0.0 - -sdk: - id: simplicity_sdk - version: 2025.6.2 - -component_path: - - path: . diff --git a/firmware/libwavebird/scripts/generate_rail_configs.sh b/firmware/libwavebird/scripts/generate_rail_configs.sh deleted file mode 100755 index 9040fc8..0000000 --- a/firmware/libwavebird/scripts/generate_rail_configs.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env bash - -VENV_DIR=".venv" - -# Check if GECKO_SDK_PATH is set -if [ -z "$GECKO_SDK_PATH" ]; then - echo "Error: GECKO_SDK_PATH environment variable not set" - exit 1 -fi - -# Create the virtual environment if it doesn't exist -if [ ! -d "$VENV_DIR" ]; then - echo "Creating Python 3.10 virtual environment..." - python3.10 -m venv $VENV_DIR -fi - -# Save the path to the configurator script -CONFIGURATOR_SCRIPT="${GECKO_SDK_PATH}/platform/radio/efr32_multiphy_configurator/Efr32RadioConfiguratorUcAdapter.py" - -# Activate the virtual environment -source $VENV_DIR/bin/activate - -# Install required Python packages -pip install jinja2 numpy pyyaml scipy - -# For each .radioconf file in config/rail, generate the corresponding rail config -for radioconf_file in config/rail/*.radioconf; do - base_name=$(basename "$radioconf_file" .radioconf) - output_path="src/platform/efr32/${base_name}" - - python ${CONFIGURATOR_SCRIPT} ${radioconf_file} -o ${output_path} - echo "Generated rail config for ${base_name} at ${output_path}" -done \ No newline at end of file diff --git a/firmware/libwavebird/src/bch3121.c b/firmware/libwavebird/src/bch3121.c deleted file mode 100644 index d23684d..0000000 --- a/firmware/libwavebird/src/bch3121.c +++ /dev/null @@ -1,167 +0,0 @@ -#include - -#include "wavebird/bch3121.h" - -// Generator polynomial, g(x) = x^10 + x^9 + x^8 + x^6 + x^5 + x^3 + 1 -#define BCH3121_POLYNOMIAL 0b11101101001 - -/** - * LUT for error positions based on syndrome, encoded as follows: - * - Bits 0-1: Number of errors (0b01 = 1, 0b10 = 2, 0b00 = uncorrectable) - * - Bits 2-6: Position of first error - * - Bits 7-11: Position of second error (if present) - * - * Generated with bch3121_generate_syndrome_table() - */ -static const uint16_t bch3121_error_positions[] = { - 0x000, 0x055, 0x059, 0xB56, 0x05D, 0xBD6, 0xBDA, 0x000, 0x061, 0xC56, 0xC5A, 0x000, 0xC5E, 0x8A6, 0x000, 0x82E, - 0x065, 0xCD6, 0xCDA, 0x626, 0xCDE, 0x000, 0x000, 0x000, 0xCE2, 0x706, 0x92A, 0x000, 0x000, 0x000, 0x8B2, 0x102, - 0x069, 0xD56, 0xD5A, 0x000, 0xD5E, 0x000, 0x6AA, 0x70E, 0xD62, 0x000, 0x000, 0x000, 0x000, 0x782, 0x000, 0x000, - 0xD66, 0x000, 0x78A, 0x000, 0x9AE, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x9C2, 0x936, 0x000, 0x186, 0x000, - 0x06D, 0xDD6, 0xDDA, 0x83A, 0xDDE, 0x000, 0x000, 0x000, 0xDE2, 0x000, 0x000, 0x000, 0x72E, 0x000, 0x792, 0x000, - 0xDE6, 0xE36, 0x000, 0xF22, 0x000, 0x586, 0x000, 0x000, 0x000, 0x000, 0x806, 0x000, 0x000, 0x000, 0x000, 0x98E, - 0xDEA, 0x986, 0x000, 0x202, 0x80E, 0x000, 0x000, 0x39A, 0xA32, 0x000, 0x000, 0x58E, 0x000, 0xE4A, 0x000, 0x000, - 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0xA46, 0xE2A, 0x9BA, 0x000, 0x000, 0xA26, 0x20A, 0xE96, 0x000, 0x000, - 0x071, 0xE56, 0xE5A, 0x000, 0xE5E, 0xE8E, 0x8BE, 0x000, 0xE62, 0x000, 0x000, 0x7A6, 0x000, 0x000, 0x000, 0x000, - 0xE66, 0xDB6, 0x000, 0x596, 0x000, 0x000, 0x000, 0x000, 0x7B2, 0x000, 0x000, 0x000, 0x816, 0xF1E, 0x000, 0x000, - 0xE6A, 0x000, 0xEBA, 0x000, 0x000, 0x000, 0x482, 0x996, 0x000, 0x41A, 0x60A, 0x882, 0x000, 0xDCA, 0x000, 0x000, - 0x000, 0x000, 0x000, 0x000, 0x88A, 0x602, 0x000, 0xDAA, 0x000, 0x48A, 0x000, 0xE86, 0x000, 0x000, 0xA12, 0x000, - 0xE6E, 0xCB6, 0xA0A, 0x000, 0x000, 0x492, 0x286, 0x000, 0x892, 0x000, 0x000, 0x000, 0x000, 0xD4A, 0x41E, 0x000, - 0xAB6, 0x035, 0x000, 0xB36, 0x000, 0xBB6, 0x612, 0xD2A, 0x000, 0xC36, 0xECE, 0x000, 0x000, 0xA02, 0x000, 0x716, - 0x000, 0xEC2, 0x000, 0x000, 0x000, 0xC4A, 0x000, 0xCAA, 0x000, 0xBCA, 0x000, 0x000, 0xACA, 0x049, 0xEAE, 0xB4A, - 0xA3E, 0xD36, 0x000, 0xBAA, 0x000, 0xB2A, 0xAAA, 0x029, 0x28E, 0x000, 0xF1A, 0x000, 0x000, 0xCCA, 0x000, 0xC2A, - 0x075, 0xED6, 0xEDA, 0x92E, 0xEDE, 0xE0E, 0x000, 0x000, 0xEE2, 0x000, 0xF12, 0x9B6, 0x942, 0x000, 0x000, 0x000, - 0xEE6, 0x000, 0x000, 0x000, 0x000, 0x000, 0x82A, 0x7A2, 0x000, 0x5AA, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, - 0xEEA, 0x000, 0xE3A, 0x000, 0x000, 0x40A, 0x61A, 0x000, 0x000, 0xA1E, 0x000, 0x000, 0x000, 0x9AA, 0x000, 0x000, - 0x836, 0x000, 0x000, 0x000, 0x000, 0x49A, 0x000, 0x9CA, 0x89A, 0x000, 0x402, 0xE06, 0x000, 0xD96, 0x000, 0x6AE, - 0xEEE, 0x61E, 0x000, 0x000, 0xF3E, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x506, 0x000, 0xA1A, 0x93A, - 0x000, 0x000, 0x49E, 0x000, 0x68E, 0x72A, 0x906, 0x000, 0x000, 0x412, 0xE4E, 0x000, 0x000, 0xD16, 0x000, 0x89E, - 0x000, 0xE42, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x90E, 0xF02, 0x686, 0x000, 0x000, 0xC96, 0xE2E, 0x000, - 0x000, 0x000, 0x50E, 0x736, 0x000, 0xC16, 0xF0A, 0x000, 0x000, 0xB96, 0x000, 0x000, 0xA96, 0x015, 0x000, 0xB16, - 0xEF2, 0xB8E, 0xD3A, 0x000, 0xA8E, 0x00D, 0x000, 0xB0E, 0x000, 0x000, 0x516, 0x000, 0x30A, 0xC0E, 0x000, 0x622, - 0x916, 0x8A2, 0x000, 0x302, 0x000, 0xC8E, 0x000, 0x392, 0x000, 0x000, 0xDCE, 0xD06, 0x4A2, 0x000, 0x000, 0x000, - 0xB3A, 0xDC2, 0x039, 0xABA, 0x000, 0xD0E, 0xBBA, 0x000, 0x000, 0x000, 0xC3A, 0xC86, 0x696, 0x000, 0xDAE, 0x000, - 0x000, 0x000, 0xCBA, 0xC06, 0xF52, 0x000, 0x000, 0x000, 0x000, 0xB06, 0xA86, 0x005, 0x000, 0x000, 0x79A, 0xB86, - 0x000, 0xD42, 0xF46, 0x000, 0x000, 0xD8E, 0x000, 0x000, 0x000, 0x000, 0xCCE, 0x000, 0x000, 0x000, 0xD2E, 0xF26, - 0x000, 0xEB6, 0xC4E, 0x000, 0x000, 0x000, 0x000, 0x000, 0xB4E, 0x79E, 0x04D, 0xACE, 0xF32, 0x000, 0xBCE, 0x000, - 0xAC2, 0x041, 0xDBA, 0xB42, 0x000, 0xBC2, 0xC2E, 0xA22, 0x000, 0xC42, 0xBAE, 0x38A, 0xB2E, 0xECA, 0x02D, 0xAAE, - 0x312, 0xCC2, 0x000, 0x000, 0x382, 0x000, 0x000, 0xEAA, 0x000, 0x000, 0xD4E, 0xD86, 0x000, 0xE16, 0xCAE, 0x000, - 0x079, 0xF56, 0xF5A, 0x000, 0xF5E, 0x000, 0x9B2, 0x000, 0xF62, 0x000, 0xE92, 0x28A, 0x000, 0x51A, 0x000, 0xA06, - 0xF66, 0x000, 0x000, 0xDA2, 0x282, 0x9A6, 0xA3A, 0x91A, 0x9C6, 0x000, 0x000, 0x000, 0x000, 0xE1E, 0x000, 0x000, - 0xF6A, 0x5A6, 0x000, 0x8C2, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x69A, 0x8AE, 0x000, 0x826, 0x000, - 0x000, 0xA0E, 0x62E, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x796, 0x000, 0x000, 0x000, 0x832, 0x000, 0x000, - 0xF6E, 0x000, 0x000, 0xCA2, 0xEBE, 0x000, 0x000, 0x88E, 0x000, 0x000, 0x48E, 0x000, 0x69E, 0x000, 0x000, 0x000, - 0x000, 0xB22, 0xAA2, 0x021, 0x000, 0xA42, 0x000, 0xBA2, 0x000, 0x60E, 0xA2E, 0xC22, 0x000, 0x000, 0x000, 0x000, - 0x8BA, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x606, 0x000, 0xE82, 0x51E, 0x000, 0x000, 0x726, 0xA4E, 0x000, - 0x91E, 0x000, 0x000, 0xD22, 0x486, 0x000, 0xE8A, 0x292, 0x000, 0x886, 0xE1A, 0x000, 0x000, 0x000, 0x732, 0x000, - 0xF72, 0x000, 0x6A2, 0x000, 0x000, 0x000, 0x000, 0x582, 0x802, 0x000, 0x000, 0x000, 0x000, 0xC9E, 0x000, 0x20E, - 0x000, 0x000, 0x000, 0x000, 0x000, 0xC1E, 0x000, 0x80A, 0x58A, 0xB9E, 0x000, 0x000, 0xA9E, 0x01D, 0x9BE, 0xB1E, - 0x000, 0x616, 0x000, 0x000, 0x522, 0x83E, 0x000, 0x000, 0x712, 0x000, 0x7AE, 0x000, 0x98A, 0x000, 0x000, 0x000, - 0x000, 0x206, 0x496, 0x982, 0xED2, 0x000, 0x000, 0x000, 0x000, 0x000, 0xD9A, 0x000, 0x000, 0xD1E, 0x922, 0x896, - 0x000, 0x78E, 0xEC6, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0xA16, 0x000, 0x702, 0x000, 0x106, 0x000, 0xEA6, - 0x992, 0xF36, 0x082, 0xE22, 0x70A, 0x000, 0x000, 0x000, 0x000, 0x000, 0xD1A, 0x000, 0xEB2, 0xD9E, 0x000, 0x000, - 0x000, 0x000, 0x000, 0x000, 0x592, 0x000, 0x7BA, 0x000, 0x000, 0x000, 0xC9A, 0x812, 0x182, 0xF4A, 0x000, 0x000, - 0x000, 0x000, 0xC1A, 0x18A, 0x000, 0x000, 0x000, 0xF2A, 0xB1A, 0x000, 0x019, 0xA9A, 0x000, 0x000, 0xB9A, 0x786, - 0xF76, 0x000, 0xC12, 0x000, 0xDBE, 0x000, 0x000, 0x000, 0xB12, 0x000, 0x011, 0xA92, 0x000, 0x6B2, 0xB92, 0x000, - 0x000, 0x000, 0x000, 0x8B6, 0x59A, 0x000, 0x000, 0x000, 0x38E, 0x000, 0xC92, 0x81A, 0x000, 0x000, 0x6A6, 0x000, - 0x99A, 0x000, 0x926, 0x000, 0x000, 0x000, 0x386, 0x000, 0x000, 0xD82, 0xD12, 0x62A, 0x000, 0x000, 0x416, 0x946, - 0x000, 0x932, 0x000, 0x000, 0xE52, 0x8AA, 0xD8A, 0x000, 0x526, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x71E, - 0xBBE, 0x306, 0xE46, 0xA2A, 0x03D, 0xABE, 0xB3E, 0x99E, 0x000, 0xD02, 0xD92, 0x000, 0xC3E, 0x000, 0x000, 0xE26, - 0x000, 0x000, 0x000, 0xEA2, 0xCBE, 0x000, 0xD0A, 0x000, 0x71A, 0xA4A, 0x000, 0x000, 0xE32, 0x000, 0x000, 0x000, - 0x000, 0xC02, 0x000, 0x000, 0xD3E, 0xA36, 0xC8A, 0x000, 0xA82, 0x001, 0x000, 0xB02, 0x000, 0xB82, 0x000, 0x000, - 0x000, 0x000, 0xB8A, 0x59E, 0xB0A, 0x000, 0x009, 0xA8A, 0x000, 0xC82, 0x000, 0x000, 0x81E, 0xF16, 0xC0A, 0x30E, - 0x000, 0x50A, 0xDC6, 0x000, 0x902, 0xF0E, 0x000, 0x316, 0x000, 0x9A2, 0xE12, 0x000, 0x000, 0x000, 0x000, 0xDA6, - 0x000, 0x000, 0x000, 0x000, 0xD52, 0x7B6, 0x000, 0x000, 0x000, 0x000, 0x000, 0x90A, 0xDB2, 0xE9E, 0x502, 0x000, - 0x000, 0x000, 0xF3A, 0x000, 0xCD2, 0x000, 0x000, 0x68A, 0x000, 0x93E, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, - 0xBD2, 0x000, 0x822, 0x7AA, 0x051, 0xAD2, 0xB52, 0x000, 0x682, 0x000, 0x000, 0xF06, 0xC52, 0x5A2, 0x000, 0x000, - 0xB46, 0x000, 0x045, 0xAC6, 0xE3E, 0x000, 0xBC6, 0xC26, 0x000, 0x000, 0xC46, 0xBA6, 0xCB2, 0xB26, 0xAA6, 0x025, - 0x000, 0x000, 0xCC6, 0x000, 0xC32, 0x000, 0x40E, 0x000, 0xBB2, 0x000, 0xF4E, 0x692, 0x031, 0xAB2, 0xB32, 0xCA6, - 0x396, 0xF42, 0xD46, 0x000, 0x000, 0x000, 0x000, 0x912, 0x406, 0xE02, 0x000, 0x000, 0x000, 0x000, 0xF2E, 0xD26, - 0x000, 0x722, 0x000, 0x000, 0xDD2, 0x000, 0xE0A, 0x000, 0x000, 0x000, 0xE9A, 0x000, 0xD32, 0x512, 0x000, 0x000, -}; - -uint32_t bch3121_encode(uint32_t message) -{ - uint32_t codeword = 0; - for (int i = 0; i < BCH3121_MESSAGE_LEN; i++) { - codeword <<= 1; - - if (message & 1) - codeword ^= BCH3121_POLYNOMIAL; - - message >>= 1; - } - - return codeword; -} - -uint32_t bch3121_decode(uint32_t *message, uint32_t codeword) -{ - uint32_t syndrome = codeword; - *message = 0; - - for (int i = 0; i < BCH3121_MESSAGE_LEN; i++) { - *message <<= 1; - - if (syndrome & 1) { - syndrome ^= BCH3121_POLYNOMIAL; - *message |= 1; - } - - syndrome >>= 1; - } - - return syndrome; -} - -int bch3121_decode_and_correct(uint32_t *message, uint32_t codeword) -{ - // Decode the codeword, immediately return the message if it contains no errors - uint32_t syndrome = bch3121_decode(message, codeword); - if (!syndrome) - return 0; - - // There is at least one error, look up the error pattern - uint16_t error_pattern = bch3121_error_positions[(uint16_t)syndrome]; - - // Check for uncorrectable errors - if (!error_pattern) - return -BCH3121_ERR_UNCORRECTABLE; - - // Extract the number of errors - uint8_t num_errors = error_pattern & 0x3; - - // Correct the first error - codeword ^= (1 << ((error_pattern >> 2) & 0x1F)); - - // Correct the second error, if present - if (num_errors == 2) - codeword ^= (1 << ((error_pattern >> 7) & 0x1F)); - - // Decode the codeword again, now that we've corrected the errors - bch3121_decode(message, codeword); - - return num_errors; -} - -void bch3121_generate_syndrome_table(uint16_t *syndrome_table) -{ - uint32_t syndrome, tmp; - uint32_t codeword = 0x0394a9d0; // Arbitrary codeword - - // Clear the table - memset(syndrome_table, 0, sizeof(uint16_t) * BCH3121_ORDER); - - // Walk through all possible error patterns - for (int i = 0; i < 31; i++) { - // Single-bit errors - syndrome = bch3121_decode(&tmp, codeword ^ (1 << i)); - syndrome_table[syndrome] = i << 2 | 1; - - // Double-bit errors - for (int j = i + 1; j < 31; j++) { - syndrome = bch3121_decode(&tmp, codeword ^ (1 << i) ^ (1 << j)); - syndrome_table[syndrome] = j << 7 | i << 2 | 2; - } - } -} \ No newline at end of file diff --git a/firmware/libwavebird/src/packet.c b/firmware/libwavebird/src/packet.c deleted file mode 100644 index e16dc14..0000000 --- a/firmware/libwavebird/src/packet.c +++ /dev/null @@ -1,167 +0,0 @@ -#include - -#include "wavebird/bch3121.h" -#include "wavebird/packet.h" - -#define PACKET_DATA_BITS 124 -#define PACKET_DATA_START 28 -#define CODEWORD_COUNT 4 -#define CRC_FINAL_XOR 0xCE98 - -// Internal state to avoid repeated allocations -static uint8_t crc_state[WAVEBIRD_MESSAGE_BYTES]; -static uint32_t codewords[CODEWORD_COUNT]; - -// CRC function pointer to allow for hardware CRC calculation -static uint16_t crc_ccitt(const uint8_t *data, size_t length); -static wavebird_packet_crc_fn_t crc_fn = crc_ccitt; - -// A "good enough" CRC-CCITT implementation for systems without hardware CRC -static uint16_t crc_ccitt(const uint8_t *data, size_t length) -{ - uint16_t crc = 0x0000; - - while (length--) { - crc ^= (uint16_t)(*data++) << 8; - - // Process each bit in the byte - for (int i = 0; i < 8; i++) { - crc = (crc & 0x8000) ? (crc << 1) ^ 0x1021 : (crc << 1); - } - } - - return crc; -} - -// Set the Nth bit in a big-endian byte array -static inline void set_bit(uint8_t *data, uint8_t length, uint8_t bit, uint8_t value) -{ - uint8_t byte_index = length - 1 - bit / 8; - uint8_t bit_mask = 1 << (bit % 8); - - if (value) { - data[byte_index] |= bit_mask; - } else { - data[byte_index] &= ~bit_mask; - } -} - -// Get the Nth bit from a big-endian byte array -static inline uint8_t get_bit(const uint8_t *data, uint8_t length, uint8_t bit) -{ - uint8_t byte_index = length - 1 - bit / 8; - uint8_t bit_mask = 1 << (bit % 8); - - return (data[byte_index] & bit_mask) ? 1 : 0; -} - -void wavebird_packet_set_crc_fn(wavebird_packet_crc_fn_t _crc_fn) -{ - crc_fn = _crc_fn; -} - -void wavebird_packet_deinterleave(uint32_t *codewords, const uint8_t *packet) -{ - // Step through each bit of the interleaved packet data - for (uint8_t i = 0; i < PACKET_DATA_BITS; i++) { - // Extract the bit from the packet - bool bit = get_bit(packet, WAVEBIRD_PACKET_BYTES, i + PACKET_DATA_START); - - // Set the bit in the appropriate codeword - if (bit) { - codewords[i % CODEWORD_COUNT] |= 1 << (i / CODEWORD_COUNT); - } else { - codewords[i % CODEWORD_COUNT] &= ~(1 << (i / CODEWORD_COUNT)); - } - } -} - -void wavebird_packet_interleave(uint8_t *packet, const uint32_t *codewords) -{ - // Step through each bit of each codeword - for (uint8_t i = 0; i < PACKET_DATA_BITS; i++) { - // Extract the bit from the codeword - bool bit = (codewords[i % CODEWORD_COUNT] >> (i / 4)) & 1; - - // Set the bit in the packet - set_bit(packet, WAVEBIRD_PACKET_BYTES, i + PACKET_DATA_START, bit); - } -} - -int wavebird_packet_decode(uint8_t *message, const uint8_t *packet) -{ - // Deinterleave the input data into 4, 31-bit codewords - wavebird_packet_deinterleave(codewords, packet); - - // Explicitly zero the first 4 bits of the message. We don't actually - // care about these, but we want the complete message to be consistent - // every time is is generated. - message[0] = 0x00; - - // Decode each codeword, and pack them into the message - for (int i = 0; i < CODEWORD_COUNT; i++) { - // Attempt to decode the codeword - uint32_t codeword; - if (bch3121_decode_and_correct(&codeword, codewords[i]) < 0) - return -WB_PACKET_ERR_DECODE_FAILED; - - // Pack the decoded codeword into the message - for (int j = 0; j < BCH3121_MESSAGE_LEN; j++) { - // Extract bit from codeword - uint8_t bit = codeword & 1; - - // Set the bit in message - set_bit(message, WAVEBIRD_MESSAGE_BYTES, i * BCH3121_MESSAGE_LEN + j, bit); - - // Set the bit in the CRC state (transposed) - set_bit(crc_state, WAVEBIRD_MESSAGE_BYTES, j * CODEWORD_COUNT + i, bit); - - // Shift the codeword right - codeword >>= 1; - } - } - - // Extract the expected CRC from the packet, and calculate the actual CRC - uint16_t expected_crc = wavebird_packet_get_crc(packet); - uint16_t actual_crc = crc_fn(crc_state, WAVEBIRD_MESSAGE_BYTES) ^ CRC_FINAL_XOR; - - // Return error code if CRCs do not match - if (expected_crc != actual_crc) - return -WB_PACKET_ERR_CRC_MISMATCH; - - return 0; -} - -void wavebird_packet_encode(uint8_t *packet, const uint8_t *message) -{ - // Construct and encode the codewords - for (int i = 0; i < CODEWORD_COUNT; i++) { - for (int j = 0; j < BCH3121_MESSAGE_LEN; j++) { - // Extract the bit from the message - uint8_t bit = get_bit(message, WAVEBIRD_MESSAGE_BYTES, i * BCH3121_MESSAGE_LEN + j); - - // Set the bit in the codeword - if (bit) { - codewords[i] |= (1 << j); - } else { - codewords[i] &= ~(1 << j); - } - - // Set the bit in the CRC state (transposed) - set_bit(crc_state, WAVEBIRD_MESSAGE_BYTES, j * CODEWORD_COUNT + i, bit); - } - - // Encode into a BCH(31,21) codeword - codewords[i] = bch3121_encode(codewords[i]); - } - - // Interleave the codewords - wavebird_packet_interleave(packet, codewords); - - // Calculate and set the CRC - uint16_t crc = crc_fn(crc_state, WAVEBIRD_MESSAGE_BYTES) ^ CRC_FINAL_XOR; - wavebird_packet_set_crc(packet, crc); - - // Set the footer - wavebird_packet_set_footer(packet, 0x000); -} \ No newline at end of file diff --git a/firmware/libwavebird/src/platform/efr32/efr32xg1/rail_config.c b/firmware/libwavebird/src/platform/efr32/efr32xg1/rail_config.c deleted file mode 100644 index 171b677..0000000 --- a/firmware/libwavebird/src/platform/efr32/efr32xg1/rail_config.c +++ /dev/null @@ -1,203 +0,0 @@ -/***************************************************************************//** - * @brief RAIL Configuration - * @details - * WARNING: Auto-Generated Radio Config - DO NOT EDIT - * Radio Configurator Version: 2304.5.2 - * RAIL Adapter Version: 2.4.33 - * RAIL Compatibility: 2.x - ******************************************************************************* - * # License - * Copyright 2019 Silicon Laboratories Inc. www.silabs.com - ******************************************************************************* - * - * SPDX-License-Identifier: Zlib - * - * The licensor of this software is Silicon Laboratories Inc. - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - ******************************************************************************/ -#include "em_device.h" -#include "rail_config.h" - -uint32_t RAILCb_CalcSymbolRate(RAIL_Handle_t railHandle) -{ - (void) railHandle; - return 0U; -} - -uint32_t RAILCb_CalcBitRate(RAIL_Handle_t railHandle) -{ - (void) railHandle; - return 0U; -} - -void RAILCb_ConfigFrameTypeLength(RAIL_Handle_t railHandle, - const RAIL_FrameType_t *frameType) -{ - (void) railHandle; - (void) frameType; -} - -static const uint8_t irCalConfig[] = { - 25, 63, 1, 6, 4, 16, 1, 0, 0, 1, 1, 6, 0, 16, 39, 0, 0, 5, 0, 1, 1, 0, 0, 0, 0, 0 -}; - -static const int32_t timingConfig[] = { - 0, 0, 0 -}; - -static RAIL_ChannelConfigEntryAttr_t channelConfigEntryAttr = { -#if RAIL_SUPPORTS_OFDM_PA - { - { 0xFFFFFFFFUL }, - { 0xFFFFFFFFUL, 0xFFFFFFFFUL } - } -#else // RAIL_SUPPORTS_OFDM_PA - { 0xFFFFFFFFUL }, -#endif // RAIL_SUPPORTS_OFDM_PA -}; - -static const uint32_t phyInfo[] = { - 16UL, - 0x00666666UL, // 102.4 - (uint32_t) NULL, - (uint32_t) irCalConfig, - (uint32_t) timingConfig, - 0x00000000UL, - 9760000UL, - 38400000UL, - 1440000UL, - 0x00000F01UL, - 0x02004924UL, - (uint32_t) NULL, - (uint32_t) NULL, - (uint32_t) NULL, - 0UL, - 0UL, - 1440000UL, - (uint32_t) NULL, - (uint32_t) NULL, - (uint32_t) NULL, -}; - -const uint32_t WaveBird_modemConfigBase[] = { - 0x01040FF0UL, (uint32_t) &phyInfo, - /* 0FF4 */ 0x00000000UL, - /* 0FF8 */ 0x0003C000UL, - /* 0FFC */ 0x0003C00FUL, - 0x00020004UL, 0x00000000UL, - /* 0008 */ 0x00000000UL, - 0x00020018UL, 0x00000012UL, - /* 001C */ 0x00000000UL, - 0x00070028UL, 0x00000000UL, - /* 002C */ 0x00000000UL, - /* 0030 */ 0x00000000UL, - /* 0034 */ 0x00000000UL, - /* 0038 */ 0x00000000UL, - /* 003C */ 0x00000000UL, - /* 0040 */ 0x00000704UL, - 0x00010048UL, 0x00000000UL, - 0x00020054UL, 0x00000000UL, - /* 0058 */ 0x00000000UL, - 0x000400A0UL, 0x000040FFUL, - /* 00A4 */ 0x00000000UL, - /* 00A8 */ 0x000041FFUL, - /* 00AC */ 0x00000000UL, - 0x00012000UL, 0x00000700UL, - 0x00012010UL, 0x00000000UL, - 0x00012018UL, 0x00000000UL, - 0x00013008UL, 0x0000AC3FUL, - 0x00023030UL, 0x00104924UL, - /* 3034 */ 0x00000001UL, - 0x00013040UL, 0x00000000UL, - 0x000140A0UL, 0x0F00277AUL, - 0x000140F4UL, 0x00001020UL, - 0x00024134UL, 0x00000880UL, - /* 4138 */ 0x000087E6UL, - 0x00024140UL, 0x0088006DUL, - /* 4144 */ 0x1153E6C0UL, - 0x00156014UL, 0x00000010UL, - /* 6018 */ 0x00087020UL, - /* 601C */ 0x0000000BUL, - /* 6020 */ 0x00003000UL, - /* 6024 */ 0x000A0000UL, - /* 6028 */ 0x03000000UL, - /* 602C */ 0x00000000UL, - /* 6030 */ 0x00FF0352UL, - /* 6034 */ 0x00000C61UL, - /* 6038 */ 0x00000001UL, - /* 603C */ 0x00320411UL, - /* 6040 */ 0x000002C4UL, - /* 6044 */ 0x00000000UL, - /* 6048 */ 0x13C0011DUL, - /* 604C */ 0x0000164FUL, - /* 6050 */ 0x002A03D0UL, - /* 6054 */ 0x0094905AUL, - /* 6058 */ 0x00000001UL, - /* 605C */ 0x00000000UL, - /* 6060 */ 0x00000000UL, - /* 6064 */ 0x00000000UL, - 0x10017014UL, 0x0007F800UL, - 0x30017014UL, 0x000000F8UL, - 0x10017018UL, 0x000000FFUL, - 0x30017018UL, 0x00000300UL, - 0x0001701CUL, 0x82730060UL, - 0x00017028UL, 0x01800000UL, - 0x00027048UL, 0x00003D3CUL, - /* 704C */ 0x000019BCUL, - 0x00037070UL, 0x00020105UL, - /* 7074 */ 0x00000433UL, - /* 7078 */ 0x00552300UL, - 0xFFFFFFFFUL, -}; - -const RAIL_ChannelConfigEntry_t WaveBird_channels[] = { - { - .phyConfigDeltaAdd = NULL, - .baseFrequency = 2404800000, - .channelSpacing = 2400000, - .physicalChannelOffset = 0, - .channelNumberStart = 0, - .channelNumberEnd = 31, - .maxPower = RAIL_TX_POWER_MAX, - .attr = &channelConfigEntryAttr, -#ifdef RADIO_CONFIG_ENABLE_CONC_PHY - .entryType = 0, -#endif -#ifdef RADIO_CONFIG_ENABLE_STACK_INFO - .stackInfo = NULL, -#endif - .alternatePhy = NULL, - }, -}; - -const RAIL_ChannelConfig_t WaveBird_channelConfig = { - .phyConfigBase = WaveBird_modemConfigBase, - .phyConfigDeltaSubtract = NULL, - .configs = WaveBird_channels, - .length = 1U, - .signature = 0UL, - .xtalFrequencyHz = 38400000UL, -}; - -const RAIL_ChannelConfig_t *channelConfigs[] = { - &WaveBird_channelConfig, - NULL -}; - -uint32_t wavebirdAccelerationBuffer[135]; diff --git a/firmware/libwavebird/src/platform/efr32/efr32xg1/rail_config.h b/firmware/libwavebird/src/platform/efr32/efr32xg1/rail_config.h deleted file mode 100644 index bb52beb..0000000 --- a/firmware/libwavebird/src/platform/efr32/efr32xg1/rail_config.h +++ /dev/null @@ -1,49 +0,0 @@ -/***************************************************************************//** - * @brief RAIL Configuration - * @details - * WARNING: Auto-Generated Radio Config Header - DO NOT EDIT - * Radio Configurator Version: 2304.5.2 - * RAIL Adapter Version: 2.4.33 - * RAIL Compatibility: 2.x - ******************************************************************************* - * # License - * Copyright 2019 Silicon Laboratories Inc. www.silabs.com - ******************************************************************************* - * - * SPDX-License-Identifier: Zlib - * - * The licensor of this software is Silicon Laboratories Inc. - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - ******************************************************************************/ - -#ifndef __RAIL_CONFIG_H__ -#define __RAIL_CONFIG_H__ - -#include -#include "rail_types.h" - -#define WAVEBIRD_ACCELERATION_BUFFER wavebirdAccelerationBuffer -extern uint32_t wavebirdAccelerationBuffer[]; - -#define RADIO_CONFIG_XTAL_FREQUENCY 38400000UL - -#define RAIL0_CHANNELS_PROFILE_BASE -extern const RAIL_ChannelConfig_t *channelConfigs[]; - -#endif // __RAIL_CONFIG_H__ diff --git a/firmware/libwavebird/src/platform/efr32/efr32xg14/rail_config.c b/firmware/libwavebird/src/platform/efr32/efr32xg14/rail_config.c deleted file mode 100644 index acbfc1b..0000000 --- a/firmware/libwavebird/src/platform/efr32/efr32xg14/rail_config.c +++ /dev/null @@ -1,240 +0,0 @@ -/***************************************************************************//** - * @brief RAIL Configuration - * @details - * WARNING: Auto-Generated Radio Config - DO NOT EDIT - * Radio Configurator Version: 2304.5.2 - * RAIL Adapter Version: 2.4.33 - * RAIL Compatibility: 2.x - ******************************************************************************* - * # License - * Copyright 2019 Silicon Laboratories Inc. www.silabs.com - ******************************************************************************* - * - * SPDX-License-Identifier: Zlib - * - * The licensor of this software is Silicon Laboratories Inc. - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - ******************************************************************************/ -#include "em_device.h" -#include "rail_config.h" - -uint32_t RAILCb_CalcSymbolRate(RAIL_Handle_t railHandle) -{ - (void) railHandle; - return 0U; -} - -uint32_t RAILCb_CalcBitRate(RAIL_Handle_t railHandle) -{ - (void) railHandle; - return 0U; -} - -void RAILCb_ConfigFrameTypeLength(RAIL_Handle_t railHandle, - const RAIL_FrameType_t *frameType) -{ - (void) railHandle; - (void) frameType; -} - -static const uint8_t irCalConfig[] = { - 25, 63, 1, 6, 4, 16, 1, 0, 0, 1, 1, 6, 0, 16, 39, 0, 0, 5, 0, 1, 1, 0, 0, 0, 0, 0 -}; - -static const int32_t timingConfig[] = { - 0, 0, 0 -}; - -static RAIL_ChannelConfigEntryAttr_t channelConfigEntryAttr = { -#if RAIL_SUPPORTS_OFDM_PA - { - { 0xFFFFFFFFUL }, - { 0xFFFFFFFFUL, 0xFFFFFFFFUL } - } -#else // RAIL_SUPPORTS_OFDM_PA - { 0xFFFFFFFFUL }, -#endif // RAIL_SUPPORTS_OFDM_PA -}; - -static const uint32_t phyInfo[] = { - 16UL, - 0x0071C71CUL, // 113.77777777777779 - (uint32_t) NULL, - (uint32_t) irCalConfig, - (uint32_t) timingConfig, - 0x00000000UL, - 7320000UL, - 34560000UL, - 1440000UL, - 0x00000F01UL, - 0x02004C44UL, - (uint32_t) NULL, - (uint32_t) NULL, - (uint32_t) NULL, - 0UL, - 0UL, - 1439718UL, - (uint32_t) NULL, - (uint32_t) NULL, - (uint32_t) NULL, -}; - -const uint32_t WaveBird_modemConfigBase[] = { - 0x01041FF0UL, 0x0037003FUL, - /* 1FF4 */ 0x00000000UL, - /* 1FF8 */ (uint32_t) &phyInfo, - /* 1FFC */ 0x00000000UL, - 0x00020004UL, 0x00000000UL, - /* 0008 */ 0x00000000UL, - 0x00020018UL, 0x00000012UL, - /* 001C */ 0x00000000UL, - 0x00070028UL, 0x00000000UL, - /* 002C */ 0x00000000UL, - /* 0030 */ 0x00000000UL, - /* 0034 */ 0x00000000UL, - /* 0038 */ 0x00000000UL, - /* 003C */ 0x00000000UL, - /* 0040 */ 0x00000704UL, - 0x00010048UL, 0x00000000UL, - 0x00020054UL, 0x00000000UL, - /* 0058 */ 0x00000000UL, - 0x000400A0UL, 0x000040FFUL, - /* 00A4 */ 0x00000000UL, - /* 00A8 */ 0x000041FFUL, - /* 00AC */ 0x00000000UL, - 0x00012000UL, 0x00000700UL, - 0x00012010UL, 0x00000000UL, - 0x00012018UL, 0x00000000UL, - 0x00013008UL, 0x0100AC13UL, - 0x00023030UL, 0x00104C44UL, - /* 3034 */ 0x00000001UL, - 0x00013040UL, 0x00000000UL, - 0x000140A0UL, 0x0F00277AUL, - 0x000140B8UL, 0x00F3C000UL, - 0x000140F4UL, 0x00001020UL, - 0x00024134UL, 0x00000880UL, - /* 4138 */ 0x000087E6UL, - 0x00024140UL, 0x0088006DUL, - /* 4144 */ 0x4D52E6C0UL, - 0x00044160UL, 0x00000000UL, - /* 4164 */ 0x00000000UL, - /* 4168 */ 0x00000006UL, - /* 416C */ 0x00000006UL, - 0x00086014UL, 0x00000010UL, - /* 6018 */ 0x00087020UL, - /* 601C */ 0x0000000FUL, - /* 6020 */ 0x00006000UL, - /* 6024 */ 0x000A0000UL, - /* 6028 */ 0x03000000UL, - /* 602C */ 0x00000000UL, - /* 6030 */ 0x00000000UL, - 0x00066050UL, 0x00FF0352UL, - /* 6054 */ 0x00001040UL, - /* 6058 */ 0x00000000UL, - /* 605C */ 0x00D20412UL, - /* 6060 */ 0x00002C48UL, - /* 6064 */ 0x00000000UL, - 0x000C6078UL, 0x11E00100UL, - /* 607C */ 0x0000164FUL, - /* 6080 */ 0x003203D0UL, - /* 6084 */ 0x00000000UL, - /* 6088 */ 0x00000000UL, - /* 608C */ 0x00000000UL, - /* 6090 */ 0x00000000UL, - /* 6094 */ 0x00000000UL, - /* 6098 */ 0x00000000UL, - /* 609C */ 0x00000000UL, - /* 60A0 */ 0x00000000UL, - /* 60A4 */ 0x00000000UL, - 0x000760E4UL, 0xCC720080UL, - /* 60E8 */ 0x00000000UL, - /* 60EC */ 0x07830464UL, - /* 60F0 */ 0x3AC81388UL, - /* 60F4 */ 0x000A209CUL, - /* 60F8 */ 0x00206100UL, - /* 60FC */ 0x123556B7UL, - 0x00036104UL, 0x0011C997UL, - /* 6108 */ 0x29043020UL, - /* 610C */ 0x0040BB88UL, - 0x00016120UL, 0x00000000UL, - 0x00016128UL, 0x0C660664UL, - 0x000C6130UL, 0x00FA53E8UL, - /* 6134 */ 0x00000000UL, - /* 6138 */ 0x00000000UL, - /* 613C */ 0x00000000UL, - /* 6140 */ 0x00000000UL, - /* 6144 */ 0x00000000UL, - /* 6148 */ 0x00000000UL, - /* 614C */ 0x00000001UL, - /* 6150 */ 0x00000000UL, - /* 6154 */ 0x00000000UL, - /* 6158 */ 0x00000000UL, - /* 615C */ 0x00000000UL, - 0x10017014UL, 0x0007F800UL, - 0x30017014UL, 0x000000FEUL, - 0x10017018UL, 0x000000FFUL, - 0x30017018UL, 0x00001300UL, - 0x0005701CUL, 0x862A0060UL, - /* 7020 */ 0x00000000UL, - /* 7024 */ 0x00000082UL, - /* 7028 */ 0x01800000UL, - /* 702C */ 0x000000D5UL, - 0x00027048UL, 0x00003D3CUL, - /* 704C */ 0x000019BCUL, - 0x00037070UL, 0x00220103UL, - /* 7074 */ 0x0008300AUL, - /* 7078 */ 0x00552300UL, - 0xFFFFFFFFUL, -}; - -const RAIL_ChannelConfigEntry_t WaveBird_channels[] = { - { - .phyConfigDeltaAdd = NULL, - .baseFrequency = 2404800000, - .channelSpacing = 2400000, - .physicalChannelOffset = 0, - .channelNumberStart = 0, - .channelNumberEnd = 31, - .maxPower = RAIL_TX_POWER_MAX, - .attr = &channelConfigEntryAttr, -#ifdef RADIO_CONFIG_ENABLE_CONC_PHY - .entryType = 0, -#endif -#ifdef RADIO_CONFIG_ENABLE_STACK_INFO - .stackInfo = NULL, -#endif - .alternatePhy = NULL, - }, -}; - -const RAIL_ChannelConfig_t WaveBird_channelConfig = { - .phyConfigBase = WaveBird_modemConfigBase, - .phyConfigDeltaSubtract = NULL, - .configs = WaveBird_channels, - .length = 1U, - .signature = 0UL, - .xtalFrequencyHz = 38400000UL, -}; - -const RAIL_ChannelConfig_t *channelConfigs[] = { - &WaveBird_channelConfig, - NULL -}; - -uint32_t wavebirdAccelerationBuffer[209]; diff --git a/firmware/libwavebird/src/platform/efr32/efr32xg14/rail_config.h b/firmware/libwavebird/src/platform/efr32/efr32xg14/rail_config.h deleted file mode 100644 index bb52beb..0000000 --- a/firmware/libwavebird/src/platform/efr32/efr32xg14/rail_config.h +++ /dev/null @@ -1,49 +0,0 @@ -/***************************************************************************//** - * @brief RAIL Configuration - * @details - * WARNING: Auto-Generated Radio Config Header - DO NOT EDIT - * Radio Configurator Version: 2304.5.2 - * RAIL Adapter Version: 2.4.33 - * RAIL Compatibility: 2.x - ******************************************************************************* - * # License - * Copyright 2019 Silicon Laboratories Inc. www.silabs.com - ******************************************************************************* - * - * SPDX-License-Identifier: Zlib - * - * The licensor of this software is Silicon Laboratories Inc. - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - ******************************************************************************/ - -#ifndef __RAIL_CONFIG_H__ -#define __RAIL_CONFIG_H__ - -#include -#include "rail_types.h" - -#define WAVEBIRD_ACCELERATION_BUFFER wavebirdAccelerationBuffer -extern uint32_t wavebirdAccelerationBuffer[]; - -#define RADIO_CONFIG_XTAL_FREQUENCY 38400000UL - -#define RAIL0_CHANNELS_PROFILE_BASE -extern const RAIL_ChannelConfig_t *channelConfigs[]; - -#endif // __RAIL_CONFIG_H__ diff --git a/firmware/libwavebird/src/platform/efr32/efr32xg22/rail_config.c b/firmware/libwavebird/src/platform/efr32/efr32xg22/rail_config.c deleted file mode 100644 index a52cea0..0000000 --- a/firmware/libwavebird/src/platform/efr32/efr32xg22/rail_config.c +++ /dev/null @@ -1,292 +0,0 @@ -/***************************************************************************//** - * @brief RAIL Configuration - * @details - * WARNING: Auto-Generated Radio Config - DO NOT EDIT - * Radio Configurator Version: 2304.5.2 - * RAIL Adapter Version: 2.4.33 - * RAIL Compatibility: 2.x - ******************************************************************************* - * # License - * Copyright 2019 Silicon Laboratories Inc. www.silabs.com - ******************************************************************************* - * - * SPDX-License-Identifier: Zlib - * - * The licensor of this software is Silicon Laboratories Inc. - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - ******************************************************************************/ -#include "em_device.h" -#include "rail_config.h" - -uint32_t RAILCb_CalcSymbolRate(RAIL_Handle_t railHandle) -{ - (void) railHandle; - return 0U; -} - -uint32_t RAILCb_CalcBitRate(RAIL_Handle_t railHandle) -{ - (void) railHandle; - return 0U; -} - -void RAILCb_ConfigFrameTypeLength(RAIL_Handle_t railHandle, - const RAIL_FrameType_t *frameType) -{ - (void) railHandle; - (void) frameType; -} - -static const uint8_t irCalConfig[] = { - 25, 63, 1, 6, 4, 16, 1, 0, 0, 1, 1, 6, 0, 16, 39, 0, 0, 5, 0, 1, 1, 0, 0, 0, 0, 0 -}; - -static const int32_t timingConfig[] = { - 0, 0, 0 -}; - -static const uint8_t hfxoRetimingConfigEntries[] = { - 2, 0, 0, 0, 0x00, 0xf0, 0x49, 0x02, 6, 20, 0, 0, 0x00, 0xe0, 0x93, 0x04, 5, 56, 0, 0, 0xa0, 0x08, 0, 0, 0, 0, 0x58, 0x09, 1, 4, 7, 6, 0x10, 0x0a, 1, 4, 7, 7, 0xc8, 0x0a, 0, 4, 8, 7, 0x80, 0x0b, 0, 4, 8, 8, 0x38, 0x0c, 0, 4, 9, 8, 0x61, 0x08, 0, 0, 0, 0, 0x68, 0x08, 0, 0, 0, 0, 0xc7, 0x09, 1, 4, 4, 3, 0x2c, 0x0b, 1, 4, 4, 4, 0x92, 0x0c, 1, 4, 5, 4 -}; - -static RAIL_ChannelConfigEntryAttr_t channelConfigEntryAttr = { -#if RAIL_SUPPORTS_OFDM_PA - { - { 0xFFFFFFFFUL }, - { 0xFFFFFFFFUL, 0xFFFFFFFFUL } - } -#else // RAIL_SUPPORTS_OFDM_PA - { 0xFFFFFFFFUL }, -#endif // RAIL_SUPPORTS_OFDM_PA -}; - -static const uint32_t phyInfo[] = { - 16UL, - 0x00888888UL, // 136.53333333333333 - (uint32_t) NULL, - (uint32_t) irCalConfig, - (uint32_t) timingConfig, - 0x00000000UL, - 10800000UL, - 34560000UL, - 1440000UL, - 0x00F50F01UL, - 0x02103A3DUL, - (uint32_t) NULL, - (uint32_t) hfxoRetimingConfigEntries, - (uint32_t) NULL, - 0UL, - 0UL, - 1440010UL, - (uint32_t) NULL, - (uint32_t) NULL, - (uint32_t) NULL, -}; - -const uint32_t WaveBird_modemConfigBase[] = { - 0x0002400CUL, 0x00000000UL, - /* 4010 */ 0x00004000UL, - 0x00024020UL, 0x00000012UL, - /* 4024 */ 0x00000000UL, - 0x00074030UL, 0x00000000UL, - /* 4034 */ 0x00000000UL, - /* 4038 */ 0x00000000UL, - /* 403C */ 0x00000000UL, - /* 4040 */ 0x00000000UL, - /* 4044 */ 0x00004000UL, - /* 4048 */ 0x00040704UL, - 0x00014050UL, 0x00000000UL, - 0x0002405CUL, 0x00000000UL, - /* 4060 */ 0x00000000UL, - 0x000140A8UL, 0x00000000UL, - 0x000440BCUL, 0x00000000UL, - /* 40C0 */ 0x00000000UL, - /* 40C4 */ 0x00000000UL, - /* 40C8 */ 0x00000000UL, - 0x00044104UL, 0x000040FFUL, - /* 4108 */ 0x00000000UL, - /* 410C */ 0x000041FFUL, - /* 4110 */ 0x00000000UL, - 0x1001C020UL, 0x0007F800UL, - 0x3001C020UL, 0x000802F5UL, - 0x1001C024UL, 0x000000FFUL, - 0x3001C024UL, 0x00001300UL, - 0x0007C028UL, 0x83B380ECUL, - /* C02C */ 0x51407543UL, - /* C030 */ 0x48000FA0UL, - /* C034 */ 0x00004030UL, - /* C038 */ 0x00000000UL, - /* C03C */ 0x00000000UL, - /* C040 */ 0x0000022EUL, - 0x0004C050UL, 0x04301151UL, - /* C054 */ 0xE6092D0EUL, - /* C058 */ 0x08070654UL, - /* C05C */ 0x0002B6D1UL, - 0x000AC064UL, 0x1C003004UL, - /* C068 */ 0x09183040UL, - /* C06C */ 0x2079640DUL, - /* C070 */ 0x01FBFCEBUL, - /* C074 */ 0x03E8F67FUL, - /* C078 */ 0x15724BBDUL, - /* C07C */ 0x0518A311UL, - /* C080 */ 0x76543210UL, - /* C084 */ 0x00000A98UL, - /* C088 */ 0x00000000UL, - 0x01010008UL, 0x00000700UL, - 0x01010018UL, 0x00000000UL, - 0x01010020UL, 0x00000000UL, - 0x0108401CUL, 0x00000010UL, - /* 4020 */ 0x00087020UL, - /* 4024 */ 0x0001000BUL, - /* 4028 */ 0x00002000UL, - /* 402C */ 0x000A0000UL, - /* 4030 */ 0x03000000UL, - /* 4034 */ 0x00000000UL, - /* 4038 */ 0x00000000UL, - 0x01064058UL, 0x00FF0352UL, - /* 405C */ 0x00000840UL, - /* 4060 */ 0x00000008UL, - /* 4064 */ 0x00320411UL, - /* 4068 */ 0x000002C4UL, - /* 406C */ 0x00000000UL, - 0x01114080UL, 0x11E00106UL, - /* 4084 */ 0x0000164FUL, - /* 4088 */ 0x002B03D3UL, - /* 408C */ 0x00000000UL, - /* 4090 */ 0x00000000UL, - /* 4094 */ 0x00000000UL, - /* 4098 */ 0x00000000UL, - /* 409C */ 0x00000000UL, - /* 40A0 */ 0x00000000UL, - /* 40A4 */ 0x00000000UL, - /* 40A8 */ 0x00000000UL, - /* 40AC */ 0x00000000UL, - /* 40B0 */ 0x00000000UL, - /* 40B4 */ 0x00000000UL, - /* 40B8 */ 0x00000000UL, - /* 40BC */ 0x00000000UL, - /* 40C0 */ 0x00000000UL, - 0x010240E0UL, 0x00000033UL, - /* 40E4 */ 0x00000000UL, - 0x010140ECUL, 0x8C74987DUL, - 0x010540F4UL, 0x07830464UL, - /* 40F8 */ 0x3AC81388UL, - /* 40FC */ 0x000A209CUL, - /* 4100 */ 0x00206100UL, - /* 4104 */ 0x123556B7UL, - 0x0103410CUL, 0x001164F0UL, - /* 4110 */ 0x29043020UL, - /* 4114 */ 0x4040BB88UL, - 0x01024124UL, 0x00000000UL, - /* 4128 */ 0x00000000UL, - 0x010A4130UL, 0x0C660664UL, - /* 4134 */ 0x0000010CUL, - /* 4138 */ 0x00FA53E8UL, - /* 413C */ 0x00000000UL, - /* 4140 */ 0x00000000UL, - /* 4144 */ 0x00000000UL, - /* 4148 */ 0x00000000UL, - /* 414C */ 0x00000000UL, - /* 4150 */ 0x00000000UL, - /* 4154 */ 0x00000101UL, - 0x01034168UL, 0x07830464UL, - /* 416C */ 0x00821388UL, - /* 4170 */ 0x00000000UL, - 0x01044230UL, 0x00000000UL, - /* 4234 */ 0x0E000000UL, - /* 4238 */ 0x00000000UL, - /* 423C */ 0x00000000UL, - 0x01024244UL, 0x00000000UL, - /* 4248 */ 0x001F81F4UL, - 0x010C4254UL, 0x00000000UL, - /* 4258 */ 0x003C0000UL, - /* 425C */ 0x00000000UL, - /* 4260 */ 0x00000000UL, - /* 4264 */ 0x55555555UL, - /* 4268 */ 0x00000017UL, - /* 426C */ 0x00000000UL, - /* 4270 */ 0x00000000UL, - /* 4274 */ 0x0006AAAAUL, - /* 4278 */ 0x00000000UL, - /* 427C */ 0x00000000UL, - /* 4280 */ 0x00000000UL, - 0x01018010UL, 0x00000003UL, - 0x01028038UL, 0x00103A3DUL, - /* 803C */ 0x00000001UL, - 0x0103809CUL, 0x00000000UL, - /* 80A0 */ 0x0003B870UL, - /* 80A4 */ 0x0002C0FFUL, - 0x110180A8UL, 0x000001F0UL, - 0x310180A8UL, 0x01014205UL, - 0x110180ACUL, 0x000001F0UL, - 0x310180ACUL, 0x000D0A05UL, - 0x010280B0UL, 0x02000300UL, - /* 80B4 */ 0x01000037UL, - 0x02020098UL, 0x04000C00UL, - /* 009C */ 0x0000004CUL, - 0x020100A4UL, 0x00000400UL, - 0x020200D0UL, 0xAA400005UL, - /* 00D4 */ 0x00000188UL, - 0x020100E4UL, 0x11512C6CUL, - 0x020200F4UL, 0x00000000UL, - /* 00F8 */ 0x1108213DUL, - 0x120100FCUL, 0x0000003FUL, - 0x320100FCUL, 0x00045400UL, - 0x02010130UL, 0x02510060UL, - 0x02010154UL, 0x00003FC4UL, - 0x02010168UL, 0x00000400UL, - 0x03014FFCUL, (uint32_t) &phyInfo, - 0xFFFFFFFFUL, -}; - -const RAIL_ChannelConfigEntry_t WaveBird_channels[] = { - { - .phyConfigDeltaAdd = NULL, - .baseFrequency = 2404800000, - .channelSpacing = 2400000, - .physicalChannelOffset = 0, - .channelNumberStart = 0, - .channelNumberEnd = 31, - .maxPower = RAIL_TX_POWER_MAX, - .attr = &channelConfigEntryAttr, -#ifdef RADIO_CONFIG_ENABLE_CONC_PHY - .entryType = 0, -#endif -#ifdef RADIO_CONFIG_ENABLE_STACK_INFO - .stackInfo = NULL, -#endif - .alternatePhy = NULL, - }, -}; - -const RAIL_ChannelConfig_t WaveBird_channelConfig = { - .phyConfigBase = WaveBird_modemConfigBase, - .phyConfigDeltaSubtract = NULL, - .configs = WaveBird_channels, - .length = 1U, - .signature = 0UL, - .xtalFrequencyHz = 38400000UL, -}; - -const RAIL_ChannelConfig_t *channelConfigs[] = { - &WaveBird_channelConfig, - NULL -}; - -uint32_t wavebirdAccelerationBuffer[305]; diff --git a/firmware/libwavebird/src/platform/efr32/efr32xg22/rail_config.h b/firmware/libwavebird/src/platform/efr32/efr32xg22/rail_config.h deleted file mode 100644 index bb52beb..0000000 --- a/firmware/libwavebird/src/platform/efr32/efr32xg22/rail_config.h +++ /dev/null @@ -1,49 +0,0 @@ -/***************************************************************************//** - * @brief RAIL Configuration - * @details - * WARNING: Auto-Generated Radio Config Header - DO NOT EDIT - * Radio Configurator Version: 2304.5.2 - * RAIL Adapter Version: 2.4.33 - * RAIL Compatibility: 2.x - ******************************************************************************* - * # License - * Copyright 2019 Silicon Laboratories Inc. www.silabs.com - ******************************************************************************* - * - * SPDX-License-Identifier: Zlib - * - * The licensor of this software is Silicon Laboratories Inc. - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - ******************************************************************************/ - -#ifndef __RAIL_CONFIG_H__ -#define __RAIL_CONFIG_H__ - -#include -#include "rail_types.h" - -#define WAVEBIRD_ACCELERATION_BUFFER wavebirdAccelerationBuffer -extern uint32_t wavebirdAccelerationBuffer[]; - -#define RADIO_CONFIG_XTAL_FREQUENCY 38400000UL - -#define RAIL0_CHANNELS_PROFILE_BASE -extern const RAIL_ChannelConfig_t *channelConfigs[]; - -#endif // __RAIL_CONFIG_H__ diff --git a/firmware/libwavebird/src/platform/efr32/radio_efr32.c b/firmware/libwavebird/src/platform/efr32/radio_efr32.c deleted file mode 100644 index 71fb9d6..0000000 --- a/firmware/libwavebird/src/platform/efr32/radio_efr32.c +++ /dev/null @@ -1,322 +0,0 @@ -/** - * WaveBird radio implementation for EFR32 radios. - */ - -#include "rail.h" -#include "rail_config.h" - -#include "wavebird/packet.h" -#include "wavebird/radio.h" - -// Radio states -enum { - WB_RADIO_IDLE, - WB_RADIO_RX_PAIRING_SCANNING, - WB_RADIO_RX_PAIRING_QUALIFYING, - WB_RADIO_RX_ACTIVE, -}; - -// Mapping from WaveBird channel number to channel index -// Assumes a starting frequency of 2404.80 Mhz, with channel spacing of 2.4 Mhz -// The channel map is 0-indexed, WaveBird channels on the channel dial are 1-indexed -static const uint8_t WAVEBIRD_CHANNEL_MAP[] = { - 31, 29, 0, 2, 6, 4, 8, 10, 14, 12, 17, 19, 23, 21, 25, 27, -}; - -// Interrupt status flags -static volatile bool packet_held = false; -static volatile bool sync_word_detected = false; -static volatile int error_code = 0; - -// Current radio state -static uint8_t radio_state = WB_RADIO_IDLE; -static uint8_t current_channel = 0; - -// Callback functions -static wavebird_radio_packet_fn_t packet_callback = NULL; -static wavebird_radio_error_fn_t error_callback = NULL; -static wavebird_radio_pairing_started_fn_t pairing_started_callback = NULL; -static wavebird_radio_pairing_finished_fn_t pairing_finished_callback = NULL; - -// RAIL handle and RX buffer -static RAIL_Handle_t rail_handle; -static __ALIGNED(RAIL_FIFO_ALIGNMENT) uint8_t packet_buffer[WAVEBIRD_PACKET_BYTES]; - -// Pairing timeouts -#define PAIRING_TIMEOUT 30000000 // Timeout entire pairing process after 30 seconds -#define PAIRING_DETECT_TIMEOUT 10000 // Listen for sync words for 10ms on each channel -#define PAIRING_QUALIFY_TIMEOUT 200000 // Hold on the channel for 200ms to qualify activity - -// Pairing configuration -static wavebird_radio_qualify_fn_t qualify_fn = NULL; -static uint8_t qualify_threshold = 5; - -// Pairing state -static struct pairing_state { - bool first_scan; - uint8_t channel; - uint32_t timeout; - uint32_t detect_timeout; - uint32_t qualify_timeout; - uint8_t qualified_packets; -} pairing_state; - -// Interrupt handler for RAIL events -static void handle_rail_event(RAIL_Handle_t handle, RAIL_Events_t events) -{ - // Handle RX events - if (events & RAIL_EVENTS_RX_COMPLETION) { - if (events & RAIL_EVENT_RX_PACKET_RECEIVED) { - // When in active RX mode, or qualifying a channel for pairing, hold the packet - if (radio_state == WB_RADIO_RX_PAIRING_QUALIFYING || radio_state == WB_RADIO_RX_ACTIVE) { - RAIL_HoldRxPacket(handle); - packet_held = true; - } - } else { - // RX completed without a packet, this is an error - error_code = -WB_RADIO_ERR_NO_PACKET; - } - } - - // Perform all calibrations when needed - if (events & RAIL_EVENT_CAL_NEEDED) { - RAIL_Status_t status = RAIL_Calibrate(handle, NULL, RAIL_CAL_ALL_PENDING); - if (status != RAIL_STATUS_NO_ERROR) { - // Calibration error - error_code = -WB_RADIO_ERR_CALIBRATION; - } - } - - // Check for sync words during channel scanning - if (radio_state == WB_RADIO_RX_PAIRING_SCANNING && events & RAIL_EVENT_RX_SYNC1_DETECT) { - sync_word_detected = true; - } -} - -// Copy the oldest pending packet from the radio buffer to the application buffer -static bool get_oldest_pending_packet(uint8_t *buffer, RAIL_Handle_t rail_handle) -{ - RAIL_RxPacketInfo_t packet_info; - RAIL_RxPacketHandle_t rx_handle; - - // Get the oldest complete packet (if any) - rx_handle = RAIL_GetRxPacketInfo(rail_handle, RAIL_RX_PACKET_HANDLE_OLDEST_COMPLETE, &packet_info); - if (rx_handle == RAIL_RX_PACKET_HANDLE_INVALID) - return false; - - // Copy the packet from the radio buffer to the application buffer - RAIL_CopyRxPacket(buffer, &packet_info); - RAIL_ReleaseRxPacket(rail_handle, rx_handle); - - return true; -} - -int wavebird_radio_init(wavebird_radio_packet_fn_t packet_fn, wavebird_radio_error_fn_t error_fn) -{ - RAIL_Status_t status = RAIL_STATUS_NO_ERROR; - - // Set the callback functions - packet_callback = packet_fn; - error_callback = error_fn; - - // Initialize RAIL handle - RAIL_Config_t rail_config = {.eventsCallback = handle_rail_event}; - rail_handle = RAIL_Init(&rail_config, NULL); - if (rail_handle == NULL) - return -WB_RADIO_ERR; - - // Configure data handling - RAIL_DataConfig_t data_config = {TX_PACKET_DATA, RX_PACKET_DATA, PACKET_MODE, PACKET_MODE}; - status |= RAIL_ConfigData(rail_handle, &data_config); - - // Configure channels, channelConfigs is generated from radio_settings.radioconf - status |= RAIL_ConfigChannels(rail_handle, channelConfigs[0], NULL); - - // Configure calibration - status |= RAIL_ConfigCal(rail_handle, RAIL_CAL_ALL); - - // Configure events - RAIL_Events_t event_mask = - (RAIL_EVENT_RX_SYNC1_DETECT | RAIL_EVENT_CAL_NEEDED | RAIL_EVENTS_RX_COMPLETION | RAIL_EVENT_RX_PACKET_RECEIVED); - status |= RAIL_ConfigEvents(rail_handle, RAIL_EVENTS_ALL, event_mask); - - // Configure RX transitions - RAIL_StateTransitions_t rx_transitions = {RAIL_RF_STATE_RX, RAIL_RF_STATE_RX}; - status |= RAIL_SetRxTransitions(rail_handle, &rx_transitions); - - // Check for errors during configuration - if (status != RAIL_STATUS_NO_ERROR) - return -WB_RADIO_ERR; - - return 0; -} - -uint8_t wavebird_radio_get_channel(void) -{ - return current_channel; -} - -int wavebird_radio_set_channel(uint8_t channel) -{ - // Check the channel is valid - if (channel > 15) - return -WB_RADIO_ERR_INVALID_CHANNEL; - - // Get the RAIL channel from the WaveBird channel number - uint8_t rail_channel = WAVEBIRD_CHANNEL_MAP[channel]; - - // Start receiving on the new channel - RAIL_StartRx(rail_handle, rail_channel, NULL); - - // Update the current channel - current_channel = channel; - - // Update the radio state - radio_state = WB_RADIO_RX_ACTIVE; - - return 0; -} - -void wavebird_radio_configure_qualification(wavebird_radio_qualify_fn_t _qualify_fn, uint8_t _qualify_threshold) -{ - qualify_fn = _qualify_fn; - qualify_threshold = _qualify_threshold; -} - -void wavebird_radio_set_pairing_started_callback(wavebird_radio_pairing_started_fn_t callback) -{ - pairing_started_callback = callback; -} - -void wavebird_radio_set_pairing_finished_callback(wavebird_radio_pairing_finished_fn_t callback) -{ - pairing_finished_callback = callback; -} - -void wavebird_radio_start_pairing(void) -{ - // Stop any ongoing RX - RAIL_Idle(rail_handle, RAIL_IDLE, true); - - // Reset the pairing state - pairing_state.timeout = RAIL_GetTime() + PAIRING_TIMEOUT; - pairing_state.first_scan = true; - pairing_state.channel = 0; - pairing_state.qualified_packets = 0; - - // Start the channel scanning process - radio_state = WB_RADIO_RX_PAIRING_SCANNING; - - // Fire the pairing started callback - if (pairing_started_callback) - pairing_started_callback(); -} - -void wavebird_radio_stop_pairing(void) -{ - // Reset the channel - wavebird_radio_set_channel(current_channel); - - // Fire the pairing finished callback - if (pairing_finished_callback) - pairing_finished_callback(WB_RADIO_PAIRING_CANCELLED, current_channel); -} - -void wavebird_radio_process(void) -{ - switch (radio_state) { - // Do nothing in the idle state - case WB_RADIO_IDLE: - break; - - // Loop through channels, listening for sync words - case WB_RADIO_RX_PAIRING_SCANNING: - // Check for activity on the current channel - if (sync_word_detected) { - sync_word_detected = false; - pairing_state.qualify_timeout = RAIL_GetTime() + PAIRING_QUALIFY_TIMEOUT; - - radio_state = WB_RADIO_RX_PAIRING_QUALIFYING; - } - - // Check if the pairing timeout has expired - if (RAIL_GetTime() > pairing_state.timeout) { - // Reset the channel to the original channel - wavebird_radio_set_channel(current_channel); - - // Fire the pairing callback - if (pairing_finished_callback) - pairing_finished_callback(WB_RADIO_PAIRING_TIMEOUT, current_channel); - - break; - } - - // If this is the first scan, or the detect timeout has expired, move to the next channel - if (pairing_state.first_scan || RAIL_GetTime() > pairing_state.detect_timeout) { - if (pairing_state.first_scan) { - pairing_state.first_scan = false; - } else { - pairing_state.channel = (pairing_state.channel + 1) % 16; - } - - pairing_state.detect_timeout = RAIL_GetTime() + PAIRING_DETECT_TIMEOUT; - RAIL_StartRx(rail_handle, WAVEBIRD_CHANNEL_MAP[pairing_state.channel], NULL); - } - - break; - - // Hold on the channel for a short time to qualify pairing activity - case WB_RADIO_RX_PAIRING_QUALIFYING: - // Check for packets on the current channel - if (packet_held) { - while (get_oldest_pending_packet(packet_buffer, rail_handle)) { - // Check if the packet qualifies for pairing - if (!qualify_fn || qualify_fn(packet_buffer)) - pairing_state.qualified_packets++; - - // If we have received enough qualifying packets, finish pairing - if (pairing_state.qualified_packets >= qualify_threshold) { - // Set the new channel - wavebird_radio_set_channel(pairing_state.channel); - - // Fire the pairing callback - if (pairing_finished_callback) - pairing_finished_callback(WB_RADIO_PAIRING_SUCCESS, pairing_state.channel); - - break; - } - } - - packet_held = false; - } - - // If the qualify timeout has expired, move back to scanning - if ((RAIL_GetTime() > pairing_state.qualify_timeout)) { - pairing_state.qualified_packets = 0; - - radio_state = WB_RADIO_RX_PAIRING_SCANNING; - } - break; - - // Listen for packets on the selected/paired channel - case WB_RADIO_RX_ACTIVE: - // Process received packets, if any - if (packet_held) { - while (get_oldest_pending_packet(packet_buffer, rail_handle)) { - // Pass the packet to the packet handler - if (packet_callback != NULL) - packet_callback(packet_buffer); - } - - // Clear the interrupt flag - packet_held = false; - } else if (error_code < 0) { - // Handle errors from the interrupt handler - if (error_callback != NULL) - error_callback(error_code); - - // Clear the interrupt flag - error_code = 0; - } - } -} \ No newline at end of file diff --git a/firmware/libwavebird/test/CMakeLists.txt b/firmware/libwavebird/test/CMakeLists.txt deleted file mode 100644 index b66af50..0000000 --- a/firmware/libwavebird/test/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -# Include the unity test framework -if(NOT unity_FOUND) - include(FetchContent) - FetchContent_Declare(Unity GIT_REPOSITORY https://github.com/ThrowTheSwitch/Unity.git) - FetchContent_MakeAvailable(Unity) -endif() - -# Helper function to create a test -function(add_libwavebird_test TEST_NAME) - # Create the test executable - add_executable(${TEST_NAME} ${ARGN}) - - # Link against the wavebird library and Unity - target_link_libraries(${TEST_NAME} wavebird unity::framework) - - # Add the test to CTest - add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME}) -endfunction() - -# GameCube controller target tests -add_libwavebird_test(test_bch3121 test_bch3121.c) -add_libwavebird_test(test_packet test_packet.c) diff --git a/firmware/libwavebird/test/fixtures.h b/firmware/libwavebird/test/fixtures.h deleted file mode 100644 index bf370c2..0000000 --- a/firmware/libwavebird/test/fixtures.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include - -// Input state from a real controller (resting state) -// - ID: 0x2B1 -// - Buttons: None -// - Stick: 0x88, 0x7F -// - Substick: 0x88, 0x82 -// - Triggers: 0x1A, 0x14 -static const uint8_t packet_input_state_resting[] = { - 0x40, 0x44, 0x2A, 0xEC, 0x66, 0xF5, 0xE5, 0x6D, 0x7F, 0x5F, 0x53, 0x9D, 0xB1, 0xB2, 0x23, 0x36, 0x88, 0x60, 0x00, -}; - -static const uint8_t message_input_state_resting[] = { - 0x00, 0xAB, 0x10, 0x00, 0x88, 0x7F, 0x88, 0x82, 0x1A, 0x14, 0x00, -}; - -static const uint32_t codewords_input_state_resting[] = { - 0x001afff3, - 0x07756a5f, - 0x59fffc80, - 0x0394a9d0, -}; - -// Origin from a real controller -// - ID: 0x2B1 -// - Stick: 0x86, 0x7F -// - Substick: 0x8B, 0x83 -// - Triggers: 0x1B, 0x13 -static const uint8_t packet_origin[] = { - 0x26, 0x26, 0x84, 0x4E, 0xAC, 0x8A, 0xC6, 0xC0, 0xA4, 0x26, 0xB5, 0x7E, 0xDB, 0xA5, 0x04, 0x51, 0x70, 0x20, 0x00, -}; - -static const uint8_t message_origin[] = { - 0x00, 0xEB, 0x18, 0x67, 0xF8, 0xB8, 0x31, 0xB1, 0x30, 0x00, 0x00, -}; \ No newline at end of file diff --git a/firmware/libwavebird/test/test_bch3121.c b/firmware/libwavebird/test/test_bch3121.c deleted file mode 100644 index c6eba6f..0000000 --- a/firmware/libwavebird/test/test_bch3121.c +++ /dev/null @@ -1,109 +0,0 @@ -#include "unity.h" - -#include "wavebird/bch3121.h" - -static const uint32_t valid_codeword = 0x0394a9d0; -static const uint32_t valid_message = 0x00015620; - -void setUp(void) -{ -} - -void tearDown(void) -{ -} - -// Test bch3121_decode decodes a valid codeword -static void test_decode() -{ - uint32_t decoded_message; - uint32_t syndrome = bch3121_decode(&decoded_message, valid_codeword); - - TEST_ASSERT_EQUAL(0, syndrome); - TEST_ASSERT_EQUAL_HEX32(valid_message, decoded_message); -} - -// Test bch3121_decode fails for a single-bit error in every position -static void test_decode_failure() -{ - uint32_t decoded_message; - - for (int i = 0; i < 31; i++) { - uint32_t corrupted_codeword = valid_codeword ^ (1 << i); - uint32_t syndrome = bch3121_decode(&decoded_message, corrupted_codeword); - - TEST_ASSERT_NOT_EQUAL(0, syndrome); - } -} - -// Test bch3121_decode_and_correct can correct a single-bit error in every position -static void test_decode_correct_single_error() -{ - uint32_t decoded_message; - - for (int i = 0; i < 31; i++) { - uint32_t corrupted_codeword = valid_codeword ^ (1 << i); - int errors_corrected = bch3121_decode_and_correct(&decoded_message, corrupted_codeword); - - TEST_ASSERT_EQUAL(1, errors_corrected); - TEST_ASSERT_EQUAL_HEX32(valid_message, decoded_message); - } -} - -// Test bch3121_decode_and_correct can correct a double-bit error in every pair of positions -static void test_decode_correct_double_error() -{ - uint32_t decoded_message; - - for (int i = 0; i < 31; i++) { - for (int j = i + 1; j < 31; j++) { - uint32_t corrupted_codeword = valid_codeword ^ (1 << i) ^ (1 << j); - int errors_corrected = bch3121_decode_and_correct(&decoded_message, corrupted_codeword); - - TEST_ASSERT_EQUAL(2, errors_corrected); - TEST_ASSERT_EQUAL_HEX32(valid_message, decoded_message); - } - } -} - -// Test bch3121_decode_and_correct fails for a triple-bit error -static void test_decode_correct_triple_error() -{ - uint32_t decoded_message; - uint32_t corrupted_codeword = valid_codeword ^ 0x7; - int rcode = bch3121_decode_and_correct(&decoded_message, corrupted_codeword); - - TEST_ASSERT_EQUAL(-BCH3121_ERR_UNCORRECTABLE, rcode); -} - -// Test bch3121_encode encodes a message -static void test_encode() -{ - uint32_t encoded_codeword = bch3121_encode(valid_message); - TEST_ASSERT_EQUAL_HEX32(valid_codeword, encoded_codeword); -} - -static void test_encode_decode() -{ - uint32_t codeword = bch3121_encode(0x12345); - uint32_t message; - int rcode = bch3121_decode_and_correct(&message, codeword); - - TEST_ASSERT_EQUAL(0, rcode); - TEST_ASSERT_EQUAL_HEX32(0x12345, message); -} - -int main(int argc, char **argv) -{ - UNITY_BEGIN(); - - RUN_TEST(test_decode); - RUN_TEST(test_decode_failure); - RUN_TEST(test_decode_correct_single_error); - RUN_TEST(test_decode_correct_double_error); - RUN_TEST(test_decode_correct_triple_error); - RUN_TEST(test_encode); - RUN_TEST(test_encode_decode); - - return UNITY_END(); -} \ No newline at end of file diff --git a/firmware/libwavebird/test/test_packet.c b/firmware/libwavebird/test/test_packet.c deleted file mode 100644 index e3670fe..0000000 --- a/firmware/libwavebird/test/test_packet.c +++ /dev/null @@ -1,253 +0,0 @@ -#include - -#include "unity.h" - -#include "wavebird/message.h" -#include "wavebird/packet.h" - -#include "fixtures.h" - -void setUp(void) -{ -} - -void tearDown(void) -{ -} - -static void test_deinterleave() -{ - uint32_t codewords[4] = {0}; - wavebird_packet_deinterleave(codewords, packet_input_state_resting); - - TEST_ASSERT_EQUAL_HEX32(codewords_input_state_resting[0], codewords[0]); - TEST_ASSERT_EQUAL_HEX32(codewords_input_state_resting[1], codewords[1]); - TEST_ASSERT_EQUAL_HEX32(codewords_input_state_resting[2], codewords[2]); - TEST_ASSERT_EQUAL_HEX32(codewords_input_state_resting[3], codewords[3]); -} - -static void test_interleave() -{ - uint8_t packet[WAVEBIRD_PACKET_BYTES]; - wavebird_packet_interleave(packet, codewords_input_state_resting); - - // Check 15.5 bytes are equal - TEST_ASSERT_EQUAL_HEX8_ARRAY(packet_input_state_resting, packet, 15); - TEST_ASSERT_EQUAL_HEX8(packet_input_state_resting[15] & 0xf0, packet[15] & 0xf0); -} - -static void test_deinterleave_interleave() -{ - // Deinterleave a packet - uint32_t codewords[4]; - wavebird_packet_deinterleave(codewords, packet_input_state_resting); - - // Interleave the resulting codewords - uint8_t packet[WAVEBIRD_PACKET_BYTES]; - wavebird_packet_interleave(packet, codewords); - - // Check all 15.5 bytes are equal - TEST_ASSERT_EQUAL_HEX8_ARRAY(packet_input_state_resting, packet, 15); - TEST_ASSERT_EQUAL_HEX8(packet_input_state_resting[15] & 0xf0, packet[15] & 0xf0); -} - -static void test_encode_input_state() -{ - // Encode the input state message - uint8_t packet[WAVEBIRD_PACKET_BYTES]; - wavebird_packet_encode(packet, message_input_state_resting); - - // Check the crc is correct - uint16_t expected_crc = wavebird_packet_get_crc(packet_input_state_resting); - uint16_t actual_crc = wavebird_packet_get_crc(packet); - TEST_ASSERT_EQUAL_HEX16(expected_crc, actual_crc); - - // Check all 15.5 bytes of data are correct - TEST_ASSERT_EQUAL_HEX8_ARRAY(packet_input_state_resting, packet, 15); - TEST_ASSERT_EQUAL_HEX8(packet_input_state_resting[15] & 0xf0, packet[15] & 0xf0); -} - -static void test_encode_origin() -{ - // Encode the origin message - uint8_t packet[WAVEBIRD_PACKET_BYTES]; - wavebird_packet_encode(packet, message_origin); - - // Check the crc is correct - uint16_t expected_crc = wavebird_packet_get_crc(packet_origin); - uint16_t actual_crc = wavebird_packet_get_crc(packet); - TEST_ASSERT_EQUAL_HEX16(expected_crc, actual_crc); - - // Check all 15.5 bytes of data are correct - TEST_ASSERT_EQUAL_HEX8_ARRAY(packet_origin, packet, 15); - TEST_ASSERT_EQUAL_HEX8(packet_origin[15] & 0xf0, packet[15] & 0xf0); -} - -static void test_decode_input_state() -{ - uint8_t message[WAVEBIRD_MESSAGE_BYTES]; - int rcode = wavebird_packet_decode(message, packet_input_state_resting); - - // Check decoding was successful - TEST_ASSERT_EQUAL(0, rcode); - - // Check the message type - TEST_ASSERT_EQUAL(WB_MESSAGE_TYPE_INPUT_STATE, wavebird_message_get_type(message)); - - // Check the controller ID - TEST_ASSERT_EQUAL_HEX16(0x2B1, wavebird_message_get_controller_id(message)); - - // Check the buttons - uint16_t buttons = wavebird_input_state_get_buttons(message); - TEST_ASSERT_EQUAL_HEX16(0x0000, buttons); - - // Check the analog inputs - TEST_ASSERT_EQUAL_HEX8(0x88, wavebird_input_state_get_stick_x(message)); - TEST_ASSERT_EQUAL_HEX8(0x7F, wavebird_input_state_get_stick_y(message)); - TEST_ASSERT_EQUAL_HEX8(0x88, wavebird_input_state_get_substick_x(message)); - TEST_ASSERT_EQUAL_HEX8(0x82, wavebird_input_state_get_substick_y(message)); - TEST_ASSERT_EQUAL_HEX8(0x1A, wavebird_input_state_get_trigger_left(message)); - TEST_ASSERT_EQUAL_HEX8(0x14, wavebird_input_state_get_trigger_right(message)); -} - -static void test_decode_origin() -{ - uint8_t message[WAVEBIRD_MESSAGE_BYTES]; - int rcode = wavebird_packet_decode(message, packet_origin); - - // Check decoding was successful - TEST_ASSERT_EQUAL(0, rcode); - - // Check the message type - TEST_ASSERT_EQUAL(WB_MESSAGE_TYPE_ORIGIN, wavebird_message_get_type(message)); - - // Check the controller ID - TEST_ASSERT_EQUAL_HEX16(0x2B1, wavebird_message_get_controller_id(message)); - - // Check the origin values - TEST_ASSERT_EQUAL_HEX8(0x86, wavebird_origin_get_stick_x(message)); - TEST_ASSERT_EQUAL_HEX8(0x7F, wavebird_origin_get_stick_y(message)); - TEST_ASSERT_EQUAL_HEX8(0x8B, wavebird_origin_get_substick_x(message)); - TEST_ASSERT_EQUAL_HEX8(0x83, wavebird_origin_get_substick_y(message)); - TEST_ASSERT_EQUAL_HEX8(0x1B, wavebird_origin_get_trigger_left(message)); - TEST_ASSERT_EQUAL_HEX8(0x13, wavebird_origin_get_trigger_right(message)); -} - -static void test_decode_single_error() -{ - uint8_t packet[WAVEBIRD_PACKET_BYTES]; - uint8_t message[WAVEBIRD_MESSAGE_BYTES]; - - // Corrupt a single bit in the payload portion of the packet - for (int i = 0; i < 124; i++) { - // Start with a clean packet - memcpy(packet, packet_input_state_resting, WAVEBIRD_PACKET_BYTES); - - // Corrupt a single bit in the packet - packet[i / 8] ^= (1 << (7 - i % 8)); - - // Attempt to decode the corrupted packet, check decoding was successful - int rcode = wavebird_packet_decode(message, packet); - TEST_ASSERT_EQUAL(0, rcode); - TEST_ASSERT_EQUAL_HEX8_ARRAY(message_input_state_resting, message, WAVEBIRD_MESSAGE_BYTES); - } -} - -static void test_decode_burst_error() -{ - uint8_t packet[WAVEBIRD_PACKET_BYTES]; - uint8_t message[WAVEBIRD_MESSAGE_BYTES]; - - // Sweep through the payload portion of the packet a nibble at a time - for (int i = 0; i < 30; i++) { - // Start with a clean packet - memcpy(packet, packet_input_state_resting, WAVEBIRD_PACKET_BYTES); - - // Corrupt a byte in the packet - if (i % 2 == 0) { - packet[i / 2] ^= 0xFF; - } else { - packet[i / 2] ^= 0x0F; - packet[i / 2 + 1] ^= 0xF0; - } - - // Attempt to decode the corrupted packet, check decoding was successful - int rcode = wavebird_packet_decode(message, packet); - TEST_ASSERT_EQUAL(0, rcode); - TEST_ASSERT_EQUAL_HEX8_ARRAY(message_input_state_resting, message, WAVEBIRD_MESSAGE_BYTES); - } -} - -static void test_decode_failure() -{ - uint8_t packet[WAVEBIRD_PACKET_BYTES]; - uint8_t message[WAVEBIRD_MESSAGE_BYTES]; - - // Sweep through the payload portion of the packet a byte at a time - for (int i = 0; i < 15; i++) { - // Start with a clean packet - memcpy(packet, packet_input_state_resting, WAVEBIRD_PACKET_BYTES); - - // Corrupt two bytes in the payload portion of the packet - packet[i] ^= 0xFF; - packet[i + 1] ^= 0xFF; - - // Attempt to decode the corrupted packet, check decoding failed - int rcode = wavebird_packet_decode(message, packet); - TEST_ASSERT_NOT_EQUAL(0, rcode); - } -} - -static void test_decode_crc_mismatch() -{ - uint8_t packet[WAVEBIRD_PACKET_BYTES]; - uint8_t message[WAVEBIRD_MESSAGE_BYTES]; - - // Start with a clean packet - memcpy(packet, packet_input_state_resting, WAVEBIRD_PACKET_BYTES); - - // Corrupt a bit in the CRC portion of the packet - packet[15] ^= 0x01; - - // Attempt to decode the corrupted packet, check decoding failed - int rcode = wavebird_packet_decode(message, packet); - TEST_ASSERT_EQUAL(-WB_PACKET_ERR_CRC_MISMATCH, rcode); -} - -static void test_encode_decode() -{ - uint8_t packet[WAVEBIRD_PACKET_BYTES] = {0}; - uint8_t message[WAVEBIRD_MESSAGE_BYTES] = {0}; - - // Encode a message into a packet - wavebird_packet_encode(packet, message_input_state_resting); - - // Decode the packet back into a message - int rc = wavebird_packet_decode(message, packet); - - // Check decoding was successful - TEST_ASSERT_EQUAL(0, rc); - - // Check the decoded message is identical to the original - TEST_ASSERT_EQUAL_HEX8_ARRAY(message_input_state_resting, message, WAVEBIRD_MESSAGE_BYTES); -} - -int main(int argc, char **argv) -{ - UNITY_BEGIN(); - - RUN_TEST(test_deinterleave); - RUN_TEST(test_interleave); - RUN_TEST(test_deinterleave_interleave); - RUN_TEST(test_encode_input_state); - RUN_TEST(test_encode_origin); - RUN_TEST(test_decode_input_state); - RUN_TEST(test_decode_origin); - RUN_TEST(test_decode_single_error); - RUN_TEST(test_decode_burst_error); - RUN_TEST(test_decode_failure); - RUN_TEST(test_decode_crc_mismatch); - RUN_TEST(test_encode_decode); - - return UNITY_END(); -} From 5f5e3d26a1a88ee6a85d7727da482d1e6a3cda97 Mon Sep 17 00:00:00 2001 From: James Smith Date: Sat, 10 Jan 2026 11:49:46 -0800 Subject: [PATCH 10/15] Add libwavebird as submodule --- .gitmodules | 3 +++ firmware/libwavebird | 1 + 2 files changed, 4 insertions(+) create mode 100644 .gitmodules create mode 160000 firmware/libwavebird diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..bfa4852 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "firmware/libwavebird"] + path = firmware/libwavebird + url = https://github.com/loopj/libwavebird.git diff --git a/firmware/libwavebird b/firmware/libwavebird new file mode 160000 index 0000000..4d1d2ea --- /dev/null +++ b/firmware/libwavebird @@ -0,0 +1 @@ +Subproject commit 4d1d2ea326129718b4c6be9ff48e383dfd4b647e From 3c99e63bb0ac6540cff3e598746922da8f14076c Mon Sep 17 00:00:00 2001 From: James Smith Date: Sat, 10 Jan 2026 12:28:30 -0800 Subject: [PATCH 11/15] Add build script --- firmware/build.sh | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100755 firmware/build.sh diff --git a/firmware/build.sh b/firmware/build.sh new file mode 100755 index 0000000..ec96f83 --- /dev/null +++ b/firmware/build.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +# Check that BOARD environment variable is set +if [[ -z "$BOARD" ]]; then + echo "Error: BOARD environment variable not set" + exit 1 +fi + +# Clean previous build artifacts +rm -rf target/$BOARD + +# Generate the CMake project for the specified board +slc generate wavephoenix.slcp --with "$BOARD;wavephoenix" --export-destination target/$BOARD --output-type cmake --sdk-extensions=.,libsi,libwavebird + +# Build the project +cd target/$BOARD/wavephoenix_cmake +cmake --workflow --preset project + +# Generate the .gbl file +/Applications/Commander.app/Contents/MacOS/commander gbl create \ + --app build/default_config/wavephoenix.s37 \ + build/default_config/wavephoenix.gbl \ No newline at end of file From 673626ebfaacc25704736f49ed77544b50be9c1f Mon Sep 17 00:00:00 2001 From: James Smith Date: Sat, 10 Jan 2026 12:30:24 -0800 Subject: [PATCH 12/15] Add libjoybus as submodule --- .gitmodules | 3 +++ firmware/libjoybus | 1 + firmware/src/main.c | 5 ++--- 3 files changed, 6 insertions(+), 3 deletions(-) create mode 160000 firmware/libjoybus diff --git a/.gitmodules b/.gitmodules index bfa4852..39c9fb5 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "firmware/libwavebird"] path = firmware/libwavebird url = https://github.com/loopj/libwavebird.git +[submodule "firmware/libjoybus"] + path = firmware/libjoybus + url = https://github.com/loopj/libjoybus.git diff --git a/firmware/libjoybus b/firmware/libjoybus new file mode 160000 index 0000000..a1ffcac --- /dev/null +++ b/firmware/libjoybus @@ -0,0 +1 @@ +Subproject commit a1ffcaceb4a169ac3ec92315acfe66249fff8493 diff --git a/firmware/src/main.c b/firmware/src/main.c index 035ff45..e9e4422 100644 --- a/firmware/src/main.c +++ b/firmware/src/main.c @@ -8,9 +8,8 @@ #include "si/commands.h" #include "si/device/gc_controller.h" -#include "wavebird/message.h" -#include "wavebird/packet.h" -#include "wavebird/radio.h" + +#include #include "button.h" #include "channel_wheel.h" From 620f9f1b5d85abe8661372a37e109c7fa2376b2f Mon Sep 17 00:00:00 2001 From: James Smith Date: Sat, 10 Jan 2026 12:58:25 -0800 Subject: [PATCH 13/15] Port to libjoybus --- firmware/boards/efr32xg22e.slcc | 8 +- firmware/boards/rf-bm-bg22c3.slcc | 8 +- firmware/build.sh | 2 +- firmware/libsi/CMakeLists.txt | 36 -- firmware/libsi/README.md | 21 - firmware/libsi/include/si/commands.h | 53 --- firmware/libsi/include/si/crc8.h | 14 - .../libsi/include/si/device/gc_controller.h | 167 -------- firmware/libsi/include/si/si.h | 170 -------- firmware/libsi/libsi.slcc | 23 -- firmware/libsi/libsi.slce | 10 - firmware/libsi/src/commands.c | 93 ----- firmware/libsi/src/crc8.c | 32 -- firmware/libsi/src/device/gc_controller.c | 362 ----------------- firmware/libsi/src/platform/efr32/si_efr32.c | 324 --------------- firmware/libsi/test/CMakeLists.txt | 12 - firmware/libsi/test/test_commands.c | 44 -- firmware/libsi/test/test_gc_controller.c | 376 ------------------ firmware/libsi/test/test_main.c | 27 -- firmware/src/main.c | 109 +++-- firmware/src/version.h | 2 +- firmware/wavephoenix.slcp | 4 +- 22 files changed, 63 insertions(+), 1834 deletions(-) delete mode 100644 firmware/libsi/CMakeLists.txt delete mode 100644 firmware/libsi/README.md delete mode 100644 firmware/libsi/include/si/commands.h delete mode 100644 firmware/libsi/include/si/crc8.h delete mode 100644 firmware/libsi/include/si/device/gc_controller.h delete mode 100644 firmware/libsi/include/si/si.h delete mode 100644 firmware/libsi/libsi.slcc delete mode 100644 firmware/libsi/libsi.slce delete mode 100644 firmware/libsi/src/commands.c delete mode 100644 firmware/libsi/src/crc8.c delete mode 100644 firmware/libsi/src/device/gc_controller.c delete mode 100644 firmware/libsi/src/platform/efr32/si_efr32.c delete mode 100644 firmware/libsi/test/CMakeLists.txt delete mode 100644 firmware/libsi/test/test_commands.c delete mode 100644 firmware/libsi/test/test_gc_controller.c delete mode 100644 firmware/libsi/test/test_main.c diff --git a/firmware/boards/efr32xg22e.slcc b/firmware/boards/efr32xg22e.slcc index 891b78c..3b2bde7 100644 --- a/firmware/boards/efr32xg22e.slcc +++ b/firmware/boards/efr32xg22e.slcc @@ -12,10 +12,14 @@ requires: - name: printf define: - - name: SI_DATA_PORT + - name: JOYBUS_PORT value: gpioPortD - - name: SI_DATA_PIN + - name: JOYBUS_PIN value: 3 + - name: JOYBUS_TIMER + value: TIMER1 + - name: JOYBUS_USART + value: USART0 - name: HAS_PAIR_BTN value: 1 - name: PAIR_BTN_PORT diff --git a/firmware/boards/rf-bm-bg22c3.slcc b/firmware/boards/rf-bm-bg22c3.slcc index 63a73ab..6fac91e 100644 --- a/firmware/boards/rf-bm-bg22c3.slcc +++ b/firmware/boards/rf-bm-bg22c3.slcc @@ -9,10 +9,14 @@ requires: - name: efr32bg22c224f512gm32 define: - - name: SI_DATA_PORT + - name: JOYBUS_PORT value: gpioPortA - - name: SI_DATA_PIN + - name: JOYBUS_PIN value: 2 + - name: JOYBUS_TIMER + value: TIMER1 + - name: JOYBUS_USART + value: USART0 - name: HAS_PAIR_BTN value: 1 - name: PAIR_BTN_PORT diff --git a/firmware/build.sh b/firmware/build.sh index ec96f83..987866e 100755 --- a/firmware/build.sh +++ b/firmware/build.sh @@ -10,7 +10,7 @@ fi rm -rf target/$BOARD # Generate the CMake project for the specified board -slc generate wavephoenix.slcp --with "$BOARD;wavephoenix" --export-destination target/$BOARD --output-type cmake --sdk-extensions=.,libsi,libwavebird +slc generate wavephoenix.slcp --with "$BOARD;wavephoenix" --export-destination target/$BOARD --output-type cmake --sdk-extensions=.,libjoybus,libwavebird # Build the project cd target/$BOARD/wavephoenix_cmake diff --git a/firmware/libsi/CMakeLists.txt b/firmware/libsi/CMakeLists.txt deleted file mode 100644 index 1884b87..0000000 --- a/firmware/libsi/CMakeLists.txt +++ /dev/null @@ -1,36 +0,0 @@ -# Set minimum CMake version -cmake_minimum_required(VERSION "3.21") - -# Configure project and languages -project(si LANGUAGES C) - -# Define the library -add_library(si STATIC "src/crc8.c" "src/commands.c" "src/device/gc_controller.c") - -# Specify the include paths -target_include_directories(si PUBLIC include) - -# EFR32 platform specific settings -if(CMAKE_CROSSCOMPILING) - # Download and make the GeckoSDK CMake targets available - if(NOT GeckoSDK_FOUND) - include(FetchContent) - FetchContent_Declare( - GeckoSDK - GIT_REPOSITORY https://github.com/loopj/gecko-sdk-cmake.git - GIT_TAG main - ) - FetchContent_MakeAvailable(GeckoSDK) - endif() - - # Add the platform-specific source files - target_sources(si PRIVATE "src/platform/efr32/si_efr32.c") - - # Depend on emlib from the Gecko SDK - target_link_libraries(si GeckoSDK::emlib GeckoSDK::emdrv::dmadrv) -endif() - -# Add the test target -if(NOT CMAKE_CROSSCOMPILING) - add_subdirectory(test) -endif() diff --git a/firmware/libsi/README.md b/firmware/libsi/README.md deleted file mode 100644 index 8a9529e..0000000 --- a/firmware/libsi/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# libsi - -A partial implementation of the SI (Serial Interface) protocol used by N64 and GameCube controllers. - -## Platforms - -- EFR32 Series 1 and Series 2 SoCs - -## Running tests - -- Build the test suite - - ```bash - cmake -Bbuild && cmake --build build --target test_si - ``` - -- Run the tests - - ```bash - ./build/test/test_si - ``` diff --git a/firmware/libsi/include/si/commands.h b/firmware/libsi/include/si/commands.h deleted file mode 100644 index 202fa10..0000000 --- a/firmware/libsi/include/si/commands.h +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -#include -#include - -#include "si.h" - -/** - * Function type for command handlers. - * - * @param command the command to handle - * @param callback function to call when the command is complete - * @param context user-defined context - * - * @return 0 on success, negative error code on failure - */ -typedef int (*si_command_handler_fn)(const uint8_t *command, si_callback_fn callback, void *context); - -/** - * Register a command handler for commands from an SI host. - * - * @param command the command to handle - * @param command_length the length of the command - * @param handler the command handler function - * - */ -void si_command_register(uint8_t command, uint8_t length, si_command_handler_fn handler, void *context); - -/** - * Get the expected length of an SI command. - * - * @param command the command to check - * - * @return the expected length of the command, in bytes, or 0 if the command is unknown - */ -uint8_t si_command_get_length(uint8_t command); - -/** - * Get the command handler for an SI command. - * - * @param command the command to check - * - * @return the command handler function, or NULL if the command is unknown - */ -si_command_handler_fn si_command_get_handler(uint8_t command); - -/** - * Process incoming SI commands. - * - * This function should be called periodically to check for incoming commands - * and handle them as needed. - */ -void si_command_process(); \ No newline at end of file diff --git a/firmware/libsi/include/si/crc8.h b/firmware/libsi/include/si/crc8.h deleted file mode 100644 index 4daed30..0000000 --- a/firmware/libsi/include/si/crc8.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include -#include - -/** - * Calculate the CRC-8 checksum of a data buffer, using the polynomial 0x85. - * - * This is used with various SI commands that transfer blocks of data. - * - * @param data The data buffer - * @param size The size of the data buffer - */ -uint8_t si_crc8(uint8_t *data, size_t size); \ No newline at end of file diff --git a/firmware/libsi/include/si/device/gc_controller.h b/firmware/libsi/include/si/device/gc_controller.h deleted file mode 100644 index c194e18..0000000 --- a/firmware/libsi/include/si/device/gc_controller.h +++ /dev/null @@ -1,167 +0,0 @@ -/** - * SI command handling for GameCube controllers. - */ - -#pragma once - -#include -#include - -#include "si/si.h" - -/** - * Rumble motor states. - */ -enum si_device_gc_motor_state { - SI_DEVICE_GC_MOTOR_STOP, - SI_DEVICE_GC_MOTOR_RUMBLE, - SI_DEVICE_GC_MOTOR_STOP_HARD, -}; - -/** - * Analog modes. - */ -enum { - SI_DEVICE_GC_ANALOG_MODE_0, - SI_DEVICE_GC_ANALOG_MODE_1, - SI_DEVICE_GC_ANALOG_MODE_2, - SI_DEVICE_GC_ANALOG_MODE_3, - SI_DEVICE_GC_ANALOG_MODE_4, -}; - -/** - * GameCube controller input state. - * - * On the wire, the button state bits are sent in the following order: - * Error, Error Latch, Need Origin, Start, Y, X, B, A, Use Origin, L, R, Z, Up, Down, Right, Left - */ -struct si_device_gc_input_state { - union { - struct { - // Byte 0 - uint8_t a : 1; // Bit 0 - uint8_t b : 1; // Bit 1 - uint8_t x : 1; // Bit 2 - uint8_t y : 1; // Bit 3 - uint8_t start : 1; // Bit 4 - uint8_t need_origin : 1; // Bit 5, should the host fetch the origin? - uint8_t error_latch : 1; // Bit 6, error code, latched - uint8_t error : 1; // Bit 7, error code - - // Byte 1 - uint8_t left : 1; // Bit 0 - uint8_t right : 1; // Bit 1 - uint8_t down : 1; // Bit 2 - uint8_t up : 1; // Bit 3 - uint8_t z : 1; // Bit 4 - uint8_t r : 1; // Bit 5 - uint8_t l : 1; // Bit 6 - uint8_t use_origin : 1; // Bit 7, should the host use the origin? - } __attribute__((packed)); - - uint8_t bytes[2]; - } buttons; - - uint8_t stick_x; - uint8_t stick_y; - uint8_t substick_x; - uint8_t substick_y; - uint8_t trigger_left; - uint8_t trigger_right; - uint8_t analog_a; - uint8_t analog_b; -} __attribute__((packed)); - -/** - * GameCube controller device state. - */ -struct si_device_gc_controller { - uint8_t info[3]; - struct si_device_gc_input_state origin; - struct si_device_gc_input_state input; - bool input_valid; -}; - -/** - * Initialize to present on the SI bus as a GameCube controller. - * - * This function sets up the initial state, and registers SI command - * handlers for OEM GameCube controller, and WaveBird controller commands. - * - * @param device the device to initialize - * @param type the device type flags - * @param input_state pointer to an input state buffer - */ -void si_device_gc_init(struct si_device_gc_controller *device, uint8_t type); - -/** - * Check if the device is a WaveBird controller. - * - * @param device the device to check - * - * @return true if the device is a WaveBird controller - */ -static inline bool si_device_gc_is_wireless(struct si_device_gc_controller *device) -{ - return device->info[0] & SI_GC_WIRELESS; -} - -/** - * Set the wireless ID of the controller. - * - * Wireless IDs are 10-bit numbers used to identify a WaveBird controller. - * Although these IDs aren’t globally unique, they are assumed to be distinct enough - * so that it’s unlikely for a single user to have two controllers with the same ID. - * The ID helps bind a controller to a specific port after data reception. - * - * @param device the device to set the wireless ID for - * @param wireless_id the new 10-bit wireless ID - */ -void si_device_gc_set_wireless_id(struct si_device_gc_controller *device, uint16_t wireless_id); - -/** - * Get the current wireless ID of the controller. - * - * @param device the device to get the wireless ID from - * - * @return the current 10-bit wireless ID - */ -static inline uint16_t si_device_gc_get_wireless_id(struct si_device_gc_controller *device) -{ - return (device->info[1] & 0xC0) << 2 | device->info[2]; -} - -/** - * Determine if the wireless ID has been fixed. - * - * Fixing the wireless ID is used to bind a WaveBird controller to a specific receiver. - * - * @param device the device to check - * - * @return true if the wireless ID is fixed - */ -static inline bool si_device_gc_wireless_id_fixed(struct si_device_gc_controller *device) -{ - return device->info[1] & SI_WIRELESS_FIX_ID; -} - -/** - * Mark the input state as valid. - * - * @param device the device to set the input state for - * @param valid true if the input state is valid - */ -static inline void si_device_set_input_valid(struct si_device_gc_controller *device, bool valid) -{ - device->input_valid = valid; -} - -/** - * Update the origin of the controller. - * - * If the origin data differs from the current origin, the "need origin" flag is set. - * - * @param device the device to set the wireless origin for - * @param new_origin pointer to the new origin data - */ -void si_device_gc_set_origin(struct si_device_gc_controller *device, struct si_device_gc_input_state *new_origin); \ No newline at end of file diff --git a/firmware/libsi/include/si/si.h b/firmware/libsi/include/si/si.h deleted file mode 100644 index 7233f2e..0000000 --- a/firmware/libsi/include/si/si.h +++ /dev/null @@ -1,170 +0,0 @@ -/** - * SI (Serial Interface) protocol. - * - * SI is a half-duplex, asynchronous serial protocol using a single, open-drain - * line with an external pull-up resistor. - * - * The operating voltage is 3.3V. - * - * OEM GameCube controllers send SI pulses at 250 kHz (a 4µs period): - * - Logic 0 pulse: 3µs low, 1µs high - * - Logic 1 pulse: 1µs low, 3µs high - * - Stop bit: 2µs low, 2µs high - * - * WaveBird receivers send SI pulses at 225 kHz (a 4.44µs period): - * - Logic 0 pulse: 3.33µs low, 1.11µs high - * - Logic 1 pulse: 1.11µs low, 3.33µs high - * - Stop bit: 2.22µs low, 2.22µs high - * - * A GameCube/Wii console sends SI pulses at 200 kHz (a 5µs period): - * - Logic 0 pulse: 3.75µs low, 1.25µs high - * - Logic 1 pulse: 1.25µs low, 3.75µs high - * - Stop bit: 1.25µs low, 3.75µs high (same as logic 1) - * - * Communication: - * - Host (console) sends a 1-3 byte command to a device (controller). - * - Device responds with a multi-byte response. - * - Command and responses are terminated with a stop bit. - * - * Commands: - * - 0x00 - Get device type and status - * - 0xFF - Reset device - * - Other commands are device-specific - * - * Implementation: - * - Implementations need to clock in and out pulses on the SI line quickly and accurately. - * - Bit-banging is not feasible due to the tight timing requirements. - * - Recommended to use timer peripherals to capture pulses. - * - Recommended to use uart/timer peripherals to transmit pulses. - * - Recommended to use DMA to transfer data between the uart/timer and memory. - */ - -#pragma once - -#include -#include - -// SI transfers are max 64 bytes -#define SI_BLOCK_SIZE 64 - -// Common commands -#define SI_CMD_RESET 0xFF -#define SI_CMD_RESET_LEN 1 -#define SI_CMD_RESET_RESP 3 - -#define SI_CMD_INFO 0x00 -#define SI_CMD_INFO_LEN 1 -#define SI_CMD_INFO_RESP 3 - -// GameCube controller commands -#define SI_CMD_GC_SHORT_POLL 0x40 -#define SI_CMD_GC_SHORT_POLL_LEN 3 -#define SI_CMD_GC_SHORT_POLL_RESP 8 - -#define SI_CMD_GC_READ_ORIGIN 0x41 -#define SI_CMD_GC_READ_ORIGIN_LEN 1 -#define SI_CMD_GC_READ_ORIGIN_RESP 10 - -#define SI_CMD_GC_CALIBRATE 0x42 -#define SI_CMD_GC_CALIBRATE_LEN 3 -#define SI_CMD_GC_CALIBRATE_RESP 10 - -#define SI_CMD_GC_LONG_POLL 0x43 -#define SI_CMD_GC_LONG_POLL_LEN 3 -#define SI_CMD_GC_LONG_POLL_RESP 10 - -#define SI_CMD_GC_PROBE_DEVICE 0x4D -#define SI_CMD_GC_PROBE_DEVICE_LEN 3 -#define SI_CMD_GC_PROBE_DEVICE_RESP 8 - -#define SI_CMD_GC_FIX_DEVICE 0x4E -#define SI_CMD_GC_FIX_DEVICE_LEN 3 -#define SI_CMD_GC_FIX_DEVICE_RESP 3 - -// SI device info flags -// On wireless controllers 0x00C0FF is reserved for the controller ID - -// Byte 0 -#define SI_GC_STANDARD 0x01 -#define SI_WIRELESS_STATE 0x02 -#define SI_TYPE_GC 0x08 -#define SI_GC_NOMOTOR 0x20 -#define SI_WIRELESS_RECEIVED 0x40 -#define SI_GC_WIRELESS 0x80 - -// Byte 1 -#define SI_WIRELESS_FIX_ID 0x10 -#define SI_WIRELESS_ORIGIN 0x20 - -// Byte 2 -#define SI_HAS_ERROR 0x80 -#define SI_HAS_LATCHED_ERROR 0x40 -#define SI_NEED_ORIGIN 0x20 -#define SI_MOTOR_STATE_MASK 0x18 -#define SI_ANALOG_MODE_MASK 0x07 - -// SI modes -enum { - SI_MODE_HOST, - SI_MODE_DEVICE, -}; - -// Error codes -enum { - SI_ERR_NOT_READY = 1, - SI_ERR_UNKNOWN_COMMAND, - SI_ERR_INVALID_COMMAND, - SI_ERR_TRANSFER_FAILED, - SI_ERR_TRANSFER_TIMEOUT, -}; - -/** - * Function type for transfer completion callbacks. - * - * @param result 0 on success, negative error code on failure - */ -typedef void (*si_callback_fn)(int result); - -/** - * Initialize the SI bus. - * - * @param port the GPIO port to use - * @param pin the GPIO pin to use - * @param mode the SI mode (host or device) - * @param rx_freq the receive frequency, in Hz - * @param tx_freq the transmit frequency, in Hz - */ -void si_init(uint8_t port, uint8_t pin, uint8_t mode, uint32_t rx_freq, uint32_t tx_freq); - -/** - * Write data to the SI bus. - * - * @param data the data to send - * @param length the length of the data - * @param callback function to call when the transfer is complete - */ -void si_write_bytes(const uint8_t *data, uint8_t length, si_callback_fn callback); - -/** - * Read data from the SI bus. - * - * @param buffer the buffer to read into - * @param length the number of bytes expected - * @param callback function to call when the transfer is complete - */ -void si_read_bytes(uint8_t *buffer, uint8_t length, si_callback_fn callback); - -/** - * Read a single command from the SI bus. - * - * @param buffer the buffer to read into - * @param callback function to call when the command has been read - */ -void si_read_command(uint8_t *buffer, si_callback_fn callback); - -/** - * Wait for the SI bus to be idle. - * - * This function will block until the SI bus is idle. - */ -void si_await_bus_idle(void); \ No newline at end of file diff --git a/firmware/libsi/libsi.slcc b/firmware/libsi/libsi.slcc deleted file mode 100644 index d430ea4..0000000 --- a/firmware/libsi/libsi.slcc +++ /dev/null @@ -1,23 +0,0 @@ -id: libsi -label: libsi -package: "ext-comp" -description: > - Implementation of the Joybus protocol used by N64 and GameCube controllers. -category: External -quality: production - -provides: - - name: libsi - -requires: - - name: emlib - - name: dmadrv - -include: - - path: include - -source: - - path: src/commands.c - - path: src/crc8.c - - path: src/device/gc_controller.c - - path: src/platform/efr32/si_efr32.c diff --git a/firmware/libsi/libsi.slce b/firmware/libsi/libsi.slce deleted file mode 100644 index 1c9ee24..0000000 --- a/firmware/libsi/libsi.slce +++ /dev/null @@ -1,10 +0,0 @@ -id: libsi -label: libsi -version: 1.0.0 - -sdk: - id: simplicity_sdk - version: 2025.6.2 - -component_path: - - path: . diff --git a/firmware/libsi/src/commands.c b/firmware/libsi/src/commands.c deleted file mode 100644 index 71d9506..0000000 --- a/firmware/libsi/src/commands.c +++ /dev/null @@ -1,93 +0,0 @@ -#include "si/commands.h" - -enum { - COMMAND_STATE_IDLE, - COMMAND_STATE_RX, - COMMAND_STATE_TX, - COMMAND_STATE_ERROR, -}; - -struct command_entry { - uint8_t length; - si_command_handler_fn handler; - void *context; -}; - -static uint8_t command_state = COMMAND_STATE_IDLE; -static struct command_entry command_table[256] = {0}; -static uint8_t command_buffer[SI_BLOCK_SIZE]; -static bool auto_tx_rx_transition = true; - -static void on_tx_complete(int result); -static void on_rx_complete(int result); - -void si_command_register(uint8_t command, uint8_t length, si_command_handler_fn handler, void *context) -{ - command_table[command].length = length; - command_table[command].handler = handler; - command_table[command].context = context; -} - -uint8_t si_command_get_length(uint8_t command) -{ - return command_table[command].length; -} - -si_command_handler_fn si_command_get_handler(uint8_t command) -{ - return command_table[command].handler; -} - -void si_command_process() -{ - if (command_state == COMMAND_STATE_ERROR) { - si_await_bus_idle(); - command_state = COMMAND_STATE_IDLE; - } - - if (command_state == COMMAND_STATE_IDLE) { - command_state = COMMAND_STATE_RX; - si_read_command(command_buffer, on_rx_complete); - } -} - -// Command handler TX completion callback -static void on_tx_complete(int result) -{ - if (result == 0) { - // Check if we need to transition back to RX mode - if (auto_tx_rx_transition) { - command_state = COMMAND_STATE_RX; - si_read_command(command_buffer, on_rx_complete); - } else { - command_state = COMMAND_STATE_IDLE; - } - } else { - command_state = COMMAND_STATE_ERROR; - } -} - -// Command handler RX completion callback -static void on_rx_complete(int result) -{ - if (result == 0) { - // Look up the command in the table - struct command_entry *command = &command_table[command_buffer[0]]; - if (command->handler) { - // Call the command handler - int rc = command->handler(command_buffer, on_tx_complete, command->context); - if (rc > 0) { - // Response will be sent asynchronously, wait for TX complete callback - command_state = COMMAND_STATE_TX; - return; - } else if (rc == 0) { - // No response sent, immediately go back to idle - command_state = COMMAND_STATE_IDLE; - return; - } - } - } - - // Error during command read or handler not found - command_state = COMMAND_STATE_ERROR; -} \ No newline at end of file diff --git a/firmware/libsi/src/crc8.c b/firmware/libsi/src/crc8.c deleted file mode 100644 index e15c00c..0000000 --- a/firmware/libsi/src/crc8.c +++ /dev/null @@ -1,32 +0,0 @@ -#include "si/crc8.h" - -// CRC-8 lookup table for polynomial 0x85 -const uint8_t CRC8_LUT[] = { - 0x00, 0x85, 0x8F, 0x0A, 0x9B, 0x1E, 0x14, 0x91, 0xB3, 0x36, 0x3C, 0xB9, 0x28, 0xAD, 0xA7, 0x22, 0xE3, 0x66, 0x6C, - 0xE9, 0x78, 0xFD, 0xF7, 0x72, 0x50, 0xD5, 0xDF, 0x5A, 0xCB, 0x4E, 0x44, 0xC1, 0x43, 0xC6, 0xCC, 0x49, 0xD8, 0x5D, - 0x57, 0xD2, 0xF0, 0x75, 0x7F, 0xFA, 0x6B, 0xEE, 0xE4, 0x61, 0xA0, 0x25, 0x2F, 0xAA, 0x3B, 0xBE, 0xB4, 0x31, 0x13, - 0x96, 0x9C, 0x19, 0x88, 0x0D, 0x07, 0x82, 0x86, 0x03, 0x09, 0x8C, 0x1D, 0x98, 0x92, 0x17, 0x35, 0xB0, 0xBA, 0x3F, - 0xAE, 0x2B, 0x21, 0xA4, 0x65, 0xE0, 0xEA, 0x6F, 0xFE, 0x7B, 0x71, 0xF4, 0xD6, 0x53, 0x59, 0xDC, 0x4D, 0xC8, 0xC2, - 0x47, 0xC5, 0x40, 0x4A, 0xCF, 0x5E, 0xDB, 0xD1, 0x54, 0x76, 0xF3, 0xF9, 0x7C, 0xED, 0x68, 0x62, 0xE7, 0x26, 0xA3, - 0xA9, 0x2C, 0xBD, 0x38, 0x32, 0xB7, 0x95, 0x10, 0x1A, 0x9F, 0x0E, 0x8B, 0x81, 0x04, 0x89, 0x0C, 0x06, 0x83, 0x12, - 0x97, 0x9D, 0x18, 0x3A, 0xBF, 0xB5, 0x30, 0xA1, 0x24, 0x2E, 0xAB, 0x6A, 0xEF, 0xE5, 0x60, 0xF1, 0x74, 0x7E, 0xFB, - 0xD9, 0x5C, 0x56, 0xD3, 0x42, 0xC7, 0xCD, 0x48, 0xCA, 0x4F, 0x45, 0xC0, 0x51, 0xD4, 0xDE, 0x5B, 0x79, 0xFC, 0xF6, - 0x73, 0xE2, 0x67, 0x6D, 0xE8, 0x29, 0xAC, 0xA6, 0x23, 0xB2, 0x37, 0x3D, 0xB8, 0x9A, 0x1F, 0x15, 0x90, 0x01, 0x84, - 0x8E, 0x0B, 0x0F, 0x8A, 0x80, 0x05, 0x94, 0x11, 0x1B, 0x9E, 0xBC, 0x39, 0x33, 0xB6, 0x27, 0xA2, 0xA8, 0x2D, 0xEC, - 0x69, 0x63, 0xE6, 0x77, 0xF2, 0xF8, 0x7D, 0x5F, 0xDA, 0xD0, 0x55, 0xC4, 0x41, 0x4B, 0xCE, 0x4C, 0xC9, 0xC3, 0x46, - 0xD7, 0x52, 0x58, 0xDD, 0xFF, 0x7A, 0x70, 0xF5, 0x64, 0xE1, 0xEB, 0x6E, 0xAF, 0x2A, 0x20, 0xA5, 0x34, 0xB1, 0xBB, - 0x3E, 0x1C, 0x99, 0x93, 0x16, 0x87, 0x02, 0x08, 0x8D, -}; - -uint8_t si_crc8(uint8_t *data, size_t size) -{ - uint8_t val = 0; - uint8_t *end = data + size; - - while (data < end) { - val = CRC8_LUT[val ^ *data]; - data++; - } - - return val; -} diff --git a/firmware/libsi/src/device/gc_controller.c b/firmware/libsi/src/device/gc_controller.c deleted file mode 100644 index c6e2d0d..0000000 --- a/firmware/libsi/src/device/gc_controller.c +++ /dev/null @@ -1,362 +0,0 @@ -#include - -#include "si/commands.h" -#include "si/device/gc_controller.h" - -/* - * Pack an "full" input state into a "short" input state, depending on the analog mode. - * - * The "short poll" command used by games expects 8-byte responses, this is presumably - * so it fit in a nice round multiple of 32-bit words. - * - * The full input state is 10 bytes long, so there are various ways to "pack" the input - * state into 8 bytes. Depending on the analog mode, either one pair of analog inputs - * can be omitted, or two pairs of analog inputs can be truncated to 4 bits. - * - * All production games, with the exception of Luigi's Mansion, use analog mode 3. This - * mode omits the analog A/B inputs, and sends the substick X/Y and triggers at full - * precision. Analog A/B buttons were only present in pre-production GameCube - * controllers. - */ -static uint8_t *pack_input_state(struct si_device_gc_input_state *src, uint8_t analog_mode) -{ - // Buffer for packed input state - static uint8_t packed_state[8]; - - // Copy the button and stick data - memcpy(packed_state, src, 4); - - // Pack the remaining analog input data - switch (analog_mode) { - default: - // Substick X/Y full precision, triggers and analog A/B truncated to 4 bits - packed_state[4] = src->substick_x; - packed_state[5] = src->substick_y; - packed_state[6] = (src->trigger_left & 0xF0) | (src->trigger_right >> 4); - packed_state[7] = (src->analog_a & 0xF0) | (src->analog_b >> 4); - break; - case SI_DEVICE_GC_ANALOG_MODE_1: - // Triggers full precision, substick X/Y and analog A/B truncated to 4 bits - packed_state[4] = (src->substick_x & 0xF0) | (src->substick_y >> 4); - packed_state[5] = src->trigger_left; - packed_state[6] = src->trigger_right; - packed_state[7] = (src->analog_a & 0xF0) | (src->analog_b >> 4); - break; - case SI_DEVICE_GC_ANALOG_MODE_2: - // Analog A/B full precision, substick X/Y and triggers truncated to 4 bits - packed_state[4] = (src->substick_x & 0xF0) | (src->substick_y >> 4); - packed_state[5] = (src->trigger_left & 0xF0) | (src->trigger_right >> 4); - packed_state[6] = src->analog_a; - packed_state[7] = src->analog_b; - break; - case SI_DEVICE_GC_ANALOG_MODE_3: - // Substick X/Y and triggers full precision, analog A/B omitted - packed_state[4] = src->substick_x; - packed_state[5] = src->substick_y; - packed_state[6] = src->trigger_left; - packed_state[7] = src->trigger_right; - break; - case SI_DEVICE_GC_ANALOG_MODE_4: - // Substick X/Y and analog A/B full precision, triggers omitted - packed_state[4] = src->substick_x; - packed_state[5] = src->substick_y; - packed_state[6] = src->analog_a; - packed_state[7] = src->analog_b; - break; - } - - return packed_state; -} - -// Set or clear the "need origin" flag in the input state and device info -static void set_need_origin(struct si_device_gc_controller *device, bool need_origin) -{ - // Always set the need_origin flag in the input state - device->input.buttons.need_origin = need_origin; - - // Also update the device info for non-wireless controllers - if (!si_device_gc_is_wireless(device)) { - if (need_origin) { - device->info[2] |= SI_NEED_ORIGIN; - } else { - device->info[2] &= ~SI_NEED_ORIGIN; - } - } -} - -/** - * Handle "info" commands. - * - * Command: {0x00} - * Response: A 3-byte device info. - */ -static int handle_info(const uint8_t *command, si_callback_fn callback, void *context) -{ - struct si_device_gc_controller *device = (struct si_device_gc_controller *)context; - - // Respond with the device info - si_write_bytes(device->info, SI_CMD_INFO_RESP, callback); - - return SI_CMD_INFO_RESP; -} - -/** - * Handle "reset" commands. - * - * Command: {0xFF} - * Response: A 3-byte device info. - */ -static int handle_reset(const uint8_t *command, si_callback_fn callback, void *context) -{ - struct si_device_gc_controller *device = (struct si_device_gc_controller *)context; - - // TODO: Stop the rumble motor, if active - - // Respond with the device type and status - si_write_bytes(device->info, SI_CMD_RESET_RESP, callback); - - return SI_CMD_RESET_RESP; -} - -/** - * Handle "short poll" commands, to fetch the current input state. - * - * Command: {0x40, analog_mode, motor_state} - * Response: An 8-byte packed input state, see `pack_input_state` for details - */ -static int handle_short_poll(const uint8_t *command, si_callback_fn callback, void *context) -{ - struct si_device_gc_controller *device = (struct si_device_gc_controller *)context; - - // Extract the analog mode and motor state from the command - uint8_t analog_mode = command[1] & 0x07; - uint8_t motor_state = command[2] & 0x03; - - if (!si_device_gc_is_wireless(device)) { - // Update the origin flags - device->input.buttons.use_origin = true; - - // Save the analog mode and motor state - device->info[2] &= ~(SI_MOTOR_STATE_MASK | SI_ANALOG_MODE_MASK); - device->info[2] |= motor_state << 3 | analog_mode; - } - - // If the input state is valid, use that for the response, otherwise use the origin - struct si_device_gc_input_state *state = device->input_valid ? &device->input : &device->origin; - - // Most games use analog mode 3, which is just the first 8 bytes of the full input state - // Otherwise, pack the input state based on the analog mode - uint8_t *short_state; - if (analog_mode == SI_DEVICE_GC_ANALOG_MODE_3) { - short_state = (uint8_t *)state; - } else { - short_state = pack_input_state(state, analog_mode); - } - - // Respond with the 8-byte "short" input state - si_write_bytes(short_state, SI_CMD_GC_SHORT_POLL_RESP, callback); - - return SI_CMD_GC_SHORT_POLL_RESP; -} - -/** - * Handle "read origin" commands. - * - * Command: {0x41} - * Response: A 10-byte input state representing the current origin. - */ -static int handle_read_origin(const uint8_t *command, si_callback_fn callback, void *context) -{ - struct si_device_gc_controller *device = (struct si_device_gc_controller *)context; - - // Clear the "need origin" flag - set_need_origin(device, false); - - // Respond with the origin - si_write_bytes((uint8_t *)(&device->origin), SI_CMD_GC_READ_ORIGIN_RESP, callback); - - return SI_CMD_GC_READ_ORIGIN_RESP; -} - -/** - * Handle "calibrate" commands. - * - * Command: {0x42, 0x00, 0x00} - * Response: A 10-byte input state representing the current origin. - */ -static int handle_calibrate(const uint8_t *command, si_callback_fn callback, void *context) -{ - struct si_device_gc_controller *device = (struct si_device_gc_controller *)context; - - // Set current analog input state as the origin - device->origin.stick_x = device->input.stick_x; - device->origin.stick_y = device->input.stick_y; - device->origin.substick_x = device->input.substick_x; - device->origin.substick_y = device->input.substick_y; - device->origin.trigger_left = device->input.trigger_left; - device->origin.trigger_right = device->input.trigger_right; - - // Clear the "need origin" flag - set_need_origin(device, false); - - // Respond with the new origin - si_write_bytes((uint8_t *)(&device->origin), SI_CMD_GC_CALIBRATE_RESP, callback); - - return SI_CMD_GC_CALIBRATE_RESP; -} - -/** - * Handle "long poll" commands, to fetch the current input state with full precision. - * - * Command Format: {0x43, analog_mode, motor_state} - * Response: A 10-byte input state. - * - * NOTE: This command is not used by any games, but is included for completeness. - */ -static int handle_long_poll(const uint8_t *command, si_callback_fn callback, void *context) -{ - struct si_device_gc_controller *device = (struct si_device_gc_controller *)context; - - // Extract the analog mode and motor state from the command - uint8_t analog_mode = command[1] & 0x07; - uint8_t motor_state = command[2] & 0x03; - - if (!si_device_gc_is_wireless(device)) { - // Update the origin flags - device->input.buttons.use_origin = true; - - // Save the analog mode and motor state - device->info[2] &= ~(SI_MOTOR_STATE_MASK | SI_ANALOG_MODE_MASK); - device->info[2] |= motor_state << 3 | analog_mode; - } - - // If the input state is valid, use that for the response, otherwise use the origin - struct si_device_gc_input_state *state = device->input_valid ? &device->input : &device->origin; - - // Respond with the current input state - si_write_bytes((uint8_t *)state, SI_CMD_GC_LONG_POLL_RESP, callback); - - return SI_CMD_GC_LONG_POLL_RESP; -} - -/** - * Handle "probe device" commands. - * - * Probe device is exclusively used by "launch window" games, I'm assuming to detect - * capabilities of wireless controllers that were never actually released. Later games - * will just use the "info" command to detect if a controller is wireless. - * - * An OEM WaveBird receiver will respond to this command with 8 bytes of zeroes until - * it has received packets from a controller, at which point it will ignore further - * probe commands. - * - * Command: {0x4D, 0x??, 0x??} - 2nd and 3rd bytes seem to differ every time - * Response: 8 bytes of zeroes. - */ -static int handle_probe_device(const uint8_t *command, si_callback_fn callback, void *context) -{ - struct si_device_gc_controller *device = (struct si_device_gc_controller *)context; - - // Don't respond to probe commands if we already received data from a controller - if (device->info[0] & SI_WIRELESS_RECEIVED) - return 0; - - // Respond with 8 bytes of zeroes - uint8_t response[8] = {0}; - si_write_bytes(response, SI_CMD_GC_PROBE_DEVICE_RESP, callback); - - return SI_CMD_GC_PROBE_DEVICE_RESP; -} - -/** - * Handle "fix device" commands, to "fix" the receiver ID to a specific controller ID. - * - * This is used to pair a WaveBird controller with a specific receiver. - * - * Command: {0x4E, wireless_id_h | SI_WIRELESS_FIX_ID, wireless_id_l} - * Response: A 3-byte device info. - */ -static int handle_fix_device(const uint8_t *command, si_callback_fn callback, void *context) -{ - struct si_device_gc_controller *device = (struct si_device_gc_controller *)context; - - // Extract the wireless ID from the command - uint16_t wireless_id = ((command[1] & 0xC0) << 2) | command[2]; - - // Set the wireless id in the device info - device->info[1] = (device->info[1] & ~0xC0) | ((wireless_id >> 2) & 0xC0); - device->info[2] = wireless_id & 0xFF; - - // Update other device info flags - device->info[0] |= SI_GC_STANDARD | SI_WIRELESS_STATE; - device->info[1] |= SI_WIRELESS_FIX_ID; - - // Respond with the new device info - si_write_bytes(device->info, SI_CMD_GC_FIX_DEVICE_RESP, callback); - - return SI_CMD_GC_FIX_DEVICE_RESP; -} - -void si_device_gc_init(struct si_device_gc_controller *device, uint8_t type) -{ - // Set the initial device info flags - device->info[0] = type; - device->info[1] = 0x00; - device->info[2] = 0x00; - - // Set the initial origin - memset(&device->origin, 0, sizeof(device->origin)); - device->origin.stick_x = 0x80; - device->origin.stick_y = 0x80; - device->origin.substick_x = 0x80; - device->origin.substick_y = 0x80; - - // Set the initial input state - memcpy(&device->input, &device->origin, sizeof(device->input)); - - // Mark the input as valid initially - device->input_valid = true; - - // Register the SI commands handled by GameCube controllers - si_command_register(SI_CMD_INFO, SI_CMD_INFO_LEN, handle_info, device); - si_command_register(SI_CMD_GC_SHORT_POLL, SI_CMD_GC_SHORT_POLL_LEN, handle_short_poll, device); - si_command_register(SI_CMD_GC_READ_ORIGIN, SI_CMD_GC_READ_ORIGIN_LEN, handle_read_origin, device); - si_command_register(SI_CMD_GC_CALIBRATE, SI_CMD_GC_CALIBRATE_LEN, handle_calibrate, device); - si_command_register(SI_CMD_GC_LONG_POLL, SI_CMD_GC_LONG_POLL_LEN, handle_long_poll, device); - si_command_register(SI_CMD_RESET, SI_CMD_RESET_LEN, handle_reset, device); - - // Register additional commands handled by WaveBird receivers - if (si_device_gc_is_wireless(device)) { - si_command_register(SI_CMD_GC_PROBE_DEVICE, SI_CMD_GC_PROBE_DEVICE_LEN, handle_probe_device, device); - si_command_register(SI_CMD_GC_FIX_DEVICE, SI_CMD_GC_FIX_DEVICE_LEN, handle_fix_device, device); - } -} - -void si_device_gc_set_wireless_id(struct si_device_gc_controller *device, uint16_t wireless_id) -{ - if (si_device_gc_wireless_id_fixed(device)) - return; - - // Set the wireless ID in the device info - device->info[1] = (device->info[1] & ~0xC0) | ((wireless_id >> 2) & 0xC0); - device->info[2] = wireless_id & 0xFF; - - // Update other device info flags - device->info[0] |= SI_GC_STANDARD | SI_WIRELESS_RECEIVED; -} - -void si_device_gc_set_origin(struct si_device_gc_controller *device, struct si_device_gc_input_state *new_origin) -{ - // Check if the origin packet is different from the last known origin - if (memcmp(&device->origin.stick_x, &new_origin->stick_x, 6) != 0) { - // Update the origin state - memcpy(&device->origin.stick_x, &new_origin->stick_x, 6); - - // Tell the host that new origin data is available - set_need_origin(device, true); - } - - // Set the "has wireless origin" flag in the device info - if (si_device_gc_is_wireless(device)) - device->info[1] |= SI_WIRELESS_ORIGIN; -} \ No newline at end of file diff --git a/firmware/libsi/src/platform/efr32/si_efr32.c b/firmware/libsi/src/platform/efr32/si_efr32.c deleted file mode 100644 index 01851e5..0000000 --- a/firmware/libsi/src/platform/efr32/si_efr32.c +++ /dev/null @@ -1,324 +0,0 @@ -#include - -#include "em_cmu.h" -#include "em_gpio.h" -#include "em_ldma.h" -#include "em_timer.h" -#include "em_usart.h" - -#include "dmadrv.h" - -#include "si/commands.h" - -// RX peripheral configuration -#define SI_RX_TIMER TIMER0 -#define SI_RX_TIMER_IDX 0 -#define SI_RX_TIMER_CLK cmuClock_TIMER0 -#define SI_RX_LDMA_PERIPHERAL ldmaPeripheralSignal_TIMER0_CC0 - -// TX peripheral configuration -#define SI_TX_USART USART0 -#define SI_TX_USART_IDX 0 -#define SI_TX_USART_CLK cmuClock_USART0 -#define SI_TX_USART_IRQn USART0_TX_IRQn -#define SI_TX_USART_IRQHandler USART0_TX_IRQHandler -#define SI_TX_LDMA_PERIPHERAL ldmaPeripheralSignal_USART0_TXBL - -// Number of chips per bit for the line coding -#define CHIPS_PER_BIT 4 - -// Line coding (inverted, since we're inverting the USART output) -#define BIT_0 0b1110 -#define BIT_1 0b1000 -#define DEVICE_STOP 0b1100 -#define HOST_STOP 0b1000 - -// SI bus idle period (in microseconds) -#define BUS_IDLE_US 100 - -// RX buffer size (16 edges per byte) -#define RX_BUFFER_SIZE 16 - -// TX buffer size (4 chips per bit, extra byte for stop bit) -#define TX_BUFFER_SIZE (SI_BLOCK_SIZE * CHIPS_PER_BIT + 1) - -// SI configuration -static uint8_t si_data_port; -static uint8_t si_data_pin; -static bool si_mode; - -// RX state -static uint16_t rx_edge_timings[2][RX_BUFFER_SIZE]; -static uint16_t rx_pulse_period_half; -static uint16_t rx_bus_idle_period; -static unsigned int rx_dma_channel; - -// TX State -static uint8_t tx_buffer[TX_BUFFER_SIZE]; -static unsigned int tx_dma_channel; - -// Transfer state -static struct { - uint8_t *data; - uint8_t length; - si_callback_fn callback; -} si_xfer; - -static void init_rx(uint8_t port, uint8_t pin, uint32_t freq); -static void init_tx(uint8_t port, uint8_t pin, uint32_t freq); -static uint8_t *encode_byte(uint8_t *dest, uint8_t src); -static void decode_edge_timings(uint8_t *dest, uint16_t *src); -static bool ldma_callback_rx(unsigned int chan, unsigned int iteration, void *user_data); - -void si_init(uint8_t port, uint8_t pin, uint8_t mode, uint32_t rx_freq, uint32_t tx_freq) -{ - // Initialize LDMA - DMADRV_Init(); - - // Use the HFXO as the TIMER clock source - CMU_ClockSelectSet(cmuClock_EM01GRPACLK, cmuSelect_HFXO); - - // Enable clocks - CMU_ClockEnable(cmuClock_GPIO, true); - - // Set the SI data line as open-drain output - GPIO_PinModeSet(port, pin, gpioModeWiredAnd, 1); - - // Initialize SI RX and TX - init_rx(port, pin, rx_freq); - init_tx(port, pin, tx_freq); - - // Save the SI configuration - si_data_port = port; - si_data_pin = pin; - si_mode = mode; -} - -void si_write_bytes(const uint8_t *bytes, uint8_t length, si_callback_fn callback) -{ - // Save the transfer state - si_xfer.data = (uint8_t *)bytes; - si_xfer.length = length; - si_xfer.callback = callback; - - // Convert the bytes to appropriate line coding and add to the buffer - uint8_t *buf_ptr = tx_buffer; - for (int i = 0; i < length; i++) - buf_ptr = encode_byte(buf_ptr, bytes[i]); - - // Add the stop bit - buf_ptr[0] = (si_mode == SI_MODE_HOST ? HOST_STOP : DEVICE_STOP) << 4; - - // Start the DMA transfer - DMADRV_MemoryPeripheral(tx_dma_channel, SI_TX_LDMA_PERIPHERAL, (void *)&(SI_TX_USART->TXDATA), tx_buffer, true, - length * CHIPS_PER_BIT + 1, dmadrvDataSize1, NULL, NULL); -} - -void si_read_bytes(uint8_t *buffer, uint8_t length, si_callback_fn callback) -{ - // Save the transfer state - si_xfer.data = buffer; - si_xfer.length = length; - si_xfer.callback = callback; - - // Clear the RX buffer - while (TIMER_CaptureGet(SI_RX_TIMER, 0)) - ; - - // Start the input capture timer - TIMER_Enable(SI_RX_TIMER, true); - - // Start the LDMA transfer - DMADRV_PeripheralMemoryPingPong(rx_dma_channel, SI_RX_LDMA_PERIPHERAL, rx_edge_timings[0], rx_edge_timings[1], - (void *)&(SI_RX_TIMER->CC[0].ICF), true, 16, dmadrvDataSize2, ldma_callback_rx, NULL); -} - -void si_read_command(uint8_t *buffer, si_callback_fn callback) -{ - si_read_bytes(buffer, 0, callback); -} - -void si_await_bus_idle(void) -{ - // Start the timer - TIMER_Enable(SI_RX_TIMER, true); - - while (1) { - // Wait for the line to go high - while (GPIO_PinInGet(si_data_port, si_data_pin) == 0) - ; - - // Start timing the bus idle period - TIMER_CounterSet(SI_RX_TIMER, 0); - - // Wait for either the bus idle period to elapse or line to go low - while (GPIO_PinInGet(si_data_port, si_data_pin) == 1) { - if (TIMER_CounterGet(SI_RX_TIMER) >= rx_bus_idle_period) - goto idle_detected; - } - } - -idle_detected: - // Stop the timer - TIMER_Enable(SI_RX_TIMER, false); -} - -// Initialize for SI pulse capture -static void init_rx(uint8_t port, uint8_t pin, uint32_t freq) -{ - // Allocate a DMA channel - DMADRV_AllocateChannel(&rx_dma_channel, NULL); - - // Set up the timings for rx pulses - uint32_t rx_timer_freq = CMU_ClockFreqGet(SI_RX_TIMER_CLK); - rx_pulse_period_half = (rx_timer_freq / freq) / 2; - rx_bus_idle_period = rx_timer_freq / 1000000UL * BUS_IDLE_US; - - // Enable clocks - CMU_ClockEnable(SI_RX_TIMER_CLK, true); - - // Initialize timer - TIMER_Init_TypeDef timerInit = TIMER_INIT_DEFAULT; - timerInit.enable = false; - TIMER_Init(SI_RX_TIMER, &timerInit); - - // Configure CC0 for pulse width capture - TIMER_InitCC_TypeDef timerCCInit = TIMER_INITCC_DEFAULT; - timerCCInit.edge = timerEdgeBoth; - timerCCInit.mode = timerCCModeCapture; - TIMER_InitCC(SI_RX_TIMER, 0, &timerCCInit); - - // Route timer capture input to the SI GPIO - GPIO->TIMERROUTE[SI_RX_TIMER_IDX].ROUTEEN = GPIO_TIMER_ROUTEEN_CC0PEN; - GPIO->TIMERROUTE[SI_RX_TIMER_IDX].CC0ROUTE = - (port << _GPIO_TIMER_CC0ROUTE_PORT_SHIFT) | (pin << _GPIO_TIMER_CC0ROUTE_PIN_SHIFT); - - // Set LDMA interrupts as high priority, since we need to reply immediately on completed RX - NVIC_SetPriority(LDMA_IRQn, 0); -} - -// Initialize for SI data transmission -static void init_tx(uint8_t port, uint8_t pin, uint32_t freq) -{ - // Allocate a DMA channel - DMADRV_AllocateChannel(&tx_dma_channel, NULL); - - // Enable clocks - CMU_ClockEnable(SI_TX_USART_CLK, true); - - // Initialize USART - USART_InitSync_TypeDef usartConfig = USART_INITSYNC_DEFAULT; - usartConfig.baudrate = freq * CHIPS_PER_BIT; - usartConfig.msbf = true; - USART_InitSync(SI_TX_USART, &usartConfig); - - // Invert the TX output so we have an active-low signal - SI_TX_USART->CTRL_SET = USART_CTRL_TXINV; - - // Route USART output to the SI GPIO - GPIO->USARTROUTE[SI_TX_USART_IDX].ROUTEEN = GPIO_USART_ROUTEEN_TXPEN; - GPIO->USARTROUTE[SI_TX_USART_IDX].TXROUTE = - (port << _GPIO_USART_TXROUTE_PORT_SHIFT) | (pin << _GPIO_USART_TXROUTE_PIN_SHIFT); - - // Enable USART TX complete interrupts - USART_IntEnable(SI_TX_USART, USART_IF_TXC); - NVIC_EnableIRQ(SI_TX_USART_IRQn); -} - -// Process received SI edge timings into a byte -static void decode_edge_timings(uint8_t *dest, uint16_t *src) -{ - // Clear the destination byte - *dest = 0; - - // Write out the byte, most significant bit first - for (int i = 7; i >= 0; i--) { - // Determine how long the SI line was low - // NOTE: We're explicitly casting back to uint16_t to handle timer overflow - uint16_t ticks_low = (uint16_t)(src[1] - src[0]); - - // Set the bit based on the low period of the pulse - *dest |= (ticks_low < rx_pulse_period_half) << i; - - // Move to the next pair of edges - src += 2; - } -} - -// Convert a byte to the appropriate line coding for transmission -static uint8_t *encode_byte(uint8_t *dest, uint8_t src) -{ - uint8_t bit_7 = (src & 0x80) ? BIT_1 << 4 : BIT_0 << 4; - uint8_t bit_6 = (src & 0x40) ? BIT_1 : BIT_0; - *dest++ = bit_7 | bit_6; - - uint8_t bit_5 = (src & 0x20) ? BIT_1 << 4 : BIT_0 << 4; - uint8_t bit_4 = (src & 0x10) ? BIT_1 : BIT_0; - *dest++ = bit_5 | bit_4; - - uint8_t bit_3 = (src & 0x08) ? BIT_1 << 4 : BIT_0 << 4; - uint8_t bit_2 = (src & 0x04) ? BIT_1 : BIT_0; - *dest++ = bit_3 | bit_2; - - uint8_t bit_1 = (src & 0x02) ? BIT_1 << 4 : BIT_0 << 4; - uint8_t bit_0 = (src & 0x01) ? BIT_1 : BIT_0; - *dest++ = bit_1 | bit_0; - - return dest; -} - -// LDMA callback for RX data capture -static bool ldma_callback_rx(unsigned int chan, unsigned int iteration, void *user_data) -{ - // Iteration count is 1-indexed - uint8_t byte_idx = iteration - 1; - - // Process the received pulses into the byte buffer - decode_edge_timings(&si_xfer.data[byte_idx], rx_edge_timings[byte_idx % 2]); - - // If this is the first byte, determine how many bytes are expected - if (si_xfer.length == 0 && iteration == 1) { - si_xfer.length = si_command_get_length(si_xfer.data[0]); - - // Unknown command, stop the transfer - if (si_xfer.length == 0) { - // Don't clock in any more data - TIMER_Enable(SI_RX_TIMER, false); - - // Call the transfer callback if one is set - if (si_xfer.callback) - si_xfer.callback(-SI_ERR_UNKNOWN_COMMAND); - - // Stop the LDMA chain - return false; - } - } - - // We have all the bytes we expected - if (iteration == si_xfer.length) { - // Don't clock in any more data - TIMER_Enable(SI_RX_TIMER, false); - - // Call the transfer callback if one is set - if (si_xfer.callback) - si_xfer.callback(0); - - // Stop the LDMA chain - return false; - } - - // Continue the LDMA chain - return true; -} - -// USART TX complete interrupt handler -void SI_TX_USART_IRQHandler() -{ - // Clear the interrupt flags - uint32_t flags = USART_IntGet(SI_TX_USART); - USART_IntClear(SI_TX_USART, flags); - - // Call the transfer callback if one is set - if (si_xfer.callback) - si_xfer.callback(0); -} \ No newline at end of file diff --git a/firmware/libsi/test/CMakeLists.txt b/firmware/libsi/test/CMakeLists.txt deleted file mode 100644 index 55f0f5d..0000000 --- a/firmware/libsi/test/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -# Include the unity test framework -if(NOT unity_FOUND) - include(FetchContent) - FetchContent_Declare(Unity GIT_REPOSITORY https://github.com/ThrowTheSwitch/Unity.git) - FetchContent_MakeAvailable(Unity) -endif() - -# Define the test and set the sources -add_executable(test_si "test_main.c" "test_commands.c" "test_gc_controller.c") - -# Link dependencies -target_link_libraries(test_si si unity::framework) \ No newline at end of file diff --git a/firmware/libsi/test/test_commands.c b/firmware/libsi/test/test_commands.c deleted file mode 100644 index 107301e..0000000 --- a/firmware/libsi/test/test_commands.c +++ /dev/null @@ -1,44 +0,0 @@ -#include "unity.h" - -#include "si/commands.h" -#include "si/si.h" - -// Stub functions -void si_await_bus_idle(void) -{ -} - -static int handle_info(const uint8_t *command, si_callback_fn callback, void *context) -{ - return 3; -} - -static int handle_reset(const uint8_t *command, si_callback_fn callback, void *context) -{ - return 3; -} - -static void test_register_command() -{ - si_command_register(0x00, 1, handle_info, NULL); - TEST_ASSERT_EQUAL(1, si_command_get_length(0x00)); - TEST_ASSERT_EQUAL(handle_info, si_command_get_handler(0x00)); - - si_command_register(0xFF, 3, handle_reset, NULL); - TEST_ASSERT_EQUAL(3, si_command_get_length(0xFF)); - TEST_ASSERT_EQUAL(handle_reset, si_command_get_handler(0xFF)); -} - -static void test_register_command_missing() -{ - TEST_ASSERT_EQUAL(0, si_command_get_length(0x69)); - TEST_ASSERT_NULL(si_command_get_handler(0x69)); -} - -void test_commands(void) -{ - Unity.TestFile = __FILE_NAME__; - - RUN_TEST(test_register_command); - RUN_TEST(test_register_command_missing); -} \ No newline at end of file diff --git a/firmware/libsi/test/test_gc_controller.c b/firmware/libsi/test/test_gc_controller.c deleted file mode 100644 index cdf16ec..0000000 --- a/firmware/libsi/test/test_gc_controller.c +++ /dev/null @@ -1,376 +0,0 @@ -#include -#include - -#include "unity.h" - -#include "si/commands.h" -#include "si/device/gc_controller.h" -#include "si/si.h" - -// Mock SI implementation -static uint8_t response_buf[SI_BLOCK_SIZE] = {0}; -static uint8_t response_len = 0; - -void si_write_bytes(const uint8_t *data, uint8_t length, si_callback_fn callback) -{ - memcpy(response_buf, data, length); - response_len = length; -} - -void si_read_command(uint8_t *data, si_callback_fn callback) -{ -} - -// Simulate receiving a command -static int simulate_command(struct si_device_gc_controller *device, uint8_t *command) -{ - si_command_handler_fn handler = si_command_get_handler(command[0]); - return handler(command, NULL, device); -} - -// Test that the device info response is correct for a standard GameCube controller -static void test_gcc_info() -{ - // Initialize as a standard GameCube controller - struct si_device_gc_controller device; - si_device_gc_init(&device, SI_TYPE_GC | SI_GC_STANDARD); - - // Send an info command - uint8_t info_command[] = {SI_CMD_INFO}; - simulate_command(&device, info_command); - - // Test device info response is as expected - uint8_t expected_response[] = {0x09, 0x00, 0x00}; - TEST_ASSERT_EQUAL(3, response_len); - TEST_ASSERT_EQUAL_HEX8_ARRAY(expected_response, response_buf, 3); -} - -// Test that the "need origin" flag is cleared after a "read origin" command -static void test_gcc_info_after_read_origin() -{ - // Initialize as a standard GameCube controller - struct si_device_gc_controller device; - si_device_gc_init(&device, SI_TYPE_GC | SI_GC_STANDARD); - - // Set an origin - struct si_device_gc_input_state new_origin = { - .stick_x = 0x81, - .stick_y = 0x82, - .substick_x = 0x83, - .substick_y = 0x84, - .trigger_left = 0x11, - .trigger_right = 0x12, - }; - si_device_gc_set_origin(&device, &new_origin); - - // Send an info command - uint8_t info_command[] = {SI_CMD_INFO}; - simulate_command(&device, info_command); - - // Test device info response is as expected - uint8_t expected_response[] = {0x09, 0x00, 0x20}; - TEST_ASSERT_EQUAL(3, response_len); - TEST_ASSERT_EQUAL_HEX8_ARRAY(expected_response, response_buf, 3); - - // Send a read origin command - uint8_t read_origin_command[] = {SI_CMD_GC_READ_ORIGIN}; - simulate_command(&device, read_origin_command); - - // Send another info command - simulate_command(&device, info_command); - - // Verify the "need_origin" flag is not present - uint8_t expected_response_2[] = {0x09, 0x00, 0x00}; - TEST_ASSERT_EQUAL(3, response_len); - TEST_ASSERT_EQUAL_HEX8_ARRAY(expected_response_2, response_buf, 3); -} - -// Test that "analog mode" and "motor state" are saved after a "poll" command -static void test_gcc_info_after_poll() -{ - // Initialize as a standard GameCube controller - struct si_device_gc_controller device; - si_device_gc_init(&device, SI_TYPE_GC | SI_GC_STANDARD); - - // Send a read origin command - uint8_t read_origin_command[] = {SI_CMD_GC_READ_ORIGIN}; - simulate_command(&device, read_origin_command); - - // Send a poll command, analog_mode = 3, motor_state = 1 - uint8_t poll_command[] = {SI_CMD_GC_SHORT_POLL, 3, 1}; - simulate_command(&device, poll_command); - - // Send an info command - uint8_t info_command[] = {SI_CMD_INFO}; - simulate_command(&device, info_command); - - // Verify the "analog_mode" and "motor_state" are present - uint8_t expected_response[] = {0x09, 0x00, 0x0B}; - TEST_ASSERT_EQUAL(3, response_len); - TEST_ASSERT_EQUAL_HEX8_ARRAY(expected_response, response_buf, 3); -} - -// Test that the device info response is correct for a WaveBird receiver -static void test_wavebird_info() -{ - // Initialize as a WaveBird receiver - struct si_device_gc_controller device; - si_device_gc_init(&device, SI_TYPE_GC | SI_GC_WIRELESS | SI_GC_NOMOTOR); - - // Send an info command - uint8_t info_command[] = {SI_CMD_INFO}; - simulate_command(&device, info_command); - - // Test device info response is as expected - uint8_t expected_response[] = {0xA8, 0x00, 0x00}; - TEST_ASSERT_EQUAL(3, response_len); - TEST_ASSERT_EQUAL_HEX8_ARRAY(expected_response, response_buf, 3); -} - -// Test that the device info response is correct when setting the wireless ID -static void test_wavebird_info_after_set_wireless_id() -{ - // Initialize as a WaveBird receiver - struct si_device_gc_controller device; - si_device_gc_init(&device, SI_TYPE_GC | SI_GC_WIRELESS | SI_GC_NOMOTOR); - - // Set a 10-bit wireless ID - si_device_gc_set_wireless_id(&device, 0x2B1); - TEST_ASSERT_EQUAL(0x2B1, si_device_gc_get_wireless_id(&device)); - - // Send an info command - uint8_t info_command[] = {SI_CMD_INFO}; - simulate_command(&device, info_command); - - // Test device info response includes the controller ID - uint8_t expected_response[] = {0xE9, 0x80, 0xB1}; - TEST_ASSERT_EQUAL(3, response_len); - TEST_ASSERT_EQUAL_HEX8_ARRAY(expected_response, response_buf, 3); -} - -// Test that the device info response is correct when setting the wireless ID multiple times -static void test_wavebird_info_after_set_wireless_id_multiple() -{ - // Initialize as a WaveBird receiver - struct si_device_gc_controller device; - si_device_gc_init(&device, SI_TYPE_GC | SI_GC_WIRELESS | SI_GC_NOMOTOR); - - // Set a 10-bit wireless ID - si_device_gc_set_wireless_id(&device, 0x2B1); - TEST_ASSERT_EQUAL(0x2B1, si_device_gc_get_wireless_id(&device)); - - // Set a different 10-bit wireless ID - si_device_gc_set_wireless_id(&device, 0x32F); - TEST_ASSERT_EQUAL(0x32F, si_device_gc_get_wireless_id(&device)); - - // Send an info command - uint8_t info_command[] = {SI_CMD_INFO}; - simulate_command(&device, info_command); - - // Test device info response includes the most recent controller ID - uint8_t expected_response[] = {0xE9, 0xC0, 0x2F}; - TEST_ASSERT_EQUAL(3, response_len); - TEST_ASSERT_EQUAL_HEX8_ARRAY(expected_response, response_buf, 3); -} - -// Test that the device info response is correct when fixing the wireless ID -static void test_wavebird_info_after_fix_device() -{ - uint8_t info_command[] = {SI_CMD_INFO}; - - // Initialize as a WaveBird receiver - struct si_device_gc_controller device; - si_device_gc_init(&device, SI_TYPE_GC | SI_GC_WIRELESS | SI_GC_NOMOTOR); - - // Send an info command - simulate_command(&device, info_command); - - // Test device info response is as expected - uint8_t expected_info_response[] = {0xA8, 0x00, 0x00}; - TEST_ASSERT_EQUAL(3, response_len); - TEST_ASSERT_EQUAL_HEX8_ARRAY(expected_info_response, response_buf, 3); - - // Set the wireless ID (e.g. after packet reception) - si_device_gc_set_wireless_id(&device, 0x2B1); - - // Send an info command - simulate_command(&device, info_command); - - // Test device info response is as expected - uint8_t expected_info_response_2[] = {0xE9, 0x80, 0xB1}; - TEST_ASSERT_EQUAL(3, response_len); - TEST_ASSERT_EQUAL_HEX8_ARRAY(expected_info_response_2, response_buf, 3); - - // Send a fix device command - uint8_t fix_device_command[] = {SI_CMD_GC_FIX_DEVICE, 0x90, 0xB1}; - simulate_command(&device, fix_device_command); - - // Check fix device response is as expected - uint8_t expected_fix_response[] = {0xEB, 0x90, 0xB1}; - TEST_ASSERT_EQUAL(3, response_len); - TEST_ASSERT_EQUAL_HEX8_ARRAY(expected_fix_response, response_buf, 3); - - // Send an info command - simulate_command(&device, info_command); - - // Test device info response includes the fixed controller ID - uint8_t expected_response[] = {0xEB, 0x90, 0xB1}; - TEST_ASSERT_EQUAL(3, response_len); - TEST_ASSERT_EQUAL_HEX8_ARRAY(expected_response, response_buf, 3); -} - -// Test that the device info is correct when the console fixes the wireless ID, -// but we have not yet received a packet from the controller -static void test_wavebird_fix_device_without_wireless_id() -{ - uint8_t info_command[] = {SI_CMD_INFO}; - - // Initialize as a WaveBird receiver - struct si_device_gc_controller device; - si_device_gc_init(&device, SI_TYPE_GC | SI_GC_WIRELESS | SI_GC_NOMOTOR); - - // Send an info command - simulate_command(&device, info_command); - - // Test device info response is as expected - uint8_t expected_info_response[] = {0xA8, 0x00, 0x00}; - TEST_ASSERT_EQUAL(3, response_len); - TEST_ASSERT_EQUAL_HEX8_ARRAY(expected_info_response, response_buf, 3); - - // Send a fix device command - uint8_t fix_device_command[] = {SI_CMD_GC_FIX_DEVICE, 0x90, 0xB1}; - simulate_command(&device, fix_device_command); - - // Check fix device response is as expected - uint8_t expected_fix_response[] = {0xAB, 0x90, 0xB1}; - TEST_ASSERT_EQUAL(3, response_len); - TEST_ASSERT_EQUAL_HEX8_ARRAY(expected_fix_response, response_buf, 3); - - // Send an info command - simulate_command(&device, info_command); - - // Test device info response includes the fixed controller ID - uint8_t expected_info_response_2[] = {0xAB, 0x90, 0xB1}; - TEST_ASSERT_EQUAL(3, response_len); - TEST_ASSERT_EQUAL_HEX8_ARRAY(expected_info_response_2, response_buf, 3); -} - -// Test that setting wireless ID fails when the controller ID has already been fixed -static void test_set_wireless_id_when_fixed(void) -{ - // Initialize as a WaveBird receiver - struct si_device_gc_controller device; - si_device_gc_init(&device, SI_TYPE_GC | SI_GC_WIRELESS | SI_GC_NOMOTOR); - si_device_gc_set_wireless_id(&device, 0x2B1); - - // Send a fix device command - uint8_t fix_device_command[] = {SI_CMD_GC_FIX_DEVICE, 0x90, 0xB1}; - simulate_command(&device, fix_device_command); - - // Try to set a different 10-bit wireless ID - si_device_gc_set_wireless_id(&device, 0x123); - TEST_ASSERT_EQUAL_HEX16(0x2B1, si_device_gc_get_wireless_id(&device)); -} - -static void test_set_wireless_origin(void) -{ - uint8_t info_command[] = {SI_CMD_INFO}; - - // Initialize as a WaveBird receiver - struct si_device_gc_controller device; - si_device_gc_init(&device, SI_TYPE_GC | SI_GC_WIRELESS | SI_GC_NOMOTOR); - si_device_gc_set_wireless_id(&device, 0x2B1); - - // Send an info command - simulate_command(&device, info_command); - - // Test device info response is as expected - uint8_t expected_info_response[] = {0xE9, 0x80, 0xB1}; - TEST_ASSERT_EQUAL(3, response_len); - TEST_ASSERT_EQUAL_HEX8_ARRAY(expected_info_response, response_buf, 3); - - // Send a fix device command - uint8_t fix_device_command[] = {SI_CMD_GC_FIX_DEVICE, 0x90, 0xB1}; - simulate_command(&device, fix_device_command); - - // Send an info command - simulate_command(&device, info_command); - - // Test device info is as expected - uint8_t expected_info_response_2[] = {0xEB, 0x90, 0xB1}; - TEST_ASSERT_EQUAL(3, response_len); - TEST_ASSERT_EQUAL_HEX8_ARRAY(expected_info_response_2, response_buf, 3); - - // Set the wireless origin - struct si_device_gc_input_state origin = { - .stick_x = 0x85, - .stick_y = 0x86, - .substick_x = 0x87, - .substick_y = 0x88, - .trigger_left = 0x11, - .trigger_right = 0x12, - }; - si_device_gc_set_origin(&device, &origin); - - // Check the origin state is set correctly - TEST_ASSERT_EQUAL_UINT8(0x85, device.origin.stick_x); - TEST_ASSERT_EQUAL_UINT8(0x86, device.origin.stick_y); - TEST_ASSERT_EQUAL_UINT8(0x87, device.origin.substick_x); - TEST_ASSERT_EQUAL_UINT8(0x88, device.origin.substick_y); - TEST_ASSERT_EQUAL_UINT8(0x11, device.origin.trigger_left); - TEST_ASSERT_EQUAL_UINT8(0x12, device.origin.trigger_right); - - // Send an info command - simulate_command(&device, info_command); - - // Test device info response includes the origin flag - uint8_t expected_info_response_3[] = {0xEB, 0xB0, 0xB1}; - TEST_ASSERT_EQUAL(3, response_len); - TEST_ASSERT_EQUAL_HEX8_ARRAY(expected_info_response_3, response_buf, 3); -} - -// Test that the probe device response is correct, and ignored after setting the wireless ID -static void test_wavebird_probe_response() -{ - int rc; - - // Initialize as a WaveBird receiver - struct si_device_gc_controller device; - si_device_gc_init(&device, SI_TYPE_GC | SI_GC_WIRELESS | SI_GC_NOMOTOR); - - // Send a probe command - uint8_t probe_command[] = {SI_CMD_GC_PROBE_DEVICE, 0x00, 0x00}; - rc = simulate_command(&device, probe_command); - - // Test probe response is as expected - uint8_t expected_probe_response[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - TEST_ASSERT_EQUAL(8, rc); - TEST_ASSERT_EQUAL_HEX8_ARRAY(expected_probe_response, response_buf, 8); - - // Set a 10-bit wireless ID - si_device_gc_set_wireless_id(&device, 0x2B1); - TEST_ASSERT_EQUAL(0x2B1, si_device_gc_get_wireless_id(&device)); - - // Send another probe command - rc = simulate_command(&device, probe_command); - - // Test probe response is ignored after setting wireless ID - TEST_ASSERT_EQUAL(0, rc); -} - -void test_gc_controller(void) -{ - Unity.TestFile = __FILE_NAME__; - - RUN_TEST(test_gcc_info); - RUN_TEST(test_gcc_info_after_read_origin); - RUN_TEST(test_gcc_info_after_poll); - RUN_TEST(test_wavebird_info); - RUN_TEST(test_wavebird_info_after_set_wireless_id); - RUN_TEST(test_wavebird_info_after_set_wireless_id_multiple); - RUN_TEST(test_wavebird_info_after_fix_device); - RUN_TEST(test_set_wireless_id_when_fixed); - RUN_TEST(test_wavebird_fix_device_without_wireless_id); - RUN_TEST(test_set_wireless_origin); - RUN_TEST(test_wavebird_probe_response); -} \ No newline at end of file diff --git a/firmware/libsi/test/test_main.c b/firmware/libsi/test/test_main.c deleted file mode 100644 index a080b74..0000000 --- a/firmware/libsi/test/test_main.c +++ /dev/null @@ -1,27 +0,0 @@ -#include "unity.h" - -extern void test_commands(void); -extern void test_gc_controller(void); - -__attribute__((weak)) void suiteSetUp(void) -{ -} - -void setUp(void) -{ -} -void tearDown(void) -{ -} - -int main(int argc, char **argv) -{ - UNITY_BEGIN(); - - suiteSetUp(); - - test_commands(); - test_gc_controller(); - - return UNITY_END(); -} diff --git a/firmware/src/main.c b/firmware/src/main.c index e9e4422..40f948d 100644 --- a/firmware/src/main.c +++ b/firmware/src/main.c @@ -6,8 +6,8 @@ #include "em_gpio.h" #include "sl_main_init.h" -#include "si/commands.h" -#include "si/device/gc_controller.h" +#include +#include #include @@ -73,9 +73,10 @@ struct { uint8_t _reserved; } packet_stats = {0}; -// SI state -static struct si_device_gc_controller si_device = {0}; -static bool enable_si_command_handling = true; +// Joybus state +static struct joybus_gecko joybus_gecko; +static struct joybus_gc_controller joybus_gc_controller; +static struct joybus *joybus = JOYBUS(&joybus_gecko); // Buttons, switches, and LEDs static struct led *status_led = NULL; @@ -101,16 +102,18 @@ void SysTick_Handler(void) // Initialize (or reinitialize) as a controller on the SI bus static void initialize_controller(uint8_t controller_type) { - if (controller_type == WP_CONT_TYPE_GC_WAVEBIRD) { - // Present as an OEM WaveBird receiver - si_device_gc_init(&si_device, SI_TYPE_GC | SI_GC_WIRELESS | SI_GC_NOMOTOR); - enable_si_command_handling = true; - } else if (controller_type == WP_CONT_TYPE_GC_WIRED_NOMOTOR) { - // Present as a wired GameCube controller without rumble - si_device_gc_init(&si_device, SI_TYPE_GC | SI_GC_STANDARD | SI_GC_NOMOTOR); - } else if (controller_type == WP_CONT_TYPE_GC_WIRED) { - // Present as an OEM wired GameCube controller - si_device_gc_init(&si_device, SI_TYPE_GC | SI_GC_STANDARD); + switch (controller_type) { + case WP_CONT_TYPE_GC_WIRED: + joybus_gc_controller_init(&joybus_gc_controller, JOYBUS_GAMECUBE_CONTROLLER); + break; + case WP_CONT_TYPE_GC_WIRED_NOMOTOR: + joybus_gc_controller_init(&joybus_gc_controller, JOYBUS_GAMECUBE_CONTROLLER | JOYBUS_ID_GCN_NO_MOTOR); + break; + default: + printf("Unknown controller type '%d', defaulting to WaveBird", controller_type); + case WP_CONT_TYPE_GC_WAVEBIRD: + joybus_gc_controller_init(&joybus_gc_controller, JOYBUS_WAVEBIRD_RECEIVER); + break; } } @@ -160,13 +163,13 @@ static void handle_wavebird_packet(const uint8_t *packet) // Check the controller id is as expected if (settings.cont_type == WP_CONT_TYPE_GC_WAVEBIRD) { // Implement wireless ID pinning exactly as OEM WaveBird receivers do - if (si_device_gc_wireless_id_fixed(&si_device)) { + if (joybus_gc_controller_wireless_id_fixed(&joybus_gc_controller)) { // Drop packets from other controllers if the ID has been fixed - if (si_device_gc_get_wireless_id(&si_device) != wireless_id) + if (joybus_gc_controller_get_wireless_id(&joybus_gc_controller) != wireless_id) return; } else { // Set the controller ID if it is not fixed - si_device_gc_set_wireless_id(&si_device, wireless_id); + joybus_gc_controller_set_wireless_id(&joybus_gc_controller, wireless_id); } } else { // Emulate wireless ID pinning for wired controllers @@ -191,40 +194,28 @@ static void handle_wavebird_packet(const uint8_t *packet) // Handle input state packets // - // Clear the buttons in the SI input state - si_device.input.buttons.bytes[0] &= ~0x1F; - si_device.input.buttons.bytes[1] &= ~0x7F; - // Copy the buttons from the WaveBird message - si_device.input.buttons.bytes[0] |= (message[3] & 0x80) >> 7 | (message[2] & 0x0F) << 1; - si_device.input.buttons.bytes[1] |= (message[3] & 0x7F); + joybus_gc_controller.input.buttons &= ~JOYBUS_GCN_BUTTON_MASK; + joybus_gc_controller.input.buttons |= + ((message[3] & 0x80) >> 7) | ((message[2] & 0x0F) << 1) | ((message[3] & 0x7F) << 8); // Copy the stick, substick, and trigger values - memcpy(&si_device.input.stick_x, &message[4], 6); - - // We have a good input state, enable SI command handling if it was disabled - enable_si_command_handling = true; + memcpy(&joybus_gc_controller.input.stick_x, &message[4], 6); - // Set the input state as valid + // Set the input state as valid, and (re)set the validity timer + joybus_gc_controller_input_valid(&joybus_gc_controller, true); input_valid_until = millis + INPUT_VALID_MS; - si_device_set_input_valid(&si_device, true); } else { // // Handle origin packets // // Copy the origin values from the packet - struct si_device_gc_input_state new_origin = { - .stick_x = wavebird_origin_get_stick_x(message), - .stick_y = wavebird_origin_get_stick_y(message), - .substick_x = wavebird_origin_get_substick_x(message), - .substick_y = wavebird_origin_get_substick_y(message), - .trigger_left = wavebird_origin_get_trigger_left(message), - .trigger_right = wavebird_origin_get_trigger_right(message), - }; - - // Update the origin state in the SI device - si_device_gc_set_origin(&si_device, &new_origin); + struct joybus_gc_controller_input new_origin; + wavebird_origin_copy((uint8_t *)&new_origin.stick_x, message); + + // Update the origin state in the Joybus device + joybus_gc_controller_set_origin(&joybus_gc_controller, &new_origin); } } @@ -243,8 +234,8 @@ static void handle_pairing_started(void) // Set the pairing active flag pairing_active = true; - // Disable SI command handling during pairing - enable_si_command_handling = false; + // Disable Joybus bus during pairing + joybus_disable(joybus); // Set the LED effect to indicate pairing mode if (status_led) @@ -277,19 +268,16 @@ static void handle_pairing_finished(uint8_t status, uint8_t channel) // Slow-blink the status LED to indicate pairing timeout if (status_led) led_effect_blink(status_led, 500, 3); - - // Immediately reenable SI command handling - enable_si_command_handling = true; } else { printf("Pairing cancelled\n"); // Turn off the status LED if (status_led) led_off(status_led); - - // Immediately reenable SI command handling - enable_si_command_handling = true; } + + // Re-enable Joybus + joybus_enable(joybus); } // Get a printable name for the controller type @@ -328,8 +316,8 @@ static void gpio_init(void) CMU_ClockEnable(cmuClock_GPIO, true); // Make SWDIO available as a GPIO, if necessary - if (SI_DATA_PORT == GPIO_SWDIO_PORT && SI_DATA_PIN == GPIO_SWDIO_PIN) { - printf("[WARNING] SI is using SWDIO as GPIO, disabling SWD\n"); + if (JOYBUS_PORT == GPIO_SWDIO_PORT && JOYBUS_PIN == GPIO_SWDIO_PIN) { + printf("[WARNING] Joybus is using SWDIO as GPIO, disabling SWD\n"); GPIO_DbgSWDIOEnable(false); } @@ -389,12 +377,16 @@ int main(void) wavebird_radio_set_channel(settings.chan); } - // Initialize the SI bus - si_init(SI_DATA_PORT, SI_DATA_PIN, SI_MODE_DEVICE, 200000, 250000); + // Initialize Joybus + joybus_gecko_init(&joybus_gecko, JOYBUS_PORT, JOYBUS_PIN, JOYBUS_TIMER, JOYBUS_USART); + joybus_target_register(joybus, JOYBUS_TARGET(&joybus_gc_controller)); // Register to handle controller SI commands initialize_controller(settings.cont_type); + // Enable Joybus + joybus_enable(joybus); + // Lets-a-go! printf("WavePhoenix receiver ready!\n"); printf("- Firmware version: %d.%d.%d\n", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH); @@ -402,15 +394,8 @@ int main(void) printf("- Controller type: %s\n", get_controller_type_name(settings.cont_type)); printf("\n"); - // Wait for the SI bus to be idle before starting the main loop - si_await_bus_idle(); - // Main loop while (1) { - // Check if we need to initiate the next SI transfer - if (enable_si_command_handling) - si_command_process(); - // Check for new wavebird packets wavebird_radio_process(); @@ -419,7 +404,7 @@ int main(void) led_effect_update(status_led, millis); // Invalidate stale inputs - if (si_device.input_valid && (int32_t)(millis - input_valid_until) >= 0) - si_device_set_input_valid(&si_device, false); + if (joybus_gc_controller.input_valid && (int32_t)(millis - input_valid_until) >= 0) + joybus_gc_controller_input_valid(&joybus_gc_controller, false); } } diff --git a/firmware/src/version.h b/firmware/src/version.h index f0080e4..e1299bf 100644 --- a/firmware/src/version.h +++ b/firmware/src/version.h @@ -2,4 +2,4 @@ #define VERSION_MAJOR 0 #define VERSION_MINOR 9 -#define VERSION_PATCH 3 \ No newline at end of file +#define VERSION_PATCH 99 \ No newline at end of file diff --git a/firmware/wavephoenix.slcp b/firmware/wavephoenix.slcp index 8668450..6ee345d 100644 --- a/firmware/wavephoenix.slcp +++ b/firmware/wavephoenix.slcp @@ -15,8 +15,8 @@ component: - id: bootloader_interface - id: gpiointerrupt - id: emlib - - id: libsi - from: libsi + - id: libjoybus + from: libjoybus - id: libwavebird from: libwavebird From 14b788eabd7c146d1693831c37120f85cda4a3e9 Mon Sep 17 00:00:00 2001 From: James Smith Date: Sun, 11 Jan 2026 10:56:57 -0800 Subject: [PATCH 14/15] Re-work github action --- .github/workflows/build.yml | 183 ++++++++++++++++++++++-------------- 1 file changed, 111 insertions(+), 72 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a980d08..b8e5f48 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,49 +1,38 @@ name: Build and Release on: - workflow_dispatch: push: - branches: - - main - paths: - - "bootloader/**" - - "firmware/**" - tags: - - "v*" - -env: - REPO_PATH: ${{ github.workspace }}/wavephoenix - BOOTLOADER_PATH: ${{ github.workspace }}/wavephoenix/bootloader - FIRMWARE_PATH: ${{ github.workspace }}/wavephoenix/firmware - GECKO_SDK_PATH: ${{ github.workspace }}/gecko_sdk - SIMPLICITY_COMMANDER_PATH: ${{ github.workspace }}/commander - SLC_CLI_PATH: ${{ github.workspace }}/slc_cli - ARM_TOOLCHAIN_PATH: ${{ github.workspace }}/arm-toolchain - GECKO_SDK_VERSION: v4.4.5 - ARM_GNU_TOOLCHAIN_VERSION: 12.2.rel1 + branches: [main] + tags: ["v*"] + paths: ["bootloader/**", "firmware/**"] + pull_request: + branches: [main] + workflow_dispatch: jobs: build: runs-on: ubuntu-latest + + env: + ARM_GNU_TOOLCHAIN_VERSION: 13.3.rel1 + SIMPLICITY_SDK_VERSION: v2025.6.2 + strategy: matrix: - board: - - efr32xg22e - - rf-bm-bg22c3 + board: ["efr32xg22e", "rf-bm-bg22c3"] + steps: + # Checkout repository and submodules + - name: Checkout repository + uses: actions/checkout@v6 + with: + submodules: true + + # Set up dependencies - name: Install build dependencies run: | - export DEBIAN_FRONTEND=noninteractive sudo apt-get update - sudo apt-get -o Dpkg::Options::="--path-exclude=/usr/share/man/*" -o Dpkg::Options::="--path-exclude=/usr/share/doc/*" -o Dpkg::Options::="--path-exclude=/usr/share/locale*" install -y cmake ninja-build - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install Python packages - run: pip install jinja2 pyyaml numpy scipy + sudo apt-get install -y cmake ninja-build unzip wget - name: Set up Java uses: actions/setup-java@v4 @@ -51,77 +40,118 @@ jobs: distribution: "corretto" java-version: "21" + # Setup ARM GNU Toolchain - name: Cache ARM GNU Toolchain id: cache-arm-toolchain uses: actions/cache@v4 with: - path: ${{ env.ARM_TOOLCHAIN_PATH }} + path: arm-gnu-toolchain-${{ env.ARM_GNU_TOOLCHAIN_VERSION }}-x86_64-arm-none-eabi key: arm-toolchain-${{ env.ARM_GNU_TOOLCHAIN_VERSION }} - name: Install ARM GNU Toolchain if: steps.cache-arm-toolchain.outputs.cache-hit != 'true' run: | - mkdir -p ${{ env.ARM_TOOLCHAIN_PATH }} - curl -L "https://developer.arm.com/-/media/Files/downloads/gnu/${{ env.ARM_GNU_TOOLCHAIN_VERSION }}/binrel/arm-gnu-toolchain-${{ env.ARM_GNU_TOOLCHAIN_VERSION }}-x86_64-arm-none-eabi.tar.xz" | tar -xJ -C "${{ env.ARM_TOOLCHAIN_PATH }}" --strip-components=1 + wget -q https://developer.arm.com/-/media/Files/downloads/gnu/${{ env.ARM_GNU_TOOLCHAIN_VERSION }}/binrel/arm-gnu-toolchain-${{ env.ARM_GNU_TOOLCHAIN_VERSION }}-x86_64-arm-none-eabi.tar.xz + tar xf arm-gnu-toolchain-${{ env.ARM_GNU_TOOLCHAIN_VERSION }}-x86_64-arm-none-eabi.tar.xz - name: Add ARM GNU Toolchain to PATH - run: echo "${{ env.ARM_TOOLCHAIN_PATH }}/bin" >> $GITHUB_PATH + run: | + echo "$PWD/arm-gnu-toolchain-${{ env.ARM_GNU_TOOLCHAIN_VERSION }}-x86_64-arm-none-eabi/bin" >> $GITHUB_PATH - - name: Checkout repository - uses: actions/checkout@v4 + # Setup Simplicity SDK + - name: Cache Simplicity SDK + id: cache-simplicity-sdk + uses: actions/cache@v4 with: - path: ${{ env.REPO_PATH }} + path: simplicity_sdk + key: simplicity-sdk-${{ env.SIMPLICITY_SDK_VERSION }} + + - name: Install Simplicity SDK + if: steps.cache-simplicity-sdk.outputs.cache-hit != 'true' + run: | + wget -q https://github.com/SiliconLabs/simplicity_sdk/releases/download/${{ env.SIMPLICITY_SDK_VERSION }}/simplicity-sdk.zip + unzip -q simplicity-sdk.zip -d simplicity_sdk - - name: Cache Gecko SDK - id: cache-gecko-sdk + # Setup SLC-CLI + - name: Cache SLC-CLI + id: cache-slc-cli uses: actions/cache@v4 with: - path: ${{ env.GECKO_SDK_PATH }} - key: gecko-sdk-${{ env.GECKO_SDK_VERSION }} + path: slc_cli + key: slc-cli-${{ runner.os }} + + - name: Install SLC-CLI + if: steps.cache-slc-cli.outputs.cache-hit != 'true' + run: | + wget -q https://www.silabs.com/documents/login/software/slc_cli_linux.zip + unzip -q slc_cli_linux.zip + chmod +x slc_cli/slc + + - name: Add SLC-CLI to PATH + run: | + echo "$PWD/slc_cli" >> $GITHUB_PATH - - name: Download Gecko SDK - if: steps.cache-gecko-sdk.outputs.cache-hit != 'true' + - name: Configure SLC-CLI run: | - wget -nv https://github.com/SiliconLabs/gecko_sdk/releases/download/${{ env.GECKO_SDK_VERSION }}/gecko-sdk.zip - unzip -q gecko-sdk.zip -d ${{ env.GECKO_SDK_PATH }} + slc configuration --sdk simplicity_sdk + slc configuration --gcc-toolchain arm-gnu-toolchain-${{ env.ARM_GNU_TOOLCHAIN_VERSION }}-x86_64-arm-none-eabi + slc signature trust --sdk simplicity_sdk + + # Setup Simplicity Commander + - name: Cache Simplicity Commander + id: cache-simplicity-commander + uses: actions/cache@v4 + with: + path: commander + key: simplicity-commander-${{ runner.os }} - - name: "Download Simplicity Commander" + - name: Install Simplicity Commander + if: steps.cache-simplicity-commander.outputs.cache-hit != 'true' run: | - wget -nv https://www.silabs.com/documents/login/software/SimplicityCommander-Linux.zip + wget -q https://www.silabs.com/documents/login/software/SimplicityCommander-Linux.zip unzip -q SimplicityCommander-Linux.zip tar -xf SimplicityCommander-Linux/Commander_linux_x86_64_*.tar.bz - - name: Download SLC-CLI + - name: Add Simplicity Commander to PATH run: | - wget -nv https://www.silabs.com/documents/login/software/slc_cli_linux.zip - unzip -q slc_cli_linux.zip + echo "$PWD/commander" >> $GITHUB_PATH - - name: Build bootloader + # Build bootloader + - name: Generate bootloader project files + working-directory: bootloader run: | - export ARM_GCC_DIR="${{ env.ARM_TOOLCHAIN_PATH }}" - - cd ${{ env.BOOTLOADER_PATH }} - ${{ env.SLC_CLI_PATH }}/slc signature trust --sdk ${{ env.GECKO_SDK_PATH }} - ${{ env.SLC_CLI_PATH }}/slc generate -s ${{ env.GECKO_SDK_PATH }} -p bootloader-${{ matrix.board }}.slcp -d bootloader_project -o cmake + slc generate bootloader-${{ matrix.board }}.slcp \ + -o cmake \ + -d target/${{ matrix.board }} - cd bootloader_project/bootloader_cmake + - name: Build bootloader + working-directory: bootloader/target/${{ matrix.board }}/bootloader_cmake + run: | cmake --workflow --preset project - cmake --preset project && cmake --build --preset default_config + + # Build firmware + - name: Generate firmware project files + working-directory: firmware + run: | + slc generate wavephoenix.slcp \ + --with "${{ matrix.board }};wavephoenix" \ + --sdk-extensions=.,libjoybus,libwavebird \ + -o cmake \ + -d target/${{ matrix.board }} - name: Build firmware + working-directory: firmware/target/${{ matrix.board }}/wavephoenix_cmake run: | - cd ${{ env.FIRMWARE_PATH }} - cmake --preset ${{ matrix.board }} && cmake --build --preset ${{ matrix.board }} + cmake --workflow --preset project - - name: Update trunk tag - if: github.ref == 'refs/heads/main' + - name: Generate firmware .gbl + working-directory: firmware/target/${{ matrix.board }}/wavephoenix_cmake/build/default_config run: | - cd ${{ env.REPO_PATH }} - git tag -f trunk - git push -f origin trunk - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + commander gbl create \ + --app wavephoenix.s37 \ + wavephoenix.gbl + # Upload artifacts - name: Rename artifacts run: | if [[ "${GITHUB_REF}" == refs/tags/* ]]; then @@ -130,9 +160,18 @@ jobs: VERSION="trunk" fi - mv ${{ env.BOOTLOADER_PATH }}/bootloader_project/bootloader_cmake/build/default_config/bootloader.hex "wavephoenix-bootloader-${VERSION}-${{ matrix.board }}.hex" - mv ${{ env.FIRMWARE_PATH }}/build/${{ matrix.board }}/receiver/receiver.hex "wavephoenix-receiver-${VERSION}-${{ matrix.board }}.hex" - mv ${{ env.FIRMWARE_PATH }}/build/${{ matrix.board }}/receiver/receiver.gbl "wavephoenix-receiver-${VERSION}-${{ matrix.board }}.gbl" + mv bootloader/target/${{ matrix.board }}/bootloader_cmake/build/default_config/bootloader.hex "wavephoenix-bootloader-${VERSION}-${{ matrix.board }}.hex" + mv firmware/target/${{ matrix.board }}/wavephoenix_cmake/build/default_config/wavephoenix.hex "wavephoenix-receiver-${VERSION}-${{ matrix.board }}.hex" + mv firmware/target/${{ matrix.board }}/wavephoenix_cmake/build/default_config/wavephoenix.gbl "wavephoenix-receiver-${VERSION}-${{ matrix.board }}.gbl" + + - name: Update trunk tag + if: github.ref == 'refs/heads/main' + run: | + cd ${{ env.REPO_PATH }} + git tag -f trunk + git push -f origin trunk + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Upload artifacts to trunk release if: github.ref == 'refs/heads/main' From 37084d26a0ca1e6328dd26449e2768063f4ee3cf Mon Sep 17 00:00:00 2001 From: James Smith Date: Sun, 11 Jan 2026 14:26:18 -0800 Subject: [PATCH 15/15] Update README --- README.md | 153 +++++++------------------------ WORKLOG.md | 94 +++++++++++++++++++ hardware/mini-receiver/README.md | 14 +-- 3 files changed, 127 insertions(+), 134 deletions(-) create mode 100644 WORKLOG.md diff --git a/README.md b/README.md index 76b406a..995b51c 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@

- An open-source implementation of the Nintendo WaveBird protocol using Silicon Labs Wireless Gecko SoCs + Open-source implementation of the Nintendo WaveBird receiver using Silicon Labs Wireless Gecko SoCs

Discord @@ -17,141 +17,48 @@ The WaveBird controller is, in my opinion, one of the best controllers ever made. It is wireless, has an insane battery life, and is very comfortable to hold. The WaveBird set a new standard for wireless controllers. It was the first major console controller to use radio frequency (RF) technology, providing a reliable, lag-free connection that didn't require line-of-sight, unlike infrared controllers. -Unfortunately, Nintendo stopped producing the WaveBird more than a decade ago, causing a dwindling supply of controllers and, especially, receivers. Given the decreasing supply of receivers and the increasing resale prices, I decided to see if I could design my own from scratch. +Unfortunately, Nintendo stopped producing the WaveBird more than a decade ago, causing a dwindling supply of controllers and, especially, receivers. Given the decreasing supply of receivers and the increasing resale prices, I decided to design my own from scratch. -## Firmware +## Features -The firmware for WavePhoenix is split into several components: +The reference implementation of WavePhoenix includes the following features: -- [`libwavebird`](firmware/libwavebird) - implementation of the WaveBird protocol -- [`libsi`](firmware/libsi/) - implementation of the SI protocol, used by controllers to communicate with GameCube/Wii consoles -- [`receiver`](firmware/receiver) - reference implementation of firmware for a WaveBird receiver -- [`bootloader`](bootloader) - bootloader for updating application firmware via Bluetooth - -## Hardware - -The hardware for a reference WavePhoenix receiver is available in the [`hardware/mini-receiver`](hardware/mini-receiver) directory. The receiver is designed to be as cheap as possible and easy to build. - -WavePhoenix Mini Receiver WavePhoenix Mini Receiver - -The hardware folder contains a simple PCB which hosts a [RF-BM-BG22C3](https://www.rfstariot.com/rf-bm-bg22c3-efr32bg22-bluetooth-module_p93.html) module, a pairing button, a status LED, and a JST connector for connecting to a GameCube controller port, as well as a 3D printable case. - -- [`case`](hardware/mini-receiver/case) - 3D printable case for the receiver -- [`gerbers`](hardware/mini-receiver/gerbers) - Gerber files for ordering the PCB -- [`KiCad`](hardware/mini-receiver/KiCad) - KiCad project files for the PCB - -Check out the [`hardware/mini-receiver/README.md`](hardware/mini-receiver/README.md) for more information on how to build your own receiver. - -## Protocol - -I have documented the key parts of WaveBird protocol in the headers of [`libwavebird`](firmware/libwavebird): - -- [radio.h](firmware/libwavebird/include/wavebird/radio.h) - describes the radio timings and modulation -- [packet.h](firmware/libwavebird/include/wavebird/packet.h) - describes the packet format, encoding, and crc -- [message.h](firmware/libwavebird/include/wavebird/message.h) - describes the structure of the decoded packets, aka the *input state* and *origin* messages - -Sam Edwards' excellent [WaveBird reversing](https://github.com/CFSworks/wavebird-reversing) documentation contain the most detailed information about the protocol, but I have tried to fill in the gaps where possible. - -## Development - -### Finding an SoC - -In mid-2020 I began researching the WaveBird protocol and came across Sam Edwards' incredibly well-written [WaveBird reverse-engineering](https://github.com/CFSworks/wavebird-reversing) documentation. Sam's docs aren't quite perfect, but they were **essential** in getting this working, thanks, Sam! - -The most challenging part of this project was finding a wireless SoC that supported the specific modulation used by the WaveBird. What Sam describes in the [line coding and framing](https://github.com/CFSworks/wavebird-reversing/tree/master/05_line_coding_and_framing#a-closer-look-at-the-wavebirds-line-code) section of his document is actually a form of DSSS (Direct Sequence Spread Spectrum), one which uses *15 chips per bit*. - -DSSS is a modulation technique that spreads the signal over a wide frequency band, making it more robust to interference and noise [here's a great breakdown by GeekyMuch](https://medium.com/networks-security/dsss-direct-sequence-spread-spectrum-a31005f281cc). - -While it would certainly be possible to implement 15-chip DSSS demodulation using a software-defined radio, I wanted to find an SoC that could do this in hardware *in real-time*. After reading more datasheets than I care to admit, I discovered the [Silicon Labs Wireless Gecko EFR32FG1](https://www.silabs.com/wireless/proprietary/efr32fg1-series-1-sub-ghz-2-4-ghz-socs) family of SoCs, the [datasheet](https://www.silabs.com/documents/public/data-sheets/efr32fg1-datasheet.pdf) contains the magic incantation: - -![15 chip DSSS](images/efr32fg1-datasheet-dsss.png) - -Once I saw this, I had enough confidence to order a development kit and start working on the firmware. - -> [!NOTE] -> Did you know that the codename for the GameCube CPU was *Gekko*? How fitting that we are using a *Gecko* SoC! - -### Receiving and decoding packets - -As I mentioned above, Sam's WaveBird reverse engineering documentation isn't *quite* right on a few things. Most importantly, I don't believe the DSSS chip length is accurate. After setting the DSSS chip length to 15 and configuring the many other radio settings, I got my first packet! - -![First packet](images/first-packets.png) +- Full compatibility with the original WaveBird controller +- One-button virtual pairing, like modern wireless devices +- Status LED to indicate pairing status and radio activity +- Over-the-air firmware updates via Bluetooth +- Open source hardware and firmware +- 3D printable case -Decoding the packets was now simply a matter of writing some C code to de-interleave the packet, decode the [BCH(31,21)](https://en.wikipedia.org/wiki/BCH_code) messages, and perform the CRC check. After doing this, we finally had a decoded *input state* message containing the state of all the buttons and analog inputs on the controller! +## Buying a WavePhoenix -![Packet decoding](images/packet-decoding.png) +Don't want to build your own? A number of vendors are now selling pre-assembled WavePhoenix receivers! -But wait. Every now and then, I was getting a "corrupted" packet that somehow was still passing the CRC check. What could this be? It turns out that in addition to broadcasting *input state* messages 250 times per second, the WaveBird controller also broadcasts *origin* messages. These are sent as soon as the controller is powered on and then repeated every second after that. The origin message contains the state of the analog sticks when the console was powered on, which we'll need later. +- [Laser Bear Industries](https://www.laserbear.net/products/wavephoenix-replacement-wavebird-gamecube-receiver) - US-based seller, custom enclosure and PCB by Greg +- [Colester Productions](https://shop.colesterproductions.com/product/wave-phoenix) - US-based seller, uses my original PCB and [ +SadSnifit's fantastic case](https://makerworld.com/en/models/1463984-wavephoenix-wavebird-shell) +- [Various AliExpress sellers](https://www.aliexpress.us/w/wholesale-%22wavephoenix%22.html) -### Communicating with a GameCube +## Building a WavePhoenix -Now we have real-time packet decoding; the next step is to send/receive commands on the *SI bus*. The SI bus is how GameCube/Wii consoles communicate with connected controllers. Jeff Longo has an insanely detailed and precise writeup about the [GameCube controller protocol](https://jefflongo.dev/posts/gc-controller-reverse-engineering-part-1/), which you should check out! +The bare minimum hardware needed to build a WavePhoenix is a EFR32BG22 radio module (such as the [RF-BM-BG22C3](https://www.rfstariot.com/rf-bm-bg22c3-efr32bg22-bluetooth-module_p93.html)), a GameCube controller connector, and 3 wires! -Bits are clocked in and out at speeds between 250kHz (wired OEM controllers) and 200kHz (consoles). To do this without tying up the CPU, I had to take full advantage of the peripherals on the EFR32 SoCs. +Since most people prefer a more streamlined solution, I designed a custom PCB and 3D-printable case called the *Mini Receiver*. -> [!NOTE] -> An OEM WaveBird receiver clocks data out at 225kHz, slightly slower than wired controllers! +You can find detailed build instructions in the [hardware/mini-receiver](hardware/mini-receiver) directory. -#### Listen for incoming commands +## Firmware Components -The first step is to listen for the various SI commands from the console. +The firmware for WavePhoenix is composed of the following components: -To read these commands, we do the following: - -- Configure a `TIMER` peripheral in capture/compare mode and attach it to the SI GPIO -- Connect the `TIMER` to an `LDMA` peripheral, filling the DMA buffer with 16 "edge timings" at a time -- Listen for a "DMA full" interrupt -- Determine the length of each "low" pulse by measuring the time between pairs of edges -- Based on the length of a low pulse, we store either a `0` or a `1` in our incoming byte buffer -- The first byte identifies the SI command type; this tells us how many bits to expect in the rest of the SI transmission -- Repeat until we have a complete SI command - -#### Replying to commands - -Once we have a complete SI command, we need to respond (quickly!). We do this as follows: - -- Use a `USART` peripheral to clock out pulses with precise timing without tying up the CPU -- Split each outgoing bit into 4 chips, so `0` becomes `0001`, `1` becomes `1110`, and stop becomes `0011` -- Configure the `USART` to output onto the SI GPIO, and set the baud rate to 1MHz (4 chips per bit @ 250kHz bitrate) -- Feed the chips into the `USART` transmit buffer, and wait for the "TX complete" interrupt to be triggered - -#### Command types - -The most important command is the *get input state* command, which the console requests every frame. Since we should already have the latest input state packet from the WaveBird controller, we can simply send it back to the console after converting it to the SI format. - -There are a bunch of other commands that we need to respond to, such as the *info* and *get origin* commands, but I'll leave that as an exercise for the reader to discover in the code. - -### Wireless ID pinning - -Since the WaveBird has no true concept of pairing, it is possible for multiple WaveBird controllers to be on the same channel at the same time. This is a problem, since the receiver will receive packets from all controllers on this channel. The WaveBird uses a simple "ID pinning" mechanism to solve this problem. - -An OEM WaveBird receiver will look at the 10-bit controller ID present in all packets, and "fix" the ID to the first controller it receives a packet from. - -### Channel selection - -As well as supporting a channel wheel just like the original WaveBird receiver, I also wanted to add one-button "virtual pairing" support. This allows you to press a button on the receiver to enter pairing mode, and then hold a combination of buttons on a controller to "pair" it with the receiver. Under the hood, this scans through all 16 potential channels looking for wireless packets, and then checks if the appropriate buttons are being held on that channel. - -### A more modern SoC - -Although it doesn't explicitly call this out in the datasheet, I discovered that the EFR32xG22 series of SoCs *also* support 15-chip DSSS. This is great news since they are cheaper and more modern than the EFR32xG1 series. The EFR32BG22 SoC in particular has many pre-built modules which host the SoC, crystal, antenna, and RF matching network - such as the tiny [RF-BM-BG22C3 from RF-Star](https://www.rfstariot.com/rf-bm-bg22c3-efr32bg22-bluetooth-module_p93.html) - -### Tuning, tuning, tuning - -The hardest part of this project *by far* was tuning the radio settings. - -The original WaveBird receiver has excellent range and wireless sensitivity. It is able to receive the full 250 packets per second at a distance of 10m+, so my goal was always to match this performance. - -Dropping an occasional packet here and there is not a huge problem. Most GameCube games run at 60 frames per second so we have a little bit of leeway for the occasional dropped packet. However, if we drop too many packets this will result in missed inputs. - -My initial radio configuration only managed to receive around 60 packets per second, so I spent a *lot* of time tuning the settings to get better performance. I'm now measuring 230+ packets per second at distances of around 5m, which is pretty close, and I think finally good enough to release. - -I still think there is *a ton* of room for improvement here, pull requests would be very welcome! +- [`libwavebird`](https://github.com/loopj/libwavebird) - my implementation of Nintendo's WaveBird protocol for Silicon Labs Gecko SoCs +- [`libjoybus`](https://github.com/loopj/libjoybus) - my implementation of the Joybus protocol used by N64 and GameCube controllers +- [`firmware`](firmware) - reference implementation of firmware for a WaveBird receiver +- [`bootloader`](bootloader) - bootloader for updating application firmware via Bluetooth -## Future ideas +## Worklog -- **Transmitter firmware** - `libwavebird` includes packet encoding functions, so with a little work you could "build your own" WaveBird controller! -- **N64 WaveBird receiver** - the N64 uses the same SI protocol as the GameCube, with just a slightly different input polling command, so designing a WaveBird receiver for the N64 is within reach! -- **USB HID dongle** - instead of sending input data over the SI bus, we could instead incorporate something like a [CH9329](https://www.wch-ic.com/products/CH9329.html) and send the input data over USB HID. This would allow you to use a WaveBird controller on any device that supports USB HID, such as a PC running Dolphin or RetroArch! +Check out the [worklog](WORKLOG.md) for a detailed history of the development of the WavePhoenix project. ## Special Thanks @@ -164,6 +71,8 @@ I still think there is *a ton* of room for improvement here, pull requests would ## License -The firmware in this repository is licensed under the [MIT License](firmware/LICENSE). +WavePhoenix is a permissively licensed open source project. + +The firmware is licensed under the [MIT License](firmware/LICENSE). The hardware is licensed under the [Solderpad Hardware License v2.1](hardware/LICENSE). diff --git a/WORKLOG.md b/WORKLOG.md new file mode 100644 index 0000000..5a76cd3 --- /dev/null +++ b/WORKLOG.md @@ -0,0 +1,94 @@ +# Development Worklog + +## Finding an SoC + +In mid-2020 I began researching the WaveBird protocol and came across Sam Edwards' incredibly well-written [WaveBird reverse-engineering](https://github.com/CFSworks/wavebird-reversing) documentation. Sam's docs aren't quite perfect, but they were **essential** in getting this working, thanks, Sam! + +The most challenging part of this project was finding a wireless SoC that supported the specific modulation used by the WaveBird. What Sam describes in the [line coding and framing](https://github.com/CFSworks/wavebird-reversing/tree/master/05_line_coding_and_framing#a-closer-look-at-the-wavebirds-line-code) section of his document is actually a form of DSSS (Direct Sequence Spread Spectrum), one which uses *15 chips per bit*. + +DSSS is a modulation technique that spreads the signal over a wide frequency band, making it more robust to interference and noise [here's a great breakdown by GeekyMuch](https://medium.com/networks-security/dsss-direct-sequence-spread-spectrum-a31005f281cc). + +While it would certainly be possible to implement 15-chip DSSS demodulation using a software-defined radio, I wanted to find an SoC that could do this in hardware *in real-time*. After reading more datasheets than I care to admit, I discovered the [Silicon Labs Wireless Gecko EFR32FG1](https://www.silabs.com/wireless/proprietary/efr32fg1-series-1-sub-ghz-2-4-ghz-socs) family of SoCs, the [datasheet](https://www.silabs.com/documents/public/data-sheets/efr32fg1-datasheet.pdf) contains the magic incantation: + +![15 chip DSSS](images/efr32fg1-datasheet-dsss.png) + +Once I saw this, I had enough confidence to order a development kit and start working on the firmware. + +> [!NOTE] +> Did you know that the codename for the GameCube CPU was *Gekko*? How fitting that we are using a *Gecko* SoC! + +## Receiving and decoding packets + +As I mentioned above, Sam's WaveBird reverse engineering documentation isn't *quite* right on a few things. Most importantly, I don't believe the DSSS chip length is accurate. After setting the DSSS chip length to 15 and configuring the many other radio settings, I got my first packet! + +![First packet](images/first-packets.png) + +Decoding the packets was now simply a matter of writing some C code to de-interleave the packet, decode the [BCH(31,21)](https://en.wikipedia.org/wiki/BCH_code) messages, and perform the CRC check. After doing this, we finally had a decoded *input state* message containing the state of all the buttons and analog inputs on the controller! + +![Packet decoding](images/packet-decoding.png) + +But wait. Every now and then, I was getting a "corrupted" packet that somehow was still passing the CRC check. What could this be? It turns out that in addition to broadcasting *input state* messages 250 times per second, the WaveBird controller also broadcasts *origin* messages. These are sent as soon as the controller is powered on and then repeated every second after that. The origin message contains the state of the analog sticks when the console was powered on, which we'll need later. + +## Communicating with a GameCube + +Now we have real-time packet decoding; the next step is to send/receive commands on the *SI bus*. The SI bus is how GameCube/Wii consoles communicate with connected controllers. Jeff Longo has an insanely detailed and precise writeup about the [GameCube controller protocol](https://jefflongo.dev/posts/gc-controller-reverse-engineering-part-1/), which you should check out! + +Bits are clocked in and out at speeds between 250kHz (wired OEM controllers) and 200kHz (consoles). To do this without tying up the CPU, I had to take full advantage of the peripherals on the EFR32 SoCs. + +> [!NOTE] +> An OEM WaveBird receiver clocks data out at 225kHz, slightly slower than wired controllers! + +### Listen for incoming commands + +The first step is to listen for the various SI commands from the console. + +To read these commands, we do the following: + +- Configure a `TIMER` peripheral in capture/compare mode and attach it to the SI GPIO +- Connect the `TIMER` to an `LDMA` peripheral, filling the DMA buffer with 16 "edge timings" at a time +- Listen for a "DMA full" interrupt +- Determine the length of each "low" pulse by measuring the time between pairs of edges +- Based on the length of a low pulse, we store either a `0` or a `1` in our incoming byte buffer +- The first byte identifies the SI command type; this tells us how many bits to expect in the rest of the SI transmission +- Repeat until we have a complete SI command + +### Replying to commands + +Once we have a complete SI command, we need to respond (quickly!). We do this as follows: + +- Use a `USART` peripheral to clock out pulses with precise timing without tying up the CPU +- Split each outgoing bit into 4 chips, so `0` becomes `0001`, `1` becomes `1110`, and stop becomes `0011` +- Configure the `USART` to output onto the SI GPIO, and set the baud rate to 1MHz (4 chips per bit @ 250kHz bitrate) +- Feed the chips into the `USART` transmit buffer, and wait for the "TX complete" interrupt to be triggered + +### Command types + +The most important command is the *get input state* command, which the console requests every frame. Since we should already have the latest input state packet from the WaveBird controller, we can simply send it back to the console after converting it to the SI format. + +There are a bunch of other commands that we need to respond to, such as the *info* and *get origin* commands, but I'll leave that as an exercise for the reader to discover in the code. + +## Wireless ID pinning + +Since the WaveBird has no true concept of pairing, it is possible for multiple WaveBird controllers to be on the same channel at the same time. This is a problem, since the receiver will receive packets from all controllers on this channel. The WaveBird uses a simple "ID pinning" mechanism to solve this problem. + +An OEM WaveBird receiver will look at the 10-bit controller ID present in all packets, and "fix" the ID to the first controller it receives a packet from. + +## Channel selection + +As well as supporting a channel wheel just like the original WaveBird receiver, I also wanted to add one-button "virtual pairing" support. This allows you to press a button on the receiver to enter pairing mode, and then hold a combination of buttons on a controller to "pair" it with the receiver. Under the hood, this scans through all 16 potential channels looking for wireless packets, and then checks if the appropriate buttons are being held on that channel. + +## A more modern SoC + +Although it doesn't explicitly call this out in the datasheet, I discovered that the EFR32xG22 series of SoCs *also* support 15-chip DSSS. This is great news since they are cheaper and more modern than the EFR32xG1 series. The EFR32BG22 SoC in particular has many pre-built modules which host the SoC, crystal, antenna, and RF matching network - such as the tiny [RF-BM-BG22C3 from RF-Star](https://www.rfstariot.com/rf-bm-bg22c3-efr32bg22-bluetooth-module_p93.html) + +## Tuning, tuning, tuning + +The hardest part of this project *by far* was tuning the radio settings. + +The original WaveBird receiver has excellent range and wireless sensitivity. It is able to receive the full 250 packets per second at a distance of 10m+, so my goal was always to match this performance. + +Dropping an occasional packet here and there is not a huge problem. Most GameCube games run at 60 frames per second so we have a little bit of leeway for the occasional dropped packet. However, if we drop too many packets this will result in missed inputs. + +My initial radio configuration only managed to receive around 60 packets per second, so I spent a *lot* of time tuning the settings to get better performance. I'm now measuring 230+ packets per second at distances of around 5m, which is pretty close, and I think finally good enough to release. + +I still think there is *a ton* of room for improvement here, pull requests would be very welcome! \ No newline at end of file diff --git a/hardware/mini-receiver/README.md b/hardware/mini-receiver/README.md index 881e5df..8331444 100644 --- a/hardware/mini-receiver/README.md +++ b/hardware/mini-receiver/README.md @@ -6,15 +6,6 @@ The receiver uses a cheap, off-the-shelf wireless module (the [RF-BM-BG22C3](htt WavePhoenix Mini Receiver WavePhoenix Mini Receiver -## Features - -- Full compatibility with the original WaveBird controller -- One-button virtual pairing, like modern wireless devices -- Status LED to indicate pairing status and radio activity -- Over-the-air firmware updates via Bluetooth -- Open source hardware and firmware -- 3D printable case - ## Build Guide ### Parts @@ -79,10 +70,9 @@ Unfortunately the RF-BM-BG22C3 module is not available for JLC's awesome economi ## Case -A 3D printable case for the receiver can be found in the [`case`](case) directory. The case is designed to fit the assembled PCB and has a slot for the GameCube connector. +I highly recommend you use [SadSnifit's WavePhoenix shell](https://makerworld.com/en/models/1463984-wavephoenix-wavebird-shell) for the case, it is very well designed and looks great. -I recommend printing the parts in the following orientation: -![images/print-orientation.png](images/print-orientation.png) +Alternatively, my original 3D printable case for the receiver can be found in the [`case`](case) directory. ## Cable