Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add low-level RF functions Initialize() and ReadPartInfo() #250

Merged
merged 5 commits into from
Feb 4, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Sts1CobcSw/Periphery/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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()
195 changes: 195 additions & 0 deletions Sts1CobcSw/Periphery/Rf.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
#include <Sts1CobcSw/Hal/GpioPin.hpp>
#include <Sts1CobcSw/Hal/IoNames.hpp>
#include <Sts1CobcSw/Hal/Spi.hpp>
#include <Sts1CobcSw/Periphery/Rf.hpp>
#include <Sts1CobcSw/Periphery/RfNames.hpp>
#include <Sts1CobcSw/Serial/Byte.hpp>
#include <Sts1CobcSw/Utility/Span.hpp>

#include <rodos_no_using_namespace.h>

#include <array>
#include <climits>
#include <cstdint>
#include <span>
#include <string_view>


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<Byte const> commandBuffer) -> void;

template<std::size_t nResponseBytes>
auto SendCommandWithResponse(std::span<Byte const> commandBuffer)
-> std::array<Byte, nResponseBytes>;

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<Byte>({cmdPartInfo});
auto responseBuffer = SendCommandWithResponse<partInfoResponseLength>(sendBuffer);

// NOLINTNEXTLINE(hicpp-signed-bitwise)
return static_cast<std::uint16_t>(static_cast<std::uint16_t>(responseBuffer[1]) << CHAR_BIT
| static_cast<std::uint16_t>(responseBuffer[2]));
}


// --- Private function definitions ---

auto PowerUp(PowerUpBootOptions bootOptions,
PowerUpXtalOptions xtalOptions,
std::uint32_t xoFrequency) -> void
{
auto const powerUpBuffer = std::to_array<Byte>(
{cmdPowerUp,
static_cast<Byte>(bootOptions),
static_cast<Byte>(xtalOptions),
static_cast<Byte>(xoFrequency >> (CHAR_BIT * 3)), // NOLINT(hicpp-signed-bitwise)
static_cast<Byte>(xoFrequency >> (CHAR_BIT * 2)), // NOLINT(hicpp-signed-bitwise)
static_cast<Byte>(xoFrequency >> (CHAR_BIT)), // NOLINT(hicpp-signed-bitwise)
static_cast<Byte>(xoFrequency)});

SendCommandNoResponse(powerUpBuffer);
}


auto SendCommandNoResponse(std::span<Byte const> 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<std::size_t nResponseBytes>
auto SendCommandWithResponse(std::span<Byte const> commandBuffer)
-> std::array<Byte, nResponseBytes>
{
csGpioPin.Reset();
AT(NOW() + 20 * MICROSECONDS);
hal::WriteTo(&spi, commandBuffer);
AT(NOW() + 2 * MICROSECONDS);
csGpioPin.Set();

auto responseBuffer = std::array<Byte, nResponseBytes>{};
WaitOnCts();
// WaitOnCts leaves CS pin low, read response afterwards
hal::ReadFrom(&spi, std::span<Byte, nResponseBytes>(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<Byte>({cmdReadCmdBuff});
do
{
AT(NOW() + 20 * MICROSECONDS);
csGpioPin.Reset();
AT(NOW() + 20 * MICROSECONDS);

hal::WriteTo(&spi, std::span<Byte const, std::size(sendBuffer)>(sendBuffer));
auto ctsBuffer = std::array<Byte, 1>{};
hal::ReadFrom(&spi, std::span<Byte, std::size(ctsBuffer)>(ctsBuffer));

if(ctsBuffer[0] != readyCtsByte)
{
AT(NOW() + 2 * MICROSECONDS);
csGpioPin.Set();
}
else
{
break;
}
} while(true);
}
}
17 changes: 17 additions & 0 deletions Sts1CobcSw/Periphery/Rf.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#pragma once

#include <Sts1CobcSw/Periphery/RfNames.hpp>
#include <Sts1CobcSw/Serial/Byte.hpp>

#include <array>
#include <cstdint>
#include <span>
#include <string_view>


namespace sts1cobcsw::periphery::rf
{
auto Initialize() -> void;
auto InitializeGpioAndSpi() -> void;
danielschloms marked this conversation as resolved.
Show resolved Hide resolved
auto GetPartInfo() -> std::uint16_t;
danielschloms marked this conversation as resolved.
Show resolved Hide resolved
}
82 changes: 82 additions & 0 deletions Sts1CobcSw/Periphery/RfNames.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#pragma once
#include <Sts1CobcSw/Serial/Byte.hpp>

#include <cstdint>

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
};
}
3 changes: 3 additions & 0 deletions Tests/HardwareTests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
52 changes: 52 additions & 0 deletions Tests/HardwareTests/Rf.test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#include <Sts1CobcSw/Hal/IoNames.hpp>
#include <Sts1CobcSw/Periphery/Rf.hpp>

#include <Tests/HardwareTests/Utility.hpp>

#include <rodos_no_using_namespace.h>

#include <span>
#include <string_view>
danielschloms marked this conversation as resolved.
Show resolved Hide resolved


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;
}
Loading