From d9da402915b712f596c84f602bafe24417238e73 Mon Sep 17 00:00:00 2001 From: Daniel Schloms Date: Thu, 1 Feb 2024 16:42:34 +0100 Subject: [PATCH 1/5] Add minimal RF code for basic 'read part info' test --- Sts1CobcSw/Periphery/CMakeLists.txt | 2 +- Sts1CobcSw/Periphery/Rf.cpp | 195 ++++++++++++++++++++++++++++ Sts1CobcSw/Periphery/Rf.hpp | 17 +++ Sts1CobcSw/Periphery/RfNames.hpp | 82 ++++++++++++ Tests/HardwareTests/CMakeLists.txt | 3 + Tests/HardwareTests/Rf.test.cpp | 52 ++++++++ 6 files changed, 350 insertions(+), 1 deletion(-) create mode 100644 Sts1CobcSw/Periphery/Rf.cpp create mode 100644 Sts1CobcSw/Periphery/Rf.hpp create mode 100644 Sts1CobcSw/Periphery/RfNames.hpp create mode 100644 Tests/HardwareTests/Rf.test.cpp diff --git a/Sts1CobcSw/Periphery/CMakeLists.txt b/Sts1CobcSw/Periphery/CMakeLists.txt index bd7d7af3..70551098 100644 --- a/Sts1CobcSw/Periphery/CMakeLists.txt +++ b/Sts1CobcSw/Periphery/CMakeLists.txt @@ -1,7 +1,7 @@ target_link_libraries(Sts1CobcSw_Periphery PUBLIC rodos::rodos Sts1CobcSw_Serial) if(CMAKE_SYSTEM_NAME STREQUAL Generic) - target_sources(Sts1CobcSw_Periphery PRIVATE PersistentState.cpp Flash.cpp Fram.cpp) + target_sources(Sts1CobcSw_Periphery PRIVATE PersistentState.cpp Flash.cpp Fram.cpp Rf.cpp) target_link_libraries(Sts1CobcSw_Periphery PRIVATE Sts1CobcSw_Utility) target_link_libraries(Sts1CobcSw_Periphery PUBLIC Sts1CobcSw_Hal) endif() diff --git a/Sts1CobcSw/Periphery/Rf.cpp b/Sts1CobcSw/Periphery/Rf.cpp new file mode 100644 index 00000000..e672fd7a --- /dev/null +++ b/Sts1CobcSw/Periphery/Rf.cpp @@ -0,0 +1,195 @@ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + + +namespace sts1cobcsw::periphery::rf +{ +using RODOS::AT; +using RODOS::MICROSECONDS; +using RODOS::MILLISECONDS; +using RODOS::NOW; + +// --- Globals --- + +auto spi = RODOS::HAL_SPI( + hal::rfSpiIndex, hal::rfSpiSckPin, hal::rfSpiMisoPin, hal::rfSpiMosiPin, hal::spiNssDummyPin); +auto csGpioPin = hal::GpioPin(hal::rfCsPin); +auto nirqGpioPin = hal::GpioPin(hal::rfNirqPin); +auto sdnGpioPin = hal::GpioPin(hal::rfSdnPin); +auto gpio0GpioPin = hal::GpioPin(hal::rfGpio0Pin); +auto gpio1GpioPin = hal::GpioPin(hal::rfGpio1Pin); +auto paEnablePin = hal::GpioPin(hal::rfPaEnablePin); + +// TODO: This should probably be somewhere else as it is not directly related to the RF module +auto watchdogResetGpioPin = hal::GpioPin(hal::watchdogClearPin); + +constexpr std::uint16_t partInfo = 0x4463; +constexpr std::uint32_t powerUpXoFrequency = 26'000'000; // 26 MHz + +// --- Private function declarations --- + +auto PowerUp(PowerUpBootOptions bootOptions, + PowerUpXtalOptions xtalOptions, + std::uint32_t xoFrequency) -> void; + +auto SendCommandNoResponse(std::span commandBuffer) -> void; + +template +auto SendCommandWithResponse(std::span commandBuffer) + -> std::array; + +auto WaitOnCts() -> void; + +// --- Public function definitions --- + +// TODO: Get rid of all the magic numbers +// TODO: Replace all C-style arrays with std::array + +auto Initialize() -> void +{ + InitializeGpioAndSpi(); + + PowerUp(PowerUpBootOptions::noPatch, PowerUpXtalOptions::xtal, powerUpXoFrequency); + + // Here comes the initialization of the RF module +} + + +auto InitializeGpioAndSpi() -> void +{ + csGpioPin.Direction(hal::PinDirection::out); + csGpioPin.Set(); + + nirqGpioPin.Direction(hal::PinDirection::in); + + sdnGpioPin.Direction(hal::PinDirection::out); + sdnGpioPin.Set(); + + gpio0GpioPin.Direction(hal::PinDirection::out); + gpio0GpioPin.Reset(); + + watchdogResetGpioPin.Direction(hal::PinDirection::out); + watchdogResetGpioPin.Reset(); + AT(NOW() + 1 * MILLISECONDS); + watchdogResetGpioPin.Set(); + AT(NOW() + 1 * MILLISECONDS); + watchdogResetGpioPin.Reset(); + + constexpr auto baudrate = 10'000'000; + auto spiError = spi.init(baudrate, /*slave=*/false, /*tiMode=*/false); + if(spiError == -1) + { + RODOS::PRINTF("Error initializing RF SPI!\n"); + // TODO: proper error handling + return; + } + + // Enable Si4463 and wait for PoR to finish + AT(NOW() + 100 * MILLISECONDS); + sdnGpioPin.Reset(); + AT(NOW() + 20 * MILLISECONDS); +} + + +auto GetPartInfo() -> std::uint16_t +{ + auto const sendBuffer = std::to_array({cmdPartInfo}); + auto responseBuffer = SendCommandWithResponse(sendBuffer); + + // NOLINTNEXTLINE(hicpp-signed-bitwise) + return static_cast(static_cast(responseBuffer[1]) << CHAR_BIT + | static_cast(responseBuffer[2])); +} + + +// --- Private function definitions --- + +auto PowerUp(PowerUpBootOptions bootOptions, + PowerUpXtalOptions xtalOptions, + std::uint32_t xoFrequency) -> void +{ + auto const powerUpBuffer = std::to_array( + {cmdPowerUp, + static_cast(bootOptions), + static_cast(xtalOptions), + static_cast(xoFrequency >> (CHAR_BIT * 3)), // NOLINT(hicpp-signed-bitwise) + static_cast(xoFrequency >> (CHAR_BIT * 2)), // NOLINT(hicpp-signed-bitwise) + static_cast(xoFrequency >> (CHAR_BIT)), // NOLINT(hicpp-signed-bitwise) + static_cast(xoFrequency)}); + + SendCommandNoResponse(powerUpBuffer); +} + + +auto SendCommandNoResponse(std::span commandBuffer) -> void +{ + csGpioPin.Reset(); + AT(NOW() + 20 * MICROSECONDS); + hal::WriteTo(&spi, commandBuffer); + AT(NOW() + 2 * MICROSECONDS); + csGpioPin.Set(); + WaitOnCts(); + // No response -> just set the CS pin again + csGpioPin.Set(); +} + + +template +auto SendCommandWithResponse(std::span commandBuffer) + -> std::array +{ + csGpioPin.Reset(); + AT(NOW() + 20 * MICROSECONDS); + hal::WriteTo(&spi, commandBuffer); + AT(NOW() + 2 * MICROSECONDS); + csGpioPin.Set(); + + auto responseBuffer = std::array{}; + WaitOnCts(); + // WaitOnCts leaves CS pin low, read response afterwards + hal::ReadFrom(&spi, std::span(responseBuffer)); + csGpioPin.Set(); + + return responseBuffer; +} + + +//! @brief Polls the CTS byte until 0xFF is received (i.e. Si4463 is ready for command). +auto WaitOnCts() -> void +{ + auto sendBuffer = std::to_array({cmdReadCmdBuff}); + do + { + AT(NOW() + 20 * MICROSECONDS); + csGpioPin.Reset(); + AT(NOW() + 20 * MICROSECONDS); + + hal::WriteTo(&spi, std::span(sendBuffer)); + auto ctsBuffer = std::array{}; + hal::ReadFrom(&spi, std::span(ctsBuffer)); + + if(ctsBuffer[0] != readyCtsByte) + { + AT(NOW() + 2 * MICROSECONDS); + csGpioPin.Set(); + } + else + { + break; + } + } while(true); +} +} diff --git a/Sts1CobcSw/Periphery/Rf.hpp b/Sts1CobcSw/Periphery/Rf.hpp new file mode 100644 index 00000000..a16e4a8e --- /dev/null +++ b/Sts1CobcSw/Periphery/Rf.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include + + +namespace sts1cobcsw::periphery::rf +{ +auto Initialize() -> void; +auto InitializeGpioAndSpi() -> void; +auto GetPartInfo() -> std::uint16_t; +} \ No newline at end of file diff --git a/Sts1CobcSw/Periphery/RfNames.hpp b/Sts1CobcSw/Periphery/RfNames.hpp new file mode 100644 index 00000000..8690f344 --- /dev/null +++ b/Sts1CobcSw/Periphery/RfNames.hpp @@ -0,0 +1,82 @@ +#pragma once +#include + +#include + +namespace sts1cobcsw::periphery::rf +{ + +// In the following, abbreviations are used to adhere to the API documentation + +enum class TxType +{ + morse, // From GPIO pin + packet // From FIFO +}; + +// TODO: How will transmitting large amounts of data work? +inline constexpr auto maxRxBytes = 128; + +// Si4463 Command IDs +// TODO: change to enum (class)? +inline constexpr auto cmdNop = 0x00_b; +inline constexpr auto cmdPartInfo = 0x01_b; +inline constexpr auto cmdPowerUp = 0x02_b; +inline constexpr auto cmdFuncInfo = 0x10_b; +inline constexpr auto cmdSetProperty = 0x11_b; +inline constexpr auto cmdFifoInfo = 0x15_b; +inline constexpr auto cmdGetIntStatus = 0x20_b; +inline constexpr auto cmdStartTx = 0x31_b; +inline constexpr auto cmdChangeState = 0x34_b; +inline constexpr auto cmdReadCmdBuff = 0x44_b; + +// GetIntStatus constants +inline constexpr auto getIntStatusResponseLength = 8; + +inline constexpr auto partInfoResponseLength = 8; + +// Response to READY_CMD_BUFF if ready for command +inline constexpr auto readyCtsByte = 0xFF_b; + +// Property groups +enum class PropertyGroup : std::uint8_t +{ + global = 0x00, // Global + intCtl = 0x01, // Interrupt control + frrCtl = 0x02, // Fast response register control + preamble = 0x10, // Preamble + sync = 0x11, // Sync word + pkt = 0x12, // Packet + modem = 0x20, // + modemChflt = 0x21, // + pa = 0x22, // + synth = 0x23, // + match = 0x30, // + freqControl = 0x40, // + rxHop = 0x50, // + pti = 0xF0 // +}; + +// SetProperty command constants +// SetProperty starts with Cmd ID, Group, # of properties, Start property +inline constexpr auto setPropertyHeaderSize = 4; +inline constexpr auto maxNProperties = 12; + +// PowerUp command constants +enum class PowerUpBootOptions : std::uint8_t +{ + noPatch = 0x01, + patch = 0x81 +}; + +enum class PowerUpXtalOptions : std::uint8_t +{ + xtal = 0x00, // Reference signal is derived from the internal crystal oscillator + txco = 0x01 // Reference signal is derived from an external TCXO +}; + +enum class PowerMode : std::uint8_t +{ + standby = 0x01 +}; +} \ No newline at end of file diff --git a/Tests/HardwareTests/CMakeLists.txt b/Tests/HardwareTests/CMakeLists.txt index 93896aec..1bdec444 100644 --- a/Tests/HardwareTests/CMakeLists.txt +++ b/Tests/HardwareTests/CMakeLists.txt @@ -35,6 +35,9 @@ target_link_libraries( add_program(Gpio Gpio.test.cpp) target_link_libraries(Sts1CobcSwTests_Gpio PRIVATE rodos::rodos Sts1CobcSw_Hal) +add_program(Rf Rf.test.cpp) +target_link_libraries(Sts1CobcSwTests_Rf PRIVATE rodos::rodos Sts1CobcSw_Hal Sts1CobcSw_Periphery Sts1CobcSwTests_Utility) + add_program(Uart Uart.test.cpp) target_link_libraries(Sts1CobcSwTests_Uart PRIVATE rodos::rodos Sts1CobcSw_Hal Sts1CobcSw_Utility) diff --git a/Tests/HardwareTests/Rf.test.cpp b/Tests/HardwareTests/Rf.test.cpp new file mode 100644 index 00000000..0df6c8e0 --- /dev/null +++ b/Tests/HardwareTests/Rf.test.cpp @@ -0,0 +1,52 @@ +#include +#include + +#include + +#include + +#include +#include + + +namespace sts1cobcsw +{ +using RODOS::PRINTF; + + +auto uciUart = RODOS::HAL_UART(hal::uciUartIndex, hal::uciUartTxPin, hal::uciUartRxPin); + + +class RfTest : public RODOS::StaticThread<> +{ +public: + RfTest() : StaticThread("RfTest") + { + } + + +private: + void init() override + { + constexpr auto uartBaudRate = 115200; + uciUart.init(uartBaudRate); + } + + + void run() override + { + PRINTF("\nRF test\n\n"); + + periphery::rf::Initialize(); + PRINTF("RF module initialized\n"); + + PRINTF("\n"); + auto correctPartInfo = 0x4463; + auto partInfo = periphery::rf::GetPartInfo(); + PRINTF("Part info: 0x%4x == 0x%4x\n", partInfo, correctPartInfo); + Check(partInfo == correctPartInfo); + + // Here comes the rest of the RF test + } +} rfTest; +} From a42c31e1d2eb7d7d162403340b47194e2e76d8aa Mon Sep 17 00:00:00 2001 From: Daniel Schloms Date: Sat, 3 Feb 2024 18:21:56 +0100 Subject: [PATCH 2/5] Remove RfNames.hpp and move relevant parts to Rf.cpp --- Sts1CobcSw/Periphery/RfNames.hpp | 82 -------------------------------- 1 file changed, 82 deletions(-) delete mode 100644 Sts1CobcSw/Periphery/RfNames.hpp diff --git a/Sts1CobcSw/Periphery/RfNames.hpp b/Sts1CobcSw/Periphery/RfNames.hpp deleted file mode 100644 index 8690f344..00000000 --- a/Sts1CobcSw/Periphery/RfNames.hpp +++ /dev/null @@ -1,82 +0,0 @@ -#pragma once -#include - -#include - -namespace sts1cobcsw::periphery::rf -{ - -// In the following, abbreviations are used to adhere to the API documentation - -enum class TxType -{ - morse, // From GPIO pin - packet // From FIFO -}; - -// TODO: How will transmitting large amounts of data work? -inline constexpr auto maxRxBytes = 128; - -// Si4463 Command IDs -// TODO: change to enum (class)? -inline constexpr auto cmdNop = 0x00_b; -inline constexpr auto cmdPartInfo = 0x01_b; -inline constexpr auto cmdPowerUp = 0x02_b; -inline constexpr auto cmdFuncInfo = 0x10_b; -inline constexpr auto cmdSetProperty = 0x11_b; -inline constexpr auto cmdFifoInfo = 0x15_b; -inline constexpr auto cmdGetIntStatus = 0x20_b; -inline constexpr auto cmdStartTx = 0x31_b; -inline constexpr auto cmdChangeState = 0x34_b; -inline constexpr auto cmdReadCmdBuff = 0x44_b; - -// GetIntStatus constants -inline constexpr auto getIntStatusResponseLength = 8; - -inline constexpr auto partInfoResponseLength = 8; - -// Response to READY_CMD_BUFF if ready for command -inline constexpr auto readyCtsByte = 0xFF_b; - -// Property groups -enum class PropertyGroup : std::uint8_t -{ - global = 0x00, // Global - intCtl = 0x01, // Interrupt control - frrCtl = 0x02, // Fast response register control - preamble = 0x10, // Preamble - sync = 0x11, // Sync word - pkt = 0x12, // Packet - modem = 0x20, // - modemChflt = 0x21, // - pa = 0x22, // - synth = 0x23, // - match = 0x30, // - freqControl = 0x40, // - rxHop = 0x50, // - pti = 0xF0 // -}; - -// SetProperty command constants -// SetProperty starts with Cmd ID, Group, # of properties, Start property -inline constexpr auto setPropertyHeaderSize = 4; -inline constexpr auto maxNProperties = 12; - -// PowerUp command constants -enum class PowerUpBootOptions : std::uint8_t -{ - noPatch = 0x01, - patch = 0x81 -}; - -enum class PowerUpXtalOptions : std::uint8_t -{ - xtal = 0x00, // Reference signal is derived from the internal crystal oscillator - txco = 0x01 // Reference signal is derived from an external TCXO -}; - -enum class PowerMode : std::uint8_t -{ - standby = 0x01 -}; -} \ No newline at end of file From c5749e7848a120588e3016c6c187408d20eee2ea Mon Sep 17 00:00:00 2001 From: Daniel Schloms Date: Sat, 3 Feb 2024 18:37:47 +0100 Subject: [PATCH 3/5] Address requested changes in PR --- Sts1CobcSw/Periphery/Rf.cpp | 60 ++++++++++++++++++++---------- Sts1CobcSw/Periphery/Rf.hpp | 8 +--- Tests/HardwareTests/CMakeLists.txt | 2 +- Tests/HardwareTests/Rf.test.cpp | 11 +----- 4 files changed, 43 insertions(+), 38 deletions(-) diff --git a/Sts1CobcSw/Periphery/Rf.cpp b/Sts1CobcSw/Periphery/Rf.cpp index e672fd7a..4e395ea1 100644 --- a/Sts1CobcSw/Periphery/Rf.cpp +++ b/Sts1CobcSw/Periphery/Rf.cpp @@ -2,17 +2,13 @@ #include #include #include -#include #include -#include #include #include #include -#include #include -#include namespace sts1cobcsw::periphery::rf @@ -22,7 +18,19 @@ using RODOS::MICROSECONDS; using RODOS::MILLISECONDS; using RODOS::NOW; -// --- Globals --- +enum class PowerUpBootOptions : std::uint8_t +{ + noPatch = 0x01, + patch = 0x81 +}; + +enum class PowerUpXtalOptions : std::uint8_t +{ + xtal = 0x00, // Reference signal is derived from the internal crystal oscillator + txco = 0x01 // Reference signal is derived from an external TCXO +}; + +// --- Private globals --- auto spi = RODOS::HAL_SPI( hal::rfSpiIndex, hal::rfSpiSckPin, hal::rfSpiMisoPin, hal::rfSpiMosiPin, hal::spiNssDummyPin); @@ -36,11 +44,23 @@ auto paEnablePin = hal::GpioPin(hal::rfPaEnablePin); // TODO: This should probably be somewhere else as it is not directly related to the RF module auto watchdogResetGpioPin = hal::GpioPin(hal::watchdogClearPin); -constexpr std::uint16_t partInfo = 0x4463; constexpr std::uint32_t powerUpXoFrequency = 26'000'000; // 26 MHz +// Si4463 command headers +constexpr auto cmdPartInfo = 0x01_b; +constexpr auto cmdPowerUp = 0x02_b; +constexpr auto cmdReadCmdBuff = 0x44_b; + +// Command response lengths +constexpr auto partInfoResponseLength = 8U; + +// Check for this value when waiting for the Si4463 (WaitOnCts()) +constexpr auto readyCtsByte = 0xFF_b; + // --- Private function declarations --- +auto InitializeGpioAndSpi() -> void; + auto PowerUp(PowerUpBootOptions bootOptions, PowerUpXtalOptions xtalOptions, std::uint32_t xoFrequency) -> void; @@ -68,6 +88,19 @@ auto Initialize() -> void } +auto ReadPartInfo() -> std::uint16_t +{ + auto const sendBuffer = std::to_array({cmdPartInfo}); + auto responseBuffer = SendCommandWithResponse(sendBuffer); + + // NOLINTNEXTLINE(hicpp-signed-bitwise) + return static_cast(static_cast(responseBuffer[1]) << CHAR_BIT + | static_cast(responseBuffer[2])); +} + + +// --- Private function definitions --- + auto InitializeGpioAndSpi() -> void { csGpioPin.Direction(hal::PinDirection::out); @@ -104,19 +137,6 @@ auto InitializeGpioAndSpi() -> void } -auto GetPartInfo() -> std::uint16_t -{ - auto const sendBuffer = std::to_array({cmdPartInfo}); - auto responseBuffer = SendCommandWithResponse(sendBuffer); - - // NOLINTNEXTLINE(hicpp-signed-bitwise) - return static_cast(static_cast(responseBuffer[1]) << CHAR_BIT - | static_cast(responseBuffer[2])); -} - - -// --- Private function definitions --- - auto PowerUp(PowerUpBootOptions bootOptions, PowerUpXtalOptions xtalOptions, std::uint32_t xoFrequency) -> void @@ -189,7 +209,7 @@ auto WaitOnCts() -> void else { break; - } + } } while(true); } } diff --git a/Sts1CobcSw/Periphery/Rf.hpp b/Sts1CobcSw/Periphery/Rf.hpp index a16e4a8e..18e41848 100644 --- a/Sts1CobcSw/Periphery/Rf.hpp +++ b/Sts1CobcSw/Periphery/Rf.hpp @@ -1,17 +1,11 @@ #pragma once -#include -#include -#include #include -#include -#include namespace sts1cobcsw::periphery::rf { auto Initialize() -> void; -auto InitializeGpioAndSpi() -> void; -auto GetPartInfo() -> std::uint16_t; +auto ReadPartInfo() -> std::uint16_t; } \ No newline at end of file diff --git a/Tests/HardwareTests/CMakeLists.txt b/Tests/HardwareTests/CMakeLists.txt index 1bdec444..916349e7 100644 --- a/Tests/HardwareTests/CMakeLists.txt +++ b/Tests/HardwareTests/CMakeLists.txt @@ -36,7 +36,7 @@ add_program(Gpio Gpio.test.cpp) target_link_libraries(Sts1CobcSwTests_Gpio PRIVATE rodos::rodos Sts1CobcSw_Hal) add_program(Rf Rf.test.cpp) -target_link_libraries(Sts1CobcSwTests_Rf PRIVATE rodos::rodos Sts1CobcSw_Hal Sts1CobcSw_Periphery Sts1CobcSwTests_Utility) +target_link_libraries(Sts1CobcSwTests_Rf PRIVATE rodos::rodos Sts1CobcSw_Periphery Sts1CobcSwTests_Utility) add_program(Uart Uart.test.cpp) target_link_libraries(Sts1CobcSwTests_Uart PRIVATE rodos::rodos Sts1CobcSw_Hal Sts1CobcSw_Utility) diff --git a/Tests/HardwareTests/Rf.test.cpp b/Tests/HardwareTests/Rf.test.cpp index 0df6c8e0..3969d7fc 100644 --- a/Tests/HardwareTests/Rf.test.cpp +++ b/Tests/HardwareTests/Rf.test.cpp @@ -1,22 +1,15 @@ -#include #include #include #include -#include -#include - namespace sts1cobcsw { using RODOS::PRINTF; -auto uciUart = RODOS::HAL_UART(hal::uciUartIndex, hal::uciUartTxPin, hal::uciUartRxPin); - - class RfTest : public RODOS::StaticThread<> { public: @@ -28,8 +21,6 @@ class RfTest : public RODOS::StaticThread<> private: void init() override { - constexpr auto uartBaudRate = 115200; - uciUart.init(uartBaudRate); } @@ -42,7 +33,7 @@ class RfTest : public RODOS::StaticThread<> PRINTF("\n"); auto correctPartInfo = 0x4463; - auto partInfo = periphery::rf::GetPartInfo(); + auto partInfo = periphery::rf::ReadPartInfo(); PRINTF("Part info: 0x%4x == 0x%4x\n", partInfo, correctPartInfo); Check(partInfo == correctPartInfo); From 6f7671852bf1f9c1c5af024b2b706507126a4e9c Mon Sep 17 00:00:00 2001 From: Daniel Schloms Date: Sat, 3 Feb 2024 18:53:32 +0100 Subject: [PATCH 4/5] Add code for full RF initialization from mft branch --- Sts1CobcSw/Periphery/Rf.cpp | 630 +++++++++++++++++++++++++++++++- Sts1CobcSw/Periphery/Rf.hpp | 8 +- Tests/HardwareTests/Rf.test.cpp | 2 +- 3 files changed, 635 insertions(+), 5 deletions(-) diff --git a/Sts1CobcSw/Periphery/Rf.cpp b/Sts1CobcSw/Periphery/Rf.cpp index 4e395ea1..e593b738 100644 --- a/Sts1CobcSw/Periphery/Rf.cpp +++ b/Sts1CobcSw/Periphery/Rf.cpp @@ -30,6 +30,24 @@ enum class PowerUpXtalOptions : std::uint8_t txco = 0x01 // Reference signal is derived from an external TCXO }; +enum class PropertyGroup : std::uint8_t +{ + global = 0x00, // + intCtl = 0x01, // Interrupt control + frrCtl = 0x02, // Fast response register control + preamble = 0x10, // + sync = 0x11, // Sync word + pkt = 0x12, // Packet + modem = 0x20, // + modemChflt = 0x21, // + pa = 0x22, // Power amplifier + synth = 0x23, // + match = 0x30, // + freqControl = 0x40, // + rxHop = 0x50, // + pti = 0xF0 // Packet trace interface +}; + // --- Private globals --- auto spi = RODOS::HAL_SPI( @@ -46,9 +64,10 @@ auto watchdogResetGpioPin = hal::GpioPin(hal::watchdogClearPin); constexpr std::uint32_t powerUpXoFrequency = 26'000'000; // 26 MHz -// Si4463 command headers +// Si4463 commands constexpr auto cmdPartInfo = 0x01_b; constexpr auto cmdPowerUp = 0x02_b; +constexpr auto cmdSetProperty = 0x11_b; constexpr auto cmdReadCmdBuff = 0x44_b; // Command response lengths @@ -57,6 +76,10 @@ constexpr auto partInfoResponseLength = 8U; // Check for this value when waiting for the Si4463 (WaitOnCts()) constexpr auto readyCtsByte = 0xFF_b; +// Max. number of properties that can be set in a single command +constexpr auto maxNProperties = 12; +constexpr auto setPropertyHeaderSize = 4; + // --- Private function declarations --- auto InitializeGpioAndSpi() -> void; @@ -65,6 +88,11 @@ auto PowerUp(PowerUpBootOptions bootOptions, PowerUpXtalOptions xtalOptions, std::uint32_t xoFrequency) -> void; +[[deprecated]] auto SendCommand(std::uint8_t * data, + std::size_t length, + std::uint8_t * responseData, + std::size_t responseLength) -> void; + auto SendCommandNoResponse(std::span commandBuffer) -> void; template @@ -73,18 +101,522 @@ auto SendCommandWithResponse(std::span commandBuffer) auto WaitOnCts() -> void; +auto SetTxType(TxType txType) -> void; + +template + requires(nProperties >= 1 and nProperties <= maxNProperties) +auto SetProperty(PropertyGroup propertyGroup, + Byte startProperty, + std::span propertyValues) -> void; + // --- Public function definitions --- // TODO: Get rid of all the magic numbers // TODO: Replace all C-style arrays with std::array -auto Initialize() -> void +auto Initialize(TxType txType) -> void { + // TODO: Don't forget that WDT_Clear has to be triggered regularely for the TX to work! (even + // without the watchdog timer on the PCB it needs to be triggered at least once after boot to + // enable the TX) + InitializeGpioAndSpi(); + auto sendBuffer = std::array{}; + PowerUp(PowerUpBootOptions::noPatch, PowerUpXtalOptions::xtal, powerUpXoFrequency); - // Here comes the initialization of the RF module + // GPIO Pin Cfg + sendBuffer[0] = 0x13; + sendBuffer[1] = 0x00; + sendBuffer[2] = 0x00; + sendBuffer[3] = 0x00; + sendBuffer[4] = 0x00; + sendBuffer[5] = 0x00; + sendBuffer[6] = 0x00; + sendBuffer[7] = 0x00; + SendCommand(data(sendBuffer), 8, nullptr, 0); + + // Global XO Tune 2 + sendBuffer[0] = 0x11; + sendBuffer[1] = 0x00; + sendBuffer[2] = 0x02; + sendBuffer[3] = 0x00; + sendBuffer[4] = 0x52; // GLOBAL_XO_TUNE + sendBuffer[5] = 0x00; // GLOBAL_CLK_CFG + SendCommand(data(sendBuffer), 6, nullptr, 0); + + // RF Global Config 1 + sendBuffer[0] = 0x11; + sendBuffer[1] = 0x00; + sendBuffer[2] = 0x01; + sendBuffer[3] = 0x03; + sendBuffer[4] = 0x60; // GLOBAL_CONFIG: High performance mode, Generic packet format, Split + // FiFo mode, Fast sequencer mode + SendCommand(data(sendBuffer), 5, nullptr, 0); + + // RF Int Ctl Enable + sendBuffer[0] = 0x11; + sendBuffer[1] = 0x01; + sendBuffer[2] = 0x01; + sendBuffer[3] = 0x00; + sendBuffer[4] = 0x01; // INT_CTL: Enable packet handler interrupts + SendCommand(data(sendBuffer), 5, nullptr, 0); + + // TX Preamble Length + sendBuffer[0] = 0x11; + sendBuffer[1] = 0x10; + sendBuffer[2] = 0x09; + sendBuffer[3] = 0x00; + sendBuffer[4] = 0x00; // PREAMBLE_TX_LENGTH: 0 bytes preamble + sendBuffer[5] = 0x14; // PREAMBLE_CONFIG_STD_1: Normal sync timeout, + // 14 bit preamble RX threshold + sendBuffer[6] = 0x00; // PREAMBLE_CONFIG_NSTD: No non-standard preamble pattern TODO: Maybe we + // can detect RS+CC encoded preamble this way and be CCSDS compliant on + // uplink too? Problem: Max pattern length is 32 bit + sendBuffer[7] = 0x0F; // PREAMBLE_CONFIG_STD_2: No extended RX preamble timeout, 0x0f nibbles + // timeout until detected preamble is discarded as invalid + sendBuffer[8] = 0x31; // PREAMBLE_CONFIG: First transmitted preamble bit is 1, unit of + // preampreamble TX length is in bytes + sendBuffer[9] = 0x00; // PREAMBLE_PATTERN: Non-standard pattern + sendBuffer[10] = 0x00; // Non-standard pattern + sendBuffer[11] = 0x00; // Non-standard pattern + sendBuffer[12] = 0x00; // Non-standard pattern + SendCommand(data(sendBuffer), 13, nullptr, 0); + + // Sync word config + sendBuffer[0] = 0x11; + sendBuffer[1] = 0x11; + sendBuffer[2] = 0x05; + sendBuffer[3] = 0x00; + sendBuffer[4] = 0x43; // SYNC_CONFIG: Allow 4 bit sync word errors, 4 byte sync word + sendBuffer[5] = 0b01011000; // SYNC_BITS: Valid CCSDS TM sync word for + // Reed-Solomon or convolutional coding + sendBuffer[6] = 0b11110011; // Be careful: Send order is MSB-first but Little endian so the + // lowest bit of the + sendBuffer[7] = 0b00111111; // highest byte is transmitted first, + // which is different to how the CCSDS spec + sendBuffer[8] = 0b10111000; // annotates those bit patterns! + // TODO: Check that pattern! + SendCommand(data(sendBuffer), 9, nullptr, 0); + + // CRC Config + sendBuffer[0] = 0x11; + sendBuffer[1] = 0x12; + sendBuffer[2] = 0x01; + sendBuffer[3] = 0x00; + sendBuffer[4] = 0x00; // PKT_CRC_CONFIG: No CRC + SendCommand(data(sendBuffer), 5, nullptr, 0); + + // Whitening and Packet Parameters + sendBuffer[0] = 0x11; + sendBuffer[1] = 0x12; + sendBuffer[2] = 0x02; + sendBuffer[3] = 0x05; + sendBuffer[4] = 0x00; // PKT_WHT_BIT_NUM: Disable whitening + sendBuffer[5] = 0x01; // PKT_CONFIG1: Don't split RX and TX field information (length, ...), + // enable RX packet handler, use normal (2)FSK, no Manchester coding, no + // CRC, data transmission with MSB first. + SendCommand(data(sendBuffer), 6, nullptr, 0); + + // Pkt Length part 1 + sendBuffer[0] = 0x11; + sendBuffer[1] = 0x12; + sendBuffer[2] = 0x0C; + sendBuffer[3] = 0x08; + sendBuffer[4] = 0x60; // PKT_LEN: Infinite receive, big endian (MSB first) + sendBuffer[5] = 0x00; // PKT_LEN_FIELD_SOURCE + sendBuffer[6] = 0x00; // PKT_LEN_ADJUST + sendBuffer[7] = 0x30; // PKT_TX_THRESHOLD: Trigger TX FiFo almost empty interrupt when 0x30 + // bytes in FiFo (size 0x40) are empty + sendBuffer[8] = 0x30; // PKT_RX_THRESHOLD: Trigger RX FiFo almost full interrupt when 0x30 + // bytes in FiFo (size 0x40) are full + sendBuffer[9] = 0x00; // PKT_FIELD_1_LENGTH + sendBuffer[10] = 0x00; + sendBuffer[11] = 0x04; // PKT_FIELD_1_CONFIG + sendBuffer[12] = 0x80; // PKT_FIELD_1_CRC_CONFIG + sendBuffer[13] = 0x00; // PKT_FIELD_2_LENGTH + sendBuffer[14] = 0x00; + sendBuffer[15] = 0x00; // PKT_FIELD_2_CONFIG + SendCommand(data(sendBuffer), 16, nullptr, 0); + + // Pkt Length part 2 + sendBuffer[0] = 0x11; + sendBuffer[1] = 0x12; + sendBuffer[2] = 0x0C; + sendBuffer[3] = 0x14; + sendBuffer[4] = 0x00; // PKT_FIELD_2_CRC_CONFIG + sendBuffer[5] = 0x00; // PKT_FIELD_3_LENGTH + sendBuffer[6] = 0x00; + sendBuffer[7] = 0x00; // PKT_FIELD_3_CONFIG + sendBuffer[8] = 0x00; // PKT_FIELD_3_CRC_CONFIG + sendBuffer[9] = 0x00; // PKT_FIELD_4_LENGTH + sendBuffer[10] = 0x00; + sendBuffer[11] = 0x00; // PKT_FIELD_4_CONFIG + sendBuffer[12] = 0x00; // PKT_FIELD_4_CRC_CONFIG + sendBuffer[13] = 0x00; // PKT_FIELD_5_LENGTH + sendBuffer[14] = 0x00; + sendBuffer[15] = 0x00; // PKT_FIELD_5_CONFIG + SendCommand(data(sendBuffer), 16, nullptr, 0); + + // Pkt Length part 3 + sendBuffer[0] = 0x11; + sendBuffer[1] = 0x12; + sendBuffer[2] = 0x0C; + sendBuffer[3] = 0x20; + sendBuffer[4] = 0x00; // PKT_FIELD_5_CRC_CONFIG + sendBuffer[5] = 0x00; // PKT_RX_FIELD_1_LENGTH + sendBuffer[6] = 0x00; + sendBuffer[7] = 0x00; // PKT_RX_FIELD_1_CONFIG + sendBuffer[8] = 0x00; // PKT_RX_FIELD_1_CRC_CONFIG + sendBuffer[9] = 0x00; // PKT_RX_FIELD_2_LENGTH + sendBuffer[10] = 0x00; + sendBuffer[11] = 0x00; // PKT_RX_FIELD_2_CONFIG + sendBuffer[12] = 0x00; // PKT_RX_FIELD_2_CRC_CONFIG + sendBuffer[13] = 0x00; // PKT_RX_FIELD_3_LENGTH + sendBuffer[14] = 0x00; + sendBuffer[15] = 0x00; // PKT_RX_FIELD_3_CONFIG + SendCommand(data(sendBuffer), 16, nullptr, 0); + + // Pkt Length part 4 + sendBuffer[0] = 0x11; + sendBuffer[1] = 0x12; + sendBuffer[2] = 0x09; + sendBuffer[3] = 0x2C; + sendBuffer[4] = 0x00; // PKT_RX_FIELD_3_CRC_CONFIG + sendBuffer[5] = 0x00; // PKT_RX_FIELD_4_LENGTH + sendBuffer[6] = 0x00; + sendBuffer[7] = 0x00; // PKT_RX_FIELD_4_CONFIG + sendBuffer[8] = 0x00; // PKT_RX_FIELD_4_CRC_CONFIG + sendBuffer[9] = 0x00; // PKT_RX_FIELD_5_LENGTH + sendBuffer[10] = 0x00; + sendBuffer[11] = 0x00; // PKT_RX_FIELD_5_CONFIG + sendBuffer[12] = 0x00; // PKT_RX_FIELD_5_CRC_CONFIG + SendCommand(data(sendBuffer), 13, nullptr, 0); + + // RF Modem Mod Type + SetTxType(txType); + sendBuffer[0] = 0x11; + sendBuffer[1] = 0x20; + sendBuffer[2] = 0x07; + sendBuffer[3] = 0x06; // SetTxType sets modem properties from 0x00 to 0x05 + sendBuffer[4] = 0x00; // MODEM_TX_NCO_MODE: TXOSR=x10=0, NCOMOD=F_XTAL/10=2600000=0x027ac40 + sendBuffer[5] = 0x27; + sendBuffer[6] = 0xAC; + sendBuffer[7] = 0x40; + sendBuffer[8] = 0x00; // MODEM_FREQ_DEVIATION: (2^19 * outdiv * deviation_Hz)/(N_presc * F_xo) + // = (2^19 * 8 * 9600/4)/(2 * 26000000) = 194 = 0x0000C2 + sendBuffer[9] = 0x00; + sendBuffer[10] = 0xC2; + SendCommand(data(sendBuffer), 11, nullptr, 0); + + + // RF Modem TX Ramp Delay, Modem MDM Ctrl, Modem IF Ctrl, Modem IF Freq & Modem Decimation Cfg + sendBuffer[0] = 0x11; + sendBuffer[1] = 0x20; + sendBuffer[2] = 0x08; + sendBuffer[3] = 0x18; + sendBuffer[4] = 0x01; // MODEM_TX_RAMP_DELAY: Ramp Delay 1 + sendBuffer[5] = 0x80; // MODEM_MDM_CTRL: Slicer phase source from detector's output + sendBuffer[6] = 0x08; // MODEM_IF_CONTROL: No ETSI mode, fixed IF mode, + // normal IF mode (nonzero IF) + sendBuffer[7] = 0x03; // MODEM_IF_FREQ: IF = (2^19 * outdiv * IF_Freq_Hz)/(npresc * freq_xo) = + // (2^19 * 8 * xxx)/(2 * 26000000) = 0x03C000 (default value) + // TODO: Is it important what we chose here? + sendBuffer[8] = 0xC0; + sendBuffer[9] = 0x00; + sendBuffer[10] = 0x70; // MODEM_DECIMATION_CFG1: Decimation NDEC0 = 0, NDEC1 = decimation by 8, + // NDEC2 = decimation by 2 + sendBuffer[11] = 0x20; // MODEM_DECIMATION_CFG0: Normal decimate-by-8 filter gain, + // don't bypass the + // decimate-by-2 polyphase filter, bypass the decimate-by-3 polyphase filter, enable + // droop compensation, channel selection filter in normal mode (27 tap filter) + SendCommand(data(sendBuffer), 12, nullptr, 0); + + // RF Modem BCR Oversampling Rate, Modem BCR NCO Offset, Modem BCR Gain, Modem BCR Gear & Modem + // BCR Misc + // TODO: What values to use here? + sendBuffer[0] = 0x11; + sendBuffer[1] = 0x20; + sendBuffer[2] = 0x09; + sendBuffer[3] = 0x22; + sendBuffer[4] = 0x03; // MODEM_BCR_OSR: RX symbol oversampling rate of 0x30D/8 = 781/8 = 97.625 + // (According to the datasheet usual values are in the range of 8 to 12 + // where this value seems to be odd?) + sendBuffer[5] = 0x0D; + sendBuffer[6] = 0x00; // MODEM_BCR_NCO_OFFSET: BCR NCO offset of + // 0x00A7C6/64 = 42950/64 = 671.09375 + sendBuffer[7] = 0xA7; + sendBuffer[8] = 0xC6; + sendBuffer[9] = 0x00; // MODEM_BCR_GAIN: BCR gain 0x054 = 84 + sendBuffer[10] = 0x54; + sendBuffer[11] = 0x02; // MODEM_BCR_GEAR: BCR loop gear control. CRSLOW=2, CRFAST=0 + sendBuffer[12] = 0xC2; // MODEM_BCR_MISC1: Stop NCO for one sample clock in BCR mid-point phase + // sampling condition to escape, disable NCO resetting in case of + // mid-point phase sampling condition, don't double BCR loop gain, BCR + // NCO compensation is sampled upon detection of the preamble end, + // disable NCO frequency compensation, bypass compensation term feedback + // to slicer, bypass compensation term feedback to BCR tracking loop + SendCommand(data(sendBuffer), 13, nullptr, 0); + + // RF Modem AFC Gear, Modem AFC Wait, Modem AFC Gain, Modem AFC Limiter & Modem AFC Misc + // TODO: What values to use here? + sendBuffer[0] = 0x11; + sendBuffer[1] = 0x20; + sendBuffer[2] = 0x07; + sendBuffer[3] = 0x2C; + sendBuffer[4] = 0x04; // MODEM_AFC_GEAR: AFC_SLOW gain 4, AFC_FAST gain 0, Switch gear after + // detection of preamble + sendBuffer[5] = 0x36; // MODEM_AFC_WAIT: LGWAIT = 6, SHWAIT = 3 + sendBuffer[6] = 0x80; // MODEM_AFC_GAIN: AFC loop gain = 0x003, don't half the loop gain, + // disable adaptive RX bandwidth, enable frequency error estimation + sendBuffer[7] = 0x03; + sendBuffer[8] = 0x30; // MODEM_AFC_LIMITER: 0x30AF + sendBuffer[9] = 0xAF; + sendBuffer[10] = + 0x80; // MODEM_AFC_MISC: Expected freq error is less then 12*symbol rate, AFC correction of + // PLL will be frozen if a consecutive string of 1s or 0s that exceed the search + // period is encountered, don't switch clock source for frequency estimator, don't + // freeze AFC at preamble end, AFC correction uses freq estimation by moving average + // or minmax detector in async demod,disable AFC value feedback to PLL, freeze AFC + // after gear switching + SendCommand(data(sendBuffer), 11, nullptr, 0); + + // RF Modem AGC Control + // TODO: What values to use here? + sendBuffer[0] = 0x11; + sendBuffer[1] = 0x20; + sendBuffer[2] = 0x01; + sendBuffer[3] = 0x35; + sendBuffer[4] = 0xE2; // MODEM_AGC_CONTROL: reset peak detectors only on change of gain + // indicated by peak detector output, reduce ADC gain when AGC gain is at + // minimum, normal AGC speed, don't increase AGC gain during signal + // reductions in ant diversity mode, always perform gain decreases in 3dB + // steps instead of 6dB steps, AGC is enabled over whole packet length + SendCommand(data(sendBuffer), 5, nullptr, 0); + + // RF Modem AGC Window Size, AGC RF Peak Detector Decay, AGC IF Peak Detector Decay, 4FSK Gain, + // 4FSK Slicer Threshold, 4FSK SYmbol Mapping Code, OOK Attack/Decay Times + // TODO: What values to use here? + sendBuffer[0] = 0x11; + sendBuffer[1] = 0x20; + sendBuffer[2] = 0x09; + sendBuffer[3] = 0x38; + sendBuffer[4] = 0x11; // MODEM_AGC_WINDOW_SIZE: AGC gain settling window size = 1, AGC signal + // level measurement window = 1 + sendBuffer[5] = 0xAB; // MODEM_AGC_RFPD_DECAY: RF peak detector decay time = 0xAB = 171 + sendBuffer[6] = 0xAB; // MODEM_AGC_IFPD_DECAY: IF peak detector decay time = 0xAB = 171 + sendBuffer[7] = 0x00; // MODEM_FSK4_GAIN1: 4FSK Gain1 = 0, + // Normal second phase compensation factor + sendBuffer[8] = 0x02; // MODEM_FSK4_GAIN0: 4FSK Gain0 = 2, disable 2FSK phase compensation + sendBuffer[9] = 0xFF; // MODEM_FSK4_TH: 4FSK slicer threshold = 0xFFFF + sendBuffer[10] = 0xFF; + sendBuffer[11] = 0x00; // MODEM_FSK4_MAP: 4FSK symbol map 0 (`00 `01 `11 `10) + sendBuffer[12] = 0x2B; // MODEM_OOK_PDTC: OOK decay = 11, OOK attack = 2 + SendCommand(data(sendBuffer), 13, nullptr, 0); + + // RF Modem OOK Control, OOK Misc, RAW Search, RAW Control, RAW Eye, Antenna Diversity Mode, + // Antenna Diversity Control, RSSI Threshold + sendBuffer[0] = 0x11; + sendBuffer[1] = 0x20; + sendBuffer[2] = 0x09; + sendBuffer[3] = 0x42; + sendBuffer[4] = + 0xA4; // MODEM_OOK_CNT1: OOK Squelch off, OOK slicer output de-glitching by bit clock, raw + // output is synced to clock, MA_FREQUDOWN=0, AGC and OOK movign average detector + // threshold will be frozen after preamble detection, S2P_MAP=2 + sendBuffer[5] = 0x02; // MODEM_OOK_MISC: OOK uses moving average detector, OOK peak detector + // discharge does not affect decay rate, disable OOK squelch, always + // discharge peak detector, normal moving average window + sendBuffer[6] = 0xD6; // ?? + sendBuffer[7] = 0x83; // MODEM_RAW_CONTROL + sendBuffer[8] = 0x00; // MODEM_RAW_EYE: RAW eye open detector threshold + sendBuffer[9] = 0xAD; + sendBuffer[10] = 0x01; // MODEM_ANT_DIV_MODE: Antenna diversity mode + sendBuffer[11] = 0x80; // MODEM_ANT_DIV_CONTROL: Antenna diversity control + sendBuffer[12] = 0xFF; // MODEM_RSSI_THRESH: Threshold for clear channel assessment and RSSI + // interrupt generation + SendCommand(data(sendBuffer), 13, nullptr, 0); + + // RF Modem RSSI Control + sendBuffer[0] = 0x11; + sendBuffer[1] = 0x20; + sendBuffer[2] = 0x01; + sendBuffer[3] = 0x4C; + sendBuffer[4] = 0x00; // MODEM_RSSI_CONTROL: Disable RSSI latch, RSSI value is avg over last + // 4*Tb bit periods, disable RSSI threshold check after latch + SendCommand(data(sendBuffer), 5, nullptr, 0); + + // RF Modem RSSI Compensation + // TODO: Measure this + sendBuffer[0] = 0x11; + sendBuffer[1] = 0x20; + sendBuffer[2] = 0x01; + sendBuffer[3] = 0x4E; + sendBuffer[4] = 0x40; // MODEM_RSSI_COMP: Compensation/offset of measured RSSI value + SendCommand(data(sendBuffer), 5, nullptr, 0); + + // RF Modem Clock generation Band + sendBuffer[0] = 0x11; + sendBuffer[1] = 0x20; + sendBuffer[2] = 0x01; + sendBuffer[3] = 0x51; + sendBuffer[4] = 0x0A; // MODEM_CLKGEN_BAND: Band = FVCO_DIV_8, high performance mode fixed + // prescaler div2, force recalibration + SendCommand(data(sendBuffer), 5, nullptr, 0); + + // RX Filter Coefficients + // TODO: What values to use here? + sendBuffer[0] = 0x11; + sendBuffer[1] = 0x21; + sendBuffer[2] = 0x0C; + sendBuffer[3] = 0x00; + sendBuffer[4] = 0xFF; + sendBuffer[5] = 0xC4; + sendBuffer[6] = 0x30; + sendBuffer[7] = 0x7F; + sendBuffer[8] = 0xF5; + sendBuffer[9] = 0xB5; + sendBuffer[10] = 0xB8; + sendBuffer[11] = 0xDE; + sendBuffer[12] = 0x05; + sendBuffer[13] = 0x17; + sendBuffer[14] = 0x16; + sendBuffer[15] = 0x0C; + SendCommand(data(sendBuffer), 16, nullptr, 0); + sendBuffer[0] = 0x11; + sendBuffer[1] = 0x21; + sendBuffer[2] = 0x0C; + sendBuffer[3] = 0x0C; + sendBuffer[4] = 0x03; + sendBuffer[5] = 0x00; + sendBuffer[6] = 0x15; + sendBuffer[7] = 0xFF; + sendBuffer[8] = 0x00; + sendBuffer[9] = 0x00; + sendBuffer[10] = 0xFF; + sendBuffer[11] = 0xC4; + sendBuffer[12] = 0x30; + sendBuffer[13] = 0x7F; + sendBuffer[14] = 0xF5; + sendBuffer[15] = 0xB5; + SendCommand(data(sendBuffer), 16, nullptr, 0); + sendBuffer[0] = 0x11; + sendBuffer[1] = 0x21; + sendBuffer[2] = 0x0C; + sendBuffer[3] = 0x18; + sendBuffer[4] = 0xB8; + sendBuffer[5] = 0xDE; + sendBuffer[6] = 0x05; + sendBuffer[7] = 0x17; + sendBuffer[8] = 0x16; + sendBuffer[9] = 0x0C; + sendBuffer[10] = 0x03; + sendBuffer[11] = 0x00; + sendBuffer[12] = 0x15; + sendBuffer[13] = 0xFF; + sendBuffer[14] = 0x00; + sendBuffer[15] = 0x00; + SendCommand(data(sendBuffer), 16, nullptr, 0); + + // RF PA Mode + sendBuffer[0] = 0x11; + sendBuffer[1] = 0x22; + sendBuffer[2] = 0x04; + sendBuffer[3] = 0x00; + sendBuffer[4] = 0x08; // PA_MODE: PA switching amp mode, PA_SEL = HP_COARSE, disable power + // sequencing, disable external TX ramp signal + sendBuffer[5] = + 0x18; // PA_PWR_LVL: Enabled PA fingers (sets output power but not linearly; 10µA bias + // current per enabled finger, complementary drive signal with 50% duty cycle) + sendBuffer[6] = 0x00; // PA_BIAS_CLKDUTY + sendBuffer[7] = 0x91; // PA_TC: Ramping time constant = 0x1B (~10us to full-0.5dB), FSK + // modulation delay 10µs + SendCommand(data(sendBuffer), 8, nullptr, 0); + + // RF Synth Feed Forward Charge Pump Current, Integrated Charge Pump Current, VCO Gain Scaling + // Factor, FF Loop Filter Values + // TODO: What values to use here? + sendBuffer[0] = 0x11; + sendBuffer[1] = 0x23; + sendBuffer[2] = 0x07; + sendBuffer[3] = 0x00; + sendBuffer[4] = 0x2C; // SYNTH_PFDCP_CPFF: FF charge pump current = 60µA + sendBuffer[5] = 0x0E; // SYNTH_PFDCP_CPINT: Int charge pump current = 30µA + sendBuffer[6] = 0x0B; // SYNTH_VCO_KV: Set VCO scaling factor to maximum value, set tuning + // varactor gain to maximum value + sendBuffer[7] = 0x04; // SYNTH_LPFILT3: R2 value 90kOhm + sendBuffer[8] = 0x0C; // SYNTH_LPFILT2: C2 value 11.25pF + sendBuffer[9] = 0x73; // SYNTH_LPFILT1: C3 value 12pF, C1 offset 0pF, C1 value 7.21pF + sendBuffer[10] = 0x03; // SYNTH_LPFILT0: FF amp bias current 100µA + SendCommand(data(sendBuffer), 11, nullptr, 0); + + // RF Match Mask + sendBuffer[0] = 0x11; + sendBuffer[1] = 0x30; + sendBuffer[2] = 0x0C; + sendBuffer[3] = 0x00; + sendBuffer[4] = 0x00; // MATCH_VALUE_1 + sendBuffer[5] = 0x00; // MATCH_MASK_1 + sendBuffer[6] = 0x00; // MATCH_CTRL_1 + sendBuffer[7] = 0x00; // MATCH_VALUE_2 + sendBuffer[8] = 0x00; // MATCH_MASK_2 + sendBuffer[9] = 0x00; // MATCH_CTRL_2 + sendBuffer[10] = 0x00; // MATCH_VALUE_3 + sendBuffer[11] = 0x00; // MATCH_MASK_3 + sendBuffer[12] = 0x00; // MATCH_CTRL_3 + sendBuffer[13] = 0x00; // MATCH_VALUE_4 + sendBuffer[14] = 0x00; // MATCH_MASK_4 + sendBuffer[15] = 0x00; // MATCH_CTRL_4 + SendCommand(data(sendBuffer), 16, nullptr, 0); + + // Frequency Control + sendBuffer[0] = 0x11; + sendBuffer[1] = 0x40; + sendBuffer[2] = 0x08; + sendBuffer[3] = 0x00; + sendBuffer[4] = 0x41; // FREQ_CONTROL_INTE: FC_inte = 0x41 + sendBuffer[5] = 0x0E; // FREQ_CONTROL_FRAC: FC_frac. 0xD89D9 = 433.5, 0xEC4EC = 434.5 + sendBuffer[6] = 0xC4; + sendBuffer[7] = 0xEC; + // N_presc = 2, outdiv = 8, F_xo = 26MHz + // RF_channel_Hz = (FC_inte + FC_frac/2^19)*((N_presc*F_xo)/outdiv) = 433.5000048MHz MHz + sendBuffer[8] = 0x44; // FREQ_CONTROL_CHANNEL_STEP_SIZE: Channel step size = 0x4444 + sendBuffer[9] = 0x44; + sendBuffer[10] = + 0x20; // FREQ_CONTROL_W_SIZE: Window gating period (in number of crystal clock cycles) = 32 + sendBuffer[11] = 0xFE; // FREQ_CONTROL_VCOCNT_RX_ADJ: Adjust target mode for VCO calibration in + // RX mode = 0xFE int8_t + SendCommand(data(sendBuffer), 12, nullptr, 0); + + // Set RF4463 Module Antenna Switch + sendBuffer[0] = 0x13; + sendBuffer[1] = 0x00; // Don't change GPIO0 setting + sendBuffer[2] = 0x00; // Don't change GPIO1 setting + sendBuffer[3] = 0x21; // GPIO2 is active in RX state + sendBuffer[4] = 0x20; // GPIO3 is active in TX state + sendBuffer[5] = 0x27; // NIRQ is still used as NIRQ + sendBuffer[6] = 0x0B; // SDO is still used as SDO + SendCommand(data(sendBuffer), 7, nullptr, 0); + + // Frequency Adjust (stolen from Arduino demo code) + sendBuffer[0] = 0x11; + sendBuffer[1] = 0x00; + sendBuffer[2] = 0x01; + sendBuffer[3] = 0x00; + sendBuffer[4] = 0x62; + SendCommand(data(sendBuffer), 5, nullptr, 0); + + // TX Buffer = RX Buffer = 64 Byte + sendBuffer[0] = 0x11; + sendBuffer[1] = 0x00; + sendBuffer[2] = 0x01; + sendBuffer[3] = 0x03; + sendBuffer[4] = 0x40; + SendCommand(data(sendBuffer), 5, nullptr, 0); + + paEnablePin.Direction(hal::PinDirection::out); + paEnablePin.Set(); } @@ -154,6 +686,43 @@ auto PowerUp(PowerUpBootOptions bootOptions, } +[[deprecated]] auto SendCommand(std::uint8_t * data, + std::size_t length, + std::uint8_t * responseData, + std::size_t responseLength) -> void +{ + // RODOS::PRINTF("SendCommand()\n"); + csGpioPin.Reset(); + AT(NOW() + 20 * MICROSECONDS); + spi.write(data, length); + AT(NOW() + 2 * MICROSECONDS); + csGpioPin.Set(); + + auto cts = std::to_array({0x00, 0x00}); + auto req = std::to_array({0x44, 0x00}); + do + { + AT(NOW() + 20 * MICROSECONDS); + csGpioPin.Reset(); + AT(NOW() + 20 * MICROSECONDS); + spi.writeRead(std::data(req), std::size(req), std::data(cts), std::size(cts)); + if(cts[1] != 0xFF) + { + AT(NOW() + 2 * MICROSECONDS); + csGpioPin.Set(); + } + } while(cts[1] != 0xFF); + + if(responseLength > 0) + { + spi.read(responseData, responseLength); + } + + AT(NOW() + 2 * MICROSECONDS); + csGpioPin.Set(); +} + + auto SendCommandNoResponse(std::span commandBuffer) -> void { csGpioPin.Reset(); @@ -212,4 +781,59 @@ auto WaitOnCts() -> void } } while(true); } + + +auto SetTxType(TxType txType) -> void +{ + Byte modulationMode; + std::uint32_t dataRate; + + if(txType == TxType::morse) + { + modulationMode = 0x09_b; // MODEM_MODE_TYPE: TX data from GPIO0 pin, modulation OOK + dataRate = 20'000; // MODEM_DATA_RATE: unused, 20k Baud + } + else + { + modulationMode = 0x03_b; // MODEM_MODE_TYPE: TX data from packet handler, modulation 2GFSK + dataRate = 9600; // MODEM_DATA_RATE: For 9k6 Baud: (TX_DATA_RATE * MODEM_TX_NCO_MODE * + // TXOSR)/F_XTAL_Hz = (9600 * 2600000 * 10)/26000000 = 9600 = 0x002580 + } + + auto propertyValues = std::to_array({modulationMode, + 0x00_b, + 0x07_b, // DSM default config + static_cast(dataRate >> (2 * CHAR_BIT)), + static_cast(dataRate >> (CHAR_BIT)), + static_cast(dataRate)}); + + SetProperty( + PropertyGroup::modem, 0x00_b, std::span(propertyValues)); +} + + +// TODO: This does not need any template stuff, just set the size of setPropertyBuffer to take 12 +// (max.) properties and use a subspan/.first() afterwards +template + requires(nProperties >= 1 and nProperties <= maxNProperties) +auto SetProperty(PropertyGroup propertyGroup, + Byte startProperty, + std::span propertyValues) -> void +{ + auto setPropertyBuffer = std::array{}; + + setPropertyBuffer[0] = cmdSetProperty; + setPropertyBuffer[1] = static_cast(propertyGroup); + setPropertyBuffer[2] = static_cast(nProperties); + setPropertyBuffer[3] = startProperty; + + auto bufferIndex = setPropertyHeaderSize; + for(auto && propertyValue : propertyValues) + { + setPropertyBuffer[bufferIndex] = propertyValue; + bufferIndex++; + } + + SendCommandNoResponse(setPropertyBuffer); +} } diff --git a/Sts1CobcSw/Periphery/Rf.hpp b/Sts1CobcSw/Periphery/Rf.hpp index 18e41848..569279c6 100644 --- a/Sts1CobcSw/Periphery/Rf.hpp +++ b/Sts1CobcSw/Periphery/Rf.hpp @@ -6,6 +6,12 @@ namespace sts1cobcsw::periphery::rf { -auto Initialize() -> void; +enum class TxType +{ + morse, // From GPIO pin + packet // From FIFO +}; + +auto Initialize(TxType txType) -> void; auto ReadPartInfo() -> std::uint16_t; } \ No newline at end of file diff --git a/Tests/HardwareTests/Rf.test.cpp b/Tests/HardwareTests/Rf.test.cpp index 3969d7fc..d6557a93 100644 --- a/Tests/HardwareTests/Rf.test.cpp +++ b/Tests/HardwareTests/Rf.test.cpp @@ -28,7 +28,7 @@ class RfTest : public RODOS::StaticThread<> { PRINTF("\nRF test\n\n"); - periphery::rf::Initialize(); + periphery::rf::Initialize(periphery::rf::TxType::morse); PRINTF("RF module initialized\n"); PRINTF("\n"); From cd20424cc18876faba2ec035fd08afbf29c236db Mon Sep 17 00:00:00 2001 From: Patrick Kappl Date: Sun, 4 Feb 2024 11:38:19 +0000 Subject: [PATCH 5/5] Make some cosmetic changes to RF code --- Sts1CobcSw/Periphery/Rf.cpp | 35 ++++++++++++++++++----------------- Sts1CobcSw/Periphery/Rf.hpp | 1 + 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/Sts1CobcSw/Periphery/Rf.cpp b/Sts1CobcSw/Periphery/Rf.cpp index e593b738..0d4e165c 100644 --- a/Sts1CobcSw/Periphery/Rf.cpp +++ b/Sts1CobcSw/Periphery/Rf.cpp @@ -18,18 +18,21 @@ using RODOS::MICROSECONDS; using RODOS::MILLISECONDS; using RODOS::NOW; + enum class PowerUpBootOptions : std::uint8_t { noPatch = 0x01, patch = 0x81 }; + enum class PowerUpXtalOptions : std::uint8_t { xtal = 0x00, // Reference signal is derived from the internal crystal oscillator txco = 0x01 // Reference signal is derived from an external TCXO }; + enum class PropertyGroup : std::uint8_t { global = 0x00, // @@ -48,19 +51,8 @@ enum class PropertyGroup : std::uint8_t pti = 0xF0 // Packet trace interface }; -// --- Private globals --- - -auto spi = RODOS::HAL_SPI( - hal::rfSpiIndex, hal::rfSpiSckPin, hal::rfSpiMisoPin, hal::rfSpiMosiPin, hal::spiNssDummyPin); -auto csGpioPin = hal::GpioPin(hal::rfCsPin); -auto nirqGpioPin = hal::GpioPin(hal::rfNirqPin); -auto sdnGpioPin = hal::GpioPin(hal::rfSdnPin); -auto gpio0GpioPin = hal::GpioPin(hal::rfGpio0Pin); -auto gpio1GpioPin = hal::GpioPin(hal::rfGpio1Pin); -auto paEnablePin = hal::GpioPin(hal::rfPaEnablePin); -// TODO: This should probably be somewhere else as it is not directly related to the RF module -auto watchdogResetGpioPin = hal::GpioPin(hal::watchdogClearPin); +// --- Private globals --- constexpr std::uint32_t powerUpXoFrequency = 26'000'000; // 26 MHz @@ -80,10 +72,22 @@ constexpr auto readyCtsByte = 0xFF_b; constexpr auto maxNProperties = 12; constexpr auto setPropertyHeaderSize = 4; +auto spi = RODOS::HAL_SPI( + hal::rfSpiIndex, hal::rfSpiSckPin, hal::rfSpiMisoPin, hal::rfSpiMosiPin, hal::spiNssDummyPin); +auto csGpioPin = hal::GpioPin(hal::rfCsPin); +auto nirqGpioPin = hal::GpioPin(hal::rfNirqPin); +auto sdnGpioPin = hal::GpioPin(hal::rfSdnPin); +auto gpio0GpioPin = hal::GpioPin(hal::rfGpio0Pin); +auto gpio1GpioPin = hal::GpioPin(hal::rfGpio1Pin); +auto paEnablePin = hal::GpioPin(hal::rfPaEnablePin); + +// TODO: This should probably be somewhere else as it is not directly related to the RF module +auto watchdogResetGpioPin = hal::GpioPin(hal::watchdogClearPin); + + // --- Private function declarations --- auto InitializeGpioAndSpi() -> void; - auto PowerUp(PowerUpBootOptions bootOptions, PowerUpXtalOptions xtalOptions, std::uint32_t xoFrequency) -> void; @@ -92,23 +96,20 @@ auto PowerUp(PowerUpBootOptions bootOptions, std::size_t length, std::uint8_t * responseData, std::size_t responseLength) -> void; - auto SendCommandNoResponse(std::span commandBuffer) -> void; - template auto SendCommandWithResponse(std::span commandBuffer) -> std::array; auto WaitOnCts() -> void; - auto SetTxType(TxType txType) -> void; - template requires(nProperties >= 1 and nProperties <= maxNProperties) auto SetProperty(PropertyGroup propertyGroup, Byte startProperty, std::span propertyValues) -> void; + // --- Public function definitions --- // TODO: Get rid of all the magic numbers diff --git a/Sts1CobcSw/Periphery/Rf.hpp b/Sts1CobcSw/Periphery/Rf.hpp index 569279c6..a874233f 100644 --- a/Sts1CobcSw/Periphery/Rf.hpp +++ b/Sts1CobcSw/Periphery/Rf.hpp @@ -12,6 +12,7 @@ enum class TxType packet // From FIFO }; + auto Initialize(TxType txType) -> void; auto ReadPartInfo() -> std::uint16_t; } \ No newline at end of file