Compare commits
17 Commits
feature/sk
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e5ecdf1b9 | ||
|
|
6a9697124e | ||
|
|
c994e63478 | ||
|
|
1c695fbaa8 | ||
|
|
00810009ec | ||
|
|
e25b54add4 | ||
|
|
c67c41f166 | ||
|
|
671abd2f7f | ||
|
|
e86a5a1cbe | ||
|
|
8cddb07ee4 | ||
|
|
11baa1c185 | ||
|
|
c27f7a9450 | ||
|
|
bbe259c29a | ||
|
|
1bbe29b5d9 | ||
|
|
bb1a446f77 | ||
|
|
94368b458a | ||
|
|
798d529b20 |
@ -48,9 +48,13 @@ add_executable(skl_tunnel
|
|||||||
radio/src/radio_spi.c
|
radio/src/radio_spi.c
|
||||||
radio/src/radio_gpio.c
|
radio/src/radio_gpio.c
|
||||||
radio/src/radio_hw_instance.cpp
|
radio/src/radio_hw_instance.cpp
|
||||||
|
radio/src/radio_protocol_frame.cpp
|
||||||
|
radio/src/radio_protocol.cpp
|
||||||
|
|
||||||
app/src/app_logging.cpp
|
app/src/app_board.cpp
|
||||||
|
app/src/app_transparent_client.cpp
|
||||||
main.cpp
|
main.cpp
|
||||||
|
syscalls.c
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(skl_tunnel
|
target_include_directories(skl_tunnel
|
||||||
|
|||||||
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
|
||||||
@ -1,15 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by erki on 28.06.22.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef SKL_TUNNEL_APP_LOGGING_HPP
|
|
||||||
#define SKL_TUNNEL_APP_LOGGING_HPP
|
|
||||||
|
|
||||||
namespace App::Logging
|
|
||||||
{
|
|
||||||
|
|
||||||
void setup();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif //SKL_TUNNEL_APP_LOGGING_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
|
||||||
60
app/include/app_transparent_client.hpp
Normal file
60
app/include/app_transparent_client.hpp
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
//
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
enum class AppState
|
||||||
|
{
|
||||||
|
STARTUP,
|
||||||
|
PASSIVE,
|
||||||
|
ACTIVE_RX,
|
||||||
|
ACTIVE_TX,
|
||||||
|
RX_FRAME_READY
|
||||||
|
};
|
||||||
|
|
||||||
|
AppState m_state = AppState::STARTUP;
|
||||||
|
|
||||||
|
void m_cbRadioIrqHandler(radio::HwInstance*);
|
||||||
|
std::optional<AppState> m_hwInterruptToNewState();
|
||||||
|
void m_transitionToState(const AppState& new_state);
|
||||||
|
void m_processState();
|
||||||
|
void m_initiateTx();
|
||||||
|
bool m_txBufferIsReady();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //SKL_TUNNEL_APP_TRANSPARENT_CLIENT_HPP
|
||||||
@ -5,10 +5,6 @@
|
|||||||
#ifndef SKL_TUNNEL_SKULLC_SAMD21_HAL_HPP
|
#ifndef SKL_TUNNEL_SKULLC_SAMD21_HAL_HPP
|
||||||
#define SKL_TUNNEL_SKULLC_SAMD21_HAL_HPP
|
#define SKL_TUNNEL_SKULLC_SAMD21_HAL_HPP
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
#include <hal_delay.h>
|
#include <hal_delay.h>
|
||||||
#include <hal_io.h>
|
#include <hal_io.h>
|
||||||
#include <hal_usart_async.h>
|
#include <hal_usart_async.h>
|
||||||
@ -16,6 +12,12 @@
|
|||||||
#include <utility_function.hpp>
|
#include <utility_function.hpp>
|
||||||
#include <utility_tag.hpp>
|
#include <utility_tag.hpp>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
#include "app_board.hpp"
|
||||||
|
|
||||||
namespace Peripherals
|
namespace Peripherals
|
||||||
{
|
{
|
||||||
namespace Hal
|
namespace Hal
|
||||||
@ -26,7 +28,14 @@ namespace Samd
|
|||||||
struct StaticHal
|
struct StaticHal
|
||||||
{
|
{
|
||||||
static void initialize()
|
static void initialize()
|
||||||
{ }
|
{
|
||||||
|
App::Board::setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::uint32_t getMillis()
|
||||||
|
{
|
||||||
|
return App::Board::systickGet();
|
||||||
|
}
|
||||||
|
|
||||||
static void delay(const std::uint32_t milliseconds)
|
static void delay(const std::uint32_t milliseconds)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,16 +1,17 @@
|
|||||||
//
|
//
|
||||||
// Created by erki on 28.06.22.
|
// Created by erki on 15.07.22.
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "app_logging.hpp"
|
|
||||||
|
|
||||||
#include "driver_init.h"
|
#include "driver_init.h"
|
||||||
|
|
||||||
|
#include "app_board.hpp"
|
||||||
|
|
||||||
|
#include "skullc_samd21_hal.hpp"
|
||||||
|
|
||||||
#include <utility_logging.hpp>
|
#include <utility_logging.hpp>
|
||||||
#include <utility_asynclogger.hpp>
|
#include <utility_asynclogger.hpp>
|
||||||
#include <utility_staticpointer.hpp>
|
#include <utility_staticpointer.hpp>
|
||||||
|
|
||||||
#include "skullc_samd21_hal.hpp"
|
|
||||||
|
|
||||||
namespace Hal = Peripherals::Hal::Samd;
|
namespace Hal = Peripherals::Hal::Samd;
|
||||||
|
|
||||||
@ -20,6 +21,7 @@ namespace
|
|||||||
{
|
{
|
||||||
|
|
||||||
Utility::StaticPointer<Logger> m_logger;
|
Utility::StaticPointer<Logger> m_logger;
|
||||||
|
std::uint32_t m_systick_counter = 0;
|
||||||
|
|
||||||
void m_txCompleteCb(const usart_async_descriptor* const)
|
void m_txCompleteCb(const usart_async_descriptor* const)
|
||||||
{
|
{
|
||||||
@ -28,16 +30,29 @@ void m_txCompleteCb(const usart_async_descriptor* const)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace App::Logging
|
extern "C" void SysTick_Handler()
|
||||||
|
{
|
||||||
|
m_systick_counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace App::Board
|
||||||
{
|
{
|
||||||
|
|
||||||
void setup()
|
void setup()
|
||||||
{
|
{
|
||||||
|
// Logging
|
||||||
Hal::SerialInterfaceAsync<usart_async_descriptor> usart0{&USART_0};
|
Hal::SerialInterfaceAsync<usart_async_descriptor> usart0{&USART_0};
|
||||||
usart0.registerTxCallback(m_txCompleteCb);
|
usart0.registerTxCallback(m_txCompleteCb);
|
||||||
|
|
||||||
m_logger.setup(usart0);
|
m_logger.setup(usart0);
|
||||||
Utility::setLogger(*m_logger);
|
Utility::setLogger(*m_logger);
|
||||||
|
|
||||||
|
SysTick_Config(F_CPU / 1000u);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint32_t systickGet()
|
||||||
|
{
|
||||||
|
return m_systick_counter;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
145
app/src/app_transparent_client.cpp
Normal file
145
app/src/app_transparent_client.cpp
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
//
|
||||||
|
// Created by erki on 14.07.22.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "radio_hw_instance.hpp"
|
||||||
|
|
||||||
|
#include "app_transparent_client.hpp"
|
||||||
|
#include "skullc_samd21_hal.hpp"
|
||||||
|
#include "utility_logging.hpp"
|
||||||
|
|
||||||
|
#include <utility_atomicscopeguard.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_irq_handler(&m_isr_cb_pointer);
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
std::optional<AppState> new_state_request = std::nullopt;
|
||||||
|
|
||||||
|
{
|
||||||
|
Utility::AtomicScopeGuard<Peripherals::Hal::Samd::StaticHal> irq_guard;
|
||||||
|
if (m_pending_irqs)
|
||||||
|
new_state_request = m_hwInterruptToNewState();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!new_state_request && m_txBufferIsReady())
|
||||||
|
new_state_request = AppState::ACTIVE_TX;
|
||||||
|
|
||||||
|
if (new_state_request)
|
||||||
|
m_transitionToState(*new_state_request);
|
||||||
|
|
||||||
|
m_processState();
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<TransparentClient::AppState> TransparentClient::m_hwInterruptToNewState()
|
||||||
|
{
|
||||||
|
const radio::Interrupts current_irqs = *m_pending_irqs;
|
||||||
|
m_pending_irqs = std::nullopt;
|
||||||
|
|
||||||
|
SKULLC_LOG_DEBUG("APP: Processing IRQs: %d.", current_irqs);
|
||||||
|
|
||||||
|
const auto flag_is_set = [current_irqs](const radio::Interrupts& flag) -> bool
|
||||||
|
{
|
||||||
|
return std::underlying_type_t<radio::Interrupts>(current_irqs & flag);
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (m_state)
|
||||||
|
{
|
||||||
|
case AppState::STARTUP:
|
||||||
|
if (flag_is_set(radio::Interrupts::PLL_LOCK))
|
||||||
|
return AppState::PASSIVE;
|
||||||
|
break;
|
||||||
|
case AppState::PASSIVE:
|
||||||
|
if (flag_is_set(radio::Interrupts::RX_START))
|
||||||
|
return AppState::ACTIVE_RX;
|
||||||
|
else if (flag_is_set(radio::Interrupts::AMI))
|
||||||
|
return AppState::RX_FRAME_READY;
|
||||||
|
break;
|
||||||
|
case AppState::ACTIVE_RX:
|
||||||
|
[[fallthrough]];
|
||||||
|
case AppState::ACTIVE_TX:
|
||||||
|
if (flag_is_set(radio::Interrupts::TRX_END))
|
||||||
|
return AppState::PASSIVE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TransparentClient::m_transitionToState(const AppState& new_state)
|
||||||
|
{
|
||||||
|
SKULLC_LOG_DEBUG("APP: Trans. to state. %d -> %d.", m_state, new_state);
|
||||||
|
|
||||||
|
switch (m_state)
|
||||||
|
{
|
||||||
|
case AppState::STARTUP:
|
||||||
|
if (new_state == AppState::PASSIVE)
|
||||||
|
m_radio->set_current_state(radio::HwInstance::States::RX_ON);
|
||||||
|
break;
|
||||||
|
case AppState::PASSIVE:
|
||||||
|
// new_state == AppState::ACTIVE_RX is a HW transition. State machine simply locks itself.
|
||||||
|
// new_state == AppState::RX_FRAME_READY is handled in process state.
|
||||||
|
if (new_state == AppState::ACTIVE_TX)
|
||||||
|
m_initiateTx();
|
||||||
|
break;
|
||||||
|
case AppState::ACTIVE_RX:
|
||||||
|
// new_state == AppState::PASSIVE is a HW transition. State machine simply unlocks itself.
|
||||||
|
break;
|
||||||
|
case AppState::ACTIVE_TX:
|
||||||
|
if (new_state == AppState::PASSIVE)
|
||||||
|
m_radio->set_current_state(radio::HwInstance::States::RX_ON);
|
||||||
|
break;
|
||||||
|
case AppState::RX_FRAME_READY:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_state = new_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TransparentClient::m_processState()
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TransparentClient::m_initiateTx()
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TransparentClient::m_txBufferIsReady()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -34,9 +34,9 @@ set(CMAKE_CXX_COMPILER arm-none-eabi-g++)
|
|||||||
set(CMAKE_ASM_COMPILER arm-none-eabi-as)
|
set(CMAKE_ASM_COMPILER arm-none-eabi-as)
|
||||||
set(CMAKE_RANLIB arm-none-eabi-ranlib)
|
set(CMAKE_RANLIB arm-none-eabi-ranlib)
|
||||||
|
|
||||||
set(COMMON_C_FLAGS "-mcpu=cortex-m0plus -mfloat-abi=soft -mthumb -mlong-calls -fdata-sections -ffunction-sections -Wall -Wextra -O2")
|
set(COMMON_C_FLAGS "-mcpu=cortex-m0plus -mfloat-abi=soft -mthumb -mlong-calls -fdata-sections -ffunction-sections -Wall -Wextra -Wswitch-enum -Og")
|
||||||
|
|
||||||
set(CMAKE_C_FLAGS_INIT "${COMMON_C_FLAGS}")
|
set(CMAKE_C_FLAGS_INIT "${COMMON_C_FLAGS}")
|
||||||
set(CMAKE_CXX_FLAGS_INIT "${COMMON_C_FLAGS} -fno-exceptions -fno-rtti")
|
set(CMAKE_CXX_FLAGS_INIT "${COMMON_C_FLAGS} -fno-exceptions -fno-rtti -fno-use-cxa-atexit")
|
||||||
set(CMAKE_ASM_FLAGS_INIT "${COMMON_C_FLAGS}")
|
set(CMAKE_ASM_FLAGS_INIT "${COMMON_C_FLAGS}")
|
||||||
set(CMAKE_EXE_LINKER_FLAGS_INIT "${COMMON_C_FLAGS} -specs=nosys.specs")
|
set(CMAKE_EXE_LINKER_FLAGS_INIT "${COMMON_C_FLAGS} -specs=nano.specs -specs=nosys.specs")
|
||||||
|
|||||||
@ -6,4 +6,6 @@
|
|||||||
void atmel_start_init(void)
|
void atmel_start_init(void)
|
||||||
{
|
{
|
||||||
system_init();
|
system_init();
|
||||||
|
|
||||||
|
SysTick_Config(1000);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -108,7 +108,14 @@ drivers:
|
|||||||
eic_arch_wakeupen7: false
|
eic_arch_wakeupen7: false
|
||||||
eic_arch_wakeupen8: false
|
eic_arch_wakeupen8: false
|
||||||
eic_arch_wakeupen9: false
|
eic_arch_wakeupen9: false
|
||||||
optional_signals: []
|
optional_signals:
|
||||||
|
- identifier: EXTERNAL_IRQ_0:EXTINT/0
|
||||||
|
pad: PA00
|
||||||
|
mode: Enabled
|
||||||
|
configuration: null
|
||||||
|
definition: Atmel:SAMD21_Drivers:0.0.1::SAMD21E17A-MF::optional_signal_definition::EIC.EXTINT.0
|
||||||
|
name: EIC/EXTINT/0
|
||||||
|
label: EXTINT/0
|
||||||
variant: null
|
variant: null
|
||||||
clocks:
|
clocks:
|
||||||
domain_group:
|
domain_group:
|
||||||
@ -709,6 +716,12 @@ drivers:
|
|||||||
clocks:
|
clocks:
|
||||||
domain_group: null
|
domain_group: null
|
||||||
pads:
|
pads:
|
||||||
|
PA00:
|
||||||
|
name: PA00
|
||||||
|
definition: Atmel:SAMD21_Drivers:0.0.1::SAMD21E17A-MF::pad::PA00
|
||||||
|
mode: Digital input
|
||||||
|
user_label: PA00
|
||||||
|
configuration: null
|
||||||
OUT_LED_TX:
|
OUT_LED_TX:
|
||||||
name: PA06
|
name: PA06
|
||||||
definition: Atmel:SAMD21_Drivers:0.0.1::SAMD21E17A-MF::pad::PA06
|
definition: Atmel:SAMD21_Drivers:0.0.1::SAMD21E17A-MF::pad::PA06
|
||||||
|
|||||||
@ -21,6 +21,7 @@
|
|||||||
#define GPIO_PIN_FUNCTION_G 6
|
#define GPIO_PIN_FUNCTION_G 6
|
||||||
#define GPIO_PIN_FUNCTION_H 7
|
#define GPIO_PIN_FUNCTION_H 7
|
||||||
|
|
||||||
|
#define PA00 GPIO(GPIO_PORTA, 0)
|
||||||
#define OUT_LED_TX GPIO(GPIO_PORTA, 6)
|
#define OUT_LED_TX GPIO(GPIO_PORTA, 6)
|
||||||
#define OUT_XBEE_REMOTE_RESET GPIO(GPIO_PORTA, 7)
|
#define OUT_XBEE_REMOTE_RESET GPIO(GPIO_PORTA, 7)
|
||||||
#define IN_UART_TX GPIO(GPIO_PORTA, 8)
|
#define IN_UART_TX GPIO(GPIO_PORTA, 8)
|
||||||
|
|||||||
@ -682,6 +682,10 @@
|
|||||||
#endif
|
#endif
|
||||||
// </e>
|
// </e>
|
||||||
|
|
||||||
|
#ifndef CONFIG_EIC_EXTINT_MAP
|
||||||
|
#define CONFIG_EIC_EXTINT_MAP {0, PIN_PA00}, {0, 32},
|
||||||
|
#endif
|
||||||
|
|
||||||
// <<< end of configuration section >>>
|
// <<< end of configuration section >>>
|
||||||
|
|
||||||
#endif // HPL_EIC_CONFIG_H
|
#endif // HPL_EIC_CONFIG_H
|
||||||
|
|||||||
@ -25,6 +25,19 @@ void EXTERNAL_IRQ_0_init(void)
|
|||||||
{
|
{
|
||||||
_gclk_enable_channel(EIC_GCLK_ID, CONF_GCLK_EIC_SRC);
|
_gclk_enable_channel(EIC_GCLK_ID, CONF_GCLK_EIC_SRC);
|
||||||
|
|
||||||
|
// Set pin direction to input
|
||||||
|
gpio_set_pin_direction(PA00, GPIO_DIRECTION_IN);
|
||||||
|
|
||||||
|
gpio_set_pin_pull_mode(PA00,
|
||||||
|
// <y> Pull configuration
|
||||||
|
// <id> pad_pull_config
|
||||||
|
// <GPIO_PULL_OFF"> Off
|
||||||
|
// <GPIO_PULL_UP"> Pull-up
|
||||||
|
// <GPIO_PULL_DOWN"> Pull-down
|
||||||
|
GPIO_PULL_OFF);
|
||||||
|
|
||||||
|
gpio_set_pin_function(PA00, PINMUX_PA00A_EIC_EXTINT0);
|
||||||
|
|
||||||
ext_irq_init();
|
ext_irq_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -10,11 +10,17 @@
|
|||||||
#include "driver_init.h"
|
#include "driver_init.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
|
static void button_on_PA00_pressed(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Example of using EXTERNAL_IRQ_0
|
* Example of using EXTERNAL_IRQ_0
|
||||||
*/
|
*/
|
||||||
void EXTERNAL_IRQ_0_example(void)
|
void EXTERNAL_IRQ_0_example(void)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
ext_irq_register(PIN_PA00, button_on_PA00_pressed);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -33,7 +33,7 @@
|
|||||||
|
|
||||||
#include "hal_ext_irq.h"
|
#include "hal_ext_irq.h"
|
||||||
|
|
||||||
#define EXT_IRQ_AMOUNT 0
|
#define EXT_IRQ_AMOUNT 2
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Driver version
|
* \brief Driver version
|
||||||
|
|||||||
@ -69,7 +69,7 @@ static int ffs(int v)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define EXT_IRQ_AMOUNT 0
|
#define EXT_IRQ_AMOUNT 2
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief EXTINTx and pin number map
|
* \brief EXTINTx and pin number map
|
||||||
|
|||||||
46
main.cpp
46
main.cpp
@ -1,40 +1,60 @@
|
|||||||
#include <atmel_start.h>
|
#include <atmel_start.h>
|
||||||
|
|
||||||
#include <utility_logging.hpp>
|
#include <utility_logging.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 "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)
|
||||||
|
{
|
||||||
|
SKULLC_LOG_FATAL("Expression failed: (%s), source: %s:%d", expression, file, line);
|
||||||
|
|
||||||
|
__asm__("BKPT");
|
||||||
|
|
||||||
|
while (true);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(void)
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
{
|
{
|
||||||
/* Initializes MCU, drivers and middleware */
|
/* Initializes MCU, drivers and middleware */
|
||||||
atmel_start_init();
|
atmel_start_init();
|
||||||
|
|
||||||
|
Utility::Assert::setHandler(m_faultHandler);
|
||||||
|
Hal::StaticHal::initialize();
|
||||||
|
|
||||||
gpio_set_pin_level(OUT_LED_TX, false);
|
gpio_set_pin_level(OUT_LED_TX, false);
|
||||||
|
|
||||||
App::Logging::setup();
|
SKULLC_LOG_DEBUG("Begin.");
|
||||||
|
|
||||||
radio::HwInstance* radio_hw = radio::HwInstance::create_instance();
|
const App::RadioSettings settings;
|
||||||
|
|
||||||
|
m_app.setup(settings);
|
||||||
|
|
||||||
|
std::uint32_t counter = 0;
|
||||||
|
|
||||||
/* Replace with your application code */
|
/* Replace with your application code */
|
||||||
while (1)
|
while (true)
|
||||||
{
|
{
|
||||||
gpio_toggle_pin_level(OUT_LED_RX);
|
if (counter++ == 1000)
|
||||||
|
{
|
||||||
|
gpio_toggle_pin_level(OUT_LED_TX);
|
||||||
|
counter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
const int16_t radio_num = radio_hw->register_read(0x1C);
|
m_app->process();
|
||||||
|
Hal::StaticHal::delay(1);
|
||||||
SKULLC_LOG_INFO("Reg 0x1C: %d", radio_num);
|
|
||||||
|
|
||||||
gpio_toggle_pin_level(OUT_LED_TX);
|
|
||||||
|
|
||||||
delay_ms(1000);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,23 +6,85 @@
|
|||||||
#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_interrupts.hpp"
|
||||||
|
|
||||||
namespace radio
|
namespace radio
|
||||||
{
|
{
|
||||||
|
|
||||||
struct HwInstance
|
struct HwInstance
|
||||||
{
|
{
|
||||||
static HwInstance* create_instance();
|
enum class States : std::uint8_t
|
||||||
|
{
|
||||||
|
P_ON = 0,
|
||||||
|
BUSY_RX = 0x1,
|
||||||
|
BUSY_TX = 0x2,
|
||||||
|
RX_ON = 0x06,
|
||||||
|
TRX_OFF = 0x08,
|
||||||
|
PLL_ON = 0x09,
|
||||||
|
SLEEP = 0x0F,
|
||||||
|
PREP_DEEP_SLEEP = 0x10,
|
||||||
|
BUSY_RX_AACK = 0x11,
|
||||||
|
BUSY_TX_ARET = 0x12,
|
||||||
|
RX_AACK_ON = 0x16,
|
||||||
|
TX_ARET_ON = 0x19,
|
||||||
|
TRANSITION_IN_PROGRESS = 0x1F
|
||||||
|
};
|
||||||
|
|
||||||
void irq_handler();
|
static HwInstance* instance();
|
||||||
|
|
||||||
|
HwInstance();
|
||||||
|
HwInstance(const HwInstance&) = delete;
|
||||||
|
HwInstance(HwInstance&&) = delete;
|
||||||
|
HwInstance& operator=(const HwInstance&) = delete;
|
||||||
|
HwInstance& operator=(HwInstance&&) = delete;
|
||||||
|
|
||||||
|
void set_irq_handler(Utility::IFunction<void (HwInstance*)>* cb);
|
||||||
|
Interrupts get_pending_irq();
|
||||||
|
|
||||||
|
uint8_t register_read(const Registers& address);
|
||||||
|
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;
|
||||||
|
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);
|
||||||
|
|
||||||
uint8_t register_read(uint8_t address);
|
|
||||||
void register_write(uint8_t address, const uint8_t value);
|
|
||||||
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;
|
||||||
|
|
||||||
HwInstance();
|
void m_wait_can_transition();
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
64
radio/include/radio_hw_registers.hpp
Normal file
64
radio/include/radio_hw_registers.hpp
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
//
|
||||||
|
// Created by erki on 30.06.22.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef SKL_TUNNEL_RADIO_HW_REGISTERS_HPP
|
||||||
|
#define SKL_TUNNEL_RADIO_HW_REGISTERS_HPP
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace radio
|
||||||
|
{
|
||||||
|
|
||||||
|
enum class Registers : std::uint8_t
|
||||||
|
{
|
||||||
|
TRX_STATUS = (0x01),
|
||||||
|
TRX_STATE = (0x02),
|
||||||
|
TRX_CTRL_0 = (0x03),
|
||||||
|
TRX_CTRL_1 = (0x04),
|
||||||
|
PHY_TX_PWR = (0x05),
|
||||||
|
PHY_RSSI = (0x06),
|
||||||
|
PHY_ED_LEVEL = (0x07),
|
||||||
|
PHY_CC_CCA = (0x08),
|
||||||
|
CCA_THRES = (0x09),
|
||||||
|
RX_CTRL = (0x0A),
|
||||||
|
SFD_VALUE = (0x0B),
|
||||||
|
TRX_CTRL_2 = (0x0C),
|
||||||
|
ANT_DIV = (0x0D),
|
||||||
|
IRQ_MASK = (0x0E),
|
||||||
|
IRQ_STATUS = (0x0F),
|
||||||
|
VREG_CTRL = (0x10),
|
||||||
|
BATMON = (0x11),
|
||||||
|
XOSC_CTRL = (0x12),
|
||||||
|
CC_CTRL_1 = (0x14),
|
||||||
|
RX_SYN = (0x15),
|
||||||
|
XAH_CTRL_1 = (0x17),
|
||||||
|
FTN_CTRL = (0x18),
|
||||||
|
PLL_CF = (0x1A),
|
||||||
|
PLL_DCU = (0x1B),
|
||||||
|
PART_NUM = (0x1C),
|
||||||
|
VERSION_NUM = (0x1D),
|
||||||
|
MAN_ID_0 = (0x1E),
|
||||||
|
MAN_ID_1 = (0x1F),
|
||||||
|
SHORT_ADDR_0 = (0x20),
|
||||||
|
SHORT_ADDR_1 = (0x21),
|
||||||
|
PAN_ID_0 = (0x22),
|
||||||
|
PAN_ID_1 = (0x23),
|
||||||
|
IEEE_ADDR_0 = (0x24),
|
||||||
|
IEEE_ADDR_1 = (0x25),
|
||||||
|
IEEE_ADDR_2 = (0x26),
|
||||||
|
IEEE_ADDR_3 = (0x27),
|
||||||
|
IEEE_ADDR_4 = (0x28),
|
||||||
|
IEEE_ADDR_5 = (0x29),
|
||||||
|
IEEE_ADDR_6 = (0x2A),
|
||||||
|
IEEE_ADDR_7 = (0x2B),
|
||||||
|
XAH_CTRL_0 = (0x2C),
|
||||||
|
CSMA_SEED_0 = (0x2D),
|
||||||
|
CSMA_SEED_1 = (0x2E),
|
||||||
|
CSMA_BE = (0x2F),
|
||||||
|
TST_CTRL_DIGI = (0x36)
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //SKL_TUNNEL_RADIO_HW_REGISTERS_HPP
|
||||||
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
|
||||||
18
radio/include/radio_protocol.hpp
Normal file
18
radio/include/radio_protocol.hpp
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
//
|
||||||
|
// Created by erki on 28.07.22.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef SKL_TUNNEL_RADIO_PROTOCOL_HPP
|
||||||
|
#define SKL_TUNNEL_RADIO_PROTOCOL_HPP
|
||||||
|
|
||||||
|
#include "radio_protocol_frame.hpp"
|
||||||
|
|
||||||
|
namespace radio::protocol
|
||||||
|
{
|
||||||
|
|
||||||
|
std::size_t composeFrameBuffer(const FrameStructure& frame);
|
||||||
|
FrameStructure decomposeFrameBuffer(const std::uint8_t* data);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //SKL_TUNNEL_RADIO_PROTOCOL_HPP
|
||||||
135
radio/include/radio_protocol_frame.hpp
Normal file
135
radio/include/radio_protocol_frame.hpp
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
//
|
||||||
|
// Created by erki on 18.07.22.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef SKL_TUNNEL_RADIO_FRAME_HPP
|
||||||
|
#define SKL_TUNNEL_RADIO_FRAME_HPP
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <optional>
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
|
#include <utility_assert.hpp>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference section 37.1 of the SAMR21 datasheet for these structures.
|
||||||
|
*/
|
||||||
|
namespace radio::protocol
|
||||||
|
{
|
||||||
|
|
||||||
|
struct PhyHeader
|
||||||
|
{
|
||||||
|
std::uint8_t frame_length : 7;
|
||||||
|
std::uint8_t reserved : 1;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct FrameControlField
|
||||||
|
{
|
||||||
|
enum FrameType
|
||||||
|
{
|
||||||
|
FRAME_TYPE_BEACON = 0b000,
|
||||||
|
FRAME_TYPE_DATA = 0b001,
|
||||||
|
FRAME_TYPE_ACKNOWLEDGE = 0b010,
|
||||||
|
FRAME_TYPE_MAC_COMMAND = 0b011
|
||||||
|
};
|
||||||
|
|
||||||
|
enum AddressingMode
|
||||||
|
{
|
||||||
|
ADDRESSING_MODE_NOT_SPECIFIED = 0b00,
|
||||||
|
ADDRESSING_MODE_SHORT = 0b10,
|
||||||
|
ADDRESSING_MODE_LONG = 0b11
|
||||||
|
};
|
||||||
|
|
||||||
|
FrameType type : 3;
|
||||||
|
std::uint8_t security_enabled : 1;
|
||||||
|
std::uint8_t frame_pending : 1;
|
||||||
|
std::uint8_t ack_requested : 1;
|
||||||
|
std::uint8_t pan_id_compression : 1;
|
||||||
|
std::uint8_t : 3;
|
||||||
|
AddressingMode destination_addressing_mode : 2;
|
||||||
|
std::uint8_t frame_version : 2;
|
||||||
|
AddressingMode source_addressing_mode : 2;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct Address
|
||||||
|
{
|
||||||
|
std::variant<std::uint16_t, std::uint64_t> mac = 0ull;
|
||||||
|
std::uint16_t pan_id = 0;
|
||||||
|
|
||||||
|
FrameControlField::AddressingMode getAddressingMode() const
|
||||||
|
{
|
||||||
|
if (std::holds_alternative<std::uint16_t>(mac))
|
||||||
|
return FrameControlField::ADDRESSING_MODE_SHORT;
|
||||||
|
else if (std::holds_alternative<std::uint64_t>(mac))
|
||||||
|
return FrameControlField::ADDRESSING_MODE_LONG;
|
||||||
|
else
|
||||||
|
return FrameControlField::ADDRESSING_MODE_NOT_SPECIFIED;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setShortAddress(const std::uint16_t address)
|
||||||
|
{
|
||||||
|
mac = address;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setLongAddress(const std::uint64_t address)
|
||||||
|
{
|
||||||
|
mac = address;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint16_t getShortAddress() const
|
||||||
|
{
|
||||||
|
const std::uint16_t* addr = std::get_if<std::uint16_t>(&mac);
|
||||||
|
SKULLC_ASSERT_DEBUG(addr != nullptr);
|
||||||
|
|
||||||
|
return *addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint64_t getLongAddress() const
|
||||||
|
{
|
||||||
|
const std::uint64_t* addr = std::get_if<std::uint64_t>(&mac);
|
||||||
|
SKULLC_ASSERT_DEBUG(addr != nullptr);
|
||||||
|
|
||||||
|
return *addr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FrameStructure
|
||||||
|
{
|
||||||
|
FrameControlField frame_control_field;
|
||||||
|
std::uint8_t sequence_number;
|
||||||
|
Address destination_address;
|
||||||
|
Address source_address;
|
||||||
|
std::uint16_t frame_checksum;
|
||||||
|
|
||||||
|
static constexpr std::size_t max_frame_size = 127;
|
||||||
|
static constexpr std::size_t max_payload_size
|
||||||
|
= max_frame_size - sizeof(FrameControlField) - sizeof(frame_checksum)
|
||||||
|
- sizeof(sequence_number) - (2 * (sizeof(std::uint16_t) + sizeof(std::uint64_t)));
|
||||||
|
|
||||||
|
std::uint8_t payload_length;
|
||||||
|
std::array<std::uint8_t, max_payload_size> payload;
|
||||||
|
|
||||||
|
FrameStructure();
|
||||||
|
|
||||||
|
static FrameStructure createDataFrame();
|
||||||
|
|
||||||
|
FrameStructure& setPayload(const std::uint8_t* data, const std::uint8_t length);
|
||||||
|
template<std::size_t N>
|
||||||
|
FrameStructure& setPayload(const std::array<std::uint8_t, N>& data)
|
||||||
|
{
|
||||||
|
static_assert(N <= max_payload_size, "Data length N exceeds max_payload_size.");
|
||||||
|
return setPayload(data.data(), std::uint8_t(N));
|
||||||
|
}
|
||||||
|
|
||||||
|
FrameStructure& setSourceAddress(const Address& addr);
|
||||||
|
FrameStructure& setDestinationAddress(const Address& addr);
|
||||||
|
|
||||||
|
std::uint8_t getTotalFrameLength() const;
|
||||||
|
std::uint8_t calculatePayloadLength(const std::uint8_t total_frame_length) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //SKL_TUNNEL_RADIO_FRAME_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
|
||||||
|
|||||||
@ -10,28 +10,24 @@
|
|||||||
|
|
||||||
#include <hal_delay.h>
|
#include <hal_delay.h>
|
||||||
#include <hal_ext_irq.h>
|
#include <hal_ext_irq.h>
|
||||||
|
#include <utility_assert.hpp>
|
||||||
|
#include <utility_staticpointer.hpp>
|
||||||
|
#include <utility_logging.hpp>
|
||||||
|
#include <optional>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
|
||||||
radio::HwInstance* _INSTANCE = nullptr;
|
Utility::StaticPointer<radio::HwInstance> _INSTANCE;
|
||||||
|
Utility::IFunction<void (radio::HwInstance*)>* _CALLBACK;
|
||||||
|
|
||||||
void _irq_handler()
|
void _irq_handler()
|
||||||
{
|
{
|
||||||
_INSTANCE->irq_handler();
|
SKULLC_ASSERT_DEBUG(_INSTANCE);
|
||||||
}
|
|
||||||
|
|
||||||
void _startup()
|
if (_CALLBACK)
|
||||||
{
|
(*_CALLBACK)(_INSTANCE.get());
|
||||||
gpio_set_pin_level(OUT_RADIO_RST, false);
|
|
||||||
gpio_set_pin_level(OUT_RADIO_SLP_TR, false);
|
|
||||||
delay_ms(10);
|
|
||||||
|
|
||||||
ext_irq_enable(IN_RADIO_IRQ);
|
|
||||||
ext_irq_register(IN_RADIO_IRQ, _irq_handler);
|
|
||||||
|
|
||||||
gpio_set_pin_level(OUT_RADIO_RST, true);
|
|
||||||
delay_ms(10);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -39,43 +35,14 @@ void _startup()
|
|||||||
namespace radio
|
namespace radio
|
||||||
{
|
{
|
||||||
|
|
||||||
HwInstance* HwInstance::create_instance()
|
HwInstance* HwInstance::instance()
|
||||||
{
|
{
|
||||||
return new HwInstance{};
|
if (!_INSTANCE.isInitialized())
|
||||||
}
|
{
|
||||||
|
_INSTANCE.setup();
|
||||||
|
}
|
||||||
|
|
||||||
void HwInstance::irq_handler()
|
return _INSTANCE.get();
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t HwInstance::register_read(uint8_t address)
|
|
||||||
{
|
|
||||||
address = (address & 0x3F) | (1 << 7);
|
|
||||||
gpio_set_pin_level(OUT_RADIO_CS, false);
|
|
||||||
|
|
||||||
delay_us(1);
|
|
||||||
|
|
||||||
io_write(m_spi_io, &address, 1);
|
|
||||||
delay_us(1);
|
|
||||||
uint8_t data = 0xFF;
|
|
||||||
io_read(m_spi_io, &data, 1);
|
|
||||||
|
|
||||||
gpio_set_pin_level(OUT_RADIO_CS, true);
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HwInstance::register_write(const uint8_t address, const uint8_t value)
|
|
||||||
{
|
|
||||||
uint8_t data[2] = {
|
|
||||||
uint8_t((address & 0x3F) | (1 << 7) | (1 << 6)),
|
|
||||||
uint8_t(value & 0xEF)
|
|
||||||
};
|
|
||||||
|
|
||||||
gpio_set_pin_level(OUT_RADIO_CS, false);
|
|
||||||
io_write(m_spi_io, data, 2);
|
|
||||||
|
|
||||||
gpio_set_pin_level(OUT_RADIO_CS, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HwInstance::HwInstance()
|
HwInstance::HwInstance()
|
||||||
@ -86,7 +53,218 @@ HwInstance::HwInstance()
|
|||||||
spi_m_sync_get_io_descriptor(m_spi, &m_spi_io);
|
spi_m_sync_get_io_descriptor(m_spi, &m_spi_io);
|
||||||
spi_m_sync_enable(m_spi);
|
spi_m_sync_enable(m_spi);
|
||||||
|
|
||||||
_startup();
|
gpio_set_pin_level(OUT_RADIO_RST, false);
|
||||||
|
gpio_set_pin_level(OUT_RADIO_SLP_TR, false);
|
||||||
|
delay_ms(10);
|
||||||
|
|
||||||
|
ext_irq_register(IN_RADIO_IRQ, _irq_handler);
|
||||||
|
|
||||||
|
gpio_set_pin_level(OUT_RADIO_RST, true);
|
||||||
|
delay_ms(10);
|
||||||
|
|
||||||
|
// Enable safe mode for TX.
|
||||||
|
register_write(Registers::TRX_CTRL_2, 0x80);
|
||||||
|
|
||||||
|
// Disable external clock output.
|
||||||
|
std::uint8_t trx_ctrl = register_read(Registers::TRX_CTRL_0);
|
||||||
|
trx_ctrl &= ~(0x07);
|
||||||
|
trx_ctrl &= ~(0x08);
|
||||||
|
register_write(Registers::TRX_CTRL_0, trx_ctrl);
|
||||||
|
|
||||||
|
// Enable interrupts.
|
||||||
|
register_write(Registers::IRQ_MASK, 0xFF);
|
||||||
|
|
||||||
|
// clear interrupts.
|
||||||
|
register_read(Registers::IRQ_STATUS);
|
||||||
|
|
||||||
|
set_current_state(States::TRX_OFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HwInstance::set_irq_handler(Utility::IFunction<void (HwInstance*)>* cb)
|
||||||
|
{
|
||||||
|
_CALLBACK = cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
Interrupts HwInstance::get_pending_irq()
|
||||||
|
{
|
||||||
|
return Interrupts(register_read(Registers::IRQ_STATUS));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t HwInstance::register_read(const Registers& address)
|
||||||
|
{
|
||||||
|
const uint8_t address_to_write = (uint8_t(address) & 0x3F) | (1 << 7);
|
||||||
|
gpio_set_pin_level(OUT_RADIO_CS, false);
|
||||||
|
|
||||||
|
delay_us(1);
|
||||||
|
|
||||||
|
io_write(m_spi_io, &address_to_write, 1);
|
||||||
|
delay_us(1);
|
||||||
|
uint8_t data = 0xFF;
|
||||||
|
io_read(m_spi_io, &data, 1);
|
||||||
|
|
||||||
|
gpio_set_pin_level(OUT_RADIO_CS, true);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HwInstance::register_write(const Registers& address, const uint8_t value)
|
||||||
|
{
|
||||||
|
uint8_t data[2] = {
|
||||||
|
uint8_t((uint8_t(address) & 0x3F) | (1 << 7) | (1 << 6)),
|
||||||
|
uint8_t(value & 0xEF)
|
||||||
|
};
|
||||||
|
|
||||||
|
gpio_set_pin_level(OUT_RADIO_CS, false);
|
||||||
|
io_write(m_spi_io, data, 2);
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
if (new_state == m_current_state)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
m_wait_can_transition();
|
||||||
|
|
||||||
|
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)))
|
||||||
|
{
|
||||||
|
set_current_state(States::PLL_ON);
|
||||||
|
}
|
||||||
|
|
||||||
|
register_write(Registers::TRX_STATE, std::uint8_t(new_state));
|
||||||
|
m_wait_can_transition();
|
||||||
|
SKULLC_LOG_DEBUG("HW: New state. %d -> %d.", m_current_state, new_state);
|
||||||
|
m_current_state = new_state;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HwInstance::set_address_short(const std::uint16_t address)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
146
radio/src/radio_protocol.cpp
Normal file
146
radio/src/radio_protocol.cpp
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
//
|
||||||
|
// Created by erki on 28.07.22.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "radio_protocol.hpp"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
using namespace radio::protocol;
|
||||||
|
|
||||||
|
[[nodiscard]] std::uint8_t* serializeAddress(std::uint8_t* data, const Address& address)
|
||||||
|
{
|
||||||
|
std::memcpy(data, &address.pan_id, sizeof(address.pan_id));
|
||||||
|
data += sizeof(address.pan_id);
|
||||||
|
|
||||||
|
if (address.getAddressingMode() == FrameControlField::ADDRESSING_MODE_SHORT)
|
||||||
|
{
|
||||||
|
const std::uint16_t mac = address.getShortAddress();
|
||||||
|
std::memcpy(data, &mac, sizeof(mac));
|
||||||
|
data += sizeof(mac);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const std::uint64_t mac = address.getLongAddress();
|
||||||
|
std::memcpy(data, &mac, sizeof(mac));
|
||||||
|
data += sizeof(mac);
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::uint8_t* serializePhyHeader(std::uint8_t* data, const PhyHeader& header)
|
||||||
|
{
|
||||||
|
static_assert(sizeof(PhyHeader) == 1, "PHY Header must be 1 byte/octet.");
|
||||||
|
std::memcpy(data, &header, sizeof(PhyHeader));
|
||||||
|
data += sizeof(PhyHeader);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::uint8_t* serializeMacProtocolDataUnit(std::uint8_t* data, const FrameStructure& frame)
|
||||||
|
{
|
||||||
|
static_assert(sizeof(frame.frame_control_field) == 2, "FCF must be 2 bytes/octets.");
|
||||||
|
std::memcpy(data, &frame.frame_control_field, sizeof(frame.frame_control_field));
|
||||||
|
data += sizeof(FrameControlField);
|
||||||
|
|
||||||
|
std::memcpy(data, &frame.sequence_number, sizeof(frame.sequence_number));
|
||||||
|
data += sizeof(frame.sequence_number);
|
||||||
|
|
||||||
|
data = serializeAddress(data, frame.destination_address);
|
||||||
|
data = serializeAddress(data, frame.source_address);
|
||||||
|
|
||||||
|
std::memcpy(data, frame.payload.data(), frame.payload_length);
|
||||||
|
data += frame.payload_length;
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] const std::uint8_t* deserializeAddress(const std::uint8_t* data, const FrameControlField::AddressingMode& addressing_mode, Address& address)
|
||||||
|
{
|
||||||
|
std::memcpy(&address.pan_id, data, sizeof(address.pan_id));
|
||||||
|
data += sizeof(address.pan_id);
|
||||||
|
|
||||||
|
if (addressing_mode == FrameControlField::ADDRESSING_MODE_SHORT)
|
||||||
|
{
|
||||||
|
std::uint16_t mac = 0;
|
||||||
|
std::memcpy(&mac, data, sizeof(mac));
|
||||||
|
data += sizeof(mac);
|
||||||
|
|
||||||
|
address.setShortAddress(mac);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::uint64_t mac = 0;
|
||||||
|
std::memcpy(&mac, data, sizeof(mac));
|
||||||
|
data += sizeof(mac);
|
||||||
|
|
||||||
|
address.setLongAddress(mac);
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] const std::uint8_t* deserializePhyHeader(const std::uint8_t* data, PhyHeader& header)
|
||||||
|
{
|
||||||
|
std::memcpy(&header, data, sizeof(PhyHeader));
|
||||||
|
data += sizeof(PhyHeader);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] FrameStructure deserializeMacProtocolDataUnit(const std::uint8_t* data, const std::uint8_t total_frame_length)
|
||||||
|
{
|
||||||
|
FrameStructure frame;
|
||||||
|
std::memcpy(&frame.frame_control_field, data, sizeof(frame.frame_control_field));
|
||||||
|
data += sizeof(frame.frame_control_field);
|
||||||
|
|
||||||
|
std::memcpy(&frame.sequence_number, data, sizeof(frame.sequence_number));
|
||||||
|
data += sizeof(frame.sequence_number);
|
||||||
|
|
||||||
|
data = deserializeAddress(data, frame.frame_control_field.destination_addressing_mode, frame.destination_address);
|
||||||
|
data = deserializeAddress(data, frame.frame_control_field.source_addressing_mode, frame.source_address);
|
||||||
|
|
||||||
|
frame.payload_length = frame.calculatePayloadLength(total_frame_length);
|
||||||
|
std::memcpy(frame.payload.data(), data, frame.payload_length);
|
||||||
|
data += frame.payload_length;
|
||||||
|
|
||||||
|
std::memcpy(&frame.frame_checksum, data, sizeof(frame.frame_checksum));
|
||||||
|
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace radio::protocol
|
||||||
|
{
|
||||||
|
|
||||||
|
std::size_t composeFrameBuffer(std::uint8_t* data, const FrameStructure& frame)
|
||||||
|
{
|
||||||
|
std::uint8_t* const data_start = data;
|
||||||
|
|
||||||
|
PhyHeader header;
|
||||||
|
std::memset(&header, 0, sizeof(PhyHeader));
|
||||||
|
header.frame_length = frame.getTotalFrameLength();
|
||||||
|
data = serializePhyHeader(data, header);
|
||||||
|
|
||||||
|
data = serializeMacProtocolDataUnit(data, frame);
|
||||||
|
|
||||||
|
return data - data_start;
|
||||||
|
}
|
||||||
|
|
||||||
|
FrameStructure decomposeFrameBuffer(const std::uint8_t* data)
|
||||||
|
{
|
||||||
|
PhyHeader header;
|
||||||
|
data = deserializePhyHeader(data, header);
|
||||||
|
|
||||||
|
const std::uint8_t total_frame_length = header.frame_length;
|
||||||
|
const FrameStructure frame = deserializeMacProtocolDataUnit(data, total_frame_length);
|
||||||
|
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
99
radio/src/radio_protocol_frame.cpp
Normal file
99
radio/src/radio_protocol_frame.cpp
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
//
|
||||||
|
// Created by erki on 28.07.22.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "radio_protocol_frame.hpp"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
using namespace radio::protocol;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr T zeroInitialized()
|
||||||
|
{
|
||||||
|
static_assert(std::is_trivially_default_constructible<T>::value, "Struct is not trivially default constructible.");
|
||||||
|
|
||||||
|
T t;
|
||||||
|
std::memset(&t, 0, sizeof(T));
|
||||||
|
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint8_t get_address_size(const Address& address)
|
||||||
|
{
|
||||||
|
std::uint8_t size = sizeof(address.pan_id);
|
||||||
|
if (address.getAddressingMode() == FrameControlField::ADDRESSING_MODE_SHORT)
|
||||||
|
size += sizeof(std::uint16_t);
|
||||||
|
else
|
||||||
|
size += sizeof(std::uint64_t);
|
||||||
|
|
||||||
|
return size;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace radio::protocol
|
||||||
|
{
|
||||||
|
|
||||||
|
FrameStructure::FrameStructure()
|
||||||
|
: frame_control_field(zeroInitialized<FrameControlField>())
|
||||||
|
, sequence_number(0)
|
||||||
|
, frame_checksum(0)
|
||||||
|
, payload_length(0)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
FrameStructure FrameStructure::createDataFrame()
|
||||||
|
{
|
||||||
|
FrameStructure structure;
|
||||||
|
structure.frame_control_field.type = FrameControlField::FRAME_TYPE_DATA;
|
||||||
|
structure.frame_control_field.frame_version = 1;
|
||||||
|
|
||||||
|
return structure;
|
||||||
|
}
|
||||||
|
|
||||||
|
FrameStructure& FrameStructure::setPayload(const std::uint8_t* data, const std::uint8_t length)
|
||||||
|
{
|
||||||
|
std::memmove(payload.data(), data, length);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
FrameStructure& FrameStructure::setSourceAddress(const Address& addr)
|
||||||
|
{
|
||||||
|
source_address = addr;
|
||||||
|
frame_control_field.source_addressing_mode = addr.getAddressingMode();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
FrameStructure& FrameStructure::setDestinationAddress(const Address& addr)
|
||||||
|
{
|
||||||
|
destination_address = addr;
|
||||||
|
frame_control_field.destination_addressing_mode = addr.getAddressingMode();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint8_t FrameStructure::getTotalFrameLength() const
|
||||||
|
{
|
||||||
|
std::uint8_t size = sizeof(frame_control_field) + sizeof(sequence_number);
|
||||||
|
|
||||||
|
size += get_address_size(destination_address);
|
||||||
|
size += get_address_size(source_address);
|
||||||
|
size += payload_length;
|
||||||
|
size += sizeof(frame_checksum);
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint8_t FrameStructure::calculatePayloadLength(const std::uint8_t total_frame_length) const
|
||||||
|
{
|
||||||
|
std::uint8_t payload_length = total_frame_length - sizeof(frame_control_field) - sizeof(sequence_number);
|
||||||
|
payload_length -= get_address_size(destination_address);
|
||||||
|
payload_length -= get_address_size(source_address);
|
||||||
|
payload_length -= sizeof(frame_checksum);
|
||||||
|
|
||||||
|
return payload_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
38
syscalls.c
Normal file
38
syscalls.c
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
//
|
||||||
|
// Created by erki on 10.07.22.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/times.h>
|
||||||
|
|
||||||
|
extern int __io_putchar(int ch) __attribute__((weak));
|
||||||
|
extern int __io_getchar(void) __attribute__((weak));
|
||||||
|
|
||||||
|
__attribute__((weak)) int _read(int file, char *ptr, int len)
|
||||||
|
{
|
||||||
|
int DataIdx;
|
||||||
|
|
||||||
|
for (DataIdx = 0; DataIdx < len; DataIdx++)
|
||||||
|
{
|
||||||
|
*ptr++ = __io_getchar();
|
||||||
|
}
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((weak)) int _write(int file, char *ptr, int len)
|
||||||
|
{
|
||||||
|
int DataIdx;
|
||||||
|
|
||||||
|
for (DataIdx = 0; DataIdx < len; DataIdx++)
|
||||||
|
{
|
||||||
|
__io_putchar(*ptr++);
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user