forked from SiliconLabsSoftware/zephyr-silabs
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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 [1]: zephyrproject-rtos/hal_silabs#49 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. Also note that files generated by Simplicity Studio (rail_config.*) ends lines with \r. To pass Zephyr compliance, this patch imports these files with \r removed: sed -i 's/\r//g' */rail_config.[hc] Signed-off-by: Jérôme Pouiller <[email protected]>
- Loading branch information
1 parent
15fd227
commit 2653721
Showing
11 changed files
with
1,178 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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>; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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>; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,301 @@ | ||
/* | ||
* 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 events; | ||
struct k_mutex tx_lock; | ||
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_set_dt(&led_rx, 0); | ||
} | ||
} | ||
|
||
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_set_dt(&led_tx, 1); | ||
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.events, EV_BTN_PRESSED); | ||
} | ||
|
||
void cli_send(const struct shell *sh, size_t argc, char **argv) | ||
{ | ||
k_mutex_lock(&app_ctx.tx_lock, K_FOREVER); | ||
tx_packet(app_ctx.rail_handle, app_ctx.channel, app_ctx.payload, app_ctx.payload_len); | ||
k_mutex_unlock(&app_ctx.tx_lock); | ||
} | ||
|
||
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_set_dt(&led_rx, 1); | ||
RAIL_HoldRxPacket(rail_handle); | ||
k_event_post(&app_ctx.events, 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_set_dt(&led_tx, 0); | ||
} | ||
|
||
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.events); | ||
k_mutex_init(&app_ctx.tx_lock); | ||
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.events, 0xFFFFFFFF, true, K_FOREVER); | ||
if (events & EV_RAIL_RX) { | ||
rx_packets(app_ctx.rail_handle); | ||
} | ||
if (events & EV_BTN_PRESSED) { | ||
k_mutex_lock(&app_ctx.tx_lock, K_FOREVER); | ||
tx_packet(app_ctx.rail_handle, app_ctx.channel, | ||
app_ctx.payload, app_ctx.payload_len); | ||
k_mutex_unlock(&app_ctx.tx_lock); | ||
} | ||
} | ||
|
||
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); |
18 changes: 18 additions & 0 deletions
18
samples/simple_txrx/src/rail-configs/sltb010a/radio_settings.radioconf
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
Oops, something went wrong.