Compare commits
6 Commits
11baa1c185
...
00810009ec
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
00810009ec | ||
|
|
e25b54add4 | ||
|
|
c67c41f166 | ||
|
|
671abd2f7f | ||
|
|
e86a5a1cbe | ||
|
|
8cddb07ee4 |
@ -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
19
app/include/app_board.hpp
Normal 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
|
||||||
25
app/include/app_settings.hpp
Normal file
25
app/include/app_settings.hpp
Normal 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
|
||||||
45
app/include/app_transparent_client.hpp
Normal file
45
app/include/app_transparent_client.hpp
Normal 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
57
app/src/app_board.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
51
app/src/app_transparent_client.cpp
Normal file
51
app/src/app_transparent_client.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -6,4 +6,6 @@
|
|||||||
void atmel_start_init(void)
|
void atmel_start_init(void)
|
||||||
{
|
{
|
||||||
system_init();
|
system_init();
|
||||||
|
|
||||||
|
SysTick_Config(1000);
|
||||||
}
|
}
|
||||||
|
|||||||
36
main.cpp
36
main.cpp
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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();
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
30
radio/include/radio_interrupts.hpp
Normal file
30
radio/include/radio_interrupts.hpp
Normal 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
|
||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
{
|
{
|
||||||
case States::P_ON:
|
return s == States::RX_ON || s == States::RX_AACK_ON;
|
||||||
if (new_state == States::TRX_OFF)
|
};
|
||||||
|
|
||||||
|
auto is_tx_state = [](const States& s) -> bool
|
||||||
{
|
{
|
||||||
can_transition = true;
|
return s == States::TX_ARET_ON;
|
||||||
trx_state_value = 0x08;
|
};
|
||||||
}
|
|
||||||
break;
|
if ((is_rx_state(m_current_state) && is_tx_state(new_state))
|
||||||
case States::TRX_OFF:
|
|| (is_tx_state(m_current_state) && is_rx_state(new_state)))
|
||||||
if (new_state == States::RX_ON)
|
|
||||||
{
|
{
|
||||||
can_transition = true;
|
set_current_state(States::PLL_ON);
|
||||||
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_wait_transition_complete();
|
|
||||||
m_current_state = new_state;
|
m_current_state = new_state;
|
||||||
return true;
|
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);
|
||||||
{
|
}
|
||||||
int16_t radio_status = register_read(radio::Registers::TRX_STATUS);
|
|
||||||
radio_status &= 0x1F;
|
|
||||||
|
|
||||||
if (radio_status != 0x1F)
|
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)
|
||||||
|
{
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user