Compare commits

...

6 Commits

Author SHA1 Message Date
Erki
00810009ec A lot of WIP state 2022-07-15 15:12:13 +03:00
Erki
e25b54add4 Update to use skullc for enums 2022-07-15 14:23:19 +03:00
Erki
c67c41f166 Fix and implement RF interrupt handling 2022-07-15 13:52:34 +03:00
Erki
671abd2f7f Rework state transition logic to be more complete 2022-07-14 18:01:52 +03:00
Erki
e86a5a1cbe Initial client application skeleton 2022-07-14 00:26:09 +03:00
Erki
8cddb07ee4 Configuration tooling added 2022-07-13 23:45:12 +03:00
12 changed files with 438 additions and 114 deletions

View File

@ -50,6 +50,7 @@ add_executable(skl_tunnel
radio/src/radio_hw_instance.cpp radio/src/radio_hw_instance.cpp
app/src/app_logging.cpp app/src/app_logging.cpp
app/src/app_transparent_client.cpp
main.cpp main.cpp
syscalls.c syscalls.c
) )

19
app/include/app_board.hpp Normal file
View File

@ -0,0 +1,19 @@
//
// Created by erki on 15.07.22.
//
#ifndef SKL_TUNNEL_APP_BOARD_HPP
#define SKL_TUNNEL_APP_BOARD_HPP
#include <cstdint>
namespace App::Board
{
void setup();
std::uint32_t systickGet();
}
#endif //SKL_TUNNEL_APP_BOARD_HPP

View File

@ -0,0 +1,25 @@
//
// Created by erki on 14.07.22.
//
#ifndef SKL_TUNNEL_APP_SETTINGS_HPP
#define SKL_TUNNEL_APP_SETTINGS_HPP
#include <cstdint>
namespace App
{
struct RadioSettings
{
std::uint16_t short_address = 0x0230;
std::uint64_t long_address = 0x1222334455667788;
std::uint8_t channel = 11;
std::uint16_t pan_id = 0x0023;
std::int16_t tx_power_dbm = 0;
std::uint8_t retries = 3;
};
}
#endif //SKL_TUNNEL_APP_SETTINGS_HPP

View File

@ -0,0 +1,45 @@
//
// Created by erki on 13.07.22.
//
#ifndef SKL_TUNNEL_APP_TRANSPARENT_CLIENT_HPP
#define SKL_TUNNEL_APP_TRANSPARENT_CLIENT_HPP
#include <optional>
#include <utility_function.hpp>
#include "app_settings.hpp"
#include "radio_interrupts.hpp"
namespace radio
{
class HwInstance;
}
namespace App
{
class TransparentClient
{
public:
TransparentClient(const RadioSettings& initial_settings);
TransparentClient(const TransparentClient&) = delete;
TransparentClient(TransparentClient&&) = delete;
TransparentClient& operator=(const TransparentClient&) = delete;
TransparentClient& operator=(TransparentClient&&) = delete;
void apply_settings(const RadioSettings& settings);
void process();
private:
radio::HwInstance* m_radio;
Utility::FunctionOwned<TransparentClient, void (radio::HwInstance*)> m_isr_cb_pointer;
std::optional<radio::Interrupts> m_pending_irqs = std::nullopt;
void m_cbRadioIrqHandler(radio::HwInstance*);
void m_processInterrupts();
};
}
#endif //SKL_TUNNEL_APP_TRANSPARENT_CLIENT_HPP

57
app/src/app_board.cpp Normal file
View File

@ -0,0 +1,57 @@
//
// Created by erki on 15.07.22.
//
#include "app_board.hpp"
#include "driver_init.h"
#include <utility_logging.hpp>
#include <utility_asynclogger.hpp>
#include <utility_staticpointer.hpp>
#include "skullc_samd21_hal.hpp"
namespace Hal = Peripherals::Hal::Samd;
using Logger = Utility::AsyncLogger<Hal::SerialInterfaceAsync<usart_async_descriptor>, Hal::StaticHal, 5, 255>;
namespace
{
Utility::StaticPointer<Logger> m_logger;
std::uint32_t m_systick_counter = 0;
void m_txCompleteCb(const usart_async_descriptor* const)
{
m_logger->txCompleteCallback();
}
}
extern "C" void SysTick_Handler()
{
m_systick_counter++;
}
namespace App::Board
{
void setup()
{
// Logging
Hal::SerialInterfaceAsync<usart_async_descriptor> usart0{&USART_0};
usart0.registerTxCallback(m_txCompleteCb);
m_logger.setup(usart0);
Utility::setLogger(*m_logger);
SysTick_Config(1000);
}
std::uint32_t systickGet()
{
return m_systick_counter;
}
}

View File

@ -0,0 +1,51 @@
//
// Created by erki on 14.07.22.
//
#include "radio_hw_instance.hpp"
#include "app_transparent_client.hpp"
namespace App
{
TransparentClient::TransparentClient(const RadioSettings& initial_settings)
: m_radio(radio::HwInstance::instance())
, m_isr_cb_pointer(*this, &TransparentClient::m_cbRadioIrqHandler)
{
apply_settings(initial_settings);
m_radio->set_current_state(radio::HwInstance::States::PLL_ON);
}
void TransparentClient::apply_settings(const RadioSettings& settings)
{
m_radio->set_address_short(settings.short_address);
m_radio->set_address_long(settings.long_address);
m_radio->set_channel(settings.channel);
m_radio->set_pan_id(settings.pan_id);
m_radio->set_tx_power(settings.tx_power_dbm);
m_radio->set_max_retries(settings.retries);
}
void TransparentClient::process()
{
if (m_pending_irqs)
m_processInterrupts();
}
void TransparentClient::m_cbRadioIrqHandler(radio::HwInstance*)
{
const radio::Interrupts new_irqs = m_radio->get_pending_irq();
if (m_pending_irqs)
m_pending_irqs = (*m_pending_irqs) | new_irqs;
else
m_pending_irqs = new_irqs;
}
void TransparentClient::m_processInterrupts()
{
m_pending_irqs = std::nullopt;
}
}

View File

@ -6,4 +6,6 @@
void atmel_start_init(void) void atmel_start_init(void)
{ {
system_init(); system_init();
SysTick_Config(1000);
} }

View File

@ -2,16 +2,21 @@
#include <utility_logging.hpp> #include <utility_logging.hpp>
#include <utility_assert.hpp> #include <utility_assert.hpp>
#include <utility_staticpointer.hpp>
#include <utility_function.hpp>
#include "radio_hw_instance.hpp" #include "radio_hw_instance.hpp"
#include "app_logging.hpp" #include "app_logging.hpp"
#include "skullc_samd21_hal.hpp" #include "skullc_samd21_hal.hpp"
#include "app_transparent_client.hpp"
namespace Hal = Peripherals::Hal::Samd; namespace Hal = Peripherals::Hal::Samd;
namespace namespace
{ {
Utility::StaticPointer<App::TransparentClient> m_app;
[[noreturn]] void m_faultHandler(const char* expression, const char* file, const int line) [[noreturn]] void m_faultHandler(const char* expression, const char* file, const int line)
{ {
SKULLC_LOG_FATAL("Expression failed: (%s), source: %s:%d", expression, file, line); SKULLC_LOG_FATAL("Expression failed: (%s), source: %s:%d", expression, file, line);
@ -34,44 +39,25 @@ int main()
App::Logging::setup(); App::Logging::setup();
radio::HwInstance radio_hw;
SKULLC_LOG_DEBUG("Begin."); SKULLC_LOG_DEBUG("Begin.");
const App::RadioSettings settings;
m_app.setup(settings);
radio::HwInstance* radio_hw = radio::HwInstance::instance();
/* Replace with your application code */ /* Replace with your application code */
while (true) while (true)
{ {
gpio_toggle_pin_level(OUT_LED_RX); gpio_toggle_pin_level(OUT_LED_RX);
const int16_t radio_num = radio_hw.register_read(radio::Registers::PART_NUM);
SKULLC_LOG_INFO("Reg 0x1C: %d", radio_num);
gpio_toggle_pin_level(OUT_LED_TX); gpio_toggle_pin_level(OUT_LED_TX);
int16_t radio_status = radio_hw.register_read(radio::Registers::TRX_STATUS); int16_t radio_status = radio_hw->register_read(radio::Registers::TRX_STATUS);
radio_status &= 0x1F; radio_status &= 0x1F;
SKULLC_LOG_INFO("Status: %d", radio_status); SKULLC_LOG_INFO("Status: %d", radio_status);
static int transitioned = 0;
if (radio_status == 0x08 && transitioned == 0)
{
SKULLC_LOG_DEBUG("Transitioning...");
radio_hw.set_current_state(radio::HwInstance::States::PLL_ON);
transitioned = 1;
}
else if (radio_status == 0x09 && transitioned == 1)
{
SKULLC_LOG_DEBUG("Transitioning 2...");
radio_hw.set_current_state(radio::HwInstance::States::RX_ON);
transitioned = 2;
}
else if (radio_status == 0x09 && transitioned == 1)
{
SKULLC_LOG_DEBUG("Transitioning 2...");
radio_hw.set_current_state(radio::HwInstance::States::RX_ON);
transitioned = 2;
}
delay_ms(1000); delay_ms(1000);
} }
} }

View File

@ -6,8 +6,15 @@
#define SKL_TUNNEL_RADIO_HW_INSTANCE_HPP #define SKL_TUNNEL_RADIO_HW_INSTANCE_HPP
#include <hal_spi_m_sync.h> #include <hal_spi_m_sync.h>
#include <type_traits>
#include <array>
#include <cstring>
#include <optional>
#include <utility_function.hpp>
#include "radio_hw_registers.hpp" #include "radio_hw_registers.hpp"
#include "radio_interrupts.hpp"
namespace radio namespace radio
{ {
@ -17,17 +24,18 @@ struct HwInstance
enum class States : std::uint8_t enum class States : std::uint8_t
{ {
P_ON = 0, P_ON = 0,
BUSY_RX, BUSY_RX = 0x1,
BUSY_TX, BUSY_TX = 0x2,
RX_ON, RX_ON = 0x06,
TRX_OFF, TRX_OFF = 0x08,
PLL_ON, PLL_ON = 0x09,
SLEEP, SLEEP = 0x0F,
PREP_DEEP_SLEEP, PREP_DEEP_SLEEP = 0x10,
BUSY_RX_AACK, BUSY_RX_AACK = 0x11,
BUSY_TX_ARET, BUSY_TX_ARET = 0x12,
RX_AACK_ON, RX_AACK_ON = 0x16,
TX_ARET_ON TX_ARET_ON = 0x19,
TRANSITION_IN_PROGRESS = 0x1F
}; };
static HwInstance* instance(); static HwInstance* instance();
@ -38,19 +46,45 @@ struct HwInstance
HwInstance& operator=(const HwInstance&) = delete; HwInstance& operator=(const HwInstance&) = delete;
HwInstance& operator=(HwInstance&&) = delete; HwInstance& operator=(HwInstance&&) = delete;
void irq_handler(); void set_irq_handler(Utility::IFunction<void (HwInstance*)>* cb);
Interrupts get_pending_irq();
uint8_t register_read(const Registers& address); uint8_t register_read(const Registers& address);
void register_write(const Registers& address, const uint8_t value); void register_write(const Registers& address, const uint8_t value);
template<typename T>
void register_write(const Registers& initial_address, const T value)
{
static_assert(std::is_integral_v<T>, "T must be an integral type.");
std::array<std::uint8_t, sizeof(T)> data_array;
std::memcpy(data_array.data(), &value, sizeof(T));
for (std::uint8_t i = 0; i < sizeof(T); i++)
{
const std::uint8_t address = std::uint8_t(initial_address) + i;
register_write(Registers(address), std::uint8_t(data_array[i]));
}
}
States current_state() const; States current_state() const;
bool set_current_state(const States& new_state); bool set_current_state(const States& new_state);
void set_address_short(const std::uint16_t address);
void set_address_long(const std::uint64_t address);
void set_channel(const std::uint8_t channel);
void set_pan_id(const std::uint16_t pan_id);
void set_tx_power(std::int16_t power);
void set_max_retries(std::uint8_t retries);
void set_csma_max_retries(const std::optional<std::uint8_t>& retries);
void set_csma_backoff_exponential(std::uint8_t max, std::uint8_t min);
void set_csma_seed(const std::uint16_t entropy);
private: private:
spi_m_sync_descriptor* m_spi = nullptr; spi_m_sync_descriptor* m_spi = nullptr;
io_descriptor* m_spi_io = nullptr; io_descriptor* m_spi_io = nullptr;
States m_current_state = States::P_ON; States m_current_state = States::P_ON;
void m_wait_transition_complete(); void m_wait_can_transition();
}; };
} }

View File

@ -0,0 +1,30 @@
//
// Created by erki on 15.07.22.
//
#ifndef SKL_TUNNEL_RADIO_INTERRUPTS_HPP
#define SKL_TUNNEL_RADIO_INTERRUPTS_HPP
#include <cstdint>
#include <utility_enum_helpers.hpp>
namespace radio
{
enum class Interrupts : std::uint8_t
{
PLL_LOCK = (1 << 0),
PLL_UNLOCK = (1 << 1),
RX_START = (1 << 2),
TRX_END = (1 << 3),
CCA_ED_DONE = (1 << 4),
AMI = (1 << 5),
TRX_UR = (1 << 6),
BAT_LOW = (1 << 7)
};
SKULLC_ENUM_DECLARE_BITFLAG_OPERATORS(Interrupts)
}
#endif //SKL_TUNNEL_RADIO_INTERRUPTS_HPP

View File

@ -23,6 +23,11 @@ void radio_gpio_init()
gpio_set_pin_direction(IN_RADIO_IRQ, GPIO_DIRECTION_IN); gpio_set_pin_direction(IN_RADIO_IRQ, GPIO_DIRECTION_IN);
gpio_set_pin_pull_mode(IN_RADIO_IRQ, GPIO_PULL_OFF); gpio_set_pin_pull_mode(IN_RADIO_IRQ, GPIO_PULL_OFF);
EIC->CONFIG[0].reg |= (1 << 0);
EIC->INTENSET.reg |= (1 << 0);
EIC->CTRL.reg |= (1 << 1);
gpio_set_pin_function(IN_RADIO_IRQ, PINMUX_PB00A_EXTINT0); gpio_set_pin_function(IN_RADIO_IRQ, PINMUX_PB00A_EXTINT0);
#undef PINMUX_PB00A_EXTINT0 #undef PINMUX_PB00A_EXTINT0

View File

@ -7,23 +7,27 @@
#include "radio_hardware_instance.h" #include "radio_hardware_instance.h"
#include "radio_gpio.h" #include "radio_gpio.h"
#include "radio_spi.h" #include "radio_spi.h"
#include "utility_logging.hpp"
#include <hal_delay.h> #include <hal_delay.h>
#include <hal_ext_irq.h> #include <hal_ext_irq.h>
#include <utility_assert.hpp> #include <utility_assert.hpp>
#include <utility_staticpointer.hpp> #include <utility_staticpointer.hpp>
#include <utility_logging.hpp>
#include <optional> #include <optional>
#include <algorithm>
namespace namespace
{ {
Utility::StaticPointer<radio::HwInstance> _INSTANCE; Utility::StaticPointer<radio::HwInstance> _INSTANCE;
Utility::IFunction<void (radio::HwInstance*)>* _CALLBACK;
void _irq_handler() void _irq_handler()
{ {
SKULLC_ASSERT_DEBUG(_INSTANCE.isInitialized()); SKULLC_ASSERT_DEBUG(_INSTANCE);
_INSTANCE->irq_handler();
if (_CALLBACK)
(*_CALLBACK)(_INSTANCE.get());
} }
} }
@ -53,9 +57,7 @@ HwInstance::HwInstance()
gpio_set_pin_level(OUT_RADIO_SLP_TR, false); gpio_set_pin_level(OUT_RADIO_SLP_TR, false);
delay_ms(10); delay_ms(10);
ext_irq_enable(IN_RADIO_IRQ);
ext_irq_register(IN_RADIO_IRQ, _irq_handler); ext_irq_register(IN_RADIO_IRQ, _irq_handler);
ext_irq_register(0, _irq_handler);
gpio_set_pin_level(OUT_RADIO_RST, true); gpio_set_pin_level(OUT_RADIO_RST, true);
delay_ms(10); delay_ms(10);
@ -70,7 +72,7 @@ HwInstance::HwInstance()
register_write(Registers::TRX_CTRL_0, trx_ctrl); register_write(Registers::TRX_CTRL_0, trx_ctrl);
// Enable interrupts. // Enable interrupts.
register_write(Registers::IRQ_MASK, 0x08); register_write(Registers::IRQ_MASK, 0xFF);
// clear interrupts. // clear interrupts.
register_read(Registers::IRQ_STATUS); register_read(Registers::IRQ_STATUS);
@ -78,9 +80,14 @@ HwInstance::HwInstance()
set_current_state(States::TRX_OFF); set_current_state(States::TRX_OFF);
} }
void HwInstance::irq_handler() void HwInstance::set_irq_handler(Utility::IFunction<void (HwInstance*)>* cb)
{ {
SKULLC_LOG_DEBUG("IRQ set."); _CALLBACK = cb;
}
Interrupts HwInstance::get_pending_irq()
{
return Interrupts(register_read(Registers::IRQ_STATUS));
} }
uint8_t HwInstance::register_read(const Registers& address) uint8_t HwInstance::register_read(const Registers& address)
@ -113,88 +120,150 @@ void HwInstance::register_write(const Registers& address, const uint8_t value)
gpio_set_pin_level(OUT_RADIO_CS, true); gpio_set_pin_level(OUT_RADIO_CS, true);
} }
HwInstance::States HwInstance::current_state() const
{
return m_current_state;
}
bool HwInstance::set_current_state(const States& new_state) bool HwInstance::set_current_state(const States& new_state)
{ {
if (new_state == m_current_state) if (new_state == m_current_state)
return true; return true;
m_wait_transition_complete(); m_wait_can_transition();
bool can_transition = false;
std::optional<std::uint8_t> trx_state_value = std::nullopt;
switch (m_current_state) auto is_rx_state = [](const States& s) -> bool
{
return s == States::RX_ON || s == States::RX_AACK_ON;
};
auto is_tx_state = [](const States& s) -> bool
{
return s == States::TX_ARET_ON;
};
if ((is_rx_state(m_current_state) && is_tx_state(new_state))
|| (is_tx_state(m_current_state) && is_rx_state(new_state)))
{ {
case States::P_ON: set_current_state(States::PLL_ON);
if (new_state == States::TRX_OFF)
{
can_transition = true;
trx_state_value = 0x08;
}
break;
case States::TRX_OFF:
if (new_state == States::RX_ON)
{
can_transition = true;
trx_state_value = 0x06;
}
else if (new_state == States::PLL_ON)
{
can_transition = true;
trx_state_value = 0x09;
}
break;
case States::RX_ON:
if (new_state == States::PLL_ON)
{
can_transition = true;
trx_state_value = 0x09;
}
else if (new_state == States::TRX_OFF)
{
can_transition = true;
trx_state_value = 0x08;
}
break;
case States::PLL_ON:
if (new_state == States::RX_ON)
{
can_transition = true;
trx_state_value = 0x06;
}
else if (new_state == States::TRX_OFF)
{
can_transition = true;
trx_state_value = 0x08;
}
break;
default:
SKULLC_ASSERT_DEBUG(false);
return false;
} }
if (can_transition) register_write(Registers::TRX_STATE, std::uint8_t(new_state));
{ m_wait_can_transition();
register_write(Registers::TRX_STATE, *trx_state_value); m_current_state = new_state;
m_wait_transition_complete(); return true;
m_current_state = new_state;
return true;
}
else
{
return false;
}
} }
void HwInstance::m_wait_transition_complete() void HwInstance::set_address_short(const std::uint16_t address)
{ {
while (true) register_write(Registers::SHORT_ADDR_0, address);
}
void HwInstance::set_address_long(const std::uint64_t address)
{
register_write(Registers::IEEE_ADDR_0, address);
}
void HwInstance::set_channel(const std::uint8_t channel)
{
static constexpr std::uint8_t channel_min = 11;
static constexpr std::uint8_t channel_max = 26;
SKULLC_ASSERT_DEBUG(channel >= channel_min && channel <= channel_max);
if (channel > channel_max || channel < channel_min)
{ {
int16_t radio_status = register_read(radio::Registers::TRX_STATUS); return;
radio_status &= 0x1F;
if (radio_status != 0x1F)
return;
} }
std::uint8_t reg_value = register_read(Registers::PHY_CC_CCA);
reg_value &= ~0x1F;
reg_value |= channel & 0x1F;
register_write(Registers::PHY_CC_CCA, reg_value);
}
void HwInstance::set_pan_id(const std::uint16_t pan_id)
{
register_write(Registers::PAN_ID_0, pan_id);
}
void HwInstance::set_tx_power(std::int16_t power)
{
static constexpr std::array<std::uint8_t, 21> dbm_to_tx_power{
0x0F, 0x0F, 0x0F, 0x0E, 0x0E, 0x0E, 0x0E, 0x0D, 0x0D, 0x0C, 0x0C,
0x0B, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x03, 0x00
};
power += 17;
power = std::clamp(power, std::int16_t(0), std::int16_t(21));
register_write(Registers::PHY_TX_PWR, dbm_to_tx_power[power]);
}
void HwInstance::set_max_retries(std::uint8_t retries)
{
retries = std::clamp<std::uint8_t>(retries, 0, 7);
constexpr std::uint8_t mask = 0xF0;
std::uint8_t reg_value = register_read(Registers::XAH_CTRL_0);
reg_value &= ~mask;
reg_value |= (retries << 4) & mask;
register_write(Registers::XAH_CTRL_0, reg_value);
}
void HwInstance::set_csma_max_retries(const std::optional<std::uint8_t>& retries)
{
std::uint8_t value_to_write = 0;
if (!retries)
value_to_write = 7;
else
value_to_write = std::clamp<std::uint8_t>(*retries, 0, 5);
constexpr std::uint8_t mask = 0x0E;
std::uint8_t reg_value = register_read(Registers::XAH_CTRL_0);
reg_value &= ~mask;
reg_value |= (value_to_write << 1) & mask;
register_write(Registers::XAH_CTRL_0, reg_value);
}
void HwInstance::set_csma_backoff_exponential(std::uint8_t max, std::uint8_t min)
{
max = std::clamp<std::uint8_t>(max, 0, 8);
min = std::clamp<std::uint8_t>(min, 0, max);
const std::uint8_t reg_value = (max << 4) | min;
register_write(Registers::CSMA_BE, reg_value);
}
void HwInstance::set_csma_seed(const std::uint16_t entropy)
{
std::array<std::uint8_t, 2> entropy_data{ 0 };
std::memcpy(entropy_data.data(), &entropy, 2);
register_write(Registers::CSMA_SEED_0, entropy_data[0]);
constexpr std::uint8_t mask = 0x07;
std::uint8_t reg_value = register_read(Registers::CSMA_SEED_1);
reg_value &= ~mask;
reg_value |= entropy_data[1] & mask;
register_write(Registers::CSMA_SEED_1, reg_value);
}
/**
* @brief Blocks until a state transition can be initiated.
*/
void HwInstance::m_wait_can_transition()
{
States radio_status = States::TRANSITION_IN_PROGRESS;
do
{
radio_status = States(register_read(Registers::TRX_STATUS) & 0x1F);
} while (radio_status == States::BUSY_RX || radio_status == States::BUSY_TX ||
radio_status == States::BUSY_RX_AACK || radio_status == States::BUSY_TX_ARET ||
radio_status == States::TRANSITION_IN_PROGRESS);
} }
} }