Skip to content

Commit

Permalink
samples: Introduce "simple_txrx"
Browse files Browse the repository at this point in the history
This introduce an example of use of Rail library in Zephyr.

This is a reboot of PR[1]. Compared to the original PR, this version:
  - remove some files imported from Simplicity Studio. Only
    radio_config.[ch] are kept.
  - drop the useless Finite State Machine

For now only two boards (based xg22 and xg24) are supported. I hope to
add support for all the Series-2 boards in the future.

Note Zephyr does not allow to host samples application in HALs. Samples
specific to one board and using non-standard APIs are not very welcomed
in the main repo. So, the Silabs downstream is probably the best (the
only) place to provide publish it.

[1]: zephyrproject-rtos/hal_silabs#49

Signed-off-by: Jérôme Pouiller <[email protected]>
  • Loading branch information
jerome-pouiller committed Oct 10, 2024
1 parent 1ec734f commit 63479d2
Show file tree
Hide file tree
Showing 11 changed files with 1,167 additions and 0 deletions.
14 changes: 14 additions & 0 deletions samples/simple_txrx/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copyright (c) 2023 Silicon Laboratories Inc.
# SPDX-License-Identifier: Zlib

cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(simple_txrx)

target_include_directories(app PRIVATE
src/rail-configs/${CONFIG_BOARD}
)
target_sources(app PRIVATE
src/main.c
src/rail-configs/${CONFIG_BOARD}/rail_config.c
)
8 changes: 8 additions & 0 deletions samples/simple_txrx/boards/sltb010a.overlay
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* Copyright (c) 2023 Silicon Laboratories Inc.
* SPDX-License-Identifier: Zlib
*/

&cpu0 {
cpu-power-states = <&pstate_em1>;
};
8 changes: 8 additions & 0 deletions samples/simple_txrx/boards/xg24_dk2601b.overlay
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* Copyright (c) 2023 Silicon Laboratories Inc.
* SPDX-License-Identifier: Zlib
*/

&cpu0 {
cpu-power-states = <&pstate_em1>;
};
8 changes: 8 additions & 0 deletions samples/simple_txrx/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
CONFIG_SOC_GECKO_CUSTOM_RADIO_PHY=y
CONFIG_SHELL=y
CONFIG_PM=y
CONFIG_EVENTS=y
CONFIG_LOG=y

CONFIG_DEBUG_THREAD_INFO=y
CONFIG_DEBUG=y
290 changes: 290 additions & 0 deletions samples/simple_txrx/src/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,290 @@
/*
* Copyright (c) 2024 Silicon Laboratories Inc.
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/drivers/gpio.h>
#include <zephyr/shell/shell.h>
#include <zephyr/logging/log.h>
#include <zephyr/kernel.h>

#include "rail.h"
#include "rail_config.h"
#include "pa_conversions_efr32.h"

LOG_MODULE_REGISTER(app);

#ifdef RAIL0_CHANNEL_GROUP_1_PROFILE_WISUN_OFDM
# if !defined(HARDWARE_BOARD_HAS_EFF)
BUILD_ASSERT(SL_RAIL_UTIL_PA_SELECTION_SUBGHZ == RAIL_TX_POWER_MODE_OFDM_PA,
"Please use the OFDM PA settings in the sl_rail_util_pa_config.h for OFDM phys");
# endif
# if defined(HARDWARE_BOARD_HAS_EFF) && RAIL_SUPPORTS_EFF
BUILD_ASSERT(SL_RAIL_UTIL_PA_SELECTION_SUBGHZ >= RAIL_TX_POWER_MODE_OFDM_PA_EFF_30DBM,
"Please use the OFDM PA for EFF settings in the sl_rail_util_pa_config.h for OFDM phys");
# endif
#endif

static const struct gpio_dt_spec sw0 = GPIO_DT_SPEC_GET(DT_ALIAS(sw0), gpios);
static const struct gpio_dt_spec led_rx = GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios);
#if DT_NODE_EXISTS(DT_ALIAS(led1))
static const struct gpio_dt_spec led_tx = GPIO_DT_SPEC_GET(DT_ALIAS(led1), gpios);
#else
static const struct gpio_dt_spec led_tx = led_rx;
#endif

enum {
EV_RAIL_RX = BIT(0),
EV_BTN_PRESSED = BIT(1),
};

struct {
RAIL_Handle_t rail_handle;
struct k_event rx_event;
int channel;
const uint8_t *payload;
int payload_len;
} app_ctx;

void rx_packets(RAIL_Handle_t rail_handle)
{
uint8_t rx_frame[32];
RAIL_RxPacketHandle_t handle;
RAIL_RxPacketInfo_t info;
RAIL_Status_t status;

for (;;) {
handle = RAIL_GetRxPacketInfo(rail_handle, RAIL_RX_PACKET_HANDLE_OLDEST_COMPLETE,
&info);
if (handle == RAIL_RX_PACKET_HANDLE_INVALID) {
return;
}
if (info.packetBytes < sizeof(rx_frame)) {
RAIL_CopyRxPacket(rx_frame, &info);
}
status = RAIL_ReleaseRxPacket(rail_handle, handle);
if (status) {
LOG_ERR("RAIL_ReleaseRxPacket(): %d", status);
}
if (info.packetBytes < sizeof(rx_frame)) {
LOG_HEXDUMP_INF(rx_frame, info.packetBytes, "rx data:");
} else {
LOG_INF("rx: skip large packet");
}
gpio_pin_toggle_dt(&led_rx);
}
}

void tx_packet(RAIL_Handle_t rail_handle, int channel, const uint8_t *payload, int len)
{
RAIL_Status_t status;
int ret;

ret = RAIL_WriteTxFifo(rail_handle, payload, len, true);
if (ret != len) {
LOG_ERR("RAIL_WriteTxFifo(): %d", ret);
return;
}
gpio_pin_toggle_dt(&led_tx);
status = RAIL_StartTx(rail_handle, channel, RAIL_TX_OPTIONS_DEFAULT, NULL);
if (status) {
LOG_ERR("RAIL_StartTx(): %d ", status);
}
LOG_HEXDUMP_INF(payload, len, "tx data:");
}

void btn_pressed(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
{
/* This function is called from an ISR context. So, transfer the real
* processing to the main loop.
*/
k_event_post(&app_ctx.rx_event, EV_BTN_PRESSED);
}

void cli_send(const struct shell *sh, size_t argc, char **argv)
{
tx_packet(app_ctx.rail_handle, app_ctx.channel, app_ctx.payload, app_ctx.payload_len);
}

void rail_on_event(RAIL_Handle_t rail_handle, RAIL_Events_t events)
{
RAIL_Status_t status;

if (events & RAIL_EVENTS_RX_COMPLETION) {
if (events & RAIL_EVENT_RX_PACKET_RECEIVED) {
gpio_pin_toggle_dt(&led_rx);
RAIL_HoldRxPacket(rail_handle);
k_event_post(&app_ctx.rx_event, EV_RAIL_RX);
} else {
LOG_ERR("radio rx error: %08llx", events);
}
}

if (events & RAIL_EVENTS_TX_COMPLETION) {
if (!(events & RAIL_EVENT_TX_PACKET_SENT)) {
LOG_ERR("radio tx error: %08llx", events);
}
gpio_pin_toggle_dt(&led_tx);
}

if (events & RAIL_EVENTS_TXACK_COMPLETION) {
/* We do not configure Tx ack. Catch the event anyway */
LOG_INF("received ack completion");
}

if (events & RAIL_EVENT_CAL_NEEDED) {
status = RAIL_Calibrate(rail_handle, NULL, RAIL_CAL_ALL_PENDING);
if (status) {
LOG_ERR("RAIL_Calibrate(): %d", status);
}
}
}

static void rail_on_rf_ready(RAIL_Handle_t rail_handle)
{
LOG_INF("radio is ready");
}

static void rail_on_channel_config(RAIL_Handle_t rail_handle,
const RAIL_ChannelConfigEntry_t *entry)
{
sl_rail_util_pa_on_channel_config_change(rail_handle, entry);
}

static RAIL_Handle_t rail_init(void)
{
static uint8_t tx_fifo[256] __aligned(4);
RAIL_Config_t rail_config = {
.eventsCallback = &rail_on_event,
};
RAIL_DataConfig_t data_config = {
.txSource = TX_PACKET_DATA,
.rxSource = RX_PACKET_DATA,
.txMethod = PACKET_MODE,
.rxMethod = PACKET_MODE,
};
RAIL_StateTransitions_t transitions = {
.success = RAIL_RF_STATE_RX,
.error = RAIL_RF_STATE_RX,
};
RAIL_Handle_t rail_handle;
RAIL_Status_t status;
int ret;

rail_handle = RAIL_Init(&rail_config, &rail_on_rf_ready);
if (!rail_handle) {
LOG_ERR("RAIL_Init() failed");
}
status = RAIL_ConfigData(rail_handle, &data_config);
if (status) {
LOG_ERR("RAIL_ConfigData(): %d", status);
}
status = RAIL_ConfigChannels(rail_handle, channelConfigs[0], &rail_on_channel_config);
if (status) {
LOG_ERR("RAIL_ConfigChannels(): %d", status);
}
status = RAIL_SetPtiProtocol(rail_handle, RAIL_PTI_PROTOCOL_CUSTOM);
if (status) {
LOG_ERR("RAIL_SetPtiProtocol(): %d", status);
}
status = RAIL_ConfigCal(rail_handle, RAIL_CAL_TEMP | RAIL_CAL_ONETIME);
if (status) {
LOG_ERR("RAIL_ConfigCal(): %d", status);
}
status = RAIL_ConfigEvents(rail_handle, RAIL_EVENTS_ALL,
RAIL_EVENTS_RX_COMPLETION |
RAIL_EVENTS_TX_COMPLETION |
RAIL_EVENTS_TXACK_COMPLETION |
RAIL_EVENT_CAL_NEEDED);
if (status) {
LOG_ERR("RAIL_ConfigEvents(): %d", status);
}
status = RAIL_SetTxTransitions(rail_handle, &transitions);
if (status) {
LOG_ERR("RAIL_SetTxTransitions(): %d", status);
}
status = RAIL_SetRxTransitions(rail_handle, &transitions);
if (status) {
LOG_ERR("RAIL_SetRxTransitions(): %d", status);
}
ret = RAIL_SetTxFifo(rail_handle, tx_fifo, 0, sizeof(tx_fifo));
if (ret != sizeof(tx_fifo)) {
LOG_ERR("RAIL_SetTxFifo(): %d != %d", ret, sizeof(tx_fifo));
}

return rail_handle;
}

static void rail_isr_installer(void)
{
#ifdef CONFIG_SOC_SERIES_EFR32MG24
IRQ_CONNECT(SYNTH_IRQn, 0, SYNTH_IRQHandler, NULL, 0);
#else
IRQ_CONNECT(RDMAILBOX_IRQn, 0, RDMAILBOX_IRQHandler, NULL, 0);
#endif
IRQ_CONNECT(RAC_SEQ_IRQn, 0, RAC_SEQ_IRQHandler, NULL, 0);
IRQ_CONNECT(RAC_RSM_IRQn, 0, RAC_RSM_IRQHandler, NULL, 0);
IRQ_CONNECT(PROTIMER_IRQn, 0, PROTIMER_IRQHandler, NULL, 0);
IRQ_CONNECT(MODEM_IRQn, 0, MODEM_IRQHandler, NULL, 0);
IRQ_CONNECT(FRC_IRQn, 0, FRC_IRQHandler, NULL, 0);
IRQ_CONNECT(BUFC_IRQn, 0, BUFC_IRQHandler, NULL, 0);
IRQ_CONNECT(AGC_IRQn, 0, AGC_IRQHandler, NULL, 0);
}

int main(void)
{
static const uint8_t default_payload[] = {
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
};
struct gpio_callback sw0_cb;
RAIL_Status_t status;
uint32_t events;
int ret;

k_event_init(&app_ctx.rx_event);
gpio_pin_configure_dt(&led_tx, GPIO_OUTPUT_INACTIVE);
gpio_pin_configure_dt(&led_rx, GPIO_OUTPUT_INACTIVE);
gpio_pin_configure_dt(&sw0, GPIO_INPUT);
gpio_pin_interrupt_configure_dt(&sw0, GPIO_INT_EDGE_TO_ACTIVE);
gpio_init_callback(&sw0_cb, btn_pressed, BIT(sw0.pin));
gpio_add_callback(sw0.port, &sw0_cb);

rail_isr_installer();
sl_rail_util_pa_init();
app_ctx.rail_handle = rail_init();
app_ctx.channel = 0;
app_ctx.payload = default_payload;
app_ctx.payload_len = sizeof(default_payload);

ret = RAIL_SetFixedLength(app_ctx.rail_handle, app_ctx.payload_len);
if (ret != app_ctx.payload_len) {
LOG_ERR("RAIL_SetFixedLength(): %d ", ret);
}
status = RAIL_StartRx(app_ctx.rail_handle, app_ctx.channel, NULL);
if (status) {
LOG_ERR("RAIL_StartRx(): %d ", status);
}

#ifdef CONFIG_PM
status = RAIL_InitPowerManager();
if (status) {
LOG_ERR("RAIL_InitPowerManager(): %d", status);
}
#endif

for (;;) {
events = k_event_wait(&app_ctx.rx_event, 0xFFFFFFFF, true, K_FOREVER);
if (events & EV_RAIL_RX)
rx_packets(app_ctx.rail_handle);
if (events & EV_BTN_PRESSED)
tx_packet(app_ctx.rail_handle, app_ctx.channel, app_ctx.payload, app_ctx.payload_len);
}

return 0;
}

SHELL_STATIC_SUBCMD_SET_CREATE(radio_cmds,
SHELL_CMD_ARG(send, NULL, "Send a packet", cli_send, 1, 0),
SHELL_SUBCMD_SET_END
);
SHELL_CMD_ARG_REGISTER(radio, &radio_cmds, "Radio control", NULL, 2, 0);
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<multi_phy_configuration part_family="lynx" part_revision="A0" rail_adapter_version="rail_api_2.x" status_code="0" xsd_version="0.0.20">
<base_channel_configurations>
<base_channel_configuration name="Protocol Configuration" profile="Base">
<channel_config_entries>
<channel_config_entry name="Channel Group 1">
<channel_number_start>0</channel_number_start>
<channel_number_end>20</channel_number_end>
<physical_channel_offset>SAME_AS_FIRST_CHANNEL</physical_channel_offset>
<max_power>RAIL_TX_POWER_MAX</max_power>
<metadata>{"selectedPhy":"PHY_Datasheet_2450M_2GFSK_1Mbps_500K"}</metadata>
</channel_config_entry>
</channel_config_entries>
<metadata>{"selectedPhy":"PHY_Datasheet_2450M_2GFSK_1Mbps_500K"}</metadata>
<phy name="PHY_Datasheet_2450M_2GFSK_1Mbps_500K"/>
</base_channel_configuration>
</base_channel_configurations>
</multi_phy_configuration>
Loading

0 comments on commit 63479d2

Please sign in to comment.