Compare commits

..

No commits in common. "00810009ec17fc625f9534b97248e77a662e220b" and "11baa1c185655692ea3fb7562d1e2fbd66ea1c10" have entirely different histories.

12 changed files with 114 additions and 438 deletions

View File

@ -50,7 +50,6 @@ 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
) )

View File

@ -1,19 +0,0 @@
//
// 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

@ -1,25 +0,0 @@
//
// 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

@ -1,45 +0,0 @@
//
// 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

View File

@ -1,57 +0,0 @@
//
// 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

@ -1,51 +0,0 @@
//
// 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,6 +6,4 @@
void atmel_start_init(void) void atmel_start_init(void)
{ {
system_init(); system_init();
SysTick_Config(1000);
} }

View File

@ -2,21 +2,16 @@
#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);
@ -39,25 +34,44 @@ 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,15 +6,8 @@
#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
{ {
@ -24,18 +17,17 @@ struct HwInstance
enum class States : std::uint8_t enum class States : std::uint8_t
{ {
P_ON = 0, P_ON = 0,
BUSY_RX = 0x1, BUSY_RX,
BUSY_TX = 0x2, BUSY_TX,
RX_ON = 0x06, RX_ON,
TRX_OFF = 0x08, TRX_OFF,
PLL_ON = 0x09, PLL_ON,
SLEEP = 0x0F, SLEEP,
PREP_DEEP_SLEEP = 0x10, PREP_DEEP_SLEEP,
BUSY_RX_AACK = 0x11, BUSY_RX_AACK,
BUSY_TX_ARET = 0x12, BUSY_TX_ARET,
RX_AACK_ON = 0x16, RX_AACK_ON,
TX_ARET_ON = 0x19, TX_ARET_ON
TRANSITION_IN_PROGRESS = 0x1F
}; };
static HwInstance* instance(); static HwInstance* instance();
@ -46,45 +38,19 @@ struct HwInstance
HwInstance& operator=(const HwInstance&) = delete; HwInstance& operator=(const HwInstance&) = delete;
HwInstance& operator=(HwInstance&&) = delete; HwInstance& operator=(HwInstance&&) = delete;
void set_irq_handler(Utility::IFunction<void (HwInstance*)>* cb); void irq_handler();
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_can_transition(); void m_wait_transition_complete();
}; };
} }

View File

@ -1,30 +0,0 @@
//
// 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,11 +23,6 @@ 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,27 +7,23 @@
#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); SKULLC_ASSERT_DEBUG(_INSTANCE.isInitialized());
_INSTANCE->irq_handler();
if (_CALLBACK)
(*_CALLBACK)(_INSTANCE.get());
} }
} }
@ -57,7 +53,9 @@ 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);
@ -72,7 +70,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, 0xFF); register_write(Registers::IRQ_MASK, 0x08);
// clear interrupts. // clear interrupts.
register_read(Registers::IRQ_STATUS); register_read(Registers::IRQ_STATUS);
@ -80,14 +78,9 @@ HwInstance::HwInstance()
set_current_state(States::TRX_OFF); set_current_state(States::TRX_OFF);
} }
void HwInstance::set_irq_handler(Utility::IFunction<void (HwInstance*)>* cb) void HwInstance::irq_handler()
{ {
_CALLBACK = cb; SKULLC_LOG_DEBUG("IRQ set.");
}
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)
@ -120,150 +113,88 @@ 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_can_transition(); m_wait_transition_complete();
bool can_transition = false;
std::optional<std::uint8_t> trx_state_value = std::nullopt;
auto is_rx_state = [](const States& s) -> bool switch (m_current_state)
{ {
return s == States::RX_ON || s == States::RX_AACK_ON; case States::P_ON:
}; if (new_state == States::TRX_OFF)
auto is_tx_state = [](const States& s) -> bool
{ {
return s == States::TX_ARET_ON; can_transition = true;
}; trx_state_value = 0x08;
}
if ((is_rx_state(m_current_state) && is_tx_state(new_state)) break;
|| (is_tx_state(m_current_state) && is_rx_state(new_state))) case States::TRX_OFF:
if (new_state == States::RX_ON)
{ {
set_current_state(States::PLL_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;
} }
register_write(Registers::TRX_STATE, std::uint8_t(new_state)); if (can_transition)
m_wait_can_transition(); {
register_write(Registers::TRX_STATE, *trx_state_value);
m_wait_transition_complete();
m_current_state = new_state; m_current_state = new_state;
return true; return true;
} }
else
void HwInstance::set_address_short(const std::uint16_t address)
{ {
register_write(Registers::SHORT_ADDR_0, address); return false;
}
} }
void HwInstance::set_address_long(const std::uint64_t address) void HwInstance::m_wait_transition_complete()
{ {
register_write(Registers::IEEE_ADDR_0, address); while (true)
} {
int16_t radio_status = register_read(radio::Registers::TRX_STATUS);
radio_status &= 0x1F;
void HwInstance::set_channel(const std::uint8_t channel) if (radio_status != 0x1F)
{
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)
{
return; 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);
} }
} }