Skip to content

Commit e5800b3

Browse files
samples: Introduce "simple_txrx"
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]>
1 parent 12399e4 commit e5800b3

File tree

11 files changed

+1178
-0
lines changed

11 files changed

+1178
-0
lines changed

samples/simple_txrx/CMakeLists.txt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Copyright (c) 2023 Silicon Laboratories Inc.
2+
# SPDX-License-Identifier: Zlib
3+
4+
cmake_minimum_required(VERSION 3.20.0)
5+
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
6+
project(simple_txrx)
7+
8+
target_include_directories(app PRIVATE
9+
src/rail-configs/${CONFIG_BOARD}
10+
)
11+
target_sources(app PRIVATE
12+
src/main.c
13+
src/rail-configs/${CONFIG_BOARD}/rail_config.c
14+
)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/*
2+
* Copyright (c) 2023 Silicon Laboratories Inc.
3+
* SPDX-License-Identifier: Zlib
4+
*/
5+
6+
&cpu0 {
7+
cpu-power-states = <&pstate_em1>;
8+
};
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/*
2+
* Copyright (c) 2023 Silicon Laboratories Inc.
3+
* SPDX-License-Identifier: Zlib
4+
*/
5+
6+
&cpu0 {
7+
cpu-power-states = <&pstate_em1>;
8+
};

samples/simple_txrx/prj.conf

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CONFIG_SOC_GECKO_CUSTOM_RADIO_PHY=y
2+
CONFIG_SHELL=y
3+
CONFIG_PM=y
4+
CONFIG_EVENTS=y
5+
CONFIG_LOG=y
6+
7+
CONFIG_DEBUG_THREAD_INFO=y
8+
CONFIG_DEBUG=y

samples/simple_txrx/src/main.c

Lines changed: 301 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
1+
/*
2+
* Copyright (c) 2024 Silicon Laboratories Inc.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
#include <zephyr/drivers/gpio.h>
6+
#include <zephyr/shell/shell.h>
7+
#include <zephyr/logging/log.h>
8+
#include <zephyr/kernel.h>
9+
10+
#include "rail.h"
11+
#include "rail_config.h"
12+
#include "pa_conversions_efr32.h"
13+
14+
LOG_MODULE_REGISTER(app);
15+
16+
#ifdef RAIL0_CHANNEL_GROUP_1_PROFILE_WISUN_OFDM
17+
# if !defined(HARDWARE_BOARD_HAS_EFF)
18+
BUILD_ASSERT(SL_RAIL_UTIL_PA_SELECTION_SUBGHZ == RAIL_TX_POWER_MODE_OFDM_PA,
19+
"Please use the OFDM PA settings in the sl_rail_util_pa_config.h "
20+
"for OFDM phys");
21+
# endif
22+
# if defined(HARDWARE_BOARD_HAS_EFF) && RAIL_SUPPORTS_EFF
23+
BUILD_ASSERT(SL_RAIL_UTIL_PA_SELECTION_SUBGHZ >= RAIL_TX_POWER_MODE_OFDM_PA_EFF_30DBM,
24+
"Please use the OFDM PA for EFF settings in the sl_rail_util_pa_config.h "
25+
"for OFDM phys");
26+
# endif
27+
#endif
28+
29+
static const struct gpio_dt_spec sw0 = GPIO_DT_SPEC_GET(DT_ALIAS(sw0), gpios);
30+
static const struct gpio_dt_spec led_rx = GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios);
31+
#if DT_NODE_EXISTS(DT_ALIAS(led1))
32+
static const struct gpio_dt_spec led_tx = GPIO_DT_SPEC_GET(DT_ALIAS(led1), gpios);
33+
#else
34+
static const struct gpio_dt_spec led_tx = led_rx;
35+
#endif
36+
37+
enum {
38+
EV_RAIL_RX = BIT(0),
39+
EV_BTN_PRESSED = BIT(1),
40+
};
41+
42+
struct {
43+
RAIL_Handle_t rail_handle;
44+
struct k_event events;
45+
struct k_mutex tx_lock;
46+
int channel;
47+
const uint8_t *payload;
48+
int payload_len;
49+
} app_ctx;
50+
51+
void rx_packets(RAIL_Handle_t rail_handle)
52+
{
53+
uint8_t rx_frame[32];
54+
RAIL_RxPacketHandle_t handle;
55+
RAIL_RxPacketInfo_t info;
56+
RAIL_Status_t status;
57+
58+
for (;;) {
59+
handle = RAIL_GetRxPacketInfo(rail_handle, RAIL_RX_PACKET_HANDLE_OLDEST_COMPLETE,
60+
&info);
61+
if (handle == RAIL_RX_PACKET_HANDLE_INVALID) {
62+
return;
63+
}
64+
if (info.packetBytes < sizeof(rx_frame)) {
65+
RAIL_CopyRxPacket(rx_frame, &info);
66+
}
67+
status = RAIL_ReleaseRxPacket(rail_handle, handle);
68+
if (status) {
69+
LOG_ERR("RAIL_ReleaseRxPacket(): %d", status);
70+
}
71+
if (info.packetBytes < sizeof(rx_frame)) {
72+
LOG_HEXDUMP_INF(rx_frame, info.packetBytes, "rx data:");
73+
} else {
74+
LOG_INF("rx: skip large packet");
75+
}
76+
gpio_pin_set_dt(&led_rx, 0);
77+
}
78+
}
79+
80+
void tx_packet(RAIL_Handle_t rail_handle, int channel, const uint8_t *payload, int len)
81+
{
82+
RAIL_Status_t status;
83+
int ret;
84+
85+
ret = RAIL_WriteTxFifo(rail_handle, payload, len, true);
86+
if (ret != len) {
87+
LOG_ERR("RAIL_WriteTxFifo(): %d", ret);
88+
return;
89+
}
90+
gpio_pin_set_dt(&led_tx, 1);
91+
status = RAIL_StartTx(rail_handle, channel, RAIL_TX_OPTIONS_DEFAULT, NULL);
92+
if (status) {
93+
LOG_ERR("RAIL_StartTx(): %d ", status);
94+
}
95+
LOG_HEXDUMP_INF(payload, len, "tx data:");
96+
}
97+
98+
void btn_pressed(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
99+
{
100+
/* This function is called from an ISR context. So, transfer the real
101+
* processing to the main loop.
102+
*/
103+
k_event_post(&app_ctx.events, EV_BTN_PRESSED);
104+
}
105+
106+
void cli_send(const struct shell *sh, size_t argc, char **argv)
107+
{
108+
k_mutex_lock(&app_ctx.tx_lock, K_FOREVER);
109+
tx_packet(app_ctx.rail_handle, app_ctx.channel, app_ctx.payload, app_ctx.payload_len);
110+
k_mutex_unlock(&app_ctx.tx_lock);
111+
}
112+
113+
void rail_on_event(RAIL_Handle_t rail_handle, RAIL_Events_t events)
114+
{
115+
RAIL_Status_t status;
116+
117+
if (events & RAIL_EVENTS_RX_COMPLETION) {
118+
if (events & RAIL_EVENT_RX_PACKET_RECEIVED) {
119+
gpio_pin_set_dt(&led_rx, 1);
120+
RAIL_HoldRxPacket(rail_handle);
121+
k_event_post(&app_ctx.events, EV_RAIL_RX);
122+
} else {
123+
LOG_ERR("radio rx error: %08llx", events);
124+
}
125+
}
126+
127+
if (events & RAIL_EVENTS_TX_COMPLETION) {
128+
if (!(events & RAIL_EVENT_TX_PACKET_SENT)) {
129+
LOG_ERR("radio tx error: %08llx", events);
130+
}
131+
gpio_pin_set_dt(&led_tx, 0);
132+
}
133+
134+
if (events & RAIL_EVENTS_TXACK_COMPLETION) {
135+
/* We do not configure Tx ack. Catch the event anyway */
136+
LOG_INF("received ack completion");
137+
}
138+
139+
if (events & RAIL_EVENT_CAL_NEEDED) {
140+
status = RAIL_Calibrate(rail_handle, NULL, RAIL_CAL_ALL_PENDING);
141+
if (status) {
142+
LOG_ERR("RAIL_Calibrate(): %d", status);
143+
}
144+
}
145+
}
146+
147+
static void rail_on_rf_ready(RAIL_Handle_t rail_handle)
148+
{
149+
LOG_INF("radio is ready");
150+
}
151+
152+
static void rail_on_channel_config(RAIL_Handle_t rail_handle,
153+
const RAIL_ChannelConfigEntry_t *entry)
154+
{
155+
sl_rail_util_pa_on_channel_config_change(rail_handle, entry);
156+
}
157+
158+
static RAIL_Handle_t rail_init(void)
159+
{
160+
static uint8_t tx_fifo[256] __aligned(4);
161+
RAIL_Config_t rail_config = {
162+
.eventsCallback = &rail_on_event,
163+
};
164+
RAIL_DataConfig_t data_config = {
165+
.txSource = TX_PACKET_DATA,
166+
.rxSource = RX_PACKET_DATA,
167+
.txMethod = PACKET_MODE,
168+
.rxMethod = PACKET_MODE,
169+
};
170+
RAIL_StateTransitions_t transitions = {
171+
.success = RAIL_RF_STATE_RX,
172+
.error = RAIL_RF_STATE_RX,
173+
};
174+
RAIL_Handle_t rail_handle;
175+
RAIL_Status_t status;
176+
int ret;
177+
178+
rail_handle = RAIL_Init(&rail_config, &rail_on_rf_ready);
179+
if (!rail_handle) {
180+
LOG_ERR("RAIL_Init() failed");
181+
}
182+
status = RAIL_ConfigData(rail_handle, &data_config);
183+
if (status) {
184+
LOG_ERR("RAIL_ConfigData(): %d", status);
185+
}
186+
status = RAIL_ConfigChannels(rail_handle, channelConfigs[0], &rail_on_channel_config);
187+
if (status) {
188+
LOG_ERR("RAIL_ConfigChannels(): %d", status);
189+
}
190+
status = RAIL_SetPtiProtocol(rail_handle, RAIL_PTI_PROTOCOL_CUSTOM);
191+
if (status) {
192+
LOG_ERR("RAIL_SetPtiProtocol(): %d", status);
193+
}
194+
status = RAIL_ConfigCal(rail_handle, RAIL_CAL_TEMP | RAIL_CAL_ONETIME);
195+
if (status) {
196+
LOG_ERR("RAIL_ConfigCal(): %d", status);
197+
}
198+
status = RAIL_ConfigEvents(rail_handle, RAIL_EVENTS_ALL,
199+
RAIL_EVENTS_RX_COMPLETION |
200+
RAIL_EVENTS_TX_COMPLETION |
201+
RAIL_EVENTS_TXACK_COMPLETION |
202+
RAIL_EVENT_CAL_NEEDED);
203+
if (status) {
204+
LOG_ERR("RAIL_ConfigEvents(): %d", status);
205+
}
206+
status = RAIL_SetTxTransitions(rail_handle, &transitions);
207+
if (status) {
208+
LOG_ERR("RAIL_SetTxTransitions(): %d", status);
209+
}
210+
status = RAIL_SetRxTransitions(rail_handle, &transitions);
211+
if (status) {
212+
LOG_ERR("RAIL_SetRxTransitions(): %d", status);
213+
}
214+
ret = RAIL_SetTxFifo(rail_handle, tx_fifo, 0, sizeof(tx_fifo));
215+
if (ret != sizeof(tx_fifo)) {
216+
LOG_ERR("RAIL_SetTxFifo(): %d != %d", ret, sizeof(tx_fifo));
217+
}
218+
219+
return rail_handle;
220+
}
221+
222+
static void rail_isr_installer(void)
223+
{
224+
#ifdef CONFIG_SOC_SERIES_EFR32MG24
225+
IRQ_CONNECT(SYNTH_IRQn, 0, SYNTH_IRQHandler, NULL, 0);
226+
#else
227+
IRQ_CONNECT(RDMAILBOX_IRQn, 0, RDMAILBOX_IRQHandler, NULL, 0);
228+
#endif
229+
IRQ_CONNECT(RAC_SEQ_IRQn, 0, RAC_SEQ_IRQHandler, NULL, 0);
230+
IRQ_CONNECT(RAC_RSM_IRQn, 0, RAC_RSM_IRQHandler, NULL, 0);
231+
IRQ_CONNECT(PROTIMER_IRQn, 0, PROTIMER_IRQHandler, NULL, 0);
232+
IRQ_CONNECT(MODEM_IRQn, 0, MODEM_IRQHandler, NULL, 0);
233+
IRQ_CONNECT(FRC_IRQn, 0, FRC_IRQHandler, NULL, 0);
234+
IRQ_CONNECT(BUFC_IRQn, 0, BUFC_IRQHandler, NULL, 0);
235+
IRQ_CONNECT(AGC_IRQn, 0, AGC_IRQHandler, NULL, 0);
236+
}
237+
238+
int main(void)
239+
{
240+
static const uint8_t default_payload[] = {
241+
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
242+
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
243+
};
244+
struct gpio_callback sw0_cb;
245+
RAIL_Status_t status;
246+
uint32_t events;
247+
int ret;
248+
249+
k_event_init(&app_ctx.events);
250+
k_mutex_init(&app_ctx.tx_lock);
251+
gpio_pin_configure_dt(&led_tx, GPIO_OUTPUT_INACTIVE);
252+
gpio_pin_configure_dt(&led_rx, GPIO_OUTPUT_INACTIVE);
253+
gpio_pin_configure_dt(&sw0, GPIO_INPUT);
254+
gpio_pin_interrupt_configure_dt(&sw0, GPIO_INT_EDGE_TO_ACTIVE);
255+
gpio_init_callback(&sw0_cb, btn_pressed, BIT(sw0.pin));
256+
gpio_add_callback(sw0.port, &sw0_cb);
257+
258+
rail_isr_installer();
259+
sl_rail_util_pa_init();
260+
app_ctx.rail_handle = rail_init();
261+
app_ctx.channel = 0;
262+
app_ctx.payload = default_payload;
263+
app_ctx.payload_len = sizeof(default_payload);
264+
265+
ret = RAIL_SetFixedLength(app_ctx.rail_handle, app_ctx.payload_len);
266+
if (ret != app_ctx.payload_len) {
267+
LOG_ERR("RAIL_SetFixedLength(): %d ", ret);
268+
}
269+
status = RAIL_StartRx(app_ctx.rail_handle, app_ctx.channel, NULL);
270+
if (status) {
271+
LOG_ERR("RAIL_StartRx(): %d ", status);
272+
}
273+
274+
#ifdef CONFIG_PM
275+
status = RAIL_InitPowerManager();
276+
if (status) {
277+
LOG_ERR("RAIL_InitPowerManager(): %d", status);
278+
}
279+
#endif
280+
281+
for (;;) {
282+
events = k_event_wait(&app_ctx.events, 0xFFFFFFFF, true, K_FOREVER);
283+
if (events & EV_RAIL_RX) {
284+
rx_packets(app_ctx.rail_handle);
285+
}
286+
if (events & EV_BTN_PRESSED) {
287+
k_mutex_lock(&app_ctx.tx_lock, K_FOREVER);
288+
tx_packet(app_ctx.rail_handle, app_ctx.channel,
289+
app_ctx.payload, app_ctx.payload_len);
290+
k_mutex_unlock(&app_ctx.tx_lock);
291+
}
292+
}
293+
294+
return 0;
295+
}
296+
297+
SHELL_STATIC_SUBCMD_SET_CREATE(radio_cmds,
298+
SHELL_CMD_ARG(send, NULL, "Send a packet", cli_send, 1, 0),
299+
SHELL_SUBCMD_SET_END
300+
);
301+
SHELL_CMD_ARG_REGISTER(radio, &radio_cmds, "Radio control", NULL, 2, 0);
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<multi_phy_configuration part_family="lynx" part_revision="A0" rail_adapter_version="rail_api_2.x" status_code="0" xsd_version="0.0.20">
3+
<base_channel_configurations>
4+
<base_channel_configuration name="Protocol Configuration" profile="Base">
5+
<channel_config_entries>
6+
<channel_config_entry name="Channel Group 1">
7+
<channel_number_start>0</channel_number_start>
8+
<channel_number_end>20</channel_number_end>
9+
<physical_channel_offset>SAME_AS_FIRST_CHANNEL</physical_channel_offset>
10+
<max_power>RAIL_TX_POWER_MAX</max_power>
11+
<metadata>{"selectedPhy":"PHY_Datasheet_2450M_2GFSK_1Mbps_500K"}</metadata>
12+
</channel_config_entry>
13+
</channel_config_entries>
14+
<metadata>{"selectedPhy":"PHY_Datasheet_2450M_2GFSK_1Mbps_500K"}</metadata>
15+
<phy name="PHY_Datasheet_2450M_2GFSK_1Mbps_500K"/>
16+
</base_channel_configuration>
17+
</base_channel_configurations>
18+
</multi_phy_configuration>

0 commit comments

Comments
 (0)